1 /* $NetBSD: icside.c,v 1.10 2002/10/02 03:31:59 thorpej 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 * Probe and attach functions to use generic IDE driver for the ICS IDE podule 34 */ 35 36 /* 37 * Thanks to David Baildon for loaning an IDE card for the development 38 * of this driver. 39 * Thanks to Ian Copestake and David Baildon for providing register mapping 40 * information 41 */ 42 43 #include <sys/param.h> 44 45 __KERNEL_RCSID(0, "$NetBSD: icside.c,v 1.10 2002/10/02 03:31:59 thorpej Exp $"); 46 47 #include <sys/systm.h> 48 #include <sys/conf.h> 49 #include <sys/device.h> 50 #include <sys/malloc.h> 51 52 #include <machine/intr.h> 53 #include <machine/io.h> 54 #include <machine/bus.h> 55 #include <acorn32/podulebus/podulebus.h> 56 #include <acorn32/podulebus/icsidereg.h> 57 58 #include <dev/ata/atavar.h> 59 #include <dev/ic/wdcvar.h> 60 #include <dev/podulebus/podules.h> 61 62 /* 63 * ICS IDE podule device. 64 * 65 * This probes and attaches the top level ICS IDE device to the podulebus. 66 * It then configures any children of the ICS IDE device. 67 * The child is expected to be a wdc device using icside attachments. 68 */ 69 70 /* 71 * ICS IDE card softc structure. 72 * 73 * Contains the device node and podule information. 74 */ 75 76 struct icside_softc { 77 struct wdc_softc sc_wdcdev; /* common wdc definitions */ 78 podule_t *sc_podule; /* Our podule */ 79 int sc_podule_number; /* Our podule number */ 80 struct bus_space sc_tag; /* custom tag */ 81 struct podule_attach_args *sc_pa; /* podule info */ 82 bus_space_tag_t sc_latchiot; /* EEPROM page latch etc */ 83 bus_space_handle_t sc_latchioh; 84 void *sc_shutdownhook; 85 struct channel_softc *sc_chp[ICSIDE_MAX_CHANNELS]; 86 struct icside_channel { 87 struct channel_softc wdc_channel; /* generic part */ 88 void *ic_ih; /* interrupt handler */ 89 struct evcnt ic_intrcnt; /* interrupt count */ 90 u_int ic_irqaddr; /* interrupt flag */ 91 u_int ic_irqmask; /* location */ 92 bus_space_tag_t ic_irqiot; /* Bus space tag */ 93 bus_space_handle_t ic_irqioh; /* handle for IRQ */ 94 } sc_chan[ICSIDE_MAX_CHANNELS]; 95 }; 96 97 int icside_probe(struct device *, struct cfdata *, void *); 98 void icside_attach(struct device *, struct device *, void *); 99 int icside_intr(void *); 100 void icside_v6_shutdown(void *); 101 102 CFATTACH_DECL(icside, sizeof(struct icside_softc), 103 icside_probe, icside_attach, NULL, NULL); 104 105 /* 106 * Define prototypes for custom bus space functions. 107 */ 108 109 bs_rm_2_proto(icside); 110 bs_wm_2_proto(icside); 111 112 #define MAX_CHANNELS 2 113 114 /* 115 * Define a structure for describing the different card versions 116 */ 117 struct ide_version { 118 int id; /* IDE card ID */ 119 int modspace; /* Type of podule space */ 120 int channels; /* Number of channels */ 121 const char *name; /* name */ 122 int latchreg; /* EEPROM latch register */ 123 int ideregs[MAX_CHANNELS]; /* IDE registers */ 124 int auxregs[MAX_CHANNELS]; /* AUXSTAT register */ 125 int irqregs[MAX_CHANNELS]; /* IRQ register */ 126 int irqstatregs[MAX_CHANNELS]; 127 } const ide_versions[] = { 128 /* A3IN - Unsupported */ 129 /* { 0, 0, 0, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },*/ 130 /* A3USER - Unsupported */ 131 /* { 1, 0, 0, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },*/ 132 /* ARCIN V6 - Supported */ 133 { 3, 0, 2, "ARCIN V6", V6_ADDRLATCH, 134 { V6_P_IDE_BASE, V6_S_IDE_BASE }, 135 { V6_P_AUX_BASE, V6_S_AUX_BASE }, 136 { V6_P_IRQ_BASE, V6_S_IRQ_BASE }, 137 { V6_P_IRQSTAT_BASE, V6_S_IRQSTAT_BASE } 138 }, 139 /* ARCIN V5 - Supported (ID reg not supported so reads as 15) */ 140 { 15, 1, 1, "ARCIN V5", -1, 141 { V5_IDE_BASE, -1 }, 142 { V5_AUX_BASE, -1 }, 143 { V5_IRQ_BASE, -1 }, 144 { V5_IRQSTAT_BASE, -1 } 145 } 146 }; 147 148 /* 149 * Card probe function 150 * 151 * Just match the manufacturer and podule ID's 152 */ 153 154 int 155 icside_probe(struct device *parent, struct cfdata *cf, void *aux) 156 { 157 struct podule_attach_args *pa = (void *)aux; 158 159 return (pa->pa_product == PODULE_ICS_IDE); 160 } 161 162 /* 163 * Card attach function 164 * 165 * Identify the card version and configure any children. 166 */ 167 168 void 169 icside_attach(struct device *parent, struct device *self, void *aux) 170 { 171 struct icside_softc *sc = (void *)self; 172 struct podule_attach_args *pa = (void *)aux; 173 bus_space_tag_t iot; 174 bus_space_handle_t ioh; 175 const struct ide_version *ide = NULL; 176 u_int iobase; 177 int channel; 178 struct icside_channel *icp; 179 struct channel_softc *cp; 180 int loop; 181 int id; 182 183 /* Note the podule number and validate */ 184 185 if (pa->pa_podule_number == -1) 186 panic("Podule has disappeared !"); 187 188 sc->sc_podule_number = pa->pa_podule_number; 189 sc->sc_podule = pa->pa_podule; 190 podules[sc->sc_podule_number].attached = 1; 191 192 /* The ID register if present is always in FAST podule space */ 193 iot = pa->pa_iot; 194 if (bus_space_map(iot, pa->pa_podule->fast_base + 195 ID_REGISTER_OFFSET, ID_REGISTER_SPACE, 0, &ioh)) { 196 printf("%s: cannot map ID register\n", self->dv_xname); 197 return; 198 } 199 200 for (id = 0, loop = 0; loop < 4; ++loop) 201 id |= (bus_space_read_1(iot, ioh, loop) & 1) << loop; 202 203 /* Do we recognise the ID ? */ 204 for (loop = 0; loop < sizeof(ide_versions) / sizeof(struct ide_version); 205 ++loop) { 206 if (ide_versions[loop].id == id) { 207 ide = &ide_versions[loop]; 208 break; 209 } 210 } 211 212 /* Report the version and name */ 213 if (ide == NULL || ide->name == NULL) { 214 printf(": rev %d is unsupported\n", id); 215 return; 216 } else 217 printf(": %s\n", ide->name); 218 219 if (ide->latchreg != -1) { 220 sc->sc_latchiot = pa->pa_iot; 221 if (bus_space_map(iot, pa->pa_podule->fast_base + 222 ide->latchreg, 1, 0, &sc->sc_latchioh)) { 223 printf("%s: cannot map latch register\n", 224 self->dv_xname); 225 return; 226 } 227 sc->sc_shutdownhook = 228 shutdownhook_establish(icside_v6_shutdown, sc); 229 } 230 231 /* 232 * Ok we need our own bus tag as the register spacing 233 * is not the default. 234 * 235 * For the podulebus the bus tag cookie is the shift 236 * to apply to registers 237 * So duplicate the bus space tag and change the 238 * cookie. 239 * 240 * Also while we are at it replace the default 241 * read/write mulitple short functions with 242 * optimised versions 243 */ 244 245 sc->sc_tag = *pa->pa_iot; 246 sc->sc_tag.bs_cookie = (void *) REGISTER_SPACING_SHIFT; 247 sc->sc_tag.bs_rm_2 = icside_bs_rm_2; 248 sc->sc_tag.bs_wm_2 = icside_bs_wm_2; 249 250 /* Initialize wdc struct */ 251 sc->sc_wdcdev.channels = sc->sc_chp; 252 sc->sc_wdcdev.nchannels = ide->channels; 253 sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16; 254 sc->sc_wdcdev.PIO_cap = 0; 255 sc->sc_pa = pa; 256 257 for (channel = 0; channel < ide->channels; ++channel) { 258 icp = &sc->sc_chan[channel]; 259 sc->sc_wdcdev.channels[channel] = &icp->wdc_channel; 260 cp = &icp->wdc_channel; 261 262 cp->channel = channel; 263 cp->wdc = &sc->sc_wdcdev; 264 cp->ch_queue = malloc(sizeof(struct channel_queue), M_DEVBUF, 265 M_NOWAIT); 266 if (cp->ch_queue == NULL) { 267 printf("%s:%d: " 268 "can't allocate memory for command queue", 269 sc->sc_wdcdev.sc_dev.dv_xname, channel); 270 continue; 271 } 272 cp->cmd_iot = &sc->sc_tag; 273 cp->ctl_iot = &sc->sc_tag; 274 if (ide->modspace) 275 iobase = pa->pa_podule->mod_base; 276 else 277 iobase = pa->pa_podule->fast_base; 278 279 if (bus_space_map(iot, iobase + ide->ideregs[channel], 280 IDE_REGISTER_SPACE, 0, &cp->cmd_ioh)) 281 return; 282 if (bus_space_map(iot, iobase + ide->auxregs[channel], 283 AUX_REGISTER_SPACE, 0, &cp->ctl_ioh)) 284 return; 285 icp->ic_irqiot = iot; 286 if (bus_space_map(iot, iobase + ide->irqregs[channel], 287 IRQ_REGISTER_SPACE, 0, &icp->ic_irqioh)) 288 return; 289 /* Disable interrupts */ 290 (void)bus_space_read_1(iot, icp->ic_irqioh, 0); 291 /* Call common attach routines */ 292 wdcattach(cp); 293 /* Disable interrupts */ 294 (void)bus_space_read_1(iot, icp->ic_irqioh, 0); 295 pa->pa_podule->irq_addr = iobase + ide->irqstatregs[channel]; 296 pa->pa_podule->irq_mask = IRQ_STATUS_REGISTER_MASK; 297 icp->ic_irqaddr = pa->pa_podule->irq_addr; 298 icp->ic_irqmask = pa->pa_podule->irq_mask; 299 evcnt_attach_dynamic(&icp->ic_intrcnt, EVCNT_TYPE_INTR, NULL, 300 self->dv_xname, "intr"); 301 icp->ic_ih = podulebus_irq_establish(pa->pa_ih, IPL_BIO, 302 icside_intr, icp, &icp->ic_intrcnt); 303 if (icp->ic_ih == NULL) { 304 printf("%s: Cannot claim interrupt %d\n", 305 sc->sc_wdcdev.sc_dev.dv_xname, 306 pa->pa_podule->interrupt); 307 continue; 308 } 309 /* Enable interrupts */ 310 bus_space_write_1(iot, icp->ic_irqioh, 0, 0); 311 } 312 } 313 314 /* 315 * Shutdown handler -- try to restore the card to a state where 316 * RISC OS will see it. 317 */ 318 void 319 icside_v6_shutdown(void *arg) 320 { 321 struct icside_softc *sc = arg; 322 323 bus_space_write_1(sc->sc_latchiot, sc->sc_latchioh, 0, 0); 324 } 325 326 /* 327 * Podule interrupt handler 328 * 329 * If the interrupt was from our card pass it on to the wdc interrupt handler 330 */ 331 int 332 icside_intr(void *arg) 333 { 334 struct icside_channel *icp = arg; 335 volatile u_char *intraddr = (volatile u_char *)icp->ic_irqaddr; 336 337 /* XXX - not bus space yet - should really be handled by podulebus */ 338 if ((*intraddr) & icp->ic_irqmask) 339 wdcintr(&icp->wdc_channel); 340 return(0); 341 } 342