MSM: DSPS: Add support for independent DSPS restart
These changes are added to restart the DSPS independent
of other processors, when the DSPS crashes
CRs-fixed: 361257, 361260, 364130
Change-Id: I4afc4ff41b0862165dca45aa8a30ecfcd47dcb73
Acked-by: Phong Duong <pduong@qualcomm.com>
Signed-off-by: Karthik Karuppasamy <kkaruppa@codeaurora.org>
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 1382632..075c124 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -2018,6 +2018,16 @@
/* Sensors DSPS platform data */
+#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
+#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
+#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
+#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
+#define PPSS_DSPS_PIPE_BASE 0x12800000
+#define PPSS_DSPS_PIPE_SIZE 0x4000
+#define PPSS_DSPS_DDR_BASE 0x8fe00000
+#define PPSS_DSPS_DDR_SIZE 0x100000
+#define PPSS_SMEM_BASE 0x80000000
+#define PPSS_SMEM_SIZE 0x200000
#define PPSS_REG_PHYS_BASE 0x12080000
static struct dsps_clk_info dsps_clks[] = {};
@@ -2036,6 +2046,16 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.dsps_pwr_ctl_en = 1,
+ .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
+ .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
+ .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
+ .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
+ .pipe_start = PPSS_DSPS_PIPE_BASE,
+ .pipe_size = PPSS_DSPS_PIPE_SIZE,
+ .ddr_start = PPSS_DSPS_DDR_BASE,
+ .ddr_size = PPSS_DSPS_DDR_SIZE,
+ .smem_start = PPSS_SMEM_BASE,
+ .smem_size = PPSS_SMEM_SIZE,
.signature = DSPS_SIGNATURE,
};
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index be364e7..e7d8f42 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -3246,6 +3246,16 @@
/* Sensors DSPS platform data */
#ifdef CONFIG_MSM_DSPS
+#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
+#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
+#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
+#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
+#define PPSS_DSPS_PIPE_BASE 0x12800000
+#define PPSS_DSPS_PIPE_SIZE 0x4000
+#define PPSS_DSPS_DDR_BASE 0x8fe00000
+#define PPSS_DSPS_DDR_SIZE 0x100000
+#define PPSS_SMEM_BASE 0x80000000
+#define PPSS_SMEM_SIZE 0x200000
#define PPSS_REG_PHYS_BASE 0x12080000
static struct dsps_clk_info dsps_clks[] = {};
@@ -3264,6 +3274,16 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.dsps_pwr_ctl_en = 1,
+ .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
+ .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
+ .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
+ .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
+ .pipe_start = PPSS_DSPS_PIPE_BASE,
+ .pipe_size = PPSS_DSPS_PIPE_SIZE,
+ .ddr_start = PPSS_DSPS_DDR_BASE,
+ .ddr_size = PPSS_DSPS_DDR_SIZE,
+ .smem_start = PPSS_SMEM_BASE,
+ .smem_size = PPSS_SMEM_SIZE,
.signature = DSPS_SIGNATURE,
};
@@ -3274,7 +3294,6 @@
.name = "ppss_reg",
.flags = IORESOURCE_MEM,
},
-
{
.start = PPSS_WDOG_TIMER_IRQ,
.end = PPSS_WDOG_TIMER_IRQ,
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index d622af2..005ed1f 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -1568,6 +1568,16 @@
/* Sensors DSPS platform data */
#ifdef CONFIG_MSM_DSPS
+#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
+#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
+#define PPSS_DSPS_TCM_BUF_BASE 0x12040000
+#define PPSS_DSPS_TCM_BUF_SIZE 0x4000
+#define PPSS_DSPS_PIPE_BASE 0x12800000
+#define PPSS_DSPS_PIPE_SIZE 0x0 /* 8660 V2 does not use PIPE memory */
+#define PPSS_DSPS_DDR_BASE 0x8fe00000
+#define PPSS_DSPS_DDR_SIZE 0x0 /* 8660 V2 does not use DDR memory */
+#define PPSS_SMEM_BASE 0x40000000
+#define PPSS_SMEM_SIZE 0x4000
#define PPSS_REG_PHYS_BASE 0x12080000
#define MHZ (1000*1000)
@@ -1631,6 +1641,16 @@
.regs = dsps_regs,
.regs_num = ARRAY_SIZE(dsps_regs),
.init = dsps_init1,
+ .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
+ .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
+ .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
+ .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
+ .pipe_start = PPSS_DSPS_PIPE_BASE,
+ .pipe_size = PPSS_DSPS_PIPE_SIZE,
+ .ddr_start = PPSS_DSPS_DDR_BASE,
+ .ddr_size = PPSS_DSPS_DDR_SIZE,
+ .smem_start = PPSS_SMEM_BASE,
+ .smem_size = PPSS_SMEM_SIZE,
.signature = DSPS_SIGNATURE,
};
diff --git a/arch/arm/mach-msm/include/mach/msm_dsps.h b/arch/arm/mach-msm/include/mach/msm_dsps.h
index cfb2024..32a4f15 100644
--- a/arch/arm/mach-msm/include/mach/msm_dsps.h
+++ b/arch/arm/mach-msm/include/mach/msm_dsps.h
@@ -75,6 +75,16 @@
* @regs_num - number of regulators.
* @dsps_pwr_ctl_en - to enable DSPS to do power control if set 1
* otherwise the apps will do power control
+ * @tcm_code_start - start of the TCM code region as physical address
+ * @tcm_code_size - size of the TCM code region in bytes
+ * @tcm_buf_start - start of the TCM buf region as physical address
+ * @tcm_buf_size - size of the TCM buf region in bytes
+ * @pipe_start - start of the PIPE region as physical address
+ * @pipe_size - size of the PIPE region in bytes
+ * @ddr_start - start of the DDR region as physical address
+ * @ddr_size - size of the DDR region in bytes
+ * @smem_start - start of the smem region as physical address
+ * @smem_size - size of the smem region in bytes
* @signature - signature for validity check.
*/
struct msm_dsps_platform_data {
@@ -87,6 +97,16 @@
int regs_num;
int dsps_pwr_ctl_en;
void (*init)(struct msm_dsps_platform_data *data);
+ int tcm_code_start;
+ int tcm_code_size;
+ int tcm_buf_start;
+ int tcm_buf_size;
+ int pipe_start;
+ int pipe_size;
+ int ddr_start;
+ int ddr_size;
+ int smem_start;
+ int smem_size;
u32 signature;
};
diff --git a/arch/arm/mach-msm/msm_dsps.c b/arch/arm/mach-msm/msm_dsps.c
index 057665b..3b52a9f 100644
--- a/arch/arm/mach-msm/msm_dsps.c
+++ b/arch/arm/mach-msm/msm_dsps.c
@@ -15,6 +15,8 @@
*
*/
+#include <asm/atomic.h>
+
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -39,10 +41,11 @@
#include <mach/subsystem_restart.h>
#include <mach/subsystem_notif.h>
+#include "ramdump.h"
#include "timer.h"
#define DRV_NAME "msm_dsps"
-#define DRV_VERSION "3.02"
+#define DRV_VERSION "4.00"
#define PPSS_PAUSE_REG 0x1804
@@ -58,8 +61,15 @@
* @cdev - character device for user interface.
* @pdata - platform data.
* @pil - handle to DSPS Firmware loader.
+ * @dspsfw_ramdump_dev - handle to ramdump device for DSPS
+ * @dspsfw_ramdump_segments - Ramdump segment information for DSPS
+ * @smem_ramdump_dev - handle to ramdump device for smem
+ * @smem_ramdump_segments - Ramdump segment information for smem
* @is_on - DSPS is on.
* @ref_count - open/close reference count.
+ * @wdog_irq - DSPS Watchdog IRQ
+ * @wd_crash - Watchdog ISR fired
+ * @crash_in_progress - 1 if crash recovery is in progress
* @ppss_base - ppss registers virtual base address.
*/
struct dsps_drv {
@@ -73,9 +83,18 @@
void *pil;
+ void *dspsfw_ramdump_dev;
+ struct ramdump_segment dspsfw_ramdump_segments[4];
+
+ void *smem_ramdump_dev;
+ struct ramdump_segment smem_ramdump_segments[1];
+
int is_on;
int ref_count;
+ int wdog_irq;
+ atomic_t wd_crash;
+ atomic_t crash_in_progress;
void __iomem *ppss_base;
};
@@ -89,8 +108,7 @@
*/
static int dsps_crash_shutdown_g;
-
-static void dsps_fatal_handler(struct work_struct *work);
+static void dsps_restart_handler(struct work_struct *work);
/**
* Load DSPS Firmware.
@@ -360,7 +378,7 @@
return 0;
}
-static DECLARE_WORK(dsps_fatal_work, dsps_fatal_handler);
+static DECLARE_WORK(dsps_fatal_work, dsps_restart_handler);
/**
* Watchdog interrupt handler
@@ -369,6 +387,7 @@
static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
{
pr_debug("%s\n", __func__);
+ atomic_set(&drv->wd_crash, 1);
(void)schedule_work(&dsps_fatal_work);
disable_irq_nosync(irq);
return IRQ_HANDLED;
@@ -406,7 +425,7 @@
ret = put_user(val, (u32 __user *) arg);
break;
case DSPS_IOCTL_RESET:
- dsps_fatal_handler(NULL);
+ dsps_restart_handler(NULL);
ret = 0;
break;
default:
@@ -498,21 +517,52 @@
ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"ppss_wdog");
if (ppss_wdog) {
- ret = request_irq(ppss_wdog->start, dsps_wdog_bite_irq,
+ drv->wdog_irq = ppss_wdog->start;
+ ret = request_irq(drv->wdog_irq, dsps_wdog_bite_irq,
IRQF_TRIGGER_RISING, "dsps_wdog", NULL);
if (ret) {
pr_err("%s: request_irq fail %d\n", __func__, ret);
goto request_irq_err;
}
} else {
+ drv->wdog_irq = -1;
pr_debug("%s: ppss_wdog not supported.\n", __func__);
}
+ drv->dspsfw_ramdump_segments[0].address = drv->pdata->tcm_code_start;
+ drv->dspsfw_ramdump_segments[0].size = drv->pdata->tcm_code_size;
+ drv->dspsfw_ramdump_segments[1].address = drv->pdata->tcm_buf_start;
+ drv->dspsfw_ramdump_segments[1].size = drv->pdata->tcm_buf_size;
+ drv->dspsfw_ramdump_segments[2].address = drv->pdata->pipe_start;
+ drv->dspsfw_ramdump_segments[2].size = drv->pdata->pipe_size;
+ drv->dspsfw_ramdump_segments[3].address = drv->pdata->ddr_start;
+ drv->dspsfw_ramdump_segments[3].size = drv->pdata->ddr_size;
+
+ drv->dspsfw_ramdump_dev = create_ramdump_device("dsps");
+ if (!drv->dspsfw_ramdump_dev) {
+ pr_err("%s: create_ramdump_device(\"dsps\") fail\n",
+ __func__);
+ goto create_ramdump_err;
+ }
+
+ drv->smem_ramdump_segments[0].address = drv->pdata->smem_start;
+ drv->smem_ramdump_segments[0].size = drv->pdata->smem_size;
+ drv->smem_ramdump_dev = create_ramdump_device("smem");
+ if (!drv->smem_ramdump_dev) {
+ pr_err("%s: create_ramdump_device(\"smem\") fail\n",
+ __func__);
+ goto create_ramdump_err;
+ }
+
if (drv->pdata->init)
drv->pdata->init(drv->pdata);
return 0;
+create_ramdump_err:
+ disable_irq_nosync(drv->wdog_irq);
+ free_irq(drv->wdog_irq, NULL);
+
request_irq_err:
iounmap(drv->ppss_base);
@@ -600,6 +650,8 @@
}
}
+ free_irq(drv->wdog_irq, NULL);
+
iounmap(drv->ppss_base);
}
@@ -646,22 +698,42 @@
* Fatal error handler
* Resets DSPS.
*/
-static void dsps_fatal_handler(struct work_struct *work)
+static void dsps_restart_handler(struct work_struct *work)
{
uint32_t dsps_state;
+ int restart_level;
+ char *smem_reset_reason;
+ unsigned smem_reset_size;
+ const char dflt_reason[] = "Died too early due to unknown reason";
dsps_state = smsm_get_state(SMSM_DSPS_STATE);
+ restart_level = get_restart_level();
- pr_debug("%s: DSPS state 0x%x\n", __func__, dsps_state);
+ pr_debug("%s: DSPS state 0x%x. Restart lvl %d\n",
+ __func__, dsps_state, restart_level);
- if (dsps_state & SMSM_RESET) {
- pr_err("%s: DSPS fatal error detected. Resetting\n",
+ if ((dsps_state & SMSM_RESET) ||
+ (atomic_read(&drv->wd_crash) == 1)) {
+ smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
+ &smem_reset_size);
+ if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
+ smem_reset_reason[smem_reset_size-1] = 0;
+ pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+ __func__, smem_reset_reason);
+ memset(smem_reset_reason, 0, smem_reset_size);
+ wmb();
+ } else
+ pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+ __func__, dflt_reason);
+ } else
+ pr_err("%s: User-initiated DSPS reset.\nResetting DSPS\n",
__func__);
- panic("DSPS fatal error detected.");
+
+ if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
+ pr_err("%s: DSPS already resetting. Count %d\n", __func__,
+ atomic_read(&drv->crash_in_progress));
} else {
- pr_debug("%s: User-initiated DSPS reset. Resetting\n",
- __func__);
- panic("User-initiated DSPS reset.");
+ subsystem_restart("dsps");
}
}
@@ -680,13 +752,8 @@
dsps_crash_shutdown_g = 0;
return;
}
-
- if (new_state & SMSM_RESET) {
- pr_err
- ("%s: SMSM_RESET state detected. restarting the DSPS\n",
- __func__);
- panic("SMSM_RESET state detected.");
- }
+ if (new_state & SMSM_RESET)
+ dsps_restart_handler(NULL);
}
/**
@@ -697,7 +764,9 @@
static int dsps_shutdown(const struct subsys_data *subsys)
{
pr_debug("%s\n", __func__);
- dsps_unload();
+ dsps_suspend();
+ pil_force_shutdown(drv->pdata->pil_name);
+ dsps_power_off_handler();
return 0;
}
@@ -709,11 +778,14 @@
static int dsps_powerup(const struct subsys_data *subsys)
{
pr_debug("%s\n", __func__);
- if (dsps_load(drv->pdata->pil_name) != 0) {
- pr_err("%s: fail to restart DSPS after reboot\n",
- __func__);
- return 1;
+ dsps_power_on_handler();
+ pil_force_boot(drv->pdata->pil_name);
+ atomic_set(&drv->crash_in_progress, 0);
+ if (atomic_read(&drv->wd_crash) > 0) {
+ atomic_set(&drv->wd_crash, 0);
+ enable_irq(drv->wdog_irq);
}
+ dsps_resume();
return 0;
}
@@ -736,8 +808,34 @@
*/
static int dsps_ramdump(int enable, const struct subsys_data *subsys)
{
+ int ret = 0;
pr_debug("%s\n", __func__);
- return 0;
+
+ if (enable) {
+ if (drv->dspsfw_ramdump_dev != NULL) {
+ ret = do_ramdump(drv->dspsfw_ramdump_dev,
+ drv->dspsfw_ramdump_segments,
+ ARRAY_SIZE(drv->dspsfw_ramdump_segments));
+ if (ret < 0) {
+ pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
+ __func__, ret);
+ goto dsps_ramdump_out;
+ }
+ }
+ if (drv->smem_ramdump_dev != NULL) {
+ ret = do_ramdump(drv->smem_ramdump_dev,
+ drv->smem_ramdump_segments,
+ ARRAY_SIZE(drv->smem_ramdump_segments));
+ if (ret < 0) {
+ pr_err("%s: Unable to dump smem memory (rc = %d).\n",
+ __func__, ret);
+ goto dsps_ramdump_out;
+ }
+ }
+ }
+
+dsps_ramdump_out:
+ return ret;
}
static struct subsys_data dsps_ssrops = {
@@ -768,6 +866,9 @@
pr_err("%s: kzalloc fail.\n", __func__);
goto alloc_err;
}
+ atomic_set(&drv->wd_crash, 0);
+ atomic_set(&drv->crash_in_progress, 0);
+
drv->pdata = pdev->dev.platform_data;
drv->dev_class = class_create(THIS_MODULE, DRV_NAME);