1 /* $NetBSD: siop_common.c,v 1.31 2002/09/27 15:37:18 provos Exp $ */ 2 3 /* 4 * Copyright (c) 2000, 2002 Manuel Bouyer. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Manuel Bouyer. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33 /* SYM53c7/8xx PCI-SCSI I/O Processors driver */ 34 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: siop_common.c,v 1.31 2002/09/27 15:37:18 provos Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 #include <sys/malloc.h> 42 #include <sys/buf.h> 43 #include <sys/kernel.h> 44 #include <sys/scsiio.h> 45 46 #include <uvm/uvm_extern.h> 47 48 #include <machine/endian.h> 49 #include <machine/bus.h> 50 51 #include <dev/scsipi/scsi_all.h> 52 #include <dev/scsipi/scsi_message.h> 53 #include <dev/scsipi/scsipi_all.h> 54 55 #include <dev/scsipi/scsiconf.h> 56 57 #include <dev/ic/siopreg.h> 58 #include <dev/ic/siopvar_common.h> 59 60 #include "opt_siop.h" 61 62 #undef DEBUG 63 #undef DEBUG_DR 64 #undef DEBUG_NEG 65 66 int 67 siop_common_attach(sc) 68 struct siop_common_softc *sc; 69 { 70 int error, i; 71 bus_dma_segment_t seg; 72 int rseg; 73 74 /* 75 * Allocate DMA-safe memory for the script and map it. 76 */ 77 if ((sc->features & SF_CHIP_RAM) == 0) { 78 error = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, 79 PAGE_SIZE, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT); 80 if (error) { 81 printf("%s: unable to allocate script DMA memory, " 82 "error = %d\n", sc->sc_dev.dv_xname, error); 83 return error; 84 } 85 error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, PAGE_SIZE, 86 (caddr_t *)&sc->sc_script, 87 BUS_DMA_NOWAIT|BUS_DMA_COHERENT); 88 if (error) { 89 printf("%s: unable to map script DMA memory, " 90 "error = %d\n", sc->sc_dev.dv_xname, error); 91 return error; 92 } 93 error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, 94 PAGE_SIZE, 0, BUS_DMA_NOWAIT, &sc->sc_scriptdma); 95 if (error) { 96 printf("%s: unable to create script DMA map, " 97 "error = %d\n", sc->sc_dev.dv_xname, error); 98 return error; 99 } 100 error = bus_dmamap_load(sc->sc_dmat, sc->sc_scriptdma, 101 sc->sc_script, PAGE_SIZE, NULL, BUS_DMA_NOWAIT); 102 if (error) { 103 printf("%s: unable to load script DMA map, " 104 "error = %d\n", sc->sc_dev.dv_xname, error); 105 return error; 106 } 107 sc->sc_scriptaddr = 108 sc->sc_scriptdma->dm_segs[0].ds_addr; 109 sc->ram_size = PAGE_SIZE; 110 } 111 112 sc->sc_adapt.adapt_dev = &sc->sc_dev; 113 sc->sc_adapt.adapt_nchannels = 1; 114 sc->sc_adapt.adapt_openings = 0; 115 sc->sc_adapt.adapt_ioctl = siop_ioctl; 116 sc->sc_adapt.adapt_minphys = minphys; 117 118 memset(&sc->sc_chan, 0, sizeof(sc->sc_chan)); 119 sc->sc_chan.chan_adapter = &sc->sc_adapt; 120 sc->sc_chan.chan_bustype = &scsi_bustype; 121 sc->sc_chan.chan_channel = 0; 122 sc->sc_chan.chan_flags = SCSIPI_CHAN_CANGROW; 123 sc->sc_chan.chan_ntargets = 124 (sc->features & SF_BUS_WIDE) ? 16 : 8; 125 sc->sc_chan.chan_nluns = 8; 126 sc->sc_chan.chan_id = 127 bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SCID); 128 if (sc->sc_chan.chan_id == 0 || 129 sc->sc_chan.chan_id >= sc->sc_chan.chan_ntargets) 130 sc->sc_chan.chan_id = SIOP_DEFAULT_TARGET; 131 132 for (i = 0; i < 16; i++) 133 sc->targets[i] = NULL; 134 135 /* find min/max sync period for this chip */ 136 sc->st_maxsync = 0; 137 sc->dt_maxsync = 0; 138 sc->st_minsync = 255; 139 sc->dt_minsync = 255; 140 for (i = 0; i < sizeof(scf_period) / sizeof(scf_period[0]); i++) { 141 if (sc->clock_period != scf_period[i].clock) 142 continue; 143 if (sc->st_maxsync < scf_period[i].period) 144 sc->st_maxsync = scf_period[i].period; 145 if (sc->st_minsync > scf_period[i].period) 146 sc->st_minsync = scf_period[i].period; 147 } 148 if (sc->st_maxsync == 255 || sc->st_minsync == 0) 149 panic("siop: can't find my sync parameters"); 150 for (i = 0; i < sizeof(dt_scf_period) / sizeof(dt_scf_period[0]); i++) { 151 if (sc->clock_period != dt_scf_period[i].clock) 152 continue; 153 if (sc->dt_maxsync < dt_scf_period[i].period) 154 sc->dt_maxsync = dt_scf_period[i].period; 155 if (sc->dt_minsync > dt_scf_period[i].period) 156 sc->dt_minsync = dt_scf_period[i].period; 157 } 158 if (sc->dt_maxsync == 255 || sc->dt_minsync == 0) 159 panic("siop: can't find my sync parameters"); 160 return 0; 161 } 162 163 void 164 siop_common_reset(sc) 165 struct siop_common_softc *sc; 166 { 167 u_int32_t stest3; 168 169 /* reset the chip */ 170 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_ISTAT, ISTAT_SRST); 171 delay(1000); 172 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_ISTAT, 0); 173 174 /* init registers */ 175 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL0, 176 SCNTL0_ARB_MASK | SCNTL0_EPC | SCNTL0_AAP); 177 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL1, 0); 178 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL3, sc->clock_div); 179 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SXFER, 0); 180 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_DIEN, 0xff); 181 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SIEN0, 182 0xff & ~(SIEN0_CMP | SIEN0_SEL | SIEN0_RSL)); 183 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SIEN1, 184 0xff & ~(SIEN1_HTH | SIEN1_GEN)); 185 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2, 0); 186 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST3, STEST3_TE); 187 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STIME0, 188 (0xb << STIME0_SEL_SHIFT)); 189 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCID, 190 sc->sc_chan.chan_id | SCID_RRE); 191 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_RESPID0, 192 1 << sc->sc_chan.chan_id); 193 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_DCNTL, 194 (sc->features & SF_CHIP_PF) ? DCNTL_COM | DCNTL_PFEN : DCNTL_COM); 195 196 /* enable clock doubler or quadruler if appropriate */ 197 if (sc->features & (SF_CHIP_DBLR | SF_CHIP_QUAD)) { 198 stest3 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_STEST3); 199 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST1, 200 STEST1_DBLEN); 201 if (sc->features & SF_CHIP_QUAD) { 202 /* wait for PPL to lock */ 203 while ((bus_space_read_1(sc->sc_rt, sc->sc_rh, 204 SIOP_STEST4) & STEST4_LOCK) == 0) 205 delay(10); 206 } else { 207 /* data sheet says 20us - more won't hurt */ 208 delay(100); 209 } 210 /* halt scsi clock, select doubler/quad, restart clock */ 211 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST3, 212 stest3 | STEST3_HSC); 213 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST1, 214 STEST1_DBLEN | STEST1_DBLSEL); 215 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST3, stest3); 216 } else { 217 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST1, 0); 218 } 219 if (sc->features & SF_CHIP_FIFO) 220 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST5, 221 bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST5) | 222 CTEST5_DFS); 223 if (sc->features & SF_CHIP_LED0) { 224 /* Set GPIO0 as output if software LED control is required */ 225 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_GPCNTL, 226 bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_GPCNTL) & 0xfe); 227 } 228 if (sc->features & SF_BUS_ULTRA3) { 229 /* reset SCNTL4 */ 230 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL4, 0); 231 } 232 sc->mode = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_STEST4) & 233 STEST4_MODE_MASK; 234 235 /* 236 * initialise the RAM. Without this we may get scsi gross errors on 237 * the 1010 238 */ 239 if (sc->features & SF_CHIP_RAM) 240 bus_space_set_region_4(sc->sc_ramt, sc->sc_ramh, 241 0, 0, sc->ram_size / 4); 242 sc->sc_reset(sc); 243 } 244 245 /* prepare tables before sending a cmd */ 246 void 247 siop_setuptables(siop_cmd) 248 struct siop_common_cmd *siop_cmd; 249 { 250 int i; 251 struct siop_common_softc *sc = siop_cmd->siop_sc; 252 struct scsipi_xfer *xs = siop_cmd->xs; 253 int target = xs->xs_periph->periph_target; 254 int lun = xs->xs_periph->periph_lun; 255 int msgoffset = 1; 256 257 siop_cmd->siop_tables->id = htole32(sc->targets[target]->id); 258 memset(siop_cmd->siop_tables->msg_out, 0, 259 sizeof(siop_cmd->siop_tables->msg_out)); 260 /* request sense doesn't disconnect */ 261 if (xs->xs_control & XS_CTL_REQSENSE) 262 siop_cmd->siop_tables->msg_out[0] = MSG_IDENTIFY(lun, 0); 263 else if ((sc->features & SF_CHIP_GEBUG) && 264 (sc->targets[target]->flags & TARF_ISWIDE) == 0) 265 /* 266 * 1010 bug: it seems that the 1010 has problems with reselect 267 * when not in wide mode (generate false SCSI gross error). 268 * The FreeBSD sym driver has comments about it but their 269 * workaround (disable SCSI gross error reporting) doesn't 270 * work with my adapter. So disable disconnect when not 271 * wide. 272 */ 273 siop_cmd->siop_tables->msg_out[0] = MSG_IDENTIFY(lun, 0); 274 else 275 siop_cmd->siop_tables->msg_out[0] = MSG_IDENTIFY(lun, 1); 276 if (xs->xs_tag_type != 0) { 277 if ((sc->targets[target]->flags & TARF_TAG) == 0) { 278 scsipi_printaddr(xs->xs_periph); 279 printf(": tagged command type %d id %d\n", 280 siop_cmd->xs->xs_tag_type, siop_cmd->xs->xs_tag_id); 281 panic("tagged command for non-tagging device"); 282 } 283 siop_cmd->flags |= CMDFL_TAG; 284 siop_cmd->siop_tables->msg_out[1] = siop_cmd->xs->xs_tag_type; 285 /* 286 * use siop_cmd->tag not xs->xs_tag_id, caller may want a 287 * different one 288 */ 289 siop_cmd->siop_tables->msg_out[2] = siop_cmd->tag; 290 msgoffset = 3; 291 } 292 siop_cmd->siop_tables->t_msgout.count= htole32(msgoffset); 293 if (sc->targets[target]->status == TARST_ASYNC) { 294 if ((sc->targets[target]->flags & TARF_DT) && 295 (sc->mode == STEST4_MODE_LVD)) { 296 sc->targets[target]->status = TARST_PPR_NEG; 297 siop_ppr_msg(siop_cmd, msgoffset, sc->dt_minsync, 298 sc->maxoff); 299 } else if (sc->targets[target]->flags & TARF_WIDE) { 300 sc->targets[target]->status = TARST_WIDE_NEG; 301 siop_wdtr_msg(siop_cmd, msgoffset, 302 MSG_EXT_WDTR_BUS_16_BIT); 303 } else if (sc->targets[target]->flags & TARF_SYNC) { 304 sc->targets[target]->status = TARST_SYNC_NEG; 305 siop_sdtr_msg(siop_cmd, msgoffset, sc->st_minsync, 306 (sc->maxoff > 31) ? 31 : sc->maxoff); 307 } else { 308 sc->targets[target]->status = TARST_OK; 309 siop_update_xfer_mode(sc, target); 310 } 311 } 312 siop_cmd->siop_tables->status = 313 htole32(SCSI_SIOP_NOSTATUS); /* set invalid status */ 314 315 siop_cmd->siop_tables->cmd.count = 316 htole32(siop_cmd->dmamap_cmd->dm_segs[0].ds_len); 317 siop_cmd->siop_tables->cmd.addr = 318 htole32(siop_cmd->dmamap_cmd->dm_segs[0].ds_addr); 319 if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) { 320 for (i = 0; i < siop_cmd->dmamap_data->dm_nsegs; i++) { 321 siop_cmd->siop_tables->data[i].count = 322 htole32(siop_cmd->dmamap_data->dm_segs[i].ds_len); 323 siop_cmd->siop_tables->data[i].addr = 324 htole32(siop_cmd->dmamap_data->dm_segs[i].ds_addr); 325 } 326 } 327 } 328 329 int 330 siop_wdtr_neg(siop_cmd) 331 struct siop_common_cmd *siop_cmd; 332 { 333 struct siop_common_softc *sc = siop_cmd->siop_sc; 334 struct siop_common_target *siop_target = siop_cmd->siop_target; 335 int target = siop_cmd->xs->xs_periph->periph_target; 336 struct siop_common_xfer *tables = siop_cmd->siop_tables; 337 338 if (siop_target->status == TARST_WIDE_NEG) { 339 /* we initiated wide negotiation */ 340 switch (tables->msg_in[3]) { 341 case MSG_EXT_WDTR_BUS_8_BIT: 342 siop_target->flags &= ~TARF_ISWIDE; 343 sc->targets[target]->id &= ~(SCNTL3_EWS << 24); 344 break; 345 case MSG_EXT_WDTR_BUS_16_BIT: 346 if (siop_target->flags & TARF_WIDE) { 347 siop_target->flags |= TARF_ISWIDE; 348 sc->targets[target]->id |= (SCNTL3_EWS << 24); 349 break; 350 } 351 /* FALLTHROUH */ 352 default: 353 /* 354 * hum, we got more than what we can handle, shouldn't 355 * happen. Reject, and stay async 356 */ 357 siop_target->flags &= ~TARF_ISWIDE; 358 siop_target->status = TARST_OK; 359 siop_target->offset = siop_target->period = 0; 360 siop_update_xfer_mode(sc, target); 361 printf("%s: rejecting invalid wide negotiation from " 362 "target %d (%d)\n", sc->sc_dev.dv_xname, target, 363 tables->msg_in[3]); 364 tables->t_msgout.count= htole32(1); 365 tables->msg_out[0] = MSG_MESSAGE_REJECT; 366 return SIOP_NEG_MSGOUT; 367 } 368 tables->id = htole32(sc->targets[target]->id); 369 bus_space_write_1(sc->sc_rt, sc->sc_rh, 370 SIOP_SCNTL3, 371 (sc->targets[target]->id >> 24) & 0xff); 372 /* we now need to do sync */ 373 if (siop_target->flags & TARF_SYNC) { 374 siop_target->status = TARST_SYNC_NEG; 375 siop_sdtr_msg(siop_cmd, 0, sc->st_minsync, 376 (sc->maxoff > 31) ? 31 : sc->maxoff); 377 return SIOP_NEG_MSGOUT; 378 } else { 379 siop_target->status = TARST_OK; 380 siop_update_xfer_mode(sc, target); 381 return SIOP_NEG_ACK; 382 } 383 } else { 384 /* target initiated wide negotiation */ 385 if (tables->msg_in[3] >= MSG_EXT_WDTR_BUS_16_BIT 386 && (siop_target->flags & TARF_WIDE)) { 387 siop_target->flags |= TARF_ISWIDE; 388 sc->targets[target]->id |= SCNTL3_EWS << 24; 389 } else { 390 siop_target->flags &= ~TARF_ISWIDE; 391 sc->targets[target]->id &= ~(SCNTL3_EWS << 24); 392 } 393 tables->id = htole32(sc->targets[target]->id); 394 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL3, 395 (sc->targets[target]->id >> 24) & 0xff); 396 /* 397 * we did reset wide parameters, so fall back to async, 398 * but don't schedule a sync neg, target should initiate it 399 */ 400 siop_target->status = TARST_OK; 401 siop_target->offset = siop_target->period = 0; 402 siop_update_xfer_mode(sc, target); 403 siop_wdtr_msg(siop_cmd, 0, (siop_target->flags & TARF_ISWIDE) ? 404 MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT); 405 return SIOP_NEG_MSGOUT; 406 } 407 } 408 409 int 410 siop_ppr_neg(siop_cmd) 411 struct siop_common_cmd *siop_cmd; 412 { 413 struct siop_common_softc *sc = siop_cmd->siop_sc; 414 struct siop_common_target *siop_target = siop_cmd->siop_target; 415 int target = siop_cmd->xs->xs_periph->periph_target; 416 struct siop_common_xfer *tables = siop_cmd->siop_tables; 417 int sync, offset, options, scf = 0; 418 int i; 419 420 #ifdef DEBUG_NEG 421 printf("%s: anserw on ppr negotiation:", sc->sc_dev.dv_xname); 422 for (i = 0; i < 8; i++) 423 printf(" 0x%x", tables->msg_in[i]); 424 printf("\n"); 425 #endif 426 427 if (siop_target->status == TARST_PPR_NEG) { 428 /* we initiated PPR negotiation */ 429 sync = tables->msg_in[3]; 430 offset = tables->msg_in[5]; 431 options = tables->msg_in[7]; 432 if (options != MSG_EXT_PPR_DT) { 433 /* should't happen */ 434 printf("%s: ppr negotiation for target %d: " 435 "no DT option\n", sc->sc_dev.dv_xname, target); 436 siop_target->status = TARST_ASYNC; 437 siop_target->flags &= ~(TARF_DT | TARF_ISDT); 438 siop_target->offset = 0; 439 siop_target->period = 0; 440 goto reject; 441 } 442 443 if (offset > sc->maxoff || sync < sc->dt_minsync || 444 sync > sc->dt_maxsync) { 445 printf("%s: ppr negotiation for target %d: " 446 "offset (%d) or sync (%d) out of range\n", 447 sc->sc_dev.dv_xname, target, offset, sync); 448 /* should not happen */ 449 siop_target->offset = 0; 450 siop_target->period = 0; 451 goto reject; 452 } else { 453 for (i = 0; i < 454 sizeof(dt_scf_period) / sizeof(dt_scf_period[0]); 455 i++) { 456 if (sc->clock_period != dt_scf_period[i].clock) 457 continue; 458 if (dt_scf_period[i].period == sync) { 459 /* ok, found it. we now are sync. */ 460 siop_target->offset = offset; 461 siop_target->period = sync; 462 scf = dt_scf_period[i].scf; 463 siop_target->flags |= TARF_ISDT; 464 } 465 } 466 if ((siop_target->flags & TARF_ISDT) == 0) { 467 printf("%s: ppr negotiation for target %d: " 468 "sync (%d) incompatible with adapter\n", 469 sc->sc_dev.dv_xname, target, sync); 470 /* 471 * we didn't find it in our table, do async 472 * send reject msg, start SDTR/WDTR neg 473 */ 474 siop_target->status = TARST_ASYNC; 475 siop_target->flags &= ~(TARF_DT | TARF_ISDT); 476 siop_target->offset = 0; 477 siop_target->period = 0; 478 goto reject; 479 } 480 } 481 if (tables->msg_in[6] != 1) { 482 printf("%s: ppr negotiation for target %d: " 483 "transfer width (%d) incompatible with dt\n", 484 sc->sc_dev.dv_xname, target, tables->msg_in[6]); 485 /* DT mode can only be done with wide transfers */ 486 siop_target->status = TARST_ASYNC; 487 goto reject; 488 } 489 siop_target->flags |= TARF_ISWIDE; 490 sc->targets[target]->id |= (SCNTL3_EWS << 24); 491 sc->targets[target]->id &= ~(SCNTL3_SCF_MASK << 24); 492 sc->targets[target]->id |= scf << (24 + SCNTL3_SCF_SHIFT); 493 sc->targets[target]->id &= ~(SXFER_MO_MASK << 8); 494 sc->targets[target]->id |= 495 (siop_target->offset & SXFER_MO_MASK) << 8; 496 sc->targets[target]->id &= ~0xff; 497 sc->targets[target]->id |= SCNTL4_U3EN; 498 siop_target->status = TARST_OK; 499 siop_update_xfer_mode(sc, target); 500 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL3, 501 (sc->targets[target]->id >> 24) & 0xff); 502 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SXFER, 503 (sc->targets[target]->id >> 8) & 0xff); 504 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL4, 505 sc->targets[target]->id & 0xff); 506 return SIOP_NEG_ACK; 507 } else { 508 /* target initiated PPR negotiation, shouldn't happen */ 509 printf("%s: rejecting invalid PPR negotiation from " 510 "target %d\n", sc->sc_dev.dv_xname, target); 511 reject: 512 tables->t_msgout.count= htole32(1); 513 tables->msg_out[0] = MSG_MESSAGE_REJECT; 514 return SIOP_NEG_MSGOUT; 515 } 516 } 517 518 int 519 siop_sdtr_neg(siop_cmd) 520 struct siop_common_cmd *siop_cmd; 521 { 522 struct siop_common_softc *sc = siop_cmd->siop_sc; 523 struct siop_common_target *siop_target = siop_cmd->siop_target; 524 int target = siop_cmd->xs->xs_periph->periph_target; 525 int sync, maxoffset, offset, i; 526 int send_msgout = 0; 527 struct siop_common_xfer *tables = siop_cmd->siop_tables; 528 529 /* limit to Ultra/2 parameters, need PPR for Ultra/3 */ 530 maxoffset = (sc->maxoff > 31) ? 31 : sc->maxoff; 531 532 sync = tables->msg_in[3]; 533 offset = tables->msg_in[4]; 534 535 if (siop_target->status == TARST_SYNC_NEG) { 536 /* we initiated sync negotiation */ 537 siop_target->status = TARST_OK; 538 #ifdef DEBUG 539 printf("sdtr: sync %d offset %d\n", sync, offset); 540 #endif 541 if (offset > maxoffset || sync < sc->st_minsync || 542 sync > sc->st_maxsync) 543 goto reject; 544 for (i = 0; i < sizeof(scf_period) / sizeof(scf_period[0]); 545 i++) { 546 if (sc->clock_period != scf_period[i].clock) 547 continue; 548 if (scf_period[i].period == sync) { 549 /* ok, found it. we now are sync. */ 550 siop_target->offset = offset; 551 siop_target->period = sync; 552 sc->targets[target]->id &= 553 ~(SCNTL3_SCF_MASK << 24); 554 sc->targets[target]->id |= scf_period[i].scf 555 << (24 + SCNTL3_SCF_SHIFT); 556 if (sync < 25 && /* Ultra */ 557 (sc->features & SF_BUS_ULTRA3) == 0) 558 sc->targets[target]->id |= 559 SCNTL3_ULTRA << 24; 560 else 561 sc->targets[target]->id &= 562 ~(SCNTL3_ULTRA << 24); 563 sc->targets[target]->id &= 564 ~(SXFER_MO_MASK << 8); 565 sc->targets[target]->id |= 566 (offset & SXFER_MO_MASK) << 8; 567 sc->targets[target]->id &= ~0xff; /* scntl4 */ 568 goto end; 569 } 570 } 571 /* 572 * we didn't find it in our table, do async and send reject 573 * msg 574 */ 575 reject: 576 send_msgout = 1; 577 tables->t_msgout.count= htole32(1); 578 tables->msg_out[0] = MSG_MESSAGE_REJECT; 579 sc->targets[target]->id &= ~(SCNTL3_SCF_MASK << 24); 580 sc->targets[target]->id &= ~(SCNTL3_ULTRA << 24); 581 sc->targets[target]->id &= ~(SXFER_MO_MASK << 8); 582 sc->targets[target]->id &= ~0xff; /* scntl4 */ 583 siop_target->offset = siop_target->period = 0; 584 } else { /* target initiated sync neg */ 585 #ifdef DEBUG 586 printf("sdtr (target): sync %d offset %d\n", sync, offset); 587 #endif 588 if (offset == 0 || sync > sc->st_maxsync) { /* async */ 589 goto async; 590 } 591 if (offset > maxoffset) 592 offset = maxoffset; 593 if (sync < sc->st_minsync) 594 sync = sc->st_minsync; 595 /* look for sync period */ 596 for (i = 0; i < sizeof(scf_period) / sizeof(scf_period[0]); 597 i++) { 598 if (sc->clock_period != scf_period[i].clock) 599 continue; 600 if (scf_period[i].period == sync) { 601 /* ok, found it. we now are sync. */ 602 siop_target->offset = offset; 603 siop_target->period = sync; 604 sc->targets[target]->id &= 605 ~(SCNTL3_SCF_MASK << 24); 606 sc->targets[target]->id |= scf_period[i].scf 607 << (24 + SCNTL3_SCF_SHIFT); 608 if (sync < 25 && /* Ultra */ 609 (sc->features & SF_BUS_ULTRA3) == 0) 610 sc->targets[target]->id |= 611 SCNTL3_ULTRA << 24; 612 else 613 sc->targets[target]->id &= 614 ~(SCNTL3_ULTRA << 24); 615 sc->targets[target]->id &= 616 ~(SXFER_MO_MASK << 8); 617 sc->targets[target]->id |= 618 (offset & SXFER_MO_MASK) << 8; 619 sc->targets[target]->id &= ~0xff; /* scntl4 */ 620 siop_sdtr_msg(siop_cmd, 0, sync, offset); 621 send_msgout = 1; 622 goto end; 623 } 624 } 625 async: 626 siop_target->offset = siop_target->period = 0; 627 sc->targets[target]->id &= ~(SCNTL3_SCF_MASK << 24); 628 sc->targets[target]->id &= ~(SCNTL3_ULTRA << 24); 629 sc->targets[target]->id &= ~(SXFER_MO_MASK << 8); 630 sc->targets[target]->id &= ~0xff; /* scntl4 */ 631 siop_sdtr_msg(siop_cmd, 0, 0, 0); 632 send_msgout = 1; 633 } 634 end: 635 if (siop_target->status == TARST_OK) 636 siop_update_xfer_mode(sc, target); 637 #ifdef DEBUG 638 printf("id now 0x%x\n", sc->targets[target]->id); 639 #endif 640 tables->id = htole32(sc->targets[target]->id); 641 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL3, 642 (sc->targets[target]->id >> 24) & 0xff); 643 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SXFER, 644 (sc->targets[target]->id >> 8) & 0xff); 645 if (send_msgout) { 646 return SIOP_NEG_MSGOUT; 647 } else { 648 return SIOP_NEG_ACK; 649 } 650 } 651 652 void 653 siop_sdtr_msg(siop_cmd, offset, ssync, soff) 654 struct siop_common_cmd *siop_cmd; 655 int offset; 656 int ssync, soff; 657 { 658 siop_cmd->siop_tables->msg_out[offset + 0] = MSG_EXTENDED; 659 siop_cmd->siop_tables->msg_out[offset + 1] = MSG_EXT_SDTR_LEN; 660 siop_cmd->siop_tables->msg_out[offset + 2] = MSG_EXT_SDTR; 661 siop_cmd->siop_tables->msg_out[offset + 3] = ssync; 662 siop_cmd->siop_tables->msg_out[offset + 4] = soff; 663 siop_cmd->siop_tables->t_msgout.count = 664 htole32(offset + MSG_EXT_SDTR_LEN + 2); 665 } 666 667 void 668 siop_wdtr_msg(siop_cmd, offset, wide) 669 struct siop_common_cmd *siop_cmd; 670 int offset; 671 { 672 siop_cmd->siop_tables->msg_out[offset + 0] = MSG_EXTENDED; 673 siop_cmd->siop_tables->msg_out[offset + 1] = MSG_EXT_WDTR_LEN; 674 siop_cmd->siop_tables->msg_out[offset + 2] = MSG_EXT_WDTR; 675 siop_cmd->siop_tables->msg_out[offset + 3] = wide; 676 siop_cmd->siop_tables->t_msgout.count = 677 htole32(offset + MSG_EXT_WDTR_LEN + 2); 678 } 679 680 void 681 siop_ppr_msg(siop_cmd, offset, ssync, soff) 682 struct siop_common_cmd *siop_cmd; 683 int offset; 684 int ssync, soff; 685 { 686 siop_cmd->siop_tables->msg_out[offset + 0] = MSG_EXTENDED; 687 siop_cmd->siop_tables->msg_out[offset + 1] = MSG_EXT_PPR_LEN; 688 siop_cmd->siop_tables->msg_out[offset + 2] = MSG_EXT_PPR; 689 siop_cmd->siop_tables->msg_out[offset + 3] = ssync; 690 siop_cmd->siop_tables->msg_out[offset + 4] = 0; /* reserved */ 691 siop_cmd->siop_tables->msg_out[offset + 5] = soff; 692 siop_cmd->siop_tables->msg_out[offset + 6] = 1; /* wide */ 693 siop_cmd->siop_tables->msg_out[offset + 7] = MSG_EXT_PPR_DT; 694 siop_cmd->siop_tables->t_msgout.count = 695 htole32(offset + MSG_EXT_PPR_LEN + 2); 696 } 697 698 void 699 siop_minphys(bp) 700 struct buf *bp; 701 { 702 minphys(bp); 703 } 704 705 int 706 siop_ioctl(chan, cmd, arg, flag, p) 707 struct scsipi_channel *chan; 708 u_long cmd; 709 caddr_t arg; 710 int flag; 711 struct proc *p; 712 { 713 struct siop_common_softc *sc = (void *)chan->chan_adapter->adapt_dev; 714 715 switch (cmd) { 716 case SCBUSIORESET: 717 /* 718 * abort the script. This will trigger an interrupt, which will 719 * trigger a bus reset. 720 * We can't safely trigger the reset here as we can't access 721 * the required register while the script is running. 722 */ 723 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_ISTAT, ISTAT_ABRT); 724 return (0); 725 default: 726 return (ENOTTY); 727 } 728 } 729 730 void 731 siop_sdp(siop_cmd) 732 struct siop_common_cmd *siop_cmd; 733 { 734 /* save data pointer. Handle async only for now */ 735 int offset, dbc, sstat; 736 struct siop_common_softc *sc = siop_cmd->siop_sc; 737 scr_table_t *table; /* table to patch */ 738 739 if ((siop_cmd->xs->xs_control & (XS_CTL_DATA_OUT | XS_CTL_DATA_IN)) 740 == 0) 741 return; /* no data pointers to save */ 742 offset = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SCRATCHA + 1); 743 if (offset >= SIOP_NSG) { 744 printf("%s: bad offset in siop_sdp (%d)\n", 745 sc->sc_dev.dv_xname, offset); 746 return; 747 } 748 table = &siop_cmd->siop_tables->data[offset]; 749 #ifdef DEBUG_DR 750 printf("sdp: offset %d count=%d addr=0x%x ", offset, 751 table->count, table->addr); 752 #endif 753 dbc = bus_space_read_4(sc->sc_rt, sc->sc_rh, SIOP_DBC) & 0x00ffffff; 754 if (siop_cmd->xs->xs_control & XS_CTL_DATA_OUT) { 755 if (sc->features & SF_CHIP_DFBC) { 756 dbc += 757 bus_space_read_2(sc->sc_rt, sc->sc_rh, SIOP_DFBC); 758 } else { 759 /* need to account stale data in FIFO */ 760 int dfifo = 761 bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_DFIFO); 762 if (sc->features & SF_CHIP_FIFO) { 763 dfifo |= (bus_space_read_1(sc->sc_rt, sc->sc_rh, 764 SIOP_CTEST5) & CTEST5_BOMASK) << 8; 765 dbc += (dfifo - (dbc & 0x3ff)) & 0x3ff; 766 } else { 767 dbc += (dfifo - (dbc & 0x7f)) & 0x7f; 768 } 769 } 770 sstat = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SSTAT0); 771 if (sstat & SSTAT0_OLF) 772 dbc++; 773 if ((sstat & SSTAT0_ORF) && (sc->features & SF_CHIP_DFBC) == 0) 774 dbc++; 775 if (siop_cmd->siop_target->flags & TARF_ISWIDE) { 776 sstat = bus_space_read_1(sc->sc_rt, sc->sc_rh, 777 SIOP_SSTAT2); 778 if (sstat & SSTAT2_OLF1) 779 dbc++; 780 if ((sstat & SSTAT2_ORF1) && 781 (sc->features & SF_CHIP_DFBC) == 0) 782 dbc++; 783 } 784 /* clear the FIFO */ 785 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3, 786 bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3) | 787 CTEST3_CLF); 788 } 789 table->addr = 790 htole32(le32toh(table->addr) + le32toh(table->count) - dbc); 791 table->count = htole32(dbc); 792 #ifdef DEBUG_DR 793 printf("now count=%d addr=0x%x\n", table->count, table->addr); 794 #endif 795 } 796 797 void 798 siop_clearfifo(sc) 799 struct siop_common_softc *sc; 800 { 801 int timeout = 0; 802 int ctest3 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3); 803 804 #ifdef DEBUG_INTR 805 printf("DMA fifo not empty !\n"); 806 #endif 807 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3, 808 ctest3 | CTEST3_CLF); 809 while ((bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3) & 810 CTEST3_CLF) != 0) { 811 delay(1); 812 if (++timeout > 1000) { 813 printf("clear fifo failed\n"); 814 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3, 815 bus_space_read_1(sc->sc_rt, sc->sc_rh, 816 SIOP_CTEST3) & ~CTEST3_CLF); 817 return; 818 } 819 } 820 } 821 822 int 823 siop_modechange(sc) 824 struct siop_common_softc *sc; 825 { 826 int retry; 827 int sist0, sist1, stest2; 828 for (retry = 0; retry < 5; retry++) { 829 /* 830 * datasheet says to wait 100ms and re-read SIST1, 831 * to check that DIFFSENSE is stable. 832 * We may delay() 5 times for 100ms at interrupt time; 833 * hopefully this will not happen often. 834 */ 835 delay(100000); 836 sist0 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SIST0); 837 sist1 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SIST1); 838 if (sist1 & SIEN1_SBMC) 839 continue; /* we got an irq again */ 840 sc->mode = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_STEST4) & 841 STEST4_MODE_MASK; 842 stest2 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2); 843 switch(sc->mode) { 844 case STEST4_MODE_DIF: 845 printf("%s: switching to differential mode\n", 846 sc->sc_dev.dv_xname); 847 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2, 848 stest2 | STEST2_DIF); 849 break; 850 case STEST4_MODE_SE: 851 printf("%s: switching to single-ended mode\n", 852 sc->sc_dev.dv_xname); 853 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2, 854 stest2 & ~STEST2_DIF); 855 break; 856 case STEST4_MODE_LVD: 857 printf("%s: switching to LVD mode\n", 858 sc->sc_dev.dv_xname); 859 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_STEST2, 860 stest2 & ~STEST2_DIF); 861 break; 862 default: 863 printf("%s: invalid SCSI mode 0x%x\n", 864 sc->sc_dev.dv_xname, sc->mode); 865 return 0; 866 } 867 return 1; 868 } 869 printf("%s: timeout waiting for DIFFSENSE to stabilise\n", 870 sc->sc_dev.dv_xname); 871 return 0; 872 } 873 874 void 875 siop_resetbus(sc) 876 struct siop_common_softc *sc; 877 { 878 int scntl1; 879 scntl1 = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL1); 880 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL1, 881 scntl1 | SCNTL1_RST); 882 /* minimum 25 us, more time won't hurt */ 883 delay(100); 884 bus_space_write_1(sc->sc_rt, sc->sc_rh, SIOP_SCNTL1, scntl1); 885 } 886 887 void 888 siop_update_xfer_mode(sc, target) 889 struct siop_common_softc *sc; 890 int target; 891 { 892 struct siop_common_target *siop_target = sc->targets[target]; 893 struct scsipi_xfer_mode xm; 894 895 xm.xm_target = target; 896 xm.xm_mode = 0; 897 xm.xm_period = 0; 898 xm.xm_offset = 0; 899 900 901 if (siop_target->flags & TARF_ISWIDE) 902 xm.xm_mode |= PERIPH_CAP_WIDE16; 903 if (siop_target->period) { 904 xm.xm_period = siop_target->period; 905 xm.xm_offset = siop_target->offset; 906 xm.xm_mode |= PERIPH_CAP_SYNC; 907 } 908 if (siop_target->flags & TARF_TAG) { 909 /* 1010 workaround: can't do disconnect if not wide, so can't do tag */ 910 if ((sc->features & SF_CHIP_GEBUG) == 0 || 911 (sc->targets[target]->flags & TARF_ISWIDE)) 912 xm.xm_mode |= PERIPH_CAP_TQING; 913 } 914 915 scsipi_async_event(&sc->sc_chan, ASYNC_EVENT_XFER_MODE, &xm); 916 } 917