diff options
author | Yuantian Tang <andy.tang@nxp.com> | 2022-02-24 09:40:57 +0800 |
---|---|---|
committer | Andy Tang <andy.tang@nxp.com> | 2022-04-01 04:39:52 +0200 |
commit | c21cbc56af93b6f06ee53e8284763842f48fdf97 (patch) | |
tree | a4ca37f620349970cb6e8395945799272c88bd1c /drivers/ata | |
parent | fc8fcb8ca1353dc355918272b89746cb3a126a87 (diff) |
LF-5606: libata: pmp: workaround for ERR051238 for imx sata
Information about ERR051238:
Title: SATA PMP Hot Plug Async Notification Not Working
Description: A hot plug on PMP port is communicated to host through
Asynchronous notification using “Set Device Bits” (SDB) FIS which
indicate that change has happened on a port of PMP by setting the
SNotify bit along with “interrupt” and “Notify” bits. The requirement
for a host to support Asynchronous Notification is that it may
generate an interrupt if the Set Device Bits FIS is received.
The SNotification register enables software to be sure that the
interrupt was due to a notification event. In this case the Host
controller doesn't set the SNotify bit due to which the Software
is not aware of the Hotplug event.
This patch creates a polling thread to detect the hotplug event.
Once hotplug event was detected, it sets up the flags and notify
the system to take actions manually.
Signed-off-by: Andy Tang <andy.tang@nxp.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/Kconfig | 12 | ||||
-rw-r--r-- | drivers/ata/libahci.c | 31 | ||||
-rw-r--r-- | drivers/ata/libata-eh.c | 13 | ||||
-rw-r--r-- | drivers/ata/libata-pmp.c | 44 | ||||
-rw-r--r-- | drivers/ata/libata-sata.c | 3 | ||||
-rw-r--r-- | drivers/ata/libata-scsi.c | 3 |
6 files changed, 105 insertions, 1 deletions
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index a7da8ea7b3ed..f7f21305e78d 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -194,6 +194,18 @@ config AHCI_IMX If unsure, say N. +if AHCI_IMX + +config AHCI_IMX_PMP + bool "SATA Port Multiplier support on I.MX" + depends on SATA_PMP + default N + help + This option enables support for the PMP on Freescale i.MX SoC's. + + If unsure, say N. +endif # AHCI_IMX + config AHCI_CEVA tristate "CEVA AHCI SATA support" depends on OF diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 395772fa3943..c11bac8c043a 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1458,6 +1458,14 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class, if (fbs_disabled) ahci_enable_fbs(ap); +#ifdef CONFIG_AHCI_IMX_PMP + if (ap->flags & (1 << 31)) { + if (ap->flags & (1 << 29)) + ap->flags |= (1 << 30); + ata_msleep(ap, 40); + } +#endif + DPRINTK("EXIT, class=%u\n", *class); return 0; @@ -1819,6 +1827,11 @@ static void ahci_handle_port_interrupt(struct ata_port *ap, if (unlikely(resetting)) status &= ~PORT_IRQ_BAD_PMP; +#ifdef CONFIG_AHCI_IMX_PMP + status &= ~PORT_IRQ_BAD_PMP; + status &= ~PORT_IRQ_IF_ERR; +#endif + if (sata_lpm_ignore_phy_events(&ap->link)) { status &= ~PORT_IRQ_PHYRDY; ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG); @@ -1894,6 +1907,12 @@ static void ahci_port_intr(struct ata_port *ap) void __iomem *port_mmio = ahci_port_base(ap); u32 status; +#ifdef CONFIG_AHCI_IMX_PMP + status = readl(port_mmio + PORT_SCR_NTF); + if (status) + ap->flags |= (1 << 31); +#endif + status = readl(port_mmio + PORT_IRQ_STAT); writel(status, port_mmio + PORT_IRQ_STAT); @@ -2025,10 +2044,22 @@ static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) { struct ahci_port_priv *pp = qc->ap->private_data; u8 *rx_fis = pp->rx_fis; +#ifdef CONFIG_AHCI_IMX_PMP + u8 *fis; +#endif if (pp->fbs_enabled) rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ; +#ifdef CONFIG_AHCI_IMX_PMP + if (qc->ap->flags & (1 << 31)) { + if (!(qc->ap->flags & (1 << 30))) { + fis = pp->rx_fis + RX_FIS_D2H_REG; + memcpy(rx_fis + RX_FIS_D2H_REG, fis, 0x14); + } + } +#endif + /* * After a successful execution of an ATA PIO data-in command, * the device doesn't send D2H Reg FIS to update the TF and diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 1d4a6f1e88cd..78d25f6ab6c0 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -548,6 +548,10 @@ void ata_scsi_error(struct Scsi_Host *host) /* finish or retry handled scmd's and clean up */ WARN_ON(!list_empty(&eh_work_q)); +#ifdef CONFIG_AHCI_IMX_PMP + ap->flags &= ~(0x7 << 29); +#endif + DPRINTK("EXIT\n"); } @@ -1933,6 +1937,10 @@ static void ata_eh_link_autopsy(struct ata_link *link) if (ehc->i.flags & ATA_EHI_NO_AUTOPSY) return; +#ifdef CONFIG_AHCI_IMX_PMP + ata_msleep(ap, 20); +#endif + /* obtain and analyze SError */ rc = sata_scr_read(link, SCR_ERROR, &serror); if (rc == 0) { @@ -3553,6 +3561,11 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, DPRINTK("ENTER\n"); +#ifdef CONFIG_AHCI_IMX_PMP + if (ap->flags & (1 << 31)) + ap->flags |= (1 << 29); +#endif + /* prep for recovery */ ata_for_each_link(link, ap, EDGE) { struct ata_eh_context *ehc = &link->eh_context; diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index ba7be3f38617..9201b03a5df3 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include "libata.h" #include "libata-transport.h" +#include "ahci.h" const struct ata_port_operations sata_pmp_port_ops = { .inherits = &sata_port_ops, @@ -253,8 +254,36 @@ static const char *sata_pmp_spec_rev_str(const u32 *gscr) return "<unknown>"; } -#define PMP_GSCR_SII_POL 129 +#ifdef CONFIG_AHCI_IMX_PMP +struct hotplug_priv { + struct ata_port *ap; + void __iomem *port_mmio; +}; +struct hotplug_priv hpriv; + +#define TIMER_INTERVAL (2) +static int poll_thread(void *t) +{ + u32 rc; + struct ata_port *ap = hpriv.ap; + + for (;;) { + rc = ata_wait_register(ap, hpriv.port_mmio + PORT_SCR_NTF, + 0x8000, 0, 1, 2); + + if (rc == 0) + continue; + DPRINTK("----- %s %s %d hotplug detected ----\n", __FILE__,__func__,__LINE__); + hpriv.ap->flags |= (1 << 31); + sata_async_notification(hpriv.ap); + } + + return 0; +} +#endif + +#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; @@ -324,6 +353,15 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) "hotplug won't work on fan-out ports. Use warm-plug instead.\n"); } +#ifdef CONFIG_AHCI_IMX_PMP + /* create a polling thread for hotplug */ +#if 1 + hpriv.ap = ap; + hpriv.port_mmio = ahci_port_base(ap); + kernel_thread(poll_thread, (void *)ap, CLONE_SIGHAND | SIGCHLD); +#endif +#endif + return 0; fail: @@ -1102,6 +1140,10 @@ static int sata_pmp_eh_recover(struct ata_port *ap) */ void sata_pmp_error_handler(struct ata_port *ap) { +#ifdef CONFIG_AHCI_IMX_PMP + if (system_state >= SYSTEM_RUNNING) + ap->flags |= (1 << 31); +#endif ata_eh_autopsy(ap); ata_eh_report(ap); sata_pmp_eh_recover(ap); diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index 8f3ff830ab0c..89a62c5e7587 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -1354,6 +1354,9 @@ int sata_async_notification(struct ata_port *ap) * downstream ports has changed, schedule EH. */ if (sntf & (1 << SATA_PMP_CTRL_PORT)) { +#ifdef CONFIG_AHCI_IMX_PMP + ap->flags |= (1 << 31); +#endif ata_port_schedule_eh(ap); return 1; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 10303611d17b..b70ad2c09872 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -4513,6 +4513,9 @@ void ata_scsi_hotplug(struct work_struct *work) ata_scsi_scan_host(ap, 0); mutex_unlock(&ap->scsi_scan_mutex); +#ifdef CONFIG_AHCI_IMX_PMP + ap->flags &= ~(0x7 << 29); +#endif DPRINTK("EXIT\n"); } |