diff options
-rw-r--r-- | Documentation/DocBook/libata.tmpl | 47 | ||||
-rw-r--r-- | drivers/scsi/libata-core.c | 31 | ||||
-rw-r--r-- | include/linux/libata.h | 4 |
3 files changed, 76 insertions, 6 deletions
diff --git a/Documentation/DocBook/libata.tmpl b/Documentation/DocBook/libata.tmpl index d260d92089a..5bcbb6ee3bc 100644 --- a/Documentation/DocBook/libata.tmpl +++ b/Documentation/DocBook/libata.tmpl @@ -120,14 +120,27 @@ void (*dev_config) (struct ata_port *, struct ata_device *); <programlisting> void (*set_piomode) (struct ata_port *, struct ata_device *); void (*set_dmamode) (struct ata_port *, struct ata_device *); -void (*post_set_mode) (struct ata_port *ap); +void (*post_set_mode) (struct ata_port *); +unsigned int (*mode_filter) (struct ata_port *, struct ata_device *, unsigned int); </programlisting> <para> Hooks called prior to the issue of SET FEATURES - XFER MODE - command. dev->pio_mode is guaranteed to be valid when - ->set_piomode() is called, and dev->dma_mode is guaranteed to be - valid when ->set_dmamode() is called. ->post_set_mode() is + command. The optional ->mode_filter() hook is called when libata + has built a mask of the possible modes. This is passed to the + ->mode_filter() function which should return a mask of valid modes + after filtering those unsuitable due to hardware limits. It is not + valid to use this interface to add modes. + </para> + <para> + dev->pio_mode and dev->dma_mode are guaranteed to be valid when + ->set_piomode() and when ->set_dmamode() is called. The timings for + any other drive sharing the cable will also be valid at this point. + That is the library records the decisions for the modes of each + drive on a channel before it attempts to set any of them. + </para> + <para> + ->post_set_mode() is called unconditionally, after the SET FEATURES - XFER MODE command completes successfully. </para> @@ -230,6 +243,32 @@ void (*dev_select)(struct ata_port *ap, unsigned int device); </sect2> + <sect2><title>Private tuning method</title> + <programlisting> +void (*set_mode) (struct ata_port *ap); + </programlisting> + + <para> + By default libata performs drive and controller tuning in + accordance with the ATA timing rules and also applies blacklists + and cable limits. Some controllers need special handling and have + custom tuning rules, typically raid controllers that use ATA + commands but do not actually do drive timing. + </para> + + <warning> + <para> + This hook should not be used to replace the standard controller + tuning logic when a controller has quirks. Replacing the default + tuning logic in that case would bypass handling for drive and + bridge quirks that may be important to data reliability. If a + controller needs to filter the mode selection it should use the + mode_filter hook instead. + </para> + </warning> + + </sect2> + <sect2><title>Reset ATA bus</title> <programlisting> void (*phy_reset) (struct ata_port *ap); diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 41659448a20..b1ddf40533b 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -1818,7 +1818,7 @@ static void ata_host_set_dma(struct ata_port *ap) */ static void ata_set_mode(struct ata_port *ap) { - int i, rc; + int i, rc, used_dma = 0; /* step 1: calculate xfer_mask */ for (i = 0; i < ATA_MAX_DEVICES; i++) { @@ -1836,6 +1836,9 @@ static void ata_set_mode(struct ata_port *ap) dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask); dev->pio_mode = ata_xfer_mask2mode(pio_mask); dev->dma_mode = ata_xfer_mask2mode(dma_mask); + + if (dev->dma_mode) + used_dma = 1; } /* step 2: always set host PIO timings */ @@ -1857,6 +1860,17 @@ static void ata_set_mode(struct ata_port *ap) goto err_out; } + /* + * Record simplex status. If we selected DMA then the other + * host channels are not permitted to do so. + */ + + if (used_dma && (ap->host_set->flags & ATA_HOST_SIMPLEX)) + ap->host_set->simplex_claimed = 1; + + /* + * Chip specific finalisation + */ if (ap->ops->post_set_mode) ap->ops->post_set_mode(ap); @@ -2646,13 +2660,14 @@ static int ata_dma_blacklisted(const struct ata_device *dev) */ static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev) { + struct ata_host_set *hs = ap->host_set; unsigned long xfer_mask; int i; xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, ap->udma_mask); - /* use port-wide xfermask for now */ + /* FIXME: Use port-wide xfermask for now */ for (i = 0; i < ATA_MAX_DEVICES; i++) { struct ata_device *d = &ap->device[i]; if (!ata_dev_present(d)) @@ -2662,12 +2677,23 @@ static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev) xfer_mask &= ata_id_xfermask(d->id); if (ata_dma_blacklisted(d)) xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); + /* Apply cable rule here. Don't apply it early because when + we handle hot plug the cable type can itself change */ + if (ap->cbl == ATA_CBL_PATA40) + xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); } if (ata_dma_blacklisted(dev)) printk(KERN_WARNING "ata%u: dev %u is on DMA blacklist, " "disabling DMA\n", ap->id, dev->devno); + if (hs->flags & ATA_HOST_SIMPLEX) { + if (hs->simplex_claimed) + xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); + } + if (ap->ops->mode_filter) + xfer_mask = ap->ops->mode_filter(ap, dev, xfer_mask); + ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask, &dev->udma_mask); } @@ -4531,6 +4557,7 @@ int ata_device_add(const struct ata_probe_ent *ent) host_set->mmio_base = ent->mmio_base; host_set->private_data = ent->private_data; host_set->ops = ent->port_ops; + host_set->flags = ent->host_set_flags; /* register each port bound to this device */ for (i = 0; i < ent->n_ports; i++) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 6a9316cbb70..0d61357604d 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -294,6 +294,9 @@ struct ata_host_set { unsigned int n_ports; void *private_data; const struct ata_port_operations *ops; + unsigned long flags; + int simplex_claimed; /* Keep seperate in case we + ever need to do this locked */ struct ata_port * ports[0]; }; @@ -423,6 +426,7 @@ struct ata_port_operations { void (*set_piomode) (struct ata_port *, struct ata_device *); void (*set_dmamode) (struct ata_port *, struct ata_device *); + unsigned long (*mode_filter) (const struct ata_port *, struct ata_device *, unsigned long); void (*tf_load) (struct ata_port *ap, const struct ata_taskfile *tf); void (*tf_read) (struct ata_port *ap, struct ata_taskfile *tf); |