1 /* $NetBSD: simide.c,v 1.25 2008/03/18 20:46:35 cube Exp $ */ 2 3 /* 4 * Copyright (c) 1997-1998 Mark Brinicombe 5 * Copyright (c) 1997-1998 Causality Limited 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Mark Brinicombe 18 * for the NetBSD Project. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * Card driver and probe and attach functions to use generic IDE driver 34 * for the Simtec IDE podule 35 */ 36 37 /* 38 * Thanks to Gareth Simpson, Simtec Electronics for providing 39 * the hardware information 40 */ 41 42 #include <sys/cdefs.h> 43 __KERNEL_RCSID(0, "$NetBSD: simide.c,v 1.25 2008/03/18 20:46:35 cube Exp $"); 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/conf.h> 48 #include <sys/device.h> 49 #include <sys/malloc.h> 50 51 #include <machine/intr.h> 52 #include <machine/io.h> 53 #include <machine/bus.h> 54 #include <acorn32/podulebus/podulebus.h> 55 #include <acorn32/podulebus/simidereg.h> 56 57 #include <dev/ata/atavar.h> 58 #include <dev/ic/wdcreg.h> 59 #include <dev/ic/wdcvar.h> 60 #include <dev/podulebus/podules.h> 61 62 63 /* 64 * Simtec IDE podule device. 65 * 66 * This probes and attaches the top level Simtec IDE device to the podulebus. 67 * It then configures any children of the Simtec IDE device. 68 * The attach args specify whether it is configuring the primary or 69 * secondary channel. 70 * The children are expected to be wdc devices using simide attachments. 71 */ 72 73 /* 74 * Simtec IDE card softc structure. 75 * 76 * Contains the device node, podule information and global information 77 * required by the driver such as the card version and the interrupt mask. 78 */ 79 80 struct simide_softc { 81 struct wdc_softc sc_wdcdev; /* common wdc definitions */ 82 struct ata_channel *sc_chanarray[2]; /* channels definition */ 83 podule_t *sc_podule; /* Our podule info */ 84 int sc_podule_number; /* Our podule number */ 85 int sc_ctl_reg; /* Global ctl reg */ 86 int sc_version; /* Card version */ 87 bus_space_tag_t sc_ctliot; /* Bus tag */ 88 bus_space_handle_t sc_ctlioh; /* control handle */ 89 struct bus_space sc_tag; /* custom tag */ 90 struct simide_channel { 91 struct ata_channel sc_channel; /* generic part */ 92 struct ata_queue sc_chqueue; /* channel queue */ 93 irqhandler_t sc_ih; /* interrupt handler */ 94 int sc_irqmask; /* IRQ mask for this channel */ 95 } simide_channels[2]; 96 struct wdc_regs sc_wdc_regs[2]; 97 }; 98 99 int simide_probe (device_t, cfdata_t, void *); 100 void simide_attach (device_t, device_t, void *); 101 void simide_shutdown (void *arg); 102 int simide_intr (void *arg); 103 104 CFATTACH_DECL_NEW(simide, sizeof(struct simide_softc), 105 simide_probe, simide_attach, NULL, NULL); 106 107 108 /* 109 * Define prototypes for custom bus space functions. 110 */ 111 112 bs_rm_2_proto(simide); 113 bs_wm_2_proto(simide); 114 115 /* 116 * Create an array of address structures. These define the addresses and 117 * masks needed for the different channels. 118 * 119 * index = channel 120 */ 121 122 struct { 123 u_int drive_registers; 124 u_int aux_register; 125 u_int irq_mask; 126 } simide_info[] = { 127 { PRIMARY_DRIVE_REGISTERS_POFFSET, PRIMARY_AUX_REGISTER_POFFSET, 128 CONTROL_PRIMARY_IRQ }, 129 { SECONDARY_DRIVE_REGISTERS_POFFSET, SECONDARY_AUX_REGISTER_POFFSET, 130 CONTROL_SECONDARY_IRQ } 131 }; 132 133 /* 134 * Card probe function 135 * 136 * Just match the manufacturer and podule ID's 137 */ 138 139 int 140 simide_probe(device_t parent, cfdata_t cf, void *aux) 141 { 142 struct podule_attach_args *pa = (void *)aux; 143 144 return (pa->pa_product == PODULE_SIMTEC_IDE); 145 } 146 147 /* 148 * Card attach function 149 * 150 * Identify the card version and configure any children. 151 * Install a shutdown handler to kill interrupts on shutdown 152 */ 153 154 void 155 simide_attach(device_t parent, device_t self, void *aux) 156 { 157 struct simide_softc *sc = device_private(self); 158 struct podule_attach_args *pa = (void *)aux; 159 int status; 160 u_int iobase; 161 int channel, i; 162 struct simide_channel *scp; 163 struct ata_channel *cp; 164 struct wdc_regs *wdr; 165 irqhandler_t *ihp; 166 167 /* Note the podule number and validate */ 168 if (pa->pa_podule_number == -1) 169 panic("Podule has disappeared !"); 170 171 sc->sc_wdcdev.sc_atac.atac_dev = self; 172 sc->sc_podule_number = pa->pa_podule_number; 173 sc->sc_podule = pa->pa_podule; 174 podules[sc->sc_podule_number].attached = 1; 175 176 sc->sc_wdcdev.regs = sc->sc_wdc_regs; 177 178 /* 179 * Ok we need our own bus tag as the register spacing 180 * is not the default. 181 * 182 * For the podulebus the bus tag cookie is the shift 183 * to apply to registers 184 * So duplicate the bus space tag and change the 185 * cookie. 186 * 187 * Also while we are at it replace the default 188 * read/write mulitple short functions with 189 * optimised versions 190 */ 191 192 sc->sc_tag = *pa->pa_iot; 193 sc->sc_tag.bs_cookie = (void *) DRIVE_REGISTER_SPACING_SHIFT; 194 sc->sc_tag.bs_rm_2 = simide_bs_rm_2; 195 sc->sc_tag.bs_wm_2 = simide_bs_wm_2; 196 sc->sc_ctliot = pa->pa_iot; 197 198 /* Obtain bus space handles for all the control registers */ 199 if (bus_space_map(sc->sc_ctliot, pa->pa_podule->mod_base + 200 CONTROL_REGISTERS_POFFSET, CONTROL_REGISTER_SPACE, 0, 201 &sc->sc_ctlioh)) 202 panic("%s: Cannot map control registers", device_xname(self)); 203 204 /* Install a clean up handler to make sure IRQ's are disabled */ 205 if (shutdownhook_establish(simide_shutdown, (void *)sc) == NULL) 206 panic("%s: Cannot install shutdown handler", 207 device_xname(self)); 208 209 /* Set the interrupt info for this podule */ 210 sc->sc_podule->irq_addr = pa->pa_podule->mod_base 211 + CONTROL_REGISTERS_POFFSET + (CONTROL_REGISTER_OFFSET << 2); 212 sc->sc_podule->irq_mask = STATUS_IRQ; 213 214 sc->sc_ctl_reg = 0; 215 216 status = bus_space_read_1(sc->sc_ctliot, sc->sc_ctlioh, 217 STATUS_REGISTER_OFFSET); 218 219 aprint_normal(":"); 220 /* If any of the bits in STATUS_FAULT are zero then we have a fault. */ 221 if ((status & STATUS_FAULT) != STATUS_FAULT) 222 aprint_normal(" card/cable fault (%02x) -", status); 223 224 if (!(status & STATUS_RESET)) 225 aprint_normal(" (reset)"); 226 if (!(status & STATUS_ADDR_TEST)) 227 aprint_normal(" (addr)"); 228 if (!(status & STATUS_CS_TEST)) 229 aprint_normal(" (cs)"); 230 if (!(status & STATUS_RW_TEST)) 231 aprint_normal(" (rw)"); 232 233 aprint_normal("\n"); 234 235 /* Perhaps we should just abort at this point. */ 236 /* if ((status & STATUS_FAULT) != STATUS_FAULT) 237 return;*/ 238 239 /* 240 * Enable IDE, Obey IORDY and disabled slow mode 241 */ 242 sc->sc_ctl_reg |= CONTROL_IDE_ENABLE | CONTROL_IORDY 243 | CONTROL_SLOW_MODE_OFF; 244 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh, 245 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg); 246 247 /* Fill in wdc and channel infos */ 248 sc->sc_wdcdev.sc_atac.atac_cap |= ATAC_CAP_DATA16; 249 sc->sc_wdcdev.sc_atac.atac_pio_cap = 0; 250 sc->sc_wdcdev.sc_atac.atac_channels = sc->sc_chanarray; 251 sc->sc_wdcdev.sc_atac.atac_nchannels = 2; 252 for (channel = 0 ; channel < 2; channel++) { 253 scp = &sc->simide_channels[channel]; 254 sc->sc_chanarray[channel] = &scp->sc_channel; 255 cp = &scp->sc_channel; 256 wdr = &sc->sc_wdc_regs[channel]; 257 258 cp->ch_channel = channel; 259 cp->ch_atac = &sc->sc_wdcdev.sc_atac; 260 cp->ch_queue = &scp->sc_chqueue; 261 cp->ch_ndrive = 2; 262 wdr->cmd_iot = wdr->ctl_iot = &sc->sc_tag; 263 iobase = pa->pa_podule->mod_base; 264 if (bus_space_map(wdr->cmd_iot, iobase + 265 simide_info[channel].drive_registers, 266 DRIVE_REGISTERS_SPACE, 0, &wdr->cmd_baseioh)) 267 continue; 268 for (i = 0; i < WDC_NREG; i++) { 269 if (bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 270 i, i == 0 ? 4 : 1, &wdr->cmd_iohs[i]) != 0) { 271 bus_space_unmap(wdr->cmd_iot, wdr->cmd_baseioh, 272 DRIVE_REGISTERS_SPACE); 273 continue; 274 } 275 } 276 wdc_init_shadow_regs(cp); 277 if (bus_space_map(wdr->ctl_iot, iobase + 278 simide_info[channel].aux_register, 4, 0, &wdr->ctl_ioh)) { 279 bus_space_unmap(wdr->cmd_iot, wdr->cmd_baseioh, 280 DRIVE_REGISTERS_SPACE); 281 continue; 282 } 283 /* Disable interrupts and clear any pending interrupts */ 284 scp->sc_irqmask = simide_info[channel].irq_mask; 285 sc->sc_ctl_reg &= ~scp->sc_irqmask; 286 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh, 287 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg); 288 ihp = &scp->sc_ih; 289 ihp->ih_func = simide_intr; 290 ihp->ih_arg = scp; 291 ihp->ih_level = IPL_BIO; 292 ihp->ih_name = "simide"; 293 ihp->ih_maskaddr = pa->pa_podule->irq_addr; 294 ihp->ih_maskbits = scp->sc_irqmask; 295 if (irq_claim(sc->sc_podule->interrupt, ihp)) 296 panic("%s: Cannot claim interrupt %d", 297 device_xname(self), sc->sc_podule->interrupt); 298 /* clear any pending interrupts and enable interrupts */ 299 sc->sc_ctl_reg |= scp->sc_irqmask; 300 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh, 301 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg); 302 wdcattach(cp); 303 } 304 } 305 306 /* 307 * Card shutdown function 308 * 309 * Called via do_shutdown_hooks() during kernel shutdown. 310 * Clear the cards's interrupt mask to stop any podule interrupts. 311 */ 312 313 void 314 simide_shutdown(void *arg) 315 { 316 struct simide_softc *sc = arg; 317 318 sc->sc_ctl_reg &= (CONTROL_PRIMARY_IRQ | CONTROL_SECONDARY_IRQ); 319 320 /* Disable card interrupts */ 321 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh, 322 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg); 323 } 324 325 /* 326 * Podule interrupt handler 327 * 328 * If the interrupt was from our card pass it on to the wdc interrupt handler 329 */ 330 int 331 simide_intr(void *arg) 332 { 333 struct simide_channel *scp = arg; 334 irqhandler_t *ihp = &scp->sc_ih; 335 volatile u_char *intraddr = (volatile u_char *)ihp->ih_maskaddr; 336 337 /* XXX - not bus space yet - should really be handled by podulebus */ 338 if ((*intraddr) & ihp->ih_maskbits) 339 wdcintr(&scp->sc_channel); 340 341 return(0); 342 } 343