xref: /openbsd-src/sys/dev/pci/if_rge.c (revision 8bb071cdcfd5bb410ffc4d6bba584292a9a77626)
1*8bb071cdSderaadt /*	$OpenBSD: if_rge.c,v 1.35 2024/08/31 16:23:09 deraadt Exp $	*/
2be4e4fa3Skevlo 
3be4e4fa3Skevlo /*
474263a62Skevlo  * Copyright (c) 2019, 2020, 2023, 2024
574263a62Skevlo  *	Kevin Lo <kevlo@openbsd.org>
6be4e4fa3Skevlo  *
7be4e4fa3Skevlo  * Permission to use, copy, modify, and distribute this software for any
8be4e4fa3Skevlo  * purpose with or without fee is hereby granted, provided that the above
9be4e4fa3Skevlo  * copyright notice and this permission notice appear in all copies.
10be4e4fa3Skevlo  *
11be4e4fa3Skevlo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12be4e4fa3Skevlo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13be4e4fa3Skevlo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14be4e4fa3Skevlo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15be4e4fa3Skevlo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16be4e4fa3Skevlo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17be4e4fa3Skevlo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18be4e4fa3Skevlo  */
19be4e4fa3Skevlo 
20be4e4fa3Skevlo #include "bpfilter.h"
21be4e4fa3Skevlo #include "vlan.h"
221b7ab755Sdlg #include "kstat.h"
23be4e4fa3Skevlo 
24be4e4fa3Skevlo #include <sys/param.h>
25be4e4fa3Skevlo #include <sys/systm.h>
26be4e4fa3Skevlo #include <sys/sockio.h>
27be4e4fa3Skevlo #include <sys/mbuf.h>
28be4e4fa3Skevlo #include <sys/malloc.h>
29be4e4fa3Skevlo #include <sys/device.h>
30be4e4fa3Skevlo #include <sys/endian.h>
31be4e4fa3Skevlo 
32be4e4fa3Skevlo #include <net/if.h>
33be4e4fa3Skevlo #include <net/if_media.h>
34be4e4fa3Skevlo 
35be4e4fa3Skevlo #include <netinet/in.h>
36be4e4fa3Skevlo #include <netinet/if_ether.h>
37be4e4fa3Skevlo 
38be4e4fa3Skevlo #if NBPFILTER > 0
39be4e4fa3Skevlo #include <net/bpf.h>
40be4e4fa3Skevlo #endif
41be4e4fa3Skevlo 
421b7ab755Sdlg #if NKSTAT > 0
431b7ab755Sdlg #include <sys/kstat.h>
441b7ab755Sdlg #endif
451b7ab755Sdlg 
46be4e4fa3Skevlo #include <machine/bus.h>
47be4e4fa3Skevlo #include <machine/intr.h>
48be4e4fa3Skevlo 
49be4e4fa3Skevlo #include <dev/mii/mii.h>
50be4e4fa3Skevlo 
51be4e4fa3Skevlo #include <dev/pci/pcivar.h>
52be4e4fa3Skevlo #include <dev/pci/pcireg.h>
53be4e4fa3Skevlo #include <dev/pci/pcidevs.h>
54be4e4fa3Skevlo 
55be4e4fa3Skevlo #include <dev/pci/if_rgereg.h>
56be4e4fa3Skevlo 
5781a23f33Skevlo #ifdef RGE_DEBUG
5881a23f33Skevlo #define DPRINTF(x)	do { if (rge_debug > 0) printf x; } while (0)
5981a23f33Skevlo int rge_debug = 0;
6081a23f33Skevlo #else
6181a23f33Skevlo #define DPRINTF(x)
6281a23f33Skevlo #endif
6381a23f33Skevlo 
64be4e4fa3Skevlo int		rge_match(struct device *, void *, void *);
65be4e4fa3Skevlo void		rge_attach(struct device *, struct device *, void *);
667bcadffeSkevlo int		rge_activate(struct device *, int);
67be4e4fa3Skevlo int		rge_intr(void *);
68be4e4fa3Skevlo int		rge_ioctl(struct ifnet *, u_long, caddr_t);
69be4e4fa3Skevlo void		rge_start(struct ifqueue *);
70be4e4fa3Skevlo void		rge_watchdog(struct ifnet *);
718cbc3b0aSkevlo void		rge_init(struct ifnet *);
72be4e4fa3Skevlo void		rge_stop(struct ifnet *);
73be4e4fa3Skevlo int		rge_ifmedia_upd(struct ifnet *);
74be4e4fa3Skevlo void		rge_ifmedia_sts(struct ifnet *, struct ifmediareq *);
75be4e4fa3Skevlo int		rge_allocmem(struct rge_softc *);
76804ff404Skevlo int		rge_newbuf(struct rge_queues *);
77804ff404Skevlo void		rge_rx_list_init(struct rge_queues *);
78804ff404Skevlo void		rge_tx_list_init(struct rge_queues *);
79804ff404Skevlo void		rge_fill_rx_ring(struct rge_queues *);
80804ff404Skevlo int		rge_rxeof(struct rge_queues *);
81804ff404Skevlo int		rge_txeof(struct rge_queues *);
82be4e4fa3Skevlo void		rge_reset(struct rge_softc *);
83be4e4fa3Skevlo void		rge_iff(struct rge_softc *);
848cbc3b0aSkevlo void		rge_chipinit(struct rge_softc *);
85be4e4fa3Skevlo void		rge_set_phy_power(struct rge_softc *, int);
868cbc3b0aSkevlo void		rge_ephy_config(struct rge_softc *);
878cbc3b0aSkevlo void		rge_ephy_config_mac_cfg3(struct rge_softc *);
888cbc3b0aSkevlo void		rge_ephy_config_mac_cfg5(struct rge_softc *);
898cbc3b0aSkevlo int		rge_phy_config(struct rge_softc *);
9074263a62Skevlo void		rge_phy_config_mac_cfg2_8126(struct rge_softc *);
9181a23f33Skevlo void		rge_phy_config_mac_cfg3(struct rge_softc *);
9281a23f33Skevlo void		rge_phy_config_mac_cfg5(struct rge_softc *);
9381a23f33Skevlo void		rge_phy_config_mcu(struct rge_softc *, uint16_t);
94be4e4fa3Skevlo void		rge_set_macaddr(struct rge_softc *, const uint8_t *);
95be4e4fa3Skevlo void		rge_get_macaddr(struct rge_softc *, uint8_t *);
96be4e4fa3Skevlo void		rge_hw_init(struct rge_softc *);
978cbc3b0aSkevlo void		rge_hw_reset(struct rge_softc *);
98be4e4fa3Skevlo void		rge_disable_phy_ocp_pwrsave(struct rge_softc *);
99be4e4fa3Skevlo void		rge_patch_phy_mcu(struct rge_softc *, int);
100be4e4fa3Skevlo void		rge_add_media_types(struct rge_softc *);
101be4e4fa3Skevlo void		rge_config_imtype(struct rge_softc *, int);
1028cbc3b0aSkevlo void		rge_disable_aspm_clkreq(struct rge_softc *);
10381a23f33Skevlo void		rge_disable_hw_im(struct rge_softc *);
104be4e4fa3Skevlo void		rge_disable_sim_im(struct rge_softc *);
105be4e4fa3Skevlo void		rge_setup_sim_im(struct rge_softc *);
106be4e4fa3Skevlo void		rge_setup_intr(struct rge_softc *, int);
1078cbc3b0aSkevlo void		rge_switch_mcu_ram_page(struct rge_softc *, int);
108be4e4fa3Skevlo void		rge_exit_oob(struct rge_softc *);
109be4e4fa3Skevlo void		rge_write_csi(struct rge_softc *, uint32_t, uint32_t);
110be4e4fa3Skevlo uint32_t	rge_read_csi(struct rge_softc *, uint32_t);
111be4e4fa3Skevlo void		rge_write_mac_ocp(struct rge_softc *, uint16_t, uint16_t);
112be4e4fa3Skevlo uint16_t	rge_read_mac_ocp(struct rge_softc *, uint16_t);
113be4e4fa3Skevlo void		rge_write_ephy(struct rge_softc *, uint16_t, uint16_t);
11481a23f33Skevlo uint16_t	rge_read_ephy(struct rge_softc *, uint16_t);
115be4e4fa3Skevlo void		rge_write_phy(struct rge_softc *, uint16_t, uint16_t, uint16_t);
116dfa3bc0bSkevlo uint16_t	rge_read_phy(struct rge_softc *, uint16_t, uint16_t);
117be4e4fa3Skevlo void		rge_write_phy_ocp(struct rge_softc *, uint16_t, uint16_t);
118be4e4fa3Skevlo uint16_t	rge_read_phy_ocp(struct rge_softc *, uint16_t);
119be4e4fa3Skevlo int		rge_get_link_status(struct rge_softc *);
120be4e4fa3Skevlo void		rge_txstart(void *);
121be4e4fa3Skevlo void		rge_tick(void *);
122be4e4fa3Skevlo void		rge_link_state(struct rge_softc *);
1237bcadffeSkevlo #ifndef SMALL_KERNEL
1247bcadffeSkevlo int		rge_wol(struct ifnet *, int);
1257bcadffeSkevlo void		rge_wol_power(struct rge_softc *);
1267bcadffeSkevlo #endif
127be4e4fa3Skevlo 
1281b7ab755Sdlg #if NKSTAT > 0
1291b7ab755Sdlg void		rge_kstat_attach(struct rge_softc *);
1301b7ab755Sdlg #endif
1311b7ab755Sdlg 
132be4e4fa3Skevlo static const struct {
133be4e4fa3Skevlo 	uint16_t reg;
134be4e4fa3Skevlo 	uint16_t val;
1358cbc3b0aSkevlo }  rtl8125_mac_cfg3_mcu[] = {
136be4e4fa3Skevlo 	RTL8125_MAC_CFG3_MCU
13781a23f33Skevlo }, rtl8125_mac_cfg5_mcu[] = {
13881a23f33Skevlo 	RTL8125_MAC_CFG5_MCU
13974263a62Skevlo }, rtl8126_mac_cfg2_mcu[] = {
14074263a62Skevlo 	RTL8126_MAC_CFG2_MCU
141be4e4fa3Skevlo };
142be4e4fa3Skevlo 
143d895c343Sjmatthew const struct cfattach rge_ca = {
1447bcadffeSkevlo 	sizeof(struct rge_softc), rge_match, rge_attach, NULL, rge_activate
145be4e4fa3Skevlo };
146be4e4fa3Skevlo 
147d895c343Sjmatthew struct cfdriver rge_cd = {
148be4e4fa3Skevlo 	NULL, "rge", DV_IFNET
149be4e4fa3Skevlo };
150be4e4fa3Skevlo 
151be4e4fa3Skevlo const struct pci_matchid rge_devices[] = {
152be4e4fa3Skevlo 	{ PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_E3000 },
15374263a62Skevlo 	{ PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RTL8125 },
15474263a62Skevlo 	{ PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RTL8126 }
155be4e4fa3Skevlo };
156be4e4fa3Skevlo 
157be4e4fa3Skevlo int
158be4e4fa3Skevlo rge_match(struct device *parent, void *match, void *aux)
159be4e4fa3Skevlo {
160be4e4fa3Skevlo 	return (pci_matchbyid((struct pci_attach_args *)aux, rge_devices,
161be4e4fa3Skevlo 	    nitems(rge_devices)));
162be4e4fa3Skevlo }
163be4e4fa3Skevlo 
164be4e4fa3Skevlo void
165be4e4fa3Skevlo rge_attach(struct device *parent, struct device *self, void *aux)
166be4e4fa3Skevlo {
167be4e4fa3Skevlo 	struct rge_softc *sc = (struct rge_softc *)self;
168be4e4fa3Skevlo 	struct pci_attach_args *pa = aux;
169be4e4fa3Skevlo 	pci_chipset_tag_t pc = pa->pa_pc;
170be4e4fa3Skevlo 	pci_intr_handle_t ih;
171be4e4fa3Skevlo 	const char *intrstr = NULL;
172be4e4fa3Skevlo 	struct ifnet *ifp;
173804ff404Skevlo 	struct rge_queues *q;
174be4e4fa3Skevlo 	pcireg_t reg;
175be4e4fa3Skevlo 	uint32_t hwrev;
176be4e4fa3Skevlo 	uint8_t eaddr[ETHER_ADDR_LEN];
177be4e4fa3Skevlo 	int offset;
178be4e4fa3Skevlo 
179be4e4fa3Skevlo 	pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0);
180be4e4fa3Skevlo 
181be4e4fa3Skevlo 	/*
182be4e4fa3Skevlo 	 * Map control/status registers.
183be4e4fa3Skevlo 	 */
184be4e4fa3Skevlo 	if (pci_mapreg_map(pa, RGE_PCI_BAR2, PCI_MAPREG_TYPE_MEM |
185be4e4fa3Skevlo 	    PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->rge_btag, &sc->rge_bhandle,
186be4e4fa3Skevlo 	    NULL, &sc->rge_bsize, 0)) {
187be4e4fa3Skevlo 		if (pci_mapreg_map(pa, RGE_PCI_BAR1, PCI_MAPREG_TYPE_MEM |
188be4e4fa3Skevlo 		    PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->rge_btag,
189be4e4fa3Skevlo 		    &sc->rge_bhandle, NULL, &sc->rge_bsize, 0)) {
190be4e4fa3Skevlo 			if (pci_mapreg_map(pa, RGE_PCI_BAR0, PCI_MAPREG_TYPE_IO,
191be4e4fa3Skevlo 			    0, &sc->rge_btag, &sc->rge_bhandle, NULL,
192be4e4fa3Skevlo 			    &sc->rge_bsize, 0)) {
193be4e4fa3Skevlo 				printf(": can't map mem or i/o space\n");
194be4e4fa3Skevlo 				return;
195be4e4fa3Skevlo 			}
196be4e4fa3Skevlo 		}
197be4e4fa3Skevlo 	}
198be4e4fa3Skevlo 
199804ff404Skevlo 	q = malloc(sizeof(struct rge_queues), M_DEVBUF, M_NOWAIT | M_ZERO);
200804ff404Skevlo 	if (q == NULL) {
201804ff404Skevlo 		printf(": unable to allocate queue memory\n");
202804ff404Skevlo 		return;
203804ff404Skevlo 	}
204804ff404Skevlo 	q->q_sc = sc;
205804ff404Skevlo 	q->q_index = 0;
206804ff404Skevlo 
207804ff404Skevlo 	sc->sc_queues = q;
208804ff404Skevlo 	sc->sc_nqueues = 1;
209804ff404Skevlo 
210be4e4fa3Skevlo 	/*
211be4e4fa3Skevlo 	 * Allocate interrupt.
212be4e4fa3Skevlo 	 */
213d67d920dSkevlo 	if (pci_intr_map_msix(pa, 0, &ih) == 0 ||
214d67d920dSkevlo 	    pci_intr_map_msi(pa, &ih) == 0)
215be4e4fa3Skevlo 		sc->rge_flags |= RGE_FLAG_MSI;
216be4e4fa3Skevlo 	else if (pci_intr_map(pa, &ih) != 0) {
217be4e4fa3Skevlo 		printf(": couldn't map interrupt\n");
218be4e4fa3Skevlo 		return;
219be4e4fa3Skevlo 	}
220be4e4fa3Skevlo 	intrstr = pci_intr_string(pc, ih);
221be4e4fa3Skevlo 	sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE, rge_intr,
222be4e4fa3Skevlo 	    sc, sc->sc_dev.dv_xname);
223be4e4fa3Skevlo 	if (sc->sc_ih == NULL) {
224be4e4fa3Skevlo 		printf(": couldn't establish interrupt");
225be4e4fa3Skevlo 		if (intrstr != NULL)
226be4e4fa3Skevlo 			printf(" at %s", intrstr);
227be4e4fa3Skevlo 		printf("\n");
228be4e4fa3Skevlo 		return;
229be4e4fa3Skevlo 	}
230be4e4fa3Skevlo 	printf(": %s", intrstr);
231be4e4fa3Skevlo 
232be4e4fa3Skevlo 	sc->sc_dmat = pa->pa_dmat;
233be4e4fa3Skevlo 	sc->sc_pc = pa->pa_pc;
234be4e4fa3Skevlo 	sc->sc_tag = pa->pa_tag;
235be4e4fa3Skevlo 
236be4e4fa3Skevlo 	/* Determine hardware revision */
237be4e4fa3Skevlo 	hwrev = RGE_READ_4(sc, RGE_TXCFG) & RGE_TXCFG_HWREV;
238be4e4fa3Skevlo 	switch (hwrev) {
239be4e4fa3Skevlo 	case 0x60900000:
240be4e4fa3Skevlo 		sc->rge_type = MAC_CFG3;
241be4e4fa3Skevlo 		break;
24281a23f33Skevlo 	case 0x64100000:
24381a23f33Skevlo 		sc->rge_type = MAC_CFG5;
24481a23f33Skevlo 		break;
24574263a62Skevlo 	case 0x64900000:
24674263a62Skevlo 		sc->rge_type = MAC_CFG2_8126;
24774263a62Skevlo 		break;
248be4e4fa3Skevlo 	default:
249be4e4fa3Skevlo 		printf(": unknown version 0x%08x\n", hwrev);
250be4e4fa3Skevlo 		return;
251be4e4fa3Skevlo 	}
252be4e4fa3Skevlo 
253be4e4fa3Skevlo 	rge_config_imtype(sc, RGE_IMTYPE_SIM);
254be4e4fa3Skevlo 
255be4e4fa3Skevlo 	/*
256be4e4fa3Skevlo 	 * PCI Express check.
257be4e4fa3Skevlo 	 */
258be4e4fa3Skevlo 	if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
259be4e4fa3Skevlo 	    &offset, NULL)) {
260be4e4fa3Skevlo 		/* Disable PCIe ASPM and ECPM. */
261be4e4fa3Skevlo 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag,
262be4e4fa3Skevlo 		    offset + PCI_PCIE_LCSR);
263be4e4fa3Skevlo 		reg &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1 |
264be4e4fa3Skevlo 		    PCI_PCIE_LCSR_ECPM);
265be4e4fa3Skevlo 		pci_conf_write(pa->pa_pc, pa->pa_tag, offset + PCI_PCIE_LCSR,
266be4e4fa3Skevlo 		    reg);
267be4e4fa3Skevlo 	}
268be4e4fa3Skevlo 
2698cbc3b0aSkevlo 	rge_chipinit(sc);
270be4e4fa3Skevlo 
271be4e4fa3Skevlo 	rge_get_macaddr(sc, eaddr);
272be4e4fa3Skevlo 	printf(", address %s\n", ether_sprintf(eaddr));
273be4e4fa3Skevlo 
274be4e4fa3Skevlo 	memcpy(sc->sc_arpcom.ac_enaddr, eaddr, ETHER_ADDR_LEN);
275be4e4fa3Skevlo 
276be4e4fa3Skevlo 	if (rge_allocmem(sc))
277be4e4fa3Skevlo 		return;
278be4e4fa3Skevlo 
279be4e4fa3Skevlo 	ifp = &sc->sc_arpcom.ac_if;
280be4e4fa3Skevlo 	ifp->if_softc = sc;
281be4e4fa3Skevlo 	strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
282be4e4fa3Skevlo 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
283be4e4fa3Skevlo 	ifp->if_xflags = IFXF_MPSAFE;
284be4e4fa3Skevlo 	ifp->if_ioctl = rge_ioctl;
285be4e4fa3Skevlo 	ifp->if_qstart = rge_start;
286be4e4fa3Skevlo 	ifp->if_watchdog = rge_watchdog;
287cf96265bSbluhm 	ifq_init_maxlen(&ifp->if_snd, RGE_TX_LIST_CNT - 1);
288be4e4fa3Skevlo 	ifp->if_hardmtu = RGE_JUMBO_MTU;
289be4e4fa3Skevlo 
290be4e4fa3Skevlo 	ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_CSUM_IPv4 |
291be4e4fa3Skevlo 	    IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4;
292be4e4fa3Skevlo 
293be4e4fa3Skevlo #if NVLAN > 0
294be4e4fa3Skevlo 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
295be4e4fa3Skevlo #endif
296be4e4fa3Skevlo 
2977bcadffeSkevlo #ifndef SMALL_KERNEL
2987bcadffeSkevlo 	ifp->if_capabilities |= IFCAP_WOL;
2997bcadffeSkevlo 	ifp->if_wol = rge_wol;
3007bcadffeSkevlo 	rge_wol(ifp, 0);
3017bcadffeSkevlo #endif
302be4e4fa3Skevlo 	timeout_set(&sc->sc_timeout, rge_tick, sc);
303be4e4fa3Skevlo 	task_set(&sc->sc_task, rge_txstart, sc);
304be4e4fa3Skevlo 
305be4e4fa3Skevlo 	/* Initialize ifmedia structures. */
306be4e4fa3Skevlo 	ifmedia_init(&sc->sc_media, IFM_IMASK, rge_ifmedia_upd,
307be4e4fa3Skevlo 	    rge_ifmedia_sts);
308be4e4fa3Skevlo 	rge_add_media_types(sc);
309be4e4fa3Skevlo 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
310be4e4fa3Skevlo 	ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
311be4e4fa3Skevlo 	sc->sc_media.ifm_media = sc->sc_media.ifm_cur->ifm_media;
312be4e4fa3Skevlo 
313be4e4fa3Skevlo 	if_attach(ifp);
314be4e4fa3Skevlo 	ether_ifattach(ifp);
3151b7ab755Sdlg 
3161b7ab755Sdlg #if NKSTAT > 0
3171b7ab755Sdlg 	rge_kstat_attach(sc);
3181b7ab755Sdlg #endif
319be4e4fa3Skevlo }
320be4e4fa3Skevlo 
321be4e4fa3Skevlo int
3227bcadffeSkevlo rge_activate(struct device *self, int act)
3237bcadffeSkevlo {
3249a04daecSderaadt #ifndef SMALL_KERNEL
3257bcadffeSkevlo 	struct rge_softc *sc = (struct rge_softc *)self;
3269a04daecSderaadt #endif
3277bcadffeSkevlo 
3287bcadffeSkevlo 	switch (act) {
3297bcadffeSkevlo 	case DVACT_POWERDOWN:
3307bcadffeSkevlo #ifndef SMALL_KERNEL
3317bcadffeSkevlo 		rge_wol_power(sc);
3327bcadffeSkevlo #endif
333df6c2a29Sstsp 		break;
3347bcadffeSkevlo 	}
335*8bb071cdSderaadt 	return (0);
3367bcadffeSkevlo }
3377bcadffeSkevlo 
3387bcadffeSkevlo int
339be4e4fa3Skevlo rge_intr(void *arg)
340be4e4fa3Skevlo {
341be4e4fa3Skevlo 	struct rge_softc *sc = arg;
342804ff404Skevlo 	struct rge_queues *q = sc->sc_queues;
343be4e4fa3Skevlo 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
344be4e4fa3Skevlo 	uint32_t status;
345804ff404Skevlo 	int claimed = 0, rv;
346be4e4fa3Skevlo 
347be4e4fa3Skevlo 	if (!(ifp->if_flags & IFF_RUNNING))
348be4e4fa3Skevlo 		return (0);
349be4e4fa3Skevlo 
350be4e4fa3Skevlo 	/* Disable interrupts. */
351be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_IMR, 0);
352be4e4fa3Skevlo 
353be4e4fa3Skevlo 	if (!(sc->rge_flags & RGE_FLAG_MSI)) {
35481a23f33Skevlo 		if ((RGE_READ_4(sc, RGE_ISR) & sc->rge_intrs) == 0)
355be4e4fa3Skevlo 			return (0);
356be4e4fa3Skevlo 	}
35781a23f33Skevlo 
35881a23f33Skevlo 	status = RGE_READ_4(sc, RGE_ISR);
359be4e4fa3Skevlo 	if (status)
360be4e4fa3Skevlo 		RGE_WRITE_4(sc, RGE_ISR, status);
361be4e4fa3Skevlo 
362be4e4fa3Skevlo 	if (status & RGE_ISR_PCS_TIMEOUT)
363be4e4fa3Skevlo 		claimed = 1;
364be4e4fa3Skevlo 
365804ff404Skevlo 	rv = 0;
36681a23f33Skevlo 	if (status & sc->rge_intrs) {
367804ff404Skevlo 		rv |= rge_rxeof(q);
368804ff404Skevlo 		rv |= rge_txeof(q);
369be4e4fa3Skevlo 
370be4e4fa3Skevlo 		if (status & RGE_ISR_SYSTEM_ERR) {
371be4e4fa3Skevlo 			KERNEL_LOCK();
372be4e4fa3Skevlo 			rge_init(ifp);
373be4e4fa3Skevlo 			KERNEL_UNLOCK();
374be4e4fa3Skevlo 		}
375804ff404Skevlo 		claimed = 1;
376be4e4fa3Skevlo 	}
377be4e4fa3Skevlo 
378be4e4fa3Skevlo 	if (sc->rge_timerintr) {
379804ff404Skevlo 		if (!rv) {
380be4e4fa3Skevlo 			/*
381be4e4fa3Skevlo 			 * Nothing needs to be processed, fallback
382be4e4fa3Skevlo 			 * to use TX/RX interrupts.
383be4e4fa3Skevlo 			 */
384be4e4fa3Skevlo 			rge_setup_intr(sc, RGE_IMTYPE_NONE);
385be4e4fa3Skevlo 
386be4e4fa3Skevlo 			/*
387be4e4fa3Skevlo 			 * Recollect, mainly to avoid the possible
388be4e4fa3Skevlo 			 * race introduced by changing interrupt
389be4e4fa3Skevlo 			 * masks.
390be4e4fa3Skevlo 			 */
391804ff404Skevlo 			rge_rxeof(q);
392804ff404Skevlo 			rge_txeof(q);
393be4e4fa3Skevlo 		} else
394be4e4fa3Skevlo 			RGE_WRITE_4(sc, RGE_TIMERCNT, 1);
395804ff404Skevlo 	} else if (rv) {
396be4e4fa3Skevlo 		/*
397be4e4fa3Skevlo 		 * Assume that using simulated interrupt moderation
398be4e4fa3Skevlo 		 * (hardware timer based) could reduce the interrupt
399be4e4fa3Skevlo 		 * rate.
400be4e4fa3Skevlo 		 */
401be4e4fa3Skevlo 		rge_setup_intr(sc, RGE_IMTYPE_SIM);
402be4e4fa3Skevlo 	}
403be4e4fa3Skevlo 
404be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_IMR, sc->rge_intrs);
405be4e4fa3Skevlo 
406be4e4fa3Skevlo 	return (claimed);
407be4e4fa3Skevlo }
408be4e4fa3Skevlo 
409c6c75c18Sdlg static inline void
410c6c75c18Sdlg rge_tx_list_sync(struct rge_softc *sc, struct rge_queues *q,
411c6c75c18Sdlg     unsigned int idx, unsigned int len, int ops)
412c6c75c18Sdlg {
413c6c75c18Sdlg 	bus_dmamap_sync(sc->sc_dmat, q->q_tx.rge_tx_list_map,
414c6c75c18Sdlg 	    idx * sizeof(struct rge_tx_desc), len * sizeof(struct rge_tx_desc),
415c6c75c18Sdlg 	    ops);
416c6c75c18Sdlg }
417c6c75c18Sdlg 
418c6c75c18Sdlg static int
419c6c75c18Sdlg rge_encap(struct ifnet *ifp, struct rge_queues *q, struct mbuf *m, int idx)
420be4e4fa3Skevlo {
421804ff404Skevlo 	struct rge_softc *sc = q->q_sc;
422be4e4fa3Skevlo 	struct rge_tx_desc *d = NULL;
423be4e4fa3Skevlo 	struct rge_txq *txq;
424be4e4fa3Skevlo 	bus_dmamap_t txmap;
425be4e4fa3Skevlo 	uint32_t cmdsts, cflags = 0;
426c6c75c18Sdlg 	int cur, error, i;
427c6c75c18Sdlg #if NBPFILTER > 0
428c6c75c18Sdlg 	caddr_t if_bpf;
429c6c75c18Sdlg #endif
430be4e4fa3Skevlo 
431804ff404Skevlo 	txq = &q->q_tx.rge_txq[idx];
432be4e4fa3Skevlo 	txmap = txq->txq_dmamap;
433be4e4fa3Skevlo 
434be4e4fa3Skevlo 	error = bus_dmamap_load_mbuf(sc->sc_dmat, txmap, m, BUS_DMA_NOWAIT);
435be4e4fa3Skevlo 	switch (error) {
436be4e4fa3Skevlo 	case 0:
437be4e4fa3Skevlo 		break;
438be4e4fa3Skevlo 	case EFBIG: /* mbuf chain is too fragmented */
439be4e4fa3Skevlo 		if (m_defrag(m, M_DONTWAIT) == 0 &&
440be4e4fa3Skevlo 		    bus_dmamap_load_mbuf(sc->sc_dmat, txmap, m,
441be4e4fa3Skevlo 		    BUS_DMA_NOWAIT) == 0)
442be4e4fa3Skevlo 			break;
443be4e4fa3Skevlo 
444be4e4fa3Skevlo 		/* FALLTHROUGH */
445be4e4fa3Skevlo 	default:
446be4e4fa3Skevlo 		return (0);
447be4e4fa3Skevlo 	}
448be4e4fa3Skevlo 
449c6c75c18Sdlg #if NBPFILTER > 0
450c6c75c18Sdlg 	if_bpf = READ_ONCE(ifp->if_bpf);
451c6c75c18Sdlg 	if (if_bpf)
452c6c75c18Sdlg 		bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_OUT);
453c6c75c18Sdlg #endif
454c6c75c18Sdlg 
455be4e4fa3Skevlo 	bus_dmamap_sync(sc->sc_dmat, txmap, 0, txmap->dm_mapsize,
456be4e4fa3Skevlo 	    BUS_DMASYNC_PREWRITE);
457be4e4fa3Skevlo 
458c6c75c18Sdlg 	/*
459c6c75c18Sdlg 	 * Set RGE_TDEXTSTS_IPCSUM if any checksum offloading is requested.
460c6c75c18Sdlg 	 * Otherwise, RGE_TDEXTSTS_TCPCSUM / RGE_TDEXTSTS_UDPCSUM does not
461c6c75c18Sdlg 	 * take affect.
462c6c75c18Sdlg 	 */
463c6c75c18Sdlg 	if ((m->m_pkthdr.csum_flags &
464c6c75c18Sdlg 	    (M_IPV4_CSUM_OUT | M_TCP_CSUM_OUT | M_UDP_CSUM_OUT)) != 0) {
465c6c75c18Sdlg 		cflags |= RGE_TDEXTSTS_IPCSUM;
466c6c75c18Sdlg 		if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT)
467c6c75c18Sdlg 			cflags |= RGE_TDEXTSTS_TCPCSUM;
468c6c75c18Sdlg 		if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT)
469c6c75c18Sdlg 			cflags |= RGE_TDEXTSTS_UDPCSUM;
470c6c75c18Sdlg 	}
471be4e4fa3Skevlo 
472be4e4fa3Skevlo 	/* Set up hardware VLAN tagging. */
473be4e4fa3Skevlo #if NVLAN > 0
474be4e4fa3Skevlo 	if (m->m_flags & M_VLANTAG)
475cd7fda7fSkevlo 		cflags |= swap16(m->m_pkthdr.ether_vtag) | RGE_TDEXTSTS_VTAG;
476be4e4fa3Skevlo #endif
477be4e4fa3Skevlo 
478be4e4fa3Skevlo 	cur = idx;
479c6c75c18Sdlg 	for (i = 1; i < txmap->dm_nsegs; i++) {
480c6c75c18Sdlg 		cur = RGE_NEXT_TX_DESC(cur);
481be4e4fa3Skevlo 
482c6c75c18Sdlg 		cmdsts = RGE_TDCMDSTS_OWN;
483be4e4fa3Skevlo 		cmdsts |= txmap->dm_segs[i].ds_len;
484be4e4fa3Skevlo 
485be4e4fa3Skevlo 		if (cur == RGE_TX_LIST_CNT - 1)
486be4e4fa3Skevlo 			cmdsts |= RGE_TDCMDSTS_EOR;
487c6c75c18Sdlg 		if (i == txmap->dm_nsegs - 1)
488a772c87cSpatrick 			cmdsts |= RGE_TDCMDSTS_EOF;
489be4e4fa3Skevlo 
490c6c75c18Sdlg 		d = &q->q_tx.rge_tx_list[cur];
491be4e4fa3Skevlo 		d->rge_cmdsts = htole32(cmdsts);
492c6c75c18Sdlg 		d->rge_extsts = htole32(cflags);
493c6c75c18Sdlg 		d->rge_addr = htole64(txmap->dm_segs[i].ds_addr);
494be4e4fa3Skevlo 	}
495be4e4fa3Skevlo 
496be4e4fa3Skevlo 	/* Update info of TX queue and descriptors. */
497be4e4fa3Skevlo 	txq->txq_mbuf = m;
498c6c75c18Sdlg 	txq->txq_descidx = cur;
499be4e4fa3Skevlo 
500c6c75c18Sdlg 	cmdsts = RGE_TDCMDSTS_SOF;
501c6c75c18Sdlg 	cmdsts |= txmap->dm_segs[0].ds_len;
502c6c75c18Sdlg 
503c6c75c18Sdlg 	if (idx == RGE_TX_LIST_CNT - 1)
504c6c75c18Sdlg 		cmdsts |= RGE_TDCMDSTS_EOR;
505c6c75c18Sdlg 	if (txmap->dm_nsegs == 1)
506c6c75c18Sdlg 		cmdsts |= RGE_TDCMDSTS_EOF;
507c6c75c18Sdlg 
508c6c75c18Sdlg 	d = &q->q_tx.rge_tx_list[idx];
509c6c75c18Sdlg 	d->rge_cmdsts = htole32(cmdsts);
510c6c75c18Sdlg 	d->rge_extsts = htole32(cflags);
511c6c75c18Sdlg 	d->rge_addr = htole64(txmap->dm_segs[0].ds_addr);
512c6c75c18Sdlg 
513c6c75c18Sdlg 	if (cur >= idx) {
514c6c75c18Sdlg 		rge_tx_list_sync(sc, q, idx, txmap->dm_nsegs,
515c6c75c18Sdlg 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
516c6c75c18Sdlg 	} else {
517c6c75c18Sdlg 		rge_tx_list_sync(sc, q, idx, RGE_TX_LIST_CNT - idx,
518c6c75c18Sdlg 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
519c6c75c18Sdlg 		rge_tx_list_sync(sc, q, 0, cur + 1,
520c6c75c18Sdlg 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
521c6c75c18Sdlg 	}
522c6c75c18Sdlg 
523c6c75c18Sdlg 	/* Transfer ownership of packet to the chip. */
524c6c75c18Sdlg 	cmdsts |= RGE_TDCMDSTS_OWN;
525c6c75c18Sdlg 	rge_tx_list_sync(sc, q, idx, 1, BUS_DMASYNC_POSTWRITE);
526c6c75c18Sdlg 	d->rge_cmdsts = htole32(cmdsts);
527c6c75c18Sdlg 	rge_tx_list_sync(sc, q, idx, 1, BUS_DMASYNC_PREWRITE);
528c6c75c18Sdlg 
529c6c75c18Sdlg 	return (txmap->dm_nsegs);
530be4e4fa3Skevlo }
531be4e4fa3Skevlo 
532be4e4fa3Skevlo int
533be4e4fa3Skevlo rge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
534be4e4fa3Skevlo {
535be4e4fa3Skevlo 	struct rge_softc *sc = ifp->if_softc;
536be4e4fa3Skevlo 	struct ifreq *ifr = (struct ifreq *)data;
537be4e4fa3Skevlo 	int s, error = 0;
538be4e4fa3Skevlo 
539be4e4fa3Skevlo 	s = splnet();
540be4e4fa3Skevlo 
541be4e4fa3Skevlo 	switch (cmd) {
542be4e4fa3Skevlo 	case SIOCSIFADDR:
543be4e4fa3Skevlo 		ifp->if_flags |= IFF_UP;
544be4e4fa3Skevlo 		if (!(ifp->if_flags & IFF_RUNNING))
545be4e4fa3Skevlo 			rge_init(ifp);
546be4e4fa3Skevlo 		break;
547be4e4fa3Skevlo 	case SIOCSIFFLAGS:
548be4e4fa3Skevlo 		if (ifp->if_flags & IFF_UP) {
549be4e4fa3Skevlo 			if (ifp->if_flags & IFF_RUNNING)
550be4e4fa3Skevlo 				error = ENETRESET;
551be4e4fa3Skevlo 			else
552be4e4fa3Skevlo 				rge_init(ifp);
553be4e4fa3Skevlo 		} else {
554be4e4fa3Skevlo 			if (ifp->if_flags & IFF_RUNNING)
555be4e4fa3Skevlo 				rge_stop(ifp);
556be4e4fa3Skevlo 		}
557be4e4fa3Skevlo 		break;
558be4e4fa3Skevlo 	case SIOCGIFMEDIA:
559be4e4fa3Skevlo 	case SIOCSIFMEDIA:
560be4e4fa3Skevlo 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
561be4e4fa3Skevlo 		break;
56281a23f33Skevlo 	case SIOCGIFRXR:
56381a23f33Skevlo 		error = if_rxr_ioctl((struct if_rxrinfo *)ifr->ifr_data,
564da1cde29Sdlg 		    NULL, MCLBYTES, &sc->sc_queues->q_rx.rge_rx_ring);
56581a23f33Skevlo 		break;
566be4e4fa3Skevlo 	default:
567be4e4fa3Skevlo 		error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data);
568be4e4fa3Skevlo 	}
569be4e4fa3Skevlo 
570be4e4fa3Skevlo 	if (error == ENETRESET) {
571be4e4fa3Skevlo 		if (ifp->if_flags & IFF_RUNNING)
572be4e4fa3Skevlo 			rge_iff(sc);
573be4e4fa3Skevlo 		error = 0;
574be4e4fa3Skevlo 	}
575be4e4fa3Skevlo 
576be4e4fa3Skevlo 	splx(s);
577be4e4fa3Skevlo 	return (error);
578be4e4fa3Skevlo }
579be4e4fa3Skevlo 
580be4e4fa3Skevlo void
581be4e4fa3Skevlo rge_start(struct ifqueue *ifq)
582be4e4fa3Skevlo {
583be4e4fa3Skevlo 	struct ifnet *ifp = ifq->ifq_if;
584be4e4fa3Skevlo 	struct rge_softc *sc = ifp->if_softc;
585804ff404Skevlo 	struct rge_queues *q = sc->sc_queues;
586be4e4fa3Skevlo 	struct mbuf *m;
587be4e4fa3Skevlo 	int free, idx, used;
588be4e4fa3Skevlo 	int queued = 0;
589be4e4fa3Skevlo 
590be4e4fa3Skevlo 	if (!LINK_STATE_IS_UP(ifp->if_link_state)) {
591be4e4fa3Skevlo 		ifq_purge(ifq);
592be4e4fa3Skevlo 		return;
593be4e4fa3Skevlo 	}
594be4e4fa3Skevlo 
595be4e4fa3Skevlo 	/* Calculate free space. */
596804ff404Skevlo 	idx = q->q_tx.rge_txq_prodidx;
597804ff404Skevlo 	free = q->q_tx.rge_txq_considx;
598be4e4fa3Skevlo 	if (free <= idx)
599be4e4fa3Skevlo 		free += RGE_TX_LIST_CNT;
600be4e4fa3Skevlo 	free -= idx;
601be4e4fa3Skevlo 
602be4e4fa3Skevlo 	for (;;) {
6034ba1d670Sdlg 		if (free < RGE_TX_NSEGS + 2) {
604be4e4fa3Skevlo 			ifq_set_oactive(&ifp->if_snd);
605be4e4fa3Skevlo 			break;
606be4e4fa3Skevlo 		}
607be4e4fa3Skevlo 
608be4e4fa3Skevlo 		m = ifq_dequeue(ifq);
609be4e4fa3Skevlo 		if (m == NULL)
610be4e4fa3Skevlo 			break;
611be4e4fa3Skevlo 
612c6c75c18Sdlg 		used = rge_encap(ifp, q, m, idx);
613be4e4fa3Skevlo 		if (used == 0) {
614be4e4fa3Skevlo 			m_freem(m);
615be4e4fa3Skevlo 			continue;
616be4e4fa3Skevlo 		}
617be4e4fa3Skevlo 
618c6c75c18Sdlg 		KASSERT(used < free);
619be4e4fa3Skevlo 		free -= used;
620be4e4fa3Skevlo 
621be4e4fa3Skevlo 		idx += used;
622be4e4fa3Skevlo 		if (idx >= RGE_TX_LIST_CNT)
623be4e4fa3Skevlo 			idx -= RGE_TX_LIST_CNT;
624be4e4fa3Skevlo 
625be4e4fa3Skevlo 		queued++;
626be4e4fa3Skevlo 	}
627be4e4fa3Skevlo 
628be4e4fa3Skevlo 	if (queued == 0)
629be4e4fa3Skevlo 		return;
630be4e4fa3Skevlo 
631be4e4fa3Skevlo 	/* Set a timeout in case the chip goes out to lunch. */
632be4e4fa3Skevlo 	ifp->if_timer = 5;
633be4e4fa3Skevlo 
634804ff404Skevlo 	q->q_tx.rge_txq_prodidx = idx;
635be4e4fa3Skevlo 	ifq_serialize(ifq, &sc->sc_task);
636be4e4fa3Skevlo }
637be4e4fa3Skevlo 
638be4e4fa3Skevlo void
639be4e4fa3Skevlo rge_watchdog(struct ifnet *ifp)
640be4e4fa3Skevlo {
641be4e4fa3Skevlo 	struct rge_softc *sc = ifp->if_softc;
642be4e4fa3Skevlo 
643be4e4fa3Skevlo 	printf("%s: watchdog timeout\n", sc->sc_dev.dv_xname);
644be4e4fa3Skevlo 	ifp->if_oerrors++;
645be4e4fa3Skevlo 
646be4e4fa3Skevlo 	rge_init(ifp);
647be4e4fa3Skevlo }
648be4e4fa3Skevlo 
6498cbc3b0aSkevlo void
650be4e4fa3Skevlo rge_init(struct ifnet *ifp)
651be4e4fa3Skevlo {
652be4e4fa3Skevlo 	struct rge_softc *sc = ifp->if_softc;
653804ff404Skevlo 	struct rge_queues *q = sc->sc_queues;
65474263a62Skevlo 	uint32_t rxconf, val;
6558cbc3b0aSkevlo 	int i, num_miti;
656be4e4fa3Skevlo 
657be4e4fa3Skevlo 	rge_stop(ifp);
658be4e4fa3Skevlo 
659be4e4fa3Skevlo 	/* Set MAC address. */
660be4e4fa3Skevlo 	rge_set_macaddr(sc, sc->sc_arpcom.ac_enaddr);
661be4e4fa3Skevlo 
66281a23f33Skevlo 	/* Initialize RX and TX descriptors lists. */
663804ff404Skevlo 	rge_rx_list_init(q);
664804ff404Skevlo 	rge_tx_list_init(q);
665be4e4fa3Skevlo 
6668cbc3b0aSkevlo 	rge_chipinit(sc);
6678cbc3b0aSkevlo 
6688cbc3b0aSkevlo 	if (rge_phy_config(sc))
6698cbc3b0aSkevlo 		return;
6708cbc3b0aSkevlo 
6718cbc3b0aSkevlo 	RGE_SETBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
6728cbc3b0aSkevlo 
6738cbc3b0aSkevlo 	RGE_CLRBIT_1(sc, 0xf1, 0x80);
6748cbc3b0aSkevlo 	rge_disable_aspm_clkreq(sc);
6758cbc3b0aSkevlo 	RGE_WRITE_2(sc, RGE_EEE_TXIDLE_TIMER,
6768cbc3b0aSkevlo 	    RGE_JUMBO_MTU + ETHER_HDR_LEN + 32);
6778cbc3b0aSkevlo 
678be4e4fa3Skevlo 	/* Load the addresses of the RX and TX lists into the chip. */
679be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_RXDESC_ADDR_LO,
680804ff404Skevlo 	    RGE_ADDR_LO(q->q_rx.rge_rx_list_map->dm_segs[0].ds_addr));
681be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_RXDESC_ADDR_HI,
682804ff404Skevlo 	    RGE_ADDR_HI(q->q_rx.rge_rx_list_map->dm_segs[0].ds_addr));
683be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_TXDESC_ADDR_LO,
684804ff404Skevlo 	    RGE_ADDR_LO(q->q_tx.rge_tx_list_map->dm_segs[0].ds_addr));
685be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_TXDESC_ADDR_HI,
686804ff404Skevlo 	    RGE_ADDR_HI(q->q_tx.rge_tx_list_map->dm_segs[0].ds_addr));
687be4e4fa3Skevlo 
6883f3cdc7cSkevlo 	/* Set the initial RX and TX configurations. */
68974263a62Skevlo 	if (sc->rge_type == MAC_CFG3)
69074263a62Skevlo 		rxconf = RGE_RXCFG_CONFIG;
69174263a62Skevlo 	else if (sc->rge_type == MAC_CFG5)
69274263a62Skevlo 		rxconf = RGE_RXCFG_CONFIG_8125B;
69374263a62Skevlo 	else
69474263a62Skevlo 		rxconf = RGE_RXCFG_CONFIG_8126;
69574263a62Skevlo 	RGE_WRITE_4(sc, RGE_RXCFG, rxconf);
696be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_TXCFG, RGE_TXCFG_CONFIG);
697be4e4fa3Skevlo 
698be4e4fa3Skevlo 	val = rge_read_csi(sc, 0x70c) & ~0xff000000;
699be4e4fa3Skevlo 	rge_write_csi(sc, 0x70c, val | 0x27000000);
700be4e4fa3Skevlo 
70174263a62Skevlo 	if (sc->rge_type == MAC_CFG2_8126) {
70274263a62Skevlo 		/* Disable L1 timeout. */
70374263a62Skevlo 		val = rge_read_csi(sc, 0x890) & ~0x00000001;
70474263a62Skevlo 		rge_write_csi(sc, 0x890, val);
70574263a62Skevlo 	} else
706be4e4fa3Skevlo 		RGE_WRITE_2(sc, 0x0382, 0x221b);
707804ff404Skevlo 
708804ff404Skevlo 	RGE_WRITE_1(sc, RGE_RSS_CTRL, 0);
709804ff404Skevlo 
710804ff404Skevlo 	val = RGE_READ_2(sc, RGE_RXQUEUE_CTRL) & ~0x001c;
711804ff404Skevlo 	RGE_WRITE_2(sc, RGE_RXQUEUE_CTRL, val | (fls(sc->sc_nqueues) - 1) << 2);
712804ff404Skevlo 
713be4e4fa3Skevlo 	RGE_CLRBIT_1(sc, RGE_CFG1, RGE_CFG1_SPEED_DOWN);
714be4e4fa3Skevlo 
715be4e4fa3Skevlo 	rge_write_mac_ocp(sc, 0xc140, 0xffff);
716be4e4fa3Skevlo 	rge_write_mac_ocp(sc, 0xc142, 0xffff);
717be4e4fa3Skevlo 
718be4e4fa3Skevlo 	RGE_MAC_SETBIT(sc, 0xeb58, 0x0001);
719be4e4fa3Skevlo 
72074263a62Skevlo 	if (sc->rge_type == MAC_CFG2_8126)
72174263a62Skevlo 		RGE_CLRBIT_1(sc, 0xd8, 0x02);
72274263a62Skevlo 
723be4e4fa3Skevlo 	val = rge_read_mac_ocp(sc, 0xe614) & ~0x0700;
7248cbc3b0aSkevlo 	if (sc->rge_type == MAC_CFG3)
7258cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xe614, val | 0x0300);
72674263a62Skevlo 	else if (sc->rge_type == MAC_CFG5)
72781a23f33Skevlo 		rge_write_mac_ocp(sc, 0xe614, val | 0x0200);
72874263a62Skevlo 	else
72974263a62Skevlo 		rge_write_mac_ocp(sc, 0xe614, val | 0x0400);
730be4e4fa3Skevlo 
731804ff404Skevlo 	val = rge_read_mac_ocp(sc, 0xe63e) & ~0x0c00;
732804ff404Skevlo 	rge_write_mac_ocp(sc, 0xe63e, val |
733804ff404Skevlo 	    ((fls(sc->sc_nqueues) - 1) & 0x03) << 10);
734be4e4fa3Skevlo 
73581a23f33Skevlo 	RGE_MAC_CLRBIT(sc, 0xe63e, 0x0030);
73674263a62Skevlo 	if (sc->rge_type != MAC_CFG5)
737804ff404Skevlo 		RGE_MAC_SETBIT(sc, 0xe63e, 0x0020);
738be4e4fa3Skevlo 
739804ff404Skevlo 	RGE_MAC_CLRBIT(sc, 0xc0b4, 0x0001);
740804ff404Skevlo 	RGE_MAC_SETBIT(sc, 0xc0b4, 0x0001);
7418cbc3b0aSkevlo 
742be4e4fa3Skevlo 	RGE_MAC_SETBIT(sc, 0xc0b4, 0x000c);
743be4e4fa3Skevlo 
74481a23f33Skevlo 	val = rge_read_mac_ocp(sc, 0xeb6a) & ~0x00ff;
745be4e4fa3Skevlo 	rge_write_mac_ocp(sc, 0xeb6a, val | 0x0033);
746be4e4fa3Skevlo 
747be4e4fa3Skevlo 	val = rge_read_mac_ocp(sc, 0xeb50) & ~0x03e0;
748be4e4fa3Skevlo 	rge_write_mac_ocp(sc, 0xeb50, val | 0x0040);
749be4e4fa3Skevlo 
7508cbc3b0aSkevlo 	RGE_MAC_CLRBIT(sc, 0xe056, 0x00f0);
751be4e4fa3Skevlo 
752be4e4fa3Skevlo 	RGE_WRITE_1(sc, RGE_TDFNR, 0x10);
753be4e4fa3Skevlo 
754be4e4fa3Skevlo 	RGE_MAC_CLRBIT(sc, 0xe040, 0x1000);
755be4e4fa3Skevlo 
75681a23f33Skevlo 	val = rge_read_mac_ocp(sc, 0xea1c) & ~0x0003;
75781a23f33Skevlo 	rge_write_mac_ocp(sc, 0xea1c, val | 0x0001);
75881a23f33Skevlo 
7598cbc3b0aSkevlo 	rge_write_mac_ocp(sc, 0xe0c0, 0x4000);
760be4e4fa3Skevlo 
7618cbc3b0aSkevlo 	RGE_MAC_SETBIT(sc, 0xe052, 0x0060);
7628cbc3b0aSkevlo 	RGE_MAC_CLRBIT(sc, 0xe052, 0x0088);
763be4e4fa3Skevlo 
764be4e4fa3Skevlo 	val = rge_read_mac_ocp(sc, 0xd430) & ~0x0fff;
7658cbc3b0aSkevlo 	rge_write_mac_ocp(sc, 0xd430, val | 0x045f);
766be4e4fa3Skevlo 
767804ff404Skevlo 	RGE_SETBIT_1(sc, RGE_DLLPR, RGE_DLLPR_PFM_EN | RGE_DLLPR_TX_10M_PS_EN);
76881a23f33Skevlo 
7698cbc3b0aSkevlo 	if (sc->rge_type == MAC_CFG3)
77081a23f33Skevlo 		RGE_SETBIT_1(sc, RGE_MCUCMD, 0x01);
771be4e4fa3Skevlo 
772be4e4fa3Skevlo 	/* Disable EEE plus. */
773be4e4fa3Skevlo 	RGE_MAC_CLRBIT(sc, 0xe080, 0x0002);
774be4e4fa3Skevlo 
77574263a62Skevlo 	if (sc->rge_type == MAC_CFG2_8126)
77674263a62Skevlo 		RGE_MAC_CLRBIT(sc, 0xea1c, 0x0304);
77774263a62Skevlo 	else
778be4e4fa3Skevlo 		RGE_MAC_CLRBIT(sc, 0xea1c, 0x0004);
779be4e4fa3Skevlo 
780be4e4fa3Skevlo 	RGE_MAC_SETBIT(sc, 0xeb54, 0x0001);
781be4e4fa3Skevlo 	DELAY(1);
782be4e4fa3Skevlo 	RGE_MAC_CLRBIT(sc, 0xeb54, 0x0001);
783be4e4fa3Skevlo 
7848cbc3b0aSkevlo 	RGE_CLRBIT_2(sc, 0x1880, 0x0030);
7858cbc3b0aSkevlo 
78674263a62Skevlo 	/* Config interrupt type for RTL8125B/RTL8126. */
78774263a62Skevlo 	if (sc->rge_type != MAC_CFG3)
7888cbc3b0aSkevlo 		RGE_CLRBIT_1(sc, RGE_INT_CFG0, RGE_INT_CFG0_EN);
7898cbc3b0aSkevlo 
7908cbc3b0aSkevlo 	/* Clear timer interrupts. */
7918cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_TIMERINT0, 0);
7928cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_TIMERINT1, 0);
7938cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_TIMERINT2, 0);
7948cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_TIMERINT3, 0);
7958cbc3b0aSkevlo 
7968cbc3b0aSkevlo 	num_miti = (sc->rge_type == MAC_CFG3) ? 64 : 32;
7978cbc3b0aSkevlo 	/* Clear interrupt moderation timer. */
7988cbc3b0aSkevlo 	for (i = 0; i < num_miti; i++)
7998cbc3b0aSkevlo 		RGE_WRITE_4(sc, RGE_INTMITI(i), 0);
8008cbc3b0aSkevlo 
8018cbc3b0aSkevlo 	if (sc->rge_type == MAC_CFG5) {
8028cbc3b0aSkevlo 		RGE_CLRBIT_1(sc, RGE_INT_CFG0,
8038cbc3b0aSkevlo 		    RGE_INT_CFG0_TIMEOUT_BYPASS |
8048cbc3b0aSkevlo 		    RGE_INT_CFG0_MITIGATION_BYPASS);
8058cbc3b0aSkevlo 		RGE_WRITE_2(sc, RGE_INT_CFG1, 0);
8068cbc3b0aSkevlo 	}
8078cbc3b0aSkevlo 
8088cbc3b0aSkevlo 	RGE_MAC_SETBIT(sc, 0xc0ac, 0x1f80);
809be4e4fa3Skevlo 
810be4e4fa3Skevlo 	rge_write_mac_ocp(sc, 0xe098, 0xc302);
811be4e4fa3Skevlo 
8128cbc3b0aSkevlo 	RGE_MAC_CLRBIT(sc, 0xe032, 0x0003);
8138cbc3b0aSkevlo 	val = rge_read_csi(sc, 0x98) & ~0x0000ff00;
8148cbc3b0aSkevlo 	rge_write_csi(sc, 0x98, val);
8158cbc3b0aSkevlo 
8168cbc3b0aSkevlo 	val = rge_read_mac_ocp(sc, 0xe092) & ~0x00ff;
8178cbc3b0aSkevlo 	rge_write_mac_ocp(sc, 0xe092, val);
8188cbc3b0aSkevlo 
819be4e4fa3Skevlo 	if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING)
820be4e4fa3Skevlo 		RGE_SETBIT_4(sc, RGE_RXCFG, RGE_RXCFG_VLANSTRIP);
821be4e4fa3Skevlo 
822be4e4fa3Skevlo 	RGE_SETBIT_2(sc, RGE_CPLUSCMD, RGE_CPLUSCMD_RXCSUM);
823be4e4fa3Skevlo 
8248cbc3b0aSkevlo 	/* Set Maximum frame size. */
8258cbc3b0aSkevlo 	RGE_WRITE_2(sc, RGE_RXMAXSIZE, RGE_JUMBO_FRAMELEN);
826be4e4fa3Skevlo 
827be4e4fa3Skevlo 	/* Disable RXDV gate. */
828be4e4fa3Skevlo 	RGE_CLRBIT_1(sc, RGE_PPSW, 0x08);
829be4e4fa3Skevlo 	DELAY(2000);
830be4e4fa3Skevlo 
8318cbc3b0aSkevlo 	/* Program promiscuous mode and multicast filters. */
8328cbc3b0aSkevlo 	rge_iff(sc);
8338cbc3b0aSkevlo 
8348cbc3b0aSkevlo 	rge_disable_aspm_clkreq(sc);
8358cbc3b0aSkevlo 
8368cbc3b0aSkevlo 	RGE_CLRBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
8378cbc3b0aSkevlo 	DELAY(10);
8388cbc3b0aSkevlo 
839be4e4fa3Skevlo 	rge_ifmedia_upd(ifp);
840be4e4fa3Skevlo 
841be4e4fa3Skevlo 	/* Enable transmit and receive. */
842be4e4fa3Skevlo 	RGE_WRITE_1(sc, RGE_CMD, RGE_CMD_TXENB | RGE_CMD_RXENB);
843be4e4fa3Skevlo 
844be4e4fa3Skevlo 	/* Enable interrupts. */
845be4e4fa3Skevlo 	rge_setup_intr(sc, RGE_IMTYPE_SIM);
846be4e4fa3Skevlo 
847be4e4fa3Skevlo 	ifp->if_flags |= IFF_RUNNING;
848be4e4fa3Skevlo 	ifq_clr_oactive(&ifp->if_snd);
849be4e4fa3Skevlo 
850be4e4fa3Skevlo 	timeout_add_sec(&sc->sc_timeout, 1);
851be4e4fa3Skevlo }
852be4e4fa3Skevlo 
853be4e4fa3Skevlo /*
854be4e4fa3Skevlo  * Stop the adapter and free any mbufs allocated to the RX and TX lists.
855be4e4fa3Skevlo  */
856be4e4fa3Skevlo void
857be4e4fa3Skevlo rge_stop(struct ifnet *ifp)
858be4e4fa3Skevlo {
859be4e4fa3Skevlo 	struct rge_softc *sc = ifp->if_softc;
860804ff404Skevlo 	struct rge_queues *q = sc->sc_queues;
861be4e4fa3Skevlo 	int i;
862be4e4fa3Skevlo 
863be4e4fa3Skevlo 	timeout_del(&sc->sc_timeout);
864be4e4fa3Skevlo 
865be4e4fa3Skevlo 	ifp->if_timer = 0;
866be4e4fa3Skevlo 	ifp->if_flags &= ~IFF_RUNNING;
867be4e4fa3Skevlo 	sc->rge_timerintr = 0;
868be4e4fa3Skevlo 
869be4e4fa3Skevlo 	RGE_CLRBIT_4(sc, RGE_RXCFG, RGE_RXCFG_ALLPHYS | RGE_RXCFG_INDIV |
870be4e4fa3Skevlo 	    RGE_RXCFG_MULTI | RGE_RXCFG_BROAD | RGE_RXCFG_RUNT |
871be4e4fa3Skevlo 	    RGE_RXCFG_ERRPKT);
872be4e4fa3Skevlo 
8738cbc3b0aSkevlo 	rge_hw_reset(sc);
87481a23f33Skevlo 
8758cbc3b0aSkevlo 	RGE_MAC_CLRBIT(sc, 0xc0ac, 0x1f80);
876be4e4fa3Skevlo 
877be4e4fa3Skevlo 	intr_barrier(sc->sc_ih);
878be4e4fa3Skevlo 	ifq_barrier(&ifp->if_snd);
879be4e4fa3Skevlo 	ifq_clr_oactive(&ifp->if_snd);
880be4e4fa3Skevlo 
881e21346faSdlg 	if (q->q_rx.rge_head != NULL) {
882e21346faSdlg 		m_freem(q->q_rx.rge_head);
883e21346faSdlg 		q->q_rx.rge_head = NULL;
884e21346faSdlg 		q->q_rx.rge_tail = &q->q_rx.rge_head;
885e21346faSdlg 	}
886e21346faSdlg 
887be4e4fa3Skevlo 	/* Free the TX list buffers. */
888be4e4fa3Skevlo 	for (i = 0; i < RGE_TX_LIST_CNT; i++) {
889804ff404Skevlo 		if (q->q_tx.rge_txq[i].txq_mbuf != NULL) {
890be4e4fa3Skevlo 			bus_dmamap_unload(sc->sc_dmat,
891804ff404Skevlo 			    q->q_tx.rge_txq[i].txq_dmamap);
892804ff404Skevlo 			m_freem(q->q_tx.rge_txq[i].txq_mbuf);
893804ff404Skevlo 			q->q_tx.rge_txq[i].txq_mbuf = NULL;
894be4e4fa3Skevlo 		}
895be4e4fa3Skevlo 	}
896be4e4fa3Skevlo 
897be4e4fa3Skevlo 	/* Free the RX list buffers. */
898be4e4fa3Skevlo 	for (i = 0; i < RGE_RX_LIST_CNT; i++) {
899804ff404Skevlo 		if (q->q_rx.rge_rxq[i].rxq_mbuf != NULL) {
900be4e4fa3Skevlo 			bus_dmamap_unload(sc->sc_dmat,
901804ff404Skevlo 			    q->q_rx.rge_rxq[i].rxq_dmamap);
902804ff404Skevlo 			m_freem(q->q_rx.rge_rxq[i].rxq_mbuf);
903804ff404Skevlo 			q->q_rx.rge_rxq[i].rxq_mbuf = NULL;
904be4e4fa3Skevlo 		}
905be4e4fa3Skevlo 	}
906be4e4fa3Skevlo }
907be4e4fa3Skevlo 
908be4e4fa3Skevlo /*
909be4e4fa3Skevlo  * Set media options.
910be4e4fa3Skevlo  */
911be4e4fa3Skevlo int
912be4e4fa3Skevlo rge_ifmedia_upd(struct ifnet *ifp)
913be4e4fa3Skevlo {
914be4e4fa3Skevlo 	struct rge_softc *sc = ifp->if_softc;
915be4e4fa3Skevlo 	struct ifmedia *ifm = &sc->sc_media;
916be4e4fa3Skevlo 	int anar, gig, val;
917be4e4fa3Skevlo 
918be4e4fa3Skevlo 	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
919be4e4fa3Skevlo 		return (EINVAL);
920be4e4fa3Skevlo 
921be4e4fa3Skevlo 	/* Disable Gigabit Lite. */
922be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xa428, 0x0200);
923be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xa5ea, 0x0001);
92474263a62Skevlo 	if (sc->rge_type == MAC_CFG2_8126)
92574263a62Skevlo 		RGE_PHY_CLRBIT(sc, 0xa5ea, 0x0002);
926be4e4fa3Skevlo 
927be4e4fa3Skevlo 	val = rge_read_phy_ocp(sc, 0xa5d4);
928be4e4fa3Skevlo 	val &= ~RGE_ADV_2500TFDX;
92974263a62Skevlo 	if (sc->rge_type == MAC_CFG2_8126)
93074263a62Skevlo 		val &= ~RGE_ADV_5000TFDX;
931be4e4fa3Skevlo 
93274263a62Skevlo 	anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
93374263a62Skevlo 	gig = GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
93474263a62Skevlo 
935be4e4fa3Skevlo 	switch (IFM_SUBTYPE(ifm->ifm_media)) {
936be4e4fa3Skevlo 	case IFM_AUTO:
93774263a62Skevlo 		val |= (sc->rge_type != MAC_CFG2_8126) ?
93874263a62Skevlo 		    RGE_ADV_2500TFDX : (RGE_ADV_2500TFDX | RGE_ADV_5000TFDX);
93974263a62Skevlo 		break;
94074263a62Skevlo 	case IFM_5000_T:
94174263a62Skevlo 		val |= RGE_ADV_5000TFDX;
94274263a62Skevlo 		ifp->if_baudrate = IF_Gbps(5);
943be4e4fa3Skevlo 		break;
944be4e4fa3Skevlo 	case IFM_2500_T:
945be4e4fa3Skevlo 		val |= RGE_ADV_2500TFDX;
946be4e4fa3Skevlo 		ifp->if_baudrate = IF_Mbps(2500);
947be4e4fa3Skevlo 		break;
948be4e4fa3Skevlo 	case IFM_1000_T:
949be4e4fa3Skevlo 		ifp->if_baudrate = IF_Gbps(1);
950be4e4fa3Skevlo 		break;
951be4e4fa3Skevlo 	case IFM_100_TX:
952dfa3bc0bSkevlo 		gig = rge_read_phy(sc, 0, MII_100T2CR) &
953dfa3bc0bSkevlo 		    ~(GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX);
954dfa3bc0bSkevlo 		anar = ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) ?
955dfa3bc0bSkevlo 		    ANAR_TX | ANAR_TX_FD | ANAR_10_FD | ANAR_10 :
956dfa3bc0bSkevlo 		    ANAR_TX | ANAR_10_FD | ANAR_10;
957be4e4fa3Skevlo 		ifp->if_baudrate = IF_Mbps(100);
958be4e4fa3Skevlo 		break;
959be4e4fa3Skevlo 	case IFM_10_T:
960dfa3bc0bSkevlo 		gig = rge_read_phy(sc, 0, MII_100T2CR) &
961dfa3bc0bSkevlo 		    ~(GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX);
962dfa3bc0bSkevlo 		anar = ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) ?
963dfa3bc0bSkevlo 		    ANAR_10_FD | ANAR_10 : ANAR_10;
964be4e4fa3Skevlo 		ifp->if_baudrate = IF_Mbps(10);
965be4e4fa3Skevlo 		break;
966be4e4fa3Skevlo 	default:
967be4e4fa3Skevlo 		printf("%s: unsupported media type\n", sc->sc_dev.dv_xname);
968be4e4fa3Skevlo 		return (EINVAL);
969be4e4fa3Skevlo 	}
970be4e4fa3Skevlo 
971be4e4fa3Skevlo 	rge_write_phy(sc, 0, MII_ANAR, anar | ANAR_PAUSE_ASYM | ANAR_FC);
972be4e4fa3Skevlo 	rge_write_phy(sc, 0, MII_100T2CR, gig);
973be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xa5d4, val);
974dfa3bc0bSkevlo 	rge_write_phy(sc, 0, MII_BMCR, BMCR_RESET | BMCR_AUTOEN |
975dfa3bc0bSkevlo 	    BMCR_STARTNEG);
976be4e4fa3Skevlo 
977be4e4fa3Skevlo 	return (0);
978be4e4fa3Skevlo }
979be4e4fa3Skevlo 
980be4e4fa3Skevlo /*
981be4e4fa3Skevlo  * Report current media status.
982be4e4fa3Skevlo  */
983be4e4fa3Skevlo void
984be4e4fa3Skevlo rge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
985be4e4fa3Skevlo {
986be4e4fa3Skevlo 	struct rge_softc *sc = ifp->if_softc;
987be4e4fa3Skevlo 	uint16_t status = 0;
988be4e4fa3Skevlo 
989be4e4fa3Skevlo 	ifmr->ifm_status = IFM_AVALID;
990be4e4fa3Skevlo 	ifmr->ifm_active = IFM_ETHER;
991be4e4fa3Skevlo 
992be4e4fa3Skevlo 	if (rge_get_link_status(sc)) {
993be4e4fa3Skevlo 		ifmr->ifm_status |= IFM_ACTIVE;
994be4e4fa3Skevlo 
995be4e4fa3Skevlo 		status = RGE_READ_2(sc, RGE_PHYSTAT);
996be4e4fa3Skevlo 		if ((status & RGE_PHYSTAT_FDX) ||
99774263a62Skevlo 		    (status & (RGE_PHYSTAT_2500MBPS | RGE_PHYSTAT_5000MBPS)))
998be4e4fa3Skevlo 			ifmr->ifm_active |= IFM_FDX;
999be4e4fa3Skevlo 		else
1000be4e4fa3Skevlo 			ifmr->ifm_active |= IFM_HDX;
1001be4e4fa3Skevlo 
1002be4e4fa3Skevlo 		if (status & RGE_PHYSTAT_10MBPS)
1003be4e4fa3Skevlo 			ifmr->ifm_active |= IFM_10_T;
1004be4e4fa3Skevlo 		else if (status & RGE_PHYSTAT_100MBPS)
1005be4e4fa3Skevlo 			ifmr->ifm_active |= IFM_100_TX;
1006be4e4fa3Skevlo 		else if (status & RGE_PHYSTAT_1000MBPS)
1007be4e4fa3Skevlo 			ifmr->ifm_active |= IFM_1000_T;
1008be4e4fa3Skevlo 		else if (status & RGE_PHYSTAT_2500MBPS)
1009be4e4fa3Skevlo 			ifmr->ifm_active |= IFM_2500_T;
101074263a62Skevlo 		else if (status & RGE_PHYSTAT_5000MBPS)
101174263a62Skevlo 			ifmr->ifm_active |= IFM_5000_T;
1012be4e4fa3Skevlo 	}
1013be4e4fa3Skevlo }
1014be4e4fa3Skevlo 
1015be4e4fa3Skevlo /*
1016be4e4fa3Skevlo  * Allocate memory for RX/TX rings.
1017be4e4fa3Skevlo  */
1018be4e4fa3Skevlo int
1019be4e4fa3Skevlo rge_allocmem(struct rge_softc *sc)
1020be4e4fa3Skevlo {
1021804ff404Skevlo 	struct rge_queues *q = sc->sc_queues;
1022be4e4fa3Skevlo 	int error, i;
1023be4e4fa3Skevlo 
1024be4e4fa3Skevlo 	/* Allocate DMA'able memory for the TX ring. */
1025be4e4fa3Skevlo 	error = bus_dmamap_create(sc->sc_dmat, RGE_TX_LIST_SZ, 1,
10268cbc3b0aSkevlo 	    RGE_TX_LIST_SZ, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
10278cbc3b0aSkevlo 	    &q->q_tx.rge_tx_list_map);
1028be4e4fa3Skevlo 	if (error) {
1029be4e4fa3Skevlo 		printf("%s: can't create TX list map\n", sc->sc_dev.dv_xname);
1030be4e4fa3Skevlo 		return (error);
1031be4e4fa3Skevlo 	}
1032be4e4fa3Skevlo 	error = bus_dmamem_alloc(sc->sc_dmat, RGE_TX_LIST_SZ, RGE_ALIGN, 0,
1033804ff404Skevlo 	    &q->q_tx.rge_tx_listseg, 1, &q->q_tx.rge_tx_listnseg,
1034be4e4fa3Skevlo 	    BUS_DMA_NOWAIT| BUS_DMA_ZERO);
1035be4e4fa3Skevlo 	if (error) {
1036be4e4fa3Skevlo 		printf("%s: can't alloc TX list\n", sc->sc_dev.dv_xname);
1037be4e4fa3Skevlo 		return (error);
1038be4e4fa3Skevlo 	}
1039be4e4fa3Skevlo 
1040be4e4fa3Skevlo 	/* Load the map for the TX ring. */
1041804ff404Skevlo 	error = bus_dmamem_map(sc->sc_dmat, &q->q_tx.rge_tx_listseg,
1042804ff404Skevlo 	    q->q_tx.rge_tx_listnseg, RGE_TX_LIST_SZ,
1043804ff404Skevlo 	    (caddr_t *)&q->q_tx.rge_tx_list, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
1044be4e4fa3Skevlo 	if (error) {
1045be4e4fa3Skevlo 		printf("%s: can't map TX dma buffers\n", sc->sc_dev.dv_xname);
1046804ff404Skevlo 		bus_dmamem_free(sc->sc_dmat, &q->q_tx.rge_tx_listseg,
1047804ff404Skevlo 		    q->q_tx.rge_tx_listnseg);
1048be4e4fa3Skevlo 		return (error);
1049be4e4fa3Skevlo 	}
1050804ff404Skevlo 	error = bus_dmamap_load(sc->sc_dmat, q->q_tx.rge_tx_list_map,
1051804ff404Skevlo 	    q->q_tx.rge_tx_list, RGE_TX_LIST_SZ, NULL, BUS_DMA_NOWAIT);
1052be4e4fa3Skevlo 	if (error) {
1053be4e4fa3Skevlo 		printf("%s: can't load TX dma map\n", sc->sc_dev.dv_xname);
1054804ff404Skevlo 		bus_dmamap_destroy(sc->sc_dmat, q->q_tx.rge_tx_list_map);
1055be4e4fa3Skevlo 		bus_dmamem_unmap(sc->sc_dmat,
1056804ff404Skevlo 		    (caddr_t)q->q_tx.rge_tx_list, RGE_TX_LIST_SZ);
1057804ff404Skevlo 		bus_dmamem_free(sc->sc_dmat, &q->q_tx.rge_tx_listseg,
1058804ff404Skevlo 		    q->q_tx.rge_tx_listnseg);
1059be4e4fa3Skevlo 		return (error);
1060be4e4fa3Skevlo 	}
1061be4e4fa3Skevlo 
1062be4e4fa3Skevlo 	/* Create DMA maps for TX buffers. */
1063be4e4fa3Skevlo 	for (i = 0; i < RGE_TX_LIST_CNT; i++) {
1064be4e4fa3Skevlo 		error = bus_dmamap_create(sc->sc_dmat, RGE_JUMBO_FRAMELEN,
10658cbc3b0aSkevlo 		    RGE_TX_NSEGS, RGE_JUMBO_FRAMELEN, 0,
10668cbc3b0aSkevlo 		    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
1067804ff404Skevlo 		    &q->q_tx.rge_txq[i].txq_dmamap);
1068be4e4fa3Skevlo 		if (error) {
1069be4e4fa3Skevlo 			printf("%s: can't create DMA map for TX\n",
1070be4e4fa3Skevlo 			    sc->sc_dev.dv_xname);
1071be4e4fa3Skevlo 			return (error);
1072be4e4fa3Skevlo 		}
1073be4e4fa3Skevlo 	}
1074be4e4fa3Skevlo 
1075be4e4fa3Skevlo 	/* Allocate DMA'able memory for the RX ring. */
1076be4e4fa3Skevlo 	error = bus_dmamap_create(sc->sc_dmat, RGE_RX_LIST_SZ, 1,
10778cbc3b0aSkevlo 	    RGE_RX_LIST_SZ, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
10788cbc3b0aSkevlo 	    &q->q_rx.rge_rx_list_map);
1079be4e4fa3Skevlo 	if (error) {
1080be4e4fa3Skevlo 		printf("%s: can't create RX list map\n", sc->sc_dev.dv_xname);
1081be4e4fa3Skevlo 		return (error);
1082be4e4fa3Skevlo 	}
1083be4e4fa3Skevlo 	error = bus_dmamem_alloc(sc->sc_dmat, RGE_RX_LIST_SZ, RGE_ALIGN, 0,
1084804ff404Skevlo 	    &q->q_rx.rge_rx_listseg, 1, &q->q_rx.rge_rx_listnseg,
1085be4e4fa3Skevlo 	    BUS_DMA_NOWAIT| BUS_DMA_ZERO);
1086be4e4fa3Skevlo 	if (error) {
1087be4e4fa3Skevlo 		printf("%s: can't alloc RX list\n", sc->sc_dev.dv_xname);
1088be4e4fa3Skevlo 		return (error);
1089be4e4fa3Skevlo 	}
1090be4e4fa3Skevlo 
1091be4e4fa3Skevlo 	/* Load the map for the RX ring. */
1092804ff404Skevlo 	error = bus_dmamem_map(sc->sc_dmat, &q->q_rx.rge_rx_listseg,
1093804ff404Skevlo 	    q->q_rx.rge_rx_listnseg, RGE_RX_LIST_SZ,
1094804ff404Skevlo 	    (caddr_t *)&q->q_rx.rge_rx_list, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
1095be4e4fa3Skevlo 	if (error) {
1096be4e4fa3Skevlo 		printf("%s: can't map RX dma buffers\n", sc->sc_dev.dv_xname);
1097804ff404Skevlo 		bus_dmamem_free(sc->sc_dmat, &q->q_rx.rge_rx_listseg,
1098804ff404Skevlo 		    q->q_rx.rge_rx_listnseg);
1099be4e4fa3Skevlo 		return (error);
1100be4e4fa3Skevlo 	}
1101804ff404Skevlo 	error = bus_dmamap_load(sc->sc_dmat, q->q_rx.rge_rx_list_map,
1102804ff404Skevlo 	    q->q_rx.rge_rx_list, RGE_RX_LIST_SZ, NULL, BUS_DMA_NOWAIT);
1103be4e4fa3Skevlo 	if (error) {
1104be4e4fa3Skevlo 		printf("%s: can't load RX dma map\n", sc->sc_dev.dv_xname);
1105804ff404Skevlo 		bus_dmamap_destroy(sc->sc_dmat, q->q_rx.rge_rx_list_map);
1106be4e4fa3Skevlo 		bus_dmamem_unmap(sc->sc_dmat,
1107804ff404Skevlo 		    (caddr_t)q->q_rx.rge_rx_list, RGE_RX_LIST_SZ);
1108804ff404Skevlo 		bus_dmamem_free(sc->sc_dmat, &q->q_rx.rge_rx_listseg,
1109804ff404Skevlo 		    q->q_rx.rge_rx_listnseg);
1110be4e4fa3Skevlo 		return (error);
1111be4e4fa3Skevlo 	}
1112be4e4fa3Skevlo 
1113be4e4fa3Skevlo 	/* Create DMA maps for RX buffers. */
1114be4e4fa3Skevlo 	for (i = 0; i < RGE_RX_LIST_CNT; i++) {
1115be4e4fa3Skevlo 		error = bus_dmamap_create(sc->sc_dmat, RGE_JUMBO_FRAMELEN, 1,
11168cbc3b0aSkevlo 		    RGE_JUMBO_FRAMELEN, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
1117804ff404Skevlo 		    &q->q_rx.rge_rxq[i].rxq_dmamap);
1118be4e4fa3Skevlo 		if (error) {
1119be4e4fa3Skevlo 			printf("%s: can't create DMA map for RX\n",
1120be4e4fa3Skevlo 			    sc->sc_dev.dv_xname);
1121be4e4fa3Skevlo 			return (error);
1122be4e4fa3Skevlo 		}
1123be4e4fa3Skevlo 	}
1124be4e4fa3Skevlo 
1125be4e4fa3Skevlo 	return (error);
1126be4e4fa3Skevlo }
1127be4e4fa3Skevlo 
1128be4e4fa3Skevlo /*
1129be4e4fa3Skevlo  * Initialize the RX descriptor and attach an mbuf cluster.
1130be4e4fa3Skevlo  */
1131be4e4fa3Skevlo int
1132804ff404Skevlo rge_newbuf(struct rge_queues *q)
1133be4e4fa3Skevlo {
1134804ff404Skevlo 	struct rge_softc *sc = q->q_sc;
1135be4e4fa3Skevlo 	struct mbuf *m;
1136be4e4fa3Skevlo 	struct rge_rx_desc *r;
1137be4e4fa3Skevlo 	struct rge_rxq *rxq;
1138be4e4fa3Skevlo 	bus_dmamap_t rxmap;
11392ef55e5bSdlg 	uint32_t cmdsts;
114081a23f33Skevlo 	int idx;
1141be4e4fa3Skevlo 
1142da1cde29Sdlg 	m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES);
1143be4e4fa3Skevlo 	if (m == NULL)
1144be4e4fa3Skevlo 		return (ENOBUFS);
1145be4e4fa3Skevlo 
1146da1cde29Sdlg 	m->m_len = m->m_pkthdr.len = MCLBYTES;
11471361d169Sdlg 	m_adj(m, ETHER_ALIGN);
1148be4e4fa3Skevlo 
1149804ff404Skevlo 	idx = q->q_rx.rge_rxq_prodidx;
1150804ff404Skevlo 	rxq = &q->q_rx.rge_rxq[idx];
1151be4e4fa3Skevlo 	rxmap = rxq->rxq_dmamap;
1152be4e4fa3Skevlo 
115381a23f33Skevlo 	if (bus_dmamap_load_mbuf(sc->sc_dmat, rxmap, m, BUS_DMA_NOWAIT)) {
115481a23f33Skevlo 		m_freem(m);
115581a23f33Skevlo 		return (ENOBUFS);
115681a23f33Skevlo 	}
1157be4e4fa3Skevlo 
1158be4e4fa3Skevlo 	bus_dmamap_sync(sc->sc_dmat, rxmap, 0, rxmap->dm_mapsize,
1159be4e4fa3Skevlo 	    BUS_DMASYNC_PREREAD);
1160be4e4fa3Skevlo 
1161be4e4fa3Skevlo 	/* Map the segments into RX descriptors. */
1162804ff404Skevlo 	r = &q->q_rx.rge_rx_list[idx];
1163be4e4fa3Skevlo 
1164be4e4fa3Skevlo 	rxq->rxq_mbuf = m;
1165be4e4fa3Skevlo 
11662ef55e5bSdlg 	cmdsts = rxmap->dm_segs[0].ds_len;
11672ef55e5bSdlg 	if (idx == RGE_RX_LIST_CNT - 1)
11682ef55e5bSdlg 		cmdsts |= RGE_RDCMDSTS_EOR;
11692ef55e5bSdlg 
11702ef55e5bSdlg 	r->hi_qword1.rx_qword4.rge_cmdsts = htole32(cmdsts);
11712ef55e5bSdlg 	r->hi_qword1.rx_qword4.rge_extsts = htole32(0);
117293baed4dSkevlo 	r->hi_qword0.rge_addr = htole64(rxmap->dm_segs[0].ds_addr);
1173be4e4fa3Skevlo 
11742ef55e5bSdlg 	bus_dmamap_sync(sc->sc_dmat, q->q_rx.rge_rx_list_map,
11752ef55e5bSdlg 	    idx * sizeof(struct rge_rx_desc), sizeof(struct rge_rx_desc),
11762ef55e5bSdlg 	    BUS_DMASYNC_PREWRITE);
1177be4e4fa3Skevlo 
11782ef55e5bSdlg 	bus_dmamap_sync(sc->sc_dmat, q->q_rx.rge_rx_list_map,
11792ef55e5bSdlg 	    idx * sizeof(struct rge_rx_desc), sizeof(struct rge_rx_desc),
11802ef55e5bSdlg 	    BUS_DMASYNC_POSTWRITE);
11812ef55e5bSdlg 	cmdsts |= RGE_RDCMDSTS_OWN;
11822ef55e5bSdlg 	r->hi_qword1.rx_qword4.rge_cmdsts = htole32(cmdsts);
1183804ff404Skevlo 	bus_dmamap_sync(sc->sc_dmat, q->q_rx.rge_rx_list_map,
1184be4e4fa3Skevlo 	    idx * sizeof(struct rge_rx_desc), sizeof(struct rge_rx_desc),
1185be4e4fa3Skevlo 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1186be4e4fa3Skevlo 
1187804ff404Skevlo 	q->q_rx.rge_rxq_prodidx = RGE_NEXT_RX_DESC(idx);
118881a23f33Skevlo 
1189be4e4fa3Skevlo 	return (0);
1190be4e4fa3Skevlo }
1191be4e4fa3Skevlo 
1192be4e4fa3Skevlo void
1193804ff404Skevlo rge_rx_list_init(struct rge_queues *q)
1194be4e4fa3Skevlo {
1195804ff404Skevlo 	memset(q->q_rx.rge_rx_list, 0, RGE_RX_LIST_SZ);
1196be4e4fa3Skevlo 
1197804ff404Skevlo 	q->q_rx.rge_rxq_prodidx = q->q_rx.rge_rxq_considx = 0;
1198e21346faSdlg 	q->q_rx.rge_head = NULL;
1199e21346faSdlg 	q->q_rx.rge_tail = &q->q_rx.rge_head;
1200be4e4fa3Skevlo 
12012ef55e5bSdlg 	if_rxr_init(&q->q_rx.rge_rx_ring, 32, RGE_RX_LIST_CNT - 1);
1202804ff404Skevlo 	rge_fill_rx_ring(q);
120381a23f33Skevlo }
120481a23f33Skevlo 
120581a23f33Skevlo void
1206804ff404Skevlo rge_fill_rx_ring(struct rge_queues *q)
120781a23f33Skevlo {
1208804ff404Skevlo 	struct if_rxring *rxr = &q->q_rx.rge_rx_ring;
120981a23f33Skevlo 	int slots;
121081a23f33Skevlo 
121181a23f33Skevlo 	for (slots = if_rxr_get(rxr, RGE_RX_LIST_CNT); slots > 0; slots--) {
12128cbc3b0aSkevlo 		if (rge_newbuf(q))
121381a23f33Skevlo 			break;
121481a23f33Skevlo 	}
121581a23f33Skevlo 	if_rxr_put(rxr, slots);
1216be4e4fa3Skevlo }
1217be4e4fa3Skevlo 
1218be4e4fa3Skevlo void
1219804ff404Skevlo rge_tx_list_init(struct rge_queues *q)
1220be4e4fa3Skevlo {
1221804ff404Skevlo 	struct rge_softc *sc = q->q_sc;
12228cbc3b0aSkevlo 	struct rge_tx_desc *d;
1223be4e4fa3Skevlo 	int i;
1224be4e4fa3Skevlo 
1225804ff404Skevlo 	memset(q->q_tx.rge_tx_list, 0, RGE_TX_LIST_SZ);
1226be4e4fa3Skevlo 
1227be4e4fa3Skevlo 	for (i = 0; i < RGE_TX_LIST_CNT; i++)
1228804ff404Skevlo 		q->q_tx.rge_txq[i].txq_mbuf = NULL;
1229be4e4fa3Skevlo 
12308cbc3b0aSkevlo 	d = &q->q_tx.rge_tx_list[RGE_TX_LIST_CNT - 1];
12318cbc3b0aSkevlo 	d->rge_cmdsts = htole32(RGE_TDCMDSTS_EOR);
12328cbc3b0aSkevlo 
1233804ff404Skevlo 	bus_dmamap_sync(sc->sc_dmat, q->q_tx.rge_tx_list_map, 0,
1234804ff404Skevlo 	    q->q_tx.rge_tx_list_map->dm_mapsize,
1235be4e4fa3Skevlo 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1236be4e4fa3Skevlo 
1237804ff404Skevlo 	q->q_tx.rge_txq_prodidx = q->q_tx.rge_txq_considx = 0;
1238be4e4fa3Skevlo }
1239be4e4fa3Skevlo 
1240be4e4fa3Skevlo int
1241804ff404Skevlo rge_rxeof(struct rge_queues *q)
1242be4e4fa3Skevlo {
1243804ff404Skevlo 	struct rge_softc *sc = q->q_sc;
1244be4e4fa3Skevlo 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
1245be4e4fa3Skevlo 	struct mbuf *m;
1246be4e4fa3Skevlo 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
1247804ff404Skevlo 	struct if_rxring *rxr = &q->q_rx.rge_rx_ring;
1248be4e4fa3Skevlo 	struct rge_rx_desc *cur_rx;
1249be4e4fa3Skevlo 	struct rge_rxq *rxq;
1250be4e4fa3Skevlo 	uint32_t rxstat, extsts;
1251e21346faSdlg 	int i, mlen, rx = 0;
12522ef55e5bSdlg 	int cons;
1253be4e4fa3Skevlo 
12542ef55e5bSdlg 	i = cons = q->q_rx.rge_rxq_considx;
1255be4e4fa3Skevlo 
12562ef55e5bSdlg 	while (if_rxr_inuse(rxr) > 0) {
1257804ff404Skevlo 		cur_rx = &q->q_rx.rge_rx_list[i];
12582ef55e5bSdlg 
12592ef55e5bSdlg 		bus_dmamap_sync(sc->sc_dmat, q->q_rx.rge_rx_list_map,
12602ef55e5bSdlg 		    i * sizeof(*cur_rx), sizeof(*cur_rx),
12612ef55e5bSdlg 		    BUS_DMASYNC_POSTREAD);
126293baed4dSkevlo 		rxstat = letoh32(cur_rx->hi_qword1.rx_qword4.rge_cmdsts);
12632ef55e5bSdlg 		if (rxstat & RGE_RDCMDSTS_OWN) {
12642ef55e5bSdlg 			bus_dmamap_sync(sc->sc_dmat, q->q_rx.rge_rx_list_map,
12652ef55e5bSdlg 			    i * sizeof(*cur_rx), sizeof(*cur_rx),
12662ef55e5bSdlg 			    BUS_DMASYNC_PREREAD);
12678cbc3b0aSkevlo 			break;
12682ef55e5bSdlg 		}
12698cbc3b0aSkevlo 
1270804ff404Skevlo 		rxq = &q->q_rx.rge_rxq[i];
1271be4e4fa3Skevlo 		bus_dmamap_sync(sc->sc_dmat, rxq->rxq_dmamap, 0,
1272be4e4fa3Skevlo 		    rxq->rxq_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD);
1273be4e4fa3Skevlo 		bus_dmamap_unload(sc->sc_dmat, rxq->rxq_dmamap);
12742ef55e5bSdlg 		m = rxq->rxq_mbuf;
12752ef55e5bSdlg 		rxq->rxq_mbuf = NULL;
1276be4e4fa3Skevlo 
12772ef55e5bSdlg 		i = RGE_NEXT_RX_DESC(i);
12782ef55e5bSdlg 		if_rxr_put(rxr, 1);
12792ef55e5bSdlg 		rx = 1;
12802ef55e5bSdlg 
1281e21346faSdlg 		if (ISSET(rxstat, RGE_RDCMDSTS_SOF)) {
1282e21346faSdlg 			if (q->q_rx.rge_head != NULL) {
128367d345deSkevlo 				ifp->if_ierrors++;
1284e21346faSdlg 				m_freem(q->q_rx.rge_head);
1285e21346faSdlg 				q->q_rx.rge_tail = &q->q_rx.rge_head;
1286e21346faSdlg 			}
1287e21346faSdlg 
1288e21346faSdlg 			m->m_pkthdr.len = 0;
1289e21346faSdlg 		} else if (q->q_rx.rge_head == NULL) {
129067d345deSkevlo 			m_freem(m);
1291be4e4fa3Skevlo 			continue;
1292e21346faSdlg 		} else
1293e21346faSdlg 			CLR(m->m_flags, M_PKTHDR);
1294e21346faSdlg 
1295e21346faSdlg 		*q->q_rx.rge_tail = m;
1296e21346faSdlg 		q->q_rx.rge_tail = &m->m_next;
1297e21346faSdlg 
1298e21346faSdlg 		mlen = rxstat & RGE_RDCMDSTS_FRAGLEN;
1299e21346faSdlg 		m->m_len = mlen;
1300e21346faSdlg 
1301e21346faSdlg 		m = q->q_rx.rge_head;
1302e21346faSdlg 		m->m_pkthdr.len += mlen;
1303be4e4fa3Skevlo 
1304be4e4fa3Skevlo 		if (rxstat & RGE_RDCMDSTS_RXERRSUM) {
1305be4e4fa3Skevlo 			ifp->if_ierrors++;
130667d345deSkevlo 			m_freem(m);
1307e21346faSdlg 			q->q_rx.rge_head = NULL;
1308e21346faSdlg 			q->q_rx.rge_tail = &q->q_rx.rge_head;
1309be4e4fa3Skevlo 			continue;
1310be4e4fa3Skevlo 		}
1311be4e4fa3Skevlo 
1312e21346faSdlg 		if (!ISSET(rxstat, RGE_RDCMDSTS_EOF))
1313e21346faSdlg 			continue;
1314e21346faSdlg 
1315e21346faSdlg 		q->q_rx.rge_head = NULL;
1316e21346faSdlg 		q->q_rx.rge_tail = &q->q_rx.rge_head;
1317e21346faSdlg 
1318e21346faSdlg 		m_adj(m, -ETHER_CRC_LEN);
13192ef55e5bSdlg 
13202ef55e5bSdlg 		extsts = letoh32(cur_rx->hi_qword1.rx_qword4.rge_extsts);
1321be4e4fa3Skevlo 
1322be4e4fa3Skevlo 		/* Check IP header checksum. */
132393baed4dSkevlo 		if (!(extsts & RGE_RDEXTSTS_IPCSUMERR) &&
1324be4e4fa3Skevlo 		    (extsts & RGE_RDEXTSTS_IPV4))
1325be4e4fa3Skevlo 			m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
1326be4e4fa3Skevlo 
1327be4e4fa3Skevlo 		/* Check TCP/UDP checksum. */
1328be4e4fa3Skevlo 		if ((extsts & (RGE_RDEXTSTS_IPV4 | RGE_RDEXTSTS_IPV6)) &&
132993baed4dSkevlo 		    (((extsts & RGE_RDEXTSTS_TCPPKT) &&
133093baed4dSkevlo 		    !(extsts & RGE_RDEXTSTS_TCPCSUMERR)) ||
133193baed4dSkevlo 		    ((extsts & RGE_RDEXTSTS_UDPPKT) &&
133293baed4dSkevlo 		    !(extsts & RGE_RDEXTSTS_UDPCSUMERR))))
1333be4e4fa3Skevlo 			m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK |
1334be4e4fa3Skevlo 			    M_UDP_CSUM_IN_OK;
1335be4e4fa3Skevlo 
1336be4e4fa3Skevlo #if NVLAN > 0
1337be4e4fa3Skevlo 		if (extsts & RGE_RDEXTSTS_VTAG) {
1338be4e4fa3Skevlo 			m->m_pkthdr.ether_vtag =
1339be4e4fa3Skevlo 			    ntohs(extsts & RGE_RDEXTSTS_VLAN_MASK);
1340be4e4fa3Skevlo 			m->m_flags |= M_VLANTAG;
1341be4e4fa3Skevlo 		}
1342be4e4fa3Skevlo #endif
1343be4e4fa3Skevlo 
1344be4e4fa3Skevlo 		ml_enqueue(&ml, m);
1345be4e4fa3Skevlo 	}
1346be4e4fa3Skevlo 
13472ef55e5bSdlg 	if (!rx)
13482ef55e5bSdlg 		return (0);
13492ef55e5bSdlg 
13502ef55e5bSdlg 	if (i >= cons) {
13512ef55e5bSdlg 		bus_dmamap_sync(sc->sc_dmat, q->q_rx.rge_rx_list_map,
13522ef55e5bSdlg 		    cons * sizeof(*cur_rx), (i - cons) * sizeof(*cur_rx),
13532ef55e5bSdlg 		    BUS_DMASYNC_POSTWRITE);
13542ef55e5bSdlg 	} else {
13552ef55e5bSdlg 		bus_dmamap_sync(sc->sc_dmat, q->q_rx.rge_rx_list_map,
13562ef55e5bSdlg 		    cons * sizeof(*cur_rx),
13572ef55e5bSdlg 		    (RGE_RX_LIST_CNT - cons) * sizeof(*cur_rx),
13582ef55e5bSdlg 		    BUS_DMASYNC_POSTWRITE);
13592ef55e5bSdlg 		if (i > 0) {
13602ef55e5bSdlg 			bus_dmamap_sync(sc->sc_dmat, q->q_rx.rge_rx_list_map,
13612ef55e5bSdlg 			    0, i * sizeof(*cur_rx),
13622ef55e5bSdlg 			    BUS_DMASYNC_POSTWRITE);
13632ef55e5bSdlg 		}
13642ef55e5bSdlg 	}
13652ef55e5bSdlg 
136661f1e1c4Skevlo 	if (ifiq_input(&ifp->if_rcv, &ml))
136761f1e1c4Skevlo 		if_rxr_livelocked(rxr);
136861f1e1c4Skevlo 
1369804ff404Skevlo 	q->q_rx.rge_rxq_considx = i;
1370804ff404Skevlo 	rge_fill_rx_ring(q);
1371be4e4fa3Skevlo 
13722ef55e5bSdlg 	return (1);
1373be4e4fa3Skevlo }
1374be4e4fa3Skevlo 
1375be4e4fa3Skevlo int
1376804ff404Skevlo rge_txeof(struct rge_queues *q)
1377be4e4fa3Skevlo {
1378804ff404Skevlo 	struct rge_softc *sc = q->q_sc;
1379be4e4fa3Skevlo 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
1380be4e4fa3Skevlo 	struct rge_txq *txq;
1381be4e4fa3Skevlo 	uint32_t txstat;
1382c6c75c18Sdlg 	int cons, prod, cur, idx;
1383be4e4fa3Skevlo 	int free = 0;
1384be4e4fa3Skevlo 
1385804ff404Skevlo 	prod = q->q_tx.rge_txq_prodidx;
1386804ff404Skevlo 	cons = q->q_tx.rge_txq_considx;
1387be4e4fa3Skevlo 
1388c6c75c18Sdlg 	idx = cons;
1389c6c75c18Sdlg 	while (idx != prod) {
1390c6c75c18Sdlg 		txq = &q->q_tx.rge_txq[idx];
1391c6c75c18Sdlg 		cur = txq->txq_descidx;
1392be4e4fa3Skevlo 
1393c6c75c18Sdlg 		rge_tx_list_sync(sc, q, cur, 1, BUS_DMASYNC_POSTREAD);
1394c6c75c18Sdlg 		txstat = q->q_tx.rge_tx_list[cur].rge_cmdsts;
1395c6c75c18Sdlg 		rge_tx_list_sync(sc, q, cur, 1, BUS_DMASYNC_PREREAD);
1396c6c75c18Sdlg 		if (ISSET(txstat, htole32(RGE_TDCMDSTS_OWN))) {
1397be4e4fa3Skevlo 			free = 2;
1398be4e4fa3Skevlo 			break;
1399be4e4fa3Skevlo 		}
1400be4e4fa3Skevlo 
1401be4e4fa3Skevlo 		bus_dmamap_sync(sc->sc_dmat, txq->txq_dmamap, 0,
1402be4e4fa3Skevlo 		    txq->txq_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE);
1403be4e4fa3Skevlo 		bus_dmamap_unload(sc->sc_dmat, txq->txq_dmamap);
1404be4e4fa3Skevlo 		m_freem(txq->txq_mbuf);
1405be4e4fa3Skevlo 		txq->txq_mbuf = NULL;
1406be4e4fa3Skevlo 
1407c6c75c18Sdlg 		if (ISSET(txstat,
1408c6c75c18Sdlg 		    htole32(RGE_TDCMDSTS_EXCESSCOLL | RGE_TDCMDSTS_COLL)))
1409be4e4fa3Skevlo 			ifp->if_collisions++;
1410c6c75c18Sdlg 		if (ISSET(txstat, htole32(RGE_TDCMDSTS_TXERR)))
1411be4e4fa3Skevlo 			ifp->if_oerrors++;
1412be4e4fa3Skevlo 
1413c6c75c18Sdlg 		idx = RGE_NEXT_TX_DESC(cur);
1414be4e4fa3Skevlo 		free = 1;
1415be4e4fa3Skevlo 	}
1416be4e4fa3Skevlo 
1417be4e4fa3Skevlo 	if (free == 0)
1418be4e4fa3Skevlo 		return (0);
1419be4e4fa3Skevlo 
1420c6c75c18Sdlg 	if (idx >= cons) {
1421c6c75c18Sdlg 		rge_tx_list_sync(sc, q, cons, idx - cons,
1422c6c75c18Sdlg 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1423c6c75c18Sdlg 	} else {
1424c6c75c18Sdlg 		rge_tx_list_sync(sc, q, cons, RGE_TX_LIST_CNT - cons,
1425c6c75c18Sdlg 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1426c6c75c18Sdlg 		rge_tx_list_sync(sc, q, 0, idx,
1427c6c75c18Sdlg 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1428c6c75c18Sdlg 	}
1429c6c75c18Sdlg 
1430c6c75c18Sdlg 	q->q_tx.rge_txq_considx = idx;
1431be4e4fa3Skevlo 
1432be4e4fa3Skevlo 	if (ifq_is_oactive(&ifp->if_snd))
1433be4e4fa3Skevlo 		ifq_restart(&ifp->if_snd);
1434be4e4fa3Skevlo 	else if (free == 2)
1435be4e4fa3Skevlo 		ifq_serialize(&ifp->if_snd, &sc->sc_task);
1436be4e4fa3Skevlo 	else
1437be4e4fa3Skevlo 		ifp->if_timer = 0;
1438be4e4fa3Skevlo 
1439be4e4fa3Skevlo 	return (1);
1440be4e4fa3Skevlo }
1441be4e4fa3Skevlo 
1442be4e4fa3Skevlo void
1443be4e4fa3Skevlo rge_reset(struct rge_softc *sc)
1444be4e4fa3Skevlo {
1445be4e4fa3Skevlo 	int i;
1446be4e4fa3Skevlo 
14478cbc3b0aSkevlo 	RGE_CLRBIT_4(sc, RGE_RXCFG, RGE_RXCFG_ALLPHYS | RGE_RXCFG_INDIV |
14488cbc3b0aSkevlo 	    RGE_RXCFG_MULTI | RGE_RXCFG_BROAD | RGE_RXCFG_RUNT |
14498cbc3b0aSkevlo 	    RGE_RXCFG_ERRPKT);
14508cbc3b0aSkevlo 
1451be4e4fa3Skevlo 	/* Enable RXDV gate. */
1452be4e4fa3Skevlo 	RGE_SETBIT_1(sc, RGE_PPSW, 0x08);
1453be4e4fa3Skevlo 	DELAY(2000);
1454be4e4fa3Skevlo 
14558cbc3b0aSkevlo 	RGE_SETBIT_1(sc, RGE_CMD, RGE_CMD_STOPREQ);
145674263a62Skevlo 	if (sc->rge_type != MAC_CFG2_8126) {
14578cbc3b0aSkevlo 		for (i = 0; i < 20; i++) {
14588cbc3b0aSkevlo 			DELAY(10);
14598cbc3b0aSkevlo 			if (!(RGE_READ_1(sc, RGE_CMD) & RGE_CMD_STOPREQ))
14608cbc3b0aSkevlo 				break;
14618cbc3b0aSkevlo 		}
146274263a62Skevlo 	}
14638cbc3b0aSkevlo 
146481a23f33Skevlo 	for (i = 0; i < 3000; i++) {
146581a23f33Skevlo 		DELAY(50);
1466be4e4fa3Skevlo 		if ((RGE_READ_1(sc, RGE_MCUCMD) & (RGE_MCUCMD_RXFIFO_EMPTY |
1467be4e4fa3Skevlo 		    RGE_MCUCMD_TXFIFO_EMPTY)) == (RGE_MCUCMD_RXFIFO_EMPTY |
1468be4e4fa3Skevlo 		    RGE_MCUCMD_TXFIFO_EMPTY))
1469be4e4fa3Skevlo 			break;
1470be4e4fa3Skevlo 	}
14718cbc3b0aSkevlo 	if (sc->rge_type != MAC_CFG3) {
147281a23f33Skevlo 		for (i = 0; i < 3000; i++) {
147381a23f33Skevlo 			DELAY(50);
147481a23f33Skevlo 			if ((RGE_READ_2(sc, RGE_IM) & 0x0103) == 0x0103)
147581a23f33Skevlo 				break;
147681a23f33Skevlo 		}
147781a23f33Skevlo 	}
147881a23f33Skevlo 
147981a23f33Skevlo 	DELAY(2000);
1480be4e4fa3Skevlo 
1481be4e4fa3Skevlo 	/* Soft reset. */
1482be4e4fa3Skevlo 	RGE_WRITE_1(sc, RGE_CMD, RGE_CMD_RESET);
1483be4e4fa3Skevlo 
1484be4e4fa3Skevlo 	for (i = 0; i < RGE_TIMEOUT; i++) {
1485be4e4fa3Skevlo 		DELAY(100);
1486be4e4fa3Skevlo 		if (!(RGE_READ_1(sc, RGE_CMD) & RGE_CMD_RESET))
1487be4e4fa3Skevlo 			break;
1488be4e4fa3Skevlo 	}
1489be4e4fa3Skevlo 	if (i == RGE_TIMEOUT)
1490be4e4fa3Skevlo 		printf("%s: reset never completed!\n", sc->sc_dev.dv_xname);
1491be4e4fa3Skevlo }
1492be4e4fa3Skevlo 
1493be4e4fa3Skevlo void
1494be4e4fa3Skevlo rge_iff(struct rge_softc *sc)
1495be4e4fa3Skevlo {
1496be4e4fa3Skevlo 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
1497be4e4fa3Skevlo 	struct arpcom *ac = &sc->sc_arpcom;
1498be4e4fa3Skevlo 	struct ether_multi *enm;
1499be4e4fa3Skevlo 	struct ether_multistep step;
1500be4e4fa3Skevlo 	uint32_t hashes[2];
1501be4e4fa3Skevlo 	uint32_t rxfilt;
1502be4e4fa3Skevlo 	int h = 0;
1503be4e4fa3Skevlo 
1504be4e4fa3Skevlo 	rxfilt = RGE_READ_4(sc, RGE_RXCFG);
1505be4e4fa3Skevlo 	rxfilt &= ~(RGE_RXCFG_ALLPHYS | RGE_RXCFG_MULTI);
1506be4e4fa3Skevlo 	ifp->if_flags &= ~IFF_ALLMULTI;
1507be4e4fa3Skevlo 
1508be4e4fa3Skevlo 	/*
1509be4e4fa3Skevlo 	 * Always accept frames destined to our station address.
1510be4e4fa3Skevlo 	 * Always accept broadcast frames.
1511be4e4fa3Skevlo 	 */
1512be4e4fa3Skevlo 	rxfilt |= RGE_RXCFG_INDIV | RGE_RXCFG_BROAD;
1513be4e4fa3Skevlo 
1514be4e4fa3Skevlo 	if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) {
1515be4e4fa3Skevlo 		ifp->if_flags |= IFF_ALLMULTI;
1516be4e4fa3Skevlo 		rxfilt |= RGE_RXCFG_MULTI;
1517be4e4fa3Skevlo 		if (ifp->if_flags & IFF_PROMISC)
1518be4e4fa3Skevlo 			rxfilt |= RGE_RXCFG_ALLPHYS;
1519be4e4fa3Skevlo 		hashes[0] = hashes[1] = 0xffffffff;
1520be4e4fa3Skevlo 	} else {
1521be4e4fa3Skevlo 		rxfilt |= RGE_RXCFG_MULTI;
1522be4e4fa3Skevlo 		/* Program new filter. */
1523be4e4fa3Skevlo 		memset(hashes, 0, sizeof(hashes));
1524be4e4fa3Skevlo 
1525be4e4fa3Skevlo 		ETHER_FIRST_MULTI(step, ac, enm);
1526be4e4fa3Skevlo 		while (enm != NULL) {
1527be4e4fa3Skevlo 			h = ether_crc32_be(enm->enm_addrlo,
1528be4e4fa3Skevlo 			    ETHER_ADDR_LEN) >> 26;
1529be4e4fa3Skevlo 
1530be4e4fa3Skevlo 			if (h < 32)
1531be4e4fa3Skevlo 				hashes[0] |= (1 << h);
1532be4e4fa3Skevlo 			else
1533be4e4fa3Skevlo 				hashes[1] |= (1 << (h - 32));
1534be4e4fa3Skevlo 
1535be4e4fa3Skevlo 			ETHER_NEXT_MULTI(step, enm);
1536be4e4fa3Skevlo 		}
1537be4e4fa3Skevlo 	}
1538be4e4fa3Skevlo 
1539be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_RXCFG, rxfilt);
1540be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_MAR0, swap32(hashes[1]));
1541be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_MAR4, swap32(hashes[0]));
1542be4e4fa3Skevlo }
1543be4e4fa3Skevlo 
1544be4e4fa3Skevlo void
15458cbc3b0aSkevlo rge_chipinit(struct rge_softc *sc)
15468cbc3b0aSkevlo {
15478cbc3b0aSkevlo 	rge_exit_oob(sc);
15488cbc3b0aSkevlo 	rge_set_phy_power(sc, 1);
15498cbc3b0aSkevlo 	rge_hw_init(sc);
15508cbc3b0aSkevlo 	rge_hw_reset(sc);
15518cbc3b0aSkevlo }
15528cbc3b0aSkevlo 
15538cbc3b0aSkevlo void
1554be4e4fa3Skevlo rge_set_phy_power(struct rge_softc *sc, int on)
1555be4e4fa3Skevlo {
1556be4e4fa3Skevlo 	int i;
1557be4e4fa3Skevlo 
1558be4e4fa3Skevlo 	if (on) {
1559be4e4fa3Skevlo 		RGE_SETBIT_1(sc, RGE_PMCH, 0xc0);
1560be4e4fa3Skevlo 
1561be4e4fa3Skevlo 		rge_write_phy(sc, 0, MII_BMCR, BMCR_AUTOEN);
1562be4e4fa3Skevlo 
1563be4e4fa3Skevlo 		for (i = 0; i < RGE_TIMEOUT; i++) {
15641943b9bcSkrw 			if ((rge_read_phy_ocp(sc, 0xa420) & 0x0007) == 3)
1565be4e4fa3Skevlo 				break;
1566be4e4fa3Skevlo 			DELAY(1000);
1567be4e4fa3Skevlo 		}
156881a23f33Skevlo 	} else {
1569be4e4fa3Skevlo 		rge_write_phy(sc, 0, MII_BMCR, BMCR_AUTOEN | BMCR_PDOWN);
157081a23f33Skevlo 		RGE_CLRBIT_1(sc, RGE_PMCH, 0x80);
157181a23f33Skevlo 		RGE_CLRBIT_1(sc, RGE_PPSW, 0x40);
157281a23f33Skevlo 	}
1573be4e4fa3Skevlo }
1574be4e4fa3Skevlo 
1575be4e4fa3Skevlo void
15768cbc3b0aSkevlo rge_ephy_config(struct rge_softc *sc)
1577be4e4fa3Skevlo {
157881a23f33Skevlo 	switch (sc->rge_type) {
157981a23f33Skevlo 	case MAC_CFG3:
15808cbc3b0aSkevlo 		rge_ephy_config_mac_cfg3(sc);
158181a23f33Skevlo 		break;
158281a23f33Skevlo 	case MAC_CFG5:
15838cbc3b0aSkevlo 		rge_ephy_config_mac_cfg5(sc);
158481a23f33Skevlo 		break;
158581a23f33Skevlo 	default:
158674263a62Skevlo 		break;	/* Nothing to do. */
1587be4e4fa3Skevlo 	}
1588be4e4fa3Skevlo }
1589be4e4fa3Skevlo 
159081a23f33Skevlo void
15918cbc3b0aSkevlo rge_ephy_config_mac_cfg3(struct rge_softc *sc)
159281a23f33Skevlo {
159381a23f33Skevlo 	uint16_t val;
159481a23f33Skevlo 	int i;
159581a23f33Skevlo 
1596be4e4fa3Skevlo 	for (i = 0; i < nitems(rtl8125_mac_cfg3_ephy); i++)
1597be4e4fa3Skevlo 		rge_write_ephy(sc, rtl8125_mac_cfg3_ephy[i].reg,
1598be4e4fa3Skevlo 		    rtl8125_mac_cfg3_ephy[i].val);
1599be4e4fa3Skevlo 
160081a23f33Skevlo 	val = rge_read_ephy(sc, 0x002a) & ~0x7000;
160181a23f33Skevlo 	rge_write_ephy(sc, 0x002a, val | 0x3000);
160281a23f33Skevlo 	RGE_EPHY_CLRBIT(sc, 0x0019, 0x0040);
160381a23f33Skevlo 	RGE_EPHY_SETBIT(sc, 0x001b, 0x0e00);
160481a23f33Skevlo 	RGE_EPHY_CLRBIT(sc, 0x001b, 0x7000);
160581a23f33Skevlo 	rge_write_ephy(sc, 0x0002, 0x6042);
160681a23f33Skevlo 	rge_write_ephy(sc, 0x0006, 0x0014);
160781a23f33Skevlo 	val = rge_read_ephy(sc, 0x006a) & ~0x7000;
160881a23f33Skevlo 	rge_write_ephy(sc, 0x006a, val | 0x3000);
160981a23f33Skevlo 	RGE_EPHY_CLRBIT(sc, 0x0059, 0x0040);
161081a23f33Skevlo 	RGE_EPHY_SETBIT(sc, 0x005b, 0x0e00);
161181a23f33Skevlo 	RGE_EPHY_CLRBIT(sc, 0x005b, 0x7000);
161281a23f33Skevlo 	rge_write_ephy(sc, 0x0042, 0x6042);
161381a23f33Skevlo 	rge_write_ephy(sc, 0x0046, 0x0014);
16148cbc3b0aSkevlo }
16158cbc3b0aSkevlo 
16168cbc3b0aSkevlo void
16178cbc3b0aSkevlo rge_ephy_config_mac_cfg5(struct rge_softc *sc)
16188cbc3b0aSkevlo {
16198cbc3b0aSkevlo 	int i;
16208cbc3b0aSkevlo 
16218cbc3b0aSkevlo 	for (i = 0; i < nitems(rtl8125_mac_cfg5_ephy); i++)
16228cbc3b0aSkevlo 		rge_write_ephy(sc, rtl8125_mac_cfg5_ephy[i].reg,
16238cbc3b0aSkevlo 		    rtl8125_mac_cfg5_ephy[i].val);
16248cbc3b0aSkevlo }
16258cbc3b0aSkevlo 
16268cbc3b0aSkevlo int
16278cbc3b0aSkevlo rge_phy_config(struct rge_softc *sc)
16288cbc3b0aSkevlo {
16298cbc3b0aSkevlo 	int i;
16308cbc3b0aSkevlo 
16318cbc3b0aSkevlo 	rge_ephy_config(sc);
16328cbc3b0aSkevlo 
16338cbc3b0aSkevlo 	/* PHY reset. */
16348cbc3b0aSkevlo 	rge_write_phy(sc, 0, MII_ANAR,
16358cbc3b0aSkevlo 	    rge_read_phy(sc, 0, MII_ANAR) &
16368cbc3b0aSkevlo 	    ~(ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10));
16378cbc3b0aSkevlo 	rge_write_phy(sc, 0, MII_100T2CR,
16388cbc3b0aSkevlo 	    rge_read_phy(sc, 0, MII_100T2CR) &
16398cbc3b0aSkevlo 	    ~(GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX));
164074263a62Skevlo 	if (sc->rge_type == MAC_CFG2_8126)
164174263a62Skevlo 		RGE_PHY_CLRBIT(sc, 0xa5d4, RGE_ADV_2500TFDX | RGE_ADV_5000TFDX);
164274263a62Skevlo 	else
16438cbc3b0aSkevlo 		RGE_PHY_CLRBIT(sc, 0xa5d4, RGE_ADV_2500TFDX);
16448cbc3b0aSkevlo 	rge_write_phy(sc, 0, MII_BMCR, BMCR_RESET | BMCR_AUTOEN |
16458cbc3b0aSkevlo 	    BMCR_STARTNEG);
16468cbc3b0aSkevlo 	for (i = 0; i < 2500; i++) {
16478cbc3b0aSkevlo 		if (!(rge_read_phy(sc, 0, MII_BMCR) & BMCR_RESET))
16488cbc3b0aSkevlo 			break;
16498cbc3b0aSkevlo 		DELAY(1000);
16508cbc3b0aSkevlo 	}
16518cbc3b0aSkevlo 	if (i == 2500) {
16528cbc3b0aSkevlo 		printf("%s: PHY reset failed\n", sc->sc_dev.dv_xname);
16538cbc3b0aSkevlo 		return (ETIMEDOUT);
16548cbc3b0aSkevlo 	}
16558cbc3b0aSkevlo 
16568cbc3b0aSkevlo 	/* Read microcode version. */
16578cbc3b0aSkevlo 	rge_write_phy_ocp(sc, 0xa436, 0x801e);
16588cbc3b0aSkevlo 	sc->rge_mcodever = rge_read_phy_ocp(sc, 0xa438);
16598cbc3b0aSkevlo 
16608cbc3b0aSkevlo 	switch (sc->rge_type) {
166174263a62Skevlo 	case MAC_CFG2_8126:
166274263a62Skevlo 		rge_phy_config_mac_cfg2_8126(sc);
166374263a62Skevlo 		break;
16648cbc3b0aSkevlo 	case MAC_CFG3:
16658cbc3b0aSkevlo 		rge_phy_config_mac_cfg3(sc);
16668cbc3b0aSkevlo 		break;
16678cbc3b0aSkevlo 	case MAC_CFG5:
16688cbc3b0aSkevlo 		rge_phy_config_mac_cfg5(sc);
16698cbc3b0aSkevlo 		break;
16708cbc3b0aSkevlo 	default:
16718cbc3b0aSkevlo 		break;	/* Can't happen. */
16728cbc3b0aSkevlo 	}
16738cbc3b0aSkevlo 
16748cbc3b0aSkevlo 	RGE_PHY_CLRBIT(sc, 0xa5b4, 0x8000);
16758cbc3b0aSkevlo 
16768cbc3b0aSkevlo 	/* Disable EEE. */
16778cbc3b0aSkevlo 	RGE_MAC_CLRBIT(sc, 0xe040, 0x0003);
16788cbc3b0aSkevlo 	if (sc->rge_type == MAC_CFG3) {
16798cbc3b0aSkevlo 		RGE_MAC_CLRBIT(sc, 0xeb62, 0x0006);
16808cbc3b0aSkevlo 		RGE_PHY_CLRBIT(sc, 0xa432, 0x0010);
168174263a62Skevlo 	} else if (sc->rge_type == MAC_CFG5)
168274263a62Skevlo 		RGE_PHY_SETBIT(sc, 0xa432, 0x0010);
168374263a62Skevlo 
16848cbc3b0aSkevlo 	RGE_PHY_CLRBIT(sc, 0xa5d0, 0x0006);
16858cbc3b0aSkevlo 	RGE_PHY_CLRBIT(sc, 0xa6d4, 0x0001);
168674263a62Skevlo 	if (sc->rge_type == MAC_CFG2_8126)
168774263a62Skevlo 		RGE_PHY_CLRBIT(sc, 0xa6d4, 0x0002);
16888cbc3b0aSkevlo 	RGE_PHY_CLRBIT(sc, 0xa6d8, 0x0010);
16898cbc3b0aSkevlo 	RGE_PHY_CLRBIT(sc, 0xa428, 0x0080);
16908cbc3b0aSkevlo 	RGE_PHY_CLRBIT(sc, 0xa4a2, 0x0200);
16918cbc3b0aSkevlo 
169274263a62Skevlo 	/* Disable advanced EEE. */
16938cbc3b0aSkevlo 	RGE_MAC_CLRBIT(sc, 0xe052, 0x0001);
16948cbc3b0aSkevlo 	RGE_PHY_CLRBIT(sc, 0xa442, 0x3000);
16958cbc3b0aSkevlo 	RGE_PHY_CLRBIT(sc, 0xa430, 0x8000);
16968cbc3b0aSkevlo 
16978cbc3b0aSkevlo 	return (0);
16988cbc3b0aSkevlo }
16998cbc3b0aSkevlo 
17008cbc3b0aSkevlo void
170174263a62Skevlo rge_phy_config_mac_cfg2_8126(struct rge_softc *sc)
170274263a62Skevlo {
170374263a62Skevlo 	uint16_t val;
170474263a62Skevlo 	int i;
170574263a62Skevlo 	static const uint16_t mac_cfg2_a438_value[] =
170674263a62Skevlo 	    { 0x0044, 0x00a8, 0x00d6, 0x00ec, 0x00f6, 0x00fc, 0x00fe,
170774263a62Skevlo 	      0x00fe, 0x00bc, 0x0058, 0x002a, 0x003f, 0x3f02, 0x023c,
170874263a62Skevlo 	      0x3b0a, 0x1c00, 0x0000, 0x0000, 0x0000, 0x0000 };
170974263a62Skevlo 
171074263a62Skevlo 	static const uint16_t mac_cfg2_b87e_value[] =
171174263a62Skevlo 	    { 0x03ed, 0x03ff, 0x0009, 0x03fe, 0x000b, 0x0021, 0x03f7,
171274263a62Skevlo 	      0x03b8, 0x03e0, 0x0049, 0x0049, 0x03e0, 0x03b8, 0x03f7,
171374263a62Skevlo 	      0x0021, 0x000b, 0x03fe, 0x0009, 0x03ff, 0x03ed, 0x000e,
171474263a62Skevlo 	      0x03fe, 0x03ed, 0x0006, 0x001a, 0x03f1, 0x03d8, 0x0023,
171574263a62Skevlo 	      0x0054, 0x0322, 0x00dd, 0x03ab, 0x03dc, 0x0027, 0x000e,
171674263a62Skevlo 	      0x03e5, 0x03f9, 0x0012, 0x0001, 0x03f1 };
171774263a62Skevlo 
171874263a62Skevlo 	rge_phy_config_mcu(sc, RGE_MAC_CFG2_8126_MCODE_VER);
171974263a62Skevlo 
172074263a62Skevlo 	RGE_PHY_SETBIT(sc, 0xa442, 0x0800);
172174263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x80bf);
172274263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0xff00;
172374263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0xed00);
172474263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x80cd);
172574263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0xff00;
172674263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0x1000);
172774263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x80d1);
172874263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0xff00;
172974263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0xc800);
173074263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x80d4);
173174263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0xff00;
173274263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0xc800);
173374263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x80e1);
173474263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, 0x10cc);
173574263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x80e5);
173674263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, 0x4f0c);
173774263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x8387);
173874263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0xff00;
173974263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0x4700);
174074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa80c) & ~0x00c0;
174174263a62Skevlo 	rge_write_phy_ocp(sc, 0xa80c, val | 0x0080);
174274263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xac90, 0x0010);
174374263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xad2c, 0x8000);
174474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8321);
174574263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
174674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x1100);
174774263a62Skevlo 	RGE_PHY_SETBIT(sc, 0xacf8, 0x000c);
174874263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x8183);
174974263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0xff00;
175074263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0x5900);
175174263a62Skevlo 	RGE_PHY_SETBIT(sc, 0xad94, 0x0020);
175274263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xa654, 0x0800);
175374263a62Skevlo 	RGE_PHY_SETBIT(sc, 0xb648, 0x4000);
175474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x839e);
175574263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
175674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x2f00);
175774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x83f2);
175874263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
175974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0800);
176074263a62Skevlo 	RGE_PHY_SETBIT(sc, 0xada0, 0x0002);
176174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x80f3);
176274263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
176374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x9900);
176474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8126);
176574263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
176674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0xc100);
176774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x893a);
176874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x8080);
176974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8647);
177074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
177174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0xe600);
177274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x862c);
177374263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
177474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x1200);
177574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x864a);
177674263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
177774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0xe600);
177874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x80a0);
177974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0xbcbc);
178074263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x805e);
178174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0xbcbc);
178274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8056);
178374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x3077);
178474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8058);
178574263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
178674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x5a00);
178774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8098);
178874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x3077);
178974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x809a);
179074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
179174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x5a00);
179274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8052);
179374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x3733);
179474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8094);
179574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x3733);
179674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x807f);
179774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x7c75);
179874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x803d);
179974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x7c75);
180074263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8036);
180174263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
180274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x3000);
180374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8078);
180474263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
180574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x3000);
180674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8031);
180774263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
180874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x3300);
180974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8073);
181074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
181174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x3300);
181274263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xae06) & ~0xfc00;
181374263a62Skevlo 	rge_write_phy_ocp(sc, 0xae06, val | 0x7c00);
181474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x89D1);
181574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x0004);
181674263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x8fbd);
181774263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0xff00;
181874263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0x0a00);
181974263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x8fbe);
182074263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, 0x0d09);
182174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x89cd);
182274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x0f0f);
182374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x89cf);
182474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x0f0f);
182574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x83a4);
182674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x6600);
182774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x83a6);
182874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x6601);
182974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x83c0);
183074263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x6600);
183174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x83c2);
183274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x6601);
183374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8414);
183474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x6600);
183574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8416);
183674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x6601);
183774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x83f8);
183874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x6600);
183974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x83fa);
184074263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x6601);
184174263a62Skevlo 
184274263a62Skevlo 	rge_patch_phy_mcu(sc, 1);
184374263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xbd96) & ~0x1f00;
184474263a62Skevlo 	rge_write_phy_ocp(sc, 0xbd96, val | 0x1000);
184574263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xbf1c) & ~0x0007;
184674263a62Skevlo 	rge_write_phy_ocp(sc, 0xbf1c, val | 0x0007);
184774263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xbfbe, 0x8000);
184874263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xbf40) & ~0x0380;
184974263a62Skevlo 	rge_write_phy_ocp(sc, 0xbf40, val | 0x0280);
185074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xbf90) & ~0x0080;
185174263a62Skevlo 	rge_write_phy_ocp(sc, 0xbf90, val | 0x0060);
185274263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xbf90) & ~0x0010;
185374263a62Skevlo 	rge_write_phy_ocp(sc, 0xbf90, val | 0x000c);
185474263a62Skevlo 	rge_patch_phy_mcu(sc, 0);
185574263a62Skevlo 
185674263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x843b);
185774263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0xff00;
185874263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0x2000);
185974263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x843d);
186074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0xff00;
186174263a62Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0x2000);
186274263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xb516, 0x007f);
186374263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xbf80, 0x0030);
186474263a62Skevlo 
186574263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x8188);
186674263a62Skevlo 	for (i = 0; i < 11; i++)
186774263a62Skevlo 		rge_write_phy_ocp(sc, 0xa438, mac_cfg2_a438_value[i]);
186874263a62Skevlo 
186974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8015);
187074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
187174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0800);
187274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8ffd);
187374263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
187474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0);
187574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8fff);
187674263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
187774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x7f00);
187874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8ffb);
187974263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
188074263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0100);
188174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8fe9);
188274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x0002);
188374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8fef);
188474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x00a5);
188574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8ff1);
188674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x0106);
188774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8fe1);
188874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x0102);
188974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8fe3);
189074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
189174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0400);
189274263a62Skevlo 	RGE_PHY_SETBIT(sc, 0xa654, 0x0800);
189374263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xa654, 0x0003);
189474263a62Skevlo 	rge_write_phy_ocp(sc, 0xac3a, 0x5851);
189574263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xac3c) & ~0xd000;
189674263a62Skevlo 	rge_write_phy_ocp(sc, 0xac3c, val | 0x2000);
189774263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xac42) & ~0x0200;
189874263a62Skevlo 	rge_write_phy_ocp(sc, 0xac42, val | 0x01c0);
189974263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xac3e, 0xe000);
190074263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xac42, 0x0038);
190174263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xac42) & ~0x0002;
190274263a62Skevlo 	rge_write_phy_ocp(sc, 0xac42, val | 0x0005);
190374263a62Skevlo 	rge_write_phy_ocp(sc, 0xac1a, 0x00db);
190474263a62Skevlo 	rge_write_phy_ocp(sc, 0xade4, 0x01b5);
190574263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xad9c, 0x0c00);
190674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x814b);
190774263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
190874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x1100);
190974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x814d);
191074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
191174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x1100);
191274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x814f);
191374263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
191474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0b00);
191574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8142);
191674263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
191774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0100);
191874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8144);
191974263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
192074263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0100);
192174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8150);
192274263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
192374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0100);
192474263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8118);
192574263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
192674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0700);
192774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x811a);
192874263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
192974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0700);
193074263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x811c);
193174263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
193274263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0500);
193374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x810f);
193474263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
193574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0100);
193674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8111);
193774263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
193874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0100);
193974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x811d);
194074263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
194174263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0100);
194274263a62Skevlo 	RGE_PHY_SETBIT(sc, 0xac36, 0x1000);
194374263a62Skevlo 	RGE_PHY_CLRBIT(sc, 0xad1c, 0x0100);
194474263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xade8) & ~0xffc0;
194574263a62Skevlo 	rge_write_phy_ocp(sc, 0xade8, val | 0x1400);
194674263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x864b);
194774263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
194874263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x9d00);
194974263a62Skevlo 
195074263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x8f97);
195174263a62Skevlo 	for (; i < nitems(mac_cfg2_a438_value); i++)
195274263a62Skevlo 		rge_write_phy_ocp(sc, 0xa438, mac_cfg2_a438_value[i]);
195374263a62Skevlo 
195474263a62Skevlo 	RGE_PHY_SETBIT(sc, 0xad9c, 0x0020);
195574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8122);
195674263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
195774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0c00);
195874263a62Skevlo 
195974263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x82c8);
196074263a62Skevlo 	for (i = 0; i < 20; i++)
196174263a62Skevlo 		rge_write_phy_ocp(sc, 0xb87e, mac_cfg2_b87e_value[i]);
196274263a62Skevlo 
196374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x80ef);
196474263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
196574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0c00);
196674263a62Skevlo 
196774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x82a0);
196874263a62Skevlo 	for (; i < nitems(mac_cfg2_b87e_value); i++)
196974263a62Skevlo 		rge_write_phy_ocp(sc, 0xb87e, mac_cfg2_b87e_value[i]);
197074263a62Skevlo 
197174263a62Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x8018);
197274263a62Skevlo 	RGE_PHY_SETBIT(sc, 0xa438, 0x2000);
197374263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8fe4);
197474263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
197574263a62Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0);
197674263a62Skevlo 	val = rge_read_phy_ocp(sc, 0xb54c) & ~0xffc0;
197774263a62Skevlo 	rge_write_phy_ocp(sc, 0xb54c, val | 0x3700);
197874263a62Skevlo }
197974263a62Skevlo 
198074263a62Skevlo void
19818cbc3b0aSkevlo rge_phy_config_mac_cfg3(struct rge_softc *sc)
19828cbc3b0aSkevlo {
19838cbc3b0aSkevlo 	uint16_t val;
19848cbc3b0aSkevlo 	int i;
19858cbc3b0aSkevlo 	static const uint16_t mac_cfg3_a438_value[] =
19868cbc3b0aSkevlo 	    { 0x0043, 0x00a7, 0x00d6, 0x00ec, 0x00f6, 0x00fb, 0x00fd, 0x00ff,
19878cbc3b0aSkevlo 	      0x00bb, 0x0058, 0x0029, 0x0013, 0x0009, 0x0004, 0x0002 };
19888cbc3b0aSkevlo 
19898cbc3b0aSkevlo 	static const uint16_t mac_cfg3_b88e_value[] =
19908cbc3b0aSkevlo 	    { 0xc091, 0x6e12, 0xc092, 0x1214, 0xc094, 0x1516, 0xc096, 0x171b,
19918cbc3b0aSkevlo 	      0xc098, 0x1b1c, 0xc09a, 0x1f1f, 0xc09c, 0x2021, 0xc09e, 0x2224,
19928cbc3b0aSkevlo 	      0xc0a0, 0x2424, 0xc0a2, 0x2424, 0xc0a4, 0x2424, 0xc018, 0x0af2,
19938cbc3b0aSkevlo 	      0xc01a, 0x0d4a, 0xc01c, 0x0f26, 0xc01e, 0x118d, 0xc020, 0x14f3,
19948cbc3b0aSkevlo 	      0xc022, 0x175a, 0xc024, 0x19c0, 0xc026, 0x1c26, 0xc089, 0x6050,
19958cbc3b0aSkevlo 	      0xc08a, 0x5f6e, 0xc08c, 0x6e6e, 0xc08e, 0x6e6e, 0xc090, 0x6e12 };
1996be4e4fa3Skevlo 
199781a23f33Skevlo 	rge_phy_config_mcu(sc, RGE_MAC_CFG3_MCODE_VER);
1998be4e4fa3Skevlo 
1999be4e4fa3Skevlo 	RGE_PHY_SETBIT(sc, 0xad4e, 0x0010);
2000be4e4fa3Skevlo 	val = rge_read_phy_ocp(sc, 0xad16) & ~0x03ff;
2001be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xad16, val | 0x03ff);
2002be4e4fa3Skevlo 	val = rge_read_phy_ocp(sc, 0xad32) & ~0x003f;
2003be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xad32, val | 0x0006);
2004be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xac08, 0x1000);
2005be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xac08, 0x0100);
2006be4e4fa3Skevlo 	val = rge_read_phy_ocp(sc, 0xacc0) & ~0x0003;
2007be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xacc0, val | 0x0002);
2008be4e4fa3Skevlo 	val = rge_read_phy_ocp(sc, 0xad40) & ~0x00e0;
2009be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xad40, val | 0x0040);
2010be4e4fa3Skevlo 	val = rge_read_phy_ocp(sc, 0xad40) & ~0x0007;
2011be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xad40, val | 0x0004);
2012be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xac14, 0x0080);
2013be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xac80, 0x0300);
2014be4e4fa3Skevlo 	val = rge_read_phy_ocp(sc, 0xac5e) & ~0x0007;
2015be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xac5e, val | 0x0002);
2016be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xad4c, 0x00a8);
2017be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xac5c, 0x01ff);
2018be4e4fa3Skevlo 	val = rge_read_phy_ocp(sc, 0xac8a) & ~0x00f0;
2019be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xac8a, val | 0x0030);
202081a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8157);
202181a23f33Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
202281a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0500);
202381a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8159);
202481a23f33Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
202581a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0700);
2026be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x80a2);
2027be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x0153);
2028be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x809c);
2029be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x0153);
2030be4e4fa3Skevlo 
2031be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x81b3);
2032be4e4fa3Skevlo 	for (i = 0; i < nitems(mac_cfg3_a438_value); i++)
2033be4e4fa3Skevlo 		rge_write_phy_ocp(sc, 0xa438, mac_cfg3_a438_value[i]);
2034be4e4fa3Skevlo 	for (i = 0; i < 26; i++)
2035be4e4fa3Skevlo 		rge_write_phy_ocp(sc, 0xa438, 0);
2036be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x8257);
2037be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xa438, 0x020f);
2038be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x80ea);
2039be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xa438, 0x7843);
2040be4e4fa3Skevlo 
2041be4e4fa3Skevlo 	rge_patch_phy_mcu(sc, 1);
2042be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xb896, 0x0001);
2043be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xb892, 0xff00);
2044be4e4fa3Skevlo 	for (i = 0; i < nitems(mac_cfg3_b88e_value); i += 2) {
2045be4e4fa3Skevlo 		rge_write_phy_ocp(sc, 0xb88e, mac_cfg3_b88e_value[i]);
204681a23f33Skevlo 		rge_write_phy_ocp(sc, 0xb890, mac_cfg3_b88e_value[i + 1]);
2047be4e4fa3Skevlo 	}
2048be4e4fa3Skevlo 	RGE_PHY_SETBIT(sc, 0xb896, 0x0001);
2049be4e4fa3Skevlo 	rge_patch_phy_mcu(sc, 0);
2050be4e4fa3Skevlo 
2051be4e4fa3Skevlo 	RGE_PHY_SETBIT(sc, 0xd068, 0x2000);
2052be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x81a2);
2053be4e4fa3Skevlo 	RGE_PHY_SETBIT(sc, 0xa438, 0x0100);
2054be4e4fa3Skevlo 	val = rge_read_phy_ocp(sc, 0xb54c) & ~0xff00;
2055be4e4fa3Skevlo 	rge_write_phy_ocp(sc, 0xb54c, val | 0xdb00);
2056be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xa454, 0x0001);
2057be4e4fa3Skevlo 	RGE_PHY_SETBIT(sc, 0xa5d4, 0x0020);
2058be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xad4e, 0x0010);
2059be4e4fa3Skevlo 	RGE_PHY_CLRBIT(sc, 0xa86a, 0x0001);
2060be4e4fa3Skevlo 	RGE_PHY_SETBIT(sc, 0xa442, 0x0800);
20618cbc3b0aSkevlo 	RGE_PHY_SETBIT(sc, 0xa424, 0x0008);
206281a23f33Skevlo }
206381a23f33Skevlo 
206481a23f33Skevlo void
206581a23f33Skevlo rge_phy_config_mac_cfg5(struct rge_softc *sc)
206681a23f33Skevlo {
206781a23f33Skevlo 	uint16_t val;
206881a23f33Skevlo 	int i;
206981a23f33Skevlo 
207081a23f33Skevlo 	rge_phy_config_mcu(sc, RGE_MAC_CFG5_MCODE_VER);
207181a23f33Skevlo 
207281a23f33Skevlo 	RGE_PHY_SETBIT(sc, 0xa442, 0x0800);
207381a23f33Skevlo 	val = rge_read_phy_ocp(sc, 0xac46) & ~0x00f0;
207481a23f33Skevlo 	rge_write_phy_ocp(sc, 0xac46, val | 0x0090);
207581a23f33Skevlo 	val = rge_read_phy_ocp(sc, 0xad30) & ~0x0003;
207681a23f33Skevlo 	rge_write_phy_ocp(sc, 0xad30, val | 0x0001);
207781a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x80f5);
207881a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x760e);
207981a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8107);
208081a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87e, 0x360e);
208181a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87c, 0x8551);
208281a23f33Skevlo 	val = rge_read_phy_ocp(sc, 0xb87e) & ~0xff00;
208381a23f33Skevlo 	rge_write_phy_ocp(sc, 0xb87e, val | 0x0800);
208481a23f33Skevlo 	val = rge_read_phy_ocp(sc, 0xbf00) & ~0xe000;
208581a23f33Skevlo 	rge_write_phy_ocp(sc, 0xbf00, val | 0xa000);
208681a23f33Skevlo 	val = rge_read_phy_ocp(sc, 0xbf46) & ~0x0f00;
208781a23f33Skevlo 	rge_write_phy_ocp(sc, 0xbf46, val | 0x0300);
208881a23f33Skevlo 	for (i = 0; i < 10; i++) {
208981a23f33Skevlo 		rge_write_phy_ocp(sc, 0xa436, 0x8044 + i * 6);
209081a23f33Skevlo 		rge_write_phy_ocp(sc, 0xa438, 0x2417);
209181a23f33Skevlo 	}
209281a23f33Skevlo 	RGE_PHY_SETBIT(sc, 0xa4ca, 0x0040);
209361f1e1c4Skevlo 	val = rge_read_phy_ocp(sc, 0xbf84) & ~0xe000;
209461f1e1c4Skevlo 	rge_write_phy_ocp(sc, 0xbf84, val | 0xa000);
2095804ff404Skevlo 	rge_write_phy_ocp(sc, 0xa436, 0x8170);
2096804ff404Skevlo 	val = rge_read_phy_ocp(sc, 0xa438) & ~0x2700;
2097804ff404Skevlo 	rge_write_phy_ocp(sc, 0xa438, val | 0xd800);
20988cbc3b0aSkevlo 	RGE_PHY_SETBIT(sc, 0xa424, 0x0008);
209981a23f33Skevlo }
210081a23f33Skevlo 
210181a23f33Skevlo void
210281a23f33Skevlo rge_phy_config_mcu(struct rge_softc *sc, uint16_t mcode_version)
210381a23f33Skevlo {
210481a23f33Skevlo 	if (sc->rge_mcodever != mcode_version) {
210581a23f33Skevlo 		int i;
2106be4e4fa3Skevlo 
2107be4e4fa3Skevlo 		rge_patch_phy_mcu(sc, 1);
210881a23f33Skevlo 
21098cbc3b0aSkevlo 		if (sc->rge_type == MAC_CFG3) {
211081a23f33Skevlo 			rge_write_phy_ocp(sc, 0xa436, 0x8024);
211181a23f33Skevlo 			rge_write_phy_ocp(sc, 0xa438, 0x8601);
211281a23f33Skevlo 			rge_write_phy_ocp(sc, 0xa436, 0xb82e);
211381a23f33Skevlo 			rge_write_phy_ocp(sc, 0xa438, 0x0001);
211481a23f33Skevlo 
211581a23f33Skevlo 			RGE_PHY_SETBIT(sc, 0xb820, 0x0080);
211681a23f33Skevlo 
211781a23f33Skevlo 			for (i = 0; i < nitems(rtl8125_mac_cfg3_mcu); i++) {
211881a23f33Skevlo 				rge_write_phy_ocp(sc,
211981a23f33Skevlo 				    rtl8125_mac_cfg3_mcu[i].reg,
212081a23f33Skevlo 				    rtl8125_mac_cfg3_mcu[i].val);
212181a23f33Skevlo 			}
212281a23f33Skevlo 
212381a23f33Skevlo 			RGE_PHY_CLRBIT(sc, 0xb820, 0x0080);
212481a23f33Skevlo 
212581a23f33Skevlo 			rge_write_phy_ocp(sc, 0xa436, 0);
212681a23f33Skevlo 			rge_write_phy_ocp(sc, 0xa438, 0);
212781a23f33Skevlo 			RGE_PHY_CLRBIT(sc, 0xb82e, 0x0001);
212881a23f33Skevlo 			rge_write_phy_ocp(sc, 0xa436, 0x8024);
212981a23f33Skevlo 			rge_write_phy_ocp(sc, 0xa438, 0);
21308cbc3b0aSkevlo 		} else if (sc->rge_type == MAC_CFG5) {
21318cbc3b0aSkevlo 			for (i = 0; i < nitems(rtl8125_mac_cfg5_mcu); i++) {
21328cbc3b0aSkevlo 				rge_write_phy_ocp(sc,
21338cbc3b0aSkevlo 				    rtl8125_mac_cfg5_mcu[i].reg,
21348cbc3b0aSkevlo 				    rtl8125_mac_cfg5_mcu[i].val);
21358cbc3b0aSkevlo 			}
213674263a62Skevlo 		} else if (sc->rge_type == MAC_CFG2_8126) {
213774263a62Skevlo 			for (i = 0; i < nitems(rtl8126_mac_cfg2_mcu); i++) {
213874263a62Skevlo 				rge_write_phy_ocp(sc,
213974263a62Skevlo 				    rtl8126_mac_cfg2_mcu[i].reg,
214074263a62Skevlo 				    rtl8126_mac_cfg2_mcu[i].val);
214174263a62Skevlo 			}
214281a23f33Skevlo 		}
214381a23f33Skevlo 
2144be4e4fa3Skevlo 		rge_patch_phy_mcu(sc, 0);
214581a23f33Skevlo 
214681a23f33Skevlo 		/* Write microcode version. */
214781a23f33Skevlo 		rge_write_phy_ocp(sc, 0xa436, 0x801e);
214881a23f33Skevlo 		rge_write_phy_ocp(sc, 0xa438, mcode_version);
214981a23f33Skevlo 	}
2150be4e4fa3Skevlo }
2151be4e4fa3Skevlo 
2152be4e4fa3Skevlo void
2153be4e4fa3Skevlo rge_set_macaddr(struct rge_softc *sc, const uint8_t *addr)
2154be4e4fa3Skevlo {
2155be4e4fa3Skevlo 	RGE_SETBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
2156be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_MAC0,
2157be4e4fa3Skevlo 	    addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]);
2158be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_MAC4,
2159be4e4fa3Skevlo 	    addr[5] <<  8 | addr[4]);
2160be4e4fa3Skevlo 	RGE_CLRBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
2161be4e4fa3Skevlo }
2162be4e4fa3Skevlo 
2163be4e4fa3Skevlo void
2164be4e4fa3Skevlo rge_get_macaddr(struct rge_softc *sc, uint8_t *addr)
2165be4e4fa3Skevlo {
21668cbc3b0aSkevlo 	int i;
21678cbc3b0aSkevlo 
21688cbc3b0aSkevlo 	for (i = 0; i < ETHER_ADDR_LEN; i++)
21698cbc3b0aSkevlo 		addr[i] = RGE_READ_1(sc, RGE_MAC0 + i);
21708cbc3b0aSkevlo 
2171be4e4fa3Skevlo 	*(uint32_t *)&addr[0] = RGE_READ_4(sc, RGE_ADDR0);
2172be4e4fa3Skevlo 	*(uint16_t *)&addr[4] = RGE_READ_2(sc, RGE_ADDR1);
21738cbc3b0aSkevlo 
21748cbc3b0aSkevlo 	rge_set_macaddr(sc, addr);
2175be4e4fa3Skevlo }
2176be4e4fa3Skevlo 
2177be4e4fa3Skevlo void
2178be4e4fa3Skevlo rge_hw_init(struct rge_softc *sc)
2179be4e4fa3Skevlo {
21808cbc3b0aSkevlo 	uint16_t reg;
21818cbc3b0aSkevlo 	int i, npages;
2182be4e4fa3Skevlo 
21838cbc3b0aSkevlo 	rge_disable_aspm_clkreq(sc);
2184be4e4fa3Skevlo 	RGE_CLRBIT_1(sc, 0xf1, 0x80);
2185be4e4fa3Skevlo 
2186be4e4fa3Skevlo 	/* Disable UPS. */
2187be4e4fa3Skevlo 	RGE_MAC_CLRBIT(sc, 0xd40a, 0x0010);
2188be4e4fa3Skevlo 
21898cbc3b0aSkevlo 	/* Disable MAC MCU. */
21908cbc3b0aSkevlo 	rge_disable_aspm_clkreq(sc);
21918cbc3b0aSkevlo 	rge_write_mac_ocp(sc, 0xfc48, 0);
21928cbc3b0aSkevlo 	for (reg = 0xfc28; reg < 0xfc48; reg += 2)
21938cbc3b0aSkevlo 		rge_write_mac_ocp(sc, reg, 0);
2194be4e4fa3Skevlo 	DELAY(3000);
2195be4e4fa3Skevlo 	rge_write_mac_ocp(sc, 0xfc26, 0);
2196be4e4fa3Skevlo 
2197be4e4fa3Skevlo 	if (sc->rge_type == MAC_CFG3) {
21988cbc3b0aSkevlo 		for (npages = 0; npages < 3; npages++) {
21998cbc3b0aSkevlo 			rge_switch_mcu_ram_page(sc, npages);
220081a23f33Skevlo 			for (i = 0; i < nitems(rtl8125_mac_bps); i++) {
22018cbc3b0aSkevlo 				if (npages == 0)
22028cbc3b0aSkevlo 					rge_write_mac_ocp(sc,
22038cbc3b0aSkevlo 					    rtl8125_mac_bps[i].reg,
220481a23f33Skevlo 					    rtl8125_mac_bps[i].val);
22058cbc3b0aSkevlo 				else if (npages == 1)
22068cbc3b0aSkevlo 					rge_write_mac_ocp(sc,
22078cbc3b0aSkevlo 					    rtl8125_mac_bps[i].reg, 0);
22088cbc3b0aSkevlo 				else {
22098cbc3b0aSkevlo 					if (rtl8125_mac_bps[i].reg < 0xf9f8)
22108cbc3b0aSkevlo 						rge_write_mac_ocp(sc,
22118cbc3b0aSkevlo 						    rtl8125_mac_bps[i].reg, 0);
221281a23f33Skevlo 				}
22138cbc3b0aSkevlo 			}
22148cbc3b0aSkevlo 			if (npages == 2) {
22158cbc3b0aSkevlo 				rge_write_mac_ocp(sc, 0xf9f8, 0x6486);
22168cbc3b0aSkevlo 				rge_write_mac_ocp(sc, 0xf9fa, 0x0b15);
22178cbc3b0aSkevlo 				rge_write_mac_ocp(sc, 0xf9fc, 0x090e);
22188cbc3b0aSkevlo 				rge_write_mac_ocp(sc, 0xf9fe, 0x1139);
22198cbc3b0aSkevlo 			}
22208cbc3b0aSkevlo 		}
22218cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xfc26, 0x8000);
22228cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xfc2a, 0x0540);
22238cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xfc2e, 0x0a06);
22248cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xfc30, 0x0eb8);
22258cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xfc32, 0x3a5c);
22268cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xfc34, 0x10a8);
22278cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xfc40, 0x0d54);
22288cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xfc42, 0x0e24);
22298cbc3b0aSkevlo 		rge_write_mac_ocp(sc, 0xfc48, 0x307a);
223081a23f33Skevlo 	} else if (sc->rge_type == MAC_CFG5) {
22318cbc3b0aSkevlo 		rge_switch_mcu_ram_page(sc, 0);
223281a23f33Skevlo 		for (i = 0; i < nitems(rtl8125b_mac_bps); i++) {
223381a23f33Skevlo 			rge_write_mac_ocp(sc, rtl8125b_mac_bps[i].reg,
223481a23f33Skevlo 			    rtl8125b_mac_bps[i].val);
223581a23f33Skevlo 		}
2236be4e4fa3Skevlo 	}
2237be4e4fa3Skevlo 
2238be4e4fa3Skevlo 	/* Disable PHY power saving. */
223974263a62Skevlo 	if (sc->rge_type == MAC_CFG3)
2240be4e4fa3Skevlo 		rge_disable_phy_ocp_pwrsave(sc);
2241be4e4fa3Skevlo 
2242be4e4fa3Skevlo 	/* Set PCIe uncorrectable error status. */
2243be4e4fa3Skevlo 	rge_write_csi(sc, 0x108,
2244be4e4fa3Skevlo 	    rge_read_csi(sc, 0x108) | 0x00100000);
22458cbc3b0aSkevlo }
22467bcadffeSkevlo 
22478cbc3b0aSkevlo void
22488cbc3b0aSkevlo rge_hw_reset(struct rge_softc *sc)
22498cbc3b0aSkevlo {
22508cbc3b0aSkevlo 	/* Disable interrupts */
22518cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_IMR, 0);
22528cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_ISR, RGE_READ_4(sc, RGE_ISR));
22538cbc3b0aSkevlo 
22548cbc3b0aSkevlo 	/* Clear timer interrupts. */
22558cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_TIMERINT0, 0);
22568cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_TIMERINT1, 0);
22578cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_TIMERINT2, 0);
22588cbc3b0aSkevlo 	RGE_WRITE_4(sc, RGE_TIMERINT3, 0);
22598cbc3b0aSkevlo 
22608cbc3b0aSkevlo 	rge_reset(sc);
2261be4e4fa3Skevlo }
2262be4e4fa3Skevlo 
2263be4e4fa3Skevlo void
2264be4e4fa3Skevlo rge_disable_phy_ocp_pwrsave(struct rge_softc *sc)
2265be4e4fa3Skevlo {
2266be4e4fa3Skevlo 	if (rge_read_phy_ocp(sc, 0xc416) != 0x0500) {
2267be4e4fa3Skevlo 		rge_patch_phy_mcu(sc, 1);
2268be4e4fa3Skevlo 		rge_write_phy_ocp(sc, 0xc416, 0);
2269be4e4fa3Skevlo 		rge_write_phy_ocp(sc, 0xc416, 0x0500);
2270be4e4fa3Skevlo 		rge_patch_phy_mcu(sc, 0);
2271be4e4fa3Skevlo 	}
2272be4e4fa3Skevlo }
2273be4e4fa3Skevlo 
2274be4e4fa3Skevlo void
2275be4e4fa3Skevlo rge_patch_phy_mcu(struct rge_softc *sc, int set)
2276be4e4fa3Skevlo {
2277be4e4fa3Skevlo 	int i;
2278be4e4fa3Skevlo 
2279be4e4fa3Skevlo 	if (set)
2280be4e4fa3Skevlo 		RGE_PHY_SETBIT(sc, 0xb820, 0x0010);
2281be4e4fa3Skevlo 	else
2282be4e4fa3Skevlo 		RGE_PHY_CLRBIT(sc, 0xb820, 0x0010);
2283be4e4fa3Skevlo 
2284be4e4fa3Skevlo 	for (i = 0; i < 1000; i++) {
22858cbc3b0aSkevlo 		if (set) {
22868cbc3b0aSkevlo 			if ((rge_read_phy_ocp(sc, 0xb800) & 0x0040) != 0)
2287be4e4fa3Skevlo 				break;
22888cbc3b0aSkevlo 		} else {
22898cbc3b0aSkevlo 			if (!(rge_read_phy_ocp(sc, 0xb800) & 0x0040))
22908cbc3b0aSkevlo 				break;
22918cbc3b0aSkevlo 		}
229281a23f33Skevlo 		DELAY(100);
2293be4e4fa3Skevlo 	}
22948cbc3b0aSkevlo 	if (i == 1000)
22958cbc3b0aSkevlo 		printf("%s: timeout waiting to patch phy mcu\n",
22968cbc3b0aSkevlo 		    sc->sc_dev.dv_xname);
2297be4e4fa3Skevlo }
2298be4e4fa3Skevlo 
2299be4e4fa3Skevlo void
2300be4e4fa3Skevlo rge_add_media_types(struct rge_softc *sc)
2301be4e4fa3Skevlo {
2302be4e4fa3Skevlo 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T, 0, NULL);
2303be4e4fa3Skevlo 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
2304be4e4fa3Skevlo 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX, 0, NULL);
2305be4e4fa3Skevlo 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
2306be4e4fa3Skevlo 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_1000_T, 0, NULL);
2307be4e4fa3Skevlo 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
2308be4e4fa3Skevlo 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_2500_T, 0, NULL);
2309be4e4fa3Skevlo 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_2500_T | IFM_FDX, 0, NULL);
231074263a62Skevlo 
231174263a62Skevlo 	if (sc->rge_type == MAC_CFG2_8126) {
231274263a62Skevlo 		ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_5000_T, 0, NULL);
231374263a62Skevlo 		ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_5000_T | IFM_FDX,
231474263a62Skevlo 		    0, NULL);
231574263a62Skevlo 	}
2316be4e4fa3Skevlo }
2317be4e4fa3Skevlo 
2318be4e4fa3Skevlo void
2319be4e4fa3Skevlo rge_config_imtype(struct rge_softc *sc, int imtype)
2320be4e4fa3Skevlo {
2321be4e4fa3Skevlo 	switch (imtype) {
2322be4e4fa3Skevlo 	case RGE_IMTYPE_NONE:
2323be4e4fa3Skevlo 		sc->rge_intrs = RGE_INTRS;
2324be4e4fa3Skevlo 		break;
2325be4e4fa3Skevlo 	case RGE_IMTYPE_SIM:
2326be4e4fa3Skevlo 		sc->rge_intrs = RGE_INTRS_TIMER;
2327be4e4fa3Skevlo 		break;
2328be4e4fa3Skevlo 	default:
2329be4e4fa3Skevlo 		panic("%s: unknown imtype %d", sc->sc_dev.dv_xname, imtype);
2330be4e4fa3Skevlo 	}
2331be4e4fa3Skevlo }
2332be4e4fa3Skevlo 
2333be4e4fa3Skevlo void
23348cbc3b0aSkevlo rge_disable_aspm_clkreq(struct rge_softc *sc)
23358cbc3b0aSkevlo {
23368cbc3b0aSkevlo 	RGE_SETBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
233774263a62Skevlo 	if (sc->rge_type == MAC_CFG2_8126)
233874263a62Skevlo 		RGE_CLRBIT_1(sc, RGE_INT_CFG0, 0x08);
233974263a62Skevlo 	else
23408cbc3b0aSkevlo 		RGE_CLRBIT_1(sc, RGE_CFG2, RGE_CFG2_CLKREQ_EN);
23418cbc3b0aSkevlo 	RGE_CLRBIT_1(sc, RGE_CFG5, RGE_CFG5_PME_STS);
23428cbc3b0aSkevlo 	RGE_CLRBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
23438cbc3b0aSkevlo }
23448cbc3b0aSkevlo 
23458cbc3b0aSkevlo void
234681a23f33Skevlo rge_disable_hw_im(struct rge_softc *sc)
234781a23f33Skevlo {
234881a23f33Skevlo 	RGE_WRITE_2(sc, RGE_IM, 0);
234981a23f33Skevlo }
235081a23f33Skevlo 
235181a23f33Skevlo void
2352be4e4fa3Skevlo rge_disable_sim_im(struct rge_softc *sc)
2353be4e4fa3Skevlo {
235481a23f33Skevlo 	RGE_WRITE_4(sc, RGE_TIMERINT0, 0);
2355be4e4fa3Skevlo 	sc->rge_timerintr = 0;
2356be4e4fa3Skevlo }
2357be4e4fa3Skevlo 
2358be4e4fa3Skevlo void
2359be4e4fa3Skevlo rge_setup_sim_im(struct rge_softc *sc)
2360be4e4fa3Skevlo {
236181a23f33Skevlo 	RGE_WRITE_4(sc, RGE_TIMERINT0, 0x2600);
2362be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_TIMERCNT, 1);
2363be4e4fa3Skevlo 	sc->rge_timerintr = 1;
2364be4e4fa3Skevlo }
2365be4e4fa3Skevlo 
2366be4e4fa3Skevlo void
2367be4e4fa3Skevlo rge_setup_intr(struct rge_softc *sc, int imtype)
2368be4e4fa3Skevlo {
2369be4e4fa3Skevlo 	rge_config_imtype(sc, imtype);
2370be4e4fa3Skevlo 
2371be4e4fa3Skevlo 	/* Enable interrupts. */
2372be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_IMR, sc->rge_intrs);
2373be4e4fa3Skevlo 
2374be4e4fa3Skevlo 	switch (imtype) {
2375be4e4fa3Skevlo 	case RGE_IMTYPE_NONE:
2376be4e4fa3Skevlo 		rge_disable_sim_im(sc);
237781a23f33Skevlo 		rge_disable_hw_im(sc);
2378be4e4fa3Skevlo 		break;
2379be4e4fa3Skevlo 	case RGE_IMTYPE_SIM:
238081a23f33Skevlo 		rge_disable_hw_im(sc);
2381be4e4fa3Skevlo 		rge_setup_sim_im(sc);
2382be4e4fa3Skevlo 		break;
2383be4e4fa3Skevlo 	default:
2384be4e4fa3Skevlo 		panic("%s: unknown imtype %d", sc->sc_dev.dv_xname, imtype);
2385be4e4fa3Skevlo 	}
2386be4e4fa3Skevlo }
2387be4e4fa3Skevlo 
2388be4e4fa3Skevlo void
23898cbc3b0aSkevlo rge_switch_mcu_ram_page(struct rge_softc *sc, int page)
23908cbc3b0aSkevlo {
23918cbc3b0aSkevlo 	uint16_t val;
23928cbc3b0aSkevlo 
23938cbc3b0aSkevlo 	val = rge_read_mac_ocp(sc, 0xe446) & ~0x0003;
23948cbc3b0aSkevlo 	val |= page;
23958cbc3b0aSkevlo 	rge_write_mac_ocp(sc, 0xe446, val);
23968cbc3b0aSkevlo }
23978cbc3b0aSkevlo 
23988cbc3b0aSkevlo void
2399be4e4fa3Skevlo rge_exit_oob(struct rge_softc *sc)
2400be4e4fa3Skevlo {
2401be4e4fa3Skevlo 	int i;
2402be4e4fa3Skevlo 
2403be4e4fa3Skevlo 	RGE_CLRBIT_4(sc, RGE_RXCFG, RGE_RXCFG_ALLPHYS | RGE_RXCFG_INDIV |
2404be4e4fa3Skevlo 	    RGE_RXCFG_MULTI | RGE_RXCFG_BROAD | RGE_RXCFG_RUNT |
2405be4e4fa3Skevlo 	    RGE_RXCFG_ERRPKT);
2406be4e4fa3Skevlo 
2407be4e4fa3Skevlo 	/* Disable RealWoW. */
2408be4e4fa3Skevlo 	rge_write_mac_ocp(sc, 0xc0bc, 0x00ff);
2409be4e4fa3Skevlo 
2410be4e4fa3Skevlo 	rge_reset(sc);
2411be4e4fa3Skevlo 
2412be4e4fa3Skevlo 	/* Disable OOB. */
2413be4e4fa3Skevlo 	RGE_CLRBIT_1(sc, RGE_MCUCMD, RGE_MCUCMD_IS_OOB);
2414be4e4fa3Skevlo 
2415be4e4fa3Skevlo 	RGE_MAC_CLRBIT(sc, 0xe8de, 0x4000);
2416be4e4fa3Skevlo 
2417be4e4fa3Skevlo 	for (i = 0; i < 10; i++) {
2418be4e4fa3Skevlo 		DELAY(100);
2419be4e4fa3Skevlo 		if (RGE_READ_2(sc, RGE_TWICMD) & 0x0200)
2420be4e4fa3Skevlo 			break;
2421be4e4fa3Skevlo 	}
2422be4e4fa3Skevlo 
2423be4e4fa3Skevlo 	rge_write_mac_ocp(sc, 0xc0aa, 0x07d0);
242481a23f33Skevlo 	rge_write_mac_ocp(sc, 0xc0a6, 0x01b5);
2425be4e4fa3Skevlo 	rge_write_mac_ocp(sc, 0xc01e, 0x5555);
2426be4e4fa3Skevlo 
2427be4e4fa3Skevlo 	for (i = 0; i < 10; i++) {
2428be4e4fa3Skevlo 		DELAY(100);
2429be4e4fa3Skevlo 		if (RGE_READ_2(sc, RGE_TWICMD) & 0x0200)
2430be4e4fa3Skevlo 			break;
2431be4e4fa3Skevlo 	}
2432be4e4fa3Skevlo 
2433be4e4fa3Skevlo 	if (rge_read_mac_ocp(sc, 0xd42c) & 0x0100) {
2434be4e4fa3Skevlo 		for (i = 0; i < RGE_TIMEOUT; i++) {
24351943b9bcSkrw 			if ((rge_read_phy_ocp(sc, 0xa420) & 0x0007) == 2)
2436be4e4fa3Skevlo 				break;
2437be4e4fa3Skevlo 			DELAY(1000);
2438be4e4fa3Skevlo 		}
24398cbc3b0aSkevlo 		RGE_MAC_CLRBIT(sc, 0xd42c, 0x0100);
24408cbc3b0aSkevlo 		if (sc->rge_type != MAC_CFG3)
244181a23f33Skevlo 			RGE_PHY_CLRBIT(sc, 0xa466, 0x0001);
2442be4e4fa3Skevlo 		RGE_PHY_CLRBIT(sc, 0xa468, 0x000a);
2443be4e4fa3Skevlo 	}
2444be4e4fa3Skevlo }
2445be4e4fa3Skevlo 
2446be4e4fa3Skevlo void
2447be4e4fa3Skevlo rge_write_csi(struct rge_softc *sc, uint32_t reg, uint32_t val)
2448be4e4fa3Skevlo {
2449be4e4fa3Skevlo 	int i;
2450be4e4fa3Skevlo 
2451be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_CSIDR, val);
245281a23f33Skevlo 	RGE_WRITE_4(sc, RGE_CSIAR, (reg & RGE_CSIAR_ADDR_MASK) |
2453be4e4fa3Skevlo 	    (RGE_CSIAR_BYTE_EN << RGE_CSIAR_BYTE_EN_SHIFT) | RGE_CSIAR_BUSY);
2454be4e4fa3Skevlo 
24558cbc3b0aSkevlo 	for (i = 0; i < 20000; i++) {
24568cbc3b0aSkevlo 		 DELAY(1);
2457be4e4fa3Skevlo 		 if (!(RGE_READ_4(sc, RGE_CSIAR) & RGE_CSIAR_BUSY))
2458be4e4fa3Skevlo 			break;
2459be4e4fa3Skevlo 	}
2460be4e4fa3Skevlo 
2461be4e4fa3Skevlo 	DELAY(20);
2462be4e4fa3Skevlo }
2463be4e4fa3Skevlo 
2464be4e4fa3Skevlo uint32_t
2465be4e4fa3Skevlo rge_read_csi(struct rge_softc *sc, uint32_t reg)
2466be4e4fa3Skevlo {
2467be4e4fa3Skevlo 	int i;
2468be4e4fa3Skevlo 
246981a23f33Skevlo 	RGE_WRITE_4(sc, RGE_CSIAR, (reg & RGE_CSIAR_ADDR_MASK) |
2470be4e4fa3Skevlo 	    (RGE_CSIAR_BYTE_EN << RGE_CSIAR_BYTE_EN_SHIFT));
2471be4e4fa3Skevlo 
24728cbc3b0aSkevlo 	for (i = 0; i < 20000; i++) {
24738cbc3b0aSkevlo 		 DELAY(1);
2474be4e4fa3Skevlo 		 if (RGE_READ_4(sc, RGE_CSIAR) & RGE_CSIAR_BUSY)
2475be4e4fa3Skevlo 			break;
2476be4e4fa3Skevlo 	}
2477be4e4fa3Skevlo 
2478be4e4fa3Skevlo 	DELAY(20);
2479be4e4fa3Skevlo 
2480be4e4fa3Skevlo 	return (RGE_READ_4(sc, RGE_CSIDR));
2481be4e4fa3Skevlo }
2482be4e4fa3Skevlo 
2483be4e4fa3Skevlo void
2484be4e4fa3Skevlo rge_write_mac_ocp(struct rge_softc *sc, uint16_t reg, uint16_t val)
2485be4e4fa3Skevlo {
2486be4e4fa3Skevlo 	uint32_t tmp;
2487be4e4fa3Skevlo 
2488be4e4fa3Skevlo 	tmp = (reg >> 1) << RGE_MACOCP_ADDR_SHIFT;
2489be4e4fa3Skevlo 	tmp += val;
2490be4e4fa3Skevlo 	tmp |= RGE_MACOCP_BUSY;
2491be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_MACOCP, tmp);
2492be4e4fa3Skevlo }
2493be4e4fa3Skevlo 
2494be4e4fa3Skevlo uint16_t
2495be4e4fa3Skevlo rge_read_mac_ocp(struct rge_softc *sc, uint16_t reg)
2496be4e4fa3Skevlo {
2497be4e4fa3Skevlo 	uint32_t val;
2498be4e4fa3Skevlo 
2499be4e4fa3Skevlo 	val = (reg >> 1) << RGE_MACOCP_ADDR_SHIFT;
2500be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_MACOCP, val);
2501be4e4fa3Skevlo 
2502be4e4fa3Skevlo 	return (RGE_READ_4(sc, RGE_MACOCP) & RGE_MACOCP_DATA_MASK);
2503be4e4fa3Skevlo }
2504be4e4fa3Skevlo 
2505be4e4fa3Skevlo void
2506be4e4fa3Skevlo rge_write_ephy(struct rge_softc *sc, uint16_t reg, uint16_t val)
2507be4e4fa3Skevlo {
2508be4e4fa3Skevlo 	uint32_t tmp;
2509be4e4fa3Skevlo 	int i;
2510be4e4fa3Skevlo 
2511be4e4fa3Skevlo 	tmp = (reg & RGE_EPHYAR_ADDR_MASK) << RGE_EPHYAR_ADDR_SHIFT;
2512be4e4fa3Skevlo 	tmp |= RGE_EPHYAR_BUSY | (val & RGE_EPHYAR_DATA_MASK);
2513be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_EPHYAR, tmp);
2514be4e4fa3Skevlo 
2515be4e4fa3Skevlo 	for (i = 0; i < 10; i++) {
2516be4e4fa3Skevlo 		DELAY(100);
2517be4e4fa3Skevlo 		if (!(RGE_READ_4(sc, RGE_EPHYAR) & RGE_EPHYAR_BUSY))
2518be4e4fa3Skevlo 			break;
2519be4e4fa3Skevlo 	}
2520be4e4fa3Skevlo 
2521be4e4fa3Skevlo 	DELAY(20);
2522be4e4fa3Skevlo }
2523be4e4fa3Skevlo 
252481a23f33Skevlo uint16_t
252581a23f33Skevlo rge_read_ephy(struct rge_softc *sc, uint16_t reg)
252681a23f33Skevlo {
252781a23f33Skevlo 	uint32_t val;
252881a23f33Skevlo 	int i;
252981a23f33Skevlo 
253081a23f33Skevlo 	val = (reg & RGE_EPHYAR_ADDR_MASK) << RGE_EPHYAR_ADDR_SHIFT;
253181a23f33Skevlo 	RGE_WRITE_4(sc, RGE_EPHYAR, val);
253281a23f33Skevlo 
253381a23f33Skevlo 	for (i = 0; i < 10; i++) {
253481a23f33Skevlo 		DELAY(100);
253581a23f33Skevlo 		val = RGE_READ_4(sc, RGE_EPHYAR);
253681a23f33Skevlo 		if (val & RGE_EPHYAR_BUSY)
253781a23f33Skevlo 			break;
253881a23f33Skevlo 	}
253981a23f33Skevlo 
254081a23f33Skevlo 	DELAY(20);
254181a23f33Skevlo 
254281a23f33Skevlo 	return (val & RGE_EPHYAR_DATA_MASK);
254381a23f33Skevlo }
254481a23f33Skevlo 
2545be4e4fa3Skevlo void
2546be4e4fa3Skevlo rge_write_phy(struct rge_softc *sc, uint16_t addr, uint16_t reg, uint16_t val)
2547be4e4fa3Skevlo {
2548be4e4fa3Skevlo 	uint16_t off, phyaddr;
2549be4e4fa3Skevlo 
2550be4e4fa3Skevlo 	phyaddr = addr ? addr : RGE_PHYBASE + (reg / 8);
2551be4e4fa3Skevlo 	phyaddr <<= 4;
2552be4e4fa3Skevlo 
2553be4e4fa3Skevlo 	off = addr ? reg : 0x10 + (reg % 8);
2554be4e4fa3Skevlo 
2555be4e4fa3Skevlo 	phyaddr += (off - 16) << 1;
2556be4e4fa3Skevlo 
2557be4e4fa3Skevlo 	rge_write_phy_ocp(sc, phyaddr, val);
2558be4e4fa3Skevlo }
2559be4e4fa3Skevlo 
2560dfa3bc0bSkevlo uint16_t
2561dfa3bc0bSkevlo rge_read_phy(struct rge_softc *sc, uint16_t addr, uint16_t reg)
2562dfa3bc0bSkevlo {
2563dfa3bc0bSkevlo 	uint16_t off, phyaddr;
2564dfa3bc0bSkevlo 
2565dfa3bc0bSkevlo 	phyaddr = addr ? addr : RGE_PHYBASE + (reg / 8);
2566dfa3bc0bSkevlo 	phyaddr <<= 4;
2567dfa3bc0bSkevlo 
2568dfa3bc0bSkevlo 	off = addr ? reg : 0x10 + (reg % 8);
2569dfa3bc0bSkevlo 
2570dfa3bc0bSkevlo 	phyaddr += (off - 16) << 1;
2571dfa3bc0bSkevlo 
2572dfa3bc0bSkevlo 	return (rge_read_phy_ocp(sc, phyaddr));
2573dfa3bc0bSkevlo }
2574dfa3bc0bSkevlo 
2575be4e4fa3Skevlo void
2576be4e4fa3Skevlo rge_write_phy_ocp(struct rge_softc *sc, uint16_t reg, uint16_t val)
2577be4e4fa3Skevlo {
2578be4e4fa3Skevlo 	uint32_t tmp;
2579be4e4fa3Skevlo 	int i;
2580be4e4fa3Skevlo 
2581be4e4fa3Skevlo 	tmp = (reg >> 1) << RGE_PHYOCP_ADDR_SHIFT;
2582be4e4fa3Skevlo 	tmp |= RGE_PHYOCP_BUSY | val;
2583be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_PHYOCP, tmp);
2584be4e4fa3Skevlo 
2585be4e4fa3Skevlo 	for (i = 0; i < RGE_TIMEOUT; i++) {
2586be4e4fa3Skevlo 		DELAY(1);
2587be4e4fa3Skevlo 		if (!(RGE_READ_4(sc, RGE_PHYOCP) & RGE_PHYOCP_BUSY))
2588be4e4fa3Skevlo 			break;
2589be4e4fa3Skevlo 	}
2590be4e4fa3Skevlo }
2591be4e4fa3Skevlo 
2592be4e4fa3Skevlo uint16_t
2593be4e4fa3Skevlo rge_read_phy_ocp(struct rge_softc *sc, uint16_t reg)
2594be4e4fa3Skevlo {
2595be4e4fa3Skevlo 	uint32_t val;
2596be4e4fa3Skevlo 	int i;
2597be4e4fa3Skevlo 
2598be4e4fa3Skevlo 	val = (reg >> 1) << RGE_PHYOCP_ADDR_SHIFT;
2599be4e4fa3Skevlo 	RGE_WRITE_4(sc, RGE_PHYOCP, val);
2600be4e4fa3Skevlo 
2601be4e4fa3Skevlo 	for (i = 0; i < RGE_TIMEOUT; i++) {
2602be4e4fa3Skevlo 		DELAY(1);
2603be4e4fa3Skevlo 		val = RGE_READ_4(sc, RGE_PHYOCP);
2604be4e4fa3Skevlo 		if (val & RGE_PHYOCP_BUSY)
2605be4e4fa3Skevlo 			break;
2606be4e4fa3Skevlo 	}
2607be4e4fa3Skevlo 
2608be4e4fa3Skevlo 	return (val & RGE_PHYOCP_DATA_MASK);
2609be4e4fa3Skevlo }
2610be4e4fa3Skevlo 
2611be4e4fa3Skevlo int
2612be4e4fa3Skevlo rge_get_link_status(struct rge_softc *sc)
2613be4e4fa3Skevlo {
2614be4e4fa3Skevlo 	return ((RGE_READ_2(sc, RGE_PHYSTAT) & RGE_PHYSTAT_LINK) ? 1 : 0);
2615be4e4fa3Skevlo }
2616be4e4fa3Skevlo 
2617be4e4fa3Skevlo void
2618be4e4fa3Skevlo rge_txstart(void *arg)
2619be4e4fa3Skevlo {
2620be4e4fa3Skevlo 	struct rge_softc *sc = arg;
2621be4e4fa3Skevlo 
2622be4e4fa3Skevlo 	RGE_WRITE_2(sc, RGE_TXSTART, RGE_TXSTART_START);
2623be4e4fa3Skevlo }
2624be4e4fa3Skevlo 
2625be4e4fa3Skevlo void
2626be4e4fa3Skevlo rge_tick(void *arg)
2627be4e4fa3Skevlo {
2628be4e4fa3Skevlo 	struct rge_softc *sc = arg;
2629be4e4fa3Skevlo 	int s;
2630be4e4fa3Skevlo 
2631be4e4fa3Skevlo 	s = splnet();
2632be4e4fa3Skevlo 	rge_link_state(sc);
2633be4e4fa3Skevlo 	splx(s);
2634be4e4fa3Skevlo 
2635be4e4fa3Skevlo 	timeout_add_sec(&sc->sc_timeout, 1);
2636be4e4fa3Skevlo }
2637be4e4fa3Skevlo 
2638be4e4fa3Skevlo void
2639be4e4fa3Skevlo rge_link_state(struct rge_softc *sc)
2640be4e4fa3Skevlo {
2641be4e4fa3Skevlo 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
2642be4e4fa3Skevlo 	int link = LINK_STATE_DOWN;
2643be4e4fa3Skevlo 
2644be4e4fa3Skevlo 	if (rge_get_link_status(sc))
2645be4e4fa3Skevlo 		link = LINK_STATE_UP;
2646be4e4fa3Skevlo 
2647be4e4fa3Skevlo 	if (ifp->if_link_state != link) {
2648be4e4fa3Skevlo 		ifp->if_link_state = link;
2649be4e4fa3Skevlo 		if_link_state_change(ifp);
2650be4e4fa3Skevlo 	}
2651be4e4fa3Skevlo }
26527bcadffeSkevlo 
26537bcadffeSkevlo #ifndef SMALL_KERNEL
26547bcadffeSkevlo int
26557bcadffeSkevlo rge_wol(struct ifnet *ifp, int enable)
26567bcadffeSkevlo {
26577bcadffeSkevlo 	struct rge_softc *sc = ifp->if_softc;
26587bcadffeSkevlo 
26597bcadffeSkevlo 	if (enable) {
26607bcadffeSkevlo 		if (!(RGE_READ_1(sc, RGE_CFG1) & RGE_CFG1_PM_EN)) {
26617bcadffeSkevlo 			printf("%s: power management is disabled, "
26627bcadffeSkevlo 			    "cannot do WOL\n", sc->sc_dev.dv_xname);
26637bcadffeSkevlo 			return (ENOTSUP);
26647bcadffeSkevlo 		}
26657bcadffeSkevlo 
26667bcadffeSkevlo 	}
26677bcadffeSkevlo 
26687bcadffeSkevlo 	rge_iff(sc);
26697bcadffeSkevlo 
26707bcadffeSkevlo 	if (enable)
26717bcadffeSkevlo 		RGE_MAC_SETBIT(sc, 0xc0b6, 0x0001);
26727bcadffeSkevlo 	else
26737bcadffeSkevlo 		RGE_MAC_CLRBIT(sc, 0xc0b6, 0x0001);
26747bcadffeSkevlo 
26757bcadffeSkevlo 	RGE_SETBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
26767bcadffeSkevlo 	RGE_CLRBIT_1(sc, RGE_CFG5, RGE_CFG5_WOL_LANWAKE | RGE_CFG5_WOL_UCAST |
26777bcadffeSkevlo 	    RGE_CFG5_WOL_MCAST | RGE_CFG5_WOL_BCAST);
26787bcadffeSkevlo 	RGE_CLRBIT_1(sc, RGE_CFG3, RGE_CFG3_WOL_LINK | RGE_CFG3_WOL_MAGIC);
26797bcadffeSkevlo 	if (enable)
26807bcadffeSkevlo 		RGE_SETBIT_1(sc, RGE_CFG5, RGE_CFG5_WOL_LANWAKE);
26817bcadffeSkevlo 	RGE_CLRBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
26827bcadffeSkevlo 
26837bcadffeSkevlo 	return (0);
26847bcadffeSkevlo }
26857bcadffeSkevlo 
26867bcadffeSkevlo void
26877bcadffeSkevlo rge_wol_power(struct rge_softc *sc)
26887bcadffeSkevlo {
26897bcadffeSkevlo 	/* Disable RXDV gate. */
26907bcadffeSkevlo 	RGE_CLRBIT_1(sc, RGE_PPSW, 0x08);
26917bcadffeSkevlo 	DELAY(2000);
26927bcadffeSkevlo 
26937bcadffeSkevlo 	RGE_SETBIT_1(sc, RGE_CFG1, RGE_CFG1_PM_EN);
26947bcadffeSkevlo 	RGE_SETBIT_1(sc, RGE_CFG2, RGE_CFG2_PMSTS_EN);
26957bcadffeSkevlo }
26967bcadffeSkevlo #endif
26971b7ab755Sdlg 
26981b7ab755Sdlg #if NKSTAT > 0
26991b7ab755Sdlg 
27001b7ab755Sdlg #define RGE_DTCCR_CMD		(1U << 3)
27011b7ab755Sdlg #define RGE_DTCCR_LO		0x10
27021b7ab755Sdlg #define RGE_DTCCR_HI		0x14
27031b7ab755Sdlg 
27041b7ab755Sdlg struct rge_kstats {
27051b7ab755Sdlg 	struct kstat_kv		tx_ok;
27061b7ab755Sdlg 	struct kstat_kv		rx_ok;
27071b7ab755Sdlg 	struct kstat_kv		tx_er;
27081b7ab755Sdlg 	struct kstat_kv		rx_er;
27091b7ab755Sdlg 	struct kstat_kv		miss_pkt;
27101b7ab755Sdlg 	struct kstat_kv		fae;
27111b7ab755Sdlg 	struct kstat_kv		tx_1col;
27121b7ab755Sdlg 	struct kstat_kv		tx_mcol;
27131b7ab755Sdlg 	struct kstat_kv		rx_ok_phy;
27141b7ab755Sdlg 	struct kstat_kv		rx_ok_brd;
27151b7ab755Sdlg 	struct kstat_kv		rx_ok_mul;
27161b7ab755Sdlg 	struct kstat_kv		tx_abt;
27171b7ab755Sdlg 	struct kstat_kv		tx_undrn;
27181b7ab755Sdlg };
27191b7ab755Sdlg 
27201b7ab755Sdlg static const struct rge_kstats rge_kstats_tpl = {
27211b7ab755Sdlg 	.tx_ok =	KSTAT_KV_UNIT_INITIALIZER("TxOk",
27221b7ab755Sdlg 			    KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
27231b7ab755Sdlg 	.rx_ok =	KSTAT_KV_UNIT_INITIALIZER("RxOk",
27241b7ab755Sdlg 			    KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
27251b7ab755Sdlg 	.tx_er =	KSTAT_KV_UNIT_INITIALIZER("TxEr",
27261b7ab755Sdlg 			    KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
27271b7ab755Sdlg 	.rx_er =	KSTAT_KV_UNIT_INITIALIZER("RxEr",
27281b7ab755Sdlg 			    KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS),
27291b7ab755Sdlg 	.miss_pkt =	KSTAT_KV_UNIT_INITIALIZER("MissPkt",
27301b7ab755Sdlg 			    KSTAT_KV_T_COUNTER16, KSTAT_KV_U_PACKETS),
27311b7ab755Sdlg 	.fae =		KSTAT_KV_UNIT_INITIALIZER("FAE",
27321b7ab755Sdlg 			    KSTAT_KV_T_COUNTER16, KSTAT_KV_U_PACKETS),
27331b7ab755Sdlg 	.tx_1col =	KSTAT_KV_UNIT_INITIALIZER("Tx1Col",
27341b7ab755Sdlg 			    KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS),
27351b7ab755Sdlg 	.tx_mcol =	KSTAT_KV_UNIT_INITIALIZER("TxMCol",
27361b7ab755Sdlg 			    KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS),
27371b7ab755Sdlg 	.rx_ok_phy =	KSTAT_KV_UNIT_INITIALIZER("RxOkPhy",
27381b7ab755Sdlg 			    KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
27391b7ab755Sdlg 	.rx_ok_brd =	KSTAT_KV_UNIT_INITIALIZER("RxOkBrd",
27401b7ab755Sdlg 			    KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
27411b7ab755Sdlg 	.rx_ok_mul =	KSTAT_KV_UNIT_INITIALIZER("RxOkMul",
27421b7ab755Sdlg 			    KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS),
27431b7ab755Sdlg 	.tx_abt =	KSTAT_KV_UNIT_INITIALIZER("TxAbt",
27441b7ab755Sdlg 			    KSTAT_KV_T_COUNTER16, KSTAT_KV_U_PACKETS),
27451b7ab755Sdlg 	.tx_undrn =	KSTAT_KV_UNIT_INITIALIZER("TxUndrn",
27461b7ab755Sdlg 			    KSTAT_KV_T_COUNTER16, KSTAT_KV_U_PACKETS),
27471b7ab755Sdlg };
27481b7ab755Sdlg 
27491b7ab755Sdlg struct rge_kstat_softc {
27501b7ab755Sdlg 	struct rge_stats	*rge_ks_sc_stats;
27511b7ab755Sdlg 
27521b7ab755Sdlg 	bus_dmamap_t		 rge_ks_sc_map;
27531b7ab755Sdlg 	bus_dma_segment_t	 rge_ks_sc_seg;
27541b7ab755Sdlg 	int			 rge_ks_sc_nsegs;
27551b7ab755Sdlg 
27561b7ab755Sdlg 	struct rwlock		 rge_ks_sc_rwl;
27571b7ab755Sdlg };
27581b7ab755Sdlg 
27591b7ab755Sdlg static int
27601b7ab755Sdlg rge_kstat_read(struct kstat *ks)
27611b7ab755Sdlg {
27621b7ab755Sdlg 	struct rge_softc *sc = ks->ks_softc;
27631b7ab755Sdlg 	struct rge_kstat_softc *rge_ks_sc = ks->ks_ptr;
27641b7ab755Sdlg 	bus_dmamap_t map;
27651b7ab755Sdlg 	uint64_t cmd;
27661b7ab755Sdlg 	uint32_t reg;
27671b7ab755Sdlg 	uint8_t command;
27681b7ab755Sdlg 	int tmo;
27691b7ab755Sdlg 
27701b7ab755Sdlg 	command = RGE_READ_1(sc, RGE_CMD);
27711b7ab755Sdlg 	if (!ISSET(command, RGE_CMD_RXENB) || command == 0xff)
27721b7ab755Sdlg 		return (ENETDOWN);
27731b7ab755Sdlg 
27741b7ab755Sdlg 	map = rge_ks_sc->rge_ks_sc_map;
27751b7ab755Sdlg 	cmd = map->dm_segs[0].ds_addr | RGE_DTCCR_CMD;
27761b7ab755Sdlg 
27771b7ab755Sdlg 	bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
27781b7ab755Sdlg 	    BUS_DMASYNC_PREREAD);
27791b7ab755Sdlg 
27801b7ab755Sdlg 	RGE_WRITE_4(sc, RGE_DTCCR_HI, cmd >> 32);
27811b7ab755Sdlg 	bus_space_barrier(sc->rge_btag, sc->rge_bhandle, RGE_DTCCR_HI, 8,
27821b7ab755Sdlg 	    BUS_SPACE_BARRIER_WRITE);
27831b7ab755Sdlg 	RGE_WRITE_4(sc, RGE_DTCCR_LO, cmd);
27841b7ab755Sdlg 	bus_space_barrier(sc->rge_btag, sc->rge_bhandle, RGE_DTCCR_LO, 4,
27851b7ab755Sdlg 	    BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
27861b7ab755Sdlg 
27871b7ab755Sdlg 	tmo = 1000;
27881b7ab755Sdlg 	do {
27891b7ab755Sdlg 		reg = RGE_READ_4(sc, RGE_DTCCR_LO);
27901b7ab755Sdlg 		if (!ISSET(reg, RGE_DTCCR_CMD))
27911b7ab755Sdlg 			break;
27921b7ab755Sdlg 
27931b7ab755Sdlg 		delay(10);
27941b7ab755Sdlg 		bus_space_barrier(sc->rge_btag, sc->rge_bhandle,
27951b7ab755Sdlg 		    RGE_DTCCR_LO, 4, BUS_SPACE_BARRIER_READ);
27961b7ab755Sdlg 	} while (--tmo);
27971b7ab755Sdlg 
27981b7ab755Sdlg 	bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
27991b7ab755Sdlg 	    BUS_DMASYNC_POSTREAD);
28001b7ab755Sdlg 
28011b7ab755Sdlg 	if (ISSET(reg, RGE_DTCCR_CMD))
28021b7ab755Sdlg 		return (EIO);
28031b7ab755Sdlg 
28041b7ab755Sdlg 	nanouptime(&ks->ks_updated);
28051b7ab755Sdlg 
28061b7ab755Sdlg 	return (0);
28071b7ab755Sdlg }
28081b7ab755Sdlg 
28091b7ab755Sdlg static int
28101b7ab755Sdlg rge_kstat_copy(struct kstat *ks, void *dst)
28111b7ab755Sdlg {
28121b7ab755Sdlg 	struct rge_kstat_softc *rge_ks_sc = ks->ks_ptr;
28131b7ab755Sdlg 	struct rge_stats *rs = rge_ks_sc->rge_ks_sc_stats;
28141b7ab755Sdlg 	struct rge_kstats *kvs = dst;
28151b7ab755Sdlg 
28161b7ab755Sdlg 	*kvs = rge_kstats_tpl;
28171b7ab755Sdlg 	kstat_kv_u64(&kvs->tx_ok) = lemtoh64(&rs->rge_tx_ok);
28181b7ab755Sdlg 	kstat_kv_u64(&kvs->rx_ok) = lemtoh64(&rs->rge_rx_ok);
28191b7ab755Sdlg 	kstat_kv_u64(&kvs->tx_er) = lemtoh64(&rs->rge_tx_er);
28201b7ab755Sdlg 	kstat_kv_u32(&kvs->rx_er) = lemtoh32(&rs->rge_rx_er);
28211b7ab755Sdlg 	kstat_kv_u16(&kvs->miss_pkt) = lemtoh16(&rs->rge_miss_pkt);
28221b7ab755Sdlg 	kstat_kv_u16(&kvs->fae) = lemtoh16(&rs->rge_fae);
28231b7ab755Sdlg 	kstat_kv_u32(&kvs->tx_1col) = lemtoh32(&rs->rge_tx_1col);
28241b7ab755Sdlg 	kstat_kv_u32(&kvs->tx_mcol) = lemtoh32(&rs->rge_tx_mcol);
28251b7ab755Sdlg 	kstat_kv_u64(&kvs->rx_ok_phy) = lemtoh64(&rs->rge_rx_ok_phy);
28261b7ab755Sdlg 	kstat_kv_u64(&kvs->rx_ok_brd) = lemtoh64(&rs->rge_rx_ok_brd);
28271b7ab755Sdlg 	kstat_kv_u32(&kvs->rx_ok_mul) = lemtoh32(&rs->rge_rx_ok_mul);
28281b7ab755Sdlg 	kstat_kv_u16(&kvs->tx_abt) = lemtoh16(&rs->rge_tx_abt);
28291b7ab755Sdlg 	kstat_kv_u16(&kvs->tx_undrn) = lemtoh16(&rs->rge_tx_undrn);
28301b7ab755Sdlg 
28311b7ab755Sdlg 	return (0);
28321b7ab755Sdlg }
28331b7ab755Sdlg 
28341b7ab755Sdlg void
28351b7ab755Sdlg rge_kstat_attach(struct rge_softc *sc)
28361b7ab755Sdlg {
28371b7ab755Sdlg 	struct rge_kstat_softc *rge_ks_sc;
28381b7ab755Sdlg 	struct kstat *ks;
28391b7ab755Sdlg 
28401b7ab755Sdlg 	rge_ks_sc = malloc(sizeof(*rge_ks_sc), M_DEVBUF, M_NOWAIT);
28411b7ab755Sdlg 	if (rge_ks_sc == NULL) {
28421b7ab755Sdlg 		printf("%s: cannot allocate kstat softc\n",
28431b7ab755Sdlg 		    sc->sc_dev.dv_xname);
28441b7ab755Sdlg 		return;
28451b7ab755Sdlg 	}
28461b7ab755Sdlg 
28471b7ab755Sdlg 	if (bus_dmamap_create(sc->sc_dmat,
28481b7ab755Sdlg 	    sizeof(struct rge_stats), 1, sizeof(struct rge_stats), 0,
28491b7ab755Sdlg 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
28501b7ab755Sdlg 	    &rge_ks_sc->rge_ks_sc_map) != 0) {
28511b7ab755Sdlg 		printf("%s: cannot create counter dma memory map\n",
28521b7ab755Sdlg 		    sc->sc_dev.dv_xname);
28531b7ab755Sdlg 		goto free;
28541b7ab755Sdlg 	}
28551b7ab755Sdlg 
28561b7ab755Sdlg 	if (bus_dmamem_alloc(sc->sc_dmat,
28571b7ab755Sdlg 	    sizeof(struct rge_stats), RGE_STATS_ALIGNMENT, 0,
28581b7ab755Sdlg 	    &rge_ks_sc->rge_ks_sc_seg, 1, &rge_ks_sc->rge_ks_sc_nsegs,
28591b7ab755Sdlg 	    BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0) {
28601b7ab755Sdlg 		printf("%s: cannot allocate counter dma memory\n",
28611b7ab755Sdlg 		    sc->sc_dev.dv_xname);
28621b7ab755Sdlg 		goto destroy;
28631b7ab755Sdlg 	}
28641b7ab755Sdlg 
28651b7ab755Sdlg 	if (bus_dmamem_map(sc->sc_dmat,
28661b7ab755Sdlg 	    &rge_ks_sc->rge_ks_sc_seg, rge_ks_sc->rge_ks_sc_nsegs,
28671b7ab755Sdlg 	    sizeof(struct rge_stats), (caddr_t *)&rge_ks_sc->rge_ks_sc_stats,
28681b7ab755Sdlg 	    BUS_DMA_NOWAIT) != 0) {
28691b7ab755Sdlg 		printf("%s: cannot map counter dma memory\n",
28701b7ab755Sdlg 		    sc->sc_dev.dv_xname);
28711b7ab755Sdlg 		goto freedma;
28721b7ab755Sdlg 	}
28731b7ab755Sdlg 
28741b7ab755Sdlg 	if (bus_dmamap_load(sc->sc_dmat, rge_ks_sc->rge_ks_sc_map,
28751b7ab755Sdlg 	    (caddr_t)rge_ks_sc->rge_ks_sc_stats, sizeof(struct rge_stats),
28761b7ab755Sdlg 	    NULL, BUS_DMA_NOWAIT) != 0) {
28771b7ab755Sdlg 		printf("%s: cannot load counter dma memory\n",
28781b7ab755Sdlg 		    sc->sc_dev.dv_xname);
28791b7ab755Sdlg 		goto unmap;
28801b7ab755Sdlg 	}
28811b7ab755Sdlg 
28821b7ab755Sdlg 	ks = kstat_create(sc->sc_dev.dv_xname, 0, "re-stats", 0,
28831b7ab755Sdlg 	    KSTAT_T_KV, 0);
28841b7ab755Sdlg 	if (ks == NULL) {
28851b7ab755Sdlg 		printf("%s: cannot create re-stats kstat\n",
28861b7ab755Sdlg 		    sc->sc_dev.dv_xname);
28871b7ab755Sdlg 		goto unload;
28881b7ab755Sdlg 	}
28891b7ab755Sdlg 
28901b7ab755Sdlg 	ks->ks_datalen = sizeof(rge_kstats_tpl);
28911b7ab755Sdlg 
28921b7ab755Sdlg 	rw_init(&rge_ks_sc->rge_ks_sc_rwl, "rgestats");
28931b7ab755Sdlg 	kstat_set_wlock(ks, &rge_ks_sc->rge_ks_sc_rwl);
28941b7ab755Sdlg 	ks->ks_softc = sc;
28951b7ab755Sdlg 	ks->ks_ptr = rge_ks_sc;
28961b7ab755Sdlg 	ks->ks_read = rge_kstat_read;
28971b7ab755Sdlg 	ks->ks_copy = rge_kstat_copy;
28981b7ab755Sdlg 
28991b7ab755Sdlg 	kstat_install(ks);
29001b7ab755Sdlg 
29011b7ab755Sdlg 	sc->sc_kstat = ks;
29021b7ab755Sdlg 
29031b7ab755Sdlg 	return;
29041b7ab755Sdlg 
29051b7ab755Sdlg unload:
29061b7ab755Sdlg 	bus_dmamap_unload(sc->sc_dmat, rge_ks_sc->rge_ks_sc_map);
29071b7ab755Sdlg unmap:
29081b7ab755Sdlg 	bus_dmamem_unmap(sc->sc_dmat,
29091b7ab755Sdlg 	    (caddr_t)rge_ks_sc->rge_ks_sc_stats, sizeof(struct rge_stats));
29101b7ab755Sdlg freedma:
29111b7ab755Sdlg 	bus_dmamem_free(sc->sc_dmat, &rge_ks_sc->rge_ks_sc_seg, 1);
29121b7ab755Sdlg destroy:
29131b7ab755Sdlg 	bus_dmamap_destroy(sc->sc_dmat, rge_ks_sc->rge_ks_sc_map);
29141b7ab755Sdlg free:
29151b7ab755Sdlg 	free(rge_ks_sc, M_DEVBUF, sizeof(*rge_ks_sc));
29161b7ab755Sdlg }
29171b7ab755Sdlg #endif /* NKSTAT > 0 */
2918