diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata/pata_pcmcia.c | 34 |
1 files changed, 34 insertions, 0 deletions
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 820332a7ec7..171da0aeb65 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -54,6 +54,39 @@ struct ata_pcmcia_info { dev_node_t node; }; +/** + * pcmcia_set_mode - PCMCIA specific mode setup + * @ap: Port + * @r_failed_dev: Return pointer for failed device + * + * Perform the tuning and setup of the devices and timings, which + * for PCMCIA is the same as any other controller. We wrap it however + * as we need to spot hardware with incorrect or missing master/slave + * decode, which alas is embarrassingly common in the PC world + */ + +static int pcmcia_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) +{ + struct ata_device *master = &ap->device[0]; + struct ata_device *slave = &ap->device[1]; + + if (!ata_dev_enabled(master) || !ata_dev_enabled(slave)) + return ata_do_set_mode(ap, r_failed_dev); + + if (memcmp(master->id + ATA_ID_FW_REV, slave->id + ATA_ID_FW_REV, + ATA_ID_FW_REV_LEN + ATA_ID_PROD_LEN) == 0) + { + /* Suspicious match, but could be two cards from + the same vendor - check serial */ + if (memcmp(master->id + ATA_ID_SERNO, slave->id + ATA_ID_SERNO, + ATA_ID_SERNO_LEN) == 0 && master->id[ATA_ID_SERNO] >> 8) { + ata_dev_printk(slave, KERN_WARNING, "is a ghost device, ignoring.\n"); + ata_dev_disable(slave); + } + } + return ata_do_set_mode(ap, r_failed_dev); +} + static struct scsi_host_template pcmcia_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -73,6 +106,7 @@ static struct scsi_host_template pcmcia_sht = { }; static struct ata_port_operations pcmcia_port_ops = { + .set_mode = pcmcia_set_mode, .port_disable = ata_port_disable, .tf_load = ata_tf_load, .tf_read = ata_tf_read, |