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 settings */
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 }
Cache object: 534acdc170a1b6ea4f8142b207d921ef
|