I have an old Maxtor 7Y250M0 SATA drive that didn't quite work when
attached to an Intel AHCI controller.  The drive was properly
detected, but any attempt to read from the drive failed.  It worked
fine if I switched the SATA controller into IDE mode in the BIOS, so
the drive was fine.  Turns out our atascsi layer didn't set the
transfer mode like our old wdc/pciide code does.  Fixing this
omission makes the drive work.

It doesn't really make all that much sense doing this for a SATA drive
since they don't really support different DMA modes.  But there is a
PATA variant of the same drive, so I suppose somebody left an obselete
check in the firmware code.

The diff below only checks for Ultra DMA modes.  All SATA drives
should support those.  If we ever want to support ancient PATA drives
over atascsi, some additional code will be needed.

ok?


Index: atascsi.c
===================================================================
RCS file: /cvs/src/sys/dev/ata/atascsi.c,v
retrieving revision 1.125
diff -u -p -r1.125 atascsi.c
--- atascsi.c   28 Aug 2015 00:03:53 -0000      1.125
+++ atascsi.c   28 Dec 2015 23:23:14 -0000
@@ -266,6 +266,8 @@ atascsi_probe(struct scsi_link *link)
        int                             port, type, qdepth;
        int                             rv;
        u_int16_t                       cmdset;
+       u_int16_t                       validinfo, ultradma;
+       int                             i, xfermode = -1;
 
        port = link->target;
        if (port >= as->as_link.adapter_buswidth)
@@ -363,6 +365,24 @@ atascsi_probe(struct scsi_link *link)
 
        if (type != ATA_PORT_T_DISK)
                return (0);
+
+       /*
+        * Early SATA drivers (as well as PATA drives) need to have
+        * their transfer mode set properly, otherwise commands that
+        * use DMA will time out.
+        */
+       validinfo = letoh16(ap->ap_identify.validinfo);
+       if (ISSET(validinfo, ATA_ID_VALIDINFO_ULTRADMA)) {
+               ultradma = letoh16(ap->ap_identify.ultradma);
+               for (i = 7; i >= 0; i--) {
+                       if (ultradma & (1 << i)) {
+                               xfermode = ATA_SF_XFERMODE_UDMA | i;
+                               break;
+                       }
+               }
+       }
+       if (xfermode != -1)
+               (void)atascsi_port_set_features(ap, ATA_SF_XFERMODE, xfermode);
 
        if (as->as_capability & ASAA_CAP_NCQ &&
            ISSET(letoh16(ap->ap_identify.satacap), ATA_SATACAP_NCQ) &&
Index: atascsi.h
===================================================================
RCS file: /cvs/src/sys/dev/ata/atascsi.h,v
retrieving revision 1.49
diff -u -p -r1.49 atascsi.h
--- atascsi.h   15 May 2015 10:54:26 -0000      1.49
+++ atascsi.h   28 Dec 2015 23:23:14 -0000
@@ -53,6 +53,8 @@ struct scsi_link;
  * ATA SET FEATURES subcommands
  */
 #define ATA_SF_WRITECACHE_EN   0x02
+#define ATA_SF_XFERMODE                0x03
+#define  ATA_SF_XFERMODE_UDMA  0x40
 #define ATA_SF_LOOKAHEAD_EN    0xaa
 
 struct ata_identify {
@@ -77,6 +79,7 @@ struct ata_identify {
        u_int16_t       piomode;        /*  51 */
        u_int16_t       dmamode;        /*  52 */
        u_int16_t       validinfo;      /*  53 */
+#define ATA_ID_VALIDINFO_ULTRADMA      0x0004
        u_int16_t       curcyls;        /*  54 */
        u_int16_t       curheads;       /*  55 */
        u_int16_t       cursectrk;      /*  56 */

Reply via email to