375 lines
13 KiB
C
375 lines
13 KiB
C
#include "i825xx.h"
|
|
#include "ether.h"
|
|
#include "pci.h"
|
|
#include "console.h"
|
|
#include "string.h"
|
|
#include "memory.h"
|
|
#include "interrupts.h"
|
|
#include "io.h"
|
|
|
|
extern struct ether_t **etherdevs;
|
|
extern unsigned int etherdev_count;
|
|
|
|
//Registers
|
|
#define REG_CTRL 0x00000 //Control Register
|
|
#define REG_STATUS 0x00008 //Status Register
|
|
#define REG_EERD 0x00014 //EEPROM Read Register
|
|
#define REG_ICR 0x000C0 //Interrupt Cause Read Register
|
|
#define REG_IMS 0x000D0 //Interrupt Mask Set Register
|
|
#define REG_RCTL 0x00100 //Recieve Control Register
|
|
#define REG_TCTL 0x00400 //Transmit Control Register
|
|
|
|
#define REG_RDBAL 0x02800 //Recieve Descriptor Base Address Low Register
|
|
#define REG_RDBAH 0x02804 //Recieve Descriptor Base Address High Register
|
|
#define REG_RDLEN 0x02808 //Recieve Descriptor Ring buff Length (bytes) Register
|
|
#define REG_RDH 0x02810 //Recieve Descriptor Head Register
|
|
#define REG_RDT 0x02818 //Recieve Descriptor Tail Register
|
|
|
|
#define REG_TDBAL 0x03800 //Recieve Descriptor Base Address Low Register
|
|
#define REG_TDBAH 0x03804 //Recieve Descriptor Base Address High Register
|
|
#define REG_TDLEN 0x03808 //Recieve Descriptor Ring buff Length (bytes) Register
|
|
#define REG_TDH 0x03810 //Recieve Descriptor Head Register
|
|
#define REG_TDT 0x03818 //Recieve Descriptor Tail Register
|
|
|
|
#define REG_MTA 0x05200 //Multicast Table Array Register
|
|
#define REG_RAL 0x05400 //Receive Address Low (MAC filter) Register
|
|
#define REG_RAH 0x05404 //Receive Address High (MAC filter) Register
|
|
|
|
#define REG_LAST 0x1FFFC
|
|
|
|
//Control Register Flags
|
|
#define CTRL_FD (1 << 0) //Full-Duplex
|
|
#define CTRL_ASDE (1 << 5) //Auto-Speed Detection Enable
|
|
#define CTRL_SLU (1 << 6) //Set Link Up
|
|
|
|
//Status Flags
|
|
#define STATUS_LU (1 << 1) //Link Up Indication
|
|
|
|
//EEPROM Register Flags
|
|
#define EERD_START (1 << 0) //Start Read
|
|
#define EERD_DONE (1 << 4) //Read Done
|
|
|
|
//Recieve Control Register Flags
|
|
#define RCTL_EN (1 << 1)
|
|
#define RCTL_SBP (1 << 2)
|
|
#define RCTL_UPE (1 << 3)
|
|
#define RCTL_MPE (1 << 4)
|
|
#define RCTL_LPE (1 << 5)
|
|
#define RCTL_BAM (1 << 15)
|
|
#define RCTL_VFE (1 << 18)
|
|
#define RCTL_CFIEN (1 << 19)
|
|
#define RCTL_CFI (1 << 20)
|
|
#define RCTL_DPF (1 << 22)
|
|
#define RCTL_PMCF (1 << 23)
|
|
#define RCTL_BSEX (1 << 25)
|
|
#define RCTL_SECRC (1 << 26)
|
|
|
|
#define RCTL_LBM_MASK (~(3 << 6))
|
|
#define RCTL_LBM_OFF (0 << 6)
|
|
#define RCTL_LBM_ON (3 << 6)
|
|
|
|
#define RCTL_RDMTS_MASK (~(3 << 8))
|
|
#define RCTL_RDMTS_HALF (0 << 8)
|
|
#define RCTL_RDMTS_QUATER (1 << 8)
|
|
#define RCTL_RDMTS_EIGHTH (2 << 8)
|
|
|
|
#define RCTL_MO_MASK (~(3 << 12))
|
|
#define RCTL_MO_SHIFT_ZERO (0 << 12)
|
|
#define RCTL_MO_SHIFT_ONE (1 << 12)
|
|
#define RCTL_MO_SHIFT_TWO (2 << 12)
|
|
#define RCTL_MO_SHIFT_FOUR (3 << 12)
|
|
|
|
#define RCTL_BSIZE_MASK (~(3 << 16))
|
|
#define RCTL_BSIZE_2048 (0 << 16)
|
|
#define RCTL_BSIZE_1024 (1 << 16)
|
|
#define RCTL_BSIZE_512 (2 << 16)
|
|
#define RCTL_BSIZE_256 (3 << 16)
|
|
#define RCTL_BSIZE_4096 ((3 << 16) | (1 << 25))
|
|
#define RCTL_BSIZE_8192 ((2 << 16) | (1 << 25))
|
|
#define RCTL_BSIZE_16384 ((1 << 16) | (1 << 25))
|
|
|
|
//Transmit Control Register Flags
|
|
#define TCTL_EN (1 << 1)
|
|
#define TCTL_PSP (1 << 3)
|
|
|
|
//Interrupt Causes
|
|
#define ICR_TXDW (1 << 0) //Transmit Descriptor Written Back
|
|
#define ICR_TXQE (1 << 1) //Transmit Queue Empty
|
|
#define ICR_LSC (1 << 2) //Link Status Change
|
|
#define ICR_RXSEQ (1 << 3) //Receive Sequence Error
|
|
#define ICR_RXDMT (1 << 4) //Receive Descriptor Minimum Threshold Reached
|
|
#define ICR_RXO (1 << 6) //Receiver Overrun
|
|
#define ICR_RXT (1 << 7) //Receiver Timer Interrupt
|
|
#define ICR_MDAC (1 << 9) //MDI/O Access Complete
|
|
#define ICR_RXCFG (1 << 10) //Receiving /C/ ordered sets
|
|
#define ICR_PHY (1 << 12) //PHY Interrupt
|
|
#define ICR_GPI_ONE (1 << 13) //General Purpose Interrupt (SDP6)
|
|
#define ICR_GPI_TWO (1 << 14) //General Purpose Interrupt (SDP7)
|
|
#define ICR_TXD_LOW (1 << 15) //Transmit Descriptor Low Threshold Hit
|
|
#define ICR_SRPD (1 << 16) //Small Receive Packet Detected
|
|
|
|
//RX Descriptor Status Flags
|
|
#define RX_DESC_STATUS_DD (1 << 0) //Descriptor Done
|
|
#define RX_DESC_STATUS_EOP (1 << 1) //End Of Packet
|
|
|
|
//TX Descriptor Command Flags
|
|
#define TX_DESC_CMD_EOP (1 << 0) //End Of Packet
|
|
#define TX_DESC_CMD_IFCS (1 << 1) //Insert FCS/CRC
|
|
#define TX_DESC_CMD_RS (1 << 3) //Report Status
|
|
|
|
//TX Descriptor Status Flags
|
|
#define TX_DESC_STATUS_DD (1 << 0) //Descriptor Done
|
|
|
|
#define PAGE_SIZE 4096
|
|
#define PAGE_MASK (~(0xffffffff << 12))
|
|
|
|
static __inline__ unsigned long round_up_to_page(unsigned long addr) {
|
|
if ((addr & PAGE_MASK) != 0) {
|
|
addr &= ~(PAGE_MASK);
|
|
addr += PAGE_SIZE;
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
static unsigned int mmio_read(struct i825xx_device_t *net_device, unsigned int reg) {
|
|
return *(unsigned int *)(net_device->mmio_address + reg);
|
|
}
|
|
|
|
static void mmio_write(struct i825xx_device_t *net_device, unsigned int reg, unsigned int value) {
|
|
*(unsigned int *)(net_device->mmio_address + reg) = value;
|
|
}
|
|
|
|
static unsigned short net_eeprom_read(struct i825xx_device_t *net_device, unsigned char addr) {
|
|
mmio_write(net_device, REG_EERD, EERD_START | (((unsigned int) (addr)) << 8));
|
|
|
|
unsigned int tmp = 0;
|
|
while(!((tmp = mmio_read(net_device, REG_EERD)) & EERD_DONE))
|
|
io_wait();
|
|
|
|
return (unsigned short) ((tmp >> 16) & 0xFFFF);
|
|
}
|
|
|
|
static void i825xx_poll(struct i825xx_device_t *i825xx_device) {
|
|
while(i825xx_device->rx_desc[i825xx_device->rx_front].status & RX_DESC_STATUS_DD) {
|
|
unsigned char *pkt = (unsigned char *)i825xx_device->rx_buff[i825xx_device->rx_front];
|
|
unsigned short pktlen = i825xx_device->rx_desc[i825xx_device->rx_front].length;
|
|
|
|
if(!(i825xx_device->rx_desc[i825xx_device->rx_front].status & RX_DESC_STATUS_EOP)) {
|
|
kprintf("825xx - rx: no EOP support, dropping");
|
|
} else if(i825xx_device->rx_desc[i825xx_device->rx_front].length < 60) {
|
|
kprintf("825xx - rx: short packet (%d bytes)", i825xx_device->rx_desc[i825xx_device->rx_front].length);
|
|
} else {
|
|
ether_receive(i825xx_device->ether, pkt, pktlen);
|
|
}
|
|
|
|
i825xx_device->rx_desc[i825xx_device->rx_front].status = 0;
|
|
i825xx_device->rx_front = (i825xx_device->rx_front + 1) % NUM_RX_DESCRIPTORS;
|
|
mmio_write(i825xx_device, REG_RDT, i825xx_device->rx_front);
|
|
}
|
|
}
|
|
|
|
static void i825xx_isr(struct regs *r) {
|
|
struct i825xx_device_t *i825xx_device;
|
|
int i;
|
|
|
|
for (i=0;i<etherdev_count;i++) {
|
|
if (etherdevs[i]->type == 1) {
|
|
i825xx_device = (struct i825xx_device_t *)etherdevs[i]->data;
|
|
unsigned int icr = mmio_read(i825xx_device, REG_ICR);
|
|
if (icr & ICR_LSC) {
|
|
|
|
if(mmio_read(i825xx_device, REG_STATUS) & STATUS_LU) {
|
|
etherdevs[i]->state = 1;
|
|
} else {
|
|
etherdevs[i]->state = 0;
|
|
}
|
|
}
|
|
if (icr & ICR_RXT) {
|
|
i825xx_poll(i825xx_device);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int i825xx_send(struct i825xx_device_t *i825xx_device, char *packet, int len) {
|
|
memset(i825xx_device->tx_buff[i825xx_device->tx_front], 0, 8192);
|
|
memcpy(i825xx_device->tx_buff[i825xx_device->tx_front], packet, len);
|
|
i825xx_device->tx_desc[i825xx_device->tx_front].length = len;
|
|
i825xx_device->tx_desc[i825xx_device->tx_front].cmd = ((1 << 3) | (3));
|
|
|
|
unsigned int old_tx_front = i825xx_device->tx_front;
|
|
i825xx_device->tx_front = (i825xx_device->tx_front + 1) % NUM_TX_DESCRIPTORS;
|
|
mmio_write(i825xx_device, REG_TDT, i825xx_device->tx_front);
|
|
|
|
while(!(i825xx_device->tx_desc[old_tx_front].sta & 0xF))
|
|
io_wait();
|
|
|
|
return i825xx_device->tx_desc[old_tx_front].sta & TX_DESC_STATUS_DD ? 0 : -1;
|
|
}
|
|
|
|
struct ether_t *init_i825xx(int count) {
|
|
struct pci_device *pci_dev;
|
|
struct i825xx_device_t *i825xx_dev;
|
|
struct ether_t *ether_dev;
|
|
unsigned int i;
|
|
unsigned short j;
|
|
if (!pci_find_device_by_vendor(0x8086, 0x100E, &pci_dev, count)) {
|
|
return (void *)0;
|
|
}
|
|
|
|
// if not memory mapped
|
|
if (pci_dev->base[0] & 0x1) {
|
|
return (void *)0;
|
|
}
|
|
|
|
i825xx_dev = (struct i825xx_device_t *)malloc(sizeof(struct i825xx_device_t));
|
|
ether_dev = (struct ether_t *)malloc(sizeof(struct ether_t));
|
|
|
|
unsigned int addr = (pci_dev->base[0] & 0xfffffff0);
|
|
unsigned int size = round_up_to_page(REG_LAST);
|
|
unsigned int mem_space = mem_pci_sbrk(size);
|
|
|
|
|
|
for (i=0;i<size;i+=PAGE_SIZE) {
|
|
mem_map_page(addr + i, mem_space + i, 3);
|
|
}
|
|
|
|
i825xx_dev->mmio_address = mem_space;
|
|
|
|
if (!irq_install_handler(pci_dev->irq, i825xx_isr, 0)) {
|
|
kprintf("Failed to install IRQ handler...\n");
|
|
}
|
|
|
|
((unsigned short *) ðer_dev->mac[0])[0] = net_eeprom_read(i825xx_dev, 0);
|
|
((unsigned short *) ðer_dev->mac[0])[1] = net_eeprom_read(i825xx_dev, 1);
|
|
((unsigned short *) ðer_dev->mac[0])[2] = net_eeprom_read(i825xx_dev, 2);
|
|
|
|
if(!(mmio_read(i825xx_dev, REG_STATUS) & STATUS_LU)) {
|
|
mmio_write(i825xx_dev, REG_CTRL, mmio_read(i825xx_dev, REG_CTRL) | CTRL_SLU);
|
|
}
|
|
|
|
|
|
io_wait();
|
|
|
|
for(j = 0; j < 128; j++) {
|
|
mmio_write(i825xx_dev, REG_MTA + (j * 4), 0);
|
|
}
|
|
|
|
|
|
//enable all interrupts (and clear existing pending ones)
|
|
mmio_write(i825xx_dev, REG_IMS, 0x1F6DC); //this could be 0xFFFFF but that sets some reserved bits
|
|
mmio_read(i825xx_dev, REG_ICR);
|
|
|
|
//set mac address filter
|
|
mmio_write(i825xx_dev, REG_RAL, ((unsigned short *) ðer_dev->mac[0])[0] | ((((unsigned int)((unsigned short *) ðer_dev->mac[0])[1]) << 16)));
|
|
mmio_write(i825xx_dev, REG_RAH, ((unsigned short *) ðer_dev->mac[0])[2] | (1 << 31));
|
|
|
|
//init RX
|
|
i825xx_dev->rx_front = 0;
|
|
|
|
|
|
|
|
char *pagesrx = mem_alloc_pages(NUM_RX_DESCRIPTORS * 16 / PAGE_SIZE);
|
|
|
|
|
|
i825xx_dev->rx_desc = mem_pci_sbrk(NUM_RX_DESCRIPTORS * 16);
|
|
|
|
|
|
|
|
for (i=0;i<(NUM_RX_DESCRIPTORS * 16 / PAGE_SIZE);i++) {
|
|
mem_map_page(pagesrx + (i * PAGE_SIZE), (char *)i825xx_dev->rx_desc + (i * PAGE_SIZE), 3);
|
|
}
|
|
|
|
|
|
|
|
// aligned base address
|
|
i825xx_dev->rx_desc_base = pagesrx;
|
|
|
|
for( i = 0; i < NUM_RX_DESCRIPTORS; i++ )
|
|
{
|
|
unsigned int packet_buff = mem_pci_sbrk(8192);
|
|
unsigned char *loc = mem_alloc_pages(2);
|
|
|
|
mem_map_page(loc, packet_buff, 3);
|
|
mem_map_page(loc + PAGE_SIZE, packet_buff + PAGE_SIZE, 3);
|
|
|
|
i825xx_dev->rx_desc[i].address = (unsigned long long)loc;
|
|
i825xx_dev->rx_buff[i] = (char *)packet_buff;
|
|
i825xx_dev->rx_desc[i].status = 0;
|
|
}
|
|
|
|
mmio_write(i825xx_dev, REG_RDBAL, i825xx_dev->rx_desc_base);
|
|
mmio_write(i825xx_dev, REG_RDBAH, 0);
|
|
mmio_write(i825xx_dev, REG_RDLEN, NUM_RX_DESCRIPTORS * 16);
|
|
|
|
mmio_write(i825xx_dev, REG_RDH, 0);
|
|
mmio_write(i825xx_dev, REG_RDT, NUM_RX_DESCRIPTORS);
|
|
|
|
|
|
mmio_write(i825xx_dev, REG_RCTL,
|
|
(RCTL_SBP | RCTL_UPE | RCTL_MPE | RCTL_RDMTS_HALF | RCTL_SECRC |
|
|
RCTL_LPE | RCTL_BAM | RCTL_BSIZE_8192) );
|
|
|
|
|
|
|
|
i825xx_dev->tx_front = 0;
|
|
|
|
char *pagestx = mem_alloc_pages(NUM_TX_DESCRIPTORS * 16 / PAGE_SIZE);
|
|
|
|
i825xx_dev->tx_desc = mem_pci_sbrk(NUM_TX_DESCRIPTORS * 16);
|
|
|
|
for (i=0;i<(NUM_TX_DESCRIPTORS * 16 / PAGE_SIZE);i++) {
|
|
mem_map_page(pagestx + (i * PAGE_SIZE), (char *)i825xx_dev->tx_desc + (i * PAGE_SIZE), 3);
|
|
}
|
|
|
|
i825xx_dev->tx_desc_base = pagestx;
|
|
|
|
for (i=0;i<NUM_TX_DESCRIPTORS;i++) {
|
|
unsigned int packet_buff = mem_pci_sbrk(8192);
|
|
unsigned char *loc = mem_alloc_pages(2);
|
|
|
|
mem_map_page(loc, packet_buff, 3);
|
|
mem_map_page(loc + PAGE_SIZE, packet_buff + PAGE_SIZE, 3);
|
|
|
|
i825xx_dev->tx_desc[i].address = (unsigned long long)loc;
|
|
i825xx_dev->tx_buff[i] = packet_buff;
|
|
i825xx_dev->tx_desc[i].cmd = 0;
|
|
i825xx_dev->tx_desc[i].length = 0;
|
|
}
|
|
|
|
mmio_write(i825xx_dev, REG_TDBAL, i825xx_dev->tx_desc_base);
|
|
mmio_write(i825xx_dev, REG_TDBAH, 0);
|
|
mmio_write(i825xx_dev, REG_TDLEN, NUM_TX_DESCRIPTORS * 16);
|
|
|
|
mmio_write(i825xx_dev, REG_TDH, 0);
|
|
mmio_write(i825xx_dev, REG_TDT, NUM_TX_DESCRIPTORS);
|
|
|
|
ether_dev->data = (char *)i825xx_dev;
|
|
ether_dev->type = 1;
|
|
ether_dev->state = 0;
|
|
i825xx_dev->ether = ether_dev;
|
|
kprintf("ETHER: Found i825xx Ethernet Controller MAC %x:%x:%x:%x:%x:%x IRQ %d\n", ether_dev->mac[0], ether_dev->mac[1], ether_dev->mac[2], ether_dev->mac[3], ether_dev->mac[4], ether_dev->mac[5], pci_dev->irq);
|
|
|
|
return ether_dev;
|
|
|
|
}
|
|
|
|
void i825xx_enable(struct ether_t *ether) {
|
|
struct i825xx_device_t *i825xx_device = (struct i825xx_device_t *)ether->data;
|
|
|
|
mmio_write(i825xx_device, REG_TCTL, mmio_read(i825xx_device, REG_TCTL) | TCTL_EN | TCTL_PSP);
|
|
mmio_write(i825xx_device, REG_RCTL, mmio_read(i825xx_device, REG_RCTL) | RCTL_EN);
|
|
|
|
ether->state = mmio_read(i825xx_device, REG_STATUS) & STATUS_LU ? 1 : 0;
|
|
}
|
|
|
|
void i825xx_disable(struct ether_t *ether) {
|
|
struct i825xx_device_t *i825xx_device = (struct i825xx_device_t *)ether->data;
|
|
|
|
mmio_write(i825xx_device, REG_TCTL, mmio_read(i825xx_device, REG_TCTL) & ~(TCTL_EN | TCTL_PSP));
|
|
mmio_write(i825xx_device, REG_RCTL, mmio_read(i825xx_device, REG_RCTL) & ~(RCTL_EN));
|
|
|
|
ether->state = 0;
|
|
}
|