Implement general ULi 526x Ethernet driver support in U-boot
authorRoy Zang <tie-fei.zang@freescale.com>
Mon, 5 Nov 2007 09:39:24 +0000 (17:39 +0800)
committerBen Warren <bwarren@qstreams.com>
Tue, 6 Nov 2007 15:26:39 +0000 (10:26 -0500)
This patch implements general ULi 526x Ethernet driver.
Until now, it is the only native Ethernet port on
MPC8610HPCD board, but it could be used on other boards
with ULi 526x Ethernet port as well.

Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
Signed-off-by: Zhang Wei <wei.zhang@freescale.com>
Acked-by: Jon Loeliger <jdl@freescale.com>
Signed-off-by: Ben Warren <bwarren@qstreams.com>
drivers/Makefile
drivers/uli526x.c [new file with mode: 0644]
net/eth.c

index d19588f3af4c389ace46a4ab4f0b007bc492f464..207606a3aa7948b52976b05621165abd21ca7940 100755 (executable)
@@ -41,7 +41,7 @@ COBJS = 3c589.o 5701rls.o ali512x.o at45.o ata_piix.o \
          omap24xx_i2c.o pc_keyb.o \
          pci.o pci_auto.o pci_indirect.o \
          pcnet.o plb2800_eth.o ps2ser.o ps2mult.o pxa_pcmcia.o \
-         rpx_pcmcia.o rtl8019.o rtl8139.o rtl8169.o \
+         rpx_pcmcia.o rtl8019.o rtl8139.o rtl8169.o uli526x.o\
          s3c4510b_eth.o s3c4510b_uart.o \
          sed13806.o sed156x.o \
          serial.o serial_max3100.o \
diff --git a/drivers/uli526x.c b/drivers/uli526x.c
new file mode 100644 (file)
index 0000000..1267c57
--- /dev/null
@@ -0,0 +1,996 @@
+/*
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Roy Zang <tie-fei.zang@freescale.com>, Sep, 2007
+ *
+ * Description:
+ * ULI 526x Ethernet port driver.
+ * Based on the Linux driver: drivers/net/tulip/uli526x.c
+ *
+ * This 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 of the  License, or
+ * (at your option) any later version.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <net.h>
+#include <asm/io.h>
+#include <pci.h>
+#include <miiphy.h>
+
+/* some kernel function compatible define */
+
+#if defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI) && \
+       defined(CONFIG_ULI526X)
+
+#undef DEBUG
+
+/* Board/System/Debug information/definition */
+#define ULI_VENDOR_ID          0x10B9
+#define ULI5261_DEVICE_ID      0x5261
+#define ULI5263_DEVICE_ID      0x5263
+/* ULi M5261 ID*/
+#define PCI_ULI5261_ID         ULI5261_DEVICE_ID << 16 | ULI_VENDOR_ID
+/* ULi M5263 ID*/
+#define PCI_ULI5263_ID         ULI5263_DEVICE_ID << 16 | ULI_VENDOR_ID
+
+#define ULI526X_IO_SIZE        0x100
+#define TX_DESC_CNT    0x10            /* Allocated Tx descriptors */
+#define RX_DESC_CNT    PKTBUFSRX       /* Allocated Rx descriptors */
+#define TX_FREE_DESC_CNT       (TX_DESC_CNT - 2) /* Max TX packet count */
+#define TX_WAKE_DESC_CNT       (TX_DESC_CNT - 3) /* TX wakeup count */
+#define DESC_ALL_CNT           (TX_DESC_CNT + RX_DESC_CNT)
+#define TX_BUF_ALLOC           0x300
+#define RX_ALLOC_SIZE          PKTSIZE
+#define ULI526X_RESET          1
+#define CR0_DEFAULT            0
+#define CR6_DEFAULT            0x22200000
+#define CR7_DEFAULT            0x180c1
+#define CR15_DEFAULT           0x06            /* TxJabber RxWatchdog */
+#define TDES0_ERR_MASK         0x4302          /* TXJT, LC, EC, FUE */
+#define MAX_PACKET_SIZE                1514
+#define ULI5261_MAX_MULTICAST  14
+#define RX_COPY_SIZE           100
+#define MAX_CHECK_PACKET       0x8000
+
+#define ULI526X_10MHF          0
+#define ULI526X_100MHF         1
+#define ULI526X_10MFD          4
+#define ULI526X_100MFD         5
+#define ULI526X_AUTO           8
+
+#define ULI526X_TXTH_72                0x400000        /* TX TH 72 byte */
+#define ULI526X_TXTH_96                0x404000        /* TX TH 96 byte */
+#define ULI526X_TXTH_128       0x0000          /* TX TH 128 byte */
+#define ULI526X_TXTH_256       0x4000          /* TX TH 256 byte */
+#define ULI526X_TXTH_512       0x8000          /* TX TH 512 byte */
+#define ULI526X_TXTH_1K                0xC000          /* TX TH 1K  byte */
+
+/* CR9 definition: SROM/MII */
+#define CR9_SROM_READ          0x4800
+#define CR9_SRCS               0x1
+#define CR9_SRCLK              0x2
+#define CR9_CRDOUT             0x8
+#define SROM_DATA_0            0x0
+#define SROM_DATA_1            0x4
+#define PHY_DATA_1             0x20000
+#define PHY_DATA_0             0x00000
+#define MDCLKH                 0x10000
+
+#define PHY_POWER_DOWN 0x800
+
+#define SROM_V41_CODE          0x14
+
+#define SROM_CLK_WRITE(data, ioaddr) do {                      \
+       outl(data|CR9_SROM_READ|CR9_SRCS, ioaddr);              \
+       udelay(5);                                              \
+       outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK, ioaddr);    \
+       udelay(5);                                              \
+       outl(data|CR9_SROM_READ|CR9_SRCS, ioaddr);              \
+       udelay(5);                                              \
+       } while (0)
+
+/* Structure/enum declaration */
+
+struct tx_desc {
+       u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */
+       char *tx_buf_ptr;               /* Data for us */
+       struct tx_desc *next_tx_desc;
+};
+
+struct rx_desc {
+       u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */
+       char *rx_buf_ptr;               /* Data for us */
+       struct rx_desc *next_rx_desc;
+};
+
+struct uli526x_board_info {
+       u32 chip_id;    /* Chip vendor/Device ID */
+       pci_dev_t pdev;
+
+       long ioaddr;                    /* I/O base address */
+       u32 cr0_data;
+       u32 cr5_data;
+       u32 cr6_data;
+       u32 cr7_data;
+       u32 cr15_data;
+
+       /* pointer for memory physical address */
+       dma_addr_t buf_pool_dma_ptr;    /* Tx buffer pool memory */
+       dma_addr_t buf_pool_dma_start;  /* Tx buffer pool align dword */
+       dma_addr_t desc_pool_dma_ptr;   /* descriptor pool memory */
+       dma_addr_t first_tx_desc_dma;
+       dma_addr_t first_rx_desc_dma;
+
+       /* descriptor pointer */
+       unsigned char *buf_pool_ptr;    /* Tx buffer pool memory */
+       unsigned char *buf_pool_start;  /* Tx buffer pool align dword */
+       unsigned char *desc_pool_ptr;   /* descriptor pool memory */
+       struct tx_desc *first_tx_desc;
+       struct tx_desc *tx_insert_ptr;
+       struct tx_desc *tx_remove_ptr;
+       struct rx_desc *first_rx_desc;
+       struct rx_desc *rx_ready_ptr;   /* packet come pointer */
+       unsigned long tx_packet_cnt;    /* transmitted packet count */
+
+       u16 PHY_reg4;                   /* Saved Phyxcer register 4 value */
+
+       u8 media_mode;                  /* user specify media mode */
+       u8 op_mode;                     /* real work dedia mode */
+       u8 phy_addr;
+
+       /* NIC SROM data */
+       unsigned char srom[128];
+};
+
+enum uli526x_offsets {
+       DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20,
+       DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48,
+       DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70,
+       DCR15 = 0x78
+};
+
+enum uli526x_CR6_bits {
+       CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80,
+       CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000,
+       CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000
+};
+
+/* Global variable declaration -- */
+
+static unsigned char uli526x_media_mode = ULI526X_AUTO;
+
+static struct tx_desc desc_pool_array[DESC_ALL_CNT + 0x20]
+       __attribute__ ((aligned(32)));
+static char buf_pool[TX_BUF_ALLOC * TX_DESC_CNT + 4];
+
+/* For module input parameter */
+static int mode = 8;
+
+/* function declaration -- */
+static int uli526x_start_xmit(struct eth_device *dev,
+                               volatile void *packet, int length);
+static const struct ethtool_ops netdev_ethtool_ops;
+static u16 read_srom_word(long, int);
+static void uli526x_descriptor_init(struct uli526x_board_info *, unsigned long);
+static void allocate_rx_buffer(struct uli526x_board_info *);
+static void update_cr6(u32, unsigned long);
+static u16 phy_read(unsigned long, u8, u8, u32);
+static u16 phy_readby_cr10(unsigned long, u8, u8);
+static void phy_write(unsigned long, u8, u8, u16, u32);
+static void phy_writeby_cr10(unsigned long, u8, u8, u16);
+static void phy_write_1bit(unsigned long, u32, u32);
+static u16 phy_read_1bit(unsigned long, u32);
+static int uli526x_rx_packet(struct eth_device *);
+static void uli526x_free_tx_pkt(struct eth_device *,
+               struct uli526x_board_info *);
+static void uli526x_reuse_buf(struct rx_desc *);
+static void uli526x_init(struct eth_device *);
+static void uli526x_set_phyxcer(struct uli526x_board_info *);
+
+
+static int uli526x_init_one(struct eth_device *, bd_t *);
+static void uli526x_disable(struct eth_device *);
+static void set_mac_addr(struct eth_device *);
+
+static struct pci_device_id uli526x_pci_tbl[] = {
+       { ULI_VENDOR_ID, ULI5261_DEVICE_ID}, /* 5261 device */
+       { ULI_VENDOR_ID, ULI5263_DEVICE_ID}, /* 5263 device */
+       {}
+};
+
+/* ULI526X network board routine */
+
+/*
+ *     Search ULI526X board, register it
+ */
+
+int uli526x_initialize(bd_t *bis)
+{
+       pci_dev_t devno;
+       int card_number = 0;
+       struct eth_device *dev;
+       struct uli526x_board_info *db;  /* board information structure */
+
+       u32 iobase;
+       int idx = 0;
+
+       while (1) {
+               /* Find PCI device */
+               devno = pci_find_devices(uli526x_pci_tbl, idx++);
+               if (devno < 0)
+                       break;
+
+               pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase);
+               iobase &= ~0xf;
+
+               dev = (struct eth_device *)malloc(sizeof *dev);
+               sprintf(dev->name, "uli526x#%d\n", card_number);
+               db = (struct uli526x_board_info *)
+                       malloc(sizeof(struct uli526x_board_info));
+
+               dev->priv = db;
+               db->pdev = devno;
+               dev->iobase = iobase;
+
+               dev->init = uli526x_init_one;
+               dev->halt = uli526x_disable;
+               dev->send = uli526x_start_xmit;
+               dev->recv = uli526x_rx_packet;
+
+               /* init db */
+               db->ioaddr = dev->iobase;
+               /* get chip id */
+
+               pci_read_config_dword(devno, PCI_VENDOR_ID, &db->chip_id);
+#ifdef DEBUG
+               printf("uli526x: uli526x @0x%x\n", iobase);
+               printf("uli526x: chip_id%x\n", db->chip_id);
+#endif
+               eth_register(dev);
+               card_number++;
+               pci_write_config_byte(devno, PCI_LATENCY_TIMER, 0x20);
+               udelay(10 * 1000);
+       }
+       return card_number;
+}
+
+static int uli526x_init_one(struct eth_device *dev, bd_t *bis)
+{
+
+       struct uli526x_board_info *db = dev->priv;
+       int i;
+
+       switch (mode) {
+       case ULI526X_10MHF:
+       case ULI526X_100MHF:
+       case ULI526X_10MFD:
+       case ULI526X_100MFD:
+               uli526x_media_mode = mode;
+               break;
+       default:
+               uli526x_media_mode = ULI526X_AUTO;
+               break;
+       }
+
+       /* Allocate Tx/Rx descriptor memory */
+       db->desc_pool_ptr = (uchar *)&desc_pool_array[0];
+       db->desc_pool_dma_ptr = (dma_addr_t)&desc_pool_array[0];
+       if (db->desc_pool_ptr == NULL)
+               return 0;
+
+       db->buf_pool_ptr = &buf_pool[0];
+       db->buf_pool_dma_ptr = (dma_addr_t)&buf_pool[0];
+       if (db->buf_pool_ptr == NULL)
+               return 0;
+
+       db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
+       db->first_tx_desc_dma = db->desc_pool_dma_ptr;
+
+       db->buf_pool_start = db->buf_pool_ptr;
+       db->buf_pool_dma_start = db->buf_pool_dma_ptr;
+
+#ifdef DEBUG
+       printf("%s(): db->ioaddr= 0x%x\n",
+               __FUNCTION__, db->ioaddr);
+       printf("%s(): media_mode= 0x%x\n",
+               __FUNCTION__, uli526x_media_mode);
+       printf("%s(): db->desc_pool_ptr= 0x%x\n",
+               __FUNCTION__, db->desc_pool_ptr);
+       printf("%s(): db->desc_pool_dma_ptr= 0x%x\n",
+               __FUNCTION__, db->desc_pool_dma_ptr);
+       printf("%s(): db->buf_pool_ptr= 0x%x\n",
+               __FUNCTION__, db->buf_pool_ptr);
+       printf("%s(): db->buf_pool_dma_ptr= 0x%x\n",
+               __FUNCTION__, db->buf_pool_dma_ptr);
+#endif
+
+       /* read 64 word srom data */
+       for (i = 0; i < 64; i++)
+               ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr,
+                       i));
+
+       /* Set Node address */
+       if (((u16 *) db->srom)[0] == 0xffff || ((u16 *) db->srom)[0] == 0)
+       /* SROM absent, so write MAC address to ID Table */
+               set_mac_addr(dev);
+       else {          /*Exist SROM*/
+               for (i = 0; i < 6; i++)
+                       dev->enetaddr[i] = db->srom[20 + i];
+       }
+#ifdef DEBUG
+       for (i = 0; i < 6; i++)
+               printf("%c%02x", i ? ':' : ' ', dev->enetaddr[i]);
+#endif
+       db->PHY_reg4 = 0x1e0;
+
+       /* system variable init */
+       db->cr6_data = CR6_DEFAULT ;
+       db->cr6_data |= ULI526X_TXTH_256;
+       db->cr0_data = CR0_DEFAULT;
+       uli526x_init(dev);
+       return 1;
+}
+
+static void uli526x_disable(struct eth_device *dev)
+{
+#ifdef DEBUG
+       printf("uli526x_disable\n");
+#endif
+       struct uli526x_board_info *db = dev->priv;
+
+       if (!((inl(db->ioaddr + DCR12)) & 0x8)) {
+               /* Reset & stop ULI526X board */
+               outl(ULI526X_RESET, db->ioaddr + DCR0);
+               udelay(5);
+               phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id);
+
+               /* reset the board */
+               db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */
+               update_cr6(db->cr6_data, dev->iobase);
+               outl(0, dev->iobase + DCR7);            /* Disable Interrupt */
+               outl(inl(dev->iobase + DCR5), dev->iobase + DCR5);
+       }
+}
+
+/*     Initialize ULI526X board
+ *     Reset ULI526X board
+ *     Initialize TX/Rx descriptor chain structure
+ *     Send the set-up frame
+ *     Enable Tx/Rx machine
+ */
+
+static void uli526x_init(struct eth_device *dev)
+{
+
+       struct uli526x_board_info *db = dev->priv;
+       u8      phy_tmp;
+       u16     phy_value;
+       u16 phy_reg_reset;
+
+       /* Reset M526x MAC controller */
+       outl(ULI526X_RESET, db->ioaddr + DCR0); /* RESET MAC */
+       udelay(100);
+       outl(db->cr0_data, db->ioaddr + DCR0);
+       udelay(5);
+
+       /* Phy addr : In some boards,M5261/M5263 phy address != 1 */
+       db->phy_addr = 1;
+       db->tx_packet_cnt = 0;
+       for (phy_tmp = 0; phy_tmp < 32; phy_tmp++) {
+               /* peer add */
+               phy_value = phy_read(db->ioaddr, phy_tmp, 3, db->chip_id);
+               if (phy_value != 0xffff && phy_value != 0) {
+                       db->phy_addr = phy_tmp;
+                       break;
+               }
+       }
+
+#ifdef DEBUG
+       printf("%s(): db->ioaddr= 0x%x\n", __FUNCTION__, db->ioaddr);
+       printf("%s(): db->phy_addr= 0x%x\n", __FUNCTION__, db->phy_addr);
+#endif
+       if (phy_tmp == 32)
+               printf("Can not find the phy address!!!");
+
+       /* Parser SROM and media mode */
+       db->media_mode = uli526x_media_mode;
+
+       if (!(inl(db->ioaddr + DCR12) & 0x8)) {
+               /* Phyxcer capability setting */
+               phy_reg_reset = phy_read(db->ioaddr,
+                       db->phy_addr, 0, db->chip_id);
+               phy_reg_reset = (phy_reg_reset | 0x8000);
+               phy_write(db->ioaddr, db->phy_addr, 0,
+                       phy_reg_reset, db->chip_id);
+               udelay(500);
+
+               /* Process Phyxcer Media Mode */
+               uli526x_set_phyxcer(db);
+       }
+       /* Media Mode Process */
+       if (!(db->media_mode & ULI526X_AUTO))
+               db->op_mode = db->media_mode;   /* Force Mode */
+
+       /* Initialize Transmit/Receive decriptor and CR3/4 */
+       uli526x_descriptor_init(db, db->ioaddr);
+
+       /* Init CR6 to program M526X operation */
+       update_cr6(db->cr6_data, db->ioaddr);
+
+       /* Init CR7, interrupt active bit */
+       db->cr7_data = CR7_DEFAULT;
+       outl(db->cr7_data, db->ioaddr + DCR7);
+
+       /* Init CR15, Tx jabber and Rx watchdog timer */
+       outl(db->cr15_data, db->ioaddr + DCR15);
+
+       /* Enable ULI526X Tx/Rx function */
+       db->cr6_data |= CR6_RXSC | CR6_TXSC;
+       update_cr6(db->cr6_data, db->ioaddr);
+       while (!(inl(db->ioaddr + DCR12) & 0x8))
+               udelay(10);
+}
+
+/*
+ *     Hardware start transmission.
+ *     Send a packet to media from the upper layer.
+ */
+
+static int uli526x_start_xmit(struct eth_device *dev,
+                               volatile void *packet, int length)
+{
+       struct uli526x_board_info *db = dev->priv;
+       struct tx_desc *txptr;
+       unsigned int len = length;
+       /* Too large packet check */
+       if (len > MAX_PACKET_SIZE) {
+               printf(": big packet = %d\n", len);
+               return 0;
+       }
+
+       /* No Tx resource check, it never happen nromally */
+       if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) {
+               printf("No Tx resource %ld\n", db->tx_packet_cnt);
+               return 0;
+       }
+
+       /* Disable NIC interrupt */
+       outl(0, dev->iobase + DCR7);
+
+       /* transmit this packet */
+       txptr = db->tx_insert_ptr;
+       memcpy((char *)txptr->tx_buf_ptr, (char *)packet, (int)length);
+       txptr->tdes1 = cpu_to_le32(0xe1000000 | length);
+
+       /* Point to next transmit free descriptor */
+       db->tx_insert_ptr = txptr->next_tx_desc;
+
+       /* Transmit Packet Process */
+       if ((db->tx_packet_cnt < TX_DESC_CNT)) {
+               txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */
+               db->tx_packet_cnt++;                    /* Ready to send */
+               outl(0x1, dev->iobase + DCR1);  /* Issue Tx polling */
+       }
+
+       /* Got ULI526X status */
+       db->cr5_data = inl(db->ioaddr + DCR5);
+       outl(db->cr5_data, db->ioaddr + DCR5);
+
+#ifdef TX_DEBUG
+       printf("%s(): length = 0x%x\n", __FUNCTION__, length);
+       printf("%s(): cr5_data=%x\n", __FUNCTION__, db->cr5_data);
+#endif
+
+       outl(db->cr7_data, dev->iobase + DCR7);
+       uli526x_free_tx_pkt(dev, db);
+
+       return length;
+}
+
+/*
+ *     Free TX resource after TX complete
+ */
+
+static void uli526x_free_tx_pkt(struct eth_device *dev,
+       struct uli526x_board_info *db)
+{
+       struct tx_desc *txptr;
+       u32 tdes0;
+
+       txptr = db->tx_remove_ptr;
+       while (db->tx_packet_cnt) {
+               tdes0 = le32_to_cpu(txptr->tdes0);
+               /* printf(DRV_NAME ": tdes0=%x\n", tdes0); */
+               if (tdes0 & 0x80000000)
+                       break;
+
+               /* A packet sent completed */
+               db->tx_packet_cnt--;
+
+               if (tdes0 != 0x7fffffff) {
+#ifdef TX_DEBUG
+                       printf("%s()tdes0=%x\n", __FUNCTION__, tdes0);
+#endif
+                       if (tdes0 & TDES0_ERR_MASK) {
+                               if (tdes0 & 0x0002) {   /* UnderRun */
+                                       if (!(db->cr6_data & CR6_SFT)) {
+                                               db->cr6_data = db->cr6_data |
+                                                       CR6_SFT;
+                                               update_cr6(db->cr6_data,
+                                                       db->ioaddr);
+                                       }
+                               }
+                       }
+               }
+
+               txptr = txptr->next_tx_desc;
+       }/* End of while */
+
+       /* Update TX remove pointer to next */
+       db->tx_remove_ptr = txptr;
+}
+
+
+/*
+ *     Receive the come packet and pass to upper layer
+ */
+
+static int uli526x_rx_packet(struct eth_device *dev)
+{
+       struct uli526x_board_info *db = dev->priv;
+       struct rx_desc *rxptr;
+       int rxlen = 0;
+       u32 rdes0;
+
+       rxptr = db->rx_ready_ptr;
+
+       rdes0 = le32_to_cpu(rxptr->rdes0);
+#ifdef RX_DEBUG
+       printf("%s(): rxptr->rdes0=%x:%x\n", __FUNCTION__, rxptr->rdes0);
+#endif
+       if (!(rdes0 & 0x80000000)) {    /* packet owner check */
+               if ((rdes0 & 0x300) != 0x300) {
+                       /* A packet without First/Last flag */
+                       /* reuse this buf */
+                       printf("A packet without First/Last flag");
+                       uli526x_reuse_buf(rxptr);
+               } else {
+                       /* A packet with First/Last flag */
+                       rxlen = ((rdes0 >> 16) & 0x3fff) - 4;
+#ifdef RX_DEBUG
+                       printf("%s(): rxlen =%x\n", __FUNCTION__, rxlen);
+#endif
+                       /* error summary bit check */
+                       if (rdes0 & 0x8000) {
+                               /* This is a error packet */
+                               printf("Eroor: rdes0: %lx\n", rdes0);
+                       }
+
+                       if (!(rdes0 & 0x8000) ||
+                               ((db->cr6_data & CR6_PM) && (rxlen > 6))) {
+
+#ifdef RX_DEBUG
+                               printf("%s(): rx_skb_ptr =%x\n",
+                                       __FUNCTION__, rxptr->rx_buf_ptr);
+                               printf("%s(): rxlen =%x\n",
+                                       __FUNCTION__, rxlen);
+
+                               printf("%s(): buf addr =%x\n",
+                                       __FUNCTION__, rxptr->rx_buf_ptr);
+                               printf("%s(): rxlen =%x\n",
+                                       __FUNCTION__, rxlen);
+                               int i;
+                               for (i = 0; i < 0x20; i++)
+                                       printf("%s(): data[%x] =%x\n",
+                                       __FUNCTION__, i, rxptr->rx_buf_ptr[i]);
+#endif
+
+                               NetReceive(rxptr->rx_buf_ptr, rxlen);
+                               uli526x_reuse_buf(rxptr);
+
+                       } else {
+                               /* Reuse SKB buffer when the packet is error */
+                               printf("Reuse buffer, rdes0");
+                               uli526x_reuse_buf(rxptr);
+                       }
+               }
+
+               rxptr = rxptr->next_rx_desc;
+       }
+
+       db->rx_ready_ptr = rxptr;
+       return rxlen;
+}
+
+/*
+ *     Reuse the RX buffer
+ */
+
+static void uli526x_reuse_buf(struct rx_desc *rxptr)
+{
+
+       if (!(rxptr->rdes0 & cpu_to_le32(0x80000000)))
+               rxptr->rdes0 = cpu_to_le32(0x80000000);
+       else
+               printf("Buffer reuse method error");
+}
+/*
+ *     Initialize transmit/Receive descriptor
+ *     Using Chain structure, and allocate Tx/Rx buffer
+ */
+
+static void uli526x_descriptor_init(struct uli526x_board_info *db,
+       unsigned long ioaddr)
+{
+       struct tx_desc *tmp_tx;
+       struct rx_desc *tmp_rx;
+       unsigned char *tmp_buf;
+       dma_addr_t tmp_tx_dma, tmp_rx_dma;
+       dma_addr_t tmp_buf_dma;
+       int i;
+       /* tx descriptor start pointer */
+       db->tx_insert_ptr = db->first_tx_desc;
+       db->tx_remove_ptr = db->first_tx_desc;
+
+       outl(db->first_tx_desc_dma, ioaddr + DCR4);     /* TX DESC address */
+
+       /* rx descriptor start pointer */
+       db->first_rx_desc = (void *)db->first_tx_desc +
+               sizeof(struct tx_desc) * TX_DESC_CNT;
+       db->first_rx_desc_dma =  db->first_tx_desc_dma +
+               sizeof(struct tx_desc) * TX_DESC_CNT;
+       db->rx_ready_ptr = db->first_rx_desc;
+       outl(db->first_rx_desc_dma, ioaddr + DCR3);     /* RX DESC address */
+#ifdef DEBUG
+       printf("%s(): db->first_tx_desc= 0x%x\n",
+               __FUNCTION__, db->first_tx_desc);
+       printf("%s(): db->first_rx_desc_dma= 0x%x\n",
+               __FUNCTION__, db->first_rx_desc_dma);
+#endif
+       /* Init Transmit chain */
+       tmp_buf = db->buf_pool_start;
+       tmp_buf_dma = db->buf_pool_dma_start;
+       tmp_tx_dma = db->first_tx_desc_dma;
+       for (tmp_tx = db->first_tx_desc, i = 0;
+                       i < TX_DESC_CNT; i++, tmp_tx++) {
+               tmp_tx->tx_buf_ptr = tmp_buf;
+               tmp_tx->tdes0 = cpu_to_le32(0);
+               tmp_tx->tdes1 = cpu_to_le32(0x81000000);        /* IC, chain */
+               tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma);
+               tmp_tx_dma += sizeof(struct tx_desc);
+               tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma);
+               tmp_tx->next_tx_desc = tmp_tx + 1;
+               tmp_buf = tmp_buf + TX_BUF_ALLOC;
+               tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC;
+       }
+       (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma);
+       tmp_tx->next_tx_desc = db->first_tx_desc;
+
+        /* Init Receive descriptor chain */
+       tmp_rx_dma = db->first_rx_desc_dma;
+       for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT;
+                       i++, tmp_rx++) {
+               tmp_rx->rdes0 = cpu_to_le32(0);
+               tmp_rx->rdes1 = cpu_to_le32(0x01000600);
+               tmp_rx_dma += sizeof(struct rx_desc);
+               tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma);
+               tmp_rx->next_rx_desc = tmp_rx + 1;
+       }
+       (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma);
+       tmp_rx->next_rx_desc = db->first_rx_desc;
+
+       /* pre-allocate Rx buffer */
+       allocate_rx_buffer(db);
+}
+
+/*
+ *     Update CR6 value
+ *     Firstly stop ULI526X, then written value and start
+ */
+
+static void update_cr6(u32 cr6_data, unsigned long ioaddr)
+{
+
+       outl(cr6_data, ioaddr + DCR6);
+       udelay(5);
+}
+
+/*
+ *     Allocate rx buffer,
+ */
+
+static void allocate_rx_buffer(struct uli526x_board_info *db)
+{
+       int index;
+       struct rx_desc *rxptr;
+       rxptr = db->first_rx_desc;
+       u32 addr;
+
+       for (index = 0; index < RX_DESC_CNT; index++) {
+               addr = (u32)NetRxPackets[index];
+               addr += (16 - (addr & 15));
+               rxptr->rx_buf_ptr = (char *) addr;
+               rxptr->rdes2 = cpu_to_le32(addr);
+               rxptr->rdes0 = cpu_to_le32(0x80000000);
+#ifdef DEBUG
+               printf("%s(): Number 0x%x:\n", __FUNCTION__, index);
+               printf("%s(): addr 0x%x:\n", __FUNCTION__, addr);
+               printf("%s(): rxptr address = 0x%x\n", __FUNCTION__, rxptr);
+               printf("%s(): rxptr buf address = 0x%x\n", \
+                       __FUNCTION__, rxptr->rx_buf_ptr);
+               printf("%s(): rdes2  = 0x%x\n", __FUNCTION__, rxptr->rdes2);
+#endif
+               rxptr = rxptr->next_rx_desc;
+       }
+}
+
+/*
+ *     Read one word data from the serial ROM
+ */
+
+static u16 read_srom_word(long ioaddr, int offset)
+{
+       int i;
+       u16 srom_data = 0;
+       long cr9_ioaddr = ioaddr + DCR9;
+
+       outl(CR9_SROM_READ, cr9_ioaddr);
+       outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+       /* Send the Read Command 110b */
+       SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+       SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+       SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr);
+
+       /* Send the offset */
+       for (i = 5; i >= 0; i--) {
+               srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0;
+               SROM_CLK_WRITE(srom_data, cr9_ioaddr);
+       }
+
+       outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+       for (i = 16; i > 0; i--) {
+               outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr);
+               udelay(5);
+               srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT)
+                       ? 1 : 0);
+               outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+               udelay(5);
+       }
+
+       outl(CR9_SROM_READ, cr9_ioaddr);
+       return srom_data;
+}
+
+/*
+ *     Set 10/100 phyxcer capability
+ *     AUTO mode : phyxcer register4 is NIC capability
+ *     Force mode: phyxcer register4 is the force media
+ */
+
+static void uli526x_set_phyxcer(struct uli526x_board_info *db)
+{
+       u16 phy_reg;
+
+       /* Phyxcer capability setting */
+       phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0;
+
+       if (db->media_mode & ULI526X_AUTO) {
+               /* AUTO Mode */
+               phy_reg |= db->PHY_reg4;
+       } else {
+               /* Force Mode */
+               switch (db->media_mode) {
+               case ULI526X_10MHF: phy_reg |= 0x20; break;
+               case ULI526X_10MFD: phy_reg |= 0x40; break;
+               case ULI526X_100MHF: phy_reg |= 0x80; break;
+               case ULI526X_100MFD: phy_reg |= 0x100; break;
+               }
+
+       }
+
+       /* Write new capability to Phyxcer Reg4 */
+       if (!(phy_reg & 0x01e0)) {
+               phy_reg |= db->PHY_reg4;
+               db->media_mode |= ULI526X_AUTO;
+       }
+       phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id);
+
+       /* Restart Auto-Negotiation */
+       phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id);
+       udelay(50);
+}
+
+/*
+ *     Write a word to Phy register
+ */
+
+static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset,
+       u16 phy_data, u32 chip_id)
+{
+       u16 i;
+       unsigned long ioaddr;
+
+       if (chip_id == PCI_ULI5263_ID) {
+               phy_writeby_cr10(iobase, phy_addr, offset, phy_data);
+               return;
+       }
+       /* M5261/M5263 Chip */
+       ioaddr = iobase + DCR9;
+
+       /* Send 33 synchronization clock to Phy controller */
+       for (i = 0; i < 35; i++)
+               phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+       /* Send start command(01) to Phy */
+       phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+       phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+       /* Send write command(01) to Phy */
+       phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+       phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+       /* Send Phy address */
+       for (i = 0x10; i > 0; i = i >> 1)
+               phy_write_1bit(ioaddr, phy_addr & i ?
+                       PHY_DATA_1 : PHY_DATA_0, chip_id);
+
+       /* Send register address */
+       for (i = 0x10; i > 0; i = i >> 1)
+               phy_write_1bit(ioaddr, offset & i ?
+                       PHY_DATA_1 : PHY_DATA_0, chip_id);
+
+       /* written trasnition */
+       phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+       phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+
+       /* Write a word data to PHY controller */
+       for (i = 0x8000; i > 0; i >>= 1)
+               phy_write_1bit(ioaddr, phy_data & i ?
+                       PHY_DATA_1 : PHY_DATA_0, chip_id);
+}
+
+/*
+ *     Read a word data from phy register
+ */
+
+static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id)
+{
+       int i;
+       u16 phy_data;
+       unsigned long ioaddr;
+
+       if (chip_id == PCI_ULI5263_ID)
+               return phy_readby_cr10(iobase, phy_addr, offset);
+       /* M5261/M5263 Chip */
+       ioaddr = iobase + DCR9;
+
+       /* Send 33 synchronization clock to Phy controller */
+       for (i = 0; i < 35; i++)
+               phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+       /* Send start command(01) to Phy */
+       phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+       phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+
+       /* Send read command(10) to Phy */
+       phy_write_1bit(ioaddr, PHY_DATA_1, chip_id);
+       phy_write_1bit(ioaddr, PHY_DATA_0, chip_id);
+
+       /* Send Phy address */
+       for (i = 0x10; i > 0; i = i >> 1)
+               phy_write_1bit(ioaddr, phy_addr & i ?
+                       PHY_DATA_1 : PHY_DATA_0, chip_id);
+
+       /* Send register address */
+       for (i = 0x10; i > 0; i = i >> 1)
+               phy_write_1bit(ioaddr, offset & i ?
+                       PHY_DATA_1 : PHY_DATA_0, chip_id);
+
+       /* Skip transition state */
+       phy_read_1bit(ioaddr, chip_id);
+
+       /* read 16bit data */
+       for (phy_data = 0, i = 0; i < 16; i++) {
+               phy_data <<= 1;
+               phy_data |= phy_read_1bit(ioaddr, chip_id);
+       }
+
+       return phy_data;
+}
+
+static u16 phy_readby_cr10(unsigned long iobase, u8 phy_addr, u8 offset)
+{
+       unsigned long ioaddr, cr10_value;
+
+       ioaddr = iobase + DCR10;
+       cr10_value = phy_addr;
+       cr10_value = (cr10_value<<5) + offset;
+       cr10_value = (cr10_value<<16) + 0x08000000;
+       outl(cr10_value, ioaddr);
+       udelay(1);
+       while (1) {
+               cr10_value = inl(ioaddr);
+               if (cr10_value & 0x10000000)
+                       break;
+       }
+       return (cr10_value&0x0ffff);
+}
+
+static void phy_writeby_cr10(unsigned long iobase, u8 phy_addr,
+       u8 offset, u16 phy_data)
+{
+       unsigned long ioaddr, cr10_value;
+
+       ioaddr = iobase + DCR10;
+       cr10_value = phy_addr;
+       cr10_value = (cr10_value<<5) + offset;
+       cr10_value = (cr10_value<<16) + 0x04000000 + phy_data;
+       outl(cr10_value, ioaddr);
+       udelay(1);
+}
+/*
+ *     Write one bit data to Phy Controller
+ */
+
+static void phy_write_1bit(unsigned long ioaddr, u32 phy_data, u32 chip_id)
+{
+       outl(phy_data , ioaddr);                        /* MII Clock Low */
+       udelay(1);
+       outl(phy_data  | MDCLKH, ioaddr);       /* MII Clock High */
+       udelay(1);
+       outl(phy_data , ioaddr);                        /* MII Clock Low */
+       udelay(1);
+}
+
+/*
+ *     Read one bit phy data from PHY controller
+ */
+
+static u16 phy_read_1bit(unsigned long ioaddr, u32 chip_id)
+{
+       u16 phy_data;
+
+       outl(0x50000 , ioaddr);
+       udelay(1);
+       phy_data = (inl(ioaddr) >> 19) & 0x1;
+       outl(0x40000 , ioaddr);
+       udelay(1);
+
+       return phy_data;
+}
+
+/*
+ * Set MAC address to ID Table
+ */
+
+static void set_mac_addr(struct eth_device *dev)
+{
+       int i;
+       u16 addr;
+       struct uli526x_board_info *db = dev->priv;
+       outl(0x10000, db->ioaddr + DCR0);       /* Diagnosis mode */
+       /* Reset dianostic pointer port */
+       outl(0x1c0, db->ioaddr + DCR13);
+       outl(0, db->ioaddr + DCR14);    /* Clear reset port */
+       outl(0x10, db->ioaddr + DCR14); /* Reset ID Table pointer */
+       outl(0, db->ioaddr + DCR14);    /* Clear reset port */
+       outl(0, db->ioaddr + DCR13);    /* Clear CR13 */
+       /* Select ID Table access port */
+       outl(0x1b0, db->ioaddr + DCR13);
+       /* Read MAC address from CR14 */
+       for (i = 0; i < 3; i++) {
+               addr = dev->enetaddr[2 * i] | (dev->enetaddr[2 * i + 1] << 8);
+               outl(addr, db->ioaddr + DCR14);
+       }
+       /* write end */
+       outl(0, db->ioaddr + DCR13);    /* Clear CR13 */
+       outl(0, db->ioaddr + DCR0);     /* Clear CR0 */
+       udelay(10);
+       return;
+}
+#endif
index e7f1220591cfe147fa453f36bb73a7a773375fec..1b56a356c4f4544623b1093b6ef614dd1ff21a38 100644 (file)
--- a/net/eth.c
+++ b/net/eth.c
@@ -54,6 +54,7 @@ extern int rtl8169_initialize(bd_t*);
 extern int scc_initialize(bd_t*);
 extern int skge_initialize(bd_t*);
 extern int tsi108_eth_initialize(bd_t*);
+extern int uli526x_initialize(bd_t *);
 extern int tsec_initialize(bd_t*, int, char *);
 extern int npe_initialize(bd_t *);
 extern int uec_initialize(int);
@@ -238,6 +239,9 @@ int eth_initialize(bd_t *bis)
 #if defined(CONFIG_TSI108_ETH)
        tsi108_eth_initialize(bis);
 #endif
+#if defined(CONFIG_ULI526X)
+       uli526x_initialize(bis);
+#endif
 #if defined(CONFIG_RTL8139)
        rtl8139_initialize(bis);
 #endif