1 /* $NetBSD: simide.c,v 1.31 2017/10/20 07:06:06 jdolecek 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.31 2017/10/20 07:06:06 jdolecek 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 #include <sys/bus.h> 51 52 #include <machine/intr.h> 53 #include <machine/io.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 irqhandler_t sc_ih; /* interrupt handler */ 93 int sc_irqmask; /* IRQ mask for this channel */ 94 } simide_channels[2]; 95 struct wdc_regs sc_wdc_regs[2]; 96 }; 97 98 int simide_probe (device_t, cfdata_t, void *); 99 void simide_attach (device_t, device_t, void *); 100 void simide_shutdown (void *arg); 101 int simide_intr (void *arg); 102 103 CFATTACH_DECL_NEW(simide, sizeof(struct simide_softc), 104 simide_probe, simide_attach, NULL, NULL); 105 106 107 /* 108 * Define prototypes for custom bus space functions. 109 */ 110 111 bs_rm_2_proto(simide); 112 bs_wm_2_proto(simide); 113 114 /* 115 * Create an array of address structures. These define the addresses and 116 * masks needed for the different channels. 117 * 118 * index = channel 119 */ 120 121 struct { 122 u_int drive_registers; 123 u_int aux_register; 124 u_int irq_mask; 125 } simide_info[] = { 126 { PRIMARY_DRIVE_REGISTERS_POFFSET, PRIMARY_AUX_REGISTER_POFFSET, 127 CONTROL_PRIMARY_IRQ }, 128 { SECONDARY_DRIVE_REGISTERS_POFFSET, SECONDARY_AUX_REGISTER_POFFSET, 129 CONTROL_SECONDARY_IRQ } 130 }; 131 132 /* 133 * Card probe function 134 * 135 * Just match the manufacturer and podule ID's 136 */ 137 138 int 139 simide_probe(device_t parent, cfdata_t cf, void *aux) 140 { 141 struct podule_attach_args *pa = (void *)aux; 142 143 return (pa->pa_product == PODULE_SIMTEC_IDE); 144 } 145 146 /* 147 * Card attach function 148 * 149 * Identify the card version and configure any children. 150 * Install a shutdown handler to kill interrupts on shutdown 151 */ 152 153 void 154 simide_attach(device_t parent, device_t self, void *aux) 155 { 156 struct simide_softc *sc = device_private(self); 157 struct podule_attach_args *pa = (void *)aux; 158 int status; 159 u_int iobase; 160 int channel, i; 161 struct simide_channel *scp; 162 struct ata_channel *cp; 163 struct wdc_regs *wdr; 164 irqhandler_t *ihp; 165 166 /* Note the podule number and validate */ 167 if (pa->pa_podule_number == -1) 168 panic("Podule has disappeared !"); 169 170 sc->sc_wdcdev.sc_atac.atac_dev = self; 171 sc->sc_podule_number = pa->pa_podule_number; 172 sc->sc_podule = pa->pa_podule; 173 podules[sc->sc_podule_number].attached = 1; 174 175 sc->sc_wdcdev.regs = sc->sc_wdc_regs; 176 177 /* 178 * Ok we need our own bus tag as the register spacing 179 * is not the default. 180 * 181 * For the podulebus the bus tag cookie is the shift 182 * to apply to registers 183 * So duplicate the bus space tag and change the 184 * cookie. 185 * 186 * Also while we are at it replace the default 187 * read/write mulitple short functions with 188 * optimised versions 189 */ 190 191 sc->sc_tag = *pa->pa_iot; 192 sc->sc_tag.bs_cookie = (void *) DRIVE_REGISTER_SPACING_SHIFT; 193 sc->sc_tag.bs_rm_2 = simide_bs_rm_2; 194 sc->sc_tag.bs_wm_2 = simide_bs_wm_2; 195 sc->sc_ctliot = pa->pa_iot; 196 197 /* Obtain bus space handles for all the control registers */ 198 if (bus_space_map(sc->sc_ctliot, pa->pa_podule->mod_base + 199 CONTROL_REGISTERS_POFFSET, CONTROL_REGISTER_SPACE, 0, 200 &sc->sc_ctlioh)) 201 panic("%s: Cannot map control registers", device_xname(self)); 202 203 /* Install a clean up handler to make sure IRQ's are disabled */ 204 if (shutdownhook_establish(simide_shutdown, (void *)sc) == NULL) 205 panic("%s: Cannot install shutdown handler", 206 device_xname(self)); 207 208 /* Set the interrupt info for this podule */ 209 sc->sc_podule->irq_addr = pa->pa_podule->mod_base 210 + CONTROL_REGISTERS_POFFSET + (CONTROL_REGISTER_OFFSET << 2); 211 sc->sc_podule->irq_mask = STATUS_IRQ; 212 213 sc->sc_ctl_reg = 0; 214 215 status = bus_space_read_1(sc->sc_ctliot, sc->sc_ctlioh, 216 STATUS_REGISTER_OFFSET); 217 218 aprint_normal(":"); 219 /* If any of the bits in STATUS_FAULT are zero then we have a fault. */ 220 if ((status & STATUS_FAULT) != STATUS_FAULT) 221 aprint_normal(" card/cable fault (%02x) -", status); 222 223 if (!(status & STATUS_RESET)) 224 aprint_normal(" (reset)"); 225 if (!(status & STATUS_ADDR_TEST)) 226 aprint_normal(" (addr)"); 227 if (!(status & STATUS_CS_TEST)) 228 aprint_normal(" (cs)"); 229 if (!(status & STATUS_RW_TEST)) 230 aprint_normal(" (rw)"); 231 232 aprint_normal("\n"); 233 234 /* Perhaps we should just abort at this point. */ 235 /* if ((status & STATUS_FAULT) != STATUS_FAULT) 236 return;*/ 237 238 /* 239 * Enable IDE, Obey IORDY and disabled slow mode 240 */ 241 sc->sc_ctl_reg |= CONTROL_IDE_ENABLE | CONTROL_IORDY 242 | CONTROL_SLOW_MODE_OFF; 243 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh, 244 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg); 245 246 /* Fill in wdc and channel infos */ 247 sc->sc_wdcdev.sc_atac.atac_cap |= ATAC_CAP_DATA16; 248 sc->sc_wdcdev.sc_atac.atac_pio_cap = 0; 249 sc->sc_wdcdev.sc_atac.atac_channels = sc->sc_chanarray; 250 sc->sc_wdcdev.sc_atac.atac_nchannels = 2; 251 sc->sc_wdcdev.wdc_maxdrives = 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 wdr->cmd_iot = wdr->ctl_iot = &sc->sc_tag; 261 iobase = pa->pa_podule->mod_base; 262 if (bus_space_map(wdr->cmd_iot, iobase + 263 simide_info[channel].drive_registers, 264 DRIVE_REGISTERS_SPACE, 0, &wdr->cmd_baseioh)) 265 continue; 266 for (i = 0; i < WDC_NREG; i++) { 267 if (bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 268 i, i == 0 ? 4 : 1, &wdr->cmd_iohs[i]) != 0) { 269 bus_space_unmap(wdr->cmd_iot, wdr->cmd_baseioh, 270 DRIVE_REGISTERS_SPACE); 271 continue; 272 } 273 } 274 wdc_init_shadow_regs(wdr); 275 if (bus_space_map(wdr->ctl_iot, iobase + 276 simide_info[channel].aux_register, 4, 0, &wdr->ctl_ioh)) { 277 bus_space_unmap(wdr->cmd_iot, wdr->cmd_baseioh, 278 DRIVE_REGISTERS_SPACE); 279 continue; 280 } 281 /* Disable interrupts and clear any pending interrupts */ 282 scp->sc_irqmask = simide_info[channel].irq_mask; 283 sc->sc_ctl_reg &= ~scp->sc_irqmask; 284 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh, 285 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg); 286 ihp = &scp->sc_ih; 287 ihp->ih_func = simide_intr; 288 ihp->ih_arg = scp; 289 ihp->ih_level = IPL_BIO; 290 ihp->ih_name = "simide"; 291 ihp->ih_maskaddr = pa->pa_podule->irq_addr; 292 ihp->ih_maskbits = scp->sc_irqmask; 293 if (irq_claim(sc->sc_podule->interrupt, ihp)) 294 panic("%s: Cannot claim interrupt %d", 295 device_xname(self), sc->sc_podule->interrupt); 296 /* clear any pending interrupts and enable interrupts */ 297 sc->sc_ctl_reg |= scp->sc_irqmask; 298 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh, 299 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg); 300 wdcattach(cp); 301 } 302 } 303 304 /* 305 * Card shutdown function 306 * 307 * Called via do_shutdown_hooks() during kernel shutdown. 308 * Clear the cards's interrupt mask to stop any podule interrupts. 309 */ 310 311 void 312 simide_shutdown(void *arg) 313 { 314 struct simide_softc *sc = arg; 315 316 sc->sc_ctl_reg &= (CONTROL_PRIMARY_IRQ | CONTROL_SECONDARY_IRQ); 317 318 /* Disable card interrupts */ 319 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh, 320 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg); 321 } 322 323 /* 324 * Podule interrupt handler 325 * 326 * If the interrupt was from our card pass it on to the wdc interrupt handler 327 */ 328 int 329 simide_intr(void *arg) 330 { 331 struct simide_channel *scp = arg; 332 irqhandler_t *ihp = &scp->sc_ih; 333 volatile u_char *intraddr = (volatile u_char *)ihp->ih_maskaddr; 334 335 /* XXX - not bus space yet - should really be handled by podulebus */ 336 if ((*intraddr) & ihp->ih_maskbits) 337 wdcintr(&scp->sc_channel); 338 339 return(0); 340 } 341