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