linux/drivers/sh/clk/cpg.c
<<
>>
Prefs
   1/*
   2 * Helper routines for SuperH Clock Pulse Generator blocks (CPG).
   3 *
   4 *  Copyright (C) 2010  Magnus Damm
   5 *
   6 * This file is subject to the terms and conditions of the GNU General Public
   7 * License.  See the file "COPYING" in the main directory of this archive
   8 * for more details.
   9 */
  10#include <linux/clk.h>
  11#include <linux/compiler.h>
  12#include <linux/slab.h>
  13#include <linux/io.h>
  14#include <linux/sh_clk.h>
  15
  16static int sh_clk_mstp32_enable(struct clk *clk)
  17{
  18        __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit),
  19                     clk->enable_reg);
  20        return 0;
  21}
  22
  23static void sh_clk_mstp32_disable(struct clk *clk)
  24{
  25        __raw_writel(__raw_readl(clk->enable_reg) | (1 << clk->enable_bit),
  26                     clk->enable_reg);
  27}
  28
  29static struct clk_ops sh_clk_mstp32_clk_ops = {
  30        .enable         = sh_clk_mstp32_enable,
  31        .disable        = sh_clk_mstp32_disable,
  32        .recalc         = followparent_recalc,
  33};
  34
  35int __init sh_clk_mstp32_register(struct clk *clks, int nr)
  36{
  37        struct clk *clkp;
  38        int ret = 0;
  39        int k;
  40
  41        for (k = 0; !ret && (k < nr); k++) {
  42                clkp = clks + k;
  43                clkp->ops = &sh_clk_mstp32_clk_ops;
  44                ret |= clk_register(clkp);
  45        }
  46
  47        return ret;
  48}
  49
  50static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate)
  51{
  52        return clk_rate_table_round(clk, clk->freq_table, rate);
  53}
  54
  55static int sh_clk_div6_divisors[64] = {
  56        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
  57        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
  58        33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
  59        49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
  60};
  61
  62static struct clk_div_mult_table sh_clk_div6_table = {
  63        .divisors = sh_clk_div6_divisors,
  64        .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors),
  65};
  66
  67static unsigned long sh_clk_div6_recalc(struct clk *clk)
  68{
  69        struct clk_div_mult_table *table = &sh_clk_div6_table;
  70        unsigned int idx;
  71
  72        clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
  73                             table, NULL);
  74
  75        idx = __raw_readl(clk->enable_reg) & 0x003f;
  76
  77        return clk->freq_table[idx].frequency;
  78}
  79
  80static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
  81{
  82        struct clk_div_mult_table *table = &sh_clk_div6_table;
  83        u32 value;
  84        int ret, i;
  85
  86        if (!clk->parent_table || !clk->parent_num)
  87                return -EINVAL;
  88
  89        /* Search the parent */
  90        for (i = 0; i < clk->parent_num; i++)
  91                if (clk->parent_table[i] == parent)
  92                        break;
  93
  94        if (i == clk->parent_num)
  95                return -ENODEV;
  96
  97        ret = clk_reparent(clk, parent);
  98        if (ret < 0)
  99                return ret;
 100
 101        value = __raw_readl(clk->enable_reg) &
 102                ~(((1 << clk->src_width) - 1) << clk->src_shift);
 103
 104        __raw_writel(value | (i << clk->src_shift), clk->enable_reg);
 105
 106        /* Rebuild the frequency table */
 107        clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
 108                             table, &clk->arch_flags);
 109
 110        return 0;
 111}
 112
 113static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate)
 114{
 115        unsigned long value;
 116        int idx;
 117
 118        idx = clk_rate_table_find(clk, clk->freq_table, rate);
 119        if (idx < 0)
 120                return idx;
 121
 122        value = __raw_readl(clk->enable_reg);
 123        value &= ~0x3f;
 124        value |= idx;
 125        __raw_writel(value, clk->enable_reg);
 126        return 0;
 127}
 128
 129static int sh_clk_div6_enable(struct clk *clk)
 130{
 131        unsigned long value;
 132        int ret;
 133
 134        ret = sh_clk_div6_set_rate(clk, clk->rate);
 135        if (ret == 0) {
 136                value = __raw_readl(clk->enable_reg);
 137                value &= ~0x100; /* clear stop bit to enable clock */
 138                __raw_writel(value, clk->enable_reg);
 139        }
 140        return ret;
 141}
 142
 143static void sh_clk_div6_disable(struct clk *clk)
 144{
 145        unsigned long value;
 146
 147        value = __raw_readl(clk->enable_reg);
 148        value |= 0x100; /* stop clock */
 149        value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */
 150        __raw_writel(value, clk->enable_reg);
 151}
 152
 153static struct clk_ops sh_clk_div6_clk_ops = {
 154        .recalc         = sh_clk_div6_recalc,
 155        .round_rate     = sh_clk_div_round_rate,
 156        .set_rate       = sh_clk_div6_set_rate,
 157        .enable         = sh_clk_div6_enable,
 158        .disable        = sh_clk_div6_disable,
 159};
 160
 161static struct clk_ops sh_clk_div6_reparent_clk_ops = {
 162        .recalc         = sh_clk_div6_recalc,
 163        .round_rate     = sh_clk_div_round_rate,
 164        .set_rate       = sh_clk_div6_set_rate,
 165        .enable         = sh_clk_div6_enable,
 166        .disable        = sh_clk_div6_disable,
 167        .set_parent     = sh_clk_div6_set_parent,
 168};
 169
 170static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
 171                                           struct clk_ops *ops)
 172{
 173        struct clk *clkp;
 174        void *freq_table;
 175        int nr_divs = sh_clk_div6_table.nr_divisors;
 176        int freq_table_size = sizeof(struct cpufreq_frequency_table);
 177        int ret = 0;
 178        int k;
 179
 180        freq_table_size *= (nr_divs + 1);
 181        freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL);
 182        if (!freq_table) {
 183                pr_err("sh_clk_div6_register: unable to alloc memory\n");
 184                return -ENOMEM;
 185        }
 186
 187        for (k = 0; !ret && (k < nr); k++) {
 188                clkp = clks + k;
 189
 190                clkp->ops = ops;
 191                clkp->freq_table = freq_table + (k * freq_table_size);
 192                clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
 193
 194                ret = clk_register(clkp);
 195        }
 196
 197        return ret;
 198}
 199
 200int __init sh_clk_div6_register(struct clk *clks, int nr)
 201{
 202        return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops);
 203}
 204
 205int __init sh_clk_div6_reparent_register(struct clk *clks, int nr)
 206{
 207        return sh_clk_div6_register_ops(clks, nr,
 208                                        &sh_clk_div6_reparent_clk_ops);
 209}
 210
 211static unsigned long sh_clk_div4_recalc(struct clk *clk)
 212{
 213        struct clk_div4_table *d4t = clk->priv;
 214        struct clk_div_mult_table *table = d4t->div_mult_table;
 215        unsigned int idx;
 216
 217        clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
 218                             table, &clk->arch_flags);
 219
 220        idx = (__raw_readl(clk->enable_reg) >> clk->enable_bit) & 0x000f;
 221
 222        return clk->freq_table[idx].frequency;
 223}
 224
 225static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
 226{
 227        struct clk_div4_table *d4t = clk->priv;
 228        struct clk_div_mult_table *table = d4t->div_mult_table;
 229        u32 value;
 230        int ret;
 231
 232        /* we really need a better way to determine parent index, but for
 233         * now assume internal parent comes with CLK_ENABLE_ON_INIT set,
 234         * no CLK_ENABLE_ON_INIT means external clock...
 235         */
 236
 237        if (parent->flags & CLK_ENABLE_ON_INIT)
 238                value = __raw_readl(clk->enable_reg) & ~(1 << 7);
 239        else
 240                value = __raw_readl(clk->enable_reg) | (1 << 7);
 241
 242        ret = clk_reparent(clk, parent);
 243        if (ret < 0)
 244                return ret;
 245
 246        __raw_writel(value, clk->enable_reg);
 247
 248        /* Rebiuld the frequency table */
 249        clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
 250                             table, &clk->arch_flags);
 251
 252        return 0;
 253}
 254
 255static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate)
 256{
 257        struct clk_div4_table *d4t = clk->priv;
 258        unsigned long value;
 259        int idx = clk_rate_table_find(clk, clk->freq_table, rate);
 260        if (idx < 0)
 261                return idx;
 262
 263        value = __raw_readl(clk->enable_reg);
 264        value &= ~(0xf << clk->enable_bit);
 265        value |= (idx << clk->enable_bit);
 266        __raw_writel(value, clk->enable_reg);
 267
 268        if (d4t->kick)
 269                d4t->kick(clk);
 270
 271        return 0;
 272}
 273
 274static int sh_clk_div4_enable(struct clk *clk)
 275{
 276        __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << 8), clk->enable_reg);
 277        return 0;
 278}
 279
 280static void sh_clk_div4_disable(struct clk *clk)
 281{
 282        __raw_writel(__raw_readl(clk->enable_reg) | (1 << 8), clk->enable_reg);
 283}
 284
 285static struct clk_ops sh_clk_div4_clk_ops = {
 286        .recalc         = sh_clk_div4_recalc,
 287        .set_rate       = sh_clk_div4_set_rate,
 288        .round_rate     = sh_clk_div_round_rate,
 289};
 290
 291static struct clk_ops sh_clk_div4_enable_clk_ops = {
 292        .recalc         = sh_clk_div4_recalc,
 293        .set_rate       = sh_clk_div4_set_rate,
 294        .round_rate     = sh_clk_div_round_rate,
 295        .enable         = sh_clk_div4_enable,
 296        .disable        = sh_clk_div4_disable,
 297};
 298
 299static struct clk_ops sh_clk_div4_reparent_clk_ops = {
 300        .recalc         = sh_clk_div4_recalc,
 301        .set_rate       = sh_clk_div4_set_rate,
 302        .round_rate     = sh_clk_div_round_rate,
 303        .enable         = sh_clk_div4_enable,
 304        .disable        = sh_clk_div4_disable,
 305        .set_parent     = sh_clk_div4_set_parent,
 306};
 307
 308static int __init sh_clk_div4_register_ops(struct clk *clks, int nr,
 309                        struct clk_div4_table *table, struct clk_ops *ops)
 310{
 311        struct clk *clkp;
 312        void *freq_table;
 313        int nr_divs = table->div_mult_table->nr_divisors;
 314        int freq_table_size = sizeof(struct cpufreq_frequency_table);
 315        int ret = 0;
 316        int k;
 317
 318        freq_table_size *= (nr_divs + 1);
 319        freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL);
 320        if (!freq_table) {
 321                pr_err("sh_clk_div4_register: unable to alloc memory\n");
 322                return -ENOMEM;
 323        }
 324
 325        for (k = 0; !ret && (k < nr); k++) {
 326                clkp = clks + k;
 327
 328                clkp->ops = ops;
 329                clkp->priv = table;
 330
 331                clkp->freq_table = freq_table + (k * freq_table_size);
 332                clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
 333
 334                ret = clk_register(clkp);
 335        }
 336
 337        return ret;
 338}
 339
 340int __init sh_clk_div4_register(struct clk *clks, int nr,
 341                                struct clk_div4_table *table)
 342{
 343        return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div4_clk_ops);
 344}
 345
 346int __init sh_clk_div4_enable_register(struct clk *clks, int nr,
 347                                struct clk_div4_table *table)
 348{
 349        return sh_clk_div4_register_ops(clks, nr, table,
 350                                        &sh_clk_div4_enable_clk_ops);
 351}
 352
 353int __init sh_clk_div4_reparent_register(struct clk *clks, int nr,
 354                                struct clk_div4_table *table)
 355{
 356        return sh_clk_div4_register_ops(clks, nr, table,
 357                                        &sh_clk_div4_reparent_clk_ops);
 358}
 359