Home | History | Annotate | Line # | Download | only in sbus
isp_sbus.c revision 1.3
      1 /* $NetBSD: isp_sbus.c,v 1.3 1998/09/18 00:26:11 mjacob Exp $ */
      2 /*
      3  * SBus specific probe and attach routines for Qlogic ISP SCSI adapters.
      4  *
      5  * Copyright (c) 1997 by Matthew Jacob
      6  * NASA AMES Research Center
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice immediately at the beginning of the file, without modification,
     14  *    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. The name of the author may not be used to endorse or promote products
     19  *    derived from this software without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
     25  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  *
     33  */
     34 
     35 #include <sys/param.h>
     36 #include <sys/systm.h>
     37 #include <sys/device.h>
     38 #include <sys/kernel.h>
     39 #include <sys/malloc.h>
     40 #include <sys/queue.h>
     41 
     42 #include <machine/autoconf.h>
     43 #include <machine/cpu.h>
     44 #include <machine/param.h>
     45 #include <machine/vmparam.h>
     46 
     47 #include <dev/ic/isp_netbsd.h>
     48 #include <dev/microcode/isp/asm_sbus.h>
     49 #include <dev/sbus/sbusvar.h>
     50 
     51 static u_int16_t isp_sbus_rd_reg __P((struct ispsoftc *, int));
     52 static void isp_sbus_wr_reg __P((struct ispsoftc *, int, u_int16_t));
     53 static int isp_sbus_mbxdma __P((struct ispsoftc *));
     54 static int isp_sbus_dmasetup __P((struct ispsoftc *, struct scsipi_xfer *,
     55 	ispreq_t *, u_int8_t *, u_int8_t));
     56 static void isp_sbus_dmateardown __P((struct ispsoftc *, struct scsipi_xfer *,
     57 	u_int32_t));
     58 
     59 static struct ispmdvec mdvec = {
     60 	isp_sbus_rd_reg,
     61 	isp_sbus_wr_reg,
     62 	isp_sbus_mbxdma,
     63 	isp_sbus_dmasetup,
     64 	isp_sbus_dmateardown,
     65 	NULL,
     66 	NULL,
     67 	NULL,
     68 	ISP_RISC_CODE,
     69 	ISP_CODE_LENGTH,
     70 	ISP_CODE_ORG,
     71 	ISP_CODE_VERSION,
     72 	0,
     73 	0
     74 };
     75 
     76 struct isp_sbussoftc {
     77 	struct ispsoftc	sbus_isp;
     78 	sdparam		sbus_dev;
     79 	bus_space_tag_t	sbus_bustag;
     80 	bus_dma_tag_t	sbus_dmatag;
     81 	volatile u_char	*sbus_reg;
     82 	int		sbus_node;
     83 	int		sbus_pri;
     84 	struct ispmdvec	sbus_mdvec;
     85 	bus_dmamap_t	sbus_dmamap[MAXISPREQUEST];
     86 };
     87 
     88 
     89 static int isp_match __P((struct device *, struct cfdata *, void *));
     90 static void isp_sbus_attach __P((struct device *, struct device *, void *));
     91 struct cfattach isp_sbus_ca = {
     92 	sizeof (struct isp_sbussoftc), isp_match, isp_sbus_attach
     93 };
     94 
     95 static int
     96 isp_match(parent, cf, aux)
     97         struct device *parent;
     98         struct cfdata *cf;
     99         void *aux;
    100 {
    101 	int rv;
    102 #ifdef DEBUG
    103 	static int oneshot = 1;
    104 #endif
    105 	struct sbus_attach_args *sa = aux;
    106 
    107 	rv = (strcmp(cf->cf_driver->cd_name, sa->sa_name) == 0 ||
    108 		strcmp("PTI,ptisp", sa->sa_name) == 0 ||
    109 		strcmp("ptisp", sa->sa_name) == 0 ||
    110 		strcmp("SUNW,isp", sa->sa_name) == 0 ||
    111 		strcmp("QLGC,isp", sa->sa_name) == 0);
    112 #ifdef DEBUG
    113 	if (rv && oneshot) {
    114 		oneshot = 0;
    115 		printf("Qlogic ISP Driver, NetBSD (sbus) Platform Version "
    116 		    "%d.%d Core Version %d.%d\n",
    117 		    ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR,
    118 		    ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR);
    119 	}
    120 #endif
    121 	return (rv);
    122 }
    123 
    124 static void
    125 isp_sbus_attach(parent, self, aux)
    126         struct device *parent, *self;
    127         void *aux;
    128 {
    129 	int i, freq;
    130 	struct sbus_attach_args *sa = aux;
    131 	struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) self;
    132 	struct ispsoftc *isp = &sbc->sbus_isp;
    133 	ISP_LOCKVAL_DECL;
    134 
    135 	printf(" for %s\n", sa->sa_name);
    136 
    137 	sbc->sbus_bustag = sa->sa_bustag;
    138 	sbc->sbus_dmatag = sa->sa_dmatag;
    139 	sbc->sbus_pri = sa->sa_pri;
    140 	sbc->sbus_mdvec = mdvec;
    141 
    142 	if (sa->sa_npromvaddrs != 0) {
    143 		sbc->sbus_reg = (volatile u_char *) sa->sa_promvaddrs[0];
    144 	} else {
    145 		bus_space_handle_t bh;
    146 		if (sbus_bus_map(sa->sa_bustag, sa->sa_slot,
    147 				 sa->sa_offset,
    148 				 sa->sa_size,
    149 				 0, 0, &bh) != 0) {
    150 			printf("%s: cannot map registers\n", self->dv_xname);
    151 			return;
    152 		}
    153 		sbc->sbus_reg = (volatile u_char *)bh;
    154 	}
    155 	sbc->sbus_node = sa->sa_node;
    156 
    157 	freq = getpropint(sa->sa_node, "clock-frequency", 0);
    158 	if (freq) {
    159 		/*
    160 		 * Convert from HZ to MHz, rounding up.
    161 		 */
    162 		freq = (freq + 500000)/1000000;
    163 #if	0
    164 		printf("%s: %d MHz\n", self->dv_xname, freq);
    165 #endif
    166 	}
    167 	sbc->sbus_mdvec.dv_clock = freq;
    168 
    169 	/*
    170 	 * Some early versions of the PTI SBus adapter
    171 	 * would fail in trying to download (via poking)
    172 	 * FW. We give up on them.
    173 	 */
    174 	if (strcmp("PTI,ptisp", sa->sa_name) == 0 ||
    175 	    strcmp("ptisp", sa->sa_name) == 0) {
    176 		sbc->sbus_mdvec.dv_fwlen = 0;
    177 	}
    178 
    179 	isp->isp_mdvec = &sbc->sbus_mdvec;
    180 	isp->isp_bustype = ISP_BT_SBUS;
    181 	isp->isp_type = ISP_HA_SCSI_UNKNOWN;
    182 	isp->isp_param = &sbc->sbus_dev;
    183 	bzero(isp->isp_param, sizeof (sdparam));
    184 
    185 
    186 	ISP_LOCK(isp);
    187 	isp_reset(isp);
    188 	if (isp->isp_state != ISP_RESETSTATE) {
    189 		ISP_UNLOCK(isp);
    190 		return;
    191 	}
    192 	isp_init(isp);
    193 	if (isp->isp_state != ISP_INITSTATE) {
    194 		isp_uninit(isp);
    195 		ISP_UNLOCK(isp);
    196 		return;
    197 	}
    198 
    199 	for (i = 0; i < MAXISPREQUEST; i++) {
    200 
    201 		/* Allocate a DMA handle */
    202 		if (bus_dmamap_create(
    203 				sbc->sbus_dmatag,
    204 				MAXPHYS,	/* size */
    205 				1,		/* nsegments */
    206 				MAXPHYS,	/* maxsegsz */
    207 				0,		/* boundary */
    208 				BUS_DMA_NOWAIT,
    209 				&sbc->sbus_dmamap[i]) != 0) {
    210 			printf("%s: DMA map create error\n",
    211 				self->dv_xname);
    212 			return;
    213 		}
    214 	}
    215 
    216 	/* Establish interrupt channel */
    217 	bus_intr_establish(sbc->sbus_bustag,
    218 			   sbc->sbus_pri, 0,
    219 			   (int(*)__P((void*)))isp_intr, sbc);
    220 
    221 	/*
    222 	 * do generic attach.
    223 	 */
    224 	isp_attach(isp);
    225 	if (isp->isp_state != ISP_RUNSTATE) {
    226 		isp_uninit(isp);
    227 	}
    228 	ISP_UNLOCK(isp);
    229 }
    230 
    231 #define  SBUS_BIU_REGS_OFF		0x00
    232 #define	 SBUS_MBOX_REGS_OFF		0x80
    233 #define	 SBUS_SXP_REGS_OFF		0x200
    234 #define	 SBUS_RISC_REGS_OFF		0x400
    235 
    236 static u_int16_t
    237 isp_sbus_rd_reg(isp, regoff)
    238 	struct ispsoftc *isp;
    239 	int regoff;
    240 {
    241 	struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
    242 
    243 	int offset;
    244 	if ((regoff & BIU_BLOCK) != 0) {
    245 		offset = SBUS_BIU_REGS_OFF;
    246 	} else if ((regoff & MBOX_BLOCK) != 0) {
    247 		offset = SBUS_MBOX_REGS_OFF;
    248 	} else if ((regoff & SXP_BLOCK) != 0) {
    249 		offset = SBUS_SXP_REGS_OFF;
    250 	} else {
    251 		offset = SBUS_RISC_REGS_OFF;
    252 	}
    253 	regoff &= 0xff;
    254 	offset += regoff;
    255 	return (*((u_int16_t *) &sbc->sbus_reg[offset]));
    256 }
    257 
    258 static void
    259 isp_sbus_wr_reg (isp, regoff, val)
    260 	struct ispsoftc *isp;
    261 	int regoff;
    262 	u_int16_t val;
    263 {
    264 	struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
    265 	int offset;
    266 
    267 	if ((regoff & BIU_BLOCK) != 0) {
    268 		offset = SBUS_BIU_REGS_OFF;
    269 	} else if ((regoff & MBOX_BLOCK) != 0) {
    270 		offset = SBUS_MBOX_REGS_OFF;
    271 	} else if ((regoff & SXP_BLOCK) != 0) {
    272 		offset = SBUS_SXP_REGS_OFF;
    273 	} else {
    274 		offset = SBUS_RISC_REGS_OFF;
    275 	}
    276 	regoff &= 0xff;
    277 	offset += regoff;
    278 	*((u_int16_t *) &sbc->sbus_reg[offset]) = val;
    279 }
    280 
    281 static int
    282 isp_sbus_mbxdma(isp)
    283 	struct ispsoftc *isp;
    284 {
    285 	struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
    286 	bus_dma_segment_t seg;
    287 	size_t len, rseg;
    288 
    289 	/*
    290 	 * NOTE: Since most Sun machines aren't I/O coherent,
    291 	 * map the mailboxes through kdvma space to force them
    292 	 * to be uncached.
    293 	 */
    294 
    295 	/*
    296 	 * Allocate and map the request queue.
    297 	 */
    298 	len = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN);
    299 	if (bus_dmamem_alloc(sbc->sbus_dmatag, len, NBPG, 0,
    300 			     &seg, 1, &rseg, BUS_DMA_NOWAIT) != 0)
    301 		return (1);
    302 
    303 	if (bus_dmamem_map(sbc->sbus_dmatag, &seg, rseg, len,
    304 			   (caddr_t *)&isp->isp_rquest,
    305 			   BUS_DMA_NOWAIT|BUS_DMA_COHERENT) != 0)
    306 		return (1);
    307 	isp->isp_rquest_dma = seg.ds_addr;
    308 
    309 	/*
    310 	 * Allocate and map the result queue.
    311 	 */
    312 	len = ISP_QUEUE_SIZE(RESULT_QUEUE_LEN);
    313 	if (bus_dmamem_alloc(sbc->sbus_dmatag, len, NBPG, 0,
    314 			     &seg, 1, &rseg, BUS_DMA_NOWAIT) != 0)
    315 		return (1);
    316 
    317 	if (bus_dmamem_map(sbc->sbus_dmatag, &seg, rseg, len,
    318 			   (caddr_t *)&isp->isp_result,
    319 			   BUS_DMA_NOWAIT|BUS_DMA_COHERENT) != 0)
    320 		return (1);
    321 	isp->isp_result_dma = seg.ds_addr;
    322 
    323 	return (0);
    324 }
    325 
    326 /*
    327  * TODO: If kdvma_mapin fails, try using multiple smaller chunks..
    328  */
    329 
    330 static int
    331 isp_sbus_dmasetup(isp, xs, rq, iptrp, optr)
    332 	struct ispsoftc *isp;
    333 	struct scsipi_xfer *xs;
    334 	ispreq_t *rq;
    335 	u_int8_t *iptrp;
    336 	u_int8_t optr;
    337 {
    338 	struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
    339 	bus_dmamap_t dmamap;
    340 	int dosleep = (xs->flags & SCSI_NOSLEEP) != 0;
    341 
    342 	if (xs->datalen == 0) {
    343 		rq->req_seg_count = 1;
    344 		return (CMD_QUEUED);
    345 	}
    346 
    347 	if (rq->req_handle > RQUEST_QUEUE_LEN || rq->req_handle < 1) {
    348 		panic("%s: bad handle (%d) in isp_sbus_dmasetup\n",
    349 			isp->isp_name, rq->req_handle);
    350 		/* NOTREACHED */
    351 	}
    352 
    353 	dmamap = sbc->sbus_dmamap[rq->req_handle - 1];
    354 	if (dmamap->dm_nsegs != 0) {
    355 		panic("%s: dma map already allocated\n", isp->isp_name);
    356 		/* NOTREACHED */
    357 	}
    358 	if (bus_dmamap_load(sbc->sbus_dmatag, dmamap,
    359 			    xs->data, xs->datalen, NULL,
    360 			    dosleep ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT) != 0) {
    361 		XS_SETERR(xs, HBA_BOTCH);
    362 		return (CMD_COMPLETE);
    363 	}
    364 	bus_dmamap_sync(sbc->sbus_dmatag, dmamap,
    365 			dmamap->dm_segs[0].ds_addr, xs->datalen,
    366 			(xs->flags & SCSI_DATA_IN)
    367 				? BUS_DMASYNC_PREREAD
    368 				: BUS_DMASYNC_PREWRITE);
    369 
    370 	if (xs->flags & SCSI_DATA_IN) {
    371 		rq->req_flags |= REQFLAG_DATA_IN;
    372 	} else {
    373 		rq->req_flags |= REQFLAG_DATA_OUT;
    374 	}
    375 	rq->req_dataseg[0].ds_count = xs->datalen;
    376 	rq->req_dataseg[0].ds_base =  dmamap->dm_segs[0].ds_addr;
    377 	rq->req_seg_count = 1;
    378 	return (CMD_QUEUED);
    379 }
    380 
    381 static void
    382 isp_sbus_dmateardown(isp, xs, handle)
    383 	struct ispsoftc *isp;
    384 	struct scsipi_xfer *xs;
    385 	u_int32_t handle;
    386 {
    387 	struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
    388 	bus_dmamap_t dmamap;
    389 
    390 	if (handle >= RQUEST_QUEUE_LEN) {
    391 		panic("%s: bad handle (%d) in isp_sbus_dmateardown\n",
    392 			isp->isp_name, handle);
    393 		/* NOTREACHED */
    394 	}
    395 
    396 	dmamap = sbc->sbus_dmamap[handle];
    397 	if (dmamap->dm_nsegs == 0) {
    398 		panic("%s: dma map not already allocated\n", isp->isp_name);
    399 		/* NOTREACHED */
    400 	}
    401 	bus_dmamap_sync(sbc->sbus_dmatag, dmamap,
    402 			dmamap->dm_segs[0].ds_addr, xs->datalen,
    403 			(xs->flags & SCSI_DATA_IN)
    404 				? BUS_DMASYNC_POSTREAD
    405 				: BUS_DMASYNC_POSTWRITE);
    406 	bus_dmamap_unload(sbc->sbus_dmatag, dmamap);
    407 }
    408