ida_eisa.c revision 111337
1/* 2 * Copyright (c) 2000 Jonathan Lemon 3 * Copyright (c) 1999 by Matthew N. Dodd <winter@jurai.net> 4 * All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions, and the following disclaimer, 11 * without modification, immediately at the beginning of the file. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: head/sys/dev/ida/ida_eisa.c 111337 2003-02-23 18:45:50Z phk $ 28 */ 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/kernel.h> 33#include <sys/bus.h> 34 35#include <sys/bio.h> 36#include <sys/devicestat.h> 37#include <sys/conf.h> 38#include <sys/disk.h> 39 40#include <machine/bus_pio.h> 41#include <machine/bus.h> 42#include <machine/resource.h> 43#include <sys/rman.h> 44 45#include <dev/ida/idavar.h> 46#include <dev/ida/idareg.h> 47 48#include <dev/eisa/eisaconf.h> 49 50#define IDA_EISA_IOPORT_START 0x0c88 51#define IDA_EISA_IOPORT_LEN 0x0017 52 53#define IDA_EISA_IRQ_REG 0x0cc0 54#define IDA_EISA_IRQ_MASK 0xf0 55#define IDA_EISA_IRQ_15 0x80 56#define IDA_EISA_IRQ_14 0x40 57#define IDA_EISA_IRQ_11 0x10 58#define IDA_EISA_IRQ_10 0x20 59 60static int 61ida_v1_fifo_full(struct ida_softc *ida) 62{ 63 u_int8_t status; 64 65 status = ida_inb(ida, R_EISA_SYSTEM_DOORBELL); 66 return ((status & EISA_CHANNEL_CLEAR) == 0); 67} 68 69static void 70ida_v1_submit(struct ida_softc *ida, struct ida_qcb *qcb) 71{ 72 u_int16_t size; 73 74 /* 75 * On these cards, this location is actually for control flags. 76 * Set them to zero and pass in structure size via an I/O port. 77 */ 78 size = qcb->hwqcb->hdr.size << 2; 79 qcb->hwqcb->hdr.size = 0; 80 81 ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_CLEAR); 82 ida_outl(ida, R_EISA_LIST_ADDR, qcb->hwqcb_busaddr); 83 ida_outw(ida, R_EISA_LIST_LEN, size); 84 ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY); 85} 86 87static bus_addr_t 88ida_v1_done(struct ida_softc *ida) 89{ 90 struct ida_hardware_qcb *hwqcb; 91 bus_addr_t completed; 92 u_int8_t status; 93 94 if ((ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY) == 0) 95 return (0); 96 97 ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_BUSY); 98 completed = ida_inl(ida, R_EISA_COMPLETE_ADDR); 99 status = ida_inb(ida, R_EISA_LIST_STATUS); 100 ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_CLEAR); 101 102 if (completed != 0) { 103 hwqcb = (struct ida_hardware_qcb *) 104 ((bus_addr_t)ida->hwqcbs + 105 ((completed & ~3) - ida->hwqcb_busaddr)); 106 hwqcb->req.error = status; 107 } 108 109 return (completed); 110} 111 112static int 113ida_v1_int_pending(struct ida_softc *ida) 114{ 115 return (ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY); 116} 117 118static void 119ida_v1_int_enable(struct ida_softc *ida, int enable) 120{ 121 if (enable) { 122 ida_outb(ida, R_EISA_SYSTEM_DOORBELL, ~EISA_CHANNEL_CLEAR); 123 ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY); 124 ida_outb(ida, R_EISA_INT_MASK, INT_ENABLE); 125 ida_outb(ida, R_EISA_SYSTEM_MASK, INT_ENABLE); 126 ida->flags |= IDA_INTERRUPTS; 127 } else { 128 ida_outb(ida, R_EISA_SYSTEM_MASK, INT_DISABLE); 129 ida->flags &= ~IDA_INTERRUPTS; 130 } 131} 132 133static int 134ida_v2_fifo_full(struct ida_softc *ida) 135{ 136 return (ida_inl(ida, R_CMD_FIFO) == 0); 137} 138 139static void 140ida_v2_submit(struct ida_softc *ida, struct ida_qcb *qcb) 141{ 142 ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr); 143} 144 145static bus_addr_t 146ida_v2_done(struct ida_softc *ida) 147{ 148 return (ida_inl(ida, R_DONE_FIFO)); 149} 150 151static int 152ida_v2_int_pending(struct ida_softc *ida) 153{ 154 return (ida_inl(ida, R_INT_PENDING)); 155} 156 157static void 158ida_v2_int_enable(struct ida_softc *ida, int enable) 159{ 160 if (enable) 161 ida->flags |= IDA_INTERRUPTS; 162 else 163 ida->flags &= ~IDA_INTERRUPTS; 164 ida_outl(ida, R_INT_MASK, enable ? INT_ENABLE : INT_DISABLE); 165} 166 167static struct ida_access ida_v1_access = { 168 ida_v1_fifo_full, 169 ida_v1_submit, 170 ida_v1_done, 171 ida_v1_int_pending, 172 ida_v1_int_enable, 173}; 174 175static struct ida_access ida_v2_access = { 176 ida_v2_fifo_full, 177 ida_v2_submit, 178 ida_v2_done, 179 ida_v2_int_pending, 180 ida_v2_int_enable, 181}; 182 183static struct ida_board board_id[] = { 184 { 0x0e114001, "Compaq IDA controller", 185 &ida_v1_access, 0 }, 186 { 0x0e114002, "Compaq IDA-2 controller", 187 &ida_v1_access, 0 }, 188 { 0x0e114010, "Compaq IAES controller", 189 &ida_v1_access, 0 }, 190 { 0x0e114020, "Compaq SMART array controller", 191 &ida_v1_access, 0 }, 192 { 0x0e114030, "Compaq SMART-2/E array controller", 193 &ida_v2_access, 0 }, 194 195 { 0, "", 0, 0 } 196}; 197 198static struct ida_board *ida_eisa_match(eisa_id_t); 199static int ida_eisa_probe(device_t); 200static int ida_eisa_attach(device_t); 201 202static device_method_t ida_eisa_methods[] = { 203 DEVMETHOD(device_probe, ida_eisa_probe), 204 DEVMETHOD(device_attach, ida_eisa_attach), 205 DEVMETHOD(device_detach, ida_detach), 206 207 { 0, 0 } 208}; 209 210static driver_t ida_eisa_driver = { 211 "ida", 212 ida_eisa_methods, 213 sizeof(struct ida_softc) 214}; 215 216static devclass_t ida_devclass; 217 218static struct ida_board * 219ida_eisa_match(eisa_id_t id) 220{ 221 int i; 222 223 for (i = 0; board_id[i].board; i++) 224 if (board_id[i].board == id) 225 return (&board_id[i]); 226 return (NULL); 227} 228 229static int 230ida_eisa_probe(device_t dev) 231{ 232 struct ida_board *board; 233 u_int32_t io_base; 234 u_int irq = 0; 235 236 board = ida_eisa_match(eisa_get_id(dev)); 237 if (board == NULL) 238 return (ENXIO); 239 device_set_desc(dev, board->desc); 240 241 io_base = (eisa_get_slot(dev) * EISA_SLOT_SIZE); 242 243 switch (IDA_EISA_IRQ_MASK & (inb(IDA_EISA_IRQ_REG + io_base))) { 244 case IDA_EISA_IRQ_15: 245 irq = 15; 246 break; 247 case IDA_EISA_IRQ_14: 248 irq = 14; 249 break; 250 case IDA_EISA_IRQ_11: 251 irq = 11; 252 break; 253 case IDA_EISA_IRQ_10: 254 irq = 10; 255 break; 256 default: 257 device_printf(dev, "slot %d, illegal irq setting.\n", 258 eisa_get_slot(dev)); 259 return (ENXIO); 260 } 261 262 eisa_add_iospace(dev, (io_base + IDA_EISA_IOPORT_START), 263 IDA_EISA_IOPORT_LEN, RESVADDR_NONE); 264 265 eisa_add_intr(dev, irq, EISA_TRIGGER_LEVEL); /* XXX ??? */ 266 267 return (0); 268} 269 270static int 271ida_eisa_attach(device_t dev) 272{ 273 struct ida_softc *ida; 274 struct ida_board *board; 275 int error; 276 int rid; 277 278 ida = device_get_softc(dev); 279 ida->dev = dev; 280 281 board = ida_eisa_match(eisa_get_id(dev)); 282 ida->cmd = *board->accessor; 283 ida->flags = board->flags; 284 285 ida->regs_res_type = SYS_RES_IOPORT; 286 ida->regs_res_id = 0; 287 ida->regs = bus_alloc_resource(dev, ida->regs_res_type, 288 &ida->regs_res_id, 0, ~0, 1, RF_ACTIVE); 289 if (ida->regs == NULL) { 290 device_printf(dev, "can't allocate register resources\n"); 291 return (ENOMEM); 292 } 293 294 error = bus_dma_tag_create( 295 /* parent */ NULL, 296 /* alignment */ 0, 297 /* boundary */ 0, 298 /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 299 /* highaddr */ BUS_SPACE_MAXADDR, 300 /* filter */ NULL, 301 /* filterarg */ NULL, 302 /* maxsize */ MAXBSIZE, 303 /* nsegments */ IDA_NSEG, 304 /* maxsegsize */ BUS_SPACE_MAXSIZE_32BIT, 305 /* flags */ BUS_DMA_ALLOCNOW, 306 &ida->parent_dmat); 307 308 if (error != 0) { 309 device_printf(dev, "can't allocate DMA tag\n"); 310 ida_free(ida); 311 return (ENOMEM); 312 } 313 314 rid = 0; 315 ida->irq_res_type = SYS_RES_IRQ; 316 ida->irq = bus_alloc_resource(dev, ida->irq_res_type, &rid, 317 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 318 if (ida->irq == NULL) { 319 ida_free(ida); 320 return (ENOMEM); 321 } 322 323 error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY, 324 ida_intr, ida, &ida->ih); 325 if (error) { 326 device_printf(dev, "can't setup interrupt\n"); 327 ida_free(ida); 328 return (ENOMEM); 329 } 330 331 error = ida_init(ida); 332 if (error) { 333 ida_free(ida); 334 return (error); 335 } 336 337 ida_attach(ida); 338 ida->flags |= IDA_ATTACHED; 339 340 return (0); 341} 342 343DRIVER_MODULE(ida, eisa, ida_eisa_driver, ida_devclass, 0, 0); 344