1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2013 Samsung Electronics Co., Ltd. 4 * Copyright (c) 2013 Linaro Ltd. 5 * Author: Thomas Abraham <thomas.ab@samsung.com> 6 * 7 * This file includes utility functions to register clocks to common 8 * clock framework for Samsung platforms. 9 */ 10 11 #include <linux/slab.h> 12 #include <linux/clkdev.h> 13 #include <linux/clk.h> 14 #include <linux/clk-provider.h> 15 #include <linux/io.h> 16 #include <linux/of_address.h> 17 #include <linux/syscore_ops.h> 18 19 #include "clk.h" 20 21 static LIST_HEAD(clock_reg_cache_list); 22 23 void samsung_clk_save(void __iomem *base, 24 struct samsung_clk_reg_dump *rd, 25 unsigned int num_regs) 26 { 27 for (; num_regs > 0; --num_regs, ++rd) 28 rd->value = readl(base + rd->offset); 29 } 30 31 void samsung_clk_restore(void __iomem *base, 32 const struct samsung_clk_reg_dump *rd, 33 unsigned int num_regs) 34 { 35 for (; num_regs > 0; --num_regs, ++rd) 36 writel(rd->value, base + rd->offset); 37 } 38 39 struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump( 40 const unsigned long *rdump, 41 unsigned long nr_rdump) 42 { 43 struct samsung_clk_reg_dump *rd; 44 unsigned int i; 45 46 rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL); 47 if (!rd) 48 return NULL; 49 50 for (i = 0; i < nr_rdump; ++i) 51 rd[i].offset = rdump[i]; 52 53 return rd; 54 } 55 56 /* setup the essentials required to support clock lookup using ccf */ 57 struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np, 58 void __iomem *base, unsigned long nr_clks) 59 { 60 struct samsung_clk_provider *ctx; 61 int i; 62 63 ctx = kzalloc(struct_size(ctx, clk_data.hws, nr_clks), GFP_KERNEL); 64 if (!ctx) 65 panic("could not allocate clock provider context.\n"); 66 67 for (i = 0; i < nr_clks; ++i) 68 ctx->clk_data.hws[i] = ERR_PTR(-ENOENT); 69 70 ctx->reg_base = base; 71 ctx->clk_data.num = nr_clks; 72 spin_lock_init(&ctx->lock); 73 74 return ctx; 75 } 76 77 void __init samsung_clk_of_add_provider(struct device_node *np, 78 struct samsung_clk_provider *ctx) 79 { 80 if (np) { 81 if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, 82 &ctx->clk_data)) 83 panic("could not register clk provider\n"); 84 } 85 } 86 87 /* add a clock instance to the clock lookup table used for dt based lookup */ 88 void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, 89 struct clk_hw *clk_hw, unsigned int id) 90 { 91 if (id) 92 ctx->clk_data.hws[id] = clk_hw; 93 } 94 95 /* register a list of aliases */ 96 void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, 97 const struct samsung_clock_alias *list, 98 unsigned int nr_clk) 99 { 100 struct clk_hw *clk_hw; 101 unsigned int idx, ret; 102 103 for (idx = 0; idx < nr_clk; idx++, list++) { 104 if (!list->id) { 105 pr_err("%s: clock id missing for index %d\n", __func__, 106 idx); 107 continue; 108 } 109 110 clk_hw = ctx->clk_data.hws[list->id]; 111 if (!clk_hw) { 112 pr_err("%s: failed to find clock %d\n", __func__, 113 list->id); 114 continue; 115 } 116 117 ret = clk_hw_register_clkdev(clk_hw, list->alias, 118 list->dev_name); 119 if (ret) 120 pr_err("%s: failed to register lookup %s\n", 121 __func__, list->alias); 122 } 123 } 124 125 /* register a list of fixed clocks */ 126 void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, 127 const struct samsung_fixed_rate_clock *list, 128 unsigned int nr_clk) 129 { 130 struct clk_hw *clk_hw; 131 unsigned int idx, ret; 132 133 for (idx = 0; idx < nr_clk; idx++, list++) { 134 clk_hw = clk_hw_register_fixed_rate(ctx->dev, list->name, 135 list->parent_name, list->flags, list->fixed_rate); 136 if (IS_ERR(clk_hw)) { 137 pr_err("%s: failed to register clock %s\n", __func__, 138 list->name); 139 continue; 140 } 141 142 samsung_clk_add_lookup(ctx, clk_hw, list->id); 143 144 /* 145 * Unconditionally add a clock lookup for the fixed rate clocks. 146 * There are not many of these on any of Samsung platforms. 147 */ 148 ret = clk_hw_register_clkdev(clk_hw, list->name, NULL); 149 if (ret) 150 pr_err("%s: failed to register clock lookup for %s", 151 __func__, list->name); 152 } 153 } 154 155 /* register a list of fixed factor clocks */ 156 void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, 157 const struct samsung_fixed_factor_clock *list, unsigned int nr_clk) 158 { 159 struct clk_hw *clk_hw; 160 unsigned int idx; 161 162 for (idx = 0; idx < nr_clk; idx++, list++) { 163 clk_hw = clk_hw_register_fixed_factor(ctx->dev, list->name, 164 list->parent_name, list->flags, list->mult, list->div); 165 if (IS_ERR(clk_hw)) { 166 pr_err("%s: failed to register clock %s\n", __func__, 167 list->name); 168 continue; 169 } 170 171 samsung_clk_add_lookup(ctx, clk_hw, list->id); 172 } 173 } 174 175 /* register a list of mux clocks */ 176 void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, 177 const struct samsung_mux_clock *list, 178 unsigned int nr_clk) 179 { 180 struct clk_hw *clk_hw; 181 unsigned int idx; 182 183 for (idx = 0; idx < nr_clk; idx++, list++) { 184 clk_hw = clk_hw_register_mux(ctx->dev, list->name, 185 list->parent_names, list->num_parents, list->flags, 186 ctx->reg_base + list->offset, 187 list->shift, list->width, list->mux_flags, &ctx->lock); 188 if (IS_ERR(clk_hw)) { 189 pr_err("%s: failed to register clock %s\n", __func__, 190 list->name); 191 continue; 192 } 193 194 samsung_clk_add_lookup(ctx, clk_hw, list->id); 195 } 196 } 197 198 /* register a list of div clocks */ 199 void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, 200 const struct samsung_div_clock *list, 201 unsigned int nr_clk) 202 { 203 struct clk_hw *clk_hw; 204 unsigned int idx; 205 206 for (idx = 0; idx < nr_clk; idx++, list++) { 207 if (list->table) 208 clk_hw = clk_hw_register_divider_table(ctx->dev, 209 list->name, list->parent_name, list->flags, 210 ctx->reg_base + list->offset, 211 list->shift, list->width, list->div_flags, 212 list->table, &ctx->lock); 213 else 214 clk_hw = clk_hw_register_divider(ctx->dev, list->name, 215 list->parent_name, list->flags, 216 ctx->reg_base + list->offset, list->shift, 217 list->width, list->div_flags, &ctx->lock); 218 if (IS_ERR(clk_hw)) { 219 pr_err("%s: failed to register clock %s\n", __func__, 220 list->name); 221 continue; 222 } 223 224 samsung_clk_add_lookup(ctx, clk_hw, list->id); 225 } 226 } 227 228 /* register a list of gate clocks */ 229 void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, 230 const struct samsung_gate_clock *list, 231 unsigned int nr_clk) 232 { 233 struct clk_hw *clk_hw; 234 unsigned int idx; 235 236 for (idx = 0; idx < nr_clk; idx++, list++) { 237 clk_hw = clk_hw_register_gate(ctx->dev, list->name, list->parent_name, 238 list->flags, ctx->reg_base + list->offset, 239 list->bit_idx, list->gate_flags, &ctx->lock); 240 if (IS_ERR(clk_hw)) { 241 pr_err("%s: failed to register clock %s\n", __func__, 242 list->name); 243 continue; 244 } 245 246 samsung_clk_add_lookup(ctx, clk_hw, list->id); 247 } 248 } 249 250 /* 251 * obtain the clock speed of all external fixed clock sources from device 252 * tree and register it 253 */ 254 void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx, 255 struct samsung_fixed_rate_clock *fixed_rate_clk, 256 unsigned int nr_fixed_rate_clk, 257 const struct of_device_id *clk_matches) 258 { 259 const struct of_device_id *match; 260 struct device_node *clk_np; 261 u32 freq; 262 263 for_each_matching_node_and_match(clk_np, clk_matches, &match) { 264 if (of_property_read_u32(clk_np, "clock-frequency", &freq)) 265 continue; 266 fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq; 267 } 268 samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk); 269 } 270 271 /* utility function to get the rate of a specified clock */ 272 unsigned long _get_rate(const char *clk_name) 273 { 274 struct clk *clk; 275 276 clk = __clk_lookup(clk_name); 277 if (!clk) { 278 pr_err("%s: could not find clock %s\n", __func__, clk_name); 279 return 0; 280 } 281 282 return clk_get_rate(clk); 283 } 284 285 #ifdef CONFIG_PM_SLEEP 286 static int samsung_clk_suspend(void) 287 { 288 struct samsung_clock_reg_cache *reg_cache; 289 290 list_for_each_entry(reg_cache, &clock_reg_cache_list, node) { 291 samsung_clk_save(reg_cache->reg_base, reg_cache->rdump, 292 reg_cache->rd_num); 293 samsung_clk_restore(reg_cache->reg_base, reg_cache->rsuspend, 294 reg_cache->rsuspend_num); 295 } 296 return 0; 297 } 298 299 static void samsung_clk_resume(void) 300 { 301 struct samsung_clock_reg_cache *reg_cache; 302 303 list_for_each_entry(reg_cache, &clock_reg_cache_list, node) 304 samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump, 305 reg_cache->rd_num); 306 } 307 308 static struct syscore_ops samsung_clk_syscore_ops = { 309 .suspend = samsung_clk_suspend, 310 .resume = samsung_clk_resume, 311 }; 312 313 void samsung_clk_extended_sleep_init(void __iomem *reg_base, 314 const unsigned long *rdump, 315 unsigned long nr_rdump, 316 const struct samsung_clk_reg_dump *rsuspend, 317 unsigned long nr_rsuspend) 318 { 319 struct samsung_clock_reg_cache *reg_cache; 320 321 reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache), 322 GFP_KERNEL); 323 if (!reg_cache) 324 panic("could not allocate register reg_cache.\n"); 325 reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump); 326 327 if (!reg_cache->rdump) 328 panic("could not allocate register dump storage.\n"); 329 330 if (list_empty(&clock_reg_cache_list)) 331 register_syscore_ops(&samsung_clk_syscore_ops); 332 333 reg_cache->reg_base = reg_base; 334 reg_cache->rd_num = nr_rdump; 335 reg_cache->rsuspend = rsuspend; 336 reg_cache->rsuspend_num = nr_rsuspend; 337 list_add_tail(®_cache->node, &clock_reg_cache_list); 338 } 339 #endif 340 341 /* 342 * Common function which registers plls, muxes, dividers and gates 343 * for each CMU. It also add CMU register list to register cache. 344 */ 345 struct samsung_clk_provider * __init samsung_cmu_register_one( 346 struct device_node *np, 347 const struct samsung_cmu_info *cmu) 348 { 349 void __iomem *reg_base; 350 struct samsung_clk_provider *ctx; 351 352 reg_base = of_iomap(np, 0); 353 if (!reg_base) { 354 panic("%s: failed to map registers\n", __func__); 355 return NULL; 356 } 357 358 ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids); 359 if (!ctx) { 360 panic("%s: unable to allocate ctx\n", __func__); 361 return ctx; 362 } 363 364 if (cmu->pll_clks) 365 samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks, 366 reg_base); 367 if (cmu->mux_clks) 368 samsung_clk_register_mux(ctx, cmu->mux_clks, 369 cmu->nr_mux_clks); 370 if (cmu->div_clks) 371 samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks); 372 if (cmu->gate_clks) 373 samsung_clk_register_gate(ctx, cmu->gate_clks, 374 cmu->nr_gate_clks); 375 if (cmu->fixed_clks) 376 samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks, 377 cmu->nr_fixed_clks); 378 if (cmu->fixed_factor_clks) 379 samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks, 380 cmu->nr_fixed_factor_clks); 381 if (cmu->clk_regs) 382 samsung_clk_extended_sleep_init(reg_base, 383 cmu->clk_regs, cmu->nr_clk_regs, 384 cmu->suspend_regs, cmu->nr_suspend_regs); 385 386 samsung_clk_of_add_provider(np, ctx); 387 388 return ctx; 389 } 390