xref: /freebsd/sys/dev/vnic/thunder_mdio.c (revision 076ad2f8)
1 /*-
2  * Copyright (c) 2015 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Semihalf under
6  * the sponsorship of the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/resource.h>
39 #include <sys/rman.h>
40 #include <sys/socket.h>
41 #include <sys/queue.h>
42 
43 #include <machine/bus.h>
44 #include <machine/resource.h>
45 
46 #include <net/if.h>
47 #include <net/if_media.h>
48 #include <net/if_types.h>
49 #include <net/if_var.h>
50 
51 #include <dev/mii/mii.h>
52 #include <dev/mii/miivar.h>
53 
54 #include "thunder_mdio_var.h"
55 
56 #include "lmac_if.h"
57 #include "miibus_if.h"
58 
59 #define	REG_BASE_RID	0
60 
61 #define	SMI_CMD				0x00
62 #define	 SMI_CMD_PHY_REG_ADR_SHIFT	(0)
63 #define	 SMI_CMD_PHY_REG_ADR_MASK	(0x1FUL << SMI_CMD_PHY_REG_ADR_SHIFT)
64 #define	 SMI_CMD_PHY_ADR_SHIFT		(8)
65 #define	 SMI_CMD_PHY_ADR_MASK		(0x1FUL << SMI_CMD_PHY_ADR_SHIFT)
66 #define	 SMI_CMD_PHY_OP_MASK		(0x3UL << 16)
67 #define	 SMI_CMD_PHY_OP_C22_READ	(0x1UL << 16)
68 #define	 SMI_CMD_PHY_OP_C22_WRITE	(0x0UL << 16)
69 #define	 SMI_CMD_PHY_OP_C45_READ	(0x3UL << 16)
70 #define	 SMI_CMD_PHY_OP_C45_WRITE	(0x1UL << 16)
71 #define	 SMI_CMD_PHY_OP_C45_ADDR	(0x0UL << 16)
72 
73 #define	SMI_WR_DAT			0x08
74 #define	 SMI_WR_DAT_PENDING		(1UL << 17)
75 #define	 SMI_WR_DAT_VAL			(1UL << 16)
76 #define	 SMI_WR_DAT_DAT_MASK		(0xFFFFUL << 0)
77 
78 #define	SMI_RD_DAT			0x10
79 #define	 SMI_RD_DAT_PENDING		(1UL << 17)
80 #define	 SMI_RD_DAT_VAL			(1UL << 16)
81 #define	 SMI_RD_DAT_DAT_MASK		(0xFFFFUL << 0)
82 
83 #define	SMI_CLK				0x18
84 #define	 SMI_CLK_PREAMBLE		(1UL << 12)
85 #define	 SMI_CLK_MODE			(1UL << 24)
86 
87 #define	SMI_EN				0x20
88 #define	 SMI_EN_EN			(1UL << 0)	/* Enabele interface */
89 
90 #define	SMI_DRV_CTL			0x28
91 
92 static int thunder_mdio_detach(device_t);
93 
94 static int thunder_mdio_read(device_t, int, int);
95 static int thunder_mdio_write(device_t, int, int, int);
96 
97 static int thunder_ifmedia_change_stub(struct ifnet *);
98 static void thunder_ifmedia_status_stub(struct ifnet *, struct ifmediareq *);
99 
100 static int thunder_mdio_media_status(device_t, int, int *, int *, int *);
101 static int thunder_mdio_media_change(device_t, int, int, int, int);
102 static int thunder_mdio_phy_connect(device_t, int, int);
103 static int thunder_mdio_phy_disconnect(device_t, int, int);
104 
105 static device_method_t thunder_mdio_methods[] = {
106 	/* Device interface */
107 	DEVMETHOD(device_detach,	thunder_mdio_detach),
108 	/* LMAC interface */
109 	DEVMETHOD(lmac_media_status,	thunder_mdio_media_status),
110 	DEVMETHOD(lmac_media_change,	thunder_mdio_media_change),
111 	DEVMETHOD(lmac_phy_connect,	thunder_mdio_phy_connect),
112 	DEVMETHOD(lmac_phy_disconnect,	thunder_mdio_phy_disconnect),
113 	/* MII interface */
114 	DEVMETHOD(miibus_readreg,	thunder_mdio_read),
115 	DEVMETHOD(miibus_writereg,	thunder_mdio_write),
116 
117 	/* End */
118 	DEVMETHOD_END
119 };
120 
121 DEFINE_CLASS_0(thunder_mdio, thunder_mdio_driver, thunder_mdio_methods,
122     sizeof(struct thunder_mdio_softc));
123 
124 DRIVER_MODULE(miibus, thunder_mdio, miibus_driver, miibus_devclass, 0, 0);
125 MODULE_VERSION(thunder_mdio, 1);
126 MODULE_DEPEND(thunder_mdio, ether, 1, 1, 1);
127 MODULE_DEPEND(thunder_mdio, miibus, 1, 1, 1);
128 MODULE_DEPEND(thunder_mdio, mrmlbus, 1, 1, 1);
129 
130 MALLOC_DEFINE(M_THUNDER_MDIO, "ThunderX MDIO",
131     "Cavium ThunderX MDIO dynamic memory");
132 
133 #define	MDIO_LOCK_INIT(sc, name)			\
134     mtx_init(&(sc)->mtx, name, NULL, MTX_DEF)
135 
136 #define	MDIO_LOCK_DESTROY(sc)				\
137     mtx_destroy(&(sc)->mtx)
138 
139 #define	MDIO_LOCK(sc)	mtx_lock(&(sc)->mtx)
140 #define	MDIO_UNLOCK(sc)	mtx_unlock(&(sc)->mtx)
141 
142 #define	MDIO_LOCK_ASSERT(sc)				\
143     mtx_assert(&(sc)->mtx, MA_OWNED)
144 
145 
146 #define	mdio_reg_read(sc, reg)				\
147     bus_read_8((sc)->reg_base, (reg))
148 
149 #define	mdio_reg_write(sc, reg, val)			\
150     bus_write_8((sc)->reg_base, (reg), (val))
151 
152 int
153 thunder_mdio_attach(device_t dev)
154 {
155 	struct thunder_mdio_softc *sc;
156 	int rid;
157 
158 	sc = device_get_softc(dev);
159 	sc->dev = dev;
160 
161 	/* Allocate memory resources */
162 	rid = REG_BASE_RID;
163 	sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
164 	    RF_ACTIVE);
165 	if (sc->reg_base == NULL) {
166 		device_printf(dev, "Could not allocate memory\n");
167 		return (ENXIO);
168 	}
169 
170 	TAILQ_INIT(&sc->phy_desc_head);
171 	MDIO_LOCK_INIT(sc, "ThunderX MDIO lock");
172 
173 	/* Enable SMI/MDIO interface */
174 	mdio_reg_write(sc, SMI_EN, SMI_EN_EN);
175 
176 	return (0);
177 }
178 
179 static int
180 thunder_mdio_detach(device_t dev)
181 {
182 	struct thunder_mdio_softc *sc;
183 
184 	sc = device_get_softc(dev);
185 
186 	if (sc->reg_base != NULL) {
187 		bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID,
188 		    sc->reg_base);
189 	}
190 
191 	return (0);
192 }
193 
194 static __inline void
195 thunder_mdio_set_mode(struct thunder_mdio_softc *sc,
196     enum thunder_mdio_mode mode)
197 {
198 	uint64_t smi_clk;
199 
200 	if (sc->mode == mode)
201 		return;
202 
203 	/* Set mode, IEEE CLAUSE 22 or IEEE CAUSE 45 */
204 	smi_clk = mdio_reg_read(sc, SMI_CLK);
205 	if (mode == MODE_IEEE_C22)
206 		smi_clk &= ~SMI_CLK_MODE;
207 	else
208 		smi_clk |= SMI_CLK_MODE;
209 	/* Enable sending 32 bit preable on SMI transactions */
210 	smi_clk |= SMI_CLK_PREAMBLE;
211 	/* Saved setings */
212 	mdio_reg_write(sc, SMI_CLK, smi_clk);
213 	sc->mode = mode;
214 }
215 
216 static int
217 thunder_mdio_c45_addr(struct thunder_mdio_softc *sc, int phy, int reg)
218 {
219 	uint64_t smi_cmd, smi_wr_dat;
220 	ssize_t timeout;
221 
222 	thunder_mdio_set_mode(sc, MODE_IEEE_C45);
223 
224 	/* Prepare data for transmission */
225 	mdio_reg_write(sc, SMI_WR_DAT, reg & SMI_WR_DAT_DAT_MASK);
226 	/*
227 	 * Assemble command
228 	 */
229 	smi_cmd = 0;
230 	/* Set opcode */
231 	smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
232 
233 	/* Set PHY address */
234 	smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
235 	/* Set PHY register offset */
236 	smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
237 	    SMI_CMD_PHY_REG_ADR_MASK);
238 
239 	mdio_reg_write(sc, SMI_CMD, smi_cmd);
240 	for (timeout = 1000; timeout > 0; timeout--) {
241 		smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
242 		if (smi_wr_dat & SMI_WR_DAT_PENDING)
243 			DELAY(1000);
244 		else
245 			break;
246 	}
247 
248 	if (timeout <= 0)
249 		return (EIO);
250 	else {
251 		/* Return 0 on success */
252 		return (0);
253 	}
254 }
255 
256 static int
257 thunder_mdio_read(device_t dev, int phy, int reg)
258 {
259 	struct thunder_mdio_softc *sc;
260 	uint64_t smi_cmd, smi_rd_dat;
261 	ssize_t timeout;
262 	int err;
263 
264 	sc = device_get_softc(dev);
265 
266 	/* XXX Always C22 - for <= 1Gbps only */
267 	thunder_mdio_set_mode(sc, MODE_IEEE_C22);
268 
269 	/*
270 	 * Assemble command
271 	 */
272 	smi_cmd = 0;
273 	/* Set opcode */
274 	if (sc->mode == MODE_IEEE_C22)
275 		smi_cmd |= SMI_CMD_PHY_OP_C22_READ;
276 	else {
277 		smi_cmd |= SMI_CMD_PHY_OP_C45_READ;
278 		err = thunder_mdio_c45_addr(sc, phy, reg);
279 		if (err != 0)
280 			return (err);
281 
282 		reg = (reg >> 16) & 0x1F;
283 	}
284 
285 	/* Set PHY address */
286 	smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
287 	/* Set PHY register offset */
288 	smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
289 	    SMI_CMD_PHY_REG_ADR_MASK);
290 
291 	mdio_reg_write(sc, SMI_CMD, smi_cmd);
292 	for (timeout = 1000; timeout > 0; timeout--) {
293 		smi_rd_dat = mdio_reg_read(sc, SMI_RD_DAT);
294 		if (smi_rd_dat & SMI_RD_DAT_PENDING)
295 			DELAY(1000);
296 		else
297 			break;
298 	}
299 
300 	if (smi_rd_dat & SMI_RD_DAT_VAL)
301 		return (smi_rd_dat & SMI_RD_DAT_DAT_MASK);
302 	else {
303 		/* Return 0 on error */
304 		return (0);
305 	}
306 }
307 
308 static int
309 thunder_mdio_write(device_t dev, int phy, int reg, int data)
310 {
311 	struct thunder_mdio_softc *sc;
312 	uint64_t smi_cmd, smi_wr_dat;
313 	ssize_t timeout;
314 
315 	sc = device_get_softc(dev);
316 
317 	/* XXX Always C22 - for <= 1Gbps only */
318 	thunder_mdio_set_mode(sc, MODE_IEEE_C22);
319 
320 	/* Prepare data for transmission */
321 	mdio_reg_write(sc, SMI_WR_DAT, data & SMI_WR_DAT_DAT_MASK);
322 	/*
323 	 * Assemble command
324 	 */
325 	smi_cmd = 0;
326 	/* Set opcode */
327 	if (sc->mode == MODE_IEEE_C22)
328 		smi_cmd |= SMI_CMD_PHY_OP_C22_WRITE;
329 	else
330 		smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
331 
332 	/* Set PHY address */
333 	smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
334 	/* Set PHY register offset */
335 	smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
336 	    SMI_CMD_PHY_REG_ADR_MASK);
337 
338 	mdio_reg_write(sc, SMI_CMD, smi_cmd);
339 	for (timeout = 1000; timeout > 0; timeout--) {
340 		smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
341 		if (smi_wr_dat & SMI_WR_DAT_PENDING)
342 			DELAY(1000);
343 		else
344 			break;
345 	}
346 
347 	if (timeout <= 0)
348 		return (EIO);
349 	else {
350 		/* Return 0 on success */
351 		return (0);
352 	}
353 }
354 
355 static int
356 thunder_ifmedia_change_stub(struct ifnet *ifp __unused)
357 {
358 	/* Will never be called by if_media */
359 	return (0);
360 }
361 
362 static void
363 thunder_ifmedia_status_stub(struct ifnet *ifp __unused, struct ifmediareq
364     *ifmr __unused)
365 {
366 	/* Will never be called by if_media */
367 }
368 
369 static __inline struct phy_desc *
370 get_phy_desc(struct thunder_mdio_softc *sc, int lmacid)
371 {
372 	struct phy_desc *pd = NULL;
373 
374 	MDIO_LOCK_ASSERT(sc);
375 	TAILQ_FOREACH(pd, &sc->phy_desc_head, phy_desc_list) {
376 		if (pd->lmacid == lmacid)
377 			break;
378 	}
379 
380 	return (pd);
381 }
382 static int
383 thunder_mdio_media_status(device_t dev, int lmacid, int *link, int *duplex,
384     int *speed)
385 {
386 	struct thunder_mdio_softc *sc;
387 	struct mii_data *mii_sc;
388 	struct phy_desc *pd;
389 
390 	sc = device_get_softc(dev);
391 
392 	MDIO_LOCK(sc);
393 	pd = get_phy_desc(sc, lmacid);
394 	if (pd == NULL) {
395 		/* Panic when invariants are enabled, fail otherwise. */
396 		KASSERT(0, ("%s: no PHY descriptor for LMAC%d",
397 		    __func__, lmacid));
398 		MDIO_UNLOCK(sc);
399 		return (ENXIO);
400 	}
401 	mii_sc = device_get_softc(pd->miibus);
402 
403 	mii_tick(mii_sc);
404 	if ((mii_sc->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
405 	    (IFM_ACTIVE | IFM_AVALID)) {
406 		/* Link is up */
407 		*link = 1;
408 	} else
409 		*link = 0;
410 
411 	switch (IFM_SUBTYPE(mii_sc->mii_media_active)) {
412 	case IFM_10_T:
413 		*speed = 10;
414 		break;
415 	case IFM_100_TX:
416 		*speed = 100;
417 		break;
418 	case IFM_1000_T:
419 		*speed = 1000;
420 		break;
421 	default:
422 		/* IFM_NONE */
423 		*speed = 0;
424 	}
425 
426 	if ((IFM_OPTIONS(mii_sc->mii_media_active) & IFM_FDX) != 0)
427 		*duplex = 1;
428 	else
429 		*duplex = 0;
430 
431 	MDIO_UNLOCK(sc);
432 
433 	return (0);
434 }
435 
436 static int
437 thunder_mdio_media_change(device_t dev, int lmacid, int link, int duplex,
438     int speed)
439 {
440 
441 	return (EIO);
442 }
443 
444 static int
445 thunder_mdio_phy_connect(device_t dev, int lmacid, int phy)
446 {
447 	struct thunder_mdio_softc *sc;
448 	struct phy_desc *pd;
449 	int err;
450 
451 	sc = device_get_softc(dev);
452 
453 	MDIO_LOCK(sc);
454 	pd = get_phy_desc(sc, lmacid);
455 	MDIO_UNLOCK(sc);
456 	if (pd == NULL) {
457 		pd = malloc(sizeof(*pd), M_THUNDER_MDIO, (M_NOWAIT | M_ZERO));
458 		if (pd == NULL)
459 			return (ENOMEM);
460 		pd->ifp = if_alloc(IFT_ETHER);
461 		if (pd->ifp == NULL) {
462 			free(pd, M_THUNDER_MDIO);
463 			return (ENOMEM);
464 		}
465 		pd->lmacid = lmacid;
466 	}
467 
468 	err = mii_attach(dev, &pd->miibus, pd->ifp,
469 	    thunder_ifmedia_change_stub, thunder_ifmedia_status_stub,
470 	    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
471 
472 	if (err != 0) {
473 		device_printf(dev, "Could not attach PHY%d\n", phy);
474 		if_free(pd->ifp);
475 		free(pd, M_THUNDER_MDIO);
476 		return (ENXIO);
477 	}
478 
479 	MDIO_LOCK(sc);
480 	TAILQ_INSERT_TAIL(&sc->phy_desc_head, pd, phy_desc_list);
481 	MDIO_UNLOCK(sc);
482 
483 	return (0);
484 }
485 
486 static int
487 thunder_mdio_phy_disconnect(device_t dev, int lmacid, int phy)
488 {
489 	struct thunder_mdio_softc *sc;
490 	struct phy_desc *pd;
491 
492 	sc = device_get_softc(dev);
493 	MDIO_LOCK(sc);
494 
495 	pd = get_phy_desc(sc, lmacid);
496 	if (pd == NULL) {
497 		MDIO_UNLOCK(sc);
498 		return (EINVAL);
499 	}
500 
501 	/* Remove this PHY descriptor from the list */
502 	TAILQ_REMOVE(&sc->phy_desc_head, pd, phy_desc_list);
503 
504 	/* Detach miibus */
505 	bus_generic_detach(dev);
506 	device_delete_child(dev, pd->miibus);
507 	/* Free fake ifnet */
508 	if_free(pd->ifp);
509 	/* Free memory under phy descriptor */
510 	free(pd, M_THUNDER_MDIO);
511 	MDIO_UNLOCK(sc);
512 
513 	return (0);
514 }
515