xref: /minix3/minix/drivers/net/e1000/e1000.c (revision d4dd6511b9dc029206ce2c925ed135e16555aae4)
1107df7c8SDavid van Moolenbroek /* A device driver for Intel Pro/1000 Gigabit Ethernet Controllers. */
2433d6423SLionel Sambuc 
3433d6423SLionel Sambuc #include <minix/drivers.h>
4433d6423SLionel Sambuc #include <minix/netdriver.h>
5433d6423SLionel Sambuc #include <machine/pci.h>
6433d6423SLionel Sambuc #include <sys/mman.h>
7433d6423SLionel Sambuc #include "assert.h"
8433d6423SLionel Sambuc #include "e1000.h"
9433d6423SLionel Sambuc #include "e1000_hw.h"
10433d6423SLionel Sambuc #include "e1000_reg.h"
11433d6423SLionel Sambuc #include "e1000_pci.h"
12433d6423SLionel Sambuc 
13f7df02e7SDavid van Moolenbroek static int e1000_init(unsigned int instance, netdriver_addr_t *addr,
14f7df02e7SDavid van Moolenbroek 	uint32_t *caps, unsigned int *ticks);
151ad10e3aSDavid van Moolenbroek static void e1000_stop(void);
16f7df02e7SDavid van Moolenbroek static void e1000_set_mode(unsigned int, const netdriver_addr_t *,
17f7df02e7SDavid van Moolenbroek 	unsigned int);
18f7df02e7SDavid van Moolenbroek static void e1000_set_hwaddr(const netdriver_addr_t *);
191ad10e3aSDavid van Moolenbroek static int e1000_send(struct netdriver_data *data, size_t size);
201ad10e3aSDavid van Moolenbroek static ssize_t e1000_recv(struct netdriver_data *data, size_t max);
21f7df02e7SDavid van Moolenbroek static unsigned int e1000_get_link(uint32_t *);
221ad10e3aSDavid van Moolenbroek static void e1000_intr(unsigned int mask);
23f7df02e7SDavid van Moolenbroek static void e1000_tick(void);
24433d6423SLionel Sambuc static int e1000_probe(e1000_t *e, int skip);
25f7df02e7SDavid van Moolenbroek static void e1000_init_hw(e1000_t *e, netdriver_addr_t *addr);
26433d6423SLionel Sambuc static uint32_t e1000_reg_read(e1000_t *e, uint32_t reg);
27433d6423SLionel Sambuc static void e1000_reg_write(e1000_t *e, uint32_t reg, uint32_t value);
28433d6423SLionel Sambuc static void e1000_reg_set(e1000_t *e, uint32_t reg, uint32_t value);
29433d6423SLionel Sambuc static void e1000_reg_unset(e1000_t *e, uint32_t reg, uint32_t value);
301ad10e3aSDavid van Moolenbroek static u16_t eeprom_eerd(e1000_t *e, int reg);
311ad10e3aSDavid van Moolenbroek static u16_t eeprom_ich(e1000_t *e, int reg);
32433d6423SLionel Sambuc static int eeprom_ich_init(e1000_t *e);
33107df7c8SDavid van Moolenbroek static int eeprom_ich_cycle(e1000_t *e, u32_t timeout);
34433d6423SLionel Sambuc 
351ad10e3aSDavid van Moolenbroek static int e1000_instance;
361ad10e3aSDavid van Moolenbroek static e1000_t e1000_state;
371ad10e3aSDavid van Moolenbroek 
381ad10e3aSDavid van Moolenbroek static const struct netdriver e1000_table = {
39f7df02e7SDavid van Moolenbroek 	.ndr_name	= "em",
401ad10e3aSDavid van Moolenbroek 	.ndr_init	= e1000_init,
411ad10e3aSDavid van Moolenbroek 	.ndr_stop	= e1000_stop,
42f7df02e7SDavid van Moolenbroek 	.ndr_set_mode	= e1000_set_mode,
43f7df02e7SDavid van Moolenbroek 	.ndr_set_hwaddr	= e1000_set_hwaddr,
441ad10e3aSDavid van Moolenbroek 	.ndr_recv	= e1000_recv,
451ad10e3aSDavid van Moolenbroek 	.ndr_send	= e1000_send,
46f7df02e7SDavid van Moolenbroek 	.ndr_get_link	= e1000_get_link,
471ad10e3aSDavid van Moolenbroek 	.ndr_intr	= e1000_intr,
48f7df02e7SDavid van Moolenbroek 	.ndr_tick	= e1000_tick
491ad10e3aSDavid van Moolenbroek };
50433d6423SLionel Sambuc 
51107df7c8SDavid van Moolenbroek /*
52107df7c8SDavid van Moolenbroek  * The e1000 driver.
53107df7c8SDavid van Moolenbroek  */
54107df7c8SDavid van Moolenbroek int
main(int argc,char * argv[])55107df7c8SDavid van Moolenbroek main(int argc, char * argv[])
56433d6423SLionel Sambuc {
57433d6423SLionel Sambuc 
58433d6423SLionel Sambuc 	env_setargs(argc, argv);
59433d6423SLionel Sambuc 
601ad10e3aSDavid van Moolenbroek 	/* Let the netdriver library take control. */
611ad10e3aSDavid van Moolenbroek 	netdriver_task(&e1000_table);
62433d6423SLionel Sambuc 
631ad10e3aSDavid van Moolenbroek 	return 0;
64433d6423SLionel Sambuc }
65433d6423SLionel Sambuc 
66107df7c8SDavid van Moolenbroek /*
671ad10e3aSDavid van Moolenbroek  * Initialize the e1000 driver and device.
68107df7c8SDavid van Moolenbroek  */
69107df7c8SDavid van Moolenbroek static int
e1000_init(unsigned int instance,netdriver_addr_t * addr,uint32_t * caps,unsigned int * ticks)70f7df02e7SDavid van Moolenbroek e1000_init(unsigned int instance, netdriver_addr_t * addr, uint32_t * caps,
71f7df02e7SDavid van Moolenbroek 	unsigned int * ticks)
72433d6423SLionel Sambuc {
731ad10e3aSDavid van Moolenbroek 	e1000_t *e;
74433d6423SLionel Sambuc 	int r;
75433d6423SLionel Sambuc 
761ad10e3aSDavid van Moolenbroek 	e1000_instance = instance;
77433d6423SLionel Sambuc 
78433d6423SLionel Sambuc 	/* Clear state. */
79433d6423SLionel Sambuc 	memset(&e1000_state, 0, sizeof(e1000_state));
80433d6423SLionel Sambuc 
811ad10e3aSDavid van Moolenbroek 	e = &e1000_state;
821ad10e3aSDavid van Moolenbroek 
83433d6423SLionel Sambuc 	/* Perform calibration. */
84433d6423SLionel Sambuc 	if ((r = tsc_calibrate()) != OK)
85433d6423SLionel Sambuc 		panic("tsc_calibrate failed: %d", r);
86433d6423SLionel Sambuc 
871ad10e3aSDavid van Moolenbroek 	/* See if we can find a matching device. */
881ad10e3aSDavid van Moolenbroek 	if (!e1000_probe(e, instance))
891ad10e3aSDavid van Moolenbroek 		return ENXIO;
901ad10e3aSDavid van Moolenbroek 
911ad10e3aSDavid van Moolenbroek 	/* Initialize the hardware, and return its ethernet address. */
921ad10e3aSDavid van Moolenbroek 	e1000_init_hw(e, addr);
93433d6423SLionel Sambuc 
94f7df02e7SDavid van Moolenbroek 	*caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST | NDEV_CAP_HWADDR;
95f7df02e7SDavid van Moolenbroek 	*ticks = sys_hz() / 10; /* update statistics 10x/sec */
96107df7c8SDavid van Moolenbroek 	return OK;
97433d6423SLionel Sambuc }
98433d6423SLionel Sambuc 
99107df7c8SDavid van Moolenbroek /*
1001ad10e3aSDavid van Moolenbroek  * Map flash memory.  This step is optional.
101107df7c8SDavid van Moolenbroek  */
102107df7c8SDavid van Moolenbroek static void
e1000_map_flash(e1000_t * e,int devind,int did)1031ad10e3aSDavid van Moolenbroek e1000_map_flash(e1000_t * e, int devind, int did)
104433d6423SLionel Sambuc {
1051ad10e3aSDavid van Moolenbroek 	u32_t flash_addr, gfpreg, sector_base_addr;
1061ad10e3aSDavid van Moolenbroek 	size_t flash_size;
107107df7c8SDavid van Moolenbroek 
1081ad10e3aSDavid van Moolenbroek 	/* The flash memory is pointed to by BAR2.  It may not be present. */
1091ad10e3aSDavid van Moolenbroek 	if ((flash_addr = pci_attr_r32(devind, PCI_BAR_2)) == 0)
110433d6423SLionel Sambuc 		return;
1111ad10e3aSDavid van Moolenbroek 
1121ad10e3aSDavid van Moolenbroek 	/* The default flash size. */
1131ad10e3aSDavid van Moolenbroek 	flash_size = 0x10000;
1141ad10e3aSDavid van Moolenbroek 
1151ad10e3aSDavid van Moolenbroek 	switch (did) {
1161ad10e3aSDavid van Moolenbroek 	case E1000_DEV_ID_82540EM:
1171ad10e3aSDavid van Moolenbroek 	case E1000_DEV_ID_82545EM:
1181ad10e3aSDavid van Moolenbroek 	case E1000_DEV_ID_82540EP:
119e1131d9cSThomas Goering 	case E1000_DEV_ID_82540EP_LP:
1201ad10e3aSDavid van Moolenbroek 		return; /* don't even try */
1211ad10e3aSDavid van Moolenbroek 
1221ad10e3aSDavid van Moolenbroek 	/* 82566/82567/82562V series support mapping 4kB of flash memory. */
1231ad10e3aSDavid van Moolenbroek 	case E1000_DEV_ID_ICH10_D_BM_LM:
1241ad10e3aSDavid van Moolenbroek 	case E1000_DEV_ID_ICH10_R_BM_LF:
1251ad10e3aSDavid van Moolenbroek 		flash_size = 0x1000;
1261ad10e3aSDavid van Moolenbroek 		break;
127433d6423SLionel Sambuc 	}
128107df7c8SDavid van Moolenbroek 
1291ad10e3aSDavid van Moolenbroek 	e->flash = vm_map_phys(SELF, (void *)flash_addr, flash_size);
1301ad10e3aSDavid van Moolenbroek 	if (e->flash == MAP_FAILED)
1311ad10e3aSDavid van Moolenbroek 		panic("e1000: couldn't map in flash");
132433d6423SLionel Sambuc 
1331ad10e3aSDavid van Moolenbroek 	/* sector_base_addr is a "sector"-aligned address (4096 bytes). */
1341ad10e3aSDavid van Moolenbroek 	gfpreg = E1000_READ_FLASH_REG(e, ICH_FLASH_GFPREG);
1351ad10e3aSDavid van Moolenbroek 	sector_base_addr = gfpreg & FLASH_GFPREG_BASE_MASK;
136433d6423SLionel Sambuc 
1371ad10e3aSDavid van Moolenbroek 	/* flash_base_addr is byte-aligned. */
1381ad10e3aSDavid van Moolenbroek 	e->flash_base_addr = sector_base_addr << FLASH_SECTOR_ADDR_SHIFT;
139433d6423SLionel Sambuc }
140433d6423SLionel Sambuc 
141107df7c8SDavid van Moolenbroek /*
142107df7c8SDavid van Moolenbroek  * Find a matching device.  Return TRUE on success.
143107df7c8SDavid van Moolenbroek  */
144107df7c8SDavid van Moolenbroek static int
e1000_probe(e1000_t * e,int skip)145107df7c8SDavid van Moolenbroek e1000_probe(e1000_t * e, int skip)
146433d6423SLionel Sambuc {
147433d6423SLionel Sambuc 	int r, devind, ioflag;
148433d6423SLionel Sambuc 	u16_t vid, did, cr;
1491ad10e3aSDavid van Moolenbroek 	u32_t status;
150433d6423SLionel Sambuc 	u32_t base, size;
151f7df02e7SDavid van Moolenbroek 	const char *dname;
152433d6423SLionel Sambuc 
153f7df02e7SDavid van Moolenbroek 	E1000_DEBUG(3, ("%s: probe()\n", netdriver_name()));
154433d6423SLionel Sambuc 
1551ad10e3aSDavid van Moolenbroek 	/* Initialize communication to the PCI driver. */
1561ad10e3aSDavid van Moolenbroek 	pci_init();
1571ad10e3aSDavid van Moolenbroek 
158107df7c8SDavid van Moolenbroek 	/* Attempt to iterate the PCI bus. Start at the beginning. */
159433d6423SLionel Sambuc 	if ((r = pci_first_dev(&devind, &vid, &did)) == 0)
160433d6423SLionel Sambuc 		return FALSE;
161107df7c8SDavid van Moolenbroek 
162433d6423SLionel Sambuc 	/* Loop devices on the PCI bus. */
163107df7c8SDavid van Moolenbroek 	while (skip--) {
164433d6423SLionel Sambuc 		E1000_DEBUG(3, ("%s: probe() devind %d vid 0x%x did 0x%x\n",
165f7df02e7SDavid van Moolenbroek 		    netdriver_name(), devind, vid, did));
166433d6423SLionel Sambuc 
167433d6423SLionel Sambuc 		if (!(r = pci_next_dev(&devind, &vid, &did)))
168433d6423SLionel Sambuc 			return FALSE;
169433d6423SLionel Sambuc 	}
170107df7c8SDavid van Moolenbroek 
171107df7c8SDavid van Moolenbroek 	/* We found a matching card.  Set card-specific properties. */
172433d6423SLionel Sambuc 	e->eeprom_read = eeprom_eerd;
173433d6423SLionel Sambuc 
174107df7c8SDavid van Moolenbroek 	switch (did) {
175433d6423SLionel Sambuc 	case E1000_DEV_ID_ICH10_D_BM_LM:
176433d6423SLionel Sambuc 	case E1000_DEV_ID_ICH10_R_BM_LF:
177433d6423SLionel Sambuc 		e->eeprom_read = eeprom_ich;
178433d6423SLionel Sambuc 		break;
179433d6423SLionel Sambuc 
180433d6423SLionel Sambuc 	case E1000_DEV_ID_82540EM:
181433d6423SLionel Sambuc 	case E1000_DEV_ID_82545EM:
182e1131d9cSThomas Goering 	case E1000_DEV_ID_82540EP_LP:
183433d6423SLionel Sambuc 		e->eeprom_done_bit = (1 << 4);
184433d6423SLionel Sambuc 		e->eeprom_addr_off = 8;
185433d6423SLionel Sambuc 		break;
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc 	default:
188433d6423SLionel Sambuc 		e->eeprom_done_bit = (1 << 1);
189433d6423SLionel Sambuc 		e->eeprom_addr_off = 2;
190433d6423SLionel Sambuc 		break;
191433d6423SLionel Sambuc 	}
192433d6423SLionel Sambuc 
193433d6423SLionel Sambuc 	/* Inform the user about the new card. */
194433d6423SLionel Sambuc 	if (!(dname = pci_dev_name(vid, did)))
195433d6423SLionel Sambuc 		dname = "Intel Pro/1000 Gigabit Ethernet Card";
1961ad10e3aSDavid van Moolenbroek 	E1000_DEBUG(1, ("%s: %s (%04x/%04x) at %s\n",
197f7df02e7SDavid van Moolenbroek 	    netdriver_name(), dname, vid, did, pci_slot_name(devind)));
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc 	/* Reserve PCI resources found. */
2001ad10e3aSDavid van Moolenbroek 	pci_reserve(devind);
201107df7c8SDavid van Moolenbroek 
202433d6423SLionel Sambuc 	/* Read PCI configuration. */
203433d6423SLionel Sambuc 	e->irq = pci_attr_r8(devind, PCI_ILR);
204433d6423SLionel Sambuc 
205433d6423SLionel Sambuc 	if ((r = pci_get_bar(devind, PCI_BAR, &base, &size, &ioflag)) != OK)
206107df7c8SDavid van Moolenbroek 		panic("failed to get PCI BAR: %d", r);
207107df7c8SDavid van Moolenbroek 	if (ioflag)
208107df7c8SDavid van Moolenbroek 		panic("PCI BAR is not for memory");
209433d6423SLionel Sambuc 
210107df7c8SDavid van Moolenbroek 	if ((e->regs = vm_map_phys(SELF, (void *)base, size)) == MAP_FAILED)
211433d6423SLionel Sambuc 		panic("failed to map hardware registers from PCI");
212433d6423SLionel Sambuc 
213107df7c8SDavid van Moolenbroek 	/* Enable DMA bus mastering if necessary. */
214433d6423SLionel Sambuc 	cr = pci_attr_r16(devind, PCI_CR);
215433d6423SLionel Sambuc 	if (!(cr & PCI_CR_MAST_EN))
216433d6423SLionel Sambuc 		pci_attr_w16(devind, PCI_CR, cr | PCI_CR_MAST_EN);
217433d6423SLionel Sambuc 
218433d6423SLionel Sambuc 	/* Optionally map flash memory. */
2191ad10e3aSDavid van Moolenbroek 	e1000_map_flash(e, devind, did);
220107df7c8SDavid van Moolenbroek 
221107df7c8SDavid van Moolenbroek 	/* Output debug information. */
2221ad10e3aSDavid van Moolenbroek 	status = e1000_reg_read(e, E1000_REG_STATUS);
223f7df02e7SDavid van Moolenbroek 	E1000_DEBUG(3, ("%s: MEM at %p, IRQ %d\n", netdriver_name(),
224f7df02e7SDavid van Moolenbroek 	    e->regs, e->irq));
225f7df02e7SDavid van Moolenbroek 	E1000_DEBUG(3, ("%s: link %s, %s duplex\n", netdriver_name(),
2261ad10e3aSDavid van Moolenbroek 	    status & 3 ? "up"   : "down", status & 1 ? "full" : "half"));
227107df7c8SDavid van Moolenbroek 
228433d6423SLionel Sambuc 	return TRUE;
229433d6423SLionel Sambuc }
230433d6423SLionel Sambuc 
231107df7c8SDavid van Moolenbroek /*
2321ad10e3aSDavid van Moolenbroek  * Reset the card.
233107df7c8SDavid van Moolenbroek  */
2341ad10e3aSDavid van Moolenbroek static void
e1000_reset_hw(e1000_t * e)2351ad10e3aSDavid van Moolenbroek e1000_reset_hw(e1000_t * e)
2361ad10e3aSDavid van Moolenbroek {
2371ad10e3aSDavid van Moolenbroek 
2381ad10e3aSDavid van Moolenbroek 	/* Assert a Device Reset signal. */
2391ad10e3aSDavid van Moolenbroek 	e1000_reg_set(e, E1000_REG_CTRL, E1000_REG_CTRL_RST);
2401ad10e3aSDavid van Moolenbroek 
2411ad10e3aSDavid van Moolenbroek 	/* Wait one microsecond. */
242*d4dd6511Srlfnb 	micro_delay(16000);
2431ad10e3aSDavid van Moolenbroek }
2441ad10e3aSDavid van Moolenbroek 
2451ad10e3aSDavid van Moolenbroek /*
2461ad10e3aSDavid van Moolenbroek  * Initialize and return the card's ethernet address.
2471ad10e3aSDavid van Moolenbroek  */
2481ad10e3aSDavid van Moolenbroek static void
e1000_init_addr(e1000_t * e,netdriver_addr_t * addr)249f7df02e7SDavid van Moolenbroek e1000_init_addr(e1000_t * e, netdriver_addr_t * addr)
2501ad10e3aSDavid van Moolenbroek {
2511ad10e3aSDavid van Moolenbroek 	static char eakey[] = E1000_ENVVAR "#_EA";
2521ad10e3aSDavid van Moolenbroek 	static char eafmt[] = "x:x:x:x:x:x";
2531ad10e3aSDavid van Moolenbroek 	u16_t word;
2541ad10e3aSDavid van Moolenbroek 	int i;
2551ad10e3aSDavid van Moolenbroek 	long v;
2561ad10e3aSDavid van Moolenbroek 
2571ad10e3aSDavid van Moolenbroek 	/* Do we have a user defined ethernet address? */
2581ad10e3aSDavid van Moolenbroek 	eakey[sizeof(E1000_ENVVAR)-1] = '0' + e1000_instance;
2591ad10e3aSDavid van Moolenbroek 
2601ad10e3aSDavid van Moolenbroek 	for (i = 0; i < 6; i++) {
2611ad10e3aSDavid van Moolenbroek 		if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET)
2621ad10e3aSDavid van Moolenbroek 			break;
2631ad10e3aSDavid van Moolenbroek 		else
264f7df02e7SDavid van Moolenbroek 			addr->na_addr[i] = v;
2651ad10e3aSDavid van Moolenbroek 	}
2661ad10e3aSDavid van Moolenbroek 
2671ad10e3aSDavid van Moolenbroek 	/* If that fails, read Ethernet Address from EEPROM. */
2681ad10e3aSDavid van Moolenbroek 	if (i != 6) {
2691ad10e3aSDavid van Moolenbroek 		for (i = 0; i < 3; i++) {
2701ad10e3aSDavid van Moolenbroek 			word = e->eeprom_read(e, i);
271f7df02e7SDavid van Moolenbroek 			addr->na_addr[i * 2]     = (word & 0x00ff);
272f7df02e7SDavid van Moolenbroek 			addr->na_addr[i * 2 + 1] = (word & 0xff00) >> 8;
2731ad10e3aSDavid van Moolenbroek 		}
2741ad10e3aSDavid van Moolenbroek 	}
2751ad10e3aSDavid van Moolenbroek 
2761ad10e3aSDavid van Moolenbroek 	/* Set Receive Address. */
277f7df02e7SDavid van Moolenbroek 	e1000_set_hwaddr(addr);
2781ad10e3aSDavid van Moolenbroek 
279f7df02e7SDavid van Moolenbroek 	E1000_DEBUG(3, ("%s: Ethernet Address %x:%x:%x:%x:%x:%x\n",
280f7df02e7SDavid van Moolenbroek 	    netdriver_name(),
281f7df02e7SDavid van Moolenbroek 	    addr->na_addr[0], addr->na_addr[1], addr->na_addr[2],
282f7df02e7SDavid van Moolenbroek 	    addr->na_addr[3], addr->na_addr[4], addr->na_addr[5]));
2831ad10e3aSDavid van Moolenbroek }
2841ad10e3aSDavid van Moolenbroek 
2851ad10e3aSDavid van Moolenbroek /*
2861ad10e3aSDavid van Moolenbroek  * Initialize receive and transmit buffers.
2871ad10e3aSDavid van Moolenbroek  */
2881ad10e3aSDavid van Moolenbroek static void
e1000_init_buf(e1000_t * e)2891ad10e3aSDavid van Moolenbroek e1000_init_buf(e1000_t * e)
2901ad10e3aSDavid van Moolenbroek {
2911ad10e3aSDavid van Moolenbroek 	phys_bytes rx_desc_p, rx_buff_p;
2921ad10e3aSDavid van Moolenbroek 	phys_bytes tx_desc_p, tx_buff_p;
2931ad10e3aSDavid van Moolenbroek 	int i;
2941ad10e3aSDavid van Moolenbroek 
2951ad10e3aSDavid van Moolenbroek 	/* Number of descriptors. */
2961ad10e3aSDavid van Moolenbroek 	e->rx_desc_count = E1000_RXDESC_NR;
2971ad10e3aSDavid van Moolenbroek 	e->tx_desc_count = E1000_TXDESC_NR;
2981ad10e3aSDavid van Moolenbroek 
2991ad10e3aSDavid van Moolenbroek 	/* Allocate receive descriptors. */
3001ad10e3aSDavid van Moolenbroek 	if ((e->rx_desc = alloc_contig(sizeof(e1000_rx_desc_t) *
3011ad10e3aSDavid van Moolenbroek 	    e->rx_desc_count, AC_ALIGN4K, &rx_desc_p)) == NULL)
3021ad10e3aSDavid van Moolenbroek 		panic("failed to allocate RX descriptors");
3031ad10e3aSDavid van Moolenbroek 
3041ad10e3aSDavid van Moolenbroek 	memset(e->rx_desc, 0, sizeof(e1000_rx_desc_t) * e->rx_desc_count);
3051ad10e3aSDavid van Moolenbroek 
3061ad10e3aSDavid van Moolenbroek 	/* Allocate receive buffers. */
3071ad10e3aSDavid van Moolenbroek 	e->rx_buffer_size = E1000_RXDESC_NR * E1000_IOBUF_SIZE;
3081ad10e3aSDavid van Moolenbroek 
3091ad10e3aSDavid van Moolenbroek 	if ((e->rx_buffer = alloc_contig(e->rx_buffer_size, AC_ALIGN4K,
3101ad10e3aSDavid van Moolenbroek 	    &rx_buff_p)) == NULL)
3111ad10e3aSDavid van Moolenbroek 		panic("failed to allocate RX buffers");
3121ad10e3aSDavid van Moolenbroek 
3131ad10e3aSDavid van Moolenbroek 	/* Set up receive descriptors. */
3141ad10e3aSDavid van Moolenbroek 	for (i = 0; i < E1000_RXDESC_NR; i++)
3151ad10e3aSDavid van Moolenbroek 		e->rx_desc[i].buffer = rx_buff_p + i * E1000_IOBUF_SIZE;
3161ad10e3aSDavid van Moolenbroek 
3171ad10e3aSDavid van Moolenbroek 	/* Allocate transmit descriptors. */
3181ad10e3aSDavid van Moolenbroek 	if ((e->tx_desc = alloc_contig(sizeof(e1000_tx_desc_t) *
3191ad10e3aSDavid van Moolenbroek 	    e->tx_desc_count, AC_ALIGN4K, &tx_desc_p)) == NULL)
3201ad10e3aSDavid van Moolenbroek 		panic("failed to allocate TX descriptors");
3211ad10e3aSDavid van Moolenbroek 
3221ad10e3aSDavid van Moolenbroek 	memset(e->tx_desc, 0, sizeof(e1000_tx_desc_t) * e->tx_desc_count);
3231ad10e3aSDavid van Moolenbroek 
3241ad10e3aSDavid van Moolenbroek 	/* Allocate transmit buffers. */
3251ad10e3aSDavid van Moolenbroek 	e->tx_buffer_size = E1000_TXDESC_NR * E1000_IOBUF_SIZE;
3261ad10e3aSDavid van Moolenbroek 
3271ad10e3aSDavid van Moolenbroek 	if ((e->tx_buffer = alloc_contig(e->tx_buffer_size, AC_ALIGN4K,
3281ad10e3aSDavid van Moolenbroek 	    &tx_buff_p)) == NULL)
3291ad10e3aSDavid van Moolenbroek 		panic("failed to allocate TX buffers");
3301ad10e3aSDavid van Moolenbroek 
3311ad10e3aSDavid van Moolenbroek 	/* Set up transmit descriptors. */
3321ad10e3aSDavid van Moolenbroek 	for (i = 0; i < E1000_TXDESC_NR; i++)
3331ad10e3aSDavid van Moolenbroek 		e->tx_desc[i].buffer = tx_buff_p + i * E1000_IOBUF_SIZE;
3341ad10e3aSDavid van Moolenbroek 
3351ad10e3aSDavid van Moolenbroek 	/* Set up the receive ring registers. */
3361ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_RDBAL, rx_desc_p);
3371ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_RDBAH, 0);
3381ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_RDLEN,
3391ad10e3aSDavid van Moolenbroek 	    e->rx_desc_count * sizeof(e1000_rx_desc_t));
3401ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_RDH, 0);
3411ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_RDT, e->rx_desc_count - 1);
3421ad10e3aSDavid van Moolenbroek 	e1000_reg_unset(e, E1000_REG_RCTL, E1000_REG_RCTL_BSIZE);
3431ad10e3aSDavid van Moolenbroek 	e1000_reg_set(e, E1000_REG_RCTL, E1000_REG_RCTL_EN);
3441ad10e3aSDavid van Moolenbroek 
3451ad10e3aSDavid van Moolenbroek 	/* Set up the transmit ring registers. */
3461ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_TDBAL, tx_desc_p);
3471ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_TDBAH, 0);
3481ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_TDLEN,
3491ad10e3aSDavid van Moolenbroek 	    e->tx_desc_count * sizeof(e1000_tx_desc_t));
3501ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_TDH, 0);
3511ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_TDT, 0);
3521ad10e3aSDavid van Moolenbroek 	e1000_reg_set(e, E1000_REG_TCTL,
3531ad10e3aSDavid van Moolenbroek 	    E1000_REG_TCTL_EN | E1000_REG_TCTL_PSP);
3541ad10e3aSDavid van Moolenbroek }
3551ad10e3aSDavid van Moolenbroek 
3561ad10e3aSDavid van Moolenbroek /*
3571ad10e3aSDavid van Moolenbroek  * Initialize the hardware.  Return the ethernet address.
3581ad10e3aSDavid van Moolenbroek  */
3591ad10e3aSDavid van Moolenbroek static void
e1000_init_hw(e1000_t * e,netdriver_addr_t * addr)360f7df02e7SDavid van Moolenbroek e1000_init_hw(e1000_t * e, netdriver_addr_t * addr)
361433d6423SLionel Sambuc {
362433d6423SLionel Sambuc 	int r, i;
363433d6423SLionel Sambuc 
364433d6423SLionel Sambuc 	e->irq_hook = e->irq;
365433d6423SLionel Sambuc 
366433d6423SLionel Sambuc 	/*
367433d6423SLionel Sambuc 	 * Set the interrupt handler and policy.  Do not automatically
368107df7c8SDavid van Moolenbroek 	 * reenable interrupts.  Return the IRQ line number on interrupts.
369433d6423SLionel Sambuc 	 */
370433d6423SLionel Sambuc 	if ((r = sys_irqsetpolicy(e->irq, 0, &e->irq_hook)) != OK)
371433d6423SLionel Sambuc 		panic("sys_irqsetpolicy failed: %d", r);
372433d6423SLionel Sambuc 	if ((r = sys_irqenable(&e->irq_hook)) != OK)
373433d6423SLionel Sambuc 		panic("sys_irqenable failed: %d", r);
374107df7c8SDavid van Moolenbroek 
375433d6423SLionel Sambuc 	/* Reset hardware. */
376433d6423SLionel Sambuc 	e1000_reset_hw(e);
377433d6423SLionel Sambuc 
378433d6423SLionel Sambuc 	/*
379107df7c8SDavid van Moolenbroek 	 * Initialize appropriately, according to section 14.3 General
380107df7c8SDavid van Moolenbroek 	 * Configuration of Intel's Gigabit Ethernet Controllers Software
381107df7c8SDavid van Moolenbroek 	 * Developer's Manual.
382433d6423SLionel Sambuc 	 */
383107df7c8SDavid van Moolenbroek 	e1000_reg_set(e, E1000_REG_CTRL,
384107df7c8SDavid van Moolenbroek 	    E1000_REG_CTRL_ASDE | E1000_REG_CTRL_SLU);
385433d6423SLionel Sambuc 	e1000_reg_unset(e, E1000_REG_CTRL, E1000_REG_CTRL_LRST);
386433d6423SLionel Sambuc 	e1000_reg_unset(e, E1000_REG_CTRL, E1000_REG_CTRL_PHY_RST);
387433d6423SLionel Sambuc 	e1000_reg_unset(e, E1000_REG_CTRL, E1000_REG_CTRL_ILOS);
388433d6423SLionel Sambuc 	e1000_reg_write(e, E1000_REG_FCAL, 0);
389433d6423SLionel Sambuc 	e1000_reg_write(e, E1000_REG_FCAH, 0);
390433d6423SLionel Sambuc 	e1000_reg_write(e, E1000_REG_FCT, 0);
391433d6423SLionel Sambuc 	e1000_reg_write(e, E1000_REG_FCTTV, 0);
392433d6423SLionel Sambuc 	e1000_reg_unset(e, E1000_REG_CTRL, E1000_REG_CTRL_VME);
393433d6423SLionel Sambuc 
394433d6423SLionel Sambuc 	/* Clear Multicast Table Array (MTA). */
395433d6423SLionel Sambuc 	for (i = 0; i < 128; i++)
3961ad10e3aSDavid van Moolenbroek 		e1000_reg_write(e, E1000_REG_MTA + i * 4, 0);
397107df7c8SDavid van Moolenbroek 
398433d6423SLionel Sambuc 	/* Initialize statistics registers. */
399433d6423SLionel Sambuc 	for (i = 0; i < 64; i++)
4001ad10e3aSDavid van Moolenbroek 		e1000_reg_write(e, E1000_REG_CRCERRS + i * 4, 0);
401107df7c8SDavid van Moolenbroek 
402107df7c8SDavid van Moolenbroek 	/* Acquire MAC address and set up RX/TX buffers. */
4031ad10e3aSDavid van Moolenbroek 	e1000_init_addr(e, addr);
404433d6423SLionel Sambuc 	e1000_init_buf(e);
405433d6423SLionel Sambuc 
406433d6423SLionel Sambuc 	/* Enable interrupts. */
407107df7c8SDavid van Moolenbroek 	e1000_reg_set(e, E1000_REG_IMS, E1000_REG_IMS_LSC | E1000_REG_IMS_RXO |
408107df7c8SDavid van Moolenbroek 	    E1000_REG_IMS_RXT | E1000_REG_IMS_TXQE | E1000_REG_IMS_TXDW);
409433d6423SLionel Sambuc }
410433d6423SLionel Sambuc 
411107df7c8SDavid van Moolenbroek /*
412f7df02e7SDavid van Moolenbroek  * Set receive mode.
413f7df02e7SDavid van Moolenbroek  */
414f7df02e7SDavid van Moolenbroek static void
e1000_set_mode(unsigned int mode,const netdriver_addr_t * mcast_list __unused,unsigned int mcast_count __unused)415f7df02e7SDavid van Moolenbroek e1000_set_mode(unsigned int mode, const netdriver_addr_t * mcast_list __unused,
416f7df02e7SDavid van Moolenbroek 	unsigned int mcast_count __unused)
417f7df02e7SDavid van Moolenbroek {
418f7df02e7SDavid van Moolenbroek 	e1000_t *e;
419f7df02e7SDavid van Moolenbroek 	uint32_t rctl;
420f7df02e7SDavid van Moolenbroek 
421f7df02e7SDavid van Moolenbroek 	e = &e1000_state;
422f7df02e7SDavid van Moolenbroek 
423f7df02e7SDavid van Moolenbroek 	rctl = e1000_reg_read(e, E1000_REG_RCTL);
424f7df02e7SDavid van Moolenbroek 
425f7df02e7SDavid van Moolenbroek 	rctl &= ~(E1000_REG_RCTL_BAM | E1000_REG_RCTL_MPE |
426f7df02e7SDavid van Moolenbroek 	    E1000_REG_RCTL_UPE);
427f7df02e7SDavid van Moolenbroek 
428f7df02e7SDavid van Moolenbroek 	/* TODO: support for NDEV_MODE_DOWN and multicast lists */
429f7df02e7SDavid van Moolenbroek 	if (mode & NDEV_MODE_BCAST)
430f7df02e7SDavid van Moolenbroek 		rctl |= E1000_REG_RCTL_BAM;
431f7df02e7SDavid van Moolenbroek 	if (mode & (NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
432f7df02e7SDavid van Moolenbroek 		rctl |= E1000_REG_RCTL_MPE;
433f7df02e7SDavid van Moolenbroek 	if (mode & NDEV_MODE_PROMISC)
434f7df02e7SDavid van Moolenbroek 		rctl |= E1000_REG_RCTL_BAM | E1000_REG_RCTL_MPE |
435f7df02e7SDavid van Moolenbroek 		    E1000_REG_RCTL_UPE;
436f7df02e7SDavid van Moolenbroek 
437f7df02e7SDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_RCTL, rctl);
438f7df02e7SDavid van Moolenbroek }
439f7df02e7SDavid van Moolenbroek 
440f7df02e7SDavid van Moolenbroek /*
441f7df02e7SDavid van Moolenbroek  * Set hardware address.
442f7df02e7SDavid van Moolenbroek  */
443f7df02e7SDavid van Moolenbroek static void
e1000_set_hwaddr(const netdriver_addr_t * hwaddr)444f7df02e7SDavid van Moolenbroek e1000_set_hwaddr(const netdriver_addr_t * hwaddr)
445f7df02e7SDavid van Moolenbroek {
446f7df02e7SDavid van Moolenbroek 	e1000_t *e;
447f7df02e7SDavid van Moolenbroek 
448f7df02e7SDavid van Moolenbroek 	e = &e1000_state;
449f7df02e7SDavid van Moolenbroek 
450f7df02e7SDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_RAL,
451f7df02e7SDavid van Moolenbroek 	    *(const u32_t *)(&hwaddr->na_addr[0]));
452f7df02e7SDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_RAH,
453f7df02e7SDavid van Moolenbroek 	    *(const u16_t *)(&hwaddr->na_addr[4]));
454f7df02e7SDavid van Moolenbroek 	e1000_reg_set(e, E1000_REG_RAH, E1000_REG_RAH_AV);
455f7df02e7SDavid van Moolenbroek }
456f7df02e7SDavid van Moolenbroek 
457f7df02e7SDavid van Moolenbroek /*
458107df7c8SDavid van Moolenbroek  * Try to send a packet.
459107df7c8SDavid van Moolenbroek  */
4601ad10e3aSDavid van Moolenbroek static int
e1000_send(struct netdriver_data * data,size_t size)4611ad10e3aSDavid van Moolenbroek e1000_send(struct netdriver_data * data, size_t size)
462433d6423SLionel Sambuc {
4631ad10e3aSDavid van Moolenbroek 	e1000_t *e;
464433d6423SLionel Sambuc 	e1000_tx_desc_t *desc;
4651ad10e3aSDavid van Moolenbroek 	unsigned int head, tail, next;
4661ad10e3aSDavid van Moolenbroek 	char *ptr;
467433d6423SLionel Sambuc 
4681ad10e3aSDavid van Moolenbroek 	e = &e1000_state;
469433d6423SLionel Sambuc 
4701ad10e3aSDavid van Moolenbroek 	if (size > E1000_IOBUF_SIZE)
4711ad10e3aSDavid van Moolenbroek 		panic("packet too large to send");
472433d6423SLionel Sambuc 
473433d6423SLionel Sambuc 	/*
4741ad10e3aSDavid van Moolenbroek 	 * The queue tail must not advance to the point that it is equal to the
4751ad10e3aSDavid van Moolenbroek 	 * queue head, since this condition indicates that the queue is empty.
476433d6423SLionel Sambuc 	 */
477433d6423SLionel Sambuc 	head = e1000_reg_read(e, E1000_REG_TDH);
478433d6423SLionel Sambuc 	tail = e1000_reg_read(e, E1000_REG_TDT);
4791ad10e3aSDavid van Moolenbroek 	next = (tail + 1) % e->tx_desc_count;
4801ad10e3aSDavid van Moolenbroek 
4811ad10e3aSDavid van Moolenbroek 	if (next == head)
4821ad10e3aSDavid van Moolenbroek 		return SUSPEND;
4831ad10e3aSDavid van Moolenbroek 
4841ad10e3aSDavid van Moolenbroek 	/* The descriptor to use is the one pointed to by the current tail. */
485433d6423SLionel Sambuc 	desc = &e->tx_desc[tail];
486433d6423SLionel Sambuc 
4871ad10e3aSDavid van Moolenbroek 	/* Copy the packet from the caller. */
4881ad10e3aSDavid van Moolenbroek 	ptr = e->tx_buffer + tail * E1000_IOBUF_SIZE;
489433d6423SLionel Sambuc 
4901ad10e3aSDavid van Moolenbroek 	netdriver_copyin(data, 0, ptr, size);
491107df7c8SDavid van Moolenbroek 
492433d6423SLionel Sambuc 	/* Mark this descriptor ready. */
493433d6423SLionel Sambuc 	desc->status = 0;
494433d6423SLionel Sambuc 	desc->length = size;
4951ad10e3aSDavid van Moolenbroek 	desc->command = E1000_TX_CMD_EOP | E1000_TX_CMD_FCS | E1000_TX_CMD_RS;
496107df7c8SDavid van Moolenbroek 
497433d6423SLionel Sambuc 	/* Increment tail.  Start transmission. */
4981ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_TDT, next);
499433d6423SLionel Sambuc 
5001ad10e3aSDavid van Moolenbroek 	return OK;
501433d6423SLionel Sambuc }
502433d6423SLionel Sambuc 
503107df7c8SDavid van Moolenbroek /*
504107df7c8SDavid van Moolenbroek  * Try to receive a packet.
505107df7c8SDavid van Moolenbroek  */
5061ad10e3aSDavid van Moolenbroek static ssize_t
e1000_recv(struct netdriver_data * data,size_t max)5071ad10e3aSDavid van Moolenbroek e1000_recv(struct netdriver_data * data, size_t max)
508433d6423SLionel Sambuc {
5091ad10e3aSDavid van Moolenbroek 	e1000_t *e;
510433d6423SLionel Sambuc 	e1000_rx_desc_t *desc;
5111ad10e3aSDavid van Moolenbroek 	unsigned int head, tail, cur;
5121ad10e3aSDavid van Moolenbroek 	char *ptr;
5131ad10e3aSDavid van Moolenbroek 	size_t size;
514433d6423SLionel Sambuc 
5151ad10e3aSDavid van Moolenbroek 	e = &e1000_state;
516433d6423SLionel Sambuc 
5171ad10e3aSDavid van Moolenbroek 	/* If the queue head and tail are equal, the queue is empty. */
518433d6423SLionel Sambuc 	head = e1000_reg_read(e, E1000_REG_RDH);
519433d6423SLionel Sambuc 	tail = e1000_reg_read(e, E1000_REG_RDT);
5201ad10e3aSDavid van Moolenbroek 
521f7df02e7SDavid van Moolenbroek 	E1000_DEBUG(4, ("%s: head=%u, tail=%u\n",
522f7df02e7SDavid van Moolenbroek 	    netdriver_name(), head, tail));
5231ad10e3aSDavid van Moolenbroek 
5241ad10e3aSDavid van Moolenbroek 	if (head == tail)
5251ad10e3aSDavid van Moolenbroek 		return SUSPEND;
5261ad10e3aSDavid van Moolenbroek 
5271ad10e3aSDavid van Moolenbroek 	/* Has a packet been received? */
528433d6423SLionel Sambuc 	cur = (tail + 1) % e->rx_desc_count;
529433d6423SLionel Sambuc 	desc = &e->rx_desc[cur];
530433d6423SLionel Sambuc 
5311ad10e3aSDavid van Moolenbroek 	if (!(desc->status & E1000_RX_STATUS_DONE))
5321ad10e3aSDavid van Moolenbroek 		return SUSPEND;
533433d6423SLionel Sambuc 
534433d6423SLionel Sambuc 	/*
5351ad10e3aSDavid van Moolenbroek 	 * HACK: we expect all packets to fit in a single receive buffer.
5361ad10e3aSDavid van Moolenbroek 	 * Eventually, some sort of support to deal with packets spanning
5371ad10e3aSDavid van Moolenbroek 	 * multiple receive descriptors should be added.  For now, we panic,
5381ad10e3aSDavid van Moolenbroek 	 * so that we can continue after the restart; this is already an
5391ad10e3aSDavid van Moolenbroek 	 * improvement over freezing (the old behavior of this driver).
540433d6423SLionel Sambuc 	 */
5411ad10e3aSDavid van Moolenbroek 	size = desc->length;
542433d6423SLionel Sambuc 
5431ad10e3aSDavid van Moolenbroek 	if (!(desc->status & E1000_RX_STATUS_EOP))
5441ad10e3aSDavid van Moolenbroek 		panic("received packet too large");
545433d6423SLionel Sambuc 
5461ad10e3aSDavid van Moolenbroek 	/* Copy the packet to the caller. */
5471ad10e3aSDavid van Moolenbroek 	ptr = e->rx_buffer + cur * E1000_IOBUF_SIZE;
5481ad10e3aSDavid van Moolenbroek 
5491ad10e3aSDavid van Moolenbroek 	if (size > max)
5501ad10e3aSDavid van Moolenbroek 		size = max;
5511ad10e3aSDavid van Moolenbroek 
5521ad10e3aSDavid van Moolenbroek 	netdriver_copyout(data, 0, ptr, size);
5531ad10e3aSDavid van Moolenbroek 
5541ad10e3aSDavid van Moolenbroek 	/* Reset the descriptor. */
555433d6423SLionel Sambuc 	desc->status = 0;
556433d6423SLionel Sambuc 
557433d6423SLionel Sambuc 	/* Increment tail. */
5581ad10e3aSDavid van Moolenbroek 	e1000_reg_write(e, E1000_REG_RDT, cur);
5591ad10e3aSDavid van Moolenbroek 
5601ad10e3aSDavid van Moolenbroek 	/* Return the size of the received packet. */
5611ad10e3aSDavid van Moolenbroek 	return size;
562433d6423SLionel Sambuc }
563433d6423SLionel Sambuc 
564107df7c8SDavid van Moolenbroek /*
565f7df02e7SDavid van Moolenbroek  * Return the link and media status.
566107df7c8SDavid van Moolenbroek  */
567f7df02e7SDavid van Moolenbroek static unsigned int
e1000_get_link(uint32_t * media)568f7df02e7SDavid van Moolenbroek e1000_get_link(uint32_t * media)
569433d6423SLionel Sambuc {
570f7df02e7SDavid van Moolenbroek 	uint32_t status, type;
571433d6423SLionel Sambuc 
572f7df02e7SDavid van Moolenbroek 	status = e1000_reg_read(&e1000_state, E1000_REG_STATUS);
573433d6423SLionel Sambuc 
574f7df02e7SDavid van Moolenbroek 	if (!(status & E1000_REG_STATUS_LU))
575f7df02e7SDavid van Moolenbroek 		return NDEV_LINK_DOWN;
576f7df02e7SDavid van Moolenbroek 
577f7df02e7SDavid van Moolenbroek 	if (status & E1000_REG_STATUS_FD)
578f7df02e7SDavid van Moolenbroek 		type = IFM_ETHER | IFM_FDX;
579f7df02e7SDavid van Moolenbroek 	else
580f7df02e7SDavid van Moolenbroek 		type = IFM_ETHER | IFM_HDX;
581f7df02e7SDavid van Moolenbroek 
582f7df02e7SDavid van Moolenbroek 	switch (status & E1000_REG_STATUS_SPEED) {
583f7df02e7SDavid van Moolenbroek 	case E1000_REG_STATUS_SPEED_10:
584f7df02e7SDavid van Moolenbroek 		type |= IFM_10_T;
585f7df02e7SDavid van Moolenbroek 		break;
586f7df02e7SDavid van Moolenbroek 	case E1000_REG_STATUS_SPEED_100:
587f7df02e7SDavid van Moolenbroek 		type |= IFM_100_TX;
588f7df02e7SDavid van Moolenbroek 		break;
589f7df02e7SDavid van Moolenbroek 	case E1000_REG_STATUS_SPEED_1000_A:
590f7df02e7SDavid van Moolenbroek 	case E1000_REG_STATUS_SPEED_1000_B:
591f7df02e7SDavid van Moolenbroek 		type |= IFM_1000_T;
592f7df02e7SDavid van Moolenbroek 		break;
5931ad10e3aSDavid van Moolenbroek 	}
594433d6423SLionel Sambuc 
595f7df02e7SDavid van Moolenbroek 	*media = type;
596f7df02e7SDavid van Moolenbroek 	return NDEV_LINK_UP;
597433d6423SLionel Sambuc }
598433d6423SLionel Sambuc 
599107df7c8SDavid van Moolenbroek /*
600107df7c8SDavid van Moolenbroek  * Handle an interrupt.
601107df7c8SDavid van Moolenbroek  */
602107df7c8SDavid van Moolenbroek static void
e1000_intr(unsigned int __unused mask)6031ad10e3aSDavid van Moolenbroek e1000_intr(unsigned int __unused mask)
604433d6423SLionel Sambuc {
605433d6423SLionel Sambuc 	e1000_t *e;
606433d6423SLionel Sambuc 	u32_t cause;
607433d6423SLionel Sambuc 
608433d6423SLionel Sambuc 	E1000_DEBUG(3, ("e1000: interrupt\n"));
609433d6423SLionel Sambuc 
610433d6423SLionel Sambuc 	e = &e1000_state;
611433d6423SLionel Sambuc 
612107df7c8SDavid van Moolenbroek 	/* Reenable interrupts. */
613433d6423SLionel Sambuc 	if (sys_irqenable(&e->irq_hook) != OK)
614433d6423SLionel Sambuc 		panic("failed to re-enable IRQ");
615433d6423SLionel Sambuc 
616433d6423SLionel Sambuc 	/* Read the Interrupt Cause Read register. */
617107df7c8SDavid van Moolenbroek 	if ((cause = e1000_reg_read(e, E1000_REG_ICR)) != 0) {
618433d6423SLionel Sambuc 		if (cause & E1000_REG_ICR_LSC)
619f7df02e7SDavid van Moolenbroek 			netdriver_link();
620433d6423SLionel Sambuc 
621433d6423SLionel Sambuc 		if (cause & (E1000_REG_ICR_RXO | E1000_REG_ICR_RXT))
6221ad10e3aSDavid van Moolenbroek 			netdriver_recv();
623433d6423SLionel Sambuc 
624107df7c8SDavid van Moolenbroek 		if (cause & (E1000_REG_ICR_TXQE | E1000_REG_ICR_TXDW))
6251ad10e3aSDavid van Moolenbroek 			netdriver_send();
626433d6423SLionel Sambuc 	}
627433d6423SLionel Sambuc }
628433d6423SLionel Sambuc 
629107df7c8SDavid van Moolenbroek /*
630f7df02e7SDavid van Moolenbroek  * Do regular processing.
631f7df02e7SDavid van Moolenbroek  */
632f7df02e7SDavid van Moolenbroek static void
e1000_tick(void)633f7df02e7SDavid van Moolenbroek e1000_tick(void)
634f7df02e7SDavid van Moolenbroek {
635f7df02e7SDavid van Moolenbroek 	e1000_t *e;
636f7df02e7SDavid van Moolenbroek 
637f7df02e7SDavid van Moolenbroek 	e = &e1000_state;
638f7df02e7SDavid van Moolenbroek 
639f7df02e7SDavid van Moolenbroek 	/* Update statistics. */
640f7df02e7SDavid van Moolenbroek 	netdriver_stat_ierror(e1000_reg_read(e, E1000_REG_RXERRC));
641f7df02e7SDavid van Moolenbroek 	netdriver_stat_ierror(e1000_reg_read(e, E1000_REG_CRCERRS));
642f7df02e7SDavid van Moolenbroek 	netdriver_stat_ierror(e1000_reg_read(e, E1000_REG_MPC));
643f7df02e7SDavid van Moolenbroek 	netdriver_stat_coll(e1000_reg_read(e, E1000_REG_COLC));
644f7df02e7SDavid van Moolenbroek }
645f7df02e7SDavid van Moolenbroek 
646f7df02e7SDavid van Moolenbroek /*
647107df7c8SDavid van Moolenbroek  * Stop the card.
648107df7c8SDavid van Moolenbroek  */
649107df7c8SDavid van Moolenbroek static void
e1000_stop(void)6501ad10e3aSDavid van Moolenbroek e1000_stop(void)
651433d6423SLionel Sambuc {
6521ad10e3aSDavid van Moolenbroek 	e1000_t *e;
6531ad10e3aSDavid van Moolenbroek 
6541ad10e3aSDavid van Moolenbroek 	e = &e1000_state;
655107df7c8SDavid van Moolenbroek 
656f7df02e7SDavid van Moolenbroek 	E1000_DEBUG(3, ("%s: stop()\n", netdriver_name()));
657433d6423SLionel Sambuc 
658433d6423SLionel Sambuc 	e1000_reset_hw(e);
659433d6423SLionel Sambuc }
660433d6423SLionel Sambuc 
661107df7c8SDavid van Moolenbroek /*
662107df7c8SDavid van Moolenbroek  * Read from a register.
663107df7c8SDavid van Moolenbroek  */
664107df7c8SDavid van Moolenbroek static uint32_t
e1000_reg_read(e1000_t * e,uint32_t reg)665107df7c8SDavid van Moolenbroek e1000_reg_read(e1000_t * e, uint32_t reg)
666433d6423SLionel Sambuc {
667433d6423SLionel Sambuc 	uint32_t value;
668433d6423SLionel Sambuc 
669433d6423SLionel Sambuc 	/* Assume a sane register. */
670433d6423SLionel Sambuc 	assert(reg < 0x1ffff);
671433d6423SLionel Sambuc 
672433d6423SLionel Sambuc 	/* Read from memory mapped register. */
673107df7c8SDavid van Moolenbroek 	value = *(volatile uint32_t *)(e->regs + reg);
674433d6423SLionel Sambuc 
675433d6423SLionel Sambuc 	/* Return the result. */
676433d6423SLionel Sambuc 	return value;
677433d6423SLionel Sambuc }
678433d6423SLionel Sambuc 
679107df7c8SDavid van Moolenbroek /*
680107df7c8SDavid van Moolenbroek  * Write to a register.
681107df7c8SDavid van Moolenbroek  */
682107df7c8SDavid van Moolenbroek static void
e1000_reg_write(e1000_t * e,uint32_t reg,uint32_t value)683107df7c8SDavid van Moolenbroek e1000_reg_write(e1000_t * e, uint32_t reg, uint32_t value)
684433d6423SLionel Sambuc {
685107df7c8SDavid van Moolenbroek 
686433d6423SLionel Sambuc 	/* Assume a sane register. */
687433d6423SLionel Sambuc 	assert(reg < 0x1ffff);
688433d6423SLionel Sambuc 
689433d6423SLionel Sambuc 	/* Write to memory mapped register. */
690433d6423SLionel Sambuc 	*(volatile u32_t *)(e->regs + reg) = value;
691433d6423SLionel Sambuc }
692433d6423SLionel Sambuc 
693107df7c8SDavid van Moolenbroek /*
694107df7c8SDavid van Moolenbroek  * Set bits in a register.
695107df7c8SDavid van Moolenbroek  */
696107df7c8SDavid van Moolenbroek static void
e1000_reg_set(e1000_t * e,uint32_t reg,uint32_t value)697107df7c8SDavid van Moolenbroek e1000_reg_set(e1000_t * e, uint32_t reg, uint32_t value)
698433d6423SLionel Sambuc {
699433d6423SLionel Sambuc 	uint32_t data;
700433d6423SLionel Sambuc 
701433d6423SLionel Sambuc 	/* First read the current value. */
702433d6423SLionel Sambuc 	data = e1000_reg_read(e, reg);
703433d6423SLionel Sambuc 
704107df7c8SDavid van Moolenbroek 	/* Set bits, and write back. */
705433d6423SLionel Sambuc 	e1000_reg_write(e, reg, data | value);
706433d6423SLionel Sambuc }
707433d6423SLionel Sambuc 
708107df7c8SDavid van Moolenbroek /*
709107df7c8SDavid van Moolenbroek  * Clear bits in a register.
710107df7c8SDavid van Moolenbroek  */
711107df7c8SDavid van Moolenbroek static void
e1000_reg_unset(e1000_t * e,uint32_t reg,uint32_t value)712107df7c8SDavid van Moolenbroek e1000_reg_unset(e1000_t * e, uint32_t reg, uint32_t value)
713433d6423SLionel Sambuc {
714433d6423SLionel Sambuc 	uint32_t data;
715433d6423SLionel Sambuc 
716433d6423SLionel Sambuc 	/* First read the current value. */
717433d6423SLionel Sambuc 	data = e1000_reg_read(e, reg);
718433d6423SLionel Sambuc 
719107df7c8SDavid van Moolenbroek 	/* Unset bits, and write back. */
720433d6423SLionel Sambuc 	e1000_reg_write(e, reg, data & ~value);
721433d6423SLionel Sambuc }
722433d6423SLionel Sambuc 
723107df7c8SDavid van Moolenbroek /*
724107df7c8SDavid van Moolenbroek  * Read from EEPROM.
725107df7c8SDavid van Moolenbroek  */
726107df7c8SDavid van Moolenbroek static u16_t
eeprom_eerd(e1000_t * e,int reg)7271ad10e3aSDavid van Moolenbroek eeprom_eerd(e1000_t * e, int reg)
728433d6423SLionel Sambuc {
729433d6423SLionel Sambuc 	u32_t data;
730433d6423SLionel Sambuc 
731433d6423SLionel Sambuc 	/* Request EEPROM read. */
732433d6423SLionel Sambuc 	e1000_reg_write(e, E1000_REG_EERD,
733433d6423SLionel Sambuc 	    (reg << e->eeprom_addr_off) | (E1000_REG_EERD_START));
734433d6423SLionel Sambuc 
735433d6423SLionel Sambuc 	/* Wait until ready. */
736107df7c8SDavid van Moolenbroek 	while (!((data = (e1000_reg_read(e, E1000_REG_EERD))) &
737107df7c8SDavid van Moolenbroek 	    e->eeprom_done_bit));
738433d6423SLionel Sambuc 
739433d6423SLionel Sambuc 	return data >> 16;
740433d6423SLionel Sambuc }
741433d6423SLionel Sambuc 
742107df7c8SDavid van Moolenbroek /*
743107df7c8SDavid van Moolenbroek  * Initialize ICH8 flash.
744107df7c8SDavid van Moolenbroek  */
745107df7c8SDavid van Moolenbroek static int
eeprom_ich_init(e1000_t * e)746107df7c8SDavid van Moolenbroek eeprom_ich_init(e1000_t * e)
747433d6423SLionel Sambuc {
748433d6423SLionel Sambuc 	union ich8_hws_flash_status hsfsts;
749433d6423SLionel Sambuc 	int ret_val = -1;
750433d6423SLionel Sambuc 	int i = 0;
751433d6423SLionel Sambuc 
752433d6423SLionel Sambuc 	hsfsts.regval = E1000_READ_FLASH_REG16(e, ICH_FLASH_HSFSTS);
753433d6423SLionel Sambuc 
754433d6423SLionel Sambuc 	/* Check if the flash descriptor is valid */
755107df7c8SDavid van Moolenbroek 	if (hsfsts.hsf_status.fldesvalid == 0) {
756433d6423SLionel Sambuc 		E1000_DEBUG(3, ("Flash descriptor invalid. "
757433d6423SLionel Sambuc 		    "SW Sequencing must be used."));
7581ad10e3aSDavid van Moolenbroek 		return ret_val;
759433d6423SLionel Sambuc 	}
760107df7c8SDavid van Moolenbroek 
761433d6423SLionel Sambuc 	/* Clear FCERR and DAEL in hw status by writing 1 */
762433d6423SLionel Sambuc 	hsfsts.hsf_status.flcerr = 1;
763433d6423SLionel Sambuc 	hsfsts.hsf_status.dael = 1;
764433d6423SLionel Sambuc 
765433d6423SLionel Sambuc 	E1000_WRITE_FLASH_REG16(e, ICH_FLASH_HSFSTS, hsfsts.regval);
766433d6423SLionel Sambuc 
767433d6423SLionel Sambuc 	/*
768107df7c8SDavid van Moolenbroek 	 * Either we should have a hardware SPI cycle in progress bit to check
769107df7c8SDavid van Moolenbroek 	 * against, in order to start a new cycle or FDONE bit should be
770107df7c8SDavid van Moolenbroek 	 * changed in the hardware so that it is 1 after hardware reset, which
771107df7c8SDavid van Moolenbroek 	 * can then be used as an indication whether a cycle is in progress or
772107df7c8SDavid van Moolenbroek 	 * has been completed.
773433d6423SLionel Sambuc 	 */
774107df7c8SDavid van Moolenbroek 	if (hsfsts.hsf_status.flcinprog == 0) {
775433d6423SLionel Sambuc 		/*
776107df7c8SDavid van Moolenbroek 		 * There is no cycle running at present, so we can start a
777107df7c8SDavid van Moolenbroek 		 * cycle.  Begin by setting Flash Cycle Done.
778433d6423SLionel Sambuc 		 */
779433d6423SLionel Sambuc 		hsfsts.hsf_status.flcdone = 1;
780433d6423SLionel Sambuc 		E1000_WRITE_FLASH_REG16(e, ICH_FLASH_HSFSTS, hsfsts.regval);
781433d6423SLionel Sambuc 		ret_val = 0;
782107df7c8SDavid van Moolenbroek 	} else {
783433d6423SLionel Sambuc 		/*
784107df7c8SDavid van Moolenbroek 		 * Otherwise poll for sometime so the current cycle has a
785107df7c8SDavid van Moolenbroek 		 * chance to end before giving up.
786433d6423SLionel Sambuc 		 */
787107df7c8SDavid van Moolenbroek 		for (i = 0; i < ICH_FLASH_READ_COMMAND_TIMEOUT; i++) {
788107df7c8SDavid van Moolenbroek 			hsfsts.regval = E1000_READ_FLASH_REG16(e,
789107df7c8SDavid van Moolenbroek 			    ICH_FLASH_HSFSTS);
790433d6423SLionel Sambuc 
791107df7c8SDavid van Moolenbroek 			if (hsfsts.hsf_status.flcinprog == 0) {
792433d6423SLionel Sambuc 				ret_val = 0;
793433d6423SLionel Sambuc 				break;
794433d6423SLionel Sambuc 			}
795*d4dd6511Srlfnb 			micro_delay(16000);
796433d6423SLionel Sambuc 		}
797107df7c8SDavid van Moolenbroek 		if (ret_val == 0) {
798433d6423SLionel Sambuc 			/*
799433d6423SLionel Sambuc 			 * Successful in waiting for previous cycle to timeout,
800433d6423SLionel Sambuc 			 * now set the Flash Cycle Done.
801433d6423SLionel Sambuc 			 */
802433d6423SLionel Sambuc 			hsfsts.hsf_status.flcdone = 1;
803433d6423SLionel Sambuc 			E1000_WRITE_FLASH_REG16(e, ICH_FLASH_HSFSTS,
804433d6423SLionel Sambuc 			    hsfsts.regval);
805107df7c8SDavid van Moolenbroek 		} else {
806107df7c8SDavid van Moolenbroek 			E1000_DEBUG(3,
807107df7c8SDavid van Moolenbroek 			    ("Flash controller busy, cannot get access"));
808433d6423SLionel Sambuc 		}
809433d6423SLionel Sambuc 	}
8101ad10e3aSDavid van Moolenbroek 
811433d6423SLionel Sambuc 	return ret_val;
812433d6423SLionel Sambuc }
813433d6423SLionel Sambuc 
814107df7c8SDavid van Moolenbroek /*
815107df7c8SDavid van Moolenbroek  * Start ICH8 flash cycle.
816107df7c8SDavid van Moolenbroek  */
817107df7c8SDavid van Moolenbroek static int
eeprom_ich_cycle(e1000_t * e,u32_t timeout)818107df7c8SDavid van Moolenbroek eeprom_ich_cycle(e1000_t * e, u32_t timeout)
819433d6423SLionel Sambuc {
820433d6423SLionel Sambuc 	union ich8_hws_flash_ctrl hsflctl;
821433d6423SLionel Sambuc 	union ich8_hws_flash_status hsfsts;
822433d6423SLionel Sambuc 	int ret_val = -1;
823433d6423SLionel Sambuc 	u32_t i = 0;
824433d6423SLionel Sambuc 
825433d6423SLionel Sambuc 	E1000_DEBUG(3, ("e1000_flash_cycle_ich8lan"));
826433d6423SLionel Sambuc 
827433d6423SLionel Sambuc 	/* Start a cycle by writing 1 in Flash Cycle Go in Hw Flash Control */
828433d6423SLionel Sambuc 	hsflctl.regval = E1000_READ_FLASH_REG16(e, ICH_FLASH_HSFCTL);
829433d6423SLionel Sambuc 	hsflctl.hsf_ctrl.flcgo = 1;
830433d6423SLionel Sambuc 	E1000_WRITE_FLASH_REG16(e, ICH_FLASH_HSFCTL, hsflctl.regval);
831433d6423SLionel Sambuc 
832107df7c8SDavid van Moolenbroek 	/* Wait till the FDONE bit is set to 1 */
833107df7c8SDavid van Moolenbroek 	do {
834433d6423SLionel Sambuc 		hsfsts.regval = E1000_READ_FLASH_REG16(e, ICH_FLASH_HSFSTS);
835433d6423SLionel Sambuc 		if (hsfsts.hsf_status.flcdone == 1)
836433d6423SLionel Sambuc 			break;
837*d4dd6511Srlfnb 		micro_delay(16000);
838107df7c8SDavid van Moolenbroek 	} while (i++ < timeout);
839433d6423SLionel Sambuc 
840433d6423SLionel Sambuc 	if (hsfsts.hsf_status.flcdone == 1 && hsfsts.hsf_status.flcerr == 0)
841433d6423SLionel Sambuc 		ret_val = 0;
842433d6423SLionel Sambuc 
843433d6423SLionel Sambuc 	return ret_val;
844433d6423SLionel Sambuc }
845433d6423SLionel Sambuc 
846107df7c8SDavid van Moolenbroek /*
847107df7c8SDavid van Moolenbroek  * Read from ICH8 flash.
848107df7c8SDavid van Moolenbroek  */
849107df7c8SDavid van Moolenbroek static u16_t
eeprom_ich(e1000_t * e,int reg)8501ad10e3aSDavid van Moolenbroek eeprom_ich(e1000_t * e, int reg)
851433d6423SLionel Sambuc {
852433d6423SLionel Sambuc 	union ich8_hws_flash_status hsfsts;
853433d6423SLionel Sambuc 	union ich8_hws_flash_ctrl hsflctl;
854433d6423SLionel Sambuc 	u32_t flash_linear_addr;
855433d6423SLionel Sambuc 	u32_t flash_data = 0;
856433d6423SLionel Sambuc 	int ret_val = -1;
857433d6423SLionel Sambuc 	u8_t count = 0;
858433d6423SLionel Sambuc 	u16_t data = 0;
859433d6423SLionel Sambuc 
860433d6423SLionel Sambuc 	E1000_DEBUG(3, ("e1000_read_flash_data_ich8lan"));
861433d6423SLionel Sambuc 
862433d6423SLionel Sambuc 	if (reg > ICH_FLASH_LINEAR_ADDR_MASK)
8631ad10e3aSDavid van Moolenbroek 		return data;
864433d6423SLionel Sambuc 
865433d6423SLionel Sambuc 	reg *= sizeof(u16_t);
866433d6423SLionel Sambuc 	flash_linear_addr = (ICH_FLASH_LINEAR_ADDR_MASK & reg) +
867433d6423SLionel Sambuc 	    e->flash_base_addr;
868433d6423SLionel Sambuc 
869433d6423SLionel Sambuc 	do {
870*d4dd6511Srlfnb 		micro_delay(16000);
871433d6423SLionel Sambuc 
872433d6423SLionel Sambuc 		/* Steps */
873433d6423SLionel Sambuc 		ret_val = eeprom_ich_init(e);
874433d6423SLionel Sambuc 		if (ret_val != 0)
875433d6423SLionel Sambuc 			break;
876433d6423SLionel Sambuc 
877433d6423SLionel Sambuc 		hsflctl.regval = E1000_READ_FLASH_REG16(e, ICH_FLASH_HSFCTL);
878433d6423SLionel Sambuc 		/* 0b/1b corresponds to 1 or 2 byte size, respectively. */
879433d6423SLionel Sambuc 		hsflctl.hsf_ctrl.fldbcount = 1;
880433d6423SLionel Sambuc 		hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_READ;
881433d6423SLionel Sambuc 		E1000_WRITE_FLASH_REG16(e, ICH_FLASH_HSFCTL, hsflctl.regval);
882433d6423SLionel Sambuc 		E1000_WRITE_FLASH_REG(e, ICH_FLASH_FADDR, flash_linear_addr);
883433d6423SLionel Sambuc 
8841ad10e3aSDavid van Moolenbroek 		ret_val = eeprom_ich_cycle(e, ICH_FLASH_READ_COMMAND_TIMEOUT);
885433d6423SLionel Sambuc 
886433d6423SLionel Sambuc 		/*
887107df7c8SDavid van Moolenbroek 		 * Check if FCERR is set to 1, if set to 1, clear it and try
888107df7c8SDavid van Moolenbroek 		 * the whole sequence a few more times, else read in (shift in)
889107df7c8SDavid van Moolenbroek 		 * the Flash Data0, the order is least significant byte first
890107df7c8SDavid van Moolenbroek 		 * msb to lsb.
891433d6423SLionel Sambuc 		 */
892107df7c8SDavid van Moolenbroek 		if (ret_val == 0) {
893433d6423SLionel Sambuc 			flash_data = E1000_READ_FLASH_REG(e, ICH_FLASH_FDATA0);
894433d6423SLionel Sambuc 			data = (u16_t)(flash_data & 0x0000FFFF);
895433d6423SLionel Sambuc 			break;
896107df7c8SDavid van Moolenbroek 		} else {
897433d6423SLionel Sambuc 			/*
898433d6423SLionel Sambuc 			 * If we've gotten here, then things are probably
899433d6423SLionel Sambuc 			 * completely hosed, but if the error condition is
900433d6423SLionel Sambuc 			 * detected, it won't hurt to give it another try...
901433d6423SLionel Sambuc 			 * ICH_FLASH_CYCLE_REPEAT_COUNT times.
902433d6423SLionel Sambuc 			 */
903107df7c8SDavid van Moolenbroek 			hsfsts.regval = E1000_READ_FLASH_REG16(e,
904107df7c8SDavid van Moolenbroek 			    ICH_FLASH_HSFSTS);
905433d6423SLionel Sambuc 
906107df7c8SDavid van Moolenbroek 			if (hsfsts.hsf_status.flcerr == 1) {
907433d6423SLionel Sambuc 				/* Repeat for some time before giving up. */
908433d6423SLionel Sambuc 				continue;
909107df7c8SDavid van Moolenbroek 			} else if (hsfsts.hsf_status.flcdone == 0) {
910433d6423SLionel Sambuc 				E1000_DEBUG(3, ("Timeout error - flash cycle "
911433d6423SLionel Sambuc 				    "did not complete."));
912433d6423SLionel Sambuc 				break;
913433d6423SLionel Sambuc 			}
914433d6423SLionel Sambuc 		}
915433d6423SLionel Sambuc 	} while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT);
916433d6423SLionel Sambuc 
917433d6423SLionel Sambuc 	return data;
918433d6423SLionel Sambuc }
919