From owner-svn-src-head@freebsd.org Sat May 6 06:14:47 2017 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id C061DD61005; Sat, 6 May 2017 06:14:47 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 918021A25; Sat, 6 May 2017 06:14:47 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v466Ek8R087122; Sat, 6 May 2017 06:14:46 GMT (envelope-from adrian@FreeBSD.org) Received: (from adrian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v466Ekn4087118; Sat, 6 May 2017 06:14:46 GMT (envelope-from adrian@FreeBSD.org) Message-Id: <201705060614.v466Ekn4087118@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: adrian set sender to adrian@FreeBSD.org using -f From: Adrian Chadd Date: Sat, 6 May 2017 06:14:46 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r317872 - in head/sys: arm/conf arm/ralink boot/fdt/dts/arm X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 06 May 2017 06:14:47 -0000 Author: adrian Date: Sat May 6 06:14:46 2017 New Revision: 317872 URL: https://svnweb.freebsd.org/changeset/base/317872 Log: [arm] [rt1310] add initial RT1310 SoC code. This code base on lpc code. Ralink RT1310 is oem from 5V Technologies. RT1310 is ARM926EJS(arm5t). Tested: * Buffalo WZR2-G300N Submitted by: Hiroki Mori Reviewed by: mizhka Differential Revision: https://reviews.freebsd.org/D7238 Added: head/sys/arm/conf/RT1310 (contents, props changed) head/sys/arm/ralink/ head/sys/arm/ralink/files.ralink (contents, props changed) head/sys/arm/ralink/if_fv.c (contents, props changed) head/sys/arm/ralink/if_fvreg.h (contents, props changed) head/sys/arm/ralink/rt1310_gpio.c (contents, props changed) head/sys/arm/ralink/rt1310_intc.c (contents, props changed) head/sys/arm/ralink/rt1310_machdep.c (contents, props changed) head/sys/arm/ralink/rt1310_timer.c (contents, props changed) head/sys/arm/ralink/rt1310reg.h (contents, props changed) head/sys/arm/ralink/rt1310var.h (contents, props changed) head/sys/boot/fdt/dts/arm/rt1310a.dtsi (contents, props changed) head/sys/boot/fdt/dts/arm/wzr2-g300n.dts (contents, props changed) Added: head/sys/arm/conf/RT1310 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm/conf/RT1310 Sat May 6 06:14:46 2017 (r317872) @@ -0,0 +1,80 @@ +# +# Custom kernel for RT1310 boards. +# +# $FreeBSD$ +# + +ident RT1310 +include "std.arm" +hints "RT1310.hints" + +# Flattened Device Tree +options FDT +options FDT_DTB_STATIC +makeoptions FDT_DTS_FILE=wzr2-g300n.dts + +makeoptions MODULES_OVERRIDE="" + +#makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols +makeoptions WERROR="-Werror" + +options SCHED_4BSD # 4BSD scheduler +options INET # InterNETworking +options FFS # Berkeley Fast Filesystem +options TMPFS # Efficient memory filesystem +options MSDOSFS + +options ROOTDEVNAME=\"cd9660:/dev/cfid0s.rootfs.uzip\" + +options SYSVSHM # SYSV-style shared memory +options SYSVMSG # SYSV-style message queues +options SYSVSEM # SYSV-style semaphores +options _KPOSIX_PRIORITY_SCHEDULING # Posix P1003_1B real-time extensions +options MUTEX_NOINLINE +options RWLOCK_NOINLINE +options NO_FFS_SNAPSHOT +options NO_SWAPPING + +# Debugging +options ALT_BREAK_TO_DEBUGGER +options DDB +#options DEADLKRES # Enable the deadlock resolver +#options DIAGNOSTIC +#options INVARIANTS # Enable calls of extra sanity checking +#options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS +options KDB +options WITNESS # Enable checks to detect deadlocks and cycles +options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed +#options WITNESS_KDB + +# Pseudo devices +device loop +device md +device pty +device random + +# Serial ports +device uart +device uart_ns8250 + +# Flash +device cfi +device cfid + +# Networking +device ether +device mii +device bpf +device fv + +# etherswitch +device mdio +device etherswitch +device miiproxy +device ip17x + +# GPIO +device gpio +device gpioled +device rt1310gpio + Added: head/sys/arm/ralink/files.ralink ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm/ralink/files.ralink Sat May 6 06:14:46 2017 (r317872) @@ -0,0 +1,9 @@ +# $FreeBSD$ +arm/ralink/rt1310_machdep.c standard +arm/ralink/rt1310_intc.c standard +arm/ralink/rt1310_gpio.c optional rt1310gpio +arm/ralink/rt1310_timer.c standard +arm/ralink/if_fv.c optional fv + +kern/kern_clocksource.c standard + Added: head/sys/arm/ralink/if_fv.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm/ralink/if_fv.c Sat May 6 06:14:46 2017 (r317872) @@ -0,0 +1,1873 @@ +/*- + * Copyright (c) 2016 Hiroki Mori. All rights reserved. + * Copyright (C) 2007 + * Oleksandr Tymoshenko . 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 SOFTWFV IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE FV DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES 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 MIND, 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 SOFTWFV, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: $ + * + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * FV Ethernet interface driver + * copy from mips/idt/if_kr.c and netbsd code + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +/* Todo: move to options.arm */ +/*#define FV_MDIO*/ + +#ifdef FV_MDIO +#include +#include +#include "mdio_if.h" +#endif + +MODULE_DEPEND(are, ether, 1, 1, 1); +MODULE_DEPEND(are, miibus, 1, 1, 1); +#ifdef FV_MDIO +MODULE_DEPEND(are, mdio, 1, 1, 1); +#endif + +#include "miibus_if.h" + +#include + +#ifdef FV_DEBUG +void dump_txdesc(struct fv_softc *, int); +void dump_status_reg(struct fv_softc *); +#endif + +static int fv_attach(device_t); +static int fv_detach(device_t); +static int fv_ifmedia_upd(struct ifnet *); +static void fv_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int fv_ioctl(struct ifnet *, u_long, caddr_t); +static void fv_init(void *); +static void fv_init_locked(struct fv_softc *); +static void fv_link_task(void *, int); +static int fv_miibus_readreg(device_t, int, int); +static void fv_miibus_statchg(device_t); +static int fv_miibus_writereg(device_t, int, int, int); +static int fv_probe(device_t); +static void fv_reset(struct fv_softc *); +static int fv_resume(device_t); +static int fv_rx_ring_init(struct fv_softc *); +static int fv_tx_ring_init(struct fv_softc *); +static int fv_shutdown(device_t); +static void fv_start(struct ifnet *); +static void fv_start_locked(struct ifnet *); +static void fv_stop(struct fv_softc *); +static int fv_suspend(device_t); + +static void fv_rx(struct fv_softc *); +static void fv_tx(struct fv_softc *); +static void fv_intr(void *); +static void fv_tick(void *); + +static void fv_dmamap_cb(void *, bus_dma_segment_t *, int, int); +static int fv_dma_alloc(struct fv_softc *); +static void fv_dma_free(struct fv_softc *); +static int fv_newbuf(struct fv_softc *, int); +static __inline void fv_fixup_rx(struct mbuf *); + +static void fv_hinted_child(device_t bus, const char *dname, int dunit); + +static void fv_setfilt(struct fv_softc *sc); + +static device_method_t fv_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fv_probe), + DEVMETHOD(device_attach, fv_attach), + DEVMETHOD(device_detach, fv_detach), + DEVMETHOD(device_suspend, fv_suspend), + DEVMETHOD(device_resume, fv_resume), + DEVMETHOD(device_shutdown, fv_shutdown), + + /* MII interface */ + DEVMETHOD(miibus_readreg, fv_miibus_readreg), + DEVMETHOD(miibus_writereg, fv_miibus_writereg), +#if !defined(FV_MDIO) + DEVMETHOD(miibus_statchg, fv_miibus_statchg), +#endif + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + DEVMETHOD(bus_hinted_child, fv_hinted_child), + + DEVMETHOD_END +}; + +static driver_t fv_driver = { + "fv", + fv_methods, + sizeof(struct fv_softc) +}; + +static devclass_t fv_devclass; + +DRIVER_MODULE(fv, simplebus, fv_driver, fv_devclass, 0, 0); +#ifdef MII +DRIVER_MODULE(miibus, fv, miibus_driver, miibus_devclass, 0, 0); +#endif + +static struct mtx miibus_mtx; +MTX_SYSINIT(miibus_mtx, &miibus_mtx, "are mii lock", MTX_DEF); + +#ifdef FV_MDIO +static int fvmdio_probe(device_t); +static int fvmdio_attach(device_t); +static int fvmdio_detach(device_t); + +/* + * Declare an additional, separate driver for accessing the MDIO bus. + */ +static device_method_t fvmdio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fvmdio_probe), + DEVMETHOD(device_attach, fvmdio_attach), + DEVMETHOD(device_detach, fvmdio_detach), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + + /* MDIO access */ + DEVMETHOD(mdio_readreg, fv_miibus_readreg), + DEVMETHOD(mdio_writereg, fv_miibus_writereg), +}; + +DEFINE_CLASS_0(fvmdio, fvmdio_driver, fvmdio_methods, + sizeof(struct fv_softc)); +static devclass_t fvmdio_devclass; + +DRIVER_MODULE(miiproxy, fv, miiproxy_driver, miiproxy_devclass, 0, 0); +DRIVER_MODULE(fvmdio, simplebus, fvmdio_driver, fvmdio_devclass, 0, 0); +DRIVER_MODULE(mdio, fvmdio, mdio_driver, mdio_devclass, 0, 0); +#endif + +/* setup frame code refer dc code */ + +static void +fv_setfilt(struct fv_softc *sc) +{ + uint16_t eaddr[(ETHER_ADDR_LEN+1)/2]; + struct fv_desc *sframe; + int i; + struct ifnet *ifp; + struct ifmultiaddr *ifma; + uint16_t *sp; + uint8_t *ma; + + ifp = sc->fv_ifp; + + i = sc->fv_cdata.fv_tx_prod; + FV_INC(sc->fv_cdata.fv_tx_prod, FV_TX_RING_CNT); + sc->fv_cdata.fv_tx_cnt++; + sframe = &sc->fv_rdata.fv_tx_ring[i]; + sp = (uint16_t *)sc->fv_cdata.fv_sf_buff; + memset(sp, 0xff, FV_SFRAME_LEN); + + sframe->fv_addr = sc->fv_rdata.fv_sf_paddr; + sframe->fv_devcs = ADCTL_Tx_SETUP | FV_DMASIZE(FV_SFRAME_LEN); + + i = 0; + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + ma = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); + sp[i] = sp[i+1] = (ma[1] << 8 | ma[0]); + i += 2; + sp[i] = sp[i+1] = (ma[3] << 8 | ma[2]); + i += 2; + sp[i] = sp[i+1] = (ma[5] << 8 | ma[4]); + i += 2; + } + if_maddr_runlock(ifp); + + bcopy(IF_LLADDR(sc->fv_ifp), eaddr, ETHER_ADDR_LEN); + sp[90] = sp[91] = eaddr[0]; + sp[92] = sp[93] = eaddr[1]; + sp[94] = sp[95] = eaddr[2]; + + sframe->fv_stat = ADSTAT_OWN; + bus_dmamap_sync(sc->fv_cdata.fv_tx_ring_tag, + sc->fv_cdata.fv_tx_ring_map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->fv_cdata.fv_sf_tag, + sc->fv_cdata.fv_sf_buff_map, BUS_DMASYNC_PREWRITE); + CSR_WRITE_4(sc, CSR_TXPOLL, 0xFFFFFFFF); + DELAY(10000); +} + +static int +fv_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "fv,ethernet")) + return (ENXIO); + + device_set_desc(dev, "FV Ethernet interface"); + return (BUS_PROBE_DEFAULT); +} + +static int +fv_attach(device_t dev) +{ + struct ifnet *ifp; + struct fv_softc *sc; + int error = 0, rid; + int unit; + int i; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + sc->fv_dev = dev; + sc->fv_ofw = ofw_bus_get_node(dev); + + i = OF_getprop(sc->fv_ofw, "local-mac-address", (void *)&sc->fv_eaddr, 6); + if (i != 6) { + /* hardcode macaddress */ + sc->fv_eaddr[0] = 0x00; + sc->fv_eaddr[1] = 0x0C; + sc->fv_eaddr[2] = 0x42; + sc->fv_eaddr[3] = 0x09; + sc->fv_eaddr[4] = 0x5E; + sc->fv_eaddr[5] = 0x6B; + } + + mtx_init(&sc->fv_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init_mtx(&sc->fv_stat_callout, &sc->fv_mtx, 0); + TASK_INIT(&sc->fv_link_task, 0, fv_link_task, sc); + + /* Map control/status registers. */ + sc->fv_rid = 0; + sc->fv_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->fv_rid, + RF_ACTIVE | RF_SHAREABLE); + + if (sc->fv_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + error = ENXIO; + goto fail; + } + + sc->fv_btag = rman_get_bustag(sc->fv_res); + sc->fv_bhandle = rman_get_bushandle(sc->fv_res); + + /* Allocate interrupts */ + rid = 0; + sc->fv_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->fv_irq == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + error = ENXIO; + goto fail; + } + + /* Allocate ifnet structure. */ + ifp = sc->fv_ifp = if_alloc(IFT_ETHER); + + if (ifp == NULL) { + device_printf(dev, "couldn't allocate ifnet structure\n"); + error = ENOSPC; + goto fail; + } + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = fv_ioctl; + ifp->if_start = fv_start; + ifp->if_init = fv_init; + + /* ifqmaxlen is sysctl value in net/if.c */ + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + ifp->if_capenable = ifp->if_capabilities; + + if (fv_dma_alloc(sc) != 0) { + error = ENXIO; + goto fail; + } + + /* TODO: calculate prescale */ +/* + CSR_WRITE_4(sc, FV_ETHMCP, (165000000 / (1250000 + 1)) & ~1); + + CSR_WRITE_4(sc, FV_MIIMCFG, FV_MIIMCFG_R); + DELAY(1000); + CSR_WRITE_4(sc, FV_MIIMCFG, 0); +*/ + CSR_WRITE_4(sc, CSR_BUSMODE, BUSMODE_SWR); + DELAY(1000); + +#ifdef FV_MDIO + sc->fv_miiproxy = mii_attach_proxy(sc->fv_dev); +#endif + +#ifdef MII + /* Do MII setup. */ + error = mii_attach(dev, &sc->fv_miibus, ifp, fv_ifmedia_upd, + fv_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); + if (error != 0) { + device_printf(dev, "attaching PHYs failed\n"); + goto fail; + } +#else + ifmedia_init(&sc->fv_ifmedia, 0, fv_ifmedia_upd, fv_ifmedia_sts); + + ifmedia_add(&sc->fv_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); + ifmedia_set(&sc->fv_ifmedia, IFM_ETHER | IFM_AUTO); +#endif + + /* Call MI attach routine. */ + ether_ifattach(ifp, sc->fv_eaddr); + + /* Hook interrupt last to avoid having to lock softc */ + error = bus_setup_intr(dev, sc->fv_irq, INTR_TYPE_NET | INTR_MPSAFE, + NULL, fv_intr, sc, &sc->fv_intrhand); + + if (error) { + device_printf(dev, "couldn't set up irq\n"); + ether_ifdetach(ifp); + goto fail; + } + +fail: + if (error) + fv_detach(dev); + + return (error); +} + +static int +fv_detach(device_t dev) +{ + struct fv_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->fv_ifp; + + KASSERT(mtx_initialized(&sc->fv_mtx), ("vr mutex not initialized")); + + /* These should only be active if attach succeeded */ + if (device_is_attached(dev)) { + FV_LOCK(sc); + sc->fv_detach = 1; + fv_stop(sc); + FV_UNLOCK(sc); + taskqueue_drain(taskqueue_swi, &sc->fv_link_task); + ether_ifdetach(ifp); + } +#ifdef MII + if (sc->fv_miibus) + device_delete_child(dev, sc->fv_miibus); +#endif + bus_generic_detach(dev); + + if (sc->fv_intrhand) + bus_teardown_intr(dev, sc->fv_irq, sc->fv_intrhand); + if (sc->fv_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->fv_irq); + + if (sc->fv_res) + bus_release_resource(dev, SYS_RES_MEMORY, sc->fv_rid, + sc->fv_res); + + if (ifp) + if_free(ifp); + + fv_dma_free(sc); + + mtx_destroy(&sc->fv_mtx); + + return (0); + +} + +static int +fv_suspend(device_t dev) +{ + + panic("%s", __func__); + return 0; +} + +static int +fv_resume(device_t dev) +{ + + panic("%s", __func__); + return 0; +} + +static int +fv_shutdown(device_t dev) +{ + struct fv_softc *sc; + + sc = device_get_softc(dev); + + FV_LOCK(sc); + fv_stop(sc); + FV_UNLOCK(sc); + + return (0); +} + +static int +fv_miibus_readbits(struct fv_softc *sc, int count) +{ + int result; + + result = 0; + while(count--) { + result <<= 1; + CSR_WRITE_4(sc, CSR_MIIMNG, MII_RD); + DELAY(10); + CSR_WRITE_4(sc, CSR_MIIMNG, MII_RD | MII_CLK); + DELAY(10); + if (CSR_READ_4(sc, CSR_MIIMNG) & MII_DIN) + result |= 1; + } + + return (result); +} + +static int +fv_miibus_writebits(struct fv_softc *sc, int data, int count) +{ + int bit; + + while(count--) { + bit = ((data) >> count) & 0x1 ? MII_DOUT : 0; + CSR_WRITE_4(sc, CSR_MIIMNG, bit | MII_WR); + DELAY(10); + CSR_WRITE_4(sc, CSR_MIIMNG, bit | MII_WR | MII_CLK); + DELAY(10); + } + + return (0); +} + +static void +fv_miibus_turnaround(struct fv_softc *sc, int cmd) +{ + if (cmd == MII_WRCMD) { + fv_miibus_writebits(sc, 0x02, 2); + } else { + fv_miibus_readbits(sc, 1); + } +} + +static int +fv_miibus_readreg(device_t dev, int phy, int reg) +{ + struct fv_softc * sc = device_get_softc(dev); + int result; + + mtx_lock(&miibus_mtx); + fv_miibus_writebits(sc, MII_PREAMBLE, 32); + fv_miibus_writebits(sc, MII_RDCMD, 4); + fv_miibus_writebits(sc, phy, 5); + fv_miibus_writebits(sc, reg, 5); + fv_miibus_turnaround(sc, MII_RDCMD); + result = fv_miibus_readbits(sc, 16); + fv_miibus_turnaround(sc, MII_RDCMD); + mtx_unlock(&miibus_mtx); + + return (result); +} + +static int +fv_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct fv_softc * sc = device_get_softc(dev); + + mtx_lock(&miibus_mtx); + fv_miibus_writebits(sc, MII_PREAMBLE, 32); + fv_miibus_writebits(sc, MII_WRCMD, 4); + fv_miibus_writebits(sc, phy, 5); + fv_miibus_writebits(sc, reg, 5); + fv_miibus_turnaround(sc, MII_WRCMD); + fv_miibus_writebits(sc, data, 16); + mtx_unlock(&miibus_mtx); + + return (0); +} + +#if !defined(FV_MDIO) +static void +fv_miibus_statchg(device_t dev) +{ + struct fv_softc *sc; + + sc = device_get_softc(dev); + taskqueue_enqueue(taskqueue_swi, &sc->fv_link_task); +} +#endif + +static void +fv_link_task(void *arg, int pending) +{ +#ifdef MII + struct fv_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + /* int lfdx, mfdx; */ + + sc = (struct fv_softc *)arg; + + FV_LOCK(sc); + mii = device_get_softc(sc->fv_miibus); + ifp = sc->fv_ifp; + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + FV_UNLOCK(sc); + return; + } + + if (mii->mii_media_status & IFM_ACTIVE) { + if (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) + sc->fv_link_status = 1; + } else + sc->fv_link_status = 0; + + FV_UNLOCK(sc); +#endif +} + +static void +fv_reset(struct fv_softc *sc) +{ + int i; + + CSR_WRITE_4(sc, CSR_BUSMODE, BUSMODE_SWR); + + /* + * The chip doesn't take itself out of reset automatically. + * We need to do so after 2us. + */ + DELAY(1000); + CSR_WRITE_4(sc, CSR_BUSMODE, 0); + + for (i = 0; i < 1000; i++) { + /* + * Wait a bit for the reset to complete before peeking + * at the chip again. + */ + DELAY(1000); + if ((CSR_READ_4(sc, CSR_BUSMODE) & BUSMODE_SWR) == 0) + break; + } + + if (CSR_READ_4(sc, CSR_BUSMODE) & BUSMODE_SWR) + device_printf(sc->fv_dev, "reset time out\n"); + + DELAY(1000); +} + +static void +fv_init(void *xsc) +{ + struct fv_softc *sc = xsc; + + FV_LOCK(sc); + fv_init_locked(sc); + FV_UNLOCK(sc); +} + +static void +fv_init_locked(struct fv_softc *sc) +{ + struct ifnet *ifp = sc->fv_ifp; +#ifdef MII + struct mii_data *mii; +#endif + + FV_LOCK_ASSERT(sc); + +#ifdef MII + mii = device_get_softc(sc->fv_miibus); +#endif + + fv_stop(sc); + fv_reset(sc); + + /* Init circular RX list. */ + if (fv_rx_ring_init(sc) != 0) { + device_printf(sc->fv_dev, + "initialization failed: no memory for rx buffers\n"); + fv_stop(sc); + return; + } + + /* Init tx descriptors. */ + fv_tx_ring_init(sc); + + /* + * Initialize the BUSMODE register. + */ + CSR_WRITE_4(sc, CSR_BUSMODE, + /* XXX: not sure if this is a good thing or not... */ + BUSMODE_BAR | BUSMODE_PBL_32LW); + + /* + * Initialize the interrupt mask and enable interrupts. + */ + /* normal interrupts */ + sc->sc_inten = STATUS_TI | STATUS_TU | STATUS_RI | STATUS_NIS; + + /* abnormal interrupts */ + sc->sc_inten |= STATUS_TPS | STATUS_TJT | STATUS_UNF | + STATUS_RU | STATUS_RPS | STATUS_SE | STATUS_AIS; + + sc->sc_rxint_mask = STATUS_RI|STATUS_RU; + sc->sc_txint_mask = STATUS_TI|STATUS_UNF|STATUS_TJT; + + sc->sc_rxint_mask &= sc->sc_inten; + sc->sc_txint_mask &= sc->sc_inten; + + CSR_WRITE_4(sc, CSR_INTEN, sc->sc_inten); + CSR_WRITE_4(sc, CSR_STATUS, 0xffffffff); + + /* + * Give the transmit and receive rings to the chip. + */ + CSR_WRITE_4(sc, CSR_TXLIST, FV_TX_RING_ADDR(sc, 0)); + CSR_WRITE_4(sc, CSR_RXLIST, FV_RX_RING_ADDR(sc, 0)); + + /* + * Set the station address. + */ + fv_setfilt(sc); + + + /* + * Write out the opmode. + */ + CSR_WRITE_4(sc, CSR_OPMODE, OPMODE_SR | OPMODE_ST | + OPMODE_TR_128 | OPMODE_FDX | OPMODE_SPEED); + /* + * Start the receive process. + */ + CSR_WRITE_4(sc, CSR_RXPOLL, RXPOLL_RPD); + + sc->fv_link_status = 1; +#ifdef MII + mii_mediachg(mii); +#endif + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&sc->fv_stat_callout, hz, fv_tick, sc); +} + +static void +fv_start(struct ifnet *ifp) +{ + struct fv_softc *sc; + + sc = ifp->if_softc; + + FV_LOCK(sc); + fv_start_locked(ifp); + FV_UNLOCK(sc); +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + * Use Implicit Chain implementation. + */ +static int +fv_encap(struct fv_softc *sc, struct mbuf **m_head) +{ + struct fv_txdesc *txd; + struct fv_desc *desc; + struct mbuf *m; + bus_dma_segment_t txsegs[FV_MAXFRAGS]; + int error, i, nsegs, prod, si; + int padlen; + int txstat; + + FV_LOCK_ASSERT(sc); + + /* + * Some VIA Rhine wants packet buffers to be longword + * aligned, but very often our mbufs aren't. Rather than + * waste time trying to decide when to copy and when not + * to copy, just do it all the time. + */ + m = m_defrag(*m_head, M_NOWAIT); + if (m == NULL) { + device_printf(sc->fv_dev, "fv_encap m_defrag error\n"); + m_freem(*m_head); + *m_head = NULL; + return (ENOBUFS); + } + *m_head = m; + + /* + * The Rhine chip doesn't auto-pad, so we have to make + * sure to pad short frames out to the minimum frame length + * ourselves. + */ + if ((*m_head)->m_pkthdr.len < FV_MIN_FRAMELEN) { + m = *m_head; + padlen = FV_MIN_FRAMELEN - m->m_pkthdr.len; + if (M_WRITABLE(m) == 0) { + /* Get a writable copy. */ + m = m_dup(*m_head, M_NOWAIT); + m_freem(*m_head); + if (m == NULL) { + device_printf(sc->fv_dev, "fv_encap m_dup error\n"); + *m_head = NULL; + return (ENOBUFS); + } + *m_head = m; + } + if (m->m_next != NULL || M_TRAILINGSPACE(m) < padlen) { + m = m_defrag(m, M_NOWAIT); + if (m == NULL) { + device_printf(sc->fv_dev, "fv_encap m_defrag error\n"); + m_freem(*m_head); + *m_head = NULL; + return (ENOBUFS); + } + } + /* + * Manually pad short frames, and zero the pad space + * to avoid leaking data. + */ + bzero(mtod(m, char *) + m->m_pkthdr.len, padlen); + m->m_pkthdr.len += padlen; + m->m_len = m->m_pkthdr.len; + *m_head = m; + } + + prod = sc->fv_cdata.fv_tx_prod; + txd = &sc->fv_cdata.fv_txdesc[prod]; + error = bus_dmamap_load_mbuf_sg(sc->fv_cdata.fv_tx_tag, txd->tx_dmamap, + *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); + if (error == EFBIG) { + device_printf(sc->fv_dev, "fv_encap EFBIG error\n"); + m = m_defrag(*m_head, M_NOWAIT); + if (m == NULL) { + m_freem(*m_head); + *m_head = NULL; + return (ENOBUFS); + } + *m_head = m; + error = bus_dmamap_load_mbuf_sg(sc->fv_cdata.fv_tx_tag, + txd->tx_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(*m_head); + *m_head = NULL; + return (error); + } + + } else if (error != 0) + return (error); + if (nsegs == 0) { + m_freem(*m_head); + *m_head = NULL; + return (EIO); + } + + /* Check number of available descriptors. */ + if (sc->fv_cdata.fv_tx_cnt + nsegs >= (FV_TX_RING_CNT - 1)) { + bus_dmamap_unload(sc->fv_cdata.fv_tx_tag, txd->tx_dmamap); + return (ENOBUFS); + } + + txd->tx_m = *m_head; + bus_dmamap_sync(sc->fv_cdata.fv_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_PREWRITE); + + si = prod; + + /* + * Make a list of descriptors for this packet. + */ + desc = NULL; + for (i = 0; i < nsegs; i++) { + desc = &sc->fv_rdata.fv_tx_ring[prod]; + desc->fv_stat = ADSTAT_OWN; + desc->fv_devcs = txsegs[i].ds_len; + /* end of descriptor */ + if (prod == FV_TX_RING_CNT - 1) + desc->fv_devcs |= ADCTL_ER; + desc->fv_addr = txsegs[i].ds_addr; + + ++sc->fv_cdata.fv_tx_cnt; + FV_INC(prod, FV_TX_RING_CNT); + } + + /* + * Set mark last fragment with Last/Intr flag + */ + if (desc) { + desc->fv_devcs |= ADCTL_Tx_IC; + desc->fv_devcs |= ADCTL_Tx_LS; + } + + /* Update producer index. */ + sc->fv_cdata.fv_tx_prod = prod; + + /* Sync descriptors. */ + bus_dmamap_sync(sc->fv_cdata.fv_tx_ring_tag, + sc->fv_cdata.fv_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + txstat = (CSR_READ_4(sc, CSR_STATUS) >> 20) & 7; + if (txstat == 0 || txstat == 6) { + /* Transmit Process Stat is stop or suspended */ + desc = &sc->fv_rdata.fv_tx_ring[si]; + desc->fv_devcs |= ADCTL_Tx_FS; + } + else { + /* Get previous descriptor */ + si = (si + FV_TX_RING_CNT - 1) % FV_TX_RING_CNT; + desc = &sc->fv_rdata.fv_tx_ring[si]; + /* join remain data and flugs */ + desc->fv_devcs &= ~ADCTL_Tx_IC; + desc->fv_devcs &= ~ADCTL_Tx_LS; + } + + + return (0); +} + +static void +fv_start_locked(struct ifnet *ifp) *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***