[U-Boot-Users] [PATCH] Support LBA48 MODE

Chen Gong g.chen at freescale.com
Thu Aug 30 09:56:45 CEST 2007


This patch add a new SCSI command READ16 to support LBA48 mode.
But it is not complete because only 32bit not 48bit can be supported.
Now it can read correct capacity and boot from a big hard disk( >137G & <2T)

Signed-off-by: Chen Gong <g.chen at freescale.com>
---
 common/cmd_scsi.c             |   31 +++++++++-
 drivers/ahci.c                |  132 +++++++++++++++++++++++++++++++++++-----
 include/ata.h                 |    1 +
 include/configs/MPC8641HPCN.h |    1 +
 include/scsi.h                |    1 +
 5 files changed, 148 insertions(+), 18 deletions(-)

diff --git a/common/cmd_scsi.c b/common/cmd_scsi.c
index 00b84fa..49ae5c7 100644
--- a/common/cmd_scsi.c
+++ b/common/cmd_scsi.c
@@ -31,6 +31,7 @@
 #include <command.h>
 #include <asm/processor.h>
 #include <scsi.h>
+#include <ata.h>
 #include <image.h>
 #include <pci.h>
 
@@ -70,6 +71,8 @@ void scsi_setup_test_unit_ready(ccb * pccb);
 void scsi_setup_read_capacity(ccb * pccb);
 void scsi_setup_read6(ccb * pccb, unsigned long start, unsigned short blocks);
 void scsi_setup_read_ext(ccb * pccb, unsigned long start, unsigned short blocks);
+void scsi_setup_read16_ext(ccb * pccb, unsigned long start,
+		unsigned short blocks);
 void scsi_setup_inquiry(ccb * pccb);
 void scsi_ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len);
 
@@ -539,6 +542,33 @@ void scsi_setup_read_capacity(ccb * pccb)
 
 }
 
+/* FIXME: only support 32bit address, not 48bit */
+void scsi_setup_read16_ext(ccb * pccb, unsigned long start,
+		unsigned short blocks)
+{
+	pccb->cmd[0] = SCSI_READ16;
+	pccb->cmd[1] = pccb->lun<<5;
+	pccb->cmd[2] = 0;
+	pccb->cmd[3] = 0;
+	pccb->cmd[4] = 0;
+	pccb->cmd[5] = 0;
+	pccb->cmd[6] = ((unsigned char) (start>>24))&0xff;
+	pccb->cmd[7] = ((unsigned char) (start>>16))&0xff;
+	pccb->cmd[8] = ((unsigned char) (start>>8))&0xff;
+	pccb->cmd[9] = ((unsigned char) (start))&0xff;
+	pccb->cmd[10] = 0;
+	pccb->cmd[11] = 0;
+	pccb->cmd[12] = ((unsigned char) (blocks>>8))&0xff;
+	pccb->cmd[13] = (unsigned char) blocks & 0xff;
+	pccb->cmdlen = 16;
+	pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
+	debug ("scsi_setup_read16_ext: cmd: %02X %02X startblk "
+		"%02X%02X%02X%02X blccnt %02X%02X\n",
+		pccb->cmd[0], pccb->cmd[1],
+		pccb->cmd[6], pccb->cmd[7], pccb->cmd[8], pccb->cmd[9],
+		pccb->cmd[12], pccb->cmd[13]);
+}
+
 void scsi_setup_read_ext(ccb * pccb, unsigned long start, unsigned short blocks)
 {
 	pccb->cmd[0]=SCSI_READ10;
@@ -550,7 +580,6 @@ void scsi_setup_read_ext(ccb * pccb, unsigned long start, unsigned short blocks)
 	pccb->cmd[6]=0;
 	pccb->cmd[7]=((unsigned char) (blocks>>8))&0xff;
 	pccb->cmd[8]=(unsigned char) blocks & 0xff;
-	pccb->cmd[6]=0;
 	pccb->cmdlen=10;
 	pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */
 	debug ("scsi_setup_read_ext: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n",
diff --git a/drivers/ahci.c b/drivers/ahci.c
index 38f7c40..f951dea 100644
--- a/drivers/ahci.c
+++ b/drivers/ahci.c
@@ -39,11 +39,23 @@
 #include <linux/ctype.h>
 #include <ahci.h>
 
+#define COMMANDSET2_LBA48	(1 << 10)
+
 struct ahci_probe_ent *probe_ent = NULL;
 hd_driveid_t *ataid[AHCI_MAX_PORTS];
 
 #define writel_with_flush(a,b)	do { writel(a,b); readl(b); } while (0)
 
+void swap_buf_le16(u16 *buf, unsigned int buf_words)
+{
+#ifdef __BIG_ENDIAN
+	unsigned int i;
+
+	for (i = 0; i < buf_words; i++)
+		buf[i] = le16_to_cpu(buf[i]);
+#endif /* __BIG_ENDIAN */
+}
+
 
 static inline u32 ahci_port_base(u32 base, u32 port)
 {
@@ -238,6 +250,7 @@ static void ahci_print_info(struct ahci_probe_ent *probe_ent)
 	       "%s%s%s%s%s%s%s\n",
 	       cap & (1 << 31) ? "64bit " : "",
 	       cap & (1 << 30) ? "ncq " : "",
+	       cap & (1 << 29) ? "sntf " : "",
 	       cap & (1 << 28) ? "ilck " : "",
 	       cap & (1 << 27) ? "stag " : "",
 	       cap & (1 << 26) ? "pm " : "",
@@ -537,6 +550,7 @@ static int ata_scsiop_inquiry(ccb *pccb)
 		debug("scsi_ahci: SCSI inquiry command failure.\n");
 		return -EIO;
 	}
+	swap_buf_le16(tmpid, 256);
 
 	if (ataid[port])
 		free(ataid[port]);
@@ -575,20 +589,41 @@ static int ata_scsiop_read10(ccb * pccb)
 		return 0;
 	memset(fis, 0, 20);
 
-	/* Construct the FIS */
-	fis[0] = 0x27;		/* Host to device FIS. */
-	fis[1] = 1 << 7;	/* Command FIS. */
-	fis[2] = ATA_CMD_RD_DMA;	/* Command byte. */
-
-	/* LBA address, only support LBA28 in this driver */
-	fis[4] = pccb->cmd[5];
-	fis[5] = pccb->cmd[4];
-	fis[6] = pccb->cmd[3];
-	fis[7] = (pccb->cmd[2] & 0x0f) | 0xe0;
-
-	/* Sector Count */
-	fis[12] = pccb->cmd[8];
-	fis[13] = pccb->cmd[7];
+	if (lba & 0xF0000000) {
+		/* Construct the FIS */
+		fis[0] = 0x27;			/* Host to device FIS. */
+		fis[1] = 1 << 7;		/* Command FIS. */
+		fis[2] = ATA_CMD_RD_DMAX;	/* Command type. */
+		fis[3] = len & 0xff;
+		fis[11] = (len >> 8) & 0xff;
+
+		/* LBA48 address */
+		fis[4] = pccb->cmd[5];
+		fis[5] = pccb->cmd[4];
+		fis[6] = pccb->cmd[3];
+		fis[8] = pccb->cmd[2];
+
+		fis[7] = 3 << 6;
+
+		/* Sector Count */
+		fis[12] = pccb->cmd[8];
+		fis[13] = pccb->cmd[7];
+	} else {
+		/* Construct the FIS */
+		fis[0] = 0x27;		/* Host to device FIS. */
+		fis[1] = 1 << 7;	/* Command FIS. */
+		fis[2] = ATA_CMD_RD_DMA;	/* Command type. */
+
+		/* LBA28 address */
+		fis[4] = pccb->cmd[5];
+		fis[5] = pccb->cmd[4];
+		fis[6] = pccb->cmd[3];
+		fis[7] = (pccb->cmd[2] & 0x0f) | 0xe0;
+
+		/* Sector Count */
+		fis[12] = pccb->cmd[8];
+		fis[13] = pccb->cmd[7];
+	}
 
 	/* Read from ahci */
 	if (get_ahci_device_data(pccb->target, (u8 *) & fis, 20,
@@ -617,8 +652,14 @@ static int ata_scsiop_read_capacity10(ccb *pccb)
 
 	memset(buf, 0, 8);
 
-	*(u32 *) buf = le32_to_cpu(ataid[pccb->target]->lba_capacity);
-
+	if ((ataid[pccb->target]->command_set_2 & COMMANDSET2_LBA48)) {
+		((u16 *)buf)[0] = ataid[pccb->target]->lba48_capacity[1];
+		((u16 *)buf)[1] = ataid[pccb->target]->lba48_capacity[0];
+	} else {
+		((u16 *)buf)[0] = ataid[pccb->target]->lba_capacity & 0xffff;
+		((u16 *)buf)[1] =
+			(ataid[pccb->target]->lba_capacity >> 16) & 0xffff;
+	}
 	buf[6] = 512 >> 8;
 	buf[7] = 512 & 0xff;
 
@@ -627,6 +668,60 @@ static int ata_scsiop_read_capacity10(ccb *pccb)
 	return 0;
 }
 
+/*
+ * SCSI READ16 command operation.
+ */
+static int ata_scsiop_read16(ccb * pccb)
+{
+	u32 len = 0;
+	u8 fis[20];
+
+	len |= ((u32)pccb->cmd[10]) << 24;
+	len |= ((u32)pccb->cmd[11]) << 16;
+	len |= ((u32)pccb->cmd[12]) << 8;
+	len |= ((u32)pccb->cmd[13]);
+
+	/* For 10-byte and 16-byte SCSI R/W commands, transfer
+	 * length 0 means transfer 0 block of data.
+	 * However, for ATA R/W commands, sector count 0 means
+	 * 256 or 65536 sectors, not 0 sectors as in SCSI.
+	 *
+	 * WARNING: one or two older ATA drives treat 0 as 0...
+	 */
+	if (!len)
+		return 0;
+	memset(fis, 0, 20);
+
+	/* Construct the FIS */
+	fis[0] = 0x27;			/* Host to device FIS. */
+	fis[1] = 1 << 7;		/* Command FIS. */
+	fis[2] = ATA_CMD_RD_DMAX;	/* Command type. */
+	fis[3] = len & 0xff;
+	fis[11] = (len >> 8) & 0xff;
+
+	/* LBA48 address */
+	fis[4] = pccb->cmd[9];
+	fis[5] = pccb->cmd[8];
+	fis[6] = pccb->cmd[7];
+	fis[8] = pccb->cmd[6];
+
+	fis[10] = pccb->cmd[3];
+	fis[7] = 3 << 6;
+
+	/* Sector Count */
+	fis[12] = pccb->cmd[13];
+	fis[13] = pccb->cmd[12];
+
+	/* Read from ahci */
+	if (get_ahci_device_data(pccb->target, (u8 *) & fis, 20,
+				 pccb->pdata, pccb->datalen)) {
+		debug("scsi_ahci: SCSI READ16 command failure.\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
 
 /*
  * SCSI TEST UNIT READY command operation.
@@ -645,6 +740,9 @@ int scsi_exec(ccb *pccb)
 	case SCSI_READ10:
 		ret = ata_scsiop_read10(pccb);
 		break;
+	case SCSI_READ16:
+		ret = ata_scsiop_read16(pccb);
+		break;
 	case SCSI_RD_CAPAC:
 		ret = ata_scsiop_read_capacity10(pccb);
 		break;
@@ -655,7 +753,7 @@ int scsi_exec(ccb *pccb)
 		ret = ata_scsiop_inquiry(pccb);
 		break;
 	default:
-		printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]);
+		printf("Unsupported SCSI command 0x%02x\n", pccb->cmd[0]);
 		return FALSE;
 	}
 
diff --git a/include/ata.h b/include/ata.h
index 8584226..4973020 100644
--- a/include/ata.h
+++ b/include/ata.h
@@ -99,6 +99,7 @@
 #define ATA_CMD_WR_MULT	0xC5	/* Write Multiple		*/
 #define ATA_CMD_SETMULT	0xC6	/* Set Multiple Mode		*/
 #define ATA_CMD_RD_DMA	0xC8	/* Read DMA (with retries)	*/
+#define ATA_CMD_RD_DMAX	0x25	/* Read DMA EXT (with retries)	*/
 #define ATA_CMD_RD_DMAN	0xC9	/* Read DMS ( no  retries)	*/
 #define ATA_CMD_WR_DMA	0xCA	/* Write DMA (with retries)	*/
 #define ATA_CMD_WR_DMAN	0xCB	/* Write DMA ( no  retires)	*/
diff --git a/include/configs/MPC8641HPCN.h b/include/configs/MPC8641HPCN.h
index 94f1428..5b69d60 100644
--- a/include/configs/MPC8641HPCN.h
+++ b/include/configs/MPC8641HPCN.h
@@ -384,6 +384,7 @@
 #define CONFIG_SCSI_AHCI
 
 #ifdef CONFIG_SCSI_AHCI
+#define CONFIG_LBA48
 #define CONFIG_SATA_ULI5288
 #define CFG_SCSI_MAX_SCSI_ID	4
 #define CFG_SCSI_MAX_LUN	1
diff --git a/include/scsi.h b/include/scsi.h
index 2be4d40..28382f6 100644
--- a/include/scsi.h
+++ b/include/scsi.h
@@ -147,6 +147,7 @@ typedef struct SCSI_cmd_block{
 #define SCSI_MED_REMOVL	0x1E		/* Prevent/Allow medium Removal (O) */
 #define SCSI_READ6		0x08		/* Read 6-byte (MANDATORY) */
 #define SCSI_READ10		0x28		/* Read 10-byte (MANDATORY) */
+#define SCSI_READ16		0x88		/* Read 16-byte (MANDATORY) */
 #define SCSI_RD_CAPAC	0x25		/* Read Capacity (MANDATORY) */
 #define SCSI_RD_DEFECT	0x37		/* Read Defect Data (O) */
 #define SCSI_READ_LONG	0x3E		/* Read Long (O) */
-- 
1.5.2





More information about the U-Boot mailing list