diff options
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 111 |
1 files changed, 80 insertions, 31 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 9e7f55b..d3e78d9 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -670,29 +670,49 @@ static unsigned int ata_devchk(struct ata_port *ap, unsigned int device) * None. * * RETURNS: - * Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, or %ATA_DEV_UNKNOWN - * the event of failure. + * Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, %ATA_DEV_PMP or + * %ATA_DEV_UNKNOWN the event of failure. */ - unsigned int ata_dev_classify(const struct ata_taskfile *tf) { /* Apple's open source Darwin code hints that some devices only * put a proper signature into the LBA mid/high registers, * So, we only check those. It's sufficient for uniqueness. + * + * ATA/ATAPI-7 (d1532v1r1: Feb. 19, 2003) specified separate + * signatures for ATA and ATAPI devices attached on SerialATA, + * 0x3c/0xc3 and 0x69/0x96 respectively. However, SerialATA + * spec has never mentioned about using different signatures + * for ATA/ATAPI devices. Then, Serial ATA II: Port + * Multiplier specification began to use 0x69/0x96 to identify + * port multpliers and 0x3c/0xc3 to identify SEMB device. + * ATA/ATAPI-7 dropped descriptions about 0x3c/0xc3 and + * 0x69/0x96 shortly and described them as reserved for + * SerialATA. + * + * We follow the current spec and consider that 0x69/0x96 + * identifies a port multiplier and 0x3c/0xc3 a SEMB device. */ - - if (((tf->lbam == 0) && (tf->lbah == 0)) || - ((tf->lbam == 0x3c) && (tf->lbah == 0xc3))) { + if ((tf->lbam == 0) && (tf->lbah == 0)) { DPRINTK("found ATA device by sig\n"); return ATA_DEV_ATA; } - if (((tf->lbam == 0x14) && (tf->lbah == 0xeb)) || - ((tf->lbam == 0x69) && (tf->lbah == 0x96))) { + if ((tf->lbam == 0x14) && (tf->lbah == 0xeb)) { DPRINTK("found ATAPI device by sig\n"); return ATA_DEV_ATAPI; } + if ((tf->lbam == 0x69) && (tf->lbah == 0x96)) { + DPRINTK("found PMP device by sig\n"); + return ATA_DEV_PMP; + } + + if ((tf->lbam == 0x3c) && (tf->lbah == 0xc3)) { + printk("ata: SEMB device ignored\n"); + return ATA_DEV_SEMB_UNSUP; /* not yet */ + } + DPRINTK("unknown device\n"); return ATA_DEV_UNKNOWN; } @@ -3426,6 +3446,12 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline) (link->flags & ATA_LFLAG_HRST_TO_RESUME)) ehc->i.action |= ATA_EH_HARDRESET; + /* Some PMPs don't work with only SRST, force hardreset if PMP + * is supported. + */ + if (ap->flags & ATA_FLAG_PMP) + ehc->i.action |= ATA_EH_HARDRESET; + /* if we're about to do hardreset, nothing more to do */ if (ehc->i.action & ATA_EH_HARDRESET) return 0; @@ -3616,6 +3642,16 @@ int sata_std_hardreset(struct ata_link *link, unsigned int *class, /* wait a while before checking status, see SRST for more info */ msleep(150); + /* If PMP is supported, we have to do follow-up SRST. Note + * that some PMPs don't send D2H Reg FIS after hardreset at + * all if the first port is empty. Wait for it just for a + * second and request follow-up SRST. + */ + if (ap->flags & ATA_FLAG_PMP) { + ata_wait_ready(ap, jiffies + HZ); + return -EAGAIN; + } + rc = ata_wait_ready(ap, deadline); /* link occupied, -ENODEV too is an error */ if (rc) { @@ -5966,22 +6002,26 @@ int sata_scr_valid(struct ata_link *link) * @val: Place to store read value * * Read SCR register @reg of @link into *@val. This function is - * guaranteed to succeed if the cable type of the port is SATA - * and the port implements ->scr_read. + * guaranteed to succeed if @link is ap->link, the cable type of + * the port is SATA and the port implements ->scr_read. * * LOCKING: - * None. + * None if @link is ap->link. Kernel thread context otherwise. * * RETURNS: * 0 on success, negative errno on failure. */ int sata_scr_read(struct ata_link *link, int reg, u32 *val) { - struct ata_port *ap = link->ap; + if (ata_is_host_link(link)) { + struct ata_port *ap = link->ap; - if (sata_scr_valid(link)) - return ap->ops->scr_read(ap, reg, val); - return -EOPNOTSUPP; + if (sata_scr_valid(link)) + return ap->ops->scr_read(ap, reg, val); + return -EOPNOTSUPP; + } + + return sata_pmp_scr_read(link, reg, val); } /** @@ -5991,22 +6031,26 @@ int sata_scr_read(struct ata_link *link, int reg, u32 *val) * @val: value to write * * Write @val to SCR register @reg of @link. This function is - * guaranteed to succeed if the cable type of the port is SATA - * and the port implements ->scr_read. + * guaranteed to succeed if @link is ap->link, the cable type of + * the port is SATA and the port implements ->scr_read. * * LOCKING: - * None. + * None if @link is ap->link. Kernel thread context otherwise. * * RETURNS: * 0 on success, negative errno on failure. */ int sata_scr_write(struct ata_link *link, int reg, u32 val) { - struct ata_port *ap = link->ap; + if (ata_is_host_link(link)) { + struct ata_port *ap = link->ap; - if (sata_scr_valid(link)) - return ap->ops->scr_write(ap, reg, val); - return -EOPNOTSUPP; + if (sata_scr_valid(link)) + return ap->ops->scr_write(ap, reg, val); + return -EOPNOTSUPP; + } + + return sata_pmp_scr_write(link, reg, val); } /** @@ -6019,23 +6063,27 @@ int sata_scr_write(struct ata_link *link, int reg, u32 val) * function performs flush after writing to the register. * * LOCKING: - * None. + * None if @link is ap->link. Kernel thread context otherwise. * * RETURNS: * 0 on success, negative errno on failure. */ int sata_scr_write_flush(struct ata_link *link, int reg, u32 val) { - struct ata_port *ap = link->ap; - int rc; + if (ata_is_host_link(link)) { + struct ata_port *ap = link->ap; + int rc; - if (sata_scr_valid(link)) { - rc = ap->ops->scr_write(ap, reg, val); - if (rc == 0) - rc = ap->ops->scr_read(ap, reg, &val); - return rc; + if (sata_scr_valid(link)) { + rc = ap->ops->scr_write(ap, reg, val); + if (rc == 0) + rc = ap->ops->scr_read(ap, reg, &val); + return rc; + } + return -EOPNOTSUPP; } - return -EOPNOTSUPP; + + return sata_pmp_scr_write(link, reg, val); } /** @@ -6424,6 +6472,7 @@ static void ata_host_release(struct device *gendev, void *res) if (ap->scsi_host) scsi_host_put(ap->scsi_host); + kfree(ap->pmp_link); kfree(ap); host->ports[i] = NULL; } |