1a9caca6aSWojciech A. Koszek /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 4df63a5d2SIan Lepore * Copyright (c) 2012-2014 Thomas Skibo <thomasskibo@yahoo.com> 5a9caca6aSWojciech A. Koszek * All rights reserved. 6a9caca6aSWojciech A. Koszek * 7a9caca6aSWojciech A. Koszek * Redistribution and use in source and binary forms, with or without 8a9caca6aSWojciech A. Koszek * modification, are permitted provided that the following conditions 9a9caca6aSWojciech A. Koszek * are met: 10a9caca6aSWojciech A. Koszek * 1. Redistributions of source code must retain the above copyright 11a9caca6aSWojciech A. Koszek * notice, this list of conditions and the following disclaimer. 12a9caca6aSWojciech A. Koszek * 2. Redistributions in binary form must reproduce the above copyright 13a9caca6aSWojciech A. Koszek * notice, this list of conditions and the following disclaimer in the 14a9caca6aSWojciech A. Koszek * documentation and/or other materials provided with the distribution. 15a9caca6aSWojciech A. Koszek * 16a9caca6aSWojciech A. Koszek * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17a9caca6aSWojciech A. Koszek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18a9caca6aSWojciech A. Koszek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19a9caca6aSWojciech A. Koszek * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 20a9caca6aSWojciech A. Koszek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21a9caca6aSWojciech A. Koszek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22a9caca6aSWojciech A. Koszek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23a9caca6aSWojciech A. Koszek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24a9caca6aSWojciech A. Koszek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25a9caca6aSWojciech A. Koszek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26a9caca6aSWojciech A. Koszek * SUCH DAMAGE. 27a9caca6aSWojciech A. Koszek */ 28a9caca6aSWojciech A. Koszek 2940713190SWojciech A. Koszek /* 3040713190SWojciech A. Koszek * A network interface driver for Cadence GEM Gigabit Ethernet 31a9caca6aSWojciech A. Koszek * interface such as the one used in Xilinx Zynq-7000 SoC. 32a9caca6aSWojciech A. Koszek * 33a9caca6aSWojciech A. Koszek * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. 34a9caca6aSWojciech A. Koszek * (v1.4) November 16, 2012. Xilinx doc UG585. GEM is covered in Ch. 16 35a9caca6aSWojciech A. Koszek * and register definitions are in appendix B.18. 36a9caca6aSWojciech A. Koszek */ 37a9caca6aSWojciech A. Koszek 38a9caca6aSWojciech A. Koszek #include <sys/param.h> 39a9caca6aSWojciech A. Koszek #include <sys/systm.h> 40a9caca6aSWojciech A. Koszek #include <sys/bus.h> 41a9caca6aSWojciech A. Koszek #include <sys/kernel.h> 42a9caca6aSWojciech A. Koszek #include <sys/malloc.h> 43a9caca6aSWojciech A. Koszek #include <sys/mbuf.h> 44a9caca6aSWojciech A. Koszek #include <sys/module.h> 45a9caca6aSWojciech A. Koszek #include <sys/rman.h> 46a9caca6aSWojciech A. Koszek #include <sys/socket.h> 47a9caca6aSWojciech A. Koszek #include <sys/sockio.h> 48a9caca6aSWojciech A. Koszek #include <sys/sysctl.h> 49a9caca6aSWojciech A. Koszek 50a9caca6aSWojciech A. Koszek #include <machine/bus.h> 51a9caca6aSWojciech A. Koszek 52a9caca6aSWojciech A. Koszek #include <net/ethernet.h> 53a9caca6aSWojciech A. Koszek #include <net/if.h> 54a9caca6aSWojciech A. Koszek #include <net/if_arp.h> 55a9caca6aSWojciech A. Koszek #include <net/if_dl.h> 56a9caca6aSWojciech A. Koszek #include <net/if_media.h> 57a9caca6aSWojciech A. Koszek #include <net/if_mib.h> 58a9caca6aSWojciech A. Koszek #include <net/if_types.h> 59a9caca6aSWojciech A. Koszek 60a9caca6aSWojciech A. Koszek #ifdef INET 61a9caca6aSWojciech A. Koszek #include <netinet/in.h> 62a9caca6aSWojciech A. Koszek #include <netinet/in_systm.h> 63a9caca6aSWojciech A. Koszek #include <netinet/in_var.h> 64a9caca6aSWojciech A. Koszek #include <netinet/ip.h> 65a9caca6aSWojciech A. Koszek #endif 66a9caca6aSWojciech A. Koszek 67a9caca6aSWojciech A. Koszek #include <net/bpf.h> 68a9caca6aSWojciech A. Koszek #include <net/bpfdesc.h> 69a9caca6aSWojciech A. Koszek 70a9caca6aSWojciech A. Koszek #include <dev/fdt/fdt_common.h> 71a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus.h> 72a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus_subr.h> 73a9caca6aSWojciech A. Koszek 74a9caca6aSWojciech A. Koszek #include <dev/mii/mii.h> 75a9caca6aSWojciech A. Koszek #include <dev/mii/miivar.h> 7634f4c17aSMilan Obuch #include <dev/mii/mii_fdt.h> 77a9caca6aSWojciech A. Koszek 78be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 79facdd1cdSThomas Skibo 807dcddacaSMitchell Horne #if BUS_SPACE_MAXADDR > BUS_SPACE_MAXADDR_32BIT 81facdd1cdSThomas Skibo #define CGEM64 82facdd1cdSThomas Skibo #endif 83facdd1cdSThomas Skibo 84a9caca6aSWojciech A. Koszek #include <dev/cadence/if_cgem_hw.h> 85a9caca6aSWojciech A. Koszek 86a9caca6aSWojciech A. Koszek #include "miibus_if.h" 87a9caca6aSWojciech A. Koszek 88a9caca6aSWojciech A. Koszek #define IF_CGEM_NAME "cgem" 89a9caca6aSWojciech A. Koszek 90df63a5d2SIan Lepore #define CGEM_NUM_RX_DESCS 512 /* size of receive descriptor ring */ 91df63a5d2SIan Lepore #define CGEM_NUM_TX_DESCS 512 /* size of transmit descriptor ring */ 92a9caca6aSWojciech A. Koszek 93a9caca6aSWojciech A. Koszek /* Default for sysctl rxbufs. Must be < CGEM_NUM_RX_DESCS of course. */ 94df63a5d2SIan Lepore #define DEFAULT_NUM_RX_BUFS 256 /* number of receive bufs to queue. */ 95a9caca6aSWojciech A. Koszek 96df63a5d2SIan Lepore #define TX_MAX_DMA_SEGS 8 /* maximum segs in a tx mbuf dma */ 97a9caca6aSWojciech A. Koszek 98a9caca6aSWojciech A. Koszek #define CGEM_CKSUM_ASSIST (CSUM_IP | CSUM_TCP | CSUM_UDP | \ 99a9caca6aSWojciech A. Koszek CSUM_TCP_IPV6 | CSUM_UDP_IPV6) 100a9caca6aSWojciech A. Koszek 10166cc0c61SMilan Obuch #define HWQUIRK_NONE 0 10266cc0c61SMilan Obuch #define HWQUIRK_NEEDNULLQS 1 10366cc0c61SMilan Obuch #define HWQUIRK_RXHANGWAR 2 104facdd1cdSThomas Skibo 105ef5a75b1SRuslan Bukin static struct ofw_compat_data compat_data[] = { 1064c52dde5SEmmanuel Vadot { "cdns,zynq-gem", HWQUIRK_RXHANGWAR }, /* Deprecated */ 1074c52dde5SEmmanuel Vadot { "cdns,zynqmp-gem", HWQUIRK_NEEDNULLQS }, /* Deprecated */ 1084c52dde5SEmmanuel Vadot { "xlnx,zynq-gem", HWQUIRK_RXHANGWAR }, 1094c52dde5SEmmanuel Vadot { "xlnx,zynqmp-gem", HWQUIRK_NEEDNULLQS }, 1104c52dde5SEmmanuel Vadot { "microchip,mpfs-mss-gem", HWQUIRK_NEEDNULLQS }, 1114c52dde5SEmmanuel Vadot { "sifive,fu540-c000-gem", HWQUIRK_NONE }, 1124c52dde5SEmmanuel Vadot { "sifive,fu740-c000-gem", HWQUIRK_NONE }, 113facdd1cdSThomas Skibo { NULL, 0 } 114ef5a75b1SRuslan Bukin }; 115ef5a75b1SRuslan Bukin 116a9caca6aSWojciech A. Koszek struct cgem_softc { 117023911aaSIan Lepore if_t ifp; 118a9caca6aSWojciech A. Koszek struct mtx sc_mtx; 119a9caca6aSWojciech A. Koszek device_t dev; 120a9caca6aSWojciech A. Koszek device_t miibus; 121df63a5d2SIan Lepore u_int mii_media_active; /* last active media */ 122a9caca6aSWojciech A. Koszek int if_old_flags; 123a9caca6aSWojciech A. Koszek struct resource *mem_res; 124a9caca6aSWojciech A. Koszek struct resource *irq_res; 125a9caca6aSWojciech A. Koszek void *intrhand; 126a9caca6aSWojciech A. Koszek struct callout tick_ch; 127a9caca6aSWojciech A. Koszek uint32_t net_ctl_shadow; 128facdd1cdSThomas Skibo uint32_t net_cfg_shadow; 1294c52dde5SEmmanuel Vadot clk_t clk_pclk; 1304c52dde5SEmmanuel Vadot clk_t clk_hclk; 1314c52dde5SEmmanuel Vadot clk_t clk_txclk; 1324c52dde5SEmmanuel Vadot clk_t clk_rxclk; 1334c52dde5SEmmanuel Vadot clk_t clk_tsuclk; 134facdd1cdSThomas Skibo int neednullqs; 13534f4c17aSMilan Obuch int phy_contype; 136a9caca6aSWojciech A. Koszek 137a9caca6aSWojciech A. Koszek bus_dma_tag_t desc_dma_tag; 138a9caca6aSWojciech A. Koszek bus_dma_tag_t mbuf_dma_tag; 139a9caca6aSWojciech A. Koszek 140a9caca6aSWojciech A. Koszek /* receive descriptor ring */ 141a9caca6aSWojciech A. Koszek struct cgem_rx_desc *rxring; 142a9caca6aSWojciech A. Koszek bus_addr_t rxring_physaddr; 143a9caca6aSWojciech A. Koszek struct mbuf *rxring_m[CGEM_NUM_RX_DESCS]; 144a9caca6aSWojciech A. Koszek bus_dmamap_t rxring_m_dmamap[CGEM_NUM_RX_DESCS]; 145a9caca6aSWojciech A. Koszek int rxring_hd_ptr; /* where to put rcv bufs */ 146a9caca6aSWojciech A. Koszek int rxring_tl_ptr; /* where to get receives */ 147a9caca6aSWojciech A. Koszek int rxring_queued; /* how many rcv bufs queued */ 148a9caca6aSWojciech A. Koszek bus_dmamap_t rxring_dma_map; 149a9caca6aSWojciech A. Koszek int rxbufs; /* tunable number rcv bufs */ 150df63a5d2SIan Lepore int rxhangwar; /* rx hang work-around */ 151df63a5d2SIan Lepore u_int rxoverruns; /* rx overruns */ 152df63a5d2SIan Lepore u_int rxnobufs; /* rx buf ring empty events */ 153df63a5d2SIan Lepore u_int rxdmamapfails; /* rx dmamap failures */ 154df63a5d2SIan Lepore uint32_t rx_frames_prev; 155a9caca6aSWojciech A. Koszek 156a9caca6aSWojciech A. Koszek /* transmit descriptor ring */ 157a9caca6aSWojciech A. Koszek struct cgem_tx_desc *txring; 158a9caca6aSWojciech A. Koszek bus_addr_t txring_physaddr; 159a9caca6aSWojciech A. Koszek struct mbuf *txring_m[CGEM_NUM_TX_DESCS]; 160a9caca6aSWojciech A. Koszek bus_dmamap_t txring_m_dmamap[CGEM_NUM_TX_DESCS]; 161a9caca6aSWojciech A. Koszek int txring_hd_ptr; /* where to put next xmits */ 162a9caca6aSWojciech A. Koszek int txring_tl_ptr; /* next xmit mbuf to free */ 163a9caca6aSWojciech A. Koszek int txring_queued; /* num xmits segs queued */ 164df63a5d2SIan Lepore u_int txfull; /* tx ring full events */ 165df63a5d2SIan Lepore u_int txdefrags; /* tx calls to m_defrag() */ 166df63a5d2SIan Lepore u_int txdefragfails; /* tx m_defrag() failures */ 167df63a5d2SIan Lepore u_int txdmamapfails; /* tx dmamap failures */ 168df63a5d2SIan Lepore 169facdd1cdSThomas Skibo /* null descriptor rings */ 170facdd1cdSThomas Skibo void *null_qs; 171facdd1cdSThomas Skibo bus_addr_t null_qs_physaddr; 172facdd1cdSThomas Skibo 173df63a5d2SIan Lepore /* hardware provided statistics */ 174df63a5d2SIan Lepore struct cgem_hw_stats { 175df63a5d2SIan Lepore uint64_t tx_bytes; 176df63a5d2SIan Lepore uint32_t tx_frames; 177df63a5d2SIan Lepore uint32_t tx_frames_bcast; 178df63a5d2SIan Lepore uint32_t tx_frames_multi; 179df63a5d2SIan Lepore uint32_t tx_frames_pause; 180df63a5d2SIan Lepore uint32_t tx_frames_64b; 181df63a5d2SIan Lepore uint32_t tx_frames_65to127b; 182df63a5d2SIan Lepore uint32_t tx_frames_128to255b; 183df63a5d2SIan Lepore uint32_t tx_frames_256to511b; 184df63a5d2SIan Lepore uint32_t tx_frames_512to1023b; 185df63a5d2SIan Lepore uint32_t tx_frames_1024to1536b; 186df63a5d2SIan Lepore uint32_t tx_under_runs; 187df63a5d2SIan Lepore uint32_t tx_single_collisn; 188df63a5d2SIan Lepore uint32_t tx_multi_collisn; 189df63a5d2SIan Lepore uint32_t tx_excsv_collisn; 190df63a5d2SIan Lepore uint32_t tx_late_collisn; 191df63a5d2SIan Lepore uint32_t tx_deferred_frames; 192df63a5d2SIan Lepore uint32_t tx_carrier_sense_errs; 193df63a5d2SIan Lepore 194df63a5d2SIan Lepore uint64_t rx_bytes; 195df63a5d2SIan Lepore uint32_t rx_frames; 196df63a5d2SIan Lepore uint32_t rx_frames_bcast; 197df63a5d2SIan Lepore uint32_t rx_frames_multi; 198df63a5d2SIan Lepore uint32_t rx_frames_pause; 199df63a5d2SIan Lepore uint32_t rx_frames_64b; 200df63a5d2SIan Lepore uint32_t rx_frames_65to127b; 201df63a5d2SIan Lepore uint32_t rx_frames_128to255b; 202df63a5d2SIan Lepore uint32_t rx_frames_256to511b; 203df63a5d2SIan Lepore uint32_t rx_frames_512to1023b; 204df63a5d2SIan Lepore uint32_t rx_frames_1024to1536b; 205df63a5d2SIan Lepore uint32_t rx_frames_undersize; 206df63a5d2SIan Lepore uint32_t rx_frames_oversize; 207df63a5d2SIan Lepore uint32_t rx_frames_jabber; 208df63a5d2SIan Lepore uint32_t rx_frames_fcs_errs; 209df63a5d2SIan Lepore uint32_t rx_frames_length_errs; 210df63a5d2SIan Lepore uint32_t rx_symbol_errs; 211df63a5d2SIan Lepore uint32_t rx_align_errs; 212df63a5d2SIan Lepore uint32_t rx_resource_errs; 213df63a5d2SIan Lepore uint32_t rx_overrun_errs; 214df63a5d2SIan Lepore uint32_t rx_ip_hdr_csum_errs; 215df63a5d2SIan Lepore uint32_t rx_tcp_csum_errs; 216df63a5d2SIan Lepore uint32_t rx_udp_csum_errs; 217df63a5d2SIan Lepore } stats; 218a9caca6aSWojciech A. Koszek }; 219a9caca6aSWojciech A. Koszek 220a9caca6aSWojciech A. Koszek #define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) 221a9caca6aSWojciech A. Koszek #define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val))) 222a9caca6aSWojciech A. Koszek #define BARRIER(sc, off, len, flags) \ 223a9caca6aSWojciech A. Koszek (bus_barrier((sc)->mem_res, (off), (len), (flags)) 224a9caca6aSWojciech A. Koszek 225a9caca6aSWojciech A. Koszek #define CGEM_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 226a9caca6aSWojciech A. Koszek #define CGEM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 227f674004fSIan Lepore #define CGEM_LOCK_INIT(sc) mtx_init(&(sc)->sc_mtx, \ 228f674004fSIan Lepore device_get_nameunit((sc)->dev), MTX_NETWORK_LOCK, MTX_DEF) 229a9caca6aSWojciech A. Koszek #define CGEM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) 230a9caca6aSWojciech A. Koszek #define CGEM_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) 231a9caca6aSWojciech A. Koszek 2320f822edeSIan Lepore /* Allow platforms to optionally provide a way to set the reference clock. */ 2330f822edeSIan Lepore int cgem_set_ref_clk(int unit, int frequency); 2340f822edeSIan Lepore 235a9caca6aSWojciech A. Koszek static int cgem_probe(device_t dev); 236a9caca6aSWojciech A. Koszek static int cgem_attach(device_t dev); 237a9caca6aSWojciech A. Koszek static int cgem_detach(device_t dev); 238a9caca6aSWojciech A. Koszek static void cgem_tick(void *); 239a9caca6aSWojciech A. Koszek static void cgem_intr(void *); 240a9caca6aSWojciech A. Koszek 241df63a5d2SIan Lepore static void cgem_mediachange(struct cgem_softc *, struct mii_data *); 242df63a5d2SIan Lepore 243a9caca6aSWojciech A. Koszek static void 244a9caca6aSWojciech A. Koszek cgem_get_mac(struct cgem_softc *sc, u_char eaddr[]) 245a9caca6aSWojciech A. Koszek { 246a9caca6aSWojciech A. Koszek int i; 247a9caca6aSWojciech A. Koszek uint32_t rnd; 248a9caca6aSWojciech A. Koszek 249a9caca6aSWojciech A. Koszek /* See if boot loader gave us a MAC address already. */ 250a9caca6aSWojciech A. Koszek for (i = 0; i < 4; i++) { 251a9caca6aSWojciech A. Koszek uint32_t low = RD4(sc, CGEM_SPEC_ADDR_LOW(i)); 252a9caca6aSWojciech A. Koszek uint32_t high = RD4(sc, CGEM_SPEC_ADDR_HI(i)) & 0xffff; 253a9caca6aSWojciech A. Koszek if (low != 0 || high != 0) { 254a9caca6aSWojciech A. Koszek eaddr[0] = low & 0xff; 255a9caca6aSWojciech A. Koszek eaddr[1] = (low >> 8) & 0xff; 256a9caca6aSWojciech A. Koszek eaddr[2] = (low >> 16) & 0xff; 257a9caca6aSWojciech A. Koszek eaddr[3] = (low >> 24) & 0xff; 258a9caca6aSWojciech A. Koszek eaddr[4] = high & 0xff; 259a9caca6aSWojciech A. Koszek eaddr[5] = (high >> 8) & 0xff; 260a9caca6aSWojciech A. Koszek break; 261a9caca6aSWojciech A. Koszek } 262a9caca6aSWojciech A. Koszek } 263a9caca6aSWojciech A. Koszek 264a9caca6aSWojciech A. Koszek /* No MAC from boot loader? Assign a random one. */ 265a9caca6aSWojciech A. Koszek if (i == 4) { 266a9caca6aSWojciech A. Koszek rnd = arc4random(); 267a9caca6aSWojciech A. Koszek 268a9caca6aSWojciech A. Koszek eaddr[0] = 'b'; 269a9caca6aSWojciech A. Koszek eaddr[1] = 's'; 270a9caca6aSWojciech A. Koszek eaddr[2] = 'd'; 271a9caca6aSWojciech A. Koszek eaddr[3] = (rnd >> 16) & 0xff; 272a9caca6aSWojciech A. Koszek eaddr[4] = (rnd >> 8) & 0xff; 273a9caca6aSWojciech A. Koszek eaddr[5] = rnd & 0xff; 274a9caca6aSWojciech A. Koszek 275a9caca6aSWojciech A. Koszek device_printf(sc->dev, "no mac address found, assigning " 276f674004fSIan Lepore "random: %02x:%02x:%02x:%02x:%02x:%02x\n", eaddr[0], 277f674004fSIan Lepore eaddr[1], eaddr[2], eaddr[3], eaddr[4], eaddr[5]); 278df63a5d2SIan Lepore } 279a9caca6aSWojciech A. Koszek 280df63a5d2SIan Lepore /* Move address to first slot and zero out the rest. */ 281a9caca6aSWojciech A. Koszek WR4(sc, CGEM_SPEC_ADDR_LOW(0), (eaddr[3] << 24) | 282a9caca6aSWojciech A. Koszek (eaddr[2] << 16) | (eaddr[1] << 8) | eaddr[0]); 283a9caca6aSWojciech A. Koszek WR4(sc, CGEM_SPEC_ADDR_HI(0), (eaddr[5] << 8) | eaddr[4]); 284df63a5d2SIan Lepore 285df63a5d2SIan Lepore for (i = 1; i < 4; i++) { 286df63a5d2SIan Lepore WR4(sc, CGEM_SPEC_ADDR_LOW(i), 0); 287df63a5d2SIan Lepore WR4(sc, CGEM_SPEC_ADDR_HI(i), 0); 288a9caca6aSWojciech A. Koszek } 289a9caca6aSWojciech A. Koszek } 290a9caca6aSWojciech A. Koszek 291f674004fSIan Lepore /* 292f674004fSIan Lepore * cgem_mac_hash(): map 48-bit address to a 6-bit hash. The 6-bit hash 293facdd1cdSThomas Skibo * corresponds to a bit in a 64-bit hash register. Setting that bit in the 294facdd1cdSThomas Skibo * hash register enables reception of all frames with a destination address 295facdd1cdSThomas Skibo * that hashes to that 6-bit value. 296a9caca6aSWojciech A. Koszek * 297a9caca6aSWojciech A. Koszek * The hash function is described in sec. 16.2.3 in the Zynq-7000 Tech 298a9caca6aSWojciech A. Koszek * Reference Manual. Bits 0-5 in the hash are the exclusive-or of 299a9caca6aSWojciech A. Koszek * every sixth bit in the destination address. 300a9caca6aSWojciech A. Koszek */ 301a9caca6aSWojciech A. Koszek static int 302a9caca6aSWojciech A. Koszek cgem_mac_hash(u_char eaddr[]) 303a9caca6aSWojciech A. Koszek { 304a9caca6aSWojciech A. Koszek int hash; 305a9caca6aSWojciech A. Koszek int i, j; 306a9caca6aSWojciech A. Koszek 307a9caca6aSWojciech A. Koszek hash = 0; 308a9caca6aSWojciech A. Koszek for (i = 0; i < 6; i++) 309a9caca6aSWojciech A. Koszek for (j = i; j < 48; j += 6) 310a9caca6aSWojciech A. Koszek if ((eaddr[j >> 3] & (1 << (j & 7))) != 0) 311a9caca6aSWojciech A. Koszek hash ^= (1 << i); 312a9caca6aSWojciech A. Koszek 313a9caca6aSWojciech A. Koszek return hash; 314a9caca6aSWojciech A. Koszek } 315a9caca6aSWojciech A. Koszek 316bdf4b0aeSGleb Smirnoff static u_int 317bdf4b0aeSGleb Smirnoff cgem_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 318bdf4b0aeSGleb Smirnoff { 319bdf4b0aeSGleb Smirnoff uint32_t *hashes = arg; 320bdf4b0aeSGleb Smirnoff int index; 321bdf4b0aeSGleb Smirnoff 322bdf4b0aeSGleb Smirnoff index = cgem_mac_hash(LLADDR(sdl)); 323bdf4b0aeSGleb Smirnoff if (index > 31) 324f674004fSIan Lepore hashes[0] |= (1U << (index - 32)); 325bdf4b0aeSGleb Smirnoff else 326f674004fSIan Lepore hashes[1] |= (1U << index); 327bdf4b0aeSGleb Smirnoff 328bdf4b0aeSGleb Smirnoff return (1); 329bdf4b0aeSGleb Smirnoff } 330bdf4b0aeSGleb Smirnoff 331f674004fSIan Lepore /* 332f674004fSIan Lepore * After any change in rx flags or multi-cast addresses, set up hash registers 333f674004fSIan Lepore * and net config register bits. 334a9caca6aSWojciech A. Koszek */ 335a9caca6aSWojciech A. Koszek static void 336a9caca6aSWojciech A. Koszek cgem_rx_filter(struct cgem_softc *sc) 337a9caca6aSWojciech A. Koszek { 338023911aaSIan Lepore if_t ifp = sc->ifp; 339bdf4b0aeSGleb Smirnoff uint32_t hashes[2] = { 0, 0 }; 340a9caca6aSWojciech A. Koszek 341facdd1cdSThomas Skibo sc->net_cfg_shadow &= ~(CGEM_NET_CFG_MULTI_HASH_EN | 342f674004fSIan Lepore CGEM_NET_CFG_NO_BCAST | CGEM_NET_CFG_COPY_ALL); 343a9caca6aSWojciech A. Koszek 344023911aaSIan Lepore if ((if_getflags(ifp) & IFF_PROMISC) != 0) 345facdd1cdSThomas Skibo sc->net_cfg_shadow |= CGEM_NET_CFG_COPY_ALL; 346a9caca6aSWojciech A. Koszek else { 347023911aaSIan Lepore if ((if_getflags(ifp) & IFF_BROADCAST) == 0) 348facdd1cdSThomas Skibo sc->net_cfg_shadow |= CGEM_NET_CFG_NO_BCAST; 349023911aaSIan Lepore if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) { 350bdf4b0aeSGleb Smirnoff hashes[0] = 0xffffffff; 351bdf4b0aeSGleb Smirnoff hashes[1] = 0xffffffff; 352bdf4b0aeSGleb Smirnoff } else 353bdf4b0aeSGleb Smirnoff if_foreach_llmaddr(ifp, cgem_hash_maddr, hashes); 354a9caca6aSWojciech A. Koszek 355bdf4b0aeSGleb Smirnoff if (hashes[0] != 0 || hashes[1] != 0) 356facdd1cdSThomas Skibo sc->net_cfg_shadow |= CGEM_NET_CFG_MULTI_HASH_EN; 357a9caca6aSWojciech A. Koszek } 358a9caca6aSWojciech A. Koszek 359bdf4b0aeSGleb Smirnoff WR4(sc, CGEM_HASH_TOP, hashes[0]); 360bdf4b0aeSGleb Smirnoff WR4(sc, CGEM_HASH_BOT, hashes[1]); 361facdd1cdSThomas Skibo WR4(sc, CGEM_NET_CFG, sc->net_cfg_shadow); 362a9caca6aSWojciech A. Koszek } 363a9caca6aSWojciech A. Koszek 364a9caca6aSWojciech A. Koszek /* For bus_dmamap_load() callback. */ 365a9caca6aSWojciech A. Koszek static void 366a9caca6aSWojciech A. Koszek cgem_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 367a9caca6aSWojciech A. Koszek { 368a9caca6aSWojciech A. Koszek 369a9caca6aSWojciech A. Koszek if (nsegs != 1 || error != 0) 370a9caca6aSWojciech A. Koszek return; 371a9caca6aSWojciech A. Koszek *(bus_addr_t *)arg = segs[0].ds_addr; 372a9caca6aSWojciech A. Koszek } 373a9caca6aSWojciech A. Koszek 374facdd1cdSThomas Skibo /* Set up null queues for priority queues we actually can't disable. */ 375facdd1cdSThomas Skibo static void 376facdd1cdSThomas Skibo cgem_null_qs(struct cgem_softc *sc) 377facdd1cdSThomas Skibo { 378facdd1cdSThomas Skibo struct cgem_rx_desc *rx_desc; 379facdd1cdSThomas Skibo struct cgem_tx_desc *tx_desc; 380facdd1cdSThomas Skibo uint32_t queue_mask; 381facdd1cdSThomas Skibo int n; 382facdd1cdSThomas Skibo 383facdd1cdSThomas Skibo /* Read design config register 6 to determine number of queues. */ 384facdd1cdSThomas Skibo queue_mask = (RD4(sc, CGEM_DESIGN_CFG6) & 385facdd1cdSThomas Skibo CGEM_DESIGN_CFG6_DMA_PRIO_Q_MASK) >> 1; 386facdd1cdSThomas Skibo if (queue_mask == 0) 387facdd1cdSThomas Skibo return; 388facdd1cdSThomas Skibo 389facdd1cdSThomas Skibo /* Create empty RX queue and empty TX buf queues. */ 390facdd1cdSThomas Skibo memset(sc->null_qs, 0, sizeof(struct cgem_rx_desc) + 391facdd1cdSThomas Skibo sizeof(struct cgem_tx_desc)); 392facdd1cdSThomas Skibo rx_desc = sc->null_qs; 393facdd1cdSThomas Skibo rx_desc->addr = CGEM_RXDESC_OWN | CGEM_RXDESC_WRAP; 394facdd1cdSThomas Skibo tx_desc = (struct cgem_tx_desc *)(rx_desc + 1); 395facdd1cdSThomas Skibo tx_desc->ctl = CGEM_TXDESC_USED | CGEM_TXDESC_WRAP; 396facdd1cdSThomas Skibo 397facdd1cdSThomas Skibo /* Point all valid ring base pointers to the null queues. */ 398facdd1cdSThomas Skibo for (n = 1; (queue_mask & 1) != 0; n++, queue_mask >>= 1) { 399facdd1cdSThomas Skibo WR4(sc, CGEM_RX_QN_BAR(n), sc->null_qs_physaddr); 400facdd1cdSThomas Skibo WR4(sc, CGEM_TX_QN_BAR(n), sc->null_qs_physaddr + 401facdd1cdSThomas Skibo sizeof(struct cgem_rx_desc)); 402facdd1cdSThomas Skibo } 403facdd1cdSThomas Skibo } 404facdd1cdSThomas Skibo 405a9caca6aSWojciech A. Koszek /* Create DMA'able descriptor rings. */ 406a9caca6aSWojciech A. Koszek static int 407a9caca6aSWojciech A. Koszek cgem_setup_descs(struct cgem_softc *sc) 408a9caca6aSWojciech A. Koszek { 409a9caca6aSWojciech A. Koszek int i, err; 410facdd1cdSThomas Skibo int desc_rings_size = CGEM_NUM_RX_DESCS * sizeof(struct cgem_rx_desc) + 411facdd1cdSThomas Skibo CGEM_NUM_TX_DESCS * sizeof(struct cgem_tx_desc); 412facdd1cdSThomas Skibo 413facdd1cdSThomas Skibo if (sc->neednullqs) 414facdd1cdSThomas Skibo desc_rings_size += sizeof(struct cgem_rx_desc) + 415facdd1cdSThomas Skibo sizeof(struct cgem_tx_desc); 416a9caca6aSWojciech A. Koszek 417a9caca6aSWojciech A. Koszek sc->txring = NULL; 418a9caca6aSWojciech A. Koszek sc->rxring = NULL; 419a9caca6aSWojciech A. Koszek 420f674004fSIan Lepore /* Allocate non-cached DMA space for RX and TX descriptors. */ 421facdd1cdSThomas Skibo err = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 1, 422facdd1cdSThomas Skibo #ifdef CGEM64 423facdd1cdSThomas Skibo 1ULL << 32, /* Do not cross a 4G boundary. */ 424facdd1cdSThomas Skibo #else 425facdd1cdSThomas Skibo 0, 426facdd1cdSThomas Skibo #endif 427facdd1cdSThomas Skibo BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, 428facdd1cdSThomas Skibo desc_rings_size, 1, desc_rings_size, 0, 429f674004fSIan Lepore busdma_lock_mutex, &sc->sc_mtx, &sc->desc_dma_tag); 430a9caca6aSWojciech A. Koszek if (err) 431a9caca6aSWojciech A. Koszek return (err); 432a9caca6aSWojciech A. Koszek 433a9caca6aSWojciech A. Koszek /* Set up a bus_dma_tag for mbufs. */ 434a9caca6aSWojciech A. Koszek err = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 1, 0, 435facdd1cdSThomas Skibo BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 436facdd1cdSThomas Skibo TX_MAX_DMA_SEGS, MCLBYTES, 0, busdma_lock_mutex, &sc->sc_mtx, 437facdd1cdSThomas Skibo &sc->mbuf_dma_tag); 438a9caca6aSWojciech A. Koszek if (err) 439a9caca6aSWojciech A. Koszek return (err); 440a9caca6aSWojciech A. Koszek 441facdd1cdSThomas Skibo /* 44204d2d2d7SMitchell Horne * Allocate DMA memory. We allocate transmit, receive and null 44304d2d2d7SMitchell Horne * descriptor queues all at once because the hardware only provides 44404d2d2d7SMitchell Horne * one register for the upper 32 bits of rx and tx descriptor queues 44504d2d2d7SMitchell Horne * hardware addresses. 446facdd1cdSThomas Skibo */ 447f674004fSIan Lepore err = bus_dmamem_alloc(sc->desc_dma_tag, (void **)&sc->rxring, 448facdd1cdSThomas Skibo BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_ZERO, 449facdd1cdSThomas Skibo &sc->rxring_dma_map); 450a9caca6aSWojciech A. Koszek if (err) 451a9caca6aSWojciech A. Koszek return (err); 452a9caca6aSWojciech A. Koszek 453a9caca6aSWojciech A. Koszek /* Load descriptor DMA memory. */ 454a9caca6aSWojciech A. Koszek err = bus_dmamap_load(sc->desc_dma_tag, sc->rxring_dma_map, 455facdd1cdSThomas Skibo (void *)sc->rxring, desc_rings_size, 456f674004fSIan Lepore cgem_getaddr, &sc->rxring_physaddr, BUS_DMA_NOWAIT); 457a9caca6aSWojciech A. Koszek if (err) 458a9caca6aSWojciech A. Koszek return (err); 459a9caca6aSWojciech A. Koszek 460a9caca6aSWojciech A. Koszek /* Initialize RX descriptors. */ 461a9caca6aSWojciech A. Koszek for (i = 0; i < CGEM_NUM_RX_DESCS; i++) { 462a9caca6aSWojciech A. Koszek sc->rxring[i].addr = CGEM_RXDESC_OWN; 463a9caca6aSWojciech A. Koszek sc->rxring[i].ctl = 0; 464a9caca6aSWojciech A. Koszek sc->rxring_m[i] = NULL; 465023911aaSIan Lepore sc->rxring_m_dmamap[i] = NULL; 466a9caca6aSWojciech A. Koszek } 467a9caca6aSWojciech A. Koszek sc->rxring[CGEM_NUM_RX_DESCS - 1].addr |= CGEM_RXDESC_WRAP; 468a9caca6aSWojciech A. Koszek 469a9caca6aSWojciech A. Koszek sc->rxring_hd_ptr = 0; 470a9caca6aSWojciech A. Koszek sc->rxring_tl_ptr = 0; 471a9caca6aSWojciech A. Koszek sc->rxring_queued = 0; 472a9caca6aSWojciech A. Koszek 473facdd1cdSThomas Skibo sc->txring = (struct cgem_tx_desc *)(sc->rxring + CGEM_NUM_RX_DESCS); 474facdd1cdSThomas Skibo sc->txring_physaddr = sc->rxring_physaddr + CGEM_NUM_RX_DESCS * 475facdd1cdSThomas Skibo sizeof(struct cgem_rx_desc); 476a9caca6aSWojciech A. Koszek 477a9caca6aSWojciech A. Koszek /* Initialize TX descriptor ring. */ 478a9caca6aSWojciech A. Koszek for (i = 0; i < CGEM_NUM_TX_DESCS; i++) { 479a9caca6aSWojciech A. Koszek sc->txring[i].addr = 0; 480a9caca6aSWojciech A. Koszek sc->txring[i].ctl = CGEM_TXDESC_USED; 481a9caca6aSWojciech A. Koszek sc->txring_m[i] = NULL; 482023911aaSIan Lepore sc->txring_m_dmamap[i] = NULL; 483a9caca6aSWojciech A. Koszek } 484a9caca6aSWojciech A. Koszek sc->txring[CGEM_NUM_TX_DESCS - 1].ctl |= CGEM_TXDESC_WRAP; 485a9caca6aSWojciech A. Koszek 486a9caca6aSWojciech A. Koszek sc->txring_hd_ptr = 0; 487a9caca6aSWojciech A. Koszek sc->txring_tl_ptr = 0; 488a9caca6aSWojciech A. Koszek sc->txring_queued = 0; 489a9caca6aSWojciech A. Koszek 490facdd1cdSThomas Skibo if (sc->neednullqs) { 491facdd1cdSThomas Skibo sc->null_qs = (void *)(sc->txring + CGEM_NUM_TX_DESCS); 492facdd1cdSThomas Skibo sc->null_qs_physaddr = sc->txring_physaddr + 493facdd1cdSThomas Skibo CGEM_NUM_TX_DESCS * sizeof(struct cgem_tx_desc); 494facdd1cdSThomas Skibo 495facdd1cdSThomas Skibo cgem_null_qs(sc); 496facdd1cdSThomas Skibo } 497facdd1cdSThomas Skibo 498a9caca6aSWojciech A. Koszek return (0); 499a9caca6aSWojciech A. Koszek } 500a9caca6aSWojciech A. Koszek 501a9caca6aSWojciech A. Koszek /* Fill receive descriptor ring with mbufs. */ 502a9caca6aSWojciech A. Koszek static void 503a9caca6aSWojciech A. Koszek cgem_fill_rqueue(struct cgem_softc *sc) 504a9caca6aSWojciech A. Koszek { 505a9caca6aSWojciech A. Koszek struct mbuf *m = NULL; 506a9caca6aSWojciech A. Koszek bus_dma_segment_t segs[TX_MAX_DMA_SEGS]; 507a9caca6aSWojciech A. Koszek int nsegs; 508a9caca6aSWojciech A. Koszek 509a9caca6aSWojciech A. Koszek CGEM_ASSERT_LOCKED(sc); 510a9caca6aSWojciech A. Koszek 511a9caca6aSWojciech A. Koszek while (sc->rxring_queued < sc->rxbufs) { 512a9caca6aSWojciech A. Koszek /* Get a cluster mbuf. */ 513a9caca6aSWojciech A. Koszek m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 514a9caca6aSWojciech A. Koszek if (m == NULL) 515a9caca6aSWojciech A. Koszek break; 516a9caca6aSWojciech A. Koszek 517a9caca6aSWojciech A. Koszek m->m_len = MCLBYTES; 518a9caca6aSWojciech A. Koszek m->m_pkthdr.len = MCLBYTES; 519a9caca6aSWojciech A. Koszek m->m_pkthdr.rcvif = sc->ifp; 520a9caca6aSWojciech A. Koszek 521a9caca6aSWojciech A. Koszek /* Load map and plug in physical address. */ 522023911aaSIan Lepore if (bus_dmamap_create(sc->mbuf_dma_tag, 0, 523023911aaSIan Lepore &sc->rxring_m_dmamap[sc->rxring_hd_ptr])) { 524023911aaSIan Lepore sc->rxdmamapfails++; 525023911aaSIan Lepore m_free(m); 526023911aaSIan Lepore break; 527023911aaSIan Lepore } 528a9caca6aSWojciech A. Koszek if (bus_dmamap_load_mbuf_sg(sc->mbuf_dma_tag, 529a9caca6aSWojciech A. Koszek sc->rxring_m_dmamap[sc->rxring_hd_ptr], m, 530a9caca6aSWojciech A. Koszek segs, &nsegs, BUS_DMA_NOWAIT)) { 531df63a5d2SIan Lepore sc->rxdmamapfails++; 532023911aaSIan Lepore bus_dmamap_destroy(sc->mbuf_dma_tag, 533023911aaSIan Lepore sc->rxring_m_dmamap[sc->rxring_hd_ptr]); 534023911aaSIan Lepore sc->rxring_m_dmamap[sc->rxring_hd_ptr] = NULL; 535a9caca6aSWojciech A. Koszek m_free(m); 536a9caca6aSWojciech A. Koszek break; 537a9caca6aSWojciech A. Koszek } 538a9caca6aSWojciech A. Koszek sc->rxring_m[sc->rxring_hd_ptr] = m; 539a9caca6aSWojciech A. Koszek 540a9caca6aSWojciech A. Koszek /* Sync cache with receive buffer. */ 541a9caca6aSWojciech A. Koszek bus_dmamap_sync(sc->mbuf_dma_tag, 542a9caca6aSWojciech A. Koszek sc->rxring_m_dmamap[sc->rxring_hd_ptr], 543a9caca6aSWojciech A. Koszek BUS_DMASYNC_PREREAD); 544a9caca6aSWojciech A. Koszek 545a9caca6aSWojciech A. Koszek /* Write rx descriptor and increment head pointer. */ 546a9caca6aSWojciech A. Koszek sc->rxring[sc->rxring_hd_ptr].ctl = 0; 547facdd1cdSThomas Skibo #ifdef CGEM64 548facdd1cdSThomas Skibo sc->rxring[sc->rxring_hd_ptr].addrhi = segs[0].ds_addr >> 32; 549facdd1cdSThomas Skibo #endif 550a9caca6aSWojciech A. Koszek if (sc->rxring_hd_ptr == CGEM_NUM_RX_DESCS - 1) { 551a9caca6aSWojciech A. Koszek sc->rxring[sc->rxring_hd_ptr].addr = segs[0].ds_addr | 552a9caca6aSWojciech A. Koszek CGEM_RXDESC_WRAP; 553a9caca6aSWojciech A. Koszek sc->rxring_hd_ptr = 0; 554a9caca6aSWojciech A. Koszek } else 555a9caca6aSWojciech A. Koszek sc->rxring[sc->rxring_hd_ptr++].addr = segs[0].ds_addr; 556a9caca6aSWojciech A. Koszek 557a9caca6aSWojciech A. Koszek sc->rxring_queued++; 558a9caca6aSWojciech A. Koszek } 559a9caca6aSWojciech A. Koszek } 560a9caca6aSWojciech A. Koszek 561a9caca6aSWojciech A. Koszek /* Pull received packets off of receive descriptor ring. */ 562a9caca6aSWojciech A. Koszek static void 563a9caca6aSWojciech A. Koszek cgem_recv(struct cgem_softc *sc) 564a9caca6aSWojciech A. Koszek { 565023911aaSIan Lepore if_t ifp = sc->ifp; 566df63a5d2SIan Lepore struct mbuf *m, *m_hd, **m_tl; 567a9caca6aSWojciech A. Koszek uint32_t ctl; 568a9caca6aSWojciech A. Koszek 569a9caca6aSWojciech A. Koszek CGEM_ASSERT_LOCKED(sc); 570a9caca6aSWojciech A. Koszek 571a9caca6aSWojciech A. Koszek /* Pick up all packets in which the OWN bit is set. */ 572df63a5d2SIan Lepore m_hd = NULL; 573df63a5d2SIan Lepore m_tl = &m_hd; 574a9caca6aSWojciech A. Koszek while (sc->rxring_queued > 0 && 575a9caca6aSWojciech A. Koszek (sc->rxring[sc->rxring_tl_ptr].addr & CGEM_RXDESC_OWN) != 0) { 576a9caca6aSWojciech A. Koszek ctl = sc->rxring[sc->rxring_tl_ptr].ctl; 577a9caca6aSWojciech A. Koszek 578a9caca6aSWojciech A. Koszek /* Grab filled mbuf. */ 579a9caca6aSWojciech A. Koszek m = sc->rxring_m[sc->rxring_tl_ptr]; 580a9caca6aSWojciech A. Koszek sc->rxring_m[sc->rxring_tl_ptr] = NULL; 581a9caca6aSWojciech A. Koszek 582a9caca6aSWojciech A. Koszek /* Sync cache with receive buffer. */ 583a9caca6aSWojciech A. Koszek bus_dmamap_sync(sc->mbuf_dma_tag, 584a9caca6aSWojciech A. Koszek sc->rxring_m_dmamap[sc->rxring_tl_ptr], 585a9caca6aSWojciech A. Koszek BUS_DMASYNC_POSTREAD); 586a9caca6aSWojciech A. Koszek 587023911aaSIan Lepore /* Unload and destroy dmamap. */ 588a9caca6aSWojciech A. Koszek bus_dmamap_unload(sc->mbuf_dma_tag, 589a9caca6aSWojciech A. Koszek sc->rxring_m_dmamap[sc->rxring_tl_ptr]); 590023911aaSIan Lepore bus_dmamap_destroy(sc->mbuf_dma_tag, 591023911aaSIan Lepore sc->rxring_m_dmamap[sc->rxring_tl_ptr]); 592023911aaSIan Lepore sc->rxring_m_dmamap[sc->rxring_tl_ptr] = NULL; 593a9caca6aSWojciech A. Koszek 594a9caca6aSWojciech A. Koszek /* Increment tail pointer. */ 595a9caca6aSWojciech A. Koszek if (++sc->rxring_tl_ptr == CGEM_NUM_RX_DESCS) 596a9caca6aSWojciech A. Koszek sc->rxring_tl_ptr = 0; 597a9caca6aSWojciech A. Koszek sc->rxring_queued--; 598a9caca6aSWojciech A. Koszek 599f674004fSIan Lepore /* 600f674004fSIan Lepore * Check FCS and make sure entire packet landed in one mbuf 601a9caca6aSWojciech A. Koszek * cluster (which is much bigger than the largest ethernet 602a9caca6aSWojciech A. Koszek * packet). 603a9caca6aSWojciech A. Koszek */ 604a9caca6aSWojciech A. Koszek if ((ctl & CGEM_RXDESC_BAD_FCS) != 0 || 605a9caca6aSWojciech A. Koszek (ctl & (CGEM_RXDESC_SOF | CGEM_RXDESC_EOF)) != 606a9caca6aSWojciech A. Koszek (CGEM_RXDESC_SOF | CGEM_RXDESC_EOF)) { 607a9caca6aSWojciech A. Koszek /* discard. */ 608a9caca6aSWojciech A. Koszek m_free(m); 609c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 610a9caca6aSWojciech A. Koszek continue; 611a9caca6aSWojciech A. Koszek } 612a9caca6aSWojciech A. Koszek 613df63a5d2SIan Lepore /* Ready it to hand off to upper layers. */ 614a9caca6aSWojciech A. Koszek m->m_data += ETHER_ALIGN; 615a9caca6aSWojciech A. Koszek m->m_len = (ctl & CGEM_RXDESC_LENGTH_MASK); 616a9caca6aSWojciech A. Koszek m->m_pkthdr.rcvif = ifp; 617a9caca6aSWojciech A. Koszek m->m_pkthdr.len = m->m_len; 618a9caca6aSWojciech A. Koszek 619f674004fSIan Lepore /* 620f674004fSIan Lepore * Are we using hardware checksumming? Check the status in the 621f674004fSIan Lepore * receive descriptor. 622a9caca6aSWojciech A. Koszek */ 623023911aaSIan Lepore if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0) { 624a9caca6aSWojciech A. Koszek /* TCP or UDP checks out, IP checks out too. */ 625a9caca6aSWojciech A. Koszek if ((ctl & CGEM_RXDESC_CKSUM_STAT_MASK) == 626a9caca6aSWojciech A. Koszek CGEM_RXDESC_CKSUM_STAT_TCP_GOOD || 627a9caca6aSWojciech A. Koszek (ctl & CGEM_RXDESC_CKSUM_STAT_MASK) == 628a9caca6aSWojciech A. Koszek CGEM_RXDESC_CKSUM_STAT_UDP_GOOD) { 629a9caca6aSWojciech A. Koszek m->m_pkthdr.csum_flags |= 630a9caca6aSWojciech A. Koszek CSUM_IP_CHECKED | CSUM_IP_VALID | 631a9caca6aSWojciech A. Koszek CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 632a9caca6aSWojciech A. Koszek m->m_pkthdr.csum_data = 0xffff; 633a9caca6aSWojciech A. Koszek } else if ((ctl & CGEM_RXDESC_CKSUM_STAT_MASK) == 634a9caca6aSWojciech A. Koszek CGEM_RXDESC_CKSUM_STAT_IP_GOOD) { 635a9caca6aSWojciech A. Koszek /* Only IP checks out. */ 636a9caca6aSWojciech A. Koszek m->m_pkthdr.csum_flags |= 637a9caca6aSWojciech A. Koszek CSUM_IP_CHECKED | CSUM_IP_VALID; 638a9caca6aSWojciech A. Koszek m->m_pkthdr.csum_data = 0xffff; 639a9caca6aSWojciech A. Koszek } 640a9caca6aSWojciech A. Koszek } 641a9caca6aSWojciech A. Koszek 642df63a5d2SIan Lepore /* Queue it up for delivery below. */ 643df63a5d2SIan Lepore *m_tl = m; 644df63a5d2SIan Lepore m_tl = &m->m_next; 645a9caca6aSWojciech A. Koszek } 646df63a5d2SIan Lepore 647df63a5d2SIan Lepore /* Replenish receive buffers. */ 648df63a5d2SIan Lepore cgem_fill_rqueue(sc); 649df63a5d2SIan Lepore 650df63a5d2SIan Lepore /* Unlock and send up packets. */ 651df63a5d2SIan Lepore CGEM_UNLOCK(sc); 652df63a5d2SIan Lepore while (m_hd != NULL) { 653df63a5d2SIan Lepore m = m_hd; 654df63a5d2SIan Lepore m_hd = m_hd->m_next; 655df63a5d2SIan Lepore m->m_next = NULL; 656c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 657023911aaSIan Lepore if_input(ifp, m); 658df63a5d2SIan Lepore } 659df63a5d2SIan Lepore CGEM_LOCK(sc); 660a9caca6aSWojciech A. Koszek } 661a9caca6aSWojciech A. Koszek 662a9caca6aSWojciech A. Koszek /* Find completed transmits and free their mbufs. */ 663a9caca6aSWojciech A. Koszek static void 664a9caca6aSWojciech A. Koszek cgem_clean_tx(struct cgem_softc *sc) 665a9caca6aSWojciech A. Koszek { 666a9caca6aSWojciech A. Koszek struct mbuf *m; 667a9caca6aSWojciech A. Koszek uint32_t ctl; 668a9caca6aSWojciech A. Koszek 669a9caca6aSWojciech A. Koszek CGEM_ASSERT_LOCKED(sc); 670a9caca6aSWojciech A. Koszek 671a9caca6aSWojciech A. Koszek /* free up finished transmits. */ 672a9caca6aSWojciech A. Koszek while (sc->txring_queued > 0 && 673a9caca6aSWojciech A. Koszek ((ctl = sc->txring[sc->txring_tl_ptr].ctl) & 674a9caca6aSWojciech A. Koszek CGEM_TXDESC_USED) != 0) { 675023911aaSIan Lepore /* Sync cache. */ 676a9caca6aSWojciech A. Koszek bus_dmamap_sync(sc->mbuf_dma_tag, 677a9caca6aSWojciech A. Koszek sc->txring_m_dmamap[sc->txring_tl_ptr], 678a9caca6aSWojciech A. Koszek BUS_DMASYNC_POSTWRITE); 679a9caca6aSWojciech A. Koszek 680023911aaSIan Lepore /* Unload and destroy DMA map. */ 681a9caca6aSWojciech A. Koszek bus_dmamap_unload(sc->mbuf_dma_tag, 682a9caca6aSWojciech A. Koszek sc->txring_m_dmamap[sc->txring_tl_ptr]); 683023911aaSIan Lepore bus_dmamap_destroy(sc->mbuf_dma_tag, 684023911aaSIan Lepore sc->txring_m_dmamap[sc->txring_tl_ptr]); 685023911aaSIan Lepore sc->txring_m_dmamap[sc->txring_tl_ptr] = NULL; 686a9caca6aSWojciech A. Koszek 687a9caca6aSWojciech A. Koszek /* Free up the mbuf. */ 688a9caca6aSWojciech A. Koszek m = sc->txring_m[sc->txring_tl_ptr]; 689a9caca6aSWojciech A. Koszek sc->txring_m[sc->txring_tl_ptr] = NULL; 690a9caca6aSWojciech A. Koszek m_freem(m); 691a9caca6aSWojciech A. Koszek 692a9caca6aSWojciech A. Koszek /* Check the status. */ 693a9caca6aSWojciech A. Koszek if ((ctl & CGEM_TXDESC_AHB_ERR) != 0) { 694a9caca6aSWojciech A. Koszek /* Serious bus error. log to console. */ 695facdd1cdSThomas Skibo #ifdef CGEM64 696facdd1cdSThomas Skibo device_printf(sc->dev, 697facdd1cdSThomas Skibo "cgem_clean_tx: AHB error, addr=0x%x%08x\n", 698facdd1cdSThomas Skibo sc->txring[sc->txring_tl_ptr].addrhi, 699facdd1cdSThomas Skibo sc->txring[sc->txring_tl_ptr].addr); 700facdd1cdSThomas Skibo #else 701f674004fSIan Lepore device_printf(sc->dev, 702f674004fSIan Lepore "cgem_clean_tx: AHB error, addr=0x%x\n", 703a9caca6aSWojciech A. Koszek sc->txring[sc->txring_tl_ptr].addr); 704facdd1cdSThomas Skibo #endif 705a9caca6aSWojciech A. Koszek } else if ((ctl & (CGEM_TXDESC_RETRY_ERR | 706a9caca6aSWojciech A. Koszek CGEM_TXDESC_LATE_COLL)) != 0) { 707c8dfaf38SGleb Smirnoff if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1); 708a9caca6aSWojciech A. Koszek } else 709c8dfaf38SGleb Smirnoff if_inc_counter(sc->ifp, IFCOUNTER_OPACKETS, 1); 710a9caca6aSWojciech A. Koszek 711f674004fSIan Lepore /* 712f674004fSIan Lepore * If the packet spanned more than one tx descriptor, skip 713facdd1cdSThomas Skibo * descriptors until we find the end so that only 714facdd1cdSThomas Skibo * start-of-frame descriptors are processed. 715a9caca6aSWojciech A. Koszek */ 716a9caca6aSWojciech A. Koszek while ((ctl & CGEM_TXDESC_LAST_BUF) == 0) { 717a9caca6aSWojciech A. Koszek if ((ctl & CGEM_TXDESC_WRAP) != 0) 718a9caca6aSWojciech A. Koszek sc->txring_tl_ptr = 0; 719a9caca6aSWojciech A. Koszek else 720a9caca6aSWojciech A. Koszek sc->txring_tl_ptr++; 721a9caca6aSWojciech A. Koszek sc->txring_queued--; 722a9caca6aSWojciech A. Koszek 723a9caca6aSWojciech A. Koszek ctl = sc->txring[sc->txring_tl_ptr].ctl; 724a9caca6aSWojciech A. Koszek 725a9caca6aSWojciech A. Koszek sc->txring[sc->txring_tl_ptr].ctl = 726a9caca6aSWojciech A. Koszek ctl | CGEM_TXDESC_USED; 727a9caca6aSWojciech A. Koszek } 728a9caca6aSWojciech A. Koszek 729a9caca6aSWojciech A. Koszek /* Next descriptor. */ 730a9caca6aSWojciech A. Koszek if ((ctl & CGEM_TXDESC_WRAP) != 0) 731a9caca6aSWojciech A. Koszek sc->txring_tl_ptr = 0; 732a9caca6aSWojciech A. Koszek else 733a9caca6aSWojciech A. Koszek sc->txring_tl_ptr++; 734a9caca6aSWojciech A. Koszek sc->txring_queued--; 735df63a5d2SIan Lepore 736023911aaSIan Lepore if_setdrvflagbits(sc->ifp, 0, IFF_DRV_OACTIVE); 737a9caca6aSWojciech A. Koszek } 738a9caca6aSWojciech A. Koszek } 739a9caca6aSWojciech A. Koszek 740a9caca6aSWojciech A. Koszek /* Start transmits. */ 741a9caca6aSWojciech A. Koszek static void 742023911aaSIan Lepore cgem_start_locked(if_t ifp) 743a9caca6aSWojciech A. Koszek { 744023911aaSIan Lepore struct cgem_softc *sc = (struct cgem_softc *) if_getsoftc(ifp); 745a9caca6aSWojciech A. Koszek struct mbuf *m; 746a9caca6aSWojciech A. Koszek bus_dma_segment_t segs[TX_MAX_DMA_SEGS]; 747a9caca6aSWojciech A. Koszek uint32_t ctl; 748a9caca6aSWojciech A. Koszek int i, nsegs, wrap, err; 749a9caca6aSWojciech A. Koszek 750a9caca6aSWojciech A. Koszek CGEM_ASSERT_LOCKED(sc); 751a9caca6aSWojciech A. Koszek 752023911aaSIan Lepore if ((if_getdrvflags(ifp) & IFF_DRV_OACTIVE) != 0) 753a9caca6aSWojciech A. Koszek return; 754a9caca6aSWojciech A. Koszek 755a9caca6aSWojciech A. Koszek for (;;) { 756a9caca6aSWojciech A. Koszek /* Check that there is room in the descriptor ring. */ 757df63a5d2SIan Lepore if (sc->txring_queued >= 758df63a5d2SIan Lepore CGEM_NUM_TX_DESCS - TX_MAX_DMA_SEGS * 2) { 759a9caca6aSWojciech A. Koszek /* Try to make room. */ 760a9caca6aSWojciech A. Koszek cgem_clean_tx(sc); 761a9caca6aSWojciech A. Koszek 762a9caca6aSWojciech A. Koszek /* Still no room? */ 763df63a5d2SIan Lepore if (sc->txring_queued >= 764df63a5d2SIan Lepore CGEM_NUM_TX_DESCS - TX_MAX_DMA_SEGS * 2) { 765023911aaSIan Lepore if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 766df63a5d2SIan Lepore sc->txfull++; 767a9caca6aSWojciech A. Koszek break; 768a9caca6aSWojciech A. Koszek } 769a9caca6aSWojciech A. Koszek } 770a9caca6aSWojciech A. Koszek 771a9caca6aSWojciech A. Koszek /* Grab next transmit packet. */ 772023911aaSIan Lepore m = if_dequeue(ifp); 773a9caca6aSWojciech A. Koszek if (m == NULL) 774a9caca6aSWojciech A. Koszek break; 775a9caca6aSWojciech A. Koszek 776023911aaSIan Lepore /* Create and load DMA map. */ 777023911aaSIan Lepore if (bus_dmamap_create(sc->mbuf_dma_tag, 0, 778023911aaSIan Lepore &sc->txring_m_dmamap[sc->txring_hd_ptr])) { 779023911aaSIan Lepore m_freem(m); 780023911aaSIan Lepore sc->txdmamapfails++; 781023911aaSIan Lepore continue; 782023911aaSIan Lepore } 783a9caca6aSWojciech A. Koszek err = bus_dmamap_load_mbuf_sg(sc->mbuf_dma_tag, 784f674004fSIan Lepore sc->txring_m_dmamap[sc->txring_hd_ptr], m, segs, &nsegs, 785f674004fSIan Lepore BUS_DMA_NOWAIT); 786a9caca6aSWojciech A. Koszek if (err == EFBIG) { 787a9caca6aSWojciech A. Koszek /* Too many segments! defrag and try again. */ 788a9caca6aSWojciech A. Koszek struct mbuf *m2 = m_defrag(m, M_NOWAIT); 789a9caca6aSWojciech A. Koszek 790a9caca6aSWojciech A. Koszek if (m2 == NULL) { 791df63a5d2SIan Lepore sc->txdefragfails++; 792a9caca6aSWojciech A. Koszek m_freem(m); 793023911aaSIan Lepore bus_dmamap_destroy(sc->mbuf_dma_tag, 794023911aaSIan Lepore sc->txring_m_dmamap[sc->txring_hd_ptr]); 795023911aaSIan Lepore sc->txring_m_dmamap[sc->txring_hd_ptr] = NULL; 796a9caca6aSWojciech A. Koszek continue; 797a9caca6aSWojciech A. Koszek } 798a9caca6aSWojciech A. Koszek m = m2; 799a9caca6aSWojciech A. Koszek err = bus_dmamap_load_mbuf_sg(sc->mbuf_dma_tag, 800f674004fSIan Lepore sc->txring_m_dmamap[sc->txring_hd_ptr], m, segs, 801f674004fSIan Lepore &nsegs, BUS_DMA_NOWAIT); 802df63a5d2SIan Lepore sc->txdefrags++; 803a9caca6aSWojciech A. Koszek } 804a9caca6aSWojciech A. Koszek if (err) { 805a9caca6aSWojciech A. Koszek /* Give up. */ 806a9caca6aSWojciech A. Koszek m_freem(m); 807023911aaSIan Lepore bus_dmamap_destroy(sc->mbuf_dma_tag, 808023911aaSIan Lepore sc->txring_m_dmamap[sc->txring_hd_ptr]); 809023911aaSIan Lepore sc->txring_m_dmamap[sc->txring_hd_ptr] = NULL; 810df63a5d2SIan Lepore sc->txdmamapfails++; 811a9caca6aSWojciech A. Koszek continue; 812a9caca6aSWojciech A. Koszek } 813a9caca6aSWojciech A. Koszek sc->txring_m[sc->txring_hd_ptr] = m; 814a9caca6aSWojciech A. Koszek 815a9caca6aSWojciech A. Koszek /* Sync tx buffer with cache. */ 816a9caca6aSWojciech A. Koszek bus_dmamap_sync(sc->mbuf_dma_tag, 817a9caca6aSWojciech A. Koszek sc->txring_m_dmamap[sc->txring_hd_ptr], 818a9caca6aSWojciech A. Koszek BUS_DMASYNC_PREWRITE); 819a9caca6aSWojciech A. Koszek 820a9caca6aSWojciech A. Koszek /* Set wrap flag if next packet might run off end of ring. */ 821a9caca6aSWojciech A. Koszek wrap = sc->txring_hd_ptr + nsegs + TX_MAX_DMA_SEGS >= 822a9caca6aSWojciech A. Koszek CGEM_NUM_TX_DESCS; 823a9caca6aSWojciech A. Koszek 824f674004fSIan Lepore /* 825f674004fSIan Lepore * Fill in the TX descriptors back to front so that USED bit in 826f674004fSIan Lepore * first descriptor is cleared last. 827a9caca6aSWojciech A. Koszek */ 828a9caca6aSWojciech A. Koszek for (i = nsegs - 1; i >= 0; i--) { 829a9caca6aSWojciech A. Koszek /* Descriptor address. */ 830a9caca6aSWojciech A. Koszek sc->txring[sc->txring_hd_ptr + i].addr = 831a9caca6aSWojciech A. Koszek segs[i].ds_addr; 832facdd1cdSThomas Skibo #ifdef CGEM64 833facdd1cdSThomas Skibo sc->txring[sc->txring_hd_ptr + i].addrhi = 834facdd1cdSThomas Skibo segs[i].ds_addr >> 32; 835facdd1cdSThomas Skibo #endif 836a9caca6aSWojciech A. Koszek /* Descriptor control word. */ 837a9caca6aSWojciech A. Koszek ctl = segs[i].ds_len; 838a9caca6aSWojciech A. Koszek if (i == nsegs - 1) { 839a9caca6aSWojciech A. Koszek ctl |= CGEM_TXDESC_LAST_BUF; 840a9caca6aSWojciech A. Koszek if (wrap) 841a9caca6aSWojciech A. Koszek ctl |= CGEM_TXDESC_WRAP; 842a9caca6aSWojciech A. Koszek } 843a9caca6aSWojciech A. Koszek sc->txring[sc->txring_hd_ptr + i].ctl = ctl; 844a9caca6aSWojciech A. Koszek 845a9caca6aSWojciech A. Koszek if (i != 0) 846a9caca6aSWojciech A. Koszek sc->txring_m[sc->txring_hd_ptr + i] = NULL; 847a9caca6aSWojciech A. Koszek } 848a9caca6aSWojciech A. Koszek 849a9caca6aSWojciech A. Koszek if (wrap) 850a9caca6aSWojciech A. Koszek sc->txring_hd_ptr = 0; 851a9caca6aSWojciech A. Koszek else 852a9caca6aSWojciech A. Koszek sc->txring_hd_ptr += nsegs; 853a9caca6aSWojciech A. Koszek sc->txring_queued += nsegs; 854a9caca6aSWojciech A. Koszek 855a9caca6aSWojciech A. Koszek /* Kick the transmitter. */ 856a9caca6aSWojciech A. Koszek WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow | 857a9caca6aSWojciech A. Koszek CGEM_NET_CTRL_START_TX); 858a9caca6aSWojciech A. Koszek 859caa7e52fSEitan Adler /* If there is a BPF listener, bounce a copy to him. */ 860df63a5d2SIan Lepore ETHER_BPF_MTAP(ifp, m); 861df63a5d2SIan Lepore } 862a9caca6aSWojciech A. Koszek } 863a9caca6aSWojciech A. Koszek 864a9caca6aSWojciech A. Koszek static void 865023911aaSIan Lepore cgem_start(if_t ifp) 866a9caca6aSWojciech A. Koszek { 867023911aaSIan Lepore struct cgem_softc *sc = (struct cgem_softc *) if_getsoftc(ifp); 868a9caca6aSWojciech A. Koszek 869a9caca6aSWojciech A. Koszek CGEM_LOCK(sc); 870a9caca6aSWojciech A. Koszek cgem_start_locked(ifp); 871a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 872a9caca6aSWojciech A. Koszek } 873a9caca6aSWojciech A. Koszek 874a9caca6aSWojciech A. Koszek static void 875df63a5d2SIan Lepore cgem_poll_hw_stats(struct cgem_softc *sc) 876df63a5d2SIan Lepore { 877df63a5d2SIan Lepore uint32_t n; 878df63a5d2SIan Lepore 879df63a5d2SIan Lepore CGEM_ASSERT_LOCKED(sc); 880df63a5d2SIan Lepore 881df63a5d2SIan Lepore sc->stats.tx_bytes += RD4(sc, CGEM_OCTETS_TX_BOT); 882df63a5d2SIan Lepore sc->stats.tx_bytes += (uint64_t)RD4(sc, CGEM_OCTETS_TX_TOP) << 32; 883df63a5d2SIan Lepore 884df63a5d2SIan Lepore sc->stats.tx_frames += RD4(sc, CGEM_FRAMES_TX); 885df63a5d2SIan Lepore sc->stats.tx_frames_bcast += RD4(sc, CGEM_BCAST_FRAMES_TX); 886df63a5d2SIan Lepore sc->stats.tx_frames_multi += RD4(sc, CGEM_MULTI_FRAMES_TX); 887df63a5d2SIan Lepore sc->stats.tx_frames_pause += RD4(sc, CGEM_PAUSE_FRAMES_TX); 888df63a5d2SIan Lepore sc->stats.tx_frames_64b += RD4(sc, CGEM_FRAMES_64B_TX); 889df63a5d2SIan Lepore sc->stats.tx_frames_65to127b += RD4(sc, CGEM_FRAMES_65_127B_TX); 890df63a5d2SIan Lepore sc->stats.tx_frames_128to255b += RD4(sc, CGEM_FRAMES_128_255B_TX); 891df63a5d2SIan Lepore sc->stats.tx_frames_256to511b += RD4(sc, CGEM_FRAMES_256_511B_TX); 892df63a5d2SIan Lepore sc->stats.tx_frames_512to1023b += RD4(sc, CGEM_FRAMES_512_1023B_TX); 893df63a5d2SIan Lepore sc->stats.tx_frames_1024to1536b += RD4(sc, CGEM_FRAMES_1024_1518B_TX); 894df63a5d2SIan Lepore sc->stats.tx_under_runs += RD4(sc, CGEM_TX_UNDERRUNS); 895df63a5d2SIan Lepore 896df63a5d2SIan Lepore n = RD4(sc, CGEM_SINGLE_COLL_FRAMES); 897df63a5d2SIan Lepore sc->stats.tx_single_collisn += n; 898c8dfaf38SGleb Smirnoff if_inc_counter(sc->ifp, IFCOUNTER_COLLISIONS, n); 899df63a5d2SIan Lepore n = RD4(sc, CGEM_MULTI_COLL_FRAMES); 900df63a5d2SIan Lepore sc->stats.tx_multi_collisn += n; 901c8dfaf38SGleb Smirnoff if_inc_counter(sc->ifp, IFCOUNTER_COLLISIONS, n); 902df63a5d2SIan Lepore n = RD4(sc, CGEM_EXCESSIVE_COLL_FRAMES); 903df63a5d2SIan Lepore sc->stats.tx_excsv_collisn += n; 904c8dfaf38SGleb Smirnoff if_inc_counter(sc->ifp, IFCOUNTER_COLLISIONS, n); 905df63a5d2SIan Lepore n = RD4(sc, CGEM_LATE_COLL); 906df63a5d2SIan Lepore sc->stats.tx_late_collisn += n; 907c8dfaf38SGleb Smirnoff if_inc_counter(sc->ifp, IFCOUNTER_COLLISIONS, n); 908df63a5d2SIan Lepore 909df63a5d2SIan Lepore sc->stats.tx_deferred_frames += RD4(sc, CGEM_DEFERRED_TX_FRAMES); 910df63a5d2SIan Lepore sc->stats.tx_carrier_sense_errs += RD4(sc, CGEM_CARRIER_SENSE_ERRS); 911df63a5d2SIan Lepore 912df63a5d2SIan Lepore sc->stats.rx_bytes += RD4(sc, CGEM_OCTETS_RX_BOT); 913df63a5d2SIan Lepore sc->stats.rx_bytes += (uint64_t)RD4(sc, CGEM_OCTETS_RX_TOP) << 32; 914df63a5d2SIan Lepore 915df63a5d2SIan Lepore sc->stats.rx_frames += RD4(sc, CGEM_FRAMES_RX); 916df63a5d2SIan Lepore sc->stats.rx_frames_bcast += RD4(sc, CGEM_BCAST_FRAMES_RX); 917df63a5d2SIan Lepore sc->stats.rx_frames_multi += RD4(sc, CGEM_MULTI_FRAMES_RX); 918df63a5d2SIan Lepore sc->stats.rx_frames_pause += RD4(sc, CGEM_PAUSE_FRAMES_RX); 919df63a5d2SIan Lepore sc->stats.rx_frames_64b += RD4(sc, CGEM_FRAMES_64B_RX); 920df63a5d2SIan Lepore sc->stats.rx_frames_65to127b += RD4(sc, CGEM_FRAMES_65_127B_RX); 921df63a5d2SIan Lepore sc->stats.rx_frames_128to255b += RD4(sc, CGEM_FRAMES_128_255B_RX); 922df63a5d2SIan Lepore sc->stats.rx_frames_256to511b += RD4(sc, CGEM_FRAMES_256_511B_RX); 923df63a5d2SIan Lepore sc->stats.rx_frames_512to1023b += RD4(sc, CGEM_FRAMES_512_1023B_RX); 924df63a5d2SIan Lepore sc->stats.rx_frames_1024to1536b += RD4(sc, CGEM_FRAMES_1024_1518B_RX); 925df63a5d2SIan Lepore sc->stats.rx_frames_undersize += RD4(sc, CGEM_UNDERSZ_RX); 926df63a5d2SIan Lepore sc->stats.rx_frames_oversize += RD4(sc, CGEM_OVERSZ_RX); 927df63a5d2SIan Lepore sc->stats.rx_frames_jabber += RD4(sc, CGEM_JABBERS_RX); 928df63a5d2SIan Lepore sc->stats.rx_frames_fcs_errs += RD4(sc, CGEM_FCS_ERRS); 929df63a5d2SIan Lepore sc->stats.rx_frames_length_errs += RD4(sc, CGEM_LENGTH_FIELD_ERRS); 930df63a5d2SIan Lepore sc->stats.rx_symbol_errs += RD4(sc, CGEM_RX_SYMBOL_ERRS); 931df63a5d2SIan Lepore sc->stats.rx_align_errs += RD4(sc, CGEM_ALIGN_ERRS); 932df63a5d2SIan Lepore sc->stats.rx_resource_errs += RD4(sc, CGEM_RX_RESOURCE_ERRS); 933df63a5d2SIan Lepore sc->stats.rx_overrun_errs += RD4(sc, CGEM_RX_OVERRUN_ERRS); 934df63a5d2SIan Lepore sc->stats.rx_ip_hdr_csum_errs += RD4(sc, CGEM_IP_HDR_CKSUM_ERRS); 935df63a5d2SIan Lepore sc->stats.rx_tcp_csum_errs += RD4(sc, CGEM_TCP_CKSUM_ERRS); 936df63a5d2SIan Lepore sc->stats.rx_udp_csum_errs += RD4(sc, CGEM_UDP_CKSUM_ERRS); 937df63a5d2SIan Lepore } 938df63a5d2SIan Lepore 939df63a5d2SIan Lepore static void 940a9caca6aSWojciech A. Koszek cgem_tick(void *arg) 941a9caca6aSWojciech A. Koszek { 942a9caca6aSWojciech A. Koszek struct cgem_softc *sc = (struct cgem_softc *)arg; 943a9caca6aSWojciech A. Koszek struct mii_data *mii; 944a9caca6aSWojciech A. Koszek 945a9caca6aSWojciech A. Koszek CGEM_ASSERT_LOCKED(sc); 946a9caca6aSWojciech A. Koszek 947a9caca6aSWojciech A. Koszek /* Poll the phy. */ 948a9caca6aSWojciech A. Koszek if (sc->miibus != NULL) { 949a9caca6aSWojciech A. Koszek mii = device_get_softc(sc->miibus); 950a9caca6aSWojciech A. Koszek mii_tick(mii); 951a9caca6aSWojciech A. Koszek } 952a9caca6aSWojciech A. Koszek 953df63a5d2SIan Lepore /* Poll statistics registers. */ 954df63a5d2SIan Lepore cgem_poll_hw_stats(sc); 955df63a5d2SIan Lepore 956df63a5d2SIan Lepore /* Check for receiver hang. */ 957df63a5d2SIan Lepore if (sc->rxhangwar && sc->rx_frames_prev == sc->stats.rx_frames) { 958df63a5d2SIan Lepore /* 959df63a5d2SIan Lepore * Reset receiver logic by toggling RX_EN bit. 1usec 960df63a5d2SIan Lepore * delay is necessary especially when operating at 100mbps 961df63a5d2SIan Lepore * and 10mbps speeds. 962df63a5d2SIan Lepore */ 963df63a5d2SIan Lepore WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow & 964df63a5d2SIan Lepore ~CGEM_NET_CTRL_RX_EN); 965df63a5d2SIan Lepore DELAY(1); 966df63a5d2SIan Lepore WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow); 967df63a5d2SIan Lepore } 968df63a5d2SIan Lepore sc->rx_frames_prev = sc->stats.rx_frames; 969df63a5d2SIan Lepore 970a9caca6aSWojciech A. Koszek /* Next callout in one second. */ 971a9caca6aSWojciech A. Koszek callout_reset(&sc->tick_ch, hz, cgem_tick, sc); 972a9caca6aSWojciech A. Koszek } 973a9caca6aSWojciech A. Koszek 974a9caca6aSWojciech A. Koszek /* Interrupt handler. */ 975a9caca6aSWojciech A. Koszek static void 976a9caca6aSWojciech A. Koszek cgem_intr(void *arg) 977a9caca6aSWojciech A. Koszek { 978a9caca6aSWojciech A. Koszek struct cgem_softc *sc = (struct cgem_softc *)arg; 979023911aaSIan Lepore if_t ifp = sc->ifp; 980a9caca6aSWojciech A. Koszek uint32_t istatus; 981a9caca6aSWojciech A. Koszek 982a9caca6aSWojciech A. Koszek CGEM_LOCK(sc); 983a9caca6aSWojciech A. Koszek 984023911aaSIan Lepore if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) { 985a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 986a9caca6aSWojciech A. Koszek return; 987a9caca6aSWojciech A. Koszek } 988a9caca6aSWojciech A. Koszek 989df63a5d2SIan Lepore /* Read interrupt status and immediately clear the bits. */ 990a9caca6aSWojciech A. Koszek istatus = RD4(sc, CGEM_INTR_STAT); 991df63a5d2SIan Lepore WR4(sc, CGEM_INTR_STAT, istatus); 992a9caca6aSWojciech A. Koszek 993df63a5d2SIan Lepore /* Packets received. */ 994df63a5d2SIan Lepore if ((istatus & CGEM_INTR_RX_COMPLETE) != 0) 995df63a5d2SIan Lepore cgem_recv(sc); 996df63a5d2SIan Lepore 997df63a5d2SIan Lepore /* Free up any completed transmit buffers. */ 998df63a5d2SIan Lepore cgem_clean_tx(sc); 999df63a5d2SIan Lepore 1000df63a5d2SIan Lepore /* Hresp not ok. Something is very bad with DMA. Try to clear. */ 1001a9caca6aSWojciech A. Koszek if ((istatus & CGEM_INTR_HRESP_NOT_OK) != 0) { 1002f674004fSIan Lepore device_printf(sc->dev, 1003f674004fSIan Lepore "cgem_intr: hresp not okay! rx_status=0x%x\n", 1004f674004fSIan Lepore RD4(sc, CGEM_RX_STAT)); 1005a9caca6aSWojciech A. Koszek WR4(sc, CGEM_RX_STAT, CGEM_RX_STAT_HRESP_NOT_OK); 1006a9caca6aSWojciech A. Koszek } 1007a9caca6aSWojciech A. Koszek 1008df63a5d2SIan Lepore /* Receiver overrun. */ 1009a9caca6aSWojciech A. Koszek if ((istatus & CGEM_INTR_RX_OVERRUN) != 0) { 1010df63a5d2SIan Lepore /* Clear status bit. */ 1011df63a5d2SIan Lepore WR4(sc, CGEM_RX_STAT, CGEM_RX_STAT_OVERRUN); 1012a9caca6aSWojciech A. Koszek sc->rxoverruns++; 1013a9caca6aSWojciech A. Koszek } 1014df63a5d2SIan Lepore 1015df63a5d2SIan Lepore /* Receiver ran out of bufs. */ 1016df63a5d2SIan Lepore if ((istatus & CGEM_INTR_RX_USED_READ) != 0) { 1017df63a5d2SIan Lepore WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow | 1018df63a5d2SIan Lepore CGEM_NET_CTRL_FLUSH_DPRAM_PKT); 1019df63a5d2SIan Lepore cgem_fill_rqueue(sc); 1020df63a5d2SIan Lepore sc->rxnobufs++; 1021a9caca6aSWojciech A. Koszek } 1022a9caca6aSWojciech A. Koszek 1023df63a5d2SIan Lepore /* Restart transmitter if needed. */ 1024023911aaSIan Lepore if (!if_sendq_empty(ifp)) 1025023911aaSIan Lepore cgem_start_locked(ifp); 1026df63a5d2SIan Lepore 1027a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 1028a9caca6aSWojciech A. Koszek } 1029a9caca6aSWojciech A. Koszek 1030a9caca6aSWojciech A. Koszek /* Reset hardware. */ 1031a9caca6aSWojciech A. Koszek static void 1032a9caca6aSWojciech A. Koszek cgem_reset(struct cgem_softc *sc) 1033a9caca6aSWojciech A. Koszek { 1034a9caca6aSWojciech A. Koszek 1035a9caca6aSWojciech A. Koszek CGEM_ASSERT_LOCKED(sc); 1036a9caca6aSWojciech A. Koszek 1037facdd1cdSThomas Skibo /* Determine data bus width from design configuration register. */ 1038facdd1cdSThomas Skibo switch (RD4(sc, CGEM_DESIGN_CFG1) & 1039facdd1cdSThomas Skibo CGEM_DESIGN_CFG1_DMA_BUS_WIDTH_MASK) { 1040facdd1cdSThomas Skibo case CGEM_DESIGN_CFG1_DMA_BUS_WIDTH_64: 1041facdd1cdSThomas Skibo sc->net_cfg_shadow = CGEM_NET_CFG_DBUS_WIDTH_64; 1042facdd1cdSThomas Skibo break; 1043facdd1cdSThomas Skibo case CGEM_DESIGN_CFG1_DMA_BUS_WIDTH_128: 1044facdd1cdSThomas Skibo sc->net_cfg_shadow = CGEM_NET_CFG_DBUS_WIDTH_128; 1045facdd1cdSThomas Skibo break; 1046facdd1cdSThomas Skibo default: 1047facdd1cdSThomas Skibo sc->net_cfg_shadow = CGEM_NET_CFG_DBUS_WIDTH_32; 1048facdd1cdSThomas Skibo } 1049facdd1cdSThomas Skibo 1050a9caca6aSWojciech A. Koszek WR4(sc, CGEM_NET_CTRL, 0); 1051facdd1cdSThomas Skibo WR4(sc, CGEM_NET_CFG, sc->net_cfg_shadow); 1052a9caca6aSWojciech A. Koszek WR4(sc, CGEM_NET_CTRL, CGEM_NET_CTRL_CLR_STAT_REGS); 1053a9caca6aSWojciech A. Koszek WR4(sc, CGEM_TX_STAT, CGEM_TX_STAT_ALL); 1054a9caca6aSWojciech A. Koszek WR4(sc, CGEM_RX_STAT, CGEM_RX_STAT_ALL); 1055a9caca6aSWojciech A. Koszek WR4(sc, CGEM_INTR_DIS, CGEM_INTR_ALL); 1056a9caca6aSWojciech A. Koszek WR4(sc, CGEM_HASH_BOT, 0); 1057a9caca6aSWojciech A. Koszek WR4(sc, CGEM_HASH_TOP, 0); 1058a9caca6aSWojciech A. Koszek WR4(sc, CGEM_TX_QBAR, 0); /* manual says do this. */ 1059a9caca6aSWojciech A. Koszek WR4(sc, CGEM_RX_QBAR, 0); 1060a9caca6aSWojciech A. Koszek 1061a9caca6aSWojciech A. Koszek /* Get management port running even if interface is down. */ 1062facdd1cdSThomas Skibo sc->net_cfg_shadow |= CGEM_NET_CFG_MDC_CLK_DIV_48; 1063facdd1cdSThomas Skibo WR4(sc, CGEM_NET_CFG, sc->net_cfg_shadow); 1064a9caca6aSWojciech A. Koszek 1065a9caca6aSWojciech A. Koszek sc->net_ctl_shadow = CGEM_NET_CTRL_MGMT_PORT_EN; 1066a9caca6aSWojciech A. Koszek WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow); 1067a9caca6aSWojciech A. Koszek } 1068a9caca6aSWojciech A. Koszek 1069a9caca6aSWojciech A. Koszek /* Bring up the hardware. */ 1070a9caca6aSWojciech A. Koszek static void 1071a9caca6aSWojciech A. Koszek cgem_config(struct cgem_softc *sc) 1072a9caca6aSWojciech A. Koszek { 1073023911aaSIan Lepore if_t ifp = sc->ifp; 1074a9caca6aSWojciech A. Koszek uint32_t dma_cfg; 1075023911aaSIan Lepore u_char *eaddr = if_getlladdr(ifp); 1076a9caca6aSWojciech A. Koszek 1077a9caca6aSWojciech A. Koszek CGEM_ASSERT_LOCKED(sc); 1078a9caca6aSWojciech A. Koszek 1079a9caca6aSWojciech A. Koszek /* Program Net Config Register. */ 1080facdd1cdSThomas Skibo sc->net_cfg_shadow &= (CGEM_NET_CFG_MDC_CLK_DIV_MASK | 1081facdd1cdSThomas Skibo CGEM_NET_CFG_DBUS_WIDTH_MASK); 1082facdd1cdSThomas Skibo sc->net_cfg_shadow |= (CGEM_NET_CFG_FCS_REMOVE | 1083a9caca6aSWojciech A. Koszek CGEM_NET_CFG_RX_BUF_OFFSET(ETHER_ALIGN) | 1084facdd1cdSThomas Skibo CGEM_NET_CFG_GIGE_EN | CGEM_NET_CFG_1536RXEN | 1085facdd1cdSThomas Skibo CGEM_NET_CFG_FULL_DUPLEX | CGEM_NET_CFG_SPEED100); 1086a9caca6aSWojciech A. Koszek 108734f4c17aSMilan Obuch /* Check connection type, enable SGMII bits if necessary. */ 108834f4c17aSMilan Obuch if (sc->phy_contype == MII_CONTYPE_SGMII) { 108934f4c17aSMilan Obuch sc->net_cfg_shadow |= CGEM_NET_CFG_SGMII_EN; 109034f4c17aSMilan Obuch sc->net_cfg_shadow |= CGEM_NET_CFG_PCS_SEL; 109134f4c17aSMilan Obuch } 109234f4c17aSMilan Obuch 1093a9caca6aSWojciech A. Koszek /* Enable receive checksum offloading? */ 1094023911aaSIan Lepore if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0) 1095facdd1cdSThomas Skibo sc->net_cfg_shadow |= CGEM_NET_CFG_RX_CHKSUM_OFFLD_EN; 1096a9caca6aSWojciech A. Koszek 1097facdd1cdSThomas Skibo WR4(sc, CGEM_NET_CFG, sc->net_cfg_shadow); 1098a9caca6aSWojciech A. Koszek 1099a9caca6aSWojciech A. Koszek /* Program DMA Config Register. */ 1100a9caca6aSWojciech A. Koszek dma_cfg = CGEM_DMA_CFG_RX_BUF_SIZE(MCLBYTES) | 1101a9caca6aSWojciech A. Koszek CGEM_DMA_CFG_RX_PKTBUF_MEMSZ_SEL_8K | 1102a9caca6aSWojciech A. Koszek CGEM_DMA_CFG_TX_PKTBUF_MEMSZ_SEL | 1103df63a5d2SIan Lepore CGEM_DMA_CFG_AHB_FIXED_BURST_LEN_16 | 1104facdd1cdSThomas Skibo #ifdef CGEM64 1105facdd1cdSThomas Skibo CGEM_DMA_CFG_ADDR_BUS_64 | 1106facdd1cdSThomas Skibo #endif 1107df63a5d2SIan Lepore CGEM_DMA_CFG_DISC_WHEN_NO_AHB; 1108a9caca6aSWojciech A. Koszek 1109a9caca6aSWojciech A. Koszek /* Enable transmit checksum offloading? */ 1110023911aaSIan Lepore if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) 1111a9caca6aSWojciech A. Koszek dma_cfg |= CGEM_DMA_CFG_CHKSUM_GEN_OFFLOAD_EN; 1112a9caca6aSWojciech A. Koszek 1113a9caca6aSWojciech A. Koszek WR4(sc, CGEM_DMA_CFG, dma_cfg); 1114a9caca6aSWojciech A. Koszek 1115a9caca6aSWojciech A. Koszek /* Write the rx and tx descriptor ring addresses to the QBAR regs. */ 1116a9caca6aSWojciech A. Koszek WR4(sc, CGEM_RX_QBAR, (uint32_t)sc->rxring_physaddr); 1117a9caca6aSWojciech A. Koszek WR4(sc, CGEM_TX_QBAR, (uint32_t)sc->txring_physaddr); 1118facdd1cdSThomas Skibo #ifdef CGEM64 1119facdd1cdSThomas Skibo WR4(sc, CGEM_RX_QBAR_HI, (uint32_t)(sc->rxring_physaddr >> 32)); 1120facdd1cdSThomas Skibo WR4(sc, CGEM_TX_QBAR_HI, (uint32_t)(sc->txring_physaddr >> 32)); 1121facdd1cdSThomas Skibo #endif 1122a9caca6aSWojciech A. Koszek 1123a9caca6aSWojciech A. Koszek /* Enable rx and tx. */ 1124a9caca6aSWojciech A. Koszek sc->net_ctl_shadow |= (CGEM_NET_CTRL_TX_EN | CGEM_NET_CTRL_RX_EN); 1125a9caca6aSWojciech A. Koszek WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow); 1126a9caca6aSWojciech A. Koszek 1127df63a5d2SIan Lepore /* Set receive address in case it changed. */ 1128df63a5d2SIan Lepore WR4(sc, CGEM_SPEC_ADDR_LOW(0), (eaddr[3] << 24) | 1129df63a5d2SIan Lepore (eaddr[2] << 16) | (eaddr[1] << 8) | eaddr[0]); 1130df63a5d2SIan Lepore WR4(sc, CGEM_SPEC_ADDR_HI(0), (eaddr[5] << 8) | eaddr[4]); 1131df63a5d2SIan Lepore 1132a9caca6aSWojciech A. Koszek /* Set up interrupts. */ 1133f674004fSIan Lepore WR4(sc, CGEM_INTR_EN, CGEM_INTR_RX_COMPLETE | CGEM_INTR_RX_OVERRUN | 1134df63a5d2SIan Lepore CGEM_INTR_TX_USED_READ | CGEM_INTR_RX_USED_READ | 1135df63a5d2SIan Lepore CGEM_INTR_HRESP_NOT_OK); 1136a9caca6aSWojciech A. Koszek } 1137a9caca6aSWojciech A. Koszek 1138a9caca6aSWojciech A. Koszek /* Turn on interface and load up receive ring with buffers. */ 1139a9caca6aSWojciech A. Koszek static void 1140a9caca6aSWojciech A. Koszek cgem_init_locked(struct cgem_softc *sc) 1141a9caca6aSWojciech A. Koszek { 1142a9caca6aSWojciech A. Koszek struct mii_data *mii; 1143a9caca6aSWojciech A. Koszek 1144a9caca6aSWojciech A. Koszek CGEM_ASSERT_LOCKED(sc); 1145a9caca6aSWojciech A. Koszek 1146023911aaSIan Lepore if ((if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) != 0) 1147a9caca6aSWojciech A. Koszek return; 1148a9caca6aSWojciech A. Koszek 1149a9caca6aSWojciech A. Koszek cgem_config(sc); 1150a9caca6aSWojciech A. Koszek cgem_fill_rqueue(sc); 1151a9caca6aSWojciech A. Koszek 1152023911aaSIan Lepore if_setdrvflagbits(sc->ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); 1153a9caca6aSWojciech A. Koszek 1154facdd1cdSThomas Skibo if (sc->miibus != NULL) { 1155a9caca6aSWojciech A. Koszek mii = device_get_softc(sc->miibus); 1156df63a5d2SIan Lepore mii_mediachg(mii); 1157facdd1cdSThomas Skibo } 1158a9caca6aSWojciech A. Koszek 1159a9caca6aSWojciech A. Koszek callout_reset(&sc->tick_ch, hz, cgem_tick, sc); 1160a9caca6aSWojciech A. Koszek } 1161a9caca6aSWojciech A. Koszek 1162a9caca6aSWojciech A. Koszek static void 1163a9caca6aSWojciech A. Koszek cgem_init(void *arg) 1164a9caca6aSWojciech A. Koszek { 1165a9caca6aSWojciech A. Koszek struct cgem_softc *sc = (struct cgem_softc *)arg; 1166a9caca6aSWojciech A. Koszek 1167a9caca6aSWojciech A. Koszek CGEM_LOCK(sc); 1168a9caca6aSWojciech A. Koszek cgem_init_locked(sc); 1169a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 1170a9caca6aSWojciech A. Koszek } 1171a9caca6aSWojciech A. Koszek 1172a9caca6aSWojciech A. Koszek /* Turn off interface. Free up any buffers in transmit or receive queues. */ 1173a9caca6aSWojciech A. Koszek static void 1174a9caca6aSWojciech A. Koszek cgem_stop(struct cgem_softc *sc) 1175a9caca6aSWojciech A. Koszek { 1176a9caca6aSWojciech A. Koszek int i; 1177a9caca6aSWojciech A. Koszek 1178a9caca6aSWojciech A. Koszek CGEM_ASSERT_LOCKED(sc); 1179a9caca6aSWojciech A. Koszek 1180a9caca6aSWojciech A. Koszek callout_stop(&sc->tick_ch); 1181a9caca6aSWojciech A. Koszek 1182a9caca6aSWojciech A. Koszek /* Shut down hardware. */ 1183a9caca6aSWojciech A. Koszek cgem_reset(sc); 1184a9caca6aSWojciech A. Koszek 1185a9caca6aSWojciech A. Koszek /* Clear out transmit queue. */ 1186facdd1cdSThomas Skibo memset(sc->txring, 0, CGEM_NUM_TX_DESCS * sizeof(struct cgem_tx_desc)); 1187a9caca6aSWojciech A. Koszek for (i = 0; i < CGEM_NUM_TX_DESCS; i++) { 1188a9caca6aSWojciech A. Koszek sc->txring[i].ctl = CGEM_TXDESC_USED; 1189a9caca6aSWojciech A. Koszek if (sc->txring_m[i]) { 1190023911aaSIan Lepore /* Unload and destroy dmamap. */ 1191a9caca6aSWojciech A. Koszek bus_dmamap_unload(sc->mbuf_dma_tag, 1192a9caca6aSWojciech A. Koszek sc->txring_m_dmamap[i]); 1193023911aaSIan Lepore bus_dmamap_destroy(sc->mbuf_dma_tag, 1194023911aaSIan Lepore sc->txring_m_dmamap[i]); 1195023911aaSIan Lepore sc->txring_m_dmamap[i] = NULL; 1196a9caca6aSWojciech A. Koszek m_freem(sc->txring_m[i]); 1197a9caca6aSWojciech A. Koszek sc->txring_m[i] = NULL; 1198a9caca6aSWojciech A. Koszek } 1199a9caca6aSWojciech A. Koszek } 1200a9caca6aSWojciech A. Koszek sc->txring[CGEM_NUM_TX_DESCS - 1].ctl |= CGEM_TXDESC_WRAP; 1201a9caca6aSWojciech A. Koszek 1202a9caca6aSWojciech A. Koszek sc->txring_hd_ptr = 0; 1203a9caca6aSWojciech A. Koszek sc->txring_tl_ptr = 0; 1204a9caca6aSWojciech A. Koszek sc->txring_queued = 0; 1205a9caca6aSWojciech A. Koszek 1206a9caca6aSWojciech A. Koszek /* Clear out receive queue. */ 1207facdd1cdSThomas Skibo memset(sc->rxring, 0, CGEM_NUM_RX_DESCS * sizeof(struct cgem_rx_desc)); 1208a9caca6aSWojciech A. Koszek for (i = 0; i < CGEM_NUM_RX_DESCS; i++) { 1209a9caca6aSWojciech A. Koszek sc->rxring[i].addr = CGEM_RXDESC_OWN; 1210a9caca6aSWojciech A. Koszek if (sc->rxring_m[i]) { 1211023911aaSIan Lepore /* Unload and destroy dmamap. */ 1212a9caca6aSWojciech A. Koszek bus_dmamap_unload(sc->mbuf_dma_tag, 1213023911aaSIan Lepore sc->rxring_m_dmamap[i]); 1214023911aaSIan Lepore bus_dmamap_destroy(sc->mbuf_dma_tag, 1215023911aaSIan Lepore sc->rxring_m_dmamap[i]); 1216023911aaSIan Lepore sc->rxring_m_dmamap[i] = NULL; 1217a9caca6aSWojciech A. Koszek 1218a9caca6aSWojciech A. Koszek m_freem(sc->rxring_m[i]); 1219a9caca6aSWojciech A. Koszek sc->rxring_m[i] = NULL; 1220a9caca6aSWojciech A. Koszek } 1221a9caca6aSWojciech A. Koszek } 1222a9caca6aSWojciech A. Koszek sc->rxring[CGEM_NUM_RX_DESCS - 1].addr |= CGEM_RXDESC_WRAP; 1223a9caca6aSWojciech A. Koszek 1224a9caca6aSWojciech A. Koszek sc->rxring_hd_ptr = 0; 1225a9caca6aSWojciech A. Koszek sc->rxring_tl_ptr = 0; 1226a9caca6aSWojciech A. Koszek sc->rxring_queued = 0; 1227df63a5d2SIan Lepore 1228df63a5d2SIan Lepore /* Force next statchg or linkchg to program net config register. */ 1229df63a5d2SIan Lepore sc->mii_media_active = 0; 1230a9caca6aSWojciech A. Koszek } 1231a9caca6aSWojciech A. Koszek 1232a9caca6aSWojciech A. Koszek static int 1233023911aaSIan Lepore cgem_ioctl(if_t ifp, u_long cmd, caddr_t data) 1234a9caca6aSWojciech A. Koszek { 1235023911aaSIan Lepore struct cgem_softc *sc = if_getsoftc(ifp); 1236a9caca6aSWojciech A. Koszek struct ifreq *ifr = (struct ifreq *)data; 1237a9caca6aSWojciech A. Koszek struct mii_data *mii; 1238a9caca6aSWojciech A. Koszek int error = 0, mask; 1239a9caca6aSWojciech A. Koszek 1240a9caca6aSWojciech A. Koszek switch (cmd) { 1241a9caca6aSWojciech A. Koszek case SIOCSIFFLAGS: 1242a9caca6aSWojciech A. Koszek CGEM_LOCK(sc); 1243023911aaSIan Lepore if ((if_getflags(ifp) & IFF_UP) != 0) { 1244023911aaSIan Lepore if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) { 1245023911aaSIan Lepore if (((if_getflags(ifp) ^ sc->if_old_flags) & 1246a9caca6aSWojciech A. Koszek (IFF_PROMISC | IFF_ALLMULTI)) != 0) { 1247a9caca6aSWojciech A. Koszek cgem_rx_filter(sc); 1248a9caca6aSWojciech A. Koszek } 1249a9caca6aSWojciech A. Koszek } else { 1250a9caca6aSWojciech A. Koszek cgem_init_locked(sc); 1251a9caca6aSWojciech A. Koszek } 1252023911aaSIan Lepore } else if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) { 1253023911aaSIan Lepore if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING); 1254a9caca6aSWojciech A. Koszek cgem_stop(sc); 1255a9caca6aSWojciech A. Koszek } 1256023911aaSIan Lepore sc->if_old_flags = if_getflags(ifp); 1257a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 1258a9caca6aSWojciech A. Koszek break; 1259a9caca6aSWojciech A. Koszek 1260a9caca6aSWojciech A. Koszek case SIOCADDMULTI: 1261a9caca6aSWojciech A. Koszek case SIOCDELMULTI: 1262a9caca6aSWojciech A. Koszek /* Set up multi-cast filters. */ 1263023911aaSIan Lepore if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) { 1264a9caca6aSWojciech A. Koszek CGEM_LOCK(sc); 1265a9caca6aSWojciech A. Koszek cgem_rx_filter(sc); 1266a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 1267a9caca6aSWojciech A. Koszek } 1268a9caca6aSWojciech A. Koszek break; 1269a9caca6aSWojciech A. Koszek 1270a9caca6aSWojciech A. Koszek case SIOCSIFMEDIA: 1271a9caca6aSWojciech A. Koszek case SIOCGIFMEDIA: 1272facdd1cdSThomas Skibo if (sc->miibus == NULL) 1273facdd1cdSThomas Skibo return (ENXIO); 1274a9caca6aSWojciech A. Koszek mii = device_get_softc(sc->miibus); 1275a9caca6aSWojciech A. Koszek error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 1276a9caca6aSWojciech A. Koszek break; 1277a9caca6aSWojciech A. Koszek 1278a9caca6aSWojciech A. Koszek case SIOCSIFCAP: 1279a9caca6aSWojciech A. Koszek CGEM_LOCK(sc); 1280023911aaSIan Lepore mask = if_getcapenable(ifp) ^ ifr->ifr_reqcap; 1281a9caca6aSWojciech A. Koszek 1282a9caca6aSWojciech A. Koszek if ((mask & IFCAP_TXCSUM) != 0) { 1283a9caca6aSWojciech A. Koszek if ((ifr->ifr_reqcap & IFCAP_TXCSUM) != 0) { 1284a9caca6aSWojciech A. Koszek /* Turn on TX checksumming. */ 1285023911aaSIan Lepore if_setcapenablebit(ifp, IFCAP_TXCSUM | 1286023911aaSIan Lepore IFCAP_TXCSUM_IPV6, 0); 1287023911aaSIan Lepore if_sethwassistbits(ifp, CGEM_CKSUM_ASSIST, 0); 1288a9caca6aSWojciech A. Koszek 1289a9caca6aSWojciech A. Koszek WR4(sc, CGEM_DMA_CFG, 1290a9caca6aSWojciech A. Koszek RD4(sc, CGEM_DMA_CFG) | 1291a9caca6aSWojciech A. Koszek CGEM_DMA_CFG_CHKSUM_GEN_OFFLOAD_EN); 1292a9caca6aSWojciech A. Koszek } else { 1293a9caca6aSWojciech A. Koszek /* Turn off TX checksumming. */ 1294023911aaSIan Lepore if_setcapenablebit(ifp, 0, IFCAP_TXCSUM | 1295a9caca6aSWojciech A. Koszek IFCAP_TXCSUM_IPV6); 1296023911aaSIan Lepore if_sethwassistbits(ifp, 0, CGEM_CKSUM_ASSIST); 1297a9caca6aSWojciech A. Koszek 1298a9caca6aSWojciech A. Koszek WR4(sc, CGEM_DMA_CFG, 1299a9caca6aSWojciech A. Koszek RD4(sc, CGEM_DMA_CFG) & 1300a9caca6aSWojciech A. Koszek ~CGEM_DMA_CFG_CHKSUM_GEN_OFFLOAD_EN); 1301a9caca6aSWojciech A. Koszek } 1302a9caca6aSWojciech A. Koszek } 1303a9caca6aSWojciech A. Koszek if ((mask & IFCAP_RXCSUM) != 0) { 1304a9caca6aSWojciech A. Koszek if ((ifr->ifr_reqcap & IFCAP_RXCSUM) != 0) { 1305a9caca6aSWojciech A. Koszek /* Turn on RX checksumming. */ 1306023911aaSIan Lepore if_setcapenablebit(ifp, IFCAP_RXCSUM | 1307023911aaSIan Lepore IFCAP_RXCSUM_IPV6, 0); 1308facdd1cdSThomas Skibo sc->net_cfg_shadow |= 1309facdd1cdSThomas Skibo CGEM_NET_CFG_RX_CHKSUM_OFFLD_EN; 1310facdd1cdSThomas Skibo WR4(sc, CGEM_NET_CFG, sc->net_cfg_shadow); 1311a9caca6aSWojciech A. Koszek } else { 1312a9caca6aSWojciech A. Koszek /* Turn off RX checksumming. */ 1313023911aaSIan Lepore if_setcapenablebit(ifp, 0, IFCAP_RXCSUM | 1314a9caca6aSWojciech A. Koszek IFCAP_RXCSUM_IPV6); 1315facdd1cdSThomas Skibo sc->net_cfg_shadow &= 1316facdd1cdSThomas Skibo ~CGEM_NET_CFG_RX_CHKSUM_OFFLD_EN; 1317facdd1cdSThomas Skibo WR4(sc, CGEM_NET_CFG, sc->net_cfg_shadow); 1318a9caca6aSWojciech A. Koszek } 1319a9caca6aSWojciech A. Koszek } 1320023911aaSIan Lepore if ((if_getcapenable(ifp) & (IFCAP_RXCSUM | IFCAP_TXCSUM)) == 1321df63a5d2SIan Lepore (IFCAP_RXCSUM | IFCAP_TXCSUM)) 1322023911aaSIan Lepore if_setcapenablebit(ifp, IFCAP_VLAN_HWCSUM, 0); 1323df63a5d2SIan Lepore else 1324023911aaSIan Lepore if_setcapenablebit(ifp, 0, IFCAP_VLAN_HWCSUM); 1325a9caca6aSWojciech A. Koszek 1326a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 1327a9caca6aSWojciech A. Koszek break; 1328a9caca6aSWojciech A. Koszek default: 1329a9caca6aSWojciech A. Koszek error = ether_ioctl(ifp, cmd, data); 1330a9caca6aSWojciech A. Koszek break; 1331a9caca6aSWojciech A. Koszek } 1332a9caca6aSWojciech A. Koszek 1333a9caca6aSWojciech A. Koszek return (error); 1334a9caca6aSWojciech A. Koszek } 1335a9caca6aSWojciech A. Koszek 1336a9caca6aSWojciech A. Koszek /* MII bus support routines. 1337a9caca6aSWojciech A. Koszek */ 1338a9caca6aSWojciech A. Koszek static int 1339023911aaSIan Lepore cgem_ifmedia_upd(if_t ifp) 1340a9caca6aSWojciech A. Koszek { 1341023911aaSIan Lepore struct cgem_softc *sc = (struct cgem_softc *) if_getsoftc(ifp); 1342a9caca6aSWojciech A. Koszek struct mii_data *mii; 1343df63a5d2SIan Lepore struct mii_softc *miisc; 1344df63a5d2SIan Lepore int error = 0; 1345a9caca6aSWojciech A. Koszek 1346a9caca6aSWojciech A. Koszek mii = device_get_softc(sc->miibus); 1347a9caca6aSWojciech A. Koszek CGEM_LOCK(sc); 1348023911aaSIan Lepore if ((if_getflags(ifp) & IFF_UP) != 0) { 1349df63a5d2SIan Lepore LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 1350df63a5d2SIan Lepore PHY_RESET(miisc); 13510f822edeSIan Lepore error = mii_mediachg(mii); 1352df63a5d2SIan Lepore } 1353a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 1354df63a5d2SIan Lepore 13550f822edeSIan Lepore return (error); 1356a9caca6aSWojciech A. Koszek } 1357a9caca6aSWojciech A. Koszek 1358a9caca6aSWojciech A. Koszek static void 1359023911aaSIan Lepore cgem_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr) 1360a9caca6aSWojciech A. Koszek { 1361023911aaSIan Lepore struct cgem_softc *sc = (struct cgem_softc *) if_getsoftc(ifp); 1362a9caca6aSWojciech A. Koszek struct mii_data *mii; 1363a9caca6aSWojciech A. Koszek 1364a9caca6aSWojciech A. Koszek mii = device_get_softc(sc->miibus); 1365a9caca6aSWojciech A. Koszek CGEM_LOCK(sc); 1366a9caca6aSWojciech A. Koszek mii_pollstat(mii); 1367a9caca6aSWojciech A. Koszek ifmr->ifm_active = mii->mii_media_active; 1368a9caca6aSWojciech A. Koszek ifmr->ifm_status = mii->mii_media_status; 1369a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 1370a9caca6aSWojciech A. Koszek } 1371a9caca6aSWojciech A. Koszek 1372a9caca6aSWojciech A. Koszek static int 1373a9caca6aSWojciech A. Koszek cgem_miibus_readreg(device_t dev, int phy, int reg) 1374a9caca6aSWojciech A. Koszek { 1375a9caca6aSWojciech A. Koszek struct cgem_softc *sc = device_get_softc(dev); 1376a9caca6aSWojciech A. Koszek int tries, val; 1377a9caca6aSWojciech A. Koszek 1378f674004fSIan Lepore WR4(sc, CGEM_PHY_MAINT, CGEM_PHY_MAINT_CLAUSE_22 | 1379f674004fSIan Lepore CGEM_PHY_MAINT_MUST_10 | CGEM_PHY_MAINT_OP_READ | 1380a9caca6aSWojciech A. Koszek (phy << CGEM_PHY_MAINT_PHY_ADDR_SHIFT) | 1381a9caca6aSWojciech A. Koszek (reg << CGEM_PHY_MAINT_REG_ADDR_SHIFT)); 1382a9caca6aSWojciech A. Koszek 1383a9caca6aSWojciech A. Koszek /* Wait for completion. */ 1384a9caca6aSWojciech A. Koszek tries=0; 1385a9caca6aSWojciech A. Koszek while ((RD4(sc, CGEM_NET_STAT) & CGEM_NET_STAT_PHY_MGMT_IDLE) == 0) { 1386a9caca6aSWojciech A. Koszek DELAY(5); 1387a9caca6aSWojciech A. Koszek if (++tries > 200) { 1388a9caca6aSWojciech A. Koszek device_printf(dev, "phy read timeout: %d\n", reg); 1389a9caca6aSWojciech A. Koszek return (-1); 1390a9caca6aSWojciech A. Koszek } 1391a9caca6aSWojciech A. Koszek } 1392a9caca6aSWojciech A. Koszek 1393a9caca6aSWojciech A. Koszek val = RD4(sc, CGEM_PHY_MAINT) & CGEM_PHY_MAINT_DATA_MASK; 1394a9caca6aSWojciech A. Koszek 1395df63a5d2SIan Lepore if (reg == MII_EXTSR) 1396df63a5d2SIan Lepore /* 1397df63a5d2SIan Lepore * MAC does not support half-duplex at gig speeds. 1398df63a5d2SIan Lepore * Let mii(4) exclude the capability. 1399df63a5d2SIan Lepore */ 1400df63a5d2SIan Lepore val &= ~(EXTSR_1000XHDX | EXTSR_1000THDX); 1401df63a5d2SIan Lepore 1402a9caca6aSWojciech A. Koszek return (val); 1403a9caca6aSWojciech A. Koszek } 1404a9caca6aSWojciech A. Koszek 1405a9caca6aSWojciech A. Koszek static int 1406a9caca6aSWojciech A. Koszek cgem_miibus_writereg(device_t dev, int phy, int reg, int data) 1407a9caca6aSWojciech A. Koszek { 1408a9caca6aSWojciech A. Koszek struct cgem_softc *sc = device_get_softc(dev); 1409a9caca6aSWojciech A. Koszek int tries; 1410a9caca6aSWojciech A. Koszek 1411f674004fSIan Lepore WR4(sc, CGEM_PHY_MAINT, CGEM_PHY_MAINT_CLAUSE_22 | 1412f674004fSIan Lepore CGEM_PHY_MAINT_MUST_10 | CGEM_PHY_MAINT_OP_WRITE | 1413a9caca6aSWojciech A. Koszek (phy << CGEM_PHY_MAINT_PHY_ADDR_SHIFT) | 1414a9caca6aSWojciech A. Koszek (reg << CGEM_PHY_MAINT_REG_ADDR_SHIFT) | 1415a9caca6aSWojciech A. Koszek (data & CGEM_PHY_MAINT_DATA_MASK)); 1416a9caca6aSWojciech A. Koszek 1417a9caca6aSWojciech A. Koszek /* Wait for completion. */ 1418a9caca6aSWojciech A. Koszek tries = 0; 1419a9caca6aSWojciech A. Koszek while ((RD4(sc, CGEM_NET_STAT) & CGEM_NET_STAT_PHY_MGMT_IDLE) == 0) { 1420a9caca6aSWojciech A. Koszek DELAY(5); 1421a9caca6aSWojciech A. Koszek if (++tries > 200) { 1422a9caca6aSWojciech A. Koszek device_printf(dev, "phy write timeout: %d\n", reg); 1423a9caca6aSWojciech A. Koszek return (-1); 1424a9caca6aSWojciech A. Koszek } 1425a9caca6aSWojciech A. Koszek } 1426a9caca6aSWojciech A. Koszek 1427a9caca6aSWojciech A. Koszek return (0); 1428a9caca6aSWojciech A. Koszek } 1429a9caca6aSWojciech A. Koszek 1430df63a5d2SIan Lepore static void 1431df63a5d2SIan Lepore cgem_miibus_statchg(device_t dev) 1432df63a5d2SIan Lepore { 1433df63a5d2SIan Lepore struct cgem_softc *sc = device_get_softc(dev); 1434df63a5d2SIan Lepore struct mii_data *mii = device_get_softc(sc->miibus); 1435df63a5d2SIan Lepore 1436df63a5d2SIan Lepore CGEM_ASSERT_LOCKED(sc); 1437df63a5d2SIan Lepore 1438df63a5d2SIan Lepore if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 1439df63a5d2SIan Lepore (IFM_ACTIVE | IFM_AVALID) && 1440df63a5d2SIan Lepore sc->mii_media_active != mii->mii_media_active) 1441df63a5d2SIan Lepore cgem_mediachange(sc, mii); 1442df63a5d2SIan Lepore } 1443df63a5d2SIan Lepore 1444df63a5d2SIan Lepore static void 1445df63a5d2SIan Lepore cgem_miibus_linkchg(device_t dev) 1446df63a5d2SIan Lepore { 1447df63a5d2SIan Lepore struct cgem_softc *sc = device_get_softc(dev); 1448df63a5d2SIan Lepore struct mii_data *mii = device_get_softc(sc->miibus); 1449df63a5d2SIan Lepore 1450df63a5d2SIan Lepore CGEM_ASSERT_LOCKED(sc); 1451df63a5d2SIan Lepore 1452df63a5d2SIan Lepore if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 1453df63a5d2SIan Lepore (IFM_ACTIVE | IFM_AVALID) && 1454df63a5d2SIan Lepore sc->mii_media_active != mii->mii_media_active) 1455df63a5d2SIan Lepore cgem_mediachange(sc, mii); 1456df63a5d2SIan Lepore } 1457df63a5d2SIan Lepore 14580f822edeSIan Lepore /* 14590f822edeSIan Lepore * Overridable weak symbol cgem_set_ref_clk(). This allows platforms to 14600f822edeSIan Lepore * provide a function to set the cgem's reference clock. 14610f822edeSIan Lepore */ 14620f822edeSIan Lepore static int __used 14630f822edeSIan Lepore cgem_default_set_ref_clk(int unit, int frequency) 14640f822edeSIan Lepore { 14650f822edeSIan Lepore 14660f822edeSIan Lepore return 0; 14670f822edeSIan Lepore } 14680f822edeSIan Lepore __weak_reference(cgem_default_set_ref_clk, cgem_set_ref_clk); 14690f822edeSIan Lepore 1470df63a5d2SIan Lepore /* Call to set reference clock and network config bits according to media. */ 14710f822edeSIan Lepore static void 1472df63a5d2SIan Lepore cgem_mediachange(struct cgem_softc *sc, struct mii_data *mii) 14730f822edeSIan Lepore { 14740f822edeSIan Lepore int ref_clk_freq; 14750f822edeSIan Lepore 1476df63a5d2SIan Lepore CGEM_ASSERT_LOCKED(sc); 14770f822edeSIan Lepore 1478df63a5d2SIan Lepore /* Update hardware to reflect media. */ 1479facdd1cdSThomas Skibo sc->net_cfg_shadow &= ~(CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN | 14800f822edeSIan Lepore CGEM_NET_CFG_FULL_DUPLEX); 14810f822edeSIan Lepore 14820f822edeSIan Lepore switch (IFM_SUBTYPE(mii->mii_media_active)) { 14830f822edeSIan Lepore case IFM_1000_T: 1484facdd1cdSThomas Skibo sc->net_cfg_shadow |= (CGEM_NET_CFG_SPEED100 | 14850f822edeSIan Lepore CGEM_NET_CFG_GIGE_EN); 14860f822edeSIan Lepore ref_clk_freq = 125000000; 14870f822edeSIan Lepore break; 14880f822edeSIan Lepore case IFM_100_TX: 1489facdd1cdSThomas Skibo sc->net_cfg_shadow |= CGEM_NET_CFG_SPEED100; 14900f822edeSIan Lepore ref_clk_freq = 25000000; 14910f822edeSIan Lepore break; 14920f822edeSIan Lepore default: 14930f822edeSIan Lepore ref_clk_freq = 2500000; 14940f822edeSIan Lepore } 14950f822edeSIan Lepore 14960f822edeSIan Lepore if ((mii->mii_media_active & IFM_FDX) != 0) 1497facdd1cdSThomas Skibo sc->net_cfg_shadow |= CGEM_NET_CFG_FULL_DUPLEX; 1498df63a5d2SIan Lepore 1499facdd1cdSThomas Skibo WR4(sc, CGEM_NET_CFG, sc->net_cfg_shadow); 15000f822edeSIan Lepore 15014c52dde5SEmmanuel Vadot if (sc->clk_pclk != NULL) { 1502facdd1cdSThomas Skibo CGEM_UNLOCK(sc); 15034c52dde5SEmmanuel Vadot if (clk_set_freq(sc->clk_pclk, ref_clk_freq, 0)) 1504facdd1cdSThomas Skibo device_printf(sc->dev, "could not set ref clk to %d\n", 1505facdd1cdSThomas Skibo ref_clk_freq); 1506facdd1cdSThomas Skibo CGEM_LOCK(sc); 1507facdd1cdSThomas Skibo } 1508df63a5d2SIan Lepore 1509df63a5d2SIan Lepore sc->mii_media_active = mii->mii_media_active; 15100f822edeSIan Lepore } 1511df63a5d2SIan Lepore 1512df63a5d2SIan Lepore static void 1513df63a5d2SIan Lepore cgem_add_sysctls(device_t dev) 1514df63a5d2SIan Lepore { 1515df63a5d2SIan Lepore struct cgem_softc *sc = device_get_softc(dev); 1516df63a5d2SIan Lepore struct sysctl_ctx_list *ctx; 1517df63a5d2SIan Lepore struct sysctl_oid_list *child; 1518df63a5d2SIan Lepore struct sysctl_oid *tree; 1519df63a5d2SIan Lepore 1520df63a5d2SIan Lepore ctx = device_get_sysctl_ctx(dev); 1521df63a5d2SIan Lepore child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 1522df63a5d2SIan Lepore 1523df63a5d2SIan Lepore SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rxbufs", CTLFLAG_RW, 1524f674004fSIan Lepore &sc->rxbufs, 0, "Number receive buffers to provide"); 1525df63a5d2SIan Lepore 1526df63a5d2SIan Lepore SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rxhangwar", CTLFLAG_RW, 1527f674004fSIan Lepore &sc->rxhangwar, 0, "Enable receive hang work-around"); 1528df63a5d2SIan Lepore 1529df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_rxoverruns", CTLFLAG_RD, 1530f674004fSIan Lepore &sc->rxoverruns, 0, "Receive overrun events"); 1531df63a5d2SIan Lepore 1532df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_rxnobufs", CTLFLAG_RD, 1533f674004fSIan Lepore &sc->rxnobufs, 0, "Receive buf queue empty events"); 1534df63a5d2SIan Lepore 1535df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_rxdmamapfails", CTLFLAG_RD, 1536f674004fSIan Lepore &sc->rxdmamapfails, 0, "Receive DMA map failures"); 1537df63a5d2SIan Lepore 1538df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_txfull", CTLFLAG_RD, 1539f674004fSIan Lepore &sc->txfull, 0, "Transmit ring full events"); 1540df63a5d2SIan Lepore 1541df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_txdmamapfails", CTLFLAG_RD, 1542f674004fSIan Lepore &sc->txdmamapfails, 0, "Transmit DMA map failures"); 1543df63a5d2SIan Lepore 1544df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_txdefrags", CTLFLAG_RD, 1545f674004fSIan Lepore &sc->txdefrags, 0, "Transmit m_defrag() calls"); 1546df63a5d2SIan Lepore 1547df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_txdefragfails", CTLFLAG_RD, 1548f674004fSIan Lepore &sc->txdefragfails, 0, "Transmit m_defrag() failures"); 1549df63a5d2SIan Lepore 15507029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", 15517029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "GEM statistics"); 1552df63a5d2SIan Lepore child = SYSCTL_CHILDREN(tree); 1553df63a5d2SIan Lepore 1554df63a5d2SIan Lepore SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_bytes", CTLFLAG_RD, 1555df63a5d2SIan Lepore &sc->stats.tx_bytes, "Total bytes transmitted"); 1556df63a5d2SIan Lepore 1557df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames", CTLFLAG_RD, 1558df63a5d2SIan Lepore &sc->stats.tx_frames, 0, "Total frames transmitted"); 1559f674004fSIan Lepore 1560df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_bcast", CTLFLAG_RD, 1561df63a5d2SIan Lepore &sc->stats.tx_frames_bcast, 0, 1562df63a5d2SIan Lepore "Number broadcast frames transmitted"); 1563f674004fSIan Lepore 1564df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_multi", CTLFLAG_RD, 1565df63a5d2SIan Lepore &sc->stats.tx_frames_multi, 0, 1566df63a5d2SIan Lepore "Number multicast frames transmitted"); 1567f674004fSIan Lepore 1568df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_pause", 1569df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_frames_pause, 0, 1570df63a5d2SIan Lepore "Number pause frames transmitted"); 1571f674004fSIan Lepore 1572df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_64b", CTLFLAG_RD, 1573df63a5d2SIan Lepore &sc->stats.tx_frames_64b, 0, 1574df63a5d2SIan Lepore "Number frames transmitted of size 64 bytes or less"); 1575f674004fSIan Lepore 1576df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_65to127b", CTLFLAG_RD, 1577df63a5d2SIan Lepore &sc->stats.tx_frames_65to127b, 0, 1578df63a5d2SIan Lepore "Number frames transmitted of size 65-127 bytes"); 1579f674004fSIan Lepore 1580df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_128to255b", 1581df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_frames_128to255b, 0, 1582df63a5d2SIan Lepore "Number frames transmitted of size 128-255 bytes"); 1583f674004fSIan Lepore 1584df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_256to511b", 1585df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_frames_256to511b, 0, 1586df63a5d2SIan Lepore "Number frames transmitted of size 256-511 bytes"); 1587f674004fSIan Lepore 1588df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_512to1023b", 1589df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_frames_512to1023b, 0, 1590df63a5d2SIan Lepore "Number frames transmitted of size 512-1023 bytes"); 1591f674004fSIan Lepore 1592df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_1024to1536b", 1593df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_frames_1024to1536b, 0, 1594df63a5d2SIan Lepore "Number frames transmitted of size 1024-1536 bytes"); 1595f674004fSIan Lepore 1596df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_under_runs", 1597df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_under_runs, 0, 1598df63a5d2SIan Lepore "Number transmit under-run events"); 1599f674004fSIan Lepore 1600df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_single_collisn", 1601df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_single_collisn, 0, 1602df63a5d2SIan Lepore "Number single-collision transmit frames"); 1603f674004fSIan Lepore 1604df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_multi_collisn", 1605df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_multi_collisn, 0, 1606df63a5d2SIan Lepore "Number multi-collision transmit frames"); 1607f674004fSIan Lepore 1608df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_excsv_collisn", 1609df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_excsv_collisn, 0, 1610df63a5d2SIan Lepore "Number excessive collision transmit frames"); 1611f674004fSIan Lepore 1612df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_late_collisn", 1613df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_late_collisn, 0, 1614df63a5d2SIan Lepore "Number late-collision transmit frames"); 1615f674004fSIan Lepore 1616df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_deferred_frames", 1617df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_deferred_frames, 0, 1618df63a5d2SIan Lepore "Number deferred transmit frames"); 1619f674004fSIan Lepore 1620df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_carrier_sense_errs", 1621df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.tx_carrier_sense_errs, 0, 1622df63a5d2SIan Lepore "Number carrier sense errors on transmit"); 1623df63a5d2SIan Lepore 1624df63a5d2SIan Lepore SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "rx_bytes", CTLFLAG_RD, 1625df63a5d2SIan Lepore &sc->stats.rx_bytes, "Total bytes received"); 1626df63a5d2SIan Lepore 1627df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames", CTLFLAG_RD, 1628df63a5d2SIan Lepore &sc->stats.rx_frames, 0, "Total frames received"); 1629f674004fSIan Lepore 1630df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_bcast", 1631df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_bcast, 0, 1632df63a5d2SIan Lepore "Number broadcast frames received"); 1633f674004fSIan Lepore 1634df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_multi", 1635df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_multi, 0, 1636df63a5d2SIan Lepore "Number multicast frames received"); 1637f674004fSIan Lepore 1638df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_pause", 1639df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_pause, 0, 1640df63a5d2SIan Lepore "Number pause frames received"); 1641f674004fSIan Lepore 1642df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_64b", 1643df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_64b, 0, 1644df63a5d2SIan Lepore "Number frames received of size 64 bytes or less"); 1645f674004fSIan Lepore 1646df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_65to127b", 1647df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_65to127b, 0, 1648df63a5d2SIan Lepore "Number frames received of size 65-127 bytes"); 1649f674004fSIan Lepore 1650df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_128to255b", 1651df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_128to255b, 0, 1652df63a5d2SIan Lepore "Number frames received of size 128-255 bytes"); 1653f674004fSIan Lepore 1654df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_256to511b", 1655df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_256to511b, 0, 1656df63a5d2SIan Lepore "Number frames received of size 256-511 bytes"); 1657f674004fSIan Lepore 1658df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_512to1023b", 1659df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_512to1023b, 0, 1660df63a5d2SIan Lepore "Number frames received of size 512-1023 bytes"); 1661f674004fSIan Lepore 1662df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_1024to1536b", 1663df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_1024to1536b, 0, 1664df63a5d2SIan Lepore "Number frames received of size 1024-1536 bytes"); 1665f674004fSIan Lepore 1666df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_undersize", 1667df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_undersize, 0, 1668df63a5d2SIan Lepore "Number undersize frames received"); 1669f674004fSIan Lepore 1670df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_oversize", 1671df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_oversize, 0, 1672df63a5d2SIan Lepore "Number oversize frames received"); 1673f674004fSIan Lepore 1674df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_jabber", 1675df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_jabber, 0, 1676df63a5d2SIan Lepore "Number jabber frames received"); 1677f674004fSIan Lepore 1678df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_fcs_errs", 1679df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_fcs_errs, 0, 1680df63a5d2SIan Lepore "Number frames received with FCS errors"); 1681f674004fSIan Lepore 1682df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_length_errs", 1683df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_frames_length_errs, 0, 1684df63a5d2SIan Lepore "Number frames received with length errors"); 1685f674004fSIan Lepore 1686df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_symbol_errs", 1687df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_symbol_errs, 0, 1688df63a5d2SIan Lepore "Number receive symbol errors"); 1689f674004fSIan Lepore 1690df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_align_errs", 1691df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_align_errs, 0, 1692df63a5d2SIan Lepore "Number receive alignment errors"); 1693f674004fSIan Lepore 1694df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_resource_errs", 1695df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_resource_errs, 0, 1696df63a5d2SIan Lepore "Number frames received when no rx buffer available"); 1697f674004fSIan Lepore 1698df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overrun_errs", 1699df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_overrun_errs, 0, 1700f674004fSIan Lepore "Number frames received but not copied due to receive overrun"); 1701f674004fSIan Lepore 1702df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_ip_hdr_csum_errs", 1703df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_ip_hdr_csum_errs, 0, 1704f674004fSIan Lepore "Number frames received with IP header checksum errors"); 1705f674004fSIan Lepore 1706df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_tcp_csum_errs", 1707df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_tcp_csum_errs, 0, 1708df63a5d2SIan Lepore "Number frames received with TCP checksum errors"); 1709f674004fSIan Lepore 1710df63a5d2SIan Lepore SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_frames_udp_csum_errs", 1711df63a5d2SIan Lepore CTLFLAG_RD, &sc->stats.rx_udp_csum_errs, 0, 1712df63a5d2SIan Lepore "Number frames received with UDP checksum errors"); 17130f822edeSIan Lepore } 1714a9caca6aSWojciech A. Koszek 1715a9caca6aSWojciech A. Koszek static int 1716a9caca6aSWojciech A. Koszek cgem_probe(device_t dev) 1717a9caca6aSWojciech A. Koszek { 1718a9caca6aSWojciech A. Koszek 1719349ef43dSOleksandr Tymoshenko if (!ofw_bus_status_okay(dev)) 1720349ef43dSOleksandr Tymoshenko return (ENXIO); 1721349ef43dSOleksandr Tymoshenko 172266cc0c61SMilan Obuch if (ofw_bus_search_compatible(dev, compat_data)->ocd_str == NULL) 1723a9caca6aSWojciech A. Koszek return (ENXIO); 1724a9caca6aSWojciech A. Koszek 1725a9caca6aSWojciech A. Koszek device_set_desc(dev, "Cadence CGEM Gigabit Ethernet Interface"); 1726a9caca6aSWojciech A. Koszek return (0); 1727a9caca6aSWojciech A. Koszek } 1728a9caca6aSWojciech A. Koszek 1729a9caca6aSWojciech A. Koszek static int 1730a9caca6aSWojciech A. Koszek cgem_attach(device_t dev) 1731a9caca6aSWojciech A. Koszek { 1732a9caca6aSWojciech A. Koszek struct cgem_softc *sc = device_get_softc(dev); 1733023911aaSIan Lepore if_t ifp = NULL; 1734a9caca6aSWojciech A. Koszek int rid, err; 1735a9caca6aSWojciech A. Koszek u_char eaddr[ETHER_ADDR_LEN]; 173666cc0c61SMilan Obuch int hwquirks; 173734f4c17aSMilan Obuch phandle_t node; 1738a9caca6aSWojciech A. Koszek 1739a9caca6aSWojciech A. Koszek sc->dev = dev; 1740a9caca6aSWojciech A. Koszek CGEM_LOCK_INIT(sc); 1741a9caca6aSWojciech A. Koszek 1742facdd1cdSThomas Skibo /* Key off of compatible string and set hardware-specific options. */ 174366cc0c61SMilan Obuch hwquirks = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 174466cc0c61SMilan Obuch if ((hwquirks & HWQUIRK_NEEDNULLQS) != 0) 1745facdd1cdSThomas Skibo sc->neednullqs = 1; 174666cc0c61SMilan Obuch if ((hwquirks & HWQUIRK_RXHANGWAR) != 0) 1747facdd1cdSThomas Skibo sc->rxhangwar = 1; 17484c52dde5SEmmanuel Vadot /* 17494c52dde5SEmmanuel Vadot * Both pclk and hclk are mandatory but we don't have a proper 17504c52dde5SEmmanuel Vadot * clock driver for Zynq so don't make it fatal if we can't 17514c52dde5SEmmanuel Vadot * get them. 17524c52dde5SEmmanuel Vadot */ 17534c52dde5SEmmanuel Vadot if (clk_get_by_ofw_name(dev, 0, "pclk", &sc->clk_pclk) != 0) 1754facdd1cdSThomas Skibo device_printf(dev, 17554c52dde5SEmmanuel Vadot "could not retrieve pclk.\n"); 17564c52dde5SEmmanuel Vadot else { 17574c52dde5SEmmanuel Vadot if (clk_enable(sc->clk_pclk) != 0) 17584c52dde5SEmmanuel Vadot device_printf(dev, "could not enable pclk.\n"); 175966cc0c61SMilan Obuch } 17604c52dde5SEmmanuel Vadot if (clk_get_by_ofw_name(dev, 0, "hclk", &sc->clk_hclk) != 0) 1761facdd1cdSThomas Skibo device_printf(dev, 17624c52dde5SEmmanuel Vadot "could not retrieve hclk.\n"); 17634c52dde5SEmmanuel Vadot else { 17644c52dde5SEmmanuel Vadot if (clk_enable(sc->clk_hclk) != 0) 17654c52dde5SEmmanuel Vadot device_printf(dev, "could not enable hclk.\n"); 17664c52dde5SEmmanuel Vadot } 17674c52dde5SEmmanuel Vadot 17684c52dde5SEmmanuel Vadot /* Optional clocks */ 17694c52dde5SEmmanuel Vadot if (clk_get_by_ofw_name(dev, 0, "tx_clk", &sc->clk_txclk) == 0) { 17704c52dde5SEmmanuel Vadot if (clk_enable(sc->clk_txclk) != 0) { 17714c52dde5SEmmanuel Vadot device_printf(dev, "could not enable tx_clk.\n"); 17724c52dde5SEmmanuel Vadot err = ENXIO; 17734c52dde5SEmmanuel Vadot goto err_pclk; 17744c52dde5SEmmanuel Vadot } 17754c52dde5SEmmanuel Vadot } 17764c52dde5SEmmanuel Vadot if (clk_get_by_ofw_name(dev, 0, "rx_clk", &sc->clk_rxclk) == 0) { 17774c52dde5SEmmanuel Vadot if (clk_enable(sc->clk_rxclk) != 0) { 17784c52dde5SEmmanuel Vadot device_printf(dev, "could not enable rx_clk.\n"); 17794c52dde5SEmmanuel Vadot err = ENXIO; 17804c52dde5SEmmanuel Vadot goto err_tx_clk; 17814c52dde5SEmmanuel Vadot } 17824c52dde5SEmmanuel Vadot } 17834c52dde5SEmmanuel Vadot if (clk_get_by_ofw_name(dev, 0, "tsu_clk", &sc->clk_tsuclk) == 0) { 17844c52dde5SEmmanuel Vadot if (clk_enable(sc->clk_tsuclk) != 0) { 17854c52dde5SEmmanuel Vadot device_printf(dev, "could not enable tsu_clk.\n"); 17864c52dde5SEmmanuel Vadot err = ENXIO; 17874c52dde5SEmmanuel Vadot goto err_rx_clk; 17884c52dde5SEmmanuel Vadot } 1789facdd1cdSThomas Skibo } 17900f822edeSIan Lepore 179134f4c17aSMilan Obuch node = ofw_bus_get_node(dev); 179234f4c17aSMilan Obuch sc->phy_contype = mii_fdt_get_contype(node); 179334f4c17aSMilan Obuch 1794a9caca6aSWojciech A. Koszek /* Get memory resource. */ 1795a9caca6aSWojciech A. Koszek rid = 0; 1796a9caca6aSWojciech A. Koszek sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1797a9caca6aSWojciech A. Koszek RF_ACTIVE); 1798a9caca6aSWojciech A. Koszek if (sc->mem_res == NULL) { 1799a9caca6aSWojciech A. Koszek device_printf(dev, "could not allocate memory resources.\n"); 18004c52dde5SEmmanuel Vadot err = ENOMEM; 18014c52dde5SEmmanuel Vadot goto err_tsu_clk; 1802a9caca6aSWojciech A. Koszek } 1803a9caca6aSWojciech A. Koszek 1804a9caca6aSWojciech A. Koszek /* Get IRQ resource. */ 1805a9caca6aSWojciech A. Koszek rid = 0; 1806facdd1cdSThomas Skibo sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 1807facdd1cdSThomas Skibo RF_ACTIVE); 1808a9caca6aSWojciech A. Koszek if (sc->irq_res == NULL) { 1809a9caca6aSWojciech A. Koszek device_printf(dev, "could not allocate interrupt resource.\n"); 1810a9caca6aSWojciech A. Koszek cgem_detach(dev); 1811a9caca6aSWojciech A. Koszek return (ENOMEM); 1812a9caca6aSWojciech A. Koszek } 1813a9caca6aSWojciech A. Koszek 1814df63a5d2SIan Lepore /* Set up ifnet structure. */ 1815a9caca6aSWojciech A. Koszek ifp = sc->ifp = if_alloc(IFT_ETHER); 1816023911aaSIan Lepore if_setsoftc(ifp, sc); 1817df63a5d2SIan Lepore if_initname(ifp, IF_CGEM_NAME, device_get_unit(dev)); 1818023911aaSIan Lepore if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 1819023911aaSIan Lepore if_setinitfn(ifp, cgem_init); 1820023911aaSIan Lepore if_setioctlfn(ifp, cgem_ioctl); 1821023911aaSIan Lepore if_setstartfn(ifp, cgem_start); 1822023911aaSIan Lepore if_setcapabilitiesbit(ifp, IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6 | 1823023911aaSIan Lepore IFCAP_VLAN_MTU | IFCAP_VLAN_HWCSUM, 0); 1824023911aaSIan Lepore if_setsendqlen(ifp, CGEM_NUM_TX_DESCS); 1825023911aaSIan Lepore if_setsendqready(ifp); 1826a9caca6aSWojciech A. Koszek 1827023911aaSIan Lepore /* Disable hardware checksumming by default. */ 1828023911aaSIan Lepore if_sethwassist(ifp, 0); 1829023911aaSIan Lepore if_setcapenable(ifp, if_getcapabilities(ifp) & 1830023911aaSIan Lepore ~(IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6 | IFCAP_VLAN_HWCSUM)); 1831023911aaSIan Lepore 1832023911aaSIan Lepore sc->if_old_flags = if_getflags(ifp); 1833df63a5d2SIan Lepore sc->rxbufs = DEFAULT_NUM_RX_BUFS; 1834a9caca6aSWojciech A. Koszek 1835a9caca6aSWojciech A. Koszek /* Reset hardware. */ 1836df63a5d2SIan Lepore CGEM_LOCK(sc); 1837a9caca6aSWojciech A. Koszek cgem_reset(sc); 1838df63a5d2SIan Lepore CGEM_UNLOCK(sc); 1839a9caca6aSWojciech A. Koszek 1840a9caca6aSWojciech A. Koszek /* Attach phy to mii bus. */ 1841a9caca6aSWojciech A. Koszek err = mii_attach(dev, &sc->miibus, ifp, 1842f674004fSIan Lepore cgem_ifmedia_upd, cgem_ifmedia_sts, BMSR_DEFCAPMASK, 1843f674004fSIan Lepore MII_PHY_ANY, MII_OFFSET_ANY, 0); 1844facdd1cdSThomas Skibo if (err) 1845facdd1cdSThomas Skibo device_printf(dev, "warning: attaching PHYs failed\n"); 1846a9caca6aSWojciech A. Koszek 1847a9caca6aSWojciech A. Koszek /* Set up TX and RX descriptor area. */ 1848a9caca6aSWojciech A. Koszek err = cgem_setup_descs(sc); 1849a9caca6aSWojciech A. Koszek if (err) { 1850a9caca6aSWojciech A. Koszek device_printf(dev, "could not set up dma mem for descs.\n"); 1851a9caca6aSWojciech A. Koszek cgem_detach(dev); 18524c52dde5SEmmanuel Vadot goto err; 1853a9caca6aSWojciech A. Koszek } 1854a9caca6aSWojciech A. Koszek 1855a9caca6aSWojciech A. Koszek /* Get a MAC address. */ 1856a9caca6aSWojciech A. Koszek cgem_get_mac(sc, eaddr); 1857a9caca6aSWojciech A. Koszek 1858a9caca6aSWojciech A. Koszek /* Start ticks. */ 1859a9caca6aSWojciech A. Koszek callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0); 1860a9caca6aSWojciech A. Koszek 1861a9caca6aSWojciech A. Koszek ether_ifattach(ifp, eaddr); 1862a9caca6aSWojciech A. Koszek 1863a9caca6aSWojciech A. Koszek err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE | 1864a9caca6aSWojciech A. Koszek INTR_EXCL, NULL, cgem_intr, sc, &sc->intrhand); 1865a9caca6aSWojciech A. Koszek if (err) { 1866a9caca6aSWojciech A. Koszek device_printf(dev, "could not set interrupt handler.\n"); 1867a9caca6aSWojciech A. Koszek ether_ifdetach(ifp); 1868a9caca6aSWojciech A. Koszek cgem_detach(dev); 18694c52dde5SEmmanuel Vadot goto err; 1870a9caca6aSWojciech A. Koszek } 1871a9caca6aSWojciech A. Koszek 1872df63a5d2SIan Lepore cgem_add_sysctls(dev); 1873a9caca6aSWojciech A. Koszek 1874a9caca6aSWojciech A. Koszek return (0); 18754c52dde5SEmmanuel Vadot 18764c52dde5SEmmanuel Vadot err_tsu_clk: 18774c52dde5SEmmanuel Vadot if (sc->clk_tsuclk) 18784c52dde5SEmmanuel Vadot clk_release(sc->clk_tsuclk); 18794c52dde5SEmmanuel Vadot err_rx_clk: 18804c52dde5SEmmanuel Vadot if (sc->clk_rxclk) 18814c52dde5SEmmanuel Vadot clk_release(sc->clk_rxclk); 18824c52dde5SEmmanuel Vadot err_tx_clk: 18834c52dde5SEmmanuel Vadot if (sc->clk_txclk) 18844c52dde5SEmmanuel Vadot clk_release(sc->clk_txclk); 18854c52dde5SEmmanuel Vadot err_pclk: 18864c52dde5SEmmanuel Vadot if (sc->clk_pclk) 18874c52dde5SEmmanuel Vadot clk_release(sc->clk_pclk); 18884c52dde5SEmmanuel Vadot if (sc->clk_hclk) 18894c52dde5SEmmanuel Vadot clk_release(sc->clk_hclk); 18904c52dde5SEmmanuel Vadot err: 18914c52dde5SEmmanuel Vadot return (err); 1892a9caca6aSWojciech A. Koszek } 1893a9caca6aSWojciech A. Koszek 1894a9caca6aSWojciech A. Koszek static int 1895a9caca6aSWojciech A. Koszek cgem_detach(device_t dev) 1896a9caca6aSWojciech A. Koszek { 1897a9caca6aSWojciech A. Koszek struct cgem_softc *sc = device_get_softc(dev); 1898a9caca6aSWojciech A. Koszek int i; 1899a9caca6aSWojciech A. Koszek 1900a9caca6aSWojciech A. Koszek if (sc == NULL) 1901a9caca6aSWojciech A. Koszek return (ENODEV); 1902a9caca6aSWojciech A. Koszek 1903a9caca6aSWojciech A. Koszek if (device_is_attached(dev)) { 1904a9caca6aSWojciech A. Koszek CGEM_LOCK(sc); 1905a9caca6aSWojciech A. Koszek cgem_stop(sc); 1906a9caca6aSWojciech A. Koszek CGEM_UNLOCK(sc); 1907a9caca6aSWojciech A. Koszek callout_drain(&sc->tick_ch); 1908023911aaSIan Lepore if_setflagbits(sc->ifp, 0, IFF_UP); 1909a9caca6aSWojciech A. Koszek ether_ifdetach(sc->ifp); 1910a9caca6aSWojciech A. Koszek } 1911a9caca6aSWojciech A. Koszek 1912*faf0bf43SJohn Baldwin bus_generic_detach(dev); 1913a9caca6aSWojciech A. Koszek 1914df63a5d2SIan Lepore /* Release resources. */ 1915a9caca6aSWojciech A. Koszek if (sc->mem_res != NULL) { 1916a9caca6aSWojciech A. Koszek bus_release_resource(dev, SYS_RES_MEMORY, 1917a9caca6aSWojciech A. Koszek rman_get_rid(sc->mem_res), sc->mem_res); 1918a9caca6aSWojciech A. Koszek sc->mem_res = NULL; 1919a9caca6aSWojciech A. Koszek } 1920a9caca6aSWojciech A. Koszek if (sc->irq_res != NULL) { 1921a9caca6aSWojciech A. Koszek if (sc->intrhand) 1922a9caca6aSWojciech A. Koszek bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 1923a9caca6aSWojciech A. Koszek bus_release_resource(dev, SYS_RES_IRQ, 1924a9caca6aSWojciech A. Koszek rman_get_rid(sc->irq_res), sc->irq_res); 1925a9caca6aSWojciech A. Koszek sc->irq_res = NULL; 1926a9caca6aSWojciech A. Koszek } 1927a9caca6aSWojciech A. Koszek 1928a9caca6aSWojciech A. Koszek /* Release DMA resources. */ 1929c34f1a08SJohn Baldwin if (sc->rxring != NULL) { 1930c34f1a08SJohn Baldwin if (sc->rxring_physaddr != 0) { 1931023911aaSIan Lepore bus_dmamap_unload(sc->desc_dma_tag, 1932023911aaSIan Lepore sc->rxring_dma_map); 1933c34f1a08SJohn Baldwin sc->rxring_physaddr = 0; 1934facdd1cdSThomas Skibo sc->txring_physaddr = 0; 1935facdd1cdSThomas Skibo sc->null_qs_physaddr = 0; 1936c34f1a08SJohn Baldwin } 1937a9caca6aSWojciech A. Koszek bus_dmamem_free(sc->desc_dma_tag, sc->rxring, 1938a9caca6aSWojciech A. Koszek sc->rxring_dma_map); 1939c34f1a08SJohn Baldwin sc->rxring = NULL; 1940facdd1cdSThomas Skibo sc->txring = NULL; 1941facdd1cdSThomas Skibo sc->null_qs = NULL; 1942facdd1cdSThomas Skibo 1943a9caca6aSWojciech A. Koszek for (i = 0; i < CGEM_NUM_RX_DESCS; i++) 1944a9caca6aSWojciech A. Koszek if (sc->rxring_m_dmamap[i] != NULL) { 1945a9caca6aSWojciech A. Koszek bus_dmamap_destroy(sc->mbuf_dma_tag, 1946a9caca6aSWojciech A. Koszek sc->rxring_m_dmamap[i]); 1947a9caca6aSWojciech A. Koszek sc->rxring_m_dmamap[i] = NULL; 1948a9caca6aSWojciech A. Koszek } 1949a9caca6aSWojciech A. Koszek for (i = 0; i < CGEM_NUM_TX_DESCS; i++) 1950a9caca6aSWojciech A. Koszek if (sc->txring_m_dmamap[i] != NULL) { 1951a9caca6aSWojciech A. Koszek bus_dmamap_destroy(sc->mbuf_dma_tag, 1952a9caca6aSWojciech A. Koszek sc->txring_m_dmamap[i]); 1953a9caca6aSWojciech A. Koszek sc->txring_m_dmamap[i] = NULL; 1954a9caca6aSWojciech A. Koszek } 1955a9caca6aSWojciech A. Koszek } 1956a9caca6aSWojciech A. Koszek if (sc->desc_dma_tag != NULL) { 1957a9caca6aSWojciech A. Koszek bus_dma_tag_destroy(sc->desc_dma_tag); 1958a9caca6aSWojciech A. Koszek sc->desc_dma_tag = NULL; 1959a9caca6aSWojciech A. Koszek } 1960a9caca6aSWojciech A. Koszek if (sc->mbuf_dma_tag != NULL) { 1961a9caca6aSWojciech A. Koszek bus_dma_tag_destroy(sc->mbuf_dma_tag); 1962a9caca6aSWojciech A. Koszek sc->mbuf_dma_tag = NULL; 1963a9caca6aSWojciech A. Koszek } 1964a9caca6aSWojciech A. Koszek 19654c52dde5SEmmanuel Vadot if (sc->clk_tsuclk) 19664c52dde5SEmmanuel Vadot clk_release(sc->clk_tsuclk); 19674c52dde5SEmmanuel Vadot if (sc->clk_rxclk) 19684c52dde5SEmmanuel Vadot clk_release(sc->clk_rxclk); 19694c52dde5SEmmanuel Vadot if (sc->clk_txclk) 19704c52dde5SEmmanuel Vadot clk_release(sc->clk_txclk); 19714c52dde5SEmmanuel Vadot if (sc->clk_pclk) 19724c52dde5SEmmanuel Vadot clk_release(sc->clk_pclk); 19734c52dde5SEmmanuel Vadot if (sc->clk_hclk) 19744c52dde5SEmmanuel Vadot clk_release(sc->clk_hclk); 19754c52dde5SEmmanuel Vadot 1976a9caca6aSWojciech A. Koszek CGEM_LOCK_DESTROY(sc); 1977a9caca6aSWojciech A. Koszek 1978a9caca6aSWojciech A. Koszek return (0); 1979a9caca6aSWojciech A. Koszek } 1980a9caca6aSWojciech A. Koszek 1981a9caca6aSWojciech A. Koszek static device_method_t cgem_methods[] = { 1982a9caca6aSWojciech A. Koszek /* Device interface */ 1983a9caca6aSWojciech A. Koszek DEVMETHOD(device_probe, cgem_probe), 1984a9caca6aSWojciech A. Koszek DEVMETHOD(device_attach, cgem_attach), 1985a9caca6aSWojciech A. Koszek DEVMETHOD(device_detach, cgem_detach), 1986a9caca6aSWojciech A. Koszek 1987a9caca6aSWojciech A. Koszek /* MII interface */ 1988a9caca6aSWojciech A. Koszek DEVMETHOD(miibus_readreg, cgem_miibus_readreg), 1989a9caca6aSWojciech A. Koszek DEVMETHOD(miibus_writereg, cgem_miibus_writereg), 19900f822edeSIan Lepore DEVMETHOD(miibus_statchg, cgem_miibus_statchg), 1991df63a5d2SIan Lepore DEVMETHOD(miibus_linkchg, cgem_miibus_linkchg), 1992a9caca6aSWojciech A. Koszek 1993a9caca6aSWojciech A. Koszek DEVMETHOD_END 1994a9caca6aSWojciech A. Koszek }; 1995a9caca6aSWojciech A. Koszek 1996a9caca6aSWojciech A. Koszek static driver_t cgem_driver = { 1997a9caca6aSWojciech A. Koszek "cgem", 1998a9caca6aSWojciech A. Koszek cgem_methods, 1999a9caca6aSWojciech A. Koszek sizeof(struct cgem_softc), 2000a9caca6aSWojciech A. Koszek }; 2001a9caca6aSWojciech A. Koszek 200231e2d81eSJohn Baldwin DRIVER_MODULE(cgem, simplebus, cgem_driver, NULL, NULL); 20033e38757dSJohn Baldwin DRIVER_MODULE(miibus, cgem, miibus_driver, NULL, NULL); 2004a9caca6aSWojciech A. Koszek MODULE_DEPEND(cgem, miibus, 1, 1, 1); 2005a9caca6aSWojciech A. Koszek MODULE_DEPEND(cgem, ether, 1, 1, 1); 2006facdd1cdSThomas Skibo SIMPLEBUS_PNP_INFO(compat_data); 2007