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 #ifdef CONFIG_PM_SLEEP 272 static int samsung_clk_suspend(void) 273 { 274 struct samsung_clock_reg_cache *reg_cache; 275 276 list_for_each_entry(reg_cache, &clock_reg_cache_list, node) { 277 samsung_clk_save(reg_cache->reg_base, reg_cache->rdump, 278 reg_cache->rd_num); 279 samsung_clk_restore(reg_cache->reg_base, reg_cache->rsuspend, 280 reg_cache->rsuspend_num); 281 } 282 return 0; 283 } 284 285 static void samsung_clk_resume(void) 286 { 287 struct samsung_clock_reg_cache *reg_cache; 288 289 list_for_each_entry(reg_cache, &clock_reg_cache_list, node) 290 samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump, 291 reg_cache->rd_num); 292 } 293 294 static struct syscore_ops samsung_clk_syscore_ops = { 295 .suspend = samsung_clk_suspend, 296 .resume = samsung_clk_resume, 297 }; 298 299 void samsung_clk_extended_sleep_init(void __iomem *reg_base, 300 const unsigned long *rdump, 301 unsigned long nr_rdump, 302 const struct samsung_clk_reg_dump *rsuspend, 303 unsigned long nr_rsuspend) 304 { 305 struct samsung_clock_reg_cache *reg_cache; 306 307 reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache), 308 GFP_KERNEL); 309 if (!reg_cache) 310 panic("could not allocate register reg_cache.\n"); 311 reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump); 312 313 if (!reg_cache->rdump) 314 panic("could not allocate register dump storage.\n"); 315 316 if (list_empty(&clock_reg_cache_list)) 317 register_syscore_ops(&samsung_clk_syscore_ops); 318 319 reg_cache->reg_base = reg_base; 320 reg_cache->rd_num = nr_rdump; 321 reg_cache->rsuspend = rsuspend; 322 reg_cache->rsuspend_num = nr_rsuspend; 323 list_add_tail(®_cache->node, &clock_reg_cache_list); 324 } 325 #endif 326 327 /* 328 * Common function which registers plls, muxes, dividers and gates 329 * for each CMU. It also add CMU register list to register cache. 330 */ 331 struct samsung_clk_provider * __init samsung_cmu_register_one( 332 struct device_node *np, 333 const struct samsung_cmu_info *cmu) 334 { 335 void __iomem *reg_base; 336 struct samsung_clk_provider *ctx; 337 338 reg_base = of_iomap(np, 0); 339 if (!reg_base) { 340 panic("%s: failed to map registers\n", __func__); 341 return NULL; 342 } 343 344 ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids); 345 346 if (cmu->pll_clks) 347 samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks, 348 reg_base); 349 if (cmu->mux_clks) 350 samsung_clk_register_mux(ctx, cmu->mux_clks, 351 cmu->nr_mux_clks); 352 if (cmu->div_clks) 353 samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks); 354 if (cmu->gate_clks) 355 samsung_clk_register_gate(ctx, cmu->gate_clks, 356 cmu->nr_gate_clks); 357 if (cmu->fixed_clks) 358 samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks, 359 cmu->nr_fixed_clks); 360 if (cmu->fixed_factor_clks) 361 samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks, 362 cmu->nr_fixed_factor_clks); 363 if (cmu->clk_regs) 364 samsung_clk_extended_sleep_init(reg_base, 365 cmu->clk_regs, cmu->nr_clk_regs, 366 cmu->suspend_regs, cmu->nr_suspend_regs); 367 if (cmu->cpu_clks) 368 samsung_clk_register_cpu(ctx, cmu->cpu_clks, cmu->nr_cpu_clks); 369 370 samsung_clk_of_add_provider(np, ctx); 371 372 return ctx; 373 } 374