Add SATA ATAPI support for AHCI controllers.
authorHasso Tepper <hasso@dragonflybsd.org>
Tue, 20 Nov 2007 09:25:21 +0000 (09:25 +0000)
committerHasso Tepper <hasso@dragonflybsd.org>
Tue, 20 Nov 2007 09:25:21 +0000 (09:25 +0000)
Obtained-from: FreeBSD
Reviewed-by: tgen@
sys/dev/disk/nata/ata-chipset.c
sys/dev/disk/nata/atapi-cd.c
sys/dev/disk/nata/atapi-cd.h

index 636412d..9a976e0 100644 (file)
@@ -24,7 +24,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/ata/ata-chipset.c,v 1.196 2007/04/08 19:18:51 sos Exp $
- * $DragonFly: src/sys/dev/disk/nata/ata-chipset.c,v 1.9 2007/10/19 11:53:14 tgen Exp $
+ * $DragonFly: src/sys/dev/disk/nata/ata-chipset.c,v 1.10 2007/11/20 09:25:21 hasso Exp $
  */
 
 #include "opt_ata.h"
@@ -416,7 +416,7 @@ ata_request2fis_h2d(struct ata_request *request, u_int8_t *fis)
        fis[0] = 0x27;  /* host to device */
        fis[1] = 0x80;  /* command FIS (note PM goes here) */
        fis[2] = ATA_PACKET_CMD;
-       if (request->flags & ATA_R_DMA)
+       if (request->flags & (ATA_R_READ | ATA_R_WRITE))
            fis[3] = ATA_F_DMA;
        else {
            fis[5] = request->transfersize;
@@ -568,16 +568,44 @@ ata_ahci_status(device_t dev)
 
     if (action & (1 << ch->unit)) {
        u_int32_t istatus = ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset);
+       u_int32_t cstatus = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CI + offset);
 
        /* clear interrupt(s) */
        ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, action & (1 << ch->unit));
        ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset, istatus);
 
        /* do we have any PHY events ? */
+       /* XXX SOS check istatus phy bits */
        ata_sata_phy_check_events(dev);
 
-       /* do we have any device action ? */
-       return (!(ATA_INL(ctlr->r_res2, ATA_AHCI_P_CI + offset) & (1 << tag)));
+       /* do we have a potentially hanging engine to take care of? */
+       if ((istatus & 0x78400050) && (cstatus & (1 << tag))) {
+           u_int32_t cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
+           int timeout = 0;
+           /* kill off all activity on this channel */
+           ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+                    cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
+           /* XXX SOS this is not entirely wrong */
+           do {
+               DELAY(1000);
+               if (timeout++ > 500) {
+                   device_printf(dev, "stopping AHCI engine failed\n");
+                   break;
+               }
+           } while (ATA_INL(ctlr->r_res2,
+                            ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_CR);
+           /* start operations on this channel */
+           ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+                    cmd | (ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
+           return 1;
+       }
+       else
+           return (!(cstatus & (1 << tag)));
     }
     return 0;
 }
@@ -641,9 +669,29 @@ ata_ahci_begin_transaction(struct ata_request *request)
                 ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) &
                 ~ATA_AHCI_P_CMD_ATAPI);
 
-    /* issue the command */
+    /* issue command to controller */
     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CI + offset, (1 << tag));
 
+    if (!(request->flags & ATA_R_ATAPI)) {
+       /* device reset doesn't interrupt */
+       if (request->u.ata.command == ATA_DEVICE_RESET) {
+           u_int32_t tf_data;
+           int timeout = 1000000;
+
+           do {
+               DELAY(10);
+               tf_data = ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + (ch->unit<<7));
+           } while ((tf_data & ATA_S_BUSY) && timeout--);
+           if (bootverbose)
+               device_printf(ch->dev, "device_reset timeout=%dus\n",
+                             (1000000-timeout)*10);
+           request->status = tf_data;
+           if (request->status & ATA_S_ERROR)
+               request->error = tf_data >> 8;
+           return ATA_OP_FINISHED;
+       }
+    }
+
     /* start the timeout */
     callout_reset(&request->callout, request->timeout * hz,
                  (timeout_t*)ata_timeout, request);
@@ -688,7 +736,7 @@ ata_ahci_reset(device_t dev)
 {
     struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
     struct ata_channel *ch = device_get_softc(dev);
-    u_int32_t cmd;
+    u_int32_t cmd, signature;
     int offset = ch->unit << 7;
     int timeout;
 
@@ -707,10 +755,11 @@ ata_ahci_reset(device_t dev)
     timeout = 0;
     do {
        DELAY(1000);
-       if (timeout++ > 500)
+       if (timeout++ > 500) {
            device_printf(dev, "stopping AHCI engine failed\n");
            break;
        }
+    }
     while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_CR);
 
     /* issue Command List Override if supported */ 
@@ -721,43 +770,51 @@ ata_ahci_reset(device_t dev)
        timeout = 0;
        do {
            DELAY(1000);
-           if (timeout++ > 500)
+           if (timeout++ > 500) {
                device_printf(dev, "executing CLO failed\n");
                break;
            }
+       }
        while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD+offset)&ATA_AHCI_P_CMD_CLO);
     }
 
-    /* spin up device */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, ATA_AHCI_P_CMD_SUD);
-
-    /* enable interface */
+    /* reset PHY and decide what is present */
     if (ata_sata_phy_reset(dev)) {
-       switch (ATA_INL(ctlr->r_res2, ATA_AHCI_P_SIG + offset)) {
-       case 0xeb140101:
-           ch->devices = ATA_ATAPI_MASTER;
-           device_printf(ch->dev, "SATA ATAPI devices not supported yet\n");
-           ch->devices = 0;
+
+       /* clear any interrupts pending on this channel */
+       ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
+                ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
+
+       /* clear SATA error register */
+       ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
+
+       /* start operations on this channel */
+       ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+                (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
+                 ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
+
+       signature = ATA_INL(ctlr->r_res2, ATA_AHCI_P_SIG + offset);
+       switch (signature) {
+       case 0x00000101:
+           ch->devices = ATA_ATA_MASTER;
            break;
        case 0x96690101:
            ch->devices = ATA_PORTMULTIPLIER;
            device_printf(ch->dev, "Portmultipliers not supported yet\n");
            ch->devices = 0;
            break;
-       case 0x00000101:
-           ch->devices = ATA_ATA_MASTER;
+       case 0xeb140101:
+           ch->devices = ATA_ATAPI_MASTER;
            break;
+       default: /* SOS XXX */
+           if (bootverbose)
+               device_printf(ch->dev, "No signature, assuming disk device\n");
+           ch->devices = ATA_ATA_MASTER;
        }
     }
-
-    /* clear any interrupts pending on this channel */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
-            ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
-
-    /* start operations on this channel */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
-            (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
-             ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
+    if (bootverbose)
+       device_printf(dev, "ahci_reset devices=0x%b\n", ch->devices,
+                     "\20\4ATAPI_SLAVE\3ATAPI_MASTER\2ATA_SLAVE\1ATA_MASTER");
 }
 
 static void
@@ -798,7 +855,7 @@ ata_ahci_setup_fis(struct ata_ahci_cmd_tab *ctp, struct ata_request *request)
     bzero(ctp->cfis, 64);
     if (request->flags & ATA_R_ATAPI) {
        bzero(ctp->acmd, 32);
-       bcopy(request->u.atapi.ccb, ctp->acmd, 12);
+       bcopy(request->u.atapi.ccb, ctp->acmd, 16);
     }
     return ata_request2fis_h2d(request, &ctp->cfis[0]);
 }
index 4ab6eb1..3d24353 100644 (file)
@@ -24,7 +24,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/ata/atapi-cd.c,v 1.189 2006/06/28 15:04:10 sos Exp $
- * $DragonFly: src/sys/dev/disk/nata/atapi-cd.c,v 1.8 2007/07/23 19:26:09 dillon Exp $
+ * $DragonFly: src/sys/dev/disk/nata/atapi-cd.c,v 1.9 2007/11/20 09:25:21 hasso Exp $
  */
 
 #include "opt_ata.h"
@@ -1735,13 +1735,15 @@ static void
 acd_get_cap(device_t dev)
 {
     struct acd_softc *cdp = device_get_ivars(dev);
+    int8_t ccb[16] = { ATAPI_MODE_SENSE_BIG, 0, ATAPI_CDROM_CAP_PAGE,
+                      0, 0, 0, 0, sizeof(cdp->cap)>>8, sizeof(cdp->cap),
+                      0, 0, 0, 0, 0, 0, 0 };
     int count;
 
     /* get drive capabilities, some bugridden drives needs this repeated */
     for (count = 0 ; count < 5 ; count++) {
-       if (!acd_mode_sense(dev, ATAPI_CDROM_CAP_PAGE,
-                           (caddr_t)&cdp->cap, sizeof(cdp->cap)) &&
-                           cdp->cap.page_code == ATAPI_CDROM_CAP_PAGE) {
+       if (!ata_atapicmd(dev, ccb, (caddr_t)&cdp->cap, sizeof(cdp->cap),
+                         ATA_R_READ | ATA_R_QUIET, 5)) {
            cdp->cap.max_read_speed = ntohs(cdp->cap.max_read_speed);
            cdp->cap.cur_read_speed = ntohs(cdp->cap.cur_read_speed);
            cdp->cap.max_write_speed = ntohs(cdp->cap.max_write_speed);
@@ -1922,6 +1924,8 @@ acd_describe(device_t dev)
                kprintf("CD-R "); break;
            case MST_CDRW:
                kprintf("CD-RW "); break;
+           case MST_DVD:
+               kprintf("DVD "); break;
            case MST_DOOR_OPEN:
                kprintf("door open"); break;
            case MST_NO_DISC:
index 91e7a62..e93ed77 100644 (file)
@@ -24,7 +24,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/ata/atapi-cd.h,v 1.45 2006/01/05 21:27:19 sos Exp $
- * $DragonFly: src/sys/dev/disk/nata/atapi-cd.h,v 1.2 2007/06/03 03:44:14 dillon Exp $
+ * $DragonFly: src/sys/dev/disk/nata/atapi-cd.h,v 1.3 2007/11/20 09:25:21 hasso Exp $
  */
 
 #include <sys/param.h>
@@ -100,6 +100,7 @@ struct cappage {
 #define MST_CDROM               0x00
 #define MST_CDR                 0x10
 #define MST_CDRW                0x20
+#define MST_DVD                 0x40
 
 #define MST_NO_DISC             0x70
 #define MST_DOOR_OPEN           0x71