aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2018-01-11 20:31:08 -0500
committerTejun Heo <tj@kernel.org>2018-01-17 10:52:29 -0500
commiteb73390ae2413bbd5d56a396dfd79fe57de6b5ae (patch)
treebee3bca6ea2e4705347044af06b86d926680d122
parent3e507769d15e4b14b5a3688d7f5cba516cb4e72d (diff)
ata: ahci_brcm: Recover from failures to identify devices
When powering up, the SATA controller may fail to mount the HDD. The SATA controller will lock up, preventing it from negotiating to a lower speed or transmitting data. Root cause is power supply noise creating resonance at 6 Ghz and 3 GHz frequencies, which causes instability in the Clock-Data Recovery (CDR) frontend module, resulting in false acquisition of the clock at SATA 6G/3G speeds. The SATA controller may fail to mount the HDD and lock up, requiring a power cycle. Broadcom chips suspected of being susceptible to this issue include BCM7445, BCM7439, and BCM7366. The Kernel implements an error recovery mechanism that resets the SATA PHY and digital controller when the controller locks up. During this error recovery process, typically there is less activity on the board and Broadcom STB chip, so that the power supply is less noisy, thus allowing the SATA controller to lock correctly. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r--drivers/ata/ahci_brcm.c95
1 files changed, 87 insertions, 8 deletions
diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c
index ad3b8826ec79..ea430819c80b 100644
--- a/drivers/ata/ahci_brcm.c
+++ b/drivers/ata/ahci_brcm.c
@@ -96,14 +96,6 @@ struct brcm_ahci_priv {
96 enum brcm_ahci_version version; 96 enum brcm_ahci_version version;
97}; 97};
98 98
99static const struct ata_port_info ahci_brcm_port_info = {
100 .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
101 .link_flags = ATA_LFLAG_NO_DB_DELAY,
102 .pio_mask = ATA_PIO4,
103 .udma_mask = ATA_UDMA6,
104 .port_ops = &ahci_platform_ops,
105};
106
107static inline u32 brcm_sata_readreg(void __iomem *addr) 99static inline u32 brcm_sata_readreg(void __iomem *addr)
108{ 100{
109 /* 101 /*
@@ -269,6 +261,93 @@ static void brcm_sata_init(struct brcm_ahci_priv *priv)
269 brcm_sata_writereg(data, ctrl); 261 brcm_sata_writereg(data, ctrl);
270} 262}
271 263
264static unsigned int brcm_ahci_read_id(struct ata_device *dev,
265 struct ata_taskfile *tf, u16 *id)
266{
267 struct ata_port *ap = dev->link->ap;
268 struct ata_host *host = ap->host;
269 struct ahci_host_priv *hpriv = host->private_data;
270 struct brcm_ahci_priv *priv = hpriv->plat_data;
271 void __iomem *mmio = hpriv->mmio;
272 unsigned int err_mask;
273 unsigned long flags;
274 int i, rc;
275 u32 ctl;
276
277 /* Try to read the device ID and, if this fails, proceed with the
278 * recovery sequence below
279 */
280 err_mask = ata_do_dev_read_id(dev, tf, id);
281 if (likely(!err_mask))
282 return err_mask;
283
284 /* Disable host interrupts */
285 spin_lock_irqsave(&host->lock, flags);
286 ctl = readl(mmio + HOST_CTL);
287 ctl &= ~HOST_IRQ_EN;
288 writel(ctl, mmio + HOST_CTL);
289 readl(mmio + HOST_CTL); /* flush */
290 spin_unlock_irqrestore(&host->lock, flags);
291
292 /* Perform the SATA PHY reset sequence */
293 brcm_sata_phy_disable(priv, ap->port_no);
294
295 /* Bring the PHY back on */
296 brcm_sata_phy_enable(priv, ap->port_no);
297
298 /* Re-initialize and calibrate the PHY */
299 for (i = 0; i < hpriv->nports; i++) {
300 rc = phy_init(hpriv->phys[i]);
301 if (rc)
302 goto disable_phys;
303
304 rc = phy_calibrate(hpriv->phys[i]);
305 if (rc) {
306 phy_exit(hpriv->phys[i]);
307 goto disable_phys;
308 }
309 }
310
311 /* Re-enable host interrupts */
312 spin_lock_irqsave(&host->lock, flags);
313 ctl = readl(mmio + HOST_CTL);
314 ctl |= HOST_IRQ_EN;
315 writel(ctl, mmio + HOST_CTL);
316 readl(mmio + HOST_CTL); /* flush */
317 spin_unlock_irqrestore(&host->lock, flags);
318
319 return ata_do_dev_read_id(dev, tf, id);
320
321disable_phys:
322 while (--i >= 0) {
323 phy_power_off(hpriv->phys[i]);
324 phy_exit(hpriv->phys[i]);
325 }
326
327 return AC_ERR_OTHER;
328}
329
330static void brcm_ahci_host_stop(struct ata_host *host)
331{
332 struct ahci_host_priv *hpriv = host->private_data;
333
334 ahci_platform_disable_resources(hpriv);
335}
336
337static struct ata_port_operations ahci_brcm_platform_ops = {
338 .inherits = &ahci_ops,
339 .host_stop = brcm_ahci_host_stop,
340 .read_id = brcm_ahci_read_id,
341};
342
343static const struct ata_port_info ahci_brcm_port_info = {
344 .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
345 .link_flags = ATA_LFLAG_NO_DB_DELAY,
346 .pio_mask = ATA_PIO4,
347 .udma_mask = ATA_UDMA6,
348 .port_ops = &ahci_brcm_platform_ops,
349};
350
272#ifdef CONFIG_PM_SLEEP 351#ifdef CONFIG_PM_SLEEP
273static int brcm_ahci_suspend(struct device *dev) 352static int brcm_ahci_suspend(struct device *dev)
274{ 353{