ida_eisa.c revision 280347
1228063Sbapt/*-
295060Sjmallett * Copyright (c) 2000 Jonathan Lemon
395060Sjmallett * Copyright (c) 1999 by Matthew N. Dodd <winter@jurai.net>
41590Srgrimes * All Rights Reserved.
51590Srgrimes *
61590Srgrimes * Redistribution and use in source and binary forms, with or without
71590Srgrimes * modification, are permitted provided that the following conditions
81590Srgrimes * are met:
91590Srgrimes * 1. Redistributions of source code must retain the above copyright
101590Srgrimes *    notice, this list of conditions, and the following disclaimer,
111590Srgrimes *    without modification, immediately at the beginning of the file.
121590Srgrimes * 2. The name of the author may not be used to endorse or promote products
131590Srgrimes *    derived from this software without specific prior written permission.
141590Srgrimes *
151590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
161590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
171590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19228063Sbapt * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
211590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
221590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
231590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
241590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
251590Srgrimes * SUCH DAMAGE.
261590Srgrimes */
271590Srgrimes
281590Srgrimes#include <sys/cdefs.h>
291590Srgrimes__FBSDID("$FreeBSD: head/sys/dev/ida/ida_eisa.c 280347 2015-03-22 16:10:28Z mav $");
301590Srgrimes
311590Srgrimes#include <sys/param.h>
321590Srgrimes#include <sys/systm.h>
331590Srgrimes#include <sys/kernel.h>
341590Srgrimes#include <sys/module.h>
3595060Sjmallett#include <sys/bus.h>
3695060Sjmallett
371590Srgrimes#include <sys/bio.h>
381590Srgrimes#include <sys/conf.h>
3995060Sjmallett
4095060Sjmallett#include <machine/bus.h>
41228063Sbapt#include <machine/resource.h>
421590Srgrimes#include <sys/rman.h>
431590Srgrimes
4495060Sjmallett#include <geom/geom_disk.h>
451590Srgrimes
4695060Sjmallett#include <dev/ida/idavar.h>
471590Srgrimes#include <dev/ida/idareg.h>
481590Srgrimes
491590Srgrimes#include <dev/eisa/eisaconf.h>
501590Srgrimes
511590Srgrimes#define	IDA_EISA_IOPORT_START	0x0c88
5295060Sjmallett#define	IDA_EISA_IOPORT_LEN	0x0017
5395060Sjmallett
5495060Sjmallett#define	IDA_EISA_IRQ_REG	0x0cc0
5595060Sjmallett#define	IDA_EISA_IRQ_MASK	0xf0
5695060Sjmallett#define	IDA_EISA_IRQ_15		0x80
5795060Sjmallett#define	IDA_EISA_IRQ_14		0x40
5895060Sjmallett#define	IDA_EISA_IRQ_11		0x10
59228063Sbapt#define	IDA_EISA_IRQ_10		0x20
60228063Sbapt
61228063Sbaptstatic int
62228063Sbaptida_v1_fifo_full(struct ida_softc *ida)
63228063Sbapt{
6495060Sjmallett	u_int8_t status;
6595060Sjmallett
661590Srgrimes	status = ida_inb(ida, R_EISA_SYSTEM_DOORBELL);
671590Srgrimes	return ((status & EISA_CHANNEL_CLEAR) == 0);
681590Srgrimes}
6995060Sjmallett
7095887Sjmallettstatic void
711590Srgrimesida_v1_submit(struct ida_softc *ida, struct ida_qcb *qcb)
7295060Sjmallett{
731590Srgrimes	u_int16_t size;
7495060Sjmallett
7595060Sjmallett	/*
7695060Sjmallett	 * On these cards, this location is actually for control flags.
7795060Sjmallett	 * Set them to zero and pass in structure size via an I/O port.
7895060Sjmallett	 */
791590Srgrimes	size = qcb->hwqcb->hdr.size << 2;
801590Srgrimes	qcb->hwqcb->hdr.size = 0;
81228063Sbapt
821590Srgrimes	ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_CLEAR);
831590Srgrimes	ida_outl(ida, R_EISA_LIST_ADDR, qcb->hwqcb_busaddr);
84228063Sbapt	ida_outw(ida, R_EISA_LIST_LEN, size);
851590Srgrimes	ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
865165Sache}
875167Sache
8895060Sjmallettstatic bus_addr_t
8995060Sjmallettida_v1_done(struct ida_softc *ida)
9095060Sjmallett{
911590Srgrimes	struct ida_hardware_qcb *hwqcb;
921590Srgrimes	bus_addr_t completed;
931590Srgrimes	u_int8_t status;
941590Srgrimes
95228063Sbapt	if ((ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY) == 0)
961590Srgrimes		return (0);
971590Srgrimes
981590Srgrimes	ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_BUSY);
9995887Sjmallett	completed = ida_inl(ida, R_EISA_COMPLETE_ADDR);
1001590Srgrimes	status = ida_inb(ida, R_EISA_LIST_STATUS);
10195060Sjmallett	ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_CLEAR);
1021590Srgrimes
10395060Sjmallett	if (completed != 0) {
104228063Sbapt		hwqcb = (struct ida_hardware_qcb *)
10595060Sjmallett		    ((bus_addr_t)ida->hwqcbs +
10695060Sjmallett		    ((completed & ~3) - ida->hwqcb_busaddr));
10795060Sjmallett		hwqcb->req.error = status;
1081590Srgrimes	}
1091590Srgrimes
1101590Srgrimes	return (completed);
1111590Srgrimes}
1121590Srgrimes
1131590Srgrimesstatic int
11495887Sjmallettida_v1_int_pending(struct ida_softc *ida)
1151590Srgrimes{
116228063Sbapt	return (ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY);
117228063Sbapt}
118228063Sbapt
119228063Sbaptstatic void
120228063Sbaptida_v1_int_enable(struct ida_softc *ida, int enable)
121228063Sbapt{
122228063Sbapt	if (enable) {
12395060Sjmallett		ida_outb(ida, R_EISA_SYSTEM_DOORBELL, ~EISA_CHANNEL_CLEAR);
124228063Sbapt		ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
1251590Srgrimes		ida_outb(ida, R_EISA_INT_MASK, INT_ENABLE);
126228063Sbapt		ida_outb(ida, R_EISA_SYSTEM_MASK, INT_ENABLE);
127228063Sbapt		ida->flags |= IDA_INTERRUPTS;
128228063Sbapt	} else {
129228063Sbapt		ida_outb(ida, R_EISA_SYSTEM_MASK, INT_DISABLE);
130228063Sbapt		ida->flags &= ~IDA_INTERRUPTS;
131228063Sbapt	}
1321590Srgrimes}
1331590Srgrimes
134228063Sbaptstatic int
135228063Sbaptida_v2_fifo_full(struct ida_softc *ida)
1361590Srgrimes{
137228063Sbapt	return (ida_inl(ida, R_CMD_FIFO) == 0);
1381590Srgrimes}
1391590Srgrimes
140228063Sbaptstatic void
141228063Sbaptida_v2_submit(struct ida_softc *ida, struct ida_qcb *qcb)
142228063Sbapt{
143228063Sbapt	ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr);
144228063Sbapt}
145228063Sbapt
1461590Srgrimesstatic bus_addr_t
1471590Srgrimesida_v2_done(struct ida_softc *ida)
1481590Srgrimes{
14995060Sjmallett	return (ida_inl(ida, R_DONE_FIFO));
15095060Sjmallett}
15195060Sjmallett
15295887Sjmallettstatic int
15395060Sjmallettida_v2_int_pending(struct ida_softc *ida)
15495060Sjmallett{
155228063Sbapt	return (ida_inl(ida, R_INT_PENDING));
15695060Sjmallett}
15795060Sjmallett
15895060Sjmallettstatic void
15995060Sjmallettida_v2_int_enable(struct ida_softc *ida, int enable)
160100014Sjmallett{
16199939Sjmallett	if (enable)
16295060Sjmallett		ida->flags |= IDA_INTERRUPTS;
16395060Sjmallett	else
16495060Sjmallett		ida->flags &= ~IDA_INTERRUPTS;
165228063Sbapt	ida_outl(ida, R_INT_MASK, enable ? INT_ENABLE : INT_DISABLE);
16695060Sjmallett}
16795060Sjmallett
168228063Sbaptstatic struct ida_access ida_v1_access = {
16995060Sjmallett	ida_v1_fifo_full,
17095060Sjmallett	ida_v1_submit,
17195060Sjmallett	ida_v1_done,
17295060Sjmallett	ida_v1_int_pending,
17395060Sjmallett	ida_v1_int_enable,
17495060Sjmallett};
17595060Sjmallett
176100014Sjmallettstatic struct ida_access ida_v2_access = {
17799939Sjmallett	ida_v2_fifo_full,
17895060Sjmallett	ida_v2_submit,
17995060Sjmallett	ida_v2_done,
18095060Sjmallett	ida_v2_int_pending,
18195060Sjmallett	ida_v2_int_enable,
18295060Sjmallett};
18395060Sjmallett
18495060Sjmallettstatic struct ida_board board_id[] = {
18595060Sjmallett	{ 0x0e114001, "Compaq IDA controller",
18695060Sjmallett	    &ida_v1_access, 0 },
187100014Sjmallett	{ 0x0e114002, "Compaq IDA-2 controller",
18895060Sjmallett	    &ida_v1_access, 0 },
189100014Sjmallett	{ 0x0e114010, "Compaq IAES controller",
19095060Sjmallett	    &ida_v1_access, 0 },
19195060Sjmallett	{ 0x0e114020, "Compaq SMART array controller",
19295060Sjmallett	    &ida_v1_access, 0 },
19395060Sjmallett	{ 0x0e114030, "Compaq SMART-2/E array controller",
19495060Sjmallett	    &ida_v2_access, 0 },
19595060Sjmallett
19695060Sjmallett	{ 0, "", 0, 0 }
19795060Sjmallett};
19899939Sjmallett
19995060Sjmallettstatic struct 	ida_board *ida_eisa_match(eisa_id_t);
200228063Sbaptstatic int	ida_eisa_probe(device_t);
20195060Sjmallettstatic int	ida_eisa_attach(device_t);
20295060Sjmallett
203228063Sbaptstatic device_method_t ida_eisa_methods[] = {
204228063Sbapt	DEVMETHOD(device_probe,		ida_eisa_probe),
20595060Sjmallett	DEVMETHOD(device_attach,	ida_eisa_attach),
20695060Sjmallett	DEVMETHOD(device_detach,	ida_detach),
20795060Sjmallett
20895060Sjmallett	{ 0, 0 }
20995060Sjmallett};
21095060Sjmallett
21195060Sjmallettstatic driver_t ida_eisa_driver = {
21295060Sjmallett	"ida",
21395060Sjmallett	ida_eisa_methods,
2141590Srgrimes	sizeof(struct ida_softc)
2151590Srgrimes};
2161590Srgrimes
21795887Sjmallettstatic devclass_t ida_devclass;
2181590Srgrimes
219100014Sjmallettstatic struct ida_board *
22095060Sjmallettida_eisa_match(eisa_id_t id)
22195060Sjmallett{
2221590Srgrimes	int i;
2231590Srgrimes
2241590Srgrimes	for (i = 0; board_id[i].board; i++)
2251590Srgrimes		if (board_id[i].board == id)
2261590Srgrimes			return (&board_id[i]);
2271590Srgrimes	return (NULL);
22895887Sjmallett}
2291590Srgrimes
23095060Sjmallettstatic int
2311590Srgrimesida_eisa_probe(device_t dev)
2321590Srgrimes{
233228063Sbapt	struct ida_board	*board;
23495060Sjmallett	u_int32_t		io_base;
23595060Sjmallett	u_int			irq = 0;
23695060Sjmallett
2371590Srgrimes	board = ida_eisa_match(eisa_get_id(dev));
2381590Srgrimes	if (board == NULL)
2391590Srgrimes		return (ENXIO);
2401590Srgrimes	device_set_desc(dev, board->desc);
2411590Srgrimes
242228063Sbapt	io_base = (eisa_get_slot(dev) * EISA_SLOT_SIZE);
2431590Srgrimes
24495060Sjmallett	switch (IDA_EISA_IRQ_MASK & (inb(IDA_EISA_IRQ_REG + io_base))) {
24595060Sjmallett	case IDA_EISA_IRQ_15:
24695060Sjmallett		irq = 15;
2471590Srgrimes		break;
2481590Srgrimes	case IDA_EISA_IRQ_14:
2491590Srgrimes		irq = 14;
2501590Srgrimes		break;
2511590Srgrimes	case IDA_EISA_IRQ_11:
2521590Srgrimes		irq = 11;
25399939Sjmallett		break;
2541590Srgrimes	case IDA_EISA_IRQ_10:
25595060Sjmallett		irq = 10;
2561590Srgrimes		break;
25795060Sjmallett	default:
2581590Srgrimes		device_printf(dev, "slot %d, illegal irq setting.\n",
2591590Srgrimes		    eisa_get_slot(dev));
2601590Srgrimes		return (ENXIO);
2611590Srgrimes	}
2621590Srgrimes
263228063Sbapt	eisa_add_iospace(dev, (io_base + IDA_EISA_IOPORT_START),
264228063Sbapt			 IDA_EISA_IOPORT_LEN, RESVADDR_NONE);
265228063Sbapt
266228063Sbapt	eisa_add_intr(dev, irq, EISA_TRIGGER_LEVEL);		/* XXX ??? */
267228063Sbapt
268228063Sbapt	return (0);
269228063Sbapt}
270228063Sbapt
271228063Sbaptstatic int
272228063Sbaptida_eisa_attach(device_t dev)
273228063Sbapt{
274228063Sbapt	struct ida_softc	*ida;
275228063Sbapt	struct ida_board	*board;
276228063Sbapt	int			error;
277228063Sbapt	int			rid;
278228063Sbapt
279228063Sbapt	ida = device_get_softc(dev);
280228063Sbapt	ida->dev = dev;
28195060Sjmallett
28295060Sjmallett	board = ida_eisa_match(eisa_get_id(dev));
2831590Srgrimes	ida->cmd = *board->accessor;
28495887Sjmallett	ida->flags = board->flags;
28575551Sgshapiro	mtx_init(&ida->lock, "ida", NULL, MTX_DEF);
28695060Sjmallett	callout_init_mtx(&ida->ch, &ida->lock, 0);
28795060Sjmallett
288228063Sbapt	ida->regs_res_type = SYS_RES_IOPORT;
289228063Sbapt	ida->regs_res_id = 0;
29095060Sjmallett	ida->regs = bus_alloc_resource_any(dev, ida->regs_res_type,
29195060Sjmallett	    &ida->regs_res_id, RF_ACTIVE);
29295060Sjmallett	if (ida->regs == NULL) {
29375551Sgshapiro		device_printf(dev, "can't allocate register resources\n");
29475551Sgshapiro		return (ENOMEM);
29595060Sjmallett	}
296228063Sbapt
29795060Sjmallett	error = bus_dma_tag_create(
298228063Sbapt		/* parent	*/	bus_get_dma_tag(dev),
29995060Sjmallett		/* alignment	*/	1,
300228063Sbapt		/* boundary	*/	0,
301228063Sbapt		/* lowaddr	*/	BUS_SPACE_MAXADDR_32BIT,
302228063Sbapt		/* highaddr	*/	BUS_SPACE_MAXADDR,
303228063Sbapt		/* filter	*/	NULL,
304228063Sbapt		/* filterarg	*/	NULL,
305228063Sbapt		/* maxsize	*/	BUS_SPACE_MAXSIZE_32BIT,
306228063Sbapt		/* nsegments	*/	BUS_SPACE_UNRESTRICTED,
307228063Sbapt		/* maxsegsize	*/	BUS_SPACE_MAXSIZE_32BIT,
308228063Sbapt		/* flags	*/	BUS_DMA_ALLOCNOW,
309228063Sbapt		/* lockfunc	*/	NULL,
310228063Sbapt		/* lockarg	*/	NULL,
31195060Sjmallett		&ida->parent_dmat);
31295060Sjmallett
31395060Sjmallett	if (error != 0) {
314228063Sbapt		device_printf(dev, "can't allocate DMA tag\n");
315228063Sbapt		ida_free(ida);
316228063Sbapt		return (ENOMEM);
317228063Sbapt	}
318228063Sbapt
319228063Sbapt	rid = 0;
320228063Sbapt	ida->irq_res_type = SYS_RES_IRQ;
321228063Sbapt	ida->irq = bus_alloc_resource_any(dev, ida->irq_res_type, &rid,
322228063Sbapt	    RF_ACTIVE | RF_SHAREABLE);
323228063Sbapt	if (ida->irq == NULL) {
324228063Sbapt		ida_free(ida);
325228063Sbapt		return (ENOMEM);
326228063Sbapt	}
327228063Sbapt
328228063Sbapt	error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY | INTR_MPSAFE,
329228063Sbapt	    NULL, ida_intr, ida, &ida->ih);
330228063Sbapt	if (error) {
331228063Sbapt		device_printf(dev, "can't setup interrupt\n");
332228063Sbapt		ida_free(ida);
333228063Sbapt		return (ENOMEM);
33495060Sjmallett	}
33595887Sjmallett
33695060Sjmallett	error = ida_init(ida);
33795060Sjmallett	if (error) {
33895060Sjmallett		ida_free(ida);
33995060Sjmallett		return (error);
34095060Sjmallett	}
34195060Sjmallett
34295060Sjmallett	return (0);
34375551Sgshapiro}
34499939Sjmallett
3451590SrgrimesDRIVER_MODULE(ida, eisa, ida_eisa_driver, ida_devclass, 0, 0);
346228063Sbapt