1 /* $NetBSD: isp_pci.c,v 1.12 1997/04/13 20:14:32 cgd Exp $ */ 2 3 /* 4 * PCI specific probe and attach routines for Qlogic ISP SCSI adapters. 5 * 6 * Copyright (c) 1997 by Matthew Jacob 7 * NASA AMES Research Center 8 * All rights reserved. 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 immediately at the beginning of the file, without modification, 15 * this list of conditions, and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. 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 AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 26 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/malloc.h> 38 #include <sys/kernel.h> 39 #include <sys/queue.h> 40 #include <sys/device.h> 41 #include <machine/bus.h> 42 #include <machine/intr.h> 43 #include <scsi/scsi_all.h> 44 #include <scsi/scsiconf.h> 45 #include <dev/pci/pcireg.h> 46 #include <dev/pci/pcivar.h> 47 #include <dev/pci/pcidevs.h> 48 #include <vm/vm.h> 49 50 #include <dev/ic/ispreg.h> 51 #include <dev/ic/ispvar.h> 52 #include <dev/ic/ispmbox.h> 53 #include <dev/microcode/isp/asm_pci.h> 54 55 #ifdef __alpha__ /* XXX */ 56 /* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */ 57 extern vm_offset_t alpha_XXX_dmamap(vm_offset_t); 58 #undef vtophys 59 #define vtophys(va) alpha_XXX_dmamap((vm_offset_t) va) 60 #endif 61 #define KVTOPHYS(x) vtophys(x) 62 63 64 static u_int16_t isp_pci_rd_reg __P((struct ispsoftc *, int)); 65 static void isp_pci_wr_reg __P((struct ispsoftc *, int, u_int16_t)); 66 static vm_offset_t 67 isp_pci_mbxdma __P((struct ispsoftc *, vm_offset_t, u_int32_t)); 68 static int 69 isp_pci_dmasetup __P((struct ispsoftc *, struct scsi_xfer *, ispreq_t *, 70 u_int8_t *, u_int8_t)); 71 72 static void isp_pci_reset1 __P((struct ispsoftc *)); 73 74 static struct ispmdvec mdvec = { 75 isp_pci_rd_reg, 76 isp_pci_wr_reg, 77 isp_pci_mbxdma, 78 isp_pci_dmasetup, 79 NULL, 80 NULL, 81 isp_pci_reset1, 82 ISP_RISC_CODE, 83 ISP_CODE_LENGTH, 84 ISP_CODE_ORG, 85 BIU_PCI_CONF1_FIFO_16 | BIU_BURST_ENABLE, 86 60 /* MAGIC- all known PCI card implementations are 60MHz */ 87 }; 88 89 #define PCI_QLOGIC_ISP \ 90 ((PCI_PRODUCT_QLOGIC_ISP1020 << 16) | PCI_VENDOR_QLOGIC) 91 92 #define IO_MAP_REG 0x10 93 #define MEM_MAP_REG 0x14 94 95 96 #ifdef __BROKEN_INDIRECT_CONFIG 97 static int isp_pci_probe __P((struct device *, void *, void *)); 98 #else 99 static int isp_pci_probe __P((struct device *, struct cfdata *, void *)); 100 #endif 101 static void isp_pci_attach __P((struct device *, struct device *, void *)); 102 103 struct isp_pcisoftc { 104 struct ispsoftc pci_isp; 105 bus_space_tag_t pci_st; 106 bus_space_handle_t pci_sh; 107 void * pci_ih; 108 }; 109 110 struct cfattach isp_pci_ca = { 111 sizeof (struct isp_pcisoftc), isp_pci_probe, isp_pci_attach 112 }; 113 114 static int 115 isp_pci_probe(parent, match, aux) 116 struct device *parent; 117 #ifdef __BROKEN_INDIRECT_CONFIG 118 void *match, *aux; 119 #else 120 struct cfdata *match; 121 void *aux; 122 #endif 123 { 124 struct pci_attach_args *pa = aux; 125 126 if (pa->pa_id == PCI_QLOGIC_ISP) { 127 return (1); 128 } else { 129 return (0); 130 } 131 } 132 133 134 static void 135 isp_pci_attach(parent, self, aux) 136 struct device *parent, *self; 137 void *aux; 138 { 139 struct pci_attach_args *pa = aux; 140 struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) self; 141 bus_space_tag_t st, iot, memt; 142 bus_space_handle_t sh, ioh, memh; 143 pci_intr_handle_t ih; 144 const char *intrstr; 145 int ioh_valid, memh_valid; 146 147 ioh_valid = (pci_mapreg_map(pa, IO_MAP_REG, 148 PCI_MAPREG_TYPE_IO, 0, 149 &iot, &ioh, NULL, NULL) == 0); 150 memh_valid = (pci_mapreg_map(pa, MEM_MAP_REG, 151 PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0, 152 &memt, &memh, NULL, NULL) == 0); 153 154 if (memh_valid) { 155 st = memt; 156 sh = memh; 157 } else if (ioh_valid) { 158 st = iot; 159 sh = ioh; 160 } else { 161 printf(": unable to map device registers\n"); 162 return; 163 } 164 printf("\n"); 165 166 pcs->pci_st = st; 167 pcs->pci_sh = sh; 168 pcs->pci_isp.isp_mdvec = &mdvec; 169 isp_reset(&pcs->pci_isp); 170 if (pcs->pci_isp.isp_state != ISP_RESETSTATE) { 171 return; 172 } 173 isp_init(&pcs->pci_isp); 174 if (pcs->pci_isp.isp_state != ISP_INITSTATE) { 175 isp_uninit(&pcs->pci_isp); 176 return; 177 } 178 179 if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin, 180 pa->pa_intrline, &ih)) { 181 printf("%s: couldn't map interrupt\n", pcs->pci_isp.isp_name); 182 isp_uninit(&pcs->pci_isp); 183 return; 184 } 185 186 intrstr = pci_intr_string(pa->pa_pc, ih); 187 if (intrstr == NULL) 188 intrstr = "<I dunno>"; 189 pcs->pci_ih = 190 pci_intr_establish(pa->pa_pc, ih, IPL_BIO, isp_intr, &pcs->pci_isp); 191 if (pcs->pci_ih == NULL) { 192 printf("%s: couldn't establish interrupt at %s\n", 193 pcs->pci_isp.isp_name, intrstr); 194 isp_uninit(&pcs->pci_isp); 195 return; 196 } 197 printf("%s: interrupting at %s\n", pcs->pci_isp.isp_name, intrstr); 198 199 /* 200 * Do Generic attach now. 201 */ 202 isp_attach(&pcs->pci_isp); 203 if (pcs->pci_isp.isp_state != ISP_RUNSTATE) { 204 isp_uninit(&pcs->pci_isp); 205 } 206 } 207 208 #define PCI_BIU_REGS_OFF 0x00 209 #define PCI_MBOX_REGS_OFF 0x70 210 #define PCI_SXP_REGS_OFF 0x80 211 #define PCI_RISC_REGS_OFF 0x80 212 213 static u_int16_t 214 isp_pci_rd_reg(isp, regoff) 215 struct ispsoftc *isp; 216 int regoff; 217 { 218 struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp; 219 int offset; 220 if ((regoff & BIU_BLOCK) != 0) { 221 offset = PCI_BIU_REGS_OFF; 222 } else if ((regoff & MBOX_BLOCK) != 0) { 223 offset = PCI_MBOX_REGS_OFF; 224 } else if ((regoff & SXP_BLOCK) != 0) { 225 offset = PCI_SXP_REGS_OFF; 226 /* 227 * XXX 228 */ 229 panic("SXP Registers not accessible yet!"); 230 } else { 231 offset = PCI_RISC_REGS_OFF; 232 } 233 regoff &= 0xff; 234 offset += regoff; 235 return bus_space_read_2(pcs->pci_st, pcs->pci_sh, offset); 236 } 237 238 static void 239 isp_pci_wr_reg(isp, regoff, val) 240 struct ispsoftc *isp; 241 int regoff; 242 u_int16_t val; 243 { 244 struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp; 245 int offset; 246 if ((regoff & BIU_BLOCK) != 0) { 247 offset = PCI_BIU_REGS_OFF; 248 } else if ((regoff & MBOX_BLOCK) != 0) { 249 offset = PCI_MBOX_REGS_OFF; 250 } else if ((regoff & SXP_BLOCK) != 0) { 251 offset = PCI_SXP_REGS_OFF; 252 /* 253 * XXX 254 */ 255 panic("SXP Registers not accessible yet!"); 256 } else { 257 offset = PCI_RISC_REGS_OFF; 258 } 259 regoff &= 0xff; 260 offset += regoff; 261 bus_space_write_2(pcs->pci_st, pcs->pci_sh, offset, val); 262 } 263 264 static vm_offset_t 265 isp_pci_mbxdma(isp, kva, len) 266 struct ispsoftc *isp; 267 vm_offset_t kva; 268 u_int32_t len; 269 { 270 vm_offset_t pg, start, s1; 271 272 start = KVTOPHYS(kva); 273 274 pg = kva + NBPG; 275 s1 = (start >> PGSHIFT) + 1; 276 len -= NBPG; 277 278 while ((int32_t)len > 0) { 279 if (s1 != (KVTOPHYS(pg) >> PGSHIFT)) { 280 printf("%s: mailboxes across noncontiguous pages\n", 281 isp->isp_name); 282 return ((vm_offset_t) 0); 283 } 284 len -= NBPG; 285 pg += NBPG; 286 s1++; 287 } 288 return (start); 289 } 290 291 /* 292 * TODO: reduce the number of segments by 293 * cchecking for adjacent physical page. 294 */ 295 296 static int 297 isp_pci_dmasetup(isp, xs, rq, iptrp, optr) 298 struct ispsoftc *isp; 299 struct scsi_xfer *xs; 300 ispreq_t *rq; 301 u_int8_t *iptrp; 302 u_int8_t optr; 303 { 304 ispcontreq_t *crq; 305 unsigned long thiskv, nextkv; 306 int datalen, amt; 307 308 if (xs->datalen == 0) { 309 rq->req_seg_count = 1; 310 rq->req_flags |= REQFLAG_DATA_IN; 311 return (0); 312 } 313 314 if (xs->flags & SCSI_DATA_IN) { 315 rq->req_flags |= REQFLAG_DATA_IN; 316 } else { 317 rq->req_flags |= REQFLAG_DATA_OUT; 318 } 319 datalen = xs->datalen; 320 thiskv = (unsigned long) xs->data; 321 322 while (datalen && rq->req_seg_count < ISP_RQDSEG) { 323 nextkv = (thiskv + NBPG) & ~(NBPG-1); 324 amt = nextkv - thiskv; 325 if (amt > datalen) 326 amt = datalen; 327 rq->req_dataseg[rq->req_seg_count].ds_count = amt; 328 rq->req_dataseg[rq->req_seg_count].ds_base = KVTOPHYS(thiskv); 329 #if 0 330 printf("%s: seg%d: 0x%lx..0x%lx\n", isp->isp_name, 331 rq->req_seg_count, thiskv, 332 thiskv + (unsigned long) amt); 333 #endif 334 datalen -= amt; 335 thiskv = nextkv; 336 rq->req_seg_count++; 337 } 338 339 if (datalen == 0) { 340 return (0); 341 } 342 343 do { 344 int seg; 345 crq = (ispcontreq_t *) &isp->isp_rquest[*iptrp][0]; 346 *iptrp = (*iptrp + 1) & (RQUEST_QUEUE_LEN - 1); 347 if (*iptrp == optr) { 348 printf("%s: Request Queue Overflow++\n", 349 isp->isp_name); 350 return (1); 351 } 352 rq->req_header.rqs_entry_count++; 353 bzero((void *)crq, sizeof (*crq)); 354 crq->req_header.rqs_entry_count = 1; 355 crq->req_header.rqs_entry_type = RQSTYPE_DATASEG; 356 seg = 0; 357 while (datalen && seg < ISP_CDSEG) { 358 nextkv = (thiskv + NBPG) & ~(NBPG-1); 359 amt = nextkv - thiskv; 360 if (amt > datalen) 361 amt = datalen; 362 crq->req_dataseg[seg].ds_count = amt; 363 crq->req_dataseg[seg].ds_base = KVTOPHYS(thiskv); 364 #if 0 365 printf("%s: Cont%d seg%d: 0x%lx..0x%lx\n", 366 isp->isp_name, rq->req_header.rqs_entry_count, 367 seg, thiskv, thiskv + (unsigned long) amt); 368 #endif 369 datalen -= amt; 370 thiskv = nextkv; 371 rq->req_seg_count++; 372 seg++; 373 } 374 } while (datalen > 0); 375 return (0); 376 } 377 378 static void 379 isp_pci_reset1(isp) 380 struct ispsoftc *isp; 381 { 382 /* Make sure the BIOS is disabled */ 383 isp_pci_wr_reg(isp, HCCR, PCI_HCCR_CMD_BIOS); 384 } 385