/*- * Copyright (c) 2011 Rick van der Zwet * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /*- * Copyright (c) 2001-2003, Shunsuke Akiyama . * Copyright (c) 1997, 1998, 1999, 2000 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 reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ /*- * Copyright (c) 1997, 1998, 1999, 2000 * 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 reproduce 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. */ #include __FBSDID("$FreeBSD: release/10.1.0/sys/dev/usb/net/if_rtl.c 251734 2013-06-14 05:36:47Z kevlo $"); /* * RealTek RTL8152 USB to fast ethernet controller driver. * http://www.realtek.com.tw/products/productsView.aspx?Langid=1&PFid=55&Level=5&Conn=4&ProdID=323 * * /TODO: With some modification the RealTek RTL8153 USB to fast ethernet * controller driver. should also be supported, but yet no hardware to test it * http://www.realtek.com.tw/products/productsView.aspx?Langid=1&PFid=56&Level=5&Conn=4&ProdID=326 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR rtl_debug #include #include #include #include #ifdef USB_DEBUG static int rtl_debug = 1; static SYSCTL_NODE(_hw_usb, OID_AUTO, rtl, CTLFLAG_RW, 0, "USB rtl"); SYSCTL_INT(_hw_usb_rtl, OID_AUTO, debug, CTLFLAG_RW, &rtl_debug, 0, "Debug level"); #endif /* * Various supported device vendors/products. */ static const STRUCT_USB_HOST_ID rtl_devs[] = { {USB_VPI(USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_RTL8152, 0)}, }; /* prototypes */ static device_probe_t rtl_probe; static device_attach_t rtl_attach; static device_detach_t rtl_detach; static miibus_readreg_t rtl_miibus_readreg; static miibus_writereg_t rtl_miibus_writereg; static miibus_statchg_t rtl_miibus_statchg; static usb_callback_t rtl_intr_callback; static usb_callback_t rtl_bulk_read_callback; static usb_callback_t rtl_bulk_write_callback; static uether_fn_t rtl_attach_post; static uether_fn_t rtl_init; static uether_fn_t rtl_stop; static uether_fn_t rtl_start; static uether_fn_t rtl_tick; static uether_fn_t rtl_setpromisc_and_multi; static void rtl_reset(struct rtl_softc *); static int rtl_ifmedia_upd(struct ifnet *); static void rtl_ifmedia_sts(struct ifnet *, struct ifmediareq *); static const struct usb_config rtl_config[RTL_N_TRANSFER] = { [RTL_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = 16384, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = rtl_bulk_read_callback, .timeout = 0, /* no timeout */ }, [RTL_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = 16384, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = rtl_bulk_write_callback, .timeout = 1000, /* 1 seconds */ }, [RTL_INTR_DT_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = rtl_intr_callback, }, }; static device_method_t rtl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rtl_probe), DEVMETHOD(device_attach, rtl_attach), DEVMETHOD(device_detach, rtl_detach), /* MII interface */ DEVMETHOD(miibus_readreg, rtl_miibus_readreg), DEVMETHOD(miibus_writereg, rtl_miibus_writereg), DEVMETHOD(miibus_statchg, rtl_miibus_statchg), DEVMETHOD_END }; static driver_t rtl_driver = { .name = "rtl", .methods = rtl_methods, .size = sizeof(struct rtl_softc), }; static devclass_t rtl_devclass; DRIVER_MODULE_ORDERED(rtl, uhub, rtl_driver, rtl_devclass, NULL, NULL, SI_ORDER_ANY); DRIVER_MODULE(miibus, rtl, miibus_driver, miibus_devclass, NULL, NULL); MODULE_DEPEND(rtl, uether, 1, 1, 1); MODULE_DEPEND(rtl, usb, 1, 1, 1); MODULE_DEPEND(rtl, ether, 1, 1, 1); MODULE_DEPEND(rtl, miibus, 1, 1, 1); MODULE_VERSION(rtl, 1); static const struct usb_ether_methods rtl_ue_methods = { .ue_attach_post = rtl_attach_post, .ue_start = rtl_start, .ue_init = rtl_init, .ue_stop = rtl_stop, .ue_tick = rtl_tick, .ue_setmulti = rtl_setpromisc_and_multi, .ue_setpromisc = rtl_setpromisc_and_multi, .ue_mii_upd = rtl_ifmedia_upd, .ue_mii_sts = rtl_ifmedia_sts, }; static int generic_ocp_read(struct rtl_softc *sc, uint16_t index, uint16_t addr, void *buf, int len) { struct usb_device_request req; // TODO: This seems to be a global defined variable uint16_t limit = 64; int ret = 0; /* both size and indix must be 4 bytes align */ // TODO: Argue whether are supposed to be non-fatal values if ((len & 3) || !len || (index & 3) || (!buf)) { printf("[foo] generic_ocp_read(len=%x index=%x): not alligned error\n", len, index); return (ENXIO); } if ((uint32_t)index + (uint32_t)addr > 0xffff) { printf("[foo] generic_ocp_read(index=%x addr=%x): overflow error\n", len, index); return (ENXIO); } //TODO: Deal with the fact if len > limit (block fetching) if (len > limit) { printf("[foo] generic_ocp_read(len=%x limit=%x): size overflow\n", len, limit); return (ENXIO); } req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wIndex, index); USETW(req.wValue, addr); USETW(req.wLength, len); ret = uether_do_request(&sc->sc_ue, &req, buf, 500); return ret; } static int set_registers(struct rtl_softc *sc, uint16_t index, uint16_t addr, void *buf, int len) { struct usb_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wIndex, index); USETW(req.wValue, addr); USETW(req.wLength, len); //TODO: Make timeout some kind of define global? return (uether_do_request(&sc->sc_ue, &req, buf, 500)); } static int ocp_write(struct rtl_softc *sc, uint16_t index, uint16_t addr, uint16_t byteen, void *buf, int len) { uint16_t byteen_start, byteen_end, byen; uint16_t limit = 512; int ret; /* both size and indix must be 4 bytes align */ // TODO: Argue whether are supposed to be non-fatal values if ((len & 3) || !len || (index & 3) || (!buf)) { printf("[foo] ocp_write(len=%x index=%x): not alligned error\n", len, index); return (ENXIO); } if ((uint32_t)index + (uint32_t)addr > 0xffff) { printf("[foo] ocp_write(index=%x addr=%x): overflow error\n", len, index); return (ENXIO); } //TODO: Deal with the fact if len > limit (block fetching) if (len > limit) { printf("[foo] ocp_write(len=%x limit=%x): size overflow\n", len, limit); return (ENXIO); } byteen_start = byteen & BYTE_EN_START_MASK; byteen_end = byteen & BYTE_EN_END_MASK; byen = byteen_start | (byteen_start << 4); ret = set_registers(sc, index, addr | byen , buf, 4); if (ret < 0) return ret; //TODO: Implement if size > limit return ret; } static uint8_t ocp_read_1(struct rtl_softc *sc, uint16_t index, uint16_t addr) { uint32_t data; //TODO: __le32 tmp; uint32_t tmp; generic_ocp_read(sc, index, addr & ~3, &tmp, 4); //TODO: data = __le32_to_cpu(tmp); data = tmp; data >>= ((addr & 3) * 8); data &= 0xff; //printf("[foo] ocp_read_1(index=%x, addr=%x) => 0x%x\n", index, addr, data); return (uint8_t)data; } static uint16_t ocp_read_2(struct rtl_softc *sc, uint16_t index, uint16_t addr) { uint32_t data; //TODO: __le32 tmp; uint32_t tmp; generic_ocp_read(sc, index, addr & ~3, &tmp, 4); //TODO: data = __le32_to_cpu(tmp); data = tmp; data >>= ((addr & 2) * 8); data &= 0xffff; //printf("[foo] ocp_read_2(index=%x, addr=%x) => tmp=%x data=%x\n", index, addr, tmp, data); return (uint16_t)data; } static uint32_t ocp_read_4(struct rtl_softc *sc, uint16_t index, uint16_t addr) { uint32_t data; //TODO: __le32 tmp; uint32_t tmp; generic_ocp_read(sc, index, addr, &tmp, 4); //TODO: data = __le32_to_cpu(tmp); data = tmp; return data; } static int ocp_write_1(struct rtl_softc *sc, uint16_t index, uint16_t addr, uint8_t val) { uint32_t mask = 0xff; //TODO: __le32 tmp; uint32_t tmp; uint16_t byten = BYTE_EN_BYTE; uint8_t shift = addr & 3; uint32_t data = val; int ret; data &= mask; //printf("[foo] ocp_write_1(index=%x, addr=%x, val=%x)\n", index, addr, val); if (index & 3) { byten <<= shift; mask <<= (shift * 8); data <<= (shift * 8); addr &= ~3; } ret = generic_ocp_read(sc, index, addr, &tmp, 4); if (ret < 0) return ret; //TODO: data |= __le32_to_cpu(tmp) & ~mask; data |= tmp & ~mask; //TODO: tmp = __cpu_to_le32(data); tmp = data; return (ocp_write(sc, index, addr, byten, &tmp, 4)); } static int ocp_write_2(struct rtl_softc *sc, uint16_t index, uint16_t addr, uint16_t val) { uint32_t mask = 0xffff; //TODO: __le32 tmp; uint32_t tmp; uint16_t byten = BYTE_EN_WORD; uint8_t shift = addr & 2; uint32_t data = val; int ret; printf("[foo] ocp_write_2(index=%x, addr=%x, val=%x)\n", index, addr, val); data &= mask; if (index & 2) { byten <<= shift; mask <<= (shift * 8); data <<= (shift * 8); addr &= ~3; } ret = generic_ocp_read(sc, index, addr, &tmp, 4); if (ret < 0) return ret; //TODO: data |= __le32_to_cpu(tmp) & ~mask; data |= tmp & ~mask; //TODO: tmp = __cpu_to_le32(data); tmp = data; return (ocp_write(sc, index, addr, byten, &tmp, 4)); } static int ocp_write_4(struct rtl_softc *sc, uint16_t index, uint16_t addr, uint32_t val) { //TODO: __le32 tmp; uint32_t tmp; //TODO: tmp = __le32_to_cpu(data); tmp = val; //printf("[foo] ocp_write_4(index=%x, addr=%x, val=%x)\n", index, addr, tmp); return (ocp_write(sc, index, addr, BYTE_EN_DWORD, &tmp, 4)); } static uint16_t ocp_reg_read(struct rtl_softc *sc, uint16_t addr) { uint16_t ocp_base = addr & 0xf000; uint16_t ocp_index = (addr & 0x0fff) | 0xb000; if (ocp_base != sc->ocp_base) { ocp_write_2(sc, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); sc->ocp_base = ocp_base; } return ocp_read_2(sc, MCU_TYPE_PLA, ocp_index); } static void ocp_reg_write(struct rtl_softc *sc, uint16_t addr, uint16_t val) { uint16_t ocp_base = addr & 0xf000; uint16_t ocp_index = (addr & 0x0fff) | 0xb000; if (ocp_base != sc->ocp_base) { ocp_write_2(sc, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); sc->ocp_base = ocp_base; } ocp_write_2(sc, MCU_TYPE_PLA, ocp_index, val); } static inline void r8152_mdio_write(struct rtl_softc *sc, uint32_t addr, uint32_t val) { ocp_reg_write(sc, OCP_BASE_MII + addr * 2, val); } static inline uint32_t r8152_mdio_read(struct rtl_softc *sc, uint32_t addr) { return ocp_reg_read(sc, OCP_BASE_MII + addr * 2); } static int rtl_miibus_readreg(device_t dev, int phy, int reg) { //printf("[foo] rtl_miibus_readreg(phy=%x, reg=%x)\n", phy, reg); struct rtl_softc *sc = device_get_softc(dev); uint16_t rval; int locked; if (phy != 0) /* RTL8152 supports PHY == 0, only */ return (0); locked = mtx_owned(&sc->sc_mtx); if (!locked) RTL_LOCK(sc); rval = r8152_mdio_read(sc, reg); if (!locked) RTL_UNLOCK(sc); //printf("[foo] rtl_miibus_readreg(phy=%x, reg=%x) => rval=%x\n", phy, reg, rval); return (rval); } static int rtl_miibus_writereg(device_t dev, int phy, int reg, int data) { printf("[foo] rtl_miibus_writereg(phy=%x reg=%x data=%x\n", phy, reg, data); return (0); struct rtl_softc *sc = device_get_softc(dev); int locked; if (phy != 0) /* RTL8152 supports PHY == 0, only */ return (0); locked = mtx_owned(&sc->sc_mtx); if (!locked) RTL_LOCK(sc); r8152_mdio_write(sc, reg, data); if (!locked) RTL_UNLOCK(sc); return (0); } static void rtl_miibus_statchg(device_t dev) { printf("[foo] %s\n", __func__); #if 0 struct rtl_softc *sc = device_get_softc(dev); struct mii_data *mii = GET_MII(sc); uint16_t bmcr; int locked; locked = mtx_owned(&sc->sc_mtx); if (!locked) RTL_LOCK(sc); RTL_CLRBIT(sc, RTL_CR, (RTL_CR_RE | RTL_CR_TE)); bmcr = rtl_csr_read_2(sc, RTL_BMCR); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) bmcr |= RTL_BMCR_SPD_SET; else bmcr &= ~RTL_BMCR_SPD_SET; if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) bmcr |= RTL_BMCR_DUPLEX; else bmcr &= ~RTL_BMCR_DUPLEX; rtl_csr_write_2(sc, RTL_BMCR, bmcr); RTL_SETBIT(sc, RTL_CR, (RTL_CR_RE | RTL_CR_TE)); if (!locked) RTL_UNLOCK(sc); #endif } //TODO: Make global defines static const int multicast_filter_limit = 32; //static const unsigned int agg_buf_sz = 16384; /* * Program the 64-bit multicast hash filter and promiscuous mode */ static void rtl_setpromisc_and_multi(struct usb_ether *ue) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); int h = 0; uint32_t hashes[2] = { 0, 0 }; uint32_t tmp[2]; uint32_t ocp_data; struct ifmultiaddr *ifma; int mcnt = 0; RTL_LOCK_ASSERT(sc, MA_OWNED); ocp_data = ocp_read_4(sc, MCU_TYPE_PLA, PLA_RCR); ocp_data &= ~RCR_ACPT_ALL; ocp_data |= RCR_AB | RCR_APM; /* Make multicast filter */ if_maddr_rlock(ifp); TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; hashes[h >> 5] |= 1 << (h & 31); mcnt++; } if_maddr_runlock(ifp); if (ifp->if_flags & IFF_PROMISC) { ocp_data |= RCR_AM | RCR_AAP; hashes[0] = 0xffffffff; hashes[1] = 0xffffffff; } else if ((mcnt > multicast_filter_limit) || (ifp->if_flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ ocp_data |= RCR_AM; hashes[0] = 0xffffffff; hashes[1] = 0xffffffff; } else if (mcnt > 0) { ocp_data |= RCR_AM; } //TODO: tmp[0] = __cpu_to_le32(swab32(mc_filter[1])); //TODO: tmp[1] = __cpu_to_le32(swab32(mc_filter[0])); tmp[0] = hashes[0]; tmp[1] = hashes[1]; ocp_write(sc, MCU_TYPE_PLA, PLA_MAR, BYTE_EN_DWORD, tmp, sizeof(tmp)); ocp_write_4(sc, MCU_TYPE_PLA, PLA_RCR, ocp_data); } static void rtl_reset(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); int i; ocp_write_1(sc, MCU_TYPE_PLA, PLA_CR, CR_RST); for (i = 0; i != RTL_TIMEOUT; i++) { if (uether_pause(&sc->sc_ue, hz / 1000)) break; if (!(ocp_read_1(sc, MCU_TYPE_PLA, PLA_CR) & CR_RST)) break; } if (i == RTL_TIMEOUT) device_printf(sc->sc_ue.ue_dev, "reset never completed\n"); uether_pause(&sc->sc_ue, hz / 100); } static void rtl_attach_post(struct usb_ether *ue) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = uether_getsc(ue); int ret; /* reset the adapter */ rtl_reset(sc); uint32_t ocp_data; uint16_t version; ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_TCR1); version = (uint16_t)(ocp_data & VERSION_MASK); switch (version) { case 0x4c00: sc->version = RTL_VER_01; break; case 0x4c10: sc->version = RTL_VER_02; break; case 0x5c00: sc->version = RTL_VER_03; break; case 0x5c10: sc->version = RTL_VER_04; break; case 0x5c20: sc->version = RTL_VER_05; break; default: printf("[foo] Unknown version 0x%04x\n", version); break; } printf("[foo] ocp_data=%x version=%x\n", ocp_data, version); /* Get station address from the EEPROM which needs to be aligned */ //TODO: Allign automatically(?) if (sc->version == RTL_VER_01) ret = generic_ocp_read(sc, MCU_TYPE_PLA, PLA_IDR, ue->ue_eaddr, 8); else ret = generic_ocp_read(sc, MCU_TYPE_PLA, PLA_BACKUP,ue->ue_eaddr, 8); printf("[foo] attach_post: Read MAC address (version=%x ret=%x mac=%s\n", sc->version, ret, ue->ue_eaddr); if (ret < 0) { //TODO: How-to handle failures and/or invalid MAC addresses printf("[foo] attach_post: Get ether addr fail\n"); //TODO: should we make a random MAC address? // } else if (!is_valid_ether_addr(ue->ue_eaddr)) { // TODO: If no MAC is returned the device is crashed and needs to be // power-cycled, how-to inform user? } else { /* Later versions needs to have the MAC copied to the working location */ if (sc->version != RTL_VER_01) { ocp_write_1(sc, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ocp_write(sc, MCU_TYPE_PLA, PLA_IDR, BYTE_EN_SIX_BYTES, ue->ue_eaddr, 8); ocp_write_1(sc, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); } } } /* * Probe for a RTL8150 chip. */ static int rtl_probe(device_t dev) { printf("[foo] %s\n", __func__); struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != RTL_CONFIG_IDX) return (ENXIO); if (uaa->info.bIfaceIndex != RTL_IFACE_IDX) return (ENXIO); return (usbd_lookup_id_by_uaa(rtl_devs, sizeof(rtl_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int rtl_attach(device_t dev) { printf("[foo] %s\n", __func__); struct usb_attach_arg *uaa = device_get_ivars(dev); struct rtl_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; uint8_t iface_index; int error; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); iface_index = RTL_IFACE_IDX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, rtl_config, RTL_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB transfers failed\n"); goto detach; } ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; ue->ue_mtx = &sc->sc_mtx; ue->ue_methods = &rtl_ue_methods; error = uether_ifattach(ue); if (error) { device_printf(dev, "could not attach interface\n"); goto detach; } return (0); /* success */ detach: rtl_detach(dev); return (ENXIO); /* failure */ } static int rtl_detach(device_t dev) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; usbd_transfer_unsetup(sc->sc_xfer, RTL_N_TRANSFER); uether_ifdetach(ue); mtx_destroy(&sc->sc_mtx); return (0); } static void rtl_intr_callback(struct usb_xfer *xfer, usb_error_t error) { //struct rtl_softc *sc = usbd_xfer_softc(xfer); //struct ifnet *ifp = uether_getifp(&sc->sc_ue); //struct rtl_intrpkt pkt; uint32_t pkt; struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); //printf("[foo] rtl_intr_callback USB_STATE=%i\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: //if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) && // actlen >= (int)sizeof(pkt)) { //if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, &pkt, sizeof(pkt)); //printf("[foo] rtl_intr_callback pkt=%x actlen=%i\n", pkt, actlen); //ifp->if_ierrors += pkt.rtl_rxlost_cnt; //ifp->if_ierrors += pkt.rtl_crcerr_cnt; //ifp->if_collisions += pkt.rtl_col_cnt; //} /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void rtl_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct rtl_softc *sc = usbd_xfer_softc(xfer); struct usb_ether *ue = &sc->sc_ue; //struct ifnet *ifp = uether_getifp(ue); struct usb_page_cache *pc; struct rx_desc rx_desc; //uint16_t status; int actlen; int pos = 0; unsigned int pkt_len; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); printf("[foo] rtl_bulk_read_callback USB_STATE=%i actlen=%d\n", USB_GET_STATE(xfer), actlen); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); while (actlen > pos) { usbd_copy_out(pc, pos, &rx_desc, sizeof(rx_desc)); pos += (sizeof rx_desc); pkt_len = rx_desc.opts1 & RX_LEN_MASK; printf("[foo] pkt_len=%d\n", pkt_len); //ETH_ZLEN (Min. octents in frame sans FCS) if (pkt_len < 60) break; // TODO: Should we flag this as errors if (actlen < (pos + pkt_len)) break; // TODO: Chipset supports CRC and VLAN tagging uether_rxbuf(ue, pc, pos, pkt_len - CRC_SIZE); pos += pkt_len; //TODO: Data get aligned? } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); uether_rxflush(ue); return; default: /* Error */ printf("[foo] bulk read error, %s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void rtl_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct rtl_softc *sc = usbd_xfer_softc(xfer); struct ifnet *ifp = uether_getifp(&sc->sc_ue); struct usb_page_cache *pc; struct mbuf *m; int pos; struct tx_desc tx_desc; printf("[foo] rtl_bulk_write_callback USB_STATE=%i\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: printf("[foo] rtl_bulk_write_callback: transfer complete\n"); DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: //if ((sc->sc_flags & RTL_FLAG_LINK) == 0) { // printf("[foo] rtl_bulk_write_callback: error no link!\n"); // /* // * don't send anything if there is no link ! // */ // return; //} IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) return; //if (m->m_pkthdr.len > MCLBYTES) // m->m_pkthdr.len = MCLBYTES; //temp_len = m->m_pkthdr.len; pos = 0; pc = usbd_xfer_get_frame(xfer, 0); // Header description // //TODO: Complete CRC int transport_offset = 0; tx_desc.opts1 = m->m_pkthdr.len | TX_FS | TX_LS; tx_desc.opts1 |= transport_offset << GTTCPHO_SHIFT; tx_desc.opts1 |= GTSENDV4; usbd_copy_in(pc, pos, &tx_desc, sizeof(tx_desc)); pos += sizeof(tx_desc); // Content usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len); pos += m->m_pkthdr.len; printf("[foo] rtl_bulk_write_callback len=%i\n", m->m_pkthdr.len); int i; for (i = 0; i < m->m_pkthdr.len; i++) printf("%x", m->m_data[i]); printf("\n"); usbd_xfer_set_frame_len(xfer, 0, pos); /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usbd_transfer_submit(xfer); return; default: /* Error */ printf("[foo] bulk write: transfer error, %s\n", usbd_errstr(error)); ifp->if_oerrors++; if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void rtl_tick(struct usb_ether *ue) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = uether_getsc(ue); struct mii_data *mii = GET_MII(sc); RTL_LOCK_ASSERT(sc, MA_OWNED); mii_tick(mii); if ((sc->sc_flags & RTL_FLAG_LINK) == 0 && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->sc_flags |= RTL_FLAG_LINK; rtl_start(ue); } } static void rtl8152_set_mac_address(struct usb_ether *ue) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = uether_getsc(ue); ocp_write_1(sc, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ocp_write(sc, MCU_TYPE_PLA, PLA_IDR, BYTE_EN_SIX_BYTES, ue->ue_eaddr, 8); ocp_write_1(sc, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); } static void r8152b_reset_packet_filter(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t ocp_data; ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_FMC); ocp_data &= ~FMC_FCR_MCU_EN; ocp_write_2(sc, MCU_TYPE_PLA, PLA_FMC, ocp_data); ocp_data |= FMC_FCR_MCU_EN; ocp_write_2(sc, MCU_TYPE_PLA, PLA_FMC, ocp_data); } static void rtl8152_nic_reset(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); int i; ocp_write_1(sc, MCU_TYPE_PLA, PLA_CR, CR_RST); for (i = 0; i < 1000; i++) { if (!(ocp_read_1(sc, MCU_TYPE_PLA, PLA_CR) & CR_RST)) break; uether_pause(&sc->sc_ue, hz / 100); } } static inline uint8_t rtl8152_get_speed(struct rtl_softc* sc) { printf("[foo] %s\n", __func__); return ocp_read_1(sc, MCU_TYPE_PLA, PLA_PHYSTATUS); } static void rtl_set_eee_plus(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t ocp_data; uint8_t speed; speed = rtl8152_get_speed(sc); //TODO: Find out which bits represent which speed //if (speed & _10bps) { // ocp_data = ocp_read_2(tp, MCU_TYPE_PLA, PLA_EEEP_CR); // ocp_data |= EEEP_CR_EEEP_TX; // ocp_write_2(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); //} else { ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_EEEP_CR); ocp_data &= ~EEEP_CR_EEEP_TX; ocp_write_2(sc, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); //} } static void rxdy_gated_en(struct rtl_softc *sc, bool enable) { printf("[foo] %s\n", __func__); uint32_t ocp_data; ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_MISC_1); if (enable) ocp_data |= RXDY_GATED_EN; else ocp_data &= ~RXDY_GATED_EN; ocp_write_2(sc, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); } static int rtl_enable(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t ocp_data; r8152b_reset_packet_filter(sc); ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_CR); ocp_data |= CR_RE | CR_TE; ocp_write_1(sc, MCU_TYPE_PLA, PLA_CR, ocp_data); rxdy_gated_en(sc, false); return 0; } static int rtl8152_enable(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); //set_tx_qlen(sc); rtl_set_eee_plus(sc); return rtl_enable(sc); } static void rtl_disable(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t ocp_data; int i; ocp_data = ocp_read_4(sc, MCU_TYPE_PLA, PLA_RCR); ocp_data &= ~RCR_ACPT_ALL; ocp_write_4(sc, MCU_TYPE_PLA, PLA_RCR, ocp_data); rxdy_gated_en(sc, true); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL); if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY) break; uether_pause(&sc->sc_ue, hz / 100); } for (i = 0; i < 1000; i++) { if (ocp_read_2(sc, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY) break; uether_pause(&sc->sc_ue, hz / 100); } rtl8152_nic_reset(sc); } static void r8152_power_cut_en(struct rtl_softc *sc, bool enable) { printf("[foo] %s\n", __func__); uint32_t ocp_data; ocp_data = ocp_read_2(sc, MCU_TYPE_USB, USB_UPS_CTRL); if (enable) ocp_data |= POWER_CUT; else ocp_data &= ~POWER_CUT; ocp_write_2(sc, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); ocp_data = ocp_read_2(sc, MCU_TYPE_USB, USB_PM_CTRL_STATUS); ocp_data &= ~RESUME_INDICATE; ocp_write_2(sc, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); } static void rtl_rx_vlan_en(struct rtl_softc *sc, bool enable) { printf("[foo] %s\n", __func__); uint32_t ocp_data; ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_CPCR); if (enable) ocp_data |= CPCR_RX_VLAN; else ocp_data &= ~CPCR_RX_VLAN; ocp_write_2(sc, MCU_TYPE_PLA, PLA_CPCR, ocp_data); } //TODO convert //#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) static uint32_t __rtl_get_wol(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t ocp_data; uint32_t wolopts = 0; ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_CONFIG5); if (!(ocp_data & LAN_WAKE_EN)) return 0; // ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_CONFIG34); // if (ocp_data & LINK_ON_WAKE_EN) // wolopts |= WAKE_PHY; // // ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_CONFIG5); // if (ocp_data & UWF_EN) // wolopts |= WAKE_UCAST; // if (ocp_data & BWF_EN) // wolopts |= WAKE_BCAST; // if (ocp_data & MWF_EN) // wolopts |= WAKE_MCAST; // // ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_CFG_WOL); // if (ocp_data & MAGIC_EN) // wolopts |= WAKE_MAGIC; return wolopts; } static void __rtl_set_wol(struct rtl_softc *sc, uint32_t wolopts) { printf("[foo] %s\n", __func__); uint32_t ocp_data; ocp_write_1(sc, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_CONFIG34); ocp_data &= ~LINK_ON_WAKE_EN; // if (wolopts & WAKE_PHY) // ocp_data |= LINK_ON_WAKE_EN; // ocp_write_2(sc, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); // // ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_CONFIG5); // ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN | LAN_WAKE_EN); // if (wolopts & WAKE_UCAST) // ocp_data |= UWF_EN; // if (wolopts & WAKE_BCAST) // ocp_data |= BWF_EN; // if (wolopts & WAKE_MCAST) // ocp_data |= MWF_EN; // if (wolopts & WAKE_ANY) // ocp_data |= LAN_WAKE_EN; // ocp_write_2(sc, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data); // // ocp_write_1(sc, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); // // ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_CFG_WOL); // ocp_data &= ~MAGIC_EN; // if (wolopts & WAKE_MAGIC) // ocp_data |= MAGIC_EN; // ocp_write_2(sc, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data); } static void rtl_runtime_suspend_enable(struct rtl_softc *sc, bool enable) { printf("[foo] %s\n", __func__); uint32_t ocp_data; if (enable) { //__rtl_set_wol(sc, WAKE_ANY); ocp_write_1(sc, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_CONFIG34); ocp_data |= LINK_OFF_WAKE_EN; ocp_write_2(sc, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); ocp_write_1(sc, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); } else { __rtl_set_wol(sc, sc->saved_wolopts); } } static void rtl_phy_reset(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t data; int i; data = r8152_mdio_read(sc, MII_BMCR); /* don't reset again before the previous one complete */ if (data & BMCR_RESET) return; data |= BMCR_RESET; r8152_mdio_write(sc, MII_BMCR, data); for (i = 0; i < 50; i++) { uether_pause(&sc->sc_ue, hz / 1000); if ((r8152_mdio_read(sc, MII_BMCR) & BMCR_RESET) == 0) break; } } static void r8153_teredo_off(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t ocp_data; ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_TEREDO_CFG); ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK | OOB_TEREDO_EN); ocp_write_2(sc, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); ocp_write_2(sc, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE); ocp_write_2(sc, MCU_TYPE_PLA, PLA_REALWOW_TIMER, 0); ocp_write_4(sc, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0); } static void r8152b_disable_aldps(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); ocp_reg_write(sc, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE); uether_pause(&sc->sc_ue, hz / 100); } static inline void r8152b_enable_aldps(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); ocp_reg_write(sc, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | LINKENA | DIS_SDSAVE); } static void rtl8152_disable(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); r8152b_disable_aldps(sc); rtl_disable(sc); r8152b_enable_aldps(sc); } static void r8152b_hw_phy_cfg(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint16_t data; data = r8152_mdio_read(sc, MII_BMCR); if (data & BMCR_PDOWN) { data &= ~BMCR_PDOWN; r8152_mdio_write(sc, MII_BMCR, data); } } static void r8152b_exit_oob(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t ocp_data; int i; ocp_data = ocp_read_4(sc, MCU_TYPE_PLA, PLA_RCR); ocp_data &= ~RCR_ACPT_ALL; ocp_write_4(sc, MCU_TYPE_PLA, PLA_RCR, ocp_data); rxdy_gated_en(sc, true); r8153_teredo_off(sc); r8152b_hw_phy_cfg(sc); ocp_write_1(sc, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ocp_write_1(sc, MCU_TYPE_PLA, PLA_CR, 0x00); ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data &= ~MCU_BORW_EN; ocp_write_2(sc, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; uether_pause(&sc->sc_ue, hz / 10); } ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_2(sc, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; uether_pause(&sc->sc_ue, hz / 10); } rtl8152_nic_reset(sc); /* rx share fifo credit full threshold */ ocp_write_4(sc, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); ocp_write_4(sc, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_FULL); ocp_write_4(sc, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_FULL); //if (tp->udev->speed == USB_SPEED_FULL || // tp->udev->speed == USB_SPEED_LOW) { // /* rx share fifo credit near full threshold */ // ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, // RXFIFO_THR2_FULL); // ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, // RXFIFO_THR3_FULL); //} else { // /* rx share fifo credit near full threshold */ // ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, // RXFIFO_THR2_HIGH); // ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, // RXFIFO_THR3_HIGH); //} /* TX share fifo free credit full threshold */ ocp_write_4(sc, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL); ocp_write_1(sc, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD); ocp_write_4(sc, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH); ocp_write_4(sc, MCU_TYPE_USB, USB_TX_DMA, TEST_MODE_DISABLE | TX_SIZE_ADJUST1); //rtl_rx_vlan_en(sc, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); ocp_write_2(sc, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_TCR0); ocp_data |= TCR0_AUTO_FIFO; ocp_write_2(sc, MCU_TYPE_PLA, PLA_TCR0, ocp_data); } static void r8152b_enter_oob(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t ocp_data; int i; ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; ocp_write_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ocp_write_4(sc, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_OOB); ocp_write_4(sc, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_OOB); ocp_write_4(sc, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_OOB); rtl_disable(sc); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; uether_pause(&sc->sc_ue, hz / 10); } ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_2(sc, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); for (i = 0; i < 1000; i++) { ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL); if (ocp_data & LINK_LIST_READY) break; uether_pause(&sc->sc_ue, hz / 10); } ocp_write_2(sc, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); rtl_rx_vlan_en(sc, true); ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PAL_BDC_CR); ocp_data |= ALDPS_PROXY_MODE; ocp_write_2(sc, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); ocp_data = ocp_read_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; ocp_write_1(sc, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); rxdy_gated_en(sc, false); ocp_data = ocp_read_4(sc, MCU_TYPE_PLA, PLA_RCR); ocp_data |= RCR_APM | RCR_AM | RCR_AB; ocp_write_4(sc, MCU_TYPE_PLA, PLA_RCR, ocp_data); } static void rtl8152_up(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); r8152b_disable_aldps(sc); r8152b_exit_oob(sc); r8152b_enable_aldps(sc); } static void rtl8152_down(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); r8152_power_cut_en(sc, false); r8152b_disable_aldps(sc); r8152b_enter_oob(sc); r8152b_enable_aldps(sc); } static inline void r8152_mmd_indirect(struct rtl_softc *sc, uint16_t dev, uint16_t reg) { printf("[foo] %s\n", __func__); ocp_reg_write(sc, OCP_EEE_AR, FUN_ADDR | dev); ocp_reg_write(sc, OCP_EEE_DATA, reg); ocp_reg_write(sc, OCP_EEE_AR, FUN_DATA | dev); } static uint16_t r8152_mmd_read(struct rtl_softc *sc, uint16_t dev, uint16_t reg) { printf("[foo] %s\n", __func__); uint16_t data; r8152_mmd_indirect(sc, dev, reg); data = ocp_reg_read(sc, OCP_EEE_DATA); ocp_reg_write(sc, OCP_EEE_AR, 0x0000); return data; } static void r8152_mmd_write(struct rtl_softc *sc, uint16_t dev, uint16_t reg, uint16_t data) { printf("[foo] %s\n", __func__); r8152_mmd_indirect(sc, dev, reg); ocp_reg_write(sc, OCP_EEE_DATA, data); ocp_reg_write(sc, OCP_EEE_AR, 0x0000); } static void r8152_eee_en(struct rtl_softc *sc, bool enable) { printf("[foo] %s\n", __func__); uint16_t config1, config2, config3; uint32_t ocp_data; ocp_data = ocp_read_4(sc, MCU_TYPE_PLA, PLA_EEE_CR); config1 = ocp_reg_read(sc, OCP_EEE_CONFIG1) & ~sd_rise_time_mask; config2 = ocp_reg_read(sc, OCP_EEE_CONFIG2); config3 = ocp_reg_read(sc, OCP_EEE_CONFIG3) & ~fast_snr_mask; if (enable) { ocp_data |= EEE_RX_EN | EEE_TX_EN; config1 |= EEE_10_CAP | EEE_NWAY_EN | TX_QUIET_EN | RX_QUIET_EN; //TODO: config1 |= sd_rise_time(1); config2 |= RG_DACQUIET_EN | RG_LDVQUIET_EN; //TODO: config3 |= fast_snr(42); } else { ocp_data &= ~(EEE_RX_EN | EEE_TX_EN); config1 &= ~(EEE_10_CAP | EEE_NWAY_EN | TX_QUIET_EN | RX_QUIET_EN); //TODO: config1 |= sd_rise_time(7); config2 &= ~(RG_DACQUIET_EN | RG_LDVQUIET_EN); //TODO: config3 |= fast_snr(511); } ocp_write_2(sc, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data); ocp_reg_write(sc, OCP_EEE_CONFIG1, config1); ocp_reg_write(sc, OCP_EEE_CONFIG2, config2); ocp_reg_write(sc, OCP_EEE_CONFIG3, config3); } static void r8152b_enable_eee(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); #define MDIO_MMD_AN 7 /* Auto-Negotiation */ #define MDIO_AN_EEE_ADV 60 /* EEE advertisement */ #define MDIO_EEE_100TX 0x0002 /* Advertise 100TX EEE cap */ r8152_eee_en(sc, true); r8152_mmd_write(sc, MDIO_MMD_AN, MDIO_AN_EEE_ADV, MDIO_EEE_100TX); } static void r8152b_enable_fc(struct rtl_softc *sc) { uint16_t anar; anar = r8152_mdio_read(sc, MII_ANAR); anar |= ANAR_FC | ANAR_X_PAUSE_ASYM; r8152_mdio_write(sc, MII_ANAR, anar); } static void rtl_tally_reset(struct rtl_softc *sc) { printf("[foo] %s\n", __func__); uint32_t ocp_data; ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_RSTTALLY); ocp_data |= TALLY_RESET; ocp_write_2(sc, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); } static void rtl_init(struct usb_ether *ue) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); uint32_t ocp_data; RTL_LOCK_ASSERT(sc, MA_OWNED); /* * Cancel pending I/O */ //rtl_stop(ue); /* Load the promiscuous & multicast filter */ rtl_setpromisc_and_multi(ue); /* * Set the initial TX and RX configuration. */ r8152b_disable_aldps(sc); if (sc->version == RTL_VER_01) { ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_LED_FEATURE); ocp_data &= ~LED_MODE_MASK; ocp_write_2(sc, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); } /* * Enable RX and TX */ r8152_power_cut_en(sc, false); ocp_data = ocp_read_2(sc, MCU_TYPE_PLA, PLA_PHY_PWR); ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH; ocp_write_2(sc, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); ocp_data = ocp_read_4(sc, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL); ocp_data &= ~MCU_CLK_RATIO_MASK; ocp_data |= MCU_CLK_RATIO | D3_CLK_GATED_EN; ocp_write_4(sc, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ocp_data); ocp_data = GPHY_STS_MSK | SPEED_DOWN_MSK | SPDWN_RXDV_MSK | SPDWN_LINKCHG_MSK; ocp_write_4(sc, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data); //r8152b_enable_eee(sc); r8152b_enable_aldps(sc); r8152b_enable_fc(sc); rtl_tally_reset(sc); /* enable rx aggregation */ ocp_data = ocp_read_2(sc, MCU_TYPE_USB, USB_USB_CTRL); ocp_data &= ~RX_AGG_DISABLE; ocp_write_2(sc, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); ifp->if_drv_flags |= IFF_DRV_RUNNING; rtl8152_up(sc); rtl_start(ue); } /* * Set media options. */ static int rtl_ifmedia_upd(struct ifnet *ifp) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); struct mii_softc *miisc; int error; RTL_LOCK_ASSERT(sc, MA_OWNED); sc->sc_flags &= ~RTL_FLAG_LINK; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) PHY_RESET(miisc); error = mii_mediachg(mii); return (error); } /* * Report current media status. */ static void rtl_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); RTL_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; RTL_UNLOCK(sc); } static void rtl_start(struct usb_ether *ue) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = uether_getsc(ue); /* * start the USB transfers, if not already started: */ usbd_transfer_start(sc->sc_xfer[RTL_INTR_DT_RD]); usbd_transfer_start(sc->sc_xfer[RTL_BULK_DT_RD]); usbd_transfer_start(sc->sc_xfer[RTL_BULK_DT_WR]); } static void rtl_stop(struct usb_ether *ue) { printf("[foo] %s\n", __func__); struct rtl_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); RTL_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; sc->sc_flags &= ~RTL_FLAG_LINK; /* * stop all the transfers, if not already stopped: */ usbd_transfer_stop(sc->sc_xfer[RTL_BULK_DT_WR]); usbd_transfer_stop(sc->sc_xfer[RTL_BULK_DT_RD]); usbd_transfer_stop(sc->sc_xfer[RTL_INTR_DT_RD]); rtl8152_down(sc); rtl_reset(sc); }