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