root/drivers/clk/samsung/clk.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. samsung_clk_save
  2. samsung_clk_restore
  3. samsung_clk_alloc_reg_dump
  4. samsung_clk_init
  5. samsung_clk_of_add_provider
  6. samsung_clk_add_lookup
  7. samsung_clk_register_alias
  8. samsung_clk_register_fixed_rate
  9. samsung_clk_register_fixed_factor
  10. samsung_clk_register_mux
  11. samsung_clk_register_div
  12. samsung_clk_register_gate
  13. samsung_clk_of_register_fixed_ext
  14. _get_rate
  15. samsung_clk_suspend
  16. samsung_clk_resume
  17. samsung_clk_extended_sleep_init
  18. samsung_cmu_register_one

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

/* [<][>][^][v][top][bottom][index][help] */