1139749Simp/*-
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$");
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.h>
4157828Sjlemon#include <machine/resource.h>
4257828Sjlemon#include <sys/rman.h>
4357828Sjlemon
44112946Sphk#include <geom/geom_disk.h>
45112946Sphk
4657828Sjlemon#include <dev/ida/idavar.h>
4757828Sjlemon#include <dev/ida/idareg.h>
4857828Sjlemon
4957828Sjlemon#include <dev/eisa/eisaconf.h>
5057828Sjlemon
51124471Smdodd#define	IDA_EISA_IOPORT_START	0x0c88
52124471Smdodd#define	IDA_EISA_IOPORT_LEN	0x0017
5357828Sjlemon
54124471Smdodd#define	IDA_EISA_IRQ_REG	0x0cc0
55124471Smdodd#define	IDA_EISA_IRQ_MASK	0xf0
56124471Smdodd#define	IDA_EISA_IRQ_15		0x80
57124471Smdodd#define	IDA_EISA_IRQ_14		0x40
58124471Smdodd#define	IDA_EISA_IRQ_11		0x10
59124471Smdodd#define	IDA_EISA_IRQ_10		0x20
6057828Sjlemon
6157828Sjlemonstatic int
6257828Sjlemonida_v1_fifo_full(struct ida_softc *ida)
6357828Sjlemon{
6457828Sjlemon	u_int8_t status;
6557828Sjlemon
6657828Sjlemon	status = ida_inb(ida, R_EISA_SYSTEM_DOORBELL);
6757828Sjlemon	return ((status & EISA_CHANNEL_CLEAR) == 0);
6857828Sjlemon}
6957828Sjlemon
7057828Sjlemonstatic void
7157828Sjlemonida_v1_submit(struct ida_softc *ida, struct ida_qcb *qcb)
7257828Sjlemon{
7357828Sjlemon	u_int16_t size;
7457828Sjlemon
7557828Sjlemon	/*
7657828Sjlemon	 * On these cards, this location is actually for control flags.
7757828Sjlemon	 * Set them to zero and pass in structure size via an I/O port.
7857828Sjlemon	 */
7957828Sjlemon	size = qcb->hwqcb->hdr.size << 2;
8057828Sjlemon	qcb->hwqcb->hdr.size = 0;
8157828Sjlemon
8257828Sjlemon	ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_CLEAR);
8357828Sjlemon	ida_outl(ida, R_EISA_LIST_ADDR, qcb->hwqcb_busaddr);
8457828Sjlemon	ida_outw(ida, R_EISA_LIST_LEN, size);
8559209Smdodd	ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
8657828Sjlemon}
8757828Sjlemon
8857828Sjlemonstatic bus_addr_t
8957828Sjlemonida_v1_done(struct ida_softc *ida)
9057828Sjlemon{
9157828Sjlemon	struct ida_hardware_qcb *hwqcb;
9257828Sjlemon	bus_addr_t completed;
9357828Sjlemon	u_int8_t status;
9457828Sjlemon
9557828Sjlemon	if ((ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY) == 0)
9657828Sjlemon		return (0);
9757828Sjlemon
9857828Sjlemon	ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_BUSY);
9957828Sjlemon	completed = ida_inl(ida, R_EISA_COMPLETE_ADDR);
10057828Sjlemon	status = ida_inb(ida, R_EISA_LIST_STATUS);
10157828Sjlemon	ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_CLEAR);
10257828Sjlemon
10357828Sjlemon	if (completed != 0) {
104144991Smdodd		hwqcb = (struct ida_hardware_qcb *)
10557828Sjlemon		    ((bus_addr_t)ida->hwqcbs +
10657828Sjlemon		    ((completed & ~3) - ida->hwqcb_busaddr));
10757828Sjlemon		hwqcb->req.error = status;
10857828Sjlemon	}
10957828Sjlemon
11057828Sjlemon	return (completed);
11157828Sjlemon}
11257828Sjlemon
11357828Sjlemonstatic int
11457828Sjlemonida_v1_int_pending(struct ida_softc *ida)
11557828Sjlemon{
11657828Sjlemon	return (ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY);
11757828Sjlemon}
11857828Sjlemon
11957828Sjlemonstatic void
12057828Sjlemonida_v1_int_enable(struct ida_softc *ida, int enable)
12157828Sjlemon{
12257828Sjlemon	if (enable) {
12357828Sjlemon		ida_outb(ida, R_EISA_SYSTEM_DOORBELL, ~EISA_CHANNEL_CLEAR);
12457828Sjlemon		ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
12557828Sjlemon		ida_outb(ida, R_EISA_INT_MASK, INT_ENABLE);
12657828Sjlemon		ida_outb(ida, R_EISA_SYSTEM_MASK, INT_ENABLE);
12773113Sjlemon		ida->flags |= IDA_INTERRUPTS;
12857828Sjlemon	} else {
12957828Sjlemon		ida_outb(ida, R_EISA_SYSTEM_MASK, INT_DISABLE);
13073113Sjlemon		ida->flags &= ~IDA_INTERRUPTS;
13157828Sjlemon	}
13257828Sjlemon}
13357828Sjlemon
13457828Sjlemonstatic int
13557828Sjlemonida_v2_fifo_full(struct ida_softc *ida)
13657828Sjlemon{
13757828Sjlemon	return (ida_inl(ida, R_CMD_FIFO) == 0);
13857828Sjlemon}
13957828Sjlemon
14057828Sjlemonstatic void
14157828Sjlemonida_v2_submit(struct ida_softc *ida, struct ida_qcb *qcb)
14257828Sjlemon{
14357828Sjlemon	ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr);
14457828Sjlemon}
14557828Sjlemon
14657828Sjlemonstatic bus_addr_t
14757828Sjlemonida_v2_done(struct ida_softc *ida)
14857828Sjlemon{
14957828Sjlemon	return (ida_inl(ida, R_DONE_FIFO));
15057828Sjlemon}
15157828Sjlemon
15257828Sjlemonstatic int
15357828Sjlemonida_v2_int_pending(struct ida_softc *ida)
15457828Sjlemon{
15557828Sjlemon	return (ida_inl(ida, R_INT_PENDING));
15657828Sjlemon}
15757828Sjlemon
15857828Sjlemonstatic void
15957828Sjlemonida_v2_int_enable(struct ida_softc *ida, int enable)
16057828Sjlemon{
16173113Sjlemon	if (enable)
16273113Sjlemon		ida->flags |= IDA_INTERRUPTS;
16373113Sjlemon	else
16473113Sjlemon		ida->flags &= ~IDA_INTERRUPTS;
16557828Sjlemon	ida_outl(ida, R_INT_MASK, enable ? INT_ENABLE : INT_DISABLE);
16657828Sjlemon}
16757828Sjlemon
16857828Sjlemonstatic struct ida_access ida_v1_access = {
16957828Sjlemon	ida_v1_fifo_full,
17057828Sjlemon	ida_v1_submit,
17157828Sjlemon	ida_v1_done,
17257828Sjlemon	ida_v1_int_pending,
17357828Sjlemon	ida_v1_int_enable,
17457828Sjlemon};
17557828Sjlemon
17657828Sjlemonstatic struct ida_access ida_v2_access = {
17757828Sjlemon	ida_v2_fifo_full,
17857828Sjlemon	ida_v2_submit,
17957828Sjlemon	ida_v2_done,
18057828Sjlemon	ida_v2_int_pending,
18157828Sjlemon	ida_v2_int_enable,
18257828Sjlemon};
18357828Sjlemon
18457828Sjlemonstatic struct ida_board board_id[] = {
18570845Sjlemon	{ 0x0e114001, "Compaq IDA controller",
18670845Sjlemon	    &ida_v1_access, 0 },
18770845Sjlemon	{ 0x0e114002, "Compaq IDA-2 controller",
18870845Sjlemon	    &ida_v1_access, 0 },
18970845Sjlemon	{ 0x0e114010, "Compaq IAES controller",
19070845Sjlemon	    &ida_v1_access, 0 },
19170845Sjlemon	{ 0x0e114020, "Compaq SMART array controller",
19270845Sjlemon	    &ida_v1_access, 0 },
19370845Sjlemon	{ 0x0e114030, "Compaq SMART-2/E array controller",
19470845Sjlemon	    &ida_v2_access, 0 },
19557828Sjlemon
19670845Sjlemon	{ 0, "", 0, 0 }
19757828Sjlemon};
19857828Sjlemon
19957828Sjlemonstatic struct 	ida_board *ida_eisa_match(eisa_id_t);
20057828Sjlemonstatic int	ida_eisa_probe(device_t);
20157828Sjlemonstatic int	ida_eisa_attach(device_t);
20257828Sjlemon
20357828Sjlemonstatic device_method_t ida_eisa_methods[] = {
20457828Sjlemon	DEVMETHOD(device_probe,		ida_eisa_probe),
20557828Sjlemon	DEVMETHOD(device_attach,	ida_eisa_attach),
20657828Sjlemon	DEVMETHOD(device_detach,	ida_detach),
20757828Sjlemon
20857828Sjlemon	{ 0, 0 }
20957828Sjlemon};
21057828Sjlemon
21157828Sjlemonstatic driver_t ida_eisa_driver = {
21257828Sjlemon	"ida",
21357828Sjlemon	ida_eisa_methods,
21457828Sjlemon	sizeof(struct ida_softc)
21557828Sjlemon};
21657828Sjlemon
21757828Sjlemonstatic devclass_t ida_devclass;
21857828Sjlemon
21957828Sjlemonstatic struct ida_board *
22057828Sjlemonida_eisa_match(eisa_id_t id)
22157828Sjlemon{
22257828Sjlemon	int i;
22357828Sjlemon
22457828Sjlemon	for (i = 0; board_id[i].board; i++)
22557828Sjlemon		if (board_id[i].board == id)
22657828Sjlemon			return (&board_id[i]);
22757828Sjlemon	return (NULL);
22857828Sjlemon}
22957828Sjlemon
23057828Sjlemonstatic int
23157828Sjlemonida_eisa_probe(device_t dev)
23257828Sjlemon{
23357828Sjlemon	struct ida_board	*board;
23457828Sjlemon	u_int32_t		io_base;
23557828Sjlemon	u_int			irq = 0;
23657828Sjlemon
23757828Sjlemon	board = ida_eisa_match(eisa_get_id(dev));
23857828Sjlemon	if (board == NULL)
23957828Sjlemon		return (ENXIO);
24057828Sjlemon	device_set_desc(dev, board->desc);
24157828Sjlemon
24257828Sjlemon	io_base = (eisa_get_slot(dev) * EISA_SLOT_SIZE);
24357828Sjlemon
24457828Sjlemon	switch (IDA_EISA_IRQ_MASK & (inb(IDA_EISA_IRQ_REG + io_base))) {
24557828Sjlemon	case IDA_EISA_IRQ_15:
24657828Sjlemon		irq = 15;
24757828Sjlemon		break;
24857828Sjlemon	case IDA_EISA_IRQ_14:
24957828Sjlemon		irq = 14;
25057828Sjlemon		break;
25157828Sjlemon	case IDA_EISA_IRQ_11:
25257828Sjlemon		irq = 11;
25357828Sjlemon		break;
25457828Sjlemon	case IDA_EISA_IRQ_10:
25557828Sjlemon		irq = 10;
25657828Sjlemon		break;
25757828Sjlemon	default:
25857828Sjlemon		device_printf(dev, "slot %d, illegal irq setting.\n",
25957828Sjlemon		    eisa_get_slot(dev));
26057828Sjlemon		return (ENXIO);
26157828Sjlemon	}
26257828Sjlemon
26357828Sjlemon	eisa_add_iospace(dev, (io_base + IDA_EISA_IOPORT_START),
26457828Sjlemon			 IDA_EISA_IOPORT_LEN, RESVADDR_NONE);
26557828Sjlemon
266144991Smdodd	eisa_add_intr(dev, irq, EISA_TRIGGER_LEVEL);		/* XXX ??? */
26757828Sjlemon
26857828Sjlemon	return (0);
26957828Sjlemon}
27057828Sjlemon
27157828Sjlemonstatic int
27257828Sjlemonida_eisa_attach(device_t dev)
27357828Sjlemon{
27457828Sjlemon	struct ida_softc	*ida;
27557828Sjlemon	struct ida_board	*board;
27657828Sjlemon	int			error;
27757828Sjlemon	int			rid;
27857828Sjlemon
27957828Sjlemon	ida = device_get_softc(dev);
28057828Sjlemon	ida->dev = dev;
28157828Sjlemon
28257828Sjlemon	board = ida_eisa_match(eisa_get_id(dev));
28357828Sjlemon	ida->cmd = *board->accessor;
28470845Sjlemon	ida->flags = board->flags;
285239740Sjhb	mtx_init(&ida->lock, "ida", NULL, MTX_DEF);
286239740Sjhb	callout_init_mtx(&ida->ch, &ida->lock, 0);
28757828Sjlemon
28857828Sjlemon	ida->regs_res_type = SYS_RES_IOPORT;
28957828Sjlemon	ida->regs_res_id = 0;
290127135Snjl	ida->regs = bus_alloc_resource_any(dev, ida->regs_res_type,
291127135Snjl	    &ida->regs_res_id, RF_ACTIVE);
29257828Sjlemon	if (ida->regs == NULL) {
29357828Sjlemon		device_printf(dev, "can't allocate register resources\n");
29457828Sjlemon		return (ENOMEM);
29557828Sjlemon	}
29657828Sjlemon
29757828Sjlemon	error = bus_dma_tag_create(
298239740Sjhb		/* parent	*/	bus_get_dma_tag(dev),
299274188Sian		/* alignment	*/	1,
30057828Sjlemon		/* boundary	*/	0,
30157828Sjlemon		/* lowaddr	*/	BUS_SPACE_MAXADDR_32BIT,
30257828Sjlemon		/* highaddr	*/	BUS_SPACE_MAXADDR,
30357828Sjlemon		/* filter	*/	NULL,
30457828Sjlemon		/* filterarg	*/	NULL,
305280347Smav		/* maxsize	*/	BUS_SPACE_MAXSIZE_32BIT,
306280347Smav		/* nsegments	*/	BUS_SPACE_UNRESTRICTED,
30757828Sjlemon		/* maxsegsize	*/	BUS_SPACE_MAXSIZE_32BIT,
30857828Sjlemon		/* flags	*/	BUS_DMA_ALLOCNOW,
309117126Sscottl		/* lockfunc	*/	NULL,
310117126Sscottl		/* lockarg	*/	NULL,
31157828Sjlemon		&ida->parent_dmat);
31257828Sjlemon
31357828Sjlemon	if (error != 0) {
31457828Sjlemon		device_printf(dev, "can't allocate DMA tag\n");
31557828Sjlemon		ida_free(ida);
31657828Sjlemon		return (ENOMEM);
31757828Sjlemon	}
31857828Sjlemon
31957828Sjlemon	rid = 0;
32057828Sjlemon	ida->irq_res_type = SYS_RES_IRQ;
321127135Snjl	ida->irq = bus_alloc_resource_any(dev, ida->irq_res_type, &rid,
322127135Snjl	    RF_ACTIVE | RF_SHAREABLE);
32357828Sjlemon	if (ida->irq == NULL) {
32457828Sjlemon		ida_free(ida);
32557828Sjlemon		return (ENOMEM);
32657828Sjlemon	}
32757828Sjlemon
328239740Sjhb	error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY | INTR_MPSAFE,
329166901Spiso	    NULL, ida_intr, ida, &ida->ih);
33057828Sjlemon	if (error) {
33157828Sjlemon		device_printf(dev, "can't setup interrupt\n");
33257828Sjlemon		ida_free(ida);
33357828Sjlemon		return (ENOMEM);
33457828Sjlemon	}
33557828Sjlemon
336299471Shselasky	error = ida_setup(ida);
33757828Sjlemon	if (error) {
33857828Sjlemon		ida_free(ida);
33957828Sjlemon		return (error);
34057828Sjlemon	}
34157828Sjlemon
34257828Sjlemon	return (0);
34357828Sjlemon}
34457828Sjlemon
34557828SjlemonDRIVER_MODULE(ida, eisa, ida_eisa_driver, ida_devclass, 0, 0);
346