linux/arch/sh/kernel/cpu/sh4/clock-sh4-202.c
<<
>>
Prefs
   1/*
   2 * arch/sh/kernel/cpu/sh4/clock-sh4-202.c
   3 *
   4 * Additional SH4-202 support for the clock framework
   5 *
   6 *  Copyright (C) 2005  Paul Mundt
   7 *
   8 * This file is subject to the terms and conditions of the GNU General Public
   9 * License.  See the file "COPYING" in the main directory of this archive
  10 * for more details.
  11 */
  12#include <linux/init.h>
  13#include <linux/kernel.h>
  14#include <linux/err.h>
  15#include <asm/clock.h>
  16#include <asm/freq.h>
  17#include <asm/io.h>
  18
  19#define CPG2_FRQCR3     0xfe0a0018
  20
  21static int frqcr3_divisors[] = { 1, 2, 3, 4, 6, 8, 16 };
  22static int frqcr3_values[]   = { 0, 1, 2, 3, 4, 5, 6  };
  23
  24static void emi_clk_recalc(struct clk *clk)
  25{
  26        int idx = ctrl_inl(CPG2_FRQCR3) & 0x0007;
  27        clk->rate = clk->parent->rate / frqcr3_divisors[idx];
  28}
  29
  30static inline int frqcr3_lookup(struct clk *clk, unsigned long rate)
  31{
  32        int divisor = clk->parent->rate / rate;
  33        int i;
  34
  35        for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++)
  36                if (frqcr3_divisors[i] == divisor)
  37                        return frqcr3_values[i];
  38
  39        /* Safe fallback */
  40        return 5;
  41}
  42
  43static struct clk_ops sh4202_emi_clk_ops = {
  44        .recalc         = emi_clk_recalc,
  45};
  46
  47static struct clk sh4202_emi_clk = {
  48        .name           = "emi_clk",
  49        .flags          = CLK_ALWAYS_ENABLED,
  50        .ops            = &sh4202_emi_clk_ops,
  51};
  52
  53static void femi_clk_recalc(struct clk *clk)
  54{
  55        int idx = (ctrl_inl(CPG2_FRQCR3) >> 3) & 0x0007;
  56        clk->rate = clk->parent->rate / frqcr3_divisors[idx];
  57}
  58
  59static struct clk_ops sh4202_femi_clk_ops = {
  60        .recalc         = femi_clk_recalc,
  61};
  62
  63static struct clk sh4202_femi_clk = {
  64        .name           = "femi_clk",
  65        .flags          = CLK_ALWAYS_ENABLED,
  66        .ops            = &sh4202_femi_clk_ops,
  67};
  68
  69static void shoc_clk_init(struct clk *clk)
  70{
  71        int i;
  72
  73        /*
  74         * For some reason, the shoc_clk seems to be set to some really
  75         * insane value at boot (values outside of the allowable frequency
  76         * range for instance). We deal with this by scaling it back down
  77         * to something sensible just in case.
  78         *
  79         * Start scaling from the high end down until we find something
  80         * that passes rate verification..
  81         */
  82        for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) {
  83                int divisor = frqcr3_divisors[i];
  84
  85                if (clk->ops->set_rate(clk, clk->parent->rate /
  86                                                divisor, 0) == 0)
  87                        break;
  88        }
  89
  90        WARN_ON(i == ARRAY_SIZE(frqcr3_divisors));      /* Undefined clock */
  91}
  92
  93static void shoc_clk_recalc(struct clk *clk)
  94{
  95        int idx = (ctrl_inl(CPG2_FRQCR3) >> 6) & 0x0007;
  96        clk->rate = clk->parent->rate / frqcr3_divisors[idx];
  97}
  98
  99static int shoc_clk_verify_rate(struct clk *clk, unsigned long rate)
 100{
 101        struct clk *bclk = clk_get(NULL, "bus_clk");
 102        unsigned long bclk_rate = clk_get_rate(bclk);
 103
 104        clk_put(bclk);
 105
 106        if (rate > bclk_rate)
 107                return 1;
 108        if (rate > 66000000)
 109                return 1;
 110
 111        return 0;
 112}
 113
 114static int shoc_clk_set_rate(struct clk *clk, unsigned long rate, int algo_id)
 115{
 116        unsigned long frqcr3;
 117        unsigned int tmp;
 118
 119        /* Make sure we have something sensible to switch to */
 120        if (shoc_clk_verify_rate(clk, rate) != 0)
 121                return -EINVAL;
 122
 123        tmp = frqcr3_lookup(clk, rate);
 124
 125        frqcr3 = ctrl_inl(CPG2_FRQCR3);
 126        frqcr3 &= ~(0x0007 << 6);
 127        frqcr3 |= tmp << 6;
 128        ctrl_outl(frqcr3, CPG2_FRQCR3);
 129
 130        clk->rate = clk->parent->rate / frqcr3_divisors[tmp];
 131
 132        return 0;
 133}
 134
 135static struct clk_ops sh4202_shoc_clk_ops = {
 136        .init           = shoc_clk_init,
 137        .recalc         = shoc_clk_recalc,
 138        .set_rate       = shoc_clk_set_rate,
 139};
 140
 141static struct clk sh4202_shoc_clk = {
 142        .name           = "shoc_clk",
 143        .flags          = CLK_ALWAYS_ENABLED,
 144        .ops            = &sh4202_shoc_clk_ops,
 145};
 146
 147static struct clk *sh4202_onchip_clocks[] = {
 148        &sh4202_emi_clk,
 149        &sh4202_femi_clk,
 150        &sh4202_shoc_clk,
 151};
 152
 153static int __init sh4202_clk_init(void)
 154{
 155        struct clk *clk = clk_get(NULL, "master_clk");
 156        int i;
 157
 158        for (i = 0; i < ARRAY_SIZE(sh4202_onchip_clocks); i++) {
 159                struct clk *clkp = sh4202_onchip_clocks[i];
 160
 161                clkp->parent = clk;
 162                clk_register(clkp);
 163                clk_enable(clkp);
 164        }
 165
 166        /*
 167         * Now that we have the rest of the clocks registered, we need to
 168         * force the parent clock to propagate so that these clocks will
 169         * automatically figure out their rate. We cheat by handing the
 170         * parent clock its current rate and forcing child propagation.
 171         */
 172        clk_set_rate(clk, clk_get_rate(clk));
 173
 174        clk_put(clk);
 175
 176        return 0;
 177}
 178
 179arch_initcall(sh4202_clk_init);
 180
 181
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.