mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 18:13:04 +00:00
net: ethernet: mediatek: Extend SGMII related functions
Add SGMII related logic into a separate file, and also provides options for forcing 1G, 2.5, AN mode for the target PHY, that can be determined from SGMII node in DTS. Signed-off-by: Sean Wang <sean.wang@mediatek.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
3277fc683a
commit
9ffee4a827
@ -3,4 +3,4 @@
|
|||||||
# Makefile for the Mediatek SoCs built-in ethernet macs
|
# Makefile for the Mediatek SoCs built-in ethernet macs
|
||||||
#
|
#
|
||||||
|
|
||||||
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o
|
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o mtk_sgmii.o
|
||||||
|
@ -165,36 +165,37 @@ static void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth, int speed)
|
|||||||
mtk_w32(eth, val, TRGMII_TCK_CTRL);
|
mtk_w32(eth, val, TRGMII_TCK_CTRL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtk_gmac_sgmii_hw_setup(struct mtk_eth *eth, int mac_id)
|
static int mtk_gmac_sgmii_hw_setup(struct mtk_eth *eth, int mac_id)
|
||||||
{
|
{
|
||||||
|
int sid, err;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
/* Setup the link timer and QPHY power up inside SGMIISYS */
|
/* Enable GMAC with SGMII once we finish the SGMII setup. */
|
||||||
regmap_write(eth->sgmiisys, SGMSYS_PCS_LINK_TIMER,
|
|
||||||
SGMII_LINK_TIMER_DEFAULT);
|
|
||||||
|
|
||||||
regmap_read(eth->sgmiisys, SGMSYS_SGMII_MODE, &val);
|
|
||||||
val |= SGMII_REMOTE_FAULT_DIS;
|
|
||||||
regmap_write(eth->sgmiisys, SGMSYS_SGMII_MODE, val);
|
|
||||||
|
|
||||||
regmap_read(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, &val);
|
|
||||||
val |= SGMII_AN_RESTART;
|
|
||||||
regmap_write(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, val);
|
|
||||||
|
|
||||||
regmap_read(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
|
|
||||||
val &= ~SGMII_PHYA_PWD;
|
|
||||||
regmap_write(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, val);
|
|
||||||
|
|
||||||
/* Determine MUX for which GMAC uses the SGMII interface */
|
|
||||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_DUAL_GMAC_SHARED_SGMII)) {
|
|
||||||
regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
|
regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
|
||||||
val &= ~SYSCFG0_SGMII_MASK;
|
val &= ~SYSCFG0_SGMII_MASK;
|
||||||
val |= !mac_id ? SYSCFG0_SGMII_GMAC1 : SYSCFG0_SGMII_GMAC2;
|
|
||||||
regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
|
regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
|
||||||
|
|
||||||
dev_info(eth->dev, "setup shared sgmii for gmac=%d\n",
|
if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC_SHARED_SGMII))
|
||||||
mac_id);
|
sid = 0;
|
||||||
}
|
else
|
||||||
|
sid = mac_id;
|
||||||
|
|
||||||
|
if (MTK_HAS_FLAGS(eth->sgmii->flags[sid], MTK_SGMII_PHYSPEED_AN))
|
||||||
|
err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
|
||||||
|
else
|
||||||
|
err = mtk_sgmii_setup_mode_force(eth->sgmii, sid);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* Determine MUX for which GMAC uses the SGMII interface */
|
||||||
|
regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
|
||||||
|
if (!mac_id)
|
||||||
|
val |= SYSCFG0_SGMII_GMAC1;
|
||||||
|
else
|
||||||
|
val |= MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC_SHARED_SGMII) ?
|
||||||
|
SYSCFG0_SGMII_GMAC2 : SYSCFG0_SGMII_GMAC2_V2;
|
||||||
|
regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
|
||||||
|
|
||||||
/* Setup the GMAC1 going through SGMII path when SoC also support
|
/* Setup the GMAC1 going through SGMII path when SoC also support
|
||||||
* ESW on GMAC1
|
* ESW on GMAC1
|
||||||
@ -204,6 +205,8 @@ static void mtk_gmac_sgmii_hw_setup(struct mtk_eth *eth, int mac_id)
|
|||||||
mtk_w32(eth, 0, MTK_MAC_MISC);
|
mtk_w32(eth, 0, MTK_MAC_MISC);
|
||||||
dev_info(eth->dev, "setup gmac1 going through sgmii");
|
dev_info(eth->dev, "setup gmac1 going through sgmii");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtk_phy_link_adjust(struct net_device *dev)
|
static void mtk_phy_link_adjust(struct net_device *dev)
|
||||||
@ -295,6 +298,7 @@ static int mtk_phy_connect(struct net_device *dev)
|
|||||||
struct mtk_eth *eth;
|
struct mtk_eth *eth;
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
int err;
|
||||||
|
|
||||||
eth = mac->hw;
|
eth = mac->hw;
|
||||||
np = of_parse_phandle(mac->of_node, "phy-handle", 0);
|
np = of_parse_phandle(mac->of_node, "phy-handle", 0);
|
||||||
@ -314,8 +318,11 @@ static int mtk_phy_connect(struct net_device *dev)
|
|||||||
case PHY_INTERFACE_MODE_RGMII:
|
case PHY_INTERFACE_MODE_RGMII:
|
||||||
break;
|
break;
|
||||||
case PHY_INTERFACE_MODE_SGMII:
|
case PHY_INTERFACE_MODE_SGMII:
|
||||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII))
|
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
|
||||||
mtk_gmac_sgmii_hw_setup(eth, mac->id);
|
err = mtk_gmac_sgmii_hw_setup(eth, mac->id);
|
||||||
|
if (err)
|
||||||
|
goto err_phy;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PHY_INTERFACE_MODE_MII:
|
case PHY_INTERFACE_MODE_MII:
|
||||||
mac->ge_mode = 1;
|
mac->ge_mode = 1;
|
||||||
@ -2483,13 +2490,16 @@ static int mtk_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
|
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
|
||||||
eth->sgmiisys =
|
eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
|
||||||
syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
GFP_KERNEL);
|
||||||
"mediatek,sgmiisys");
|
if (!eth->sgmii)
|
||||||
if (IS_ERR(eth->sgmiisys)) {
|
return -ENOMEM;
|
||||||
dev_err(&pdev->dev, "no sgmiisys regmap found\n");
|
|
||||||
return PTR_ERR(eth->sgmiisys);
|
err = mtk_sgmii_init(eth->sgmii, pdev->dev.of_node,
|
||||||
}
|
eth->soc->ana_rgc3);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eth->soc->required_pctl) {
|
if (eth->soc->required_pctl) {
|
||||||
@ -2642,7 +2652,8 @@ static const struct mtk_soc_data mt7621_data = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_soc_data mt7622_data = {
|
static const struct mtk_soc_data mt7622_data = {
|
||||||
.caps = MTK_DUAL_GMAC_SHARED_SGMII | MTK_GMAC1_ESW | MTK_HWLRO,
|
.ana_rgc3 = 0x2028,
|
||||||
|
.caps = MTK_GMAC_SHARED_SGMII | MTK_GMAC1_ESW | MTK_HWLRO,
|
||||||
.required_clks = MT7622_CLKS_BITMAP,
|
.required_clks = MT7622_CLKS_BITMAP,
|
||||||
.required_pctl = false,
|
.required_pctl = false,
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
#ifndef MTK_ETH_H
|
#ifndef MTK_ETH_H
|
||||||
#define MTK_ETH_H
|
#define MTK_ETH_H
|
||||||
|
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/of_net.h>
|
||||||
|
#include <linux/u64_stats_sync.h>
|
||||||
#include <linux/refcount.h>
|
#include <linux/refcount.h>
|
||||||
|
|
||||||
#define MTK_QDMA_PAGE_SIZE 2048
|
#define MTK_QDMA_PAGE_SIZE 2048
|
||||||
@ -372,6 +376,7 @@
|
|||||||
#define SYSCFG0_SGMII_MASK (3 << 8)
|
#define SYSCFG0_SGMII_MASK (3 << 8)
|
||||||
#define SYSCFG0_SGMII_GMAC1 ((2 << 8) & GENMASK(9, 8))
|
#define SYSCFG0_SGMII_GMAC1 ((2 << 8) & GENMASK(9, 8))
|
||||||
#define SYSCFG0_SGMII_GMAC2 ((3 << 8) & GENMASK(9, 8))
|
#define SYSCFG0_SGMII_GMAC2 ((3 << 8) & GENMASK(9, 8))
|
||||||
|
#define SYSCFG0_SGMII_GMAC2_V2 ((1 << 8) & GENMASK(9, 8))
|
||||||
|
|
||||||
/* ethernet subsystem clock register */
|
/* ethernet subsystem clock register */
|
||||||
#define ETHSYS_CLKCFG0 0x2c
|
#define ETHSYS_CLKCFG0 0x2c
|
||||||
@ -567,7 +572,7 @@ struct mtk_rx_ring {
|
|||||||
#define MTK_SGMII BIT(8)
|
#define MTK_SGMII BIT(8)
|
||||||
#define MTK_GMAC1_SGMII (BIT(9) | MTK_SGMII)
|
#define MTK_GMAC1_SGMII (BIT(9) | MTK_SGMII)
|
||||||
#define MTK_GMAC2_SGMII (BIT(10) | MTK_SGMII)
|
#define MTK_GMAC2_SGMII (BIT(10) | MTK_SGMII)
|
||||||
#define MTK_DUAL_GMAC_SHARED_SGMII (BIT(11) | MTK_GMAC1_SGMII | \
|
#define MTK_GMAC_SHARED_SGMII (BIT(11) | MTK_GMAC1_SGMII | \
|
||||||
MTK_GMAC2_SGMII)
|
MTK_GMAC2_SGMII)
|
||||||
#define MTK_HWLRO BIT(12)
|
#define MTK_HWLRO BIT(12)
|
||||||
#define MTK_SHARED_INT BIT(13)
|
#define MTK_SHARED_INT BIT(13)
|
||||||
@ -575,6 +580,8 @@ struct mtk_rx_ring {
|
|||||||
|
|
||||||
/* struct mtk_eth_data - This is the structure holding all differences
|
/* struct mtk_eth_data - This is the structure holding all differences
|
||||||
* among various plaforms
|
* among various plaforms
|
||||||
|
* @ana_rgc3: The offset for register ANA_RGC3 related to
|
||||||
|
* sgmiisys syscon
|
||||||
* @caps Flags shown the extra capability for the SoC
|
* @caps Flags shown the extra capability for the SoC
|
||||||
* @required_clks Flags shown the bitmap for required clocks on
|
* @required_clks Flags shown the bitmap for required clocks on
|
||||||
* the target SoC
|
* the target SoC
|
||||||
@ -582,6 +589,7 @@ struct mtk_rx_ring {
|
|||||||
* the extra setup for those pins used by GMAC.
|
* the extra setup for those pins used by GMAC.
|
||||||
*/
|
*/
|
||||||
struct mtk_soc_data {
|
struct mtk_soc_data {
|
||||||
|
u32 ana_rgc3;
|
||||||
u32 caps;
|
u32 caps;
|
||||||
u32 required_clks;
|
u32 required_clks;
|
||||||
bool required_pctl;
|
bool required_pctl;
|
||||||
@ -590,6 +598,26 @@ struct mtk_soc_data {
|
|||||||
/* currently no SoC has more than 2 macs */
|
/* currently no SoC has more than 2 macs */
|
||||||
#define MTK_MAX_DEVS 2
|
#define MTK_MAX_DEVS 2
|
||||||
|
|
||||||
|
#define MTK_SGMII_PHYSPEED_AN BIT(31)
|
||||||
|
#define MTK_SGMII_PHYSPEED_MASK GENMASK(0, 2)
|
||||||
|
#define MTK_SGMII_PHYSPEED_1000 BIT(0)
|
||||||
|
#define MTK_SGMII_PHYSPEED_2500 BIT(1)
|
||||||
|
#define MTK_HAS_FLAGS(flags, _x) (((flags) & (_x)) == (_x))
|
||||||
|
|
||||||
|
/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
|
||||||
|
* characteristics
|
||||||
|
* @regmap: The register map pointing at the range used to setup
|
||||||
|
* SGMII modes
|
||||||
|
* @flags: The enum refers to which mode the sgmii wants to run on
|
||||||
|
* @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct mtk_sgmii {
|
||||||
|
struct regmap *regmap[MTK_MAX_DEVS];
|
||||||
|
u32 flags[MTK_MAX_DEVS];
|
||||||
|
u32 ana_rgc3;
|
||||||
|
};
|
||||||
|
|
||||||
/* struct mtk_eth - This is the main datasructure for holding the state
|
/* struct mtk_eth - This is the main datasructure for holding the state
|
||||||
* of the driver
|
* of the driver
|
||||||
* @dev: The device pointer
|
* @dev: The device pointer
|
||||||
@ -605,8 +633,6 @@ struct mtk_soc_data {
|
|||||||
* @msg_enable: Ethtool msg level
|
* @msg_enable: Ethtool msg level
|
||||||
* @ethsys: The register map pointing at the range used to setup
|
* @ethsys: The register map pointing at the range used to setup
|
||||||
* MII modes
|
* MII modes
|
||||||
* @sgmiisys: The register map pointing at the range used to setup
|
|
||||||
* SGMII modes
|
|
||||||
* @pctl: The register map pointing at the range used to setup
|
* @pctl: The register map pointing at the range used to setup
|
||||||
* GMAC port drive/slew values
|
* GMAC port drive/slew values
|
||||||
* @dma_refcnt: track how many netdevs are using the DMA engine
|
* @dma_refcnt: track how many netdevs are using the DMA engine
|
||||||
@ -638,7 +664,7 @@ struct mtk_eth {
|
|||||||
u32 msg_enable;
|
u32 msg_enable;
|
||||||
unsigned long sysclk;
|
unsigned long sysclk;
|
||||||
struct regmap *ethsys;
|
struct regmap *ethsys;
|
||||||
struct regmap *sgmiisys;
|
struct mtk_sgmii *sgmii;
|
||||||
struct regmap *pctl;
|
struct regmap *pctl;
|
||||||
bool hwlro;
|
bool hwlro;
|
||||||
refcount_t dma_refcnt;
|
refcount_t dma_refcnt;
|
||||||
@ -689,4 +715,9 @@ void mtk_stats_update_mac(struct mtk_mac *mac);
|
|||||||
void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
|
void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
|
||||||
u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
|
u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
|
||||||
|
|
||||||
|
int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
|
||||||
|
u32 ana_rgc3);
|
||||||
|
int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
|
||||||
|
int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id);
|
||||||
|
|
||||||
#endif /* MTK_ETH_H */
|
#endif /* MTK_ETH_H */
|
||||||
|
105
drivers/net/ethernet/mediatek/mtk_sgmii.c
Normal file
105
drivers/net/ethernet/mediatek/mtk_sgmii.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2018-2019 MediaTek Inc.
|
||||||
|
|
||||||
|
/* A library for MediaTek SGMII circuit
|
||||||
|
*
|
||||||
|
* Author: Sean Wang <sean.wang@mediatek.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
#include "mtk_eth_soc.h"
|
||||||
|
|
||||||
|
int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
|
||||||
|
{
|
||||||
|
struct device_node *np;
|
||||||
|
const char *str;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
ss->ana_rgc3 = ana_rgc3;
|
||||||
|
|
||||||
|
for (i = 0; i < MTK_MAX_DEVS; i++) {
|
||||||
|
np = of_parse_phandle(r, "mediatek,sgmiisys", i);
|
||||||
|
if (!np)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ss->regmap[i] = syscon_node_to_regmap(np);
|
||||||
|
if (IS_ERR(ss->regmap[i]))
|
||||||
|
return PTR_ERR(ss->regmap[i]);
|
||||||
|
|
||||||
|
err = of_property_read_string(np, "mediatek,physpeed", &str);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (!strcmp(str, "2500"))
|
||||||
|
ss->flags[i] |= MTK_SGMII_PHYSPEED_2500;
|
||||||
|
else if (!strcmp(str, "1000"))
|
||||||
|
ss->flags[i] |= MTK_SGMII_PHYSPEED_1000;
|
||||||
|
else if (!strcmp(str, "auto"))
|
||||||
|
ss->flags[i] |= MTK_SGMII_PHYSPEED_AN;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
|
||||||
|
{
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
if (!ss->regmap[id])
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Setup the link timer and QPHY power up inside SGMIISYS */
|
||||||
|
regmap_write(ss->regmap[id], SGMSYS_PCS_LINK_TIMER,
|
||||||
|
SGMII_LINK_TIMER_DEFAULT);
|
||||||
|
|
||||||
|
regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
|
||||||
|
val |= SGMII_REMOTE_FAULT_DIS;
|
||||||
|
regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
|
||||||
|
|
||||||
|
regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
|
||||||
|
val |= SGMII_AN_RESTART;
|
||||||
|
regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
|
||||||
|
|
||||||
|
regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
|
||||||
|
val &= ~SGMII_PHYA_PWD;
|
||||||
|
regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id)
|
||||||
|
{
|
||||||
|
unsigned int val;
|
||||||
|
int mode;
|
||||||
|
|
||||||
|
if (!ss->regmap[id])
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
regmap_read(ss->regmap[id], ss->ana_rgc3, &val);
|
||||||
|
val &= ~GENMASK(2, 3);
|
||||||
|
mode = ss->flags[id] & MTK_SGMII_PHYSPEED_MASK;
|
||||||
|
val |= (mode == MTK_SGMII_PHYSPEED_1000) ? 0 : BIT(2);
|
||||||
|
regmap_write(ss->regmap[id], ss->ana_rgc3, val);
|
||||||
|
|
||||||
|
/* Disable SGMII AN */
|
||||||
|
regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
|
||||||
|
val &= ~BIT(12);
|
||||||
|
regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
|
||||||
|
|
||||||
|
/* SGMII force mode setting */
|
||||||
|
val = 0x31120019;
|
||||||
|
regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
|
||||||
|
|
||||||
|
/* Release PHYA power down state */
|
||||||
|
regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
|
||||||
|
val &= ~SGMII_PHYA_PWD;
|
||||||
|
regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user