1 /* $NetBSD: oak.c,v 1.18 2008/04/04 16:00:58 tsutsui 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.18 2008/04/04 16:00:58 tsutsui 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 int oak_match(device_t, cfdata_t, void *); 92 void oak_attach(device_t, device_t, void *); 93 94 #if 0 95 static int oak_pdma_in(struct ncr5380_softc *, int, int, uint8_t *); 96 static int oak_pdma_out(struct ncr5380_softc *, int, int, uint8_t *); 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 CFATTACH_DECL_NEW(oak, sizeof(struct oak_softc), 113 oak_match, oak_attach, NULL, NULL); 114 115 /* 116 * Card probe function 117 * 118 * Just match the manufacturer and podule ID's 119 */ 120 121 int 122 oak_match(device_t parent, cfdata_t cf, void *aux) 123 { 124 struct podulebus_attach_args *pa = aux; 125 126 if (pa->pa_product == PODULE_OAK_SCSI) 127 return 1; 128 129 /* PowerROM */ 130 if (pa->pa_product == PODULE_ALSYSTEMS_SCSI && 131 podulebus_initloader(pa) == 0 && 132 podloader_callloader(pa, 0, 0) == PRID_OAK_SCSI1) 133 return 1; 134 135 return 0; 136 } 137 138 /* 139 * Card attach function 140 * 141 */ 142 143 void 144 oak_attach(device_t parent, device_t self, void *aux) 145 { 146 struct oak_softc *sc = device_private(self); 147 struct ncr5380_softc *ncr_sc = &sc->sc_ncr5380; 148 struct podulebus_attach_args *pa = aux; 149 #ifndef NCR5380_USE_BUS_SPACE 150 uint8_t *iobase; 151 #endif 152 char hi_option[sizeof(self->dv_xname) + 8]; 153 154 ncr_sc->sc_dev = self; 155 ncr_sc->sc_flags |= NCR5380_FORCE_POLLING; 156 ncr_sc->sc_min_dma_len = 0; 157 ncr_sc->sc_no_disconnect = 0xff; 158 ncr_sc->sc_parity_disable = 0; 159 160 ncr_sc->sc_dma_alloc = NULL; 161 ncr_sc->sc_dma_free = NULL; 162 ncr_sc->sc_dma_poll = NULL; 163 ncr_sc->sc_dma_setup = NULL; 164 ncr_sc->sc_dma_start = NULL; 165 ncr_sc->sc_dma_eop = NULL; 166 ncr_sc->sc_dma_stop = NULL; 167 ncr_sc->sc_intr_on = NULL; 168 ncr_sc->sc_intr_off = NULL; 169 170 #ifdef NCR5380_USE_BUS_SPACE 171 ncr_sc->sc_regt = pa->pa_mod_t; 172 bus_space_map(ncr_sc->sc_regt, pa->pa_mod_base, 8, 0, 173 &ncr_sc->sc_regh); 174 ncr_sc->sci_r0 = 0; 175 ncr_sc->sci_r1 = 1; 176 ncr_sc->sci_r2 = 2; 177 ncr_sc->sci_r3 = 3; 178 ncr_sc->sci_r4 = 4; 179 ncr_sc->sci_r5 = 5; 180 ncr_sc->sci_r6 = 6; 181 ncr_sc->sci_r7 = 7; 182 #else 183 iobase = (uint8_t *)pa->pa_mod_base; 184 ncr_sc->sci_r0 = iobase + 0; 185 ncr_sc->sci_r1 = iobase + 4; 186 ncr_sc->sci_r2 = iobase + 8; 187 ncr_sc->sci_r3 = iobase + 12; 188 ncr_sc->sci_r4 = iobase + 16; 189 ncr_sc->sci_r5 = iobase + 20; 190 ncr_sc->sci_r6 = iobase + 24; 191 ncr_sc->sci_r7 = iobase + 28; 192 #endif 193 sc->sc_pdmat = pa->pa_mod_t; 194 bus_space_map(sc->sc_pdmat, pa->pa_mod_base + OAK_PDMA_OFFSET, 0x20, 0, 195 &sc->sc_pdmah); 196 197 ncr_sc->sc_rev = NCR_VARIANT_NCR5380; 198 199 ncr_sc->sc_pio_in = ncr5380_pio_in; 200 ncr_sc->sc_pio_out = ncr5380_pio_out; 201 202 /* Provide an override for the host id */ 203 ncr_sc->sc_channel.chan_id = 7; 204 snprintf(hi_option, sizeof(hi_option), "%s.hostid", 205 device_xname(self)); 206 (void)get_bootconf_option(boot_args, hi_option, 207 BOOTOPT_TYPE_INT, &ncr_sc->sc_channel.chan_id); 208 ncr_sc->sc_adapter.adapt_minphys = minphys; 209 210 aprint_normal(": host=%d, using 8 bit PIO\n", 211 ncr_sc->sc_channel.chan_id); 212 213 ncr5380_attach(ncr_sc); 214 } 215 216 /* 217 * XXX The code below doesn't work correctly. I probably need more 218 * details on how the card works. [bjh21 20011202] 219 */ 220 #if 0 221 222 #ifndef OAK_TSIZE_OUT 223 #define OAK_TSIZE_OUT 128 224 #endif 225 226 #ifndef OAK_TSIZE_IN 227 #define OAK_TSIZE_IN 128 228 #endif 229 230 #define TIMEOUT 1000000 231 232 static inline int 233 oak_ready(struct ncr5380_softc *sc) 234 { 235 int i; 236 int status; 237 238 for (i = TIMEOUT; i > 0; i--) { 239 status = NCR5380_READ(sc, sci_csr); 240 if ((status & (SCI_CSR_DREQ | SCI_CSR_PHASE_MATCH)) == 241 (SCI_CSR_DREQ | SCI_CSR_PHASE_MATCH)) 242 return 1; 243 244 if ((status & SCI_CSR_PHASE_MATCH) == 0 || 245 SCI_BUSY(sc) == 0) 246 return 0; 247 } 248 printf("%s: ready timeout\n", device_xname(sc->sc_dev)); 249 return 0; 250 251 #if 0 /* The Linux driver does this: */ 252 struct oak_softc *sc = (void *)ncr_sc; 253 bus_space_tag_t pdmat = sc->sc_pdmat; 254 bus_space_handle_t pdmah = sc->sc_pdmah; 255 int i, status; 256 257 for (i = TIMEOUT; i > 0; i--) { 258 status = bus_space_read_2(pdmat, pdmah, OAK_PDMA_STATUS); 259 if (status & 0x200) 260 return 0; 261 if (status & 0x100) 262 return 1; 263 } 264 printf("%s: ready timeout, status = 0x%x\n", 265 device_xname(ncr_sc->sc_dev), status); 266 return 0; 267 #endif 268 } 269 270 271 272 /* Return zero on success. */ 273 static inline void oak_wait_not_req(struct ncr5380_softc *sc) 274 { 275 int timo; 276 for (timo = TIMEOUT; timo; timo--) { 277 if ((NCR5380_READ(sc, sci_bus_csr) & SCI_BUS_REQ) == 0 || 278 (NCR5380_READ(sc, sci_csr) & SCI_CSR_PHASE_MATCH) == 0 || 279 SCI_BUSY(sc) == 0) { 280 return; 281 } 282 } 283 printf("%s: pdma not_req timeout\n", device_xname(sc->sc_dev)); 284 } 285 286 static int 287 oak_pdma_in(struct ncr5380_softc *ncr_sc, int phase, int datalen, 288 uint8_t *data) 289 { 290 struct oak_softc *sc = (void *)ncr_sc; 291 bus_space_tag_t pdmat = sc->sc_pdmat; 292 bus_space_handle_t pdmah = sc->sc_pdmah; 293 int s, resid, len; 294 295 s = splbio(); 296 297 NCR5380_WRITE(ncr_sc, sci_mode, 298 NCR5380_READ(ncr_sc, sci_mode) | SCI_MODE_DMA); 299 NCR5380_WRITE(ncr_sc, sci_irecv, 0); 300 301 resid = datalen; 302 while (resid > 0) { 303 len = min(resid, OAK_TSIZE_IN); 304 if (oak_ready(ncr_sc) == 0) 305 goto interrupt; 306 KASSERT(BUS_SPACE_ALIGNED_POINTER(data, uint16_t)); 307 bus_space_read_multi_2(pdmat, pdmah, OAK_PDMA_READ, 308 (uint16_t *)data, len / 2); 309 data += len; 310 resid -= len; 311 } 312 313 oak_wait_not_req(ncr_sc); 314 315 interrupt: 316 SCI_CLR_INTR(ncr_sc); 317 NCR5380_WRITE(ncr_sc, sci_mode, 318 NCR5380_READ(ncr_sc, sci_mode) & ~SCI_MODE_DMA); 319 splx(s); 320 return datalen - resid; 321 } 322 323 static int 324 oak_pdma_out(struct ncr5380_softc *ncr_sc, int phase, int datalen, 325 uint8_t *data) 326 { 327 struct oak_softc *sc = (struct oak_softc *)ncr_sc; 328 bus_space_tag_t pdmat = sc->sc_pdmat; 329 bus_space_handle_t pdmah = sc->sc_pdmah; 330 int i, s, icmd, resid; 331 332 s = splbio(); 333 icmd = NCR5380_READ(ncr_sc, sci_icmd) & SCI_ICMD_RMASK; 334 NCR5380_WRITE(ncr_sc, sci_icmd, icmd | SCI_ICMD_DATA); 335 NCR5380_WRITE(ncr_sc, sci_mode, 336 NCR5380_READ(ncr_sc, sci_mode) | SCI_MODE_DMA); 337 NCR5380_WRITE(ncr_sc, sci_dma_send, 0); 338 339 resid = datalen; 340 if (oak_ready(ncr_sc) == 0) 341 goto interrupt; 342 343 if (resid > OAK_TSIZE_OUT) { 344 /* 345 * Because of the chips DMA prefetch, phase changes 346 * etc, won't be detected until we have written at 347 * least one byte more. We pre-write 4 bytes so 348 * subsequent transfers will be aligned to a 4 byte 349 * boundary. Assuming disconects will only occur on 350 * block boundaries, we then correct for the pre-write 351 * when and if we get a phase change. If the chip had 352 * DMA byte counting hardware, the assumption would not 353 * be necessary. 354 */ 355 KASSERT(BUS_SPACE_ALIGNED_POINTER(data, uint16_t)); 356 bus_space_write_multi_2(pdmat, pdmah, OAK_PDMA_WRITE, 357 (uint16_t *)data, 4 / 2); 358 data += 4; 359 resid -= 4; 360 361 for (; resid >= OAK_TSIZE_OUT; resid -= OAK_TSIZE_OUT) { 362 if (oak_ready(ncr_sc) == 0) { 363 resid += 4; /* Overshot */ 364 goto interrupt; 365 } 366 bus_space_write_multi_2(pdmat, pdmah, OAK_PDMA_WRITE, 367 (uint16_t *)data, OAK_TSIZE_OUT / 2); 368 data += OAK_TSIZE_OUT; 369 } 370 if (oak_ready(ncr_sc) == 0) { 371 resid += 4; /* Overshot */ 372 goto interrupt; 373 } 374 } 375 376 if (resid) { 377 bus_space_write_multi_2(pdmat, pdmah, OAK_PDMA_WRITE, 378 (uint16_t *)data, resid / 2); 379 resid = 0; 380 } 381 for (i = TIMEOUT; i > 0; i--) { 382 if ((NCR5380_READ(ncr_sc, sci_csr) 383 & (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) 384 != SCI_CSR_DREQ) 385 break; 386 } 387 if (i != 0) 388 bus_space_write_2(pdmat, pdmah, OAK_PDMA_WRITE, 0); 389 else 390 printf("%s: timeout waiting for final SCI_DSR_DREQ.\n", 391 device_xname(ncr_sc->sc_dev)); 392 393 oak_wait_not_req(ncr_sc); 394 interrupt: 395 SCI_CLR_INTR(ncr_sc); 396 NCR5380_WRITE(ncr_sc, sci_mode, 397 NCR5380_READ(ncr_sc, sci_mode) & ~SCI_MODE_DMA); 398 NCR5380_WRITE(ncr_sc, sci_icmd, icmd); 399 splx(s); 400 return datalen - resid; 401 } 402 #endif 403