diff options
Diffstat (limited to 'drivers/ata/libata-pmp.c')
| -rw-r--r-- | drivers/ata/libata-pmp.c | 266 |
1 files changed, 175 insertions, 91 deletions
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 51f0ffb78cb..7ccc084bf1d 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -8,8 +8,11 @@ */ #include <linux/kernel.h> +#include <linux/export.h> #include <linux/libata.h> +#include <linux/slab.h> #include "libata.h" +#include "libata-transport.h" const struct ata_port_operations sata_pmp_port_ops = { .inherits = &sata_port_ops, @@ -145,8 +148,8 @@ int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *r_val) err_mask = sata_pmp_read(link, reg, r_val); if (err_mask) { - ata_link_printk(link, KERN_WARNING, "failed to read SCR %d " - "(Emask=0x%x)\n", reg, err_mask); + ata_link_warn(link, "failed to read SCR %d (Emask=0x%x)\n", + reg, err_mask); return -EIO; } return 0; @@ -176,14 +179,35 @@ int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val) err_mask = sata_pmp_write(link, reg, val); if (err_mask) { - ata_link_printk(link, KERN_WARNING, "failed to write SCR %d " - "(Emask=0x%x)\n", reg, err_mask); + ata_link_warn(link, "failed to write SCR %d (Emask=0x%x)\n", + reg, err_mask); return -EIO; } return 0; } /** + * sata_pmp_set_lpm - configure LPM for a PMP link + * @link: PMP link to configure LPM for + * @policy: target LPM policy + * @hints: LPM hints + * + * Configure LPM for @link. This function will contain any PMP + * specific workarounds if necessary. + * + * LOCKING: + * EH context. + * + * RETURNS: + * 0 on success, -errno on failure. + */ +int sata_pmp_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + unsigned hints) +{ + return sata_link_scr_lpm(link, policy, true); +} + +/** * sata_pmp_read_gscr - read GSCR block of SATA PMP * @dev: PMP device * @gscr: buffer to read GSCR block into @@ -208,8 +232,8 @@ static int sata_pmp_read_gscr(struct ata_device *dev, u32 *gscr) err_mask = sata_pmp_read(dev->link, reg, &gscr[reg]); if (err_mask) { - ata_dev_printk(dev, KERN_ERR, "failed to read PMP " - "GSCR[%d] (Emask=0x%x)\n", reg, err_mask); + ata_dev_err(dev, "failed to read PMP GSCR[%d] (Emask=0x%x)\n", + reg, err_mask); return -EIO; } } @@ -230,10 +254,14 @@ static const char *sata_pmp_spec_rev_str(const u32 *gscr) return "<unknown>"; } +#define PMP_GSCR_SII_POL 129 + static int sata_pmp_configure(struct ata_device *dev, int print_info) { struct ata_port *ap = dev->link->ap; u32 *gscr = dev->gscr; + u16 vendor = sata_pmp_gscr_vendor(gscr); + u16 devid = sata_pmp_gscr_devid(gscr); unsigned int err_mask = 0; const char *reason; int nr_ports, rc; @@ -259,36 +287,57 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) goto fail; } + /* Disable sending Early R_OK. + * With "cached read" HDD testing and multiple ports busy on a SATA + * host controller, 3x26 PMP will very rarely drop a deferred + * R_OK that was intended for the host. Symptom will be all + * 5 drives under test will timeout, get reset, and recover. + */ + if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { + u32 reg; + + err_mask = sata_pmp_read(&ap->link, PMP_GSCR_SII_POL, ®); + if (err_mask) { + rc = -EIO; + reason = "failed to read Sil3x26 Private Register"; + goto fail; + } + reg &= ~0x1; + err_mask = sata_pmp_write(&ap->link, PMP_GSCR_SII_POL, reg); + if (err_mask) { + rc = -EIO; + reason = "failed to write Sil3x26 Private Register"; + goto fail; + } + } + if (print_info) { - ata_dev_printk(dev, KERN_INFO, "Port Multiplier %s, " - "0x%04x:0x%04x r%d, %d ports, feat 0x%x/0x%x\n", - sata_pmp_spec_rev_str(gscr), - sata_pmp_gscr_vendor(gscr), - sata_pmp_gscr_devid(gscr), - sata_pmp_gscr_rev(gscr), - nr_ports, gscr[SATA_PMP_GSCR_FEAT_EN], - gscr[SATA_PMP_GSCR_FEAT]); + ata_dev_info(dev, "Port Multiplier %s, " + "0x%04x:0x%04x r%d, %d ports, feat 0x%x/0x%x\n", + sata_pmp_spec_rev_str(gscr), vendor, devid, + sata_pmp_gscr_rev(gscr), + nr_ports, gscr[SATA_PMP_GSCR_FEAT_EN], + gscr[SATA_PMP_GSCR_FEAT]); if (!(dev->flags & ATA_DFLAG_AN)) - ata_dev_printk(dev, KERN_INFO, + ata_dev_info(dev, "Asynchronous notification not supported, " - "hotplug won't\n work on fan-out " - "ports. Use warm-plug instead.\n"); + "hotplug won't work on fan-out ports. Use warm-plug instead.\n"); } return 0; fail: - ata_dev_printk(dev, KERN_ERR, - "failed to configure Port Multiplier (%s, Emask=0x%x)\n", - reason, err_mask); + ata_dev_err(dev, + "failed to configure Port Multiplier (%s, Emask=0x%x)\n", + reason, err_mask); return rc; } -static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) +static int sata_pmp_init_links (struct ata_port *ap, int nr_ports) { struct ata_link *pmp_link = ap->pmp_link; - int i; + int i, err; if (!pmp_link) { pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS, @@ -300,6 +349,13 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) ata_link_init(ap, &pmp_link[i], i); ap->pmp_link = pmp_link; + + for (i = 0; i < SATA_PMP_MAX_PORTS; i++) { + err = ata_tlink_add(&pmp_link[i]); + if (err) { + goto err_tlink; + } + } } for (i = 0; i < nr_ports; i++) { @@ -312,6 +368,12 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) } return 0; + err_tlink: + while (--i >= 0) + ata_tlink_delete(&pmp_link[i]); + kfree(pmp_link); + ap->pmp_link = NULL; + return err; } static void sata_pmp_quirks(struct ata_port *ap) @@ -321,11 +383,15 @@ static void sata_pmp_quirks(struct ata_port *ap) u16 devid = sata_pmp_gscr_devid(gscr); struct ata_link *link; - if (vendor == 0x1095 && devid == 0x3726) { - /* sil3726 quirks */ + if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { + /* sil3x26 quirks */ ata_for_each_link(link, ap, EDGE) { - /* Class code report is unreliable and SRST - * times out under certain configurations. + /* link reports offline after LPM */ + link->flags |= ATA_LFLAG_NO_LPM; + + /* + * Class code report is unreliable and SRST times + * out under certain configurations. */ if (link->pmp < 5) link->flags |= ATA_LFLAG_NO_SRST | @@ -337,20 +403,23 @@ static void sata_pmp_quirks(struct ata_port *ap) ATA_LFLAG_ASSUME_SEMB; } } else if (vendor == 0x1095 && devid == 0x4723) { - /* sil4723 quirks */ - ata_for_each_link(link, ap, EDGE) { - /* class code report is unreliable */ - if (link->pmp < 2) - link->flags |= ATA_LFLAG_ASSUME_ATA; - - /* the config device at port 2 locks up on SRST */ - if (link->pmp == 2) - link->flags |= ATA_LFLAG_NO_SRST | - ATA_LFLAG_ASSUME_ATA; - } + /* + * sil4723 quirks + * + * Link reports offline after LPM. Class code report is + * unreliable. SIMG PMPs never got SRST reliable and the + * config device at port 2 locks up on SRST. + */ + ata_for_each_link(link, ap, EDGE) + link->flags |= ATA_LFLAG_NO_LPM | + ATA_LFLAG_NO_SRST | + ATA_LFLAG_ASSUME_ATA; } else if (vendor == 0x1095 && devid == 0x4726) { /* sil4726 quirks */ ata_for_each_link(link, ap, EDGE) { + /* link reports offline after LPM */ + link->flags |= ATA_LFLAG_NO_LPM; + /* Class code report is unreliable and SRST * times out under certain configurations. * Config device can be at port 0 or 5 and @@ -378,6 +447,19 @@ static void sata_pmp_quirks(struct ata_port *ap) * otherwise. Don't try hard to recover it. */ ap->pmp_link[ap->nr_pmp_links - 1].flags |= ATA_LFLAG_NO_RETRY; + } else if (vendor == 0x197b && (devid == 0x2352 || devid == 0x0325)) { + /* + * 0x2352: found in Thermaltake BlackX Duet, jmicron JMB350? + * 0x0325: jmicron JMB394. + */ + ata_for_each_link(link, ap, EDGE) { + /* SRST breaks detection and disks get misclassified + * LPM disabled to avoid potential problems + */ + link->flags |= ATA_LFLAG_NO_LPM | + ATA_LFLAG_NO_SRST | + ATA_LFLAG_ASSUME_ATA; + } } } @@ -404,20 +486,17 @@ int sata_pmp_attach(struct ata_device *dev) /* is it hanging off the right place? */ if (!sata_pmp_supported(ap)) { - ata_dev_printk(dev, KERN_ERR, - "host does not support Port Multiplier\n"); + ata_dev_err(dev, "host does not support Port Multiplier\n"); return -EINVAL; } if (!ata_is_host_link(link)) { - ata_dev_printk(dev, KERN_ERR, - "Port Multipliers cannot be nested\n"); + ata_dev_err(dev, "Port Multipliers cannot be nested\n"); return -EINVAL; } if (dev->devno) { - ata_dev_printk(dev, KERN_ERR, - "Port Multiplier must be the first device\n"); + ata_dev_err(dev, "Port Multiplier must be the first device\n"); return -EINVAL; } @@ -436,8 +515,7 @@ int sata_pmp_attach(struct ata_device *dev) rc = sata_pmp_init_links(ap, sata_pmp_gscr_ports(dev->gscr)); if (rc) { - ata_dev_printk(dev, KERN_INFO, - "failed to initialize PMP links\n"); + ata_dev_info(dev, "failed to initialize PMP links\n"); goto fail; } @@ -455,8 +533,6 @@ int sata_pmp_attach(struct ata_device *dev) ata_for_each_link(tlink, ap, EDGE) sata_link_init_spd(tlink); - ata_acpi_associate_sata_port(ap); - return 0; fail: @@ -481,7 +557,7 @@ static void sata_pmp_detach(struct ata_device *dev) struct ata_link *tlink; unsigned long flags; - ata_dev_printk(dev, KERN_INFO, "Port Multiplier detaching\n"); + ata_dev_info(dev, "Port Multiplier detaching\n"); WARN_ON(!ata_is_host_link(link) || dev->devno || link->pmp != SATA_PMP_CTRL_PORT); @@ -496,8 +572,6 @@ static void sata_pmp_detach(struct ata_device *dev) ap->nr_pmp_links = 0; link->pmp = 0; spin_unlock_irqrestore(ap->lock, flags); - - ata_acpi_associate_sata_port(ap); } /** @@ -528,23 +602,23 @@ static int sata_pmp_same_pmp(struct ata_device *dev, const u32 *new_gscr) new_nr_ports = sata_pmp_gscr_ports(new_gscr); if (old_vendor != new_vendor) { - ata_dev_printk(dev, KERN_INFO, "Port Multiplier " - "vendor mismatch '0x%x' != '0x%x'\n", - old_vendor, new_vendor); + ata_dev_info(dev, + "Port Multiplier vendor mismatch '0x%x' != '0x%x'\n", + old_vendor, new_vendor); return 0; } if (old_devid != new_devid) { - ata_dev_printk(dev, KERN_INFO, "Port Multiplier " - "device ID mismatch '0x%x' != '0x%x'\n", - old_devid, new_devid); + ata_dev_info(dev, + "Port Multiplier device ID mismatch '0x%x' != '0x%x'\n", + old_devid, new_devid); return 0; } if (old_nr_ports != new_nr_ports) { - ata_dev_printk(dev, KERN_INFO, "Port Multiplier " - "nr_ports mismatch '0x%x' != '0x%x'\n", - old_nr_ports, new_nr_ports); + ata_dev_info(dev, + "Port Multiplier nr_ports mismatch '0x%x' != '0x%x'\n", + old_nr_ports, new_nr_ports); return 0; } @@ -610,8 +684,7 @@ static int sata_pmp_revalidate(struct ata_device *dev, unsigned int new_class) return 0; fail: - ata_dev_printk(dev, KERN_ERR, - "PMP revalidation failed (errno=%d)\n", rc); + ata_dev_err(dev, "PMP revalidation failed (errno=%d)\n", rc); DPRINTK("EXIT, rc=%d\n", rc); return rc; } @@ -635,13 +708,14 @@ static int sata_pmp_revalidate_quick(struct ata_device *dev) err_mask = sata_pmp_read(dev->link, SATA_PMP_GSCR_PROD_ID, &prod_id); if (err_mask) { - ata_dev_printk(dev, KERN_ERR, "failed to read PMP product ID " - "(Emask=0x%x)\n", err_mask); + ata_dev_err(dev, + "failed to read PMP product ID (Emask=0x%x)\n", + err_mask); return -EIO; } if (prod_id != dev->gscr[SATA_PMP_GSCR_PROD_ID]) { - ata_dev_printk(dev, KERN_ERR, "PMP product ID mismatch\n"); + ata_dev_err(dev, "PMP product ID mismatch\n"); /* something weird is going on, request full PMP recovery */ return -EIO; } @@ -696,8 +770,7 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap, rc = ata_eh_reset(link, 0, prereset, softreset, hardreset, postreset); if (rc) { - ata_link_printk(link, KERN_ERR, - "failed to reset PMP, giving up\n"); + ata_link_err(link, "failed to reset PMP, giving up\n"); goto fail; } @@ -738,9 +811,9 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap, ehc->i.action |= ATA_EH_RESET; goto retry; } else { - ata_dev_printk(dev, KERN_ERR, "failed to recover PMP " - "after %d tries, giving up\n", - ATA_EH_PMP_TRIES); + ata_dev_err(dev, + "failed to recover PMP after %d tries, giving up\n", + ATA_EH_PMP_TRIES); goto fail; } } @@ -786,8 +859,9 @@ static int sata_pmp_eh_handle_disabled_links(struct ata_port *ap) /* unconditionally clear SError.N */ rc = sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG); if (rc) { - ata_link_printk(link, KERN_ERR, "failed to clear " - "SError.N (errno=%d)\n", rc); + ata_link_err(link, + "failed to clear SError.N (errno=%d)\n", + rc); return rc; } @@ -809,7 +883,7 @@ static int sata_pmp_handle_link_fail(struct ata_link *link, int *link_tries) /* disable this link */ if (!(link->flags & ATA_LFLAG_DISABLED)) { - ata_link_printk(link, KERN_WARNING, + ata_link_warn(link, "failed to recover link after %d tries, disabling\n", ATA_EH_PMP_LINK_TRIES); @@ -893,7 +967,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap) err_mask = sata_pmp_write(pmp_link, SATA_PMP_GSCR_FEAT_EN, gscr[SATA_PMP_GSCR_FEAT_EN]); if (err_mask) { - ata_link_printk(pmp_link, KERN_WARNING, + ata_link_warn(pmp_link, "failed to disable NOTIFY (err_mask=0x%x)\n", err_mask); goto pmp_fail; @@ -911,15 +985,25 @@ static int sata_pmp_eh_recover(struct ata_port *ap) if (rc) goto link_fail; - /* Connection status might have changed while resetting other - * links, check SATA_PMP_GSCR_ERROR before returning. - */ - /* clear SNotification */ rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf); if (rc == 0) sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf); + /* + * If LPM is active on any fan-out port, hotplug wouldn't + * work. Return w/ PHY event notification disabled. + */ + ata_for_each_link(link, ap, EDGE) + if (link->lpm_policy > ATA_LPM_MAX_POWER) + return 0; + + /* + * Connection status might have changed while resetting other + * links, enable notification and check SATA_PMP_GSCR_ERROR + * before returning. + */ + /* enable notification */ if (pmp_dev->flags & ATA_DFLAG_AN) { gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY; @@ -927,8 +1011,9 @@ static int sata_pmp_eh_recover(struct ata_port *ap) err_mask = sata_pmp_write(pmp_link, SATA_PMP_GSCR_FEAT_EN, gscr[SATA_PMP_GSCR_FEAT_EN]); if (err_mask) { - ata_dev_printk(pmp_dev, KERN_ERR, "failed to write " - "PMP_FEAT_EN (Emask=0x%x)\n", err_mask); + ata_dev_err(pmp_dev, + "failed to write PMP_FEAT_EN (Emask=0x%x)\n", + err_mask); rc = -EIO; goto pmp_fail; } @@ -937,8 +1022,9 @@ static int sata_pmp_eh_recover(struct ata_port *ap) /* check GSCR_ERROR */ err_mask = sata_pmp_read(pmp_link, SATA_PMP_GSCR_ERROR, &gscr_error); if (err_mask) { - ata_dev_printk(pmp_dev, KERN_ERR, "failed to read " - "PMP_GSCR_ERROR (Emask=0x%x)\n", err_mask); + ata_dev_err(pmp_dev, + "failed to read PMP_GSCR_ERROR (Emask=0x%x)\n", + err_mask); rc = -EIO; goto pmp_fail; } @@ -952,17 +1038,16 @@ static int sata_pmp_eh_recover(struct ata_port *ap) ata_ehi_hotplugged(&link->eh_context.i); cnt++; } else { - ata_link_printk(link, KERN_WARNING, - "PHY status changed but maxed out on retries, " - "giving up\n"); - ata_link_printk(link, KERN_WARNING, - "Manully issue scan to resume this link\n"); + ata_link_warn(link, + "PHY status changed but maxed out on retries, giving up\n"); + ata_link_warn(link, + "Manually issue scan to resume this link\n"); } } if (cnt) { - ata_port_printk(ap, KERN_INFO, "PMP SError.N set for some " - "ports, repeating recovery\n"); + ata_port_info(ap, + "PMP SError.N set for some ports, repeating recovery\n"); goto retry; } @@ -990,9 +1075,8 @@ static int sata_pmp_eh_recover(struct ata_port *ap) goto retry; } - ata_port_printk(ap, KERN_ERR, - "failed to recover PMP after %d tries, giving up\n", - ATA_EH_PMP_TRIES); + ata_port_err(ap, "failed to recover PMP after %d tries, giving up\n", + ATA_EH_PMP_TRIES); sata_pmp_detach(pmp_dev); ata_dev_disable(pmp_dev); |
