source: rtems/c/src/lib/libbsp/i386/pc386/wd8003/wd8003.c @ 17fd0ff

Last change on this file since 17fd0ff was 17fd0ff, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 12, 2018 at 6:56:42 AM

bsps: Move wd80x3.h to libchip/wd80x3.h

This header is used also by the motorola_powerpc BSP.

Update #3254.

  • Property mode set to 100644
File size: 13.8 KB
Line 
1/*
2 * RTEMS driver for WD800x
3 *
4 *  Based on the 68360 Network Driver by:
5 *    W. Eric Norum
6 *    Saskatchewan Accelerator Laboratory
7 *    University of Saskatchewan
8 *    Saskatoon, Saskatchewan, CANADA
9 *    eric@skatter.usask.ca
10 */
11
12#include <bsp.h>
13#include <libchip/wd80x3.h>
14
15#include <stdio.h>
16#include <stdarg.h>
17#include <string.h>     /* memcpy, memset */
18#include <errno.h>
19#include <rtems/error.h>
20#include <rtems/rtems_bsdnet.h>
21#include <assert.h>
22
23#include <sys/param.h>
24#include <sys/mbuf.h>
25#include <sys/socket.h>
26#include <sys/sockio.h>
27
28#include <net/if.h>
29
30#include <netinet/in.h>
31#include <netinet/if_ether.h>
32
33#include <bsp/irq.h>
34
35#define ET_MINLEN 60            /* minimum message length */
36
37/*
38 * Number of WDs supported by this driver
39 */
40#define NWDDRIVER       1
41
42/*
43 * Default number of buffer descriptors set aside for this driver.
44 * The number of transmit buffer descriptors has to be quite large
45 * since a single frame often uses four or more buffer descriptors.
46 */
47#define RX_BUF_COUNT     15
48#define TX_BUF_COUNT     4
49#define TX_BD_PER_BUF    4
50
51/*
52 * RTEMS event used by interrupt handler to signal driver tasks.
53 * This must not be any of the events used by the network task synchronization.
54 */
55#define INTERRUPT_EVENT RTEMS_EVENT_1
56
57/*
58 * RTEMS event used to start transmit daemon.
59 * This must not be the same as INTERRUPT_EVENT.
60 */
61#define START_TRANSMIT_EVENT    RTEMS_EVENT_2
62
63/*
64 * Receive buffer size -- Allow for a full ethernet packet including CRC
65 */
66#define RBUF_SIZE       1520
67
68#if (MCLBYTES < RBUF_SIZE)
69# error "Driver must have MCLBYTES > RBUF_SIZE"
70#endif
71
72/*
73 * Per-device data
74 */
75struct wd_softc {
76  struct arpcom                 arpcom;
77  struct mbuf                   **rxMbuf;
78  struct mbuf                   **txMbuf;
79  int                           acceptBroadcast;
80  int                           rxBdCount;
81  int                           txBdCount;
82  int                           txBdHead;
83  int                           txBdTail;
84  int                           txBdActiveCount;
85  rtems_id                      rxDaemonTid;
86  rtems_id                      txDaemonTid;
87  rtems_vector_number name;
88
89  unsigned int                  port;
90  unsigned char                 *base;
91  unsigned long                 bpar;
92
93  /*
94   * Statistics
95   */
96  unsigned long rxInterrupts;
97  unsigned long rxNotFirst;
98  unsigned long rxNotLast;
99  unsigned long rxGiant;
100  unsigned long rxNonOctet;
101  unsigned long rxRunt;
102  unsigned long rxBadCRC;
103  unsigned long rxOverrun;
104  unsigned long rxCollision;
105
106  unsigned long txInterrupts;
107  unsigned long txDeferred;
108  unsigned long txHeartbeat;
109  unsigned long txLateCollision;
110  unsigned long txRetryLimit;
111  unsigned long txUnderrun;
112  unsigned long txLostCarrier;
113  unsigned long txRawWait;
114};
115
116#define RO 0x10
117
118#define SHATOT (8*1024)         /* size of shared memory */
119#define SHAPAGE 256             /* shared memory information */
120#define MAXSIZ  1536            /*(MAXBUF - MESSH_SZ)*/
121#define OUTPAGE ((SHATOT-(MAXSIZ+SHAPAGE-1))/SHAPAGE)
122
123static volatile unsigned long overrun;
124static volatile unsigned long resend;
125static struct wd_softc wd_softc[NWDDRIVER];
126
127/*
128 * WD interrupt handler
129 */
130static void
131wd8003Enet_interrupt_handler (void * unused)
132{
133  unsigned int tport;
134  unsigned char status, status2;
135
136  tport = wd_softc[0].port ;
137
138  /*
139   * Read status
140   */
141  inport_byte(tport+ISR, status);
142  outport_byte(tport+IMR, 0x00);
143
144  /*
145   * Ring overwrite
146   */
147
148  if (status & MSK_OVW){
149    outport_byte(tport+CMDR, MSK_STP + MSK_RD2);        /* stop 8390 */
150    Wait_X_ms(2);
151    outport_byte(tport+RBCR0, 0);                       /* clear byte count */
152    outport_byte(tport+RBCR1, 0);
153    inport_byte(tport+ISR, status2);
154    status |= (status2 & (MSK_PTX+MSK_TXE)) ;   /* TX status */
155    outport_byte(tport+TCR, MSK_LOOP);          /* loopback mode */
156    outport_byte(tport+CMDR, MSK_STA + MSK_RD2);        /* start */
157    overrun = 1 ;
158    if ((status & (MSK_PTX+MSK_TXE)) == 0)
159        resend = 1;
160  }
161
162  /*
163   * Frame received?
164   */
165  if (status & (MSK_PRX+MSK_RXE)) {
166    outport_byte(tport+ISR, status & (MSK_PRX+MSK_RXE));
167    wd_softc[0].rxInterrupts++;
168    rtems_bsdnet_event_send (wd_softc[0].rxDaemonTid, INTERRUPT_EVENT);
169  }
170
171}
172
173/*
174 * Initialize the ethernet hardware
175 */
176static void
177wd8003Enet_initialize_hardware (struct wd_softc *sc)
178{
179  int  i1, ultra;
180  char cc1, cc2;
181  unsigned char  temp;
182  rtems_status_code status;
183  unsigned int tport;
184  unsigned char *hwaddr;
185
186  tport = sc->port;
187
188  /* address from board ROM */
189  inport_byte(tport+0x04, temp);
190  outport_byte(tport+0x04, temp & 0x7f);
191
192  hwaddr = sc->arpcom.ac_enaddr;
193  for (i1=cc2=0; i1<8; i1++) {
194    inport_byte(tport + ADDROM + i1, cc1);
195    cc2 += cc1;
196    if (i1 < 6)
197      hwaddr[i1] = cc1;
198  }
199
200  inport_byte(tport+0x04, temp);
201  outport_byte(tport+0x04, temp | 0x80);        /* alternate registers */
202  outport_byte(tport+W83CREG, MSK_RESET);       /* reset board, set buffer */
203  outport_byte(tport+W83CREG, 0);
204  outport_byte(tport+W83CREG, MSK_ENASH + (int)((sc->bpar>>13)&0x3f));
205
206  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
207  cc1 = MSK_BMS + MSK_FT10; /* configure 8 or 16 bits */
208
209  inport_byte(tport+0x07, temp) ;
210
211  ultra = ((temp & 0xf0) == 0x20 || (temp & 0xf0) == 0x40);
212  if (ultra)
213    cc1 = MSK_WTS + MSK_BMS + MSK_FT10;
214  outport_byte(tport+DCR, cc1);
215  outport_byte(tport+RBCR0, 0);
216  outport_byte(tport+RBCR1, 0);
217  outport_byte(tport+RCR, MSK_MON);             /* disable the rxer */
218  outport_byte(tport+TCR, 0);                   /* normal operation */
219  outport_byte(tport+PSTOP, OUTPAGE);           /* init PSTOP */
220  outport_byte(tport+PSTART, 0);                /* init PSTART */
221  outport_byte(tport+BNRY, -1);                 /* init BNRY */
222  outport_byte(tport+ISR, -1);                  /* clear IR's */
223  outport_byte(tport+IMR, 0x15);                /* enable interrupt */
224
225  outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
226
227  for (i1=0; i1<6; i1++)                        /* initial physical addr */
228    outport_byte(tport+PAR+i1, hwaddr[i1]);
229
230  for (i1=0; i1<MARsize; i1++)                  /* clear multicast */
231    outport_byte(tport+MAR+i1, 0);
232  outport_byte(tport+CURR, 0);                  /* init current packet */
233
234  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
235  outport_byte(tport+CMDR, MSK_STA + MSK_RD2);  /* put 8390 on line */
236  outport_byte(tport+RCR, MSK_AB);              /* MSK_AB accept broadcast */
237
238  if (ultra) {
239    inport_byte(tport+0x0c, temp);
240    outport_byte(tport+0x0c, temp | 0x80);
241    outport_byte(tport+0x05, 0x80);
242    outport_byte(tport+0x06, 0x01);
243  }
244
245  /*
246   * Set up interrupts
247   */
248
249  status = rtems_interrupt_handler_install(
250    sc->name,
251    "wd8003",
252    RTEMS_INTERRUPT_UNIQUE,
253    wd8003Enet_interrupt_handler,
254    NULL
255  );
256  assert(status == RTEMS_SUCCESSFUL);
257}
258
259static void
260wd_rxDaemon (void *arg)
261{
262  unsigned int tport;
263  struct ether_header *eh;
264  struct wd_softc *dp = (struct wd_softc *)&wd_softc[0];
265  struct ifnet *ifp = &dp->arpcom.ac_if;
266  struct mbuf *m;
267  unsigned int i2;
268  unsigned int len;
269  volatile unsigned char start, next, current;
270  unsigned char *shp, *temp;
271  unsigned short *real_short_ptr;
272  rtems_event_set events;
273
274  tport = wd_softc[0].port ;
275
276  for (;;){
277
278    rtems_bsdnet_event_receive (INTERRUPT_EVENT,
279                                RTEMS_WAIT|RTEMS_EVENT_ANY,
280                                RTEMS_NO_TIMEOUT,
281                                &events);
282
283    for (;;){
284      inport_byte(tport+BNRY, start);
285
286      outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
287      inport_byte(tport+CURR, current);
288      outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
289
290      start += 1;
291      if (start >= OUTPAGE){
292        start = 0;
293      }
294
295      if (current == start)
296        break;
297
298      /* real_short_ptr avoids cast on lvalue which gcc no longer allows */
299      shp = dp->base + 1 + (SHAPAGE * start);
300      next = *shp++;
301      real_short_ptr = (unsigned short *)shp;
302      len = *(real_short_ptr)++ - 4;
303
304      if (next >= OUTPAGE){
305        next = 0;
306      }
307
308      MGETHDR (m, M_WAIT, MT_DATA);
309      MCLGET (m, M_WAIT);
310      m->m_pkthdr.rcvif = ifp;
311
312      temp = (unsigned char *) m->m_data;
313      m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
314
315      if ((i2 = (OUTPAGE - start) * SHAPAGE - 4) < len){
316        memcpy(temp, shp, i2);
317        len -= i2;
318        temp += i2;
319        shp = dp->base;
320      }
321      memcpy(temp, shp, len);
322
323      eh = mtod (m, struct ether_header *);
324      m->m_data += sizeof(struct ether_header);
325      ether_input (ifp, eh, m);
326
327      outport_byte(tport+BNRY, next-1);
328    }
329
330  /*
331   * Ring overwrite
332   */
333    if (overrun){
334      outport_byte(tport+ISR, MSK_OVW);         /* reset IR */
335      outport_byte(tport+TCR, 0);               /* out of loopback */
336      if (resend  == 1)
337        outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);    /* resend */
338      resend = 0;
339      overrun = 0;
340    }
341
342    outport_byte(tport+IMR, 0x15);  /* re-enable IT rx */
343  }
344}
345
346static void
347sendpacket (struct ifnet *ifp, struct mbuf *m)
348{
349        struct wd_softc *dp = ifp->if_softc;
350        struct mbuf *n;
351        unsigned int len, tport;
352        uint8_t *shp, txReady;
353
354        tport = dp->port;
355
356  /*
357   * Waiting for Transmitter ready
358   */
359  inport_byte(tport+CMDR, txReady);
360  while(txReady & MSK_TXP)
361    inport_byte(tport+CMDR, txReady);
362
363  len = 0;
364  shp = dp->base + (SHAPAGE * OUTPAGE);
365
366  n = m;
367
368  for (;;){
369    len += m->m_len;
370    memcpy(shp, (char *)m->m_data, m->m_len);
371    shp += m->m_len ;
372    if ((m = m->m_next) == NULL)
373      break;
374  }
375
376  m_freem(n);
377
378  if (len < ET_MINLEN) len = ET_MINLEN;
379  outport_byte(tport+TBCR0, len);
380  outport_byte(tport+TBCR1, (len >> 8) );
381  outport_byte(tport+TPSR, OUTPAGE);
382  outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);
383}
384
385/*
386 * Driver transmit daemon
387 */
388static void
389wd_txDaemon (void *arg)
390{
391        struct wd_softc *sc = (struct wd_softc *)arg;
392        struct ifnet *ifp = &sc->arpcom.ac_if;
393        struct mbuf *m;
394        rtems_event_set events;
395
396        for (;;) {
397                /*
398                 * Wait for packet
399                 */
400                rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events);
401
402                /*
403                 * Send packets till queue is empty
404                 */
405                for (;;) {
406                        /*
407                         * Get the next mbuf chain to transmit.
408                         */
409                        IF_DEQUEUE(&ifp->if_snd, m);
410                        if (!m)
411                                break;
412                        sendpacket (ifp, m);
413                }
414                ifp->if_flags &= ~IFF_OACTIVE;
415        }
416}
417
418/*
419 * Send packet (caller provides header).
420 */
421static void
422wd_start (struct ifnet *ifp)
423{
424        struct wd_softc *sc = ifp->if_softc;
425
426        rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
427        ifp->if_flags |= IFF_OACTIVE;
428}
429
430/*
431 * Initialize and start the device
432 */
433static void
434wd_init (void *arg)
435{
436  struct wd_softc *sc = arg;
437  struct ifnet *ifp = &sc->arpcom.ac_if;
438
439  if (sc->txDaemonTid == 0) {
440
441    /*
442     * Set up WD hardware
443     */
444    wd8003Enet_initialize_hardware (sc);
445
446    /*
447     * Start driver tasks
448     */
449    sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, wd_txDaemon, sc);
450    sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, wd_rxDaemon, sc);
451  }
452
453  /*
454   * Tell the world that we're running.
455   */
456  ifp->if_flags |= IFF_RUNNING;
457
458}
459
460/*
461 * Stop the device
462 */
463static void
464wd_stop (struct wd_softc *sc)
465{
466  unsigned int tport;
467  unsigned char temp;
468  struct ifnet *ifp = &sc->arpcom.ac_if;
469
470  ifp->if_flags &= ~IFF_RUNNING;
471
472  /*
473   * Stop the transmitter
474   */
475  tport=wd_softc[0].port ;
476  inport_byte(tport+0x04,temp);
477  outport_byte(tport+0x04, temp & 0x7f);
478  outport_byte(tport + CMDR, MSK_STP + MSK_RD2);
479
480}
481
482/*
483 * Show interface statistics
484 */
485static void
486wd_stats (struct wd_softc *sc)
487{
488        printf ("      Rx Interrupts:%-8lu", sc->rxInterrupts);
489        printf ("       Not First:%-8lu", sc->rxNotFirst);
490        printf ("        Not Last:%-8lu\n", sc->rxNotLast);
491        printf ("              Giant:%-8lu", sc->rxGiant);
492        printf ("            Runt:%-8lu", sc->rxRunt);
493        printf ("       Non-octet:%-8lu\n", sc->rxNonOctet);
494        printf ("            Bad CRC:%-8lu", sc->rxBadCRC);
495        printf ("         Overrun:%-8lu", sc->rxOverrun);
496        printf ("       Collision:%-8lu\n", sc->rxCollision);
497
498        printf ("      Tx Interrupts:%-8lu", sc->txInterrupts);
499        printf ("        Deferred:%-8lu", sc->txDeferred);
500        printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
501        printf ("         No Carrier:%-8lu", sc->txLostCarrier);
502        printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
503        printf ("  Late Collision:%-8lu\n", sc->txLateCollision);
504        printf ("           Underrun:%-8lu", sc->txUnderrun);
505        printf (" Raw output wait:%-8lu\n", sc->txRawWait);
506}
507
508/*
509 * Driver ioctl handler
510 */
511static int
512wd_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
513{
514        struct wd_softc *sc = ifp->if_softc;
515        int error = 0;
516
517        switch (command) {
518        case SIOCGIFADDR:
519        case SIOCSIFADDR:
520                ether_ioctl (ifp, command, data);
521                break;
522
523        case SIOCSIFFLAGS:
524                switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
525                case IFF_RUNNING:
526                        wd_stop (sc);
527                        break;
528
529                case IFF_UP:
530                        wd_init (sc);
531                        break;
532
533                case IFF_UP | IFF_RUNNING:
534                        wd_stop (sc);
535                        wd_init (sc);
536                        break;
537
538                default:
539                        break;
540                }
541                break;
542
543        case SIO_RTEMS_SHOW_STATS:
544                wd_stats (sc);
545                break;
546
547        /*
548         * FIXME: All sorts of multicast commands need to be added here!
549         */
550        default:
551                error = EINVAL;
552                break;
553        }
554        return error;
555}
556
557/*
558 * Attach an WD driver to the system
559 */
560int
561rtems_wd_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
562{
563        struct wd_softc *sc;
564        struct ifnet *ifp;
565        int mtu;
566        int i;
567
568        /*
569         * Find a free driver
570         */
571        for (i = 0 ; i < NWDDRIVER ; i++) {
572                sc = &wd_softc[i];
573                ifp = &sc->arpcom.ac_if;
574                if (ifp->if_softc == NULL)
575                        break;
576        }
577        if (i >= NWDDRIVER) {
578                printf ("Too many WD drivers.\n");
579                return 0;
580        }
581
582        /*
583         * Process options
584         */
585        if (config->hardware_address) {
586          memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
587                  ETHER_ADDR_LEN);
588        }
589        else {
590          memset (sc->arpcom.ac_enaddr, 0x08,ETHER_ADDR_LEN);
591        }
592        if (config->mtu)
593                mtu = config->mtu;
594        else
595                mtu = ETHERMTU;
596
597        if (config->irno)
598                sc->name = config->irno;
599        else
600                sc->name = 5;
601
602        if (config->port)
603                sc->port = config->port;
604        else
605                sc->port = 0x240;
606
607        if (config->bpar) {
608                sc->bpar = config->bpar;
609                sc->base = (unsigned char*) config->bpar;
610        }
611        else {
612                sc->bpar = 0xD0000;
613                sc->base = (unsigned char*) 0xD0000;
614        }
615
616        sc->acceptBroadcast = !config->ignore_broadcast;
617
618        /*
619         * Set up network interface values
620         */
621        ifp->if_softc = sc;
622        ifp->if_unit = i + 1;
623        ifp->if_name = "wd";
624        ifp->if_mtu = mtu;
625        ifp->if_init = wd_init;
626        ifp->if_ioctl = wd_ioctl;
627        ifp->if_start = wd_start;
628        ifp->if_output = ether_output;
629        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
630        if (ifp->if_snd.ifq_maxlen == 0)
631                ifp->if_snd.ifq_maxlen = ifqmaxlen;
632
633        /*
634         * init some variables
635         */
636        overrun = 0;
637        resend = 0;
638
639        /*
640         * Attach the interface
641         */
642        if_attach (ifp);
643        ether_ifattach (ifp);
644        return 1;
645};
Note: See TracBrowser for help on using the repository browser.