]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - arch/arm/mach-s3c24xx/clock-dclk.c
Merge remote-tracking branch 'regulator/fix/palmas' into tmp
[karo-tx-linux.git] / arch / arm / mach-s3c24xx / clock-dclk.c
1 /*
2  * Copyright (c) 2004-2008 Simtec Electronics
3  *      Ben Dooks <ben@simtec.co.uk>
4  *      http://armlinux.simtec.co.uk/
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  * S3C24XX - definitions for DCLK and CLKOUT registers
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/errno.h>
15 #include <linux/clk.h>
16 #include <linux/io.h>
17
18 #include <mach/regs-clock.h>
19 #include <mach/regs-gpio.h>
20
21 #include <plat/clock.h>
22 #include <plat/cpu.h>
23
24 /* clocks that could be registered by external code */
25
26 static int s3c24xx_dclk_enable(struct clk *clk, int enable)
27 {
28         unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON);
29
30         if (enable)
31                 dclkcon |= clk->ctrlbit;
32         else
33                 dclkcon &= ~clk->ctrlbit;
34
35         __raw_writel(dclkcon, S3C24XX_DCLKCON);
36
37         return 0;
38 }
39
40 static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent)
41 {
42         unsigned long dclkcon;
43         unsigned int uclk;
44
45         if (parent == &clk_upll)
46                 uclk = 1;
47         else if (parent == &clk_p)
48                 uclk = 0;
49         else
50                 return -EINVAL;
51
52         clk->parent = parent;
53
54         dclkcon = __raw_readl(S3C24XX_DCLKCON);
55
56         if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) {
57                 if (uclk)
58                         dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK;
59                 else
60                         dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK;
61         } else {
62                 if (uclk)
63                         dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK;
64                 else
65                         dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK;
66         }
67
68         __raw_writel(dclkcon, S3C24XX_DCLKCON);
69
70         return 0;
71 }
72 static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate)
73 {
74         unsigned long div;
75
76         if ((rate == 0) || !clk->parent)
77                 return 0;
78
79         div = clk_get_rate(clk->parent) / rate;
80         if (div < 2)
81                 div = 2;
82         else if (div > 16)
83                 div = 16;
84
85         return div;
86 }
87
88 static unsigned long s3c24xx_round_dclk_rate(struct clk *clk,
89         unsigned long rate)
90 {
91         unsigned long div = s3c24xx_calc_div(clk, rate);
92
93         if (div == 0)
94                 return 0;
95
96         return clk_get_rate(clk->parent) / div;
97 }
98
99 static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate)
100 {
101         unsigned long mask, data, div = s3c24xx_calc_div(clk, rate);
102
103         if (div == 0)
104                 return -EINVAL;
105
106         if (clk == &s3c24xx_dclk0) {
107                 mask = S3C2410_DCLKCON_DCLK0_DIV_MASK |
108                         S3C2410_DCLKCON_DCLK0_CMP_MASK;
109                 data = S3C2410_DCLKCON_DCLK0_DIV(div) |
110                         S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2);
111         } else if (clk == &s3c24xx_dclk1) {
112                 mask = S3C2410_DCLKCON_DCLK1_DIV_MASK |
113                         S3C2410_DCLKCON_DCLK1_CMP_MASK;
114                 data = S3C2410_DCLKCON_DCLK1_DIV(div) |
115                         S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2);
116         } else
117                 return -EINVAL;
118
119         clk->rate = clk_get_rate(clk->parent) / div;
120         __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data),
121                 S3C24XX_DCLKCON);
122         return clk->rate;
123 }
124 static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent)
125 {
126         unsigned long mask;
127         unsigned long source;
128
129         /* calculate the MISCCR setting for the clock */
130
131         if (parent == &clk_mpll)
132                 source = S3C2410_MISCCR_CLK0_MPLL;
133         else if (parent == &clk_upll)
134                 source = S3C2410_MISCCR_CLK0_UPLL;
135         else if (parent == &clk_f)
136                 source = S3C2410_MISCCR_CLK0_FCLK;
137         else if (parent == &clk_h)
138                 source = S3C2410_MISCCR_CLK0_HCLK;
139         else if (parent == &clk_p)
140                 source = S3C2410_MISCCR_CLK0_PCLK;
141         else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0)
142                 source = S3C2410_MISCCR_CLK0_DCLK0;
143         else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1)
144                 source = S3C2410_MISCCR_CLK0_DCLK0;
145         else
146                 return -EINVAL;
147
148         clk->parent = parent;
149
150         if (clk == &s3c24xx_clkout0)
151                 mask = S3C2410_MISCCR_CLK0_MASK;
152         else {
153                 source <<= 4;
154                 mask = S3C2410_MISCCR_CLK1_MASK;
155         }
156
157         s3c2410_modify_misccr(mask, source);
158         return 0;
159 }
160
161 /* external clock definitions */
162
163 static struct clk_ops dclk_ops = {
164         .set_parent     = s3c24xx_dclk_setparent,
165         .set_rate       = s3c24xx_set_dclk_rate,
166         .round_rate     = s3c24xx_round_dclk_rate,
167 };
168
169 struct clk s3c24xx_dclk0 = {
170         .name           = "dclk0",
171         .ctrlbit        = S3C2410_DCLKCON_DCLK0EN,
172         .enable         = s3c24xx_dclk_enable,
173         .ops            = &dclk_ops,
174 };
175
176 struct clk s3c24xx_dclk1 = {
177         .name           = "dclk1",
178         .ctrlbit        = S3C2410_DCLKCON_DCLK1EN,
179         .enable         = s3c24xx_dclk_enable,
180         .ops            = &dclk_ops,
181 };
182
183 static struct clk_ops clkout_ops = {
184         .set_parent     = s3c24xx_clkout_setparent,
185 };
186
187 struct clk s3c24xx_clkout0 = {
188         .name           = "clkout0",
189         .ops            = &clkout_ops,
190 };
191
192 struct clk s3c24xx_clkout1 = {
193         .name           = "clkout1",
194         .ops            = &clkout_ops,
195 };