]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vnic/thunder_mdio.c
Since contrib/libcxxrt's ancestry was never correct, subversion 1.8 and
[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_DEPEND(thunder_mdio, ether, 1, 1, 1);
126 MODULE_DEPEND(thunder_mdio, miibus, 1, 1, 1);
127
128 MALLOC_DEFINE(M_THUNDER_MDIO, "ThunderX MDIO",
129     "Cavium ThunderX MDIO dynamic memory");
130
131 #define MDIO_LOCK_INIT(sc, name)                        \
132     mtx_init(&(sc)->mtx, name, NULL, MTX_DEF)
133
134 #define MDIO_LOCK_DESTROY(sc)                           \
135     mtx_destroy(&(sc)->mtx)
136
137 #define MDIO_LOCK(sc)   mtx_lock(&(sc)->mtx)
138 #define MDIO_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
139
140 #define MDIO_LOCK_ASSERT(sc)                            \
141     mtx_assert(&(sc)->mtx, MA_OWNED)
142
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 setings */
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 }