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