xref: /dflybsd-src/sys/dev/netif/ae/if_ae.c (revision 441d34b2441f59fde86fa4ef2d5d5cb7a6bfcb11)
1f85e0762SSepherosa Ziehau /*-
2f85e0762SSepherosa Ziehau  * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
3f85e0762SSepherosa Ziehau  * All rights reserved.
4f85e0762SSepherosa Ziehau  *
5f85e0762SSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
6f85e0762SSepherosa Ziehau  * modification, are permitted provided that the following conditions
7f85e0762SSepherosa Ziehau  * are met:
8f85e0762SSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
9f85e0762SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer.
10f85e0762SSepherosa Ziehau  * 2. Redistributions in binary form must reproduce the above copyright
11f85e0762SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer in the
12f85e0762SSepherosa Ziehau  *    documentation and/or other materials provided with the distribution.
13f85e0762SSepherosa Ziehau  *
14f85e0762SSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15f85e0762SSepherosa Ziehau  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16f85e0762SSepherosa Ziehau  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17f85e0762SSepherosa Ziehau  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18f85e0762SSepherosa Ziehau  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19f85e0762SSepherosa Ziehau  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20f85e0762SSepherosa Ziehau  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21f85e0762SSepherosa Ziehau  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22f85e0762SSepherosa Ziehau  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23f85e0762SSepherosa Ziehau  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24f85e0762SSepherosa Ziehau  *
25f85e0762SSepherosa Ziehau  * Driver for Attansic Technology Corp. L2 FastEthernet adapter.
26f85e0762SSepherosa Ziehau  *
27f85e0762SSepherosa Ziehau  * This driver is heavily based on age(4) Attansic L1 driver by Pyun YongHyeon.
28f85e0762SSepherosa Ziehau  *
29f85e0762SSepherosa Ziehau  * $FreeBSD: src/sys/dev/ae/if_ae.c,v 1.1.2.3.2.1 2009/04/15 03:14:26 kensmith Exp $
30f85e0762SSepherosa Ziehau  */
31f85e0762SSepherosa Ziehau 
32f85e0762SSepherosa Ziehau #include <sys/param.h>
33f85e0762SSepherosa Ziehau #include <sys/endian.h>
34f85e0762SSepherosa Ziehau #include <sys/kernel.h>
35f85e0762SSepherosa Ziehau #include <sys/bus.h>
36f85e0762SSepherosa Ziehau #include <sys/interrupt.h>
37f85e0762SSepherosa Ziehau #include <sys/malloc.h>
38f85e0762SSepherosa Ziehau #include <sys/proc.h>
39f85e0762SSepherosa Ziehau #include <sys/rman.h>
40f85e0762SSepherosa Ziehau #include <sys/serialize.h>
41f85e0762SSepherosa Ziehau #include <sys/socket.h>
42f85e0762SSepherosa Ziehau #include <sys/sockio.h>
43f85e0762SSepherosa Ziehau #include <sys/sysctl.h>
44f85e0762SSepherosa Ziehau 
45f85e0762SSepherosa Ziehau #include <net/ethernet.h>
46f85e0762SSepherosa Ziehau #include <net/if.h>
47f85e0762SSepherosa Ziehau #include <net/bpf.h>
48f85e0762SSepherosa Ziehau #include <net/if_arp.h>
49f85e0762SSepherosa Ziehau #include <net/if_dl.h>
50f85e0762SSepherosa Ziehau #include <net/if_media.h>
51f85e0762SSepherosa Ziehau #include <net/ifq_var.h>
52f85e0762SSepherosa Ziehau #include <net/vlan/if_vlan_var.h>
53f85e0762SSepherosa Ziehau #include <net/vlan/if_vlan_ether.h>
54f85e0762SSepherosa Ziehau 
55f85e0762SSepherosa Ziehau #include <bus/pci/pcireg.h>
56f85e0762SSepherosa Ziehau #include <bus/pci/pcivar.h>
57f85e0762SSepherosa Ziehau #include <bus/pci/pcidevs.h>
58f85e0762SSepherosa Ziehau 
59f85e0762SSepherosa Ziehau #include <dev/netif/mii_layer/miivar.h>
60f85e0762SSepherosa Ziehau 
61f85e0762SSepherosa Ziehau #include <dev/netif/ae/if_aereg.h>
62f85e0762SSepherosa Ziehau #include <dev/netif/ae/if_aevar.h>
63f85e0762SSepherosa Ziehau 
64f85e0762SSepherosa Ziehau /* "device miibus" required.  See GENERIC if you get errors here. */
65f85e0762SSepherosa Ziehau #include "miibus_if.h"
66f85e0762SSepherosa Ziehau 
67f85e0762SSepherosa Ziehau /*
68f85e0762SSepherosa Ziehau  * Devices supported by this driver.
69f85e0762SSepherosa Ziehau  */
70f85e0762SSepherosa Ziehau static const struct ae_dev {
71f85e0762SSepherosa Ziehau 	uint16_t	ae_vendorid;
72f85e0762SSepherosa Ziehau 	uint16_t	ae_deviceid;
73f85e0762SSepherosa Ziehau 	const char	*ae_name;
74f85e0762SSepherosa Ziehau } ae_devs[] = {
75f85e0762SSepherosa Ziehau         { VENDORID_ATTANSIC, DEVICEID_ATTANSIC_L2,
76f85e0762SSepherosa Ziehau             "Attansic Technology Corp, L2 Fast Ethernet" },
77f85e0762SSepherosa Ziehau 	/* Required last entry */
78f85e0762SSepherosa Ziehau 	{ 0, 0, NULL }
79f85e0762SSepherosa Ziehau };
80f85e0762SSepherosa Ziehau 
81f85e0762SSepherosa Ziehau 
82f85e0762SSepherosa Ziehau static int	ae_probe(device_t);
83f85e0762SSepherosa Ziehau static int	ae_attach(device_t);
84f85e0762SSepherosa Ziehau static int	ae_detach(device_t);
85f85e0762SSepherosa Ziehau static int	ae_shutdown(device_t);
86f85e0762SSepherosa Ziehau static int	ae_suspend(device_t);
87f85e0762SSepherosa Ziehau static int	ae_resume(device_t);
88f85e0762SSepherosa Ziehau static int	ae_miibus_readreg(device_t, int, int);
89f85e0762SSepherosa Ziehau static int	ae_miibus_writereg(device_t, int, int, int);
90f85e0762SSepherosa Ziehau static void	ae_miibus_statchg(device_t);
91f85e0762SSepherosa Ziehau 
92f85e0762SSepherosa Ziehau static int	ae_mediachange(struct ifnet *);
93f85e0762SSepherosa Ziehau static void	ae_mediastatus(struct ifnet *, struct ifmediareq *);
94f85e0762SSepherosa Ziehau static void	ae_init(void *);
95f85e0762SSepherosa Ziehau static void	ae_start(struct ifnet *);
96f85e0762SSepherosa Ziehau static int	ae_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
97f85e0762SSepherosa Ziehau static void	ae_watchdog(struct ifnet *);
98f85e0762SSepherosa Ziehau static void	ae_stop(struct ae_softc *);
99f85e0762SSepherosa Ziehau static void	ae_tick(void *);
100f85e0762SSepherosa Ziehau 
101f85e0762SSepherosa Ziehau static void	ae_intr(void *);
102f85e0762SSepherosa Ziehau static void	ae_tx_intr(struct ae_softc *);
103f85e0762SSepherosa Ziehau static void	ae_rx_intr(struct ae_softc *);
104f85e0762SSepherosa Ziehau static int	ae_rxeof(struct ae_softc *, struct ae_rxd *);
105f85e0762SSepherosa Ziehau 
106f85e0762SSepherosa Ziehau static int	ae_encap(struct ae_softc *, struct mbuf **);
107f85e0762SSepherosa Ziehau static void	ae_sysctl_node(struct ae_softc *);
108f85e0762SSepherosa Ziehau static void	ae_phy_reset(struct ae_softc *);
109f85e0762SSepherosa Ziehau static int	ae_reset(struct ae_softc *);
110f85e0762SSepherosa Ziehau static void	ae_pcie_init(struct ae_softc *);
111f85e0762SSepherosa Ziehau static void	ae_get_eaddr(struct ae_softc *);
112f85e0762SSepherosa Ziehau static void	ae_dma_free(struct ae_softc *);
113f85e0762SSepherosa Ziehau static int	ae_dma_alloc(struct ae_softc *);
114f85e0762SSepherosa Ziehau static void	ae_mac_config(struct ae_softc *);
115f85e0762SSepherosa Ziehau static void	ae_stop_rxmac(struct ae_softc *);
116f85e0762SSepherosa Ziehau static void	ae_stop_txmac(struct ae_softc *);
117f85e0762SSepherosa Ziehau static void	ae_rxfilter(struct ae_softc *);
118f85e0762SSepherosa Ziehau static void	ae_rxvlan(struct ae_softc *);
119f85e0762SSepherosa Ziehau static void	ae_update_stats_rx(uint16_t, struct ae_stats *);
120f85e0762SSepherosa Ziehau static void	ae_update_stats_tx(uint16_t, struct ae_stats *);
121f85e0762SSepherosa Ziehau static void	ae_powersave_disable(struct ae_softc *);
122f85e0762SSepherosa Ziehau static void	ae_powersave_enable(struct ae_softc *);
123f85e0762SSepherosa Ziehau 
124f85e0762SSepherosa Ziehau static device_method_t ae_methods[] = {
125f85e0762SSepherosa Ziehau 	/* Device interface. */
126f85e0762SSepherosa Ziehau 	DEVMETHOD(device_probe,		ae_probe),
127f85e0762SSepherosa Ziehau 	DEVMETHOD(device_attach,	ae_attach),
128f85e0762SSepherosa Ziehau 	DEVMETHOD(device_detach,	ae_detach),
129f85e0762SSepherosa Ziehau 	DEVMETHOD(device_shutdown,	ae_shutdown),
130f85e0762SSepherosa Ziehau 	DEVMETHOD(device_suspend,	ae_suspend),
131f85e0762SSepherosa Ziehau 	DEVMETHOD(device_resume,	ae_resume),
132f85e0762SSepherosa Ziehau 
133f85e0762SSepherosa Ziehau 	/* Bus interface. */
134f85e0762SSepherosa Ziehau 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
135f85e0762SSepherosa Ziehau 	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
136f85e0762SSepherosa Ziehau 
137f85e0762SSepherosa Ziehau 	/* MII interface. */
138f85e0762SSepherosa Ziehau 	DEVMETHOD(miibus_readreg,	ae_miibus_readreg),
139f85e0762SSepherosa Ziehau 	DEVMETHOD(miibus_writereg,	ae_miibus_writereg),
140f85e0762SSepherosa Ziehau 	DEVMETHOD(miibus_statchg,	ae_miibus_statchg),
141f85e0762SSepherosa Ziehau 	{ NULL, NULL }
142f85e0762SSepherosa Ziehau };
143f85e0762SSepherosa Ziehau 
144f85e0762SSepherosa Ziehau static driver_t ae_driver = {
145f85e0762SSepherosa Ziehau 	"ae",
146f85e0762SSepherosa Ziehau 	ae_methods,
147f85e0762SSepherosa Ziehau 	sizeof(struct ae_softc)
148f85e0762SSepherosa Ziehau };
149f85e0762SSepherosa Ziehau 
150f85e0762SSepherosa Ziehau static devclass_t ae_devclass;
151f85e0762SSepherosa Ziehau DECLARE_DUMMY_MODULE(if_ae);
152f85e0762SSepherosa Ziehau MODULE_DEPEND(if_ae, miibus, 1, 1, 1);
153f85e0762SSepherosa Ziehau DRIVER_MODULE(if_ae, pci, ae_driver, ae_devclass, 0, 0);
154f85e0762SSepherosa Ziehau DRIVER_MODULE(miibus, ae, miibus_driver, miibus_devclass, 0, 0);
155f85e0762SSepherosa Ziehau 
156f85e0762SSepherosa Ziehau /* Register access macros. */
157f85e0762SSepherosa Ziehau #define AE_WRITE_4(_sc, reg, val)	\
158f85e0762SSepherosa Ziehau 	bus_space_write_4((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg), (val))
159f85e0762SSepherosa Ziehau #define AE_WRITE_2(_sc, reg, val)	\
160f85e0762SSepherosa Ziehau 	bus_space_write_2((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg), (val))
161f85e0762SSepherosa Ziehau #define AE_WRITE_1(_sc, reg, val)	\
162f85e0762SSepherosa Ziehau 	bus_space_write_1((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg), (val))
163f85e0762SSepherosa Ziehau #define AE_READ_4(_sc, reg)		\
164f85e0762SSepherosa Ziehau 	bus_space_read_4((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg))
165f85e0762SSepherosa Ziehau #define AE_READ_2(_sc, reg)		\
166f85e0762SSepherosa Ziehau 	bus_space_read_2((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg))
167f85e0762SSepherosa Ziehau #define AE_READ_1(_sc, reg)		\
168f85e0762SSepherosa Ziehau 	bus_space_read_1((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg))
169f85e0762SSepherosa Ziehau 
170f85e0762SSepherosa Ziehau #define AE_PHY_READ(sc, reg)		\
171f85e0762SSepherosa Ziehau 	ae_miibus_readreg(sc->ae_dev, 0, reg)
172f85e0762SSepherosa Ziehau #define AE_PHY_WRITE(sc, reg, val)	\
173f85e0762SSepherosa Ziehau 	ae_miibus_writereg(sc->ae_dev, 0, reg, val)
174f85e0762SSepherosa Ziehau #define AE_CHECK_EADDR_VALID(eaddr)	\
175f85e0762SSepherosa Ziehau 	((eaddr[0] == 0 && eaddr[1] == 0) || \
176f85e0762SSepherosa Ziehau 	 (eaddr[0] == 0xffffffff && eaddr[1] == 0xffff))
177f85e0762SSepherosa Ziehau #define AE_RXD_VLAN(vtag) \
178f85e0762SSepherosa Ziehau 	(((vtag) >> 4) | (((vtag) & 0x07) << 13) | (((vtag) & 0x08) << 9))
179f85e0762SSepherosa Ziehau #define AE_TXD_VLAN(vtag) \
180f85e0762SSepherosa Ziehau 	(((vtag) << 4) | (((vtag) >> 13) & 0x07) | (((vtag) >> 9) & 0x08))
181f85e0762SSepherosa Ziehau 
182f85e0762SSepherosa Ziehau /*
183f85e0762SSepherosa Ziehau  * ae statistics.
184f85e0762SSepherosa Ziehau  */
185f85e0762SSepherosa Ziehau #define STATS_ENTRY(node, desc, field) \
186f85e0762SSepherosa Ziehau 	{ node, desc, offsetof(struct ae_stats, field) }
187f85e0762SSepherosa Ziehau struct {
188f85e0762SSepherosa Ziehau 	const char	*node;
189f85e0762SSepherosa Ziehau 	const char	*desc;
190f85e0762SSepherosa Ziehau 	intptr_t	offset;
191f85e0762SSepherosa Ziehau } ae_stats_tx[] = {
192f85e0762SSepherosa Ziehau 	STATS_ENTRY("bcast", "broadcast frames", tx_bcast),
193f85e0762SSepherosa Ziehau 	STATS_ENTRY("mcast", "multicast frames", tx_mcast),
194f85e0762SSepherosa Ziehau 	STATS_ENTRY("pause", "PAUSE frames", tx_pause),
195f85e0762SSepherosa Ziehau 	STATS_ENTRY("control", "control frames", tx_ctrl),
196f85e0762SSepherosa Ziehau 	STATS_ENTRY("defers", "deferrals occuried", tx_defer),
197f85e0762SSepherosa Ziehau 	STATS_ENTRY("exc_defers", "excessive deferrals occuried", tx_excdefer),
198f85e0762SSepherosa Ziehau 	STATS_ENTRY("singlecols", "single collisions occuried", tx_singlecol),
199f85e0762SSepherosa Ziehau 	STATS_ENTRY("multicols", "multiple collisions occuried", tx_multicol),
200f85e0762SSepherosa Ziehau 	STATS_ENTRY("latecols", "late collisions occuried", tx_latecol),
201f85e0762SSepherosa Ziehau 	STATS_ENTRY("aborts", "transmit aborts due collisions", tx_abortcol),
202f85e0762SSepherosa Ziehau 	STATS_ENTRY("underruns", "Tx FIFO underruns", tx_underrun)
203f85e0762SSepherosa Ziehau }, ae_stats_rx[] = {
204f85e0762SSepherosa Ziehau 	STATS_ENTRY("bcast", "broadcast frames", rx_bcast),
205f85e0762SSepherosa Ziehau 	STATS_ENTRY("mcast", "multicast frames", rx_mcast),
206f85e0762SSepherosa Ziehau 	STATS_ENTRY("pause", "PAUSE frames", rx_pause),
207f85e0762SSepherosa Ziehau 	STATS_ENTRY("control", "control frames", rx_ctrl),
208f85e0762SSepherosa Ziehau 	STATS_ENTRY("crc_errors", "frames with CRC errors", rx_crcerr),
209f85e0762SSepherosa Ziehau 	STATS_ENTRY("code_errors", "frames with invalid opcode", rx_codeerr),
210f85e0762SSepherosa Ziehau 	STATS_ENTRY("runt", "runt frames", rx_runt),
211f85e0762SSepherosa Ziehau 	STATS_ENTRY("frag", "fragmented frames", rx_frag),
212f85e0762SSepherosa Ziehau 	STATS_ENTRY("align_errors", "frames with alignment errors", rx_align),
213f85e0762SSepherosa Ziehau 	STATS_ENTRY("truncated", "frames truncated due to Rx FIFO inderrun",
214f85e0762SSepherosa Ziehau 	    rx_trunc)
215f85e0762SSepherosa Ziehau };
216f85e0762SSepherosa Ziehau #define AE_STATS_RX_LEN (sizeof(ae_stats_rx) / sizeof(*ae_stats_rx))
217f85e0762SSepherosa Ziehau #define AE_STATS_TX_LEN (sizeof(ae_stats_tx) / sizeof(*ae_stats_tx))
218f85e0762SSepherosa Ziehau 
219f85e0762SSepherosa Ziehau static void
220f85e0762SSepherosa Ziehau ae_stop(struct ae_softc *sc)
221f85e0762SSepherosa Ziehau {
222f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
223f85e0762SSepherosa Ziehau 	int i;
224f85e0762SSepherosa Ziehau 
225f85e0762SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
226f85e0762SSepherosa Ziehau 
227f85e0762SSepherosa Ziehau 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
228f85e0762SSepherosa Ziehau 	ifp->if_timer = 0;
229f85e0762SSepherosa Ziehau 
230f85e0762SSepherosa Ziehau 	sc->ae_flags &= ~AE_FLAG_LINK;
231f85e0762SSepherosa Ziehau 	callout_stop(&sc->ae_tick_ch);
232f85e0762SSepherosa Ziehau 
233f85e0762SSepherosa Ziehau 	/*
234f85e0762SSepherosa Ziehau 	 * Clear and disable interrupts.
235f85e0762SSepherosa Ziehau 	 */
236f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_IMR_REG, 0);
237f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_ISR_REG, 0xffffffff);
238f85e0762SSepherosa Ziehau 
239f85e0762SSepherosa Ziehau 	/*
240f85e0762SSepherosa Ziehau 	 * Stop Rx/Tx MACs.
241f85e0762SSepherosa Ziehau 	 */
242f85e0762SSepherosa Ziehau 	ae_stop_txmac(sc);
243f85e0762SSepherosa Ziehau 	ae_stop_rxmac(sc);
244f85e0762SSepherosa Ziehau 
245f85e0762SSepherosa Ziehau 	/*
246f85e0762SSepherosa Ziehau 	 * Stop DMA engines.
247f85e0762SSepherosa Ziehau 	 */
248f85e0762SSepherosa Ziehau 	AE_WRITE_1(sc, AE_DMAREAD_REG, ~AE_DMAREAD_EN);
249f85e0762SSepherosa Ziehau 	AE_WRITE_1(sc, AE_DMAWRITE_REG, ~AE_DMAWRITE_EN);
250f85e0762SSepherosa Ziehau 
251f85e0762SSepherosa Ziehau 	/*
252f85e0762SSepherosa Ziehau 	 * Wait for everything to enter idle state.
253f85e0762SSepherosa Ziehau 	 */
254f85e0762SSepherosa Ziehau 	for (i = 0; i < AE_IDLE_TIMEOUT; i++) {
255f85e0762SSepherosa Ziehau 		if (AE_READ_4(sc, AE_IDLE_REG) == 0)
256f85e0762SSepherosa Ziehau 			break;
257f85e0762SSepherosa Ziehau 		DELAY(100);
258f85e0762SSepherosa Ziehau 	}
259f85e0762SSepherosa Ziehau 	if (i == AE_IDLE_TIMEOUT)
260f85e0762SSepherosa Ziehau 		if_printf(ifp, "could not enter idle state in stop.\n");
261f85e0762SSepherosa Ziehau }
262f85e0762SSepherosa Ziehau 
263f85e0762SSepherosa Ziehau static void
264f85e0762SSepherosa Ziehau ae_stop_rxmac(struct ae_softc *sc)
265f85e0762SSepherosa Ziehau {
266f85e0762SSepherosa Ziehau 	uint32_t val;
267f85e0762SSepherosa Ziehau 	int i;
268f85e0762SSepherosa Ziehau 
269f85e0762SSepherosa Ziehau 	/*
270f85e0762SSepherosa Ziehau 	 * Stop Rx MAC engine.
271f85e0762SSepherosa Ziehau 	 */
272f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_MAC_REG);
273f85e0762SSepherosa Ziehau 	if ((val & AE_MAC_RX_EN) != 0) {
274f85e0762SSepherosa Ziehau 		val &= ~AE_MAC_RX_EN;
275f85e0762SSepherosa Ziehau 		AE_WRITE_4(sc, AE_MAC_REG, val);
276f85e0762SSepherosa Ziehau 	}
277f85e0762SSepherosa Ziehau 
278f85e0762SSepherosa Ziehau 	/*
279f85e0762SSepherosa Ziehau 	 * Stop Rx DMA engine.
280f85e0762SSepherosa Ziehau 	 */
281f85e0762SSepherosa Ziehau 	if (AE_READ_1(sc, AE_DMAWRITE_REG) == AE_DMAWRITE_EN)
282f85e0762SSepherosa Ziehau 		AE_WRITE_1(sc, AE_DMAWRITE_REG, 0);
283f85e0762SSepherosa Ziehau 
284f85e0762SSepherosa Ziehau 	/*
285f85e0762SSepherosa Ziehau 	 * Wait for IDLE state.
286f85e0762SSepherosa Ziehau 	 */
287f85e0762SSepherosa Ziehau 	for (i = 0; i < AE_IDLE_TIMEOUT; i--) {
288f85e0762SSepherosa Ziehau 		val = AE_READ_4(sc, AE_IDLE_REG);
289f85e0762SSepherosa Ziehau 		if ((val & (AE_IDLE_RXMAC | AE_IDLE_DMAWRITE)) == 0)
290f85e0762SSepherosa Ziehau 			break;
291f85e0762SSepherosa Ziehau 		DELAY(100);
292f85e0762SSepherosa Ziehau 	}
293f85e0762SSepherosa Ziehau 	if (i == AE_IDLE_TIMEOUT) {
294f85e0762SSepherosa Ziehau 		if_printf(&sc->arpcom.ac_if,
295f85e0762SSepherosa Ziehau 			  "timed out while stopping Rx MAC.\n");
296f85e0762SSepherosa Ziehau 	}
297f85e0762SSepherosa Ziehau }
298f85e0762SSepherosa Ziehau 
299f85e0762SSepherosa Ziehau static void
300f85e0762SSepherosa Ziehau ae_stop_txmac(struct ae_softc *sc)
301f85e0762SSepherosa Ziehau {
302f85e0762SSepherosa Ziehau 	uint32_t val;
303f85e0762SSepherosa Ziehau 	int i;
304f85e0762SSepherosa Ziehau 
305f85e0762SSepherosa Ziehau 	/*
306f85e0762SSepherosa Ziehau 	 * Stop Tx MAC engine.
307f85e0762SSepherosa Ziehau 	 */
308f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_MAC_REG);
309f85e0762SSepherosa Ziehau 	if ((val & AE_MAC_TX_EN) != 0) {
310f85e0762SSepherosa Ziehau 		val &= ~AE_MAC_TX_EN;
311f85e0762SSepherosa Ziehau 		AE_WRITE_4(sc, AE_MAC_REG, val);
312f85e0762SSepherosa Ziehau 	}
313f85e0762SSepherosa Ziehau 
314f85e0762SSepherosa Ziehau 	/*
315f85e0762SSepherosa Ziehau 	 * Stop Tx DMA engine.
316f85e0762SSepherosa Ziehau 	 */
317f85e0762SSepherosa Ziehau 	if (AE_READ_1(sc, AE_DMAREAD_REG) == AE_DMAREAD_EN)
318f85e0762SSepherosa Ziehau 		AE_WRITE_1(sc, AE_DMAREAD_REG, 0);
319f85e0762SSepherosa Ziehau 
320f85e0762SSepherosa Ziehau 	/*
321f85e0762SSepherosa Ziehau 	 * Wait for IDLE state.
322f85e0762SSepherosa Ziehau 	 */
323f85e0762SSepherosa Ziehau 	for (i = 0; i < AE_IDLE_TIMEOUT; i--) {
324f85e0762SSepherosa Ziehau 		val = AE_READ_4(sc, AE_IDLE_REG);
325f85e0762SSepherosa Ziehau 		if ((val & (AE_IDLE_TXMAC | AE_IDLE_DMAREAD)) == 0)
326f85e0762SSepherosa Ziehau 			break;
327f85e0762SSepherosa Ziehau 		DELAY(100);
328f85e0762SSepherosa Ziehau 	}
329f85e0762SSepherosa Ziehau 	if (i == AE_IDLE_TIMEOUT) {
330f85e0762SSepherosa Ziehau 		if_printf(&sc->arpcom.ac_if,
331f85e0762SSepherosa Ziehau 			  "timed out while stopping Tx MAC.\n");
332f85e0762SSepherosa Ziehau 	}
333f85e0762SSepherosa Ziehau }
334f85e0762SSepherosa Ziehau 
335f85e0762SSepherosa Ziehau /*
336f85e0762SSepherosa Ziehau  * Callback from MII layer when media changes.
337f85e0762SSepherosa Ziehau  */
338f85e0762SSepherosa Ziehau static void
339f85e0762SSepherosa Ziehau ae_miibus_statchg(device_t dev)
340f85e0762SSepherosa Ziehau {
341f85e0762SSepherosa Ziehau 	struct ae_softc *sc = device_get_softc(dev);
342f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
343f85e0762SSepherosa Ziehau 	struct mii_data *mii;
344f85e0762SSepherosa Ziehau 	uint32_t val;
345f85e0762SSepherosa Ziehau 
346f85e0762SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
347f85e0762SSepherosa Ziehau 
348f85e0762SSepherosa Ziehau 	if ((ifp->if_flags & IFF_RUNNING) == 0)
349f85e0762SSepherosa Ziehau 		return;
350f85e0762SSepherosa Ziehau 
351f85e0762SSepherosa Ziehau 	mii = device_get_softc(sc->ae_miibus);
352f85e0762SSepherosa Ziehau 	sc->ae_flags &= ~AE_FLAG_LINK;
353f85e0762SSepherosa Ziehau 	if ((mii->mii_media_status & IFM_AVALID) != 0) {
354f85e0762SSepherosa Ziehau 		switch (IFM_SUBTYPE(mii->mii_media_active)) {
355f85e0762SSepherosa Ziehau 		case IFM_10_T:
356f85e0762SSepherosa Ziehau 		case IFM_100_TX:
357f85e0762SSepherosa Ziehau 			sc->ae_flags |= AE_FLAG_LINK;
358f85e0762SSepherosa Ziehau 			break;
359f85e0762SSepherosa Ziehau 		default:
360f85e0762SSepherosa Ziehau 			break;
361f85e0762SSepherosa Ziehau 		}
362f85e0762SSepherosa Ziehau 	}
363f85e0762SSepherosa Ziehau 
364f85e0762SSepherosa Ziehau 	/* Stop Rx/Tx MACs. */
365f85e0762SSepherosa Ziehau 	ae_stop_rxmac(sc);
366f85e0762SSepherosa Ziehau 	ae_stop_txmac(sc);
367f85e0762SSepherosa Ziehau 
368f85e0762SSepherosa Ziehau 	/* Program MACs with resolved speed/duplex/flow-control. */
369f85e0762SSepherosa Ziehau 	if ((sc->ae_flags & AE_FLAG_LINK) != 0) {
370f85e0762SSepherosa Ziehau 		ae_mac_config(sc);
371f85e0762SSepherosa Ziehau 
372f85e0762SSepherosa Ziehau 		/*
373f85e0762SSepherosa Ziehau 		 * Restart DMA engines.
374f85e0762SSepherosa Ziehau 		 */
375f85e0762SSepherosa Ziehau 		AE_WRITE_1(sc, AE_DMAREAD_REG, AE_DMAREAD_EN);
376f85e0762SSepherosa Ziehau 		AE_WRITE_1(sc, AE_DMAWRITE_REG, AE_DMAWRITE_EN);
377f85e0762SSepherosa Ziehau 
378f85e0762SSepherosa Ziehau 		/*
379f85e0762SSepherosa Ziehau 		 * Enable Rx and Tx MACs.
380f85e0762SSepherosa Ziehau 		 */
381f85e0762SSepherosa Ziehau 		val = AE_READ_4(sc, AE_MAC_REG);
382f85e0762SSepherosa Ziehau 		val |= AE_MAC_TX_EN | AE_MAC_RX_EN;
383f85e0762SSepherosa Ziehau 		AE_WRITE_4(sc, AE_MAC_REG, val);
384f85e0762SSepherosa Ziehau 	}
385f85e0762SSepherosa Ziehau }
386f85e0762SSepherosa Ziehau 
387f85e0762SSepherosa Ziehau static void
388f85e0762SSepherosa Ziehau ae_sysctl_node(struct ae_softc *sc)
389f85e0762SSepherosa Ziehau {
390f85e0762SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
391f85e0762SSepherosa Ziehau 	struct sysctl_oid *root, *stats, *stats_rx, *stats_tx;
392f85e0762SSepherosa Ziehau 	struct ae_stats *ae_stats;
393f85e0762SSepherosa Ziehau 	unsigned int i;
394f85e0762SSepherosa Ziehau 
395f85e0762SSepherosa Ziehau 	ae_stats = &sc->stats;
396f85e0762SSepherosa Ziehau 	sysctl_ctx_init(&sc->ae_sysctl_ctx);
397f85e0762SSepherosa Ziehau 	sc->ae_sysctl_tree = SYSCTL_ADD_NODE(&sc->ae_sysctl_ctx,
398f85e0762SSepherosa Ziehau 				SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
399f85e0762SSepherosa Ziehau 				device_get_nameunit(sc->ae_dev),
400f85e0762SSepherosa Ziehau 				CTLFLAG_RD, 0, "");
401f85e0762SSepherosa Ziehau 	if (sc->ae_sysctl_tree == NULL) {
402f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "can't add sysctl node\n");
403f85e0762SSepherosa Ziehau 		return;
404f85e0762SSepherosa Ziehau 	}
405f85e0762SSepherosa Ziehau 	ctx = &sc->ae_sysctl_ctx;
406f85e0762SSepherosa Ziehau 	root = sc->ae_sysctl_tree;
407f85e0762SSepherosa Ziehau 
408f85e0762SSepherosa Ziehau 	stats = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(root), OID_AUTO, "stats",
409f85e0762SSepherosa Ziehau 	    CTLFLAG_RD, NULL, "ae statistics");
410f85e0762SSepherosa Ziehau 	if (stats == NULL) {
411f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "can't add stats sysctl node\n");
412f85e0762SSepherosa Ziehau 		return;
413f85e0762SSepherosa Ziehau 	}
414f85e0762SSepherosa Ziehau 
415f85e0762SSepherosa Ziehau 	/*
416f85e0762SSepherosa Ziehau 	 * Receiver statistcics.
417f85e0762SSepherosa Ziehau 	 */
418f85e0762SSepherosa Ziehau 	stats_rx = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, "rx",
419f85e0762SSepherosa Ziehau 	    CTLFLAG_RD, NULL, "Rx MAC statistics");
420f85e0762SSepherosa Ziehau 	if (stats_rx != NULL) {
421f85e0762SSepherosa Ziehau 		for (i = 0; i < AE_STATS_RX_LEN; i++) {
422f85e0762SSepherosa Ziehau 			SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(stats_rx),
423f85e0762SSepherosa Ziehau 			    OID_AUTO, ae_stats_rx[i].node, CTLFLAG_RD,
424f85e0762SSepherosa Ziehau 			    (char *)ae_stats + ae_stats_rx[i].offset, 0,
425f85e0762SSepherosa Ziehau 			    ae_stats_rx[i].desc);
426f85e0762SSepherosa Ziehau 		}
427f85e0762SSepherosa Ziehau 	}
428f85e0762SSepherosa Ziehau 
429f85e0762SSepherosa Ziehau 	/*
430f85e0762SSepherosa Ziehau 	 * Transmitter statistcics.
431f85e0762SSepherosa Ziehau 	 */
432f85e0762SSepherosa Ziehau 	stats_tx = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, "tx",
433f85e0762SSepherosa Ziehau 	    CTLFLAG_RD, NULL, "Tx MAC statistics");
434f85e0762SSepherosa Ziehau 	if (stats_tx != NULL) {
435f85e0762SSepherosa Ziehau 		for (i = 0; i < AE_STATS_TX_LEN; i++) {
436f85e0762SSepherosa Ziehau 			SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(stats_tx),
437f85e0762SSepherosa Ziehau 			    OID_AUTO, ae_stats_tx[i].node, CTLFLAG_RD,
438f85e0762SSepherosa Ziehau 			    (char *)ae_stats + ae_stats_tx[i].offset, 0,
439f85e0762SSepherosa Ziehau 			    ae_stats_tx[i].desc);
440f85e0762SSepherosa Ziehau 		}
441f85e0762SSepherosa Ziehau 	}
442f85e0762SSepherosa Ziehau }
443f85e0762SSepherosa Ziehau 
444f85e0762SSepherosa Ziehau static int
445f85e0762SSepherosa Ziehau ae_miibus_readreg(device_t dev, int phy, int reg)
446f85e0762SSepherosa Ziehau {
447f85e0762SSepherosa Ziehau 	struct ae_softc *sc = device_get_softc(dev);
448f85e0762SSepherosa Ziehau 	uint32_t val;
449f85e0762SSepherosa Ziehau 	int i;
450f85e0762SSepherosa Ziehau 
451f85e0762SSepherosa Ziehau 	/*
452f85e0762SSepherosa Ziehau 	 * Locking is done in upper layers.
453f85e0762SSepherosa Ziehau 	 */
454f85e0762SSepherosa Ziehau 	if (phy != sc->ae_phyaddr)
455f85e0762SSepherosa Ziehau 		return (0);
456f85e0762SSepherosa Ziehau 	val = ((reg << AE_MDIO_REGADDR_SHIFT) & AE_MDIO_REGADDR_MASK) |
457f85e0762SSepherosa Ziehau 	    AE_MDIO_START | AE_MDIO_READ | AE_MDIO_SUP_PREAMBLE |
458f85e0762SSepherosa Ziehau 	    ((AE_MDIO_CLK_25_4 << AE_MDIO_CLK_SHIFT) & AE_MDIO_CLK_MASK);
459f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MDIO_REG, val);
460f85e0762SSepherosa Ziehau 
461f85e0762SSepherosa Ziehau 	/*
462f85e0762SSepherosa Ziehau 	 * Wait for operation to complete.
463f85e0762SSepherosa Ziehau 	 */
464f85e0762SSepherosa Ziehau 	for (i = 0; i < AE_MDIO_TIMEOUT; i++) {
465f85e0762SSepherosa Ziehau 		DELAY(2);
466f85e0762SSepherosa Ziehau 		val = AE_READ_4(sc, AE_MDIO_REG);
467f85e0762SSepherosa Ziehau 		if ((val & (AE_MDIO_START | AE_MDIO_BUSY)) == 0)
468f85e0762SSepherosa Ziehau 			break;
469f85e0762SSepherosa Ziehau 	}
470f85e0762SSepherosa Ziehau 	if (i == AE_MDIO_TIMEOUT) {
471f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "phy read timeout: %d.\n", reg);
472f85e0762SSepherosa Ziehau 		return (0);
473f85e0762SSepherosa Ziehau 	}
474f85e0762SSepherosa Ziehau 	return ((val << AE_MDIO_DATA_SHIFT) & AE_MDIO_DATA_MASK);
475f85e0762SSepherosa Ziehau }
476f85e0762SSepherosa Ziehau 
477f85e0762SSepherosa Ziehau static int
478f85e0762SSepherosa Ziehau ae_miibus_writereg(device_t dev, int phy, int reg, int val)
479f85e0762SSepherosa Ziehau {
480f85e0762SSepherosa Ziehau 	struct ae_softc *sc = device_get_softc(dev);
481f85e0762SSepherosa Ziehau 	uint32_t aereg;
482f85e0762SSepherosa Ziehau 	int i;
483f85e0762SSepherosa Ziehau 
484f85e0762SSepherosa Ziehau 	/*
485f85e0762SSepherosa Ziehau 	 * Locking is done in upper layers.
486f85e0762SSepherosa Ziehau 	 */
487f85e0762SSepherosa Ziehau 	if (phy != sc->ae_phyaddr)
488f85e0762SSepherosa Ziehau 		return (0);
489f85e0762SSepherosa Ziehau 	aereg = ((reg << AE_MDIO_REGADDR_SHIFT) & AE_MDIO_REGADDR_MASK) |
490f85e0762SSepherosa Ziehau 	    AE_MDIO_START | AE_MDIO_SUP_PREAMBLE |
491f85e0762SSepherosa Ziehau 	    ((AE_MDIO_CLK_25_4 << AE_MDIO_CLK_SHIFT) & AE_MDIO_CLK_MASK) |
492f85e0762SSepherosa Ziehau 	    ((val << AE_MDIO_DATA_SHIFT) & AE_MDIO_DATA_MASK);
493f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MDIO_REG, aereg);
494f85e0762SSepherosa Ziehau 
495f85e0762SSepherosa Ziehau 	/*
496f85e0762SSepherosa Ziehau 	 * Wait for operation to complete.
497f85e0762SSepherosa Ziehau 	 */
498f85e0762SSepherosa Ziehau 	for (i = 0; i < AE_MDIO_TIMEOUT; i++) {
499f85e0762SSepherosa Ziehau 		DELAY(2);
500f85e0762SSepherosa Ziehau 		aereg = AE_READ_4(sc, AE_MDIO_REG);
501f85e0762SSepherosa Ziehau 		if ((aereg & (AE_MDIO_START | AE_MDIO_BUSY)) == 0)
502f85e0762SSepherosa Ziehau 			break;
503f85e0762SSepherosa Ziehau 	}
504f85e0762SSepherosa Ziehau 	if (i == AE_MDIO_TIMEOUT)
505f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "phy write timeout: %d.\n", reg);
506f85e0762SSepherosa Ziehau 	return (0);
507f85e0762SSepherosa Ziehau }
508f85e0762SSepherosa Ziehau 
509f85e0762SSepherosa Ziehau static int
510f85e0762SSepherosa Ziehau ae_probe(device_t dev)
511f85e0762SSepherosa Ziehau {
512f85e0762SSepherosa Ziehau 	uint16_t vendor, devid;
513f85e0762SSepherosa Ziehau 	const struct ae_dev *sp;
514f85e0762SSepherosa Ziehau 
515f85e0762SSepherosa Ziehau 	vendor = pci_get_vendor(dev);
516f85e0762SSepherosa Ziehau 	devid = pci_get_device(dev);
517f85e0762SSepherosa Ziehau 	for (sp = ae_devs; sp->ae_name != NULL; sp++) {
518f85e0762SSepherosa Ziehau 		if (vendor == sp->ae_vendorid &&
519f85e0762SSepherosa Ziehau 		    devid == sp->ae_deviceid) {
520f85e0762SSepherosa Ziehau 			device_set_desc(dev, sp->ae_name);
521f85e0762SSepherosa Ziehau 			return (0);
522f85e0762SSepherosa Ziehau 		}
523f85e0762SSepherosa Ziehau 	}
524f85e0762SSepherosa Ziehau 	return (ENXIO);
525f85e0762SSepherosa Ziehau }
526f85e0762SSepherosa Ziehau 
527f85e0762SSepherosa Ziehau static int
528f85e0762SSepherosa Ziehau ae_dma_alloc(struct ae_softc *sc)
529f85e0762SSepherosa Ziehau {
530f85e0762SSepherosa Ziehau 	bus_addr_t busaddr;
531f85e0762SSepherosa Ziehau 	int error;
532f85e0762SSepherosa Ziehau 
533f85e0762SSepherosa Ziehau 	/*
534f85e0762SSepherosa Ziehau 	 * Create parent DMA tag.
535f85e0762SSepherosa Ziehau 	 */
536f85e0762SSepherosa Ziehau 	error = bus_dma_tag_create(NULL, 1, 0,
537f85e0762SSepherosa Ziehau 				   BUS_SPACE_MAXADDR_32BIT,
538f85e0762SSepherosa Ziehau 				   BUS_SPACE_MAXADDR,
539f85e0762SSepherosa Ziehau 				   NULL, NULL,
540f85e0762SSepherosa Ziehau 				   BUS_SPACE_MAXSIZE_32BIT,
541f85e0762SSepherosa Ziehau 				   0,
542f85e0762SSepherosa Ziehau 				   BUS_SPACE_MAXSIZE_32BIT,
543f85e0762SSepherosa Ziehau 				   0, &sc->dma_parent_tag);
544f85e0762SSepherosa Ziehau 	if (error) {
545f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "could not creare parent DMA tag.\n");
546f85e0762SSepherosa Ziehau 		return (error);
547f85e0762SSepherosa Ziehau 	}
548f85e0762SSepherosa Ziehau 
549f85e0762SSepherosa Ziehau 	/*
550f85e0762SSepherosa Ziehau 	 * Create DMA stuffs for TxD.
551f85e0762SSepherosa Ziehau 	 */
552f85e0762SSepherosa Ziehau 	sc->txd_base = bus_dmamem_coherent_any(sc->dma_parent_tag, 4,
553f85e0762SSepherosa Ziehau 			AE_TXD_BUFSIZE_DEFAULT, BUS_DMA_WAITOK | BUS_DMA_ZERO,
554f85e0762SSepherosa Ziehau 			&sc->dma_txd_tag, &sc->dma_txd_map,
555f85e0762SSepherosa Ziehau 			&sc->dma_txd_busaddr);
556f85e0762SSepherosa Ziehau 	if (sc->txd_base == NULL) {
557f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "could not creare TxD DMA stuffs.\n");
558f85e0762SSepherosa Ziehau 		return ENOMEM;
559f85e0762SSepherosa Ziehau 	}
560f85e0762SSepherosa Ziehau 
561f85e0762SSepherosa Ziehau 	/*
562f85e0762SSepherosa Ziehau 	 * Create DMA stuffs for TxS.
563f85e0762SSepherosa Ziehau 	 */
564f85e0762SSepherosa Ziehau 	sc->txs_base = bus_dmamem_coherent_any(sc->dma_parent_tag, 4,
565f85e0762SSepherosa Ziehau 			AE_TXS_COUNT_DEFAULT * 4, BUS_DMA_WAITOK | BUS_DMA_ZERO,
566f85e0762SSepherosa Ziehau 			&sc->dma_txs_tag, &sc->dma_txs_map,
567f85e0762SSepherosa Ziehau 			&sc->dma_txs_busaddr);
568f85e0762SSepherosa Ziehau 	if (sc->txs_base == NULL) {
569f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "could not creare TxS DMA stuffs.\n");
570f85e0762SSepherosa Ziehau 		return ENOMEM;
571f85e0762SSepherosa Ziehau 	}
572f85e0762SSepherosa Ziehau 
573f85e0762SSepherosa Ziehau 	/*
574f85e0762SSepherosa Ziehau 	 * Create DMA stuffs for RxD.
575f85e0762SSepherosa Ziehau 	 */
576f85e0762SSepherosa Ziehau 	sc->rxd_base_dma = bus_dmamem_coherent_any(sc->dma_parent_tag, 128,
577f85e0762SSepherosa Ziehau 				AE_RXD_COUNT_DEFAULT * 1536 + 120,
578f85e0762SSepherosa Ziehau 				BUS_DMA_WAITOK | BUS_DMA_ZERO,
579f85e0762SSepherosa Ziehau 				&sc->dma_rxd_tag, &sc->dma_rxd_map,
580f85e0762SSepherosa Ziehau 				&busaddr);
581f85e0762SSepherosa Ziehau 	if (sc->rxd_base_dma == NULL) {
582f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "could not creare RxD DMA stuffs.\n");
583f85e0762SSepherosa Ziehau 		return ENOMEM;
584f85e0762SSepherosa Ziehau 	}
585f85e0762SSepherosa Ziehau 	sc->dma_rxd_busaddr = busaddr + 120;
586f85e0762SSepherosa Ziehau 	sc->rxd_base = (struct ae_rxd *)(sc->rxd_base_dma + 120);
587f85e0762SSepherosa Ziehau 
588f85e0762SSepherosa Ziehau 	return (0);
589f85e0762SSepherosa Ziehau }
590f85e0762SSepherosa Ziehau 
591f85e0762SSepherosa Ziehau static void
592f85e0762SSepherosa Ziehau ae_mac_config(struct ae_softc *sc)
593f85e0762SSepherosa Ziehau {
594f85e0762SSepherosa Ziehau 	struct mii_data *mii;
595f85e0762SSepherosa Ziehau 	uint32_t val;
596f85e0762SSepherosa Ziehau 
597f85e0762SSepherosa Ziehau 	mii = device_get_softc(sc->ae_miibus);
598f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_MAC_REG);
599f85e0762SSepherosa Ziehau 	val &= ~AE_MAC_FULL_DUPLEX;
600f85e0762SSepherosa Ziehau 	/* XXX disable AE_MAC_TX_FLOW_EN? */
601f85e0762SSepherosa Ziehau 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
602f85e0762SSepherosa Ziehau 		val |= AE_MAC_FULL_DUPLEX;
603f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MAC_REG, val);
604f85e0762SSepherosa Ziehau }
605f85e0762SSepherosa Ziehau 
606f85e0762SSepherosa Ziehau static int
607f85e0762SSepherosa Ziehau ae_rxeof(struct ae_softc *sc, struct ae_rxd *rxd)
608f85e0762SSepherosa Ziehau {
609f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
610f85e0762SSepherosa Ziehau 	struct mbuf *m;
611f85e0762SSepherosa Ziehau 	unsigned int size;
612f85e0762SSepherosa Ziehau 	uint16_t flags;
613f85e0762SSepherosa Ziehau 
614f85e0762SSepherosa Ziehau 	flags = le16toh(rxd->flags);
615f85e0762SSepherosa Ziehau #ifdef AE_DEBUG
616f85e0762SSepherosa Ziehau 	if_printf(ifp, "Rx interrupt occuried.\n");
617f85e0762SSepherosa Ziehau #endif
618f85e0762SSepherosa Ziehau 	size = le16toh(rxd->len) - ETHER_CRC_LEN;
619f85e0762SSepherosa Ziehau 	if (size < (ETHER_MIN_LEN - ETHER_CRC_LEN -
620f85e0762SSepherosa Ziehau 		    sizeof(struct ether_vlan_header))) {
621f85e0762SSepherosa Ziehau 		if_printf(ifp, "Runt frame received.");
622f85e0762SSepherosa Ziehau 		return (EIO);
623f85e0762SSepherosa Ziehau 	}
624f85e0762SSepherosa Ziehau 
625f85e0762SSepherosa Ziehau 	m = m_devget(&rxd->data[0], size, ETHER_ALIGN, ifp, NULL);
626f85e0762SSepherosa Ziehau 	if (m == NULL)
627f85e0762SSepherosa Ziehau 		return (ENOBUFS);
628f85e0762SSepherosa Ziehau 
629f85e0762SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) &&
630f85e0762SSepherosa Ziehau 	    (flags & AE_RXD_HAS_VLAN)) {
631f85e0762SSepherosa Ziehau 		m->m_pkthdr.ether_vlantag = AE_RXD_VLAN(le16toh(rxd->vlan));
632f85e0762SSepherosa Ziehau 		m->m_flags |= M_VLANTAG;
633f85e0762SSepherosa Ziehau 	}
634f85e0762SSepherosa Ziehau 	ifp->if_input(ifp, m);
635f85e0762SSepherosa Ziehau 
636f85e0762SSepherosa Ziehau 	return (0);
637f85e0762SSepherosa Ziehau }
638f85e0762SSepherosa Ziehau 
639f85e0762SSepherosa Ziehau static void
640f85e0762SSepherosa Ziehau ae_rx_intr(struct ae_softc *sc)
641f85e0762SSepherosa Ziehau {
642f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
643f85e0762SSepherosa Ziehau 	struct ae_rxd *rxd;
644f85e0762SSepherosa Ziehau 	uint16_t flags;
645f85e0762SSepherosa Ziehau 	int error;
646f85e0762SSepherosa Ziehau 
647f85e0762SSepherosa Ziehau 	/*
648f85e0762SSepherosa Ziehau 	 * Syncronize DMA buffers.
649f85e0762SSepherosa Ziehau 	 */
650f85e0762SSepherosa Ziehau 	bus_dmamap_sync(sc->dma_rxd_tag, sc->dma_rxd_map,
651f85e0762SSepherosa Ziehau 			BUS_DMASYNC_POSTREAD);
652f85e0762SSepherosa Ziehau 	for (;;) {
653f85e0762SSepherosa Ziehau 		rxd = (struct ae_rxd *)(sc->rxd_base + sc->rxd_cur);
654f85e0762SSepherosa Ziehau 
655f85e0762SSepherosa Ziehau 		flags = le16toh(rxd->flags);
656f85e0762SSepherosa Ziehau 		if ((flags & AE_RXD_UPDATE) == 0)
657f85e0762SSepherosa Ziehau 			break;
658f85e0762SSepherosa Ziehau 		rxd->flags = htole16(flags & ~AE_RXD_UPDATE);
659f85e0762SSepherosa Ziehau 
660f85e0762SSepherosa Ziehau 		/* Update stats. */
661f85e0762SSepherosa Ziehau 		ae_update_stats_rx(flags, &sc->stats);
662f85e0762SSepherosa Ziehau 
663f85e0762SSepherosa Ziehau 		/*
664f85e0762SSepherosa Ziehau 		 * Update position index.
665f85e0762SSepherosa Ziehau 		 */
666f85e0762SSepherosa Ziehau 		sc->rxd_cur = (sc->rxd_cur + 1) % AE_RXD_COUNT_DEFAULT;
667f85e0762SSepherosa Ziehau 		if ((flags & AE_RXD_SUCCESS) == 0) {
668f85e0762SSepherosa Ziehau 			ifp->if_ierrors++;
669f85e0762SSepherosa Ziehau 			continue;
670f85e0762SSepherosa Ziehau 		}
671f85e0762SSepherosa Ziehau 
672f85e0762SSepherosa Ziehau 		error = ae_rxeof(sc, rxd);
673f85e0762SSepherosa Ziehau 		if (error)
674f85e0762SSepherosa Ziehau 			ifp->if_ierrors++;
675f85e0762SSepherosa Ziehau 		else
676f85e0762SSepherosa Ziehau 			ifp->if_ipackets++;
677f85e0762SSepherosa Ziehau 	}
678f85e0762SSepherosa Ziehau 
679f85e0762SSepherosa Ziehau 	/* Update Rx index. */
680f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_MB_RXD_IDX_REG, sc->rxd_cur);
681f85e0762SSepherosa Ziehau }
682f85e0762SSepherosa Ziehau 
683f85e0762SSepherosa Ziehau static void
684f85e0762SSepherosa Ziehau ae_tx_intr(struct ae_softc *sc)
685f85e0762SSepherosa Ziehau {
686f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
687f85e0762SSepherosa Ziehau 	struct ae_txd *txd;
688f85e0762SSepherosa Ziehau 	struct ae_txs *txs;
689f85e0762SSepherosa Ziehau 	uint16_t flags;
690f85e0762SSepherosa Ziehau 
691f85e0762SSepherosa Ziehau 	/*
692f85e0762SSepherosa Ziehau 	 * Syncronize DMA buffers.
693f85e0762SSepherosa Ziehau 	 */
694f85e0762SSepherosa Ziehau 	bus_dmamap_sync(sc->dma_txd_tag, sc->dma_txd_map, BUS_DMASYNC_POSTREAD);
695f85e0762SSepherosa Ziehau 	bus_dmamap_sync(sc->dma_txs_tag, sc->dma_txs_map, BUS_DMASYNC_POSTREAD);
696f85e0762SSepherosa Ziehau 
697f85e0762SSepherosa Ziehau 	for (;;) {
698f85e0762SSepherosa Ziehau 		txs = sc->txs_base + sc->txs_ack;
699f85e0762SSepherosa Ziehau 
700f85e0762SSepherosa Ziehau 		flags = le16toh(txs->flags);
701f85e0762SSepherosa Ziehau 		if ((flags & AE_TXS_UPDATE) == 0)
702f85e0762SSepherosa Ziehau 			break;
703f85e0762SSepherosa Ziehau 		txs->flags = htole16(flags & ~AE_TXS_UPDATE);
704f85e0762SSepherosa Ziehau 
705f85e0762SSepherosa Ziehau 		/* Update stats. */
706f85e0762SSepherosa Ziehau 		ae_update_stats_tx(flags, &sc->stats);
707f85e0762SSepherosa Ziehau 
708f85e0762SSepherosa Ziehau 		/*
709f85e0762SSepherosa Ziehau 		 * Update TxS position.
710f85e0762SSepherosa Ziehau 		 */
711f85e0762SSepherosa Ziehau 		sc->txs_ack = (sc->txs_ack + 1) % AE_TXS_COUNT_DEFAULT;
712f85e0762SSepherosa Ziehau 		sc->ae_flags |= AE_FLAG_TXAVAIL;
713f85e0762SSepherosa Ziehau 		txd = (struct ae_txd *)(sc->txd_base + sc->txd_ack);
714f85e0762SSepherosa Ziehau 		if (txs->len != txd->len) {
715f85e0762SSepherosa Ziehau 			device_printf(sc->ae_dev, "Size mismatch: "
716f85e0762SSepherosa Ziehau 				"TxS:%d TxD:%d\n",
717f85e0762SSepherosa Ziehau 				le16toh(txs->len), le16toh(txd->len));
718f85e0762SSepherosa Ziehau 		}
719f85e0762SSepherosa Ziehau 
720f85e0762SSepherosa Ziehau 		/*
721f85e0762SSepherosa Ziehau 		 * Move txd ack and align on 4-byte boundary.
722f85e0762SSepherosa Ziehau 		 */
723f85e0762SSepherosa Ziehau 		sc->txd_ack = ((sc->txd_ack + le16toh(txd->len) + 4 + 3) & ~3) %
724f85e0762SSepherosa Ziehau 		    AE_TXD_BUFSIZE_DEFAULT;
725f85e0762SSepherosa Ziehau 		if ((flags & AE_TXS_SUCCESS) != 0)
726f85e0762SSepherosa Ziehau 			ifp->if_opackets++;
727f85e0762SSepherosa Ziehau 		else
728f85e0762SSepherosa Ziehau 			ifp->if_oerrors++;
729f85e0762SSepherosa Ziehau 		sc->tx_inproc--;
730f85e0762SSepherosa Ziehau 	}
731f85e0762SSepherosa Ziehau 
732f85e0762SSepherosa Ziehau 	if (sc->tx_inproc < 0) {
733f85e0762SSepherosa Ziehau 		/* XXX assert? */
734f85e0762SSepherosa Ziehau 		if_printf(ifp, "Received stray Tx interrupt(s).\n");
735f85e0762SSepherosa Ziehau 		sc->tx_inproc = 0;
736f85e0762SSepherosa Ziehau 	}
737f85e0762SSepherosa Ziehau 	if (sc->tx_inproc == 0)
738f85e0762SSepherosa Ziehau 		ifp->if_timer = 0;	/* Unarm watchdog. */
739f85e0762SSepherosa Ziehau 	if (sc->ae_flags & AE_FLAG_TXAVAIL) {
740f85e0762SSepherosa Ziehau 		ifp->if_flags &= ~IFF_OACTIVE;
741f85e0762SSepherosa Ziehau 		if (!ifq_is_empty(&ifp->if_snd))
742f85e0762SSepherosa Ziehau #ifdef foo
743f85e0762SSepherosa Ziehau 			ae_intr(sc);
744f85e0762SSepherosa Ziehau #else
745f85e0762SSepherosa Ziehau 			if_devstart(ifp);
746f85e0762SSepherosa Ziehau #endif
747f85e0762SSepherosa Ziehau 	}
748f85e0762SSepherosa Ziehau 
749f85e0762SSepherosa Ziehau 	/*
750f85e0762SSepherosa Ziehau 	 * Syncronize DMA buffers.
751f85e0762SSepherosa Ziehau 	 */
752f85e0762SSepherosa Ziehau 	bus_dmamap_sync(sc->dma_txd_tag, sc->dma_txd_map, BUS_DMASYNC_PREWRITE);
753f85e0762SSepherosa Ziehau 	bus_dmamap_sync(sc->dma_txs_tag, sc->dma_txs_map, BUS_DMASYNC_PREWRITE);
754f85e0762SSepherosa Ziehau }
755f85e0762SSepherosa Ziehau 
756f85e0762SSepherosa Ziehau static void
757f85e0762SSepherosa Ziehau ae_intr(void *xsc)
758f85e0762SSepherosa Ziehau {
759f85e0762SSepherosa Ziehau 	struct ae_softc *sc = xsc;
760f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
761f85e0762SSepherosa Ziehau 	uint32_t val;
762f85e0762SSepherosa Ziehau 
763f85e0762SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
764f85e0762SSepherosa Ziehau 
765f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_ISR_REG);
766f85e0762SSepherosa Ziehau 	if (val == 0 || (val & AE_IMR_DEFAULT) == 0)
767f85e0762SSepherosa Ziehau 		return;
768f85e0762SSepherosa Ziehau 
769f85e0762SSepherosa Ziehau #ifdef foo
770f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_ISR_REG, AE_ISR_DISABLE);
771f85e0762SSepherosa Ziehau #endif
772f85e0762SSepherosa Ziehau 
773f85e0762SSepherosa Ziehau 	/* Read interrupt status. */
774f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_ISR_REG);
775f85e0762SSepherosa Ziehau 
776f85e0762SSepherosa Ziehau 	/* Clear interrupts and disable them. */
777f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_ISR_REG, val | AE_ISR_DISABLE);
778f85e0762SSepherosa Ziehau 
779f85e0762SSepherosa Ziehau 	if (ifp->if_flags & IFF_RUNNING) {
780f85e0762SSepherosa Ziehau 		if (val & (AE_ISR_DMAR_TIMEOUT |
781f85e0762SSepherosa Ziehau 			   AE_ISR_DMAW_TIMEOUT |
782f85e0762SSepherosa Ziehau 			   AE_ISR_PHY_LINKDOWN)) {
783f85e0762SSepherosa Ziehau 			ae_init(sc);
784f85e0762SSepherosa Ziehau 		}
785f85e0762SSepherosa Ziehau 		if (val & AE_ISR_TX_EVENT)
786f85e0762SSepherosa Ziehau 			ae_tx_intr(sc);
787f85e0762SSepherosa Ziehau 		if (val & AE_ISR_RX_EVENT)
788f85e0762SSepherosa Ziehau 			ae_rx_intr(sc);
789f85e0762SSepherosa Ziehau 	}
790f85e0762SSepherosa Ziehau 
791f85e0762SSepherosa Ziehau 	/* Re-enable interrupts. */
792f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_ISR_REG, 0);
793f85e0762SSepherosa Ziehau }
794f85e0762SSepherosa Ziehau 
795f85e0762SSepherosa Ziehau static void
796f85e0762SSepherosa Ziehau ae_init(void *xsc)
797f85e0762SSepherosa Ziehau {
798f85e0762SSepherosa Ziehau 	struct ae_softc *sc = xsc;
799f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
800f85e0762SSepherosa Ziehau 	struct mii_data *mii;
801f85e0762SSepherosa Ziehau 	uint8_t eaddr[ETHER_ADDR_LEN];
802f85e0762SSepherosa Ziehau 	uint32_t val;
803f85e0762SSepherosa Ziehau 	bus_addr_t addr;
804f85e0762SSepherosa Ziehau 
805f85e0762SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
806f85e0762SSepherosa Ziehau 
807f85e0762SSepherosa Ziehau 	mii = device_get_softc(sc->ae_miibus);
808f85e0762SSepherosa Ziehau 	ae_stop(sc);
809f85e0762SSepherosa Ziehau 	ae_reset(sc);
810f85e0762SSepherosa Ziehau 	ae_pcie_init(sc);
811f85e0762SSepherosa Ziehau 	ae_powersave_disable(sc);
812f85e0762SSepherosa Ziehau 
813f85e0762SSepherosa Ziehau 	/*
814f85e0762SSepherosa Ziehau 	 * Clear and disable interrupts.
815f85e0762SSepherosa Ziehau 	 */
816f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_ISR_REG, 0xffffffff);
817f85e0762SSepherosa Ziehau 
818f85e0762SSepherosa Ziehau 	/*
819f85e0762SSepherosa Ziehau 	 * Set the MAC address.
820f85e0762SSepherosa Ziehau 	 */
821f85e0762SSepherosa Ziehau 	bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN);
822f85e0762SSepherosa Ziehau 	val = eaddr[2] << 24 | eaddr[3] << 16 | eaddr[4] << 8 | eaddr[5];
823f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_EADDR0_REG, val);
824f85e0762SSepherosa Ziehau 	val = eaddr[0] << 8 | eaddr[1];
825f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_EADDR1_REG, val);
826f85e0762SSepherosa Ziehau 
827f85e0762SSepherosa Ziehau 	/*
828f85e0762SSepherosa Ziehau 	 * Set ring buffers base addresses.
829f85e0762SSepherosa Ziehau 	 */
830f85e0762SSepherosa Ziehau 	addr = sc->dma_rxd_busaddr;
831f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_DESC_ADDR_HI_REG, BUS_ADDR_HI(addr));
832f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_RXD_ADDR_LO_REG, BUS_ADDR_LO(addr));
833f85e0762SSepherosa Ziehau 	addr = sc->dma_txd_busaddr;
834f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_TXD_ADDR_LO_REG, BUS_ADDR_LO(addr));
835f85e0762SSepherosa Ziehau 	addr = sc->dma_txs_busaddr;
836f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_TXS_ADDR_LO_REG, BUS_ADDR_LO(addr));
837f85e0762SSepherosa Ziehau 
838f85e0762SSepherosa Ziehau 	/*
839f85e0762SSepherosa Ziehau 	 * Configure ring buffers sizes.
840f85e0762SSepherosa Ziehau 	 */
841f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_RXD_COUNT_REG, AE_RXD_COUNT_DEFAULT);
842f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_TXD_BUFSIZE_REG, AE_TXD_BUFSIZE_DEFAULT / 4);
843f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_TXS_COUNT_REG, AE_TXS_COUNT_DEFAULT);
844f85e0762SSepherosa Ziehau 
845f85e0762SSepherosa Ziehau 	/*
846f85e0762SSepherosa Ziehau 	 * Configure interframe gap parameters.
847f85e0762SSepherosa Ziehau 	 */
848f85e0762SSepherosa Ziehau 	val = ((AE_IFG_TXIPG_DEFAULT << AE_IFG_TXIPG_SHIFT) &
849f85e0762SSepherosa Ziehau 	    AE_IFG_TXIPG_MASK) |
850f85e0762SSepherosa Ziehau 	    ((AE_IFG_RXIPG_DEFAULT << AE_IFG_RXIPG_SHIFT) &
851f85e0762SSepherosa Ziehau 	    AE_IFG_RXIPG_MASK) |
852f85e0762SSepherosa Ziehau 	    ((AE_IFG_IPGR1_DEFAULT << AE_IFG_IPGR1_SHIFT) &
853f85e0762SSepherosa Ziehau 	    AE_IFG_IPGR1_MASK) |
854f85e0762SSepherosa Ziehau 	    ((AE_IFG_IPGR2_DEFAULT << AE_IFG_IPGR2_SHIFT) &
855f85e0762SSepherosa Ziehau 	    AE_IFG_IPGR2_MASK);
856f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_IFG_REG, val);
857f85e0762SSepherosa Ziehau 
858f85e0762SSepherosa Ziehau 	/*
859f85e0762SSepherosa Ziehau 	 * Configure half-duplex operation.
860f85e0762SSepherosa Ziehau 	 */
861f85e0762SSepherosa Ziehau 	val = ((AE_HDPX_LCOL_DEFAULT << AE_HDPX_LCOL_SHIFT) &
862f85e0762SSepherosa Ziehau 	    AE_HDPX_LCOL_MASK) |
863f85e0762SSepherosa Ziehau 	    ((AE_HDPX_RETRY_DEFAULT << AE_HDPX_RETRY_SHIFT) &
864f85e0762SSepherosa Ziehau 	    AE_HDPX_RETRY_MASK) |
865f85e0762SSepherosa Ziehau 	    ((AE_HDPX_ABEBT_DEFAULT << AE_HDPX_ABEBT_SHIFT) &
866f85e0762SSepherosa Ziehau 	    AE_HDPX_ABEBT_MASK) |
867f85e0762SSepherosa Ziehau 	    ((AE_HDPX_JAMIPG_DEFAULT << AE_HDPX_JAMIPG_SHIFT) &
868f85e0762SSepherosa Ziehau 	    AE_HDPX_JAMIPG_MASK) | AE_HDPX_EXC_EN;
869f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_HDPX_REG, val);
870f85e0762SSepherosa Ziehau 
871f85e0762SSepherosa Ziehau 	/*
872f85e0762SSepherosa Ziehau 	 * Configure interrupt moderate timer.
873f85e0762SSepherosa Ziehau 	 */
874f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_IMT_REG, AE_IMT_DEFAULT);
875f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_MASTER_REG);
876f85e0762SSepherosa Ziehau 	val |= AE_MASTER_IMT_EN;
877f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MASTER_REG, val);
878f85e0762SSepherosa Ziehau 
879f85e0762SSepherosa Ziehau 	/*
880f85e0762SSepherosa Ziehau 	 * Configure interrupt clearing timer.
881f85e0762SSepherosa Ziehau 	 */
882f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_ICT_REG, AE_ICT_DEFAULT);
883f85e0762SSepherosa Ziehau 
884f85e0762SSepherosa Ziehau 	/*
885f85e0762SSepherosa Ziehau 	 * Configure MTU.
886f85e0762SSepherosa Ziehau 	 */
887f85e0762SSepherosa Ziehau 	val = ifp->if_mtu + ETHER_HDR_LEN + sizeof(struct ether_vlan_header) +
888f85e0762SSepherosa Ziehau 	    ETHER_CRC_LEN;
889f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_MTU_REG, val);
890f85e0762SSepherosa Ziehau 
891f85e0762SSepherosa Ziehau 	/*
892f85e0762SSepherosa Ziehau 	 * Configure cut-through threshold.
893f85e0762SSepherosa Ziehau 	 */
894f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_CUT_THRESH_REG, AE_CUT_THRESH_DEFAULT);
895f85e0762SSepherosa Ziehau 
896f85e0762SSepherosa Ziehau 	/*
897f85e0762SSepherosa Ziehau 	 * Configure flow control.
898f85e0762SSepherosa Ziehau 	 */
899f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_FLOW_THRESH_HI_REG, (AE_RXD_COUNT_DEFAULT / 8) * 7);
900f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_FLOW_THRESH_LO_REG, (AE_RXD_COUNT_MIN / 8) >
901f85e0762SSepherosa Ziehau 	    (AE_RXD_COUNT_DEFAULT / 12) ? (AE_RXD_COUNT_MIN / 8) :
902f85e0762SSepherosa Ziehau 	    (AE_RXD_COUNT_DEFAULT / 12));
903f85e0762SSepherosa Ziehau 
904f85e0762SSepherosa Ziehau 	/*
905f85e0762SSepherosa Ziehau 	 * Init mailboxes.
906f85e0762SSepherosa Ziehau 	 */
907f85e0762SSepherosa Ziehau 	sc->txd_cur = sc->rxd_cur = 0;
908f85e0762SSepherosa Ziehau 	sc->txd_cur = sc->rxd_cur = 0;
909f85e0762SSepherosa Ziehau 	sc->txs_ack = sc->txd_ack = 0;
910f85e0762SSepherosa Ziehau 	sc->rxd_cur = 0;
911f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_MB_TXD_IDX_REG, sc->txd_cur);
912f85e0762SSepherosa Ziehau 	AE_WRITE_2(sc, AE_MB_RXD_IDX_REG, sc->rxd_cur);
913f85e0762SSepherosa Ziehau 	sc->tx_inproc = 0;
914f85e0762SSepherosa Ziehau 	sc->ae_flags |= AE_FLAG_TXAVAIL; /* Free Tx's available. */
915f85e0762SSepherosa Ziehau 
916f85e0762SSepherosa Ziehau 	/*
917f85e0762SSepherosa Ziehau 	 * Enable DMA.
918f85e0762SSepherosa Ziehau 	 */
919f85e0762SSepherosa Ziehau 	AE_WRITE_1(sc, AE_DMAREAD_REG, AE_DMAREAD_EN);
920f85e0762SSepherosa Ziehau 	AE_WRITE_1(sc, AE_DMAWRITE_REG, AE_DMAWRITE_EN);
921f85e0762SSepherosa Ziehau 
922f85e0762SSepherosa Ziehau 	/*
923f85e0762SSepherosa Ziehau 	 * Check if everything is OK.
924f85e0762SSepherosa Ziehau 	 */
925f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_ISR_REG);
926f85e0762SSepherosa Ziehau 	if ((val & AE_ISR_PHY_LINKDOWN) != 0) {
927f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "Initialization failed.\n");
928f85e0762SSepherosa Ziehau 		return;
929f85e0762SSepherosa Ziehau 	}
930f85e0762SSepherosa Ziehau 
931f85e0762SSepherosa Ziehau 	/*
932f85e0762SSepherosa Ziehau 	 * Clear interrupt status.
933f85e0762SSepherosa Ziehau 	 */
934f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_ISR_REG, 0x3fffffff);
935f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_ISR_REG, 0x0);
936f85e0762SSepherosa Ziehau 
937f85e0762SSepherosa Ziehau 	/*
938f85e0762SSepherosa Ziehau 	 * Enable interrupts.
939f85e0762SSepherosa Ziehau 	 */
940f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_MASTER_REG);
941f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MASTER_REG, val | AE_MASTER_MANUAL_INT);
942f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_IMR_REG, AE_IMR_DEFAULT);
943f85e0762SSepherosa Ziehau 
944f85e0762SSepherosa Ziehau 	/*
945f85e0762SSepherosa Ziehau 	 * Disable WOL.
946f85e0762SSepherosa Ziehau 	 */
947f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_WOL_REG, 0);
948f85e0762SSepherosa Ziehau 
949f85e0762SSepherosa Ziehau 	/*
950f85e0762SSepherosa Ziehau 	 * Configure MAC.
951f85e0762SSepherosa Ziehau 	 */
952f85e0762SSepherosa Ziehau 	val = AE_MAC_TX_CRC_EN | AE_MAC_TX_AUTOPAD |
953f85e0762SSepherosa Ziehau 	    AE_MAC_FULL_DUPLEX | AE_MAC_CLK_PHY |
954f85e0762SSepherosa Ziehau 	    AE_MAC_TX_FLOW_EN | AE_MAC_RX_FLOW_EN |
955f85e0762SSepherosa Ziehau 	    ((AE_HALFBUF_DEFAULT << AE_HALFBUF_SHIFT) & AE_HALFBUF_MASK) |
956f85e0762SSepherosa Ziehau 	    ((AE_MAC_PREAMBLE_DEFAULT << AE_MAC_PREAMBLE_SHIFT) &
957f85e0762SSepherosa Ziehau 	    AE_MAC_PREAMBLE_MASK);
958f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MAC_REG, val);
959f85e0762SSepherosa Ziehau 
960f85e0762SSepherosa Ziehau 	/*
961f85e0762SSepherosa Ziehau 	 * Configure Rx MAC.
962f85e0762SSepherosa Ziehau 	 */
963f85e0762SSepherosa Ziehau 	ae_rxfilter(sc);
964f85e0762SSepherosa Ziehau 	ae_rxvlan(sc);
965f85e0762SSepherosa Ziehau 
966f85e0762SSepherosa Ziehau 	/*
967f85e0762SSepherosa Ziehau 	 * Enable Tx/Rx.
968f85e0762SSepherosa Ziehau 	 */
969f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_MAC_REG);
970f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MAC_REG, val | AE_MAC_TX_EN | AE_MAC_RX_EN);
971f85e0762SSepherosa Ziehau 
972f85e0762SSepherosa Ziehau 	sc->ae_flags &= ~AE_FLAG_LINK;
973f85e0762SSepherosa Ziehau 	mii_mediachg(mii);	/* Switch to the current media. */
974f85e0762SSepherosa Ziehau 
975f85e0762SSepherosa Ziehau 	callout_reset(&sc->ae_tick_ch, hz, ae_tick, sc);
976f85e0762SSepherosa Ziehau 	ifp->if_flags |= IFF_RUNNING;
977f85e0762SSepherosa Ziehau 	ifp->if_flags &= ~IFF_OACTIVE;
978f85e0762SSepherosa Ziehau }
979f85e0762SSepherosa Ziehau 
980f85e0762SSepherosa Ziehau static void
981f85e0762SSepherosa Ziehau ae_watchdog(struct ifnet *ifp)
982f85e0762SSepherosa Ziehau {
983f85e0762SSepherosa Ziehau 	struct ae_softc *sc = ifp->if_softc;
984f85e0762SSepherosa Ziehau 
985f85e0762SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
986f85e0762SSepherosa Ziehau 
987f85e0762SSepherosa Ziehau 	if ((sc->ae_flags & AE_FLAG_LINK) == 0)
988f85e0762SSepherosa Ziehau 		if_printf(ifp, "watchdog timeout (missed link).\n");
989f85e0762SSepherosa Ziehau 	else
990f85e0762SSepherosa Ziehau 		if_printf(ifp, "watchdog timeout - resetting.\n");
991f85e0762SSepherosa Ziehau 	ifp->if_oerrors++;
992f85e0762SSepherosa Ziehau 
993f85e0762SSepherosa Ziehau 	ae_init(sc);
994f85e0762SSepherosa Ziehau 	if (!ifq_is_empty(&ifp->if_snd))
995f85e0762SSepherosa Ziehau 		if_devstart(ifp);
996f85e0762SSepherosa Ziehau }
997f85e0762SSepherosa Ziehau 
998f85e0762SSepherosa Ziehau static void
999f85e0762SSepherosa Ziehau ae_tick(void *xsc)
1000f85e0762SSepherosa Ziehau {
1001f85e0762SSepherosa Ziehau 	struct ae_softc *sc = xsc;
1002f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
1003f85e0762SSepherosa Ziehau 	struct mii_data *mii = device_get_softc(sc->ae_miibus);
1004f85e0762SSepherosa Ziehau 
1005f85e0762SSepherosa Ziehau 	lwkt_serialize_enter(ifp->if_serializer);
1006f85e0762SSepherosa Ziehau 	mii_tick(mii);
1007f85e0762SSepherosa Ziehau 	callout_reset(&sc->ae_tick_ch, hz, ae_tick, sc);
1008f85e0762SSepherosa Ziehau 	lwkt_serialize_exit(ifp->if_serializer);
1009f85e0762SSepherosa Ziehau }
1010f85e0762SSepherosa Ziehau 
1011f85e0762SSepherosa Ziehau static void
1012f85e0762SSepherosa Ziehau ae_rxvlan(struct ae_softc *sc)
1013f85e0762SSepherosa Ziehau {
1014f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
1015f85e0762SSepherosa Ziehau 	uint32_t val;
1016f85e0762SSepherosa Ziehau 
1017f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_MAC_REG);
1018f85e0762SSepherosa Ziehau 	val &= ~AE_MAC_RMVLAN_EN;
1019f85e0762SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING)
1020f85e0762SSepherosa Ziehau 		val |= AE_MAC_RMVLAN_EN;
1021f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MAC_REG, val);
1022f85e0762SSepherosa Ziehau }
1023f85e0762SSepherosa Ziehau 
1024f85e0762SSepherosa Ziehau static void
1025f85e0762SSepherosa Ziehau ae_rxfilter(struct ae_softc *sc)
1026f85e0762SSepherosa Ziehau {
1027f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
1028f85e0762SSepherosa Ziehau 	struct ifmultiaddr *ifma;
1029f85e0762SSepherosa Ziehau 	uint32_t crc;
1030f85e0762SSepherosa Ziehau 	uint32_t mchash[2];
1031f85e0762SSepherosa Ziehau 	uint32_t rxcfg;
1032f85e0762SSepherosa Ziehau 
1033f85e0762SSepherosa Ziehau 	rxcfg = AE_READ_4(sc, AE_MAC_REG);
1034f85e0762SSepherosa Ziehau 	rxcfg &= ~(AE_MAC_MCAST_EN | AE_MAC_BCAST_EN | AE_MAC_PROMISC_EN);
1035f85e0762SSepherosa Ziehau 	rxcfg |= AE_MAC_BCAST_EN;
1036f85e0762SSepherosa Ziehau 	if (ifp->if_flags & IFF_PROMISC)
1037f85e0762SSepherosa Ziehau 		rxcfg |= AE_MAC_PROMISC_EN;
1038f85e0762SSepherosa Ziehau 	if (ifp->if_flags & IFF_ALLMULTI)
1039f85e0762SSepherosa Ziehau 		rxcfg |= AE_MAC_MCAST_EN;
1040f85e0762SSepherosa Ziehau 
1041f85e0762SSepherosa Ziehau 	/*
1042f85e0762SSepherosa Ziehau 	 * Wipe old settings.
1043f85e0762SSepherosa Ziehau 	 */
1044f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_REG_MHT0, 0);
1045f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_REG_MHT1, 0);
1046f85e0762SSepherosa Ziehau 	if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) {
1047f85e0762SSepherosa Ziehau 		AE_WRITE_4(sc, AE_REG_MHT0, 0xffffffff);
1048f85e0762SSepherosa Ziehau 		AE_WRITE_4(sc, AE_REG_MHT1, 0xffffffff);
1049f85e0762SSepherosa Ziehau 		AE_WRITE_4(sc, AE_MAC_REG, rxcfg);
1050f85e0762SSepherosa Ziehau 		return;
1051f85e0762SSepherosa Ziehau 	}
1052f85e0762SSepherosa Ziehau 
1053f85e0762SSepherosa Ziehau 	/*
1054f85e0762SSepherosa Ziehau 	 * Load multicast tables.
1055f85e0762SSepherosa Ziehau 	 */
1056f85e0762SSepherosa Ziehau 	bzero(mchash, sizeof(mchash));
1057*441d34b2SSascha Wildner 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
1058f85e0762SSepherosa Ziehau 		if (ifma->ifma_addr->sa_family != AF_LINK)
1059f85e0762SSepherosa Ziehau 			continue;
1060f85e0762SSepherosa Ziehau 		crc = ether_crc32_le(LLADDR((struct sockaddr_dl *)
1061f85e0762SSepherosa Ziehau 			ifma->ifma_addr), ETHER_ADDR_LEN);
1062f85e0762SSepherosa Ziehau 		mchash[crc >> 31] |= 1 << ((crc >> 26) & 0x1f);
1063f85e0762SSepherosa Ziehau 	}
1064f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_REG_MHT0, mchash[0]);
1065f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_REG_MHT1, mchash[1]);
1066f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MAC_REG, rxcfg);
1067f85e0762SSepherosa Ziehau }
1068f85e0762SSepherosa Ziehau 
1069f85e0762SSepherosa Ziehau static unsigned int
1070f85e0762SSepherosa Ziehau ae_tx_avail_size(struct ae_softc *sc)
1071f85e0762SSepherosa Ziehau {
1072f85e0762SSepherosa Ziehau 	unsigned int avail;
1073f85e0762SSepherosa Ziehau 
1074f85e0762SSepherosa Ziehau 	if (sc->txd_cur >= sc->txd_ack)
1075f85e0762SSepherosa Ziehau 		avail = AE_TXD_BUFSIZE_DEFAULT - (sc->txd_cur - sc->txd_ack);
1076f85e0762SSepherosa Ziehau 	else
1077f85e0762SSepherosa Ziehau 		avail = sc->txd_ack - sc->txd_cur;
1078f85e0762SSepherosa Ziehau 	return (avail - 4);     /* 4-byte header. */
1079f85e0762SSepherosa Ziehau }
1080f85e0762SSepherosa Ziehau 
1081f85e0762SSepherosa Ziehau static int
1082f85e0762SSepherosa Ziehau ae_encap(struct ae_softc *sc, struct mbuf **m_head)
1083f85e0762SSepherosa Ziehau {
1084f85e0762SSepherosa Ziehau 	struct mbuf *m0;
1085f85e0762SSepherosa Ziehau 	struct ae_txd *hdr;
1086f85e0762SSepherosa Ziehau 	unsigned int to_end;
1087f85e0762SSepherosa Ziehau 	uint16_t len;
1088f85e0762SSepherosa Ziehau 
1089f85e0762SSepherosa Ziehau 	M_ASSERTPKTHDR((*m_head));
1090f85e0762SSepherosa Ziehau 	m0 = *m_head;
1091f85e0762SSepherosa Ziehau 	len = m0->m_pkthdr.len;
1092f85e0762SSepherosa Ziehau 	if ((sc->ae_flags & AE_FLAG_TXAVAIL) == 0 ||
1093f85e0762SSepherosa Ziehau 	    ae_tx_avail_size(sc) < len) {
1094f85e0762SSepherosa Ziehau #ifdef AE_DEBUG
1095f85e0762SSepherosa Ziehau 		if_printf(sc->ifp, "No free Tx available.\n");
1096f85e0762SSepherosa Ziehau #endif
1097f85e0762SSepherosa Ziehau 		return ENOBUFS;
1098f85e0762SSepherosa Ziehau 	}
1099f85e0762SSepherosa Ziehau 
1100f85e0762SSepherosa Ziehau 	hdr = (struct ae_txd *)(sc->txd_base + sc->txd_cur);
1101f85e0762SSepherosa Ziehau 	bzero(hdr, sizeof(*hdr));
1102f85e0762SSepherosa Ziehau 
1103f85e0762SSepherosa Ziehau 	/* Header size. */
1104f85e0762SSepherosa Ziehau 	sc->txd_cur = (sc->txd_cur + 4) % AE_TXD_BUFSIZE_DEFAULT;
1105f85e0762SSepherosa Ziehau 
1106f85e0762SSepherosa Ziehau 	/* Space available to the end of the ring */
1107f85e0762SSepherosa Ziehau 	to_end = AE_TXD_BUFSIZE_DEFAULT - sc->txd_cur;
1108f85e0762SSepherosa Ziehau 
1109f85e0762SSepherosa Ziehau 	if (to_end >= len) {
1110f85e0762SSepherosa Ziehau 		m_copydata(m0, 0, len, (caddr_t)(sc->txd_base + sc->txd_cur));
1111f85e0762SSepherosa Ziehau 	} else {
1112f85e0762SSepherosa Ziehau 		m_copydata(m0, 0, to_end, (caddr_t)(sc->txd_base +
1113f85e0762SSepherosa Ziehau 		    sc->txd_cur));
1114f85e0762SSepherosa Ziehau 		m_copydata(m0, to_end, len - to_end, (caddr_t)sc->txd_base);
1115f85e0762SSepherosa Ziehau 	}
1116f85e0762SSepherosa Ziehau 
1117f85e0762SSepherosa Ziehau 	/*
1118f85e0762SSepherosa Ziehau 	 * Set TxD flags and parameters.
1119f85e0762SSepherosa Ziehau 	 */
1120f85e0762SSepherosa Ziehau 	if ((m0->m_flags & M_VLANTAG) != 0) {
1121f85e0762SSepherosa Ziehau 		hdr->vlan = htole16(AE_TXD_VLAN(m0->m_pkthdr.ether_vlantag));
1122f85e0762SSepherosa Ziehau 		hdr->len = htole16(len | AE_TXD_INSERT_VTAG);
1123f85e0762SSepherosa Ziehau 	} else {
1124f85e0762SSepherosa Ziehau 		hdr->len = htole16(len);
1125f85e0762SSepherosa Ziehau 	}
1126f85e0762SSepherosa Ziehau 
1127f85e0762SSepherosa Ziehau 	/*
1128f85e0762SSepherosa Ziehau 	 * Set current TxD position and round up to a 4-byte boundary.
1129f85e0762SSepherosa Ziehau 	 */
1130f85e0762SSepherosa Ziehau 	sc->txd_cur = ((sc->txd_cur + len + 3) & ~3) % AE_TXD_BUFSIZE_DEFAULT;
1131f85e0762SSepherosa Ziehau 	if (sc->txd_cur == sc->txd_ack)
1132f85e0762SSepherosa Ziehau 		sc->ae_flags &= ~AE_FLAG_TXAVAIL;
1133f85e0762SSepherosa Ziehau #ifdef AE_DEBUG
1134f85e0762SSepherosa Ziehau 	if_printf(sc->ifp, "New txd_cur = %d.\n", sc->txd_cur);
1135f85e0762SSepherosa Ziehau #endif
1136f85e0762SSepherosa Ziehau 
1137f85e0762SSepherosa Ziehau 	/*
1138f85e0762SSepherosa Ziehau 	 * Update TxS position and check if there are empty TxS available.
1139f85e0762SSepherosa Ziehau 	 */
1140f85e0762SSepherosa Ziehau 	sc->txs_base[sc->txs_cur].flags &= ~htole16(AE_TXS_UPDATE);
1141f85e0762SSepherosa Ziehau 	sc->txs_cur = (sc->txs_cur + 1) % AE_TXS_COUNT_DEFAULT;
1142f85e0762SSepherosa Ziehau 	if (sc->txs_cur == sc->txs_ack)
1143f85e0762SSepherosa Ziehau 		sc->ae_flags &= ~AE_FLAG_TXAVAIL;
1144f85e0762SSepherosa Ziehau 
1145f85e0762SSepherosa Ziehau 	/*
1146f85e0762SSepherosa Ziehau 	 * Synchronize DMA memory.
1147f85e0762SSepherosa Ziehau 	 */
1148f85e0762SSepherosa Ziehau 	bus_dmamap_sync(sc->dma_txd_tag, sc->dma_txd_map, BUS_DMASYNC_PREWRITE);
1149f85e0762SSepherosa Ziehau 	bus_dmamap_sync(sc->dma_txs_tag, sc->dma_txs_map, BUS_DMASYNC_PREWRITE);
1150f85e0762SSepherosa Ziehau 
1151f85e0762SSepherosa Ziehau 	return (0);
1152f85e0762SSepherosa Ziehau }
1153f85e0762SSepherosa Ziehau 
1154f85e0762SSepherosa Ziehau static void
1155f85e0762SSepherosa Ziehau ae_start(struct ifnet *ifp)
1156f85e0762SSepherosa Ziehau {
1157f85e0762SSepherosa Ziehau 	struct ae_softc *sc = ifp->if_softc;
1158f85e0762SSepherosa Ziehau 	int error, trans;
1159f85e0762SSepherosa Ziehau 
1160f85e0762SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
1161f85e0762SSepherosa Ziehau 
1162f85e0762SSepherosa Ziehau #ifdef AE_DEBUG
1163f85e0762SSepherosa Ziehau 	if_printf(ifp, "Start called.\n");
1164f85e0762SSepherosa Ziehau #endif
1165f85e0762SSepherosa Ziehau 	if ((sc->ae_flags & AE_FLAG_LINK) == 0) {
1166f85e0762SSepherosa Ziehau 		ifq_purge(&ifp->if_snd);
1167f85e0762SSepherosa Ziehau 		return;
1168f85e0762SSepherosa Ziehau 	}
1169f85e0762SSepherosa Ziehau 	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
1170f85e0762SSepherosa Ziehau 		return;
1171f85e0762SSepherosa Ziehau 
1172f85e0762SSepherosa Ziehau 	trans = 0;
1173f85e0762SSepherosa Ziehau 	while (!ifq_is_empty(&ifp->if_snd)) {
1174f85e0762SSepherosa Ziehau 		struct mbuf *m0;
1175f85e0762SSepherosa Ziehau 
1176f85e0762SSepherosa Ziehau 		m0 = ifq_dequeue(&ifp->if_snd, NULL);
1177f85e0762SSepherosa Ziehau 		if (m0 == NULL)
1178f85e0762SSepherosa Ziehau 			break;  /* Nothing to do. */
1179f85e0762SSepherosa Ziehau 
1180f85e0762SSepherosa Ziehau 		error = ae_encap(sc, &m0);
1181f85e0762SSepherosa Ziehau 		if (error != 0) {
1182f85e0762SSepherosa Ziehau 			if (m0 != NULL) {
1183f85e0762SSepherosa Ziehau 				ifq_prepend(&ifp->if_snd, m0);
1184f85e0762SSepherosa Ziehau 				ifp->if_flags |= IFF_OACTIVE;
1185f85e0762SSepherosa Ziehau #ifdef AE_DEBUG
1186f85e0762SSepherosa Ziehau 				if_printf(ifp, "Setting OACTIVE.\n");
1187f85e0762SSepherosa Ziehau #endif
1188f85e0762SSepherosa Ziehau 			}
1189f85e0762SSepherosa Ziehau 			break;
1190f85e0762SSepherosa Ziehau 		}
1191f85e0762SSepherosa Ziehau 		trans = 1;
1192f85e0762SSepherosa Ziehau 		sc->tx_inproc++;
1193f85e0762SSepherosa Ziehau 
1194f85e0762SSepherosa Ziehau 		/* Bounce a copy of the frame to BPF. */
1195f85e0762SSepherosa Ziehau 		ETHER_BPF_MTAP(ifp, m0);
1196f85e0762SSepherosa Ziehau 		m_freem(m0);
1197f85e0762SSepherosa Ziehau 	}
1198f85e0762SSepherosa Ziehau 	if (trans) {	/* Something was dequeued. */
1199f85e0762SSepherosa Ziehau 		AE_WRITE_2(sc, AE_MB_TXD_IDX_REG, sc->txd_cur / 4);
1200f85e0762SSepherosa Ziehau 		ifp->if_timer = AE_TX_TIMEOUT; /* Load watchdog. */
1201f85e0762SSepherosa Ziehau #ifdef AE_DEBUG
1202f85e0762SSepherosa Ziehau 		if_printf(ifp, "%d packets dequeued.\n", count);
1203f85e0762SSepherosa Ziehau 		if_printf(ifp, "Tx pos now is %d.\n", sc->txd_cur);
1204f85e0762SSepherosa Ziehau #endif
1205f85e0762SSepherosa Ziehau 	}
1206f85e0762SSepherosa Ziehau }
1207f85e0762SSepherosa Ziehau 
1208f85e0762SSepherosa Ziehau static int
1209f85e0762SSepherosa Ziehau ae_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
1210f85e0762SSepherosa Ziehau {
1211f85e0762SSepherosa Ziehau         struct ae_softc *sc = ifp->if_softc;
1212f85e0762SSepherosa Ziehau         struct ifreq *ifr;
1213f85e0762SSepherosa Ziehau         struct mii_data *mii;
1214f85e0762SSepherosa Ziehau         int error = 0, mask;
1215f85e0762SSepherosa Ziehau 
1216f85e0762SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
1217f85e0762SSepherosa Ziehau 
1218f85e0762SSepherosa Ziehau 	ifr = (struct ifreq *)data;
1219f85e0762SSepherosa Ziehau 	switch (cmd) {
1220f85e0762SSepherosa Ziehau 	case SIOCSIFFLAGS:
1221f85e0762SSepherosa Ziehau 		if (ifp->if_flags & IFF_UP) {
1222f85e0762SSepherosa Ziehau 			if (ifp->if_flags & IFF_RUNNING) {
1223f85e0762SSepherosa Ziehau 				if (((ifp->if_flags ^ sc->ae_if_flags)
1224f85e0762SSepherosa Ziehau 				    & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
1225f85e0762SSepherosa Ziehau 					ae_rxfilter(sc);
1226f85e0762SSepherosa Ziehau 			} else {
1227f85e0762SSepherosa Ziehau 				ae_init(sc);
1228f85e0762SSepherosa Ziehau 			}
1229f85e0762SSepherosa Ziehau 		} else {
1230f85e0762SSepherosa Ziehau 			if (ifp->if_flags & IFF_RUNNING)
1231f85e0762SSepherosa Ziehau 				ae_stop(sc);
1232f85e0762SSepherosa Ziehau 		}
1233f85e0762SSepherosa Ziehau 		sc->ae_if_flags = ifp->if_flags;
1234f85e0762SSepherosa Ziehau 		break;
1235f85e0762SSepherosa Ziehau 
1236f85e0762SSepherosa Ziehau 	case SIOCADDMULTI:
1237f85e0762SSepherosa Ziehau 	case SIOCDELMULTI:
1238f85e0762SSepherosa Ziehau 		if (ifp->if_flags & IFF_RUNNING)
1239f85e0762SSepherosa Ziehau 			ae_rxfilter(sc);
1240f85e0762SSepherosa Ziehau 		break;
1241f85e0762SSepherosa Ziehau 
1242f85e0762SSepherosa Ziehau 	case SIOCSIFMEDIA:
1243f85e0762SSepherosa Ziehau 	case SIOCGIFMEDIA:
1244f85e0762SSepherosa Ziehau 		mii = device_get_softc(sc->ae_miibus);
1245f85e0762SSepherosa Ziehau 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
1246f85e0762SSepherosa Ziehau 		break;
1247f85e0762SSepherosa Ziehau 
1248f85e0762SSepherosa Ziehau 	case SIOCSIFCAP:
1249f85e0762SSepherosa Ziehau 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
1250f85e0762SSepherosa Ziehau 		if (mask & IFCAP_VLAN_HWTAGGING) {
1251f85e0762SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
1252f85e0762SSepherosa Ziehau 			ae_rxvlan(sc);
1253f85e0762SSepherosa Ziehau 		}
1254f85e0762SSepherosa Ziehau 		break;
1255f85e0762SSepherosa Ziehau 
1256f85e0762SSepherosa Ziehau 	default:
1257f85e0762SSepherosa Ziehau 		error = ether_ioctl(ifp, cmd, data);
1258f85e0762SSepherosa Ziehau 		break;
1259f85e0762SSepherosa Ziehau 	}
1260f85e0762SSepherosa Ziehau 	return (error);
1261f85e0762SSepherosa Ziehau }
1262f85e0762SSepherosa Ziehau 
1263f85e0762SSepherosa Ziehau static int
1264f85e0762SSepherosa Ziehau ae_attach(device_t dev)
1265f85e0762SSepherosa Ziehau {
1266f85e0762SSepherosa Ziehau 	struct ae_softc *sc = device_get_softc(dev);
1267f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
1268f85e0762SSepherosa Ziehau 	int error = 0;
1269f85e0762SSepherosa Ziehau 
1270f85e0762SSepherosa Ziehau 	sc->ae_dev = dev;
1271f85e0762SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
1272f85e0762SSepherosa Ziehau 	callout_init(&sc->ae_tick_ch);
1273f85e0762SSepherosa Ziehau 
1274f85e0762SSepherosa Ziehau 	/* Enable bus mastering */
1275f85e0762SSepherosa Ziehau 	pci_enable_busmaster(dev);
1276f85e0762SSepherosa Ziehau 
1277f85e0762SSepherosa Ziehau 	/*
1278f85e0762SSepherosa Ziehau 	 * Allocate memory mapped IO
1279f85e0762SSepherosa Ziehau 	 */
1280f85e0762SSepherosa Ziehau 	sc->ae_mem_rid = PCIR_BAR(0);
1281f85e0762SSepherosa Ziehau 	sc->ae_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
1282f85e0762SSepherosa Ziehau 						&sc->ae_mem_rid, RF_ACTIVE);
1283f85e0762SSepherosa Ziehau 	if (sc->ae_mem_res == NULL) {
1284f85e0762SSepherosa Ziehau 		device_printf(dev, "can't allocate IO memory\n");
1285f85e0762SSepherosa Ziehau 		return ENXIO;
1286f85e0762SSepherosa Ziehau 	}
1287f85e0762SSepherosa Ziehau 	sc->ae_mem_bt = rman_get_bustag(sc->ae_mem_res);
1288f85e0762SSepherosa Ziehau 	sc->ae_mem_bh = rman_get_bushandle(sc->ae_mem_res);
1289f85e0762SSepherosa Ziehau 
1290f85e0762SSepherosa Ziehau 	/*
1291f85e0762SSepherosa Ziehau 	 * Allocate IRQ
1292f85e0762SSepherosa Ziehau 	 */
1293f85e0762SSepherosa Ziehau 	sc->ae_irq_rid = 0;
1294f85e0762SSepherosa Ziehau 	sc->ae_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
1295f85e0762SSepherosa Ziehau 						&sc->ae_irq_rid,
1296f85e0762SSepherosa Ziehau 						RF_SHAREABLE | RF_ACTIVE);
1297f85e0762SSepherosa Ziehau 	if (sc->ae_irq_res == NULL) {
1298f85e0762SSepherosa Ziehau 		device_printf(dev, "can't allocate irq\n");
1299f85e0762SSepherosa Ziehau 		error = ENXIO;
1300f85e0762SSepherosa Ziehau 		goto fail;
1301f85e0762SSepherosa Ziehau 	}
1302f85e0762SSepherosa Ziehau 
1303f85e0762SSepherosa Ziehau 	/* Set PHY address. */
1304f85e0762SSepherosa Ziehau 	sc->ae_phyaddr = AE_PHYADDR_DEFAULT;
1305f85e0762SSepherosa Ziehau 
1306f85e0762SSepherosa Ziehau 	/* Create sysctl tree */
1307f85e0762SSepherosa Ziehau 	ae_sysctl_node(sc);
1308f85e0762SSepherosa Ziehau 
1309f85e0762SSepherosa Ziehau 	/* Reset PHY. */
1310f85e0762SSepherosa Ziehau 	ae_phy_reset(sc);
1311f85e0762SSepherosa Ziehau 
1312f85e0762SSepherosa Ziehau 	/*
1313f85e0762SSepherosa Ziehau 	 * Reset the ethernet controller.
1314f85e0762SSepherosa Ziehau 	 */
1315f85e0762SSepherosa Ziehau 	ae_reset(sc);
1316f85e0762SSepherosa Ziehau 	ae_pcie_init(sc);
1317f85e0762SSepherosa Ziehau 
1318f85e0762SSepherosa Ziehau 	/*
1319f85e0762SSepherosa Ziehau 	 * Get PCI and chip id/revision.
1320f85e0762SSepherosa Ziehau 	 */
1321f85e0762SSepherosa Ziehau 	sc->ae_rev = pci_get_revid(dev);
1322f85e0762SSepherosa Ziehau 	sc->ae_chip_rev =
1323f85e0762SSepherosa Ziehau 	(AE_READ_4(sc, AE_MASTER_REG) >> AE_MASTER_REVNUM_SHIFT) &
1324f85e0762SSepherosa Ziehau 	AE_MASTER_REVNUM_MASK;
1325f85e0762SSepherosa Ziehau 	if (bootverbose) {
1326f85e0762SSepherosa Ziehau 		device_printf(dev, "PCI device revision : 0x%04x\n", sc->ae_rev);
1327f85e0762SSepherosa Ziehau 		device_printf(dev, "Chip id/revision : 0x%04x\n",
1328f85e0762SSepherosa Ziehau 		    sc->ae_chip_rev);
1329f85e0762SSepherosa Ziehau 	}
1330f85e0762SSepherosa Ziehau 
1331f85e0762SSepherosa Ziehau 	/*
1332f85e0762SSepherosa Ziehau 	 * XXX
1333f85e0762SSepherosa Ziehau 	 * Unintialized hardware returns an invalid chip id/revision
1334f85e0762SSepherosa Ziehau 	 * as well as 0xFFFFFFFF for Tx/Rx fifo length. It seems that
1335f85e0762SSepherosa Ziehau 	 * unplugged cable results in putting hardware into automatic
1336f85e0762SSepherosa Ziehau 	 * power down mode which in turn returns invalld chip revision.
1337f85e0762SSepherosa Ziehau 	 */
1338f85e0762SSepherosa Ziehau 	if (sc->ae_chip_rev == 0xFFFF) {
1339f85e0762SSepherosa Ziehau 		device_printf(dev,"invalid chip revision : 0x%04x -- "
1340f85e0762SSepherosa Ziehau 		    "not initialized?\n", sc->ae_chip_rev);
1341f85e0762SSepherosa Ziehau 		error = ENXIO;
1342f85e0762SSepherosa Ziehau 		goto fail;
1343f85e0762SSepherosa Ziehau 	}
1344f85e0762SSepherosa Ziehau #if 0
1345f85e0762SSepherosa Ziehau 	/* Get DMA parameters from PCIe device control register. */
1346f85e0762SSepherosa Ziehau 	pcie_ptr = pci_get_pciecap_ptr(dev);
1347f85e0762SSepherosa Ziehau 	if (pcie_ptr) {
1348f85e0762SSepherosa Ziehau 		uint16_t devctl;
1349f85e0762SSepherosa Ziehau 		sc->ae_flags |= AE_FLAG_PCIE;
1350f85e0762SSepherosa Ziehau 		devctl = pci_read_config(dev, pcie_ptr + PCIER_DEVCTRL, 2);
1351f85e0762SSepherosa Ziehau 		/* Max read request size. */
1352f85e0762SSepherosa Ziehau 		sc->ae_dma_rd_burst = ((devctl >> 12) & 0x07) <<
1353f85e0762SSepherosa Ziehau 		    DMA_CFG_RD_BURST_SHIFT;
1354f85e0762SSepherosa Ziehau 		/* Max payload size. */
1355f85e0762SSepherosa Ziehau 		sc->ae_dma_wr_burst = ((devctl >> 5) & 0x07) <<
1356f85e0762SSepherosa Ziehau 		    DMA_CFG_WR_BURST_SHIFT;
1357f85e0762SSepherosa Ziehau 		if (bootverbose) {
1358f85e0762SSepherosa Ziehau 			device_printf(dev, "Read request size : %d bytes.\n",
1359f85e0762SSepherosa Ziehau 			    128 << ((devctl >> 12) & 0x07));
1360f85e0762SSepherosa Ziehau 			device_printf(dev, "TLP payload size : %d bytes.\n",
1361f85e0762SSepherosa Ziehau 			    128 << ((devctl >> 5) & 0x07));
1362f85e0762SSepherosa Ziehau 		}
1363f85e0762SSepherosa Ziehau 	} else {
1364f85e0762SSepherosa Ziehau 		sc->ae_dma_rd_burst = DMA_CFG_RD_BURST_128;
1365f85e0762SSepherosa Ziehau 		sc->ae_dma_wr_burst = DMA_CFG_WR_BURST_128;
1366f85e0762SSepherosa Ziehau 	}
1367f85e0762SSepherosa Ziehau #endif
1368f85e0762SSepherosa Ziehau 
1369f85e0762SSepherosa Ziehau 	/* Create DMA stuffs */
1370f85e0762SSepherosa Ziehau 	error = ae_dma_alloc(sc);
1371f85e0762SSepherosa Ziehau 	if (error)
1372f85e0762SSepherosa Ziehau 		goto fail;
1373f85e0762SSepherosa Ziehau 
1374f85e0762SSepherosa Ziehau 	/* Load station address. */
1375f85e0762SSepherosa Ziehau 	ae_get_eaddr(sc);
1376f85e0762SSepherosa Ziehau 
1377f85e0762SSepherosa Ziehau 	ifp->if_softc = sc;
1378f85e0762SSepherosa Ziehau 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
1379f85e0762SSepherosa Ziehau 	ifp->if_ioctl = ae_ioctl;
1380f85e0762SSepherosa Ziehau 	ifp->if_start = ae_start;
1381f85e0762SSepherosa Ziehau 	ifp->if_init = ae_init;
1382f85e0762SSepherosa Ziehau 	ifp->if_watchdog = ae_watchdog;
1383f85e0762SSepherosa Ziehau 	ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN - 1);
1384f85e0762SSepherosa Ziehau 	ifq_set_ready(&ifp->if_snd);
1385f85e0762SSepherosa Ziehau 	ifp->if_capabilities = IFCAP_VLAN_MTU |
1386f85e0762SSepherosa Ziehau 			       IFCAP_VLAN_HWTAGGING;
1387f85e0762SSepherosa Ziehau 	ifp->if_hwassist = 0;
1388f85e0762SSepherosa Ziehau 	ifp->if_capenable = ifp->if_capabilities;
1389f85e0762SSepherosa Ziehau 
1390f85e0762SSepherosa Ziehau 	/* Set up MII bus. */
1391f85e0762SSepherosa Ziehau 	error = mii_phy_probe(dev, &sc->ae_miibus,
1392f85e0762SSepherosa Ziehau 			      ae_mediachange, ae_mediastatus);
1393f85e0762SSepherosa Ziehau 	if (error) {
1394f85e0762SSepherosa Ziehau 		device_printf(dev, "no PHY found!\n");
1395f85e0762SSepherosa Ziehau 		goto fail;
1396f85e0762SSepherosa Ziehau 	}
1397f85e0762SSepherosa Ziehau 	ether_ifattach(ifp, sc->ae_eaddr, NULL);
1398f85e0762SSepherosa Ziehau 
1399f85e0762SSepherosa Ziehau 	/* Tell the upper layer(s) we support long frames. */
1400f85e0762SSepherosa Ziehau 	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
1401f85e0762SSepherosa Ziehau 
1402f85e0762SSepherosa Ziehau 	error = bus_setup_intr(dev, sc->ae_irq_res, INTR_MPSAFE, ae_intr, sc,
1403f85e0762SSepherosa Ziehau 			       &sc->ae_irq_handle, ifp->if_serializer);
1404f85e0762SSepherosa Ziehau 	if (error) {
1405f85e0762SSepherosa Ziehau 		device_printf(dev, "could not set up interrupt handler.\n");
1406f85e0762SSepherosa Ziehau 		ether_ifdetach(ifp);
1407f85e0762SSepherosa Ziehau 		goto fail;
1408f85e0762SSepherosa Ziehau 	}
1409f85e0762SSepherosa Ziehau 	ifp->if_cpuid = ithread_cpuid(rman_get_start(sc->ae_irq_res));
1410f85e0762SSepherosa Ziehau 	KKASSERT(ifp->if_cpuid >= 0 && ifp->if_cpuid < ncpus);
1411f85e0762SSepherosa Ziehau 	return 0;
1412f85e0762SSepherosa Ziehau fail:
1413f85e0762SSepherosa Ziehau 	ae_detach(dev);
1414f85e0762SSepherosa Ziehau 	return (error);
1415f85e0762SSepherosa Ziehau }
1416f85e0762SSepherosa Ziehau 
1417f85e0762SSepherosa Ziehau static int
1418f85e0762SSepherosa Ziehau ae_detach(device_t dev)
1419f85e0762SSepherosa Ziehau {
1420f85e0762SSepherosa Ziehau 	struct ae_softc *sc = device_get_softc(dev);
1421f85e0762SSepherosa Ziehau 
1422f85e0762SSepherosa Ziehau 	if (device_is_attached(dev)) {
1423f85e0762SSepherosa Ziehau 		struct ifnet *ifp = &sc->arpcom.ac_if;
1424f85e0762SSepherosa Ziehau 
1425f85e0762SSepherosa Ziehau 		lwkt_serialize_enter(ifp->if_serializer);
1426f85e0762SSepherosa Ziehau 		sc->ae_flags |= AE_FLAG_DETACH;
1427f85e0762SSepherosa Ziehau 		ae_stop(sc);
1428f85e0762SSepherosa Ziehau 		bus_teardown_intr(dev, sc->ae_irq_res, sc->ae_irq_handle);
1429f85e0762SSepherosa Ziehau 		lwkt_serialize_exit(ifp->if_serializer);
1430f85e0762SSepherosa Ziehau 
1431f85e0762SSepherosa Ziehau 		ether_ifdetach(ifp);
1432f85e0762SSepherosa Ziehau 	}
1433f85e0762SSepherosa Ziehau 
1434f85e0762SSepherosa Ziehau 	if (sc->ae_miibus != NULL)
1435f85e0762SSepherosa Ziehau 		device_delete_child(dev, sc->ae_miibus);
1436f85e0762SSepherosa Ziehau 	bus_generic_detach(dev);
1437f85e0762SSepherosa Ziehau 
1438f85e0762SSepherosa Ziehau 	if (sc->ae_irq_res != NULL) {
1439f85e0762SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_IRQ, sc->ae_irq_rid,
1440f85e0762SSepherosa Ziehau 				     sc->ae_irq_res);
1441f85e0762SSepherosa Ziehau 	}
1442f85e0762SSepherosa Ziehau 	if (sc->ae_mem_res != NULL) {
1443f85e0762SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, sc->ae_mem_rid,
1444f85e0762SSepherosa Ziehau 				     sc->ae_mem_res);
1445f85e0762SSepherosa Ziehau 	}
1446f85e0762SSepherosa Ziehau 
1447f85e0762SSepherosa Ziehau 	if (sc->ae_sysctl_tree != NULL)
1448f85e0762SSepherosa Ziehau 		sysctl_ctx_free(&sc->ae_sysctl_ctx);
1449f85e0762SSepherosa Ziehau 
1450f85e0762SSepherosa Ziehau 	ae_dma_free(sc);
1451f85e0762SSepherosa Ziehau 
1452f85e0762SSepherosa Ziehau 	return (0);
1453f85e0762SSepherosa Ziehau }
1454f85e0762SSepherosa Ziehau 
1455f85e0762SSepherosa Ziehau static void
1456f85e0762SSepherosa Ziehau ae_dma_free(struct ae_softc *sc)
1457f85e0762SSepherosa Ziehau {
1458f85e0762SSepherosa Ziehau 	if (sc->dma_txd_tag != NULL) {
1459f85e0762SSepherosa Ziehau 		bus_dmamap_unload(sc->dma_txd_tag, sc->dma_txd_map);
1460f85e0762SSepherosa Ziehau 		bus_dmamem_free(sc->dma_txd_tag, sc->txd_base,
1461f85e0762SSepherosa Ziehau 		    sc->dma_txd_map);
1462f85e0762SSepherosa Ziehau 		bus_dma_tag_destroy(sc->dma_txd_tag);
1463f85e0762SSepherosa Ziehau 	}
1464f85e0762SSepherosa Ziehau 	if (sc->dma_txs_tag != NULL) {
1465f85e0762SSepherosa Ziehau 		bus_dmamap_unload(sc->dma_txs_tag, sc->dma_txs_map);
1466f85e0762SSepherosa Ziehau 		bus_dmamem_free(sc->dma_txs_tag, sc->txs_base,
1467f85e0762SSepherosa Ziehau 		    sc->dma_txs_map);
1468f85e0762SSepherosa Ziehau 		bus_dma_tag_destroy(sc->dma_txs_tag);
1469f85e0762SSepherosa Ziehau 	}
1470f85e0762SSepherosa Ziehau 	if (sc->dma_rxd_tag != NULL) {
1471f85e0762SSepherosa Ziehau 		bus_dmamap_unload(sc->dma_rxd_tag, sc->dma_rxd_map);
1472f85e0762SSepherosa Ziehau 		bus_dmamem_free(sc->dma_rxd_tag,
1473f85e0762SSepherosa Ziehau 		    sc->rxd_base_dma, sc->dma_rxd_map);
1474f85e0762SSepherosa Ziehau 		bus_dma_tag_destroy(sc->dma_rxd_tag);
1475f85e0762SSepherosa Ziehau 	}
1476f85e0762SSepherosa Ziehau 	if (sc->dma_parent_tag != NULL)
1477f85e0762SSepherosa Ziehau 		bus_dma_tag_destroy(sc->dma_parent_tag);
1478f85e0762SSepherosa Ziehau }
1479f85e0762SSepherosa Ziehau 
1480f85e0762SSepherosa Ziehau static void
1481f85e0762SSepherosa Ziehau ae_pcie_init(struct ae_softc *sc)
1482f85e0762SSepherosa Ziehau {
1483f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_PCIE_LTSSM_TESTMODE_REG,
1484f85e0762SSepherosa Ziehau 		   AE_PCIE_LTSSM_TESTMODE_DEFAULT);
1485f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_PCIE_DLL_TX_CTRL_REG,
1486f85e0762SSepherosa Ziehau 		   AE_PCIE_DLL_TX_CTRL_DEFAULT);
1487f85e0762SSepherosa Ziehau }
1488f85e0762SSepherosa Ziehau 
1489f85e0762SSepherosa Ziehau static void
1490f85e0762SSepherosa Ziehau ae_phy_reset(struct ae_softc *sc)
1491f85e0762SSepherosa Ziehau {
1492f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_PHY_ENABLE_REG, AE_PHY_ENABLE);
1493f85e0762SSepherosa Ziehau 	DELAY(1000);    /* XXX: pause(9) ? */
1494f85e0762SSepherosa Ziehau }
1495f85e0762SSepherosa Ziehau 
1496f85e0762SSepherosa Ziehau static int
1497f85e0762SSepherosa Ziehau ae_reset(struct ae_softc *sc)
1498f85e0762SSepherosa Ziehau {
1499f85e0762SSepherosa Ziehau 	int i;
1500f85e0762SSepherosa Ziehau 
1501f85e0762SSepherosa Ziehau 	/*
1502f85e0762SSepherosa Ziehau 	 * Issue a soft reset.
1503f85e0762SSepherosa Ziehau 	 */
1504f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_MASTER_REG, AE_MASTER_SOFT_RESET);
1505f85e0762SSepherosa Ziehau 	bus_space_barrier(sc->ae_mem_bt, sc->ae_mem_bh, AE_MASTER_REG, 4,
1506f85e0762SSepherosa Ziehau 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
1507f85e0762SSepherosa Ziehau 
1508f85e0762SSepherosa Ziehau 	/*
1509f85e0762SSepherosa Ziehau 	 * Wait for reset to complete.
1510f85e0762SSepherosa Ziehau 	 */
1511f85e0762SSepherosa Ziehau 	for (i = 0; i < AE_RESET_TIMEOUT; i++) {
1512f85e0762SSepherosa Ziehau 		if ((AE_READ_4(sc, AE_MASTER_REG) & AE_MASTER_SOFT_RESET) == 0)
1513f85e0762SSepherosa Ziehau 			break;
1514f85e0762SSepherosa Ziehau 		DELAY(10);
1515f85e0762SSepherosa Ziehau 	}
1516f85e0762SSepherosa Ziehau 	if (i == AE_RESET_TIMEOUT) {
1517f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "reset timeout.\n");
1518f85e0762SSepherosa Ziehau 		return (ENXIO);
1519f85e0762SSepherosa Ziehau 	}
1520f85e0762SSepherosa Ziehau 
1521f85e0762SSepherosa Ziehau 	/*
1522f85e0762SSepherosa Ziehau 	 * Wait for everything to enter idle state.
1523f85e0762SSepherosa Ziehau 	 */
1524f85e0762SSepherosa Ziehau 	for (i = 0; i < AE_IDLE_TIMEOUT; i++) {
1525f85e0762SSepherosa Ziehau 		if (AE_READ_4(sc, AE_IDLE_REG) == 0)
1526f85e0762SSepherosa Ziehau 			break;
1527f85e0762SSepherosa Ziehau 		DELAY(100);
1528f85e0762SSepherosa Ziehau 	}
1529f85e0762SSepherosa Ziehau 	if (i == AE_IDLE_TIMEOUT) {
1530f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "could not enter idle state.\n");
1531f85e0762SSepherosa Ziehau 		return (ENXIO);
1532f85e0762SSepherosa Ziehau 	}
1533f85e0762SSepherosa Ziehau 	return (0);
1534f85e0762SSepherosa Ziehau }
1535f85e0762SSepherosa Ziehau 
1536f85e0762SSepherosa Ziehau static int
1537f85e0762SSepherosa Ziehau ae_check_eeprom_present(struct ae_softc *sc, int *vpdc)
1538f85e0762SSepherosa Ziehau {
1539f85e0762SSepherosa Ziehau 	int error;
1540f85e0762SSepherosa Ziehau 	uint32_t val;
1541f85e0762SSepherosa Ziehau 
1542f85e0762SSepherosa Ziehau 	/*
1543f85e0762SSepherosa Ziehau 	 * Not sure why, but Linux does this.
1544f85e0762SSepherosa Ziehau 	 */
1545f85e0762SSepherosa Ziehau 	val = AE_READ_4(sc, AE_SPICTL_REG);
1546f85e0762SSepherosa Ziehau 	if ((val & AE_SPICTL_VPD_EN) != 0) {
1547f85e0762SSepherosa Ziehau 		val &= ~AE_SPICTL_VPD_EN;
1548f85e0762SSepherosa Ziehau 		AE_WRITE_4(sc, AE_SPICTL_REG, val);
1549f85e0762SSepherosa Ziehau 	}
1550f85e0762SSepherosa Ziehau 	error = pci_find_extcap(sc->ae_dev, PCIY_VPD, vpdc);
1551f85e0762SSepherosa Ziehau 	return (error);
1552f85e0762SSepherosa Ziehau }
1553f85e0762SSepherosa Ziehau 
1554f85e0762SSepherosa Ziehau static int
1555f85e0762SSepherosa Ziehau ae_vpd_read_word(struct ae_softc *sc, int reg, uint32_t *word)
1556f85e0762SSepherosa Ziehau {
1557f85e0762SSepherosa Ziehau 	uint32_t val;
1558f85e0762SSepherosa Ziehau 	int i;
1559f85e0762SSepherosa Ziehau 
1560f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_VPD_DATA_REG, 0);	/* Clear register value. */
1561f85e0762SSepherosa Ziehau 
1562f85e0762SSepherosa Ziehau 	/*
1563f85e0762SSepherosa Ziehau 	 * VPD registers start at offset 0x100. Read them.
1564f85e0762SSepherosa Ziehau 	 */
1565f85e0762SSepherosa Ziehau 	val = 0x100 + reg * 4;
1566f85e0762SSepherosa Ziehau 	AE_WRITE_4(sc, AE_VPD_CAP_REG, (val << AE_VPD_CAP_ADDR_SHIFT) &
1567f85e0762SSepherosa Ziehau 	    AE_VPD_CAP_ADDR_MASK);
1568f85e0762SSepherosa Ziehau 	for (i = 0; i < AE_VPD_TIMEOUT; i++) {
1569f85e0762SSepherosa Ziehau 		DELAY(2000);
1570f85e0762SSepherosa Ziehau 		val = AE_READ_4(sc, AE_VPD_CAP_REG);
1571f85e0762SSepherosa Ziehau 		if ((val & AE_VPD_CAP_DONE) != 0)
1572f85e0762SSepherosa Ziehau 			break;
1573f85e0762SSepherosa Ziehau 	}
1574f85e0762SSepherosa Ziehau 	if (i == AE_VPD_TIMEOUT) {
1575f85e0762SSepherosa Ziehau 		device_printf(sc->ae_dev, "timeout reading VPD register %d.\n",
1576f85e0762SSepherosa Ziehau 		    reg);
1577f85e0762SSepherosa Ziehau 		return (ETIMEDOUT);
1578f85e0762SSepherosa Ziehau 	}
1579f85e0762SSepherosa Ziehau 	*word = AE_READ_4(sc, AE_VPD_DATA_REG);
1580f85e0762SSepherosa Ziehau 	return (0);
1581f85e0762SSepherosa Ziehau }
1582f85e0762SSepherosa Ziehau 
1583f85e0762SSepherosa Ziehau static int
1584f85e0762SSepherosa Ziehau ae_get_vpd_eaddr(struct ae_softc *sc, uint32_t *eaddr)
1585f85e0762SSepherosa Ziehau {
1586f85e0762SSepherosa Ziehau 	uint32_t word, reg, val;
1587f85e0762SSepherosa Ziehau 	int error;
1588f85e0762SSepherosa Ziehau 	int found;
1589f85e0762SSepherosa Ziehau 	int vpdc;
1590f85e0762SSepherosa Ziehau 	int i;
1591f85e0762SSepherosa Ziehau 
1592f85e0762SSepherosa Ziehau 	/*
1593f85e0762SSepherosa Ziehau 	 * Check for EEPROM.
1594f85e0762SSepherosa Ziehau 	 */
1595f85e0762SSepherosa Ziehau 	error = ae_check_eeprom_present(sc, &vpdc);
1596f85e0762SSepherosa Ziehau 	if (error != 0)
1597f85e0762SSepherosa Ziehau 		return (error);
1598f85e0762SSepherosa Ziehau 
1599f85e0762SSepherosa Ziehau 	/*
1600f85e0762SSepherosa Ziehau 	 * Read the VPD configuration space.
1601f85e0762SSepherosa Ziehau 	 * Each register is prefixed with signature,
1602f85e0762SSepherosa Ziehau 	 * so we can check if it is valid.
1603f85e0762SSepherosa Ziehau 	 */
1604f85e0762SSepherosa Ziehau 	for (i = 0, found = 0; i < AE_VPD_NREGS; i++) {
1605f85e0762SSepherosa Ziehau 		error = ae_vpd_read_word(sc, i, &word);
1606f85e0762SSepherosa Ziehau 		if (error != 0)
1607f85e0762SSepherosa Ziehau 			break;
1608f85e0762SSepherosa Ziehau 
1609f85e0762SSepherosa Ziehau 		/*
1610f85e0762SSepherosa Ziehau 		 * Check signature.
1611f85e0762SSepherosa Ziehau 		 */
1612f85e0762SSepherosa Ziehau 		if ((word & AE_VPD_SIG_MASK) != AE_VPD_SIG)
1613f85e0762SSepherosa Ziehau 			break;
1614f85e0762SSepherosa Ziehau 		reg = word >> AE_VPD_REG_SHIFT;
1615f85e0762SSepherosa Ziehau 		i++;	/* Move to the next word. */
1616f85e0762SSepherosa Ziehau 		if (reg != AE_EADDR0_REG && reg != AE_EADDR1_REG)
1617f85e0762SSepherosa Ziehau 			continue;
1618f85e0762SSepherosa Ziehau 
1619f85e0762SSepherosa Ziehau 		error = ae_vpd_read_word(sc, i, &val);
1620f85e0762SSepherosa Ziehau 		if (error != 0)
1621f85e0762SSepherosa Ziehau 			break;
1622f85e0762SSepherosa Ziehau 		if (reg == AE_EADDR0_REG)
1623f85e0762SSepherosa Ziehau 			eaddr[0] = val;
1624f85e0762SSepherosa Ziehau 		else
1625f85e0762SSepherosa Ziehau 			eaddr[1] = val;
1626f85e0762SSepherosa Ziehau 		found++;
1627f85e0762SSepherosa Ziehau 	}
1628f85e0762SSepherosa Ziehau 	if (found < 2)
1629f85e0762SSepherosa Ziehau 		return (ENOENT);
1630f85e0762SSepherosa Ziehau 
1631f85e0762SSepherosa Ziehau 	eaddr[1] &= 0xffff;	/* Only last 2 bytes are used. */
1632f85e0762SSepherosa Ziehau 	if (AE_CHECK_EADDR_VALID(eaddr) != 0) {
1633f85e0762SSepherosa Ziehau 		if (bootverbose)
1634f85e0762SSepherosa Ziehau 			device_printf(sc->ae_dev,
1635f85e0762SSepherosa Ziehau 			    "VPD ethernet address registers are invalid.\n");
1636f85e0762SSepherosa Ziehau 		return (EINVAL);
1637f85e0762SSepherosa Ziehau 	}
1638f85e0762SSepherosa Ziehau 	return (0);
1639f85e0762SSepherosa Ziehau }
1640f85e0762SSepherosa Ziehau 
1641f85e0762SSepherosa Ziehau static int
1642f85e0762SSepherosa Ziehau ae_get_reg_eaddr(struct ae_softc *sc, uint32_t *eaddr)
1643f85e0762SSepherosa Ziehau {
1644f85e0762SSepherosa Ziehau 	/*
1645f85e0762SSepherosa Ziehau 	 * BIOS is supposed to set this.
1646f85e0762SSepherosa Ziehau 	 */
1647f85e0762SSepherosa Ziehau 	eaddr[0] = AE_READ_4(sc, AE_EADDR0_REG);
1648f85e0762SSepherosa Ziehau 	eaddr[1] = AE_READ_4(sc, AE_EADDR1_REG);
1649f85e0762SSepherosa Ziehau 	eaddr[1] &= 0xffff;	/* Only last 2 bytes are used. */
1650f85e0762SSepherosa Ziehau 	if (AE_CHECK_EADDR_VALID(eaddr) != 0) {
1651f85e0762SSepherosa Ziehau 		if (bootverbose)
1652f85e0762SSepherosa Ziehau 			device_printf(sc->ae_dev,
1653f85e0762SSepherosa Ziehau 			    "Ethetnet address registers are invalid.\n");
1654f85e0762SSepherosa Ziehau 		return (EINVAL);
1655f85e0762SSepherosa Ziehau 	}
1656f85e0762SSepherosa Ziehau 	return (0);
1657f85e0762SSepherosa Ziehau }
1658f85e0762SSepherosa Ziehau 
1659f85e0762SSepherosa Ziehau static void
1660f85e0762SSepherosa Ziehau ae_get_eaddr(struct ae_softc *sc)
1661f85e0762SSepherosa Ziehau {
1662f85e0762SSepherosa Ziehau 	uint32_t eaddr[2] = {0, 0};
1663f85e0762SSepherosa Ziehau 	int error;
1664f85e0762SSepherosa Ziehau 
1665f85e0762SSepherosa Ziehau 	/*
1666f85e0762SSepherosa Ziehau 	 *Check for EEPROM.
1667f85e0762SSepherosa Ziehau 	 */
1668f85e0762SSepherosa Ziehau 	error = ae_get_vpd_eaddr(sc, eaddr);
1669f85e0762SSepherosa Ziehau 	if (error)
1670f85e0762SSepherosa Ziehau 		error = ae_get_reg_eaddr(sc, eaddr);
1671f85e0762SSepherosa Ziehau 	if (error) {
1672f85e0762SSepherosa Ziehau 		if (bootverbose)
1673f85e0762SSepherosa Ziehau 			device_printf(sc->ae_dev,
1674f85e0762SSepherosa Ziehau 			    "Generating random ethernet address.\n");
1675f85e0762SSepherosa Ziehau 		eaddr[0] = karc4random();
1676f85e0762SSepherosa Ziehau 		/*
1677f85e0762SSepherosa Ziehau 		 * Set OUI to ASUSTek COMPUTER INC.
1678f85e0762SSepherosa Ziehau 		 */
1679f85e0762SSepherosa Ziehau 		sc->ae_eaddr[0] = 0x02;	/* U/L bit set. */
1680f85e0762SSepherosa Ziehau 		sc->ae_eaddr[1] = 0x1f;
1681f85e0762SSepherosa Ziehau 		sc->ae_eaddr[2] = 0xc6;
1682f85e0762SSepherosa Ziehau 		sc->ae_eaddr[3] = (eaddr[0] >> 16) & 0xff;
1683f85e0762SSepherosa Ziehau 		sc->ae_eaddr[4] = (eaddr[0] >> 8) & 0xff;
1684f85e0762SSepherosa Ziehau 		sc->ae_eaddr[5] = (eaddr[0] >> 0) & 0xff;
1685f85e0762SSepherosa Ziehau 	} else {
1686f85e0762SSepherosa Ziehau 		sc->ae_eaddr[0] = (eaddr[1] >> 8) & 0xff;
1687f85e0762SSepherosa Ziehau 		sc->ae_eaddr[1] = (eaddr[1] >> 0) & 0xff;
1688f85e0762SSepherosa Ziehau 		sc->ae_eaddr[2] = (eaddr[0] >> 24) & 0xff;
1689f85e0762SSepherosa Ziehau 		sc->ae_eaddr[3] = (eaddr[0] >> 16) & 0xff;
1690f85e0762SSepherosa Ziehau 		sc->ae_eaddr[4] = (eaddr[0] >> 8) & 0xff;
1691f85e0762SSepherosa Ziehau 		sc->ae_eaddr[5] = (eaddr[0] >> 0) & 0xff;
1692f85e0762SSepherosa Ziehau 	}
1693f85e0762SSepherosa Ziehau }
1694f85e0762SSepherosa Ziehau 
1695f85e0762SSepherosa Ziehau static int
1696f85e0762SSepherosa Ziehau ae_mediachange(struct ifnet *ifp)
1697f85e0762SSepherosa Ziehau {
1698f85e0762SSepherosa Ziehau 	struct ae_softc *sc = ifp->if_softc;
1699f85e0762SSepherosa Ziehau 	struct mii_data *mii = device_get_softc(sc->ae_miibus);
1700f85e0762SSepherosa Ziehau 	int error;
1701f85e0762SSepherosa Ziehau 
1702f85e0762SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
1703f85e0762SSepherosa Ziehau 	if (mii->mii_instance != 0) {
1704f85e0762SSepherosa Ziehau 		struct mii_softc *miisc;
1705f85e0762SSepherosa Ziehau 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
1706f85e0762SSepherosa Ziehau 			mii_phy_reset(miisc);
1707f85e0762SSepherosa Ziehau 	}
1708f85e0762SSepherosa Ziehau 	error = mii_mediachg(mii);
1709f85e0762SSepherosa Ziehau 	return (error);
1710f85e0762SSepherosa Ziehau }
1711f85e0762SSepherosa Ziehau 
1712f85e0762SSepherosa Ziehau static void
1713f85e0762SSepherosa Ziehau ae_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
1714f85e0762SSepherosa Ziehau {
1715f85e0762SSepherosa Ziehau 	struct ae_softc *sc = ifp->if_softc;
1716f85e0762SSepherosa Ziehau 	struct mii_data *mii = device_get_softc(sc->ae_miibus);
1717f85e0762SSepherosa Ziehau 
1718f85e0762SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
1719f85e0762SSepherosa Ziehau 	mii_pollstat(mii);
1720f85e0762SSepherosa Ziehau 	ifmr->ifm_status = mii->mii_media_status;
1721f85e0762SSepherosa Ziehau 	ifmr->ifm_active = mii->mii_media_active;
1722f85e0762SSepherosa Ziehau }
1723f85e0762SSepherosa Ziehau 
1724f85e0762SSepherosa Ziehau static void
1725f85e0762SSepherosa Ziehau ae_update_stats_tx(uint16_t flags, struct ae_stats *stats)
1726f85e0762SSepherosa Ziehau {
1727f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_BCAST) != 0)
1728f85e0762SSepherosa Ziehau 		stats->tx_bcast++;
1729f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_MCAST) != 0)
1730f85e0762SSepherosa Ziehau 		stats->tx_mcast++;
1731f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_PAUSE) != 0)
1732f85e0762SSepherosa Ziehau 		stats->tx_pause++;
1733f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_CTRL) != 0)
1734f85e0762SSepherosa Ziehau 		stats->tx_ctrl++;
1735f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_DEFER) != 0)
1736f85e0762SSepherosa Ziehau 		stats->tx_defer++;
1737f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_EXCDEFER) != 0)
1738f85e0762SSepherosa Ziehau 		stats->tx_excdefer++;
1739f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_SINGLECOL) != 0)
1740f85e0762SSepherosa Ziehau 		stats->tx_singlecol++;
1741f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_MULTICOL) != 0)
1742f85e0762SSepherosa Ziehau 		stats->tx_multicol++;
1743f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_LATECOL) != 0)
1744f85e0762SSepherosa Ziehau 		stats->tx_latecol++;
1745f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_ABORTCOL) != 0)
1746f85e0762SSepherosa Ziehau 		stats->tx_abortcol++;
1747f85e0762SSepherosa Ziehau 	if ((flags & AE_TXS_UNDERRUN) != 0)
1748f85e0762SSepherosa Ziehau 		stats->tx_underrun++;
1749f85e0762SSepherosa Ziehau }
1750f85e0762SSepherosa Ziehau 
1751f85e0762SSepherosa Ziehau static void
1752f85e0762SSepherosa Ziehau ae_update_stats_rx(uint16_t flags, struct ae_stats *stats)
1753f85e0762SSepherosa Ziehau {
1754f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_BCAST) != 0)
1755f85e0762SSepherosa Ziehau 		stats->rx_bcast++;
1756f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_MCAST) != 0)
1757f85e0762SSepherosa Ziehau 		stats->rx_mcast++;
1758f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_PAUSE) != 0)
1759f85e0762SSepherosa Ziehau 		stats->rx_pause++;
1760f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_CTRL) != 0)
1761f85e0762SSepherosa Ziehau 		stats->rx_ctrl++;
1762f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_CRCERR) != 0)
1763f85e0762SSepherosa Ziehau 		stats->rx_crcerr++;
1764f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_CODEERR) != 0)
1765f85e0762SSepherosa Ziehau 		stats->rx_codeerr++;
1766f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_RUNT) != 0)
1767f85e0762SSepherosa Ziehau 		stats->rx_runt++;
1768f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_FRAG) != 0)
1769f85e0762SSepherosa Ziehau 		stats->rx_frag++;
1770f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_TRUNC) != 0)
1771f85e0762SSepherosa Ziehau 		stats->rx_trunc++;
1772f85e0762SSepherosa Ziehau 	if ((flags & AE_RXD_ALIGN) != 0)
1773f85e0762SSepherosa Ziehau 		stats->rx_align++;
1774f85e0762SSepherosa Ziehau }
1775f85e0762SSepherosa Ziehau 
1776f85e0762SSepherosa Ziehau static int
1777f85e0762SSepherosa Ziehau ae_resume(device_t dev)
1778f85e0762SSepherosa Ziehau {
1779f85e0762SSepherosa Ziehau 	struct ae_softc *sc = device_get_softc(dev);
1780f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
1781f85e0762SSepherosa Ziehau 
1782f85e0762SSepherosa Ziehau 	lwkt_serialize_enter(ifp->if_serializer);
1783f85e0762SSepherosa Ziehau #if 0
1784f85e0762SSepherosa Ziehau 	AE_READ_4(sc, AE_WOL_REG);	/* Clear WOL status. */
1785f85e0762SSepherosa Ziehau #endif
178697c1b3a4SSepherosa Ziehau 	ae_phy_reset(sc);
1787f85e0762SSepherosa Ziehau 	if ((ifp->if_flags & IFF_UP) != 0)
1788f85e0762SSepherosa Ziehau 		ae_init(sc);
1789f85e0762SSepherosa Ziehau 	lwkt_serialize_exit(ifp->if_serializer);
1790f85e0762SSepherosa Ziehau 	return (0);
1791f85e0762SSepherosa Ziehau }
1792f85e0762SSepherosa Ziehau 
1793f85e0762SSepherosa Ziehau static int
1794f85e0762SSepherosa Ziehau ae_suspend(device_t dev)
1795f85e0762SSepherosa Ziehau {
1796f85e0762SSepherosa Ziehau 	struct ae_softc *sc = device_get_softc(dev);
1797f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
1798f85e0762SSepherosa Ziehau 
1799f85e0762SSepherosa Ziehau 	lwkt_serialize_enter(ifp->if_serializer);
1800f85e0762SSepherosa Ziehau 	ae_stop(sc);
1801f85e0762SSepherosa Ziehau #if 0
1802f85e0762SSepherosa Ziehau 	/* we don't use ae_pm_init because we don't want WOL */
1803f85e0762SSepherosa Ziehau 	ae_pm_init(sc);
1804f85e0762SSepherosa Ziehau #endif
1805f85e0762SSepherosa Ziehau 	lwkt_serialize_exit(ifp->if_serializer);
1806f85e0762SSepherosa Ziehau 	return (0);
1807f85e0762SSepherosa Ziehau }
1808f85e0762SSepherosa Ziehau 
1809f85e0762SSepherosa Ziehau static int
1810f85e0762SSepherosa Ziehau ae_shutdown(device_t dev)
1811f85e0762SSepherosa Ziehau {
1812f85e0762SSepherosa Ziehau 	struct ae_softc *sc = device_get_softc(dev);
1813f85e0762SSepherosa Ziehau 	struct ifnet *ifp = &sc->arpcom.ac_if;
1814f85e0762SSepherosa Ziehau 
1815f85e0762SSepherosa Ziehau 	ae_suspend(dev);
1816f85e0762SSepherosa Ziehau 
1817f85e0762SSepherosa Ziehau 	lwkt_serialize_enter(ifp->if_serializer);
1818f85e0762SSepherosa Ziehau 	ae_powersave_enable(sc);
1819f85e0762SSepherosa Ziehau 	lwkt_serialize_exit(ifp->if_serializer);
1820f85e0762SSepherosa Ziehau 
1821f85e0762SSepherosa Ziehau 	return (0);
1822f85e0762SSepherosa Ziehau }
1823f85e0762SSepherosa Ziehau 
1824f85e0762SSepherosa Ziehau static void
1825f85e0762SSepherosa Ziehau ae_powersave_disable(struct ae_softc *sc)
1826f85e0762SSepherosa Ziehau {
1827f85e0762SSepherosa Ziehau 	uint32_t val;
1828f85e0762SSepherosa Ziehau 
1829f85e0762SSepherosa Ziehau 	AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 0);
1830f85e0762SSepherosa Ziehau 	val = AE_PHY_READ(sc, AE_PHY_DBG_DATA);
1831f85e0762SSepherosa Ziehau 	if (val & AE_PHY_DBG_POWERSAVE) {
1832f85e0762SSepherosa Ziehau 		val &= ~AE_PHY_DBG_POWERSAVE;
1833f85e0762SSepherosa Ziehau 		AE_PHY_WRITE(sc, AE_PHY_DBG_DATA, val);
1834f85e0762SSepherosa Ziehau 		DELAY(1000);
1835f85e0762SSepherosa Ziehau 	}
1836f85e0762SSepherosa Ziehau }
1837f85e0762SSepherosa Ziehau 
1838f85e0762SSepherosa Ziehau static void
1839f85e0762SSepherosa Ziehau ae_powersave_enable(struct ae_softc *sc)
1840f85e0762SSepherosa Ziehau {
1841f85e0762SSepherosa Ziehau 	uint32_t val;
1842f85e0762SSepherosa Ziehau 
1843f85e0762SSepherosa Ziehau 	/*
1844f85e0762SSepherosa Ziehau 	 * XXX magic numbers.
1845f85e0762SSepherosa Ziehau 	 */
1846f85e0762SSepherosa Ziehau 	AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 0);
1847f85e0762SSepherosa Ziehau 	val = AE_PHY_READ(sc, AE_PHY_DBG_DATA);
1848f85e0762SSepherosa Ziehau 	AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, val | 0x1000);
1849f85e0762SSepherosa Ziehau 	AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 2);
1850f85e0762SSepherosa Ziehau 	AE_PHY_WRITE(sc, AE_PHY_DBG_DATA, 0x3000);
1851f85e0762SSepherosa Ziehau 	AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 3);
1852f85e0762SSepherosa Ziehau 	AE_PHY_WRITE(sc, AE_PHY_DBG_DATA, 0);
1853f85e0762SSepherosa Ziehau }
1854