===================================================================
@@ -3,6 +3,7 @@
*
* Copyright (C) 2012 Renesas Solutions Corp.
* Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
*
* based on pm-sh7372.c
* Copyright (C) 2011 Magnus Damm
@@ -13,7 +14,9 @@
*/
#include <linux/console.h>
#include <linux/delay.h>
+#include <linux/notifier.h>
#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include <linux/pm.h>
#include <linux/pm_clock.h>
#include <asm/io.h>
@@ -149,21 +152,92 @@ static void rmobile_init_pm_domain(struc
__rmobile_pd_power_up(rmobile_pd, false);
}
-void rmobile_init_domains(struct rmobile_pm_domain domains[], int num)
-{
- int j;
-
- for (j = 0; j < num; j++)
- rmobile_init_pm_domain(&domains[j]);
-}
-
void rmobile_add_device_to_domain(const char *domain_name,
struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ int ret;
- pm_genpd_name_add_device(domain_name, dev);
+ do
+ ret = pm_genpd_name_add_device(domain_name, dev);
+ while (ret == -EAGAIN);
if (pm_clk_no_clocks(dev))
pm_clk_add(dev, NULL);
}
+
+#ifdef CONFIG_USE_OF
+
+static void rmobile_read_domain_from_dt(struct device *dev)
+{
+ const char *domain_name;
+ int ret;
+
+ ret = of_property_read_string(dev->of_node, "renesas,pmdomain",
+ &domain_name);
+ if (!ret)
+ rmobile_add_device_to_domain(domain_name,
+ to_platform_device(dev));
+}
+
+static void rmobile_remove_from_domain(struct device *dev)
+{
+ struct generic_pm_domain *genpd = dev_to_genpd(dev);
+ int ret;
+
+ /*
+ * The check below takes care of the situations in which the device's
+ * pm_domain pointer contains a valid address, but that is not an
+ * address of a generic PM domain object.
+ */
+ if (pm_genpd_present(genpd)) {
+ do
+ ret = pm_genpd_remove_device(genpd, dev);
+ while (ret == -EAGAIN);
+ }
+}
+
+static int rmobile_pm_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct device *dev = data;
+
+ switch (event) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->of_node)
+ rmobile_read_domain_from_dt(dev);
+
+ break;
+
+ case BUS_NOTIFY_DEL_DEVICE:
+ rmobile_remove_from_domain(dev);
+
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block platform_nb = {
+ .notifier_call = rmobile_pm_notifier_call,
+};
+
+static void rmobile_add_bus_notifier_for_domains(void)
+{
+ bus_register_notifier(&platform_bus_type, &platform_nb);
+}
+
+#else
+
+static inline void rmobile_add_bus_notifier_for_domains(void) {}
+
+#endif /* CONFIG_USE_OF */
+
+void rmobile_init_domains(struct rmobile_pm_domain domains[], int num)
+{
+ int j;
+
+ for (j = 0; j < num; j++)
+ rmobile_init_pm_domain(&domains[j]);
+
+ rmobile_add_bus_notifier_for_domains();
+}
#endif /* CONFIG_PM */