summaryrefslogtreecommitdiff
path: root/drivers/ata
diff options
context:
space:
mode:
authorYuantian Tang <andy.tang@nxp.com>2022-02-24 09:40:57 +0800
committerAndy Tang <andy.tang@nxp.com>2022-04-01 04:39:52 +0200
commitc21cbc56af93b6f06ee53e8284763842f48fdf97 (patch)
treea4ca37f620349970cb6e8395945799272c88bd1c /drivers/ata
parentfc8fcb8ca1353dc355918272b89746cb3a126a87 (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/Kconfig12
-rw-r--r--drivers/ata/libahci.c31
-rw-r--r--drivers/ata/libata-eh.c13
-rw-r--r--drivers/ata/libata-pmp.c44
-rw-r--r--drivers/ata/libata-sata.c3
-rw-r--r--drivers/ata/libata-scsi.c3
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");
}