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