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/syscore_ops.h>
  15#include "clk.h"
  16
  17static DEFINE_SPINLOCK(lock);
  18static struct clk **clk_table;
  19static void __iomem *reg_base;
  20#ifdef CONFIG_OF
  21static struct clk_onecell_data clk_data;
  22#endif
  23
  24#ifdef CONFIG_PM_SLEEP
  25static struct samsung_clk_reg_dump *reg_dump;
  26static unsigned long nr_reg_dump;
  27
  28static int samsung_clk_suspend(void)
  29{
  30        struct samsung_clk_reg_dump *rd = reg_dump;
  31        unsigned long i;
  32
  33        for (i = 0; i < nr_reg_dump; i++, rd++)
  34                rd->value = __raw_readl(reg_base + rd->offset);
  35
  36        return 0;
  37}
  38
  39static void samsung_clk_resume(void)
  40{
  41        struct samsung_clk_reg_dump *rd = reg_dump;
  42        unsigned long i;
  43
  44        for (i = 0; i < nr_reg_dump; i++, rd++)
  45                __raw_writel(rd->value, reg_base + rd->offset);
  46}
  47
  48static struct syscore_ops samsung_clk_syscore_ops = {
  49        .suspend        = samsung_clk_suspend,
  50        .resume         = samsung_clk_resume,
  51};
  52#endif /* CONFIG_PM_SLEEP */
  53
  54/* setup the essentials required to support clock lookup using ccf */
  55void __init samsung_clk_init(struct device_node *np, void __iomem *base,
  56                unsigned long nr_clks, unsigned long *rdump,
  57                unsigned long nr_rdump, unsigned long *soc_rdump,
  58                unsigned long nr_soc_rdump)
  59{
  60        reg_base = base;
  61
  62#ifdef CONFIG_PM_SLEEP
  63        if (rdump && nr_rdump) {
  64                unsigned int idx;
  65                reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
  66                                * (nr_rdump + nr_soc_rdump), GFP_KERNEL);
  67                if (!reg_dump) {
  68                        pr_err("%s: memory alloc for register dump failed\n",
  69                                        __func__);
  70                        return;
  71                }
  72
  73                for (idx = 0; idx < nr_rdump; idx++)
  74                        reg_dump[idx].offset = rdump[idx];
  75                for (idx = 0; idx < nr_soc_rdump; idx++)
  76                        reg_dump[nr_rdump + idx].offset = soc_rdump[idx];
  77                nr_reg_dump = nr_rdump + nr_soc_rdump;
  78                register_syscore_ops(&samsung_clk_syscore_ops);
  79        }
  80#endif
  81
  82        clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
  83        if (!clk_table)
  84                panic("could not allocate clock lookup table\n");
  85
  86        if (!np)
  87                return;
  88
  89#ifdef CONFIG_OF
  90        clk_data.clks = clk_table;
  91        clk_data.clk_num = nr_clks;
  92        of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
  93#endif
  94}
  95
  96/* add a clock instance to the clock lookup table used for dt based lookup */
  97void samsung_clk_add_lookup(struct clk *clk, unsigned int id)
  98{
  99        if (clk_table && id)
 100                clk_table[id] = clk;
 101}
 102
 103/* register a list of aliases */
 104void __init samsung_clk_register_alias(struct samsung_clock_alias *list,
 105                                        unsigned int nr_clk)
 106{
 107        struct clk *clk;
 108        unsigned int idx, ret;
 109
 110        if (!clk_table) {
 111                pr_err("%s: clock table missing\n", __func__);
 112                return;
 113        }
 114
 115        for (idx = 0; idx < nr_clk; idx++, list++) {
 116                if (!list->id) {
 117                        pr_err("%s: clock id missing for index %d\n", __func__,
 118                                idx);
 119                        continue;
 120                }
 121
 122                clk = clk_table[list->id];
 123                if (!clk) {
 124                        pr_err("%s: failed to find clock %d\n", __func__,
 125                                list->id);
 126                        continue;
 127                }
 128
 129                ret = clk_register_clkdev(clk, list->alias, list->dev_name);
 130                if (ret)
 131                        pr_err("%s: failed to register lookup %s\n",
 132                                        __func__, list->alias);
 133        }
 134}
 135
 136/* register a list of fixed clocks */
 137void __init samsung_clk_register_fixed_rate(
 138                struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
 139{
 140        struct clk *clk;
 141        unsigned int idx, ret;
 142
 143        for (idx = 0; idx < nr_clk; idx++, list++) {
 144                clk = clk_register_fixed_rate(NULL, list->name,
 145                        list->parent_name, list->flags, list->fixed_rate);
 146                if (IS_ERR(clk)) {
 147                        pr_err("%s: failed to register clock %s\n", __func__,
 148                                list->name);
 149                        continue;
 150                }
 151
 152                samsung_clk_add_lookup(clk, list->id);
 153
 154                /*
 155                 * Unconditionally add a clock lookup for the fixed rate clocks.
 156                 * There are not many of these on any of Samsung platforms.
 157                 */
 158                ret = clk_register_clkdev(clk, list->name, NULL);
 159                if (ret)
 160                        pr_err("%s: failed to register clock lookup for %s",
 161                                __func__, list->name);
 162        }
 163}
 164
 165/* register a list of fixed factor clocks */
 166void __init samsung_clk_register_fixed_factor(
 167                struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
 168{
 169        struct clk *clk;
 170        unsigned int idx;
 171
 172        for (idx = 0; idx < nr_clk; idx++, list++) {
 173                clk = clk_register_fixed_factor(NULL, list->name,
 174                        list->parent_name, list->flags, list->mult, list->div);
 175                if (IS_ERR(clk)) {
 176                        pr_err("%s: failed to register clock %s\n", __func__,
 177                                list->name);
 178                        continue;
 179                }
 180
 181                samsung_clk_add_lookup(clk, list->id);
 182        }
 183}
 184
 185/* register a list of mux clocks */
 186void __init samsung_clk_register_mux(struct samsung_mux_clock *list,
 187                                        unsigned int nr_clk)
 188{
 189        struct clk *clk;
 190        unsigned int idx, ret;
 191
 192        for (idx = 0; idx < nr_clk; idx++, list++) {
 193                clk = clk_register_mux(NULL, list->name, list->parent_names,
 194                        list->num_parents, list->flags, reg_base + list->offset,
 195                        list->shift, list->width, list->mux_flags, &lock);
 196                if (IS_ERR(clk)) {
 197                        pr_err("%s: failed to register clock %s\n", __func__,
 198                                list->name);
 199                        continue;
 200                }
 201
 202                samsung_clk_add_lookup(clk, list->id);
 203
 204                /* register a clock lookup only if a clock alias is specified */
 205                if (list->alias) {
 206                        ret = clk_register_clkdev(clk, list->alias,
 207                                                list->dev_name);
 208                        if (ret)
 209                                pr_err("%s: failed to register lookup %s\n",
 210                                                __func__, list->alias);
 211                }
 212        }
 213}
 214
 215/* register a list of div clocks */
 216void __init samsung_clk_register_div(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                                        reg_base + list->offset, list->shift,
 227                                        list->width, list->div_flags,
 228                                        list->table, &lock);
 229                else
 230                        clk = clk_register_divider(NULL, list->name,
 231                                        list->parent_name, list->flags,
 232                                        reg_base + list->offset, list->shift,
 233                                        list->width, list->div_flags, &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(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_gate_clock *list,
 255                                                unsigned int nr_clk)
 256{
 257        struct clk *clk;
 258        unsigned int idx, ret;
 259
 260        for (idx = 0; idx < nr_clk; idx++, list++) {
 261                clk = clk_register_gate(NULL, list->name, list->parent_name,
 262                                list->flags, reg_base + list->offset,
 263                                list->bit_idx, list->gate_flags, &lock);
 264                if (IS_ERR(clk)) {
 265                        pr_err("%s: failed to register clock %s\n", __func__,
 266                                list->name);
 267                        continue;
 268                }
 269
 270                /* register a clock lookup only if a clock alias is specified */
 271                if (list->alias) {
 272                        ret = clk_register_clkdev(clk, list->alias,
 273                                                        list->dev_name);
 274                        if (ret)
 275                                pr_err("%s: failed to register lookup %s\n",
 276                                        __func__, list->alias);
 277                }
 278
 279                samsung_clk_add_lookup(clk, list->id);
 280        }
 281}
 282
 283/*
 284 * obtain the clock speed of all external fixed clock sources from device
 285 * tree and register it
 286 */
 287#ifdef CONFIG_OF
 288void __init samsung_clk_of_register_fixed_ext(
 289                        struct samsung_fixed_rate_clock *fixed_rate_clk,
 290                        unsigned int nr_fixed_rate_clk,
 291                        struct of_device_id *clk_matches)
 292{
 293        const struct of_device_id *match;
 294        struct device_node *np;
 295        u32 freq;
 296
 297        for_each_matching_node_and_match(np, clk_matches, &match) {
 298                if (of_property_read_u32(np, "clock-frequency", &freq))
 299                        continue;
 300                fixed_rate_clk[(u32)match->data].fixed_rate = freq;
 301        }
 302        samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk);
 303}
 304#endif
 305
 306/* utility function to get the rate of a specified clock */
 307unsigned long _get_rate(const char *clk_name)
 308{
 309        struct clk *clk;
 310
 311        clk = __clk_lookup(clk_name);
 312        if (!clk) {
 313                pr_err("%s: could not find clock %s\n", __func__, clk_name);
 314                return 0;
 315        }
 316
 317        return clk_get_rate(clk);
 318}
 319