diff options
author | Sean Wang <sean.wang@mediatek.com> | 2019-06-01 03:03:13 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-06-04 01:00:00 +0300 |
commit | 7093f9d80c7cb82f47628d6477fd4dc252d21a10 (patch) | |
tree | c9214e2966dceed931f27158310006070441ef8f /drivers/net/ethernet/mediatek/mtk_eth_path.c | |
parent | 9ffee4a8276c734956b8eab19d1c9b0ecb6b79c5 (diff) | |
download | linux-7093f9d80c7cb82f47628d6477fd4dc252d21a10.tar.xz |
net: ethernet: mediatek: Integrate hardware path from GMAC to PHY variants
All path route on various SoCs all would be managed in common function
mtk_setup_hw_path that is determined by the both applied devicetree
regarding the path between GMAC and the target PHY or switch by the
capability of target SoC in the runtime.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/mediatek/mtk_eth_path.c')
-rw-r--r-- | drivers/net/ethernet/mediatek/mtk_eth_path.c | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_path.c b/drivers/net/ethernet/mediatek/mtk_eth_path.c new file mode 100644 index 000000000000..61f705d945e5 --- /dev/null +++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 MediaTek Inc. + +/* A library for configuring path from GMAC/GDM to target PHY + * + * Author: Sean Wang <sean.wang@mediatek.com> + * + */ + +#include <linux/phy.h> +#include <linux/regmap.h> + +#include "mtk_eth_soc.h" + +struct mtk_eth_muxc { + int (*set_path)(struct mtk_eth *eth, int path); +}; + +static const char * const mtk_eth_mux_name[] = { + "mux_gdm1_to_gmac1_esw", "mux_gmac2_gmac0_to_gephy", + "mux_u3_gmac2_to_qphy", "mux_gmac1_gmac2_to_sgmii_rgmii", + "mux_gmac12_to_gephy_sgmii", +}; + +static const char * const mtk_eth_path_name[] = { + "gmac1_rgmii", "gmac1_trgmii", "gmac1_sgmii", "gmac2_rgmii", + "gmac2_sgmii", "gmac2_gephy", "gdm1_esw", +}; + +static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, int path) +{ + bool updated = true; + u32 val, mask, set; + + switch (path) { + case MTK_ETH_PATH_GMAC1_SGMII: + mask = ~(u32)MTK_MUX_TO_ESW; + set = 0; + break; + case MTK_ETH_PATH_GDM1_ESW: + mask = ~(u32)MTK_MUX_TO_ESW; + set = MTK_MUX_TO_ESW; + break; + default: + updated = false; + break; + }; + + if (updated) { + val = mtk_r32(eth, MTK_MAC_MISC); + val = (val & mask) | set; + mtk_w32(eth, val, MTK_MAC_MISC); + } + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name[path], __func__, updated); + + return 0; +} + +static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, int path) +{ + unsigned int val = 0; + bool updated = true; + + switch (path) { + case MTK_ETH_PATH_GMAC2_GEPHY: + val = ~(u32)GEPHY_MAC_SEL; + break; + default: + updated = false; + break; + } + + if (updated) + regmap_update_bits(eth->infra, INFRA_MISC2, GEPHY_MAC_SEL, val); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name[path], __func__, updated); + + return 0; +} + +static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path) +{ + unsigned int val = 0; + bool updated = true; + + switch (path) { + case MTK_ETH_PATH_GMAC2_SGMII: + val = CO_QPHY_SEL; + break; + default: + updated = false; + break; + } + + if (updated) + regmap_update_bits(eth->infra, INFRA_MISC2, CO_QPHY_SEL, val); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name[path], __func__, updated); + + return 0; +} + +static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, int path) +{ + unsigned int val = 0; + bool updated = true; + + switch (path) { + case MTK_ETH_PATH_GMAC1_SGMII: + val = SYSCFG0_SGMII_GMAC1; + break; + case MTK_ETH_PATH_GMAC2_SGMII: + val = SYSCFG0_SGMII_GMAC2; + break; + case MTK_ETH_PATH_GMAC1_RGMII: + case MTK_ETH_PATH_GMAC2_RGMII: + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + val &= SYSCFG0_SGMII_MASK; + + if ((path == MTK_GMAC1_RGMII && val == SYSCFG0_SGMII_GMAC1) || + (path == MTK_GMAC2_RGMII && val == SYSCFG0_SGMII_GMAC2)) + val = 0; + else + updated = false; + break; + default: + updated = false; + break; + }; + + if (updated) + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name[path], __func__, updated); + + return 0; +} + +static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, int path) +{ + unsigned int val = 0; + bool updated = true; + + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + + switch (path) { + case MTK_ETH_PATH_GMAC1_SGMII: + val |= SYSCFG0_SGMII_GMAC1_V2; + break; + case MTK_ETH_PATH_GMAC2_GEPHY: + val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2; + break; + case MTK_ETH_PATH_GMAC2_SGMII: + val |= SYSCFG0_SGMII_GMAC2_V2; + break; + default: + updated = false; + }; + + if (updated) + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name[path], __func__, updated); + + return 0; +} + +static const struct mtk_eth_muxc mtk_eth_muxc[] = { + { .set_path = set_mux_gdm1_to_gmac1_esw, }, + { .set_path = set_mux_gmac2_gmac0_to_gephy, }, + { .set_path = set_mux_u3_gmac2_to_qphy, }, + { .set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii, }, + { .set_path = set_mux_gmac12_to_gephy_sgmii, } +}; + +static int mtk_eth_mux_setup(struct mtk_eth *eth, int path) +{ + int i, err = 0; + + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_PATH_BIT(path))) { + dev_err(eth->dev, "path %s isn't support on the SoC\n", + mtk_eth_path_name[path]); + return -EINVAL; + } + + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_MUX)) + return 0; + + /* Setup MUX in path fabric */ + for (i = 0; i < MTK_ETH_MUX_MAX; i++) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_MUX_BIT(i))) { + err = mtk_eth_muxc[i].set_path(eth, path); + if (err) + goto out; + } else { + dev_dbg(eth->dev, "mux %s isn't present on the SoC\n", + mtk_eth_mux_name[i]); + } + } + +out: + return err; +} + +static int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id) +{ + unsigned int val = 0; + int sid, err, path; + + path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII : + MTK_ETH_PATH_GMAC2_SGMII; + + /* Setup proper MUXes along the path */ + err = mtk_eth_mux_setup(eth, path); + if (err) + return err; + + /* The path GMAC to SGMII will be enabled once the SGMIISYS is being + * setup done. + */ + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, ~(u32)SYSCFG0_SGMII_MASK); + + /* Decide how GMAC and SGMIISYS be mapped */ + sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? 0 : mac_id; + + /* Setup SGMIISYS with the determined property */ + 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; + + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + + return 0; +} + +static int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id) +{ + int err, path = 0; + + if (mac_id == 1) + path = MTK_ETH_PATH_GMAC2_GEPHY; + + if (!path) + return -EINVAL; + + /* Setup proper MUXes along the path */ + err = mtk_eth_mux_setup(eth, path); + if (err) + return err; + + return 0; +} + +static int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id) +{ + int err, path; + + path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_RGMII : + MTK_ETH_PATH_GMAC2_RGMII; + + /* Setup proper MUXes along the path */ + err = mtk_eth_mux_setup(eth, path); + if (err) + return err; + + return 0; +} + +int mtk_setup_hw_path(struct mtk_eth *eth, int mac_id, int phymode) +{ + int err; + + switch (phymode) { + case PHY_INTERFACE_MODE_TRGMII: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_REVMII: + case PHY_INTERFACE_MODE_RMII: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RGMII)) { + err = mtk_gmac_rgmii_path_setup(eth, mac_id); + if (err) + return err; + } + break; + case PHY_INTERFACE_MODE_SGMII: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { + err = mtk_gmac_sgmii_path_setup(eth, mac_id); + if (err) + return err; + } + break; + case PHY_INTERFACE_MODE_GMII: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_GEPHY)) { + err = mtk_gmac_gephy_path_setup(eth, mac_id); + if (err) + return err; + } + break; + default: + break; + } + + return 0; +} |