xref: /openbmc/linux/drivers/net/dsa/sja1105/sja1105_mdio.c (revision 5a8f09748ee79f2ef28e560bd095587a0e204b3d)
1*5a8f0974SVladimir Oltean // SPDX-License-Identifier: GPL-2.0
2*5a8f0974SVladimir Oltean /* Copyright 2021, NXP Semiconductors
3*5a8f0974SVladimir Oltean  */
4*5a8f0974SVladimir Oltean #include <linux/of_mdio.h>
5*5a8f0974SVladimir Oltean #include "sja1105.h"
6*5a8f0974SVladimir Oltean 
7*5a8f0974SVladimir Oltean enum sja1105_mdio_opcode {
8*5a8f0974SVladimir Oltean 	SJA1105_C45_ADDR = 0,
9*5a8f0974SVladimir Oltean 	SJA1105_C22 = 1,
10*5a8f0974SVladimir Oltean 	SJA1105_C45_DATA = 2,
11*5a8f0974SVladimir Oltean 	SJA1105_C45_DATA_AUTOINC = 3,
12*5a8f0974SVladimir Oltean };
13*5a8f0974SVladimir Oltean 
14*5a8f0974SVladimir Oltean static u64 sja1105_base_t1_encode_addr(struct sja1105_private *priv,
15*5a8f0974SVladimir Oltean 				       int phy, enum sja1105_mdio_opcode op,
16*5a8f0974SVladimir Oltean 				       int xad)
17*5a8f0974SVladimir Oltean {
18*5a8f0974SVladimir Oltean 	const struct sja1105_regs *regs = priv->info->regs;
19*5a8f0974SVladimir Oltean 
20*5a8f0974SVladimir Oltean 	return regs->mdio_100base_t1 | (phy << 7) | (op << 5) | (xad << 0);
21*5a8f0974SVladimir Oltean }
22*5a8f0974SVladimir Oltean 
23*5a8f0974SVladimir Oltean static int sja1105_base_t1_mdio_read(struct mii_bus *bus, int phy, int reg)
24*5a8f0974SVladimir Oltean {
25*5a8f0974SVladimir Oltean 	struct sja1105_mdio_private *mdio_priv = bus->priv;
26*5a8f0974SVladimir Oltean 	struct sja1105_private *priv = mdio_priv->priv;
27*5a8f0974SVladimir Oltean 	u64 addr;
28*5a8f0974SVladimir Oltean 	u32 tmp;
29*5a8f0974SVladimir Oltean 	int rc;
30*5a8f0974SVladimir Oltean 
31*5a8f0974SVladimir Oltean 	if (reg & MII_ADDR_C45) {
32*5a8f0974SVladimir Oltean 		u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
33*5a8f0974SVladimir Oltean 
34*5a8f0974SVladimir Oltean 		addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR,
35*5a8f0974SVladimir Oltean 						   mmd);
36*5a8f0974SVladimir Oltean 
37*5a8f0974SVladimir Oltean 		tmp = reg & MII_REGADDR_C45_MASK;
38*5a8f0974SVladimir Oltean 
39*5a8f0974SVladimir Oltean 		rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
40*5a8f0974SVladimir Oltean 		if (rc < 0)
41*5a8f0974SVladimir Oltean 			return rc;
42*5a8f0974SVladimir Oltean 
43*5a8f0974SVladimir Oltean 		addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA,
44*5a8f0974SVladimir Oltean 						   mmd);
45*5a8f0974SVladimir Oltean 
46*5a8f0974SVladimir Oltean 		rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
47*5a8f0974SVladimir Oltean 		if (rc < 0)
48*5a8f0974SVladimir Oltean 			return rc;
49*5a8f0974SVladimir Oltean 
50*5a8f0974SVladimir Oltean 		return tmp & 0xffff;
51*5a8f0974SVladimir Oltean 	}
52*5a8f0974SVladimir Oltean 
53*5a8f0974SVladimir Oltean 	/* Clause 22 read */
54*5a8f0974SVladimir Oltean 	addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f);
55*5a8f0974SVladimir Oltean 
56*5a8f0974SVladimir Oltean 	rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
57*5a8f0974SVladimir Oltean 	if (rc < 0)
58*5a8f0974SVladimir Oltean 		return rc;
59*5a8f0974SVladimir Oltean 
60*5a8f0974SVladimir Oltean 	return tmp & 0xffff;
61*5a8f0974SVladimir Oltean }
62*5a8f0974SVladimir Oltean 
63*5a8f0974SVladimir Oltean static int sja1105_base_t1_mdio_write(struct mii_bus *bus, int phy, int reg,
64*5a8f0974SVladimir Oltean 				      u16 val)
65*5a8f0974SVladimir Oltean {
66*5a8f0974SVladimir Oltean 	struct sja1105_mdio_private *mdio_priv = bus->priv;
67*5a8f0974SVladimir Oltean 	struct sja1105_private *priv = mdio_priv->priv;
68*5a8f0974SVladimir Oltean 	u64 addr;
69*5a8f0974SVladimir Oltean 	u32 tmp;
70*5a8f0974SVladimir Oltean 	int rc;
71*5a8f0974SVladimir Oltean 
72*5a8f0974SVladimir Oltean 	if (reg & MII_ADDR_C45) {
73*5a8f0974SVladimir Oltean 		u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
74*5a8f0974SVladimir Oltean 
75*5a8f0974SVladimir Oltean 		addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR,
76*5a8f0974SVladimir Oltean 						   mmd);
77*5a8f0974SVladimir Oltean 
78*5a8f0974SVladimir Oltean 		tmp = reg & MII_REGADDR_C45_MASK;
79*5a8f0974SVladimir Oltean 
80*5a8f0974SVladimir Oltean 		rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
81*5a8f0974SVladimir Oltean 		if (rc < 0)
82*5a8f0974SVladimir Oltean 			return rc;
83*5a8f0974SVladimir Oltean 
84*5a8f0974SVladimir Oltean 		addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA,
85*5a8f0974SVladimir Oltean 						   mmd);
86*5a8f0974SVladimir Oltean 
87*5a8f0974SVladimir Oltean 		tmp = val & 0xffff;
88*5a8f0974SVladimir Oltean 
89*5a8f0974SVladimir Oltean 		rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
90*5a8f0974SVladimir Oltean 		if (rc < 0)
91*5a8f0974SVladimir Oltean 			return rc;
92*5a8f0974SVladimir Oltean 
93*5a8f0974SVladimir Oltean 		return 0;
94*5a8f0974SVladimir Oltean 	}
95*5a8f0974SVladimir Oltean 
96*5a8f0974SVladimir Oltean 	/* Clause 22 write */
97*5a8f0974SVladimir Oltean 	addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f);
98*5a8f0974SVladimir Oltean 
99*5a8f0974SVladimir Oltean 	tmp = val & 0xffff;
100*5a8f0974SVladimir Oltean 
101*5a8f0974SVladimir Oltean 	return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
102*5a8f0974SVladimir Oltean }
103*5a8f0974SVladimir Oltean 
104*5a8f0974SVladimir Oltean static int sja1105_base_tx_mdio_read(struct mii_bus *bus, int phy, int reg)
105*5a8f0974SVladimir Oltean {
106*5a8f0974SVladimir Oltean 	struct sja1105_mdio_private *mdio_priv = bus->priv;
107*5a8f0974SVladimir Oltean 	struct sja1105_private *priv = mdio_priv->priv;
108*5a8f0974SVladimir Oltean 	const struct sja1105_regs *regs = priv->info->regs;
109*5a8f0974SVladimir Oltean 	u32 tmp;
110*5a8f0974SVladimir Oltean 	int rc;
111*5a8f0974SVladimir Oltean 
112*5a8f0974SVladimir Oltean 	rc = sja1105_xfer_u32(priv, SPI_READ, regs->mdio_100base_tx + reg,
113*5a8f0974SVladimir Oltean 			      &tmp, NULL);
114*5a8f0974SVladimir Oltean 	if (rc < 0)
115*5a8f0974SVladimir Oltean 		return rc;
116*5a8f0974SVladimir Oltean 
117*5a8f0974SVladimir Oltean 	return tmp & 0xffff;
118*5a8f0974SVladimir Oltean }
119*5a8f0974SVladimir Oltean 
120*5a8f0974SVladimir Oltean static int sja1105_base_tx_mdio_write(struct mii_bus *bus, int phy, int reg,
121*5a8f0974SVladimir Oltean 				      u16 val)
122*5a8f0974SVladimir Oltean {
123*5a8f0974SVladimir Oltean 	struct sja1105_mdio_private *mdio_priv = bus->priv;
124*5a8f0974SVladimir Oltean 	struct sja1105_private *priv = mdio_priv->priv;
125*5a8f0974SVladimir Oltean 	const struct sja1105_regs *regs = priv->info->regs;
126*5a8f0974SVladimir Oltean 	u32 tmp = val;
127*5a8f0974SVladimir Oltean 
128*5a8f0974SVladimir Oltean 	return sja1105_xfer_u32(priv, SPI_WRITE, regs->mdio_100base_tx + reg,
129*5a8f0974SVladimir Oltean 				&tmp, NULL);
130*5a8f0974SVladimir Oltean }
131*5a8f0974SVladimir Oltean 
132*5a8f0974SVladimir Oltean static int sja1105_mdiobus_base_tx_register(struct sja1105_private *priv,
133*5a8f0974SVladimir Oltean 					    struct device_node *mdio_node)
134*5a8f0974SVladimir Oltean {
135*5a8f0974SVladimir Oltean 	struct sja1105_mdio_private *mdio_priv;
136*5a8f0974SVladimir Oltean 	struct device_node *np;
137*5a8f0974SVladimir Oltean 	struct mii_bus *bus;
138*5a8f0974SVladimir Oltean 	int rc = 0;
139*5a8f0974SVladimir Oltean 
140*5a8f0974SVladimir Oltean 	np = of_find_compatible_node(mdio_node, NULL,
141*5a8f0974SVladimir Oltean 				     "nxp,sja1110-base-tx-mdio");
142*5a8f0974SVladimir Oltean 	if (!np)
143*5a8f0974SVladimir Oltean 		return 0;
144*5a8f0974SVladimir Oltean 
145*5a8f0974SVladimir Oltean 	if (!of_device_is_available(np))
146*5a8f0974SVladimir Oltean 		goto out_put_np;
147*5a8f0974SVladimir Oltean 
148*5a8f0974SVladimir Oltean 	bus = mdiobus_alloc_size(sizeof(*mdio_priv));
149*5a8f0974SVladimir Oltean 	if (!bus) {
150*5a8f0974SVladimir Oltean 		rc = -ENOMEM;
151*5a8f0974SVladimir Oltean 		goto out_put_np;
152*5a8f0974SVladimir Oltean 	}
153*5a8f0974SVladimir Oltean 
154*5a8f0974SVladimir Oltean 	bus->name = "SJA1110 100base-TX MDIO bus";
155*5a8f0974SVladimir Oltean 	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-tx",
156*5a8f0974SVladimir Oltean 		 dev_name(priv->ds->dev));
157*5a8f0974SVladimir Oltean 	bus->read = sja1105_base_tx_mdio_read;
158*5a8f0974SVladimir Oltean 	bus->write = sja1105_base_tx_mdio_write;
159*5a8f0974SVladimir Oltean 	bus->parent = priv->ds->dev;
160*5a8f0974SVladimir Oltean 	mdio_priv = bus->priv;
161*5a8f0974SVladimir Oltean 	mdio_priv->priv = priv;
162*5a8f0974SVladimir Oltean 
163*5a8f0974SVladimir Oltean 	rc = of_mdiobus_register(bus, np);
164*5a8f0974SVladimir Oltean 	if (rc) {
165*5a8f0974SVladimir Oltean 		mdiobus_free(bus);
166*5a8f0974SVladimir Oltean 		goto out_put_np;
167*5a8f0974SVladimir Oltean 	}
168*5a8f0974SVladimir Oltean 
169*5a8f0974SVladimir Oltean 	priv->mdio_base_tx = bus;
170*5a8f0974SVladimir Oltean 
171*5a8f0974SVladimir Oltean out_put_np:
172*5a8f0974SVladimir Oltean 	of_node_put(np);
173*5a8f0974SVladimir Oltean 
174*5a8f0974SVladimir Oltean 	return 0;
175*5a8f0974SVladimir Oltean }
176*5a8f0974SVladimir Oltean 
177*5a8f0974SVladimir Oltean static void sja1105_mdiobus_base_tx_unregister(struct sja1105_private *priv)
178*5a8f0974SVladimir Oltean {
179*5a8f0974SVladimir Oltean 	if (!priv->mdio_base_tx)
180*5a8f0974SVladimir Oltean 		return;
181*5a8f0974SVladimir Oltean 
182*5a8f0974SVladimir Oltean 	mdiobus_unregister(priv->mdio_base_tx);
183*5a8f0974SVladimir Oltean 	mdiobus_free(priv->mdio_base_tx);
184*5a8f0974SVladimir Oltean 	priv->mdio_base_tx = NULL;
185*5a8f0974SVladimir Oltean }
186*5a8f0974SVladimir Oltean 
187*5a8f0974SVladimir Oltean static int sja1105_mdiobus_base_t1_register(struct sja1105_private *priv,
188*5a8f0974SVladimir Oltean 					    struct device_node *mdio_node)
189*5a8f0974SVladimir Oltean {
190*5a8f0974SVladimir Oltean 	struct sja1105_mdio_private *mdio_priv;
191*5a8f0974SVladimir Oltean 	struct device_node *np;
192*5a8f0974SVladimir Oltean 	struct mii_bus *bus;
193*5a8f0974SVladimir Oltean 	int rc = 0;
194*5a8f0974SVladimir Oltean 
195*5a8f0974SVladimir Oltean 	np = of_find_compatible_node(mdio_node, NULL,
196*5a8f0974SVladimir Oltean 				     "nxp,sja1110-base-t1-mdio");
197*5a8f0974SVladimir Oltean 	if (!np)
198*5a8f0974SVladimir Oltean 		return 0;
199*5a8f0974SVladimir Oltean 
200*5a8f0974SVladimir Oltean 	if (!of_device_is_available(np))
201*5a8f0974SVladimir Oltean 		goto out_put_np;
202*5a8f0974SVladimir Oltean 
203*5a8f0974SVladimir Oltean 	bus = mdiobus_alloc_size(sizeof(*mdio_priv));
204*5a8f0974SVladimir Oltean 	if (!bus) {
205*5a8f0974SVladimir Oltean 		rc = -ENOMEM;
206*5a8f0974SVladimir Oltean 		goto out_put_np;
207*5a8f0974SVladimir Oltean 	}
208*5a8f0974SVladimir Oltean 
209*5a8f0974SVladimir Oltean 	bus->name = "SJA1110 100base-T1 MDIO bus";
210*5a8f0974SVladimir Oltean 	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-t1",
211*5a8f0974SVladimir Oltean 		 dev_name(priv->ds->dev));
212*5a8f0974SVladimir Oltean 	bus->read = sja1105_base_t1_mdio_read;
213*5a8f0974SVladimir Oltean 	bus->write = sja1105_base_t1_mdio_write;
214*5a8f0974SVladimir Oltean 	bus->parent = priv->ds->dev;
215*5a8f0974SVladimir Oltean 	mdio_priv = bus->priv;
216*5a8f0974SVladimir Oltean 	mdio_priv->priv = priv;
217*5a8f0974SVladimir Oltean 
218*5a8f0974SVladimir Oltean 	rc = of_mdiobus_register(bus, np);
219*5a8f0974SVladimir Oltean 	if (rc) {
220*5a8f0974SVladimir Oltean 		mdiobus_free(bus);
221*5a8f0974SVladimir Oltean 		goto out_put_np;
222*5a8f0974SVladimir Oltean 	}
223*5a8f0974SVladimir Oltean 
224*5a8f0974SVladimir Oltean 	priv->mdio_base_t1 = bus;
225*5a8f0974SVladimir Oltean 
226*5a8f0974SVladimir Oltean out_put_np:
227*5a8f0974SVladimir Oltean 	of_node_put(np);
228*5a8f0974SVladimir Oltean 
229*5a8f0974SVladimir Oltean 	return rc;
230*5a8f0974SVladimir Oltean }
231*5a8f0974SVladimir Oltean 
232*5a8f0974SVladimir Oltean static void sja1105_mdiobus_base_t1_unregister(struct sja1105_private *priv)
233*5a8f0974SVladimir Oltean {
234*5a8f0974SVladimir Oltean 	if (!priv->mdio_base_t1)
235*5a8f0974SVladimir Oltean 		return;
236*5a8f0974SVladimir Oltean 
237*5a8f0974SVladimir Oltean 	mdiobus_unregister(priv->mdio_base_t1);
238*5a8f0974SVladimir Oltean 	mdiobus_free(priv->mdio_base_t1);
239*5a8f0974SVladimir Oltean 	priv->mdio_base_t1 = NULL;
240*5a8f0974SVladimir Oltean }
241*5a8f0974SVladimir Oltean 
242*5a8f0974SVladimir Oltean int sja1105_mdiobus_register(struct dsa_switch *ds)
243*5a8f0974SVladimir Oltean {
244*5a8f0974SVladimir Oltean 	struct sja1105_private *priv = ds->priv;
245*5a8f0974SVladimir Oltean 	const struct sja1105_regs *regs = priv->info->regs;
246*5a8f0974SVladimir Oltean 	struct device_node *switch_node = ds->dev->of_node;
247*5a8f0974SVladimir Oltean 	struct device_node *mdio_node;
248*5a8f0974SVladimir Oltean 	int rc;
249*5a8f0974SVladimir Oltean 
250*5a8f0974SVladimir Oltean 	mdio_node = of_get_child_by_name(switch_node, "mdios");
251*5a8f0974SVladimir Oltean 	if (!mdio_node)
252*5a8f0974SVladimir Oltean 		return 0;
253*5a8f0974SVladimir Oltean 
254*5a8f0974SVladimir Oltean 	if (!of_device_is_available(mdio_node))
255*5a8f0974SVladimir Oltean 		goto out_put_mdio_node;
256*5a8f0974SVladimir Oltean 
257*5a8f0974SVladimir Oltean 	if (regs->mdio_100base_tx != SJA1105_RSV_ADDR) {
258*5a8f0974SVladimir Oltean 		rc = sja1105_mdiobus_base_tx_register(priv, mdio_node);
259*5a8f0974SVladimir Oltean 		if (rc)
260*5a8f0974SVladimir Oltean 			goto err_put_mdio_node;
261*5a8f0974SVladimir Oltean 	}
262*5a8f0974SVladimir Oltean 
263*5a8f0974SVladimir Oltean 	if (regs->mdio_100base_t1 != SJA1105_RSV_ADDR) {
264*5a8f0974SVladimir Oltean 		rc = sja1105_mdiobus_base_t1_register(priv, mdio_node);
265*5a8f0974SVladimir Oltean 		if (rc)
266*5a8f0974SVladimir Oltean 			goto err_free_base_tx_mdiobus;
267*5a8f0974SVladimir Oltean 	}
268*5a8f0974SVladimir Oltean 
269*5a8f0974SVladimir Oltean out_put_mdio_node:
270*5a8f0974SVladimir Oltean 	of_node_put(mdio_node);
271*5a8f0974SVladimir Oltean 
272*5a8f0974SVladimir Oltean 	return 0;
273*5a8f0974SVladimir Oltean 
274*5a8f0974SVladimir Oltean err_free_base_tx_mdiobus:
275*5a8f0974SVladimir Oltean 	sja1105_mdiobus_base_tx_unregister(priv);
276*5a8f0974SVladimir Oltean err_put_mdio_node:
277*5a8f0974SVladimir Oltean 	of_node_put(mdio_node);
278*5a8f0974SVladimir Oltean 
279*5a8f0974SVladimir Oltean 	return rc;
280*5a8f0974SVladimir Oltean }
281*5a8f0974SVladimir Oltean 
282*5a8f0974SVladimir Oltean void sja1105_mdiobus_unregister(struct dsa_switch *ds)
283*5a8f0974SVladimir Oltean {
284*5a8f0974SVladimir Oltean 	struct sja1105_private *priv = ds->priv;
285*5a8f0974SVladimir Oltean 
286*5a8f0974SVladimir Oltean 	sja1105_mdiobus_base_t1_unregister(priv);
287*5a8f0974SVladimir Oltean 	sja1105_mdiobus_base_tx_unregister(priv);
288*5a8f0974SVladimir Oltean }
289