]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vnic/thunder_mdio.c
Merge compiler-rt trunk r351319, and resolve conflicts.
[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
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 setings */
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 }