From d217d4d98ac6fbea8bdd467edead6388cfe1a1c0 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Fri, 12 Oct 2007 14:12:42 +0000 Subject: [PATCH] Add et(4), which supports Agere ET1310 based Ethernet chips (PCIe only) This chips supports two RX rings, one is currently used for packets whose size are smaller than 110 bytes, the other one is used for the rest packets sizes. Its RX interrupt moderation is quite similar to what bge(4) does: Two control variables, one is used to control how many packet should be received the other is used to control RX interrupt delay, RX interrupt moderation is achieved through the interaction of these two variables. Its TX interrupt moderation is more straightforward than RX's ;), you can tell hardware which TX segment should trigger interrupt. It also has a hardware timer, which is set to 1Hz currently to prevent if_watchdog() from (mis)firing. I didn't figure out how to add polling(4) support for this chip, its RX state ring simply stops working if interrupts are disabled. However, its hardware timer may be used to mimic polling(4) support. The missing features of the driver as of this commit: - Hardware checksum - Hardware vlan tagging - Jumbo buffer support Hopefully, they will be added later. Add TruePHY (will any vendors name their PHY as FalsePHY one day?) support into miibus(4) for Agere ET1011C PHY, which is used by et(4). The data sheet says model is 1 for ET1011C, while my testing card's model is 4; it may be just a variant. --- share/man/man4/Makefile | 3 +- share/man/man4/et.4 | 155 ++ share/man/man4/miibus.4 | 5 +- sys/conf/files | 4 +- sys/config/GENERIC | 3 +- sys/config/LINT | 6 +- sys/dev/netif/Makefile | 9 +- sys/dev/netif/et/Makefile | 8 + sys/dev/netif/et/bitops.h | 80 + sys/dev/netif/et/if_et.c | 2257 ++++++++++++++++++++++++++ sys/dev/netif/et/if_etreg.h | 333 ++++ sys/dev/netif/et/if_etvar.h | 227 +++ sys/dev/netif/mii_layer/Makefile | 4 +- sys/dev/netif/mii_layer/truephy.c | 334 ++++ sys/dev/netif/mii_layer/truephyreg.h | 52 + 15 files changed, 3469 insertions(+), 11 deletions(-) create mode 100644 share/man/man4/et.4 create mode 100644 sys/dev/netif/et/Makefile create mode 100644 sys/dev/netif/et/bitops.h create mode 100644 sys/dev/netif/et/if_et.c create mode 100644 sys/dev/netif/et/if_etreg.h create mode 100644 sys/dev/netif/et/if_etvar.h create mode 100644 sys/dev/netif/mii_layer/truephy.c create mode 100644 sys/dev/netif/mii_layer/truephyreg.h diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 19187790c5..5240e5d383 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 8.1 (Berkeley) 6/18/93 # $FreeBSD: src/share/man/man4/Makefile,v 1.83.2.66 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/share/man/man4/Makefile,v 1.69 2007/10/02 13:37:38 hasso Exp $ +# $DragonFly: src/share/man/man4/Makefile,v 1.70 2007/10/12 14:12:42 sephe Exp $ MAN= aac.4 \ acpi.4 \ @@ -60,6 +60,7 @@ MAN= aac.4 \ ef.4 \ ehci.4 \ em.4 \ + et.4 \ exca.4 \ faith.4 \ fast_ipsec.4 \ diff --git a/share/man/man4/et.4 b/share/man/man4/et.4 new file mode 100644 index 0000000000..283d5c0d31 --- /dev/null +++ b/share/man/man4/et.4 @@ -0,0 +1,155 @@ +.\" +.\" Copyright (c) 2007 The DragonFly Project. 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. Neither the name of The DragonFly Project nor the names of its +.\" contributors may be used to endorse or promote products derived +.\" from this software without specific, prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 +.\" COPYRIGHT HOLDERS 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. +.\" +.\" $DragonFly: src/share/man/man4/et.4,v 1.1 2007/10/12 14:12:42 sephe Exp $ +.\" +.Dd October 11, 2007 +.Dt ET 4 +.Os +.Sh NAME +.Nm et +.Nd "Agere ET1310 10/100/Gigabit Ethernet device" +.Sh SYNOPSIS +.Cd "device miibus" +.Cd "device et" +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Pa /boot/loader.conf : +.Bd -literal -offset indent +if_et_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver supports the PCIe Ethernet adapters based on Agere ET1310. +.Pp +The +.Nm +driver supports the following +.Ar media +types: +.Pp +.Bl -tag -width 10baseT/UTP -compact +.It Cm autoselect +Enable autoselection of the media types and options +.Pp +.It Cm 10baseT/UTP +Set 10Mbps operation. +The +.Ar mediaopt +option can also be used to select either +.Ar full-duplex +or +.Ar half-duplex +modes. +.Pp +.It Cm 100baseTX +Set 100Mbps (Fast Ethernet) operation. +The +.Ar mediaopt +option can also be used to select either +.Ar full-duplex +or +.Ar half-duplex +modes. +.Pp +.It Cm 1000baseT +Set 1000Mbps (Gigabit Ethernet) operation. +The +.Ar mediaopt +option can only be set +.Ar full-duplex +mode. +.El +.Pp +The +.Nm +driver supports the following +.Ar media +options: +.Pp +.Bl -tag -width full-duplex -compact +.It Cm full-duplex +Force full duplex operation. +.Pp +.It Cm half-duplex +Force half duplex operation. +.El +.Pp +Note that the 1000baseT media type is only available +if it is supported by the adapter. +For more information on configuring this device, +see +.Xr ifconfig 8 . +.Sh TUNABLES +.Bl -tag -width ".Va hw.et.rx_intr_npkts" +.It Va hw.et.rx_intr_npkts +This value controls how many packets should be recevied +before a receive interrupt is generated. +The default value is 32. +.It Va hw.et.rx_intr_delay +This value delays the generation of receive interrupts +in units of 10 microseconds (I guess). +It is used together with +.Va hw.et.rx_intr_npkts +to achieve RX interrupt moderation. +The default value is 20. +.It Va hw.et.tx_intr_nsegs +This value controls how many segments (not packets) should be transmitted +before a transmit interrupt is generated. +The default value is 126. +.It Va hw.et.timer +This value controls how often the a timer interrupt should be generated. +It is used together with +.Va hw.et.tx_intr_nsegs +to achieve TX interrupt moderation. +The default value is 1000000000 (nanoseconds). +.El +.Sh SEE ALSO +.Xr arp 4 , +.Xr ifmedia 4 , +.Xr miibus 4 , +.Xr netintro 4 , +.Xr ng_ether 4 , +.Xr vlan 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Dx 1.11 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Sepherosa Ziehau +.Aq sepherosa@gmail.com . diff --git a/share/man/man4/miibus.4 b/share/man/man4/miibus.4 index d6d6a35c21..a6f02a5cd7 100644 --- a/share/man/man4/miibus.4 +++ b/share/man/man4/miibus.4 @@ -7,7 +7,7 @@ .\" I doubt you would ever notice due to large differences. .\" .\" $FreeBSD: src/share/man/man4/miibus.4,v 1.3.2.6 2004/03/01 09:22:11 brueffer Exp $ -.\" $DragonFly: src/share/man/man4/miibus.4,v 1.13 2007/10/02 11:39:45 swildner Exp $ +.\" $DragonFly: src/share/man/man4/miibus.4,v 1.14 2007/10/12 14:12:42 sephe Exp $ .\" .Dd February 15, 2003 .Dt MIIBUS 4 @@ -62,6 +62,8 @@ Broadcom BCM570xx Gigabit Ethernet DEC/Intel 21143 and various workalikes .It Xr ed 4 NE[12]000, SMC Ultra, 3c503, DS8390 cards +.It Xr et 4 +Agere ET1310 10/100/1000 Ethernet .It Xr fxp 4 Intel EtherExpress PRO/100B .It Xr gx 4 @@ -121,6 +123,7 @@ but as a result are not well behaved newbus device drivers. .Xr bge 4 , .Xr dc 4 , .Xr ed 4 , +.Xr et 4 , .Xr fxp 4 , .Xr gx 4 , .Xr lge 4 , diff --git a/sys/conf/files b/sys/conf/files index e230525d71..f4deb3f2c3 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/sys/conf/files,v 1.184 2007/10/02 13:37:38 hasso Exp $ +# $DragonFly: src/sys/conf/files,v 1.185 2007/10/12 14:12:42 sephe Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -209,6 +209,7 @@ dev/netif/ep/if_ep_isa.c optional ep isa dev/netif/ep/if_ep_eisa.c optional ep eisa dev/netif/ep/if_ep_pccard.c optional ep pccard dev/atm/en/midway.c optional en +dev/netif/et/if_et.c optional et dev/netif/ex/if_ex.c optional ex dev/netif/ex/if_ex_isa.c optional ex isa dev/netif/ex/if_ex_pccard.c optional ex pccard @@ -302,6 +303,7 @@ dev/netif/mii_layer/acphy.c optional miibus dev/netif/mii_layer/ruephy.c optional miibus dev/netif/mii_layer/rgephy.c optional miibus dev/netif/mii_layer/ip1000phy.c optional miibus +dev/netif/mii_layer/truephy.c optional miibus dev/netif/mii_layer/miibus_if.m optional miibus dev/raid/mlx/mlx_disk.c optional mlx dev/raid/mlx/mlx_pci.c optional mlx diff --git a/sys/config/GENERIC b/sys/config/GENERIC index a03b548b18..6bf62d1530 100644 --- a/sys/config/GENERIC +++ b/sys/config/GENERIC @@ -4,7 +4,7 @@ # Check the LINT configuration file in sys/config, for an # exhaustive list of options. # -# $DragonFly: src/sys/config/GENERIC,v 1.53 2007/10/02 13:37:38 hasso Exp $ +# $DragonFly: src/sys/config/GENERIC,v 1.54 2007/10/12 14:12:42 sephe Exp $ platform pc32 machine i386 @@ -215,6 +215,7 @@ device miibus # MII bus support device bce # Broadcom NetXtreme II Gigabit Ethernet device bfe # Broadcom BCM440x 10/100 Ethernet device dc # DEC/Intel 21143 and various workalikes +device et # Agere ET1310 10/100/1000 Ethernet device fxp # Intel EtherExpress PRO/100B (82557, 82558) device pcn # AMD Am79C97x PCI 10/100 NICs device rl # RealTek 8129/8139 diff --git a/sys/config/LINT b/sys/config/LINT index ff06a19eb6..b0435dd843 100644 --- a/sys/config/LINT +++ b/sys/config/LINT @@ -3,7 +3,7 @@ # as much of the source tree as it can. # # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $ -# $DragonFly: src/sys/config/LINT,v 1.133 2007/10/02 13:37:38 hasso Exp $ +# $DragonFly: src/sys/config/LINT,v 1.134 2007/10/12 14:12:42 sephe Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1747,6 +1747,9 @@ options AHD_REG_PRETTY_PRINT # The `em' device provides support for the Intel Pro/1000 Family of Gigabit # adapters (82542, 82543, 82544, 82540). # +# The `et' device provides support for the Agere ET1310 10/100/1000 PCIe +# adapters. +# # The `fxp' device provides support for the Intel EtherExpress Pro/100B # PCI Fast Ethernet adapters. # @@ -1970,6 +1973,7 @@ device vx # 3Com 3c590, 3c595 (``Vortex'') # Gigabit Ethernet NICs. device bge # Broadcom BCM570x (``Tigon III'') device em # Intel Pro/1000 (82542,82543,82544,82540) +device et # Agere ET1310 10/100/1000 Ethernet device gx # Intel Pro/1000 (82542, 82543) device lge # Level 1 LXT1001 (``Mercury'') device nfe # nVidia nForce2/3 MCP04/51/55 CK804 diff --git a/sys/dev/netif/Makefile b/sys/dev/netif/Makefile index cbe5960ec7..132f904c43 100644 --- a/sys/dev/netif/Makefile +++ b/sys/dev/netif/Makefile @@ -1,8 +1,9 @@ -# $DragonFly: src/sys/dev/netif/Makefile,v 1.27 2007/09/08 06:16:27 sephe Exp $ +# $DragonFly: src/sys/dev/netif/Makefile,v 1.28 2007/10/12 14:12:42 sephe Exp $ # -SUBDIR= an acx ar ath aue axe bce bfe bge bwi cue dc ed em ep fwe fxp gx ipw \ - iwi kue lge lnc mii_layer my nfe nge pcn ral ray re rl rtw rue rum \ - sbni sbsh sf sis sk sr ste stge ti tl tx txp ural vge vr vx wb wi xe xl +SUBDIR= an acx ar ath aue axe bce bfe bge bwi cue dc ed em ep et fwe fxp gx \ + ipw iwi kue lge lnc mii_layer my nfe nge pcn ral ray re rl rtw rue \ + rum sbni sbsh sf sis sk sr ste stge ti tl tx txp ural vge vr vx wb wi \ + xe xl .include diff --git a/sys/dev/netif/et/Makefile b/sys/dev/netif/et/Makefile new file mode 100644 index 0000000000..111ad84cfb --- /dev/null +++ b/sys/dev/netif/et/Makefile @@ -0,0 +1,8 @@ +# $DragonFly: src/sys/dev/netif/et/Makefile,v 1.1 2007/10/12 14:12:42 sephe Exp $ + +KMOD= if_et +SRCS= if_et.c +SRCS+= miibus_if.h device_if.h bus_if.h pci_if.h +KMODDEPS= miibus + +.include diff --git a/sys/dev/netif/et/bitops.h b/sys/dev/netif/et/bitops.h new file mode 100644 index 0000000000..b45fe82001 --- /dev/null +++ b/sys/dev/netif/et/bitops.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * 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. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``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 David + * Young 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. + * + * $DragonFly: src/sys/dev/netif/et/Attic/bitops.h,v 1.1 2007/10/12 14:12:42 sephe Exp $ + */ + +#ifndef _BITOPS_H +#define _BITOPS_H + +/* + * __BIT(n): Return a bitmask with bit m set, where the least + * significant bit is bit 0. + * + * __BITS(m, n): Return a bitmask with bits m through n, inclusive, + * set. It does not matter whether m>n or m<=n. The + * least significant bit is bit 0. + * + * A "bitfield" is a span of consecutive bits defined by a bitmask, + * where 1s select the bits in the bitfield. __SHIFTIN, __SHIFTOUT, + * and SHIFTOUT_MASK help read and write bitfields from device registers. + * + * __SHIFTIN(v, mask): Left-shift bits `v' into the bitfield + * defined by `mask', and return them. No + * side-effects. + * + * __SHIFTOUT(v, mask): Extract and return the bitfield selected + * by `mask' from `v', right-shifting the + * bits so that the rightmost selected bit + * is at bit 0. No side-effects. + * + * __SHIFTOUT_MASK(mask): Right-shift the bits in `mask' so that + * the rightmost non-zero bit is at bit + * 0. This is useful for finding the + * greatest unsigned value that a bitfield + * can hold. No side-effects. Note that + * SHIFTOUT_MASK(m) = SHIFTOUT(m, m). + */ + +/* __BIT(n): nth bit, where __BIT(0) == 0x1. */ +#define __BIT(__n) (((__n) == 32) ? 0 : ((uint32_t)1 << (__n))) + +/* __BITS(m, n): bits m through n, m < n. */ +#define __BITS(__m, __n) \ + ((__BIT(MAX((__m), (__n)) + 1) - 1) ^ (__BIT(MIN((__m), (__n))) - 1)) + +/* Find least significant bit that is set */ +#define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) + +#define __SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask)) +#define __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask)) +#define __SHIFTOUT_MASK(__mask) __SHIFTOUT((__mask), (__mask)) + +#endif /* !_BITOPS_H */ diff --git a/sys/dev/netif/et/if_et.c b/sys/dev/netif/et/if_et.c new file mode 100644 index 0000000000..4b8b9f5bb9 --- /dev/null +++ b/sys/dev/netif/et/if_et.c @@ -0,0 +1,2257 @@ +/* + * Copyright (c) 2007 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * 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. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + * $DragonFly: src/sys/dev/netif/et/if_et.c,v 1.1 2007/10/12 14:12:42 sephe Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "miibus_if.h" + +#include "bitops.h" +#include "if_etreg.h" +#include "if_etvar.h" + +static int et_probe(device_t); +static int et_attach(device_t); +static int et_detach(device_t); +static int et_shutdown(device_t); + +static int et_miibus_readreg(device_t, int, int); +static int et_miibus_writereg(device_t, int, int, int); +static void et_miibus_statchg(device_t); + +static void et_init(void *); +static int et_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *); +static void et_start(struct ifnet *); +static void et_watchdog(struct ifnet *); +static int et_ifmedia_upd(struct ifnet *); +static void et_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static int et_sysctl_rx_intr_npkts(SYSCTL_HANDLER_ARGS); +static int et_sysctl_rx_intr_delay(SYSCTL_HANDLER_ARGS); + +static void et_intr(void *); +static void et_enable_intrs(struct et_softc *, uint32_t); +static void et_disable_intrs(struct et_softc *); +static void et_rxeof(struct et_softc *); +static void et_txeof(struct et_softc *); + +static int et_dma_alloc(device_t); +static void et_dma_free(device_t); +static int et_dma_mem_create(device_t, bus_size_t, bus_dma_tag_t *, + void **, bus_addr_t *, bus_dmamap_t *); +static void et_dma_mem_destroy(bus_dma_tag_t, void *, bus_dmamap_t); +static int et_dma_mbuf_create(device_t); +static void et_dma_mbuf_destroy(device_t, int, const int[]); +static void et_dma_ring_addr(void *, bus_dma_segment_t *, int, int); +static void et_dma_buf_addr(void *, bus_dma_segment_t *, int, + bus_size_t, int); +static int et_init_tx_ring(struct et_softc *); +static int et_init_rx_ring(struct et_softc *); +static void et_free_tx_ring(struct et_softc *); +static void et_free_rx_ring(struct et_softc *); +static int et_encap(struct et_softc *, struct mbuf **); +static int et_newbuf(struct et_rxbuf_data *, int, int, int); +static int et_newbuf_cluster(struct et_rxbuf_data *, int, int); +static int et_newbuf_hdr(struct et_rxbuf_data *, int, int); + +static void et_stop(struct et_softc *); +static int et_chip_init(struct et_softc *); +static void et_chip_attach(struct et_softc *); +static void et_init_mac(struct et_softc *); +static void et_init_rxmac(struct et_softc *); +static void et_init_txmac(struct et_softc *); +static int et_init_rxdma(struct et_softc *); +static int et_init_txdma(struct et_softc *); +static int et_start_rxdma(struct et_softc *); +static int et_start_txdma(struct et_softc *); +static int et_stop_rxdma(struct et_softc *); +static int et_stop_txdma(struct et_softc *); +static int et_enable_txrx(struct et_softc *); +static void et_reset(struct et_softc *); +static int et_bus_config(device_t); +static void et_get_eaddr(device_t, uint8_t[]); +static void et_setmulti(struct et_softc *); +static void et_tick(void *); + +static const struct et_dev { + uint16_t vid; + uint16_t did; + const char *desc; +} et_devices[] = { + { PCI_VENDOR_LUCENT, PCI_PRODUCT_LUCENT_ET1310, + "Agere ET1310 Gigabit Ethernet" }, + { PCI_VENDOR_LUCENT, PCI_PRODUCT_LUCENT_ET1310_FAST, + "Agere ET1310 Fast Ethernet" }, + { 0, 0, NULL } +}; + +static device_method_t et_methods[] = { + DEVMETHOD(device_probe, et_probe), + DEVMETHOD(device_attach, et_attach), + DEVMETHOD(device_detach, et_detach), + DEVMETHOD(device_shutdown, et_shutdown), +#if 0 + DEVMETHOD(device_suspend, et_suspend), + DEVMETHOD(device_resume, et_resume), +#endif + + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + DEVMETHOD(miibus_readreg, et_miibus_readreg), + DEVMETHOD(miibus_writereg, et_miibus_writereg), + DEVMETHOD(miibus_statchg, et_miibus_statchg), + + { 0, 0 } +}; + +static driver_t et_driver = { + "et", + et_methods, + sizeof(struct et_softc) +}; + +static devclass_t et_devclass; + +DECLARE_DUMMY_MODULE(if_et); +MODULE_DEPEND(if_et, miibus, 1, 1, 1); +DRIVER_MODULE(if_et, pci, et_driver, et_devclass, 0, 0); +DRIVER_MODULE(miibus, et, miibus_driver, miibus_devclass, 0, 0); + +static int et_rx_intr_npkts = 32; +static int et_rx_intr_delay = 20; /* x10 usec */ +static int et_tx_intr_nsegs = 126; +static uint32_t et_timer = 1000 * 1000 * 1000; /* nanosec */ + +TUNABLE_INT("hw.et.timer", &et_timer); +TUNABLE_INT("hw.et.rx_intr_npkts", &et_rx_intr_npkts); +TUNABLE_INT("hw.et.rx_intr_intvl", &et_rx_intr_delay); +TUNABLE_INT("hw.et_tx_intr_nsegs", &et_tx_intr_nsegs); + +struct et_bsize { + int bufsize; + et_newbuf_t newbuf; +}; + +static const struct et_bsize et_bufsize[ET_RX_NRING] = { + { .bufsize = 0, .newbuf = et_newbuf_hdr }, + { .bufsize = 0, .newbuf = et_newbuf_cluster }, +}; + +static int +et_probe(device_t dev) +{ + const struct et_dev *d; + uint16_t did, vid; + + vid = pci_get_vendor(dev); + did = pci_get_device(dev); + + for (d = et_devices; d->desc != NULL; ++d) { + if (vid == d->vid && did == d->did) { + device_set_desc(dev, d->desc); + return 0; + } + } + return ENXIO; +} + +static int +et_attach(device_t dev) +{ + struct et_softc *sc = device_get_softc(dev); + struct ifnet *ifp = &sc->arpcom.ac_if; + uint8_t eaddr[ETHER_ADDR_LEN]; + int error; + + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + callout_init(&sc->sc_tick); + + /* + * Initialize tunables + */ + sc->sc_rx_intr_npkts = et_rx_intr_npkts; + sc->sc_rx_intr_delay = et_rx_intr_delay; + sc->sc_tx_intr_nsegs = et_tx_intr_nsegs; + sc->sc_timer = et_timer; + +#ifndef BURN_BRIDGES + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + uint32_t irq, mem; + + irq = pci_read_config(dev, PCIR_INTLINE, 4); + mem = pci_read_config(dev, ET_PCIR_BAR, 4); + + device_printf(dev, "chip is in D%d power mode " + "-- setting to D0\n", pci_get_powerstate(dev)); + + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + pci_write_config(dev, PCIR_INTLINE, irq, 4); + pci_write_config(dev, ET_PCIR_BAR, mem, 4); + } +#endif /* !BURN_BRIDGE */ + + /* Enable bus mastering */ + pci_enable_busmaster(dev); + + /* + * Allocate IO memory + */ + sc->sc_mem_rid = ET_PCIR_BAR; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_mem_rid, RF_ACTIVE); + if (sc->sc_mem_res == NULL) { + device_printf(dev, "can't allocate IO memory\n"); + return ENXIO; + } + sc->sc_mem_bt = rman_get_bustag(sc->sc_mem_res); + sc->sc_mem_bh = rman_get_bushandle(sc->sc_mem_res); + + /* + * Allocate IRQ + */ + sc->sc_irq_rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irq_rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(dev, "can't allocate irq\n"); + error = ENXIO; + goto fail; + } + + /* + * Create sysctl tree + */ + sysctl_ctx_init(&sc->sc_sysctl_ctx); + sc->sc_sysctl_tree = SYSCTL_ADD_NODE(&sc->sc_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_hw), + OID_AUTO, + device_get_nameunit(dev), + CTLFLAG_RD, 0, ""); + if (sc->sc_sysctl_tree == NULL) { + device_printf(dev, "can't add sysctl node\n"); + error = ENXIO; + goto fail; + } + + SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx, + SYSCTL_CHILDREN(sc->sc_sysctl_tree), + OID_AUTO, "rx_intr_npkts", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, et_sysctl_rx_intr_npkts, "I", + "RX IM, # packets per RX interrupt"); + SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx, + SYSCTL_CHILDREN(sc->sc_sysctl_tree), + OID_AUTO, "rx_intr_delay", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, et_sysctl_rx_intr_delay, "I", + "RX IM, RX interrupt delay (x10 usec)"); + SYSCTL_ADD_INT(&sc->sc_sysctl_ctx, + SYSCTL_CHILDREN(sc->sc_sysctl_tree), OID_AUTO, + "tx_intr_nsegs", CTLFLAG_RW, &sc->sc_tx_intr_nsegs, 0, + "TX IM, # segments per TX interrupt"); + SYSCTL_ADD_UINT(&sc->sc_sysctl_ctx, + SYSCTL_CHILDREN(sc->sc_sysctl_tree), OID_AUTO, + "timer", CTLFLAG_RW, &sc->sc_timer, 0, + "TX timer"); + + error = et_bus_config(dev); + if (error) + goto fail; + + et_get_eaddr(dev, eaddr); + + CSR_WRITE_4(sc, ET_PM, + ET_PM_SYSCLK_GATE | ET_PM_TXCLK_GATE | ET_PM_RXCLK_GATE); + + et_reset(sc); + + et_disable_intrs(sc); + + error = et_dma_alloc(dev); + if (error) + goto fail; + + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = et_init; + ifp->if_ioctl = et_ioctl; + ifp->if_start = et_start; + ifp->if_watchdog = et_watchdog; + ifp->if_mtu = ETHERMTU; + ifq_set_maxlen(&ifp->if_snd, ET_TX_NDESC); + ifq_set_ready(&ifp->if_snd); + + et_chip_attach(sc); + + error = mii_phy_probe(dev, &sc->sc_miibus, + et_ifmedia_upd, et_ifmedia_sts); + if (error) { + device_printf(dev, "can't probe any PHY\n"); + goto fail; + } + + ether_ifattach(ifp, eaddr, NULL); + + error = bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE, et_intr, sc, + &sc->sc_irq_handle, ifp->if_serializer); + if (error) { + ether_ifdetach(ifp); + device_printf(dev, "can't setup intr\n"); + goto fail; + } + return 0; +fail: + et_detach(dev); + return error; +} + +static int +et_detach(device_t dev) +{ + struct et_softc *sc = device_get_softc(dev); + + if (device_is_attached(dev)) { + struct ifnet *ifp = &sc->arpcom.ac_if; + + lwkt_serialize_enter(ifp->if_serializer); + et_stop(sc); + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_handle); + lwkt_serialize_exit(ifp->if_serializer); + + ether_ifdetach(ifp); + } + + if (sc->sc_sysctl_tree != NULL) + sysctl_ctx_free(&sc->sc_sysctl_ctx); + + if (sc->sc_miibus != NULL) + device_delete_child(dev, sc->sc_miibus); + bus_generic_detach(dev); + + if (sc->sc_irq_res != NULL) { + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, + sc->sc_irq_res); + } + + if (sc->sc_mem_res != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, + sc->sc_mem_res); + } + + et_dma_free(dev); + + return 0; +} + +static int +et_shutdown(device_t dev) +{ + struct et_softc *sc = device_get_softc(dev); + struct ifnet *ifp = &sc->arpcom.ac_if; + + lwkt_serialize_enter(ifp->if_serializer); + et_stop(sc); + lwkt_serialize_exit(ifp->if_serializer); + return 0; +} + +static int +et_miibus_readreg(device_t dev, int phy, int reg) +{ + struct et_softc *sc = device_get_softc(dev); + uint32_t val; + int i, ret; + + /* Stop any pending operations */ + CSR_WRITE_4(sc, ET_MII_CMD, 0); + + val = __SHIFTIN(phy, ET_MII_ADDR_PHY) | + __SHIFTIN(reg, ET_MII_ADDR_REG); + CSR_WRITE_4(sc, ET_MII_ADDR, val); + + /* Start reading */ + CSR_WRITE_4(sc, ET_MII_CMD, ET_MII_CMD_READ); + +#define NRETRY 50 + + for (i = 0; i < NRETRY; ++i) { + val = CSR_READ_4(sc, ET_MII_IND); + if ((val & (ET_MII_IND_BUSY | ET_MII_IND_INVALID)) == 0) + break; + DELAY(50); + } + if (i == NRETRY) { + if_printf(&sc->arpcom.ac_if, + "read phy %d, reg %d timed out\n", phy, reg); + ret = 0; + goto back; + } + +#undef NRETRY + + val = CSR_READ_4(sc, ET_MII_STAT); + ret = __SHIFTOUT(val, ET_MII_STAT_VALUE); + +back: + /* Make sure that the current operation is stopped */ + CSR_WRITE_4(sc, ET_MII_CMD, 0); + return ret; +} + +static int +et_miibus_writereg(device_t dev, int phy, int reg, int val0) +{ + struct et_softc *sc = device_get_softc(dev); + uint32_t val; + int i; + + /* Stop any pending operations */ + CSR_WRITE_4(sc, ET_MII_CMD, 0); + + val = __SHIFTIN(phy, ET_MII_ADDR_PHY) | + __SHIFTIN(reg, ET_MII_ADDR_REG); + CSR_WRITE_4(sc, ET_MII_ADDR, val); + + /* Start writing */ + CSR_WRITE_4(sc, ET_MII_CTRL, __SHIFTIN(val0, ET_MII_CTRL_VALUE)); + +#define NRETRY 100 + + for (i = 0; i < NRETRY; ++i) { + val = CSR_READ_4(sc, ET_MII_IND); + if ((val & ET_MII_IND_BUSY) == 0) + break; + DELAY(50); + } + if (i == NRETRY) { + if_printf(&sc->arpcom.ac_if, + "write phy %d, reg %d timed out\n", phy, reg); + et_miibus_readreg(dev, phy, reg); + } + +#undef NRETRY + + /* Make sure that the current operation is stopped */ + CSR_WRITE_4(sc, ET_MII_CMD, 0); + return 0; +} + +static void +et_miibus_statchg(device_t dev) +{ + struct et_softc *sc = device_get_softc(dev); + struct mii_data *mii = device_get_softc(sc->sc_miibus); + uint32_t cfg2, ctrl; + + cfg2 = CSR_READ_4(sc, ET_MAC_CFG2); + cfg2 &= ~(ET_MAC_CFG2_MODE_MII | ET_MAC_CFG2_MODE_GMII | + ET_MAC_CFG2_FDX | ET_MAC_CFG2_BIGFRM); + cfg2 |= ET_MAC_CFG2_LENCHK | ET_MAC_CFG2_CRC | ET_MAC_CFG2_PADCRC | + __SHIFTIN(7, ET_MAC_CFG2_PREAMBLE_LEN); + + ctrl = CSR_READ_4(sc, ET_MAC_CTRL); + ctrl &= ~(ET_MAC_CTRL_GHDX | ET_MAC_CTRL_MODE_MII); + + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) { + cfg2 |= ET_MAC_CFG2_MODE_GMII; + } else { + cfg2 |= ET_MAC_CFG2_MODE_MII; + ctrl |= ET_MAC_CTRL_MODE_MII; + } + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) + cfg2 |= ET_MAC_CFG2_FDX; + else + ctrl |= ET_MAC_CTRL_GHDX; + + CSR_WRITE_4(sc, ET_MAC_CTRL, ctrl); + CSR_WRITE_4(sc, ET_MAC_CFG2, cfg2); +} + +static int +et_ifmedia_upd(struct ifnet *ifp) +{ + struct et_softc *sc = ifp->if_softc; + struct mii_data *mii = device_get_softc(sc->sc_miibus); + + if (mii->mii_instance != 0) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + mii_mediachg(mii); + + return 0; +} + +static void +et_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct et_softc *sc = ifp->if_softc; + struct mii_data *mii = device_get_softc(sc->sc_miibus); + + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +static void +et_stop(struct et_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ASSERT_SERIALIZED(ifp->if_serializer); + + callout_stop(&sc->sc_tick); + + et_stop_rxdma(sc); + et_stop_txdma(sc); + + et_disable_intrs(sc); + + et_free_tx_ring(sc); + et_free_rx_ring(sc); + + et_reset(sc); + + sc->sc_tx = 0; + sc->sc_tx_intr = 0; + + ifp->if_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); +} + +static int +et_bus_config(device_t dev) +{ + uint32_t val, max_plsz; + uint16_t ack_latency, replay_timer; + + /* + * Test whether EEPROM is valid + * NOTE: Read twice to get the correct value + */ + pci_read_config(dev, ET_PCIR_EEPROM_STATUS, 1); + val = pci_read_config(dev, ET_PCIR_EEPROM_STATUS, 1); + if (val & ET_PCIM_EEPROM_STATUS_ERROR) { + device_printf(dev, "EEPROM status error 0x%02x\n", val); + return ENXIO; + } + + /* TODO: LED */ + + /* + * Configure ACK latency and replay timer according to + * max playload size + */ + val = pci_read_config(dev, ET_PCIR_DEVICE_CAPS, 4); + max_plsz = val & ET_PCIM_DEVICE_CAPS_MAX_PLSZ; + + switch (max_plsz) { + case ET_PCIV_DEVICE_CAPS_PLSZ_128: + ack_latency = ET_PCIV_ACK_LATENCY_128; + replay_timer = ET_PCIV_REPLAY_TIMER_128; + break; + + case ET_PCIV_DEVICE_CAPS_PLSZ_256: + ack_latency = ET_PCIV_ACK_LATENCY_256; + replay_timer = ET_PCIV_REPLAY_TIMER_256; + break; + + default: + ack_latency = pci_read_config(dev, ET_PCIR_ACK_LATENCY, 2); + replay_timer = pci_read_config(dev, ET_PCIR_REPLAY_TIMER, 2); + device_printf(dev, "ack latency %u, replay timer %u\n", + ack_latency, replay_timer); + break; + } + if (ack_latency != 0) { + pci_write_config(dev, ET_PCIR_ACK_LATENCY, ack_latency, 2); + pci_write_config(dev, ET_PCIR_REPLAY_TIMER, replay_timer, 2); + } + + /* + * Set L0s and L1 latency timer to 2us + */ + val = ET_PCIV_L0S_LATENCY(2) | ET_PCIV_L1_LATENCY(2); + pci_write_config(dev, ET_PCIR_L0S_L1_LATENCY, val, 1); + + /* + * Set max read request size to 2048 bytes + */ + val = pci_read_config(dev, ET_PCIR_DEVICE_CTRL, 2); + val &= ~ET_PCIM_DEVICE_CTRL_MAX_RRSZ; + val |= ET_PCIV_DEVICE_CTRL_RRSZ_2K; + pci_write_config(dev, ET_PCIR_DEVICE_CTRL, val, 2); + + return 0; +} + +static void +et_get_eaddr(device_t dev, uint8_t eaddr[]) +{ + uint32_t val; + int i; + + val = pci_read_config(dev, ET_PCIR_MAC_ADDR0, 4); + for (i = 0; i < 4; ++i) + eaddr[i] = (val >> (8 * i)) & 0xff; + + val = pci_read_config(dev, ET_PCIR_MAC_ADDR1, 2); + for (; i < ETHER_ADDR_LEN; ++i) + eaddr[i] = (val >> (8 * (i - 4))) & 0xff; +} + +static void +et_reset(struct et_softc *sc) +{ + CSR_WRITE_4(sc, ET_MAC_CFG1, + ET_MAC_CFG1_RST_TXFUNC | ET_MAC_CFG1_RST_RXFUNC | + ET_MAC_CFG1_RST_TXMC | ET_MAC_CFG1_RST_RXMC | + ET_MAC_CFG1_SIM_RST | ET_MAC_CFG1_SOFT_RST); + + CSR_WRITE_4(sc, ET_SWRST, + ET_SWRST_TXDMA | ET_SWRST_RXDMA | + ET_SWRST_TXMAC | ET_SWRST_RXMAC | + ET_SWRST_MAC | ET_SWRST_MAC_STAT | ET_SWRST_MMC); + + CSR_WRITE_4(sc, ET_MAC_CFG1, + ET_MAC_CFG1_RST_TXFUNC | ET_MAC_CFG1_RST_RXFUNC | + ET_MAC_CFG1_RST_TXMC | ET_MAC_CFG1_RST_RXMC); + CSR_WRITE_4(sc, ET_MAC_CFG1, 0); +} + +static void +et_disable_intrs(struct et_softc *sc) +{ + CSR_WRITE_4(sc, ET_INTR_MASK, 0xffffffff); +} + +static void +et_enable_intrs(struct et_softc *sc, uint32_t intrs) +{ + CSR_WRITE_4(sc, ET_INTR_MASK, ~intrs); +} + +static int +et_dma_alloc(device_t dev) +{ + struct et_softc *sc = device_get_softc(dev); + struct et_txdesc_ring *tx_ring = &sc->sc_tx_ring; + struct et_txstatus_data *txsd = &sc->sc_tx_status; + struct et_rxstat_ring *rxst_ring = &sc->sc_rxstat_ring; + struct et_rxstatus_data *rxsd = &sc->sc_rx_status; + int i, error; + + /* + * Create top level DMA tag + */ + error = bus_dma_tag_create(NULL, 1, 0, + BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, + NULL, NULL, + MAXBSIZE, + BUS_SPACE_UNRESTRICTED, + BUS_SPACE_MAXSIZE_32BIT, + 0, &sc->sc_dtag); + if (error) { + device_printf(dev, "can't create DMA tag\n"); + return error; + } + + /* + * Create TX ring DMA stuffs + */ + error = et_dma_mem_create(dev, ET_TX_RING_SIZE, &tx_ring->tr_dtag, + (void **)&tx_ring->tr_desc, + &tx_ring->tr_paddr, &tx_ring->tr_dmap); + if (error) { + device_printf(dev, "can't create TX ring DMA stuffs\n"); + return error; + } + + /* + * Create TX status DMA stuffs + */ + error = et_dma_mem_create(dev, sizeof(uint32_t), &txsd->txsd_dtag, + (void **)&txsd->txsd_status, + &txsd->txsd_paddr, &txsd->txsd_dmap); + if (error) { + device_printf(dev, "can't create TX status DMA stuffs\n"); + return error; + } + + /* + * Create DMA stuffs for RX rings + */ + for (i = 0; i < ET_RX_NRING; ++i) { + static const uint32_t rx_ring_posreg[ET_RX_NRING] = + { ET_RX_RING0_POS, ET_RX_RING1_POS }; + + struct et_rxdesc_ring *rx_ring = &sc->sc_rx_ring[i]; + + error = et_dma_mem_create(dev, ET_RX_RING_SIZE, + &rx_ring->rr_dtag, + (void **)&rx_ring->rr_desc, + &rx_ring->rr_paddr, + &rx_ring->rr_dmap); + if (error) { + device_printf(dev, "can't create DMA stuffs for " + "the %d RX ring\n", i); + return error; + } + rx_ring->rr_posreg = rx_ring_posreg[i]; + } + + /* + * Create RX stat ring DMA stuffs + */ + error = et_dma_mem_create(dev, ET_RXSTAT_RING_SIZE, + &rxst_ring->rsr_dtag, + (void **)&rxst_ring->rsr_stat, + &rxst_ring->rsr_paddr, &rxst_ring->rsr_dmap); + if (error) { + device_printf(dev, "can't create RX stat ring DMA stuffs\n"); + return error; + } + + /* + * Create RX status DMA stuffs + */ + error = et_dma_mem_create(dev, sizeof(struct et_rxstatus), + &rxsd->rxsd_dtag, + (void **)&rxsd->rxsd_status, + &rxsd->rxsd_paddr, &rxsd->rxsd_dmap); + if (error) { + device_printf(dev, "can't create RX status DMA stuffs\n"); + return error; + } + + /* + * Create mbuf DMA stuffs + */ + error = et_dma_mbuf_create(dev); + if (error) + return error; + + return 0; +} + +static void +et_dma_free(device_t dev) +{ + struct et_softc *sc = device_get_softc(dev); + struct et_txdesc_ring *tx_ring = &sc->sc_tx_ring; + struct et_txstatus_data *txsd = &sc->sc_tx_status; + struct et_rxstat_ring *rxst_ring = &sc->sc_rxstat_ring; + struct et_rxstatus_data *rxsd = &sc->sc_rx_status; + int i, rx_done[ET_RX_NRING]; + + /* + * Destroy TX ring DMA stuffs + */ + et_dma_mem_destroy(tx_ring->tr_dtag, tx_ring->tr_desc, + tx_ring->tr_dmap); + + /* + * Destroy TX status DMA stuffs + */ + et_dma_mem_destroy(txsd->txsd_dtag, txsd->txsd_status, + txsd->txsd_dmap); + + /* + * Destroy DMA stuffs for RX rings + */ + for (i = 0; i < ET_RX_NRING; ++i) { + struct et_rxdesc_ring *rx_ring = &sc->sc_rx_ring[i]; + + et_dma_mem_destroy(rx_ring->rr_dtag, rx_ring->rr_desc, + rx_ring->rr_dmap); + } + + /* + * Destroy RX stat ring DMA stuffs + */ + et_dma_mem_destroy(rxst_ring->rsr_dtag, rxst_ring->rsr_stat, + rxst_ring->rsr_dmap); + + /* + * Destroy RX status DMA stuffs + */ + et_dma_mem_destroy(rxsd->rxsd_dtag, rxsd->rxsd_status, + rxsd->rxsd_dmap); + + /* + * Destroy mbuf DMA stuffs + */ + for (i = 0; i < ET_RX_NRING; ++i) + rx_done[i] = ET_RX_NDESC; + et_dma_mbuf_destroy(dev, ET_TX_NDESC, rx_done); + + /* + * Destroy top level DMA tag + */ + if (sc->sc_dtag != NULL) + bus_dma_tag_destroy(sc->sc_dtag); +} + +static int +et_dma_mbuf_create(device_t dev) +{ + struct et_softc *sc = device_get_softc(dev); + struct et_txbuf_data *tbd = &sc->sc_tx_data; + int i, error, rx_done[ET_RX_NRING]; + + /* + * Create mbuf DMA tag + */ + error = bus_dma_tag_create(sc->sc_dtag, 1, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, + NULL, NULL, + MCLBYTES, ET_NSEG_MAX, + BUS_SPACE_MAXSIZE_32BIT, + BUS_DMA_ALLOCNOW, &sc->sc_mbuf_dtag); + if (error) { + device_printf(dev, "can't create mbuf DMA tag\n"); + return error; + } + + /* + * Create spare DMA map for RX mbufs + */ + error = bus_dmamap_create(sc->sc_mbuf_dtag, 0, &sc->sc_mbuf_tmp_dmap); + if (error) { + device_printf(dev, "can't create spare mbuf DMA map\n"); + bus_dma_tag_destroy(sc->sc_mbuf_dtag); + sc->sc_mbuf_dtag = NULL; + return error; + } + + /* + * Create DMA maps for RX mbufs + */ + bzero(rx_done, sizeof(rx_done)); + for (i = 0; i < ET_RX_NRING; ++i) { + struct et_rxbuf_data *rbd = &sc->sc_rx_data[i]; + int j; + + for (j = 0; j < ET_RX_NDESC; ++j) { + error = bus_dmamap_create(sc->sc_mbuf_dtag, 0, + &rbd->rbd_buf[j].rb_dmap); + if (error) { + device_printf(dev, "can't create %d RX mbuf " + "for %d RX ring\n", j, i); + rx_done[i] = j; + et_dma_mbuf_destroy(dev, 0, rx_done); + return error; + } + } + rx_done[i] = ET_RX_NDESC; + + rbd->rbd_softc = sc; + rbd->rbd_ring = &sc->sc_rx_ring[i]; + } + + /* + * Create DMA maps for TX mbufs + */ + for (i = 0; i < ET_TX_NDESC; ++i) { + error = bus_dmamap_create(sc->sc_mbuf_dtag, 0, + &tbd->tbd_buf[i].tb_dmap); + if (error) { + device_printf(dev, "can't create %d TX mbuf " + "DMA map\n", i); + et_dma_mbuf_destroy(dev, i, rx_done); + return error; + } + } + + return 0; +} + +static void +et_dma_mbuf_destroy(device_t dev, int tx_done, const int rx_done[]) +{ + struct et_softc *sc = device_get_softc(dev); + struct et_txbuf_data *tbd = &sc->sc_tx_data; + int i; + + if (sc->sc_mbuf_dtag == NULL) + return; + + /* + * Destroy DMA maps for RX mbufs + */ + for (i = 0; i < ET_RX_NRING; ++i) { + struct et_rxbuf_data *rbd = &sc->sc_rx_data[i]; + int j; + + for (j = 0; j < rx_done[i]; ++j) { + struct et_rxbuf *rb = &rbd->rbd_buf[j]; + + KASSERT(rb->rb_mbuf == NULL, + ("RX mbuf in %d RX ring is not freed yet\n", i)); + bus_dmamap_destroy(sc->sc_mbuf_dtag, rb->rb_dmap); + } + } + + /* + * Destroy DMA maps for TX mbufs + */ + for (i = 0; i < tx_done; ++i) { + struct et_txbuf *tb = &tbd->tbd_buf[i]; + + KASSERT(tb->tb_mbuf == NULL, ("TX mbuf is not freed yet\n")); + bus_dmamap_destroy(sc->sc_mbuf_dtag, tb->tb_dmap); + } + + /* + * Destroy spare mbuf DMA map + */ + bus_dmamap_destroy(sc->sc_mbuf_dtag, sc->sc_mbuf_tmp_dmap); + + /* + * Destroy mbuf DMA tag + */ + bus_dma_tag_destroy(sc->sc_mbuf_dtag); + sc->sc_mbuf_dtag = NULL; +} + +static int +et_dma_mem_create(device_t dev, bus_size_t size, bus_dma_tag_t *dtag, + void **addr, bus_addr_t *paddr, bus_dmamap_t *dmap) +{ + struct et_softc *sc = device_get_softc(dev); + int error; + + error = bus_dma_tag_create(sc->sc_dtag, ET_ALIGN, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, + NULL, NULL, + size, 1, BUS_SPACE_MAXSIZE_32BIT, + 0, dtag); + if (error) { + device_printf(dev, "can't create DMA tag\n"); + return error; + } + + error = bus_dmamem_alloc(*dtag, addr, BUS_DMA_WAITOK | BUS_DMA_ZERO, + dmap); + if (error) { + device_printf(dev, "can't allocate DMA mem\n"); + bus_dma_tag_destroy(*dtag); + *dtag = NULL; + return error; + } + + error = bus_dmamap_load(*dtag, *dmap, *addr, size, + et_dma_ring_addr, paddr, BUS_DMA_WAITOK); + if (error) { + device_printf(dev, "can't load DMA mem\n"); + bus_dmamem_free(*dtag, *addr, *dmap); + bus_dma_tag_destroy(*dtag); + *dtag = NULL; + return error; + } + return 0; +} + +static void +et_dma_mem_destroy(bus_dma_tag_t dtag, void *addr, bus_dmamap_t dmap) +{ + if (dtag != NULL) { + bus_dmamap_unload(dtag, dmap); + bus_dmamem_free(dtag, addr, dmap); + bus_dma_tag_destroy(dtag); + } +} + +static void +et_dma_ring_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error) +{ + KASSERT(nseg == 1, ("too many segments\n")); + *((bus_addr_t *)arg) = seg->ds_addr; +} + +static void +et_chip_attach(struct et_softc *sc) +{ + uint32_t val; + + /* + * Perform minimal initialization + */ + + /* Disable loopback */ + CSR_WRITE_4(sc, ET_LOOPBACK, 0); + + /* Reset MAC */ + CSR_WRITE_4(sc, ET_MAC_CFG1, + ET_MAC_CFG1_RST_TXFUNC | ET_MAC_CFG1_RST_RXFUNC | + ET_MAC_CFG1_RST_TXMC | ET_MAC_CFG1_RST_RXMC | + ET_MAC_CFG1_SIM_RST | ET_MAC_CFG1_SOFT_RST); + + /* + * Setup half duplex mode + */ + val = __SHIFTIN(10, ET_MAC_HDX_ALT_BEB_TRUNC) | + __SHIFTIN(15, ET_MAC_HDX_REXMIT_MAX) | + __SHIFTIN(55, ET_MAC_HDX_COLLWIN) | + ET_MAC_HDX_EXC_DEFER; + CSR_WRITE_4(sc, ET_MAC_HDX, val); + + /* Clear MAC control */ + CSR_WRITE_4(sc, ET_MAC_CTRL, 0); + + /* Reset MII */ + CSR_WRITE_4(sc, ET_MII_CFG, ET_MII_CFG_CLKRST); + + /* Bring MAC out of reset state */ + CSR_WRITE_4(sc, ET_MAC_CFG1, 0); + + /* Enable memory controllers */ + CSR_WRITE_4(sc, ET_MMC_CTRL, ET_MMC_CTRL_ENABLE); +} + +static void +et_intr(void *xsc) +{ + struct et_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + uint32_t intrs; + + ASSERT_SERIALIZED(ifp->if_serializer); + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + et_disable_intrs(sc); + + intrs = CSR_READ_4(sc, ET_INTR_STATUS); + intrs &= ET_INTRS; + if (intrs == 0) /* Not interested */ + goto back; + + if (intrs & ET_INTR_RXEOF) + et_rxeof(sc); + if (intrs & (ET_INTR_TXEOF | ET_INTR_TIMER)) + et_txeof(sc); + if (intrs & ET_INTR_TIMER) + CSR_WRITE_4(sc, ET_TIMER, sc->sc_timer); +back: + et_enable_intrs(sc, ET_INTRS); +} + +static void +et_init(void *xsc) +{ + struct et_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + const struct et_bsize *arr; + int error, i; + + ASSERT_SERIALIZED(ifp->if_serializer); + + et_stop(sc); + + arr = ifp->if_mtu <= ETHERMTU ? et_bufsize : NULL; + for (i = 0; i < ET_RX_NRING; ++i) { + sc->sc_rx_data[i].rbd_bufsize = arr[i].bufsize; + sc->sc_rx_data[i].rbd_newbuf = arr[i].newbuf; + } + + error = et_init_tx_ring(sc); + if (error) + goto back; + + error = et_init_rx_ring(sc); + if (error) + goto back; + + error = et_chip_init(sc); + if (error) + goto back; + + error = et_enable_txrx(sc); + if (error) + goto back; + + error = et_start_rxdma(sc); + if (error) + goto back; + + error = et_start_txdma(sc); + if (error) + goto back; + + et_enable_intrs(sc, ET_INTRS); + + callout_reset(&sc->sc_tick, hz, et_tick, sc); + + CSR_WRITE_4(sc, ET_TIMER, sc->sc_timer); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; +back: + if (error) + et_stop(sc); +} + +static int +et_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) +{ + struct et_softc *sc = ifp->if_softc; + struct mii_data *mii = device_get_softc(sc->sc_miibus); + struct ifreq *ifr = (struct ifreq *)data; + int error = 0; + + ASSERT_SERIALIZED(ifp->if_serializer); + + switch (cmd) { + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING) { + if ((ifp->if_flags ^ sc->sc_if_flags) & + (IFF_ALLMULTI | IFF_PROMISC | IFF_BROADCAST)) + et_setmulti(sc); + } else { + et_init(sc); + } + } else { + if (ifp->if_flags & IFF_RUNNING) + et_stop(sc); + } + sc->sc_if_flags = ifp->if_flags; + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifp->if_flags & IFF_RUNNING) + et_setmulti(sc); + break; + + case SIOCSIFMTU: + /* TODO */ + error = EOPNOTSUPP; + break; + + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + return error; +} + +static void +et_start(struct ifnet *ifp) +{ + struct et_softc *sc = ifp->if_softc; + struct et_txbuf_data *tbd = &sc->sc_tx_data; + int trans; + + ASSERT_SERIALIZED(ifp->if_serializer); + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + trans = 0; + for (;;) { + struct mbuf *m; + + if ((tbd->tbd_used + ET_NSEG_SPARE) > ET_TX_NDESC) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + + m = ifq_dequeue(&ifp->if_snd, NULL); + if (m == NULL) + break; + + if (et_encap(sc, &m)) { + ifp->if_oerrors++; + ifp->if_flags |= IFF_OACTIVE; + break; + } + trans = 1; + + BPF_MTAP(ifp, m); + } + + if (trans) + ifp->if_timer = 5; +} + +static void +et_watchdog(struct ifnet *ifp) +{ + ASSERT_SERIALIZED(ifp->if_serializer); + + if_printf(ifp, "watchdog timed out\n"); + + ifp->if_init(ifp->if_softc); + ifp->if_start(ifp); +} + +static int +et_stop_rxdma(struct et_softc *sc) +{ + CSR_WRITE_4(sc, ET_RXDMA_CTRL, + ET_RXDMA_CTRL_HALT | ET_RXDMA_CTRL_RING1_ENABLE); + + DELAY(5); + if ((CSR_READ_4(sc, ET_RXDMA_CTRL) & ET_RXDMA_CTRL_HALTED) == 0) { + if_printf(&sc->arpcom.ac_if, "can't stop RX DMA engine\n"); + return ETIMEDOUT; + } + return 0; +} + +static int +et_stop_txdma(struct et_softc *sc) +{ + CSR_WRITE_4(sc, ET_TXDMA_CTRL, + ET_TXDMA_CTRL_HALT | ET_TXDMA_CTRL_SINGLE_EPKT); + return 0; +} + +static void +et_free_tx_ring(struct et_softc *sc) +{ + struct et_txbuf_data *tbd = &sc->sc_tx_data; + struct et_txdesc_ring *tx_ring = &sc->sc_tx_ring; + int i; + + for (i = 0; i < ET_TX_NDESC; ++i) { + struct et_txbuf *tb = &tbd->tbd_buf[i]; + + if (tb->tb_mbuf != NULL) { + bus_dmamap_unload(sc->sc_mbuf_dtag, tb->tb_dmap); + m_freem(tb->tb_mbuf); + tb->tb_mbuf = NULL; + } + } + + bzero(tx_ring->tr_desc, ET_TX_RING_SIZE); + bus_dmamap_sync(tx_ring->tr_dtag, tx_ring->tr_dmap, + BUS_DMASYNC_PREWRITE); +} + +static void +et_free_rx_ring(struct et_softc *sc) +{ + int n; + + for (n = 0; n < ET_RX_NRING; ++n) { + struct et_rxbuf_data *rbd = &sc->sc_rx_data[n]; + struct et_rxdesc_ring *rx_ring = &sc->sc_rx_ring[n]; + int i; + + for (i = 0; i < ET_RX_NDESC; ++i) { + struct et_rxbuf *rb = &rbd->rbd_buf[i]; + + if (rb->rb_mbuf != NULL) { + bus_dmamap_unload(sc->sc_mbuf_dtag, + rb->rb_dmap); + m_freem(rb->rb_mbuf); + rb->rb_mbuf = NULL; + } + } + + bzero(rx_ring->rr_desc, ET_RX_RING_SIZE); + bus_dmamap_sync(rx_ring->rr_dtag, rx_ring->rr_dmap, + BUS_DMASYNC_PREWRITE); + } +} + +static void +et_setmulti(struct et_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + uint32_t hash[4] = { 0, 0, 0, 0 }; + uint32_t rxmac_ctrl, pktfilt; + struct ifmultiaddr *ifma; + int i, count; + + pktfilt = CSR_READ_4(sc, ET_PKTFILT); + rxmac_ctrl = CSR_READ_4(sc, ET_RXMAC_CTRL); + + pktfilt &= ~(ET_PKTFILT_BCAST | ET_PKTFILT_MCAST | ET_PKTFILT_UCAST); + if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) { + rxmac_ctrl |= ET_RXMAC_CTRL_NO_PKTFILT; + goto back; + } + + count = 0; + LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + uint32_t *hp, h; + + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN); + h = (h & 0x3f800000) >> 23; + + hp = &hash[0]; + if (h >= 32 && h < 64) { + h -= 32; + hp = &hash[1]; + } else if (h >= 64 && h < 96) { + h -= 64; + hp = &hash[2]; + } else if (h >= 96) { + h -= 96; + hp = &hash[3]; + } + *hp |= (1 << h); + + ++count; + } + + for (i = 0; i < 4; ++i) + CSR_WRITE_4(sc, ET_MULTI_HASH + (i * 4), hash[i]); + + if (count > 0) + pktfilt |= ET_PKTFILT_MCAST; + rxmac_ctrl &= ~ET_RXMAC_CTRL_NO_PKTFILT; +back: + CSR_WRITE_4(sc, ET_PKTFILT, pktfilt); + CSR_WRITE_4(sc, ET_RXMAC_CTRL, rxmac_ctrl); +} + +static int +et_chip_init(struct et_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + uint32_t rxq_end; + int error; + + /* + * Split internal memory between TX and RX according to MTU + */ + if (ifp->if_mtu < 2048) + rxq_end = 0x2bc; + else if (ifp->if_mtu < 8192) + rxq_end = 0x1ff; + else + rxq_end = 0x1b3; + CSR_WRITE_4(sc, ET_RXQ_START, 0); + CSR_WRITE_4(sc, ET_RXQ_END, rxq_end); + CSR_WRITE_4(sc, ET_TXQ_START, rxq_end + 1); + CSR_WRITE_4(sc, ET_TXQ_END, ET_INTERN_MEM_END); + + /* No loopback */ + CSR_WRITE_4(sc, ET_LOOPBACK, 0); + + /* Clear MSI configure */ + CSR_WRITE_4(sc, ET_MSI_CFG, 0); + + /* Disable timer */ + CSR_WRITE_4(sc, ET_TIMER, 0); + + /* Initialize MAC */ + et_init_mac(sc); + + /* Enable memory controllers */ + CSR_WRITE_4(sc, ET_MMC_CTRL, ET_MMC_CTRL_ENABLE); + + /* Initialize RX MAC */ + et_init_rxmac(sc); + + /* Initialize TX MAC */ + et_init_txmac(sc); + + /* Initialize RX DMA engine */ + error = et_init_rxdma(sc); + if (error) + return error; + + /* Initialize TX DMA engine */ + error = et_init_txdma(sc); + if (error) + return error; + + return 0; +} + +static int +et_init_tx_ring(struct et_softc *sc) +{ + struct et_txdesc_ring *tx_ring = &sc->sc_tx_ring; + struct et_txstatus_data *txsd = &sc->sc_tx_status; + struct et_txbuf_data *tbd = &sc->sc_tx_data; + + bzero(tx_ring->tr_desc, ET_TX_RING_SIZE); + bus_dmamap_sync(tx_ring->tr_dtag, tx_ring->tr_dmap, + BUS_DMASYNC_PREWRITE); + + tbd->tbd_start_index = 0; + tbd->tbd_start_wrap = 0; + tbd->tbd_used = 0; + + bzero(txsd->txsd_status, sizeof(uint32_t)); + bus_dmamap_sync(txsd->txsd_dtag, txsd->txsd_dmap, + BUS_DMASYNC_PREWRITE); + return 0; +} + +static int +et_init_rx_ring(struct et_softc *sc) +{ + struct et_rxstatus_data *rxsd = &sc->sc_rx_status; + struct et_rxstat_ring *rxst_ring = &sc->sc_rxstat_ring; + int n; + + for (n = 0; n < ET_RX_NRING; ++n) { + struct et_rxbuf_data *rbd = &sc->sc_rx_data[n]; + int i, error; + + for (i = 0; i < ET_RX_NDESC; ++i) { + error = rbd->rbd_newbuf(rbd, i, 1); + if (error) { + if_printf(&sc->arpcom.ac_if, "%d ring %d buf, " + "newbuf failed: %d\n", n, i, error); + return error; + } + } + } + + bzero(rxsd->rxsd_status, sizeof(struct et_rxstatus)); + bus_dmamap_sync(rxsd->rxsd_dtag, rxsd->rxsd_dmap, + BUS_DMASYNC_PREWRITE); + + bzero(rxst_ring->rsr_stat, ET_RXSTAT_RING_SIZE); + bus_dmamap_sync(rxst_ring->rsr_dtag, rxst_ring->rsr_dmap, + BUS_DMASYNC_PREWRITE); + + return 0; +} + +static void +et_dma_buf_addr(void *xctx, bus_dma_segment_t *segs, int nsegs, + bus_size_t mapsz __unused, int error) +{ + struct et_dmamap_ctx *ctx = xctx; + int i; + + if (error) + return; + + if (nsegs > ctx->nsegs) { + ctx->nsegs = 0; + return; + } + + ctx->nsegs = nsegs; + for (i = 0; i < nsegs; ++i) + ctx->segs[i] = segs[i]; +} + +static int +et_init_rxdma(struct et_softc *sc) +{ + struct et_rxstatus_data *rxsd = &sc->sc_rx_status; + struct et_rxstat_ring *rxst_ring = &sc->sc_rxstat_ring; + struct et_rxdesc_ring *rx_ring; + int error; + + error = et_stop_rxdma(sc); + if (error) { + if_printf(&sc->arpcom.ac_if, "can't init RX DMA engine\n"); + return error; + } + + /* + * Install RX status + */ + CSR_WRITE_4(sc, ET_RX_STATUS_HI, ET_ADDR_HI(rxsd->rxsd_paddr)); + CSR_WRITE_4(sc, ET_RX_STATUS_LO, ET_ADDR_LO(rxsd->rxsd_paddr)); + + /* + * Install RX stat ring + */ + CSR_WRITE_4(sc, ET_RXSTAT_HI, ET_ADDR_HI(rxst_ring->rsr_paddr)); + CSR_WRITE_4(sc, ET_RXSTAT_LO, ET_ADDR_LO(rxst_ring->rsr_paddr)); + CSR_WRITE_4(sc, ET_RXSTAT_CNT, ET_RX_NSTAT - 1); + CSR_WRITE_4(sc, ET_RXSTAT_POS, 0); + CSR_WRITE_4(sc, ET_RXSTAT_MINCNT, ((ET_RX_NSTAT * 15) / 100) - 1); + + /* Match ET_RXSTAT_POS */ + rxst_ring->rsr_index = 0; + rxst_ring->rsr_wrap = 0; + + /* + * Install the 2nd RX descriptor ring + */ + rx_ring = &sc->sc_rx_ring[1]; + CSR_WRITE_4(sc, ET_RX_RING1_HI, ET_ADDR_HI(rx_ring->rr_paddr)); + CSR_WRITE_4(sc, ET_RX_RING1_LO, ET_ADDR_LO(rx_ring->rr_paddr)); + CSR_WRITE_4(sc, ET_RX_RING1_CNT, ET_RX_NDESC - 1); + CSR_WRITE_4(sc, ET_RX_RING1_POS, ET_RX_RING1_POS_WRAP); + CSR_WRITE_4(sc, ET_RX_RING1_MINCNT, ((ET_RX_NDESC * 15) / 100) - 1); + + /* Match ET_RX_RING1_POS */ + rx_ring->rr_index = 0; + rx_ring->rr_wrap = 1; + + /* + * Install the 1st RX descriptor ring + */ + rx_ring = &sc->sc_rx_ring[0]; + CSR_WRITE_4(sc, ET_RX_RING0_HI, ET_ADDR_HI(rx_ring->rr_paddr)); + CSR_WRITE_4(sc, ET_RX_RING0_LO, ET_ADDR_LO(rx_ring->rr_paddr)); + CSR_WRITE_4(sc, ET_RX_RING0_CNT, ET_RX_NDESC - 1); + CSR_WRITE_4(sc, ET_RX_RING0_POS, ET_RX_RING0_POS_WRAP); + CSR_WRITE_4(sc, ET_RX_RING0_MINCNT, ((ET_RX_NDESC * 15) / 100) - 1); + + /* Match ET_RX_RING0_POS */ + rx_ring->rr_index = 0; + rx_ring->rr_wrap = 1; + + /* + * RX intr moderation + */ + CSR_WRITE_4(sc, ET_RX_INTR_NPKTS, sc->sc_rx_intr_npkts); + CSR_WRITE_4(sc, ET_RX_INTR_DELAY, sc->sc_rx_intr_delay); + + return 0; +} + +static int +et_init_txdma(struct et_softc *sc) +{ + struct et_txdesc_ring *tx_ring = &sc->sc_tx_ring; + struct et_txstatus_data *txsd = &sc->sc_tx_status; + int error; + + error = et_stop_txdma(sc); + if (error) { + if_printf(&sc->arpcom.ac_if, "can't init TX DMA engine\n"); + return error; + } + + /* + * Install TX descriptor ring + */ + CSR_WRITE_4(sc, ET_TX_RING_HI, ET_ADDR_HI(tx_ring->tr_paddr)); + CSR_WRITE_4(sc, ET_TX_RING_LO, ET_ADDR_LO(tx_ring->tr_paddr)); + CSR_WRITE_4(sc, ET_TX_RING_CNT, ET_TX_NDESC - 1); + + /* + * Install TX status + */ + CSR_WRITE_4(sc, ET_TX_STATUS_HI, ET_ADDR_HI(txsd->txsd_paddr)); + CSR_WRITE_4(sc, ET_TX_STATUS_LO, ET_ADDR_LO(txsd->txsd_paddr)); + + CSR_WRITE_4(sc, ET_TX_READY_POS, 0); + + /* Match ET_TX_READY_POS */ + tx_ring->tr_ready_index = 0; + tx_ring->tr_ready_wrap = 0; + + return 0; +} + +static void +et_init_mac(struct et_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + const uint8_t *eaddr = IF_LLADDR(ifp); + uint32_t val; + + /* Reset MAC */ + CSR_WRITE_4(sc, ET_MAC_CFG1, + ET_MAC_CFG1_RST_TXFUNC | ET_MAC_CFG1_RST_RXFUNC | + ET_MAC_CFG1_RST_TXMC | ET_MAC_CFG1_RST_RXMC | + ET_MAC_CFG1_SIM_RST | ET_MAC_CFG1_SOFT_RST); + + /* + * Setup inter packet gap + */ + val = __SHIFTIN(56, ET_IPG_NONB2B_1) | + __SHIFTIN(88, ET_IPG_NONB2B_2) | + __SHIFTIN(80, ET_IPG_MINIFG) | + __SHIFTIN(96, ET_IPG_B2B); + CSR_WRITE_4(sc, ET_IPG, val); + + /* + * Setup half duplex mode + */ + val = __SHIFTIN(10, ET_MAC_HDX_ALT_BEB_TRUNC) | + __SHIFTIN(15, ET_MAC_HDX_REXMIT_MAX) | + __SHIFTIN(55, ET_MAC_HDX_COLLWIN) | + ET_MAC_HDX_EXC_DEFER; + CSR_WRITE_4(sc, ET_MAC_HDX, val); + + /* Clear MAC control */ + CSR_WRITE_4(sc, ET_MAC_CTRL, 0); + + /* Reset MII */ + CSR_WRITE_4(sc, ET_MII_CFG, ET_MII_CFG_CLKRST); + + /* + * Set MAC address + */ + val = eaddr[2] | (eaddr[3] << 8) | (eaddr[4] << 16) | (eaddr[5] << 24); + CSR_WRITE_4(sc, ET_MAC_ADDR1, val); + val = (eaddr[0] << 16) | (eaddr[1] << 24); + CSR_WRITE_4(sc, ET_MAC_ADDR2, val); + + /* Set max frame length */ + CSR_WRITE_4(sc, ET_MAX_FRMLEN, + ETHER_HDR_LEN + EVL_ENCAPLEN + ifp->if_mtu + ETHER_CRC_LEN); + + /* Bring MAC out of reset state */ + CSR_WRITE_4(sc, ET_MAC_CFG1, 0); +} + +static void +et_init_rxmac(struct et_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + const uint8_t *eaddr = IF_LLADDR(ifp); + uint32_t val; + int i; + + /* Disable RX MAC and WOL */ + CSR_WRITE_4(sc, ET_RXMAC_CTRL, ET_RXMAC_CTRL_WOL_DISABLE); + + /* + * Clear all WOL related registers + */ + for (i = 0; i < 3; ++i) + CSR_WRITE_4(sc, ET_WOL_CRC + (i * 4), 0); + for (i = 0; i < 20; ++i) + CSR_WRITE_4(sc, ET_WOL_MASK + (i * 4), 0); + + /* + * Set WOL source address. XXX is this necessary? + */ + val = (eaddr[2] << 24) | (eaddr[3] << 16) | (eaddr[4] << 8) | eaddr[5]; + CSR_WRITE_4(sc, ET_WOL_SA_LO, val); + val = (eaddr[0] << 8) | eaddr[1]; + CSR_WRITE_4(sc, ET_WOL_SA_HI, val); + + /* Clear packet filters */ + CSR_WRITE_4(sc, ET_PKTFILT, 0); + + /* No ucast filtering */ + CSR_WRITE_4(sc, ET_UCAST_FILTADDR1, 0); + CSR_WRITE_4(sc, ET_UCAST_FILTADDR2, 0); + CSR_WRITE_4(sc, ET_UCAST_FILTADDR3, 0); + + if (ifp->if_mtu > 8192) { + /* + * In order to transmit jumbo packets greater than 8k, + * the FIFO between RX MAC and RX DMA needs to be reduced + * in size to (16k - MTU). In order to implement this, we + * must use "cut through" mode in the RX MAC, which chops + * packets down into segments which are (max_size * 16). + * In this case we selected 256 bytes, since this is the + * size of the PCI-Express TLP's that the 1310 uses. + */ + val = __SHIFTIN(16, ET_RXMAC_MC_SEGSZ_MAX) | + ET_RXMAC_MC_SEGSZ_ENABLE; + } else { + val = 0; + } + CSR_WRITE_4(sc, ET_RXMAC_MC_SEGSZ, val); + + CSR_WRITE_4(sc, ET_RXMAC_MC_WATERMARK, 0); + + /* Initialize RX MAC management register */ + CSR_WRITE_4(sc, ET_RXMAC_MGT, 0); + + CSR_WRITE_4(sc, ET_RXMAC_SPACE_AVL, 0); + + CSR_WRITE_4(sc, ET_RXMAC_MGT, + ET_RXMAC_MGT_PASS_ECRC | + ET_RXMAC_MGT_PASS_ELEN | + ET_RXMAC_MGT_PASS_ETRUNC | + ET_RXMAC_MGT_CHECK_PKT); + + /* + * Configure runt filtering (may not work on certain chip generation) + */ + val = __SHIFTIN(ETHER_MIN_LEN, ET_PKTFILT_MINLEN) | ET_PKTFILT_FRAG; + CSR_WRITE_4(sc, ET_PKTFILT, val); + + /* Enable RX MAC but leave WOL disabled */ + CSR_WRITE_4(sc, ET_RXMAC_CTRL, + ET_RXMAC_CTRL_WOL_DISABLE | ET_RXMAC_CTRL_ENABLE); + + /* + * Setup multicast hash and allmulti/promisc mode + */ + et_setmulti(sc); +} + +static void +et_init_txmac(struct et_softc *sc) +{ + /* Disable TX MAC and FC(?) */ + CSR_WRITE_4(sc, ET_TXMAC_CTRL, ET_TXMAC_CTRL_FC_DISABLE); + + /* No flow control yet */ + CSR_WRITE_4(sc, ET_TXMAC_FLOWCTRL, 0); + + /* Enable TX MAC but leave FC(?) diabled */ + CSR_WRITE_4(sc, ET_TXMAC_CTRL, + ET_TXMAC_CTRL_ENABLE | ET_TXMAC_CTRL_FC_DISABLE); +} + +static int +et_start_rxdma(struct et_softc *sc) +{ + uint32_t val = 0; + + val |= __SHIFTIN(sc->sc_rx_data[0].rbd_bufsize, + ET_RXDMA_CTRL_RING0_SIZE) | + ET_RXDMA_CTRL_RING0_ENABLE; + val |= __SHIFTIN(sc->sc_rx_data[1].rbd_bufsize, + ET_RXDMA_CTRL_RING1_SIZE) | + ET_RXDMA_CTRL_RING1_ENABLE; + + CSR_WRITE_4(sc, ET_RXDMA_CTRL, val); + + DELAY(5); + + if (CSR_READ_4(sc, ET_RXDMA_CTRL) & ET_RXDMA_CTRL_HALTED) { + if_printf(&sc->arpcom.ac_if, "can't start RX DMA engine\n"); + return ETIMEDOUT; + } + return 0; +} + +static int +et_start_txdma(struct et_softc *sc) +{ + CSR_WRITE_4(sc, ET_TXDMA_CTRL, ET_TXDMA_CTRL_SINGLE_EPKT); + return 0; +} + +static int +et_enable_txrx(struct et_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + uint32_t val; + int i; + + val = CSR_READ_4(sc, ET_MAC_CFG1); + val |= ET_MAC_CFG1_TXEN | ET_MAC_CFG1_RXEN; + val &= ~(ET_MAC_CFG1_TXFLOW | ET_MAC_CFG1_RXFLOW | + ET_MAC_CFG1_LOOPBACK); + CSR_WRITE_4(sc, ET_MAC_CFG1, val); + + et_ifmedia_upd(ifp); + +#define NRETRY 100 + + for (i = 0; i < NRETRY; ++i) { + val = CSR_READ_4(sc, ET_MAC_CFG1); + if ((val & (ET_MAC_CFG1_SYNC_TXEN | ET_MAC_CFG1_SYNC_RXEN)) == + (ET_MAC_CFG1_SYNC_TXEN | ET_MAC_CFG1_SYNC_RXEN)) + break; + + DELAY(10); + } + if (i == NRETRY) { + if_printf(ifp, "can't enable RX/TX\n"); + return ETIMEDOUT; + } + +#undef NRETRY + return 0; +} + +static void +et_rxeof(struct et_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + struct et_rxstatus_data *rxsd = &sc->sc_rx_status; + struct et_rxstat_ring *rxst_ring = &sc->sc_rxstat_ring; + uint32_t rxs_stat_ring; + int rxst_wrap, rxst_index; + + bus_dmamap_sync(rxsd->rxsd_dtag, rxsd->rxsd_dmap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_sync(rxst_ring->rsr_dtag, rxst_ring->rsr_dmap, + BUS_DMASYNC_POSTREAD); + + rxs_stat_ring = rxsd->rxsd_status->rxs_stat_ring; + rxst_wrap = (rxs_stat_ring & ET_RXS_STATRING_WRAP) ? 1 : 0; + rxst_index = __SHIFTOUT(rxs_stat_ring, ET_RXS_STATRING_INDEX); + + while (rxst_index != rxst_ring->rsr_index || + rxst_wrap != rxst_ring->rsr_wrap) { + struct et_rxbuf_data *rbd; + struct et_rxdesc_ring *rx_ring; + struct et_rxstat *st; + struct et_rxbuf *rb; + struct mbuf *m; + int buflen, buf_idx, ring_idx; + uint32_t rxstat_pos, rxring_pos; + + KKASSERT(rxst_ring->rsr_index < ET_RX_NSTAT); + st = &rxst_ring->rsr_stat[rxst_ring->rsr_index]; + + buflen = __SHIFTOUT(st->rxst_info2, ET_RXST_INFO2_LEN); + buf_idx = __SHIFTOUT(st->rxst_info2, ET_RXST_INFO2_BUFIDX); + ring_idx = __SHIFTOUT(st->rxst_info2, ET_RXST_INFO2_RINGIDX); + + if (++rxst_ring->rsr_index == ET_RX_NSTAT) { + rxst_ring->rsr_index = 0; + rxst_ring->rsr_wrap ^= 1; + } + rxstat_pos = __SHIFTIN(rxst_ring->rsr_index, + ET_RXSTAT_POS_INDEX); + if (rxst_ring->rsr_wrap) + rxstat_pos |= ET_RXSTAT_POS_WRAP; + CSR_WRITE_4(sc, ET_RXSTAT_POS, rxstat_pos); + + if (ring_idx >= ET_RX_NRING) { + ifp->if_ierrors++; + if_printf(ifp, "invalid ring index %d\n", ring_idx); + continue; + } + if (buf_idx >= ET_RX_NDESC) { + ifp->if_ierrors++; + if_printf(ifp, "invalid buf index %d\n", buf_idx); + continue; + } + + rbd = &sc->sc_rx_data[ring_idx]; + rb = &rbd->rbd_buf[buf_idx]; + m = rb->rb_mbuf; + bus_dmamap_sync(sc->sc_mbuf_dtag, rb->rb_dmap, + BUS_DMASYNC_POSTREAD); + + if (rbd->rbd_newbuf(rbd, buf_idx, 0) == 0) { + m->m_pkthdr.len = m->m_len = buflen; + m->m_pkthdr.rcvif = ifp; + + ifp->if_ipackets++; + ifp->if_input(ifp, m); + } else { + ifp->if_ierrors++; + } + + rx_ring = &sc->sc_rx_ring[ring_idx]; + + if (buf_idx != rx_ring->rr_index) { + if_printf(ifp, "WARNING!! ring %d, " + "buf_idx %d, rr_idx %d\n", + ring_idx, buf_idx, rx_ring->rr_index); + } + + KKASSERT(rx_ring->rr_index < ET_RX_NDESC); + if (++rx_ring->rr_index == ET_RX_NDESC) { + rx_ring->rr_index = 0; + rx_ring->rr_wrap ^= 1; + } + rxring_pos = __SHIFTIN(rx_ring->rr_index, ET_RX_RING_POS_INDEX); + if (rx_ring->rr_wrap) + rxring_pos |= ET_RX_RING_POS_WRAP; + CSR_WRITE_4(sc, rx_ring->rr_posreg, rxring_pos); + } +} + +static int +et_encap(struct et_softc *sc, struct mbuf **m0) +{ + struct mbuf *m = *m0; + bus_dma_segment_t segs[ET_NSEG_MAX]; + struct et_dmamap_ctx ctx; + struct et_txdesc_ring *tx_ring = &sc->sc_tx_ring; + struct et_txbuf_data *tbd = &sc->sc_tx_data; + struct et_txdesc *td; + bus_dmamap_t map; + int error, maxsegs, first_idx, last_idx, i; + uint32_t tx_ready_pos, last_td_ctrl2; + + maxsegs = ET_TX_NDESC - tbd->tbd_used; + if (maxsegs > ET_NSEG_MAX) + maxsegs = ET_NSEG_MAX; + KASSERT(maxsegs >= ET_NSEG_SPARE, + ("not enough spare TX desc (%d)\n", maxsegs)); + + KKASSERT(tx_ring->tr_ready_index < ET_TX_NDESC); + first_idx = tx_ring->tr_ready_index; + map = tbd->tbd_buf[first_idx].tb_dmap; + + ctx.nsegs = maxsegs; + ctx.segs = segs; + error = bus_dmamap_load_mbuf(sc->sc_mbuf_dtag, map, m, + et_dma_buf_addr, &ctx, BUS_DMA_NOWAIT); + if (!error && ctx.nsegs == 0) { + bus_dmamap_unload(sc->sc_mbuf_dtag, map); + error = EFBIG; + } + if (error && error != EFBIG) { + if_printf(&sc->arpcom.ac_if, "can't load TX mbuf"); + goto back; + } + if (error) { /* error == EFBIG */ + struct mbuf *m_new; + + m_new = m_defrag(m, MB_DONTWAIT); + if (m_new == NULL) { + if_printf(&sc->arpcom.ac_if, "can't defrag TX mbuf\n"); + error = ENOBUFS; + goto back; + } else { + *m0 = m = m_new; + } + + ctx.nsegs = maxsegs; + ctx.segs = segs; + error = bus_dmamap_load_mbuf(sc->sc_mbuf_dtag, map, m, + et_dma_buf_addr, &ctx, + BUS_DMA_NOWAIT); + if (error || ctx.nsegs == 0) { + if (ctx.nsegs == 0) { + bus_dmamap_unload(sc->sc_mbuf_dtag, map); + error = EFBIG; + } + if_printf(&sc->arpcom.ac_if, + "can't load defraged TX mbuf\n"); + goto back; + } + } + + bus_dmamap_sync(sc->sc_mbuf_dtag, map, BUS_DMASYNC_PREWRITE); + + last_td_ctrl2 = ET_TDCTRL2_LAST_FRAG; + sc->sc_tx += ctx.nsegs; + if (sc->sc_tx / sc->sc_tx_intr_nsegs != sc->sc_tx_intr) { + sc->sc_tx_intr = sc->sc_tx / sc->sc_tx_intr_nsegs; + last_td_ctrl2 |= ET_TDCTRL2_INTR; + } + + last_idx = -1; + for (i = 0; i < ctx.nsegs; ++i) { + int idx; + + idx = (first_idx + i) % ET_TX_NDESC; + td = &tx_ring->tr_desc[idx]; + td->td_addr_hi = ET_ADDR_HI(segs[i].ds_addr); + td->td_addr_lo = ET_ADDR_LO(segs[i].ds_addr); + td->td_ctrl1 = __SHIFTIN(segs[i].ds_len, ET_TDCTRL1_LEN); + + if (i == ctx.nsegs - 1) { /* Last frag */ + td->td_ctrl2 = last_td_ctrl2; + last_idx = idx; + } + + KKASSERT(tx_ring->tr_ready_index < ET_TX_NDESC); + if (++tx_ring->tr_ready_index == ET_TX_NDESC) { + tx_ring->tr_ready_index = 0; + tx_ring->tr_ready_wrap ^= 1; + } + } + td = &tx_ring->tr_desc[first_idx]; + td->td_ctrl2 |= ET_TDCTRL2_FIRST_FRAG; /* First frag */ + + KKASSERT(last_idx >= 0); + tbd->tbd_buf[first_idx].tb_dmap = tbd->tbd_buf[last_idx].tb_dmap; + tbd->tbd_buf[last_idx].tb_dmap = map; + tbd->tbd_buf[last_idx].tb_mbuf = m; + + tbd->tbd_used += ctx.nsegs; + KKASSERT(tbd->tbd_used <= ET_TX_NDESC); + + bus_dmamap_sync(tx_ring->tr_dtag, tx_ring->tr_dmap, + BUS_DMASYNC_PREWRITE); + + tx_ready_pos = __SHIFTIN(tx_ring->tr_ready_index, + ET_TX_READY_POS_INDEX); + if (tx_ring->tr_ready_wrap) + tx_ready_pos |= ET_TX_READY_POS_WRAP; + CSR_WRITE_4(sc, ET_TX_READY_POS, tx_ready_pos); + + error = 0; +back: + if (error) { + m_freem(m); + *m0 = NULL; + } + return error; +} + +static void +et_txeof(struct et_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + struct et_txdesc_ring *tx_ring = &sc->sc_tx_ring; + struct et_txbuf_data *tbd = &sc->sc_tx_data; + uint32_t tx_done; + int end, wrap; + + if (tbd->tbd_used == 0) + return; + + tx_done = CSR_READ_4(sc, ET_TX_DONE_POS); + end = __SHIFTOUT(tx_done, ET_TX_DONE_POS_INDEX); + wrap = (tx_done & ET_TX_DONE_POS_WRAP) ? 1 : 0; + + while (tbd->tbd_start_index != end || tbd->tbd_start_wrap != wrap) { + struct et_txbuf *tb; + + KKASSERT(tbd->tbd_start_index < ET_TX_NDESC); + tb = &tbd->tbd_buf[tbd->tbd_start_index]; + + bzero(&tx_ring->tr_desc[tbd->tbd_start_index], + sizeof(struct et_txdesc)); + bus_dmamap_sync(tx_ring->tr_dtag, tx_ring->tr_dmap, + BUS_DMASYNC_PREWRITE); + + if (tb->tb_mbuf != NULL) { + bus_dmamap_unload(sc->sc_mbuf_dtag, tb->tb_dmap); + m_freem(tb->tb_mbuf); + tb->tb_mbuf = NULL; + } + + if (++tbd->tbd_start_index == ET_TX_NDESC) { + tbd->tbd_start_index = 0; + tbd->tbd_start_wrap ^= 1; + } + + KKASSERT(tbd->tbd_used > 0); + tbd->tbd_used--; + } + + if (tbd->tbd_used == 0) + ifp->if_timer = 0; + if (tbd->tbd_used + ET_NSEG_SPARE <= ET_TX_NDESC) + ifp->if_flags &= ~IFF_OACTIVE; + + ifp->if_start(ifp); +} + +static void +et_tick(void *xsc) +{ + struct et_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + + lwkt_serialize_enter(ifp->if_serializer); + + mii_tick(device_get_softc(sc->sc_miibus)); + callout_reset(&sc->sc_tick, hz, et_tick, sc); + + lwkt_serialize_exit(ifp->if_serializer); +} + +static int +et_newbuf_cluster(struct et_rxbuf_data *rbd, int buf_idx, int init) +{ + return et_newbuf(rbd, buf_idx, init, MCLBYTES); +} + +static int +et_newbuf_hdr(struct et_rxbuf_data *rbd, int buf_idx, int init) +{ + return et_newbuf(rbd, buf_idx, init, MHLEN); +} + +static int +et_newbuf(struct et_rxbuf_data *rbd, int buf_idx, int init, int len0) +{ + struct et_softc *sc = rbd->rbd_softc; + struct et_rxdesc_ring *rx_ring; + struct et_rxdesc *desc; + struct et_rxbuf *rb; + struct mbuf *m; + struct et_dmamap_ctx ctx; + bus_dma_segment_t seg; + bus_dmamap_t dmap; + int error, len; + + KKASSERT(buf_idx < ET_RX_NDESC); + rb = &rbd->rbd_buf[buf_idx]; + + m = m_getl(len0, init ? MB_WAIT : MB_DONTWAIT, MT_DATA, M_PKTHDR, &len); + if (m == NULL) { + error = ENOBUFS; + + /* XXX for debug */ + if_printf(&sc->arpcom.ac_if, + "m_getl failed, size %d\n", len0); + if (init) { + return error; + } else { + goto back; + } + } + m->m_len = m->m_pkthdr.len = len; + + /* + * Try load RX mbuf into temporary DMA tag + */ + ctx.nsegs = 1; + ctx.segs = &seg; + error = bus_dmamap_load_mbuf(sc->sc_mbuf_dtag, sc->sc_mbuf_tmp_dmap, m, + et_dma_buf_addr, &ctx, + init ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT); + if (error || ctx.nsegs == 0) { + if (!error) { + bus_dmamap_unload(sc->sc_mbuf_dtag, + sc->sc_mbuf_tmp_dmap); + error = EFBIG; + if_printf(&sc->arpcom.ac_if, "too many segments?!\n"); + } + m_freem(m); + + /* XXX for debug */ + if_printf(&sc->arpcom.ac_if, "can't load RX mbuf\n"); + if (init) { + return error; + } else { + goto back; + } + } + + if (!init) + bus_dmamap_unload(sc->sc_mbuf_dtag, rb->rb_dmap); + rb->rb_mbuf = m; + rb->rb_paddr = seg.ds_addr; + + /* + * Swap RX buf's DMA map with the loaded temporary one + */ + dmap = rb->rb_dmap; + rb->rb_dmap = sc->sc_mbuf_tmp_dmap; + sc->sc_mbuf_tmp_dmap = dmap; + + error = 0; +back: + rx_ring = rbd->rbd_ring; + desc = &rx_ring->rr_desc[buf_idx]; + + desc->rd_addr_hi = ET_ADDR_HI(rb->rb_paddr); + desc->rd_addr_lo = ET_ADDR_LO(rb->rb_paddr); + desc->rd_ctrl = __SHIFTIN(buf_idx, ET_RDCTRL_BUFIDX); + + bus_dmamap_sync(rx_ring->rr_dtag, rx_ring->rr_dmap, + BUS_DMASYNC_PREWRITE); + return error; +} + +static int +et_sysctl_rx_intr_npkts(SYSCTL_HANDLER_ARGS) +{ + struct et_softc *sc = arg1; + struct ifnet *ifp = &sc->arpcom.ac_if; + int error = 0, v; + + lwkt_serialize_enter(ifp->if_serializer); + + v = sc->sc_rx_intr_npkts; + error = sysctl_handle_int(oidp, &v, 0, req); + if (error || req->newptr == NULL) + goto back; + if (v <= 0) { + error = EINVAL; + goto back; + } + + if (sc->sc_rx_intr_npkts != v) { + if (ifp->if_flags & IFF_RUNNING) + CSR_WRITE_4(sc, ET_RX_INTR_NPKTS, v); + sc->sc_rx_intr_npkts = v; + } +back: + lwkt_serialize_exit(ifp->if_serializer); + return error; +} + +static int +et_sysctl_rx_intr_delay(SYSCTL_HANDLER_ARGS) +{ + struct et_softc *sc = arg1; + struct ifnet *ifp = &sc->arpcom.ac_if; + int error = 0, v; + + lwkt_serialize_enter(ifp->if_serializer); + + v = sc->sc_rx_intr_delay; + error = sysctl_handle_int(oidp, &v, 0, req); + if (error || req->newptr == NULL) + goto back; + if (v <= 0) { + error = EINVAL; + goto back; + } + + if (sc->sc_rx_intr_delay != v) { + if (ifp->if_flags & IFF_RUNNING) + CSR_WRITE_4(sc, ET_RX_INTR_DELAY, v); + sc->sc_rx_intr_delay = v; + } +back: + lwkt_serialize_exit(ifp->if_serializer); + return error; +} diff --git a/sys/dev/netif/et/if_etreg.h b/sys/dev/netif/et/if_etreg.h new file mode 100644 index 0000000000..1bbdef5ae6 --- /dev/null +++ b/sys/dev/netif/et/if_etreg.h @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2007 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * 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. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + * $DragonFly: src/sys/dev/netif/et/if_etreg.h,v 1.1 2007/10/12 14:12:42 sephe Exp $ + */ + +#ifndef _IF_ETREG_H +#define _IF_ETREG_H + +#define ET_INTERN_MEM_SIZE 0x400 +#define ET_INTERN_MEM_END (ET_INTERN_MEM_SIZE - 1) + +/* + * PCI registers + * + * ET_PCIV_ACK_LATENCY_{128,256} are from + * PCI EXPRESS BASE SPECIFICATION, REV. 1.0a, Table 3-5 + * + * ET_PCIV_REPLAY_TIMER_{128,256} are from + * PCI EXPRESS BASE SPECIFICATION, REV. 1.0a, Table 3-4 + */ +#define ET_PCIR_BAR PCIR_BAR(0) + +#define ET_PCIR_DEVICE_CAPS 0x4c +#define ET_PCIM_DEVICE_CAPS_MAX_PLSZ 0x7 /* Max playload size */ +#define ET_PCIV_DEVICE_CAPS_PLSZ_128 0x0 +#define ET_PCIV_DEVICE_CAPS_PLSZ_256 0x1 + +#define ET_PCIR_DEVICE_CTRL 0x50 +#define ET_PCIM_DEVICE_CTRL_MAX_RRSZ 0x7000 /* Max read request size */ +#define ET_PCIV_DEVICE_CTRL_RRSZ_2K 0x4000 + +#define ET_PCIR_MAC_ADDR0 0xa4 +#define ET_PCIR_MAC_ADDR1 0xa8 + +#define ET_PCIR_EEPROM_STATUS 0xb2 /* XXX undocumented */ +#define ET_PCIM_EEPROM_STATUS_ERROR 0x4c + +#define ET_PCIR_ACK_LATENCY 0xc0 +#define ET_PCIV_ACK_LATENCY_128 237 +#define ET_PCIV_ACK_LATENCY_256 416 + +#define ET_PCIR_REPLAY_TIMER 0xc2 +#define ET_REPLAY_TIMER_RX_L0S_ADJ 250 /* XXX infered from default */ +#define ET_PCIV_REPLAY_TIMER_128 (711 + ET_REPLAY_TIMER_RX_L0S_ADJ) +#define ET_PCIV_REPLAY_TIMER_256 (1248 + ET_REPLAY_TIMER_RX_L0S_ADJ) + +#define ET_PCIR_L0S_L1_LATENCY 0xcf +#define ET_PCIM_L0S_LATENCY __BITS(2, 0) +#define ET_PCIM_L1_LATENCY __BITS(5, 3) +#define ET_PCIV_L0S_LATENCY(l) __SHIFTIN((l) - 1, ET_PCIM_L0S_LATENCY) +#define ET_PCIV_L1_LATENCY(l) __SHIFTIN((l) - 1, ET_PCIM_L1_LATENCY) + +/* + * CSR + */ +#define ET_TXQ_START 0x0000 +#define ET_TXQ_END 0x0004 +#define ET_RXQ_START 0x0008 +#define ET_RXQ_END 0x000c + +#define ET_PM 0x0010 +#define ET_PM_SYSCLK_GATE __BIT(3) +#define ET_PM_TXCLK_GATE __BIT(4) +#define ET_PM_RXCLK_GATE __BIT(5) + +#define ET_INTR_STATUS 0x0018 +#define ET_INTR_MASK 0x001c + +#define ET_SWRST 0x0028 +#define ET_SWRST_TXDMA __BIT(0) +#define ET_SWRST_RXDMA __BIT(1) +#define ET_SWRST_TXMAC __BIT(2) +#define ET_SWRST_RXMAC __BIT(3) +#define ET_SWRST_MAC __BIT(4) +#define ET_SWRST_MAC_STAT __BIT(5) +#define ET_SWRST_MMC __BIT(6) +#define ET_SWRST_SELFCLR_DISABLE __BIT(31) + +#define ET_MSI_CFG 0x0030 + +#define ET_LOOPBACK 0x0034 + +#define ET_TIMER 0x0038 + +#define ET_TXDMA_CTRL 0x1000 +#define ET_TXDMA_CTRL_HALT __BIT(0) +#define ET_TXDMA_CTRL_CACHE_THR __BITS(7, 4) +#define ET_TXDMA_CTRL_SINGLE_EPKT __BIT(8) /* ??? */ + +#define ET_TX_RING_HI 0x1004 +#define ET_TX_RING_LO 0x1008 +#define ET_TX_RING_CNT 0x100c + +#define ET_TX_STATUS_HI 0x101c +#define ET_TX_STATUS_LO 0x1020 + +#define ET_TX_READY_POS 0x1024 +#define ET_TX_READY_POS_INDEX __BITS(9, 0) +#define ET_TX_READY_POS_WRAP __BIT(10) + +#define ET_TX_DONE_POS 0x1060 +#define ET_TX_DONE_POS_INDEX __BITS(9, 0) +#define ET_TX_DONE_POS_WRAP __BIT(10) + +#define ET_RXDMA_CTRL 0x2000 +#define ET_RXDMA_CTRL_HALT __BIT(0) +#define ET_RXDMA_CTRL_RING0_SIZE __BITS(9, 8) +#define ET_RXDMA_CTRL_RING0_ENABLE __BIT(10) +#define ET_RXDMA_CTRL_RING1_SIZE __BITS(12, 11) +#define ET_RXDMA_CTRL_RING1_ENABLE __BIT(13) +#define ET_RXDMA_CTRL_HALTED __BIT(17) + +#define ET_RX_STATUS_LO 0x2004 +#define ET_RX_STATUS_HI 0x2008 + +#define ET_RX_INTR_NPKTS 0x200c +#define ET_RX_INTR_DELAY 0x2010 + +#define ET_RXSTAT_LO 0x2020 +#define ET_RXSTAT_HI 0x2024 +#define ET_RXSTAT_CNT 0x2028 + +#define ET_RXSTAT_POS 0x2030 +#define ET_RXSTAT_POS_INDEX __BITS(11, 0) +#define ET_RXSTAT_POS_WRAP __BIT(12) + +#define ET_RXSTAT_MINCNT 0x2038 + +#define ET_RX_RING0_LO 0x203c +#define ET_RX_RING0_HI 0x2040 +#define ET_RX_RING0_CNT 0x2044 + +#define ET_RX_RING0_POS 0x204c +#define ET_RX_RING0_POS_INDEX __BITS(9, 0) +#define ET_RX_RING0_POS_WRAP __BIT(10) + +#define ET_RX_RING0_MINCNT 0x2054 + +#define ET_RX_RING1_LO 0x2058 +#define ET_RX_RING1_HI 0x205c +#define ET_RX_RING1_CNT 0x2060 + +#define ET_RX_RING1_POS 0x2068 +#define ET_RX_RING1_POS_INDEX __BITS(9, 0) +#define ET_RX_RING1_POS_WRAP __BIT(10) + +#define ET_RX_RING1_MINCNT 0x2070 + +#define ET_TXMAC_CTRL 0x3000 +#define ET_TXMAC_CTRL_ENABLE __BIT(0) +#define ET_TXMAC_CTRL_FC_DISABLE __BIT(3) + +#define ET_TXMAC_FLOWCTRL 0x3010 + +#define ET_RXMAC_CTRL 0x4000 +#define ET_RXMAC_CTRL_ENABLE __BIT(0) +#define ET_RXMAC_CTRL_NO_PKTFILT __BIT(2) +#define ET_RXMAC_CTRL_WOL_DISABLE __BIT(3) + +#define ET_WOL_CRC 0x4004 +#define ET_WOL_SA_LO 0x4010 +#define ET_WOL_SA_HI 0x4014 +#define ET_WOL_MASK 0x4018 + +#define ET_UCAST_FILTADDR1 0x4068 +#define ET_UCAST_FILTADDR2 0x406c +#define ET_UCAST_FILTADDR3 0x4070 + +#define ET_MULTI_HASH 0x4074 + +#define ET_PKTFILT 0x4084 +#define ET_PKTFILT_BCAST __BIT(0) +#define ET_PKTFILT_MCAST __BIT(1) +#define ET_PKTFILT_UCAST __BIT(2) +#define ET_PKTFILT_FRAG __BIT(3) +#define ET_PKTFILT_MINLEN __BITS(22, 16) + +#define ET_RXMAC_MC_SEGSZ 0x4088 +#define ET_RXMAC_MC_SEGSZ_ENABLE __BIT(0) +#define ET_RXMAC_MC_SEGSZ_FC __BIT(1) +#define ET_RXMAC_MC_SEGSZ_MAX __BITS(9, 2) + +#define ET_RXMAC_MC_WATERMARK 0x408c +#define ET_RXMAC_SPACE_AVL 0x4094 + +#define ET_RXMAC_MGT 0x4098 +#define ET_RXMAC_MGT_PASS_ECRC __BIT(4) +#define ET_RXMAC_MGT_PASS_ELEN __BIT(5) +#define ET_RXMAC_MGT_PASS_ETRUNC __BIT(16) +#define ET_RXMAC_MGT_CHECK_PKT __BIT(17) + +#define ET_MAC_CFG1 0x5000 +#define ET_MAC_CFG1_TXEN __BIT(0) +#define ET_MAC_CFG1_SYNC_TXEN __BIT(1) +#define ET_MAC_CFG1_RXEN __BIT(2) +#define ET_MAC_CFG1_SYNC_RXEN __BIT(3) +#define ET_MAC_CFG1_TXFLOW __BIT(4) +#define ET_MAC_CFG1_RXFLOW __BIT(5) +#define ET_MAC_CFG1_LOOPBACK __BIT(8) +#define ET_MAC_CFG1_RST_TXFUNC __BIT(16) +#define ET_MAC_CFG1_RST_RXFUNC __BIT(17) +#define ET_MAC_CFG1_RST_TXMC __BIT(18) +#define ET_MAC_CFG1_RST_RXMC __BIT(19) +#define ET_MAC_CFG1_SIM_RST __BIT(30) +#define ET_MAC_CFG1_SOFT_RST __BIT(31) + +#define ET_MAC_CFG2 0x5004 +#define ET_MAC_CFG2_FDX __BIT(0) +#define ET_MAC_CFG2_CRC __BIT(1) +#define ET_MAC_CFG2_PADCRC __BIT(2) +#define ET_MAC_CFG2_LENCHK __BIT(4) +#define ET_MAC_CFG2_BIGFRM __BIT(5) +#define ET_MAC_CFG2_MODE_MII __BIT(8) +#define ET_MAC_CFG2_MODE_GMII __BIT(9) +#define ET_MAC_CFG2_PREAMBLE_LEN __BITS(15, 12) + +#define ET_IPG 0x5008 +#define ET_IPG_B2B __BITS(6, 0) +#define ET_IPG_MINIFG __BITS(15, 8) +#define ET_IPG_NONB2B_2 __BITS(22, 16) +#define ET_IPG_NONB2B_1 __BITS(30, 24) + +#define ET_MAC_HDX 0x500c +#define ET_MAC_HDX_COLLWIN __BITS(9, 0) +#define ET_MAC_HDX_REXMIT_MAX __BITS(15, 12) +#define ET_MAC_HDX_EXC_DEFER __BIT(16) +#define ET_MAC_HDX_NOBACKOFF __BIT(17) +#define ET_MAC_HDX_BP_NOBACKOFF __BIT(18) +#define ET_MAC_HDX_ALT_BEB __BIT(19) +#define ET_MAC_HDX_ALT_BEB_TRUNC __BITS(23, 20) + +#define ET_MAX_FRMLEN 0x5010 + +#define ET_MII_CFG 0x5020 +#define ET_MII_CFG_CLKRST __BITS(2, 0) +#define ET_MII_CFG_PREAMBLE_SUP __BIT(4) +#define ET_MII_CFG_SCAN_AUTOINC __BIT(5) +#define ET_MII_CFG_RST __BIT(31) + +#define ET_MII_CMD 0x5024 +#define ET_MII_CMD_READ __BIT(0) + +#define ET_MII_ADDR 0x5028 +#define ET_MII_ADDR_REG __BITS(4, 0) +#define ET_MII_ADDR_PHY __BITS(12, 8) + +#define ET_MII_CTRL 0x502c +#define ET_MII_CTRL_VALUE __BITS(15, 0) + +#define ET_MII_STAT 0x5030 +#define ET_MII_STAT_VALUE __BITS(15, 0) + +#define ET_MII_IND 0x5034 +#define ET_MII_IND_BUSY __BIT(0) +#define ET_MII_IND_INVALID __BIT(2) + +#define ET_MAC_CTRL 0x5038 +#define ET_MAC_CTRL_MODE_MII __BIT(24) +#define ET_MAC_CTRL_LHDX __BIT(25) +#define ET_MAC_CTRL_GHDX __BIT(26) + +#define ET_MAC_ADDR1 0x5040 +#define ET_MAC_ADDR2 0x5044 + +#define ET_MMC_CTRL 0x7000 +#define ET_MMC_CTRL_ENABLE __BIT(0) +#define ET_MMC_CTRL_ARB_DISABLE __BIT(1) +#define ET_MMC_CTRL_RXMAC_DISABLE __BIT(2) +#define ET_MMC_CTRL_TXMAC_DISABLE __BIT(3) +#define ET_MMC_CTRL_TXDMA_DISABLE __BIT(4) +#define ET_MMC_CTRL_RXDMA_DISABLE __BIT(5) +#define ET_MMC_CTRL_FORCE_CE __BIT(6) + +/* + * Interrupts + */ +#define ET_INTR_TXEOF __BIT(3) +#define ET_INTR_TXDMA_ERROR __BIT(4) +#define ET_INTR_RXEOF __BIT(5) +#define ET_INTR_RXRING0_LOW __BIT(6) +#define ET_INTR_RXRING1_LOW __BIT(7) +#define ET_INTR_RXSTAT_LOW __BIT(8) +#define ET_INTR_RXDMA_ERROR __BIT(9) +#define ET_INTR_TIMER __BIT(14) +#define ET_INTR_WOL __BIT(15) +#define ET_INTR_PHY __BIT(16) +#define ET_INTR_TXMAC __BIT(17) +#define ET_INTR_RXMAC __BIT(18) +#define ET_INTR_MAC_STATS __BIT(19) +#define ET_INTR_SLAVE_TO __BIT(20) + +#define ET_INTRS (ET_INTR_TXEOF | \ + ET_INTR_RXEOF | \ + ET_INTR_TIMER) + +/* + * RX ring position uses same layout + */ +#define ET_RX_RING_POS_INDEX __BITS(9, 0) +#define ET_RX_RING_POS_WRAP __BIT(10) + +#endif /* !_IF_ETREG_H */ diff --git a/sys/dev/netif/et/if_etvar.h b/sys/dev/netif/et/if_etvar.h new file mode 100644 index 0000000000..193d52c2c8 --- /dev/null +++ b/sys/dev/netif/et/if_etvar.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2007 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * 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. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + * $DragonFly: src/sys/dev/netif/et/if_etvar.h,v 1.1 2007/10/12 14:12:42 sephe Exp $ + */ + +#ifndef _IF_ETVAR_H +#define _IF_ETVAR_H + +#define ET_ALIGN 0x1000 +#define ET_NSEG_MAX 32 /* XXX no limit actually */ +#define ET_NSEG_SPARE 5 + +#define ET_TX_NDESC 512 +#define ET_RX_NDESC 512 +#define ET_RX_NRING 2 +#define ET_RX_NSTAT (ET_RX_NRING * ET_RX_NDESC) + +#define ET_TX_RING_SIZE (ET_TX_NDESC * sizeof(struct et_txdesc)) +#define ET_RX_RING_SIZE (ET_RX_NDESC * sizeof(struct et_rxdesc)) +#define ET_RXSTAT_RING_SIZE (ET_RX_NSTAT * sizeof(struct et_rxstat)) + +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4((sc)->sc_mem_bt, (sc)->sc_mem_bh, (reg), (val)) +#define CSR_READ_4(sc, reg) \ + bus_space_read_4((sc)->sc_mem_bt, (sc)->sc_mem_bh, (reg)) + +#define ET_ADDR_HI(addr) ((uint64_t) (addr) >> 32) +#define ET_ADDR_LO(addr) ((uint64_t) (addr) & 0xffffffff) + +struct et_txdesc { + uint32_t td_addr_hi; + uint32_t td_addr_lo; + uint32_t td_ctrl1; /* ET_TDCTRL1_ */ + uint32_t td_ctrl2; /* ET_TDCTRL2_ */ +} __packed; + +#define ET_TDCTRL1_LEN __BITS(15, 0) + +#define ET_TDCTRL2_LAST_FRAG __BIT(0) +#define ET_TDCTRL2_FIRST_FRAG __BIT(1) +#define ET_TDCTRL2_INTR __BIT(2) + +struct et_rxdesc { + uint32_t rd_addr_lo; + uint32_t rd_addr_hi; + uint32_t rd_ctrl; /* ET_RDCTRL_ */ +} __packed; + +#define ET_RDCTRL_BUFIDX __BITS(9, 0) + +struct et_rxstat { + uint32_t rxst_info1; + uint32_t rxst_info2; /* ET_RXST_INFO2_ */ +} __packed; + +#define ET_RXST_INFO2_LEN __BITS(15, 0) +#define ET_RXST_INFO2_BUFIDX __BITS(25, 16) +#define ET_RXST_INFO2_RINGIDX __BITS(27, 26) + +struct et_rxstatus { + uint32_t rxs_ring; + uint32_t rxs_stat_ring; /* ET_RXS_STATRING_ */ +} __packed; + +#define ET_RXS_STATRING_INDEX __BITS(27, 16) +#define ET_RXS_STATRING_WRAP __BIT(28) + +struct et_dmamap_ctx { + int nsegs; + bus_dma_segment_t *segs; +}; + +struct et_txbuf { + struct mbuf *tb_mbuf; + bus_dmamap_t tb_dmap; +}; + +struct et_rxbuf { + struct mbuf *rb_mbuf; + bus_dmamap_t rb_dmap; + bus_addr_t rb_paddr; +}; + +struct et_txstatus_data { + uint32_t *txsd_status; + bus_addr_t txsd_paddr; + bus_dma_tag_t txsd_dtag; + bus_dmamap_t txsd_dmap; +}; + +struct et_rxstatus_data { + struct et_rxstatus *rxsd_status; + bus_addr_t rxsd_paddr; + bus_dma_tag_t rxsd_dtag; + bus_dmamap_t rxsd_dmap; +}; + +struct et_rxstat_ring { + struct et_rxstat *rsr_stat; + bus_addr_t rsr_paddr; + bus_dma_tag_t rsr_dtag; + bus_dmamap_t rsr_dmap; + + int rsr_index; + int rsr_wrap; +}; + +struct et_txdesc_ring { + struct et_txdesc *tr_desc; + bus_addr_t tr_paddr; + bus_dma_tag_t tr_dtag; + bus_dmamap_t tr_dmap; + + int tr_ready_index; + int tr_ready_wrap; +}; + +struct et_rxdesc_ring { + struct et_rxdesc *rr_desc; + bus_addr_t rr_paddr; + bus_dma_tag_t rr_dtag; + bus_dmamap_t rr_dmap; + + uint32_t rr_posreg; + int rr_index; + int rr_wrap; +}; + +struct et_txbuf_data { + struct et_txbuf tbd_buf[ET_TX_NDESC]; + + int tbd_start_index; + int tbd_start_wrap; + int tbd_used; +}; + +struct et_softc; +struct et_rxbuf_data; +typedef int (*et_newbuf_t)(struct et_rxbuf_data *, int, int); + +struct et_rxbuf_data { + struct et_rxbuf rbd_buf[ET_RX_NDESC]; + + struct et_softc *rbd_softc; + struct et_rxdesc_ring *rbd_ring; + + int rbd_bufsize; + et_newbuf_t rbd_newbuf; +}; + +struct et_softc { + struct arpcom arpcom; + int sc_if_flags; + + int sc_mem_rid; + struct resource *sc_mem_res; + bus_space_tag_t sc_mem_bt; + bus_space_handle_t sc_mem_bh; + + int sc_irq_rid; + struct resource *sc_irq_res; + void *sc_irq_handle; + + device_t sc_miibus; + struct callout sc_tick; + + bus_dma_tag_t sc_dtag; + + struct et_rxdesc_ring sc_rx_ring[ET_RX_NRING]; + struct et_rxstat_ring sc_rxstat_ring; + struct et_rxstatus_data sc_rx_status; + + struct et_txdesc_ring sc_tx_ring; + struct et_txstatus_data sc_tx_status; + + bus_dma_tag_t sc_mbuf_dtag; + bus_dmamap_t sc_mbuf_tmp_dmap; + struct et_rxbuf_data sc_rx_data[ET_RX_NRING]; + struct et_txbuf_data sc_tx_data; + + uint32_t sc_tx; + uint32_t sc_tx_intr; + + struct sysctl_ctx_list sc_sysctl_ctx; + struct sysctl_oid *sc_sysctl_tree; + + /* + * Sysctl variables + */ + int sc_rx_intr_npkts; + int sc_rx_intr_delay; + int sc_tx_intr_nsegs; + uint32_t sc_timer; +}; + +#endif /* !_IF_ETVAR_H */ diff --git a/sys/dev/netif/mii_layer/Makefile b/sys/dev/netif/mii_layer/Makefile index 7e14527935..287e744e24 100644 --- a/sys/dev/netif/mii_layer/Makefile +++ b/sys/dev/netif/mii_layer/Makefile @@ -1,12 +1,12 @@ # $FreeBSD: src/sys/modules/mii/Makefile,v 1.11.2.6 2001/06/18 18:49:12 wpaul Exp $ -# $DragonFly: src/sys/dev/netif/mii_layer/Makefile,v 1.10 2006/11/16 13:43:55 sephe Exp $ +# $DragonFly: src/sys/dev/netif/mii_layer/Makefile,v 1.11 2007/10/12 14:12:42 sephe Exp $ KMOD = miibus SRCS = mii.c mii_physubr.c ukphy.c ukphy_subr.c bus_if.h pci_if.h SRCS += miibus_if.h device_if.h miibus_if.c e1000phy.c exphy.c nsphy.c SRCS += mlphy.c tlphy.c rlphy.c amphy.c dcphy.c pnphy.c nsgphy.c SRCS += pnaphy.c brgphy.c xmphy.c inphy.c lxtphy.c qsphy.c acphy.c -SRCS += ruephy.c rgephy.c ciphy.c ip1000phy.c +SRCS += ruephy.c rgephy.c ciphy.c ip1000phy.c truephy.c .include "./Makefile.miidevs" diff --git a/sys/dev/netif/mii_layer/truephy.c b/sys/dev/netif/mii_layer/truephy.c new file mode 100644 index 0000000000..ee9443e1b6 --- /dev/null +++ b/sys/dev/netif/mii_layer/truephy.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2007 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * 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. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + * $DragonFly: src/sys/dev/netif/mii_layer/truephy.c,v 1.1 2007/10/12 14:12:42 sephe Exp $ + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "miibus_if.h" + +static int truephy_service(struct mii_softc *, struct mii_data *, int); +static int truephy_attach(device_t); +static int truephy_probe(device_t); +static void truephy_reset(struct mii_softc *); +static void truephy_status(struct mii_softc *); + +static device_method_t truephy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, truephy_probe), + DEVMETHOD(device_attach, truephy_attach), + DEVMETHOD(device_detach, ukphy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + { 0, 0 } +}; + +static const struct mii_phydesc truephys[] = { + MII_PHYDESC(AGERE, ET1011C), + MII_PHYDESC_NULL +}; + +static devclass_t truephy_devclass; + +static driver_t truephy_driver = { + "truephy", + truephy_methods, + sizeof(struct mii_softc) +}; + +DRIVER_MODULE(truephy, miibus, truephy_driver, truephy_devclass, 0, 0); + +static const struct truephy_dsp { + uint16_t index; + uint16_t data; +} truephy_dspcode[] = { + { 0x880b, 0x0926 }, /* AfeIfCreg4B1000Msbs */ + { 0x880c, 0x0926 }, /* AfeIfCreg4B100Msbs */ + { 0x880d, 0x0926 }, /* AfeIfCreg4B10Msbs */ + + { 0x880e, 0xb4d3 }, /* AfeIfCreg4B1000Lsbs */ + { 0x880f, 0xb4d3 }, /* AfeIfCreg4B100Lsbs */ + { 0x8810, 0xb4d3 }, /* AfeIfCreg4B10Lsbs */ + + { 0x8805, 0xb03e }, /* AfeIfCreg3B1000Msbs */ + { 0x8806, 0xb03e }, /* AfeIfCreg3B100Msbs */ + { 0x8807, 0xff00 }, /* AfeIfCreg3B10Msbs */ + + { 0x8808, 0xe090 }, /* AfeIfCreg3B1000Lsbs */ + { 0x8809, 0xe110 }, /* AfeIfCreg3B100Lsbs */ + { 0x880a, 0x0000 }, /* AfeIfCreg3B10Lsbs */ + + { 0x300d, 1 }, /* DisableNorm */ + + { 0x280c, 0x0180 }, /* LinkHoldEnd */ + + { 0x1c21, 0x0002 }, /* AlphaM */ + + { 0x3821, 6 }, /* FfeLkgTx0 */ + { 0x381d, 1 }, /* FfeLkg1g4 */ + { 0x381e, 1 }, /* FfeLkg1g5 */ + { 0x381f, 1 }, /* FfeLkg1g6 */ + { 0x3820, 1 }, /* FfeLkg1g7 */ + + { 0x8402, 0x01f0 }, /* Btinact */ + { 0x800e, 20 }, /* LftrainTime */ + { 0x800f, 24 }, /* DvguardTime */ + { 0x8010, 46 } /* IdlguardTime */ +}; + +static int +truephy_probe(device_t dev) +{ + struct mii_attach_args *ma = device_get_ivars(dev); + const struct mii_phydesc *mpd; + + mpd = mii_phy_match(ma, truephys); + if (mpd != NULL) { + device_set_desc(dev, mpd->mpd_name); + return 0; + } + return ENXIO; +} + +static int +truephy_attach(device_t dev) +{ + struct mii_softc *sc; + struct mii_attach_args *ma; + struct mii_data *mii; + + sc = device_get_softc(dev); + ma = device_get_ivars(dev); + + mii_softc_init(sc, ma); + sc->mii_dev = device_get_parent(dev); + mii = device_get_softc(sc->mii_dev); + LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); + + sc->mii_inst = mii->mii_instance; + sc->mii_service = truephy_service; + sc->mii_reset = truephy_reset; + sc->mii_pdata = mii; + + sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP; + + mii->mii_instance++; + + truephy_reset(sc); + + sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; + if (sc->mii_capabilities & BMSR_EXTSTAT) { + sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); + /* No 1000baseT half-duplex support */ + sc->mii_extcapabilities &= ~EXTSR_1000THDX; + } + + device_printf(dev, " "); + if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && + (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) + kprintf("no media present"); + else + mii_phy_add_media(sc); + kprintf("\n"); + + MIIBUS_MEDIAINIT(sc->mii_dev); + return 0; +} + +static int +truephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) +{ + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + int bmcr; + + switch (cmd) { + case MII_POLLSTAT: + /* + * If we're not polling our PHY instance, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return 0; + break; + + case MII_MEDIACHG: + /* + * If the media indicates a different PHY instance, + * isolate ourselves. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + bmcr = PHY_READ(sc, MII_BMCR); + PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); + return 0; + } + + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + break; + + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { + bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_AUTOEN; + PHY_WRITE(sc, MII_BMCR, bmcr); + PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_PDOWN); + } + + mii_phy_set_media(sc); + + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { + bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_PDOWN; + PHY_WRITE(sc, MII_BMCR, bmcr); + + if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { + PHY_WRITE(sc, MII_BMCR, + bmcr | BMCR_AUTOEN | BMCR_STARTNEG); + } + } + break; + + case MII_TICK: + /* + * If we're not currently selected, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return 0; + + if (mii_phy_tick(sc) == EJUSTRETURN) + return 0; + break; + } + + /* Update the media status. */ + truephy_status(sc); + + /* Callback if something changed. */ + mii_phy_update(sc, cmd); + return 0; +} + +static void +truephy_reset(struct mii_softc *sc) +{ + int i; + + for (i = 0; i < 2; ++i) { + PHY_READ(sc, MII_PHYIDR1); + PHY_READ(sc, MII_PHYIDR2); + + PHY_READ(sc, TRUEPHY_CTRL); + PHY_WRITE(sc, TRUEPHY_CTRL, 0x6); + + PHY_WRITE(sc, TRUEPHY_INDEX, 0x402); + PHY_READ(sc, TRUEPHY_DATA); + + PHY_WRITE(sc, TRUEPHY_CTRL, 0x2); + } + + PHY_READ(sc, MII_BMCR); + PHY_READ(sc, TRUEPHY_CTRL); + PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_PDOWN | BMCR_S1000); + PHY_WRITE(sc, TRUEPHY_CTRL, 0x7); + +#define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) + + for (i = 0; i < N(truephy_dspcode); ++i) { + const struct truephy_dsp *dsp = &truephy_dspcode[i]; + + PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); + PHY_WRITE(sc, TRUEPHY_DATA, dsp->data); + + PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); + PHY_READ(sc, TRUEPHY_DATA); + } + +#undef N + + PHY_READ(sc, MII_BMCR); + PHY_READ(sc, TRUEPHY_CTRL); + PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_S1000); + PHY_WRITE(sc, TRUEPHY_CTRL, 0x2); + + mii_phy_reset(sc); +} + +static void +truephy_status(struct mii_softc *sc) +{ + struct mii_data *mii = sc->mii_pdata; + int bmsr, bmcr, sr; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + sr = PHY_READ(sc, TRUEPHY_SR); + bmcr = PHY_READ(sc, MII_BMCR); + + bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); + if (bmsr & BMSR_LINK) + mii->mii_media_status |= IFM_ACTIVE; + + if (bmcr & BMCR_AUTOEN) { + if ((bmsr & BMSR_ACOMP) == 0) { + mii->mii_media_active |= IFM_NONE; + return; + } + } + + switch (sr & TRUEPHY_SR_SPD_MASK) { + case TRUEPHY_SR_SPD_1000T: + mii->mii_media_active |= IFM_1000_T; + break; + case TRUEPHY_SR_SPD_100TX: + mii->mii_media_active |= IFM_100_TX; + break; + case TRUEPHY_SR_SPD_10T: + mii->mii_media_active |= IFM_10_T; + break; + default: + mii->mii_media_active |= IFM_NONE; + break; + } + + if (sr & TRUEPHY_SR_FDX) + mii->mii_media_active |= IFM_FDX; +} diff --git a/sys/dev/netif/mii_layer/truephyreg.h b/sys/dev/netif/mii_layer/truephyreg.h new file mode 100644 index 0000000000..50e5250cb3 --- /dev/null +++ b/sys/dev/netif/mii_layer/truephyreg.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2007 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * 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. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + * $DragonFly: src/sys/dev/netif/mii_layer/truephyreg.h,v 1.1 2007/10/12 14:12:42 sephe Exp $ + */ + +#ifndef _MII_TRUEPHYREG_H +#define _MII_TRUEPHYREG_H + +#define TRUEPHY_INDEX 0x10 /* XXX reserved in DS */ +#define TRUEPHY_DATA 0x11 /* XXX reserved in DS */ + +#define TRUEPHY_CTRL 0x12 + +#define TRUEPHY_SR 0x1a +#define TRUEPHY_SR_SPD_MASK 0x0300 +#define TRUEPHY_SR_SPD_1000T 0x0200 +#define TRUEPHY_SR_SPD_100TX 0x0100 +#define TRUEPHY_SR_SPD_10T 0x0000 +#define TRUEPHY_SR_FDX 0x0080 + +#endif /* !_MII_TRUEPHYREG_H */ -- 2.41.0