net: rtl8139: Factor out hardware reset
[platform/kernel/u-boot.git] / drivers / net / rtl8139.c
index 90e9f12..68ef9ee 100644 (file)
@@ -44,7 +44,7 @@
      which reserves the ranges 0x00000-0x10000 and 0x98000-0xA0000.  My
      interpretation of this "reserved" is that Etherboot may do whatever it
      likes, as long as its environment is kept intact (like the BIOS
-     variables).  Hopefully fixed rtl_poll() once and for all. The symptoms
+     variables).  Hopefully fixed rtl8139_recv() once and for all.     The symptoms
      were that if Etherboot was left at the boot menu for several minutes, the
      first eth_poll failed.  Seems like I am the only person who does this.
      First of all I fixed the debugging code and then set out for a long bug
@@ -65,7 +65,7 @@
      corruption because of exceeding 32K during runtime.
 
   28 Jul 1999  (Matthias Meixner - meixner@rbg.informatik.tu-darmstadt.de)
-     rtl_poll was quite broken: it used the RxOK interrupt flag instead
+     rtl8139_recv was quite broken: it used the RxOK interrupt flag instead
      of the RxBufferEmpty flag which often resulted in very bad
      transmission performace - below 1kBytes/s.
 
@@ -200,10 +200,10 @@ static unsigned char rx_ring[RX_BUF_LEN+16] __attribute__((aligned(4)));
 
 static int rtl8139_probe(struct eth_device *dev, bd_t *bis);
 static int rtl8139_read_eeprom(unsigned int location, unsigned int addr_len);
-static void rtl_reset(struct eth_device *dev);
-static int rtl_transmit(struct eth_device *dev, void *packet, int length);
-static int rtl_poll(struct eth_device *dev);
-static void rtl_disable(struct eth_device *dev);
+static void rtl8139_reset(struct eth_device *dev);
+static int rtl8139_send(struct eth_device *dev, void *packet, int length);
+static int rtl8139_recv(struct eth_device *dev);
+static void rtl8139_stop(struct eth_device *dev);
 static int rtl_bcast_addr(struct eth_device *dev, const u8 *bcast_mac, int join)
 {
        return (0);
@@ -245,9 +245,9 @@ int rtl8139_initialize(bd_t *bis)
                dev->priv = (void *) devno;
                dev->iobase = (int)bus_to_phys(iobase);
                dev->init = rtl8139_probe;
-               dev->halt = rtl_disable;
-               dev->send = rtl_transmit;
-               dev->recv = rtl_poll;
+               dev->halt = rtl8139_stop;
+               dev->send = rtl8139_send;
+               dev->recv = rtl8139_recv;
                dev->mcast = rtl_bcast_addr;
 
                eth_register (dev);
@@ -277,7 +277,7 @@ static int rtl8139_probe(struct eth_device *dev, bd_t *bis)
        for (i = 0; i < 3; i++)
                *ap++ = le16_to_cpu (rtl8139_read_eeprom(i + 7, addr_len));
 
-       rtl_reset(dev);
+       rtl8139_reset(dev);
 
        if (inb(ioaddr + RTL_REG_MEDIASTATUS) & RTL_REG_MEDIASTATUS_MSRLINKFAIL) {
                printf("Cable not connected or other link failure\n");
@@ -357,104 +357,116 @@ static const unsigned int rtl8139_rx_config =
        (RX_FIFO_THRESH << 13) |
        (RX_DMA_BURST << 8);
 
-static void set_rx_mode(struct eth_device *dev) {
-       unsigned int mc_filter[2];
-       int rx_mode;
+static void rtl8139_set_rx_mode(struct eth_device *dev)
+{
        /* !IFF_PROMISC */
-       rx_mode = RTL_REG_RXCONFIG_ACCEPTBROADCAST |
-                 RTL_REG_RXCONFIG_ACCEPTMULTICAST |
-                 RTL_REG_RXCONFIG_ACCEPTMYPHYS;
-       mc_filter[1] = mc_filter[0] = 0xffffffff;
+       unsigned int rx_mode = RTL_REG_RXCONFIG_ACCEPTBROADCAST |
+                              RTL_REG_RXCONFIG_ACCEPTMULTICAST |
+                              RTL_REG_RXCONFIG_ACCEPTMYPHYS;
 
        outl(rtl8139_rx_config | rx_mode, ioaddr + RTL_REG_RXCONFIG);
 
-       outl(mc_filter[0], ioaddr + RTL_REG_MAR0 + 0);
-       outl(mc_filter[1], ioaddr + RTL_REG_MAR0 + 4);
+       outl(0xffffffff, ioaddr + RTL_REG_MAR0 + 0);
+       outl(0xffffffff, ioaddr + RTL_REG_MAR0 + 4);
 }
 
-static void rtl_reset(struct eth_device *dev)
+static void rtl8139_hw_reset(struct eth_device *dev)
 {
+       u8 reg;
        int i;
 
        outb(RTL_REG_CHIPCMD_CMDRESET, ioaddr + RTL_REG_CHIPCMD);
 
-       cur_rx = 0;
-       cur_tx = 0;
-
        /* Give the chip 10ms to finish the reset. */
-       for (i=0; i<100; ++i){
-               if ((inb(ioaddr + RTL_REG_CHIPCMD) &
-                    RTL_REG_CHIPCMD_CMDRESET) == 0)
+       for (i = 0; i < 100; i++) {
+               reg = inb(ioaddr + RTL_REG_CHIPCMD);
+               if (!(reg & RTL_REG_CHIPCMD_CMDRESET))
                        break;
-               udelay (100); /* wait 100us */
+
+               udelay(100);
        }
+}
+
+static void rtl8139_reset(struct eth_device *dev)
+{
+       int i;
 
+       cur_rx = 0;
+       cur_tx = 0;
+
+       rtl8139_hw_reset(dev);
 
        for (i = 0; i < ETH_ALEN; i++)
                outb(dev->enetaddr[i], ioaddr + RTL_REG_MAC0 + i);
 
        /* Must enable Tx/Rx before setting transfer thresholds! */
        outb(RTL_REG_CHIPCMD_CMDRXENB | RTL_REG_CHIPCMD_CMDTXENB,
-               ioaddr + RTL_REG_CHIPCMD);
-       outl((RX_FIFO_THRESH<<13) | (RX_BUF_LEN_IDX<<11) | (RX_DMA_BURST<<8),
-               ioaddr + RTL_REG_RXCONFIG);             /* accept no frames yet!  */
-       outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + RTL_REG_TXCONFIG);
-
-       /* The Linux driver changes RTL_REG_CONFIG1 here to use a different LED pattern
-        * for half duplex or full/autodetect duplex (for full/autodetect, the
-        * outputs are TX/RX, Link10/100, FULL, while for half duplex it uses
-        * TX/RX, Link100, Link10).  This is messy, because it doesn't match
-        * the inscription on the mounting bracket.  It should not be changed
-        * from the configuration EEPROM default, because the card manufacturer
-        * should have set that to match the card.  */
-
-       debug_cond(DEBUG_RX,
-               "rx ring address is %lX\n",(unsigned long)rx_ring);
+            ioaddr + RTL_REG_CHIPCMD);
+
+       /* accept no frames yet! */
+       outl(rtl8139_rx_config, ioaddr + RTL_REG_RXCONFIG);
+       outl((TX_DMA_BURST << 8) | 0x03000000, ioaddr + RTL_REG_TXCONFIG);
+
+       /*
+        * The Linux driver changes RTL_REG_CONFIG1 here to use a different
+        * LED pattern for half duplex or full/autodetect duplex (for
+        * full/autodetect, the outputs are TX/RX, Link10/100, FULL, while
+        * for half duplex it uses TX/RX, Link100, Link10).  This is messy,
+        * because it doesn't match the inscription on the mounting bracket.
+        * It should not be changed from the configuration EEPROM default,
+        * because the card manufacturer should have set that to match the
+        * card.
+        */
+       debug_cond(DEBUG_RX, "rx ring address is %p\n", rx_ring);
+
        flush_cache((unsigned long)rx_ring, RX_BUF_LEN);
        outl(phys_to_bus((int)rx_ring), ioaddr + RTL_REG_RXBUF);
 
-       /* If we add multicast support, the RTL_REG_MAR0 register would have to be
-        * initialized to 0xffffffffffffffff (two 32 bit accesses).  Etherboot
-        * only needs broadcast (for ARP/RARP/BOOTP/DHCP) and unicast.  */
-
+       /*
+        * If we add multicast support, the RTL_REG_MAR0 register would have
+        * to be initialized to 0xffffffffffffffff (two 32 bit accesses).
+        * Etherboot only needs broadcast (for ARP/RARP/BOOTP/DHCP) and
+        * unicast.
+        */
        outb(RTL_REG_CHIPCMD_CMDRXENB | RTL_REG_CHIPCMD_CMDTXENB,
-               ioaddr + RTL_REG_CHIPCMD);
+            ioaddr + RTL_REG_CHIPCMD);
 
        outl(rtl8139_rx_config, ioaddr + RTL_REG_RXCONFIG);
 
        /* Start the chip's Tx and Rx process. */
        outl(0, ioaddr + RTL_REG_RXMISSED);
 
-       /* set_rx_mode */
-       set_rx_mode(dev);
+       rtl8139_set_rx_mode(dev);
 
        /* Disable all known interrupts by setting the interrupt mask. */
        outw(0, ioaddr + RTL_REG_INTRMASK);
 }
 
-static int rtl_transmit(struct eth_device *dev, void *packet, int length)
+static int rtl8139_send(struct eth_device *dev, void *packet, int length)
 {
-       unsigned int status;
-       unsigned long txstatus;
        unsigned int len = length;
+       unsigned long txstatus;
+       unsigned int status;
        int i = 0;
 
        ioaddr = dev->iobase;
 
-       memcpy((char *)tx_buffer, (char *)packet, (int)length);
+       memcpy(tx_buffer, packet, length);
 
        debug_cond(DEBUG_TX, "sending %d bytes\n", len);
 
-       /* Note: RTL8139 doesn't auto-pad, send minimum payload (another 4
-        * bytes are sent automatically for the FCS, totalling to 64 bytes). */
-       while (len < ETH_ZLEN) {
+       /*
+        * Note: RTL8139 doesn't auto-pad, send minimum payload (another 4
+        * bytes are sent automatically for the FCS, totalling to 64 bytes).
+        */
+       while (len < ETH_ZLEN)
                tx_buffer[len++] = '\0';
-       }
 
        flush_cache((unsigned long)tx_buffer, length);
-       outl(phys_to_bus((int)tx_buffer), ioaddr + RTL_REG_TXADDR0 + cur_tx*4);
-       outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | len,
-               ioaddr + RTL_REG_TXSTATUS0 + cur_tx*4);
+       outl(phys_to_bus((unsigned long)tx_buffer),
+            ioaddr + RTL_REG_TXADDR0 + cur_tx * 4);
+       outl(((TX_FIFO_THRESH << 11) & 0x003f0000) | len,
+            ioaddr + RTL_REG_TXSTATUS0 + cur_tx * 4);
 
        do {
                status = inw(ioaddr + RTL_REG_INTRSTATUS);
@@ -462,62 +474,57 @@ static int rtl_transmit(struct eth_device *dev, void *packet, int length)
                 * Only acknlowledge interrupt sources we can properly
                 * handle here - the RTL_REG_INTRSTATUS_RXOVERFLOW/
                 * RTL_REG_INTRSTATUS_RXFIFOOVER MUST be handled in the
-                * rtl_poll() function.
+                * rtl8139_recv() function.
                 */
-               outw(status & (RTL_REG_INTRSTATUS_TXOK |
-                              RTL_REG_INTRSTATUS_TXERR |
-                              RTL_REG_INTRSTATUS_PCIERR),
-                       ioaddr + RTL_REG_INTRSTATUS);
-               if ((status & (RTL_REG_INTRSTATUS_TXOK |
-                              RTL_REG_INTRSTATUS_TXERR |
-                              RTL_REG_INTRSTATUS_PCIERR)) != 0)
+               status &= RTL_REG_INTRSTATUS_TXOK | RTL_REG_INTRSTATUS_TXERR |
+                         RTL_REG_INTRSTATUS_PCIERR;
+               outw(status, ioaddr + RTL_REG_INTRSTATUS);
+               if (status)
                        break;
+
                udelay(10);
        } while (i++ < RTL_TIMEOUT);
 
-       txstatus = inl(ioaddr + RTL_REG_TXSTATUS0 + cur_tx*4);
-
-       if (status & RTL_REG_INTRSTATUS_TXOK) {
-               cur_tx = (cur_tx + 1) % NUM_TX_DESC;
-
-               debug_cond(DEBUG_TX,
-                       "tx done, status %hX txstatus %lX\n",
-                       status, txstatus);
-
-               return length;
-       } else {
+       txstatus = inl(ioaddr + RTL_REG_TXSTATUS0 + cur_tx * 4);
 
+       if (!(status & RTL_REG_INTRSTATUS_TXOK)) {
                debug_cond(DEBUG_TX,
-                       "tx timeout/error (%d usecs), status %hX txstatus %lX\n",
-                       10*i, status, txstatus);
+                          "tx timeout/error (%d usecs), status %hX txstatus %lX\n",
+                          10 * i, status, txstatus);
 
-               rtl_reset(dev);
+               rtl8139_reset(dev);
 
                return 0;
        }
+
+       cur_tx = (cur_tx + 1) % NUM_TX_DESC;
+
+       debug_cond(DEBUG_TX, "tx done, status %hX txstatus %lX\n",
+                  status, txstatus);
+
+       return length;
 }
 
-static int rtl_poll(struct eth_device *dev)
+static int rtl8139_recv(struct eth_device *dev)
 {
-       unsigned int status;
-       unsigned int ring_offs;
+       const unsigned int rxstat = RTL_REG_INTRSTATUS_RXFIFOOVER |
+                                   RTL_REG_INTRSTATUS_RXOVERFLOW |
+                                   RTL_REG_INTRSTATUS_RXOK;
        unsigned int rx_size, rx_status;
-       int length=0;
+       unsigned int ring_offs;
+       unsigned int status;
+       int length = 0;
 
        ioaddr = dev->iobase;
 
-       if (inb(ioaddr + RTL_REG_CHIPCMD) & RTL_REG_CHIPCMD_RXBUFEMPTY) {
+       if (inb(ioaddr + RTL_REG_CHIPCMD) & RTL_REG_CHIPCMD_RXBUFEMPTY)
                return 0;
-       }
 
        status = inw(ioaddr + RTL_REG_INTRSTATUS);
        /* See below for the rest of the interrupt acknowledges.  */
-       outw(status & ~(RTL_REG_INTRSTATUS_RXFIFOOVER |
-                       RTL_REG_INTRSTATUS_RXOVERFLOW |
-                       RTL_REG_INTRSTATUS_RXOK),
-               ioaddr + RTL_REG_INTRSTATUS);
+       outw(status & ~rxstat, ioaddr + RTL_REG_INTRSTATUS);
 
-       debug_cond(DEBUG_RX, "rtl_poll: int %hX ", status);
+       debug_cond(DEBUG_RX, "%s: int %hX ", __func__, status);
 
        ring_offs = cur_rx % RX_BUF_LEN;
        /* ring_offs is guaranteed being 4-byte aligned */
@@ -528,53 +535,48 @@ static int rtl_poll(struct eth_device *dev)
        if ((rx_status & (RTL_STS_RXBADSYMBOL | RTL_STS_RXRUNT |
                          RTL_STS_RXTOOLONG | RTL_STS_RXCRCERR |
                          RTL_STS_RXBADALIGN)) ||
-           (rx_size < ETH_ZLEN) || (rx_size > ETH_FRAME_LEN + 4)) {
+           (rx_size < ETH_ZLEN) ||
+           (rx_size > ETH_FRAME_LEN + 4)) {
                printf("rx error %hX\n", rx_status);
-               rtl_reset(dev); /* this clears all interrupts still pending */
+               /* this clears all interrupts still pending */
+               rtl8139_reset(dev);
                return 0;
        }
 
        /* Received a good packet */
        length = rx_size - 4;   /* no one cares about the FCS */
-       if (ring_offs+4+rx_size-4 > RX_BUF_LEN) {
-               int semi_count = RX_BUF_LEN - ring_offs - 4;
+       if (ring_offs + 4 + rx_size - 4 > RX_BUF_LEN) {
                unsigned char rxdata[RX_BUF_LEN];
+               int semi_count = RX_BUF_LEN - ring_offs - 4;
 
                memcpy(rxdata, rx_ring + ring_offs + 4, semi_count);
-               memcpy(&(rxdata[semi_count]), rx_ring, rx_size-4-semi_count);
+               memcpy(&rxdata[semi_count], rx_ring,
+                      rx_size - 4 - semi_count);
 
                net_process_received_packet(rxdata, length);
                debug_cond(DEBUG_RX, "rx packet %d+%d bytes",
-                       semi_count, rx_size-4-semi_count);
+                          semi_count, rx_size - 4 - semi_count);
        } else {
                net_process_received_packet(rx_ring + ring_offs + 4, length);
-               debug_cond(DEBUG_RX, "rx packet %d bytes", rx_size-4);
+               debug_cond(DEBUG_RX, "rx packet %d bytes", rx_size - 4);
        }
        flush_cache((unsigned long)rx_ring, RX_BUF_LEN);
 
-       cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
+       cur_rx = ROUND(cur_rx + rx_size + 4, 4);
        outw(cur_rx - 16, ioaddr + RTL_REG_RXBUFPTR);
-       /* See RTL8139 Programming Guide V0.1 for the official handling of
-        * Rx overflow situations.  The document itself contains basically no
-        * usable information, except for a few exception handling rules.  */
-       outw(status & (RTL_REG_INTRSTATUS_RXFIFOOVER |
-                      RTL_REG_INTRSTATUS_RXOVERFLOW |
-                      RTL_REG_INTRSTATUS_RXOK), ioaddr + RTL_REG_INTRSTATUS);
+       /*
+        * See RTL8139 Programming Guide V0.1 for the official handling of
+        * Rx overflow situations. The document itself contains basically
+        * no usable information, except for a few exception handling rules.
+        */
+       outw(status & rxstat, ioaddr + RTL_REG_INTRSTATUS);
+
        return length;
 }
 
-static void rtl_disable(struct eth_device *dev)
+static void rtl8139_stop(struct eth_device *dev)
 {
-       int i;
-
        ioaddr = dev->iobase;
 
-       /* reset the chip */
-       outb(RTL_REG_CHIPCMD_CMDRESET, ioaddr + RTL_REG_CHIPCMD);
-
-       /* Give the chip 10ms to finish the reset. */
-       for (i=0; i<100; ++i){
-               if ((inb(ioaddr + RTL_REG_CHIPCMD) & RTL_REG_CHIPCMD_CMDRESET) == 0) break;
-               udelay (100); /* wait 100us */
-       }
+       rtl8139_hw_reset(dev);
 }