ioat.c revision 289983
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 289983 2015-10-26 03:30:50Z 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>
41287117Scem#include <sys/sysctl.h>
42287117Scem#include <sys/time.h>
43287117Scem#include <dev/pci/pcireg.h>
44287117Scem#include <dev/pci/pcivar.h>
45287117Scem#include <machine/bus.h>
46287117Scem#include <machine/resource.h>
47287117Scem#include <machine/stdarg.h>
48287117Scem
49287117Scem#include "ioat.h"
50287117Scem#include "ioat_hw.h"
51287117Scem#include "ioat_internal.h"
52287117Scem
53289904Scem#define	IOAT_INTR_TIMO	(hz / 10)
54289907Scem#define	IOAT_REFLK	(&ioat->submit_lock)
55289904Scem
56287117Scemstatic int ioat_probe(device_t device);
57287117Scemstatic int ioat_attach(device_t device);
58287117Scemstatic int ioat_detach(device_t device);
59287403Scemstatic int ioat_setup_intr(struct ioat_softc *ioat);
60287403Scemstatic int ioat_teardown_intr(struct ioat_softc *ioat);
61287117Scemstatic int ioat3_attach(device_t device);
62289912Scemstatic int ioat_start_channel(struct ioat_softc *ioat);
63287117Scemstatic int ioat_map_pci_bar(struct ioat_softc *ioat);
64287117Scemstatic void ioat_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg,
65287117Scem    int error);
66287117Scemstatic void ioat_interrupt_handler(void *arg);
67287414Scemstatic boolean_t ioat_model_resets_msix(struct ioat_softc *ioat);
68287117Scemstatic void ioat_process_events(struct ioat_softc *ioat);
69287117Scemstatic inline uint32_t ioat_get_active(struct ioat_softc *ioat);
70287117Scemstatic inline uint32_t ioat_get_ring_space(struct ioat_softc *ioat);
71289982Scemstatic void ioat_free_ring(struct ioat_softc *, uint32_t size,
72289982Scem    struct ioat_descriptor **);
73287117Scemstatic void ioat_free_ring_entry(struct ioat_softc *ioat,
74287117Scem    struct ioat_descriptor *desc);
75289982Scemstatic struct ioat_descriptor *ioat_alloc_ring_entry(struct ioat_softc *,
76289982Scem    int mflags);
77289982Scemstatic int ioat_reserve_space(struct ioat_softc *, uint32_t, int mflags);
78289907Scemstatic struct ioat_descriptor *ioat_get_ring_entry(struct ioat_softc *ioat,
79287117Scem    uint32_t index);
80289982Scemstatic struct ioat_descriptor **ioat_prealloc_ring(struct ioat_softc *,
81289982Scem    uint32_t size, boolean_t need_dscr, int mflags);
82289982Scemstatic int ring_grow(struct ioat_softc *, uint32_t oldorder,
83289982Scem    struct ioat_descriptor **);
84289982Scemstatic int ring_shrink(struct ioat_softc *, uint32_t oldorder,
85289982Scem    struct ioat_descriptor **);
86287117Scemstatic void ioat_timer_callback(void *arg);
87287117Scemstatic void dump_descriptor(void *hw_desc);
88287117Scemstatic void ioat_submit_single(struct ioat_softc *ioat);
89287117Scemstatic void ioat_comp_update_map(void *arg, bus_dma_segment_t *seg, int nseg,
90287117Scem    int error);
91287117Scemstatic int ioat_reset_hw(struct ioat_softc *ioat);
92287117Scemstatic void ioat_setup_sysctl(device_t device);
93289908Scemstatic int sysctl_handle_reset(SYSCTL_HANDLER_ARGS);
94289907Scemstatic inline struct ioat_softc *ioat_get(struct ioat_softc *,
95289907Scem    enum ioat_ref_kind);
96289907Scemstatic inline void ioat_put(struct ioat_softc *, enum ioat_ref_kind);
97289907Scemstatic inline void ioat_putn(struct ioat_softc *, uint32_t,
98289907Scem    enum ioat_ref_kind);
99289907Scemstatic void ioat_drain(struct ioat_softc *);
100287117Scem
101289776Scem#define	ioat_log_message(v, ...) do {					\
102289776Scem	if ((v) <= g_ioat_debug_level) {				\
103289776Scem		device_printf(ioat->device, __VA_ARGS__);		\
104289776Scem	}								\
105289776Scem} while (0)
106289776Scem
107287117ScemMALLOC_DEFINE(M_IOAT, "ioat", "ioat driver memory allocations");
108287117ScemSYSCTL_NODE(_hw, OID_AUTO, ioat, CTLFLAG_RD, 0, "ioat node");
109287117Scem
110287117Scemstatic int g_force_legacy_interrupts;
111287117ScemSYSCTL_INT(_hw_ioat, OID_AUTO, force_legacy_interrupts, CTLFLAG_RDTUN,
112287117Scem    &g_force_legacy_interrupts, 0, "Set to non-zero to force MSI-X disabled");
113287117Scem
114289776Scemint g_ioat_debug_level = 0;
115287117ScemSYSCTL_INT(_hw_ioat, OID_AUTO, debug_level, CTLFLAG_RWTUN, &g_ioat_debug_level,
116287117Scem    0, "Set log level (0-3) for ioat(4). Higher is more verbose.");
117287117Scem
118287117Scem/*
119287117Scem * OS <-> Driver interface structures
120287117Scem */
121287117Scemstatic device_method_t ioat_pci_methods[] = {
122287117Scem	/* Device interface */
123287117Scem	DEVMETHOD(device_probe,     ioat_probe),
124287117Scem	DEVMETHOD(device_attach,    ioat_attach),
125287117Scem	DEVMETHOD(device_detach,    ioat_detach),
126287117Scem	{ 0, 0 }
127287117Scem};
128287117Scem
129287117Scemstatic driver_t ioat_pci_driver = {
130287117Scem	"ioat",
131287117Scem	ioat_pci_methods,
132287117Scem	sizeof(struct ioat_softc),
133287117Scem};
134287117Scem
135287117Scemstatic devclass_t ioat_devclass;
136287117ScemDRIVER_MODULE(ioat, pci, ioat_pci_driver, ioat_devclass, 0, 0);
137287117Scem
138287117Scem/*
139287117Scem * Private data structures
140287117Scem */
141287117Scemstatic struct ioat_softc *ioat_channel[IOAT_MAX_CHANNELS];
142287117Scemstatic int ioat_channel_index = 0;
143287117ScemSYSCTL_INT(_hw_ioat, OID_AUTO, channels, CTLFLAG_RD, &ioat_channel_index, 0,
144287117Scem    "Number of IOAT channels attached");
145287117Scem
146287117Scemstatic struct _pcsid
147287117Scem{
148287117Scem	u_int32_t   type;
149287117Scem	const char  *desc;
150287117Scem} pci_ids[] = {
151287117Scem	{ 0x34308086, "TBG IOAT Ch0" },
152287117Scem	{ 0x34318086, "TBG IOAT Ch1" },
153287117Scem	{ 0x34328086, "TBG IOAT Ch2" },
154287117Scem	{ 0x34338086, "TBG IOAT Ch3" },
155287117Scem	{ 0x34298086, "TBG IOAT Ch4" },
156287117Scem	{ 0x342a8086, "TBG IOAT Ch5" },
157287117Scem	{ 0x342b8086, "TBG IOAT Ch6" },
158287117Scem	{ 0x342c8086, "TBG IOAT Ch7" },
159287117Scem
160287117Scem	{ 0x37108086, "JSF IOAT Ch0" },
161287117Scem	{ 0x37118086, "JSF IOAT Ch1" },
162287117Scem	{ 0x37128086, "JSF IOAT Ch2" },
163287117Scem	{ 0x37138086, "JSF IOAT Ch3" },
164287117Scem	{ 0x37148086, "JSF IOAT Ch4" },
165287117Scem	{ 0x37158086, "JSF IOAT Ch5" },
166287117Scem	{ 0x37168086, "JSF IOAT Ch6" },
167287117Scem	{ 0x37178086, "JSF IOAT Ch7" },
168287117Scem	{ 0x37188086, "JSF IOAT Ch0 (RAID)" },
169287117Scem	{ 0x37198086, "JSF IOAT Ch1 (RAID)" },
170287117Scem
171287117Scem	{ 0x3c208086, "SNB IOAT Ch0" },
172287117Scem	{ 0x3c218086, "SNB IOAT Ch1" },
173287117Scem	{ 0x3c228086, "SNB IOAT Ch2" },
174287117Scem	{ 0x3c238086, "SNB IOAT Ch3" },
175287117Scem	{ 0x3c248086, "SNB IOAT Ch4" },
176287117Scem	{ 0x3c258086, "SNB IOAT Ch5" },
177287117Scem	{ 0x3c268086, "SNB IOAT Ch6" },
178287117Scem	{ 0x3c278086, "SNB IOAT Ch7" },
179287117Scem	{ 0x3c2e8086, "SNB IOAT Ch0 (RAID)" },
180287117Scem	{ 0x3c2f8086, "SNB IOAT Ch1 (RAID)" },
181287117Scem
182287117Scem	{ 0x0e208086, "IVB IOAT Ch0" },
183287117Scem	{ 0x0e218086, "IVB IOAT Ch1" },
184287117Scem	{ 0x0e228086, "IVB IOAT Ch2" },
185287117Scem	{ 0x0e238086, "IVB IOAT Ch3" },
186287117Scem	{ 0x0e248086, "IVB IOAT Ch4" },
187287117Scem	{ 0x0e258086, "IVB IOAT Ch5" },
188287117Scem	{ 0x0e268086, "IVB IOAT Ch6" },
189287117Scem	{ 0x0e278086, "IVB IOAT Ch7" },
190287117Scem	{ 0x0e2e8086, "IVB IOAT Ch0 (RAID)" },
191287117Scem	{ 0x0e2f8086, "IVB IOAT Ch1 (RAID)" },
192287117Scem
193287117Scem	{ 0x2f208086, "HSW IOAT Ch0" },
194287117Scem	{ 0x2f218086, "HSW IOAT Ch1" },
195287117Scem	{ 0x2f228086, "HSW IOAT Ch2" },
196287117Scem	{ 0x2f238086, "HSW IOAT Ch3" },
197287117Scem	{ 0x2f248086, "HSW IOAT Ch4" },
198287117Scem	{ 0x2f258086, "HSW IOAT Ch5" },
199287117Scem	{ 0x2f268086, "HSW IOAT Ch6" },
200287117Scem	{ 0x2f278086, "HSW IOAT Ch7" },
201287117Scem	{ 0x2f2e8086, "HSW IOAT Ch0 (RAID)" },
202287117Scem	{ 0x2f2f8086, "HSW IOAT Ch1 (RAID)" },
203287117Scem
204287117Scem	{ 0x0c508086, "BWD IOAT Ch0" },
205287117Scem	{ 0x0c518086, "BWD IOAT Ch1" },
206287117Scem	{ 0x0c528086, "BWD IOAT Ch2" },
207287117Scem	{ 0x0c538086, "BWD IOAT Ch3" },
208287117Scem
209287117Scem	{ 0x6f508086, "BDXDE IOAT Ch0" },
210287117Scem	{ 0x6f518086, "BDXDE IOAT Ch1" },
211287117Scem	{ 0x6f528086, "BDXDE IOAT Ch2" },
212287117Scem	{ 0x6f538086, "BDXDE IOAT Ch3" },
213287117Scem
214287117Scem	{ 0x00000000, NULL           }
215287117Scem};
216287117Scem
217287117Scem/*
218287117Scem * OS <-> Driver linkage functions
219287117Scem */
220287117Scemstatic int
221287117Scemioat_probe(device_t device)
222287117Scem{
223287117Scem	struct _pcsid *ep;
224287117Scem	u_int32_t type;
225287117Scem
226287117Scem	type = pci_get_devid(device);
227287117Scem	for (ep = pci_ids; ep->type; ep++) {
228287117Scem		if (ep->type == type) {
229287117Scem			device_set_desc(device, ep->desc);
230287117Scem			return (0);
231287117Scem		}
232287117Scem	}
233287117Scem	return (ENXIO);
234287117Scem}
235287117Scem
236287117Scemstatic int
237287117Scemioat_attach(device_t device)
238287117Scem{
239287117Scem	struct ioat_softc *ioat;
240287117Scem	int error;
241287117Scem
242287117Scem	ioat = DEVICE2SOFTC(device);
243287117Scem	ioat->device = device;
244287117Scem
245287117Scem	error = ioat_map_pci_bar(ioat);
246287117Scem	if (error != 0)
247287117Scem		goto err;
248287117Scem
249287117Scem	ioat->version = ioat_read_cbver(ioat);
250287117Scem	if (ioat->version < IOAT_VER_3_0) {
251287117Scem		error = ENODEV;
252287117Scem		goto err;
253287117Scem	}
254287117Scem
255287117Scem	error = ioat3_attach(device);
256287117Scem	if (error != 0)
257287117Scem		goto err;
258287117Scem
259287117Scem	error = pci_enable_busmaster(device);
260287117Scem	if (error != 0)
261287117Scem		goto err;
262287117Scem
263289907Scem	error = ioat_setup_intr(ioat);
264289907Scem	if (error != 0)
265289907Scem		goto err;
266289907Scem
267289912Scem	error = ioat_reset_hw(ioat);
268289760Scem	if (error != 0)
269289907Scem		goto err;
270289760Scem
271289760Scem	ioat_process_events(ioat);
272289760Scem	ioat_setup_sysctl(device);
273289760Scem
274287117Scem	ioat_channel[ioat_channel_index++] = ioat;
275289760Scem	ioat_test_attach();
276287117Scem
277287117Scemerr:
278287117Scem	if (error != 0)
279287117Scem		ioat_detach(device);
280287117Scem	return (error);
281287117Scem}
282287117Scem
283287117Scemstatic int
284287117Scemioat_detach(device_t device)
285287117Scem{
286287117Scem	struct ioat_softc *ioat;
287287117Scem
288287117Scem	ioat = DEVICE2SOFTC(device);
289289760Scem
290289760Scem	ioat_test_detach();
291289907Scem	ioat_drain(ioat);
292289904Scem
293289904Scem	ioat_teardown_intr(ioat);
294287117Scem	callout_drain(&ioat->timer);
295287117Scem
296287117Scem	pci_disable_busmaster(device);
297287117Scem
298287117Scem	if (ioat->pci_resource != NULL)
299287117Scem		bus_release_resource(device, SYS_RES_MEMORY,
300287117Scem		    ioat->pci_resource_id, ioat->pci_resource);
301287117Scem
302289982Scem	if (ioat->ring != NULL)
303289982Scem		ioat_free_ring(ioat, 1 << ioat->ring_size_order, ioat->ring);
304287117Scem
305287117Scem	if (ioat->comp_update != NULL) {
306287117Scem		bus_dmamap_unload(ioat->comp_update_tag, ioat->comp_update_map);
307287117Scem		bus_dmamem_free(ioat->comp_update_tag, ioat->comp_update,
308287117Scem		    ioat->comp_update_map);
309287117Scem		bus_dma_tag_destroy(ioat->comp_update_tag);
310287117Scem	}
311287117Scem
312287117Scem	bus_dma_tag_destroy(ioat->hw_desc_tag);
313287117Scem
314287403Scem	return (0);
315287403Scem}
316287403Scem
317287403Scemstatic int
318287403Scemioat_teardown_intr(struct ioat_softc *ioat)
319287403Scem{
320287403Scem
321287117Scem	if (ioat->tag != NULL)
322287403Scem		bus_teardown_intr(ioat->device, ioat->res, ioat->tag);
323287117Scem
324287117Scem	if (ioat->res != NULL)
325287403Scem		bus_release_resource(ioat->device, SYS_RES_IRQ,
326287117Scem		    rman_get_rid(ioat->res), ioat->res);
327287117Scem
328287403Scem	pci_release_msi(ioat->device);
329287117Scem	return (0);
330287117Scem}
331287117Scem
332287117Scemstatic int
333289912Scemioat_start_channel(struct ioat_softc *ioat)
334287117Scem{
335287117Scem	uint64_t status;
336287117Scem	uint32_t chanerr;
337287117Scem	int i;
338287117Scem
339287117Scem	ioat_acquire(&ioat->dmaengine);
340287117Scem	ioat_null(&ioat->dmaengine, NULL, NULL, 0);
341287117Scem	ioat_release(&ioat->dmaengine);
342287117Scem
343287117Scem	for (i = 0; i < 100; i++) {
344287117Scem		DELAY(1);
345287117Scem		status = ioat_get_chansts(ioat);
346287117Scem		if (is_ioat_idle(status))
347287117Scem			return (0);
348287117Scem	}
349287117Scem
350287117Scem	chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
351287117Scem	ioat_log_message(0, "could not start channel: "
352289983Scem	    "status = %#jx error = %b\n", (uintmax_t)status, (int)chanerr,
353289983Scem	    IOAT_CHANERR_STR);
354287117Scem	return (ENXIO);
355287117Scem}
356287117Scem
357287117Scem/*
358287117Scem * Initialize Hardware
359287117Scem */
360287117Scemstatic int
361287117Scemioat3_attach(device_t device)
362287117Scem{
363287117Scem	struct ioat_softc *ioat;
364287117Scem	struct ioat_descriptor **ring;
365287117Scem	struct ioat_descriptor *next;
366287117Scem	struct ioat_dma_hw_descriptor *dma_hw_desc;
367287117Scem	uint32_t capabilities;
368287117Scem	int i, num_descriptors;
369287117Scem	int error;
370287117Scem	uint8_t xfercap;
371287117Scem
372287117Scem	error = 0;
373287117Scem	ioat = DEVICE2SOFTC(device);
374287117Scem	capabilities = ioat_read_dmacapability(ioat);
375287117Scem
376287117Scem	xfercap = ioat_read_xfercap(ioat);
377287117Scem	ioat->max_xfer_size = 1 << xfercap;
378287117Scem
379287117Scem	/* TODO: need to check DCA here if we ever do XOR/PQ */
380287117Scem
381287117Scem	mtx_init(&ioat->submit_lock, "ioat_submit", NULL, MTX_DEF);
382287117Scem	mtx_init(&ioat->cleanup_lock, "ioat_process_events", NULL, MTX_DEF);
383289760Scem	callout_init(&ioat->timer, 1);
384287117Scem
385287117Scem	ioat->is_resize_pending = FALSE;
386287117Scem	ioat->is_completion_pending = FALSE;
387287117Scem	ioat->is_reset_pending = FALSE;
388287117Scem	ioat->is_channel_running = FALSE;
389287117Scem
390287117Scem	bus_dma_tag_create(bus_get_dma_tag(ioat->device), sizeof(uint64_t), 0x0,
391287117Scem	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
392287117Scem	    sizeof(uint64_t), 1, sizeof(uint64_t), 0, NULL, NULL,
393287117Scem	    &ioat->comp_update_tag);
394287117Scem
395287117Scem	error = bus_dmamem_alloc(ioat->comp_update_tag,
396287117Scem	    (void **)&ioat->comp_update, BUS_DMA_ZERO, &ioat->comp_update_map);
397287117Scem	if (ioat->comp_update == NULL)
398287117Scem		return (ENOMEM);
399287117Scem
400287117Scem	error = bus_dmamap_load(ioat->comp_update_tag, ioat->comp_update_map,
401287117Scem	    ioat->comp_update, sizeof(uint64_t), ioat_comp_update_map, ioat,
402287117Scem	    0);
403287117Scem	if (error != 0)
404287117Scem		return (error);
405287117Scem
406287117Scem	ioat->ring_size_order = IOAT_MIN_ORDER;
407287117Scem
408287117Scem	num_descriptors = 1 << ioat->ring_size_order;
409287117Scem
410287117Scem	bus_dma_tag_create(bus_get_dma_tag(ioat->device), 0x40, 0x0,
411287117Scem	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
412287117Scem	    sizeof(struct ioat_dma_hw_descriptor), 1,
413287117Scem	    sizeof(struct ioat_dma_hw_descriptor), 0, NULL, NULL,
414287117Scem	    &ioat->hw_desc_tag);
415287117Scem
416287117Scem	ioat->ring = malloc(num_descriptors * sizeof(*ring), M_IOAT,
417289982Scem	    M_ZERO | M_WAITOK);
418287117Scem	if (ioat->ring == NULL)
419287117Scem		return (ENOMEM);
420287117Scem
421287117Scem	ring = ioat->ring;
422287117Scem	for (i = 0; i < num_descriptors; i++) {
423289982Scem		ring[i] = ioat_alloc_ring_entry(ioat, M_WAITOK);
424287117Scem		if (ring[i] == NULL)
425287117Scem			return (ENOMEM);
426287117Scem
427287117Scem		ring[i]->id = i;
428287117Scem	}
429287117Scem
430287117Scem	for (i = 0; i < num_descriptors - 1; i++) {
431287117Scem		next = ring[i + 1];
432287117Scem		dma_hw_desc = ring[i]->u.dma;
433287117Scem
434287117Scem		dma_hw_desc->next = next->hw_desc_bus_addr;
435287117Scem	}
436287117Scem
437287117Scem	ring[i]->u.dma->next = ring[0]->hw_desc_bus_addr;
438287117Scem
439289982Scem	ioat->head = ioat->hw_head = 0;
440287117Scem	ioat->tail = 0;
441287117Scem	ioat->last_seen = 0;
442287117Scem	return (0);
443287117Scem}
444287117Scem
445287117Scemstatic int
446287117Scemioat_map_pci_bar(struct ioat_softc *ioat)
447287117Scem{
448287117Scem
449287117Scem	ioat->pci_resource_id = PCIR_BAR(0);
450289911Scem	ioat->pci_resource = bus_alloc_resource_any(ioat->device,
451289911Scem	    SYS_RES_MEMORY, &ioat->pci_resource_id, RF_ACTIVE);
452287117Scem
453287117Scem	if (ioat->pci_resource == NULL) {
454287117Scem		ioat_log_message(0, "unable to allocate pci resource\n");
455287117Scem		return (ENODEV);
456287117Scem	}
457287117Scem
458287117Scem	ioat->pci_bus_tag = rman_get_bustag(ioat->pci_resource);
459287117Scem	ioat->pci_bus_handle = rman_get_bushandle(ioat->pci_resource);
460287117Scem	return (0);
461287117Scem}
462287117Scem
463287117Scemstatic void
464287117Scemioat_comp_update_map(void *arg, bus_dma_segment_t *seg, int nseg, int error)
465287117Scem{
466287117Scem	struct ioat_softc *ioat = arg;
467287117Scem
468289912Scem	KASSERT(error == 0, ("%s: error:%d", __func__, error));
469287117Scem	ioat->comp_update_bus_addr = seg[0].ds_addr;
470287117Scem}
471287117Scem
472287117Scemstatic void
473287117Scemioat_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
474287117Scem{
475287117Scem	bus_addr_t *baddr;
476287117Scem
477289912Scem	KASSERT(error == 0, ("%s: error:%d", __func__, error));
478287117Scem	baddr = arg;
479287117Scem	*baddr = segs->ds_addr;
480287117Scem}
481287117Scem
482287117Scem/*
483287117Scem * Interrupt setup and handlers
484287117Scem */
485287117Scemstatic int
486287403Scemioat_setup_intr(struct ioat_softc *ioat)
487287117Scem{
488287117Scem	uint32_t num_vectors;
489287117Scem	int error;
490287117Scem	boolean_t use_msix;
491287117Scem	boolean_t force_legacy_interrupts;
492287117Scem
493287117Scem	use_msix = FALSE;
494287117Scem	force_legacy_interrupts = FALSE;
495287117Scem
496287117Scem	if (!g_force_legacy_interrupts && pci_msix_count(ioat->device) >= 1) {
497287117Scem		num_vectors = 1;
498287117Scem		pci_alloc_msix(ioat->device, &num_vectors);
499287117Scem		if (num_vectors == 1)
500287117Scem			use_msix = TRUE;
501287117Scem	}
502287117Scem
503287117Scem	if (use_msix) {
504287117Scem		ioat->rid = 1;
505287117Scem		ioat->res = bus_alloc_resource_any(ioat->device, SYS_RES_IRQ,
506287117Scem		    &ioat->rid, RF_ACTIVE);
507287117Scem	} else {
508287117Scem		ioat->rid = 0;
509287117Scem		ioat->res = bus_alloc_resource_any(ioat->device, SYS_RES_IRQ,
510287117Scem		    &ioat->rid, RF_SHAREABLE | RF_ACTIVE);
511287117Scem	}
512287117Scem	if (ioat->res == NULL) {
513287117Scem		ioat_log_message(0, "bus_alloc_resource failed\n");
514287117Scem		return (ENOMEM);
515287117Scem	}
516287117Scem
517287117Scem	ioat->tag = NULL;
518287117Scem	error = bus_setup_intr(ioat->device, ioat->res, INTR_MPSAFE |
519287117Scem	    INTR_TYPE_MISC, NULL, ioat_interrupt_handler, ioat, &ioat->tag);
520287117Scem	if (error != 0) {
521287117Scem		ioat_log_message(0, "bus_setup_intr failed\n");
522287117Scem		return (error);
523287117Scem	}
524287117Scem
525287117Scem	ioat_write_intrctrl(ioat, IOAT_INTRCTRL_MASTER_INT_EN);
526287117Scem	return (0);
527287117Scem}
528287117Scem
529287403Scemstatic boolean_t
530287414Scemioat_model_resets_msix(struct ioat_softc *ioat)
531287403Scem{
532287403Scem	u_int32_t pciid;
533287403Scem
534287403Scem	pciid = pci_get_devid(ioat->device);
535287403Scem	switch (pciid) {
536287414Scem		/* BWD: */
537287414Scem	case 0x0c508086:
538287414Scem	case 0x0c518086:
539287414Scem	case 0x0c528086:
540287414Scem	case 0x0c538086:
541287414Scem		/* BDXDE: */
542287403Scem	case 0x6f508086:
543287403Scem	case 0x6f518086:
544287403Scem	case 0x6f528086:
545287403Scem	case 0x6f538086:
546287403Scem		return (TRUE);
547287403Scem	}
548287403Scem
549287403Scem	return (FALSE);
550287403Scem}
551287403Scem
552287117Scemstatic void
553287117Scemioat_interrupt_handler(void *arg)
554287117Scem{
555287117Scem	struct ioat_softc *ioat = arg;
556287117Scem
557287117Scem	ioat_process_events(ioat);
558287117Scem}
559287117Scem
560287117Scemstatic void
561287117Scemioat_process_events(struct ioat_softc *ioat)
562287117Scem{
563287117Scem	struct ioat_descriptor *desc;
564287117Scem	struct bus_dmadesc *dmadesc;
565287117Scem	uint64_t comp_update, status;
566287117Scem	uint32_t completed;
567287117Scem
568287117Scem	mtx_lock(&ioat->cleanup_lock);
569287117Scem
570287117Scem	completed = 0;
571287117Scem	comp_update = *ioat->comp_update;
572287117Scem	status = comp_update & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_MASK;
573287117Scem
574289979Scem	CTR0(KTR_IOAT, __func__);
575287117Scem
576289909Scem	if (status == ioat->last_seen)
577289909Scem		goto out;
578287117Scem
579287117Scem	while (1) {
580287117Scem		desc = ioat_get_ring_entry(ioat, ioat->tail);
581287117Scem		dmadesc = &desc->bus_dmadesc;
582289979Scem		CTR1(KTR_IOAT, "completing desc %d", ioat->tail);
583287117Scem
584287117Scem		if (dmadesc->callback_fn)
585287117Scem			(*dmadesc->callback_fn)(dmadesc->callback_arg);
586287117Scem
587289907Scem		completed++;
588287117Scem		ioat->tail++;
589287117Scem		if (desc->hw_desc_bus_addr == status)
590287117Scem			break;
591287117Scem	}
592287117Scem
593287117Scem	ioat->last_seen = desc->hw_desc_bus_addr;
594287117Scem
595287117Scem	if (ioat->head == ioat->tail) {
596287117Scem		ioat->is_completion_pending = FALSE;
597289904Scem		callout_reset(&ioat->timer, IOAT_INTR_TIMO,
598289904Scem		    ioat_timer_callback, ioat);
599287117Scem	}
600287117Scem
601289909Scemout:
602287117Scem	ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
603287117Scem	mtx_unlock(&ioat->cleanup_lock);
604289907Scem
605289907Scem	ioat_putn(ioat, completed, IOAT_ACTIVE_DESCR_REF);
606289982Scem	wakeup(&ioat->tail);
607287117Scem}
608287117Scem
609287117Scem/*
610287117Scem * User API functions
611287117Scem */
612287117Scembus_dmaengine_t
613287117Scemioat_get_dmaengine(uint32_t index)
614287117Scem{
615287117Scem
616289907Scem	if (index >= ioat_channel_index)
617289907Scem		return (NULL);
618289907Scem	return (&ioat_get(ioat_channel[index], IOAT_DMAENGINE_REF)->dmaengine);
619287117Scem}
620287117Scem
621287117Scemvoid
622289907Scemioat_put_dmaengine(bus_dmaengine_t dmaengine)
623289907Scem{
624289907Scem	struct ioat_softc *ioat;
625289907Scem
626289907Scem	ioat = to_ioat_softc(dmaengine);
627289907Scem	ioat_put(ioat, IOAT_DMAENGINE_REF);
628289907Scem}
629289907Scem
630289907Scemvoid
631287117Scemioat_acquire(bus_dmaengine_t dmaengine)
632287117Scem{
633287117Scem	struct ioat_softc *ioat;
634287117Scem
635287117Scem	ioat = to_ioat_softc(dmaengine);
636287117Scem	mtx_lock(&ioat->submit_lock);
637289979Scem	CTR0(KTR_IOAT, __func__);
638287117Scem}
639287117Scem
640287117Scemvoid
641287117Scemioat_release(bus_dmaengine_t dmaengine)
642287117Scem{
643287117Scem	struct ioat_softc *ioat;
644287117Scem
645289776Scem	ioat = to_ioat_softc(dmaengine);
646289979Scem	CTR0(KTR_IOAT, __func__);
647289982Scem	ioat_write_2(ioat, IOAT_DMACOUNT_OFFSET, (uint16_t)ioat->hw_head);
648287117Scem	mtx_unlock(&ioat->submit_lock);
649287117Scem}
650287117Scem
651287117Scemstruct bus_dmadesc *
652287117Scemioat_null(bus_dmaengine_t dmaengine, bus_dmaengine_callback_t callback_fn,
653287117Scem    void *callback_arg, uint32_t flags)
654287117Scem{
655287117Scem	struct ioat_softc *ioat;
656287117Scem	struct ioat_descriptor *desc;
657287117Scem	struct ioat_dma_hw_descriptor *hw_desc;
658289982Scem	int mflags;
659287117Scem
660287117Scem	KASSERT((flags & ~DMA_ALL_FLAGS) == 0, ("Unrecognized flag(s): %#x",
661287117Scem		flags & ~DMA_ALL_FLAGS));
662289982Scem	if ((flags & DMA_NO_WAIT) != 0)
663289982Scem		mflags = M_NOWAIT;
664289982Scem	else
665289982Scem		mflags = M_WAITOK;
666287117Scem
667287117Scem	ioat = to_ioat_softc(dmaengine);
668289906Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
669287117Scem
670289982Scem	if (ioat_reserve_space(ioat, 1, mflags) != 0)
671287117Scem		return (NULL);
672287117Scem
673289979Scem	CTR0(KTR_IOAT, __func__);
674287117Scem
675287117Scem	desc = ioat_get_ring_entry(ioat, ioat->head);
676287117Scem	hw_desc = desc->u.dma;
677287117Scem
678287117Scem	hw_desc->u.control_raw = 0;
679287117Scem	hw_desc->u.control.null = 1;
680287117Scem	hw_desc->u.control.completion_update = 1;
681287117Scem
682287117Scem	if ((flags & DMA_INT_EN) != 0)
683287117Scem		hw_desc->u.control.int_enable = 1;
684287117Scem
685287117Scem	hw_desc->size = 8;
686287117Scem	hw_desc->src_addr = 0;
687287117Scem	hw_desc->dest_addr = 0;
688287117Scem
689287117Scem	desc->bus_dmadesc.callback_fn = callback_fn;
690287117Scem	desc->bus_dmadesc.callback_arg = callback_arg;
691287117Scem
692287117Scem	ioat_submit_single(ioat);
693287117Scem	return (&desc->bus_dmadesc);
694287117Scem}
695287117Scem
696287117Scemstruct bus_dmadesc *
697287117Scemioat_copy(bus_dmaengine_t dmaengine, bus_addr_t dst,
698287117Scem    bus_addr_t src, bus_size_t len, bus_dmaengine_callback_t callback_fn,
699287117Scem    void *callback_arg, uint32_t flags)
700287117Scem{
701287117Scem	struct ioat_descriptor *desc;
702287117Scem	struct ioat_dma_hw_descriptor *hw_desc;
703287117Scem	struct ioat_softc *ioat;
704289982Scem	int mflags;
705287117Scem
706287117Scem	KASSERT((flags & ~DMA_ALL_FLAGS) == 0, ("Unrecognized flag(s): %#x",
707287117Scem		flags & ~DMA_ALL_FLAGS));
708289982Scem	if ((flags & DMA_NO_WAIT) != 0)
709289982Scem		mflags = M_NOWAIT;
710289982Scem	else
711289982Scem		mflags = M_WAITOK;
712287117Scem
713287117Scem	ioat = to_ioat_softc(dmaengine);
714289906Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
715287117Scem
716287117Scem	if (len > ioat->max_xfer_size) {
717287117Scem		ioat_log_message(0, "%s: max_xfer_size = %d, requested = %d\n",
718287117Scem		    __func__, ioat->max_xfer_size, (int)len);
719287117Scem		return (NULL);
720287117Scem	}
721287117Scem
722289982Scem	if (ioat_reserve_space(ioat, 1, mflags) != 0)
723287117Scem		return (NULL);
724287117Scem
725289979Scem	CTR0(KTR_IOAT, __func__);
726287117Scem
727287117Scem	desc = ioat_get_ring_entry(ioat, ioat->head);
728287117Scem	hw_desc = desc->u.dma;
729287117Scem
730287117Scem	hw_desc->u.control_raw = 0;
731287117Scem	hw_desc->u.control.completion_update = 1;
732287117Scem
733287117Scem	if ((flags & DMA_INT_EN) != 0)
734287117Scem		hw_desc->u.control.int_enable = 1;
735287117Scem
736287117Scem	hw_desc->size = len;
737287117Scem	hw_desc->src_addr = src;
738287117Scem	hw_desc->dest_addr = dst;
739287117Scem
740287117Scem	if (g_ioat_debug_level >= 3)
741287117Scem		dump_descriptor(hw_desc);
742287117Scem
743287117Scem	desc->bus_dmadesc.callback_fn = callback_fn;
744287117Scem	desc->bus_dmadesc.callback_arg = callback_arg;
745287117Scem
746287117Scem	ioat_submit_single(ioat);
747287117Scem	return (&desc->bus_dmadesc);
748287117Scem}
749287117Scem
750287117Scem/*
751287117Scem * Ring Management
752287117Scem */
753287117Scemstatic inline uint32_t
754287117Scemioat_get_active(struct ioat_softc *ioat)
755287117Scem{
756287117Scem
757287117Scem	return ((ioat->head - ioat->tail) & ((1 << ioat->ring_size_order) - 1));
758287117Scem}
759287117Scem
760287117Scemstatic inline uint32_t
761287117Scemioat_get_ring_space(struct ioat_softc *ioat)
762287117Scem{
763287117Scem
764287117Scem	return ((1 << ioat->ring_size_order) - ioat_get_active(ioat) - 1);
765287117Scem}
766287117Scem
767287117Scemstatic struct ioat_descriptor *
768289982Scemioat_alloc_ring_entry(struct ioat_softc *ioat, int mflags)
769287117Scem{
770287117Scem	struct ioat_dma_hw_descriptor *hw_desc;
771287117Scem	struct ioat_descriptor *desc;
772289982Scem	int error, busdmaflag;
773287117Scem
774289905Scem	error = ENOMEM;
775289905Scem	hw_desc = NULL;
776289905Scem
777289982Scem	if ((mflags & M_WAITOK) != 0)
778289982Scem		busdmaflag = BUS_DMA_WAITOK;
779289982Scem	else
780289982Scem		busdmaflag = BUS_DMA_NOWAIT;
781289982Scem
782289982Scem	desc = malloc(sizeof(*desc), M_IOAT, mflags);
783287117Scem	if (desc == NULL)
784289905Scem		goto out;
785287117Scem
786289905Scem	bus_dmamem_alloc(ioat->hw_desc_tag, (void **)&hw_desc,
787289982Scem	    BUS_DMA_ZERO | busdmaflag, &ioat->hw_desc_map);
788289905Scem	if (hw_desc == NULL)
789289905Scem		goto out;
790289905Scem
791289905Scem	desc->u.dma = hw_desc;
792289905Scem
793289905Scem	error = bus_dmamap_load(ioat->hw_desc_tag, ioat->hw_desc_map, hw_desc,
794289905Scem	    sizeof(*hw_desc), ioat_dmamap_cb, &desc->hw_desc_bus_addr,
795289982Scem	    busdmaflag);
796289905Scem	if (error)
797289905Scem		goto out;
798289905Scem
799289905Scemout:
800289905Scem	if (error) {
801289905Scem		ioat_free_ring_entry(ioat, desc);
802287117Scem		return (NULL);
803287117Scem	}
804287117Scem	return (desc);
805287117Scem}
806287117Scem
807287117Scemstatic void
808287117Scemioat_free_ring_entry(struct ioat_softc *ioat, struct ioat_descriptor *desc)
809287117Scem{
810287117Scem
811287117Scem	if (desc == NULL)
812287117Scem		return;
813287117Scem
814287117Scem	if (desc->u.dma)
815287117Scem		bus_dmamem_free(ioat->hw_desc_tag, desc->u.dma,
816287117Scem		    ioat->hw_desc_map);
817287117Scem	free(desc, M_IOAT);
818287117Scem}
819287117Scem
820289982Scem/*
821289982Scem * Reserves space in this IOAT descriptor ring by ensuring enough slots remain
822289982Scem * for 'num_descs'.
823289982Scem *
824289982Scem * If mflags contains M_WAITOK, blocks until enough space is available.
825289982Scem *
826289982Scem * Returns zero on success, or an errno on error.  If num_descs is beyond the
827289982Scem * maximum ring size, returns EINVAl; if allocation would block and mflags
828289982Scem * contains M_NOWAIT, returns EAGAIN.
829289982Scem *
830289982Scem * Must be called with the submit_lock held; returns with the lock held.  The
831289982Scem * lock may be dropped to allocate the ring.
832289982Scem *
833289982Scem * (The submit_lock is needed to add any entries to the ring, so callers are
834289982Scem * assured enough room is available.)
835289982Scem */
836287117Scemstatic int
837289982Scemioat_reserve_space(struct ioat_softc *ioat, uint32_t num_descs, int mflags)
838287117Scem{
839289982Scem	struct ioat_descriptor **new_ring;
840289982Scem	uint32_t order;
841289982Scem	int error;
842287117Scem
843289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
844289982Scem	error = 0;
845289982Scem
846289982Scem	if (num_descs < 1 || num_descs > (1 << IOAT_MAX_ORDER)) {
847289982Scem		error = EINVAL;
848289982Scem		goto out;
849289982Scem	}
850289982Scem
851289982Scem	for (;;) {
852287117Scem		if (ioat_get_ring_space(ioat) >= num_descs)
853289982Scem			goto out;
854287117Scem
855289982Scem		order = ioat->ring_size_order;
856289982Scem		if (ioat->is_resize_pending || order == IOAT_MAX_ORDER) {
857289982Scem			if ((mflags & M_WAITOK) != 0) {
858289982Scem				msleep(&ioat->tail, &ioat->submit_lock, 0,
859289982Scem				    "ioat_rsz", 0);
860289982Scem				continue;
861289982Scem			}
862287117Scem
863289982Scem			error = EAGAIN;
864289982Scem			break;
865289982Scem		}
866289982Scem
867289982Scem		ioat->is_resize_pending = TRUE;
868289982Scem		for (;;) {
869289982Scem			mtx_unlock(&ioat->submit_lock);
870289982Scem
871289982Scem			new_ring = ioat_prealloc_ring(ioat, 1 << (order + 1),
872289982Scem			    TRUE, mflags);
873289982Scem
874289982Scem			mtx_lock(&ioat->submit_lock);
875289982Scem			KASSERT(ioat->ring_size_order == order,
876289982Scem			    ("is_resize_pending should protect order"));
877289982Scem
878289982Scem			if (new_ring == NULL) {
879289982Scem				KASSERT((mflags & M_WAITOK) == 0,
880289982Scem				    ("allocation failed"));
881289982Scem				error = EAGAIN;
882289982Scem				break;
883289982Scem			}
884289982Scem
885289982Scem			error = ring_grow(ioat, order, new_ring);
886289982Scem			if (error == 0)
887289982Scem				break;
888289982Scem		}
889289982Scem		ioat->is_resize_pending = FALSE;
890289982Scem		wakeup(&ioat->tail);
891289982Scem		if (error)
892289982Scem			break;
893287117Scem	}
894289982Scem
895289982Scemout:
896289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
897289982Scem	return (error);
898287117Scem}
899287117Scem
900289982Scemstatic struct ioat_descriptor **
901289982Scemioat_prealloc_ring(struct ioat_softc *ioat, uint32_t size, boolean_t need_dscr,
902289982Scem    int mflags)
903289982Scem{
904289982Scem	struct ioat_descriptor **ring;
905289982Scem	uint32_t i;
906289982Scem	int error;
907289982Scem
908289982Scem	KASSERT(size > 0 && powerof2(size), ("bogus size"));
909289982Scem
910289982Scem	ring = malloc(size * sizeof(*ring), M_IOAT, M_ZERO | mflags);
911289982Scem	if (ring == NULL)
912289982Scem		return (NULL);
913289982Scem
914289982Scem	if (need_dscr) {
915289982Scem		error = ENOMEM;
916289982Scem		for (i = size / 2; i < size; i++) {
917289982Scem			ring[i] = ioat_alloc_ring_entry(ioat, mflags);
918289982Scem			if (ring[i] == NULL)
919289982Scem				goto out;
920289982Scem			ring[i]->id = i;
921289982Scem		}
922289982Scem	}
923289982Scem	error = 0;
924289982Scem
925289982Scemout:
926289982Scem	if (error != 0 && ring != NULL) {
927289982Scem		ioat_free_ring(ioat, size, ring);
928289982Scem		ring = NULL;
929289982Scem	}
930289982Scem	return (ring);
931289982Scem}
932289982Scem
933289982Scemstatic void
934289982Scemioat_free_ring(struct ioat_softc *ioat, uint32_t size,
935289982Scem    struct ioat_descriptor **ring)
936289982Scem{
937289982Scem	uint32_t i;
938289982Scem
939289982Scem	for (i = 0; i < size; i++) {
940289982Scem		if (ring[i] != NULL)
941289982Scem			ioat_free_ring_entry(ioat, ring[i]);
942289982Scem	}
943289982Scem	free(ring, M_IOAT);
944289982Scem}
945289982Scem
946287117Scemstatic struct ioat_descriptor *
947287117Scemioat_get_ring_entry(struct ioat_softc *ioat, uint32_t index)
948287117Scem{
949287117Scem
950287117Scem	return (ioat->ring[index % (1 << ioat->ring_size_order)]);
951287117Scem}
952287117Scem
953289982Scemstatic int
954289982Scemring_grow(struct ioat_softc *ioat, uint32_t oldorder,
955289982Scem    struct ioat_descriptor **newring)
956287117Scem{
957289982Scem	struct ioat_descriptor *tmp, *next;
958287117Scem	struct ioat_dma_hw_descriptor *hw;
959289982Scem	uint32_t oldsize, newsize, head, tail, i, end;
960289982Scem	int error;
961287117Scem
962289982Scem	CTR0(KTR_IOAT, __func__);
963287117Scem
964289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
965287117Scem
966289982Scem	if (oldorder != ioat->ring_size_order || oldorder >= IOAT_MAX_ORDER) {
967289982Scem		error = EINVAL;
968289982Scem		goto out;
969289982Scem	}
970287117Scem
971289982Scem	oldsize = (1 << oldorder);
972289982Scem	newsize = (1 << (oldorder + 1));
973287117Scem
974289982Scem	mtx_lock(&ioat->cleanup_lock);
975287117Scem
976289982Scem	head = ioat->head & (oldsize - 1);
977289982Scem	tail = ioat->tail & (oldsize - 1);
978287117Scem
979289982Scem	/* Copy old descriptors to new ring */
980289982Scem	for (i = 0; i < oldsize; i++)
981289982Scem		newring[i] = ioat->ring[i];
982287117Scem
983289982Scem	/*
984289982Scem	 * If head has wrapped but tail hasn't, we must swap some descriptors
985289982Scem	 * around so that tail can increment directly to head.
986289982Scem	 */
987289982Scem	if (head < tail) {
988289982Scem		for (i = 0; i <= head; i++) {
989289982Scem			tmp = newring[oldsize + i];
990287117Scem
991289982Scem			newring[oldsize + i] = newring[i];
992289982Scem			newring[oldsize + i]->id = oldsize + i;
993287117Scem
994289982Scem			newring[i] = tmp;
995289982Scem			newring[i]->id = i;
996287117Scem		}
997289982Scem		head += oldsize;
998289982Scem	}
999287117Scem
1000289982Scem	KASSERT(head >= tail, ("invariants"));
1001287117Scem
1002289982Scem	/* Head didn't wrap; we only need to link in oldsize..newsize */
1003289982Scem	if (head < oldsize) {
1004289982Scem		i = oldsize - 1;
1005289982Scem		end = newsize;
1006287117Scem	} else {
1007289982Scem		/* Head did wrap; link newhead..newsize and 0..oldhead */
1008289982Scem		i = head;
1009289982Scem		end = newsize + (head - oldsize) + 1;
1010289982Scem	}
1011287117Scem
1012289982Scem	/*
1013289982Scem	 * Fix up hardware ring, being careful not to trample the active
1014289982Scem	 * section (tail -> head).
1015289982Scem	 */
1016289982Scem	for (; i < end; i++) {
1017289982Scem		KASSERT((i & (newsize - 1)) < tail ||
1018289982Scem		    (i & (newsize - 1)) >= head, ("trampling snake"));
1019287117Scem
1020289982Scem		next = newring[(i + 1) & (newsize - 1)];
1021289982Scem		hw = newring[i & (newsize - 1)]->u.dma;
1022287117Scem		hw->next = next->hw_desc_bus_addr;
1023287117Scem	}
1024287117Scem
1025287117Scem	free(ioat->ring, M_IOAT);
1026289982Scem	ioat->ring = newring;
1027289982Scem	ioat->ring_size_order = oldorder + 1;
1028289982Scem	ioat->tail = tail;
1029289982Scem	ioat->head = head;
1030289982Scem	error = 0;
1031287117Scem
1032289982Scem	mtx_unlock(&ioat->cleanup_lock);
1033289982Scemout:
1034289982Scem	if (error)
1035289982Scem		ioat_free_ring(ioat, (1 << (oldorder + 1)), newring);
1036289982Scem	return (error);
1037287117Scem}
1038287117Scem
1039289982Scemstatic int
1040289982Scemring_shrink(struct ioat_softc *ioat, uint32_t oldorder,
1041289982Scem    struct ioat_descriptor **newring)
1042289982Scem{
1043289982Scem	struct ioat_dma_hw_descriptor *hw;
1044289982Scem	struct ioat_descriptor *ent, *next;
1045289982Scem	uint32_t oldsize, newsize, current_idx, new_idx, i;
1046289982Scem	int error;
1047289982Scem
1048289982Scem	CTR0(KTR_IOAT, __func__);
1049289982Scem
1050289982Scem	mtx_assert(&ioat->submit_lock, MA_OWNED);
1051289982Scem
1052289982Scem	if (oldorder != ioat->ring_size_order || oldorder <= IOAT_MIN_ORDER) {
1053289982Scem		error = EINVAL;
1054289982Scem		goto out_unlocked;
1055289982Scem	}
1056289982Scem
1057289982Scem	oldsize = (1 << oldorder);
1058289982Scem	newsize = (1 << (oldorder - 1));
1059289982Scem
1060289982Scem	mtx_lock(&ioat->cleanup_lock);
1061289982Scem
1062289982Scem	/* Can't shrink below current active set! */
1063289982Scem	if (ioat_get_active(ioat) >= newsize) {
1064289982Scem		error = ENOMEM;
1065289982Scem		goto out;
1066289982Scem	}
1067289982Scem
1068289982Scem	/*
1069289982Scem	 * Copy current descriptors to the new ring, dropping the removed
1070289982Scem	 * descriptors.
1071289982Scem	 */
1072289982Scem	for (i = 0; i < newsize; i++) {
1073289982Scem		current_idx = (ioat->tail + i) & (oldsize - 1);
1074289982Scem		new_idx = (ioat->tail + i) & (newsize - 1);
1075289982Scem
1076289982Scem		newring[new_idx] = ioat->ring[current_idx];
1077289982Scem		newring[new_idx]->id = new_idx;
1078289982Scem	}
1079289982Scem
1080289982Scem	/* Free deleted descriptors */
1081289982Scem	for (i = newsize; i < oldsize; i++) {
1082289982Scem		ent = ioat_get_ring_entry(ioat, ioat->tail + i);
1083289982Scem		ioat_free_ring_entry(ioat, ent);
1084289982Scem	}
1085289982Scem
1086289982Scem	/* Fix up hardware ring. */
1087289982Scem	hw = newring[(ioat->tail + newsize - 1) & (newsize - 1)]->u.dma;
1088289982Scem	next = newring[(ioat->tail + newsize) & (newsize - 1)];
1089289982Scem	hw->next = next->hw_desc_bus_addr;
1090289982Scem
1091289982Scem	free(ioat->ring, M_IOAT);
1092289982Scem	ioat->ring = newring;
1093289982Scem	ioat->ring_size_order = oldorder - 1;
1094289982Scem	error = 0;
1095289982Scem
1096289982Scemout:
1097289982Scem	mtx_unlock(&ioat->cleanup_lock);
1098289982Scemout_unlocked:
1099289982Scem	if (error)
1100289982Scem		ioat_free_ring(ioat, (1 << (oldorder - 1)), newring);
1101289982Scem	return (error);
1102289982Scem}
1103289982Scem
1104287117Scemstatic void
1105289910Scemioat_halted_debug(struct ioat_softc *ioat, uint32_t chanerr)
1106289910Scem{
1107289910Scem	struct ioat_descriptor *desc;
1108289910Scem
1109289983Scem	ioat_log_message(0, "Channel halted (%b)\n", (int)chanerr,
1110289983Scem	    IOAT_CHANERR_STR);
1111289910Scem	if (chanerr == 0)
1112289910Scem		return;
1113289910Scem
1114289982Scem	mtx_lock(&ioat->submit_lock);
1115289910Scem	desc = ioat_get_ring_entry(ioat, ioat->tail + 0);
1116289910Scem	dump_descriptor(desc->u.raw);
1117289910Scem
1118289910Scem	desc = ioat_get_ring_entry(ioat, ioat->tail + 1);
1119289910Scem	dump_descriptor(desc->u.raw);
1120289982Scem	mtx_unlock(&ioat->submit_lock);
1121289910Scem}
1122289910Scem
1123289910Scemstatic void
1124287117Scemioat_timer_callback(void *arg)
1125287117Scem{
1126289982Scem	struct ioat_descriptor **newring;
1127287117Scem	struct ioat_softc *ioat;
1128287117Scem	uint64_t status;
1129289982Scem	uint32_t chanerr, order;
1130287117Scem
1131287117Scem	ioat = arg;
1132289904Scem	ioat_log_message(1, "%s\n", __func__);
1133287117Scem
1134287117Scem	if (ioat->is_completion_pending) {
1135287117Scem		status = ioat_get_chansts(ioat);
1136287117Scem
1137287117Scem		/*
1138287117Scem		 * When halted due to errors, check for channel programming
1139287117Scem		 * errors before advancing the completion state.
1140287117Scem		 */
1141287117Scem		if (is_ioat_halted(status)) {
1142287117Scem			chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
1143289910Scem			ioat_halted_debug(ioat, chanerr);
1144287117Scem		}
1145287117Scem		ioat_process_events(ioat);
1146287117Scem	} else {
1147287117Scem		mtx_lock(&ioat->submit_lock);
1148289982Scem		order = ioat->ring_size_order;
1149289982Scem		if (ioat->is_resize_pending || order == IOAT_MIN_ORDER) {
1150289982Scem			mtx_unlock(&ioat->submit_lock);
1151289982Scem			goto out;
1152289982Scem		}
1153289982Scem		ioat->is_resize_pending = TRUE;
1154289982Scem		mtx_unlock(&ioat->submit_lock);
1155287117Scem
1156289982Scem		newring = ioat_prealloc_ring(ioat, 1 << (order - 1), FALSE,
1157289982Scem		    M_NOWAIT);
1158287117Scem
1159289982Scem		mtx_lock(&ioat->submit_lock);
1160289982Scem		KASSERT(ioat->ring_size_order == order,
1161289982Scem		    ("resize_pending protects order"));
1162289982Scem
1163289982Scem		if (newring != NULL)
1164289982Scem			ring_shrink(ioat, order, newring);
1165289982Scem
1166289982Scem		ioat->is_resize_pending = FALSE;
1167287117Scem		mtx_unlock(&ioat->submit_lock);
1168287117Scem
1169289982Scemout:
1170289982Scem		/* Slowly scale the ring down if idle. */
1171287117Scem		if (ioat->ring_size_order > IOAT_MIN_ORDER)
1172289982Scem			callout_reset(&ioat->timer, 10 * hz,
1173287117Scem			    ioat_timer_callback, ioat);
1174287117Scem	}
1175287117Scem}
1176287117Scem
1177287117Scem/*
1178287117Scem * Support Functions
1179287117Scem */
1180287117Scemstatic void
1181287117Scemioat_submit_single(struct ioat_softc *ioat)
1182287117Scem{
1183287117Scem
1184289907Scem	ioat_get(ioat, IOAT_ACTIVE_DESCR_REF);
1185287117Scem	atomic_add_rel_int(&ioat->head, 1);
1186289982Scem	atomic_add_rel_int(&ioat->hw_head, 1);
1187287117Scem
1188287117Scem	if (!ioat->is_completion_pending) {
1189287117Scem		ioat->is_completion_pending = TRUE;
1190289904Scem		callout_reset(&ioat->timer, IOAT_INTR_TIMO,
1191289904Scem		    ioat_timer_callback, ioat);
1192287117Scem	}
1193287117Scem}
1194287117Scem
1195287117Scemstatic int
1196287117Scemioat_reset_hw(struct ioat_softc *ioat)
1197287117Scem{
1198287117Scem	uint64_t status;
1199287117Scem	uint32_t chanerr;
1200289912Scem	unsigned timeout;
1201287117Scem
1202287117Scem	status = ioat_get_chansts(ioat);
1203287117Scem	if (is_ioat_active(status) || is_ioat_idle(status))
1204287117Scem		ioat_suspend(ioat);
1205287117Scem
1206287117Scem	/* Wait at most 20 ms */
1207287117Scem	for (timeout = 0; (is_ioat_active(status) || is_ioat_idle(status)) &&
1208287117Scem	    timeout < 20; timeout++) {
1209287117Scem		DELAY(1000);
1210287117Scem		status = ioat_get_chansts(ioat);
1211287117Scem	}
1212287117Scem	if (timeout == 20)
1213287117Scem		return (ETIMEDOUT);
1214287117Scem
1215289912Scem	KASSERT(ioat_get_active(ioat) == 0, ("active after quiesce"));
1216289912Scem
1217287117Scem	chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
1218287117Scem	ioat_write_4(ioat, IOAT_CHANERR_OFFSET, chanerr);
1219287117Scem
1220287117Scem	/*
1221287117Scem	 * IOAT v3 workaround - CHANERRMSK_INT with 3E07h to masks out errors
1222287117Scem	 *  that can cause stability issues for IOAT v3.
1223287117Scem	 */
1224287117Scem	pci_write_config(ioat->device, IOAT_CFG_CHANERRMASK_INT_OFFSET, 0x3e07,
1225287117Scem	    4);
1226287117Scem	chanerr = pci_read_config(ioat->device, IOAT_CFG_CHANERR_INT_OFFSET, 4);
1227287117Scem	pci_write_config(ioat->device, IOAT_CFG_CHANERR_INT_OFFSET, chanerr, 4);
1228287117Scem
1229287414Scem	/*
1230287414Scem	 * BDXDE and BWD models reset MSI-X registers on device reset.
1231287414Scem	 * Save/restore their contents manually.
1232287414Scem	 */
1233289908Scem	if (ioat_model_resets_msix(ioat)) {
1234289908Scem		ioat_log_message(1, "device resets MSI-X registers; saving\n");
1235287414Scem		pci_save_state(ioat->device);
1236289908Scem	}
1237287414Scem
1238287117Scem	ioat_reset(ioat);
1239287117Scem
1240287117Scem	/* Wait at most 20 ms */
1241287117Scem	for (timeout = 0; ioat_reset_pending(ioat) && timeout < 20; timeout++)
1242287117Scem		DELAY(1000);
1243287117Scem	if (timeout == 20)
1244287117Scem		return (ETIMEDOUT);
1245287117Scem
1246289908Scem	if (ioat_model_resets_msix(ioat)) {
1247289908Scem		ioat_log_message(1, "device resets registers; restored\n");
1248287414Scem		pci_restore_state(ioat->device);
1249289908Scem	}
1250287403Scem
1251289912Scem	/* Reset attempts to return the hardware to "halted." */
1252289912Scem	status = ioat_get_chansts(ioat);
1253289912Scem	if (is_ioat_active(status) || is_ioat_idle(status)) {
1254289912Scem		/* So this really shouldn't happen... */
1255289912Scem		ioat_log_message(0, "Device is active after a reset?\n");
1256289912Scem		ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
1257289912Scem		return (0);
1258289912Scem	}
1259289912Scem
1260289912Scem	chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
1261289912Scem	ioat_halted_debug(ioat, chanerr);
1262289912Scem	if (chanerr != 0)
1263289912Scem		return (EIO);
1264289912Scem
1265289912Scem	/*
1266289912Scem	 * Bring device back online after reset.  Writing CHAINADDR brings the
1267289912Scem	 * device back to active.
1268289912Scem	 *
1269289912Scem	 * The internal ring counter resets to zero, so we have to start over
1270289912Scem	 * at zero as well.
1271289912Scem	 */
1272289982Scem	ioat->tail = ioat->head = ioat->hw_head = 0;
1273289912Scem	ioat->last_seen = 0;
1274289912Scem
1275289912Scem	ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
1276289912Scem	ioat_write_chancmp(ioat, ioat->comp_update_bus_addr);
1277289912Scem	ioat_write_chainaddr(ioat, ioat->ring[0]->hw_desc_bus_addr);
1278289912Scem	return (ioat_start_channel(ioat));
1279287117Scem}
1280287117Scem
1281289908Scemstatic int
1282289908Scemsysctl_handle_reset(SYSCTL_HANDLER_ARGS)
1283289908Scem{
1284289908Scem	struct ioat_softc *ioat;
1285289908Scem	int error, arg;
1286289908Scem
1287289908Scem	ioat = arg1;
1288289908Scem
1289289908Scem	arg = 0;
1290289908Scem	error = SYSCTL_OUT(req, &arg, sizeof(arg));
1291289908Scem	if (error != 0 || req->newptr == NULL)
1292289908Scem		return (error);
1293289908Scem
1294289908Scem	error = SYSCTL_IN(req, &arg, sizeof(arg));
1295289908Scem	if (error != 0)
1296289908Scem		return (error);
1297289908Scem
1298289908Scem	if (arg != 0)
1299289908Scem		error = ioat_reset_hw(ioat);
1300289908Scem
1301289908Scem	return (error);
1302289908Scem}
1303289908Scem
1304287117Scemstatic void
1305287117Scemdump_descriptor(void *hw_desc)
1306287117Scem{
1307287117Scem	int i, j;
1308287117Scem
1309287117Scem	for (i = 0; i < 2; i++) {
1310287117Scem		for (j = 0; j < 8; j++)
1311287117Scem			printf("%08x ", ((uint32_t *)hw_desc)[i * 8 + j]);
1312287117Scem		printf("\n");
1313287117Scem	}
1314287117Scem}
1315287117Scem
1316287117Scemstatic void
1317287117Scemioat_setup_sysctl(device_t device)
1318287117Scem{
1319289908Scem	struct sysctl_oid_list *par;
1320289908Scem	struct sysctl_ctx_list *ctx;
1321289908Scem	struct sysctl_oid *tree;
1322287117Scem	struct ioat_softc *ioat;
1323287117Scem
1324287117Scem	ioat = DEVICE2SOFTC(device);
1325289908Scem	ctx = device_get_sysctl_ctx(device);
1326289908Scem	tree = device_get_sysctl_tree(device);
1327289908Scem	par = SYSCTL_CHILDREN(tree);
1328287117Scem
1329289980Scem	SYSCTL_ADD_INT(ctx, par, OID_AUTO, "version", CTLFLAG_RD,
1330289980Scem	    &ioat->version, 0, "HW version (0xMM form)");
1331289980Scem	SYSCTL_ADD_UINT(ctx, par, OID_AUTO, "max_xfer_size", CTLFLAG_RD,
1332289980Scem	    &ioat->max_xfer_size, 0, "HW maximum transfer size");
1333289980Scem
1334289908Scem	SYSCTL_ADD_UINT(ctx, par, OID_AUTO, "ring_size_order", CTLFLAG_RD,
1335289982Scem	    &ioat->ring_size_order, 0, "SW descriptor ring size order");
1336289908Scem	SYSCTL_ADD_UINT(ctx, par, OID_AUTO, "head", CTLFLAG_RD, &ioat->head, 0,
1337289982Scem	    "SW descriptor head pointer index");
1338289908Scem	SYSCTL_ADD_UINT(ctx, par, OID_AUTO, "tail", CTLFLAG_RD, &ioat->tail, 0,
1339289982Scem	    "SW descriptor tail pointer index");
1340289982Scem	SYSCTL_ADD_UINT(ctx, par, OID_AUTO, "hw_head", CTLFLAG_RD,
1341289982Scem	    &ioat->hw_head, 0, "HW DMACOUNT");
1342289908Scem
1343289980Scem	SYSCTL_ADD_UQUAD(ctx, par, OID_AUTO, "last_completion", CTLFLAG_RD,
1344289980Scem	    ioat->comp_update, "HW addr of last completion");
1345289980Scem
1346289980Scem	SYSCTL_ADD_INT(ctx, par, OID_AUTO, "is_resize_pending", CTLFLAG_RD,
1347289980Scem	    &ioat->is_resize_pending, 0, "resize pending");
1348289980Scem	SYSCTL_ADD_INT(ctx, par, OID_AUTO, "is_completion_pending", CTLFLAG_RD,
1349289980Scem	    &ioat->is_completion_pending, 0, "completion pending");
1350289980Scem	SYSCTL_ADD_INT(ctx, par, OID_AUTO, "is_reset_pending", CTLFLAG_RD,
1351289980Scem	    &ioat->is_reset_pending, 0, "reset pending");
1352289980Scem	SYSCTL_ADD_INT(ctx, par, OID_AUTO, "is_channel_running", CTLFLAG_RD,
1353289980Scem	    &ioat->is_channel_running, 0, "channel running");
1354289980Scem
1355289908Scem	SYSCTL_ADD_PROC(ctx, par, OID_AUTO, "force_hw_reset",
1356289908Scem	    CTLTYPE_INT | CTLFLAG_RW, ioat, 0, sysctl_handle_reset, "I",
1357289908Scem	    "Set to non-zero to reset the hardware");
1358287117Scem}
1359289907Scem
1360289907Scemstatic inline struct ioat_softc *
1361289907Scemioat_get(struct ioat_softc *ioat, enum ioat_ref_kind kind)
1362289907Scem{
1363289907Scem	uint32_t old;
1364289907Scem
1365289907Scem	KASSERT(kind < IOAT_NUM_REF_KINDS, ("bogus"));
1366289907Scem
1367289907Scem	old = atomic_fetchadd_32(&ioat->refcnt, 1);
1368289907Scem	KASSERT(old < UINT32_MAX, ("refcnt overflow"));
1369289907Scem
1370289907Scem#ifdef INVARIANTS
1371289907Scem	old = atomic_fetchadd_32(&ioat->refkinds[kind], 1);
1372289907Scem	KASSERT(old < UINT32_MAX, ("refcnt kind overflow"));
1373289907Scem#endif
1374289907Scem
1375289907Scem	return (ioat);
1376289907Scem}
1377289907Scem
1378289907Scemstatic inline void
1379289907Scemioat_putn(struct ioat_softc *ioat, uint32_t n, enum ioat_ref_kind kind)
1380289907Scem{
1381289907Scem	uint32_t old;
1382289907Scem
1383289907Scem	KASSERT(kind < IOAT_NUM_REF_KINDS, ("bogus"));
1384289907Scem
1385289907Scem	if (n == 0)
1386289907Scem		return;
1387289907Scem
1388289907Scem#ifdef INVARIANTS
1389289907Scem	old = atomic_fetchadd_32(&ioat->refkinds[kind], -n);
1390289907Scem	KASSERT(old >= n, ("refcnt kind underflow"));
1391289907Scem#endif
1392289907Scem
1393289907Scem	/* Skip acquiring the lock if resulting refcnt > 0. */
1394289907Scem	for (;;) {
1395289907Scem		old = ioat->refcnt;
1396289907Scem		if (old <= n)
1397289907Scem			break;
1398289907Scem		if (atomic_cmpset_32(&ioat->refcnt, old, old - n))
1399289907Scem			return;
1400289907Scem	}
1401289907Scem
1402289907Scem	mtx_lock(IOAT_REFLK);
1403289907Scem	old = atomic_fetchadd_32(&ioat->refcnt, -n);
1404289907Scem	KASSERT(old >= n, ("refcnt error"));
1405289907Scem
1406289907Scem	if (old == n)
1407289907Scem		wakeup(IOAT_REFLK);
1408289907Scem	mtx_unlock(IOAT_REFLK);
1409289907Scem}
1410289907Scem
1411289907Scemstatic inline void
1412289907Scemioat_put(struct ioat_softc *ioat, enum ioat_ref_kind kind)
1413289907Scem{
1414289907Scem
1415289907Scem	ioat_putn(ioat, 1, kind);
1416289907Scem}
1417289907Scem
1418289907Scemstatic void
1419289907Scemioat_drain(struct ioat_softc *ioat)
1420289907Scem{
1421289907Scem
1422289907Scem	mtx_lock(IOAT_REFLK);
1423289907Scem	while (ioat->refcnt > 0)
1424289907Scem		msleep(IOAT_REFLK, IOAT_REFLK, 0, "ioat_drain", 0);
1425289907Scem	mtx_unlock(IOAT_REFLK);
1426289907Scem}
1427