xref: /openbsd/sys/dev/fdt/mvsw.c (revision 5a38ef86)
1 /*	$OpenBSD: mvsw.c,v 1.4 2021/09/06 19:55:27 patrick Exp $	*/
2 /*
3  * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/timeout.h>
22 
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_misc.h>
28 #include <dev/ofw/fdt.h>
29 
30 #include <dev/mii/mii.h>
31 
32 /* Registers */
33 
34 /* SMI registers */
35 #define MVSW_SMI_CMD			0x00
36 #define  MVSW_SMI_CMD_BUSY		0x8000
37 #define  MVSW_SMI_CMD_C45		0x0000
38 #define  MVSW_SMI_CMD_C22		0x1000
39 #define  MVSW_SMI_CMD_C22_READ		0x0800
40 #define  MVSW_SMI_CMD_C22_WRITE		0x0400
41 #define  MVSW_SMI_CMD_C45_READ		0x0c00
42 #define  MVSW_SMI_CMD_C45_READINC	0x0800
43 #define  MVSW_SMI_CMD_C45_WRITE		0x0400
44 #define  MVSW_SMI_CMD_C45_ADDR		0x0000
45 #define  MVSW_SMI_CMD_DEVAD(x)		((x) << 5)
46 #define  MVSW_SMI_CMD_REGAD(x)		((x) << 0)
47 #define MVSW_SMI_DATA			0x01
48 
49 #define MVSW_SMI_TIMEOUT	1600
50 
51 /* Switch registers */
52 #define MVSW_PORT(x)			(0x10 + (x))
53 #define MVSW_G2				0x1c
54 
55 #define MVSW_PORT_SWITCHID		0x03
56 #define  MVSW_PORT_SWITCHID_PROD_MASK	0xfff0
57 #define  MVSW_PORT_SWITCHID_PROD_88E6141 0x3400
58 #define  MVSW_PORT_SWITCHID_PROD_88E6341 0x3410
59 #define  MVSW_PORT_SWITCHID_REV_MASK	0x000f
60 #define MVSW_PORT_CTRL			0x04
61 #define  MVSW_PORT_CTRL_STATE_MASK	0x0003
62 #define  MVSW_PORT_CTRL_STATE_FORWARD	0x0003
63 #define MVSW_G2_SMI_PHY_CMD		0x18
64 #define MVSW_G2_SMI_PHY_DATA		0x19
65 
66 /* SERDES registers */
67 #define MVSW_SERDES(x)			(0x10 + (x))
68 #define MVSW_SERDES_BMCR		(0x2000 + MII_BMCR)
69 
70 /* XXX #include <dev/mii/mdio.h> */
71 #define MDIO_MMD_PHYXS		4
72 
73 struct mvsw_softc {
74 	struct device	sc_dev;
75 
76 	struct mii_bus	*sc_mdio;
77 	int 		sc_reg;
78 };
79 
80 int	mvsw_match(struct device *, void *, void *);
81 void	mvsw_attach(struct device *, struct device *, void *);
82 
83 struct cfattach	mvsw_ca = {
84 	sizeof (struct mvsw_softc), mvsw_match, mvsw_attach
85 };
86 
87 struct cfdriver mvsw_cd = {
88 	NULL, "mvsw", DV_DULL
89 };
90 
91 int	mvsw_smi_read(struct mvsw_softc *, int, int);
92 void	mvsw_smi_write(struct mvsw_softc *, int, int, int);
93 int	mvsw_phy_read(struct mvsw_softc *, int, int);
94 void	mvsw_phy_write(struct mvsw_softc *, int, int, int);
95 int	mvsw_serdes_read(struct mvsw_softc *, int, int, int);
96 void	mvsw_serdes_write(struct mvsw_softc *, int, int, int, int);
97 
98 void	mvsw_port_enable(struct mvsw_softc *, int);
99 void	mvsw_phy_enable(struct mvsw_softc *, int);
100 void	mvsw_serdes_enable(struct mvsw_softc *, int);
101 
102 int
103 mvsw_match(struct device *parent, void *match, void *aux)
104 {
105 	struct fdt_attach_args *faa = aux;
106 
107 	return OF_is_compatible(faa->fa_node, "marvell,mv88e6085");
108 }
109 
110 void
111 mvsw_attach(struct device *parent, struct device *self, void *aux)
112 {
113 	struct mvsw_softc *sc = (struct mvsw_softc *)self;
114 	struct fdt_attach_args *faa = aux;
115 	int ports, port, node;
116 	uint32_t phy;
117 	uint16_t swid;
118 
119 	if (faa->fa_nreg < 1) {
120 		printf(": no registers\n");
121 		return;
122 	}
123 
124 	sc->sc_reg = faa->fa_reg[0].addr;
125 	printf(" phy %d", sc->sc_reg);
126 
127 	sc->sc_mdio = mii_bynode(OF_parent(faa->fa_node));
128 	if (sc->sc_mdio == NULL) {
129 		printf(": can't find mdio bus\n");
130 		return;
131 	}
132 
133 	swid = mvsw_smi_read(sc, MVSW_PORT(0), MVSW_PORT_SWITCHID);
134 	switch (swid & MVSW_PORT_SWITCHID_PROD_MASK) {
135 	case MVSW_PORT_SWITCHID_PROD_88E6141:
136 		printf(": 88E6141");
137 		break;
138 	case MVSW_PORT_SWITCHID_PROD_88E6341:
139 		printf(": 88E6341");
140 		break;
141 	default:
142 		printf(": unknown product 0x%04x\n",
143 		   swid & MVSW_PORT_SWITCHID_PROD_MASK);
144 		return;
145 	}
146 	printf(" rev %d\n", swid & MVSW_PORT_SWITCHID_REV_MASK);
147 
148 	ports = OF_getnodebyname(faa->fa_node, "ports");
149 	if (ports == 0)
150 		return;
151 	for (port = OF_child(ports); port; port = OF_peer(port)) {
152 		phy = OF_getpropint(port, "phy-handle", 0);
153 		node = OF_getnodebyphandle(phy);
154 		if (node)
155 			mvsw_phy_enable(sc, node);
156 		else
157 			mvsw_serdes_enable(sc, port);
158 
159 		mvsw_port_enable(sc, port);
160 	}
161 }
162 
163 static inline int
164 mvsw_read(struct mvsw_softc *sc, int reg)
165 {
166 	struct mii_bus *md = sc->sc_mdio;
167 	return md->md_readreg(md->md_cookie, sc->sc_reg, reg);
168 }
169 
170 static inline void
171 mvsw_write(struct mvsw_softc *sc, int reg, int val)
172 {
173 	struct mii_bus *md = sc->sc_mdio;
174 	md->md_writereg(md->md_cookie, sc->sc_reg, reg, val);
175 }
176 
177 int
178 mvsw_smi_wait(struct mvsw_softc *sc)
179 {
180 	int i;
181 
182 	for (i = 0; i < MVSW_SMI_TIMEOUT; i++) {
183 		if ((mvsw_read(sc, MVSW_SMI_CMD) & MVSW_SMI_CMD_BUSY) == 0)
184 			return 0;
185 		delay(10);
186 	}
187 
188 	printf("%s: SMI timeout\n", sc->sc_dev.dv_xname);
189 	return ETIMEDOUT;
190 }
191 
192 int
193 mvsw_smi_read(struct mvsw_softc *sc, int phy, int reg)
194 {
195 	if (mvsw_smi_wait(sc))
196 		return -1;
197 
198 	mvsw_write(sc, MVSW_SMI_CMD, MVSW_SMI_CMD_BUSY |
199 	    MVSW_SMI_CMD_DEVAD(phy) | MVSW_SMI_CMD_REGAD(reg) |
200 	    MVSW_SMI_CMD_C22 | MVSW_SMI_CMD_C22_READ);
201 
202 	if (mvsw_smi_wait(sc))
203 		return -1;
204 
205 	return mvsw_read(sc, MVSW_SMI_DATA);
206 }
207 
208 void
209 mvsw_smi_write(struct mvsw_softc *sc, int phy, int reg, int val)
210 {
211 	if (mvsw_smi_wait(sc))
212 		return;
213 
214 	mvsw_write(sc, MVSW_SMI_DATA, val);
215 	mvsw_write(sc, MVSW_SMI_CMD, MVSW_SMI_CMD_BUSY |
216 	    MVSW_SMI_CMD_DEVAD(phy) | MVSW_SMI_CMD_REGAD(reg) |
217 	    MVSW_SMI_CMD_C22 | MVSW_SMI_CMD_C22_WRITE);
218 
219 	mvsw_smi_wait(sc);
220 }
221 
222 int
223 mvsw_phy_wait(struct mvsw_softc *sc)
224 {
225 	int i;
226 
227 	for (i = 0; i < MVSW_SMI_TIMEOUT; i++) {
228 		if ((mvsw_smi_read(sc, MVSW_G2,
229 		    MVSW_G2_SMI_PHY_CMD) & MVSW_SMI_CMD_BUSY) == 0)
230 			return 0;
231 		delay(10);
232 	}
233 
234 	printf("%s: SMI PHY timeout\n", sc->sc_dev.dv_xname);
235 	return ETIMEDOUT;
236 }
237 
238 int
239 mvsw_phy_read(struct mvsw_softc *sc, int phy, int reg)
240 {
241 	if (mvsw_phy_wait(sc))
242 		return -1;
243 
244 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD,
245 	    MVSW_SMI_CMD_DEVAD(phy) | MVSW_SMI_CMD_REGAD(reg) |
246 	    MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C22 | MVSW_SMI_CMD_C22_READ);
247 
248 	if (mvsw_phy_wait(sc))
249 		return -1;
250 
251 	return mvsw_smi_read(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA);
252 }
253 
254 void
255 mvsw_phy_write(struct mvsw_softc *sc, int phy, int reg, int val)
256 {
257 	if (mvsw_phy_wait(sc))
258 		return;
259 
260 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA, val);
261 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD,
262 	    MVSW_SMI_CMD_DEVAD(phy) | MVSW_SMI_CMD_REGAD(reg) |
263 	    MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C22 | MVSW_SMI_CMD_C22_WRITE);
264 }
265 
266 int
267 mvsw_serdes_read(struct mvsw_softc *sc, int port, int dev, int addr)
268 {
269 	if (mvsw_phy_wait(sc))
270 		return -1;
271 
272 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA, addr);
273 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD,
274 	    MVSW_SMI_CMD_DEVAD(port) | MVSW_SMI_CMD_REGAD(dev) |
275 	    MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C45 | MVSW_SMI_CMD_C45_ADDR);
276 
277 	if (mvsw_phy_wait(sc))
278 		return -1;
279 
280 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD,
281 	    MVSW_SMI_CMD_DEVAD(port) | MVSW_SMI_CMD_REGAD(dev) |
282 	    MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C45 | MVSW_SMI_CMD_C45_READ);
283 
284 	if (mvsw_phy_wait(sc))
285 		return -1;
286 
287 	return mvsw_smi_read(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA);
288 }
289 
290 void
291 mvsw_serdes_write(struct mvsw_softc *sc, int port, int dev, int addr, int val)
292 {
293 	if (mvsw_phy_wait(sc))
294 		return;
295 
296 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA, addr);
297 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD,
298 	    MVSW_SMI_CMD_DEVAD(port) | MVSW_SMI_CMD_REGAD(dev) |
299 	    MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C45 | MVSW_SMI_CMD_C45_ADDR);
300 
301 	if (mvsw_phy_wait(sc))
302 		return;
303 
304 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_DATA, val);
305 	mvsw_smi_write(sc, MVSW_G2, MVSW_G2_SMI_PHY_CMD,
306 	    MVSW_SMI_CMD_DEVAD(port) | MVSW_SMI_CMD_REGAD(dev) |
307 	    MVSW_SMI_CMD_BUSY | MVSW_SMI_CMD_C45 | MVSW_SMI_CMD_C45_WRITE);
308 }
309 
310 void
311 mvsw_port_enable(struct mvsw_softc *sc, int node)
312 {
313 	uint16_t val;
314 	int port;
315 
316 	port = OF_getpropint(node, "reg", -1);
317 	if (port == -1)
318 		return;
319 
320 	/* Enable port. */
321 	val = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL);
322 	val &= ~MVSW_PORT_CTRL_STATE_MASK;
323 	val |= MVSW_PORT_CTRL_STATE_FORWARD;
324 	mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL, val);
325 }
326 
327 void
328 mvsw_phy_enable(struct mvsw_softc *sc, int node)
329 {
330 	uint16_t val;
331 	int phy;
332 
333 	phy = OF_getpropint(node, "reg", -1);
334 	if (phy == -1)
335 		return;
336 
337 	/* Power-up PHY. */
338 	val = mvsw_phy_read(sc, phy, MII_BMCR);
339 	val &= ~BMCR_PDOWN;
340 	mvsw_phy_write(sc, phy, MII_BMCR, val);
341 }
342 
343 void
344 mvsw_serdes_enable(struct mvsw_softc *sc, int node)
345 {
346 	uint16_t val;
347 	int port;
348 
349 	port = OF_getpropint(node, "reg", -1);
350 	if (port == -1)
351 		return;
352 
353 	/* Power-up SERDES. */
354 	val = mvsw_serdes_read(sc, MVSW_SERDES(port),
355 	    MDIO_MMD_PHYXS, MVSW_SERDES_BMCR);
356 	val &= ~BMCR_PDOWN;
357 	val |= BMCR_AUTOEN;
358 	mvsw_serdes_write(sc, MVSW_SERDES(port),
359 	    MDIO_MMD_PHYXS, MVSW_SERDES_BMCR, val);
360 }
361