/* * @(#)ipi.c 1.1 94/10/31 Copyright (c) 1988 by Sun Microsystems, Inc. * * ip.c - Boot PROM Driver for IPI Disk controller. */ #include #include #include #include #include #include #include #include #define DEV_BSIZE 512 #define LABEL /* #define SUPERFAST */ /* For very high speed boot */ /* #define DEBUG */ /* For debug purposes */ #ifdef SUPERFAST #define BIGSIZE (256) /* size in blocks (512) is ok with DVMA */ int gotcyl = -1; #endif struct ipidma { struct ipi3pkt ipi3pkt; u_long pad_to_align1; #ifndef SUPERFAST char buff_addr[8196]; /* R/W data */ #else char buff_addr[BIGSIZE * 512]; /* R/W data */ #endif u_long pad_to_align3; }; #define NSTD 4 u_long ipistd[NSTD] = { 0x1080000, 0x1080400, 0x1080800, 0x1080c00 }; struct devinfo ipinfo = { sizeof (struct ipidevice), /* size of register I/O space */ sizeof (struct ipidma), /* size of DMA memory */ 1024, /* size of parameter block */ NSTD, /* # of standard addresses */ ipistd, /* vector of standard addresses */ MAP_VME32A32D, /* which map space */ DEV_BSIZE * 16 /* transfer size 8k */ }; /* * external functions that are called by the driver interfaces. */ extern int xxboot(), xxprobe(); extern int nullsys(); int ipiopen(), ipistrategy(), ipiready(); extern int await(); /* * boottab structure that is used in the boot routine. */ struct boottab ipidriver = { "id", xxprobe, xxboot, ipiopen, nullsys, ipistrategy, "id: PANTHER (ipi) controller", &ipinfo, }; #define ADDR_DVMA(x) (u_long)((u_long)x & ~0xfff00000) #define ALIGN(x, i) (u_long)(((u_long)x + (u_long)i) & ~(u_long)(i - 1)) #define HOWMANY(x, y) ((((u_int)(x))+(((u_int)(y))-1))/((u_int)(y))) #define CTLR(sip) ( ( (sip->si_ctlr) >> 8) & 0xf) / 4 /* No kidding */ #define UNIT(sip) (sip->si_unit & 07) #define TIMEOUT 0xfffff /* timeout location */ #define LOW 0 #define HIGH 1 /* * Description: opens the IPI disk controller * Synopsis: status = ipiopen(sip) * status :(int) 0 = opened * -1 = error * sip :(int *) point to saioreq structure * Routines: bzero */ ipiopen(sip) register struct saioreq *sip; { register struct ipidevice *ipaddr; caddr_t xfer_ma; struct dk_label *dkptr; int opcode = IP_READ; int k; int i = 0; ipaddr = (struct ipidevice *)sip->si_devaddr; #ifndef BOOTBLOCK /* Verify that ipi is really present */ if (peekl((char *)&ipaddr->dev_csr) == -1) return (-1); #endif !BOOTBLOCK /* Wait for CSR_RESET bit to go LOW for 1000000 loops * after which barf "IPI reset" */ if (await (&ipaddr->dev_csr, CSR_RESET, LOW, 70000000, "reset")) { return(-1); /* waited too long */ } k = ipaddr->dev_resp; #ifdef lint k = k; #endif while ( i++ < 100000) { if (ipiready(sip)) break; } if (!isspinning(ipiready, sip)) { return(-1); } #ifdef LABEL dkptr = (struct dk_label *)sip->si_devdata; xfer_ma = (caddr_t)(((struct ipidma *)(sip->si_dmaaddr))->buff_addr); xfer_ma = (caddr_t)ALIGN(xfer_ma, 4); ipicmd (opcode|(RWMOD <<8), IP_SYNC, CTLR(sip), UNIT(sip), 0, 2, (caddr_t)xfer_ma, sip); bcopy(xfer_ma, dkptr, 2 * DEV_BSIZE); if ( dkptr->dkl_magic != 0xDABE){ printf("Bad label\n"); return(-1); } #endif return(0); } ipiready(sip) struct saioreq *sip; { int status; status = ipicmd (IP_REPORT_STAT|(RWMOD <<8), IP_SYNC, CTLR(sip), UNIT(sip), 0, 2, (caddr_t)0, sip); return(status); } /* * Description: Executes read or write on the IPI controller * Synopsis: status = ipistrategy(sip, rw) * status :(int) = 0, failure * = 1, success (count==0) * = character count read, success * sip :(int *) pointer to saio stucture * rw :(int) read/write command code (overwritten) * Routines: ipicmd */ int ipistrategy(sip, rw) struct saioreq *sip; register int rw; { register int opcode = (rw == WRITE) ? IP_WRITE : IP_READ; register int blkno, count, boff, part; struct dk_label *dkptr; caddr_t ma, xfer_ma; #ifdef SUPERFAST int savblkno, savcount, cylno; #endif #ifdef LABEL dkptr = (struct dk_label *)sip->si_devdata; part = sip->si_boff; boff = dkptr->dkl_map[part].dkl_cylno * dkptr->dkl_nsect * dkptr->dkl_nhead; #endif blkno = sip->si_bn + boff; count = sip->si_cc / DEV_BSIZE; if ((blkno + count) >= (boff + dkptr->dkl_map[part].dkl_nblk)) return(0); ma = sip->si_ma; xfer_ma = (caddr_t)(((struct ipidma *)(sip->si_dmaaddr))->buff_addr); xfer_ma = (caddr_t)ALIGN(xfer_ma, 4); if (opcode == IP_WRITE) bcopy(ma, xfer_ma, count*DEV_BSIZE); #ifdef SUPERFAST /* get the cyl */ savblkno = blkno; savcount = count; cylno = blkno / BIGSIZE ; count = BIGSIZE; blkno = cylno * BIGSIZE; if (cylno != gotcyl) { #endif ipicmd (opcode|(RWMOD <<8), IP_SYNC, CTLR(sip), UNIT(sip), blkno, count, (caddr_t)xfer_ma, sip); #ifdef SUPERFAST } blkno = savblkno; count = savcount; gotcyl = cylno; xfer_ma += (blkno % BIGSIZE)*512; #endif if (opcode == IP_READ) bcopy(xfer_ma, ma, count*DEV_BSIZE); return(count * DEV_BSIZE); } /* * Description: Internal interface to the IPI controller command set * * Synopsis: status = ipicmd(opcode, mode, ctlr, unit, blkno, count, bufaddr, sip) * status :(int) = -1, severe unretryable failure * = 0, failure * = 1, success (count==0) * = character count read, success * opcode :(int) command code * mode : IP_SYNC * sip :(int *) pointer to saio stucture * blkno :(int) block number * count :(int) number of sectors * Routines: bzero */ int ipicmd(opcode, mode, ctlr, unit, blkno, count, bufaddr, sip) u_long opcode, mode; int ctlr, unit, blkno, count; caddr_t bufaddr; struct saioreq *sip; { struct ipi3pkt *pktptr; struct ippcb pcb; struct ippcb *ppcb = &pcb; /* pcb and DVMA is already available */ bzero(ppcb, sizeof(struct ippcb)); pktptr = &(((struct ipidma *)(sip->si_dmaaddr))->ipi3pkt); pktptr = (struct ipi3pkt *)ALIGN(pktptr, 4); bzero(pktptr, sizeof(struct ipi3pkt)); ppcb->pcb_ppkt = pktptr; ppcb->pcb_mode = mode; pktptr->pkt_hdr.hdr.hdr_refno = 0xBAD; pktptr->pkt_hdr.hdr.hdr_opcode = opcode & 0xff; pktptr->pkt_hdr.hdr.hdr_mods = 1; pktptr->pkt_hdr.hdr.hdr_ctlr = ctlr; pktptr->pkt_hdr.hdr.hdr_unit = unit; switch(opcode & 0xff) { case IP_READ: case IP_WRITE: pktptr->pkt_hdr.hdr.hdr_pktlen = sizeof(struct read_write) + 6; pktptr->p_cmd.rdwr.xnp.pad = 0; pktptr->p_cmd.rdwr.xnp.id = XFER_NOTIFY; pktptr->p_cmd.rdwr.xnp.len = 0x5; pktptr->p_cmd.rdwr.xnp.bufadr = (caddr_t)ADDR_DVMA(bufaddr); pktptr->p_cmd.rdwr.cxp.pad = 0; pktptr->p_cmd.rdwr.cxp.id = CMD_EXTENT; pktptr->p_cmd.rdwr.cxp.len = 0x9; pktptr->p_cmd.rdwr.cxp.blk_cnt = count; pktptr->p_cmd.rdwr.cxp.lbn = blkno; break; case IP_REPORT_STAT: pktptr->pkt_hdr.hdr.hdr_pktlen = 6; break; default: break; } return ( ipigo(ppcb, sip) ? count * DEV_BSIZE : 0 ); } int ipigo(ppcb, sip) struct ippcb *ppcb; struct saioreq *sip; { register struct ipidevice *ipaddr; int k; u_long *pptr; ipaddr = (struct ipidevice *)sip->si_devaddr; if (await (&ipaddr->dev_csr, CSR_CRBUSY, LOW, 1000000, "device too busy")) { return(1); /* waited too long */ } ipaddr->dev_cmdreg = ADDR_DVMA(ppcb->pcb_ppkt); if (await(&ipaddr->dev_csr, CSR_CRBUSY, LOW, 10000000, "device still busy")){ return(0); /* waited too long */ } if (await(&ipaddr->dev_csr, CSR_RRVLID, HIGH, 10000000, "no response")) { return(0); /* waited too long */ } if (ipaddr->dev_csr & CSR_ERROR) { ipaddr->dev_csr |= CSR_RESET; printf("Error/doing reset\n"); k = ipaddr->dev_resp; return(0); } pptr = (u_long *)&ipaddr->dev_resp_pkt; #define IP_NOTOPER 0x40000000 /* goes in include file */ #define IP_NOTREADY 0x10000000 k = ipaddr->dev_resp; #ifdef lint k = k; #endif if ((pptr[1] & 0xffff0000) == 0x3010000 ) { if (pptr[3] & IP_NOTOPER ) { return(0); } if (pptr[3] & IP_NOTREADY) { return(0); } } return(1); } await(paddr, val, ord, timeout, s) long *paddr; u_long val, ord; int timeout; char *s; { int i; for (i=0; i < timeout; i++) { if (ord) { if ( (*paddr & val)) return(0); else continue; } else { if (!(*paddr & val)) return(0); else continue; } } printf("timeout: ipi: %s\n", s); return(1); } #ifdef DEBUG pr_pkt(pptr) u_long *pptr; { int i; printf("pptr %x:: ", pptr); for (i=0; i< 12; i++) { printf("%x ", *pptr++); } printf("\n"); } #endif