148156Sjlemon/*-
257828Sjlemon * Copyright (c) 1999,2000 Jonathan Lemon
348156Sjlemon * All rights reserved.
448156Sjlemon *
548156Sjlemon # Derived from the original IDA Compaq RAID driver, which is
648156Sjlemon * Copyright (c) 1996, 1997, 1998, 1999
748156Sjlemon *    Mark Dawson and David James. All rights reserved.
848156Sjlemon *
948156Sjlemon * Redistribution and use in source and binary forms, with or without
1048156Sjlemon * modification, are permitted provided that the following conditions
1148156Sjlemon * are met:
1248156Sjlemon * 1. Redistributions of source code must retain the above copyright
1348156Sjlemon *    notice, this list of conditions and the following disclaimer.
1448156Sjlemon * 2. Redistributions in binary form must reproduce the above copyright
1548156Sjlemon *    notice, this list of conditions and the following disclaimer in the
1648156Sjlemon *    documentation and/or other materials provided with the distribution.
1748156Sjlemon *
1848156Sjlemon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1948156Sjlemon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2048156Sjlemon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2148156Sjlemon * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2248156Sjlemon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2348156Sjlemon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2448156Sjlemon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2548156Sjlemon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2648156Sjlemon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2748156Sjlemon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2848156Sjlemon * SUCH DAMAGE.
2948156Sjlemon */
3048156Sjlemon
31119418Sobrien#include <sys/cdefs.h>
32119418Sobrien__FBSDID("$FreeBSD$");
33119418Sobrien
3448156Sjlemon/*
3548156Sjlemon * Generic driver for Compaq SMART RAID adapters.
3648156Sjlemon */
3748156Sjlemon
3848156Sjlemon#include <sys/param.h>
3973113Sjlemon#include <sys/kernel.h>
4048156Sjlemon#include <sys/systm.h>
41239740Sjhb#include <sys/lock.h>
4248156Sjlemon#include <sys/malloc.h>
43239740Sjhb#include <sys/mutex.h>
44124540Smdodd#include <sys/stat.h>
4548156Sjlemon
4660041Sphk#include <sys/bio.h>
4748156Sjlemon#include <sys/bus.h>
48111337Sphk#include <sys/conf.h>
49124538Smdodd#include <sys/endian.h>
5048156Sjlemon
5148156Sjlemon#include <machine/bus.h>
5248156Sjlemon#include <sys/rman.h>
5348156Sjlemon
54112946Sphk#include <geom/geom_disk.h>
55112946Sphk
5648156Sjlemon#include <dev/ida/idareg.h>
5748156Sjlemon#include <dev/ida/idavar.h>
58124540Smdodd#include <dev/ida/idaio.h>
5948156Sjlemon
6048156Sjlemon/* prototypes */
61239740Sjhbstatic int ida_alloc_qcbs(struct ida_softc *ida);
62239740Sjhbstatic void ida_done(struct ida_softc *ida, struct ida_qcb *qcb);
6348156Sjlemonstatic void ida_start(struct ida_softc *ida);
64239740Sjhbstatic void ida_startio(struct ida_softc *ida);
65239740Sjhbstatic void ida_startup(void *arg);
66239740Sjhbstatic void ida_timeout(void *arg);
6773113Sjlemonstatic int ida_wait(struct ida_softc *ida, struct ida_qcb *qcb);
6848156Sjlemon
69124540Smdoddstatic d_ioctl_t ida_ioctl;
70124540Smdoddstatic struct cdevsw ida_cdevsw = {
71126080Sphk	.d_version =	D_VERSION,
72124540Smdodd	.d_ioctl =	ida_ioctl,
73124540Smdodd	.d_name =	"ida",
74124540Smdodd};
75124540Smdodd
7648156Sjlemonvoid
7748156Sjlemonida_free(struct ida_softc *ida)
7848156Sjlemon{
7957828Sjlemon	int i;
8048156Sjlemon
81239740Sjhb	if (ida->ih != NULL)
82239740Sjhb		bus_teardown_intr(ida->dev, ida->irq, ida->ih);
83239740Sjhb
84239740Sjhb	mtx_lock(&ida->lock);
85138853Smdodd	callout_stop(&ida->ch);
86239740Sjhb	mtx_unlock(&ida->lock);
87239740Sjhb	callout_drain(&ida->ch);
88138853Smdodd
89145024Smdodd	if (ida->buffer_dmat) {
90239740Sjhb		for (i = 0; i < IDA_QCB_MAX; i++)
91145024Smdodd			bus_dmamap_destroy(ida->buffer_dmat, ida->qcbs[i].dmamap);
9248156Sjlemon		bus_dma_tag_destroy(ida->buffer_dmat);
93145024Smdodd	}
9448156Sjlemon
95145024Smdodd	if (ida->hwqcb_dmat) {
96145024Smdodd		if (ida->hwqcb_busaddr)
97145024Smdodd			bus_dmamap_unload(ida->hwqcb_dmat, ida->hwqcb_dmamap);
98145024Smdodd		if (ida->hwqcbs)
99145024Smdodd			bus_dmamem_free(ida->hwqcb_dmat, ida->hwqcbs,
100145024Smdodd			    ida->hwqcb_dmamap);
10148156Sjlemon		bus_dma_tag_destroy(ida->hwqcb_dmat);
102145024Smdodd	}
10348156Sjlemon
10448156Sjlemon	if (ida->qcbs != NULL)
10548156Sjlemon		free(ida->qcbs, M_DEVBUF);
10648156Sjlemon
10748156Sjlemon	if (ida->irq != NULL)
10848156Sjlemon		bus_release_resource(ida->dev, ida->irq_res_type,
10948156Sjlemon		    0, ida->irq);
11048156Sjlemon
11148156Sjlemon	if (ida->parent_dmat != NULL)
11248156Sjlemon		bus_dma_tag_destroy(ida->parent_dmat);
11348156Sjlemon
11448156Sjlemon	if (ida->regs != NULL)
11548156Sjlemon		bus_release_resource(ida->dev, ida->regs_res_type,
11648156Sjlemon		    ida->regs_res_id, ida->regs);
117239740Sjhb
118239740Sjhb	mtx_destroy(&ida->lock);
11948156Sjlemon}
12048156Sjlemon
12148156Sjlemon/*
12248156Sjlemon * record bus address from bus_dmamap_load
12348156Sjlemon */
12448156Sjlemonstatic void
125144991Smdoddida_dma_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
12648156Sjlemon{
127144991Smdodd	bus_addr_t *baddr;
12848156Sjlemon
129144991Smdodd	baddr = (bus_addr_t *)arg;
130144991Smdodd	*baddr = segs->ds_addr;
13148156Sjlemon}
13248156Sjlemon
13348156Sjlemonstatic __inline struct ida_qcb *
13448156Sjlemonida_get_qcb(struct ida_softc *ida)
13548156Sjlemon{
13648156Sjlemon	struct ida_qcb *qcb;
13748156Sjlemon
13848156Sjlemon	if ((qcb = SLIST_FIRST(&ida->free_qcbs)) != NULL) {
13948156Sjlemon		SLIST_REMOVE_HEAD(&ida->free_qcbs, link.sle);
140239740Sjhb		bzero(qcb->hwqcb, sizeof(struct ida_hdr) + sizeof(struct ida_req));
14148156Sjlemon	}
14248156Sjlemon	return (qcb);
14348156Sjlemon}
14448156Sjlemon
145239740Sjhbstatic __inline void
146239740Sjhbida_free_qcb(struct ida_softc *ida, struct ida_qcb *qcb)
147239740Sjhb{
148239740Sjhb
149239740Sjhb	qcb->state = QCB_FREE;
150239740Sjhb	qcb->buf = NULL;
151239740Sjhb	qcb->error = 0;
152239740Sjhb	SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle);
153239740Sjhb}
154239740Sjhb
15557828Sjlemonstatic __inline bus_addr_t
15657828Sjlemonidahwqcbvtop(struct ida_softc *ida, struct ida_hardware_qcb *hwqcb)
15757828Sjlemon{
15857828Sjlemon	return (ida->hwqcb_busaddr +
15957828Sjlemon	    ((bus_addr_t)hwqcb - (bus_addr_t)ida->hwqcbs));
16057828Sjlemon}
16157828Sjlemon
16257828Sjlemonstatic __inline struct ida_qcb *
16357828Sjlemonidahwqcbptov(struct ida_softc *ida, bus_addr_t hwqcb_addr)
16457828Sjlemon{
16557828Sjlemon	struct ida_hardware_qcb *hwqcb;
16657828Sjlemon
16757828Sjlemon	hwqcb = (struct ida_hardware_qcb *)
16857828Sjlemon	    ((bus_addr_t)ida->hwqcbs + (hwqcb_addr - ida->hwqcb_busaddr));
16957828Sjlemon	return (hwqcb->qcb);
17057828Sjlemon}
17157828Sjlemon
172239740Sjhbstatic int
173239740Sjhbida_alloc_qcbs(struct ida_softc *ida)
17448156Sjlemon{
17548156Sjlemon	struct ida_qcb *qcb;
176239740Sjhb	int error, i;
17748156Sjlemon
178239740Sjhb	for (i = 0; i < IDA_QCB_MAX; i++) {
179239740Sjhb		qcb = &ida->qcbs[i];
18048156Sjlemon
181239740Sjhb		error = bus_dmamap_create(ida->buffer_dmat, /*flags*/0, &qcb->dmamap);
182239740Sjhb		if (error != 0)
183239740Sjhb			return (error);
18448156Sjlemon
185239740Sjhb		qcb->ida = ida;
186239740Sjhb		qcb->flags = QCB_FREE;
187239740Sjhb		qcb->hwqcb = &ida->hwqcbs[i];
188239740Sjhb		qcb->hwqcb->qcb = qcb;
189239740Sjhb		qcb->hwqcb_busaddr = idahwqcbvtop(ida, qcb->hwqcb);
190239740Sjhb		SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle);
191239740Sjhb	}
192239740Sjhb	return (0);
19348156Sjlemon}
19448156Sjlemon
19548156Sjlemonint
19648156Sjlemonida_init(struct ida_softc *ida)
19748156Sjlemon{
198239740Sjhb	struct ida_controller_info cinfo;
199239740Sjhb	device_t child;
200239740Sjhb	int error, i, unit;
20148156Sjlemon
20248156Sjlemon	SLIST_INIT(&ida->free_qcbs);
20348156Sjlemon	STAILQ_INIT(&ida->qcb_queue);
204144991Smdodd	bioq_init(&ida->bio_queue);
20548156Sjlemon
20648156Sjlemon	ida->qcbs = (struct ida_qcb *)
20769781Sdwmalone	    malloc(IDA_QCB_MAX * sizeof(struct ida_qcb), M_DEVBUF,
20869781Sdwmalone		M_NOWAIT | M_ZERO);
20948156Sjlemon	if (ida->qcbs == NULL)
21048156Sjlemon		return (ENOMEM);
21148156Sjlemon
21248156Sjlemon	/*
21348156Sjlemon	 * Create our DMA tags
21448156Sjlemon	 */
21548156Sjlemon
21648156Sjlemon	/* DMA tag for our hardware QCB structures */
217138851Smdodd	error = bus_dma_tag_create(
218138851Smdodd		/* parent	*/ ida->parent_dmat,
219138851Smdodd		/* alignment	*/ 1,
220138851Smdodd		/* boundary	*/ 0,
221138851Smdodd		/* lowaddr	*/ BUS_SPACE_MAXADDR,
222138851Smdodd		/* highaddr	*/ BUS_SPACE_MAXADDR,
223138851Smdodd		/* filter	*/ NULL,
224138851Smdodd		/* filterarg	*/ NULL,
225138851Smdodd		/* maxsize	*/ IDA_QCB_MAX * sizeof(struct ida_hardware_qcb),
226138851Smdodd		/* nsegments	*/ 1,
227138851Smdodd		/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
228138851Smdodd		/* flags	*/ 0,
229239740Sjhb		/* lockfunc	*/ NULL,
230239740Sjhb		/* lockarg	*/ NULL,
231138851Smdodd		&ida->hwqcb_dmat);
23248156Sjlemon	if (error)
233144991Smdodd		return (ENOMEM);
23448156Sjlemon
23548156Sjlemon	/* DMA tag for mapping buffers into device space */
236138851Smdodd	error = bus_dma_tag_create(
237138851Smdodd		/* parent 	*/ ida->parent_dmat,
238138851Smdodd		/* alignment	*/ 1,
239138851Smdodd		/* boundary	*/ 0,
240138851Smdodd		/* lowaddr	*/ BUS_SPACE_MAXADDR,
241138851Smdodd		/* highaddr	*/ BUS_SPACE_MAXADDR,
242138851Smdodd		/* filter	*/ NULL,
243138851Smdodd		/* filterarg	*/ NULL,
244281826Smav		/* maxsize	*/ DFLTPHYS,
245138851Smdodd		/* nsegments	*/ IDA_NSEG,
246138851Smdodd		/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
247138851Smdodd		/* flags	*/ 0,
248138851Smdodd		/* lockfunc	*/ busdma_lock_mutex,
249138851Smdodd		/* lockarg	*/ &Giant,
250138851Smdodd		&ida->buffer_dmat);
25148156Sjlemon	if (error)
252144991Smdodd		return (ENOMEM);
25348156Sjlemon
254144991Smdodd	/* Allocation of hardware QCBs */
25548156Sjlemon	/* XXX allocation is rounded to hardware page size */
25648156Sjlemon	error = bus_dmamem_alloc(ida->hwqcb_dmat,
25748156Sjlemon	    (void **)&ida->hwqcbs, BUS_DMA_NOWAIT, &ida->hwqcb_dmamap);
25848156Sjlemon	if (error)
259144991Smdodd		return (ENOMEM);
26048156Sjlemon
261144991Smdodd	/* And permanently map them in */
262144991Smdodd	bus_dmamap_load(ida->hwqcb_dmat, ida->hwqcb_dmamap,
26348156Sjlemon	    ida->hwqcbs, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb),
26448156Sjlemon	    ida_dma_map_cb, &ida->hwqcb_busaddr, /*flags*/0);
26548156Sjlemon
26648156Sjlemon	bzero(ida->hwqcbs, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb));
26748156Sjlemon
268239740Sjhb	error = ida_alloc_qcbs(ida);
269239740Sjhb	if (error)
270239740Sjhb		return (error);
27148156Sjlemon
272239740Sjhb	mtx_lock(&ida->lock);
27357828Sjlemon	ida->cmd.int_enable(ida, 0);
27448156Sjlemon
27548156Sjlemon	error = ida_command(ida, CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo),
27673113Sjlemon	    IDA_CONTROLLER, 0, DMA_DATA_IN);
27748156Sjlemon	if (error) {
278239740Sjhb		mtx_unlock(&ida->lock);
27948156Sjlemon		device_printf(ida->dev, "CMD_GET_CTRL_INFO failed.\n");
280239740Sjhb		return (error);
28148156Sjlemon	}
28248156Sjlemon
28348156Sjlemon	device_printf(ida->dev, "drives=%d firm_rev=%c%c%c%c\n",
28448156Sjlemon	    cinfo.num_drvs, cinfo.firm_rev[0], cinfo.firm_rev[1],
28548156Sjlemon	    cinfo.firm_rev[2], cinfo.firm_rev[3]);
28648156Sjlemon
28763934Sjlemon	if (ida->flags & IDA_FIRMWARE) {
28863934Sjlemon		int data;
28948156Sjlemon
29063934Sjlemon		error = ida_command(ida, CMD_START_FIRMWARE,
29173113Sjlemon		    &data, sizeof(data), IDA_CONTROLLER, 0, DMA_DATA_IN);
29263934Sjlemon		if (error) {
293239740Sjhb			mtx_unlock(&ida->lock);
29463934Sjlemon			device_printf(ida->dev, "CMD_START_FIRMWARE failed.\n");
295239740Sjhb			return (error);
29663934Sjlemon		}
29763934Sjlemon	}
298239740Sjhb
299239740Sjhb	ida->cmd.int_enable(ida, 1);
300239740Sjhb	ida->flags |= IDA_ATTACHED;
301239740Sjhb	mtx_unlock(&ida->lock);
30263934Sjlemon
303239740Sjhb	for (i = 0; i < cinfo.num_drvs; i++) {
304239740Sjhb		child = device_add_child(ida->dev, /*"idad"*/NULL, -1);
305239740Sjhb		if (child != NULL)
306239740Sjhb			device_set_ivars(child, (void *)(intptr_t)i);
307239740Sjhb	}
308239740Sjhb
309239740Sjhb	ida->ich.ich_func = ida_startup;
310239740Sjhb	ida->ich.ich_arg = ida;
311239740Sjhb	if (config_intrhook_establish(&ida->ich) != 0) {
312239740Sjhb		device_delete_children(ida->dev);
313239740Sjhb		device_printf(ida->dev, "Cannot establish configuration hook\n");
314239740Sjhb		return (error);
315239740Sjhb	}
316239740Sjhb
317239740Sjhb	unit = device_get_unit(ida->dev);
318239740Sjhb	ida->ida_dev_t = make_dev(&ida_cdevsw, unit,
319124540Smdodd				 UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR,
320239740Sjhb				 "ida%d", unit);
321124540Smdodd	ida->ida_dev_t->si_drv1 = ida;
322124540Smdodd
323239740Sjhb	return (0);
324239740Sjhb}
32548156Sjlemon
326239740Sjhbstatic void
327239740Sjhbida_startup(void *arg)
328239740Sjhb{
329239740Sjhb	struct ida_softc *ida;
330239740Sjhb
331239740Sjhb	ida = arg;
332239740Sjhb
333239740Sjhb	config_intrhook_disestablish(&ida->ich);
334239740Sjhb
335239740Sjhb	mtx_lock(&Giant);
33648156Sjlemon	bus_generic_attach(ida->dev);
337239740Sjhb	mtx_unlock(&Giant);
33848156Sjlemon}
33948156Sjlemon
34057828Sjlemonint
34157828Sjlemonida_detach(device_t dev)
34257828Sjlemon{
34357828Sjlemon	struct ida_softc *ida;
344239740Sjhb	int error;
34557828Sjlemon
346144991Smdodd	ida = (struct ida_softc *)device_get_softc(dev);
34757828Sjlemon
348239740Sjhb	error = bus_generic_detach(dev);
349239740Sjhb	if (error)
350239740Sjhb		return (error);
351239740Sjhb	error = device_delete_children(dev);
352239740Sjhb	if (error)
353239740Sjhb		return (error);
354239740Sjhb
35557828Sjlemon	/*
35657828Sjlemon	 * XXX
357144991Smdodd	 * before detaching, we must make sure that the system is
35857828Sjlemon	 * quiescent; nothing mounted, no pending activity.
35957828Sjlemon	 */
36057828Sjlemon
36157828Sjlemon	/*
36257828Sjlemon	 * XXX
36357828Sjlemon	 * now, how are we supposed to maintain a list of our drives?
36457828Sjlemon	 * iterate over our "child devices"?
36557828Sjlemon	 */
36657828Sjlemon
367124540Smdodd	destroy_dev(ida->ida_dev_t);
36857828Sjlemon	ida_free(ida);
36957828Sjlemon	return (error);
37057828Sjlemon}
37157828Sjlemon
37248156Sjlemonstatic void
373239740Sjhbida_data_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
37448156Sjlemon{
375239740Sjhb	struct ida_hardware_qcb *hwqcb;
376239740Sjhb	struct ida_softc *ida;
377239740Sjhb	struct ida_qcb *qcb;
378239740Sjhb	bus_dmasync_op_t op;
37948156Sjlemon	int i;
38048156Sjlemon
381239740Sjhb	qcb = arg;
382239740Sjhb	ida = qcb->ida;
383239740Sjhb	if (!dumping)
384239740Sjhb		mtx_assert(&ida->lock, MA_OWNED);
385239740Sjhb	if (error) {
386239740Sjhb		qcb->error = error;
387239740Sjhb		ida_done(ida, qcb);
388239740Sjhb		return;
389239740Sjhb	}
390239740Sjhb
391239740Sjhb	hwqcb = qcb->hwqcb;
392144991Smdodd	hwqcb->hdr.size = htole16((sizeof(struct ida_req) +
393124538Smdodd	    sizeof(struct ida_sgb) * IDA_NSEG) >> 2);
39448156Sjlemon
39548156Sjlemon	for (i = 0; i < nsegments; i++) {
396124538Smdodd		hwqcb->seg[i].addr = htole32(segs[i].ds_addr);
397124538Smdodd		hwqcb->seg[i].length = htole32(segs[i].ds_len);
39848156Sjlemon	}
39948156Sjlemon	hwqcb->req.sgcount = nsegments;
400239740Sjhb	if (qcb->flags & DMA_DATA_TRANSFER) {
401239740Sjhb		switch (qcb->flags & DMA_DATA_TRANSFER) {
402239740Sjhb		case DMA_DATA_TRANSFER:
403239740Sjhb			op = BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE;
404239740Sjhb			break;
405239740Sjhb		case DMA_DATA_IN:
406239740Sjhb			op = BUS_DMASYNC_PREREAD;
407239740Sjhb			break;
408239740Sjhb		default:
409239740Sjhb			KASSERT((qcb->flags & DMA_DATA_TRANSFER) ==
410239740Sjhb			    DMA_DATA_OUT, ("bad DMA data flags"));
411239740Sjhb			op = BUS_DMASYNC_PREWRITE;
412239740Sjhb			break;
413239740Sjhb		}
414239740Sjhb		bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op);
415239740Sjhb	}
416239740Sjhb	bus_dmamap_sync(ida->hwqcb_dmat, ida->hwqcb_dmamap,
417239740Sjhb	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
418239740Sjhb
419239740Sjhb	STAILQ_INSERT_TAIL(&ida->qcb_queue, qcb, link.stqe);
420239740Sjhb	ida_start(ida);
421239740Sjhb	ida->flags &= ~IDA_QFROZEN;
42248156Sjlemon}
42348156Sjlemon
424239740Sjhbstatic int
425239740Sjhbida_map_qcb(struct ida_softc *ida, struct ida_qcb *qcb, void *data,
426239740Sjhb    bus_size_t datasize)
427239740Sjhb{
428239740Sjhb	int error, flags;
429239740Sjhb
430239740Sjhb	if (ida->flags & IDA_INTERRUPTS)
431239740Sjhb		flags = BUS_DMA_WAITOK;
432239740Sjhb	else
433239740Sjhb		flags = BUS_DMA_NOWAIT;
434239740Sjhb	error = bus_dmamap_load(ida->buffer_dmat, qcb->dmamap, data, datasize,
435239740Sjhb	    ida_data_cb, qcb, flags);
436239740Sjhb	if (error == EINPROGRESS) {
437239740Sjhb		ida->flags |= IDA_QFROZEN;
438239740Sjhb		error = 0;
439239740Sjhb	}
440239740Sjhb	return (error);
441239740Sjhb}
442239740Sjhb
44348156Sjlemonint
44448156Sjlemonida_command(struct ida_softc *ida, int command, void *data, int datasize,
44573113Sjlemon	int drive, u_int32_t pblkno, int flags)
44648156Sjlemon{
44748156Sjlemon	struct ida_hardware_qcb *hwqcb;
44848156Sjlemon	struct ida_qcb *qcb;
449239740Sjhb	int error;
45048156Sjlemon
451239740Sjhb	if (!dumping)
452239740Sjhb		mtx_assert(&ida->lock, MA_OWNED);
45348156Sjlemon	qcb = ida_get_qcb(ida);
45448156Sjlemon
45548156Sjlemon	if (qcb == NULL) {
456239740Sjhb		device_printf(ida->dev, "out of QCBs\n");
45773113Sjlemon		return (EAGAIN);
45848156Sjlemon	}
45948156Sjlemon
460239740Sjhb	qcb->flags = flags | IDA_COMMAND;
46148156Sjlemon	hwqcb = qcb->hwqcb;
46263934Sjlemon	hwqcb->hdr.drive = drive;
463124538Smdodd	hwqcb->req.blkno = htole32(pblkno);
464124538Smdodd	hwqcb->req.bcount = htole16(howmany(datasize, DEV_BSIZE));
46548156Sjlemon	hwqcb->req.command = command;
46648156Sjlemon
467239740Sjhb	error = ida_map_qcb(ida, qcb, data, datasize);
468239740Sjhb	if (error == 0) {
469239740Sjhb		error = ida_wait(ida, qcb);
470239740Sjhb		/* Don't free QCB on a timeout in case it later completes. */
471239740Sjhb		if (error)
472239740Sjhb			return (error);
473239740Sjhb		error = qcb->error;
474239740Sjhb	}
47548156Sjlemon
47648156Sjlemon	/* XXX should have status returned here? */
47748156Sjlemon	/* XXX have "status pointer" area in QCB? */
47848156Sjlemon
479239740Sjhb	ida_free_qcb(ida, qcb);
48073113Sjlemon	return (error);
48148156Sjlemon}
48248156Sjlemon
48348156Sjlemonvoid
48459249Sphkida_submit_buf(struct ida_softc *ida, struct bio *bp)
48548156Sjlemon{
486239740Sjhb	mtx_lock(&ida->lock);
487144991Smdodd	bioq_insert_tail(&ida->bio_queue, bp);
488239740Sjhb	ida_startio(ida);
489239740Sjhb	mtx_unlock(&ida->lock);
49048156Sjlemon}
49148156Sjlemon
49248156Sjlemonstatic void
493239740Sjhbida_startio(struct ida_softc *ida)
49448156Sjlemon{
49548156Sjlemon	struct ida_hardware_qcb *hwqcb;
49648156Sjlemon	struct ida_qcb *qcb;
497239740Sjhb	struct idad_softc *drv;
49859249Sphk	struct bio *bp;
499239740Sjhb	int error;
50048156Sjlemon
501239740Sjhb	mtx_assert(&ida->lock, MA_OWNED);
502239740Sjhb	for (;;) {
503239740Sjhb		if (ida->flags & IDA_QFROZEN)
504239740Sjhb			return;
505239740Sjhb		bp = bioq_first(&ida->bio_queue);
506239740Sjhb		if (bp == NULL)
507239740Sjhb			return;				/* no more buffers */
50848156Sjlemon
509239740Sjhb		qcb = ida_get_qcb(ida);
510239740Sjhb		if (qcb == NULL)
511239740Sjhb			return;				/* out of resources */
51248156Sjlemon
513239740Sjhb		bioq_remove(&ida->bio_queue, bp);
514239740Sjhb		qcb->buf = bp;
515239740Sjhb		qcb->flags = bp->bio_cmd == BIO_READ ? DMA_DATA_IN : DMA_DATA_OUT;
51648156Sjlemon
517239740Sjhb		hwqcb = qcb->hwqcb;
518239740Sjhb		drv = bp->bio_driver1;
519239740Sjhb		hwqcb->hdr.drive = drv->drive;
520239740Sjhb		hwqcb->req.blkno = bp->bio_pblkno;
521239740Sjhb		hwqcb->req.bcount = howmany(bp->bio_bcount, DEV_BSIZE);
522239740Sjhb		hwqcb->req.command = bp->bio_cmd == BIO_READ ? CMD_READ : CMD_WRITE;
52348156Sjlemon
524239740Sjhb		error = ida_map_qcb(ida, qcb, bp->bio_data, bp->bio_bcount);
525239740Sjhb		if (error) {
526239740Sjhb			qcb->error = error;
527239740Sjhb			ida_done(ida, qcb);
528239740Sjhb		}
52948156Sjlemon	}
53048156Sjlemon}
53148156Sjlemon
53248156Sjlemonstatic void
53348156Sjlemonida_start(struct ida_softc *ida)
53448156Sjlemon{
53548156Sjlemon	struct ida_qcb *qcb;
53648156Sjlemon
537239740Sjhb	if (!dumping)
538239740Sjhb		mtx_assert(&ida->lock, MA_OWNED);
53948156Sjlemon	while ((qcb = STAILQ_FIRST(&ida->qcb_queue)) != NULL) {
54057828Sjlemon		if (ida->cmd.fifo_full(ida))
54157828Sjlemon			break;
54248156Sjlemon		STAILQ_REMOVE_HEAD(&ida->qcb_queue, link.stqe);
54348156Sjlemon		/*
54448156Sjlemon		 * XXX
545138853Smdodd		 * place the qcb on an active list?
54648156Sjlemon		 */
547138853Smdodd
548138853Smdodd		/* Set a timeout. */
549239740Sjhb		if (!ida->qactive && !dumping)
550138853Smdodd			callout_reset(&ida->ch, hz * 5, ida_timeout, ida);
551138853Smdodd		ida->qactive++;
552138853Smdodd
55348156Sjlemon		qcb->state = QCB_ACTIVE;
55457828Sjlemon		ida->cmd.submit(ida, qcb);
55548156Sjlemon	}
55648156Sjlemon}
55748156Sjlemon
55873113Sjlemonstatic int
55973113Sjlemonida_wait(struct ida_softc *ida, struct ida_qcb *qcb)
56048156Sjlemon{
56148156Sjlemon	struct ida_qcb *qcb_done = NULL;
56248156Sjlemon	bus_addr_t completed;
56373113Sjlemon	int delay;
56448156Sjlemon
565239740Sjhb	if (!dumping)
566239740Sjhb		mtx_assert(&ida->lock, MA_OWNED);
56773113Sjlemon	if (ida->flags & IDA_INTERRUPTS) {
568239740Sjhb		if (mtx_sleep(qcb, &ida->lock, PRIBIO, "idacmd", 5 * hz)) {
569239740Sjhb			qcb->state = QCB_TIMEDOUT;
57073113Sjlemon			return (ETIMEDOUT);
571239740Sjhb		}
57273113Sjlemon		return (0);
57348156Sjlemon	}
57448156Sjlemon
57573113Sjlemonagain:
57673113Sjlemon	delay = 5 * 1000 * 100;			/* 5 sec delay */
57757828Sjlemon	while ((completed = ida->cmd.done(ida)) == 0) {
578239740Sjhb		if (delay-- == 0) {
579239740Sjhb			qcb->state = QCB_TIMEDOUT;
58073113Sjlemon			return (ETIMEDOUT);
581239740Sjhb		}
58248156Sjlemon		DELAY(10);
58348156Sjlemon	}
58448156Sjlemon
58548156Sjlemon	qcb_done = idahwqcbptov(ida, completed & ~3);
58648156Sjlemon	if (qcb_done != qcb)
58773113Sjlemon		goto again;
58848156Sjlemon	ida_done(ida, qcb);
58973113Sjlemon	return (0);
59048156Sjlemon}
59148156Sjlemon
59248156Sjlemonvoid
59348156Sjlemonida_intr(void *data)
59448156Sjlemon{
59548156Sjlemon	struct ida_softc *ida;
59648156Sjlemon	struct ida_qcb *qcb;
59748156Sjlemon	bus_addr_t completed;
59848156Sjlemon
59948156Sjlemon	ida = (struct ida_softc *)data;
60048156Sjlemon
601239740Sjhb	mtx_lock(&ida->lock);
602239740Sjhb	if (ida->cmd.int_pending(ida) == 0) {
603239740Sjhb		mtx_unlock(&ida->lock);
60448156Sjlemon		return;				/* not our interrupt */
605239740Sjhb	}
60648156Sjlemon
60757828Sjlemon	while ((completed = ida->cmd.done(ida)) != 0) {
60848156Sjlemon		qcb = idahwqcbptov(ida, completed & ~3);
60948156Sjlemon
61048156Sjlemon		if (qcb == NULL || qcb->state != QCB_ACTIVE) {
61148156Sjlemon			device_printf(ida->dev,
612106591Sjhb			    "ignoring completion %jx\n", (intmax_t)completed);
61348156Sjlemon			continue;
61448156Sjlemon		}
615124449Smdodd		/* Handle "Bad Command List" errors. */
616124449Smdodd		if ((completed & 3) && (qcb->hwqcb->req.error == 0))
617124449Smdodd			qcb->hwqcb->req.error = CMD_REJECTED;
61848156Sjlemon		ida_done(ida, qcb);
61948156Sjlemon	}
620239740Sjhb	ida_startio(ida);
621239740Sjhb	mtx_unlock(&ida->lock);
62248156Sjlemon}
62348156Sjlemon
62448156Sjlemon/*
62548156Sjlemon * should switch out command type; may be status, not just I/O.
62648156Sjlemon */
62748156Sjlemonstatic void
62848156Sjlemonida_done(struct ida_softc *ida, struct ida_qcb *qcb)
62948156Sjlemon{
630239740Sjhb	bus_dmasync_op_t op;
631239740Sjhb	int active, error = 0;
63248156Sjlemon
63348156Sjlemon	/*
63448156Sjlemon	 * finish up command
63548156Sjlemon	 */
636239740Sjhb	if (!dumping)
637239740Sjhb		mtx_assert(&ida->lock, MA_OWNED);
638239740Sjhb	active = (qcb->state != QCB_FREE);
639239740Sjhb	if (qcb->flags & DMA_DATA_TRANSFER && active) {
640239740Sjhb		switch (qcb->flags & DMA_DATA_TRANSFER) {
641239740Sjhb		case DMA_DATA_TRANSFER:
642239740Sjhb			op = BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE;
643239740Sjhb			break;
644239740Sjhb		case DMA_DATA_IN:
645239740Sjhb			op = BUS_DMASYNC_POSTREAD;
646239740Sjhb			break;
647239740Sjhb		default:
648239740Sjhb			KASSERT((qcb->flags & DMA_DATA_TRANSFER) ==
649239740Sjhb			    DMA_DATA_OUT, ("bad DMA data flags"));
650239740Sjhb			op = BUS_DMASYNC_POSTWRITE;
651239740Sjhb			break;
652239740Sjhb		}
65348156Sjlemon		bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op);
65448156Sjlemon		bus_dmamap_unload(ida->buffer_dmat, qcb->dmamap);
65548156Sjlemon	}
656239740Sjhb	if (active)
657239740Sjhb		bus_dmamap_sync(ida->hwqcb_dmat, ida->hwqcb_dmamap,
658239740Sjhb		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
65948156Sjlemon
660124423Smdodd	if (qcb->hwqcb->req.error & SOFT_ERROR) {
661124423Smdodd		if (qcb->buf)
662124423Smdodd			device_printf(ida->dev, "soft %s error\n",
663124423Smdodd				qcb->buf->bio_cmd == BIO_READ ?
664124423Smdodd					"read" : "write");
665124423Smdodd		else
666124423Smdodd			device_printf(ida->dev, "soft error\n");
667124423Smdodd	}
66848156Sjlemon	if (qcb->hwqcb->req.error & HARD_ERROR) {
66948156Sjlemon		error = 1;
670124423Smdodd		if (qcb->buf)
671124423Smdodd			device_printf(ida->dev, "hard %s error\n",
672124423Smdodd				qcb->buf->bio_cmd == BIO_READ ?
673124423Smdodd					"read" : "write");
674124423Smdodd		else
675124423Smdodd			device_printf(ida->dev, "hard error\n");
67648156Sjlemon	}
67748156Sjlemon	if (qcb->hwqcb->req.error & CMD_REJECTED) {
67848156Sjlemon		error = 1;
67948156Sjlemon		device_printf(ida->dev, "invalid request\n");
68048156Sjlemon	}
681239740Sjhb	if (qcb->error) {
682239740Sjhb		error = 1;
683239740Sjhb		device_printf(ida->dev, "request failed to map: %d\n", qcb->error);
684239740Sjhb	}
68548156Sjlemon
68648156Sjlemon	if (qcb->flags & IDA_COMMAND) {
68773113Sjlemon		if (ida->flags & IDA_INTERRUPTS)
68848156Sjlemon			wakeup(qcb);
689239740Sjhb		if (qcb->state == QCB_TIMEDOUT)
690239740Sjhb			ida_free_qcb(ida, qcb);
69148156Sjlemon	} else {
692145023Smdodd		KASSERT(qcb->buf != NULL, ("ida_done(): qcb->buf is NULL!"));
69348156Sjlemon		if (error)
69459249Sphk			qcb->buf->bio_flags |= BIO_ERROR;
69559485Smdodd		idad_intr(qcb->buf);
696239740Sjhb		ida_free_qcb(ida, qcb);
69748156Sjlemon	}
69848156Sjlemon
699239740Sjhb	if (!active)
700239740Sjhb		return;
701239740Sjhb
702138853Smdodd	ida->qactive--;
703138853Smdodd	/* Reschedule or cancel timeout */
704138853Smdodd	if (ida->qactive)
705138853Smdodd		callout_reset(&ida->ch, hz * 5, ida_timeout, ida);
706138853Smdodd	else
707138853Smdodd		callout_stop(&ida->ch);
70848156Sjlemon}
709124540Smdodd
710138853Smdoddstatic void
711239740Sjhbida_timeout(void *arg)
712138853Smdodd{
713138853Smdodd	struct ida_softc *ida;
714138853Smdodd
715138853Smdodd	ida = (struct ida_softc *)arg;
716138853Smdodd	device_printf(ida->dev, "%s() qactive %d\n", __func__, ida->qactive);
717138853Smdodd
718138853Smdodd	if (ida->flags & IDA_INTERRUPTS)
719138853Smdodd		device_printf(ida->dev, "IDA_INTERRUPTS\n");
720138853Smdodd
721138853Smdodd	device_printf(ida->dev,	"\t   R_CMD_FIFO: %08x\n"
722138853Smdodd				"\t  R_DONE_FIFO: %08x\n"
723138853Smdodd				"\t   R_INT_MASK: %08x\n"
724138853Smdodd				"\t     R_STATUS: %08x\n"
725138853Smdodd				"\tR_INT_PENDING: %08x\n",
726138853Smdodd					ida_inl(ida, R_CMD_FIFO),
727138853Smdodd					ida_inl(ida, R_DONE_FIFO),
728138853Smdodd					ida_inl(ida, R_INT_MASK),
729138853Smdodd					ida_inl(ida, R_STATUS),
730138853Smdodd					ida_inl(ida, R_INT_PENDING));
731138853Smdodd
732138853Smdodd	return;
733138853Smdodd}
734138853Smdodd
735124540Smdodd/*
736124540Smdodd * IOCTL stuff follows.
737124540Smdodd */
738124540Smdoddstruct cmd_info {
739124540Smdodd	int	cmd;
740124540Smdodd	int	len;
741124540Smdodd	int	flags;
742124540Smdodd};
743124540Smdoddstatic struct cmd_info *ida_cmd_lookup(int);
744124540Smdodd
745124540Smdoddstatic int
746130585Sphkida_ioctl (struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
747124540Smdodd{
748124540Smdodd	struct ida_softc *sc;
749124540Smdodd	struct ida_user_command *uc;
750124540Smdodd	struct cmd_info *ci;
751124540Smdodd	int len;
752124540Smdodd	int flags;
753124540Smdodd	int error;
754124540Smdodd	int data;
755124540Smdodd	void *daddr;
756124540Smdodd
757124540Smdodd	sc = (struct ida_softc *)dev->si_drv1;
758124540Smdodd	uc = (struct ida_user_command *)addr;
759124540Smdodd	error = 0;
760124540Smdodd
761124540Smdodd	switch (cmd) {
762124540Smdodd	case IDAIO_COMMAND:
763124540Smdodd		ci = ida_cmd_lookup(uc->command);
764124540Smdodd		if (ci == NULL) {
765124540Smdodd			error = EINVAL;
766124540Smdodd			break;
767124540Smdodd		}
768124540Smdodd		len = ci->len;
769124540Smdodd		flags = ci->flags;
770124540Smdodd		if (len)
771124540Smdodd			daddr = &uc->d.buf;
772124540Smdodd		else {
773124540Smdodd			daddr = &data;
774124540Smdodd			len = sizeof(data);
775124540Smdodd		}
776239740Sjhb		mtx_lock(&sc->lock);
777124540Smdodd		error = ida_command(sc, uc->command, daddr, len,
778124540Smdodd				    uc->drive, uc->blkno, flags);
779239740Sjhb		mtx_unlock(&sc->lock);
780124540Smdodd		break;
781124540Smdodd	default:
782124540Smdodd		error = ENOIOCTL;
783124540Smdodd		break;
784124540Smdodd	}
785124540Smdodd	return (error);
786124540Smdodd}
787124540Smdodd
788124540Smdoddstatic struct cmd_info ci_list[] = {
789124540Smdodd	{ CMD_GET_LOG_DRV_INFO,
790124540Smdodd			sizeof(struct ida_drive_info), DMA_DATA_IN },
791124540Smdodd	{ CMD_GET_CTRL_INFO,
792124540Smdodd			sizeof(struct ida_controller_info), DMA_DATA_IN },
793124540Smdodd	{ CMD_SENSE_DRV_STATUS,
794124540Smdodd			sizeof(struct ida_drive_status), DMA_DATA_IN },
795124540Smdodd	{ CMD_START_RECOVERY,		0, 0 },
796124540Smdodd	{ CMD_GET_PHYS_DRV_INFO,
797124540Smdodd			sizeof(struct ida_phys_drv_info), DMA_DATA_TRANSFER },
798124540Smdodd	{ CMD_BLINK_DRV_LEDS,
799124540Smdodd			sizeof(struct ida_blink_drv_leds), DMA_DATA_OUT },
800124540Smdodd	{ CMD_SENSE_DRV_LEDS,
801124540Smdodd			sizeof(struct ida_blink_drv_leds), DMA_DATA_IN },
802124540Smdodd	{ CMD_GET_LOG_DRV_EXT,
803124540Smdodd			sizeof(struct ida_drive_info_ext), DMA_DATA_IN },
804124540Smdodd	{ CMD_RESET_CTRL,		0, 0 },
805124540Smdodd	{ CMD_GET_CONFIG,		0, 0 },
806124540Smdodd	{ CMD_SET_CONFIG,		0, 0 },
807124540Smdodd	{ CMD_LABEL_LOG_DRV,
808124540Smdodd			sizeof(struct ida_label_logical), DMA_DATA_OUT },
809124540Smdodd	{ CMD_SET_SURFACE_DELAY,	0, 0 },
810124540Smdodd	{ CMD_SENSE_BUS_PARAMS,		0, 0 },
811124540Smdodd	{ CMD_SENSE_SUBSYS_INFO,	0, 0 },
812124540Smdodd	{ CMD_SENSE_SURFACE_ATS,	0, 0 },
813124540Smdodd	{ CMD_PASSTHROUGH,		0, 0 },
814124540Smdodd	{ CMD_RESET_SCSI_DEV,		0, 0 },
815124540Smdodd	{ CMD_PAUSE_BG_ACT,		0, 0 },
816124540Smdodd	{ CMD_RESUME_BG_ACT,		0, 0 },
817124540Smdodd	{ CMD_START_FIRMWARE,		0, 0 },
818124540Smdodd	{ CMD_SENSE_DRV_ERR_LOG,	0, 0 },
819124540Smdodd	{ CMD_START_CPM,		0, 0 },
820124540Smdodd	{ CMD_SENSE_CP,			0, 0 },
821124540Smdodd	{ CMD_STOP_CPM,			0, 0 },
822124540Smdodd	{ CMD_FLUSH_CACHE,		0, 0 },
823124540Smdodd	{ CMD_ACCEPT_MEDIA_EXCH,	0, 0 },
824124540Smdodd	{ 0, 0, 0 }
825124540Smdodd};
826124540Smdodd
827124540Smdoddstatic struct cmd_info *
828124540Smdoddida_cmd_lookup (int command)
829124540Smdodd{
830124540Smdodd	struct cmd_info *ci;
831124540Smdodd
832124540Smdodd	ci = ci_list;
833124540Smdodd	while (ci->cmd) {
834124540Smdodd		if (ci->cmd == command)
835124540Smdodd			return (ci);
836124540Smdodd		ci++;
837124540Smdodd	}
838124540Smdodd	return (NULL);
839124540Smdodd}
840