Pull more power management updates from Rafael Wysocki: "These are mostly ARM cpufreq driver updates plus a cpufreq core cleanup, an ARM-wide change to make schedutil the default scaling governor, an intel_pstate driver fix and some runtime PM changes regarding kerneldoc comments. Specifics: - Add adaptive voltage scaling (AVS) support to the brcmstb cpufreq driver and clean it up (Florian Fainelli, Markus Mayer). - Add a new Tegra cpufreq driver and clean up the existing one (Jon Hunter, Sumit Gupta). - Add bandwidth level support to the Qcom cpufreq driver along with OPP changes (Sibi Sankar). - Clean up the sti, cpufreq-dt, ap806, CPPC cpufreq drivers (Viresh Kumar, Lee Jones, Ivan Kokshaysky, Sven Auhagen, Xin Hao). - Make schedutil the default governor for ARM (Valentin Schneider). - Fix dependency issues for the imx cpufreq driver (Walter Lozano). - Clean up cached_resolved_idx handlihng in the cpufreq core (Viresh Kumar). - Fix the intel_pstate driver to use the correct maximum frequency value when MSR_TURBO_RATIO_LIMIT is 0 (Srinivas Pandruvada). - Provide kenrneldoc comments for multiple runtime PM helpers and improve the pm_runtime_get_if_active() kerneldoc (Rafael Wysocki)" * tag 'pm-5.9-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (22 commits) cpufreq: intel_pstate: Fix cpuinfo_max_freq when MSR_TURBO_RATIO_LIMIT is 0 PM: runtime: Improve kerneldoc of pm_runtime_get_if_active() PM: runtime: Add kerneldoc comments to multiple helpers cpufreq: make schedutil the default for arm and arm64 cpufreq: cached_resolved_idx can not be negative cpufreq: Add Tegra194 cpufreq driver dt-bindings: arm: Add NVIDIA Tegra194 CPU Complex binding cpufreq: imx: Select NVMEM_IMX_OCOTP cpufreq: sti-cpufreq: Fix some formatting and misspelling issues cpufreq: tegra186: Simplify probe return path cpufreq: CPPC: Reuse caps variable in few routines cpufreq: ap806: fix cpufreq driver needs ap cpu clk cpufreq: cppc: Reorder code and remove apply_hisi_workaround variable cpufreq: dt: fix oops on armada37xx cpufreq: brcmstb-avs-cpufreq: send S2_ENTER / S2_EXIT commands to AVS cpufreq: brcmstb-avs-cpufreq: Support polling AVS firmware cpufreq: brcmstb-avs-cpufreq: more flexible interface for __issue_avs_command() cpufreq: qcom: Disable fast switch when scaling DDR/L3 cpufreq: qcom: Update the bandwidth levels on frequency change OPP: Add and export helper to set bandwidth ...
Unified · Split
| @@ -0,0 +1,69 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: "http://devicetree.org/schemas/arm/nvidia,tegra194-ccplex.yaml#" | ||
| 5 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | ||
| 6 | |||
| 7 | title: NVIDIA Tegra194 CPU Complex device tree bindings | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Thierry Reding <thierry.reding@gmail.com> | ||
| 11 | - Jonathan Hunter <jonathanh@nvidia.com> | ||
| 12 | - Sumit Gupta <sumitg@nvidia.com> | ||
| 13 | |||
| 14 | description: |+ | ||
| 15 | Tegra194 SOC has homogeneous architecture where each cluster has two | ||
| 16 | symmetric cores. Compatible string in "cpus" node represents the CPU | ||
| 17 | Complex having all clusters. | ||
| 18 | |||
| 19 | properties: | ||
| 20 | $nodename: | ||
| 21 | const: cpus | ||
| 22 | |||
| 23 | compatible: | ||
| 24 | enum: | ||
| 25 | - nvidia,tegra194-ccplex | ||
| 26 | |||
| 27 | nvidia,bpmp: | ||
| 28 | $ref: '/schemas/types.yaml#/definitions/phandle' | ||
| 29 | description: | | ||
| 30 | Specifies the bpmp node that needs to be queried to get | ||
| 31 | operating point data for all CPUs. | ||
| 32 | |||
| 33 | examples: | ||
| 34 | - | | ||
| 35 | cpus { | ||
| 36 | compatible = "nvidia,tegra194-ccplex"; | ||
| 37 | nvidia,bpmp = <&bpmp>; | ||
| 38 | #address-cells = <1>; | ||
| 39 | #size-cells = <0>; | ||
| 40 | |||
| 41 | cpu0_0: cpu@0 { | ||
| 42 | compatible = "nvidia,tegra194-carmel"; | ||
| 43 | device_type = "cpu"; | ||
| 44 | reg = <0x0>; | ||
| 45 | enable-method = "psci"; | ||
| 46 | }; | ||
| 47 | |||
| 48 | cpu0_1: cpu@1 { | ||
| 49 | compatible = "nvidia,tegra194-carmel"; | ||
| 50 | device_type = "cpu"; | ||
| 51 | reg = <0x001>; | ||
| 52 | enable-method = "psci"; | ||
| 53 | }; | ||
| 54 | |||
| 55 | cpu1_0: cpu@100 { | ||
| 56 | compatible = "nvidia,tegra194-carmel"; | ||
| 57 | device_type = "cpu"; | ||
| 58 | reg = <0x100>; | ||
| 59 | enable-method = "psci"; | ||
| 60 | }; | ||
| 61 | |||
| 62 | cpu1_1: cpu@101 { | ||
| 63 | compatible = "nvidia,tegra194-carmel"; | ||
| 64 | device_type = "cpu"; | ||
| 65 | reg = <0x101>; | ||
| 66 | enable-method = "psci"; | ||
| 67 | }; | ||
| 68 | }; | ||
| 69 | ... | ||
| @@ -1085,24 +1085,26 @@ int __pm_runtime_resume(struct device *dev, int rpmflags) · 1084 unmodified lines | |||
| 1085 | EXPORT_SYMBOL_GPL(__pm_runtime_resume); | 1085 | EXPORT_SYMBOL_GPL(__pm_runtime_resume); |
| 1086 | 1086 | ||
| 1087 | /** | 1087 | /** |
| 1088 | * pm_runtime_get_if_active - Conditionally bump up the device's usage counter. | 1088 | * pm_runtime_get_if_active - Conditionally bump up device usage counter. |
| 1089 | * @dev: Device to handle. | 1089 | * @dev: Device to handle. |
| 1090 | * @ign_usage_count: Whether or not to look at the current usage counter value. | ||
| 1090 | * | 1091 | * |
| 1091 | * Return -EINVAL if runtime PM is disabled for the device. | 1092 | * Return -EINVAL if runtime PM is disabled for @dev. |
| 1092 | * | 1093 | * |
| 1093 | * Otherwise, if the device's runtime PM status is RPM_ACTIVE and either | 1094 | * Otherwise, if the runtime PM status of @dev is %RPM_ACTIVE and either |
| 1094 | * ign_usage_count is true or the device's usage_count is non-zero, increment | 1095 | * @ign_usage_count is %true or the runtime PM usage counter of @dev is not |
| 1095 | * the counter and return 1. Otherwise return 0 without changing the counter. | 1096 | * zero, increment the usage counter of @dev and return 1. Otherwise, return 0 |
| 1097 | * without changing the usage counter. | ||
| 1096 | * | 1098 | * |
| 1097 | * If ign_usage_count is true, the function can be used to prevent suspending | 1099 | * If @ign_usage_count is %true, this function can be used to prevent suspending |
| 1098 | * the device when its runtime PM status is RPM_ACTIVE. | 1100 | * the device when its runtime PM status is %RPM_ACTIVE. |
| 1099 | * | 1101 | * |
| 1100 | * If ign_usage_count is false, the function can be used to prevent suspending | 1102 | * If @ign_usage_count is %false, this function can be used to prevent |
| 1101 | * the device when both its runtime PM status is RPM_ACTIVE and its usage_count | 1103 | * suspending the device when both its runtime PM status is %RPM_ACTIVE and its |
| 1102 | * is non-zero. | 1104 | * runtime PM usage counter is not zero. |
| 1103 | * | 1105 | * |
| 1104 | * The caller is resposible for putting the device's usage count when ther | 1106 | * The caller is resposible for decrementing the runtime PM usage counter of |
| 1105 | * return value is greater than zero. | 1107 | * @dev after this function has returned a positive value for it. |
| 1106 | */ | 1108 | */ |
| 1107 | int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count) | 1109 | int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count) |
| 1108 | { | 1110 | { |
| @@ -37,7 +37,7 @@ config CPU_FREQ_STAT · 36 unmodified lines | |||
| 37 | choice | 37 | choice |
| 38 | prompt "Default CPUFreq governor" | 38 | prompt "Default CPUFreq governor" |
| 39 | default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ | 39 | default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ |
| 40 | default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if BIG_LITTLE | 40 | default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if ARM64 || ARM |
| 41 | default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if X86_INTEL_PSTATE && SMP | 41 | default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if X86_INTEL_PSTATE && SMP |
| 42 | default CPU_FREQ_DEFAULT_GOV_PERFORMANCE | 42 | default CPU_FREQ_DEFAULT_GOV_PERFORMANCE |
| 43 | help | 43 | help |
| @@ -41,6 +41,7 @@ config ARM_ARMADA_37XX_CPUFREQ · 40 unmodified lines | |||
| 41 | config ARM_ARMADA_8K_CPUFREQ | 41 | config ARM_ARMADA_8K_CPUFREQ |
| 42 | tristate "Armada 8K CPUFreq driver" | 42 | tristate "Armada 8K CPUFreq driver" |
| 43 | depends on ARCH_MVEBU && CPUFREQ_DT | 43 | depends on ARCH_MVEBU && CPUFREQ_DT |
| 44 | select ARMADA_AP_CPU_CLK | ||
| 44 | help | 45 | help |
| 45 | This enables the CPUFreq driver support for Marvell | 46 | This enables the CPUFreq driver support for Marvell |
| 46 | Armada8k SOCs. | 47 | Armada8k SOCs. |
| @@ -93,6 +94,7 @@ config ARM_IMX6Q_CPUFREQ · 46 unmodified lines | |||
| 93 | tristate "Freescale i.MX6 cpufreq support" | 94 | tristate "Freescale i.MX6 cpufreq support" |
| 94 | depends on ARCH_MXC | 95 | depends on ARCH_MXC |
| 95 | depends on REGULATOR_ANATOP | 96 | depends on REGULATOR_ANATOP |
| 97 | select NVMEM_IMX_OCOTP | ||
| 96 | select PM_OPP | 98 | select PM_OPP |
| 97 | help | 99 | help |
| 98 | This adds cpufreq driver support for Freescale i.MX6 series SoCs. | 100 | This adds cpufreq driver support for Freescale i.MX6 series SoCs. |
| @@ -314,6 +316,13 @@ config ARM_TEGRA186_CPUFREQ · 215 unmodified lines | |||
| 314 | help | 316 | help |
| 315 | This adds the CPUFreq driver support for Tegra186 SOCs. | 317 | This adds the CPUFreq driver support for Tegra186 SOCs. |
| 316 | 318 | ||
| 319 | config ARM_TEGRA194_CPUFREQ | ||
| 320 | tristate "Tegra194 CPUFreq support" | ||
| 321 | depends on ARCH_TEGRA_194_SOC && TEGRA_BPMP | ||
| 322 | default y | ||
| 323 | help | ||
| 324 | This adds CPU frequency driver support for Tegra194 SOCs. | ||
| 325 | |||
| 317 | config ARM_TI_CPUFREQ | 326 | config ARM_TI_CPUFREQ |
| 318 | bool "Texas Instruments CPUFreq support" | 327 | bool "Texas Instruments CPUFreq support" |
| 319 | depends on ARCH_OMAP2PLUS | 328 | depends on ARCH_OMAP2PLUS |
| @@ -83,6 +83,7 @@ obj-$(CONFIG_ARM_TANGO_CPUFREQ) += tango-cpufreq.o · 82 unmodified lines | |||
| 83 | obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o | 83 | obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o |
| 84 | obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o | 84 | obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o |
| 85 | obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o | 85 | obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o |
| 86 | obj-$(CONFIG_ARM_TEGRA194_CPUFREQ) += tegra194-cpufreq.o | ||
| 86 | obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o | 87 | obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o |
| 87 | obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o | 88 | obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o |
| 88 | 89 | ||
| @@ -456,6 +456,7 @@ static int __init armada37xx_cpufreq_driver_init(void) · 455 unmodified lines | |||
| 456 | /* Now that everything is setup, enable the DVFS at hardware level */ | 456 | /* Now that everything is setup, enable the DVFS at hardware level */ |
| 457 | armada37xx_cpufreq_enable_dvfs(nb_pm_base); | 457 | armada37xx_cpufreq_enable_dvfs(nb_pm_base); |
| 458 | 458 | ||
| 459 | memset(&pdata, 0, sizeof(pdata)); | ||
| 459 | pdata.suspend = armada37xx_cpufreq_suspend; | 460 | pdata.suspend = armada37xx_cpufreq_suspend; |
| 460 | pdata.resume = armada37xx_cpufreq_resume; | 461 | pdata.resume = armada37xx_cpufreq_resume; |
| 461 | 462 | ||
| @@ -42,6 +42,7 @@ · 41 unmodified lines | |||
| 42 | */ | 42 | */ |
| 43 | 43 | ||
| 44 | #include <linux/cpufreq.h> | 44 | #include <linux/cpufreq.h> |
| 45 | #include <linux/delay.h> | ||
| 45 | #include <linux/interrupt.h> | 46 | #include <linux/interrupt.h> |
| 46 | #include <linux/io.h> | 47 | #include <linux/io.h> |
| 47 | #include <linux/module.h> | 48 | #include <linux/module.h> |
| @@ -178,6 +179,7 @@ struct private_data { · 130 unmodified lines | |||
| 178 | struct completion done; | 179 | struct completion done; |
| 179 | struct semaphore sem; | 180 | struct semaphore sem; |
| 180 | struct pmap pmap; | 181 | struct pmap pmap; |
| 182 | int host_irq; | ||
| 181 | }; | 183 | }; |
| 182 | 184 | ||
| 183 | static void __iomem *__map_region(const char *name) | 185 | static void __iomem *__map_region(const char *name) |
| @@ -195,11 +197,36 @@ static void __iomem *__map_region(const char *name) · 11 unmodified lines | |||
| 195 | return ptr; | 197 | return ptr; |
| 196 | } | 198 | } |
| 197 | 199 | ||
| 198 | static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, | 200 | static unsigned long wait_for_avs_command(struct private_data *priv, |
| 201 | unsigned long timeout) | ||
| 202 | { | ||
| 203 | unsigned long time_left = 0; | ||
| 204 | u32 val; | ||
| 205 | |||
| 206 | /* Event driven, wait for the command interrupt */ | ||
| 207 | if (priv->host_irq >= 0) | ||
| 208 | return wait_for_completion_timeout(&priv->done, | ||
| 209 | msecs_to_jiffies(timeout)); | ||
| 210 | |||
| 211 | /* Polling for command completion */ | ||
| 212 | do { | ||
| 213 | time_left = timeout; | ||
| 214 | val = readl(priv->base + AVS_MBOX_STATUS); | ||
| 215 | if (val) | ||
| 216 | break; | ||
| 217 | |||
| 218 | usleep_range(1000, 2000); | ||
| 219 | } while (--timeout); | ||
| 220 | |||
| 221 | return time_left; | ||
| 222 | } | ||
| 223 | |||
| 224 | static int __issue_avs_command(struct private_data *priv, unsigned int cmd, | ||
| 225 | unsigned int num_in, unsigned int num_out, | ||
| 199 | u32 args[]) | 226 | u32 args[]) |
| 200 | { | 227 | { |
| 201 | unsigned long time_left = msecs_to_jiffies(AVS_TIMEOUT); | ||
| 202 | void __iomem *base = priv->base; | 228 | void __iomem *base = priv->base; |
| 229 | unsigned long time_left; | ||
| 203 | unsigned int i; | 230 | unsigned int i; |
| 204 | int ret; | 231 | int ret; |
| 205 | u32 val; | 232 | u32 val; |
| @@ -225,11 +252,9 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, · 19 unmodified lines | |||
| 225 | /* Clear status before we begin. */ | 252 | /* Clear status before we begin. */ |
| 226 | writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); | 253 | writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); |
| 227 | 254 | ||
| 228 | /* We need to send arguments for this command. */ | 255 | /* Provide input parameters */ |
| 229 | if (args && is_send) { | 256 | for (i = 0; i < num_in; i++) |
| 230 | for (i = 0; i < AVS_MAX_CMD_ARGS; i++) | 257 | writel(args[i], base + AVS_MBOX_PARAM(i)); |
| 231 | writel(args[i], base + AVS_MBOX_PARAM(i)); | ||
| 232 | } | ||
| 233 | 258 | ||
| 234 | /* Protect from spurious interrupts. */ | 259 | /* Protect from spurious interrupts. */ |
| 235 | reinit_completion(&priv->done); | 260 | reinit_completion(&priv->done); |
| @@ -239,7 +264,7 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, · 3 unmodified lines | |||
| 239 | writel(AVS_CPU_L2_INT_MASK, priv->avs_intr_base + AVS_CPU_L2_SET0); | 264 | writel(AVS_CPU_L2_INT_MASK, priv->avs_intr_base + AVS_CPU_L2_SET0); |
| 240 | 265 | ||
| 241 | /* Wait for AVS co-processor to finish processing the command. */ | 266 | /* Wait for AVS co-processor to finish processing the command. */ |
| 242 | time_left = wait_for_completion_timeout(&priv->done, time_left); | 267 | time_left = wait_for_avs_command(priv, AVS_TIMEOUT); |
| 243 | 268 | ||
| 244 | /* | 269 | /* |
| 245 | * If the AVS status is not in the expected range, it means AVS didn't | 270 | * If the AVS status is not in the expected range, it means AVS didn't |
| @@ -256,11 +281,9 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, · 10 unmodified lines | |||
| 256 | goto out; | 281 | goto out; |
| 257 | } | 282 | } |
| 258 | 283 | ||
| 259 | /* This command returned arguments, so we read them back. */ | 284 | /* Process returned values */ |
| 260 | if (args && !is_send) { | 285 | for (i = 0; i < num_out; i++) |
| 261 | for (i = 0; i < AVS_MAX_CMD_ARGS; i++) | 286 | args[i] = readl(base + AVS_MBOX_PARAM(i)); |
| 262 | args[i] = readl(base + AVS_MBOX_PARAM(i)); | ||
| 263 | } | ||
| 264 | 287 | ||
| 265 | /* Clear status to tell AVS co-processor we are done. */ | 288 | /* Clear status to tell AVS co-processor we are done. */ |
| 266 | writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); | 289 | writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); |
| @@ -338,7 +361,7 @@ static int brcm_avs_get_pmap(struct private_data *priv, struct pmap *pmap) · 71 unmodified lines | |||
| 338 | u32 args[AVS_MAX_CMD_ARGS]; | 361 | u32 args[AVS_MAX_CMD_ARGS]; |
| 339 | int ret; | 362 | int ret; |
| 340 | 363 | ||
| 341 | ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, false, args); | 364 | ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, 0, 4, args); |
| 342 | if (ret || !pmap) | 365 | if (ret || !pmap) |
| 343 | return ret; | 366 | return ret; |
| 344 | 367 | ||
| @@ -359,7 +382,7 @@ static int brcm_avs_set_pmap(struct private_data *priv, struct pmap *pmap) · 14 unmodified lines | |||
| 359 | args[2] = pmap->p2; | 382 | args[2] = pmap->p2; |
| 360 | args[3] = pmap->state; | 383 | args[3] = pmap->state; |
| 361 | 384 | ||
| 362 | return __issue_avs_command(priv, AVS_CMD_SET_PMAP, true, args); | 385 | return __issue_avs_command(priv, AVS_CMD_SET_PMAP, 4, 0, args); |
| 363 | } | 386 | } |
| 364 | 387 | ||
| 365 | static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate) | 388 | static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate) |
| @@ -367,7 +390,7 @@ static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate) · 1 unmodified line | |||
| 367 | u32 args[AVS_MAX_CMD_ARGS]; | 390 | u32 args[AVS_MAX_CMD_ARGS]; |
| 368 | int ret; | 391 | int ret; |
| 369 | 392 | ||
| 370 | ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, false, args); | 393 | ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, 0, 1, args); |
| 371 | if (ret) | 394 | if (ret) |
| 372 | return ret; | 395 | return ret; |
| 373 | *pstate = args[0]; | 396 | *pstate = args[0]; |
| @@ -381,7 +404,8 @@ static int brcm_avs_set_pstate(struct private_data *priv, unsigned int pstate) · 7 unmodified lines | |||
| 381 | 404 | ||
| 382 | args[0] = pstate; | 405 | args[0] = pstate; |
| 383 | 406 | ||
| 384 | return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, true, args); | 407 | return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, 1, 0, args); |
| 408 | |||
| 385 | } | 409 | } |
| 386 | 410 | ||
| 387 | static u32 brcm_avs_get_voltage(void __iomem *base) | 411 | static u32 brcm_avs_get_voltage(void __iomem *base) |
| @@ -482,7 +506,14 @@ static int brcm_avs_suspend(struct cpufreq_policy *policy) · 94 unmodified lines | |||
| 482 | * AVS co-processor, not necessarily the P-state we are running at now. | 506 | * AVS co-processor, not necessarily the P-state we are running at now. |
| 483 | * So, we get the current P-state explicitly. | 507 | * So, we get the current P-state explicitly. |
| 484 | */ | 508 | */ |
| 485 | return brcm_avs_get_pstate(priv, &priv->pmap.state); | 509 | ret = brcm_avs_get_pstate(priv, &priv->pmap.state); |
| 510 | if (ret) | ||
| 511 | return ret; | ||
| 512 | |||
| 513 | /* This is best effort. Nothing to do if it fails. */ | ||
| 514 | (void)__issue_avs_command(priv, AVS_CMD_S2_ENTER, 0, 0, NULL); | ||
| 515 | |||
| 516 | return 0; | ||
| 486 | } | 517 | } |
| 487 | 518 | ||
| 488 | static int brcm_avs_resume(struct cpufreq_policy *policy) | 519 | static int brcm_avs_resume(struct cpufreq_policy *policy) |
| @@ -490,6 +521,9 @@ static int brcm_avs_resume(struct cpufreq_policy *policy) · 1 unmodified line | |||
| 490 | struct private_data *priv = policy->driver_data; | 521 | struct private_data *priv = policy->driver_data; |
| 491 | int ret; | 522 | int ret; |
| 492 | 523 | ||
| 524 | /* This is best effort. Nothing to do if it fails. */ | ||
| 525 | (void)__issue_avs_command(priv, AVS_CMD_S2_EXIT, 0, 0, NULL); | ||
| 526 | |||
| 493 | ret = brcm_avs_set_pmap(priv, &priv->pmap); | 527 | ret = brcm_avs_set_pmap(priv, &priv->pmap); |
| 494 | if (ret == -EEXIST) { | 528 | if (ret == -EEXIST) { |
| 495 | struct platform_device *pdev = cpufreq_get_driver_data(); | 529 | struct platform_device *pdev = cpufreq_get_driver_data(); |
| @@ -511,7 +545,7 @@ static int brcm_avs_prepare_init(struct platform_device *pdev) · 15 unmodified lines | |||
| 511 | { | 545 | { |
| 512 | struct private_data *priv; | 546 | struct private_data *priv; |
| 513 | struct device *dev; | 547 | struct device *dev; |
| 514 | int host_irq, ret; | 548 | int ret; |
| 515 | 549 | ||
| 516 | dev = &pdev->dev; | 550 | dev = &pdev->dev; |
| 517 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | 551 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
| @@ -538,19 +572,14 @@ static int brcm_avs_prepare_init(struct platform_device *pdev) · 20 unmodified lines | |||
| 538 | goto unmap_base; | 572 | goto unmap_base; |
| 539 | } | 573 | } |
| 540 | 574 | ||
| 541 | host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR); | 575 | priv->host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR); |
| 542 | if (host_irq < 0) { | ||
| 543 | dev_err(dev, "Couldn't find interrupt %s -- %d\n", | ||
| 544 | BRCM_AVS_HOST_INTR, host_irq); | ||
| 545 | ret = host_irq; | ||
| 546 | goto unmap_intr_base; | ||
| 547 | } | ||
| 548 | 576 | ||
| 549 | ret = devm_request_irq(dev, host_irq, irq_handler, IRQF_TRIGGER_RISING, | 577 | ret = devm_request_irq(dev, priv->host_irq, irq_handler, |
| 578 | IRQF_TRIGGER_RISING, | ||
| 550 | BRCM_AVS_HOST_INTR, priv); | 579 | BRCM_AVS_HOST_INTR, priv); |
| 551 | if (ret) { | 580 | if (ret && priv->host_irq >= 0) { |
| 552 | dev_err(dev, "IRQ request failed: %s (%d) -- %d\n", | 581 | dev_err(dev, "IRQ request failed: %s (%d) -- %d\n", |
| 553 | BRCM_AVS_HOST_INTR, host_irq, ret); | 582 | BRCM_AVS_HOST_INTR, priv->host_irq, ret); |
| 554 | goto unmap_intr_base; | 583 | goto unmap_intr_base; |
| 555 | } | 584 | } |
| 556 | 585 | ||
| @@ -593,7 +622,7 @@ static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy) · 36 unmodified lines | |||
| 593 | /* All cores share the same clock and thus the same policy. */ | 622 | /* All cores share the same clock and thus the same policy. */ |
| 594 | cpumask_setall(policy->cpus); | 623 | cpumask_setall(policy->cpus); |
| 595 | 624 | ||
| 596 | ret = __issue_avs_command(priv, AVS_CMD_ENABLE, false, NULL); | 625 | ret = __issue_avs_command(priv, AVS_CMD_ENABLE, 0, 0, NULL); |
| 597 | if (!ret) { | 626 | if (!ret) { |
| 598 | unsigned int pstate; | 627 | unsigned int pstate; |
| 599 | 628 | ||
| @@ -45,8 +45,6 @@ struct cppc_workaround_oem_info { · 44 unmodified lines | |||
| 45 | u32 oem_revision; | 45 | u32 oem_revision; |
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | static bool apply_hisi_workaround; | ||
| 49 | |||
| 50 | static struct cppc_workaround_oem_info wa_info[] = { | 48 | static struct cppc_workaround_oem_info wa_info[] = { |
| 51 | { | 49 | { |
| 52 | .oem_id = "HISI ", | 50 | .oem_id = "HISI ", |
| @@ -59,50 +57,6 @@ static struct cppc_workaround_oem_info wa_info[] = { · 6 unmodified lines | |||
| 59 | } | 57 | } |
| 60 | }; | 58 | }; |
| 61 | 59 | ||
| 62 | static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu, | ||
| 63 | unsigned int perf); | ||
| 64 | |||
| 65 | /* | ||
| 66 | * HISI platform does not support delivered performance counter and | ||
| 67 | * reference performance counter. It can calculate the performance using the | ||
| 68 | * platform specific mechanism. We reuse the desired performance register to | ||
| 69 | * store the real performance calculated by the platform. | ||
| 70 | */ | ||
| 71 | static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum) | ||
| 72 | { | ||
| 73 | struct cppc_cpudata *cpudata = all_cpu_data[cpunum]; | ||
| 74 | u64 desired_perf; | ||
| 75 | int ret; | ||
| 76 | |||
| 77 | ret = cppc_get_desired_perf(cpunum, &desired_perf); | ||
| 78 | if (ret < 0) | ||
| 79 | return -EIO; | ||
| 80 | |||
| 81 | return cppc_cpufreq_perf_to_khz(cpudata, desired_perf); | ||
| 82 | } | ||
| 83 | |||
| 84 | static void cppc_check_hisi_workaround(void) | ||
| 85 | { | ||
| 86 | struct acpi_table_header *tbl; | ||
| 87 | acpi_status status = AE_OK; | ||
| 88 | int i; | ||
| 89 | |||
| 90 | status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl); | ||
| 91 | if (ACPI_FAILURE(status) || !tbl) | ||
| 92 | return; | ||
| 93 | |||
| 94 | for (i = 0; i < ARRAY_SIZE(wa_info); i++) { | ||
| 95 | if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) && | ||
| 96 | !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) && | ||
| 97 | wa_info[i].oem_revision == tbl->oem_revision) { | ||
| 98 | apply_hisi_workaround = true; | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | acpi_put_table(tbl); | ||
| 104 | } | ||
| 105 | |||
| 106 | /* Callback function used to retrieve the max frequency from DMI */ | 60 | /* Callback function used to retrieve the max frequency from DMI */ |
| 107 | static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) | 61 | static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) |
| 108 | { | 62 | { |
| @@ -161,7 +115,7 @@ static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu, · 52 unmodified lines | |||
| 161 | if (!max_khz) | 115 | if (!max_khz) |
| 162 | max_khz = cppc_get_dmi_max_khz(); | 116 | max_khz = cppc_get_dmi_max_khz(); |
| 163 | mul = max_khz; | 117 | mul = max_khz; |
| 164 | div = cpu->perf_caps.highest_perf; | 118 | div = caps->highest_perf; |
| 165 | } | 119 | } |
| 166 | return (u64)perf * mul / div; | 120 | return (u64)perf * mul / div; |
| 167 | } | 121 | } |
| @@ -184,7 +138,7 @@ static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu, · 16 unmodified lines | |||
| 184 | } else { | 138 | } else { |
| 185 | if (!max_khz) | 139 | if (!max_khz) |
| 186 | max_khz = cppc_get_dmi_max_khz(); | 140 | max_khz = cppc_get_dmi_max_khz(); |
| 187 | mul = cpu->perf_caps.highest_perf; | 141 | mul = caps->highest_perf; |
| 188 | div = max_khz; | 142 | div = max_khz; |
| 189 | } | 143 | } |
| 190 | 144 | ||
| @@ -402,9 +356,6 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum) · 211 unmodified lines | |||
| 402 | struct cppc_cpudata *cpu = all_cpu_data[cpunum]; | 356 | struct cppc_cpudata *cpu = all_cpu_data[cpunum]; |
| 403 | int ret; | 357 | int ret; |
| 404 | 358 | ||
| 405 | if (apply_hisi_workaround) | ||
| 406 | return hisi_cppc_cpufreq_get_rate(cpunum); | ||
| 407 | |||
| 408 | ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0); | 359 | ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0); |
| 409 | if (ret) | 360 | if (ret) |
| 410 | return ret; | 361 | return ret; |
| @@ -455,6 +406,48 @@ static struct cpufreq_driver cppc_cpufreq_driver = { · 44 unmodified lines | |||
| 455 | .name = "cppc_cpufreq", | 406 | .name = "cppc_cpufreq", |
| 456 | }; | 407 | }; |
| 457 | 408 | ||
| 409 | /* | ||
| 410 | * HISI platform does not support delivered performance counter and | ||
| 411 | * reference performance counter. It can calculate the performance using the | ||
| 412 | * platform specific mechanism. We reuse the desired performance register to | ||
| 413 | * store the real performance calculated by the platform. | ||
| 414 | */ | ||
| 415 | static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum) | ||
| 416 | { | ||
| 417 | struct cppc_cpudata *cpudata = all_cpu_data[cpunum]; | ||
| 418 | u64 desired_perf; | ||
| 419 | int ret; | ||
| 420 | |||
| 421 | ret = cppc_get_desired_perf(cpunum, &desired_perf); | ||
| 422 | if (ret < 0) | ||
| 423 | return -EIO; | ||
| 424 | |||
| 425 | return cppc_cpufreq_perf_to_khz(cpudata, desired_perf); | ||
| 426 | } | ||
| 427 | |||
| 428 | static void cppc_check_hisi_workaround(void) | ||
| 429 | { | ||
| 430 | struct acpi_table_header *tbl; | ||
| 431 | acpi_status status = AE_OK; | ||
| 432 | int i; | ||
| 433 | |||
| 434 | status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl); | ||
| 435 | if (ACPI_FAILURE(status) || !tbl) | ||
| 436 | return; | ||
| 437 | |||
| 438 | for (i = 0; i < ARRAY_SIZE(wa_info); i++) { | ||
| 439 | if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) && | ||
| 440 | !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) && | ||
| 441 | wa_info[i].oem_revision == tbl->oem_revision) { | ||
| 442 | /* Overwrite the get() callback */ | ||
| 443 | cppc_cpufreq_driver.get = hisi_cppc_cpufreq_get_rate; | ||
| 444 | break; | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | acpi_put_table(tbl); | ||
| 449 | } | ||
| 450 | |||
| 458 | static int __init cppc_cpufreq_init(void) | 451 | static int __init cppc_cpufreq_init(void) |
| 459 | { | 452 | { |
| 460 | int i, ret = 0; | 453 | int i, ret = 0; |
| @@ -132,6 +132,8 @@ static const struct of_device_id blacklist[] __initconst = { · 131 unmodified lines | |||
| 132 | { .compatible = "qcom,apq8096", }, | 132 | { .compatible = "qcom,apq8096", }, |
| 133 | { .compatible = "qcom,msm8996", }, | 133 | { .compatible = "qcom,msm8996", }, |
| 134 | { .compatible = "qcom,qcs404", }, | 134 | { .compatible = "qcom,qcs404", }, |
| 135 | { .compatible = "qcom,sc7180", }, | ||
| 136 | { .compatible = "qcom,sdm845", }, | ||
| 135 | 137 | ||
| 136 | { .compatible = "st,stih407", }, | 138 | { .compatible = "st,stih407", }, |
| 137 | { .compatible = "st,stih410", }, | 139 | { .compatible = "st,stih410", }, |
| @@ -541,7 +541,7 @@ unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy, · 540 unmodified lines | |||
| 541 | policy->cached_target_freq = target_freq; | 541 | policy->cached_target_freq = target_freq; |
| 542 | 542 | ||
| 543 | if (cpufreq_driver->target_index) { | 543 | if (cpufreq_driver->target_index) { |
| 544 | int idx; | 544 | unsigned int idx; |
| 545 | 545 | ||
| 546 | idx = cpufreq_frequency_table_target(policy, target_freq, | 546 | idx = cpufreq_frequency_table_target(policy, target_freq, |
| 547 | CPUFREQ_RELATION_L); | 547 | CPUFREQ_RELATION_L); |
| @@ -1647,6 +1647,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) · 1646 unmodified lines | |||
| 1647 | 1647 | ||
| 1648 | intel_pstate_get_hwp_max(cpu->cpu, &phy_max, ¤t_max); | 1648 | intel_pstate_get_hwp_max(cpu->cpu, &phy_max, ¤t_max); |
| 1649 | cpu->pstate.turbo_freq = phy_max * cpu->pstate.scaling; | 1649 | cpu->pstate.turbo_freq = phy_max * cpu->pstate.scaling; |
| 1650 | cpu->pstate.turbo_pstate = phy_max; | ||
| 1650 | } else { | 1651 | } else { |
| 1651 | cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; | 1652 | cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; |
| 1652 | } | 1653 | } |
| @@ -6,6 +6,7 @@ · 5 unmodified lines | |||
| 6 | #include <linux/bitfield.h> | 6 | #include <linux/bitfield.h> |
| 7 | #include <linux/cpufreq.h> | 7 | #include <linux/cpufreq.h> |
| 8 | #include <linux/init.h> | 8 | #include <linux/init.h> |
| 9 | #include <linux/interconnect.h> | ||
| 9 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 10 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| 11 | #include <linux/of_address.h> | 12 | #include <linux/of_address.h> |
| @@ -30,6 +31,48 @@ · 18 unmodified lines | |||
| 30 | 31 | ||
| 31 | static unsigned long cpu_hw_rate, xo_rate; | 32 | static unsigned long cpu_hw_rate, xo_rate; |
| 32 | static struct platform_device *global_pdev; | 33 | static struct platform_device *global_pdev; |
| 34 | static bool icc_scaling_enabled; | ||
| 35 | |||
| 36 | static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy, | ||
| 37 | unsigned long freq_khz) | ||
| 38 | { | ||
| 39 | unsigned long freq_hz = freq_khz * 1000; | ||
| 40 | struct dev_pm_opp *opp; | ||
| 41 | struct device *dev; | ||
| 42 | int ret; | ||
| 43 | |||
| 44 | dev = get_cpu_device(policy->cpu); | ||
| 45 | if (!dev) | ||
| 46 | return -ENODEV; | ||
| 47 | |||
| 48 | opp = dev_pm_opp_find_freq_exact(dev, freq_hz, true); | ||
| 49 | if (IS_ERR(opp)) | ||
| 50 | return PTR_ERR(opp); | ||
| 51 | |||
| 52 | ret = dev_pm_opp_set_bw(dev, opp); | ||
| 53 | dev_pm_opp_put(opp); | ||
| 54 | return ret; | ||
| 55 | } | ||
| 56 | |||
| 57 | static int qcom_cpufreq_update_opp(struct device *cpu_dev, | ||
| 58 | unsigned long freq_khz, | ||
| 59 | unsigned long volt) | ||
| 60 | { | ||
| 61 | unsigned long freq_hz = freq_khz * 1000; | ||
| 62 | int ret; | ||
| 63 | |||
| 64 | /* Skip voltage update if the opp table is not available */ | ||
| 65 | if (!icc_scaling_enabled) | ||
| 66 | return dev_pm_opp_add(cpu_dev, freq_hz, volt); | ||
| 67 | |||
| 68 | ret = dev_pm_opp_adjust_voltage(cpu_dev, freq_hz, volt, volt, volt); | ||
| 69 | if (ret) { | ||
| 70 | dev_err(cpu_dev, "Voltage update failed freq=%ld\n", freq_khz); | ||
| 71 | return ret; | ||
| 72 | } | ||
| 73 | |||
| 74 | return dev_pm_opp_enable(cpu_dev, freq_hz); | ||
| 75 | } | ||
| 33 | 76 | ||
| 34 | static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, | 77 | static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, |
| 35 | unsigned int index) | 78 | unsigned int index) |
| @@ -39,6 +82,9 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, · 3 unmodified lines | |||
| 39 | 82 | ||
| 40 | writel_relaxed(index, perf_state_reg); | 83 | writel_relaxed(index, perf_state_reg); |
| 41 | 84 | ||
| 85 | if (icc_scaling_enabled) | ||
| 86 | qcom_cpufreq_set_bw(policy, freq); | ||
| 87 | |||
| 42 | arch_set_freq_scale(policy->related_cpus, freq, | 88 | arch_set_freq_scale(policy->related_cpus, freq, |
| 43 | policy->cpuinfo.max_freq); | 89 | policy->cpuinfo.max_freq); |
| 44 | return 0; | 90 | return 0; |
| @@ -66,13 +112,10 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, · 21 unmodified lines | |||
| 66 | unsigned int target_freq) | 112 | unsigned int target_freq) |
| 67 | { | 113 | { |
| 68 | void __iomem *perf_state_reg = policy->driver_data; | 114 | void __iomem *perf_state_reg = policy->driver_data; |
| 69 | int index; | 115 | unsigned int index; |
| 70 | unsigned long freq; | 116 | unsigned long freq; |
| 71 | 117 | ||
| 72 | index = policy->cached_resolved_idx; | 118 | index = policy->cached_resolved_idx; |
| 73 | if (index < 0) | ||
| 74 | return 0; | ||
| 75 | |||
| 76 | writel_relaxed(index, perf_state_reg); | 119 | writel_relaxed(index, perf_state_reg); |
| 77 | 120 | ||
| 78 | freq = policy->freq_table[index].frequency; | 121 | freq = policy->freq_table[index].frequency; |
| @@ -89,11 +132,34 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, · 10 unmodified lines | |||
| 89 | u32 data, src, lval, i, core_count, prev_freq = 0, freq; | 132 | u32 data, src, lval, i, core_count, prev_freq = 0, freq; |
| 90 | u32 volt; | 133 | u32 volt; |
| 91 | struct cpufreq_frequency_table *table; | 134 | struct cpufreq_frequency_table *table; |
| 135 | struct dev_pm_opp *opp; | ||
| 136 | unsigned long rate; | ||
| 137 | int ret; | ||
| 92 | 138 | ||
| 93 | table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL); | 139 | table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL); |
| 94 | if (!table) | 140 | if (!table) |
| 95 | return -ENOMEM; | 141 | return -ENOMEM; |
| 96 | 142 | ||
| 143 | ret = dev_pm_opp_of_add_table(cpu_dev); | ||
| 144 | if (!ret) { | ||
| 145 | /* Disable all opps and cross-validate against LUT later */ | ||
| 146 | icc_scaling_enabled = true; | ||
| 147 | for (rate = 0; ; rate++) { | ||
| 148 | opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate); | ||
| 149 | if (IS_ERR(opp)) | ||
| 150 | break; | ||
| 151 | |||
| 152 | dev_pm_opp_put(opp); | ||
| 153 | dev_pm_opp_disable(cpu_dev, rate); | ||
| 154 | } | ||
| 155 | } else if (ret != -ENODEV) { | ||
| 156 | dev_err(cpu_dev, "Invalid opp table in device tree\n"); | ||
| 157 | return ret; | ||
| 158 | } else { | ||
| 159 | policy->fast_switch_possible = true; | ||
| 160 | icc_scaling_enabled = false; | ||
| 161 | } | ||
| 162 | |||
| 97 | for (i = 0; i < LUT_MAX_ENTRIES; i++) { | 163 | for (i = 0; i < LUT_MAX_ENTRIES; i++) { |
| 98 | data = readl_relaxed(base + REG_FREQ_LUT + | 164 | data = readl_relaxed(base + REG_FREQ_LUT + |
| 99 | i * LUT_ROW_SIZE); | 165 | i * LUT_ROW_SIZE); |
| @@ -112,7 +178,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, · 12 unmodified lines | |||
| 112 | 178 | ||
| 113 | if (freq != prev_freq && core_count != LUT_TURBO_IND) { | 179 | if (freq != prev_freq && core_count != LUT_TURBO_IND) { |
| 114 | table[i].frequency = freq; | 180 | table[i].frequency = freq; |
| 115 | dev_pm_opp_add(cpu_dev, freq * 1000, volt); | 181 | qcom_cpufreq_update_opp(cpu_dev, freq, volt); |
| 116 | dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i, | 182 | dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i, |
| 117 | freq, core_count); | 183 | freq, core_count); |
| 118 | } else if (core_count == LUT_TURBO_IND) { | 184 | } else if (core_count == LUT_TURBO_IND) { |
| @@ -133,7 +199,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, · 14 unmodified lines | |||
| 133 | if (prev->frequency == CPUFREQ_ENTRY_INVALID) { | 199 | if (prev->frequency == CPUFREQ_ENTRY_INVALID) { |
| 134 | prev->frequency = prev_freq; | 200 | prev->frequency = prev_freq; |
| 135 | prev->flags = CPUFREQ_BOOST_FREQ; | 201 | prev->flags = CPUFREQ_BOOST_FREQ; |
| 136 | dev_pm_opp_add(cpu_dev, prev_freq * 1000, volt); | 202 | qcom_cpufreq_update_opp(cpu_dev, prev_freq, volt); |
| 137 | } | 203 | } |
| 138 | 204 | ||
| 139 | break; | 205 | break; |
| @@ -240,8 +306,6 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) · 100 unmodified lines | |||
| 240 | 306 | ||
| 241 | dev_pm_opp_of_register_em(cpu_dev, policy->cpus); | 307 | dev_pm_opp_of_register_em(cpu_dev, policy->cpus); |
| 242 | 308 | ||
| 243 | policy->fast_switch_possible = true; | ||
| 244 | |||
| 245 | return 0; | 309 | return 0; |
| 246 | error: | 310 | error: |
| 247 | devm_iounmap(dev, base); | 311 | devm_iounmap(dev, base); |
| @@ -254,6 +318,7 @@ static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) · 6 unmodified lines | |||
| 254 | void __iomem *base = policy->driver_data - REG_PERF_STATE; | 318 | void __iomem *base = policy->driver_data - REG_PERF_STATE; |
| 255 | 319 | ||
| 256 | dev_pm_opp_remove_all_dynamic(cpu_dev); | 320 | dev_pm_opp_remove_all_dynamic(cpu_dev); |
| 321 | dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); | ||
| 257 | kfree(policy->freq_table); | 322 | kfree(policy->freq_table); |
| 258 | devm_iounmap(&global_pdev->dev, base); | 323 | devm_iounmap(&global_pdev->dev, base); |
| 259 | 324 | ||
| @@ -282,6 +347,7 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = { · 22 unmodified lines | |||
| 282 | 347 | ||
| 283 | static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) | 348 | static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) |
| 284 | { | 349 | { |
| 350 | struct device *cpu_dev; | ||
| 285 | struct clk *clk; | 351 | struct clk *clk; |
| 286 | int ret; | 352 | int ret; |
| 287 | 353 | ||
| @@ -301,6 +367,15 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) · 13 unmodified lines | |||
| 301 | 367 | ||
| 302 | global_pdev = pdev; | 368 | global_pdev = pdev; |
| 303 | 369 | ||
| 370 | /* Check for optional interconnect paths on CPU0 */ | ||
| 371 | cpu_dev = get_cpu_device(0); | ||
| 372 | if (!cpu_dev) | ||
| 373 | return -EPROBE_DEFER; | ||
| 374 | |||
| 375 | ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL); | ||
| 376 | if (ret) | ||
| 377 | return ret; | ||
| 378 | |||
| 304 | ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver); | 379 | ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver); |
| 305 | if (ret) | 380 | if (ret) |
| 306 | dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n"); | 381 | dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n"); |
| @@ -40,11 +40,11 @@ enum { · 39 unmodified lines | |||
| 40 | }; | 40 | }; |
| 41 | 41 | ||
| 42 | /** | 42 | /** |
| 43 | * ST CPUFreq Driver Data | 43 | * struct sti_cpufreq_ddata - ST CPUFreq Driver Data |
| 44 | * | 44 | * |
| 45 | * @cpu_node CPU's OF node | 45 | * @cpu: CPU's OF node |
| 46 | * @syscfg_eng Engineering Syscon register map | 46 | * @syscfg_eng: Engineering Syscon register map |
| 47 | * @regmap Syscon register map | 47 | * @syscfg: Syscon register map |
| 48 | */ | 48 | */ |
| 49 | static struct sti_cpufreq_ddata { | 49 | static struct sti_cpufreq_ddata { |
| 50 | struct device *cpu; | 50 | struct device *cpu; |
| @@ -223,15 +223,9 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev) · 222 unmodified lines | |||
| 223 | } | 223 | } |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | tegra_bpmp_put(bpmp); | ||
| 227 | |||
| 228 | tegra186_cpufreq_driver.driver_data = data; | 226 | tegra186_cpufreq_driver.driver_data = data; |
| 229 | 227 | ||
| 230 | err = cpufreq_register_driver(&tegra186_cpufreq_driver); | 228 | err = cpufreq_register_driver(&tegra186_cpufreq_driver); |
| 231 | if (err) | ||
| 232 | return err; | ||
| 233 | |||
| 234 | return 0; | ||
| 235 | 229 | ||
| 236 | put_bpmp: | 230 | put_bpmp: |
| 237 | tegra_bpmp_put(bpmp); | 231 | tegra_bpmp_put(bpmp); |
| @@ -0,0 +1,390 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/cpu.h> | ||
| 7 | #include <linux/cpufreq.h> | ||
| 8 | #include <linux/delay.h> | ||
| 9 | #include <linux/dma-mapping.h> | ||
| 10 | #include <linux/module.h> | ||
| 11 | #include <linux/of.h> | ||
| 12 | #include <linux/of_platform.h> | ||
| 13 | #include <linux/platform_device.h> | ||
| 14 | #include <linux/slab.h> | ||
| 15 | |||
| 16 | #include <asm/smp_plat.h> | ||
| 17 | |||
| 18 | #include <soc/tegra/bpmp.h> | ||
| 19 | #include <soc/tegra/bpmp-abi.h> | ||
| 20 | |||
| 21 | #define KHZ 1000 | ||
| 22 | #define REF_CLK_MHZ 408 /* 408 MHz */ | ||
| 23 | #define US_DELAY 500 | ||
| 24 | #define US_DELAY_MIN 2 | ||
| 25 | #define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ) | ||
| 26 | #define MAX_CNT ~0U | ||
| 27 | |||
| 28 | /* cpufreq transisition latency */ | ||
| 29 | #define TEGRA_CPUFREQ_TRANSITION_LATENCY (300 * 1000) /* unit in nanoseconds */ | ||
| 30 | |||
| 31 | enum cluster { | ||
| 32 | CLUSTER0, | ||
| 33 | CLUSTER1, | ||
| 34 | CLUSTER2, | ||
| 35 | CLUSTER3, | ||
| 36 | MAX_CLUSTERS, | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct tegra194_cpufreq_data { | ||
| 40 | void __iomem *regs; | ||
| 41 | size_t num_clusters; | ||
| 42 | struct cpufreq_frequency_table **tables; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct tegra_cpu_ctr { | ||
| 46 | u32 cpu; | ||
| 47 | u32 delay; | ||
| 48 | u32 coreclk_cnt, last_coreclk_cnt; | ||
| 49 | u32 refclk_cnt, last_refclk_cnt; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct read_counters_work { | ||
| 53 | struct work_struct work; | ||
| 54 | struct tegra_cpu_ctr c; | ||
| 55 | }; | ||
| 56 | |||
| 57 | static struct workqueue_struct *read_counters_wq; | ||
| 58 | |||
| 59 | static enum cluster get_cpu_cluster(u8 cpu) | ||
| 60 | { | ||
| 61 | return MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* | ||
| 65 | * Read per-core Read-only system register NVFREQ_FEEDBACK_EL1. | ||
| 66 | * The register provides frequency feedback information to | ||
| 67 | * determine the average actual frequency a core has run at over | ||
| 68 | * a period of time. | ||
| 69 | * [31:0] PLLP counter: Counts at fixed frequency (408 MHz) | ||
| 70 | * [63:32] Core clock counter: counts on every core clock cycle | ||
| 71 | * where the core is architecturally clocking | ||
| 72 | */ | ||
| 73 | static u64 read_freq_feedback(void) | ||
| 74 | { | ||
| 75 | u64 val = 0; | ||
| 76 | |||
| 77 | asm volatile("mrs %0, s3_0_c15_c0_5" : "=r" (val) : ); | ||
| 78 | |||
| 79 | return val; | ||
| 80 | } | ||
| 81 | |||
| 82 | static inline u32 map_ndiv_to_freq(struct mrq_cpu_ndiv_limits_response | ||
| 83 | *nltbl, u16 ndiv) | ||
| 84 | { | ||
| 85 | return nltbl->ref_clk_hz / KHZ * ndiv / (nltbl->pdiv * nltbl->mdiv); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void tegra_read_counters(struct work_struct *work) | ||
| 89 | { | ||
| 90 | struct read_counters_work *read_counters_work; | ||
| 91 | struct tegra_cpu_ctr *c; | ||
| 92 | u64 val; | ||
| 93 | |||
| 94 | /* | ||
| 95 | * ref_clk_counter(32 bit counter) runs on constant clk, | ||
| 96 | * pll_p(408MHz). | ||
| 97 | * It will take = 2 ^ 32 / 408 MHz to overflow ref clk counter | ||
| 98 | * = 10526880 usec = 10.527 sec to overflow | ||
| 99 | * | ||
| 100 | * Like wise core_clk_counter(32 bit counter) runs on core clock. | ||
| 101 | * It's synchronized to crab_clk (cpu_crab_clk) which runs at | ||
| 102 | * freq of cluster. Assuming max cluster clock ~2000MHz, | ||
| 103 | * It will take = 2 ^ 32 / 2000 MHz to overflow core clk counter | ||
| 104 | * = ~2.147 sec to overflow | ||
| 105 | */ | ||
| 106 | read_counters_work = container_of(work, struct read_counters_work, | ||
| 107 | work); | ||
| 108 | c = &read_counters_work->c; | ||
| 109 | |||
| 110 | val = read_freq_feedback(); | ||
| 111 | c->last_refclk_cnt = lower_32_bits(val); | ||
| 112 | c->last_coreclk_cnt = upper_32_bits(val); | ||
| 113 | udelay(c->delay); | ||
| 114 | val = read_freq_feedback(); | ||
| 115 | c->refclk_cnt = lower_32_bits(val); | ||
| 116 | c->coreclk_cnt = upper_32_bits(val); | ||
| 117 | } | ||
| 118 | |||
| 119 | /* | ||
| 120 | * Return instantaneous cpu speed | ||
| 121 | * Instantaneous freq is calculated as - | ||
| 122 | * -Takes sample on every query of getting the freq. | ||
| 123 | * - Read core and ref clock counters; | ||
| 124 | * - Delay for X us | ||
| 125 | * - Read above cycle counters again | ||
| 126 | * - Calculates freq by subtracting current and previous counters | ||
| 127 | * divided by the delay time or eqv. of ref_clk_counter in delta time | ||
| 128 | * - Return Kcycles/second, freq in KHz | ||
| 129 | * | ||
| 130 | * delta time period = x sec | ||
| 131 | * = delta ref_clk_counter / (408 * 10^6) sec | ||
| 132 | * freq in Hz = cycles/sec | ||
| 133 | * = (delta cycles / x sec | ||
| 134 | * = (delta cycles * 408 * 10^6) / delta ref_clk_counter | ||
| 135 | * in KHz = (delta cycles * 408 * 10^3) / delta ref_clk_counter | ||
| 136 | * | ||
| 137 | * @cpu - logical cpu whose freq to be updated | ||
| 138 | * Returns freq in KHz on success, 0 if cpu is offline | ||
| 139 | */ | ||
| 140 | static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay) | ||
| 141 | { | ||
| 142 | struct read_counters_work read_counters_work; | ||
| 143 | struct tegra_cpu_ctr c; | ||
| 144 | u32 delta_refcnt; | ||
| 145 | u32 delta_ccnt; | ||
| 146 | u32 rate_mhz; | ||
| 147 | |||
| 148 | /* | ||
| 149 | * udelay() is required to reconstruct cpu frequency over an | ||
| 150 | * observation window. Using workqueue to call udelay() with | ||
| 151 | * interrupts enabled. | ||
| 152 | */ | ||
| 153 | read_counters_work.c.cpu = cpu; | ||
| 154 | read_counters_work.c.delay = delay; | ||
| 155 | INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters); | ||
| 156 | queue_work_on(cpu, read_counters_wq, &read_counters_work.work); | ||
| 157 | flush_work(&read_counters_work.work); | ||
| 158 | c = read_counters_work.c; | ||
| 159 | |||
| 160 | if (c.coreclk_cnt < c.last_coreclk_cnt) | ||
| 161 | delta_ccnt = c.coreclk_cnt + (MAX_CNT - c.last_coreclk_cnt); | ||
| 162 | else | ||
| 163 | delta_ccnt = c.coreclk_cnt - c.last_coreclk_cnt; | ||
| 164 | if (!delta_ccnt) | ||
| 165 | return 0; | ||
| 166 | |||
| 167 | /* ref clock is 32 bits */ | ||
| 168 | if (c.refclk_cnt < c.last_refclk_cnt) | ||
| 169 | delta_refcnt = c.refclk_cnt + (MAX_CNT - c.last_refclk_cnt); | ||
| 170 | else | ||
| 171 | delta_refcnt = c.refclk_cnt - c.last_refclk_cnt; | ||
| 172 | if (!delta_refcnt) { | ||
| 173 | pr_debug("cpufreq: %d is idle, delta_refcnt: 0\n", cpu); | ||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | rate_mhz = ((unsigned long)(delta_ccnt * REF_CLK_MHZ)) / delta_refcnt; | ||
| 177 | |||
| 178 | return (rate_mhz * KHZ); /* in KHz */ | ||
| 179 | } | ||
| 180 | |||
| 181 | static unsigned int tegra194_get_speed(u32 cpu) | ||
| 182 | { | ||
| 183 | return tegra194_get_speed_common(cpu, US_DELAY); | ||
| 184 | } | ||
| 185 | |||
| 186 | static int tegra194_cpufreq_init(struct cpufreq_policy *policy) | ||
| 187 | { | ||
| 188 | struct tegra194_cpufreq_data *data = cpufreq_get_driver_data(); | ||
| 189 | int cl = get_cpu_cluster(policy->cpu); | ||
| 190 | u32 cpu; | ||
| 191 | |||
| 192 | if (cl >= data->num_clusters) | ||
| 193 | return -EINVAL; | ||
| 194 | |||
| 195 | /* boot freq */ | ||
| 196 | policy->cur = tegra194_get_speed_common(policy->cpu, US_DELAY_MIN); | ||
| 197 | |||
| 198 | /* set same policy for all cpus in a cluster */ | ||
| 199 | for (cpu = (cl * 2); cpu < ((cl + 1) * 2); cpu++) | ||
| 200 | cpumask_set_cpu(cpu, policy->cpus); | ||
| 201 | |||
| 202 | policy->freq_table = data->tables[cl]; | ||
| 203 | policy->cpuinfo.transition_latency = TEGRA_CPUFREQ_TRANSITION_LATENCY; | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | static void set_cpu_ndiv(void *data) | ||
| 209 | { | ||
| 210 | struct cpufreq_frequency_table *tbl = data; | ||
| 211 | u64 ndiv_val = (u64)tbl->driver_data; | ||
| 212 | |||
| 213 | asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val)); | ||
| 214 | } | ||
| 215 | |||
| 216 | static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy, | ||
| 217 | unsigned int index) | ||
| 218 | { | ||
| 219 | struct cpufreq_frequency_table *tbl = policy->freq_table + index; | ||
| 220 | |||
| 221 | /* | ||
| 222 | * Each core writes frequency in per core register. Then both cores | ||
| 223 | * in a cluster run at same frequency which is the maximum frequency | ||
| 224 | * request out of the values requested by both cores in that cluster. | ||
| 225 | */ | ||
| 226 | on_each_cpu_mask(policy->cpus, set_cpu_ndiv, tbl, true); | ||
| 227 | |||
| 228 | return 0; | ||
| 229 | } | ||
| 230 | |||
| 231 | static struct cpufreq_driver tegra194_cpufreq_driver = { | ||
| 232 | .name = "tegra194", | ||
| 233 | .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS | | ||
| 234 | CPUFREQ_NEED_INITIAL_FREQ_CHECK, | ||
| 235 | .verify = cpufreq_generic_frequency_table_verify, | ||
| 236 | .target_index = tegra194_cpufreq_set_target, | ||
| 237 | .get = tegra194_get_speed, | ||
| 238 | .init = tegra194_cpufreq_init, | ||
| 239 | .attr = cpufreq_generic_attr, | ||
| 240 | }; | ||
| 241 | |||
| 242 | static void tegra194_cpufreq_free_resources(void) | ||
| 243 | { | ||
| 244 | destroy_workqueue(read_counters_wq); | ||
| 245 | } | ||
| 246 | |||
| 247 | static struct cpufreq_frequency_table * | ||
| 248 | init_freq_table(struct platform_device *pdev, struct tegra_bpmp *bpmp, | ||
| 249 | unsigned int cluster_id) | ||
| 250 | { | ||
| 251 | struct cpufreq_frequency_table *freq_table; | ||
| 252 | struct mrq_cpu_ndiv_limits_response resp; | ||
| 253 | unsigned int num_freqs, ndiv, delta_ndiv; | ||
| 254 | struct mrq_cpu_ndiv_limits_request req; | ||
| 255 | struct tegra_bpmp_message msg; | ||
| 256 | u16 freq_table_step_size; | ||
| 257 | int err, index; | ||
| 258 | |||
| 259 | memset(&req, 0, sizeof(req)); | ||
| 260 | req.cluster_id = cluster_id; | ||
| 261 | |||
| 262 | memset(&msg, 0, sizeof(msg)); | ||
| 263 | msg.mrq = MRQ_CPU_NDIV_LIMITS; | ||
| 264 | msg.tx.data = &req; | ||
| 265 | msg.tx.size = sizeof(req); | ||
| 266 | msg.rx.data = &resp; | ||
| 267 | msg.rx.size = sizeof(resp); | ||
| 268 | |||
| 269 | err = tegra_bpmp_transfer(bpmp, &msg); | ||
| 270 | if (err) | ||
| 271 | return ERR_PTR(err); | ||
| 272 | |||
| 273 | /* | ||
| 274 | * Make sure frequency table step is a multiple of mdiv to match | ||
| 275 | * vhint table granularity. | ||
| 276 | */ | ||
| 277 | freq_table_step_size = resp.mdiv * | ||
| 278 | DIV_ROUND_UP(CPUFREQ_TBL_STEP_HZ, resp.ref_clk_hz); | ||
| 279 | |||
| 280 | dev_dbg(&pdev->dev, "cluster %d: frequency table step size: %d\n", | ||
| 281 | cluster_id, freq_table_step_size); | ||
| 282 | |||
| 283 | delta_ndiv = resp.ndiv_max - resp.ndiv_min; | ||
| 284 | |||
| 285 | if (unlikely(delta_ndiv == 0)) { | ||
| 286 | num_freqs = 1; | ||
| 287 | } else { | ||
| 288 | /* We store both ndiv_min and ndiv_max hence the +1 */ | ||
| 289 | num_freqs = delta_ndiv / freq_table_step_size + 1; | ||
| 290 | } | ||
| 291 | |||
| 292 | num_freqs += (delta_ndiv % freq_table_step_size) ? 1 : 0; | ||
| 293 | |||
| 294 | freq_table = devm_kcalloc(&pdev->dev, num_freqs + 1, | ||
| 295 | sizeof(*freq_table), GFP_KERNEL); | ||
| 296 | if (!freq_table) | ||
| 297 | return ERR_PTR(-ENOMEM); | ||
| 298 | |||
| 299 | for (index = 0, ndiv = resp.ndiv_min; | ||
| 300 | ndiv < resp.ndiv_max; | ||
| 301 | index++, ndiv += freq_table_step_size) { | ||
| 302 | freq_table[index].driver_data = ndiv; | ||
| 303 | freq_table[index].frequency = map_ndiv_to_freq(&resp, ndiv); | ||
| 304 | } | ||
| 305 | |||
| 306 | freq_table[index].driver_data = resp.ndiv_max; | ||
| 307 | freq_table[index++].frequency = map_ndiv_to_freq(&resp, resp.ndiv_max); | ||
| 308 | freq_table[index].frequency = CPUFREQ_TABLE_END; | ||
| 309 | |||
| 310 | return freq_table; | ||
| 311 | } | ||
| 312 | |||
| 313 | static int tegra194_cpufreq_probe(struct platform_device *pdev) | ||
| 314 | { | ||
| 315 | struct tegra194_cpufreq_data *data; | ||
| 316 | struct tegra_bpmp *bpmp; | ||
| 317 | int err, i; | ||
| 318 | |||
| 319 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
| 320 | if (!data) | ||
| 321 | return -ENOMEM; | ||
| 322 | |||
| 323 | data->num_clusters = MAX_CLUSTERS; | ||
| 324 | data->tables = devm_kcalloc(&pdev->dev, data->num_clusters, | ||
| 325 | sizeof(*data->tables), GFP_KERNEL); | ||
| 326 | if (!data->tables) | ||
| 327 | return -ENOMEM; | ||
| 328 | |||
| 329 | platform_set_drvdata(pdev, data); | ||
| 330 | |||
| 331 | bpmp = tegra_bpmp_get(&pdev->dev); | ||
| 332 | if (IS_ERR(bpmp)) | ||
| 333 | return PTR_ERR(bpmp); | ||
| 334 | |||
| 335 | read_counters_wq = alloc_workqueue("read_counters_wq", __WQ_LEGACY, 1); | ||
| 336 | if (!read_counters_wq) { | ||
| 337 | dev_err(&pdev->dev, "fail to create_workqueue\n"); | ||
| 338 | err = -EINVAL; | ||
| 339 | goto put_bpmp; | ||
| 340 | } | ||
| 341 | |||
| 342 | for (i = 0; i < data->num_clusters; i++) { | ||
| 343 | data->tables[i] = init_freq_table(pdev, bpmp, i); | ||
| 344 | if (IS_ERR(data->tables[i])) { | ||
| 345 | err = PTR_ERR(data->tables[i]); | ||
| 346 | goto err_free_res; | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | tegra194_cpufreq_driver.driver_data = data; | ||
| 351 | |||
| 352 | err = cpufreq_register_driver(&tegra194_cpufreq_driver); | ||
| 353 | if (!err) | ||
| 354 | goto put_bpmp; | ||
| 355 | |||
| 356 | err_free_res: | ||
| 357 | tegra194_cpufreq_free_resources(); | ||
| 358 | put_bpmp: | ||
| 359 | tegra_bpmp_put(bpmp); | ||
| 360 | return err; | ||
| 361 | } | ||
| 362 | |||
| 363 | static int tegra194_cpufreq_remove(struct platform_device *pdev) | ||
| 364 | { | ||
| 365 | cpufreq_unregister_driver(&tegra194_cpufreq_driver); | ||
| 366 | tegra194_cpufreq_free_resources(); | ||
| 367 | |||
| 368 | return 0; | ||
| 369 | } | ||
| 370 | |||
| 371 | static const struct of_device_id tegra194_cpufreq_of_match[] = { | ||
| 372 | { .compatible = "nvidia,tegra194-ccplex", }, | ||
| 373 | { /* sentinel */ } | ||
| 374 | }; | ||
| 375 | MODULE_DEVICE_TABLE(of, tegra194_cpufreq_of_match); | ||
| 376 | |||
| 377 | static struct platform_driver tegra194_ccplex_driver = { | ||
| 378 | .driver = { | ||
| 379 | .name = "tegra194-cpufreq", | ||
| 380 | .of_match_table = tegra194_cpufreq_of_match, | ||
| 381 | }, | ||
| 382 | .probe = tegra194_cpufreq_probe, | ||
| 383 | .remove = tegra194_cpufreq_remove, | ||
| 384 | }; | ||
| 385 | module_platform_driver(tegra194_ccplex_driver); | ||
| 386 | |||
| 387 | MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); | ||
| 388 | MODULE_AUTHOR("Sumit Gupta <sumitg@nvidia.com>"); | ||
| 389 | MODULE_DESCRIPTION("NVIDIA Tegra194 cpufreq driver"); | ||
| 390 | MODULE_LICENSE("GPL v2"); | ||
| @@ -831,6 +831,37 @@ static int _set_required_opps(struct device *dev, · 830 unmodified lines | |||
| 831 | return ret; | 831 | return ret; |
| 832 | } | 832 | } |
| 833 | 833 | ||
| 834 | /** | ||
| 835 | * dev_pm_opp_set_bw() - sets bandwidth levels corresponding to an opp | ||
| 836 | * @dev: device for which we do this operation | ||
| 837 | * @opp: opp based on which the bandwidth levels are to be configured | ||
| 838 | * | ||
| 839 | * This configures the bandwidth to the levels specified by the OPP. However | ||
| 840 | * if the OPP specified is NULL the bandwidth levels are cleared out. | ||
| 841 | * | ||
| 842 | * Return: 0 on success or a negative error value. | ||
| 843 | */ | ||
| 844 | int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) | ||
| 845 | { | ||
| 846 | struct opp_table *opp_table; | ||
| 847 | int ret; | ||
| 848 | |||
| 849 | opp_table = _find_opp_table(dev); | ||
| 850 | if (IS_ERR(opp_table)) { | ||
| 851 | dev_err(dev, "%s: device opp table doesn't exist\n", __func__); | ||
| 852 | return PTR_ERR(opp_table); | ||
| 853 | } | ||
| 854 | |||
| 855 | if (opp) | ||
| 856 | ret = _set_opp_bw(opp_table, opp, dev, false); | ||
| 857 | else | ||
| 858 | ret = _set_opp_bw(opp_table, NULL, dev, true); | ||
| 859 | |||
| 860 | dev_pm_opp_put_opp_table(opp_table); | ||
| 861 | return ret; | ||
| 862 | } | ||
| 863 | EXPORT_SYMBOL_GPL(dev_pm_opp_set_bw); | ||
| 864 | |||
| 834 | /** | 865 | /** |
| 835 | * dev_pm_opp_set_rate() - Configure new OPP based on frequency | 866 | * dev_pm_opp_set_rate() - Configure new OPP based on frequency |
| 836 | * @dev: device for which we do this operation | 867 | * @dev: device for which we do this operation |
| @@ -127,7 +127,7 @@ struct cpufreq_policy { · 126 unmodified lines | |||
| 127 | 127 | ||
| 128 | /* Cached frequency lookup from cpufreq_driver_resolve_freq. */ | 128 | /* Cached frequency lookup from cpufreq_driver_resolve_freq. */ |
| 129 | unsigned int cached_target_freq; | 129 | unsigned int cached_target_freq; |
| 130 | int cached_resolved_idx; | 130 | unsigned int cached_resolved_idx; |
| 131 | 131 | ||
| 132 | /* Synchronization for frequency transitions */ | 132 | /* Synchronization for frequency transitions */ |
| 133 | bool transition_ongoing; /* Tracks transition status */ | 133 | bool transition_ongoing; /* Tracks transition status */ |
| @@ -152,6 +152,7 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names · 151 unmodified lines | |||
| 152 | void dev_pm_opp_detach_genpd(struct opp_table *opp_table); | 152 | void dev_pm_opp_detach_genpd(struct opp_table *opp_table); |
| 153 | int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); | 153 | int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); |
| 154 | int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); | 154 | int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); |
| 155 | int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp); | ||
| 155 | int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); | 156 | int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); |
| 156 | int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); | 157 | int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); |
| 157 | void dev_pm_opp_remove_table(struct device *dev); | 158 | void dev_pm_opp_remove_table(struct device *dev); |
| @@ -343,6 +344,11 @@ static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_f · 185 unmodified lines | |||
| 343 | return -ENOTSUPP; | 344 | return -ENOTSUPP; |
| 344 | } | 345 | } |
| 345 | 346 | ||
| 347 | static inline int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) | ||
| 348 | { | ||
| 349 | return -EOPNOTSUPP; | ||
| 350 | } | ||
| 351 | |||
| 346 | static inline int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask) | 352 | static inline int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask) |
| 347 | { | 353 | { |
| 348 | return -ENOTSUPP; | 354 | return -ENOTSUPP; |
| @@ -60,58 +60,151 @@ extern void pm_runtime_put_suppliers(struct device *dev); · 59 unmodified lines | |||
| 60 | extern void pm_runtime_new_link(struct device *dev); | 60 | extern void pm_runtime_new_link(struct device *dev); |
| 61 | extern void pm_runtime_drop_link(struct device *dev); | 61 | extern void pm_runtime_drop_link(struct device *dev); |
| 62 | 62 | ||
| 63 | /** | ||
| 64 | * pm_runtime_get_if_in_use - Conditionally bump up runtime PM usage counter. | ||
| 65 | * @dev: Target device. | ||
| 66 | * | ||
| 67 | * Increment the runtime PM usage counter of @dev if its runtime PM status is | ||
| 68 | * %RPM_ACTIVE and its runtime PM usage counter is greater than 0. | ||
| 69 | */ | ||
| 63 | static inline int pm_runtime_get_if_in_use(struct device *dev) | 70 | static inline int pm_runtime_get_if_in_use(struct device *dev) |
| 64 | { | 71 | { |
| 65 | return pm_runtime_get_if_active(dev, false); | 72 | return pm_runtime_get_if_active(dev, false); |
| 66 | } | 73 | } |
| 67 | 74 | ||
| 75 | /** | ||
| 76 | * pm_suspend_ignore_children - Set runtime PM behavior regarding children. | ||
| 77 | * @dev: Target device. | ||
| 78 | * @enable: Whether or not to ignore possible dependencies on children. | ||
| 79 | * | ||
| 80 | * The dependencies of @dev on its children will not be taken into account by | ||
| 81 | * the runtime PM framework going forward if @enable is %true, or they will | ||
| 82 | * be taken into account otherwise. | ||
| 83 | */ | ||
| 68 | static inline void pm_suspend_ignore_children(struct device *dev, bool enable) | 84 | static inline void pm_suspend_ignore_children(struct device *dev, bool enable) |
| 69 | { | 85 | { |
| 70 | dev->power.ignore_children = enable; | 86 | dev->power.ignore_children = enable; |
| 71 | } | 87 | } |
| 72 | 88 | ||
| 89 | /** | ||
| 90 | * pm_runtime_get_noresume - Bump up runtime PM usage counter of a device. | ||
| 91 | * @dev: Target device. | ||
| 92 | */ | ||
| 73 | static inline void pm_runtime_get_noresume(struct device *dev) | 93 | static inline void pm_runtime_get_noresume(struct device *dev) |
| 74 | { | 94 | { |
| 75 | atomic_inc(&dev->power.usage_count); | 95 | atomic_inc(&dev->power.usage_count); |
| 76 | } | 96 | } |
| 77 | 97 | ||
| 98 | /** | ||
| 99 | * pm_runtime_put_noidle - Drop runtime PM usage counter of a device. | ||
| 100 | * @dev: Target device. | ||
| 101 | * | ||
| 102 | * Decrement the runtime PM usage counter of @dev unless it is 0 already. | ||
| 103 | */ | ||
| 78 | static inline void pm_runtime_put_noidle(struct device *dev) | 104 | static inline void pm_runtime_put_noidle(struct device *dev) |
| 79 | { | 105 | { |
| 80 | atomic_add_unless(&dev->power.usage_count, -1, 0); | 106 | atomic_add_unless(&dev->power.usage_count, -1, 0); |
| 81 | } | 107 | } |
| 82 | 108 | ||
| 109 | /** | ||
| 110 | * pm_runtime_suspended - Check whether or not a device is runtime-suspended. | ||
| 111 | * @dev: Target device. | ||
| 112 | * | ||
| 113 | * Return %true if runtime PM is enabled for @dev and its runtime PM status is | ||
| 114 | * %RPM_SUSPENDED, or %false otherwise. | ||
| 115 | * | ||
| 116 | * Note that the return value of this function can only be trusted if it is | ||
| 117 | * called under the runtime PM lock of @dev or under conditions in which | ||
| 118 | * runtime PM cannot be either disabled or enabled for @dev and its runtime PM | ||
| 119 | * status cannot change. | ||
| 120 | */ | ||
| 83 | static inline bool pm_runtime_suspended(struct device *dev) | 121 | static inline bool pm_runtime_suspended(struct device *dev) |
| 84 | { | 122 | { |
| 85 | return dev->power.runtime_status == RPM_SUSPENDED | 123 | return dev->power.runtime_status == RPM_SUSPENDED |
| 86 | && !dev->power.disable_depth; | 124 | && !dev->power.disable_depth; |
| 87 | } | 125 | } |
| 88 | 126 | ||
| 127 | /** | ||
| 128 | * pm_runtime_active - Check whether or not a device is runtime-active. | ||
| 129 | * @dev: Target device. | ||
| 130 | * | ||
| 131 | * Return %true if runtime PM is enabled for @dev and its runtime PM status is | ||
| 132 | * %RPM_ACTIVE, or %false otherwise. | ||
| 133 | * | ||
| 134 | * Note that the return value of this function can only be trusted if it is | ||
| 135 | * called under the runtime PM lock of @dev or under conditions in which | ||
| 136 | * runtime PM cannot be either disabled or enabled for @dev and its runtime PM | ||
| 137 | * status cannot change. | ||
| 138 | */ | ||
| 89 | static inline bool pm_runtime_active(struct device *dev) | 139 | static inline bool pm_runtime_active(struct device *dev) |
| 90 | { | 140 | { |
| 91 | return dev->power.runtime_status == RPM_ACTIVE | 141 | return dev->power.runtime_status == RPM_ACTIVE |
| 92 | || dev->power.disable_depth; | 142 | || dev->power.disable_depth; |
| 93 | } | 143 | } |
| 94 | 144 | ||
| 145 | /** | ||
| 146 | * pm_runtime_status_suspended - Check if runtime PM status is "suspended". | ||
| 147 | * @dev: Target device. | ||
| 148 | * | ||
| 149 | * Return %true if the runtime PM status of @dev is %RPM_SUSPENDED, or %false | ||
| 150 | * otherwise, regardless of whether or not runtime PM has been enabled for @dev. | ||
| 151 | * | ||
| 152 | * Note that the return value of this function can only be trusted if it is | ||
| 153 | * called under the runtime PM lock of @dev or under conditions in which the | ||
| 154 | * runtime PM status of @dev cannot change. | ||
| 155 | */ | ||
| 95 | static inline bool pm_runtime_status_suspended(struct device *dev) | 156 | static inline bool pm_runtime_status_suspended(struct device *dev) |
| 96 | { | 157 | { |
| 97 | return dev->power.runtime_status == RPM_SUSPENDED; | 158 | return dev->power.runtime_status == RPM_SUSPENDED; |
| 98 | } | 159 | } |
| 99 | 160 | ||
| 161 | /** | ||
| 162 | * pm_runtime_enabled - Check if runtime PM is enabled. | ||
| 163 | * @dev: Target device. | ||
| 164 | * | ||
| 165 | * Return %true if runtime PM is enabled for @dev or %false otherwise. | ||
| 166 | * | ||
| 167 | * Note that the return value of this function can only be trusted if it is | ||
| 168 | * called under the runtime PM lock of @dev or under conditions in which | ||
| 169 | * runtime PM cannot be either disabled or enabled for @dev. | ||
| 170 | */ | ||
| 100 | static inline bool pm_runtime_enabled(struct device *dev) | 171 | static inline bool pm_runtime_enabled(struct device *dev) |
| 101 | { | 172 | { |
| 102 | return !dev->power.disable_depth; | 173 | return !dev->power.disable_depth; |
| 103 | } | 174 | } |
| 104 | 175 | ||
| 176 | /** | ||
| 177 | * pm_runtime_has_no_callbacks - Check if runtime PM callbacks may be present. | ||
| 178 | * @dev: Target device. | ||
| 179 | * | ||
| 180 | * Return %true if @dev is a special device without runtime PM callbacks or | ||
| 181 | * %false otherwise. | ||
| 182 | */ | ||
| 105 | static inline bool pm_runtime_has_no_callbacks(struct device *dev) | 183 | static inline bool pm_runtime_has_no_callbacks(struct device *dev) |
| 106 | { | 184 | { |
| 107 | return dev->power.no_callbacks; | 185 | return dev->power.no_callbacks; |
| 108 | } | 186 | } |
| 109 | 187 | ||
| 188 | /** | ||
| 189 | * pm_runtime_mark_last_busy - Update the last access time of a device. | ||
| 190 | * @dev: Target device. | ||
| 191 | * | ||
| 192 | * Update the last access time of @dev used by the runtime PM autosuspend | ||
| 193 | * mechanism to the current time as returned by ktime_get_mono_fast_ns(). | ||
| 194 | */ | ||
| 110 | static inline void pm_runtime_mark_last_busy(struct device *dev) | 195 | static inline void pm_runtime_mark_last_busy(struct device *dev) |
| 111 | { | 196 | { |
| 112 | WRITE_ONCE(dev->power.last_busy, ktime_get_mono_fast_ns()); | 197 | WRITE_ONCE(dev->power.last_busy, ktime_get_mono_fast_ns()); |
| 113 | } | 198 | } |
| 114 | 199 | ||
| 200 | /** | ||
| 201 | * pm_runtime_is_irq_safe - Check if runtime PM can work in interrupt context. | ||
| 202 | * @dev: Target device. | ||
| 203 | * | ||
| 204 | * Return %true if @dev has been marked as an "IRQ-safe" device (with respect | ||
| 205 | * to runtime PM), in which case its runtime PM callabcks can be expected to | ||
| 206 | * work correctly when invoked from interrupt handlers. | ||
| 207 | */ | ||
| 115 | static inline bool pm_runtime_is_irq_safe(struct device *dev) | 208 | static inline bool pm_runtime_is_irq_safe(struct device *dev) |
| 116 | { | 209 | { |
| 117 | return dev->power.irq_safe; | 210 | return dev->power.irq_safe; |
| @@ -191,97 +284,250 @@ static inline void pm_runtime_drop_link(struct device *dev) {} · 73 unmodified lines | |||
| 191 | 284 | ||
| 192 | #endif /* !CONFIG_PM */ | 285 | #endif /* !CONFIG_PM */ |
| 193 | 286 | ||
| 287 | /** | ||
| 288 | * pm_runtime_idle - Conditionally set up autosuspend of a device or suspend it. | ||
| 289 | * @dev: Target device. | ||
| 290 | * | ||
| 291 | * Invoke the "idle check" callback of @dev and, depending on its return value, | ||
| 292 | * set up autosuspend of @dev or suspend it (depending on whether or not | ||
| 293 | * autosuspend has been enabled for it). | ||
| 294 | */ | ||
| 194 | static inline int pm_runtime_idle(struct device *dev) | 295 | static inline int pm_runtime_idle(struct device *dev) |
| 195 | { | 296 | { |
| 196 | return __pm_runtime_idle(dev, 0); | 297 | return __pm_runtime_idle(dev, 0); |
| 197 | } | 298 | } |
| 198 | 299 | ||
| 300 | /** | ||
| 301 | * pm_runtime_suspend - Suspend a device synchronously. | ||
| 302 | * @dev: Target device. | ||
| 303 | */ | ||
| 199 | static inline int pm_runtime_suspend(struct device *dev) | 304 | static inline int pm_runtime_suspend(struct device *dev) |
| 200 | { | 305 | { |
| 201 | return __pm_runtime_suspend(dev, 0); | 306 | return __pm_runtime_suspend(dev, 0); |
| 202 | } | 307 | } |
| 203 | 308 | ||
| 309 | /** | ||
| 310 | * pm_runtime_autosuspend - Set up autosuspend of a device or suspend it. | ||
| 311 | * @dev: Target device. | ||
| 312 | * | ||
| 313 | * Set up autosuspend of @dev or suspend it (depending on whether or not | ||
| 314 | * autosuspend is enabled for it) without engaging its "idle check" callback. | ||
| 315 | */ | ||
| 204 | static inline int pm_runtime_autosuspend(struct device *dev) | 316 | static inline int pm_runtime_autosuspend(struct device *dev) |
| 205 | { | 317 | { |
| 206 | return __pm_runtime_suspend(dev, RPM_AUTO); | 318 | return __pm_runtime_suspend(dev, RPM_AUTO); |
| 207 | } | 319 | } |
| 208 | 320 | ||
| 321 | /** | ||
| 322 | * pm_runtime_resume - Resume a device synchronously. | ||
| 323 | * @dev: Target device. | ||
| 324 | */ | ||
| 209 | static inline int pm_runtime_resume(struct device *dev) | 325 | static inline int pm_runtime_resume(struct device *dev) |
| 210 | { | 326 | { |
| 211 | return __pm_runtime_resume(dev, 0); | 327 | return __pm_runtime_resume(dev, 0); |
| 212 | } | 328 | } |
| 213 | 329 | ||
| 330 | /** | ||
| 331 | * pm_request_idle - Queue up "idle check" execution for a device. | ||
| 332 | * @dev: Target device. | ||
| 333 | * | ||
| 334 | * Queue up a work item to run an equivalent of pm_runtime_idle() for @dev | ||
| 335 | * asynchronously. | ||
| 336 | */ | ||
| 214 | static inline int pm_request_idle(struct device *dev) | 337 | static inline int pm_request_idle(struct device *dev) |
| 215 | { | 338 | { |
| 216 | return __pm_runtime_idle(dev, RPM_ASYNC); | 339 | return __pm_runtime_idle(dev, RPM_ASYNC); |
| 217 | } | 340 | } |
| 218 | 341 | ||
| 342 | /** | ||
| 343 | * pm_request_resume - Queue up runtime-resume of a device. | ||
| 344 | * @dev: Target device. | ||
| 345 | */ | ||
| 219 | static inline int pm_request_resume(struct device *dev) | 346 | static inline int pm_request_resume(struct device *dev) |
| 220 | { | 347 | { |
| 221 | return __pm_runtime_resume(dev, RPM_ASYNC); | 348 | return __pm_runtime_resume(dev, RPM_ASYNC); |
| 222 | } | 349 | } |
| 223 | 350 | ||
| 351 | /** | ||
| 352 | * pm_request_autosuspend - Queue up autosuspend of a device. | ||
| 353 | * @dev: Target device. | ||
| 354 | * | ||
| 355 | * Queue up a work item to run an equivalent pm_runtime_autosuspend() for @dev | ||
| 356 | * asynchronously. | ||
| 357 | */ | ||
| 224 | static inline int pm_request_autosuspend(struct device *dev) | 358 | static inline int pm_request_autosuspend(struct device *dev) |
| 225 | { | 359 | { |
| 226 | return __pm_runtime_suspend(dev, RPM_ASYNC | RPM_AUTO); | 360 | return __pm_runtime_suspend(dev, RPM_ASYNC | RPM_AUTO); |
| 227 | } | 361 | } |
| 228 | 362 | ||
| 363 | /** | ||
| 364 | * pm_runtime_get - Bump up usage counter and queue up resume of a device. | ||
| 365 | * @dev: Target device. | ||
| 366 | * | ||
| 367 | * Bump up the runtime PM usage counter of @dev and queue up a work item to | ||
| 368 | * carry out runtime-resume of it. | ||
| 369 | */ | ||
| 229 | static inline int pm_runtime_get(struct device *dev) | 370 | static inline int pm_runtime_get(struct device *dev) |
| 230 | { | 371 | { |
| 231 | return __pm_runtime_resume(dev, RPM_GET_PUT | RPM_ASYNC); | 372 | return __pm_runtime_resume(dev, RPM_GET_PUT | RPM_ASYNC); |
| 232 | } | 373 | } |
| 233 | 374 | ||
| 375 | /** | ||
| 376 | * pm_runtime_get_sync - Bump up usage counter of a device and resume it. | ||
| 377 | * @dev: Target device. | ||
| 378 | * | ||
| 379 | * Bump up the runtime PM usage counter of @dev and carry out runtime-resume of | ||
| 380 | * it synchronously. | ||
| 381 | * | ||
| 382 | * The possible return values of this function are the same as for | ||
| 383 | * pm_runtime_resume() and the runtime PM usage counter of @dev remains | ||
| 384 | * incremented in all cases, even if it returns an error code. | ||
| 385 | */ | ||
| 234 | static inline int pm_runtime_get_sync(struct device *dev) | 386 | static inline int pm_runtime_get_sync(struct device *dev) |
| 235 | { | 387 | { |
| 236 | return __pm_runtime_resume(dev, RPM_GET_PUT); | 388 | return __pm_runtime_resume(dev, RPM_GET_PUT); |
| 237 | } | 389 | } |
| 238 | 390 | ||
| 391 | /** | ||
| 392 | * pm_runtime_put - Drop device usage counter and queue up "idle check" if 0. | ||
| 393 | * @dev: Target device. | ||
| 394 | * | ||
| 395 | * Decrement the runtime PM usage counter of @dev and if it turns out to be | ||
| 396 | * equal to 0, queue up a work item for @dev like in pm_request_idle(). | ||
| 397 | */ | ||
| 239 | static inline int pm_runtime_put(struct device *dev) | 398 | static inline int pm_runtime_put(struct device *dev) |
| 240 | { | 399 | { |
| 241 | return __pm_runtime_idle(dev, RPM_GET_PUT | RPM_ASYNC); | 400 | return __pm_runtime_idle(dev, RPM_GET_PUT | RPM_ASYNC); |
| 242 | } | 401 | } |
| 243 | 402 | ||
| 403 | /** | ||
| 404 | * pm_runtime_put_autosuspend - Drop device usage counter and queue autosuspend if 0. | ||
| 405 | * @dev: Target device. | ||
| 406 | * | ||
| 407 | * Decrement the runtime PM usage counter of @dev and if it turns out to be | ||
| 408 | * equal to 0, queue up a work item for @dev like in pm_request_autosuspend(). | ||
| 409 | */ | ||
| 244 | static inline int pm_runtime_put_autosuspend(struct device *dev) | 410 | static inline int pm_runtime_put_autosuspend(struct device *dev) |
| 245 | { | 411 | { |
| 246 | return __pm_runtime_suspend(dev, | 412 | return __pm_runtime_suspend(dev, |
| 247 | RPM_GET_PUT | RPM_ASYNC | RPM_AUTO); | 413 | RPM_GET_PUT | RPM_ASYNC | RPM_AUTO); |
| 248 | } | 414 | } |
| 249 | 415 | ||
| 416 | /** | ||
| 417 | * pm_runtime_put_sync - Drop device usage counter and run "idle check" if 0. | ||
| 418 | * @dev: Target device. | ||
| 419 | * | ||
| 420 | * Decrement the runtime PM usage counter of @dev and if it turns out to be | ||
| 421 | * equal to 0, invoke the "idle check" callback of @dev and, depending on its | ||
| 422 | * return value, set up autosuspend of @dev or suspend it (depending on whether | ||
| 423 | * or not autosuspend has been enabled for it). | ||
| 424 | * | ||
| 425 | * The possible return values of this function are the same as for | ||
| 426 | * pm_runtime_idle() and the runtime PM usage counter of @dev remains | ||
| 427 | * decremented in all cases, even if it returns an error code. | ||
| 428 | */ | ||
| 250 | static inline int pm_runtime_put_sync(struct device *dev) | 429 | static inline int pm_runtime_put_sync(struct device *dev) |
| 251 | { | 430 | { |
| 252 | return __pm_runtime_idle(dev, RPM_GET_PUT); | 431 | return __pm_runtime_idle(dev, RPM_GET_PUT); |
| 253 | } | 432 | } |
| 254 | 433 | ||
| 434 | /** | ||
| 435 | * pm_runtime_put_sync_suspend - Drop device usage counter and suspend if 0. | ||
| 436 | * @dev: Target device. | ||
| 437 | * | ||
| 438 | * Decrement the runtime PM usage counter of @dev and if it turns out to be | ||
| 439 | * equal to 0, carry out runtime-suspend of @dev synchronously. | ||
| 440 | * | ||
| 441 | * The possible return values of this function are the same as for | ||
| 442 | * pm_runtime_suspend() and the runtime PM usage counter of @dev remains | ||
| 443 | * decremented in all cases, even if it returns an error code. | ||
| 444 | */ | ||
| 255 | static inline int pm_runtime_put_sync_suspend(struct device *dev) | 445 | static inline int pm_runtime_put_sync_suspend(struct device *dev) |
| 256 | { | 446 | { |
| 257 | return __pm_runtime_suspend(dev, RPM_GET_PUT); | 447 | return __pm_runtime_suspend(dev, RPM_GET_PUT); |
| 258 | } | 448 | } |
| 259 | 449 | ||
| 450 | /** | ||
| 451 | * pm_runtime_put_sync_autosuspend - Drop device usage counter and autosuspend if 0. | ||
| 452 | * @dev: Target device. | ||
| 453 | * | ||
| 454 | * Decrement the runtime PM usage counter of @dev and if it turns out to be | ||
| 455 | * equal to 0, set up autosuspend of @dev or suspend it synchronously (depending | ||
| 456 | * on whether or not autosuspend has been enabled for it). | ||
| 457 | * | ||
| 458 | * The possible return values of this function are the same as for | ||
| 459 | * pm_runtime_autosuspend() and the runtime PM usage counter of @dev remains | ||
| 460 | * decremented in all cases, even if it returns an error code. | ||
| 461 | */ | ||
| 260 | static inline int pm_runtime_put_sync_autosuspend(struct device *dev) | 462 | static inline int pm_runtime_put_sync_autosuspend(struct device *dev) |
| 261 | { | 463 | { |
| 262 | return __pm_runtime_suspend(dev, RPM_GET_PUT | RPM_AUTO); | 464 | return __pm_runtime_suspend(dev, RPM_GET_PUT | RPM_AUTO); |
| 263 | } | 465 | } |
| 264 | 466 | ||
| 467 | /** | ||
| 468 | * pm_runtime_set_active - Set runtime PM status to "active". | ||
| 469 | * @dev: Target device. | ||
| 470 | * | ||
| 471 | * Set the runtime PM status of @dev to %RPM_ACTIVE and ensure that dependencies | ||
| 472 | * of it will be taken into account. | ||
| 473 | * | ||
| 474 | * It is not valid to call this function for devices with runtime PM enabled. | ||
| 475 | */ | ||
| 265 | static inline int pm_runtime_set_active(struct device *dev) | 476 | static inline int pm_runtime_set_active(struct device *dev) |
| 266 | { | 477 | { |
| 267 | return __pm_runtime_set_status(dev, RPM_ACTIVE); | 478 | return __pm_runtime_set_status(dev, RPM_ACTIVE); |
| 268 | } | 479 | } |
| 269 | 480 | ||
| 481 | /** | ||
| 482 | * pm_runtime_set_suspended - Set runtime PM status to "active". | ||
| 483 | * @dev: Target device. | ||
| 484 | * | ||
| 485 | * Set the runtime PM status of @dev to %RPM_SUSPENDED and ensure that | ||
| 486 | * dependencies of it will be taken into account. | ||
| 487 | * | ||
| 488 | * It is not valid to call this function for devices with runtime PM enabled. | ||
| 489 | */ | ||
| 270 | static inline int pm_runtime_set_suspended(struct device *dev) | 490 | static inline int pm_runtime_set_suspended(struct device *dev) |
| 271 | { | 491 | { |
| 272 | return __pm_runtime_set_status(dev, RPM_SUSPENDED); | 492 | return __pm_runtime_set_status(dev, RPM_SUSPENDED); |
| 273 | } | 493 | } |
| 274 | 494 | ||
| 495 | /** | ||
| 496 | * pm_runtime_disable - Disable runtime PM for a device. | ||
| 497 | * @dev: Target device. | ||
| 498 | * | ||
| 499 | * Prevent the runtime PM framework from working with @dev (by incrementing its | ||
| 500 | * "blocking" counter). | ||
| 501 | * | ||
| 502 | * For each invocation of this function for @dev there must be a matching | ||
| 503 | * pm_runtime_enable() call in order for runtime PM to be enabled for it. | ||
| 504 | */ | ||
| 275 | static inline void pm_runtime_disable(struct device *dev) | 505 | static inline void pm_runtime_disable(struct device *dev) |
| 276 | { | 506 | { |
| 277 | __pm_runtime_disable(dev, true); | 507 | __pm_runtime_disable(dev, true); |
| 278 | } | 508 | } |
| 279 | 509 | ||
| 510 | /** | ||
| 511 | * pm_runtime_use_autosuspend - Allow autosuspend to be used for a device. | ||
| 512 | * @dev: Target device. | ||
| 513 | * | ||
| 514 | * Allow the runtime PM autosuspend mechanism to be used for @dev whenever | ||
| 515 | * requested (or "autosuspend" will be handled as direct runtime-suspend for | ||
| 516 | * it). | ||
| 517 | */ | ||
| 280 | static inline void pm_runtime_use_autosuspend(struct device *dev) | 518 | static inline void pm_runtime_use_autosuspend(struct device *dev) |
| 281 | { | 519 | { |
| 282 | __pm_runtime_use_autosuspend(dev, true); | 520 | __pm_runtime_use_autosuspend(dev, true); |
| 283 | } | 521 | } |
| 284 | 522 | ||
| 523 | /** | ||
| 524 | * pm_runtime_dont_use_autosuspend - Prevent autosuspend from being used. | ||
| 525 | * @dev: Target device. | ||
| 526 | * | ||
| 527 | * Prevent the runtime PM autosuspend mechanism from being used for @dev which | ||
| 528 | * means that "autosuspend" will be handled as direct runtime-suspend for it | ||
| 529 | * going forward. | ||
| 530 | */ | ||
| 285 | static inline void pm_runtime_dont_use_autosuspend(struct device *dev) | 531 | static inline void pm_runtime_dont_use_autosuspend(struct device *dev) |
| 286 | { | 532 | { |
| 287 | __pm_runtime_use_autosuspend(dev, false); | 533 | __pm_runtime_use_autosuspend(dev, false); |