xref: /freebsd-src/sys/dev/cadence/if_cgem.c (revision faf0bf43df0c2a16cbffc1e8f88f105a259c7e77)
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