source: rtems/c/src/libchip/network/greth.c @ b3ee778e

4.104.114.84.95
Last change on this file since b3ee778e was 81d914d8, checked in by Joel Sherrill <joel.sherrill@…>, on 07/11/06 at 14:15:22

2006-07-11 Jerry Needell <jerry.needell@…>

  • libchip/Makefile.am, libchip/preinstall.am: Merge SPARC updates from 4.6 branch. Original modifications by Gaisler Enterprises.
  • libchip/network/greth.c, libchip/network/greth.h: New files.
  • Property mode set to 100644
File size: 15.8 KB
Line 
1/*
2 *  RTEMS driver for Opencores Ethernet Controller
3 *
4 *  Weakly based on dec21140 rtems driver and open_eth linux driver
5 *  Written by Jiri Gaisler, Gaisler Research
6 *
7 *  The license and distribution terms for this file may be
8 *  found in found in the file LICENSE in this distribution or at
9 *  http://www.OARcorp.com/rtems/license.html.
10 *
11 *  $Id$
12 */
13
14#include <rtems.h>
15
16#define GRETH_SUPPORTED
17#include <bsp.h>
18
19#include <inttypes.h>
20#include <errno.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <stdarg.h>
24#include <rtems/error.h>
25#include <rtems/rtems_bsdnet.h>
26#include "greth.h"
27
28#include <sys/param.h>
29#include <sys/mbuf.h>
30
31#include <sys/socket.h>
32#include <sys/sockio.h>
33#include <net/if.h>
34#include <netinet/in.h>
35#include <netinet/if_ether.h>
36
37#ifdef malloc
38#undef malloc
39#endif
40#ifdef free
41#undef free
42#endif
43
44/*
45#define GRETH_DEBUG
46*/
47
48#ifdef CPU_U32_FIX
49extern void ipalign(struct mbuf *m);
50#endif
51
52/*
53 * Number of OCs supported by this driver
54 */
55#define NOCDRIVER       1
56
57/*
58 * Receive buffer size -- Allow for a full ethernet packet including CRC
59 */
60#define RBUF_SIZE 1518
61
62#define ET_MINLEN 64            /* minimum message length */
63
64/*
65 * RTEMS event used by interrupt handler to signal driver tasks.
66 * This must not be any of the events used by the network task synchronization.
67 */
68#define INTERRUPT_EVENT RTEMS_EVENT_1
69
70/*
71 * RTEMS event used to start transmit daemon.
72 * This must not be the same as INTERRUPT_EVENT.
73 */
74#define START_TRANSMIT_EVENT    RTEMS_EVENT_2
75
76 /* event to send when tx buffers become available */
77#define GRETH_TX_WAIT_EVENT  RTEMS_EVENT_3
78
79 /* suspend when all TX descriptors exhausted */
80 /*
81#define GRETH_SUSPEND_NOTXBUF
82 */
83
84#if (MCLBYTES < RBUF_SIZE)
85# error "Driver must have MCLBYTES > RBUF_SIZE"
86#endif
87
88/* Ethernet buffer descriptor */
89
90typedef struct _greth_rxtxdesc {
91   volatile uint32_t ctrl; /* Length and status */
92   uint32_t *addr;         /* Buffer pointer */
93} greth_rxtxdesc;
94
95/*
96 * Per-device data
97 */
98struct greth_softc
99{
100
101   struct arpcom arpcom;
102   
103   greth_regs *regs;
104   
105   int acceptBroadcast;
106   rtems_id rxDaemonTid;
107   rtems_id txDaemonTid;
108   
109   unsigned int tx_ptr;
110   unsigned int rx_ptr;
111   unsigned int txbufs;
112   unsigned int rxbufs;
113   greth_rxtxdesc *txdesc;
114   greth_rxtxdesc *rxdesc;
115   struct mbuf **rxmbuf;
116   rtems_vector_number vector;
117   
118   /*
119    * Statistics
120    */
121   unsigned long rxInterrupts;
122   
123   unsigned long rxPackets;
124   unsigned long rxLengthError;
125   unsigned long rxNonOctet;
126   unsigned long rxBadCRC;
127   unsigned long rxOverrun;
128   
129   unsigned long txInterrupts;
130   
131   unsigned long txDeferred;
132   unsigned long txHeartbeat;
133   unsigned long txLateCollision;
134   unsigned long txRetryLimit;
135   unsigned long txUnderrun;
136};
137
138static struct greth_softc greth;
139
140static char *almalloc(int sz)
141{
142        char *tmp;
143        tmp = calloc(1,2*sz);
144        tmp = (char *) (((int)tmp+sz) & ~(sz -1));
145        return(tmp);
146}
147
148/* GRETH interrupt handler */
149
150static rtems_isr
151greth_interrupt_handler (rtems_vector_number v)
152{
153    uint32_t status;
154    /* read and clear interrupt cause */
155
156    status = greth.regs->status;
157    greth.regs->status = status;
158
159    /* Frame received? */
160    if (status & (GRETH_STATUS_RXERR | GRETH_STATUS_RXIRQ))
161      {
162        greth.rxInterrupts++;
163        rtems_event_send (greth.rxDaemonTid, INTERRUPT_EVENT);
164      }
165#ifdef GRETH_SUSPEND_NOTXBUF
166    if (status & (GRETH_STATUS_TXERR | GRETH_STATUS_TXIRQ))
167      {
168        greth.txInterrupts++;
169        rtems_event_send (greth.txDaemonTid, GRETH_TX_WAIT_EVENT);
170      }
171#endif
172      /*
173#ifdef __leon__
174      LEON_Clear_interrupt(v-0x10);
175#endif
176      */
177}
178
179static uint32_t read_mii(uint32_t addr)
180{
181    while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
182    greth.regs->mdio_ctrl = addr << 6 | GRETH_MDIO_READ;
183    while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
184    if (!(greth.regs->mdio_ctrl & GRETH_MDIO_LINKFAIL))
185        return((greth.regs->mdio_ctrl >> 16) & 0xFFFF);
186    else {
187        printf("greth: failed to read mii\n");
188        return (0);
189    }
190}
191
192static void write_mii(uint32_t addr, uint32_t data)
193{
194    while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
195    greth.regs->mdio_ctrl =
196    ((data & 0xFFFF) << 16) | (addr << 8) | GRETH_MDIO_WRITE;
197    while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
198}
199/*
200 * Initialize the ethernet hardware
201 */
202static void
203greth_initialize_hardware (struct greth_softc *sc)
204{
205    struct mbuf *m;
206    int i;
207    int fd;
208   
209    greth_regs *regs;
210
211    regs = sc->regs;
212
213    /* Reset the controller.  */
214    greth.rxInterrupts = 0;
215    greth.rxPackets = 0;
216
217    regs->ctrl = 0;
218    regs->ctrl = GRETH_CTRL_RST;        /* Reset ON */
219    regs->ctrl = 0;                     /* Reset OFF */
220   
221    /* reset PHY and wait for complettion */
222    /*
223    */
224    write_mii(0, 0x8000);
225    while (read_mii(0) & 0x8000) {}
226    fd = regs->mdio_ctrl >> 24; /*duplex mode*/
227    printf("greth: driver attached, PHY config : 0x%04x\n", read_mii(0));
228
229    /* Initialize rx/tx descriptor pointers */
230    sc->txdesc = (greth_rxtxdesc *) almalloc(1024);
231    sc->rxdesc = (greth_rxtxdesc *) almalloc(1024);
232    sc->tx_ptr = 0;
233    sc->rx_ptr = 0;
234    regs->txdesc = (int) sc->txdesc;
235    regs->rxdesc = (int) sc->rxdesc;
236   
237    sc->rxmbuf = calloc(sc->rxbufs, sizeof(*sc->rxmbuf));
238
239    for (i = 0; i < sc->txbufs; i++)
240      {
241          sc->txdesc[i].addr = (uint32_t *) calloc(1, GRETH_MAXBUF_LEN);
242#ifdef GRETH_DEBUG
243          printf("TXBUF: %08x\n", (int) sc->txdesc[i].addr);
244#endif
245      }
246    /*printf("RXbufs: %i\n", sc->rxbufs);*/
247    for (i = 0; i < sc->rxbufs; i++)
248      {
249
250          MGETHDR (m, M_WAIT, MT_DATA);
251          MCLGET (m, M_WAIT);
252          m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
253          sc->rxmbuf[i] = m;
254          sc->rxdesc[i].addr = (uint32_t *) mtod(m, uint32_t *);
255          sc->rxdesc[i].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ;
256#ifdef GRETH_DEBUG
257          printf("RXBUF: %08x\n", (int) sc->rxdesc[i].addr);
258#endif
259      }
260    sc->rxdesc[sc->rxbufs - 1].ctrl |= GRETH_RXD_WRAP;
261
262    /* set ethernet address.  */
263    regs->mac_addr_msb =
264      sc->arpcom.ac_enaddr[0] << 8 | sc->arpcom.ac_enaddr[1];
265    regs->mac_addr_lsb =
266      sc->arpcom.ac_enaddr[2] << 24 | sc->arpcom.ac_enaddr[3] << 16 |
267      sc->arpcom.ac_enaddr[4] << 8 | sc->arpcom.ac_enaddr[5];
268
269    /* install interrupt vector */
270    set_vector(greth_interrupt_handler, sc->vector, 1);
271
272    /* clear all pending interrupts */
273
274    regs->status = 0xffffffff;
275
276#ifdef GRETH_SUSPEND_NOTXBUF
277    regs->ctrl |= GRETH_CTRL_TXIRQ;
278#endif
279
280    regs->ctrl |= GRETH_CTRL_RXEN | (fd << 4) | GRETH_CTRL_RXIRQ;
281}
282
283static void
284greth_rxDaemon (void *arg)
285{
286    struct ether_header *eh;
287    struct greth_softc *dp = (struct greth_softc *) &greth;
288    struct ifnet *ifp = &dp->arpcom.ac_if;
289    struct mbuf *m;
290    unsigned int len, len_status, bad;
291    rtems_event_set events;
292   
293    /*printf("Started RxDaemon\n");*/
294    for (;;)
295      {
296        rtems_bsdnet_event_receive (INTERRUPT_EVENT,
297                                    RTEMS_WAIT | RTEMS_EVENT_ANY,
298                                    RTEMS_NO_TIMEOUT, &events);
299       
300#ifdef GRETH_ETH_DEBUG
301    printf ("r\n");
302#endif
303    /*printf("Packet received\n");*/
304            while (!((len_status =
305                   dp->rxdesc[dp->rx_ptr].ctrl) & GRETH_RXD_ENABLE))
306            {
307              /*printf("Status: %x\n", dp->rxdesc[dp->rx_ptr].ctrl);*/
308                bad = 0;
309
310                if (len_status & GRETH_RXD_TOOLONG)
311                  {
312                      dp->rxLengthError++;
313                      bad = 1;
314                  }
315                if (len_status & GRETH_RXD_DRIBBLE)
316                  {
317                      dp->rxNonOctet++;
318                      bad = 1;
319                  }
320                if (len_status & GRETH_RXD_CRCERR)
321                  {
322                      dp->rxBadCRC++;
323                      bad = 1;
324                  }
325                if (len_status & GRETH_RXD_OVERRUN)
326                  {
327                      dp->rxOverrun++;
328                      bad = 1;
329                  }
330                if (!bad)
331                  {
332                    /*printf("Received Ok packet\n");*/
333                      /* pass on the packet in the receive buffer */
334                    len = len_status & 0x7FF;
335                    m = dp->rxmbuf[dp->rx_ptr];
336                    m->m_len = m->m_pkthdr.len =
337                      len - sizeof (struct ether_header);
338                    /*printf("Packet of length: %i\n", len);*/
339                    eh = mtod (m, struct ether_header *);
340                    m->m_data += sizeof (struct ether_header);
341                    /*printf("Mbuf handling done\n");*/
342#ifdef CPU_U32_FIX
343                    /*printf("Ip aligning\n");*/
344                    ipalign(m); /* Align packet on 32-bit boundary */
345#endif
346                    /*printf("Calling stack\n");*/
347                    /*printf("Ifp: %x, Eh: %x, M: %x\n", (int)ifp, (int)eh, (int)m);*/
348                    ether_input (ifp, eh, m);
349                    /*printf("Returned from stack\n");*/
350                    /* get a new mbuf */
351                    /*printf("Getting new mbuf\n");*/
352                    MGETHDR (m, M_WAIT, MT_DATA);
353                    MCLGET (m, M_WAIT);
354                    /*printf("Got new mbuf\n");*/
355                    dp->rxmbuf[dp->rx_ptr] = m;
356                    m->m_pkthdr.rcvif = ifp;
357                    dp->rxdesc[dp->rx_ptr].addr =
358                      (uint32_t *) mtod (m, uint32_t *);
359                    dp->rxPackets++;
360                  }
361                /*printf("Reenabling desc\n");*/
362                dp->rxdesc[dp->rx_ptr].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ;
363                if (dp->rx_ptr == dp->rxbufs - 1) {
364                  dp->rxdesc[dp->rx_ptr].ctrl |= GRETH_RXD_WRAP;
365                }
366                dp->regs->ctrl |= GRETH_CTRL_RXEN;
367                /*printf("rxptr: %i\n", dp->rx_ptr);*/
368                dp->rx_ptr = (dp->rx_ptr + 1) % dp->rxbufs;
369                /*printf("RxDesc reenabled\n");*/
370            }
371      }
372   
373}
374
375static int inside = 0;
376static void
377sendpacket (struct ifnet *ifp, struct mbuf *m)
378{
379    struct greth_softc *dp = ifp->if_softc;
380    unsigned char *temp;
381    struct mbuf *n;
382    unsigned int len;
383   
384    /*printf("Send packet entered\n");*/
385    if (inside) printf ("error: sendpacket re-entered!!\n");
386    inside = 1;
387    /*
388     * Waiting for Transmitter ready
389     */
390    n = m;
391
392    while (dp->txdesc[dp->tx_ptr].ctrl & GRETH_TXD_ENABLE)
393      {
394#ifdef GRETH_SUSPEND_NOTXBUF
395        dp->txdesc[dp->tx_ptr].ctrl |= GRETH_TXD_IRQ;
396        rtems_event_set events;
397        rtems_bsdnet_event_receive (GRETH_TX_WAIT_EVENT,
398                                    RTEMS_WAIT | RTEMS_EVENT_ANY,
399                                    TOD_MILLISECONDS_TO_TICKS(500), &events);
400#endif
401      }
402
403    len = 0;
404    temp = (unsigned char *) dp->txdesc[dp->tx_ptr].addr;
405#ifdef GRETH_DEBUG
406    printf("TXD: 0x%08x\n", (int) m->m_data);
407#endif
408    for (;;)
409        {
410#ifdef GRETH_DEBUG
411          int i;
412          printf("MBUF: 0x%08x : ", (int) m->m_data);
413          for (i=0;i<m->m_len;i++)
414            printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
415          printf("\n");
416#endif
417          len += m->m_len;
418          if (len <= RBUF_SIZE)
419            memcpy ((void *) temp, (char *) m->m_data, m->m_len);
420          temp += m->m_len;
421          if ((m = m->m_next) == NULL)
422              break;
423        }
424
425    m_freem (n);
426
427    /* don't send long packets */
428
429    if (len <= GRETH_MAXBUF_LEN) {
430      if (dp->tx_ptr < dp->txbufs-1) {
431        dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_ENABLE | len;
432      } else {
433        dp->txdesc[dp->tx_ptr].ctrl =
434          GRETH_TXD_WRAP | GRETH_TXD_ENABLE | len;
435      }
436      dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN;
437      dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
438    }
439    inside = 0;
440}
441
442/*
443 * Driver transmit daemon
444 */
445void
446greth_txDaemon (void *arg)
447{
448    struct greth_softc *sc = (struct greth_softc *) arg;
449    struct ifnet *ifp = &sc->arpcom.ac_if;
450    struct mbuf *m;
451    rtems_event_set events;
452   
453    for (;;)
454      {
455          /*
456           * Wait for packet
457           */
458
459        rtems_bsdnet_event_receive (START_TRANSMIT_EVENT,
460                                      RTEMS_EVENT_ANY | RTEMS_WAIT,
461                                      RTEMS_NO_TIMEOUT, &events);
462#ifdef GRETH_DEBUG
463    printf ("t\n");
464#endif
465
466          /*
467           * Send packets till queue is empty
468           */
469          for (;;)
470            {
471                /*
472                 * Get the next mbuf chain to transmit.
473                 */
474                IF_DEQUEUE (&ifp->if_snd, m);
475                if (!m)
476                    break;
477               
478                sendpacket (ifp, m);
479            }
480          ifp->if_flags &= ~IFF_OACTIVE;
481      }
482}
483
484
485static void
486greth_start (struct ifnet *ifp)
487{
488    struct greth_softc *sc = ifp->if_softc;
489   
490    ifp->if_flags |= IFF_OACTIVE;
491    rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
492   
493}
494
495/*
496 * Initialize and start the device
497 */
498static void
499greth_init (void *arg)
500{
501    struct greth_softc *sc = arg;
502    struct ifnet *ifp = &sc->arpcom.ac_if;
503
504    if (sc->txDaemonTid == 0)
505      {
506
507          /*
508           * Set up GRETH hardware
509           */
510          greth_initialize_hardware (sc);
511
512          /*
513           * Start driver tasks
514           */
515          sc->rxDaemonTid = rtems_bsdnet_newproc ("DCrx", 4096,
516                                                  greth_rxDaemon, sc);
517          sc->txDaemonTid = rtems_bsdnet_newproc ("DCtx", 4096,
518                                                  greth_txDaemon, sc);
519      }
520
521    /*
522     * Tell the world that we're running.
523     */
524    ifp->if_flags |= IFF_RUNNING;
525
526}
527
528/*
529 * Stop the device
530 */
531static void
532greth_stop (struct greth_softc *sc)
533{
534    struct ifnet *ifp = &sc->arpcom.ac_if;
535
536    ifp->if_flags &= ~IFF_RUNNING;
537
538    sc->regs->ctrl = 0;                 /* RX/TX OFF */
539    sc->regs->ctrl = GRETH_CTRL_RST;    /* Reset ON */
540    sc->regs->ctrl = 0;                 /* Reset OFF */
541}
542
543
544/*
545 * Show interface statistics
546 */
547static void
548greth_stats (struct greth_softc *sc)
549{
550  printf ("      Rx Interrupts:%-8lu", sc->rxInterrupts);
551  printf ("      Rx Packets:%-8lu", sc->rxPackets);
552  printf ("          Length:%-8lu", sc->rxLengthError);
553  printf ("       Non-octet:%-8lu\n", sc->rxNonOctet);
554  printf ("            Bad CRC:%-8lu", sc->rxBadCRC);
555  printf ("         Overrun:%-8lu", sc->rxOverrun);
556  printf ("      Tx Interrupts:%-8lu", sc->txInterrupts);
557}
558
559/*
560 * Driver ioctl handler
561 */
562static int
563greth_ioctl (struct ifnet *ifp, int command, caddr_t data)
564{
565    struct greth_softc *sc = ifp->if_softc;
566    int error = 0;
567
568    switch (command)
569      {
570      case SIOCGIFADDR:
571      case SIOCSIFADDR:
572          ether_ioctl (ifp, command, data);
573          break;
574
575      case SIOCSIFFLAGS:
576          switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
577            {
578            case IFF_RUNNING:
579                greth_stop (sc);
580                break;
581
582            case IFF_UP:
583                greth_init (sc);
584                break;
585
586            case IFF_UP | IFF_RUNNING:
587                greth_stop (sc);
588                greth_init (sc);
589                break;
590       default:
591                break;
592            }
593          break;
594
595      case SIO_RTEMS_SHOW_STATS:
596          greth_stats (sc);
597          break;
598
599          /*
600           * FIXME: All sorts of multicast commands need to be added here!
601           */
602      default:
603          error = EINVAL;
604          break;
605      }
606
607    return error;
608}
609
610/*
611 * Attach an GRETH driver to the system
612 */
613int
614rtems_greth_driver_attach (struct rtems_bsdnet_ifconfig *config,
615                           greth_configuration_t *chip)
616{
617    struct greth_softc *sc;
618    struct ifnet *ifp;
619    int mtu;
620    int unitNumber;
621    char *unitName;
622
623      /* parse driver name */
624    if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
625        return 0;
626
627    sc = &greth;
628    ifp = &sc->arpcom.ac_if;
629    memset (sc, 0, sizeof (*sc));
630
631    if (config->hardware_address)
632      {
633          memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
634                  ETHER_ADDR_LEN);
635      }
636    else
637      {
638          memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
639      }
640
641    if (config->mtu)
642        mtu = config->mtu;
643    else
644        mtu = ETHERMTU;
645
646    sc->acceptBroadcast = !config->ignore_broadcast;
647    sc->regs = (void *) chip->base_address;
648    sc->vector = chip->vector;
649    sc->txbufs = chip->txd_count;
650    sc->rxbufs = chip->rxd_count;
651   
652    /*
653     * Set up network interface values
654     */
655    ifp->if_softc = sc;
656    ifp->if_unit = unitNumber;
657    ifp->if_name = unitName;
658    ifp->if_mtu = mtu;
659    ifp->if_init = greth_init;
660    ifp->if_ioctl = greth_ioctl;
661    ifp->if_start = greth_start;
662    ifp->if_output = ether_output;
663    ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
664    if (ifp->if_snd.ifq_maxlen == 0)
665        ifp->if_snd.ifq_maxlen = ifqmaxlen;
666
667    /*
668     * Attach the interface
669     */
670    if_attach (ifp);
671    ether_ifattach (ifp);
672
673#ifdef GRETH_DEBUG
674    printf ("GRETH : driver has been attached\n");
675#endif
676    return 1;
677};
678
Note: See TracBrowser for help on using the repository browser.