xref: /linux/drivers/net/dsa/motorcomm/pcs-921x.c (revision cece0b573deb5d102547835da720f60d7b7888cc)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2026 David Yang
4  */
5 
6 #include "chip.h"
7 #include "mdio_bus.h"
8 #include "pcs.h"
9 #include "smi.h"
10 
11 static int
12 yt921x_serdes_config(struct yt921x_priv *priv, int port, unsigned int neg_mode,
13 		     phy_interface_t interface,
14 		     const unsigned long *advertising, bool permit_pause_to_mac)
15 {
16 	bool inband = neg_mode & PHYLINK_PCS_NEG_INBAND;
17 	struct yt921x_port *pp = &priv->ports[port];
18 	bool changed = false;
19 	u16 bmcr;
20 	u32 mask;
21 	u32 ctrl;
22 	u16 val;
23 	int adv;
24 	int res;
25 
26 	switch (interface) {
27 	case PHY_INTERFACE_MODE_SGMII:
28 		ctrl = YT921X_SERDES_MODE_SGMII;
29 		break;
30 	case PHY_INTERFACE_MODE_100BASEX:
31 		ctrl = YT921X_SERDES_MODE_100BASEX;
32 		break;
33 	case PHY_INTERFACE_MODE_1000BASEX:
34 		ctrl = YT921X_SERDES_MODE_1000BASEX;
35 		break;
36 	case PHY_INTERFACE_MODE_2500BASEX:
37 		ctrl = YT921X_SERDES_MODE_2500BASEX;
38 		break;
39 	default:
40 		return -EOPNOTSUPP;
41 	}
42 
43 	res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
44 				     YT921X_SERDES_MODE_M, ctrl);
45 	if (res)
46 		return res;
47 
48 	mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_CTRL_PAUSE_AN;
49 	ctrl = 0;
50 	if (inband)
51 		ctrl |= YT921X_PORT_CTRL_LINK_AN;
52 	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED && permit_pause_to_mac &&
53 	    interface != PHY_INTERFACE_MODE_SGMII)
54 		ctrl |= YT921X_PORT_CTRL_PAUSE_AN;
55 	res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl);
56 	if (res)
57 		return res;
58 
59 	if (inband) {
60 		res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port),
61 					    YT921X_SERDES_LINK);
62 		if (res)
63 			return res;
64 	}
65 
66 	adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising);
67 	if (adv >= 0) {
68 		res = yt921x_intif_modify_changed(priv, port, MII_ADVERTISE,
69 						  U16_MAX, adv);
70 		if (res < 0)
71 			return res;
72 
73 		changed = !!res;
74 	}
75 
76 	res = yt921x_intif_read(priv, port, MII_BMCR, &val);
77 	if (res)
78 		return res;
79 
80 	bmcr = val;
81 	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
82 		bmcr |= BMCR_ANENABLE;
83 	else
84 		bmcr &= ~BMCR_ANENABLE;
85 
86 	/* If the ANENABLE bit was changed, the PHY will restart negotiation,
87 	 * so we don't need to flag a change to trigger its own restart.
88 	 */
89 	if (bmcr != val)
90 		changed = false;
91 
92 	bmcr &= ~BMCR_ISOLATE;
93 	res = yt921x_intif_write(priv, port, MII_BMCR, bmcr);
94 	if (res)
95 		return res;
96 
97 	pp->inband = inband;
98 	return changed;
99 }
100 
101 static unsigned int
102 yt921x_phylink_pcs_inband_caps(struct phylink_pcs *pcs,
103 			       phy_interface_t interface)
104 {
105 	if (interface == PHY_INTERFACE_MODE_100BASEX)
106 		return LINK_INBAND_DISABLE;
107 
108 	return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
109 }
110 
111 static void
112 yt921x_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
113 			     struct phylink_link_state *state)
114 {
115 	struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
116 	struct yt921x_priv *priv = yt921x_port_to_priv(pp);
117 	struct device *dev = yt921x_priv_to_device(priv);
118 	int port = pp->index;
119 	u16 bmsr;
120 	u16 lpa;
121 	int res;
122 
123 	mutex_lock(&priv->reg_lock);
124 
125 	res = yt921x_intif_read(priv, port, MII_BMSR, &bmsr);
126 	if (res)
127 		goto end;
128 
129 	res = yt921x_intif_read(priv, port, MII_LPA, &lpa);
130 
131 end:
132 	mutex_unlock(&priv->reg_lock);
133 
134 	if (res) {
135 		dev_err_ratelimited(dev, "Failed to %s PCS port %d: %i\n",
136 				    "get state of", port, res);
137 		state->link = false;
138 		return;
139 	}
140 
141 	if (state->interface == PHY_INTERFACE_MODE_100BASEX) {
142 		state->speed = SPEED_100;
143 		state->duplex = DUPLEX_FULL;
144 		state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX;
145 		state->link = !!(bmsr & BMSR_LSTATUS);
146 		return;
147 	}
148 
149 	phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa);
150 }
151 
152 static void yt921x_phylink_pcs_an_restart(struct phylink_pcs *pcs)
153 {
154 	struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
155 	struct yt921x_priv *priv = yt921x_port_to_priv(pp);
156 	struct device *dev = yt921x_priv_to_device(priv);
157 	int port = pp->index;
158 	u16 val;
159 	int res;
160 
161 	mutex_lock(&priv->reg_lock);
162 
163 	res = yt921x_intif_read(priv, port, MII_BMCR, &val);
164 	if (res)
165 		goto end;
166 
167 	val |= BMCR_ANRESTART;
168 	res = yt921x_intif_write(priv, port, MII_BMCR, val);
169 
170 end:
171 	mutex_unlock(&priv->reg_lock);
172 
173 	if (res)
174 		dev_err(dev, "Failed to %s PCS port %d: %i\n", "restart",
175 			port, res);
176 }
177 
178 static void yt921x_phylink_pcs_disable(struct phylink_pcs *pcs)
179 {
180 	struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
181 	struct yt921x_priv *priv = yt921x_port_to_priv(pp);
182 	struct device *dev = yt921x_priv_to_device(priv);
183 	int port = pp->index;
184 	int res;
185 
186 	mutex_lock(&priv->reg_lock);
187 	res = yt921x_intif_modify(priv, port, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN);
188 	mutex_unlock(&priv->reg_lock);
189 
190 	if (res)
191 		dev_err(dev, "Failed to %s PCS port %d: %i\n", "power down",
192 			port, res);
193 }
194 
195 static int yt921x_phylink_pcs_enable(struct phylink_pcs *pcs)
196 {
197 	struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
198 	struct yt921x_priv *priv = yt921x_port_to_priv(pp);
199 	int port = pp->index;
200 	u16 val;
201 	int res;
202 
203 	mutex_lock(&priv->reg_lock);
204 
205 	res = yt921x_intif_read(priv, port, MII_BMCR, &val);
206 	if (res)
207 		goto end;
208 
209 	val &= ~BMCR_PDOWN;
210 	val |= BMCR_ANRESTART;
211 	res = yt921x_intif_write(priv, port, MII_BMCR, val);
212 
213 end:
214 	mutex_unlock(&priv->reg_lock);
215 
216 	return res;
217 }
218 
219 static int
220 yt921x_phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
221 			  phy_interface_t interface,
222 			  const unsigned long *advertising,
223 			  bool permit_pause_to_mac)
224 {
225 	struct yt921x_port *pp = pcs_to_yt921x_port(pcs);
226 	struct yt921x_priv *priv = yt921x_port_to_priv(pp);
227 	int port = pp->index;
228 	int res;
229 
230 	mutex_lock(&priv->reg_lock);
231 	res = yt921x_serdes_config(priv, port, neg_mode, interface,
232 				   advertising, permit_pause_to_mac);
233 	mutex_unlock(&priv->reg_lock);
234 
235 	return res;
236 }
237 
238 const struct phylink_pcs_ops yt921x_phylink_pcs_ops = {
239 	.pcs_inband_caps	= yt921x_phylink_pcs_inband_caps,
240 	.pcs_get_state		= yt921x_phylink_pcs_get_state,
241 	.pcs_an_restart		= yt921x_phylink_pcs_an_restart,
242 	.pcs_disable		= yt921x_phylink_pcs_disable,
243 	.pcs_enable		= yt921x_phylink_pcs_enable,
244 	.pcs_config		= yt921x_phylink_pcs_config,
245 };
246