if_txp.c revision 219902
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: head/sys/dev/txp/if_txp.c 219902 2011-03-23 13:10:15Z jhb $");
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>
6280219Swpaul#include <net/if_arp.h>
6380219Swpaul#include <net/ethernet.h>
6480219Swpaul#include <net/if_dl.h>
65189714Syongari#include <net/if_media.h>
6680219Swpaul#include <net/if_types.h>
6783115Sbrooks#include <net/if_vlan_var.h>
6880219Swpaul
6980219Swpaul#include <netinet/in.h>
7080219Swpaul#include <netinet/in_systm.h>
7180219Swpaul#include <netinet/ip.h>
7280219Swpaul
73189714Syongari#include <dev/mii/mii.h>
7480219Swpaul
7580219Swpaul#include <dev/pci/pcireg.h>
7680219Swpaul#include <dev/pci/pcivar.h>
7780219Swpaul
78189714Syongari#include <machine/bus.h>
79189714Syongari#include <machine/in_cksum.h>
8080219Swpaul
8180219Swpaul#include <dev/txp/if_txpreg.h>
8280219Swpaul#include <dev/txp/3c990img.h>
8380219Swpaul
84189714SyongariMODULE_DEPEND(txp, pci, 1, 1, 1);
85189714SyongariMODULE_DEPEND(txp, ether, 1, 1, 1);
8680219Swpaul
8780219Swpaul/*
88189714Syongari * XXX Known Typhoon firmware issues.
89189714Syongari *
90189714Syongari * 1. It seems that firmware has Tx TCP/UDP checksum offloading bug.
91189714Syongari *    The firmware hangs when it's told to compute TCP/UDP checksum.
92189714Syongari *    I'm not sure whether the firmware requires special alignment to
93189714Syongari *    do checksum offloading but datasheet says nothing about that.
94189714Syongari * 2. Datasheet says nothing for maximum number of fragmented
95189714Syongari *    descriptors supported. Experimentation shows up to 16 fragment
96189714Syongari *    descriptors are supported in the firmware. For TSO case, upper
97189714Syongari *    stack can send 64KB sized IP datagram plus link header size(
98189714Syongari *    ethernet header + VLAN tag)  frame but controller can handle up
99189714Syongari *    to 64KB frame given that PAGE_SIZE is 4KB(i.e. 16 * PAGE_SIZE).
100189714Syongari *    Because frames that need TSO operation of hardware can be
101189714Syongari *    larger than 64KB I disabled TSO capability. TSO operation for
102189714Syongari *    less than or equal to 16 fragment descriptors works without
103189714Syongari *    problems, though.
104189714Syongari * 3. VLAN hardware tag stripping is always enabled in the firmware
105189714Syongari *    even if it's explicitly told to not strip the tag. It's
106189714Syongari *    possible to add the tag back in Rx handler if VLAN hardware
107189714Syongari *    tag is not active but I didn't try that as it would be
108189714Syongari *    layering violation.
109189714Syongari * 4. TXP_CMD_RECV_BUFFER_CONTROL does not work as expected in
110189714Syongari *    datasheet such that driver should handle the alignment
111189714Syongari *    restriction by copying received frame to align the frame on
112189714Syongari *    32bit boundary on strict-alignment architectures. This adds a
113189714Syongari *    lot of CPU burden and it effectively reduce Rx performance on
114189714Syongari *    strict-alignment architectures(e.g. sparc64, arm, mips and ia64).
115189714Syongari *
116189714Syongari * Unfortunately it seems that 3Com have no longer interests in
117189714Syongari * releasing fixed firmware so we may have to live with these bugs.
118189714Syongari */
119189714Syongari
120189714Syongari#define	TXP_CSUM_FEATURES	(CSUM_IP)
121189714Syongari
122189714Syongari/*
12380219Swpaul * Various supported device vendors/types and their names.
12480219Swpaul */
12580219Swpaulstatic struct txp_type txp_devs[] = {
12680219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_95,
12780219Swpaul	    "3Com 3cR990-TX-95 Etherlink with 3XP Processor" },
12880219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_97,
12980219Swpaul	    "3Com 3cR990-TX-97 Etherlink with 3XP Processor" },
13080219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_TXM,
13180219Swpaul	    "3Com 3cR990B-TXM Etherlink with 3XP Processor" },
13280219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_95,
13380219Swpaul	    "3Com 3cR990-SRV-95 Etherlink Server with 3XP Processor" },
13480219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_97,
13580219Swpaul	    "3Com 3cR990-SRV-97 Etherlink Server with 3XP Processor" },
13680219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_SRV,
13780219Swpaul	    "3Com 3cR990B-SRV Etherlink Server with 3XP Processor" },
13880219Swpaul	{ 0, 0, NULL }
13980219Swpaul};
14080219Swpaul
141149678Sjhbstatic int txp_probe(device_t);
142149678Sjhbstatic int txp_attach(device_t);
143149678Sjhbstatic int txp_detach(device_t);
144189714Syongaristatic int txp_shutdown(device_t);
145189714Syongaristatic int txp_suspend(device_t);
146189714Syongaristatic int txp_resume(device_t);
147189714Syongaristatic int txp_intr(void *);
148189714Syongaristatic void txp_int_task(void *, int);
149149678Sjhbstatic void txp_tick(void *);
150149678Sjhbstatic int txp_ioctl(struct ifnet *, u_long, caddr_t);
151149678Sjhbstatic void txp_start(struct ifnet *);
152151772Sjhbstatic void txp_start_locked(struct ifnet *);
153189714Syongaristatic int txp_encap(struct txp_softc *, struct txp_tx_ring *, struct mbuf **);
154149678Sjhbstatic void txp_stop(struct txp_softc *);
155149678Sjhbstatic void txp_init(void *);
156151772Sjhbstatic void txp_init_locked(struct txp_softc *);
157189714Syongaristatic void txp_watchdog(struct txp_softc *);
15880219Swpaul
159189714Syongaristatic int txp_reset(struct txp_softc *);
160189714Syongaristatic int txp_boot(struct txp_softc *, uint32_t);
161189714Syongaristatic int txp_sleep(struct txp_softc *, int);
162189714Syongaristatic int txp_wait(struct txp_softc *, uint32_t);
16392739Salfredstatic int txp_download_fw(struct txp_softc *);
16492739Salfredstatic int txp_download_fw_wait(struct txp_softc *);
165149678Sjhbstatic int txp_download_fw_section(struct txp_softc *,
16692739Salfred    struct txp_fw_section_header *, int);
16792739Salfredstatic int txp_alloc_rings(struct txp_softc *);
168189714Syongaristatic void txp_init_rings(struct txp_softc *);
169189714Syongaristatic int txp_dma_alloc(struct txp_softc *, char *, bus_dma_tag_t *,
170189714Syongari    bus_size_t, bus_size_t, bus_dmamap_t *, void **, bus_size_t, bus_addr_t *);
171189714Syongaristatic void txp_dma_free(struct txp_softc *, bus_dma_tag_t *, bus_dmamap_t *,
172189714Syongari    void **);
173189714Syongaristatic void txp_free_rings(struct txp_softc *);
17492739Salfredstatic int txp_rxring_fill(struct txp_softc *);
17592739Salfredstatic void txp_rxring_empty(struct txp_softc *);
17692739Salfredstatic void txp_set_filter(struct txp_softc *);
17780219Swpaul
17892739Salfredstatic int txp_cmd_desc_numfree(struct txp_softc *);
179189689Syongaristatic int txp_command(struct txp_softc *, uint16_t, uint16_t, uint32_t,
180189689Syongari    uint32_t, uint16_t *, uint32_t *, uint32_t *, int);
181189714Syongaristatic int txp_ext_command(struct txp_softc *, uint16_t, uint16_t,
182189689Syongari    uint32_t, uint32_t, struct txp_ext_desc *, uint8_t,
18392739Salfred    struct txp_rsp_desc **, int);
184189714Syongaristatic int txp_response(struct txp_softc *, uint16_t, uint16_t,
18592739Salfred    struct txp_rsp_desc **);
186149678Sjhbstatic void txp_rsp_fixup(struct txp_softc *, struct txp_rsp_desc *,
18792739Salfred    struct txp_rsp_desc *);
188189714Syongaristatic int txp_set_capabilities(struct txp_softc *);
18980219Swpaul
19092739Salfredstatic void txp_ifmedia_sts(struct ifnet *, struct ifmediareq *);
19192739Salfredstatic int txp_ifmedia_upd(struct ifnet *);
19280219Swpaul#ifdef TXP_DEBUG
19392739Salfredstatic void txp_show_descriptor(void *);
19480219Swpaul#endif
19592739Salfredstatic void txp_tx_reclaim(struct txp_softc *, struct txp_tx_ring *);
19692739Salfredstatic void txp_rxbuf_reclaim(struct txp_softc *);
197189714Syongari#ifndef __NO_STRICT_ALIGNMENT
198189714Syongaristatic __inline void txp_fixup_rx(struct mbuf *);
19980219Swpaul#endif
200189714Syongaristatic int txp_rx_reclaim(struct txp_softc *, struct txp_rx_ring *, int);
201189714Syongaristatic void txp_stats_save(struct txp_softc *);
202189714Syongaristatic void txp_stats_update(struct txp_softc *, struct txp_rsp_desc *);
203189714Syongaristatic void txp_sysctl_node(struct txp_softc *);
204189714Syongaristatic int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int);
205189714Syongaristatic int sysctl_hw_txp_proc_limit(SYSCTL_HANDLER_ARGS);
20680219Swpaul
207189714Syongaristatic int prefer_iomap = 0;
208189714SyongariTUNABLE_INT("hw.txp.prefer_iomap", &prefer_iomap);
209189714Syongari
21080219Swpaulstatic device_method_t txp_methods[] = {
21180219Swpaul        /* Device interface */
21280219Swpaul	DEVMETHOD(device_probe,		txp_probe),
21380219Swpaul	DEVMETHOD(device_attach,	txp_attach),
21480219Swpaul	DEVMETHOD(device_detach,	txp_detach),
21580219Swpaul	DEVMETHOD(device_shutdown,	txp_shutdown),
216189714Syongari	DEVMETHOD(device_suspend,	txp_suspend),
217189714Syongari	DEVMETHOD(device_resume,	txp_resume),
218189714Syongari
219189714Syongari	{ NULL, NULL }
22080219Swpaul};
22180219Swpaul
22280219Swpaulstatic driver_t txp_driver = {
22380219Swpaul	"txp",
22480219Swpaul	txp_methods,
22580219Swpaul	sizeof(struct txp_softc)
22680219Swpaul};
22780219Swpaul
22880219Swpaulstatic devclass_t txp_devclass;
22980219Swpaul
230113506SmdoddDRIVER_MODULE(txp, pci, txp_driver, txp_devclass, 0, 0);
23180219Swpaul
23280219Swpaulstatic int
233189685Syongaritxp_probe(device_t dev)
23480219Swpaul{
23580219Swpaul	struct txp_type *t;
23680219Swpaul
23780219Swpaul	t = txp_devs;
23880219Swpaul
239189688Syongari	while (t->txp_name != NULL) {
24080219Swpaul		if ((pci_get_vendor(dev) == t->txp_vid) &&
24180219Swpaul		    (pci_get_device(dev) == t->txp_did)) {
24280219Swpaul			device_set_desc(dev, t->txp_name);
243189688Syongari			return (BUS_PROBE_DEFAULT);
24480219Swpaul		}
24580219Swpaul		t++;
24680219Swpaul	}
24780219Swpaul
248189688Syongari	return (ENXIO);
24980219Swpaul}
25080219Swpaul
25180219Swpaulstatic int
252189685Syongaritxp_attach(device_t dev)
25380219Swpaul{
25480219Swpaul	struct txp_softc *sc;
25580219Swpaul	struct ifnet *ifp;
256189714Syongari	struct txp_rsp_desc *rsp;
257189689Syongari	uint16_t p1;
258189714Syongari	uint32_t p2, reg;
259189714Syongari	int error = 0, pmc, rid;
260189714Syongari	uint8_t eaddr[ETHER_ADDR_LEN], *ver;
26180219Swpaul
26280219Swpaul	sc = device_get_softc(dev);
26380219Swpaul	sc->sc_dev = dev;
26480219Swpaul
26593818Sjhb	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
266151772Sjhb	    MTX_DEF);
267151772Sjhb	callout_init_mtx(&sc->sc_tick, &sc->sc_mtx, 0);
268189714Syongari	TASK_INIT(&sc->sc_int_task, 0, txp_int_task, sc);
269189714Syongari	TAILQ_INIT(&sc->sc_busy_list);
270189714Syongari	TAILQ_INIT(&sc->sc_free_list);
271151772Sjhb
272189714Syongari	ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts);
273189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0, NULL);
274189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_HDX, 0, NULL);
275189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
276189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
277189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_HDX, 0, NULL);
278189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
279189714Syongari	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
280189714Syongari
28180219Swpaul	pci_enable_busmaster(dev);
282189714Syongari	/* Prefer memory space register mapping over IO space. */
283189714Syongari	if (prefer_iomap == 0) {
284189714Syongari		sc->sc_res_id = PCIR_BAR(1);
285189714Syongari		sc->sc_res_type = SYS_RES_MEMORY;
286189714Syongari	} else {
287189714Syongari		sc->sc_res_id = PCIR_BAR(0);
288189714Syongari		sc->sc_res_type = SYS_RES_IOPORT;
289189714Syongari	}
290189714Syongari	sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type,
291189714Syongari	    &sc->sc_res_id, RF_ACTIVE);
292189714Syongari	if (sc->sc_res == NULL && prefer_iomap == 0) {
293189714Syongari		sc->sc_res_id = PCIR_BAR(0);
294189714Syongari		sc->sc_res_type = SYS_RES_IOPORT;
295189714Syongari		sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type,
296189714Syongari		    &sc->sc_res_id, RF_ACTIVE);
297189714Syongari	}
29880219Swpaul	if (sc->sc_res == NULL) {
29980219Swpaul		device_printf(dev, "couldn't map ports/memory\n");
300189714Syongari		ifmedia_removeall(&sc->sc_ifmedia);
301189714Syongari		mtx_destroy(&sc->sc_mtx);
302189714Syongari		return (ENXIO);
30380219Swpaul	}
30480219Swpaul
305189714Syongari	/* Enable MWI. */
306189714Syongari	reg = pci_read_config(dev, PCIR_COMMAND, 2);
307189714Syongari	reg |= PCIM_CMD_MWRICEN;
308189714Syongari	pci_write_config(dev, PCIR_COMMAND, reg, 2);
309189714Syongari	/* Check cache line size. */
310189714Syongari	reg = pci_read_config(dev, PCIR_CACHELNSZ, 1);
311189714Syongari	reg <<= 4;
312189714Syongari	if (reg == 0 || (reg % 16) != 0)
313189714Syongari		device_printf(sc->sc_dev,
314189714Syongari		    "invalid cache line size : %u\n", reg);
31580219Swpaul
31680219Swpaul	/* Allocate interrupt */
31780219Swpaul	rid = 0;
318127135Snjl	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
31980219Swpaul	    RF_SHAREABLE | RF_ACTIVE);
32080219Swpaul
32180219Swpaul	if (sc->sc_irq == NULL) {
32280219Swpaul		device_printf(dev, "couldn't map interrupt\n");
32380219Swpaul		error = ENXIO;
32480219Swpaul		goto fail;
32580219Swpaul	}
32680219Swpaul
327189714Syongari	if ((error = txp_alloc_rings(sc)) != 0)
32880219Swpaul		goto fail;
329189714Syongari	txp_init_rings(sc);
330189714Syongari	txp_sysctl_node(sc);
331189714Syongari	/* Reset controller and make it reload sleep image. */
332189714Syongari	if (txp_reset(sc) != 0) {
333170596Syongari		error = ENXIO;
334170596Syongari		goto fail;
335170596Syongari	}
33680219Swpaul
337189714Syongari	/* Let controller boot from sleep image. */
338189714Syongari	if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) {
339189714Syongari		device_printf(sc->sc_dev, "could not boot sleep image\n");
340170596Syongari		error = ENXIO;
341170596Syongari		goto fail;
342170596Syongari	}
34380219Swpaul
344189714Syongari	/* Get station address. */
34580219Swpaul	if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0,
346189714Syongari	    &p1, &p2, NULL, TXP_CMD_WAIT)) {
347149678Sjhb		error = ENXIO;
34880219Swpaul		goto fail;
34980219Swpaul	}
35080219Swpaul
351189714Syongari	p1 = le16toh(p1);
352189689Syongari	eaddr[0] = ((uint8_t *)&p1)[1];
353189689Syongari	eaddr[1] = ((uint8_t *)&p1)[0];
354189714Syongari	p2 = le32toh(p2);
355189689Syongari	eaddr[2] = ((uint8_t *)&p2)[3];
356189689Syongari	eaddr[3] = ((uint8_t *)&p2)[2];
357189689Syongari	eaddr[4] = ((uint8_t *)&p2)[1];
358189689Syongari	eaddr[5] = ((uint8_t *)&p2)[0];
35980219Swpaul
360189714Syongari	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
361189714Syongari	if (ifp == NULL) {
362189714Syongari		device_printf(dev, "can not allocate ifnet structure\n");
363189714Syongari		error = ENOSPC;
364189714Syongari		goto fail;
365189714Syongari	}
36680219Swpaul
367189714Syongari	/*
368189714Syongari	 * Show sleep image version information which may help to
369189714Syongari	 * diagnose sleep image specific issues.
370189714Syongari	 */
371189714Syongari	rsp = NULL;
372189714Syongari	if (txp_ext_command(sc, TXP_CMD_READ_VERSION, 0, 0, 0, NULL, 0,
373189714Syongari	    &rsp, TXP_CMD_WAIT)) {
374189714Syongari		device_printf(dev, "can not read sleep image version\n");
375189714Syongari		error = ENXIO;
376189714Syongari		goto fail;
377189714Syongari	}
378189714Syongari	if (rsp->rsp_numdesc == 0) {
379189714Syongari		p2 = le32toh(rsp->rsp_par2) & 0xFFFF;
380189714Syongari		device_printf(dev, "Typhoon 1.0 sleep image (2000/%02u/%02u)\n",
381189714Syongari		    p2 >> 8, p2 & 0xFF);
382189714Syongari	} else if (rsp->rsp_numdesc == 2) {
383189714Syongari		p2 = le32toh(rsp->rsp_par2);
384189714Syongari		ver = (uint8_t *)(rsp + 1);
385189714Syongari		/*
386189714Syongari		 * Even if datasheet says the command returns a NULL
387189714Syongari		 * terminated version string, explicitly terminate
388189714Syongari		 * the string. Given that several bugs of firmware
389189714Syongari		 * I can't trust this simple one.
390189714Syongari		 */
391189714Syongari		ver[25] = '\0';
392189714Syongari		device_printf(dev,
393189714Syongari		    "Typhoon 1.1+ sleep image %02u.%03u.%03u %s\n",
394189714Syongari		    p2 >> 24, (p2 >> 12) & 0xFFF, p2 & 0xFFF, ver);
395189714Syongari	} else {
396189714Syongari		p2 = le32toh(rsp->rsp_par2);
397189714Syongari		device_printf(dev,
398189714Syongari		    "Unknown Typhoon sleep image version: %u:0x%08x\n",
399189714Syongari		    rsp->rsp_numdesc, p2);
400189714Syongari	}
401189714Syongari	if (rsp != NULL)
402189714Syongari		free(rsp, M_DEVBUF);
40380219Swpaul
40480219Swpaul	sc->sc_xcvr = TXP_XCVR_AUTO;
40580219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0,
406189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT);
407189714Syongari	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
40880219Swpaul
40980219Swpaul	ifp->if_softc = sc;
410121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
411151772Sjhb	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
41280219Swpaul	ifp->if_ioctl = txp_ioctl;
41380219Swpaul	ifp->if_start = txp_start;
41480219Swpaul	ifp->if_init = txp_init;
415189714Syongari	ifp->if_snd.ifq_drv_maxlen = TX_ENTRIES - 1;
416189714Syongari	IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
417189714Syongari	IFQ_SET_READY(&ifp->if_snd);
41880219Swpaul	/*
419189714Syongari	 * It's possible to read firmware's offload capability but
420189714Syongari	 * we have not downloaded the firmware yet so announce
421189714Syongari	 * working capability here. We're not interested in IPSec
422189714Syongari	 * capability and due to the lots of firmware bug we can't
423189714Syongari	 * advertise the whole capability anyway.
42480219Swpaul	 */
425189714Syongari	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM;
426219902Sjhb	if (pci_find_cap(dev, PCIY_PMG, &pmc) == 0)
427189714Syongari		ifp->if_capabilities |= IFCAP_WOL_MAGIC;
428189714Syongari	/* Enable all capabilities. */
429189714Syongari	ifp->if_capenable = ifp->if_capabilities;
430189714Syongari
431147256Sbrooks	ether_ifattach(ifp, eaddr);
432149678Sjhb
433189714Syongari	/* VLAN capability setup. */
434189714Syongari	ifp->if_capabilities |= IFCAP_VLAN_MTU;
435189714Syongari	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
436189714Syongari	ifp->if_capenable = ifp->if_capabilities;
437189714Syongari	/* Tell the upper layer(s) we support long frames. */
438189714Syongari	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
439189714Syongari
440189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
441189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
442189714Syongari
443189714Syongari	/* Create local taskq. */
444189714Syongari	sc->sc_tq = taskqueue_create_fast("txp_taskq", M_WAITOK,
445189714Syongari	    taskqueue_thread_enqueue, &sc->sc_tq);
446189714Syongari	if (sc->sc_tq == NULL) {
447189714Syongari		device_printf(dev, "could not create taskqueue.\n");
448189714Syongari		ether_ifdetach(ifp);
449189714Syongari		error = ENXIO;
450189714Syongari		goto fail;
451189714Syongari	}
452189714Syongari	taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
453189714Syongari	    device_get_nameunit(sc->sc_dev));
454189714Syongari
455189714Syongari	/* Put controller into sleep. */
456189714Syongari	if (txp_sleep(sc, 0) != 0) {
457189714Syongari		ether_ifdetach(ifp);
458189714Syongari		error = ENXIO;
459189714Syongari		goto fail;
460189714Syongari	}
461189714Syongari
462151772Sjhb	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
463189714Syongari	    txp_intr, NULL, sc, &sc->sc_intrhand);
464149678Sjhb
465189714Syongari	if (error != 0) {
466149678Sjhb		ether_ifdetach(ifp);
467189714Syongari		device_printf(dev, "couldn't set up interrupt handler.\n");
468149678Sjhb		goto fail;
469149678Sjhb	}
470149678Sjhb
471189688Syongari	return (0);
47280219Swpaul
47380219Swpaulfail:
474189714Syongari	if (error != 0)
475189714Syongari		txp_detach(dev);
476189688Syongari	return (error);
47780219Swpaul}
47880219Swpaul
47980219Swpaulstatic int
480189685Syongaritxp_detach(device_t dev)
48180219Swpaul{
48280219Swpaul	struct txp_softc *sc;
48380219Swpaul	struct ifnet *ifp;
48480219Swpaul
48580219Swpaul	sc = device_get_softc(dev);
486189714Syongari
487147256Sbrooks	ifp = sc->sc_ifp;
488189714Syongari	if (device_is_attached(dev)) {
489189714Syongari		TXP_LOCK(sc);
490189714Syongari		sc->sc_flags |= TXP_FLAG_DETACH;
491189714Syongari		txp_stop(sc);
492189714Syongari		TXP_UNLOCK(sc);
493189714Syongari		callout_drain(&sc->sc_tick);
494189714Syongari		taskqueue_drain(sc->sc_tq, &sc->sc_int_task);
495189714Syongari		ether_ifdetach(ifp);
496189714Syongari	}
497189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
49880219Swpaul
49980219Swpaul	ifmedia_removeall(&sc->sc_ifmedia);
50080219Swpaul	if (sc->sc_intrhand != NULL)
50180219Swpaul		bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand);
50280219Swpaul	if (sc->sc_irq != NULL)
50380219Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
50480219Swpaul	if (sc->sc_res != NULL)
505189714Syongari		bus_release_resource(dev, sc->sc_res_type, sc->sc_res_id,
506189714Syongari		    sc->sc_res);
507189714Syongari	if (sc->sc_ifp != NULL) {
508150306Simp		if_free(sc->sc_ifp);
509189714Syongari		sc->sc_ifp = NULL;
510189714Syongari	}
511189714Syongari	txp_free_rings(sc);
512189714Syongari	mtx_destroy(&sc->sc_mtx);
51380219Swpaul
51480219Swpaul	return (0);
51580219Swpaul}
51680219Swpaul
51780219Swpaulstatic int
518189714Syongaritxp_reset(struct txp_softc *sc)
51980219Swpaul{
520189689Syongari	uint32_t r;
52180219Swpaul	int i;
52280219Swpaul
523189714Syongari	/* Disable interrupts. */
524189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
525189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
526189714Syongari	/* Ack all pending interrupts. */
527189714Syongari	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
528189714Syongari
52992643Sjeff	r = 0;
53080219Swpaul	WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL);
53180219Swpaul	DELAY(1000);
53280219Swpaul	WRITE_REG(sc, TXP_SRR, 0);
53380219Swpaul
534189714Syongari	/* Should wait max 6 seconds. */
53580219Swpaul	for (i = 0; i < 6000; i++) {
53680219Swpaul		r = READ_REG(sc, TXP_A2H_0);
53780219Swpaul		if (r == STAT_WAITING_FOR_HOST_REQUEST)
53880219Swpaul			break;
53980219Swpaul		DELAY(1000);
54080219Swpaul	}
54180219Swpaul
542189714Syongari	if (r != STAT_WAITING_FOR_HOST_REQUEST)
54380219Swpaul		device_printf(sc->sc_dev, "reset hung\n");
544189714Syongari
545189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
546189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
547189714Syongari	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
548189714Syongari
549189714Syongari	/*
550189714Syongari	 * Give more time to complete loading sleep image before
551189714Syongari	 * trying to boot from sleep image.
552189714Syongari	 */
553189714Syongari	DELAY(5000);
554189714Syongari
555189714Syongari	return (0);
556189714Syongari}
557189714Syongari
558189714Syongaristatic int
559189714Syongaritxp_boot(struct txp_softc *sc, uint32_t state)
560189714Syongari{
561189714Syongari
562189714Syongari	/* See if it's waiting for boot, and try to boot it. */
563189714Syongari	if (txp_wait(sc, state) != 0) {
564189714Syongari		device_printf(sc->sc_dev, "not waiting for boot\n");
565189714Syongari		return (ENXIO);
56680219Swpaul	}
56780219Swpaul
568189714Syongari	WRITE_REG(sc, TXP_H2A_2, TXP_ADDR_HI(sc->sc_ldata.txp_boot_paddr));
569189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
570189714Syongari	WRITE_REG(sc, TXP_H2A_1, TXP_ADDR_LO(sc->sc_ldata.txp_boot_paddr));
571189714Syongari	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
572189714Syongari	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD);
573189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
574189714Syongari
575189714Syongari	/* See if it booted. */
576189714Syongari	if (txp_wait(sc, STAT_RUNNING) != 0) {
577189714Syongari		device_printf(sc->sc_dev, "firmware not running\n");
578189714Syongari		return (ENXIO);
579189714Syongari	}
580189714Syongari
581189714Syongari	/* Clear TX and CMD ring write registers. */
582189714Syongari	WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL);
583189714Syongari	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
584189714Syongari	WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL);
585189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
586189714Syongari	WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL);
587189714Syongari	TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE);
588189714Syongari	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL);
589189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
590189714Syongari
59180219Swpaul	return (0);
59280219Swpaul}
59380219Swpaul
59480219Swpaulstatic int
595189685Syongaritxp_download_fw(struct txp_softc *sc)
59680219Swpaul{
59780219Swpaul	struct txp_fw_file_header *fileheader;
59880219Swpaul	struct txp_fw_section_header *secthead;
599189714Syongari	int sect;
600189714Syongari	uint32_t error, ier, imr;
60180219Swpaul
602189714Syongari	TXP_LOCK_ASSERT(sc);
603189714Syongari
604189022Syongari	error = 0;
60580219Swpaul	ier = READ_REG(sc, TXP_IER);
60680219Swpaul	WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0);
60780219Swpaul
60880219Swpaul	imr = READ_REG(sc, TXP_IMR);
60980219Swpaul	WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0);
61080219Swpaul
611189714Syongari	if (txp_wait(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) {
61280219Swpaul		device_printf(sc->sc_dev, "not waiting for host request\n");
613189714Syongari		error = ETIMEDOUT;
614189022Syongari		goto fail;
61580219Swpaul	}
61680219Swpaul
617189714Syongari	/* Ack the status. */
61880219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
61980219Swpaul
62080219Swpaul	fileheader = (struct txp_fw_file_header *)tc990image;
62180219Swpaul	if (bcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) {
622189714Syongari		device_printf(sc->sc_dev, "firmware invalid magic\n");
623189022Syongari		goto fail;
62480219Swpaul	}
62580219Swpaul
626189714Syongari	/* Tell boot firmware to get ready for image. */
627189714Syongari	WRITE_REG(sc, TXP_H2A_1, le32toh(fileheader->addr));
628189714Syongari	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
629189714Syongari	WRITE_REG(sc, TXP_H2A_2, le32toh(fileheader->hmac[0]));
630189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
631189714Syongari	WRITE_REG(sc, TXP_H2A_3, le32toh(fileheader->hmac[1]));
632189714Syongari	TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE);
633189714Syongari	WRITE_REG(sc, TXP_H2A_4, le32toh(fileheader->hmac[2]));
634189714Syongari	TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE);
635189714Syongari	WRITE_REG(sc, TXP_H2A_5, le32toh(fileheader->hmac[3]));
636189714Syongari	TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE);
637189714Syongari	WRITE_REG(sc, TXP_H2A_6, le32toh(fileheader->hmac[4]));
638189714Syongari	TXP_BARRIER(sc, TXP_H2A_6, 4, BUS_SPACE_BARRIER_WRITE);
63980219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE);
640189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
64180219Swpaul
64280219Swpaul	if (txp_download_fw_wait(sc)) {
643189714Syongari		device_printf(sc->sc_dev, "firmware wait failed, initial\n");
644189714Syongari		error = ETIMEDOUT;
645189022Syongari		goto fail;
64680219Swpaul	}
64780219Swpaul
648189689Syongari	secthead = (struct txp_fw_section_header *)(((uint8_t *)tc990image) +
64980219Swpaul	    sizeof(struct txp_fw_file_header));
65080219Swpaul
651189714Syongari	for (sect = 0; sect < le32toh(fileheader->nsections); sect++) {
652189714Syongari		if ((error = txp_download_fw_section(sc, secthead, sect)) != 0)
653189022Syongari			goto fail;
65480219Swpaul		secthead = (struct txp_fw_section_header *)
655189714Syongari		    (((uint8_t *)secthead) + le32toh(secthead->nbytes) +
65680219Swpaul		    sizeof(*secthead));
65780219Swpaul	}
65880219Swpaul
65980219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE);
660189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
66180219Swpaul
662189714Syongari	if (txp_wait(sc, STAT_WAITING_FOR_BOOT) != 0) {
66380219Swpaul		device_printf(sc->sc_dev, "not waiting for boot\n");
664189714Syongari		error = ETIMEDOUT;
665189022Syongari		goto fail;
66680219Swpaul	}
66780219Swpaul
668189022Syongarifail:
66980219Swpaul	WRITE_REG(sc, TXP_IER, ier);
67080219Swpaul	WRITE_REG(sc, TXP_IMR, imr);
67180219Swpaul
672189022Syongari	return (error);
67380219Swpaul}
67480219Swpaul
67580219Swpaulstatic int
676189685Syongaritxp_download_fw_wait(struct txp_softc *sc)
67780219Swpaul{
678189714Syongari	uint32_t i;
67980219Swpaul
680189714Syongari	TXP_LOCK_ASSERT(sc);
681189714Syongari
682189714Syongari	for (i = 0; i < TXP_TIMEOUT; i++) {
683189714Syongari		if ((READ_REG(sc, TXP_ISR) & TXP_INT_A2H_0) != 0)
68480219Swpaul			break;
68580219Swpaul		DELAY(50);
68680219Swpaul	}
68780219Swpaul
688189714Syongari	if (i == TXP_TIMEOUT) {
689189714Syongari		device_printf(sc->sc_dev, "firmware wait failed comm0\n");
690189714Syongari		return (ETIMEDOUT);
69180219Swpaul	}
69280219Swpaul
69380219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
69480219Swpaul
695189714Syongari	if (READ_REG(sc, TXP_A2H_0) != STAT_WAITING_FOR_SEGMENT) {
696189714Syongari		device_printf(sc->sc_dev, "firmware not waiting for segment\n");
697189714Syongari		return (ETIMEDOUT);
69880219Swpaul	}
69980219Swpaul	return (0);
70080219Swpaul}
70180219Swpaul
70280219Swpaulstatic int
703189685Syongaritxp_download_fw_section(struct txp_softc *sc,
704189685Syongari    struct txp_fw_section_header *sect, int sectnum)
70580219Swpaul{
706189714Syongari	bus_dma_tag_t sec_tag;
707189714Syongari	bus_dmamap_t sec_map;
708189714Syongari	bus_addr_t sec_paddr;
709189714Syongari	uint8_t *sec_buf;
71080219Swpaul	int rseg, err = 0;
71180219Swpaul	struct mbuf m;
712189689Syongari	uint16_t csum;
71380219Swpaul
714189714Syongari	TXP_LOCK_ASSERT(sc);
715189714Syongari
716189714Syongari	/* Skip zero length sections. */
717189714Syongari	if (le32toh(sect->nbytes) == 0)
71880219Swpaul		return (0);
71980219Swpaul
720189714Syongari	/* Make sure we aren't past the end of the image. */
721189689Syongari	rseg = ((uint8_t *)sect) - ((uint8_t *)tc990image);
72280219Swpaul	if (rseg >= sizeof(tc990image)) {
723189714Syongari		device_printf(sc->sc_dev,
724189714Syongari		    "firmware invalid section address, section %d\n", sectnum);
725189714Syongari		return (EIO);
72680219Swpaul	}
72780219Swpaul
728189714Syongari	/* Make sure this section doesn't go past the end. */
729189714Syongari	rseg += le32toh(sect->nbytes);
73080219Swpaul	if (rseg >= sizeof(tc990image)) {
731189714Syongari		device_printf(sc->sc_dev, "firmware truncated section %d\n",
73280219Swpaul		    sectnum);
733189714Syongari		return (EIO);
73480219Swpaul	}
73580219Swpaul
736189714Syongari	sec_tag = NULL;
737189714Syongari	sec_map = NULL;
738189714Syongari	sec_buf = NULL;
739189714Syongari	/* XXX */
740189714Syongari	TXP_UNLOCK(sc);
741189714Syongari	err = txp_dma_alloc(sc, "firmware sections", &sec_tag, sizeof(uint32_t),
742189714Syongari	    0, &sec_map, (void **)&sec_buf, le32toh(sect->nbytes), &sec_paddr);
743189714Syongari	TXP_LOCK(sc);
744189714Syongari	if (err != 0)
745189714Syongari		goto bail;
746189714Syongari	bcopy(((uint8_t *)sect) + sizeof(*sect), sec_buf,
747189714Syongari	    le32toh(sect->nbytes));
74880219Swpaul
74980219Swpaul	/*
75080219Swpaul	 * dummy up mbuf and verify section checksum
75180219Swpaul	 */
75280219Swpaul	m.m_type = MT_DATA;
75380219Swpaul	m.m_next = m.m_nextpkt = NULL;
754189714Syongari	m.m_len = le32toh(sect->nbytes);
755189714Syongari	m.m_data = sec_buf;
75680219Swpaul	m.m_flags = 0;
757189714Syongari	csum = in_cksum(&m, le32toh(sect->nbytes));
75880219Swpaul	if (csum != sect->cksum) {
759189714Syongari		device_printf(sc->sc_dev,
760189714Syongari		    "firmware section %d, bad cksum (expected 0x%x got 0x%x)\n",
761189714Syongari		    sectnum, le16toh(sect->cksum), csum);
762189714Syongari		err = EIO;
76380219Swpaul		goto bail;
76480219Swpaul	}
76580219Swpaul
766189714Syongari	bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_PREWRITE);
767189714Syongari
768189714Syongari	WRITE_REG(sc, TXP_H2A_1, le32toh(sect->nbytes));
769189714Syongari	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
770189714Syongari	WRITE_REG(sc, TXP_H2A_2, le16toh(sect->cksum));
771189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
772189714Syongari	WRITE_REG(sc, TXP_H2A_3, le32toh(sect->addr));
773189714Syongari	TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE);
774189714Syongari	WRITE_REG(sc, TXP_H2A_4, TXP_ADDR_HI(sec_paddr));
775189714Syongari	TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE);
776189714Syongari	WRITE_REG(sc, TXP_H2A_5, TXP_ADDR_LO(sec_paddr));
777189714Syongari	TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE);
77880219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE);
779189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
78080219Swpaul
78180219Swpaul	if (txp_download_fw_wait(sc)) {
782189714Syongari		device_printf(sc->sc_dev,
783189714Syongari		    "firmware wait failed, section %d\n", sectnum);
784189714Syongari		err = ETIMEDOUT;
78580219Swpaul	}
78680219Swpaul
787189714Syongari	bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_POSTWRITE);
78880219Swpaulbail:
789189714Syongari	txp_dma_free(sc, &sec_tag, &sec_map, (void **)&sec_buf);
79080219Swpaul	return (err);
79180219Swpaul}
79280219Swpaul
793189714Syongaristatic int
794189685Syongaritxp_intr(void *vsc)
79580219Swpaul{
796189714Syongari	struct txp_softc *sc;
797189714Syongari	uint32_t status;
798189714Syongari
799189714Syongari	sc = vsc;
800189714Syongari	status = READ_REG(sc, TXP_ISR);
801189714Syongari	if ((status & TXP_INT_LATCH) == 0)
802189714Syongari		return (FILTER_STRAY);
803189714Syongari	WRITE_REG(sc, TXP_ISR, status);
804189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
805189714Syongari	taskqueue_enqueue(sc->sc_tq, &sc->sc_int_task);
806189714Syongari
807189714Syongari	return (FILTER_HANDLED);
808189714Syongari}
809189714Syongari
810189714Syongaristatic void
811189714Syongaritxp_int_task(void *arg, int pending)
812189714Syongari{
813189714Syongari	struct txp_softc *sc;
814189714Syongari	struct ifnet *ifp;
815189714Syongari	struct txp_hostvar *hv;
816189689Syongari	uint32_t isr;
817189714Syongari	int more;
81880219Swpaul
819189714Syongari	sc = (struct txp_softc *)arg;
820189714Syongari
821151772Sjhb	TXP_LOCK(sc);
822189714Syongari	ifp = sc->sc_ifp;
823189714Syongari	hv = sc->sc_hostvar;
82480219Swpaul	isr = READ_REG(sc, TXP_ISR);
825189714Syongari	if ((isr & TXP_INT_LATCH) != 0)
82680219Swpaul		WRITE_REG(sc, TXP_ISR, isr);
82780219Swpaul
828189714Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
829189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
830189714Syongari		    sc->sc_cdata.txp_hostvar_map,
831189714Syongari		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
832189714Syongari		more = 0;
83380219Swpaul		if ((*sc->sc_rxhir.r_roff) != (*sc->sc_rxhir.r_woff))
834189714Syongari			more += txp_rx_reclaim(sc, &sc->sc_rxhir,
835189714Syongari			    sc->sc_process_limit);
83680219Swpaul		if ((*sc->sc_rxlor.r_roff) != (*sc->sc_rxlor.r_woff))
837189714Syongari			more += txp_rx_reclaim(sc, &sc->sc_rxlor,
838189714Syongari			    sc->sc_process_limit);
839189714Syongari		/*
840189714Syongari		 * XXX
841189714Syongari		 * It seems controller is not smart enough to handle
842189714Syongari		 * FIFO overflow conditions under heavy network load.
843189714Syongari		 * No matter how often new Rx buffers are passed to
844189714Syongari		 * controller the situation didn't change. Maybe
845189714Syongari		 * flow-control would be the only way to mitigate the
846189714Syongari		 * issue but firmware does not have commands that
847189714Syongari		 * control the threshold of emitting pause frames.
848189714Syongari		 */
84980219Swpaul		if (hv->hv_rx_buf_write_idx == hv->hv_rx_buf_read_idx)
85080219Swpaul			txp_rxbuf_reclaim(sc);
85180219Swpaul		if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons !=
852189714Syongari		    TXP_OFFSET2IDX(le32toh(*(sc->sc_txhir.r_off)))))
85380219Swpaul			txp_tx_reclaim(sc, &sc->sc_txhir);
85480219Swpaul		if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons !=
855189714Syongari		    TXP_OFFSET2IDX(le32toh(*(sc->sc_txlor.r_off)))))
85680219Swpaul			txp_tx_reclaim(sc, &sc->sc_txlor);
857189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
858189714Syongari		    sc->sc_cdata.txp_hostvar_map,
859189714Syongari		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
860189714Syongari		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
861189714Syongari			txp_start_locked(sc->sc_ifp);
862189714Syongari		if (more != 0 || READ_REG(sc, TXP_ISR & TXP_INT_LATCH) != 0) {
863189714Syongari			taskqueue_enqueue(sc->sc_tq, &sc->sc_int_task);
864189714Syongari			TXP_UNLOCK(sc);
865189714Syongari			return;
866189714Syongari		}
86780219Swpaul	}
86880219Swpaul
869189714Syongari	/* Re-enable interrupts. */
870189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_NONE);
871151772Sjhb	TXP_UNLOCK(sc);
87280219Swpaul}
87380219Swpaul
874189714Syongari#ifndef __NO_STRICT_ALIGNMENT
875189714Syongaristatic __inline void
876189714Syongaritxp_fixup_rx(struct mbuf *m)
87780219Swpaul{
878189714Syongari	int i;
879189714Syongari	uint16_t *src, *dst;
880189714Syongari
881189714Syongari	src = mtod(m, uint16_t *);
882189714Syongari	dst = src - (TXP_RXBUF_ALIGN - ETHER_ALIGN) / sizeof *src;
883189714Syongari
884189714Syongari	for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++)
885189714Syongari		*dst++ = *src++;
886189714Syongari
887189714Syongari	m->m_data -= TXP_RXBUF_ALIGN - ETHER_ALIGN;
888189714Syongari}
889189714Syongari#endif
890189714Syongari
891189714Syongaristatic int
892189714Syongaritxp_rx_reclaim(struct txp_softc *sc, struct txp_rx_ring *r, int count)
893189714Syongari{
894189714Syongari	struct ifnet *ifp;
89580219Swpaul	struct txp_rx_desc *rxd;
89680219Swpaul	struct mbuf *m;
897189714Syongari	struct txp_rx_swdesc *sd;
898189714Syongari	uint32_t roff, woff, rx_stat, prog;
89980219Swpaul
900151772Sjhb	TXP_LOCK_ASSERT(sc);
90180219Swpaul
902189714Syongari	ifp = sc->sc_ifp;
90380219Swpaul
904189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_POSTREAD |
905189714Syongari	    BUS_DMASYNC_POSTWRITE);
90680219Swpaul
907189714Syongari	roff = le32toh(*r->r_roff);
908189714Syongari	woff = le32toh(*r->r_woff);
909189714Syongari	rxd = r->r_desc + roff / sizeof(struct txp_rx_desc);
910189714Syongari	for (prog = 0; roff != woff; prog++, count--) {
911189714Syongari		if (count <= 0)
912189714Syongari			break;
913189714Syongari		bcopy((u_long *)&rxd->rx_vaddrlo, &sd, sizeof(sd));
914189714Syongari		KASSERT(sd != NULL, ("%s: Rx desc ring corrupted", __func__));
915189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map,
916189714Syongari		    BUS_DMASYNC_POSTREAD);
917189714Syongari		bus_dmamap_unload(sc->sc_cdata.txp_rx_tag, sd->sd_map);
91880219Swpaul		m = sd->sd_mbuf;
919189714Syongari		KASSERT(m != NULL, ("%s: Rx buffer ring corrupted", __func__));
92080219Swpaul		sd->sd_mbuf = NULL;
921189714Syongari		TAILQ_REMOVE(&sc->sc_busy_list, sd, sd_next);
922189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next);
923189714Syongari		if ((rxd->rx_flags & RX_FLAGS_ERROR) != 0) {
924189714Syongari			if (bootverbose)
925189714Syongari				device_printf(sc->sc_dev, "Rx error %u\n",
926189714Syongari				    le32toh(rxd->rx_stat) & RX_ERROR_MASK);
927151772Sjhb			m_freem(m);
928189714Syongari			goto next;
92980219Swpaul		}
930189714Syongari
931189714Syongari		m->m_pkthdr.len = m->m_len = le16toh(rxd->rx_len);
932189714Syongari		m->m_pkthdr.rcvif = ifp;
933189714Syongari#ifndef __NO_STRICT_ALIGNMENT
934189714Syongari		txp_fixup_rx(m);
93580219Swpaul#endif
936189714Syongari		rx_stat = le32toh(rxd->rx_stat);
937189714Syongari		if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) {
938189714Syongari			if ((rx_stat & RX_STAT_IPCKSUMBAD) != 0)
939189714Syongari				m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
940189714Syongari			else if ((rx_stat & RX_STAT_IPCKSUMGOOD) != 0)
941189714Syongari				m->m_pkthdr.csum_flags |=
942189714Syongari				    CSUM_IP_CHECKED|CSUM_IP_VALID;
94380219Swpaul
944189714Syongari			if ((rx_stat & RX_STAT_TCPCKSUMGOOD) != 0 ||
945189714Syongari			    (rx_stat & RX_STAT_UDPCKSUMGOOD) != 0) {
946189714Syongari				m->m_pkthdr.csum_flags |=
947189714Syongari				    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
948189714Syongari				m->m_pkthdr.csum_data = 0xffff;
949189714Syongari			}
95080219Swpaul		}
95180219Swpaul
952189714Syongari		/*
953189714Syongari		 * XXX
954189714Syongari		 * Typhoon has a firmware bug that VLAN tag is always
955189714Syongari		 * stripped out even if it is told to not remove the tag.
956189714Syongari		 * Therefore don't check if_capenable here.
957189714Syongari		 */
958189714Syongari		if (/* (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && */
959189714Syongari		    (rx_stat & RX_STAT_VLAN) != 0) {
960189714Syongari			m->m_pkthdr.ether_vtag =
961189714Syongari			    bswap16((le32toh(rxd->rx_vlan) >> 16));
962162375Sandre			m->m_flags |= M_VLANTAG;
96380219Swpaul		}
96483115Sbrooks
965151772Sjhb		TXP_UNLOCK(sc);
966106937Ssam		(*ifp->if_input)(ifp, m);
967151772Sjhb		TXP_LOCK(sc);
96880219Swpaul
96980219Swpaulnext:
97080219Swpaul		roff += sizeof(struct txp_rx_desc);
97180219Swpaul		if (roff == (RX_ENTRIES * sizeof(struct txp_rx_desc))) {
97280219Swpaul			roff = 0;
97380219Swpaul			rxd = r->r_desc;
97480219Swpaul		} else
97580219Swpaul			rxd++;
976189714Syongari		prog++;
97780219Swpaul	}
97880219Swpaul
979189714Syongari	if (prog == 0)
980189714Syongari		return (0);
981189714Syongari
982189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_PREREAD |
983189714Syongari	    BUS_DMASYNC_PREWRITE);
984189714Syongari	*r->r_roff = le32toh(roff);
985189714Syongari
986189714Syongari	return (count > 0 ? 0 : EAGAIN);
98780219Swpaul}
98880219Swpaul
98980219Swpaulstatic void
990189685Syongaritxp_rxbuf_reclaim(struct txp_softc *sc)
99180219Swpaul{
992189714Syongari	struct txp_hostvar *hv;
99380219Swpaul	struct txp_rxbuf_desc *rbd;
994189714Syongari	struct txp_rx_swdesc *sd;
995189714Syongari	bus_dma_segment_t segs[1];
996189714Syongari	int nsegs, prod, prog;
997189714Syongari	uint32_t cons;
99880219Swpaul
999151772Sjhb	TXP_LOCK_ASSERT(sc);
1000189714Syongari
1001189714Syongari	hv = sc->sc_hostvar;
1002189714Syongari	cons = TXP_OFFSET2IDX(le32toh(hv->hv_rx_buf_read_idx));
1003189714Syongari	prod = sc->sc_rxbufprod;
1004189714Syongari	TXP_DESC_INC(prod, RXBUF_ENTRIES);
1005189714Syongari	if (prod == cons)
100680219Swpaul		return;
100780219Swpaul
1008189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag,
1009189714Syongari	    sc->sc_cdata.txp_rxbufs_map,
1010189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
101180219Swpaul
1012189714Syongari	for (prog = 0; prod != cons; prog++) {
1013189714Syongari		sd = TAILQ_FIRST(&sc->sc_free_list);
1014189714Syongari		if (sd == NULL)
101580219Swpaul			break;
1016189714Syongari		rbd = sc->sc_rxbufs + prod;
1017189714Syongari		bcopy((u_long *)&rbd->rb_vaddrlo, &sd, sizeof(sd));
1018151772Sjhb		sd->sd_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
101980219Swpaul		if (sd->sd_mbuf == NULL)
1020189714Syongari			break;
102180219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
1022189714Syongari#ifndef __NO_STRICT_ALIGNMENT
1023189714Syongari		m_adj(sd->sd_mbuf, TXP_RXBUF_ALIGN);
1024189714Syongari#endif
1025189714Syongari		if (bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_rx_tag,
1026189714Syongari		    sd->sd_map, sd->sd_mbuf, segs, &nsegs, 0) != 0) {
1027189714Syongari			m_freem(sd->sd_mbuf);
1028189714Syongari			sd->sd_mbuf = NULL;
1029189714Syongari			break;
1030189714Syongari		}
1031189714Syongari		KASSERT(nsegs == 1, ("%s : %d segments returned!", __func__,
1032189714Syongari		    nsegs));
1033189714Syongari		TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next);
1034189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_busy_list, sd, sd_next);
1035189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map,
1036189714Syongari		    BUS_DMASYNC_PREREAD);
1037189714Syongari		rbd->rb_paddrlo = htole32(TXP_ADDR_LO(segs[0].ds_addr));
1038189714Syongari		rbd->rb_paddrhi = htole32(TXP_ADDR_HI(segs[0].ds_addr));
1039189714Syongari		TXP_DESC_INC(prod, RXBUF_ENTRIES);
104080219Swpaul	}
104180219Swpaul
1042189714Syongari	if (prog == 0)
1043189714Syongari		return;
1044189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag,
1045189714Syongari	    sc->sc_cdata.txp_rxbufs_map,
1046189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1047189714Syongari	prod = (prod + RXBUF_ENTRIES - 1) % RXBUF_ENTRIES;
1048189714Syongari	sc->sc_rxbufprod = prod;
1049189714Syongari	hv->hv_rx_buf_write_idx = htole32(TXP_IDX2OFFSET(prod));
105080219Swpaul}
105180219Swpaul
105280219Swpaul/*
105380219Swpaul * Reclaim mbufs and entries from a transmit ring.
105480219Swpaul */
105580219Swpaulstatic void
1056189685Syongaritxp_tx_reclaim(struct txp_softc *sc, struct txp_tx_ring *r)
105780219Swpaul{
1058189714Syongari	struct ifnet *ifp;
1059189714Syongari	uint32_t idx;
1060189714Syongari	uint32_t cons, cnt;
1061189714Syongari	struct txp_tx_desc *txd;
1062189714Syongari	struct txp_swdesc *sd;
106380219Swpaul
1064151772Sjhb	TXP_LOCK_ASSERT(sc);
106580219Swpaul
1066189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_POSTREAD |
1067189714Syongari	    BUS_DMASYNC_POSTWRITE);
1068189714Syongari	ifp = sc->sc_ifp;
1069189714Syongari	idx = TXP_OFFSET2IDX(le32toh(*(r->r_off)));
1070189714Syongari	cons = r->r_cons;
1071189714Syongari	cnt = r->r_cnt;
1072189714Syongari	txd = r->r_desc + cons;
1073189714Syongari	sd = sc->sc_txd + cons;
1074189714Syongari
1075189714Syongari	for (cnt = r->r_cnt; cons != idx && cnt > 0; cnt--) {
1076189714Syongari		if ((txd->tx_flags & TX_FLAGS_TYPE_M) == TX_FLAGS_TYPE_DATA) {
1077189714Syongari			if (sd->sd_mbuf != NULL) {
1078189714Syongari				bus_dmamap_sync(sc->sc_cdata.txp_tx_tag,
1079189714Syongari				    sd->sd_map, BUS_DMASYNC_POSTWRITE);
1080189714Syongari				bus_dmamap_unload(sc->sc_cdata.txp_tx_tag,
1081189714Syongari				    sd->sd_map);
1082189714Syongari				m_freem(sd->sd_mbuf);
1083189714Syongari				sd->sd_mbuf = NULL;
108480219Swpaul				txd->tx_addrlo = 0;
108580219Swpaul				txd->tx_addrhi = 0;
1086189714Syongari				txd->tx_flags = 0;
108780219Swpaul			}
108880219Swpaul		}
1089148887Srwatson		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
109080219Swpaul
109180219Swpaul		if (++cons == TX_ENTRIES) {
109280219Swpaul			txd = r->r_desc;
109380219Swpaul			cons = 0;
109480219Swpaul			sd = sc->sc_txd;
109580219Swpaul		} else {
109680219Swpaul			txd++;
109780219Swpaul			sd++;
109880219Swpaul		}
109980219Swpaul	}
110080219Swpaul
1101189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_PREREAD |
1102189714Syongari	    BUS_DMASYNC_PREWRITE);
110380219Swpaul	r->r_cons = cons;
110480219Swpaul	r->r_cnt = cnt;
110580219Swpaul	if (cnt == 0)
1106189714Syongari		sc->sc_watchdog_timer = 0;
110780219Swpaul}
110880219Swpaul
110980219Swpaulstatic int
1110189685Syongaritxp_shutdown(device_t dev)
111180219Swpaul{
1112189714Syongari
1113189714Syongari	return (txp_suspend(dev));
1114189714Syongari}
1115189714Syongari
1116189714Syongaristatic int
1117189714Syongaritxp_suspend(device_t dev)
1118189714Syongari{
111980219Swpaul	struct txp_softc *sc;
1120189714Syongari	struct ifnet *ifp;
1121189714Syongari	uint8_t *eaddr;
1122189714Syongari	uint16_t p1;
1123189714Syongari	uint32_t p2;
1124189714Syongari	int pmc;
1125189714Syongari	uint16_t pmstat;
112680219Swpaul
112780219Swpaul	sc = device_get_softc(dev);
112880219Swpaul
1129151772Sjhb	TXP_LOCK(sc);
1130189714Syongari	ifp = sc->sc_ifp;
1131189714Syongari	txp_stop(sc);
1132189714Syongari	txp_init_rings(sc);
1133189714Syongari	/* Reset controller and make it reload sleep image. */
1134189714Syongari	txp_reset(sc);
1135189714Syongari	/* Let controller boot from sleep image. */
1136189714Syongari	if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0)
1137189714Syongari		device_printf(sc->sc_dev, "couldn't boot sleep image\n");
1138151772Sjhb
1139189714Syongari	/* Set station address. */
1140189714Syongari	eaddr = IF_LLADDR(sc->sc_ifp);
1141189714Syongari	p1 = 0;
1142189714Syongari	((uint8_t *)&p1)[1] = eaddr[0];
1143189714Syongari	((uint8_t *)&p1)[0] = eaddr[1];
1144189714Syongari	p1 = le16toh(p1);
1145189714Syongari	((uint8_t *)&p2)[3] = eaddr[2];
1146189714Syongari	((uint8_t *)&p2)[2] = eaddr[3];
1147189714Syongari	((uint8_t *)&p2)[1] = eaddr[4];
1148189714Syongari	((uint8_t *)&p2)[0] = eaddr[5];
1149189714Syongari	p2 = le32toh(p2);
1150189714Syongari	txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0, NULL, NULL,
1151189714Syongari	    NULL, TXP_CMD_WAIT);
1152189714Syongari	txp_set_filter(sc);
1153189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
1154189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
1155189714Syongari	txp_sleep(sc, sc->sc_ifp->if_capenable);
1156219902Sjhb	if (pci_find_cap(sc->sc_dev, PCIY_PMG, &pmc) == 0) {
1157189714Syongari		/* Request PME. */
1158189714Syongari		pmstat = pci_read_config(sc->sc_dev,
1159189714Syongari		    pmc + PCIR_POWER_STATUS, 2);
1160189714Syongari		pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE);
1161189714Syongari		if ((ifp->if_capenable & IFCAP_WOL) != 0)
1162189714Syongari			pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
1163189714Syongari		pci_write_config(sc->sc_dev,
1164189714Syongari		    pmc + PCIR_POWER_STATUS, pmstat, 2);
1165189714Syongari	}
1166189714Syongari	TXP_UNLOCK(sc);
116780219Swpaul
1168189714Syongari	return (0);
1169189714Syongari}
1170189714Syongari
1171189714Syongaristatic int
1172189714Syongaritxp_resume(device_t dev)
1173189714Syongari{
1174189714Syongari	struct txp_softc *sc;
1175189714Syongari	int pmc;
1176189714Syongari	uint16_t pmstat;
1177189714Syongari
1178189714Syongari	sc = device_get_softc(dev);
1179189714Syongari
1180189714Syongari	TXP_LOCK(sc);
1181219902Sjhb	if (pci_find_cap(sc->sc_dev, PCIY_PMG, &pmc) == 0) {
1182189714Syongari		/* Disable PME and clear PME status. */
1183189714Syongari		pmstat = pci_read_config(sc->sc_dev,
1184189714Syongari		    pmc + PCIR_POWER_STATUS, 2);
1185189714Syongari		if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) {
1186189714Syongari			pmstat &= ~PCIM_PSTAT_PMEENABLE;
1187189714Syongari			pci_write_config(sc->sc_dev,
1188189714Syongari			    pmc + PCIR_POWER_STATUS, pmstat, 2);
1189189714Syongari		}
1190189714Syongari	}
1191189714Syongari	if ((sc->sc_ifp->if_flags & IFF_UP) != 0)
1192189714Syongari		txp_init_locked(sc);
1193151772Sjhb	TXP_UNLOCK(sc);
119480219Swpaul
1195189688Syongari	return (0);
119680219Swpaul}
119780219Swpaul
1198189714Syongaristruct txp_dmamap_arg {
1199189714Syongari	bus_addr_t	txp_busaddr;
1200189714Syongari};
1201189714Syongari
1202189714Syongaristatic void
1203189714Syongaritxp_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
1204189714Syongari{
1205189714Syongari	struct txp_dmamap_arg *ctx;
1206189714Syongari
1207189714Syongari	if (error != 0)
1208189714Syongari		return;
1209189714Syongari
1210189714Syongari	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
1211189714Syongari
1212189714Syongari	ctx = (struct txp_dmamap_arg *)arg;
1213189714Syongari	ctx->txp_busaddr = segs[0].ds_addr;
1214189714Syongari}
1215189714Syongari
121680219Swpaulstatic int
1217189714Syongaritxp_dma_alloc(struct txp_softc *sc, char *type, bus_dma_tag_t *tag,
1218189714Syongari    bus_size_t alignment, bus_size_t boundary, bus_dmamap_t *map, void **buf,
1219189714Syongari    bus_size_t size, bus_addr_t *paddr)
1220189714Syongari{
1221189714Syongari	struct txp_dmamap_arg ctx;
1222189714Syongari	int error;
1223189714Syongari
1224189714Syongari	/* Create DMA block tag. */
1225189714Syongari	error = bus_dma_tag_create(
1226189714Syongari	    sc->sc_cdata.txp_parent_tag,	/* parent */
1227189714Syongari	    alignment, boundary,	/* algnmnt, boundary */
1228189714Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
1229189714Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1230189714Syongari	    NULL, NULL,			/* filter, filterarg */
1231189714Syongari	    size,			/* maxsize */
1232189714Syongari	    1,				/* nsegments */
1233189714Syongari	    size,			/* maxsegsize */
1234189714Syongari	    0,				/* flags */
1235189714Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1236189714Syongari	    tag);
1237189714Syongari	if (error != 0) {
1238189714Syongari		device_printf(sc->sc_dev,
1239189714Syongari		    "could not create DMA tag for %s.\n", type);
1240189714Syongari		return (error);
1241189714Syongari	}
1242189714Syongari
1243189714Syongari	*paddr = 0;
1244189714Syongari	/* Allocate DMA'able memory and load the DMA map. */
1245189714Syongari	error = bus_dmamem_alloc(*tag, buf, BUS_DMA_WAITOK | BUS_DMA_ZERO |
1246189714Syongari	    BUS_DMA_COHERENT, map);
1247189714Syongari	if (error != 0) {
1248189714Syongari		device_printf(sc->sc_dev,
1249189714Syongari		    "could not allocate DMA'able memory for %s.\n", type);
1250189714Syongari		return (error);
1251189714Syongari	}
1252189714Syongari
1253189714Syongari	ctx.txp_busaddr = 0;
1254189714Syongari	error = bus_dmamap_load(*tag, *map, *(uint8_t **)buf,
1255189714Syongari	    size, txp_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
1256189714Syongari	if (error != 0 || ctx.txp_busaddr == 0) {
1257189714Syongari		device_printf(sc->sc_dev,
1258189714Syongari		    "could not load DMA'able memory for %s.\n", type);
1259189714Syongari		return (error);
1260189714Syongari	}
1261189714Syongari	*paddr = ctx.txp_busaddr;
1262189714Syongari
1263189714Syongari	return (0);
1264189714Syongari}
1265189714Syongari
1266189714Syongaristatic void
1267189714Syongaritxp_dma_free(struct txp_softc *sc, bus_dma_tag_t *tag, bus_dmamap_t *map,
1268189714Syongari    void **buf)
1269189714Syongari{
1270189714Syongari
1271189714Syongari	if (*tag != NULL) {
1272189714Syongari		if (*map != NULL)
1273189714Syongari			bus_dmamap_unload(*tag, *map);
1274189714Syongari		if (*map != NULL && buf != NULL)
1275189714Syongari			bus_dmamem_free(*tag, *(uint8_t **)buf, *map);
1276189714Syongari		*(uint8_t **)buf = NULL;
1277189714Syongari		*map = NULL;
1278189714Syongari		bus_dma_tag_destroy(*tag);
1279189714Syongari		*tag = NULL;
1280189714Syongari	}
1281189714Syongari}
1282189714Syongari
1283189714Syongaristatic int
1284189685Syongaritxp_alloc_rings(struct txp_softc *sc)
128580219Swpaul{
128680219Swpaul	struct txp_boot_record *boot;
128780219Swpaul	struct txp_ldata *ld;
1288189714Syongari	struct txp_swdesc *txd;
1289189714Syongari	struct txp_rxbuf_desc *rbd;
1290189714Syongari	struct txp_rx_swdesc *sd;
1291189714Syongari	int error, i;
129280219Swpaul
1293189714Syongari	ld = &sc->sc_ldata;
1294189714Syongari	boot = ld->txp_boot;
129580219Swpaul
129680219Swpaul	/* boot record */
129780219Swpaul	sc->sc_boot = boot;
129880219Swpaul
1299189714Syongari	/*
1300189714Syongari	 * Create parent ring/DMA block tag.
1301189714Syongari	 * Datasheet says that all ring addresses and descriptors
1302189714Syongari	 * support 64bits addressing. However the controller is
1303189714Syongari	 * known to have no support DAC so limit DMA address space
1304189714Syongari	 * to 32bits.
1305189714Syongari	 */
1306189714Syongari	error = bus_dma_tag_create(
1307189714Syongari	    bus_get_dma_tag(sc->sc_dev), /* parent */
1308189714Syongari	    1, 0,			/* algnmnt, boundary */
1309189714Syongari	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1310189714Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1311189714Syongari	    NULL, NULL,			/* filter, filterarg */
1312189714Syongari	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
1313189714Syongari	    0,				/* nsegments */
1314189714Syongari	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1315189714Syongari	    0,				/* flags */
1316189714Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1317189714Syongari	    &sc->sc_cdata.txp_parent_tag);
1318189714Syongari	if (error != 0) {
1319189714Syongari		device_printf(sc->sc_dev, "could not create parent DMA tag.\n");
1320189714Syongari		return (error);
1321189714Syongari	}
132280219Swpaul
1323189714Syongari	/* Boot record. */
1324189714Syongari	error = txp_dma_alloc(sc, "boot record",
1325189714Syongari	    &sc->sc_cdata.txp_boot_tag, sizeof(uint32_t), 0,
1326189714Syongari	    &sc->sc_cdata.txp_boot_map, (void **)&sc->sc_ldata.txp_boot,
1327189714Syongari	    sizeof(struct txp_boot_record),
1328189714Syongari	    &sc->sc_ldata.txp_boot_paddr);
1329189714Syongari	if (error != 0)
1330189714Syongari		return (error);
1331189714Syongari	boot = sc->sc_ldata.txp_boot;
1332189714Syongari	sc->sc_boot = boot;
1333189714Syongari
1334189714Syongari	/* Host variables. */
1335189714Syongari	error = txp_dma_alloc(sc, "host variables",
1336189714Syongari	    &sc->sc_cdata.txp_hostvar_tag, sizeof(uint32_t), 0,
1337189714Syongari	    &sc->sc_cdata.txp_hostvar_map, (void **)&sc->sc_ldata.txp_hostvar,
1338189714Syongari	    sizeof(struct txp_hostvar),
1339189714Syongari	    &sc->sc_ldata.txp_hostvar_paddr);
1340189714Syongari	if (error != 0)
1341189714Syongari		return (error);
1342189714Syongari	boot->br_hostvar_lo =
1343189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_hostvar_paddr));
1344189714Syongari	boot->br_hostvar_hi =
1345189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_hostvar_paddr));
1346189714Syongari	sc->sc_hostvar = sc->sc_ldata.txp_hostvar;
1347189714Syongari
1348189714Syongari	/* Hi priority tx ring. */
1349189714Syongari	error = txp_dma_alloc(sc, "hi priority tx ring",
1350189714Syongari	    &sc->sc_cdata.txp_txhiring_tag, sizeof(struct txp_tx_desc), 0,
1351189714Syongari	    &sc->sc_cdata.txp_txhiring_map, (void **)&sc->sc_ldata.txp_txhiring,
1352189714Syongari	    sizeof(struct txp_tx_desc) * TX_ENTRIES,
1353189714Syongari	    &sc->sc_ldata.txp_txhiring_paddr);
1354189714Syongari	if (error != 0)
1355189714Syongari		return (error);
1356189714Syongari	boot->br_txhipri_lo =
1357189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_txhiring_paddr));
1358189714Syongari	boot->br_txhipri_hi =
1359189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_txhiring_paddr));
1360189714Syongari	boot->br_txhipri_siz =
1361189714Syongari	    htole32(TX_ENTRIES * sizeof(struct txp_tx_desc));
1362189714Syongari	sc->sc_txhir.r_tag = sc->sc_cdata.txp_txhiring_tag;
1363189714Syongari	sc->sc_txhir.r_map = sc->sc_cdata.txp_txhiring_map;
136480219Swpaul	sc->sc_txhir.r_reg = TXP_H2A_1;
1365189714Syongari	sc->sc_txhir.r_desc = sc->sc_ldata.txp_txhiring;
136680219Swpaul	sc->sc_txhir.r_cons = sc->sc_txhir.r_prod = sc->sc_txhir.r_cnt = 0;
136780219Swpaul	sc->sc_txhir.r_off = &sc->sc_hostvar->hv_tx_hi_desc_read_idx;
136880219Swpaul
1369189714Syongari	/* Low priority tx ring. */
1370189714Syongari	error = txp_dma_alloc(sc, "low priority tx ring",
1371189714Syongari	    &sc->sc_cdata.txp_txloring_tag, sizeof(struct txp_tx_desc), 0,
1372189714Syongari	    &sc->sc_cdata.txp_txloring_map, (void **)&sc->sc_ldata.txp_txloring,
1373189714Syongari	    sizeof(struct txp_tx_desc) * TX_ENTRIES,
1374189714Syongari	    &sc->sc_ldata.txp_txloring_paddr);
1375189714Syongari	if (error != 0)
1376189714Syongari		return (error);
1377189714Syongari	boot->br_txlopri_lo =
1378189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_txloring_paddr));
1379189714Syongari	boot->br_txlopri_hi =
1380189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_txloring_paddr));
1381189714Syongari	boot->br_txlopri_siz =
1382189714Syongari	    htole32(TX_ENTRIES * sizeof(struct txp_tx_desc));
1383189714Syongari	sc->sc_txlor.r_tag = sc->sc_cdata.txp_txloring_tag;
1384189714Syongari	sc->sc_txlor.r_map = sc->sc_cdata.txp_txloring_map;
138580219Swpaul	sc->sc_txlor.r_reg = TXP_H2A_3;
1386189714Syongari	sc->sc_txlor.r_desc = sc->sc_ldata.txp_txloring;
138780219Swpaul	sc->sc_txlor.r_cons = sc->sc_txlor.r_prod = sc->sc_txlor.r_cnt = 0;
138880219Swpaul	sc->sc_txlor.r_off = &sc->sc_hostvar->hv_tx_lo_desc_read_idx;
138980219Swpaul
1390189714Syongari	/* High priority rx ring. */
1391189714Syongari	error = txp_dma_alloc(sc, "hi priority rx ring",
1392196721Syongari	    &sc->sc_cdata.txp_rxhiring_tag,
1393196721Syongari	    roundup(sizeof(struct txp_rx_desc), 16), 0,
1394189714Syongari	    &sc->sc_cdata.txp_rxhiring_map, (void **)&sc->sc_ldata.txp_rxhiring,
1395189714Syongari	    sizeof(struct txp_rx_desc) * RX_ENTRIES,
1396189714Syongari	    &sc->sc_ldata.txp_rxhiring_paddr);
1397189714Syongari	if (error != 0)
1398189714Syongari		return (error);
1399189714Syongari	boot->br_rxhipri_lo =
1400189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxhiring_paddr));
1401189714Syongari	boot->br_rxhipri_hi =
1402189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxhiring_paddr));
1403189714Syongari	boot->br_rxhipri_siz =
1404189714Syongari	    htole32(RX_ENTRIES * sizeof(struct txp_rx_desc));
1405189714Syongari	sc->sc_rxhir.r_tag = sc->sc_cdata.txp_rxhiring_tag;
1406189714Syongari	sc->sc_rxhir.r_map = sc->sc_cdata.txp_rxhiring_map;
1407189714Syongari	sc->sc_rxhir.r_desc = sc->sc_ldata.txp_rxhiring;
140880219Swpaul	sc->sc_rxhir.r_roff = &sc->sc_hostvar->hv_rx_hi_read_idx;
140980219Swpaul	sc->sc_rxhir.r_woff = &sc->sc_hostvar->hv_rx_hi_write_idx;
141080219Swpaul
1411189714Syongari	/* Low priority rx ring. */
1412189714Syongari	error = txp_dma_alloc(sc, "low priority rx ring",
1413196721Syongari	    &sc->sc_cdata.txp_rxloring_tag,
1414196721Syongari	    roundup(sizeof(struct txp_rx_desc), 16), 0,
1415189714Syongari	    &sc->sc_cdata.txp_rxloring_map, (void **)&sc->sc_ldata.txp_rxloring,
1416189714Syongari	    sizeof(struct txp_rx_desc) * RX_ENTRIES,
1417189714Syongari	    &sc->sc_ldata.txp_rxloring_paddr);
1418189714Syongari	if (error != 0)
1419189714Syongari		return (error);
1420189714Syongari	boot->br_rxlopri_lo =
1421189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxloring_paddr));
1422189714Syongari	boot->br_rxlopri_hi =
1423189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxloring_paddr));
1424189714Syongari	boot->br_rxlopri_siz =
1425189714Syongari	    htole32(RX_ENTRIES * sizeof(struct txp_rx_desc));
1426189714Syongari	sc->sc_rxlor.r_tag = sc->sc_cdata.txp_rxloring_tag;
1427189714Syongari	sc->sc_rxlor.r_map = sc->sc_cdata.txp_rxloring_map;
1428189714Syongari	sc->sc_rxlor.r_desc = sc->sc_ldata.txp_rxloring;
142980219Swpaul	sc->sc_rxlor.r_roff = &sc->sc_hostvar->hv_rx_lo_read_idx;
143080219Swpaul	sc->sc_rxlor.r_woff = &sc->sc_hostvar->hv_rx_lo_write_idx;
143180219Swpaul
1432189714Syongari	/* Command ring. */
1433189714Syongari	error = txp_dma_alloc(sc, "command ring",
1434189714Syongari	    &sc->sc_cdata.txp_cmdring_tag, sizeof(struct txp_cmd_desc), 0,
1435189714Syongari	    &sc->sc_cdata.txp_cmdring_map, (void **)&sc->sc_ldata.txp_cmdring,
1436189714Syongari	    sizeof(struct txp_cmd_desc) * CMD_ENTRIES,
1437189714Syongari	    &sc->sc_ldata.txp_cmdring_paddr);
1438189714Syongari	if (error != 0)
1439189714Syongari		return (error);
1440189714Syongari	boot->br_cmd_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_cmdring_paddr));
1441189714Syongari	boot->br_cmd_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_cmdring_paddr));
1442189714Syongari	boot->br_cmd_siz = htole32(CMD_ENTRIES * sizeof(struct txp_cmd_desc));
1443189714Syongari	sc->sc_cmdring.base = sc->sc_ldata.txp_cmdring;
144480219Swpaul	sc->sc_cmdring.size = CMD_ENTRIES * sizeof(struct txp_cmd_desc);
144580219Swpaul	sc->sc_cmdring.lastwrite = 0;
144680219Swpaul
1447189714Syongari	/* Response ring. */
1448189714Syongari	error = txp_dma_alloc(sc, "response ring",
1449189714Syongari	    &sc->sc_cdata.txp_rspring_tag, sizeof(struct txp_rsp_desc), 0,
1450189714Syongari	    &sc->sc_cdata.txp_rspring_map, (void **)&sc->sc_ldata.txp_rspring,
1451189714Syongari	    sizeof(struct txp_rsp_desc) * RSP_ENTRIES,
1452189714Syongari	    &sc->sc_ldata.txp_rspring_paddr);
1453189714Syongari	if (error != 0)
1454189714Syongari		return (error);
1455189714Syongari	boot->br_resp_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rspring_paddr));
1456189714Syongari	boot->br_resp_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rspring_paddr));
1457189714Syongari	boot->br_resp_siz = htole32(RSP_ENTRIES * sizeof(struct txp_rsp_desc));
1458189714Syongari	sc->sc_rspring.base = sc->sc_ldata.txp_rspring;
145980219Swpaul	sc->sc_rspring.size = RSP_ENTRIES * sizeof(struct txp_rsp_desc);
146080219Swpaul	sc->sc_rspring.lastwrite = 0;
146180219Swpaul
1462189714Syongari	/* Receive buffer ring. */
1463189714Syongari	error = txp_dma_alloc(sc, "receive buffer ring",
1464189714Syongari	    &sc->sc_cdata.txp_rxbufs_tag, sizeof(struct txp_rxbuf_desc), 0,
1465189714Syongari	    &sc->sc_cdata.txp_rxbufs_map, (void **)&sc->sc_ldata.txp_rxbufs,
1466189714Syongari	    sizeof(struct txp_rxbuf_desc) * RXBUF_ENTRIES,
1467189714Syongari	    &sc->sc_ldata.txp_rxbufs_paddr);
1468189714Syongari	if (error != 0)
1469189714Syongari		return (error);
1470189714Syongari	boot->br_rxbuf_lo =
1471189714Syongari	    htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxbufs_paddr));
1472189714Syongari	boot->br_rxbuf_hi =
1473189714Syongari	    htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxbufs_paddr));
1474189714Syongari	boot->br_rxbuf_siz =
1475189714Syongari	    htole32(RXBUF_ENTRIES * sizeof(struct txp_rxbuf_desc));
1476189714Syongari	sc->sc_rxbufs = sc->sc_ldata.txp_rxbufs;
147780219Swpaul
1478189714Syongari	/* Zero ring. */
1479189714Syongari	error = txp_dma_alloc(sc, "zero buffer",
1480189714Syongari	    &sc->sc_cdata.txp_zero_tag, sizeof(uint32_t), 0,
1481189714Syongari	    &sc->sc_cdata.txp_zero_map, (void **)&sc->sc_ldata.txp_zero,
1482189714Syongari	    sizeof(uint32_t), &sc->sc_ldata.txp_zero_paddr);
1483189714Syongari	if (error != 0)
1484189714Syongari		return (error);
1485189714Syongari	boot->br_zero_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_zero_paddr));
1486189714Syongari	boot->br_zero_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_zero_paddr));
1487189714Syongari
1488189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_boot_tag, sc->sc_cdata.txp_boot_map,
1489189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1490189714Syongari
1491189714Syongari	/* Create Tx buffers. */
1492189714Syongari	error = bus_dma_tag_create(
1493189714Syongari	    sc->sc_cdata.txp_parent_tag,	/* parent */
1494189714Syongari	    1, 0,			/* algnmnt, boundary */
1495189714Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
1496189714Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1497189714Syongari	    NULL, NULL,			/* filter, filterarg */
1498189714Syongari	    MCLBYTES * TXP_MAXTXSEGS,	/* maxsize */
1499189714Syongari	    TXP_MAXTXSEGS,		/* nsegments */
1500189714Syongari	    MCLBYTES,			/* maxsegsize */
1501189714Syongari	    0,				/* flags */
1502189714Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1503189714Syongari	    &sc->sc_cdata.txp_tx_tag);
1504189714Syongari	if (error != 0) {
1505189714Syongari		device_printf(sc->sc_dev, "could not create Tx DMA tag.\n");
1506189714Syongari		goto fail;
1507189714Syongari	}
1508189714Syongari
1509189714Syongari	/* Create tag for Rx buffers. */
1510189714Syongari	error = bus_dma_tag_create(
1511189714Syongari	    sc->sc_cdata.txp_parent_tag,	/* parent */
1512189714Syongari	    TXP_RXBUF_ALIGN, 0,		/* algnmnt, boundary */
1513189714Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
1514189714Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1515189714Syongari	    NULL, NULL,			/* filter, filterarg */
1516189714Syongari	    MCLBYTES,			/* maxsize */
1517189714Syongari	    1,				/* nsegments */
1518189714Syongari	    MCLBYTES,			/* maxsegsize */
1519189714Syongari	    0,				/* flags */
1520189714Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1521189714Syongari	    &sc->sc_cdata.txp_rx_tag);
1522189714Syongari	if (error != 0) {
1523189714Syongari		device_printf(sc->sc_dev, "could not create Rx DMA tag.\n");
1524189714Syongari		goto fail;
1525189714Syongari	}
1526189714Syongari
1527189714Syongari	/* Create DMA maps for Tx buffers. */
1528189714Syongari	for (i = 0; i < TX_ENTRIES; i++) {
1529189714Syongari		txd = &sc->sc_txd[i];
1530189714Syongari		txd->sd_mbuf = NULL;
1531189714Syongari		txd->sd_map = NULL;
1532189714Syongari		error = bus_dmamap_create(sc->sc_cdata.txp_tx_tag, 0,
1533189714Syongari		    &txd->sd_map);
1534189714Syongari		if (error != 0) {
1535189714Syongari			device_printf(sc->sc_dev,
1536189714Syongari			    "could not create Tx dmamap.\n");
1537189714Syongari			goto fail;
1538189714Syongari		}
1539189714Syongari	}
1540189714Syongari
1541189714Syongari	/* Create DMA maps for Rx buffers. */
154280219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
1543189714Syongari		sd = malloc(sizeof(struct txp_rx_swdesc), M_DEVBUF,
1544189714Syongari		    M_NOWAIT | M_ZERO);
1545189714Syongari		if (sd == NULL) {
1546189714Syongari			error = ENOMEM;
1547189714Syongari			goto fail;
1548189714Syongari		}
1549189714Syongari		/*
1550189714Syongari		 * The virtual address part of descriptor is not used
1551189714Syongari		 * by hardware so use that to save an ring entry. We
1552189714Syongari		 * need bcopy here otherwise the address wouldn't be
1553189714Syongari		 * valid on big-endian architectures.
1554189714Syongari		 */
1555189714Syongari		rbd = sc->sc_rxbufs + i;
1556189714Syongari		bcopy(&sd, (u_long *)&rbd->rb_vaddrlo, sizeof(sd));
155780457Swpaul		sd->sd_mbuf = NULL;
1558189714Syongari		sd->sd_map = NULL;
1559189714Syongari		error = bus_dmamap_create(sc->sc_cdata.txp_rx_tag, 0,
1560189714Syongari		    &sd->sd_map);
1561189714Syongari		if (error != 0) {
1562189714Syongari			device_printf(sc->sc_dev,
1563189714Syongari			    "could not create Rx dmamap.\n");
1564189714Syongari			goto fail;
1565189714Syongari		}
1566189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next);
156780219Swpaul	}
1568189714Syongari
1569189714Syongarifail:
1570189714Syongari	return (error);
1571189714Syongari}
1572189714Syongari
1573189714Syongaristatic void
1574189714Syongaritxp_init_rings(struct txp_softc *sc)
1575189714Syongari{
1576189714Syongari
1577189714Syongari	bzero(sc->sc_ldata.txp_hostvar, sizeof(struct txp_hostvar));
1578189714Syongari	bzero(sc->sc_ldata.txp_zero, sizeof(uint32_t));
1579189714Syongari	sc->sc_txhir.r_cons = 0;
1580189714Syongari	sc->sc_txhir.r_prod = 0;
1581189714Syongari	sc->sc_txhir.r_cnt = 0;
1582189714Syongari	sc->sc_txlor.r_cons = 0;
1583189714Syongari	sc->sc_txlor.r_prod = 0;
1584189714Syongari	sc->sc_txlor.r_cnt = 0;
1585189714Syongari	sc->sc_cmdring.lastwrite = 0;
1586189714Syongari	sc->sc_rspring.lastwrite = 0;
158780219Swpaul	sc->sc_rxbufprod = 0;
1588189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1589189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1590189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1591189714Syongari}
159280219Swpaul
1593189714Syongaristatic int
1594189714Syongaritxp_wait(struct txp_softc *sc, uint32_t state)
1595189714Syongari{
1596189714Syongari	uint32_t reg;
1597189714Syongari	int i;
159880219Swpaul
1599189714Syongari	for (i = 0; i < TXP_TIMEOUT; i++) {
1600189714Syongari		reg = READ_REG(sc, TXP_A2H_0);
1601189714Syongari		if (reg == state)
160280219Swpaul			break;
160380219Swpaul		DELAY(50);
160480219Swpaul	}
160580219Swpaul
1606189714Syongari	return (i == TXP_TIMEOUT ? ETIMEDOUT : 0);
1607189714Syongari}
160880219Swpaul
1609189714Syongaristatic void
1610189714Syongaritxp_free_rings(struct txp_softc *sc)
1611189714Syongari{
1612189714Syongari	struct txp_swdesc *txd;
1613189714Syongari	struct txp_rx_swdesc *sd;
1614189714Syongari	int i;
161580219Swpaul
1616189714Syongari	/* Tx buffers. */
1617189714Syongari	if (sc->sc_cdata.txp_tx_tag != NULL) {
1618189714Syongari		for (i = 0; i < TX_ENTRIES; i++) {
1619189714Syongari			txd = &sc->sc_txd[i];
1620189714Syongari			if (txd->sd_map != NULL) {
1621189714Syongari				bus_dmamap_destroy(sc->sc_cdata.txp_tx_tag,
1622189714Syongari				    txd->sd_map);
1623189714Syongari				txd->sd_map = NULL;
1624189714Syongari			}
1625189714Syongari		}
1626189714Syongari		bus_dma_tag_destroy(sc->sc_cdata.txp_tx_tag);
1627189714Syongari		sc->sc_cdata.txp_tx_tag = NULL;
162880219Swpaul	}
1629189714Syongari	/* Rx buffers. */
1630189714Syongari	if (sc->sc_cdata.txp_rx_tag != NULL) {
1631189714Syongari		if (sc->sc_rxbufs != NULL) {
1632189714Syongari			KASSERT(TAILQ_FIRST(&sc->sc_busy_list) == NULL,
1633189714Syongari			    ("%s : still have busy Rx buffers", __func__));
1634189714Syongari			while ((sd = TAILQ_FIRST(&sc->sc_free_list)) != NULL) {
1635189714Syongari				TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next);
1636189714Syongari				if (sd->sd_map != NULL) {
1637189714Syongari					bus_dmamap_destroy(
1638189714Syongari					    sc->sc_cdata.txp_rx_tag,
1639189714Syongari					    sd->sd_map);
1640189714Syongari					sd->sd_map = NULL;
1641189714Syongari				}
1642189714Syongari				free(sd, M_DEVBUF);
1643189714Syongari			}
1644189714Syongari		}
1645189714Syongari		bus_dma_tag_destroy(sc->sc_cdata.txp_rx_tag);
1646189714Syongari		sc->sc_cdata.txp_rx_tag = NULL;
164780219Swpaul	}
164880219Swpaul
1649189714Syongari	/* Hi priority Tx ring. */
1650189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_txhiring_tag,
1651189714Syongari	    &sc->sc_cdata.txp_txhiring_map,
1652189714Syongari	    (void **)&sc->sc_ldata.txp_txhiring);
1653189714Syongari	/* Low priority Tx ring. */
1654189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_txloring_tag,
1655189714Syongari	    &sc->sc_cdata.txp_txloring_map,
1656189714Syongari	    (void **)&sc->sc_ldata.txp_txloring);
1657189714Syongari	/* Hi priority Rx ring. */
1658189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_rxhiring_tag,
1659189714Syongari	    &sc->sc_cdata.txp_rxhiring_map,
1660189714Syongari	    (void **)&sc->sc_ldata.txp_rxhiring);
1661189714Syongari	/* Low priority Rx ring. */
1662189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_rxloring_tag,
1663189714Syongari	    &sc->sc_cdata.txp_rxloring_map,
1664189714Syongari	    (void **)&sc->sc_ldata.txp_rxloring);
1665189714Syongari	/* Receive buffer ring. */
1666189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_rxbufs_tag,
1667189714Syongari	    &sc->sc_cdata.txp_rxbufs_map, (void **)&sc->sc_ldata.txp_rxbufs);
1668189714Syongari	/* Command ring. */
1669189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_cmdring_tag,
1670189714Syongari	    &sc->sc_cdata.txp_cmdring_map, (void **)&sc->sc_ldata.txp_cmdring);
1671189714Syongari	/* Response ring. */
1672189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_rspring_tag,
1673189714Syongari	    &sc->sc_cdata.txp_rspring_map, (void **)&sc->sc_ldata.txp_rspring);
1674189714Syongari	/* Zero ring. */
1675189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_zero_tag,
1676189714Syongari	    &sc->sc_cdata.txp_zero_map, (void **)&sc->sc_ldata.txp_zero);
1677189714Syongari	/* Host variables. */
1678189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_hostvar_tag,
1679189714Syongari	    &sc->sc_cdata.txp_hostvar_map, (void **)&sc->sc_ldata.txp_hostvar);
1680189714Syongari	/* Boot record. */
1681189714Syongari	txp_dma_free(sc, &sc->sc_cdata.txp_boot_tag,
1682189714Syongari	    &sc->sc_cdata.txp_boot_map, (void **)&sc->sc_ldata.txp_boot);
168380219Swpaul
1684189714Syongari	if (sc->sc_cdata.txp_parent_tag != NULL) {
1685189714Syongari		bus_dma_tag_destroy(sc->sc_cdata.txp_parent_tag);
1686189714Syongari		sc->sc_cdata.txp_parent_tag = NULL;
1687189714Syongari	}
1688189714Syongari
168980219Swpaul}
169080219Swpaul
169180219Swpaulstatic int
1692189685Syongaritxp_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
169380219Swpaul{
169480219Swpaul	struct txp_softc *sc = ifp->if_softc;
169580219Swpaul	struct ifreq *ifr = (struct ifreq *)data;
1696189714Syongari	int capenable, error = 0, mask;
169780219Swpaul
1698189714Syongari	switch(command) {
169980219Swpaul	case SIOCSIFFLAGS:
1700151772Sjhb		TXP_LOCK(sc);
1701189714Syongari		if ((ifp->if_flags & IFF_UP) != 0) {
1702189714Syongari			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
1703189714Syongari				if (((ifp->if_flags ^ sc->sc_if_flags)
1704189714Syongari				    & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
1705189714Syongari					txp_set_filter(sc);
1706189714Syongari			} else {
1707189714Syongari				if ((sc->sc_flags & TXP_FLAG_DETACH) == 0)
1708189714Syongari					txp_init_locked(sc);
1709189714Syongari			}
171080219Swpaul		} else {
1711189714Syongari			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
171280219Swpaul				txp_stop(sc);
171380219Swpaul		}
1714189714Syongari		sc->sc_if_flags = ifp->if_flags;
1715151772Sjhb		TXP_UNLOCK(sc);
171680219Swpaul		break;
171780219Swpaul	case SIOCADDMULTI:
171880219Swpaul	case SIOCDELMULTI:
171980219Swpaul		/*
172080219Swpaul		 * Multicast list has changed; set the hardware
172180219Swpaul		 * filter accordingly.
172280219Swpaul		 */
1723151772Sjhb		TXP_LOCK(sc);
1724189714Syongari		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
1725189714Syongari			txp_set_filter(sc);
1726151772Sjhb		TXP_UNLOCK(sc);
172780219Swpaul		break;
1728189714Syongari	case SIOCSIFCAP:
1729189714Syongari		TXP_LOCK(sc);
1730189714Syongari		capenable = ifp->if_capenable;
1731189714Syongari		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
1732189714Syongari		if ((mask & IFCAP_TXCSUM) != 0 &&
1733189714Syongari		    (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
1734189714Syongari			ifp->if_capenable ^= IFCAP_TXCSUM;
1735189714Syongari			if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
1736189714Syongari				ifp->if_hwassist |= TXP_CSUM_FEATURES;
1737189714Syongari			else
1738189714Syongari				ifp->if_hwassist &= ~TXP_CSUM_FEATURES;
1739189714Syongari		}
1740189714Syongari		if ((mask & IFCAP_RXCSUM) != 0 &&
1741189714Syongari		    (ifp->if_capabilities & IFCAP_RXCSUM) != 0)
1742189714Syongari			ifp->if_capenable ^= IFCAP_RXCSUM;
1743189714Syongari		if ((mask & IFCAP_WOL_MAGIC) != 0 &&
1744189714Syongari		    (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0)
1745189714Syongari			ifp->if_capenable ^= IFCAP_WOL_MAGIC;
1746189714Syongari		if ((mask & IFCAP_VLAN_HWTAGGING) != 0 &&
1747189714Syongari		    (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0)
1748189714Syongari			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
1749189714Syongari		if ((mask & IFCAP_VLAN_HWCSUM) != 0 &&
1750189714Syongari		    (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0)
1751189714Syongari			ifp->if_capenable ^= IFCAP_VLAN_HWCSUM;
1752189714Syongari		if ((ifp->if_capenable & IFCAP_TXCSUM) == 0)
1753189714Syongari			ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM;
1754189714Syongari		if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
1755189714Syongari			ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM;
1756189714Syongari		if (capenable != ifp->if_capenable)
1757189714Syongari			txp_set_capabilities(sc);
1758189714Syongari		TXP_UNLOCK(sc);
1759189714Syongari		VLAN_CAPABILITIES(ifp);
1760189714Syongari		break;
176180219Swpaul	case SIOCGIFMEDIA:
176280219Swpaul	case SIOCSIFMEDIA:
176380219Swpaul		error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, command);
176480219Swpaul		break;
176580219Swpaul	default:
1766106937Ssam		error = ether_ioctl(ifp, command, data);
176780219Swpaul		break;
176880219Swpaul	}
176980219Swpaul
1770189688Syongari	return (error);
177180219Swpaul}
177280219Swpaul
177380219Swpaulstatic int
1774189685Syongaritxp_rxring_fill(struct txp_softc *sc)
177580219Swpaul{
1776189714Syongari	struct txp_rxbuf_desc *rbd;
1777189714Syongari	struct txp_rx_swdesc *sd;
1778189714Syongari	bus_dma_segment_t segs[1];
1779189714Syongari	int error, i, nsegs;
178080219Swpaul
1781151772Sjhb	TXP_LOCK_ASSERT(sc);
178280219Swpaul
1783189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag,
1784189714Syongari	    sc->sc_cdata.txp_rxbufs_map,
1785189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1786189714Syongari
178780219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
1788189714Syongari		sd = TAILQ_FIRST(&sc->sc_free_list);
1789189714Syongari		if (sd == NULL)
1790189714Syongari			return (ENOMEM);
1791189714Syongari		rbd = sc->sc_rxbufs + i;
1792189714Syongari		bcopy(&sd, (u_long *)&rbd->rb_vaddrlo, sizeof(sd));
1793189714Syongari		KASSERT(sd->sd_mbuf == NULL,
1794189714Syongari		    ("%s : Rx buffer ring corrupted", __func__));
1795151772Sjhb		sd->sd_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
179680219Swpaul		if (sd->sd_mbuf == NULL)
1797189714Syongari			return (ENOMEM);
179880219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
1799189714Syongari#ifndef __NO_STRICT_ALIGNMENT
1800189714Syongari		m_adj(sd->sd_mbuf, TXP_RXBUF_ALIGN);
1801189714Syongari#endif
1802189714Syongari		if ((error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_rx_tag,
1803189714Syongari		    sd->sd_map, sd->sd_mbuf, segs, &nsegs, 0)) != 0) {
1804189714Syongari			m_freem(sd->sd_mbuf);
1805189714Syongari			sd->sd_mbuf = NULL;
1806189714Syongari			return (error);
1807189714Syongari		}
1808189714Syongari		KASSERT(nsegs == 1, ("%s : %d segments returned!", __func__,
1809189714Syongari		    nsegs));
1810189714Syongari		TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next);
1811189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_busy_list, sd, sd_next);
1812189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map,
1813189714Syongari		    BUS_DMASYNC_PREREAD);
1814189714Syongari		rbd->rb_paddrlo = htole32(TXP_ADDR_LO(segs[0].ds_addr));
1815189714Syongari		rbd->rb_paddrhi = htole32(TXP_ADDR_HI(segs[0].ds_addr));
181680219Swpaul	}
181780219Swpaul
1818189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag,
1819189714Syongari	    sc->sc_cdata.txp_rxbufs_map,
1820189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1821189714Syongari	sc->sc_rxbufprod = RXBUF_ENTRIES - 1;
1822189714Syongari	sc->sc_hostvar->hv_rx_buf_write_idx =
1823189714Syongari	    htole32(TXP_IDX2OFFSET(RXBUF_ENTRIES - 1));
182480219Swpaul
1825189688Syongari	return (0);
182680219Swpaul}
182780219Swpaul
182880219Swpaulstatic void
1829189685Syongaritxp_rxring_empty(struct txp_softc *sc)
183080219Swpaul{
1831189714Syongari	struct txp_rx_swdesc *sd;
1832189714Syongari	int cnt;
183380219Swpaul
1834151772Sjhb	TXP_LOCK_ASSERT(sc);
1835189714Syongari
183680219Swpaul	if (sc->sc_rxbufs == NULL)
183780219Swpaul		return;
1838189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1839189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1840189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
184180219Swpaul
1842189714Syongari	/* Release allocated Rx buffers. */
1843189714Syongari	cnt = 0;
1844189714Syongari	while ((sd = TAILQ_FIRST(&sc->sc_busy_list)) != NULL) {
1845189714Syongari		TAILQ_REMOVE(&sc->sc_busy_list, sd, sd_next);
1846189714Syongari		KASSERT(sd->sd_mbuf != NULL,
1847189714Syongari		    ("%s : Rx buffer ring corrupted", __func__));
1848189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map,
1849189714Syongari		    BUS_DMASYNC_POSTREAD);
1850189714Syongari		bus_dmamap_unload(sc->sc_cdata.txp_rx_tag, sd->sd_map);
1851189714Syongari		m_freem(sd->sd_mbuf);
1852189714Syongari		sd->sd_mbuf = NULL;
1853189714Syongari		TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next);
1854189714Syongari		cnt++;
185580219Swpaul	}
185680219Swpaul}
185780219Swpaul
185880219Swpaulstatic void
1859189685Syongaritxp_init(void *xsc)
186080219Swpaul{
186180219Swpaul	struct txp_softc *sc;
1862151772Sjhb
1863151772Sjhb	sc = xsc;
1864151772Sjhb	TXP_LOCK(sc);
1865151772Sjhb	txp_init_locked(sc);
1866151772Sjhb	TXP_UNLOCK(sc);
1867151772Sjhb}
1868151772Sjhb
1869151772Sjhbstatic void
1870189685Syongaritxp_init_locked(struct txp_softc *sc)
1871151772Sjhb{
187280219Swpaul	struct ifnet *ifp;
1873189714Syongari	uint8_t *eaddr;
1874189689Syongari	uint16_t p1;
1875189689Syongari	uint32_t p2;
1876189714Syongari	int error;
187780219Swpaul
1878151772Sjhb	TXP_LOCK_ASSERT(sc);
1879147256Sbrooks	ifp = sc->sc_ifp;
188080219Swpaul
1881189714Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
188280219Swpaul		return;
188380219Swpaul
1884189714Syongari	/* Initialize ring structure. */
1885189714Syongari	txp_init_rings(sc);
1886189714Syongari	/* Wakeup controller. */
1887189714Syongari	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_WAKEUP);
1888189714Syongari	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
1889189714Syongari	/*
1890189714Syongari	 * It seems that earlier NV image can go back to online from
1891189714Syongari	 * wakeup command but newer ones require controller reset.
1892189714Syongari	 * So jut reset controller again.
1893189714Syongari	 */
1894189714Syongari	if (txp_reset(sc) != 0)
1895189714Syongari		goto init_fail;
1896189714Syongari	/* Download firmware. */
1897189714Syongari	error = txp_download_fw(sc);
1898189714Syongari	if (error != 0) {
1899189714Syongari		device_printf(sc->sc_dev, "could not download firmware.\n");
1900189714Syongari		goto init_fail;
1901189714Syongari	}
1902189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1903189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1904189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1905189714Syongari	if ((error = txp_rxring_fill(sc)) != 0) {
1906189714Syongari		device_printf(sc->sc_dev, "no memory for Rx buffers.\n");
1907189714Syongari		goto init_fail;
1908189714Syongari	}
1909189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1910189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1911189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1912189714Syongari	if (txp_boot(sc, STAT_WAITING_FOR_BOOT) != 0) {
1913189714Syongari		device_printf(sc->sc_dev, "could not boot firmware.\n");
1914189714Syongari		goto init_fail;
1915189714Syongari	}
191680219Swpaul
1917189714Syongari	/*
1918189714Syongari	 * Quite contrary to Typhoon T2 software functional specification,
1919189714Syongari	 * it seems that TXP_CMD_RECV_BUFFER_CONTROL command is not
1920189714Syongari	 * implemented in the firmware. This means driver should have to
1921189714Syongari	 * handle misaligned frames on alignment architectures. AFAIK this
1922189714Syongari	 * is the only controller manufactured by 3Com that has this stupid
1923189714Syongari	 * bug. 3Com should fix this.
1924189714Syongari	 */
1925189714Syongari	if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
1926189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0)
1927189714Syongari		goto init_fail;
1928189714Syongari	/* Undocumented command(interrupt coalescing disable?) - From Linux. */
1929189714Syongari	if (txp_command(sc, TXP_CMD_FILTER_DEFINE, 0, 0, 0, NULL, NULL, NULL,
1930189714Syongari	    TXP_CMD_NOWAIT) != 0)
1931189714Syongari		goto init_fail;
193280219Swpaul
193380219Swpaul	/* Set station address. */
1934189714Syongari	eaddr = IF_LLADDR(sc->sc_ifp);
1935189714Syongari	p1 = 0;
1936189714Syongari	((uint8_t *)&p1)[1] = eaddr[0];
1937189714Syongari	((uint8_t *)&p1)[0] = eaddr[1];
1938189714Syongari	p1 = le16toh(p1);
1939189714Syongari	((uint8_t *)&p2)[3] = eaddr[2];
1940189714Syongari	((uint8_t *)&p2)[2] = eaddr[3];
1941189714Syongari	((uint8_t *)&p2)[1] = eaddr[4];
1942189714Syongari	((uint8_t *)&p2)[0] = eaddr[5];
1943189714Syongari	p2 = le32toh(p2);
1944189714Syongari	if (txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0,
1945189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0)
1946189714Syongari		goto init_fail;
194780219Swpaul
194880219Swpaul	txp_set_filter(sc);
1949189714Syongari	txp_set_capabilities(sc);
195080219Swpaul
1951189714Syongari	if (txp_command(sc, TXP_CMD_CLEAR_STATISTICS, 0, 0, 0,
1952189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT))
1953189714Syongari		goto init_fail;
1954189714Syongari	if (txp_command(sc, TXP_CMD_XCVR_SELECT, sc->sc_xcvr, 0, 0,
1955189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0)
1956189714Syongari		goto init_fail;
1957189714Syongari	if (txp_command(sc, TXP_CMD_TX_ENABLE, 0, 0, 0, NULL, NULL, NULL,
1958189714Syongari	    TXP_CMD_NOWAIT) != 0)
1959189714Syongari		goto init_fail;
1960189714Syongari	if (txp_command(sc, TXP_CMD_RX_ENABLE, 0, 0, 0, NULL, NULL, NULL,
1961189714Syongari	    TXP_CMD_NOWAIT) != 0)
1962189714Syongari		goto init_fail;
196380219Swpaul
1964189714Syongari	/* Ack all pending interrupts and enable interrupts. */
1965189714Syongari	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
1966189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTRS);
1967189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_NONE);
196880219Swpaul
1969148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
1970148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
197180219Swpaul
1972151772Sjhb	callout_reset(&sc->sc_tick, hz, txp_tick, sc);
1973189714Syongari	return;
1974189714Syongari
1975189714Syongariinit_fail:
1976189714Syongari	txp_rxring_empty(sc);
1977189714Syongari	txp_init_rings(sc);
1978189714Syongari	txp_reset(sc);
1979189714Syongari	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
198080219Swpaul}
198180219Swpaul
198280219Swpaulstatic void
1983189685Syongaritxp_tick(void *vsc)
198480219Swpaul{
1985189714Syongari	struct txp_softc *sc;
1986189714Syongari	struct ifnet *ifp;
1987189714Syongari	struct txp_rsp_desc *rsp;
198880219Swpaul	struct txp_ext_desc *ext;
1989189714Syongari	int link;
199080219Swpaul
1991189714Syongari	sc = vsc;
1992151772Sjhb	TXP_LOCK_ASSERT(sc);
1993189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1994189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1995189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
199680219Swpaul	txp_rxbuf_reclaim(sc);
1997189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
1998189714Syongari	    sc->sc_cdata.txp_hostvar_map,
1999189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
200080219Swpaul
2001189714Syongari	ifp = sc->sc_ifp;
2002189714Syongari	rsp = NULL;
2003189714Syongari
2004189714Syongari	link = sc->sc_flags & TXP_FLAG_LINK;
2005189714Syongari	if (txp_ext_command(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0,
2006189714Syongari	    &rsp, TXP_CMD_WAIT))
200780219Swpaul		goto out;
200880219Swpaul	if (rsp->rsp_numdesc != 6)
200980219Swpaul		goto out;
2010189714Syongari	txp_stats_update(sc, rsp);
2011189714Syongari	if (link == 0 && (sc->sc_flags & TXP_FLAG_LINK) != 0) {
2012189714Syongari		ext = (struct txp_ext_desc *)(rsp + 1);
2013189714Syongari		/* Update baudrate with resolved speed. */
2014189714Syongari		if ((ext[5].ext_2 & 0x02) != 0)
2015189714Syongari			ifp->if_baudrate = IF_Mbps(100);
2016189714Syongari		else
2017189714Syongari			ifp->if_baudrate = IF_Mbps(10);
2018189714Syongari	}
201980219Swpaul
202080219Swpaulout:
202180219Swpaul	if (rsp != NULL)
202280219Swpaul		free(rsp, M_DEVBUF);
2023189714Syongari	txp_watchdog(sc);
2024151772Sjhb	callout_reset(&sc->sc_tick, hz, txp_tick, sc);
202580219Swpaul}
202680219Swpaul
202780219Swpaulstatic void
2028189685Syongaritxp_start(struct ifnet *ifp)
202980219Swpaul{
2030151772Sjhb	struct txp_softc *sc;
2031151772Sjhb
2032151772Sjhb	sc = ifp->if_softc;
2033151772Sjhb	TXP_LOCK(sc);
2034151772Sjhb	txp_start_locked(ifp);
2035151772Sjhb	TXP_UNLOCK(sc);
2036151772Sjhb}
2037151772Sjhb
2038151772Sjhbstatic void
2039189685Syongaritxp_start_locked(struct ifnet *ifp)
2040151772Sjhb{
2041189714Syongari	struct txp_softc *sc;
2042189714Syongari	struct mbuf *m_head;
2043189714Syongari	int enq;
204480219Swpaul
2045189714Syongari	sc = ifp->if_softc;
2046151772Sjhb	TXP_LOCK_ASSERT(sc);
2047189714Syongari
2048148887Srwatson	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
2049189714Syongari	   IFF_DRV_RUNNING || (sc->sc_flags & TXP_FLAG_LINK) == 0)
205080219Swpaul		return;
205180219Swpaul
2052189714Syongari	for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) {
2053189714Syongari		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
2054189714Syongari		if (m_head == NULL)
205580219Swpaul			break;
2056189714Syongari		/*
2057189714Syongari		 * Pack the data into the transmit ring. If we
2058189714Syongari		 * don't have room, set the OACTIVE flag and wait
2059189714Syongari		 * for the NIC to drain the ring.
2060189714Syongari		 * ATM only Hi-ring is used.
2061189714Syongari		 */
2062189714Syongari		if (txp_encap(sc, &sc->sc_txhir, &m_head)) {
2063189714Syongari			if (m_head == NULL)
2064189714Syongari				break;
2065189714Syongari			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
2066189714Syongari			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2067189714Syongari			break;
2068189714Syongari		}
206980219Swpaul
2070189714Syongari		/*
2071189714Syongari		 * If there's a BPF listener, bounce a copy of this frame
2072189714Syongari		 * to him.
2073189714Syongari		 */
2074189714Syongari		ETHER_BPF_MTAP(ifp, m_head);
207580219Swpaul
2076189714Syongari		/* Send queued frame. */
2077189714Syongari		WRITE_REG(sc, sc->sc_txhir.r_reg,
2078189714Syongari		    TXP_IDX2OFFSET(sc->sc_txhir.r_prod));
2079189714Syongari	}
208080219Swpaul
2081189714Syongari	if (enq > 0) {
2082189714Syongari		/* Set a timeout in case the chip goes out to lunch. */
2083189714Syongari		sc->sc_watchdog_timer = TXP_TX_TIMEOUT;
2084189714Syongari	}
2085189714Syongari}
208680219Swpaul
2087189714Syongaristatic int
2088189714Syongaritxp_encap(struct txp_softc *sc, struct txp_tx_ring *r, struct mbuf **m_head)
2089189714Syongari{
2090189714Syongari	struct txp_tx_desc *first_txd;
2091189714Syongari	struct txp_frag_desc *fxd;
2092189714Syongari	struct txp_swdesc *sd;
2093189714Syongari	struct mbuf *m;
2094189714Syongari	bus_dma_segment_t txsegs[TXP_MAXTXSEGS];
2095189714Syongari	int error, i, nsegs;
209680219Swpaul
2097189714Syongari	TXP_LOCK_ASSERT(sc);
209880219Swpaul
2099189714Syongari	M_ASSERTPKTHDR((*m_head));
210080219Swpaul
2101189714Syongari	m = *m_head;
2102189714Syongari	first_txd = r->r_desc + r->r_prod;
2103189714Syongari	sd = sc->sc_txd + r->r_prod;
210480219Swpaul
2105189714Syongari	error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_tx_tag, sd->sd_map,
2106189714Syongari	    *m_head, txsegs, &nsegs, 0);
2107189714Syongari	if (error == EFBIG) {
2108189714Syongari		m = m_collapse(*m_head, M_DONTWAIT, TXP_MAXTXSEGS);
2109189714Syongari		if (m == NULL) {
2110189714Syongari			m_freem(*m_head);
2111189714Syongari			*m_head = NULL;
2112189714Syongari			return (ENOMEM);
211380219Swpaul		}
2114189714Syongari		*m_head = m;
2115189714Syongari		error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_tx_tag,
2116189714Syongari		    sd->sd_map, *m_head, txsegs, &nsegs, 0);
2117189714Syongari		if (error != 0) {
2118189714Syongari			m_freem(*m_head);
2119189714Syongari			*m_head = NULL;
2120189714Syongari			return (error);
2121189714Syongari		}
2122189714Syongari	} else if (error != 0)
2123189714Syongari		return (error);
2124189714Syongari	if (nsegs == 0) {
2125189714Syongari		m_freem(*m_head);
2126189714Syongari		*m_head = NULL;
2127189714Syongari		return (EIO);
2128189714Syongari	}
212983115Sbrooks
2130189714Syongari	/* Check descriptor overrun. */
2131189714Syongari	if (r->r_cnt + nsegs >= TX_ENTRIES - TXP_TXD_RESERVED) {
2132189714Syongari		bus_dmamap_unload(sc->sc_cdata.txp_tx_tag, sd->sd_map);
2133189714Syongari		return (ENOBUFS);
2134189714Syongari	}
2135189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_tx_tag, sd->sd_map,
2136189714Syongari	    BUS_DMASYNC_PREWRITE);
2137189714Syongari	sd->sd_mbuf = m;
213880219Swpaul
2139189714Syongari	first_txd->tx_flags = TX_FLAGS_TYPE_DATA;
2140189714Syongari	first_txd->tx_numdesc = 0;
2141189714Syongari	first_txd->tx_addrlo = 0;
2142189714Syongari	first_txd->tx_addrhi = 0;
2143189714Syongari	first_txd->tx_totlen = 0;
2144189714Syongari	first_txd->tx_pflags = 0;
2145189714Syongari	r->r_cnt++;
2146189714Syongari	TXP_DESC_INC(r->r_prod, TX_ENTRIES);
2147189714Syongari
2148189714Syongari	/* Configure Tx IP/TCP/UDP checksum offload. */
2149189714Syongari	if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0)
2150189714Syongari		first_txd->tx_pflags |= htole32(TX_PFLAGS_IPCKSUM);
2151189714Syongari#ifdef notyet
2152189714Syongari	/* XXX firmware bug. */
2153189714Syongari	if ((m->m_pkthdr.csum_flags & CSUM_TCP) != 0)
2154189714Syongari		first_txd->tx_pflags |= htole32(TX_PFLAGS_TCPCKSUM);
2155189714Syongari	if ((m->m_pkthdr.csum_flags & CSUM_UDP) != 0)
2156189714Syongari		first_txd->tx_pflags |= htole32(TX_PFLAGS_UDPCKSUM);
215780219Swpaul#endif
215880219Swpaul
2159189714Syongari	/* Configure VLAN hardware tag insertion. */
2160189714Syongari	if ((m->m_flags & M_VLANTAG) != 0)
2161189714Syongari		first_txd->tx_pflags |=
2162189714Syongari		    htole32(TX_PFLAGS_VLAN | TX_PFLAGS_PRIO |
2163189714Syongari		    (bswap16(m->m_pkthdr.ether_vtag) << TX_PFLAGS_VLANTAG_S));
216480219Swpaul
2165189714Syongari	for (i = 0; i < nsegs; i++) {
2166189714Syongari		fxd = (struct txp_frag_desc *)(r->r_desc + r->r_prod);
2167189714Syongari		fxd->frag_flags = FRAG_FLAGS_TYPE_FRAG | TX_FLAGS_VALID;
2168189714Syongari		fxd->frag_rsvd1 = 0;
2169189714Syongari		fxd->frag_len = htole16(txsegs[i].ds_len);
2170189714Syongari		fxd->frag_addrhi = htole32(TXP_ADDR_HI(txsegs[i].ds_addr));
2171189714Syongari		fxd->frag_addrlo = htole32(TXP_ADDR_LO(txsegs[i].ds_addr));
2172189714Syongari		fxd->frag_rsvd2 = 0;
2173189714Syongari		first_txd->tx_numdesc++;
2174189714Syongari		r->r_cnt++;
2175189714Syongari		TXP_DESC_INC(r->r_prod, TX_ENTRIES);
2176189714Syongari	}
217780219Swpaul
2178189714Syongari	/* Lastly set valid flag. */
2179189714Syongari	first_txd->tx_flags |= TX_FLAGS_VALID;
218080219Swpaul
2181189714Syongari	/* Sync descriptors. */
2182189714Syongari	bus_dmamap_sync(r->r_tag, r->r_map,
2183189714Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
218480219Swpaul
2185189714Syongari	return (0);
218680219Swpaul}
218780219Swpaul
218880219Swpaul/*
218980219Swpaul * Handle simple commands sent to the typhoon
219080219Swpaul */
219180219Swpaulstatic int
2192189689Syongaritxp_command(struct txp_softc *sc, uint16_t id, uint16_t in1, uint32_t in2,
2193189689Syongari    uint32_t in3, uint16_t *out1, uint32_t *out2, uint32_t *out3, int wait)
219480219Swpaul{
2195189714Syongari	struct txp_rsp_desc *rsp;
219680219Swpaul
2197189714Syongari	rsp = NULL;
2198189714Syongari	if (txp_ext_command(sc, id, in1, in2, in3, NULL, 0, &rsp, wait) != 0) {
2199189714Syongari		device_printf(sc->sc_dev, "command 0x%02x failed\n", id);
220080219Swpaul		return (-1);
2201189714Syongari	}
220280219Swpaul
2203189714Syongari	if (wait == TXP_CMD_NOWAIT)
220480219Swpaul		return (0);
220580219Swpaul
2206189714Syongari	KASSERT(rsp != NULL, ("rsp is NULL!\n"));
220780219Swpaul	if (out1 != NULL)
2208189714Syongari		*out1 = le16toh(rsp->rsp_par1);
220980219Swpaul	if (out2 != NULL)
2210189714Syongari		*out2 = le32toh(rsp->rsp_par2);
221180219Swpaul	if (out3 != NULL)
2212189714Syongari		*out3 = le32toh(rsp->rsp_par3);
221380219Swpaul	free(rsp, M_DEVBUF);
221480219Swpaul	return (0);
221580219Swpaul}
221680219Swpaul
221780219Swpaulstatic int
2218189714Syongaritxp_ext_command(struct txp_softc *sc, uint16_t id, uint16_t in1, uint32_t in2,
2219189689Syongari    uint32_t in3, struct txp_ext_desc *in_extp, uint8_t in_extn,
2220189004Srdivacky    struct txp_rsp_desc **rspp, int wait)
222180219Swpaul{
2222189714Syongari	struct txp_hostvar *hv;
222380219Swpaul	struct txp_cmd_desc *cmd;
222480219Swpaul	struct txp_ext_desc *ext;
2225189689Syongari	uint32_t idx, i;
2226189689Syongari	uint16_t seq;
2227189714Syongari	int error;
222880219Swpaul
2229189714Syongari	error = 0;
2230189714Syongari	hv = sc->sc_hostvar;
223180219Swpaul	if (txp_cmd_desc_numfree(sc) < (in_extn + 1)) {
2232189714Syongari		device_printf(sc->sc_dev,
2233189714Syongari		    "%s : out of free cmd descriptors for command 0x%02x\n",
2234189714Syongari		    __func__, id);
2235189714Syongari		return (ENOBUFS);
223680219Swpaul	}
223780219Swpaul
2238189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_cmdring_tag,
2239189714Syongari	    sc->sc_cdata.txp_cmdring_map, BUS_DMASYNC_POSTWRITE);
224080219Swpaul	idx = sc->sc_cmdring.lastwrite;
2241189689Syongari	cmd = (struct txp_cmd_desc *)(((uint8_t *)sc->sc_cmdring.base) + idx);
224280219Swpaul	bzero(cmd, sizeof(*cmd));
224380219Swpaul
224480219Swpaul	cmd->cmd_numdesc = in_extn;
2245189714Syongari	seq = sc->sc_seq++;
2246189714Syongari	cmd->cmd_seq = htole16(seq);
2247189714Syongari	cmd->cmd_id = htole16(id);
2248189714Syongari	cmd->cmd_par1 = htole16(in1);
2249189714Syongari	cmd->cmd_par2 = htole32(in2);
2250189714Syongari	cmd->cmd_par3 = htole32(in3);
225180219Swpaul	cmd->cmd_flags = CMD_FLAGS_TYPE_CMD |
2252189714Syongari	    (wait == TXP_CMD_WAIT ? CMD_FLAGS_RESP : 0) | CMD_FLAGS_VALID;
225380219Swpaul
225480219Swpaul	idx += sizeof(struct txp_cmd_desc);
225580219Swpaul	if (idx == sc->sc_cmdring.size)
225680219Swpaul		idx = 0;
225780219Swpaul
225880219Swpaul	for (i = 0; i < in_extn; i++) {
2259189689Syongari		ext = (struct txp_ext_desc *)(((uint8_t *)sc->sc_cmdring.base) + idx);
226080219Swpaul		bcopy(in_extp, ext, sizeof(struct txp_ext_desc));
226180219Swpaul		in_extp++;
226280219Swpaul		idx += sizeof(struct txp_cmd_desc);
226380219Swpaul		if (idx == sc->sc_cmdring.size)
226480219Swpaul			idx = 0;
226580219Swpaul	}
226680219Swpaul
226780219Swpaul	sc->sc_cmdring.lastwrite = idx;
2268189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_cmdring_tag,
2269189714Syongari	    sc->sc_cdata.txp_cmdring_map, BUS_DMASYNC_PREWRITE);
2270189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2271189714Syongari	    sc->sc_cdata.txp_hostvar_map, BUS_DMASYNC_PREREAD |
2272189714Syongari	    BUS_DMASYNC_PREWRITE);
227380219Swpaul	WRITE_REG(sc, TXP_H2A_2, sc->sc_cmdring.lastwrite);
2274189714Syongari	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
227580219Swpaul
2276189714Syongari	if (wait == TXP_CMD_NOWAIT)
227780219Swpaul		return (0);
227880219Swpaul
2279189714Syongari	for (i = 0; i < TXP_TIMEOUT; i++) {
2280189714Syongari		bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2281189714Syongari		    sc->sc_cdata.txp_hostvar_map, BUS_DMASYNC_POSTREAD |
2282189714Syongari		    BUS_DMASYNC_POSTWRITE);
2283189714Syongari		if (le32toh(hv->hv_resp_read_idx) !=
2284189714Syongari		    le32toh(hv->hv_resp_write_idx)) {
2285189714Syongari			error = txp_response(sc, id, seq, rspp);
2286189714Syongari			bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2287189714Syongari			    sc->sc_cdata.txp_hostvar_map,
2288189714Syongari			    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
2289189714Syongari			if (error != 0)
2290189714Syongari				return (error);
2291189714Syongari 			if (*rspp != NULL)
229280219Swpaul				break;
229380219Swpaul		}
229480219Swpaul		DELAY(50);
229580219Swpaul	}
2296189714Syongari	if (i == TXP_TIMEOUT) {
2297189714Syongari		device_printf(sc->sc_dev, "command 0x%02x timedout\n", id);
2298189714Syongari		error = ETIMEDOUT;
229980219Swpaul	}
230080219Swpaul
2301189714Syongari	return (error);
230280219Swpaul}
230380219Swpaul
230480219Swpaulstatic int
2305189714Syongaritxp_response(struct txp_softc *sc, uint16_t id, uint16_t seq,
2306189004Srdivacky    struct txp_rsp_desc **rspp)
230780219Swpaul{
2308189714Syongari	struct txp_hostvar *hv;
230980219Swpaul	struct txp_rsp_desc *rsp;
2310189714Syongari	uint32_t ridx;
231180219Swpaul
2312189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_rspring_tag,
2313189714Syongari	    sc->sc_cdata.txp_rspring_map, BUS_DMASYNC_POSTREAD);
2314189714Syongari	hv = sc->sc_hostvar;
2315189714Syongari	ridx = le32toh(hv->hv_resp_read_idx);
2316189714Syongari	while (ridx != le32toh(hv->hv_resp_write_idx)) {
2317189689Syongari		rsp = (struct txp_rsp_desc *)(((uint8_t *)sc->sc_rspring.base) + ridx);
231880219Swpaul
2319189714Syongari		if (id == le16toh(rsp->rsp_id) &&
2320189714Syongari		    le16toh(rsp->rsp_seq) == seq) {
232180219Swpaul			*rspp = (struct txp_rsp_desc *)malloc(
232280219Swpaul			    sizeof(struct txp_rsp_desc) * (rsp->rsp_numdesc + 1),
232380219Swpaul			    M_DEVBUF, M_NOWAIT);
2324189714Syongari			if (*rspp == NULL) {
2325189714Syongari				device_printf(sc->sc_dev,"%s : command 0x%02x "
2326189714Syongari				    "memory allocation failure\n",
2327189714Syongari				    __func__, id);
2328189714Syongari				return (ENOMEM);
2329189714Syongari			}
233080219Swpaul			txp_rsp_fixup(sc, rsp, *rspp);
233180219Swpaul			return (0);
233280219Swpaul		}
233380219Swpaul
2334189714Syongari		if ((rsp->rsp_flags & RSP_FLAGS_ERROR) != 0) {
2335189714Syongari			device_printf(sc->sc_dev,
2336189714Syongari			    "%s : command 0x%02x response error!\n", __func__,
2337189714Syongari			    le16toh(rsp->rsp_id));
233880219Swpaul			txp_rsp_fixup(sc, rsp, NULL);
2339189714Syongari			ridx = le32toh(hv->hv_resp_read_idx);
234080219Swpaul			continue;
234180219Swpaul		}
234280219Swpaul
2343189714Syongari		/*
2344189714Syongari		 * The following unsolicited responses are handled during
2345189714Syongari		 * processing of TXP_CMD_READ_STATISTICS which requires
2346189714Syongari		 * response. Driver abuses the command to detect media
2347189714Syongari		 * status change.
2348189714Syongari		 * TXP_CMD_FILTER_DEFINE is not an unsolicited response
2349189714Syongari		 * but we don't process response ring in interrupt handler
2350189714Syongari		 * so we have to ignore this command here, otherwise
2351189714Syongari		 * unknown command message would be printed.
2352189714Syongari		 */
2353189714Syongari		switch (le16toh(rsp->rsp_id)) {
235480219Swpaul		case TXP_CMD_CYCLE_STATISTICS:
2355189714Syongari		case TXP_CMD_FILTER_DEFINE:
2356189714Syongari			break;
235780219Swpaul		case TXP_CMD_MEDIA_STATUS_READ:
2358189714Syongari			if ((le16toh(rsp->rsp_par1) & 0x0800) == 0) {
2359189714Syongari				sc->sc_flags |= TXP_FLAG_LINK;
2360189714Syongari				if_link_state_change(sc->sc_ifp,
2361189714Syongari				    LINK_STATE_UP);
2362189714Syongari			} else {
2363189714Syongari				sc->sc_flags &= ~TXP_FLAG_LINK;
2364189714Syongari				if_link_state_change(sc->sc_ifp,
2365189714Syongari				    LINK_STATE_DOWN);
2366189714Syongari			}
236780219Swpaul			break;
236880219Swpaul		case TXP_CMD_HELLO_RESPONSE:
2369189714Syongari			/*
2370189714Syongari			 * Driver should repsond to hello message but
2371189714Syongari			 * TXP_CMD_READ_STATISTICS is issued for every
2372189714Syongari			 * hz, therefore there is no need to send an
2373189714Syongari			 * explicit command here.
2374189714Syongari			 */
2375189714Syongari			device_printf(sc->sc_dev, "%s : hello\n", __func__);
237680219Swpaul			break;
237780219Swpaul		default:
2378189714Syongari			device_printf(sc->sc_dev,
2379189714Syongari			    "%s : unknown command 0x%02x\n", __func__,
2380189714Syongari			    le16toh(rsp->rsp_id));
238180219Swpaul		}
238280219Swpaul		txp_rsp_fixup(sc, rsp, NULL);
2383189714Syongari		ridx = le32toh(hv->hv_resp_read_idx);
238480219Swpaul	}
238580219Swpaul
238680219Swpaul	return (0);
238780219Swpaul}
238880219Swpaul
238980219Swpaulstatic void
2390189685Syongaritxp_rsp_fixup(struct txp_softc *sc, struct txp_rsp_desc *rsp,
2391189685Syongari    struct txp_rsp_desc *dst)
239280219Swpaul{
2393189714Syongari	struct txp_rsp_desc *src;
2394189714Syongari	struct txp_hostvar *hv;
2395189689Syongari	uint32_t i, ridx;
239680219Swpaul
2397189714Syongari	src = rsp;
2398189714Syongari	hv = sc->sc_hostvar;
2399189714Syongari	ridx = le32toh(hv->hv_resp_read_idx);
240080219Swpaul
240180219Swpaul	for (i = 0; i < rsp->rsp_numdesc + 1; i++) {
240280219Swpaul		if (dst != NULL)
240380219Swpaul			bcopy(src, dst++, sizeof(struct txp_rsp_desc));
240480219Swpaul		ridx += sizeof(struct txp_rsp_desc);
240580219Swpaul		if (ridx == sc->sc_rspring.size) {
240680219Swpaul			src = sc->sc_rspring.base;
240780219Swpaul			ridx = 0;
240880219Swpaul		} else
240980219Swpaul			src++;
2410189714Syongari		sc->sc_rspring.lastwrite = ridx;
241180219Swpaul	}
2412189686Syongari
2413189714Syongari	hv->hv_resp_read_idx = htole32(ridx);
241480219Swpaul}
241580219Swpaul
241680219Swpaulstatic int
2417189685Syongaritxp_cmd_desc_numfree(struct txp_softc *sc)
241880219Swpaul{
2419189714Syongari	struct txp_hostvar *hv;
2420189714Syongari	struct txp_boot_record *br;
2421189689Syongari	uint32_t widx, ridx, nfree;
242280219Swpaul
2423189714Syongari	bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag,
2424189714Syongari	    sc->sc_cdata.txp_hostvar_map,
2425189714Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2426189714Syongari	hv = sc->sc_hostvar;
2427189714Syongari	br = sc->sc_boot;
242880219Swpaul	widx = sc->sc_cmdring.lastwrite;
2429189714Syongari	ridx = le32toh(hv->hv_cmd_read_idx);
243080219Swpaul
243180219Swpaul	if (widx == ridx) {
243280219Swpaul		/* Ring is completely free */
2433189714Syongari		nfree = le32toh(br->br_cmd_siz) - sizeof(struct txp_cmd_desc);
243480219Swpaul	} else {
243580219Swpaul		if (widx > ridx)
2436189714Syongari			nfree = le32toh(br->br_cmd_siz) -
243780219Swpaul			    (widx - ridx + sizeof(struct txp_cmd_desc));
243880219Swpaul		else
243980219Swpaul			nfree = ridx - widx - sizeof(struct txp_cmd_desc);
244080219Swpaul	}
244180219Swpaul
244280219Swpaul	return (nfree / sizeof(struct txp_cmd_desc));
244380219Swpaul}
244480219Swpaul
2445189714Syongaristatic int
2446189714Syongaritxp_sleep(struct txp_softc *sc, int capenable)
2447189714Syongari{
2448189714Syongari	uint16_t events;
2449189714Syongari	int error;
2450189714Syongari
2451189714Syongari	events = 0;
2452189714Syongari	if ((capenable & IFCAP_WOL_MAGIC) != 0)
2453189714Syongari		events |= 0x01;
2454189714Syongari	error = txp_command(sc, TXP_CMD_ENABLE_WAKEUP_EVENTS, events, 0, 0,
2455189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT);
2456189714Syongari	if (error == 0) {
2457189714Syongari		/* Goto sleep. */
2458189714Syongari		error = txp_command(sc, TXP_CMD_GOTO_SLEEP, 0, 0, 0, NULL,
2459189714Syongari		    NULL, NULL, TXP_CMD_NOWAIT);
2460189714Syongari		if (error == 0) {
2461189714Syongari			error = txp_wait(sc, STAT_SLEEPING);
2462189714Syongari			if (error != 0)
2463189714Syongari				device_printf(sc->sc_dev,
2464189714Syongari				    "unable to enter into sleep\n");
2465189714Syongari		}
2466189714Syongari	}
2467189714Syongari
2468189714Syongari	return (error);
2469189714Syongari}
2470189714Syongari
247180219Swpaulstatic void
2472189685Syongaritxp_stop(struct txp_softc *sc)
247380219Swpaul{
247480219Swpaul	struct ifnet *ifp;
247580219Swpaul
2476151772Sjhb	TXP_LOCK_ASSERT(sc);
2477147256Sbrooks	ifp = sc->sc_ifp;
247880219Swpaul
2479189714Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
2480189714Syongari		return;
2481189714Syongari
2482189714Syongari	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
2483189714Syongari	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
2484189714Syongari
2485148887Srwatson	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
2486189714Syongari	sc->sc_flags &= ~TXP_FLAG_LINK;
248780219Swpaul
2488151772Sjhb	callout_stop(&sc->sc_tick);
248980219Swpaul
2490189714Syongari	txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL,
2491189714Syongari	    TXP_CMD_NOWAIT);
2492189714Syongari	txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL,
2493189714Syongari	    TXP_CMD_NOWAIT);
2494189714Syongari	/* Save statistics for later use. */
2495189714Syongari	txp_stats_save(sc);
2496189714Syongari	/* Halt controller. */
2497189714Syongari	txp_command(sc, TXP_CMD_HALT, 0, 0, 0, NULL, NULL, NULL,
2498189714Syongari	    TXP_CMD_NOWAIT);
249980219Swpaul
2500189714Syongari	if (txp_wait(sc, STAT_HALTED) != 0)
2501189714Syongari		device_printf(sc->sc_dev, "controller halt timedout!\n");
2502189714Syongari	/* Reclaim Tx/Rx buffers. */
2503189714Syongari	if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons !=
2504189714Syongari	    TXP_OFFSET2IDX(le32toh(*(sc->sc_txhir.r_off)))))
2505189714Syongari		txp_tx_reclaim(sc, &sc->sc_txhir);
2506189714Syongari	if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons !=
2507189714Syongari	    TXP_OFFSET2IDX(le32toh(*(sc->sc_txlor.r_off)))))
2508189714Syongari		txp_tx_reclaim(sc, &sc->sc_txlor);
250980219Swpaul	txp_rxring_empty(sc);
2510189714Syongari
2511189714Syongari	txp_init_rings(sc);
2512189714Syongari	/* Reset controller and make it reload sleep image. */
2513189714Syongari	txp_reset(sc);
2514189714Syongari	/* Let controller boot from sleep image. */
2515189714Syongari	if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0)
2516189714Syongari		device_printf(sc->sc_dev, "could not boot sleep image\n");
2517189714Syongari	txp_sleep(sc, 0);
251880219Swpaul}
251980219Swpaul
252080219Swpaulstatic void
2521189714Syongaritxp_watchdog(struct txp_softc *sc)
252280219Swpaul{
2523189714Syongari	struct ifnet *ifp;
2524189687Syongari
2525189714Syongari	TXP_LOCK_ASSERT(sc);
2526189714Syongari
2527189714Syongari	if (sc->sc_watchdog_timer == 0 || --sc->sc_watchdog_timer)
2528189714Syongari		return;
2529189714Syongari
2530189714Syongari	ifp = sc->sc_ifp;
2531189714Syongari	if_printf(ifp, "watchdog timeout -- resetting\n");
2532189714Syongari	ifp->if_oerrors++;
2533189714Syongari	txp_stop(sc);
2534189714Syongari	txp_init_locked(sc);
253580219Swpaul}
253680219Swpaul
253780219Swpaulstatic int
2538189685Syongaritxp_ifmedia_upd(struct ifnet *ifp)
253980219Swpaul{
254080219Swpaul	struct txp_softc *sc = ifp->if_softc;
254180219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
2542189689Syongari	uint16_t new_xcvr;
254380219Swpaul
2544151772Sjhb	TXP_LOCK(sc);
2545151772Sjhb	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) {
2546151772Sjhb		TXP_UNLOCK(sc);
254780219Swpaul		return (EINVAL);
2548151772Sjhb	}
254980219Swpaul
255080219Swpaul	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) {
255180219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
255280219Swpaul			new_xcvr = TXP_XCVR_10_FDX;
255380219Swpaul		else
255480219Swpaul			new_xcvr = TXP_XCVR_10_HDX;
255580219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) {
255680219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
255780219Swpaul			new_xcvr = TXP_XCVR_100_FDX;
255880219Swpaul		else
255980219Swpaul			new_xcvr = TXP_XCVR_100_HDX;
256080219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) {
256180219Swpaul		new_xcvr = TXP_XCVR_AUTO;
2562151772Sjhb	} else {
2563151772Sjhb		TXP_UNLOCK(sc);
256480219Swpaul		return (EINVAL);
2565151772Sjhb	}
256680219Swpaul
256780219Swpaul	/* nothing to do */
2568151772Sjhb	if (sc->sc_xcvr == new_xcvr) {
2569151772Sjhb		TXP_UNLOCK(sc);
257080219Swpaul		return (0);
2571151772Sjhb	}
257280219Swpaul
257380219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, new_xcvr, 0, 0,
2574189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT);
257580219Swpaul	sc->sc_xcvr = new_xcvr;
2576151772Sjhb	TXP_UNLOCK(sc);
257780219Swpaul
257880219Swpaul	return (0);
257980219Swpaul}
258080219Swpaul
258180219Swpaulstatic void
2582189685Syongaritxp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
258380219Swpaul{
258480219Swpaul	struct txp_softc *sc = ifp->if_softc;
258580219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
2586189689Syongari	uint16_t bmsr, bmcr, anar, anlpar;
258780219Swpaul
258880219Swpaul	ifmr->ifm_status = IFM_AVALID;
258980219Swpaul	ifmr->ifm_active = IFM_ETHER;
259080219Swpaul
2591151772Sjhb	TXP_LOCK(sc);
2592189714Syongari	/* Check whether firmware is running. */
2593189714Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
2594189714Syongari		goto bail;
259580219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
2596189714Syongari	    &bmsr, NULL, NULL, TXP_CMD_WAIT))
259780219Swpaul		goto bail;
259880219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
2599189714Syongari	    &bmsr, NULL, NULL, TXP_CMD_WAIT))
260080219Swpaul		goto bail;
260180219Swpaul
260280219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMCR, 0,
2603189714Syongari	    &bmcr, NULL, NULL, TXP_CMD_WAIT))
260480219Swpaul		goto bail;
260580219Swpaul
260680219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANLPAR, 0,
2607189714Syongari	    &anlpar, NULL, NULL, TXP_CMD_WAIT))
260880219Swpaul		goto bail;
2609173666Syongari
2610173666Syongari	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANAR, 0,
2611189714Syongari	    &anar, NULL, NULL, TXP_CMD_WAIT))
2612173666Syongari		goto bail;
2613151772Sjhb	TXP_UNLOCK(sc);
261480219Swpaul
261580219Swpaul	if (bmsr & BMSR_LINK)
261680219Swpaul		ifmr->ifm_status |= IFM_ACTIVE;
261780219Swpaul
261880219Swpaul	if (bmcr & BMCR_ISO) {
261980219Swpaul		ifmr->ifm_active |= IFM_NONE;
262080219Swpaul		ifmr->ifm_status = 0;
262180219Swpaul		return;
262280219Swpaul	}
262380219Swpaul
262480219Swpaul	if (bmcr & BMCR_LOOP)
262580219Swpaul		ifmr->ifm_active |= IFM_LOOP;
262680219Swpaul
262780219Swpaul	if (bmcr & BMCR_AUTOEN) {
262880219Swpaul		if ((bmsr & BMSR_ACOMP) == 0) {
262980219Swpaul			ifmr->ifm_active |= IFM_NONE;
263080219Swpaul			return;
263180219Swpaul		}
263280219Swpaul
2633173666Syongari		anlpar &= anar;
2634173665Syongari		if (anlpar & ANLPAR_TX_FD)
2635173665Syongari			ifmr->ifm_active |= IFM_100_TX|IFM_FDX;
2636173665Syongari		else if (anlpar & ANLPAR_T4)
263780219Swpaul			ifmr->ifm_active |= IFM_100_T4;
263880219Swpaul		else if (anlpar & ANLPAR_TX)
263980219Swpaul			ifmr->ifm_active |= IFM_100_TX;
264080219Swpaul		else if (anlpar & ANLPAR_10_FD)
264180219Swpaul			ifmr->ifm_active |= IFM_10_T|IFM_FDX;
264280219Swpaul		else if (anlpar & ANLPAR_10)
264380219Swpaul			ifmr->ifm_active |= IFM_10_T;
264480219Swpaul		else
264580219Swpaul			ifmr->ifm_active |= IFM_NONE;
264680219Swpaul	} else
264780219Swpaul		ifmr->ifm_active = ifm->ifm_cur->ifm_media;
264880219Swpaul	return;
264980219Swpaul
265080219Swpaulbail:
2651151772Sjhb	TXP_UNLOCK(sc);
265280219Swpaul	ifmr->ifm_active |= IFM_NONE;
265380219Swpaul	ifmr->ifm_status &= ~IFM_AVALID;
265480219Swpaul}
265580219Swpaul
265680219Swpaul#ifdef TXP_DEBUG
265780219Swpaulstatic void
2658189685Syongaritxp_show_descriptor(void *d)
265980219Swpaul{
266080219Swpaul	struct txp_cmd_desc *cmd = d;
266180219Swpaul	struct txp_rsp_desc *rsp = d;
266280219Swpaul	struct txp_tx_desc *txd = d;
266380219Swpaul	struct txp_frag_desc *frgd = d;
266480219Swpaul
266580219Swpaul	switch (cmd->cmd_flags & CMD_FLAGS_TYPE_M) {
266680219Swpaul	case CMD_FLAGS_TYPE_CMD:
266780219Swpaul		/* command descriptor */
266880219Swpaul		printf("[cmd flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
2669189714Syongari		    cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id),
2670189714Syongari		    le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1),
2671189714Syongari		    le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3));
267280219Swpaul		break;
267380219Swpaul	case CMD_FLAGS_TYPE_RESP:
267480219Swpaul		/* response descriptor */
267580219Swpaul		printf("[rsp flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
2676189714Syongari		    rsp->rsp_flags, rsp->rsp_numdesc, le16toh(rsp->rsp_id),
2677189714Syongari		    le16toh(rsp->rsp_seq), le16toh(rsp->rsp_par1),
2678189714Syongari		    le32toh(rsp->rsp_par2), le32toh(rsp->rsp_par3));
267980219Swpaul		break;
268080219Swpaul	case CMD_FLAGS_TYPE_DATA:
268180219Swpaul		/* data header (assuming tx for now) */
268280219Swpaul		printf("[data flags 0x%x num %d totlen %d addr 0x%x/0x%x pflags 0x%x]",
2683189714Syongari		    txd->tx_flags, txd->tx_numdesc, le16toh(txd->tx_totlen),
2684189714Syongari		    le32toh(txd->tx_addrlo), le32toh(txd->tx_addrhi),
2685189714Syongari		    le32toh(txd->tx_pflags));
268680219Swpaul		break;
268780219Swpaul	case CMD_FLAGS_TYPE_FRAG:
268880219Swpaul		/* fragment descriptor */
268980219Swpaul		printf("[frag flags 0x%x rsvd1 0x%x len %d addr 0x%x/0x%x rsvd2 0x%x]",
2690189714Syongari		    frgd->frag_flags, frgd->frag_rsvd1, le16toh(frgd->frag_len),
2691189714Syongari		    le32toh(frgd->frag_addrlo), le32toh(frgd->frag_addrhi),
2692189714Syongari		    le32toh(frgd->frag_rsvd2));
269380219Swpaul		break;
269480219Swpaul	default:
269580219Swpaul		printf("[unknown(%x) flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
269680219Swpaul		    cmd->cmd_flags & CMD_FLAGS_TYPE_M,
2697189714Syongari		    cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id),
2698189714Syongari		    le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1),
2699189714Syongari		    le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3));
270080219Swpaul		break;
270180219Swpaul	}
270280219Swpaul}
270380219Swpaul#endif
270480219Swpaul
270580219Swpaulstatic void
2706189685Syongaritxp_set_filter(struct txp_softc *sc)
270780219Swpaul{
2708189690Syongari	struct ifnet *ifp;
2709189690Syongari	uint32_t crc, mchash[2];
2710189689Syongari	uint16_t filter;
271180219Swpaul	struct ifmultiaddr *ifma;
2712189690Syongari	int mcnt;
271380219Swpaul
2714189690Syongari	TXP_LOCK_ASSERT(sc);
271580219Swpaul
2716189690Syongari	ifp = sc->sc_ifp;
271780219Swpaul	filter = TXP_RXFILT_DIRECT;
2718189690Syongari	if ((ifp->if_flags & IFF_BROADCAST) != 0)
271980219Swpaul		filter |= TXP_RXFILT_BROADCAST;
2720189690Syongari	if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) {
2721189690Syongari		if ((ifp->if_flags & IFF_ALLMULTI) != 0)
2722189690Syongari			filter |= TXP_RXFILT_ALLMULTI;
2723189690Syongari		if ((ifp->if_flags & IFF_PROMISC) != 0)
2724189690Syongari			filter = TXP_RXFILT_PROMISC;
2725189690Syongari		goto setit;
2726189690Syongari	}
272780219Swpaul
2728189690Syongari	mchash[0] = mchash[1] = 0;
2729189690Syongari	mcnt = 0;
2730195049Srwatson	if_maddr_rlock(ifp);
2731189690Syongari	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
2732189690Syongari		if (ifma->ifma_addr->sa_family != AF_LINK)
2733189690Syongari			continue;
2734189690Syongari		crc = ether_crc32_be(LLADDR((struct sockaddr_dl *)
2735189690Syongari		    ifma->ifma_addr), ETHER_ADDR_LEN);
2736189690Syongari		crc &= 0x3f;
2737189690Syongari		mchash[crc >> 5] |= 1 << (crc & 0x1f);
2738189690Syongari		mcnt++;
2739189690Syongari	}
2740195049Srwatson	if_maddr_runlock(ifp);
274180219Swpaul
2742189690Syongari	if (mcnt > 0) {
2743189690Syongari		filter |= TXP_RXFILT_HASHMULTI;
2744189690Syongari		txp_command(sc, TXP_CMD_MCAST_HASH_MASK_WRITE, 2, mchash[0],
2745189714Syongari		    mchash[1], NULL, NULL, NULL, TXP_CMD_NOWAIT);
274680219Swpaul	}
274780219Swpaul
274880219Swpaulsetit:
274980219Swpaul	txp_command(sc, TXP_CMD_RX_FILTER_WRITE, filter, 0, 0,
2750189714Syongari	    NULL, NULL, NULL, TXP_CMD_NOWAIT);
275180219Swpaul}
275280219Swpaul
2753189714Syongaristatic int
2754189714Syongaritxp_set_capabilities(struct txp_softc *sc)
2755189714Syongari{
2756189714Syongari	struct ifnet *ifp;
2757189714Syongari	uint32_t rxcap, txcap;
2758189714Syongari
2759189714Syongari	TXP_LOCK_ASSERT(sc);
2760189714Syongari
2761189714Syongari	rxcap = txcap = 0;
2762189714Syongari	ifp = sc->sc_ifp;
2763189714Syongari	if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) {
2764189714Syongari		if ((ifp->if_hwassist & CSUM_IP) != 0)
2765189714Syongari			txcap |= OFFLOAD_IPCKSUM;
2766189714Syongari		if ((ifp->if_hwassist & CSUM_TCP) != 0)
2767189714Syongari			txcap |= OFFLOAD_TCPCKSUM;
2768189714Syongari		if ((ifp->if_hwassist & CSUM_UDP) != 0)
2769189714Syongari			txcap |= OFFLOAD_UDPCKSUM;
2770189714Syongari		rxcap = txcap;
2771189714Syongari	}
2772189714Syongari	if ((ifp->if_capenable & IFCAP_RXCSUM) == 0)
2773189714Syongari		rxcap &= ~(OFFLOAD_IPCKSUM | OFFLOAD_TCPCKSUM |
2774189714Syongari		    OFFLOAD_UDPCKSUM);
2775189714Syongari	if ((ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) {
2776189714Syongari		rxcap |= OFFLOAD_VLAN;
2777189714Syongari		txcap |= OFFLOAD_VLAN;
2778189714Syongari	}
2779189714Syongari
2780189714Syongari	/* Tell firmware new offload configuration. */
2781189714Syongari	return (txp_command(sc, TXP_CMD_OFFLOAD_WRITE, 0, txcap, rxcap, NULL,
2782189714Syongari	    NULL, NULL, TXP_CMD_NOWAIT));
2783189714Syongari}
2784189714Syongari
278580219Swpaulstatic void
2786189714Syongaritxp_stats_save(struct txp_softc *sc)
278780219Swpaul{
2788189714Syongari	struct txp_rsp_desc *rsp;
278980219Swpaul
2790189714Syongari	TXP_LOCK_ASSERT(sc);
2791189714Syongari
2792189714Syongari	rsp = NULL;
2793189714Syongari	if (txp_ext_command(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0,
2794189714Syongari	    &rsp, TXP_CMD_WAIT))
279580219Swpaul		goto out;
2796189714Syongari	if (rsp->rsp_numdesc != 6)
2797189714Syongari		goto out;
2798189714Syongari	txp_stats_update(sc, rsp);
2799189714Syongariout:
2800189714Syongari	if (rsp != NULL)
2801189714Syongari		free(rsp, M_DEVBUF);
2802189714Syongari	bcopy(&sc->sc_stats, &sc->sc_ostats, sizeof(struct txp_hw_stats));
2803189714Syongari}
280480219Swpaul
2805189714Syongaristatic void
2806189714Syongaritxp_stats_update(struct txp_softc *sc, struct txp_rsp_desc *rsp)
2807189714Syongari{
2808189714Syongari	struct ifnet *ifp;
2809189714Syongari	struct txp_hw_stats *ostats, *stats;
2810189714Syongari	struct txp_ext_desc *ext;
2811189714Syongari
2812189714Syongari	TXP_LOCK_ASSERT(sc);
2813189714Syongari
2814189714Syongari	ifp = sc->sc_ifp;
281580219Swpaul	ext = (struct txp_ext_desc *)(rsp + 1);
2816189714Syongari	ostats = &sc->sc_ostats;
2817189714Syongari	stats = &sc->sc_stats;
2818189714Syongari	stats->tx_frames = ostats->tx_frames + le32toh(rsp->rsp_par2);
2819189714Syongari	stats->tx_bytes = ostats->tx_bytes + (uint64_t)le32toh(rsp->rsp_par3) +
2820189714Syongari	    ((uint64_t)le32toh(ext[0].ext_1) << 32);
2821189714Syongari	stats->tx_deferred = ostats->tx_deferred + le32toh(ext[0].ext_2);
2822189714Syongari	stats->tx_late_colls = ostats->tx_late_colls + le32toh(ext[0].ext_3);
2823189714Syongari	stats->tx_colls = ostats->tx_colls + le32toh(ext[0].ext_4);
2824189714Syongari	stats->tx_carrier_lost = ostats->tx_carrier_lost +
2825189714Syongari	    le32toh(ext[1].ext_1);
2826189714Syongari	stats->tx_multi_colls = ostats->tx_multi_colls +
2827189714Syongari	    le32toh(ext[1].ext_2);
2828189714Syongari	stats->tx_excess_colls = ostats->tx_excess_colls +
2829189714Syongari	    le32toh(ext[1].ext_3);
2830189714Syongari	stats->tx_fifo_underruns = ostats->tx_fifo_underruns +
2831189714Syongari	    le32toh(ext[1].ext_4);
2832189714Syongari	stats->tx_mcast_oflows = ostats->tx_mcast_oflows +
2833189714Syongari	    le32toh(ext[2].ext_1);
2834189714Syongari	stats->tx_filtered = ostats->tx_filtered + le32toh(ext[2].ext_2);
2835189714Syongari	stats->rx_frames = ostats->rx_frames + le32toh(ext[2].ext_3);
2836189714Syongari	stats->rx_bytes = ostats->rx_bytes + (uint64_t)le32toh(ext[2].ext_4) +
2837189714Syongari	    ((uint64_t)le32toh(ext[3].ext_1) << 32);
2838189714Syongari	stats->rx_fifo_oflows = ostats->rx_fifo_oflows + le32toh(ext[3].ext_2);
2839189714Syongari	stats->rx_badssd = ostats->rx_badssd + le32toh(ext[3].ext_3);
2840189714Syongari	stats->rx_crcerrs = ostats->rx_crcerrs + le32toh(ext[3].ext_4);
2841189714Syongari	stats->rx_lenerrs = ostats->rx_lenerrs + le32toh(ext[4].ext_1);
2842189714Syongari	stats->rx_bcast_frames = ostats->rx_bcast_frames +
2843189714Syongari	    le32toh(ext[4].ext_2);
2844189714Syongari	stats->rx_mcast_frames = ostats->rx_mcast_frames +
2845189714Syongari	    le32toh(ext[4].ext_3);
2846189714Syongari	stats->rx_oflows = ostats->rx_oflows + le32toh(ext[4].ext_4);
2847189714Syongari	stats->rx_filtered = ostats->rx_filtered + le32toh(ext[5].ext_1);
284880219Swpaul
2849189714Syongari	ifp->if_ierrors = stats->rx_fifo_oflows + stats->rx_badssd +
2850189714Syongari	    stats->rx_crcerrs + stats->rx_lenerrs + stats->rx_oflows;
2851189714Syongari	ifp->if_oerrors = stats->tx_deferred + stats->tx_carrier_lost +
2852189714Syongari	    stats->tx_fifo_underruns + stats->tx_mcast_oflows;
2853189714Syongari	ifp->if_collisions = stats->tx_late_colls + stats->tx_multi_colls +
2854189714Syongari	    stats->tx_excess_colls;
2855189714Syongari	ifp->if_opackets = stats->tx_frames;
2856189714Syongari	ifp->if_ipackets = stats->rx_frames;
2857189714Syongari}
285880219Swpaul
2859189714Syongari#define	TXP_SYSCTL_STAT_ADD32(c, h, n, p, d)	\
2860189714Syongari	    SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d)
286180219Swpaul
2862217323Smdf#if __FreeBSD_version >= 900030
2863189714Syongari#define	TXP_SYSCTL_STAT_ADD64(c, h, n, p, d)	\
2864217323Smdf	    SYSCTL_ADD_UQUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d)
2865217323Smdf#elif __FreeBSD_version > 800000
2866217323Smdf#define	TXP_SYSCTL_STAT_ADD64(c, h, n, p, d)	\
2867189714Syongari	    SYSCTL_ADD_QUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d)
2868189714Syongari#else
2869189714Syongari#define	TXP_SYSCTL_STAT_ADD64(c, h, n, p, d)	\
2870189714Syongari	    SYSCTL_ADD_ULONG(c, h, OID_AUTO, n, CTLFLAG_RD, p, d)
287180219Swpaul#endif
287280219Swpaul
2873189714Syongaristatic void
2874189714Syongaritxp_sysctl_node(struct txp_softc *sc)
2875189714Syongari{
2876189714Syongari	struct sysctl_ctx_list *ctx;
2877189714Syongari	struct sysctl_oid_list *child, *parent;
2878189714Syongari	struct sysctl_oid *tree;
2879189714Syongari	struct txp_hw_stats *stats;
2880189714Syongari	int error;
288180219Swpaul
2882189714Syongari	stats = &sc->sc_stats;
2883189714Syongari	ctx = device_get_sysctl_ctx(sc->sc_dev);
2884189714Syongari	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
2885189714Syongari	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "process_limit",
2886189714Syongari	    CTLTYPE_INT | CTLFLAG_RW, &sc->sc_process_limit, 0,
2887189714Syongari	    sysctl_hw_txp_proc_limit, "I",
2888189714Syongari	    "max number of Rx events to process");
2889189714Syongari	/* Pull in device tunables. */
2890189714Syongari	sc->sc_process_limit = TXP_PROC_DEFAULT;
2891189714Syongari	error = resource_int_value(device_get_name(sc->sc_dev),
2892189714Syongari	    device_get_unit(sc->sc_dev), "process_limit",
2893189714Syongari	    &sc->sc_process_limit);
2894189714Syongari	if (error == 0) {
2895189714Syongari		if (sc->sc_process_limit < TXP_PROC_MIN ||
2896189714Syongari		    sc->sc_process_limit > TXP_PROC_MAX) {
2897189714Syongari			device_printf(sc->sc_dev,
2898189714Syongari			    "process_limit value out of range; "
2899189714Syongari			    "using default: %d\n", TXP_PROC_DEFAULT);
2900189714Syongari			sc->sc_process_limit = TXP_PROC_DEFAULT;
2901189714Syongari		}
290280219Swpaul	}
2903189714Syongari	tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
2904189714Syongari	    NULL, "TXP statistics");
2905189714Syongari	parent = SYSCTL_CHILDREN(tree);
290680219Swpaul
2907189714Syongari	/* Tx statistics. */
2908189714Syongari	tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD,
2909189714Syongari	    NULL, "Tx MAC statistics");
2910189714Syongari	child = SYSCTL_CHILDREN(tree);
291180219Swpaul
2912189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "frames",
2913189714Syongari	    &stats->tx_frames, "Frames");
2914189714Syongari	TXP_SYSCTL_STAT_ADD64(ctx, child, "octets",
2915189714Syongari	    &stats->tx_bytes, "Octets");
2916189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "deferred",
2917189714Syongari	    &stats->tx_deferred, "Deferred frames");
2918189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "late_colls",
2919189714Syongari	    &stats->tx_late_colls, "Late collisions");
2920189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "colls",
2921189714Syongari	    &stats->tx_colls, "Collisions");
2922189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "carrier_lost",
2923189714Syongari	    &stats->tx_carrier_lost, "Carrier lost");
2924189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "multi_colls",
2925189714Syongari	    &stats->tx_multi_colls, "Multiple collisions");
2926189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "excess_colls",
2927189714Syongari	    &stats->tx_excess_colls, "Excessive collisions");
2928189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "fifo_underruns",
2929189714Syongari	    &stats->tx_fifo_underruns, "FIFO underruns");
2930189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "mcast_oflows",
2931189714Syongari	    &stats->tx_mcast_oflows, "Multicast overflows");
2932189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "filtered",
2933189714Syongari	    &stats->tx_filtered, "Filtered frames");
293480219Swpaul
2935189714Syongari	/* Rx statistics. */
2936189714Syongari	tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD,
2937189714Syongari	    NULL, "Rx MAC statistics");
2938189714Syongari	child = SYSCTL_CHILDREN(tree);
2939189714Syongari
2940189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "frames",
2941189714Syongari	    &stats->rx_frames, "Frames");
2942189714Syongari	TXP_SYSCTL_STAT_ADD64(ctx, child, "octets",
2943189714Syongari	    &stats->rx_bytes, "Octets");
2944189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "fifo_oflows",
2945189714Syongari	    &stats->rx_fifo_oflows, "FIFO overflows");
2946189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "badssd",
2947189714Syongari	    &stats->rx_badssd, "Bad SSD");
2948189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "crcerrs",
2949189714Syongari	    &stats->rx_crcerrs, "CRC errors");
2950189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "lenerrs",
2951189714Syongari	    &stats->rx_lenerrs, "Length errors");
2952189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "bcast_frames",
2953189714Syongari	    &stats->rx_bcast_frames, "Broadcast frames");
2954189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "mcast_frames",
2955189714Syongari	    &stats->rx_mcast_frames, "Multicast frames");
2956189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "oflows",
2957189714Syongari	    &stats->rx_oflows, "Overflows");
2958189714Syongari	TXP_SYSCTL_STAT_ADD32(ctx, child, "filtered",
2959189714Syongari	    &stats->rx_filtered, "Filtered frames");
296080219Swpaul}
2961189714Syongari
2962189714Syongari#undef TXP_SYSCTL_STAT_ADD32
2963189714Syongari#undef TXP_SYSCTL_STAT_ADD64
2964189714Syongari
2965189714Syongaristatic int
2966189714Syongarisysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high)
2967189714Syongari{
2968189714Syongari	int error, value;
2969189714Syongari
2970189714Syongari	if (arg1 == NULL)
2971189714Syongari		return (EINVAL);
2972189714Syongari	value = *(int *)arg1;
2973189714Syongari	error = sysctl_handle_int(oidp, &value, 0, req);
2974189714Syongari	if (error || req->newptr == NULL)
2975189714Syongari		return (error);
2976189714Syongari	if (value < low || value > high)
2977189714Syongari		return (EINVAL);
2978189714Syongari        *(int *)arg1 = value;
2979189714Syongari
2980189714Syongari        return (0);
2981189714Syongari}
2982189714Syongari
2983189714Syongaristatic int
2984189714Syongarisysctl_hw_txp_proc_limit(SYSCTL_HANDLER_ARGS)
2985189714Syongari{
2986189714Syongari	return (sysctl_int_range(oidp, arg1, arg2, req,
2987189714Syongari	    TXP_PROC_MIN, TXP_PROC_MAX));
2988189714Syongari}
2989