1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc * lance.c
3433d6423SLionel Sambuc *
4433d6423SLionel Sambuc * This file contains a ethernet device driver for AMD LANCE based ethernet
5433d6423SLionel Sambuc * cards.
6433d6423SLionel Sambuc *
7433d6423SLionel Sambuc * Created: Jul 27, 2002 by Kazuya Kodama <kazuya@nii.ac.jp>
8433d6423SLionel Sambuc * Adapted for Minix 3: Sep 05, 2005 by Joren l'Ami <jwlami@cs.vu.nl>
9433d6423SLionel Sambuc */
10433d6423SLionel Sambuc
11433d6423SLionel Sambuc #define VERBOSE 0 /* Verbose debugging output */
12433d6423SLionel Sambuc #define LANCE_FKEY 0 /* Use function key to dump Lance stats */
13433d6423SLionel Sambuc
14433d6423SLionel Sambuc #include <minix/drivers.h>
15433d6423SLionel Sambuc #include <minix/netdriver.h>
16433d6423SLionel Sambuc
17433d6423SLionel Sambuc #include <assert.h>
18433d6423SLionel Sambuc
19433d6423SLionel Sambuc #include <minix/syslib.h>
20433d6423SLionel Sambuc #include <minix/endpoint.h>
21433d6423SLionel Sambuc #include <machine/pci.h>
22433d6423SLionel Sambuc #include <minix/ds.h>
23433d6423SLionel Sambuc
24433d6423SLionel Sambuc #include "lance.h"
25433d6423SLionel Sambuc
26f7df02e7SDavid van Moolenbroek static int do_init(unsigned int instance, netdriver_addr_t *addr,
27f7df02e7SDavid van Moolenbroek uint32_t *caps, unsigned int *ticks);
28f7df02e7SDavid van Moolenbroek static void ec_confaddr(netdriver_addr_t *addr, unsigned int instance);
29433d6423SLionel Sambuc static void ec_reinit(ether_card_t *ec);
306315775fSDavid van Moolenbroek static void do_intr(unsigned int mask);
31f7df02e7SDavid van Moolenbroek static void do_set_mode(unsigned int mode, const netdriver_addr_t *mcast_list,
32f7df02e7SDavid van Moolenbroek unsigned int mcast_count);
336315775fSDavid van Moolenbroek static int do_send(struct netdriver_data *data, size_t size);
346315775fSDavid van Moolenbroek static ssize_t do_recv(struct netdriver_data *data, size_t max);
356315775fSDavid van Moolenbroek static void do_stop(void);
36433d6423SLionel Sambuc static void lance_dump(void);
376315775fSDavid van Moolenbroek static void do_other(const message *m_ptr, int ipc_status);
386315775fSDavid van Moolenbroek static void get_addressing(int devind, ether_card_t *ec);
396315775fSDavid van Moolenbroek static int lance_probe(ether_card_t *ec, unsigned int skip);
40f7df02e7SDavid van Moolenbroek static void lance_init_hw(ether_card_t *ec, netdriver_addr_t *addr,
41f7df02e7SDavid van Moolenbroek unsigned int instance);
42433d6423SLionel Sambuc
43433d6423SLionel Sambuc /* Accesses Lance Control and Status Registers */
44433d6423SLionel Sambuc static u8_t in_byte(port_t port);
45433d6423SLionel Sambuc static u16_t in_word(port_t port);
46433d6423SLionel Sambuc static void out_word(port_t port, u16_t value);
47433d6423SLionel Sambuc static u16_t read_csr(port_t ioaddr, u16_t csrno);
48433d6423SLionel Sambuc static void write_csr(port_t ioaddr, u16_t csrno, u16_t value);
49433d6423SLionel Sambuc
506315775fSDavid van Moolenbroek static ether_card_t ec_state;
516315775fSDavid van Moolenbroek
52433d6423SLionel Sambuc /* --- LANCE --- */
53433d6423SLionel Sambuc /* General */
546315775fSDavid van Moolenbroek typedef uint32_t Address;
55433d6423SLionel Sambuc
56433d6423SLionel Sambuc #define ETH_FRAME_LEN 1518
57433d6423SLionel Sambuc
58433d6423SLionel Sambuc #define LANCE_MUST_PAD 0x00000001
59433d6423SLionel Sambuc #define LANCE_ENABLE_AUTOSELECT 0x00000002
60433d6423SLionel Sambuc #define LANCE_SELECT_PHONELINE 0x00000004
61433d6423SLionel Sambuc #define LANCE_MUST_UNRESET 0x00000008
62433d6423SLionel Sambuc
63433d6423SLionel Sambuc static const struct lance_chip_type
64433d6423SLionel Sambuc {
65433d6423SLionel Sambuc int id_number;
66433d6423SLionel Sambuc const char *name;
67433d6423SLionel Sambuc int flags;
68433d6423SLionel Sambuc } chip_table[] = {
69433d6423SLionel Sambuc {0x0000, "LANCE 7990", /* Ancient lance chip. */
70433d6423SLionel Sambuc LANCE_MUST_PAD + LANCE_MUST_UNRESET},
71433d6423SLionel Sambuc {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */
72433d6423SLionel Sambuc LANCE_ENABLE_AUTOSELECT},
73433d6423SLionel Sambuc {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */
74433d6423SLionel Sambuc LANCE_ENABLE_AUTOSELECT},
75433d6423SLionel Sambuc {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */
76433d6423SLionel Sambuc LANCE_ENABLE_AUTOSELECT},
77433d6423SLionel Sambuc {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */
78433d6423SLionel Sambuc LANCE_ENABLE_AUTOSELECT},
79433d6423SLionel Sambuc {0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */
80433d6423SLionel Sambuc LANCE_ENABLE_AUTOSELECT},
81433d6423SLionel Sambuc {0x2625, "PCnet-FAST III 79C973",/* 79C973 PCInet-FAST III. */
82433d6423SLionel Sambuc LANCE_ENABLE_AUTOSELECT},
83433d6423SLionel Sambuc {0x2626, "PCnet/HomePNA 79C978",
84433d6423SLionel Sambuc LANCE_ENABLE_AUTOSELECT|LANCE_SELECT_PHONELINE},
85433d6423SLionel Sambuc {0x0, "PCnet (unknown)",
86433d6423SLionel Sambuc LANCE_ENABLE_AUTOSELECT},
87433d6423SLionel Sambuc };
88433d6423SLionel Sambuc
89433d6423SLionel Sambuc /* ############## for LANCE device ############## */
90433d6423SLionel Sambuc #define LANCE_ETH_ADDR 0x0
91433d6423SLionel Sambuc #define LANCE_DATA 0x10
92433d6423SLionel Sambuc #define LANCE_ADDR 0x12
93433d6423SLionel Sambuc #define LANCE_RESET 0x14
94433d6423SLionel Sambuc #define LANCE_BUS_IF 0x16
95433d6423SLionel Sambuc #define LANCE_TOTAL_SIZE 0x18
96433d6423SLionel Sambuc
97433d6423SLionel Sambuc /* Use 2^4=16 {Rx,Tx} buffers */
98433d6423SLionel Sambuc #define LANCE_LOG_RX_BUFFERS 4
99433d6423SLionel Sambuc #define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
100433d6423SLionel Sambuc #define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
101433d6423SLionel Sambuc #define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
102433d6423SLionel Sambuc
103433d6423SLionel Sambuc #define LANCE_LOG_TX_BUFFERS 4
104433d6423SLionel Sambuc #define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
105433d6423SLionel Sambuc #define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
106433d6423SLionel Sambuc #define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
107433d6423SLionel Sambuc
108433d6423SLionel Sambuc /* for lance_interface */
109433d6423SLionel Sambuc struct lance_init_block
110433d6423SLionel Sambuc {
111433d6423SLionel Sambuc unsigned short mode;
112433d6423SLionel Sambuc unsigned char phys_addr[6];
113433d6423SLionel Sambuc unsigned long filter[2];
114433d6423SLionel Sambuc Address rx_ring;
115433d6423SLionel Sambuc Address tx_ring;
116433d6423SLionel Sambuc };
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc struct lance_rx_head
119433d6423SLionel Sambuc {
120433d6423SLionel Sambuc union {
121433d6423SLionel Sambuc Address base;
122433d6423SLionel Sambuc unsigned char addr[4];
123433d6423SLionel Sambuc } u;
124433d6423SLionel Sambuc short buf_length; /* 2s complement */
125433d6423SLionel Sambuc short msg_length;
126433d6423SLionel Sambuc };
127433d6423SLionel Sambuc
128433d6423SLionel Sambuc struct lance_tx_head
129433d6423SLionel Sambuc {
130433d6423SLionel Sambuc union {
131433d6423SLionel Sambuc Address base;
132433d6423SLionel Sambuc unsigned char addr[4];
133433d6423SLionel Sambuc } u;
134433d6423SLionel Sambuc short buf_length; /* 2s complement */
135433d6423SLionel Sambuc short misc;
136433d6423SLionel Sambuc };
137433d6423SLionel Sambuc
138433d6423SLionel Sambuc struct lance_interface
139433d6423SLionel Sambuc {
140433d6423SLionel Sambuc struct lance_init_block init_block;
141433d6423SLionel Sambuc struct lance_rx_head rx_ring[RX_RING_SIZE];
142433d6423SLionel Sambuc struct lance_tx_head tx_ring[TX_RING_SIZE];
143433d6423SLionel Sambuc unsigned char rbuf[RX_RING_SIZE][ETH_FRAME_LEN];
144433d6423SLionel Sambuc unsigned char tbuf[TX_RING_SIZE][ETH_FRAME_LEN];
145433d6423SLionel Sambuc };
146433d6423SLionel Sambuc
147433d6423SLionel Sambuc /* =============== global variables =============== */
148433d6423SLionel Sambuc /* AKA the stuff that really should have been in ether_card_t */
149433d6423SLionel Sambuc static struct lance_interface *lp;
150433d6423SLionel Sambuc #define LANCE_BUF_SIZE (sizeof(struct lance_interface))
151433d6423SLionel Sambuc static char *lance_buf = NULL;
152433d6423SLionel Sambuc static int rx_slot_nr = 0; /* Rx-slot number */
153433d6423SLionel Sambuc static int tx_slot_nr = 0; /* Tx-slot number */
154433d6423SLionel Sambuc static int cur_tx_slot_nr = 0; /* Tx-slot number */
1556315775fSDavid van Moolenbroek static phys_bytes tx_ring_base[TX_RING_SIZE]; /* Tx-slot physical address */
156433d6423SLionel Sambuc static char isstored[TX_RING_SIZE]; /* Tx-slot in-use */
157433d6423SLionel Sambuc
1586315775fSDavid van Moolenbroek static const struct netdriver lance_table = {
159f7df02e7SDavid van Moolenbroek .ndr_name = "le",
1606315775fSDavid van Moolenbroek .ndr_init = do_init,
1616315775fSDavid van Moolenbroek .ndr_stop = do_stop,
162f7df02e7SDavid van Moolenbroek .ndr_set_mode = do_set_mode,
1636315775fSDavid van Moolenbroek .ndr_recv = do_recv,
1646315775fSDavid van Moolenbroek .ndr_send = do_send,
1656315775fSDavid van Moolenbroek .ndr_intr = do_intr,
1666315775fSDavid van Moolenbroek .ndr_other = do_other,
1676315775fSDavid van Moolenbroek };
168433d6423SLionel Sambuc
169433d6423SLionel Sambuc /*===========================================================================*
170433d6423SLionel Sambuc * main *
171433d6423SLionel Sambuc *===========================================================================*/
main(int argc,char ** argv)172433d6423SLionel Sambuc int main(int argc, char **argv)
173433d6423SLionel Sambuc {
174433d6423SLionel Sambuc
175433d6423SLionel Sambuc env_setargs(argc, argv);
176433d6423SLionel Sambuc
1776315775fSDavid van Moolenbroek netdriver_task(&lance_table);
178433d6423SLionel Sambuc
179433d6423SLionel Sambuc return 0;
180433d6423SLionel Sambuc }
181433d6423SLionel Sambuc
182433d6423SLionel Sambuc /*===========================================================================*
183433d6423SLionel Sambuc * lance_dump *
184433d6423SLionel Sambuc *===========================================================================*/
lance_dump()185433d6423SLionel Sambuc static void lance_dump()
186433d6423SLionel Sambuc {
187433d6423SLionel Sambuc ether_card_t *ec;
188433d6423SLionel Sambuc int isr, csr;
189433d6423SLionel Sambuc unsigned short ioaddr;
190433d6423SLionel Sambuc
191433d6423SLionel Sambuc printf("\n");
192433d6423SLionel Sambuc ec = &ec_state;
193433d6423SLionel Sambuc
194f7df02e7SDavid van Moolenbroek printf("lance driver %s:\n", netdriver_name());
195433d6423SLionel Sambuc
196433d6423SLionel Sambuc ioaddr = ec->ec_port;
197433d6423SLionel Sambuc isr = read_csr(ioaddr, LANCE_CSR0);
198f7df02e7SDavid van Moolenbroek printf("isr = 0x%x, mode = 0x%x\n", isr, ec->ec_mode);
199433d6423SLionel Sambuc
200433d6423SLionel Sambuc printf("irq = %d\tioadr = 0x%x\n", ec->ec_irq, ec->ec_port);
201433d6423SLionel Sambuc
202433d6423SLionel Sambuc csr = read_csr(ioaddr, LANCE_CSR0);
203433d6423SLionel Sambuc printf("CSR0: 0x%x\n", csr);
204433d6423SLionel Sambuc csr = read_csr(ioaddr, LANCE_CSR3);
205433d6423SLionel Sambuc printf("CSR3: 0x%x\n", csr);
206433d6423SLionel Sambuc csr = read_csr(ioaddr, LANCE_CSR4);
207433d6423SLionel Sambuc printf("CSR4: 0x%x\n", csr);
208433d6423SLionel Sambuc csr = read_csr(ioaddr, LANCE_CSR5);
209433d6423SLionel Sambuc printf("CSR5: 0x%x\n", csr);
210433d6423SLionel Sambuc csr = read_csr(ioaddr, LANCE_CSR15);
211433d6423SLionel Sambuc printf("CSR15: 0x%x\n", csr);
212433d6423SLionel Sambuc }
213433d6423SLionel Sambuc
214433d6423SLionel Sambuc /*===========================================================================*
2156315775fSDavid van Moolenbroek * do_other *
216433d6423SLionel Sambuc *===========================================================================*/
do_other(const message * m_ptr,int ipc_status)2176315775fSDavid van Moolenbroek static void do_other(const message *m_ptr, int ipc_status)
218433d6423SLionel Sambuc {
219433d6423SLionel Sambuc
2206315775fSDavid van Moolenbroek if (is_ipc_notify(ipc_status) && m_ptr->m_source == TTY_PROC_NR)
2216315775fSDavid van Moolenbroek lance_dump();
222433d6423SLionel Sambuc }
223433d6423SLionel Sambuc
224433d6423SLionel Sambuc /*===========================================================================*
225433d6423SLionel Sambuc * ec_confaddr *
226433d6423SLionel Sambuc *===========================================================================*/
ec_confaddr(netdriver_addr_t * addr,unsigned int instance)227f7df02e7SDavid van Moolenbroek static void ec_confaddr(netdriver_addr_t *addr, unsigned int instance)
228433d6423SLionel Sambuc {
229433d6423SLionel Sambuc int i;
230433d6423SLionel Sambuc char eakey[16];
231433d6423SLionel Sambuc static char eafmt[]= "x:x:x:x:x:x";
232433d6423SLionel Sambuc long v;
233433d6423SLionel Sambuc
234433d6423SLionel Sambuc /* User defined ethernet address? */
235433d6423SLionel Sambuc strlcpy(eakey, "LANCE0_EA", sizeof(eakey));
236f7df02e7SDavid van Moolenbroek eakey[5] += instance;
237433d6423SLionel Sambuc
238433d6423SLionel Sambuc for (i = 0; i < 6; i++)
239433d6423SLionel Sambuc {
240f7df02e7SDavid van Moolenbroek v= addr->na_addr[i];
241433d6423SLionel Sambuc if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET)
242433d6423SLionel Sambuc break;
243f7df02e7SDavid van Moolenbroek addr->na_addr[i]= v;
244433d6423SLionel Sambuc }
245433d6423SLionel Sambuc
246433d6423SLionel Sambuc if (i != 0 && i != 6)
247433d6423SLionel Sambuc {
248433d6423SLionel Sambuc /* It's all or nothing; force a panic. */
2496315775fSDavid van Moolenbroek panic("invalid ethernet address supplied");
250433d6423SLionel Sambuc }
251433d6423SLionel Sambuc }
252433d6423SLionel Sambuc
2536315775fSDavid van Moolenbroek /*===========================================================================*
2546315775fSDavid van Moolenbroek * do_init *
2556315775fSDavid van Moolenbroek *===========================================================================*/
do_init(unsigned int instance,netdriver_addr_t * addr,uint32_t * caps,unsigned int * ticks __unused)256f7df02e7SDavid van Moolenbroek static int do_init(unsigned int instance, netdriver_addr_t *addr,
257f7df02e7SDavid van Moolenbroek uint32_t *caps, unsigned int *ticks __unused)
2586315775fSDavid van Moolenbroek {
2596315775fSDavid van Moolenbroek /* Initialize the lance driver. */
2606315775fSDavid van Moolenbroek ether_card_t *ec;
2616315775fSDavid van Moolenbroek #if VERBOSE
262f7df02e7SDavid van Moolenbroek int i;
2636315775fSDavid van Moolenbroek #endif
2646315775fSDavid van Moolenbroek #if LANCE_FKEY
265f7df02e7SDavid van Moolenbroek int r, fkeys, sfkeys;
2666315775fSDavid van Moolenbroek #endif
2676315775fSDavid van Moolenbroek
2686315775fSDavid van Moolenbroek #if LANCE_FKEY
2696315775fSDavid van Moolenbroek fkeys = sfkeys = 0;
2706315775fSDavid van Moolenbroek bit_set( sfkeys, 7 );
2716315775fSDavid van Moolenbroek if ( (r = fkey_map(&fkeys, &sfkeys)) != OK )
2726315775fSDavid van Moolenbroek printf("Warning: lance couldn't observe Shift+F7 key: %d\n",r);
2736315775fSDavid van Moolenbroek #endif
2746315775fSDavid van Moolenbroek
2756315775fSDavid van Moolenbroek /* Initialize the driver state. */
2766315775fSDavid van Moolenbroek ec= &ec_state;
2776315775fSDavid van Moolenbroek memset(ec, 0, sizeof(*ec));
2786315775fSDavid van Moolenbroek
2796315775fSDavid van Moolenbroek /* See if there is a matching card. */
2806315775fSDavid van Moolenbroek if (!lance_probe(ec, instance))
2816315775fSDavid van Moolenbroek return ENXIO;
2826315775fSDavid van Moolenbroek
2836315775fSDavid van Moolenbroek /* Initialize the hardware. */
284f7df02e7SDavid van Moolenbroek lance_init_hw(ec, addr, instance);
2856315775fSDavid van Moolenbroek
2866315775fSDavid van Moolenbroek #if VERBOSE
287f7df02e7SDavid van Moolenbroek printf("%s: Ethernet address ", netdriver_name());
2886315775fSDavid van Moolenbroek for (i= 0; i < 6; i++)
289f7df02e7SDavid van Moolenbroek printf("%x%c", addr->na_addr[i], i < 5 ? ':' : '\n');
2906315775fSDavid van Moolenbroek #endif
2916315775fSDavid van Moolenbroek
292f7df02e7SDavid van Moolenbroek *caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST;
2936315775fSDavid van Moolenbroek return OK;
2946315775fSDavid van Moolenbroek }
295433d6423SLionel Sambuc
296433d6423SLionel Sambuc /*===========================================================================*
297433d6423SLionel Sambuc * ec_reinit *
298433d6423SLionel Sambuc *===========================================================================*/
ec_reinit(ether_card_t * ec)2996315775fSDavid van Moolenbroek static void ec_reinit(ether_card_t *ec)
300433d6423SLionel Sambuc {
301*47db417bSDavid van Moolenbroek spin_t spin;
302433d6423SLionel Sambuc int i;
303433d6423SLionel Sambuc unsigned short ioaddr = ec->ec_port;
304433d6423SLionel Sambuc
305433d6423SLionel Sambuc /* stop */
306433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
307*47db417bSDavid van Moolenbroek /* init */
308*47db417bSDavid van Moolenbroek write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_INIT);
309*47db417bSDavid van Moolenbroek /* poll for IDON */
310*47db417bSDavid van Moolenbroek SPIN_FOR(&spin, 1000) {
311*47db417bSDavid van Moolenbroek if (read_csr(ioaddr, LANCE_CSR0) & LANCE_CSR0_IDON)
312*47db417bSDavid van Moolenbroek break;
313433d6423SLionel Sambuc }
314433d6423SLionel Sambuc
315*47db417bSDavid van Moolenbroek /* Set 'Multicast Table' */
316*47db417bSDavid van Moolenbroek for (i=0;i<4;++i)
317433d6423SLionel Sambuc {
318*47db417bSDavid van Moolenbroek write_csr(ioaddr, LANCE_CSR8 + i, 0xffff);
319433d6423SLionel Sambuc }
320433d6423SLionel Sambuc
321433d6423SLionel Sambuc /* Set 'Receive Mode' */
322f7df02e7SDavid van Moolenbroek if (ec->ec_mode & NDEV_MODE_PROMISC)
323433d6423SLionel Sambuc {
324433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_PROM);
325433d6423SLionel Sambuc }
326433d6423SLionel Sambuc else
327433d6423SLionel Sambuc {
328f7df02e7SDavid van Moolenbroek if (ec->ec_mode &
329f7df02e7SDavid van Moolenbroek (NDEV_MODE_BCAST | NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
330433d6423SLionel Sambuc {
331433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR15, 0x0000);
332433d6423SLionel Sambuc }
333433d6423SLionel Sambuc else
334433d6423SLionel Sambuc {
335433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_DRCVBC);
336433d6423SLionel Sambuc }
337433d6423SLionel Sambuc }
338433d6423SLionel Sambuc
339*47db417bSDavid van Moolenbroek /* purge Tx-ring */
340*47db417bSDavid van Moolenbroek tx_slot_nr = cur_tx_slot_nr = 0;
341*47db417bSDavid van Moolenbroek for (i=0; i<TX_RING_SIZE; i++)
342*47db417bSDavid van Moolenbroek {
343*47db417bSDavid van Moolenbroek lp->tx_ring[i].u.base = 0;
344*47db417bSDavid van Moolenbroek isstored[i]=0;
345*47db417bSDavid van Moolenbroek }
346*47db417bSDavid van Moolenbroek cur_tx_slot_nr = tx_slot_nr;
347*47db417bSDavid van Moolenbroek
348*47db417bSDavid van Moolenbroek /* re-init Rx-ring */
349*47db417bSDavid van Moolenbroek rx_slot_nr = 0;
350*47db417bSDavid van Moolenbroek for (i=0; i<RX_RING_SIZE; i++)
351*47db417bSDavid van Moolenbroek {
352*47db417bSDavid van Moolenbroek lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
353*47db417bSDavid van Moolenbroek lp->rx_ring[i].u.addr[3] |= 0x80;
354*47db417bSDavid van Moolenbroek }
355*47db417bSDavid van Moolenbroek
356433d6423SLionel Sambuc /* start && enable interrupt */
357433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR0,
358433d6423SLionel Sambuc LANCE_CSR0_IDON|LANCE_CSR0_IENA|LANCE_CSR0_STRT);
359433d6423SLionel Sambuc
360433d6423SLionel Sambuc return;
361433d6423SLionel Sambuc }
362433d6423SLionel Sambuc
363433d6423SLionel Sambuc /*===========================================================================*
364f7df02e7SDavid van Moolenbroek * do_set_mode *
365433d6423SLionel Sambuc *===========================================================================*/
do_set_mode(unsigned int mode,const netdriver_addr_t * mcast_list __unused,unsigned int mcast_count __unused)366f7df02e7SDavid van Moolenbroek static void do_set_mode(unsigned int mode,
367f7df02e7SDavid van Moolenbroek const netdriver_addr_t *mcast_list __unused,
368f7df02e7SDavid van Moolenbroek unsigned int mcast_count __unused)
369433d6423SLionel Sambuc {
3706315775fSDavid van Moolenbroek ether_card_t *ec;
371433d6423SLionel Sambuc
3726315775fSDavid van Moolenbroek ec = &ec_state;
3736315775fSDavid van Moolenbroek
374f7df02e7SDavid van Moolenbroek ec->ec_mode = mode;
3756315775fSDavid van Moolenbroek
3766315775fSDavid van Moolenbroek ec_reinit(ec);
3776315775fSDavid van Moolenbroek }
3786315775fSDavid van Moolenbroek
3796315775fSDavid van Moolenbroek /*===========================================================================*
3806315775fSDavid van Moolenbroek * do_intr *
3816315775fSDavid van Moolenbroek *===========================================================================*/
do_intr(unsigned int __unused mask)3826315775fSDavid van Moolenbroek static void do_intr(unsigned int __unused mask)
3836315775fSDavid van Moolenbroek {
3846315775fSDavid van Moolenbroek ether_card_t *ec;
3856315775fSDavid van Moolenbroek int must_restart = 0;
3866315775fSDavid van Moolenbroek int r, check, status;
3876315775fSDavid van Moolenbroek int isr = 0x0000;
3886315775fSDavid van Moolenbroek unsigned short ioaddr;
3896315775fSDavid van Moolenbroek
3906315775fSDavid van Moolenbroek ec = &ec_state;
3916315775fSDavid van Moolenbroek ioaddr = ec->ec_port;
392433d6423SLionel Sambuc
393433d6423SLionel Sambuc for (;;)
394433d6423SLionel Sambuc {
395433d6423SLionel Sambuc #if VERBOSE
396433d6423SLionel Sambuc printf("ETH: Reading ISR...");
397433d6423SLionel Sambuc #endif
398433d6423SLionel Sambuc isr = read_csr(ioaddr, LANCE_CSR0);
399433d6423SLionel Sambuc if (isr & (LANCE_CSR0_ERR|LANCE_CSR0_RINT|LANCE_CSR0_TINT)) {
400433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR0,
401433d6423SLionel Sambuc isr & ~(LANCE_CSR0_IENA|LANCE_CSR0_TDMD|LANCE_CSR0_STOP
402433d6423SLionel Sambuc |LANCE_CSR0_STRT|LANCE_CSR0_INIT) );
403433d6423SLionel Sambuc }
404433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR0,
405433d6423SLionel Sambuc LANCE_CSR0_BABL|LANCE_CSR0_CERR|LANCE_CSR0_MISS|LANCE_CSR0_MERR
406433d6423SLionel Sambuc |LANCE_CSR0_IDON|LANCE_CSR0_IENA);
407433d6423SLionel Sambuc
408433d6423SLionel Sambuc if ((isr & (LANCE_CSR0_TINT|LANCE_CSR0_RINT|LANCE_CSR0_MISS
409433d6423SLionel Sambuc |LANCE_CSR0_BABL|LANCE_CSR0_ERR)) == 0x0000)
410433d6423SLionel Sambuc {
411433d6423SLionel Sambuc #if VERBOSE
412433d6423SLionel Sambuc printf("OK\n");
413433d6423SLionel Sambuc #endif
414433d6423SLionel Sambuc break;
415433d6423SLionel Sambuc }
416433d6423SLionel Sambuc
417433d6423SLionel Sambuc if (isr & LANCE_CSR0_MISS)
418433d6423SLionel Sambuc {
419433d6423SLionel Sambuc #if VERBOSE
420433d6423SLionel Sambuc printf("RX Missed Frame\n");
421433d6423SLionel Sambuc #endif
422f7df02e7SDavid van Moolenbroek netdriver_stat_ierror(1);
423433d6423SLionel Sambuc }
424433d6423SLionel Sambuc if ((isr & LANCE_CSR0_BABL) || (isr & LANCE_CSR0_TINT))
425433d6423SLionel Sambuc {
426433d6423SLionel Sambuc if (isr & LANCE_CSR0_BABL)
427433d6423SLionel Sambuc {
428433d6423SLionel Sambuc #if VERBOSE
429433d6423SLionel Sambuc printf("TX Timeout\n");
430433d6423SLionel Sambuc #endif
431f7df02e7SDavid van Moolenbroek netdriver_stat_oerror(1);
432433d6423SLionel Sambuc }
433433d6423SLionel Sambuc if (isr & LANCE_CSR0_TINT)
434433d6423SLionel Sambuc {
435433d6423SLionel Sambuc #if VERBOSE
436433d6423SLionel Sambuc printf("TX INT\n");
437433d6423SLionel Sambuc #endif
438433d6423SLionel Sambuc /* status check: restart if needed. */
439433d6423SLionel Sambuc status = lp->tx_ring[cur_tx_slot_nr].u.base;
440433d6423SLionel Sambuc
4416315775fSDavid van Moolenbroek /* did an error (UFLO, LCOL, LCAR, RTRY) occur? */
442433d6423SLionel Sambuc if (status & 0x40000000)
443433d6423SLionel Sambuc {
444433d6423SLionel Sambuc status = lp->tx_ring[cur_tx_slot_nr].misc;
445f7df02e7SDavid van Moolenbroek netdriver_stat_oerror(1);
4466315775fSDavid van Moolenbroek if (status & 0x4000) /* UFLO */
447433d6423SLionel Sambuc {
448433d6423SLionel Sambuc must_restart=1;
449433d6423SLionel Sambuc }
450433d6423SLionel Sambuc }
451433d6423SLionel Sambuc else
452433d6423SLionel Sambuc {
453433d6423SLionel Sambuc if (status & 0x18000000)
454f7df02e7SDavid van Moolenbroek netdriver_stat_coll(1);
455433d6423SLionel Sambuc }
456433d6423SLionel Sambuc }
457433d6423SLionel Sambuc /* transmit a packet on the next slot if it exists. */
458433d6423SLionel Sambuc check = 0;
459433d6423SLionel Sambuc if (isstored[cur_tx_slot_nr]==1)
460433d6423SLionel Sambuc {
461433d6423SLionel Sambuc /* free the tx-slot just transmitted */
462433d6423SLionel Sambuc isstored[cur_tx_slot_nr]=0;
463433d6423SLionel Sambuc cur_tx_slot_nr = (cur_tx_slot_nr + 1) & TX_RING_MOD_MASK;
464433d6423SLionel Sambuc
465433d6423SLionel Sambuc /* next tx-slot is ready? */
466433d6423SLionel Sambuc if (isstored[cur_tx_slot_nr]==1)
467433d6423SLionel Sambuc check=1;
468433d6423SLionel Sambuc else
469433d6423SLionel Sambuc check=0;
470433d6423SLionel Sambuc }
471433d6423SLionel Sambuc else
472433d6423SLionel Sambuc {
473433d6423SLionel Sambuc panic("got premature TX INT..");
474433d6423SLionel Sambuc }
475433d6423SLionel Sambuc if (check==1)
476433d6423SLionel Sambuc {
477433d6423SLionel Sambuc lp->tx_ring[cur_tx_slot_nr].u.addr[3] = 0x83;
478433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_IENA|LANCE_CSR0_TDMD);
479433d6423SLionel Sambuc }
480433d6423SLionel Sambuc /* we set a buffered message in the slot if it exists. */
481433d6423SLionel Sambuc /* and transmit it, if needed. */
4826315775fSDavid van Moolenbroek if (!must_restart)
4836315775fSDavid van Moolenbroek netdriver_send();
484433d6423SLionel Sambuc }
485433d6423SLionel Sambuc if (isr & LANCE_CSR0_RINT)
486433d6423SLionel Sambuc {
487433d6423SLionel Sambuc #if VERBOSE
488433d6423SLionel Sambuc printf("RX INT\n");
489433d6423SLionel Sambuc #endif
4906315775fSDavid van Moolenbroek netdriver_recv();
491433d6423SLionel Sambuc }
492433d6423SLionel Sambuc
493433d6423SLionel Sambuc if (must_restart == 1)
494433d6423SLionel Sambuc {
495433d6423SLionel Sambuc #if VERBOSE
496433d6423SLionel Sambuc printf("ETH: restarting...\n");
497433d6423SLionel Sambuc #endif
4986315775fSDavid van Moolenbroek
499*47db417bSDavid van Moolenbroek ec_reinit(ec);
500*47db417bSDavid van Moolenbroek
501*47db417bSDavid van Moolenbroek /* store a buffered message on the slot if it exists */
502*47db417bSDavid van Moolenbroek netdriver_send();
503433d6423SLionel Sambuc }
504433d6423SLionel Sambuc }
505433d6423SLionel Sambuc
5066315775fSDavid van Moolenbroek /* reenable interrupts */
5076315775fSDavid van Moolenbroek if ((r = sys_irqenable(&ec->ec_hook)) != OK)
5086315775fSDavid van Moolenbroek panic("couldn't enable interrupt: %d", r);
509433d6423SLionel Sambuc }
510433d6423SLionel Sambuc
511433d6423SLionel Sambuc /*===========================================================================*
5126315775fSDavid van Moolenbroek * do_recv *
513433d6423SLionel Sambuc *===========================================================================*/
do_recv(struct netdriver_data * data,size_t max)5146315775fSDavid van Moolenbroek static ssize_t do_recv(struct netdriver_data *data, size_t max)
5156315775fSDavid van Moolenbroek {
516433d6423SLionel Sambuc ether_card_t *ec;
517433d6423SLionel Sambuc vir_bytes length;
518433d6423SLionel Sambuc int packet_processed;
519433d6423SLionel Sambuc int status;
5206315775fSDavid van Moolenbroek unsigned short ioaddr;
521433d6423SLionel Sambuc
5226315775fSDavid van Moolenbroek ec = &ec_state;
5236315775fSDavid van Moolenbroek ioaddr = ec->ec_port;
524433d6423SLionel Sambuc
525433d6423SLionel Sambuc /* we check all the received slots until find a properly received packet */
526433d6423SLionel Sambuc packet_processed = FALSE;
527433d6423SLionel Sambuc while (!packet_processed)
528433d6423SLionel Sambuc {
529433d6423SLionel Sambuc status = lp->rx_ring[rx_slot_nr].u.base >> 24;
530433d6423SLionel Sambuc
5316315775fSDavid van Moolenbroek /* is the slot marked as ready? */
5326315775fSDavid van Moolenbroek if ( (status & 0x80) != 0x00 )
5336315775fSDavid van Moolenbroek return SUSPEND; /* no */
5346315775fSDavid van Moolenbroek
5356315775fSDavid van Moolenbroek /* did an error occur? */
536433d6423SLionel Sambuc if (status != 0x03)
537433d6423SLionel Sambuc {
538433d6423SLionel Sambuc if (status & 0x01)
539f7df02e7SDavid van Moolenbroek netdriver_stat_ierror(1);
540433d6423SLionel Sambuc length = 0;
541433d6423SLionel Sambuc }
542433d6423SLionel Sambuc else
543433d6423SLionel Sambuc {
544433d6423SLionel Sambuc length = lp->rx_ring[rx_slot_nr].msg_length;
545433d6423SLionel Sambuc }
5466315775fSDavid van Moolenbroek
5476315775fSDavid van Moolenbroek /* do we now have a valid packet? */
548433d6423SLionel Sambuc if (length > 0)
549433d6423SLionel Sambuc {
5506315775fSDavid van Moolenbroek if (length > max)
5516315775fSDavid van Moolenbroek length = max;
5526315775fSDavid van Moolenbroek netdriver_copyout(data, 0, lp->rbuf[rx_slot_nr], length);
553433d6423SLionel Sambuc packet_processed = TRUE;
554433d6423SLionel Sambuc }
5556315775fSDavid van Moolenbroek
556433d6423SLionel Sambuc /* set up this slot again, and we move to the next slot */
557433d6423SLionel Sambuc lp->rx_ring[rx_slot_nr].buf_length = -ETH_FRAME_LEN;
558433d6423SLionel Sambuc lp->rx_ring[rx_slot_nr].u.addr[3] |= 0x80;
559433d6423SLionel Sambuc
560433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR0,
561433d6423SLionel Sambuc LANCE_CSR0_BABL|LANCE_CSR0_CERR|LANCE_CSR0_MISS
562433d6423SLionel Sambuc |LANCE_CSR0_MERR|LANCE_CSR0_IDON|LANCE_CSR0_IENA);
563433d6423SLionel Sambuc
564433d6423SLionel Sambuc rx_slot_nr = (rx_slot_nr + 1) & RX_RING_MOD_MASK;
565433d6423SLionel Sambuc }
5666315775fSDavid van Moolenbroek
5676315775fSDavid van Moolenbroek /* return the length of the packet we have received */
5686315775fSDavid van Moolenbroek return length;
569433d6423SLionel Sambuc }
570433d6423SLionel Sambuc
571433d6423SLionel Sambuc /*===========================================================================*
5726315775fSDavid van Moolenbroek * do_send *
573433d6423SLionel Sambuc *===========================================================================*/
do_send(struct netdriver_data * data,size_t size)5746315775fSDavid van Moolenbroek static int do_send(struct netdriver_data *data, size_t size)
575433d6423SLionel Sambuc {
5766315775fSDavid van Moolenbroek int check;
577433d6423SLionel Sambuc ether_card_t *ec;
578433d6423SLionel Sambuc unsigned short ioaddr;
579433d6423SLionel Sambuc
580433d6423SLionel Sambuc ec = &ec_state;
581433d6423SLionel Sambuc
5826315775fSDavid van Moolenbroek /* if all slots are used, this request must be deferred */
583433d6423SLionel Sambuc if (isstored[tx_slot_nr]==1)
5846315775fSDavid van Moolenbroek return SUSPEND;
585433d6423SLionel Sambuc
5866315775fSDavid van Moolenbroek /* copy the packet to the slot on DMA address */
5876315775fSDavid van Moolenbroek netdriver_copyin(data, 0, lp->tbuf[tx_slot_nr], size);
588433d6423SLionel Sambuc
589433d6423SLionel Sambuc /* set-up for transmitting, and transmit it if needed. */
5906315775fSDavid van Moolenbroek lp->tx_ring[tx_slot_nr].buf_length = -size;
591433d6423SLionel Sambuc lp->tx_ring[tx_slot_nr].misc = 0x0;
5926315775fSDavid van Moolenbroek lp->tx_ring[tx_slot_nr].u.base = tx_ring_base[tx_slot_nr];
593433d6423SLionel Sambuc isstored[tx_slot_nr]=1;
594433d6423SLionel Sambuc if (cur_tx_slot_nr == tx_slot_nr)
595433d6423SLionel Sambuc check=1;
596433d6423SLionel Sambuc else
597433d6423SLionel Sambuc check=0;
598433d6423SLionel Sambuc tx_slot_nr = (tx_slot_nr + 1) & TX_RING_MOD_MASK;
599433d6423SLionel Sambuc
600433d6423SLionel Sambuc if (check == 1)
601433d6423SLionel Sambuc {
602433d6423SLionel Sambuc ioaddr = ec->ec_port;
603433d6423SLionel Sambuc lp->tx_ring[cur_tx_slot_nr].u.addr[3] = 0x83;
604433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_IENA|LANCE_CSR0_TDMD);
605433d6423SLionel Sambuc }
606433d6423SLionel Sambuc
6076315775fSDavid van Moolenbroek return OK;
608433d6423SLionel Sambuc }
609433d6423SLionel Sambuc
6106315775fSDavid van Moolenbroek /*===========================================================================*
6116315775fSDavid van Moolenbroek * do_stop *
612433d6423SLionel Sambuc *===========================================================================*/
do_stop(void)6136315775fSDavid van Moolenbroek static void do_stop(void)
6146315775fSDavid van Moolenbroek {
615433d6423SLionel Sambuc ether_card_t *ec;
616433d6423SLionel Sambuc unsigned short ioaddr;
617433d6423SLionel Sambuc
6186315775fSDavid van Moolenbroek ec = &ec_state;
619433d6423SLionel Sambuc
620433d6423SLionel Sambuc ioaddr = ec->ec_port;
621433d6423SLionel Sambuc
622433d6423SLionel Sambuc /* stop */
623433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
624433d6423SLionel Sambuc
625433d6423SLionel Sambuc /* Reset */
626433d6423SLionel Sambuc in_word(ioaddr+LANCE_RESET);
627433d6423SLionel Sambuc }
628433d6423SLionel Sambuc
629433d6423SLionel Sambuc /*===========================================================================*
6306315775fSDavid van Moolenbroek * get_addressing *
631433d6423SLionel Sambuc *===========================================================================*/
get_addressing(int devind,ether_card_t * ec)6326315775fSDavid van Moolenbroek static void get_addressing(int devind, ether_card_t *ec)
633433d6423SLionel Sambuc {
6346315775fSDavid van Moolenbroek unsigned int ioaddr;
635433d6423SLionel Sambuc int reg, irq;
636433d6423SLionel Sambuc
637433d6423SLionel Sambuc for (reg = PCI_BAR; reg <= PCI_BAR_6; reg += 4)
638433d6423SLionel Sambuc {
639433d6423SLionel Sambuc ioaddr = pci_attr_r32(devind, reg);
640433d6423SLionel Sambuc
641433d6423SLionel Sambuc if ((ioaddr & PCI_BAR_IO_MASK) == 0 || (ioaddr & PCI_BAR_IO) == 0)
642433d6423SLionel Sambuc continue;
643433d6423SLionel Sambuc /* Strip the I/O address out of the returned value */
644433d6423SLionel Sambuc ioaddr &= PCI_BAR_IO_MASK;
6456315775fSDavid van Moolenbroek ec->ec_port = ioaddr;
6466315775fSDavid van Moolenbroek }
6476315775fSDavid van Moolenbroek
648433d6423SLionel Sambuc /* KK: Get the IRQ number */
649433d6423SLionel Sambuc irq = pci_attr_r8(devind, PCI_IPR);
650433d6423SLionel Sambuc if (irq)
651433d6423SLionel Sambuc irq = pci_attr_r8(devind, PCI_ILR);
652433d6423SLionel Sambuc ec->ec_irq = irq;
653433d6423SLionel Sambuc }
654433d6423SLionel Sambuc
655433d6423SLionel Sambuc /*===========================================================================*
656433d6423SLionel Sambuc * lance_probe *
657433d6423SLionel Sambuc *===========================================================================*/
lance_probe(ether_card_t * ec,unsigned int skip)6586315775fSDavid van Moolenbroek static int lance_probe(ether_card_t *ec, unsigned int skip)
659433d6423SLionel Sambuc {
660433d6423SLionel Sambuc unsigned short pci_cmd;
661433d6423SLionel Sambuc unsigned short ioaddr;
662433d6423SLionel Sambuc int lance_version, chip_version;
663433d6423SLionel Sambuc int devind, r;
664433d6423SLionel Sambuc u16_t vid, did;
665433d6423SLionel Sambuc
6666315775fSDavid van Moolenbroek pci_init();
6676315775fSDavid van Moolenbroek
668433d6423SLionel Sambuc r= pci_first_dev(&devind, &vid, &did);
669433d6423SLionel Sambuc if (r == 0)
670433d6423SLionel Sambuc return 0;
671433d6423SLionel Sambuc
672433d6423SLionel Sambuc while (skip--)
673433d6423SLionel Sambuc {
674433d6423SLionel Sambuc r= pci_next_dev(&devind, &vid, &did);
675433d6423SLionel Sambuc if (!r)
676433d6423SLionel Sambuc return 0;
677433d6423SLionel Sambuc }
678433d6423SLionel Sambuc
679433d6423SLionel Sambuc pci_reserve(devind);
680433d6423SLionel Sambuc
6816315775fSDavid van Moolenbroek get_addressing(devind, ec);
682433d6423SLionel Sambuc
683433d6423SLionel Sambuc /* ===== Bus Master ? ===== */
684433d6423SLionel Sambuc pci_cmd = pci_attr_r32(devind, PCI_CR);
685433d6423SLionel Sambuc if (!(pci_cmd & PCI_CR_MAST_EN)) {
686433d6423SLionel Sambuc pci_cmd |= PCI_CR_MAST_EN;
687433d6423SLionel Sambuc pci_attr_w32(devind, PCI_CR, pci_cmd);
688433d6423SLionel Sambuc }
689433d6423SLionel Sambuc
690433d6423SLionel Sambuc /* ===== Probe Details ===== */
691433d6423SLionel Sambuc ioaddr = ec->ec_port;
692433d6423SLionel Sambuc
693433d6423SLionel Sambuc /* Reset */
694433d6423SLionel Sambuc in_word(ioaddr+LANCE_RESET);
695433d6423SLionel Sambuc
696433d6423SLionel Sambuc if (read_csr(ioaddr, LANCE_CSR0) != LANCE_CSR0_STOP)
697433d6423SLionel Sambuc {
6986315775fSDavid van Moolenbroek return 0;
699433d6423SLionel Sambuc }
700433d6423SLionel Sambuc
701433d6423SLionel Sambuc /* Probe Chip Version */
702433d6423SLionel Sambuc out_word(ioaddr+LANCE_ADDR, 88); /* Get the version of the chip */
703433d6423SLionel Sambuc if (in_word(ioaddr+LANCE_ADDR) != 88)
704433d6423SLionel Sambuc lance_version = 0;
705433d6423SLionel Sambuc else
706433d6423SLionel Sambuc {
707433d6423SLionel Sambuc chip_version = read_csr(ioaddr, LANCE_CSR88);
708433d6423SLionel Sambuc chip_version |= read_csr(ioaddr, LANCE_CSR89) << 16;
709433d6423SLionel Sambuc
710433d6423SLionel Sambuc if ((chip_version & 0xfff) != 0x3)
711433d6423SLionel Sambuc {
7126315775fSDavid van Moolenbroek return 0;
713433d6423SLionel Sambuc }
714433d6423SLionel Sambuc chip_version = (chip_version >> 12) & 0xffff;
715433d6423SLionel Sambuc for (lance_version = 1; chip_table[lance_version].id_number != 0;
716433d6423SLionel Sambuc ++lance_version)
717433d6423SLionel Sambuc if (chip_table[lance_version].id_number == chip_version)
718433d6423SLionel Sambuc break;
719433d6423SLionel Sambuc }
720433d6423SLionel Sambuc
721433d6423SLionel Sambuc #if VERBOSE
722433d6423SLionel Sambuc printf("%s: %s at %X:%d\n",
723f7df02e7SDavid van Moolenbroek netdriver_name(), chip_table[lance_version].name,
724433d6423SLionel Sambuc ec->ec_port, ec->ec_irq);
725433d6423SLionel Sambuc #endif
726433d6423SLionel Sambuc
727433d6423SLionel Sambuc return lance_version;
728433d6423SLionel Sambuc }
729433d6423SLionel Sambuc
730433d6423SLionel Sambuc /*===========================================================================*
7316315775fSDavid van Moolenbroek * virt_to_bus *
732433d6423SLionel Sambuc *===========================================================================*/
virt_to_bus(void * ptr)7336315775fSDavid van Moolenbroek static phys_bytes virt_to_bus(void *ptr)
734433d6423SLionel Sambuc {
7356315775fSDavid van Moolenbroek phys_bytes value;
7366315775fSDavid van Moolenbroek int r;
7376315775fSDavid van Moolenbroek
7386315775fSDavid van Moolenbroek if ((r = sys_umap(SELF, VM_D, (vir_bytes)ptr, 4, &value)) != OK)
7396315775fSDavid van Moolenbroek panic("sys_umap failed: %d", r);
7406315775fSDavid van Moolenbroek
7416315775fSDavid van Moolenbroek return value;
7426315775fSDavid van Moolenbroek }
7436315775fSDavid van Moolenbroek
7446315775fSDavid van Moolenbroek /*===========================================================================*
7456315775fSDavid van Moolenbroek * lance_init_hw *
7466315775fSDavid van Moolenbroek *===========================================================================*/
lance_init_hw(ether_card_t * ec,netdriver_addr_t * addr,unsigned int instance)747f7df02e7SDavid van Moolenbroek static void lance_init_hw(ether_card_t *ec, netdriver_addr_t *addr,
748f7df02e7SDavid van Moolenbroek unsigned int instance)
7496315775fSDavid van Moolenbroek {
7506315775fSDavid van Moolenbroek phys_bytes lance_buf_phys;
7516315775fSDavid van Moolenbroek int i, r;
7526315775fSDavid van Moolenbroek Address l;
753433d6423SLionel Sambuc unsigned short ioaddr = ec->ec_port;
754433d6423SLionel Sambuc
755433d6423SLionel Sambuc /* ============= setup init_block(cf. lance_probe1) ================ */
756433d6423SLionel Sambuc /* make sure data structure is 8-byte aligned and below 16MB (for DMA) */
757433d6423SLionel Sambuc
7586315775fSDavid van Moolenbroek /* Allocate memory */
7596315775fSDavid van Moolenbroek if ((lance_buf = alloc_contig(LANCE_BUF_SIZE, AC_ALIGN4K|AC_LOWER16M,
7606315775fSDavid van Moolenbroek &lance_buf_phys)) == NULL)
7616315775fSDavid van Moolenbroek panic("alloc_contig failed: %d", LANCE_BUF_SIZE);
7626315775fSDavid van Moolenbroek
7636315775fSDavid van Moolenbroek l = (vir_bytes)lance_buf;
764433d6423SLionel Sambuc lp = (struct lance_interface *)l;
765433d6423SLionel Sambuc
766433d6423SLionel Sambuc /* disable Tx and Rx */
767433d6423SLionel Sambuc lp->init_block.mode = LANCE_CSR15_DTX|LANCE_CSR15_DRX;
768433d6423SLionel Sambuc lp->init_block.filter[0] = lp->init_block.filter[1] = 0x0;
769433d6423SLionel Sambuc /* using multiple Rx/Tx buffer */
770433d6423SLionel Sambuc lp->init_block.rx_ring
771433d6423SLionel Sambuc = (virt_to_bus(&lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
772433d6423SLionel Sambuc lp->init_block.tx_ring
773433d6423SLionel Sambuc = (virt_to_bus(&lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
774433d6423SLionel Sambuc
775433d6423SLionel Sambuc l = virt_to_bus(&lp->init_block);
776433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR1, (unsigned short)l);
777433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR2, (unsigned short)(l >> 16));
778433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR4,
779433d6423SLionel Sambuc LANCE_CSR4_APAD_XMT|LANCE_CSR4_MFCOM|LANCE_CSR4_RCVCCOM
780433d6423SLionel Sambuc |LANCE_CSR4_TXSTRTM|LANCE_CSR4_JABM);
781433d6423SLionel Sambuc
782433d6423SLionel Sambuc /* ============= Get MAC address (cf. lance_probe1) ================ */
783433d6423SLionel Sambuc for (i = 0; i < 6; ++i)
784f7df02e7SDavid van Moolenbroek addr->na_addr[i]=in_byte(ioaddr+LANCE_ETH_ADDR+i);
7856315775fSDavid van Moolenbroek
7866315775fSDavid van Moolenbroek /* Allow the user to override the hardware address. */
787f7df02e7SDavid van Moolenbroek ec_confaddr(addr, instance);
788433d6423SLionel Sambuc
789433d6423SLionel Sambuc /* ============ (re)start init_block(cf. lance_reset) =============== */
790433d6423SLionel Sambuc /* Reset the LANCE */
791433d6423SLionel Sambuc (void)in_word(ioaddr+LANCE_RESET);
792433d6423SLionel Sambuc
793433d6423SLionel Sambuc /* ----- Re-initialize the LANCE ----- */
794433d6423SLionel Sambuc /* Set station address */
795433d6423SLionel Sambuc for (i = 0; i < 6; ++i)
796f7df02e7SDavid van Moolenbroek lp->init_block.phys_addr[i] = addr->na_addr[i];
797433d6423SLionel Sambuc /* Preset the receive ring headers */
798433d6423SLionel Sambuc for (i=0; i<RX_RING_SIZE; i++)
799433d6423SLionel Sambuc {
800433d6423SLionel Sambuc lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
801433d6423SLionel Sambuc /* OWN */
802433d6423SLionel Sambuc lp->rx_ring[i].u.base = virt_to_bus(lp->rbuf[i]) & 0xffffff;
803433d6423SLionel Sambuc /* we set the top byte as the very last thing */
804433d6423SLionel Sambuc lp->rx_ring[i].u.addr[3] = 0x80;
805433d6423SLionel Sambuc }
806433d6423SLionel Sambuc /* Preset the transmitting ring headers */
807433d6423SLionel Sambuc for (i=0; i<TX_RING_SIZE; i++)
808433d6423SLionel Sambuc {
809433d6423SLionel Sambuc lp->tx_ring[i].u.base = 0;
8106315775fSDavid van Moolenbroek tx_ring_base[i] = virt_to_bus(lp->tbuf[i]) & 0xffffff;
811433d6423SLionel Sambuc isstored[i] = 0;
812433d6423SLionel Sambuc }
813433d6423SLionel Sambuc /* enable Rx and Tx */
814433d6423SLionel Sambuc lp->init_block.mode = 0x0;
815433d6423SLionel Sambuc
816433d6423SLionel Sambuc l = (Address)virt_to_bus(&lp->init_block);
817433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR1, (short)l);
818433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR2, (short)(l >> 16));
819433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR4,
820433d6423SLionel Sambuc LANCE_CSR4_APAD_XMT|LANCE_CSR4_MFCOM|LANCE_CSR4_RCVCCOM
821433d6423SLionel Sambuc |LANCE_CSR4_TXSTRTM|LANCE_CSR4_JABM);
822433d6423SLionel Sambuc
823433d6423SLionel Sambuc /* ----- start when init done. ----- */
824*47db417bSDavid van Moolenbroek ec_reinit(ec);
825433d6423SLionel Sambuc
8266315775fSDavid van Moolenbroek /* Set the interrupt handler */
8276315775fSDavid van Moolenbroek ec->ec_hook = ec->ec_irq;
8286315775fSDavid van Moolenbroek if ((r=sys_irqsetpolicy(ec->ec_irq, 0, &ec->ec_hook)) != OK)
8296315775fSDavid van Moolenbroek panic("couldn't set IRQ policy: %d", r);
8306315775fSDavid van Moolenbroek if ((r = sys_irqenable(&ec->ec_hook)) != OK)
8316315775fSDavid van Moolenbroek panic("couldn't enable interrupt: %d", r);
8326315775fSDavid van Moolenbroek
833433d6423SLionel Sambuc /* start && enable interrupt */
834433d6423SLionel Sambuc write_csr(ioaddr, LANCE_CSR0,
835433d6423SLionel Sambuc LANCE_CSR0_IDON|LANCE_CSR0_IENA|LANCE_CSR0_STRT);
836433d6423SLionel Sambuc }
837433d6423SLionel Sambuc
838433d6423SLionel Sambuc /*===========================================================================*
839433d6423SLionel Sambuc * in_byte *
840433d6423SLionel Sambuc *===========================================================================*/
in_byte(port_t port)841433d6423SLionel Sambuc static u8_t in_byte(port_t port)
842433d6423SLionel Sambuc {
843433d6423SLionel Sambuc int r;
844433d6423SLionel Sambuc u32_t value;
845433d6423SLionel Sambuc
846433d6423SLionel Sambuc r= sys_inb(port, &value);
847433d6423SLionel Sambuc if (r != OK)
848433d6423SLionel Sambuc panic("sys_inb failed: %d", r);
849433d6423SLionel Sambuc return value;
850433d6423SLionel Sambuc }
851433d6423SLionel Sambuc
852433d6423SLionel Sambuc /*===========================================================================*
853433d6423SLionel Sambuc * in_word *
854433d6423SLionel Sambuc *===========================================================================*/
in_word(port_t port)855433d6423SLionel Sambuc static u16_t in_word(port_t port)
856433d6423SLionel Sambuc {
857433d6423SLionel Sambuc int r;
858433d6423SLionel Sambuc u32_t value;
859433d6423SLionel Sambuc
860433d6423SLionel Sambuc r= sys_inw(port, &value);
861433d6423SLionel Sambuc if (r != OK)
862433d6423SLionel Sambuc panic("sys_inw failed: %d", r);
863433d6423SLionel Sambuc return value;
864433d6423SLionel Sambuc }
865433d6423SLionel Sambuc
866433d6423SLionel Sambuc
867433d6423SLionel Sambuc /*===========================================================================*
868433d6423SLionel Sambuc * out_word *
869433d6423SLionel Sambuc *===========================================================================*/
out_word(port_t port,u16_t value)870433d6423SLionel Sambuc static void out_word(port_t port, u16_t value)
871433d6423SLionel Sambuc {
872433d6423SLionel Sambuc int r;
873433d6423SLionel Sambuc
874433d6423SLionel Sambuc r= sys_outw(port, value);
875433d6423SLionel Sambuc if (r != OK)
876433d6423SLionel Sambuc panic("sys_outw failed: %d", r);
877433d6423SLionel Sambuc }
878433d6423SLionel Sambuc
879433d6423SLionel Sambuc /*===========================================================================*
880433d6423SLionel Sambuc * read_csr *
881433d6423SLionel Sambuc *===========================================================================*/
read_csr(port_t ioaddr,u16_t csrno)882433d6423SLionel Sambuc static u16_t read_csr(port_t ioaddr, u16_t csrno)
883433d6423SLionel Sambuc {
884433d6423SLionel Sambuc out_word(ioaddr+LANCE_ADDR, csrno);
885433d6423SLionel Sambuc return in_word(ioaddr+LANCE_DATA);
886433d6423SLionel Sambuc }
887433d6423SLionel Sambuc
888433d6423SLionel Sambuc /*===========================================================================*
889433d6423SLionel Sambuc * write_csr *
890433d6423SLionel Sambuc *===========================================================================*/
write_csr(port_t ioaddr,u16_t csrno,u16_t value)891433d6423SLionel Sambuc static void write_csr(port_t ioaddr, u16_t csrno, u16_t value)
892433d6423SLionel Sambuc {
893433d6423SLionel Sambuc out_word(ioaddr+LANCE_ADDR, csrno);
894433d6423SLionel Sambuc out_word(ioaddr+LANCE_DATA, value);
895433d6423SLionel Sambuc }
896