Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpuidle: psci: Move enabling OSI mode after power domains creation

A switch from OSI to PC mode is only possible if all CPUs other than the
calling one are OFF, either through a call to CPU_OFF or not yet booted.

Currently OSI mode is enabled before power domains are created. In cases
where CPUidle states are not using hierarchical CPU topology the bail out
path tries to switch back to PC mode which gets denied by firmware since
other CPUs are online at this point and creates inconsistent state as
firmware is in OSI mode and Linux in PC mode.

This change moves enabling OSI mode after power domains are created,
this would makes sure that hierarchical CPU topology is used before
switching firmware to OSI mode.

Cc: stable@vger.kernel.org
Fixes: 70c179b49870 ("cpuidle: psci: Allow PM domain to be initialized even if no OSI mode")
Signed-off-by: Maulik Shah <quic_mkshah@quicinc.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Maulik Shah and committed by
Ulf Hansson
12acb348 9a8fa00d

+13 -26
+13 -26
drivers/cpuidle/cpuidle-psci-domain.c
··· 120 120 } 121 121 } 122 122 123 - static bool psci_pd_try_set_osi_mode(void) 124 - { 125 - int ret; 126 - 127 - if (!psci_has_osi_support()) 128 - return false; 129 - 130 - ret = psci_set_osi_mode(true); 131 - if (ret) 132 - return false; 133 - 134 - return true; 135 - } 136 - 137 123 static void psci_cpuidle_domain_sync_state(struct device *dev) 138 124 { 139 125 /* ··· 138 152 { 139 153 struct device_node *np = pdev->dev.of_node; 140 154 struct device_node *node; 141 - bool use_osi; 155 + bool use_osi = psci_has_osi_support(); 142 156 int ret = 0, pd_count = 0; 143 157 144 158 if (!np) 145 159 return -ENODEV; 146 - 147 - /* If OSI mode is supported, let's try to enable it. */ 148 - use_osi = psci_pd_try_set_osi_mode(); 149 160 150 161 /* 151 162 * Parse child nodes for the "#power-domain-cells" property and ··· 153 170 continue; 154 171 155 172 ret = psci_pd_init(node, use_osi); 156 - if (ret) 157 - goto put_node; 173 + if (ret) { 174 + of_node_put(node); 175 + goto exit; 176 + } 158 177 159 178 pd_count++; 160 179 } 161 180 162 181 /* Bail out if not using the hierarchical CPU topology. */ 163 182 if (!pd_count) 164 - goto no_pd; 183 + return 0; 165 184 166 185 /* Link genpd masters/subdomains to model the CPU topology. */ 167 186 ret = dt_idle_pd_init_topology(np); 187 + if (ret) 188 + goto remove_pd; 189 + 190 + /* let's try to enable OSI. */ 191 + ret = psci_set_osi_mode(use_osi); 168 192 if (ret) 169 193 goto remove_pd; 170 194 ··· 179 189 use_osi ? "OSI" : "PC"); 180 190 return 0; 181 191 182 - put_node: 183 - of_node_put(node); 184 192 remove_pd: 193 + dt_idle_pd_remove_topology(np); 185 194 psci_pd_remove(); 195 + exit: 186 196 pr_err("failed to create CPU PM domains ret=%d\n", ret); 187 - no_pd: 188 - if (use_osi) 189 - psci_set_osi_mode(false); 190 197 return ret; 191 198 } 192 199