Add the driver for IC Plus 1000A Ethernet card

This commit is contained in:
Jia-Ju Bai 2016-11-02 13:18:48 +00:00
parent 6238e058c4
commit 26ed3497c8
5 changed files with 1266 additions and 0 deletions

View File

@ -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 <minix.service.mk>

View File

@ -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 <baijiaju1990@163.com>
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.

View File

@ -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; i<n; i++) {
ret = 0;
for (j=0; j<1000000; j++)
ret++;
}
return ret;
}
static u16_t read_eeprom(ic_driver *pdev, int addr) {
u16_t ret, data, val, base = pdev->ic_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));
}

View File

@ -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
;
};

View File

@ -0,0 +1,262 @@
#include <minix/drivers.h>
#include <minix/netdriver.h>
#include <machine/pci.h>
#include <minix/ds.h>
#include <assert.h>
/* 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;