diff options
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 499 |
1 files changed, 467 insertions, 32 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index def3682f416a..be95fdb69726 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -87,11 +87,34 @@ static struct workqueue_struct *ata_wq; struct workqueue_struct *ata_aux_wq; +struct ata_force_param { + const char *name; + unsigned int cbl; + int spd_limit; + unsigned long xfer_mask; + unsigned int horkage_on; + unsigned int horkage_off; +}; + +struct ata_force_ent { + int port; + int device; + struct ata_force_param param; +}; + +static struct ata_force_ent *ata_force_tbl; +static int ata_force_tbl_size; + +static char ata_force_param_buf[PAGE_SIZE] __initdata; +/* param_buf is thrown away after initialization, disallow read */ +module_param_string(force, ata_force_param_buf, sizeof(ata_force_param_buf), 0); +MODULE_PARM_DESC(force, "Force ATA configurations including cable type, link speed and transfer mode (see Documentation/kernel-parameters.txt for details)"); + int atapi_enabled = 1; module_param(atapi_enabled, int, 0444); MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on)"); -int atapi_dmadir = 0; +static int atapi_dmadir = 0; module_param(atapi_dmadir, int, 0444); MODULE_PARM_DESC(atapi_dmadir, "Enable ATAPI DMADIR bridge support (0=off, 1=on)"); @@ -130,6 +153,217 @@ MODULE_VERSION(DRV_VERSION); /** + * ata_force_cbl - force cable type according to libata.force + * @ap: ATA port of interest + * + * Force cable type according to libata.force and whine about it. + * The last entry which has matching port number is used, so it + * can be specified as part of device force parameters. For + * example, both "a:40c,1.00:udma4" and "1.00:40c,udma4" have the + * same effect. + * + * LOCKING: + * EH context. + */ +void ata_force_cbl(struct ata_port *ap) +{ + int i; + + for (i = ata_force_tbl_size - 1; i >= 0; i--) { + const struct ata_force_ent *fe = &ata_force_tbl[i]; + + if (fe->port != -1 && fe->port != ap->print_id) + continue; + + if (fe->param.cbl == ATA_CBL_NONE) + continue; + + ap->cbl = fe->param.cbl; + ata_port_printk(ap, KERN_NOTICE, + "FORCE: cable set to %s\n", fe->param.name); + return; + } +} + +/** + * ata_force_spd_limit - force SATA spd limit according to libata.force + * @link: ATA link of interest + * + * Force SATA spd limit according to libata.force and whine about + * it. When only the port part is specified (e.g. 1:), the limit + * applies to all links connected to both the host link and all + * fan-out ports connected via PMP. If the device part is + * specified as 0 (e.g. 1.00:), it specifies the first fan-out + * link not the host link. Device number 15 always points to the + * host link whether PMP is attached or not. + * + * LOCKING: + * EH context. + */ +static void ata_force_spd_limit(struct ata_link *link) +{ + int linkno, i; + + if (ata_is_host_link(link)) + linkno = 15; + else + linkno = link->pmp; + + for (i = ata_force_tbl_size - 1; i >= 0; i--) { + const struct ata_force_ent *fe = &ata_force_tbl[i]; + + if (fe->port != -1 && fe->port != link->ap->print_id) + continue; + + if (fe->device != -1 && fe->device != linkno) + continue; + + if (!fe->param.spd_limit) + continue; + + link->hw_sata_spd_limit = (1 << fe->param.spd_limit) - 1; + ata_link_printk(link, KERN_NOTICE, + "FORCE: PHY spd limit set to %s\n", fe->param.name); + return; + } +} + +/** + * ata_force_xfermask - force xfermask according to libata.force + * @dev: ATA device of interest + * + * Force xfer_mask according to libata.force and whine about it. + * For consistency with link selection, device number 15 selects + * the first device connected to the host link. + * + * LOCKING: + * EH context. + */ +static void ata_force_xfermask(struct ata_device *dev) +{ + int devno = dev->link->pmp + dev->devno; + int alt_devno = devno; + int i; + + /* allow n.15 for the first device attached to host port */ + if (ata_is_host_link(dev->link) && devno == 0) + alt_devno = 15; + + for (i = ata_force_tbl_size - 1; i >= 0; i--) { + const struct ata_force_ent *fe = &ata_force_tbl[i]; + unsigned long pio_mask, mwdma_mask, udma_mask; + + if (fe->port != -1 && fe->port != dev->link->ap->print_id) + continue; + + if (fe->device != -1 && fe->device != devno && + fe->device != alt_devno) + continue; + + if (!fe->param.xfer_mask) + continue; + + ata_unpack_xfermask(fe->param.xfer_mask, + &pio_mask, &mwdma_mask, &udma_mask); + if (udma_mask) + dev->udma_mask = udma_mask; + else if (mwdma_mask) { + dev->udma_mask = 0; + dev->mwdma_mask = mwdma_mask; + } else { + dev->udma_mask = 0; + dev->mwdma_mask = 0; + dev->pio_mask = pio_mask; + } + + ata_dev_printk(dev, KERN_NOTICE, + "FORCE: xfer_mask set to %s\n", fe->param.name); + return; + } +} + +/** + * ata_force_horkage - force horkage according to libata.force + * @dev: ATA device of interest + * + * Force horkage according to libata.force and whine about it. + * For consistency with link selection, device number 15 selects + * the first device connected to the host link. + * + * LOCKING: + * EH context. + */ +static void ata_force_horkage(struct ata_device *dev) +{ + int devno = dev->link->pmp + dev->devno; + int alt_devno = devno; + int i; + + /* allow n.15 for the first device attached to host port */ + if (ata_is_host_link(dev->link) && devno == 0) + alt_devno = 15; + + for (i = 0; i < ata_force_tbl_size; i++) { + const struct ata_force_ent *fe = &ata_force_tbl[i]; + + if (fe->port != -1 && fe->port != dev->link->ap->print_id) + continue; + + if (fe->device != -1 && fe->device != devno && + fe->device != alt_devno) + continue; + + if (!(~dev->horkage & fe->param.horkage_on) && + !(dev->horkage & fe->param.horkage_off)) + continue; + + dev->horkage |= fe->param.horkage_on; + dev->horkage &= ~fe->param.horkage_off; + + ata_dev_printk(dev, KERN_NOTICE, + "FORCE: horkage modified (%s)\n", fe->param.name); + } +} + +/** + * atapi_cmd_type - Determine ATAPI command type from SCSI opcode + * @opcode: SCSI opcode + * + * Determine ATAPI command type from @opcode. + * + * LOCKING: + * None. + * + * RETURNS: + * ATAPI_{READ|WRITE|READ_CD|PASS_THRU|MISC} + */ +int atapi_cmd_type(u8 opcode) +{ + switch (opcode) { + case GPCMD_READ_10: + case GPCMD_READ_12: + return ATAPI_READ; + + case GPCMD_WRITE_10: + case GPCMD_WRITE_12: + case GPCMD_WRITE_AND_VERIFY_10: + return ATAPI_WRITE; + + case GPCMD_READ_CD: + case GPCMD_READ_CD_MSF: + return ATAPI_READ_CD; + + case ATA_16: + case ATA_12: + if (atapi_passthru16) + return ATAPI_PASS_THRU; + /* fall thru */ + default: + return ATAPI_MISC; + } +} + +/** * ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure * @tf: Taskfile to convert * @pmp: Port multiplier port @@ -776,7 +1010,7 @@ static void ata_dev_disable_pm(struct ata_device *dev) void ata_lpm_schedule(struct ata_port *ap, enum link_pm policy) { ap->pm_policy = policy; - ap->link.eh_info.action |= ATA_EHI_LPM; + ap->link.eh_info.action |= ATA_EH_LPM; ap->link.eh_info.flags |= ATA_EHI_NO_AUTOPSY; ata_port_schedule_eh(ap); } @@ -1220,12 +1454,12 @@ static int ata_hpa_resize(struct ata_device *dev) /* read native max address */ rc = ata_read_native_max_address(dev, &native_sectors); if (rc) { - /* If HPA isn't going to be unlocked, skip HPA - * resizing from the next try. + /* If device aborted the command or HPA isn't going to + * be unlocked, skip HPA resizing. */ - if (!ata_ignore_hpa) { + if (rc == -EACCES || !ata_ignore_hpa) { ata_dev_printk(dev, KERN_WARNING, "HPA support seems " - "broken, will skip HPA handling\n"); + "broken, skipping HPA handling\n"); dev->horkage |= ATA_HORKAGE_BROKEN_HPA; /* we can continue if device aborted the command */ @@ -1524,7 +1758,7 @@ void ata_port_flush_task(struct ata_port *ap) cancel_rearming_delayed_work(&ap->port_task); if (ata_msg_ctl(ap)) - ata_port_printk(ap, KERN_DEBUG, "%s: EXIT\n", __FUNCTION__); + ata_port_printk(ap, KERN_DEBUG, "%s: EXIT\n", __func__); } static void ata_qc_complete_internal(struct ata_queued_cmd *qc) @@ -1861,7 +2095,7 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, int rc; if (ata_msg_ctl(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __FUNCTION__); + ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __func__); ata_dev_select(ap, dev->devno, 1, 1); /* select device 0/1 */ retry: @@ -1896,24 +2130,34 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, id, sizeof(id[0]) * ATA_ID_WORDS, 0); if (err_mask) { if (err_mask & AC_ERR_NODEV_HINT) { - DPRINTK("ata%u.%d: NODEV after polling detection\n", - ap->print_id, dev->devno); + ata_dev_printk(dev, KERN_DEBUG, + "NODEV after polling detection\n"); return -ENOENT; } - /* Device or controller might have reported the wrong - * device class. Give a shot at the other IDENTIFY if - * the current one is aborted by the device. - */ - if (may_fallback && - (err_mask == AC_ERR_DEV) && (tf.feature & ATA_ABORTED)) { - may_fallback = 0; + if ((err_mask == AC_ERR_DEV) && (tf.feature & ATA_ABORTED)) { + /* Device or controller might have reported + * the wrong device class. Give a shot at the + * other IDENTIFY if the current one is + * aborted by the device. + */ + if (may_fallback) { + may_fallback = 0; - if (class == ATA_DEV_ATA) - class = ATA_DEV_ATAPI; - else - class = ATA_DEV_ATA; - goto retry; + if (class == ATA_DEV_ATA) + class = ATA_DEV_ATAPI; + else + class = ATA_DEV_ATA; + goto retry; + } + + /* Control reaches here iff the device aborted + * both flavors of IDENTIFYs which happens + * sometimes with phantom devices. + */ + ata_dev_printk(dev, KERN_DEBUG, + "both IDENTIFYs aborted, assuming NODEV\n"); + return -ENOENT; } rc = -EIO; @@ -2058,15 +2302,16 @@ int ata_dev_configure(struct ata_device *dev) if (!ata_dev_enabled(dev) && ata_msg_info(ap)) { ata_dev_printk(dev, KERN_INFO, "%s: ENTER/EXIT -- nodev\n", - __FUNCTION__); + __func__); return 0; } if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __FUNCTION__); + ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __func__); /* set horkage */ dev->horkage |= ata_dev_blacklisted(dev); + ata_force_horkage(dev); /* let ACPI work its magic */ rc = ata_acpi_on_devcfg(dev); @@ -2083,7 +2328,7 @@ int ata_dev_configure(struct ata_device *dev) ata_dev_printk(dev, KERN_DEBUG, "%s: cfg 49:%04x 82:%04x 83:%04x 84:%04x " "85:%04x 86:%04x 87:%04x 88:%04x\n", - __FUNCTION__, + __func__, id[49], id[82], id[83], id[84], id[85], id[86], id[87], id[88]); @@ -2200,6 +2445,7 @@ int ata_dev_configure(struct ata_device *dev) else if (dev->class == ATA_DEV_ATAPI) { const char *cdb_intr_string = ""; const char *atapi_an_string = ""; + const char *dma_dir_string = ""; u32 sntf; rc = atapi_cdb_len(id); @@ -2240,13 +2486,19 @@ int ata_dev_configure(struct ata_device *dev) cdb_intr_string = ", CDB intr"; } + if (atapi_dmadir || atapi_id_dmadir(dev->id)) { + dev->flags |= ATA_DFLAG_DMADIR; + dma_dir_string = ", DMADIR"; + } + /* print device info to dmesg */ if (ata_msg_drv(ap) && print_info) ata_dev_printk(dev, KERN_INFO, - "ATAPI: %s, %s, max %s%s%s\n", + "ATAPI: %s, %s, max %s%s%s%s\n", modelbuf, fwrevbuf, ata_mode_string(xfer_mask), - cdb_intr_string, atapi_an_string); + cdb_intr_string, atapi_an_string, + dma_dir_string); } /* determine max_sectors */ @@ -2308,13 +2560,13 @@ int ata_dev_configure(struct ata_device *dev) if (ata_msg_probe(ap)) ata_dev_printk(dev, KERN_DEBUG, "%s: EXIT, drv_stat = 0x%x\n", - __FUNCTION__, ata_chk_status(ap)); + __func__, ata_chk_status(ap)); return 0; err_out_nosup: if (ata_msg_probe(ap)) ata_dev_printk(dev, KERN_DEBUG, - "%s: EXIT, err\n", __FUNCTION__); + "%s: EXIT, err\n", __func__); return rc; } @@ -2446,7 +2698,7 @@ int ata_bus_probe(struct ata_port *ap) specific sequence bass-ackwards so that PDIAG- is released by the slave device */ - ata_link_for_each_dev(dev, &ap->link) { + ata_link_for_each_dev_reverse(dev, &ap->link) { if (tries[dev->devno]) dev->class = classes[dev->devno]; @@ -3150,6 +3402,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) mode_mask = ATA_DMA_MASK_CFA; ata_dev_xfermask(dev); + ata_force_xfermask(dev); pio_mask = ata_pack_xfermask(dev->pio_mask, 0, 0); dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask); @@ -6497,7 +6750,8 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) */ int sata_link_init_spd(struct ata_link *link) { - u32 scontrol, spd; + u32 scontrol; + u8 spd; int rc; rc = sata_scr_read(link, SCR_CONTROL, &scontrol); @@ -6508,6 +6762,8 @@ int sata_link_init_spd(struct ata_link *link) if (spd) link->hw_sata_spd_limit &= (1 << spd) - 1; + ata_force_spd_limit(link); + link->sata_spd_limit = link->hw_sata_spd_limit; return 0; @@ -7168,7 +7424,7 @@ void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t mesg) pci_save_state(pdev); pci_disable_device(pdev); - if (mesg.event == PM_EVENT_SUSPEND) + if (mesg.event & PM_EVENT_SLEEP) pci_set_power_state(pdev, PCI_D3hot); } @@ -7218,10 +7474,187 @@ int ata_pci_device_resume(struct pci_dev *pdev) #endif /* CONFIG_PCI */ +static int __init ata_parse_force_one(char **cur, + struct ata_force_ent *force_ent, + const char **reason) +{ + /* FIXME: Currently, there's no way to tag init const data and + * using __initdata causes build failure on some versions of + * gcc. Once __initdataconst is implemented, add const to the + * following structure. + */ + static struct ata_force_param force_tbl[] __initdata = { + { "40c", .cbl = ATA_CBL_PATA40 }, + { "80c", .cbl = ATA_CBL_PATA80 }, + { "short40c", .cbl = ATA_CBL_PATA40_SHORT }, + { "unk", .cbl = ATA_CBL_PATA_UNK }, + { "ign", .cbl = ATA_CBL_PATA_IGN }, + { "sata", .cbl = ATA_CBL_SATA }, + { "1.5Gbps", .spd_limit = 1 }, + { "3.0Gbps", .spd_limit = 2 }, + { "noncq", .horkage_on = ATA_HORKAGE_NONCQ }, + { "ncq", .horkage_off = ATA_HORKAGE_NONCQ }, + { "pio0", .xfer_mask = 1 << (ATA_SHIFT_PIO + 0) }, + { "pio1", .xfer_mask = 1 << (ATA_SHIFT_PIO + 1) }, + { "pio2", .xfer_mask = 1 << (ATA_SHIFT_PIO + 2) }, + { "pio3", .xfer_mask = 1 << (ATA_SHIFT_PIO + 3) }, + { "pio4", .xfer_mask = 1 << (ATA_SHIFT_PIO + 4) }, + { "pio5", .xfer_mask = 1 << (ATA_SHIFT_PIO + 5) }, + { "pio6", .xfer_mask = 1 << (ATA_SHIFT_PIO + 6) }, + { "mwdma0", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 0) }, + { "mwdma1", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 1) }, + { "mwdma2", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 2) }, + { "mwdma3", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 3) }, + { "mwdma4", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 4) }, + { "udma0", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 0) }, + { "udma16", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 0) }, + { "udma/16", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 0) }, + { "udma1", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 1) }, + { "udma25", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 1) }, + { "udma/25", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 1) }, + { "udma2", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 2) }, + { "udma33", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 2) }, + { "udma/33", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 2) }, + { "udma3", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 3) }, + { "udma44", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 3) }, + { "udma/44", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 3) }, + { "udma4", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 4) }, + { "udma66", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 4) }, + { "udma/66", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 4) }, + { "udma5", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 5) }, + { "udma100", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 5) }, + { "udma/100", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 5) }, + { "udma6", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 6) }, + { "udma133", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 6) }, + { "udma/133", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 6) }, + { "udma7", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 7) }, + }; + char *start = *cur, *p = *cur; + char *id, *val, *endp; + const struct ata_force_param *match_fp = NULL; + int nr_matches = 0, i; + + /* find where this param ends and update *cur */ + while (*p != '\0' && *p != ',') + p++; + + if (*p == '\0') + *cur = p; + else + *cur = p + 1; + + *p = '\0'; + + /* parse */ + p = strchr(start, ':'); + if (!p) { + val = strstrip(start); + goto parse_val; + } + *p = '\0'; + + id = strstrip(start); + val = strstrip(p + 1); + + /* parse id */ + p = strchr(id, '.'); + if (p) { + *p++ = '\0'; + force_ent->device = simple_strtoul(p, &endp, 10); + if (p == endp || *endp != '\0') { + *reason = "invalid device"; + return -EINVAL; + } + } + + force_ent->port = simple_strtoul(id, &endp, 10); + if (p == endp || *endp != '\0') { + *reason = "invalid port/link"; + return -EINVAL; + } + + parse_val: + /* parse val, allow shortcuts so that both 1.5 and 1.5Gbps work */ + for (i = 0; i < ARRAY_SIZE(force_tbl); i++) { + const struct ata_force_param *fp = &force_tbl[i]; + + if (strncasecmp(val, fp->name, strlen(val))) + continue; + + nr_matches++; + match_fp = fp; + + if (strcasecmp(val, fp->name) == 0) { + nr_matches = 1; + break; + } + } + + if (!nr_matches) { + *reason = "unknown value"; + return -EINVAL; + } + if (nr_matches > 1) { + *reason = "ambigious value"; + return -EINVAL; + } + + force_ent->param = *match_fp; + + return 0; +} + +static void __init ata_parse_force_param(void) +{ + int idx = 0, size = 1; + int last_port = -1, last_device = -1; + char *p, *cur, *next; + + /* calculate maximum number of params and allocate force_tbl */ + for (p = ata_force_param_buf; *p; p++) + if (*p == ',') + size++; + + ata_force_tbl = kzalloc(sizeof(ata_force_tbl[0]) * size, GFP_KERNEL); + if (!ata_force_tbl) { + printk(KERN_WARNING "ata: failed to extend force table, " + "libata.force ignored\n"); + return; + } + + /* parse and populate the table */ + for (cur = ata_force_param_buf; *cur != '\0'; cur = next) { + const char *reason = ""; + struct ata_force_ent te = { .port = -1, .device = -1 }; + + next = cur; + if (ata_parse_force_one(&next, &te, &reason)) { + printk(KERN_WARNING "ata: failed to parse force " + "parameter \"%s\" (%s)\n", + cur, reason); + continue; + } + + if (te.port == -1) { + te.port = last_port; + te.device = last_device; + } + + ata_force_tbl[idx++] = te; + + last_port = te.port; + last_device = te.device; + } + + ata_force_tbl_size = idx; +} static int __init ata_init(void) { ata_probe_timeout *= HZ; + + ata_parse_force_param(); + ata_wq = create_workqueue("ata"); if (!ata_wq) return -ENOMEM; @@ -7238,6 +7671,7 @@ static int __init ata_init(void) static void __exit ata_exit(void) { + kfree(ata_force_tbl); destroy_workqueue(ata_wq); destroy_workqueue(ata_aux_wq); } @@ -7378,6 +7812,7 @@ EXPORT_SYMBOL_GPL(ata_tf_read); EXPORT_SYMBOL_GPL(ata_noop_dev_select); EXPORT_SYMBOL_GPL(ata_std_dev_select); EXPORT_SYMBOL_GPL(sata_print_link_status); +EXPORT_SYMBOL_GPL(atapi_cmd_type); EXPORT_SYMBOL_GPL(ata_tf_to_fis); EXPORT_SYMBOL_GPL(ata_tf_from_fis); EXPORT_SYMBOL_GPL(ata_pack_xfermask); |