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