1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc * rtl8169.c
3433d6423SLionel Sambuc *
4433d6423SLionel Sambuc * This file contains a ethernet device driver for Realtek rtl8169 based
5433d6423SLionel Sambuc * ethernet cards.
6433d6423SLionel Sambuc *
7433d6423SLionel Sambuc */
8433d6423SLionel Sambuc
9433d6423SLionel Sambuc #include <minix/drivers.h>
10433d6423SLionel Sambuc #include <minix/netdriver.h>
11433d6423SLionel Sambuc
12433d6423SLionel Sambuc #include <machine/pci.h>
13433d6423SLionel Sambuc #include <assert.h>
14433d6423SLionel Sambuc
15433d6423SLionel Sambuc #include "rtl8169.h"
16433d6423SLionel Sambuc
17d76bd1f0SDavid van Moolenbroek #define VERBOSE 0 /* display message during init */
18433d6423SLionel Sambuc
19d76bd1f0SDavid van Moolenbroek #define RE_DTCC_VALUE 600 /* DTCC Update once every 10 minutes */
20433d6423SLionel Sambuc
21433d6423SLionel Sambuc #define RX_CONFIG_MASK 0xff7e1880 /* Clears the bits supported by chip */
22433d6423SLionel Sambuc
23d76bd1f0SDavid van Moolenbroek #define RE_INTR_MASK (RL_IMR_TDU | RL_IMR_FOVW | RL_IMR_PUN | RL_IMR_RDU | \
24d76bd1f0SDavid van Moolenbroek RL_IMR_TER | RL_IMR_TOK | RL_IMR_RER | RL_IMR_ROK)
25433d6423SLionel Sambuc
26433d6423SLionel Sambuc #define RL_ENVVAR "RTLETH" /* Configuration */
27433d6423SLionel Sambuc
28433d6423SLionel Sambuc typedef struct re_desc
29433d6423SLionel Sambuc {
30433d6423SLionel Sambuc u32_t status; /* command/status */
31433d6423SLionel Sambuc u32_t vlan; /* VLAN */
32433d6423SLionel Sambuc u32_t addr_low; /* low 32-bits of physical buffer address */
33433d6423SLionel Sambuc u32_t addr_high; /* high 32-bits of physical buffer address */
34433d6423SLionel Sambuc } re_desc;
35433d6423SLionel Sambuc
36433d6423SLionel Sambuc typedef struct re_dtcc
37433d6423SLionel Sambuc {
38433d6423SLionel Sambuc u32_t TxOk_low; /* low 32-bits of Tx Ok packets */
39433d6423SLionel Sambuc u32_t TxOk_high; /* high 32-bits of Tx Ok packets */
40433d6423SLionel Sambuc u32_t RxOk_low; /* low 32-bits of Rx Ok packets */
41433d6423SLionel Sambuc u32_t RxOk_high; /* high 32-bits of Rx Ok packets */
42433d6423SLionel Sambuc u32_t TxEr_low; /* low 32-bits of Tx errors */
43433d6423SLionel Sambuc u32_t TxEr_high; /* high 32-bits of Tx errors */
44433d6423SLionel Sambuc u32_t RxEr; /* Rx errors */
45433d6423SLionel Sambuc u16_t MissPkt; /* Missed packets */
46d76bd1f0SDavid van Moolenbroek u16_t FAE; /* Frame Alignment Error packets (MII only) */
47d76bd1f0SDavid van Moolenbroek u32_t Tx1Col; /* Tx Ok packets with 1 collision before Tx */
48d76bd1f0SDavid van Moolenbroek u32_t TxMCol; /* Tx Ok packets with 2..15 collisions */
49d76bd1f0SDavid van Moolenbroek u32_t RxOkPhy_low; /* low 32-bits of Rx Ok packets for us */
50d76bd1f0SDavid van Moolenbroek u32_t RxOkPhy_high; /* high 32-bits of Rx Ok packets for us */
51d76bd1f0SDavid van Moolenbroek u32_t RxOkBrd_low; /* low 32-bits of Rx Ok broadcast packets */
52d76bd1f0SDavid van Moolenbroek u32_t RxOkBrd_high; /* high 32-bits of Rx Ok broadcast packets */
53d76bd1f0SDavid van Moolenbroek u32_t RxOkMul; /* Rx Ok multicast packets */
54433d6423SLionel Sambuc u16_t TxAbt; /* Tx abort packets */
55433d6423SLionel Sambuc u16_t TxUndrn; /* Tx underrun packets */
56433d6423SLionel Sambuc } re_dtcc;
57433d6423SLionel Sambuc
58433d6423SLionel Sambuc typedef struct re {
59433d6423SLionel Sambuc port_t re_base_port;
60433d6423SLionel Sambuc int re_irq;
61433d6423SLionel Sambuc int re_mode;
62433d6423SLionel Sambuc int re_link_up;
63433d6423SLionel Sambuc int re_got_int;
64433d6423SLionel Sambuc int re_send_int;
65433d6423SLionel Sambuc int re_report_link;
66433d6423SLionel Sambuc int re_need_reset;
67433d6423SLionel Sambuc int re_tx_alive;
68433d6423SLionel Sambuc u32_t re_mac;
69d76bd1f0SDavid van Moolenbroek const char *re_model;
70433d6423SLionel Sambuc
71433d6423SLionel Sambuc /* Rx */
72433d6423SLionel Sambuc int re_rx_head;
73433d6423SLionel Sambuc struct {
74433d6423SLionel Sambuc phys_bytes ret_buf;
75433d6423SLionel Sambuc char *v_ret_buf;
76433d6423SLionel Sambuc } re_rx[N_RX_DESC];
77433d6423SLionel Sambuc
78433d6423SLionel Sambuc re_desc *re_rx_desc; /* Rx descriptor buffer */
79433d6423SLionel Sambuc phys_bytes p_rx_desc; /* Rx descriptor buffer physical */
80433d6423SLionel Sambuc
81433d6423SLionel Sambuc /* Tx */
82433d6423SLionel Sambuc int re_tx_head;
83433d6423SLionel Sambuc struct {
84433d6423SLionel Sambuc int ret_busy;
85433d6423SLionel Sambuc phys_bytes ret_buf;
86433d6423SLionel Sambuc char *v_ret_buf;
87433d6423SLionel Sambuc } re_tx[N_TX_DESC];
88433d6423SLionel Sambuc re_desc *re_tx_desc; /* Tx descriptor buffer */
89433d6423SLionel Sambuc phys_bytes p_tx_desc; /* Tx descriptor buffer physical */
90d76bd1f0SDavid van Moolenbroek int re_tx_busy; /* how many Tx descriptors are busy? */
91433d6423SLionel Sambuc
92433d6423SLionel Sambuc int re_hook_id; /* IRQ hook id at kernel */
93433d6423SLionel Sambuc phys_bytes dtcc_buf; /* Dump Tally Counter buffer physical */
94433d6423SLionel Sambuc re_dtcc *v_dtcc_buf; /* Dump Tally Counter buffer */
95433d6423SLionel Sambuc u32_t dtcc_counter; /* DTCC update counter */
96433d6423SLionel Sambuc u32_t interrupts;
97d76bd1f0SDavid van Moolenbroek } re_t;
98433d6423SLionel Sambuc
99433d6423SLionel Sambuc static re_t re_state;
100433d6423SLionel Sambuc
my_inb(u16_t port)101433d6423SLionel Sambuc static unsigned my_inb(u16_t port)
102433d6423SLionel Sambuc {
103433d6423SLionel Sambuc u32_t value;
104433d6423SLionel Sambuc int s;
105433d6423SLionel Sambuc if ((s = sys_inb(port, &value)) != OK)
106433d6423SLionel Sambuc printf("RTL8169: warning, sys_inb failed: %d\n", s);
107433d6423SLionel Sambuc return value;
108433d6423SLionel Sambuc }
my_inw(u16_t port)109433d6423SLionel Sambuc static unsigned my_inw(u16_t port)
110433d6423SLionel Sambuc {
111433d6423SLionel Sambuc u32_t value;
112433d6423SLionel Sambuc int s;
113433d6423SLionel Sambuc if ((s = sys_inw(port, &value)) != OK)
114433d6423SLionel Sambuc printf("RTL8169: warning, sys_inw failed: %d\n", s);
115433d6423SLionel Sambuc return value;
116433d6423SLionel Sambuc }
my_inl(u16_t port)117433d6423SLionel Sambuc static unsigned my_inl(u16_t port)
118433d6423SLionel Sambuc {
119433d6423SLionel Sambuc u32_t value;
120433d6423SLionel Sambuc int s;
121433d6423SLionel Sambuc if ((s = sys_inl(port, &value)) != OK)
122433d6423SLionel Sambuc printf("RTL8169: warning, sys_inl failed: %d\n", s);
123433d6423SLionel Sambuc return value;
124433d6423SLionel Sambuc }
125433d6423SLionel Sambuc #define rl_inb(port, offset) (my_inb((port) + (offset)))
126433d6423SLionel Sambuc #define rl_inw(port, offset) (my_inw((port) + (offset)))
127433d6423SLionel Sambuc #define rl_inl(port, offset) (my_inl((port) + (offset)))
128433d6423SLionel Sambuc
my_outb(u16_t port,u8_t value)129433d6423SLionel Sambuc static void my_outb(u16_t port, u8_t value)
130433d6423SLionel Sambuc {
131433d6423SLionel Sambuc int s;
132433d6423SLionel Sambuc
133433d6423SLionel Sambuc if ((s = sys_outb(port, value)) != OK)
134433d6423SLionel Sambuc printf("RTL8169: warning, sys_outb failed: %d\n", s);
135433d6423SLionel Sambuc }
my_outw(u16_t port,u16_t value)136433d6423SLionel Sambuc static void my_outw(u16_t port, u16_t value)
137433d6423SLionel Sambuc {
138433d6423SLionel Sambuc int s;
139433d6423SLionel Sambuc
140433d6423SLionel Sambuc if ((s = sys_outw(port, value)) != OK)
141433d6423SLionel Sambuc printf("RTL8169: warning, sys_outw failed: %d\n", s);
142433d6423SLionel Sambuc }
my_outl(u16_t port,u32_t value)143433d6423SLionel Sambuc static void my_outl(u16_t port, u32_t value)
144433d6423SLionel Sambuc {
145433d6423SLionel Sambuc int s;
146433d6423SLionel Sambuc
147433d6423SLionel Sambuc if ((s = sys_outl(port, value)) != OK)
148433d6423SLionel Sambuc printf("RTL8169: warning, sys_outl failed: %d\n", s);
149433d6423SLionel Sambuc }
150433d6423SLionel Sambuc #define rl_outb(port, offset, value) (my_outb((port) + (offset), (value)))
151433d6423SLionel Sambuc #define rl_outw(port, offset, value) (my_outw((port) + (offset), (value)))
152433d6423SLionel Sambuc #define rl_outl(port, offset, value) (my_outl((port) + (offset), (value)))
153433d6423SLionel Sambuc
154*f7df02e7SDavid van Moolenbroek static int rl_init(unsigned int instance, netdriver_addr_t *addr,
155*f7df02e7SDavid van Moolenbroek uint32_t *caps, unsigned int *ticks);
156d76bd1f0SDavid van Moolenbroek static int rl_probe(re_t *rep, unsigned int skip);
157433d6423SLionel Sambuc static void rl_init_buf(re_t *rep);
158*f7df02e7SDavid van Moolenbroek static void rl_init_hw(re_t *rep, netdriver_addr_t *addr,
159*f7df02e7SDavid van Moolenbroek unsigned int instance);
160433d6423SLionel Sambuc static void rl_reset_hw(re_t *rep);
161*f7df02e7SDavid van Moolenbroek static void rl_confaddr(re_t *rep, netdriver_addr_t *addr,
162*f7df02e7SDavid van Moolenbroek unsigned int instance);
163*f7df02e7SDavid van Moolenbroek static void rl_set_hwaddr(const netdriver_addr_t *addr);
164d76bd1f0SDavid van Moolenbroek static void rl_stop(void);
165433d6423SLionel Sambuc static void rl_rec_mode(re_t *rep);
166*f7df02e7SDavid van Moolenbroek static void rl_set_mode(unsigned int mode, const netdriver_addr_t *mcast_list,
167*f7df02e7SDavid van Moolenbroek unsigned int mcast_count);
168d76bd1f0SDavid van Moolenbroek static ssize_t rl_recv(struct netdriver_data *data, size_t max);
169d76bd1f0SDavid van Moolenbroek static int rl_send(struct netdriver_data *data, size_t size);
170*f7df02e7SDavid van Moolenbroek static unsigned int rl_get_link(uint32_t *media);
171d76bd1f0SDavid van Moolenbroek static void rl_intr(unsigned int mask);
172433d6423SLionel Sambuc static void rl_check_ints(re_t *rep);
173433d6423SLionel Sambuc static void rl_do_reset(re_t *rep);
174d76bd1f0SDavid van Moolenbroek #if VERBOSE
175d76bd1f0SDavid van Moolenbroek static void rl_report_link(re_t *rep);
176433d6423SLionel Sambuc static void dump_phy(const re_t *rep);
177d76bd1f0SDavid van Moolenbroek #endif
178433d6423SLionel Sambuc static void rl_handler(re_t *rep);
179*f7df02e7SDavid van Moolenbroek static void rl_tick(void);
180433d6423SLionel Sambuc
181d76bd1f0SDavid van Moolenbroek static const struct netdriver rl_table = {
182*f7df02e7SDavid van Moolenbroek .ndr_name = "re",
183d76bd1f0SDavid van Moolenbroek .ndr_init = rl_init,
184d76bd1f0SDavid van Moolenbroek .ndr_stop = rl_stop,
185*f7df02e7SDavid van Moolenbroek .ndr_set_mode = rl_set_mode,
186*f7df02e7SDavid van Moolenbroek .ndr_set_hwaddr = rl_set_hwaddr,
187d76bd1f0SDavid van Moolenbroek .ndr_recv = rl_recv,
188d76bd1f0SDavid van Moolenbroek .ndr_send = rl_send,
189*f7df02e7SDavid van Moolenbroek .ndr_get_link = rl_get_link,
190d76bd1f0SDavid van Moolenbroek .ndr_intr = rl_intr,
191*f7df02e7SDavid van Moolenbroek .ndr_tick = rl_tick
192d76bd1f0SDavid van Moolenbroek };
193433d6423SLionel Sambuc
194433d6423SLionel Sambuc /*===========================================================================*
195433d6423SLionel Sambuc * main *
196433d6423SLionel Sambuc *===========================================================================*/
main(int argc,char * argv[])197433d6423SLionel Sambuc int main(int argc, char *argv[])
198433d6423SLionel Sambuc {
199433d6423SLionel Sambuc env_setargs(argc, argv);
200433d6423SLionel Sambuc
201d76bd1f0SDavid van Moolenbroek netdriver_task(&rl_table);
202433d6423SLionel Sambuc
203d76bd1f0SDavid van Moolenbroek return 0;
204433d6423SLionel Sambuc }
205433d6423SLionel Sambuc
206433d6423SLionel Sambuc /*===========================================================================*
207d76bd1f0SDavid van Moolenbroek * rl_init *
208433d6423SLionel Sambuc *===========================================================================*/
rl_init(unsigned int instance,netdriver_addr_t * addr,uint32_t * caps,unsigned int * ticks)209*f7df02e7SDavid van Moolenbroek static int rl_init(unsigned int instance, netdriver_addr_t *addr,
210*f7df02e7SDavid van Moolenbroek uint32_t *caps, unsigned int *ticks)
211433d6423SLionel Sambuc {
212433d6423SLionel Sambuc /* Initialize the rtl8169 driver. */
213d76bd1f0SDavid van Moolenbroek re_t *rep;
214433d6423SLionel Sambuc
215d76bd1f0SDavid van Moolenbroek /* Initialize driver state. */
216d76bd1f0SDavid van Moolenbroek rep = &re_state;
217d76bd1f0SDavid van Moolenbroek memset(rep, 0, sizeof(*rep));
218433d6423SLionel Sambuc
219d76bd1f0SDavid van Moolenbroek /* Try to find a matching device. */
220d76bd1f0SDavid van Moolenbroek if (!rl_probe(rep, instance))
221d76bd1f0SDavid van Moolenbroek return ENXIO;
222433d6423SLionel Sambuc
223433d6423SLionel Sambuc /* Claim buffer memory now. */
224433d6423SLionel Sambuc rl_init_buf(&re_state);
225433d6423SLionel Sambuc
226d76bd1f0SDavid van Moolenbroek /* Initialize the device we found. */
227*f7df02e7SDavid van Moolenbroek rl_init_hw(rep, addr, instance);
228433d6423SLionel Sambuc
229*f7df02e7SDavid van Moolenbroek *caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST | NDEV_CAP_HWADDR;
230*f7df02e7SDavid van Moolenbroek *ticks = sys_hz();
231d76bd1f0SDavid van Moolenbroek return OK;
232433d6423SLionel Sambuc }
233433d6423SLionel Sambuc
234433d6423SLionel Sambuc /*===========================================================================*
235d76bd1f0SDavid van Moolenbroek * rl_stop *
236433d6423SLionel Sambuc *===========================================================================*/
rl_stop(void)237d76bd1f0SDavid van Moolenbroek static void rl_stop(void)
238433d6423SLionel Sambuc {
239433d6423SLionel Sambuc re_t *rep;
240433d6423SLionel Sambuc
241433d6423SLionel Sambuc rep = &re_state;
242433d6423SLionel Sambuc
243d76bd1f0SDavid van Moolenbroek rl_outb(rep->re_base_port, RL_CR, RL_CR_RST);
244433d6423SLionel Sambuc }
245433d6423SLionel Sambuc
mdio_write(u16_t port,int regaddr,int value)246433d6423SLionel Sambuc static void mdio_write(u16_t port, int regaddr, int value)
247433d6423SLionel Sambuc {
248433d6423SLionel Sambuc int i;
249433d6423SLionel Sambuc
250d76bd1f0SDavid van Moolenbroek rl_outl(port, RL_PHYAR,
251d76bd1f0SDavid van Moolenbroek 0x80000000 | (regaddr & 0x1F) << 16 | (value & 0xFFFF));
252433d6423SLionel Sambuc
253433d6423SLionel Sambuc for (i = 20; i > 0; i--) {
254433d6423SLionel Sambuc /*
255433d6423SLionel Sambuc * Check if the RTL8169 has completed writing to the specified
256433d6423SLionel Sambuc * MII register
257433d6423SLionel Sambuc */
258433d6423SLionel Sambuc if (!(rl_inl(port, RL_PHYAR) & 0x80000000))
259433d6423SLionel Sambuc break;
260433d6423SLionel Sambuc else
261433d6423SLionel Sambuc micro_delay(50);
262433d6423SLionel Sambuc }
263433d6423SLionel Sambuc }
264433d6423SLionel Sambuc
mdio_read(u16_t port,int regaddr)265433d6423SLionel Sambuc static int mdio_read(u16_t port, int regaddr)
266433d6423SLionel Sambuc {
267433d6423SLionel Sambuc int i, value = -1;
268433d6423SLionel Sambuc
269433d6423SLionel Sambuc rl_outl(port, RL_PHYAR, (regaddr & 0x1F) << 16);
270433d6423SLionel Sambuc
271433d6423SLionel Sambuc for (i = 20; i > 0; i--) {
272433d6423SLionel Sambuc /*
273433d6423SLionel Sambuc * Check if the RTL8169 has completed retrieving data from
274433d6423SLionel Sambuc * the specified MII register
275433d6423SLionel Sambuc */
276433d6423SLionel Sambuc if (rl_inl(port, RL_PHYAR) & 0x80000000) {
277433d6423SLionel Sambuc value = (int)(rl_inl(port, RL_PHYAR) & 0xFFFF);
278433d6423SLionel Sambuc break;
279433d6423SLionel Sambuc } else
280433d6423SLionel Sambuc micro_delay(50);
281433d6423SLionel Sambuc }
282433d6423SLionel Sambuc return value;
283433d6423SLionel Sambuc }
284433d6423SLionel Sambuc
rtl8169_update_stat(re_t * rep)285433d6423SLionel Sambuc static void rtl8169_update_stat(re_t *rep)
286433d6423SLionel Sambuc {
287*f7df02e7SDavid van Moolenbroek static u64_t last_miss = 0, last_coll = 0;
288*f7df02e7SDavid van Moolenbroek u64_t miss, coll;
289433d6423SLionel Sambuc port_t port;
290433d6423SLionel Sambuc int i;
291433d6423SLionel Sambuc
292433d6423SLionel Sambuc port = rep->re_base_port;
293433d6423SLionel Sambuc
294433d6423SLionel Sambuc /* Dump Tally Counter Command */
295433d6423SLionel Sambuc rl_outl(port, RL_DTCCR_HI, 0); /* 64 bits */
296433d6423SLionel Sambuc rl_outl(port, RL_DTCCR_LO, rep->dtcc_buf | RL_DTCCR_CMD);
297433d6423SLionel Sambuc for (i = 0; i < 1000; i++) {
298433d6423SLionel Sambuc if (!(rl_inl(port, RL_DTCCR_LO) & RL_DTCCR_CMD))
299433d6423SLionel Sambuc break;
300433d6423SLionel Sambuc micro_delay(10);
301433d6423SLionel Sambuc }
302433d6423SLionel Sambuc
303433d6423SLionel Sambuc /* Update counters */
304*f7df02e7SDavid van Moolenbroek miss = rep->v_dtcc_buf->MissPkt;
305*f7df02e7SDavid van Moolenbroek netdriver_stat_ierror(miss - last_miss);
306*f7df02e7SDavid van Moolenbroek last_miss = miss;
307*f7df02e7SDavid van Moolenbroek
308*f7df02e7SDavid van Moolenbroek coll = rep->v_dtcc_buf->Tx1Col + rep->v_dtcc_buf->TxMCol;
309*f7df02e7SDavid van Moolenbroek netdriver_stat_coll(coll - last_coll);
310*f7df02e7SDavid van Moolenbroek last_coll = coll;
311433d6423SLionel Sambuc }
312433d6423SLionel Sambuc
313433d6423SLionel Sambuc #if 0
314433d6423SLionel Sambuc /*===========================================================================*
315433d6423SLionel Sambuc * rtl8169_dump *
316433d6423SLionel Sambuc *===========================================================================*/
317433d6423SLionel Sambuc static void rtl8169_dump(void)
318433d6423SLionel Sambuc {
319433d6423SLionel Sambuc re_dtcc *dtcc;
320433d6423SLionel Sambuc re_t *rep;
321433d6423SLionel Sambuc
322433d6423SLionel Sambuc rep = &re_state;
323433d6423SLionel Sambuc
324433d6423SLionel Sambuc printf("\n");
325433d6423SLionel Sambuc
326433d6423SLionel Sambuc rtl8169_update_stat(rep);
327433d6423SLionel Sambuc
328*f7df02e7SDavid van Moolenbroek printf("Realtek RTL 8169 driver %s:\n", netdriver_name());
329433d6423SLionel Sambuc
330d76bd1f0SDavid van Moolenbroek printf("interrupts :%8u\n", rep->interrupts);
331433d6423SLionel Sambuc
332433d6423SLionel Sambuc printf("\nRealtek RTL 8169 Tally Counters:\n");
333433d6423SLionel Sambuc
334433d6423SLionel Sambuc dtcc = rep->v_dtcc_buf;
335433d6423SLionel Sambuc
336433d6423SLionel Sambuc if (dtcc->TxOk_high)
337d76bd1f0SDavid van Moolenbroek printf("TxOk :%8u%08u\t",
338d76bd1f0SDavid van Moolenbroek dtcc->TxOk_high, dtcc->TxOk_low);
339433d6423SLionel Sambuc else
340d76bd1f0SDavid van Moolenbroek printf("TxOk :%16u\t", dtcc->TxOk_low);
341433d6423SLionel Sambuc
342433d6423SLionel Sambuc if (dtcc->RxOk_high)
343d76bd1f0SDavid van Moolenbroek printf("RxOk :%8u%08u\n",
344d76bd1f0SDavid van Moolenbroek dtcc->RxOk_high, dtcc->RxOk_low);
345433d6423SLionel Sambuc else
346d76bd1f0SDavid van Moolenbroek printf("RxOk :%16u\n", dtcc->RxOk_low);
347433d6423SLionel Sambuc
348433d6423SLionel Sambuc if (dtcc->TxEr_high)
349d76bd1f0SDavid van Moolenbroek printf("TxEr :%8u%08u\t",
350d76bd1f0SDavid van Moolenbroek dtcc->TxEr_high, dtcc->TxEr_low);
351433d6423SLionel Sambuc else
352d76bd1f0SDavid van Moolenbroek printf("TxEr :%16u\t", dtcc->TxEr_low);
353433d6423SLionel Sambuc
354d76bd1f0SDavid van Moolenbroek printf("RxEr :%16u\n", dtcc->RxEr);
355433d6423SLionel Sambuc
356d76bd1f0SDavid van Moolenbroek printf("Tx1Col :%16u\t", dtcc->Tx1Col);
357d76bd1f0SDavid van Moolenbroek printf("TxMCol :%16u\n", dtcc->TxMCol);
358433d6423SLionel Sambuc
359433d6423SLionel Sambuc if (dtcc->RxOkPhy_high)
360d76bd1f0SDavid van Moolenbroek printf("RxOkPhy :%8u%08u\t",
361d76bd1f0SDavid van Moolenbroek dtcc->RxOkPhy_high, dtcc->RxOkPhy_low);
362433d6423SLionel Sambuc else
363d76bd1f0SDavid van Moolenbroek printf("RxOkPhy :%16u\t", dtcc->RxOkPhy_low);
364433d6423SLionel Sambuc
365433d6423SLionel Sambuc if (dtcc->RxOkBrd_high)
366d76bd1f0SDavid van Moolenbroek printf("RxOkBrd :%8u%08u\n",
367d76bd1f0SDavid van Moolenbroek dtcc->RxOkBrd_high, dtcc->RxOkBrd_low);
368433d6423SLionel Sambuc else
369d76bd1f0SDavid van Moolenbroek printf("RxOkBrd :%16u\n", dtcc->RxOkBrd_low);
370433d6423SLionel Sambuc
371d76bd1f0SDavid van Moolenbroek printf("RxOkMul :%16u\t", dtcc->RxOkMul);
372433d6423SLionel Sambuc printf("MissPkt :%16d\n", dtcc->MissPkt);
373433d6423SLionel Sambuc
374433d6423SLionel Sambuc printf("\nRealtek RTL 8169 Miscellaneous Info:\n");
375433d6423SLionel Sambuc
376433d6423SLionel Sambuc printf("tx_head :%8d busy %d\t",
377433d6423SLionel Sambuc rep->re_tx_head, rep->re_tx[rep->re_tx_head].ret_busy);
378433d6423SLionel Sambuc }
379433d6423SLionel Sambuc #endif
380433d6423SLionel Sambuc
381433d6423SLionel Sambuc /*===========================================================================*
382*f7df02e7SDavid van Moolenbroek * rl_set_mode *
383433d6423SLionel Sambuc *===========================================================================*/
rl_set_mode(unsigned int mode,const netdriver_addr_t * mcast_list __unused,unsigned int mcast_count __unused)384*f7df02e7SDavid van Moolenbroek static void rl_set_mode(unsigned int mode,
385*f7df02e7SDavid van Moolenbroek const netdriver_addr_t * mcast_list __unused,
386*f7df02e7SDavid van Moolenbroek unsigned int mcast_count __unused)
387433d6423SLionel Sambuc {
388433d6423SLionel Sambuc re_t *rep;
389433d6423SLionel Sambuc
390433d6423SLionel Sambuc rep = &re_state;
391433d6423SLionel Sambuc
392d76bd1f0SDavid van Moolenbroek rep->re_mode = mode;
393433d6423SLionel Sambuc
394433d6423SLionel Sambuc rl_rec_mode(rep);
395433d6423SLionel Sambuc }
396433d6423SLionel Sambuc
397433d6423SLionel Sambuc /*===========================================================================*
398433d6423SLionel Sambuc * rl_probe *
399433d6423SLionel Sambuc *===========================================================================*/
rl_probe(re_t * rep,unsigned int skip)400d76bd1f0SDavid van Moolenbroek static int rl_probe(re_t *rep, unsigned int skip)
401433d6423SLionel Sambuc {
402433d6423SLionel Sambuc int r, devind;
403433d6423SLionel Sambuc u16_t vid, did;
404433d6423SLionel Sambuc u32_t bar;
405433d6423SLionel Sambuc u8_t ilr;
406433d6423SLionel Sambuc #if VERBOSE
407*f7df02e7SDavid van Moolenbroek const char *dname;
408433d6423SLionel Sambuc #endif
409433d6423SLionel Sambuc
410d76bd1f0SDavid van Moolenbroek pci_init();
411d76bd1f0SDavid van Moolenbroek
412433d6423SLionel Sambuc r = pci_first_dev(&devind, &vid, &did);
413433d6423SLionel Sambuc if (r == 0)
414433d6423SLionel Sambuc return 0;
415433d6423SLionel Sambuc
416433d6423SLionel Sambuc while (skip--) {
417433d6423SLionel Sambuc r = pci_next_dev(&devind, &vid, &did);
418433d6423SLionel Sambuc if (!r)
419433d6423SLionel Sambuc return 0;
420433d6423SLionel Sambuc }
421433d6423SLionel Sambuc
422433d6423SLionel Sambuc #if VERBOSE
423433d6423SLionel Sambuc dname = pci_dev_name(vid, did);
424433d6423SLionel Sambuc if (!dname)
425433d6423SLionel Sambuc dname = "unknown device";
426*f7df02e7SDavid van Moolenbroek printf("%s: ", netdriver_name());
427433d6423SLionel Sambuc printf("%s (%x/%x) at %s\n", dname, vid, did, pci_slot_name(devind));
428433d6423SLionel Sambuc #endif
429433d6423SLionel Sambuc
430433d6423SLionel Sambuc pci_reserve(devind);
431433d6423SLionel Sambuc bar = pci_attr_r32(devind, PCI_BAR) & 0xffffffe0;
432433d6423SLionel Sambuc if (bar < 0x400) {
433433d6423SLionel Sambuc panic("base address is not properly configured");
434433d6423SLionel Sambuc }
435433d6423SLionel Sambuc rep->re_base_port = bar;
436433d6423SLionel Sambuc
437433d6423SLionel Sambuc ilr = pci_attr_r8(devind, PCI_ILR);
438433d6423SLionel Sambuc rep->re_irq = ilr;
439433d6423SLionel Sambuc #if VERBOSE
440433d6423SLionel Sambuc printf("%s: using I/O address 0x%lx, IRQ %d\n",
441*f7df02e7SDavid van Moolenbroek netdriver_name(), (unsigned long)bar, ilr);
442433d6423SLionel Sambuc #endif
443433d6423SLionel Sambuc
444433d6423SLionel Sambuc return TRUE;
445433d6423SLionel Sambuc }
446433d6423SLionel Sambuc
447433d6423SLionel Sambuc /*===========================================================================*
448433d6423SLionel Sambuc * rl_init_buf *
449433d6423SLionel Sambuc *===========================================================================*/
rl_init_buf(re_t * rep)450d76bd1f0SDavid van Moolenbroek static void rl_init_buf(re_t *rep)
451433d6423SLionel Sambuc {
452433d6423SLionel Sambuc size_t rx_bufsize, tx_bufsize, rx_descsize, tx_descsize, tot_bufsize;
453433d6423SLionel Sambuc struct re_desc *desc;
454433d6423SLionel Sambuc phys_bytes buf;
455433d6423SLionel Sambuc char *mallocbuf;
456433d6423SLionel Sambuc int d;
457433d6423SLionel Sambuc
458433d6423SLionel Sambuc /* Allocate receive and transmit descriptors */
459433d6423SLionel Sambuc rx_descsize = (N_RX_DESC * sizeof(struct re_desc));
460433d6423SLionel Sambuc tx_descsize = (N_TX_DESC * sizeof(struct re_desc));
461433d6423SLionel Sambuc
462433d6423SLionel Sambuc /* Allocate receive and transmit buffers */
463*f7df02e7SDavid van Moolenbroek tx_bufsize = NDEV_ETH_PACKET_MAX_TAGGED;
464433d6423SLionel Sambuc if (tx_bufsize % 4)
465433d6423SLionel Sambuc tx_bufsize += 4-(tx_bufsize % 4); /* Align */
466433d6423SLionel Sambuc rx_bufsize = RX_BUFSIZE;
467433d6423SLionel Sambuc tot_bufsize = rx_descsize + tx_descsize;
468433d6423SLionel Sambuc tot_bufsize += (N_TX_DESC * tx_bufsize) + (N_RX_DESC * rx_bufsize);
469433d6423SLionel Sambuc tot_bufsize += sizeof(struct re_dtcc);
470433d6423SLionel Sambuc
471433d6423SLionel Sambuc if (tot_bufsize % 4096)
472433d6423SLionel Sambuc tot_bufsize += 4096 - (tot_bufsize % 4096);
473433d6423SLionel Sambuc
474433d6423SLionel Sambuc if (!(mallocbuf = alloc_contig(tot_bufsize, AC_ALIGN64K, &buf)))
475433d6423SLionel Sambuc panic("Couldn't allocate kernel buffer");
476433d6423SLionel Sambuc
477433d6423SLionel Sambuc /* Rx Descriptor */
478433d6423SLionel Sambuc rep->re_rx_desc = (re_desc *)mallocbuf;
479433d6423SLionel Sambuc rep->p_rx_desc = buf;
480433d6423SLionel Sambuc memset(mallocbuf, 0x00, rx_descsize);
481433d6423SLionel Sambuc buf += rx_descsize;
482433d6423SLionel Sambuc mallocbuf += rx_descsize;
483433d6423SLionel Sambuc
484433d6423SLionel Sambuc /* Tx Descriptor */
485433d6423SLionel Sambuc rep->re_tx_desc = (re_desc *)mallocbuf;
486433d6423SLionel Sambuc rep->p_tx_desc = buf;
487433d6423SLionel Sambuc memset(mallocbuf, 0x00, tx_descsize);
488433d6423SLionel Sambuc buf += tx_descsize;
489433d6423SLionel Sambuc mallocbuf += tx_descsize;
490433d6423SLionel Sambuc
491433d6423SLionel Sambuc desc = rep->re_rx_desc;
492433d6423SLionel Sambuc for (d = 0; d < N_RX_DESC; d++) {
493433d6423SLionel Sambuc /* Setting Rx buffer */
494433d6423SLionel Sambuc rep->re_rx[d].ret_buf = buf;
495433d6423SLionel Sambuc rep->re_rx[d].v_ret_buf = mallocbuf;
496433d6423SLionel Sambuc buf += rx_bufsize;
497433d6423SLionel Sambuc mallocbuf += rx_bufsize;
498433d6423SLionel Sambuc
499433d6423SLionel Sambuc /* Setting Rx descriptor */
500d76bd1f0SDavid van Moolenbroek if (d == (N_RX_DESC - 1)) /* Last descriptor: set EOR bit */
501d76bd1f0SDavid van Moolenbroek desc->status = DESC_EOR | DESC_OWN |
502d76bd1f0SDavid van Moolenbroek (RX_BUFSIZE & DESC_RX_LENMASK);
503433d6423SLionel Sambuc else
504d76bd1f0SDavid van Moolenbroek desc->status = DESC_OWN |
505d76bd1f0SDavid van Moolenbroek (RX_BUFSIZE & DESC_RX_LENMASK);
506433d6423SLionel Sambuc
507433d6423SLionel Sambuc desc->addr_low = rep->re_rx[d].ret_buf;
508433d6423SLionel Sambuc desc++;
509433d6423SLionel Sambuc }
510433d6423SLionel Sambuc desc = rep->re_tx_desc;
511433d6423SLionel Sambuc for (d = 0; d < N_TX_DESC; d++) {
512433d6423SLionel Sambuc rep->re_tx[d].ret_busy = FALSE;
513433d6423SLionel Sambuc rep->re_tx[d].ret_buf = buf;
514433d6423SLionel Sambuc rep->re_tx[d].v_ret_buf = mallocbuf;
515433d6423SLionel Sambuc buf += tx_bufsize;
516433d6423SLionel Sambuc mallocbuf += tx_bufsize;
517433d6423SLionel Sambuc
518433d6423SLionel Sambuc /* Setting Tx descriptor */
519433d6423SLionel Sambuc desc->addr_low = rep->re_tx[d].ret_buf;
520433d6423SLionel Sambuc desc++;
521433d6423SLionel Sambuc }
522d76bd1f0SDavid van Moolenbroek rep->re_tx_busy = 0;
523433d6423SLionel Sambuc
524433d6423SLionel Sambuc /* Dump Tally Counter buffer */
525433d6423SLionel Sambuc rep->dtcc_buf = buf;
526433d6423SLionel Sambuc rep->v_dtcc_buf = (re_dtcc *)mallocbuf;
527433d6423SLionel Sambuc }
528433d6423SLionel Sambuc
529433d6423SLionel Sambuc /*===========================================================================*
530433d6423SLionel Sambuc * rl_init_hw *
531433d6423SLionel Sambuc *===========================================================================*/
rl_init_hw(re_t * rep,netdriver_addr_t * addr,unsigned int instance)532*f7df02e7SDavid van Moolenbroek static void rl_init_hw(re_t *rep, netdriver_addr_t *addr,
533*f7df02e7SDavid van Moolenbroek unsigned int instance)
534433d6423SLionel Sambuc {
535433d6423SLionel Sambuc int s;
536433d6423SLionel Sambuc #if VERBOSE
537433d6423SLionel Sambuc int i;
538433d6423SLionel Sambuc #endif
539433d6423SLionel Sambuc
540433d6423SLionel Sambuc /*
541433d6423SLionel Sambuc * Set the interrupt handler. The policy is to only send HARD_INT
542433d6423SLionel Sambuc * notifications. Don't reenable interrupts automatically. The id
543433d6423SLionel Sambuc * that is passed back is the interrupt line number.
544433d6423SLionel Sambuc */
545433d6423SLionel Sambuc rep->re_hook_id = rep->re_irq;
546433d6423SLionel Sambuc if ((s = sys_irqsetpolicy(rep->re_irq, 0, &rep->re_hook_id)) != OK)
547433d6423SLionel Sambuc printf("RTL8169: error, couldn't set IRQ policy: %d\n", s);
548433d6423SLionel Sambuc
549433d6423SLionel Sambuc rl_reset_hw(rep);
550433d6423SLionel Sambuc
551433d6423SLionel Sambuc if ((s = sys_irqenable(&rep->re_hook_id)) != OK)
552433d6423SLionel Sambuc printf("RTL8169: error, couldn't enable interrupts: %d\n", s);
553433d6423SLionel Sambuc
554433d6423SLionel Sambuc #if VERBOSE
555433d6423SLionel Sambuc printf("%s: model: %s mac: 0x%08x\n",
556*f7df02e7SDavid van Moolenbroek netdriver_name(), rep->re_model, rep->re_mac);
557433d6423SLionel Sambuc #endif
558433d6423SLionel Sambuc
559*f7df02e7SDavid van Moolenbroek rl_confaddr(rep, addr, instance);
560d76bd1f0SDavid van Moolenbroek
561433d6423SLionel Sambuc #if VERBOSE
562*f7df02e7SDavid van Moolenbroek printf("%s: Ethernet address ", netdriver_name());
563433d6423SLionel Sambuc for (i = 0; i < 6; i++) {
564*f7df02e7SDavid van Moolenbroek printf("%x%c", addr->na_addr[i],
565433d6423SLionel Sambuc i < 5 ? ':' : '\n');
566433d6423SLionel Sambuc }
567433d6423SLionel Sambuc #endif
568433d6423SLionel Sambuc }
569433d6423SLionel Sambuc
rtl8169s_phy_config(port_t port)570433d6423SLionel Sambuc static void rtl8169s_phy_config(port_t port)
571433d6423SLionel Sambuc {
572433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x0001);
573433d6423SLionel Sambuc mdio_write(port, 0x06, 0x006e);
574433d6423SLionel Sambuc mdio_write(port, 0x08, 0x0708);
575433d6423SLionel Sambuc mdio_write(port, 0x15, 0x4000);
576433d6423SLionel Sambuc mdio_write(port, 0x18, 0x65c7);
577433d6423SLionel Sambuc
578433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x0001);
579433d6423SLionel Sambuc mdio_write(port, 0x03, 0x00a1);
580433d6423SLionel Sambuc mdio_write(port, 0x02, 0x0008);
581433d6423SLionel Sambuc mdio_write(port, 0x01, 0x0120);
582433d6423SLionel Sambuc mdio_write(port, 0x00, 0x1000);
583433d6423SLionel Sambuc mdio_write(port, 0x04, 0x0800);
584433d6423SLionel Sambuc mdio_write(port, 0x04, 0x0000);
585433d6423SLionel Sambuc
586433d6423SLionel Sambuc mdio_write(port, 0x03, 0xff41);
587433d6423SLionel Sambuc mdio_write(port, 0x02, 0xdf60);
588433d6423SLionel Sambuc mdio_write(port, 0x01, 0x0140);
589433d6423SLionel Sambuc mdio_write(port, 0x00, 0x0077);
590433d6423SLionel Sambuc mdio_write(port, 0x04, 0x7800);
591433d6423SLionel Sambuc mdio_write(port, 0x04, 0x7000);
592433d6423SLionel Sambuc
593433d6423SLionel Sambuc mdio_write(port, 0x03, 0x802f);
594433d6423SLionel Sambuc mdio_write(port, 0x02, 0x4f02);
595433d6423SLionel Sambuc mdio_write(port, 0x01, 0x0409);
596433d6423SLionel Sambuc mdio_write(port, 0x00, 0xf0f9);
597433d6423SLionel Sambuc mdio_write(port, 0x04, 0x9800);
598433d6423SLionel Sambuc mdio_write(port, 0x04, 0x9000);
599433d6423SLionel Sambuc
600433d6423SLionel Sambuc mdio_write(port, 0x03, 0xdf01);
601433d6423SLionel Sambuc mdio_write(port, 0x02, 0xdf20);
602433d6423SLionel Sambuc mdio_write(port, 0x01, 0xff95);
603433d6423SLionel Sambuc mdio_write(port, 0x00, 0xba00);
604433d6423SLionel Sambuc mdio_write(port, 0x04, 0xa800);
605433d6423SLionel Sambuc mdio_write(port, 0x04, 0xa000);
606433d6423SLionel Sambuc
607433d6423SLionel Sambuc mdio_write(port, 0x03, 0xff41);
608433d6423SLionel Sambuc mdio_write(port, 0x02, 0xdf20);
609433d6423SLionel Sambuc mdio_write(port, 0x01, 0x0140);
610433d6423SLionel Sambuc mdio_write(port, 0x00, 0x00bb);
611433d6423SLionel Sambuc mdio_write(port, 0x04, 0xb800);
612433d6423SLionel Sambuc mdio_write(port, 0x04, 0xb000);
613433d6423SLionel Sambuc
614433d6423SLionel Sambuc mdio_write(port, 0x03, 0xdf41);
615433d6423SLionel Sambuc mdio_write(port, 0x02, 0xdc60);
616433d6423SLionel Sambuc mdio_write(port, 0x01, 0x6340);
617433d6423SLionel Sambuc mdio_write(port, 0x00, 0x007d);
618433d6423SLionel Sambuc mdio_write(port, 0x04, 0xd800);
619433d6423SLionel Sambuc mdio_write(port, 0x04, 0xd000);
620433d6423SLionel Sambuc
621433d6423SLionel Sambuc mdio_write(port, 0x03, 0xdf01);
622433d6423SLionel Sambuc mdio_write(port, 0x02, 0xdf20);
623433d6423SLionel Sambuc mdio_write(port, 0x01, 0x100a);
624433d6423SLionel Sambuc mdio_write(port, 0x00, 0xa0ff);
625433d6423SLionel Sambuc mdio_write(port, 0x04, 0xf800);
626433d6423SLionel Sambuc mdio_write(port, 0x04, 0xf000);
627433d6423SLionel Sambuc
628433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x0000);
629433d6423SLionel Sambuc mdio_write(port, 0x0b, 0x0000);
630433d6423SLionel Sambuc mdio_write(port, 0x00, 0x9200);
631433d6423SLionel Sambuc }
632433d6423SLionel Sambuc
rtl8169scd_phy_config(port_t port)633433d6423SLionel Sambuc static void rtl8169scd_phy_config(port_t port)
634433d6423SLionel Sambuc {
635433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x0001);
636433d6423SLionel Sambuc mdio_write(port, 0x04, 0x0000);
637433d6423SLionel Sambuc mdio_write(port, 0x03, 0x00a1);
638433d6423SLionel Sambuc mdio_write(port, 0x02, 0x0008);
639433d6423SLionel Sambuc mdio_write(port, 0x01, 0x0120);
640433d6423SLionel Sambuc mdio_write(port, 0x00, 0x1000);
641433d6423SLionel Sambuc mdio_write(port, 0x04, 0x0800);
642433d6423SLionel Sambuc mdio_write(port, 0x04, 0x9000);
643433d6423SLionel Sambuc mdio_write(port, 0x03, 0x802f);
644433d6423SLionel Sambuc mdio_write(port, 0x02, 0x4f02);
645433d6423SLionel Sambuc mdio_write(port, 0x01, 0x0409);
646433d6423SLionel Sambuc mdio_write(port, 0x00, 0xf099);
647433d6423SLionel Sambuc mdio_write(port, 0x04, 0x9800);
648433d6423SLionel Sambuc mdio_write(port, 0x04, 0xa000);
649433d6423SLionel Sambuc mdio_write(port, 0x03, 0xdf01);
650433d6423SLionel Sambuc mdio_write(port, 0x02, 0xdf20);
651433d6423SLionel Sambuc mdio_write(port, 0x01, 0xff95);
652433d6423SLionel Sambuc mdio_write(port, 0x00, 0xba00);
653433d6423SLionel Sambuc mdio_write(port, 0x04, 0xa800);
654433d6423SLionel Sambuc mdio_write(port, 0x04, 0xf000);
655433d6423SLionel Sambuc mdio_write(port, 0x03, 0xdf01);
656433d6423SLionel Sambuc mdio_write(port, 0x02, 0xdf20);
657433d6423SLionel Sambuc mdio_write(port, 0x01, 0x101a);
658433d6423SLionel Sambuc mdio_write(port, 0x00, 0xa0ff);
659433d6423SLionel Sambuc mdio_write(port, 0x04, 0xf800);
660433d6423SLionel Sambuc mdio_write(port, 0x04, 0x0000);
661433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x0000);
662433d6423SLionel Sambuc
663433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x0001);
664433d6423SLionel Sambuc mdio_write(port, 0x10, 0xf41b);
665433d6423SLionel Sambuc mdio_write(port, 0x14, 0xfb54);
666433d6423SLionel Sambuc mdio_write(port, 0x18, 0xf5c7);
667433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x0000);
668433d6423SLionel Sambuc
669433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x0001);
670433d6423SLionel Sambuc mdio_write(port, 0x17, 0x0cc0);
671433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x0000);
672433d6423SLionel Sambuc }
673433d6423SLionel Sambuc
674433d6423SLionel Sambuc /*===========================================================================*
675433d6423SLionel Sambuc * rl_reset_hw *
676433d6423SLionel Sambuc *===========================================================================*/
rl_reset_hw(re_t * rep)677d76bd1f0SDavid van Moolenbroek static void rl_reset_hw(re_t *rep)
678433d6423SLionel Sambuc {
679433d6423SLionel Sambuc port_t port;
680433d6423SLionel Sambuc u32_t t;
681433d6423SLionel Sambuc int i;
682433d6423SLionel Sambuc
683433d6423SLionel Sambuc port = rep->re_base_port;
684433d6423SLionel Sambuc
685433d6423SLionel Sambuc rl_outw(port, RL_IMR, 0x0000);
686433d6423SLionel Sambuc
687433d6423SLionel Sambuc /* Reset the device */
688433d6423SLionel Sambuc rl_outb(port, RL_CR, RL_CR_RST);
689433d6423SLionel Sambuc SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_RST), 1000000);
690433d6423SLionel Sambuc if (rl_inb(port, RL_CR) & RL_CR_RST)
691433d6423SLionel Sambuc printf("rtl8169: reset failed to complete");
692433d6423SLionel Sambuc rl_outw(port, RL_ISR, 0xFFFF);
693433d6423SLionel Sambuc
694433d6423SLionel Sambuc /* Get Model and MAC info */
695433d6423SLionel Sambuc t = rl_inl(port, RL_TCR);
696433d6423SLionel Sambuc rep->re_mac = (t & (RL_TCR_HWVER_AM | RL_TCR_HWVER_BM));
697433d6423SLionel Sambuc switch (rep->re_mac) {
698433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8169:
699433d6423SLionel Sambuc rep->re_model = "RTL8169";
700433d6423SLionel Sambuc
701433d6423SLionel Sambuc rl_outw(port, RL_CCR_UNDOC, 0x01);
702433d6423SLionel Sambuc break;
703433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8169S:
704433d6423SLionel Sambuc rep->re_model = "RTL8169S";
705433d6423SLionel Sambuc
706433d6423SLionel Sambuc rtl8169s_phy_config(port);
707433d6423SLionel Sambuc
708433d6423SLionel Sambuc rl_outw(port, RL_CCR_UNDOC, 0x01);
709433d6423SLionel Sambuc mdio_write(port, 0x0b, 0x0000); /* w 0x0b 15 0 0 */
710433d6423SLionel Sambuc break;
711433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8110S:
712433d6423SLionel Sambuc rep->re_model = "RTL8110S";
713433d6423SLionel Sambuc
714433d6423SLionel Sambuc rtl8169s_phy_config(port);
715433d6423SLionel Sambuc
716433d6423SLionel Sambuc rl_outw(port, RL_CCR_UNDOC, 0x01);
717433d6423SLionel Sambuc break;
718433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8169SB:
719433d6423SLionel Sambuc rep->re_model = "RTL8169SB";
720433d6423SLionel Sambuc
721433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x02);
722433d6423SLionel Sambuc mdio_write(port, 0x01, 0x90d0);
723433d6423SLionel Sambuc mdio_write(port, 0x1f, 0x00);
724433d6423SLionel Sambuc
725433d6423SLionel Sambuc rl_outw(port, RL_CCR_UNDOC, 0x01);
726433d6423SLionel Sambuc break;
727433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8110SCd:
728433d6423SLionel Sambuc rep->re_model = "RTL8110SCd";
729433d6423SLionel Sambuc
730433d6423SLionel Sambuc rtl8169scd_phy_config(port);
731433d6423SLionel Sambuc
732433d6423SLionel Sambuc rl_outw(port, RL_CCR_UNDOC, 0x01);
733433d6423SLionel Sambuc break;
734433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8105E:
735433d6423SLionel Sambuc rep->re_model = "RTL8105E";
736433d6423SLionel Sambuc break;
737433d6423SLionel Sambuc default:
738433d6423SLionel Sambuc rep->re_model = "Unknown";
739433d6423SLionel Sambuc rep->re_mac = t;
740433d6423SLionel Sambuc break;
741433d6423SLionel Sambuc }
742433d6423SLionel Sambuc
743433d6423SLionel Sambuc mdio_write(port, MII_CTRL, MII_CTRL_RST);
744433d6423SLionel Sambuc for (i = 0; i < 1000; i++) {
745433d6423SLionel Sambuc t = mdio_read(port, MII_CTRL);
746433d6423SLionel Sambuc if (!(t & MII_CTRL_RST))
747433d6423SLionel Sambuc break;
748433d6423SLionel Sambuc else
749433d6423SLionel Sambuc micro_delay(100);
750433d6423SLionel Sambuc }
751433d6423SLionel Sambuc
752d76bd1f0SDavid van Moolenbroek t = mdio_read(port, MII_CTRL);
753d76bd1f0SDavid van Moolenbroek t |= MII_CTRL_ANE | MII_CTRL_DM | MII_CTRL_SP_1000;
754433d6423SLionel Sambuc mdio_write(port, MII_CTRL, t);
755433d6423SLionel Sambuc
756433d6423SLionel Sambuc t = mdio_read(port, MII_ANA);
757433d6423SLionel Sambuc t |= MII_ANA_10THD | MII_ANA_10TFD | MII_ANA_100TXHD | MII_ANA_100TXFD;
758433d6423SLionel Sambuc t |= MII_ANA_PAUSE_SYM | MII_ANA_PAUSE_ASYM;
759433d6423SLionel Sambuc mdio_write(port, MII_ANA, t);
760433d6423SLionel Sambuc
761433d6423SLionel Sambuc t = mdio_read(port, MII_1000_CTRL) | 0x300;
762433d6423SLionel Sambuc mdio_write(port, MII_1000_CTRL, t);
763433d6423SLionel Sambuc
764433d6423SLionel Sambuc /* Restart Auto-Negotiation Process */
765433d6423SLionel Sambuc t = mdio_read(port, MII_CTRL) | MII_CTRL_ANE | MII_CTRL_RAN;
766433d6423SLionel Sambuc mdio_write(port, MII_CTRL, t);
767433d6423SLionel Sambuc
768433d6423SLionel Sambuc rl_outw(port, RL_9346CR, RL_9346CR_EEM_CONFIG); /* Unlock */
769433d6423SLionel Sambuc
770433d6423SLionel Sambuc switch (rep->re_mac) {
771433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8169S:
772433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8110S:
773433d6423SLionel Sambuc /* Bit-3 and bit-14 of the C+CR register MUST be 1. */
774433d6423SLionel Sambuc t = rl_inw(port, RL_CPLUSCMD);
775433d6423SLionel Sambuc rl_outw(port, RL_CPLUSCMD, t | RL_CPLUS_MULRW | (1 << 14));
776433d6423SLionel Sambuc break;
777433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8169:
778433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8169SB:
779433d6423SLionel Sambuc case RL_TCR_HWVER_RTL8110SCd:
780433d6423SLionel Sambuc t = rl_inw(port, RL_CPLUSCMD);
781433d6423SLionel Sambuc rl_outw(port, RL_CPLUSCMD, t | RL_CPLUS_MULRW);
782433d6423SLionel Sambuc break;
783433d6423SLionel Sambuc }
784433d6423SLionel Sambuc
785433d6423SLionel Sambuc rl_outw(port, RL_INTRMITIGATE, 0x00);
786433d6423SLionel Sambuc
787433d6423SLionel Sambuc t = rl_inb(port, RL_CR);
788433d6423SLionel Sambuc rl_outb(port, RL_CR, t | RL_CR_RE | RL_CR_TE);
789433d6423SLionel Sambuc
790433d6423SLionel Sambuc /* Initialize Rx */
791433d6423SLionel Sambuc rl_outw(port, RL_RMS, RX_BUFSIZE); /* Maximum rx packet size */
792433d6423SLionel Sambuc t = rl_inl(port, RL_RCR) & RX_CONFIG_MASK;
793433d6423SLionel Sambuc rl_outl(port, RL_RCR, RL_RCR_RXFTH_UNLIM | RL_RCR_MXDMA_1024 | t);
794433d6423SLionel Sambuc rl_outl(port, RL_RDSAR_LO, rep->p_rx_desc);
795433d6423SLionel Sambuc rl_outl(port, RL_RDSAR_HI, 0x00); /* For 64 bit */
796433d6423SLionel Sambuc
797433d6423SLionel Sambuc /* Initialize Tx */
798433d6423SLionel Sambuc rl_outw(port, RL_ETTHR, 0x3f); /* No early transmit */
799433d6423SLionel Sambuc rl_outl(port, RL_TCR, RL_TCR_MXDMA_2048 | RL_TCR_IFG_STD);
800433d6423SLionel Sambuc rl_outl(port, RL_TNPDS_LO, rep->p_tx_desc);
801433d6423SLionel Sambuc rl_outl(port, RL_TNPDS_HI, 0x00); /* For 64 bit */
802433d6423SLionel Sambuc
803433d6423SLionel Sambuc rl_outw(port, RL_9346CR, RL_9346CR_EEM_NORMAL); /* Lock */
804433d6423SLionel Sambuc
805433d6423SLionel Sambuc rl_outw(port, RL_MPC, 0x00);
806433d6423SLionel Sambuc rl_outw(port, RL_MULINT, rl_inw(port, RL_MULINT) & 0xF000);
807433d6423SLionel Sambuc rl_outw(port, RL_IMR, RE_INTR_MASK);
808433d6423SLionel Sambuc }
809433d6423SLionel Sambuc
810433d6423SLionel Sambuc /*===========================================================================*
811433d6423SLionel Sambuc * rl_confaddr *
812433d6423SLionel Sambuc *===========================================================================*/
rl_confaddr(re_t * rep,netdriver_addr_t * addr,unsigned int instance)813*f7df02e7SDavid van Moolenbroek static void rl_confaddr(re_t *rep, netdriver_addr_t *addr,
814*f7df02e7SDavid van Moolenbroek unsigned int instance)
815433d6423SLionel Sambuc {
816433d6423SLionel Sambuc static char eakey[] = RL_ENVVAR "#_EA";
817433d6423SLionel Sambuc static char eafmt[] = "x:x:x:x:x:x";
818433d6423SLionel Sambuc int i;
819433d6423SLionel Sambuc port_t port;
820433d6423SLionel Sambuc long v;
821433d6423SLionel Sambuc
822433d6423SLionel Sambuc /* User defined ethernet address? */
823*f7df02e7SDavid van Moolenbroek eakey[sizeof(RL_ENVVAR)-1] = '0' + instance;
824433d6423SLionel Sambuc
825433d6423SLionel Sambuc port = rep->re_base_port;
826433d6423SLionel Sambuc
827433d6423SLionel Sambuc for (i = 0; i < 6; i++) {
828433d6423SLionel Sambuc if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET)
829433d6423SLionel Sambuc break;
830*f7df02e7SDavid van Moolenbroek addr->na_addr[i] = v;
831433d6423SLionel Sambuc }
832433d6423SLionel Sambuc
833433d6423SLionel Sambuc if (i != 0 && i != 6)
834433d6423SLionel Sambuc env_panic(eakey); /* It's all or nothing */
835433d6423SLionel Sambuc
836433d6423SLionel Sambuc /* Should update ethernet address in hardware */
837*f7df02e7SDavid van Moolenbroek if (i == 6)
838*f7df02e7SDavid van Moolenbroek rl_set_hwaddr(addr);
839*f7df02e7SDavid van Moolenbroek
840*f7df02e7SDavid van Moolenbroek /* Get ethernet address */
841*f7df02e7SDavid van Moolenbroek for (i = 0; i < 6; i++)
842*f7df02e7SDavid van Moolenbroek addr->na_addr[i] = rl_inb(port, RL_IDR+i);
843*f7df02e7SDavid van Moolenbroek }
844*f7df02e7SDavid van Moolenbroek
845*f7df02e7SDavid van Moolenbroek /*===========================================================================*
846*f7df02e7SDavid van Moolenbroek * rl_set_hwaddr *
847*f7df02e7SDavid van Moolenbroek *===========================================================================*/
rl_set_hwaddr(const netdriver_addr_t * addr)848*f7df02e7SDavid van Moolenbroek static void rl_set_hwaddr(const netdriver_addr_t *addr)
849*f7df02e7SDavid van Moolenbroek {
850*f7df02e7SDavid van Moolenbroek re_t *rep;
851*f7df02e7SDavid van Moolenbroek port_t port;
852*f7df02e7SDavid van Moolenbroek u32_t w;
853*f7df02e7SDavid van Moolenbroek int i;
854*f7df02e7SDavid van Moolenbroek
855*f7df02e7SDavid van Moolenbroek rep = &re_state;
856*f7df02e7SDavid van Moolenbroek
857433d6423SLionel Sambuc port = rep->re_base_port;
858433d6423SLionel Sambuc rl_outb(port, RL_9346CR, RL_9346CR_EEM_CONFIG);
859433d6423SLionel Sambuc w = 0;
860433d6423SLionel Sambuc for (i = 0; i < 4; i++)
861*f7df02e7SDavid van Moolenbroek w |= (addr->na_addr[i] << (i * 8));
862433d6423SLionel Sambuc rl_outl(port, RL_IDR, w);
863433d6423SLionel Sambuc w = 0;
864433d6423SLionel Sambuc for (i = 4; i < 6; i++)
865*f7df02e7SDavid van Moolenbroek w |= (addr->na_addr[i] << ((i-4) * 8));
866433d6423SLionel Sambuc rl_outl(port, RL_IDR + 4, w);
867433d6423SLionel Sambuc rl_outb(port, RL_9346CR, RL_9346CR_EEM_NORMAL);
868433d6423SLionel Sambuc }
869433d6423SLionel Sambuc
870433d6423SLionel Sambuc /*===========================================================================*
871433d6423SLionel Sambuc * rl_rec_mode *
872433d6423SLionel Sambuc *===========================================================================*/
rl_rec_mode(re_t * rep)873d76bd1f0SDavid van Moolenbroek static void rl_rec_mode(re_t *rep)
874433d6423SLionel Sambuc {
875433d6423SLionel Sambuc port_t port;
876433d6423SLionel Sambuc u32_t rcr;
877433d6423SLionel Sambuc u32_t mc_filter[2]; /* Multicast hash filter */
878433d6423SLionel Sambuc
879433d6423SLionel Sambuc port = rep->re_base_port;
880433d6423SLionel Sambuc
881433d6423SLionel Sambuc mc_filter[1] = mc_filter[0] = 0xffffffff;
882433d6423SLionel Sambuc rl_outl(port, RL_MAR + 0, mc_filter[0]);
883433d6423SLionel Sambuc rl_outl(port, RL_MAR + 4, mc_filter[1]);
884433d6423SLionel Sambuc
885433d6423SLionel Sambuc rcr = rl_inl(port, RL_RCR);
886433d6423SLionel Sambuc rcr &= ~(RL_RCR_AB | RL_RCR_AM | RL_RCR_APM | RL_RCR_AAP);
887*f7df02e7SDavid van Moolenbroek if (rep->re_mode & NDEV_MODE_PROMISC)
888433d6423SLionel Sambuc rcr |= RL_RCR_AB | RL_RCR_AM | RL_RCR_AAP;
889*f7df02e7SDavid van Moolenbroek if (rep->re_mode & NDEV_MODE_BCAST)
890433d6423SLionel Sambuc rcr |= RL_RCR_AB;
891*f7df02e7SDavid van Moolenbroek if (rep->re_mode & (NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
892433d6423SLionel Sambuc rcr |= RL_RCR_AM;
893433d6423SLionel Sambuc rcr |= RL_RCR_APM;
894433d6423SLionel Sambuc rl_outl(port, RL_RCR, RL_RCR_RXFTH_UNLIM | RL_RCR_MXDMA_1024 | rcr);
895433d6423SLionel Sambuc }
896433d6423SLionel Sambuc
897433d6423SLionel Sambuc /*===========================================================================*
898d76bd1f0SDavid van Moolenbroek * rl_recv *
899433d6423SLionel Sambuc *===========================================================================*/
rl_recv(struct netdriver_data * data,size_t max)900d76bd1f0SDavid van Moolenbroek static ssize_t rl_recv(struct netdriver_data *data, size_t max)
901433d6423SLionel Sambuc {
902d76bd1f0SDavid van Moolenbroek int index;
903433d6423SLionel Sambuc port_t port;
904433d6423SLionel Sambuc unsigned totlen, packlen;
905433d6423SLionel Sambuc re_desc *desc;
906d76bd1f0SDavid van Moolenbroek u32_t rxstat;
907433d6423SLionel Sambuc re_t *rep;
908433d6423SLionel Sambuc
909433d6423SLionel Sambuc rep = &re_state;
910433d6423SLionel Sambuc
911433d6423SLionel Sambuc port = rep->re_base_port;
912433d6423SLionel Sambuc
913d76bd1f0SDavid van Moolenbroek if (rl_inb(port, RL_CR) & RL_CR_BUFE)
914d76bd1f0SDavid van Moolenbroek return SUSPEND; /* Receive buffer is empty, suspend */
915433d6423SLionel Sambuc
916433d6423SLionel Sambuc index = rep->re_rx_head;
917433d6423SLionel Sambuc desc = rep->re_rx_desc;
918433d6423SLionel Sambuc desc += index;
919d76bd1f0SDavid van Moolenbroek
920d76bd1f0SDavid van Moolenbroek for (;;) {
921433d6423SLionel Sambuc rxstat = desc->status;
922433d6423SLionel Sambuc
923433d6423SLionel Sambuc if (rxstat & DESC_OWN)
924d76bd1f0SDavid van Moolenbroek return SUSPEND;
925433d6423SLionel Sambuc
926433d6423SLionel Sambuc if (rxstat & DESC_RX_CRC)
927*f7df02e7SDavid van Moolenbroek netdriver_stat_ierror(1);
928433d6423SLionel Sambuc
929d76bd1f0SDavid van Moolenbroek if ((rxstat & (DESC_FS | DESC_LS)) == (DESC_FS | DESC_LS))
930d76bd1f0SDavid van Moolenbroek break;
931d76bd1f0SDavid van Moolenbroek
932433d6423SLionel Sambuc #if VERBOSE
933d76bd1f0SDavid van Moolenbroek printf("rl_recv: packet is fragmented\n");
934433d6423SLionel Sambuc #endif
935433d6423SLionel Sambuc /* Fix the fragmented packet */
936433d6423SLionel Sambuc if (index == N_RX_DESC - 1) {
937d76bd1f0SDavid van Moolenbroek desc->status = DESC_EOR | DESC_OWN |
938d76bd1f0SDavid van Moolenbroek (RX_BUFSIZE & DESC_RX_LENMASK);
939433d6423SLionel Sambuc index = 0;
940433d6423SLionel Sambuc desc = rep->re_rx_desc;
941433d6423SLionel Sambuc } else {
942d76bd1f0SDavid van Moolenbroek desc->status = DESC_OWN |
943d76bd1f0SDavid van Moolenbroek (RX_BUFSIZE & DESC_RX_LENMASK);
944433d6423SLionel Sambuc index++;
945433d6423SLionel Sambuc desc++;
946433d6423SLionel Sambuc }
947d76bd1f0SDavid van Moolenbroek /* Loop until we get correct packet */
948433d6423SLionel Sambuc }
949433d6423SLionel Sambuc
950433d6423SLionel Sambuc totlen = rxstat & DESC_RX_LENMASK;
951*f7df02e7SDavid van Moolenbroek if (totlen < 8 || totlen > 2 * NDEV_ETH_PACKET_MAX) {
952433d6423SLionel Sambuc /* Someting went wrong */
953d76bd1f0SDavid van Moolenbroek printf("rl_recv: bad length (%u) in status 0x%08x\n",
954433d6423SLionel Sambuc totlen, rxstat);
955433d6423SLionel Sambuc panic(NULL);
956433d6423SLionel Sambuc }
957433d6423SLionel Sambuc
958433d6423SLionel Sambuc /* Should subtract the CRC */
959*f7df02e7SDavid van Moolenbroek packlen = totlen - NDEV_ETH_PACKET_CRC;
960d76bd1f0SDavid van Moolenbroek if (packlen > max)
961d76bd1f0SDavid van Moolenbroek packlen = max;
962433d6423SLionel Sambuc
963d76bd1f0SDavid van Moolenbroek netdriver_copyout(data, 0, rep->re_rx[index].v_ret_buf, packlen);
964433d6423SLionel Sambuc
965433d6423SLionel Sambuc if (index == N_RX_DESC - 1) {
966d76bd1f0SDavid van Moolenbroek desc->status = DESC_EOR | DESC_OWN |
967d76bd1f0SDavid van Moolenbroek (RX_BUFSIZE & DESC_RX_LENMASK);
968433d6423SLionel Sambuc index = 0;
969433d6423SLionel Sambuc } else {
970433d6423SLionel Sambuc desc->status = DESC_OWN | (RX_BUFSIZE & DESC_RX_LENMASK);
971433d6423SLionel Sambuc index++;
972433d6423SLionel Sambuc }
973433d6423SLionel Sambuc rep->re_rx_head = index;
974433d6423SLionel Sambuc assert(rep->re_rx_head < N_RX_DESC);
975433d6423SLionel Sambuc
976d76bd1f0SDavid van Moolenbroek return packlen;
977433d6423SLionel Sambuc }
978433d6423SLionel Sambuc
979433d6423SLionel Sambuc /*===========================================================================*
980d76bd1f0SDavid van Moolenbroek * rl_send *
981433d6423SLionel Sambuc *===========================================================================*/
rl_send(struct netdriver_data * data,size_t size)982d76bd1f0SDavid van Moolenbroek static int rl_send(struct netdriver_data *data, size_t size)
983433d6423SLionel Sambuc {
984433d6423SLionel Sambuc int tx_head;
985433d6423SLionel Sambuc re_t *rep;
986433d6423SLionel Sambuc re_desc *desc;
987433d6423SLionel Sambuc
988433d6423SLionel Sambuc rep = &re_state;
989433d6423SLionel Sambuc
990433d6423SLionel Sambuc tx_head = rep->re_tx_head;
991433d6423SLionel Sambuc
992433d6423SLionel Sambuc desc = rep->re_tx_desc;
993433d6423SLionel Sambuc desc += tx_head;
994433d6423SLionel Sambuc
995d76bd1f0SDavid van Moolenbroek assert(desc);
996433d6423SLionel Sambuc assert(rep->re_tx_desc);
997433d6423SLionel Sambuc assert(rep->re_tx_head >= 0 && rep->re_tx_head < N_TX_DESC);
998433d6423SLionel Sambuc
999433d6423SLionel Sambuc if (rep->re_tx[tx_head].ret_busy)
1000d76bd1f0SDavid van Moolenbroek return SUSPEND;
1001433d6423SLionel Sambuc
1002d76bd1f0SDavid van Moolenbroek netdriver_copyin(data, 0, rep->re_tx[tx_head].v_ret_buf, size);
1003433d6423SLionel Sambuc
1004433d6423SLionel Sambuc rep->re_tx[tx_head].ret_busy = TRUE;
1005d76bd1f0SDavid van Moolenbroek rep->re_tx_busy++;
1006433d6423SLionel Sambuc
1007433d6423SLionel Sambuc if (tx_head == N_TX_DESC - 1) {
1008433d6423SLionel Sambuc desc->status = DESC_EOR | DESC_OWN | DESC_FS | DESC_LS | size;
1009433d6423SLionel Sambuc tx_head = 0;
1010433d6423SLionel Sambuc } else {
1011433d6423SLionel Sambuc desc->status = DESC_OWN | DESC_FS | DESC_LS | size;
1012433d6423SLionel Sambuc tx_head++;
1013433d6423SLionel Sambuc }
1014433d6423SLionel Sambuc
1015433d6423SLionel Sambuc assert(tx_head < N_TX_DESC);
1016433d6423SLionel Sambuc rep->re_tx_head = tx_head;
1017433d6423SLionel Sambuc
1018433d6423SLionel Sambuc rl_outl(rep->re_base_port, RL_TPPOLL, RL_TPPOLL_NPQ);
1019433d6423SLionel Sambuc
1020d76bd1f0SDavid van Moolenbroek return OK;
1021433d6423SLionel Sambuc }
1022433d6423SLionel Sambuc
1023433d6423SLionel Sambuc /*===========================================================================*
1024433d6423SLionel Sambuc * rl_check_ints *
1025433d6423SLionel Sambuc *===========================================================================*/
rl_check_ints(re_t * rep)1026d76bd1f0SDavid van Moolenbroek static void rl_check_ints(re_t *rep)
1027433d6423SLionel Sambuc {
1028d76bd1f0SDavid van Moolenbroek if (!rep->re_got_int)
1029d76bd1f0SDavid van Moolenbroek return;
1030d76bd1f0SDavid van Moolenbroek rep->re_got_int = FALSE;
1031433d6423SLionel Sambuc
1032d76bd1f0SDavid van Moolenbroek netdriver_recv();
1033433d6423SLionel Sambuc
1034433d6423SLionel Sambuc if (rep->re_need_reset)
1035433d6423SLionel Sambuc rl_do_reset(rep);
1036433d6423SLionel Sambuc
1037433d6423SLionel Sambuc if (rep->re_send_int) {
1038d76bd1f0SDavid van Moolenbroek rep->re_send_int = FALSE;
1039d76bd1f0SDavid van Moolenbroek
1040d76bd1f0SDavid van Moolenbroek netdriver_send();
1041433d6423SLionel Sambuc }
1042433d6423SLionel Sambuc
1043433d6423SLionel Sambuc if (rep->re_report_link) {
1044433d6423SLionel Sambuc rep->re_report_link = FALSE;
1045433d6423SLionel Sambuc
1046*f7df02e7SDavid van Moolenbroek netdriver_link();
1047*f7df02e7SDavid van Moolenbroek
1048d76bd1f0SDavid van Moolenbroek #if VERBOSE
1049433d6423SLionel Sambuc rl_report_link(rep);
1050d76bd1f0SDavid van Moolenbroek #endif
1051433d6423SLionel Sambuc }
1052433d6423SLionel Sambuc }
1053433d6423SLionel Sambuc
1054433d6423SLionel Sambuc /*===========================================================================*
1055*f7df02e7SDavid van Moolenbroek * rl_get_link *
1056*f7df02e7SDavid van Moolenbroek *===========================================================================*/
rl_get_link(uint32_t * media)1057*f7df02e7SDavid van Moolenbroek static unsigned int rl_get_link(uint32_t *media)
1058*f7df02e7SDavid van Moolenbroek {
1059*f7df02e7SDavid van Moolenbroek re_t *rep;
1060*f7df02e7SDavid van Moolenbroek u8_t mii_status;
1061*f7df02e7SDavid van Moolenbroek
1062*f7df02e7SDavid van Moolenbroek rep = &re_state;
1063*f7df02e7SDavid van Moolenbroek
1064*f7df02e7SDavid van Moolenbroek mii_status = rl_inb(rep->re_base_port, RL_PHYSTAT);
1065*f7df02e7SDavid van Moolenbroek
1066*f7df02e7SDavid van Moolenbroek if (!(mii_status & RL_STAT_LINK))
1067*f7df02e7SDavid van Moolenbroek return NDEV_LINK_DOWN;
1068*f7df02e7SDavid van Moolenbroek
1069*f7df02e7SDavid van Moolenbroek if (mii_status & RL_STAT_1000)
1070*f7df02e7SDavid van Moolenbroek *media = IFM_ETHER | IFM_1000_T;
1071*f7df02e7SDavid van Moolenbroek else if (mii_status & RL_STAT_100)
1072*f7df02e7SDavid van Moolenbroek *media = IFM_ETHER | IFM_100_TX;
1073*f7df02e7SDavid van Moolenbroek else if (mii_status & RL_STAT_10)
1074*f7df02e7SDavid van Moolenbroek *media = IFM_ETHER | IFM_10_T;
1075*f7df02e7SDavid van Moolenbroek
1076*f7df02e7SDavid van Moolenbroek if (mii_status & RL_STAT_FULLDUP)
1077*f7df02e7SDavid van Moolenbroek *media |= IFM_FDX;
1078*f7df02e7SDavid van Moolenbroek else
1079*f7df02e7SDavid van Moolenbroek *media |= IFM_HDX;
1080*f7df02e7SDavid van Moolenbroek
1081*f7df02e7SDavid van Moolenbroek return NDEV_LINK_UP;
1082*f7df02e7SDavid van Moolenbroek }
1083*f7df02e7SDavid van Moolenbroek
1084*f7df02e7SDavid van Moolenbroek /*===========================================================================*
1085433d6423SLionel Sambuc * rl_report_link *
1086433d6423SLionel Sambuc *===========================================================================*/
1087433d6423SLionel Sambuc #if VERBOSE
rl_report_link(re_t * rep)1088d76bd1f0SDavid van Moolenbroek static void rl_report_link(re_t *rep)
1089d76bd1f0SDavid van Moolenbroek {
1090433d6423SLionel Sambuc port_t port;
1091433d6423SLionel Sambuc u8_t mii_status;
1092433d6423SLionel Sambuc
1093433d6423SLionel Sambuc port = rep->re_base_port;
1094433d6423SLionel Sambuc
1095433d6423SLionel Sambuc mii_status = rl_inb(port, RL_PHYSTAT);
1096433d6423SLionel Sambuc
1097433d6423SLionel Sambuc if (mii_status & RL_STAT_LINK) {
1098433d6423SLionel Sambuc rep->re_link_up = 1;
1099*f7df02e7SDavid van Moolenbroek printf("%s: link up at ", netdriver_name());
1100433d6423SLionel Sambuc } else {
1101433d6423SLionel Sambuc rep->re_link_up = 0;
1102*f7df02e7SDavid van Moolenbroek printf("%s: link down\n", netdriver_name());
1103433d6423SLionel Sambuc return;
1104433d6423SLionel Sambuc }
1105433d6423SLionel Sambuc
1106433d6423SLionel Sambuc if (mii_status & RL_STAT_1000)
1107433d6423SLionel Sambuc printf("1000 Mbps");
1108433d6423SLionel Sambuc else if (mii_status & RL_STAT_100)
1109433d6423SLionel Sambuc printf("100 Mbps");
1110433d6423SLionel Sambuc else if (mii_status & RL_STAT_10)
1111433d6423SLionel Sambuc printf("10 Mbps");
1112433d6423SLionel Sambuc
1113433d6423SLionel Sambuc if (mii_status & RL_STAT_FULLDUP)
1114433d6423SLionel Sambuc printf(", full duplex");
1115433d6423SLionel Sambuc else
1116433d6423SLionel Sambuc printf(", half duplex");
1117433d6423SLionel Sambuc printf("\n");
1118433d6423SLionel Sambuc
1119433d6423SLionel Sambuc dump_phy(rep);
1120433d6423SLionel Sambuc }
1121d76bd1f0SDavid van Moolenbroek #endif
1122433d6423SLionel Sambuc
1123433d6423SLionel Sambuc /*===========================================================================*
1124433d6423SLionel Sambuc * rl_do_reset *
1125433d6423SLionel Sambuc *===========================================================================*/
rl_do_reset(re_t * rep)1126d76bd1f0SDavid van Moolenbroek static void rl_do_reset(re_t *rep)
1127433d6423SLionel Sambuc {
1128433d6423SLionel Sambuc rep->re_need_reset = FALSE;
1129433d6423SLionel Sambuc rl_reset_hw(rep);
1130433d6423SLionel Sambuc rl_rec_mode(rep);
1131433d6423SLionel Sambuc
1132433d6423SLionel Sambuc rep->re_tx_head = 0;
1133d76bd1f0SDavid van Moolenbroek if (rep->re_tx[rep->re_tx_head].ret_busy)
1134d76bd1f0SDavid van Moolenbroek rep->re_tx_busy--;
1135433d6423SLionel Sambuc rep->re_tx[rep->re_tx_head].ret_busy = FALSE;
1136433d6423SLionel Sambuc rep->re_send_int = TRUE;
1137433d6423SLionel Sambuc }
1138433d6423SLionel Sambuc
1139d76bd1f0SDavid van Moolenbroek #if VERBOSE
dump_phy(const re_t * rep)1140433d6423SLionel Sambuc static void dump_phy(const re_t *rep)
1141433d6423SLionel Sambuc {
1142433d6423SLionel Sambuc port_t port;
1143433d6423SLionel Sambuc u32_t t;
1144433d6423SLionel Sambuc
1145433d6423SLionel Sambuc port = rep->re_base_port;
1146433d6423SLionel Sambuc
1147433d6423SLionel Sambuc t = rl_inb(port, RL_CONFIG0);
1148433d6423SLionel Sambuc printf("CONFIG0\t\t:");
1149433d6423SLionel Sambuc t = t & RL_CFG0_ROM;
1150433d6423SLionel Sambuc if (t == RL_CFG0_ROM128K)
1151433d6423SLionel Sambuc printf(" 128K Boot ROM");
1152433d6423SLionel Sambuc else if (t == RL_CFG0_ROM64K)
1153433d6423SLionel Sambuc printf(" 64K Boot ROM");
1154433d6423SLionel Sambuc else if (t == RL_CFG0_ROM32K)
1155433d6423SLionel Sambuc printf(" 32K Boot ROM");
1156433d6423SLionel Sambuc else if (t == RL_CFG0_ROM16K)
1157433d6423SLionel Sambuc printf(" 16K Boot ROM");
1158433d6423SLionel Sambuc else if (t == RL_CFG0_ROM8K)
1159433d6423SLionel Sambuc printf(" 8K Boot ROM");
1160433d6423SLionel Sambuc else if (t == RL_CFG0_ROMNO)
1161433d6423SLionel Sambuc printf(" No Boot ROM");
1162433d6423SLionel Sambuc printf("\n");
1163433d6423SLionel Sambuc
1164433d6423SLionel Sambuc t = rl_inb(port, RL_CONFIG1);
1165433d6423SLionel Sambuc printf("CONFIG1\t\t:");
1166433d6423SLionel Sambuc if (t & RL_CFG1_LEDS1)
1167433d6423SLionel Sambuc printf(" LED1");
1168433d6423SLionel Sambuc if (t & RL_CFG1_LEDS0)
1169433d6423SLionel Sambuc printf(" LED0");
1170433d6423SLionel Sambuc if (t & RL_CFG1_DVRLOAD)
1171433d6423SLionel Sambuc printf(" Driver");
1172433d6423SLionel Sambuc if (t & RL_CFG1_LWACT)
1173433d6423SLionel Sambuc printf(" LWAKE");
1174433d6423SLionel Sambuc if (t & RL_CFG1_IOMAP)
1175433d6423SLionel Sambuc printf(" IOMAP");
1176433d6423SLionel Sambuc if (t & RL_CFG1_MEMMAP)
1177433d6423SLionel Sambuc printf(" MEMMAP");
1178433d6423SLionel Sambuc if (t & RL_CFG1_VPD)
1179433d6423SLionel Sambuc printf(" VPD");
1180433d6423SLionel Sambuc if (t & RL_CFG1_PME)
1181433d6423SLionel Sambuc printf(" PME");
1182433d6423SLionel Sambuc printf("\n");
1183433d6423SLionel Sambuc
1184433d6423SLionel Sambuc t = rl_inb(port, RL_CONFIG2);
1185433d6423SLionel Sambuc printf("CONFIG2\t\t:");
1186433d6423SLionel Sambuc if (t & RL_CFG2_AUX)
1187433d6423SLionel Sambuc printf(" AUX");
1188433d6423SLionel Sambuc if (t & RL_CFG2_PCIBW)
1189433d6423SLionel Sambuc printf(" PCI-64-Bit");
1190433d6423SLionel Sambuc else
1191433d6423SLionel Sambuc printf(" PCI-32-Bit");
1192433d6423SLionel Sambuc t = t & RL_CFG2_PCICLK;
1193433d6423SLionel Sambuc if (t == RL_CFG2_66MHZ)
1194433d6423SLionel Sambuc printf(" 66 MHz");
1195433d6423SLionel Sambuc else if (t == RL_CFG2_33MHZ)
1196433d6423SLionel Sambuc printf(" 33 MHz");
1197433d6423SLionel Sambuc printf("\n");
1198433d6423SLionel Sambuc
1199433d6423SLionel Sambuc t = mdio_read(port, MII_CTRL);
1200433d6423SLionel Sambuc printf("MII_CTRL\t:");
1201433d6423SLionel Sambuc if (t & MII_CTRL_RST)
1202433d6423SLionel Sambuc printf(" Reset");
1203433d6423SLionel Sambuc if (t & MII_CTRL_LB)
1204433d6423SLionel Sambuc printf(" Loopback");
1205433d6423SLionel Sambuc if (t & MII_CTRL_ANE)
1206433d6423SLionel Sambuc printf(" ANE");
1207433d6423SLionel Sambuc if (t & MII_CTRL_PD)
1208433d6423SLionel Sambuc printf(" Power-down");
1209433d6423SLionel Sambuc if (t & MII_CTRL_ISO)
1210433d6423SLionel Sambuc printf(" Isolate");
1211433d6423SLionel Sambuc if (t & MII_CTRL_RAN)
1212433d6423SLionel Sambuc printf(" RAN");
1213433d6423SLionel Sambuc if (t & MII_CTRL_DM)
1214433d6423SLionel Sambuc printf(" Full-duplex");
1215433d6423SLionel Sambuc if (t & MII_CTRL_CT)
1216433d6423SLionel Sambuc printf(" COL-signal");
1217433d6423SLionel Sambuc t = t & (MII_CTRL_SP_LSB | MII_CTRL_SP_MSB);
1218433d6423SLionel Sambuc if (t == MII_CTRL_SP_10)
1219433d6423SLionel Sambuc printf(" 10 Mb/s");
1220433d6423SLionel Sambuc else if (t == MII_CTRL_SP_100)
1221433d6423SLionel Sambuc printf(" 100 Mb/s");
1222433d6423SLionel Sambuc else if (t == MII_CTRL_SP_1000)
1223433d6423SLionel Sambuc printf(" 1000 Mb/s");
1224433d6423SLionel Sambuc printf("\n");
1225433d6423SLionel Sambuc
1226433d6423SLionel Sambuc t = mdio_read(port, MII_STATUS);
1227433d6423SLionel Sambuc printf("MII_STATUS\t:");
1228433d6423SLionel Sambuc if (t & MII_STATUS_100T4)
1229433d6423SLionel Sambuc printf(" 100Base-T4");
1230433d6423SLionel Sambuc if (t & MII_STATUS_100XFD)
1231433d6423SLionel Sambuc printf(" 100BaseX-FD");
1232433d6423SLionel Sambuc if (t & MII_STATUS_100XHD)
1233433d6423SLionel Sambuc printf(" 100BaseX-HD");
1234433d6423SLionel Sambuc if (t & MII_STATUS_10FD)
1235433d6423SLionel Sambuc printf(" 10Mbps-FD");
1236433d6423SLionel Sambuc if (t & MII_STATUS_10HD)
1237433d6423SLionel Sambuc printf(" 10Mbps-HD");
1238433d6423SLionel Sambuc if (t & MII_STATUS_100T2FD)
1239433d6423SLionel Sambuc printf(" 100Base-T2-FD");
1240433d6423SLionel Sambuc if (t & MII_STATUS_100T2HD)
1241433d6423SLionel Sambuc printf(" 100Base-T2-HD");
1242433d6423SLionel Sambuc if (t & MII_STATUS_EXT_STAT)
1243433d6423SLionel Sambuc printf(" Ext-stat");
1244433d6423SLionel Sambuc if (t & MII_STATUS_RES)
1245433d6423SLionel Sambuc printf(" res-0x%x", t & MII_STATUS_RES);
1246433d6423SLionel Sambuc if (t & MII_STATUS_MFPS)
1247433d6423SLionel Sambuc printf(" MFPS");
1248433d6423SLionel Sambuc if (t & MII_STATUS_ANC)
1249433d6423SLionel Sambuc printf(" ANC");
1250433d6423SLionel Sambuc if (t & MII_STATUS_RF)
1251433d6423SLionel Sambuc printf(" remote-fault");
1252433d6423SLionel Sambuc if (t & MII_STATUS_ANA)
1253433d6423SLionel Sambuc printf(" ANA");
1254433d6423SLionel Sambuc if (t & MII_STATUS_LS)
1255433d6423SLionel Sambuc printf(" Link");
1256433d6423SLionel Sambuc if (t & MII_STATUS_JD)
1257433d6423SLionel Sambuc printf(" Jabber");
1258433d6423SLionel Sambuc if (t & MII_STATUS_EC)
1259433d6423SLionel Sambuc printf(" Extended-capability");
1260433d6423SLionel Sambuc printf("\n");
1261433d6423SLionel Sambuc
1262433d6423SLionel Sambuc t = mdio_read(port, MII_ANA);
1263433d6423SLionel Sambuc printf("MII_ANA\t\t: 0x%04x\n", t);
1264433d6423SLionel Sambuc
1265433d6423SLionel Sambuc t = mdio_read(port, MII_ANLPA);
1266433d6423SLionel Sambuc printf("MII_ANLPA\t: 0x%04x\n", t);
1267433d6423SLionel Sambuc
1268433d6423SLionel Sambuc t = mdio_read(port, MII_ANE);
1269433d6423SLionel Sambuc printf("MII_ANE\t\t:");
1270433d6423SLionel Sambuc if (t & MII_ANE_RES)
1271433d6423SLionel Sambuc printf(" res-0x%x", t & MII_ANE_RES);
1272433d6423SLionel Sambuc if (t & MII_ANE_PDF)
1273433d6423SLionel Sambuc printf(" Par-Detect-Fault");
1274433d6423SLionel Sambuc if (t & MII_ANE_LPNPA)
1275433d6423SLionel Sambuc printf(" LP-Next-Page-Able");
1276433d6423SLionel Sambuc if (t & MII_ANE_NPA)
1277433d6423SLionel Sambuc printf(" Loc-Next-Page-Able");
1278433d6423SLionel Sambuc if (t & MII_ANE_PR)
1279433d6423SLionel Sambuc printf(" Page-Received");
1280433d6423SLionel Sambuc if (t & MII_ANE_LPANA)
1281433d6423SLionel Sambuc printf(" LP-Auto-Neg-Able");
1282433d6423SLionel Sambuc printf("\n");
1283433d6423SLionel Sambuc
1284433d6423SLionel Sambuc t = mdio_read(port, MII_1000_CTRL);
1285433d6423SLionel Sambuc printf("MII_1000_CTRL\t:");
1286433d6423SLionel Sambuc if (t & MII_1000C_FULL)
1287433d6423SLionel Sambuc printf(" 1000BaseT-FD");
1288433d6423SLionel Sambuc if (t & MII_1000C_HALF)
1289433d6423SLionel Sambuc printf(" 1000BaseT-HD");
1290433d6423SLionel Sambuc printf("\n");
1291433d6423SLionel Sambuc
1292433d6423SLionel Sambuc t = mdio_read(port, MII_1000_STATUS);
1293433d6423SLionel Sambuc if (t) {
1294433d6423SLionel Sambuc printf("MII_1000_STATUS\t:");
1295433d6423SLionel Sambuc if (t & MII_1000S_LRXOK)
1296433d6423SLionel Sambuc printf(" Local-Receiver");
1297433d6423SLionel Sambuc if (t & MII_1000S_RRXOK)
1298433d6423SLionel Sambuc printf(" Remote-Receiver");
1299433d6423SLionel Sambuc if (t & MII_1000S_HALF)
1300433d6423SLionel Sambuc printf(" 1000BaseT-HD");
1301433d6423SLionel Sambuc if (t & MII_1000S_FULL)
1302433d6423SLionel Sambuc printf(" 1000BaseT-FD");
1303433d6423SLionel Sambuc printf("\n");
1304433d6423SLionel Sambuc
1305433d6423SLionel Sambuc t = mdio_read(port, MII_EXT_STATUS);
1306433d6423SLionel Sambuc printf("MII_EXT_STATUS\t:");
1307433d6423SLionel Sambuc if (t & MII_ESTAT_1000XFD)
1308433d6423SLionel Sambuc printf(" 1000BaseX-FD");
1309433d6423SLionel Sambuc if (t & MII_ESTAT_1000XHD)
1310433d6423SLionel Sambuc printf(" 1000BaseX-HD");
1311433d6423SLionel Sambuc if (t & MII_ESTAT_1000TFD)
1312433d6423SLionel Sambuc printf(" 1000BaseT-FD");
1313433d6423SLionel Sambuc if (t & MII_ESTAT_1000THD)
1314433d6423SLionel Sambuc printf(" 1000BaseT-HD");
1315433d6423SLionel Sambuc printf("\n");
1316433d6423SLionel Sambuc }
1317433d6423SLionel Sambuc }
1318d76bd1f0SDavid van Moolenbroek #endif
1319433d6423SLionel Sambuc
1320d76bd1f0SDavid van Moolenbroek /*===========================================================================*
1321d76bd1f0SDavid van Moolenbroek * rl_intr *
1322d76bd1f0SDavid van Moolenbroek *===========================================================================*/
rl_intr(unsigned int __unused mask)1323d76bd1f0SDavid van Moolenbroek static void rl_intr(unsigned int __unused mask)
1324433d6423SLionel Sambuc {
1325d76bd1f0SDavid van Moolenbroek re_t *rep;
1326433d6423SLionel Sambuc int s;
1327433d6423SLionel Sambuc
1328d76bd1f0SDavid van Moolenbroek rep = &re_state;
1329d76bd1f0SDavid van Moolenbroek
1330433d6423SLionel Sambuc /* Run interrupt handler at driver level. */
1331d76bd1f0SDavid van Moolenbroek rl_handler(rep);
1332433d6423SLionel Sambuc
1333433d6423SLionel Sambuc /* Reenable interrupts for this hook. */
1334d76bd1f0SDavid van Moolenbroek if ((s = sys_irqenable(&rep->re_hook_id)) != OK)
1335433d6423SLionel Sambuc printf("RTL8169: error, couldn't enable interrupts: %d\n", s);
1336d76bd1f0SDavid van Moolenbroek
1337d76bd1f0SDavid van Moolenbroek /* Perform tasks based on the flagged conditions. */
1338d76bd1f0SDavid van Moolenbroek rl_check_ints(rep);
1339433d6423SLionel Sambuc }
1340433d6423SLionel Sambuc
1341433d6423SLionel Sambuc /*===========================================================================*
1342433d6423SLionel Sambuc * rl_handler *
1343433d6423SLionel Sambuc *===========================================================================*/
rl_handler(re_t * rep)1344433d6423SLionel Sambuc static void rl_handler(re_t *rep)
1345433d6423SLionel Sambuc {
1346433d6423SLionel Sambuc int i, port, tx_head, tx_tail, link_up;
1347433d6423SLionel Sambuc u16_t isr;
1348433d6423SLionel Sambuc re_desc *desc;
1349433d6423SLionel Sambuc
1350433d6423SLionel Sambuc port = rep->re_base_port;
1351433d6423SLionel Sambuc
1352433d6423SLionel Sambuc /* Ack interrupt */
1353433d6423SLionel Sambuc isr = rl_inw(port, RL_ISR);
1354433d6423SLionel Sambuc if(!isr)
1355433d6423SLionel Sambuc return;
1356433d6423SLionel Sambuc rl_outw(port, RL_ISR, isr);
1357433d6423SLionel Sambuc rep->interrupts++;
1358433d6423SLionel Sambuc
1359433d6423SLionel Sambuc if (isr & RL_IMR_FOVW) {
1360433d6423SLionel Sambuc isr &= ~RL_IMR_FOVW;
1361433d6423SLionel Sambuc /* Should do anything? */
1362433d6423SLionel Sambuc }
1363433d6423SLionel Sambuc if (isr & RL_IMR_PUN) {
1364433d6423SLionel Sambuc isr &= ~RL_IMR_PUN;
1365433d6423SLionel Sambuc
1366433d6423SLionel Sambuc /*
1367433d6423SLionel Sambuc * Either the link status changed or there was a TX fifo
1368433d6423SLionel Sambuc * underrun.
1369433d6423SLionel Sambuc */
1370433d6423SLionel Sambuc link_up = !(!(rl_inb(port, RL_PHYSTAT) & RL_STAT_LINK));
1371433d6423SLionel Sambuc if (link_up != rep->re_link_up) {
1372433d6423SLionel Sambuc rep->re_report_link = TRUE;
1373433d6423SLionel Sambuc rep->re_got_int = TRUE;
1374433d6423SLionel Sambuc }
1375433d6423SLionel Sambuc }
1376433d6423SLionel Sambuc
1377433d6423SLionel Sambuc if (isr & (RL_ISR_RDU | RL_ISR_RER | RL_ISR_ROK)) {
1378433d6423SLionel Sambuc if (isr & RL_ISR_RER)
1379*f7df02e7SDavid van Moolenbroek netdriver_stat_ierror(1);
1380433d6423SLionel Sambuc isr &= ~(RL_ISR_RDU | RL_ISR_RER | RL_ISR_ROK);
1381433d6423SLionel Sambuc
1382433d6423SLionel Sambuc rep->re_got_int = TRUE;
1383433d6423SLionel Sambuc }
1384433d6423SLionel Sambuc
1385433d6423SLionel Sambuc if ((isr & (RL_ISR_TDU | RL_ISR_TER | RL_ISR_TOK)) || 1) {
1386433d6423SLionel Sambuc if (isr & RL_ISR_TER)
1387*f7df02e7SDavid van Moolenbroek netdriver_stat_oerror(1);
1388433d6423SLionel Sambuc isr &= ~(RL_ISR_TDU | RL_ISR_TER | RL_ISR_TOK);
1389433d6423SLionel Sambuc
1390433d6423SLionel Sambuc /* Transmit completed */
1391433d6423SLionel Sambuc tx_head = rep->re_tx_head;
1392433d6423SLionel Sambuc tx_tail = tx_head+1;
1393433d6423SLionel Sambuc if (tx_tail >= N_TX_DESC)
1394433d6423SLionel Sambuc tx_tail = 0;
1395433d6423SLionel Sambuc for (i = 0; i < 2 * N_TX_DESC; i++) {
1396433d6423SLionel Sambuc if (!rep->re_tx[tx_tail].ret_busy) {
1397433d6423SLionel Sambuc /* Strange, this buffer is not in-use.
1398433d6423SLionel Sambuc * Increment tx_tail until tx_head is
1399433d6423SLionel Sambuc * reached (or until we find a buffer that
1400433d6423SLionel Sambuc * is in-use.
1401433d6423SLionel Sambuc */
1402433d6423SLionel Sambuc if (tx_tail == tx_head)
1403433d6423SLionel Sambuc break;
1404433d6423SLionel Sambuc if (++tx_tail >= N_TX_DESC)
1405433d6423SLionel Sambuc tx_tail = 0;
1406433d6423SLionel Sambuc assert(tx_tail < N_TX_DESC);
1407433d6423SLionel Sambuc continue;
1408433d6423SLionel Sambuc }
1409433d6423SLionel Sambuc desc = rep->re_tx_desc;
1410433d6423SLionel Sambuc desc += tx_tail;
1411433d6423SLionel Sambuc if (desc->status & DESC_OWN) {
1412433d6423SLionel Sambuc /* Buffer is not yet ready */
1413433d6423SLionel Sambuc break;
1414433d6423SLionel Sambuc }
1415433d6423SLionel Sambuc
1416433d6423SLionel Sambuc rep->re_tx[tx_tail].ret_busy = FALSE;
1417d76bd1f0SDavid van Moolenbroek rep->re_tx_busy--;
1418433d6423SLionel Sambuc
1419433d6423SLionel Sambuc if (++tx_tail >= N_TX_DESC)
1420433d6423SLionel Sambuc tx_tail = 0;
1421433d6423SLionel Sambuc assert(tx_tail < N_TX_DESC);
1422433d6423SLionel Sambuc
1423433d6423SLionel Sambuc rep->re_send_int = TRUE;
1424433d6423SLionel Sambuc rep->re_got_int = TRUE;
1425d76bd1f0SDavid van Moolenbroek rep->re_tx_alive = TRUE;
1426433d6423SLionel Sambuc }
1427433d6423SLionel Sambuc assert(i < 2 * N_TX_DESC);
1428433d6423SLionel Sambuc }
1429433d6423SLionel Sambuc
1430433d6423SLionel Sambuc /* Ignore Reserved Interrupt */
1431433d6423SLionel Sambuc if (isr & RL_ISR_RES)
1432433d6423SLionel Sambuc isr &= ~RL_ISR_RES;
1433433d6423SLionel Sambuc
1434433d6423SLionel Sambuc if (isr)
1435433d6423SLionel Sambuc printf("rl_handler: unhandled interrupt isr = 0x%04x\n", isr);
1436433d6423SLionel Sambuc }
1437433d6423SLionel Sambuc
1438433d6423SLionel Sambuc /*===========================================================================*
1439*f7df02e7SDavid van Moolenbroek * rl_tick *
1440433d6423SLionel Sambuc *===========================================================================*/
rl_tick(void)1441*f7df02e7SDavid van Moolenbroek static void rl_tick(void)
1442433d6423SLionel Sambuc {
1443433d6423SLionel Sambuc re_t *rep;
1444d76bd1f0SDavid van Moolenbroek
1445433d6423SLionel Sambuc rep = &re_state;
1446433d6423SLionel Sambuc
1447433d6423SLionel Sambuc /* Should collect statistics */
1448433d6423SLionel Sambuc if (!(++rep->dtcc_counter % RE_DTCC_VALUE))
1449433d6423SLionel Sambuc rtl8169_update_stat(rep);
1450433d6423SLionel Sambuc
1451d76bd1f0SDavid van Moolenbroek assert(rep->re_tx_busy >= 0 && rep->re_tx_busy <= N_TX_DESC);
1452d76bd1f0SDavid van Moolenbroek if (rep->re_tx_busy == 0) {
1453433d6423SLionel Sambuc /* Assume that an idle system is alive */
1454433d6423SLionel Sambuc rep->re_tx_alive = TRUE;
1455433d6423SLionel Sambuc return;
1456433d6423SLionel Sambuc }
1457433d6423SLionel Sambuc if (rep->re_tx_alive) {
1458433d6423SLionel Sambuc rep->re_tx_alive = FALSE;
1459433d6423SLionel Sambuc return;
1460433d6423SLionel Sambuc }
1461*f7df02e7SDavid van Moolenbroek printf("%s: TX timeout, resetting\n", netdriver_name());
1462433d6423SLionel Sambuc printf("tx_head :%8d busy %d\t",
1463433d6423SLionel Sambuc rep->re_tx_head, rep->re_tx[rep->re_tx_head].ret_busy);
1464433d6423SLionel Sambuc rep->re_need_reset = TRUE;
1465433d6423SLionel Sambuc rep->re_got_int = TRUE;
1466433d6423SLionel Sambuc
1467d76bd1f0SDavid van Moolenbroek rl_check_ints(rep);
1468433d6423SLionel Sambuc }
1469