1 /* $NetBSD: oak.c,v 1.9 2002/05/22 22:43:14 bjh21 Exp $ */ 2 3 /* 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Mark Brinicombe of Causality Limited. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Oak Solutions SCSI 1 driver using the generic NCR5380 driver. 41 * 42 * From <URL:http://foldoc.doc.ic.ac.uk/acorn/doc/scsi>: 43 * --------8<-------- 44 * From: Hugo Fiennes 45 * [...] 46 * The oak scsi plays some other tricks to get max around 2.2Mb/sec: 47 * it is a 16- bit interface (using their own hardware and an 8-bit 48 * scsi controller to 'double-up' the data). What it does is: every 49 * 128 bytes it uses a polling loop (see above) to check data is 50 * present and the drive has reported no errors, etc. Inside each 128 51 * byte block it just reads data as fast as it can: on a normal card 52 * this would result in disaster if the drive wasn't fast enough to 53 * feed the machine: on the oak card however, the hardware will not 54 * assert IOGT (IO grant), so hanging the machine in a wait state 55 * until data is ready. This can have problems: if the drive is to 56 * slow (unlikely) the machine will completely stiff as the ARM3 can't 57 * be kept in such a state for more than 10(?) us. 58 * -------->8-------- 59 * 60 * So far, my attempts at doing this have failed, though. 61 * 62 * This card has to be polled: it doesn't have anything connected to 63 * PIRQ*. This seems to be a common failing of Archimedes disc 64 * controllers. 65 */ 66 67 #include <sys/cdefs.h> 68 __KERNEL_RCSID(0, "$NetBSD: oak.c,v 1.9 2002/05/22 22:43:14 bjh21 Exp $"); 69 70 #include <sys/param.h> 71 72 #include <sys/systm.h> 73 #include <sys/kernel.h> 74 #include <sys/device.h> 75 #include <sys/buf.h> 76 #include <dev/scsipi/scsi_all.h> 77 #include <dev/scsipi/scsipi_all.h> 78 #include <dev/scsipi/scsiconf.h> 79 80 #include <dev/ic/ncr5380reg.h> 81 #include <dev/ic/ncr5380var.h> 82 83 #include <machine/bootconfig.h> 84 85 #include <dev/podulebus/podulebus.h> 86 #include <dev/podulebus/podules.h> 87 #include <dev/podulebus/powerromreg.h> 88 89 #include <dev/podulebus/oakreg.h> 90 91 void oak_attach (struct device *, struct device *, void *); 92 int oak_match (struct device *, struct cfdata *, void *); 93 94 #if 0 95 static int oak_pdma_in(struct ncr5380_softc *, int, int, u_char *); 96 static int oak_pdma_out(struct ncr5380_softc *, int, int, u_char *); 97 #endif 98 99 /* 100 * Oak SCSI 1 softc structure. 101 * 102 * Contains the generic ncr5380 device node, podule information and 103 * global information required by the driver. 104 */ 105 106 struct oak_softc { 107 struct ncr5380_softc sc_ncr5380; 108 bus_space_tag_t sc_pdmat; 109 bus_space_handle_t sc_pdmah; 110 }; 111 112 struct cfattach oak_ca = { 113 sizeof(struct oak_softc), oak_match, oak_attach 114 }; 115 116 /* 117 * Card probe function 118 * 119 * Just match the manufacturer and podule ID's 120 */ 121 122 int 123 oak_match(struct device *parent, struct cfdata *cf, void *aux) 124 { 125 struct podulebus_attach_args *pa = aux; 126 127 if (pa->pa_product == PODULE_OAK_SCSI) 128 return 1; 129 130 /* PowerROM */ 131 if (pa->pa_product == PODULE_ALSYSTEMS_SCSI && 132 podulebus_initloader(pa) == 0 && 133 podloader_callloader(pa, 0, 0) == PRID_OAK_SCSI1) 134 return 1; 135 136 return 0; 137 } 138 139 /* 140 * Card attach function 141 * 142 */ 143 144 void 145 oak_attach(struct device *parent, struct device *self, void *aux) 146 { 147 struct oak_softc *sc = (struct oak_softc *)self; 148 struct podulebus_attach_args *pa = aux; 149 #ifndef NCR5380_USE_BUS_SPACE 150 u_char *iobase; 151 #endif 152 char hi_option[sizeof(sc->sc_ncr5380.sc_dev.dv_xname) + 8]; 153 154 sc->sc_ncr5380.sc_flags |= NCR5380_FORCE_POLLING; 155 sc->sc_ncr5380.sc_min_dma_len = 0; 156 sc->sc_ncr5380.sc_no_disconnect = 0xff; 157 sc->sc_ncr5380.sc_parity_disable = 0; 158 159 sc->sc_ncr5380.sc_dma_alloc = NULL; 160 sc->sc_ncr5380.sc_dma_free = NULL; 161 sc->sc_ncr5380.sc_dma_poll = NULL; 162 sc->sc_ncr5380.sc_dma_setup = NULL; 163 sc->sc_ncr5380.sc_dma_start = NULL; 164 sc->sc_ncr5380.sc_dma_eop = NULL; 165 sc->sc_ncr5380.sc_dma_stop = NULL; 166 sc->sc_ncr5380.sc_intr_on = NULL; 167 sc->sc_ncr5380.sc_intr_off = NULL; 168 169 #ifdef NCR5380_USE_BUS_SPACE 170 sc->sc_ncr5380.sc_regt = pa->pa_mod_t; 171 bus_space_map(sc->sc_ncr5380.sc_regt, pa->pa_mod_base, 8, 0, 172 &sc->sc_ncr5380.sc_regh); 173 sc->sc_ncr5380.sci_r0 = 0; 174 sc->sc_ncr5380.sci_r1 = 1; 175 sc->sc_ncr5380.sci_r2 = 2; 176 sc->sc_ncr5380.sci_r3 = 3; 177 sc->sc_ncr5380.sci_r4 = 4; 178 sc->sc_ncr5380.sci_r5 = 5; 179 sc->sc_ncr5380.sci_r6 = 6; 180 sc->sc_ncr5380.sci_r7 = 7; 181 #else 182 iobase = (u_char *)pa->pa_mod_base; 183 sc->sc_ncr5380.sci_r0 = iobase + 0; 184 sc->sc_ncr5380.sci_r1 = iobase + 4; 185 sc->sc_ncr5380.sci_r2 = iobase + 8; 186 sc->sc_ncr5380.sci_r3 = iobase + 12; 187 sc->sc_ncr5380.sci_r4 = iobase + 16; 188 sc->sc_ncr5380.sci_r5 = iobase + 20; 189 sc->sc_ncr5380.sci_r6 = iobase + 24; 190 sc->sc_ncr5380.sci_r7 = iobase + 28; 191 #endif 192 sc->sc_pdmat = pa->pa_mod_t; 193 bus_space_map(sc->sc_pdmat, pa->pa_mod_base + OAK_PDMA_OFFSET, 0x20, 0, 194 &sc->sc_pdmah); 195 196 sc->sc_ncr5380.sc_rev = NCR_VARIANT_NCR5380; 197 198 sc->sc_ncr5380.sc_pio_in = ncr5380_pio_in; 199 sc->sc_ncr5380.sc_pio_out = ncr5380_pio_out; 200 201 /* Provide an override for the host id */ 202 sc->sc_ncr5380.sc_channel.chan_id = 7; 203 sprintf(hi_option, "%s.hostid", sc->sc_ncr5380.sc_dev.dv_xname); 204 (void)get_bootconf_option(boot_args, hi_option, 205 BOOTOPT_TYPE_INT, &sc->sc_ncr5380.sc_channel.chan_id); 206 sc->sc_ncr5380.sc_adapter.adapt_minphys = minphys; 207 208 printf(": host=%d, using 8 bit PIO\n", 209 sc->sc_ncr5380.sc_channel.chan_id); 210 211 ncr5380_attach(&sc->sc_ncr5380); 212 } 213 214 /* 215 * XXX The code below doesn't work correctly. I probably need more 216 * details on how the card works. [bjh21 20011202] 217 */ 218 #if 0 219 220 #ifndef OAK_TSIZE_OUT 221 #define OAK_TSIZE_OUT 128 222 #endif 223 224 #ifndef OAK_TSIZE_IN 225 #define OAK_TSIZE_IN 128 226 #endif 227 228 #define TIMEOUT 1000000 229 230 static __inline int 231 oak_ready(struct ncr5380_softc *sc) 232 { 233 int i; 234 int status; 235 236 for (i = TIMEOUT; i > 0; i--) { 237 status = NCR5380_READ(sc, sci_csr); 238 if ((status & (SCI_CSR_DREQ | SCI_CSR_PHASE_MATCH)) == 239 (SCI_CSR_DREQ | SCI_CSR_PHASE_MATCH)) 240 return(1); 241 242 if ((status & SCI_CSR_PHASE_MATCH) == 0 || 243 SCI_BUSY(sc) == 0) 244 return(0); 245 } 246 printf("%s: ready timeout\n", sc->sc_dev.dv_xname); 247 return(0); 248 249 #if 0 /* The Linux driver does this: */ 250 struct oak_softc *sc = (void *)ncr_sc; 251 bus_space_tag_t pdmat = sc->sc_pdmat; 252 bus_space_handle_t pdmah = sc->sc_pdmah; 253 int i, status; 254 255 for (i = TIMEOUT; i > 0; i--) { 256 status = bus_space_read_2(pdmat, pdmah, OAK_PDMA_STATUS); 257 if (status & 0x200) 258 return(0); 259 if (status & 0x100) 260 return(1); 261 } 262 printf("%s: ready timeout, status = 0x%x\n", ncr_sc->sc_dev.dv_xname, 263 status); 264 return(0); 265 #endif 266 } 267 268 269 270 /* Return zero on success. */ 271 static __inline void oak_wait_not_req(struct ncr5380_softc *sc) 272 { 273 int timo; 274 for (timo = TIMEOUT; timo; timo--) { 275 if ((NCR5380_READ(sc, sci_bus_csr) & SCI_BUS_REQ) == 0 || 276 (NCR5380_READ(sc, sci_csr) & SCI_CSR_PHASE_MATCH) == 0 || 277 SCI_BUSY(sc) == 0) { 278 return; 279 } 280 } 281 printf("%s: pdma not_req timeout\n", sc->sc_dev.dv_xname); 282 } 283 284 static int 285 oak_pdma_in(struct ncr5380_softc *ncr_sc, int phase, int datalen, 286 u_char *data) 287 { 288 struct oak_softc *sc = (void *)ncr_sc; 289 bus_space_tag_t pdmat = sc->sc_pdmat; 290 bus_space_handle_t pdmah = sc->sc_pdmah; 291 int s, resid, len; 292 293 s = splbio(); 294 295 NCR5380_WRITE(ncr_sc, sci_mode, 296 NCR5380_READ(ncr_sc, sci_mode) | SCI_MODE_DMA); 297 NCR5380_WRITE(ncr_sc, sci_irecv, 0); 298 299 resid = datalen; 300 while (resid > 0) { 301 len = min(resid, OAK_TSIZE_IN); 302 if (oak_ready(ncr_sc) == 0) 303 goto interrupt; 304 KASSERT(BUS_SPACE_ALIGNED_POINTER(data, u_int16_t)); 305 bus_space_read_multi_2(pdmat, pdmah, OAK_PDMA_READ, 306 (u_int16_t *)data, len/2); 307 data += len; 308 resid -= len; 309 } 310 311 oak_wait_not_req(ncr_sc); 312 313 interrupt: 314 SCI_CLR_INTR(ncr_sc); 315 NCR5380_WRITE(ncr_sc, sci_mode, 316 NCR5380_READ(ncr_sc, sci_mode) & ~SCI_MODE_DMA); 317 splx(s); 318 return datalen - resid; 319 } 320 321 static int 322 oak_pdma_out(struct ncr5380_softc *ncr_sc, int phase, int datalen, 323 u_char *data) 324 { 325 struct oak_softc *sc = (void *)ncr_sc; 326 bus_space_tag_t pdmat = sc->sc_pdmat; 327 bus_space_handle_t pdmah = sc->sc_pdmah; 328 int i, s, icmd, resid; 329 330 s = splbio(); 331 icmd = NCR5380_READ(ncr_sc, sci_icmd) & SCI_ICMD_RMASK; 332 NCR5380_WRITE(ncr_sc, sci_icmd, icmd | SCI_ICMD_DATA); 333 NCR5380_WRITE(ncr_sc, sci_mode, 334 NCR5380_READ(ncr_sc, sci_mode) | SCI_MODE_DMA); 335 NCR5380_WRITE(ncr_sc, sci_dma_send, 0); 336 337 resid = datalen; 338 if (oak_ready(ncr_sc) == 0) 339 goto interrupt; 340 341 if (resid > OAK_TSIZE_OUT) { 342 /* 343 * Because of the chips DMA prefetch, phase changes 344 * etc, won't be detected until we have written at 345 * least one byte more. We pre-write 4 bytes so 346 * subsequent transfers will be aligned to a 4 byte 347 * boundary. Assuming disconects will only occur on 348 * block boundaries, we then correct for the pre-write 349 * when and if we get a phase change. If the chip had 350 * DMA byte counting hardware, the assumption would not 351 * be necessary. 352 */ 353 KASSERT(BUS_SPACE_ALIGNED_POINTER(data, u_int16_t)); 354 bus_space_write_multi_2(pdmat, pdmah, OAK_PDMA_WRITE, 355 (u_int16_t *)data, 4/2); 356 data += 4; 357 resid -= 4; 358 359 for (; resid >= OAK_TSIZE_OUT; resid -= OAK_TSIZE_OUT) { 360 if (oak_ready(ncr_sc) == 0) { 361 resid += 4; /* Overshot */ 362 goto interrupt; 363 } 364 bus_space_write_multi_2(pdmat, pdmah, OAK_PDMA_WRITE, 365 (u_int16_t *)data, OAK_TSIZE_OUT/2); 366 data += OAK_TSIZE_OUT; 367 } 368 if (oak_ready(ncr_sc) == 0) { 369 resid += 4; /* Overshot */ 370 goto interrupt; 371 } 372 } 373 374 if (resid) { 375 bus_space_write_multi_2(pdmat, pdmah, OAK_PDMA_WRITE, 376 (u_int16_t *)data, resid/2); 377 resid = 0; 378 } 379 for (i = TIMEOUT; i > 0; i--) { 380 if ((NCR5380_READ(ncr_sc, sci_csr) 381 & (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) 382 != SCI_CSR_DREQ) 383 break; 384 } 385 if (i != 0) 386 bus_space_write_2(pdmat, pdmah, OAK_PDMA_WRITE, 0); 387 else 388 printf("%s: timeout waiting for final SCI_DSR_DREQ.\n", 389 ncr_sc->sc_dev.dv_xname); 390 391 oak_wait_not_req(ncr_sc); 392 interrupt: 393 SCI_CLR_INTR(ncr_sc); 394 NCR5380_WRITE(ncr_sc, sci_mode, 395 NCR5380_READ(ncr_sc, sci_mode) & ~SCI_MODE_DMA); 396 NCR5380_WRITE(ncr_sc, sci_icmd, icmd); 397 splx(s); 398 return(datalen - resid); 399 } 400 #endif 401