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