/*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1997, 1998-2003 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must rlproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /* * RealTek 8139C+/8169/8169S/8110S/8168/8111/8101E PCI NIC driver * * Written by Bill Paul * Senior Networking Software Engineer * Wind River Systems */ /* * This driver is designed to support RealTek's next generation of * 10/100 and 10/100/1000 PCI ethernet controllers. There are currently * seven devices in this family: the RTL8139C+, the RTL8169, the RTL8169S, * RTL8110S, the RTL8168, the RTL8111 and the RTL8101E. * * The 8139C+ is a 10/100 ethernet chip. It is backwards compatible * with the older 8139 family, however it also supports a special * C+ mode of operation that provides several new performance enhancing * features. These include: * * o Descriptor based DMA mechanism. Each descriptor rlpresents * a single packet fragment. Data buffers may be aligned on * any byte boundary. * * o 64-bit DMA * * o TCP/IP checksum offload for both RX and TX * * o High and normal priority transmit DMA rings * * o VLAN tag insertion and extraction * * o TCP large send (segmentation offload) * * Like the 8139, the 8139C+ also has a built-in 10/100 PHY. The C+ * programming API is fairly straightforward. The RX filtering, EEPROM * access and PHY access is the same as it is on the older 8139 series * chips. * * The 8169 is a 64-bit 10/100/1000 gigabit ethernet MAC. It has almost the * same programming API and feature set as the 8139C+ with the following * differences and additions: * * o 1000Mbps mode * * o Jumbo frames * * o GMII and TBI ports/registers for interfacing with copper * or fiber PHYs * * o RX and TX DMA rings can have up to 1024 descriptors * (the 8139C+ allows a maximum of 64) * * o Slight differences in register layout from the 8139C+ * * The TX start and timer interrupt registers are at different locations * on the 8169 than they are on the 8139C+. Also, the status word in the * RX descriptor has a slightly different bit layout. The 8169 does not * have a built-in PHY. Most reference boards use a Marvell 88E1000 'Alaska' * copper gigE PHY. * * The 8169S/8110S 10/100/1000 devices have built-in copper gigE PHYs * (the 'S' stands for 'single-chip'). These devices have the same * programming API as the older 8169, but also have some vendor-specific * registers for the on-board PHY. The 8110S is a LAN-on-motherboard * part designed to be pin-compatible with the RealTek 8100 10/100 chip. * * This driver takes advantage of the RX and TX checksum offload and * VLAN tag insertion/extraction features. It also implements TX * interrupt moderation using the timer interrupt registers, which * significantly reduces TX interrupt load. There is also support * for jumbo frames, however the 8169/8169S/8110S can not transmit * jumbo frames larger than 7440, so the max MTU possible with this * driver is 7422 bytes. * * Adapted for Minix 3.1.6 by tim kelly (c) 2018 */ #include "../drivers.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../kernel/const.h" #include "../../kernel/config.h" #include "../../kernel/type.h" #define tmra_ut timer_t #define tmra_inittimer(tp) tmr_inittimer(tp) #define Proc_number(p) proc_number(p) #define debug 1 #define printW() ((void)0) #define VERBOSE 1 /* display message during init */ #include "rtl8111.h" #include "if_rlreg.h" #include "pcireg.h" #define DELAY(x) micro_delay(x) /* * Various supported device vendors/types and their names. */ static struct rl_type rl_devs[] = { { DLINK_VENDORID, DLINK_DEVICEID_528T, 0, "D-Link DGE-528(T) Gigabit Ethernet Adapter" }, { DLINK_VENDORID, DLINK_DEVICEID_530T_REVC, 0, "D-Link DGE-530(T) Gigabit Ethernet Adapter" }, { RT_VENDORID, RT_DEVICEID_8139, 0, "RealTek 8139C+ 10/100BaseTX" }, { RT_VENDORID, RT_DEVICEID_8101E, 0, "RealTek 810xE PCIe 10/100baseTX" }, { RT_VENDORID, RT_DEVICEID_8168, 0, "RealTek 8168/8111 B/C/CP/D/DP/E/F/G PCIe Gigabit Ethernet" }, { NCUBE_VENDORID, RT_DEVICEID_8168, 0, "TP-Link TG-3468 v2 (RTL8168) Gigabit Ethernet" }, { RT_VENDORID, RT_DEVICEID_8169, 0, "RealTek 8169/8169S/8169SB(L)/8110S/8110SB(L) Gigabit Ethernet" }, { RT_VENDORID, RT_DEVICEID_8169SC, 0, "RealTek 8169SC/8110SC Single-chip Gigabit Ethernet" }, { COREGA_VENDORID, COREGA_DEVICEID_CGLAPCIGT, 0, "Corega CG-LAPCIGT (RTL8169S) Gigabit Ethernet" }, { LINKSYS_VENDORID, LINKSYS_DEVICEID_EG1032, 0, "Linksys EG1032 (RTL8169S) Gigabit Ethernet" }, { USR_VENDORID, USR_DEVICEID_997902, 0, "US Robotics 997902 (RTL8169S) Gigabit Ethernet" }, { 0, 0, 0, NULL } }; static const struct rl_hwrev re_hwrevs[] = { { RL_TCR_HWVER_RTL8168G, RL_8169, "8168G/8111G", RL_JUMBO_MTU_9K}, { 0, 0, NULL, 0 } }; static rl_t rl_table[RL_PORT_NR]; static u16_t eth_ign_proto; static tmra_ut rl_watchdog; /* * 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. */ PRIVATE message m; PRIVATE int int_event_check; /* set to TRUE if events arrived */ static char *progname; u32_t system_hz; EXTERN int env_argc; EXTERN char **env_argv; /* SEF functions and variables. */ FORWARD _PROTOTYPE( void sef_local_startup, (void) ); FORWARD _PROTOTYPE( int sef_cb_init_fresh, (int type, sef_init_info_t *info) ); FORWARD _PROTOTYPE( void rl_pci_conf, (void) ); FORWARD _PROTOTYPE( int rl_probe, (rl_t *rlp) ); FORWARD _PROTOTYPE( void rl_init, (message *mp) ); FORWARD _PROTOTYPE( unsigned my_inb, (U16_t port) ); FORWARD _PROTOTYPE( unsigned my_inw, (U16_t port) ); FORWARD _PROTOTYPE( unsigned my_inl, (U16_t port) ); FORWARD _PROTOTYPE( void my_outb, (U16_t port, U8_t value) ); FORWARD _PROTOTYPE( void my_outw, (U16_t port, U16_t value) ); FORWARD _PROTOTYPE( void my_outl, (U16_t port, U32_t 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))) #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))) FORWARD _PROTOTYPE( int mdio_read, (U16_t port, int regaddr) ); FORWARD _PROTOTYPE( void mdio_write, (U16_t port, int regaddr, int value) ); FORWARD _PROTOTYPE( void rl_eeprom_putbyte, (struct rl_t *rl, u32_t addr) ); FORWARD _PROTOTYPE( void rl_eeprom_getword, (struct rl_t *rl, u32_t addr, u16_t *dest) ); FORWARD _PROTOTYPE( void rl_read_eeprom, (struct rl_t *rl, caddr_t dest, u32_t off, u32_t cnt) ); /*===========================================================================* * main * *===========================================================================*/ int main(int argc, char *argv[]) { int r; rl_t *rlp; /* SEF local startup. */ env_setargs(argc, argv); sef_local_startup(); printf("rtl8111 main()\n"); while (TRUE) { if ((r = sef_receive(ANY, &m)) != OK) panic("rtl8111", "sef_receive failed", r); /* printf("rtl8111 m.m_type = 0x%x \n", m.m_type); */ if (is_notify(m.m_type)) { 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 PM_PROC_NR: { sigset_t set; if (getsigset(&set) != 0) break; if (sigismember(&set, SIGTERM)) rtl8111_stop(); break; } default: panic("rtl8111", "illegal notify from", m.m_type); } /* done, get new message */ continue; } switch (m.m_type) { case DL_CONF: rl_init(&m); break; case DL_GETNAME: rl_getname(&m); break; case DL_WRITEV_S: rl_writev_s(&m, FALSE); break; case DL_READV_S: rl_readv_s(&m, FALSE); break; case DL_GETSTAT: rl_getstat(&m); break; case DL_GETSTAT_S: rl_getstat_s(&m); break; default: panic("rtl8111", "illegal message", m.m_type); } } } /*===========================================================================* * sef_local_startup * *===========================================================================*/ PRIVATE void sef_local_startup() { /* Register init callbacks. */ sef_setcb_init_fresh(sef_cb_init_fresh); sef_setcb_init_restart(sef_cb_init_fresh); /* No live update support for now. */ /* Let SEF perform startup. */ sef_startup(); } /*===========================================================================* * sef_cb_init_fresh * *===========================================================================*/ PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info) { /* Initialize the rtl8111 driver. */ u32_t inet_proc_nr; int r; rl_t *rlp; long v; system_hz = sys_hz(); (progname = strrchr(env_argv[0], '/')) ? progname++ : (progname = env_argv[0]); v = 0; (void) env_parse("ETH_IGN_PROTO", "x", 0, &v, 0x0000L, 0xFFFFL); eth_ign_proto = htons((u16_t) v); /* Claim buffer memory now under Minix, before MM takes it all. */ for (rlp = &rl_table[0]; rlp < rl_table + RL_PORT_NR; rlp++) rl_init_buf(rlp); /* * Try to notify INET that we are present (again). If INET cannot * be found, assume this is the first time we started and INET is * not yet alive. */ #if 1 r = ds_retrieve_label_num("inet", &inet_proc_nr); if (r == OK) notify(inet_proc_nr); else if (r != ESRCH) printf("rtl8111: ds_retrieve_label_num failed for 'inet': %d\n", r); #endif return(OK); } /*===========================================================================* * rl_pci_conf * *===========================================================================*/ static void rl_pci_conf() { int i, h; rl_t *rlp; static char envvar[] = RL_ENVVAR "#"; static char envfmt[] = "*:d.d.d"; static char val[128]; long v; for (i = 0, rlp = rl_table; i < RL_PORT_NR; i++, rlp++) { strcpy(rlp->rl_name, "rtl8111#0"); rlp->rl_name[8] += i; rlp->rl_seen = FALSE; envvar[sizeof(RL_ENVVAR)-1] = '0' + i; if (0 == env_get_param(envvar, val, sizeof(val)) && !env_prefix(envvar, "pci")) { env_panic(envvar); } v = 0; (void) env_parse(envvar, envfmt, 1, &v, 0, 255); rlp->rl_pcibus = v; v = 0; (void) env_parse(envvar, envfmt, 2, &v, 0, 255); rlp->rl_pcidev = v; v = 0; (void) env_parse(envvar, envfmt, 3, &v, 0, 255); rlp->rl_pcifunc = v; } pci_init(); for (h = 1; h >= 0; h--) { for (i = 0, rlp = rl_table; i < RL_PORT_NR; i++, rlp++) { if (((rlp->rl_pcibus | rlp->rl_pcidev | rlp->rl_pcifunc) != 0) != h) { continue; } if (rl_probe(rlp)) rlp->rl_seen = TRUE; } } } /*===========================================================================* * rl_probe * *===========================================================================*/ static int rl_probe(rlp) rl_t *rlp; { int i, r, devind, just_one; u16_t vid, did; u32_t bar; u8_t ilr; char *dname; if ((rlp->rl_pcibus | rlp->rl_pcidev | rlp->rl_pcifunc) != 0) { /* Look for specific PCI device */ r = pci_find_dev(rlp->rl_pcibus, rlp->rl_pcidev, rlp->rl_pcifunc, &devind); if (r == 0) { printf("%s: no PCI found at %d.%d.%d\n", rlp->rl_name, rlp->rl_pcibus, rlp->rl_pcidev, rlp->rl_pcifunc); return 0; } pci_ids(devind, &vid, &did); just_one = TRUE; } else { r = pci_first_dev(&devind, &vid, &did); if (r == 0) return 0; just_one = FALSE; } for (;;) { for (i = 0; pcitab[i].vid != 0; i++) { if (pcitab[i].vid != vid) continue; if (pcitab[i].did != did) continue; if (pcitab[i].checkclass) { panic("rtl_probe", "class check not implemented", NO_NUM); } break; } if (pcitab[i].vid != 0) break; if (just_one) { printf("%s: wrong PCI device (%04x/%04x) found at %d.%d.%d\n", rlp->rl_name, vid, did, rlp->rl_pcibus, rlp->rl_pcidev, rlp->rl_pcifunc); return 0; } r = pci_next_dev(&devind, &vid, &did); if (!r) return 0; } dname = pci_dev_name(vid, did); if (!dname) dname = "unknown device"; printf("%s: ", rlp->rl_name); printf("%s (%x/%x) at %s\n", dname, vid, did, pci_slot_name(devind)); pci_reserve(devind); bar = pci_attr_r32(devind, PCI_BAR) & 0xffffffe0; if (bar < 0x400) { panic("rtl_probe", "base address is not properly configured", NO_NUM); } rlp->rl_base_port = bar; ilr = pci_attr_r8(devind, PCI_ILR); rlp->rl_irq = ilr; if (debug) { printf("%s: using I/O address 0x%lx, IRQ %d\n", rlp->rl_name, (unsigned long)bar, ilr); } /* look for PCI-Express */ u32_t reg; if (pci_find_cap(devind, PCIY_EXPRESS, ®) == 0) { rlp->rl_flags |= RL_FLAG_PCIE; rlp->rl_expcap = reg; printf("%s: PCI-Express found\n", rlp->rl_name); } /* enable bus mastering for dbdma (1 << 4) */ /* enable memory write and invalidate (1 << 2) */ u16_t cr = pci_attr_r16(devind, PCI_CR); pci_attr_w16(devind, PCI_CR, cr | ((1 << 2) | ( 1<< 4))); cr = pci_attr_r16(devind, PCI_CR); if (cr & ((1 << 2) | (1 << 4))) printf("%s: bus mastering/invalidate set\n", rlp->rl_name); else printf("%s: could not set bus mastering/invalidate \n", rlp->rl_name); /* Disable ASPM L0S/L1 and CLKREQ. */ u16_t cap = pci_attr_r16(devind, rlp->rl_expcap + PCIER_LINK_CAP); if ((cap & PCIEM_LINK_CAP_ASPM) != 0) { u16_t ctl = pci_attr_r16(devind, rlp->rl_expcap + PCIER_LINK_CTL); if ((ctl & (PCIEM_LINK_CTL_ECPM | PCIEM_LINK_CTL_ASPMC))!= 0) { ctl &= ~(PCIEM_LINK_CTL_ECPM | PCIEM_LINK_CTL_ASPMC); pci_attr_w16(devind, rlp->rl_expcap + PCIER_LINK_CTL, ctl); printf("%s: ASPM disabled\n", rlp->rl_name); } else printf("%s: ASPM already disabled\n", rlp->rl_name); } else printf("%s: no ASPM capability\n", rlp->rl_name); return TRUE; } /*===========================================================================* * rl_conf_hw * *===========================================================================*/ static void rl_conf_hw(rlp) rl_t *rlp; { static eth_stat_t empty_stat = {0, 0, 0, 0, 0, 0 /* ,... */ }; rlp->rl_mode = REM_DISABLED; /* Superfluous */ if (rlp->rl_seen) rlp->rl_mode = REM_ENABLED; /* PCI device is present */ if (rlp->rl_mode != REM_ENABLED) return; printf("%s: PCI device is present, setting rl_*\n", rlp->rl_name); rlp->rl_flags = REF_EMPTY; rlp->rl_link_up = 0; rlp->rl_got_int = 0; rlp->rl_send_int = 0; rlp->rl_report_link = 0; rlp->rl_need_reset = 0; rlp->rl_tx_alive = 0; rlp->rl_rx_head = 0; rlp->rl_read_s = 0; rlp->rl_tx_head = 0; rlp->rl_stat = empty_stat; rlp->dtcc_counter = 0; } /*===========================================================================* * rl_confaddr * *===========================================================================*/ static void rl_confaddr(rlp) rl_t *rlp; { 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' + (rlp-rl_table); port = rlp->rl_base_port; for (i = 0; i < 6; i++) { if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET) break; rlp->rl_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 = rlp->rl_base_port; rl_outb(port, RL_9346CR, RL_9346CR_EEM_CONFIG); w = 0; for (i = 0; i < 4; i++) w |= (rlp->rl_address.ea_addr[i] << (i * 8)); rl_outl(port, RL_IDR, w); w = 0; for (i = 4; i < 6; i++) w |= (rlp->rl_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++) rlp->rl_address.ea_addr[i] = rl_inb(port, RL_IDR+i); } static void rtl8169s_phy_config(port_t port) { return; } static void rtl8169sd_phy_config(port_t port) { return; } /*===========================================================================* * rl_reset_hw * *===========================================================================*/ static void rl_reset_hw(rlp) rl_t *rlp; { port_t port; u32_t t; int i; clock_t t0, t1; port = rlp->rl_base_port; rl_outw(port, RL_IMR, 0x0000); /* turn off MSI enable */ rl_outb(port, RL_EECMD, RL_EE_MODE); u8_t cfg = rl_inb(port, RL_CFG2); if ((cfg & RL_CFG2_MSI) != 0) { printf("rl_reset_hw: turning off MSI enable bit.\n"); cfg &= ~RL_CFG2_MSI; rl_outb(port, RL_CFG2, cfg); } rl_outb(port, RL_EECMD, RL_EEMODE_OFF); /* Get Model and MAC info */ const struct rl_hwrev *hw_rev = re_hwrevs; u32_t hwrev = rl_inl(port, RL_TCR); switch (hwrev & 0x70000000) { case 0x00000000: case 0x10000000: printf("%s: Chip rev. 0x%08x\n", rlp->rl_name, hwrev & 0xfc800000); hwrev &= (RL_TCR_HWVER_AM | 0x80000000); break; default: printf("%s: Chip rev. 0x%08x\n", rlp->rl_name, hwrev & 0x7c800000); rlp->rl_mac = hwrev & 0x00700000; hwrev &= RL_TCR_HWVER_AM; break; } printf("%s: MAC rev. 0x%08x\n", rlp->rl_name, rlp->rl_mac); while (hw_rev->rl_desc != NULL) { printf("%s: checking %s...", rlp->rl_name, hw_rev->rl_desc); if (hw_rev->rl_rev == hwrev) { rlp->rl_type = hw_rev->rl_type; rlp->rl_hwrev = hw_rev; printf("match\n"); break; } hw_rev++; } if (hw_rev->rl_desc == NULL) { panic("rtl8111", "Unknown H/W revision:", hwrev); } switch (rlp->rl_hwrev->rl_rev) { case RL_TCR_HWVER_RTL8168G: rlp->rl_model = "RTL8168G/8111G"; /* configure this chip */ rlp->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP | RL_FLAG_AUTOPAD | RL_FLAG_JUMBOV2 | RL_FLAG_CMDSTOP_WAIT_TXQ | RL_FLAG_WOL_MANLINK | RL_FLAG_8168G_PLUS; #if 0 printf("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); rl_outw(port, 0x82, 0x01); printf("Set PHY Reg 0x0bh = 0x00h\n"); mdio_write(port, 0x0b, 0x0000); /* w 0x0b 15 0 0 */ #endif break; default: rlp->rl_model = "Unknown"; rlp->rl_mac = t; break; } rlp->rl_cfg0 = RL_CFG0; rlp->rl_cfg1 = RL_CFG1; rlp->rl_cfg2 = RL_CFG2; rlp->rl_cfg3 = RL_CFG3; rlp->rl_cfg4 = RL_CFG4; rlp->rl_cfg5 = RL_CFG5; /* Reset the device */ printf("rl_reset_hw: (before reset) port = 0x%x, RL_CR = 0x%x\n", port, rl_inb(port, RL_CR)); rl_outb(port, RL_CR, RL_CR_RST); getuptime(&t0); do { if (!(rl_inb(port, RL_CR) & RL_CR_RST)) break; } while (getuptime(&t1) == OK && (t1 - t0) < system_hz); printf("rl_reset_hw: (after reset) port = 0x%x, RL_CR = 0x%x\n", port, rl_inb(port, RL_CR)); if (rl_inb(port, RL_CR) & RL_CR_RST) printf("rtl8111: reset failed to complete"); rl_outw(port, RL_ISR, 0xFFFF); /* Enable PME. */ rl_outb(port, RL_EECMD, RL_EE_MODE); cfg = rl_inb(port, rlp->rl_cfg1); cfg |= RL_CFG1_PME; rl_outb(port, rlp->rl_cfg1, cfg); cfg = rl_inb(port, rlp->rl_cfg5); cfg &= RL_CFG5_PME_STS; rl_outb(port, rlp->rl_cfg5, cfg); rl_outb(port, RL_EECMD, RL_EEMODE_OFF); /* Take controller out of deep sleep mode. */ if ((rlp->rl_flags & RL_FLAG_MACSLEEP) != 0) { if ((rl_inb(port, RL_MACDBG) & 0x80) == 0x80) rl_outw(port, RL_GPIO, rl_inb(port, RL_GPIO) | 0x01); else rl_outw(port, RL_GPIO, rl_inb(port, RL_GPIO) & ~0x01); } /* Take PHY out of power down mode. */ if ((rlp->rl_flags & RL_FLAG_PHYWAKE_PM) != 0) { rl_outw(port, RL_PMCH, rl_inb(port, RL_PMCH) | 0x80); if (hw_rev->rl_rev == RL_HWREV_8401E) rl_outw(port, 0xD1, rl_inb(port, 0xD1) & ~0x08); } if ((rlp->rl_flags & RL_FLAG_PHYWAKE) != 0) { /* vendor (Realtek) specific */ mdio_write(port, 0x1f, 0); mdio_write(port, 0x0e, 0); } mdio_write(port, MII_CTRL, MII_CTRL_RST); for (i = 0; i < 1000; i++) { t = mdio_read(port, MII_CTRL); if (!(t & MII_CTRL_RST)) break; else micro_delay(100); } t = mdio_read(port, MII_CTRL) | MII_CTRL_ANE | MII_CTRL_DM | MII_CTRL_SP_1000; mdio_write(port, MII_CTRL, t); t = mdio_read(port, MII_ANA); t |= MII_ANA_10THD | MII_ANA_10TFD | MII_ANA_100TXHD | MII_ANA_100TXFD; t |= MII_ANA_PAUSE_SYM | MII_ANA_PAUSE_ASYM; mdio_write(port, MII_ANA, t); t = mdio_read(port, MII_1000_CTRL) | 0x300; mdio_write(port, MII_1000_CTRL, t); /* Restart Auto-Negotiation Process */ t = mdio_read(port, MII_CTRL) | MII_CTRL_ANE | MII_CTRL_RAN; mdio_write(port, MII_CTRL, t); rl_outw(port, RL_9346CR, RL_9346CR_EEM_CONFIG); /* Unlock */ t = rl_inw(port, RL_CPLUSCMD); if /* ((rlp->rl_mac == RL_TCR_HWVER_RTL8111S) || */ (rlp->rl_mac == RL_TCR_HWVER_RTL8110S) /* ) */ { printf("Set MAC Reg C+CR Offset 0xE0. " "Bit-3 and bit-14 MUST be 1\n"); rl_outw(port, RL_CPLUSCMD, t | RL_CPLUS_MULRW | (1 << 14)); } else rl_outw(port, RL_CPLUSCMD, t | RL_CPLUS_MULRW); rl_outw(port, RL_INTRMITIGATE, 0x00); t = rl_inb(port, RL_CR); rl_outb(port, RL_CR, t | RL_CR_RE | RL_CR_TE); /* Initialize Rx */ rl_outw(port, RL_RMS, RX_BUFSIZE); /* Maximum rx packet size */ t = rl_inl(port, RL_RCR) & RX_CONFIG_MASK; rl_outl(port, RL_RCR, RL_RCR_RXFTH_UNLIM | RL_RCR_MXDMA_1024 | t); rl_outl(port, RL_RDSAR_LO, rlp->p_rx_desc); rl_outl(port, RL_RDSAR_HI, 0x00); /* For 64 bit */ /* Initialize Tx */ rl_outw(port, RL_ETTHR, 0x3f); /* No early transmit */ rl_outl(port, RL_TCR, RL_TCR_MXDMA_2048 | RL_TCR_IFG_STD); rl_outl(port, RL_TNPDS_LO, rlp->p_tx_desc); rl_outl(port, RL_TNPDS_HI, 0x00); /* For 64 bit */ rl_outw(port, RL_9346CR, RL_9346CR_EEM_NORMAL); /* Lock */ rl_outw(port, RL_MPC, 0x00); rl_outw(port, RL_MULINT, rl_inw(port, RL_MULINT) & 0xF000); rl_outw(port, RL_IMR, RL_INTR_MASK); } /*===========================================================================* * rl_init_buf * *===========================================================================*/ static void rl_init_buf(rlp) rl_t *rlp; { size_t rx_bufsize, tx_bufsize, rx_descsize, tx_descsize, tot_bufsize; struct rl_desc *desc; phys_bytes buf; char *mallocbuf; int d; assert(!rlp->setup); /* Allocate receive and transmit descriptors */ rx_descsize = (N_RX_DESC * sizeof(struct rl_desc)); tx_descsize = (N_TX_DESC * sizeof(struct rl_desc)); /* 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 = rx_descsize + tx_descsize; tot_bufsize += (N_TX_DESC * tx_bufsize) + (N_RX_DESC * rx_bufsize); tot_bufsize += sizeof(struct rl_dtcc); if (tot_bufsize % 4096) tot_bufsize += 4096 - (tot_bufsize % 4096); if (!(mallocbuf = alloc_contig(tot_bufsize, AC_ALIGN64K, &buf))) panic("RTL8111", "Couldn't allocate kernel buffer", NO_NUM); /* Rx Descriptor */ rlp->rl_rx_desc = (rl_desc *)mallocbuf; rlp->p_rx_desc = buf; memset(mallocbuf, 0x00, rx_descsize); buf += rx_descsize; mallocbuf += rx_descsize; /* Tx Descriptor */ rlp->rl_tx_desc = (rl_desc *)mallocbuf; rlp->p_tx_desc = buf; memset(mallocbuf, 0x00, tx_descsize); buf += tx_descsize; mallocbuf += tx_descsize; desc = rlp->rl_rx_desc; for (d = 0; d < N_RX_DESC; d++) { /* Setting Rx buffer */ rlp->rl_rx[d].ret_buf = buf; rlp->rl_rx[d].v_ret_buf = mallocbuf; buf += rx_bufsize; mallocbuf += rx_bufsize; /* Setting Rx descriptor */ if (d == (N_RX_DESC - 1)) /* Last descriptor? if so, set the EOR bit */ desc->status = DESC_EOR | DESC_OWN | (RX_BUFSIZE & DESC_RX_LENMASK); else desc->status = DESC_OWN | (RX_BUFSIZE & DESC_RX_LENMASK); desc->addr_low = rlp->rl_rx[d].ret_buf; desc++; } desc = rlp->rl_tx_desc; for (d = 0; d < N_TX_DESC; d++) { rlp->rl_tx[d].ret_busy = FALSE; rlp->rl_tx[d].ret_buf = buf; rlp->rl_tx[d].v_ret_buf = mallocbuf; buf += tx_bufsize; mallocbuf += tx_bufsize; /* Setting Tx descriptor */ desc->addr_low = rlp->rl_tx[d].ret_buf; desc++; } /* Dump Tally Counter buffer */ rlp->dtcc_buf = buf; rlp->v_dtcc_buf = (rl_dtcc *)mallocbuf; rlp->setup = 1; } /*===========================================================================* * rl_init_hw * *===========================================================================*/ static void rl_init_hw(rlp) rl_t *rlp; { int s, i; rlp->rl_flags = REF_EMPTY; rlp->rl_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. */ rlp->rl_hook_id = rlp->rl_irq; if ((s = sys_irqsetpolicy(rlp->rl_irq, 0, &rlp->rl_hook_id)) != OK) printf("RTL8111: error, couldn't set IRQ policy: %d\n", s); rl_reset_hw(rlp); if ((s = sys_irqenable(&rlp->rl_hook_id)) != OK) printf("RTL8111: error, couldn't enable interrupts: %d\n", s); printf("%s: model: %s mac: 0x%08lx\n", rlp->rl_name, rlp->rl_model, rlp->rl_mac); rl_confaddr(rlp); if (debug) { printf("%s: Ethernet address ", rlp->rl_name); for (i = 0; i < 6; i++) { printf("%x%c", rlp->rl_address.ea_addr[i], i < 5 ? ':' : '\n'); } } } /*===========================================================================* * rl_rec_mode * *===========================================================================*/ static void rl_rec_mode(rlp) rl_t *rlp; { port_t port; u32_t rcr; u32_t mc_filter[2]; /* Multicast hash filter */ port = rlp->rl_base_port; mc_filter[1] = mc_filter[0] = 0xffffffff; rl_outl(port, RL_MAR + 0, mc_filter[0]); rl_outl(port, RL_MAR + 4, mc_filter[1]); rcr = rl_inl(port, RL_RCR); rcr &= ~(RL_RCR_AB | RL_RCR_AM | RL_RCR_APM | RL_RCR_AAP); if (rlp->rl_flags & REF_PROMISC) rcr |= RL_RCR_AB | RL_RCR_AM | RL_RCR_AAP; if (rlp->rl_flags & REF_BROAD) rcr |= RL_RCR_AB; if (rlp->rl_flags & REF_MULTI) rcr |= RL_RCR_AM; rcr |= RL_RCR_APM; rl_outl(port, RL_RCR, RL_RCR_RXFTH_UNLIM | RL_RCR_MXDMA_1024 | rcr); } /*===========================================================================* * rl_init * *===========================================================================*/ static void rl_init(mp) message *mp; { static int first_time = 1; int port; rl_t *rlp; message reply_mess; if (first_time) { first_time = 0; rl_pci_conf(); /* Configure PCI devices. */ tmra_inittimer(&rl_watchdog); /* Use a synchronous alarm instead of a watchdog timer. */ sys_setalarm(system_hz, 0); } port = mp->DL_PORT; if (port < 0 || port >= RL_PORT_NR) { reply_mess.m_type = DL_CONF_REPLY; reply_mess.m3_i1 = ENXIO; mess_reply(mp, &reply_mess); return; } rlp = &rl_table[port]; if (rlp->rl_mode == REM_DISABLED) { /* This is the default, try to (re)locate the device. */ rl_conf_hw(rlp); if (rlp->rl_mode == REM_DISABLED) { /* Probe failed, or the device is configured off. */ reply_mess.m_type = DL_CONF_REPLY; reply_mess.m3_i1 = ENXIO; mess_reply(mp, &reply_mess); return; } if (rlp->rl_mode == REM_ENABLED) rl_init_hw(rlp); } assert(rlp->rl_mode == REM_ENABLED); assert(rlp->rl_flags & REF_ENABLED); rlp->rl_flags &= ~(REF_PROMISC | REF_MULTI | REF_BROAD); if (mp->DL_MODE & DL_PROMISC_REQ) rlp->rl_flags |= REF_PROMISC; if (mp->DL_MODE & DL_MULTI_REQ) rlp->rl_flags |= REF_MULTI; if (mp->DL_MODE & DL_BROAD_REQ) rlp->rl_flags |= REF_BROAD; rlp->rl_client = mp->m_source; rl_rec_mode(rlp); reply_mess.m_type = DL_CONF_REPLY; reply_mess.m3_i1 = mp->DL_PORT; reply_mess.m3_i2 = RL_PORT_NR; *(ether_addr_t *) reply_mess.m3_ca1 = rlp->rl_address; mess_reply(mp, &reply_mess); } /*===========================================================================* * rl_readv_s * *===========================================================================*/ static void rl_readv_s(message *mp, int from_int) { int i, j, n, s, dl_port, rl_client, count, size, index; port_t port; unsigned totlen, packlen; phys_bytes src_phys; rl_desc *desc; u32_t rxstat = 0x12345678; rl_t *rlp; iovec_s_t *iovp; int cps; int iov_offset = 0; dl_port = mp->DL_PORT; count = mp->DL_COUNT; if (dl_port < 0 || dl_port >= RL_PORT_NR) panic("rtl8111", " illegal port", dl_port); rlp = &rl_table[dl_port]; rl_client = mp->DL_PROC; rlp->rl_client = rl_client; assert(rlp->rl_mode == REM_ENABLED); assert(rlp->rl_flags & REF_ENABLED); port = rlp->rl_base_port; /* printf("rl_readv_s: "); */ /* * 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)) { printf("Receive buffer is empty\n"); goto suspend; /* Receive buffer is empty, suspend */ } index = rlp->rl_rx_head; readvs_test_loop: desc = rlp->rl_rx_desc; desc += index; readvs_loop: rxstat = desc->status; if (rxstat & DESC_OWN) { /* printf("skipping to suspend\n"); */ goto suspend; } if (rxstat & DESC_RX_CRC) rlp->rl_stat.ets_CRCerr++; if ((rxstat & (DESC_FS | DESC_LS)) != (DESC_FS | DESC_LS)) { printf("rl_readv_s: packet is fragmented\n"); /* Fix the fragmented packet */ if (index == N_RX_DESC - 1) { desc->status = DESC_EOR | DESC_OWN | (RX_BUFSIZE & DESC_RX_LENMASK); index = 0; desc = rlp->rl_rx_desc; } else { desc->status = DESC_OWN | (RX_BUFSIZE & DESC_RX_LENMASK); index++; desc++; } goto readvs_loop; /* Loop until we get correct packet */ } totlen = rxstat & DESC_RX_LENMASK; if (totlen < 8 || totlen > 2 * ETH_MAX_PACK_SIZE) { /* Something went wrong */ printf("rl_readv_s: bad length (%u) in status 0x%08lx\n", totlen, rxstat); panic(NULL, NULL, NO_NUM); } /* Should subtract the CRC */ packlen = totlen - ETH_CRC_SIZE; size = 0; src_phys = rlp->rl_rx[index].ret_buf; for (i = 0; i < count; i += IOVEC_NR, iov_offset += IOVEC_NR * sizeof(rlp->rl_iovec_s[0])) { n = IOVEC_NR; if (i + n > count) n = count-i; cps = sys_safecopyfrom(rl_client, mp->DL_GRANT, iov_offset, (vir_bytes) rlp->rl_iovec_s, n * sizeof(rlp->rl_iovec_s[0]), D); if (cps != OK) { panic(__FILE__, "rl_readv_s: sys_safecopyfrom failed", cps); } for (j = 0, iovp = rlp->rl_iovec_s; j < n; j++, iovp++) { s = iovp->iov_size; if (size + s > packlen) { assert(packlen > size); s = packlen-size; } cps = sys_safecopyto(rl_client, iovp->iov_grant, 0, (vir_bytes) rlp->rl_rx[index].v_ret_buf + size, s, D); if (cps != OK) panic(__FILE__, "rl_readv_s: sys_safecopyto failed", cps); size += s; if (size == packlen) break; } if (size == packlen) break; } if (size < packlen) assert(0); rlp->rl_stat.ets_packetR++; rlp->rl_read_s = packlen; if (index == N_RX_DESC - 1) { desc->status = DESC_EOR | DESC_OWN | (RX_BUFSIZE & DESC_RX_LENMASK); index = 0; } else { desc->status = DESC_OWN | (RX_BUFSIZE & DESC_RX_LENMASK); index++; } rlp->rl_rx_head = index; assert(rlp->rl_rx_head < N_RX_DESC); rlp->rl_flags = (rlp->rl_flags & ~REF_READING) | REF_PACK_RECV; if (!from_int) { /* printf("rl_readv_s: replying\n"); */ reply(rlp, OK, FALSE); } /* printf("rl_readv_s: returning\n"); */ return; suspend: if (from_int) { assert(rlp->rl_flags & REF_READING); /* printf("rl_readv_s: suspending/returning without replying\n"); */ /* No need to store any state */ return; } rlp->rl_rx_mess = *mp; assert(!(rlp->rl_flags & REF_READING)); rlp->rl_flags |= REF_READING; /* printf("rl_readv_s: suspending/replying\n"); */ reply(rlp, OK, FALSE); } /*===========================================================================* * rl_writev_s * *===========================================================================*/ static void rl_writev_s(message *mp, int from_int) { int i, j, n, s, port, count, size; int tx_head, rl_client; rl_t *rlp; iovec_s_t *iovp; rl_desc *desc; char *ret; int cps; int iov_offset = 0; port = mp->DL_PORT; count = mp->DL_COUNT; if (port < 0 || port >= RL_PORT_NR) panic("rtl8111", "illegal port", port); rlp = &rl_table[port]; assert(mp); assert(port >= 0 && port < RL_PORT_NR); assert(rlp->setup); rl_client = mp->DL_PROC; rlp->rl_client = rl_client; assert(rlp->rl_mode == REM_ENABLED); assert(rlp->rl_flags & REF_ENABLED); if (from_int) { assert(rlp->rl_flags & REF_SEND_AVAIL); rlp->rl_flags &= ~REF_SEND_AVAIL; rlp->rl_send_int = FALSE; rlp->rl_tx_alive = TRUE; } tx_head = rlp->rl_tx_head; desc = rlp->rl_tx_desc; desc += tx_head; if(!desc || !rlp->rl_tx_desc) { printf("desc 0x%lx, rl_tx_desc 0x%lx, tx_head %d, setup %d\n", desc, rlp->rl_tx_desc, tx_head, rlp->setup); } assert(rlp->rl_tx_desc); assert(rlp->rl_tx_head >= 0 && rlp->rl_tx_head < N_TX_DESC); assert(desc); if (rlp->rl_tx[tx_head].ret_busy) { assert(!(rlp->rl_flags & REF_SEND_AVAIL)); rlp->rl_flags |= REF_SEND_AVAIL; if (rlp->rl_tx[tx_head].ret_busy) goto suspend; /* * Race condition, the interrupt handler may clear re_busy * before we got a chance to set REF_SEND_AVAIL. Checking * ret_busy twice should be sufficient. */ #if VERBOSE printf("rl_writev_s: race detected\n"); #endif rlp->rl_flags &= ~REF_SEND_AVAIL; rlp->rl_send_int = FALSE; } assert(!(rlp->rl_flags & REF_SEND_AVAIL)); assert(!(rlp->rl_flags & REF_PACK_SENT)); size = 0; ret = rlp->rl_tx[tx_head].v_ret_buf; for (i = 0; i < count; i += IOVEC_NR, iov_offset += IOVEC_NR * sizeof(rlp->rl_iovec_s[0])) { n = IOVEC_NR; if (i + n > count) n = count - i; cps = sys_safecopyfrom(rl_client, mp->DL_GRANT, iov_offset, (vir_bytes) rlp->rl_iovec_s, n * sizeof(rlp->rl_iovec_s[0]), D); if (cps != OK) { panic(__FILE__, "rl_writev_s: sys_safecopyfrom failed", cps); } for (j = 0, iovp = rlp->rl_iovec_s; j < n; j++, iovp++) { s = iovp->iov_size; if (size + s > ETH_MAX_PACK_SIZE_TAGGED) panic("rtl8111", "invalid packet size", NO_NUM); cps = sys_safecopyfrom(rl_client, iovp->iov_grant, 0, (vir_bytes) ret, s, D); if (cps != OK) { panic(__FILE__, "rl_writev_s: sys_safecopyfrom failed", cps); } size += s; ret += s; } } assert(desc); if (size < ETH_MIN_PACK_SIZE) panic("rtl8111", "invalid packet size", size); rlp->rl_tx[tx_head].ret_busy = TRUE; if (tx_head == N_TX_DESC - 1) { desc->status = DESC_EOR | DESC_OWN | DESC_FS | DESC_LS | size; tx_head = 0; } else { desc->status = DESC_OWN | DESC_FS | DESC_LS | size; tx_head++; } assert(tx_head < N_TX_DESC); rlp->rl_tx_head = tx_head; rl_outl(rlp->rl_base_port, RL_TPPOLL, RL_TPPOLL_NPQ); rlp->rl_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(rlp, OK, FALSE); return; suspend: if (from_int) panic("rtl8111", "should not be sending\n", NO_NUM); rlp->rl_tx_mess = *mp; reply(rlp, OK, FALSE); } /*===========================================================================* * rtl8111_stop * *===========================================================================*/ static void rtl8111_stop() { int i; rl_t *rlp; for (i = 0, rlp = &rl_table[0]; i < RL_PORT_NR; i++, rlp++) { if (rlp->rl_mode != REM_ENABLED) continue; rl_outb(rlp->rl_base_port, RL_CR, 0); } exit(0); } static void rtl8111_update_stat(rl_t *rlp) { port_t port; int i; port = rlp->rl_base_port; /* Fetch Missed Packets */ rlp->rl_stat.ets_missedP += rl_inw(port, RL_MPC); rl_outw(port, RL_MPC, 0x00); #if 0 /* Dump Tally Counter Command */ rl_outl(port, RL_DTCCR_HI, 0); /* 64 bits */ rl_outl(port, RL_DTCCR_LO, rlp->dtcc_buf | RL_DTCCR_CMD); for (i = 0; i < 1000; i++) { if (!(rl_inl(port, RL_DTCCR_LO) & RL_DTCCR_CMD)) break; micro_delay(10); } #endif /* Update counters */ rlp->rl_stat.ets_frameAll = rlp->v_dtcc_buf->FAE; rlp->rl_stat.ets_transDef = rlp->v_dtcc_buf->TxUndrn; rlp->rl_stat.ets_transAb = rlp->v_dtcc_buf->TxAbt; rlp->rl_stat.ets_collision = rlp->v_dtcc_buf->Tx1Col + rlp->v_dtcc_buf->TxMCol; } /*===========================================================================* * rtl8111_dump * *===========================================================================*/ static void rtl8111_dump(void) { rl_dtcc *dtcc; rl_t *rlp; int i; printf("\n"); for (i = 0, rlp = &rl_table[0]; i < RL_PORT_NR; i++, rlp++) { if (rlp->rl_mode == REM_DISABLED) printf("Realtek RTL 8111 port %d is disabled\n", i); if (rlp->rl_mode != REM_ENABLED) continue; rtl8111_update_stat(rlp); printf("Realtek RTL 8111 statistics of port %d:\n", i); printf("recvErr :%8ld\t", rlp->rl_stat.ets_recvErr); printf("sendErr :%8ld\t", rlp->rl_stat.ets_sendErr); printf("OVW :%8ld\n", rlp->rl_stat.ets_OVW); printf("CRCerr :%8ld\t", rlp->rl_stat.ets_CRCerr); printf("frameAll :%8ld\t", rlp->rl_stat.ets_frameAll); printf("missedP :%8ld\n", rlp->rl_stat.ets_missedP); printf("packetR :%8ld\t", rlp->rl_stat.ets_packetR); printf("packetT :%8ld\t", rlp->rl_stat.ets_packetT); printf("transDef :%8ld\n", rlp->rl_stat.ets_transDef); printf("collision :%8ld\t", rlp->rl_stat.ets_collision); printf("transAb :%8ld\t", rlp->rl_stat.ets_transAb); printf("carrSense :%8ld\n", rlp->rl_stat.ets_carrSense); printf("fifoUnder :%8ld\t", rlp->rl_stat.ets_fifoUnder); printf("fifoOver :%8ld\t", rlp->rl_stat.ets_fifoOver); printf("OWC :%8ld\n", rlp->rl_stat.ets_OWC); printf("interrupts :%8lu\n", rlp->interrupts); printf("\nRealtek RTL 8111 Tally Counters:\n"); dtcc = rlp->v_dtcc_buf; if (dtcc->TxOk_high) printf("TxOk :%8ld%08ld\t", dtcc->TxOk_high, dtcc->TxOk_low); else printf("TxOk :%16lu\t", dtcc->TxOk_low); if (dtcc->RxOk_high) printf("RxOk :%8ld%08ld\n", dtcc->RxOk_high, dtcc->RxOk_low); else printf("RxOk :%16lu\n", dtcc->RxOk_low); if (dtcc->TxEr_high) printf("TxEr :%8ld%08ld\t", dtcc->TxEr_high, dtcc->TxEr_low); else printf("TxEr :%16ld\t", dtcc->TxEr_low); printf("RxEr :%16ld\n", dtcc->RxEr); printf("Tx1Col :%16ld\t", dtcc->Tx1Col); printf("TxMCol :%16ld\n", dtcc->TxMCol); if (dtcc->RxOkPhy_high) printf("RxOkPhy :%8ld%08ld\t", dtcc->RxOkPhy_high, dtcc->RxOkPhy_low); else printf("RxOkPhy :%16ld\t", dtcc->RxOkPhy_low); if (dtcc->RxOkBrd_high) printf("RxOkBrd :%8ld%08ld\n", dtcc->RxOkBrd_high, dtcc->RxOkBrd_low); else printf("RxOkBrd :%16ld\n", dtcc->RxOkBrd_low); printf("RxOkMul :%16ld\t", dtcc->RxOkMul); printf("MissPkt :%16d\n", dtcc->MissPkt); printf("\nRealtek RTL 8111 Miscellaneous Info:\n"); printf("rl_flags : 0x%08x\n", rlp->rl_flags); printf("tx_head :%8d busy %d\t", rlp->rl_tx_head, rlp->rl_tx[rlp->rl_tx_head].ret_busy); } } /*===========================================================================* * rl_report_link * *===========================================================================*/ static void rl_report_link(rlp) rl_t *rlp; { port_t port; u8_t mii_status; rlp->rl_report_link = FALSE; port = rlp->rl_base_port; mii_status = rl_inb(port, RL_PHYSTAT); if (mii_status & RL_STAT_LINK) { rlp->rl_link_up = 1; printf("%s: link up at ", rlp->rl_name); } else { rlp->rl_link_up = 0; printf("%s: link down\n", rlp->rl_name); return; } if (mii_status & RL_STAT_1000) printf("1000 Mbps"); else if (mii_status & RL_STAT_100) printf("100 Mbps"); else if (mii_status & RL_STAT_10) printf("10 Mbps"); if (mii_status & RL_STAT_FULLDUP) printf(", full duplex"); else printf(", half duplex"); printf("\n"); dump_phy(rlp); } /*===========================================================================* * rl_getstat * *===========================================================================*/ static void rl_getstat(mp) message *mp; { int r, port; eth_stat_t stats; rl_t *rlp; port = mp->DL_PORT; if (port < 0 || port >= RL_PORT_NR) panic("rtl8111", "illegal port", port); rlp = &rl_table[port]; rlp->rl_client = mp->DL_PROC; assert(rlp->rl_mode == REM_ENABLED); assert(rlp->rl_flags & REF_ENABLED); stats = rlp->rl_stat; r = sys_datacopy(SELF, (vir_bytes) &stats, mp->DL_PROC, (vir_bytes) mp->DL_ADDR, sizeof(stats)); if (r != OK) panic(__FILE__, "rl_getstat: sys_datacopy failed", r); mp->m_type = DL_STAT_REPLY; mp->DL_PORT = port; mp->DL_STAT = OK; r = send(mp->m_source, mp); if (r != OK) panic("RTL8111", "rl_getstat: send failed: %d\n", r); } /*===========================================================================* * rl_getstat_s * *===========================================================================*/ static void rl_getstat_s(mp) message *mp; { int r, port; eth_stat_t stats; rl_t *rlp; port = mp->DL_PORT; if (port < 0 || port >= RL_PORT_NR) panic("rtl8111", "illegal port", port); rlp = &rl_table[port]; rlp->rl_client = mp->DL_PROC; assert(rlp->rl_mode == REM_ENABLED); assert(rlp->rl_flags & REF_ENABLED); stats = rlp->rl_stat; r = sys_safecopyto(mp->DL_PROC, mp->DL_GRANT, 0, (vir_bytes) &stats, sizeof(stats), D); if (r != OK) panic(__FILE__, "rl_getstat_s: sys_safecopyto failed", r); mp->m_type = DL_STAT_REPLY; mp->DL_PORT = port; mp->DL_STAT = OK; r = send(mp->m_source, mp); if (r != OK) panic("RTL8111", "rl_getstat_s: send failed: %d\n", r); } /*===========================================================================* * rl_do_reset * *===========================================================================*/ static void rl_do_reset(rlp) rl_t *rlp; { rlp->rl_need_reset = FALSE; rl_reset_hw(rlp); rl_rec_mode(rlp); rlp->rl_tx_head = 0; if (rlp->rl_flags & REF_SEND_AVAIL) { rlp->rl_tx[rlp->rl_tx_head].ret_busy = FALSE; rlp->rl_send_int = TRUE; } } /*===========================================================================* * rl_check_ints * *===========================================================================*/ static void rl_check_ints(rlp) rl_t *rlp; { int rl_flags; rl_flags = rlp->rl_flags; /* printf("%s: rl_flags = 0x%x\n", rlp->rl_name, rl_flags); */ if ((rl_flags & REF_READING) && !(rl_inb(rlp->rl_base_port, RL_CR) & RL_CR_BUFE)) { assert(rlp->rl_rx_mess.m_type != NULL); assert(rlp->rl_rx_mess.m_type == DL_READV_S); rl_readv_s(&rlp->rl_rx_mess, TRUE /* from int */); } if (rlp->rl_need_reset) rl_do_reset(rlp); if (rlp->rl_send_int) { assert(rlp->rl_tx_mess.m_type == DL_WRITEV_S); rl_writev_s(&rlp->rl_tx_mess, TRUE /* from int */); } if (rlp->rl_report_link) rl_report_link(rlp); if (rlp->rl_flags & (REF_PACK_SENT | REF_PACK_RECV)) reply(rlp, OK, TRUE); } /*===========================================================================* * rl_getname * *===========================================================================*/ static void rl_getname(mp) message *mp; { int r; strncpy(mp->DL_NAME, progname, sizeof(mp->DL_NAME)); mp->DL_NAME[sizeof(mp->DL_NAME)-1] = '\0'; mp->m_type = DL_NAME_REPLY; r = send(mp->m_source, mp); if (r != OK) panic("RTL8111", "rl_getname: send failed: %d\n", r); } /*===========================================================================* * rl_handler * *===========================================================================*/ static int rl_handler(rlp) rl_t *rlp; { int i, port, tx_head, tx_tail, link_up; u16_t isr; rl_desc *desc; int_event_check = FALSE; /* disable check by default */ port = rlp->rl_base_port; /* Ack interrupt */ isr = rl_inw(port, RL_ISR); if(!isr) return 0; rl_outw(port, RL_ISR, isr); rlp->interrupts++; if (isr & RL_IMR_FOVW) { isr &= ~RL_IMR_FOVW; /* Should do anything? */ rlp->rl_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_PHYSTAT) & RL_STAT_LINK)); if (link_up != rlp->rl_link_up) { rlp->rl_report_link = TRUE; rlp->rl_got_int = TRUE; int_event_check = TRUE; } } if (isr & (RL_ISR_RDU | RL_ISR_RER | RL_ISR_ROK)) { if (isr & RL_ISR_RER) rlp->rl_stat.ets_recvErr++; isr &= ~(RL_ISR_RDU | RL_ISR_RER | RL_ISR_ROK); if (!rlp->rl_got_int && (rlp->rl_flags & REF_READING)) { rlp->rl_got_int = TRUE; int_event_check = TRUE; } } if ((isr & (RL_ISR_TDU | RL_ISR_TER | RL_ISR_TOK)) || 1) { if (isr & RL_ISR_TER) rlp->rl_stat.ets_sendErr++; isr &= ~(RL_ISR_TDU | RL_ISR_TER | RL_ISR_TOK); /* Transmit completed */ tx_head = rlp->rl_tx_head; tx_tail = tx_head+1; if (tx_tail >= N_TX_DESC) tx_tail = 0; for (i = 0; i < 2 * N_TX_DESC; i++) { if (!rlp->rl_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_DESC) tx_tail = 0; assert(tx_tail < N_TX_DESC); continue; } desc = rlp->rl_tx_desc; desc += tx_tail; if (desc->status & DESC_OWN) { /* Buffer is not yet ready */ break; } rlp->rl_stat.ets_packetT++; rlp->rl_tx[tx_tail].ret_busy = FALSE; if (++tx_tail >= N_TX_DESC) tx_tail = 0; assert(tx_tail < N_TX_DESC); if (rlp->rl_flags & REF_SEND_AVAIL) { rlp->rl_send_int = TRUE; if (!rlp->rl_got_int) { rlp->rl_got_int = TRUE; int_event_check = TRUE; } } } assert(i < 2 * N_TX_DESC); } /* Ignore Reserved Interrupt */ if (isr & RL_ISR_RES) isr &= ~RL_ISR_RES; 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; { int i; rl_t *rlp; /* Use a synchronous alarm instead of a watchdog timer. */ sys_setalarm(system_hz, 0); for (i = 0, rlp = &rl_table[0]; i < RL_PORT_NR; i++, rlp++) { if (rlp->rl_mode != REM_ENABLED) continue; /* Should collect statistics */ if (!(++rlp->dtcc_counter % RL_DTCC_VALUE)) rtl8111_update_stat(rlp); if (!(rlp->rl_flags & REF_SEND_AVAIL)) { /* Assume that an idle system is alive */ rlp->rl_tx_alive = TRUE; continue; } if (rlp->rl_tx_alive) { rlp->rl_tx_alive = FALSE; continue; } printf("rl_watchdog_f: resetting port %d mode 0x%x flags 0x%x\n", i, rlp->rl_mode, rlp->rl_flags); printf("tx_head :%8d busy %d\t", rlp->rl_tx_head, rlp->rl_tx[rlp->rl_tx_head].ret_busy); rlp->rl_need_reset = TRUE; rlp->rl_got_int = TRUE; check_int_events(); } #if 0 struct ifnet *ifp; RL_LOCK_ASSERT(sc); if (sc->rl_watchdog_timer == 0 || --sc->rl_watchdog_timer != 0) return; ifp = sc->rl_ifp; rl_txeof(sc); if (sc->rl_ldata.rl_tx_free == sc->rl_ldata.rl_tx_desc_cnt) { if_printf(ifp, "watchdog timeout (missed Tx interrupts) " "-- recovering\n"); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) rl_start_locked(ifp); return; } if_printf(ifp, "watchdog timeout\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); rl_rxeof(sc, NULL); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; rl_init_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) rl_start_locked(ifp); #endif } /*===========================================================================* * reply * *===========================================================================*/ static void reply(rlp, err, may_block) rl_t *rlp; int err; int may_block; { message reply; int status; int r; clock_t now; status = 0; if (rlp->rl_flags & REF_PACK_SENT) status |= DL_PACK_SEND; if (rlp->rl_flags & REF_PACK_RECV) status |= DL_PACK_RECV; reply.m_type = DL_TASK_REPLY; reply.DL_PORT = rlp - rl_table; reply.DL_PROC = rlp->rl_client; reply.DL_STAT = status | ((u32_t) err << 16); reply.DL_COUNT = rlp->rl_read_s; if (OK != (r = getuptime(&now))) panic("rtl8111", "getuptime() failed:", r); reply.DL_CLCK = now; r = send(rlp->rl_client, &reply); if (r == ELOCKED && may_block) { printW(); printf("send locked\n"); return; } if (r < 0) { printf("RTL8111 tried sending to %d, type %d\n", rlp->rl_client, reply.m_type); panic("rtl8111", "send failed:", r); } rlp->rl_read_s = 0; rlp->rl_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("rtl8111", "unable to mess_reply", NO_NUM); } static void dump_phy(rl_t *rlp) { #if VERBOSE port_t port; u32_t t; port = rlp->rl_base_port; t = rl_inb(port, RL_CONFIG0); printf("CONFIG0\t\t:"); t = t & RL_CFG0_ROM; if (t == RL_CFG0_ROM128K) printf(" 128K Boot ROM"); else if (t == RL_CFG0_ROM64K) printf(" 64K Boot ROM"); else if (t == RL_CFG0_ROM32K) printf(" 32K Boot ROM"); else if (t == RL_CFG0_ROM16K) printf(" 16K Boot ROM"); else if (t == RL_CFG0_ROM8K) printf(" 8K Boot ROM"); else if (t == RL_CFG0_ROMNO) printf(" No Boot ROM"); printf("\n"); t = rl_inb(port, RL_CONFIG1); printf("CONFIG1\t\t:"); if (t & RL_CFG1_LEDS1) printf(" LED1"); if (t & RL_CFG1_LEDS0) printf(" LED0"); if (t & RL_CFG1_DVRLOAD) printf(" Driver"); if (t & RL_CFG1_LWACT) printf(" LWAKE"); if (t & RL_CFG1_IOMAP) printf(" IOMAP"); if (t & RL_CFG1_MEMMAP) printf(" MEMMAP"); if (t & RL_CFG1_VPD) printf(" VPD"); if (t & RL_CFG1_PME) printf(" PME"); printf("\n"); t = rl_inb(port, RL_CONFIG2); printf("CONFIG2\t\t:"); if (t & RL_CFG2_AUX) printf(" AUX"); if (t & RL_CFG2_PCIBW) printf(" PCI-64-Bit"); else printf(" PCI-32-Bit"); t = t & RL_CFG2_PCICLK; if (t == RL_CFG2_66MHZ) printf(" 66 MHz"); else if (t == RL_CFG2_33MHZ) printf(" 33 MHz"); printf("\n"); t = mdio_read(port, MII_CTRL); printf("MII_CTRL\t:"); if (t & MII_CTRL_RST) printf(" Reset"); if (t & MII_CTRL_LB) printf(" Loopback"); if (t & MII_CTRL_ANE) printf(" ANE"); if (t & MII_CTRL_PD) printf(" Power-down"); if (t & MII_CTRL_ISO) printf(" Isolate"); if (t & MII_CTRL_RAN) printf(" RAN"); if (t & MII_CTRL_DM) printf(" Full-duplex"); if (t & MII_CTRL_CT) printf(" COL-signal"); t = t & (MII_CTRL_SP_LSB | MII_CTRL_SP_MSB); if (t == MII_CTRL_SP_10) printf(" 10 Mb/s"); else if (t == MII_CTRL_SP_100) printf(" 100 Mb/s"); else if (t == MII_CTRL_SP_1000) printf(" 1000 Mb/s"); printf("\n"); t = mdio_read(port, MII_STATUS); printf("MII_STATUS\t:"); if (t & MII_STATUS_100T4) printf(" 100Base-T4"); if (t & MII_STATUS_100XFD) printf(" 100BaseX-FD"); if (t & MII_STATUS_100XHD) printf(" 100BaseX-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 = mdio_read(port, MII_ANA); printf("MII_ANA\t\t: 0x%04lx\n", t); t = mdio_read(port, MII_ANLPA); printf("MII_ANLPA\t: 0x%04lx\n", t); t = mdio_read(port, MII_ANE); printf("MII_ANE\t\t:"); 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 = mdio_read(port, MII_1000_CTRL); printf("MII_1000_CTRL\t:"); if (t & MII_1000C_FULL) printf(" 1000BaseT-FD"); if (t & MII_1000C_HALF) printf(" 1000BaseT-HD"); printf("\n"); t = mdio_read(port, MII_1000_STATUS); if (t) { printf("MII_1000_STATUS\t:"); if (t & MII_1000S_LRXOK) printf(" Local-Receiver"); if (t & MII_1000S_RRXOK) printf(" Remote-Receiver"); if (t & MII_1000S_HALF) printf(" 1000BaseT-HD"); if (t & MII_1000S_FULL) printf(" 1000BaseT-FD"); printf("\n"); t = mdio_read(port, MII_EXT_STATUS); printf("MII_EXT_STATUS\t:"); if (t & MII_ESTAT_1000XFD) printf(" 1000BaseX-FD"); if (t & MII_ESTAT_1000XHD) printf(" 1000BaseX-HD"); if (t & MII_ESTAT_1000TFD) printf(" 1000BaseT-FD"); if (t & MII_ESTAT_1000THD) printf(" 1000BaseT-HD"); printf("\n"); } #endif } static void do_hard_int(void) { int i, s; for (i = 0; i < RL_PORT_NR; i++) { /* Run interrupt handler at driver level. */ rl_handler(&rl_table[i]); /* Reenable interrupts for this hook. */ if ((s = sys_irqenable(&rl_table[i].rl_hook_id)) != OK) printf("RTL8111: error, couldn't enable interrupts: %d\n", s); } } static unsigned my_inb(U16_t port) { u32_t value; int s; if ((s = sys_inb(port, &value)) != OK) printf("RTL8111: 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("RTL8111: 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("RTL8111: warning, sys_inl failed: %d\n", s); return value; } static void my_outb(U16_t port, U8_t value) { int s; if ((s = sys_outb(port, value)) != OK) printf("RTL8111: 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("RTL8111: 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("RTL8111: warning, sys_outl failed: %d\n", s); } static void mdio_write(U16_t port, int regaddr, int value) { int i; rl_outl(port, RL_PHYAR, 0x80000000 | (regaddr & 0x1F) << 16 | (value & 0xFFFF)); for (i = 20; i > 0; i--) { /* * Check if the RTL8111 has completed writing to the specified * MII register */ if (!(rl_inl(port, RL_PHYAR) & 0x80000000)) break; else micro_delay(50); } } static int mdio_read(U16_t port, int regaddr) { int i, value = -1; rl_outl(port, RL_PHYAR, (regaddr & 0x1F) << 16); for (i = 20; i > 0; i--) { /* * Check if the RTL8111 has completed retrieving data from * the specified MII register */ if (rl_inl(port, RL_PHYAR) & 0x80000000) { value = (int)(rl_inl(port, RL_PHYAR) & 0xFFFF); break; } else micro_delay(50); } return value; } #if 0 static __inline void bus_space_write_1(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t offset, u_int8_t value) { if (tag == X86_BUS_SPACE_IO) outb(bsh + offset, value); else *(volatile u_int8_t *)(bsh + offset) = value; } #endif #define CSR_WRITE_4(rl, reg, val) rl_outl(rl->rl_base_port, reg, val) #define CSR_WRITE_2(rl, reg, val) rl_outw(rl->rl_base_port, reg, val) #define CSR_WRITE_1(rl, reg, val) rl_outb(rl->rl_base_port, reg, val) #define CSR_READ_4(rl, reg) rl_inl(rl->rl_base_port, reg) #define CSR_READ_2(rl, reg) rl_inw(rl->rl_base_port, reg) #define CSR_READ_1(rl, reg) rl_inb(rl->rl_base_port, reg) #define CSR_SETBIT_1(sc, offset, val) \ CSR_WRITE_1(sc, offset, CSR_READ_1(sc, offset) | (val)) #define CSR_CLRBIT_1(sc, offset, val) \ CSR_WRITE_1(sc, offset, CSR_READ_1(sc, offset) & ~(val)) #define EE_SET(x) \ CSR_WRITE_1(rl, RL_EECMD, CSR_READ_1(rl, RL_EECMD) | x) #define EE_CLR(x) \ CSR_WRITE_1(rl, RL_EECMD, CSR_READ_1(rl, RL_EECMD) & ~x) /* * Send a read command and address to the EEPROM, check for ACK. */ static void rl_eeprom_putbyte(struct rl_t *rl, u32_t addr) { int d, i; d = addr | (RL_9346_READ << rl->rl_eewidth); /* * Feed in each bit and strobe the clock. */ for (i = 1 << (rl->rl_eewidth + 3); i; i >>= 1) { if (d & i) { EE_SET(RL_EE_DATAIN); } else { EE_CLR(RL_EE_DATAIN); } DELAY(100); EE_SET(RL_EE_CLK); DELAY(150); EE_CLR(RL_EE_CLK); DELAY(100); } } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void rl_eeprom_getword(struct rl_t *rl, u32_t addr, u16_t *dest) { u32_t i; u16_t word = 0; /* * Send address of word we want to read. */ rl_eeprom_putbyte(rl, addr); /* * Start reading bits from EEPROM. */ for (i = 0x8000; i; i >>= 1) { EE_SET(RL_EE_CLK); DELAY(100); if (CSR_READ_1(rl, RL_EECMD) & RL_EE_DATAOUT) word |= i; EE_CLR(RL_EE_CLK); DELAY(100); } *dest = word; } /* * Read a sequence of words from the EEPROM. */ static void rl_read_eeprom(struct rl_t *rl, caddr_t dest, u32_t off, u32_t cnt) { u32_t i; u16_t word = 0, *ptr; CSR_SETBIT_1(rl, RL_EECMD, RL_EEMODE_PROGRAM); DELAY(100); for (i = 0; i < cnt; i++) { CSR_SETBIT_1(rl, RL_EECMD, RL_EE_SEL); rl_eeprom_getword(rl, off + i, &word); CSR_CLRBIT_1(rl, RL_EECMD, RL_EE_SEL); ptr = (u16_t *)(dest + (i * 2)); *ptr = word; } CSR_CLRBIT_1(rl, RL_EECMD, RL_EEMODE_PROGRAM); } /***************************************************************************** * check_int_events * * * * If a hard interrupt message came in, call the or_check_ints for the right * * card * *****************************************************************************/ static void check_int_events(void) { int i; rl_t *rlp; /* the interrupt message doesn't contain information about the port, try * to find it */ for (rlp = rl_table; rlp< rl_table + RL_PORT_NR; rlp++) { if (rlp->rl_mode != REM_ENABLED) continue; if (!rlp->rl_got_int) continue; rlp->rl_got_int = 0; assert (rlp->rl_flags & REF_ENABLED); rl_check_ints (rlp); } }