linux/drivers/clk/samsung/clk.c
<<
>>
Prefs
   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
  21static LIST_HEAD(clock_reg_cache_list);
  22
  23void 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
  31void 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
  39struct 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 */
  57struct 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
  78void __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 */
  89void 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 */
  97void __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 */
 127void __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 */
 157void __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 */
 177void __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 */
 200void __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 */
 230void __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 */
 255void __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 */
 273unsigned 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
 287static 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
 300static 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
 309static struct syscore_ops samsung_clk_syscore_ops = {
 310        .suspend = samsung_clk_suspend,
 311        .resume = samsung_clk_resume,
 312};
 313
 314void 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 */
 346struct 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}
 391