|
|
@@ -141,6 +141,7 @@
|
|
|
#include <linux/cpu.h>
|
|
|
#include <linux/of.h>
|
|
|
#include <linux/of_address.h>
|
|
|
+#include <linux/suspend.h>
|
|
|
#include <linux/bootmem.h>
|
|
|
|
|
|
#include <linux/platform_data/ti-sysc.h>
|
|
|
@@ -236,6 +237,9 @@ static struct omap_hwmod_soc_ops soc_ops;
|
|
|
/* omap_hwmod_list contains all registered struct omap_hwmods */
|
|
|
static LIST_HEAD(omap_hwmod_list);
|
|
|
|
|
|
+/* oh_reidle_list contains all omap_hwmods with HWMOD_NEEDS_REIDLE set */
|
|
|
+static LIST_HEAD(oh_reidle_list);
|
|
|
+
|
|
|
/* mpu_oh: used to add/remove MPU initiator from sleepdep list */
|
|
|
static struct omap_hwmod *mpu_oh;
|
|
|
|
|
|
@@ -2266,6 +2270,28 @@ int omap_hwmod_parse_module_range(struct omap_hwmod *oh,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * _setup_reidle- check hwmod @oh and add to reidle list
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ * @n: (unused)
|
|
|
+ *
|
|
|
+ * Check hwmod for HWMOD_NEEDS_REIDLE flag and add to list if
|
|
|
+ * necessary. Return 0 on success.
|
|
|
+ */
|
|
|
+static int _setup_reidle(struct omap_hwmod *oh, void *data)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (oh->flags & HWMOD_NEEDS_REIDLE) {
|
|
|
+ ret = omap_hwmod_enable_reidle(oh);
|
|
|
+
|
|
|
+ if (!ret)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* _init_mpu_rt_base - populate the virtual address for a hwmod
|
|
|
* @oh: struct omap_hwmod * to locate the virtual address
|
|
|
@@ -2915,6 +2941,54 @@ static int _am33xx_deassert_hardreset(struct omap_hwmod *oh,
|
|
|
oh->prcm.omap4.rstst_offs);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * _reidle - enable then idle a single hwmod
|
|
|
+ *
|
|
|
+ * enables and then immediately reidles an hwmod, as certain hwmods may
|
|
|
+ * not have their sysconfig registers programmed in an idle friendly state
|
|
|
+ * by default
|
|
|
+ */
|
|
|
+static void _reidle(struct omap_hwmod *oh)
|
|
|
+{
|
|
|
+ pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__);
|
|
|
+
|
|
|
+ omap_hwmod_enable(oh);
|
|
|
+ omap_hwmod_softreset(oh);
|
|
|
+ omap_hwmod_idle(oh);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _reidle_all - enable then idle all hwmods in oh_reidle_list
|
|
|
+ *
|
|
|
+ * Called by pm_notifier to make sure flagged modules do not block suspend
|
|
|
+ * after context loss.
|
|
|
+ */
|
|
|
+static int _reidle_all(void)
|
|
|
+{
|
|
|
+ struct omap_hwmod_list *oh_list_item = NULL;
|
|
|
+
|
|
|
+ list_for_each_entry(oh_list_item, &oh_reidle_list, oh_list) {
|
|
|
+ _reidle(oh_list_item->oh);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int _omap_device_pm_notifier(struct notifier_block *self,
|
|
|
+ unsigned long action, void *dev)
|
|
|
+{
|
|
|
+ switch (action) {
|
|
|
+ case PM_POST_SUSPEND:
|
|
|
+ _reidle_all();
|
|
|
+ }
|
|
|
+
|
|
|
+ return NOTIFY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+static struct notifier_block pm_nb = {
|
|
|
+ .notifier_call = _omap_device_pm_notifier,
|
|
|
+};
|
|
|
+
|
|
|
/* Public functions */
|
|
|
|
|
|
u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs)
|
|
|
@@ -3566,6 +3640,52 @@ static int __init omap_hwmod_setup_all(void)
|
|
|
}
|
|
|
omap_postcore_initcall(omap_hwmod_setup_all);
|
|
|
|
|
|
+/**
|
|
|
+ * omap_hwmod_enable_reidle - add an omap_hwmod to reidle list
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ *
|
|
|
+ * Adds the omap_hwmod to the oh_reidle_list so it will gets enabled then idled
|
|
|
+ * after each suspend cycle. Returns 0 on success.
|
|
|
+ */
|
|
|
+int omap_hwmod_enable_reidle(struct omap_hwmod *oh)
|
|
|
+{
|
|
|
+ struct omap_hwmod_list *oh_list_item = NULL;
|
|
|
+
|
|
|
+ oh_list_item = kzalloc(sizeof(*oh_list_item), GFP_KERNEL);
|
|
|
+
|
|
|
+ if (!oh_list_item)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ oh_list_item->oh = oh;
|
|
|
+ list_add(&oh_list_item->oh_list, &oh_reidle_list);
|
|
|
+
|
|
|
+ pr_debug("omap_hwmod: %s: added to reidle list\n", oh->name);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap_hwmod_disable_reidle - remove an omap_hwmod from reidle list
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ *
|
|
|
+ * Remove the omap_hwmod from the oh_reidle_list. Returns 0 on success.
|
|
|
+ */
|
|
|
+int omap_hwmod_disable_reidle(struct omap_hwmod *oh)
|
|
|
+{
|
|
|
+ struct omap_hwmod_list *li, *oh_list_item = NULL;
|
|
|
+
|
|
|
+ list_for_each_entry_safe(oh_list_item, li, &oh_reidle_list, oh_list) {
|
|
|
+ if (oh_list_item->oh == oh) {
|
|
|
+ list_del(&oh_list_item->oh_list);
|
|
|
+ pr_debug("omap_hwmod: %s: removed from reidle list\n",
|
|
|
+ oh->name);
|
|
|
+ kfree(oh_list_item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* omap_hwmod_enable - enable an omap_hwmod
|
|
|
* @oh: struct omap_hwmod *
|
|
|
@@ -3985,6 +4105,21 @@ void __init omap_hwmod_init(void)
|
|
|
inited = true;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * omap_hwmod_setup_reidle - add hwmods to reidle list and register notifier
|
|
|
+ *
|
|
|
+ * Returns 0 on success.
|
|
|
+ */
|
|
|
+int omap_hwmod_setup_reidle(void)
|
|
|
+{
|
|
|
+ omap_hwmod_for_each(_setup_reidle, NULL);
|
|
|
+
|
|
|
+ if (!list_empty(&oh_reidle_list))
|
|
|
+ register_pm_notifier(&pm_nb);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* omap_hwmod_get_main_clk - get pointer to main clock name
|
|
|
* @oh: struct omap_hwmod *
|