1 /* $OpenBSD: mvsw.c,v 1.5 2022/04/06 18:59:28 naddy 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 const 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
mvsw_match(struct device * parent,void * match,void * aux)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
mvsw_attach(struct device * parent,struct device * self,void * aux)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
mvsw_read(struct mvsw_softc * sc,int reg)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
mvsw_write(struct mvsw_softc * sc,int reg,int val)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
mvsw_smi_wait(struct mvsw_softc * sc)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
mvsw_smi_read(struct mvsw_softc * sc,int phy,int reg)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
mvsw_smi_write(struct mvsw_softc * sc,int phy,int reg,int val)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
mvsw_phy_wait(struct mvsw_softc * sc)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
mvsw_phy_read(struct mvsw_softc * sc,int phy,int reg)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
mvsw_phy_write(struct mvsw_softc * sc,int phy,int reg,int val)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
mvsw_serdes_read(struct mvsw_softc * sc,int port,int dev,int addr)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
mvsw_serdes_write(struct mvsw_softc * sc,int port,int dev,int addr,int val)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
mvsw_port_enable(struct mvsw_softc * sc,int node)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
mvsw_phy_enable(struct mvsw_softc * sc,int node)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
mvsw_serdes_enable(struct mvsw_softc * sc,int node)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