diff options
Diffstat (limited to 'arch/arm/plat-nomadik')
-rw-r--r-- | arch/arm/plat-nomadik/Kconfig | 5 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/gpio.c | 434 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/include/plat/gpio.h | 70 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/timer.c | 145 |
5 files changed, 596 insertions, 59 deletions
diff --git a/arch/arm/plat-nomadik/Kconfig b/arch/arm/plat-nomadik/Kconfig index 159daf583f85..5da3f97c537b 100644 --- a/arch/arm/plat-nomadik/Kconfig +++ b/arch/arm/plat-nomadik/Kconfig | |||
@@ -19,4 +19,9 @@ config HAS_MTU | |||
19 | to multiple interrupt generating programmable | 19 | to multiple interrupt generating programmable |
20 | 32-bit free running decrementing counters. | 20 | 32-bit free running decrementing counters. |
21 | 21 | ||
22 | config NOMADIK_GPIO | ||
23 | bool | ||
24 | help | ||
25 | Support for the Nomadik GPIO controller. | ||
26 | |||
22 | endif | 27 | endif |
diff --git a/arch/arm/plat-nomadik/Makefile b/arch/arm/plat-nomadik/Makefile index 37c7cdd0f8f0..c33547361bd7 100644 --- a/arch/arm/plat-nomadik/Makefile +++ b/arch/arm/plat-nomadik/Makefile | |||
@@ -3,3 +3,4 @@ | |||
3 | # Licensed under GPLv2 | 3 | # Licensed under GPLv2 |
4 | 4 | ||
5 | obj-$(CONFIG_HAS_MTU) += timer.o | 5 | obj-$(CONFIG_HAS_MTU) += timer.o |
6 | obj-$(CONFIG_NOMADIK_GPIO) += gpio.o | ||
diff --git a/arch/arm/plat-nomadik/gpio.c b/arch/arm/plat-nomadik/gpio.c new file mode 100644 index 000000000000..d28900cfa541 --- /dev/null +++ b/arch/arm/plat-nomadik/gpio.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * Generic GPIO driver for logic cells found in the Nomadik SoC | ||
3 | * | ||
4 | * Copyright (C) 2008,2009 STMicroelectronics | ||
5 | * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it> | ||
6 | * Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/gpio.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/irq.h> | ||
24 | |||
25 | #include <mach/hardware.h> | ||
26 | #include <mach/gpio.h> | ||
27 | |||
28 | /* | ||
29 | * The GPIO module in the Nomadik family of Systems-on-Chip is an | ||
30 | * AMBA device, managing 32 pins and alternate functions. The logic block | ||
31 | * is currently only used in the Nomadik. | ||
32 | * | ||
33 | * Symbols in this file are called "nmk_gpio" for "nomadik gpio" | ||
34 | */ | ||
35 | |||
36 | #define NMK_GPIO_PER_CHIP 32 | ||
37 | struct nmk_gpio_chip { | ||
38 | struct gpio_chip chip; | ||
39 | void __iomem *addr; | ||
40 | struct clk *clk; | ||
41 | unsigned int parent_irq; | ||
42 | spinlock_t lock; | ||
43 | /* Keep track of configured edges */ | ||
44 | u32 edge_rising; | ||
45 | u32 edge_falling; | ||
46 | }; | ||
47 | |||
48 | /* Mode functions */ | ||
49 | int nmk_gpio_set_mode(int gpio, int gpio_mode) | ||
50 | { | ||
51 | struct nmk_gpio_chip *nmk_chip; | ||
52 | unsigned long flags; | ||
53 | u32 afunc, bfunc, bit; | ||
54 | |||
55 | nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); | ||
56 | if (!nmk_chip) | ||
57 | return -EINVAL; | ||
58 | |||
59 | bit = 1 << (gpio - nmk_chip->chip.base); | ||
60 | |||
61 | spin_lock_irqsave(&nmk_chip->lock, flags); | ||
62 | afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & ~bit; | ||
63 | bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & ~bit; | ||
64 | if (gpio_mode & NMK_GPIO_ALT_A) | ||
65 | afunc |= bit; | ||
66 | if (gpio_mode & NMK_GPIO_ALT_B) | ||
67 | bfunc |= bit; | ||
68 | writel(afunc, nmk_chip->addr + NMK_GPIO_AFSLA); | ||
69 | writel(bfunc, nmk_chip->addr + NMK_GPIO_AFSLB); | ||
70 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | EXPORT_SYMBOL(nmk_gpio_set_mode); | ||
75 | |||
76 | int nmk_gpio_get_mode(int gpio) | ||
77 | { | ||
78 | struct nmk_gpio_chip *nmk_chip; | ||
79 | u32 afunc, bfunc, bit; | ||
80 | |||
81 | nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); | ||
82 | if (!nmk_chip) | ||
83 | return -EINVAL; | ||
84 | |||
85 | bit = 1 << (gpio - nmk_chip->chip.base); | ||
86 | |||
87 | afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit; | ||
88 | bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit; | ||
89 | |||
90 | return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0); | ||
91 | } | ||
92 | EXPORT_SYMBOL(nmk_gpio_get_mode); | ||
93 | |||
94 | |||
95 | /* IRQ functions */ | ||
96 | static inline int nmk_gpio_get_bitmask(int gpio) | ||
97 | { | ||
98 | return 1 << (gpio % 32); | ||
99 | } | ||
100 | |||
101 | static void nmk_gpio_irq_ack(unsigned int irq) | ||
102 | { | ||
103 | int gpio; | ||
104 | struct nmk_gpio_chip *nmk_chip; | ||
105 | |||
106 | gpio = NOMADIK_IRQ_TO_GPIO(irq); | ||
107 | nmk_chip = get_irq_chip_data(irq); | ||
108 | if (!nmk_chip) | ||
109 | return; | ||
110 | writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC); | ||
111 | } | ||
112 | |||
113 | static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip, | ||
114 | int gpio, bool enable) | ||
115 | { | ||
116 | u32 bitmask = nmk_gpio_get_bitmask(gpio); | ||
117 | u32 reg; | ||
118 | |||
119 | /* we must individually set/clear the two edges */ | ||
120 | if (nmk_chip->edge_rising & bitmask) { | ||
121 | reg = readl(nmk_chip->addr + NMK_GPIO_RIMSC); | ||
122 | if (enable) | ||
123 | reg |= bitmask; | ||
124 | else | ||
125 | reg &= ~bitmask; | ||
126 | writel(reg, nmk_chip->addr + NMK_GPIO_RIMSC); | ||
127 | } | ||
128 | if (nmk_chip->edge_falling & bitmask) { | ||
129 | reg = readl(nmk_chip->addr + NMK_GPIO_FIMSC); | ||
130 | if (enable) | ||
131 | reg |= bitmask; | ||
132 | else | ||
133 | reg &= ~bitmask; | ||
134 | writel(reg, nmk_chip->addr + NMK_GPIO_FIMSC); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | static void nmk_gpio_irq_modify(unsigned int irq, bool enable) | ||
139 | { | ||
140 | int gpio; | ||
141 | struct nmk_gpio_chip *nmk_chip; | ||
142 | unsigned long flags; | ||
143 | u32 bitmask; | ||
144 | |||
145 | gpio = NOMADIK_IRQ_TO_GPIO(irq); | ||
146 | nmk_chip = get_irq_chip_data(irq); | ||
147 | bitmask = nmk_gpio_get_bitmask(gpio); | ||
148 | if (!nmk_chip) | ||
149 | return; | ||
150 | |||
151 | spin_lock_irqsave(&nmk_chip->lock, flags); | ||
152 | __nmk_gpio_irq_modify(nmk_chip, gpio, enable); | ||
153 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | ||
154 | } | ||
155 | |||
156 | static void nmk_gpio_irq_mask(unsigned int irq) | ||
157 | { | ||
158 | nmk_gpio_irq_modify(irq, false); | ||
159 | }; | ||
160 | |||
161 | static void nmk_gpio_irq_unmask(unsigned int irq) | ||
162 | { | ||
163 | nmk_gpio_irq_modify(irq, true); | ||
164 | } | ||
165 | |||
166 | static int nmk_gpio_irq_set_type(unsigned int irq, unsigned int type) | ||
167 | { | ||
168 | bool enabled = !(irq_to_desc(irq)->status & IRQ_DISABLED); | ||
169 | int gpio; | ||
170 | struct nmk_gpio_chip *nmk_chip; | ||
171 | unsigned long flags; | ||
172 | u32 bitmask; | ||
173 | |||
174 | gpio = NOMADIK_IRQ_TO_GPIO(irq); | ||
175 | nmk_chip = get_irq_chip_data(irq); | ||
176 | bitmask = nmk_gpio_get_bitmask(gpio); | ||
177 | if (!nmk_chip) | ||
178 | return -EINVAL; | ||
179 | |||
180 | if (type & IRQ_TYPE_LEVEL_HIGH) | ||
181 | return -EINVAL; | ||
182 | if (type & IRQ_TYPE_LEVEL_LOW) | ||
183 | return -EINVAL; | ||
184 | |||
185 | spin_lock_irqsave(&nmk_chip->lock, flags); | ||
186 | |||
187 | if (enabled) | ||
188 | __nmk_gpio_irq_modify(nmk_chip, gpio, false); | ||
189 | |||
190 | nmk_chip->edge_rising &= ~bitmask; | ||
191 | if (type & IRQ_TYPE_EDGE_RISING) | ||
192 | nmk_chip->edge_rising |= bitmask; | ||
193 | |||
194 | nmk_chip->edge_falling &= ~bitmask; | ||
195 | if (type & IRQ_TYPE_EDGE_FALLING) | ||
196 | nmk_chip->edge_falling |= bitmask; | ||
197 | |||
198 | if (enabled) | ||
199 | __nmk_gpio_irq_modify(nmk_chip, gpio, true); | ||
200 | |||
201 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static struct irq_chip nmk_gpio_irq_chip = { | ||
207 | .name = "Nomadik-GPIO", | ||
208 | .ack = nmk_gpio_irq_ack, | ||
209 | .mask = nmk_gpio_irq_mask, | ||
210 | .unmask = nmk_gpio_irq_unmask, | ||
211 | .set_type = nmk_gpio_irq_set_type, | ||
212 | }; | ||
213 | |||
214 | static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) | ||
215 | { | ||
216 | struct nmk_gpio_chip *nmk_chip; | ||
217 | struct irq_chip *host_chip = get_irq_chip(irq); | ||
218 | unsigned int gpio_irq; | ||
219 | u32 pending; | ||
220 | unsigned int first_irq; | ||
221 | |||
222 | if (host_chip->mask_ack) | ||
223 | host_chip->mask_ack(irq); | ||
224 | else { | ||
225 | host_chip->mask(irq); | ||
226 | if (host_chip->ack) | ||
227 | host_chip->ack(irq); | ||
228 | } | ||
229 | |||
230 | nmk_chip = get_irq_data(irq); | ||
231 | first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); | ||
232 | while ( (pending = readl(nmk_chip->addr + NMK_GPIO_IS)) ) { | ||
233 | gpio_irq = first_irq + __ffs(pending); | ||
234 | generic_handle_irq(gpio_irq); | ||
235 | } | ||
236 | |||
237 | host_chip->unmask(irq); | ||
238 | } | ||
239 | |||
240 | static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip) | ||
241 | { | ||
242 | unsigned int first_irq; | ||
243 | int i; | ||
244 | |||
245 | first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); | ||
246 | for (i = first_irq; i < first_irq + NMK_GPIO_PER_CHIP; i++) { | ||
247 | set_irq_chip(i, &nmk_gpio_irq_chip); | ||
248 | set_irq_handler(i, handle_edge_irq); | ||
249 | set_irq_flags(i, IRQF_VALID); | ||
250 | set_irq_chip_data(i, nmk_chip); | ||
251 | set_irq_type(i, IRQ_TYPE_EDGE_FALLING); | ||
252 | } | ||
253 | set_irq_chained_handler(nmk_chip->parent_irq, nmk_gpio_irq_handler); | ||
254 | set_irq_data(nmk_chip->parent_irq, nmk_chip); | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | /* I/O Functions */ | ||
259 | static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset) | ||
260 | { | ||
261 | struct nmk_gpio_chip *nmk_chip = | ||
262 | container_of(chip, struct nmk_gpio_chip, chip); | ||
263 | |||
264 | writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC); | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, | ||
269 | int val) | ||
270 | { | ||
271 | struct nmk_gpio_chip *nmk_chip = | ||
272 | container_of(chip, struct nmk_gpio_chip, chip); | ||
273 | |||
274 | writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRS); | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned offset) | ||
279 | { | ||
280 | struct nmk_gpio_chip *nmk_chip = | ||
281 | container_of(chip, struct nmk_gpio_chip, chip); | ||
282 | u32 bit = 1 << offset; | ||
283 | |||
284 | return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0; | ||
285 | } | ||
286 | |||
287 | static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset, | ||
288 | int val) | ||
289 | { | ||
290 | struct nmk_gpio_chip *nmk_chip = | ||
291 | container_of(chip, struct nmk_gpio_chip, chip); | ||
292 | u32 bit = 1 << offset; | ||
293 | |||
294 | if (val) | ||
295 | writel(bit, nmk_chip->addr + NMK_GPIO_DATS); | ||
296 | else | ||
297 | writel(bit, nmk_chip->addr + NMK_GPIO_DATC); | ||
298 | } | ||
299 | |||
300 | /* This structure is replicated for each GPIO block allocated at probe time */ | ||
301 | static struct gpio_chip nmk_gpio_template = { | ||
302 | .direction_input = nmk_gpio_make_input, | ||
303 | .get = nmk_gpio_get_input, | ||
304 | .direction_output = nmk_gpio_make_output, | ||
305 | .set = nmk_gpio_set_output, | ||
306 | .ngpio = NMK_GPIO_PER_CHIP, | ||
307 | .can_sleep = 0, | ||
308 | }; | ||
309 | |||
310 | static int __init nmk_gpio_probe(struct platform_device *dev) | ||
311 | { | ||
312 | struct nmk_gpio_platform_data *pdata = dev->dev.platform_data; | ||
313 | struct nmk_gpio_chip *nmk_chip; | ||
314 | struct gpio_chip *chip; | ||
315 | struct resource *res; | ||
316 | struct clk *clk; | ||
317 | int irq; | ||
318 | int ret; | ||
319 | |||
320 | if (!pdata) | ||
321 | return -ENODEV; | ||
322 | |||
323 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
324 | if (!res) { | ||
325 | ret = -ENOENT; | ||
326 | goto out; | ||
327 | } | ||
328 | |||
329 | irq = platform_get_irq(dev, 0); | ||
330 | if (irq < 0) { | ||
331 | ret = irq; | ||
332 | goto out; | ||
333 | } | ||
334 | |||
335 | if (request_mem_region(res->start, resource_size(res), | ||
336 | dev_name(&dev->dev)) == NULL) { | ||
337 | ret = -EBUSY; | ||
338 | goto out; | ||
339 | } | ||
340 | |||
341 | clk = clk_get(&dev->dev, NULL); | ||
342 | if (IS_ERR(clk)) { | ||
343 | ret = PTR_ERR(clk); | ||
344 | goto out_release; | ||
345 | } | ||
346 | |||
347 | clk_enable(clk); | ||
348 | |||
349 | nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL); | ||
350 | if (!nmk_chip) { | ||
351 | ret = -ENOMEM; | ||
352 | goto out_clk; | ||
353 | } | ||
354 | /* | ||
355 | * The virt address in nmk_chip->addr is in the nomadik register space, | ||
356 | * so we can simply convert the resource address, without remapping | ||
357 | */ | ||
358 | nmk_chip->clk = clk; | ||
359 | nmk_chip->addr = io_p2v(res->start); | ||
360 | nmk_chip->chip = nmk_gpio_template; | ||
361 | nmk_chip->parent_irq = irq; | ||
362 | spin_lock_init(&nmk_chip->lock); | ||
363 | |||
364 | chip = &nmk_chip->chip; | ||
365 | chip->base = pdata->first_gpio; | ||
366 | chip->label = pdata->name; | ||
367 | chip->dev = &dev->dev; | ||
368 | chip->owner = THIS_MODULE; | ||
369 | |||
370 | ret = gpiochip_add(&nmk_chip->chip); | ||
371 | if (ret) | ||
372 | goto out_free; | ||
373 | |||
374 | platform_set_drvdata(dev, nmk_chip); | ||
375 | |||
376 | nmk_gpio_init_irq(nmk_chip); | ||
377 | |||
378 | dev_info(&dev->dev, "Bits %i-%i at address %p\n", | ||
379 | nmk_chip->chip.base, nmk_chip->chip.base+31, nmk_chip->addr); | ||
380 | return 0; | ||
381 | |||
382 | out_free: | ||
383 | kfree(nmk_chip); | ||
384 | out_clk: | ||
385 | clk_disable(clk); | ||
386 | clk_put(clk); | ||
387 | out_release: | ||
388 | release_mem_region(res->start, resource_size(res)); | ||
389 | out: | ||
390 | dev_err(&dev->dev, "Failure %i for GPIO %i-%i\n", ret, | ||
391 | pdata->first_gpio, pdata->first_gpio+31); | ||
392 | return ret; | ||
393 | } | ||
394 | |||
395 | static int __exit nmk_gpio_remove(struct platform_device *dev) | ||
396 | { | ||
397 | struct nmk_gpio_chip *nmk_chip; | ||
398 | struct resource *res; | ||
399 | |||
400 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
401 | |||
402 | nmk_chip = platform_get_drvdata(dev); | ||
403 | gpiochip_remove(&nmk_chip->chip); | ||
404 | clk_disable(nmk_chip->clk); | ||
405 | clk_put(nmk_chip->clk); | ||
406 | kfree(nmk_chip); | ||
407 | release_mem_region(res->start, resource_size(res)); | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | |||
412 | static struct platform_driver nmk_gpio_driver = { | ||
413 | .driver = { | ||
414 | .owner = THIS_MODULE, | ||
415 | .name = "gpio", | ||
416 | }, | ||
417 | .probe = nmk_gpio_probe, | ||
418 | .remove = __exit_p(nmk_gpio_remove), | ||
419 | .suspend = NULL, /* to be done */ | ||
420 | .resume = NULL, | ||
421 | }; | ||
422 | |||
423 | static int __init nmk_gpio_init(void) | ||
424 | { | ||
425 | return platform_driver_register(&nmk_gpio_driver); | ||
426 | } | ||
427 | |||
428 | arch_initcall(nmk_gpio_init); | ||
429 | |||
430 | MODULE_AUTHOR("Prafulla WADASKAR and Alessandro Rubini"); | ||
431 | MODULE_DESCRIPTION("Nomadik GPIO Driver"); | ||
432 | MODULE_LICENSE("GPL"); | ||
433 | |||
434 | |||
diff --git a/arch/arm/plat-nomadik/include/plat/gpio.h b/arch/arm/plat-nomadik/include/plat/gpio.h new file mode 100644 index 000000000000..4200811249ca --- /dev/null +++ b/arch/arm/plat-nomadik/include/plat/gpio.h | |||
@@ -0,0 +1,70 @@ | |||
1 | /* | ||
2 | * Structures and registers for GPIO access in the Nomadik SoC | ||
3 | * | ||
4 | * Copyright (C) 2008 STMicroelectronics | ||
5 | * Author: Prafulla WADASKAR <prafulla.wadaskar@st.com> | ||
6 | * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #ifndef __ASM_PLAT_GPIO_H | ||
13 | #define __ASM_PLAT_GPIO_H | ||
14 | |||
15 | #include <asm-generic/gpio.h> | ||
16 | |||
17 | /* | ||
18 | * These currently cause a function call to happen, they may be optimized | ||
19 | * if needed by adding cpu-specific defines to identify blocks | ||
20 | * (see mach-pxa/include/mach/gpio.h as an example using GPLR etc) | ||
21 | */ | ||
22 | #define gpio_get_value __gpio_get_value | ||
23 | #define gpio_set_value __gpio_set_value | ||
24 | #define gpio_cansleep __gpio_cansleep | ||
25 | #define gpio_to_irq __gpio_to_irq | ||
26 | |||
27 | /* | ||
28 | * "nmk_gpio" and "NMK_GPIO" stand for "Nomadik GPIO", leaving | ||
29 | * the "gpio" namespace for generic and cross-machine functions | ||
30 | */ | ||
31 | |||
32 | /* Register in the logic block */ | ||
33 | #define NMK_GPIO_DAT 0x00 | ||
34 | #define NMK_GPIO_DATS 0x04 | ||
35 | #define NMK_GPIO_DATC 0x08 | ||
36 | #define NMK_GPIO_PDIS 0x0c | ||
37 | #define NMK_GPIO_DIR 0x10 | ||
38 | #define NMK_GPIO_DIRS 0x14 | ||
39 | #define NMK_GPIO_DIRC 0x18 | ||
40 | #define NMK_GPIO_SLPC 0x1c | ||
41 | #define NMK_GPIO_AFSLA 0x20 | ||
42 | #define NMK_GPIO_AFSLB 0x24 | ||
43 | |||
44 | #define NMK_GPIO_RIMSC 0x40 | ||
45 | #define NMK_GPIO_FIMSC 0x44 | ||
46 | #define NMK_GPIO_IS 0x48 | ||
47 | #define NMK_GPIO_IC 0x4c | ||
48 | #define NMK_GPIO_RWIMSC 0x50 | ||
49 | #define NMK_GPIO_FWIMSC 0x54 | ||
50 | #define NMK_GPIO_WKS 0x58 | ||
51 | |||
52 | /* Alternate functions: function C is set in hw by setting both A and B */ | ||
53 | #define NMK_GPIO_ALT_GPIO 0 | ||
54 | #define NMK_GPIO_ALT_A 1 | ||
55 | #define NMK_GPIO_ALT_B 2 | ||
56 | #define NMK_GPIO_ALT_C (NMK_GPIO_ALT_A | NMK_GPIO_ALT_B) | ||
57 | |||
58 | extern int nmk_gpio_set_mode(int gpio, int gpio_mode); | ||
59 | extern int nmk_gpio_get_mode(int gpio); | ||
60 | |||
61 | /* | ||
62 | * Platform data to register a block: only the initial gpio/irq number. | ||
63 | */ | ||
64 | struct nmk_gpio_platform_data { | ||
65 | char *name; | ||
66 | int first_gpio; | ||
67 | int first_irq; | ||
68 | }; | ||
69 | |||
70 | #endif /* __ASM_PLAT_GPIO_H */ | ||
diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c index fa7cb3a57cbf..0ff3798769ab 100644 --- a/arch/arm/plat-nomadik/timer.c +++ b/arch/arm/plat-nomadik/timer.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * linux/arch/arm/mach-nomadik/timer.c | 2 | * linux/arch/arm/mach-nomadik/timer.c |
3 | * | 3 | * |
4 | * Copyright (C) 2008 STMicroelectronics | 4 | * Copyright (C) 2008 STMicroelectronics |
5 | * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x | 5 | * Copyright (C) 2010 Alessandro Rubini |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2, as | 8 | * it under the terms of the GNU General Public License version 2, as |
@@ -18,123 +18,150 @@ | |||
18 | 18 | ||
19 | #include <plat/mtu.h> | 19 | #include <plat/mtu.h> |
20 | 20 | ||
21 | static u32 nmdk_count; /* accumulated count */ | 21 | void __iomem *mtu_base; /* ssigned by machine code */ |
22 | static u32 nmdk_cycle; /* write-once */ | ||
23 | |||
24 | /* setup by the platform code */ | ||
25 | void __iomem *mtu_base; | ||
26 | 22 | ||
27 | /* | 23 | /* |
28 | * clocksource: the MTU device is a decrementing counters, so we negate | 24 | * Kernel assumes that sched_clock can be called early |
29 | * the value being read. | 25 | * but the MTU may not yet be initialized. |
30 | */ | 26 | */ |
31 | static cycle_t nmdk_read_timer(struct clocksource *cs) | 27 | static cycle_t nmdk_read_timer_dummy(struct clocksource *cs) |
32 | { | 28 | { |
33 | u32 count = readl(mtu_base + MTU_VAL(0)); | 29 | return 0; |
34 | return nmdk_count + nmdk_cycle - count; | 30 | } |
35 | 31 | ||
32 | /* clocksource: MTU decrements, so we negate the value being read. */ | ||
33 | static cycle_t nmdk_read_timer(struct clocksource *cs) | ||
34 | { | ||
35 | return -readl(mtu_base + MTU_VAL(0)); | ||
36 | } | 36 | } |
37 | 37 | ||
38 | static struct clocksource nmdk_clksrc = { | 38 | static struct clocksource nmdk_clksrc = { |
39 | .name = "mtu_0", | 39 | .name = "mtu_0", |
40 | .rating = 120, | 40 | .rating = 200, |
41 | .read = nmdk_read_timer, | 41 | .read = nmdk_read_timer_dummy, |
42 | .mask = CLOCKSOURCE_MASK(32), | ||
42 | .shift = 20, | 43 | .shift = 20, |
43 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 44 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
44 | }; | 45 | }; |
45 | 46 | ||
46 | /* | 47 | /* |
47 | * Clockevent device: currently only periodic mode is supported | 48 | * Override the global weak sched_clock symbol with this |
49 | * local implementation which uses the clocksource to get some | ||
50 | * better resolution when scheduling the kernel. We accept that | ||
51 | * this wraps around for now, since it is just a relative time | ||
52 | * stamp. (Inspired by OMAP implementation.) | ||
48 | */ | 53 | */ |
54 | unsigned long long notrace sched_clock(void) | ||
55 | { | ||
56 | return clocksource_cyc2ns(nmdk_clksrc.read( | ||
57 | &nmdk_clksrc), | ||
58 | nmdk_clksrc.mult, | ||
59 | nmdk_clksrc.shift); | ||
60 | } | ||
61 | |||
62 | /* Clockevent device: use one-shot mode */ | ||
49 | static void nmdk_clkevt_mode(enum clock_event_mode mode, | 63 | static void nmdk_clkevt_mode(enum clock_event_mode mode, |
50 | struct clock_event_device *dev) | 64 | struct clock_event_device *dev) |
51 | { | 65 | { |
66 | u32 cr; | ||
67 | |||
52 | switch (mode) { | 68 | switch (mode) { |
53 | case CLOCK_EVT_MODE_PERIODIC: | 69 | case CLOCK_EVT_MODE_PERIODIC: |
54 | /* count current value? */ | 70 | pr_err("%s: periodic mode not supported\n", __func__); |
55 | writel(readl(mtu_base + MTU_IMSC) | 1, mtu_base + MTU_IMSC); | ||
56 | break; | 71 | break; |
57 | case CLOCK_EVT_MODE_ONESHOT: | 72 | case CLOCK_EVT_MODE_ONESHOT: |
58 | BUG(); /* Not supported, yet */ | 73 | /* Load highest value, enable device, enable interrupts */ |
59 | /* FALLTHROUGH */ | 74 | cr = readl(mtu_base + MTU_CR(1)); |
75 | writel(0, mtu_base + MTU_LR(1)); | ||
76 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(1)); | ||
77 | writel(0x2, mtu_base + MTU_IMSC); | ||
78 | break; | ||
60 | case CLOCK_EVT_MODE_SHUTDOWN: | 79 | case CLOCK_EVT_MODE_SHUTDOWN: |
61 | case CLOCK_EVT_MODE_UNUSED: | 80 | case CLOCK_EVT_MODE_UNUSED: |
62 | writel(readl(mtu_base + MTU_IMSC) & ~1, mtu_base + MTU_IMSC); | 81 | /* disable irq */ |
82 | writel(0, mtu_base + MTU_IMSC); | ||
63 | break; | 83 | break; |
64 | case CLOCK_EVT_MODE_RESUME: | 84 | case CLOCK_EVT_MODE_RESUME: |
65 | break; | 85 | break; |
66 | } | 86 | } |
67 | } | 87 | } |
68 | 88 | ||
89 | static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev) | ||
90 | { | ||
91 | /* writing the value has immediate effect */ | ||
92 | writel(evt, mtu_base + MTU_LR(1)); | ||
93 | return 0; | ||
94 | } | ||
95 | |||
69 | static struct clock_event_device nmdk_clkevt = { | 96 | static struct clock_event_device nmdk_clkevt = { |
70 | .name = "mtu_0", | 97 | .name = "mtu_1", |
71 | .features = CLOCK_EVT_FEAT_PERIODIC, | 98 | .features = CLOCK_EVT_FEAT_ONESHOT, |
72 | .shift = 32, | 99 | .shift = 32, |
73 | .rating = 100, | 100 | .rating = 200, |
74 | .set_mode = nmdk_clkevt_mode, | 101 | .set_mode = nmdk_clkevt_mode, |
102 | .set_next_event = nmdk_clkevt_next, | ||
75 | }; | 103 | }; |
76 | 104 | ||
77 | /* | 105 | /* |
78 | * IRQ Handler for the timer 0 of the MTU block. The irq is not shared | 106 | * IRQ Handler for timer 1 of the MTU block. |
79 | * as we are the only users of mtu0 by now. | ||
80 | */ | 107 | */ |
81 | static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id) | 108 | static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id) |
82 | { | 109 | { |
83 | /* ack: "interrupt clear register" */ | 110 | struct clock_event_device *evdev = dev_id; |
84 | writel(1 << 0, mtu_base + MTU_ICR); | ||
85 | |||
86 | /* we can't count lost ticks, unfortunately */ | ||
87 | nmdk_count += nmdk_cycle; | ||
88 | nmdk_clkevt.event_handler(&nmdk_clkevt); | ||
89 | 111 | ||
112 | writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */ | ||
113 | evdev->event_handler(evdev); | ||
90 | return IRQ_HANDLED; | 114 | return IRQ_HANDLED; |
91 | } | 115 | } |
92 | 116 | ||
93 | /* | ||
94 | * Set up timer interrupt, and return the current time in seconds. | ||
95 | */ | ||
96 | static struct irqaction nmdk_timer_irq = { | 117 | static struct irqaction nmdk_timer_irq = { |
97 | .name = "Nomadik Timer Tick", | 118 | .name = "Nomadik Timer Tick", |
98 | .flags = IRQF_DISABLED | IRQF_TIMER, | 119 | .flags = IRQF_DISABLED | IRQF_TIMER, |
99 | .handler = nmdk_timer_interrupt, | 120 | .handler = nmdk_timer_interrupt, |
121 | .dev_id = &nmdk_clkevt, | ||
100 | }; | 122 | }; |
101 | 123 | ||
102 | static void nmdk_timer_reset(void) | ||
103 | { | ||
104 | u32 cr; | ||
105 | |||
106 | writel(0, mtu_base + MTU_CR(0)); /* off */ | ||
107 | |||
108 | /* configure load and background-load, and fire it up */ | ||
109 | writel(nmdk_cycle, mtu_base + MTU_LR(0)); | ||
110 | writel(nmdk_cycle, mtu_base + MTU_BGLR(0)); | ||
111 | cr = MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS; | ||
112 | writel(cr, mtu_base + MTU_CR(0)); | ||
113 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0)); | ||
114 | } | ||
115 | |||
116 | void __init nmdk_timer_init(void) | 124 | void __init nmdk_timer_init(void) |
117 | { | 125 | { |
118 | unsigned long rate; | 126 | unsigned long rate; |
119 | int bits; | 127 | u32 cr = MTU_CRn_32BITS;; |
120 | 128 | ||
121 | rate = CLOCK_TICK_RATE; /* 2.4MHz */ | 129 | /* |
122 | nmdk_cycle = (rate + HZ/2) / HZ; | 130 | * Tick rate is 2.4MHz for Nomadik and 110MHz for ux500: |
131 | * use a divide-by-16 counter if it's more than 16MHz | ||
132 | */ | ||
133 | rate = CLOCK_TICK_RATE; | ||
134 | if (rate > 16 << 20) { | ||
135 | rate /= 16; | ||
136 | cr |= MTU_CRn_PRESCALE_16; | ||
137 | } else { | ||
138 | cr |= MTU_CRn_PRESCALE_1; | ||
139 | } | ||
123 | 140 | ||
124 | /* Init the timer and register clocksource */ | 141 | /* Timer 0 is the free running clocksource */ |
125 | nmdk_timer_reset(); | 142 | writel(cr, mtu_base + MTU_CR(0)); |
143 | writel(0, mtu_base + MTU_LR(0)); | ||
144 | writel(0, mtu_base + MTU_BGLR(0)); | ||
145 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0)); | ||
126 | 146 | ||
127 | nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift); | 147 | nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift); |
128 | bits = 8*sizeof(nmdk_count); | 148 | /* Now the scheduling clock is ready */ |
129 | nmdk_clksrc.mask = CLOCKSOURCE_MASK(bits); | 149 | nmdk_clksrc.read = nmdk_read_timer; |
130 | 150 | ||
131 | if (clocksource_register(&nmdk_clksrc)) | 151 | if (clocksource_register(&nmdk_clksrc)) |
132 | printk(KERN_ERR "timer: failed to initialize clock " | 152 | pr_err("timer: failed to initialize clock source %s\n", |
133 | "source %s\n", nmdk_clksrc.name); | 153 | nmdk_clksrc.name); |
154 | |||
155 | /* Timer 1 is used for events, fix according to rate */ | ||
156 | writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */ | ||
157 | nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift); | ||
158 | nmdk_clkevt.max_delta_ns = | ||
159 | clockevent_delta2ns(0xffffffff, &nmdk_clkevt); | ||
160 | nmdk_clkevt.min_delta_ns = | ||
161 | clockevent_delta2ns(0x00000002, &nmdk_clkevt); | ||
162 | nmdk_clkevt.cpumask = cpumask_of(0); | ||
134 | 163 | ||
135 | /* Register irq and clockevents */ | 164 | /* Register irq and clockevents */ |
136 | setup_irq(IRQ_MTU0, &nmdk_timer_irq); | 165 | setup_irq(IRQ_MTU0, &nmdk_timer_irq); |
137 | nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift); | ||
138 | nmdk_clkevt.cpumask = cpumask_of(0); | ||
139 | clockevents_register_device(&nmdk_clkevt); | 166 | clockevents_register_device(&nmdk_clkevt); |
140 | } | 167 | } |