17093f9d8SSean Wang // SPDX-License-Identifier: GPL-2.0
27093f9d8SSean Wang // Copyright (c) 2018-2019 MediaTek Inc.
37093f9d8SSean Wang 
47093f9d8SSean Wang /* A library for configuring path from GMAC/GDM to target PHY
57093f9d8SSean Wang  *
67093f9d8SSean Wang  * Author: Sean Wang <sean.wang@mediatek.com>
77093f9d8SSean Wang  *
87093f9d8SSean Wang  */
97093f9d8SSean Wang 
107093f9d8SSean Wang #include <linux/phy.h>
117093f9d8SSean Wang #include <linux/regmap.h>
127093f9d8SSean Wang 
137093f9d8SSean Wang #include "mtk_eth_soc.h"
147093f9d8SSean Wang 
157093f9d8SSean Wang struct mtk_eth_muxc {
16e2c74694SRené van Dorst 	const char	*name;
17e2c74694SRené van Dorst 	int		cap_bit;
1851a4df60SLorenzo Bianconi 	int		(*set_path)(struct mtk_eth *eth, u64 path);
197093f9d8SSean Wang };
207093f9d8SSean Wang 
mtk_eth_path_name(u64 path)2151a4df60SLorenzo Bianconi static const char *mtk_eth_path_name(u64 path)
22e2c74694SRené van Dorst {
23e2c74694SRené van Dorst 	switch (path) {
24e2c74694SRené van Dorst 	case MTK_ETH_PATH_GMAC1_RGMII:
25e2c74694SRené van Dorst 		return "gmac1_rgmii";
26e2c74694SRené van Dorst 	case MTK_ETH_PATH_GMAC1_TRGMII:
27e2c74694SRené van Dorst 		return "gmac1_trgmii";
28e2c74694SRené van Dorst 	case MTK_ETH_PATH_GMAC1_SGMII:
29e2c74694SRené van Dorst 		return "gmac1_sgmii";
30e2c74694SRené van Dorst 	case MTK_ETH_PATH_GMAC2_RGMII:
31e2c74694SRené van Dorst 		return "gmac2_rgmii";
32e2c74694SRené van Dorst 	case MTK_ETH_PATH_GMAC2_SGMII:
33e2c74694SRené van Dorst 		return "gmac2_sgmii";
34e2c74694SRené van Dorst 	case MTK_ETH_PATH_GMAC2_GEPHY:
35e2c74694SRené van Dorst 		return "gmac2_gephy";
36e2c74694SRené van Dorst 	case MTK_ETH_PATH_GDM1_ESW:
37e2c74694SRené van Dorst 		return "gdm1_esw";
38e2c74694SRené van Dorst 	default:
39e2c74694SRené van Dorst 		return "unknown path";
40e2c74694SRené van Dorst 	}
41e2c74694SRené van Dorst }
427093f9d8SSean Wang 
set_mux_gdm1_to_gmac1_esw(struct mtk_eth * eth,u64 path)4351a4df60SLorenzo Bianconi static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, u64 path)
447093f9d8SSean Wang {
457093f9d8SSean Wang 	bool updated = true;
46*445eb644SLorenzo Bianconi 	u32 mask, set, reg;
477093f9d8SSean Wang 
487093f9d8SSean Wang 	switch (path) {
497093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC1_SGMII:
507093f9d8SSean Wang 		mask = ~(u32)MTK_MUX_TO_ESW;
517093f9d8SSean Wang 		set = 0;
527093f9d8SSean Wang 		break;
537093f9d8SSean Wang 	case MTK_ETH_PATH_GDM1_ESW:
547093f9d8SSean Wang 		mask = ~(u32)MTK_MUX_TO_ESW;
557093f9d8SSean Wang 		set = MTK_MUX_TO_ESW;
567093f9d8SSean Wang 		break;
577093f9d8SSean Wang 	default:
587093f9d8SSean Wang 		updated = false;
597093f9d8SSean Wang 		break;
6011b3412cSYueHaibing 	}
617093f9d8SSean Wang 
62*445eb644SLorenzo Bianconi 	if (mtk_is_netsys_v3_or_greater(eth))
63*445eb644SLorenzo Bianconi 		reg = MTK_MAC_MISC_V3;
64*445eb644SLorenzo Bianconi 	else
65*445eb644SLorenzo Bianconi 		reg = MTK_MAC_MISC;
66*445eb644SLorenzo Bianconi 
67*445eb644SLorenzo Bianconi 	if (updated)
68*445eb644SLorenzo Bianconi 		mtk_m32(eth, mask, set, reg);
697093f9d8SSean Wang 
707093f9d8SSean Wang 	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
71e2c74694SRené van Dorst 		mtk_eth_path_name(path), __func__, updated);
727093f9d8SSean Wang 
737093f9d8SSean Wang 	return 0;
747093f9d8SSean Wang }
757093f9d8SSean Wang 
set_mux_gmac2_gmac0_to_gephy(struct mtk_eth * eth,u64 path)7651a4df60SLorenzo Bianconi static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, u64 path)
777093f9d8SSean Wang {
787093f9d8SSean Wang 	unsigned int val = 0;
797093f9d8SSean Wang 	bool updated = true;
807093f9d8SSean Wang 
817093f9d8SSean Wang 	switch (path) {
827093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC2_GEPHY:
837093f9d8SSean Wang 		val = ~(u32)GEPHY_MAC_SEL;
847093f9d8SSean Wang 		break;
857093f9d8SSean Wang 	default:
867093f9d8SSean Wang 		updated = false;
877093f9d8SSean Wang 		break;
887093f9d8SSean Wang 	}
897093f9d8SSean Wang 
907093f9d8SSean Wang 	if (updated)
917093f9d8SSean Wang 		regmap_update_bits(eth->infra, INFRA_MISC2, GEPHY_MAC_SEL, val);
927093f9d8SSean Wang 
937093f9d8SSean Wang 	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
94e2c74694SRené van Dorst 		mtk_eth_path_name(path), __func__, updated);
957093f9d8SSean Wang 
967093f9d8SSean Wang 	return 0;
977093f9d8SSean Wang }
987093f9d8SSean Wang 
set_mux_u3_gmac2_to_qphy(struct mtk_eth * eth,u64 path)9951a4df60SLorenzo Bianconi static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, u64 path)
1007093f9d8SSean Wang {
101f5d43dddSDaniel Golle 	unsigned int val = 0, mask = 0, reg = 0;
1027093f9d8SSean Wang 	bool updated = true;
1037093f9d8SSean Wang 
1047093f9d8SSean Wang 	switch (path) {
1057093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC2_SGMII:
106f5d43dddSDaniel Golle 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_U3_COPHY_V2)) {
107f5d43dddSDaniel Golle 			reg = USB_PHY_SWITCH_REG;
108f5d43dddSDaniel Golle 			val = SGMII_QPHY_SEL;
109f5d43dddSDaniel Golle 			mask = QPHY_SEL_MASK;
110f5d43dddSDaniel Golle 		} else {
111f5d43dddSDaniel Golle 			reg = INFRA_MISC2;
1127093f9d8SSean Wang 			val = CO_QPHY_SEL;
113f5d43dddSDaniel Golle 			mask = val;
114f5d43dddSDaniel Golle 		}
1157093f9d8SSean Wang 		break;
1167093f9d8SSean Wang 	default:
1177093f9d8SSean Wang 		updated = false;
1187093f9d8SSean Wang 		break;
1197093f9d8SSean Wang 	}
1207093f9d8SSean Wang 
1217093f9d8SSean Wang 	if (updated)
122f5d43dddSDaniel Golle 		regmap_update_bits(eth->infra, reg, mask, val);
1237093f9d8SSean Wang 
1247093f9d8SSean Wang 	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
125e2c74694SRené van Dorst 		mtk_eth_path_name(path), __func__, updated);
1267093f9d8SSean Wang 
1277093f9d8SSean Wang 	return 0;
1287093f9d8SSean Wang }
1297093f9d8SSean Wang 
set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth * eth,u64 path)13051a4df60SLorenzo Bianconi static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path)
1317093f9d8SSean Wang {
1327093f9d8SSean Wang 	unsigned int val = 0;
1337093f9d8SSean Wang 	bool updated = true;
1347093f9d8SSean Wang 
1357093f9d8SSean Wang 	switch (path) {
1367093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC1_SGMII:
1377093f9d8SSean Wang 		val = SYSCFG0_SGMII_GMAC1;
1387093f9d8SSean Wang 		break;
1397093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC2_SGMII:
1407093f9d8SSean Wang 		val = SYSCFG0_SGMII_GMAC2;
1417093f9d8SSean Wang 		break;
1427093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC1_RGMII:
1437093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC2_RGMII:
1447093f9d8SSean Wang 		regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
1457093f9d8SSean Wang 		val &= SYSCFG0_SGMII_MASK;
1467093f9d8SSean Wang 
1477093f9d8SSean Wang 		if ((path == MTK_GMAC1_RGMII && val == SYSCFG0_SGMII_GMAC1) ||
1487093f9d8SSean Wang 		    (path == MTK_GMAC2_RGMII && val == SYSCFG0_SGMII_GMAC2))
1497093f9d8SSean Wang 			val = 0;
1507093f9d8SSean Wang 		else
1517093f9d8SSean Wang 			updated = false;
1527093f9d8SSean Wang 		break;
1537093f9d8SSean Wang 	default:
1547093f9d8SSean Wang 		updated = false;
1557093f9d8SSean Wang 		break;
15611b3412cSYueHaibing 	}
1577093f9d8SSean Wang 
1587093f9d8SSean Wang 	if (updated)
1597093f9d8SSean Wang 		regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
1607093f9d8SSean Wang 				   SYSCFG0_SGMII_MASK, val);
1617093f9d8SSean Wang 
1627093f9d8SSean Wang 	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
163e2c74694SRené van Dorst 		mtk_eth_path_name(path), __func__, updated);
1647093f9d8SSean Wang 
1657093f9d8SSean Wang 	return 0;
1667093f9d8SSean Wang }
1677093f9d8SSean Wang 
set_mux_gmac12_to_gephy_sgmii(struct mtk_eth * eth,u64 path)16851a4df60SLorenzo Bianconi static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, u64 path)
1697093f9d8SSean Wang {
1707093f9d8SSean Wang 	unsigned int val = 0;
1717093f9d8SSean Wang 	bool updated = true;
1727093f9d8SSean Wang 
1737093f9d8SSean Wang 	regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
1747093f9d8SSean Wang 
1757093f9d8SSean Wang 	switch (path) {
1767093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC1_SGMII:
1777093f9d8SSean Wang 		val |= SYSCFG0_SGMII_GMAC1_V2;
1787093f9d8SSean Wang 		break;
1797093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC2_GEPHY:
1807093f9d8SSean Wang 		val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2;
1817093f9d8SSean Wang 		break;
1827093f9d8SSean Wang 	case MTK_ETH_PATH_GMAC2_SGMII:
1837093f9d8SSean Wang 		val |= SYSCFG0_SGMII_GMAC2_V2;
1847093f9d8SSean Wang 		break;
1857093f9d8SSean Wang 	default:
1867093f9d8SSean Wang 		updated = false;
18711b3412cSYueHaibing 	}
1887093f9d8SSean Wang 
1897093f9d8SSean Wang 	if (updated)
1907093f9d8SSean Wang 		regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
1917093f9d8SSean Wang 				   SYSCFG0_SGMII_MASK, val);
1927093f9d8SSean Wang 
1937093f9d8SSean Wang 	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
194e2c74694SRené van Dorst 		mtk_eth_path_name(path), __func__, updated);
1957093f9d8SSean Wang 
1967093f9d8SSean Wang 	return 0;
1977093f9d8SSean Wang }
1987093f9d8SSean Wang 
1997093f9d8SSean Wang static const struct mtk_eth_muxc mtk_eth_muxc[] = {
200e2c74694SRené van Dorst 	{
201e2c74694SRené van Dorst 		.name = "mux_gdm1_to_gmac1_esw",
202e2c74694SRené van Dorst 		.cap_bit = MTK_ETH_MUX_GDM1_TO_GMAC1_ESW,
203e2c74694SRené van Dorst 		.set_path = set_mux_gdm1_to_gmac1_esw,
204e2c74694SRené van Dorst 	}, {
205e2c74694SRené van Dorst 		.name = "mux_gmac2_gmac0_to_gephy",
206e2c74694SRené van Dorst 		.cap_bit = MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY,
207e2c74694SRené van Dorst 		.set_path = set_mux_gmac2_gmac0_to_gephy,
208e2c74694SRené van Dorst 	}, {
209e2c74694SRené van Dorst 		.name = "mux_u3_gmac2_to_qphy",
210e2c74694SRené van Dorst 		.cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY,
211e2c74694SRené van Dorst 		.set_path = set_mux_u3_gmac2_to_qphy,
212e2c74694SRené van Dorst 	}, {
213e2c74694SRené van Dorst 		.name = "mux_gmac1_gmac2_to_sgmii_rgmii",
214e2c74694SRené van Dorst 		.cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII,
215e2c74694SRené van Dorst 		.set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii,
216e2c74694SRené van Dorst 	}, {
217e2c74694SRené van Dorst 		.name = "mux_gmac12_to_gephy_sgmii",
218e2c74694SRené van Dorst 		.cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII,
219e2c74694SRené van Dorst 		.set_path = set_mux_gmac12_to_gephy_sgmii,
220e2c74694SRené van Dorst 	},
2217093f9d8SSean Wang };
2227093f9d8SSean Wang 
mtk_eth_mux_setup(struct mtk_eth * eth,u64 path)22351a4df60SLorenzo Bianconi static int mtk_eth_mux_setup(struct mtk_eth *eth, u64 path)
2247093f9d8SSean Wang {
2257093f9d8SSean Wang 	int i, err = 0;
2267093f9d8SSean Wang 
227e2c74694SRené van Dorst 	if (!MTK_HAS_CAPS(eth->soc->caps, path)) {
2287093f9d8SSean Wang 		dev_err(eth->dev, "path %s isn't support on the SoC\n",
229e2c74694SRené van Dorst 			mtk_eth_path_name(path));
2307093f9d8SSean Wang 		return -EINVAL;
2317093f9d8SSean Wang 	}
2327093f9d8SSean Wang 
2337093f9d8SSean Wang 	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_MUX))
2347093f9d8SSean Wang 		return 0;
2357093f9d8SSean Wang 
2367093f9d8SSean Wang 	/* Setup MUX in path fabric */
237e2c74694SRené van Dorst 	for (i = 0; i < ARRAY_SIZE(mtk_eth_muxc); i++) {
238e2c74694SRené van Dorst 		if (MTK_HAS_CAPS(eth->soc->caps, mtk_eth_muxc[i].cap_bit)) {
2397093f9d8SSean Wang 			err = mtk_eth_muxc[i].set_path(eth, path);
2407093f9d8SSean Wang 			if (err)
2417093f9d8SSean Wang 				goto out;
2427093f9d8SSean Wang 		} else {
2437093f9d8SSean Wang 			dev_dbg(eth->dev, "mux %s isn't present on the SoC\n",
244e2c74694SRené van Dorst 				mtk_eth_muxc[i].name);
2457093f9d8SSean Wang 		}
2467093f9d8SSean Wang 	}
2477093f9d8SSean Wang 
2487093f9d8SSean Wang out:
2497093f9d8SSean Wang 	return err;
2507093f9d8SSean Wang }
2517093f9d8SSean Wang 
mtk_gmac_sgmii_path_setup(struct mtk_eth * eth,int mac_id)2527e538372SRené van Dorst int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id)
2537093f9d8SSean Wang {
25451a4df60SLorenzo Bianconi 	u64 path;
2557093f9d8SSean Wang 
2567093f9d8SSean Wang 	path = (mac_id == 0) ?  MTK_ETH_PATH_GMAC1_SGMII :
2577093f9d8SSean Wang 				MTK_ETH_PATH_GMAC2_SGMII;
2587093f9d8SSean Wang 
2597093f9d8SSean Wang 	/* Setup proper MUXes along the path */
260a76b6b1fSZheng Yongjun 	return mtk_eth_mux_setup(eth, path);
2617093f9d8SSean Wang }
2627093f9d8SSean Wang 
mtk_gmac_gephy_path_setup(struct mtk_eth * eth,int mac_id)2637e538372SRené van Dorst int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id)
2647093f9d8SSean Wang {
26551a4df60SLorenzo Bianconi 	u64 path = 0;
2667093f9d8SSean Wang 
2677093f9d8SSean Wang 	if (mac_id == 1)
2687093f9d8SSean Wang 		path = MTK_ETH_PATH_GMAC2_GEPHY;
2697093f9d8SSean Wang 
2707093f9d8SSean Wang 	if (!path)
2717093f9d8SSean Wang 		return -EINVAL;
2727093f9d8SSean Wang 
2737093f9d8SSean Wang 	/* Setup proper MUXes along the path */
274bb7eae6dSZheng Yongjun 	return mtk_eth_mux_setup(eth, path);
2757093f9d8SSean Wang }
2767093f9d8SSean Wang 
mtk_gmac_rgmii_path_setup(struct mtk_eth * eth,int mac_id)2777e538372SRené van Dorst int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id)
2787093f9d8SSean Wang {
27951a4df60SLorenzo Bianconi 	u64 path;
2807093f9d8SSean Wang 
2817093f9d8SSean Wang 	path = (mac_id == 0) ?  MTK_ETH_PATH_GMAC1_RGMII :
2827093f9d8SSean Wang 				MTK_ETH_PATH_GMAC2_RGMII;
2837093f9d8SSean Wang 
2847093f9d8SSean Wang 	/* Setup proper MUXes along the path */
285bb7eae6dSZheng Yongjun 	return mtk_eth_mux_setup(eth, path);
2867093f9d8SSean Wang }
2877093f9d8SSean Wang 
288