180219Swpaul/*	$OpenBSD: if_txp.c,v 1.48 2001/06/27 06:34:50 kjc Exp $	*/
280219Swpaul
3139749Simp/*-
480219Swpaul * Copyright (c) 2001
580219Swpaul *	Jason L. Wright <jason@thought.net>, Theo de Raadt, and
680219Swpaul *	Aaron Campbell <aaron@monkey.org>.  All rights reserved.
780219Swpaul *
880219Swpaul * Redistribution and use in source and binary forms, with or without
980219Swpaul * modification, are permitted provided that the following conditions
1080219Swpaul * are met:
1180219Swpaul * 1. Redistributions of source code must retain the above copyright
1280219Swpaul *    notice, this list of conditions and the following disclaimer.
1380219Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1480219Swpaul *    notice, this list of conditions and the following disclaimer in the
1580219Swpaul *    documentation and/or other materials provided with the distribution.
1680219Swpaul * 3. All advertising materials mentioning features or use of this software
1780219Swpaul *    must display the following acknowledgement:
1880219Swpaul *	This product includes software developed by Jason L. Wright,
1980219Swpaul *	Theo de Raadt and Aaron Campbell.
2080219Swpaul * 4. Neither the name of the author nor the names of any co-contributors
2180219Swpaul *    may be used to endorse or promote products derived from this software
2280219Swpaul *    without specific prior written permission.
2380219Swpaul *
2480219Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
2580219Swpaul * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2680219Swpaul * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2780219Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2880219Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2980219Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3080219Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3180219Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3280219Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3380219Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3480219Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3580219Swpaul */
3680219Swpaul
37119418Sobrien#include <sys/cdefs.h>
38119418Sobrien__FBSDID("$FreeBSD: stable/11/sys/dev/txp/if_txp.c 347962 2019-05-18 20:43:13Z brooks $");
39119418Sobrien
4080219Swpaul/*
4180219Swpaul * Driver for 3c990 (Typhoon) Ethernet ASIC
4280219Swpaul */
4380219Swpaul#include <sys/param.h>
4480219Swpaul#include <sys/systm.h>
45189714Syongari#include <sys/bus.h>
46189714Syongari#include <sys/endian.h>
47189714Syongari#include <sys/kernel.h>
48189714Syongari#include <sys/lock.h>
49189714Syongari#include <sys/malloc.h>
5080219Swpaul#include <sys/mbuf.h>
51129879Sphk#include <sys/module.h>
52189714Syongari#include <sys/mutex.h>
53189714Syongari#include <sys/queue.h>
54189714Syongari#include <sys/rman.h>
5580219Swpaul#include <sys/socket.h>
56189714Syongari#include <sys/sockio.h>
57189714Syongari#include <sys/sysctl.h>
58189714Syongari#include <sys/taskqueue.h>
5980219Swpaul
60189714Syongari#include <net/bpf.h>
6180219Swpaul#include <net/if.h>
62257176Sglebius#include <net/if_var.h>
6380219Swpaul#include <net/if_arp.h>
6480219Swpaul#include <net/ethernet.h>
6580219Swpaul#include <net/if_dl.h>
66189714Syongari#include <net/if_media.h>
6780219Swpaul#include <net/if_types.h>
6883115Sbrooks#include <net/if_vlan_var.h>
6980219Swpaul
7080219Swpaul#include <netinet/in.h>
7180219Swpaul#include <netinet/in_systm.h>
7280219Swpaul#include <netinet/ip.h>
7380219Swpaul
74189714Syongari#include <dev/mii/mii.h>
7580219Swpaul
7680219Swpaul#include <dev/pci/pcireg.h>
7780219Swpaul#include <dev/pci/pcivar.h>
7880219Swpaul
79189714Syongari#include <machine/bus.h>
80189714Syongari#include <machine/in_cksum.h>
8180219Swpaul
8280219Swpaul#include <dev/txp/if_txpreg.h>
8380219Swpaul#include <dev/txp/3c990img.h>
8480219Swpaul
85189714SyongariMODULE_DEPEND(txp, pci, 1, 1, 1);
86189714SyongariMODULE_DEPEND(txp, ether, 1, 1, 1);
8780219Swpaul
8880219Swpaul/*
89189714Syongari * XXX Known Typhoon firmware issues.
90189714Syongari *
91189714Syongari * 1. It seems that firmware has Tx TCP/UDP checksum offloading bug.
92189714Syongari *    The firmware hangs when it's told to compute TCP/UDP checksum.
93189714Syongari *    I'm not sure whether the firmware requires special alignment to
94189714Syongari *    do checksum offloading but datasheet says nothing about that.
95189714Syongari * 2. Datasheet says nothing for maximum number of fragmented
96189714Syongari *    descriptors supported. Experimentation shows up to 16 fragment
97189714Syongari *    descriptors are supported in the firmware. For TSO case, upper
98189714Syongari *    stack can send 64KB sized IP datagram plus link header size(
99189714Syongari *    ethernet header + VLAN tag)  frame but controller can handle up
100189714Syongari *    to 64KB frame given that PAGE_SIZE is 4KB(i.e. 16 * PAGE_SIZE).
101189714Syongari *    Because frames that need TSO operation of hardware can be
102189714Syongari *    larger than 64KB I disabled TSO capability. TSO operation for
103189714Syongari *    less than or equal to 16 fragment descriptors works without
104189714Syongari *    problems, though.
105189714Syongari * 3. VLAN hardware tag stripping is always enabled in the firmware
106189714Syongari *    even if it's explicitly told to not strip the tag. It's
107189714Syongari *    possible to add the tag back in Rx handler if VLAN hardware
108189714Syongari *    tag is not active but I didn't try that as it would be
109189714Syongari *    layering violation.
110189714Syongari * 4. TXP_CMD_RECV_BUFFER_CONTROL does not work as expected in
111189714Syongari *    datasheet such that driver should handle the alignment
112189714Syongari *    restriction by copying received frame to align the frame on
113189714Syongari *    32bit boundary on strict-alignment architectures. This adds a
114189714Syongari *    lot of CPU burden and it effectively reduce Rx performance on
115268351Smarcel *    strict-alignment architectures(e.g. sparc64, arm and mips).
116189714Syongari *
117189714Syongari * Unfortunately it seems that 3Com have no longer interests in
118189714Syongari * releasing fixed firmware so we may have to live with these bugs.
119189714Syongari */
120189714Syongari
121189714Syongari#define	TXP_CSUM_FEATURES	(CSUM_IP)
122189714Syongari
123189714Syongari/*
12480219Swpaul * Various supported device vendors/types and their names.
12580219Swpaul */
12680219Swpaulstatic struct txp_type txp_devs[] = {
12780219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_95,
12880219Swpaul	    "3Com 3cR990-TX-95 Etherlink with 3XP Processor" },
12980219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_97,
13080219Swpaul	    "3Com 3cR990-TX-97 Etherlink with 3XP Processor" },
13180219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_TXM,
13280219Swpaul	    "3Com 3cR990B-TXM Etherlink with 3XP Processor" },
13380219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_95,
13480219Swpaul	    "3Com 3cR990-SRV-95 Etherlink Server with 3XP Processor" },
13580219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_97,
13680219Swpaul	    "3Com 3cR990-SRV-97 Etherlink Server with 3XP Processor" },
13780219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_SRV,
13880219Swpaul	    "3Com 3cR990B-SRV Etherlink Server with 3XP Processor" },
13980219Swpaul	{ 0, 0, NULL }
14080219Swpaul};
14180219Swpaul
142149678Sjhbstatic int txp_probe(device_t);
143149678Sjhbstatic int txp_attach(device_t);
144149678Sjhbstatic int txp_detach(device_t);
145189714Syongaristatic int txp_shutdown(device_t);
146189714Syongaristatic int txp_suspend(device_t);
147189714Syongaristatic int txp_resume(device_t);
148189714Syongaristatic int txp_intr(void *);
149189714Syongaristatic void txp_int_task(void *, int);
150149678Sjhbstatic void txp_tick(void *);
151149678Sjhbstatic int txp_ioctl(struct ifnet *, u_long, caddr_t);
152272067Sglebiusstatic uint64_t txp_get_counter(struct ifnet *, ift_counter);
153149678Sjhbstatic void txp_start(struct ifnet *);
154151772Sjhbstatic void txp_start_locked(struct ifnet *);
155189714Syongaristatic int txp_encap(struct txp_softc *, struct txp_tx_ring *, struct mbuf **);
156149678Sjhbstatic void txp_stop(struct txp_softc *);
157149678Sjhbstatic void txp_init(void *);
158151772Sjhbstatic void txp_init_locked(struct txp_softc *);
159189714Syongaristatic void txp_watchdog(struct txp_softc *);
16080219Swpaul
161189714Syongaristatic int txp_reset(struct txp_softc *);
162189714Syongaristatic int txp_boot(struct txp_softc *, uint32_t);
163189714Syongaristatic int txp_sleep(struct txp_softc *, int);
164189714Syongaristatic int txp_wait(struct txp_softc *, uint32_t);
16592739Salfredstatic int txp_download_fw(struct txp_softc *);
16692739Salfredstatic int txp_download_fw_wait(struct txp_softc *);
167149678Sjhbstatic int txp_download_fw_section(struct txp_softc *,
16892739Salfred    struct txp_fw_section_header *, int);
16992739Salfredstatic int txp_alloc_rings(struct txp_softc *);
170189714Syongaristatic void txp_init_rings(struct txp_softc *);
171189714Syongaristatic int txp_dma_alloc(struct txp_softc *, char *, bus_dma_tag_t *,
172189714Syongari    bus_size_t, bus_size_t, bus_dmamap_t *, void **, bus_size_t, bus_addr_t *);
173267580Sjhbstatic void txp_dma_free(struct txp_softc *, bus_dma_tag_t *, bus_dmamap_t,
174267580Sjhb    void **, bus_addr_t *);
175189714Syongaristatic void txp_free_rings(struct txp_softc *);
17692739Salfredstatic int txp_rxring_fill(struct txp_softc *);
17792739Salfredstatic void txp_rxring_empty(struct txp_softc *);
17892739Salfredstatic void txp_set_filter(struct txp_softc *);
17980219Swpaul
18092739Salfredstatic int txp_cmd_desc_numfree(struct txp_softc *);
181189689Syongaristatic int txp_command(struct txp_softc *, uint16_t, uint16_t, uint32_t,
182189689Syongari    uint32_t, uint16_t *, uint32_t *, uint32_t *, int);
183189714Syongaristatic int txp_ext_command(struct txp_softc *, uint16_t, uint16_t,
184189689Syongari    uint32_t, uint32_t, struct txp_ext_desc *, uint8_t,
18592739Salfred    struct txp_rsp_desc **, int);
186189714Syongaristatic int txp_response(struct txp_softc *, uint16_t, uint16_t,
18792739Salfred    struct txp_rsp_desc **);
188149678Sjhbstatic void txp_rsp_fixup(struct txp_softc *, struct txp_rsp_desc *,
18992739Salfred    struct txp_rsp_desc *);
190189714Syongaristatic int txp_set_capabilities(struct txp_softc *);
19180219Swpaul
19292739Salfredstatic void txp_ifmedia_sts(struct ifnet *, struct ifmediareq *);
19392739Salfredstatic int txp_ifmedia_upd(struct ifnet *);
19480219Swpaul#ifdef TXP_DEBUG
19592739Salfredstatic void txp_show_descriptor(void *);
19680219Swpaul#endif
19792739Salfredstatic void txp_tx_reclaim(struct txp_softc *, struct txp_tx_ring *);
19892739Salfredstatic void txp_rxbuf_reclaim(struct txp_softc *);
199189714Syongari#ifndef __NO_STRICT_ALIGNMENT
200189714Syongaristatic __inline void txp_fixup_rx(struct mbuf *);
20180219Swpaul#endif
202189714Syongaristatic int txp_rx_reclaim(struct txp_softc *, struct txp_rx_ring *, int);
203189714Syongaristatic void txp_stats_save(struct txp_softc *);
204189714Syongaristatic void txp_stats_update(struct txp_softc *, struct txp_rsp_desc *);
205189714Syongaristatic void txp_sysctl_node(struct txp_softc *);
206189714Syongaristatic int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int);
207189714Syongaristatic int sysctl_hw_txp_proc_limit(SYSCTL_HANDLER_ARGS);
20880219Swpaul
209189714Syongaristatic int prefer_iomap = 0;
210189714SyongariTUNABLE_INT("hw.txp.prefer_iomap", &prefer_iomap);
211189714Syongari
21280219Swpaulstatic device_method_t txp_methods[] = {
21380219Swpaul        /* Device interface */
21480219Swpaul	DEVMETHOD(device_probe,		txp_probe),
21580219Swpaul	DEVMETHOD(device_attach,	txp_attach),
21680219Swpaul	DEVMETHOD(device_detach,	txp_detach),
21780219Swpaul	DEVMETHOD(device_shutdown,	txp_shutdown),
218189714Syongari	DEVMETHOD(device_suspend,	txp_suspend),
219189714Syongari	DEVMETHOD(device_resume,	txp_resume),
220189714Syongari
221189714Syongari	{ NULL, NULL }
22280219Swpaul};
22380219Swpaul
22480219Swpaulstatic driver_t txp_driver = {
22580219Swpaul	"txp",
22680219Swpaul	txp_methods,
22780219Swpaul	sizeof(struct txp_softc)
22880219Swpaul};
22980219Swpaul
23080219Swpaulstatic devclass_t txp_devclass;
23180219Swpaul
232113506SmdoddDRIVER_MODULE(txp, pci, txp_driver, txp_devclass, 0, 0);
23380219Swpaul
23480219Swpaulstatic int
235189685Syongaritxp_probe(device_t dev)
23680219Swpaul{
23780219Swpaul	struct txp_type *t;
23880219Swpaul
23980219Swpaul	t = txp_devs;
24080219Swpaul
241189688Syongari	while (t->txp_name != NULL) {
24280219Swpaul		if ((pci_get_vendor(dev) == t->txp_vid) &&
24380219Swpaul		    (pci_get_device(dev) == t->txp_did)) {
24480219Swpaul			device_set_desc(dev, t->txp_name);
245189688Syongari			return (BUS_PROBE_DEFAULT);
24680219Swpaul		}
24780219Swpaul		t++;
24880219Swpaul	}
24980219Swpaul
250189688Syongari	return (ENXIO);
25180219Swpaul}
25280219Swpaul
25380219Swpaulstatic int
254189685Syongaritxp_attach(device_t dev)
25580219Swpaul{
25680219Swpaul	struct txp_softc *sc;
25780219Swpaul	struct ifnet *ifp;
258189714Syongari	struct txp_rsp_desc *rsp;
259189689Syongari	uint16_t p1;
260189714Syongari	uint32_t p2, reg;
261189714Syongari	int error = 0, pmc, rid;
262189714Syongari	uint8_t eaddr[ETHER_ADDR_LEN], *ver;
26380219Swpaul
26480219Swpaul	sc = device_get_softc(dev);
26580219Swpaul	sc->sc_dev = dev;
26680219Swpaul
26793818Sjhb	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
268151772Sjhb	    MTX_DEF);
269151772Sjhb	callout_init_mtx(&sc->sc_tick, &sc->sc_mtx, 0);
270189714Syongari	TASK_INIT(&sc->sc_int_task, 0, txp_int_task, sc);
271189714Syongari	TAILQ_INIT(&sc->sc_busy_list);
272189714Syongari	TAILQ_INIT(&sc->sc_free_list);
273151772Sjhb
274189714Syongari	ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts);
275189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0, NULL);
276189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_HDX, 0, NULL);
277189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
278189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
279189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_HDX, 0, NULL);
280189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
281189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
282189714Syongari
28380219Swpaul	pci_enable_busmaster(dev);
284189714Syongari	/* Prefer memory space register mapping over IO space. */
285189714Syongari	if (prefer_iomap == 0) {
286189714Syongari		sc->sc_res_id = PCIR_BAR(1);
287189714Syongari		sc->sc_res_type = SYS_RES_MEMORY;
288189714Syongari	} else {
289189714Syongari		sc->sc_res_id = PCIR_BAR(0);
290189714Syongari		sc->sc_res_type = SYS_RES_IOPORT;
291189714Syongari	}
292189714Syongari	sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type,
293189714Syongari	    &sc->sc_res_id, RF_ACTIVE);
294189714Syongari	if (sc->sc_res == NULL && prefer_iomap == 0) {
295189714Syongari		sc->sc_res_id = PCIR_BAR(0);
296189714Syongari		sc->sc_res_type = SYS_RES_IOPORT;
297189714Syongari		sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type,
298189714Syongari		    &sc->sc_res_id, RF_ACTIVE);
299189714Syongari	}
30080219Swpaul	if (sc->sc_res == NULL) {
30180219Swpaul		device_printf(dev, "couldn't map ports/memory\n");
302189714Syongari		ifmedia_removeall(&sc->sc_ifmedia);
303189714Syongari		mtx_destroy(&sc->sc_mtx);
304189714Syongari		return (ENXIO);
30580219Swpaul	}
30680219Swpaul
307189714Syongari	/* Enable MWI. */
308189714Syongari	reg = pci_read_config(dev, PCIR_COMMAND, 2);
309189714Syongari	reg |= PCIM_CMD_MWRICEN;
310189714Syongari	pci_write_config(dev, PCIR_COMMAND, reg, 2);
311189714Syongari	/* Check cache line size. */
312189714Syongari	reg = pci_read_config(dev, PCIR_CACHELNSZ, 1);
313189714Syongari	reg <<= 4;
314189714Syongari	if (reg == 0 || (reg % 16) != 0)
315189714Syongari		device_printf(sc->sc_dev,
316189714Syongari		    "invalid cache line size : %u\n", reg);
31780219Swpaul
31880219Swpaul	/* Allocate interrupt */
31980219Swpaul	rid = 0;
320127135Snjl	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
32180219Swpaul	    RF_SHAREABLE | RF_ACTIVE);
32280219Swpaul
32380219Swpaul	if (sc->sc_irq == NULL) {
32480219Swpaul		device_printf(dev, "couldn't map interrupt\n");
32580219Swpaul		error = ENXIO;
32680219Swpaul		goto fail;
32780219Swpaul	}
32880219Swpaul
329189714Syongari	if ((error = txp_alloc_rings(sc)) != 0)
33080219Swpaul		goto fail;
331189714Syongari	txp_init_rings(sc);
332189714Syongari	txp_sysctl_node(sc);
333189714Syongari	/* Reset controller and make it reload sleep image. */
334189714Syongari	if (txp_reset(sc) != 0) {
335170596Syongari		error = ENXIO;
336170596Syongari		goto fail;
337170596Syongari	}
33880219Swpaul
339189714Syongari	/* Let controller boot from sleep image. */
340189714Syongari	if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) {
341189714Syongari		device_printf(sc->sc_dev, "could not boot sleep image\n");
342170596Syongari		error = ENXIO;
343170596Syongari		goto fail;
344170596Syongari	}
34580219Swpaul
346189714Syongari	/* Get station address. */
34780219Swpaul	if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0,
348189714Syongari	    &p1, &p2, NULL, TXP_CMD_WAIT)) {
349149678Sjhb		error = ENXIO;
35080219Swpaul		goto fail;
35180219Swpaul	}
35280219Swpaul
353189714Syongari	p1 = le16toh(p1);
354189689Syongari	eaddr[0] = ((uint8_t *)&p1)[1];
355189689Syongari	eaddr[1] = ((uint8_t *)&p1)[0];
356189714Syongari	p2 = le32toh(p2);
357189689Syongari	eaddr[2] = ((uint8_t *)&p2)[3];
358189689Syongari	eaddr[3] = ((uint8_t *)&p2)[2];
359189689Syongari	eaddr[4] = ((uint8_t *)&p2)[1];
360189689Syongari	eaddr[5] = ((uint8_t *)&p2)[0];
36180219Swpaul
362189714Syongari	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
363189714Syongari	if (ifp == NULL) {
364189714Syongari		device_printf(dev, "can not allocate ifnet structure\n");
365189714Syongari		error = ENOSPC;
366189714Syongari		goto fail;
367189714Syongari	}
36880219Swpaul
369189714Syongari	/*
370189714Syongari	 * Show sleep image version information which may help to
371189714Syongari	 * diagnose sleep image specific issues.
372189714Syongari	 */
373189714Syongari	rsp = NULL;
374327589Spfg	if (txp_ext_command(sc, TXP_CMD_VERSIONS_READ, 0, 0, 0, NULL, 0,
375189714Syongari	    &rsp, TXP_CMD_WAIT)) {
376189714Syongari		device_printf(dev, "can not read sleep image version\n");
377189714Syongari		error = ENXIO;
378189714Syongari		goto fail;
379189714Syongari	}
380189714Syongari	if (rsp->rsp_numdesc == 0) {
381189714Syongari		p2 = le32toh(rsp->rsp_par2) & 0xFFFF;
382189714Syongari		device_printf(dev, "Typhoon 1.0 sleep image (2000/%02u/%02u)\n",
383189714Syongari		    p2 >> 8, p2 & 0xFF);
384189714Syongari	} else if (rsp->rsp_numdesc == 2) {
385189714Syongari		p2 = le32toh(rsp->rsp_par2);
386189714Syongari		ver = (uint8_t *)(rsp + 1);
387189714Syongari		/*
388189714Syongari		 * Even if datasheet says the command returns a NULL
389189714Syongari		 * terminated version string, explicitly terminate
390189714Syongari		 * the string. Given that several bugs of firmware
391189714Syongari		 * I can't trust this simple one.
392189714Syongari		 */
393189714Syongari		ver[25] = '\0';
394189714Syongari		device_printf(dev,
395189714Syongari		    "Typhoon 1.1+ sleep image %02u.%03u.%03u %s\n",
396189714Syongari		    p2 >> 24, (p2 >> 12) & 0xFFF, p2 & 0xFFF, ver);
397189714Syongari	} else {
398189714Syongari		p2 = le32toh(rsp->rsp_par2);
399189714Syongari		device_printf(dev,
400189714Syongari		    "Unknown Typhoon sleep image version: %u:0x%08x\n",
401189714Syongari		    rsp->rsp_numdesc, p2);
402189714Syongari	}
403189714Syongari	if (rsp != NULL)
404189714Syongari		free(rsp, M_DEVBUF);
40580219Swpaul
40680219Swpaul	sc->sc_xcvr = TXP_XCVR_AUTO;
40780219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0,
408189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT);
409189714Syongari	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
41080219Swpaul
41180219Swpaul	ifp->if_softc = sc;
412121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
413151772Sjhb	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
41480219Swpaul	ifp->if_ioctl = txp_ioctl;
41580219Swpaul	ifp->if_start = txp_start;
41680219Swpaul	ifp->if_init = txp_init;
417272067Sglebius	ifp->if_get_counter = txp_get_counter;
418189714Syongari	ifp->if_snd.ifq_drv_maxlen = TX_ENTRIES - 1;
419189714Syongari	IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
420189714Syongari	IFQ_SET_READY(&ifp->if_snd);
42180219Swpaul	/*
422189714Syongari	 * It's possible to read firmware's offload capability but
423189714Syongari	 * we have not downloaded the firmware yet so announce
424189714Syongari	 * working capability here. We're not interested in IPSec
425189714Syongari	 * capability and due to the lots of firmware bug we can't
426189714Syongari	 * advertise the whole capability anyway.
42780219Swpaul	 */
428189714Syongari	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM;
429219902Sjhb	if (pci_find_cap(dev, PCIY_PMG, &pmc) == 0)
430189714Syongari		ifp->if_capabilities |= IFCAP_WOL_MAGIC;
431189714Syongari	/* Enable all capabilities. */
432189714Syongari	ifp->if_capenable = ifp->if_capabilities;
433189714Syongari
434147256Sbrooks	ether_ifattach(ifp, eaddr);
435149678Sjhb
436189714Syongari	/* VLAN capability setup. */
437189714Syongari	ifp->if_capabilities |= IFCAP_VLAN_MTU;
438189714Syongari	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
439189714Syongari	ifp->if_capenable = ifp->if_capabilities;
440189714Syongari	/* Tell the upper layer(s) we support long frames. */
441270856Sglebius	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
442189714Syongari
443189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
444189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
445189714Syongari
446189714Syongari	/* Create local taskq. */
447189714Syongari	sc->sc_tq = taskqueue_create_fast("txp_taskq", M_WAITOK,
448189714Syongari	    taskqueue_thread_enqueue, &sc->sc_tq);
449189714Syongari	if (sc->sc_tq == NULL) {
450189714Syongari		device_printf(dev, "could not create taskqueue.\n");
451189714Syongari		ether_ifdetach(ifp);
452189714Syongari		error = ENXIO;
453189714Syongari		goto fail;
454189714Syongari	}
455189714Syongari	taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
456189714Syongari	    device_get_nameunit(sc->sc_dev));
457189714Syongari
458189714Syongari	/* Put controller into sleep. */
459189714Syongari	if (txp_sleep(sc, 0) != 0) {
460189714Syongari		ether_ifdetach(ifp);
461189714Syongari		error = ENXIO;
462189714Syongari		goto fail;
463189714Syongari	}
464189714Syongari
465151772Sjhb	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
466189714Syongari	    txp_intr, NULL, sc, &sc->sc_intrhand);
467149678Sjhb
468189714Syongari	if (error != 0) {
469149678Sjhb		ether_ifdetach(ifp);
470189714Syongari		device_printf(dev, "couldn't set up interrupt handler.\n");
471149678Sjhb		goto fail;
472149678Sjhb	}
473149678Sjhb
474347962Sbrooks	gone_by_fcp101_dev(dev);
475347962Sbrooks
476189688Syongari	return (0);
47780219Swpaul
47880219Swpaulfail:
479189714Syongari	if (error != 0)
480189714Syongari		txp_detach(dev);
481189688Syongari	return (error);
48280219Swpaul}
48380219Swpaul
48480219Swpaulstatic int
485189685Syongaritxp_detach(device_t dev)
48680219Swpaul{
48780219Swpaul	struct txp_softc *sc;
48880219Swpaul	struct ifnet *ifp;
48980219Swpaul
49080219Swpaul	sc = device_get_softc(dev);
491189714Syongari
492147256Sbrooks	ifp = sc->sc_ifp;
493189714Syongari	if (device_is_attached(dev)) {
494189714Syongari		TXP_LOCK(sc);
495189714Syongari		sc->sc_flags |= TXP_FLAG_DETACH;
496189714Syongari		txp_stop(sc);
497189714Syongari		TXP_UNLOCK(sc);
498189714Syongari		callout_drain(&sc->sc_tick);
499189714Syongari		taskqueue_drain(sc->sc_tq, &sc->sc_int_task);
500189714Syongari		ether_ifdetach(ifp);
501189714Syongari	}
502189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
50380219Swpaul
50480219Swpaul	ifmedia_removeall(&sc->sc_ifmedia);
50580219Swpaul	if (sc->sc_intrhand != NULL)
50680219Swpaul		bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand);
50780219Swpaul	if (sc->sc_irq != NULL)
50880219Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
50980219Swpaul	if (sc->sc_res != NULL)
510189714Syongari		bus_release_resource(dev, sc->sc_res_type, sc->sc_res_id,
511189714Syongari		    sc->sc_res);
512189714Syongari	if (sc->sc_ifp != NULL) {
513150306Simp		if_free(sc->sc_ifp);
514189714Syongari		sc->sc_ifp = NULL;
515189714Syongari	}
516189714Syongari	txp_free_rings(sc);
517189714Syongari	mtx_destroy(&sc->sc_mtx);
51880219Swpaul
51980219Swpaul	return (0);
52080219Swpaul}
52180219Swpaul
52280219Swpaulstatic int
523189714Syongaritxp_reset(struct txp_softc *sc)
52480219Swpaul{
525189689Syongari	uint32_t r;
52680219Swpaul	int i;
52780219Swpaul
528189714Syongari	/* Disable interrupts. */
529189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
530189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
531189714Syongari	/* Ack all pending interrupts. */
532189714Syongari	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
533189714Syongari
53492643Sjeff	r = 0;
53580219Swpaul	WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL);
53680219Swpaul	DELAY(1000);
53780219Swpaul	WRITE_REG(sc, TXP_SRR, 0);
53880219Swpaul
539189714Syongari	/* Should wait max 6 seconds. */
54080219Swpaul	for (i = 0; i < 6000; i++) {
54180219Swpaul		r = READ_REG(sc, TXP_A2H_0);
54280219Swpaul		if (r == STAT_WAITING_FOR_HOST_REQUEST)
54380219Swpaul			break;
54480219Swpaul		DELAY(1000);
54580219Swpaul	}
54680219Swpaul
547189714Syongari	if (r != STAT_WAITING_FOR_HOST_REQUEST)
54880219Swpaul		device_printf(sc->sc_dev, "reset hung\n");
549189714Syongari
550189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
551189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
552189714Syongari	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
553189714Syongari
554189714Syongari	/*
555189714Syongari	 * Give more time to complete loading sleep image before
556189714Syongari	 * trying to boot from sleep image.
557189714Syongari	 */
558189714Syongari	DELAY(5000);
559189714Syongari
560189714Syongari	return (0);
561189714Syongari}
562189714Syongari
563189714Syongaristatic int
564189714Syongaritxp_boot(struct txp_softc *sc, uint32_t state)
565189714Syongari{
566189714Syongari
567189714Syongari	/* See if it's waiting for boot, and try to boot it. */
568189714Syongari	if (txp_wait(sc, state) != 0) {
569189714Syongari		device_printf(sc->sc_dev, "not waiting for boot\n");
570189714Syongari		return (ENXIO);
57180219Swpaul	}
57280219Swpaul
573189714Syongari	WRITE_REG(sc, TXP_H2A_2, TXP_ADDR_HI(sc->sc_ldata.txp_boot_paddr));
574189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
575189714Syongari	WRITE_REG(sc, TXP_H2A_1, TXP_ADDR_LO(sc->sc_ldata.txp_boot_paddr));
576189714Syongari	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
577189714Syongari	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD);
578189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
579189714Syongari
580189714Syongari	/* See if it booted. */
581189714Syongari	if (txp_wait(sc, STAT_RUNNING) != 0) {
582189714Syongari		device_printf(sc->sc_dev, "firmware not running\n");
583189714Syongari		return (ENXIO);
584189714Syongari	}
585189714Syongari
586189714Syongari	/* Clear TX and CMD ring write registers. */
587189714Syongari	WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL);
588189714Syongari	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
589189714Syongari	WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL);
590189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
591189714Syongari	WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL);
592189714Syongari	TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE);
593189714Syongari	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL);
594189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
595189714Syongari
59680219Swpaul	return (0);
59780219Swpaul}
59880219Swpaul
59980219Swpaulstatic int
600189685Syongaritxp_download_fw(struct txp_softc *sc)
60180219Swpaul{
60280219Swpaul	struct txp_fw_file_header *fileheader;
60380219Swpaul	struct txp_fw_section_header *secthead;
604189714Syongari	int sect;
605189714Syongari	uint32_t error, ier, imr;
60680219Swpaul
607189714Syongari	TXP_LOCK_ASSERT(sc);
608189714Syongari
609189022Syongari	error = 0;
61080219Swpaul	ier = READ_REG(sc, TXP_IER);
61180219Swpaul	WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0);
61280219Swpaul
61380219Swpaul	imr = READ_REG(sc, TXP_IMR);
61480219Swpaul	WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0);
61580219Swpaul
616189714Syongari	if (txp_wait(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) {
61780219Swpaul		device_printf(sc->sc_dev, "not waiting for host request\n");
618189714Syongari		error = ETIMEDOUT;
619189022Syongari		goto fail;
62080219Swpaul	}
62180219Swpaul
622189714Syongari	/* Ack the status. */
62380219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
62480219Swpaul
62580219Swpaul	fileheader = (struct txp_fw_file_header *)tc990image;
62680219Swpaul	if (bcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) {
627189714Syongari		device_printf(sc->sc_dev, "firmware invalid magic\n");
628189022Syongari		goto fail;
62980219Swpaul	}
63080219Swpaul
631189714Syongari	/* Tell boot firmware to get ready for image. */
632189714Syongari	WRITE_REG(sc, TXP_H2A_1, le32toh(fileheader->addr));
633189714Syongari	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
634189714Syongari	WRITE_REG(sc, TXP_H2A_2, le32toh(fileheader->hmac[0]));
635189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
636189714Syongari	WRITE_REG(sc, TXP_H2A_3, le32toh(fileheader->hmac[1]));
637189714Syongari	TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE);
638189714Syongari	WRITE_REG(sc, TXP_H2A_4, le32toh(fileheader->hmac[2]));
639189714Syongari	TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE);
640189714Syongari	WRITE_REG(sc, TXP_H2A_5, le32toh(fileheader->hmac[3]));
641189714Syongari	TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE);
642189714Syongari	WRITE_REG(sc, TXP_H2A_6, le32toh(fileheader->hmac[4]));
643189714Syongari	TXP_BARRIER(sc, TXP_H2A_6, 4, BUS_SPACE_BARRIER_WRITE);
64480219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE);
645189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
64680219Swpaul
64780219Swpaul	if (txp_download_fw_wait(sc)) {
648189714Syongari		device_printf(sc->sc_dev, "firmware wait failed, initial\n");
649189714Syongari		error = ETIMEDOUT;
650189022Syongari		goto fail;
65180219Swpaul	}
65280219Swpaul
653189689Syongari	secthead = (struct txp_fw_section_header *)(((uint8_t *)tc990image) +
65480219Swpaul	    sizeof(struct txp_fw_file_header));
65580219Swpaul
656189714Syongari	for (sect = 0; sect < le32toh(fileheader->nsections); sect++) {
657189714Syongari		if ((error = txp_download_fw_section(sc, secthead, sect)) != 0)
658189022Syongari			goto fail;
65980219Swpaul		secthead = (struct txp_fw_section_header *)
660189714Syongari		    (((uint8_t *)secthead) + le32toh(secthead->nbytes) +
66180219Swpaul		    sizeof(*secthead));
66280219Swpaul	}
66380219Swpaul
66480219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE);
665189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
66680219Swpaul
667189714Syongari	if (txp_wait(sc, STAT_WAITING_FOR_BOOT) != 0) {
66880219Swpaul		device_printf(sc->sc_dev, "not waiting for boot\n");
669189714Syongari		error = ETIMEDOUT;
670189022Syongari		goto fail;
67180219Swpaul	}
67280219Swpaul
673189022Syongarifail:
67480219Swpaul	WRITE_REG(sc, TXP_IER, ier);
67580219Swpaul	WRITE_REG(sc, TXP_IMR, imr);
67680219Swpaul
677189022Syongari	return (error);
67880219Swpaul}
67980219Swpaul
68080219Swpaulstatic int
681189685Syongaritxp_download_fw_wait(struct txp_softc *sc)
68280219Swpaul{
683189714Syongari	uint32_t i;
68480219Swpaul
685189714Syongari	TXP_LOCK_ASSERT(sc);
686189714Syongari
687189714Syongari	for (i = 0; i < TXP_TIMEOUT; i++) {
688189714Syongari		if ((READ_REG(sc, TXP_ISR) & TXP_INT_A2H_0) != 0)
68980219Swpaul			break;
69080219Swpaul		DELAY(50);
69180219Swpaul	}
69280219Swpaul
693189714Syongari	if (i == TXP_TIMEOUT) {
694189714Syongari		device_printf(sc->sc_dev, "firmware wait failed comm0\n");
695189714Syongari		return (ETIMEDOUT);
69680219Swpaul	}
69780219Swpaul
69880219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
69980219Swpaul
700189714Syongari	if (READ_REG(sc, TXP_A2H_0) != STAT_WAITING_FOR_SEGMENT) {
701189714Syongari		device_printf(sc->sc_dev, "firmware not waiting for segment\n");
702189714Syongari		return (ETIMEDOUT);
70380219Swpaul	}
70480219Swpaul	return (0);
70580219Swpaul}
70680219Swpaul
70780219Swpaulstatic int
708189685Syongaritxp_download_fw_section(struct txp_softc *sc,
709189685Syongari    struct txp_fw_section_header *sect, int sectnum)
71080219Swpaul{
711189714Syongari	bus_dma_tag_t sec_tag;
712189714Syongari	bus_dmamap_t sec_map;
713189714Syongari	bus_addr_t sec_paddr;
714189714Syongari	uint8_t *sec_buf;
71580219Swpaul	int rseg, err = 0;
71680219Swpaul	struct mbuf m;
717189689Syongari	uint16_t csum;
71880219Swpaul
719189714Syongari	TXP_LOCK_ASSERT(sc);
720189714Syongari
721189714Syongari	/* Skip zero length sections. */
722189714Syongari	if (le32toh(sect->nbytes) == 0)
72380219Swpaul		return (0);
72480219Swpaul
725189714Syongari	/* Make sure we aren't past the end of the image. */
726189689Syongari	rseg = ((uint8_t *)sect) - ((uint8_t *)tc990image);
72780219Swpaul	if (rseg >= sizeof(tc990image)) {
728189714Syongari		device_printf(sc->sc_dev,
729189714Syongari		    "firmware invalid section address, section %d\n", sectnum);
730189714Syongari		return (EIO);
73180219Swpaul	}
73280219Swpaul
733189714Syongari	/* Make sure this section doesn't go past the end. */
734189714Syongari	rseg += le32toh(sect->nbytes);
73580219Swpaul	if (rseg >= sizeof(tc990image)) {
736189714Syongari		device_printf(sc->sc_dev, "firmware truncated section %d\n",
73780219Swpaul		    sectnum);
738189714Syongari		return (EIO);
73980219Swpaul	}
74080219Swpaul
741189714Syongari	sec_tag = NULL;
742189714Syongari	sec_map = NULL;
743189714Syongari	sec_buf = NULL;
744189714Syongari	/* XXX */
745189714Syongari	TXP_UNLOCK(sc);
746189714Syongari	err = txp_dma_alloc(sc, "firmware sections", &sec_tag, sizeof(uint32_t),
747189714Syongari	    0, &sec_map, (void **)&sec_buf, le32toh(sect->nbytes), &sec_paddr);
748189714Syongari	TXP_LOCK(sc);
749189714Syongari	if (err != 0)
750189714Syongari		goto bail;
751189714Syongari	bcopy(((uint8_t *)sect) + sizeof(*sect), sec_buf,
752189714Syongari	    le32toh(sect->nbytes));
75380219Swpaul
75480219Swpaul	/*
75580219Swpaul	 * dummy up mbuf and verify section checksum
75680219Swpaul	 */
75780219Swpaul	m.m_type = MT_DATA;
75880219Swpaul	m.m_next = m.m_nextpkt = NULL;
759189714Syongari	m.m_len = le32toh(sect->nbytes);
760189714Syongari	m.m_data = sec_buf;
76180219Swpaul	m.m_flags = 0;
762189714Syongari	csum = in_cksum(&m, le32toh(sect->nbytes));
76380219Swpaul	if (csum != sect->cksum) {
764189714Syongari		device_printf(sc->sc_dev,
765189714Syongari		    "firmware section %d, bad cksum (expected 0x%x got 0x%x)\n",
766189714Syongari		    sectnum, le16toh(sect->cksum), csum);
767189714Syongari		err = EIO;
76880219Swpaul		goto bail;
76980219Swpaul	}
77080219Swpaul
771189714Syongari	bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_PREWRITE);
772189714Syongari
773189714Syongari	WRITE_REG(sc, TXP_H2A_1, le32toh(sect->nbytes));
774189714Syongari	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
775189714Syongari	WRITE_REG(sc, TXP_H2A_2, le16toh(sect->cksum));
776189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
777189714Syongari	WRITE_REG(sc, TXP_H2A_3, le32toh(sect->addr));
778189714Syongari	TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE);
779189714Syongari	WRITE_REG(sc, TXP_H2A_4, TXP_ADDR_HI(sec_paddr));
780189714Syongari	TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE);
781189714Syongari	WRITE_REG(sc, TXP_H2A_5, TXP_ADDR_LO(sec_paddr));
782189714Syongari	TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE);
78380219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE);
784189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
78580219Swpaul
78680219Swpaul	if (txp_download_fw_wait(sc)) {
787189714Syongari		device_printf(sc->sc_dev,
788189714Syongari		    "firmware wait failed, section %d\n", sectnum);
789189714Syongari		err = ETIMEDOUT;
79080219Swpaul	}
79180219Swpaul
792189714Syongari	bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_POSTWRITE);
79380219Swpaulbail:
794267580Sjhb	txp_dma_free(sc, &sec_tag, sec_map, (void **)&sec_buf, &sec_paddr);
79580219Swpaul	return (err);
79680219Swpaul}
79780219Swpaul
798189714Syongaristatic int
799189685Syongaritxp_intr(void *vsc)
80080219Swpaul{
801189714Syongari	struct txp_softc *sc;
802189714Syongari	uint32_t status;
803189714Syongari
804189714Syongari	sc = vsc;
805189714Syongari	status = READ_REG(sc, TXP_ISR);
806189714Syongari	if ((status & TXP_INT_LATCH) == 0)
807189714Syongari		return (FILTER_STRAY);
808189714Syongari	WRITE_REG(sc, TXP_ISR, status);
809189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
810189714Syongari	taskqueue_enqueue(sc->sc_tq, &sc->sc_int_task);
811189714Syongari
812189714Syongari	return (FILTER_HANDLED);
813189714Syongari}
814189714Syongari
815189714Syongaristatic void
816189714Syongaritxp_int_task(void *arg, int pending)
817189714Syongari{
818189714Syongari	struct txp_softc *sc;
819189714Syongari	struct ifnet *ifp;
820189714Syongari	struct txp_hostvar *hv;
821189689Syongari	uint32_t isr;
822189714Syongari	int more;
82380219Swpaul
824189714Syongari	sc = (struct txp_softc *)arg;
825189714Syongari
826151772Sjhb	TXP_LOCK(sc);
827189714Syongari	ifp = sc->sc_ifp;
828189714Syongari	hv = sc->sc_hostvar;
82980219Swpaul	isr = READ_REG(sc, TXP_ISR);
830189714Syongari	if ((isr & TXP_INT_LATCH) != 0)
83180219Swpaul		WRITE_REG(sc, TXP_ISR, isr);
83280219Swpaul
833189714Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
834189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
835189714Syongari		    sc->sc_cdata.txp_hostvar_map,
836189714Syongari		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
837189714Syongari		more = 0;
83880219Swpaul		if ((*sc->sc_rxhir.r_roff) != (*sc->sc_rxhir.r_woff))
839189714Syongari			more += txp_rx_reclaim(sc, &sc->sc_rxhir,
840189714Syongari			    sc->sc_process_limit);
84180219Swpaul		if ((*sc->sc_rxlor.r_roff) != (*sc->sc_rxlor.r_woff))
842189714Syongari			more += txp_rx_reclaim(sc, &sc->sc_rxlor,
843189714Syongari			    sc->sc_process_limit);
844189714Syongari		/*
845189714Syongari		 * XXX
846189714Syongari		 * It seems controller is not smart enough to handle
847189714Syongari		 * FIFO overflow conditions under heavy network load.
848189714Syongari		 * No matter how often new Rx buffers are passed to
849189714Syongari		 * controller the situation didn't change. Maybe
850189714Syongari		 * flow-control would be the only way to mitigate the
851189714Syongari		 * issue but firmware does not have commands that
852189714Syongari		 * control the threshold of emitting pause frames.
853189714Syongari		 */
85480219Swpaul		if (hv->hv_rx_buf_write_idx == hv->hv_rx_buf_read_idx)
85580219Swpaul			txp_rxbuf_reclaim(sc);
85680219Swpaul		if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons !=
857189714Syongari		    TXP_OFFSET2IDX(le32toh(*(sc->sc_txhir.r_off)))))
85880219Swpaul			txp_tx_reclaim(sc, &sc->sc_txhir);
85980219Swpaul		if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons !=
860189714Syongari		    TXP_OFFSET2IDX(le32toh(*(sc->sc_txlor.r_off)))))
86180219Swpaul			txp_tx_reclaim(sc, &sc->sc_txlor);
862189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
863189714Syongari		    sc->sc_cdata.txp_hostvar_map,
864189714Syongari		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
865189714Syongari		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
866189714Syongari			txp_start_locked(sc->sc_ifp);
867189714Syongari		if (more != 0 || READ_REG(sc, TXP_ISR & TXP_INT_LATCH) != 0) {
868189714Syongari			taskqueue_enqueue(sc->sc_tq, &sc->sc_int_task);
869189714Syongari			TXP_UNLOCK(sc);
870189714Syongari			return;
871189714Syongari		}
87280219Swpaul	}
87380219Swpaul
874189714Syongari	/* Re-enable interrupts. */
875189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_NONE);
876151772Sjhb	TXP_UNLOCK(sc);
87780219Swpaul}
87880219Swpaul
879189714Syongari#ifndef __NO_STRICT_ALIGNMENT
880189714Syongaristatic __inline void
881189714Syongaritxp_fixup_rx(struct mbuf *m)
88280219Swpaul{
883189714Syongari	int i;
884189714Syongari	uint16_t *src, *dst;
885189714Syongari
886189714Syongari	src = mtod(m, uint16_t *);
887189714Syongari	dst = src - (TXP_RXBUF_ALIGN - ETHER_ALIGN) / sizeof *src;
888189714Syongari
889189714Syongari	for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++)
890189714Syongari		*dst++ = *src++;
891189714Syongari
892189714Syongari	m->m_data -= TXP_RXBUF_ALIGN - ETHER_ALIGN;
893189714Syongari}
894189714Syongari#endif
895189714Syongari
896189714Syongaristatic int
897189714Syongaritxp_rx_reclaim(struct txp_softc *sc, struct txp_rx_ring *r, int count)
898189714Syongari{
899189714Syongari	struct ifnet *ifp;
90080219Swpaul	struct txp_rx_desc *rxd;
90180219Swpaul	struct mbuf *m;
902189714Syongari	struct txp_rx_swdesc *sd;
903189714Syongari	uint32_t roff, woff, rx_stat, prog;
90480219Swpaul
905151772Sjhb	TXP_LOCK_ASSERT(sc);
90680219Swpaul
907189714Syongari	ifp = sc->sc_ifp;
90880219Swpaul
909189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_POSTREAD |
910189714Syongari	    BUS_DMASYNC_POSTWRITE);
91180219Swpaul
912189714Syongari	roff = le32toh(*r->r_roff);
913189714Syongari	woff = le32toh(*r->r_woff);
914189714Syongari	rxd = r->r_desc + roff / sizeof(struct txp_rx_desc);
915189714Syongari	for (prog = 0; roff != woff; prog++, count--) {
916189714Syongari		if (count <= 0)
917189714Syongari			break;
918189714Syongari		bcopy((u_long *)&rxd->rx_vaddrlo, &sd, sizeof(sd));
919189714Syongari		KASSERT(sd != NULL, ("%s: Rx desc ring corrupted", __func__));
920189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map,
921189714Syongari		    BUS_DMASYNC_POSTREAD);
922189714Syongari		bus_dmamap_unload(sc->sc_cdata.txp_rx_tag, sd->sd_map);
92380219Swpaul		m = sd->sd_mbuf;
924189714Syongari		KASSERT(m != NULL, ("%s: Rx buffer ring corrupted", __func__));
92580219Swpaul		sd->sd_mbuf = NULL;
926189714Syongari		TAILQ_REMOVE(&sc->sc_busy_list, sd, sd_next);
927189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next);
928189714Syongari		if ((rxd->rx_flags & RX_FLAGS_ERROR) != 0) {
929189714Syongari			if (bootverbose)
930189714Syongari				device_printf(sc->sc_dev, "Rx error %u\n",
931189714Syongari				    le32toh(rxd->rx_stat) & RX_ERROR_MASK);
932151772Sjhb			m_freem(m);
933189714Syongari			goto next;
93480219Swpaul		}
935189714Syongari
936189714Syongari		m->m_pkthdr.len = m->m_len = le16toh(rxd->rx_len);
937189714Syongari		m->m_pkthdr.rcvif = ifp;
938189714Syongari#ifndef __NO_STRICT_ALIGNMENT
939189714Syongari		txp_fixup_rx(m);
94080219Swpaul#endif
941189714Syongari		rx_stat = le32toh(rxd->rx_stat);
942189714Syongari		if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) {
943189714Syongari			if ((rx_stat & RX_STAT_IPCKSUMBAD) != 0)
944189714Syongari				m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
945189714Syongari			else if ((rx_stat & RX_STAT_IPCKSUMGOOD) != 0)
946189714Syongari				m->m_pkthdr.csum_flags |=
947189714Syongari				    CSUM_IP_CHECKED|CSUM_IP_VALID;
94880219Swpaul
949189714Syongari			if ((rx_stat & RX_STAT_TCPCKSUMGOOD) != 0 ||
950189714Syongari			    (rx_stat & RX_STAT_UDPCKSUMGOOD) != 0) {
951189714Syongari				m->m_pkthdr.csum_flags |=
952189714Syongari				    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
953189714Syongari				m->m_pkthdr.csum_data = 0xffff;
954189714Syongari			}
95580219Swpaul		}
95680219Swpaul
957189714Syongari		/*
958189714Syongari		 * XXX
959189714Syongari		 * Typhoon has a firmware bug that VLAN tag is always
960189714Syongari		 * stripped out even if it is told to not remove the tag.
961189714Syongari		 * Therefore don't check if_capenable here.
962189714Syongari		 */
963189714Syongari		if (/* (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && */
964189714Syongari		    (rx_stat & RX_STAT_VLAN) != 0) {
965189714Syongari			m->m_pkthdr.ether_vtag =
966189714Syongari			    bswap16((le32toh(rxd->rx_vlan) >> 16));
967162375Sandre			m->m_flags |= M_VLANTAG;
96880219Swpaul		}
96983115Sbrooks
970151772Sjhb		TXP_UNLOCK(sc);
971106937Ssam		(*ifp->if_input)(ifp, m);
972151772Sjhb		TXP_LOCK(sc);
97380219Swpaul
97480219Swpaulnext:
97580219Swpaul		roff += sizeof(struct txp_rx_desc);
97680219Swpaul		if (roff == (RX_ENTRIES * sizeof(struct txp_rx_desc))) {
97780219Swpaul			roff = 0;
97880219Swpaul			rxd = r->r_desc;
97980219Swpaul		} else
98080219Swpaul			rxd++;
981189714Syongari		prog++;
98280219Swpaul	}
98380219Swpaul
984189714Syongari	if (prog == 0)
985189714Syongari		return (0);
986189714Syongari
987189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_PREREAD |
988189714Syongari	    BUS_DMASYNC_PREWRITE);
989189714Syongari	*r->r_roff = le32toh(roff);
990189714Syongari
991189714Syongari	return (count > 0 ? 0 : EAGAIN);
99280219Swpaul}
99380219Swpaul
99480219Swpaulstatic void
995189685Syongaritxp_rxbuf_reclaim(struct txp_softc *sc)
99680219Swpaul{
997189714Syongari	struct txp_hostvar *hv;
99880219Swpaul	struct txp_rxbuf_desc *rbd;
999189714Syongari	struct txp_rx_swdesc *sd;
1000189714Syongari	bus_dma_segment_t segs[1];
1001189714Syongari	int nsegs, prod, prog;
1002189714Syongari	uint32_t cons;
100380219Swpaul
1004151772Sjhb	TXP_LOCK_ASSERT(sc);
1005189714Syongari
1006189714Syongari	hv = sc->sc_hostvar;
1007189714Syongari	cons = TXP_OFFSET2IDX(le32toh(hv->hv_rx_buf_read_idx));
1008189714Syongari	prod = sc->sc_rxbufprod;
1009189714Syongari	TXP_DESC_INC(prod, RXBUF_ENTRIES);
1010189714Syongari	if (prod == cons)
101180219Swpaul		return;
101280219Swpaul
1013189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag,
1014189714Syongari	    sc->sc_cdata.txp_rxbufs_map,
1015189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
101680219Swpaul
1017189714Syongari	for (prog = 0; prod != cons; prog++) {
1018189714Syongari		sd = TAILQ_FIRST(&sc->sc_free_list);
1019189714Syongari		if (sd == NULL)
102080219Swpaul			break;
1021189714Syongari		rbd = sc->sc_rxbufs + prod;
1022189714Syongari		bcopy((u_long *)&rbd->rb_vaddrlo, &sd, sizeof(sd));
1023243857Sglebius		sd->sd_mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
102480219Swpaul		if (sd->sd_mbuf == NULL)
1025189714Syongari			break;
102680219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
1027189714Syongari#ifndef __NO_STRICT_ALIGNMENT
1028189714Syongari		m_adj(sd->sd_mbuf, TXP_RXBUF_ALIGN);
1029189714Syongari#endif
1030189714Syongari		if (bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_rx_tag,
1031189714Syongari		    sd->sd_map, sd->sd_mbuf, segs, &nsegs, 0) != 0) {
1032189714Syongari			m_freem(sd->sd_mbuf);
1033189714Syongari			sd->sd_mbuf = NULL;
1034189714Syongari			break;
1035189714Syongari		}
1036189714Syongari		KASSERT(nsegs == 1, ("%s : %d segments returned!", __func__,
1037189714Syongari		    nsegs));
1038189714Syongari		TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next);
1039189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_busy_list, sd, sd_next);
1040189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map,
1041189714Syongari		    BUS_DMASYNC_PREREAD);
1042189714Syongari		rbd->rb_paddrlo = htole32(TXP_ADDR_LO(segs[0].ds_addr));
1043189714Syongari		rbd->rb_paddrhi = htole32(TXP_ADDR_HI(segs[0].ds_addr));
1044189714Syongari		TXP_DESC_INC(prod, RXBUF_ENTRIES);
104580219Swpaul	}
104680219Swpaul
1047189714Syongari	if (prog == 0)
1048189714Syongari		return;
1049189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag,
1050189714Syongari	    sc->sc_cdata.txp_rxbufs_map,
1051189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1052189714Syongari	prod = (prod + RXBUF_ENTRIES - 1) % RXBUF_ENTRIES;
1053189714Syongari	sc->sc_rxbufprod = prod;
1054189714Syongari	hv->hv_rx_buf_write_idx = htole32(TXP_IDX2OFFSET(prod));
105580219Swpaul}
105680219Swpaul
105780219Swpaul/*
105880219Swpaul * Reclaim mbufs and entries from a transmit ring.
105980219Swpaul */
106080219Swpaulstatic void
1061189685Syongaritxp_tx_reclaim(struct txp_softc *sc, struct txp_tx_ring *r)
106280219Swpaul{
1063189714Syongari	struct ifnet *ifp;
1064189714Syongari	uint32_t idx;
1065189714Syongari	uint32_t cons, cnt;
1066189714Syongari	struct txp_tx_desc *txd;
1067189714Syongari	struct txp_swdesc *sd;
106880219Swpaul
1069151772Sjhb	TXP_LOCK_ASSERT(sc);
107080219Swpaul
1071189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_POSTREAD |
1072189714Syongari	    BUS_DMASYNC_POSTWRITE);
1073189714Syongari	ifp = sc->sc_ifp;
1074189714Syongari	idx = TXP_OFFSET2IDX(le32toh(*(r->r_off)));
1075189714Syongari	cons = r->r_cons;
1076189714Syongari	cnt = r->r_cnt;
1077189714Syongari	txd = r->r_desc + cons;
1078189714Syongari	sd = sc->sc_txd + cons;
1079189714Syongari
1080189714Syongari	for (cnt = r->r_cnt; cons != idx && cnt > 0; cnt--) {
1081189714Syongari		if ((txd->tx_flags & TX_FLAGS_TYPE_M) == TX_FLAGS_TYPE_DATA) {
1082189714Syongari			if (sd->sd_mbuf != NULL) {
1083189714Syongari				bus_dmamap_sync(sc->sc_cdata.txp_tx_tag,
1084189714Syongari				    sd->sd_map, BUS_DMASYNC_POSTWRITE);
1085189714Syongari				bus_dmamap_unload(sc->sc_cdata.txp_tx_tag,
1086189714Syongari				    sd->sd_map);
1087189714Syongari				m_freem(sd->sd_mbuf);
1088189714Syongari				sd->sd_mbuf = NULL;
108980219Swpaul				txd->tx_addrlo = 0;
109080219Swpaul				txd->tx_addrhi = 0;
1091189714Syongari				txd->tx_flags = 0;
109280219Swpaul			}
109380219Swpaul		}
1094148887Srwatson		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
109580219Swpaul
109680219Swpaul		if (++cons == TX_ENTRIES) {
109780219Swpaul			txd = r->r_desc;
109880219Swpaul			cons = 0;
109980219Swpaul			sd = sc->sc_txd;
110080219Swpaul		} else {
110180219Swpaul			txd++;
110280219Swpaul			sd++;
110380219Swpaul		}
110480219Swpaul	}
110580219Swpaul
1106189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_PREREAD |
1107189714Syongari	    BUS_DMASYNC_PREWRITE);
110880219Swpaul	r->r_cons = cons;
110980219Swpaul	r->r_cnt = cnt;
111080219Swpaul	if (cnt == 0)
1111189714Syongari		sc->sc_watchdog_timer = 0;
111280219Swpaul}
111380219Swpaul
111480219Swpaulstatic int
1115189685Syongaritxp_shutdown(device_t dev)
111680219Swpaul{
1117189714Syongari
1118189714Syongari	return (txp_suspend(dev));
1119189714Syongari}
1120189714Syongari
1121189714Syongaristatic int
1122189714Syongaritxp_suspend(device_t dev)
1123189714Syongari{
112480219Swpaul	struct txp_softc *sc;
1125189714Syongari	struct ifnet *ifp;
1126189714Syongari	uint8_t *eaddr;
1127189714Syongari	uint16_t p1;
1128189714Syongari	uint32_t p2;
1129189714Syongari	int pmc;
1130189714Syongari	uint16_t pmstat;
113180219Swpaul
113280219Swpaul	sc = device_get_softc(dev);
113380219Swpaul
1134151772Sjhb	TXP_LOCK(sc);
1135189714Syongari	ifp = sc->sc_ifp;
1136189714Syongari	txp_stop(sc);
1137189714Syongari	txp_init_rings(sc);
1138189714Syongari	/* Reset controller and make it reload sleep image. */
1139189714Syongari	txp_reset(sc);
1140189714Syongari	/* Let controller boot from sleep image. */
1141189714Syongari	if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0)
1142189714Syongari		device_printf(sc->sc_dev, "couldn't boot sleep image\n");
1143151772Sjhb
1144189714Syongari	/* Set station address. */
1145189714Syongari	eaddr = IF_LLADDR(sc->sc_ifp);
1146189714Syongari	p1 = 0;
1147189714Syongari	((uint8_t *)&p1)[1] = eaddr[0];
1148189714Syongari	((uint8_t *)&p1)[0] = eaddr[1];
1149189714Syongari	p1 = le16toh(p1);
1150189714Syongari	((uint8_t *)&p2)[3] = eaddr[2];
1151189714Syongari	((uint8_t *)&p2)[2] = eaddr[3];
1152189714Syongari	((uint8_t *)&p2)[1] = eaddr[4];
1153189714Syongari	((uint8_t *)&p2)[0] = eaddr[5];
1154189714Syongari	p2 = le32toh(p2);
1155189714Syongari	txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0, NULL, NULL,
1156189714Syongari	    NULL, TXP_CMD_WAIT);
1157189714Syongari	txp_set_filter(sc);
1158189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
1159189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
1160189714Syongari	txp_sleep(sc, sc->sc_ifp->if_capenable);
1161219902Sjhb	if (pci_find_cap(sc->sc_dev, PCIY_PMG, &pmc) == 0) {
1162189714Syongari		/* Request PME. */
1163189714Syongari		pmstat = pci_read_config(sc->sc_dev,
1164189714Syongari		    pmc + PCIR_POWER_STATUS, 2);
1165189714Syongari		pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE);
1166189714Syongari		if ((ifp->if_capenable & IFCAP_WOL) != 0)
1167189714Syongari			pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
1168189714Syongari		pci_write_config(sc->sc_dev,
1169189714Syongari		    pmc + PCIR_POWER_STATUS, pmstat, 2);
1170189714Syongari	}
1171189714Syongari	TXP_UNLOCK(sc);
117280219Swpaul
1173189714Syongari	return (0);
1174189714Syongari}
1175189714Syongari
1176189714Syongaristatic int
1177189714Syongaritxp_resume(device_t dev)
1178189714Syongari{
1179189714Syongari	struct txp_softc *sc;
1180189714Syongari	int pmc;
1181189714Syongari	uint16_t pmstat;
1182189714Syongari
1183189714Syongari	sc = device_get_softc(dev);
1184189714Syongari
1185189714Syongari	TXP_LOCK(sc);
1186219902Sjhb	if (pci_find_cap(sc->sc_dev, PCIY_PMG, &pmc) == 0) {
1187189714Syongari		/* Disable PME and clear PME status. */
1188189714Syongari		pmstat = pci_read_config(sc->sc_dev,
1189189714Syongari		    pmc + PCIR_POWER_STATUS, 2);
1190189714Syongari		if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) {
1191189714Syongari			pmstat &= ~PCIM_PSTAT_PMEENABLE;
1192189714Syongari			pci_write_config(sc->sc_dev,
1193189714Syongari			    pmc + PCIR_POWER_STATUS, pmstat, 2);
1194189714Syongari		}
1195189714Syongari	}
1196189714Syongari	if ((sc->sc_ifp->if_flags & IFF_UP) != 0)
1197189714Syongari		txp_init_locked(sc);
1198151772Sjhb	TXP_UNLOCK(sc);
119980219Swpaul
1200189688Syongari	return (0);
120180219Swpaul}
120280219Swpaul
1203189714Syongaristruct txp_dmamap_arg {
1204189714Syongari	bus_addr_t	txp_busaddr;
1205189714Syongari};
1206189714Syongari
1207189714Syongaristatic void
1208189714Syongaritxp_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1209189714Syongari{
1210189714Syongari	struct txp_dmamap_arg *ctx;
1211189714Syongari
1212189714Syongari	if (error != 0)
1213189714Syongari		return;
1214189714Syongari
1215189714Syongari	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
1216189714Syongari
1217189714Syongari	ctx = (struct txp_dmamap_arg *)arg;
1218189714Syongari	ctx->txp_busaddr = segs[0].ds_addr;
1219189714Syongari}
1220189714Syongari
122180219Swpaulstatic int
1222189714Syongaritxp_dma_alloc(struct txp_softc *sc, char *type, bus_dma_tag_t *tag,
1223189714Syongari    bus_size_t alignment, bus_size_t boundary, bus_dmamap_t *map, void **buf,
1224189714Syongari    bus_size_t size, bus_addr_t *paddr)
1225189714Syongari{
1226189714Syongari	struct txp_dmamap_arg ctx;
1227189714Syongari	int error;
1228189714Syongari
1229189714Syongari	/* Create DMA block tag. */
1230189714Syongari	error = bus_dma_tag_create(
1231189714Syongari	    sc->sc_cdata.txp_parent_tag,	/* parent */
1232189714Syongari	    alignment, boundary,	/* algnmnt, boundary */
1233189714Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
1234189714Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1235189714Syongari	    NULL, NULL,			/* filter, filterarg */
1236189714Syongari	    size,			/* maxsize */
1237189714Syongari	    1,				/* nsegments */
1238189714Syongari	    size,			/* maxsegsize */
1239189714Syongari	    0,				/* flags */
1240189714Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1241189714Syongari	    tag);
1242189714Syongari	if (error != 0) {
1243189714Syongari		device_printf(sc->sc_dev,
1244189714Syongari		    "could not create DMA tag for %s.\n", type);
1245189714Syongari		return (error);
1246189714Syongari	}
1247189714Syongari
1248189714Syongari	*paddr = 0;
1249189714Syongari	/* Allocate DMA'able memory and load the DMA map. */
1250189714Syongari	error = bus_dmamem_alloc(*tag, buf, BUS_DMA_WAITOK | BUS_DMA_ZERO |
1251189714Syongari	    BUS_DMA_COHERENT, map);
1252189714Syongari	if (error != 0) {
1253189714Syongari		device_printf(sc->sc_dev,
1254189714Syongari		    "could not allocate DMA'able memory for %s.\n", type);
1255189714Syongari		return (error);
1256189714Syongari	}
1257189714Syongari
1258189714Syongari	ctx.txp_busaddr = 0;
1259189714Syongari	error = bus_dmamap_load(*tag, *map, *(uint8_t **)buf,
1260189714Syongari	    size, txp_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
1261189714Syongari	if (error != 0 || ctx.txp_busaddr == 0) {
1262189714Syongari		device_printf(sc->sc_dev,
1263189714Syongari		    "could not load DMA'able memory for %s.\n", type);
1264189714Syongari		return (error);
1265189714Syongari	}
1266189714Syongari	*paddr = ctx.txp_busaddr;
1267189714Syongari
1268189714Syongari	return (0);
1269189714Syongari}
1270189714Syongari
1271189714Syongaristatic void
1272267580Sjhbtxp_dma_free(struct txp_softc *sc, bus_dma_tag_t *tag, bus_dmamap_t map,
1273267580Sjhb    void **buf, bus_addr_t *paddr)
1274189714Syongari{
1275189714Syongari
1276189714Syongari	if (*tag != NULL) {
1277267580Sjhb		if (*paddr != 0)
1278267580Sjhb			bus_dmamap_unload(*tag, map);
1279267580Sjhb		if (buf != NULL)
1280267580Sjhb			bus_dmamem_free(*tag, *(uint8_t **)buf, map);
1281189714Syongari		*(uint8_t **)buf = NULL;
1282267580Sjhb		*paddr = 0;
1283189714Syongari		bus_dma_tag_destroy(*tag);
1284189714Syongari		*tag = NULL;
1285189714Syongari	}
1286189714Syongari}
1287189714Syongari
1288189714Syongaristatic int
1289189685Syongaritxp_alloc_rings(struct txp_softc *sc)
129080219Swpaul{
129180219Swpaul	struct txp_boot_record *boot;
129280219Swpaul	struct txp_ldata *ld;
1293189714Syongari	struct txp_swdesc *txd;
1294189714Syongari	struct txp_rxbuf_desc *rbd;
1295189714Syongari	struct txp_rx_swdesc *sd;
1296189714Syongari	int error, i;
129780219Swpaul
1298189714Syongari	ld = &sc->sc_ldata;
1299189714Syongari	boot = ld->txp_boot;
130080219Swpaul
130180219Swpaul	/* boot record */
130280219Swpaul	sc->sc_boot = boot;
130380219Swpaul
1304189714Syongari	/*
1305189714Syongari	 * Create parent ring/DMA block tag.
1306189714Syongari	 * Datasheet says that all ring addresses and descriptors
1307189714Syongari	 * support 64bits addressing. However the controller is
1308189714Syongari	 * known to have no support DAC so limit DMA address space
1309189714Syongari	 * to 32bits.
1310189714Syongari	 */
1311189714Syongari	error = bus_dma_tag_create(
1312189714Syongari	    bus_get_dma_tag(sc->sc_dev), /* parent */
1313189714Syongari	    1, 0,			/* algnmnt, boundary */
1314189714Syongari	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1315189714Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1316189714Syongari	    NULL, NULL,			/* filter, filterarg */
1317189714Syongari	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
1318189714Syongari	    0,				/* nsegments */
1319189714Syongari	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1320189714Syongari	    0,				/* flags */
1321189714Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1322189714Syongari	    &sc->sc_cdata.txp_parent_tag);
1323189714Syongari	if (error != 0) {
1324189714Syongari		device_printf(sc->sc_dev, "could not create parent DMA tag.\n");
1325189714Syongari		return (error);
1326189714Syongari	}
132780219Swpaul
1328189714Syongari	/* Boot record. */
1329189714Syongari	error = txp_dma_alloc(sc, "boot record",
1330189714Syongari	    &sc->sc_cdata.txp_boot_tag, sizeof(uint32_t), 0,
1331189714Syongari	    &sc->sc_cdata.txp_boot_map, (void **)&sc->sc_ldata.txp_boot,
1332189714Syongari	    sizeof(struct txp_boot_record),
1333189714Syongari	    &sc->sc_ldata.txp_boot_paddr);
1334189714Syongari	if (error != 0)
1335189714Syongari		return (error);
1336189714Syongari	boot = sc->sc_ldata.txp_boot;
1337189714Syongari	sc->sc_boot = boot;
1338189714Syongari
1339189714Syongari	/* Host variables. */
1340189714Syongari	error = txp_dma_alloc(sc, "host variables",
1341189714Syongari	    &sc->sc_cdata.txp_hostvar_tag, sizeof(uint32_t), 0,
1342189714Syongari	    &sc->sc_cdata.txp_hostvar_map, (void **)&sc->sc_ldata.txp_hostvar,
1343189714Syongari	    sizeof(struct txp_hostvar),
1344189714Syongari	    &sc->sc_ldata.txp_hostvar_paddr);
1345189714Syongari	if (error != 0)
1346189714Syongari		return (error);
1347189714Syongari	boot->br_hostvar_lo =
1348189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_hostvar_paddr));
1349189714Syongari	boot->br_hostvar_hi =
1350189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_hostvar_paddr));
1351189714Syongari	sc->sc_hostvar = sc->sc_ldata.txp_hostvar;
1352189714Syongari
1353189714Syongari	/* Hi priority tx ring. */
1354189714Syongari	error = txp_dma_alloc(sc, "hi priority tx ring",
1355189714Syongari	    &sc->sc_cdata.txp_txhiring_tag, sizeof(struct txp_tx_desc), 0,
1356189714Syongari	    &sc->sc_cdata.txp_txhiring_map, (void **)&sc->sc_ldata.txp_txhiring,
1357189714Syongari	    sizeof(struct txp_tx_desc) * TX_ENTRIES,
1358189714Syongari	    &sc->sc_ldata.txp_txhiring_paddr);
1359189714Syongari	if (error != 0)
1360189714Syongari		return (error);
1361189714Syongari	boot->br_txhipri_lo =
1362189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_txhiring_paddr));
1363189714Syongari	boot->br_txhipri_hi =
1364189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_txhiring_paddr));
1365189714Syongari	boot->br_txhipri_siz =
1366189714Syongari	    htole32(TX_ENTRIES * sizeof(struct txp_tx_desc));
1367189714Syongari	sc->sc_txhir.r_tag = sc->sc_cdata.txp_txhiring_tag;
1368189714Syongari	sc->sc_txhir.r_map = sc->sc_cdata.txp_txhiring_map;
136980219Swpaul	sc->sc_txhir.r_reg = TXP_H2A_1;
1370189714Syongari	sc->sc_txhir.r_desc = sc->sc_ldata.txp_txhiring;
137180219Swpaul	sc->sc_txhir.r_cons = sc->sc_txhir.r_prod = sc->sc_txhir.r_cnt = 0;
137280219Swpaul	sc->sc_txhir.r_off = &sc->sc_hostvar->hv_tx_hi_desc_read_idx;
137380219Swpaul
1374189714Syongari	/* Low priority tx ring. */
1375189714Syongari	error = txp_dma_alloc(sc, "low priority tx ring",
1376189714Syongari	    &sc->sc_cdata.txp_txloring_tag, sizeof(struct txp_tx_desc), 0,
1377189714Syongari	    &sc->sc_cdata.txp_txloring_map, (void **)&sc->sc_ldata.txp_txloring,
1378189714Syongari	    sizeof(struct txp_tx_desc) * TX_ENTRIES,
1379189714Syongari	    &sc->sc_ldata.txp_txloring_paddr);
1380189714Syongari	if (error != 0)
1381189714Syongari		return (error);
1382189714Syongari	boot->br_txlopri_lo =
1383189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_txloring_paddr));
1384189714Syongari	boot->br_txlopri_hi =
1385189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_txloring_paddr));
1386189714Syongari	boot->br_txlopri_siz =
1387189714Syongari	    htole32(TX_ENTRIES * sizeof(struct txp_tx_desc));
1388189714Syongari	sc->sc_txlor.r_tag = sc->sc_cdata.txp_txloring_tag;
1389189714Syongari	sc->sc_txlor.r_map = sc->sc_cdata.txp_txloring_map;
139080219Swpaul	sc->sc_txlor.r_reg = TXP_H2A_3;
1391189714Syongari	sc->sc_txlor.r_desc = sc->sc_ldata.txp_txloring;
139280219Swpaul	sc->sc_txlor.r_cons = sc->sc_txlor.r_prod = sc->sc_txlor.r_cnt = 0;
139380219Swpaul	sc->sc_txlor.r_off = &sc->sc_hostvar->hv_tx_lo_desc_read_idx;
139480219Swpaul
1395189714Syongari	/* High priority rx ring. */
1396189714Syongari	error = txp_dma_alloc(sc, "hi priority rx ring",
1397196721Syongari	    &sc->sc_cdata.txp_rxhiring_tag,
1398196721Syongari	    roundup(sizeof(struct txp_rx_desc), 16), 0,
1399189714Syongari	    &sc->sc_cdata.txp_rxhiring_map, (void **)&sc->sc_ldata.txp_rxhiring,
1400189714Syongari	    sizeof(struct txp_rx_desc) * RX_ENTRIES,
1401189714Syongari	    &sc->sc_ldata.txp_rxhiring_paddr);
1402189714Syongari	if (error != 0)
1403189714Syongari		return (error);
1404189714Syongari	boot->br_rxhipri_lo =
1405189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxhiring_paddr));
1406189714Syongari	boot->br_rxhipri_hi =
1407189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxhiring_paddr));
1408189714Syongari	boot->br_rxhipri_siz =
1409189714Syongari	    htole32(RX_ENTRIES * sizeof(struct txp_rx_desc));
1410189714Syongari	sc->sc_rxhir.r_tag = sc->sc_cdata.txp_rxhiring_tag;
1411189714Syongari	sc->sc_rxhir.r_map = sc->sc_cdata.txp_rxhiring_map;
1412189714Syongari	sc->sc_rxhir.r_desc = sc->sc_ldata.txp_rxhiring;
141380219Swpaul	sc->sc_rxhir.r_roff = &sc->sc_hostvar->hv_rx_hi_read_idx;
141480219Swpaul	sc->sc_rxhir.r_woff = &sc->sc_hostvar->hv_rx_hi_write_idx;
141580219Swpaul
1416189714Syongari	/* Low priority rx ring. */
1417189714Syongari	error = txp_dma_alloc(sc, "low priority rx ring",
1418196721Syongari	    &sc->sc_cdata.txp_rxloring_tag,
1419196721Syongari	    roundup(sizeof(struct txp_rx_desc), 16), 0,
1420189714Syongari	    &sc->sc_cdata.txp_rxloring_map, (void **)&sc->sc_ldata.txp_rxloring,
1421189714Syongari	    sizeof(struct txp_rx_desc) * RX_ENTRIES,
1422189714Syongari	    &sc->sc_ldata.txp_rxloring_paddr);
1423189714Syongari	if (error != 0)
1424189714Syongari		return (error);
1425189714Syongari	boot->br_rxlopri_lo =
1426189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxloring_paddr));
1427189714Syongari	boot->br_rxlopri_hi =
1428189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxloring_paddr));
1429189714Syongari	boot->br_rxlopri_siz =
1430189714Syongari	    htole32(RX_ENTRIES * sizeof(struct txp_rx_desc));
1431189714Syongari	sc->sc_rxlor.r_tag = sc->sc_cdata.txp_rxloring_tag;
1432189714Syongari	sc->sc_rxlor.r_map = sc->sc_cdata.txp_rxloring_map;
1433189714Syongari	sc->sc_rxlor.r_desc = sc->sc_ldata.txp_rxloring;
143480219Swpaul	sc->sc_rxlor.r_roff = &sc->sc_hostvar->hv_rx_lo_read_idx;
143580219Swpaul	sc->sc_rxlor.r_woff = &sc->sc_hostvar->hv_rx_lo_write_idx;
143680219Swpaul
1437189714Syongari	/* Command ring. */
1438189714Syongari	error = txp_dma_alloc(sc, "command ring",
1439189714Syongari	    &sc->sc_cdata.txp_cmdring_tag, sizeof(struct txp_cmd_desc), 0,
1440189714Syongari	    &sc->sc_cdata.txp_cmdring_map, (void **)&sc->sc_ldata.txp_cmdring,
1441189714Syongari	    sizeof(struct txp_cmd_desc) * CMD_ENTRIES,
1442189714Syongari	    &sc->sc_ldata.txp_cmdring_paddr);
1443189714Syongari	if (error != 0)
1444189714Syongari		return (error);
1445189714Syongari	boot->br_cmd_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_cmdring_paddr));
1446189714Syongari	boot->br_cmd_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_cmdring_paddr));
1447189714Syongari	boot->br_cmd_siz = htole32(CMD_ENTRIES * sizeof(struct txp_cmd_desc));
1448189714Syongari	sc->sc_cmdring.base = sc->sc_ldata.txp_cmdring;
144980219Swpaul	sc->sc_cmdring.size = CMD_ENTRIES * sizeof(struct txp_cmd_desc);
145080219Swpaul	sc->sc_cmdring.lastwrite = 0;
145180219Swpaul
1452189714Syongari	/* Response ring. */
1453189714Syongari	error = txp_dma_alloc(sc, "response ring",
1454189714Syongari	    &sc->sc_cdata.txp_rspring_tag, sizeof(struct txp_rsp_desc), 0,
1455189714Syongari	    &sc->sc_cdata.txp_rspring_map, (void **)&sc->sc_ldata.txp_rspring,
1456189714Syongari	    sizeof(struct txp_rsp_desc) * RSP_ENTRIES,
1457189714Syongari	    &sc->sc_ldata.txp_rspring_paddr);
1458189714Syongari	if (error != 0)
1459189714Syongari		return (error);
1460189714Syongari	boot->br_resp_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rspring_paddr));
1461189714Syongari	boot->br_resp_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rspring_paddr));
1462189714Syongari	boot->br_resp_siz = htole32(RSP_ENTRIES * sizeof(struct txp_rsp_desc));
1463189714Syongari	sc->sc_rspring.base = sc->sc_ldata.txp_rspring;
146480219Swpaul	sc->sc_rspring.size = RSP_ENTRIES * sizeof(struct txp_rsp_desc);
146580219Swpaul	sc->sc_rspring.lastwrite = 0;
146680219Swpaul
1467189714Syongari	/* Receive buffer ring. */
1468189714Syongari	error = txp_dma_alloc(sc, "receive buffer ring",
1469189714Syongari	    &sc->sc_cdata.txp_rxbufs_tag, sizeof(struct txp_rxbuf_desc), 0,
1470189714Syongari	    &sc->sc_cdata.txp_rxbufs_map, (void **)&sc->sc_ldata.txp_rxbufs,
1471189714Syongari	    sizeof(struct txp_rxbuf_desc) * RXBUF_ENTRIES,
1472189714Syongari	    &sc->sc_ldata.txp_rxbufs_paddr);
1473189714Syongari	if (error != 0)
1474189714Syongari		return (error);
1475189714Syongari	boot->br_rxbuf_lo =
1476189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxbufs_paddr));
1477189714Syongari	boot->br_rxbuf_hi =
1478189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxbufs_paddr));
1479189714Syongari	boot->br_rxbuf_siz =
1480189714Syongari	    htole32(RXBUF_ENTRIES * sizeof(struct txp_rxbuf_desc));
1481189714Syongari	sc->sc_rxbufs = sc->sc_ldata.txp_rxbufs;
148280219Swpaul
1483189714Syongari	/* Zero ring. */
1484189714Syongari	error = txp_dma_alloc(sc, "zero buffer",
1485189714Syongari	    &sc->sc_cdata.txp_zero_tag, sizeof(uint32_t), 0,
1486189714Syongari	    &sc->sc_cdata.txp_zero_map, (void **)&sc->sc_ldata.txp_zero,
1487189714Syongari	    sizeof(uint32_t), &sc->sc_ldata.txp_zero_paddr);
1488189714Syongari	if (error != 0)
1489189714Syongari		return (error);
1490189714Syongari	boot->br_zero_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_zero_paddr));
1491189714Syongari	boot->br_zero_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_zero_paddr));
1492189714Syongari
1493189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_boot_tag, sc->sc_cdata.txp_boot_map,
1494189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1495189714Syongari
1496189714Syongari	/* Create Tx buffers. */
1497189714Syongari	error = bus_dma_tag_create(
1498189714Syongari	    sc->sc_cdata.txp_parent_tag,	/* parent */
1499189714Syongari	    1, 0,			/* algnmnt, boundary */
1500189714Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
1501189714Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1502189714Syongari	    NULL, NULL,			/* filter, filterarg */
1503189714Syongari	    MCLBYTES * TXP_MAXTXSEGS,	/* maxsize */
1504189714Syongari	    TXP_MAXTXSEGS,		/* nsegments */
1505189714Syongari	    MCLBYTES,			/* maxsegsize */
1506189714Syongari	    0,				/* flags */
1507189714Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1508189714Syongari	    &sc->sc_cdata.txp_tx_tag);
1509189714Syongari	if (error != 0) {
1510189714Syongari		device_printf(sc->sc_dev, "could not create Tx DMA tag.\n");
1511189714Syongari		goto fail;
1512189714Syongari	}
1513189714Syongari
1514189714Syongari	/* Create tag for Rx buffers. */
1515189714Syongari	error = bus_dma_tag_create(
1516189714Syongari	    sc->sc_cdata.txp_parent_tag,	/* parent */
1517189714Syongari	    TXP_RXBUF_ALIGN, 0,		/* algnmnt, boundary */
1518189714Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
1519189714Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1520189714Syongari	    NULL, NULL,			/* filter, filterarg */
1521189714Syongari	    MCLBYTES,			/* maxsize */
1522189714Syongari	    1,				/* nsegments */
1523189714Syongari	    MCLBYTES,			/* maxsegsize */
1524189714Syongari	    0,				/* flags */
1525189714Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1526189714Syongari	    &sc->sc_cdata.txp_rx_tag);
1527189714Syongari	if (error != 0) {
1528189714Syongari		device_printf(sc->sc_dev, "could not create Rx DMA tag.\n");
1529189714Syongari		goto fail;
1530189714Syongari	}
1531189714Syongari
1532189714Syongari	/* Create DMA maps for Tx buffers. */
1533189714Syongari	for (i = 0; i < TX_ENTRIES; i++) {
1534189714Syongari		txd = &sc->sc_txd[i];
1535189714Syongari		txd->sd_mbuf = NULL;
1536189714Syongari		txd->sd_map = NULL;
1537189714Syongari		error = bus_dmamap_create(sc->sc_cdata.txp_tx_tag, 0,
1538189714Syongari		    &txd->sd_map);
1539189714Syongari		if (error != 0) {
1540189714Syongari			device_printf(sc->sc_dev,
1541189714Syongari			    "could not create Tx dmamap.\n");
1542189714Syongari			goto fail;
1543189714Syongari		}
1544189714Syongari	}
1545189714Syongari
1546189714Syongari	/* Create DMA maps for Rx buffers. */
154780219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
1548189714Syongari		sd = malloc(sizeof(struct txp_rx_swdesc), M_DEVBUF,
1549189714Syongari		    M_NOWAIT | M_ZERO);
1550189714Syongari		if (sd == NULL) {
1551189714Syongari			error = ENOMEM;
1552189714Syongari			goto fail;
1553189714Syongari		}
1554189714Syongari		/*
1555189714Syongari		 * The virtual address part of descriptor is not used
1556189714Syongari		 * by hardware so use that to save an ring entry. We
1557189714Syongari		 * need bcopy here otherwise the address wouldn't be
1558189714Syongari		 * valid on big-endian architectures.
1559189714Syongari		 */
1560189714Syongari		rbd = sc->sc_rxbufs + i;
1561189714Syongari		bcopy(&sd, (u_long *)&rbd->rb_vaddrlo, sizeof(sd));
156280457Swpaul		sd->sd_mbuf = NULL;
1563189714Syongari		sd->sd_map = NULL;
1564189714Syongari		error = bus_dmamap_create(sc->sc_cdata.txp_rx_tag, 0,
1565189714Syongari		    &sd->sd_map);
1566189714Syongari		if (error != 0) {
1567189714Syongari			device_printf(sc->sc_dev,
1568189714Syongari			    "could not create Rx dmamap.\n");
1569189714Syongari			goto fail;
1570189714Syongari		}
1571189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next);
157280219Swpaul	}
1573189714Syongari
1574189714Syongarifail:
1575189714Syongari	return (error);
1576189714Syongari}
1577189714Syongari
1578189714Syongaristatic void
1579189714Syongaritxp_init_rings(struct txp_softc *sc)
1580189714Syongari{
1581189714Syongari
1582189714Syongari	bzero(sc->sc_ldata.txp_hostvar, sizeof(struct txp_hostvar));
1583189714Syongari	bzero(sc->sc_ldata.txp_zero, sizeof(uint32_t));
1584189714Syongari	sc->sc_txhir.r_cons = 0;
1585189714Syongari	sc->sc_txhir.r_prod = 0;
1586189714Syongari	sc->sc_txhir.r_cnt = 0;
1587189714Syongari	sc->sc_txlor.r_cons = 0;
1588189714Syongari	sc->sc_txlor.r_prod = 0;
1589189714Syongari	sc->sc_txlor.r_cnt = 0;
1590189714Syongari	sc->sc_cmdring.lastwrite = 0;
1591189714Syongari	sc->sc_rspring.lastwrite = 0;
159280219Swpaul	sc->sc_rxbufprod = 0;
1593189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1594189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1595189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1596189714Syongari}
159780219Swpaul
1598189714Syongaristatic int
1599189714Syongaritxp_wait(struct txp_softc *sc, uint32_t state)
1600189714Syongari{
1601189714Syongari	uint32_t reg;
1602189714Syongari	int i;
160380219Swpaul
1604189714Syongari	for (i = 0; i < TXP_TIMEOUT; i++) {
1605189714Syongari		reg = READ_REG(sc, TXP_A2H_0);
1606189714Syongari		if (reg == state)
160780219Swpaul			break;
160880219Swpaul		DELAY(50);
160980219Swpaul	}
161080219Swpaul
1611189714Syongari	return (i == TXP_TIMEOUT ? ETIMEDOUT : 0);
1612189714Syongari}
161380219Swpaul
1614189714Syongaristatic void
1615189714Syongaritxp_free_rings(struct txp_softc *sc)
1616189714Syongari{
1617189714Syongari	struct txp_swdesc *txd;
1618189714Syongari	struct txp_rx_swdesc *sd;
1619189714Syongari	int i;
162080219Swpaul
1621189714Syongari	/* Tx buffers. */
1622189714Syongari	if (sc->sc_cdata.txp_tx_tag != NULL) {
1623189714Syongari		for (i = 0; i < TX_ENTRIES; i++) {
1624189714Syongari			txd = &sc->sc_txd[i];
1625189714Syongari			if (txd->sd_map != NULL) {
1626189714Syongari				bus_dmamap_destroy(sc->sc_cdata.txp_tx_tag,
1627189714Syongari				    txd->sd_map);
1628189714Syongari				txd->sd_map = NULL;
1629189714Syongari			}
1630189714Syongari		}
1631189714Syongari		bus_dma_tag_destroy(sc->sc_cdata.txp_tx_tag);
1632189714Syongari		sc->sc_cdata.txp_tx_tag = NULL;
163380219Swpaul	}
1634189714Syongari	/* Rx buffers. */
1635189714Syongari	if (sc->sc_cdata.txp_rx_tag != NULL) {
1636189714Syongari		if (sc->sc_rxbufs != NULL) {
1637189714Syongari			KASSERT(TAILQ_FIRST(&sc->sc_busy_list) == NULL,
1638189714Syongari			    ("%s : still have busy Rx buffers", __func__));
1639189714Syongari			while ((sd = TAILQ_FIRST(&sc->sc_free_list)) != NULL) {
1640189714Syongari				TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next);
1641189714Syongari				if (sd->sd_map != NULL) {
1642189714Syongari					bus_dmamap_destroy(
1643189714Syongari					    sc->sc_cdata.txp_rx_tag,
1644189714Syongari					    sd->sd_map);
1645189714Syongari					sd->sd_map = NULL;
1646189714Syongari				}
1647189714Syongari				free(sd, M_DEVBUF);
1648189714Syongari			}
1649189714Syongari		}
1650189714Syongari		bus_dma_tag_destroy(sc->sc_cdata.txp_rx_tag);
1651189714Syongari		sc->sc_cdata.txp_rx_tag = NULL;
165280219Swpaul	}
165380219Swpaul
1654189714Syongari	/* Hi priority Tx ring. */
1655189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_txhiring_tag,
1656267580Sjhb	    sc->sc_cdata.txp_txhiring_map,
1657267580Sjhb	    (void **)&sc->sc_ldata.txp_txhiring,
1658267580Sjhb	    &sc->sc_ldata.txp_txhiring_paddr);
1659189714Syongari	/* Low priority Tx ring. */
1660189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_txloring_tag,
1661267580Sjhb	    sc->sc_cdata.txp_txloring_map,
1662267580Sjhb	    (void **)&sc->sc_ldata.txp_txloring,
1663267580Sjhb	    &sc->sc_ldata.txp_txloring_paddr);
1664189714Syongari	/* Hi priority Rx ring. */
1665189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_rxhiring_tag,
1666267580Sjhb	    sc->sc_cdata.txp_rxhiring_map,
1667267580Sjhb	    (void **)&sc->sc_ldata.txp_rxhiring,
1668267580Sjhb	    &sc->sc_ldata.txp_rxhiring_paddr);
1669189714Syongari	/* Low priority Rx ring. */
1670189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_rxloring_tag,
1671267580Sjhb	    sc->sc_cdata.txp_rxloring_map,
1672267580Sjhb	    (void **)&sc->sc_ldata.txp_rxloring,
1673267580Sjhb	    &sc->sc_ldata.txp_rxloring_paddr);
1674189714Syongari	/* Receive buffer ring. */
1675189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_rxbufs_tag,
1676267580Sjhb	    sc->sc_cdata.txp_rxbufs_map, (void **)&sc->sc_ldata.txp_rxbufs,
1677267580Sjhb	    &sc->sc_ldata.txp_rxbufs_paddr);
1678189714Syongari	/* Command ring. */
1679189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_cmdring_tag,
1680267580Sjhb	    sc->sc_cdata.txp_cmdring_map, (void **)&sc->sc_ldata.txp_cmdring,
1681267580Sjhb	    &sc->sc_ldata.txp_cmdring_paddr);
1682189714Syongari	/* Response ring. */
1683189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_rspring_tag,
1684267580Sjhb	    sc->sc_cdata.txp_rspring_map, (void **)&sc->sc_ldata.txp_rspring,
1685267580Sjhb	    &sc->sc_ldata.txp_rspring_paddr);
1686189714Syongari	/* Zero ring. */
1687189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_zero_tag,
1688267580Sjhb	    sc->sc_cdata.txp_zero_map, (void **)&sc->sc_ldata.txp_zero,
1689267580Sjhb	    &sc->sc_ldata.txp_zero_paddr);
1690189714Syongari	/* Host variables. */
1691189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_hostvar_tag,
1692267580Sjhb	    sc->sc_cdata.txp_hostvar_map, (void **)&sc->sc_ldata.txp_hostvar,
1693267580Sjhb	    &sc->sc_ldata.txp_hostvar_paddr);
1694189714Syongari	/* Boot record. */
1695189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_boot_tag,
1696267580Sjhb	    sc->sc_cdata.txp_boot_map, (void **)&sc->sc_ldata.txp_boot,
1697267580Sjhb	    &sc->sc_ldata.txp_boot_paddr);
169880219Swpaul
1699189714Syongari	if (sc->sc_cdata.txp_parent_tag != NULL) {
1700189714Syongari		bus_dma_tag_destroy(sc->sc_cdata.txp_parent_tag);
1701189714Syongari		sc->sc_cdata.txp_parent_tag = NULL;
1702189714Syongari	}
1703189714Syongari
170480219Swpaul}
170580219Swpaul
170680219Swpaulstatic int
1707189685Syongaritxp_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
170880219Swpaul{
170980219Swpaul	struct txp_softc *sc = ifp->if_softc;
171080219Swpaul	struct ifreq *ifr = (struct ifreq *)data;
1711189714Syongari	int capenable, error = 0, mask;
171280219Swpaul
1713189714Syongari	switch(command) {
171480219Swpaul	case SIOCSIFFLAGS:
1715151772Sjhb		TXP_LOCK(sc);
1716189714Syongari		if ((ifp->if_flags & IFF_UP) != 0) {
1717189714Syongari			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
1718189714Syongari				if (((ifp->if_flags ^ sc->sc_if_flags)
1719189714Syongari				    & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
1720189714Syongari					txp_set_filter(sc);
1721189714Syongari			} else {
1722189714Syongari				if ((sc->sc_flags & TXP_FLAG_DETACH) == 0)
1723189714Syongari					txp_init_locked(sc);
1724189714Syongari			}
172580219Swpaul		} else {
1726189714Syongari			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
172780219Swpaul				txp_stop(sc);
172880219Swpaul		}
1729189714Syongari		sc->sc_if_flags = ifp->if_flags;
1730151772Sjhb		TXP_UNLOCK(sc);
173180219Swpaul		break;
173280219Swpaul	case SIOCADDMULTI:
173380219Swpaul	case SIOCDELMULTI:
173480219Swpaul		/*
173580219Swpaul		 * Multicast list has changed; set the hardware
173680219Swpaul		 * filter accordingly.
173780219Swpaul		 */
1738151772Sjhb		TXP_LOCK(sc);
1739189714Syongari		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
1740189714Syongari			txp_set_filter(sc);
1741151772Sjhb		TXP_UNLOCK(sc);
174280219Swpaul		break;
1743189714Syongari	case SIOCSIFCAP:
1744189714Syongari		TXP_LOCK(sc);
1745189714Syongari		capenable = ifp->if_capenable;
1746189714Syongari		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
1747189714Syongari		if ((mask & IFCAP_TXCSUM) != 0 &&
1748189714Syongari		    (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
1749189714Syongari			ifp->if_capenable ^= IFCAP_TXCSUM;
1750189714Syongari			if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
1751189714Syongari				ifp->if_hwassist |= TXP_CSUM_FEATURES;
1752189714Syongari			else
1753189714Syongari				ifp->if_hwassist &= ~TXP_CSUM_FEATURES;
1754189714Syongari		}
1755189714Syongari		if ((mask & IFCAP_RXCSUM) != 0 &&
1756189714Syongari		    (ifp->if_capabilities & IFCAP_RXCSUM) != 0)
1757189714Syongari			ifp->if_capenable ^= IFCAP_RXCSUM;
1758189714Syongari		if ((mask & IFCAP_WOL_MAGIC) != 0 &&
1759189714Syongari		    (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0)
1760189714Syongari			ifp->if_capenable ^= IFCAP_WOL_MAGIC;
1761189714Syongari		if ((mask & IFCAP_VLAN_HWTAGGING) != 0 &&
1762189714Syongari		    (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0)
1763189714Syongari			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
1764189714Syongari		if ((mask & IFCAP_VLAN_HWCSUM) != 0 &&
1765189714Syongari		    (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0)
1766189714Syongari			ifp->if_capenable ^= IFCAP_VLAN_HWCSUM;
1767189714Syongari		if ((ifp->if_capenable & IFCAP_TXCSUM) == 0)
1768189714Syongari			ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM;
1769189714Syongari		if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
1770189714Syongari			ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM;
1771189714Syongari		if (capenable != ifp->if_capenable)
1772189714Syongari			txp_set_capabilities(sc);
1773189714Syongari		TXP_UNLOCK(sc);
1774189714Syongari		VLAN_CAPABILITIES(ifp);
1775189714Syongari		break;
177680219Swpaul	case SIOCGIFMEDIA:
177780219Swpaul	case SIOCSIFMEDIA:
177880219Swpaul		error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, command);
177980219Swpaul		break;
178080219Swpaul	default:
1781106937Ssam		error = ether_ioctl(ifp, command, data);
178280219Swpaul		break;
178380219Swpaul	}
178480219Swpaul
1785189688Syongari	return (error);
178680219Swpaul}
178780219Swpaul
178880219Swpaulstatic int
1789189685Syongaritxp_rxring_fill(struct txp_softc *sc)
179080219Swpaul{
1791189714Syongari	struct txp_rxbuf_desc *rbd;
1792189714Syongari	struct txp_rx_swdesc *sd;
1793189714Syongari	bus_dma_segment_t segs[1];
1794189714Syongari	int error, i, nsegs;
179580219Swpaul
1796151772Sjhb	TXP_LOCK_ASSERT(sc);
179780219Swpaul
1798189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag,
1799189714Syongari	    sc->sc_cdata.txp_rxbufs_map,
1800189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1801189714Syongari
180280219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
1803189714Syongari		sd = TAILQ_FIRST(&sc->sc_free_list);
1804189714Syongari		if (sd == NULL)
1805189714Syongari			return (ENOMEM);
1806189714Syongari		rbd = sc->sc_rxbufs + i;
1807189714Syongari		bcopy(&sd, (u_long *)&rbd->rb_vaddrlo, sizeof(sd));
1808189714Syongari		KASSERT(sd->sd_mbuf == NULL,
1809189714Syongari		    ("%s : Rx buffer ring corrupted", __func__));
1810243857Sglebius		sd->sd_mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
181180219Swpaul		if (sd->sd_mbuf == NULL)
1812189714Syongari			return (ENOMEM);
181380219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
1814189714Syongari#ifndef __NO_STRICT_ALIGNMENT
1815189714Syongari		m_adj(sd->sd_mbuf, TXP_RXBUF_ALIGN);
1816189714Syongari#endif
1817189714Syongari		if ((error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_rx_tag,
1818189714Syongari		    sd->sd_map, sd->sd_mbuf, segs, &nsegs, 0)) != 0) {
1819189714Syongari			m_freem(sd->sd_mbuf);
1820189714Syongari			sd->sd_mbuf = NULL;
1821189714Syongari			return (error);
1822189714Syongari		}
1823189714Syongari		KASSERT(nsegs == 1, ("%s : %d segments returned!", __func__,
1824189714Syongari		    nsegs));
1825189714Syongari		TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next);
1826189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_busy_list, sd, sd_next);
1827189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map,
1828189714Syongari		    BUS_DMASYNC_PREREAD);
1829189714Syongari		rbd->rb_paddrlo = htole32(TXP_ADDR_LO(segs[0].ds_addr));
1830189714Syongari		rbd->rb_paddrhi = htole32(TXP_ADDR_HI(segs[0].ds_addr));
183180219Swpaul	}
183280219Swpaul
1833189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag,
1834189714Syongari	    sc->sc_cdata.txp_rxbufs_map,
1835189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1836189714Syongari	sc->sc_rxbufprod = RXBUF_ENTRIES - 1;
1837189714Syongari	sc->sc_hostvar->hv_rx_buf_write_idx =
1838189714Syongari	    htole32(TXP_IDX2OFFSET(RXBUF_ENTRIES - 1));
183980219Swpaul
1840189688Syongari	return (0);
184180219Swpaul}
184280219Swpaul
184380219Swpaulstatic void
1844189685Syongaritxp_rxring_empty(struct txp_softc *sc)
184580219Swpaul{
1846189714Syongari	struct txp_rx_swdesc *sd;
1847189714Syongari	int cnt;
184880219Swpaul
1849151772Sjhb	TXP_LOCK_ASSERT(sc);
1850189714Syongari
185180219Swpaul	if (sc->sc_rxbufs == NULL)
185280219Swpaul		return;
1853189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1854189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1855189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
185680219Swpaul
1857189714Syongari	/* Release allocated Rx buffers. */
1858189714Syongari	cnt = 0;
1859189714Syongari	while ((sd = TAILQ_FIRST(&sc->sc_busy_list)) != NULL) {
1860189714Syongari		TAILQ_REMOVE(&sc->sc_busy_list, sd, sd_next);
1861189714Syongari		KASSERT(sd->sd_mbuf != NULL,
1862189714Syongari		    ("%s : Rx buffer ring corrupted", __func__));
1863189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map,
1864189714Syongari		    BUS_DMASYNC_POSTREAD);
1865189714Syongari		bus_dmamap_unload(sc->sc_cdata.txp_rx_tag, sd->sd_map);
1866189714Syongari		m_freem(sd->sd_mbuf);
1867189714Syongari		sd->sd_mbuf = NULL;
1868189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next);
1869189714Syongari		cnt++;
187080219Swpaul	}
187180219Swpaul}
187280219Swpaul
187380219Swpaulstatic void
1874189685Syongaritxp_init(void *xsc)
187580219Swpaul{
187680219Swpaul	struct txp_softc *sc;
1877151772Sjhb
1878151772Sjhb	sc = xsc;
1879151772Sjhb	TXP_LOCK(sc);
1880151772Sjhb	txp_init_locked(sc);
1881151772Sjhb	TXP_UNLOCK(sc);
1882151772Sjhb}
1883151772Sjhb
1884151772Sjhbstatic void
1885189685Syongaritxp_init_locked(struct txp_softc *sc)
1886151772Sjhb{
188780219Swpaul	struct ifnet *ifp;
1888189714Syongari	uint8_t *eaddr;
1889189689Syongari	uint16_t p1;
1890189689Syongari	uint32_t p2;
1891189714Syongari	int error;
189280219Swpaul
1893151772Sjhb	TXP_LOCK_ASSERT(sc);
1894147256Sbrooks	ifp = sc->sc_ifp;
189580219Swpaul
1896189714Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
189780219Swpaul		return;
189880219Swpaul
1899189714Syongari	/* Initialize ring structure. */
1900189714Syongari	txp_init_rings(sc);
1901189714Syongari	/* Wakeup controller. */
1902189714Syongari	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_WAKEUP);
1903189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
1904189714Syongari	/*
1905189714Syongari	 * It seems that earlier NV image can go back to online from
1906189714Syongari	 * wakeup command but newer ones require controller reset.
1907189714Syongari	 * So jut reset controller again.
1908189714Syongari	 */
1909189714Syongari	if (txp_reset(sc) != 0)
1910189714Syongari		goto init_fail;
1911189714Syongari	/* Download firmware. */
1912189714Syongari	error = txp_download_fw(sc);
1913189714Syongari	if (error != 0) {
1914189714Syongari		device_printf(sc->sc_dev, "could not download firmware.\n");
1915189714Syongari		goto init_fail;
1916189714Syongari	}
1917189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1918189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1919189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1920189714Syongari	if ((error = txp_rxring_fill(sc)) != 0) {
1921189714Syongari		device_printf(sc->sc_dev, "no memory for Rx buffers.\n");
1922189714Syongari		goto init_fail;
1923189714Syongari	}
1924189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1925189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1926189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1927189714Syongari	if (txp_boot(sc, STAT_WAITING_FOR_BOOT) != 0) {
1928189714Syongari		device_printf(sc->sc_dev, "could not boot firmware.\n");
1929189714Syongari		goto init_fail;
1930189714Syongari	}
193180219Swpaul
1932189714Syongari	/*
1933189714Syongari	 * Quite contrary to Typhoon T2 software functional specification,
1934189714Syongari	 * it seems that TXP_CMD_RECV_BUFFER_CONTROL command is not
1935189714Syongari	 * implemented in the firmware. This means driver should have to
1936189714Syongari	 * handle misaligned frames on alignment architectures. AFAIK this
1937189714Syongari	 * is the only controller manufactured by 3Com that has this stupid
1938189714Syongari	 * bug. 3Com should fix this.
1939189714Syongari	 */
1940189714Syongari	if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
1941189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0)
1942189714Syongari		goto init_fail;
1943189714Syongari	/* Undocumented command(interrupt coalescing disable?) - From Linux. */
1944189714Syongari	if (txp_command(sc, TXP_CMD_FILTER_DEFINE, 0, 0, 0, NULL, NULL, NULL,
1945189714Syongari	    TXP_CMD_NOWAIT) != 0)
1946189714Syongari		goto init_fail;
194780219Swpaul
194880219Swpaul	/* Set station address. */
1949189714Syongari	eaddr = IF_LLADDR(sc->sc_ifp);
1950189714Syongari	p1 = 0;
1951189714Syongari	((uint8_t *)&p1)[1] = eaddr[0];
1952189714Syongari	((uint8_t *)&p1)[0] = eaddr[1];
1953189714Syongari	p1 = le16toh(p1);
1954189714Syongari	((uint8_t *)&p2)[3] = eaddr[2];
1955189714Syongari	((uint8_t *)&p2)[2] = eaddr[3];
1956189714Syongari	((uint8_t *)&p2)[1] = eaddr[4];
1957189714Syongari	((uint8_t *)&p2)[0] = eaddr[5];
1958189714Syongari	p2 = le32toh(p2);
1959189714Syongari	if (txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0,
1960189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0)
1961189714Syongari		goto init_fail;
196280219Swpaul
196380219Swpaul	txp_set_filter(sc);
1964189714Syongari	txp_set_capabilities(sc);
196580219Swpaul
1966189714Syongari	if (txp_command(sc, TXP_CMD_CLEAR_STATISTICS, 0, 0, 0,
1967189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT))
1968189714Syongari		goto init_fail;
1969189714Syongari	if (txp_command(sc, TXP_CMD_XCVR_SELECT, sc->sc_xcvr, 0, 0,
1970189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0)
1971189714Syongari		goto init_fail;
1972189714Syongari	if (txp_command(sc, TXP_CMD_TX_ENABLE, 0, 0, 0, NULL, NULL, NULL,
1973189714Syongari	    TXP_CMD_NOWAIT) != 0)
1974189714Syongari		goto init_fail;
1975189714Syongari	if (txp_command(sc, TXP_CMD_RX_ENABLE, 0, 0, 0, NULL, NULL, NULL,
1976189714Syongari	    TXP_CMD_NOWAIT) != 0)
1977189714Syongari		goto init_fail;
197880219Swpaul
1979189714Syongari	/* Ack all pending interrupts and enable interrupts. */
1980189714Syongari	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
1981189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTRS);
1982189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_NONE);
198380219Swpaul
1984148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
1985148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
198680219Swpaul
1987151772Sjhb	callout_reset(&sc->sc_tick, hz, txp_tick, sc);
1988189714Syongari	return;
1989189714Syongari
1990189714Syongariinit_fail:
1991189714Syongari	txp_rxring_empty(sc);
1992189714Syongari	txp_init_rings(sc);
1993189714Syongari	txp_reset(sc);
1994189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
199580219Swpaul}
199680219Swpaul
199780219Swpaulstatic void
1998189685Syongaritxp_tick(void *vsc)
199980219Swpaul{
2000189714Syongari	struct txp_softc *sc;
2001189714Syongari	struct ifnet *ifp;
2002189714Syongari	struct txp_rsp_desc *rsp;
200380219Swpaul	struct txp_ext_desc *ext;
2004189714Syongari	int link;
200580219Swpaul
2006189714Syongari	sc = vsc;
2007151772Sjhb	TXP_LOCK_ASSERT(sc);
2008189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2009189714Syongari	    sc->sc_cdata.txp_hostvar_map,
2010189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
201180219Swpaul	txp_rxbuf_reclaim(sc);
2012189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2013189714Syongari	    sc->sc_cdata.txp_hostvar_map,
2014189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
201580219Swpaul
2016189714Syongari	ifp = sc->sc_ifp;
2017189714Syongari	rsp = NULL;
2018189714Syongari
2019189714Syongari	link = sc->sc_flags & TXP_FLAG_LINK;
2020189714Syongari	if (txp_ext_command(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0,
2021189714Syongari	    &rsp, TXP_CMD_WAIT))
202280219Swpaul		goto out;
202380219Swpaul	if (rsp->rsp_numdesc != 6)
202480219Swpaul		goto out;
2025189714Syongari	txp_stats_update(sc, rsp);
2026189714Syongari	if (link == 0 && (sc->sc_flags & TXP_FLAG_LINK) != 0) {
2027189714Syongari		ext = (struct txp_ext_desc *)(rsp + 1);
2028189714Syongari		/* Update baudrate with resolved speed. */
2029189714Syongari		if ((ext[5].ext_2 & 0x02) != 0)
2030189714Syongari			ifp->if_baudrate = IF_Mbps(100);
2031189714Syongari		else
2032189714Syongari			ifp->if_baudrate = IF_Mbps(10);
2033189714Syongari	}
203480219Swpaul
203580219Swpaulout:
203680219Swpaul	if (rsp != NULL)
203780219Swpaul		free(rsp, M_DEVBUF);
2038189714Syongari	txp_watchdog(sc);
2039151772Sjhb	callout_reset(&sc->sc_tick, hz, txp_tick, sc);
204080219Swpaul}
204180219Swpaul
204280219Swpaulstatic void
2043189685Syongaritxp_start(struct ifnet *ifp)
204480219Swpaul{
2045151772Sjhb	struct txp_softc *sc;
2046151772Sjhb
2047151772Sjhb	sc = ifp->if_softc;
2048151772Sjhb	TXP_LOCK(sc);
2049151772Sjhb	txp_start_locked(ifp);
2050151772Sjhb	TXP_UNLOCK(sc);
2051151772Sjhb}
2052151772Sjhb
2053151772Sjhbstatic void
2054189685Syongaritxp_start_locked(struct ifnet *ifp)
2055151772Sjhb{
2056189714Syongari	struct txp_softc *sc;
2057189714Syongari	struct mbuf *m_head;
2058189714Syongari	int enq;
205980219Swpaul
2060189714Syongari	sc = ifp->if_softc;
2061151772Sjhb	TXP_LOCK_ASSERT(sc);
2062189714Syongari
2063148887Srwatson	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
2064189714Syongari	   IFF_DRV_RUNNING || (sc->sc_flags & TXP_FLAG_LINK) == 0)
206580219Swpaul		return;
206680219Swpaul
2067189714Syongari	for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) {
2068189714Syongari		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
2069189714Syongari		if (m_head == NULL)
207080219Swpaul			break;
2071189714Syongari		/*
2072189714Syongari		 * Pack the data into the transmit ring. If we
2073189714Syongari		 * don't have room, set the OACTIVE flag and wait
2074189714Syongari		 * for the NIC to drain the ring.
2075189714Syongari		 * ATM only Hi-ring is used.
2076189714Syongari		 */
2077189714Syongari		if (txp_encap(sc, &sc->sc_txhir, &m_head)) {
2078189714Syongari			if (m_head == NULL)
2079189714Syongari				break;
2080189714Syongari			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
2081189714Syongari			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2082189714Syongari			break;
2083189714Syongari		}
208480219Swpaul
2085189714Syongari		/*
2086189714Syongari		 * If there's a BPF listener, bounce a copy of this frame
2087189714Syongari		 * to him.
2088189714Syongari		 */
2089189714Syongari		ETHER_BPF_MTAP(ifp, m_head);
209080219Swpaul
2091189714Syongari		/* Send queued frame. */
2092189714Syongari		WRITE_REG(sc, sc->sc_txhir.r_reg,
2093189714Syongari		    TXP_IDX2OFFSET(sc->sc_txhir.r_prod));
2094189714Syongari	}
209580219Swpaul
2096189714Syongari	if (enq > 0) {
2097189714Syongari		/* Set a timeout in case the chip goes out to lunch. */
2098189714Syongari		sc->sc_watchdog_timer = TXP_TX_TIMEOUT;
2099189714Syongari	}
2100189714Syongari}
210180219Swpaul
2102189714Syongaristatic int
2103189714Syongaritxp_encap(struct txp_softc *sc, struct txp_tx_ring *r, struct mbuf **m_head)
2104189714Syongari{
2105189714Syongari	struct txp_tx_desc *first_txd;
2106189714Syongari	struct txp_frag_desc *fxd;
2107189714Syongari	struct txp_swdesc *sd;
2108189714Syongari	struct mbuf *m;
2109189714Syongari	bus_dma_segment_t txsegs[TXP_MAXTXSEGS];
2110189714Syongari	int error, i, nsegs;
211180219Swpaul
2112189714Syongari	TXP_LOCK_ASSERT(sc);
211380219Swpaul
2114189714Syongari	M_ASSERTPKTHDR((*m_head));
211580219Swpaul
2116189714Syongari	m = *m_head;
2117189714Syongari	first_txd = r->r_desc + r->r_prod;
2118189714Syongari	sd = sc->sc_txd + r->r_prod;
211980219Swpaul
2120189714Syongari	error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_tx_tag, sd->sd_map,
2121189714Syongari	    *m_head, txsegs, &nsegs, 0);
2122189714Syongari	if (error == EFBIG) {
2123243857Sglebius		m = m_collapse(*m_head, M_NOWAIT, TXP_MAXTXSEGS);
2124189714Syongari		if (m == NULL) {
2125189714Syongari			m_freem(*m_head);
2126189714Syongari			*m_head = NULL;
2127189714Syongari			return (ENOMEM);
212880219Swpaul		}
2129189714Syongari		*m_head = m;
2130189714Syongari		error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_tx_tag,
2131189714Syongari		    sd->sd_map, *m_head, txsegs, &nsegs, 0);
2132189714Syongari		if (error != 0) {
2133189714Syongari			m_freem(*m_head);
2134189714Syongari			*m_head = NULL;
2135189714Syongari			return (error);
2136189714Syongari		}
2137189714Syongari	} else if (error != 0)
2138189714Syongari		return (error);
2139189714Syongari	if (nsegs == 0) {
2140189714Syongari		m_freem(*m_head);
2141189714Syongari		*m_head = NULL;
2142189714Syongari		return (EIO);
2143189714Syongari	}
214483115Sbrooks
2145189714Syongari	/* Check descriptor overrun. */
2146189714Syongari	if (r->r_cnt + nsegs >= TX_ENTRIES - TXP_TXD_RESERVED) {
2147189714Syongari		bus_dmamap_unload(sc->sc_cdata.txp_tx_tag, sd->sd_map);
2148189714Syongari		return (ENOBUFS);
2149189714Syongari	}
2150189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_tx_tag, sd->sd_map,
2151189714Syongari	    BUS_DMASYNC_PREWRITE);
2152189714Syongari	sd->sd_mbuf = m;
215380219Swpaul
2154189714Syongari	first_txd->tx_flags = TX_FLAGS_TYPE_DATA;
2155189714Syongari	first_txd->tx_numdesc = 0;
2156189714Syongari	first_txd->tx_addrlo = 0;
2157189714Syongari	first_txd->tx_addrhi = 0;
2158189714Syongari	first_txd->tx_totlen = 0;
2159189714Syongari	first_txd->tx_pflags = 0;
2160189714Syongari	r->r_cnt++;
2161189714Syongari	TXP_DESC_INC(r->r_prod, TX_ENTRIES);
2162189714Syongari
2163189714Syongari	/* Configure Tx IP/TCP/UDP checksum offload. */
2164189714Syongari	if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0)
2165189714Syongari		first_txd->tx_pflags |= htole32(TX_PFLAGS_IPCKSUM);
2166189714Syongari#ifdef notyet
2167189714Syongari	/* XXX firmware bug. */
2168189714Syongari	if ((m->m_pkthdr.csum_flags & CSUM_TCP) != 0)
2169189714Syongari		first_txd->tx_pflags |= htole32(TX_PFLAGS_TCPCKSUM);
2170189714Syongari	if ((m->m_pkthdr.csum_flags & CSUM_UDP) != 0)
2171189714Syongari		first_txd->tx_pflags |= htole32(TX_PFLAGS_UDPCKSUM);
217280219Swpaul#endif
217380219Swpaul
2174189714Syongari	/* Configure VLAN hardware tag insertion. */
2175189714Syongari	if ((m->m_flags & M_VLANTAG) != 0)
2176189714Syongari		first_txd->tx_pflags |=
2177189714Syongari		    htole32(TX_PFLAGS_VLAN | TX_PFLAGS_PRIO |
2178189714Syongari		    (bswap16(m->m_pkthdr.ether_vtag) << TX_PFLAGS_VLANTAG_S));
217980219Swpaul
2180189714Syongari	for (i = 0; i < nsegs; i++) {
2181189714Syongari		fxd = (struct txp_frag_desc *)(r->r_desc + r->r_prod);
2182189714Syongari		fxd->frag_flags = FRAG_FLAGS_TYPE_FRAG | TX_FLAGS_VALID;
2183189714Syongari		fxd->frag_rsvd1 = 0;
2184189714Syongari		fxd->frag_len = htole16(txsegs[i].ds_len);
2185189714Syongari		fxd->frag_addrhi = htole32(TXP_ADDR_HI(txsegs[i].ds_addr));
2186189714Syongari		fxd->frag_addrlo = htole32(TXP_ADDR_LO(txsegs[i].ds_addr));
2187189714Syongari		fxd->frag_rsvd2 = 0;
2188189714Syongari		first_txd->tx_numdesc++;
2189189714Syongari		r->r_cnt++;
2190189714Syongari		TXP_DESC_INC(r->r_prod, TX_ENTRIES);
2191189714Syongari	}
219280219Swpaul
2193189714Syongari	/* Lastly set valid flag. */
2194189714Syongari	first_txd->tx_flags |= TX_FLAGS_VALID;
219580219Swpaul
2196189714Syongari	/* Sync descriptors. */
2197189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map,
2198189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
219980219Swpaul
2200189714Syongari	return (0);
220180219Swpaul}
220280219Swpaul
220380219Swpaul/*
220480219Swpaul * Handle simple commands sent to the typhoon
220580219Swpaul */
220680219Swpaulstatic int
2207189689Syongaritxp_command(struct txp_softc *sc, uint16_t id, uint16_t in1, uint32_t in2,
2208189689Syongari    uint32_t in3, uint16_t *out1, uint32_t *out2, uint32_t *out3, int wait)
220980219Swpaul{
2210189714Syongari	struct txp_rsp_desc *rsp;
221180219Swpaul
2212189714Syongari	rsp = NULL;
2213189714Syongari	if (txp_ext_command(sc, id, in1, in2, in3, NULL, 0, &rsp, wait) != 0) {
2214189714Syongari		device_printf(sc->sc_dev, "command 0x%02x failed\n", id);
221580219Swpaul		return (-1);
2216189714Syongari	}
221780219Swpaul
2218189714Syongari	if (wait == TXP_CMD_NOWAIT)
221980219Swpaul		return (0);
222080219Swpaul
2221189714Syongari	KASSERT(rsp != NULL, ("rsp is NULL!\n"));
222280219Swpaul	if (out1 != NULL)
2223189714Syongari		*out1 = le16toh(rsp->rsp_par1);
222480219Swpaul	if (out2 != NULL)
2225189714Syongari		*out2 = le32toh(rsp->rsp_par2);
222680219Swpaul	if (out3 != NULL)
2227189714Syongari		*out3 = le32toh(rsp->rsp_par3);
222880219Swpaul	free(rsp, M_DEVBUF);
222980219Swpaul	return (0);
223080219Swpaul}
223180219Swpaul
223280219Swpaulstatic int
2233189714Syongaritxp_ext_command(struct txp_softc *sc, uint16_t id, uint16_t in1, uint32_t in2,
2234189689Syongari    uint32_t in3, struct txp_ext_desc *in_extp, uint8_t in_extn,
2235189004Srdivacky    struct txp_rsp_desc **rspp, int wait)
223680219Swpaul{
2237189714Syongari	struct txp_hostvar *hv;
223880219Swpaul	struct txp_cmd_desc *cmd;
223980219Swpaul	struct txp_ext_desc *ext;
2240189689Syongari	uint32_t idx, i;
2241189689Syongari	uint16_t seq;
2242189714Syongari	int error;
224380219Swpaul
2244189714Syongari	error = 0;
2245189714Syongari	hv = sc->sc_hostvar;
224680219Swpaul	if (txp_cmd_desc_numfree(sc) < (in_extn + 1)) {
2247189714Syongari		device_printf(sc->sc_dev,
2248189714Syongari		    "%s : out of free cmd descriptors for command 0x%02x\n",
2249189714Syongari		    __func__, id);
2250189714Syongari		return (ENOBUFS);
225180219Swpaul	}
225280219Swpaul
2253189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_cmdring_tag,
2254189714Syongari	    sc->sc_cdata.txp_cmdring_map, BUS_DMASYNC_POSTWRITE);
225580219Swpaul	idx = sc->sc_cmdring.lastwrite;
2256189689Syongari	cmd = (struct txp_cmd_desc *)(((uint8_t *)sc->sc_cmdring.base) + idx);
225780219Swpaul	bzero(cmd, sizeof(*cmd));
225880219Swpaul
225980219Swpaul	cmd->cmd_numdesc = in_extn;
2260189714Syongari	seq = sc->sc_seq++;
2261189714Syongari	cmd->cmd_seq = htole16(seq);
2262189714Syongari	cmd->cmd_id = htole16(id);
2263189714Syongari	cmd->cmd_par1 = htole16(in1);
2264189714Syongari	cmd->cmd_par2 = htole32(in2);
2265189714Syongari	cmd->cmd_par3 = htole32(in3);
226680219Swpaul	cmd->cmd_flags = CMD_FLAGS_TYPE_CMD |
2267189714Syongari	    (wait == TXP_CMD_WAIT ? CMD_FLAGS_RESP : 0) | CMD_FLAGS_VALID;
226880219Swpaul
226980219Swpaul	idx += sizeof(struct txp_cmd_desc);
227080219Swpaul	if (idx == sc->sc_cmdring.size)
227180219Swpaul		idx = 0;
227280219Swpaul
227380219Swpaul	for (i = 0; i < in_extn; i++) {
2274189689Syongari		ext = (struct txp_ext_desc *)(((uint8_t *)sc->sc_cmdring.base) + idx);
227580219Swpaul		bcopy(in_extp, ext, sizeof(struct txp_ext_desc));
227680219Swpaul		in_extp++;
227780219Swpaul		idx += sizeof(struct txp_cmd_desc);
227880219Swpaul		if (idx == sc->sc_cmdring.size)
227980219Swpaul			idx = 0;
228080219Swpaul	}
228180219Swpaul
228280219Swpaul	sc->sc_cmdring.lastwrite = idx;
2283189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_cmdring_tag,
2284189714Syongari	    sc->sc_cdata.txp_cmdring_map, BUS_DMASYNC_PREWRITE);
2285189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2286189714Syongari	    sc->sc_cdata.txp_hostvar_map, BUS_DMASYNC_PREREAD |
2287189714Syongari	    BUS_DMASYNC_PREWRITE);
228880219Swpaul	WRITE_REG(sc, TXP_H2A_2, sc->sc_cmdring.lastwrite);
2289189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
229080219Swpaul
2291189714Syongari	if (wait == TXP_CMD_NOWAIT)
229280219Swpaul		return (0);
229380219Swpaul
2294189714Syongari	for (i = 0; i < TXP_TIMEOUT; i++) {
2295189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2296189714Syongari		    sc->sc_cdata.txp_hostvar_map, BUS_DMASYNC_POSTREAD |
2297189714Syongari		    BUS_DMASYNC_POSTWRITE);
2298189714Syongari		if (le32toh(hv->hv_resp_read_idx) !=
2299189714Syongari		    le32toh(hv->hv_resp_write_idx)) {
2300189714Syongari			error = txp_response(sc, id, seq, rspp);
2301189714Syongari			bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2302189714Syongari			    sc->sc_cdata.txp_hostvar_map,
2303189714Syongari			    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
2304189714Syongari			if (error != 0)
2305189714Syongari				return (error);
2306189714Syongari 			if (*rspp != NULL)
230780219Swpaul				break;
230880219Swpaul		}
230980219Swpaul		DELAY(50);
231080219Swpaul	}
2311189714Syongari	if (i == TXP_TIMEOUT) {
2312189714Syongari		device_printf(sc->sc_dev, "command 0x%02x timedout\n", id);
2313189714Syongari		error = ETIMEDOUT;
231480219Swpaul	}
231580219Swpaul
2316189714Syongari	return (error);
231780219Swpaul}
231880219Swpaul
231980219Swpaulstatic int
2320189714Syongaritxp_response(struct txp_softc *sc, uint16_t id, uint16_t seq,
2321189004Srdivacky    struct txp_rsp_desc **rspp)
232280219Swpaul{
2323189714Syongari	struct txp_hostvar *hv;
232480219Swpaul	struct txp_rsp_desc *rsp;
2325189714Syongari	uint32_t ridx;
232680219Swpaul
2327189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rspring_tag,
2328189714Syongari	    sc->sc_cdata.txp_rspring_map, BUS_DMASYNC_POSTREAD);
2329189714Syongari	hv = sc->sc_hostvar;
2330189714Syongari	ridx = le32toh(hv->hv_resp_read_idx);
2331189714Syongari	while (ridx != le32toh(hv->hv_resp_write_idx)) {
2332189689Syongari		rsp = (struct txp_rsp_desc *)(((uint8_t *)sc->sc_rspring.base) + ridx);
233380219Swpaul
2334189714Syongari		if (id == le16toh(rsp->rsp_id) &&
2335189714Syongari		    le16toh(rsp->rsp_seq) == seq) {
233680219Swpaul			*rspp = (struct txp_rsp_desc *)malloc(
233780219Swpaul			    sizeof(struct txp_rsp_desc) * (rsp->rsp_numdesc + 1),
233880219Swpaul			    M_DEVBUF, M_NOWAIT);
2339189714Syongari			if (*rspp == NULL) {
2340189714Syongari				device_printf(sc->sc_dev,"%s : command 0x%02x "
2341189714Syongari				    "memory allocation failure\n",
2342189714Syongari				    __func__, id);
2343189714Syongari				return (ENOMEM);
2344189714Syongari			}
234580219Swpaul			txp_rsp_fixup(sc, rsp, *rspp);
234680219Swpaul			return (0);
234780219Swpaul		}
234880219Swpaul
2349189714Syongari		if ((rsp->rsp_flags & RSP_FLAGS_ERROR) != 0) {
2350189714Syongari			device_printf(sc->sc_dev,
2351189714Syongari			    "%s : command 0x%02x response error!\n", __func__,
2352189714Syongari			    le16toh(rsp->rsp_id));
235380219Swpaul			txp_rsp_fixup(sc, rsp, NULL);
2354189714Syongari			ridx = le32toh(hv->hv_resp_read_idx);
235580219Swpaul			continue;
235680219Swpaul		}
235780219Swpaul
2358189714Syongari		/*
2359189714Syongari		 * The following unsolicited responses are handled during
2360189714Syongari		 * processing of TXP_CMD_READ_STATISTICS which requires
2361189714Syongari		 * response. Driver abuses the command to detect media
2362189714Syongari		 * status change.
2363189714Syongari		 * TXP_CMD_FILTER_DEFINE is not an unsolicited response
2364189714Syongari		 * but we don't process response ring in interrupt handler
2365189714Syongari		 * so we have to ignore this command here, otherwise
2366189714Syongari		 * unknown command message would be printed.
2367189714Syongari		 */
2368189714Syongari		switch (le16toh(rsp->rsp_id)) {
236980219Swpaul		case TXP_CMD_CYCLE_STATISTICS:
2370189714Syongari		case TXP_CMD_FILTER_DEFINE:
2371189714Syongari			break;
237280219Swpaul		case TXP_CMD_MEDIA_STATUS_READ:
2373189714Syongari			if ((le16toh(rsp->rsp_par1) & 0x0800) == 0) {
2374189714Syongari				sc->sc_flags |= TXP_FLAG_LINK;
2375189714Syongari				if_link_state_change(sc->sc_ifp,
2376189714Syongari				    LINK_STATE_UP);
2377189714Syongari			} else {
2378189714Syongari				sc->sc_flags &= ~TXP_FLAG_LINK;
2379189714Syongari				if_link_state_change(sc->sc_ifp,
2380189714Syongari				    LINK_STATE_DOWN);
2381189714Syongari			}
238280219Swpaul			break;
238380219Swpaul		case TXP_CMD_HELLO_RESPONSE:
2384189714Syongari			/*
2385189714Syongari			 * Driver should repsond to hello message but
2386189714Syongari			 * TXP_CMD_READ_STATISTICS is issued for every
2387189714Syongari			 * hz, therefore there is no need to send an
2388189714Syongari			 * explicit command here.
2389189714Syongari			 */
2390189714Syongari			device_printf(sc->sc_dev, "%s : hello\n", __func__);
239180219Swpaul			break;
239280219Swpaul		default:
2393189714Syongari			device_printf(sc->sc_dev,
2394189714Syongari			    "%s : unknown command 0x%02x\n", __func__,
2395189714Syongari			    le16toh(rsp->rsp_id));
239680219Swpaul		}
239780219Swpaul		txp_rsp_fixup(sc, rsp, NULL);
2398189714Syongari		ridx = le32toh(hv->hv_resp_read_idx);
239980219Swpaul	}
240080219Swpaul
240180219Swpaul	return (0);
240280219Swpaul}
240380219Swpaul
240480219Swpaulstatic void
2405189685Syongaritxp_rsp_fixup(struct txp_softc *sc, struct txp_rsp_desc *rsp,
2406189685Syongari    struct txp_rsp_desc *dst)
240780219Swpaul{
2408189714Syongari	struct txp_rsp_desc *src;
2409189714Syongari	struct txp_hostvar *hv;
2410189689Syongari	uint32_t i, ridx;
241180219Swpaul
2412189714Syongari	src = rsp;
2413189714Syongari	hv = sc->sc_hostvar;
2414189714Syongari	ridx = le32toh(hv->hv_resp_read_idx);
241580219Swpaul
241680219Swpaul	for (i = 0; i < rsp->rsp_numdesc + 1; i++) {
241780219Swpaul		if (dst != NULL)
241880219Swpaul			bcopy(src, dst++, sizeof(struct txp_rsp_desc));
241980219Swpaul		ridx += sizeof(struct txp_rsp_desc);
242080219Swpaul		if (ridx == sc->sc_rspring.size) {
242180219Swpaul			src = sc->sc_rspring.base;
242280219Swpaul			ridx = 0;
242380219Swpaul		} else
242480219Swpaul			src++;
2425189714Syongari		sc->sc_rspring.lastwrite = ridx;
242680219Swpaul	}
2427189686Syongari
2428189714Syongari	hv->hv_resp_read_idx = htole32(ridx);
242980219Swpaul}
243080219Swpaul
243180219Swpaulstatic int
2432189685Syongaritxp_cmd_desc_numfree(struct txp_softc *sc)
243380219Swpaul{
2434189714Syongari	struct txp_hostvar *hv;
2435189714Syongari	struct txp_boot_record *br;
2436189689Syongari	uint32_t widx, ridx, nfree;
243780219Swpaul
2438189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2439189714Syongari	    sc->sc_cdata.txp_hostvar_map,
2440189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2441189714Syongari	hv = sc->sc_hostvar;
2442189714Syongari	br = sc->sc_boot;
244380219Swpaul	widx = sc->sc_cmdring.lastwrite;
2444189714Syongari	ridx = le32toh(hv->hv_cmd_read_idx);
244580219Swpaul
244680219Swpaul	if (widx == ridx) {
244780219Swpaul		/* Ring is completely free */
2448189714Syongari		nfree = le32toh(br->br_cmd_siz) - sizeof(struct txp_cmd_desc);
244980219Swpaul	} else {
245080219Swpaul		if (widx > ridx)
2451189714Syongari			nfree = le32toh(br->br_cmd_siz) -
245280219Swpaul			    (widx - ridx + sizeof(struct txp_cmd_desc));
245380219Swpaul		else
245480219Swpaul			nfree = ridx - widx - sizeof(struct txp_cmd_desc);
245580219Swpaul	}
245680219Swpaul
245780219Swpaul	return (nfree / sizeof(struct txp_cmd_desc));
245880219Swpaul}
245980219Swpaul
2460189714Syongaristatic int
2461189714Syongaritxp_sleep(struct txp_softc *sc, int capenable)
2462189714Syongari{
2463189714Syongari	uint16_t events;
2464189714Syongari	int error;
2465189714Syongari
2466189714Syongari	events = 0;
2467189714Syongari	if ((capenable & IFCAP_WOL_MAGIC) != 0)
2468189714Syongari		events |= 0x01;
2469189714Syongari	error = txp_command(sc, TXP_CMD_ENABLE_WAKEUP_EVENTS, events, 0, 0,
2470189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT);
2471189714Syongari	if (error == 0) {
2472189714Syongari		/* Goto sleep. */
2473189714Syongari		error = txp_command(sc, TXP_CMD_GOTO_SLEEP, 0, 0, 0, NULL,
2474189714Syongari		    NULL, NULL, TXP_CMD_NOWAIT);
2475189714Syongari		if (error == 0) {
2476189714Syongari			error = txp_wait(sc, STAT_SLEEPING);
2477189714Syongari			if (error != 0)
2478189714Syongari				device_printf(sc->sc_dev,
2479189714Syongari				    "unable to enter into sleep\n");
2480189714Syongari		}
2481189714Syongari	}
2482189714Syongari
2483189714Syongari	return (error);
2484189714Syongari}
2485189714Syongari
248680219Swpaulstatic void
2487189685Syongaritxp_stop(struct txp_softc *sc)
248880219Swpaul{
248980219Swpaul	struct ifnet *ifp;
249080219Swpaul
2491151772Sjhb	TXP_LOCK_ASSERT(sc);
2492147256Sbrooks	ifp = sc->sc_ifp;
249380219Swpaul
2494189714Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
2495189714Syongari		return;
2496189714Syongari
2497189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
2498189714Syongari	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
2499189714Syongari
2500148887Srwatson	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
2501189714Syongari	sc->sc_flags &= ~TXP_FLAG_LINK;
250280219Swpaul
2503151772Sjhb	callout_stop(&sc->sc_tick);
250480219Swpaul
2505189714Syongari	txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL,
2506189714Syongari	    TXP_CMD_NOWAIT);
2507189714Syongari	txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL,
2508189714Syongari	    TXP_CMD_NOWAIT);
2509189714Syongari	/* Save statistics for later use. */
2510189714Syongari	txp_stats_save(sc);
2511189714Syongari	/* Halt controller. */
2512189714Syongari	txp_command(sc, TXP_CMD_HALT, 0, 0, 0, NULL, NULL, NULL,
2513189714Syongari	    TXP_CMD_NOWAIT);
251480219Swpaul
2515189714Syongari	if (txp_wait(sc, STAT_HALTED) != 0)
2516189714Syongari		device_printf(sc->sc_dev, "controller halt timedout!\n");
2517189714Syongari	/* Reclaim Tx/Rx buffers. */
2518189714Syongari	if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons !=
2519189714Syongari	    TXP_OFFSET2IDX(le32toh(*(sc->sc_txhir.r_off)))))
2520189714Syongari		txp_tx_reclaim(sc, &sc->sc_txhir);
2521189714Syongari	if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons !=
2522189714Syongari	    TXP_OFFSET2IDX(le32toh(*(sc->sc_txlor.r_off)))))
2523189714Syongari		txp_tx_reclaim(sc, &sc->sc_txlor);
252480219Swpaul	txp_rxring_empty(sc);
2525189714Syongari
2526189714Syongari	txp_init_rings(sc);
2527189714Syongari	/* Reset controller and make it reload sleep image. */
2528189714Syongari	txp_reset(sc);
2529189714Syongari	/* Let controller boot from sleep image. */
2530189714Syongari	if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0)
2531189714Syongari		device_printf(sc->sc_dev, "could not boot sleep image\n");
2532189714Syongari	txp_sleep(sc, 0);
253380219Swpaul}
253480219Swpaul
253580219Swpaulstatic void
2536189714Syongaritxp_watchdog(struct txp_softc *sc)
253780219Swpaul{
2538189714Syongari	struct ifnet *ifp;
2539189687Syongari
2540189714Syongari	TXP_LOCK_ASSERT(sc);
2541189714Syongari
2542189714Syongari	if (sc->sc_watchdog_timer == 0 || --sc->sc_watchdog_timer)
2543189714Syongari		return;
2544189714Syongari
2545189714Syongari	ifp = sc->sc_ifp;
2546189714Syongari	if_printf(ifp, "watchdog timeout -- resetting\n");
2547272067Sglebius	if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
2548189714Syongari	txp_stop(sc);
2549189714Syongari	txp_init_locked(sc);
255080219Swpaul}
255180219Swpaul
255280219Swpaulstatic int
2553189685Syongaritxp_ifmedia_upd(struct ifnet *ifp)
255480219Swpaul{
255580219Swpaul	struct txp_softc *sc = ifp->if_softc;
255680219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
2557189689Syongari	uint16_t new_xcvr;
255880219Swpaul
2559151772Sjhb	TXP_LOCK(sc);
2560151772Sjhb	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) {
2561151772Sjhb		TXP_UNLOCK(sc);
256280219Swpaul		return (EINVAL);
2563151772Sjhb	}
256480219Swpaul
256580219Swpaul	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) {
256680219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
256780219Swpaul			new_xcvr = TXP_XCVR_10_FDX;
256880219Swpaul		else
256980219Swpaul			new_xcvr = TXP_XCVR_10_HDX;
257080219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) {
257180219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
257280219Swpaul			new_xcvr = TXP_XCVR_100_FDX;
257380219Swpaul		else
257480219Swpaul			new_xcvr = TXP_XCVR_100_HDX;
257580219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) {
257680219Swpaul		new_xcvr = TXP_XCVR_AUTO;
2577151772Sjhb	} else {
2578151772Sjhb		TXP_UNLOCK(sc);
257980219Swpaul		return (EINVAL);
2580151772Sjhb	}
258180219Swpaul
258280219Swpaul	/* nothing to do */
2583151772Sjhb	if (sc->sc_xcvr == new_xcvr) {
2584151772Sjhb		TXP_UNLOCK(sc);
258580219Swpaul		return (0);
2586151772Sjhb	}
258780219Swpaul
258880219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, new_xcvr, 0, 0,
2589189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT);
259080219Swpaul	sc->sc_xcvr = new_xcvr;
2591151772Sjhb	TXP_UNLOCK(sc);
259280219Swpaul
259380219Swpaul	return (0);
259480219Swpaul}
259580219Swpaul
259680219Swpaulstatic void
2597189685Syongaritxp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
259880219Swpaul{
259980219Swpaul	struct txp_softc *sc = ifp->if_softc;
260080219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
2601189689Syongari	uint16_t bmsr, bmcr, anar, anlpar;
260280219Swpaul
260380219Swpaul	ifmr->ifm_status = IFM_AVALID;
260480219Swpaul	ifmr->ifm_active = IFM_ETHER;
260580219Swpaul
2606151772Sjhb	TXP_LOCK(sc);
2607189714Syongari	/* Check whether firmware is running. */
2608189714Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
2609189714Syongari		goto bail;
261080219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
2611189714Syongari	    &bmsr, NULL, NULL, TXP_CMD_WAIT))
261280219Swpaul		goto bail;
261380219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
2614189714Syongari	    &bmsr, NULL, NULL, TXP_CMD_WAIT))
261580219Swpaul		goto bail;
261680219Swpaul
261780219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMCR, 0,
2618189714Syongari	    &bmcr, NULL, NULL, TXP_CMD_WAIT))
261980219Swpaul		goto bail;
262080219Swpaul
262180219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANLPAR, 0,
2622189714Syongari	    &anlpar, NULL, NULL, TXP_CMD_WAIT))
262380219Swpaul		goto bail;
2624173666Syongari
2625173666Syongari	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANAR, 0,
2626189714Syongari	    &anar, NULL, NULL, TXP_CMD_WAIT))
2627173666Syongari		goto bail;
2628151772Sjhb	TXP_UNLOCK(sc);
262980219Swpaul
263080219Swpaul	if (bmsr & BMSR_LINK)
263180219Swpaul		ifmr->ifm_status |= IFM_ACTIVE;
263280219Swpaul
263380219Swpaul	if (bmcr & BMCR_ISO) {
263480219Swpaul		ifmr->ifm_active |= IFM_NONE;
263580219Swpaul		ifmr->ifm_status = 0;
263680219Swpaul		return;
263780219Swpaul	}
263880219Swpaul
263980219Swpaul	if (bmcr & BMCR_LOOP)
264080219Swpaul		ifmr->ifm_active |= IFM_LOOP;
264180219Swpaul
264280219Swpaul	if (bmcr & BMCR_AUTOEN) {
264380219Swpaul		if ((bmsr & BMSR_ACOMP) == 0) {
264480219Swpaul			ifmr->ifm_active |= IFM_NONE;
264580219Swpaul			return;
264680219Swpaul		}
264780219Swpaul
2648173666Syongari		anlpar &= anar;
2649173665Syongari		if (anlpar & ANLPAR_TX_FD)
2650173665Syongari			ifmr->ifm_active |= IFM_100_TX|IFM_FDX;
2651173665Syongari		else if (anlpar & ANLPAR_T4)
265280219Swpaul			ifmr->ifm_active |= IFM_100_T4;
265380219Swpaul		else if (anlpar & ANLPAR_TX)
265480219Swpaul			ifmr->ifm_active |= IFM_100_TX;
265580219Swpaul		else if (anlpar & ANLPAR_10_FD)
265680219Swpaul			ifmr->ifm_active |= IFM_10_T|IFM_FDX;
265780219Swpaul		else if (anlpar & ANLPAR_10)
265880219Swpaul			ifmr->ifm_active |= IFM_10_T;
265980219Swpaul		else
266080219Swpaul			ifmr->ifm_active |= IFM_NONE;
266180219Swpaul	} else
266280219Swpaul		ifmr->ifm_active = ifm->ifm_cur->ifm_media;
266380219Swpaul	return;
266480219Swpaul
266580219Swpaulbail:
2666151772Sjhb	TXP_UNLOCK(sc);
266780219Swpaul	ifmr->ifm_active |= IFM_NONE;
266880219Swpaul	ifmr->ifm_status &= ~IFM_AVALID;
266980219Swpaul}
267080219Swpaul
267180219Swpaul#ifdef TXP_DEBUG
267280219Swpaulstatic void
2673189685Syongaritxp_show_descriptor(void *d)
267480219Swpaul{
267580219Swpaul	struct txp_cmd_desc *cmd = d;
267680219Swpaul	struct txp_rsp_desc *rsp = d;
267780219Swpaul	struct txp_tx_desc *txd = d;
267880219Swpaul	struct txp_frag_desc *frgd = d;
267980219Swpaul
268080219Swpaul	switch (cmd->cmd_flags & CMD_FLAGS_TYPE_M) {
268180219Swpaul	case CMD_FLAGS_TYPE_CMD:
268280219Swpaul		/* command descriptor */
268380219Swpaul		printf("[cmd flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
2684189714Syongari		    cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id),
2685189714Syongari		    le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1),
2686189714Syongari		    le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3));
268780219Swpaul		break;
268880219Swpaul	case CMD_FLAGS_TYPE_RESP:
268980219Swpaul		/* response descriptor */
269080219Swpaul		printf("[rsp flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
2691189714Syongari		    rsp->rsp_flags, rsp->rsp_numdesc, le16toh(rsp->rsp_id),
2692189714Syongari		    le16toh(rsp->rsp_seq), le16toh(rsp->rsp_par1),
2693189714Syongari		    le32toh(rsp->rsp_par2), le32toh(rsp->rsp_par3));
269480219Swpaul		break;
269580219Swpaul	case CMD_FLAGS_TYPE_DATA:
269680219Swpaul		/* data header (assuming tx for now) */
269780219Swpaul		printf("[data flags 0x%x num %d totlen %d addr 0x%x/0x%x pflags 0x%x]",
2698189714Syongari		    txd->tx_flags, txd->tx_numdesc, le16toh(txd->tx_totlen),
2699189714Syongari		    le32toh(txd->tx_addrlo), le32toh(txd->tx_addrhi),
2700189714Syongari		    le32toh(txd->tx_pflags));
270180219Swpaul		break;
270280219Swpaul	case CMD_FLAGS_TYPE_FRAG:
270380219Swpaul		/* fragment descriptor */
270480219Swpaul		printf("[frag flags 0x%x rsvd1 0x%x len %d addr 0x%x/0x%x rsvd2 0x%x]",
2705189714Syongari		    frgd->frag_flags, frgd->frag_rsvd1, le16toh(frgd->frag_len),
2706189714Syongari		    le32toh(frgd->frag_addrlo), le32toh(frgd->frag_addrhi),
2707189714Syongari		    le32toh(frgd->frag_rsvd2));
270880219Swpaul		break;
270980219Swpaul	default:
271080219Swpaul		printf("[unknown(%x) flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
271180219Swpaul		    cmd->cmd_flags & CMD_FLAGS_TYPE_M,
2712189714Syongari		    cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id),
2713189714Syongari		    le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1),
2714189714Syongari		    le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3));
271580219Swpaul		break;
271680219Swpaul	}
271780219Swpaul}
271880219Swpaul#endif
271980219Swpaul
272080219Swpaulstatic void
2721189685Syongaritxp_set_filter(struct txp_softc *sc)
272280219Swpaul{
2723189690Syongari	struct ifnet *ifp;
2724189690Syongari	uint32_t crc, mchash[2];
2725189689Syongari	uint16_t filter;
272680219Swpaul	struct ifmultiaddr *ifma;
2727189690Syongari	int mcnt;
272880219Swpaul
2729189690Syongari	TXP_LOCK_ASSERT(sc);
273080219Swpaul
2731189690Syongari	ifp = sc->sc_ifp;
273280219Swpaul	filter = TXP_RXFILT_DIRECT;
2733189690Syongari	if ((ifp->if_flags & IFF_BROADCAST) != 0)
273480219Swpaul		filter |= TXP_RXFILT_BROADCAST;
2735189690Syongari	if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) {
2736189690Syongari		if ((ifp->if_flags & IFF_ALLMULTI) != 0)
2737189690Syongari			filter |= TXP_RXFILT_ALLMULTI;
2738189690Syongari		if ((ifp->if_flags & IFF_PROMISC) != 0)
2739189690Syongari			filter = TXP_RXFILT_PROMISC;
2740189690Syongari		goto setit;
2741189690Syongari	}
274280219Swpaul
2743189690Syongari	mchash[0] = mchash[1] = 0;
2744189690Syongari	mcnt = 0;
2745195049Srwatson	if_maddr_rlock(ifp);
2746189690Syongari	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
2747189690Syongari		if (ifma->ifma_addr->sa_family != AF_LINK)
2748189690Syongari			continue;
2749189690Syongari		crc = ether_crc32_be(LLADDR((struct sockaddr_dl *)
2750189690Syongari		    ifma->ifma_addr), ETHER_ADDR_LEN);
2751189690Syongari		crc &= 0x3f;
2752189690Syongari		mchash[crc >> 5] |= 1 << (crc & 0x1f);
2753189690Syongari		mcnt++;
2754189690Syongari	}
2755195049Srwatson	if_maddr_runlock(ifp);
275680219Swpaul
2757189690Syongari	if (mcnt > 0) {
2758189690Syongari		filter |= TXP_RXFILT_HASHMULTI;
2759189690Syongari		txp_command(sc, TXP_CMD_MCAST_HASH_MASK_WRITE, 2, mchash[0],
2760189714Syongari		    mchash[1], NULL, NULL, NULL, TXP_CMD_NOWAIT);
276180219Swpaul	}
276280219Swpaul
276380219Swpaulsetit:
276480219Swpaul	txp_command(sc, TXP_CMD_RX_FILTER_WRITE, filter, 0, 0,
2765189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT);
276680219Swpaul}
276780219Swpaul
2768189714Syongaristatic int
2769189714Syongaritxp_set_capabilities(struct txp_softc *sc)
2770189714Syongari{
2771189714Syongari	struct ifnet *ifp;
2772189714Syongari	uint32_t rxcap, txcap;
2773189714Syongari
2774189714Syongari	TXP_LOCK_ASSERT(sc);
2775189714Syongari
2776189714Syongari	rxcap = txcap = 0;
2777189714Syongari	ifp = sc->sc_ifp;
2778189714Syongari	if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) {
2779189714Syongari		if ((ifp->if_hwassist & CSUM_IP) != 0)
2780189714Syongari			txcap |= OFFLOAD_IPCKSUM;
2781189714Syongari		if ((ifp->if_hwassist & CSUM_TCP) != 0)
2782189714Syongari			txcap |= OFFLOAD_TCPCKSUM;
2783189714Syongari		if ((ifp->if_hwassist & CSUM_UDP) != 0)
2784189714Syongari			txcap |= OFFLOAD_UDPCKSUM;
2785189714Syongari		rxcap = txcap;
2786189714Syongari	}
2787189714Syongari	if ((ifp->if_capenable & IFCAP_RXCSUM) == 0)
2788189714Syongari		rxcap &= ~(OFFLOAD_IPCKSUM | OFFLOAD_TCPCKSUM |
2789189714Syongari		    OFFLOAD_UDPCKSUM);
2790189714Syongari	if ((ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) {
2791189714Syongari		rxcap |= OFFLOAD_VLAN;
2792189714Syongari		txcap |= OFFLOAD_VLAN;
2793189714Syongari	}
2794189714Syongari
2795189714Syongari	/* Tell firmware new offload configuration. */
2796189714Syongari	return (txp_command(sc, TXP_CMD_OFFLOAD_WRITE, 0, txcap, rxcap, NULL,
2797189714Syongari	    NULL, NULL, TXP_CMD_NOWAIT));
2798189714Syongari}
2799189714Syongari
280080219Swpaulstatic void
2801189714Syongaritxp_stats_save(struct txp_softc *sc)
280280219Swpaul{
2803189714Syongari	struct txp_rsp_desc *rsp;
280480219Swpaul
2805189714Syongari	TXP_LOCK_ASSERT(sc);
2806189714Syongari
2807189714Syongari	rsp = NULL;
2808189714Syongari	if (txp_ext_command(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0,
2809189714Syongari	    &rsp, TXP_CMD_WAIT))
281080219Swpaul		goto out;
2811189714Syongari	if (rsp->rsp_numdesc != 6)
2812189714Syongari		goto out;
2813189714Syongari	txp_stats_update(sc, rsp);
2814189714Syongariout:
2815189714Syongari	if (rsp != NULL)
2816189714Syongari		free(rsp, M_DEVBUF);
2817189714Syongari	bcopy(&sc->sc_stats, &sc->sc_ostats, sizeof(struct txp_hw_stats));
2818189714Syongari}
281980219Swpaul
2820189714Syongaristatic void
2821189714Syongaritxp_stats_update(struct txp_softc *sc, struct txp_rsp_desc *rsp)
2822189714Syongari{
2823189714Syongari	struct txp_hw_stats *ostats, *stats;
2824189714Syongari	struct txp_ext_desc *ext;
2825189714Syongari
2826189714Syongari	TXP_LOCK_ASSERT(sc);
2827189714Syongari
282880219Swpaul	ext = (struct txp_ext_desc *)(rsp + 1);
2829189714Syongari	ostats = &sc->sc_ostats;
2830189714Syongari	stats = &sc->sc_stats;
2831189714Syongari	stats->tx_frames = ostats->tx_frames + le32toh(rsp->rsp_par2);
2832189714Syongari	stats->tx_bytes = ostats->tx_bytes + (uint64_t)le32toh(rsp->rsp_par3) +
2833189714Syongari	    ((uint64_t)le32toh(ext[0].ext_1) << 32);
2834189714Syongari	stats->tx_deferred = ostats->tx_deferred + le32toh(ext[0].ext_2);
2835189714Syongari	stats->tx_late_colls = ostats->tx_late_colls + le32toh(ext[0].ext_3);
2836189714Syongari	stats->tx_colls = ostats->tx_colls + le32toh(ext[0].ext_4);
2837189714Syongari	stats->tx_carrier_lost = ostats->tx_carrier_lost +
2838189714Syongari	    le32toh(ext[1].ext_1);
2839189714Syongari	stats->tx_multi_colls = ostats->tx_multi_colls +
2840189714Syongari	    le32toh(ext[1].ext_2);
2841189714Syongari	stats->tx_excess_colls = ostats->tx_excess_colls +
2842189714Syongari	    le32toh(ext[1].ext_3);
2843189714Syongari	stats->tx_fifo_underruns = ostats->tx_fifo_underruns +
2844189714Syongari	    le32toh(ext[1].ext_4);
2845189714Syongari	stats->tx_mcast_oflows = ostats->tx_mcast_oflows +
2846189714Syongari	    le32toh(ext[2].ext_1);
2847189714Syongari	stats->tx_filtered = ostats->tx_filtered + le32toh(ext[2].ext_2);
2848189714Syongari	stats->rx_frames = ostats->rx_frames + le32toh(ext[2].ext_3);
2849189714Syongari	stats->rx_bytes = ostats->rx_bytes + (uint64_t)le32toh(ext[2].ext_4) +
2850189714Syongari	    ((uint64_t)le32toh(ext[3].ext_1) << 32);
2851189714Syongari	stats->rx_fifo_oflows = ostats->rx_fifo_oflows + le32toh(ext[3].ext_2);
2852189714Syongari	stats->rx_badssd = ostats->rx_badssd + le32toh(ext[3].ext_3);
2853189714Syongari	stats->rx_crcerrs = ostats->rx_crcerrs + le32toh(ext[3].ext_4);
2854189714Syongari	stats->rx_lenerrs = ostats->rx_lenerrs + le32toh(ext[4].ext_1);
2855189714Syongari	stats->rx_bcast_frames = ostats->rx_bcast_frames +
2856189714Syongari	    le32toh(ext[4].ext_2);
2857189714Syongari	stats->rx_mcast_frames = ostats->rx_mcast_frames +
2858189714Syongari	    le32toh(ext[4].ext_3);
2859189714Syongari	stats->rx_oflows = ostats->rx_oflows + le32toh(ext[4].ext_4);
2860189714Syongari	stats->rx_filtered = ostats->rx_filtered + le32toh(ext[5].ext_1);
2861272067Sglebius}
286280219Swpaul
2863272067Sglebiusstatic uint64_t
2864272067Sglebiustxp_get_counter(struct ifnet *ifp, ift_counter cnt)
2865272067Sglebius{
2866272067Sglebius	struct txp_softc *sc;
2867272067Sglebius	struct txp_hw_stats *stats;
2868272067Sglebius
2869272067Sglebius	sc = if_getsoftc(ifp);
2870272067Sglebius	stats = &sc->sc_stats;
2871272067Sglebius
2872272067Sglebius	switch (cnt) {
2873272067Sglebius	case IFCOUNTER_IERRORS:
2874272067Sglebius		return (stats->rx_fifo_oflows + stats->rx_badssd +
2875272067Sglebius		    stats->rx_crcerrs + stats->rx_lenerrs + stats->rx_oflows);
2876272067Sglebius	case IFCOUNTER_OERRORS:
2877272067Sglebius		return (stats->tx_deferred + stats->tx_carrier_lost +
2878272067Sglebius		    stats->tx_fifo_underruns + stats->tx_mcast_oflows);
2879272067Sglebius	case IFCOUNTER_COLLISIONS:
2880272067Sglebius		return (stats->tx_late_colls + stats->tx_multi_colls +
2881272067Sglebius		    stats->tx_excess_colls);
2882272067Sglebius	case IFCOUNTER_OPACKETS:
2883272067Sglebius		return (stats->tx_frames);
2884272067Sglebius	case IFCOUNTER_IPACKETS:
2885272067Sglebius		return (stats->rx_frames);
2886272067Sglebius	default:
2887272067Sglebius		return (if_get_counter_default(ifp, cnt));
2888272067Sglebius	}
2889189714Syongari}
289080219Swpaul
2891189714Syongari#define	TXP_SYSCTL_STAT_ADD32(c, h, n, p, d)	\
2892189714Syongari	    SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d)
289380219Swpaul
2894217323Smdf#if __FreeBSD_version >= 900030
2895189714Syongari#define	TXP_SYSCTL_STAT_ADD64(c, h, n, p, d)	\
2896217323Smdf	    SYSCTL_ADD_UQUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d)
2897217323Smdf#elif __FreeBSD_version > 800000
2898217323Smdf#define	TXP_SYSCTL_STAT_ADD64(c, h, n, p, d)	\
2899189714Syongari	    SYSCTL_ADD_QUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d)
2900189714Syongari#else
2901189714Syongari#define	TXP_SYSCTL_STAT_ADD64(c, h, n, p, d)	\
2902189714Syongari	    SYSCTL_ADD_ULONG(c, h, OID_AUTO, n, CTLFLAG_RD, p, d)
290380219Swpaul#endif
290480219Swpaul
2905189714Syongaristatic void
2906189714Syongaritxp_sysctl_node(struct txp_softc *sc)
2907189714Syongari{
2908189714Syongari	struct sysctl_ctx_list *ctx;
2909189714Syongari	struct sysctl_oid_list *child, *parent;
2910189714Syongari	struct sysctl_oid *tree;
2911189714Syongari	struct txp_hw_stats *stats;
2912189714Syongari	int error;
291380219Swpaul
2914189714Syongari	stats = &sc->sc_stats;
2915189714Syongari	ctx = device_get_sysctl_ctx(sc->sc_dev);
2916189714Syongari	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
2917189714Syongari	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "process_limit",
2918189714Syongari	    CTLTYPE_INT | CTLFLAG_RW, &sc->sc_process_limit, 0,
2919189714Syongari	    sysctl_hw_txp_proc_limit, "I",
2920189714Syongari	    "max number of Rx events to process");
2921189714Syongari	/* Pull in device tunables. */
2922189714Syongari	sc->sc_process_limit = TXP_PROC_DEFAULT;
2923189714Syongari	error = resource_int_value(device_get_name(sc->sc_dev),
2924189714Syongari	    device_get_unit(sc->sc_dev), "process_limit",
2925189714Syongari	    &sc->sc_process_limit);
2926189714Syongari	if (error == 0) {
2927189714Syongari		if (sc->sc_process_limit < TXP_PROC_MIN ||
2928189714Syongari		    sc->sc_process_limit > TXP_PROC_MAX) {
2929189714Syongari			device_printf(sc->sc_dev,
2930189714Syongari			    "process_limit value out of range; "
2931189714Syongari			    "using default: %d\n", TXP_PROC_DEFAULT);
2932189714Syongari			sc->sc_process_limit = TXP_PROC_DEFAULT;
2933189714Syongari		}
293480219Swpaul	}
2935189714Syongari	tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
2936189714Syongari	    NULL, "TXP statistics");
2937189714Syongari	parent = SYSCTL_CHILDREN(tree);
293880219Swpaul
2939189714Syongari	/* Tx statistics. */
2940189714Syongari	tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD,
2941189714Syongari	    NULL, "Tx MAC statistics");
2942189714Syongari	child = SYSCTL_CHILDREN(tree);
294380219Swpaul
2944189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "frames",
2945189714Syongari	    &stats->tx_frames, "Frames");
2946189714Syongari	TXP_SYSCTL_STAT_ADD64(ctx, child, "octets",
2947189714Syongari	    &stats->tx_bytes, "Octets");
2948189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "deferred",
2949189714Syongari	    &stats->tx_deferred, "Deferred frames");
2950189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "late_colls",
2951189714Syongari	    &stats->tx_late_colls, "Late collisions");
2952189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "colls",
2953189714Syongari	    &stats->tx_colls, "Collisions");
2954189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "carrier_lost",
2955189714Syongari	    &stats->tx_carrier_lost, "Carrier lost");
2956189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "multi_colls",
2957189714Syongari	    &stats->tx_multi_colls, "Multiple collisions");
2958189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "excess_colls",
2959189714Syongari	    &stats->tx_excess_colls, "Excessive collisions");
2960189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "fifo_underruns",
2961189714Syongari	    &stats->tx_fifo_underruns, "FIFO underruns");
2962189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "mcast_oflows",
2963189714Syongari	    &stats->tx_mcast_oflows, "Multicast overflows");
2964189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "filtered",
2965189714Syongari	    &stats->tx_filtered, "Filtered frames");
296680219Swpaul
2967189714Syongari	/* Rx statistics. */
2968189714Syongari	tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD,
2969189714Syongari	    NULL, "Rx MAC statistics");
2970189714Syongari	child = SYSCTL_CHILDREN(tree);
2971189714Syongari
2972189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "frames",
2973189714Syongari	    &stats->rx_frames, "Frames");
2974189714Syongari	TXP_SYSCTL_STAT_ADD64(ctx, child, "octets",
2975189714Syongari	    &stats->rx_bytes, "Octets");
2976189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "fifo_oflows",
2977189714Syongari	    &stats->rx_fifo_oflows, "FIFO overflows");
2978189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "badssd",
2979189714Syongari	    &stats->rx_badssd, "Bad SSD");
2980189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "crcerrs",
2981189714Syongari	    &stats->rx_crcerrs, "CRC errors");
2982189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "lenerrs",
2983189714Syongari	    &stats->rx_lenerrs, "Length errors");
2984189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "bcast_frames",
2985189714Syongari	    &stats->rx_bcast_frames, "Broadcast frames");
2986189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "mcast_frames",
2987189714Syongari	    &stats->rx_mcast_frames, "Multicast frames");
2988189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "oflows",
2989189714Syongari	    &stats->rx_oflows, "Overflows");
2990189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "filtered",
2991189714Syongari	    &stats->rx_filtered, "Filtered frames");
299280219Swpaul}
2993189714Syongari
2994189714Syongari#undef TXP_SYSCTL_STAT_ADD32
2995189714Syongari#undef TXP_SYSCTL_STAT_ADD64
2996189714Syongari
2997189714Syongaristatic int
2998189714Syongarisysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high)
2999189714Syongari{
3000189714Syongari	int error, value;
3001189714Syongari
3002189714Syongari	if (arg1 == NULL)
3003189714Syongari		return (EINVAL);
3004189714Syongari	value = *(int *)arg1;
3005189714Syongari	error = sysctl_handle_int(oidp, &value, 0, req);
3006189714Syongari	if (error || req->newptr == NULL)
3007189714Syongari		return (error);
3008189714Syongari	if (value < low || value > high)
3009189714Syongari		return (EINVAL);
3010189714Syongari        *(int *)arg1 = value;
3011189714Syongari
3012189714Syongari        return (0);
3013189714Syongari}
3014189714Syongari
3015189714Syongaristatic int
3016189714Syongarisysctl_hw_txp_proc_limit(SYSCTL_HANDLER_ARGS)
3017189714Syongari{
3018189714Syongari	return (sysctl_int_range(oidp, arg1, arg2, req,
3019189714Syongari	    TXP_PROC_MIN, TXP_PROC_MAX));
3020189714Syongari}
3021