quinn-os/i825xx.c
Andrew Pamment 0d5c86649b Fixes
2021-11-26 21:06:12 +10:00

434 lines
15 KiB
C

/* This file is a heavily modified version of Joshua Cornutt's intel driver
* the original license can be found following...
*/
/*
* Copyright (c) 2011 Joshua Cornutt <jcornutt@randomaccessit.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#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_TIPG 0x0410
#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) {
if (net_device->bar_type == 0) {
return *(volatile unsigned int *)(net_device->mmio_address + reg);
} else {
outportl(net_device->io_address, reg);
return inportl(net_device->io_address + 4);
}
}
static void mmio_write(struct i825xx_device_t *net_device, unsigned int reg, unsigned int value) {
if (net_device->bar_type == 0) {
*(volatile unsigned int *)(net_device->mmio_address + reg) = value;
} else {
outportl(net_device->io_address, reg);
outportl(net_device->io_address + 4, value);
}
}
static unsigned short net_eeprom_read(struct i825xx_device_t *net_device, unsigned char addr) {
unsigned int tmp = 0;
unsigned short data;
if (net_device->have_eeprom) {
mmio_write(REG_EERD, (1) | ((unsigned int) (addr) << 8));
while (!((tmp = mmio_read(REG_EERD)) & (1 << 4)));
} else {
mmio_write(REG_EERD, (1) | ((unsigned int) (addr) << 2));
while (!((tmp = mmio_read(REG_EERD)) & ( 1 << 1)));
}
mmio_write(net_device, REG_EERD, EERD_START | (((unsigned int) (addr)) << 8));
data = (unsigned short)((tmp >> 16) & 0xffff);
return data;
}
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) {
i825xx_device->tx_desc[i825xx_device->tx_front]->addr = (unsigned long long)packet;
i825xx_device->tx_desc[i825xx_device->tx_front]->length = len;
i825xx_device->tx_desc[i825xx_device->tx_front]->cmd = TX_DESC_CMD_EOP | TX_DESC_CMD_IFCS | TX_DESC_CMD_RS;
i825xx_device->tx_desc[i825xx_device->tx_front]->sta = 0;
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 & 0xFF));
return i825xx_device->tx_desc[old_tx_front]->sta & TX_DESC_STATUS_DD ? 0 : -1;
}
unsigned char i825xx_detect_eeprom() {
unsigned int val = 0;
unsigned char eeprom_exists = 0;
mmio_write(REG_EERD, 0x1);
for (int i = 0; i < 1000 && !eeprom_exists;i++) {
val = mmio_read(REG_EERD);
if (val & 0x10) {
eeprom_exists = 1;
} else {
eeprom_exists = 0;
}
}
return eeprom_exists;
}
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)) {
if (!pci_find_device_by_vendor(0x8086, 0x153A, &pci_dev, count)) {
if (!pci_find_device_by_vendor(0x8086, 0x10EA, &pci_dev, count)) {
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));
// if not memory mapped
if (pci_dev->base[0] & 0x1) {
i825xx_dev->bar_type = 1;
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;
} else {
i825xx_dev->bar_type = 0;
i825xx_dev->io_address = pci_dev->base[0] & 0xfffffffc;
}
i825xx_dev->have_eeprom = i825xx_detect_eeprom();
if (i825xx_dev->have_eeprom) {
unsigned short temp;
temp = net_eeprom_read(0);
mac[0] = temp & 0xff;
mac[1] = temp >> 8;
temp = net_eeprom_read(1);
mac[2] = temp & 0xff;
mac[3] = temp >> 8;
temp = net_eeprom_read(2);
mac[4] = temp & 0xff;
mac[5] = temp >> 8;
} else {
unsigned char *mem_base_mac_8 = (unsigned char *)(mem_base + 0x5400);
unsigned int *mem_base_mac_32 = (unsigned int *)(mem_base + 0x5400);
if (mem_base_mac_32[0] != 0) {
for (int i = 0; i < 6; i++) {
mac[i] = mem_base_mac_8[i];
}
} else {
free(i825xx_dev);
free(ether_dev);
return (void *)0;
}
}
char *pagesrx = mem_alloc_pages((NUM_RX_DESCRIPTORS * sizeof(struct i825xx_rx_desc_t) + 16) / PAGE_SIZE);
unsigned char ptr = mem_pci_sbrk(NUM_RX_DESCRIPTORS * sizeof(struct i825xx_rx_desc_t) + 16);
i825xx_dev->rx_buff = (struct i825xx_rx_desc_t *)ptr;
for (i=0;i<(NUM_RX_DESCRIPTORS * sizeof(struct i825xx_rx_desc_t) + 16) / PAGE_SIZE;i++) {
mem_map_page(pagesrx + (i * PAGE_SIZE), (char *)i825xx_dev->rx_desc + (i * PAGE_SIZE), 3);
}
for (i = 0; i < NUM_RX_DESCRIPTORS; i++) {
i825xx_dev->rx_desc[i] = (struct i825xx_rx_desc_t *)((unsigned char *)i825xx_dev->rx_buff + i * 16);
i825xx_dev->rx_desc[i]->address = (unsigned long long)(unsigned char *)(malloc(8192 + 16));
i825xx_dev->rx_desc[i]->status = 0;
}
mmio_write(i825xx_dev, REG_TDBAL, (unsigned int)((unsigned long long)ptr >> 32));
mmio_write(i825xx_dev, REG_TDBAH, (unsigned int)((unsigned long long)ptr & 0xFFFFFFFF));
mmio_write(i825xx_dev, REG_RDBAL, (unsigned long long)ptr);
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 - 1);
i825xx_dev->rx_front = 0;
mmio_write(i825xx_dev, REG_RCTRL, RCTL_SBP| RCTL_UPE | RCTL_MPE | RCTL_LBM_OFF | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_8192);
pagesrx = mem_alloc_pages((NUM_TX_DESCRIPTORS * sizeof(struct i825xx_tx_desc_t) + 16) / PAGE_SIZE);
ptr = mem_pci_sbrk(NUM_TX_DESCRIPTORS * sizeof(struct i825xx_tx_desc_t) + 16);
i825xx_dev->tx_buff = (struct i825xx_tx_desc_t *)ptr;
for(i = 0; i < NUM_TX_DESCRIPTORS; i++)
{
tx_descs[i] = (struct i825xx_tx_desc_t *)((unsigned char*)i825xx_dev->tx_buff + i*16);
tx_descs[i]->addr = 0;
tx_descs[i]->cmd = 0;
tx_descs[i]->status = TX_DESC_STATUS_DD;
}
mmio_write(i825xx_dev, REG_TDBAH, (unsigned int)((unsigned long long)ptr >> 32) );
mmio_write(i825xx_dev, REG_TDBAL, (unsigned int)((unsigned long long)ptr & 0xFFFFFFFF));
//now setup total length of descriptors
mmio_write(i825xx_dev, REG_TDLEN, NUM_TX_DESCRIPTORS * 16);
//setup numbers
mmio_write(i825xx_dev, REG_TDH, 0);
mmio_write(i825xx_dev, REG_TDT, 0);
i825xx_dev->tx_front = 0;
mmio_write(i825xx_dev, REG_TCTRL, (15 << TCTL_CT_SHIFT)
| (64 << TCTL_COLD_SHIFT)
| TCTL_RTLC);
// This line of code overrides the one before it but I left both to highlight that the previous one works with e1000 cards, but for the e1000e cards
// you should set the TCTRL register as follows. For detailed description of each bit, please refer to the Intel Manual.
// In the case of I217 and 82577LM packets will not be sent if the TCTRL is not configured using the following bits.
mmio_write(i825xx_dev, REG_TCTRL, 0b0110000000000111111000011111010);
mmio_write(i825xx_dev, REG_TIPG, 0x0060200A);
if (!irq_install_handler(pci_dev->irq, i825xx_isr, 0)) {
kprintf("Failed to install IRQ handler...\n");
free(i825xx_dev);
free(ether_dev);
return (void *)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);
ether_dev->data = (char *)i825xx_dev;
ether_dev->type = 1;
ether_dev->state = 0;
ether_dev->default_gw = 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;
}