- /* 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];
-
- /* Read from ahci */
- if (get_ahci_device_data(pccb->target, (u8 *) & fis, 20,
- pccb->pdata, pccb->datalen)) {
- debug("scsi_ahci: SCSI READ10 command failure.\n");
- return -EIO;
+ debug("scsi_ahci: %s %d blocks starting from lba 0x%x\n",
+ is_write ? "write" : "read", (unsigned)lba, blocks);
+
+ /* Preset the FIS */
+ memset(fis, 0, sizeof(fis));
+ fis[0] = 0x27; /* Host to device FIS. */
+ fis[1] = 1 << 7; /* Command FIS. */
+ /* Command byte (read/write). */
+ fis[2] = is_write ? ATA_CMD_WRITE_EXT : ATA_CMD_READ_EXT;
+
+ while (blocks) {
+ u16 now_blocks; /* number of blocks per iteration */
+ u32 transfer_size; /* number of bytes per iteration */
+
+ now_blocks = min(MAX_SATA_BLOCKS_READ_WRITE, blocks);
+
+ transfer_size = ATA_SECT_SIZE * now_blocks;
+ if (transfer_size > user_buffer_size) {
+ printf("scsi_ahci: Error: buffer too small.\n");
+ return -EIO;
+ }
+
+ /* LBA48 SATA command but only use 32bit address range within
+ * that. The next smaller command range (28bit) is too small.
+ */
+ fis[4] = (lba >> 0) & 0xff;
+ fis[5] = (lba >> 8) & 0xff;
+ fis[6] = (lba >> 16) & 0xff;
+ fis[7] = 1 << 6; /* device reg: set LBA mode */
+ fis[8] = ((lba >> 24) & 0xff);
+ fis[3] = 0xe0; /* features */
+
+ /* Block (sector) count */
+ fis[12] = (now_blocks >> 0) & 0xff;
+ fis[13] = (now_blocks >> 8) & 0xff;
+
+ /* Read/Write from ahci */
+ if (ahci_device_data_io(pccb->target, (u8 *) &fis, sizeof(fis),
+ user_buffer, user_buffer_size,
+ is_write)) {
+ debug("scsi_ahci: SCSI %s10 command failure.\n",
+ is_write ? "WRITE" : "READ");
+ return -EIO;
+ }
+
+ /* If this transaction is a write, do a following flush.
+ * Writes in u-boot are so rare, and the logic to know when is
+ * the last write and do a flush only there is sufficiently
+ * difficult. Just do a flush after every write. This incurs,
+ * usually, one extra flush when the rare writes do happen.
+ */
+ if (is_write) {
+ if (-EIO == ata_io_flush(pccb->target))
+ return -EIO;
+ }
+ user_buffer += transfer_size;
+ user_buffer_size -= transfer_size;
+ blocks -= now_blocks;
+ lba += now_blocks;