index cd65ea4e9c54e633bd66a0178ca3f06ad16e8db9..56c452db430a3c237e7e439e8afae2336b8029a7 100644 (file)
#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>
/* 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;
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
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)
}
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 *
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 *