MLK-22360 soc: imx8qm/imx8qxp: Fix power state of parent power domains
authorRanjani Vaidyanathan <ranjani.vaidyanathan@nxp.com>
Wed, 17 Jul 2019 16:33:46 +0000 (11:33 -0500)
committerRanjani Vaidyanathan <ranjani.vaidyanathan@nxp.com>
Mon, 5 Aug 2019 18:11:32 +0000 (13:11 -0500)
Fix power state of parent power domains that have no device
associated with them.

Current power domain driver does not work well in two cases:
1. A device is controlled by multiple power domains
2. Multiple devices are controlled by a single power domain that
is dependent on another power domain(s).

This patch attempts to fix these two issues.

Signed-off-by: Ranjani Vaidyanathan <ranjani.vaidyanathan@nxp.com>
drivers/soc/imx/pm-domain-imx8.h
drivers/soc/imx/pm-domains.c

index a9489a8..e4bd207 100644 (file)
@@ -38,7 +38,7 @@ struct imx8_pm_domain {
        int (*suspend)(void);
        void (*resume)(void);
        sc_rsrc_t rsrc_id;
-       bool runtime_idle_active;
+       bool dev_attached;
        struct list_head clks;
 
        /* indicate the possible clk state lost */
index 0ea07eb..b971f23 100644 (file)
@@ -84,12 +84,12 @@ static int imx8_pd_power(struct generic_pm_domain *domain, bool power_on)
 {
        struct imx8_pm_domain *pd;
        sc_err_t sci_err = SC_ERR_NONE;
+       unsigned int pd_state;
 
        pd = container_of(domain, struct imx8_pm_domain, pd);
 
        if (pd->rsrc_id == SC_R_NONE)
                return 0;
-
        /* keep uart console power on for no_console_suspend */
        if (pd->rsrc_id == rsrc_debug_console &&
                !console_suspend_enabled && !power_on)
@@ -99,15 +99,67 @@ static int imx8_pd_power(struct generic_pm_domain *domain, bool power_on)
                if (is_resume_needed(domain))
                        return 0;
 
-       sci_err = sc_pm_set_resource_power_mode(pm_ipc_handle, pd->rsrc_id,
-               (power_on) ? SC_PM_PW_MODE_ON :
-               pd->pd.state_idx ? SC_PM_PW_MODE_OFF : SC_PM_PW_MODE_LP);
-       if (sci_err) {
-               pr_err("Failed power operation on resource %d sc_err %d\n",
-                               pd->rsrc_id, sci_err);
-               return -EINVAL;
-       }
+       /*
+        * Power domain with no device associated with it is handled
+        * when the child power domain is powered off/lp.
+        */
+       if (!power_on && !domain->device_count && pd->dev_attached)
+               return 0;
+
+       pd_state = pd->pd.state_idx;
 
+       if (power_on) {
+               sci_err = sc_pm_set_resource_power_mode(pm_ipc_handle,
+                                       pd->rsrc_id, SC_PM_PW_MODE_ON);
+               if (sci_err) {
+                       pr_err("Failed power operation on resource %d sc_err %d\n",
+                                       pd->rsrc_id, sci_err);
+                       return -EINVAL;
+               }
+       } else {
+               struct generic_pm_domain *cur_domain = domain, *master = NULL;
+
+               /*
+                * Set the state of a parent power domain that has no device
+                * associated with it to be the same as the child.
+                * Walk up the power domain tree from the child node to the
+                * top parent.
+                */
+               while (!list_empty(&cur_domain->slave_links)) {
+                       struct gpd_link *link;
+
+                       pd = container_of(cur_domain, struct imx8_pm_domain, pd);
+                       if ((cur_domain == domain) || (!cur_domain->device_count)) {
+                               sci_err = sc_pm_set_resource_power_mode(pm_ipc_handle,
+                                               pd->rsrc_id,
+                                               (pd_state) ? SC_PM_PW_MODE_OFF : SC_PM_PW_MODE_LP);
+                               if (sci_err) {
+                                       pr_err("Failed power operation on resource %d sc_err %d\n",
+                                                       pd->rsrc_id, sci_err);
+                                       return -EINVAL;
+                               }
+                       }
+
+                       list_for_each_entry(link, &cur_domain->slave_links, slave_node)
+                               master = link->master;
+
+                       cur_domain = master;
+               }
+               /* Fix the state for the top parent. */
+               pd = container_of(cur_domain, struct imx8_pm_domain, pd);
+               if (pd->rsrc_id != SC_R_NONE) {
+                       sci_err = sc_pm_set_resource_power_mode(pm_ipc_handle,
+                                               pd->rsrc_id,
+                                               (pd_state) ? SC_PM_PW_MODE_OFF : SC_PM_PW_MODE_LP);
+                       if (sci_err) {
+                               pr_err("Failed power operation on resource %d sc_err %d\n",
+                                               pd->rsrc_id, sci_err);
+                               return -EINVAL;
+                       }
+               }
+
+
+       }
        /* keep HDMI TX resource power on */
        if (power_on && (pd->rsrc_id == SC_R_HDMI ||
                                        pd->rsrc_id == SC_R_HDMI_I2S ||
@@ -243,6 +295,7 @@ static int imx8_attach_dev(struct generic_pm_domain *genpd, struct device *dev)
 
        pd = container_of(genpd, struct imx8_pm_domain, pd);
 
+       pd->dev_attached = true;
        num_clks = of_count_phandle_with_args(node, "assigned-clocks",
                                                "#clock-cells");
        if (num_clks == -EINVAL)
@@ -283,6 +336,7 @@ static void imx8_detach_dev(struct generic_pm_domain *genpd, struct device *dev)
        struct imx8_pm_rsrc_clks *imx8_rsrc_clk, *tmp;
 
        pd = container_of(genpd, struct imx8_pm_domain, pd);
+       pd->dev_attached = false;
 
        /* Free all the clock entry nodes. */
        if (list_empty(&pd->clks))
@@ -353,6 +407,7 @@ static void imx8_pd_setup(struct imx8_pm_domain *pd)
        pd->pd.states[1].power_on_latency_ns =  2500000;
 
        pd->pd.state_count = 2;
+       pd->dev_attached = true;
 }
 
 static int __init imx8_add_pm_domains(struct device_node *parent,