ida_eisa.c revision 129879
157828Sjlemon/*
257828Sjlemon * Copyright (c) 2000 Jonathan Lemon
357828Sjlemon * Copyright (c) 1999 by Matthew N. Dodd <winter@jurai.net>
457828Sjlemon * All Rights Reserved.
557828Sjlemon *
657828Sjlemon * Redistribution and use in source and binary forms, with or without
757828Sjlemon * modification, are permitted provided that the following conditions
857828Sjlemon * are met:
957828Sjlemon * 1. Redistributions of source code must retain the above copyright
1057828Sjlemon *    notice, this list of conditions, and the following disclaimer,
1157828Sjlemon *    without modification, immediately at the beginning of the file.
1257828Sjlemon * 2. The name of the author may not be used to endorse or promote products
1357828Sjlemon *    derived from this software without specific prior written permission.
1457828Sjlemon *
1557828Sjlemon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1657828Sjlemon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1757828Sjlemon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1857828Sjlemon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
1957828Sjlemon * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2057828Sjlemon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2157828Sjlemon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2257828Sjlemon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2357828Sjlemon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2457828Sjlemon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2557828Sjlemon * SUCH DAMAGE.
2657828Sjlemon */
2757828Sjlemon
28119418Sobrien#include <sys/cdefs.h>
29119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ida/ida_eisa.c 129879 2004-05-30 20:08:47Z phk $");
30119418Sobrien
3157828Sjlemon#include <sys/param.h>
3257828Sjlemon#include <sys/systm.h>
3357828Sjlemon#include <sys/kernel.h>
34129879Sphk#include <sys/module.h>
3557828Sjlemon#include <sys/bus.h>
3657828Sjlemon
3760041Sphk#include <sys/bio.h>
38111337Sphk#include <sys/conf.h>
3957828Sjlemon
4057828Sjlemon#include <machine/bus_pio.h>
4157828Sjlemon#include <machine/bus.h>
4257828Sjlemon#include <machine/resource.h>
4357828Sjlemon#include <sys/rman.h>
4457828Sjlemon
45112946Sphk#include <geom/geom_disk.h>
46112946Sphk
4757828Sjlemon#include <dev/ida/idavar.h>
4857828Sjlemon#include <dev/ida/idareg.h>
4957828Sjlemon
5057828Sjlemon#include <dev/eisa/eisaconf.h>
5157828Sjlemon
52124471Smdodd#define	IDA_EISA_IOPORT_START	0x0c88
53124471Smdodd#define	IDA_EISA_IOPORT_LEN	0x0017
5457828Sjlemon
55124471Smdodd#define	IDA_EISA_IRQ_REG	0x0cc0
56124471Smdodd#define	IDA_EISA_IRQ_MASK	0xf0
57124471Smdodd#define	IDA_EISA_IRQ_15		0x80
58124471Smdodd#define	IDA_EISA_IRQ_14		0x40
59124471Smdodd#define	IDA_EISA_IRQ_11		0x10
60124471Smdodd#define	IDA_EISA_IRQ_10		0x20
6157828Sjlemon
6257828Sjlemonstatic int
6357828Sjlemonida_v1_fifo_full(struct ida_softc *ida)
6457828Sjlemon{
6557828Sjlemon	u_int8_t status;
6657828Sjlemon
6757828Sjlemon	status = ida_inb(ida, R_EISA_SYSTEM_DOORBELL);
6857828Sjlemon	return ((status & EISA_CHANNEL_CLEAR) == 0);
6957828Sjlemon}
7057828Sjlemon
7157828Sjlemonstatic void
7257828Sjlemonida_v1_submit(struct ida_softc *ida, struct ida_qcb *qcb)
7357828Sjlemon{
7457828Sjlemon	u_int16_t size;
7557828Sjlemon
7657828Sjlemon	/*
7757828Sjlemon	 * On these cards, this location is actually for control flags.
7857828Sjlemon	 * Set them to zero and pass in structure size via an I/O port.
7957828Sjlemon	 */
8057828Sjlemon	size = qcb->hwqcb->hdr.size << 2;
8157828Sjlemon	qcb->hwqcb->hdr.size = 0;
8257828Sjlemon
8357828Sjlemon	ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_CLEAR);
8457828Sjlemon	ida_outl(ida, R_EISA_LIST_ADDR, qcb->hwqcb_busaddr);
8557828Sjlemon	ida_outw(ida, R_EISA_LIST_LEN, size);
8659209Smdodd	ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
8757828Sjlemon}
8857828Sjlemon
8957828Sjlemonstatic bus_addr_t
9057828Sjlemonida_v1_done(struct ida_softc *ida)
9157828Sjlemon{
9257828Sjlemon	struct ida_hardware_qcb *hwqcb;
9357828Sjlemon	bus_addr_t completed;
9457828Sjlemon	u_int8_t status;
9557828Sjlemon
9657828Sjlemon	if ((ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY) == 0)
9757828Sjlemon		return (0);
9857828Sjlemon
9957828Sjlemon	ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_BUSY);
10057828Sjlemon	completed = ida_inl(ida, R_EISA_COMPLETE_ADDR);
10157828Sjlemon	status = ida_inb(ida, R_EISA_LIST_STATUS);
10257828Sjlemon	ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_CLEAR);
10357828Sjlemon
10457828Sjlemon	if (completed != 0) {
10557828Sjlemon        	hwqcb = (struct ida_hardware_qcb *)
10657828Sjlemon		    ((bus_addr_t)ida->hwqcbs +
10757828Sjlemon		    ((completed & ~3) - ida->hwqcb_busaddr));
10857828Sjlemon		hwqcb->req.error = status;
10957828Sjlemon	}
11057828Sjlemon
11157828Sjlemon	return (completed);
11257828Sjlemon}
11357828Sjlemon
11457828Sjlemonstatic int
11557828Sjlemonida_v1_int_pending(struct ida_softc *ida)
11657828Sjlemon{
11757828Sjlemon	return (ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY);
11857828Sjlemon}
11957828Sjlemon
12057828Sjlemonstatic void
12157828Sjlemonida_v1_int_enable(struct ida_softc *ida, int enable)
12257828Sjlemon{
12357828Sjlemon	if (enable) {
12457828Sjlemon		ida_outb(ida, R_EISA_SYSTEM_DOORBELL, ~EISA_CHANNEL_CLEAR);
12557828Sjlemon		ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
12657828Sjlemon		ida_outb(ida, R_EISA_INT_MASK, INT_ENABLE);
12757828Sjlemon		ida_outb(ida, R_EISA_SYSTEM_MASK, INT_ENABLE);
12873113Sjlemon		ida->flags |= IDA_INTERRUPTS;
12957828Sjlemon	} else {
13057828Sjlemon		ida_outb(ida, R_EISA_SYSTEM_MASK, INT_DISABLE);
13173113Sjlemon		ida->flags &= ~IDA_INTERRUPTS;
13257828Sjlemon	}
13357828Sjlemon}
13457828Sjlemon
13557828Sjlemonstatic int
13657828Sjlemonida_v2_fifo_full(struct ida_softc *ida)
13757828Sjlemon{
13857828Sjlemon	return (ida_inl(ida, R_CMD_FIFO) == 0);
13957828Sjlemon}
14057828Sjlemon
14157828Sjlemonstatic void
14257828Sjlemonida_v2_submit(struct ida_softc *ida, struct ida_qcb *qcb)
14357828Sjlemon{
14457828Sjlemon	ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr);
14557828Sjlemon}
14657828Sjlemon
14757828Sjlemonstatic bus_addr_t
14857828Sjlemonida_v2_done(struct ida_softc *ida)
14957828Sjlemon{
15057828Sjlemon	return (ida_inl(ida, R_DONE_FIFO));
15157828Sjlemon}
15257828Sjlemon
15357828Sjlemonstatic int
15457828Sjlemonida_v2_int_pending(struct ida_softc *ida)
15557828Sjlemon{
15657828Sjlemon	return (ida_inl(ida, R_INT_PENDING));
15757828Sjlemon}
15857828Sjlemon
15957828Sjlemonstatic void
16057828Sjlemonida_v2_int_enable(struct ida_softc *ida, int enable)
16157828Sjlemon{
16273113Sjlemon	if (enable)
16373113Sjlemon		ida->flags |= IDA_INTERRUPTS;
16473113Sjlemon	else
16573113Sjlemon		ida->flags &= ~IDA_INTERRUPTS;
16657828Sjlemon	ida_outl(ida, R_INT_MASK, enable ? INT_ENABLE : INT_DISABLE);
16757828Sjlemon}
16857828Sjlemon
16957828Sjlemonstatic struct ida_access ida_v1_access = {
17057828Sjlemon	ida_v1_fifo_full,
17157828Sjlemon	ida_v1_submit,
17257828Sjlemon	ida_v1_done,
17357828Sjlemon	ida_v1_int_pending,
17457828Sjlemon	ida_v1_int_enable,
17557828Sjlemon};
17657828Sjlemon
17757828Sjlemonstatic struct ida_access ida_v2_access = {
17857828Sjlemon	ida_v2_fifo_full,
17957828Sjlemon	ida_v2_submit,
18057828Sjlemon	ida_v2_done,
18157828Sjlemon	ida_v2_int_pending,
18257828Sjlemon	ida_v2_int_enable,
18357828Sjlemon};
18457828Sjlemon
18557828Sjlemonstatic struct ida_board board_id[] = {
18670845Sjlemon	{ 0x0e114001, "Compaq IDA controller",
18770845Sjlemon	    &ida_v1_access, 0 },
18870845Sjlemon	{ 0x0e114002, "Compaq IDA-2 controller",
18970845Sjlemon	    &ida_v1_access, 0 },
19070845Sjlemon	{ 0x0e114010, "Compaq IAES controller",
19170845Sjlemon	    &ida_v1_access, 0 },
19270845Sjlemon	{ 0x0e114020, "Compaq SMART array controller",
19370845Sjlemon	    &ida_v1_access, 0 },
19470845Sjlemon	{ 0x0e114030, "Compaq SMART-2/E array controller",
19570845Sjlemon	    &ida_v2_access, 0 },
19657828Sjlemon
19770845Sjlemon	{ 0, "", 0, 0 }
19857828Sjlemon};
19957828Sjlemon
20057828Sjlemonstatic struct 	ida_board *ida_eisa_match(eisa_id_t);
20157828Sjlemonstatic int	ida_eisa_probe(device_t);
20257828Sjlemonstatic int	ida_eisa_attach(device_t);
20357828Sjlemon
20457828Sjlemonstatic device_method_t ida_eisa_methods[] = {
20557828Sjlemon	DEVMETHOD(device_probe,		ida_eisa_probe),
20657828Sjlemon	DEVMETHOD(device_attach,	ida_eisa_attach),
20757828Sjlemon	DEVMETHOD(device_detach,	ida_detach),
20857828Sjlemon
20957828Sjlemon	{ 0, 0 }
21057828Sjlemon};
21157828Sjlemon
21257828Sjlemonstatic driver_t ida_eisa_driver = {
21357828Sjlemon	"ida",
21457828Sjlemon	ida_eisa_methods,
21557828Sjlemon	sizeof(struct ida_softc)
21657828Sjlemon};
21757828Sjlemon
21857828Sjlemonstatic devclass_t ida_devclass;
21957828Sjlemon
22057828Sjlemonstatic struct ida_board *
22157828Sjlemonida_eisa_match(eisa_id_t id)
22257828Sjlemon{
22357828Sjlemon	int i;
22457828Sjlemon
22557828Sjlemon	for (i = 0; board_id[i].board; i++)
22657828Sjlemon		if (board_id[i].board == id)
22757828Sjlemon			return (&board_id[i]);
22857828Sjlemon	return (NULL);
22957828Sjlemon}
23057828Sjlemon
23157828Sjlemonstatic int
23257828Sjlemonida_eisa_probe(device_t dev)
23357828Sjlemon{
23457828Sjlemon	struct ida_board	*board;
23557828Sjlemon	u_int32_t		io_base;
23657828Sjlemon	u_int			irq = 0;
23757828Sjlemon
23857828Sjlemon	board = ida_eisa_match(eisa_get_id(dev));
23957828Sjlemon	if (board == NULL)
24057828Sjlemon		return (ENXIO);
24157828Sjlemon	device_set_desc(dev, board->desc);
24257828Sjlemon
24357828Sjlemon	io_base = (eisa_get_slot(dev) * EISA_SLOT_SIZE);
24457828Sjlemon
24557828Sjlemon	switch (IDA_EISA_IRQ_MASK & (inb(IDA_EISA_IRQ_REG + io_base))) {
24657828Sjlemon	case IDA_EISA_IRQ_15:
24757828Sjlemon		irq = 15;
24857828Sjlemon		break;
24957828Sjlemon	case IDA_EISA_IRQ_14:
25057828Sjlemon		irq = 14;
25157828Sjlemon		break;
25257828Sjlemon	case IDA_EISA_IRQ_11:
25357828Sjlemon		irq = 11;
25457828Sjlemon		break;
25557828Sjlemon	case IDA_EISA_IRQ_10:
25657828Sjlemon		irq = 10;
25757828Sjlemon		break;
25857828Sjlemon	default:
25957828Sjlemon		device_printf(dev, "slot %d, illegal irq setting.\n",
26057828Sjlemon		    eisa_get_slot(dev));
26157828Sjlemon		return (ENXIO);
26257828Sjlemon	}
26357828Sjlemon
26457828Sjlemon	eisa_add_iospace(dev, (io_base + IDA_EISA_IOPORT_START),
26557828Sjlemon			 IDA_EISA_IOPORT_LEN, RESVADDR_NONE);
26657828Sjlemon
26757828Sjlemon        eisa_add_intr(dev, irq, EISA_TRIGGER_LEVEL);		/* XXX ??? */
26857828Sjlemon
26957828Sjlemon	return (0);
27057828Sjlemon}
27157828Sjlemon
27257828Sjlemonstatic int
27357828Sjlemonida_eisa_attach(device_t dev)
27457828Sjlemon{
27557828Sjlemon	struct ida_softc	*ida;
27657828Sjlemon	struct ida_board	*board;
27757828Sjlemon	int			error;
27857828Sjlemon	int			rid;
27957828Sjlemon
28057828Sjlemon	ida = device_get_softc(dev);
28157828Sjlemon	ida->dev = dev;
28257828Sjlemon
28357828Sjlemon	board = ida_eisa_match(eisa_get_id(dev));
28457828Sjlemon	ida->cmd = *board->accessor;
28570845Sjlemon	ida->flags = board->flags;
28657828Sjlemon
28757828Sjlemon	ida->regs_res_type = SYS_RES_IOPORT;
28857828Sjlemon	ida->regs_res_id = 0;
289127135Snjl	ida->regs = bus_alloc_resource_any(dev, ida->regs_res_type,
290127135Snjl	    &ida->regs_res_id, RF_ACTIVE);
29157828Sjlemon	if (ida->regs == NULL) {
29257828Sjlemon		device_printf(dev, "can't allocate register resources\n");
29357828Sjlemon		return (ENOMEM);
29457828Sjlemon	}
29557828Sjlemon
29657828Sjlemon	error = bus_dma_tag_create(
29757828Sjlemon		/* parent	*/	NULL,
29857828Sjlemon		/* alignment	*/	0,
29957828Sjlemon		/* boundary	*/	0,
30057828Sjlemon		/* lowaddr	*/	BUS_SPACE_MAXADDR_32BIT,
30157828Sjlemon		/* highaddr	*/	BUS_SPACE_MAXADDR,
30257828Sjlemon		/* filter	*/	NULL,
30357828Sjlemon		/* filterarg	*/	NULL,
30457828Sjlemon		/* maxsize	*/	MAXBSIZE,
30557828Sjlemon		/* nsegments	*/	IDA_NSEG,
30657828Sjlemon		/* maxsegsize	*/	BUS_SPACE_MAXSIZE_32BIT,
30757828Sjlemon		/* flags	*/	BUS_DMA_ALLOCNOW,
308117126Sscottl		/* lockfunc	*/	NULL,
309117126Sscottl		/* lockarg	*/	NULL,
31057828Sjlemon		&ida->parent_dmat);
31157828Sjlemon
31257828Sjlemon	if (error != 0) {
31357828Sjlemon		device_printf(dev, "can't allocate DMA tag\n");
31457828Sjlemon		ida_free(ida);
31557828Sjlemon		return (ENOMEM);
31657828Sjlemon	}
31757828Sjlemon
31857828Sjlemon	rid = 0;
31957828Sjlemon	ida->irq_res_type = SYS_RES_IRQ;
320127135Snjl	ida->irq = bus_alloc_resource_any(dev, ida->irq_res_type, &rid,
321127135Snjl	    RF_ACTIVE | RF_SHAREABLE);
32257828Sjlemon	if (ida->irq == NULL) {
32357828Sjlemon		ida_free(ida);
32457828Sjlemon		return (ENOMEM);
32557828Sjlemon	}
32657828Sjlemon
32773280Smarkm	error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY,
32857828Sjlemon	    ida_intr, ida, &ida->ih);
32957828Sjlemon	if (error) {
33057828Sjlemon		device_printf(dev, "can't setup interrupt\n");
33157828Sjlemon		ida_free(ida);
33257828Sjlemon		return (ENOMEM);
33357828Sjlemon	}
33457828Sjlemon
33557828Sjlemon	error = ida_init(ida);
33657828Sjlemon	if (error) {
33757828Sjlemon		ida_free(ida);
33857828Sjlemon		return (error);
33957828Sjlemon	}
34057828Sjlemon
34157828Sjlemon	ida_attach(ida);
34273113Sjlemon	ida->flags |= IDA_ATTACHED;
34357828Sjlemon
34457828Sjlemon	return (0);
34557828Sjlemon}
34657828Sjlemon
34757828SjlemonDRIVER_MODULE(ida, eisa, ida_eisa_driver, ida_devclass, 0, 0);
348