📄 uli526x.c
字号:
/* * 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){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -