/* File: HD_LOW.C Low Level Harddisk Driver. AHDI Compatible. */ /* Copyright (c) 1988 - 1991 by Ted Schipper. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. This software is provided AS IS with no warranties of any kind. The author shall have no liability with respect to the infringement of copyrights, trade secrets or any patents by this file or any part thereof. In no event will the author be liable for any lost revenue or profits or other special, indirect and consequential damages. */ #include "dma.h" #include "mfp.h" #include "hddriver.h" #include "system.h" /*************************************************************************** LOW-LEVEL Driver ------------------ HISTORY --------- Oct 1988. THS. Started. Tested read, write and timeout routines. V0.00 ***************************************************************************/ /*************************************************************************** * * Function name : hwrite. Write to physical sectors on hard disk. * Parameters : long sector number. 21 bits address. * short sector count. 8 bits count. * long buffer addr 32 bits address. * short device number. 4 bits: bit 0 = drive # ( 0 or 1) * bit 1-3 = controller # (0-7) * Returns : long OK = write went ok. * ERROR = timeout. * Description : Write count sectors to ASCI device, starting at given * sector number, under DMA. Use the data starting at addr. * Comments : Assumes processor is in supervisor mode, and not more * than 254 sectors written. */ long hwrite(sect_nr,sect_cnt,buf_addr,dev) long sect_nr; short sect_cnt; long buf_addr; short dev; { long status; short dummy = 0; FLOCK = -1; /* disable FDC operations */ setdma(buf_addr); /* setup DMA transfer address */ DMA->MODE = NO_DMA | HDC; /* write 1st byte (0) with A1 low */ DMA->DATA = (short)((dev >> 1) << 5) | (HD_WRITE); /* cntrl + opcode */ DMA->MODE = NO_DMA | HDC | A0; /* A1 high again */ if (setss(dev,sect_nr,sect_cnt) != OK) /* write bytes 1,2,3,4 to contrlr */ { hdone(); /* restore DMA device to normal */ return(ERROR); } DMA->MODE = NO_DMA | SC_REG; /* clear FIFO = toggle R/W bit */ DMA->MODE = DMA_WR | NO_DMA | SC_REG; /* and select sector count reg */ DMA->SECT_CNT = sect_cnt; /* write sector cnt to DMA device */ DMA->MODE = DMA_WR | NO_DMA | HDC | A0;/* select DMA data register again */ DMA->DATA = dummy; /* write control byte ( = 0 ) */ DMA->MODE = DMA_WR; /* start DMA transfer */ status = endcmd(DMA_WR | NO_DMA | HDC | A0); /* wait for DMA completion */ hdone(); /* restore DMA device to normal */ return(status); } /*************************************************************************** * * Function name : hread. Read physical sectors from hard disk. * Parameters : long sector number. 21 bits address. * short sector count. 8 bits count. * long buffer addr 32 bits address. * short device number. 4 bits: bit 0 = drive # ( 0 or 1) * bit 1-3 = controller # (0-7) * Returns : long OK = read went ok. * ERROR = timeout. * Description : Read count sectors from ASCI device, starting at given * sector number, under DMA. Place the data starting at addr. * * Comments : Assumes processor is in supervisor mode, and not more * than 254 sectors are read. */ long hread(sect_nr,sect_cnt,buf_addr,dev) long sect_nr; short sect_cnt; long buf_addr; short dev; { long status; short dummy = 0; FLOCK = -1; /* disable FDC operations */ DMA->MODE = NO_DMA | HDC; /* write 1st byte (0) with A1 low */ DMA->DATA = (short)((dev >> 1) << 5) | (HD_READ); setdma(buf_addr); /* setup DMA transfer address */ if (setss(dev,sect_nr,sect_cnt) != OK) /* write bytes 1,2,3,4 to contrlr */ { hdone(); /* restore DMA device to normal */ return(ERROR); } DMA->MODE = DMA_WR | NO_DMA | SC_REG; /* clear FIFO = toggle R/W bit */ DMA->MODE = NO_DMA | SC_REG; /* and select sector count reg */ DMA->SECT_CNT = sect_cnt; /* write sector cnt to DMA device */ DMA->MODE = NO_DMA | HDC | A0; /* select DMA data register again */ DMA->DATA = dummy; /* write control byte ( = 0 ) */ DMA->MODE = dummy; /* start DMA transfer ( = 0 ) */ status = endcmd(NO_DMA | HDC | A0); /* wait for DMA completion */ hdone(); /* restore DMA device to normal */ return(status); } /*************************************************************************** * * Function name : endcmd. Wait for end of ASCI command and get status. * Parameters : short Mode word to write DMA mode register. * Returns : long. ASCI completion status or * ERROR when timeout occured. * Description : Wait for the end of an ASCI command with a long * timeout. Write mode word to DMA mode register and * read the DMA data register. Data register should * contain the completion status byte. * Comments : Assumes processor is in supervisor mode. */ long endcmd(mode) short mode; { if (fdone() != OK) /* wait for operation done ack */ return(ERRORL); DMA->MODE = mode; /* write mode word to mode register */ return((long)(DMA->DATA & 0x00FF)); /* return completion byte */ } /*************************************************************************** * * Function name : setss. Set ACSI drive, sector number and number of sectors * Parameters : short drive number. Disk drive number to use. * long sector number. First sector to be transfered. * short sector count. Number of sectors to be transfered. * Returns : long. OK = SCSI bytes ack OK. * ERROR = SCSI bytes not ack, timed out. * Description : Send 4 bytes: drv | MSB sector, MidSB sector, LSB sector, * sector count to controller. Wait after each byte for * ACSI acknowledge. * Comments : Assumes processor is in supervisor mode. */ long setss(drv,sect_nr,sect_cnt) short drv; long sect_nr; short sect_cnt; { DMA->MODE = NO_DMA | HDC | A0; /* A0 line high again */ if (qdone() != OK) /* wait for ack */ return(ERROR); DMA->DATA = (short)(((drv & 1) << 5) | (((short)(sect_nr >> 16)) & 0x1F)); DMA->MODE = NO_DMA | HDC | A0; /* write drv | MSB sector */ if (qdone() != OK) /* wait for ack */ return(ERROR); DMA->DATA = (short)((short)sect_nr >> 8); /* write MidSB sector */ DMA->MODE = NO_DMA | HDC | A0; if (qdone() != OK) return(ERROR); DMA->DATA = (short) sect_nr; /* write LSB sector */ DMA->MODE = NO_DMA | HDC | A0; if (qdone() != OK) return(ERROR); DMA->DATA = (short) sect_cnt; /* write sector count */ DMA->MODE = NO_DMA | HDC | A0; if (qdone() != OK) return(ERROR); return(OK); /* everything OK, tell the caller */ } /*************************************************************************** * * Function name : hdone. Restore DMA device to normal. * Parameters : None. * Returns : long. Status of DMA status register. * Description : Restore the DMA mode register for FDC use and clear * the FDC lock variable. Return (= read) the DMA status * register. * Comments : Assumes processor is in supervisor mode. */ long hdone() { DMA->MODE = NO_DMA; /* restore DMA mode register */ FLOCK = 0; /* FDC operations may get going again */ return((long)DMA->STATUS); /* read and return DMA status register */ } /*************************************************************************** * * Function name : setdma. Setup DMA base (transfer) address. * Parameters : long address. Address to place in base register. * Returns : None. (void) * Description : setup the DMA base HIGH / MID / LOW registers for * a DMA operation. * Comments : Assumes processor is in supervisor mode. */ void setdma(addr) long addr; { DMA->ADDR[LOW] = (char)(addr); DMA->ADDR[MID] = (char)(addr >> 8); DMA->ADDR[HIGH] = (char)(addr >> 16); } /*************************************************************************** * * Function name : qdone. Wait for command byte handshake. * Parameters : none. * Returns : long. OK = Handshake received. * ERROR = Handshake not received, timed out. * Description : Wait for a DMA handshake after a command byte has * been send. Use a short timeout. * Comments : None. */ long qdone() { return(wait_dma_cmpl(STIMEOUT)); } /*************************************************************************** * * Function name : fdone. Wait for operation done handshake. * Parameters : none. * Returns : long. OK = Handshake received. * ERROR = Handshake not received, timed out. * Description : Wait for a DMA handshake after an operation has * been completed. Use a long timeout. * Comments : None. */ long fdone() { return(wait_dma_cmpl(LTIMEOUT)); } /*************************************************************************** * * Function name : wait_dma_cmpl. Wait for DMA interrupt with timeout. * Parameters : unsigned long timerticks to wait before timeout. * Returns : long. OK = DMA interrupt occured. * ERROR = No DMA interrupt occured, timed out. * Description : Wait for a DMA interrupt (IRQ line) to occur within * timerticks * 5 milliseconds. * Comments : Assumes hz_200 timerfield is being updated and * processor is in supervisor mode. */ long wait_dma_cmpl(t_ticks) unsigned long t_ticks; { unsigned long to_count; to_count = t_ticks + HZ_200; /* calc value timer must get to */ do { if ( (MFP->GPIP & IO_DINT) == 0) /* Poll DMA IRQ interrupt */ return(OK); /* got interrupt, then OK */ } while (HZ_200 <= to_count); /* check timer */ return(ERROR); /* no interrupt, and timer expired, */ /* return ERROR status. */ }