Blame view
drivers/clk/ux500/clk-prcmu.c
8.22 KB
3b01f87be clk: ux500: Adapt... |
1 2 3 4 5 6 7 8 9 10 |
/* * PRCMU clock implementation for ux500 platform. * * Copyright (C) 2012 ST-Ericsson SA * Author: Ulf Hansson <ulf.hansson@linaro.org> * * License terms: GNU General Public License (GPL) version 2 */ #include <linux/clk-provider.h> |
3b01f87be clk: ux500: Adapt... |
11 12 13 14 15 16 17 18 19 20 21 |
#include <linux/mfd/dbx500-prcmu.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/err.h> #include "clk.h" #define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw) struct clk_prcmu { struct clk_hw hw; u8 cg_sel; |
2850985f7 clk: ux500: Suppo... |
22 |
int is_prepared; |
3b01f87be clk: ux500: Adapt... |
23 |
int is_enabled; |
2850985f7 clk: ux500: Suppo... |
24 |
int opp_requested; |
3b01f87be clk: ux500: Adapt... |
25 26 27 28 29 30 |
}; /* PRCMU clock operations. */ static int clk_prcmu_prepare(struct clk_hw *hw) { |
2850985f7 clk: ux500: Suppo... |
31 |
int ret; |
3b01f87be clk: ux500: Adapt... |
32 |
struct clk_prcmu *clk = to_clk_prcmu(hw); |
2850985f7 clk: ux500: Suppo... |
33 34 35 36 |
ret = prcmu_request_clock(clk->cg_sel, true); if (!ret) clk->is_prepared = 1; |
24c039f6a clk: ux500: Remov... |
37 |
return ret; |
3b01f87be clk: ux500: Adapt... |
38 39 40 41 42 43 44 45 |
} static void clk_prcmu_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); if (prcmu_request_clock(clk->cg_sel, false)) pr_err("clk_prcmu: %s failed to disable %s. ", __func__, |
836ee0f7d clk: Convert __cl... |
46 |
clk_hw_get_name(hw)); |
2850985f7 clk: ux500: Suppo... |
47 48 49 50 51 52 53 54 |
else clk->is_prepared = 0; } static int clk_prcmu_is_prepared(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); return clk->is_prepared; |
3b01f87be clk: ux500: Adapt... |
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
} static int clk_prcmu_enable(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); clk->is_enabled = 1; return 0; } static void clk_prcmu_disable(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); clk->is_enabled = 0; } static int clk_prcmu_is_enabled(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); return clk->is_enabled; } static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_prcmu *clk = to_clk_prcmu(hw); return prcmu_clock_rate(clk->cg_sel); } static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct clk_prcmu *clk = to_clk_prcmu(hw); return prcmu_round_clock_rate(clk->cg_sel, rate); } static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_prcmu *clk = to_clk_prcmu(hw); return prcmu_set_clock_rate(clk->cg_sel, rate); } |
3b01f87be clk: ux500: Adapt... |
96 97 98 99 |
static int clk_prcmu_opp_prepare(struct clk_hw *hw) { int err; struct clk_prcmu *clk = to_clk_prcmu(hw); |
2850985f7 clk: ux500: Suppo... |
100 101 |
if (!clk->opp_requested) { err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, |
836ee0f7d clk: Convert __cl... |
102 |
(char *)clk_hw_get_name(hw), |
2850985f7 clk: ux500: Suppo... |
103 104 105 106 |
100); if (err) { pr_err("clk_prcmu: %s fail req APE OPP for %s. ", |
836ee0f7d clk: Convert __cl... |
107 |
__func__, clk_hw_get_name(hw)); |
2850985f7 clk: ux500: Suppo... |
108 109 110 |
return err; } clk->opp_requested = 1; |
3b01f87be clk: ux500: Adapt... |
111 112 113 |
} err = prcmu_request_clock(clk->cg_sel, true); |
2850985f7 clk: ux500: Suppo... |
114 115 |
if (err) { prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, |
836ee0f7d clk: Convert __cl... |
116 |
(char *)clk_hw_get_name(hw)); |
2850985f7 clk: ux500: Suppo... |
117 118 119 |
clk->opp_requested = 0; return err; } |
3b01f87be clk: ux500: Adapt... |
120 |
|
2850985f7 clk: ux500: Suppo... |
121 122 |
clk->is_prepared = 1; return 0; |
3b01f87be clk: ux500: Adapt... |
123 124 125 126 127 |
} static void clk_prcmu_opp_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); |
2850985f7 clk: ux500: Suppo... |
128 129 130 |
if (prcmu_request_clock(clk->cg_sel, false)) { pr_err("clk_prcmu: %s failed to disable %s. ", __func__, |
836ee0f7d clk: Convert __cl... |
131 |
clk_hw_get_name(hw)); |
2850985f7 clk: ux500: Suppo... |
132 133 134 135 136 |
return; } if (clk->opp_requested) { prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, |
836ee0f7d clk: Convert __cl... |
137 |
(char *)clk_hw_get_name(hw)); |
2850985f7 clk: ux500: Suppo... |
138 139 140 141 |
clk->opp_requested = 0; } clk->is_prepared = 0; |
3b01f87be clk: ux500: Adapt... |
142 |
} |
b0ea0fc75 clk: ux500: Suppo... |
143 144 145 146 |
static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) { int err; struct clk_prcmu *clk = to_clk_prcmu(hw); |
2850985f7 clk: ux500: Suppo... |
147 148 149 150 151 |
if (!clk->opp_requested) { err = prcmu_request_ape_opp_100_voltage(true); if (err) { pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s. ", |
836ee0f7d clk: Convert __cl... |
152 |
__func__, clk_hw_get_name(hw)); |
2850985f7 clk: ux500: Suppo... |
153 154 155 |
return err; } clk->opp_requested = 1; |
b0ea0fc75 clk: ux500: Suppo... |
156 157 158 |
} err = prcmu_request_clock(clk->cg_sel, true); |
2850985f7 clk: ux500: Suppo... |
159 |
if (err) { |
b0ea0fc75 clk: ux500: Suppo... |
160 |
prcmu_request_ape_opp_100_voltage(false); |
2850985f7 clk: ux500: Suppo... |
161 162 163 |
clk->opp_requested = 0; return err; } |
b0ea0fc75 clk: ux500: Suppo... |
164 |
|
2850985f7 clk: ux500: Suppo... |
165 166 |
clk->is_prepared = 1; return 0; |
b0ea0fc75 clk: ux500: Suppo... |
167 168 169 170 171 |
} static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); |
2850985f7 clk: ux500: Suppo... |
172 173 174 |
if (prcmu_request_clock(clk->cg_sel, false)) { pr_err("clk_prcmu: %s failed to disable %s. ", __func__, |
836ee0f7d clk: Convert __cl... |
175 |
clk_hw_get_name(hw)); |
2850985f7 clk: ux500: Suppo... |
176 177 178 179 180 181 182 183 184 |
return; } if (clk->opp_requested) { prcmu_request_ape_opp_100_voltage(false); clk->opp_requested = 0; } clk->is_prepared = 0; |
b0ea0fc75 clk: ux500: Suppo... |
185 |
} |
3b01f87be clk: ux500: Adapt... |
186 187 188 |
static struct clk_ops clk_prcmu_scalable_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, |
2850985f7 clk: ux500: Suppo... |
189 |
.is_prepared = clk_prcmu_is_prepared, |
3b01f87be clk: ux500: Adapt... |
190 191 192 193 194 195 196 197 198 199 200 |
.enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, .round_rate = clk_prcmu_round_rate, .set_rate = clk_prcmu_set_rate, }; static struct clk_ops clk_prcmu_gate_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, |
2850985f7 clk: ux500: Suppo... |
201 |
.is_prepared = clk_prcmu_is_prepared, |
3b01f87be clk: ux500: Adapt... |
202 203 204 205 206 |
.enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, }; |
a816d250e clk: ux500: Suppo... |
207 208 209 210 211 212 |
static struct clk_ops clk_prcmu_scalable_rate_ops = { .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, .round_rate = clk_prcmu_round_rate, .set_rate = clk_prcmu_set_rate, }; |
70b1fce2e clk: ux500: Suppo... |
213 214 215 216 |
static struct clk_ops clk_prcmu_rate_ops = { .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, }; |
3b01f87be clk: ux500: Adapt... |
217 218 219 |
static struct clk_ops clk_prcmu_opp_gate_ops = { .prepare = clk_prcmu_opp_prepare, .unprepare = clk_prcmu_opp_unprepare, |
2850985f7 clk: ux500: Suppo... |
220 |
.is_prepared = clk_prcmu_is_prepared, |
3b01f87be clk: ux500: Adapt... |
221 222 223 224 225 |
.enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, }; |
b0ea0fc75 clk: ux500: Suppo... |
226 227 228 |
static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { .prepare = clk_prcmu_opp_volt_prepare, .unprepare = clk_prcmu_opp_volt_unprepare, |
2850985f7 clk: ux500: Suppo... |
229 |
.is_prepared = clk_prcmu_is_prepared, |
b0ea0fc75 clk: ux500: Suppo... |
230 231 232 233 234 235 236 |
.enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, .round_rate = clk_prcmu_round_rate, .set_rate = clk_prcmu_set_rate, }; |
3b01f87be clk: ux500: Adapt... |
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
static struct clk *clk_reg_prcmu(const char *name, const char *parent_name, u8 cg_sel, unsigned long rate, unsigned long flags, struct clk_ops *clk_prcmu_ops) { struct clk_prcmu *clk; struct clk_init_data clk_prcmu_init; struct clk *clk_reg; if (!name) { pr_err("clk_prcmu: %s invalid arguments passed ", __func__); return ERR_PTR(-EINVAL); } clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL); if (!clk) { pr_err("clk_prcmu: %s could not allocate clk ", __func__); return ERR_PTR(-ENOMEM); } clk->cg_sel = cg_sel; |
2850985f7 clk: ux500: Suppo... |
262 |
clk->is_prepared = 1; |
3b01f87be clk: ux500: Adapt... |
263 |
clk->is_enabled = 1; |
2850985f7 clk: ux500: Suppo... |
264 |
clk->opp_requested = 0; |
3b01f87be clk: ux500: Adapt... |
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
/* "rate" can be used for changing the initial frequency */ if (rate) prcmu_set_clock_rate(cg_sel, rate); clk_prcmu_init.name = name; clk_prcmu_init.ops = clk_prcmu_ops; clk_prcmu_init.flags = flags; clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL); clk_prcmu_init.num_parents = (parent_name ? 1 : 0); clk->hw.init = &clk_prcmu_init; clk_reg = clk_register(NULL, &clk->hw); if (IS_ERR_OR_NULL(clk_reg)) goto free_clk; return clk_reg; free_clk: kfree(clk); pr_err("clk_prcmu: %s failed to register clk ", __func__); return ERR_PTR(-ENOMEM); } struct clk *clk_reg_prcmu_scalable(const char *name, const char *parent_name, u8 cg_sel, unsigned long rate, unsigned long flags) { return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, &clk_prcmu_scalable_ops); } struct clk *clk_reg_prcmu_gate(const char *name, const char *parent_name, u8 cg_sel, unsigned long flags) { return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, &clk_prcmu_gate_ops); } |
a816d250e clk: ux500: Suppo... |
307 308 309 310 311 312 313 314 315 |
struct clk *clk_reg_prcmu_scalable_rate(const char *name, const char *parent_name, u8 cg_sel, unsigned long rate, unsigned long flags) { return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, &clk_prcmu_scalable_rate_ops); } |
70b1fce2e clk: ux500: Suppo... |
316 317 318 319 320 321 322 323 |
struct clk *clk_reg_prcmu_rate(const char *name, const char *parent_name, u8 cg_sel, unsigned long flags) { return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, &clk_prcmu_rate_ops); } |
3b01f87be clk: ux500: Adapt... |
324 325 326 327 328 329 330 331 |
struct clk *clk_reg_prcmu_opp_gate(const char *name, const char *parent_name, u8 cg_sel, unsigned long flags) { return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, &clk_prcmu_opp_gate_ops); } |
b0ea0fc75 clk: ux500: Suppo... |
332 333 334 335 336 337 338 339 340 341 |
struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name, const char *parent_name, u8 cg_sel, unsigned long rate, unsigned long flags) { return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, &clk_prcmu_opp_volt_scalable_ops); } |