99e31c9b34de445fccc33896f7316271cca39c29
[openwrt.git] / target / linux / mcs814x / files-3.3 / arch / arm / mach-mcs814x / clock.c
1 /*
2  * Moschip MCS814x clock routines
3  *
4  * Copyright (C) 2012, Florian Fainelli <florian@openwrt.org>
5  *
6  * Licensed under GPLv2
7  */
8 #include <linux/kernel.h>
9 #include <linux/init.h>
10 #include <linux/export.h>
11 #include <linux/spinlock.h>
12 #include <linux/err.h>
13 #include <linux/io.h>
14 #include <linux/clkdev.h>
15 #include <linux/clk.h>
16
17 #include <mach/mcs814x.h>
18
19 /* System configuration registers offsets */
20 #define SYSDBG_BS1      0x00
21 #define SYSDBG_SYSCTL   0x08
22 #define  SYSCTL_EMAC    (1 << 0)
23 #define  SYSCTL_CIPHER  (1 << 16)
24 #define SYSDBG_PLL_CTL  0x3C
25
26 #define CPU_FREQ_SHIFT  27
27 #define CPU_FREQ_MASK   0x0F
28 #define SDRAM_FREQ_BIT  (1 << 22)
29
30 #define KHZ     1000
31 #define MHZ     (KHZ * KHZ)
32
33 struct clk_ops {
34         unsigned long (*get_rate)(struct clk *clk);
35         int (*set_rate)(struct clk *clk, unsigned long rate);
36         struct clk *(*get_parent)(struct clk *clk);
37         int (*enable)(struct clk *clk, int enable);
38 };
39
40 struct clk {
41         struct clk *parent;             /* parent clk */
42         unsigned long rate;             /* clock rate in Hz */
43         unsigned long divider;          /* clock divider */
44         u32 usecount;                   /* reference count */
45         struct clk_ops *ops;            /* clock operation */
46         void __iomem *enable_reg;       /* clock enable register */
47         u32 enable_mask;                /* clock enable mask */
48 };
49
50 static unsigned long clk_divide_parent(struct clk *clk)
51 {
52         if (clk->parent && clk->divider)
53                 return clk_get_rate(clk->parent) / clk->divider;
54         else
55                 return 0;
56 }
57
58 static int clk_local_onoff_enable(struct clk *clk, int enable)
59 {
60         u32 tmp;
61
62         /* no enable_reg means the clock is always enabled */
63         if (!clk->enable_reg)
64                 return 0;
65
66         tmp = __raw_readl(clk->enable_reg);
67         if (!enable)
68                 tmp &= ~clk->enable_mask;
69         else
70                 tmp |= clk->enable_mask;
71
72         __raw_writel(tmp, clk->enable_reg);
73
74         return 0;
75 }
76
77 static struct clk_ops default_clk_ops = {
78         .get_rate       = clk_divide_parent,
79         .enable         = clk_local_onoff_enable,
80 };
81
82 static DEFINE_SPINLOCK(clocks_lock);
83
84 static const unsigned long cpu_freq_table[] = {
85         175000,
86         300000,
87         125000,
88         137500,
89         212500,
90         250000,
91         162500,
92         187500,
93         162500,
94         150000,
95         225000,
96         237500,
97         200000,
98         262500,
99         275000,
100         287500
101 };
102
103 static struct clk clk_cpu;
104
105 /* System clock is fixed at 50Mhz */
106 static struct clk clk_sys = {
107         .rate   = 50 * MHZ,
108 };
109
110 static struct clk clk_sdram;
111
112 static struct clk clk_timer0 = {
113         .parent = &clk_sdram,
114         .divider = 2,
115         .ops    = &default_clk_ops,
116 };
117
118 static struct clk clk_timer1_2 = {
119         .parent = &clk_sys,
120 };
121
122 /* Watchdog clock is system clock / 128 */
123 static struct clk clk_wdt = {
124         .parent = &clk_sys,
125         .divider = 128,
126         .ops    = &default_clk_ops,
127 };
128
129 static struct clk clk_emac = {
130         .ops            = &default_clk_ops,
131         .enable_reg     = (void __iomem *)(_CONFADDR_SYSDBG + SYSDBG_SYSCTL),
132         .enable_mask    = SYSCTL_EMAC,
133 };
134
135 static struct clk clk_ephy = {
136         .ops            = &default_clk_ops,
137         .enable_reg     = (void __iomem *)(_CONFADDR_SYSDBG + SYSDBG_PLL_CTL),
138         .enable_mask    = ~(1 << 0),
139 };
140
141 static struct clk clk_cipher = {
142         .ops            = &default_clk_ops,
143         .enable_reg     = (void __iomem *)(_CONFADDR_SYSDBG + SYSDBG_SYSCTL),
144         .enable_mask    = SYSCTL_CIPHER,
145 };
146
147 #define CLK(_dev, _con, _clk)   \
148 { .dev_id = (_dev), .con_id = (_con), .clk = (_clk) },
149
150 static struct clk_lookup mcs814x_chip_clks[] = {
151         CLK("cpu", NULL, &clk_cpu)
152         CLK("sys", NULL, &clk_sys)
153         CLK("sdram", NULL, &clk_sdram)
154         /* 32-bits timer0 */
155         CLK("timer0", NULL, &clk_timer0)
156         /* 16-bits timer1 */
157         CLK("timer1", NULL, &clk_timer1_2)
158         /* 64-bits timer2, same as timer 1 */
159         CLK("timer2", NULL, &clk_timer1_2)
160         CLK(NULL, "wdt", &clk_wdt)
161         CLK(NULL, "emac", &clk_emac)
162         CLK(NULL, "ephy", &clk_ephy)
163         CLK(NULL, "cipher", &clk_cipher)
164 };
165
166 static void local_clk_disable(struct clk *clk)
167 {
168         WARN_ON(!clk->usecount);
169
170         if (clk->usecount > 0) {
171                 clk->usecount--;
172
173                 if ((clk->usecount == 0) && (clk->ops->enable))
174                         clk->ops->enable(clk, 0);
175
176                 if (clk->parent)
177                         local_clk_disable(clk->parent);
178         }
179 }
180
181 static int local_clk_enable(struct clk *clk)
182 {
183         int ret = 0;
184
185         if (clk->parent)
186                 ret = local_clk_enable(clk->parent);
187
188         if (ret)
189                 return ret;
190
191         if ((clk->usecount == 0) && (clk->ops->enable))
192                 ret = clk->ops->enable(clk, 1);
193
194         if (!ret)
195                 clk->usecount++;
196         else if (clk->parent && clk->parent->ops->enable)
197                 local_clk_disable(clk->parent);
198
199         return ret;
200 }
201
202 int clk_enable(struct clk *clk)
203 {
204         int ret;
205         unsigned long flags;
206
207         spin_lock_irqsave(&clocks_lock, flags);
208         ret = local_clk_enable(clk);
209         spin_unlock_irqrestore(&clocks_lock, flags);
210
211         return ret;
212 }
213 EXPORT_SYMBOL(clk_enable);
214
215 void clk_disable(struct clk *clk)
216 {
217         unsigned long flags;
218
219         spin_lock_irqsave(&clocks_lock, flags);
220         local_clk_disable(clk);
221         spin_unlock_irqrestore(&clocks_lock, flags);
222 }
223 EXPORT_SYMBOL(clk_disable);
224
225 unsigned long clk_get_rate(struct clk *clk)
226 {
227         if (unlikely(IS_ERR_OR_NULL(clk)))
228                 return 0;
229
230         if (clk->rate)
231                 return clk->rate;
232
233         if (clk->ops && clk->ops->get_rate)
234                 return clk->ops->get_rate(clk);
235
236         return clk_get_rate(clk->parent);
237 }
238 EXPORT_SYMBOL(clk_get_rate);
239
240 struct clk *clk_get_parent(struct clk *clk)
241 {
242         unsigned long flags;
243
244         if (unlikely(IS_ERR_OR_NULL(clk)))
245                 return NULL;
246
247         if (!clk->ops || !clk->ops->get_parent)
248                 return clk->parent;
249
250         spin_lock_irqsave(&clocks_lock, flags);
251         clk->parent = clk->ops->get_parent(clk);
252         spin_unlock_irqrestore(&clocks_lock, flags);
253
254         return clk->parent;
255 }
256 EXPORT_SYMBOL(clk_get_parent);
257
258 void __init mcs814x_clk_init(void)
259 {
260         u32 bs1;
261         u8 cpu_freq;
262
263         clkdev_add_table(mcs814x_chip_clks, ARRAY_SIZE(mcs814x_chip_clks));
264
265         /* read the bootstrap registers to know the exact clocking scheme */
266         bs1 = __raw_readl(_CONFADDR_SYSDBG + SYSDBG_BS1);
267         cpu_freq = (bs1 >> CPU_FREQ_SHIFT) & CPU_FREQ_MASK;
268
269         pr_info("CPU frequency: %lu (kHz)\n", cpu_freq_table[cpu_freq]);
270         clk_cpu.rate = cpu_freq * KHZ;
271
272         /* read SDRAM frequency */
273         if (bs1 & SDRAM_FREQ_BIT)
274                 clk_sdram.rate = 100 * MHZ;
275         else
276                 clk_sdram.rate = 133 * MHZ;
277
278         pr_info("SDRAM frequency: %lu (MHz)\n", clk_sdram.rate / MHZ);
279 }
280