ioat.c revision 292228
1287117Scem/*-
2287117Scem * Copyright (C) 2012 Intel Corporation
3287117Scem * All rights reserved.
4287117Scem *
5287117Scem * Redistribution and use in source and binary forms, with or without
6287117Scem * modification, are permitted provided that the following conditions
7287117Scem * are met:
8287117Scem * 1. Redistributions of source code must retain the above copyright
9287117Scem *    notice, this list of conditions and the following disclaimer.
10287117Scem * 2. Redistributions in binary form must reproduce the above copyright
11287117Scem *    notice, this list of conditions and the following disclaimer in the
12287117Scem *    documentation and/or other materials provided with the distribution.
13287117Scem *
14287117Scem * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15287117Scem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16287117Scem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17287117Scem * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18287117Scem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19287117Scem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20287117Scem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21287117Scem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22287117Scem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23287117Scem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24287117Scem * SUCH DAMAGE.
25287117Scem */
26287117Scem
27287117Scem#include <sys/cdefs.h>
28287117Scem__FBSDID("$FreeBSD: head/sys/dev/ioat/ioat.c 292228 2015-12-14 22:01:52Z cem $");
29287117Scem
30287117Scem#include <sys/param.h>
31287117Scem#include <sys/systm.h>
32287117Scem#include <sys/bus.h>
33287117Scem#include <sys/conf.h>
34287117Scem#include <sys/ioccom.h>
35287117Scem#include <sys/kernel.h>
36287117Scem#include <sys/lock.h>
37287117Scem#include <sys/malloc.h>
38287117Scem#include <sys/module.h>
39287117Scem#include <sys/mutex.h>
40287117Scem#include <sys/rman.h>
41290229Scem#include <sys/sbuf.h>
42287117Scem#include <sys/sysctl.h>
43287117Scem#include <sys/time.h>
44287117Scem#include <dev/pci/pcireg.h>
45287117Scem#include <dev/pci/pcivar.h>
46287117Scem#include <machine/bus.h>
47287117Scem#include <machine/resource.h>
48287117Scem#include <machine/stdarg.h>
49287117Scem
50287117Scem#include "ioat.h"
51287117Scem#include "ioat_hw.h"
52287117Scem#include "ioat_internal.h"
53287117Scem
54289904Scem#define	IOAT_INTR_TIMO	(hz / 10)
55289907Scem#define	IOAT_REFLK	(&ioat->submit_lock)
56289904Scem
57287117Scemstatic int ioat_probe(device_t device);
58287117Scemstatic int ioat_attach(device_t device);
59287117Scemstatic int ioat_detach(device_t device);
60287403Scemstatic int ioat_setup_intr(struct ioat_softc *ioat);
61287403Scemstatic int ioat_teardown_intr(struct ioat_softc *ioat);
62287117Scemstatic int ioat3_attach(device_t device);
63289912Scemstatic int ioat_start_channel(struct ioat_softc *ioat);
64287117Scemstatic int ioat_map_pci_bar(struct ioat_softc *ioat);
65287117Scemstatic void ioat_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg,
66287117Scem    int error);
67287117Scemstatic void ioat_interrupt_handler(void *arg);
68287414Scemstatic boolean_t ioat_model_resets_msix(struct ioat_softc *ioat);
69290229Scemstatic int chanerr_to_errno(uint32_t);
70287117Scemstatic void ioat_process_events(struct ioat_softc *ioat);
71287117Scemstatic inline uint32_t ioat_get_active(struct ioat_softc *ioat);
72287117Scemstatic inline uint32_t ioat_get_ring_space(struct ioat_softc *ioat);
73289982Scemstatic void ioat_free_ring(struct ioat_softc *, uint32_t size,
74289982Scem    struct ioat_descriptor **);
75287117Scemstatic void ioat_free_ring_entry(struct ioat_softc *ioat,
76287117Scem    struct ioat_descriptor *desc);
77289982Scemstatic struct ioat_descriptor *ioat_alloc_ring_entry(struct ioat_softc *,
78289982Scem    int mflags);
79289982Scemstatic int ioat_reserve_space(struct ioat_softc *, uint32_t, int mflags);
80289907Scemstatic struct ioat_descriptor *ioat_get_ring_entry(struct ioat_softc *ioat,
81287117Scem    uint32_t index);
82289982Scemstatic struct ioat_descriptor **ioat_prealloc_ring(struct ioat_softc *,
83289982Scem    uint32_t size, boolean_t need_dscr, int mflags);
84289982Scemstatic int ring_grow(struct ioat_softc *, uint32_t oldorder,
85289982Scem    struct ioat_descriptor **);
86289982Scemstatic int ring_shrink(struct ioat_softc *, uint32_t oldorder,
87289982Scem    struct ioat_descriptor **);
88290229Scemstatic void ioat_halted_debug(struct ioat_softc *, uint32_t);
89287117Scemstatic void ioat_timer_callback(void *arg);
90287117Scemstatic void dump_descriptor(void *hw_desc);
91287117Scemstatic void ioat_submit_single(struct ioat_softc *ioat);
92287117Scemstatic void ioat_comp_update_map(void *arg, bus_dma_segment_t *seg, int nseg,
93287117Scem    int error);
94287117Scemstatic int ioat_reset_hw(struct ioat_softc *ioat);
95287117Scemstatic void ioat_setup_sysctl(device_t device);
96289908Scemstatic int sysctl_handle_reset(SYSCTL_HANDLER_ARGS);
97289907Scemstatic inline struct ioat_softc *ioat_get(struct ioat_softc *,
98289907Scem    enum ioat_ref_kind);
99289907Scemstatic inline void ioat_put(struct ioat_softc *, enum ioat_ref_kind);
100290229Scemstatic inline void _ioat_putn(struct ioat_softc *, uint32_t,
101290229Scem    enum ioat_ref_kind, boolean_t);
102289907Scemstatic inline void ioat_putn(struct ioat_softc *, uint32_t,
103289907Scem    enum ioat_ref_kind);
104290229Scemstatic inline void ioat_putn_locked(struct ioat_softc *, uint32_t,
105290229Scem    enum ioat_ref_kind);
106290131Scemstatic void ioat_drain_locked(struct ioat_softc *);
107287117Scem
108289776Scem#define	ioat_log_message(v, ...) do {					\
109289776Scem	if ((v) <= g_ioat_debug_level) {				\
110289776Scem		device_printf(ioat->device, __VA_ARGS__);		\
111289776Scem	}								\
112289776Scem} while (0)
113289776Scem
114287117ScemMALLOC_DEFINE(M_IOAT, "ioat", "ioat driver memory allocations");
115287117ScemSYSCTL_NODE(_hw, OID_AUTO, ioat, CTLFLAG_RD, 0, "ioat node");
116287117Scem
117287117Scemstatic int g_force_legacy_interrupts;
118287117ScemSYSCTL_INT(_hw_ioat, OID_AUTO, force_legacy_interrupts, CTLFLAG_RDTUN,
119287117Scem    &g_force_legacy_interrupts, 0, "Set to non-zero to force MSI-X disabled");
120287117Scem
121289776Scemint g_ioat_debug_level = 0;
122287117ScemSYSCTL_INT(_hw_ioat, OID_AUTO, debug_level, CTLFLAG_RWTUN, &g_ioat_debug_level,
123287117Scem    0, "Set log level (0-3) for ioat(4). Higher is more verbose.");
124287117Scem
125287117Scem/*
126287117Scem * OS <-> Driver interface structures
127287117Scem */
128287117Scemstatic device_method_t ioat_pci_methods[] = {
129287117Scem	/* Device interface */
130287117Scem	DEVMETHOD(device_probe,     ioat_probe),
131287117Scem	DEVMETHOD(device_attach,    ioat_attach),
132287117Scem	DEVMETHOD(device_detach,    ioat_detach),
133287117Scem	{ 0, 0 }
134287117Scem};
135287117Scem
136287117Scemstatic driver_t ioat_pci_driver = {
137287117Scem	"ioat",
138287117Scem	ioat_pci_methods,
139287117Scem	sizeof(struct ioat_softc),
140287117Scem};
141287117Scem
142287117Scemstatic devclass_t ioat_devclass;
143287117ScemDRIVER_MODULE(ioat, pci, ioat_pci_driver, ioat_devclass, 0, 0);
144291826ScemMODULE_VERSION(ioat, 1);
145287117Scem
146287117Scem/*
147287117Scem * Private data structures
148287117Scem */
149287117Scemstatic struct ioat_softc *ioat_channel[IOAT_MAX_CHANNELS];
150287117Scemstatic int ioat_channel_index = 0;
151287117ScemSYSCTL_INT(_hw_ioat, OID_AUTO, channels, CTLFLAG_RD, &ioat_channel_index, 0,
152287117Scem    "Number of IOAT channels attached");
153287117Scem
154287117Scemstatic struct _pcsid
155287117Scem{
156287117Scem	u_int32_t   type;
157287117Scem	const char  *desc;
158287117Scem} pci_ids[] = {
159287117Scem	{ 0x34308086, "TBG IOAT Ch0" },
160287117Scem	{ 0x34318086, "TBG IOAT Ch1" },
161287117Scem	{ 0x34328086, "TBG IOAT Ch2" },
162287117Scem	{ 0x34338086, "TBG IOAT Ch3" },
163287117Scem	{ 0x34298086, "TBG IOAT Ch4" },
164287117Scem	{ 0x342a8086, "TBG IOAT Ch5" },
165287117Scem	{ 0x342b8086, "TBG IOAT Ch6" },
166287117Scem	{ 0x342c8086, "TBG IOAT Ch7" },
167287117Scem
168287117Scem	{ 0x37108086, "JSF IOAT Ch0" },
169287117Scem	{ 0x37118086, "JSF IOAT Ch1" },
170287117Scem	{ 0x37128086, "JSF IOAT Ch2" },
171287117Scem	{ 0x37138086, "JSF IOAT Ch3" },
172287117Scem	{ 0x37148086, "JSF IOAT Ch4" },
173287117Scem	{ 0x37158086, "JSF IOAT Ch5" },
174287117Scem	{ 0x37168086, "JSF IOAT Ch6" },
175287117Scem	{ 0x37178086, "JSF IOAT Ch7" },
176287117Scem	{ 0x37188086, "JSF IOAT Ch0 (RAID)" },
177287117Scem	{ 0x37198086, "JSF IOAT Ch1 (RAID)" },
178287117Scem
179287117Scem	{ 0x3c208086, "SNB IOAT Ch0" },
180287117Scem	{ 0x3c218086, "SNB IOAT Ch1" },
181287117Scem	{ 0x3c228086, "SNB IOAT Ch2" },
182287117Scem	{ 0x3c238086, "SNB IOAT Ch3" },
183287117Scem	{ 0x3c248086, "SNB IOAT Ch4" },
184287117Scem	{ 0x3c258086, "SNB IOAT Ch5" },
185287117Scem	{ 0x3c268086, "SNB IOAT Ch6" },
186287117Scem	{ 0x3c278086, "SNB IOAT Ch7" },
187287117Scem	{ 0x3c2e8086, "SNB IOAT Ch0 (RAID)" },
188287117Scem	{ 0x3c2f8086, "SNB IOAT Ch1 (RAID)" },
189287117Scem
190287117Scem	{ 0x0e208086, "IVB IOAT Ch0" },
191287117Scem	{ 0x0e218086, "IVB IOAT Ch1" },
192287117Scem	{ 0x0e228086, "IVB IOAT Ch2" },
193287117Scem	{ 0x0e238086, "IVB IOAT Ch3" },
194287117Scem	{ 0x0e248086, "IVB IOAT Ch4" },
195287117Scem	{ 0x0e258086, "IVB IOAT Ch5" },
196287117Scem	{ 0x0e268086, "IVB IOAT Ch6" },
197287117Scem	{ 0x0e278086, "IVB IOAT Ch7" },
198287117Scem	{ 0x0e2e8086, "IVB IOAT Ch0 (RAID)" },
199287117Scem	{ 0x0e2f8086, "IVB IOAT Ch1 (RAID)" },
200287117Scem
201287117Scem	{ 0x2f208086, "HSW IOAT Ch0" },
202287117Scem	{ 0x2f218086, "HSW IOAT Ch1" },
203287117Scem	{ 0x2f228086, "HSW IOAT Ch2" },
204287117Scem	{ 0x2f238086, "HSW IOAT Ch3" },
205287117Scem	{ 0x2f248086, "HSW IOAT Ch4" },
206287117Scem	{ 0x2f258086, "HSW IOAT Ch5" },
207287117Scem	{ 0x2f268086, "HSW IOAT Ch6" },
208287117Scem	{ 0x2f278086, "HSW IOAT Ch7" },
209287117Scem	{ 0x2f2e8086, "HSW IOAT Ch0 (RAID)" },
210287117Scem	{ 0x2f2f8086, "HSW IOAT Ch1 (RAID)" },
211287117Scem
212287117Scem	{ 0x0c508086, "BWD IOAT Ch0" },
213287117Scem	{ 0x0c518086, "BWD IOAT Ch1" },
214287117Scem	{ 0x0c528086, "BWD IOAT Ch2" },
215287117Scem	{ 0x0c538086, "BWD IOAT Ch3" },
216287117Scem
217287117Scem	{ 0x6f508086, "BDXDE IOAT Ch0" },
218287117Scem	{ 0x6f518086, "BDXDE IOAT Ch1" },
219287117Scem	{ 0x6f528086, "BDXDE IOAT Ch2" },
220287117Scem	{ 0x6f538086, "BDXDE IOAT Ch3" },
221287117Scem
222292032Scem	{ 0x6f208086, "BDX IOAT Ch0" },
223292032Scem	{ 0x6f218086, "BDX IOAT Ch1" },
224292032Scem	{ 0x6f228086, "BDX IOAT Ch2" },
225292032Scem	{ 0x6f238086, "BDX IOAT Ch3" },
226292032Scem	{ 0x6f248086, "BDX IOAT Ch4" },
227292032Scem	{ 0x6f258086, "BDX IOAT Ch5" },
228292032Scem	{ 0x6f268086, "BDX IOAT Ch6" },
229292032Scem	{ 0x6f278086, "BDX IOAT Ch7" },
230292032Scem	{ 0x6f2e8086, "BDX IOAT Ch0 (RAID)" },
231292032Scem	{ 0x6f2f8086, "BDX IOAT Ch1 (RAID)" },
232292032Scem
233287117Scem	{ 0x00000000, NULL           }
234287117Scem};
235287117Scem
236287117Scem/*
237287117Scem * OS <-> Driver linkage functions
238287117Scem */
239287117Scemstatic int
240287117Scemioat_probe(device_t device)
241287117Scem{
242287117Scem	struct _pcsid *ep;
243287117Scem	u_int32_t type;
244287117Scem
245287117Scem	type = pci_get_devid(device);
246287117Scem	for (ep = pci_ids; ep->type; ep++) {
247287117Scem		if (ep->type == type) {
248287117Scem			device_set_desc(device, ep->desc);
249287117Scem			return (0);
250287117Scem		}
251287117Scem	}
252287117Scem	return (ENXIO);
253287117Scem}
254287117Scem
255287117Scemstatic int
256287117Scemioat_attach(device_t device)
257287117Scem{
258287117Scem	struct ioat_softc *ioat;
259287117Scem	int error;
260287117Scem
261287117Scem	ioat = DEVICE2SOFTC(device);
262287117Scem	ioat->device = device;
263287117Scem
264287117Scem	error = ioat_map_pci_bar(ioat);
265287117Scem	if (error != 0)
266287117Scem		goto err;
267287117Scem
268287117Scem	ioat->version = ioat_read_cbver(ioat);
269287117Scem	if (ioat->version < IOAT_VER_3_0) {
270287117Scem		error = ENODEV;
271287117Scem		goto err;
272287117Scem	}
273287117Scem
274287117Scem	error = ioat3_attach(device);
275287117Scem	if (error != 0)
276287117Scem		goto err;
277287117Scem
278287117Scem	error = pci_enable_busmaster(device);
279287117Scem	if (error != 0)
280287117Scem		goto err;
281287117Scem
282289907Scem	error = ioat_setup_intr(ioat);
283289907Scem	if (error != 0)
284289907Scem		goto err;
285289907Scem
286289912Scem	error = ioat_reset_hw(ioat);
287289760Scem	if (error != 0)
288289907Scem		goto err;
289289760Scem
290289760Scem	ioat_process_events(ioat);
291289760Scem	ioat_setup_sysctl(device);
292289760Scem
293290131Scem	ioat->chan_idx = ioat_channel_index;
294287117Scem	ioat_channel[ioat_channel_index++] = ioat;
295289760Scem	ioat_test_attach();
296287117Scem
297287117Scemerr:
298287117Scem	if (error != 0)
299287117Scem		ioat_detach(device);
300287117Scem	return (error);
301287117Scem}
302287117Scem
303287117Scemstatic int
304287117Scemioat_detach(device_t device)
305287117Scem{
306287117Scem	struct ioat_softc *ioat;
307287117Scem
308287117Scem	ioat = DEVICE2SOFTC(device);
309289760Scem
310289760Scem	ioat_test_detach();
311289904Scem
312290131Scem	mtx_lock(IOAT_REFLK);
313290131Scem	ioat->quiescing = TRUE;
314290131Scem	ioat_channel[ioat->chan_idx] = NULL;
315290131Scem
316290131Scem	ioat_drain_locked(ioat);
317290131Scem	mtx_unlock(IOAT_REFLK);
318290131Scem
319289904Scem	ioat_teardown_intr(ioat);
320287117Scem	callout_drain(&ioat->timer);
321287117Scem
322287117Scem	pci_disable_busmaster(device);
323287117Scem
324287117Scem	if (ioat->pci_resource != NULL)
325287117Scem		bus_release_resource(device, SYS_RES_MEMORY,
326287117Scem		    ioat->pci_resource_id, ioat->pci_resource);
327287117Scem
328289982Scem	if (ioat->ring != NULL)
329289982Scem		ioat_free_ring(ioat, 1 << ioat->ring_size_order, ioat->ring);
330287117Scem
331287117Scem	if (ioat->comp_update != NULL) {
332287117Scem		bus_dmamap_unload(ioat->comp_update_tag, ioat->comp_update_map);
333287117Scem		bus_dmamem_free(ioat->comp_update_tag, ioat->comp_update,
334287117Scem		    ioat->comp_update_map);
335287117Scem		bus_dma_tag_destroy(ioat->comp_update_tag);
336287117Scem	}
337287117Scem
338287117Scem	bus_dma_tag_destroy(ioat->hw_desc_tag);
339287117Scem
340287403Scem	return (0);
341287403Scem}
342287403Scem
343287403Scemstatic int
344287403Scemioat_teardown_intr(struct ioat_softc *ioat)
345287403Scem{
346287403Scem
347287117Scem	if (ioat->tag != NULL)
348287403Scem		bus_teardown_intr(ioat->device, ioat->res, ioat->tag);
349287117Scem
350287117Scem	if (ioat->res != NULL)
351287403Scem		bus_release_resource(ioat->device, SYS_RES_IRQ,
352287117Scem		    rman_get_rid(ioat->res), ioat->res);
353287117Scem
354287403Scem	pci_release_msi(ioat->device);
355287117Scem	return (0);
356287117Scem}
357287117Scem
358287117Scemstatic int
359289912Scemioat_start_channel(struct ioat_softc *ioat)
360287117Scem{
361287117Scem	uint64_t status;
362287117Scem	uint32_t chanerr;
363287117Scem	int i;
364287117Scem
365287117Scem	ioat_acquire(&ioat->dmaengine);
366287117Scem	ioat_null(&ioat->dmaengine, NULL, NULL, 0);
367287117Scem	ioat_release(&ioat->dmaengine);
368287117Scem
369287117Scem	for (i = 0; i < 100; i++) {
370287117Scem		DELAY(1);
371287117Scem		status = ioat_get_chansts(ioat);
372287117Scem		if (is_ioat_idle(status))
373287117Scem			return (0);
374287117Scem	}
375287117Scem
376287117Scem	chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
377287117Scem	ioat_log_message(0, "could not start channel: "
378289983Scem	    "status = %#jx error = %b\n", (uintmax_t)status, (int)chanerr,
379289983Scem	    IOAT_CHANERR_STR);
380287117Scem	return (ENXIO);
381287117Scem}
382287117Scem
383287117Scem/*
384287117Scem * Initialize Hardware
385287117Scem */
386287117Scemstatic int
387287117Scemioat3_attach(device_t device)
388287117Scem{
389287117Scem	struct ioat_softc *ioat;
390287117Scem	struct ioat_descriptor **ring;
391287117Scem	struct ioat_descriptor *next;
392287117Scem	struct ioat_dma_hw_descriptor *dma_hw_desc;
393287117Scem	int i, num_descriptors;
394287117Scem	int error;
395287117Scem	uint8_t xfercap;
396287117Scem
397287117Scem	error = 0;
398287117Scem	ioat = DEVICE2SOFTC(device);
399290087Scem	ioat->capabilities = ioat_read_dmacapability(ioat);
400287117Scem
401290087Scem	ioat_log_message(1, "Capabilities: %b\n", (int)ioat->capabilities,
402290087Scem	    IOAT_DMACAP_STR);
403290087Scem
404287117Scem	xfercap = ioat_read_xfercap(ioat);
405287117Scem	ioat->max_xfer_size = 1 << xfercap;
406287117Scem
407292228Scem	ioat->intrdelay_supported = (ioat_read_2(ioat, IOAT_INTRDELAY_OFFSET) &
408292228Scem	    IOAT_INTRDELAY_SUPPORTED) != 0;
409292228Scem	if (ioat->intrdelay_supported)
410292228Scem		ioat->intrdelay_max = IOAT_INTRDELAY_US_MASK;
411292228Scem
412287117Scem	/* TODO: need to check DCA here if we ever do XOR/PQ */
413287117Scem
414287117Scem	mtx_init(&ioat->submit_lock, "ioat_submit", NULL, MTX_DEF);
415290229Scem	mtx_init(&ioat->cleanup_lock, "ioat_cleanup", NULL, MTX_DEF);
416289760Scem	callout_init(&ioat->timer, 1);
417287117Scem
418290229Scem	/* Establish lock order for Witness */
419290229Scem	mtx_lock(&ioat->submit_lock);
420290229Scem	mtx_lock(&ioat->cleanup_lock);
421290229Scem	mtx_unlock(&ioat->cleanup_lock);
422290229Scem	mtx_unlock(&ioat->submit_lock);
423290229Scem
424287117Scem	ioat->is_resize_pending = FALSE;
425287117Scem	ioat->is_completion_pending = FALSE;
426287117Scem	ioat->is_reset_pending = FALSE;
427287117Scem	ioat->is_channel_running = FALSE;
428287117Scem
429287117Scem	bus_dma_tag_create(bus_get_dma_tag(ioat->device), sizeof(uint64_t), 0x0,
430287117Scem	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
431287117Scem	    sizeof(uint64_t), 1, sizeof(uint64_t), 0, NULL, NULL,
432287117Scem	    &ioat->comp_update_tag);
433287117Scem
434287117Scem	error = bus_dmamem_alloc(ioat->comp_update_tag,
435287117Scem	    (void **)&ioat->comp_update, BUS_DMA_ZERO, &ioat->comp_update_map);
436287117Scem	if (ioat->comp_update == NULL)
437287117Scem		return (ENOMEM);
438287117Scem
439287117Scem	error = bus_dmamap_load(ioat->comp_update_tag, ioat->comp_update_map,
440287117Scem	    ioat->comp_update, sizeof(uint64_t), ioat_comp_update_map, ioat,
441287117Scem	    0);
442287117Scem	if (error != 0)
443287117Scem		return (error);
444287117Scem
445287117Scem	ioat->ring_size_order = IOAT_MIN_ORDER;
446287117Scem
447287117Scem	num_descriptors = 1 << ioat->ring_size_order;
448287117Scem
449287117Scem	bus_dma_tag_create(bus_get_dma_tag(ioat->device), 0x40, 0x0,
450287117Scem	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
451287117Scem	    sizeof(struct ioat_dma_hw_descriptor), 1,
452287117Scem	    sizeof(struct ioat_dma_hw_descriptor), 0, NULL, NULL,
453287117Scem	    &ioat->hw_desc_tag);
454287117Scem
455287117Scem	ioat->ring = malloc(num_descriptors * sizeof(*ring), M_IOAT,
456289982Scem	    M_ZERO | M_WAITOK);
457287117Scem	if (ioat->ring == NULL)
458287117Scem		return (ENOMEM);
459287117Scem
460287117Scem	ring = ioat->ring;
461287117Scem	for (i = 0; i < num_descriptors; i++) {
462289982Scem		ring[i] = ioat_alloc_ring_entry(ioat, M_WAITOK);
463287117Scem		if (ring[i] == NULL)
464287117Scem			return (ENOMEM);
465287117Scem
466287117Scem		ring[i]->id = i;
467287117Scem	}
468287117Scem
469287117Scem	for (i = 0; i < num_descriptors - 1; i++) {
470287117Scem		next = ring[i + 1];
471287117Scem		dma_hw_desc = ring[i]->u.dma;
472287117Scem
473287117Scem		dma_hw_desc->next = next->hw_desc_bus_addr;
474287117Scem	}
475287117Scem
476287117Scem	ring[i]->u.dma->next = ring[0]->hw_desc_bus_addr;
477287117Scem
478289982Scem	ioat->head = ioat->hw_head = 0;
479287117Scem	ioat->tail = 0;
480287117Scem	ioat->last_seen = 0;
481287117Scem	return (0);
482287117Scem}
483287117Scem
484287117Scemstatic int
485287117Scemioat_map_pci_bar(struct ioat_softc *ioat)
486287117Scem{
487287117Scem
488287117Scem	ioat->pci_resource_id = PCIR_BAR(0);
489289911Scem	ioat->pci_resource = bus_alloc_resource_any(ioat->device,
490289911Scem	    SYS_RES_MEMORY, &ioat->pci_resource_id, RF_ACTIVE);
491287117Scem
492287117Scem	if (ioat->pci_resource == NULL) {
493287117Scem		ioat_log_message(0, "unable to allocate pci resource\n");
494287117Scem		return (ENODEV);
495287117Scem	}
496287117Scem
497287117Scem	ioat->pci_bus_tag = rman_get_bustag(ioat->pci_resource);
498287117Scem	ioat->pci_bus_handle = rman_get_bushandle(ioat->pci_resource);
499287117Scem	return (0);
500287117Scem}
501287117Scem
502287117Scemstatic void
503287117Scemioat_comp_update_map(void *arg, bus_dma_segment_t *seg, int nseg, int error)
504287117Scem{
505287117Scem	struct ioat_softc *ioat = arg;
506287117Scem
507289912Scem	KASSERT(error == 0, ("%s: error:%d", __func__, error));
508287117Scem	ioat->comp_update_bus_addr = seg[0].ds_addr;
509287117Scem}
510287117Scem
511287117Scemstatic void
512287117Scemioat_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
513287117Scem{
514287117Scem	bus_addr_t *baddr;
515287117Scem
516289912Scem	KASSERT(error == 0, ("%s: error:%d", __func__, error));
517287117Scem	baddr = arg;
518287117Scem	*baddr = segs->ds_addr;
519287117Scem}
520287117Scem
521287117Scem/*
522287117Scem * Interrupt setup and handlers
523287117Scem */
524287117Scemstatic int
525287403Scemioat_setup_intr(struct ioat_softc *ioat)
526287117Scem{
527287117Scem	uint32_t num_vectors;
528287117Scem	int error;
529287117Scem	boolean_t use_msix;
530287117Scem	boolean_t force_legacy_interrupts;
531287117Scem
532287117Scem	use_msix = FALSE;
533287117Scem	force_legacy_interrupts = FALSE;
534287117Scem
535287117Scem	if (!g_force_legacy_interrupts && pci_msix_count(ioat->device) >= 1) {
536287117Scem		num_vectors = 1;
537287117Scem		pci_alloc_msix(ioat->device, &num_vectors);
538287117Scem		if (num_vectors == 1)
539287117Scem			use_msix = TRUE;
540287117Scem	}
541287117Scem
542287117Scem	if (use_msix) {
543287117Scem		ioat->rid = 1;
544287117Scem		ioat->res = bus_alloc_resource_any(ioat->device, SYS_RES_IRQ,
545287117Scem		    &ioat->rid, RF_ACTIVE);
546287117Scem	} else {
547287117Scem		ioat->rid = 0;
548287117Scem		ioat->res = bus_alloc_resource_any(ioat->device, SYS_RES_IRQ,
549287117Scem		    &ioat->rid, RF_SHAREABLE | RF_ACTIVE);
550287117Scem	}
551287117Scem	if (ioat->res == NULL) {
552287117Scem		ioat_log_message(0, "bus_alloc_resource failed\n");
553287117Scem		return (ENOMEM);
554287117Scem	}
555287117Scem
556287117Scem	ioat->tag = NULL;
557287117Scem	error = bus_setup_intr(ioat->device, ioat->res, INTR_MPSAFE |
558287117Scem	    INTR_TYPE_MISC, NULL, ioat_interrupt_handler, ioat, &ioat->tag);
559287117Scem	if (error != 0) {
560287117Scem		ioat_log_message(0, "bus_setup_intr failed\n");
561287117Scem		return (error);
562287117Scem	}
563287117Scem
564287117Scem	ioat_write_intrctrl(ioat, IOAT_INTRCTRL_MASTER_INT_EN);
565287117Scem	return (0);
566287117Scem}
567287117Scem
568287403Scemstatic boolean_t
569287414Scemioat_model_resets_msix(struct ioat_softc *ioat)
570287403Scem{
571287403Scem	u_int32_t pciid;
572287403Scem
573287403Scem	pciid = pci_get_devid(ioat->device);
574287403Scem	switch (pciid) {
575287414Scem		/* BWD: */
576287414Scem	case 0x0c508086:
577287414Scem	case 0x0c518086:
578287414Scem	case 0x0c528086:
579287414Scem	case 0x0c538086:
580287414Scem		/* BDXDE: */
581287403Scem	case 0x6f508086:
582287403Scem	case 0x6f518086:
583287403Scem	case 0x6f528086:
584287403Scem	case 0x6f538086:
585287403Scem		return (TRUE);
586287403Scem	}
587287403Scem
588287403Scem	return (FALSE);
589287403Scem}
590287403Scem
591287117Scemstatic void
592287117Scemioat_interrupt_handler(void *arg)
593287117Scem{
594287117Scem	struct ioat_softc *ioat = arg;
595287117Scem
596292226Scem	ioat->stats.interrupts++;
597287117Scem	ioat_process_events(ioat);
598287117Scem}
599287117Scem
600290229Scemstatic int
601290229Scemchanerr_to_errno(uint32_t chanerr)
602290229Scem{
603290229Scem
604290229Scem	if (chanerr == 0)
605290229Scem		return (0);
606290229Scem	if ((chanerr & (IOAT_CHANERR_XSADDERR | IOAT_CHANERR_XDADDERR)) != 0)
607290229Scem		return (EFAULT);
608290229Scem	if ((chanerr & (IOAT_CHANERR_RDERR | IOAT_CHANERR_WDERR)) != 0)
609290229Scem		return (EIO);
610290229Scem	/* This one is probably our fault: */
611290229Scem	if ((chanerr & IOAT_CHANERR_NDADDERR) != 0)
612290229Scem		return (EIO);
613290229Scem	return (EIO);
614290229Scem}
615290229Scem
616287117Scemstatic void
617287117Scemioat_process_events(struct ioat_softc *ioat)
618287117Scem{
619287117Scem	struct ioat_descriptor *desc;
620287117Scem	struct bus_dmadesc *dmadesc;
621287117Scem	uint64_t comp_update, status;
622290229Scem	uint32_t completed, chanerr;
623290229Scem	int error;
624287117Scem
625287117Scem	mtx_lock(&ioat->cleanup_lock);
626287117Scem
627287117Scem	completed = 0;
628287117Scem	comp_update = *ioat->comp_update;
629287117Scem	status = comp_update & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_MASK;
630287117Scem
631289979Scem	CTR0(KTR_IOAT, __func__);
632287117Scem
633289909Scem	if (status == ioat->last_seen)
634289909Scem		goto out;
635287117Scem
636287117Scem	while (1) {
637287117Scem		desc = ioat_get_ring_entry(ioat, ioat->tail);
638287117Scem		dmadesc = &desc->bus_dmadesc;
639289979Scem		CTR1(KTR_IOAT, "completing desc %d", ioat->tail);
640287117Scem
641290229Scem		if (dmadesc->callback_fn != NULL)
642290229Scem			dmadesc->callback_fn(dmadesc->callback_arg, 0);
643287117Scem
644289907Scem		completed++;
645287117Scem		ioat->tail++;
646287117Scem		if (desc->hw_desc_bus_addr == status)
647287117Scem			break;
648287117Scem	}
649287117Scem
650287117Scem	ioat->last_seen = desc->hw_desc_bus_addr;
651287117Scem
652287117Scem	if (ioat->head == ioat->tail) {
653287117Scem		ioat->is_completion_pending = FALSE;
654289904Scem		callout_reset(&ioat->timer, IOAT_INTR_TIMO,
655289904Scem		    ioat_timer_callback, ioat);
656287117Scem	}
657287117Scem
658292226Scem	ioat->stats.descriptors_processed += completed;
659292226Scem
660289909Scemout:
661287117Scem	ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
662287117Scem	mtx_unlock(&ioat->cleanup_lock);
663289907Scem
664289907Scem	ioat_putn(ioat, completed, IOAT_ACTIVE_DESCR_REF);
665289982Scem	wakeup(&ioat->tail);
666290229Scem
667290229Scem	if (!is_ioat_halted(comp_update))
668290229Scem		return;
669290229Scem
670292226Scem	ioat->stats.channel_halts++;
671292226Scem
672290229Scem	/*
673290229Scem	 * Fatal programming error on this DMA channel.  Flush any outstanding
674290229Scem	 * work with error status and restart the engine.
675290229Scem	 */
676290229Scem	ioat_log_message(0, "Channel halted due to fatal programming error\n");
677290229Scem	mtx_lock(&ioat->submit_lock);
678290229Scem	mtx_lock(&ioat->cleanup_lock);
679290229Scem	ioat->quiescing = TRUE;
680290229Scem
681290229Scem	chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
682290229Scem	ioat_halted_debug(ioat, chanerr);
683292226Scem	ioat->stats.last_halt_chanerr = chanerr;
684290229Scem
685290229Scem	while (ioat_get_active(ioat) > 0) {
686290229Scem		desc = ioat_get_ring_entry(ioat, ioat->tail);
687290229Scem		dmadesc = &desc->bus_dmadesc;
688290229Scem		CTR1(KTR_IOAT, "completing err desc %d", ioat->tail);
689290229Scem
690290229Scem		if (dmadesc->callback_fn != NULL)
691290229Scem			dmadesc->callback_fn(dmadesc->callback_arg,
692290229Scem			    chanerr_to_errno(chanerr));
693290229Scem
694290229Scem		ioat_putn_locked(ioat, 1, IOAT_ACTIVE_DESCR_REF);
695290229Scem		ioat->tail++;
696292226Scem		ioat->stats.descriptors_processed++;
697292226Scem		ioat->stats.descriptors_error++;
698290229Scem	}
699290229Scem
700290229Scem	/* Clear error status */
701290229Scem	ioat_write_4(ioat, IOAT_CHANERR_OFFSET, chanerr);
702290229Scem
703290229Scem	mtx_unlock(&ioat->cleanup_lock);
704290229Scem	mtx_unlock(&ioat->submit_lock);
705290229Scem
706290229Scem	ioat_log_message(0, "Resetting channel to recover from error\n");
707290229Scem	error = ioat_reset_hw(ioat);
708290229Scem	KASSERT(error == 0, ("%s: reset failed: %d", __func__, error));
709287117Scem}
710287117Scem
711287117Scem/*
712287117Scem * User API functions
713287117Scem */
714287117Scembus_dmaengine_t
715287117Scemioat_get_dmaengine(uint32_t index)
716287117Scem{
717290131Scem	struct ioat_softc *sc;
718287117Scem
719289907Scem	if (index >= ioat_channel_index)
720289907Scem		return (NULL);
721290131Scem
722290131Scem	sc = ioat_channel[index];
723290131Scem	if (sc == NULL || sc->quiescing)
724290131Scem		return (NULL);
725290131Scem
726290131Scem	return (&ioat_get(sc, IOAT_DMAENGINE_REF)->dmaengine);
727287117Scem}
728287117Scem
729287117Scemvoid
730289907Scemioat_put_dmaengine(bus_dmaengine_t dmaengine)
731289907Scem{
732289907Scem	struct ioat_softc *ioat;
733289907Scem
734289907Scem	ioat = to_ioat_softc(dmaengine);
735289907Scem	ioat_put(ioat, IOAT_DMAENGINE_REF);
736289907Scem}
737289907Scem
738292228Scemint
739292228Scemioat_set_interrupt_coalesce(bus_dmaengine_t dmaengine, uint16_t delay)
740292228Scem{
741292228Scem	struct ioat_softc *ioat;
742292228Scem
743292228Scem	ioat = to_ioat_softc(dmaengine);
744292228Scem	if (!ioat->intrdelay_supported)
745292228Scem		return (ENODEV);
746292228Scem	if (delay > ioat->intrdelay_max)
747292228Scem		return (ERANGE);
748292228Scem
749292228Scem	ioat_write_2(ioat, IOAT_INTRDELAY_OFFSET, delay);
750292228Scem	ioat->cached_intrdelay =
751292228Scem	    ioat_read_2(ioat, IOAT_INTRDELAY_OFFSET) & IOAT_INTRDELAY_US_MASK;
752292228Scem	return (0);
753292228Scem}
754292228Scem
755292228Scemuint16_t
756292228Scemioat_get_max_coalesce_period(bus_dmaengine_t dmaengine)
757292228Scem{
758292228Scem	struct ioat_softc *ioat;
759292228Scem
760292228Scem	ioat = to_ioat_softc(dmaengine);
761292228Scem	return (ioat->intrdelay_max);
762292228Scem}
763292228Scem
764289907Scemvoid
765287117Scemioat_acquire(bus_dmaengine_t dmaengine)
766287117Scem{
767287117Scem	struct ioat_softc *ioat;
768287117Scem
769287117Scem	ioat = to_ioat_softc(dmaengine);
770287117Scem	mtx_lock(&ioat->submit_lock);
771289979Scem	CTR0(KTR_IOAT, __func__);
772287117Scem}
773287117Scem
774287117Scemvoid
775287117Scemioat_release(bus_dmaengine_t dmaengine)
776287117Scem{
777287117Scem	struct ioat_softc *ioat;
778287117Scem
779289776Scem	ioat = to_ioat_softc(dmaengine);
780289979Scem	CTR0(KTR_IOAT, __func__);
781289982Scem	ioat_write_2(ioat, IOAT_DMACOUNT_OFFSET, (uint16_t)ioat->hw_head);
782287117Scem	mtx_unlock(&ioat->submit_lock);
783287117Scem}
784287117Scem
785290020Scemstatic struct ioat_descriptor *
786290020Scemioat_op_generic(struct ioat_softc *ioat, uint8_t op,
787290020Scem    uint32_t size, uint64_t src, uint64_t dst,
788290020Scem    bus_dmaengine_callback_t callback_fn, void *callback_arg,
789290020Scem    uint32_t flags)
790287117Scem{
791290020Scem	struct ioat_generic_hw_descriptor *hw_desc;
792287117Scem	struct ioat_descriptor *desc;
793289982Scem	int mflags;
794287117Scem
795290020Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
796290020Scem
797287117Scem	KASSERT((flags & ~DMA_ALL_FLAGS) == 0, ("Unrecognized flag(s): %#x",
798287117Scem		flags & ~DMA_ALL_FLAGS));
799289982Scem	if ((flags & DMA_NO_WAIT) != 0)
800289982Scem		mflags = M_NOWAIT;
801289982Scem	else
802289982Scem		mflags = M_WAITOK;
803287117Scem
804290020Scem	if (size > ioat->max_xfer_size) {
805290020Scem		ioat_log_message(0, "%s: max_xfer_size = %d, requested = %u\n",
806290020Scem		    __func__, ioat->max_xfer_size, (unsigned)size);
807290020Scem		return (NULL);
808290020Scem	}
809287117Scem
810289982Scem	if (ioat_reserve_space(ioat, 1, mflags) != 0)
811287117Scem		return (NULL);
812287117Scem
813287117Scem	desc = ioat_get_ring_entry(ioat, ioat->head);
814290020Scem	hw_desc = desc->u.generic;
815287117Scem
816287117Scem	hw_desc->u.control_raw = 0;
817290020Scem	hw_desc->u.control_generic.op = op;
818290020Scem	hw_desc->u.control_generic.completion_update = 1;
819287117Scem
820287117Scem	if ((flags & DMA_INT_EN) != 0)
821290020Scem		hw_desc->u.control_generic.int_enable = 1;
822287117Scem
823290020Scem	hw_desc->size = size;
824290020Scem	hw_desc->src_addr = src;
825290020Scem	hw_desc->dest_addr = dst;
826287117Scem
827287117Scem	desc->bus_dmadesc.callback_fn = callback_fn;
828287117Scem	desc->bus_dmadesc.callback_arg = callback_arg;
829290020Scem	return (desc);
830290020Scem}
831287117Scem
832290020Scemstruct bus_dmadesc *
833290020Scemioat_null(bus_dmaengine_t dmaengine, bus_dmaengine_callback_t callback_fn,
834290020Scem    void *callback_arg, uint32_t flags)
835290020Scem{
836290020Scem	struct ioat_dma_hw_descriptor *hw_desc;
837290020Scem	struct ioat_descriptor *desc;
838290020Scem	struct ioat_softc *ioat;
839290020Scem
840290020Scem	CTR0(KTR_IOAT, __func__);
841290020Scem	ioat = to_ioat_softc(dmaengine);
842290020Scem
843290020Scem	desc = ioat_op_generic(ioat, IOAT_OP_COPY, 8, 0, 0, callback_fn,
844290020Scem	    callback_arg, flags);
845290020Scem	if (desc == NULL)
846290020Scem		return (NULL);
847290020Scem
848290020Scem	hw_desc = desc->u.dma;
849290020Scem	hw_desc->u.control.null = 1;
850287117Scem	ioat_submit_single(ioat);
851287117Scem	return (&desc->bus_dmadesc);
852287117Scem}
853287117Scem
854287117Scemstruct bus_dmadesc *
855287117Scemioat_copy(bus_dmaengine_t dmaengine, bus_addr_t dst,
856287117Scem    bus_addr_t src, bus_size_t len, bus_dmaengine_callback_t callback_fn,
857287117Scem    void *callback_arg, uint32_t flags)
858287117Scem{
859290020Scem	struct ioat_dma_hw_descriptor *hw_desc;
860287117Scem	struct ioat_descriptor *desc;
861287117Scem	struct ioat_softc *ioat;
862287117Scem
863290020Scem	CTR0(KTR_IOAT, __func__);
864287117Scem	ioat = to_ioat_softc(dmaengine);
865287117Scem
866290020Scem	if (((src | dst) & (0xffffull << 48)) != 0) {
867290020Scem		ioat_log_message(0, "%s: High 16 bits of src/dst invalid\n",
868290020Scem		    __func__);
869287117Scem		return (NULL);
870287117Scem	}
871287117Scem
872290020Scem	desc = ioat_op_generic(ioat, IOAT_OP_COPY, len, src, dst, callback_fn,
873290020Scem	    callback_arg, flags);
874290020Scem	if (desc == NULL)
875287117Scem		return (NULL);
876287117Scem
877287117Scem	hw_desc = desc->u.dma;
878287117Scem	if (g_ioat_debug_level >= 3)
879287117Scem		dump_descriptor(hw_desc);
880287117Scem
881287117Scem	ioat_submit_single(ioat);
882287117Scem	return (&desc->bus_dmadesc);
883287117Scem}
884287117Scem
885290021Scemstruct bus_dmadesc *
886292031Scemioat_copy_8k_aligned(bus_dmaengine_t dmaengine, bus_addr_t dst1,
887292031Scem    bus_addr_t dst2, bus_addr_t src1, bus_addr_t src2,
888292031Scem    bus_dmaengine_callback_t callback_fn, void *callback_arg, uint32_t flags)
889292031Scem{
890292031Scem	struct ioat_dma_hw_descriptor *hw_desc;
891292031Scem	struct ioat_descriptor *desc;
892292031Scem	struct ioat_softc *ioat;
893292031Scem
894292031Scem	CTR0(KTR_IOAT, __func__);
895292031Scem	ioat = to_ioat_softc(dmaengine);
896292031Scem
897292031Scem	if (((src1 | src2 | dst1 | dst2) & (0xffffull << 48)) != 0) {
898292031Scem		ioat_log_message(0, "%s: High 16 bits of src/dst invalid\n",
899292031Scem		    __func__);
900292031Scem		return (NULL);
901292031Scem	}
902292031Scem	if (((src1 | src2 | dst1 | dst2) & PAGE_MASK) != 0) {
903292031Scem		ioat_log_message(0, "%s: Addresses must be page-aligned\n",
904292031Scem		    __func__);
905292031Scem		return (NULL);
906292031Scem	}
907292031Scem
908292031Scem	desc = ioat_op_generic(ioat, IOAT_OP_COPY, 2 * PAGE_SIZE, src1, dst1,
909292031Scem	    callback_fn, callback_arg, flags);
910292031Scem	if (desc == NULL)
911292031Scem		return (NULL);
912292031Scem
913292031Scem	hw_desc = desc->u.dma;
914292031Scem	if (src2 != src1 + PAGE_SIZE) {
915292031Scem		hw_desc->u.control.src_page_break = 1;
916292031Scem		hw_desc->next_src_addr = src2;
917292031Scem	}
918292031Scem	if (dst2 != dst1 + PAGE_SIZE) {
919292031Scem		hw_desc->u.control.dest_page_break = 1;
920292031Scem		hw_desc->next_dest_addr = dst2;
921292031Scem	}
922292031Scem
923292031Scem	if (g_ioat_debug_level >= 3)
924292031Scem		dump_descriptor(hw_desc);
925292031Scem
926292031Scem	ioat_submit_single(ioat);
927292031Scem	return (&desc->bus_dmadesc);
928292031Scem}
929292031Scem
930292031Scemstruct bus_dmadesc *
931290021Scemioat_blockfill(bus_dmaengine_t dmaengine, bus_addr_t dst, uint64_t fillpattern,
932290021Scem    bus_size_t len, bus_dmaengine_callback_t callback_fn, void *callback_arg,
933290021Scem    uint32_t flags)
934290021Scem{
935290021Scem	struct ioat_fill_hw_descriptor *hw_desc;
936290021Scem	struct ioat_descriptor *desc;
937290021Scem	struct ioat_softc *ioat;
938290021Scem
939290021Scem	CTR0(KTR_IOAT, __func__);
940290021Scem	ioat = to_ioat_softc(dmaengine);
941290021Scem
942290087Scem	if ((ioat->capabilities & IOAT_DMACAP_BFILL) == 0) {
943290087Scem		ioat_log_message(0, "%s: Device lacks BFILL capability\n",
944290087Scem		    __func__);
945290087Scem		return (NULL);
946290087Scem	}
947290087Scem
948290021Scem	if ((dst & (0xffffull << 48)) != 0) {
949290021Scem		ioat_log_message(0, "%s: High 16 bits of dst invalid\n",
950290021Scem		    __func__);
951290021Scem		return (NULL);
952290021Scem	}
953290021Scem
954290021Scem	desc = ioat_op_generic(ioat, IOAT_OP_FILL, len, fillpattern, dst,
955290021Scem	    callback_fn, callback_arg, flags);
956290021Scem	if (desc == NULL)
957290021Scem		return (NULL);
958290021Scem
959290021Scem	hw_desc = desc->u.fill;
960290021Scem	if (g_ioat_debug_level >= 3)
961290021Scem		dump_descriptor(hw_desc);
962290021Scem
963290021Scem	ioat_submit_single(ioat);
964290021Scem	return (&desc->bus_dmadesc);
965290021Scem}
966290021Scem
967287117Scem/*
968287117Scem * Ring Management
969287117Scem */
970287117Scemstatic inline uint32_t
971287117Scemioat_get_active(struct ioat_softc *ioat)
972287117Scem{
973287117Scem
974287117Scem	return ((ioat->head - ioat->tail) & ((1 << ioat->ring_size_order) - 1));
975287117Scem}
976287117Scem
977287117Scemstatic inline uint32_t
978287117Scemioat_get_ring_space(struct ioat_softc *ioat)
979287117Scem{
980287117Scem
981287117Scem	return ((1 << ioat->ring_size_order) - ioat_get_active(ioat) - 1);
982287117Scem}
983287117Scem
984287117Scemstatic struct ioat_descriptor *
985289982Scemioat_alloc_ring_entry(struct ioat_softc *ioat, int mflags)
986287117Scem{
987290020Scem	struct ioat_generic_hw_descriptor *hw_desc;
988287117Scem	struct ioat_descriptor *desc;
989289982Scem	int error, busdmaflag;
990287117Scem
991289905Scem	error = ENOMEM;
992289905Scem	hw_desc = NULL;
993289905Scem
994289982Scem	if ((mflags & M_WAITOK) != 0)
995289982Scem		busdmaflag = BUS_DMA_WAITOK;
996289982Scem	else
997289982Scem		busdmaflag = BUS_DMA_NOWAIT;
998289982Scem
999289982Scem	desc = malloc(sizeof(*desc), M_IOAT, mflags);
1000287117Scem	if (desc == NULL)
1001289905Scem		goto out;
1002287117Scem
1003289905Scem	bus_dmamem_alloc(ioat->hw_desc_tag, (void **)&hw_desc,
1004289982Scem	    BUS_DMA_ZERO | busdmaflag, &ioat->hw_desc_map);
1005289905Scem	if (hw_desc == NULL)
1006289905Scem		goto out;
1007289905Scem
1008290229Scem	memset(&desc->bus_dmadesc, 0, sizeof(desc->bus_dmadesc));
1009290020Scem	desc->u.generic = hw_desc;
1010289905Scem
1011289905Scem	error = bus_dmamap_load(ioat->hw_desc_tag, ioat->hw_desc_map, hw_desc,
1012289905Scem	    sizeof(*hw_desc), ioat_dmamap_cb, &desc->hw_desc_bus_addr,
1013289982Scem	    busdmaflag);
1014289905Scem	if (error)
1015289905Scem		goto out;
1016289905Scem
1017289905Scemout:
1018289905Scem	if (error) {
1019289905Scem		ioat_free_ring_entry(ioat, desc);
1020287117Scem		return (NULL);
1021287117Scem	}
1022287117Scem	return (desc);
1023287117Scem}
1024287117Scem
1025287117Scemstatic void
1026287117Scemioat_free_ring_entry(struct ioat_softc *ioat, struct ioat_descriptor *desc)
1027287117Scem{
1028287117Scem
1029287117Scem	if (desc == NULL)
1030287117Scem		return;
1031287117Scem
1032290020Scem	if (desc->u.generic)
1033290020Scem		bus_dmamem_free(ioat->hw_desc_tag, desc->u.generic,
1034287117Scem		    ioat->hw_desc_map);
1035287117Scem	free(desc, M_IOAT);
1036287117Scem}
1037287117Scem
1038289982Scem/*
1039289982Scem * Reserves space in this IOAT descriptor ring by ensuring enough slots remain
1040289982Scem * for 'num_descs'.
1041289982Scem *
1042289982Scem * If mflags contains M_WAITOK, blocks until enough space is available.
1043289982Scem *
1044289982Scem * Returns zero on success, or an errno on error.  If num_descs is beyond the
1045289982Scem * maximum ring size, returns EINVAl; if allocation would block and mflags
1046289982Scem * contains M_NOWAIT, returns EAGAIN.
1047289982Scem *
1048289982Scem * Must be called with the submit_lock held; returns with the lock held.  The
1049289982Scem * lock may be dropped to allocate the ring.
1050289982Scem *
1051289982Scem * (The submit_lock is needed to add any entries to the ring, so callers are
1052289982Scem * assured enough room is available.)
1053289982Scem */
1054287117Scemstatic int
1055289982Scemioat_reserve_space(struct ioat_softc *ioat, uint32_t num_descs, int mflags)
1056287117Scem{
1057289982Scem	struct ioat_descriptor **new_ring;
1058289982Scem	uint32_t order;
1059289982Scem	int error;
1060287117Scem
1061289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
1062289982Scem	error = 0;
1063289982Scem
1064289982Scem	if (num_descs < 1 || num_descs > (1 << IOAT_MAX_ORDER)) {
1065289982Scem		error = EINVAL;
1066289982Scem		goto out;
1067289982Scem	}
1068290131Scem	if (ioat->quiescing) {
1069290131Scem		error = ENXIO;
1070290131Scem		goto out;
1071290131Scem	}
1072289982Scem
1073289982Scem	for (;;) {
1074287117Scem		if (ioat_get_ring_space(ioat) >= num_descs)
1075289982Scem			goto out;
1076287117Scem
1077289982Scem		order = ioat->ring_size_order;
1078289982Scem		if (ioat->is_resize_pending || order == IOAT_MAX_ORDER) {
1079289982Scem			if ((mflags & M_WAITOK) != 0) {
1080289982Scem				msleep(&ioat->tail, &ioat->submit_lock, 0,
1081289982Scem				    "ioat_rsz", 0);
1082289982Scem				continue;
1083289982Scem			}
1084287117Scem
1085289982Scem			error = EAGAIN;
1086289982Scem			break;
1087289982Scem		}
1088289982Scem
1089289982Scem		ioat->is_resize_pending = TRUE;
1090289982Scem		for (;;) {
1091289982Scem			mtx_unlock(&ioat->submit_lock);
1092289982Scem
1093289982Scem			new_ring = ioat_prealloc_ring(ioat, 1 << (order + 1),
1094289982Scem			    TRUE, mflags);
1095289982Scem
1096289982Scem			mtx_lock(&ioat->submit_lock);
1097289982Scem			KASSERT(ioat->ring_size_order == order,
1098289982Scem			    ("is_resize_pending should protect order"));
1099289982Scem
1100289982Scem			if (new_ring == NULL) {
1101289982Scem				KASSERT((mflags & M_WAITOK) == 0,
1102289982Scem				    ("allocation failed"));
1103289982Scem				error = EAGAIN;
1104289982Scem				break;
1105289982Scem			}
1106289982Scem
1107289982Scem			error = ring_grow(ioat, order, new_ring);
1108289982Scem			if (error == 0)
1109289982Scem				break;
1110289982Scem		}
1111289982Scem		ioat->is_resize_pending = FALSE;
1112289982Scem		wakeup(&ioat->tail);
1113289982Scem		if (error)
1114289982Scem			break;
1115287117Scem	}
1116289982Scem
1117289982Scemout:
1118289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
1119289982Scem	return (error);
1120287117Scem}
1121287117Scem
1122289982Scemstatic struct ioat_descriptor **
1123289982Scemioat_prealloc_ring(struct ioat_softc *ioat, uint32_t size, boolean_t need_dscr,
1124289982Scem    int mflags)
1125289982Scem{
1126289982Scem	struct ioat_descriptor **ring;
1127289982Scem	uint32_t i;
1128289982Scem	int error;
1129289982Scem
1130289982Scem	KASSERT(size > 0 && powerof2(size), ("bogus size"));
1131289982Scem
1132289982Scem	ring = malloc(size * sizeof(*ring), M_IOAT, M_ZERO | mflags);
1133289982Scem	if (ring == NULL)
1134289982Scem		return (NULL);
1135289982Scem
1136289982Scem	if (need_dscr) {
1137289982Scem		error = ENOMEM;
1138289982Scem		for (i = size / 2; i < size; i++) {
1139289982Scem			ring[i] = ioat_alloc_ring_entry(ioat, mflags);
1140289982Scem			if (ring[i] == NULL)
1141289982Scem				goto out;
1142289982Scem			ring[i]->id = i;
1143289982Scem		}
1144289982Scem	}
1145289982Scem	error = 0;
1146289982Scem
1147289982Scemout:
1148289982Scem	if (error != 0 && ring != NULL) {
1149289982Scem		ioat_free_ring(ioat, size, ring);
1150289982Scem		ring = NULL;
1151289982Scem	}
1152289982Scem	return (ring);
1153289982Scem}
1154289982Scem
1155289982Scemstatic void
1156289982Scemioat_free_ring(struct ioat_softc *ioat, uint32_t size,
1157289982Scem    struct ioat_descriptor **ring)
1158289982Scem{
1159289982Scem	uint32_t i;
1160289982Scem
1161289982Scem	for (i = 0; i < size; i++) {
1162289982Scem		if (ring[i] != NULL)
1163289982Scem			ioat_free_ring_entry(ioat, ring[i]);
1164289982Scem	}
1165289982Scem	free(ring, M_IOAT);
1166289982Scem}
1167289982Scem
1168287117Scemstatic struct ioat_descriptor *
1169287117Scemioat_get_ring_entry(struct ioat_softc *ioat, uint32_t index)
1170287117Scem{
1171287117Scem
1172287117Scem	return (ioat->ring[index % (1 << ioat->ring_size_order)]);
1173287117Scem}
1174287117Scem
1175289982Scemstatic int
1176289982Scemring_grow(struct ioat_softc *ioat, uint32_t oldorder,
1177289982Scem    struct ioat_descriptor **newring)
1178287117Scem{
1179289982Scem	struct ioat_descriptor *tmp, *next;
1180287117Scem	struct ioat_dma_hw_descriptor *hw;
1181289982Scem	uint32_t oldsize, newsize, head, tail, i, end;
1182289982Scem	int error;
1183287117Scem
1184289982Scem	CTR0(KTR_IOAT, __func__);
1185287117Scem
1186289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
1187287117Scem
1188289982Scem	if (oldorder != ioat->ring_size_order || oldorder >= IOAT_MAX_ORDER) {
1189289982Scem		error = EINVAL;
1190289982Scem		goto out;
1191289982Scem	}
1192287117Scem
1193289982Scem	oldsize = (1 << oldorder);
1194289982Scem	newsize = (1 << (oldorder + 1));
1195287117Scem
1196289982Scem	mtx_lock(&ioat->cleanup_lock);
1197287117Scem
1198289982Scem	head = ioat->head & (oldsize - 1);
1199289982Scem	tail = ioat->tail & (oldsize - 1);
1200287117Scem
1201289982Scem	/* Copy old descriptors to new ring */
1202289982Scem	for (i = 0; i < oldsize; i++)
1203289982Scem		newring[i] = ioat->ring[i];
1204287117Scem
1205289982Scem	/*
1206289982Scem	 * If head has wrapped but tail hasn't, we must swap some descriptors
1207289982Scem	 * around so that tail can increment directly to head.
1208289982Scem	 */
1209289982Scem	if (head < tail) {
1210289982Scem		for (i = 0; i <= head; i++) {
1211289982Scem			tmp = newring[oldsize + i];
1212287117Scem
1213289982Scem			newring[oldsize + i] = newring[i];
1214289982Scem			newring[oldsize + i]->id = oldsize + i;
1215287117Scem
1216289982Scem			newring[i] = tmp;
1217289982Scem			newring[i]->id = i;
1218287117Scem		}
1219289982Scem		head += oldsize;
1220289982Scem	}
1221287117Scem
1222289982Scem	KASSERT(head >= tail, ("invariants"));
1223287117Scem
1224289982Scem	/* Head didn't wrap; we only need to link in oldsize..newsize */
1225289982Scem	if (head < oldsize) {
1226289982Scem		i = oldsize - 1;
1227289982Scem		end = newsize;
1228287117Scem	} else {
1229289982Scem		/* Head did wrap; link newhead..newsize and 0..oldhead */
1230289982Scem		i = head;
1231289982Scem		end = newsize + (head - oldsize) + 1;
1232289982Scem	}
1233287117Scem
1234289982Scem	/*
1235289982Scem	 * Fix up hardware ring, being careful not to trample the active
1236289982Scem	 * section (tail -> head).
1237289982Scem	 */
1238289982Scem	for (; i < end; i++) {
1239289982Scem		KASSERT((i & (newsize - 1)) < tail ||
1240289982Scem		    (i & (newsize - 1)) >= head, ("trampling snake"));
1241287117Scem
1242289982Scem		next = newring[(i + 1) & (newsize - 1)];
1243289982Scem		hw = newring[i & (newsize - 1)]->u.dma;
1244287117Scem		hw->next = next->hw_desc_bus_addr;
1245287117Scem	}
1246287117Scem
1247287117Scem	free(ioat->ring, M_IOAT);
1248289982Scem	ioat->ring = newring;
1249289982Scem	ioat->ring_size_order = oldorder + 1;
1250289982Scem	ioat->tail = tail;
1251289982Scem	ioat->head = head;
1252289982Scem	error = 0;
1253287117Scem
1254289982Scem	mtx_unlock(&ioat->cleanup_lock);
1255289982Scemout:
1256289982Scem	if (error)
1257289982Scem		ioat_free_ring(ioat, (1 << (oldorder + 1)), newring);
1258289982Scem	return (error);
1259287117Scem}
1260287117Scem
1261289982Scemstatic int
1262289982Scemring_shrink(struct ioat_softc *ioat, uint32_t oldorder,
1263289982Scem    struct ioat_descriptor **newring)
1264289982Scem{
1265289982Scem	struct ioat_dma_hw_descriptor *hw;
1266289982Scem	struct ioat_descriptor *ent, *next;
1267289982Scem	uint32_t oldsize, newsize, current_idx, new_idx, i;
1268289982Scem	int error;
1269289982Scem
1270289982Scem	CTR0(KTR_IOAT, __func__);
1271289982Scem
1272289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
1273289982Scem
1274289982Scem	if (oldorder != ioat->ring_size_order || oldorder <= IOAT_MIN_ORDER) {
1275289982Scem		error = EINVAL;
1276289982Scem		goto out_unlocked;
1277289982Scem	}
1278289982Scem
1279289982Scem	oldsize = (1 << oldorder);
1280289982Scem	newsize = (1 << (oldorder - 1));
1281289982Scem
1282289982Scem	mtx_lock(&ioat->cleanup_lock);
1283289982Scem
1284289982Scem	/* Can't shrink below current active set! */
1285289982Scem	if (ioat_get_active(ioat) >= newsize) {
1286289982Scem		error = ENOMEM;
1287289982Scem		goto out;
1288289982Scem	}
1289289982Scem
1290289982Scem	/*
1291289982Scem	 * Copy current descriptors to the new ring, dropping the removed
1292289982Scem	 * descriptors.
1293289982Scem	 */
1294289982Scem	for (i = 0; i < newsize; i++) {
1295289982Scem		current_idx = (ioat->tail + i) & (oldsize - 1);
1296289982Scem		new_idx = (ioat->tail + i) & (newsize - 1);
1297289982Scem
1298289982Scem		newring[new_idx] = ioat->ring[current_idx];
1299289982Scem		newring[new_idx]->id = new_idx;
1300289982Scem	}
1301289982Scem
1302289982Scem	/* Free deleted descriptors */
1303289982Scem	for (i = newsize; i < oldsize; i++) {
1304289982Scem		ent = ioat_get_ring_entry(ioat, ioat->tail + i);
1305289982Scem		ioat_free_ring_entry(ioat, ent);
1306289982Scem	}
1307289982Scem
1308289982Scem	/* Fix up hardware ring. */
1309289982Scem	hw = newring[(ioat->tail + newsize - 1) & (newsize - 1)]->u.dma;
1310289982Scem	next = newring[(ioat->tail + newsize) & (newsize - 1)];
1311289982Scem	hw->next = next->hw_desc_bus_addr;
1312289982Scem
1313289982Scem	free(ioat->ring, M_IOAT);
1314289982Scem	ioat->ring = newring;
1315289982Scem	ioat->ring_size_order = oldorder - 1;
1316289982Scem	error = 0;
1317289982Scem
1318289982Scemout:
1319289982Scem	mtx_unlock(&ioat->cleanup_lock);
1320289982Scemout_unlocked:
1321289982Scem	if (error)
1322289982Scem		ioat_free_ring(ioat, (1 << (oldorder - 1)), newring);
1323289982Scem	return (error);
1324289982Scem}
1325289982Scem
1326287117Scemstatic void
1327289910Scemioat_halted_debug(struct ioat_softc *ioat, uint32_t chanerr)
1328289910Scem{
1329289910Scem	struct ioat_descriptor *desc;
1330289910Scem
1331289983Scem	ioat_log_message(0, "Channel halted (%b)\n", (int)chanerr,
1332289983Scem	    IOAT_CHANERR_STR);
1333289910Scem	if (chanerr == 0)
1334289910Scem		return;
1335289910Scem
1336290229Scem	mtx_assert(&ioat->cleanup_lock, MA_OWNED);
1337290229Scem
1338289910Scem	desc = ioat_get_ring_entry(ioat, ioat->tail + 0);
1339289910Scem	dump_descriptor(desc->u.raw);
1340289910Scem
1341289910Scem	desc = ioat_get_ring_entry(ioat, ioat->tail + 1);
1342289910Scem	dump_descriptor(desc->u.raw);
1343289910Scem}
1344289910Scem
1345289910Scemstatic void
1346287117Scemioat_timer_callback(void *arg)
1347287117Scem{
1348289982Scem	struct ioat_descriptor **newring;
1349287117Scem	struct ioat_softc *ioat;
1350290229Scem	uint32_t order;
1351287117Scem
1352287117Scem	ioat = arg;
1353289904Scem	ioat_log_message(1, "%s\n", __func__);
1354287117Scem
1355287117Scem	if (ioat->is_completion_pending) {
1356290229Scem		ioat_process_events(ioat);
1357290229Scem		return;
1358290229Scem	}
1359287117Scem
1360290229Scem	/* Slowly scale the ring down if idle. */
1361290229Scem	mtx_lock(&ioat->submit_lock);
1362290229Scem	order = ioat->ring_size_order;
1363290229Scem	if (ioat->is_resize_pending || order == IOAT_MIN_ORDER) {
1364289982Scem		mtx_unlock(&ioat->submit_lock);
1365290229Scem		goto out;
1366290229Scem	}
1367290229Scem	ioat->is_resize_pending = TRUE;
1368290229Scem	mtx_unlock(&ioat->submit_lock);
1369287117Scem
1370290229Scem	newring = ioat_prealloc_ring(ioat, 1 << (order - 1), FALSE,
1371290229Scem	    M_NOWAIT);
1372287117Scem
1373290229Scem	mtx_lock(&ioat->submit_lock);
1374290229Scem	KASSERT(ioat->ring_size_order == order,
1375290229Scem	    ("resize_pending protects order"));
1376289982Scem
1377290229Scem	if (newring != NULL)
1378290229Scem		ring_shrink(ioat, order, newring);
1379289982Scem
1380290229Scem	ioat->is_resize_pending = FALSE;
1381290229Scem	mtx_unlock(&ioat->submit_lock);
1382287117Scem
1383289982Scemout:
1384290229Scem	if (ioat->ring_size_order > IOAT_MIN_ORDER)
1385290229Scem		callout_reset(&ioat->timer, 10 * hz,
1386290229Scem		    ioat_timer_callback, ioat);
1387287117Scem}
1388287117Scem
1389287117Scem/*
1390287117Scem * Support Functions
1391287117Scem */
1392287117Scemstatic void
1393287117Scemioat_submit_single(struct ioat_softc *ioat)
1394287117Scem{
1395287117Scem
1396289907Scem	ioat_get(ioat, IOAT_ACTIVE_DESCR_REF);
1397287117Scem	atomic_add_rel_int(&ioat->head, 1);
1398289982Scem	atomic_add_rel_int(&ioat->hw_head, 1);
1399287117Scem
1400287117Scem	if (!ioat->is_completion_pending) {
1401287117Scem		ioat->is_completion_pending = TRUE;
1402289904Scem		callout_reset(&ioat->timer, IOAT_INTR_TIMO,
1403289904Scem		    ioat_timer_callback, ioat);
1404287117Scem	}
1405292226Scem
1406292226Scem	ioat->stats.descriptors_submitted++;
1407287117Scem}
1408287117Scem
1409287117Scemstatic int
1410287117Scemioat_reset_hw(struct ioat_softc *ioat)
1411287117Scem{
1412287117Scem	uint64_t status;
1413287117Scem	uint32_t chanerr;
1414289912Scem	unsigned timeout;
1415290131Scem	int error;
1416287117Scem
1417290131Scem	mtx_lock(IOAT_REFLK);
1418290131Scem	ioat->quiescing = TRUE;
1419290131Scem	ioat_drain_locked(ioat);
1420290131Scem	mtx_unlock(IOAT_REFLK);
1421290131Scem
1422287117Scem	status = ioat_get_chansts(ioat);
1423287117Scem	if (is_ioat_active(status) || is_ioat_idle(status))
1424287117Scem		ioat_suspend(ioat);
1425287117Scem
1426287117Scem	/* Wait at most 20 ms */
1427287117Scem	for (timeout = 0; (is_ioat_active(status) || is_ioat_idle(status)) &&
1428287117Scem	    timeout < 20; timeout++) {
1429287117Scem		DELAY(1000);
1430287117Scem		status = ioat_get_chansts(ioat);
1431287117Scem	}
1432290131Scem	if (timeout == 20) {
1433290131Scem		error = ETIMEDOUT;
1434290131Scem		goto out;
1435290131Scem	}
1436287117Scem
1437289912Scem	KASSERT(ioat_get_active(ioat) == 0, ("active after quiesce"));
1438289912Scem
1439287117Scem	chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
1440287117Scem	ioat_write_4(ioat, IOAT_CHANERR_OFFSET, chanerr);
1441287117Scem
1442287117Scem	/*
1443287117Scem	 * IOAT v3 workaround - CHANERRMSK_INT with 3E07h to masks out errors
1444287117Scem	 *  that can cause stability issues for IOAT v3.
1445287117Scem	 */
1446287117Scem	pci_write_config(ioat->device, IOAT_CFG_CHANERRMASK_INT_OFFSET, 0x3e07,
1447287117Scem	    4);
1448287117Scem	chanerr = pci_read_config(ioat->device, IOAT_CFG_CHANERR_INT_OFFSET, 4);
1449287117Scem	pci_write_config(ioat->device, IOAT_CFG_CHANERR_INT_OFFSET, chanerr, 4);
1450287117Scem
1451287414Scem	/*
1452287414Scem	 * BDXDE and BWD models reset MSI-X registers on device reset.
1453287414Scem	 * Save/restore their contents manually.
1454287414Scem	 */
1455289908Scem	if (ioat_model_resets_msix(ioat)) {
1456289908Scem		ioat_log_message(1, "device resets MSI-X registers; saving\n");
1457287414Scem		pci_save_state(ioat->device);
1458289908Scem	}
1459287414Scem
1460287117Scem	ioat_reset(ioat);
1461287117Scem
1462287117Scem	/* Wait at most 20 ms */
1463287117Scem	for (timeout = 0; ioat_reset_pending(ioat) && timeout < 20; timeout++)
1464287117Scem		DELAY(1000);
1465290131Scem	if (timeout == 20) {
1466290131Scem		error = ETIMEDOUT;
1467290131Scem		goto out;
1468290131Scem	}
1469287117Scem
1470289908Scem	if (ioat_model_resets_msix(ioat)) {
1471289908Scem		ioat_log_message(1, "device resets registers; restored\n");
1472287414Scem		pci_restore_state(ioat->device);
1473289908Scem	}
1474287403Scem
1475289912Scem	/* Reset attempts to return the hardware to "halted." */
1476289912Scem	status = ioat_get_chansts(ioat);
1477289912Scem	if (is_ioat_active(status) || is_ioat_idle(status)) {
1478289912Scem		/* So this really shouldn't happen... */
1479289912Scem		ioat_log_message(0, "Device is active after a reset?\n");
1480289912Scem		ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
1481290131Scem		error = 0;
1482290131Scem		goto out;
1483289912Scem	}
1484289912Scem
1485289912Scem	chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
1486290131Scem	if (chanerr != 0) {
1487290229Scem		mtx_lock(&ioat->cleanup_lock);
1488290229Scem		ioat_halted_debug(ioat, chanerr);
1489290229Scem		mtx_unlock(&ioat->cleanup_lock);
1490290131Scem		error = EIO;
1491290131Scem		goto out;
1492290131Scem	}
1493289912Scem
1494289912Scem	/*
1495289912Scem	 * Bring device back online after reset.  Writing CHAINADDR brings the
1496289912Scem	 * device back to active.
1497289912Scem	 *
1498289912Scem	 * The internal ring counter resets to zero, so we have to start over
1499289912Scem	 * at zero as well.
1500289912Scem	 */
1501289982Scem	ioat->tail = ioat->head = ioat->hw_head = 0;
1502289912Scem	ioat->last_seen = 0;
1503289912Scem
1504289912Scem	ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
1505289912Scem	ioat_write_chancmp(ioat, ioat->comp_update_bus_addr);
1506289912Scem	ioat_write_chainaddr(ioat, ioat->ring[0]->hw_desc_bus_addr);
1507290131Scem	error = 0;
1508290131Scem
1509290131Scemout:
1510290131Scem	mtx_lock(IOAT_REFLK);
1511290131Scem	ioat->quiescing = FALSE;
1512290131Scem	mtx_unlock(IOAT_REFLK);
1513290131Scem
1514290131Scem	if (error == 0)
1515290131Scem		error = ioat_start_channel(ioat);
1516290131Scem
1517290131Scem	return (error);
1518287117Scem}
1519287117Scem
1520289908Scemstatic int
1521290229Scemsysctl_handle_chansts(SYSCTL_HANDLER_ARGS)
1522290229Scem{
1523290229Scem	struct ioat_softc *ioat;
1524290229Scem	struct sbuf sb;
1525290229Scem	uint64_t status;
1526290229Scem	int error;
1527290229Scem
1528290229Scem	ioat = arg1;
1529290229Scem
1530290229Scem	status = ioat_get_chansts(ioat) & IOAT_CHANSTS_STATUS;
1531290229Scem
1532290229Scem	sbuf_new_for_sysctl(&sb, NULL, 256, req);
1533290229Scem	switch (status) {
1534290229Scem	case IOAT_CHANSTS_ACTIVE:
1535290229Scem		sbuf_printf(&sb, "ACTIVE");
1536290229Scem		break;
1537290229Scem	case IOAT_CHANSTS_IDLE:
1538290229Scem		sbuf_printf(&sb, "IDLE");
1539290229Scem		break;
1540290229Scem	case IOAT_CHANSTS_SUSPENDED:
1541290229Scem		sbuf_printf(&sb, "SUSPENDED");
1542290229Scem		break;
1543290229Scem	case IOAT_CHANSTS_HALTED:
1544290229Scem		sbuf_printf(&sb, "HALTED");
1545290229Scem		break;
1546290229Scem	case IOAT_CHANSTS_ARMED:
1547290229Scem		sbuf_printf(&sb, "ARMED");
1548290229Scem		break;
1549290229Scem	default:
1550290229Scem		sbuf_printf(&sb, "UNKNOWN");
1551290229Scem		break;
1552290229Scem	}
1553290229Scem	error = sbuf_finish(&sb);
1554290229Scem	sbuf_delete(&sb);
1555290229Scem
1556290229Scem	if (error != 0 || req->newptr == NULL)
1557290229Scem		return (error);
1558290229Scem	return (EINVAL);
1559290229Scem}
1560290229Scem
1561290229Scemstatic int
1562292226Scemsysctl_handle_dpi(SYSCTL_HANDLER_ARGS)
1563292226Scem{
1564292226Scem	struct ioat_softc *ioat;
1565292226Scem	struct sbuf sb;
1566292226Scem#define	PRECISION	"1"
1567292226Scem	const uintmax_t factor = 10;
1568292226Scem	uintmax_t rate;
1569292226Scem	int error;
1570292226Scem
1571292226Scem	ioat = arg1;
1572292226Scem	sbuf_new_for_sysctl(&sb, NULL, 16, req);
1573292226Scem
1574292226Scem	if (ioat->stats.interrupts == 0) {
1575292226Scem		sbuf_printf(&sb, "NaN");
1576292226Scem		goto out;
1577292226Scem	}
1578292226Scem	rate = ioat->stats.descriptors_processed * factor /
1579292226Scem	    ioat->stats.interrupts;
1580292226Scem	sbuf_printf(&sb, "%ju.%." PRECISION "ju", rate / factor,
1581292226Scem	    rate % factor);
1582292226Scem#undef	PRECISION
1583292226Scemout:
1584292226Scem	error = sbuf_finish(&sb);
1585292226Scem	sbuf_delete(&sb);
1586292226Scem	if (error != 0 || req->newptr == NULL)
1587292226Scem		return (error);
1588292226Scem	return (EINVAL);
1589292226Scem}
1590292226Scem
1591292226Scemstatic int
1592290229Scemsysctl_handle_error(SYSCTL_HANDLER_ARGS)
1593290229Scem{
1594290229Scem	struct ioat_descriptor *desc;
1595290229Scem	struct ioat_softc *ioat;
1596290229Scem	int error, arg;
1597290229Scem
1598290229Scem	ioat = arg1;
1599290229Scem
1600290229Scem	arg = 0;
1601290229Scem	error = SYSCTL_OUT(req, &arg, sizeof(arg));
1602290229Scem	if (error != 0 || req->newptr == NULL)
1603290229Scem		return (error);
1604290229Scem
1605290229Scem	error = SYSCTL_IN(req, &arg, sizeof(arg));
1606290229Scem	if (error != 0)
1607290229Scem		return (error);
1608290229Scem
1609290229Scem	if (arg != 0) {
1610290229Scem		ioat_acquire(&ioat->dmaengine);
1611290229Scem		desc = ioat_op_generic(ioat, IOAT_OP_COPY, 1,
1612290229Scem		    0xffff000000000000ull, 0xffff000000000000ull, NULL, NULL,
1613290229Scem		    0);
1614290229Scem		if (desc == NULL)
1615290229Scem			error = ENOMEM;
1616290229Scem		else
1617290229Scem			ioat_submit_single(ioat);
1618290229Scem		ioat_release(&ioat->dmaengine);
1619290229Scem	}
1620290229Scem	return (error);
1621290229Scem}
1622290229Scem
1623290229Scemstatic int
1624289908Scemsysctl_handle_reset(SYSCTL_HANDLER_ARGS)
1625289908Scem{
1626289908Scem	struct ioat_softc *ioat;
1627289908Scem	int error, arg;
1628289908Scem
1629289908Scem	ioat = arg1;
1630289908Scem
1631289908Scem	arg = 0;
1632289908Scem	error = SYSCTL_OUT(req, &arg, sizeof(arg));
1633289908Scem	if (error != 0 || req->newptr == NULL)
1634289908Scem		return (error);
1635289908Scem
1636289908Scem	error = SYSCTL_IN(req, &arg, sizeof(arg));
1637289908Scem	if (error != 0)
1638289908Scem		return (error);
1639289908Scem
1640289908Scem	if (arg != 0)
1641289908Scem		error = ioat_reset_hw(ioat);
1642289908Scem
1643289908Scem	return (error);
1644289908Scem}
1645289908Scem
1646287117Scemstatic void
1647287117Scemdump_descriptor(void *hw_desc)
1648287117Scem{
1649287117Scem	int i, j;
1650287117Scem
1651287117Scem	for (i = 0; i < 2; i++) {
1652287117Scem		for (j = 0; j < 8; j++)
1653287117Scem			printf("%08x ", ((uint32_t *)hw_desc)[i * 8 + j]);
1654287117Scem		printf("\n");
1655287117Scem	}
1656287117Scem}
1657287117Scem
1658287117Scemstatic void
1659287117Scemioat_setup_sysctl(device_t device)
1660287117Scem{
1661292226Scem	struct sysctl_oid_list *par, *statpar, *state, *hammer;
1662289908Scem	struct sysctl_ctx_list *ctx;
1663292226Scem	struct sysctl_oid *tree, *tmp;
1664287117Scem	struct ioat_softc *ioat;
1665287117Scem
1666287117Scem	ioat = DEVICE2SOFTC(device);
1667289908Scem	ctx = device_get_sysctl_ctx(device);
1668289908Scem	tree = device_get_sysctl_tree(device);
1669289908Scem	par = SYSCTL_CHILDREN(tree);
1670287117Scem
1671289980Scem	SYSCTL_ADD_INT(ctx, par, OID_AUTO, "version", CTLFLAG_RD,
1672289980Scem	    &ioat->version, 0, "HW version (0xMM form)");
1673289980Scem	SYSCTL_ADD_UINT(ctx, par, OID_AUTO, "max_xfer_size", CTLFLAG_RD,
1674289980Scem	    &ioat->max_xfer_size, 0, "HW maximum transfer size");
1675292228Scem	SYSCTL_ADD_INT(ctx, par, OID_AUTO, "intrdelay_supported", CTLFLAG_RD,
1676292228Scem	    &ioat->intrdelay_supported, 0, "Is INTRDELAY supported");
1677292228Scem	SYSCTL_ADD_U16(ctx, par, OID_AUTO, "intrdelay_max", CTLFLAG_RD,
1678292228Scem	    &ioat->intrdelay_max, 0,
1679292228Scem	    "Maximum configurable INTRDELAY on this channel (microseconds)");
1680289980Scem
1681292226Scem	tmp = SYSCTL_ADD_NODE(ctx, par, OID_AUTO, "state", CTLFLAG_RD, NULL,
1682292226Scem	    "IOAT channel internal state");
1683292226Scem	state = SYSCTL_CHILDREN(tmp);
1684292226Scem
1685292226Scem	SYSCTL_ADD_UINT(ctx, state, OID_AUTO, "ring_size_order", CTLFLAG_RD,
1686289982Scem	    &ioat->ring_size_order, 0, "SW descriptor ring size order");
1687292226Scem	SYSCTL_ADD_UINT(ctx, state, OID_AUTO, "head", CTLFLAG_RD, &ioat->head,
1688292226Scem	    0, "SW descriptor head pointer index");
1689292226Scem	SYSCTL_ADD_UINT(ctx, state, OID_AUTO, "tail", CTLFLAG_RD, &ioat->tail,
1690292226Scem	    0, "SW descriptor tail pointer index");
1691292226Scem	SYSCTL_ADD_UINT(ctx, state, OID_AUTO, "hw_head", CTLFLAG_RD,
1692289982Scem	    &ioat->hw_head, 0, "HW DMACOUNT");
1693289908Scem
1694292226Scem	SYSCTL_ADD_UQUAD(ctx, state, OID_AUTO, "last_completion", CTLFLAG_RD,
1695289980Scem	    ioat->comp_update, "HW addr of last completion");
1696289980Scem
1697292226Scem	SYSCTL_ADD_INT(ctx, state, OID_AUTO, "is_resize_pending", CTLFLAG_RD,
1698289980Scem	    &ioat->is_resize_pending, 0, "resize pending");
1699292226Scem	SYSCTL_ADD_INT(ctx, state, OID_AUTO, "is_completion_pending",
1700292226Scem	    CTLFLAG_RD, &ioat->is_completion_pending, 0, "completion pending");
1701292226Scem	SYSCTL_ADD_INT(ctx, state, OID_AUTO, "is_reset_pending", CTLFLAG_RD,
1702289980Scem	    &ioat->is_reset_pending, 0, "reset pending");
1703292226Scem	SYSCTL_ADD_INT(ctx, state, OID_AUTO, "is_channel_running", CTLFLAG_RD,
1704289980Scem	    &ioat->is_channel_running, 0, "channel running");
1705289980Scem
1706292226Scem	SYSCTL_ADD_PROC(ctx, state, OID_AUTO, "chansts",
1707292226Scem	    CTLTYPE_STRING | CTLFLAG_RD, ioat, 0, sysctl_handle_chansts, "A",
1708292226Scem	    "String of the channel status");
1709292226Scem
1710292228Scem	SYSCTL_ADD_U16(ctx, state, OID_AUTO, "intrdelay", CTLFLAG_RD,
1711292228Scem	    &ioat->cached_intrdelay, 0,
1712292228Scem	    "Current INTRDELAY on this channel (cached, microseconds)");
1713292228Scem
1714292226Scem	tmp = SYSCTL_ADD_NODE(ctx, par, OID_AUTO, "hammer", CTLFLAG_RD, NULL,
1715292226Scem	    "Big hammers (mostly for testing)");
1716292226Scem	hammer = SYSCTL_CHILDREN(tmp);
1717292226Scem
1718292226Scem	SYSCTL_ADD_PROC(ctx, hammer, OID_AUTO, "force_hw_reset",
1719289908Scem	    CTLTYPE_INT | CTLFLAG_RW, ioat, 0, sysctl_handle_reset, "I",
1720289908Scem	    "Set to non-zero to reset the hardware");
1721292226Scem	SYSCTL_ADD_PROC(ctx, hammer, OID_AUTO, "force_hw_error",
1722290229Scem	    CTLTYPE_INT | CTLFLAG_RW, ioat, 0, sysctl_handle_error, "I",
1723290229Scem	    "Set to non-zero to inject a recoverable hardware error");
1724292226Scem
1725292226Scem	tmp = SYSCTL_ADD_NODE(ctx, par, OID_AUTO, "stats", CTLFLAG_RD, NULL,
1726292226Scem	    "IOAT channel statistics");
1727292226Scem	statpar = SYSCTL_CHILDREN(tmp);
1728292226Scem
1729292226Scem	SYSCTL_ADD_UQUAD(ctx, statpar, OID_AUTO, "interrupts", CTLFLAG_RW,
1730292226Scem	    &ioat->stats.interrupts,
1731292226Scem	    "Number of interrupts processed on this channel");
1732292226Scem	SYSCTL_ADD_UQUAD(ctx, statpar, OID_AUTO, "descriptors", CTLFLAG_RW,
1733292226Scem	    &ioat->stats.descriptors_processed,
1734292226Scem	    "Number of descriptors processed on this channel");
1735292226Scem	SYSCTL_ADD_UQUAD(ctx, statpar, OID_AUTO, "submitted", CTLFLAG_RW,
1736292226Scem	    &ioat->stats.descriptors_submitted,
1737292226Scem	    "Number of descriptors submitted to this channel");
1738292226Scem	SYSCTL_ADD_UQUAD(ctx, statpar, OID_AUTO, "errored", CTLFLAG_RW,
1739292226Scem	    &ioat->stats.descriptors_error,
1740292226Scem	    "Number of descriptors failed by channel errors");
1741292226Scem	SYSCTL_ADD_U32(ctx, statpar, OID_AUTO, "halts", CTLFLAG_RW,
1742292226Scem	    &ioat->stats.channel_halts, 0,
1743292226Scem	    "Number of times the channel has halted");
1744292226Scem	SYSCTL_ADD_U32(ctx, statpar, OID_AUTO, "last_halt_chanerr", CTLFLAG_RW,
1745292226Scem	    &ioat->stats.last_halt_chanerr, 0,
1746292226Scem	    "The raw CHANERR when the channel was last halted");
1747292226Scem
1748292226Scem	SYSCTL_ADD_PROC(ctx, statpar, OID_AUTO, "desc_per_interrupt",
1749292226Scem	    CTLTYPE_STRING | CTLFLAG_RD, ioat, 0, sysctl_handle_dpi, "A",
1750292226Scem	    "Descriptors per interrupt");
1751287117Scem}
1752289907Scem
1753289907Scemstatic inline struct ioat_softc *
1754289907Scemioat_get(struct ioat_softc *ioat, enum ioat_ref_kind kind)
1755289907Scem{
1756289907Scem	uint32_t old;
1757289907Scem
1758289907Scem	KASSERT(kind < IOAT_NUM_REF_KINDS, ("bogus"));
1759289907Scem
1760289907Scem	old = atomic_fetchadd_32(&ioat->refcnt, 1);
1761289907Scem	KASSERT(old < UINT32_MAX, ("refcnt overflow"));
1762289907Scem
1763289907Scem#ifdef INVARIANTS
1764289907Scem	old = atomic_fetchadd_32(&ioat->refkinds[kind], 1);
1765289907Scem	KASSERT(old < UINT32_MAX, ("refcnt kind overflow"));
1766289907Scem#endif
1767289907Scem
1768289907Scem	return (ioat);
1769289907Scem}
1770289907Scem
1771289907Scemstatic inline void
1772289907Scemioat_putn(struct ioat_softc *ioat, uint32_t n, enum ioat_ref_kind kind)
1773289907Scem{
1774290229Scem
1775290229Scem	_ioat_putn(ioat, n, kind, FALSE);
1776290229Scem}
1777290229Scem
1778290229Scemstatic inline void
1779290229Scemioat_putn_locked(struct ioat_softc *ioat, uint32_t n, enum ioat_ref_kind kind)
1780290229Scem{
1781290229Scem
1782290229Scem	_ioat_putn(ioat, n, kind, TRUE);
1783290229Scem}
1784290229Scem
1785290229Scemstatic inline void
1786290229Scem_ioat_putn(struct ioat_softc *ioat, uint32_t n, enum ioat_ref_kind kind,
1787290229Scem    boolean_t locked)
1788290229Scem{
1789289907Scem	uint32_t old;
1790289907Scem
1791289907Scem	KASSERT(kind < IOAT_NUM_REF_KINDS, ("bogus"));
1792289907Scem
1793289907Scem	if (n == 0)
1794289907Scem		return;
1795289907Scem
1796289907Scem#ifdef INVARIANTS
1797289907Scem	old = atomic_fetchadd_32(&ioat->refkinds[kind], -n);
1798289907Scem	KASSERT(old >= n, ("refcnt kind underflow"));
1799289907Scem#endif
1800289907Scem
1801289907Scem	/* Skip acquiring the lock if resulting refcnt > 0. */
1802289907Scem	for (;;) {
1803289907Scem		old = ioat->refcnt;
1804289907Scem		if (old <= n)
1805289907Scem			break;
1806289907Scem		if (atomic_cmpset_32(&ioat->refcnt, old, old - n))
1807289907Scem			return;
1808289907Scem	}
1809289907Scem
1810290229Scem	if (locked)
1811290229Scem		mtx_assert(IOAT_REFLK, MA_OWNED);
1812290229Scem	else
1813290229Scem		mtx_lock(IOAT_REFLK);
1814290229Scem
1815289907Scem	old = atomic_fetchadd_32(&ioat->refcnt, -n);
1816289907Scem	KASSERT(old >= n, ("refcnt error"));
1817289907Scem
1818289907Scem	if (old == n)
1819289907Scem		wakeup(IOAT_REFLK);
1820290229Scem	if (!locked)
1821290229Scem		mtx_unlock(IOAT_REFLK);
1822289907Scem}
1823289907Scem
1824289907Scemstatic inline void
1825289907Scemioat_put(struct ioat_softc *ioat, enum ioat_ref_kind kind)
1826289907Scem{
1827289907Scem
1828289907Scem	ioat_putn(ioat, 1, kind);
1829289907Scem}
1830289907Scem
1831289907Scemstatic void
1832290131Scemioat_drain_locked(struct ioat_softc *ioat)
1833289907Scem{
1834289907Scem
1835290131Scem	mtx_assert(IOAT_REFLK, MA_OWNED);
1836289907Scem	while (ioat->refcnt > 0)
1837289907Scem		msleep(IOAT_REFLK, IOAT_REFLK, 0, "ioat_drain", 0);
1838289907Scem}
1839