1 /* $NetBSD: icside.c,v 1.3 2002/05/22 22:43:18 bjh21 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 return (pa->pa_product == PODULE_ICS_IDE); 154 } 155 156 /* 157 * Card attach function 158 * 159 * Identify the card version and configure any children. 160 */ 161 162 void 163 icside_attach(parent, self, aux) 164 struct device *parent, *self; 165 void *aux; 166 { 167 struct icside_softc *sc = (void *)self; 168 struct podule_attach_args *pa = (void *)aux; 169 bus_space_tag_t iot; 170 bus_space_handle_t ioh; 171 struct ide_version *ide = NULL; 172 u_int iobase; 173 int channel; 174 struct icside_channel *icp; 175 struct channel_softc *cp; 176 int loop; 177 int id; 178 179 /* Note the podule number and validate */ 180 181 if (pa->pa_podule_number == -1) 182 panic("Podule has disappeared !"); 183 184 sc->sc_podule_number = pa->pa_podule_number; 185 sc->sc_podule = pa->pa_podule; 186 podules[sc->sc_podule_number].attached = 1; 187 188 /* The ID register if present is always in FAST podule space */ 189 iot = pa->pa_iot; 190 if (bus_space_map(iot, pa->pa_podule->fast_base + 191 ID_REGISTER_OFFSET, ID_REGISTER_SPACE, 0, &ioh)) { 192 printf("%s: cannot map ID register\n", self->dv_xname); 193 return; 194 } 195 196 for (id = 0, loop = 0; loop < 4; ++loop) 197 id |= (bus_space_read_1(iot, ioh, loop) & 1) << loop; 198 199 /* Do we recognise the ID ? */ 200 for (loop = 0; loop < sizeof(ide_versions) / sizeof(struct ide_version); 201 ++loop) { 202 if (ide_versions[loop].id == id) { 203 ide = &ide_versions[loop]; 204 break; 205 } 206 } 207 208 /* Report the version and name */ 209 if (ide == NULL || ide->name == NULL) { 210 printf(": rev %d is unsupported\n", id); 211 return; 212 } else 213 printf(": %s\n", ide->name); 214 215 /* 216 * Ok we need our own bus tag as the register spacing 217 * is not the default. 218 * 219 * For the podulebus the bus tag cookie is the shift 220 * to apply to registers 221 * So duplicate the bus space tag and change the 222 * cookie. 223 * 224 * Also while we are at it replace the default 225 * read/write mulitple short functions with 226 * optimised versions 227 */ 228 229 sc->sc_tag = *pa->pa_iot; 230 sc->sc_tag.bs_cookie = (void *) REGISTER_SPACING_SHIFT; 231 sc->sc_tag.bs_rm_2 = icside_bs_rm_2; 232 sc->sc_tag.bs_wm_2 = icside_bs_wm_2; 233 234 /* Initialize wdc struct */ 235 sc->sc_wdcdev.channels = malloc( 236 sizeof(struct channel_softc *) * ide->channels, M_DEVBUF, M_NOWAIT); 237 sc->icside_channels = malloc( 238 sizeof(struct icside_channel) * ide->channels, M_DEVBUF, M_NOWAIT); 239 if (sc->sc_wdcdev.channels == NULL || sc->icside_channels == NULL) { 240 printf("%s: can't allocate channel infos\n", 241 sc->sc_wdcdev.sc_dev.dv_xname); 242 return; 243 } 244 sc->sc_wdcdev.nchannels = ide->channels; 245 sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16; 246 sc->sc_wdcdev.PIO_cap = 0; 247 sc->sc_pa = pa; 248 249 for (channel = 0; channel < ide->channels; ++channel) { 250 icp = &sc->icside_channels[channel]; 251 sc->sc_wdcdev.channels[channel] = &icp->wdc_channel; 252 cp = &icp->wdc_channel; 253 254 cp->channel = channel; 255 cp->wdc = &sc->sc_wdcdev; 256 cp->ch_queue = malloc(sizeof(struct channel_queue), M_DEVBUF, 257 M_NOWAIT); 258 if (cp->ch_queue == NULL) { 259 printf("%s %s channel: " 260 "can't allocate memory for command queue", 261 sc->sc_wdcdev.sc_dev.dv_xname, 262 (channel == 0) ? "primary" : "secondary"); 263 continue; 264 } 265 cp->cmd_iot = &sc->sc_tag; 266 cp->ctl_iot = &sc->sc_tag; 267 if (ide->modspace) 268 iobase = pa->pa_podule->mod_base; 269 else 270 iobase = pa->pa_podule->fast_base; 271 272 if (bus_space_map(iot, iobase + ide->ideregs[channel], 273 IDE_REGISTER_SPACE, 0, &cp->cmd_ioh)) 274 return; 275 if (bus_space_map(iot, iobase + ide->auxregs[channel], 276 AUX_REGISTER_SPACE, 0, &cp->ctl_ioh)) 277 return; 278 icp->ic_irqiot = iot; 279 if (bus_space_map(iot, iobase + ide->irqregs[channel], 280 IRQ_REGISTER_SPACE, 0, &icp->ic_irqioh)) 281 return; 282 /* Disable interrupts */ 283 (void)bus_space_read_1(iot, icp->ic_irqioh, 0); 284 /* Call common attach routines */ 285 wdcattach(cp); 286 /* Disable interrupts */ 287 (void)bus_space_read_1(iot, icp->ic_irqioh, 0); 288 pa->pa_podule->irq_addr = iobase + ide->irqstatregs[channel]; 289 pa->pa_podule->irq_mask = IRQ_STATUS_REGISTER_MASK; 290 icp->ic_irqaddr = pa->pa_podule->irq_addr; 291 icp->ic_irqmask = pa->pa_podule->irq_mask; 292 evcnt_attach_dynamic(&icp->ic_intrcnt, EVCNT_TYPE_INTR, NULL, 293 self->dv_xname, "intr"); 294 icp->ic_ih = podulebus_irq_establish(pa->pa_ih, IPL_BIO, 295 icside_intr, icp, &icp->ic_intrcnt); 296 if (icp->ic_ih == NULL) { 297 printf("%s: Cannot claim interrupt %d\n", 298 sc->sc_wdcdev.sc_dev.dv_xname, 299 pa->pa_podule->interrupt); 300 continue; 301 } 302 /* Enable interrupts */ 303 bus_space_write_1(iot, icp->ic_irqioh, 0, 0); 304 } 305 } 306 307 /* 308 * Podule interrupt handler 309 * 310 * If the interrupt was from our card pass it on to the wdc interrupt handler 311 */ 312 int 313 icside_intr(arg) 314 void *arg; 315 { 316 struct icside_channel *icp = arg; 317 volatile u_char *intraddr = (volatile u_char *)icp->ic_irqaddr; 318 319 /* XXX - not bus space yet - should really be handled by podulebus */ 320 if ((*intraddr) & icp->ic_irqmask) 321 wdcintr(&icp->wdc_channel); 322 return(0); 323 } 324