1/* $NetBSD: asc_ioasic.c,v 1.27 2022/05/03 20:52:31 andvar Exp $ */ 2 3/*- 4 * Copyright (c) 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Tohru Nishimura. 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 33__KERNEL_RCSID(0, "$NetBSD: asc_ioasic.c,v 1.27 2022/05/03 20:52:31 andvar Exp $"); 34 35#include <sys/param.h> 36#include <sys/buf.h> 37#include <sys/bus.h> 38#include <sys/device.h> 39#include <sys/systm.h> 40 41#include <uvm/uvm_extern.h> 42 43#include <dev/scsipi/scsi_all.h> 44#include <dev/scsipi/scsipi_all.h> 45#include <dev/scsipi/scsiconf.h> 46#include <dev/scsipi/scsi_message.h> 47 48#include <dev/ic/ncr53c9xreg.h> 49#include <dev/ic/ncr53c9xvar.h> 50 51#include <dev/tc/tcvar.h> 52#include <dev/tc/ioasicvar.h> 53#include <dev/tc/ioasicreg.h> 54 55struct asc_softc { 56 struct ncr53c9x_softc sc_ncr53c9x; /* glue to MI code */ 57 bus_space_tag_t sc_bst; /* bus space tag */ 58 bus_space_handle_t sc_bsh; /* bus space handle */ 59 bus_space_handle_t sc_scsi_bsh; /* ASC register handle */ 60 bus_dma_tag_t sc_dmat; /* bus dma tag */ 61 bus_dmamap_t sc_dmamap; /* bus dmamap */ 62 uint8_t **sc_dmaaddr; 63 size_t *sc_dmalen; 64 size_t sc_dmasize; 65 unsigned int sc_flags; 66#define ASC_ISPULLUP 0x0001 67#define ASC_DMAACTIVE 0x0002 68#define ASC_MAPLOADED 0x0004 69}; 70 71#define ASC_READ_REG(asc, reg) \ 72 ((uint8_t)bus_space_read_4((asc)->sc_bst, (asc)->sc_scsi_bsh, \ 73 (reg) * sizeof(uint32_t))) 74#define ASC_WRITE_REG(asc, reg, val) \ 75 bus_space_write_4((asc)->sc_bst, (asc)->sc_scsi_bsh, \ 76 (reg) * sizeof(uint32_t), (uint8_t)(val)) 77 78static int asc_ioasic_match(device_t, cfdata_t, void *); 79static void asc_ioasic_attach(device_t, device_t, void *); 80 81CFATTACH_DECL_NEW(asc_ioasic, sizeof(struct asc_softc), 82 asc_ioasic_match, asc_ioasic_attach, NULL, NULL); 83 84static uint8_t asc_read_reg(struct ncr53c9x_softc *, int); 85static void asc_write_reg(struct ncr53c9x_softc *, int, u_char); 86static int asc_dma_isintr(struct ncr53c9x_softc *sc); 87static void asc_ioasic_reset(struct ncr53c9x_softc *); 88static int asc_ioasic_intr(struct ncr53c9x_softc *); 89static int asc_ioasic_setup(struct ncr53c9x_softc *, 90 uint8_t **, size_t *, int, size_t *); 91static void asc_ioasic_go(struct ncr53c9x_softc *); 92static void asc_ioasic_stop(struct ncr53c9x_softc *); 93static int asc_dma_isactive(struct ncr53c9x_softc *); 94 95static struct ncr53c9x_glue asc_ioasic_glue = { 96 asc_read_reg, 97 asc_write_reg, 98 asc_dma_isintr, 99 asc_ioasic_reset, 100 asc_ioasic_intr, 101 asc_ioasic_setup, 102 asc_ioasic_go, 103 asc_ioasic_stop, 104 asc_dma_isactive, 105 NULL, 106}; 107 108static int 109asc_ioasic_match(device_t parent, cfdata_t cf, void *aux) 110{ 111 struct ioasicdev_attach_args *d = aux; 112 113 if (strncmp("asc", d->iada_modname, TC_ROM_LLEN)) 114 return 0; 115 116 return 1; 117} 118 119static void 120asc_ioasic_attach(device_t parent, device_t self, void *aux) 121{ 122 struct asc_softc *asc = device_private(self); 123 struct ncr53c9x_softc *sc = &asc->sc_ncr53c9x; 124 struct ioasicdev_attach_args *d = aux; 125 struct ioasic_softc *isc = device_private(parent); 126 127 /* 128 * Set up glue for MI code early; we use some of it here. 129 */ 130 sc->sc_dev = self; 131 sc->sc_glue = &asc_ioasic_glue; 132 asc->sc_bst = isc->sc_bst; 133 asc->sc_bsh = isc->sc_bsh; 134 if (bus_space_subregion(asc->sc_bst, asc->sc_bsh, 135 IOASIC_SLOT_12_START, 0x100, &asc->sc_scsi_bsh)) { 136 aprint_error(": failed to map device registers\n"); 137 return; 138 } 139 asc->sc_dmat = isc->sc_dmat; 140 if (bus_dmamap_create(asc->sc_dmat, PAGE_SIZE * 2, 141 2, PAGE_SIZE, PAGE_SIZE, BUS_DMA_NOWAIT, 142 &asc->sc_dmamap)) { 143 aprint_error(": failed to create DMA map\n"); 144 return; 145 } 146 147 sc->sc_id = 7; 148 sc->sc_freq = 25000000; 149 150 /* gimme MHz */ 151 sc->sc_freq /= 1000000; 152 153 ioasic_intr_establish(parent, d->iada_cookie, TC_IPL_BIO, 154 ncr53c9x_intr, sc); 155 156 /* 157 * XXX More of this should be in ncr53c9x_attach(), but 158 * XXX should we really poke around the chip that much in 159 * XXX the MI code? Think about this more... 160 */ 161 162 /* 163 * Set up static configuration info. 164 */ 165 sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB; 166 sc->sc_cfg2 = NCRCFG2_SCSI2; 167 sc->sc_cfg3 = 0; 168 sc->sc_rev = NCR_VARIANT_NCR53C94; 169 170 /* 171 * XXX minsync and maxxfer _should_ be set up in MI code, 172 * XXX but it appears to have some dependency on what sort 173 * XXX of DMA we're hooked up to, etc. 174 */ 175 176 /* 177 * This is the value used to start sync negotiations 178 * Note that the NCR register "SYNCTP" is programmed 179 * in "clocks per byte", and has a minimum value of 4. 180 * The SCSI period used in negotiation is one-fourth 181 * of the time (in nanoseconds) needed to transfer one byte. 182 * Since the chip's clock is given in MHz, we have the following 183 * formula: 4 * period = (1000 / freq) * 4 184 */ 185 sc->sc_minsync = (1000 / sc->sc_freq) * 5 / 4 ; 186 sc->sc_maxxfer = 64 * 1024; 187 188 /* Do the common parts of attachment. */ 189 sc->sc_adapter.adapt_minphys = minphys; 190 sc->sc_adapter.adapt_request = ncr53c9x_scsipi_request; 191 ncr53c9x_attach(sc); 192} 193 194void 195asc_ioasic_reset(struct ncr53c9x_softc *sc) 196{ 197 struct asc_softc *asc = (struct asc_softc *)sc; 198 uint32_t ssr; 199 200 ssr = bus_space_read_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR); 201 ssr &= ~IOASIC_CSR_DMAEN_SCSI; 202 bus_space_write_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR, ssr); 203 bus_space_write_4(asc->sc_bst, asc->sc_bsh, IOASIC_SCSI_SCR, 0); 204 205 if (asc->sc_flags & ASC_MAPLOADED) 206 bus_dmamap_unload(asc->sc_dmat, asc->sc_dmamap); 207 asc->sc_flags &= ~(ASC_DMAACTIVE|ASC_MAPLOADED); 208} 209 210#define TWOPAGE(a) (PAGE_SIZE*2 - ((a) & (PAGE_SIZE-1))) 211 212int 213asc_ioasic_setup(struct ncr53c9x_softc *sc, uint8_t **addr, size_t *len, 214 int ispullup, size_t *dmasize) 215{ 216 struct asc_softc *asc = (struct asc_softc *)sc; 217 uint32_t ssr, scr, *p; 218 size_t size; 219 vaddr_t cp; 220 221 NCR_DMA(("%s: start %d@%p,%s\n", device_xname(sc->sc_dev), 222 *asc->sc_dmalen, *asc->sc_dmaaddr, ispullup ? "IN" : "OUT")); 223 224 /* upto two 4KB pages */ 225 size = uimin(*dmasize, TWOPAGE((size_t)*addr)); 226 asc->sc_dmaaddr = addr; 227 asc->sc_dmalen = len; 228 asc->sc_dmasize = size; 229 asc->sc_flags = (ispullup) ? ASC_ISPULLUP : 0; 230 *dmasize = size; /* return trimmed transfer size */ 231 232 /* stop DMA engine first */ 233 ssr = bus_space_read_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR); 234 ssr &= ~IOASIC_CSR_DMAEN_SCSI; 235 bus_space_write_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR, ssr); 236 237 /* have dmamap for the transferring addresses */ 238 if (bus_dmamap_load(asc->sc_dmat, asc->sc_dmamap, 239 *addr, size, NULL /* kernel address */, BUS_DMA_NOWAIT)) 240 panic("%s: cannot allocate DMA address", 241 device_xname(sc->sc_dev)); 242 243 /* take care of 8B constraint on starting address */ 244 cp = (vaddr_t)*addr; 245 if ((cp & 7) == 0) { 246 /* comfortably aligned to 8B boundary */ 247 scr = 0; 248 } 249 else { 250 /* truncate to the boundary */ 251 p = (uint32_t *)(cp & ~7); 252 /* how many 16bit quantities in subject */ 253 scr = (cp & 7) >> 1; 254 /* trim down physical address too */ 255 asc->sc_dmamap->dm_segs[0].ds_addr &= ~7; 256 asc->sc_dmamap->dm_segs[0].ds_len += (cp & 6); 257 if ((asc->sc_flags & ASC_ISPULLUP) == 0) { 258 /* push down to SCSI device */ 259 scr |= 4; 260 /* round up physical address in this case */ 261 asc->sc_dmamap->dm_segs[0].ds_addr += 8; 262 /* don't care excess cache flush */ 263 } 264 /* pack fixup data in SDR0/SDR1 pair and instruct SCR */ 265 bus_space_write_4(asc->sc_bst, asc->sc_bsh, 266 IOASIC_SCSI_SDR0, p[0]); 267 bus_space_write_4(asc->sc_bst, asc->sc_bsh, 268 IOASIC_SCSI_SDR1, p[1]); 269 } 270 bus_space_write_4(asc->sc_bst, asc->sc_bsh, 271 IOASIC_SCSI_DMAPTR, 272 IOASIC_DMA_ADDR(asc->sc_dmamap->dm_segs[0].ds_addr)); 273 bus_space_write_4(asc->sc_bst, asc->sc_bsh, 274 IOASIC_SCSI_NEXTPTR, 275 (asc->sc_dmamap->dm_nsegs == 1) ? 276 ~0 : IOASIC_DMA_ADDR(asc->sc_dmamap->dm_segs[1].ds_addr)); 277 bus_space_write_4(asc->sc_bst, asc->sc_bsh, IOASIC_SCSI_SCR, scr); 278 279 /* synchronize dmamap contents with memory image */ 280 bus_dmamap_sync(asc->sc_dmat, asc->sc_dmamap, 281 0, size, 282 (ispullup) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); 283 284 asc->sc_flags |= ASC_MAPLOADED; 285 return 0; 286} 287 288void 289asc_ioasic_go(struct ncr53c9x_softc *sc) 290{ 291 struct asc_softc *asc = (struct asc_softc *)sc; 292 uint32_t ssr; 293 294 ssr = bus_space_read_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR); 295 if (asc->sc_flags & ASC_ISPULLUP) 296 ssr |= IOASIC_CSR_SCSI_DIR; 297 else { 298 /* ULTRIX does in this way */ 299 ssr &= ~IOASIC_CSR_SCSI_DIR; 300 bus_space_write_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR, ssr); 301 ssr = bus_space_read_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR); 302 } 303 ssr |= IOASIC_CSR_DMAEN_SCSI; 304 bus_space_write_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR, ssr); 305 asc->sc_flags |= ASC_DMAACTIVE; 306} 307 308static int 309asc_ioasic_intr(struct ncr53c9x_softc *sc) 310{ 311 struct asc_softc *asc = (struct asc_softc *)sc; 312 ssize_t trans, resid; 313 u_int tcl, tcm, ssr, scr, intr; 314 315 if ((asc->sc_flags & ASC_DMAACTIVE) == 0) 316 panic("%s: DMA wasn't active", __func__); 317 318#define IOASIC_ASC_ERRORS \ 319 (IOASIC_INTR_SCSI_PTR_LOAD|IOASIC_INTR_SCSI_OVRUN|IOASIC_INTR_SCSI_READ_E) 320 /* 321 * When doing polled I/O, the SCSI bits in the interrupt register won't 322 * get cleared by the interrupt processing. This will cause the DMA 323 * address registers to not load on the next DMA transfer. 324 * Check for these bits here, and clear them if needed. 325 */ 326 intr = bus_space_read_4(asc->sc_bst, asc->sc_bsh, IOASIC_INTR); 327 if ((intr & IOASIC_ASC_ERRORS) != 0) { 328 intr &= ~IOASIC_ASC_ERRORS; 329 bus_space_write_4(asc->sc_bst, asc->sc_bsh, IOASIC_INTR, intr); 330 } 331 332 /* DMA has stopped */ 333 ssr = bus_space_read_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR); 334 ssr &= ~IOASIC_CSR_DMAEN_SCSI; 335 bus_space_write_4(asc->sc_bst, asc->sc_bsh, IOASIC_CSR, ssr); 336 337 asc->sc_flags &= ~ASC_DMAACTIVE; 338 339 if (asc->sc_dmasize == 0) { 340 /* A "Transfer Pad" operation completed */ 341 tcl = ASC_READ_REG(asc, NCR_TCL); 342 tcm = ASC_READ_REG(asc, NCR_TCM); 343 NCR_DMA(("ioasic_intr: discarded %d bytes (tcl=%d, tcm=%d)\n", 344 tcl | (tcm << 8), tcl, tcm)); 345 return 0; 346 } 347 348 resid = 0; 349 if ((asc->sc_flags & ASC_ISPULLUP) == 0 && 350 (resid = (ASC_READ_REG(asc, NCR_FFLAG) & NCRFIFO_FF)) != 0) { 351 NCR_DMA(("ioasic_intr: empty FIFO of %d ", resid)); 352 DELAY(1); 353 } 354 355 resid += (tcl = ASC_READ_REG(asc, NCR_TCL)); 356 resid += (tcm = ASC_READ_REG(asc, NCR_TCM)) << 8; 357 358 trans = asc->sc_dmasize - resid; 359 if (trans < 0) { /* transferred < 0 ? */ 360 printf("ioasic_intr: xfer (%zd) > req (%zu)\n", 361 trans, asc->sc_dmasize); 362 trans = asc->sc_dmasize; 363 } 364 NCR_DMA(("ioasic_intr: tcl=%d, tcm=%d; trans=%d, resid=%d\n", 365 tcl, tcm, trans, resid)); 366 367 bus_dmamap_sync(asc->sc_dmat, asc->sc_dmamap, 368 0, asc->sc_dmasize, 369 (asc->sc_flags & ASC_ISPULLUP) ? 370 BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); 371 372 scr = bus_space_read_4(asc->sc_bst, asc->sc_bsh, IOASIC_SCSI_SCR); 373 if ((asc->sc_flags & ASC_ISPULLUP) && scr != 0) { 374 uint32_t sdr[2], ptr; 375 376 sdr[0] = bus_space_read_4(asc->sc_bst, asc->sc_bsh, 377 IOASIC_SCSI_SDR0); 378 sdr[1] = bus_space_read_4(asc->sc_bst, asc->sc_bsh, 379 IOASIC_SCSI_SDR1); 380 ptr = bus_space_read_4(asc->sc_bst, asc->sc_bsh, 381 IOASIC_SCSI_DMAPTR); 382 ptr = (ptr >> 3) & 0x1ffffffc; 383 /* 384 * scr: 1 -> short[0] 385 * 2 -> short[0] + short[1] 386 * 3 -> short[0] + short[1] + short[2] 387 */ 388 scr &= IOASIC_SCR_WORD; 389 memcpy((void *)MIPS_PHYS_TO_KSEG0(ptr), sdr, scr << 1); 390 } 391 392 bus_dmamap_unload(asc->sc_dmat, asc->sc_dmamap); 393 asc->sc_flags &= ~ASC_MAPLOADED; 394 395 *asc->sc_dmalen -= trans; 396 *asc->sc_dmaaddr += trans; 397 398 return 0; 399} 400 401 402void 403asc_ioasic_stop(struct ncr53c9x_softc *sc) 404{ 405 struct asc_softc *asc = (struct asc_softc *)sc; 406 407 if (asc->sc_flags & ASC_MAPLOADED) { 408 bus_dmamap_sync(asc->sc_dmat, asc->sc_dmamap, 409 0, asc->sc_dmasize, 410 (asc->sc_flags & ASC_ISPULLUP) ? 411 BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); 412 bus_dmamap_unload(asc->sc_dmat, asc->sc_dmamap); 413 } 414 415 asc->sc_flags &= ~(ASC_DMAACTIVE|ASC_MAPLOADED); 416} 417 418static uint8_t 419asc_read_reg(struct ncr53c9x_softc *sc, int reg) 420{ 421 struct asc_softc *asc = (struct asc_softc *)sc; 422 423 return ASC_READ_REG(asc, reg); 424} 425 426static void 427asc_write_reg(struct ncr53c9x_softc *sc, int reg, uint8_t val) 428{ 429 struct asc_softc *asc = (struct asc_softc *)sc; 430 431 ASC_WRITE_REG(asc, reg, val); 432} 433 434static int 435asc_dma_isintr(struct ncr53c9x_softc *sc) 436{ 437 struct asc_softc *asc = (struct asc_softc *)sc; 438 439 return (ASC_READ_REG(asc, NCR_STAT) & NCRSTAT_INT) != 0; 440} 441 442static int 443asc_dma_isactive(struct ncr53c9x_softc *sc) 444{ 445 struct asc_softc *asc = (struct asc_softc *)sc; 446 447 return (asc->sc_flags & ASC_DMAACTIVE) != 0; 448} 449