ida_eisa.c revision 111337
1218767Sdes/* 298937Sdes * Copyright (c) 2000 Jonathan Lemon 398937Sdes * Copyright (c) 1999 by Matthew N. Dodd <winter@jurai.net> 498937Sdes * All Rights Reserved. 598937Sdes * 698937Sdes * Redistribution and use in source and binary forms, with or without 798937Sdes * modification, are permitted provided that the following conditions 898937Sdes * are met: 998937Sdes * 1. Redistributions of source code must retain the above copyright 1098937Sdes * notice, this list of conditions, and the following disclaimer, 1198937Sdes * without modification, immediately at the beginning of the file. 1298937Sdes * 2. The name of the author may not be used to endorse or promote products 1398937Sdes * derived from this software without specific prior written permission. 14164146Sdes * 1598937Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1698937Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1798937Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1898937Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 1998937Sdes * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2098937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2198937Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2298937Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2398937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2498937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2598937Sdes * SUCH DAMAGE. 2698937Sdes * 2798937Sdes * $FreeBSD: head/sys/dev/ida/ida_eisa.c 111337 2003-02-23 18:45:50Z phk $ 28204861Sdes */ 2998937Sdes 3098937Sdes#include <sys/param.h> 3199060Sdes#include <sys/systm.h> 32113908Sdes#include <sys/kernel.h> 3398937Sdes#include <sys/bus.h> 3498937Sdes 3598937Sdes#include <sys/bio.h> 3698937Sdes#include <sys/devicestat.h> 3798937Sdes#include <sys/conf.h> 3898937Sdes#include <sys/disk.h> 39204861Sdes 4098937Sdes#include <machine/bus_pio.h> 4198937Sdes#include <machine/bus.h> 4298937Sdes#include <machine/resource.h> 4398937Sdes#include <sys/rman.h> 4498937Sdes 4598937Sdes#include <dev/ida/idavar.h> 4698937Sdes#include <dev/ida/idareg.h> 4798937Sdes 4898937Sdes#include <dev/eisa/eisaconf.h> 49162852Sdes 50146998Sdes#define IDA_EISA_IOPORT_START 0x0c88 5198937Sdes#define IDA_EISA_IOPORT_LEN 0x0017 52124208Sdes 5398937Sdes#define IDA_EISA_IRQ_REG 0x0cc0 5498937Sdes#define IDA_EISA_IRQ_MASK 0xf0 5598937Sdes#define IDA_EISA_IRQ_15 0x80 56113908Sdes#define IDA_EISA_IRQ_14 0x40 5798937Sdes#define IDA_EISA_IRQ_11 0x10 5898937Sdes#define IDA_EISA_IRQ_10 0x20 5998937Sdes 6098937Sdesstatic int 61218767Sdesida_v1_fifo_full(struct ida_softc *ida) 6298937Sdes{ 6398937Sdes u_int8_t status; 6498937Sdes 6598937Sdes status = ida_inb(ida, R_EISA_SYSTEM_DOORBELL); 66204861Sdes return ((status & EISA_CHANNEL_CLEAR) == 0); 6798937Sdes} 68162852Sdes 69126274Sdesstatic void 70126274Sdesida_v1_submit(struct ida_softc *ida, struct ida_qcb *qcb) 71126274Sdes{ 72162852Sdes u_int16_t size; 73180750Sdes 74137015Sdes /* 75218767Sdes * On these cards, this location is actually for control flags. 76218767Sdes * Set them to zero and pass in structure size via an I/O port. 77218767Sdes */ 78218767Sdes size = qcb->hwqcb->hdr.size << 2; 7998937Sdes qcb->hwqcb->hdr.size = 0; 80113908Sdes 81197670Sdes ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_CLEAR); 82204861Sdes ida_outl(ida, R_EISA_LIST_ADDR, qcb->hwqcb_busaddr); 8398937Sdes ida_outw(ida, R_EISA_LIST_LEN, size); 84113908Sdes ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY); 85218767Sdes} 86137015Sdes 87113908Sdesstatic bus_addr_t 88113908Sdesida_v1_done(struct ida_softc *ida) 89113908Sdes{ 90189006Sdes struct ida_hardware_qcb *hwqcb; 91218767Sdes bus_addr_t completed; 92124208Sdes u_int8_t status; 93124208Sdes 94146998Sdes if ((ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY) == 0) 95218767Sdes return (0); 96204861Sdes 9798937Sdes ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_BUSY); 98204861Sdes completed = ida_inl(ida, R_EISA_COMPLETE_ADDR); 99204861Sdes status = ida_inb(ida, R_EISA_LIST_STATUS); 10098937Sdes ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_CLEAR); 10198937Sdes 10298937Sdes if (completed != 0) { 10398937Sdes hwqcb = (struct ida_hardware_qcb *) 10498937Sdes ((bus_addr_t)ida->hwqcbs + 10598937Sdes ((completed & ~3) - ida->hwqcb_busaddr)); 106113908Sdes hwqcb->req.error = status; 107113908Sdes } 108113908Sdes 109113908Sdes return (completed); 110113908Sdes} 111113908Sdes 112113908Sdesstatic int 113218767Sdesida_v1_int_pending(struct ida_softc *ida) 114113908Sdes{ 115113908Sdes return (ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY); 116113908Sdes} 117180750Sdes 118113908Sdesstatic void 119162852Sdesida_v1_int_enable(struct ida_softc *ida, int enable) 120113908Sdes{ 121113908Sdes if (enable) { 122113908Sdes ida_outb(ida, R_EISA_SYSTEM_DOORBELL, ~EISA_CHANNEL_CLEAR); 12398937Sdes ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY); 124113908Sdes ida_outb(ida, R_EISA_INT_MASK, INT_ENABLE); 12598937Sdes ida_outb(ida, R_EISA_SYSTEM_MASK, INT_ENABLE); 126124208Sdes ida->flags |= IDA_INTERRUPTS; 12798937Sdes } else { 128124208Sdes ida_outb(ida, R_EISA_SYSTEM_MASK, INT_DISABLE); 129124208Sdes ida->flags &= ~IDA_INTERRUPTS; 130124208Sdes } 13198937Sdes} 13298937Sdes 13398937Sdesstatic int 13498937Sdesida_v2_fifo_full(struct ida_softc *ida) 13598937Sdes{ 13698937Sdes return (ida_inl(ida, R_CMD_FIFO) == 0); 13798937Sdes} 13898937Sdes 13998937Sdesstatic void 14098937Sdesida_v2_submit(struct ida_softc *ida, struct ida_qcb *qcb) 14198937Sdes{ 14298937Sdes ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr); 14398937Sdes} 14498937Sdes 14598937Sdesstatic bus_addr_t 14698937Sdesida_v2_done(struct ida_softc *ida) 14798937Sdes{ 148180744Sdes return (ida_inl(ida, R_DONE_FIFO)); 14998937Sdes} 150113908Sdes 151157016Sdesstatic int 15298937Sdesida_v2_int_pending(struct ida_softc *ida) 15398937Sdes{ 154126274Sdes return (ida_inl(ida, R_INT_PENDING)); 15598937Sdes} 156204861Sdes 157204861Sdesstatic void 15898937Sdesida_v2_int_enable(struct ida_softc *ida, int enable) 15998937Sdes{ 160126274Sdes if (enable) 16198937Sdes ida->flags |= IDA_INTERRUPTS; 162204861Sdes else 163197670Sdes ida->flags &= ~IDA_INTERRUPTS; 16498937Sdes ida_outl(ida, R_INT_MASK, enable ? INT_ENABLE : INT_DISABLE); 165204861Sdes} 166214979Sdes 167204861Sdesstatic struct ida_access ida_v1_access = { 168197670Sdes ida_v1_fifo_full, 169197670Sdes ida_v1_submit, 17098937Sdes ida_v1_done, 171180746Sdes ida_v1_int_pending, 172180746Sdes ida_v1_int_enable, 17398937Sdes}; 174126274Sdes 175146998Sdesstatic struct ida_access ida_v2_access = { 17698937Sdes ida_v2_fifo_full, 17798937Sdes ida_v2_submit, 17898937Sdes ida_v2_done, 17998937Sdes ida_v2_int_pending, 18098937Sdes ida_v2_int_enable, 18198937Sdes}; 18298937Sdes 18398937Sdesstatic struct ida_board board_id[] = { 18498937Sdes { 0x0e114001, "Compaq IDA controller", 18598937Sdes &ida_v1_access, 0 }, 18698937Sdes { 0x0e114002, "Compaq IDA-2 controller", 18798937Sdes &ida_v1_access, 0 }, 18898937Sdes { 0x0e114010, "Compaq IAES controller", 18998937Sdes &ida_v1_access, 0 }, 19098937Sdes { 0x0e114020, "Compaq SMART array controller", 191124208Sdes &ida_v1_access, 0 }, 19298937Sdes { 0x0e114030, "Compaq SMART-2/E array controller", 19398937Sdes &ida_v2_access, 0 }, 19498937Sdes 19598937Sdes { 0, "", 0, 0 } 19698937Sdes}; 19798937Sdes 19898937Sdesstatic struct ida_board *ida_eisa_match(eisa_id_t); 19998937Sdesstatic int ida_eisa_probe(device_t); 200124208Sdesstatic int ida_eisa_attach(device_t); 201124208Sdes 202124208Sdesstatic device_method_t ida_eisa_methods[] = { 203124208Sdes DEVMETHOD(device_probe, ida_eisa_probe), 204124208Sdes DEVMETHOD(device_attach, ida_eisa_attach), 205149749Sdes DEVMETHOD(device_detach, ida_detach), 206124208Sdes 207124208Sdes { 0, 0 } 208124208Sdes}; 209124208Sdes 210126274Sdesstatic driver_t ida_eisa_driver = { 211146998Sdes "ida", 21298937Sdes ida_eisa_methods, 21398937Sdes sizeof(struct ida_softc) 214124208Sdes}; 215126274Sdes 216162852Sdesstatic devclass_t ida_devclass; 217162852Sdes 218162852Sdesstatic struct ida_board * 21998937Sdesida_eisa_match(eisa_id_t id) 22098937Sdes{ 221137015Sdes int i; 222137015Sdes 223137015Sdes for (i = 0; board_id[i].board; i++) 22498937Sdes if (board_id[i].board == id) 225124208Sdes return (&board_id[i]); 22698937Sdes return (NULL); 22798937Sdes} 228124208Sdes 22998937Sdesstatic int 230124208Sdesida_eisa_probe(device_t dev) 231124208Sdes{ 23298937Sdes struct ida_board *board; 23398937Sdes u_int32_t io_base; 23498937Sdes u_int irq = 0; 23598937Sdes 236218767Sdes board = ida_eisa_match(eisa_get_id(dev)); 23798937Sdes if (board == NULL) 23898937Sdes return (ENXIO); 23998937Sdes device_set_desc(dev, board->desc); 24098937Sdes 24198937Sdes io_base = (eisa_get_slot(dev) * EISA_SLOT_SIZE); 242126274Sdes 24398937Sdes switch (IDA_EISA_IRQ_MASK & (inb(IDA_EISA_IRQ_REG + io_base))) { 244146998Sdes case IDA_EISA_IRQ_15: 245146998Sdes irq = 15; 246146998Sdes break; 24798937Sdes case IDA_EISA_IRQ_14: 248106121Sdes irq = 14; 249106121Sdes break; 25099060Sdes case IDA_EISA_IRQ_11: 251204861Sdes irq = 11; 25298937Sdes break; 25398937Sdes case IDA_EISA_IRQ_10: 25498937Sdes irq = 10; 25598937Sdes break; 25698937Sdes default: 25798937Sdes device_printf(dev, "slot %d, illegal irq setting.\n", 25898937Sdes eisa_get_slot(dev)); 259106121Sdes return (ENXIO); 260207311Sdes } 261207311Sdes 262207311Sdes eisa_add_iospace(dev, (io_base + IDA_EISA_IOPORT_START), 263207311Sdes IDA_EISA_IOPORT_LEN, RESVADDR_NONE); 264207311Sdes 265207311Sdes eisa_add_intr(dev, irq, EISA_TRIGGER_LEVEL); /* XXX ??? */ 266207311Sdes 26798937Sdes return (0); 268207311Sdes} 26998937Sdes 270207311Sdesstatic int 271207311Sdesida_eisa_attach(device_t dev) 272207311Sdes{ 273207311Sdes struct ida_softc *ida; 27498937Sdes struct ida_board *board; 27598937Sdes int error; 27698937Sdes int rid; 27798937Sdes 27898937Sdes ida = device_get_softc(dev); 27998937Sdes ida->dev = dev; 280180750Sdes 28198937Sdes board = ida_eisa_match(eisa_get_id(dev)); 28298937Sdes ida->cmd = *board->accessor; 28398937Sdes ida->flags = board->flags; 28499060Sdes 28598937Sdes ida->regs_res_type = SYS_RES_IOPORT; 28698937Sdes ida->regs_res_id = 0; 287113908Sdes ida->regs = bus_alloc_resource(dev, ida->regs_res_type, 288113908Sdes &ida->regs_res_id, 0, ~0, 1, RF_ACTIVE); 28998937Sdes if (ida->regs == NULL) { 290204861Sdes device_printf(dev, "can't allocate register resources\n"); 29198937Sdes return (ENOMEM); 292106121Sdes } 29398937Sdes 294106121Sdes error = bus_dma_tag_create( 295146998Sdes /* parent */ NULL, 296146998Sdes /* alignment */ 0, 29798937Sdes /* boundary */ 0, 29898937Sdes /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 29998937Sdes /* highaddr */ BUS_SPACE_MAXADDR, 300106121Sdes /* filter */ NULL, 30198937Sdes /* filterarg */ NULL, 30298937Sdes /* maxsize */ MAXBSIZE, 30398937Sdes /* nsegments */ IDA_NSEG, 30498937Sdes /* maxsegsize */ BUS_SPACE_MAXSIZE_32BIT, 305106121Sdes /* flags */ BUS_DMA_ALLOCNOW, 30698937Sdes &ida->parent_dmat); 30798937Sdes 30898937Sdes if (error != 0) { 30998937Sdes device_printf(dev, "can't allocate DMA tag\n"); 310146998Sdes ida_free(ida); 31198937Sdes return (ENOMEM); 31298937Sdes } 31398937Sdes 31498937Sdes rid = 0; 31598937Sdes ida->irq_res_type = SYS_RES_IRQ; 31698937Sdes ida->irq = bus_alloc_resource(dev, ida->irq_res_type, &rid, 317106121Sdes 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 31898937Sdes if (ida->irq == NULL) { 31998937Sdes ida_free(ida); 32098937Sdes return (ENOMEM); 32198937Sdes } 32298937Sdes 32398937Sdes error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY, 32498937Sdes ida_intr, ida, &ida->ih); 32598937Sdes if (error) { 32698937Sdes device_printf(dev, "can't setup interrupt\n"); 32798937Sdes ida_free(ida); 32898937Sdes return (ENOMEM); 329106121Sdes } 330218767Sdes 331218767Sdes error = ida_init(ida); 33298937Sdes if (error) { 333218767Sdes ida_free(ida); 33498937Sdes return (error); 335218767Sdes } 336218767Sdes 33798937Sdes ida_attach(ida); 338218767Sdes ida->flags |= IDA_ATTACHED; 33998937Sdes 340218767Sdes return (0); 341218767Sdes} 34298937Sdes 343218767SdesDRIVER_MODULE(ida, eisa, ida_eisa_driver, ida_devclass, 0, 0); 34498937Sdes