/* * rtl8139.c * * This file contains a ethernet device driver for Realtek rtl8139 based * ethernet cards. * * Created: Aug 2003 by Philip Homburg * Changes: * Aug 15, 2004 sync alarms replace watchdogs timers (Jorrit N. Herder) * May 02, 2004 flag alarms replace micro_elapsed() (Jorrit N. Herder) * */ #define VERBOSE 0 /* Verbose debugging output */ #define RTL8139_FKEY 0 /* Use function key to dump RTL8139 status */ #include "rtl8139.h" re_t re_state; static int re_instance; static unsigned my_inb(u16_t port) { u32_t value; int s; if ((s=sys_inb(port, &value)) !=OK) printf("RTL8139: warning, sys_inb failed: %d\n", s); return value; } static unsigned my_inw(u16_t port) { u32_t value; int s; if ((s=sys_inw(port, &value)) !=OK) printf("RTL8139: warning, sys_inw failed: %d\n", s); return value; } static unsigned my_inl(u16_t port) { u32_t value; int s; if ((s=sys_inl(port, &value)) !=OK) printf("RTL8139: warning, sys_inl failed: %d\n", s); return value; } #define rl_inb(port, offset) (my_inb((port) + (offset))) #define rl_inw(port, offset) (my_inw((port) + (offset))) #define rl_inl(port, offset) (my_inl((port) + (offset))) static void my_outb(u16_t port, u8_t value) { int s; if ((s=sys_outb(port, value)) !=OK) printf("RTL8139: warning, sys_outb failed: %d\n", s); } static void my_outw(u16_t port, u16_t value) { int s; if ((s=sys_outw(port, value)) !=OK) printf("RTL8139: warning, sys_outw failed: %d\n", s); } static void my_outl(u16_t port, u32_t value) { int s; if ((s=sys_outl(port, value)) !=OK) printf("RTL8139: warning, sys_outl failed: %d\n", s); } #define rl_outb(port, offset, value) (my_outb((port) + (offset), (value))) #define rl_outw(port, offset, value) (my_outw((port) + (offset), (value))) #define rl_outl(port, offset, value) (my_outl((port) + (offset), (value))) static void rl_init(message *mp); static void rl_pci_conf(void); static int rl_probe(re_t *rep, int skip); static void rl_conf_hw(re_t *rep); static void rl_init_buf(re_t *rep); static void rl_init_hw(re_t *rep); static void rl_reset_hw(re_t *rep); static void rl_confaddr(re_t *rep); static void rl_rec_mode(re_t *rep); static void rl_readv_s(const message *mp, int from_int); static void rl_writev_s(const message *mp, int from_int); static void rl_check_ints(re_t *rep); static void rl_report_link(re_t *rep); static void mii_print_techab(u16_t techab); static void mii_print_stat_speed(u16_t stat, u16_t extstat); static void rl_clear_rx(re_t *rep); static void rl_do_reset(re_t *rep); static void rl_getstat_s(message *mp); static void reply(re_t *rep); static void mess_reply(message *req, message *reply); static void check_int_events(void); static void do_hard_int(void); static void rtl8139_dump(message *m); #if 0 static void dump_phy(re_t *rep); #endif static int rl_handler(re_t *rep); static void rl_watchdog_f(timer_t *tp); static void tell_dev(vir_bytes start, size_t size, int pci_bus, int pci_dev, int pci_func); /* The message used in the main loop is made global, so that rl_watchdog_f() * can change its message type to fake an interrupt message. */ static message m; static int int_event_check; /* set to TRUE if events arrived */ static u32_t system_hz; /* SEF functions and variables. */ static void sef_local_startup(void); static int sef_cb_init_fresh(int type, sef_init_info_t *info); static void sef_cb_signal_handler(int signo); EXTERN int sef_cb_lu_prepare(int state); EXTERN int sef_cb_lu_state_isvalid(int state); EXTERN void sef_cb_lu_state_dump(int state); /*===========================================================================* * main * *===========================================================================*/ int main(int argc, char *argv[]) { int r; int ipc_status; /* SEF local startup. */ env_setargs(argc, argv); sef_local_startup(); while (TRUE) { if ((r= netdriver_receive(ANY, &m, &ipc_status)) != OK) panic("netdriver_receive failed: %d", r); if (is_ipc_notify(ipc_status)) { switch (_ENDPOINT_P(m.m_source)) { case CLOCK: /* * Under MINIX, synchronous alarms are * used instead of watchdog functions. * The approach is very different: MINIX * VMD timeouts are handled within the * kernel (the watchdog is executed by * CLOCK), and notify() the driver in * some cases. MINIX timeouts result in * a SYN_ALARM message to the driver and * thus are handled where they should be * handled. Locally, watchdog functions * are used again. */ rl_watchdog_f(NULL); break; case HARDWARE: do_hard_int(); if (int_event_check) check_int_events(); break ; case TTY_PROC_NR: rtl8139_dump(&m); break; default: panic("illegal notify from: %d", m.m_source); } /* done, get nwe message */ continue; } switch (m.m_type) { case DL_WRITEV_S: rl_writev_s(&m, FALSE); break; case DL_READV_S: rl_readv_s(&m, FALSE); break; case DL_CONF: rl_init(&m); break; case DL_GETSTAT_S: rl_getstat_s(&m); break; default: panic("illegal message: %d", m.m_type); } } } /*===========================================================================* * sef_local_startup * *===========================================================================*/ static void sef_local_startup() { /* Register init callbacks. */ sef_setcb_init_fresh(sef_cb_init_fresh); sef_setcb_init_lu(sef_cb_init_fresh); sef_setcb_init_restart(sef_cb_init_fresh); /* Register live update callbacks. */ sef_setcb_lu_prepare(sef_cb_lu_prepare); sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid); sef_setcb_lu_state_dump(sef_cb_lu_state_dump); /* Register signal callbacks. */ sef_setcb_signal_handler(sef_cb_signal_handler); /* Let SEF perform startup. */ sef_startup(); } /*===========================================================================* * sef_cb_init_fresh * *===========================================================================*/ static int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info)) { /* Initialize the rtl8139 driver. */ long v; #if RTL8139_FKEY int r, fkeys, sfkeys; #endif system_hz = sys_hz(); v = 0; (void) env_parse("instance", "d", 0, &v, 0, 255); re_instance = (int) v; #if RTL8139_FKEY /* Observe some function key for debug dumps. */ fkeys = sfkeys = 0; bit_set(sfkeys, 9); if ((r=fkey_map(&fkeys, &sfkeys)) != OK) printf("Warning: RTL8139 couldn't observe Shift+F9 key: %d\n",r); #endif /* Claim buffer memory now. */ rl_init_buf(&re_state); /* Announce we are up! */ netdriver_announce(); return(OK); } /*===========================================================================* * sef_cb_signal_handler * *===========================================================================*/ static void sef_cb_signal_handler(int signo) { re_t *rep; /* Only check for termination signal, ignore anything else. */ if (signo != SIGTERM) return; rep = &re_state; if (rep->re_mode == REM_ENABLED) rl_outb(rep->re_base_port, RL_CR, 0); exit(0); } /*===========================================================================* * check_int_events * *===========================================================================*/ static void check_int_events(void) { re_t *rep; rep= &re_state; if (rep->re_mode != REM_ENABLED) return; if (!rep->re_got_int) return; rep->re_got_int= 0; assert(rep->re_flags & REF_ENABLED); rl_check_ints(rep); } /*===========================================================================* * rtl8139_dump * *===========================================================================*/ static void rtl8139_dump(m) message *m; /* pointer to request message */ { re_t *rep; rep= &re_state; printf("\n"); if (rep->re_mode == REM_DISABLED) printf("Realtek RTL 8139 instance %d is disabled\n", re_instance); if (rep->re_mode != REM_ENABLED) return; printf("Realtek RTL 8139 statistics of instance %d:\n", re_instance); printf("recvErr :%8ld\t", rep->re_stat.ets_recvErr); printf("sendErr :%8ld\t", rep->re_stat.ets_sendErr); printf("OVW :%8ld\n", rep->re_stat.ets_OVW); printf("CRCerr :%8ld\t", rep->re_stat.ets_CRCerr); printf("frameAll :%8ld\t", rep->re_stat.ets_frameAll); printf("missedP :%8ld\n", rep->re_stat.ets_missedP); printf("packetR :%8ld\t", rep->re_stat.ets_packetR); printf("packetT :%8ld\t", rep->re_stat.ets_packetT); printf("transDef :%8ld\n", rep->re_stat.ets_transDef); printf("collision :%8ld\t", rep->re_stat.ets_collision); printf("transAb :%8ld\t", rep->re_stat.ets_transAb); printf("carrSense :%8ld\n", rep->re_stat.ets_carrSense); printf("fifoUnder :%8ld\t", rep->re_stat.ets_fifoUnder); printf("fifoOver :%8ld\t", rep->re_stat.ets_fifoOver); printf("CDheartbeat:%8ld\n", rep->re_stat.ets_CDheartbeat); printf("OWC :%8ld\t", rep->re_stat.ets_OWC); printf("re_flags = 0x%x\n", rep->re_flags); printf("TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", rl_inw(rep->re_base_port, RL_TSAD), rl_inl(rep->re_base_port, RL_TSD0+0*4), rl_inl(rep->re_base_port, RL_TSD0+1*4), rl_inl(rep->re_base_port, RL_TSD0+2*4), rl_inl(rep->re_base_port, RL_TSD0+3*4)); printf("tx_head %d, tx_tail %d, busy: %d %d %d %d\n", rep->re_tx_head, rep->re_tx_tail, rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); } /*===========================================================================* * rl_init * *===========================================================================*/ static void rl_init(mp) message *mp; { static int first_time= 1; re_t *rep; message reply_mess; if (first_time) { first_time= 0; rl_pci_conf(); /* Configure PCI devices. */ /* Use a synchronous alarm instead of a watchdog timer. */ sys_setalarm(system_hz, 0); } rep= &re_state; if (rep->re_mode == REM_DISABLED) { /* This is the default, try to (re)locate the device. */ rl_conf_hw(rep); if (rep->re_mode == REM_DISABLED) { /* Probe failed, or the device is configured off. */ reply_mess.m_type= DL_CONF_REPLY; reply_mess.DL_STAT= ENXIO; mess_reply(mp, &reply_mess); return; } if (rep->re_mode == REM_ENABLED) rl_init_hw(rep); #if VERBOSE /* load silently ... can always check status later */ rl_report_link(rep); #endif } assert(rep->re_mode == REM_ENABLED); assert(rep->re_flags & REF_ENABLED); rep->re_flags &= ~(REF_PROMISC | REF_MULTI | REF_BROAD); if (mp->DL_MODE & DL_PROMISC_REQ) rep->re_flags |= REF_PROMISC; if (mp->DL_MODE & DL_MULTI_REQ) rep->re_flags |= REF_MULTI; if (mp->DL_MODE & DL_BROAD_REQ) rep->re_flags |= REF_BROAD; rl_rec_mode(rep); reply_mess.m_type = DL_CONF_REPLY; reply_mess.DL_STAT = OK; *(ether_addr_t *) reply_mess.DL_HWADDR = rep->re_address; mess_reply(mp, &reply_mess); } /*===========================================================================* * rl_pci_conf * *===========================================================================*/ static void rl_pci_conf() { re_t *rep; rep= &re_state; strcpy(rep->re_name, "rtl8139#0"); rep->re_name[8] += re_instance; rep->re_seen= FALSE; pci_init(); if (rl_probe(rep, re_instance)) rep->re_seen= TRUE; } /*===========================================================================* * rl_probe * *===========================================================================*/ static int rl_probe(rep, skip) re_t *rep; int skip; { int r, devind; u16_t vid, did; u32_t bar; u8_t ilr; #if VERBOSE char *dname; #endif r= pci_first_dev(&devind, &vid, &did); if (r == 0) return 0; while (skip--) { r= pci_next_dev(&devind, &vid, &did); if (!r) return 0; } #if VERBOSE /* stay silent at startup, can always get status later */ dname= pci_dev_name(vid, did); if (!dname) dname= "unknown device"; printf("%s: ", rep->re_name); printf("%s (%x/%x) at %s\n", dname, vid, did, pci_slot_name(devind)); #endif pci_reserve(devind); /* printf("cr = 0x%x\n", pci_attr_r16(devind, PCI_CR)); */ bar= pci_attr_r32(devind, PCI_BAR) & 0xffffffe0; if (bar < 0x400) { panic("base address is not properly configured"); } rep->re_base_port= bar; ilr= pci_attr_r8(devind, PCI_ILR); rep->re_irq= ilr; if (debug) { printf("%s: using I/O address 0x%lx, IRQ %d\n", rep->re_name, (unsigned long)bar, ilr); } return TRUE; } /*===========================================================================* * rl_conf_hw * *===========================================================================*/ static void rl_conf_hw(rep) re_t *rep; { static eth_stat_t empty_stat = {0, 0, 0, 0, 0, 0 /* ,... */ }; rep->re_mode= REM_DISABLED; /* Superfluous */ if (rep->re_seen) { /* PCI device is present */ rep->re_mode= REM_ENABLED; } if (rep->re_mode != REM_ENABLED) return; rep->re_flags= REF_EMPTY; rep->re_link_up= -1; /* Unknown */ rep->re_got_int= 0; rep->re_send_int= 0; rep->re_report_link= 0; rep->re_clear_rx= 0; rep->re_need_reset= 0; rep->re_tx_alive= 0; rep->re_read_s= 0; rep->re_tx_head= 0; rep->re_tx_tail= 0; rep->re_ertxth= RL_TSD_ERTXTH_8; rep->re_stat= empty_stat; } /*===========================================================================* * rl_init_buf * *===========================================================================*/ static void rl_init_buf(rep) re_t *rep; { size_t rx_bufsize, tx_bufsize, tot_bufsize; phys_bytes buf; char *mallocbuf; int i, off; /* Allocate receive and transmit buffers */ tx_bufsize= ETH_MAX_PACK_SIZE_TAGGED; if (tx_bufsize % 4) tx_bufsize += 4-(tx_bufsize % 4); /* Align */ rx_bufsize= RX_BUFSIZE; tot_bufsize= N_TX_BUF*tx_bufsize + rx_bufsize; if (tot_bufsize % 4096) tot_bufsize += 4096-(tot_bufsize % 4096); #define BUF_ALIGNMENT (64*1024) if(!(mallocbuf = alloc_contig(BUF_ALIGNMENT + tot_bufsize, 0, &buf))) { panic("Couldn't allocate kernel buffer"); } /* click-align mallocced buffer. this is what we used to get * from kmalloc() too. */ if((off = buf % BUF_ALIGNMENT)) { mallocbuf += BUF_ALIGNMENT - off; buf += BUF_ALIGNMENT - off; } tell_dev((vir_bytes)mallocbuf, tot_bufsize, 0, 0, 0); for (i= 0; ire_tx[i].ret_buf= buf; rep->re_tx[i].v_ret_buf= mallocbuf; buf += tx_bufsize; mallocbuf += tx_bufsize; } rep->re_rx_buf= buf; rep->v_re_rx_buf= mallocbuf; } /*===========================================================================* * rl_init_hw * *===========================================================================*/ static void rl_init_hw(rep) re_t *rep; { int s, i; rep->re_flags = REF_EMPTY; rep->re_flags |= REF_ENABLED; /* Set the interrupt handler. The policy is to only send HARD_INT * notifications. Don't reenable interrupts automatically. The id * that is passed back is the interrupt line number. */ rep->re_hook_id = rep->re_irq; if ((s=sys_irqsetpolicy(rep->re_irq, 0, &rep->re_hook_id)) != OK) printf("RTL8139: error, couldn't set IRQ policy: %d\n", s); rl_reset_hw(rep); if ((s=sys_irqenable(&rep->re_hook_id)) != OK) printf("RTL8139: error, couldn't enable interrupts: %d\n", s); #if VERBOSE /* stay silent during startup, can always get status later */ if (rep->re_model) { printf("%s: model %s\n", rep->re_name, rep->re_model); } else { printf("%s: unknown model 0x%08x\n", rep->re_name, rl_inl(rep->re_base_port, RL_TCR) & (RL_TCR_HWVER_AM | RL_TCR_HWVER_BM)); } #endif rl_confaddr(rep); if (debug) { printf("%s: Ethernet address ", rep->re_name); for (i= 0; i < 6; i++) { printf("%x%c", rep->re_address.ea_addr[i], i < 5 ? ':' : '\n'); } } } /*===========================================================================* * rl_reset_hw * *===========================================================================*/ static void rl_reset_hw(rep) re_t *rep; { port_t port; u32_t t; phys_bytes bus_buf; int i; port= rep->re_base_port; #if 0 /* Reset the PHY */ rl_outb(port, RL_BMCR, MII_CTRL_RST); SPIN_UNTIL(!(rl_inb(port, RL_BMCR) & MII_CTRL_RST), 1000000); if (rl_inb(port, RL_BMCR) & MII_CTRL_RST) panic("reset PHY failed to complete"); #endif /* Reset the device */ #if VERBOSE printf("rl_reset_hw: (before reset) port = 0x%x, RL_CR = 0x%x\n", port, rl_inb(port, RL_CR)); #endif rl_outb(port, RL_CR, RL_CR_RST); SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_RST), 1000000); #if VERBOSE printf("rl_reset_hw: (after reset) port = 0x%x, RL_CR = 0x%x\n", port, rl_inb(port, RL_CR)); #endif if (rl_inb(port, RL_CR) & RL_CR_RST) printf("rtl8139: reset failed to complete"); t= rl_inl(port, RL_TCR); switch(t & (RL_TCR_HWVER_AM | RL_TCR_HWVER_BM)) { case RL_TCR_HWVER_RTL8139: rep->re_model= "RTL8139"; break; case RL_TCR_HWVER_RTL8139A: rep->re_model= "RTL8139A"; break; case RL_TCR_HWVER_RTL8139AG: rep->re_model= "RTL8139A-G / RTL8139C"; break; case RL_TCR_HWVER_RTL8139B: rep->re_model= "RTL8139B / RTL8130"; break; case RL_TCR_HWVER_RTL8100: rep->re_model= "RTL8100"; break; case RL_TCR_HWVER_RTL8100B: rep->re_model= "RTL8100B/RTL8139D"; break; case RL_TCR_HWVER_RTL8139CP: rep->re_model= "RTL8139C+"; break; case RL_TCR_HWVER_RTL8101: rep->re_model= "RTL8101"; break; default: rep->re_model= NULL; break; } #if 0 printf("REVID: 0x%02x\n", rl_inb(port, RL_REVID)); #endif /* Intialize Rx */ /* Should init multicast mask */ #if 0 08-0f R/W MAR[0-7] multicast #endif bus_buf= vm_1phys2bus(rep->re_rx_buf); rl_outl(port, RL_RBSTART, bus_buf); /* Initialize Tx */ for (i= 0; ire_tx[i].ret_busy= FALSE; bus_buf= vm_1phys2bus(rep->re_tx[i].ret_buf); rl_outl(port, RL_TSAD0+i*4, bus_buf); t= rl_inl(port, RL_TSD0+i*4); assert(t & RL_TSD_OWN); } #if 0 dump_phy(rep); #endif t= rl_inw(port, RL_IMR); rl_outw(port, RL_IMR, t | (RL_IMR_SERR | RL_IMR_TIMEOUT | RL_IMR_LENCHG)); t= rl_inw(port, RL_IMR); rl_outw(port, RL_IMR, t | (RL_IMR_FOVW | RL_IMR_PUN | RL_IMR_RXOVW | RL_IMR_RER | RL_IMR_ROK)); t= rl_inw(port, RL_IMR); rl_outw(port, RL_IMR, t | (RL_IMR_TER | RL_IMR_TOK)); t= rl_inb(port, RL_CR); rl_outb(port, RL_CR, t | RL_CR_RE); t= rl_inb(port, RL_CR); rl_outb(port, RL_CR, t | RL_CR_TE); rl_outl(port, RL_RCR, RX_BUFBITS); t= rl_inl(port, RL_TCR); rl_outl(port, RL_TCR, t | RL_TCR_IFG_STD); } /*===========================================================================* * rl_confaddr * *===========================================================================*/ static void rl_confaddr(rep) re_t *rep; { static char eakey[]= RL_ENVVAR "#_EA"; static char eafmt[]= "x:x:x:x:x:x"; int i; port_t port; u32_t w; long v; /* User defined ethernet address? */ eakey[sizeof(RL_ENVVAR)-1]= '0' + re_instance; port= rep->re_base_port; for (i= 0; i < 6; i++) { if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET) break; rep->re_address.ea_addr[i]= v; } if (i != 0 && i != 6) env_panic(eakey); /* It's all or nothing */ /* Should update ethernet address in hardware */ if (i == 6) { port= rep->re_base_port; rl_outb(port, RL_9346CR, RL_9346CR_EEM_CONFIG); w= 0; for (i= 0; i<4; i++) w |= (rep->re_address.ea_addr[i] << (i*8)); rl_outl(port, RL_IDR, w); w= 0; for (i= 4; i<6; i++) w |= (rep->re_address.ea_addr[i] << ((i-4)*8)); rl_outl(port, RL_IDR+4, w); rl_outb(port, RL_9346CR, RL_9346CR_EEM_NORMAL); } /* Get ethernet address */ for (i= 0; i<6; i++) rep->re_address.ea_addr[i]= rl_inb(port, RL_IDR+i); } /*===========================================================================* * rl_rec_mode * *===========================================================================*/ static void rl_rec_mode(rep) re_t *rep; { port_t port; u32_t rcr; port= rep->re_base_port; rcr= rl_inl(port, RL_RCR); rcr &= ~(RL_RCR_AB|RL_RCR_AM|RL_RCR_APM|RL_RCR_AAP); if (rep->re_flags & REF_PROMISC) rcr |= RL_RCR_AB | RL_RCR_AM | RL_RCR_AAP; if (rep->re_flags & REF_BROAD) rcr |= RL_RCR_AB; if (rep->re_flags & REF_MULTI) rcr |= RL_RCR_AM; rcr |= RL_RCR_APM; rl_outl(port, RL_RCR, rcr); } /*===========================================================================* * rl_readv_s * *===========================================================================*/ static void rl_readv_s(const message *mp, int from_int) { int i, j, n, o, s, s1, count, size; port_t port; unsigned amount, totlen, packlen; u16_t d_start, d_end; u32_t l, rxstat = 0x12345678; re_t *rep; iovec_s_t *iovp; int cps; int iov_offset = 0; rep= &re_state; rep->re_client= mp->m_source; count = mp->DL_COUNT; if (rep->re_clear_rx) goto suspend; /* Buffer overflow */ assert(rep->re_mode == REM_ENABLED); assert(rep->re_flags & REF_ENABLED); port= rep->re_base_port; /* Assume that the RL_CR_BUFE check was been done by rl_checks_ints */ if (!from_int && (rl_inb(port, RL_CR) & RL_CR_BUFE)) { /* Receive buffer is empty, suspend */ goto suspend; } d_start= rl_inw(port, RL_CAPR) + RL_CAPR_DATA_OFF; d_end= rl_inw(port, RL_CBR) % RX_BUFSIZE; if (d_start >= RX_BUFSIZE) { printf("rl_readv: strange value in RL_CAPR: 0x%x\n", rl_inw(port, RL_CAPR)); d_start %= RX_BUFSIZE; } if (d_end > d_start) amount= d_end-d_start; else amount= d_end+RX_BUFSIZE - d_start; rxstat = *(u32_t *) (rep->v_re_rx_buf + d_start); if (rep->re_clear_rx) { #if 0 printf("rl_readv: late buffer overflow\n"); #endif goto suspend; /* Buffer overflow */ } /* Should convert from little endian to host byte order */ if (!(rxstat & RL_RXS_ROK)) { printf("rxstat = 0x%08x\n", rxstat); printf("d_start: 0x%x, d_end: 0x%x, rxstat: 0x%x\n", d_start, d_end, rxstat); panic("received packet not OK"); } totlen= (rxstat >> RL_RXS_LEN_S); if (totlen < 8 || totlen > 2*ETH_MAX_PACK_SIZE) { /* Someting went wrong */ printf( "rl_readv: bad length (%u) in status 0x%08x at offset 0x%x\n", totlen, rxstat, d_start); printf( "d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%x\n", d_start, d_end, totlen, rxstat); panic(NULL); } #if 0 printf("d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%x\n", d_start, d_end, totlen, rxstat); #endif if (totlen+4 > amount) { printf("rl_readv: packet not yet ready\n"); goto suspend; } /* Should subtract the CRC */ packlen= totlen - ETH_CRC_SIZE; size= 0; o= d_start+4; for (i= 0; ire_iovec_s[0])) { n= IOVEC_NR; if (i+n > count) n= count-i; cps = sys_safecopyfrom(mp->m_source, mp->DL_GRANT, iov_offset, (vir_bytes) rep->re_iovec_s, n * sizeof(rep->re_iovec_s[0])); if (cps != OK) { panic("rl_readv_s: sys_safecopyfrom failed: %d", cps); } for (j= 0, iovp= rep->re_iovec_s; jiov_size; if (size + s > packlen) { assert(packlen > size); s= packlen-size; } #if 0 if (sys_umap(mp->m_source, D, iovp->iov_addr, s, &dst_phys) != OK) panic("umap_local failed"); #endif if (o >= RX_BUFSIZE) { o -= RX_BUFSIZE; assert(o < RX_BUFSIZE); } if (o+s > RX_BUFSIZE) { assert(om_source, iovp->iov_grant, 0, (vir_bytes) rep->v_re_rx_buf+o, s1); if (cps != OK) { panic("rl_readv_s: sys_safecopyto failed: %d", cps); } cps = sys_safecopyto(mp->m_source, iovp->iov_grant, s1, (vir_bytes) rep->v_re_rx_buf, s-s1); if (cps != OK) { panic("rl_readv_s: sys_safecopyto failed: %d", cps); } } else { cps = sys_safecopyto(mp->m_source, iovp->iov_grant, 0, (vir_bytes) rep->v_re_rx_buf+o, s); if (cps != OK) panic("rl_readv_s: sys_safecopyto failed: %d", cps); } size += s; if (size == packlen) break; o += s; } if (size == packlen) break; } if (size < packlen) { assert(0); } if (rep->re_clear_rx) { /* For some reason the receiver FIFO is not stopped when * the buffer is full. */ #if 0 printf("rl_readv: later buffer overflow\n"); #endif goto suspend; /* Buffer overflow */ } rep->re_stat.ets_packetR++; rep->re_read_s= packlen; rep->re_flags= (rep->re_flags & ~REF_READING) | REF_PACK_RECV; /* Avoid overflow in 16-bit computations */ l= d_start; l += totlen+4; l= (l+3) & ~3; /* align */ if (l >= RX_BUFSIZE) { l -= RX_BUFSIZE; assert(l < RX_BUFSIZE); } rl_outw(port, RL_CAPR, l-RL_CAPR_DATA_OFF); if (!from_int) reply(rep); return; suspend: if (from_int) { assert(rep->re_flags & REF_READING); /* No need to store any state */ return; } rep->re_rx_mess= *mp; assert(!(rep->re_flags & REF_READING)); rep->re_flags |= REF_READING; reply(rep); } /*===========================================================================* * rl_writev_s * *===========================================================================*/ static void rl_writev_s(const message *mp, int from_int) { int i, j, n, s, count, size; int tx_head; re_t *rep; iovec_s_t *iovp; char *ret; int cps; int iov_offset = 0; rep= &re_state; rep->re_client= mp->m_source; count = mp->DL_COUNT; assert(rep->re_mode == REM_ENABLED); assert(rep->re_flags & REF_ENABLED); if (from_int) { assert(rep->re_flags & REF_SEND_AVAIL); rep->re_flags &= ~REF_SEND_AVAIL; rep->re_send_int= FALSE; rep->re_tx_alive= TRUE; } tx_head= rep->re_tx_head; if (rep->re_tx[tx_head].ret_busy) { assert(!(rep->re_flags & REF_SEND_AVAIL)); rep->re_flags |= REF_SEND_AVAIL; goto suspend; } assert(!(rep->re_flags & REF_SEND_AVAIL)); assert(!(rep->re_flags & REF_PACK_SENT)); size= 0; ret = rep->re_tx[tx_head].v_ret_buf; for (i= 0; ire_iovec_s[0])) { n= IOVEC_NR; if (i+n > count) n= count-i; cps = sys_safecopyfrom(mp->m_source, mp->DL_GRANT, iov_offset, (vir_bytes) rep->re_iovec_s, n * sizeof(rep->re_iovec_s[0])); if (cps != OK) { panic("rl_writev_s: sys_safecopyfrom failed: %d", cps); } for (j= 0, iovp= rep->re_iovec_s; jiov_size; if (size + s > ETH_MAX_PACK_SIZE_TAGGED) { panic("invalid packet size"); } cps = sys_safecopyfrom(mp->m_source, iovp->iov_grant, 0, (vir_bytes) ret, s); if (cps != OK) { panic("rl_writev_s: sys_safecopyfrom failed: %d", cps); } size += s; ret += s; } } if (size < ETH_MIN_PACK_SIZE) panic("invalid packet size: %d", size); rl_outl(rep->re_base_port, RL_TSD0+tx_head*4, rep->re_ertxth | size); rep->re_tx[tx_head].ret_busy= TRUE; if (++tx_head == N_TX_BUF) tx_head= 0; assert(tx_head < RL_N_TX); rep->re_tx_head= tx_head; rep->re_flags |= REF_PACK_SENT; /* If the interrupt handler called, don't send a reply. The reply * will be sent after all interrupts are handled. */ if (from_int) return; reply(rep); return; suspend: #if 0 printf("rl_writev: head %d, tail %d, busy: %d %d %d %d\n", tx_head, rep->re_tx_tail, rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); printf("rl_writev: TSD: 0x%x, 0x%x, 0x%x, 0x%x\n", rl_inl(rep->re_base_port, RL_TSD0+0*4), rl_inl(rep->re_base_port, RL_TSD0+1*4), rl_inl(rep->re_base_port, RL_TSD0+2*4), rl_inl(rep->re_base_port, RL_TSD0+3*4)); #endif if (from_int) panic("should not be sending"); rep->re_tx_mess= *mp; reply(rep); } /*===========================================================================* * rl_check_ints * *===========================================================================*/ static void rl_check_ints(rep) re_t *rep; { #if 0 10-1f R/W TSD[0-3] Transmit Status of Descriptor [0-3] 31 R CRS Carrier Sense Lost 30 R TABT Transmit Abort 29 R OWC Out of Window Collision 27-24 R NCC[3-0] Number of Collision Count 23-22 reserved 21-16 R/W ERTXH[5-0] Early Tx Threshold 15 R TOK Transmit OK 14 R TUN Transmit FIFO Underrun 13 R/W OWN OWN 12-0 R/W SIZE Descriptor Size 3e-3f R/W ISR Interrupt Status Register 6 R/W FOVW Fx FIFO Overflow Interrupt 5 R/W PUN/LinkChg Packet Underrun / Link Change Interrupt 3 R/W TER Transmit Error Interrupt 2 R/W TOK Transmit OK Interrupt 3e-3f R/W ISR Interrupt Status Register 15 R/W SERR System Error Interrupt 14 R/W TimeOut Time Out Interrupt 13 R/W LenChg Cable Length Change Interrupt 3e-3f R/W ISR Interrupt Status Register 4 R/W RXOVW Rx Buffer Overflow Interrupt 1 R/W RER Receive Error Interrupt 0 R/W ROK Receive OK Interrupt 4c-4f R/W MPC Missed Packet Counter 60-61 R TSAD Transmit Status of All Descriptors 15-12 R TOK[3-0] TOK bit of Descriptor [3-0] 11-8 R TUN[3-0] TUN bit of Descriptor [3-0] 7-4 R TABT[3-0] TABT bit of Descriptor [3-0] 3-0 R OWN[3-0] OWN bit of Descriptor [3-0] 6c-6d R DIS Disconnect Counter 15-0 R DCNT Disconnect Counter 6e-6f R FCSC False Carrier Sense Counter 15-0 R FCSCNT False Carrier event counter 72-73 R REC RX_ER Counter 15-0 R RXERCNT Received packet counter #endif int re_flags; re_flags= rep->re_flags; if ((re_flags & REF_READING) && !(rl_inb(rep->re_base_port, RL_CR) & RL_CR_BUFE)) { rl_readv_s(&rep->re_rx_mess, TRUE /* from int */); } if (rep->re_clear_rx) rl_clear_rx(rep); if (rep->re_need_reset) rl_do_reset(rep); if (rep->re_send_int) { rl_writev_s(&rep->re_tx_mess, TRUE /* from int */); } if (rep->re_report_link) rl_report_link(rep); if (rep->re_flags & (REF_PACK_SENT | REF_PACK_RECV)) reply(rep); } /*===========================================================================* * rl_report_link * *===========================================================================*/ static void rl_report_link(rep) re_t *rep; { port_t port; u16_t mii_ctrl, mii_status, mii_ana, mii_anlpa, mii_ane, mii_extstat; u8_t msr; int f, link_up; rep->re_report_link= FALSE; port= rep->re_base_port; msr= rl_inb(port, RL_MSR); link_up= !(msr & RL_MSR_LINKB); rep->re_link_up= link_up; if (!link_up) { printf("%s: link down\n", rep->re_name); return; } mii_ctrl= rl_inw(port, RL_BMCR); mii_status= rl_inw(port, RL_BMSR); mii_ana= rl_inw(port, RL_ANAR); mii_anlpa= rl_inw(port, RL_ANLPAR); mii_ane= rl_inw(port, RL_ANER); mii_extstat= 0; if (mii_ctrl & (MII_CTRL_LB|MII_CTRL_PD|MII_CTRL_ISO)) { printf("%s: PHY: ", rep->re_name); f= 1; if (mii_ctrl & MII_CTRL_LB) { printf("loopback mode"); f= 0; } if (mii_ctrl & MII_CTRL_PD) { if (!f) printf(", "); f= 0; printf("powered down"); } if (mii_ctrl & MII_CTRL_ISO) { if (!f) printf(", "); f= 0; printf("isolated"); } printf("\n"); return; } if (!(mii_ctrl & MII_CTRL_ANE)) { printf("%s: manual config: ", rep->re_name); switch(mii_ctrl & (MII_CTRL_SP_LSB|MII_CTRL_SP_MSB)) { case MII_CTRL_SP_10: printf("10 Mbps"); break; case MII_CTRL_SP_100: printf("100 Mbps"); break; case MII_CTRL_SP_1000: printf("1000 Mbps"); break; case MII_CTRL_SP_RES: printf("reserved speed"); break; } if (mii_ctrl & MII_CTRL_DM) printf(", full duplex"); else printf(", half duplex"); printf("\n"); return; } if (!debug) goto resspeed; printf("%s: ", rep->re_name); mii_print_stat_speed(mii_status, mii_extstat); printf("\n"); if (!(mii_status & MII_STATUS_ANC)) printf("%s: auto-negotiation not complete\n", rep->re_name); if (mii_status & MII_STATUS_RF) printf("%s: remote fault detected\n", rep->re_name); if (!(mii_status & MII_STATUS_ANA)) { printf("%s: local PHY has no auto-negotiation ability\n", rep->re_name); } if (!(mii_status & MII_STATUS_LS)) printf("%s: link down\n", rep->re_name); if (mii_status & MII_STATUS_JD) printf("%s: jabber condition detected\n", rep->re_name); if (!(mii_status & MII_STATUS_EC)) { printf("%s: no extended register set\n", rep->re_name); goto resspeed; } if (!(mii_status & MII_STATUS_ANC)) goto resspeed; printf("%s: local cap.: ", rep->re_name); mii_print_techab(mii_ana); printf("\n"); if (mii_ane & MII_ANE_PDF) printf("%s: parallel detection fault\n", rep->re_name); if (!(mii_ane & MII_ANE_LPANA)) { printf("%s: link-partner does not support auto-negotiation\n", rep->re_name); goto resspeed; } printf("%s: remote cap.: ", rep->re_name); mii_print_techab(mii_anlpa); printf("\n"); resspeed: printf("%s: ", rep->re_name); printf("link up at %d Mbps, ", (msr & RL_MSR_SPEED_10) ? 10 : 100); printf("%s duplex\n", ((mii_ctrl & MII_CTRL_DM) ? "full" : "half")); } static void mii_print_techab(u16_t techab) { int fs, ft; if ((techab & MII_ANA_SEL_M) != MII_ANA_SEL_802_3) { printf("strange selector 0x%x, value 0x%x", techab & MII_ANA_SEL_M, (techab & MII_ANA_TAF_M) >> MII_ANA_TAF_S); return; } fs= 1; if (techab & (MII_ANA_100T4 | MII_ANA_100TXFD | MII_ANA_100TXHD)) { printf("100 Mbps: "); fs= 0; ft= 1; if (techab & MII_ANA_100T4) { printf("T4"); ft= 0; } if (techab & (MII_ANA_100TXFD | MII_ANA_100TXHD)) { if (!ft) printf(", "); ft= 0; printf("TX-"); switch(techab & (MII_ANA_100TXFD|MII_ANA_100TXHD)) { case MII_ANA_100TXFD: printf("FD"); break; case MII_ANA_100TXHD: printf("HD"); break; default: printf("FD/HD"); break; } } } if (techab & (MII_ANA_10TFD | MII_ANA_10THD)) { if (!fs) printf(", "); printf("10 Mbps: "); fs= 0; printf("T-"); switch(techab & (MII_ANA_10TFD|MII_ANA_10THD)) { case MII_ANA_10TFD: printf("FD"); break; case MII_ANA_10THD: printf("HD"); break; default: printf("FD/HD"); break; } } if (techab & MII_ANA_PAUSE_SYM) { if (!fs) printf(", "); fs= 0; printf("pause(SYM)"); } if (techab & MII_ANA_PAUSE_ASYM) { if (!fs) printf(", "); fs= 0; printf("pause(ASYM)"); } if (techab & MII_ANA_TAF_RES) { if (!fs) printf(", "); fs= 0; printf("0x%x", (techab & MII_ANA_TAF_RES) >> MII_ANA_TAF_S); } } static void mii_print_stat_speed(u16_t stat, u16_t extstat) { int fs, ft; fs= 1; if (stat & MII_STATUS_EXT_STAT) { if (extstat & (MII_ESTAT_1000XFD | MII_ESTAT_1000XHD | MII_ESTAT_1000TFD | MII_ESTAT_1000THD)) { printf("1000 Mbps: "); fs= 0; ft= 1; if (extstat & (MII_ESTAT_1000XFD | MII_ESTAT_1000XHD)) { ft= 0; printf("X-"); switch(extstat & (MII_ESTAT_1000XFD|MII_ESTAT_1000XHD)) { case MII_ESTAT_1000XFD: printf("FD"); break; case MII_ESTAT_1000XHD: printf("HD"); break; default: printf("FD/HD"); break; } } if (extstat & (MII_ESTAT_1000TFD | MII_ESTAT_1000THD)) { if (!ft) printf(", "); ft= 0; printf("T-"); switch(extstat & (MII_ESTAT_1000TFD|MII_ESTAT_1000THD)) { case MII_ESTAT_1000TFD: printf("FD"); break; case MII_ESTAT_1000THD: printf("HD"); break; default: printf("FD/HD"); break; } } } } if (stat & (MII_STATUS_100T4 | MII_STATUS_100XFD | MII_STATUS_100XHD | MII_STATUS_100T2FD | MII_STATUS_100T2HD)) { if (!fs) printf(", "); fs= 0; printf("100 Mbps: "); ft= 1; if (stat & MII_STATUS_100T4) { printf("T4"); ft= 0; } if (stat & (MII_STATUS_100XFD | MII_STATUS_100XHD)) { if (!ft) printf(", "); ft= 0; printf("TX-"); switch(stat & (MII_STATUS_100XFD|MII_STATUS_100XHD)) { case MII_STATUS_100XFD: printf("FD"); break; case MII_STATUS_100XHD: printf("HD"); break; default: printf("FD/HD"); break; } } if (stat & (MII_STATUS_100T2FD | MII_STATUS_100T2HD)) { if (!ft) printf(", "); ft= 0; printf("T2-"); switch(stat & (MII_STATUS_100T2FD|MII_STATUS_100T2HD)) { case MII_STATUS_100T2FD: printf("FD"); break; case MII_STATUS_100T2HD: printf("HD"); break; default: printf("FD/HD"); break; } } } if (stat & (MII_STATUS_10FD | MII_STATUS_10HD)) { if (!fs) printf(", "); printf("10 Mbps: "); fs= 0; printf("T-"); switch(stat & (MII_STATUS_10FD|MII_STATUS_10HD)) { case MII_STATUS_10FD: printf("FD"); break; case MII_STATUS_10HD: printf("HD"); break; default: printf("FD/HD"); break; } } } /*===========================================================================* * rl_clear_rx * *===========================================================================*/ static void rl_clear_rx(re_t *rep) { port_t port; u8_t cr; rep->re_clear_rx= FALSE; port= rep->re_base_port; /* Reset the receiver */ cr= rl_inb(port, RL_CR); cr &= ~RL_CR_RE; rl_outb(port, RL_CR, cr); SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_RE), 1000000); if (rl_inb(port, RL_CR) & RL_CR_RE) panic("cannot disable receiver"); #if 0 printf("RBSTART = 0x%08x\n", rl_inl(port, RL_RBSTART)); printf("CAPR = 0x%04x\n", rl_inw(port, RL_CAPR)); printf("CBR = 0x%04x\n", rl_inw(port, RL_CBR)); printf("RCR = 0x%08x\n", rl_inl(port, RL_RCR)); #endif rl_outb(port, RL_CR, cr | RL_CR_RE); rl_outl(port, RL_RCR, RX_BUFBITS); rl_rec_mode(rep); rep->re_stat.ets_missedP++; } /*===========================================================================* * rl_do_reset * *===========================================================================*/ static void rl_do_reset(rep) re_t *rep; { rep->re_need_reset= FALSE; rl_reset_hw(rep); rl_rec_mode(rep); rep->re_tx_head= 0; if (rep->re_flags & REF_SEND_AVAIL) { rep->re_tx[rep->re_tx_head].ret_busy= FALSE; rep->re_send_int= TRUE; } } /*===========================================================================* * rl_getstat_s * *===========================================================================*/ static void rl_getstat_s(mp) message *mp; { int r; eth_stat_t stats; re_t *rep; rep= &re_state; assert(rep->re_mode == REM_ENABLED); assert(rep->re_flags & REF_ENABLED); stats= rep->re_stat; r = sys_safecopyto(mp->m_source, mp->DL_GRANT, 0, (vir_bytes) &stats, sizeof(stats)); if (r != OK) panic("rl_getstat_s: sys_safecopyto failed: %d", r); mp->m_type= DL_STAT_REPLY; r= send(mp->m_source, mp); if (r != OK) panic("rl_getstat_s: send failed: %d", r); } /*===========================================================================* * reply * *===========================================================================*/ static void reply(rep) re_t *rep; { message reply; int flags; int r; flags = DL_NOFLAGS; if (rep->re_flags & REF_PACK_SENT) flags |= DL_PACK_SEND; if (rep->re_flags & REF_PACK_RECV) flags |= DL_PACK_RECV; reply.m_type = DL_TASK_REPLY; reply.DL_FLAGS = flags; reply.DL_COUNT = rep->re_read_s; r= send(rep->re_client, &reply); if (r < 0) { printf("RTL8139 tried sending to %d, type %d\n", rep->re_client, reply.m_type); panic("send failed: %d", r); } rep->re_read_s = 0; rep->re_flags &= ~(REF_PACK_SENT | REF_PACK_RECV); } /*===========================================================================* * mess_reply * *===========================================================================*/ static void mess_reply(req, reply_mess) message *req; message *reply_mess; { if (send(req->m_source, reply_mess) != OK) panic("unable to mess_reply"); } #if 0 /*===========================================================================* * dump_phy * *===========================================================================*/ static void dump_phy(rep) re_t *rep; { port_t port; u32_t t; port= rep->re_base_port; t= rl_inb(port, RL_MSR); printf("MSR: 0x%02lx\n", t); if (t & RL_MSR_SPEED_10) printf("\t10 Mbps\n"); if (t & RL_MSR_LINKB) printf("\tLink failed\n"); t= rl_inb(port, RL_CONFIG1); printf("CONFIG1: 0x%02lx\n", t); t= rl_inb(port, RL_CONFIG3); printf("CONFIG3: 0x%02lx\n", t); t= rl_inb(port, RL_CONFIG4); printf("CONFIG4: 0x%02lx\n", t); t= rl_inw(port, RL_BMCR); printf("BMCR (MII_CTRL): 0x%04lx\n", t); t= rl_inw(port, RL_BMSR); printf("BMSR:"); if (t & MII_STATUS_100T4) printf(" 100Base-T4"); if (t & MII_STATUS_100XFD) printf(" 100Base-X-FD"); if (t & MII_STATUS_100XHD) printf(" 100Base-X-HD"); if (t & MII_STATUS_10FD) printf(" 10Mbps-FD"); if (t & MII_STATUS_10HD) printf(" 10Mbps-HD"); if (t & MII_STATUS_100T2FD) printf(" 100Base-T2-FD"); if (t & MII_STATUS_100T2HD) printf(" 100Base-T2-HD"); if (t & MII_STATUS_EXT_STAT) printf(" Ext-stat"); if (t & MII_STATUS_RES) printf(" res-0x%lx", t & MII_STATUS_RES); if (t & MII_STATUS_MFPS) printf(" MFPS"); if (t & MII_STATUS_ANC) printf(" ANC"); if (t & MII_STATUS_RF) printf(" remote-fault"); if (t & MII_STATUS_ANA) printf(" ANA"); if (t & MII_STATUS_LS) printf(" Link"); if (t & MII_STATUS_JD) printf(" Jabber"); if (t & MII_STATUS_EC) printf(" Extended-capability"); printf("\n"); t= rl_inw(port, RL_ANAR); printf("ANAR (MII_ANA): 0x%04lx\n", t); t= rl_inw(port, RL_ANLPAR); printf("ANLPAR: 0x%04lx\n", t); t= rl_inw(port, RL_ANER); printf("ANER (MII_ANE): "); if (t & MII_ANE_RES) printf(" res-0x%lx", t & MII_ANE_RES); if (t & MII_ANE_PDF) printf(" Par-Detect-Fault"); if (t & MII_ANE_LPNPA) printf(" LP-Next-Page-Able"); if (t & MII_ANE_NPA) printf(" Loc-Next-Page-Able"); if (t & MII_ANE_PR) printf(" Page-Received"); if (t & MII_ANE_LPANA) printf(" LP-Auto-Neg-Able"); printf("\n"); t= rl_inw(port, RL_NWAYTR); printf("NWAYTR: 0x%04lx\n", t); t= rl_inw(port, RL_CSCR); printf("CSCR: 0x%04lx\n", t); t= rl_inb(port, RL_CONFIG5); printf("CONFIG5: 0x%02lx\n", t); } #endif /*===========================================================================* * do_hard_int * *===========================================================================*/ static void do_hard_int(void) { int s; /* Run interrupt handler at driver level. */ rl_handler(&re_state); /* Reenable interrupts for this hook. */ if ((s=sys_irqenable(&re_state.re_hook_id)) != OK) printf("RTL8139: error, couldn't enable interrupts: %d\n", s); } /*===========================================================================* * rl_handler * *===========================================================================*/ static int rl_handler(re_t *rep) { int i, port, tx_head, tx_tail, link_up; u16_t isr, tsad; u32_t tsd, tcr, ertxth; #if 0 u8_t cr; #endif int_event_check = FALSE; /* disable check by default */ port= rep->re_base_port; /* Ack interrupt */ isr= rl_inw(port, RL_ISR); rl_outw(port, RL_ISR, isr); if (isr & RL_IMR_FOVW) { isr &= ~RL_IMR_FOVW; /* Should do anything? */ rep->re_stat.ets_fifoOver++; } if (isr & RL_IMR_PUN) { isr &= ~RL_IMR_PUN; /* Either the link status changed or there was a TX fifo * underrun. */ link_up= !(rl_inb(port, RL_MSR) & RL_MSR_LINKB); if (link_up != rep->re_link_up) { rep->re_report_link= TRUE; rep->re_got_int= TRUE; int_event_check = TRUE; } } if (isr & RL_IMR_RXOVW) { isr &= ~RL_IMR_RXOVW; /* Clear the receive buffer */ rep->re_clear_rx= TRUE; rep->re_got_int= TRUE; int_event_check = TRUE; } if (isr & (RL_ISR_RER | RL_ISR_ROK)) { isr &= ~(RL_ISR_RER | RL_ISR_ROK); if (!rep->re_got_int && (rep->re_flags & REF_READING)) { rep->re_got_int= TRUE; int_event_check = TRUE; } } #if 0 if ((isr & (RL_ISR_TER | RL_ISR_TOK)) && (rep->re_flags & REF_SEND_AVAIL) && (rep->re_tx[0].ret_busy || rep->re_tx[1].ret_busy || rep->re_tx[2].ret_busy || rep->re_tx[3].ret_busy)) { printf( "rl_handler, SEND_AVAIL: tx_head %d, tx_tail %d, busy: %d %d %d %d\n", rep->re_tx_head, rep->re_tx_tail, rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); printf( "rl_handler: TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", rl_inw(port, RL_TSAD), rl_inl(port, RL_TSD0+0*4), rl_inl(port, RL_TSD0+1*4), rl_inl(port, RL_TSD0+2*4), rl_inl(port, RL_TSD0+3*4)); } #endif if ((isr & (RL_ISR_TER | RL_ISR_TOK)) || 1) { isr &= ~(RL_ISR_TER | RL_ISR_TOK); tsad= rl_inw(port, RL_TSAD); if (tsad & (RL_TSAD_TABT0|RL_TSAD_TABT1| RL_TSAD_TABT2|RL_TSAD_TABT3)) { #if 0 /* Do we need a watch dog? */ /* Just reset the whole chip */ rep->re_need_reset= TRUE; rep->re_got_int= TRUE; int_event_check = TRUE; #elif 0 /* Reset transmitter */ rep->re_stat.ets_transAb++; cr= rl_inb(port, RL_CR); cr &= ~RL_CR_TE; rl_outb(port, RL_CR, cr); SPIN_UNTIL(!(rl_inb(port, RL_CR) & RL_CR_TE), 1000000); if (rl_inb(port, RL_CR) & RL_CR_TE) { panic("cannot disable transmitter"); } rl_outb(port, RL_CR, cr | RL_CR_TE); tcr= rl_inl(port, RL_TCR); rl_outl(port, RL_TCR, tcr | RL_TCR_IFG_STD); printf("rl_handler: reset after abort\n"); if (rep->re_flags & REF_SEND_AVAIL) { printf("rl_handler: REF_SEND_AVAIL\n"); rep->re_send_int= TRUE; rep->re_got_int= TRUE; int_event_check = TRUE; } for (i= 0; i< N_TX_BUF; i++) rep->re_tx[i].ret_busy= FALSE; rep->re_tx_head= 0; #else printf("rl_handler, TABT, tasd = 0x%04x\n", tsad); /* Find the aborted transmit request */ for (i= 0; i< N_TX_BUF; i++) { tsd= rl_inl(port, RL_TSD0+i*4); if (tsd & RL_TSD_TABT) break; } if (i >= N_TX_BUF) { printf( "rl_handler: can't find aborted TX req.\n"); } else { printf("TSD%d = 0x%04x\n", i, tsd); /* Set head and tail to this buffer */ rep->re_tx_head= rep->re_tx_tail= i; } /* Aborted transmission, just kick the device * and be done with it. */ rep->re_stat.ets_transAb++; tcr= rl_inl(port, RL_TCR); rl_outl(port, RL_TCR, tcr | RL_TCR_CLRABT); #endif } /* Transmit completed */ tx_head= rep->re_tx_head; tx_tail= rep->re_tx_tail; for (i= 0; i< 2*N_TX_BUF; i++) { if (!rep->re_tx[tx_tail].ret_busy) { /* Strange, this buffer is not in-use. * Increment tx_tail until tx_head is * reached (or until we find a buffer that * is in-use. */ if (tx_tail == tx_head) break; if (++tx_tail >= N_TX_BUF) tx_tail= 0; assert(tx_tail < RL_N_TX); rep->re_tx_tail= tx_tail; continue; } tsd= rl_inl(port, RL_TSD0+tx_tail*4); if (!(tsd & RL_TSD_OWN)) { /* Buffer is not yet ready */ break; } /* Should collect statistics */ if (tsd & RL_TSD_CRS) rep->re_stat.ets_carrSense++; if (tsd & RL_TSD_TABT) { printf("rl_handler, TABT, TSD%d = 0x%04x\n", tx_tail, tsd); assert(0); /* CLRABT is not all that * effective, why not? */ rep->re_stat.ets_transAb++; tcr= rl_inl(port, RL_TCR); rl_outl(port, RL_TCR, tcr | RL_TCR_CLRABT); } if (tsd & RL_TSD_OWC) rep->re_stat.ets_OWC++; if (tsd & RL_TSD_CDH) rep->re_stat.ets_CDheartbeat++; /* What about collisions? */ if (tsd & RL_TSD_TOK) rep->re_stat.ets_packetT++; else rep->re_stat.ets_sendErr++; if (tsd & RL_TSD_TUN) { rep->re_stat.ets_fifoUnder++; /* Increase ERTXTH */ ertxth= tsd + (1 << RL_TSD_ERTXTH_S); ertxth &= RL_TSD_ERTXTH_M; if (debug && ertxth > rep->re_ertxth) { printf("%s: new ertxth: %d bytes\n", rep->re_name, (ertxth >> RL_TSD_ERTXTH_S) * 32); rep->re_ertxth= ertxth; } } rep->re_tx[tx_tail].ret_busy= FALSE; #if 0 if (rep->re_flags & REF_SEND_AVAIL) { printf("TSD%d: %08lx\n", tx_tail, tsd); printf( "rl_handler: head %d, tail %d, busy: %d %d %d %d\n", tx_head, tx_tail, rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); } #endif if (++tx_tail >= N_TX_BUF) tx_tail= 0; assert(tx_tail < RL_N_TX); rep->re_tx_tail= tx_tail; if (rep->re_flags & REF_SEND_AVAIL) { #if 0 printf("rl_handler: REF_SEND_AVAIL\n"); #endif rep->re_send_int= TRUE; if (!rep->re_got_int) { rep->re_got_int= TRUE; int_event_check = TRUE; } } } assert(i < 2*N_TX_BUF); } if (isr) { printf("rl_handler: unhandled interrupt: isr = 0x%04x\n", isr); } return 1; } /*===========================================================================* * rl_watchdog_f * *===========================================================================*/ static void rl_watchdog_f(tp) timer_t *tp; { re_t *rep; /* Use a synchronous alarm instead of a watchdog timer. */ sys_setalarm(system_hz, 0); rep= &re_state; if (rep->re_mode != REM_ENABLED) return; if (!(rep->re_flags & REF_SEND_AVAIL)) { /* Assume that an idle system is alive */ rep->re_tx_alive= TRUE; return; } if (rep->re_tx_alive) { rep->re_tx_alive= FALSE; return; } printf("rl_watchdog_f: resetting instance %d\n", re_instance); printf("TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", rl_inw(rep->re_base_port, RL_TSAD), rl_inl(rep->re_base_port, RL_TSD0+0*4), rl_inl(rep->re_base_port, RL_TSD0+1*4), rl_inl(rep->re_base_port, RL_TSD0+2*4), rl_inl(rep->re_base_port, RL_TSD0+3*4)); printf("tx_head %d, tx_tail %d, busy: %d %d %d %d\n", rep->re_tx_head, rep->re_tx_tail, rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); rep->re_need_reset= TRUE; rep->re_got_int= TRUE; check_int_events(); } #if 0 static void rtl_init(struct dpeth *dep); static u16_t get_ee_word(dpeth_t *dep, int a); static void ee_wen(dpeth_t *dep); static void set_ee_word(dpeth_t *dep, int a, u16_t w); static void ee_wds(dpeth_t *dep); static void rtl_init(dep) dpeth_t *dep; { u8_t reg_a, reg_b, cr, config0, config2, config3; int i; char val[128]; printf("rtl_init called\n"); ne_init(dep); /* ID */ outb_reg0(dep, DP_CR, CR_PS_P0); reg_a = inb_reg0(dep, DP_DUM1); reg_b = inb_reg0(dep, DP_DUM2); printf("rtl_init: '%c', '%c'\n", reg_a, reg_b); outb_reg0(dep, DP_CR, CR_PS_P3); config0 = inb_reg3(dep, 3); config2 = inb_reg3(dep, 5); config3 = inb_reg3(dep, 6); outb_reg0(dep, DP_CR, CR_PS_P0); printf("rtl_init: config 0/2/3 = %x/%x/%x\n", config0, config2, config3); if (0 == sys_getkenv("RTL8029FD",9+1, val, sizeof(val))) { printf("rtl_init: setting full-duplex mode\n"); outb_reg0(dep, DP_CR, CR_PS_P3); cr= inb_reg3(dep, 1); outb_reg3(dep, 1, cr | 0xc0); outb_reg3(dep, 6, config3 | 0x40); config3 = inb_reg3(dep, 6); config2= inb_reg3(dep, 5); outb_reg3(dep, 5, config2 | 0x20); config2= inb_reg3(dep, 5); outb_reg3(dep, 1, cr); outb_reg0(dep, DP_CR, CR_PS_P0); printf("rtl_init: config 2 = %x\n", config2); printf("rtl_init: config 3 = %x\n", config3); } for (i= 0; i<64; i++) printf("%x ", get_ee_word(dep, i)); printf("\n"); if (0 == sys_getkenv("RTL8029MN",9+1, val, sizeof(val))) { ee_wen(dep); set_ee_word(dep, 0x78/2, 0x10ec); set_ee_word(dep, 0x7A/2, 0x8029); set_ee_word(dep, 0x7C/2, 0x10ec); set_ee_word(dep, 0x7E/2, 0x8029); ee_wds(dep); assert(get_ee_word(dep, 0x78/2) == 0x10ec); assert(get_ee_word(dep, 0x7A/2) == 0x8029); assert(get_ee_word(dep, 0x7C/2) == 0x10ec); assert(get_ee_word(dep, 0x7E/2) == 0x8029); } if (0 == sys_getkenv("RTL8029XXX",10+1, val, sizeof(val))) { ee_wen(dep); set_ee_word(dep, 0x76/2, 0x8029); ee_wds(dep); assert(get_ee_word(dep, 0x76/2) == 0x8029); } } static u16_t get_ee_word(dep, a) dpeth_t *dep; int a; { int b, i, cmd; u16_t w; outb_reg0(dep, DP_CR, CR_PS_P3); /* Bank 3 */ /* Switch to 9346 mode and enable CS */ outb_reg3(dep, 1, 0x80 | 0x8); cmd= 0x180 | (a & 0x3f); /* 1 1 0 a5 a4 a3 a2 a1 a0 */ for (i= 8; i >= 0; i--) { b= (cmd & (1 << i)); b= (b ? 2 : 0); /* Cmd goes out on the rising edge of the clock */ outb_reg3(dep, 1, 0x80 | 0x8 | b); outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); } outb_reg3(dep, 1, 0x80 | 0x8); /* End of cmd */ w= 0; for (i= 0; i<16; i++) { w <<= 1; /* Data is shifted out on the rising edge. Read at the * falling edge. */ outb_reg3(dep, 1, 0x80 | 0x8 | 0x4); outb_reg3(dep, 1, 0x80 | 0x8 | b); b= inb_reg3(dep, 1); w |= (b & 1); } outb_reg3(dep, 1, 0x80); /* drop CS */ outb_reg3(dep, 1, 0x00); /* back to normal */ outb_reg0(dep, DP_CR, CR_PS_P0); /* back to bank 0 */ return w; } static void ee_wen(dep) dpeth_t *dep; { int b, i, cmd; u16_t w; outb_reg0(dep, DP_CR, CR_PS_P3); /* Bank 3 */ /* Switch to 9346 mode and enable CS */ outb_reg3(dep, 1, 0x80 | 0x8); cmd= 0x130; /* 1 0 0 1 1 x x x x */ for (i= 8; i >= 0; i--) { b= (cmd & (1 << i)); b= (b ? 2 : 0); /* Cmd goes out on the rising edge of the clock */ outb_reg3(dep, 1, 0x80 | 0x8 | b); outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); } outb_reg3(dep, 1, 0x80 | 0x8); /* End of cmd */ outb_reg3(dep, 1, 0x80); /* Drop CS */ /* micro_delay(1); */ /* Is this required? */ } static void set_ee_word(dpeth_t *dep, int a, u16_t w) dpeth_t *dep; int a; u16_t w; { int b, i, cmd; outb_reg3(dep, 1, 0x80 | 0x8); /* Set CS */ cmd= 0x140 | (a & 0x3f); /* 1 0 1 a5 a4 a3 a2 a1 a0 */ for (i= 8; i >= 0; i--) { b= (cmd & (1 << i)); b= (b ? 2 : 0); /* Cmd goes out on the rising edge of the clock */ outb_reg3(dep, 1, 0x80 | 0x8 | b); outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); } for (i= 15; i >= 0; i--) { b= (w & (1 << i)); b= (b ? 2 : 0); /* Cmd goes out on the rising edge of the clock */ outb_reg3(dep, 1, 0x80 | 0x8 | b); outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); } outb_reg3(dep, 1, 0x80 | 0x8); /* End of data */ outb_reg3(dep, 1, 0x80); /* Drop CS */ /* micro_delay(1); */ /* Is this required? */ outb_reg3(dep, 1, 0x80 | 0x8); /* Set CS */ SPIN_UNTIL(inb_reg3(dep, 1) & 1, 10000); if (!(inb_reg3(dep, 1) & 1)) panic("device remains busy"); } static void ee_wds(dep) dpeth_t *dep; { int b, i, cmd; u16_t w; outb_reg0(dep, DP_CR, CR_PS_P3); /* Bank 3 */ /* Switch to 9346 mode and enable CS */ outb_reg3(dep, 1, 0x80 | 0x8); cmd= 0x100; /* 1 0 0 0 0 x x x x */ for (i= 8; i >= 0; i--) { b= (cmd & (1 << i)); b= (b ? 2 : 0); /* Cmd goes out on the rising edge of the clock */ outb_reg3(dep, 1, 0x80 | 0x8 | b); outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); } outb_reg3(dep, 1, 0x80 | 0x8); /* End of cmd */ outb_reg3(dep, 1, 0x80); /* Drop CS */ outb_reg3(dep, 1, 0x00); /* back to normal */ outb_reg0(dep, DP_CR, CR_PS_P0); /* back to bank 0 */ } #endif static void tell_dev(buf, size, pci_bus, pci_dev, pci_func) vir_bytes buf; size_t size; int pci_bus; int pci_dev; int pci_func; { int r; endpoint_t dev_e; message m; r= ds_retrieve_label_endpt("amddev", &dev_e); if (r != OK) { #if 0 printf( "rtl8139`tell_dev: ds_retrieve_label_endpt failed for 'amddev': %d\n", r); #endif return; } m.m_type= IOMMU_MAP; m.m2_i1= pci_bus; m.m2_i2= pci_dev; m.m2_i3= pci_func; m.m2_l1= buf; m.m2_l2= size; r= sendrec(dev_e, &m); if (r != OK) { printf("rtl8139`tell_dev: sendrec to %d failed: %d\n", dev_e, r); return; } if (m.m_type != OK) { printf("rtl8139`tell_dev: dma map request failed: %d\n", m.m_type); return; } } /* * $PchId: rtl8139.c,v 1.3 2003/09/11 14:15:15 philip Exp $ */