| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * POWERNV cpufreq driver for the IBM POWER processors |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * (C) Copyright IBM 2014 |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Author: Vaidyanathan Srinivasan <svaidy at linux.vnet.ibm.com> |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 10 | | - * the Free Software Foundation; either version 2, or (at your option) |
|---|
| 11 | | - * any later version. |
|---|
| 12 | | - * |
|---|
| 13 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | | - * GNU General Public License for more details. |
|---|
| 17 | | - * |
|---|
| 18 | 8 | */ |
|---|
| 19 | 9 | |
|---|
| 20 | 10 | #define pr_fmt(fmt) "powernv-cpufreq: " fmt |
|---|
| .. | .. |
|---|
| 75 | 65 | * highest_lpstate_idx |
|---|
| 76 | 66 | * @last_sampled_time: Time from boot in ms when global pstates were |
|---|
| 77 | 67 | * last set |
|---|
| 78 | | - * @last_lpstate_idx, Last set value of local pstate and global |
|---|
| 79 | | - * last_gpstate_idx pstate in terms of cpufreq table index |
|---|
| 68 | + * @last_lpstate_idx: Last set value of local pstate and global |
|---|
| 69 | + * @last_gpstate_idx: pstate in terms of cpufreq table index |
|---|
| 80 | 70 | * @timer: Is used for ramping down if cpu goes idle for |
|---|
| 81 | 71 | * a long time with global pstate held high |
|---|
| 82 | 72 | * @gpstate_lock: A spinlock to maintain synchronization between |
|---|
| 83 | 73 | * routines called by the timer handler and |
|---|
| 84 | 74 | * governer's target_index calls |
|---|
| 75 | + * @policy: Associated CPUFreq policy |
|---|
| 85 | 76 | */ |
|---|
| 86 | 77 | struct global_pstate_info { |
|---|
| 87 | 78 | int highest_lpstate_idx; |
|---|
| .. | .. |
|---|
| 96 | 87 | |
|---|
| 97 | 88 | static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; |
|---|
| 98 | 89 | |
|---|
| 99 | | -DEFINE_HASHTABLE(pstate_revmap, POWERNV_MAX_PSTATES_ORDER); |
|---|
| 90 | +static DEFINE_HASHTABLE(pstate_revmap, POWERNV_MAX_PSTATES_ORDER); |
|---|
| 100 | 91 | /** |
|---|
| 101 | 92 | * struct pstate_idx_revmap_data: Entry in the hashmap pstate_revmap |
|---|
| 102 | 93 | * indexed by a function of pstate id. |
|---|
| .. | .. |
|---|
| 181 | 172 | |
|---|
| 182 | 173 | /* Use following functions for conversions between pstate_id and index */ |
|---|
| 183 | 174 | |
|---|
| 184 | | -/** |
|---|
| 175 | +/* |
|---|
| 185 | 176 | * idx_to_pstate : Returns the pstate id corresponding to the |
|---|
| 186 | 177 | * frequency in the cpufreq frequency table |
|---|
| 187 | 178 | * powernv_freqs indexed by @i. |
|---|
| .. | .. |
|---|
| 199 | 190 | return powernv_freqs[i].driver_data; |
|---|
| 200 | 191 | } |
|---|
| 201 | 192 | |
|---|
| 202 | | -/** |
|---|
| 193 | +/* |
|---|
| 203 | 194 | * pstate_to_idx : Returns the index in the cpufreq frequencytable |
|---|
| 204 | 195 | * powernv_freqs for the frequency whose corresponding |
|---|
| 205 | 196 | * pstate id is @pstate. |
|---|
| .. | .. |
|---|
| 245 | 236 | u32 len_ids, len_freqs; |
|---|
| 246 | 237 | u32 pstate_min, pstate_max, pstate_nominal; |
|---|
| 247 | 238 | u32 pstate_turbo, pstate_ultra_turbo; |
|---|
| 239 | + int rc = -ENODEV; |
|---|
| 248 | 240 | |
|---|
| 249 | 241 | power_mgt = of_find_node_by_path("/ibm,opal/power-mgt"); |
|---|
| 250 | 242 | if (!power_mgt) { |
|---|
| .. | .. |
|---|
| 254 | 246 | |
|---|
| 255 | 247 | if (of_property_read_u32(power_mgt, "ibm,pstate-min", &pstate_min)) { |
|---|
| 256 | 248 | pr_warn("ibm,pstate-min node not found\n"); |
|---|
| 257 | | - return -ENODEV; |
|---|
| 249 | + goto out; |
|---|
| 258 | 250 | } |
|---|
| 259 | 251 | |
|---|
| 260 | 252 | if (of_property_read_u32(power_mgt, "ibm,pstate-max", &pstate_max)) { |
|---|
| 261 | 253 | pr_warn("ibm,pstate-max node not found\n"); |
|---|
| 262 | | - return -ENODEV; |
|---|
| 254 | + goto out; |
|---|
| 263 | 255 | } |
|---|
| 264 | 256 | |
|---|
| 265 | 257 | if (of_property_read_u32(power_mgt, "ibm,pstate-nominal", |
|---|
| 266 | 258 | &pstate_nominal)) { |
|---|
| 267 | 259 | pr_warn("ibm,pstate-nominal not found\n"); |
|---|
| 268 | | - return -ENODEV; |
|---|
| 260 | + goto out; |
|---|
| 269 | 261 | } |
|---|
| 270 | 262 | |
|---|
| 271 | 263 | if (of_property_read_u32(power_mgt, "ibm,pstate-ultra-turbo", |
|---|
| .. | .. |
|---|
| 294 | 286 | pstate_ids = of_get_property(power_mgt, "ibm,pstate-ids", &len_ids); |
|---|
| 295 | 287 | if (!pstate_ids) { |
|---|
| 296 | 288 | pr_warn("ibm,pstate-ids not found\n"); |
|---|
| 297 | | - return -ENODEV; |
|---|
| 289 | + goto out; |
|---|
| 298 | 290 | } |
|---|
| 299 | 291 | |
|---|
| 300 | 292 | pstate_freqs = of_get_property(power_mgt, "ibm,pstate-frequencies-mhz", |
|---|
| 301 | 293 | &len_freqs); |
|---|
| 302 | 294 | if (!pstate_freqs) { |
|---|
| 303 | 295 | pr_warn("ibm,pstate-frequencies-mhz not found\n"); |
|---|
| 304 | | - return -ENODEV; |
|---|
| 296 | + goto out; |
|---|
| 305 | 297 | } |
|---|
| 306 | 298 | |
|---|
| 307 | 299 | if (len_ids != len_freqs) { |
|---|
| .. | .. |
|---|
| 312 | 304 | nr_pstates = min(len_ids, len_freqs) / sizeof(u32); |
|---|
| 313 | 305 | if (!nr_pstates) { |
|---|
| 314 | 306 | pr_warn("No PStates found\n"); |
|---|
| 315 | | - return -ENODEV; |
|---|
| 307 | + goto out; |
|---|
| 316 | 308 | } |
|---|
| 317 | 309 | |
|---|
| 318 | 310 | powernv_pstate_info.nr_pstates = nr_pstates; |
|---|
| .. | .. |
|---|
| 328 | 320 | powernv_freqs[i].frequency = freq * 1000; /* kHz */ |
|---|
| 329 | 321 | powernv_freqs[i].driver_data = id & 0xFF; |
|---|
| 330 | 322 | |
|---|
| 331 | | - revmap_data = (struct pstate_idx_revmap_data *) |
|---|
| 332 | | - kmalloc(sizeof(*revmap_data), GFP_KERNEL); |
|---|
| 323 | + revmap_data = kmalloc(sizeof(*revmap_data), GFP_KERNEL); |
|---|
| 324 | + if (!revmap_data) { |
|---|
| 325 | + rc = -ENOMEM; |
|---|
| 326 | + goto out; |
|---|
| 327 | + } |
|---|
| 333 | 328 | |
|---|
| 334 | 329 | revmap_data->pstate_id = id & 0xFF; |
|---|
| 335 | 330 | revmap_data->cpufreq_table_idx = i; |
|---|
| .. | .. |
|---|
| 353 | 348 | |
|---|
| 354 | 349 | /* End of list marker entry */ |
|---|
| 355 | 350 | powernv_freqs[i].frequency = CPUFREQ_TABLE_END; |
|---|
| 351 | + |
|---|
| 352 | + of_node_put(power_mgt); |
|---|
| 356 | 353 | return 0; |
|---|
| 354 | +out: |
|---|
| 355 | + of_node_put(power_mgt); |
|---|
| 356 | + return rc; |
|---|
| 357 | 357 | } |
|---|
| 358 | 358 | |
|---|
| 359 | 359 | /* Returns the CPU frequency corresponding to the pstate_id. */ |
|---|
| .. | .. |
|---|
| 382 | 382 | powernv_freqs[powernv_pstate_info.nominal].frequency); |
|---|
| 383 | 383 | } |
|---|
| 384 | 384 | |
|---|
| 385 | | -struct freq_attr cpufreq_freq_attr_cpuinfo_nominal_freq = |
|---|
| 385 | +static struct freq_attr cpufreq_freq_attr_cpuinfo_nominal_freq = |
|---|
| 386 | 386 | __ATTR_RO(cpuinfo_nominal_freq); |
|---|
| 387 | 387 | |
|---|
| 388 | 388 | #define SCALING_BOOST_FREQS_ATTR_INDEX 2 |
|---|
| .. | .. |
|---|
| 662 | 662 | /** |
|---|
| 663 | 663 | * gpstate_timer_handler |
|---|
| 664 | 664 | * |
|---|
| 665 | | - * @data: pointer to cpufreq_policy on which timer was queued |
|---|
| 665 | + * @t: Timer context used to fetch global pstate info struct |
|---|
| 666 | 666 | * |
|---|
| 667 | 667 | * This handler brings down the global pstate closer to the local pstate |
|---|
| 668 | 668 | * according quadratic equation. Queues a new timer if it is still not equal |
|---|
| 669 | 669 | * to local pstate |
|---|
| 670 | 670 | */ |
|---|
| 671 | | -void gpstate_timer_handler(struct timer_list *t) |
|---|
| 671 | +static void gpstate_timer_handler(struct timer_list *t) |
|---|
| 672 | 672 | { |
|---|
| 673 | 673 | struct global_pstate_info *gpstates = from_timer(gpstates, t, timer); |
|---|
| 674 | 674 | struct cpufreq_policy *policy = gpstates->policy; |
|---|
| .. | .. |
|---|
| 904 | 904 | .notifier_call = powernv_cpufreq_reboot_notifier, |
|---|
| 905 | 905 | }; |
|---|
| 906 | 906 | |
|---|
| 907 | | -void powernv_cpufreq_work_fn(struct work_struct *work) |
|---|
| 907 | +static void powernv_cpufreq_work_fn(struct work_struct *work) |
|---|
| 908 | 908 | { |
|---|
| 909 | 909 | struct chip *chip = container_of(work, struct chip, throttle); |
|---|
| 910 | 910 | struct cpufreq_policy *policy; |
|---|
| .. | .. |
|---|
| 1133 | 1133 | if (rc) |
|---|
| 1134 | 1134 | goto out; |
|---|
| 1135 | 1135 | |
|---|
| 1136 | | - register_reboot_notifier(&powernv_cpufreq_reboot_nb); |
|---|
| 1137 | | - opal_message_notifier_register(OPAL_MSG_OCC, &powernv_cpufreq_opal_nb); |
|---|
| 1138 | | - |
|---|
| 1139 | 1136 | if (powernv_pstate_info.wof_enabled) |
|---|
| 1140 | 1137 | powernv_cpufreq_driver.boost_enabled = true; |
|---|
| 1141 | 1138 | else |
|---|
| .. | .. |
|---|
| 1144 | 1141 | rc = cpufreq_register_driver(&powernv_cpufreq_driver); |
|---|
| 1145 | 1142 | if (rc) { |
|---|
| 1146 | 1143 | pr_info("Failed to register the cpufreq driver (%d)\n", rc); |
|---|
| 1147 | | - goto cleanup_notifiers; |
|---|
| 1144 | + goto cleanup; |
|---|
| 1148 | 1145 | } |
|---|
| 1149 | 1146 | |
|---|
| 1150 | 1147 | if (powernv_pstate_info.wof_enabled) |
|---|
| 1151 | 1148 | cpufreq_enable_boost_support(); |
|---|
| 1152 | 1149 | |
|---|
| 1150 | + register_reboot_notifier(&powernv_cpufreq_reboot_nb); |
|---|
| 1151 | + opal_message_notifier_register(OPAL_MSG_OCC, &powernv_cpufreq_opal_nb); |
|---|
| 1152 | + |
|---|
| 1153 | 1153 | return 0; |
|---|
| 1154 | | -cleanup_notifiers: |
|---|
| 1155 | | - unregister_all_notifiers(); |
|---|
| 1154 | +cleanup: |
|---|
| 1156 | 1155 | clean_chip_info(); |
|---|
| 1157 | 1156 | out: |
|---|
| 1158 | 1157 | pr_info("Platform driver disabled. System does not support PState control\n"); |
|---|