xref: /minix3/minix/drivers/net/rtl8139/rtl8139.c (revision f7df02e7476731c31f12548e38bcadbaf0233f6a)
1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc  * rtl8139.c
3433d6423SLionel Sambuc  *
4433d6423SLionel Sambuc  * This file contains a ethernet device driver for Realtek rtl8139 based
5433d6423SLionel Sambuc  * ethernet cards.
6433d6423SLionel Sambuc  *
7433d6423SLionel Sambuc  * Created:	Aug 2003 by Philip Homburg <philip@cs.vu.nl>
8433d6423SLionel Sambuc  * Changes:
9433d6423SLionel Sambuc  *   Aug 15, 2004   sync alarms replace watchdogs timers  (Jorrit N. Herder)
10433d6423SLionel Sambuc  *   May 02, 2004   flag alarms replace micro_elapsed()  (Jorrit N. Herder)
11433d6423SLionel Sambuc  *
12433d6423SLionel Sambuc  */
13433d6423SLionel Sambuc 
14433d6423SLionel Sambuc #define VERBOSE 0 /* Verbose debugging output */
15433d6423SLionel Sambuc #define RTL8139_FKEY 0 /* Use function key to dump RTL8139 status */
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc #include "rtl8139.h"
18433d6423SLionel Sambuc 
194081bff6SDavid van Moolenbroek static re_t re_state;
20433d6423SLionel Sambuc 
my_inb(u16_t port)21433d6423SLionel Sambuc static unsigned my_inb(u16_t port) {
22433d6423SLionel Sambuc 	u32_t value;
23433d6423SLionel Sambuc 	int s;
24433d6423SLionel Sambuc 	if ((s=sys_inb(port, &value)) !=OK)
25433d6423SLionel Sambuc 		printf("RTL8139: warning, sys_inb failed: %d\n", s);
26433d6423SLionel Sambuc 	return value;
27433d6423SLionel Sambuc }
my_inw(u16_t port)28433d6423SLionel Sambuc static unsigned my_inw(u16_t port) {
29433d6423SLionel Sambuc 	u32_t value;
30433d6423SLionel Sambuc 	int s;
31433d6423SLionel Sambuc 	if ((s=sys_inw(port, &value)) !=OK)
32433d6423SLionel Sambuc 		printf("RTL8139: warning, sys_inw failed: %d\n", s);
33433d6423SLionel Sambuc 	return value;
34433d6423SLionel Sambuc }
my_inl(u16_t port)35433d6423SLionel Sambuc static unsigned my_inl(u16_t port) {
36433d6423SLionel Sambuc 	u32_t value;
37433d6423SLionel Sambuc 	int s;
38433d6423SLionel Sambuc 	if ((s=sys_inl(port, &value)) !=OK)
39433d6423SLionel Sambuc 		printf("RTL8139: warning, sys_inl failed: %d\n", s);
40433d6423SLionel Sambuc 	return value;
41433d6423SLionel Sambuc }
42433d6423SLionel Sambuc #define rl_inb(port, offset)	(my_inb((port) + (offset)))
43433d6423SLionel Sambuc #define rl_inw(port, offset)	(my_inw((port) + (offset)))
44433d6423SLionel Sambuc #define rl_inl(port, offset)	(my_inl((port) + (offset)))
45433d6423SLionel Sambuc 
my_outb(u16_t port,u8_t value)46433d6423SLionel Sambuc static void my_outb(u16_t port, u8_t value) {
47433d6423SLionel Sambuc 	int s;
48433d6423SLionel Sambuc 	if ((s=sys_outb(port, value)) !=OK)
49433d6423SLionel Sambuc 		printf("RTL8139: warning, sys_outb failed: %d\n", s);
50433d6423SLionel Sambuc }
my_outw(u16_t port,u16_t value)51433d6423SLionel Sambuc static void my_outw(u16_t port, u16_t value) {
52433d6423SLionel Sambuc 	int s;
53433d6423SLionel Sambuc 	if ((s=sys_outw(port, value)) !=OK)
54433d6423SLionel Sambuc 		printf("RTL8139: warning, sys_outw failed: %d\n", s);
55433d6423SLionel Sambuc }
my_outl(u16_t port,u32_t value)56433d6423SLionel Sambuc static void my_outl(u16_t port, u32_t value) {
57433d6423SLionel Sambuc 	int s;
58433d6423SLionel Sambuc 	if ((s=sys_outl(port, value)) !=OK)
59433d6423SLionel Sambuc 		printf("RTL8139: warning, sys_outl failed: %d\n", s);
60433d6423SLionel Sambuc }
61433d6423SLionel Sambuc #define rl_outb(port, offset, value)	(my_outb((port) + (offset), (value)))
62433d6423SLionel Sambuc #define rl_outw(port, offset, value)	(my_outw((port) + (offset), (value)))
63433d6423SLionel Sambuc #define rl_outl(port, offset, value)	(my_outl((port) + (offset), (value)))
64433d6423SLionel Sambuc 
65*f7df02e7SDavid van Moolenbroek static int rl_init(unsigned int instance, netdriver_addr_t *addr,
66*f7df02e7SDavid van Moolenbroek 	uint32_t *caps, unsigned int *ticks);
674081bff6SDavid van Moolenbroek static int rl_probe(re_t *rep, unsigned int skip);
68433d6423SLionel Sambuc static void rl_init_buf(re_t *rep);
69*f7df02e7SDavid van Moolenbroek static void rl_init_hw(re_t *rep, netdriver_addr_t *addr,
70*f7df02e7SDavid van Moolenbroek 	unsigned int instance);
71433d6423SLionel Sambuc static void rl_reset_hw(re_t *rep);
72*f7df02e7SDavid van Moolenbroek static void rl_set_hwaddr(const netdriver_addr_t *addr);
73*f7df02e7SDavid van Moolenbroek static void rl_confaddr(re_t *rep, netdriver_addr_t *addr,
74*f7df02e7SDavid van Moolenbroek 	unsigned int instance);
754081bff6SDavid van Moolenbroek static void rl_stop(void);
76433d6423SLionel Sambuc static void rl_rec_mode(re_t *rep);
77*f7df02e7SDavid van Moolenbroek static void rl_set_mode(unsigned int mode, const netdriver_addr_t *mcast_list,
78*f7df02e7SDavid van Moolenbroek 	unsigned int mcast_count);
794081bff6SDavid van Moolenbroek static ssize_t rl_recv(struct netdriver_data *data, size_t max);
804081bff6SDavid van Moolenbroek static int rl_send(struct netdriver_data *data, size_t size);
81*f7df02e7SDavid van Moolenbroek static unsigned int rl_get_link(uint32_t *media);
824081bff6SDavid van Moolenbroek static void rl_intr(unsigned int mask);
83433d6423SLionel Sambuc static void rl_check_ints(re_t *rep);
844081bff6SDavid van Moolenbroek #if VERBOSE
85*f7df02e7SDavid van Moolenbroek static void rl_report_link(re_t *rep);
86433d6423SLionel Sambuc static void mii_print_techab(u16_t techab);
87433d6423SLionel Sambuc static void mii_print_stat_speed(u16_t stat, u16_t extstat);
884081bff6SDavid van Moolenbroek #endif
89433d6423SLionel Sambuc static void rl_clear_rx(re_t *rep);
90433d6423SLionel Sambuc static void rl_do_reset(re_t *rep);
914081bff6SDavid van Moolenbroek static void rl_other(const message *m_ptr, int ipc_status);
924081bff6SDavid van Moolenbroek static void rl_dump(void);
93433d6423SLionel Sambuc #if 0
94433d6423SLionel Sambuc static void dump_phy(re_t *rep);
95433d6423SLionel Sambuc #endif
96433d6423SLionel Sambuc static int rl_handler(re_t *rep);
97*f7df02e7SDavid van Moolenbroek static void rl_tick(void);
984081bff6SDavid van Moolenbroek static void tell_iommu(vir_bytes start, size_t size, int pci_bus, int
99433d6423SLionel Sambuc 	pci_dev, int pci_func);
100433d6423SLionel Sambuc 
1014081bff6SDavid van Moolenbroek static const struct netdriver rl_table = {
102*f7df02e7SDavid van Moolenbroek 	.ndr_name	= "rl",
1034081bff6SDavid van Moolenbroek 	.ndr_init	= rl_init,
1044081bff6SDavid van Moolenbroek 	.ndr_stop	= rl_stop,
105*f7df02e7SDavid van Moolenbroek 	.ndr_set_mode	= rl_set_mode,
106*f7df02e7SDavid van Moolenbroek 	.ndr_set_hwaddr	= rl_set_hwaddr,
1074081bff6SDavid van Moolenbroek 	.ndr_recv	= rl_recv,
1084081bff6SDavid van Moolenbroek 	.ndr_send	= rl_send,
109*f7df02e7SDavid van Moolenbroek 	.ndr_get_link	= rl_get_link,
1104081bff6SDavid van Moolenbroek 	.ndr_intr	= rl_intr,
111*f7df02e7SDavid van Moolenbroek 	.ndr_tick	= rl_tick,
1124081bff6SDavid van Moolenbroek 	.ndr_other	= rl_other,
1134081bff6SDavid van Moolenbroek };
114433d6423SLionel Sambuc 
115433d6423SLionel Sambuc /*===========================================================================*
116433d6423SLionel Sambuc  *				main					     *
117433d6423SLionel Sambuc  *===========================================================================*/
main(int argc,char * argv[])118433d6423SLionel Sambuc int main(int argc, char *argv[])
119433d6423SLionel Sambuc {
120433d6423SLionel Sambuc 
121433d6423SLionel Sambuc 	env_setargs(argc, argv);
122433d6423SLionel Sambuc 
1234081bff6SDavid van Moolenbroek 	netdriver_task(&rl_table);
124433d6423SLionel Sambuc 
1254081bff6SDavid van Moolenbroek 	return 0;
126433d6423SLionel Sambuc }
127433d6423SLionel Sambuc 
128433d6423SLionel Sambuc /*===========================================================================*
1294081bff6SDavid van Moolenbroek  *				rl_intr					     *
130433d6423SLionel Sambuc  *===========================================================================*/
rl_intr(unsigned int __unused mask)1314081bff6SDavid van Moolenbroek static void rl_intr(unsigned int __unused mask)
132433d6423SLionel Sambuc {
133433d6423SLionel Sambuc 	re_t *rep;
1344081bff6SDavid van Moolenbroek 	int s;
135433d6423SLionel Sambuc 
136433d6423SLionel Sambuc 	rep = &re_state;
137433d6423SLionel Sambuc 
1384081bff6SDavid van Moolenbroek 	/* Run interrupt handler at driver level. */
1394081bff6SDavid van Moolenbroek 	rl_handler(rep);
1404081bff6SDavid van Moolenbroek 
1414081bff6SDavid van Moolenbroek 	/* Reenable interrupts for this hook. */
1424081bff6SDavid van Moolenbroek 	if ((s = sys_irqenable(&rep->re_hook_id)) != OK)
1434081bff6SDavid van Moolenbroek 		printf("RTL8139: error, couldn't enable interrupts: %d\n", s);
1444081bff6SDavid van Moolenbroek 
1454081bff6SDavid van Moolenbroek 	/* Perform tasks based on the flagged conditions. */
146433d6423SLionel Sambuc 	rl_check_ints(rep);
147433d6423SLionel Sambuc }
148433d6423SLionel Sambuc 
149433d6423SLionel Sambuc /*===========================================================================*
1504081bff6SDavid van Moolenbroek  *				rl_other				     *
151433d6423SLionel Sambuc  *===========================================================================*/
rl_other(const message * m_ptr,int ipc_status)1524081bff6SDavid van Moolenbroek static void rl_other(const message *m_ptr, int ipc_status)
1534081bff6SDavid van Moolenbroek {
1544081bff6SDavid van Moolenbroek 	if (is_ipc_notify(ipc_status) && m_ptr->m_source == TTY_PROC_NR)
1554081bff6SDavid van Moolenbroek 		rl_dump();
1564081bff6SDavid van Moolenbroek }
1574081bff6SDavid van Moolenbroek 
1584081bff6SDavid van Moolenbroek /*===========================================================================*
1594081bff6SDavid van Moolenbroek  *				rl_stop					     *
1604081bff6SDavid van Moolenbroek  *===========================================================================*/
rl_stop(void)1614081bff6SDavid van Moolenbroek static void rl_stop(void)
1624081bff6SDavid van Moolenbroek {
1634081bff6SDavid van Moolenbroek 	re_t *rep;
1644081bff6SDavid van Moolenbroek 
1654081bff6SDavid van Moolenbroek 	rep = &re_state;
1664081bff6SDavid van Moolenbroek 
1674081bff6SDavid van Moolenbroek 	rl_outb(rep->re_base_port, RL_CR, 0);
1684081bff6SDavid van Moolenbroek }
1694081bff6SDavid van Moolenbroek 
1704081bff6SDavid van Moolenbroek /*===========================================================================*
1714081bff6SDavid van Moolenbroek  *				rl_dump					     *
1724081bff6SDavid van Moolenbroek  *===========================================================================*/
rl_dump(void)1734081bff6SDavid van Moolenbroek static void rl_dump(void)
174433d6423SLionel Sambuc {
175433d6423SLionel Sambuc 	re_t *rep;
176433d6423SLionel Sambuc 
177433d6423SLionel Sambuc 	rep= &re_state;
178433d6423SLionel Sambuc 
179433d6423SLionel Sambuc 	printf("\n");
180*f7df02e7SDavid van Moolenbroek 	printf("Realtek RTL 8139 device %s:\n", netdriver_name());
181433d6423SLionel Sambuc 
182433d6423SLionel Sambuc 	printf("TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
183433d6423SLionel Sambuc 		rl_inw(rep->re_base_port, RL_TSAD),
184433d6423SLionel Sambuc 		rl_inl(rep->re_base_port, RL_TSD0+0*4),
185433d6423SLionel Sambuc 		rl_inl(rep->re_base_port, RL_TSD0+1*4),
186433d6423SLionel Sambuc 		rl_inl(rep->re_base_port, RL_TSD0+2*4),
187433d6423SLionel Sambuc 		rl_inl(rep->re_base_port, RL_TSD0+3*4));
188433d6423SLionel Sambuc 	printf("tx_head %d, tx_tail %d, busy: %d %d %d %d\n",
189433d6423SLionel Sambuc 		rep->re_tx_head, rep->re_tx_tail,
190433d6423SLionel Sambuc 		rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy,
191433d6423SLionel Sambuc 		rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy);
192433d6423SLionel Sambuc }
193433d6423SLionel Sambuc 
194433d6423SLionel Sambuc /*===========================================================================*
195*f7df02e7SDavid van Moolenbroek  *				rl_set_mode				     *
196433d6423SLionel Sambuc  *===========================================================================*/
rl_set_mode(unsigned int mode,const netdriver_addr_t * mcast_list,unsigned int mcast_count)197*f7df02e7SDavid van Moolenbroek static void rl_set_mode(unsigned int mode, const netdriver_addr_t *mcast_list,
198*f7df02e7SDavid van Moolenbroek 	unsigned int mcast_count)
199433d6423SLionel Sambuc {
200433d6423SLionel Sambuc 	re_t *rep;
201433d6423SLionel Sambuc 
202433d6423SLionel Sambuc 	rep= &re_state;
203433d6423SLionel Sambuc 
2044081bff6SDavid van Moolenbroek 	rep->re_mode = mode;
205433d6423SLionel Sambuc 
206433d6423SLionel Sambuc 	rl_rec_mode(rep);
207433d6423SLionel Sambuc }
208433d6423SLionel Sambuc 
209433d6423SLionel Sambuc /*===========================================================================*
2104081bff6SDavid van Moolenbroek  *				rl_init					     *
211433d6423SLionel Sambuc  *===========================================================================*/
rl_init(unsigned int instance,netdriver_addr_t * addr,uint32_t * caps,unsigned int * ticks)212*f7df02e7SDavid van Moolenbroek static int rl_init(unsigned int instance, netdriver_addr_t *addr,
213*f7df02e7SDavid van Moolenbroek 	uint32_t *caps, unsigned int *ticks)
214433d6423SLionel Sambuc {
2154081bff6SDavid van Moolenbroek /* Initialize the rtl8139 driver. */
216433d6423SLionel Sambuc 	re_t *rep;
2174081bff6SDavid van Moolenbroek #if RTL8139_FKEY
2184081bff6SDavid van Moolenbroek 	int r, fkeys, sfkeys;
2194081bff6SDavid van Moolenbroek #endif
220433d6423SLionel Sambuc 
2214081bff6SDavid van Moolenbroek 	/* Initialize driver state. */
222433d6423SLionel Sambuc 	rep= &re_state;
2234081bff6SDavid van Moolenbroek 	memset(rep, 0, sizeof(*rep));
224433d6423SLionel Sambuc 
2254081bff6SDavid van Moolenbroek 	rep->re_link_up= -1;	/* Unknown */
2264081bff6SDavid van Moolenbroek 	rep->re_ertxth= RL_TSD_ERTXTH_8;
227433d6423SLionel Sambuc 
2284081bff6SDavid van Moolenbroek 	/* Try to find a matching device. */
2294081bff6SDavid van Moolenbroek 	if (!rl_probe(rep, instance))
2304081bff6SDavid van Moolenbroek 		return ENXIO;
2314081bff6SDavid van Moolenbroek 
2324081bff6SDavid van Moolenbroek 	/* Claim buffer memory. */
2334081bff6SDavid van Moolenbroek 	rl_init_buf(rep);
2344081bff6SDavid van Moolenbroek 
2354081bff6SDavid van Moolenbroek 	/* Initialize the device we found. */
236*f7df02e7SDavid van Moolenbroek 	rl_init_hw(rep, addr, instance);
2374081bff6SDavid van Moolenbroek 
2384081bff6SDavid van Moolenbroek #if VERBOSE
2394081bff6SDavid van Moolenbroek 	/* Report initial link status. */
2404081bff6SDavid van Moolenbroek 	rl_report_link(rep);
2414081bff6SDavid van Moolenbroek #endif
2424081bff6SDavid van Moolenbroek 
2434081bff6SDavid van Moolenbroek #if RTL8139_FKEY
2444081bff6SDavid van Moolenbroek 	/* Observe some function key for debug dumps. */
2454081bff6SDavid van Moolenbroek 	fkeys = sfkeys = 0; bit_set(sfkeys, 9);
2464081bff6SDavid van Moolenbroek 	if ((r = fkey_map(&fkeys, &sfkeys)) != OK)
2474081bff6SDavid van Moolenbroek 	    printf("Warning: RTL8139 couldn't observe Shift+F9 key: %d\n",r);
2484081bff6SDavid van Moolenbroek #endif
2494081bff6SDavid van Moolenbroek 
250*f7df02e7SDavid van Moolenbroek 	*caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST | NDEV_CAP_HWADDR;
251*f7df02e7SDavid van Moolenbroek 	*ticks = sys_hz();
2524081bff6SDavid van Moolenbroek 	return OK;
253433d6423SLionel Sambuc }
254433d6423SLionel Sambuc 
255433d6423SLionel Sambuc /*===========================================================================*
256433d6423SLionel Sambuc  *				rl_probe				     *
257433d6423SLionel Sambuc  *===========================================================================*/
rl_probe(re_t * rep,unsigned int skip)2584081bff6SDavid van Moolenbroek static int rl_probe(re_t *rep, unsigned int skip)
259433d6423SLionel Sambuc {
260433d6423SLionel Sambuc 	int r, devind;
261b49f4cacSDavid van Moolenbroek 	u16_t cr, vid, did;
262433d6423SLionel Sambuc 	u32_t bar;
263433d6423SLionel Sambuc 	u8_t ilr;
264433d6423SLionel Sambuc #if VERBOSE
265*f7df02e7SDavid van Moolenbroek 	const char *dname;
266433d6423SLionel Sambuc #endif
267433d6423SLionel Sambuc 
2684081bff6SDavid van Moolenbroek 	pci_init();
2694081bff6SDavid van Moolenbroek 
270433d6423SLionel Sambuc 	r= pci_first_dev(&devind, &vid, &did);
271433d6423SLionel Sambuc 	if (r == 0)
272433d6423SLionel Sambuc 		return 0;
273433d6423SLionel Sambuc 
274433d6423SLionel Sambuc 	while (skip--)
275433d6423SLionel Sambuc 	{
276433d6423SLionel Sambuc 		r= pci_next_dev(&devind, &vid, &did);
277433d6423SLionel Sambuc 		if (!r)
278433d6423SLionel Sambuc 			return 0;
279433d6423SLionel Sambuc 	}
280433d6423SLionel Sambuc 
281433d6423SLionel Sambuc #if VERBOSE	/* stay silent at startup, can always get status later */
282433d6423SLionel Sambuc 	dname= pci_dev_name(vid, did);
283433d6423SLionel Sambuc 	if (!dname)
284433d6423SLionel Sambuc 		dname= "unknown device";
285*f7df02e7SDavid van Moolenbroek 	printf("%s: ", netdriver_name());
286433d6423SLionel Sambuc 	printf("%s (%x/%x) at %s\n", dname, vid, did, pci_slot_name(devind));
287433d6423SLionel Sambuc #endif
288433d6423SLionel Sambuc 	pci_reserve(devind);
289b49f4cacSDavid van Moolenbroek 
290b49f4cacSDavid van Moolenbroek 	/* Enable bus mastering if necessary. */
291b49f4cacSDavid van Moolenbroek 	cr = pci_attr_r16(devind, PCI_CR);
292b49f4cacSDavid van Moolenbroek 	/* printf("cr = 0x%x\n", cr); */
293b49f4cacSDavid van Moolenbroek 	if (!(cr & PCI_CR_MAST_EN))
294b49f4cacSDavid van Moolenbroek 		pci_attr_w16(devind, PCI_CR, cr | PCI_CR_MAST_EN);
295b49f4cacSDavid van Moolenbroek 
296433d6423SLionel Sambuc 	bar= pci_attr_r32(devind, PCI_BAR) & 0xffffffe0;
297433d6423SLionel Sambuc 	if (bar < 0x400) {
298433d6423SLionel Sambuc 		panic("base address is not properly configured");
299433d6423SLionel Sambuc 	}
300433d6423SLionel Sambuc 	rep->re_base_port= bar;
301433d6423SLionel Sambuc 
302433d6423SLionel Sambuc 	ilr= pci_attr_r8(devind, PCI_ILR);
303433d6423SLionel Sambuc 	rep->re_irq= ilr;
3044081bff6SDavid van Moolenbroek #if VERBOSE
305433d6423SLionel Sambuc 	printf("%s: using I/O address 0x%lx, IRQ %d\n",
306*f7df02e7SDavid van Moolenbroek 		netdriver_name(), (unsigned long)bar, ilr);
3074081bff6SDavid van Moolenbroek #endif
308433d6423SLionel Sambuc 
309433d6423SLionel Sambuc 	return TRUE;
310433d6423SLionel Sambuc }
311433d6423SLionel Sambuc 
312433d6423SLionel Sambuc /*===========================================================================*
313433d6423SLionel Sambuc  *				rl_init_buf				     *
314433d6423SLionel Sambuc  *===========================================================================*/
rl_init_buf(re_t * rep)3154081bff6SDavid van Moolenbroek static void rl_init_buf(re_t *rep)
316433d6423SLionel Sambuc {
317433d6423SLionel Sambuc 	size_t rx_bufsize, tx_bufsize, tot_bufsize;
318433d6423SLionel Sambuc 	phys_bytes buf;
319433d6423SLionel Sambuc 	char *mallocbuf;
320433d6423SLionel Sambuc 	int i, off;
321433d6423SLionel Sambuc 
322433d6423SLionel Sambuc 	/* Allocate receive and transmit buffers */
323*f7df02e7SDavid van Moolenbroek 	tx_bufsize= NDEV_ETH_PACKET_MAX_TAGGED;
324433d6423SLionel Sambuc 	if (tx_bufsize % 4)
325433d6423SLionel Sambuc 		tx_bufsize += 4-(tx_bufsize % 4);	/* Align */
326433d6423SLionel Sambuc 	rx_bufsize= RX_BUFSIZE;
327433d6423SLionel Sambuc 	tot_bufsize= N_TX_BUF*tx_bufsize + rx_bufsize;
328433d6423SLionel Sambuc 
329433d6423SLionel Sambuc 	if (tot_bufsize % 4096)
330433d6423SLionel Sambuc 		tot_bufsize += 4096-(tot_bufsize % 4096);
331433d6423SLionel Sambuc 
332433d6423SLionel Sambuc #define BUF_ALIGNMENT (64*1024)
333433d6423SLionel Sambuc 
3344081bff6SDavid van Moolenbroek 	if (!(mallocbuf = alloc_contig(BUF_ALIGNMENT + tot_bufsize, 0, &buf)))
335433d6423SLionel Sambuc 		panic("Couldn't allocate kernel buffer");
336433d6423SLionel Sambuc 
337433d6423SLionel Sambuc 	/* click-align mallocced buffer. this is what we used to get
338433d6423SLionel Sambuc 	 * from kmalloc() too.
339433d6423SLionel Sambuc 	 */
340433d6423SLionel Sambuc 	if((off = buf % BUF_ALIGNMENT)) {
341433d6423SLionel Sambuc 		mallocbuf += BUF_ALIGNMENT - off;
342433d6423SLionel Sambuc 		buf += BUF_ALIGNMENT - off;
343433d6423SLionel Sambuc 	}
344433d6423SLionel Sambuc 
3454081bff6SDavid van Moolenbroek 	tell_iommu((vir_bytes)mallocbuf, tot_bufsize, 0, 0, 0);
346433d6423SLionel Sambuc 
347433d6423SLionel Sambuc 	for (i= 0; i<N_TX_BUF; i++)
348433d6423SLionel Sambuc 	{
349433d6423SLionel Sambuc 		rep->re_tx[i].ret_buf= buf;
350433d6423SLionel Sambuc 		rep->re_tx[i].v_ret_buf= mallocbuf;
351433d6423SLionel Sambuc 		buf += tx_bufsize;
352433d6423SLionel Sambuc 		mallocbuf += tx_bufsize;
353433d6423SLionel Sambuc 	}
354433d6423SLionel Sambuc 	rep->re_rx_buf= buf;
355433d6423SLionel Sambuc 	rep->v_re_rx_buf= mallocbuf;
356433d6423SLionel Sambuc }
357433d6423SLionel Sambuc 
358433d6423SLionel Sambuc /*===========================================================================*
359433d6423SLionel Sambuc  *				rl_init_hw				     *
360433d6423SLionel Sambuc  *===========================================================================*/
rl_init_hw(re_t * rep,netdriver_addr_t * addr,unsigned int instance)361*f7df02e7SDavid van Moolenbroek static void rl_init_hw(re_t *rep, netdriver_addr_t *addr,
362*f7df02e7SDavid van Moolenbroek 	unsigned int instance)
363433d6423SLionel Sambuc {
3644081bff6SDavid van Moolenbroek #if VERBOSE
3654081bff6SDavid van Moolenbroek 	int i;
3664081bff6SDavid van Moolenbroek #endif
3674081bff6SDavid van Moolenbroek 	int s;
368433d6423SLionel Sambuc 
369433d6423SLionel Sambuc 	/* Set the interrupt handler. The policy is to only send HARD_INT
370433d6423SLionel Sambuc 	 * notifications. Don't reenable interrupts automatically. The id
371433d6423SLionel Sambuc 	 * that is passed back is the interrupt line number.
372433d6423SLionel Sambuc 	 */
373433d6423SLionel Sambuc 	rep->re_hook_id = rep->re_irq;
374433d6423SLionel Sambuc 	if ((s=sys_irqsetpolicy(rep->re_irq, 0, &rep->re_hook_id)) != OK)
375433d6423SLionel Sambuc 		printf("RTL8139: error, couldn't set IRQ policy: %d\n", s);
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc 	rl_reset_hw(rep);
378433d6423SLionel Sambuc 
379433d6423SLionel Sambuc 	if ((s=sys_irqenable(&rep->re_hook_id)) != OK)
380433d6423SLionel Sambuc 		printf("RTL8139: error, couldn't enable interrupts: %d\n", s);
381433d6423SLionel Sambuc 
382433d6423SLionel Sambuc #if VERBOSE	/* stay silent during startup, can always get status later */
383433d6423SLionel Sambuc 	if (rep->re_model) {
384*f7df02e7SDavid van Moolenbroek 		printf("%s: model %s\n", netdriver_name(), rep->re_model);
385433d6423SLionel Sambuc 	} else
386433d6423SLionel Sambuc 	{
387433d6423SLionel Sambuc 		printf("%s: unknown model 0x%08x\n",
388*f7df02e7SDavid van Moolenbroek 			netdriver_name(),
389433d6423SLionel Sambuc 			rl_inl(rep->re_base_port, RL_TCR) &
390433d6423SLionel Sambuc 			(RL_TCR_HWVER_AM | RL_TCR_HWVER_BM));
391433d6423SLionel Sambuc 	}
392433d6423SLionel Sambuc #endif
393433d6423SLionel Sambuc 
394*f7df02e7SDavid van Moolenbroek 	rl_confaddr(rep, addr, instance);
3954081bff6SDavid van Moolenbroek 
3964081bff6SDavid van Moolenbroek #if VERBOSE
397*f7df02e7SDavid van Moolenbroek 	printf("%s: Ethernet address ", netdriver_name());
398433d6423SLionel Sambuc 	for (i= 0; i < 6; i++)
399*f7df02e7SDavid van Moolenbroek 		printf("%x%c", addr->na_addr[i], i < 5 ? ':' : '\n');
4004081bff6SDavid van Moolenbroek #endif
401433d6423SLionel Sambuc }
402433d6423SLionel Sambuc 
403433d6423SLionel Sambuc /*===========================================================================*
404433d6423SLionel Sambuc  *				rl_reset_hw				     *
405433d6423SLionel Sambuc  *===========================================================================*/
rl_reset_hw(re_t * rep)4064081bff6SDavid van Moolenbroek static void rl_reset_hw(re_t *rep)
407433d6423SLionel Sambuc {
408433d6423SLionel Sambuc 	port_t port;
409433d6423SLionel Sambuc 	u32_t t;
410433d6423SLionel Sambuc 	phys_bytes bus_buf;
411433d6423SLionel Sambuc 	int i;
412433d6423SLionel Sambuc 
413433d6423SLionel Sambuc 	port= rep->re_base_port;
414433d6423SLionel Sambuc 
415433d6423SLionel Sambuc #if 0
416433d6423SLionel Sambuc 	/* Reset the PHY */
417433d6423SLionel Sambuc 	rl_outb(port, RL_BMCR, MII_CTRL_RST);
418433d6423SLionel Sambuc 	SPIN_UNTIL(!(rl_inb(port, RL_BMCR) & MII_CTRL_RST), 1000000);
419433d6423SLionel Sambuc 	if (rl_inb(port, RL_BMCR) & MII_CTRL_RST)
420433d6423SLionel Sambuc 		panic("reset PHY failed to complete");
421433d6423SLionel Sambuc #endif
422433d6423SLionel Sambuc 
423433d6423SLionel Sambuc 	/* Reset the device */
424433d6423SLionel Sambuc #if VERBOSE
425433d6423SLionel Sambuc 	printf("rl_reset_hw: (before reset) port = 0x%x, RL_CR = 0x%x\n",
426433d6423SLionel Sambuc 		port, rl_inb(port, RL_CR));
427433d6423SLionel Sambuc #endif
428433d6423SLionel Sambuc 	rl_outb(port, RL_CR, RL_CR_RST);
429433d6423SLionel Sambuc 	SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_RST), 1000000);
430433d6423SLionel Sambuc #if VERBOSE
431433d6423SLionel Sambuc 	printf("rl_reset_hw: (after reset) port = 0x%x, RL_CR = 0x%x\n",
432433d6423SLionel Sambuc 		port, rl_inb(port, RL_CR));
433433d6423SLionel Sambuc #endif
434433d6423SLionel Sambuc 	if (rl_inb(port, RL_CR) & RL_CR_RST)
435433d6423SLionel Sambuc 		printf("rtl8139: reset failed to complete");
436433d6423SLionel Sambuc 
437433d6423SLionel Sambuc 	t= rl_inl(port, RL_TCR);
438433d6423SLionel Sambuc 	switch(t & (RL_TCR_HWVER_AM | RL_TCR_HWVER_BM))
439433d6423SLionel Sambuc 	{
440433d6423SLionel Sambuc 	case RL_TCR_HWVER_RTL8139: rep->re_model= "RTL8139"; break;
441433d6423SLionel Sambuc 	case RL_TCR_HWVER_RTL8139A: rep->re_model= "RTL8139A"; break;
442433d6423SLionel Sambuc 	case RL_TCR_HWVER_RTL8139AG:
443433d6423SLionel Sambuc 		rep->re_model= "RTL8139A-G / RTL8139C";
444433d6423SLionel Sambuc 		break;
445433d6423SLionel Sambuc 	case RL_TCR_HWVER_RTL8139B:
446433d6423SLionel Sambuc 		rep->re_model= "RTL8139B / RTL8130";
447433d6423SLionel Sambuc 		break;
448433d6423SLionel Sambuc 	case RL_TCR_HWVER_RTL8100: rep->re_model= "RTL8100"; break;
449433d6423SLionel Sambuc 	case RL_TCR_HWVER_RTL8100B:
450433d6423SLionel Sambuc 		rep->re_model= "RTL8100B/RTL8139D";
451433d6423SLionel Sambuc 		break;
452433d6423SLionel Sambuc 	case RL_TCR_HWVER_RTL8139CP: rep->re_model= "RTL8139C+"; break;
453433d6423SLionel Sambuc 	case RL_TCR_HWVER_RTL8101: rep->re_model= "RTL8101"; break;
454433d6423SLionel Sambuc 	default:
455433d6423SLionel Sambuc 		rep->re_model= NULL;
456433d6423SLionel Sambuc 		break;
457433d6423SLionel Sambuc 	}
458433d6423SLionel Sambuc 
459433d6423SLionel Sambuc #if 0
460433d6423SLionel Sambuc 	printf("REVID: 0x%02x\n", rl_inb(port, RL_REVID));
461433d6423SLionel Sambuc #endif
462433d6423SLionel Sambuc 
463433d6423SLionel Sambuc 	/* Intialize Rx */
464433d6423SLionel Sambuc 
465433d6423SLionel Sambuc 	/* Should init multicast mask */
466433d6423SLionel Sambuc #if 0
467433d6423SLionel Sambuc 08-0f	R/W	MAR[0-7]	multicast
468433d6423SLionel Sambuc #endif
469433d6423SLionel Sambuc 	bus_buf= vm_1phys2bus(rep->re_rx_buf);
470433d6423SLionel Sambuc 	rl_outl(port, RL_RBSTART, bus_buf);
471433d6423SLionel Sambuc 
472433d6423SLionel Sambuc 	/* Initialize Tx */
473433d6423SLionel Sambuc 	for (i= 0; i<N_TX_BUF; i++)
474433d6423SLionel Sambuc 	{
475433d6423SLionel Sambuc 		rep->re_tx[i].ret_busy= FALSE;
476433d6423SLionel Sambuc 		bus_buf= vm_1phys2bus(rep->re_tx[i].ret_buf);
477433d6423SLionel Sambuc 		rl_outl(port, RL_TSAD0+i*4, bus_buf);
478433d6423SLionel Sambuc 		t= rl_inl(port, RL_TSD0+i*4);
479433d6423SLionel Sambuc 		assert(t & RL_TSD_OWN);
480433d6423SLionel Sambuc 	}
481433d6423SLionel Sambuc 
4824081bff6SDavid van Moolenbroek 	rep->re_tx_busy = 0;
4834081bff6SDavid van Moolenbroek 
484433d6423SLionel Sambuc #if 0
485433d6423SLionel Sambuc 	dump_phy(rep);
486433d6423SLionel Sambuc #endif
487433d6423SLionel Sambuc 
488433d6423SLionel Sambuc 	t= rl_inw(port, RL_IMR);
489433d6423SLionel Sambuc 	rl_outw(port, RL_IMR, t | (RL_IMR_SERR | RL_IMR_TIMEOUT |
490433d6423SLionel Sambuc 		RL_IMR_LENCHG));
491433d6423SLionel Sambuc 
492433d6423SLionel Sambuc 	t= rl_inw(port, RL_IMR);
493433d6423SLionel Sambuc 	rl_outw(port, RL_IMR, t | (RL_IMR_FOVW | RL_IMR_PUN |
494433d6423SLionel Sambuc 		RL_IMR_RXOVW | RL_IMR_RER | RL_IMR_ROK));
495433d6423SLionel Sambuc 
496433d6423SLionel Sambuc 	t= rl_inw(port, RL_IMR);
497433d6423SLionel Sambuc 	rl_outw(port, RL_IMR, t | (RL_IMR_TER | RL_IMR_TOK));
498433d6423SLionel Sambuc 
499433d6423SLionel Sambuc 	t= rl_inb(port, RL_CR);
500433d6423SLionel Sambuc 	rl_outb(port, RL_CR, t | RL_CR_RE);
501433d6423SLionel Sambuc 
502433d6423SLionel Sambuc 	t= rl_inb(port, RL_CR);
503433d6423SLionel Sambuc 	rl_outb(port, RL_CR, t | RL_CR_TE);
504433d6423SLionel Sambuc 
505433d6423SLionel Sambuc 	rl_outl(port, RL_RCR, RX_BUFBITS);
506433d6423SLionel Sambuc 
507433d6423SLionel Sambuc 	t= rl_inl(port, RL_TCR);
508433d6423SLionel Sambuc 	rl_outl(port, RL_TCR, t | RL_TCR_IFG_STD);
509433d6423SLionel Sambuc }
510433d6423SLionel Sambuc 
511433d6423SLionel Sambuc /*===========================================================================*
512*f7df02e7SDavid van Moolenbroek  *				rl_set_hwaddr				     *
513*f7df02e7SDavid van Moolenbroek  *===========================================================================*/
rl_set_hwaddr(const netdriver_addr_t * addr)514*f7df02e7SDavid van Moolenbroek static void rl_set_hwaddr(const netdriver_addr_t *addr)
515*f7df02e7SDavid van Moolenbroek {
516*f7df02e7SDavid van Moolenbroek 	re_t *rep;
517*f7df02e7SDavid van Moolenbroek 	port_t port;
518*f7df02e7SDavid van Moolenbroek 	u32_t w;
519*f7df02e7SDavid van Moolenbroek 	int i;
520*f7df02e7SDavid van Moolenbroek 
521*f7df02e7SDavid van Moolenbroek 	rep = &re_state;
522*f7df02e7SDavid van Moolenbroek 
523*f7df02e7SDavid van Moolenbroek 	port= rep->re_base_port;
524*f7df02e7SDavid van Moolenbroek 	rl_outb(port, RL_9346CR, RL_9346CR_EEM_CONFIG);
525*f7df02e7SDavid van Moolenbroek 	w= 0;
526*f7df02e7SDavid van Moolenbroek 	for (i= 0; i<4; i++)
527*f7df02e7SDavid van Moolenbroek 		w |= (addr->na_addr[i] << (i*8));
528*f7df02e7SDavid van Moolenbroek 	rl_outl(port, RL_IDR, w);
529*f7df02e7SDavid van Moolenbroek 	w= 0;
530*f7df02e7SDavid van Moolenbroek 	for (i= 4; i<6; i++)
531*f7df02e7SDavid van Moolenbroek 		w |= (addr->na_addr[i] << ((i-4)*8));
532*f7df02e7SDavid van Moolenbroek 	rl_outl(port, RL_IDR+4, w);
533*f7df02e7SDavid van Moolenbroek 	rl_outb(port, RL_9346CR, RL_9346CR_EEM_NORMAL);
534*f7df02e7SDavid van Moolenbroek }
535*f7df02e7SDavid van Moolenbroek 
536*f7df02e7SDavid van Moolenbroek /*===========================================================================*
537433d6423SLionel Sambuc  *				rl_confaddr				     *
538433d6423SLionel Sambuc  *===========================================================================*/
rl_confaddr(re_t * rep,netdriver_addr_t * addr,unsigned int instance)539*f7df02e7SDavid van Moolenbroek static void rl_confaddr(re_t *rep, netdriver_addr_t *addr,
540*f7df02e7SDavid van Moolenbroek 	unsigned int instance)
541433d6423SLionel Sambuc {
542433d6423SLionel Sambuc 	static char eakey[]= RL_ENVVAR "#_EA";
543433d6423SLionel Sambuc 	static char eafmt[]= "x:x:x:x:x:x";
544433d6423SLionel Sambuc 	port_t port;
545*f7df02e7SDavid van Moolenbroek 	int i;
546433d6423SLionel Sambuc 	long v;
547433d6423SLionel Sambuc 
548433d6423SLionel Sambuc 	/* User defined ethernet address? */
549*f7df02e7SDavid van Moolenbroek 	eakey[sizeof(RL_ENVVAR)-1]= '0' + instance;
550433d6423SLionel Sambuc 
551433d6423SLionel Sambuc 	for (i= 0; i < 6; i++)
552433d6423SLionel Sambuc 	{
553433d6423SLionel Sambuc 		if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET)
554433d6423SLionel Sambuc 			break;
555*f7df02e7SDavid van Moolenbroek 		addr->na_addr[i]= v;
556433d6423SLionel Sambuc 	}
557433d6423SLionel Sambuc 
558433d6423SLionel Sambuc 	if (i != 0 && i != 6) env_panic(eakey);	/* It's all or nothing */
559433d6423SLionel Sambuc 
560433d6423SLionel Sambuc 	/* Should update ethernet address in hardware */
561433d6423SLionel Sambuc 	if (i == 6)
562*f7df02e7SDavid van Moolenbroek 		rl_set_hwaddr(addr);
563433d6423SLionel Sambuc 
564433d6423SLionel Sambuc 	/* Get ethernet address */
565*f7df02e7SDavid van Moolenbroek 	port= rep->re_base_port;
566*f7df02e7SDavid van Moolenbroek 
567433d6423SLionel Sambuc 	for (i= 0; i<6; i++)
568*f7df02e7SDavid van Moolenbroek 		addr->na_addr[i]= rl_inb(port, RL_IDR+i);
569433d6423SLionel Sambuc }
570433d6423SLionel Sambuc 
571433d6423SLionel Sambuc /*===========================================================================*
572433d6423SLionel Sambuc  *				rl_rec_mode				     *
573433d6423SLionel Sambuc  *===========================================================================*/
rl_rec_mode(re_t * rep)5744081bff6SDavid van Moolenbroek static void rl_rec_mode(re_t *rep)
575433d6423SLionel Sambuc {
576433d6423SLionel Sambuc 	port_t port;
577433d6423SLionel Sambuc 	u32_t rcr;
578433d6423SLionel Sambuc 
579433d6423SLionel Sambuc 	port= rep->re_base_port;
580433d6423SLionel Sambuc 	rcr= rl_inl(port, RL_RCR);
581433d6423SLionel Sambuc 	rcr &= ~(RL_RCR_AB|RL_RCR_AM|RL_RCR_APM|RL_RCR_AAP);
582*f7df02e7SDavid van Moolenbroek 	if (rep->re_mode & NDEV_MODE_PROMISC)
583433d6423SLionel Sambuc 		rcr |= RL_RCR_AB | RL_RCR_AM | RL_RCR_AAP;
584*f7df02e7SDavid van Moolenbroek 	if (rep->re_mode & NDEV_MODE_BCAST)
585433d6423SLionel Sambuc 		rcr |= RL_RCR_AB;
586*f7df02e7SDavid van Moolenbroek 	if (rep->re_mode & (NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
587433d6423SLionel Sambuc 		rcr |= RL_RCR_AM;
588433d6423SLionel Sambuc 	rcr |= RL_RCR_APM;
589433d6423SLionel Sambuc 
590433d6423SLionel Sambuc 	rl_outl(port, RL_RCR, rcr);
591433d6423SLionel Sambuc }
592433d6423SLionel Sambuc 
593433d6423SLionel Sambuc /*===========================================================================*
5944081bff6SDavid van Moolenbroek  *				rl_recv					     *
595433d6423SLionel Sambuc  *===========================================================================*/
rl_recv(struct netdriver_data * data,size_t max)5964081bff6SDavid van Moolenbroek static ssize_t rl_recv(struct netdriver_data *data, size_t max)
597433d6423SLionel Sambuc {
5984081bff6SDavid van Moolenbroek 	int o, s;
599433d6423SLionel Sambuc 	port_t port;
600433d6423SLionel Sambuc 	unsigned amount, totlen, packlen;
601433d6423SLionel Sambuc 	u16_t d_start, d_end;
6024081bff6SDavid van Moolenbroek 	u32_t l, rxstat;
603433d6423SLionel Sambuc 	re_t *rep;
604433d6423SLionel Sambuc 
605433d6423SLionel Sambuc 	rep= &re_state;
606433d6423SLionel Sambuc 
607433d6423SLionel Sambuc 	if (rep->re_clear_rx)
6084081bff6SDavid van Moolenbroek 		return SUSPEND;	/* Buffer overflow */
609433d6423SLionel Sambuc 
610433d6423SLionel Sambuc 	port= rep->re_base_port;
611433d6423SLionel Sambuc 
6124081bff6SDavid van Moolenbroek 	if (rl_inb(port, RL_CR) & RL_CR_BUFE)
613433d6423SLionel Sambuc 	{
614433d6423SLionel Sambuc 		/* Receive buffer is empty, suspend */
6154081bff6SDavid van Moolenbroek 		return SUSPEND;
616433d6423SLionel Sambuc 	}
617433d6423SLionel Sambuc 
618433d6423SLionel Sambuc 	d_start= rl_inw(port, RL_CAPR) + RL_CAPR_DATA_OFF;
619433d6423SLionel Sambuc 	d_end= rl_inw(port, RL_CBR) % RX_BUFSIZE;
620433d6423SLionel Sambuc 
621433d6423SLionel Sambuc #if RX_BUFSIZE <= USHRT_MAX
622433d6423SLionel Sambuc 	if (d_start >= RX_BUFSIZE)
623433d6423SLionel Sambuc 	{
6244081bff6SDavid van Moolenbroek 		printf("rl_recv: strange value in RL_CAPR: 0x%x\n",
625433d6423SLionel Sambuc 			rl_inw(port, RL_CAPR));
626433d6423SLionel Sambuc 		d_start %= RX_BUFSIZE;
627433d6423SLionel Sambuc 	}
628433d6423SLionel Sambuc #endif
629433d6423SLionel Sambuc 
630433d6423SLionel Sambuc 	if (d_end > d_start)
631433d6423SLionel Sambuc 		amount= d_end-d_start;
632433d6423SLionel Sambuc 	else
633433d6423SLionel Sambuc 		amount= d_end+RX_BUFSIZE - d_start;
634433d6423SLionel Sambuc 
635433d6423SLionel Sambuc 	rxstat = *(u32_t *) (rep->v_re_rx_buf + d_start);
636433d6423SLionel Sambuc 
637433d6423SLionel Sambuc 	/* Should convert from little endian to host byte order */
638433d6423SLionel Sambuc 
639433d6423SLionel Sambuc 	if (!(rxstat & RL_RXS_ROK))
640433d6423SLionel Sambuc 	{
641433d6423SLionel Sambuc 		printf("rxstat = 0x%08x\n", rxstat);
642433d6423SLionel Sambuc 		printf("d_start: 0x%x, d_end: 0x%x, rxstat: 0x%x\n",
643433d6423SLionel Sambuc 			d_start, d_end, rxstat);
644433d6423SLionel Sambuc 		panic("received packet not OK");
645433d6423SLionel Sambuc 	}
646433d6423SLionel Sambuc 	totlen= (rxstat >> RL_RXS_LEN_S);
647*f7df02e7SDavid van Moolenbroek 	if (totlen < 8 || totlen > 2*NDEV_ETH_PACKET_MAX)
648433d6423SLionel Sambuc 	{
649433d6423SLionel Sambuc 		/* Someting went wrong */
650433d6423SLionel Sambuc 		printf(
6514081bff6SDavid van Moolenbroek 		"rl_recv: bad length (%u) in status 0x%08x at offset 0x%x\n",
652433d6423SLionel Sambuc 			totlen, rxstat, d_start);
653433d6423SLionel Sambuc 		printf(
654433d6423SLionel Sambuc 		"d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%x\n",
655433d6423SLionel Sambuc 			d_start, d_end, totlen, rxstat);
656433d6423SLionel Sambuc 		panic(NULL);
657433d6423SLionel Sambuc 	}
658433d6423SLionel Sambuc 
659433d6423SLionel Sambuc #if 0
660433d6423SLionel Sambuc 	printf("d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%x\n",
661433d6423SLionel Sambuc 		d_start, d_end, totlen, rxstat);
662433d6423SLionel Sambuc #endif
663433d6423SLionel Sambuc 
664433d6423SLionel Sambuc 	if (totlen+4 > amount)
665433d6423SLionel Sambuc 	{
6664081bff6SDavid van Moolenbroek 		printf("rl_recv: packet not yet ready\n");
6674081bff6SDavid van Moolenbroek 		return SUSPEND;
668433d6423SLionel Sambuc 	}
669433d6423SLionel Sambuc 
670433d6423SLionel Sambuc 	/* Should subtract the CRC */
671*f7df02e7SDavid van Moolenbroek 	packlen = MIN(totlen - NDEV_ETH_PACKET_CRC, max);
672433d6423SLionel Sambuc 
6734081bff6SDavid van Moolenbroek 	/* Copy out the data.  The packet may wrap in the receive buffer. */
6744081bff6SDavid van Moolenbroek 	o = (d_start+4) % RX_BUFSIZE;
675*f7df02e7SDavid van Moolenbroek 	s = MIN(RX_BUFSIZE - o, (int)packlen);
676433d6423SLionel Sambuc 
6774081bff6SDavid van Moolenbroek 	netdriver_copyout(data, 0, rep->v_re_rx_buf + o, s);
678*f7df02e7SDavid van Moolenbroek 	if (s < (int)packlen)
6794081bff6SDavid van Moolenbroek 		netdriver_copyout(data, s, rep->v_re_rx_buf, packlen - s);
680433d6423SLionel Sambuc 
681433d6423SLionel Sambuc 	/* Avoid overflow in 16-bit computations */
682433d6423SLionel Sambuc 	l= d_start;
683433d6423SLionel Sambuc 	l += totlen+4;
684433d6423SLionel Sambuc 	l= (l+3) & ~3;	/* align */
685433d6423SLionel Sambuc 	if (l >= RX_BUFSIZE)
686433d6423SLionel Sambuc 	{
687433d6423SLionel Sambuc 		l -= RX_BUFSIZE;
688433d6423SLionel Sambuc 		assert(l < RX_BUFSIZE);
689433d6423SLionel Sambuc 	}
690433d6423SLionel Sambuc 	rl_outw(port, RL_CAPR, l-RL_CAPR_DATA_OFF);
691433d6423SLionel Sambuc 
6924081bff6SDavid van Moolenbroek 	return packlen;
693433d6423SLionel Sambuc }
694433d6423SLionel Sambuc 
695433d6423SLionel Sambuc /*===========================================================================*
6964081bff6SDavid van Moolenbroek  *				rl_send					     *
697433d6423SLionel Sambuc  *===========================================================================*/
rl_send(struct netdriver_data * data,size_t size)6984081bff6SDavid van Moolenbroek static int rl_send(struct netdriver_data *data, size_t size)
699433d6423SLionel Sambuc {
700433d6423SLionel Sambuc 	int tx_head;
701433d6423SLionel Sambuc 	re_t *rep;
702433d6423SLionel Sambuc 
703433d6423SLionel Sambuc 	rep= &re_state;
704433d6423SLionel Sambuc 
705433d6423SLionel Sambuc 	tx_head= rep->re_tx_head;
706433d6423SLionel Sambuc 	if (rep->re_tx[tx_head].ret_busy)
7074081bff6SDavid van Moolenbroek 		return SUSPEND;
708433d6423SLionel Sambuc 
7094081bff6SDavid van Moolenbroek 	netdriver_copyin(data, 0, rep->re_tx[tx_head].v_ret_buf, size);
710433d6423SLionel Sambuc 
7114081bff6SDavid van Moolenbroek 	rl_outl(rep->re_base_port, RL_TSD0+tx_head*4, rep->re_ertxth | size);
712433d6423SLionel Sambuc 	rep->re_tx[tx_head].ret_busy= TRUE;
7134081bff6SDavid van Moolenbroek 	rep->re_tx_busy++;
714433d6423SLionel Sambuc 
715433d6423SLionel Sambuc 	if (++tx_head == N_TX_BUF)
716433d6423SLionel Sambuc 		tx_head= 0;
717433d6423SLionel Sambuc 	assert(tx_head < RL_N_TX);
718433d6423SLionel Sambuc 	rep->re_tx_head= tx_head;
719433d6423SLionel Sambuc 
7204081bff6SDavid van Moolenbroek 	return OK;
721433d6423SLionel Sambuc }
722433d6423SLionel Sambuc 
723433d6423SLionel Sambuc /*===========================================================================*
724433d6423SLionel Sambuc  *				rl_check_ints				     *
725433d6423SLionel Sambuc  *===========================================================================*/
rl_check_ints(re_t * rep)7264081bff6SDavid van Moolenbroek static void rl_check_ints(re_t *rep)
727433d6423SLionel Sambuc {
728433d6423SLionel Sambuc #if 0
729433d6423SLionel Sambuc 10-1f	R/W	TSD[0-3]	Transmit Status of Descriptor [0-3]
730433d6423SLionel Sambuc 	31	R	CRS	Carrier Sense Lost
731433d6423SLionel Sambuc 	30	R	TABT	Transmit Abort
732433d6423SLionel Sambuc 	29	R	OWC	Out of Window Collision
733433d6423SLionel Sambuc 	27-24	R	NCC[3-0] Number of Collision Count
734433d6423SLionel Sambuc 	23-22			reserved
735433d6423SLionel Sambuc 	21-16	R/W	ERTXH[5-0] Early Tx Threshold
736433d6423SLionel Sambuc 	15	R	TOK	Transmit OK
737433d6423SLionel Sambuc 	14	R	TUN	Transmit FIFO Underrun
738433d6423SLionel Sambuc 	13	R/W	OWN	OWN
739433d6423SLionel Sambuc 	12-0	R/W	SIZE	Descriptor Size
740433d6423SLionel Sambuc 3e-3f	R/W	ISR		Interrupt Status Register
741433d6423SLionel Sambuc 	6	R/W	FOVW	Fx FIFO Overflow Interrupt
742433d6423SLionel Sambuc 	5	R/W	PUN/LinkChg Packet Underrun / Link Change Interrupt
743433d6423SLionel Sambuc 	3	R/W	TER	Transmit Error Interrupt
744433d6423SLionel Sambuc 	2	R/W	TOK	Transmit OK Interrupt
745433d6423SLionel Sambuc 3e-3f	R/W	ISR		Interrupt Status Register
746433d6423SLionel Sambuc 	15	R/W	SERR	System Error Interrupt
747433d6423SLionel Sambuc 	14	R/W	TimeOut	Time Out Interrupt
748433d6423SLionel Sambuc 	13	R/W	LenChg	Cable Length Change Interrupt
749433d6423SLionel Sambuc 3e-3f	R/W	ISR		Interrupt Status Register
750433d6423SLionel Sambuc 	4	R/W	RXOVW	Rx Buffer Overflow Interrupt
751433d6423SLionel Sambuc 	1	R/W	RER	Receive Error Interrupt
752433d6423SLionel Sambuc 	0	R/W	ROK	Receive OK Interrupt
753433d6423SLionel Sambuc 4c-4f	R/W	MPC		Missed Packet Counter
754433d6423SLionel Sambuc 60-61	R	TSAD		Transmit Status of All Descriptors
755433d6423SLionel Sambuc 	15-12	R	TOK[3-0] TOK bit of Descriptor [3-0]
756433d6423SLionel Sambuc 	11-8	R	TUN[3-0] TUN bit of Descriptor [3-0]
757433d6423SLionel Sambuc 	7-4	R	TABT[3-0] TABT bit of Descriptor [3-0]
758433d6423SLionel Sambuc 	3-0     R       OWN[3-0] OWN bit of Descriptor [3-0]
759433d6423SLionel Sambuc 6c-6d	R	DIS		Disconnect Counter
760433d6423SLionel Sambuc 	15-0	R	DCNT	Disconnect Counter
761433d6423SLionel Sambuc 6e-6f	R	FCSC		False Carrier Sense Counter
762433d6423SLionel Sambuc 	15-0	R	FCSCNT	False Carrier event counter
763433d6423SLionel Sambuc 72-73	R	REC		RX_ER Counter
764433d6423SLionel Sambuc 	15-0	R	RXERCNT	Received packet counter
765433d6423SLionel Sambuc #endif
766433d6423SLionel Sambuc 
7674081bff6SDavid van Moolenbroek 	if (!rep->re_got_int)
7684081bff6SDavid van Moolenbroek 		return;
7694081bff6SDavid van Moolenbroek 	rep->re_got_int = FALSE;
770433d6423SLionel Sambuc 
7714081bff6SDavid van Moolenbroek 	netdriver_recv();
772433d6423SLionel Sambuc 
773433d6423SLionel Sambuc 	if (rep->re_clear_rx)
774433d6423SLionel Sambuc 		rl_clear_rx(rep);
775433d6423SLionel Sambuc 
776433d6423SLionel Sambuc 	if (rep->re_need_reset)
777433d6423SLionel Sambuc 		rl_do_reset(rep);
778433d6423SLionel Sambuc 
7794081bff6SDavid van Moolenbroek 	if (rep->re_send_int) {
7804081bff6SDavid van Moolenbroek 		rep->re_send_int = FALSE;
7814081bff6SDavid van Moolenbroek 
7824081bff6SDavid van Moolenbroek 		netdriver_send();
783433d6423SLionel Sambuc 	}
784433d6423SLionel Sambuc 
7854081bff6SDavid van Moolenbroek 	if (rep->re_report_link) {
7864081bff6SDavid van Moolenbroek 		rep->re_report_link = FALSE;
787433d6423SLionel Sambuc 
788*f7df02e7SDavid van Moolenbroek 		netdriver_link();
789*f7df02e7SDavid van Moolenbroek #if VERBOSE
7904081bff6SDavid van Moolenbroek 		rl_report_link(rep);
791*f7df02e7SDavid van Moolenbroek #endif
7924081bff6SDavid van Moolenbroek 	}
793433d6423SLionel Sambuc }
794433d6423SLionel Sambuc 
795433d6423SLionel Sambuc /*===========================================================================*
796*f7df02e7SDavid van Moolenbroek  *				rl_get_link				     *
797*f7df02e7SDavid van Moolenbroek  *===========================================================================*/
rl_get_link(uint32_t * media)798*f7df02e7SDavid van Moolenbroek static unsigned int rl_get_link(uint32_t *media)
799*f7df02e7SDavid van Moolenbroek {
800*f7df02e7SDavid van Moolenbroek 	port_t port;
801*f7df02e7SDavid van Moolenbroek 	u8_t msr;
802*f7df02e7SDavid van Moolenbroek 	u16_t mii_ctrl;
803*f7df02e7SDavid van Moolenbroek 	re_t *rep;
804*f7df02e7SDavid van Moolenbroek 
805*f7df02e7SDavid van Moolenbroek 	rep = &re_state;
806*f7df02e7SDavid van Moolenbroek 
807*f7df02e7SDavid van Moolenbroek 	port= rep->re_base_port;
808*f7df02e7SDavid van Moolenbroek 	msr= rl_inb(port, RL_MSR);
809*f7df02e7SDavid van Moolenbroek 
810*f7df02e7SDavid van Moolenbroek 	if (msr & RL_MSR_LINKB)
811*f7df02e7SDavid van Moolenbroek 		return NDEV_LINK_DOWN;
812*f7df02e7SDavid van Moolenbroek 
813*f7df02e7SDavid van Moolenbroek 	if (msr & RL_MSR_SPEED_10)
814*f7df02e7SDavid van Moolenbroek 		*media = IFM_ETHER | IFM_10_T;
815*f7df02e7SDavid van Moolenbroek 	else
816*f7df02e7SDavid van Moolenbroek 		*media = IFM_ETHER | IFM_100_TX;
817*f7df02e7SDavid van Moolenbroek 
818*f7df02e7SDavid van Moolenbroek 	mii_ctrl= rl_inw(port, RL_BMCR);
819*f7df02e7SDavid van Moolenbroek 	if (mii_ctrl & MII_CTRL_DM)
820*f7df02e7SDavid van Moolenbroek 		*media |= IFM_FDX;
821*f7df02e7SDavid van Moolenbroek 	else
822*f7df02e7SDavid van Moolenbroek 		*media |= IFM_HDX;
823*f7df02e7SDavid van Moolenbroek 
824*f7df02e7SDavid van Moolenbroek 	return NDEV_LINK_UP;
825*f7df02e7SDavid van Moolenbroek }
826*f7df02e7SDavid van Moolenbroek 
827*f7df02e7SDavid van Moolenbroek #if VERBOSE
828*f7df02e7SDavid van Moolenbroek /*===========================================================================*
829433d6423SLionel Sambuc  *				rl_report_link				     *
830433d6423SLionel Sambuc  *===========================================================================*/
rl_report_link(re_t * rep)8314081bff6SDavid van Moolenbroek static void rl_report_link(re_t *rep)
832433d6423SLionel Sambuc {
833433d6423SLionel Sambuc 	port_t port;
834433d6423SLionel Sambuc 	u16_t mii_ctrl, mii_status, mii_ana, mii_anlpa, mii_ane, mii_extstat;
835433d6423SLionel Sambuc 	u8_t msr;
836433d6423SLionel Sambuc 	int f, link_up;
837433d6423SLionel Sambuc 
838433d6423SLionel Sambuc 	port= rep->re_base_port;
839433d6423SLionel Sambuc 	msr= rl_inb(port, RL_MSR);
840433d6423SLionel Sambuc 	link_up= !(msr & RL_MSR_LINKB);
841433d6423SLionel Sambuc 	rep->re_link_up= link_up;
842433d6423SLionel Sambuc 	if (!link_up)
843433d6423SLionel Sambuc 	{
844*f7df02e7SDavid van Moolenbroek 		printf("%s: link down\n", netdriver_name());
845433d6423SLionel Sambuc 		return;
846433d6423SLionel Sambuc 	}
847433d6423SLionel Sambuc 
848433d6423SLionel Sambuc 	mii_ctrl= rl_inw(port, RL_BMCR);
849433d6423SLionel Sambuc 	mii_status= rl_inw(port, RL_BMSR);
850433d6423SLionel Sambuc 	mii_ana= rl_inw(port, RL_ANAR);
851433d6423SLionel Sambuc 	mii_anlpa= rl_inw(port, RL_ANLPAR);
852433d6423SLionel Sambuc 	mii_ane= rl_inw(port, RL_ANER);
853433d6423SLionel Sambuc 	mii_extstat= 0;
854433d6423SLionel Sambuc 
855433d6423SLionel Sambuc 	if (mii_ctrl & (MII_CTRL_LB|MII_CTRL_PD|MII_CTRL_ISO))
856433d6423SLionel Sambuc 	{
857*f7df02e7SDavid van Moolenbroek 		printf("%s: PHY: ", netdriver_name());
858433d6423SLionel Sambuc 		f= 1;
859433d6423SLionel Sambuc 		if (mii_ctrl & MII_CTRL_LB)
860433d6423SLionel Sambuc 		{
861433d6423SLionel Sambuc 			printf("loopback mode");
862433d6423SLionel Sambuc 			f= 0;
863433d6423SLionel Sambuc 		}
864433d6423SLionel Sambuc 		if (mii_ctrl & MII_CTRL_PD)
865433d6423SLionel Sambuc 		{
866433d6423SLionel Sambuc 			if (!f) printf(", ");
867433d6423SLionel Sambuc 			f= 0;
868433d6423SLionel Sambuc 			printf("powered down");
869433d6423SLionel Sambuc 		}
870433d6423SLionel Sambuc 		if (mii_ctrl & MII_CTRL_ISO)
871433d6423SLionel Sambuc 		{
872433d6423SLionel Sambuc 			if (!f) printf(", ");
873433d6423SLionel Sambuc 			f= 0;
874433d6423SLionel Sambuc 			printf("isolated");
875433d6423SLionel Sambuc 		}
876433d6423SLionel Sambuc 		printf("\n");
877433d6423SLionel Sambuc 		return;
878433d6423SLionel Sambuc 	}
879433d6423SLionel Sambuc 	if (!(mii_ctrl & MII_CTRL_ANE))
880433d6423SLionel Sambuc 	{
881*f7df02e7SDavid van Moolenbroek 		printf("%s: manual config: ", netdriver_name());
882433d6423SLionel Sambuc 		switch(mii_ctrl & (MII_CTRL_SP_LSB|MII_CTRL_SP_MSB))
883433d6423SLionel Sambuc 		{
884433d6423SLionel Sambuc 		case MII_CTRL_SP_10:	printf("10 Mbps"); break;
885433d6423SLionel Sambuc 		case MII_CTRL_SP_100:	printf("100 Mbps"); break;
886433d6423SLionel Sambuc 		case MII_CTRL_SP_1000:	printf("1000 Mbps"); break;
887433d6423SLionel Sambuc 		case MII_CTRL_SP_RES:	printf("reserved speed"); break;
888433d6423SLionel Sambuc 		}
889433d6423SLionel Sambuc 		if (mii_ctrl & MII_CTRL_DM)
890433d6423SLionel Sambuc 			printf(", full duplex");
891433d6423SLionel Sambuc 		else
892433d6423SLionel Sambuc 			printf(", half duplex");
893433d6423SLionel Sambuc 		printf("\n");
894433d6423SLionel Sambuc 		return;
895433d6423SLionel Sambuc 	}
896433d6423SLionel Sambuc 
8974081bff6SDavid van Moolenbroek #if VERBOSE
898*f7df02e7SDavid van Moolenbroek 	printf("%s: ", netdriver_name());
899433d6423SLionel Sambuc 	mii_print_stat_speed(mii_status, mii_extstat);
900433d6423SLionel Sambuc 	printf("\n");
901433d6423SLionel Sambuc 
902433d6423SLionel Sambuc 	if (!(mii_status & MII_STATUS_ANC))
903*f7df02e7SDavid van Moolenbroek 		printf("%s: auto-negotiation not complete\n",
904*f7df02e7SDavid van Moolenbroek 		    netdriver_name());
905433d6423SLionel Sambuc 	if (mii_status & MII_STATUS_RF)
906*f7df02e7SDavid van Moolenbroek 		printf("%s: remote fault detected\n", netdriver_name());
907433d6423SLionel Sambuc 	if (!(mii_status & MII_STATUS_ANA))
908433d6423SLionel Sambuc 	{
909433d6423SLionel Sambuc 		printf("%s: local PHY has no auto-negotiation ability\n",
910*f7df02e7SDavid van Moolenbroek 			netdriver_name());
911433d6423SLionel Sambuc 	}
912433d6423SLionel Sambuc 	if (!(mii_status & MII_STATUS_LS))
913*f7df02e7SDavid van Moolenbroek 		printf("%s: link down\n", netdriver_name());
914433d6423SLionel Sambuc 	if (mii_status & MII_STATUS_JD)
915*f7df02e7SDavid van Moolenbroek 		printf("%s: jabber condition detected\n",
916*f7df02e7SDavid van Moolenbroek 		    netdriver_name());
917433d6423SLionel Sambuc 	if (!(mii_status & MII_STATUS_EC))
918433d6423SLionel Sambuc 	{
919*f7df02e7SDavid van Moolenbroek 		printf("%s: no extended register set\n", netdriver_name());
920433d6423SLionel Sambuc 		goto resspeed;
921433d6423SLionel Sambuc 	}
922433d6423SLionel Sambuc 	if (!(mii_status & MII_STATUS_ANC))
923433d6423SLionel Sambuc 		goto resspeed;
924433d6423SLionel Sambuc 
925*f7df02e7SDavid van Moolenbroek 	printf("%s: local cap.: ", netdriver_name());
926433d6423SLionel Sambuc 	mii_print_techab(mii_ana);
927433d6423SLionel Sambuc 	printf("\n");
928433d6423SLionel Sambuc 
929433d6423SLionel Sambuc 	if (mii_ane & MII_ANE_PDF)
930*f7df02e7SDavid van Moolenbroek 		printf("%s: parallel detection fault\n", netdriver_name());
931433d6423SLionel Sambuc 	if (!(mii_ane & MII_ANE_LPANA))
932433d6423SLionel Sambuc 	{
933433d6423SLionel Sambuc 		printf("%s: link-partner does not support auto-negotiation\n",
934*f7df02e7SDavid van Moolenbroek 			netdriver_name());
935433d6423SLionel Sambuc 		goto resspeed;
936433d6423SLionel Sambuc 	}
937433d6423SLionel Sambuc 
938*f7df02e7SDavid van Moolenbroek 	printf("%s: remote cap.: ", netdriver_name());
939433d6423SLionel Sambuc 	mii_print_techab(mii_anlpa);
940433d6423SLionel Sambuc 	printf("\n");
941433d6423SLionel Sambuc resspeed:
9424081bff6SDavid van Moolenbroek #endif
9434081bff6SDavid van Moolenbroek 
944*f7df02e7SDavid van Moolenbroek 	printf("%s: ", netdriver_name());
945433d6423SLionel Sambuc 	printf("link up at %d Mbps, ", (msr & RL_MSR_SPEED_10) ? 10 : 100);
946433d6423SLionel Sambuc 	printf("%s duplex\n", ((mii_ctrl & MII_CTRL_DM) ? "full" : "half"));
947433d6423SLionel Sambuc 
948433d6423SLionel Sambuc }
949433d6423SLionel Sambuc 
mii_print_techab(u16_t techab)950433d6423SLionel Sambuc static void mii_print_techab(u16_t techab)
951433d6423SLionel Sambuc {
952433d6423SLionel Sambuc 	int fs, ft;
953433d6423SLionel Sambuc 	if ((techab & MII_ANA_SEL_M) != MII_ANA_SEL_802_3)
954433d6423SLionel Sambuc 	{
955433d6423SLionel Sambuc 		printf("strange selector 0x%x, value 0x%x",
956433d6423SLionel Sambuc 			techab & MII_ANA_SEL_M,
957433d6423SLionel Sambuc 			(techab & MII_ANA_TAF_M) >> MII_ANA_TAF_S);
958433d6423SLionel Sambuc 		return;
959433d6423SLionel Sambuc 	}
960433d6423SLionel Sambuc 	fs= 1;
961433d6423SLionel Sambuc 	if (techab & (MII_ANA_100T4 | MII_ANA_100TXFD | MII_ANA_100TXHD))
962433d6423SLionel Sambuc 	{
963433d6423SLionel Sambuc 		printf("100 Mbps: ");
964433d6423SLionel Sambuc 		fs= 0;
965433d6423SLionel Sambuc 		ft= 1;
966433d6423SLionel Sambuc 		if (techab & MII_ANA_100T4)
967433d6423SLionel Sambuc 		{
968433d6423SLionel Sambuc 			printf("T4");
969433d6423SLionel Sambuc 			ft= 0;
970433d6423SLionel Sambuc 		}
971433d6423SLionel Sambuc 		if (techab & (MII_ANA_100TXFD | MII_ANA_100TXHD))
972433d6423SLionel Sambuc 		{
973433d6423SLionel Sambuc 			if (!ft)
974433d6423SLionel Sambuc 				printf(", ");
975433d6423SLionel Sambuc 			ft= 0;
976433d6423SLionel Sambuc 			printf("TX-");
977433d6423SLionel Sambuc 			switch(techab & (MII_ANA_100TXFD|MII_ANA_100TXHD))
978433d6423SLionel Sambuc 			{
979433d6423SLionel Sambuc 			case MII_ANA_100TXFD:	printf("FD"); break;
980433d6423SLionel Sambuc 			case MII_ANA_100TXHD:	printf("HD"); break;
981433d6423SLionel Sambuc 			default:		printf("FD/HD"); break;
982433d6423SLionel Sambuc 			}
983433d6423SLionel Sambuc 		}
984433d6423SLionel Sambuc 	}
985433d6423SLionel Sambuc 	if (techab & (MII_ANA_10TFD | MII_ANA_10THD))
986433d6423SLionel Sambuc 	{
987433d6423SLionel Sambuc 		if (!fs)
988433d6423SLionel Sambuc 			printf(", ");
989433d6423SLionel Sambuc 		printf("10 Mbps: ");
990433d6423SLionel Sambuc 		fs= 0;
991433d6423SLionel Sambuc 		printf("T-");
992433d6423SLionel Sambuc 		switch(techab & (MII_ANA_10TFD|MII_ANA_10THD))
993433d6423SLionel Sambuc 		{
994433d6423SLionel Sambuc 		case MII_ANA_10TFD:	printf("FD"); break;
995433d6423SLionel Sambuc 		case MII_ANA_10THD:	printf("HD"); break;
996433d6423SLionel Sambuc 		default:		printf("FD/HD"); break;
997433d6423SLionel Sambuc 		}
998433d6423SLionel Sambuc 	}
999433d6423SLionel Sambuc 	if (techab & MII_ANA_PAUSE_SYM)
1000433d6423SLionel Sambuc 	{
1001433d6423SLionel Sambuc 		if (!fs)
1002433d6423SLionel Sambuc 			printf(", ");
1003433d6423SLionel Sambuc 		fs= 0;
1004433d6423SLionel Sambuc 		printf("pause(SYM)");
1005433d6423SLionel Sambuc 	}
1006433d6423SLionel Sambuc 	if (techab & MII_ANA_PAUSE_ASYM)
1007433d6423SLionel Sambuc 	{
1008433d6423SLionel Sambuc 		if (!fs)
1009433d6423SLionel Sambuc 			printf(", ");
1010433d6423SLionel Sambuc 		fs= 0;
1011433d6423SLionel Sambuc 		printf("pause(ASYM)");
1012433d6423SLionel Sambuc 	}
1013433d6423SLionel Sambuc 	if (techab & MII_ANA_TAF_RES)
1014433d6423SLionel Sambuc 	{
1015433d6423SLionel Sambuc 		if (!fs)
1016433d6423SLionel Sambuc 			printf(", ");
1017433d6423SLionel Sambuc 		fs= 0;
1018433d6423SLionel Sambuc 		printf("0x%x", (techab & MII_ANA_TAF_RES) >> MII_ANA_TAF_S);
1019433d6423SLionel Sambuc 	}
1020433d6423SLionel Sambuc }
1021433d6423SLionel Sambuc 
mii_print_stat_speed(u16_t stat,u16_t extstat)1022433d6423SLionel Sambuc static void mii_print_stat_speed(u16_t stat, u16_t extstat)
1023433d6423SLionel Sambuc {
1024433d6423SLionel Sambuc 	int fs, ft;
1025433d6423SLionel Sambuc 	fs= 1;
1026433d6423SLionel Sambuc 	if (stat & MII_STATUS_EXT_STAT)
1027433d6423SLionel Sambuc 	{
1028433d6423SLionel Sambuc 		if (extstat & (MII_ESTAT_1000XFD | MII_ESTAT_1000XHD |
1029433d6423SLionel Sambuc 			MII_ESTAT_1000TFD | MII_ESTAT_1000THD))
1030433d6423SLionel Sambuc 		{
1031433d6423SLionel Sambuc 			printf("1000 Mbps: ");
1032433d6423SLionel Sambuc 			fs= 0;
1033433d6423SLionel Sambuc 			ft= 1;
1034433d6423SLionel Sambuc 			if (extstat & (MII_ESTAT_1000XFD | MII_ESTAT_1000XHD))
1035433d6423SLionel Sambuc 			{
1036433d6423SLionel Sambuc 				ft= 0;
1037433d6423SLionel Sambuc 				printf("X-");
1038433d6423SLionel Sambuc 				switch(extstat &
1039433d6423SLionel Sambuc 					(MII_ESTAT_1000XFD|MII_ESTAT_1000XHD))
1040433d6423SLionel Sambuc 				{
1041433d6423SLionel Sambuc 				case MII_ESTAT_1000XFD:	printf("FD"); break;
1042433d6423SLionel Sambuc 				case MII_ESTAT_1000XHD:	printf("HD"); break;
1043433d6423SLionel Sambuc 				default:		printf("FD/HD"); break;
1044433d6423SLionel Sambuc 				}
1045433d6423SLionel Sambuc 			}
1046433d6423SLionel Sambuc 			if (extstat & (MII_ESTAT_1000TFD | MII_ESTAT_1000THD))
1047433d6423SLionel Sambuc 			{
1048433d6423SLionel Sambuc 				if (!ft)
1049433d6423SLionel Sambuc 					printf(", ");
1050433d6423SLionel Sambuc 				ft= 0;
1051433d6423SLionel Sambuc 				printf("T-");
1052433d6423SLionel Sambuc 				switch(extstat &
1053433d6423SLionel Sambuc 					(MII_ESTAT_1000TFD|MII_ESTAT_1000THD))
1054433d6423SLionel Sambuc 				{
1055433d6423SLionel Sambuc 				case MII_ESTAT_1000TFD:	printf("FD"); break;
1056433d6423SLionel Sambuc 				case MII_ESTAT_1000THD:	printf("HD"); break;
1057433d6423SLionel Sambuc 				default:		printf("FD/HD"); break;
1058433d6423SLionel Sambuc 				}
1059433d6423SLionel Sambuc 			}
1060433d6423SLionel Sambuc 		}
1061433d6423SLionel Sambuc 	}
1062433d6423SLionel Sambuc 	if (stat & (MII_STATUS_100T4 |
1063433d6423SLionel Sambuc 		MII_STATUS_100XFD | MII_STATUS_100XHD |
1064433d6423SLionel Sambuc 		MII_STATUS_100T2FD | MII_STATUS_100T2HD))
1065433d6423SLionel Sambuc 	{
1066433d6423SLionel Sambuc 		if (!fs)
1067433d6423SLionel Sambuc 			printf(", ");
1068433d6423SLionel Sambuc 		fs= 0;
1069433d6423SLionel Sambuc 		printf("100 Mbps: ");
1070433d6423SLionel Sambuc 		ft= 1;
1071433d6423SLionel Sambuc 		if (stat & MII_STATUS_100T4)
1072433d6423SLionel Sambuc 		{
1073433d6423SLionel Sambuc 			printf("T4");
1074433d6423SLionel Sambuc 			ft= 0;
1075433d6423SLionel Sambuc 		}
1076433d6423SLionel Sambuc 		if (stat & (MII_STATUS_100XFD | MII_STATUS_100XHD))
1077433d6423SLionel Sambuc 		{
1078433d6423SLionel Sambuc 			if (!ft)
1079433d6423SLionel Sambuc 				printf(", ");
1080433d6423SLionel Sambuc 			ft= 0;
1081433d6423SLionel Sambuc 			printf("TX-");
1082433d6423SLionel Sambuc 			switch(stat & (MII_STATUS_100XFD|MII_STATUS_100XHD))
1083433d6423SLionel Sambuc 			{
1084433d6423SLionel Sambuc 			case MII_STATUS_100XFD:	printf("FD"); break;
1085433d6423SLionel Sambuc 			case MII_STATUS_100XHD:	printf("HD"); break;
1086433d6423SLionel Sambuc 			default:		printf("FD/HD"); break;
1087433d6423SLionel Sambuc 			}
1088433d6423SLionel Sambuc 		}
1089433d6423SLionel Sambuc 		if (stat & (MII_STATUS_100T2FD | MII_STATUS_100T2HD))
1090433d6423SLionel Sambuc 		{
1091433d6423SLionel Sambuc 			if (!ft)
1092433d6423SLionel Sambuc 				printf(", ");
1093433d6423SLionel Sambuc 			ft= 0;
1094433d6423SLionel Sambuc 			printf("T2-");
1095433d6423SLionel Sambuc 			switch(stat & (MII_STATUS_100T2FD|MII_STATUS_100T2HD))
1096433d6423SLionel Sambuc 			{
1097433d6423SLionel Sambuc 			case MII_STATUS_100T2FD:	printf("FD"); break;
1098433d6423SLionel Sambuc 			case MII_STATUS_100T2HD:	printf("HD"); break;
1099433d6423SLionel Sambuc 			default:		printf("FD/HD"); break;
1100433d6423SLionel Sambuc 			}
1101433d6423SLionel Sambuc 		}
1102433d6423SLionel Sambuc 	}
1103433d6423SLionel Sambuc 	if (stat & (MII_STATUS_10FD | MII_STATUS_10HD))
1104433d6423SLionel Sambuc 	{
1105433d6423SLionel Sambuc 		if (!fs)
1106433d6423SLionel Sambuc 			printf(", ");
1107433d6423SLionel Sambuc 		printf("10 Mbps: ");
1108433d6423SLionel Sambuc 		fs= 0;
1109433d6423SLionel Sambuc 		printf("T-");
1110433d6423SLionel Sambuc 		switch(stat & (MII_STATUS_10FD|MII_STATUS_10HD))
1111433d6423SLionel Sambuc 		{
1112433d6423SLionel Sambuc 		case MII_STATUS_10FD:	printf("FD"); break;
1113433d6423SLionel Sambuc 		case MII_STATUS_10HD:	printf("HD"); break;
1114433d6423SLionel Sambuc 		default:		printf("FD/HD"); break;
1115433d6423SLionel Sambuc 		}
1116433d6423SLionel Sambuc 	}
1117433d6423SLionel Sambuc }
11184081bff6SDavid van Moolenbroek #endif /* VERBOSE */
1119433d6423SLionel Sambuc 
1120433d6423SLionel Sambuc /*===========================================================================*
1121433d6423SLionel Sambuc  *				rl_clear_rx				     *
1122433d6423SLionel Sambuc  *===========================================================================*/
rl_clear_rx(re_t * rep)1123433d6423SLionel Sambuc static void rl_clear_rx(re_t *rep)
1124433d6423SLionel Sambuc {
1125433d6423SLionel Sambuc 	port_t port;
1126433d6423SLionel Sambuc 	u8_t cr;
1127433d6423SLionel Sambuc 
1128433d6423SLionel Sambuc 	rep->re_clear_rx= FALSE;
1129433d6423SLionel Sambuc 	port= rep->re_base_port;
1130433d6423SLionel Sambuc 
1131433d6423SLionel Sambuc 	/* Reset the receiver */
1132433d6423SLionel Sambuc 	cr= rl_inb(port, RL_CR);
1133433d6423SLionel Sambuc 	cr &= ~RL_CR_RE;
1134433d6423SLionel Sambuc 	rl_outb(port, RL_CR, cr);
1135433d6423SLionel Sambuc 	SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_RE), 1000000);
1136433d6423SLionel Sambuc 	if (rl_inb(port, RL_CR) & RL_CR_RE)
1137433d6423SLionel Sambuc 		panic("cannot disable receiver");
1138433d6423SLionel Sambuc 
1139433d6423SLionel Sambuc #if 0
1140433d6423SLionel Sambuc 	printf("RBSTART = 0x%08x\n", rl_inl(port, RL_RBSTART));
1141433d6423SLionel Sambuc 	printf("CAPR = 0x%04x\n", rl_inw(port, RL_CAPR));
1142433d6423SLionel Sambuc 	printf("CBR = 0x%04x\n", rl_inw(port, RL_CBR));
1143433d6423SLionel Sambuc 	printf("RCR = 0x%08x\n", rl_inl(port, RL_RCR));
1144433d6423SLionel Sambuc #endif
1145433d6423SLionel Sambuc 
1146433d6423SLionel Sambuc 	rl_outb(port, RL_CR, cr | RL_CR_RE);
1147433d6423SLionel Sambuc 
1148433d6423SLionel Sambuc 	rl_outl(port, RL_RCR, RX_BUFBITS);
1149433d6423SLionel Sambuc 
1150433d6423SLionel Sambuc 	rl_rec_mode(rep);
1151433d6423SLionel Sambuc 
1152*f7df02e7SDavid van Moolenbroek 	netdriver_stat_ierror(1);
1153433d6423SLionel Sambuc }
1154433d6423SLionel Sambuc 
1155433d6423SLionel Sambuc /*===========================================================================*
1156433d6423SLionel Sambuc  *				rl_do_reset				     *
1157433d6423SLionel Sambuc  *===========================================================================*/
rl_do_reset(re_t * rep)11584081bff6SDavid van Moolenbroek static void rl_do_reset(re_t *rep)
1159433d6423SLionel Sambuc {
1160433d6423SLionel Sambuc 	rep->re_need_reset= FALSE;
1161433d6423SLionel Sambuc 	rl_reset_hw(rep);
1162433d6423SLionel Sambuc 	rl_rec_mode(rep);
1163433d6423SLionel Sambuc 
1164433d6423SLionel Sambuc 	rep->re_tx_head= 0;
11654081bff6SDavid van Moolenbroek 	if (rep->re_tx[rep->re_tx_head].ret_busy)
11664081bff6SDavid van Moolenbroek 		rep->re_tx_busy--;
1167433d6423SLionel Sambuc 	rep->re_tx[rep->re_tx_head].ret_busy= FALSE;
1168433d6423SLionel Sambuc 	rep->re_send_int= TRUE;
1169433d6423SLionel Sambuc }
1170433d6423SLionel Sambuc 
1171433d6423SLionel Sambuc #if 0
1172433d6423SLionel Sambuc /*===========================================================================*
1173433d6423SLionel Sambuc  *				dump_phy				     *
1174433d6423SLionel Sambuc  *===========================================================================*/
11754081bff6SDavid van Moolenbroek static void dump_phy(re_t *rep)
1176433d6423SLionel Sambuc {
1177433d6423SLionel Sambuc 	port_t port;
1178433d6423SLionel Sambuc 	u32_t t;
1179433d6423SLionel Sambuc 
1180433d6423SLionel Sambuc 	port= rep->re_base_port;
1181433d6423SLionel Sambuc 
1182433d6423SLionel Sambuc 	t= rl_inb(port, RL_MSR);
1183433d6423SLionel Sambuc 	printf("MSR: 0x%02lx\n", t);
1184433d6423SLionel Sambuc 	if (t & RL_MSR_SPEED_10)
1185433d6423SLionel Sambuc 		printf("\t10 Mbps\n");
1186433d6423SLionel Sambuc 	if (t & RL_MSR_LINKB)
1187433d6423SLionel Sambuc 		printf("\tLink failed\n");
1188433d6423SLionel Sambuc 
1189433d6423SLionel Sambuc 	t= rl_inb(port, RL_CONFIG1);
1190433d6423SLionel Sambuc 	printf("CONFIG1: 0x%02lx\n", t);
1191433d6423SLionel Sambuc 
1192433d6423SLionel Sambuc 	t= rl_inb(port, RL_CONFIG3);
1193433d6423SLionel Sambuc 	printf("CONFIG3: 0x%02lx\n", t);
1194433d6423SLionel Sambuc 
1195433d6423SLionel Sambuc 	t= rl_inb(port, RL_CONFIG4);
1196433d6423SLionel Sambuc 	printf("CONFIG4: 0x%02lx\n", t);
1197433d6423SLionel Sambuc 
1198433d6423SLionel Sambuc 	t= rl_inw(port, RL_BMCR);
1199433d6423SLionel Sambuc 	printf("BMCR (MII_CTRL): 0x%04lx\n", t);
1200433d6423SLionel Sambuc 
1201433d6423SLionel Sambuc 	t= rl_inw(port, RL_BMSR);
1202433d6423SLionel Sambuc 	printf("BMSR:");
1203433d6423SLionel Sambuc 	if (t & MII_STATUS_100T4)
1204433d6423SLionel Sambuc 		printf(" 100Base-T4");
1205433d6423SLionel Sambuc 	if (t & MII_STATUS_100XFD)
1206433d6423SLionel Sambuc 		printf(" 100Base-X-FD");
1207433d6423SLionel Sambuc 	if (t & MII_STATUS_100XHD)
1208433d6423SLionel Sambuc 		printf(" 100Base-X-HD");
1209433d6423SLionel Sambuc 	if (t & MII_STATUS_10FD)
1210433d6423SLionel Sambuc 		printf(" 10Mbps-FD");
1211433d6423SLionel Sambuc 	if (t & MII_STATUS_10HD)
1212433d6423SLionel Sambuc 		printf(" 10Mbps-HD");
1213433d6423SLionel Sambuc 	if (t & MII_STATUS_100T2FD)
1214433d6423SLionel Sambuc 		printf(" 100Base-T2-FD");
1215433d6423SLionel Sambuc 	if (t & MII_STATUS_100T2HD)
1216433d6423SLionel Sambuc 		printf(" 100Base-T2-HD");
1217433d6423SLionel Sambuc 	if (t & MII_STATUS_EXT_STAT)
1218433d6423SLionel Sambuc 		printf(" Ext-stat");
1219433d6423SLionel Sambuc 	if (t & MII_STATUS_RES)
1220433d6423SLionel Sambuc 		printf(" res-0x%lx", t & MII_STATUS_RES);
1221433d6423SLionel Sambuc 	if (t & MII_STATUS_MFPS)
1222433d6423SLionel Sambuc 		printf(" MFPS");
1223433d6423SLionel Sambuc 	if (t & MII_STATUS_ANC)
1224433d6423SLionel Sambuc 		printf(" ANC");
1225433d6423SLionel Sambuc 	if (t & MII_STATUS_RF)
1226433d6423SLionel Sambuc 		printf(" remote-fault");
1227433d6423SLionel Sambuc 	if (t & MII_STATUS_ANA)
1228433d6423SLionel Sambuc 		printf(" ANA");
1229433d6423SLionel Sambuc 	if (t & MII_STATUS_LS)
1230433d6423SLionel Sambuc 		printf(" Link");
1231433d6423SLionel Sambuc 	if (t & MII_STATUS_JD)
1232433d6423SLionel Sambuc 		printf(" Jabber");
1233433d6423SLionel Sambuc 	if (t & MII_STATUS_EC)
1234433d6423SLionel Sambuc 		printf(" Extended-capability");
1235433d6423SLionel Sambuc 	printf("\n");
1236433d6423SLionel Sambuc 
1237433d6423SLionel Sambuc 	t= rl_inw(port, RL_ANAR);
1238433d6423SLionel Sambuc 	printf("ANAR (MII_ANA): 0x%04lx\n", t);
1239433d6423SLionel Sambuc 
1240433d6423SLionel Sambuc 	t= rl_inw(port, RL_ANLPAR);
1241433d6423SLionel Sambuc 	printf("ANLPAR: 0x%04lx\n", t);
1242433d6423SLionel Sambuc 
1243433d6423SLionel Sambuc 	t= rl_inw(port, RL_ANER);
1244433d6423SLionel Sambuc 	printf("ANER (MII_ANE): ");
1245433d6423SLionel Sambuc 	if (t & MII_ANE_RES)
1246433d6423SLionel Sambuc 		printf(" res-0x%lx", t & MII_ANE_RES);
1247433d6423SLionel Sambuc 	if (t & MII_ANE_PDF)
1248433d6423SLionel Sambuc 		printf(" Par-Detect-Fault");
1249433d6423SLionel Sambuc 	if (t & MII_ANE_LPNPA)
1250433d6423SLionel Sambuc 		printf(" LP-Next-Page-Able");
1251433d6423SLionel Sambuc 	if (t & MII_ANE_NPA)
1252433d6423SLionel Sambuc 		printf(" Loc-Next-Page-Able");
1253433d6423SLionel Sambuc 	if (t & MII_ANE_PR)
1254433d6423SLionel Sambuc 		printf(" Page-Received");
1255433d6423SLionel Sambuc 	if (t & MII_ANE_LPANA)
1256433d6423SLionel Sambuc 		printf(" LP-Auto-Neg-Able");
1257433d6423SLionel Sambuc 	printf("\n");
1258433d6423SLionel Sambuc 
1259433d6423SLionel Sambuc 	t= rl_inw(port, RL_NWAYTR);
1260433d6423SLionel Sambuc 	printf("NWAYTR: 0x%04lx\n", t);
1261433d6423SLionel Sambuc 	t= rl_inw(port, RL_CSCR);
1262433d6423SLionel Sambuc 	printf("CSCR: 0x%04lx\n", t);
1263433d6423SLionel Sambuc 
1264433d6423SLionel Sambuc 	t= rl_inb(port, RL_CONFIG5);
1265433d6423SLionel Sambuc 	printf("CONFIG5: 0x%02lx\n", t);
1266433d6423SLionel Sambuc }
1267433d6423SLionel Sambuc #endif
1268433d6423SLionel Sambuc 
1269433d6423SLionel Sambuc /*===========================================================================*
1270433d6423SLionel Sambuc  *				rl_handler				     *
1271433d6423SLionel Sambuc  *===========================================================================*/
rl_handler(re_t * rep)1272433d6423SLionel Sambuc static int rl_handler(re_t *rep)
1273433d6423SLionel Sambuc {
1274433d6423SLionel Sambuc 	int i, port, tx_head, tx_tail, link_up;
1275433d6423SLionel Sambuc 	u16_t isr, tsad;
1276433d6423SLionel Sambuc 	u32_t tsd, tcr, ertxth;
1277433d6423SLionel Sambuc 
1278433d6423SLionel Sambuc 	port= rep->re_base_port;
1279433d6423SLionel Sambuc 
1280433d6423SLionel Sambuc 	/* Ack interrupt */
1281433d6423SLionel Sambuc 	isr= rl_inw(port, RL_ISR);
1282433d6423SLionel Sambuc 	rl_outw(port, RL_ISR, isr);
1283433d6423SLionel Sambuc 
1284433d6423SLionel Sambuc 	if (isr & RL_IMR_FOVW)
1285433d6423SLionel Sambuc 	{
1286433d6423SLionel Sambuc 		isr &= ~RL_IMR_FOVW;
1287433d6423SLionel Sambuc 		/* Should do anything? */
1288433d6423SLionel Sambuc 	}
1289433d6423SLionel Sambuc 	if (isr & RL_IMR_PUN)
1290433d6423SLionel Sambuc 	{
1291433d6423SLionel Sambuc 		isr &= ~RL_IMR_PUN;
1292433d6423SLionel Sambuc 
1293433d6423SLionel Sambuc 		/* Either the link status changed or there was a TX fifo
1294433d6423SLionel Sambuc 		 * underrun.
1295433d6423SLionel Sambuc 		 */
1296433d6423SLionel Sambuc 		link_up= !(rl_inb(port, RL_MSR) & RL_MSR_LINKB);
1297433d6423SLionel Sambuc 		if (link_up != rep->re_link_up)
1298433d6423SLionel Sambuc 		{
1299433d6423SLionel Sambuc 			rep->re_report_link= TRUE;
1300433d6423SLionel Sambuc 			rep->re_got_int= TRUE;
1301433d6423SLionel Sambuc 		}
1302433d6423SLionel Sambuc 	}
1303433d6423SLionel Sambuc 	if (isr & RL_IMR_RXOVW)
1304433d6423SLionel Sambuc 	{
1305433d6423SLionel Sambuc 		isr &= ~RL_IMR_RXOVW;
1306433d6423SLionel Sambuc 
1307433d6423SLionel Sambuc 		/* Clear the receive buffer */
1308433d6423SLionel Sambuc 		rep->re_clear_rx= TRUE;
1309433d6423SLionel Sambuc 		rep->re_got_int= TRUE;
1310433d6423SLionel Sambuc 	}
1311433d6423SLionel Sambuc 
1312433d6423SLionel Sambuc 	if (isr & (RL_ISR_RER | RL_ISR_ROK))
1313433d6423SLionel Sambuc 	{
1314433d6423SLionel Sambuc 		isr &= ~(RL_ISR_RER | RL_ISR_ROK);
1315433d6423SLionel Sambuc 
1316433d6423SLionel Sambuc 		rep->re_got_int= TRUE;
1317433d6423SLionel Sambuc 	}
1318433d6423SLionel Sambuc 	if ((isr & (RL_ISR_TER | RL_ISR_TOK)) || 1)
1319433d6423SLionel Sambuc 	{
1320433d6423SLionel Sambuc 		isr &= ~(RL_ISR_TER | RL_ISR_TOK);
1321433d6423SLionel Sambuc 
1322433d6423SLionel Sambuc 		tsad= rl_inw(port, RL_TSAD);
1323433d6423SLionel Sambuc 		if (tsad & (RL_TSAD_TABT0|RL_TSAD_TABT1|
1324433d6423SLionel Sambuc 			RL_TSAD_TABT2|RL_TSAD_TABT3))
1325433d6423SLionel Sambuc 		{
1326433d6423SLionel Sambuc 			printf("rl_handler, TABT, tasd = 0x%04x\n",
1327433d6423SLionel Sambuc 				tsad);
1328433d6423SLionel Sambuc 
1329433d6423SLionel Sambuc 			/* Find the aborted transmit request */
1330433d6423SLionel Sambuc 			for (i= 0; i< N_TX_BUF; i++)
1331433d6423SLionel Sambuc 			{
1332433d6423SLionel Sambuc 				tsd= rl_inl(port, RL_TSD0+i*4);
1333433d6423SLionel Sambuc 				if (tsd & RL_TSD_TABT)
1334433d6423SLionel Sambuc 					break;
1335433d6423SLionel Sambuc 			}
1336433d6423SLionel Sambuc 			if (i >= N_TX_BUF)
1337433d6423SLionel Sambuc 			{
1338433d6423SLionel Sambuc 				printf(
1339433d6423SLionel Sambuc 				"rl_handler: can't find aborted TX req.\n");
1340433d6423SLionel Sambuc 			}
1341433d6423SLionel Sambuc 			else
1342433d6423SLionel Sambuc 			{
1343433d6423SLionel Sambuc 				printf("TSD%d = 0x%04x\n", i, tsd);
1344433d6423SLionel Sambuc 
1345433d6423SLionel Sambuc 				/* Set head and tail to this buffer */
1346433d6423SLionel Sambuc 				rep->re_tx_head= rep->re_tx_tail= i;
1347433d6423SLionel Sambuc 			}
1348433d6423SLionel Sambuc 
1349433d6423SLionel Sambuc 			/* Aborted transmission, just kick the device
1350433d6423SLionel Sambuc 			 * and be done with it.
1351433d6423SLionel Sambuc 			 */
1352*f7df02e7SDavid van Moolenbroek 			netdriver_stat_oerror(1);
1353*f7df02e7SDavid van Moolenbroek 
1354433d6423SLionel Sambuc 			tcr= rl_inl(port, RL_TCR);
1355433d6423SLionel Sambuc 			rl_outl(port, RL_TCR, tcr | RL_TCR_CLRABT);
1356433d6423SLionel Sambuc 		}
1357433d6423SLionel Sambuc 
1358433d6423SLionel Sambuc 		/* Transmit completed */
1359433d6423SLionel Sambuc 		tx_head= rep->re_tx_head;
1360433d6423SLionel Sambuc 		tx_tail= rep->re_tx_tail;
1361433d6423SLionel Sambuc 		for (i= 0; i< 2*N_TX_BUF; i++)
1362433d6423SLionel Sambuc 		{
1363*f7df02e7SDavid van Moolenbroek 			if (rep->re_tx_busy == 0)
1364*f7df02e7SDavid van Moolenbroek 				break;
1365433d6423SLionel Sambuc 			if (!rep->re_tx[tx_tail].ret_busy)
1366433d6423SLionel Sambuc 			{
1367433d6423SLionel Sambuc 				/* Strange, this buffer is not in-use.
1368433d6423SLionel Sambuc 				 * Increment tx_tail until tx_head is
1369433d6423SLionel Sambuc 				 * reached (or until we find a buffer that
1370433d6423SLionel Sambuc 				 * is in-use.
1371433d6423SLionel Sambuc 				 */
1372433d6423SLionel Sambuc 				if (tx_tail == tx_head)
1373433d6423SLionel Sambuc 					break;
1374433d6423SLionel Sambuc 				if (++tx_tail >= N_TX_BUF)
1375433d6423SLionel Sambuc 					tx_tail= 0;
1376433d6423SLionel Sambuc 				assert(tx_tail < RL_N_TX);
1377433d6423SLionel Sambuc 				rep->re_tx_tail= tx_tail;
1378433d6423SLionel Sambuc 				continue;
1379433d6423SLionel Sambuc 			}
1380433d6423SLionel Sambuc 			tsd= rl_inl(port, RL_TSD0+tx_tail*4);
1381*f7df02e7SDavid van Moolenbroek 			if (!(tsd & (RL_TSD_TABT | RL_TSD_TOK | RL_TSD_TUN)))
1382433d6423SLionel Sambuc 			{
1383433d6423SLionel Sambuc 				/* Buffer is not yet ready */
1384433d6423SLionel Sambuc 				break;
1385433d6423SLionel Sambuc 			}
1386433d6423SLionel Sambuc 
1387433d6423SLionel Sambuc 			/* Should collect statistics */
1388433d6423SLionel Sambuc 			if (tsd & RL_TSD_TABT)
1389433d6423SLionel Sambuc 			{
1390433d6423SLionel Sambuc 				printf("rl_handler, TABT, TSD%d = 0x%04x\n",
1391433d6423SLionel Sambuc 					tx_tail, tsd);
1392*f7df02e7SDavid van Moolenbroek 				panic("TX abort"); /* CLRABT is not all that
1393*f7df02e7SDavid van Moolenbroek 						    * that effective, why not?
1394433d6423SLionel Sambuc 						    */
1395433d6423SLionel Sambuc 				tcr= rl_inl(port, RL_TCR);
1396433d6423SLionel Sambuc 				rl_outl(port, RL_TCR, tcr | RL_TCR_CLRABT);
1397433d6423SLionel Sambuc 			}
1398433d6423SLionel Sambuc 
1399433d6423SLionel Sambuc 			/* What about collisions? */
1400*f7df02e7SDavid van Moolenbroek 			if (!(tsd & RL_TSD_TOK))
1401*f7df02e7SDavid van Moolenbroek 				netdriver_stat_oerror(1);
1402433d6423SLionel Sambuc 			if (tsd & RL_TSD_TUN)
1403433d6423SLionel Sambuc 			{
1404433d6423SLionel Sambuc 				/* Increase ERTXTH */
1405433d6423SLionel Sambuc 				ertxth= tsd + (1 << RL_TSD_ERTXTH_S);
1406433d6423SLionel Sambuc 				ertxth &= RL_TSD_ERTXTH_M;
14074081bff6SDavid van Moolenbroek #if VERBOSE
14084081bff6SDavid van Moolenbroek 				if (ertxth > rep->re_ertxth)
1409433d6423SLionel Sambuc 				{
1410433d6423SLionel Sambuc 					printf("%s: new ertxth: %d bytes\n",
1411*f7df02e7SDavid van Moolenbroek 						netdriver_name(),
1412433d6423SLionel Sambuc 						(ertxth >> RL_TSD_ERTXTH_S) *
1413433d6423SLionel Sambuc 						32);
1414433d6423SLionel Sambuc 					rep->re_ertxth= ertxth;
1415433d6423SLionel Sambuc 				}
14164081bff6SDavid van Moolenbroek #endif
1417433d6423SLionel Sambuc 			}
1418433d6423SLionel Sambuc 			rep->re_tx[tx_tail].ret_busy= FALSE;
14194081bff6SDavid van Moolenbroek 			rep->re_tx_busy--;
1420433d6423SLionel Sambuc 
1421433d6423SLionel Sambuc #if 0
1422433d6423SLionel Sambuc 			printf("TSD%d: %08lx\n", tx_tail, tsd);
1423433d6423SLionel Sambuc 			printf(
1424433d6423SLionel Sambuc 			"rl_handler: head %d, tail %d, busy: %d %d %d %d\n",
1425433d6423SLionel Sambuc 				tx_head, tx_tail,
1426433d6423SLionel Sambuc 				rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy,
1427433d6423SLionel Sambuc 				rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy);
1428433d6423SLionel Sambuc #endif
1429433d6423SLionel Sambuc 
1430433d6423SLionel Sambuc 			if (++tx_tail >= N_TX_BUF)
1431433d6423SLionel Sambuc 				tx_tail= 0;
1432433d6423SLionel Sambuc 			assert(tx_tail < RL_N_TX);
1433433d6423SLionel Sambuc 			rep->re_tx_tail= tx_tail;
1434433d6423SLionel Sambuc 
1435433d6423SLionel Sambuc 			rep->re_send_int= TRUE;
1436433d6423SLionel Sambuc 			rep->re_got_int= TRUE;
14374081bff6SDavid van Moolenbroek 			rep->re_tx_alive= TRUE;
1438433d6423SLionel Sambuc 		}
1439433d6423SLionel Sambuc 		assert(i < 2*N_TX_BUF);
1440433d6423SLionel Sambuc 	}
1441433d6423SLionel Sambuc 	if (isr)
1442433d6423SLionel Sambuc 	{
1443433d6423SLionel Sambuc 		printf("rl_handler: unhandled interrupt: isr = 0x%04x\n",
1444433d6423SLionel Sambuc 			isr);
1445433d6423SLionel Sambuc 	}
1446433d6423SLionel Sambuc 
1447433d6423SLionel Sambuc 	return 1;
1448433d6423SLionel Sambuc }
1449433d6423SLionel Sambuc 
1450433d6423SLionel Sambuc /*===========================================================================*
1451*f7df02e7SDavid van Moolenbroek  *				rl_tick					     *
1452433d6423SLionel Sambuc  *===========================================================================*/
rl_tick(void)1453*f7df02e7SDavid van Moolenbroek static void rl_tick(void)
1454433d6423SLionel Sambuc {
1455433d6423SLionel Sambuc 	re_t *rep;
14564081bff6SDavid van Moolenbroek 
1457433d6423SLionel Sambuc 	rep= &re_state;
1458433d6423SLionel Sambuc 
14594081bff6SDavid van Moolenbroek 	assert(rep->re_tx_busy >= 0 && rep->re_tx_busy <= N_TX_BUF);
14604081bff6SDavid van Moolenbroek 	if (rep->re_tx_busy == 0)
1461433d6423SLionel Sambuc 	{
1462433d6423SLionel Sambuc 		/* Assume that an idle system is alive */
1463433d6423SLionel Sambuc 		rep->re_tx_alive= TRUE;
1464433d6423SLionel Sambuc 		return;
1465433d6423SLionel Sambuc 	}
1466433d6423SLionel Sambuc 	if (rep->re_tx_alive)
1467433d6423SLionel Sambuc 	{
1468433d6423SLionel Sambuc 		rep->re_tx_alive= FALSE;
1469433d6423SLionel Sambuc 		return;
1470433d6423SLionel Sambuc 	}
1471*f7df02e7SDavid van Moolenbroek 	printf("%s: TX timeout, resetting\n", netdriver_name());
1472433d6423SLionel Sambuc 	printf("TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
1473433d6423SLionel Sambuc 		rl_inw(rep->re_base_port, RL_TSAD),
1474433d6423SLionel Sambuc 		rl_inl(rep->re_base_port, RL_TSD0+0*4),
1475433d6423SLionel Sambuc 		rl_inl(rep->re_base_port, RL_TSD0+1*4),
1476433d6423SLionel Sambuc 		rl_inl(rep->re_base_port, RL_TSD0+2*4),
1477433d6423SLionel Sambuc 		rl_inl(rep->re_base_port, RL_TSD0+3*4));
1478433d6423SLionel Sambuc 	printf("tx_head %d, tx_tail %d, busy: %d %d %d %d\n",
1479433d6423SLionel Sambuc 		rep->re_tx_head, rep->re_tx_tail,
1480433d6423SLionel Sambuc 		rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy,
1481433d6423SLionel Sambuc 		rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy);
1482433d6423SLionel Sambuc 	rep->re_need_reset= TRUE;
1483433d6423SLionel Sambuc 	rep->re_got_int= TRUE;
1484433d6423SLionel Sambuc 
14854081bff6SDavid van Moolenbroek 	rl_check_ints(rep);
1486433d6423SLionel Sambuc }
1487433d6423SLionel Sambuc 
14884081bff6SDavid van Moolenbroek /* TODO: obviously this needs a lot of work. */
tell_iommu(vir_bytes buf,size_t size,int pci_bus,int pci_dev,int pci_func)14894081bff6SDavid van Moolenbroek static void tell_iommu(vir_bytes buf, size_t size, int pci_bus, int pci_dev,
14904081bff6SDavid van Moolenbroek 	int pci_func)
1491433d6423SLionel Sambuc {
1492433d6423SLionel Sambuc 	int r;
1493433d6423SLionel Sambuc 	endpoint_t dev_e;
1494433d6423SLionel Sambuc 	message m;
1495433d6423SLionel Sambuc 
1496433d6423SLionel Sambuc 	r= ds_retrieve_label_endpt("amddev", &dev_e);
1497433d6423SLionel Sambuc 	if (r != OK)
1498433d6423SLionel Sambuc 	{
1499433d6423SLionel Sambuc #if 0
15004081bff6SDavid van Moolenbroek 		printf("rtl8139`tell_dev: ds_retrieve_label_endpt failed "
15014081bff6SDavid van Moolenbroek 		    "for 'amddev': %d\n", r);
1502433d6423SLionel Sambuc #endif
1503433d6423SLionel Sambuc 		return;
1504433d6423SLionel Sambuc 	}
1505433d6423SLionel Sambuc 
1506433d6423SLionel Sambuc 	m.m_type= IOMMU_MAP;
1507433d6423SLionel Sambuc 	m.m2_i1= pci_bus;
1508433d6423SLionel Sambuc 	m.m2_i2= pci_dev;
1509433d6423SLionel Sambuc 	m.m2_i3= pci_func;
1510433d6423SLionel Sambuc 	m.m2_l1= buf;
1511433d6423SLionel Sambuc 	m.m2_l2= size;
1512433d6423SLionel Sambuc 
1513433d6423SLionel Sambuc 	r= ipc_sendrec(dev_e, &m);
1514433d6423SLionel Sambuc 	if (r != OK)
1515433d6423SLionel Sambuc 	{
1516433d6423SLionel Sambuc 		printf("rtl8139`tell_dev: ipc_sendrec to %d failed: %d\n",
1517433d6423SLionel Sambuc 			dev_e, r);
1518433d6423SLionel Sambuc 		return;
1519433d6423SLionel Sambuc 	}
1520433d6423SLionel Sambuc 	if (m.m_type != OK)
1521433d6423SLionel Sambuc 	{
1522433d6423SLionel Sambuc 		printf("rtl8139`tell_dev: dma map request failed: %d\n",
1523433d6423SLionel Sambuc 			m.m_type);
1524433d6423SLionel Sambuc 		return;
1525433d6423SLionel Sambuc 	}
1526433d6423SLionel Sambuc }
1527433d6423SLionel Sambuc 
1528433d6423SLionel Sambuc /*
1529433d6423SLionel Sambuc  * $PchId: rtl8139.c,v 1.3 2003/09/11 14:15:15 philip Exp $
1530433d6423SLionel Sambuc  */
1531