diff options
Diffstat (limited to 'sys/dev/phy/e1000.c')
-rw-r--r-- | sys/dev/phy/e1000.c | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/sys/dev/phy/e1000.c b/sys/dev/phy/e1000.c index c9782a5..9f1b492 100644 --- a/sys/dev/phy/e1000.c +++ b/sys/dev/phy/e1000.c @@ -34,6 +34,9 @@ #include <sys/mmio.h> #include <dev/phy/e1000regs.h> #include <dev/pci/pci.h> +#include <dev/pci/pciregs.h> +#include <dev/timer.h> +#include <net/if_var.h> #include <string.h> #define pr_trace(fmt, ...) kprintf("e1000: " fmt, ##__VA_ARGS__) @@ -41,15 +44,50 @@ #define E1000_VENDOR 0x8086 #define E1000_DEVICE 0x100E +#define E1000_TIMEOUT 500 /* In msec */ +static struct timer tmr; static struct pci_device *e1000; +static struct netif netif; struct e1000_nic { void *vap; uint8_t has_eeprom : 1; uint16_t eeprom_size; + uint16_t io_port; }; +static int +e1000_poll_reg(volatile uint32_t *reg, uint32_t bits, bool pollset) +{ + size_t usec_start, usec; + size_t elapsed_msec; + uint32_t val; + bool tmp; + + usec_start = tmr.get_time_usec(); + + for (;;) { + val = mmio_read32(reg); + tmp = (pollset) ? ISSET(val, bits) : !ISSET(val, bits); + + usec = tmr.get_time_usec(); + elapsed_msec = (usec - usec_start) / 1000; + + /* If tmp is set, the register updated in time */ + if (tmp) { + break; + } + + /* Exit with an error if we timeout */ + if (elapsed_msec > E1000_TIMEOUT) { + return -ETIME; + } + } + + return 0; +} + /* * Query information about any EEPROMs for diagnostic * purposes. @@ -91,12 +129,138 @@ eeprom_query(struct e1000_nic *np) } /* + * If there is no EEPROM, we can still read + * the MAC address through the Receive address + * registers + * + * XXX: This is typically only used as a fallback. + * + * Returns a less than zero value if an ethernet + * address is not found, which would be kind of + * not good. + * + * @np: NIC descriptor + * @addr: Pointer to MAC address data + */ +static int +e1000_read_recvaddr(struct e1000_nic *np, struct netif_addr *addr) +{ + const uint32_t RECVADDR_OFF = 0x5400; + uint32_t tmp; + uint32_t *dword_p; + + dword_p = PTR_OFFSET(np->vap, RECVADDR_OFF); + + if (dword_p[0] == 0) { + pr_error("bad hwaddr in recvaddr\n"); + return -ENOTSUP; + } + + /* DWORD 0 */ + tmp = mmio_read32(&dword_p[0]); + addr->data[0] = tmp & 0xFF; + addr->data[1] = (tmp >> 8) & 0xFF; + addr->data[2] = (tmp >> 16) & 0xFF; + addr->data[3] = (tmp >> 24) & 0xFF; + + /* DWORD 1 */ + tmp = mmio_read32(&dword_p[1]); + addr->data[4] = tmp & 0xFF; + addr->data[5] = (tmp >> 8) & 0xFF; + return 0; +} + +/* + * Read 16-bytes from the NIC's on-board EEPROM. + * + * XXX: This should only be used if the caller is + * certain that the NIC has an EEPROM + * + * @addr: EEPROM address to read from + * + * A returned value of 0xFFFF should be seen as invalid. + */ +static uint16_t +eeprom_readw(struct e1000_nic *np, uint8_t addr) +{ + uint32_t eerd, *eerd_p; + int error; + + if (!np->has_eeprom) { + pr_error("e1000_read_eeprom: EEPROM not present\n"); + return 0xFFFF; + } + + eerd_p = PTR_OFFSET(np->vap, E1000_EERD); + eerd = (addr << 8) | E1000_EERD_START; + mmio_write32(eerd_p, eerd); + + error = e1000_poll_reg(eerd_p, E1000_EERD_DONE, true); + if (error < 0) { + pr_error("e1000_read_eeprom: timeout\n"); + return 0xFFFF; + } + + eerd = mmio_read32(eerd_p); + return (eerd >> 16) & 0xFFFF; +} + +/* + * Read the MAC address from the NICs EEPROM. + * + * XXX: This should usually work, however if the NIC does + * not have an on-board EEPROM, this will fail. In such + * cases, e1000_read_recvaddr() can be called instead. + * + * @np: NIC descriptor + * @addr: Pointer to MAC address data + */ +static int +e1000_read_macaddr(struct e1000_nic *np, struct netif_addr *addr) +{ + uint16_t eeprom_word; + + if (!np->has_eeprom) { + pr_trace("EEPROM not present, trying recvaddr\n"); + return e1000_read_recvaddr(np, addr); + } + + /* Word 0 */ + eeprom_word = eeprom_readw(np, E1000_HWADDR0); + addr->data[0] = (eeprom_word & 0xFF); + addr->data[1] = (eeprom_word >> 8) & 0xFF; + + /* Word 1 */ + eeprom_word = eeprom_readw(np, E1000_HWADDR1); + addr->data[2] = (eeprom_word & 0xFF); + addr->data[3] = (eeprom_word >> 8) & 0xFF; + + /* Word 2 */ + eeprom_word = eeprom_readw(np, E1000_HWADDR2); + addr->data[4] = (eeprom_word & 0xFF); + addr->data[5] = (eeprom_word >> 8) & 0xFF; + return 0; +} + +/* * Initialize an E1000(e) chip */ static int e1000_chip_init(struct e1000_nic *np) { + struct netif_addr *addr = &netif.addr; + int error; + eeprom_query(np); + if ((error = e1000_read_macaddr(np, addr)) < 0) { + return error; + } + + pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n", + (uint64_t)addr->data[0], (uint64_t)addr->data[1], + (uint64_t)addr->data[2], (uint64_t)addr->data[3], + (uint64_t)addr->data[4], (uint64_t)addr->data[5]); + return 0; } @@ -128,6 +292,18 @@ e1000_init(void) return -ENODEV; } + /* Get a GP timer */ + if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) { + pr_error("failed to fetch general purpose timer\n"); + return -ENODEV; + } + + /* We need msleep() */ + if (tmr.msleep == NULL) { + pr_error("general purpose timer has no msleep()\n"); + return -ENODEV; + } + memset(&nic, 0, sizeof(nic)); pr_trace("e1000 at pci%d:%x.%x.%d\n", e1000->bus, e1000->device_id, e1000->func, |