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