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

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@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) */
participants (1)
-
Chen Gong