Commit | Line | Data |
---|---|---|
9f123def VK |
1 | /* |
2 | * CPUFreq support for Armada 370/XP platforms. | |
3 | * | |
4 | * Copyright (C) 2012-2016 Marvell | |
5 | * | |
6 | * Yehuda Yitschak <yehuday@marvell.com> | |
7 | * Gregory Clement <gregory.clement@free-electrons.com> | |
8 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | |
9 | * | |
10 | * This file is licensed under the terms of the GNU General Public | |
11 | * License version 2. This program is licensed "as is" without any | |
12 | * warranty of any kind, whether express or implied. | |
13 | */ | |
14 | ||
15 | #define pr_fmt(fmt) "mvebu-pmsu: " fmt | |
16 | ||
17 | #include <linux/clk.h> | |
18 | #include <linux/cpu.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/of_address.h> | |
22 | #include <linux/platform_device.h> | |
23 | #include <linux/pm_opp.h> | |
24 | #include <linux/resource.h> | |
25 | ||
26 | static int __init armada_xp_pmsu_cpufreq_init(void) | |
27 | { | |
28 | struct device_node *np; | |
29 | struct resource res; | |
30 | int ret, cpu; | |
31 | ||
32 | if (!of_machine_is_compatible("marvell,armadaxp")) | |
33 | return 0; | |
34 | ||
35 | /* | |
36 | * In order to have proper cpufreq handling, we need to ensure | |
37 | * that the Device Tree description of the CPU clock includes | |
38 | * the definition of the PMU DFS registers. If not, we do not | |
39 | * register the clock notifier and the cpufreq driver. This | |
40 | * piece of code is only for compatibility with old Device | |
41 | * Trees. | |
42 | */ | |
43 | np = of_find_compatible_node(NULL, NULL, "marvell,armada-xp-cpu-clock"); | |
44 | if (!np) | |
45 | return 0; | |
46 | ||
47 | ret = of_address_to_resource(np, 1, &res); | |
48 | if (ret) { | |
49 | pr_warn(FW_WARN "not enabling cpufreq, deprecated armada-xp-cpu-clock binding\n"); | |
50 | of_node_put(np); | |
51 | return 0; | |
52 | } | |
53 | ||
54 | of_node_put(np); | |
55 | ||
56 | /* | |
57 | * For each CPU, this loop registers the operating points | |
58 | * supported (which are the nominal CPU frequency and half of | |
59 | * it), and registers the clock notifier that will take care | |
60 | * of doing the PMSU part of a frequency transition. | |
61 | */ | |
62 | for_each_possible_cpu(cpu) { | |
63 | struct device *cpu_dev; | |
64 | struct clk *clk; | |
65 | int ret; | |
66 | ||
67 | cpu_dev = get_cpu_device(cpu); | |
68 | if (!cpu_dev) { | |
69 | pr_err("Cannot get CPU %d\n", cpu); | |
70 | continue; | |
71 | } | |
72 | ||
73 | clk = clk_get(cpu_dev, 0); | |
74 | if (IS_ERR(clk)) { | |
75 | pr_err("Cannot get clock for CPU %d\n", cpu); | |
76 | return PTR_ERR(clk); | |
77 | } | |
78 | ||
79 | /* | |
80 | * In case of a failure of dev_pm_opp_add(), we don't | |
81 | * bother with cleaning up the registered OPP (there's | |
82 | * no function to do so), and simply cancel the | |
83 | * registration of the cpufreq device. | |
84 | */ | |
85 | ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk), 0); | |
86 | if (ret) { | |
87 | clk_put(clk); | |
88 | return ret; | |
89 | } | |
90 | ||
91 | ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk) / 2, 0); | |
92 | if (ret) { | |
93 | clk_put(clk); | |
94 | return ret; | |
95 | } | |
96 | ||
97 | ret = dev_pm_opp_set_sharing_cpus(cpu_dev, | |
98 | cpumask_of(cpu_dev->id)); | |
99 | if (ret) | |
100 | dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", | |
101 | __func__, ret); | |
102 | } | |
103 | ||
104 | platform_device_register_simple("cpufreq-dt", -1, NULL, 0); | |
105 | return 0; | |
106 | } | |
107 | device_initcall(armada_xp_pmsu_cpufreq_init); |