summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorShahid Akhtar <sakhtar@ti.com>2011-04-07 08:43:23 +0100
committerAndy Green <andy.green@linaro.org>2011-04-07 08:43:23 +0100
commitdf691d0cf2151e2443f3c9c958e9b4b91c6e53b0 (patch)
tree934d1ea40c5cbb42aa4f7c3bf08be9639eca4607 /drivers
parent053f5674c835434514f070ee46a09bb68173b12f (diff)
syslink:ipu_pm: Modifications for timer setup for WDT
Modified to configure timer 3 that is used to trigger hibernation and it is also used as watch dog timer. Since both ducati cores can go to retention together and if one core is not responding both need to be reset, we only use 1 timer [Hari K] The free irq while freeing checks the dev_id as the token with the dev_id that it was registered earlier. To avoid the race condition as when the remote proc frees the dmtimer handle save the handle in ipu_pm to use it as a token when passing to free_irq A cleaner solution should be investigated for watchdog timer Change-Id: I15d3c0182d84861819cc36abf1f78cbc1674375f Signed-off-by: Shahid Akhtar <sakhtar@ti.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dsp/syslink/Kconfig33
-rw-r--r--drivers/dsp/syslink/ipu_pm/ipu_pm.c198
-rw-r--r--drivers/dsp/syslink/ipu_pm/ipu_pm.h24
3 files changed, 250 insertions, 5 deletions
diff --git a/drivers/dsp/syslink/Kconfig b/drivers/dsp/syslink/Kconfig
index 0ac3187d475..08eeb5c8715 100644
--- a/drivers/dsp/syslink/Kconfig
+++ b/drivers/dsp/syslink/Kconfig
@@ -29,11 +29,36 @@ config DUCATI_BASEIMAGE_PHYS_ADDR
loaded.
config SYSLINK_DUCATI_PM
- tristate "DUCATI POWER MANAGEMENT"
- depends on SYSLINK_PROC && SYSLINK_PROC4430
- default n
+ bool "IPU Power Management"
+ depends on SYSLINK_PROC
+ default y
help
- Ducati Power Management Implementation
+ Enables the options available for ipu_pm implementation
+
+config SYSLINK_IPU_SELF_HIBERNATION
+ bool "Enable IPU Self hibernation"
+ depends on SYSLINK_DUCATI_PM
+ default y
+ help
+ IPU will hibernate by it self after a configurable time, this
+ controls the self hibernation, IPU will hibernate when a system
+ suspend is executed and wake up when system resumes.
+
+config DUCATI_WATCH_DOG
+ bool "Enable Ducati watch dog timer"
+ depends on SYSLINK_IPU_SELF_HIBERNATION
+ default n
+ help
+ Ducati cores will trigger reset if any of the two M3 cores stop
+ responding after 7 seconds. Requires Hibernation enabled. If
+ hibernation is enabled, M3 cores go to hibernation after 5
+ seconds. Ducati cannot go to hibernation if fault occurs on one
+ of the M3 cores
+
+config SYSLINK_IPU_PM_TRACES
+ bool "IPU PM Debug Traces"
+ depends on SYSLINK_DUCATI_PM
+ default n
config OMAP_DEVICE_HANDLER
tristate "Device Handler"
diff --git a/drivers/dsp/syslink/ipu_pm/ipu_pm.c b/drivers/dsp/syslink/ipu_pm/ipu_pm.c
index f3ca91a569c..0af1b592ff6 100644
--- a/drivers/dsp/syslink/ipu_pm/ipu_pm.c
+++ b/drivers/dsp/syslink/ipu_pm/ipu_pm.c
@@ -27,7 +27,12 @@
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/clk.h>
#include <linux/uaccess.h>
+#include <linux/irq.h>
+
#include <linux/platform_device.h>
#include <syslink/notify.h>
#include <syslink/notify_driver.h>
@@ -261,6 +266,18 @@ static inline int ipu_pm_rel_cstr(struct ipu_pm_object *handle,
struct rcb_block *rcb_p,
struct ipu_pm_params *params);
+/* Hibernate and watch dog timer interrupt */
+static irqreturn_t ipu_pm_timer_interrupt(int irq,
+ void *dev_id);
+
+/* Hibernate and watch dog timer function */
+static int ipu_pm_timer_state(int event);
+
+#ifdef CONFIG_DUCATI_WATCH_DOG
+/* Functions for reporing watch dog reset */
+static int ipu_pm_notify_event(int event, void *data);
+#endif
+
/** ============================================================================
* Globals
* ============================================================================
@@ -371,7 +388,9 @@ static struct ipu_pm_params pm_params = {
.pm_notification_event = PM_NOTIFICATION,
.proc_id = A9,
.remote_proc_id = -1,
- .line_id = 0
+ .line_id = 0,
+ .hib_timer_state = PM_HIB_TIMER_RESET,
+ .wdt_time = 0
} ;
/* Functions to request resources */
@@ -423,6 +442,8 @@ static int (*release_fxn[PM_NUM_RES]) (struct ipu_pm_object *handle,
};
+static struct blocking_notifier_head ipu_pm_notifier;
+
/*
Function to schedule the recover process
*
@@ -2392,6 +2413,11 @@ int ipu_pm_save_ctx(int proc_id)
num_loaded_cores = app_loaded + sys_loaded;
+#ifdef CONFIG_SYSLINK_IPU_SELF_HIBERNATION
+ /* Turn off timer before hibernation */
+ ipu_pm_timer_state(PM_HIB_TIMER_OFF);
+#endif
+
flag = 1;
timeout = jiffies + msecs_to_jiffies(WAIT_FOR_IDLE_TIMEOUT);
/* Wait fot Ducati to hibernate */
@@ -2434,10 +2460,15 @@ int ipu_pm_save_ctx(int proc_id)
pr_err("Not able to save iommu");
} else
goto error;
+
+
exit:
mutex_unlock(ipu_pm_state.gate_handle);
return 0;
error:
+#ifdef CONFIG_SYSLINK_IPU_SELF_HIBERNATION
+ ipu_pm_timer_state(PM_HIB_TIMER_ON);
+#endif
mutex_unlock(ipu_pm_state.gate_handle);
pr_debug("Aborting hibernation process\n");
return -EINVAL;
@@ -2473,6 +2504,8 @@ int ipu_pm_restore_ctx(int proc_id)
/* Enable/disable ipu hibernation*/
#ifdef CONFIG_SYSLINK_IPU_SELF_HIBERNATION
handle->rcb_table->pm_flags.hibernateAllowed = 1;
+ /* turn on ducati hibernation timer */
+ ipu_pm_timer_state(PM_HIB_TIMER_ON);
#else
handle->rcb_table->pm_flags.hibernateAllowed = 0;
#endif
@@ -2533,6 +2566,10 @@ int ipu_pm_restore_ctx(int proc_id)
goto error;
handle->rcb_table->state_flag &= ~APP_PROC_DOWN;
}
+#ifdef CONFIG_SYSLINK_IPU_SELF_HIBERNATION
+ /* turn on ducati hibernation timer */
+ ipu_pm_timer_state(PM_HIB_TIMER_ON);
+#endif
#ifdef CONFIG_OMAP_PM
retval = omap_pm_set_max_sdma_lat(&pm_qos_handle_2,
IPU_PM_MM_MPU_LAT_CONSTRAINT);
@@ -2649,6 +2686,8 @@ int ipu_pm_setup(struct ipu_pm_config *cfg)
goto exit;
}
+ BLOCKING_INIT_NOTIFIER_HEAD(&ipu_pm_notifier);
+
return retval;
exit:
pr_err("ipu_pm_setup failed! retval = 0x%x", retval);
@@ -2705,6 +2744,7 @@ int ipu_pm_attach(u16 remote_proc_id, void *shared_addr)
__func__, __LINE__);
goto exit;
}
+ handle->dmtimer = NULL;
} else if (remote_proc_id == APP_M3 && IS_ERR_OR_NULL(app_rproc)) {
pr_debug("requesting app_rproc\n");
app_rproc = omap_rproc_get("ducati-proc1");
@@ -2715,6 +2755,7 @@ int ipu_pm_attach(u16 remote_proc_id, void *shared_addr)
__func__, __LINE__);
goto exit;
}
+ handle->dmtimer = NULL;
}
if (IS_ERR_OR_NULL(ducati_iommu)) {
@@ -2775,6 +2816,12 @@ int ipu_pm_detach(u16 remote_proc_id)
goto exit;
}
+#ifdef CONFIG_SYSLINK_IPU_SELF_HIBERNATION
+ /* reset the ducati hibernation timer */
+ if (remote_proc_id == SYS_M3)
+ ipu_pm_timer_state(PM_HIB_TIMER_RESET);
+#endif
+
/* When recovering clean_up was called, so wait for completion.
* If not make sure there is no resource pending.
*/
@@ -2847,6 +2894,7 @@ int ipu_pm_detach(u16 remote_proc_id)
if (recover)
recover = false;
global_rcb = NULL;
+ first_time = 1;
}
/* Deleting the handle based on remote_proc_id */
@@ -2912,3 +2960,151 @@ exit:
return retval;
}
EXPORT_SYMBOL(ipu_pm_destroy);
+
+static irqreturn_t ipu_pm_timer_interrupt(int irq, void *dev_id)
+{
+ struct omap_dm_timer *gpt = (struct omap_dm_timer *)dev_id;
+
+ ipu_pm_timer_state(PM_HIB_TIMER_EXPIRE);
+ omap_dm_timer_write_status(gpt, OMAP_TIMER_INT_OVERFLOW);
+ return IRQ_HANDLED;
+}
+
+/* Function implements hibernation and watch dog timer
+ * The functionality is based on following states
+ * RESET: Timer is disabed
+ * OFF: Timer is OFF
+ * ON: Timer running
+ * HIBERNATE: Waking up for ducati cores to hibernate
+ * WD_RESET: Waiting for Ducati cores to complete hibernation
+ */
+static int ipu_pm_timer_state(int event)
+{
+ int retval = 0;
+ int tick_rate;
+ struct ipu_pm_object *handle;
+ struct ipu_pm_params *params;
+
+ handle = ipu_pm_get_handle(SYS_M3);
+ if (handle == NULL) {
+ pr_err("ipu_pm_timer_state handle ptr NULL\n");
+ retval = PTR_ERR(handle);
+ goto exit;
+ }
+ params = handle->params;
+ if (params == NULL) {
+ pr_err("ipu_pm_timer_state params ptr NULL\n");
+ retval = PTR_ERR(params);
+ goto exit;
+ }
+ if (sys_rproc == NULL)
+ goto exit;
+
+ switch (event) {
+ case PM_HIB_TIMER_EXPIRE:
+ if (params->hib_timer_state == PM_HIB_TIMER_ON) {
+ /* If any resource in use, no hibernation */
+ if (handle->rcb_table->state_flag & HIB_REF_MASK)
+ goto exit;
+
+ pr_debug("Starting hibernation, waking up M3 cores");
+ handle->rcb_table->state_flag |= (SYS_PROC_HIB |
+ APP_PROC_HIB | ENABLE_IPU_HIB);
+#ifdef CONFIG_DUCATI_WATCH_DOG
+ if (sys_rproc->dmtimer != NULL)
+ omap_dm_timer_set_load(sys_rproc->dmtimer, 1,
+ params->wdt_time);
+ params->hib_timer_state = PM_HIB_TIMER_WDRESET;
+ } else if (params->hib_timer_state ==
+ PM_HIB_TIMER_WDRESET) {
+ /* notify devh to begin error recovery here */
+ pr_debug("Timer ISR: Trigger WD reset + recovery\n");
+ ipu_pm_notify_event(0, NULL);
+ if (sys_rproc->dmtimer != NULL)
+ omap_dm_timer_stop(sys_rproc->dmtimer);
+ params->hib_timer_state = PM_HIB_TIMER_OFF;
+#endif
+ }
+ break;
+ case PM_HIB_TIMER_RESET:
+ /* disable timer and remove irq handler */
+ if (handle->dmtimer) {
+ free_irq(OMAP44XX_IRQ_GPT3, (void *)handle->dmtimer);
+ handle->dmtimer = NULL;
+ params->hib_timer_state = PM_HIB_TIMER_RESET;
+ }
+ break;
+ case PM_HIB_TIMER_OFF: /* disable timer */
+ /* no need to disable timer since it
+ * is done in rproc context */
+ params->hib_timer_state = PM_HIB_TIMER_OFF;
+ break;
+ case PM_HIB_TIMER_ON: /* enable timer */
+ if (params->hib_timer_state == PM_HIB_TIMER_RESET) {
+ tick_rate = clk_get_rate(omap_dm_timer_get_fclk(
+ sys_rproc->dmtimer));
+ handle->rcb_table->hib_time = 0xFFFFFFFF - (
+ (tick_rate/1000) * PM_HIB_DEFAULT_TIME);
+ params->wdt_time = 0xFFFFFFFF - (
+ (tick_rate/1000) * PM_HIB_WDT_TIME);
+ retval = request_irq(OMAP44XX_IRQ_GPT3,
+ ipu_pm_timer_interrupt,
+ IRQF_DISABLED,
+ "HIB_TIMER",
+ (void *)sys_rproc->dmtimer);
+ if (retval < 0)
+ pr_warn("request_irq status: %x\n", retval);
+ /*
+ * store the dmtimer handle locally to use during
+ * free_irq as dev_id token in cases where the remote
+ * proc frees the dmtimer handle first
+ */
+ handle->dmtimer = sys_rproc->dmtimer;
+ }
+ if (sys_rproc->dmtimer != NULL)
+ omap_dm_timer_set_load_start(sys_rproc->dmtimer, 1,
+ handle->rcb_table->hib_time);
+ params->hib_timer_state = PM_HIB_TIMER_ON;
+ break;
+ }
+ return retval;
+exit:
+ if (retval < 0)
+ pr_err("ipu_pm_timer_state failed, retval: %x\n", retval);
+ return retval;
+}
+
+/*
+ * ======== ipu_pm_notify_event ========
+ * IPU event notifications.
+ */
+#ifdef CONFIG_DUCATI_WATCH_DOG
+static int ipu_pm_notify_event(int event, void *data)
+{
+ return blocking_notifier_call_chain(&ipu_pm_notifier, event, data);
+}
+#endif
+
+/*
+ * ======== ipu_pm_register_notifier ========
+ * Register for IPC events.
+ */
+int ipu_pm_register_notifier(struct notifier_block *nb)
+{
+ if (!nb)
+ return -EINVAL;
+ return blocking_notifier_chain_register(&ipu_pm_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(ipu_pm_register_notifier);
+
+/*
+ * ======== ipu_pm_unregister_notifier ========
+ * Un-register for events.
+ */
+int ipu_pm_unregister_notifier(struct notifier_block *nb)
+{
+ if (!nb)
+ return -EINVAL;
+ return blocking_notifier_chain_unregister(&ipu_pm_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(ipu_pm_unregister_notifier);
diff --git a/drivers/dsp/syslink/ipu_pm/ipu_pm.h b/drivers/dsp/syslink/ipu_pm/ipu_pm.h
index 42021d5e7f1..9b11cc54533 100644
--- a/drivers/dsp/syslink/ipu_pm/ipu_pm.h
+++ b/drivers/dsp/syslink/ipu_pm/ipu_pm.h
@@ -225,6 +225,9 @@
#define SYS_PROC_DOWN 0x00010000
#define APP_PROC_DOWN 0x00020000
#define ENABLE_IPU_HIB 0x00000040
+#define SYS_PROC_HIB 0x00000001
+#define APP_PROC_HIB 0x00000002
+#define HIB_REF_MASK 0x00000F80
#define SYS_PROC_IDLING 0x00000001
#define APP_PROC_IDLING 0x00000002
@@ -337,6 +340,18 @@ enum pm_event_type{
PM_LAST_EVENT
};
+
+enum pm_hib_timer_event{
+ PM_HIB_TIMER_RESET,
+ PM_HIB_TIMER_OFF,
+ PM_HIB_TIMER_ON,
+ PM_HIB_TIMER_WDRESET,
+ PM_HIB_TIMER_EXPIRE
+};
+
+#define PM_HIB_DEFAULT_TIME 5000 /* 5 SEC */
+#define PM_HIB_WDT_TIME 3000 /* 3 SEC */
+
struct rcb_message {
unsigned rcb_flag:1;
unsigned rcb_num:6;
@@ -429,6 +444,8 @@ struct ipu_pm_params {
int remote_proc_id;
int line_id;
void *gate_mp;
+ int hib_timer_state;
+ int wdt_time;
};
/* This structure defines attributes for initialization of the ipu_pm module. */
@@ -465,6 +482,7 @@ struct ipu_pm_object {
struct work_struct work;
struct kfifo fifo;
spinlock_t lock;
+ struct omap_dm_timer *dmtimer;
};
/* Function for PM resources Callback */
@@ -537,4 +555,10 @@ int ipu_pm_module_set_bandwidth(unsigned rsrc,
/* Function to get ducati state flag from share memory */
u32 ipu_pm_get_state(int proc_id);
+/* Function to register notifier from devh module */
+int ipu_pm_register_notifier(struct notifier_block *nb);
+
+/* Function to unregister notifier from devh module */
+int ipu_pm_unregister_notifier(struct notifier_block *nb);
+
#endif