summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/dev/dev_ide_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/dev/dev_ide_common.c')
-rw-r--r--cfe/cfe/dev/dev_ide_common.c1249
1 files changed, 1249 insertions, 0 deletions
diff --git a/cfe/cfe/dev/dev_ide_common.c b/cfe/cfe/dev/dev_ide_common.c
new file mode 100644
index 0000000..e15ac8a
--- /dev/null
+++ b/cfe/cfe/dev/dev_ide_common.c
@@ -0,0 +1,1249 @@
+/* *********************************************************************
+ * Broadcom Common Firmware Environment (CFE)
+ *
+ * Generic IDE disk driver File: dev_ide_common.c
+ *
+ * This is a simple driver for IDE hard disks. The mechanics
+ * of talking to the I/O ports are abstracted sufficiently to make
+ * this driver usable for various bus interfaces.
+ *
+ * Author: Mitch Lichtenberg (mpl@broadcom.com)
+ *
+ *********************************************************************
+ *
+ * Copyright 2000,2001,2002,2003
+ * Broadcom Corporation. All rights reserved.
+ *
+ * This software is furnished under license and may be used and
+ * copied only in accordance with the following terms and
+ * conditions. Subject to these conditions, you may download,
+ * copy, install, use, modify and distribute modified or unmodified
+ * copies of this software in source and/or binary form. No title
+ * or ownership is transferred hereby.
+ *
+ * 1) Any source code used, modified or distributed must reproduce
+ * and retain this copyright notice and list of conditions
+ * as they appear in the source file.
+ *
+ * 2) No right is granted to use any trade name, trademark, or
+ * logo of Broadcom Corporation. The "Broadcom Corporation"
+ * name may not be used to endorse or promote products derived
+ * from this software without the prior written permission of
+ * Broadcom Corporation.
+ *
+ * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
+ * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
+ * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************* */
+
+
+#include "lib_types.h"
+#include "lib_malloc.h"
+#include "lib_printf.h"
+#include "lib_string.h"
+#include "cfe_timer.h"
+#include "cfe_iocb.h"
+#include "cfe_device.h"
+#include "cfe_ioctl.h"
+#include "cfe_error.h"
+
+#include "dev_ide_common.h"
+
+#include "dev_ide.h"
+
+/* *********************************************************************
+ * Macros
+ ********************************************************************* */
+
+#define DISK_MASTER 0
+#define DISK_SLAVE 1
+
+#define IDE_WRITEREG8(ide,reg,val) IDEDISP_WRITEREG8(ide->idecommon_dispatch,reg,val)
+#define IDE_WRITEREG16(ide,reg,val) IDEDISP_WRITEREG8(ide->idecommon_dispatch,reg,val)
+#define IDE_WRITEBUF(ide,reg,buf,len) IDEDISP_WRITEBUF(ide->idecommon_dispatch,reg,buf,len)
+#define IDE_READREG8(ide,reg) IDEDISP_READREG8(ide->idecommon_dispatch,reg)
+#define IDE_READREG16(ide,reg) IDEDISP_READREG16(ide->idecommon_dispatch,reg)
+#define IDE_READBUF(ide,reg,buf,len) IDEDISP_READBUF(ide->idecommon_dispatch,reg,buf,len)
+
+#define GETWORD_LE(buf,wordidx) (((unsigned int) (buf)[(wordidx)*2]) + \
+ (((unsigned int) (buf)[(wordidx)*2+1]) << 8))
+
+#define _IDE_DEBUG_
+
+
+static void idecommon_testdrq(idecommon_t *ide);
+
+/* *********************************************************************
+ * idecommon_sectorshift(size)
+ *
+ * Given a sector size, return log2(size). We cheat; this is
+ * only needed for 2048 and 512-byte sectors.
+ * Explicitly using shifts and masks in sector number calculations
+ * helps on 32-bit-only platforms, since we probably won't need
+ * a helper library.
+ *
+ * Input parameters:
+ * size - sector size
+ *
+ * Return value:
+ * # of bits to shift
+ ********************************************************************* */
+
+#define idecommon_sectorshift(size) (((size)==2048)?11:9)
+
+/* *********************************************************************
+ * idecommon_waitnotbusy(ide)
+ *
+ * Wait for an IDE device to report "not busy"
+ *
+ * Input parameters:
+ * ide - IDE interface
+ *
+ * Return value:
+ * 0 if ok, else -1 if timeout
+ ********************************************************************* */
+
+static int idecommon_waitnotbusy(idecommon_t *ide)
+{
+ int32_t timer;
+ uint8_t status;
+
+ TIMER_SET(timer,10*CFE_HZ);
+
+ while (!TIMER_EXPIRED(timer)) {
+ status = IDE_READREG8(ide,IDE_REG_STATUS);
+ if (!(status & IDE_STS_BSY) && (status & IDE_STS_DRQ)) {
+ idecommon_testdrq(ide);
+ continue;
+ }
+ if ((status & (IDE_STS_BSY | IDE_STS_DRQ )) == 0) return 0;
+ POLL();
+ }
+
+#ifdef _IDE_DEBUG_
+ xprintf("Device did not become unbusy\n");
+#endif
+ return -1;
+}
+
+#if 0
+/* *********************************************************************
+ * idecommon_waitready(ide)
+ *
+ * Wait for the specified device to become ready.
+ *
+ * Input parameters:
+ * ide - IDE interface
+ *
+ * Return value:
+ * 0 if device became ready
+ * -1 if device did not become ready
+ ********************************************************************* */
+
+static int idecommon_waitready(idecommon_t *ide)
+{
+ int32_t timer;
+ uint8_t status;
+
+ TIMER_SET(timer,10*CFE_HZ);
+
+ while (!TIMER_EXPIRED(timer)) {
+ status = IDE_READREG8(ide,IDE_REG_STATUS);
+ if (status & IDE_STS_RDY) return 0;
+ POLL();
+ }
+
+#ifdef _IDE_DEBUG_
+ xprintf("Disk did not become ready\n");
+#endif
+
+ return -1;
+}
+#endif
+
+/* *********************************************************************
+ * idecommon_waitbusy(idx)
+ *
+ * Wait for an IDE disk to start processing a command, or at
+ * least long enough to indicate that it is doing so.
+ * The code below looks suspiciously like a timing loop.
+ * unfortunately, that's what it is, determined empirically
+ * for certain ATA flash cards. Without this many reads to the
+ * ALTSTAT register, the PCMCIA controller deasserts the
+ * card detect pins briefly. Anyone have any clues?
+ *
+ * Input parameters:
+ * ide - IDE interface
+ *
+ * Return value:
+ * void
+ ********************************************************************* */
+
+static void idecommon_waitbusy(idecommon_t *ide)
+{
+ int idx;
+
+ for (idx = 0; idx < 10; idx++) {
+ IDE_READREG8(ide,IDE_REG_ALTSTAT);
+ IDE_READREG8(ide,IDE_REG_ALTSTAT);
+ IDE_READREG8(ide,IDE_REG_ALTSTAT);
+ IDE_READREG8(ide,IDE_REG_ALTSTAT);
+ }
+}
+
+
+/* *********************************************************************
+ * idecommon_wait_drq(ide)
+ *
+ * Wait for the BUSY bit to be clear and the DRQ bit to be set.
+ * This is usually the indication that it's time to transfer data.
+ *
+ * Input parameters:
+ * ide - IDE interface
+ * 0 if DRQ is set
+ * -1 if timeout occured
+ ********************************************************************* */
+
+static int idecommon_wait_drq(idecommon_t *ide)
+{
+ int32_t timer;
+ uint8_t status;
+
+ TIMER_SET(timer,10*CFE_HZ);
+
+ while (!TIMER_EXPIRED(timer)) {
+ POLL();
+ status = IDE_READREG8(ide,IDE_REG_STATUS);
+ if (!(status & IDE_STS_BSY) && (status & IDE_STS_ERR)) {
+ xprintf("Drive status: %02X error %02X\n",status,
+ IDE_READREG8(ide,IDE_REG_ERROR));
+ return -1;
+ }
+ if (!(status & IDE_STS_BSY) && (status & IDE_STS_DRQ)) return 0;
+ }
+
+#ifdef _IDE_DEBUG_
+ xprintf("Timeout waiting for disk\n");
+#endif
+
+ return -1;
+}
+
+/* *********************************************************************
+ * idecommon_testdrq(ide)
+ *
+ * Debug routine. Check the DRQ bit. If it's set, it is not
+ * supposed to be, so transfer data until it clears and report
+ * how much we had to transfer.
+ *
+ * Input parameters:
+ * ide - IDE interface
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+#ifdef _IDE_DEBUG_
+static void idecommon_testdrq(idecommon_t *ide)
+{
+ uint8_t status;
+ uint16_t data;
+ int idx;
+
+ status = IDE_READREG8(ide,IDE_REG_STATUS);
+ if (status & IDE_STS_DRQ) {
+ xprintf("Error: DRQ should be zero\n");
+ idx = 0;
+ while (status & IDE_STS_DRQ) {
+ data = IDE_READREG16(ide,IDE_REG_DATA);
+ idx++;
+ status = IDE_READREG8(ide,IDE_REG_STATUS);
+ }
+ xprintf("Had to read data %d times to clear DRQ\n",idx);
+ }
+}
+#else
+#define idecommon_testdrq(ide)
+#endif
+
+
+/* *********************************************************************
+ * idecommon_dumpregs(ide)
+ *
+ * Dump out the IDE registers. (debug routine)
+ *
+ * Input parameters:
+ * ide - ide disk
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void idecommon_dumpregs(idecommon_t *ide)
+{
+#if 0
+ uint8_t reg = 0;
+
+ reg = IDE_READREG8(ide,IDE_REG_STATUS);
+ xprintf("Status:%02X ",reg);
+
+ reg = IDE_READREG8(ide,IDE_REG_DRVHD);
+ xprintf("DrvHd:%02X ",reg);
+
+ reg = IDE_READREG8(ide,IDE_REG_CYLLSB);
+ xprintf("CylLSB:%02X ",reg);
+
+ reg = IDE_READREG8(ide,IDE_REG_CYLMSB);
+ xprintf("CylMSB:%02X ",reg);
+
+ reg = IDE_READREG8(ide,IDE_REG_SECNUM);
+ xprintf("SecNum:%02X ",reg);
+
+ reg = IDE_READREG8(ide,IDE_REG_SECCNT);
+ xprintf("SecCnt:%02X ",reg);
+
+ xprintf("\n");
+#endif
+}
+
+
+/* *********************************************************************
+ * idecommon_reset(ide)
+ *
+ * Reset the IDE interface.
+ *
+ * Input parameters:
+ * ide - IDE interface
+ *
+ * Return value:
+ * 0 if ok, else -1 if a timeout occured
+ ********************************************************************* */
+
+static int idecommon_reset(idecommon_t *ide)
+{
+ return 0;
+}
+
+/* *********************************************************************
+ * idecommon_identify(ide,buffer)
+ *
+ * Execute an IDENTIFY command to get information about the disk.
+ *
+ * Input parameters:
+ * ide - IDE interface
+ * buffer - pointer to 512 byte buffer
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int idecommon_identify(idecommon_t *ide,unsigned char *buffer)
+{
+
+ /* Device Select Protocol; see ATAPI-4 sect 9.6 */
+
+ if (idecommon_waitnotbusy(ide) < 0) return -1;
+ IDE_WRITEREG8(ide,IDE_REG_DRVHD,(ide->idecommon_unit<<4)|0);
+ if (idecommon_waitnotbusy(ide) < 0) return -1;
+
+ /* Set device registers */
+
+ IDE_WRITEREG8(ide,IDE_REG_CYLLSB,0);
+ IDE_WRITEREG8(ide,IDE_REG_CYLMSB,0);
+ IDE_WRITEREG8(ide,IDE_REG_SECNUM,1);
+ IDE_WRITEREG8(ide,IDE_REG_SECCNT,1);
+
+ idecommon_testdrq(ide);
+
+ /* Issue command, then read ALT STATUS (9.7) */
+
+ if (ide->idecommon_atapi) {
+ IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_ATAPI_IDENTIFY);
+ }
+ else {
+ IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_DRIVE_INFO);
+ }
+
+ IDE_READREG8(ide,IDE_REG_ALTSTAT);
+ idecommon_waitbusy(ide); /* should not be necessary */
+
+ /* Wait BSY=0 && DRQ=1, then read buffer, see sect 9.7 */
+
+ if (idecommon_wait_drq(ide) < 0) return -1;
+ IDE_READBUF(ide,IDE_REG_DATA,buffer,DISK_SECTORSIZE);
+
+ idecommon_testdrq(ide);
+
+ return 0;
+}
+
+/* *********************************************************************
+ * idecommon_packet(ide,packet,pktlen,databuf,datalen)
+ *
+ * Process an IDE "packet" command, for ATAPI devices
+ *
+ * Input parameters:
+ * ide - IDE interface
+ * packet,pktlen - command packet
+ * databuf,datalen - data buffer
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int idecommon_packet(idecommon_t *ide,
+ uint8_t *packet,int pktlen,
+ uint8_t *databuf,int datalen)
+{
+ uint8_t status;
+
+ /*
+ * Not valid on non-ATAPI disks
+ */
+
+ if (!ide->idecommon_atapi) return -1;
+
+ /*
+ * Set up the standard IDE registers for an ATAPI PACKET command
+ */
+
+ /* Device Select Protocol */
+ if (idecommon_waitnotbusy(ide) < 0) return -1;
+ IDE_WRITEREG8(ide,IDE_REG_DRVHD,(ide->idecommon_unit<<4));
+ if (idecommon_waitnotbusy(ide) < 0) return -1;
+
+ /* Device Registers */
+ IDE_WRITEREG8(ide,IDE_REG_BCLSB,(datalen & 0xFF));
+ IDE_WRITEREG8(ide,IDE_REG_BCMSB,((datalen >> 8) & 0xFF));
+ IDE_WRITEREG8(ide,IDE_REG_SECNUM,0);
+ IDE_WRITEREG8(ide,IDE_REG_SECCNT,0);
+ IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_ATAPI_PACKET);
+
+ /*
+ * Wait for the DRQ bit to indicate that we should send
+ * the packet.
+ */
+
+ if (idecommon_wait_drq(ide) < 0) return -1;
+
+ status = IDE_READREG8(ide,IDE_REG_IR);
+
+ /*
+ * Send the packet to the device
+ */
+
+ IDE_WRITEBUF(ide,IDE_REG_DATA,packet,pktlen);
+
+ /*
+ * Wait for BSY to be cleared and DRQ to be set.
+ */
+
+ if (idecommon_wait_drq(ide) < 0) return -1;
+ status = IDE_READREG8(ide,IDE_REG_ALTSTAT);
+ if (idecommon_wait_drq(ide) < 0) return -1;
+ status = IDE_READREG8(ide,IDE_REG_IR);
+
+
+ /*
+ * Transfer data, if necessary. The direction will depend
+ * on what the drive says. If this is a non-data command,
+ * passing databuf == NULL can avoid all this.
+ */
+
+ if (databuf) {
+ status = IDE_READREG8(ide,IDE_REG_IR);
+ if (status & IDE_IR_CD) {
+ xprintf("Error: Command/data should be zero\n");
+ }
+
+ if (status & IDE_IR_IO) { /* from device (READ) */
+ IDE_READBUF(ide,IDE_REG_DATA,databuf,datalen);
+ }
+ else { /* to device (WRITE) */
+ IDE_WRITEBUF(ide,IDE_REG_DATA,databuf,datalen);
+ }
+
+ }
+
+
+ idecommon_testdrq(ide);
+
+ return 0;
+
+}
+
+
+/* *********************************************************************
+ * idecommon_request_sense(ide)
+ *
+ * Request sense data. This also clears a UNIT_ATTENTION condition
+ *
+ * Input parameters:
+ * ide - IDE interface
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+static int idecommon_request_sense(idecommon_t *ide)
+{
+ uint8_t cdb[12];
+ uint8_t sensedata[32];
+ int res;
+ int numbytes;
+
+ numbytes = sizeof(sensedata);
+
+ cdb[0] = CDB_CMD_REQSENSE;
+ cdb[1] = 0;
+ cdb[2] = 0;
+ cdb[3] = 0;
+ cdb[4] = sizeof(sensedata);
+ cdb[5] = 0;
+ cdb[6] = 0;
+ cdb[7] = 0;
+ cdb[8] = 0;
+ cdb[9] = 0;
+ cdb[10] = 0;
+ cdb[11] = 0;
+
+ memset(sensedata,0,sizeof(sensedata));
+
+ res = idecommon_packet(ide,cdb,sizeof(cdb),sensedata,numbytes);
+
+#if 0
+ xprintf("Sense data: ");
+ xprintf("Err:%02X ",sensedata[0]);
+ xprintf("Key:%02X ",sensedata[1]);
+ xprintf("Information:%02X%02X ",sensedata[2],sensedata[3]);
+ xprintf("ASC:%02X ASCQ:%02X ",sensedata[12],sensedata[13]);
+ xprintf("\n");
+#endif
+
+ idecommon_testdrq(ide);
+
+ return res;
+}
+
+
+/* *********************************************************************
+ * idecommon_read_atapi(ide,lba,numsec,buffer)
+ *
+ * Read sector(s) from the device. This version is for ATAPI devs.
+ *
+ * Input parameters:
+ * ide - IDE interface
+ * lba - logical block address
+ * numsec - number of sectors
+ * buffer - buffer address
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int idecommon_read_atapi(idecommon_t *ide,uint64_t lba,
+ int numsec,unsigned char *buffer)
+{
+ uint8_t cdb[12];
+ int res = 0;
+ int numbytes;
+ int idx;
+
+ numbytes = numsec << idecommon_sectorshift(ide->idecommon_sectorsize);
+
+ cdb[0] = CDB_CMD_READ;
+ cdb[1] = 0;
+ cdb[2] = ((lba >> 24) & 0xFF);
+ cdb[3] = ((lba >> 16) & 0xFF);
+ cdb[4] = ((lba >> 8) & 0xFF);
+ cdb[5] = ((lba >> 0) & 0xFF);
+ cdb[6] = 0;
+ cdb[7] = ((numsec >> 8) & 0xFF);
+ cdb[8] = ((numsec >> 0) & 0xFF);
+ cdb[9] = 0;
+ cdb[10] = 0;
+ cdb[11] = 0;
+
+ for (idx = 0; idx < 4; idx++) {
+ res = idecommon_packet(ide,cdb,sizeof(cdb),buffer,numbytes);
+ if (res < 0) {
+ idecommon_request_sense(ide);
+ continue;
+ }
+ break;
+ }
+
+ return res;
+}
+
+
+/* *********************************************************************
+ * idecommon_write_atapi(ide,lba,numsec,buffer)
+ *
+ * Write sector(s) to the device. This version is for ATAPI disks
+ *
+ * Input parameters:
+ * ide - IDE interface
+ * lba - logical block address
+ * numsec - number of sectors
+ * buffer - buffer address
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int idecommon_write_atapi(idecommon_t *ide,uint64_t lba,
+ int numsec,unsigned char *buffer)
+{
+ uint8_t cdb[12];
+ int res;
+ int numbytes;
+
+ numbytes = numsec << idecommon_sectorshift(ide->idecommon_sectorsize);
+
+ cdb[0] = CDB_CMD_WRITE;
+ cdb[1] = 0;
+ cdb[2] = ((lba >> 24) & 0xFF);
+ cdb[3] = ((lba >> 16) & 0xFF);
+ cdb[4] = ((lba >> 8) & 0xFF);
+ cdb[5] = ((lba >> 0) & 0xFF);
+ cdb[6] = 0;
+ cdb[7] = ((numsec >> 8) & 0xFF);
+ cdb[8] = ((numsec >> 0) & 0xFF);
+ cdb[9] = 0;
+ cdb[10] = 0;
+ cdb[11] = 0;
+
+ res = idecommon_packet(ide,cdb,sizeof(cdb),buffer,numbytes);
+
+ return res;
+}
+
+
+/* *********************************************************************
+ * idecommon_read_lba(ide,lba,numsec,buffer)
+ *
+ * Read sector(s) from the device.
+ *
+ * Input parameters:
+ * ide - IDE interface
+ * lba - logical block address
+ * numsec - number of sectors
+ * buffer - buffer address
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int idecommon_read_lba(idecommon_t *ide,uint64_t lba,int numsec,unsigned char *buffer)
+{
+ int secidx;
+ unsigned char *ptr;
+
+ if (idecommon_waitnotbusy(ide) < 0) return -1;
+ IDE_WRITEREG8(ide,IDE_REG_DRVHD,(ide->idecommon_unit<<4) | ((lba >> 24) & 0x0F) | 0x40);
+ if (idecommon_waitnotbusy(ide) < 0) return -1;
+
+ IDE_WRITEREG8(ide,IDE_REG_CYLMSB,((lba >> 16) & 0xFF));
+ IDE_WRITEREG8(ide,IDE_REG_CYLLSB,((lba >> 8) & 0xFF));
+ IDE_WRITEREG8(ide,IDE_REG_SECNUM,(lba & 0xFF));
+ IDE_WRITEREG8(ide,IDE_REG_SECCNT,numsec);
+
+ idecommon_testdrq(ide);
+
+ IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_READ);
+
+ idecommon_waitbusy(ide);
+ if (idecommon_wait_drq(ide) < 0) return -1;
+
+ ptr = buffer;
+
+ for (secidx = 0; secidx < numsec; secidx++) {
+ IDE_READBUF(ide,IDE_REG_DATA,ptr,ide->idecommon_sectorsize);
+ ptr += ide->idecommon_sectorsize;
+ }
+
+ idecommon_testdrq(ide);
+
+ return 0;
+}
+
+
+/* *********************************************************************
+ * idecommon_write_lba(ide,lba,numsec,buffer)
+ *
+ * Write sector(s) from the device.
+ *
+ * Input parameters:
+ * ide - IDE interface
+ * lba - logical block address
+ * numsec - number of sectors
+ * buffer - buffer address
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int idecommon_write_lba(idecommon_t *ide,uint64_t lba,int numsec,unsigned char *buffer)
+{
+ int secidx;
+ uint8_t *ptr;
+
+ if (idecommon_waitnotbusy(ide) < 0) return -1;
+ IDE_WRITEREG8(ide,IDE_REG_DRVHD,(ide->idecommon_unit<<4) | ((lba >> 24) & 0x0F) | 0x40);
+ if (idecommon_waitnotbusy(ide) < 0) return -1;
+
+ IDE_WRITEREG8(ide,IDE_REG_CYLMSB,((lba >> 16) & 0xFF));
+ IDE_WRITEREG8(ide,IDE_REG_CYLLSB,((lba >> 8) & 0xFF));
+ IDE_WRITEREG8(ide,IDE_REG_SECNUM,(lba & 0xFF));
+ IDE_WRITEREG8(ide,IDE_REG_SECCNT,numsec);
+
+ IDE_WRITEREG8(ide,IDE_REG_COMMAND,IDE_CMD_WRITE);
+
+ if (idecommon_wait_drq(ide) < 0) return -1;
+
+ ptr = buffer;
+
+ for (secidx = 0; secidx < numsec; secidx++) {
+ IDE_WRITEBUF(ide,IDE_REG_DATA,ptr,ide->idecommon_sectorsize);
+ ptr += ide->idecommon_sectorsize;
+ }
+
+ idecommon_testdrq(ide);
+
+ return 0;
+}
+
+
+/* *********************************************************************
+ * idecommon_diagnostic(ide)
+ *
+ * run the device diagnostics on the IDE device. This also
+ * helps us determine if it's an IDE or ATAPI disk, since the
+ * diagnostic will leave a signature in the registers.
+ *
+ * Input parameters:
+ * softc - IDE interface
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int idecommon_diagnostic(idecommon_t *softc)
+{
+ if (idecommon_waitnotbusy(softc) < 0) return -1;
+ IDE_WRITEREG8(softc,IDE_REG_DRVHD,(softc->idecommon_unit<<4));
+ if (idecommon_waitnotbusy(softc) < 0) return -1;
+
+ IDE_WRITEREG8(softc,IDE_REG_COMMAND,IDE_CMD_DIAGNOSTIC);
+ if (idecommon_waitnotbusy(softc) < 0) return -1;
+
+ cfe_sleep(CFE_HZ/2);
+ idecommon_dumpregs(softc);
+
+ return 0;
+}
+
+
+/* *********************************************************************
+ * idecommon_getmodel(buffer,model)
+ *
+ * Get the ASCII model name out of an IDE identify buffer. some
+ * byte swapping is involved here. The trailing blanks are trimmed.
+ *
+ * Input parameters:
+ * buffer - 512-byte buffer from IDENTIFY command
+ * model - 41-byte string (max) for model name
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void idecommon_getmodel(uint8_t *buffer,char *model)
+{
+ uint16_t w;
+ int idx;
+
+ for (idx = 0; idx < 20; idx++) {
+ w = GETWORD_LE(buffer,27+idx);
+ model[idx*2] = w >> 8;
+ model[idx*2+1] = w & 0xFF;
+ }
+ for (idx = 39; idx > 0; idx--) {
+ if (model[idx] != ' ') {
+ model[idx+1] = '\0';
+ break;
+ }
+ }
+
+}
+
+/* *********************************************************************
+ * idecommon_devprobe(softc)
+ *
+ * Probe the IDE device, to determine if it's actually present
+ * or not. If present, determine if it's IDE or ATAPI and
+ * get the device size. Init our internal structures so we know
+ * how to talk to the device.
+ *
+ * Input parameters:
+ * softc - IDE structure
+ * noisy - display stuff as we probe
+ *
+ * Return value:
+ * 0 if ok, else error code
+ ********************************************************************* */
+
+int idecommon_devprobe(idecommon_t *softc,int noisy)
+{
+ int res;
+ int atapi;
+ unsigned char buffer[DISK_SECTORSIZE];
+ unsigned char model[41];
+ uint64_t ttlsect;
+ int devtype;
+ uint16_t w;
+ char *typename;
+
+ /*
+ * Reset the drive
+ */
+
+ res = idecommon_reset(softc);
+ if (res < 0) return -1;
+
+ /*
+ * Run diagnostic to get the signature.
+ */
+
+ res = idecommon_diagnostic(softc);
+ if (res < 0) return res;
+
+ /*
+ * Test signature
+ */
+
+ atapi = 0;
+ if ((IDE_READREG8(softc,IDE_REG_CYLLSB) == ATAPI_SIG_LSB) &&
+ (IDE_READREG8(softc,IDE_REG_CYLMSB) == ATAPI_SIG_MSB)) {
+ atapi = 1;
+ }
+
+ if (noisy) {
+ if (atapi) xprintf("ATAPI: ");
+ else xprintf("IDE: ");
+ }
+
+ /*
+ * Do tha appropriate IDENTIFY command to get device information
+ */
+
+ softc->idecommon_atapi = atapi;
+ res = idecommon_identify(softc,buffer);
+ if (res < 0) return -1;
+
+ /*
+ * Using that information, determine our device type
+ */
+
+ if (!atapi) {
+ devtype = IDE_DEVTYPE_DISK;
+ typename = "Disk";
+ }
+ else {
+ w = GETWORD_LE(buffer,0);
+ switch ((w >> 8) & 31) {
+ case 5: /* CD-ROM */
+ devtype = IDE_DEVTYPE_CDROM;
+ typename = "CD-ROM";
+ break;
+ default:
+ devtype = IDE_DEVTYPE_ATAPIDISK;
+ typename = "Disk";
+ break;
+ }
+ }
+
+ /*
+ * Say nice things about the device.
+ */
+
+ idecommon_getmodel(buffer,model);
+ if (noisy) xprintf("%s, \"%s\"",typename,model);
+
+
+#ifdef _IDE_DEBUG_
+ if (!softc->idecommon_atapi) {
+ ttlsect = (GETWORD_LE(buffer,57) + (GETWORD_LE(buffer,58) << 16));
+ if (noisy) xprintf(", Sectors: %llu (%lld MB)",ttlsect,
+ (uint64_t) (ttlsect/2048));
+ }
+ else {
+ ttlsect = 0;
+ }
+#endif
+ if (noisy) xprintf("\n");
+
+ /*
+ * Initialize internal structure info, especially pointers to the
+ * read/write routines and the sector size.
+ */
+
+ softc->idecommon_ttlsect = ttlsect;
+ idecommon_init(softc,devtype);
+
+ return res;
+}
+
+/* *********************************************************************
+ * idecommon_open(ctx)
+ *
+ * Process the CFE OPEN call for this device. For IDE disks,
+ * the device is reset and identified, and the geometry is
+ * determined.
+ *
+ * Input parameters:
+ * ctx - device context
+ *
+ * Return value:
+ * 0 if ok, else error code
+ ********************************************************************* */
+
+
+int idecommon_open(cfe_devctx_t *ctx)
+{
+ idecommon_t *softc = ctx->dev_softc;
+ int res;
+
+ if (softc->idecommon_deferprobe) {
+ res = idecommon_devprobe(softc,0);
+ if (res < 0) return res;
+ }
+
+ return 0;
+}
+
+/* *********************************************************************
+ * idecommon_read(ctx,buffer)
+ *
+ * Process a CFE READ command for the IDE device. This is
+ * more complex than it looks, since CFE offsets are byte offsets
+ * and we may need to read partial sectors.
+ *
+ * Input parameters:
+ * ctx - device context
+ * buffer - buffer descriptor
+ *
+ * Return value:
+ * number of bytes read, or <0 if an error occured
+ ********************************************************************* */
+
+int idecommon_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
+{
+ idecommon_t *softc = ctx->dev_softc;
+ unsigned char *bptr;
+ int blen;
+ int numsec;
+ int res = 0;
+ int amtcopy;
+ uint64_t lba;
+ uint64_t offset;
+ unsigned char sector[MAX_SECTORSIZE];
+ int sectorshift;
+
+ sectorshift = idecommon_sectorshift(softc->idecommon_sectorsize);
+
+ bptr = buffer->buf_ptr;
+ blen = buffer->buf_length;
+ offset = buffer->buf_offset;
+ numsec = (blen + softc->idecommon_sectorsize - 1) >> sectorshift;
+
+ if (offset & (softc->idecommon_sectorsize-1)) {
+ lba = (offset >> sectorshift);
+ res = (*softc->idecommon_readfunc)(softc,lba,1,sector);
+ if (res < 0) goto out;
+ amtcopy = softc->idecommon_sectorsize - (offset & (softc->idecommon_sectorsize-1));
+ if (amtcopy > blen) amtcopy = blen;
+ memcpy(bptr,&sector[offset & (softc->idecommon_sectorsize-1)],amtcopy);
+ bptr += amtcopy;
+ offset += amtcopy;
+ blen -= amtcopy;
+ }
+
+ while (blen >= softc->idecommon_sectorsize) {
+ lba = (offset >> sectorshift);
+ amtcopy = softc->idecommon_sectorsize;
+ res = (*softc->idecommon_readfunc)(softc,lba,1,bptr);
+ if (res < 0) goto out;
+ bptr += amtcopy;
+ offset += amtcopy;
+ blen -= amtcopy;
+ }
+
+ if (blen) {
+ lba = (offset >> sectorshift);
+ res = (*softc->idecommon_readfunc)(softc,lba,1,sector);
+ if (res < 0) goto out;
+ amtcopy = blen;
+ memcpy(bptr,sector,amtcopy);
+ bptr += amtcopy;
+ offset += amtcopy;
+ blen -= amtcopy;
+ }
+
+out:
+ buffer->buf_retlen = bptr - buffer->buf_ptr;
+
+ return res;
+}
+
+/* *********************************************************************
+ * idecommon_inpstat(ctx,inpstat)
+ *
+ * Test input status for the IDE disk. Disks are always ready
+ * to read.
+ *
+ * Input parameters:
+ * ctx - device context
+ * inpstat - input status structure
+ *
+ * Return value:
+ * 0
+ ********************************************************************* */
+
+int idecommon_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat)
+{
+ /* idecommon_t *softc = ctx->dev_softc; */
+
+ inpstat->inp_status = 1;
+ return 0;
+}
+
+/* *********************************************************************
+ * idecommon_write(ctx,buffer)
+ *
+ * Process a CFE WRITE command for the IDE device. If the write
+ * involves partial sectors, the affected sectors are read first
+ * and the changes are merged in.
+ *
+ * Input parameters:
+ * ctx - device context
+ * buffer - buffer descriptor
+ *
+ * Return value:
+ * number of bytes write, or <0 if an error occured
+ ********************************************************************* */
+
+int idecommon_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
+{
+ idecommon_t *softc = ctx->dev_softc;
+ unsigned char *bptr;
+ int blen;
+ int numsec;
+ int res = 0;
+ int amtcopy;
+ uint64_t offset;
+ uint64_t lba;
+ unsigned char sector[MAX_SECTORSIZE];
+ int sectorshift;
+
+ sectorshift = (softc->idecommon_sectorsize == 2048) ? 11 : 9;
+
+ bptr = buffer->buf_ptr;
+ blen = buffer->buf_length;
+ offset = buffer->buf_offset;
+ numsec = (blen + softc->idecommon_sectorsize - 1) >> sectorshift;
+
+ if (offset & (softc->idecommon_sectorsize-1)) {
+ lba = (offset >> sectorshift);
+ res = (*softc->idecommon_readfunc)(softc,lba,1,sector);
+ if (res < 0) goto out;
+ amtcopy = softc->idecommon_sectorsize - (offset & (softc->idecommon_sectorsize-1));
+ if (amtcopy > blen) amtcopy = blen;
+ memcpy(&sector[offset & (softc->idecommon_sectorsize-1)],bptr,amtcopy);
+ res = (*softc->idecommon_writefunc)(softc,lba,1,sector);
+ if (res < 0) goto out;
+ bptr += amtcopy;
+ offset += amtcopy;
+ blen -= amtcopy;
+ }
+
+ while (blen >= softc->idecommon_sectorsize) {
+ amtcopy = softc->idecommon_sectorsize;
+ lba = (offset >> sectorshift);
+ res = (*softc->idecommon_writefunc)(softc,lba,1,bptr);
+ if (res < 0) goto out;
+ bptr += amtcopy;
+ offset += amtcopy;
+ blen -= amtcopy;
+ }
+
+ if (blen) {
+ lba = (offset >> sectorshift);
+ res = (*softc->idecommon_readfunc)(softc,lba,1,sector);
+ if (res < 0) goto out;
+ amtcopy = blen;
+ memcpy(sector,bptr,amtcopy);
+ res = (*softc->idecommon_writefunc)(softc,lba,1,sector);
+ if (res < 0) goto out;
+ bptr += amtcopy;
+ offset += amtcopy;
+ blen -= amtcopy;
+ }
+
+out:
+ buffer->buf_retlen = bptr - buffer->buf_ptr;
+
+ return res;
+}
+
+
+/* *********************************************************************
+ * idecommon_ioctl(ctx,buffer)
+ *
+ * Process device I/O control requests for the IDE device.
+ *
+ * Input parameters:
+ * ctx - device context
+ * buffer - buffer descriptor
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+int idecommon_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
+{
+ idecommon_t *softc = ctx->dev_softc;
+ unsigned int *info = (unsigned int *) buffer->buf_ptr;
+ unsigned long long *linfo = (unsigned long long *) buffer->buf_ptr;
+ blockdev_info_t *devinfo;
+
+ switch ((int)buffer->buf_ioctlcmd) {
+ case IOCTL_BLOCK_GETBLOCKSIZE:
+ *info = softc->idecommon_sectorsize;
+ break;
+ case IOCTL_BLOCK_GETTOTALBLOCKS:
+ *linfo = softc->idecommon_ttlsect;
+ break;
+ case IOCTL_BLOCK_GETDEVTYPE:
+ devinfo = (blockdev_info_t *) buffer->buf_ptr;
+ devinfo->blkdev_totalblocks = softc->idecommon_ttlsect;
+ devinfo->blkdev_blocksize = softc->idecommon_sectorsize;
+ devinfo->blkdev_devtype = (softc->idecommon_devtype == IDE_DEVTYPE_CDROM) ?
+ BLOCK_DEVTYPE_CDROM : BLOCK_DEVTYPE_DISK;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/* *********************************************************************
+ * idecommon_close(ctx)
+ *
+ * Close the I/O device.
+ *
+ * Input parameters:
+ * ctx - device context
+ *
+ * Return value:
+ * 0 if ok, else error code
+ ********************************************************************* */
+
+int idecommon_close(cfe_devctx_t *ctx)
+{
+ /* idecommon_t *softc = ctx->dev_softc; */
+
+ return 0;
+}
+
+
+/* *********************************************************************
+ * idecommon_init(ide,devtype)
+ *
+ * Set up internal values based on the device type
+ *
+ * Input parameters:
+ * ide - IDE interface
+ * devtype - device type
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+void idecommon_init(idecommon_t *ide,int devtype)
+{
+
+ ide->idecommon_devtype = devtype;
+
+ switch (ide->idecommon_devtype) {
+ case IDE_DEVTYPE_DISK:
+ ide->idecommon_atapi = FALSE;
+ ide->idecommon_sectorsize = DISK_SECTORSIZE;
+ break;
+ case IDE_DEVTYPE_CDROM:
+ ide->idecommon_atapi = TRUE;
+ ide->idecommon_sectorsize = CDROM_SECTORSIZE;
+ break;
+ case IDE_DEVTYPE_ATAPIDISK:
+ ide->idecommon_atapi = TRUE;
+ ide->idecommon_sectorsize = DISK_SECTORSIZE;
+ break;
+ default:
+ ide->idecommon_atapi = FALSE;
+ ide->idecommon_sectorsize = DISK_SECTORSIZE;
+ break;
+ }
+
+ if (ide->idecommon_atapi) {
+ ide->idecommon_readfunc = idecommon_read_atapi;
+ ide->idecommon_writefunc = idecommon_write_atapi;
+ }
+ else {
+ ide->idecommon_readfunc = idecommon_read_lba;
+ ide->idecommon_writefunc = idecommon_write_lba;
+ }
+}
+
+/* *********************************************************************
+ * idecommon_attach(devdisp)
+ *
+ * Set up a cfe_devdisp structure that points at the idecommon
+ * structures.
+ *
+ * Input parameters:
+ * devdisp - cfe_devdisp_t structure
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+void idecommon_attach(cfe_devdisp_t *disp)
+{
+ disp->dev_open = idecommon_open;
+ disp->dev_read = idecommon_read;
+ disp->dev_inpstat = idecommon_inpstat;
+ disp->dev_write = idecommon_write;
+ disp->dev_ioctl = idecommon_ioctl;
+ disp->dev_close = idecommon_close;
+ disp->dev_poll = NULL;
+ disp->dev_reset = NULL;
+}