clk-prcmu.c (9920B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * PRCMU clock implementation for ux500 platform. 4 * 5 * Copyright (C) 2012 ST-Ericsson SA 6 * Author: Ulf Hansson <ulf.hansson@linaro.org> 7 */ 8 9#include <linux/clk-provider.h> 10#include <linux/mfd/dbx500-prcmu.h> 11#include <linux/slab.h> 12#include <linux/io.h> 13#include <linux/err.h> 14#include "clk.h" 15 16#define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw) 17#define to_clk_prcmu_clkout(_hw) container_of(_hw, struct clk_prcmu_clkout, hw) 18 19struct clk_prcmu { 20 struct clk_hw hw; 21 u8 cg_sel; 22 int opp_requested; 23}; 24 25struct clk_prcmu_clkout { 26 struct clk_hw hw; 27 u8 clkout_id; 28 u8 source; 29 u8 divider; 30}; 31 32/* PRCMU clock operations. */ 33 34static int clk_prcmu_prepare(struct clk_hw *hw) 35{ 36 struct clk_prcmu *clk = to_clk_prcmu(hw); 37 38 return prcmu_request_clock(clk->cg_sel, true); 39} 40 41static void clk_prcmu_unprepare(struct clk_hw *hw) 42{ 43 struct clk_prcmu *clk = to_clk_prcmu(hw); 44 if (prcmu_request_clock(clk->cg_sel, false)) 45 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, 46 clk_hw_get_name(hw)); 47} 48 49static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw, 50 unsigned long parent_rate) 51{ 52 struct clk_prcmu *clk = to_clk_prcmu(hw); 53 return prcmu_clock_rate(clk->cg_sel); 54} 55 56static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate, 57 unsigned long *parent_rate) 58{ 59 struct clk_prcmu *clk = to_clk_prcmu(hw); 60 return prcmu_round_clock_rate(clk->cg_sel, rate); 61} 62 63static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, 64 unsigned long parent_rate) 65{ 66 struct clk_prcmu *clk = to_clk_prcmu(hw); 67 return prcmu_set_clock_rate(clk->cg_sel, rate); 68} 69 70static int clk_prcmu_opp_prepare(struct clk_hw *hw) 71{ 72 int err; 73 struct clk_prcmu *clk = to_clk_prcmu(hw); 74 75 if (!clk->opp_requested) { 76 err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, 77 (char *)clk_hw_get_name(hw), 78 100); 79 if (err) { 80 pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", 81 __func__, clk_hw_get_name(hw)); 82 return err; 83 } 84 clk->opp_requested = 1; 85 } 86 87 err = prcmu_request_clock(clk->cg_sel, true); 88 if (err) { 89 prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, 90 (char *)clk_hw_get_name(hw)); 91 clk->opp_requested = 0; 92 return err; 93 } 94 95 return 0; 96} 97 98static void clk_prcmu_opp_unprepare(struct clk_hw *hw) 99{ 100 struct clk_prcmu *clk = to_clk_prcmu(hw); 101 102 if (prcmu_request_clock(clk->cg_sel, false)) { 103 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, 104 clk_hw_get_name(hw)); 105 return; 106 } 107 108 if (clk->opp_requested) { 109 prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, 110 (char *)clk_hw_get_name(hw)); 111 clk->opp_requested = 0; 112 } 113} 114 115static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) 116{ 117 int err; 118 struct clk_prcmu *clk = to_clk_prcmu(hw); 119 120 if (!clk->opp_requested) { 121 err = prcmu_request_ape_opp_100_voltage(true); 122 if (err) { 123 pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", 124 __func__, clk_hw_get_name(hw)); 125 return err; 126 } 127 clk->opp_requested = 1; 128 } 129 130 err = prcmu_request_clock(clk->cg_sel, true); 131 if (err) { 132 prcmu_request_ape_opp_100_voltage(false); 133 clk->opp_requested = 0; 134 return err; 135 } 136 137 return 0; 138} 139 140static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) 141{ 142 struct clk_prcmu *clk = to_clk_prcmu(hw); 143 144 if (prcmu_request_clock(clk->cg_sel, false)) { 145 pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, 146 clk_hw_get_name(hw)); 147 return; 148 } 149 150 if (clk->opp_requested) { 151 prcmu_request_ape_opp_100_voltage(false); 152 clk->opp_requested = 0; 153 } 154} 155 156static const struct clk_ops clk_prcmu_scalable_ops = { 157 .prepare = clk_prcmu_prepare, 158 .unprepare = clk_prcmu_unprepare, 159 .recalc_rate = clk_prcmu_recalc_rate, 160 .round_rate = clk_prcmu_round_rate, 161 .set_rate = clk_prcmu_set_rate, 162}; 163 164static const struct clk_ops clk_prcmu_gate_ops = { 165 .prepare = clk_prcmu_prepare, 166 .unprepare = clk_prcmu_unprepare, 167 .recalc_rate = clk_prcmu_recalc_rate, 168}; 169 170static const struct clk_ops clk_prcmu_scalable_rate_ops = { 171 .recalc_rate = clk_prcmu_recalc_rate, 172 .round_rate = clk_prcmu_round_rate, 173 .set_rate = clk_prcmu_set_rate, 174}; 175 176static const struct clk_ops clk_prcmu_rate_ops = { 177 .recalc_rate = clk_prcmu_recalc_rate, 178}; 179 180static const struct clk_ops clk_prcmu_opp_gate_ops = { 181 .prepare = clk_prcmu_opp_prepare, 182 .unprepare = clk_prcmu_opp_unprepare, 183 .recalc_rate = clk_prcmu_recalc_rate, 184}; 185 186static const struct clk_ops clk_prcmu_opp_volt_scalable_ops = { 187 .prepare = clk_prcmu_opp_volt_prepare, 188 .unprepare = clk_prcmu_opp_volt_unprepare, 189 .recalc_rate = clk_prcmu_recalc_rate, 190 .round_rate = clk_prcmu_round_rate, 191 .set_rate = clk_prcmu_set_rate, 192}; 193 194static struct clk_hw *clk_reg_prcmu(const char *name, 195 const char *parent_name, 196 u8 cg_sel, 197 unsigned long rate, 198 unsigned long flags, 199 const struct clk_ops *clk_prcmu_ops) 200{ 201 struct clk_prcmu *clk; 202 struct clk_init_data clk_prcmu_init; 203 int ret; 204 205 if (!name) { 206 pr_err("clk_prcmu: %s invalid arguments passed\n", __func__); 207 return ERR_PTR(-EINVAL); 208 } 209 210 clk = kzalloc(sizeof(*clk), GFP_KERNEL); 211 if (!clk) 212 return ERR_PTR(-ENOMEM); 213 214 clk->cg_sel = cg_sel; 215 clk->opp_requested = 0; 216 /* "rate" can be used for changing the initial frequency */ 217 if (rate) 218 prcmu_set_clock_rate(cg_sel, rate); 219 220 clk_prcmu_init.name = name; 221 clk_prcmu_init.ops = clk_prcmu_ops; 222 clk_prcmu_init.flags = flags; 223 clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL); 224 clk_prcmu_init.num_parents = (parent_name ? 1 : 0); 225 clk->hw.init = &clk_prcmu_init; 226 227 ret = clk_hw_register(NULL, &clk->hw); 228 if (ret) 229 goto free_clk; 230 231 return &clk->hw; 232 233free_clk: 234 kfree(clk); 235 pr_err("clk_prcmu: %s failed to register clk\n", __func__); 236 return ERR_PTR(-ENOMEM); 237} 238 239struct clk_hw *clk_reg_prcmu_scalable(const char *name, 240 const char *parent_name, 241 u8 cg_sel, 242 unsigned long rate, 243 unsigned long flags) 244{ 245 return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, 246 &clk_prcmu_scalable_ops); 247} 248 249struct clk_hw *clk_reg_prcmu_gate(const char *name, 250 const char *parent_name, 251 u8 cg_sel, 252 unsigned long flags) 253{ 254 return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, 255 &clk_prcmu_gate_ops); 256} 257 258struct clk_hw *clk_reg_prcmu_scalable_rate(const char *name, 259 const char *parent_name, 260 u8 cg_sel, 261 unsigned long rate, 262 unsigned long flags) 263{ 264 return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, 265 &clk_prcmu_scalable_rate_ops); 266} 267 268struct clk_hw *clk_reg_prcmu_rate(const char *name, 269 const char *parent_name, 270 u8 cg_sel, 271 unsigned long flags) 272{ 273 return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, 274 &clk_prcmu_rate_ops); 275} 276 277struct clk_hw *clk_reg_prcmu_opp_gate(const char *name, 278 const char *parent_name, 279 u8 cg_sel, 280 unsigned long flags) 281{ 282 return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, 283 &clk_prcmu_opp_gate_ops); 284} 285 286struct clk_hw *clk_reg_prcmu_opp_volt_scalable(const char *name, 287 const char *parent_name, 288 u8 cg_sel, 289 unsigned long rate, 290 unsigned long flags) 291{ 292 return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, 293 &clk_prcmu_opp_volt_scalable_ops); 294} 295 296/* The clkout (external) clock is special and need special ops */ 297 298static int clk_prcmu_clkout_prepare(struct clk_hw *hw) 299{ 300 struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); 301 302 return prcmu_config_clkout(clk->clkout_id, clk->source, clk->divider); 303} 304 305static void clk_prcmu_clkout_unprepare(struct clk_hw *hw) 306{ 307 struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); 308 int ret; 309 310 /* The clkout clock is disabled by dividing by 0 */ 311 ret = prcmu_config_clkout(clk->clkout_id, clk->source, 0); 312 if (ret) 313 pr_err("clk_prcmu: %s failed to disable %s\n", __func__, 314 clk_hw_get_name(hw)); 315} 316 317static unsigned long clk_prcmu_clkout_recalc_rate(struct clk_hw *hw, 318 unsigned long parent_rate) 319{ 320 struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); 321 322 return (parent_rate / clk->divider); 323} 324 325static u8 clk_prcmu_clkout_get_parent(struct clk_hw *hw) 326{ 327 struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); 328 329 return clk->source; 330} 331 332static int clk_prcmu_clkout_set_parent(struct clk_hw *hw, u8 index) 333{ 334 struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw); 335 336 clk->source = index; 337 /* Make sure the change reaches the hardware immediately */ 338 if (clk_hw_is_prepared(hw)) 339 return clk_prcmu_clkout_prepare(hw); 340 return 0; 341} 342 343static const struct clk_ops clk_prcmu_clkout_ops = { 344 .prepare = clk_prcmu_clkout_prepare, 345 .unprepare = clk_prcmu_clkout_unprepare, 346 .recalc_rate = clk_prcmu_clkout_recalc_rate, 347 .get_parent = clk_prcmu_clkout_get_parent, 348 .set_parent = clk_prcmu_clkout_set_parent, 349}; 350 351struct clk_hw *clk_reg_prcmu_clkout(const char *name, 352 const char * const *parent_names, 353 int num_parents, 354 u8 source, u8 divider) 355 356{ 357 struct clk_prcmu_clkout *clk; 358 struct clk_init_data clk_prcmu_clkout_init; 359 u8 clkout_id; 360 int ret; 361 362 if (!name) { 363 pr_err("clk_prcmu_clkout: %s invalid arguments passed\n", __func__); 364 return ERR_PTR(-EINVAL); 365 } 366 367 if (!strcmp(name, "clkout1")) 368 clkout_id = 0; 369 else if (!strcmp(name, "clkout2")) 370 clkout_id = 1; 371 else { 372 pr_err("clk_prcmu_clkout: %s bad clock name\n", __func__); 373 return ERR_PTR(-EINVAL); 374 } 375 376 clk = kzalloc(sizeof(*clk), GFP_KERNEL); 377 if (!clk) 378 return ERR_PTR(-ENOMEM); 379 380 clk->clkout_id = clkout_id; 381 clk->source = source; 382 clk->divider = divider; 383 384 clk_prcmu_clkout_init.name = name; 385 clk_prcmu_clkout_init.ops = &clk_prcmu_clkout_ops; 386 clk_prcmu_clkout_init.flags = CLK_GET_RATE_NOCACHE; 387 clk_prcmu_clkout_init.parent_names = parent_names; 388 clk_prcmu_clkout_init.num_parents = num_parents; 389 clk->hw.init = &clk_prcmu_clkout_init; 390 391 ret = clk_hw_register(NULL, &clk->hw); 392 if (ret) 393 goto free_clkout; 394 395 return &clk->hw; 396free_clkout: 397 kfree(clk); 398 pr_err("clk_prcmu_clkout: %s failed to register clk\n", __func__); 399 return ERR_PTR(-ENOMEM); 400}