ida_eisa.c revision 117126
1124208Sdes/* 2124208Sdes * Copyright (c) 2000 Jonathan Lemon 3124208Sdes * Copyright (c) 1999 by Matthew N. Dodd <winter@jurai.net> 4124208Sdes * All Rights Reserved. 5124208Sdes * 6124208Sdes * Redistribution and use in source and binary forms, with or without 7124208Sdes * modification, are permitted provided that the following conditions 8124208Sdes * are met: 9124208Sdes * 1. Redistributions of source code must retain the above copyright 10124208Sdes * notice, this list of conditions, and the following disclaimer, 11124208Sdes * without modification, immediately at the beginning of the file. 12124208Sdes * 2. The name of the author may not be used to endorse or promote products 13124208Sdes * derived from this software without specific prior written permission. 14124208Sdes * 15124208Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16124208Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17124208Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18124208Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 19124208Sdes * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20124208Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21124208Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22124208Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23124208Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24124208Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25124208Sdes * SUCH DAMAGE. 26157016Sdes * 27157016Sdes * $FreeBSD: head/sys/dev/ida/ida_eisa.c 117126 2003-07-01 15:52:06Z scottl $ 28157016Sdes */ 29157016Sdes 30157016Sdes#include <sys/param.h> 31162852Sdes#include <sys/systm.h> 32124208Sdes#include <sys/kernel.h> 33124208Sdes#include <sys/bus.h> 34162852Sdes 35162852Sdes#include <sys/bio.h> 36162852Sdes#include <sys/conf.h> 37162852Sdes 38124208Sdes#include <machine/bus_pio.h> 39124208Sdes#include <machine/bus.h> 40124208Sdes#include <machine/resource.h> 41124208Sdes#include <sys/rman.h> 42124208Sdes 43124208Sdes#include <geom/geom_disk.h> 44124208Sdes 45124208Sdes#include <dev/ida/idavar.h> 46124208Sdes#include <dev/ida/idareg.h> 47124208Sdes 48124208Sdes#include <dev/eisa/eisaconf.h> 49124208Sdes 50124208Sdes#define IDA_EISA_IOPORT_START 0x0c88 51124208Sdes#define IDA_EISA_IOPORT_LEN 0x0017 52124208Sdes 53124208Sdes#define IDA_EISA_IRQ_REG 0x0cc0 54124208Sdes#define IDA_EISA_IRQ_MASK 0xf0 55124208Sdes#define IDA_EISA_IRQ_15 0x80 56124208Sdes#define IDA_EISA_IRQ_14 0x40 57124208Sdes#define IDA_EISA_IRQ_11 0x10 58124208Sdes#define IDA_EISA_IRQ_10 0x20 59124208Sdes 60124208Sdesstatic int 61124208Sdesida_v1_fifo_full(struct ida_softc *ida) 62124208Sdes{ 63124208Sdes u_int8_t status; 64124208Sdes 65124208Sdes status = ida_inb(ida, R_EISA_SYSTEM_DOORBELL); 66124208Sdes return ((status & EISA_CHANNEL_CLEAR) == 0); 67124208Sdes} 68124208Sdes 69124208Sdesstatic void 70124208Sdesida_v1_submit(struct ida_softc *ida, struct ida_qcb *qcb) 71124208Sdes{ 72124208Sdes u_int16_t size; 73124208Sdes 74124208Sdes /* 75124208Sdes * On these cards, this location is actually for control flags. 76124208Sdes * Set them to zero and pass in structure size via an I/O port. 77124208Sdes */ 78124208Sdes size = qcb->hwqcb->hdr.size << 2; 79124208Sdes qcb->hwqcb->hdr.size = 0; 80124208Sdes 81124208Sdes ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_CLEAR); 82124208Sdes ida_outl(ida, R_EISA_LIST_ADDR, qcb->hwqcb_busaddr); 83124208Sdes ida_outw(ida, R_EISA_LIST_LEN, size); 84124208Sdes ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY); 85124208Sdes} 86124208Sdes 87124208Sdesstatic bus_addr_t 88124208Sdesida_v1_done(struct ida_softc *ida) 89124208Sdes{ 90124208Sdes struct ida_hardware_qcb *hwqcb; 91124208Sdes bus_addr_t completed; 92124208Sdes u_int8_t status; 93124208Sdes 94124208Sdes if ((ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY) == 0) 95124208Sdes return (0); 96124208Sdes 97124208Sdes ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_BUSY); 98124208Sdes completed = ida_inl(ida, R_EISA_COMPLETE_ADDR); 99124208Sdes status = ida_inb(ida, R_EISA_LIST_STATUS); 100124208Sdes ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_CLEAR); 101124208Sdes 102124208Sdes if (completed != 0) { 103124208Sdes hwqcb = (struct ida_hardware_qcb *) 104124208Sdes ((bus_addr_t)ida->hwqcbs + 105124208Sdes ((completed & ~3) - ida->hwqcb_busaddr)); 106124208Sdes hwqcb->req.error = status; 107124208Sdes } 108124208Sdes 109124208Sdes return (completed); 110124208Sdes} 111124208Sdes 112124208Sdesstatic int 113124208Sdesida_v1_int_pending(struct ida_softc *ida) 114124208Sdes{ 115124208Sdes return (ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY); 116124208Sdes} 117124208Sdes 118124208Sdesstatic void 119124208Sdesida_v1_int_enable(struct ida_softc *ida, int enable) 120124208Sdes{ 121124208Sdes if (enable) { 122124208Sdes ida_outb(ida, R_EISA_SYSTEM_DOORBELL, ~EISA_CHANNEL_CLEAR); 123124208Sdes ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY); 124124208Sdes ida_outb(ida, R_EISA_INT_MASK, INT_ENABLE); 125124208Sdes ida_outb(ida, R_EISA_SYSTEM_MASK, INT_ENABLE); 126124208Sdes ida->flags |= IDA_INTERRUPTS; 127124208Sdes } else { 128124208Sdes ida_outb(ida, R_EISA_SYSTEM_MASK, INT_DISABLE); 129124208Sdes ida->flags &= ~IDA_INTERRUPTS; 130124208Sdes } 131124208Sdes} 132124208Sdes 133124208Sdesstatic int 134124208Sdesida_v2_fifo_full(struct ida_softc *ida) 135124208Sdes{ 136124208Sdes return (ida_inl(ida, R_CMD_FIFO) == 0); 137124208Sdes} 138124208Sdes 139124208Sdesstatic void 140124208Sdesida_v2_submit(struct ida_softc *ida, struct ida_qcb *qcb) 141124208Sdes{ 142124208Sdes ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr); 143124208Sdes} 144124208Sdes 145124208Sdesstatic bus_addr_t 146124208Sdesida_v2_done(struct ida_softc *ida) 147124208Sdes{ 148124208Sdes return (ida_inl(ida, R_DONE_FIFO)); 149124208Sdes} 150124208Sdes 151124208Sdesstatic int 152124208Sdesida_v2_int_pending(struct ida_softc *ida) 153124208Sdes{ 154124208Sdes return (ida_inl(ida, R_INT_PENDING)); 155124208Sdes} 156124208Sdes 157124208Sdesstatic void 158124208Sdesida_v2_int_enable(struct ida_softc *ida, int enable) 159124208Sdes{ 160124208Sdes if (enable) 161124208Sdes ida->flags |= IDA_INTERRUPTS; 162124208Sdes else 163124208Sdes ida->flags &= ~IDA_INTERRUPTS; 164157016Sdes 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 /* lockfunc */ NULL, 307 /* lockarg */ NULL, 308 &ida->parent_dmat); 309 310 if (error != 0) { 311 device_printf(dev, "can't allocate DMA tag\n"); 312 ida_free(ida); 313 return (ENOMEM); 314 } 315 316 rid = 0; 317 ida->irq_res_type = SYS_RES_IRQ; 318 ida->irq = bus_alloc_resource(dev, ida->irq_res_type, &rid, 319 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 320 if (ida->irq == NULL) { 321 ida_free(ida); 322 return (ENOMEM); 323 } 324 325 error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY, 326 ida_intr, ida, &ida->ih); 327 if (error) { 328 device_printf(dev, "can't setup interrupt\n"); 329 ida_free(ida); 330 return (ENOMEM); 331 } 332 333 error = ida_init(ida); 334 if (error) { 335 ida_free(ida); 336 return (error); 337 } 338 339 ida_attach(ida); 340 ida->flags |= IDA_ATTACHED; 341 342 return (0); 343} 344 345DRIVER_MODULE(ida, eisa, ida_eisa_driver, ida_devclass, 0, 0); 346