
This is a driver-breaking update to the netdriver library, which is used by all network drivers. The aim of this change is to make the library more compatible with NetBSD, and in particular with various features that are expected to be supported by the NetBSD userland. The main changes made by this patch are the following: - each network driver now has a NetBSD-style short device name; - drivers are not expected to receive packets right after startup; - extended support for receipt modes, including multicast lists; - support for multiple parallel send, receive requests; - embedding of I/O vectors in send and receive requests; - support for capabilities, including checksum offloading; - support for reporting link status updates to the TCP/IP stack; - support for setting and retrieving media status; - support for changing the hardware (MAC) address; - support for NetBSD interface flags IFF_DEBUG, IFF_LINK[0-2]; - support for NetBSD error statistics; - support for regular time-based ("tick") callbacks. IMPORTANT: this patch applies a minimal update to the existing drivers in order to make them work at all with the new netdriver library. It however does *not* change all drivers to make use of the new features. In fact, strictly speaking, all drivers are now violating requirements imposed by the new library in one way or another, most notably by enabling packet receipt when starting the driver. Changing all the drivers to be compliant, and to support the newly added options, is left to future patches. The existing drivers should currently *not* be taken as examples of how to implement a new network driver! With that said, a few drivers have already been changed to make use of some of the new features: fxp, e1000, rtl8139, and rtl8169 now report link and media status, and the last three of those now support setting the hardware MAC address on the fly. In addition, dp8390 has been changed to default to PCI autoconfiguration if no configuration is specified through environment variables. Change-Id: I4b3ea9c0b9bc25d5b0609c6ff256fb0db71cdc42
990 lines
27 KiB
C
990 lines
27 KiB
C
#include <minix/drivers.h>
|
|
#include <minix/netdriver.h>
|
|
#include <machine/pci.h>
|
|
#include <sys/mman.h>
|
|
#include "ip1000.h"
|
|
#include "io.h"
|
|
|
|
/* global value */
|
|
static NDR_driver g_driver;
|
|
static int g_instance;
|
|
|
|
/* driver interface */
|
|
static int NDR_init(unsigned int instance, netdriver_addr_t * addr,
|
|
uint32_t * caps, unsigned int * ticks);
|
|
static void NDR_stop(void);
|
|
static void NDR_set_mode(unsigned int mode,
|
|
const netdriver_addr_t * mcast_list, unsigned int mcast_count);
|
|
static ssize_t NDR_recv(struct netdriver_data *data, size_t max);
|
|
static int NDR_send(struct netdriver_data *data, size_t size);
|
|
static void NDR_intr(unsigned int mask);
|
|
|
|
/* internal function */
|
|
static int dev_probe(NDR_driver *pdev, int instance);
|
|
static int dev_init_buf(NDR_driver *pdev);
|
|
static int dev_init_hw(NDR_driver *pdev, netdriver_addr_t *addr);
|
|
static int dev_reset_hw(NDR_driver *pdev);
|
|
static void dev_conf_addr(NDR_driver *pdev, netdriver_addr_t *addr);
|
|
static void dev_handler(NDR_driver *pdev);
|
|
static void dev_check_ints(NDR_driver *pdev);
|
|
|
|
/* developer interface */
|
|
static int dev_real_reset(u32_t *base);
|
|
static int dev_init_io(u32_t *base);
|
|
static int dev_init_mii(u32_t *base);
|
|
static void dev_intr_control(u32_t *base, int flag);
|
|
static void dev_rx_tx_control(u32_t *base, int flag);
|
|
static void dev_get_addr(u32_t *base, u8_t *pa);
|
|
static int dev_check_link(u32_t *base);
|
|
static void dev_set_rec_mode(u32_t *base, int mode);
|
|
static void dev_start_tx(u32_t *base);
|
|
static u32_t dev_read_clear_intr_status(u32_t *base);
|
|
static void dev_init_rx_desc(NDR_desc *desc_start, int index, size_t buf_size,
|
|
phys_bytes buf_dma, int max_desc_num, phys_bytes desc_dma_start);
|
|
static void dev_init_tx_desc(NDR_desc *desc_start, int index, size_t buf_size,
|
|
phys_bytes buf_dma, int max_desc_num, phys_bytes desc_dma_start);
|
|
static void dev_set_desc_reg(u32_t *base, phys_bytes rx_addr,
|
|
phys_bytes tx_addr);
|
|
static int dev_rx_ok_desc(u32_t *base, NDR_desc *desc, int index);
|
|
static int dev_rx_len_desc(u32_t *base, NDR_desc *desc, int index);
|
|
static void dev_set_rx_desc_done(u32_t *base, NDR_desc *desc, int index);
|
|
static void dev_set_tx_desc_prepare(u32_t *base, NDR_desc *desc, int index,
|
|
size_t data_size);
|
|
static int dev_tx_ok_desc(u32_t *base, NDR_desc *desc, int index);
|
|
static void dev_set_tx_desc_done(u32_t *base, NDR_desc *desc, int index);
|
|
|
|
/* ======= Developer implemented function ======= */
|
|
/* ====== Self-defined function ======*/
|
|
static u16_t read_eeprom(u32_t base, int addr) {
|
|
u32_t ret, data, val;
|
|
int i;
|
|
|
|
val = EC_READ | (addr & 0xff);
|
|
ndr_out16(base, REG_EEPROM_CTRL, val);
|
|
for (i = 0; i < 100; i++) {
|
|
micro_delay(10000);
|
|
data = ndr_in16(base, REG_EEPROM_CTRL);
|
|
if (!(data & EC_BUSY)) {
|
|
ret = ndr_in16(base, REG_EEPROM_DATA);
|
|
break;
|
|
}
|
|
}
|
|
if (i == 100)
|
|
printf("IP1000: Fail to read EEPROM\n");
|
|
return ret;
|
|
}
|
|
|
|
static u16_t read_phy_reg(u32_t base, int phy_addr, int phy_reg) {
|
|
int i, j, fieldlen[8];
|
|
u32_t field[8];
|
|
u8_t data, polar;
|
|
|
|
field[0] = 0xffffffff; fieldlen[0] = 32;
|
|
field[1] = 0x0001; fieldlen[1] = 2;
|
|
field[2] = 0x0002; fieldlen[2] = 2;
|
|
field[3] = phy_addr; fieldlen[3] = 5;
|
|
field[4] = phy_reg; fieldlen[4] = 5;
|
|
field[5] = 0x0000; fieldlen[5] = 2;
|
|
field[6] = 0x0000; fieldlen[6] = 16;
|
|
field[7] = 0x0000; fieldlen[7] = 1;
|
|
|
|
polar = ndr_in8(base, REG_PHY_CTRL) & 0x28;
|
|
for (i = 0; i < 5; i++) {
|
|
for (j = 0; j < fieldlen[i]; j++) {
|
|
data = (field[i] >> (fieldlen[i] - j - 1)) << 1;
|
|
data = (0x02 & data) | (0x04 | polar);
|
|
ndr_out8(base, REG_PHY_CTRL, data);
|
|
micro_delay(10);
|
|
ndr_out8(base, REG_PHY_CTRL, (data | 0x01));
|
|
micro_delay(10);
|
|
}
|
|
}
|
|
ndr_out8(base, REG_PHY_CTRL, (polar | 0x04));
|
|
micro_delay(10);
|
|
ndr_out8(base, REG_PHY_CTRL, (polar | 0x05));
|
|
micro_delay(10);
|
|
ndr_out8(base, REG_PHY_CTRL, polar);
|
|
micro_delay(10);
|
|
data = ndr_in8(base, REG_PHY_CTRL);
|
|
ndr_out8(base, REG_PHY_CTRL, (polar | 0x01));
|
|
micro_delay(10);
|
|
for (i = 0; i < fieldlen[6]; i++) {
|
|
ndr_out8(base, REG_PHY_CTRL, polar);
|
|
micro_delay(10);
|
|
data = ((ndr_in8(base, REG_PHY_CTRL) & 0x02) >> 1) & 0x01;
|
|
ndr_out8(base, REG_PHY_CTRL, (polar | 0x01));
|
|
micro_delay(10);
|
|
field[6] |= (data << (fieldlen[6] - i - 1));
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
ndr_out8(base, REG_PHY_CTRL, (polar | 0x04));
|
|
micro_delay(10);
|
|
ndr_out8(base, REG_PHY_CTRL, (polar | 0x05));
|
|
micro_delay(10);
|
|
}
|
|
ndr_out8(base, REG_PHY_CTRL, (polar | 0x04));
|
|
return field[6];
|
|
}
|
|
|
|
static void write_phy_reg(u32_t base, int phy_addr, int phy_reg, u16_t val) {
|
|
int i, j, fieldlen[8];
|
|
u32_t field[8];
|
|
u8_t data, polar;
|
|
|
|
field[0] = 0xffffffff; fieldlen[0] = 32;
|
|
field[1] = 0x0001; fieldlen[1] = 2;
|
|
field[2] = 0x0001; fieldlen[2] = 2;
|
|
field[3] = phy_addr; fieldlen[3] = 5;
|
|
field[4] = phy_reg; fieldlen[4] = 5;
|
|
field[5] = 0x0002; fieldlen[5] = 2;
|
|
field[6] = val; fieldlen[6] = 16;
|
|
field[7] = 0x0000; fieldlen[7] = 1;
|
|
|
|
polar = ndr_in8(base, REG_PHY_CTRL) & 0x28;
|
|
for (i = 0; i < 7; i++) {
|
|
for (j = 0; j < fieldlen[i]; j++) {
|
|
data = (field[i] >> (field[i] - j - 1)) << 1;
|
|
data = (0x02 & data) | (0x04 | polar);
|
|
ndr_out8(base, REG_PHY_CTRL, data);
|
|
micro_delay(10);
|
|
ndr_out8(base, REG_PHY_CTRL, (data | 0x01));
|
|
micro_delay(10);
|
|
}
|
|
}
|
|
for (i = 0; i < fieldlen[7]; i ++) {
|
|
ndr_out8(base, REG_PHY_CTRL, polar);
|
|
micro_delay(10);
|
|
field[7] |= ((ndr_in8(base, REG_PHY_CTRL) & 0x02) >> 1)
|
|
<< (fieldlen[7] - i -1);
|
|
ndr_out8(base, REG_PHY_CTRL, (polar | 0x01));
|
|
micro_delay(10);
|
|
}
|
|
}
|
|
|
|
/* ====== Developer interface ======*/
|
|
/* Real hardware reset (### RESET_HARDWARE_CAN_FAIL ###)
|
|
* -- Return OK means success, Others means failure */
|
|
static int dev_real_reset(u32_t *base) {
|
|
u32_t data, base0 = base[0];
|
|
data = ndr_in32(base0, REG_ASIC_CTRL);
|
|
ndr_out32(base0, REG_ASIC_CTRL, data | AC_RESET_ALL);
|
|
micro_delay(5000);
|
|
if (ndr_in32(base0, REG_ASIC_CTRL) & AC_RESET_BUSY)
|
|
return -EIO;
|
|
return OK;
|
|
}
|
|
|
|
/* Intialize other hardware I/O registers (### INIT_HARDWARE_IO_CAN_FAIL ###)
|
|
* -- Return OK means success, Others means failure */
|
|
static int dev_init_io(u32_t *base) {
|
|
u32_t mac_ctrl, physet, mode0, mode1, base0 = base[0];
|
|
mode0 = read_eeprom(base0, 6);
|
|
mode1 = ndr_in16(base0, REG_ASIC_CTRL);
|
|
mode1 &= ~(AC_LED_MODE_B1 | AC_LED_MODE | AC_LED_SPEED);
|
|
if ((mode0 & 0x03) > 1)
|
|
mode1 |= AC_LED_MODE_B1;
|
|
if ((mode0 & 0x01) == 1)
|
|
mode1 |= AC_LED_MODE;
|
|
if ((mode0 & 0x08) == 8)
|
|
mode1 |= AC_LED_SPEED;
|
|
ndr_out32(base0, REG_ASIC_CTRL, mode1);
|
|
physet = ndr_in8(base0, REG_PHY_SET);
|
|
physet = (physet & 0xf8) | ((mode0 & 0x70) >> 4);
|
|
ndr_out8(base0, REG_PHY_SET, physet);
|
|
mac_ctrl = ndr_in32(base0, REG_MAC_CTRL);
|
|
mac_ctrl |= (MC_STAT_DISABLE | MC_TX_FC_ENA | MC_RX_FC_ENA);
|
|
ndr_out32(base0, REG_MAC_CTRL, 0);
|
|
ndr_out16(base0, REG_MAX_FRAME, RX_BUF_SIZE);
|
|
ndr_out8(base0, REG_RX_DMA_PERIOD, 0x01);
|
|
ndr_out8(base0, REG_RX_DMA_UTH, 0x30);
|
|
ndr_out8(base0, REG_RX_DMA_BTH, 0x30);
|
|
ndr_out8(base0, REG_TX_DMA_PERIOD, 0x26);
|
|
ndr_out8(base0, REG_TX_DMA_UTH, 0x04);
|
|
ndr_out8(base0, REG_TX_DMA_BTH, 0x30);
|
|
ndr_out16(base0, REG_FLOW_ON_TH, 0x0740);
|
|
ndr_out16(base0, REG_FLOW_OFF_TH, 0x00bf);
|
|
ndr_out32(base0, REG_MAC_CTRL, mac_ctrl);
|
|
return OK;
|
|
}
|
|
|
|
/* Intialize MII interface (### MII_INIT_CAN_FAIL ###)
|
|
* -- Return OK means success, Others means failure */
|
|
static int dev_init_mii(u32_t *base) {
|
|
int i, phyaddr;
|
|
u8_t revision;
|
|
u16_t phyctrl, cr1000, length, address, value;
|
|
u16_t *param;
|
|
u32_t status, base0 = base[0];
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
phyaddr = (i + 0x01) % 32;
|
|
status = read_phy_reg(base0, phyaddr, 0x01);
|
|
if ((status != 0xffff) && (status != 0))
|
|
break;
|
|
}
|
|
if (i == 32)
|
|
return -EIO;
|
|
if (phyaddr != -1) {
|
|
cr1000 = read_phy_reg(base0, phyaddr, 0x09);
|
|
cr1000 |= 0x0700;
|
|
write_phy_reg(base0, phyaddr, 0x09, cr1000);
|
|
phyctrl = read_phy_reg(base0, phyaddr, 0x00);
|
|
}
|
|
|
|
param = &PhyParam[0];
|
|
length = (*param) & 0x00ff;
|
|
revision = (u8_t)((*param) >> 8);
|
|
param++;
|
|
while (length != 0) {
|
|
if (g_driver.revision == revision) {
|
|
while (length > 1) {
|
|
address = *param;
|
|
value = *(param + 1);
|
|
param += 2;
|
|
write_phy_reg(base0, phyaddr, address, value);
|
|
length -= 4;
|
|
}
|
|
break;
|
|
}
|
|
else {
|
|
param += length / 2;
|
|
length = *param & 0x00ff;
|
|
revision = (u8_t)((*param) >> 8);
|
|
param++;
|
|
}
|
|
}
|
|
write_phy_reg(base0, phyaddr, 0x00, phyctrl | 0x8200);
|
|
return OK;
|
|
}
|
|
|
|
/* Enable or disable interrupt (### INTR_ENABLE_DISABLE ###) */
|
|
static void dev_intr_control(u32_t *base, int flag) {
|
|
u32_t base0 = base[0];
|
|
if (flag == INTR_ENABLE)
|
|
ndr_out16(base0, REG_IMR, CMD_INTR_ENABLE);
|
|
else if (flag == INTR_DISABLE)
|
|
ndr_out16(base0, REG_IMR, 0);
|
|
}
|
|
|
|
/* Enable or disable Rx/Tx (### RX_TX_ENABLE_DISABLE ###) */
|
|
static void dev_rx_tx_control(u32_t *base, int flag) {
|
|
u32_t data, base0 = base[0];
|
|
data = ndr_in32(base0, REG_MAC_CTRL);
|
|
if (flag == RX_TX_ENABLE)
|
|
ndr_out32(base0, REG_MAC_CTRL, data | (MC_RX_ENABLE | MC_TX_ENABLE));
|
|
else if (flag == RX_TX_DISABLE) {
|
|
ndr_out32(base0, REG_MAC_CTRL, 0);
|
|
ndr_out32(base0, REG_ASIC_CTRL, AC_RESET_ALL);
|
|
}
|
|
}
|
|
|
|
/* Get MAC address to the array 'pa' (### GET_MAC_ADDR ###) */
|
|
static void dev_get_addr(u32_t *base, u8_t *pa) {
|
|
u32_t i, sta_addr[3], base0 = base[0];
|
|
for (i = 0; i < 3; i++) {
|
|
sta_addr[i] = read_eeprom(base0, 16 + i);
|
|
ndr_out16(base0, (REG_STA_ADDR0 + i * 2), sta_addr[i]);
|
|
}
|
|
pa[0] = (u8_t)(ndr_in16(base0, REG_STA_ADDR0) & 0x00ff);
|
|
pa[1] = (u8_t)((ndr_in16(base0, REG_STA_ADDR0) & 0xff00) >> 8);
|
|
pa[2] = (u8_t)(ndr_in16(base0, REG_STA_ADDR1) & 0x00ff);
|
|
pa[3] = (u8_t)((ndr_in16(base0, REG_STA_ADDR1) & 0xff00) >> 8);
|
|
pa[4] = (u8_t)(ndr_in16(base0, REG_STA_ADDR2) & 0x00ff);
|
|
pa[5] = (u8_t)((ndr_in16(base0, REG_STA_ADDR2) & 0xff00) >> 8);
|
|
}
|
|
|
|
/* Check link status (### CHECK_LINK ###)
|
|
* -- Return LINK_UP or LINK_DOWN */
|
|
static int dev_check_link(u32_t *base) {
|
|
u32_t phy_ctrl, mac_ctrl, base0 = base[0];
|
|
int ret;
|
|
char speed[20], duplex[20];
|
|
|
|
phy_ctrl = ndr_in8(base0, REG_PHY_CTRL);
|
|
mac_ctrl = ndr_in8(base0, REG_MAC_CTRL);
|
|
switch (phy_ctrl & PC_LINK_SPEED) {
|
|
case PC_LINK_SPEED10:
|
|
strcpy(speed, "10Mbps");
|
|
ret = LINK_UP;
|
|
break;
|
|
case PC_LINK_SPEED100:
|
|
strcpy(speed, "100Mbps");
|
|
ret = LINK_UP;
|
|
break;
|
|
case PC_LINK_SPEED1000:
|
|
strcpy(speed, "1000Mbps");
|
|
ret = LINK_UP;
|
|
break;
|
|
default:
|
|
strcpy(speed, "unknown");
|
|
ret = LINK_DOWN;
|
|
break;
|
|
}
|
|
if (phy_ctrl & PC_DUPLEX_STS) {
|
|
strcpy(duplex, "full");
|
|
mac_ctrl |= (MC_DUPLEX_SEL | MC_TX_FC_ENA | MC_RX_FC_ENA);
|
|
}
|
|
else
|
|
strcpy(duplex, "half");
|
|
ndr_out32(base0, REG_MAC_CTRL, mac_ctrl);
|
|
#ifdef MY_DEBUG
|
|
printf("NDR: Link speed is %s, %s duplex\n", speed, duplex);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/* Set driver receive mode (### SET_REC_MODE ###) */
|
|
static void dev_set_rec_mode(u32_t *base, int mode) {
|
|
u32_t data, base0 = base[0];
|
|
data = ndr_in8(base0, REG_RCR);
|
|
data &= ~(CMD_RCR_UNICAST | CMD_RCR_MULTICAST | CMD_RCR_BROADCAST);
|
|
if (mode & NDEV_MODE_PROMISC)
|
|
data |= CMD_RCR_UNICAST | CMD_RCR_MULTICAST | CMD_RCR_MULTICAST;
|
|
if (mode & NDEV_MODE_BCAST)
|
|
data |= CMD_RCR_BROADCAST;
|
|
if (mode & (NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
|
|
data |= CMD_RCR_MULTICAST;
|
|
data |= CMD_RCR_UNICAST;
|
|
ndr_out8(base0, REG_RCR, data);
|
|
}
|
|
|
|
/* Start Tx channel (### START_TX_CHANNEL ###) */
|
|
static void dev_start_tx(u32_t *base) {
|
|
u32_t base0 = base[0];
|
|
ndr_out32(base0, REG_DMA_CTRL, CMD_TX_START);
|
|
}
|
|
|
|
/* Read and clear interrupt (### READ_CLEAR_INTR_STS ###) */
|
|
static u32_t dev_read_clear_intr_status(u32_t *base) {
|
|
u32_t data, base0 = base[0];
|
|
data = ndr_in16(base0, REG_ISR);
|
|
ndr_out16(base0, REG_ISR, 0);
|
|
return data;
|
|
}
|
|
|
|
/* ---------- WITH DESCRIPTOR ---------- */
|
|
/* Intialize Rx descriptor (### INIT_RX_DESC ###) */
|
|
static void dev_init_rx_desc(NDR_desc *desc_start, int index, size_t buf_size,
|
|
phys_bytes buf_dma, int max_desc_num, phys_bytes desc_dma_start) {
|
|
NDR_desc *desc = desc_start + index;
|
|
desc->status = 0;
|
|
desc->frag_info = (u64_t)(buf_dma);
|
|
desc->frag_info |= ((u64_t)buf_size << 48) & RFI_FRAG_LEN;
|
|
if (index == max_desc_num - 1)
|
|
desc->next = desc_dma_start;
|
|
else
|
|
desc->next = desc_dma_start + (index + 1) * sizeof(NDR_desc);
|
|
}
|
|
|
|
/* Intialize Tx descriptor (### INIT_TX_DESC ###) */
|
|
static void dev_init_tx_desc(NDR_desc *desc_start, int index, size_t buf_size,
|
|
phys_bytes buf_dma, int max_desc_num, phys_bytes desc_dma_start) {
|
|
NDR_desc *desc = desc_start + index;
|
|
desc->status = TFS_TFD_DONE;
|
|
desc->frag_info = (u64_t)(buf_dma);
|
|
if (index == max_desc_num - 1)
|
|
desc->next = desc_dma_start;
|
|
else
|
|
desc->next = desc_dma_start + (index + 1) * sizeof(NDR_desc);
|
|
}
|
|
|
|
/* Set Rx/Tx descriptor address into device register (### SET_DESC_REG ###) */
|
|
static void dev_set_desc_reg(u32_t *base, phys_bytes rx_addr,
|
|
phys_bytes tx_addr) {
|
|
u32_t base0 = base[0];
|
|
ndr_out32(base0, REG_RX_DESC_BASEL, rx_addr);
|
|
ndr_out32(base0, REG_RX_DESC_BASEU, 0);
|
|
ndr_out32(base0, REG_TX_DESC_BASEL, tx_addr);
|
|
ndr_out32(base0, REG_TX_DESC_BASEU, 0);
|
|
}
|
|
|
|
/* Check whether Rx is OK from Rx descriptor (### CHECK_RX_OK_FROM_DESC ###)
|
|
* -- Current buffer number is index
|
|
* -- Return RX_OK or RX_SUSPEND or RX_ERROR */
|
|
static int dev_rx_ok_desc(u32_t *base, NDR_desc *desc, int index) {
|
|
if (desc->status & RFS_RFD_DONE) {
|
|
if (desc->status & RFS_ERROR)
|
|
return RX_ERROR;
|
|
if ((desc->status & RFS_NORMAL) == RFS_NORMAL)
|
|
return RX_OK;
|
|
}
|
|
return RX_SUSPEND;
|
|
}
|
|
|
|
/* Get length from Rx descriptor (### GET_RX_LENGTH_FROM_DESC ###)
|
|
* -- Current buffer number is index
|
|
* -- Return the length */
|
|
static int dev_rx_len_desc(u32_t *base, NDR_desc *desc, int index) {
|
|
int totlen;
|
|
totlen = (int)(desc->status & RFS_FRAME_LEN);
|
|
return totlen;
|
|
}
|
|
|
|
/* Set Rx descriptor after Rx done (### SET_RX_DESC_DONE ###)
|
|
* -- Current buffer number is index */
|
|
static void dev_set_rx_desc_done(u32_t *base, NDR_desc *desc, int index) {
|
|
desc->status = 0;
|
|
}
|
|
|
|
/* Set Tx descriptor to prepare transmitting (### SET_TX_DESC_PREPARE)
|
|
* -- Current buffer number is index */
|
|
static void dev_set_tx_desc_prepare(u32_t *base, NDR_desc *desc, int index,
|
|
size_t data_size) {
|
|
desc->status = TFS_TFD_DONE;
|
|
desc->status |= (u64_t)(TFS_WORD_ALIGN | (TFS_FRAMEID & index)
|
|
| (TFS_FRAG_COUNT & (1 << 24))) | TFS_TX_DMA_INDICATE;
|
|
desc->frag_info |= TFI_FRAG_LEN & ((u64_t)((data_size > 60 ? data_size : 60)
|
|
& 0xffff) << 48);
|
|
desc->status &= (u64_t)(~(TFS_TFD_DONE));
|
|
}
|
|
|
|
/* Check whether Tx is OK from Tx descriptor (### CHECK_TX_OK_FROM_DESC ###)
|
|
* -- Current buffer number is index
|
|
* -- Return TX_OK or TX_SUSPEND or TX_ERROR */
|
|
static int dev_tx_ok_desc(u32_t *base, NDR_desc *desc, int index) {
|
|
if (desc->status & TFS_TFD_DONE)
|
|
return TX_OK;
|
|
return TX_SUSPEND;
|
|
}
|
|
|
|
/* Set Tx descriptor after Tx done (### SET_TX_DESC_DONE ###)
|
|
* -- Current buffer number is index */
|
|
static void dev_set_tx_desc_done(u32_t *base, NDR_desc *desc, int index) {
|
|
desc->status = 0;
|
|
}
|
|
|
|
/* Driver interface table */
|
|
static const struct netdriver NDR_table = {
|
|
.ndr_name = "stge",
|
|
.ndr_init = NDR_init,
|
|
.ndr_stop = NDR_stop,
|
|
.ndr_set_mode = NDR_set_mode,
|
|
.ndr_recv = NDR_recv,
|
|
.ndr_send = NDR_send,
|
|
.ndr_intr = NDR_intr,
|
|
};
|
|
|
|
int main(int argc, char *argv[]) {
|
|
env_setargs(argc, argv);
|
|
netdriver_task(&NDR_table);
|
|
}
|
|
|
|
/* Initialize the driver */
|
|
static int
|
|
NDR_init(unsigned int instance, netdriver_addr_t * addr, uint32_t * caps,
|
|
unsigned int * ticks __unused)
|
|
{
|
|
int i, ret = 0;
|
|
|
|
/* Intialize driver data structure */
|
|
memset(&g_driver, 0, sizeof(g_driver));
|
|
g_driver.link = LINK_UNKNOWN;
|
|
g_instance = instance;
|
|
|
|
/* Probe the device */
|
|
if (dev_probe(&g_driver, instance)) {
|
|
printf("NDR: Device is not found\n");
|
|
ret = -ENODEV;
|
|
goto err_probe;
|
|
}
|
|
|
|
/* Intialize hardware */
|
|
if (dev_init_hw(&g_driver, addr)) {
|
|
printf("NDR: Fail to initialize hardware\n");
|
|
ret = -EIO;
|
|
goto err_init_hw;
|
|
}
|
|
|
|
/* Allocate and initialize buffer */
|
|
if (dev_init_buf(&g_driver)) {
|
|
printf("NDR: Fail to initialize buffer\n");
|
|
ret = -ENODEV;
|
|
goto err_init_buf;
|
|
}
|
|
|
|
/* Enable interrupts */
|
|
/* ### INTR_ENABLE_DISABLE ### */
|
|
dev_intr_control(g_driver.base, INTR_ENABLE);
|
|
|
|
/* Start Rx and Tx */
|
|
/* ### RX_TX_ENABLE_DISABLE ### */
|
|
dev_rx_tx_control(g_driver.base, RX_TX_ENABLE);
|
|
|
|
/* Clear send and recv flag */
|
|
g_driver.send_flag = FALSE;
|
|
g_driver.recv_flag = FALSE;
|
|
|
|
*caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST;
|
|
return OK;
|
|
|
|
err_init_buf:
|
|
err_init_hw:
|
|
err_probe:
|
|
return ret;
|
|
}
|
|
|
|
/* Stop the driver */
|
|
static void NDR_stop(void) {
|
|
/* Free Rx and Tx buffer*/
|
|
free_contig(g_driver.buf, g_driver.buf_size);
|
|
|
|
/* Stop interrupt */
|
|
/* ### INTR_ENABLE_DISABLE ### */
|
|
dev_intr_control(g_driver.base, INTR_DISABLE);
|
|
|
|
/* Stop Rx and Tx */
|
|
/* ### RX_TX_ENABLE_DISABLE ### */
|
|
dev_rx_tx_control(g_driver.base, RX_TX_DISABLE);
|
|
}
|
|
|
|
/* Set driver mode */
|
|
static void
|
|
NDR_set_mode(unsigned int mode, const netdriver_addr_t * mcast_list __unused,
|
|
unsigned int mcast_count __unused)
|
|
{
|
|
g_driver.mode = mode;
|
|
/* Set driver receive mode */
|
|
/* ### SET_REC_MODE ### */
|
|
dev_set_rec_mode(g_driver.base, mode);
|
|
}
|
|
|
|
/* Receive data */
|
|
static ssize_t NDR_recv(struct netdriver_data *data, size_t max) {
|
|
NDR_driver *pdev = &g_driver;
|
|
u32_t totlen, packlen;
|
|
int index, ret, offset = 0;
|
|
NDR_desc *desc;
|
|
|
|
index = pdev->rx_head;
|
|
desc = pdev->rx_desc;
|
|
desc += index;
|
|
/* Check whether Rx is OK from Rx descriptor */
|
|
/* ### CHECK_RX_OK_FROM_DESC ### */
|
|
ret = dev_rx_ok_desc(pdev->base, desc, index);
|
|
if (ret == RX_SUSPEND)
|
|
return SUSPEND;
|
|
else if (ret == RX_ERROR)
|
|
printf("NDR: Rx error now\n");
|
|
/* Get length from Rx descriptor */
|
|
/* ### GET_RX_LENGTH_FROM_DESC ### */
|
|
totlen = dev_rx_len_desc(pdev->base, desc, index);
|
|
|
|
/* Get data length */
|
|
/* ### Get , int inde, int indexxRx data length ### */
|
|
if (totlen < 8 || totlen > 2 * NDEV_ETH_PACKET_MAX) {
|
|
printf("NDR: Bad data length: %d\n", totlen);
|
|
panic(NULL);
|
|
}
|
|
|
|
packlen = totlen;
|
|
if (packlen > max)
|
|
packlen = max;
|
|
|
|
/* Copy data to user */
|
|
netdriver_copyout(data, 0, pdev->rx[index].buf + offset, packlen);
|
|
|
|
/* Set Rx descriptor after Rx done */
|
|
/* ### SET_RX_DESC_DONE ### */
|
|
dev_set_rx_desc_done(pdev->base, desc, index);
|
|
if (index == RX_BUFFER_NUM - 1)
|
|
index = 0;
|
|
else
|
|
index++;
|
|
pdev->rx_head = index;
|
|
|
|
#ifdef MY_DEBUG
|
|
printf("NDR: Successfully receive a packet, length = %d\n", packlen);
|
|
#endif
|
|
|
|
return packlen;
|
|
}
|
|
|
|
/* Transmit data */
|
|
static int NDR_send(struct netdriver_data *data, size_t size) {
|
|
NDR_driver *pdev = &g_driver;
|
|
int tx_head, i;
|
|
NDR_desc *desc;
|
|
|
|
tx_head = pdev->tx_head;
|
|
if (pdev->tx[tx_head].busy)
|
|
return SUSPEND;
|
|
|
|
/* Copy data from user */
|
|
netdriver_copyin(data, 0, pdev->tx[tx_head].buf, size);
|
|
|
|
/* Set busy */
|
|
pdev->tx[tx_head].busy = TRUE;
|
|
pdev->tx_busy_num++;
|
|
|
|
desc = pdev->tx_desc;
|
|
desc += tx_head;
|
|
/* Set Tx descriptor to prepare transmitting */
|
|
/* ### SET_TX_DESC_PREPARE ### */
|
|
dev_set_tx_desc_prepare(pdev->base, desc, tx_head, size);
|
|
if (tx_head == TX_BUFFER_NUM - 1)
|
|
tx_head = 0;
|
|
else
|
|
tx_head++;
|
|
pdev->tx_head = tx_head;
|
|
|
|
/* Start Tx channel */
|
|
/* ### START_TX ### */
|
|
dev_start_tx(pdev->base);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Handle interrupt */
|
|
static void NDR_intr(unsigned int mask) {
|
|
int s;
|
|
|
|
/* Run interrupt handler at driver level */
|
|
dev_handler(&g_driver);
|
|
|
|
/* Reenable interrupts for this hook */
|
|
if ((s = sys_irqenable(&g_driver.hook)) != OK)
|
|
printf("NDR: Cannot enable OS interrupts: %d\n", s);
|
|
|
|
/* Perform tasks based on the flagged conditions */
|
|
dev_check_ints(&g_driver);
|
|
}
|
|
|
|
/* Match the device and get base address */
|
|
static int dev_probe(NDR_driver *pdev, int instance) {
|
|
int devind, ioflag, i;
|
|
u16_t cr, vid, did;
|
|
u32_t bar, size, base;
|
|
u8_t irq, rev;
|
|
u8_t *reg;
|
|
|
|
/* 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 and I/O space */
|
|
cr = pci_attr_r16(devind, PCI_CR);
|
|
pci_attr_w16(devind, PCI_CR, cr | 0x105);
|
|
|
|
/* Get base address */
|
|
for (i = 0; i < 6; i++)
|
|
pdev->base[i] = 0;
|
|
#ifdef DMA_BASE_IOMAP
|
|
for (i = 0; i < 6; i++) {
|
|
if (pci_get_bar(devind, PCI_BAR + i * 4, &base, &size, &ioflag)) {
|
|
/* printf("NDR: Fail to get PCI BAR\n"); */
|
|
continue;
|
|
}
|
|
if (ioflag) {
|
|
/* printf("NDR: PCI BAR is not for memory\n"); */
|
|
continue;
|
|
}
|
|
if ((reg = vm_map_phys(SELF, (void *)base, size)) == MAP_FAILED) {
|
|
printf("NDR: Fail to map hardware registers from PCI\n");
|
|
return -EIO;
|
|
}
|
|
pdev->base[i] = (u32_t)reg;
|
|
}
|
|
#else
|
|
for (i = 0; i < 6; i++)
|
|
pdev->base[i] = pci_attr_r32(devind, PCI_BAR + i * 4) & 0xffffffe0;
|
|
#endif
|
|
pdev->dev_name = pci_dev_name(vid, did);
|
|
pdev->irq = pci_attr_r8(devind, PCI_ILR);
|
|
pdev->revision = pci_attr_r8(devind, PCI_REV);
|
|
pdev->did = did;
|
|
pdev->vid = vid;
|
|
pdev->devind = devind;
|
|
|
|
#ifdef MY_DEBUG
|
|
printf("NDR: Hardware name is %s\n", pdev->dev_name);
|
|
for (i = 0; i < 6; i++)
|
|
printf("NDR: PCI BAR%d is 0x%08x\n", i, pdev->base[i]);
|
|
printf("NDR: IRQ number is 0x%02x\n", pdev->irq);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Intialize hardware */
|
|
static int dev_init_hw(NDR_driver *pdev, netdriver_addr_t *addr) {
|
|
int r, ret;
|
|
|
|
/* Set the OS interrupt handler */
|
|
pdev->hook = pdev->irq;
|
|
if ((r = sys_irqsetpolicy(pdev->irq, 0, &pdev->hook)) != OK) {
|
|
printf("NDR: Fail to set OS IRQ policy: %d\n", r);
|
|
ret = -EFAULT;
|
|
goto err_irq_policy;
|
|
}
|
|
|
|
/* Reset hardware */
|
|
if (dev_reset_hw(pdev)) {
|
|
printf("NDR: Fail to reset the device\n");
|
|
ret = -EIO;
|
|
goto err_reset_hw;
|
|
}
|
|
|
|
/* Enable OS IRQ */
|
|
if ((r = sys_irqenable(&pdev->hook)) != OK) {
|
|
printf("NDR: Fail to enable OS IRQ: %d\n", r);
|
|
ret = -EFAULT;
|
|
goto err_irq_enable;
|
|
}
|
|
|
|
/* Configure MAC address */
|
|
dev_conf_addr(pdev, addr);
|
|
|
|
/* Detect link status */
|
|
/* ### CHECK_LINK ### */
|
|
pdev->link = dev_check_link(pdev->base);
|
|
#ifdef MY_DEBUG
|
|
if (pdev->link)
|
|
printf("NDR: Link up\n");
|
|
else
|
|
printf("NDR: Link down\n");
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
err_reset_hw:
|
|
err_irq_enable:
|
|
err_irq_policy:
|
|
return ret;
|
|
}
|
|
|
|
/* Reset hardware */
|
|
static int dev_reset_hw(NDR_driver *pdev) {
|
|
int ret;
|
|
|
|
/* Reset the chip */
|
|
/* ### RESET_HARDWARE_CAN_FAIL ### */
|
|
if (dev_real_reset(pdev->base)) {
|
|
printf("NDR: Fail to reset the hardware\n");
|
|
ret = -EIO;
|
|
goto err_real_reset;
|
|
}
|
|
|
|
/* Initialize other hardware I/O registers */
|
|
/* ### SET_RX_DESC_REG ### */
|
|
if (dev_init_io(pdev->base)) {
|
|
printf("NDR: Fail to initialize I/O registers\n");
|
|
ret = -EIO;
|
|
goto err_init_io;
|
|
}
|
|
|
|
/* Initialize MII interface */
|
|
/* ### MII_INIT_CAN_FAIL ### */
|
|
if (dev_init_mii(pdev->base)) {
|
|
printf("NDR: Fail to initialize MII interface\n");
|
|
ret = -EIO;
|
|
goto err_init_mii;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_init_mii:
|
|
err_init_io:
|
|
err_real_reset:
|
|
return ret;
|
|
}
|
|
|
|
/* Configure MAC address */
|
|
static void dev_conf_addr(NDR_driver *pdev, netdriver_addr_t *addr) {
|
|
u8_t pa[6];
|
|
|
|
/* Get MAC address */
|
|
/* ### GET_MAC_ADDR ### */
|
|
dev_get_addr(pdev->base, pa);
|
|
addr->na_addr[0] = pa[0];
|
|
addr->na_addr[1] = pa[1];
|
|
addr->na_addr[2] = pa[2];
|
|
addr->na_addr[3] = pa[3];
|
|
addr->na_addr[4] = pa[4];
|
|
addr->na_addr[5] = pa[5];
|
|
#ifdef MY_DEBUG
|
|
printf("NDR: Ethernet address is %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
addr->na_addr[0], addr->na_addr[1], addr->na_addr[2],
|
|
addr->na_addr[3], addr->na_addr[4], addr->na_addr[5]);
|
|
#endif
|
|
}
|
|
|
|
/* Allocate and initialize buffer */
|
|
static int dev_init_buf(NDR_driver *pdev) {
|
|
size_t rx_desc_size, tx_desc_size, rx_buf_size, tx_buf_size, tot_buf_size;
|
|
phys_bytes buf_dma;
|
|
char *buf;
|
|
int i;
|
|
|
|
/* Build Rx and Tx buffer */
|
|
tx_buf_size = TX_BUF_SIZE;
|
|
if (tx_buf_size % 4)
|
|
tx_buf_size += 4 - (tx_buf_size % 4);
|
|
rx_buf_size = RX_BUF_SIZE;
|
|
if (rx_buf_size % 4)
|
|
rx_buf_size += 4 - (rx_buf_size % 4);
|
|
tot_buf_size = TX_BUFFER_NUM * tx_buf_size + RX_BUFFER_NUM * rx_buf_size;
|
|
rx_desc_size = RX_BUFFER_NUM * sizeof(NDR_desc);
|
|
tx_desc_size = TX_BUFFER_NUM * sizeof(NDR_desc);
|
|
tot_buf_size += rx_desc_size + tx_desc_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("NDR: Fail to allocate memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
pdev->buf_size = tot_buf_size;
|
|
pdev->buf = buf;
|
|
|
|
/* Rx descriptor buffer location */
|
|
pdev->rx_desc = (NDR_desc *)buf;
|
|
pdev->rx_desc_dma = buf_dma;
|
|
memset(buf, 0, rx_desc_size);
|
|
buf += rx_desc_size;
|
|
buf_dma += rx_desc_size;
|
|
|
|
/* Tx descriptor buffer location */
|
|
pdev->tx_desc = (NDR_desc *)buf;
|
|
pdev->tx_desc_dma = buf_dma;
|
|
memset(buf, 0, tx_desc_size);
|
|
buf += tx_desc_size;
|
|
buf_dma += tx_desc_size;
|
|
|
|
/* Rx buffer assignment */
|
|
for (i = 0; i < RX_BUFFER_NUM; i++) {
|
|
/* Initialize Rx buffer */
|
|
pdev->rx[i].buf_dma = buf_dma;
|
|
pdev->rx[i].buf = buf;
|
|
buf_dma += rx_buf_size;
|
|
buf += rx_buf_size;
|
|
/* Set Rx descriptor */
|
|
/* ### INIT_RX_DESC ### */
|
|
dev_init_rx_desc(pdev->rx_desc, i, rx_buf_size, pdev->rx[i].buf_dma,
|
|
RX_BUFFER_NUM, pdev->rx_desc_dma);
|
|
}
|
|
|
|
/* Tx buffer assignment */
|
|
for (i = 0; i < TX_BUFFER_NUM; i++) {
|
|
/* Set Tx buffer */
|
|
pdev->tx[i].busy = 0;
|
|
pdev->tx[i].buf_dma = buf_dma;
|
|
pdev->tx[i].buf = buf;
|
|
buf_dma += tx_buf_size;
|
|
buf += tx_buf_size;
|
|
/* Initialize Tx descriptor */
|
|
/* ### INIT_TX_DESC ### */
|
|
dev_init_tx_desc(pdev->tx_desc, i, tx_buf_size, pdev->tx[i].buf_dma,
|
|
TX_BUFFER_NUM, pdev->tx_desc_dma);
|
|
}
|
|
|
|
/* Set Rx/Tx descriptor address into device register */
|
|
/* ### SET_DESC_REG ### */
|
|
dev_set_desc_reg(pdev->base, g_driver.rx_desc_dma,
|
|
g_driver.tx_desc_dma);
|
|
|
|
pdev->tx_busy_num = 0;
|
|
pdev->tx_head = 0;
|
|
pdev->tx_tail = 0;
|
|
pdev->rx_head = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Real handler interrupt */
|
|
static void dev_handler(NDR_driver *pdev) {
|
|
u32_t intr_status;
|
|
int tx_head, tx_tail, index, flag = 0, ret;
|
|
NDR_desc *desc;
|
|
|
|
/* Read and clear interrupt status */
|
|
/* ### READ_CLEAR_INTR_STS ### */
|
|
intr_status = dev_read_clear_intr_status(pdev->base);
|
|
|
|
/* Enable interrupt */
|
|
/* ### INTR_ENABLE_DISABLE ### */
|
|
dev_intr_control(pdev->base, INTR_ENABLE);
|
|
|
|
/* Check link status */
|
|
if (intr_status & INTR_STS_LINK) {
|
|
pdev->link = dev_check_link(pdev->base);
|
|
#ifdef MY_DEBUG
|
|
printf("NDR: Link state change\n");
|
|
#endif
|
|
flag++;
|
|
}
|
|
/* Check Rx request status */
|
|
if (intr_status & INTR_STS_RX) {
|
|
pdev->recv_flag = TRUE;
|
|
flag++;
|
|
}
|
|
/* Check Tx request status */
|
|
if (intr_status & INTR_STS_TX) {
|
|
pdev->send_flag = TRUE;
|
|
flag++;
|
|
|
|
/* Manage Tx Buffer */
|
|
tx_head = pdev->tx_head;
|
|
tx_tail = pdev->tx_tail;
|
|
while (tx_tail != tx_head) {
|
|
if (!pdev->tx[tx_tail].busy)
|
|
printf("NDR: Strange, buffer not busy?\n");
|
|
index = tx_tail;
|
|
desc = pdev->tx_desc;
|
|
desc += tx_tail;
|
|
/* Check whether Tx is OK from Tx descriptor */
|
|
/* ### CHECK_TX_OK_FROM_DESC ### */
|
|
ret = dev_tx_ok_desc(pdev->base, desc, index);
|
|
if (ret == TX_SUSPEND)
|
|
break;
|
|
else if (ret == TX_ERROR)
|
|
printf("NDR: Tx error now\n");
|
|
|
|
pdev->tx[tx_tail].busy = FALSE;
|
|
pdev->tx_busy_num--;
|
|
|
|
if (++tx_tail >= TX_BUFFER_NUM)
|
|
tx_tail = 0;
|
|
|
|
pdev->send_flag = TRUE;
|
|
pdev->recv_flag = TRUE;
|
|
|
|
/* Set Tx descriptor after Tx done */
|
|
/* ### SET_TX_DESC_DONE ### */
|
|
dev_set_tx_desc_done(pdev->base, desc, index);
|
|
#ifdef MY_DEBUG
|
|
printf("NDR: Successfully send a packet\n");
|
|
#endif
|
|
}
|
|
pdev->tx_tail = tx_tail;
|
|
}
|
|
#ifdef MY_DEBUG
|
|
if (!flag) {
|
|
printf("NDR: Unknown error in interrupt 0x%08x\n", intr_status);
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Check interrupt and perform */
|
|
static void dev_check_ints(NDR_driver *pdev) {
|
|
if (!pdev->recv_flag)
|
|
return;
|
|
pdev->recv_flag = FALSE;
|
|
|
|
/* Handle data receive */
|
|
netdriver_recv();
|
|
|
|
/* Handle data transmit */
|
|
if (pdev->send_flag) {
|
|
pdev->send_flag = FALSE;
|
|
netdriver_send();
|
|
}
|
|
}
|