1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018-2019 MediaTek Inc.
3 
4 /* A library for MediaTek SGMII circuit
5  *
6  * Author: Sean Wang <sean.wang@mediatek.com>
7  *
8  */
9 
10 #include <linux/mfd/syscon.h>
11 #include <linux/of.h>
12 #include <linux/phylink.h>
13 #include <linux/regmap.h>
14 
15 #include "mtk_eth_soc.h"
16 
pcs_to_mtk_pcs(struct phylink_pcs * pcs)17 static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
18 {
19 	return container_of(pcs, struct mtk_pcs, pcs);
20 }
21 
22 /* For SGMII interface mode */
mtk_pcs_setup_mode_an(struct mtk_pcs * mpcs)23 static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
24 {
25 	unsigned int val;
26 
27 	/* Setup the link timer and QPHY power up inside SGMIISYS */
28 	regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
29 		     SGMII_LINK_TIMER_DEFAULT);
30 
31 	regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
32 	val |= SGMII_REMOTE_FAULT_DIS;
33 	regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
34 
35 	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
36 	val |= SGMII_AN_RESTART;
37 	regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
38 
39 	regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
40 	val &= ~SGMII_PHYA_PWD;
41 	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
42 
43 	return 0;
44 
45 }
46 
47 /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
48  * fixed speed.
49  */
mtk_pcs_setup_mode_force(struct mtk_pcs * mpcs,phy_interface_t interface)50 static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
51 				    phy_interface_t interface)
52 {
53 	unsigned int val;
54 
55 	regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
56 	val &= ~RG_PHY_SPEED_MASK;
57 	if (interface == PHY_INTERFACE_MODE_2500BASEX)
58 		val |= RG_PHY_SPEED_3_125G;
59 	regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
60 
61 	/* Disable SGMII AN */
62 	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
63 	val &= ~SGMII_AN_ENABLE;
64 	regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
65 
66 	/* Set the speed etc but leave the duplex unchanged */
67 	regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
68 	val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
69 	val |= SGMII_SPEED_1000;
70 	regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
71 
72 	/* Release PHYA power down state */
73 	regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
74 	val &= ~SGMII_PHYA_PWD;
75 	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
76 
77 	return 0;
78 }
79 
mtk_pcs_config(struct phylink_pcs * pcs,unsigned int mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)80 static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
81 			  phy_interface_t interface,
82 			  const unsigned long *advertising,
83 			  bool permit_pause_to_mac)
84 {
85 	struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
86 	int err = 0;
87 
88 	/* Setup SGMIISYS with the determined property */
89 	if (interface != PHY_INTERFACE_MODE_SGMII)
90 		err = mtk_pcs_setup_mode_force(mpcs, interface);
91 	else if (phylink_autoneg_inband(mode))
92 		err = mtk_pcs_setup_mode_an(mpcs);
93 
94 	return err;
95 }
96 
mtk_pcs_restart_an(struct phylink_pcs * pcs)97 static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
98 {
99 	struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
100 	unsigned int val;
101 
102 	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
103 	val |= SGMII_AN_RESTART;
104 	regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
105 }
106 
mtk_pcs_link_up(struct phylink_pcs * pcs,unsigned int mode,phy_interface_t interface,int speed,int duplex)107 static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
108 			    phy_interface_t interface, int speed, int duplex)
109 {
110 	struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
111 	unsigned int val;
112 
113 	if (!phy_interface_mode_is_8023z(interface))
114 		return;
115 
116 	/* SGMII force duplex setting */
117 	regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
118 	val &= ~SGMII_DUPLEX_FULL;
119 	if (duplex == DUPLEX_FULL)
120 		val |= SGMII_DUPLEX_FULL;
121 
122 	regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
123 }
124 
125 static const struct phylink_pcs_ops mtk_pcs_ops = {
126 	.pcs_config = mtk_pcs_config,
127 	.pcs_an_restart = mtk_pcs_restart_an,
128 	.pcs_link_up = mtk_pcs_link_up,
129 };
130 
mtk_sgmii_init(struct mtk_sgmii * ss,struct device_node * r,u32 ana_rgc3)131 int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
132 {
133 	struct device_node *np;
134 	int i;
135 
136 	for (i = 0; i < MTK_MAX_DEVS; i++) {
137 		np = of_parse_phandle(r, "mediatek,sgmiisys", i);
138 		if (!np)
139 			break;
140 
141 		ss->pcs[i].ana_rgc3 = ana_rgc3;
142 		ss->pcs[i].regmap = syscon_node_to_regmap(np);
143 		of_node_put(np);
144 		if (IS_ERR(ss->pcs[i].regmap))
145 			return PTR_ERR(ss->pcs[i].regmap);
146 
147 		ss->pcs[i].pcs.ops = &mtk_pcs_ops;
148 	}
149 
150 	return 0;
151 }
152 
mtk_sgmii_select_pcs(struct mtk_sgmii * ss,int id)153 struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
154 {
155 	if (!ss->pcs[id].regmap)
156 		return NULL;
157 
158 	return &ss->pcs[id].pcs;
159 }
160