[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