|  | /* | 
|  | * eepro100.c -- This file implements the eepro100 driver for etherboot. | 
|  | * | 
|  | * | 
|  | * Copyright (C) AW Computer Systems. | 
|  | * written by R.E.Wolff -- R.E.Wolff@BitWizard.nl | 
|  | * | 
|  | * | 
|  | * AW Computer Systems is contributing to the free software community | 
|  | * by paying for this driver and then putting the result under GPL. | 
|  | * | 
|  | * If you need a Linux device driver, please contact BitWizard for a | 
|  | * quote. | 
|  | * | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public License as | 
|  | * published by the Free Software Foundation; either version 2, or (at | 
|  | * your option) any later version. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, but | 
|  | * WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License | 
|  | * along with this program; if not, write to the Free Software | 
|  | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | * | 
|  | * | 
|  | *              date       version  by   what | 
|  | *  Written:    May 29 1997  V0.10  REW  Initial revision. | 
|  | * changes:     May 31 1997  V0.90  REW  Works! | 
|  | *              Jun 1  1997  V0.91  REW  Cleanup | 
|  | *              Jun 2  1997  V0.92  REW  Add some code documentation | 
|  | *              Jul 25 1997  V1.00  REW  Tested by AW to work in a PROM | 
|  | *                                       Cleanup for publication | 
|  | * | 
|  | * This is the etherboot-3.1 compatible intel etherexpress Pro/100B | 
|  | * driver. | 
|  | * | 
|  | * It was written from scratch, with Donald Beckers eepro100.c kernel | 
|  | * driver as a guideline. Mostly the 82557 related definitions and the | 
|  | * lower level routines have been cut-and-pasted into this source. | 
|  | * | 
|  | * The driver was finished before Intel got the NDA out of the closet. | 
|  | * I still don't have the docs. | 
|  | * */ | 
|  |  | 
|  |  | 
|  |  | 
|  | /* Philosophy of this driver. | 
|  | * | 
|  | * Probing: | 
|  | * | 
|  | * Using a subset of "bios32" and "pci" functions of the linux kernel, | 
|  | * the pci 82557 chip is detected. | 
|  | * | 
|  | * | 
|  | * Initialization: | 
|  | * | 
|  | * | 
|  | * The chip is then initialized to "know" its ethernet address, and to | 
|  | * start recieving packets. The Linux driver has a whole transmit and | 
|  | * recieve ring of buffers. This is neat if you need high performance: | 
|  | * you can write the buffers asynchronously to the chip reading the | 
|  | * buffers and transmitting them over the network.  Performance is NOT | 
|  | * an issue here. We can boot a 400k kernel in about two | 
|  | * seconds. (Theory: 0.4 seconds). Booting a system is going to take | 
|  | * about half a minute anyway, so getting 10 times closer to the | 
|  | * theoretical limit is going to make a difference of a few percent. | 
|  | * | 
|  | * | 
|  | * Transmitting and recieving. | 
|  | * | 
|  | * We have only one transmit descriptor. It has two buffer descriptors: | 
|  | * one for the header, and the other for the data. | 
|  | * We have only one receive buffer. The chip is told to recieve packets, | 
|  | * and suspend itself once it got one. The recieve (poll) routine simply | 
|  | * looks at the recieve buffer to see if there is already a packet there. | 
|  | * if there is, the buffer is copied, and the reciever is restarted. | 
|  | * | 
|  | * Caveats: | 
|  | * | 
|  | * The etherboot framework moves the code to the 32k segment from | 
|  | * 0x98000 to 0xa0000. There is just a little room between the end of | 
|  | * this driver and the 0xa0000 address. If you compile in too many | 
|  | * features, this will overflow. | 
|  | * The number under "hex" in the output of size that scrolls by while | 
|  | * compiling should be less than 8000. Maybe even the stack is up there, | 
|  | * so that you need even more headroom. | 
|  | * | 
|  | * If you run into trouble, the method used to give "pci.c" dynamic | 
|  | * allocation should be used to allocate the larger variables (like | 
|  | * the packet buffers) | 
|  | * */ | 
|  |  | 
|  | #ifdef INCLUDE_EEPRO100 | 
|  |  | 
|  |  | 
|  | /* The etherboot authors seem to dislike the argument ordering in | 
|  | * outb macros that Linux uses. I disklike the confusion that this | 
|  | * has caused even more.... This file uses the Linux argument ordering.  */ | 
|  | #define LINUX_OUT_MACROS | 
|  |  | 
|  | #include "netboot.h" | 
|  | #include "nic.h" | 
|  | #include <linux/bios32.h> | 
|  | #include <linux/pci.h> | 
|  |  | 
|  | static int ioaddr; | 
|  |  | 
|  | typedef unsigned char  u8; | 
|  | typedef   signed char  s8; | 
|  | typedef unsigned short u16; | 
|  | typedef   signed short s16; | 
|  | typedef unsigned int   u32; | 
|  | typedef   signed int   s32; | 
|  |  | 
|  |  | 
|  | enum speedo_offsets { | 
|  | SCBStatus = 0, SCBCmd = 2,      /* Rx/Command Unit command and status. */ | 
|  | SCBPointer = 4,                 /* General purpose pointer. */ | 
|  | SCBPort = 8,                    /* Misc. commands and operands.  */ | 
|  | SCBflash = 12, SCBeeprom = 14,  /* EEPROM and flash memory control. */ | 
|  | SCBCtrlMDI = 16,                /* MDI interface control. */ | 
|  | SCBEarlyRx = 20,                /* Early receive byte count. */ | 
|  | }; | 
|  |  | 
|  |  | 
|  | static int read_eeprom(int ioaddr, int location); | 
|  | static void udelay (int val); | 
|  | void hd (void *where, int n); | 
|  |  | 
|  |  | 
|  | /***********************************************************************/ | 
|  | /*                       I82557 related defines                        */ | 
|  | /***********************************************************************/ | 
|  |  | 
|  | /* Serial EEPROM section. | 
|  | A "bit" grungy, but we work our way through bit-by-bit :->. */ | 
|  | /*  EEPROM_Ctrl bits. */ | 
|  | #define EE_SHIFT_CLK    0x01    /* EEPROM shift clock. */ | 
|  | #define EE_CS           0x02    /* EEPROM chip select. */ | 
|  | #define EE_DATA_WRITE   0x04    /* EEPROM chip data in. */ | 
|  | #define EE_WRITE_0      0x01 | 
|  | #define EE_WRITE_1      0x05 | 
|  | #define EE_DATA_READ    0x08    /* EEPROM chip data out. */ | 
|  | #define EE_ENB          (0x4800 | EE_CS) | 
|  |  | 
|  |  | 
|  | /* Delay between EEPROM clock transitions. | 
|  | This is a "nasty" timing loop, but PC compatible machines are defined | 
|  | to delay an ISA compatible period for the SLOW_DOWN_IO macro.  */ | 
|  | #define eeprom_delay(nanosec)   do { int _i = 3; while (--_i > 0) \ | 
|  | { __SLOW_DOWN_IO; }} while (0) | 
|  |  | 
|  | /* The EEPROM commands include the alway-set leading bit. */ | 
|  | #define EE_WRITE_CMD    (5 << 6) | 
|  | #define EE_READ_CMD     (6 << 6) | 
|  | #define EE_ERASE_CMD    (7 << 6) | 
|  |  | 
|  | /* The SCB accepts the following controls for the Tx and Rx units: */ | 
|  | #define  CU_START       0x0010 | 
|  | #define  CU_RESUME      0x0020 | 
|  | #define  CU_STATSADDR   0x0040 | 
|  | #define  CU_SHOWSTATS   0x0050  /* Dump statistics counters. */ | 
|  | #define  CU_CMD_BASE    0x0060  /* Base address to add to add CU commands. */ | 
|  | #define  CU_DUMPSTATS   0x0070  /* Dump then reset stats counters. */ | 
|  |  | 
|  | #define  RX_START       0x0001 | 
|  | #define  RX_RESUME      0x0002 | 
|  | #define  RX_ABORT       0x0004 | 
|  | #define  RX_ADDR_LOAD   0x0006 | 
|  | #define  RX_RESUMENR    0x0007 | 
|  | #define INT_MASK        0x0100 | 
|  | #define DRVR_INT        0x0200          /* Driver generated interrupt. */ | 
|  |  | 
|  | enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240, | 
|  | S80C24, PhyUndefined, DP83840A=10, }; | 
|  |  | 
|  |  | 
|  |  | 
|  | /* Commands that can be put in a command list entry. */ | 
|  | enum commands { | 
|  | CmdNOp = 0, | 
|  | CmdIASetup = 1, | 
|  | CmdConfigure = 2, | 
|  | CmdMulticastList = 3, | 
|  | CmdTx = 4, | 
|  | CmdTDR = 5, | 
|  | CmdDump = 6, | 
|  | CmdDiagnose = 7, | 
|  |  | 
|  | /* And some extra flags: */ | 
|  | CmdSuspend = 0x4000,      /* Suspend after completion. */ | 
|  | CmdIntr = 0x2000,         /* Interrupt after completion. */ | 
|  | CmdTxFlex = 0x0008,       /* Use "Flexible mode" for CmdTx command. */ | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* How to wait for the command unit to accept a command. | 
|  | Typically this takes 0 ticks. */ | 
|  | static inline void wait_for_cmd_done(int cmd_ioaddr) | 
|  | { | 
|  | short wait = 100; | 
|  | do   ; | 
|  | while(inb(cmd_ioaddr) && --wait >= 0); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Elements of the dump_statistics block. This block must be lword aligned. */ | 
|  | struct speedo_stats { | 
|  | u32 tx_good_frames; | 
|  | u32 tx_coll16_errs; | 
|  | u32 tx_late_colls; | 
|  | u32 tx_underruns; | 
|  | u32 tx_lost_carrier; | 
|  | u32 tx_deferred; | 
|  | u32 tx_one_colls; | 
|  | u32 tx_multi_colls; | 
|  | u32 tx_total_colls; | 
|  | u32 rx_good_frames; | 
|  | u32 rx_crc_errs; | 
|  | u32 rx_align_errs; | 
|  | u32 rx_resource_errs; | 
|  | u32 rx_overrun_errs; | 
|  | u32 rx_colls_errs; | 
|  | u32 rx_runt_errs; | 
|  | u32 done_marker; | 
|  | } lstats; | 
|  |  | 
|  |  | 
|  | /* A speedo3 TX buffer descriptor with two buffers... */ | 
|  | struct TxFD { | 
|  | volatile s16 status; | 
|  | s16 command; | 
|  | u32 link;          /* void * */ | 
|  | u32 tx_desc_addr;  /* (almost) Always points to the tx_buf_addr element. */ | 
|  | s32 count;         /* # of TBD (=2), Tx start thresh., etc. */ | 
|  | /* This constitutes two "TBD" entries: hdr and data */ | 
|  | u32 tx_buf_addr0;  /* void *, header of frame to be transmitted.  */ | 
|  | s32 tx_buf_size0;  /* Length of Tx hdr. */ | 
|  | u32 tx_buf_addr1;  /* void *, data to be transmitted.  */ | 
|  | s32 tx_buf_size1;  /* Length of Tx data. */ | 
|  | } txfd; | 
|  |  | 
|  |  | 
|  |  | 
|  | /* The Speedo3 Rx buffer descriptors. */ | 
|  | struct RxFD {               /* Receive frame descriptor. */ | 
|  | volatile s16 status; | 
|  | s16 command; | 
|  | u32 link;                 /* struct RxFD * */ | 
|  | u32 rx_buf_addr;          /* void * */ | 
|  | u16 count; | 
|  | u16 size; | 
|  | char packet[1518]; | 
|  | } rxfd; | 
|  |  | 
|  |  | 
|  |  | 
|  | static int congenb = 0;         /* Enable congestion control in the DP83840. */ | 
|  | static int txfifo = 8;          /* Tx FIFO threshold in 4 byte units, 0-15 */ | 
|  | static int rxfifo = 8;          /* Rx FIFO threshold, default 32 bytes. */ | 
|  | static int txdmacount = 0;      /* Tx DMA burst length, 0-127, default 0. */ | 
|  | static int rxdmacount = 0;      /* Rx DMA length, 0 means no preemption. */ | 
|  |  | 
|  |  | 
|  |  | 
|  | /* I don't understand a byte in this structure. It was copied from the | 
|  | * Linux kernel initialization for the eepro100. -- REW */ | 
|  | struct ConfCmd { | 
|  | s16 status; | 
|  | s16 command; | 
|  | u32 link; | 
|  | unsigned char data[22]; | 
|  | } confcmd = { | 
|  | 0, CmdConfigure, | 
|  | (u32) & txfd, | 
|  | {22, 0x08, 0, 0,  0, 0x80, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */ | 
|  | 0, 0x2E, 0,  0x60, 0, | 
|  | 0xf2, 0x48,   0, 0x40, 0xf2, 0x80,        /* 0x40=Force full-duplex */ | 
|  | 0x3f, 0x05, } | 
|  | }; | 
|  |  | 
|  |  | 
|  |  | 
|  | #define TIME_OUT 1000000 | 
|  |  | 
|  | /* The "pci" code needs to allocate a few structures. It wants to | 
|  | * increment the "kernel memory ends here" pointer to allocate memory. | 
|  | * I guess that a machine with PCI has more than 2Mb of memory, so | 
|  | * that is where those things are put. Those structures don't survive | 
|  | * the "jump to kernel start". | 
|  | *   */ | 
|  | #define MEM_START 0x200000   /* Memory starts at 2Mb for now....  */ | 
|  | #define MEM_END   0 | 
|  |  | 
|  | static unsigned short eeprom [0x40]; | 
|  |  | 
|  | static void calibrate_delay (void); | 
|  |  | 
|  |  | 
|  | /***********************************************************************/ | 
|  | /*                       Locally used functions                        */ | 
|  | /***********************************************************************/ | 
|  |  | 
|  |  | 
|  | /* Support function: mdio_write | 
|  | * | 
|  | * This probably writes to the "physical media interface chip". | 
|  | * -- REW | 
|  | */ | 
|  |  | 
|  |  | 
|  | static int mdio_write(int ioaddr, int phy_id, int location, int value) | 
|  | { | 
|  | int val, boguscnt = 64*4;         /* <64 usec. to complete, typ 27 ticks */ | 
|  |  | 
|  | outl(0x04000000 | (location<<16) | (phy_id<<21) | value, | 
|  | ioaddr + SCBCtrlMDI); | 
|  | do { | 
|  | udelay(16); | 
|  |  | 
|  | val = inl(ioaddr + SCBCtrlMDI); | 
|  | if (--boguscnt < 0) { | 
|  | printf(" mdio_write() timed out with val = %8.8x.\n", val); | 
|  | } | 
|  | } while (! (val & 0x10000000)); | 
|  | return val & 0xffff; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Support function: mdio_read | 
|  | * | 
|  | * This probably reads a register in the "physical media interface chip". | 
|  | * -- REW | 
|  | */ | 
|  | static int mdio_read(int ioaddr, int phy_id, int location) | 
|  | { | 
|  | int val, boguscnt = 64*4;               /* <64 usec. to complete, typ 27 ticks */ | 
|  | outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI); | 
|  | do { | 
|  | udelay(16); | 
|  |  | 
|  | val = inl(ioaddr + SCBCtrlMDI); | 
|  | if (--boguscnt < 0) { | 
|  | printf( " mdio_read() timed out with val = %8.8x.\n", val); | 
|  | } | 
|  | } while (! (val & 0x10000000)); | 
|  | return val & 0xffff; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Support function: read_eeprom | 
|  | * reads a value from the eeprom at a specified location. | 
|  | * Arguments: ioaddr:    address of the 82557 chip | 
|  | *            location:  address of the location to read from the eeprom. | 
|  | * returns:   value read from eeprom at location. | 
|  | */ | 
|  | static int read_eeprom(int ioaddr, int location) | 
|  | { | 
|  | int i; | 
|  | unsigned short retval = 0; | 
|  | int ee_addr = ioaddr + SCBeeprom; | 
|  | int read_cmd = location | EE_READ_CMD; | 
|  |  | 
|  | outw(EE_ENB & ~EE_CS, ee_addr); | 
|  | outw(EE_ENB, ee_addr); | 
|  |  | 
|  | /* Shift the read command bits out. */ | 
|  | for (i = 10; i >= 0; i--) { | 
|  | short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; | 
|  | outw(EE_ENB | dataval, ee_addr); | 
|  | eeprom_delay(100); | 
|  | outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); | 
|  | eeprom_delay(150); | 
|  | outw(EE_ENB | dataval, ee_addr);        /* Finish EEPROM a clock tick. */ | 
|  | eeprom_delay(250); | 
|  | } | 
|  | outw(EE_ENB, ee_addr); | 
|  |  | 
|  | for (i = 15; i >= 0; i--) { | 
|  | outw(EE_ENB | EE_SHIFT_CLK, ee_addr); | 
|  | eeprom_delay(100); | 
|  | retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0); | 
|  | outw(EE_ENB, ee_addr); | 
|  | eeprom_delay(100); | 
|  | } | 
|  |  | 
|  | /* Terminate the EEPROM access. */ | 
|  | outw(EE_ENB & ~EE_CS, ee_addr); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void inline whereami (char *str) | 
|  | { | 
|  | #if 0 | 
|  | printf ("%s\r\n", str); | 
|  | sleep (2); | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /***********************************************************************/ | 
|  | /*                    Externally visible functions                     */ | 
|  | /***********************************************************************/ | 
|  |  | 
|  |  | 
|  | /* function: eepro100_reset / eth_reset | 
|  | * resets the card. This is used to allow Linux to probe the card again | 
|  | * from a "virginal" state.... | 
|  | * Arguments: none | 
|  | * | 
|  | * returns:   void. | 
|  | */ | 
|  |  | 
|  | static void eepro100_reset(struct nic *nic) | 
|  | { | 
|  | outl(0, ioaddr + SCBPort); | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /* function: eepro100_transmit / eth_transmit | 
|  | * This transmits a packet. | 
|  | * | 
|  | * Arguments: char d[6]:          destination ethernet address. | 
|  | *            unsigned short t:   ethernet protocol type. | 
|  | *            unsigned short s:   size of the data-part of the packet. | 
|  | *            char *p:            the data for the packet. | 
|  | * returns:   void. | 
|  | */ | 
|  |  | 
|  | static void eepro100_transmit(struct nic *nic, char *d, unsigned int t, unsigned int s, char *p) | 
|  | { | 
|  | struct eth_hdr { | 
|  | unsigned char dst_addr[6]; | 
|  | unsigned char src_addr[6]; | 
|  | unsigned short type; | 
|  | } hdr; | 
|  | unsigned short status; | 
|  | int to; | 
|  | int s1, s2; | 
|  |  | 
|  | status = inw(ioaddr + SCBStatus); | 
|  | /* Acknowledge all of the current interrupt sources ASAP. */ | 
|  | outw(status & 0xfc00, ioaddr + SCBStatus); | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printf ("transmitting type %x packet (%d bytes). status = %x, cmd=%x\r\n", | 
|  | t, s, status, inw (ioaddr + SCBCmd)); | 
|  | #endif | 
|  |  | 
|  |  | 
|  | memcpy (&hdr.dst_addr, d, 6); | 
|  | memcpy (&hdr.src_addr, &arptable[ARP_CLIENT].node, 6); | 
|  |  | 
|  | hdr.type = htons (t); | 
|  |  | 
|  | txfd.status = 0; | 
|  | txfd.command = CmdSuspend | CmdTx | CmdTxFlex; | 
|  | txfd.link   = virt_to_bus (&txfd); | 
|  | txfd.count   = 0x02208000; | 
|  | txfd.tx_desc_addr = (u32)&txfd.tx_buf_addr0; | 
|  |  | 
|  | txfd.tx_buf_addr0 = virt_to_bus (&hdr); | 
|  | txfd.tx_buf_size0 = sizeof (hdr); | 
|  |  | 
|  | txfd.tx_buf_addr1 = virt_to_bus (p); | 
|  | txfd.tx_buf_size1 = s; | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printf ("txfd: \r\n"); | 
|  | hd (&txfd, sizeof (txfd)); | 
|  | #endif | 
|  |  | 
|  | outl(virt_to_bus(&txfd), ioaddr + SCBPointer); | 
|  | outw(INT_MASK | CU_START, ioaddr + SCBCmd); | 
|  | wait_for_cmd_done(ioaddr + SCBCmd); | 
|  |  | 
|  | s1 = inw (ioaddr + SCBStatus); | 
|  | to = TIME_OUT; | 
|  | while (!txfd.status && --to) | 
|  | /* Wait */; | 
|  | s2 = inw (ioaddr + SCBStatus); | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printf ("Tx: Loop executed %d times.\r\n", TIME_OUT-to); | 
|  | printf ("s1 = %x, s2 = %x.\r\n", s1, s2); | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | /* function: eepro100_poll / eth_poll | 
|  | * This recieves a packet from the network. | 
|  | * | 
|  | * Arguments: none | 
|  | * | 
|  | * returns:   1 if a packet was recieved. | 
|  | *            0 if no pacet was recieved. | 
|  | * side effects: | 
|  | *            returns the packet in the array nic->packet. | 
|  | *            returns the length of the packet in nic->packetlen. | 
|  | */ | 
|  |  | 
|  | static int eepro100_poll(struct nic *nic) | 
|  | { | 
|  | int to; | 
|  |  | 
|  | to = TIME_OUT; | 
|  | while (!rxfd.status && --to) | 
|  | /* Wait */; | 
|  |  | 
|  | /* Ok. We got a packet. Now restart the reciever.... */ | 
|  | rxfd.status = 0; | 
|  | rxfd.command = 0xc000; | 
|  | outl(virt_to_bus(&rxfd), ioaddr + SCBPointer); | 
|  | outw(INT_MASK | RX_START, ioaddr + SCBCmd); | 
|  | wait_for_cmd_done(ioaddr + SCBCmd); | 
|  |  | 
|  | if (to) { | 
|  | #ifdef DEBUG | 
|  | printf ("Got a packet: Len = %d.\r\n", rxfd.count & 0x3fff); | 
|  | #endif | 
|  | nic->packetlen =  rxfd.count & 0x3fff; | 
|  | bcopy (rxfd.packet, nic->packet, sizeof (rxfd.packet)); | 
|  | #ifdef DEBUG | 
|  | hd (nic->packet, 0x30); | 
|  | #endif | 
|  | return 1; | 
|  | } else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void eepro100_disable(struct nic *nic) | 
|  | { | 
|  | } | 
|  |  | 
|  | /* exported function: eepro100_probe / eth_probe | 
|  | * initializes a card | 
|  | * | 
|  | * side effects: | 
|  | *            leaves the ioaddress of the 82557 chip in the variable ioaddr. | 
|  | *            leaves the 82557 initialized, and ready to recieve packets. | 
|  | */ | 
|  |  | 
|  | struct nic *eepro100_probe(struct nic *nic, unsigned short *probeaddrs) | 
|  | { | 
|  | int pci_index; | 
|  | u16 sum = 0; | 
|  | int i, j, to; | 
|  | unsigned short value; | 
|  | int options; | 
|  | int promisc; | 
|  |  | 
|  | if (probeaddrs == 0 || probeaddrs[0] == 0) | 
|  | return 0; | 
|  |  | 
|  | ioaddr = probeaddrs[0] & ~3; /* Mask the bit that says "this is an io addr" */ | 
|  |  | 
|  | /* Ok. Got one. Read the eeprom. */ | 
|  | for (j = 0, i = 0; i < 0x40; i++) { | 
|  | value = read_eeprom(ioaddr, i); | 
|  | eeprom[i] = value; | 
|  | sum += value; | 
|  | } | 
|  |  | 
|  | printf ("Ethernet addr: "); | 
|  | for (i=0;i<6;i++) { | 
|  | arptable[ARP_CLIENT].node[i] =  (eeprom[i/2] >> (8*(i&1))) & 0xff; | 
|  | printf ("%b%c", arptable[ARP_CLIENT].node[i] , i < 5?':':' '); | 
|  | } | 
|  | printf ("\r\n"); | 
|  |  | 
|  | if (sum != 0xBABA) | 
|  | printf("eepro100: Invalid EEPROM checksum %#4.4x, " | 
|  | "check settings before activating this device!\r\n", sum); | 
|  | outl(0, ioaddr + SCBPort); | 
|  | udelay (10); | 
|  |  | 
|  | whereami ("Got eeprom."); | 
|  |  | 
|  | outl(virt_to_bus(&lstats), ioaddr + SCBPointer); | 
|  | outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd); | 
|  | wait_for_cmd_done(ioaddr + SCBCmd); | 
|  |  | 
|  | whereami ("set stats addr."); | 
|  | /* INIT RX stuff. */ | 
|  |  | 
|  | /* Base = 0 */ | 
|  | outl(0, ioaddr + SCBPointer); | 
|  | outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd); | 
|  | wait_for_cmd_done(ioaddr + SCBCmd); | 
|  |  | 
|  | whereami ("set rx base addr."); | 
|  |  | 
|  | rxfd.status  = 0x0001; | 
|  | rxfd.command = 0x0000; | 
|  | rxfd.link    = virt_to_bus(&rxfd); | 
|  | rxfd.rx_buf_addr = (int) &nic->packet; | 
|  | rxfd.count   = 0; | 
|  | rxfd.size    = 1528; | 
|  |  | 
|  | outl(virt_to_bus(&rxfd), ioaddr + SCBPointer); | 
|  | outw(INT_MASK | RX_START, ioaddr + SCBCmd); | 
|  | wait_for_cmd_done(ioaddr + SCBCmd); | 
|  |  | 
|  | whereami ("started RX process."); | 
|  |  | 
|  | /* Start the reciever.... */ | 
|  | rxfd.status = 0; | 
|  | rxfd.command = 0xc000; | 
|  | outl(virt_to_bus(&rxfd), ioaddr + SCBPointer); | 
|  | outw(INT_MASK | RX_START, ioaddr + SCBCmd); | 
|  |  | 
|  |  | 
|  | /* INIT TX stuff. */ | 
|  |  | 
|  | /* Base = 0 */ | 
|  | outl(0, ioaddr + SCBPointer); | 
|  | outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd); | 
|  | wait_for_cmd_done(ioaddr + SCBCmd); | 
|  |  | 
|  | whereami ("set TX base addr."); | 
|  |  | 
|  | txfd.command      = (CmdIASetup); | 
|  | txfd.status       = 0x0000; | 
|  | txfd.link         = virt_to_bus (&confcmd); | 
|  |  | 
|  | { | 
|  | char *p = (char *)&txfd.tx_desc_addr; | 
|  |  | 
|  | for (i=0;i<6;i++) | 
|  | p[i] = arptable[ARP_CLIENT].node[i]; | 
|  | } | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printf ("Setup_eaddr:\r\n"); | 
|  | hd (&txfd, 0x20); | 
|  | #endif | 
|  | /*      options = 0x40; */ /* 10mbps half duplex... */ | 
|  | options = 0x00;            /* Autosense */ | 
|  |  | 
|  | promisc = 0; | 
|  |  | 
|  | printf ("eeprom[6] = %x \r\n", eeprom[6]); | 
|  | if (   ((eeprom[6]>>8) & 0x3f) == DP83840 | 
|  | || ((eeprom[6]>>8) & 0x3f) == DP83840A) { | 
|  | int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422; | 
|  | if (congenb) | 
|  | mdi_reg23 |= 0x0100; | 
|  | printf("  DP83840 specific setup, setting register 23 to %x.\r\n", | 
|  | mdi_reg23); | 
|  | mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23); | 
|  | } | 
|  | whereami ("Done DP8340 special setup."); | 
|  | if (options != 0) { | 
|  | mdio_write(ioaddr, eeprom[6] & 0x1f, 0, | 
|  | ((options & 0x20) ? 0x2000 : 0) |    /* 100mbps? */ | 
|  | ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */ | 
|  | whereami ("set mdio_register."); | 
|  | } | 
|  |  | 
|  | confcmd.command  = CmdSuspend | CmdConfigure; | 
|  | confcmd.status   = 0x0000; | 
|  | confcmd.link     = virt_to_bus (&txfd); | 
|  | confcmd.data[1]  = (txfifo << 4) | rxfifo; | 
|  | confcmd.data[4]  = rxdmacount; | 
|  | confcmd.data[5]  = txdmacount + 0x80; | 
|  | confcmd.data[15] = promisc ? 0x49: 0x48; | 
|  | confcmd.data[19] = (options & 0x10) ? 0xC0 : 0x80; | 
|  | confcmd.data[21] = promisc ? 0x0D: 0x05; | 
|  |  | 
|  | outl(virt_to_bus(&txfd), ioaddr + SCBPointer); | 
|  | outw(INT_MASK | CU_START, ioaddr + SCBCmd); | 
|  | wait_for_cmd_done(ioaddr + SCBCmd); | 
|  |  | 
|  | whereami ("started TX thingy (config, iasetup)."); | 
|  |  | 
|  | to = TIME_OUT; | 
|  | while (!txfd.status && --to) | 
|  | /* Wait */; | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printf ("\r\nLoop executed %d times.\r\n", TIME_OUT-to); | 
|  | #endif | 
|  | nic->reset = eepro100_reset; | 
|  | nic->poll = eepro100_poll; | 
|  | nic->transmit = eepro100_transmit; | 
|  | nic->disable = eepro100_disable; | 
|  | return nic; | 
|  | } | 
|  |  | 
|  | static int loops_per_sec; | 
|  | static int loops_per_usec; | 
|  |  | 
|  | static void udelay (int val) | 
|  | { | 
|  | int c; | 
|  |  | 
|  | for(c=0; c<val*loops_per_usec; c++) { | 
|  | /* Nothing */; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | #if 1 | 
|  |  | 
|  | /* I don't have the possibility to test the below code. Murphey tells me | 
|  | * it won't work. | 
|  | */ | 
|  |  | 
|  | static void calibrate_delay (void) | 
|  | { | 
|  | loops_per_usec = 500; | 
|  | } | 
|  | #else | 
|  |  | 
|  |  | 
|  | #define __delay udelay | 
|  | #define jiffies currticks() | 
|  | #define HZ 18 | 
|  |  | 
|  | #define LPS_PREC 8 | 
|  |  | 
|  | /* Copied from /usr/src/linux/init/main.c */ | 
|  | /* with just a few modifications. */ | 
|  | void calibrate_delay (void) | 
|  | { | 
|  | int ticks; | 
|  | int loopbit; | 
|  | int lps_precision = LPS_PREC; | 
|  |  | 
|  | loops_per_sec = (1<<12); | 
|  | loops_per_usec = 1; | 
|  |  | 
|  | printk("Calibrating delay loop.. "); | 
|  | while (loops_per_sec <<= 1) { | 
|  | /* wait for "start of" clock tick */ | 
|  | ticks = jiffies; | 
|  | while (ticks == jiffies) | 
|  | /* nothing */; | 
|  | /* Go .. */ | 
|  | ticks = jiffies; | 
|  | __delay(loops_per_sec); | 
|  | ticks = jiffies - ticks; | 
|  | if (ticks) | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Do a binary approximation to get loops_per_second set to equal one clock | 
|  | (up to lps_precision bits) */ | 
|  | loops_per_sec >>= 1; | 
|  | loopbit = loops_per_sec; | 
|  | while ( lps_precision-- && (loopbit >>= 1) ) { | 
|  | loops_per_sec |= loopbit; | 
|  | ticks = jiffies; | 
|  | while (ticks == jiffies); | 
|  | ticks = jiffies; | 
|  | __delay(loops_per_sec); | 
|  | if (jiffies != ticks)   /* longer than 1 tick */ | 
|  | loops_per_sec &= ~loopbit; | 
|  | } | 
|  |  | 
|  | /* finally, adjust loops per second in terms of seconds instead of clocks */ | 
|  | loops_per_sec *= HZ; | 
|  | /* Round the value and print it */ | 
|  | printk("ok - %d.%d BogoMIPS\n", | 
|  | (loops_per_sec+25000)/500000, | 
|  | ((loops_per_sec+25000)/50000) % 10); | 
|  | loops_per_usec = loops_per_sec / 1000000; | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /*********************************************************************/ | 
|  |  | 
|  | #ifdef DEBUG | 
|  |  | 
|  | /* Hexdump a number of bytes from memory... */ | 
|  | void hd (void *where, int n) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | while (n > 0) { | 
|  | printf ("%X ", where); | 
|  | for (i=0;i < ( (n>16)?16:n);i++) | 
|  | printf (" %b", ((char *)where)[i]); | 
|  | printf ("\n\r"); | 
|  | n -= 16; | 
|  | where += 16; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #endif /* INCLUDE_EEPRO100 */ |