Commit 6754f556103be5bd172263b1075ddbb7157afbad
Committed by
Rafael J. Wysocki
1 parent
300586778d
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
cpufreq / highbank: add support for highbank cpufreq
Highbank processors depend on the external ECME to perform voltage management based on a requested frequency. Communication between the A9 cores and the ECME happens over the pl320 IPC channel. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Reviewed-by: Shawn Guo <shawn.guo@linaro.org> Reviewed-by: Mike Turquette <mturquette@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Showing 6 changed files with 149 additions and 2 deletions Side-by-side Diff
arch/arm/boot/dts/highbank.dts
... | ... | @@ -37,6 +37,16 @@ |
37 | 37 | next-level-cache = <&L2>; |
38 | 38 | clocks = <&a9pll>; |
39 | 39 | clock-names = "cpu"; |
40 | + operating-points = < | |
41 | + /* kHz ignored */ | |
42 | + 1300000 1000000 | |
43 | + 1200000 1000000 | |
44 | + 1100000 1000000 | |
45 | + 800000 1000000 | |
46 | + 400000 1000000 | |
47 | + 200000 1000000 | |
48 | + >; | |
49 | + clock-latency = <100000>; | |
40 | 50 | }; |
41 | 51 | |
42 | 52 | cpu@901 { |
arch/arm/mach-highbank/Kconfig
drivers/cpufreq/Kconfig.arm
... | ... | @@ -83,4 +83,19 @@ |
83 | 83 | default y |
84 | 84 | help |
85 | 85 | This adds the CPUFreq driver support for SPEAr SOCs. |
86 | + | |
87 | +config ARM_HIGHBANK_CPUFREQ | |
88 | + tristate "Calxeda Highbank-based" | |
89 | + depends on ARCH_HIGHBANK | |
90 | + select CPU_FREQ_TABLE | |
91 | + select GENERIC_CPUFREQ_CPU0 | |
92 | + select PM_OPP | |
93 | + select REGULATOR | |
94 | + | |
95 | + default m | |
96 | + help | |
97 | + This adds the CPUFreq driver for Calxeda Highbank SoC | |
98 | + based boards. | |
99 | + | |
100 | + If in doubt, say N. |
drivers/cpufreq/Makefile
... | ... | @@ -51,8 +51,9 @@ |
51 | 51 | obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o |
52 | 52 | obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o |
53 | 53 | obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o |
54 | -obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o | |
54 | +obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o | |
55 | 55 | obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o |
56 | +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o | |
56 | 57 | |
57 | 58 | ################################################################################## |
58 | 59 | # PowerPC platform drivers |
drivers/cpufreq/cpufreq-cpu0.c
... | ... | @@ -182,7 +182,11 @@ |
182 | 182 | struct device_node *np; |
183 | 183 | int ret; |
184 | 184 | |
185 | - np = of_find_node_by_path("/cpus/cpu@0"); | |
185 | + for_each_child_of_node(of_find_node_by_path("/cpus"), np) { | |
186 | + if (of_get_property(np, "operating-points", NULL)) | |
187 | + break; | |
188 | + } | |
189 | + | |
186 | 190 | if (!np) { |
187 | 191 | pr_err("failed to find cpu0 node\n"); |
188 | 192 | return -ENOENT; |
drivers/cpufreq/highbank-cpufreq.c
1 | +/* | |
2 | + * Copyright (C) 2012 Calxeda, Inc. | |
3 | + * | |
4 | + * This program is free software; you can redistribute it and/or modify | |
5 | + * it under the terms of the GNU General Public License version 2 as | |
6 | + * published by the Free Software Foundation. | |
7 | + * | |
8 | + * This driver provides the clk notifier callbacks that are used when | |
9 | + * the cpufreq-cpu0 driver changes to frequency to alert the highbank | |
10 | + * EnergyCore Management Engine (ECME) about the need to change | |
11 | + * voltage. The ECME interfaces with the actual voltage regulators. | |
12 | + */ | |
13 | + | |
14 | +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
15 | + | |
16 | +#include <linux/kernel.h> | |
17 | +#include <linux/module.h> | |
18 | +#include <linux/clk.h> | |
19 | +#include <linux/cpu.h> | |
20 | +#include <linux/err.h> | |
21 | +#include <linux/of.h> | |
22 | +#include <linux/mailbox.h> | |
23 | + | |
24 | +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 | |
25 | +#define HB_CPUFREQ_IPC_LEN 7 | |
26 | +#define HB_CPUFREQ_VOLT_RETRIES 15 | |
27 | + | |
28 | +static int hb_voltage_change(unsigned int freq) | |
29 | +{ | |
30 | + int i; | |
31 | + u32 msg[HB_CPUFREQ_IPC_LEN]; | |
32 | + | |
33 | + msg[0] = HB_CPUFREQ_CHANGE_NOTE; | |
34 | + msg[1] = freq / 1000000; | |
35 | + for (i = 2; i < HB_CPUFREQ_IPC_LEN; i++) | |
36 | + msg[i] = 0; | |
37 | + | |
38 | + return pl320_ipc_transmit(msg); | |
39 | +} | |
40 | + | |
41 | +static int hb_cpufreq_clk_notify(struct notifier_block *nb, | |
42 | + unsigned long action, void *hclk) | |
43 | +{ | |
44 | + struct clk_notifier_data *clk_data = hclk; | |
45 | + int i = 0; | |
46 | + | |
47 | + if (action == PRE_RATE_CHANGE) { | |
48 | + if (clk_data->new_rate > clk_data->old_rate) | |
49 | + while (hb_voltage_change(clk_data->new_rate)) | |
50 | + if (i++ > HB_CPUFREQ_VOLT_RETRIES) | |
51 | + return NOTIFY_BAD; | |
52 | + } else if (action == POST_RATE_CHANGE) { | |
53 | + if (clk_data->new_rate < clk_data->old_rate) | |
54 | + while (hb_voltage_change(clk_data->new_rate)) | |
55 | + if (i++ > HB_CPUFREQ_VOLT_RETRIES) | |
56 | + return NOTIFY_BAD; | |
57 | + } | |
58 | + | |
59 | + return NOTIFY_DONE; | |
60 | +} | |
61 | + | |
62 | +static struct notifier_block hb_cpufreq_clk_nb = { | |
63 | + .notifier_call = hb_cpufreq_clk_notify, | |
64 | +}; | |
65 | + | |
66 | +static int hb_cpufreq_driver_init(void) | |
67 | +{ | |
68 | + struct device *cpu_dev; | |
69 | + struct clk *cpu_clk; | |
70 | + struct device_node *np; | |
71 | + int ret; | |
72 | + | |
73 | + if (!of_machine_is_compatible("calxeda,highbank")) | |
74 | + return -ENODEV; | |
75 | + | |
76 | + for_each_child_of_node(of_find_node_by_path("/cpus"), np) | |
77 | + if (of_get_property(np, "operating-points", NULL)) | |
78 | + break; | |
79 | + | |
80 | + if (!np) { | |
81 | + pr_err("failed to find highbank cpufreq node\n"); | |
82 | + return -ENOENT; | |
83 | + } | |
84 | + | |
85 | + cpu_dev = get_cpu_device(0); | |
86 | + if (!cpu_dev) { | |
87 | + pr_err("failed to get highbank cpufreq device\n"); | |
88 | + ret = -ENODEV; | |
89 | + goto out_put_node; | |
90 | + } | |
91 | + | |
92 | + cpu_dev->of_node = np; | |
93 | + | |
94 | + cpu_clk = clk_get(cpu_dev, NULL); | |
95 | + if (IS_ERR(cpu_clk)) { | |
96 | + ret = PTR_ERR(cpu_clk); | |
97 | + pr_err("failed to get cpu0 clock: %d\n", ret); | |
98 | + goto out_put_node; | |
99 | + } | |
100 | + | |
101 | + ret = clk_notifier_register(cpu_clk, &hb_cpufreq_clk_nb); | |
102 | + if (ret) { | |
103 | + pr_err("failed to register clk notifier: %d\n", ret); | |
104 | + goto out_put_node; | |
105 | + } | |
106 | + | |
107 | +out_put_node: | |
108 | + of_node_put(np); | |
109 | + return ret; | |
110 | +} | |
111 | +module_init(hb_cpufreq_driver_init); | |
112 | + | |
113 | +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); | |
114 | +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); | |
115 | +MODULE_LICENSE("GPL"); |