aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-shmobile/clock-emev2.c
diff options
context:
space:
mode:
authorMagnus Damm <damm@opensource.se>2012-05-16 02:44:58 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2012-05-17 18:14:01 -0400
commit7f627f0380cb5ba3e05bcaac31ecf40c1f508ec1 (patch)
treec9a6738e73afdeb7aca1bef4d0114b1f3fc7428c /arch/arm/mach-shmobile/clock-emev2.c
parenta07e103ef08c6907d695a06467d7ee950796fccf (diff)
mach-shmobile: Emma Mobile EV2 SoC base support V3
This is V3 of the Emma Mobile EV2 SoC support. Included here is support for serial and timer devices which is just about enough to boot a kernel. Signed-off-by: Magnus Damm <damm@opensource.se> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'arch/arm/mach-shmobile/clock-emev2.c')
-rw-r--r--arch/arm/mach-shmobile/clock-emev2.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/arch/arm/mach-shmobile/clock-emev2.c b/arch/arm/mach-shmobile/clock-emev2.c
new file mode 100644
index 000000000000..73a121659314
--- /dev/null
+++ b/arch/arm/mach-shmobile/clock-emev2.c
@@ -0,0 +1,226 @@
1/*
2 * Emma Mobile EV2 clock framework support
3 *
4 * Copyright (C) 2012 Magnus Damm
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 as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <linux/io.h>
22#include <linux/sh_clk.h>
23#include <linux/clkdev.h>
24#include <mach/common.h>
25
26#define EMEV2_SMU_BASE 0xe0110000
27
28/* EMEV2 SMU registers */
29#define USIAU0_RSTCTRL 0x094
30#define USIBU1_RSTCTRL 0x0ac
31#define USIBU2_RSTCTRL 0x0b0
32#define USIBU3_RSTCTRL 0x0b4
33#define STI_RSTCTRL 0x124
34#define USIAU0GCLKCTRL 0x4a0
35#define USIBU1GCLKCTRL 0x4b8
36#define USIBU2GCLKCTRL 0x4bc
37#define USIBU3GCLKCTRL 0x04c0
38#define STIGCLKCTRL 0x528
39#define USIAU0SCLKDIV 0x61c
40#define USIB2SCLKDIV 0x65c
41#define USIB3SCLKDIV 0x660
42#define STI_CLKSEL 0x688
43
44/* not pretty, but hey */
45static void __iomem *smu_base;
46
47static void emev2_smu_write(unsigned long value, int offs)
48{
49 BUG_ON(!smu_base || (offs >= PAGE_SIZE));
50 iowrite32(value, smu_base + offs);
51}
52
53static struct clk_mapping smu_mapping = {
54 .phys = EMEV2_SMU_BASE,
55 .len = PAGE_SIZE,
56};
57
58/* Fixed 32 KHz root clock from C32K pin */
59static struct clk c32k_clk = {
60 .rate = 32768,
61 .mapping = &smu_mapping,
62};
63
64/* PLL3 multiplies C32K with 7000 */
65static unsigned long pll3_recalc(struct clk *clk)
66{
67 return clk->parent->rate * 7000;
68}
69
70static struct sh_clk_ops pll3_clk_ops = {
71 .recalc = pll3_recalc,
72};
73
74static struct clk pll3_clk = {
75 .ops = &pll3_clk_ops,
76 .parent = &c32k_clk,
77};
78
79static struct clk *main_clks[] = {
80 &c32k_clk,
81 &pll3_clk,
82};
83
84enum { SCLKDIV_USIAU0, SCLKDIV_USIBU2, SCLKDIV_USIBU1, SCLKDIV_USIBU3,
85 SCLKDIV_NR };
86
87#define SCLKDIV(_reg, _shift) \
88{ \
89 .parent = &pll3_clk, \
90 .enable_reg = IOMEM(EMEV2_SMU_BASE + (_reg)), \
91 .enable_bit = _shift, \
92}
93
94static struct clk sclkdiv_clks[SCLKDIV_NR] = {
95 [SCLKDIV_USIAU0] = SCLKDIV(USIAU0SCLKDIV, 0),
96 [SCLKDIV_USIBU2] = SCLKDIV(USIB2SCLKDIV, 16),
97 [SCLKDIV_USIBU1] = SCLKDIV(USIB2SCLKDIV, 0),
98 [SCLKDIV_USIBU3] = SCLKDIV(USIB3SCLKDIV, 0),
99};
100
101enum { GCLK_USIAU0_SCLK, GCLK_USIBU1_SCLK, GCLK_USIBU2_SCLK, GCLK_USIBU3_SCLK,
102 GCLK_STI_SCLK,
103 GCLK_NR };
104
105#define GCLK_SCLK(_parent, _reg) \
106{ \
107 .parent = _parent, \
108 .enable_reg = IOMEM(EMEV2_SMU_BASE + (_reg)), \
109 .enable_bit = 1, /* SCLK_GCC */ \
110}
111
112static struct clk gclk_clks[GCLK_NR] = {
113 [GCLK_USIAU0_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIAU0],
114 USIAU0GCLKCTRL),
115 [GCLK_USIBU1_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU1],
116 USIBU1GCLKCTRL),
117 [GCLK_USIBU2_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU2],
118 USIBU2GCLKCTRL),
119 [GCLK_USIBU3_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU3],
120 USIBU3GCLKCTRL),
121 [GCLK_STI_SCLK] = GCLK_SCLK(&c32k_clk, STIGCLKCTRL),
122};
123
124static int emev2_gclk_enable(struct clk *clk)
125{
126 iowrite32(ioread32(clk->mapped_reg) | (1 << clk->enable_bit),
127 clk->mapped_reg);
128 return 0;
129}
130
131static void emev2_gclk_disable(struct clk *clk)
132{
133 iowrite32(ioread32(clk->mapped_reg) & ~(1 << clk->enable_bit),
134 clk->mapped_reg);
135}
136
137static struct sh_clk_ops emev2_gclk_clk_ops = {
138 .enable = emev2_gclk_enable,
139 .disable = emev2_gclk_disable,
140 .recalc = followparent_recalc,
141};
142
143static int __init emev2_gclk_register(struct clk *clks, int nr)
144{
145 struct clk *clkp;
146 int ret = 0;
147 int k;
148
149 for (k = 0; !ret && (k < nr); k++) {
150 clkp = clks + k;
151 clkp->ops = &emev2_gclk_clk_ops;
152 ret |= clk_register(clkp);
153 }
154
155 return ret;
156}
157
158static unsigned long emev2_sclkdiv_recalc(struct clk *clk)
159{
160 unsigned int sclk_div;
161
162 sclk_div = (ioread32(clk->mapped_reg) >> clk->enable_bit) & 0xff;
163
164 return clk->parent->rate / (sclk_div + 1);
165}
166
167static struct sh_clk_ops emev2_sclkdiv_clk_ops = {
168 .recalc = emev2_sclkdiv_recalc,
169};
170
171static int __init emev2_sclkdiv_register(struct clk *clks, int nr)
172{
173 struct clk *clkp;
174 int ret = 0;
175 int k;
176
177 for (k = 0; !ret && (k < nr); k++) {
178 clkp = clks + k;
179 clkp->ops = &emev2_sclkdiv_clk_ops;
180 ret |= clk_register(clkp);
181 }
182
183 return ret;
184}
185
186static struct clk_lookup lookups[] = {
187 CLKDEV_DEV_ID("serial8250-em.0", &gclk_clks[GCLK_USIAU0_SCLK]),
188 CLKDEV_DEV_ID("serial8250-em.1", &gclk_clks[GCLK_USIBU1_SCLK]),
189 CLKDEV_DEV_ID("serial8250-em.2", &gclk_clks[GCLK_USIBU2_SCLK]),
190 CLKDEV_DEV_ID("serial8250-em.3", &gclk_clks[GCLK_USIBU3_SCLK]),
191 CLKDEV_DEV_ID("em_sti.0", &gclk_clks[GCLK_STI_SCLK]),
192};
193
194void __init emev2_clock_init(void)
195{
196 int k, ret = 0;
197
198 smu_base = ioremap(EMEV2_SMU_BASE, PAGE_SIZE);
199 BUG_ON(!smu_base);
200
201 /* setup STI timer to run on 37.768 kHz and deassert reset */
202 emev2_smu_write(0, STI_CLKSEL);
203 emev2_smu_write(1, STI_RSTCTRL);
204
205 /* deassert reset for UART0->UART3 */
206 emev2_smu_write(2, USIAU0_RSTCTRL);
207 emev2_smu_write(2, USIBU1_RSTCTRL);
208 emev2_smu_write(2, USIBU2_RSTCTRL);
209 emev2_smu_write(2, USIBU3_RSTCTRL);
210
211 for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
212 ret = clk_register(main_clks[k]);
213
214 if (!ret)
215 ret = emev2_sclkdiv_register(sclkdiv_clks, SCLKDIV_NR);
216
217 if (!ret)
218 ret = emev2_gclk_register(gclk_clks, GCLK_NR);
219
220 clkdev_add_table(lookups, ARRAY_SIZE(lookups));
221
222 if (!ret)
223 shmobile_clk_init();
224 else
225 panic("failed to setup emev2 clocks\n");
226}