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