Merge remote-tracking branch 'lsk/v3.10/topic/gator' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / clk / versatile / clk-vexpress-spc.c
1 /*
2  * Copyright (C) 2012 ARM Limited
3  * Copyright (C) 2012 Linaro
4  *
5  * Author: Viresh Kumar <viresh.kumar@linaro.org>
6  *
7  * This file is licensed under the terms of the GNU General Public
8  * License version 2. This program is licensed "as is" without any
9  * warranty of any kind, whether express or implied.
10  */
11
12 /* SPC clock programming interface for Vexpress cpus */
13
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16 #include <linux/clk-provider.h>
17 #include <linux/clkdev.h>
18 #include <linux/err.h>
19 #include <linux/io.h>
20 #include <linux/of.h>
21 #include <linux/slab.h>
22 #include <linux/types.h>
23 #include <linux/vexpress.h>
24
25 struct clk_spc {
26         struct clk_hw hw;
27         spinlock_t *lock;
28         int cluster;
29 };
30
31 #define to_clk_spc(spc) container_of(spc, struct clk_spc, hw)
32
33 static unsigned long spc_recalc_rate(struct clk_hw *hw,
34                 unsigned long parent_rate)
35 {
36         struct clk_spc *spc = to_clk_spc(hw);
37         u32 freq;
38
39         if (vexpress_spc_get_performance(spc->cluster, &freq)) {
40                 return -EIO;
41                 pr_err("%s: Failed", __func__);
42         }
43
44         return freq * 1000;
45 }
46
47 static long spc_round_rate(struct clk_hw *hw, unsigned long drate,
48                 unsigned long *parent_rate)
49 {
50         return drate;
51 }
52
53 static int spc_set_rate(struct clk_hw *hw, unsigned long rate,
54                 unsigned long parent_rate)
55 {
56         struct clk_spc *spc = to_clk_spc(hw);
57
58         return vexpress_spc_set_performance(spc->cluster, rate / 1000);
59 }
60
61 static struct clk_ops clk_spc_ops = {
62         .recalc_rate = spc_recalc_rate,
63         .round_rate = spc_round_rate,
64         .set_rate = spc_set_rate,
65 };
66
67 struct clk *vexpress_clk_register_spc(const char *name, int cluster_id)
68 {
69         struct clk_init_data init;
70         struct clk_spc *spc;
71         struct clk *clk;
72
73         if (!name) {
74                 pr_err("Invalid name passed");
75                 return ERR_PTR(-EINVAL);
76         }
77
78         spc = kzalloc(sizeof(*spc), GFP_KERNEL);
79         if (!spc) {
80                 pr_err("could not allocate spc clk\n");
81                 return ERR_PTR(-ENOMEM);
82         }
83
84         spc->hw.init = &init;
85         spc->cluster = cluster_id;
86
87         init.name = name;
88         init.ops = &clk_spc_ops;
89         init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
90         init.num_parents = 0;
91
92         clk = clk_register(NULL, &spc->hw);
93         if (!IS_ERR_OR_NULL(clk))
94                 return clk;
95
96         pr_err("clk register failed\n");
97         kfree(spc);
98
99         return NULL;
100 }
101
102 #if defined(CONFIG_OF)
103 void __init vexpress_clk_of_register_spc(void)
104 {
105         char name[14] = "cpu-cluster.X";
106         struct device_node *node = NULL;
107         struct clk *clk;
108         const u32 *val;
109         int cluster_id = 0, len;
110
111         if (!of_find_compatible_node(NULL, NULL, "arm,vexpress-spc")) {
112                 pr_debug("%s: No SPC found, Exiting!!\n", __func__);
113                 return;
114         }
115
116         while ((node = of_find_node_by_name(node, "cluster"))) {
117                 val = of_get_property(node, "reg", &len);
118                 if (val && len == 4)
119                         cluster_id = be32_to_cpup(val);
120
121                 name[12] = cluster_id + '0';
122                 clk = vexpress_clk_register_spc(name, cluster_id);
123                 if (IS_ERR(clk))
124                         return;
125
126                 pr_debug("Registered clock '%s'\n", name);
127                 clk_register_clkdev(clk, NULL, name);
128         }
129 }
130 CLK_OF_DECLARE(spc, "arm,vexpress-spc", vexpress_clk_of_register_spc);
131 #endif