diff options
Diffstat (limited to 'arch/arm/plat-mxc')
-rw-r--r-- | arch/arm/plat-mxc/Kconfig | 8 | ||||
-rw-r--r-- | arch/arm/plat-mxc/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/plat-mxc/clock.c | 331 | ||||
-rw-r--r-- | arch/arm/plat-mxc/gpio.c | 253 | ||||
-rw-r--r-- | arch/arm/plat-mxc/iomux-mx1-mx2.c | 156 | ||||
-rw-r--r-- | arch/arm/plat-mxc/irq.c | 70 | ||||
-rw-r--r-- | arch/arm/plat-mxc/time.c | 228 |
7 files changed, 1032 insertions, 18 deletions
diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig index bb6e12738fb3..e14eaad11dd5 100644 --- a/arch/arm/plat-mxc/Kconfig +++ b/arch/arm/plat-mxc/Kconfig | |||
@@ -3,9 +3,14 @@ if ARCH_MXC | |||
3 | menu "Freescale MXC Implementations" | 3 | menu "Freescale MXC Implementations" |
4 | 4 | ||
5 | choice | 5 | choice |
6 | prompt "MXC/iMX System Type" | 6 | prompt "MXC/iMX Base Type" |
7 | default ARCH_MX3 | 7 | default ARCH_MX3 |
8 | 8 | ||
9 | config ARCH_MX2 | ||
10 | bool "MX2-based" | ||
11 | help | ||
12 | This enables support for systems based on the Freescale i.MX2 family | ||
13 | |||
9 | config ARCH_MX3 | 14 | config ARCH_MX3 |
10 | bool "MX3-based" | 15 | bool "MX3-based" |
11 | help | 16 | help |
@@ -13,6 +18,7 @@ config ARCH_MX3 | |||
13 | 18 | ||
14 | endchoice | 19 | endchoice |
15 | 20 | ||
21 | source "arch/arm/mach-mx2/Kconfig" | ||
16 | source "arch/arm/mach-mx3/Kconfig" | 22 | source "arch/arm/mach-mx3/Kconfig" |
17 | 23 | ||
18 | endmenu | 24 | endmenu |
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile index f96dc0362068..db66e9ae8414 100644 --- a/arch/arm/plat-mxc/Makefile +++ b/arch/arm/plat-mxc/Makefile | |||
@@ -3,4 +3,6 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | # Common support | 5 | # Common support |
6 | obj-y := irq.o | 6 | obj-y := irq.o clock.o gpio.o time.o |
7 | |||
8 | obj-$(CONFIG_ARCH_MX2) += iomux-mx1-mx2.o | ||
diff --git a/arch/arm/plat-mxc/clock.c b/arch/arm/plat-mxc/clock.c new file mode 100644 index 000000000000..1aa86fd60d71 --- /dev/null +++ b/arch/arm/plat-mxc/clock.c | |||
@@ -0,0 +1,331 @@ | |||
1 | /* | ||
2 | * Based on arch/arm/plat-omap/clock.c | ||
3 | * | ||
4 | * Copyright (C) 2004 - 2005 Nokia corporation | ||
5 | * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> | ||
6 | * Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com> | ||
7 | * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. | ||
8 | * Copyright 2008 Juergen Beisert, kernel@pengutronix.de | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version 2 | ||
13 | * of the License, or (at your option) any later version. | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
22 | * MA 02110-1301, USA. | ||
23 | */ | ||
24 | |||
25 | /* #define DEBUG */ | ||
26 | |||
27 | #include <linux/clk.h> | ||
28 | #include <linux/err.h> | ||
29 | #include <linux/errno.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/io.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/list.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/mutex.h> | ||
36 | #include <linux/platform_device.h> | ||
37 | #include <linux/proc_fs.h> | ||
38 | #include <linux/semaphore.h> | ||
39 | #include <linux/string.h> | ||
40 | #include <linux/version.h> | ||
41 | |||
42 | #include <asm/arch/clock.h> | ||
43 | |||
44 | static LIST_HEAD(clocks); | ||
45 | static DEFINE_MUTEX(clocks_mutex); | ||
46 | |||
47 | /*------------------------------------------------------------------------- | ||
48 | * Standard clock functions defined in include/linux/clk.h | ||
49 | *-------------------------------------------------------------------------*/ | ||
50 | |||
51 | /* | ||
52 | * Retrieve a clock by name. | ||
53 | * | ||
54 | * Note that we first try to use device id on the bus | ||
55 | * and clock name. If this fails, we try to use "<name>.<id>". If this fails, | ||
56 | * we try to use clock name only. | ||
57 | * The reference count to the clock's module owner ref count is incremented. | ||
58 | */ | ||
59 | struct clk *clk_get(struct device *dev, const char *id) | ||
60 | { | ||
61 | struct clk *p, *clk = ERR_PTR(-ENOENT); | ||
62 | int idno; | ||
63 | const char *str; | ||
64 | |||
65 | if (id == NULL) | ||
66 | return clk; | ||
67 | |||
68 | if (dev == NULL || dev->bus != &platform_bus_type) | ||
69 | idno = -1; | ||
70 | else | ||
71 | idno = to_platform_device(dev)->id; | ||
72 | |||
73 | mutex_lock(&clocks_mutex); | ||
74 | |||
75 | list_for_each_entry(p, &clocks, node) { | ||
76 | if (p->id == idno && | ||
77 | strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | ||
78 | clk = p; | ||
79 | goto found; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | str = strrchr(id, '.'); | ||
84 | if (str) { | ||
85 | int cnt = str - id; | ||
86 | str++; | ||
87 | idno = simple_strtol(str, NULL, 10); | ||
88 | list_for_each_entry(p, &clocks, node) { | ||
89 | if (p->id == idno && | ||
90 | strlen(p->name) == cnt && | ||
91 | strncmp(id, p->name, cnt) == 0 && | ||
92 | try_module_get(p->owner)) { | ||
93 | clk = p; | ||
94 | goto found; | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | list_for_each_entry(p, &clocks, node) { | ||
100 | if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | ||
101 | clk = p; | ||
102 | goto found; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | printk(KERN_WARNING "clk: Unable to get requested clock: %s\n", id); | ||
107 | |||
108 | found: | ||
109 | mutex_unlock(&clocks_mutex); | ||
110 | |||
111 | return clk; | ||
112 | } | ||
113 | EXPORT_SYMBOL(clk_get); | ||
114 | |||
115 | static void __clk_disable(struct clk *clk) | ||
116 | { | ||
117 | if (clk == NULL || IS_ERR(clk)) | ||
118 | return; | ||
119 | |||
120 | __clk_disable(clk->parent); | ||
121 | __clk_disable(clk->secondary); | ||
122 | |||
123 | if (!(--clk->usecount) && clk->disable) | ||
124 | clk->disable(clk); | ||
125 | } | ||
126 | |||
127 | static int __clk_enable(struct clk *clk) | ||
128 | { | ||
129 | if (clk == NULL || IS_ERR(clk)) | ||
130 | return -EINVAL; | ||
131 | |||
132 | __clk_enable(clk->parent); | ||
133 | __clk_enable(clk->secondary); | ||
134 | |||
135 | if (clk->usecount++ == 0 && clk->enable) | ||
136 | clk->enable(clk); | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | /* This function increments the reference count on the clock and enables the | ||
142 | * clock if not already enabled. The parent clock tree is recursively enabled | ||
143 | */ | ||
144 | int clk_enable(struct clk *clk) | ||
145 | { | ||
146 | int ret = 0; | ||
147 | |||
148 | if (clk == NULL || IS_ERR(clk)) | ||
149 | return -EINVAL; | ||
150 | |||
151 | mutex_lock(&clocks_mutex); | ||
152 | ret = __clk_enable(clk); | ||
153 | mutex_unlock(&clocks_mutex); | ||
154 | |||
155 | return ret; | ||
156 | } | ||
157 | EXPORT_SYMBOL(clk_enable); | ||
158 | |||
159 | /* This function decrements the reference count on the clock and disables | ||
160 | * the clock when reference count is 0. The parent clock tree is | ||
161 | * recursively disabled | ||
162 | */ | ||
163 | void clk_disable(struct clk *clk) | ||
164 | { | ||
165 | if (clk == NULL || IS_ERR(clk)) | ||
166 | return; | ||
167 | |||
168 | mutex_lock(&clocks_mutex); | ||
169 | __clk_disable(clk); | ||
170 | mutex_unlock(&clocks_mutex); | ||
171 | } | ||
172 | EXPORT_SYMBOL(clk_disable); | ||
173 | |||
174 | /* Retrieve the *current* clock rate. If the clock itself | ||
175 | * does not provide a special calculation routine, ask | ||
176 | * its parent and so on, until one is able to return | ||
177 | * a valid clock rate | ||
178 | */ | ||
179 | unsigned long clk_get_rate(struct clk *clk) | ||
180 | { | ||
181 | if (clk == NULL || IS_ERR(clk)) | ||
182 | return 0UL; | ||
183 | |||
184 | if (clk->get_rate) | ||
185 | return clk->get_rate(clk); | ||
186 | |||
187 | return clk_get_rate(clk->parent); | ||
188 | } | ||
189 | EXPORT_SYMBOL(clk_get_rate); | ||
190 | |||
191 | /* Decrement the clock's module reference count */ | ||
192 | void clk_put(struct clk *clk) | ||
193 | { | ||
194 | if (clk && !IS_ERR(clk)) | ||
195 | module_put(clk->owner); | ||
196 | } | ||
197 | EXPORT_SYMBOL(clk_put); | ||
198 | |||
199 | /* Round the requested clock rate to the nearest supported | ||
200 | * rate that is less than or equal to the requested rate. | ||
201 | * This is dependent on the clock's current parent. | ||
202 | */ | ||
203 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
204 | { | ||
205 | if (clk == NULL || IS_ERR(clk) || !clk->round_rate) | ||
206 | return 0; | ||
207 | |||
208 | return clk->round_rate(clk, rate); | ||
209 | } | ||
210 | EXPORT_SYMBOL(clk_round_rate); | ||
211 | |||
212 | /* Set the clock to the requested clock rate. The rate must | ||
213 | * match a supported rate exactly based on what clk_round_rate returns | ||
214 | */ | ||
215 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
216 | { | ||
217 | int ret = -EINVAL; | ||
218 | |||
219 | if (clk == NULL || IS_ERR(clk) || clk->set_rate == NULL || rate == 0) | ||
220 | return ret; | ||
221 | |||
222 | mutex_lock(&clocks_mutex); | ||
223 | ret = clk->set_rate(clk, rate); | ||
224 | mutex_unlock(&clocks_mutex); | ||
225 | |||
226 | return ret; | ||
227 | } | ||
228 | EXPORT_SYMBOL(clk_set_rate); | ||
229 | |||
230 | /* Set the clock's parent to another clock source */ | ||
231 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
232 | { | ||
233 | int ret = -EINVAL; | ||
234 | |||
235 | if (clk == NULL || IS_ERR(clk) || parent == NULL || | ||
236 | IS_ERR(parent) || clk->set_parent == NULL) | ||
237 | return ret; | ||
238 | |||
239 | mutex_lock(&clocks_mutex); | ||
240 | ret = clk->set_parent(clk, parent); | ||
241 | if (ret == 0) | ||
242 | clk->parent = parent; | ||
243 | mutex_unlock(&clocks_mutex); | ||
244 | |||
245 | return ret; | ||
246 | } | ||
247 | EXPORT_SYMBOL(clk_set_parent); | ||
248 | |||
249 | /* Retrieve the clock's parent clock source */ | ||
250 | struct clk *clk_get_parent(struct clk *clk) | ||
251 | { | ||
252 | struct clk *ret = NULL; | ||
253 | |||
254 | if (clk == NULL || IS_ERR(clk)) | ||
255 | return ret; | ||
256 | |||
257 | return clk->parent; | ||
258 | } | ||
259 | EXPORT_SYMBOL(clk_get_parent); | ||
260 | |||
261 | /* | ||
262 | * Add a new clock to the clock tree. | ||
263 | */ | ||
264 | int clk_register(struct clk *clk) | ||
265 | { | ||
266 | if (clk == NULL || IS_ERR(clk)) | ||
267 | return -EINVAL; | ||
268 | |||
269 | mutex_lock(&clocks_mutex); | ||
270 | list_add(&clk->node, &clocks); | ||
271 | mutex_unlock(&clocks_mutex); | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | EXPORT_SYMBOL(clk_register); | ||
276 | |||
277 | /* Remove a clock from the clock tree */ | ||
278 | void clk_unregister(struct clk *clk) | ||
279 | { | ||
280 | if (clk == NULL || IS_ERR(clk)) | ||
281 | return; | ||
282 | |||
283 | mutex_lock(&clocks_mutex); | ||
284 | list_del(&clk->node); | ||
285 | mutex_unlock(&clocks_mutex); | ||
286 | } | ||
287 | EXPORT_SYMBOL(clk_unregister); | ||
288 | |||
289 | #ifdef CONFIG_PROC_FS | ||
290 | static int mxc_clock_read_proc(char *page, char **start, off_t off, | ||
291 | int count, int *eof, void *data) | ||
292 | { | ||
293 | struct clk *clkp; | ||
294 | char *p = page; | ||
295 | int len; | ||
296 | |||
297 | list_for_each_entry(clkp, &clocks, node) { | ||
298 | p += sprintf(p, "%s-%d:\t\t%lu, %d", clkp->name, clkp->id, | ||
299 | clk_get_rate(clkp), clkp->usecount); | ||
300 | if (clkp->parent) | ||
301 | p += sprintf(p, ", %s-%d\n", clkp->parent->name, | ||
302 | clkp->parent->id); | ||
303 | else | ||
304 | p += sprintf(p, "\n"); | ||
305 | } | ||
306 | |||
307 | len = (p - page) - off; | ||
308 | if (len < 0) | ||
309 | len = 0; | ||
310 | |||
311 | *eof = (len <= count) ? 1 : 0; | ||
312 | *start = page + off; | ||
313 | |||
314 | return len; | ||
315 | } | ||
316 | |||
317 | static int __init mxc_setup_proc_entry(void) | ||
318 | { | ||
319 | struct proc_dir_entry *res; | ||
320 | |||
321 | res = create_proc_read_entry("cpu/clocks", 0, NULL, | ||
322 | mxc_clock_read_proc, NULL); | ||
323 | if (!res) { | ||
324 | printk(KERN_ERR "Failed to create proc/cpu/clocks\n"); | ||
325 | return -ENOMEM; | ||
326 | } | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | late_initcall(mxc_setup_proc_entry); | ||
331 | #endif | ||
diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c new file mode 100644 index 000000000000..4a7736717d86 --- /dev/null +++ b/arch/arm/plat-mxc/gpio.c | |||
@@ -0,0 +1,253 @@ | |||
1 | /* | ||
2 | * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de> | ||
3 | * Copyright 2008 Juergen Beisert, kernel@pengutronix.de | ||
4 | * | ||
5 | * Based on code from Freescale, | ||
6 | * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version 2 | ||
11 | * of the License, or (at your option) any later version. | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/init.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/irq.h> | ||
25 | #include <linux/gpio.h> | ||
26 | #include <asm/hardware.h> | ||
27 | #include <asm-generic/bug.h> | ||
28 | |||
29 | static struct mxc_gpio_port *mxc_gpio_ports; | ||
30 | static int gpio_table_size; | ||
31 | |||
32 | /* Note: This driver assumes 32 GPIOs are handled in one register */ | ||
33 | |||
34 | static void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index) | ||
35 | { | ||
36 | __raw_writel(1 << index, port->base + GPIO_ISR); | ||
37 | } | ||
38 | |||
39 | static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index, | ||
40 | int enable) | ||
41 | { | ||
42 | u32 l; | ||
43 | |||
44 | l = __raw_readl(port->base + GPIO_IMR); | ||
45 | l = (l & (~(1 << index))) | (!!enable << index); | ||
46 | __raw_writel(l, port->base + GPIO_IMR); | ||
47 | } | ||
48 | |||
49 | static void gpio_ack_irq(u32 irq) | ||
50 | { | ||
51 | u32 gpio = irq_to_gpio(irq); | ||
52 | _clear_gpio_irqstatus(&mxc_gpio_ports[gpio / 32], gpio & 0x1f); | ||
53 | } | ||
54 | |||
55 | static void gpio_mask_irq(u32 irq) | ||
56 | { | ||
57 | u32 gpio = irq_to_gpio(irq); | ||
58 | _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 0); | ||
59 | } | ||
60 | |||
61 | static void gpio_unmask_irq(u32 irq) | ||
62 | { | ||
63 | u32 gpio = irq_to_gpio(irq); | ||
64 | _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1); | ||
65 | } | ||
66 | |||
67 | static int gpio_set_irq_type(u32 irq, u32 type) | ||
68 | { | ||
69 | u32 gpio = irq_to_gpio(irq); | ||
70 | struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; | ||
71 | u32 bit, val; | ||
72 | int edge; | ||
73 | void __iomem *reg = port->base; | ||
74 | |||
75 | switch (type) { | ||
76 | case IRQT_RISING: | ||
77 | edge = GPIO_INT_RISE_EDGE; | ||
78 | break; | ||
79 | case IRQT_FALLING: | ||
80 | edge = GPIO_INT_FALL_EDGE; | ||
81 | break; | ||
82 | case IRQT_LOW: | ||
83 | edge = GPIO_INT_LOW_LEV; | ||
84 | break; | ||
85 | case IRQT_HIGH: | ||
86 | edge = GPIO_INT_HIGH_LEV; | ||
87 | break; | ||
88 | default: /* this includes IRQT_BOTHEDGE */ | ||
89 | return -EINVAL; | ||
90 | } | ||
91 | |||
92 | reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ | ||
93 | bit = gpio & 0xf; | ||
94 | val = __raw_readl(reg) & ~(0x3 << (bit << 1)); | ||
95 | __raw_writel(val | (edge << (bit << 1)), reg); | ||
96 | _clear_gpio_irqstatus(port, gpio & 0x1f); | ||
97 | |||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | /* handle n interrupts in one status register */ | ||
102 | static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat) | ||
103 | { | ||
104 | u32 gpio_irq_no; | ||
105 | |||
106 | gpio_irq_no = port->virtual_irq_start; | ||
107 | for (; irq_stat != 0; irq_stat >>= 1, gpio_irq_no++) { | ||
108 | |||
109 | if ((irq_stat & 1) == 0) | ||
110 | continue; | ||
111 | |||
112 | BUG_ON(!(irq_desc[gpio_irq_no].handle_irq)); | ||
113 | irq_desc[gpio_irq_no].handle_irq(gpio_irq_no, | ||
114 | &irq_desc[gpio_irq_no]); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | #ifdef CONFIG_ARCH_MX3 | ||
119 | /* MX3 has one interrupt *per* gpio port */ | ||
120 | static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) | ||
121 | { | ||
122 | u32 irq_stat; | ||
123 | struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq); | ||
124 | |||
125 | irq_stat = __raw_readl(port->base + GPIO_ISR) & | ||
126 | __raw_readl(port->base + GPIO_IMR); | ||
127 | BUG_ON(!irq_stat); | ||
128 | mxc_gpio_irq_handler(port, irq_stat); | ||
129 | } | ||
130 | #endif | ||
131 | |||
132 | #ifdef CONFIG_ARCH_MX2 | ||
133 | /* MX2 has one interrupt *for all* gpio ports */ | ||
134 | static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc) | ||
135 | { | ||
136 | int i; | ||
137 | u32 irq_msk, irq_stat; | ||
138 | struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq); | ||
139 | |||
140 | /* walk through all interrupt status registers */ | ||
141 | for (i = 0; i < gpio_table_size; i++) { | ||
142 | irq_msk = __raw_readl(port[i].base + GPIO_IMR); | ||
143 | if (!irq_msk) | ||
144 | continue; | ||
145 | |||
146 | irq_stat = __raw_readl(port[i].base + GPIO_ISR) & irq_msk; | ||
147 | if (irq_stat) | ||
148 | mxc_gpio_irq_handler(&port[i], irq_stat); | ||
149 | } | ||
150 | } | ||
151 | #endif | ||
152 | |||
153 | static struct irq_chip gpio_irq_chip = { | ||
154 | .ack = gpio_ack_irq, | ||
155 | .mask = gpio_mask_irq, | ||
156 | .unmask = gpio_unmask_irq, | ||
157 | .set_type = gpio_set_irq_type, | ||
158 | }; | ||
159 | |||
160 | static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, | ||
161 | int dir) | ||
162 | { | ||
163 | struct mxc_gpio_port *port = | ||
164 | container_of(chip, struct mxc_gpio_port, chip); | ||
165 | u32 l; | ||
166 | |||
167 | l = __raw_readl(port->base + GPIO_GDIR); | ||
168 | if (dir) | ||
169 | l |= 1 << offset; | ||
170 | else | ||
171 | l &= ~(1 << offset); | ||
172 | __raw_writel(l, port->base + GPIO_GDIR); | ||
173 | } | ||
174 | |||
175 | static void mxc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
176 | { | ||
177 | struct mxc_gpio_port *port = | ||
178 | container_of(chip, struct mxc_gpio_port, chip); | ||
179 | void __iomem *reg = port->base + GPIO_DR; | ||
180 | u32 l; | ||
181 | |||
182 | l = (__raw_readl(reg) & (~(1 << offset))) | (value << offset); | ||
183 | __raw_writel(l, reg); | ||
184 | } | ||
185 | |||
186 | static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
187 | { | ||
188 | struct mxc_gpio_port *port = | ||
189 | container_of(chip, struct mxc_gpio_port, chip); | ||
190 | |||
191 | return (__raw_readl(port->base + GPIO_DR) >> offset) & 1; | ||
192 | } | ||
193 | |||
194 | static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | ||
195 | { | ||
196 | _set_gpio_direction(chip, offset, 0); | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static int mxc_gpio_direction_output(struct gpio_chip *chip, | ||
201 | unsigned offset, int value) | ||
202 | { | ||
203 | _set_gpio_direction(chip, offset, 1); | ||
204 | mxc_gpio_set(chip, offset, value); | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt) | ||
209 | { | ||
210 | int i, j; | ||
211 | |||
212 | /* save for local usage */ | ||
213 | mxc_gpio_ports = port; | ||
214 | gpio_table_size = cnt; | ||
215 | |||
216 | printk(KERN_INFO "MXC GPIO hardware\n"); | ||
217 | |||
218 | for (i = 0; i < cnt; i++) { | ||
219 | /* disable the interrupt and clear the status */ | ||
220 | __raw_writel(0, port[i].base + GPIO_IMR); | ||
221 | __raw_writel(~0, port[i].base + GPIO_ISR); | ||
222 | for (j = port[i].virtual_irq_start; | ||
223 | j < port[i].virtual_irq_start + 32; j++) { | ||
224 | set_irq_chip(j, &gpio_irq_chip); | ||
225 | set_irq_handler(j, handle_edge_irq); | ||
226 | set_irq_flags(j, IRQF_VALID); | ||
227 | } | ||
228 | |||
229 | /* register gpio chip */ | ||
230 | port[i].chip.direction_input = mxc_gpio_direction_input; | ||
231 | port[i].chip.direction_output = mxc_gpio_direction_output; | ||
232 | port[i].chip.get = mxc_gpio_get; | ||
233 | port[i].chip.set = mxc_gpio_set; | ||
234 | port[i].chip.base = i * 32; | ||
235 | port[i].chip.ngpio = 32; | ||
236 | |||
237 | /* its a serious configuration bug when it fails */ | ||
238 | BUG_ON( gpiochip_add(&port[i].chip) < 0 ); | ||
239 | |||
240 | #ifdef CONFIG_ARCH_MX3 | ||
241 | /* setup one handler for each entry */ | ||
242 | set_irq_chained_handler(port[i].irq, mx3_gpio_irq_handler); | ||
243 | set_irq_data(port[i].irq, &port[i]); | ||
244 | #endif | ||
245 | } | ||
246 | |||
247 | #ifdef CONFIG_ARCH_MX2 | ||
248 | /* setup one handler for all GPIO interrupts */ | ||
249 | set_irq_chained_handler(port[0].irq, mx2_gpio_irq_handler); | ||
250 | set_irq_data(port[0].irq, port); | ||
251 | #endif | ||
252 | return 0; | ||
253 | } | ||
diff --git a/arch/arm/plat-mxc/iomux-mx1-mx2.c b/arch/arm/plat-mxc/iomux-mx1-mx2.c new file mode 100644 index 000000000000..1985571eb40c --- /dev/null +++ b/arch/arm/plat-mxc/iomux-mx1-mx2.c | |||
@@ -0,0 +1,156 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-mxc/generic.c | ||
3 | * | ||
4 | * author: Sascha Hauer | ||
5 | * Created: april 20th, 2004 | ||
6 | * Copyright: Synertronixx GmbH | ||
7 | * | ||
8 | * Common code for i.MX machines | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/errno.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/string.h> | ||
31 | #include <linux/gpio.h> | ||
32 | |||
33 | #include <asm/hardware.h> | ||
34 | #include <asm/mach/map.h> | ||
35 | #include <asm/arch/iomux-mx1-mx2.h> | ||
36 | |||
37 | void mxc_gpio_mode(int gpio_mode) | ||
38 | { | ||
39 | unsigned int pin = gpio_mode & GPIO_PIN_MASK; | ||
40 | unsigned int port = (gpio_mode & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT; | ||
41 | unsigned int ocr = (gpio_mode & GPIO_OCR_MASK) >> GPIO_OCR_SHIFT; | ||
42 | unsigned int tmp; | ||
43 | |||
44 | /* Pullup enable */ | ||
45 | tmp = __raw_readl(VA_GPIO_BASE + MXC_PUEN(port)); | ||
46 | if (gpio_mode & GPIO_PUEN) | ||
47 | tmp |= (1 << pin); | ||
48 | else | ||
49 | tmp &= ~(1 << pin); | ||
50 | __raw_writel(tmp, VA_GPIO_BASE + MXC_PUEN(port)); | ||
51 | |||
52 | /* Data direction */ | ||
53 | tmp = __raw_readl(VA_GPIO_BASE + MXC_DDIR(port)); | ||
54 | if (gpio_mode & GPIO_OUT) | ||
55 | tmp |= 1 << pin; | ||
56 | else | ||
57 | tmp &= ~(1 << pin); | ||
58 | __raw_writel(tmp, VA_GPIO_BASE + MXC_DDIR(port)); | ||
59 | |||
60 | /* Primary / alternate function */ | ||
61 | tmp = __raw_readl(VA_GPIO_BASE + MXC_GPR(port)); | ||
62 | if (gpio_mode & GPIO_AF) | ||
63 | tmp |= (1 << pin); | ||
64 | else | ||
65 | tmp &= ~(1 << pin); | ||
66 | __raw_writel(tmp, VA_GPIO_BASE + MXC_GPR(port)); | ||
67 | |||
68 | /* use as gpio? */ | ||
69 | tmp = __raw_readl(VA_GPIO_BASE + MXC_GIUS(port)); | ||
70 | if (gpio_mode & (GPIO_PF | GPIO_AF)) | ||
71 | tmp &= ~(1 << pin); | ||
72 | else | ||
73 | tmp |= (1 << pin); | ||
74 | __raw_writel(tmp, VA_GPIO_BASE + MXC_GIUS(port)); | ||
75 | |||
76 | if (pin < 16) { | ||
77 | tmp = __raw_readl(VA_GPIO_BASE + MXC_OCR1(port)); | ||
78 | tmp &= ~(3 << (pin * 2)); | ||
79 | tmp |= (ocr << (pin * 2)); | ||
80 | __raw_writel(tmp, VA_GPIO_BASE + MXC_OCR1(port)); | ||
81 | |||
82 | tmp = __raw_readl(VA_GPIO_BASE + MXC_ICONFA1(port)); | ||
83 | tmp &= ~(3 << (pin * 2)); | ||
84 | tmp |= ((gpio_mode >> GPIO_AOUT_SHIFT) & 3) << (pin * 2); | ||
85 | __raw_writel(tmp, VA_GPIO_BASE + MXC_ICONFA1(port)); | ||
86 | |||
87 | tmp = __raw_readl(VA_GPIO_BASE + MXC_ICONFB1(port)); | ||
88 | tmp &= ~(3 << (pin * 2)); | ||
89 | tmp |= ((gpio_mode >> GPIO_BOUT_SHIFT) & 3) << (pin * 2); | ||
90 | __raw_writel(tmp, VA_GPIO_BASE + MXC_ICONFB1(port)); | ||
91 | } else { | ||
92 | pin -= 16; | ||
93 | |||
94 | tmp = __raw_readl(VA_GPIO_BASE + MXC_OCR2(port)); | ||
95 | tmp &= ~(3 << (pin * 2)); | ||
96 | tmp |= (ocr << (pin * 2)); | ||
97 | __raw_writel(tmp, VA_GPIO_BASE + MXC_OCR2(port)); | ||
98 | |||
99 | tmp = __raw_readl(VA_GPIO_BASE + MXC_ICONFA2(port)); | ||
100 | tmp &= ~(3 << (pin * 2)); | ||
101 | tmp |= ((gpio_mode >> GPIO_AOUT_SHIFT) & 3) << (pin * 2); | ||
102 | __raw_writel(tmp, VA_GPIO_BASE + MXC_ICONFA2(port)); | ||
103 | |||
104 | tmp = __raw_readl(VA_GPIO_BASE + MXC_ICONFB2(port)); | ||
105 | tmp &= ~(3 << (pin * 2)); | ||
106 | tmp |= ((gpio_mode >> GPIO_BOUT_SHIFT) & 3) << (pin * 2); | ||
107 | __raw_writel(tmp, VA_GPIO_BASE + MXC_ICONFB2(port)); | ||
108 | } | ||
109 | } | ||
110 | EXPORT_SYMBOL(mxc_gpio_mode); | ||
111 | |||
112 | int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count, | ||
113 | int alloc_mode, const char *label) | ||
114 | { | ||
115 | const int *p = pin_list; | ||
116 | int i; | ||
117 | unsigned gpio; | ||
118 | unsigned mode; | ||
119 | |||
120 | for (i = 0; i < count; i++) { | ||
121 | gpio = *p & (GPIO_PIN_MASK | GPIO_PORT_MASK); | ||
122 | mode = *p & ~(GPIO_PIN_MASK | GPIO_PORT_MASK); | ||
123 | |||
124 | if (gpio >= (GPIO_PORT_MAX + 1) * 32) | ||
125 | goto setup_error; | ||
126 | |||
127 | if (alloc_mode & MXC_GPIO_ALLOC_MODE_RELEASE) | ||
128 | gpio_free(gpio); | ||
129 | else if (!(alloc_mode & MXC_GPIO_ALLOC_MODE_NO_ALLOC)) | ||
130 | if (gpio_request(gpio, label) | ||
131 | && !(alloc_mode & MXC_GPIO_ALLOC_MODE_TRY_ALLOC)) | ||
132 | goto setup_error; | ||
133 | |||
134 | if (!(alloc_mode & (MXC_GPIO_ALLOC_MODE_ALLOC_ONLY | | ||
135 | MXC_GPIO_ALLOC_MODE_RELEASE))) | ||
136 | mxc_gpio_mode(gpio | mode); | ||
137 | |||
138 | p++; | ||
139 | } | ||
140 | return 0; | ||
141 | |||
142 | setup_error: | ||
143 | if (alloc_mode & (MXC_GPIO_ALLOC_MODE_NO_ALLOC | | ||
144 | MXC_GPIO_ALLOC_MODE_TRY_ALLOC)) | ||
145 | return -EINVAL; | ||
146 | |||
147 | while (p != pin_list) { | ||
148 | p--; | ||
149 | gpio = *p & (GPIO_PIN_MASK | GPIO_PORT_MASK); | ||
150 | gpio_free(gpio); | ||
151 | } | ||
152 | |||
153 | return -EINVAL; | ||
154 | } | ||
155 | EXPORT_SYMBOL(mxc_gpio_setup_multiple_pins); | ||
156 | |||
diff --git a/arch/arm/plat-mxc/irq.c b/arch/arm/plat-mxc/irq.c index 2ad5a6917b3f..1fbe01da6925 100644 --- a/arch/arm/plat-mxc/irq.c +++ b/arch/arm/plat-mxc/irq.c | |||
@@ -1,24 +1,59 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. | 2 | * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. |
3 | * Copyright 2008 Juergen Beisert, kernel@pengutronix.de | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version 2 | ||
8 | * of the License, or (at your option) any later version. | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
17 | * MA 02110-1301, USA. | ||
3 | */ | 18 | */ |
4 | 19 | ||
5 | /* | 20 | #include <linux/irq.h> |
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 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/moduleparam.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <asm/hardware.h> | ||
17 | #include <asm/io.h> | 21 | #include <asm/io.h> |
18 | #include <asm/irq.h> | ||
19 | #include <asm/mach/irq.h> | ||
20 | #include <asm/arch/common.h> | 22 | #include <asm/arch/common.h> |
21 | 23 | ||
24 | #define AVIC_BASE IO_ADDRESS(AVIC_BASE_ADDR) | ||
25 | #define AVIC_INTCNTL (AVIC_BASE + 0x00) /* int control reg */ | ||
26 | #define AVIC_NIMASK (AVIC_BASE + 0x04) /* int mask reg */ | ||
27 | #define AVIC_INTENNUM (AVIC_BASE + 0x08) /* int enable number reg */ | ||
28 | #define AVIC_INTDISNUM (AVIC_BASE + 0x0C) /* int disable number reg */ | ||
29 | #define AVIC_INTENABLEH (AVIC_BASE + 0x10) /* int enable reg high */ | ||
30 | #define AVIC_INTENABLEL (AVIC_BASE + 0x14) /* int enable reg low */ | ||
31 | #define AVIC_INTTYPEH (AVIC_BASE + 0x18) /* int type reg high */ | ||
32 | #define AVIC_INTTYPEL (AVIC_BASE + 0x1C) /* int type reg low */ | ||
33 | #define AVIC_NIPRIORITY7 (AVIC_BASE + 0x20) /* norm int priority lvl7 */ | ||
34 | #define AVIC_NIPRIORITY6 (AVIC_BASE + 0x24) /* norm int priority lvl6 */ | ||
35 | #define AVIC_NIPRIORITY5 (AVIC_BASE + 0x28) /* norm int priority lvl5 */ | ||
36 | #define AVIC_NIPRIORITY4 (AVIC_BASE + 0x2C) /* norm int priority lvl4 */ | ||
37 | #define AVIC_NIPRIORITY3 (AVIC_BASE + 0x30) /* norm int priority lvl3 */ | ||
38 | #define AVIC_NIPRIORITY2 (AVIC_BASE + 0x34) /* norm int priority lvl2 */ | ||
39 | #define AVIC_NIPRIORITY1 (AVIC_BASE + 0x38) /* norm int priority lvl1 */ | ||
40 | #define AVIC_NIPRIORITY0 (AVIC_BASE + 0x3C) /* norm int priority lvl0 */ | ||
41 | #define AVIC_NIVECSR (AVIC_BASE + 0x40) /* norm int vector/status */ | ||
42 | #define AVIC_FIVECSR (AVIC_BASE + 0x44) /* fast int vector/status */ | ||
43 | #define AVIC_INTSRCH (AVIC_BASE + 0x48) /* int source reg high */ | ||
44 | #define AVIC_INTSRCL (AVIC_BASE + 0x4C) /* int source reg low */ | ||
45 | #define AVIC_INTFRCH (AVIC_BASE + 0x50) /* int force reg high */ | ||
46 | #define AVIC_INTFRCL (AVIC_BASE + 0x54) /* int force reg low */ | ||
47 | #define AVIC_NIPNDH (AVIC_BASE + 0x58) /* norm int pending high */ | ||
48 | #define AVIC_NIPNDL (AVIC_BASE + 0x5C) /* norm int pending low */ | ||
49 | #define AVIC_FIPNDH (AVIC_BASE + 0x60) /* fast int pending high */ | ||
50 | #define AVIC_FIPNDL (AVIC_BASE + 0x64) /* fast int pending low */ | ||
51 | |||
52 | #define SYSTEM_PREV_REG IO_ADDRESS(IIM_BASE_ADDR + 0x20) | ||
53 | #define SYSTEM_SREV_REG IO_ADDRESS(IIM_BASE_ADDR + 0x24) | ||
54 | #define IIM_PROD_REV_SH 3 | ||
55 | #define IIM_PROD_REV_LEN 5 | ||
56 | |||
22 | /* Disable interrupt number "irq" in the AVIC */ | 57 | /* Disable interrupt number "irq" in the AVIC */ |
23 | static void mxc_mask_irq(unsigned int irq) | 58 | static void mxc_mask_irq(unsigned int irq) |
24 | { | 59 | { |
@@ -32,7 +67,7 @@ static void mxc_unmask_irq(unsigned int irq) | |||
32 | } | 67 | } |
33 | 68 | ||
34 | static struct irq_chip mxc_avic_chip = { | 69 | static struct irq_chip mxc_avic_chip = { |
35 | .mask_ack = mxc_mask_irq, | 70 | .ack = mxc_mask_irq, |
36 | .mask = mxc_mask_irq, | 71 | .mask = mxc_mask_irq, |
37 | .unmask = mxc_unmask_irq, | 72 | .unmask = mxc_unmask_irq, |
38 | }; | 73 | }; |
@@ -71,5 +106,8 @@ void __init mxc_init_irq(void) | |||
71 | reg |= (0xF << 28); | 106 | reg |= (0xF << 28); |
72 | __raw_writel(reg, AVIC_NIPRIORITY6); | 107 | __raw_writel(reg, AVIC_NIPRIORITY6); |
73 | 108 | ||
109 | /* init architectures chained interrupt handler */ | ||
110 | mxc_register_gpios(); | ||
111 | |||
74 | printk(KERN_INFO "MXC IRQ initialized\n"); | 112 | printk(KERN_INFO "MXC IRQ initialized\n"); |
75 | } | 113 | } |
diff --git a/arch/arm/plat-mxc/time.c b/arch/arm/plat-mxc/time.c new file mode 100644 index 000000000000..3bf86343fdf4 --- /dev/null +++ b/arch/arm/plat-mxc/time.c | |||
@@ -0,0 +1,228 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-mxc/time.c | ||
3 | * | ||
4 | * Copyright (C) 2000-2001 Deep Blue Solutions | ||
5 | * Copyright (C) 2002 Shane Nay (shane@minirl.com) | ||
6 | * Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com) | ||
7 | * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version 2 | ||
12 | * of the License, or (at your option) any later version. | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
21 | * MA 02110-1301, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/clockchips.h> | ||
27 | #include <linux/clk.h> | ||
28 | |||
29 | #include <asm/hardware.h> | ||
30 | #include <asm/mach/time.h> | ||
31 | #include <asm/arch/common.h> | ||
32 | #include <asm/arch/mxc_timer.h> | ||
33 | |||
34 | static struct clock_event_device clockevent_mxc; | ||
35 | static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_UNUSED; | ||
36 | |||
37 | /* clock source for the timer */ | ||
38 | static struct clk *timer_clk; | ||
39 | |||
40 | /* clock source */ | ||
41 | |||
42 | static cycle_t mxc_get_cycles(void) | ||
43 | { | ||
44 | return __raw_readl(TIMER_BASE + MXC_TCN); | ||
45 | } | ||
46 | |||
47 | static struct clocksource clocksource_mxc = { | ||
48 | .name = "mxc_timer1", | ||
49 | .rating = 200, | ||
50 | .read = mxc_get_cycles, | ||
51 | .mask = CLOCKSOURCE_MASK(32), | ||
52 | .shift = 20, | ||
53 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
54 | }; | ||
55 | |||
56 | static int __init mxc_clocksource_init(void) | ||
57 | { | ||
58 | unsigned int clock; | ||
59 | |||
60 | clock = clk_get_rate(timer_clk); | ||
61 | |||
62 | clocksource_mxc.mult = clocksource_hz2mult(clock, | ||
63 | clocksource_mxc.shift); | ||
64 | clocksource_register(&clocksource_mxc); | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | /* clock event */ | ||
70 | |||
71 | static int mxc_set_next_event(unsigned long evt, | ||
72 | struct clock_event_device *unused) | ||
73 | { | ||
74 | unsigned long tcmp; | ||
75 | |||
76 | tcmp = __raw_readl(TIMER_BASE + MXC_TCN) + evt; | ||
77 | __raw_writel(tcmp, TIMER_BASE + MXC_TCMP); | ||
78 | |||
79 | return (int)(tcmp - __raw_readl(TIMER_BASE + MXC_TCN)) < 0 ? | ||
80 | -ETIME : 0; | ||
81 | } | ||
82 | |||
83 | #ifdef DEBUG | ||
84 | static const char *clock_event_mode_label[] = { | ||
85 | [CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC", | ||
86 | [CLOCK_EVT_MODE_ONESHOT] = "CLOCK_EVT_MODE_ONESHOT", | ||
87 | [CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN", | ||
88 | [CLOCK_EVT_MODE_UNUSED] = "CLOCK_EVT_MODE_UNUSED" | ||
89 | }; | ||
90 | #endif /* DEBUG */ | ||
91 | |||
92 | static void mxc_set_mode(enum clock_event_mode mode, | ||
93 | struct clock_event_device *evt) | ||
94 | { | ||
95 | unsigned long flags; | ||
96 | |||
97 | /* | ||
98 | * The timer interrupt generation is disabled at least | ||
99 | * for enough time to call mxc_set_next_event() | ||
100 | */ | ||
101 | local_irq_save(flags); | ||
102 | |||
103 | /* Disable interrupt in GPT module */ | ||
104 | gpt_irq_disable(); | ||
105 | |||
106 | if (mode != clockevent_mode) { | ||
107 | /* Set event time into far-far future */ | ||
108 | __raw_writel(__raw_readl(TIMER_BASE + MXC_TCN) - 3, | ||
109 | TIMER_BASE + MXC_TCMP); | ||
110 | /* Clear pending interrupt */ | ||
111 | gpt_irq_acknowledge(); | ||
112 | } | ||
113 | |||
114 | #ifdef DEBUG | ||
115 | printk(KERN_INFO "mxc_set_mode: changing mode from %s to %s\n", | ||
116 | clock_event_mode_label[clockevent_mode], | ||
117 | clock_event_mode_label[mode]); | ||
118 | #endif /* DEBUG */ | ||
119 | |||
120 | /* Remember timer mode */ | ||
121 | clockevent_mode = mode; | ||
122 | local_irq_restore(flags); | ||
123 | |||
124 | switch (mode) { | ||
125 | case CLOCK_EVT_MODE_PERIODIC: | ||
126 | printk(KERN_ERR"mxc_set_mode: Periodic mode is not " | ||
127 | "supported for i.MX\n"); | ||
128 | break; | ||
129 | case CLOCK_EVT_MODE_ONESHOT: | ||
130 | /* | ||
131 | * Do not put overhead of interrupt enable/disable into | ||
132 | * mxc_set_next_event(), the core has about 4 minutes | ||
133 | * to call mxc_set_next_event() or shutdown clock after | ||
134 | * mode switching | ||
135 | */ | ||
136 | local_irq_save(flags); | ||
137 | gpt_irq_enable(); | ||
138 | local_irq_restore(flags); | ||
139 | break; | ||
140 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
141 | case CLOCK_EVT_MODE_UNUSED: | ||
142 | case CLOCK_EVT_MODE_RESUME: | ||
143 | /* Left event sources disabled, no more interrupts appear */ | ||
144 | break; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * IRQ handler for the timer | ||
150 | */ | ||
151 | static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id) | ||
152 | { | ||
153 | struct clock_event_device *evt = &clockevent_mxc; | ||
154 | uint32_t tstat; | ||
155 | |||
156 | tstat = __raw_readl(TIMER_BASE + MXC_TSTAT); | ||
157 | |||
158 | gpt_irq_acknowledge(); | ||
159 | |||
160 | evt->event_handler(evt); | ||
161 | |||
162 | return IRQ_HANDLED; | ||
163 | } | ||
164 | |||
165 | static struct irqaction mxc_timer_irq = { | ||
166 | .name = "i.MX Timer Tick", | ||
167 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
168 | .handler = mxc_timer_interrupt, | ||
169 | }; | ||
170 | |||
171 | static struct clock_event_device clockevent_mxc = { | ||
172 | .name = "mxc_timer1", | ||
173 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
174 | .shift = 32, | ||
175 | .set_mode = mxc_set_mode, | ||
176 | .set_next_event = mxc_set_next_event, | ||
177 | .rating = 200, | ||
178 | }; | ||
179 | |||
180 | static int __init mxc_clockevent_init(void) | ||
181 | { | ||
182 | unsigned int clock; | ||
183 | |||
184 | clock = clk_get_rate(timer_clk); | ||
185 | |||
186 | clockevent_mxc.mult = div_sc(clock, NSEC_PER_SEC, | ||
187 | clockevent_mxc.shift); | ||
188 | clockevent_mxc.max_delta_ns = | ||
189 | clockevent_delta2ns(0xfffffffe, &clockevent_mxc); | ||
190 | clockevent_mxc.min_delta_ns = | ||
191 | clockevent_delta2ns(0xff, &clockevent_mxc); | ||
192 | |||
193 | clockevent_mxc.cpumask = cpumask_of_cpu(0); | ||
194 | |||
195 | clockevents_register_device(&clockevent_mxc); | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | void __init mxc_timer_init(const char *clk_timer) | ||
201 | { | ||
202 | timer_clk = clk_get(NULL, clk_timer); | ||
203 | if (!timer_clk) { | ||
204 | printk(KERN_ERR"Cannot determine timer clock. Giving up.\n"); | ||
205 | return; | ||
206 | } | ||
207 | |||
208 | clk_enable(timer_clk); | ||
209 | |||
210 | /* | ||
211 | * Initialise to a known state (all timers off, and timing reset) | ||
212 | */ | ||
213 | __raw_writel(0, TIMER_BASE + MXC_TCTL); | ||
214 | __raw_writel(0, TIMER_BASE + MXC_TPRER); /* see datasheet note */ | ||
215 | |||
216 | __raw_writel(TCTL_FRR | /* free running */ | ||
217 | TCTL_VAL | /* set clocksource and arch specific bits */ | ||
218 | TCTL_TEN, /* start the timer */ | ||
219 | TIMER_BASE + MXC_TCTL); | ||
220 | |||
221 | /* init and register the timer to the framework */ | ||
222 | mxc_clocksource_init(); | ||
223 | mxc_clockevent_init(); | ||
224 | |||
225 | /* Make irqs happen */ | ||
226 | setup_irq(TIMER_INTERRUPT, &mxc_timer_irq); | ||
227 | } | ||
228 | |||