]> xenbits.xensource.com Git - seabios.git/commitdiff
ahci: set transfer mode according to the capabilities of connected drive
authorGerd Hoffmann <kraxel@redhat.com>
Sat, 20 Feb 2016 14:20:15 +0000 (15:20 +0100)
committerKevin O'Connor <kevin@koconnor.net>
Mon, 29 Feb 2016 16:23:09 +0000 (11:23 -0500)
Use case: cf cards behind sata-ide bridge, which might not support
the default transfer mode.

Based on a patch by Werner Zeh <werner.zeh@siemens.com>,
with some minor tweaks applied.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
src/hw/ahci.c
src/hw/ata.h

index 9310850b2b4a636481c72b71c74dd4b473ada1c1..261a7d24b6a45ecb50c721a8490540d8c44e2698 100644 (file)
@@ -515,6 +515,63 @@ static int ahci_port_setup(struct ahci_port_s *port)
                               , ata_extract_version(buffer)
                               , (u32)adjsize, adjprefix);
         port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0);
+
+        s8 multi_dma = -1;
+        s8 pio_mode = -1;
+        s8 udma_mode = -1;
+        // If bit 2 in word 53 is set, udma information is valid in word 88.
+        if (buffer[53] & 0x04) {
+            udma_mode = 6;
+            while ((udma_mode >= 0) &&
+                   !((buffer[88] & 0x7f) & ( 1 << udma_mode ))) {
+                udma_mode--;
+            }
+        }
+        // If bit 1 in word 53 is set, multiword-dma and advanced pio modes
+        // are available in words 63 and 64.
+        if (buffer[53] & 0x02) {
+            pio_mode = 4;
+            multi_dma = 3;
+            while ((multi_dma >= 0) &&
+                   !((buffer[63] & 0x7) & ( 1 << multi_dma ))) {
+                multi_dma--;
+            }
+            while ((pio_mode >= 3) &&
+                   !((buffer[64] & 0x3) & ( 1 << ( pio_mode - 3 ) ))) {
+                pio_mode--;
+            }
+        }
+        dprintf(2, "AHCI/%d: supported modes: udma %d, multi-dma %d, pio %d\n",
+                port->pnr, udma_mode, multi_dma, pio_mode);
+
+        sata_prep_simple(&port->cmd->fis, ATA_CMD_SET_FEATURES);
+        port->cmd->fis.feature = ATA_SET_FEATRUE_TRANSFER_MODE;
+        // Select used mode. UDMA first, then Multi-DMA followed by
+        // advanced PIO modes 3 or 4. If non, set default PIO.
+        if (udma_mode >= 0) {
+            dprintf(1, "AHCI/%d: Set transfer mode to UDMA-%d\n",
+                    port->pnr, udma_mode);
+            port->cmd->fis.sector_count = ATA_TRANSFER_MODE_ULTRA_DMA
+                                          | udma_mode;
+        } else if (multi_dma >= 0) {
+            dprintf(1, "AHCI/%d: Set transfer mode to Multi-DMA-%d\n",
+                    port->pnr, multi_dma);
+            port->cmd->fis.sector_count = ATA_TRANSFER_MODE_MULTIWORD_DMA
+                                          | multi_dma;
+        } else if (pio_mode >= 3) {
+            dprintf(1, "AHCI/%d: Set transfer mode to PIO-%d\n",
+                    port->pnr, pio_mode);
+            port->cmd->fis.sector_count = ATA_TRANSFER_MODE_PIO_FLOW_CTRL
+                                          | pio_mode;
+        } else {
+            dprintf(1, "AHCI/%d: Set transfer mode to default PIO\n",
+                    port->pnr);
+            port->cmd->fis.sector_count = ATA_TRANSFER_MODE_DEFAULT_PIO;
+        }
+        rc = ahci_command(port, 1, 0, 0, 0);
+        if (rc < 0) {
+            dprintf(1, "AHCI/%d: Set transfer mode failed.\n", port->pnr);
+        }
     } else {
         // found cdrom (atapi)
         port->drive.type = DTYPE_AHCI_ATAPI;
index cd14e59e9b0e94a0c6db5a38de4c214c46fb555f..9de24906d8f2c508e1c38ae5e9fcf9777b79fea2 100644 (file)
@@ -155,4 +155,9 @@ void ata_setup(void);
 #define ATA_CMD_READ_NATIVE_MAX_ADDRESS      0xF8
 #define ATA_CMD_SET_MAX                      0xF9
 
+#define ATA_SET_FEATRUE_TRANSFER_MODE        0x03
+#define ATA_TRANSFER_MODE_ULTRA_DMA          0x40
+#define ATA_TRANSFER_MODE_MULTIWORD_DMA      0x20
+#define ATA_TRANSFER_MODE_PIO_FLOW_CTRL      0x08
+#define ATA_TRANSFER_MODE_DEFAULT_PIO        0x00
 #endif // ata.h