source: bootcd/isolinux/syslinux-6.03/gpxe/src/drivers/net/natsemi.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 16.1 KB
Line 
1/*
2   natsemi.c - gPXE driver for the NatSemi DP8381x series.
3 
4   Based on:
5
6   natsemi.c: An Etherboot driver for the NatSemi DP8381x series.
7
8   Copyright (C) 2001 Entity Cyber, Inc.
9   
10   This development of this Etherboot driver was funded by
11   
12      Sicom Systems: http://www.sicompos.com/
13   
14   Author: Marty Connor <mdc@etherboot.org>
15   Adapted from a Linux driver which was written by Donald Becker
16   
17   This software may be used and distributed according to the terms
18   of the GNU Public License (GPL), incorporated herein by reference.
19   
20   Original Copyright Notice:
21   
22   Written/copyright 1999-2001 by Donald Becker.
23   
24   This software may be used and distributed according to the terms of
25   the GNU General Public License (GPL), incorporated herein by reference.
26   Drivers based on or derived from this code fall under the GPL and must
27   retain the authorship, copyright and license notice.  This file is not
28   a complete program and may only be used when the entire operating
29   system is licensed under the GPL.  License for under other terms may be
30   available.  Contact the original author for details.
31   
32   The original author may be reached as becker@scyld.com, or at
33   Scyld Computing Corporation
34   410 Severn Ave., Suite 210
35   Annapolis MD 21403
36   
37   Support information and updates available at
38   http://www.scyld.com/network/netsemi.html
39   
40   References:
41   
42   http://www.scyld.com/expert/100mbps.html
43   http://www.scyld.com/expert/NWay.html
44   Datasheet is available from:
45   http://www.national.com/pf/DP/DP83815.html
46
47*/
48
49FILE_LICENCE ( GPL_ANY );
50
51/* Revision History */
52
53/*
54  02 Jul 2007  Udayan Kumar      1.2 ported the driver from etherboot to gPXE API.
55                                     Fully rewritten,adapting the old driver.
56                                     Added a circular buffer for transmit and receive.
57                                     transmit routine will not wait for transmission to finish.
58                                     poll routine deals with it.
59  13 Dec 2003  Tim Legge         1.1 Enabled Multicast Support
60  29 May 2001  Marty Connor      1.0 Initial Release. Tested with Netgear FA311 and FA312 boards
61*/
62
63#include <stdint.h>
64#include <stdlib.h>
65#include <stdio.h>
66#include <string.h>
67#include <gpxe/io.h>
68#include <errno.h>
69#include <byteswap.h>
70#include <unistd.h>
71#include <gpxe/pci.h>
72#include <gpxe/if_ether.h>
73#include <gpxe/ethernet.h>
74#include <gpxe/iobuf.h>
75#include <gpxe/netdevice.h>
76#include <gpxe/spi_bit.h>
77#include <gpxe/threewire.h>
78#include <gpxe/nvo.h>
79#include "natsemi.h"
80
81/*  Function Prototypes: */
82 
83static int natsemi_spi_read_bit ( struct bit_basher *, unsigned int );
84static void natsemi_spi_write_bit ( struct bit_basher *,unsigned int, unsigned long );
85static void natsemi_init_eeprom ( struct natsemi_private * );
86static int natsemi_probe (struct pci_device *pci, const struct pci_device_id *id);
87static void natsemi_reset (struct net_device *netdev);
88static int natsemi_open (struct net_device *netdev);
89static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf);
90static void natsemi_poll (struct net_device *netdev);
91static void natsemi_close (struct net_device *netdev);
92static void natsemi_irq (struct net_device *netdev, int enable);
93static void natsemi_remove (struct pci_device *pci);
94
95/** natsemi net device operations */
96static struct net_device_operations natsemi_operations = {
97        .open           = natsemi_open,
98        .close          = natsemi_close,
99        .transmit       = natsemi_transmit,
100        .poll           = natsemi_poll,
101        .irq            = natsemi_irq,
102};
103
104static int natsemi_spi_read_bit ( struct bit_basher *basher,
105                              unsigned int bit_id ) {
106        struct natsemi_private *np = container_of ( basher, struct natsemi_private,
107                                                 spibit.basher );
108        uint8_t mask = natsemi_ee_bits[bit_id];
109        uint8_t eereg;
110
111        eereg = inb ( np->ioaddr + EE_REG );
112        return ( eereg & mask );
113}
114
115static void natsemi_spi_write_bit ( struct bit_basher *basher,
116                                unsigned int bit_id, unsigned long data ) {
117        struct natsemi_private *np = container_of ( basher, struct natsemi_private,
118                                                 spibit.basher );
119        uint8_t mask = natsemi_ee_bits[bit_id];
120        uint8_t eereg;
121
122        eereg = inb ( np->ioaddr + EE_REG );
123        eereg &= ~mask;
124        eereg |= ( data & mask );
125        outb ( eereg, np->ioaddr + EE_REG );
126}
127
128static struct bit_basher_operations natsemi_basher_ops = {
129        .read = natsemi_spi_read_bit,
130        .write = natsemi_spi_write_bit,
131};
132
133/* It looks that this portion of EEPROM can be used for
134 * non-volatile stored options. Data sheet does not talk about this region.
135 * Currently it is not working. But with some efforts it can.
136 */
137static struct nvo_fragment natsemi_nvo_fragments[] = {
138        { 0x0c, 0x68 },
139        { 0, 0 }
140};
141
142/*
143 * Set up for EEPROM access
144 *
145 * @v NAT               NATSEMI NIC
146 */
147static void natsemi_init_eeprom ( struct natsemi_private *np ) {
148
149        /* Initialise three-wire bus
150         */
151        np->spibit.basher.op = &natsemi_basher_ops;
152        np->spibit.bus.mode = SPI_MODE_THREEWIRE;
153        np->spibit.endianness = SPI_BIT_LITTLE_ENDIAN;
154        init_spi_bit_basher ( &np->spibit );
155
156        /*natsemi DP 83815 only supports at93c46
157         */
158        init_at93c46 ( &np->eeprom, 16 );
159        np->eeprom.bus = &np->spibit.bus;
160        np->nvo.nvs = &np->eeprom.nvs;
161        np->nvo.fragments = natsemi_nvo_fragments;
162}
163
164/**
165 * Probe PCI device
166 *
167 * @v pci       PCI device
168 * @v id        PCI ID
169 * @ret rc      Return status code
170 */
171static int natsemi_probe (struct pci_device *pci,
172                       const struct pci_device_id *id __unused) {
173        struct net_device *netdev;
174        struct natsemi_private *np = NULL;
175        uint8_t ll_addr_encoded[MAX_LL_ADDR_LEN];
176        uint8_t last=0,last1=0;
177        uint8_t prev_bytes[2];
178        int i;
179        int rc;
180
181        /* Allocate net device
182         */
183        netdev = alloc_etherdev (sizeof (*np));
184        if (! netdev)
185                return -ENOMEM;
186
187        netdev_init (netdev, &natsemi_operations);
188        np = netdev->priv;
189        pci_set_drvdata (pci, netdev);
190        netdev->dev = &pci->dev;
191        memset (np, 0, sizeof (*np));
192        np->ioaddr = pci->ioaddr;
193
194        adjust_pci_device (pci);
195
196        natsemi_reset (netdev);
197        natsemi_init_eeprom ( np );
198        nvs_read ( &np->eeprom.nvs, EE_MAC-1, prev_bytes, 1 );
199        nvs_read ( &np->eeprom.nvs, EE_MAC, ll_addr_encoded, ETH_ALEN );
200
201        /* decoding the MAC address read from NVS
202         * and save it in netdev->ll_addr
203         */
204        last = prev_bytes[1] >> 7;
205        for ( i = 0 ; i < ETH_ALEN ; i++ ) {
206                last1 = ll_addr_encoded[i] >> 7;
207                netdev->hw_addr[i] = ll_addr_encoded[i] << 1 | last;
208                last = last1;
209        }
210
211        /* Mark as link up; we don't yet handle link state */
212        netdev_link_up ( netdev );
213
214        if ((rc = register_netdev (netdev)) != 0)
215                goto err_register_netdev;
216
217        return 0;
218
219err_register_netdev:
220
221        natsemi_reset (netdev);
222        netdev_put (netdev);
223        return rc;
224}
225
226/**
227 * Remove PCI device
228 *
229 * @v pci       PCI device
230 */
231static void natsemi_remove (struct pci_device *pci) {
232        struct net_device *netdev = pci_get_drvdata (pci);
233 
234        unregister_netdev (netdev);
235        natsemi_reset (netdev);
236        netdev_nullify ( netdev );
237        netdev_put (netdev);
238}
239
240/**
241 * Reset NIC
242 *
243 * @v           NATSEMI NIC
244 *
245 * Issues a hardware reset and waits for the reset to complete.
246 */
247static void natsemi_reset (struct net_device *netdev)
248{
249        struct natsemi_private *np = netdev->priv;
250        int i;
251        u32 cfg;
252        u32 wcsr;
253        u32 rfcr;
254        u16 pmatch[3];
255        u16 sopass[3];
256
257        natsemi_irq (netdev, 0);
258
259        /*
260         * Resetting the chip causes some registers to be lost.
261         * Natsemi suggests NOT reloading the EEPROM while live, so instead
262         * we save the state that would have been loaded from EEPROM
263         * on a normal power-up (see the spec EEPROM map).
264         */
265
266        /* CFG */
267        cfg = inl (np->ioaddr + ChipConfig) & CFG_RESET_SAVE;
268
269        /* WCSR */
270        wcsr = inl (np->ioaddr + WOLCmd) & WCSR_RESET_SAVE;
271
272        /* RFCR */
273        rfcr = inl (np->ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
274
275        /* PMATCH */
276        for (i = 0; i < 3; i++) {
277                outl(i*2, np->ioaddr + RxFilterAddr);
278                pmatch[i] = inw(np->ioaddr + RxFilterData);
279        }
280
281        /* SOPAS */
282        for (i = 0; i < 3; i++) {
283                outl(0xa+(i*2), np->ioaddr + RxFilterAddr);
284                sopass[i] = inw(np->ioaddr + RxFilterData);
285        }
286
287        /* now whack the chip */
288        outl(ChipReset, np->ioaddr + ChipCmd);
289        for (i=0; i<NATSEMI_HW_TIMEOUT; i++) {
290                if (! (inl (np->ioaddr + ChipCmd) & ChipReset))
291                       break;
292                udelay(5);
293        }
294        if (i == NATSEMI_HW_TIMEOUT) {
295                DBG ("natsemi_reset: reset did not complete in %d usec.\n", i*5);
296        }
297
298        /* restore CFG */
299        cfg |= inl(np->ioaddr + ChipConfig) & ~CFG_RESET_SAVE;
300        cfg &= ~(CfgExtPhy | CfgPhyDis);
301        outl (cfg, np->ioaddr + ChipConfig);
302
303        /* restore WCSR */
304        wcsr |= inl (np->ioaddr + WOLCmd) & ~WCSR_RESET_SAVE;
305        outl (wcsr, np->ioaddr + WOLCmd);
306
307        /* read RFCR */
308        rfcr |= inl (np->ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE;
309
310        /* restore PMATCH */
311        for (i = 0; i < 3; i++) {
312                outl (i*2, np->ioaddr + RxFilterAddr);
313                outw (pmatch[i], np->ioaddr + RxFilterData);
314        }
315        for (i = 0; i < 3; i++) {
316                outl (0xa+(i*2), np->ioaddr + RxFilterAddr);
317                outw (sopass[i], np->ioaddr + RxFilterData);
318        }
319        /* restore RFCR */
320        outl (rfcr, np->ioaddr + RxFilterAddr);
321}
322
323/**
324 * Open NIC
325 *
326 * @v netdev            Net device
327 * @ret rc              Return status code
328 */
329static int natsemi_open (struct net_device *netdev)
330{
331        struct natsemi_private *np = netdev->priv;
332        uint32_t tx_config, rx_config;
333        int i;
334       
335        /* Disable PME:
336         * The PME bit is initialized from the EEPROM contents.
337         * PCI cards probably have PME disabled, but motherboard
338         * implementations may have PME set to enable WakeOnLan.
339         * With PME set the chip will scan incoming packets but
340         * nothing will be written to memory.
341         */
342        outl (inl (np->ioaddr + ClkRun) & ~0x100, np->ioaddr + ClkRun);
343
344        /* Set MAC address in NIC
345         */
346        for (i = 0 ; i < ETH_ALEN ; i+=2) {
347                outl (i, np->ioaddr + RxFilterAddr);
348                outw (netdev->ll_addr[i] + (netdev->ll_addr[i + 1] << 8),
349                       np->ioaddr + RxFilterData);
350        }
351
352        /* Setup Tx Ring
353         */
354        np->tx_cur = 0;
355        np->tx_dirty = 0;
356        for (i = 0 ; i < TX_RING_SIZE ; i++) {
357                np->tx[i].link   = virt_to_bus ((i + 1 < TX_RING_SIZE) ? &np->tx[i + 1] : &np->tx[0]);
358                np->tx[i].cmdsts = 0;
359                np->tx[i].bufptr = 0;
360        }
361        outl (virt_to_bus (&np->tx[0]),np->ioaddr + TxRingPtr);
362
363        DBG ("Natsemi Tx descriptor loaded with: %#08x\n",
364             inl (np->ioaddr + TxRingPtr));
365
366        /* Setup RX ring
367         */
368        np->rx_cur = 0;
369        for (i = 0 ; i < NUM_RX_DESC ; i++) {
370                np->iobuf[i] = alloc_iob (RX_BUF_SIZE);
371                if (! np->iobuf[i])
372                        goto memory_alloc_err;
373                np->rx[i].link   = virt_to_bus ((i + 1 < NUM_RX_DESC)
374                                                ? &np->rx[i + 1] : &np->rx[0]);
375                np->rx[i].cmdsts = RX_BUF_SIZE;
376                np->rx[i].bufptr = virt_to_bus (np->iobuf[i]->data);
377                DBG (" Address of iobuf [%d] = %p and iobuf->data = %p \n", i,
378                      &np->iobuf[i],  &np->iobuf[i]->data);
379        }
380        outl (virt_to_bus (&np->rx[0]), np->ioaddr + RxRingPtr);
381
382        DBG ("Natsemi Rx descriptor loaded with: %#08x\n",
383              inl (np->ioaddr + RxRingPtr));           
384
385        /* Setup RX Filter
386         */
387        outl (RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys,
388              np->ioaddr + RxFilterAddr);
389
390        /* Initialize other registers.
391         * Configure the PCI bus bursts and FIFO thresholds.
392         * Configure for standard, in-spec Ethernet.
393         */
394        if (inl (np->ioaddr + ChipConfig) & 0x20000000) {       /* Full duplex */
395                DBG ("Full duplex\n");
396                tx_config = 0xD0801002 |  0xC0000000;
397                rx_config = 0x10000020 |  0x10000000;
398        } else {
399                DBG ("Half duplex\n");
400                tx_config = 0x10801002 & ~0xC0000000;
401                rx_config = 0x00000020 & ~0x10000000;
402        }
403        outl (tx_config, np->ioaddr + TxConfig);
404        outl (rx_config, np->ioaddr + RxConfig);
405
406        DBG ("Tx config register = %#08x Rx config register = %#08x\n",
407               inl (np->ioaddr + TxConfig),
408               inl (np->ioaddr + RxConfig));
409
410        /*Set the Interrupt Mask register
411         */
412        outl((RxOk|RxErr|TxOk|TxErr),np->ioaddr + IntrMask);
413        /*start the receiver
414         */
415        outl (RxOn, np->ioaddr + ChipCmd);
416       
417        return 0;
418                       
419memory_alloc_err:
420
421        /* Frees any allocated buffers when memory
422         * for all buffers requested is not available
423         */
424        i = 0;
425        while (np->rx[i].cmdsts == RX_BUF_SIZE) {
426                free_iob (np->iobuf[i]);
427                i++;
428        }
429        return -ENOMEM;
430}
431
432/**
433 * Close NIC
434 *
435 * @v netdev            Net device
436 */
437static void natsemi_close (struct net_device *netdev)
438{
439        struct natsemi_private *np = netdev->priv;
440        int i;
441
442        natsemi_reset (netdev);
443
444        for (i = 0; i < NUM_RX_DESC ; i++) {
445                free_iob (np->iobuf[i]);
446        }
447}
448
449/**
450 * Transmit packet
451 *
452 * @v netdev    Network device
453 * @v iobuf     I/O buffer
454 * @ret rc      Return status code
455 */
456static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf)
457{
458        struct natsemi_private *np = netdev->priv;
459
460        if (np->tx[np->tx_cur].cmdsts != 0) {
461                DBG ("TX overflow\n");
462                return -ENOBUFS;
463        }
464
465        /* Used by netdev_tx_complete ()
466         */
467        np->tx_iobuf[np->tx_cur] = iobuf;
468
469        /* Pad and align packet has not been used because its not required
470         * by the hardware.
471         *      iob_pad (iobuf, ETH_ZLEN);
472         * can be used to achieve it, if required
473         */
474
475        /* Add the packet to TX ring
476         */
477        np->tx[np->tx_cur].bufptr = virt_to_bus (iobuf->data);
478        np->tx[np->tx_cur].cmdsts = iob_len (iobuf) | OWN;
479
480        DBG ("TX id %d at %#08lx + %#08zx\n", np->tx_cur,
481             virt_to_bus (&iobuf->data), iob_len (iobuf));
482
483        /* increment the circular buffer pointer to the next buffer location
484         */
485        np->tx_cur = (np->tx_cur + 1) % TX_RING_SIZE;
486
487        /*start the transmitter
488         */
489        outl (TxOn, np->ioaddr + ChipCmd);
490
491        return 0;
492}
493
494/**
495 * Poll for received packets
496 *
497 * @v netdev    Network device
498 */
499static void natsemi_poll (struct net_device *netdev)
500{
501        struct natsemi_private *np = netdev->priv;
502        unsigned int tx_status;
503        unsigned int rx_status;
504        unsigned int intr_status;
505        unsigned int rx_len;
506        struct io_buffer *rx_iob;
507        int i;
508       
509        /* read the interrupt register
510         */
511        intr_status = inl (np->ioaddr + IntrStatus);
512
513        if (!intr_status)
514                goto end;
515
516        DBG ("natsemi_poll: intr_status = %#08x\n", intr_status);
517
518        /* Check status of transmitted packets
519         */
520        i = np->tx_dirty;
521        while (i != np->tx_cur) {
522                tx_status = np->tx[np->tx_dirty].cmdsts;
523
524                DBG ("tx_dirty = %d tx_cur=%d tx_status=%#08x\n",
525                     np->tx_dirty, np->tx_cur, tx_status);
526               
527                if (tx_status & OWN)
528                        break;
529
530                if (! (tx_status & DescPktOK)) {
531                        netdev_tx_complete_err (netdev,np->tx_iobuf[np->tx_dirty],-EINVAL);
532                        DBG ("Error transmitting packet, tx_status: %#08x\n",
533                             tx_status);
534                } else {
535                        netdev_tx_complete (netdev, np->tx_iobuf[np->tx_dirty]);
536                        DBG ("Success transmitting packet\n");
537                }
538
539                np->tx[np->tx_dirty].cmdsts = 0;
540                np->tx_dirty = (np->tx_dirty + 1) % TX_RING_SIZE;
541                i = (i + 1) % TX_RING_SIZE;
542        }
543       
544        /* Process received packets
545         */
546        rx_status = (unsigned int) np->rx[np->rx_cur].cmdsts;
547        while ((rx_status & OWN)) {
548                rx_len = (rx_status & DSIZE) - CRC_SIZE;
549
550                DBG ("Received packet, rx_curr = %d, rx_status = %#08x, rx_len = %d\n",
551                     np->rx_cur, rx_status, rx_len);
552               
553                if ((rx_status & (DescMore | DescPktOK | RxTooLong)) != DescPktOK) {
554                        netdev_rx_err (netdev, NULL, -EINVAL);
555
556                        DBG ("natsemi_poll: Corrupted packet received!"
557                             " Status = %#08x\n",
558                              np->rx[np->rx_cur].cmdsts);
559
560                } else  {
561
562
563                        /* If unable allocate space for this packet,
564                         *  try again next poll
565                         */
566                        rx_iob = alloc_iob (rx_len);
567                        if (! rx_iob)
568                                goto end;
569                        memcpy (iob_put (rx_iob, rx_len),
570                                np->iobuf[np->rx_cur]->data, rx_len);
571                        /* Add this packet to the receive queue.
572                         */
573                        netdev_rx (netdev, rx_iob);
574                }
575                np->rx[np->rx_cur].cmdsts = RX_BUF_SIZE;
576                np->rx_cur = (np->rx_cur + 1) % NUM_RX_DESC;
577                rx_status = np->rx[np->rx_cur].cmdsts;
578        }
579end:
580        /* re-enable the potentially idle receive state machine
581         */
582        outl (RxOn, np->ioaddr + ChipCmd);     
583}                               
584
585/**
586 * Enable/disable interrupts
587 *
588 * @v netdev    Network device
589 * @v enable    Non-zero for enable, zero for disable
590 */
591static void natsemi_irq (struct net_device *netdev, int enable)
592{
593        struct natsemi_private *np = netdev->priv;
594
595        outl ((enable ? (RxOk | RxErr | TxOk|TxErr) : 0),
596              np->ioaddr + IntrMask);
597        outl ((enable ? 1 : 0), np->ioaddr + IntrEnable);
598}
599
600static struct pci_device_id natsemi_nics[] = {
601        PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815", 0),
602};
603
604struct pci_driver natsemi_driver __pci_driver = {
605        .ids = natsemi_nics,
606        .id_count = (sizeof (natsemi_nics) / sizeof (natsemi_nics[0])),
607        .probe = natsemi_probe,
608        .remove = natsemi_remove,
609};
Note: See TracBrowser for help on using the repository browser.