]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vnic/thunder_mdio.c
Update to bmake-20201101
[FreeBSD/FreeBSD.git] / sys / dev / vnic / thunder_mdio.c
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 #define mdio_reg_read(sc, reg)                          \
146     bus_read_8((sc)->reg_base, (reg))
147
148 #define mdio_reg_write(sc, reg, val)                    \
149     bus_write_8((sc)->reg_base, (reg), (val))
150
151 int
152 thunder_mdio_attach(device_t dev)
153 {
154         struct thunder_mdio_softc *sc;
155         int rid;
156
157         sc = device_get_softc(dev);
158         sc->dev = dev;
159
160         /* Allocate memory resources */
161         rid = REG_BASE_RID;
162         sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
163             RF_ACTIVE);
164         if (sc->reg_base == NULL) {
165                 device_printf(dev, "Could not allocate memory\n");
166                 return (ENXIO);
167         }
168
169         TAILQ_INIT(&sc->phy_desc_head);
170         MDIO_LOCK_INIT(sc, "ThunderX MDIO lock");
171
172         /* Enable SMI/MDIO interface */
173         mdio_reg_write(sc, SMI_EN, SMI_EN_EN);
174
175         return (0);
176 }
177
178 static int
179 thunder_mdio_detach(device_t dev)
180 {
181         struct thunder_mdio_softc *sc;
182
183         sc = device_get_softc(dev);
184
185         if (sc->reg_base != NULL) {
186                 bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID,
187                     sc->reg_base);
188         }
189
190         return (0);
191 }
192
193 static __inline void
194 thunder_mdio_set_mode(struct thunder_mdio_softc *sc,
195     enum thunder_mdio_mode mode)
196 {
197         uint64_t smi_clk;
198
199         if (sc->mode == mode)
200                 return;
201
202         /* Set mode, IEEE CLAUSE 22 or IEEE CAUSE 45 */
203         smi_clk = mdio_reg_read(sc, SMI_CLK);
204         if (mode == MODE_IEEE_C22)
205                 smi_clk &= ~SMI_CLK_MODE;
206         else
207                 smi_clk |= SMI_CLK_MODE;
208         /* Enable sending 32 bit preable on SMI transactions */
209         smi_clk |= SMI_CLK_PREAMBLE;
210         /* Saved setings */
211         mdio_reg_write(sc, SMI_CLK, smi_clk);
212         sc->mode = mode;
213 }
214
215 static int
216 thunder_mdio_c45_addr(struct thunder_mdio_softc *sc, int phy, int reg)
217 {
218         uint64_t smi_cmd, smi_wr_dat;
219         ssize_t timeout;
220
221         thunder_mdio_set_mode(sc, MODE_IEEE_C45);
222
223         /* Prepare data for transmission */
224         mdio_reg_write(sc, SMI_WR_DAT, reg & SMI_WR_DAT_DAT_MASK);
225         /*
226          * Assemble command
227          */
228         smi_cmd = 0;
229         /* Set opcode */
230         smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
231
232         /* Set PHY address */
233         smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
234         /* Set PHY register offset */
235         smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
236             SMI_CMD_PHY_REG_ADR_MASK);
237
238         mdio_reg_write(sc, SMI_CMD, smi_cmd);
239         for (timeout = 1000; timeout > 0; timeout--) {
240                 smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
241                 if (smi_wr_dat & SMI_WR_DAT_PENDING)
242                         DELAY(1000);
243                 else
244                         break;
245         }
246
247         if (timeout <= 0)
248                 return (EIO);
249         else {
250                 /* Return 0 on success */
251                 return (0);
252         }
253 }
254
255 static int
256 thunder_mdio_read(device_t dev, int phy, int reg)
257 {
258         struct thunder_mdio_softc *sc;
259         uint64_t smi_cmd, smi_rd_dat;
260         ssize_t timeout;
261         int err;
262
263         sc = device_get_softc(dev);
264
265         /* XXX Always C22 - for <= 1Gbps only */
266         thunder_mdio_set_mode(sc, MODE_IEEE_C22);
267
268         /*
269          * Assemble command
270          */
271         smi_cmd = 0;
272         /* Set opcode */
273         if (sc->mode == MODE_IEEE_C22)
274                 smi_cmd |= SMI_CMD_PHY_OP_C22_READ;
275         else {
276                 smi_cmd |= SMI_CMD_PHY_OP_C45_READ;
277                 err = thunder_mdio_c45_addr(sc, phy, reg);
278                 if (err != 0)
279                         return (err);
280
281                 reg = (reg >> 16) & 0x1F;
282         }
283
284         /* Set PHY address */
285         smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
286         /* Set PHY register offset */
287         smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
288             SMI_CMD_PHY_REG_ADR_MASK);
289
290         mdio_reg_write(sc, SMI_CMD, smi_cmd);
291         for (timeout = 1000; timeout > 0; timeout--) {
292                 smi_rd_dat = mdio_reg_read(sc, SMI_RD_DAT);
293                 if (smi_rd_dat & SMI_RD_DAT_PENDING)
294                         DELAY(1000);
295                 else
296                         break;
297         }
298
299         if (smi_rd_dat & SMI_RD_DAT_VAL)
300                 return (smi_rd_dat & SMI_RD_DAT_DAT_MASK);
301         else {
302                 /* Return 0 on error */
303                 return (0);
304         }
305 }
306
307 static int
308 thunder_mdio_write(device_t dev, int phy, int reg, int data)
309 {
310         struct thunder_mdio_softc *sc;
311         uint64_t smi_cmd, smi_wr_dat;
312         ssize_t timeout;
313
314         sc = device_get_softc(dev);
315
316         /* XXX Always C22 - for <= 1Gbps only */
317         thunder_mdio_set_mode(sc, MODE_IEEE_C22);
318
319         /* Prepare data for transmission */
320         mdio_reg_write(sc, SMI_WR_DAT, data & SMI_WR_DAT_DAT_MASK);
321         /*
322          * Assemble command
323          */
324         smi_cmd = 0;
325         /* Set opcode */
326         if (sc->mode == MODE_IEEE_C22)
327                 smi_cmd |= SMI_CMD_PHY_OP_C22_WRITE;
328         else
329                 smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
330
331         /* Set PHY address */
332         smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
333         /* Set PHY register offset */
334         smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
335             SMI_CMD_PHY_REG_ADR_MASK);
336
337         mdio_reg_write(sc, SMI_CMD, smi_cmd);
338         for (timeout = 1000; timeout > 0; timeout--) {
339                 smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
340                 if (smi_wr_dat & SMI_WR_DAT_PENDING)
341                         DELAY(1000);
342                 else
343                         break;
344         }
345
346         if (timeout <= 0)
347                 return (EIO);
348         else {
349                 /* Return 0 on success */
350                 return (0);
351         }
352 }
353
354 static int
355 thunder_ifmedia_change_stub(struct ifnet *ifp __unused)
356 {
357         /* Will never be called by if_media */
358         return (0);
359 }
360
361 static void
362 thunder_ifmedia_status_stub(struct ifnet *ifp __unused, struct ifmediareq
363     *ifmr __unused)
364 {
365         /* Will never be called by if_media */
366 }
367
368 static __inline struct phy_desc *
369 get_phy_desc(struct thunder_mdio_softc *sc, int lmacid)
370 {
371         struct phy_desc *pd = NULL;
372
373         MDIO_LOCK_ASSERT(sc);
374         TAILQ_FOREACH(pd, &sc->phy_desc_head, phy_desc_list) {
375                 if (pd->lmacid == lmacid)
376                         break;
377         }
378
379         return (pd);
380 }
381 static int
382 thunder_mdio_media_status(device_t dev, int lmacid, int *link, int *duplex,
383     int *speed)
384 {
385         struct thunder_mdio_softc *sc;
386         struct mii_data *mii_sc;
387         struct phy_desc *pd;
388
389         sc = device_get_softc(dev);
390
391         MDIO_LOCK(sc);
392         pd = get_phy_desc(sc, lmacid);
393         if (pd == NULL) {
394                 /* Panic when invariants are enabled, fail otherwise. */
395                 KASSERT(0, ("%s: no PHY descriptor for LMAC%d",
396                     __func__, lmacid));
397                 MDIO_UNLOCK(sc);
398                 return (ENXIO);
399         }
400         mii_sc = device_get_softc(pd->miibus);
401
402         mii_tick(mii_sc);
403         if ((mii_sc->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
404             (IFM_ACTIVE | IFM_AVALID)) {
405                 /* Link is up */
406                 *link = 1;
407         } else
408                 *link = 0;
409
410         switch (IFM_SUBTYPE(mii_sc->mii_media_active)) {
411         case IFM_10_T:
412                 *speed = 10;
413                 break;
414         case IFM_100_TX:
415                 *speed = 100;
416                 break;
417         case IFM_1000_T:
418                 *speed = 1000;
419                 break;
420         default:
421                 /* IFM_NONE */
422                 *speed = 0;
423         }
424
425         if ((IFM_OPTIONS(mii_sc->mii_media_active) & IFM_FDX) != 0)
426                 *duplex = 1;
427         else
428                 *duplex = 0;
429
430         MDIO_UNLOCK(sc);
431
432         return (0);
433 }
434
435 static int
436 thunder_mdio_media_change(device_t dev, int lmacid, int link, int duplex,
437     int speed)
438 {
439
440         return (EIO);
441 }
442
443 static int
444 thunder_mdio_phy_connect(device_t dev, int lmacid, int phy)
445 {
446         struct thunder_mdio_softc *sc;
447         struct phy_desc *pd;
448         int err;
449
450         sc = device_get_softc(dev);
451
452         MDIO_LOCK(sc);
453         pd = get_phy_desc(sc, lmacid);
454         MDIO_UNLOCK(sc);
455         if (pd == NULL) {
456                 pd = malloc(sizeof(*pd), M_THUNDER_MDIO, (M_NOWAIT | M_ZERO));
457                 if (pd == NULL)
458                         return (ENOMEM);
459                 pd->ifp = if_alloc(IFT_ETHER);
460                 if (pd->ifp == NULL) {
461                         free(pd, M_THUNDER_MDIO);
462                         return (ENOMEM);
463                 }
464                 pd->lmacid = lmacid;
465         }
466
467         err = mii_attach(dev, &pd->miibus, pd->ifp,
468             thunder_ifmedia_change_stub, thunder_ifmedia_status_stub,
469             BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
470
471         if (err != 0) {
472                 device_printf(dev, "Could not attach PHY%d\n", phy);
473                 if_free(pd->ifp);
474                 free(pd, M_THUNDER_MDIO);
475                 return (ENXIO);
476         }
477
478         MDIO_LOCK(sc);
479         TAILQ_INSERT_TAIL(&sc->phy_desc_head, pd, phy_desc_list);
480         MDIO_UNLOCK(sc);
481
482         return (0);
483 }
484
485 static int
486 thunder_mdio_phy_disconnect(device_t dev, int lmacid, int phy)
487 {
488         struct thunder_mdio_softc *sc;
489         struct phy_desc *pd;
490
491         sc = device_get_softc(dev);
492         MDIO_LOCK(sc);
493
494         pd = get_phy_desc(sc, lmacid);
495         if (pd == NULL) {
496                 MDIO_UNLOCK(sc);
497                 return (EINVAL);
498         }
499
500         /* Remove this PHY descriptor from the list */
501         TAILQ_REMOVE(&sc->phy_desc_head, pd, phy_desc_list);
502
503         /* Detach miibus */
504         bus_generic_detach(dev);
505         device_delete_child(dev, pd->miibus);
506         /* Free fake ifnet */
507         if_free(pd->ifp);
508         /* Free memory under phy descriptor */
509         free(pd, M_THUNDER_MDIO);
510         MDIO_UNLOCK(sc);
511
512         return (0);
513 }