From 022136b354f57076feaa8f0ae820cd578efdbad0 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Wed, 19 Oct 2016 10:11:26 +0000 Subject: [PATCH] Add the driver for VIA Technology 6105/6106S Ethernet card Change-Id: I690c34f0a37bcbb20a5b7748e3dd315707460caf --- distrib/sets/lists/minix-base/md.i386 | 2 + distrib/sets/lists/minix-debug/md.i386 | 1 + minix/drivers/net/Makefile | 1 + minix/drivers/net/vt6105/Makefile | 14 + minix/drivers/net/vt6105/vt6105.c | 807 +++++++++++++++++++++++++ minix/drivers/net/vt6105/vt6105.conf | 15 + minix/drivers/net/vt6105/vt6105.h | 194 ++++++ minix/fs/procfs/service.c | 1 + 8 files changed, 1035 insertions(+) create mode 100755 minix/drivers/net/vt6105/Makefile create mode 100644 minix/drivers/net/vt6105/vt6105.c create mode 100755 minix/drivers/net/vt6105/vt6105.conf create mode 100644 minix/drivers/net/vt6105/vt6105.h diff --git a/distrib/sets/lists/minix-base/md.i386 b/distrib/sets/lists/minix-base/md.i386 index 3280f85f5..52aa190aa 100644 --- a/distrib/sets/lists/minix-base/md.i386 +++ b/distrib/sets/lists/minix-base/md.i386 @@ -23,6 +23,7 @@ ./etc/system.conf.d/vbfs minix-base ./etc/system.conf.d/vbox minix-base ./etc/system.conf.d/virtio_net minix-base +./etc/system.conf.d/vt6105 minix-base ./service/3c90x minix-base ./service/acpi minix-base ./service/ahci minix-base @@ -53,6 +54,7 @@ ./service/vbox minix-base ./service/virtio_blk minix-base ./service/virtio_net minix-base +./service/vt6105 minix-base ./usr/lib/libhgfs.a minix-base ./usr/lib/libhgfs_pic.a minix-base ./usr/lib/libm387.a minix-base diff --git a/distrib/sets/lists/minix-debug/md.i386 b/distrib/sets/lists/minix-debug/md.i386 index 377a58e2c..ee66c819e 100644 --- a/distrib/sets/lists/minix-debug/md.i386 +++ b/distrib/sets/lists/minix-debug/md.i386 @@ -39,6 +39,7 @@ ./usr/libdata/debug/service/vbox.debug minix-debug debug ./usr/libdata/debug/service/virtio_blk.debug minix-debug debug ./usr/libdata/debug/service/virtio_net.debug minix-debug debug +./usr/libdata/debug/service/vt6105.debug minix-debug debug ./usr/libdata/debug/usr/lib/libm387.so.0.1.debug minix-debug debug ./usr/libdata/debug/usr/tests/minix-posix/test47.debug minix-debug debug ./usr/libdata/debug/usr/tests/minix-posix/test51.debug minix-debug debug diff --git a/minix/drivers/net/Makefile b/minix/drivers/net/Makefile index 51cf094f0..b8c4e479b 100644 --- a/minix/drivers/net/Makefile +++ b/minix/drivers/net/Makefile @@ -12,6 +12,7 @@ SUBDIR+= lance SUBDIR+= rtl8139 SUBDIR+= rtl8169 SUBDIR+= virtio_net +SUBDIR+= vt6105 .endif # ${MACHINE_ARCH} == "i386" .if ${MACHINE_ARCH} == "earm" diff --git a/minix/drivers/net/vt6105/Makefile b/minix/drivers/net/vt6105/Makefile new file mode 100755 index 000000000..8ac42452f --- /dev/null +++ b/minix/drivers/net/vt6105/Makefile @@ -0,0 +1,14 @@ +# Makefile for the VIA Technology 6105/6106S Ethernet driver (vt6105) +PROG= vt6105 +SRCS= vt6105.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/vt6105/vt6105.c b/minix/drivers/net/vt6105/vt6105.c new file mode 100644 index 000000000..92b628977 --- /dev/null +++ b/minix/drivers/net/vt6105/vt6105.c @@ -0,0 +1,807 @@ +#include "vt6105.h" + +/* global value */ +static vt_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("VT6105: sys_inb failed: %d\n", r); + return (u8_t)value; +} +#define vt_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("VT6105: sys_inw failed: %d\n", r); + return (u16_t)value; +} +#define vt_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("VT6105: sys_inl failed: %d\n", r); + return value; +} +#define vt_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("VT6105: sys_outb failed: %d\n", r); +} +#define vt_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("VT6105: sys_outw failed: %d\n", r); +} +#define vt_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("VT6105: sys_outl failed: %d\n", r); +} +#define vt_outl(port, offset, value) (my_outl((port) + (offset), (value))) + +/* driver interface */ +static int vt_init(unsigned int instance, ether_addr_t *addr); +static void vt_stop(void); +static void vt_mode(unsigned int mode); +static ssize_t vt_recv(struct netdriver_data *data, size_t max); +static int vt_send(struct netdriver_data *data, size_t size); +static void vt_intr(unsigned int mask); +static void vt_stat(eth_stat_t *stat); +static void vt_alarm(clock_t stamp); + +static int vt_probe(vt_driver *pdev, int instance); +static int vt_init_buf(vt_driver *pdev); +static int vt_init_hw(vt_driver *pdev, ether_addr_t *addr); +static void vt_reset_hw(vt_driver *pdev); +static void vt_power_init(vt_driver *pdev); +static void vt_conf_addr(vt_driver *pdev, ether_addr_t *addr); +static void vt_check_link(vt_driver *pdev); +static void vt_handler(vt_driver *pdev); +static void vt_check_ints(vt_driver *pdev); + +static const struct netdriver vt_table = { + .ndr_init = vt_init, + .ndr_stop = vt_stop, + .ndr_mode = vt_mode, + .ndr_recv = vt_recv, + .ndr_send = vt_send, + .ndr_stat = vt_stat, + .ndr_intr = vt_intr, + .ndr_alarm = vt_alarm +}; + +int main(int argc, char *argv[]) { + env_setargs(argc, argv); + netdriver_task(&vt_table); +} + +/* Initialize the driver */ +static int vt_init(unsigned int instance, ether_addr_t *addr) { + int ret = 0; + + /* Intialize driver data structure */ + memset(&g_driver, 0, sizeof(g_driver)); + g_driver.vt_link = VT_LINK_UNKNOWN; + strcpy(g_driver.vt_name, "vt6105#0"); + g_driver.vt_name[7] += instance; + g_instance = instance; + + /* Probe the device */ + if (vt_probe(&g_driver, instance)) { + printf("VT6105: Device is not found!\n"); + ret = -ENODEV; + goto err_probe; + } + + /* Allocate and initialize buffer */ + if (vt_init_buf(&g_driver)) { + printf("VT6105: Device is not found!\n"); + ret = -ENODEV; + goto err_init_buf; + } + + /* Intialize hardware */ + if (vt_init_hw(&g_driver, addr)) { + printf("VT6105: 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.vt_send_flag = FALSE; + g_driver.vt_recv_flag = FALSE; + + return 0; + +err_init_hw: + free_contig(g_driver.vt_buf, g_driver.vt_buf_size); +err_init_buf: +err_probe: + return ret; +} + +/* Match the device and get base address */ +static int vt_probe(vt_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("VT6105: base address is not properly configured!\n"); + return -EIO; + } + pdev->vt_base_addr = bar; + + /* Get irq number */ + irq = pci_attr_r8(devind, PCI_ILR); + pdev->vt_irq = irq; + + /* Get revision ID */ + rev = pci_attr_r8(devind, PCI_REV); + pdev->vt_revision = rev; + +#ifdef VT6105_DEBUG + printf("VT6105: Base address is 0x%08x\n", pdev->vt_base_addr); + printf("VT6105: IRQ number is 0x%08x\n", pdev->vt_irq); + printf("VT6105: Revision ID is 0x%08x\n", pdev->vt_revision); +#endif + + return 0; +} + +/* Allocate and initialize buffer */ +static int vt_init_buf(vt_driver *pdev) { + size_t rx_desc_size, tx_desc_size, rx_buf_size, tx_buf_size, tot_buf_size; + vt_desc *desc; + phys_bytes buf_dma, next; + char *buf; + int i; + + /* Build Rx and Tx descriptor buffer */ + rx_desc_size = VT_RX_DESC_NUM * sizeof(vt_desc); + tx_desc_size = VT_TX_DESC_NUM * sizeof(vt_desc); + + /* Allocate Rx and Tx buffer */ + tx_buf_size = VT_TX_BUF_SIZE; + if (tx_buf_size % 4) + tx_buf_size += 4 - (tx_buf_size % 4); + rx_buf_size = VT_RX_BUF_SIZE; + tot_buf_size = rx_desc_size + tx_desc_size; + tot_buf_size += VT_TX_DESC_NUM * tx_buf_size + VT_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("VT6105: Fail to allocate memory!\n"); + return -ENOMEM; + } + pdev->vt_buf_size = tot_buf_size; + pdev->vt_buf = buf; + + /* Rx descriptor */ + pdev->vt_rx_desc = (vt_desc *)buf; + pdev->vt_rx_desc_dma = buf_dma; + memset(buf, 0, rx_desc_size); + buf += rx_desc_size; + buf_dma += rx_desc_size; + + /* Tx descriptor */ + pdev->vt_tx_desc = (vt_desc *)buf; + pdev->vt_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->vt_rx_desc; + next = pdev->vt_rx_desc_dma; + for (i = 0; i < VT_RX_DESC_NUM; i++) { + /* Set Rx buffer */ + pdev->vt_rx[i].buf_dma = buf_dma; + pdev->vt_rx[i].buf = buf; + buf_dma += rx_buf_size; + buf += rx_buf_size; + + /* Set Rx descriptor */ + desc->status = VT_DESC_OWN | + ((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK); + desc->addr = pdev->vt_rx[i].buf_dma; + desc->length = VT_RX_BUF_SIZE; + if (i == (VT_RX_DESC_NUM - 1)) + desc->next_desc = pdev->vt_rx_desc_dma; + else { + next += sizeof(vt_desc); + desc->next_desc = next; + desc++; + } + } + + /* Tx buffer assignment */ + desc = pdev->vt_tx_desc; + next = pdev->vt_tx_desc_dma; + for (i = 0; i < VT_TX_DESC_NUM; i++) { + /* Set Tx buffer */ + pdev->vt_tx[i].busy = 0; + pdev->vt_tx[i].buf_dma = buf_dma; + pdev->vt_tx[i].buf = buf; + buf_dma += tx_buf_size; + buf += tx_buf_size; + + /* Set Rx descriptor */ + desc->addr = pdev->vt_tx[i].buf_dma; + desc->length = VT_TX_BUF_SIZE; + if (i == (VT_TX_DESC_NUM - 1)) + desc->next_desc = pdev->vt_tx_desc_dma; + else { + next += sizeof(vt_desc); + desc->next_desc = next; + desc++; + } + } + pdev->vt_tx_busy_num = 0; + pdev->vt_tx_head = 0; + pdev->vt_tx_tail = 0; + pdev->vt_rx_head = 0; + pdev->vt_tx_alive = FALSE; + + return 0; +} + +/* Intialize hardware */ +static int vt_init_hw(vt_driver *pdev, ether_addr_t *addr) { + int r, ret; + + /* Set the interrupt handler */ + pdev->vt_hook = pdev->vt_irq; + if ((r = sys_irqsetpolicy(pdev->vt_irq, 0, &pdev->vt_hook)) != OK) { + printf("VT6105: Fail to set IRQ policy: %d!\n", r); + ret = -EFAULT; + goto err_irq_policy; + } + + /* Reset hardware */ + vt_reset_hw(pdev); + + /* Enable IRQ */ + if ((r = sys_irqenable(&pdev->vt_hook)) != OK) { + printf("VT6105: Fail to enable IRQ: %d!\n", r); + ret = -EFAULT; + goto err_irq_enable; + } + + /* Configure MAC address */ + vt_conf_addr(pdev, addr); + + /* Detect link status */ + vt_check_link(pdev); +#ifdef VT6105_DEBUG + if (pdev->vt_link) + printf("VT6105: Link up!\n"); + else + printf("VT6105: Link down!\n"); +#endif + + return 0; + +err_irq_enable: +err_irq_policy: + return ret; +} + +/* Reset hardware */ +static void vt_reset_hw(vt_driver *pdev) { + u16_t base = pdev->vt_base_addr; + u8_t db; + u16_t dw; + u32_t dl; + + /* Let power registers into sane state */ + vt_power_init(pdev); + + /* Reset the chip */ + vt_outw(base, VT_REG_CR, VT_CMD_RESET); + vt_inb(base, VT_REG_ADDR); + +#ifdef VT6105_DEBUG + if (vt_inw(base, VT_REG_CR) & VT_CMD_RESET) { + vt_outb(base, VT_REG_MCR1, 0x40); + usleep(100000); + if (vt_inw(base, VT_REG_CR) & VT_CMD_RESET) + printf("VT6105: Fail to completely reset!\n"); + } + else + printf("VT6105: Reset successfully!\n"); +#endif + + /* Initialize regsiters */ + vt_outw(base, VT_REG_BCR0, 0x0006); + vt_outb(base, VT_REG_TCR, 0x20); /* Tx threshold is 256 bytes */ + vt_outb(base, VT_REG_RCR, 0x78); /* Rx threshold is 256 bytes */ + vt_outl(base, VT_REG_RD_BASE, pdev->vt_rx_desc_dma); /* Set Rx descriptor base address */ + vt_outl(base, VT_REG_TD_BASE, pdev->vt_tx_desc_dma); /* Set Tx descriptor base address */ + +#ifdef VT6105_DEBUG + dl = vt_inl(base, VT_REG_RD_BASE); + printf("VT6105: Rx descriptor DMA address is: 0x%08x\n", dl); + dl = vt_inl(base, VT_REG_TD_BASE); + printf("VT6105: Tx descriptor DMA address is: 0x%08x\n", dl); +#endif + + /* Enable interrupts */ + vt_outw(base, VT_REG_IMR, 0xfeff); + + /* Start the device, Rx and Tx */ + dw = VT_CMD_START | VT_CMD_RX_ON | VT_CMD_TX_ON | VT_CMD_NO_POLL | VT_CMD_FDUPLEX; + vt_outw(base, VT_REG_CR, dw); + dw = vt_inw(base, VT_REG_CR); + +#ifdef VT6105_DEBUG + dw = vt_inw(base, VT_REG_CR); + printf("VT6105: Control status is 0x%04x\n", dw); +#endif +} + +/* Initialize power registers */ +static void vt_power_init(vt_driver *pdev) { + u8_t db, wolstat; + u16_t dw, base = pdev->vt_base_addr; + u32_t dl; + + /* Make sure chip is in power state D0 */ + db = vt_inb(base, VT_REG_STICK); + vt_outb(base, VT_REG_STICK, db & 0xfc); + + /* Disable "force PME-enable" */ + vt_outb(base, VT_REG_WOLC_SET, 0x80); + + /* Clear power-event config bits (WOL) */ + vt_outb(base, VT_REG_WOL_SET, 0xff); + + /* Save power-event status bits */ + wolstat = vt_inb(base, VT_REG_WOLS_SET); + + /* Clear power-event status bits */ + vt_outb(base, VT_REG_WOLS_CLR, 0xff); + +#ifdef VT6105_DEBUG + if (wolstat) { + char reason[50]; + switch (wolstat) { + case VT_WOL_MAGIC: + strcpy(reason, "Magic packet"); + break; + case VT_WOL_LINK_UP: + strcpy(reason, "Link up"); + break; + case VT_WOL_LINK_DOWN: + strcpy(reason, "Link down"); + break; + case VT_WOL_UCAST: + strcpy(reason, "Unicast packet"); + break; + case VT_WOL_MCAST: + strcpy(reason, "Multicast/Broadcast packet"); + break; + default: + strcpy(reason, "Unknown"); + } + printf("VT6105: Wake system up: %s\n", reason); + } +#endif +} + +static void vt_conf_addr(vt_driver *pdev, ether_addr_t *addr) { + u16_t dw, base = pdev->vt_base_addr; + int i; + + for (i=0; i<6; i++) + addr->ea_addr[i] = vt_inb(base, VT_REG_ADDR + i); + +#ifdef VT6105_DEBUG + printf("VT6105: 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 +} + +/* Detect link status (MII interface) */ +static void vt_check_link(vt_driver *pdev) { + u16_t base = pdev->vt_base_addr; + u32_t res; + + vt_outb(base, VT_REG_MII_CFG, 0x01); + vt_outb(base, VT_REG_MII_ADDR, 0x01); + vt_outb(base, VT_REG_MII_CR, 0x40); + usleep(10000); + res = vt_inw(base, VT_REG_MII_DATA); + if (res & 0x0004) + pdev->vt_link = TRUE; + else + pdev->vt_link = FALSE; +} + +/* Stop the driver */ +static void vt_stop(void) { + u16_t base = g_driver.vt_base_addr; + + /* Free Rx and Tx buffer*/ + free_contig(g_driver.vt_buf, g_driver.vt_buf_size); + + /* Stop IRQ and device */ + vt_outw(base, VT_REG_IMR, 0x0000); + vt_outw(base, VT_REG_CR, VT_CMD_STOP); +} + +/* Set driver mode */ +static void vt_mode(unsigned int mode) { + vt_driver *pdev = &g_driver; + u16_t base = pdev->vt_base_addr; + u8_t rcr; + + pdev->vt_mode = mode; + vt_outl(base, VT_REG_MAR0, 0xffffffff); + vt_outl(base, VT_REG_MAR1, 0xffffffff); + + rcr = vt_inb(base, VT_REG_RCR); + rcr &= ~(VT_RCR_AB | VT_RCR_AM | VT_RCR_AP | VT_RCR_AE | VT_RCR_AR); + if (pdev->vt_mode & NDEV_PROMISC) + rcr |= VT_RCR_AB | VT_RCR_AM | VT_RCR_AE | VT_RCR_AR; + if (pdev->vt_mode & NDEV_BROAD) + rcr |= VT_RCR_AB; + if (pdev->vt_mode & NDEV_MULTI) + rcr |= VT_RCR_AM; + rcr |= VT_RCR_AP; + vt_outb(base, VT_REG_RCR, 0x60 | rcr); +} + +/* Receive data */ +static ssize_t vt_recv(struct netdriver_data *data, size_t max) { + vt_driver *pdev = &g_driver; + u32_t rxstat, totlen, packlen; + vt_desc *desc; + int index, i; + + index = pdev->vt_rx_head; + desc = pdev->vt_rx_desc; + desc += index; + + /* Manage Rx buffer */ + for (;;) { + rxstat = desc->status; + + if (rxstat & VT_DESC_OWN) + return SUSPEND; + + if (rxstat & VT_DESC_RX_ALL_ERR) { +#ifdef VT6105_DEBUG + printf("VT6105: Rx error: 0x%08x\n", rxstat); +#endif + if (rxstat & VT_DESC_RX_FOV) { +#ifdef VT6105_DEBUG + printf("VT6105: Rx buffer overflow\n"); +#endif + pdev->vt_stat.ets_fifoOver++; + } + if (rxstat & VT_DESC_RX_FAE) { +#ifdef VT6105_DEBUG + printf("VT6105: Rx frames not align\n"); +#endif + pdev->vt_stat.ets_frameAll++; + } + if (rxstat & VT_DESC_RX_CRCE) { +#ifdef VT6105_DEBUG + printf("VT6105: Rx CRC error\n"); +#endif + pdev->vt_stat.ets_CRCerr++; + } + } + + /* Normal packet */ + if ((rxstat & VT_DESC_RX_PACK) == VT_DESC_RX_PACK) + break; + + /* Other packet */ + if (index == VT_RX_DESC_NUM - 1) { + desc->status = VT_DESC_OWN | + ((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK); + index = 0; + desc = pdev->vt_rx_desc; + } + else { + desc->status = VT_DESC_OWN | + ((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK); + index++; + desc++; + } + } + + /* Get data length */ + totlen = (rxstat & VT_DESC_RX_LENMASK) >> 16; + if (totlen < 8 || totlen > 2 * ETH_MAX_PACK_SIZE) { + printf("VT6105: Bad data length: %d\n", totlen); + panic(NULL); + } + + /* Substract CRC to get packet */ + packlen = totlen - ETH_CRC_SIZE; + if (packlen > max) + packlen = max; + + /* Copy data to user */ + netdriver_copyout(data, 0, pdev->vt_rx[index].buf, packlen); + pdev->vt_stat.ets_packetR++; + + /* Set Rx descriptor status */ + if (index == VT_RX_DESC_NUM - 1) { + desc->status = VT_DESC_OWN | + ((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK); + index = 0; + } + else { + desc->status = VT_DESC_OWN | + ((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK); + index++; + } + pdev->vt_rx_head = index; + +#ifdef VT6105_DEBUG + printf("VT6105: Successfully receive a packet, length = %d\n", packlen); +#endif + + return packlen; +} + +/* Transmit data */ +static int vt_send(struct netdriver_data *data, size_t size) { + vt_driver *pdev = &g_driver; + vt_desc *desc; + int tx_head, i; + u8_t db; + u16_t base = pdev->vt_base_addr; + + tx_head = pdev->vt_tx_head; + desc = pdev->vt_tx_desc; + desc += tx_head; + + if (pdev->vt_tx[tx_head].busy) + return SUSPEND; + + /* Copy data from user */ + netdriver_copyin(data, 0, pdev->vt_tx[tx_head].buf, size); + + /* Set busy */ + pdev->vt_tx[tx_head].busy = TRUE; + pdev->vt_tx_busy_num++; + + /* Set Tx descriptor status */ + desc->status = VT_DESC_OWN | VT_DESC_FIRST | VT_DESC_LAST; + desc->length = 0x00e08000 | (size >= 60 ? size : 60); + if (tx_head == VT_TX_DESC_NUM - 1) + tx_head = 0; + else + tx_head++; + pdev->vt_tx_head = tx_head; + + /* Wake up transmit channel */ + db = vt_inb(base, VT_REG_CR); + db |= VT_CMD_TX_DEMAND; + vt_outb(base, VT_REG_CR, db); + + return 0; +} + +/* Handle Interrupt */ +static void vt_intr(unsigned int mask) { + int s; + + /* Run interrupt handler at driver level */ + vt_handler(&g_driver); + + /* Reenable interrupts for this hook */ + if ((s = sys_irqenable(&g_driver.vt_hook)) != OK) + printf("VT6105: Cannot enable interrupts: %d\n", s); + + /* Perform tasks based on the flagged conditions */ + vt_check_ints(&g_driver); +} + +/* Real handler interrupt */ +static void vt_handler(vt_driver *pdev) { + u16_t base = pdev->vt_base_addr; + int intr_status; + u8_t db; + u16_t dw; + u32_t dl, txstat; + int flag = 0, tx_head, tx_tail; + vt_desc *desc; + + /* Get interrupt status */ + intr_status = vt_inw(base, VT_REG_ISR); + + /* Clear interrupt */ + dw = intr_status & ~(VT_INTR_PCI_ERR | VT_INTR_PORT_CHANGE); + vt_outw(base, VT_REG_ISR, dw); + + /* Check link status */ + if (intr_status & VT_INTR_PORT_CHANGE) + printf("VT6105: Port state change!\n"); + + /* Check interrupt status */ + if (intr_status & VT_INTR_RX_DONE) { + pdev->vt_recv_flag = TRUE; + flag++; + + if (intr_status & VT_INTR_RX_ERR) { + pdev->vt_stat.ets_recvErr++; + printf("VT6105: Rx error in interrupt: 0x%04x\n", intr_status); + } + if (intr_status & VT_INTR_RX_NOBUF) + printf("VT6105: Rx no buffer in interrupt: 0x%04x\n", intr_status); + if (intr_status & VT_INTR_RX_EMPTY) + printf("VT6105: Rx buffer empty in interrupt: 0x%04x\n", intr_status); + if (intr_status & VT_INTR_RX_OVERFLOW) + printf("VT6105: Rx buffer overflow in interrupt: 0x%04x\n", intr_status); + if (intr_status & VT_INTR_RX_DROPPED) + printf("VT6105: Rx buffer dropped in interrupt: 0x%04x\n", intr_status); + } + if (intr_status & VT_INTR_TX_DONE) { + pdev->vt_send_flag = TRUE; + flag++; + + if (intr_status & VT_INTR_TX_ERR) { + pdev->vt_stat.ets_sendErr++; + printf("VT6105: Tx error in interrupt: 0x%04x\n", intr_status); + } + if (intr_status & VT_INTR_TX_UNDER_RUN) + printf("VT6105: Tx buffer underflow in interrupt: 0x%04x\n", intr_status); + if (intr_status & VT_INTR_TX_ABORT) + printf("VT6105: Tx abort in interrupt: 0x%04x\n", intr_status); + + /* Manage Tx Buffer */ + tx_head = pdev->vt_tx_head; + tx_tail = pdev->vt_tx_tail; + while (tx_tail != tx_head) { + desc = pdev->vt_tx_desc; + desc += tx_tail; + if (!pdev->vt_tx[tx_tail].busy) + printf("VT6105: Strange, buffer not busy?\n"); + txstat = desc->status; + + /* Check whether the buffer is ready */ + if (txstat & VT_DESC_OWN) { + break; + } + + if (txstat & VT_DESC_TX_ERR) { +#ifdef VT6105_DEBUG + printf("VT6105: Tx error: 0x%08x\n", txstat); +#endif + if (txstat & VT_DESC_TX_UDF) { +#ifdef VT6105_DEBUG + printf("VT6105: Tx buffer underflow\n"); +#endif + pdev->vt_stat.ets_fifoUnder++; + } + if (txstat & VT_DESC_TX_CRS) { +#ifdef VT6105_DEBUG + printf("VT6105: Tx carrier sense lost\n"); +#endif + pdev->vt_stat.ets_carrSense++; + } + if (txstat & VT_DESC_TX_CDH) { +#ifdef VT6105_DEBUG + printf("VT6105: Tx collision detect\n"); +#endif + pdev->vt_stat.ets_collision++; + } + if (txstat & VT_DESC_TX_OWC) { +#ifdef VT6105_DEBUG + printf("VT6105: Tx buffer out of winidow\n"); +#endif + pdev->vt_stat.ets_OWC++; + } + } + + pdev->vt_stat.ets_packetT++; + pdev->vt_tx[tx_tail].busy = FALSE; + pdev->vt_tx_busy_num--; + + if (++tx_tail >= VT_TX_DESC_NUM) + tx_tail = 0; + + pdev->vt_send_flag = TRUE; + pdev->vt_recv_flag = TRUE; + pdev->vt_tx_alive = TRUE; + +#ifdef VT6105_DEBUG + printf("VT6105: Successfully send a packet\n"); +#endif + } + pdev->vt_tx_tail = tx_tail; + } + if (!flag) { + printf("VT6105: Unknown error in interrupt: 0x%04x\n", intr_status); + return; + } + + /* Perform tasks based on the flagged condition */ + vt_check_ints(pdev); +} + +/* Check interrupt and perform */ +static void vt_check_ints(vt_driver *pdev) { + if (!pdev->vt_recv_flag) + return; + pdev->vt_recv_flag = FALSE; + + /* Handle data receive */ + netdriver_recv(); + + /* Handle data transmit */ + if (pdev->vt_send_flag) { + pdev->vt_send_flag = FALSE; + netdriver_send(); + } +} + +static void vt_stat(eth_stat_t *stat) { + memcpy(stat, &g_driver.vt_stat, sizeof(*stat)); +} + +static void vt_alarm(clock_t stamp) { + vt_driver *pdev = &g_driver; + + /* Use a synchronous alarm instead of a watchdog timer */ + sys_setalarm(sys_hz(), 0); + + /* Assume the an idle system is alive */ + if (!pdev->vt_tx_busy_num) { + pdev->vt_tx_alive = TRUE; + return; + } + if (pdev->vt_tx_alive) { + pdev->vt_tx_alive = FALSE; + return; + } + + printf("VT6105: Resetting the driver\n"); + vt_reset_hw(pdev); + pdev->vt_recv_flag = TRUE; + + vt_check_ints(pdev); +} diff --git a/minix/drivers/net/vt6105/vt6105.conf b/minix/drivers/net/vt6105/vt6105.conf new file mode 100755 index 000000000..887aa4257 --- /dev/null +++ b/minix/drivers/net/vt6105/vt6105.conf @@ -0,0 +1,15 @@ +service vt6105 +{ + type net; + descr "VIA Technology 6105/6106S Ethernet Card"; + system + UMAP # 14 + IRQCTL # 19 + DEVIO # 21 + ; + pci device 1106:3106; + ipc + SYSTEM pm rs log tty ds vm + pci inet lwip amddev + ; +}; diff --git a/minix/drivers/net/vt6105/vt6105.h b/minix/drivers/net/vt6105/vt6105.h new file mode 100644 index 000000000..51c842724 --- /dev/null +++ b/minix/drivers/net/vt6105/vt6105.h @@ -0,0 +1,194 @@ +#include +#include +#include +#include +#include + +/* PCI Register */ +#define VT_PCI_VID 0x00 /* Vendor ID */ +#define VT_PCI_DID 0x02 /* Device ID */ +#define VT_PCI_CMD 0x04 /* Command */ +#define VT_PCI_STS 0x06 /* Status */ +#define VT_PCI_RID 0x08 /* Revision ID */ +#define VT_PCI_CC 0x09 /* Class Code */ +#define VT_PCI_CLS 0x0c /* Cache Line Size */ +#define VT_PCI_LT 0x0d /* Latency Timer */ +#define VT_PCI_HT 0x0e /* Header Type */ + +/* Internal Register */ +#define VT_REG_ADDR 0x00 /* Ethernet Address */ +#define VT_REG_RCR 0x06 /* Receive Configure Request */ +#define VT_REG_TCR 0x07 /* Transmit Configure Request */ +#define VT_REG_CR 0x08 /* Control 0 */ +#define VT_REG_ISR 0x0c /* Interrupt Status */ +#define VT_REG_IMR 0x0e /* Interrupt Mask */ +#define VT_REG_MAR0 0x10 /* Multicast Address 0 */ +#define VT_REG_MAR1 0x14 /* Multicast Address 1 */ +#define VT_REG_RD_BASE 0x18 /* Receive Descriptor Base Address */ +#define VT_REG_TD_BASE 0x1c /* Transmit Descriptor Base Address */ +#define VT_REG_MII_CFG 0x6c /* MII Configuration */ +#define VT_REG_MII_STS 0x6d /* MII Status */ +#define VT_REG_BCR0 0x6e /* Bus Control 0 */ +#define VT_REG_BCR1 0x6f /* Bus Control 1 */ +#define VT_REG_MII_CR 0x70 /* MII Control */ +#define VT_REG_MII_ADDR 0x71 /* MII Interface Address*/ +#define VT_REG_MII_DATA 0x72 /* MII Data Port */ +#define VT_REG_EECSR 0x74 /* EEPROM Control/Status */ +#define VT_REG_CFG_A 0x78 /* Chip Configuration A */ +#define VT_REG_CFG_B 0x79 /* Chip Configuration B */ +#define VT_REG_CFG_C 0x7a /* Chip Configuration C */ +#define VT_REG_CFG_D 0x7b /* Chip Configuration D */ +#define VT_REG_MCR0 0x80 /* Miscellaneous Control 0 */ +#define VT_REG_MCR1 0x81 /* Miscellaneous Control 1 */ +#define VT_REG_STICK 0x83 /* Sticky Hardware Control */ +#define VT_REG_MISR 0x84 /* MII Interrupt Status */ +#define VT_REG_MIMR 0x86 /* MII Interrupt Mask */ +#define VT_REG_FCR0 0x98 /* Flow Control 0 */ +#define VT_REG_FCR1 0x99 /* Flow Control 1 */ +#define VT_REG_WOL_SET 0xA4 /* Wake On LAN Set */ +#define VT_REG_WOL_CLR 0xA0 /* Wake On LAN Clear */ +#define VT_REG_PCS_CLR 0xA5 /* Power Configuration Set */ +#define VT_REG_PCS_SET 0xA1 /* Power Configuration Clear */ +#define VT_REG_WOLC_SET 0xA7 /* WOL Configuration Set */ +#define VT_REG_WOLC_CLR 0xA3 /* WOL Configuration Clear */ +#define VT_REG_WOLS_SET 0xA8 /* WOL Status Clear */ +#define VT_REG_WOLS_CLR 0xAC /* WOL Status Set */ + +/* Interrupt Status/Mask Register */ +#define VT_INTR_RX_DONE 0x0001 +#define VT_INTR_TX_DONE 0x0002 +#define VT_INTR_RX_ERR 0x0004 +#define VT_INTR_TX_ERR 0x0008 +#define VT_INTR_RX_EMPTY 0x0020 +#define VT_INTR_PCI_ERR 0x0040 +#define VT_INTR_STS_MAX 0x0080 +#define VT_INTR_RX_EARLY 0x0100 +#define VT_INTR_TX_UNDER_RUN 0x0210 +#define VT_INTR_RX_OVERFLOW 0x0400 +#define VT_INTR_RX_DROPPED 0x0800 +#define VT_INTR_RX_NOBUF 0x1000 +#define VT_INTR_TX_ABORT 0x2000 +#define VT_INTR_PORT_CHANGE 0x4000 +#define VT_INTR_RX_WAKE_UP 0x8000 + +/* Control Register Command */ +#define VT_CMD_START 0x0002 +#define VT_CMD_STOP 0x0004 +#define VT_CMD_RX_ON 0x0008 +#define VT_CMD_TX_ON 0x0010 +#define VT_CMD_TX_DEMAND 0x0020 +#define VT_CMD_RX_DEMAND 0x0040 +#define VT_CMD_EARLY_RX 0x0100 +#define VT_CMD_EARLY_TX 0x0200 +#define VT_CMD_FDUPLEX 0x0400 +#define VT_CMD_NO_POLL 0x0800 +#define VT_CMD_RESET 0x8000 + +/* Receive Configure Mode */ +#define VT_RCR_AP 0x10 +#define VT_RCR_AB 0x08 +#define VT_RCR_AM 0x04 +#define VT_RCR_AR 0x02 +#define VT_RCR_AE 0x01 + +/* General Descriptor Flag */ +#define VT_DESC_OWN 0x80000000 /* Ownership */ +#define VT_DESC_FIRST 0x00000200 /* First Descriptor */ +#define VT_DESC_LAST 0x00000100 /* Last Descriptor */ + +/* Rx Descriptor Flag */ +#define VT_DESC_RX_LENMASK 0x7fff0000 /* Receive Frame Length Mask */ +#define VT_DESC_RX_OK 0x00008000 /* Receive OK */ +#define VT_DESC_RX_MAR 0x00002000 /* Multicast Address Received */ +#define VT_DESC_RX_BAR 0x00001000 /* Broadcast Address Received */ +#define VT_DESC_RX_PHY 0x00000800 /* Physical Address Received */ +#define VT_DESC_RX_LINK_ERR 0x00000080 /* Descriptor Link Structure Error */ +#define VT_DESC_RX_RUNT 0x00000020 /* Runt Packet */ +#define VT_DESC_RX_LONG 0x00000010 /* Long Packet */ +#define VT_DESC_RX_FOV 0x00000008 /* FIFO Overflow */ +#define VT_DESC_RX_FAE 0x00000004 /* Frame Align Error */ +#define VT_DESC_RX_CRCE 0x00000002 /* CRC Error */ +#define VT_DESC_RX_ERR 0x00000001 /* Receive Error */ +#define VT_DESC_RX_ALL_ERR (VT_DESC_RX_LINK_ERR | VT_DESC_RX_RUNT | \ + VT_DESC_RX_LONG | VT_DESC_RX_FOV | \ + VT_DESC_RX_FAE | VT_DESC_RX_CRCE | VT_DESC_RX_ERR) +#define VT_DESC_RX_PACK (VT_DESC_FIRST | VT_DESC_LAST) + +/* Tx Descriptor Flag */ +#define VT_DESC_TX_ERR 0x00008000 /* Transmit Error */ +#define VT_DESC_TX_UDF 0x00000800 /* FIFO Underflow */ +#define VT_DESC_TX_CRS 0x00000400 /* Carrier Sense Lost Detect */ +#define VT_DESC_TX_OWC 0x00000200 /* Out of Window Collision */ +#define VT_DESC_TX_ABT 0x00000100 /* Excessive Collision Abort */ +#define VT_DESC_TX_CDH 0x00000010 /* Collision Detect */ + +/* WOL status */ +#define VT_WOL_UCAST 0x10 +#define VT_WOL_MAGIC 0x20 +#define VT_WOL_MCAST 0x30 +#define VT_WOL_LINK_UP 0x40 +#define VT_WOL_LINK_DOWN 0x80 + +/* Link status */ +#define VT_LINK_UP 1 +#define VT_LINK_DOWN 0 +#define VT_LINK_UNKNOWN -1 + +#define VT_RX_BUF_SIZE 1536 +#define VT_TX_BUF_SIZE 1536 +#define VT_RX_DESC_NUM 64 +#define VT_TX_DESC_NUM 64 + +/* #define VT6105_DEBUG */ + +/* Data Descriptor */ +typedef struct vt_desc { + u32_t status; /* command/status */ + u32_t length; /* Buffer/Frame length */ + u32_t addr; /* DMA buffer address */ + u32_t next_desc; /* Next descriptor */ +} vt_desc; + +/* Driver Data Structure */ +typedef struct vt_driver { + u16_t vt_base_addr; /* Base address */ + int vt_revision; /* Revision ID */ + int vt_irq; /* IRQ number */ + int vt_mode; + int vt_link; /* Whether link-up */ + int vt_recv_flag; /* Receive flag */ + int vt_send_flag; /* Send flag */ + int vt_report_link; + int vt_tx_alive; + int vt_tx_busy; + + /* Buffer */ + size_t vt_buf_size; + char *vt_buf; + + /* Rx data */ + int vt_rx_head; + struct { + phys_bytes buf_dma; + char *buf; + } vt_rx[VT_RX_DESC_NUM]; + vt_desc *vt_rx_desc; /* Rx descriptor buffer */ + phys_bytes vt_rx_desc_dma; /* Rx descriptor DMA buffer */ + + /* Tx data */ + int vt_tx_head; + int vt_tx_tail; + struct { + int busy; + phys_bytes buf_dma; + char *buf; + } vt_tx[VT_TX_DESC_NUM]; + vt_desc *vt_tx_desc; /* Tx descriptor buffer */ + phys_bytes vt_tx_desc_dma; /* Tx descriptor DMA buffer */ + int vt_tx_busy_num; /* Number of busy Tx descriptors */ + + int vt_hook; /* IRQ hook id at kernel */ + eth_stat_t vt_stat; + + char vt_name[20]; +} vt_driver; diff --git a/minix/fs/procfs/service.c b/minix/fs/procfs/service.c index bb2606e65..e8b06eef5 100644 --- a/minix/fs/procfs/service.c +++ b/minix/fs/procfs/service.c @@ -73,6 +73,7 @@ service_get_policies(struct policies * pol, index_t slot) { .label = "rtl8169", .policy_str = "reset" }, { .label = "uds", .policy_str = "reset" }, { .label = "virtio_net", .policy_str = "reset" }, + { .label = "vt6105", .policy_str = "reset" }, /* power */ { .label = "acpi", .policy_str = "" }, { .label = "tps65217", .policy_str = "" }, -- 2.44.0