diff --git a/minix/drivers/net/ip1000/Makefile b/minix/drivers/net/ip1000/Makefile new file mode 100755 index 000000000..6cb4e4aaf --- /dev/null +++ b/minix/drivers/net/ip1000/Makefile @@ -0,0 +1,14 @@ +# Makefile for the ip1000 Ethernet drivers +PROG= ip1000 +SRCS= ip1000.c + +FILES=${PROG}.conf +FILESNAME=${PROG} +FILESDIR= /etc/system.conf.d + +DPADD+= ${LIBNETDRIVER} ${LIBSYS} +LDADD+= -lnetdriver -lsys + +CPPFLAGS+= -I${NETBSDSRCDIR}/minix + +.include diff --git a/minix/drivers/net/ip1000/README b/minix/drivers/net/ip1000/README new file mode 100644 index 000000000..21863614b --- /dev/null +++ b/minix/drivers/net/ip1000/README @@ -0,0 +1,15 @@ +The ip1000 driver is for IC Plus 1000A/Family (ST1023) Ethernet card. + +This driver is referred to Minix(3.4.0) vt6105 driver, +Linux(4.2.1) ipg driver and IP1000A LF-DS-R08 datasheet. + +The supported PCI number is 13F0:1023:13F0:1023. +Maybe the Ethernet cards of other PCI numbers for IC Plus can be supported. + +Revision 1.0 2016/11/02 +Authored by Jia-Ju Bai + +Something can be improved: +1. Ethernet address can not be modified at present. +2. Dump interfaces are not provided. +3. The device needs serveral seconds to work normally after initialization. diff --git a/minix/drivers/net/ip1000/ip1000.c b/minix/drivers/net/ip1000/ip1000.c new file mode 100644 index 000000000..0dff4e87a --- /dev/null +++ b/minix/drivers/net/ip1000/ip1000.c @@ -0,0 +1,959 @@ +#include "ip1000.h" + +/* global value */ +static ic_driver g_driver; +static int g_instance; + +/* I/O function */ +static u8_t my_inb(u16_t port) { + u32_t value; + int r; + if ((r = sys_inb(port, &value)) != OK) + printf("IP1000: sys_inb failed: %d\n", r); + return (u8_t)value; +} +#define ic_inb(port, offset) (my_inb((port) + (offset))) + +static u16_t my_inw(u16_t port) { + u32_t value; + int r; + if ((r = sys_inw(port, &value)) != OK) + printf("IP1000: sys_inw failed: %d\n", r); + return (u16_t)value; +} +#define ic_inw(port, offset) (my_inw((port) + (offset))) + +static u32_t my_inl(u16_t port) { + u32_t value; + int r; + if ((r = sys_inl(port, &value)) != OK) + printf("IP1000: sys_inl failed: %d\n", r); + return value; +} +#define ic_inl(port, offset) (my_inl((port) + (offset))) + +static void my_outb(u16_t port, u8_t value) { + int r; + if ((r = sys_outb(port, value)) != OK) + printf("IP1000: sys_outb failed: %d\n", r); +} +#define ic_outb(port, offset, value) (my_outb((port) + (offset), (value))) + +static void my_outw(u16_t port, u16_t value) { + int r; + if ((r = sys_outw(port, value)) != OK) + printf("IP1000: sys_outw failed: %d\n", r); +} +#define ic_outw(port, offset, value) (my_outw((port) + (offset), (value))) + +static void my_outl(u16_t port, u32_t value) { + int r; + if ((r = sys_outl(port, value)) != OK) + printf("IP1000: sys_outl failed: %d\n", r); +} +#define ic_outl(port, offset, value) (my_outl((port) + (offset), (value))) + +static int udelay(int n) { + int i, j; + int ret = 0; + + for (i=0; iic_base_addr; + int i; + + val = IC_EC_READ | (addr & 0xff); + ic_outw(base, IC_REG_EEPROM_CTRL, val); + for (i = 0; i < 100; i++) { + usleep(100000); + data = ic_inw(base, IC_REG_EEPROM_CTRL); + if (!(data & IC_EC_BUSY)) { + ret = ic_inw(base, IC_REG_EEPROM_DATA); + break; + } + } + if (i == 100) + printf("IP1000: Fail to read EEPROM\n"); + return ret; +} + +static int mdio_read(ic_driver *pdev, int phy_id, int phy_reg) { + u16_t bit_data, base = pdev->ic_base_addr; + struct { + u32_t field; + u32_t len; + } p[] = { + {0xffffffff, 32}, {0x01, 2}, {0x02, 2}, + {phy_id, 5}, {phy_reg, 5}, {0x00, 2}, {0x00, 16}, {0x00, 1}}; + int i, j; + u8_t polarity, data; + + polarity = ic_inb(base, IC_REG_PHY_CTRL); + polarity &= (IC_PC_DUPLEX_POLARITY | IC_PC_LINK_POLARITY); + for (j = 0; j < 5; j++) { + for (i = 0; i < p[j].len; i++) { + data = (p[j].field >> (p[j].len - 1 - i)) << 1; + data &= IC_PC_MGMTDATA; + data |= polarity | IC_PC_MGMTDIR; + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_LO | data); + udelay(10); + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_HI | data); + udelay(10); + } + } + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_LO | + (polarity | IC_PC_MGMTDIR)); + udelay(10); + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_HI | + (polarity | IC_PC_MGMTDIR)); + udelay(10); + + ic_outb(base, IC_REG_PHY_CTRL, polarity | IC_PC_MGMTCLK_LO); + udelay(10); + bit_data = ((ic_inb(base, IC_REG_PHY_CTRL) & IC_PC_MGMTDATA) >> 1) & 1; + ic_outb(base, IC_REG_PHY_CTRL, polarity | IC_PC_MGMTCLK_HI); + udelay(10); + + for (i = 0; i < p[6].len; i++) { + ic_outb(base, IC_REG_PHY_CTRL, polarity | IC_PC_MGMTCLK_LO); + udelay(10); + bit_data = ((ic_inb(base, IC_REG_PHY_CTRL) & IC_PC_MGMTDATA) >> 1) & 1; + ic_outb(base, IC_REG_PHY_CTRL, polarity | IC_PC_MGMTCLK_HI); + udelay(10); + p[6].field |= (bit_data << (p[6].len - 1 - i)); + } + + for (i = 0; i < 3; i++) { + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_LO | + (polarity | IC_PC_MGMTDIR)); + udelay(10); + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_HI | + (polarity | IC_PC_MGMTDIR)); + udelay(10); + } + ic_outb(base, IC_REG_PHY_CTRL, polarity | IC_PC_MGMTCLK_LO | IC_PC_MGMTDIR); + + return p[6].field; +} + +static void mdio_write(ic_driver *pdev, int phy_id, int phy_reg, int val) { + u16_t bit_data, base = pdev->ic_base_addr; + struct { + u32_t field; + u32_t len; + } p[] = { + {0xffffffff, 32}, {0x01, 2}, {0x01, 2}, + {phy_id, 5}, {phy_reg, 5}, {0x02, 2}, {val & 0xffff, 16}, {0x00, 1}}; + int i, j; + u8_t polarity, data; + + polarity = ic_inb(base, IC_REG_PHY_CTRL); + polarity &= (IC_PC_DUPLEX_POLARITY | IC_PC_LINK_POLARITY); + for (j = 0; j < 7; j++) { + for (i = 0; i < p[j].len; i++) { + data = (p[j].field >> (p[j].len - 1 - i)) << 1; + data &= IC_PC_MGMTDATA; + data |= polarity | IC_PC_MGMTDIR; + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_LO | data); + udelay(10); + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_HI | data); + udelay(10); + } + } + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_LO | polarity); + udelay(10); + ic_inb(base, IC_REG_PHY_CTRL); + ic_outb(base, IC_REG_PHY_CTRL, IC_PC_MGMTCLK_HI | polarity); + udelay(10); +} + +/* driver interface */ +static int ic_init(unsigned int instance, ether_addr_t *addr); +static void ic_stop(void); +static void ic_mode(unsigned int mode); +static ssize_t ic_recv(struct netdriver_data *data, size_t max); +static int ic_send(struct netdriver_data *data, size_t size); +static void ic_intr(unsigned int mask); +static void ic_stat(eth_stat_t *stat); +static void ic_alarm(clock_t stamp); + +static int ic_probe(ic_driver *pdev, int instance); +static int ic_init_buf(ic_driver *pdev); +static int ic_init_hw(ic_driver *pdev, ether_addr_t *addr); +static int ic_reset_hw(ic_driver *pdev); +static void ic_power_init(ic_driver *pdev); +static void ic_init_mii(ic_driver *pdev); +static void ic_init_mode(ic_driver *pdev); +static void ic_check_link(ic_driver *pdev); +static void ic_conf_addr(ic_driver *pdev, ether_addr_t *addr); +static void ic_handler(ic_driver *pdev); +static void ic_check_ints(ic_driver *pdev); + +static const struct netdriver ic_table = { + .ndr_init = ic_init, + .ndr_stop = ic_stop, + .ndr_mode = ic_mode, + .ndr_recv = ic_recv, + .ndr_send = ic_send, + .ndr_stat = ic_stat, + .ndr_intr = ic_intr +}; + +int main(int argc, char *argv[]) { + env_setargs(argc, argv); + netdriver_task(&ic_table); +} + +/* Initialize the driver */ +static int ic_init(unsigned int instance, ether_addr_t *addr) { + int ret = 0; + + /* Intialize driver data structure */ + memset(&g_driver, 0, sizeof(g_driver)); + g_driver.ic_link = IC_LINK_UNKNOWN; + strcpy(g_driver.ic_name, "ip1000#0"); + g_driver.ic_name[7] += instance; + g_instance = instance; + + /* Probe the device */ + if (ic_probe(&g_driver, instance)) { + printf("IP1000: Device is not found\n"); + ret = -ENODEV; + goto err_probe; + } + + /* Allocate and initialize buffer */ + if (ic_init_buf(&g_driver)) { + printf("IP1000: Fail to initialize buffer\n"); + ret = -ENODEV; + goto err_init_buf; + } + + /* Intialize hardware */ + if (ic_init_hw(&g_driver, addr)) { + printf("IP1000: Find to initialize hardware\n"); + ret = -EIO; + goto err_init_hw; + } + + /* Use a synchronous alarm instead of a watchdog timer */ + sys_setalarm(sys_hz(), 0); + + /* Clear send and recv flag */ + g_driver.ic_send_flag = FALSE; + g_driver.ic_recv_flag = FALSE; + + return 0; + +err_init_hw: + free_contig(g_driver.ic_buf, g_driver.ic_buf_size); +err_init_buf: +err_probe: + return ret; +} + +/* Match the device and get base address */ +static int ic_probe(ic_driver *pdev, int instance) { + int devind; + u16_t cr, vid, did; + u32_t bar; + u8_t irq, rev; + + /* Find pci device */ + pci_init(); + if (!pci_first_dev(&devind, &vid, &did)) + return -EIO; + while (instance--) { + if (!pci_next_dev(&devind, &vid, &did)) + return -EIO; + } + pci_reserve(devind); + + /* Enable bus mastering */ + cr = pci_attr_r16(devind, PCI_CR); + if (!(cr & PCI_CR_MAST_EN)) + pci_attr_w16(devind, PCI_CR, cr | PCI_CR_MAST_EN); + + /* Get base address */ + bar = pci_attr_r32(devind, PCI_BAR) & 0xffffffe0; + if (bar < 0x400) { + printf("IP1000: Base address is not properly configured\n"); + return -EIO; + } + pdev->ic_base_addr = bar; + + /* Get irq number */ + irq = pci_attr_r8(devind, PCI_ILR); + pdev->ic_irq = irq; + + /* Get revision ID */ + rev = pci_attr_r8(devind, PCI_REV); + pdev->ic_revision = rev; + +#ifdef IP1000_DEBUG + printf("IP1000: Base address is 0x%08x\n", pdev->ic_base_addr); + printf("IP1000: IRQ number is 0x%08x\n", pdev->ic_irq); + printf("IP1000: Revision ID is 0x%08x\n", pdev->ic_revision); +#endif + + return 0; +} + +/* Allocate and initialize buffer */ +static int ic_init_buf(ic_driver *pdev) { + size_t rx_desc_size, tx_desc_size, rx_buf_size, tx_buf_size, tot_buf_size; + ic_desc *desc; + phys_bytes buf_dma, next; + char *buf; + int i; + u64_t frag_size; + + /* Build Rx and Tx descriptor buffer */ + rx_desc_size = IC_RX_DESC_NUM * sizeof(ic_desc); + tx_desc_size = IC_TX_DESC_NUM * sizeof(ic_desc); + + /* Allocate Rx and Tx buffer */ + tx_buf_size = IC_TX_BUF_SIZE; + if (tx_buf_size % 4) + tx_buf_size += 4 - (tx_buf_size % 4); + rx_buf_size = IC_RX_BUF_SIZE; + if (rx_buf_size % 4) + rx_buf_size += 4 - (rx_buf_size % 4); + tot_buf_size = rx_desc_size + tx_desc_size; + tot_buf_size += IC_TX_DESC_NUM * tx_buf_size + IC_RX_DESC_NUM * rx_buf_size; + if (tot_buf_size % 4096) + tot_buf_size += 4096 - (tot_buf_size % 4096); + + if (!(buf = alloc_contig(tot_buf_size, 0, &buf_dma))) { + printf("IP1000: Fail to allocate memory!\n"); + return -ENOMEM; + } + pdev->ic_buf_size = tot_buf_size; + pdev->ic_buf = buf; + + /* Rx descriptor */ + pdev->ic_rx_desc = (ic_desc *)buf; + pdev->ic_rx_desc_dma = buf_dma; + memset(buf, 0, rx_desc_size); + buf += rx_desc_size; + buf_dma += rx_desc_size; + + /* Tx descriptor */ + pdev->ic_tx_desc = (ic_desc *)buf; + pdev->ic_tx_desc_dma = buf_dma; + memset(buf, 0, tx_desc_size); + buf += tx_desc_size; + buf_dma += tx_desc_size; + + /* Rx buffer assignment */ + desc = pdev->ic_rx_desc; + next = pdev->ic_rx_desc_dma; + for (i = 0; i < IC_RX_DESC_NUM; i++) { + /* Set Rx buffer */ + pdev->ic_rx[i].buf_dma = buf_dma; + pdev->ic_rx[i].buf = buf; + buf_dma += rx_buf_size; + buf += rx_buf_size; + + /* Set Rx descriptor */ + frag_size = (u64_t)rx_buf_size; + desc->status = 0x0000000000000000ULL; + desc->frag_info = (u64_t)(pdev->ic_rx[i].buf_dma); + desc->frag_info |= (u64_t)((frag_size << 48) & IC_RFI_FRAG_LEN); + if (i == (IC_RX_DESC_NUM - 1)) + desc->next_desc = (u64_t)(pdev->ic_rx_desc_dma); + else { + next += sizeof(ic_desc); + desc->next_desc = (u64_t)next; + desc++; + } + } + + /* Tx buffer assignment */ + desc = pdev->ic_tx_desc; + next = pdev->ic_tx_desc_dma; + for (i = 0; i < IC_TX_DESC_NUM; i++) { + /* Set Tx buffer */ + pdev->ic_tx[i].busy = 0; + pdev->ic_tx[i].buf_dma = buf_dma; + pdev->ic_tx[i].buf = buf; + buf_dma += tx_buf_size; + buf += tx_buf_size; + + /* Set Rx descriptor */ + desc->status = IC_TFS_TFD_DONE; + desc->frag_info = (u64_t)(pdev->ic_tx[i].buf_dma); + if (i == (IC_TX_DESC_NUM - 1)) + desc->next_desc = (u64_t)(pdev->ic_tx_desc_dma); + else { + next += sizeof(ic_desc); + desc->next_desc = (u64_t)next; + desc++; + } + } + pdev->ic_tx_busy_num = 0; + pdev->ic_tx_head = 0; + pdev->ic_tx_tail = 0; + pdev->ic_rx_head = 0; + pdev->ic_tx_alive = FALSE; + + return 0; +} + +/* Intialize hardware */ +static int ic_init_hw(ic_driver *pdev, ether_addr_t *addr) { + int r, ret; + + /* Set the interrupt handler */ + pdev->ic_hook = pdev->ic_irq; + if ((r = sys_irqsetpolicy(pdev->ic_irq, 0, &pdev->ic_hook)) != OK) { + printf("IP1000: Fail to set IRQ policy: %d\n", r); + ret = -EFAULT; + goto err_irq_policy; + } + + /* Reset hardware */ + if (ic_reset_hw(pdev)) { + printf("IP1000: Fail to reset the hardware\n"); + ret = -EIO; + goto err_reset; + } + + /* Enable IRQ */ + if ((r = sys_irqenable(&pdev->ic_hook)) != OK) { + printf("IP1000: Fail to enable IRQ: %d\n", r); + ret = -EFAULT; + goto err_irq_enable; + } + + /* Configure MAC address */ + ic_conf_addr(pdev, addr); + + return 0; + +err_irq_enable: +err_reset: +err_irq_policy: + return ret; +} + +/* Reset hardware */ +static int ic_reset_hw(ic_driver *pdev) { + u16_t base = pdev->ic_base_addr; + u32_t dl, mac_ctrl; + + /* Reset the chip */ + dl = ic_inl(base, IC_REG_ASIC_CTRL); + dl |= IC_AC_RESET_ALL; + ic_outl(base, IC_REG_ASIC_CTRL, dl); + usleep(10000); + dl = ic_inl(base, IC_REG_ASIC_CTRL); + if (dl & IC_AC_RESET_BUSY) { +#ifdef IP1000_DEBUG + printf("IP1000: Fail to completely reset\n"); +#endif + return -EIO; + } +#ifdef IP1000_DEBUG + printf("IP1000: Reset successfully\n"); +#endif + + /* Let power registers into sane state */ + ic_power_init(pdev); + + /* Initialize MII registers */ + ic_init_mii(pdev); + + /* Initialize hardware mode */ + ic_init_mode(pdev); + + /* Set Rx/Tx descriptor base address */ + ic_outl(base, IC_REG_RX_DESC0, pdev->ic_rx_desc_dma); + ic_outl(base, IC_REG_RX_DESC1, 0x00000000); + ic_outl(base, IC_REG_TX_DESC0, pdev->ic_tx_desc_dma); + ic_outl(base, IC_REG_TX_DESC1, 0x00000000); + +#ifdef IP1000_DEBUG + dl = ic_inl(base, IC_REG_RX_DESC0); + printf("IP1000: Rx descriptor DMA address is: 0x%08x\n", dl); + dl = ic_inl(base, IC_REG_TX_DESC0); + printf("IP1000: Tx descriptor DMA address is: 0x%08x\n", dl); +#endif + + /* Enable interrupts */ + ic_outw(base, IC_REG_INTR_ENA, IC_IR_COMMON); + + /* Start Rx and Tx */ + mac_ctrl = ic_inl(base, IC_REG_MAC_CTRL); + mac_ctrl |= (IC_MC_RX_ENABLE | IC_MC_TX_ENABLE); + ic_outl(base, IC_REG_MAC_CTRL, mac_ctrl); + + return 0; +} + +/* Initialize power registers */ +static void ic_power_init(ic_driver *pdev) { + u8_t physet; + u16_t base = pdev->ic_base_addr; + u32_t mode; + + /* Read LED mode from EEPROM and set LED mode*/ + pdev->ic_led_mode = read_eeprom(pdev, 6); + mode = ic_inl(base, IC_REG_ASIC_CTRL); + mode &= ~(IC_AC_LED_MODE_BIT1 | IC_AC_LED_MODE | IC_AC_LED_SPEED); + if ((pdev->ic_led_mode & 0x03) > 1) + mode |= IC_AC_LED_MODE_BIT1; + if ((pdev->ic_led_mode & 0x01) == 1) + mode |= IC_AC_LED_MODE; + if ((pdev->ic_led_mode & 0x08) == 8) + mode |= IC_AC_LED_SPEED; + ic_outl(base, IC_REG_ASIC_CTRL, mode); + + /* Set physet register*/ + physet = ic_inb(base, IC_REG_PHY_SET); + physet &= ~(0x07); + physet |= ((pdev->ic_led_mode & 0x70) >> 4); + ic_outb(base, IC_REG_PHY_SET, physet); +} + +/* Initialize MII registers */ +static void ic_init_mii(ic_driver *pdev) { + u8_t revision; + u16_t base = pdev->ic_base_addr; + u32_t status; + u16_t length, address, value, mii_phyctrl, mii_1000cr; + const u16_t *phy_param; + int i, phyaddr; + + /* Read MII physical address */ + for (i = 0; i < 32; i++) { + phyaddr = (0x01 + i) % 32; + status = mdio_read(pdev, phyaddr, 0x01); + if ((status != 0xffff) && (status != 0)) + break; + } + if (i == 32) { + printf("IP1000: Fail to read MII phyical address\n"); + return; + } +#ifdef IP1000_DEBUG + printf("IP1000: MII Physical address is %d\n", phyaddr); +#endif + + mii_1000cr = mdio_read(pdev, phyaddr, 0x09); + mii_1000cr |= 0x0700; + mdio_write(pdev, phyaddr, 0x09, mii_1000cr); + mii_phyctrl = mdio_read(pdev, phyaddr, 0x00); + + phy_param = &DefaultPhyParam[0]; + length = *phy_param & 0x00ff; + revision = (u8_t)((*phy_param) >> 8); + phy_param++; + while (length != 0) { + if (pdev->ic_revision == revision) { + while (length > 1) { + address = *phy_param; + value = *(phy_param + 1); + phy_param += 2; + mdio_write(pdev, phyaddr, address, value); + length -= 4; + } + break; + } + else { + phy_param += length / 2; + length = *phy_param & 0x00ff; + revision = (u8_t)((*phy_param) >> 8); + phy_param++; + } + } + + mii_phyctrl |= 0x8200; + mdio_write(pdev, phyaddr, 0x00, mii_phyctrl); +} + +/* Intialize hardware mode */ +static void ic_init_mode(ic_driver *pdev) { + u16_t base = pdev->ic_base_addr; + u32_t mac_ctrl, mac_ctrl1; + + /* Disable interrupt first */ + ic_outw(base, IC_REG_INTR_ENA, 0x0000); + + /* Configure MAC register */ + mac_ctrl = ic_inl(base, IC_REG_MAC_CTRL); + mac_ctrl1 = mac_ctrl | IC_MC_STAT_DISABLE; + if (mac_ctrl & IC_MC_TX_ENABLED) + mac_ctrl1 |= IC_MC_TX_ENABLE; + if (mac_ctrl & IC_MC_RX_ENABLED) + mac_ctrl1 |= IC_MC_RX_ENABLE; + ic_outl(base, IC_REG_MAC_CTRL, (mac_ctrl & (IC_MC_RX_DISABLE | + IC_MC_TX_DISABLE))); + ic_outl(base, IC_REG_MAC_CTRL, 0x00000000); + + /* Set ic Rx mode */ + ic_mode(0x00); + + /* Set RX max frame size */ + ic_outw(base, IC_REG_MAX_FRAME, IC_RX_BUF_SIZE); + + /* Set some values */ + ic_outb(base, IC_REG_RX_DMA_PERIOD, 0x01); + ic_outb(base, IC_REG_RX_DMA_UTH, 0x30); + ic_outb(base, IC_REG_RX_DMA_BTH, 0x30); + ic_outb(base, IC_REG_TX_DMA_PERIOD, 0x26); + ic_outb(base, IC_REG_TX_DMA_UTH, 0x04); + ic_outb(base, IC_REG_TX_DMA_BTH, 0x30); + ic_outw(base, IC_REG_FLOW_ON_TH, 0x0740); + ic_outw(base, IC_REG_FLOW_OFF_TH, 0x00bf); + + ic_outw(base, IC_REG_DEBUG_CTRL, ic_inw(base, IC_REG_DEBUG_CTRL) | 0x0200); + ic_outw(base, IC_REG_DEBUG_CTRL, ic_inw(base, IC_REG_DEBUG_CTRL) | 0x0010); + ic_outw(base, IC_REG_DEBUG_CTRL, ic_inw(base, IC_REG_DEBUG_CTRL) | 0x0020); + + ic_outl(base, 0x98, 0x0fffffff); + + /* Restore MAC register */ + ic_outl(base, IC_REG_MAC_CTRL, mac_ctrl1); + + /* Check link state */ + ic_check_link(pdev); +} + +/* Check link state */ +static void ic_check_link(ic_driver *pdev) { + u8_t phy_ctrl; + u16_t base = pdev->ic_base_addr; + u32_t mac_ctrl; + char speed[20], duplex[20]; + + phy_ctrl = ic_inb(base, IC_REG_PHY_CTRL); + mac_ctrl = ic_inl(base, IC_REG_MAC_CTRL); + + /* Check link speed */ + switch (phy_ctrl & IC_PC_LINK_SPEED) { + case IC_PC_LINK_SPEED10: + strcpy(speed, "10Mbps"); + pdev->ic_link = IC_LINK_UP; + break; + case IC_PC_LINK_SPEED100: + strcpy(speed, "100Mbps"); + pdev->ic_link = IC_LINK_UP; + break; + case IC_PC_LINK_SPEED1000: + strcpy(speed, "1000Mbps"); + pdev->ic_link = IC_LINK_UP; + break; + default: + strcpy(speed, "unknown"); + pdev->ic_link = IC_LINK_DOWN; + break; + } + + /* Check link duplex */ + if (phy_ctrl & IC_PC_DUPLEX_STS) { + strcpy(duplex, "full"); + mac_ctrl |= (IC_MC_DUPLEX_SEL | IC_MC_TX_FC_ENA | IC_MC_RX_FC_ENA); + } + else + strcpy(duplex, "half"); + +#ifdef IP1000_DEBUG + printf("IP1000: Link speed is %s, %s duplex\n", speed, duplex); +#endif + + ic_outl(base, IC_REG_MAC_CTRL, mac_ctrl); +} + +/* Configure MAC address */ +static void ic_conf_addr(ic_driver *pdev, ether_addr_t *addr) { + u16_t dw, base = pdev->ic_base_addr; + int i; + + /* Read station address from EEPROM */ + for (i = 0; i< 3; i++) + pdev->sta_addr[i] = read_eeprom(pdev, 16 + i); + for (i = 0; i < 3; i++) + ic_outw(base, IC_REG_STA_ADDR0 + i * 2, pdev->sta_addr[i]); + + /* Read MAC address */ + addr->ea_addr[0] = (u8_t)(ic_inw(base, IC_REG_STA_ADDR0) & 0x00ff); + addr->ea_addr[1] = (u8_t)((ic_inw(base, IC_REG_STA_ADDR0) & 0xff00) >> 8); + addr->ea_addr[2] = (u8_t)(ic_inw(base, IC_REG_STA_ADDR1) & 0x00ff); + addr->ea_addr[3] = (u8_t)((ic_inw(base, IC_REG_STA_ADDR1) & 0xff00) >> 8); + addr->ea_addr[4] = (u8_t)(ic_inw(base, IC_REG_STA_ADDR2) & 0x00ff); + addr->ea_addr[5] = (u8_t)((ic_inw(base, IC_REG_STA_ADDR2) & 0xff00) >> 8); + +#ifdef IP1000_DEBUG + printf("IP1000: Ethernet address is %02x:%02x:%02x:%02x:%02x:%02x\n", + addr->ea_addr[0], addr->ea_addr[1], addr->ea_addr[2], + addr->ea_addr[3], addr->ea_addr[4], addr->ea_addr[5]); +#endif +} + +/* Stop the driver */ +static void ic_stop(void) { + u16_t base = g_driver.ic_base_addr; + + /* Free Rx and Tx buffer*/ + free_contig(g_driver.ic_buf, g_driver.ic_buf_size); + + /* Stop IRQ, Rx and Tx */ + ic_outw(base, IC_REG_INTR_ENA, 0x0000); + ic_outl(base, IC_REG_ASIC_CTRL, IC_AC_RESET_ALL); +} + +/* Set driver mode */ +static void ic_mode(unsigned int mode) { + ic_driver *pdev = &g_driver; + u16_t base = pdev->ic_base_addr; + u8_t rm; + + pdev->ic_mode = mode; + ic_outl(base, IC_REG_HASH_TLB0, 0x00000000); + ic_outl(base, IC_REG_HASH_TLB1, 0x00000000); + + rm = ic_inb(base, IC_REG_RX_MODE); + rm &= ~(IC_RM_UNICAST | IC_RM_MULTICAST | IC_RM_BROADCAST | + IC_RM_ALLFRAMES); + if (pdev->ic_mode & NDEV_PROMISC) + rm |= IC_RM_ALLFRAMES; + if (pdev->ic_mode & NDEV_BROAD) + rm |= IC_RM_BROADCAST; + if (pdev->ic_mode & NDEV_MULTI) + rm |= IC_RM_MULTICAST; + rm |= (IC_RM_UNICAST | IC_RM_BROADCAST); + ic_outb(base, IC_REG_RX_MODE, rm); +} + +/* Receive data */ +static ssize_t ic_recv(struct netdriver_data *data, size_t max) { + ic_driver *pdev = &g_driver; + u32_t totlen, packlen; + u64_t rxstat; + ic_desc *desc; + int index, i; + + index = pdev->ic_rx_head; + desc = pdev->ic_rx_desc; + desc += index; + + /* Manage Rx buffer */ + rxstat = desc->status; + + if ((rxstat & IC_RFS_NORMAL) != IC_RFS_NORMAL) + return SUSPEND; + + if (rxstat & IC_RFS_ALL_ERR) { +#ifdef IP1000_DEBUG + printf("IP1000: Rx error: 0x%016llx\n", rxstat); +#endif + if (rxstat & IC_RFS_OVERRUN) { +#ifdef IP1000_DEBUG + printf("IP1000: Rx buffer overflow\n"); +#endif + pdev->ic_stat.ets_fifoOver++; + } + if (rxstat & IC_RFS_ALIGN_ERR) { +#ifdef IP1000_DEBUG + printf("IP1000: Rx frames not align\n"); +#endif + pdev->ic_stat.ets_frameAll++; + } + } + + /* Get data length */ + totlen = (u32_t)(rxstat & IC_RFS_FRAME_LEN); + if (totlen < 8 || totlen > 2 * ETH_MAX_PACK_SIZE) { + printf("IP1000: Bad data length: %d\n", totlen); + panic(NULL); + } + + /* Do not need to substract CRC to get packet */ + packlen = totlen; + + /* Copy data to user */ + netdriver_copyout(data, 0, pdev->ic_rx[index].buf, packlen); + pdev->ic_stat.ets_packetR++; + + /* Set Rx descriptor status */ + desc->status = 0x0000000000000000ULL; + if (index == IC_RX_DESC_NUM - 1) + index = 0; + else + index++; + + pdev->ic_rx_head = index; + +#ifdef IP1000_DEBUG + printf("IP1000: Successfully receive a packet, length = %d\n", packlen); +#endif + + return packlen; +} + +/* Transmit data */ +static int ic_send(struct netdriver_data *data, size_t size) { + ic_driver *pdev = &g_driver; + ic_desc *desc; + int tx_head, i; + u16_t base = pdev->ic_base_addr; + + tx_head = pdev->ic_tx_head; + desc = pdev->ic_tx_desc; + desc += tx_head; + + if (pdev->ic_tx[tx_head].busy) + return SUSPEND; + + /* Copy data from user */ + netdriver_copyin(data, 0, pdev->ic_tx[tx_head].buf, size); + + /* Set busy */ + pdev->ic_tx[tx_head].busy = TRUE; + pdev->ic_tx_busy_num++; + + /* Set Tx descriptor status */ + desc->status = IC_TFS_TFD_DONE; + desc->status |= (u64_t)(IC_TFS_WORD_ALIGN | (IC_TFS_FRAMEID & tx_head) | + (IC_TFS_FRAG_COUNT & (1 << 24))); + desc->status |= IC_TFS_TX_DMA_INDICATE; + desc->frag_info |= IC_TFI_FRAG_LEN & + ((u64_t)((size >= 60 ? size : 60) & 0xffff) << 48); + desc->status &= (u64_t)(~(IC_TFS_TFD_DONE)); + + if (tx_head == IC_TX_DESC_NUM - 1) + tx_head = 0; + else + tx_head++; + pdev->ic_tx_head = tx_head; + + /* Wake up transmit channel */ + ic_outl(base, IC_REG_DMA_CTRL, IC_DC_TX_POLL); + + return 0; +} + +/* Handle Interrupt */ +static void ic_intr(unsigned int mask) { + int s; + + /* Run interrupt handler at driver level */ + ic_handler(&g_driver); + + /* Reenable interrupts for this hook */ + if ((s = sys_irqenable(&g_driver.ic_hook)) != OK) + printf("IP1000: Cannot enable interrupts: %d\n", s); + + /* Perform tasks based on the flagged conditions */ + ic_check_ints(&g_driver); +} + +/* Real handler interrupt */ +static void ic_handler(ic_driver *pdev) { + u16_t intr_status, base = pdev->ic_base_addr; + int flag = 0, tx_head, tx_tail; + ic_desc *desc; + + /* Get interrupt status */ + intr_status = ic_inw(base, IC_REG_INTR_STS_ACK); + + /* Clear interrupt */ + ic_outw(base, IC_REG_INTR_ENA, IC_IR_COMMON); + + /* Check link status */ + if (intr_status & IC_IR_LINK_EVENT) { +#ifdef IP1000_DEBUG + printf("IP1000: Link state change!\n"); +#endif + flag++; + ic_check_link(pdev); + } + + /* Check interrupt error */ + if (intr_status & IC_IR_HOST_ERR) + printf("IP1000: Host error in interrupt: 0x%04x\n", intr_status); + if (intr_status & IC_IR_RFD_END) + printf("IP1000: Rx list end in interrupt: 0x%04x\n", intr_status); + if (intr_status & IC_IR_MAC_CTRL) + printf("IP1000: MAC control frame in interrupt: 0x%04x\n", intr_status); + if (intr_status & IC_IR_RX_EARLY) + printf("IP1000: Rx early in interrupt: 0x%04x\n", intr_status); + if (intr_status & IC_IR_UPDATE_STS) + printf("IP1000: Status update in interrupt: 0x%04x\n", intr_status); + + /* Check interrupt status */ + if (intr_status & IC_IR_RX_DMA_DONE) { + pdev->ic_recv_flag = TRUE; + flag++; + } + if (intr_status & IC_IR_TX_DMA_DONE) { + pdev->ic_send_flag = TRUE; + flag++; + + /* Manage Tx Buffer */ + tx_head = pdev->ic_tx_head; + tx_tail = pdev->ic_tx_tail; + while (tx_tail != tx_head) { + desc = pdev->ic_tx_desc; + desc += tx_tail; + if (!pdev->ic_tx[tx_tail].busy) + printf("IP1000: Strange, buffer not busy?\n"); + + if (!(desc->status & IC_TFS_TFD_DONE)) + break; + + pdev->ic_stat.ets_packetT++; + pdev->ic_tx[tx_tail].busy = FALSE; + pdev->ic_tx_busy_num--; + + if (++tx_tail >= IC_TX_DESC_NUM) + tx_tail = 0; + + pdev->ic_send_flag = TRUE; + pdev->ic_recv_flag = TRUE; + pdev->ic_tx_alive = TRUE; + +#ifdef IP1000_DEBUG + printf("IP1000: Successfully send a packet\n"); +#endif + } + pdev->ic_tx_tail = tx_tail; + } + if (!flag) { +#ifdef IP1000_DEBUG + printf("IP1000: Unknown error in interrupt: 0x%04x\n", intr_status); +#endif + return; + } + + /* Perform tasks based on the flagged condition */ + ic_check_ints(pdev); +} + +/* Check interrupt and perform */ +static void ic_check_ints(ic_driver *pdev) { + if (!pdev->ic_recv_flag) + return; + pdev->ic_recv_flag = FALSE; + + /* Handle data receive */ + netdriver_recv(); + + /* Handle data transmit */ + if (pdev->ic_send_flag) { + pdev->ic_send_flag = FALSE; + netdriver_send(); + } +} + +static void ic_stat(eth_stat_t *stat) { + memcpy(stat, &g_driver.ic_stat, sizeof(*stat)); +} diff --git a/minix/drivers/net/ip1000/ip1000.conf b/minix/drivers/net/ip1000/ip1000.conf new file mode 100755 index 000000000..b9bf36b2a --- /dev/null +++ b/minix/drivers/net/ip1000/ip1000.conf @@ -0,0 +1,16 @@ +service ip1000 +{ + type net; + descr "IC Plus 1000A Ethernet Card"; + system + UMAP # 14 + IRQCTL # 19 + DEVIO # 21 + ; + pci device 13f0:1023; + ipc + SYSTEM pm rs log tty ds vm + pci inet lwip amddev + ; +}; + diff --git a/minix/drivers/net/ip1000/ip1000.h b/minix/drivers/net/ip1000/ip1000.h new file mode 100644 index 000000000..cfbab782d --- /dev/null +++ b/minix/drivers/net/ip1000/ip1000.h @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include + +/* PCI Register */ +#define IC_PCI_VID 0x00 /* Vendor ID */ +#define IC_PCI_DID 0x02 /* Device ID */ +#define IC_PCI_CMD 0x04 /* Command */ +#define IC_PCI_STS 0x06 /* Status */ +#define IC_PCI_RID 0x08 /* Revision ID */ +#define IC_PCI_CC 0x09 /* Class Code */ +#define IC_PCI_CLS 0x0c /* Cache Line Size */ +#define IC_PCI_LT 0x0d /* Latency Timer */ +#define IC_PCI_HT 0x0e /* Header Type */ + +/* Internal Register */ +#define IC_REG_DMA_CTRL 0x00 /* DMA Control */ +#define IC_REG_TX_DESC0 0x10 /* Tx Descriptor Address 0 */ +#define IC_REG_TX_DESC1 0x14 /* Tx Descriptor Address 1 */ +#define IC_REG_TX_DMA_BTH 0x18 /* Tx DMA Burst Threshold */ +#define IC_REG_TX_DMA_UTH 0x19 /* Tx DMA Urgent Threshold */ +#define IC_REG_TX_DMA_PERIOD 0x1a /* Tx DMA Poll Period */ +#define IC_REG_RX_DESC0 0x1c /* Rx Descriptor Address 0 */ +#define IC_REG_RX_DESC1 0x20 /* Rx Descriptor Address 1 */ +#define IC_REG_RX_DMA_BTH 0x24 /* Rx DMA Burst Threshold */ +#define IC_REG_RX_DMA_UTH 0x25 /* Rx DMA Urgent Threshold */ +#define IC_REG_RX_DMA_PERIOD 0x26 /* Rx DMA Poll Period */ +#define IC_REG_DEBUG_CTRL 0x2c /* Debug Control */ +#define IC_REG_ASIC_CTRL 0x30 /* Main Control */ +#define IC_REG_FLOW_OFF_TH 0x3c /* Flow Off Threshold */ +#define IC_REG_FLOW_ON_TH 0x3e /* Flow On Threshold */ +#define IC_REG_EEPROM_DATA 0x48 /* EEPROM Data */ +#define IC_REG_EEPROM_CTRL 0x4a /* EEPROM Control */ +#define IC_REG_INTR_STS_ACK 0x5a /* Interrupt Status Acknowlege */ +#define IC_REG_INTR_ENA 0x5c /* Interrupt Enable */ +#define IC_REG_INTR_STS 0x5e /* Interrupt Status */ +#define IC_REG_TX_STS 0x60 /* Tx Status */ +#define IC_REG_MAC_CTRL 0x6c /* MAC Control */ +#define IC_REG_PHY_SET 0x75 /* Physical Register Set */ +#define IC_REG_PHY_CTRL 0x76 /* Physical Register Control */ +#define IC_REG_STA_ADDR0 0x78 /* Station Address 0 */ +#define IC_REG_STA_ADDR1 0x7a /* Station Address 1 */ +#define IC_REG_STA_ADDR2 0x7c /* Station Address 2 */ +#define IC_REG_MAX_FRAME 0x86 /* Max Frame Size */ +#define IC_REG_RX_MODE 0x88 /* Receive Mode */ +#define IC_REG_HASH_TLB0 0x8c /* Hash Table 0 */ +#define IC_REG_HASH_TLB1 0x90 /* Hash Table 1 */ + +/* Receive Configure Mode */ +#define IC_RM_UNICAST 0x01 +#define IC_RM_MULTICAST 0x02 +#define IC_RM_BROADCAST 0x04 +#define IC_RM_ALLFRAMES 0x08 + +/* Interrupt Enable/Status Command */ +#define IC_IR_IRQ_STS 0x0001 +#define IC_IR_HOST_ERR 0x0002 +#define IC_IR_TX_DONE 0x0004 +#define IC_IR_MAC_CTRL 0x0008 +#define IC_IR_RX_DONE 0x0010 +#define IC_IR_RX_EARLY 0x0020 +#define IC_IR_REQUESTED 0x0040 +#define IC_IR_UPDATE_STS 0x0080 +#define IC_IR_LINK_EVENT 0x0100 +#define IC_IR_TX_DMA_DONE 0x0200 +#define IC_IR_RX_DMA_DONE 0x0400 +#define IC_IR_RFD_END 0x0800 +#define IC_IR_RX_DMA_PRIOR 0x1000 +#define IC_IR_COMMON (IC_IR_HOST_ERR | IC_IR_TX_DMA_DONE | \ + IC_IR_TX_DONE | IC_IR_REQUESTED | \ + IC_IR_UPDATE_STS | IC_IR_LINK_EVENT | \ + IC_IR_RX_DMA_DONE | IC_IR_RX_DONE | \ + IC_IR_RX_DMA_PRIOR) + +/* ASIC Control Register Command */ +#define IC_AC_SPEED10 0x00000010 +#define IC_AC_SPEED100 0x00000020 +#define IC_AC_SPEED1000 0x00000040 +#define IC_AC_LED_MODE 0x00004000 +#define IC_AC_GB_RESET 0x00010000 +#define IC_AC_RX_RESET 0x00020000 +#define IC_AC_TX_RESET 0x00040000 +#define IC_AC_DMA 0x00080000 +#define IC_AC_FIFO 0x00100000 +#define IC_AC_NETWORK 0x00200000 +#define IC_AC_HOST 0x00400000 +#define IC_AC_AUTO_INIT 0x00800000 +#define IC_AC_INTR_IRQ 0x02000000 +#define IC_AC_RESET_BUSY 0x04000000 +#define IC_AC_LED_SPEED 0x08000000 +#define IC_AC_LED_MODE_BIT1 0x20000000 +#define IC_AC_RESET_ALL (IC_AC_GB_RESET | IC_AC_RX_RESET | \ + IC_AC_TX_RESET | IC_AC_DMA | IC_AC_FIFO | \ + IC_AC_NETWORK | IC_AC_HOST | IC_AC_AUTO_INIT) + +/* EEPROM Control Command */ +#define IC_EC_ADDDR 0x83ff +#define IC_EC_OPCODE 0x00ff +#define IC_EC_WRITE 0x0100 +#define IC_EC_READ 0x0200 +#define IC_EC_ERASE 0x0300 +#define IC_EC_BUSY 0x8000 + +/* MAC Control Command */ +#define IC_MC_DUPLEX_SEL 0x00000020 +#define IC_MC_TX_FC_ENA 0x00000080 +#define IC_MC_RX_FC_ENA 0x00000100 +#define IC_MC_STAT_DISABLE 0x00400000 +#define IC_MC_TX_ENABLE 0x01000000 +#define IC_MC_TX_DISABLE 0x02000000 +#define IC_MC_TX_ENABLED 0x04000000 +#define IC_MC_RX_ENABLE 0x08000000 +#define IC_MC_RX_DISABLE 0x10000000 +#define IC_MC_RX_ENABLED 0x20000000 +#define IC_MC_PAUSED 0x40000000 + +/* DMA Control Command */ +#define IC_DC_TX_POLL 0x00001000 + +/* Physical Control Command */ +#define IC_PC_MGMTCLK_LO 0x00 +#define IC_PC_MGMTCLK_HI 0x01 +#define IC_PC_MGMTDATA 0x02 +#define IC_PC_MGMTDIR 0x04 +#define IC_PC_DUPLEX_POLARITY 0x08 +#define IC_PC_DUPLEX_STS 0x10 +#define IC_PC_LINK_POLARITY 0x20 +#define IC_PC_LINK_SPEED 0xc0 +#define IC_PC_LINK_SPEED10 0x40 +#define IC_PC_LINK_SPEED100 0x80 +#define IC_PC_LINK_SPEED1000 0xc0 + +/* Tx Status */ +#define IC_TS_TX_ERR 0x00000001 +#define IC_TS_TX_COLLISION 0x00000004 +#define IC_TS_TX_MAX_COLL 0x00000008 +#define IC_TS_TX_UNDERRUN 0x00000010 +#define IC_TS_TX_IND_REQD 0x00000040 +#define IC_TS_TX_DONE 0x00000080 +#define IC_TS_TX_FRAMEID 0xffff0000 + +/* Tx Frame Status */ +#define IC_TFS_FRAMEID 0x000000000000ffffULL +#define IC_TFS_WORD_ALIGN 0x0000000000030000ULL +#define IC_TFS_TCP_CHECKSUM 0x0000000000040000ULL +#define IC_TFS_UDP_CHECKSUM 0x0000000000080000ULL +#define IC_TFS_IP_CHECKSUM 0x0000000000100000ULL +#define IC_TFS_FRAG_COUNT 0x000000000f000000ULL +#define IC_TFS_WORD_ALIGN 0x0000000000030000ULL +#define IC_TFS_TX_DMA_INDICATE 0x0000000000800000ULL +#define IC_TFS_TFD_DONE 0x0000000080000000ULL + +/* Tx Frame Information */ +#define IC_TFI_FRAG_ADDR 0x000000ffffffffffULL +#define IC_TFI_FRAG_LEN 0xffff000000000000ULL + +/* Rx Frame Status */ +#define IC_RFS_FRAME_LEN 0x000000000000ffffULL +#define IC_RFS_OVERRUN 0x0000000000010000ULL +#define IC_RFS_RUNT 0x0000000000020000ULL +#define IC_RFS_ALIGN_ERR 0x0000000000040000ULL +#define IC_RFS_FCS_ERR 0x0000000000080000ULL +#define IC_RFS_OVERSIZE 0x0000000000100000ULL +#define IC_RFS_LEN_ERR 0x0000000000200000ULL +#define IC_RFS_VLAN_DETECT 0x0000000000400000ULL +#define IC_RFS_TCP_DETECT 0x0000000000800000ULL +#define IC_RFS_TCP_ERR 0x0000000001000000ULL +#define IC_RFS_UCP_DETECT 0x0000000002000000ULL +#define IC_RFS_UCP_ERR 0x0000000004000000ULL +#define IC_RFS_IP_DETECT 0x0000000008000000ULL +#define IC_RFS_IP_ERR 0x0000000010000000ULL +#define IC_RFS_FRAME_START 0x0000000020000000ULL +#define IC_RFS_FRAME_END 0x0000000040000000ULL +#define IC_RFS_RFD_DONE 0x0000000080000000ULL +#define IC_RFS_TCI 0x0000ffff00000000ULL +#define IC_RFS_NORMAL (IC_RFS_RFD_DONE | IC_RFS_FRAME_START | \ + IC_RFS_FRAME_END) +#define IC_RFS_ALL_ERR (IC_RFS_OVERRUN | IC_RFS_RUNT | \ + IC_RFS_ALIGN_ERR | IC_RFS_FCS_ERR | \ + IC_RFS_OVERSIZE | IC_RFS_LEN_ERR) + +/* Rx Frame Information */ +#define IC_RFI_FRAG_ADDR 0x000000ffffffffffULL +#define IC_RFI_FRAG_LEN 0xffff000000000000ULL + +/* Link Status */ +#define IC_LINK_UP 1 +#define IC_LINK_DOWN 0 +#define IC_LINK_UNKNOWN -1 + +#define IC_RX_BUF_SIZE 1536 +#define IC_TX_BUF_SIZE 1536 +#define IC_RX_DESC_NUM 256 +#define IC_TX_DESC_NUM 256 + +/* #define IP1000_DEBUG */ + +/* Physical parameters */ +static const u16_t DefaultPhyParam[] = { + (0x4000 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31, + 0x0000, 30, 0x005e, 9, 0x0700, + (0x4100 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31, + 0x0000, 30, 0x005e, 9, 0x0700, 0x0000 +}; + +/* Data Descriptor */ +typedef struct ic_desc { + u64_t next_desc; /* Next descriptor */ + u64_t status; /* Status information */ + u64_t frag_info; /* Length and DMA buffer address */ +} ic_desc; + +/* Driver Data Structure */ +typedef struct ic_driver { + u16_t ic_base_addr; /* Base address */ + int ic_revision; /* Revision ID */ + int ic_irq; /* IRQ number */ + int ic_mode; + int ic_link; /* Whether link-up */ + int ic_recv_flag; /* Receive flag */ + int ic_send_flag; /* Send flag */ + int ic_tx_alive; + int ic_tx_busy; + + /* LED mode read from EEPROM */ + u16_t ic_led_mode; + + /* Station address read from EEPROM */ + u16_t sta_addr[3]; + + /* Buffer */ + size_t ic_buf_size; + char *ic_buf; + + /* Rx data */ + int ic_rx_head; + struct { + phys_bytes buf_dma; + char *buf; + } ic_rx[IC_RX_DESC_NUM]; + ic_desc *ic_rx_desc; /* Rx descriptor buffer */ + phys_bytes ic_rx_desc_dma; /* Rx descriptor DMA buffer */ + + /* Tx data */ + int ic_tx_head; + int ic_tx_tail; + struct { + int busy; + phys_bytes buf_dma; + char *buf; + } ic_tx[IC_TX_DESC_NUM]; + ic_desc *ic_tx_desc; /* Tx descriptor buffer */ + phys_bytes ic_tx_desc_dma; /* Tx descriptor DMA buffer */ + int ic_tx_busy_num; /* Number of busy Tx descriptors */ + + int ic_hook; /* IRQ hook id at kernel */ + eth_stat_t ic_stat; + + char ic_name[20]; +} ic_driver;