source: rtems-libbsd/rtemsbsd/sys/dev/atsam/if_atsam.c @ 80a7fe6

5-freebsd-12
Last change on this file since 80a7fe6 was 80a7fe6, checked in by Christian Mauderer <Christian.Mauderer@…>, on Sep 22, 2017 at 7:41:18 AM

if-atsam: Port to rtems-libbsd.

  • Property mode set to 100644
File size: 30.6 KB
Line 
1/*
2 * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <info@embedded-brains.de>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <machine/rtems-bsd-kernel-space.h>
33
34#include <bsp.h>
35
36#ifdef LIBBSP_ARM_ATSAM_BSP_H
37
38#include <bsp/irq.h>
39
40#include <stdio.h>
41
42#include <sys/types.h>
43#include <sys/param.h>
44#include <sys/mbuf.h>
45#include <sys/socket.h>
46#include <sys/sockio.h>
47#include <sys/kernel.h>
48#include <sys/module.h>
49#include <sys/bus.h>
50
51#include <net/if.h>
52#include <net/if_var.h>
53#include <net/if_types.h>
54#include <net/if_media.h>
55
56#include <netinet/in.h>
57#include <netinet/if_ether.h>
58
59#include <dev/mii/mii.h>
60#include <dev/mii/miivar.h>
61
62#include <libchip/chip.h>
63#include <libchip/include/gmacd.h>
64#include <libchip/include/pio.h>
65
66#include <rtems/rtems_mii_ioctl.h>
67#include <rtems/bsd/local/miibus_if.h>
68
69/*
70 * Number of interfaces supported by the driver
71 */
72#define NIFACES                 1
73
74/** Enable/Disable CopyAllFrame */
75#define GMAC_CAF_DISABLE        0
76#define GMAC_CAF_ENABLE         1
77
78/** Enable/Disable NoBroadCast */
79#define GMAC_NBC_DISABLE        0
80#define GMAC_NBC_ENABLE         1
81
82/** The PIN list of PIO for GMAC */
83#define BOARD_GMAC_PINS                                                    \
84        { (PIO_PD0A_GTXCK | PIO_PD1A_GTXEN | PIO_PD2A_GTX0 | PIO_PD3A_GTX1 \
85          | PIO_PD4A_GRXDV | PIO_PD5A_GRX0 | PIO_PD6A_GRX1                 \
86          | PIO_PD7A_GRXER                                                 \
87          | PIO_PD8A_GMDC | PIO_PD9A_GMDIO), PIOD, ID_PIOD, PIO_PERIPH_A,  \
88          PIO_DEFAULT }
89/** The runtime pin configure list for GMAC */
90#define BOARD_GMAC_RUN_PINS                     BOARD_GMAC_PINS
91
92/** Multicast Enable */
93#define GMAC_MC_ENABLE                          (1u << 6)
94#define HASH_INDEX_AMOUNT                       6
95#define HASH_ELEMENTS_PER_INDEX                 8
96#define MAC_ADDR_MASK                           0x0000FFFFFFFFFFFF
97#define MAC_IDX_MASK                            (1u << 0)
98
99/** Promiscuous Mode Enable */
100#define GMAC_PROM_ENABLE                        (1u << 4)
101
102/** RX Defines */
103#define GMAC_RX_BUFFER_SIZE                     1536
104#define GMAC_RX_BUF_DESC_ADDR_MASK              0xFFFFFFFC
105#define GMAC_RX_SET_OFFSET                      (1u << 15)
106#define GMAC_RX_SET_USED_WRAP                   ((1u << 1) | (1u << 0))
107#define GMAC_RX_SET_WRAP                        (1u << 1)
108#define GMAC_RX_SET_USED                        (1u << 0)
109/** TX Defines */
110#define GMAC_TX_SET_EOF                         (1u << 15)
111#define GMAC_TX_SET_WRAP                        (1u << 30)
112#define GMAC_TX_SET_USED                        (1u << 31)
113
114#define GMAC_DESCRIPTOR_ALIGNMENT               8
115
116/** Events */
117#define ATSAMV7_ETH_RX_EVENT_INTERRUPT          RTEMS_EVENT_1
118#define ATSAMV7_ETH_TX_EVENT_INTERRUPT          RTEMS_EVENT_2
119#define ATSAMV7_ETH_START_TRANSMIT_EVENT        RTEMS_EVENT_3
120
121#define ATSAMV7_ETH_RX_DATA_OFFSET              2
122
123#define WATCHDOG_TIMEOUT                        5
124
125#define SIO_RTEMS_SHOW_STATS _IO('i', 250)
126
127/* FIXME: Make these configurable */
128#define MDIO_RETRIES 10
129#define MDIO_PHY MII_PHY_ANY
130#define RXBUF_COUNT 8
131#define TXBUF_COUNT 64
132#define IGNORE_RX_ERR false
133
134
135/** The PINs for GMAC */
136static const Pin gmacPins[] = { BOARD_GMAC_RUN_PINS };
137
138typedef struct if_atsam_gmac {
139        /** The GMAC driver instance */
140        sGmacd gGmacd;
141        uint32_t retries;
142} if_atsam_gmac;
143
144typedef struct ring_buffer {
145        unsigned tx_bd_used;
146        unsigned tx_bd_free;
147        size_t length;
148} ring_buffer;
149
150/*
151 * Per-device data
152 */
153typedef struct if_atsam_softc {
154        /*
155         * Data
156         */
157        device_t dev;
158        struct ifnet *ifp;
159        struct mtx mtx;
160        if_atsam_gmac Gmac_inst;
161        uint8_t GMacAddress[6];
162        rtems_id rx_daemon_tid;
163        rtems_id tx_daemon_tid;
164        rtems_vector_number interrupt_number;
165        struct mbuf **rx_mbuf;
166        struct mbuf **tx_mbuf;
167        volatile sGmacTxDescriptor *tx_bd_base;
168        size_t rx_bd_fill_idx;
169        size_t amount_rx_buf;
170        size_t amount_tx_buf;
171        ring_buffer tx_ring;
172
173        /*
174         * mii bus
175         */
176        device_t miibus;
177        uint8_t link_speed;
178        uint8_t link_duplex;
179        struct callout mii_tick_ch;
180
181        /*
182         * Statistics
183         */
184        unsigned rx_overrun_errors;
185        unsigned rx_interrupts;
186        unsigned tx_complete_int;
187        unsigned tx_tur_errors;
188        unsigned tx_rlex_errors;
189        unsigned tx_tfc_errors;
190        unsigned tx_hresp_errors;
191        unsigned tx_interrupts;
192} if_atsam_softc;
193
194static void if_atsam_watchdog(void *arg);
195
196#define IF_ATSAM_LOCK(sc) mtx_lock(&(sc)->mtx)
197
198#define IF_ATSAM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
199
200static void if_atsam_event_send(rtems_id task, rtems_event_set event)
201{
202        rtems_event_send(task, event);
203}
204
205
206static void if_atsam_event_receive(if_atsam_softc *sc, rtems_event_set in)
207{
208        rtems_event_set out;
209
210        IF_ATSAM_UNLOCK(sc);
211        rtems_event_receive(
212            in,
213            RTEMS_EVENT_ANY | RTEMS_WAIT,
214            RTEMS_NO_TIMEOUT,
215            &out
216        );
217        IF_ATSAM_LOCK(sc);
218}
219
220
221static struct mbuf *if_atsam_new_mbuf(struct ifnet *ifp)
222{
223        struct mbuf *m;
224
225        MGETHDR(m, M_NOWAIT, MT_DATA);
226        if (m != NULL) {
227                MCLGET(m, M_NOWAIT);
228                if ((m->m_flags & M_EXT) != 0) {
229                        m->m_pkthdr.rcvif = ifp;
230                        m->m_data = mtod(m, char *);
231                        rtems_cache_invalidate_multiple_data_lines(mtod(m, void *),
232                            GMAC_RX_BUFFER_SIZE);
233                } else {
234                        m_free(m);
235                        m = NULL;
236                }
237        }
238        return (m);
239}
240
241
242static uint8_t if_atsam_wait_phy(Gmac *pHw, uint32_t retry)
243{
244        volatile uint32_t retry_count = 0;
245
246        while (!GMAC_IsIdle(pHw)) {
247                if (retry == 0) {
248                        continue;
249                }
250                retry_count++;
251
252                if (retry_count >= retry) {
253                        return (1);
254                }
255                rtems_task_wake_after(1);
256        }
257
258        return (0);
259}
260
261
262static uint8_t
263if_atsam_write_phy(Gmac *pHw, uint8_t PhyAddress, uint8_t Address,
264    uint32_t Value, uint32_t retry)
265{
266        GMAC_PHYMaintain(pHw, PhyAddress, Address, 0, (uint16_t)Value);
267        if (if_atsam_wait_phy(pHw, retry) == 1) {
268                return (1);
269        }
270        return (0);
271}
272
273
274static uint8_t
275if_atsam_read_phy(Gmac *pHw,
276    uint8_t PhyAddress, uint8_t Address, uint32_t *pvalue, uint32_t retry)
277{
278        GMAC_PHYMaintain(pHw, PhyAddress, Address, 1, 0);
279        if (if_atsam_wait_phy(pHw, retry) == 1) {
280                return (1);
281        }
282        *pvalue = GMAC_PHYData(pHw);
283        return (0);
284}
285
286
287static uint8_t
288if_atsam_init_phy(if_atsam_gmac *gmac_inst, uint32_t mck,
289    const Pin *pResetPins, uint32_t nbResetPins, const Pin *pGmacPins,
290    uint32_t nbGmacPins)
291{
292        uint8_t rc = 1;
293        Gmac *pHw = gmac_inst->gGmacd.pHw;
294
295        /* Perform RESET */
296        if (pResetPins) {
297                /* Configure PINS */
298                PIO_Configure(pResetPins, nbResetPins);
299                PIO_Clear(pResetPins);
300                rtems_task_wake_after(1);
301                PIO_Set(pResetPins);
302        }
303        /* Configure GMAC runtime pins */
304        if (rc) {
305                PIO_Configure(pGmacPins, nbGmacPins);
306                rc = GMAC_SetMdcClock(pHw, mck);
307
308                if (!rc) {
309                        return (0);
310                }
311        }
312        return (rc);
313}
314
315
316static int
317if_atsam_miibus_readreg(device_t dev, int phy, int reg)
318{
319        uint32_t val;
320        uint8_t err;
321        if_atsam_softc *sc = device_get_softc(dev);
322
323        IF_ATSAM_LOCK(sc);
324        err = if_atsam_read_phy(sc->Gmac_inst.gGmacd.pHw,
325            (uint8_t)phy, (uint8_t)reg, &val, sc->Gmac_inst.retries);
326        IF_ATSAM_UNLOCK(sc);
327
328        return (err == 0 ? val : 0);
329}
330
331
332static int
333if_atsam_miibus_writereg(device_t dev, int phy, int reg, int data)
334{
335        uint8_t err;
336        if_atsam_softc *sc = device_get_softc(dev);
337
338        IF_ATSAM_LOCK(sc);
339        err = if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw,
340            (uint8_t)phy, (uint8_t)reg, data, sc->Gmac_inst.retries);
341        IF_ATSAM_UNLOCK(sc);
342
343        return 0;
344}
345
346
347/*
348 * Interrupt Handler for the network driver
349 */
350static void if_atsam_interrupt_handler(void *arg)
351{
352        if_atsam_softc *sc = (if_atsam_softc *)arg;
353        uint32_t irq_status_val;
354        rtems_event_set rx_event = 0;
355        rtems_event_set tx_event = 0;
356        Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
357
358        /* Get interrupt status */
359        irq_status_val = GMAC_GetItStatus(pHw, 0);
360
361        /* Check receive interrupts */
362        if ((irq_status_val & GMAC_IER_ROVR) != 0) {
363                ++sc->rx_overrun_errors;
364                rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT;
365        }
366        if ((irq_status_val & GMAC_IER_RCOMP) != 0) {
367                rx_event = ATSAMV7_ETH_RX_EVENT_INTERRUPT;
368        }
369        /* Send events to receive task and switch off rx interrupts */
370        if (rx_event != 0) {
371                ++sc->rx_interrupts;
372                /* Erase the interrupts for RX completion and errors */
373                GMAC_DisableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0);
374                (void)if_atsam_event_send(sc->rx_daemon_tid, rx_event);
375        }
376        if ((irq_status_val & GMAC_IER_TUR) != 0) {
377                ++sc->tx_tur_errors;
378                tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
379        }
380        if ((irq_status_val & GMAC_IER_RLEX) != 0) {
381                ++sc->tx_rlex_errors;
382                tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
383        }
384        if ((irq_status_val & GMAC_IER_TFC) != 0) {
385                ++sc->tx_tfc_errors;
386                tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
387        }
388        if ((irq_status_val & GMAC_IER_HRESP) != 0) {
389                ++sc->tx_hresp_errors;
390                tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
391        }
392        if ((irq_status_val & GMAC_IER_TCOMP) != 0) {
393                ++sc->tx_complete_int;
394                tx_event = ATSAMV7_ETH_TX_EVENT_INTERRUPT;
395        }
396        /* Send events to transmit task and switch off tx interrupts */
397        if (tx_event != 0) {
398                ++sc->tx_interrupts;
399                /* Erase the interrupts for TX completion and errors */
400                GMAC_DisableIt(pHw, GMAC_INT_TX_BITS, 0);
401                (void)if_atsam_event_send(sc->tx_daemon_tid, tx_event);
402        }
403}
404/*
405 * Receive daemon
406 */
407static void if_atsam_rx_daemon(void *arg)
408{
409        if_atsam_softc *sc = (if_atsam_softc *)arg;
410        struct ifnet *ifp = sc->ifp;
411        rtems_event_set events = 0;
412        void *rx_bd_base;
413        struct mbuf *m;
414        struct mbuf *n;
415        volatile sGmacRxDescriptor *buffer_desc;
416        int frame_len;
417        uint32_t tmp_rx_bd_address;
418        size_t i;
419        Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
420
421        IF_ATSAM_LOCK(sc);
422
423        if (IGNORE_RX_ERR) {
424                pHw->GMAC_NCFGR |= GMAC_NCFGR_IRXER;
425        } else {
426                pHw->GMAC_NCFGR &= ~GMAC_NCFGR_IRXER;
427        }
428
429        /* Allocate memory space for priority queue descriptor list */
430        rx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacRxDescriptor),
431                GMAC_DESCRIPTOR_ALIGNMENT, 0);
432        assert(rx_bd_base != NULL);
433
434        buffer_desc = (sGmacRxDescriptor *)rx_bd_base;
435        buffer_desc->addr.val = GMAC_RX_SET_USED_WRAP;
436        buffer_desc->status.val = 0;
437
438        GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 1);
439        GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 2);
440
441        /* Allocate memory space for buffer descriptor list */
442        rx_bd_base = rtems_cache_coherent_allocate(
443                sc->amount_rx_buf * sizeof(sGmacRxDescriptor),
444                GMAC_DESCRIPTOR_ALIGNMENT, 0);
445        assert(rx_bd_base != NULL);
446        buffer_desc = (sGmacRxDescriptor *)rx_bd_base;
447
448        /* Create descriptor list and mark as empty */
449        for (sc->rx_bd_fill_idx = 0; sc->rx_bd_fill_idx < sc->amount_rx_buf;
450            ++sc->rx_bd_fill_idx) {
451                m = if_atsam_new_mbuf(ifp);
452                assert(m != NULL);
453                sc->rx_mbuf[sc->rx_bd_fill_idx] = m;
454                buffer_desc->addr.val = ((uint32_t)m->m_data) &
455                    GMAC_RX_BUF_DESC_ADDR_MASK;
456                buffer_desc->status.val = 0;
457                if (sc->rx_bd_fill_idx == (sc->amount_rx_buf - 1)) {
458                        buffer_desc->addr.bm.bWrap = 1;
459                } else {
460                        buffer_desc++;
461                }
462        }
463        buffer_desc = (sGmacRxDescriptor *)rx_bd_base;
464
465        /* Set 2 Byte Receive Buffer Offset */
466        pHw->GMAC_NCFGR |= GMAC_RX_SET_OFFSET;
467
468        /* Write Buffer Queue Base Address Register */
469        GMAC_ReceiveEnable(pHw, 0);
470        GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 0);
471
472        /* Set address for address matching */
473        GMAC_SetAddress(pHw, 0, sc->GMacAddress);
474
475        /* Enable Receiving of data */
476        GMAC_ReceiveEnable(pHw, 1);
477
478        /* Setup the interrupts for RX completion and errors */
479        GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0);
480
481        sc->rx_bd_fill_idx = 0;
482
483        while (true) {
484                /* Wait for events */
485                if_atsam_event_receive(sc, ATSAMV7_ETH_RX_EVENT_INTERRUPT);
486
487                /*
488                 * Check for all packets with a set ownership bit
489                 */
490                while (buffer_desc->addr.bm.bOwnership == 1) {
491                        if (buffer_desc->status.bm.bEof == 1) {
492                                m = sc->rx_mbuf[sc->rx_bd_fill_idx];
493
494                                /* New mbuf for desc */
495                                n = if_atsam_new_mbuf(ifp);
496                                if (n != NULL) {
497                                        frame_len = (int)
498                                            (buffer_desc->status.bm.len);
499
500                                        /* Update mbuf */
501                                        m->m_data = mtod(m, char*)+ETHER_ALIGN;
502                                        m->m_len = frame_len;
503                                        m->m_pkthdr.len = frame_len;
504                                        IF_ATSAM_UNLOCK(sc);
505                                        sc->ifp->if_input(ifp, m);
506                                        IF_ATSAM_LOCK(sc);
507                                        m = n;
508                                } else {
509                                        (void)if_atsam_event_send(
510                                            sc->tx_daemon_tid, ATSAMV7_ETH_START_TRANSMIT_EVENT);
511                                }
512                                sc->rx_mbuf[sc->rx_bd_fill_idx] = m;
513                                tmp_rx_bd_address = (uint32_t)m->m_data &
514                                    GMAC_RX_BUF_DESC_ADDR_MASK;
515
516                                /* Switch pointer to next buffer descriptor */
517                                if (sc->rx_bd_fill_idx ==
518                                    (sc->amount_rx_buf - 1)) {
519                                        tmp_rx_bd_address |= GMAC_RX_SET_WRAP;
520                                        sc->rx_bd_fill_idx = 0;
521                                } else {
522                                        ++sc->rx_bd_fill_idx;
523                                }
524
525                                /*
526                                 * Give ownership to GMAC for further processing
527                                 */
528                                tmp_rx_bd_address &= ~GMAC_RX_SET_USED;
529                                _ARM_Data_synchronization_barrier();
530                                buffer_desc->addr.val = tmp_rx_bd_address;
531
532                                buffer_desc = (sGmacRxDescriptor *)rx_bd_base
533                                    + sc->rx_bd_fill_idx;
534                        }
535                }
536                /* Setup the interrupts for RX completion and errors */
537                GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0);
538        }
539}
540
541/*
542 * Update of current transmit buffer position.
543 */
544static void if_atsam_tx_bd_pos_update(size_t *pos, size_t amount_tx_buf)
545{
546        *pos = (*pos + 1) % amount_tx_buf;
547}
548
549/*
550 * Is RingBuffer empty
551 */
552static bool if_atsam_ring_buffer_empty(ring_buffer *ring_buffer)
553{
554        return (ring_buffer->tx_bd_used == ring_buffer->tx_bd_free);
555}
556
557/*
558 * Is RingBuffer full
559 */
560static bool if_atsam_ring_buffer_full(ring_buffer *ring_buffer)
561{
562        size_t tx_bd_used_next = ring_buffer->tx_bd_used;
563
564        if_atsam_tx_bd_pos_update(&tx_bd_used_next, ring_buffer->length);
565        return (tx_bd_used_next == ring_buffer->tx_bd_free);
566}
567
568/*
569 * Cleanup transmit file descriptors by freeing mbufs which are not needed any
570 * longer due to correct transmission.
571 */
572static void if_atsam_tx_bd_cleanup(if_atsam_softc *sc)
573{
574        struct mbuf *m;
575        volatile sGmacTxDescriptor *cur;
576        bool eof_needed = false;
577
578        while (!if_atsam_ring_buffer_empty(&sc->tx_ring)){
579                cur = sc->tx_bd_base + sc->tx_ring.tx_bd_free;
580                if (((cur->status.bm.bUsed == 1) && !eof_needed) || eof_needed) {
581                        eof_needed = true;
582                        cur->status.val |= GMAC_TX_SET_USED;
583                        m = sc->tx_mbuf[sc->tx_ring.tx_bd_free];
584                        m_free(m);
585                        sc->tx_mbuf[sc->tx_ring.tx_bd_free] = 0;
586                        if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_free,
587                            sc->tx_ring.length);
588                        if (cur->status.bm.bLastBuffer) {
589                                eof_needed = false;
590                        }
591                } else {
592                        break;
593                }
594        }
595}
596
597/*
598 * Prepare Ethernet frame to start transmission.
599 */
600static bool if_atsam_send_packet(if_atsam_softc *sc, struct mbuf *m)
601{
602        volatile sGmacTxDescriptor *cur;
603        volatile sGmacTxDescriptor *start_packet_tx_bd = 0;
604        int pos = 0;
605        uint32_t tmp_val = 0;
606        Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
607        bool success;
608
609        if_atsam_tx_bd_cleanup(sc);
610        /* Wait for interrupt in case no buffer descriptors are available */
611        /* Wait for events */
612        while (true) {
613                if (if_atsam_ring_buffer_full(&sc->tx_ring)) {
614                        /* Setup the interrupts for TX completion and errors */
615                        GMAC_EnableIt(pHw, GMAC_INT_TX_BITS, 0);
616                        success = false;
617                        break;
618                }
619
620                /*
621                 * Get current mbuf for data fill
622                 */
623                cur = &sc->tx_bd_base[sc->tx_ring.tx_bd_used];
624                /* Set the transfer data */
625                if (m->m_len) {
626                        uintptr_t cache_adjustment = mtod(m, uintptr_t) % 32;
627
628                        rtems_cache_flush_multiple_data_lines(
629                          mtod(m, const char *) - cache_adjustment,
630                          (size_t)(m->m_len + cache_adjustment));
631
632                        cur->addr = mtod(m, uint32_t);
633                        tmp_val = (uint32_t)m->m_len | GMAC_TX_SET_USED;
634                        if (sc->tx_ring.tx_bd_used == (sc->tx_ring.length - 1)) {
635                                tmp_val |= GMAC_TX_SET_WRAP;
636                        }
637                        if (pos == 0) {
638                                start_packet_tx_bd = cur;
639                        }
640                        sc->tx_mbuf[sc->tx_ring.tx_bd_used] = m;
641                        m = m->m_next;
642                        if_atsam_tx_bd_pos_update(&sc->tx_ring.tx_bd_used,
643                            sc->tx_ring.length);
644                } else {
645                        /* Discard empty mbufs */
646                        m = m_free(m);
647                }
648
649                /*
650                 * Send out the buffer once the complete mbuf_chain has been
651                 * processed
652                 */
653                if (m == NULL) {
654                        tmp_val |= GMAC_TX_SET_EOF;
655                        tmp_val &= ~GMAC_TX_SET_USED;
656                        _ARM_Data_synchronization_barrier();
657                        cur->status.val = tmp_val;
658                        start_packet_tx_bd->status.val &= ~GMAC_TX_SET_USED;
659                        _ARM_Data_synchronization_barrier();
660                        GMAC_TransmissionStart(pHw);
661                        success = true;
662                        break;
663                } else {
664                        if (pos > 0) {
665                                tmp_val &= ~GMAC_TX_SET_USED;
666                        }
667                        pos++;
668                        cur->status.val = tmp_val;
669                }
670        }
671        return success;
672}
673
674
675/*
676 * Transmit daemon
677 */
678static void if_atsam_tx_daemon(void *arg)
679{
680        if_atsam_softc *sc = (if_atsam_softc *)arg;
681        rtems_event_set events = 0;
682        sGmacTxDescriptor *buffer_desc;
683        int bd_number;
684        void *tx_bd_base;
685        struct mbuf *m;
686        bool success;
687
688        IF_ATSAM_LOCK(sc);
689
690        Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
691        struct ifnet *ifp = sc->ifp;
692
693        GMAC_TransmitEnable(pHw, 0);
694
695        /* Allocate memory space for priority queue descriptor list */
696        tx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacTxDescriptor),
697                GMAC_DESCRIPTOR_ALIGNMENT, 0);
698        assert(tx_bd_base != NULL);
699
700        buffer_desc = (sGmacTxDescriptor *)tx_bd_base;
701        buffer_desc->addr = 0;
702        buffer_desc->status.val = GMAC_TX_SET_USED | GMAC_TX_SET_WRAP;
703
704        GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 1);
705        GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 2);
706
707        /* Allocate memory space for buffer descriptor list */
708        tx_bd_base = rtems_cache_coherent_allocate(
709                sc->amount_tx_buf * sizeof(sGmacTxDescriptor),
710                GMAC_DESCRIPTOR_ALIGNMENT, 0);
711        assert(tx_bd_base != NULL);
712        buffer_desc = (sGmacTxDescriptor *)tx_bd_base;
713
714        /* Create descriptor list and mark as empty */
715        for (bd_number = 0; bd_number < sc->amount_tx_buf; bd_number++) {
716                buffer_desc->addr = 0;
717                buffer_desc->status.val = GMAC_TX_SET_USED;
718                if (bd_number == (sc->amount_tx_buf - 1)) {
719                        buffer_desc->status.bm.bWrap = 1;
720                } else {
721                        buffer_desc++;
722                }
723        }
724        buffer_desc = (sGmacTxDescriptor *)tx_bd_base;
725
726        /* Write Buffer Queue Base Address Register */
727        GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 0);
728
729        /* Enable Transmission of data */
730        GMAC_TransmitEnable(pHw, 1);
731
732        /* Set variables in context */
733        sc->tx_bd_base = tx_bd_base;
734
735        while (true) {
736                /* Wait for events */
737                if_atsam_event_receive(sc,
738                    ATSAMV7_ETH_START_TRANSMIT_EVENT |
739                    ATSAMV7_ETH_TX_EVENT_INTERRUPT);
740                //printf("TX Transmit Event received\n");
741
742                /*
743                 * Send packets till queue is empty
744                 */
745                while (true) {
746                        /*
747                         * Get the mbuf chain to transmit
748                         */
749                        if_atsam_tx_bd_cleanup(sc);
750                        IF_DEQUEUE(&ifp->if_snd, m);
751                        if (!m) {
752                                ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
753                                break;
754                        }
755                        success = if_atsam_send_packet(sc, m);
756                        if (!success){
757                                break;
758                        }
759                }
760        }
761}
762
763
764/*
765 * Send packet (caller provides header).
766 */
767static void if_atsam_enet_start(struct ifnet *ifp)
768{
769        if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc;
770
771        ifp->if_drv_flags |= IFF_DRV_OACTIVE;
772        if_atsam_event_send(sc->tx_daemon_tid,
773            ATSAMV7_ETH_START_TRANSMIT_EVENT);
774}
775
776
777static void if_atsam_miibus_statchg(device_t dev)
778{
779        uint8_t link_speed = GMAC_SPEED_100M;
780        uint8_t link_duplex = GMAC_DUPLEX_FULL;
781        if_atsam_softc *sc = device_get_softc(dev);
782        struct mii_data *mii = device_get_softc(sc->miibus);
783
784        Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
785
786        if (IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) {
787                link_duplex = GMAC_DUPLEX_FULL;
788        } else {
789                link_duplex = GMAC_DUPLEX_HALF;
790        }
791
792        switch (IFM_SUBTYPE(mii->mii_media_active)) {
793        case IFM_10_T:
794                link_speed = GMAC_SPEED_10M;
795                break;
796        case IFM_100_TX:
797                link_speed = GMAC_SPEED_100M;
798                break;
799        case IFM_1000_T:
800                link_speed = GMAC_SPEED_1000M;
801                break;
802        default:
803                /* FIXME: What to do in that case? */
804                break;
805        }
806
807        if (sc->link_speed != link_speed || sc->link_duplex != link_duplex) {
808                GMAC_SetLinkSpeed(pHw, link_speed, link_duplex);
809                sc->link_speed = link_speed;
810                sc->link_duplex = link_duplex;
811        }
812}
813
814
815static int
816if_atsam_mii_ifmedia_upd(struct ifnet *ifp)
817{
818        if_atsam_softc *sc;
819        struct mii_data *mii;
820
821        sc = ifp->if_softc;
822        if (sc->miibus == NULL)
823                return (ENXIO);
824
825        mii = device_get_softc(sc->miibus);
826        return (mii_mediachg(mii));
827}
828
829
830static void
831if_atsam_mii_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
832{
833        if_atsam_softc *sc;
834        struct mii_data *mii;
835
836        sc = ifp->if_softc;
837        if (sc->miibus == NULL)
838                return;
839
840        mii = device_get_softc(sc->miibus);
841        mii_pollstat(mii);
842        ifmr->ifm_active = mii->mii_media_active;
843        ifmr->ifm_status = mii->mii_media_status;
844}
845
846
847static void
848if_atsam_mii_tick(void *context)
849{
850        if_atsam_softc *sc = context;
851
852        if (sc->miibus == NULL)
853                return;
854
855        IF_ATSAM_UNLOCK(sc);
856
857        mii_tick(device_get_softc(sc->miibus));
858        callout_reset(&sc->mii_tick_ch, hz, if_atsam_mii_tick, sc);
859}
860
861
862/*
863 * Sets up the hardware and chooses the interface to be used
864 */
865static void if_atsam_init(void *arg)
866{
867        rtems_status_code status;
868
869        if_atsam_softc *sc = (if_atsam_softc *)arg;
870        struct ifnet *ifp = sc->ifp;
871        uint32_t dmac_cfg = 0;
872        uint32_t gmii_val = 0;
873
874        if (ifp->if_flags & IFF_DRV_RUNNING) {
875                return;
876        }
877        ifp->if_flags |= IFF_DRV_RUNNING;
878        sc->interrupt_number = GMAC_IRQn;
879
880        /* Disable Watchdog */
881        WDT_Disable(WDT);
882
883        /* Enable Peripheral Clock */
884        if ((PMC->PMC_PCSR1 & (1u << 7)) != (1u << 7)) {
885                PMC->PMC_PCER1 = 1 << 7;
886        }
887        /* Setup interrupts */
888        NVIC_ClearPendingIRQ(GMAC_IRQn);
889        NVIC_EnableIRQ(GMAC_IRQn);
890
891        /* Configuration of DMAC */
892        dmac_cfg = (GMAC_DCFGR_DRBS(GMAC_RX_BUFFER_SIZE >> 6)) |
893            GMAC_DCFGR_RXBMS(3) | GMAC_DCFGR_TXPBMS | GMAC_DCFGR_FBLDO_INCR16;
894        GMAC_SetDMAConfig(sc->Gmac_inst.gGmacd.pHw, dmac_cfg, 0);
895
896        /* Shut down Transmit and Receive */
897        GMAC_ReceiveEnable(sc->Gmac_inst.gGmacd.pHw, 0);
898        GMAC_TransmitEnable(sc->Gmac_inst.gGmacd.pHw, 0);
899
900        GMAC_StatisticsWriteEnable(sc->Gmac_inst.gGmacd.pHw, 1);
901
902        /*
903         * Allocate mbuf pointers
904         */
905        sc->rx_mbuf = malloc(sc->amount_rx_buf * sizeof *sc->rx_mbuf,
906                M_TEMP, M_NOWAIT);
907        sc->tx_mbuf = malloc(sc->amount_tx_buf * sizeof *sc->tx_mbuf,
908                M_TEMP, M_NOWAIT);
909
910        /* Install interrupt handler */
911        status = rtems_interrupt_handler_install(sc->interrupt_number,
912                "Ethernet",
913                RTEMS_INTERRUPT_UNIQUE,
914                if_atsam_interrupt_handler,
915                sc);
916        assert(status == RTEMS_SUCCESSFUL);
917
918        /*
919         * Start driver tasks
920         */
921        sc->rx_daemon_tid = rtems_bsdnet_newproc("SCrx", 4096,
922                if_atsam_rx_daemon, sc);
923        sc->tx_daemon_tid = rtems_bsdnet_newproc("SCtx", 4096,
924                if_atsam_tx_daemon, sc);
925
926        callout_reset(&sc->mii_tick_ch, hz, if_atsam_mii_tick, sc);
927
928        ifp->if_drv_flags |= IFF_DRV_RUNNING;
929}
930
931
932/*
933 * Stop the device
934 */
935static void if_atsam_stop(struct if_atsam_softc *sc)
936{
937        struct ifnet *ifp = sc->ifp;
938        Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
939
940        ifp->if_flags &= ~IFF_DRV_RUNNING;
941
942        /* Disable MDIO interface and TX/RX */
943        pHw->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN);
944        pHw->GMAC_NCR &= ~GMAC_NCR_MPE;
945}
946
947
948/*
949 * Show interface statistics
950 */
951static void if_atsam_stats(struct if_atsam_softc *sc)
952{
953        int eno = EIO;
954        Gmac *pHw;
955
956        pHw = sc->Gmac_inst.gGmacd.pHw;
957
958        printf("\n** Context Statistics **\n");
959        printf("Rx interrupts: %u\n", sc->rx_interrupts);
960        printf("Tx interrupts: %u\n", sc->tx_interrupts);
961        printf("Error Tur Tx interrupts: %u\n\n", sc->tx_tur_errors);
962        printf("Error Rlex Tx interrupts: %u\n\n", sc->tx_rlex_errors);
963        printf("Error Tfc Tx interrupts: %u\n\n", sc->tx_tfc_errors);
964        printf("Error Hresp Tx interrupts: %u\n\n", sc->tx_hresp_errors);
965        printf("Tx complete interrupts: %u\n\n", sc->tx_complete_int);
966        printf("\n** Statistics **\n");
967        printf("Octets Transmitted Low: %lu\n", pHw->GMAC_OTLO);
968        printf("Octets Transmitted High: %lu\n", pHw->GMAC_OTHI);
969        printf("Frames Transmitted: %lu\n", pHw->GMAC_FT);
970        printf("Broadcast Frames Transmitted: %lu\n", pHw->GMAC_BCFT);
971        printf("Multicast Frames Transmitted: %lu\n", pHw->GMAC_MFT);
972        printf("Pause Frames Transmitted: %lu\n", pHw->GMAC_PFT);
973        printf("64 Byte Frames Transmitted: %lu\n", pHw->GMAC_BFT64);
974        printf("65 to 127 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFT127);
975        printf("128 to 255 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFR255);
976        printf("256 to 511 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFT511);
977        printf("512 to 1023 Byte Frames Transmitted: %lu\n",
978            pHw->GMAC_TBFT1023);
979        printf("1024 to 1518 Byte Frames Transmitted: %lu\n",
980            pHw->GMAC_TBFT1518);
981        printf("Greater Than 1518 Byte Frames Transmitted: %lu\n",
982            pHw->GMAC_GTBFT1518);
983        printf("Transmit Underruns: %lu\n", pHw->GMAC_TUR);
984        printf("Single Collision Frames: %lu\n", pHw->GMAC_SCF);
985        printf("Multiple Collision Frames: %lu\n", pHw->GMAC_MCF);
986        printf("Excessive Collisions: %lu\n", pHw->GMAC_EC);
987        printf("Late Collisions: %lu\n", pHw->GMAC_LC);
988        printf("Deferred Transmission Frames: %lu\n", pHw->GMAC_DTF);
989        printf("Carrier Sense Errors: %lu\n", pHw->GMAC_CSE);
990        printf("Octets Received Low: %lu\n", pHw->GMAC_ORLO);
991        printf("Octets Received High: %lu\n", pHw->GMAC_ORHI);
992        printf("Frames Received: %lu\n", pHw->GMAC_FR);
993        printf("Broadcast Frames Received: %lu\n", pHw->GMAC_BCFR);
994        printf("Multicast Frames Received: %lu\n", pHw->GMAC_MFR);
995        printf("Pause Frames Received: %lu\n", pHw->GMAC_PFR);
996        printf("64 Byte Frames Received: %lu\n", pHw->GMAC_BFR64);
997        printf("65 to 127 Byte Frames Received: %lu\n", pHw->GMAC_TBFR127);
998        printf("128 to 255 Byte Frames Received: %lu\n", pHw->GMAC_TBFR255);
999        printf("256 to 511 Byte Frames Received: %lu\n", pHw->GMAC_TBFR511);
1000        printf("512 to 1023 Byte Frames Received: %lu\n", pHw->GMAC_TBFR1023);
1001        printf("1024 to 1518 Byte Frames Received: %lu\n", pHw->GMAC_TBFR1518);
1002        printf("1519 to Maximum Byte Frames Received: %lu\n",
1003            pHw->GMAC_TBFR1518);
1004        printf("Undersize Frames Received: %lu\n", pHw->GMAC_UFR);
1005        printf("Oversize Frames Received: %lu\n", pHw->GMAC_OFR);
1006        printf("Jabbers Received: %lu\n", pHw->GMAC_JR);
1007        printf("Frame Check Sequence Errors: %lu\n", pHw->GMAC_FCSE);
1008        printf("Length Field Frame Errors: %lu\n", pHw->GMAC_LFFE);
1009        printf("Receive Symbol Errors: %lu\n", pHw->GMAC_RSE);
1010        printf("Alignment Errors: %lu\n", pHw->GMAC_AE);
1011        printf("Receive Resource Errors: %lu\n", pHw->GMAC_RRE);
1012        printf("Receive Overrun: %lu\n", pHw->GMAC_ROE);
1013        printf("IP Header Checksum Errors: %lu\n", pHw->GMAC_IHCE);
1014        printf("TCP Checksum Errors: %lu\n", pHw->GMAC_TCE);
1015        printf("UDP Checksum Errors: %lu\n", pHw->GMAC_UCE);
1016}
1017
1018
1019/*
1020 * Calculates the index that is to be sent into the hash registers
1021 */
1022static void if_atsam_get_hash_index(uint64_t addr, uint32_t *val)
1023{
1024        uint64_t tmp_val;
1025        uint8_t i, j;
1026        uint64_t idx;
1027        int offset = 0;
1028
1029        addr &= MAC_ADDR_MASK;
1030
1031        for (i = 0; i < HASH_INDEX_AMOUNT; ++i) {
1032                tmp_val = 0;
1033                offset = 0;
1034                for (j = 0; j < HASH_ELEMENTS_PER_INDEX; j++) {
1035                        idx = (addr >> (offset + i)) & MAC_IDX_MASK;
1036                        tmp_val ^= idx;
1037                        offset += HASH_INDEX_AMOUNT;
1038                }
1039                if (tmp_val > 0) {
1040                        *val |= (1u << i);
1041                }
1042        }
1043}
1044
1045
1046/*
1047 * Dis/Enable promiscuous Mode
1048 */
1049static void if_atsam_promiscuous_mode(if_atsam_softc *sc, bool enable)
1050{
1051        Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
1052
1053        if (enable) {
1054                pHw->GMAC_NCFGR |= GMAC_PROM_ENABLE;
1055        } else {
1056                pHw->GMAC_NCFGR &= ~GMAC_PROM_ENABLE;
1057        }
1058}
1059
1060
1061static int
1062if_atsam_mediaioctl(if_atsam_softc *sc, struct ifreq *ifr, u_long command)
1063{
1064        struct mii_data *mii;
1065
1066        if (sc->miibus == NULL)
1067                return (EINVAL);
1068
1069        mii = device_get_softc(sc->miibus);
1070        return (ifmedia_ioctl(sc->ifp, ifr, &mii->mii_media, command));
1071}
1072
1073
1074/*
1075 * Driver ioctl handler
1076 */
1077static int
1078if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
1079{
1080        if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc;
1081        struct ifreq *ifr = (struct ifreq *)data;
1082        int rv = 0;
1083        bool prom_enable;
1084        struct mii_data *mii;
1085
1086        switch (command) {
1087        case SIOCGIFMEDIA:
1088        case SIOCSIFMEDIA:
1089                rv = if_atsam_mediaioctl(sc, ifr, command);
1090                break;
1091        case SIOCSIFFLAGS:
1092                if (ifp->if_flags & IFF_UP) {
1093                        if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
1094                                if_atsam_init(sc);
1095                        }
1096                        prom_enable = ((ifp->if_flags & IFF_PROMISC) != 0);
1097                        if_atsam_promiscuous_mode(sc, prom_enable);
1098                } else {
1099                        if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1100                                if_atsam_stop(sc);
1101                        }
1102                }
1103                break;
1104        case SIO_RTEMS_SHOW_STATS:
1105                if_atsam_stats(sc);
1106                break;
1107        default:
1108                rv = ether_ioctl(ifp, command, data);
1109                break;
1110        }
1111        return (rv);
1112}
1113
1114/*
1115 * Attach an SAMV71 driver to the system
1116 */
1117static int if_atsam_driver_attach(device_t dev)
1118{
1119        if_atsam_softc *sc;
1120        struct ifnet *ifp;
1121        int unit;
1122        char *unitName;
1123        uint8_t eaddr[ETHER_ADDR_LEN];
1124
1125        sc = device_get_softc(dev);
1126        unit = device_get_unit(dev);
1127        assert(unit == 0);
1128
1129        sc->dev = dev;
1130        sc->ifp = ifp = if_alloc(IFT_ETHER);
1131
1132        mtx_init(&sc->mtx, device_get_nameunit(sc->dev), MTX_NETWORK_LOCK,
1133            MTX_DEF);
1134
1135        rtems_bsd_get_mac_address(device_get_name(sc->dev), unit, eaddr);
1136
1137        sc->Gmac_inst.retries = MDIO_RETRIES;
1138
1139        memcpy(sc->GMacAddress, eaddr, ETHER_ADDR_LEN);
1140
1141        sc->amount_rx_buf = RXBUF_COUNT;
1142        sc->amount_tx_buf = TXBUF_COUNT;
1143
1144        sc->tx_ring.tx_bd_used = 0;
1145        sc->tx_ring.tx_bd_free = 0;
1146        sc->tx_ring.length = sc->amount_tx_buf;
1147
1148        /* Set Initial Link Speed */
1149        sc->link_speed = GMAC_SPEED_100M;
1150        sc->link_duplex = GMAC_DUPLEX_FULL;
1151
1152        GMACD_Init(&sc->Gmac_inst.gGmacd, GMAC, ID_GMAC, GMAC_CAF_ENABLE,
1153            GMAC_NBC_DISABLE);
1154
1155        /* Enable MDIO interface */
1156        GMAC_EnableMdio(sc->Gmac_inst.gGmacd.pHw);
1157
1158        /* PHY initialize */
1159        if_atsam_init_phy(&sc->Gmac_inst, BOARD_MCK, NULL, 0,
1160            gmacPins, PIO_LISTSIZE(gmacPins));
1161
1162        /*
1163         * MII Bus
1164         */
1165        callout_init_mtx(&sc->mii_tick_ch, &sc->mtx, CALLOUT_RETURNUNLOCKED);
1166        mii_attach(dev, &sc->miibus, ifp,
1167            if_atsam_mii_ifmedia_upd, if_atsam_mii_ifmedia_sts, BMSR_DEFCAPMASK,
1168            MDIO_PHY, MII_OFFSET_ANY, 0);
1169
1170        /*
1171         * Set up network interface values
1172         */
1173        ifp->if_softc = sc;
1174        if_initname(ifp, device_get_name(dev), device_get_unit(dev));
1175        ifp->if_init = if_atsam_init;
1176        ifp->if_ioctl = if_atsam_ioctl;
1177        ifp->if_start = if_atsam_enet_start;
1178        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
1179        IFQ_SET_MAXLEN(&ifp->if_snd, TXBUF_COUNT - 1);
1180        ifp->if_snd.ifq_drv_maxlen = TXBUF_COUNT - 1;
1181        IFQ_SET_READY(&ifp->if_snd);
1182
1183        /*
1184         * Attach the interface
1185         */
1186        ether_ifattach(ifp, eaddr);
1187
1188        return (0);
1189}
1190
1191static int
1192if_atsam_probe(device_t dev)
1193{
1194        int unit = device_get_unit(dev);
1195        int error;
1196
1197        if (unit >= 0 && unit < NIFACES) {
1198                error = BUS_PROBE_DEFAULT;
1199        } else {
1200                error = ENXIO;
1201        }
1202
1203        return (error);
1204}
1205
1206static device_method_t if_atsam_methods[] = {
1207        DEVMETHOD(device_probe,         if_atsam_probe),
1208        DEVMETHOD(device_attach,        if_atsam_driver_attach),
1209        DEVMETHOD(miibus_readreg,       if_atsam_miibus_readreg),
1210        DEVMETHOD(miibus_writereg,      if_atsam_miibus_writereg),
1211        DEVMETHOD(miibus_statchg,       if_atsam_miibus_statchg),
1212        DEVMETHOD_END
1213};
1214
1215static driver_t if_atsam_nexus_driver = {
1216        "if_atsam",
1217        if_atsam_methods,
1218        sizeof(struct if_atsam_softc)
1219};
1220
1221static devclass_t if_atsam_devclass;
1222DRIVER_MODULE(if_atsam, nexus, if_atsam_nexus_driver, if_atsam_devclass, 0, 0);
1223MODULE_DEPEND(if_atsam, miibus, 1, 1, 1);
1224MODULE_DEPEND(if_atsam, nexus, 1, 1, 1);
1225MODULE_DEPEND(if_atsam, ether, 1, 1, 1);
1226DRIVER_MODULE(miibus, if_atsam, miibus_driver, miibus_devclass, NULL, NULL);
1227
1228#endif /* LIBBSP_ARM_ATSAM_BSP_H */
Note: See TracBrowser for help on using the repository browser.