ida_eisa.c revision 146734
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 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/sys/dev/ida/ida_eisa.c 146734 2005-05-29 04:42:30Z nyan $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/kernel.h> 34#include <sys/module.h> 35#include <sys/bus.h> 36 37#include <sys/bio.h> 38#include <sys/conf.h> 39 40#include <machine/bus.h> 41#include <machine/resource.h> 42#include <sys/rman.h> 43 44#include <geom/geom_disk.h> 45 46#include <dev/ida/idavar.h> 47#include <dev/ida/idareg.h> 48 49#include <dev/eisa/eisaconf.h> 50 51#define IDA_EISA_IOPORT_START 0x0c88 52#define IDA_EISA_IOPORT_LEN 0x0017 53 54#define IDA_EISA_IRQ_REG 0x0cc0 55#define IDA_EISA_IRQ_MASK 0xf0 56#define IDA_EISA_IRQ_15 0x80 57#define IDA_EISA_IRQ_14 0x40 58#define IDA_EISA_IRQ_11 0x10 59#define IDA_EISA_IRQ_10 0x20 60 61static int 62ida_v1_fifo_full(struct ida_softc *ida) 63{ 64 u_int8_t status; 65 66 status = ida_inb(ida, R_EISA_SYSTEM_DOORBELL); 67 return ((status & EISA_CHANNEL_CLEAR) == 0); 68} 69 70static void 71ida_v1_submit(struct ida_softc *ida, struct ida_qcb *qcb) 72{ 73 u_int16_t size; 74 75 /* 76 * On these cards, this location is actually for control flags. 77 * Set them to zero and pass in structure size via an I/O port. 78 */ 79 size = qcb->hwqcb->hdr.size << 2; 80 qcb->hwqcb->hdr.size = 0; 81 82 ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_CLEAR); 83 ida_outl(ida, R_EISA_LIST_ADDR, qcb->hwqcb_busaddr); 84 ida_outw(ida, R_EISA_LIST_LEN, size); 85 ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY); 86} 87 88static bus_addr_t 89ida_v1_done(struct ida_softc *ida) 90{ 91 struct ida_hardware_qcb *hwqcb; 92 bus_addr_t completed; 93 u_int8_t status; 94 95 if ((ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY) == 0) 96 return (0); 97 98 ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_BUSY); 99 completed = ida_inl(ida, R_EISA_COMPLETE_ADDR); 100 status = ida_inb(ida, R_EISA_LIST_STATUS); 101 ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_CLEAR); 102 103 if (completed != 0) { 104 hwqcb = (struct ida_hardware_qcb *) 105 ((bus_addr_t)ida->hwqcbs + 106 ((completed & ~3) - ida->hwqcb_busaddr)); 107 hwqcb->req.error = status; 108 } 109 110 return (completed); 111} 112 113static int 114ida_v1_int_pending(struct ida_softc *ida) 115{ 116 return (ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY); 117} 118 119static void 120ida_v1_int_enable(struct ida_softc *ida, int enable) 121{ 122 if (enable) { 123 ida_outb(ida, R_EISA_SYSTEM_DOORBELL, ~EISA_CHANNEL_CLEAR); 124 ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY); 125 ida_outb(ida, R_EISA_INT_MASK, INT_ENABLE); 126 ida_outb(ida, R_EISA_SYSTEM_MASK, INT_ENABLE); 127 ida->flags |= IDA_INTERRUPTS; 128 } else { 129 ida_outb(ida, R_EISA_SYSTEM_MASK, INT_DISABLE); 130 ida->flags &= ~IDA_INTERRUPTS; 131 } 132} 133 134static int 135ida_v2_fifo_full(struct ida_softc *ida) 136{ 137 return (ida_inl(ida, R_CMD_FIFO) == 0); 138} 139 140static void 141ida_v2_submit(struct ida_softc *ida, struct ida_qcb *qcb) 142{ 143 ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr); 144} 145 146static bus_addr_t 147ida_v2_done(struct ida_softc *ida) 148{ 149 return (ida_inl(ida, R_DONE_FIFO)); 150} 151 152static int 153ida_v2_int_pending(struct ida_softc *ida) 154{ 155 return (ida_inl(ida, R_INT_PENDING)); 156} 157 158static void 159ida_v2_int_enable(struct ida_softc *ida, int enable) 160{ 161 if (enable) 162 ida->flags |= IDA_INTERRUPTS; 163 else 164 ida->flags &= ~IDA_INTERRUPTS; 165 ida_outl(ida, R_INT_MASK, enable ? INT_ENABLE : INT_DISABLE); 166} 167 168static struct ida_access ida_v1_access = { 169 ida_v1_fifo_full, 170 ida_v1_submit, 171 ida_v1_done, 172 ida_v1_int_pending, 173 ida_v1_int_enable, 174}; 175 176static struct ida_access ida_v2_access = { 177 ida_v2_fifo_full, 178 ida_v2_submit, 179 ida_v2_done, 180 ida_v2_int_pending, 181 ida_v2_int_enable, 182}; 183 184static struct ida_board board_id[] = { 185 { 0x0e114001, "Compaq IDA controller", 186 &ida_v1_access, 0 }, 187 { 0x0e114002, "Compaq IDA-2 controller", 188 &ida_v1_access, 0 }, 189 { 0x0e114010, "Compaq IAES controller", 190 &ida_v1_access, 0 }, 191 { 0x0e114020, "Compaq SMART array controller", 192 &ida_v1_access, 0 }, 193 { 0x0e114030, "Compaq SMART-2/E array controller", 194 &ida_v2_access, 0 }, 195 196 { 0, "", 0, 0 } 197}; 198 199static struct ida_board *ida_eisa_match(eisa_id_t); 200static int ida_eisa_probe(device_t); 201static int ida_eisa_attach(device_t); 202 203static device_method_t ida_eisa_methods[] = { 204 DEVMETHOD(device_probe, ida_eisa_probe), 205 DEVMETHOD(device_attach, ida_eisa_attach), 206 DEVMETHOD(device_detach, ida_detach), 207 208 { 0, 0 } 209}; 210 211static driver_t ida_eisa_driver = { 212 "ida", 213 ida_eisa_methods, 214 sizeof(struct ida_softc) 215}; 216 217static devclass_t ida_devclass; 218 219static struct ida_board * 220ida_eisa_match(eisa_id_t id) 221{ 222 int i; 223 224 for (i = 0; board_id[i].board; i++) 225 if (board_id[i].board == id) 226 return (&board_id[i]); 227 return (NULL); 228} 229 230static int 231ida_eisa_probe(device_t dev) 232{ 233 struct ida_board *board; 234 u_int32_t io_base; 235 u_int irq = 0; 236 237 board = ida_eisa_match(eisa_get_id(dev)); 238 if (board == NULL) 239 return (ENXIO); 240 device_set_desc(dev, board->desc); 241 242 io_base = (eisa_get_slot(dev) * EISA_SLOT_SIZE); 243 244 switch (IDA_EISA_IRQ_MASK & (inb(IDA_EISA_IRQ_REG + io_base))) { 245 case IDA_EISA_IRQ_15: 246 irq = 15; 247 break; 248 case IDA_EISA_IRQ_14: 249 irq = 14; 250 break; 251 case IDA_EISA_IRQ_11: 252 irq = 11; 253 break; 254 case IDA_EISA_IRQ_10: 255 irq = 10; 256 break; 257 default: 258 device_printf(dev, "slot %d, illegal irq setting.\n", 259 eisa_get_slot(dev)); 260 return (ENXIO); 261 } 262 263 eisa_add_iospace(dev, (io_base + IDA_EISA_IOPORT_START), 264 IDA_EISA_IOPORT_LEN, RESVADDR_NONE); 265 266 eisa_add_intr(dev, irq, EISA_TRIGGER_LEVEL); /* XXX ??? */ 267 268 return (0); 269} 270 271static int 272ida_eisa_attach(device_t dev) 273{ 274 struct ida_softc *ida; 275 struct ida_board *board; 276 int error; 277 int rid; 278 279 ida = device_get_softc(dev); 280 ida->dev = dev; 281 282 board = ida_eisa_match(eisa_get_id(dev)); 283 ida->cmd = *board->accessor; 284 ida->flags = board->flags; 285 286 ida->regs_res_type = SYS_RES_IOPORT; 287 ida->regs_res_id = 0; 288 ida->regs = bus_alloc_resource_any(dev, ida->regs_res_type, 289 &ida->regs_res_id, RF_ACTIVE); 290 if (ida->regs == NULL) { 291 device_printf(dev, "can't allocate register resources\n"); 292 return (ENOMEM); 293 } 294 295 error = bus_dma_tag_create( 296 /* parent */ NULL, 297 /* alignment */ 0, 298 /* boundary */ 0, 299 /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, 300 /* highaddr */ BUS_SPACE_MAXADDR, 301 /* filter */ NULL, 302 /* filterarg */ NULL, 303 /* maxsize */ MAXBSIZE, 304 /* nsegments */ IDA_NSEG, 305 /* maxsegsize */ BUS_SPACE_MAXSIZE_32BIT, 306 /* flags */ BUS_DMA_ALLOCNOW, 307 /* lockfunc */ NULL, 308 /* lockarg */ NULL, 309 &ida->parent_dmat); 310 311 if (error != 0) { 312 device_printf(dev, "can't allocate DMA tag\n"); 313 ida_free(ida); 314 return (ENOMEM); 315 } 316 317 rid = 0; 318 ida->irq_res_type = SYS_RES_IRQ; 319 ida->irq = bus_alloc_resource_any(dev, ida->irq_res_type, &rid, 320 RF_ACTIVE | RF_SHAREABLE); 321 if (ida->irq == NULL) { 322 ida_free(ida); 323 return (ENOMEM); 324 } 325 326 error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY, 327 ida_intr, ida, &ida->ih); 328 if (error) { 329 device_printf(dev, "can't setup interrupt\n"); 330 ida_free(ida); 331 return (ENOMEM); 332 } 333 334 error = ida_init(ida); 335 if (error) { 336 ida_free(ida); 337 return (error); 338 } 339 340 ida_attach(ida); 341 ida->flags |= IDA_ATTACHED; 342 343 return (0); 344} 345 346DRIVER_MODULE(ida, eisa, ida_eisa_driver, ida_devclass, 0, 0); 347