diff options
Diffstat (limited to 'arch/arm/mach-msm/clock-pcom.c')
-rw-r--r-- | arch/arm/mach-msm/clock-pcom.c | 149 |
1 files changed, 94 insertions, 55 deletions
diff --git a/arch/arm/mach-msm/clock-pcom.c b/arch/arm/mach-msm/clock-pcom.c index a52c970df157..9a80449518e6 100644 --- a/arch/arm/mach-msm/clock-pcom.c +++ b/arch/arm/mach-msm/clock-pcom.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2007 Google, Inc. | 2 | * Copyright (C) 2007 Google, Inc. |
3 | * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved. | 3 | * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved. |
4 | * | 4 | * |
5 | * This software is licensed under the terms of the GNU General Public | 5 | * This software is licensed under the terms of the GNU General Public |
6 | * License version 2, as published by the Free Software Foundation, and | 6 | * License version 2, as published by the Free Software Foundation, and |
@@ -13,20 +13,33 @@ | |||
13 | * | 13 | * |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/kernel.h> | ||
16 | #include <linux/err.h> | 17 | #include <linux/err.h> |
17 | #include <linux/ctype.h> | 18 | #include <linux/platform_device.h> |
18 | #include <linux/stddef.h> | 19 | #include <linux/module.h> |
20 | #include <linux/clk-provider.h> | ||
21 | #include <linux/clkdev.h> | ||
22 | |||
19 | #include <mach/clk.h> | 23 | #include <mach/clk.h> |
20 | 24 | ||
21 | #include "proc_comm.h" | 25 | #include "proc_comm.h" |
22 | #include "clock.h" | 26 | #include "clock.h" |
23 | #include "clock-pcom.h" | 27 | #include "clock-pcom.h" |
24 | 28 | ||
25 | /* | 29 | struct clk_pcom { |
26 | * glue for the proc_comm interface | 30 | unsigned id; |
27 | */ | 31 | unsigned long flags; |
28 | static int pc_clk_enable(unsigned id) | 32 | struct msm_clk msm_clk; |
33 | }; | ||
34 | |||
35 | static inline struct clk_pcom *to_clk_pcom(struct clk_hw *hw) | ||
29 | { | 36 | { |
37 | return container_of(to_msm_clk(hw), struct clk_pcom, msm_clk); | ||
38 | } | ||
39 | |||
40 | static int pc_clk_enable(struct clk_hw *hw) | ||
41 | { | ||
42 | unsigned id = to_clk_pcom(hw)->id; | ||
30 | int rc = msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); | 43 | int rc = msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); |
31 | if (rc < 0) | 44 | if (rc < 0) |
32 | return rc; | 45 | return rc; |
@@ -34,14 +47,16 @@ static int pc_clk_enable(unsigned id) | |||
34 | return (int)id < 0 ? -EINVAL : 0; | 47 | return (int)id < 0 ? -EINVAL : 0; |
35 | } | 48 | } |
36 | 49 | ||
37 | static void pc_clk_disable(unsigned id) | 50 | static void pc_clk_disable(struct clk_hw *hw) |
38 | { | 51 | { |
52 | unsigned id = to_clk_pcom(hw)->id; | ||
39 | msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); | 53 | msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); |
40 | } | 54 | } |
41 | 55 | ||
42 | int pc_clk_reset(unsigned id, enum clk_reset_action action) | 56 | static int pc_clk_reset(struct clk_hw *hw, enum clk_reset_action action) |
43 | { | 57 | { |
44 | int rc; | 58 | int rc; |
59 | unsigned id = to_clk_pcom(hw)->id; | ||
45 | 60 | ||
46 | if (action == CLK_RESET_ASSERT) | 61 | if (action == CLK_RESET_ASSERT) |
47 | rc = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_ASSERT, &id, NULL); | 62 | rc = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_ASSERT, &id, NULL); |
@@ -54,85 +69,109 @@ int pc_clk_reset(unsigned id, enum clk_reset_action action) | |||
54 | return (int)id < 0 ? -EINVAL : 0; | 69 | return (int)id < 0 ? -EINVAL : 0; |
55 | } | 70 | } |
56 | 71 | ||
57 | static int pc_clk_set_rate(unsigned id, unsigned rate) | 72 | static int pc_clk_set_rate(struct clk_hw *hw, unsigned long new_rate, |
73 | unsigned long p_rate) | ||
58 | { | 74 | { |
59 | /* The rate _might_ be rounded off to the nearest KHz value by the | 75 | struct clk_pcom *p = to_clk_pcom(hw); |
76 | unsigned id = p->id, rate = new_rate; | ||
77 | int rc; | ||
78 | |||
79 | /* | ||
80 | * The rate _might_ be rounded off to the nearest KHz value by the | ||
60 | * remote function. So a return value of 0 doesn't necessarily mean | 81 | * remote function. So a return value of 0 doesn't necessarily mean |
61 | * that the exact rate was set successfully. | 82 | * that the exact rate was set successfully. |
62 | */ | 83 | */ |
63 | int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate); | 84 | if (p->flags & CLKFLAG_MIN) |
64 | if (rc < 0) | 85 | rc = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate); |
65 | return rc; | ||
66 | else | ||
67 | return (int)id < 0 ? -EINVAL : 0; | ||
68 | } | ||
69 | |||
70 | static int pc_clk_set_min_rate(unsigned id, unsigned rate) | ||
71 | { | ||
72 | int rc = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate); | ||
73 | if (rc < 0) | ||
74 | return rc; | ||
75 | else | ||
76 | return (int)id < 0 ? -EINVAL : 0; | ||
77 | } | ||
78 | |||
79 | static int pc_clk_set_max_rate(unsigned id, unsigned rate) | ||
80 | { | ||
81 | int rc = msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &rate); | ||
82 | if (rc < 0) | ||
83 | return rc; | ||
84 | else | 86 | else |
85 | return (int)id < 0 ? -EINVAL : 0; | 87 | rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate); |
86 | } | ||
87 | |||
88 | static int pc_clk_set_flags(unsigned id, unsigned flags) | ||
89 | { | ||
90 | int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags); | ||
91 | if (rc < 0) | 88 | if (rc < 0) |
92 | return rc; | 89 | return rc; |
93 | else | 90 | else |
94 | return (int)id < 0 ? -EINVAL : 0; | 91 | return (int)id < 0 ? -EINVAL : 0; |
95 | } | 92 | } |
96 | 93 | ||
97 | static unsigned pc_clk_get_rate(unsigned id) | 94 | static unsigned long pc_clk_recalc_rate(struct clk_hw *hw, unsigned long p_rate) |
98 | { | 95 | { |
96 | unsigned id = to_clk_pcom(hw)->id; | ||
99 | if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL)) | 97 | if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL)) |
100 | return 0; | 98 | return 0; |
101 | else | 99 | else |
102 | return id; | 100 | return id; |
103 | } | 101 | } |
104 | 102 | ||
105 | static unsigned pc_clk_is_enabled(unsigned id) | 103 | static int pc_clk_is_enabled(struct clk_hw *hw) |
106 | { | 104 | { |
105 | unsigned id = to_clk_pcom(hw)->id; | ||
107 | if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL)) | 106 | if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL)) |
108 | return 0; | 107 | return 0; |
109 | else | 108 | else |
110 | return id; | 109 | return id; |
111 | } | 110 | } |
112 | 111 | ||
113 | static long pc_clk_round_rate(unsigned id, unsigned rate) | 112 | static long pc_clk_round_rate(struct clk_hw *hw, unsigned long rate, |
113 | unsigned long *p_rate) | ||
114 | { | 114 | { |
115 | |||
116 | /* Not really supported; pc_clk_set_rate() does rounding on it's own. */ | 115 | /* Not really supported; pc_clk_set_rate() does rounding on it's own. */ |
117 | return rate; | 116 | return rate; |
118 | } | 117 | } |
119 | 118 | ||
120 | static bool pc_clk_is_local(unsigned id) | 119 | static struct clk_ops clk_ops_pcom = { |
121 | { | ||
122 | return false; | ||
123 | } | ||
124 | |||
125 | struct clk_ops clk_ops_pcom = { | ||
126 | .enable = pc_clk_enable, | 120 | .enable = pc_clk_enable, |
127 | .disable = pc_clk_disable, | 121 | .disable = pc_clk_disable, |
128 | .auto_off = pc_clk_disable, | ||
129 | .reset = pc_clk_reset, | ||
130 | .set_rate = pc_clk_set_rate, | 122 | .set_rate = pc_clk_set_rate, |
131 | .set_min_rate = pc_clk_set_min_rate, | 123 | .recalc_rate = pc_clk_recalc_rate, |
132 | .set_max_rate = pc_clk_set_max_rate, | ||
133 | .set_flags = pc_clk_set_flags, | ||
134 | .get_rate = pc_clk_get_rate, | ||
135 | .is_enabled = pc_clk_is_enabled, | 124 | .is_enabled = pc_clk_is_enabled, |
136 | .round_rate = pc_clk_round_rate, | 125 | .round_rate = pc_clk_round_rate, |
137 | .is_local = pc_clk_is_local, | ||
138 | }; | 126 | }; |
127 | |||
128 | static int msm_clock_pcom_probe(struct platform_device *pdev) | ||
129 | { | ||
130 | const struct pcom_clk_pdata *pdata = pdev->dev.platform_data; | ||
131 | int i, ret; | ||
132 | |||
133 | for (i = 0; i < pdata->num_lookups; i++) { | ||
134 | const struct clk_pcom_desc *desc = &pdata->lookup[i]; | ||
135 | struct clk *c; | ||
136 | struct clk_pcom *p; | ||
137 | struct clk_hw *hw; | ||
138 | struct clk_init_data init; | ||
139 | |||
140 | p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); | ||
141 | if (!p) | ||
142 | return -ENOMEM; | ||
143 | |||
144 | p->id = desc->id; | ||
145 | p->flags = desc->flags; | ||
146 | p->msm_clk.reset = pc_clk_reset; | ||
147 | |||
148 | hw = &p->msm_clk.hw; | ||
149 | hw->init = &init; | ||
150 | |||
151 | init.name = desc->name; | ||
152 | init.ops = &clk_ops_pcom; | ||
153 | init.num_parents = 0; | ||
154 | init.flags = CLK_IS_ROOT; | ||
155 | |||
156 | if (!(p->flags & CLKFLAG_AUTO_OFF)) | ||
157 | init.flags |= CLK_IGNORE_UNUSED; | ||
158 | |||
159 | c = devm_clk_register(&pdev->dev, hw); | ||
160 | ret = clk_register_clkdev(c, desc->con, desc->dev); | ||
161 | if (ret) | ||
162 | return ret; | ||
163 | } | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static struct platform_driver msm_clock_pcom_driver = { | ||
169 | .probe = msm_clock_pcom_probe, | ||
170 | .driver = { | ||
171 | .name = "msm-clock-pcom", | ||
172 | .owner = THIS_MODULE, | ||
173 | }, | ||
174 | }; | ||
175 | module_platform_driver(msm_clock_pcom_driver); | ||
176 | |||
177 | MODULE_LICENSE("GPL v2"); | ||