ahci: support scsi writing in AHCI driver

The "scsi write" command requires support from underlying driver.
This CL enables SCSI_WRITE10 in AHCI driver.

Tested in U-Boot console, try to i/o with sector #64:
scsi read 1000 40 1
md.b 1000 200 # check if things are not 0xcc
mw.b 1000 cc 200 # try to fill with 0xcc
scsi write 1000 40 1
mw.b 1000 0 200 # fill with zero
md.b 1000 200 # should be all 0
scsi read 1000 40 1
md.b 1000 200 # should be all 0xcc

Signed-off-by: Hung-Te Lin <hungte@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Hung-Te Lin 2012-10-29 05:23:53 +00:00 committed by Tom Rini
parent e81058c05b
commit b7a21b70d0

View File

@ -43,12 +43,13 @@ hd_driveid_t *ataid[AHCI_MAX_PORTS];
#define writel_with_flush(a,b) do { writel(a,b); readl(b); } while (0) #define writel_with_flush(a,b) do { writel(a,b); readl(b); } while (0)
/* /*
* Some controllers limit number of blocks they can read at once. Contemporary * Some controllers limit number of blocks they can read/write at once.
* SSD devices work much faster if the read size is aligned to a power of 2. * Contemporary SSD devices work much faster if the read/write size is aligned
* Let's set default to 128 and allowing to be overwritten if needed. * to a power of 2. Let's set default to 128 and allowing to be overwritten if
* needed.
*/ */
#ifndef MAX_SATA_BLOCKS_READ #ifndef MAX_SATA_BLOCKS_READ_WRITE
#define MAX_SATA_BLOCKS_READ 0x80 #define MAX_SATA_BLOCKS_READ_WRITE 0x80
#endif #endif
static inline u32 ahci_port_base(u32 base, u32 port) static inline u32 ahci_port_base(u32 base, u32 port)
@ -464,8 +465,8 @@ static int ahci_port_start(u8 port)
} }
static int get_ahci_device_data(u8 port, u8 *fis, int fis_len, u8 *buf, static int ahci_device_data_io(u8 port, u8 *fis, int fis_len, u8 *buf,
int buf_len) int buf_len, u8 is_write)
{ {
struct ahci_ioports *pp = &(probe_ent->port[port]); struct ahci_ioports *pp = &(probe_ent->port[port]);
@ -474,7 +475,7 @@ static int get_ahci_device_data(u8 port, u8 *fis, int fis_len, u8 *buf,
u32 port_status; u32 port_status;
int sg_count; int sg_count;
debug("Enter get_ahci_device_data: for port %d\n", port); debug("Enter %s: for port %d\n", __func__, port);
if (port > probe_ent->n_ports) { if (port > probe_ent->n_ports) {
printf("Invaild port number %d\n", port); printf("Invaild port number %d\n", port);
@ -490,7 +491,7 @@ static int get_ahci_device_data(u8 port, u8 *fis, int fis_len, u8 *buf,
memcpy((unsigned char *)pp->cmd_tbl, fis, fis_len); memcpy((unsigned char *)pp->cmd_tbl, fis, fis_len);
sg_count = ahci_fill_sg(port, buf, buf_len); sg_count = ahci_fill_sg(port, buf, buf_len);
opts = (fis_len >> 2) | (sg_count << 16); opts = (fis_len >> 2) | (sg_count << 16) | (is_write << 6);
ahci_fill_cmd_slot(pp, opts); ahci_fill_cmd_slot(pp, opts);
writel_with_flush(1, port_mmio + PORT_CMD_ISSUE); writel_with_flush(1, port_mmio + PORT_CMD_ISSUE);
@ -499,8 +500,7 @@ static int get_ahci_device_data(u8 port, u8 *fis, int fis_len, u8 *buf,
printf("timeout exit!\n"); printf("timeout exit!\n");
return -1; return -1;
} }
debug("get_ahci_device_data: %d byte transferred.\n", debug("%s: %d byte transferred.\n", __func__, pp->cmd_slot->status);
pp->cmd_slot->status);
return 0; return 0;
} }
@ -570,8 +570,8 @@ static int ata_scsiop_inquiry(ccb *pccb)
if (!(tmpid = malloc(sizeof(hd_driveid_t)))) if (!(tmpid = malloc(sizeof(hd_driveid_t))))
return -ENOMEM; return -ENOMEM;
if (get_ahci_device_data(port, (u8 *) & fis, 20, if (ahci_device_data_io(port, (u8 *) &fis, 20, tmpid,
tmpid, sizeof(hd_driveid_t))) { sizeof(hd_driveid_t), 0)) {
debug("scsi_ahci: SCSI inquiry command failure.\n"); debug("scsi_ahci: SCSI inquiry command failure.\n");
return -EIO; return -EIO;
} }
@ -590,9 +590,9 @@ static int ata_scsiop_inquiry(ccb *pccb)
/* /*
* SCSI READ10 command operation. * SCSI READ10/WRITE10 command operation.
*/ */
static int ata_scsiop_read10(ccb * pccb) static int ata_scsiop_read_write(ccb *pccb, u8 is_write)
{ {
u32 lba = 0; u32 lba = 0;
u16 blocks = 0; u16 blocks = 0;
@ -616,20 +616,21 @@ static int ata_scsiop_read10(ccb * pccb)
*/ */
blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]); blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]);
debug("scsi_ahci: read %d blocks starting from lba 0x%x\n", debug("scsi_ahci: %s %d blocks starting from lba 0x%x\n",
(unsigned)lba, blocks); is_write ? "write" : "read", (unsigned)lba, blocks);
/* Preset the FIS */ /* Preset the FIS */
memset(fis, 0, 20); memset(fis, 0, 20);
fis[0] = 0x27; /* Host to device FIS. */ fis[0] = 0x27; /* Host to device FIS. */
fis[1] = 1 << 7; /* Command FIS. */ fis[1] = 1 << 7; /* Command FIS. */
fis[2] = ATA_CMD_RD_DMA; /* Command byte. */ /* Command byte (read/write). */
fis[2] = is_write ? ATA_CMD_WR_DMA : ATA_CMD_RD_DMA;
while (blocks) { while (blocks) {
u16 now_blocks; /* number of blocks per iteration */ u16 now_blocks; /* number of blocks per iteration */
u32 transfer_size; /* number of bytes per iteration */ u32 transfer_size; /* number of bytes per iteration */
now_blocks = min(MAX_SATA_BLOCKS_READ, blocks); now_blocks = min(MAX_SATA_BLOCKS_READ_WRITE, blocks);
transfer_size = ATA_BLOCKSIZE * now_blocks; transfer_size = ATA_BLOCKSIZE * now_blocks;
if (transfer_size > user_buffer_size) { if (transfer_size > user_buffer_size) {
@ -647,10 +648,12 @@ static int ata_scsiop_read10(ccb * pccb)
fis[12] = (now_blocks >> 0) & 0xff; fis[12] = (now_blocks >> 0) & 0xff;
fis[13] = (now_blocks >> 8) & 0xff; fis[13] = (now_blocks >> 8) & 0xff;
/* Read from ahci */ /* Read/Write from ahci */
if (get_ahci_device_data(pccb->target, (u8 *) &fis, sizeof(fis), if (ahci_device_data_io(pccb->target, (u8 *) &fis, sizeof(fis),
user_buffer, user_buffer_size)) { user_buffer, user_buffer_size,
debug("scsi_ahci: SCSI READ10 command failure.\n"); is_write)) {
debug("scsi_ahci: SCSI %s10 command failure.\n",
is_write ? "WRITE" : "READ");
return -EIO; return -EIO;
} }
user_buffer += transfer_size; user_buffer += transfer_size;
@ -703,7 +706,10 @@ int scsi_exec(ccb *pccb)
switch (pccb->cmd[0]) { switch (pccb->cmd[0]) {
case SCSI_READ10: case SCSI_READ10:
ret = ata_scsiop_read10(pccb); ret = ata_scsiop_read_write(pccb, 0);
break;
case SCSI_WRITE10:
ret = ata_scsiop_read_write(pccb, 1);
break; break;
case SCSI_RD_CAPAC: case SCSI_RD_CAPAC:
ret = ata_scsiop_read_capacity10(pccb); ret = ata_scsiop_read_capacity10(pccb);