summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Molfetta <sjmolfetta@ti.com>2017-09-28 18:35:03 -0500
committerSheng Zhao <shengzhao@ti.com>2017-11-02 14:37:00 -0500
commitc1298188dbaa7264cb428477cab87659b4a5dfbf (patch)
tree0b04282f1c1b2bae439a67d53da2e00f396d2bf5
parenta3c8fb0b6b01d045b7d9710a353cd14d97b52de6 (diff)
downloadomap-omapzoom-c1298188dbaa7264cb428477cab87659b4a5dfbf.tar.gz
remoteproc/omap: Fix DSP recovery issues with EDMA
Resetting the DSP subsystem while there are pending DSP EDMA transfers or while the DSP EDMA is receiving DMA events from various peripherals (e.g. McASP) can cause various issues in the recovery process: - L3 errors which create a complete system lockup - DSPSS does not reset properly and put TPTCs into an unusable state Prior to shutdown of a DSP core, two cleanup/checks are required in order to mitigate these issues: 1. Clear DSP EDMA crossbar routings Upon remoteproc reset of the DSP, DMA events may continue to arrive to the DSPs EDMA instance after the reset sequence has completed. Clearing the crossbar ensures that no unexpected events arrive to the DSP's EDMA after reset 2. Wait for DSP EDMA traffic completion After clearing DSP EDMA crossbar routings, we disable all future events and poll the various active bits of the TPCC and both TPTCs of the DSP's EDMA and wait for any pending transfers to complete prior to issuing the reset. Change-Id: If34e4f361cbccd6eb86476812be0d3b0362a0190 Signed-off-by: Stephen Molfetta <sjmolfetta@ti.com> Signed-off-by: Angela Stegmaier <angelabaker@ti.com> [s-anna@ti.com: rebase from 3.14 to 4.4] Signed-off-by: Suman Anna <s-anna@ti.com> [shengzhao@ti.com: test and port from 3.14 to 4.4] Signed-off-by: Sheng Zhao <shengzhao@ti.com>
-rw-r--r--arch/arm/mach-omap2/pdata-quirks.c18
-rw-r--r--arch/arm/mach-omap2/remoteproc.c142
-rw-r--r--arch/arm/mach-omap2/remoteproc.h4
-rw-r--r--drivers/remoteproc/omap_remoteproc.c3
-rw-r--r--include/linux/platform_data/remoteproc-omap.h3
5 files changed, 167 insertions, 3 deletions
diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
index d73fdd04dc42..ea1077a68fe8 100644
--- a/arch/arm/mach-omap2/pdata-quirks.c
+++ b/arch/arm/mach-omap2/pdata-quirks.c
@@ -474,6 +474,20 @@ static struct iommu_platform_data dra7_dsp_mmu_edma_pdata = {
.device_idle = omap_device_idle,
};
+static struct omap_rproc_pdata dra7_dsp1_pdata = {
+ .device_enable = omap_rproc_device_enable,
+ .device_shutdown = omap_rproc_device_shutdown,
+ .timer_ops = &omap_rproc_dmtimer_ops,
+ .pre_shutdown = dra7_dsp1_pre_shutdown,
+};
+
+static struct omap_rproc_pdata dra7_dsp2_pdata = {
+ .device_enable = omap_rproc_device_enable,
+ .device_shutdown = omap_rproc_device_shutdown,
+ .timer_ops = &omap_rproc_dmtimer_ops,
+ .pre_shutdown = dra7_dsp2_pre_shutdown,
+};
+
static struct omap_hsmmc_platform_data dra7_hsmmc_data_mmc1;
static struct omap_hsmmc_platform_data dra7_hsmmc_data_mmc2;
static struct omap_hsmmc_platform_data dra7_hsmmc_data_mmc3;
@@ -590,9 +604,9 @@ static struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("ti,dra7-ipu", 0x58820000, "58820000.ipu",
&omap4_ipu_dsp_pdata),
OF_DEV_AUXDATA("ti,dra7-dsp", 0x40800000, "40800000.dsp",
- &omap4_ipu_dsp_pdata),
+ &dra7_dsp1_pdata),
OF_DEV_AUXDATA("ti,dra7-dsp", 0x41000000, "41000000.dsp",
- &omap4_ipu_dsp_pdata),
+ &dra7_dsp2_pdata),
OF_DEV_AUXDATA("ti,dra7-hsmmc", 0x4809c000, "4809c000.mmc",
&dra7_hsmmc_data_mmc1),
OF_DEV_AUXDATA("ti,dra7-hsmmc", 0x480b4000, "480b4000.mmc",
diff --git a/arch/arm/mach-omap2/remoteproc.c b/arch/arm/mach-omap2/remoteproc.c
index 5c969101bc22..fd4435778fe7 100644
--- a/arch/arm/mach-omap2/remoteproc.c
+++ b/arch/arm/mach-omap2/remoteproc.c
@@ -22,6 +22,148 @@
#include "omap_device.h"
#include "remoteproc.h"
+#include "soc.h"
+
+#define DSP1_EDMA_TPCC 0x40D10000
+#define DSP2_EDMA_TPCC 0x41510000
+#define DSP1_EDMA_TPTC0 0x40D05000
+#define DSP2_EDMA_TPTC0 0x41505000
+#define EDMA_TPCC_CCSTAT_OFFSET 0x640
+#define EDMA_TPCC_EECR_OFFSET 0x1028
+#define EDMA_TPCC_EECRH_OFFSET 0x102C
+#define EDMA_TPCC_QEECR_OFFSET 0x1088
+#define EDMA_DSP_TPTC1_OFFSET 0x1000
+#define EDMA_TPTC_TCSTAT0_OFFSET 0x100
+#define EDMA_TPTC_TCSTAT1_OFFSET (EDMA_DSP_TPTC1_OFFSET + \
+ EDMA_TPTC_TCSTAT0_OFFSET)
+
+#define CTRL_CORE_DMA_DSP1_DREQ 0x4A002CF8
+#define CTRL_CORE_DMA_DSP2_DREQ 0x4A002D20
+
+static void dra7_wait_dsp_edma_compl(u32 inst)
+{
+ u32 dsp_edma_tpcc_base, dsp_edma_tptc_base;
+ int timeout;
+ void __iomem *tpcc_base, *tptc_base;
+
+ if (!soc_is_dra7xx())
+ return;
+
+ dsp_edma_tpcc_base = inst ? DSP2_EDMA_TPCC : DSP1_EDMA_TPCC;
+ dsp_edma_tptc_base = inst ? DSP2_EDMA_TPTC0 : DSP1_EDMA_TPTC0;
+
+ tpcc_base = ioremap(dsp_edma_tpcc_base, SZ_16K);
+ if (!tpcc_base) {
+ pr_err("DSP EDMA TPCC ioremap failed\n");
+ goto map_err1;
+ }
+
+ tptc_base = ioremap(dsp_edma_tptc_base, SZ_8K);
+ if (!tptc_base) {
+ pr_err("DSP EDMA TPTC ioremap failed\n");
+ goto map_err2;
+ }
+
+ /* Disable all future EDMA and QDMA events to DSPx EDMA TPCC */
+ writel_relaxed(0xFFFFFFFF, tpcc_base + EDMA_TPCC_EECR_OFFSET);
+ writel_relaxed(0xFFFFFFFF, tpcc_base + EDMA_TPCC_EECRH_OFFSET);
+ writel_relaxed(0xFFFFFFFF, tpcc_base + EDMA_TPCC_QEECR_OFFSET);
+
+ /*
+ * Poll CCSTAT to ensure all actively serviced or queued events have
+ * been completed.
+ *
+ * The timeout is based on the duration which the EDMA CC queue will
+ * drain based on the slowest typical application. This is chosen to
+ * be 1.0625ms, which assumes a full event queue with transfers for
+ * an 8kHz audio stream, plus one extra transfer for safe measure.
+ */
+ timeout = 1063;
+ pr_warn("waiting for DSP%d EDMA traffic on TPCC to complete\n",
+ inst+1);
+ while (((readl_relaxed(tpcc_base + EDMA_TPCC_CCSTAT_OFFSET))
+ != 0x0) && --timeout)
+ udelay(1);
+ if (timeout == 0)
+ pr_warn("DSP%d EDMA transaction may be ongoing during shutdown! TPCC is active!\n",
+ inst + 1);
+
+ /*
+ * Check that PROGBUSY SRCACTV WSACTV, and DSTACTV bits of TCSTAT
+ * registers for DSP TPTC0 and TPTC1 are cleared prior to shutdown.
+ *
+ * The timeout is based on the duration of the EDMA transfer expected
+ * by the slowest typical application, which is chosen as 125us. This
+ * would be the transfer request rate of an 8kHz audio stream, with one
+ * extra transfer for safe measure.
+ */
+ timeout = 125;
+ pr_warn("waiting for DSP%d EDMA traffic on TPTC0 to complete\n",
+ inst+1);
+ while (((readl_relaxed(tptc_base + EDMA_TPTC_TCSTAT0_OFFSET) & 0x77)
+ != 0x0) && --timeout)
+ udelay(1);
+ if (timeout == 0)
+ pr_warn("DSP%d EDMA transaction may be ongoing during shutdown! TPTC0 is active!\n",
+ inst + 1);
+
+ timeout = 125;
+ pr_warn("waiting for DSP%d EDMA traffic on TPTC1 to complete\n",
+ inst+1);
+ while (((readl_relaxed(tptc_base + EDMA_TPTC_TCSTAT1_OFFSET) & 0x77)
+ != 0x0) && --timeout)
+ udelay(1);
+ if (timeout == 0)
+ pr_warn("DSP%d EDMA transaction may be ongoing during shutdown! TPTC1 is active!\n",
+ inst + 1);
+ iounmap(tptc_base);
+map_err2:
+ iounmap(tpcc_base);
+map_err1:
+ return;
+}
+
+static void dra7_clear_dsp_edma_xbar(u32 inst)
+{
+ u32 dsp_dreq_base;
+ void __iomem *iomem_base;
+ u8 offset;
+
+ if (!soc_is_dra7xx())
+ return;
+
+ dsp_dreq_base = inst ? CTRL_CORE_DMA_DSP2_DREQ :
+ CTRL_CORE_DMA_DSP1_DREQ;
+
+ iomem_base = ioremap(dsp_dreq_base, SZ_64);
+ if (!iomem_base) {
+ pr_err("DSP EDMA ioremap failed\n");
+ return;
+ }
+
+ pr_warn("Clearing all EDMA XBAR routings to DSP%d\n", inst+1);
+
+ /*
+ * Clear all connections to DSPx EDMA crossbar, from
+ * CTRL_CORE_DMA_DSPx_DREQ_0_1 to CTRL_CORE_DMA_DSPx_DREQ_18_19.
+ */
+ for (offset = 0x0; offset <= 0x24; offset += 0x4)
+ writel_relaxed(0x0, iomem_base + offset);
+
+ iounmap(iomem_base);
+}
+
+void dra7_dsp1_pre_shutdown(void)
+{
+ dra7_clear_dsp_edma_xbar(0);
+ dra7_wait_dsp_edma_compl(0);
+}
+
+void dra7_dsp2_pre_shutdown(void)
+{
+ dra7_clear_dsp_edma_xbar(1);
+ dra7_wait_dsp_edma_compl(1);
+}
/**
* omap_rproc_device_enable - enable the remoteproc device
diff --git a/arch/arm/mach-omap2/remoteproc.h b/arch/arm/mach-omap2/remoteproc.h
index 6236b507df7b..bc0e3ea19d2b 100644
--- a/arch/arm/mach-omap2/remoteproc.h
+++ b/arch/arm/mach-omap2/remoteproc.h
@@ -23,6 +23,8 @@
struct omap_dm_timer;
#if IS_ENABLED(CONFIG_OMAP_REMOTEPROC)
+void dra7_dsp1_pre_shutdown(void);
+void dra7_dsp2_pre_shutdown(void);
int omap_rproc_device_enable(struct platform_device *pdev);
int omap_rproc_device_shutdown(struct platform_device *pdev);
struct omap_dm_timer *omap_rproc_request_timer(struct device_node *np);
@@ -32,6 +34,8 @@ int omap_rproc_stop_timer(struct omap_dm_timer *timer);
int omap_rproc_get_timer_irq(struct omap_dm_timer *timer);
void omap_rproc_ack_timer_irq(struct omap_dm_timer *timer);
#else
+static inline void dra7_dsp1_pre_shutdown(void) { }
+static inline void dra7_dsp2_pre_shutdown(void) { }
static inline int omap_rproc_device_enable(struct platform_device *pdev)
{
return 0;
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
index 5d6f17bdfb2f..41f6f03fb789 100644
--- a/drivers/remoteproc/omap_remoteproc.c
+++ b/drivers/remoteproc/omap_remoteproc.c
@@ -532,6 +532,9 @@ static int omap_rproc_stop(struct rproc *rproc)
return ret;
}
+ if (pdata->pre_shutdown)
+ pdata->pre_shutdown();
+
ret = pdata->device_shutdown(pdev);
if (ret)
goto out;
diff --git a/include/linux/platform_data/remoteproc-omap.h b/include/linux/platform_data/remoteproc-omap.h
index e728eef50a92..b5c7554cffda 100644
--- a/include/linux/platform_data/remoteproc-omap.h
+++ b/include/linux/platform_data/remoteproc-omap.h
@@ -45,12 +45,13 @@ struct omap_rproc_timer_ops {
* struct omap_rproc_pdata - omap remoteproc's platform data
* @device_enable: omap-specific handler for enabling a device
* @device_shutdown: omap-specific handler for shutting down a device
+ * @pre_shutdown: omap-specific handler for performing pre-shutdown cleanup
* @timer_ops: platform data ops for OMAP dmtimer handlers
*/
struct omap_rproc_pdata {
int (*device_enable)(struct platform_device *pdev);
int (*device_shutdown)(struct platform_device *pdev);
-
+ void (*pre_shutdown)(void);
struct omap_rproc_timer_ops *timer_ops;
};