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