ioat.c revision 293221
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 293221 2016-01-05 20:42:19Z 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
739292413Scemioat_get_hwversion(bus_dmaengine_t dmaengine)
740292413Scem{
741292413Scem	struct ioat_softc *ioat;
742292413Scem
743292413Scem	ioat = to_ioat_softc(dmaengine);
744292413Scem	return (ioat->version);
745292413Scem}
746292413Scem
747293221Scemsize_t
748293221Scemioat_get_max_io_size(bus_dmaengine_t dmaengine)
749293221Scem{
750293221Scem	struct ioat_softc *ioat;
751293221Scem
752293221Scem	ioat = to_ioat_softc(dmaengine);
753293221Scem	return (ioat->max_xfer_size);
754293221Scem}
755293221Scem
756292413Scemint
757292228Scemioat_set_interrupt_coalesce(bus_dmaengine_t dmaengine, uint16_t delay)
758292228Scem{
759292228Scem	struct ioat_softc *ioat;
760292228Scem
761292228Scem	ioat = to_ioat_softc(dmaengine);
762292228Scem	if (!ioat->intrdelay_supported)
763292228Scem		return (ENODEV);
764292228Scem	if (delay > ioat->intrdelay_max)
765292228Scem		return (ERANGE);
766292228Scem
767292228Scem	ioat_write_2(ioat, IOAT_INTRDELAY_OFFSET, delay);
768292228Scem	ioat->cached_intrdelay =
769292228Scem	    ioat_read_2(ioat, IOAT_INTRDELAY_OFFSET) & IOAT_INTRDELAY_US_MASK;
770292228Scem	return (0);
771292228Scem}
772292228Scem
773292228Scemuint16_t
774292228Scemioat_get_max_coalesce_period(bus_dmaengine_t dmaengine)
775292228Scem{
776292228Scem	struct ioat_softc *ioat;
777292228Scem
778292228Scem	ioat = to_ioat_softc(dmaengine);
779292228Scem	return (ioat->intrdelay_max);
780292228Scem}
781292228Scem
782289907Scemvoid
783287117Scemioat_acquire(bus_dmaengine_t dmaengine)
784287117Scem{
785287117Scem	struct ioat_softc *ioat;
786287117Scem
787287117Scem	ioat = to_ioat_softc(dmaengine);
788287117Scem	mtx_lock(&ioat->submit_lock);
789289979Scem	CTR0(KTR_IOAT, __func__);
790287117Scem}
791287117Scem
792287117Scemvoid
793287117Scemioat_release(bus_dmaengine_t dmaengine)
794287117Scem{
795287117Scem	struct ioat_softc *ioat;
796287117Scem
797289776Scem	ioat = to_ioat_softc(dmaengine);
798289979Scem	CTR0(KTR_IOAT, __func__);
799289982Scem	ioat_write_2(ioat, IOAT_DMACOUNT_OFFSET, (uint16_t)ioat->hw_head);
800287117Scem	mtx_unlock(&ioat->submit_lock);
801287117Scem}
802287117Scem
803290020Scemstatic struct ioat_descriptor *
804290020Scemioat_op_generic(struct ioat_softc *ioat, uint8_t op,
805290020Scem    uint32_t size, uint64_t src, uint64_t dst,
806290020Scem    bus_dmaengine_callback_t callback_fn, void *callback_arg,
807290020Scem    uint32_t flags)
808287117Scem{
809290020Scem	struct ioat_generic_hw_descriptor *hw_desc;
810287117Scem	struct ioat_descriptor *desc;
811289982Scem	int mflags;
812287117Scem
813290020Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
814290020Scem
815287117Scem	KASSERT((flags & ~DMA_ALL_FLAGS) == 0, ("Unrecognized flag(s): %#x",
816287117Scem		flags & ~DMA_ALL_FLAGS));
817289982Scem	if ((flags & DMA_NO_WAIT) != 0)
818289982Scem		mflags = M_NOWAIT;
819289982Scem	else
820289982Scem		mflags = M_WAITOK;
821287117Scem
822290020Scem	if (size > ioat->max_xfer_size) {
823290020Scem		ioat_log_message(0, "%s: max_xfer_size = %d, requested = %u\n",
824290020Scem		    __func__, ioat->max_xfer_size, (unsigned)size);
825290020Scem		return (NULL);
826290020Scem	}
827287117Scem
828289982Scem	if (ioat_reserve_space(ioat, 1, mflags) != 0)
829287117Scem		return (NULL);
830287117Scem
831287117Scem	desc = ioat_get_ring_entry(ioat, ioat->head);
832290020Scem	hw_desc = desc->u.generic;
833287117Scem
834287117Scem	hw_desc->u.control_raw = 0;
835290020Scem	hw_desc->u.control_generic.op = op;
836290020Scem	hw_desc->u.control_generic.completion_update = 1;
837287117Scem
838287117Scem	if ((flags & DMA_INT_EN) != 0)
839290020Scem		hw_desc->u.control_generic.int_enable = 1;
840287117Scem
841290020Scem	hw_desc->size = size;
842290020Scem	hw_desc->src_addr = src;
843290020Scem	hw_desc->dest_addr = dst;
844287117Scem
845287117Scem	desc->bus_dmadesc.callback_fn = callback_fn;
846287117Scem	desc->bus_dmadesc.callback_arg = callback_arg;
847290020Scem	return (desc);
848290020Scem}
849287117Scem
850290020Scemstruct bus_dmadesc *
851290020Scemioat_null(bus_dmaengine_t dmaengine, bus_dmaengine_callback_t callback_fn,
852290020Scem    void *callback_arg, uint32_t flags)
853290020Scem{
854290020Scem	struct ioat_dma_hw_descriptor *hw_desc;
855290020Scem	struct ioat_descriptor *desc;
856290020Scem	struct ioat_softc *ioat;
857290020Scem
858290020Scem	CTR0(KTR_IOAT, __func__);
859290020Scem	ioat = to_ioat_softc(dmaengine);
860290020Scem
861290020Scem	desc = ioat_op_generic(ioat, IOAT_OP_COPY, 8, 0, 0, callback_fn,
862290020Scem	    callback_arg, flags);
863290020Scem	if (desc == NULL)
864290020Scem		return (NULL);
865290020Scem
866290020Scem	hw_desc = desc->u.dma;
867290020Scem	hw_desc->u.control.null = 1;
868287117Scem	ioat_submit_single(ioat);
869287117Scem	return (&desc->bus_dmadesc);
870287117Scem}
871287117Scem
872287117Scemstruct bus_dmadesc *
873287117Scemioat_copy(bus_dmaengine_t dmaengine, bus_addr_t dst,
874287117Scem    bus_addr_t src, bus_size_t len, bus_dmaengine_callback_t callback_fn,
875287117Scem    void *callback_arg, uint32_t flags)
876287117Scem{
877290020Scem	struct ioat_dma_hw_descriptor *hw_desc;
878287117Scem	struct ioat_descriptor *desc;
879287117Scem	struct ioat_softc *ioat;
880287117Scem
881290020Scem	CTR0(KTR_IOAT, __func__);
882287117Scem	ioat = to_ioat_softc(dmaengine);
883287117Scem
884290020Scem	if (((src | dst) & (0xffffull << 48)) != 0) {
885290020Scem		ioat_log_message(0, "%s: High 16 bits of src/dst invalid\n",
886290020Scem		    __func__);
887287117Scem		return (NULL);
888287117Scem	}
889287117Scem
890290020Scem	desc = ioat_op_generic(ioat, IOAT_OP_COPY, len, src, dst, callback_fn,
891290020Scem	    callback_arg, flags);
892290020Scem	if (desc == NULL)
893287117Scem		return (NULL);
894287117Scem
895287117Scem	hw_desc = desc->u.dma;
896287117Scem	if (g_ioat_debug_level >= 3)
897287117Scem		dump_descriptor(hw_desc);
898287117Scem
899287117Scem	ioat_submit_single(ioat);
900287117Scem	return (&desc->bus_dmadesc);
901287117Scem}
902287117Scem
903290021Scemstruct bus_dmadesc *
904292031Scemioat_copy_8k_aligned(bus_dmaengine_t dmaengine, bus_addr_t dst1,
905292031Scem    bus_addr_t dst2, bus_addr_t src1, bus_addr_t src2,
906292031Scem    bus_dmaengine_callback_t callback_fn, void *callback_arg, uint32_t flags)
907292031Scem{
908292031Scem	struct ioat_dma_hw_descriptor *hw_desc;
909292031Scem	struct ioat_descriptor *desc;
910292031Scem	struct ioat_softc *ioat;
911292031Scem
912292031Scem	CTR0(KTR_IOAT, __func__);
913292031Scem	ioat = to_ioat_softc(dmaengine);
914292031Scem
915292031Scem	if (((src1 | src2 | dst1 | dst2) & (0xffffull << 48)) != 0) {
916292031Scem		ioat_log_message(0, "%s: High 16 bits of src/dst invalid\n",
917292031Scem		    __func__);
918292031Scem		return (NULL);
919292031Scem	}
920292031Scem	if (((src1 | src2 | dst1 | dst2) & PAGE_MASK) != 0) {
921292031Scem		ioat_log_message(0, "%s: Addresses must be page-aligned\n",
922292031Scem		    __func__);
923292031Scem		return (NULL);
924292031Scem	}
925292031Scem
926292031Scem	desc = ioat_op_generic(ioat, IOAT_OP_COPY, 2 * PAGE_SIZE, src1, dst1,
927292031Scem	    callback_fn, callback_arg, flags);
928292031Scem	if (desc == NULL)
929292031Scem		return (NULL);
930292031Scem
931292031Scem	hw_desc = desc->u.dma;
932292031Scem	if (src2 != src1 + PAGE_SIZE) {
933292031Scem		hw_desc->u.control.src_page_break = 1;
934292031Scem		hw_desc->next_src_addr = src2;
935292031Scem	}
936292031Scem	if (dst2 != dst1 + PAGE_SIZE) {
937292031Scem		hw_desc->u.control.dest_page_break = 1;
938292031Scem		hw_desc->next_dest_addr = dst2;
939292031Scem	}
940292031Scem
941292031Scem	if (g_ioat_debug_level >= 3)
942292031Scem		dump_descriptor(hw_desc);
943292031Scem
944292031Scem	ioat_submit_single(ioat);
945292031Scem	return (&desc->bus_dmadesc);
946292031Scem}
947292031Scem
948292031Scemstruct bus_dmadesc *
949290021Scemioat_blockfill(bus_dmaengine_t dmaengine, bus_addr_t dst, uint64_t fillpattern,
950290021Scem    bus_size_t len, bus_dmaengine_callback_t callback_fn, void *callback_arg,
951290021Scem    uint32_t flags)
952290021Scem{
953290021Scem	struct ioat_fill_hw_descriptor *hw_desc;
954290021Scem	struct ioat_descriptor *desc;
955290021Scem	struct ioat_softc *ioat;
956290021Scem
957290021Scem	CTR0(KTR_IOAT, __func__);
958290021Scem	ioat = to_ioat_softc(dmaengine);
959290021Scem
960290087Scem	if ((ioat->capabilities & IOAT_DMACAP_BFILL) == 0) {
961290087Scem		ioat_log_message(0, "%s: Device lacks BFILL capability\n",
962290087Scem		    __func__);
963290087Scem		return (NULL);
964290087Scem	}
965290087Scem
966290021Scem	if ((dst & (0xffffull << 48)) != 0) {
967290021Scem		ioat_log_message(0, "%s: High 16 bits of dst invalid\n",
968290021Scem		    __func__);
969290021Scem		return (NULL);
970290021Scem	}
971290021Scem
972290021Scem	desc = ioat_op_generic(ioat, IOAT_OP_FILL, len, fillpattern, dst,
973290021Scem	    callback_fn, callback_arg, flags);
974290021Scem	if (desc == NULL)
975290021Scem		return (NULL);
976290021Scem
977290021Scem	hw_desc = desc->u.fill;
978290021Scem	if (g_ioat_debug_level >= 3)
979290021Scem		dump_descriptor(hw_desc);
980290021Scem
981290021Scem	ioat_submit_single(ioat);
982290021Scem	return (&desc->bus_dmadesc);
983290021Scem}
984290021Scem
985287117Scem/*
986287117Scem * Ring Management
987287117Scem */
988287117Scemstatic inline uint32_t
989287117Scemioat_get_active(struct ioat_softc *ioat)
990287117Scem{
991287117Scem
992287117Scem	return ((ioat->head - ioat->tail) & ((1 << ioat->ring_size_order) - 1));
993287117Scem}
994287117Scem
995287117Scemstatic inline uint32_t
996287117Scemioat_get_ring_space(struct ioat_softc *ioat)
997287117Scem{
998287117Scem
999287117Scem	return ((1 << ioat->ring_size_order) - ioat_get_active(ioat) - 1);
1000287117Scem}
1001287117Scem
1002287117Scemstatic struct ioat_descriptor *
1003289982Scemioat_alloc_ring_entry(struct ioat_softc *ioat, int mflags)
1004287117Scem{
1005290020Scem	struct ioat_generic_hw_descriptor *hw_desc;
1006287117Scem	struct ioat_descriptor *desc;
1007289982Scem	int error, busdmaflag;
1008287117Scem
1009289905Scem	error = ENOMEM;
1010289905Scem	hw_desc = NULL;
1011289905Scem
1012289982Scem	if ((mflags & M_WAITOK) != 0)
1013289982Scem		busdmaflag = BUS_DMA_WAITOK;
1014289982Scem	else
1015289982Scem		busdmaflag = BUS_DMA_NOWAIT;
1016289982Scem
1017289982Scem	desc = malloc(sizeof(*desc), M_IOAT, mflags);
1018287117Scem	if (desc == NULL)
1019289905Scem		goto out;
1020287117Scem
1021289905Scem	bus_dmamem_alloc(ioat->hw_desc_tag, (void **)&hw_desc,
1022289982Scem	    BUS_DMA_ZERO | busdmaflag, &ioat->hw_desc_map);
1023289905Scem	if (hw_desc == NULL)
1024289905Scem		goto out;
1025289905Scem
1026290229Scem	memset(&desc->bus_dmadesc, 0, sizeof(desc->bus_dmadesc));
1027290020Scem	desc->u.generic = hw_desc;
1028289905Scem
1029289905Scem	error = bus_dmamap_load(ioat->hw_desc_tag, ioat->hw_desc_map, hw_desc,
1030289905Scem	    sizeof(*hw_desc), ioat_dmamap_cb, &desc->hw_desc_bus_addr,
1031289982Scem	    busdmaflag);
1032289905Scem	if (error)
1033289905Scem		goto out;
1034289905Scem
1035289905Scemout:
1036289905Scem	if (error) {
1037289905Scem		ioat_free_ring_entry(ioat, desc);
1038287117Scem		return (NULL);
1039287117Scem	}
1040287117Scem	return (desc);
1041287117Scem}
1042287117Scem
1043287117Scemstatic void
1044287117Scemioat_free_ring_entry(struct ioat_softc *ioat, struct ioat_descriptor *desc)
1045287117Scem{
1046287117Scem
1047287117Scem	if (desc == NULL)
1048287117Scem		return;
1049287117Scem
1050290020Scem	if (desc->u.generic)
1051290020Scem		bus_dmamem_free(ioat->hw_desc_tag, desc->u.generic,
1052287117Scem		    ioat->hw_desc_map);
1053287117Scem	free(desc, M_IOAT);
1054287117Scem}
1055287117Scem
1056289982Scem/*
1057289982Scem * Reserves space in this IOAT descriptor ring by ensuring enough slots remain
1058289982Scem * for 'num_descs'.
1059289982Scem *
1060289982Scem * If mflags contains M_WAITOK, blocks until enough space is available.
1061289982Scem *
1062289982Scem * Returns zero on success, or an errno on error.  If num_descs is beyond the
1063289982Scem * maximum ring size, returns EINVAl; if allocation would block and mflags
1064289982Scem * contains M_NOWAIT, returns EAGAIN.
1065289982Scem *
1066289982Scem * Must be called with the submit_lock held; returns with the lock held.  The
1067289982Scem * lock may be dropped to allocate the ring.
1068289982Scem *
1069289982Scem * (The submit_lock is needed to add any entries to the ring, so callers are
1070289982Scem * assured enough room is available.)
1071289982Scem */
1072287117Scemstatic int
1073289982Scemioat_reserve_space(struct ioat_softc *ioat, uint32_t num_descs, int mflags)
1074287117Scem{
1075289982Scem	struct ioat_descriptor **new_ring;
1076289982Scem	uint32_t order;
1077289982Scem	int error;
1078287117Scem
1079289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
1080289982Scem	error = 0;
1081289982Scem
1082289982Scem	if (num_descs < 1 || num_descs > (1 << IOAT_MAX_ORDER)) {
1083289982Scem		error = EINVAL;
1084289982Scem		goto out;
1085289982Scem	}
1086290131Scem	if (ioat->quiescing) {
1087290131Scem		error = ENXIO;
1088290131Scem		goto out;
1089290131Scem	}
1090289982Scem
1091289982Scem	for (;;) {
1092287117Scem		if (ioat_get_ring_space(ioat) >= num_descs)
1093289982Scem			goto out;
1094287117Scem
1095289982Scem		order = ioat->ring_size_order;
1096289982Scem		if (ioat->is_resize_pending || order == IOAT_MAX_ORDER) {
1097289982Scem			if ((mflags & M_WAITOK) != 0) {
1098289982Scem				msleep(&ioat->tail, &ioat->submit_lock, 0,
1099289982Scem				    "ioat_rsz", 0);
1100289982Scem				continue;
1101289982Scem			}
1102287117Scem
1103289982Scem			error = EAGAIN;
1104289982Scem			break;
1105289982Scem		}
1106289982Scem
1107289982Scem		ioat->is_resize_pending = TRUE;
1108289982Scem		for (;;) {
1109289982Scem			mtx_unlock(&ioat->submit_lock);
1110289982Scem
1111289982Scem			new_ring = ioat_prealloc_ring(ioat, 1 << (order + 1),
1112289982Scem			    TRUE, mflags);
1113289982Scem
1114289982Scem			mtx_lock(&ioat->submit_lock);
1115289982Scem			KASSERT(ioat->ring_size_order == order,
1116289982Scem			    ("is_resize_pending should protect order"));
1117289982Scem
1118289982Scem			if (new_ring == NULL) {
1119289982Scem				KASSERT((mflags & M_WAITOK) == 0,
1120289982Scem				    ("allocation failed"));
1121289982Scem				error = EAGAIN;
1122289982Scem				break;
1123289982Scem			}
1124289982Scem
1125289982Scem			error = ring_grow(ioat, order, new_ring);
1126289982Scem			if (error == 0)
1127289982Scem				break;
1128289982Scem		}
1129289982Scem		ioat->is_resize_pending = FALSE;
1130289982Scem		wakeup(&ioat->tail);
1131289982Scem		if (error)
1132289982Scem			break;
1133287117Scem	}
1134289982Scem
1135289982Scemout:
1136289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
1137289982Scem	return (error);
1138287117Scem}
1139287117Scem
1140289982Scemstatic struct ioat_descriptor **
1141289982Scemioat_prealloc_ring(struct ioat_softc *ioat, uint32_t size, boolean_t need_dscr,
1142289982Scem    int mflags)
1143289982Scem{
1144289982Scem	struct ioat_descriptor **ring;
1145289982Scem	uint32_t i;
1146289982Scem	int error;
1147289982Scem
1148289982Scem	KASSERT(size > 0 && powerof2(size), ("bogus size"));
1149289982Scem
1150289982Scem	ring = malloc(size * sizeof(*ring), M_IOAT, M_ZERO | mflags);
1151289982Scem	if (ring == NULL)
1152289982Scem		return (NULL);
1153289982Scem
1154289982Scem	if (need_dscr) {
1155289982Scem		error = ENOMEM;
1156289982Scem		for (i = size / 2; i < size; i++) {
1157289982Scem			ring[i] = ioat_alloc_ring_entry(ioat, mflags);
1158289982Scem			if (ring[i] == NULL)
1159289982Scem				goto out;
1160289982Scem			ring[i]->id = i;
1161289982Scem		}
1162289982Scem	}
1163289982Scem	error = 0;
1164289982Scem
1165289982Scemout:
1166289982Scem	if (error != 0 && ring != NULL) {
1167289982Scem		ioat_free_ring(ioat, size, ring);
1168289982Scem		ring = NULL;
1169289982Scem	}
1170289982Scem	return (ring);
1171289982Scem}
1172289982Scem
1173289982Scemstatic void
1174289982Scemioat_free_ring(struct ioat_softc *ioat, uint32_t size,
1175289982Scem    struct ioat_descriptor **ring)
1176289982Scem{
1177289982Scem	uint32_t i;
1178289982Scem
1179289982Scem	for (i = 0; i < size; i++) {
1180289982Scem		if (ring[i] != NULL)
1181289982Scem			ioat_free_ring_entry(ioat, ring[i]);
1182289982Scem	}
1183289982Scem	free(ring, M_IOAT);
1184289982Scem}
1185289982Scem
1186287117Scemstatic struct ioat_descriptor *
1187287117Scemioat_get_ring_entry(struct ioat_softc *ioat, uint32_t index)
1188287117Scem{
1189287117Scem
1190287117Scem	return (ioat->ring[index % (1 << ioat->ring_size_order)]);
1191287117Scem}
1192287117Scem
1193289982Scemstatic int
1194289982Scemring_grow(struct ioat_softc *ioat, uint32_t oldorder,
1195289982Scem    struct ioat_descriptor **newring)
1196287117Scem{
1197289982Scem	struct ioat_descriptor *tmp, *next;
1198287117Scem	struct ioat_dma_hw_descriptor *hw;
1199289982Scem	uint32_t oldsize, newsize, head, tail, i, end;
1200289982Scem	int error;
1201287117Scem
1202289982Scem	CTR0(KTR_IOAT, __func__);
1203287117Scem
1204289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
1205287117Scem
1206289982Scem	if (oldorder != ioat->ring_size_order || oldorder >= IOAT_MAX_ORDER) {
1207289982Scem		error = EINVAL;
1208289982Scem		goto out;
1209289982Scem	}
1210287117Scem
1211289982Scem	oldsize = (1 << oldorder);
1212289982Scem	newsize = (1 << (oldorder + 1));
1213287117Scem
1214289982Scem	mtx_lock(&ioat->cleanup_lock);
1215287117Scem
1216289982Scem	head = ioat->head & (oldsize - 1);
1217289982Scem	tail = ioat->tail & (oldsize - 1);
1218287117Scem
1219289982Scem	/* Copy old descriptors to new ring */
1220289982Scem	for (i = 0; i < oldsize; i++)
1221289982Scem		newring[i] = ioat->ring[i];
1222287117Scem
1223289982Scem	/*
1224289982Scem	 * If head has wrapped but tail hasn't, we must swap some descriptors
1225289982Scem	 * around so that tail can increment directly to head.
1226289982Scem	 */
1227289982Scem	if (head < tail) {
1228289982Scem		for (i = 0; i <= head; i++) {
1229289982Scem			tmp = newring[oldsize + i];
1230287117Scem
1231289982Scem			newring[oldsize + i] = newring[i];
1232289982Scem			newring[oldsize + i]->id = oldsize + i;
1233287117Scem
1234289982Scem			newring[i] = tmp;
1235289982Scem			newring[i]->id = i;
1236287117Scem		}
1237289982Scem		head += oldsize;
1238289982Scem	}
1239287117Scem
1240289982Scem	KASSERT(head >= tail, ("invariants"));
1241287117Scem
1242289982Scem	/* Head didn't wrap; we only need to link in oldsize..newsize */
1243289982Scem	if (head < oldsize) {
1244289982Scem		i = oldsize - 1;
1245289982Scem		end = newsize;
1246287117Scem	} else {
1247289982Scem		/* Head did wrap; link newhead..newsize and 0..oldhead */
1248289982Scem		i = head;
1249289982Scem		end = newsize + (head - oldsize) + 1;
1250289982Scem	}
1251287117Scem
1252289982Scem	/*
1253289982Scem	 * Fix up hardware ring, being careful not to trample the active
1254289982Scem	 * section (tail -> head).
1255289982Scem	 */
1256289982Scem	for (; i < end; i++) {
1257289982Scem		KASSERT((i & (newsize - 1)) < tail ||
1258289982Scem		    (i & (newsize - 1)) >= head, ("trampling snake"));
1259287117Scem
1260289982Scem		next = newring[(i + 1) & (newsize - 1)];
1261289982Scem		hw = newring[i & (newsize - 1)]->u.dma;
1262287117Scem		hw->next = next->hw_desc_bus_addr;
1263287117Scem	}
1264287117Scem
1265287117Scem	free(ioat->ring, M_IOAT);
1266289982Scem	ioat->ring = newring;
1267289982Scem	ioat->ring_size_order = oldorder + 1;
1268289982Scem	ioat->tail = tail;
1269289982Scem	ioat->head = head;
1270289982Scem	error = 0;
1271287117Scem
1272289982Scem	mtx_unlock(&ioat->cleanup_lock);
1273289982Scemout:
1274289982Scem	if (error)
1275289982Scem		ioat_free_ring(ioat, (1 << (oldorder + 1)), newring);
1276289982Scem	return (error);
1277287117Scem}
1278287117Scem
1279289982Scemstatic int
1280289982Scemring_shrink(struct ioat_softc *ioat, uint32_t oldorder,
1281289982Scem    struct ioat_descriptor **newring)
1282289982Scem{
1283289982Scem	struct ioat_dma_hw_descriptor *hw;
1284289982Scem	struct ioat_descriptor *ent, *next;
1285289982Scem	uint32_t oldsize, newsize, current_idx, new_idx, i;
1286289982Scem	int error;
1287289982Scem
1288289982Scem	CTR0(KTR_IOAT, __func__);
1289289982Scem
1290289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
1291289982Scem
1292289982Scem	if (oldorder != ioat->ring_size_order || oldorder <= IOAT_MIN_ORDER) {
1293289982Scem		error = EINVAL;
1294289982Scem		goto out_unlocked;
1295289982Scem	}
1296289982Scem
1297289982Scem	oldsize = (1 << oldorder);
1298289982Scem	newsize = (1 << (oldorder - 1));
1299289982Scem
1300289982Scem	mtx_lock(&ioat->cleanup_lock);
1301289982Scem
1302289982Scem	/* Can't shrink below current active set! */
1303289982Scem	if (ioat_get_active(ioat) >= newsize) {
1304289982Scem		error = ENOMEM;
1305289982Scem		goto out;
1306289982Scem	}
1307289982Scem
1308289982Scem	/*
1309289982Scem	 * Copy current descriptors to the new ring, dropping the removed
1310289982Scem	 * descriptors.
1311289982Scem	 */
1312289982Scem	for (i = 0; i < newsize; i++) {
1313289982Scem		current_idx = (ioat->tail + i) & (oldsize - 1);
1314289982Scem		new_idx = (ioat->tail + i) & (newsize - 1);
1315289982Scem
1316289982Scem		newring[new_idx] = ioat->ring[current_idx];
1317289982Scem		newring[new_idx]->id = new_idx;
1318289982Scem	}
1319289982Scem
1320289982Scem	/* Free deleted descriptors */
1321289982Scem	for (i = newsize; i < oldsize; i++) {
1322289982Scem		ent = ioat_get_ring_entry(ioat, ioat->tail + i);
1323289982Scem		ioat_free_ring_entry(ioat, ent);
1324289982Scem	}
1325289982Scem
1326289982Scem	/* Fix up hardware ring. */
1327289982Scem	hw = newring[(ioat->tail + newsize - 1) & (newsize - 1)]->u.dma;
1328289982Scem	next = newring[(ioat->tail + newsize) & (newsize - 1)];
1329289982Scem	hw->next = next->hw_desc_bus_addr;
1330289982Scem
1331289982Scem	free(ioat->ring, M_IOAT);
1332289982Scem	ioat->ring = newring;
1333289982Scem	ioat->ring_size_order = oldorder - 1;
1334289982Scem	error = 0;
1335289982Scem
1336289982Scemout:
1337289982Scem	mtx_unlock(&ioat->cleanup_lock);
1338289982Scemout_unlocked:
1339289982Scem	if (error)
1340289982Scem		ioat_free_ring(ioat, (1 << (oldorder - 1)), newring);
1341289982Scem	return (error);
1342289982Scem}
1343289982Scem
1344287117Scemstatic void
1345289910Scemioat_halted_debug(struct ioat_softc *ioat, uint32_t chanerr)
1346289910Scem{
1347289910Scem	struct ioat_descriptor *desc;
1348289910Scem
1349289983Scem	ioat_log_message(0, "Channel halted (%b)\n", (int)chanerr,
1350289983Scem	    IOAT_CHANERR_STR);
1351289910Scem	if (chanerr == 0)
1352289910Scem		return;
1353289910Scem
1354290229Scem	mtx_assert(&ioat->cleanup_lock, MA_OWNED);
1355290229Scem
1356289910Scem	desc = ioat_get_ring_entry(ioat, ioat->tail + 0);
1357289910Scem	dump_descriptor(desc->u.raw);
1358289910Scem
1359289910Scem	desc = ioat_get_ring_entry(ioat, ioat->tail + 1);
1360289910Scem	dump_descriptor(desc->u.raw);
1361289910Scem}
1362289910Scem
1363289910Scemstatic void
1364287117Scemioat_timer_callback(void *arg)
1365287117Scem{
1366289982Scem	struct ioat_descriptor **newring;
1367287117Scem	struct ioat_softc *ioat;
1368290229Scem	uint32_t order;
1369287117Scem
1370287117Scem	ioat = arg;
1371289904Scem	ioat_log_message(1, "%s\n", __func__);
1372287117Scem
1373287117Scem	if (ioat->is_completion_pending) {
1374290229Scem		ioat_process_events(ioat);
1375290229Scem		return;
1376290229Scem	}
1377287117Scem
1378290229Scem	/* Slowly scale the ring down if idle. */
1379290229Scem	mtx_lock(&ioat->submit_lock);
1380290229Scem	order = ioat->ring_size_order;
1381290229Scem	if (ioat->is_resize_pending || order == IOAT_MIN_ORDER) {
1382289982Scem		mtx_unlock(&ioat->submit_lock);
1383290229Scem		goto out;
1384290229Scem	}
1385290229Scem	ioat->is_resize_pending = TRUE;
1386290229Scem	mtx_unlock(&ioat->submit_lock);
1387287117Scem
1388290229Scem	newring = ioat_prealloc_ring(ioat, 1 << (order - 1), FALSE,
1389290229Scem	    M_NOWAIT);
1390287117Scem
1391290229Scem	mtx_lock(&ioat->submit_lock);
1392290229Scem	KASSERT(ioat->ring_size_order == order,
1393290229Scem	    ("resize_pending protects order"));
1394289982Scem
1395290229Scem	if (newring != NULL)
1396290229Scem		ring_shrink(ioat, order, newring);
1397289982Scem
1398290229Scem	ioat->is_resize_pending = FALSE;
1399290229Scem	mtx_unlock(&ioat->submit_lock);
1400287117Scem
1401289982Scemout:
1402290229Scem	if (ioat->ring_size_order > IOAT_MIN_ORDER)
1403290229Scem		callout_reset(&ioat->timer, 10 * hz,
1404290229Scem		    ioat_timer_callback, ioat);
1405287117Scem}
1406287117Scem
1407287117Scem/*
1408287117Scem * Support Functions
1409287117Scem */
1410287117Scemstatic void
1411287117Scemioat_submit_single(struct ioat_softc *ioat)
1412287117Scem{
1413287117Scem
1414289907Scem	ioat_get(ioat, IOAT_ACTIVE_DESCR_REF);
1415287117Scem	atomic_add_rel_int(&ioat->head, 1);
1416289982Scem	atomic_add_rel_int(&ioat->hw_head, 1);
1417287117Scem
1418287117Scem	if (!ioat->is_completion_pending) {
1419287117Scem		ioat->is_completion_pending = TRUE;
1420289904Scem		callout_reset(&ioat->timer, IOAT_INTR_TIMO,
1421289904Scem		    ioat_timer_callback, ioat);
1422287117Scem	}
1423292226Scem
1424292226Scem	ioat->stats.descriptors_submitted++;
1425287117Scem}
1426287117Scem
1427287117Scemstatic int
1428287117Scemioat_reset_hw(struct ioat_softc *ioat)
1429287117Scem{
1430287117Scem	uint64_t status;
1431287117Scem	uint32_t chanerr;
1432289912Scem	unsigned timeout;
1433290131Scem	int error;
1434287117Scem
1435290131Scem	mtx_lock(IOAT_REFLK);
1436290131Scem	ioat->quiescing = TRUE;
1437290131Scem	ioat_drain_locked(ioat);
1438290131Scem	mtx_unlock(IOAT_REFLK);
1439290131Scem
1440287117Scem	status = ioat_get_chansts(ioat);
1441287117Scem	if (is_ioat_active(status) || is_ioat_idle(status))
1442287117Scem		ioat_suspend(ioat);
1443287117Scem
1444287117Scem	/* Wait at most 20 ms */
1445287117Scem	for (timeout = 0; (is_ioat_active(status) || is_ioat_idle(status)) &&
1446287117Scem	    timeout < 20; timeout++) {
1447287117Scem		DELAY(1000);
1448287117Scem		status = ioat_get_chansts(ioat);
1449287117Scem	}
1450290131Scem	if (timeout == 20) {
1451290131Scem		error = ETIMEDOUT;
1452290131Scem		goto out;
1453290131Scem	}
1454287117Scem
1455289912Scem	KASSERT(ioat_get_active(ioat) == 0, ("active after quiesce"));
1456289912Scem
1457287117Scem	chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
1458287117Scem	ioat_write_4(ioat, IOAT_CHANERR_OFFSET, chanerr);
1459287117Scem
1460287117Scem	/*
1461287117Scem	 * IOAT v3 workaround - CHANERRMSK_INT with 3E07h to masks out errors
1462287117Scem	 *  that can cause stability issues for IOAT v3.
1463287117Scem	 */
1464287117Scem	pci_write_config(ioat->device, IOAT_CFG_CHANERRMASK_INT_OFFSET, 0x3e07,
1465287117Scem	    4);
1466287117Scem	chanerr = pci_read_config(ioat->device, IOAT_CFG_CHANERR_INT_OFFSET, 4);
1467287117Scem	pci_write_config(ioat->device, IOAT_CFG_CHANERR_INT_OFFSET, chanerr, 4);
1468287117Scem
1469287414Scem	/*
1470287414Scem	 * BDXDE and BWD models reset MSI-X registers on device reset.
1471287414Scem	 * Save/restore their contents manually.
1472287414Scem	 */
1473289908Scem	if (ioat_model_resets_msix(ioat)) {
1474289908Scem		ioat_log_message(1, "device resets MSI-X registers; saving\n");
1475287414Scem		pci_save_state(ioat->device);
1476289908Scem	}
1477287414Scem
1478287117Scem	ioat_reset(ioat);
1479287117Scem
1480287117Scem	/* Wait at most 20 ms */
1481287117Scem	for (timeout = 0; ioat_reset_pending(ioat) && timeout < 20; timeout++)
1482287117Scem		DELAY(1000);
1483290131Scem	if (timeout == 20) {
1484290131Scem		error = ETIMEDOUT;
1485290131Scem		goto out;
1486290131Scem	}
1487287117Scem
1488289908Scem	if (ioat_model_resets_msix(ioat)) {
1489289908Scem		ioat_log_message(1, "device resets registers; restored\n");
1490287414Scem		pci_restore_state(ioat->device);
1491289908Scem	}
1492287403Scem
1493289912Scem	/* Reset attempts to return the hardware to "halted." */
1494289912Scem	status = ioat_get_chansts(ioat);
1495289912Scem	if (is_ioat_active(status) || is_ioat_idle(status)) {
1496289912Scem		/* So this really shouldn't happen... */
1497289912Scem		ioat_log_message(0, "Device is active after a reset?\n");
1498289912Scem		ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
1499290131Scem		error = 0;
1500290131Scem		goto out;
1501289912Scem	}
1502289912Scem
1503289912Scem	chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
1504290131Scem	if (chanerr != 0) {
1505290229Scem		mtx_lock(&ioat->cleanup_lock);
1506290229Scem		ioat_halted_debug(ioat, chanerr);
1507290229Scem		mtx_unlock(&ioat->cleanup_lock);
1508290131Scem		error = EIO;
1509290131Scem		goto out;
1510290131Scem	}
1511289912Scem
1512289912Scem	/*
1513289912Scem	 * Bring device back online after reset.  Writing CHAINADDR brings the
1514289912Scem	 * device back to active.
1515289912Scem	 *
1516289912Scem	 * The internal ring counter resets to zero, so we have to start over
1517289912Scem	 * at zero as well.
1518289912Scem	 */
1519289982Scem	ioat->tail = ioat->head = ioat->hw_head = 0;
1520289912Scem	ioat->last_seen = 0;
1521289912Scem
1522289912Scem	ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
1523289912Scem	ioat_write_chancmp(ioat, ioat->comp_update_bus_addr);
1524289912Scem	ioat_write_chainaddr(ioat, ioat->ring[0]->hw_desc_bus_addr);
1525290131Scem	error = 0;
1526290131Scem
1527290131Scemout:
1528290131Scem	mtx_lock(IOAT_REFLK);
1529290131Scem	ioat->quiescing = FALSE;
1530290131Scem	mtx_unlock(IOAT_REFLK);
1531290131Scem
1532290131Scem	if (error == 0)
1533290131Scem		error = ioat_start_channel(ioat);
1534290131Scem
1535290131Scem	return (error);
1536287117Scem}
1537287117Scem
1538289908Scemstatic int
1539290229Scemsysctl_handle_chansts(SYSCTL_HANDLER_ARGS)
1540290229Scem{
1541290229Scem	struct ioat_softc *ioat;
1542290229Scem	struct sbuf sb;
1543290229Scem	uint64_t status;
1544290229Scem	int error;
1545290229Scem
1546290229Scem	ioat = arg1;
1547290229Scem
1548290229Scem	status = ioat_get_chansts(ioat) & IOAT_CHANSTS_STATUS;
1549290229Scem
1550290229Scem	sbuf_new_for_sysctl(&sb, NULL, 256, req);
1551290229Scem	switch (status) {
1552290229Scem	case IOAT_CHANSTS_ACTIVE:
1553290229Scem		sbuf_printf(&sb, "ACTIVE");
1554290229Scem		break;
1555290229Scem	case IOAT_CHANSTS_IDLE:
1556290229Scem		sbuf_printf(&sb, "IDLE");
1557290229Scem		break;
1558290229Scem	case IOAT_CHANSTS_SUSPENDED:
1559290229Scem		sbuf_printf(&sb, "SUSPENDED");
1560290229Scem		break;
1561290229Scem	case IOAT_CHANSTS_HALTED:
1562290229Scem		sbuf_printf(&sb, "HALTED");
1563290229Scem		break;
1564290229Scem	case IOAT_CHANSTS_ARMED:
1565290229Scem		sbuf_printf(&sb, "ARMED");
1566290229Scem		break;
1567290229Scem	default:
1568290229Scem		sbuf_printf(&sb, "UNKNOWN");
1569290229Scem		break;
1570290229Scem	}
1571290229Scem	error = sbuf_finish(&sb);
1572290229Scem	sbuf_delete(&sb);
1573290229Scem
1574290229Scem	if (error != 0 || req->newptr == NULL)
1575290229Scem		return (error);
1576290229Scem	return (EINVAL);
1577290229Scem}
1578290229Scem
1579290229Scemstatic int
1580292226Scemsysctl_handle_dpi(SYSCTL_HANDLER_ARGS)
1581292226Scem{
1582292226Scem	struct ioat_softc *ioat;
1583292226Scem	struct sbuf sb;
1584292226Scem#define	PRECISION	"1"
1585292226Scem	const uintmax_t factor = 10;
1586292226Scem	uintmax_t rate;
1587292226Scem	int error;
1588292226Scem
1589292226Scem	ioat = arg1;
1590292226Scem	sbuf_new_for_sysctl(&sb, NULL, 16, req);
1591292226Scem
1592292226Scem	if (ioat->stats.interrupts == 0) {
1593292226Scem		sbuf_printf(&sb, "NaN");
1594292226Scem		goto out;
1595292226Scem	}
1596292226Scem	rate = ioat->stats.descriptors_processed * factor /
1597292226Scem	    ioat->stats.interrupts;
1598292226Scem	sbuf_printf(&sb, "%ju.%." PRECISION "ju", rate / factor,
1599292226Scem	    rate % factor);
1600292226Scem#undef	PRECISION
1601292226Scemout:
1602292226Scem	error = sbuf_finish(&sb);
1603292226Scem	sbuf_delete(&sb);
1604292226Scem	if (error != 0 || req->newptr == NULL)
1605292226Scem		return (error);
1606292226Scem	return (EINVAL);
1607292226Scem}
1608292226Scem
1609292226Scemstatic int
1610290229Scemsysctl_handle_error(SYSCTL_HANDLER_ARGS)
1611290229Scem{
1612290229Scem	struct ioat_descriptor *desc;
1613290229Scem	struct ioat_softc *ioat;
1614290229Scem	int error, arg;
1615290229Scem
1616290229Scem	ioat = arg1;
1617290229Scem
1618290229Scem	arg = 0;
1619290229Scem	error = SYSCTL_OUT(req, &arg, sizeof(arg));
1620290229Scem	if (error != 0 || req->newptr == NULL)
1621290229Scem		return (error);
1622290229Scem
1623290229Scem	error = SYSCTL_IN(req, &arg, sizeof(arg));
1624290229Scem	if (error != 0)
1625290229Scem		return (error);
1626290229Scem
1627290229Scem	if (arg != 0) {
1628290229Scem		ioat_acquire(&ioat->dmaengine);
1629290229Scem		desc = ioat_op_generic(ioat, IOAT_OP_COPY, 1,
1630290229Scem		    0xffff000000000000ull, 0xffff000000000000ull, NULL, NULL,
1631290229Scem		    0);
1632290229Scem		if (desc == NULL)
1633290229Scem			error = ENOMEM;
1634290229Scem		else
1635290229Scem			ioat_submit_single(ioat);
1636290229Scem		ioat_release(&ioat->dmaengine);
1637290229Scem	}
1638290229Scem	return (error);
1639290229Scem}
1640290229Scem
1641290229Scemstatic int
1642289908Scemsysctl_handle_reset(SYSCTL_HANDLER_ARGS)
1643289908Scem{
1644289908Scem	struct ioat_softc *ioat;
1645289908Scem	int error, arg;
1646289908Scem
1647289908Scem	ioat = arg1;
1648289908Scem
1649289908Scem	arg = 0;
1650289908Scem	error = SYSCTL_OUT(req, &arg, sizeof(arg));
1651289908Scem	if (error != 0 || req->newptr == NULL)
1652289908Scem		return (error);
1653289908Scem
1654289908Scem	error = SYSCTL_IN(req, &arg, sizeof(arg));
1655289908Scem	if (error != 0)
1656289908Scem		return (error);
1657289908Scem
1658289908Scem	if (arg != 0)
1659289908Scem		error = ioat_reset_hw(ioat);
1660289908Scem
1661289908Scem	return (error);
1662289908Scem}
1663289908Scem
1664287117Scemstatic void
1665287117Scemdump_descriptor(void *hw_desc)
1666287117Scem{
1667287117Scem	int i, j;
1668287117Scem
1669287117Scem	for (i = 0; i < 2; i++) {
1670287117Scem		for (j = 0; j < 8; j++)
1671287117Scem			printf("%08x ", ((uint32_t *)hw_desc)[i * 8 + j]);
1672287117Scem		printf("\n");
1673287117Scem	}
1674287117Scem}
1675287117Scem
1676287117Scemstatic void
1677287117Scemioat_setup_sysctl(device_t device)
1678287117Scem{
1679292226Scem	struct sysctl_oid_list *par, *statpar, *state, *hammer;
1680289908Scem	struct sysctl_ctx_list *ctx;
1681292226Scem	struct sysctl_oid *tree, *tmp;
1682287117Scem	struct ioat_softc *ioat;
1683287117Scem
1684287117Scem	ioat = DEVICE2SOFTC(device);
1685289908Scem	ctx = device_get_sysctl_ctx(device);
1686289908Scem	tree = device_get_sysctl_tree(device);
1687289908Scem	par = SYSCTL_CHILDREN(tree);
1688287117Scem
1689289980Scem	SYSCTL_ADD_INT(ctx, par, OID_AUTO, "version", CTLFLAG_RD,
1690289980Scem	    &ioat->version, 0, "HW version (0xMM form)");
1691289980Scem	SYSCTL_ADD_UINT(ctx, par, OID_AUTO, "max_xfer_size", CTLFLAG_RD,
1692289980Scem	    &ioat->max_xfer_size, 0, "HW maximum transfer size");
1693292228Scem	SYSCTL_ADD_INT(ctx, par, OID_AUTO, "intrdelay_supported", CTLFLAG_RD,
1694292228Scem	    &ioat->intrdelay_supported, 0, "Is INTRDELAY supported");
1695292228Scem	SYSCTL_ADD_U16(ctx, par, OID_AUTO, "intrdelay_max", CTLFLAG_RD,
1696292228Scem	    &ioat->intrdelay_max, 0,
1697292228Scem	    "Maximum configurable INTRDELAY on this channel (microseconds)");
1698289980Scem
1699292226Scem	tmp = SYSCTL_ADD_NODE(ctx, par, OID_AUTO, "state", CTLFLAG_RD, NULL,
1700292226Scem	    "IOAT channel internal state");
1701292226Scem	state = SYSCTL_CHILDREN(tmp);
1702292226Scem
1703292226Scem	SYSCTL_ADD_UINT(ctx, state, OID_AUTO, "ring_size_order", CTLFLAG_RD,
1704289982Scem	    &ioat->ring_size_order, 0, "SW descriptor ring size order");
1705292226Scem	SYSCTL_ADD_UINT(ctx, state, OID_AUTO, "head", CTLFLAG_RD, &ioat->head,
1706292226Scem	    0, "SW descriptor head pointer index");
1707292226Scem	SYSCTL_ADD_UINT(ctx, state, OID_AUTO, "tail", CTLFLAG_RD, &ioat->tail,
1708292226Scem	    0, "SW descriptor tail pointer index");
1709292226Scem	SYSCTL_ADD_UINT(ctx, state, OID_AUTO, "hw_head", CTLFLAG_RD,
1710289982Scem	    &ioat->hw_head, 0, "HW DMACOUNT");
1711289908Scem
1712292226Scem	SYSCTL_ADD_UQUAD(ctx, state, OID_AUTO, "last_completion", CTLFLAG_RD,
1713289980Scem	    ioat->comp_update, "HW addr of last completion");
1714289980Scem
1715292226Scem	SYSCTL_ADD_INT(ctx, state, OID_AUTO, "is_resize_pending", CTLFLAG_RD,
1716289980Scem	    &ioat->is_resize_pending, 0, "resize pending");
1717292226Scem	SYSCTL_ADD_INT(ctx, state, OID_AUTO, "is_completion_pending",
1718292226Scem	    CTLFLAG_RD, &ioat->is_completion_pending, 0, "completion pending");
1719292226Scem	SYSCTL_ADD_INT(ctx, state, OID_AUTO, "is_reset_pending", CTLFLAG_RD,
1720289980Scem	    &ioat->is_reset_pending, 0, "reset pending");
1721292226Scem	SYSCTL_ADD_INT(ctx, state, OID_AUTO, "is_channel_running", CTLFLAG_RD,
1722289980Scem	    &ioat->is_channel_running, 0, "channel running");
1723289980Scem
1724292226Scem	SYSCTL_ADD_PROC(ctx, state, OID_AUTO, "chansts",
1725292226Scem	    CTLTYPE_STRING | CTLFLAG_RD, ioat, 0, sysctl_handle_chansts, "A",
1726292226Scem	    "String of the channel status");
1727292226Scem
1728292228Scem	SYSCTL_ADD_U16(ctx, state, OID_AUTO, "intrdelay", CTLFLAG_RD,
1729292228Scem	    &ioat->cached_intrdelay, 0,
1730292228Scem	    "Current INTRDELAY on this channel (cached, microseconds)");
1731292228Scem
1732292226Scem	tmp = SYSCTL_ADD_NODE(ctx, par, OID_AUTO, "hammer", CTLFLAG_RD, NULL,
1733292226Scem	    "Big hammers (mostly for testing)");
1734292226Scem	hammer = SYSCTL_CHILDREN(tmp);
1735292226Scem
1736292226Scem	SYSCTL_ADD_PROC(ctx, hammer, OID_AUTO, "force_hw_reset",
1737289908Scem	    CTLTYPE_INT | CTLFLAG_RW, ioat, 0, sysctl_handle_reset, "I",
1738289908Scem	    "Set to non-zero to reset the hardware");
1739292226Scem	SYSCTL_ADD_PROC(ctx, hammer, OID_AUTO, "force_hw_error",
1740290229Scem	    CTLTYPE_INT | CTLFLAG_RW, ioat, 0, sysctl_handle_error, "I",
1741290229Scem	    "Set to non-zero to inject a recoverable hardware error");
1742292226Scem
1743292226Scem	tmp = SYSCTL_ADD_NODE(ctx, par, OID_AUTO, "stats", CTLFLAG_RD, NULL,
1744292226Scem	    "IOAT channel statistics");
1745292226Scem	statpar = SYSCTL_CHILDREN(tmp);
1746292226Scem
1747292226Scem	SYSCTL_ADD_UQUAD(ctx, statpar, OID_AUTO, "interrupts", CTLFLAG_RW,
1748292226Scem	    &ioat->stats.interrupts,
1749292226Scem	    "Number of interrupts processed on this channel");
1750292226Scem	SYSCTL_ADD_UQUAD(ctx, statpar, OID_AUTO, "descriptors", CTLFLAG_RW,
1751292226Scem	    &ioat->stats.descriptors_processed,
1752292226Scem	    "Number of descriptors processed on this channel");
1753292226Scem	SYSCTL_ADD_UQUAD(ctx, statpar, OID_AUTO, "submitted", CTLFLAG_RW,
1754292226Scem	    &ioat->stats.descriptors_submitted,
1755292226Scem	    "Number of descriptors submitted to this channel");
1756292226Scem	SYSCTL_ADD_UQUAD(ctx, statpar, OID_AUTO, "errored", CTLFLAG_RW,
1757292226Scem	    &ioat->stats.descriptors_error,
1758292226Scem	    "Number of descriptors failed by channel errors");
1759292226Scem	SYSCTL_ADD_U32(ctx, statpar, OID_AUTO, "halts", CTLFLAG_RW,
1760292226Scem	    &ioat->stats.channel_halts, 0,
1761292226Scem	    "Number of times the channel has halted");
1762292226Scem	SYSCTL_ADD_U32(ctx, statpar, OID_AUTO, "last_halt_chanerr", CTLFLAG_RW,
1763292226Scem	    &ioat->stats.last_halt_chanerr, 0,
1764292226Scem	    "The raw CHANERR when the channel was last halted");
1765292226Scem
1766292226Scem	SYSCTL_ADD_PROC(ctx, statpar, OID_AUTO, "desc_per_interrupt",
1767292226Scem	    CTLTYPE_STRING | CTLFLAG_RD, ioat, 0, sysctl_handle_dpi, "A",
1768292226Scem	    "Descriptors per interrupt");
1769287117Scem}
1770289907Scem
1771289907Scemstatic inline struct ioat_softc *
1772289907Scemioat_get(struct ioat_softc *ioat, enum ioat_ref_kind kind)
1773289907Scem{
1774289907Scem	uint32_t old;
1775289907Scem
1776289907Scem	KASSERT(kind < IOAT_NUM_REF_KINDS, ("bogus"));
1777289907Scem
1778289907Scem	old = atomic_fetchadd_32(&ioat->refcnt, 1);
1779289907Scem	KASSERT(old < UINT32_MAX, ("refcnt overflow"));
1780289907Scem
1781289907Scem#ifdef INVARIANTS
1782289907Scem	old = atomic_fetchadd_32(&ioat->refkinds[kind], 1);
1783289907Scem	KASSERT(old < UINT32_MAX, ("refcnt kind overflow"));
1784289907Scem#endif
1785289907Scem
1786289907Scem	return (ioat);
1787289907Scem}
1788289907Scem
1789289907Scemstatic inline void
1790289907Scemioat_putn(struct ioat_softc *ioat, uint32_t n, enum ioat_ref_kind kind)
1791289907Scem{
1792290229Scem
1793290229Scem	_ioat_putn(ioat, n, kind, FALSE);
1794290229Scem}
1795290229Scem
1796290229Scemstatic inline void
1797290229Scemioat_putn_locked(struct ioat_softc *ioat, uint32_t n, enum ioat_ref_kind kind)
1798290229Scem{
1799290229Scem
1800290229Scem	_ioat_putn(ioat, n, kind, TRUE);
1801290229Scem}
1802290229Scem
1803290229Scemstatic inline void
1804290229Scem_ioat_putn(struct ioat_softc *ioat, uint32_t n, enum ioat_ref_kind kind,
1805290229Scem    boolean_t locked)
1806290229Scem{
1807289907Scem	uint32_t old;
1808289907Scem
1809289907Scem	KASSERT(kind < IOAT_NUM_REF_KINDS, ("bogus"));
1810289907Scem
1811289907Scem	if (n == 0)
1812289907Scem		return;
1813289907Scem
1814289907Scem#ifdef INVARIANTS
1815289907Scem	old = atomic_fetchadd_32(&ioat->refkinds[kind], -n);
1816289907Scem	KASSERT(old >= n, ("refcnt kind underflow"));
1817289907Scem#endif
1818289907Scem
1819289907Scem	/* Skip acquiring the lock if resulting refcnt > 0. */
1820289907Scem	for (;;) {
1821289907Scem		old = ioat->refcnt;
1822289907Scem		if (old <= n)
1823289907Scem			break;
1824289907Scem		if (atomic_cmpset_32(&ioat->refcnt, old, old - n))
1825289907Scem			return;
1826289907Scem	}
1827289907Scem
1828290229Scem	if (locked)
1829290229Scem		mtx_assert(IOAT_REFLK, MA_OWNED);
1830290229Scem	else
1831290229Scem		mtx_lock(IOAT_REFLK);
1832290229Scem
1833289907Scem	old = atomic_fetchadd_32(&ioat->refcnt, -n);
1834289907Scem	KASSERT(old >= n, ("refcnt error"));
1835289907Scem
1836289907Scem	if (old == n)
1837289907Scem		wakeup(IOAT_REFLK);
1838290229Scem	if (!locked)
1839290229Scem		mtx_unlock(IOAT_REFLK);
1840289907Scem}
1841289907Scem
1842289907Scemstatic inline void
1843289907Scemioat_put(struct ioat_softc *ioat, enum ioat_ref_kind kind)
1844289907Scem{
1845289907Scem
1846289907Scem	ioat_putn(ioat, 1, kind);
1847289907Scem}
1848289907Scem
1849289907Scemstatic void
1850290131Scemioat_drain_locked(struct ioat_softc *ioat)
1851289907Scem{
1852289907Scem
1853290131Scem	mtx_assert(IOAT_REFLK, MA_OWNED);
1854289907Scem	while (ioat->refcnt > 0)
1855289907Scem		msleep(IOAT_REFLK, IOAT_REFLK, 0, "ioat_drain", 0);
1856289907Scem}
1857