diff options
author | Ben Dooks <ben-linux@fluff.org> | 2007-02-11 12:31:01 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-02-11 12:36:09 -0500 |
commit | a21765a70ec06be175d3997320a83fa66fcc8955 (patch) | |
tree | 24bdbf437a9bd5b7c1af05898f5aa25dccf67fe9 /arch/arm/plat-s3c24xx | |
parent | d19494b187b20e363f9b434b9ceab4159ac88324 (diff) |
[ARM] 4157/2: S3C24XX: move arch/arch/mach-s3c2410 into cpu components
The following patch and script moves the arch/arm/mach-s3c2410
directory into arch/arm/plat-s3c24xx for the generic core code
and inti arch/arm/mach-s3c{cpu} for the cpu/machine support files
Include directory include/asm-arm/plat-s3c24xx is added for the
core include files.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/plat-s3c24xx')
-rw-r--r-- | arch/arm/plat-s3c24xx/Kconfig | 96 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/Makefile | 30 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/clock.c | 449 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/common-smdk.c | 200 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/cpu.c | 357 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/devs.c | 585 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/dma.c | 1441 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/gpio.c | 188 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/irq.c | 801 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/pm-simtec.c | 66 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/pm.c | 659 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/s3c244x-irq.c | 146 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/s3c244x.c | 184 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/s3c244x.h | 25 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/sleep.S | 159 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/time.c | 262 |
16 files changed, 5648 insertions, 0 deletions
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig new file mode 100644 index 000000000000..97813645b5ee --- /dev/null +++ b/arch/arm/plat-s3c24xx/Kconfig | |||
@@ -0,0 +1,96 @@ | |||
1 | # arch/arm/plat-s3c24xx/Kconfig | ||
2 | # | ||
3 | # Copyright 2007 Simtec Electronics | ||
4 | # | ||
5 | # Licensed under GPLv2 | ||
6 | |||
7 | config PLAT_S3C24XX | ||
8 | bool | ||
9 | depends on ARCH_S3C2410 | ||
10 | default y | ||
11 | help | ||
12 | Base platform code for any Samsung S3C device | ||
13 | |||
14 | config CPU_S3C244X | ||
15 | bool | ||
16 | depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442) | ||
17 | help | ||
18 | Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems. | ||
19 | |||
20 | config PM_SIMTEC | ||
21 | bool | ||
22 | help | ||
23 | Common power management code for systems that are | ||
24 | compatible with the Simtec style of power management | ||
25 | |||
26 | config S3C2410_BOOT_WATCHDOG | ||
27 | bool "S3C2410 Initialisation watchdog" | ||
28 | depends on ARCH_S3C2410 && S3C2410_WATCHDOG | ||
29 | help | ||
30 | Say y to enable the watchdog during the kernel decompression | ||
31 | stage. If the kernel fails to uncompress, then the watchdog | ||
32 | will trigger a reset and the system should restart. | ||
33 | |||
34 | config S3C2410_BOOT_ERROR_RESET | ||
35 | bool "S3C2410 Reboot on decompression error" | ||
36 | depends on ARCH_S3C2410 | ||
37 | help | ||
38 | Say y here to use the watchdog to reset the system if the | ||
39 | kernel decompressor detects an error during decompression. | ||
40 | |||
41 | config S3C2410_PM_DEBUG | ||
42 | bool "S3C2410 PM Suspend debug" | ||
43 | depends on ARCH_S3C2410 && PM | ||
44 | help | ||
45 | Say Y here if you want verbose debugging from the PM Suspend and | ||
46 | Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt> | ||
47 | for more information. | ||
48 | |||
49 | config S3C2410_PM_CHECK | ||
50 | bool "S3C2410 PM Suspend Memory CRC" | ||
51 | depends on ARCH_S3C2410 && PM && CRC32 | ||
52 | help | ||
53 | Enable the PM code's memory area checksum over sleep. This option | ||
54 | will generate CRCs of all blocks of memory, and store them before | ||
55 | going to sleep. The blocks are then checked on resume for any | ||
56 | errors. | ||
57 | |||
58 | config S3C2410_PM_CHECK_CHUNKSIZE | ||
59 | int "S3C2410 PM Suspend CRC Chunksize (KiB)" | ||
60 | depends on ARCH_S3C2410 && PM && S3C2410_PM_CHECK | ||
61 | default 64 | ||
62 | help | ||
63 | Set the chunksize in Kilobytes of the CRC for checking memory | ||
64 | corruption over suspend and resume. A smaller value will mean that | ||
65 | the CRC data block will take more memory, but wil identify any | ||
66 | faults with better precision. | ||
67 | |||
68 | config S3C2410_LOWLEVEL_UART_PORT | ||
69 | int "S3C2410 UART to use for low-level messages" | ||
70 | default 0 | ||
71 | help | ||
72 | Choice of which UART port to use for the low-level messages, | ||
73 | such as the `Uncompressing...` at start time. The value of | ||
74 | this configuration should be between zero and two. The port | ||
75 | must have been initialised by the boot-loader before use. | ||
76 | |||
77 | config S3C2410_DMA | ||
78 | bool "S3C2410 DMA support" | ||
79 | depends on ARCH_S3C2410 | ||
80 | help | ||
81 | S3C2410 DMA support. This is needed for drivers like sound which | ||
82 | use the S3C2410's DMA system to move data to and from the | ||
83 | peripheral blocks. | ||
84 | |||
85 | config S3C2410_DMA_DEBUG | ||
86 | bool "S3C2410 DMA support debug" | ||
87 | depends on ARCH_S3C2410 && S3C2410_DMA | ||
88 | help | ||
89 | Enable debugging output for the DMA code. This option sends info | ||
90 | to the kernel log, at priority KERN_DEBUG. | ||
91 | |||
92 | config MACH_SMDK | ||
93 | bool | ||
94 | help | ||
95 | Common machine code for SMDK2410 and SMDK2440 | ||
96 | |||
diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile new file mode 100644 index 000000000000..8e5ccaa1f03c --- /dev/null +++ b/arch/arm/plat-s3c24xx/Makefile | |||
@@ -0,0 +1,30 @@ | |||
1 | # arch/arm/plat-s3c24xx/Makefile | ||
2 | # | ||
3 | # Copyright 2007 Simtec Electronics | ||
4 | # | ||
5 | # Licensed under GPLv2 | ||
6 | |||
7 | obj-y := | ||
8 | obj-m := | ||
9 | obj-n := | ||
10 | obj- := | ||
11 | |||
12 | |||
13 | # Core files | ||
14 | |||
15 | obj-y += cpu.o | ||
16 | obj-y += irq.o | ||
17 | obj-y += devs.o | ||
18 | obj-y += gpio.o | ||
19 | obj-y += time.o | ||
20 | obj-y += clock.o | ||
21 | |||
22 | # Architecture dependant builds | ||
23 | |||
24 | obj-$(CONFIG_CPU_S3C244X) += s3c244x.o | ||
25 | obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o | ||
26 | obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o | ||
27 | obj-$(CONFIG_PM) += pm.o | ||
28 | obj-$(CONFIG_PM) += sleep.o | ||
29 | obj-$(CONFIG_S3C2410_DMA) += dma.o | ||
30 | obj-$(CONFIG_MACH_SMDK) += common-smdk.o | ||
diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c new file mode 100644 index 000000000000..d3dc03a7383a --- /dev/null +++ b/arch/arm/plat-s3c24xx/clock.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/clock.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C24XX Core clock control support | ||
7 | * | ||
8 | * Based on, and code from linux/arch/arm/mach-versatile/clock.c | ||
9 | ** | ||
10 | ** Copyright (C) 2004 ARM Limited. | ||
11 | ** Written by Deep Blue Solutions Limited. | ||
12 | * | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
27 | */ | ||
28 | |||
29 | #include <linux/init.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/list.h> | ||
33 | #include <linux/errno.h> | ||
34 | #include <linux/err.h> | ||
35 | #include <linux/platform_device.h> | ||
36 | #include <linux/sysdev.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/ioport.h> | ||
39 | #include <linux/clk.h> | ||
40 | #include <linux/mutex.h> | ||
41 | #include <linux/delay.h> | ||
42 | |||
43 | #include <asm/hardware.h> | ||
44 | #include <asm/irq.h> | ||
45 | #include <asm/io.h> | ||
46 | |||
47 | #include <asm/arch/regs-clock.h> | ||
48 | #include <asm/arch/regs-gpio.h> | ||
49 | |||
50 | #include <asm/plat-s3c24xx/clock.h> | ||
51 | #include <asm/plat-s3c24xx/cpu.h> | ||
52 | |||
53 | /* clock information */ | ||
54 | |||
55 | static LIST_HEAD(clocks); | ||
56 | |||
57 | DEFINE_MUTEX(clocks_mutex); | ||
58 | |||
59 | /* enable and disable calls for use with the clk struct */ | ||
60 | |||
61 | static int clk_null_enable(struct clk *clk, int enable) | ||
62 | { | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /* Clock API calls */ | ||
67 | |||
68 | struct clk *clk_get(struct device *dev, const char *id) | ||
69 | { | ||
70 | struct clk *p; | ||
71 | struct clk *clk = ERR_PTR(-ENOENT); | ||
72 | int idno; | ||
73 | |||
74 | if (dev == NULL || dev->bus != &platform_bus_type) | ||
75 | idno = -1; | ||
76 | else | ||
77 | idno = to_platform_device(dev)->id; | ||
78 | |||
79 | mutex_lock(&clocks_mutex); | ||
80 | |||
81 | list_for_each_entry(p, &clocks, list) { | ||
82 | if (p->id == idno && | ||
83 | strcmp(id, p->name) == 0 && | ||
84 | try_module_get(p->owner)) { | ||
85 | clk = p; | ||
86 | break; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | /* check for the case where a device was supplied, but the | ||
91 | * clock that was being searched for is not device specific */ | ||
92 | |||
93 | if (IS_ERR(clk)) { | ||
94 | list_for_each_entry(p, &clocks, list) { | ||
95 | if (p->id == -1 && strcmp(id, p->name) == 0 && | ||
96 | try_module_get(p->owner)) { | ||
97 | clk = p; | ||
98 | break; | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | |||
103 | mutex_unlock(&clocks_mutex); | ||
104 | return clk; | ||
105 | } | ||
106 | |||
107 | void clk_put(struct clk *clk) | ||
108 | { | ||
109 | module_put(clk->owner); | ||
110 | } | ||
111 | |||
112 | int clk_enable(struct clk *clk) | ||
113 | { | ||
114 | if (IS_ERR(clk) || clk == NULL) | ||
115 | return -EINVAL; | ||
116 | |||
117 | clk_enable(clk->parent); | ||
118 | |||
119 | mutex_lock(&clocks_mutex); | ||
120 | |||
121 | if ((clk->usage++) == 0) | ||
122 | (clk->enable)(clk, 1); | ||
123 | |||
124 | mutex_unlock(&clocks_mutex); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | void clk_disable(struct clk *clk) | ||
129 | { | ||
130 | if (IS_ERR(clk) || clk == NULL) | ||
131 | return; | ||
132 | |||
133 | mutex_lock(&clocks_mutex); | ||
134 | |||
135 | if ((--clk->usage) == 0) | ||
136 | (clk->enable)(clk, 0); | ||
137 | |||
138 | mutex_unlock(&clocks_mutex); | ||
139 | clk_disable(clk->parent); | ||
140 | } | ||
141 | |||
142 | |||
143 | unsigned long clk_get_rate(struct clk *clk) | ||
144 | { | ||
145 | if (IS_ERR(clk)) | ||
146 | return 0; | ||
147 | |||
148 | if (clk->rate != 0) | ||
149 | return clk->rate; | ||
150 | |||
151 | if (clk->get_rate != NULL) | ||
152 | return (clk->get_rate)(clk); | ||
153 | |||
154 | if (clk->parent != NULL) | ||
155 | return clk_get_rate(clk->parent); | ||
156 | |||
157 | return clk->rate; | ||
158 | } | ||
159 | |||
160 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
161 | { | ||
162 | if (!IS_ERR(clk) && clk->round_rate) | ||
163 | return (clk->round_rate)(clk, rate); | ||
164 | |||
165 | return rate; | ||
166 | } | ||
167 | |||
168 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
169 | { | ||
170 | int ret; | ||
171 | |||
172 | if (IS_ERR(clk)) | ||
173 | return -EINVAL; | ||
174 | |||
175 | mutex_lock(&clocks_mutex); | ||
176 | ret = (clk->set_rate)(clk, rate); | ||
177 | mutex_unlock(&clocks_mutex); | ||
178 | |||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | struct clk *clk_get_parent(struct clk *clk) | ||
183 | { | ||
184 | return clk->parent; | ||
185 | } | ||
186 | |||
187 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
188 | { | ||
189 | int ret = 0; | ||
190 | |||
191 | if (IS_ERR(clk)) | ||
192 | return -EINVAL; | ||
193 | |||
194 | mutex_lock(&clocks_mutex); | ||
195 | |||
196 | if (clk->set_parent) | ||
197 | ret = (clk->set_parent)(clk, parent); | ||
198 | |||
199 | mutex_unlock(&clocks_mutex); | ||
200 | |||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | EXPORT_SYMBOL(clk_get); | ||
205 | EXPORT_SYMBOL(clk_put); | ||
206 | EXPORT_SYMBOL(clk_enable); | ||
207 | EXPORT_SYMBOL(clk_disable); | ||
208 | EXPORT_SYMBOL(clk_get_rate); | ||
209 | EXPORT_SYMBOL(clk_round_rate); | ||
210 | EXPORT_SYMBOL(clk_set_rate); | ||
211 | EXPORT_SYMBOL(clk_get_parent); | ||
212 | EXPORT_SYMBOL(clk_set_parent); | ||
213 | |||
214 | /* base clocks */ | ||
215 | |||
216 | struct clk clk_xtal = { | ||
217 | .name = "xtal", | ||
218 | .id = -1, | ||
219 | .rate = 0, | ||
220 | .parent = NULL, | ||
221 | .ctrlbit = 0, | ||
222 | }; | ||
223 | |||
224 | struct clk clk_mpll = { | ||
225 | .name = "mpll", | ||
226 | .id = -1, | ||
227 | }; | ||
228 | |||
229 | struct clk clk_upll = { | ||
230 | .name = "upll", | ||
231 | .id = -1, | ||
232 | .parent = NULL, | ||
233 | .ctrlbit = 0, | ||
234 | }; | ||
235 | |||
236 | struct clk clk_f = { | ||
237 | .name = "fclk", | ||
238 | .id = -1, | ||
239 | .rate = 0, | ||
240 | .parent = &clk_mpll, | ||
241 | .ctrlbit = 0, | ||
242 | }; | ||
243 | |||
244 | struct clk clk_h = { | ||
245 | .name = "hclk", | ||
246 | .id = -1, | ||
247 | .rate = 0, | ||
248 | .parent = NULL, | ||
249 | .ctrlbit = 0, | ||
250 | }; | ||
251 | |||
252 | struct clk clk_p = { | ||
253 | .name = "pclk", | ||
254 | .id = -1, | ||
255 | .rate = 0, | ||
256 | .parent = NULL, | ||
257 | .ctrlbit = 0, | ||
258 | }; | ||
259 | |||
260 | struct clk clk_usb_bus = { | ||
261 | .name = "usb-bus", | ||
262 | .id = -1, | ||
263 | .rate = 0, | ||
264 | .parent = &clk_upll, | ||
265 | }; | ||
266 | |||
267 | /* clocks that could be registered by external code */ | ||
268 | |||
269 | static int s3c24xx_dclk_enable(struct clk *clk, int enable) | ||
270 | { | ||
271 | unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); | ||
272 | |||
273 | if (enable) | ||
274 | dclkcon |= clk->ctrlbit; | ||
275 | else | ||
276 | dclkcon &= ~clk->ctrlbit; | ||
277 | |||
278 | __raw_writel(dclkcon, S3C24XX_DCLKCON); | ||
279 | |||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) | ||
284 | { | ||
285 | unsigned long dclkcon; | ||
286 | unsigned int uclk; | ||
287 | |||
288 | if (parent == &clk_upll) | ||
289 | uclk = 1; | ||
290 | else if (parent == &clk_p) | ||
291 | uclk = 0; | ||
292 | else | ||
293 | return -EINVAL; | ||
294 | |||
295 | clk->parent = parent; | ||
296 | |||
297 | dclkcon = __raw_readl(S3C24XX_DCLKCON); | ||
298 | |||
299 | if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { | ||
300 | if (uclk) | ||
301 | dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; | ||
302 | else | ||
303 | dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; | ||
304 | } else { | ||
305 | if (uclk) | ||
306 | dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; | ||
307 | else | ||
308 | dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; | ||
309 | } | ||
310 | |||
311 | __raw_writel(dclkcon, S3C24XX_DCLKCON); | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | |||
317 | static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) | ||
318 | { | ||
319 | unsigned long mask; | ||
320 | unsigned long source; | ||
321 | |||
322 | /* calculate the MISCCR setting for the clock */ | ||
323 | |||
324 | if (parent == &clk_xtal) | ||
325 | source = S3C2410_MISCCR_CLK0_MPLL; | ||
326 | else if (parent == &clk_upll) | ||
327 | source = S3C2410_MISCCR_CLK0_UPLL; | ||
328 | else if (parent == &clk_f) | ||
329 | source = S3C2410_MISCCR_CLK0_FCLK; | ||
330 | else if (parent == &clk_h) | ||
331 | source = S3C2410_MISCCR_CLK0_HCLK; | ||
332 | else if (parent == &clk_p) | ||
333 | source = S3C2410_MISCCR_CLK0_PCLK; | ||
334 | else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) | ||
335 | source = S3C2410_MISCCR_CLK0_DCLK0; | ||
336 | else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) | ||
337 | source = S3C2410_MISCCR_CLK0_DCLK0; | ||
338 | else | ||
339 | return -EINVAL; | ||
340 | |||
341 | clk->parent = parent; | ||
342 | |||
343 | if (clk == &s3c24xx_dclk0) | ||
344 | mask = S3C2410_MISCCR_CLK0_MASK; | ||
345 | else { | ||
346 | source <<= 4; | ||
347 | mask = S3C2410_MISCCR_CLK1_MASK; | ||
348 | } | ||
349 | |||
350 | s3c2410_modify_misccr(mask, source); | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | /* external clock definitions */ | ||
355 | |||
356 | struct clk s3c24xx_dclk0 = { | ||
357 | .name = "dclk0", | ||
358 | .id = -1, | ||
359 | .ctrlbit = S3C2410_DCLKCON_DCLK0EN, | ||
360 | .enable = s3c24xx_dclk_enable, | ||
361 | .set_parent = s3c24xx_dclk_setparent, | ||
362 | }; | ||
363 | |||
364 | struct clk s3c24xx_dclk1 = { | ||
365 | .name = "dclk1", | ||
366 | .id = -1, | ||
367 | .ctrlbit = S3C2410_DCLKCON_DCLK0EN, | ||
368 | .enable = s3c24xx_dclk_enable, | ||
369 | .set_parent = s3c24xx_dclk_setparent, | ||
370 | }; | ||
371 | |||
372 | struct clk s3c24xx_clkout0 = { | ||
373 | .name = "clkout0", | ||
374 | .id = -1, | ||
375 | .set_parent = s3c24xx_clkout_setparent, | ||
376 | }; | ||
377 | |||
378 | struct clk s3c24xx_clkout1 = { | ||
379 | .name = "clkout1", | ||
380 | .id = -1, | ||
381 | .set_parent = s3c24xx_clkout_setparent, | ||
382 | }; | ||
383 | |||
384 | struct clk s3c24xx_uclk = { | ||
385 | .name = "uclk", | ||
386 | .id = -1, | ||
387 | }; | ||
388 | |||
389 | /* initialise the clock system */ | ||
390 | |||
391 | int s3c24xx_register_clock(struct clk *clk) | ||
392 | { | ||
393 | clk->owner = THIS_MODULE; | ||
394 | |||
395 | if (clk->enable == NULL) | ||
396 | clk->enable = clk_null_enable; | ||
397 | |||
398 | /* add to the list of available clocks */ | ||
399 | |||
400 | mutex_lock(&clocks_mutex); | ||
401 | list_add(&clk->list, &clocks); | ||
402 | mutex_unlock(&clocks_mutex); | ||
403 | |||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | /* initalise all the clocks */ | ||
408 | |||
409 | int __init s3c24xx_setup_clocks(unsigned long xtal, | ||
410 | unsigned long fclk, | ||
411 | unsigned long hclk, | ||
412 | unsigned long pclk) | ||
413 | { | ||
414 | printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); | ||
415 | |||
416 | /* initialise the main system clocks */ | ||
417 | |||
418 | clk_xtal.rate = xtal; | ||
419 | clk_upll.rate = s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal); | ||
420 | |||
421 | clk_mpll.rate = fclk; | ||
422 | clk_h.rate = hclk; | ||
423 | clk_p.rate = pclk; | ||
424 | clk_f.rate = fclk; | ||
425 | |||
426 | /* assume uart clocks are correctly setup */ | ||
427 | |||
428 | /* register our clocks */ | ||
429 | |||
430 | if (s3c24xx_register_clock(&clk_xtal) < 0) | ||
431 | printk(KERN_ERR "failed to register master xtal\n"); | ||
432 | |||
433 | if (s3c24xx_register_clock(&clk_mpll) < 0) | ||
434 | printk(KERN_ERR "failed to register mpll clock\n"); | ||
435 | |||
436 | if (s3c24xx_register_clock(&clk_upll) < 0) | ||
437 | printk(KERN_ERR "failed to register upll clock\n"); | ||
438 | |||
439 | if (s3c24xx_register_clock(&clk_f) < 0) | ||
440 | printk(KERN_ERR "failed to register cpu fclk\n"); | ||
441 | |||
442 | if (s3c24xx_register_clock(&clk_h) < 0) | ||
443 | printk(KERN_ERR "failed to register cpu hclk\n"); | ||
444 | |||
445 | if (s3c24xx_register_clock(&clk_p) < 0) | ||
446 | printk(KERN_ERR "failed to register cpu pclk\n"); | ||
447 | |||
448 | return 0; | ||
449 | } | ||
diff --git a/arch/arm/plat-s3c24xx/common-smdk.c b/arch/arm/plat-s3c24xx/common-smdk.c new file mode 100644 index 000000000000..908efa7d745f --- /dev/null +++ b/arch/arm/plat-s3c24xx/common-smdk.c | |||
@@ -0,0 +1,200 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/common-smdk.c | ||
2 | * | ||
3 | * Copyright (c) 2006 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Common code for SMDK2410 and SMDK2440 boards | ||
7 | * | ||
8 | * http://www.fluff.org/ben/smdk2440/ | ||
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 version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/timer.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | |||
23 | #include <linux/mtd/mtd.h> | ||
24 | #include <linux/mtd/nand.h> | ||
25 | #include <linux/mtd/nand_ecc.h> | ||
26 | #include <linux/mtd/partitions.h> | ||
27 | |||
28 | #include <asm/mach/arch.h> | ||
29 | #include <asm/mach/map.h> | ||
30 | #include <asm/mach/irq.h> | ||
31 | |||
32 | #include <asm/hardware.h> | ||
33 | #include <asm/io.h> | ||
34 | #include <asm/irq.h> | ||
35 | |||
36 | #include <asm/arch/regs-gpio.h> | ||
37 | #include <asm/arch/leds-gpio.h> | ||
38 | |||
39 | #include <asm/arch/nand.h> | ||
40 | |||
41 | #include <asm/plat-s3c24xx/common-smdk.h> | ||
42 | #include <asm/plat-s3c24xx/devs.h> | ||
43 | #include <asm/plat-s3c24xx/pm.h> | ||
44 | |||
45 | /* LED devices */ | ||
46 | |||
47 | static struct s3c24xx_led_platdata smdk_pdata_led4 = { | ||
48 | .gpio = S3C2410_GPF4, | ||
49 | .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, | ||
50 | .name = "led4", | ||
51 | .def_trigger = "timer", | ||
52 | }; | ||
53 | |||
54 | static struct s3c24xx_led_platdata smdk_pdata_led5 = { | ||
55 | .gpio = S3C2410_GPF5, | ||
56 | .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, | ||
57 | .name = "led5", | ||
58 | .def_trigger = "nand-disk", | ||
59 | }; | ||
60 | |||
61 | static struct s3c24xx_led_platdata smdk_pdata_led6 = { | ||
62 | .gpio = S3C2410_GPF6, | ||
63 | .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, | ||
64 | .name = "led6", | ||
65 | }; | ||
66 | |||
67 | static struct s3c24xx_led_platdata smdk_pdata_led7 = { | ||
68 | .gpio = S3C2410_GPF7, | ||
69 | .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, | ||
70 | .name = "led7", | ||
71 | }; | ||
72 | |||
73 | static struct platform_device smdk_led4 = { | ||
74 | .name = "s3c24xx_led", | ||
75 | .id = 0, | ||
76 | .dev = { | ||
77 | .platform_data = &smdk_pdata_led4, | ||
78 | }, | ||
79 | }; | ||
80 | |||
81 | static struct platform_device smdk_led5 = { | ||
82 | .name = "s3c24xx_led", | ||
83 | .id = 1, | ||
84 | .dev = { | ||
85 | .platform_data = &smdk_pdata_led5, | ||
86 | }, | ||
87 | }; | ||
88 | |||
89 | static struct platform_device smdk_led6 = { | ||
90 | .name = "s3c24xx_led", | ||
91 | .id = 2, | ||
92 | .dev = { | ||
93 | .platform_data = &smdk_pdata_led6, | ||
94 | }, | ||
95 | }; | ||
96 | |||
97 | static struct platform_device smdk_led7 = { | ||
98 | .name = "s3c24xx_led", | ||
99 | .id = 3, | ||
100 | .dev = { | ||
101 | .platform_data = &smdk_pdata_led7, | ||
102 | }, | ||
103 | }; | ||
104 | |||
105 | /* NAND parititon from 2.4.18-swl5 */ | ||
106 | |||
107 | static struct mtd_partition smdk_default_nand_part[] = { | ||
108 | [0] = { | ||
109 | .name = "Boot Agent", | ||
110 | .size = SZ_16K, | ||
111 | .offset = 0, | ||
112 | }, | ||
113 | [1] = { | ||
114 | .name = "S3C2410 flash partition 1", | ||
115 | .offset = 0, | ||
116 | .size = SZ_2M, | ||
117 | }, | ||
118 | [2] = { | ||
119 | .name = "S3C2410 flash partition 2", | ||
120 | .offset = SZ_4M, | ||
121 | .size = SZ_4M, | ||
122 | }, | ||
123 | [3] = { | ||
124 | .name = "S3C2410 flash partition 3", | ||
125 | .offset = SZ_8M, | ||
126 | .size = SZ_2M, | ||
127 | }, | ||
128 | [4] = { | ||
129 | .name = "S3C2410 flash partition 4", | ||
130 | .offset = SZ_1M * 10, | ||
131 | .size = SZ_4M, | ||
132 | }, | ||
133 | [5] = { | ||
134 | .name = "S3C2410 flash partition 5", | ||
135 | .offset = SZ_1M * 14, | ||
136 | .size = SZ_1M * 10, | ||
137 | }, | ||
138 | [6] = { | ||
139 | .name = "S3C2410 flash partition 6", | ||
140 | .offset = SZ_1M * 24, | ||
141 | .size = SZ_1M * 24, | ||
142 | }, | ||
143 | [7] = { | ||
144 | .name = "S3C2410 flash partition 7", | ||
145 | .offset = SZ_1M * 48, | ||
146 | .size = SZ_16M, | ||
147 | } | ||
148 | }; | ||
149 | |||
150 | static struct s3c2410_nand_set smdk_nand_sets[] = { | ||
151 | [0] = { | ||
152 | .name = "NAND", | ||
153 | .nr_chips = 1, | ||
154 | .nr_partitions = ARRAY_SIZE(smdk_default_nand_part), | ||
155 | .partitions = smdk_default_nand_part, | ||
156 | }, | ||
157 | }; | ||
158 | |||
159 | /* choose a set of timings which should suit most 512Mbit | ||
160 | * chips and beyond. | ||
161 | */ | ||
162 | |||
163 | static struct s3c2410_platform_nand smdk_nand_info = { | ||
164 | .tacls = 20, | ||
165 | .twrph0 = 60, | ||
166 | .twrph1 = 20, | ||
167 | .nr_sets = ARRAY_SIZE(smdk_nand_sets), | ||
168 | .sets = smdk_nand_sets, | ||
169 | }; | ||
170 | |||
171 | /* devices we initialise */ | ||
172 | |||
173 | static struct platform_device __initdata *smdk_devs[] = { | ||
174 | &s3c_device_nand, | ||
175 | &smdk_led4, | ||
176 | &smdk_led5, | ||
177 | &smdk_led6, | ||
178 | &smdk_led7, | ||
179 | }; | ||
180 | |||
181 | void __init smdk_machine_init(void) | ||
182 | { | ||
183 | /* Configure the LEDs (even if we have no LED support)*/ | ||
184 | |||
185 | s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); | ||
186 | s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); | ||
187 | s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); | ||
188 | s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP); | ||
189 | |||
190 | s3c2410_gpio_setpin(S3C2410_GPF4, 1); | ||
191 | s3c2410_gpio_setpin(S3C2410_GPF5, 1); | ||
192 | s3c2410_gpio_setpin(S3C2410_GPF6, 1); | ||
193 | s3c2410_gpio_setpin(S3C2410_GPF7, 1); | ||
194 | |||
195 | s3c_device_nand.dev.platform_data = &smdk_nand_info; | ||
196 | |||
197 | platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs)); | ||
198 | |||
199 | s3c2410_pm_init(); | ||
200 | } | ||
diff --git a/arch/arm/plat-s3c24xx/cpu.c b/arch/arm/plat-s3c24xx/cpu.c new file mode 100644 index 000000000000..2fbb74969379 --- /dev/null +++ b/arch/arm/plat-s3c24xx/cpu.c | |||
@@ -0,0 +1,357 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/cpu.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * http://www.simtec.co.uk/products/SWLINUX/ | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * | ||
7 | * S3C24XX CPU Support | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | |||
25 | #include <linux/init.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/ioport.h> | ||
29 | #include <linux/serial_core.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | |||
32 | #include <asm/hardware.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/io.h> | ||
35 | #include <asm/delay.h> | ||
36 | |||
37 | #include <asm/mach/arch.h> | ||
38 | #include <asm/mach/map.h> | ||
39 | |||
40 | #include <asm/arch/regs-gpio.h> | ||
41 | #include <asm/arch/regs-serial.h> | ||
42 | |||
43 | #include <asm/plat-s3c24xx/cpu.h> | ||
44 | #include <asm/plat-s3c24xx/devs.h> | ||
45 | #include <asm/plat-s3c24xx/clock.h> | ||
46 | #include <asm/plat-s3c24xx/s3c2400.h> | ||
47 | #include <asm/plat-s3c24xx/s3c2410.h> | ||
48 | #include <asm/plat-s3c24xx/s3c2412.h> | ||
49 | #include "s3c244x.h" | ||
50 | #include <asm/plat-s3c24xx/s3c2440.h> | ||
51 | #include <asm/plat-s3c24xx/s3c2442.h> | ||
52 | |||
53 | struct cpu_table { | ||
54 | unsigned long idcode; | ||
55 | unsigned long idmask; | ||
56 | void (*map_io)(struct map_desc *mach_desc, int size); | ||
57 | void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); | ||
58 | void (*init_clocks)(int xtal); | ||
59 | int (*init)(void); | ||
60 | const char *name; | ||
61 | }; | ||
62 | |||
63 | /* table of supported CPUs */ | ||
64 | |||
65 | static const char name_s3c2400[] = "S3C2400"; | ||
66 | static const char name_s3c2410[] = "S3C2410"; | ||
67 | static const char name_s3c2412[] = "S3C2412"; | ||
68 | static const char name_s3c2440[] = "S3C2440"; | ||
69 | static const char name_s3c2442[] = "S3C2442"; | ||
70 | static const char name_s3c2410a[] = "S3C2410A"; | ||
71 | static const char name_s3c2440a[] = "S3C2440A"; | ||
72 | |||
73 | static struct cpu_table cpu_ids[] __initdata = { | ||
74 | { | ||
75 | .idcode = 0x32410000, | ||
76 | .idmask = 0xffffffff, | ||
77 | .map_io = s3c2410_map_io, | ||
78 | .init_clocks = s3c2410_init_clocks, | ||
79 | .init_uarts = s3c2410_init_uarts, | ||
80 | .init = s3c2410_init, | ||
81 | .name = name_s3c2410 | ||
82 | }, | ||
83 | { | ||
84 | .idcode = 0x32410002, | ||
85 | .idmask = 0xffffffff, | ||
86 | .map_io = s3c2410_map_io, | ||
87 | .init_clocks = s3c2410_init_clocks, | ||
88 | .init_uarts = s3c2410_init_uarts, | ||
89 | .init = s3c2410_init, | ||
90 | .name = name_s3c2410a | ||
91 | }, | ||
92 | { | ||
93 | .idcode = 0x32440000, | ||
94 | .idmask = 0xffffffff, | ||
95 | .map_io = s3c244x_map_io, | ||
96 | .init_clocks = s3c244x_init_clocks, | ||
97 | .init_uarts = s3c244x_init_uarts, | ||
98 | .init = s3c2440_init, | ||
99 | .name = name_s3c2440 | ||
100 | }, | ||
101 | { | ||
102 | .idcode = 0x32440001, | ||
103 | .idmask = 0xffffffff, | ||
104 | .map_io = s3c244x_map_io, | ||
105 | .init_clocks = s3c244x_init_clocks, | ||
106 | .init_uarts = s3c244x_init_uarts, | ||
107 | .init = s3c2440_init, | ||
108 | .name = name_s3c2440a | ||
109 | }, | ||
110 | { | ||
111 | .idcode = 0x32440aaa, | ||
112 | .idmask = 0xffffffff, | ||
113 | .map_io = s3c244x_map_io, | ||
114 | .init_clocks = s3c244x_init_clocks, | ||
115 | .init_uarts = s3c244x_init_uarts, | ||
116 | .init = s3c2442_init, | ||
117 | .name = name_s3c2442 | ||
118 | }, | ||
119 | { | ||
120 | .idcode = 0x32412001, | ||
121 | .idmask = 0xffffffff, | ||
122 | .map_io = s3c2412_map_io, | ||
123 | .init_clocks = s3c2412_init_clocks, | ||
124 | .init_uarts = s3c2412_init_uarts, | ||
125 | .init = s3c2412_init, | ||
126 | .name = name_s3c2412, | ||
127 | }, | ||
128 | { /* a newer version of the s3c2412 */ | ||
129 | .idcode = 0x32412003, | ||
130 | .idmask = 0xffffffff, | ||
131 | .map_io = s3c2412_map_io, | ||
132 | .init_clocks = s3c2412_init_clocks, | ||
133 | .init_uarts = s3c2412_init_uarts, | ||
134 | .init = s3c2412_init, | ||
135 | .name = name_s3c2412, | ||
136 | }, | ||
137 | { | ||
138 | .idcode = 0x0, /* S3C2400 doesn't have an idcode */ | ||
139 | .idmask = 0xffffffff, | ||
140 | .map_io = s3c2400_map_io, | ||
141 | .init_clocks = s3c2400_init_clocks, | ||
142 | .init_uarts = s3c2400_init_uarts, | ||
143 | .init = s3c2400_init, | ||
144 | .name = name_s3c2400 | ||
145 | }, | ||
146 | }; | ||
147 | |||
148 | /* minimal IO mapping */ | ||
149 | |||
150 | static struct map_desc s3c_iodesc[] __initdata = { | ||
151 | IODESC_ENT(GPIO), | ||
152 | IODESC_ENT(IRQ), | ||
153 | IODESC_ENT(MEMCTRL), | ||
154 | IODESC_ENT(UART) | ||
155 | }; | ||
156 | |||
157 | |||
158 | static struct cpu_table * | ||
159 | s3c_lookup_cpu(unsigned long idcode) | ||
160 | { | ||
161 | struct cpu_table *tab; | ||
162 | int count; | ||
163 | |||
164 | tab = cpu_ids; | ||
165 | for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) { | ||
166 | if ((idcode & tab->idmask) == tab->idcode) | ||
167 | return tab; | ||
168 | } | ||
169 | |||
170 | return NULL; | ||
171 | } | ||
172 | |||
173 | /* board information */ | ||
174 | |||
175 | static struct s3c24xx_board *board; | ||
176 | |||
177 | void s3c24xx_set_board(struct s3c24xx_board *b) | ||
178 | { | ||
179 | int i; | ||
180 | |||
181 | board = b; | ||
182 | |||
183 | if (b->clocks_count != 0) { | ||
184 | struct clk **ptr = b->clocks; | ||
185 | |||
186 | for (i = b->clocks_count; i > 0; i--, ptr++) | ||
187 | s3c24xx_register_clock(*ptr); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | /* cpu information */ | ||
192 | |||
193 | static struct cpu_table *cpu; | ||
194 | |||
195 | static unsigned long s3c24xx_read_idcode_v5(void) | ||
196 | { | ||
197 | #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) | ||
198 | return __raw_readl(S3C2412_GSTATUS1); | ||
199 | #else | ||
200 | return 1UL; /* don't look like an 2400 */ | ||
201 | #endif | ||
202 | } | ||
203 | |||
204 | static unsigned long s3c24xx_read_idcode_v4(void) | ||
205 | { | ||
206 | #ifndef CONFIG_CPU_S3C2400 | ||
207 | return __raw_readl(S3C2410_GSTATUS1); | ||
208 | #else | ||
209 | return 0UL; | ||
210 | #endif | ||
211 | } | ||
212 | |||
213 | void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) | ||
214 | { | ||
215 | unsigned long idcode = 0x0; | ||
216 | |||
217 | /* initialise the io descriptors we need for initialisation */ | ||
218 | iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); | ||
219 | |||
220 | if (cpu_architecture() >= CPU_ARCH_ARMv5) { | ||
221 | idcode = s3c24xx_read_idcode_v5(); | ||
222 | } else { | ||
223 | idcode = s3c24xx_read_idcode_v4(); | ||
224 | } | ||
225 | |||
226 | cpu = s3c_lookup_cpu(idcode); | ||
227 | |||
228 | if (cpu == NULL) { | ||
229 | printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); | ||
230 | panic("Unknown S3C24XX CPU"); | ||
231 | } | ||
232 | |||
233 | printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); | ||
234 | |||
235 | if (cpu->map_io == NULL || cpu->init == NULL) { | ||
236 | printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); | ||
237 | panic("Unsupported S3C24XX CPU"); | ||
238 | } | ||
239 | |||
240 | (cpu->map_io)(mach_desc, size); | ||
241 | } | ||
242 | |||
243 | /* s3c24xx_init_clocks | ||
244 | * | ||
245 | * Initialise the clock subsystem and associated information from the | ||
246 | * given master crystal value. | ||
247 | * | ||
248 | * xtal = 0 -> use default PLL crystal value (normally 12MHz) | ||
249 | * != 0 -> PLL crystal value in Hz | ||
250 | */ | ||
251 | |||
252 | void __init s3c24xx_init_clocks(int xtal) | ||
253 | { | ||
254 | if (xtal == 0) | ||
255 | xtal = 12*1000*1000; | ||
256 | |||
257 | if (cpu == NULL) | ||
258 | panic("s3c24xx_init_clocks: no cpu setup?\n"); | ||
259 | |||
260 | if (cpu->init_clocks == NULL) | ||
261 | panic("s3c24xx_init_clocks: cpu has no clock init\n"); | ||
262 | else | ||
263 | (cpu->init_clocks)(xtal); | ||
264 | } | ||
265 | |||
266 | /* uart management */ | ||
267 | |||
268 | static int nr_uarts __initdata = 0; | ||
269 | |||
270 | static struct s3c2410_uartcfg uart_cfgs[3]; | ||
271 | |||
272 | /* s3c24xx_init_uartdevs | ||
273 | * | ||
274 | * copy the specified platform data and configuration into our central | ||
275 | * set of devices, before the data is thrown away after the init process. | ||
276 | * | ||
277 | * This also fills in the array passed to the serial driver for the | ||
278 | * early initialisation of the console. | ||
279 | */ | ||
280 | |||
281 | void __init s3c24xx_init_uartdevs(char *name, | ||
282 | struct s3c24xx_uart_resources *res, | ||
283 | struct s3c2410_uartcfg *cfg, int no) | ||
284 | { | ||
285 | struct platform_device *platdev; | ||
286 | struct s3c2410_uartcfg *cfgptr = uart_cfgs; | ||
287 | struct s3c24xx_uart_resources *resp; | ||
288 | int uart; | ||
289 | |||
290 | memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no); | ||
291 | |||
292 | for (uart = 0; uart < no; uart++, cfg++, cfgptr++) { | ||
293 | platdev = s3c24xx_uart_src[cfgptr->hwport]; | ||
294 | |||
295 | resp = res + cfgptr->hwport; | ||
296 | |||
297 | s3c24xx_uart_devs[uart] = platdev; | ||
298 | |||
299 | platdev->name = name; | ||
300 | platdev->resource = resp->resources; | ||
301 | platdev->num_resources = resp->nr_resources; | ||
302 | |||
303 | platdev->dev.platform_data = cfgptr; | ||
304 | } | ||
305 | |||
306 | nr_uarts = no; | ||
307 | } | ||
308 | |||
309 | void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) | ||
310 | { | ||
311 | if (cpu == NULL) | ||
312 | return; | ||
313 | |||
314 | if (cpu->init_uarts == NULL) { | ||
315 | printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n"); | ||
316 | } else | ||
317 | (cpu->init_uarts)(cfg, no); | ||
318 | } | ||
319 | |||
320 | static int __init s3c_arch_init(void) | ||
321 | { | ||
322 | int ret; | ||
323 | |||
324 | // do the correct init for cpu | ||
325 | |||
326 | if (cpu == NULL) | ||
327 | panic("s3c_arch_init: NULL cpu\n"); | ||
328 | |||
329 | ret = (cpu->init)(); | ||
330 | if (ret != 0) | ||
331 | return ret; | ||
332 | |||
333 | ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts); | ||
334 | if (ret != 0) | ||
335 | return ret; | ||
336 | |||
337 | if (board != NULL) { | ||
338 | struct platform_device **ptr = board->devices; | ||
339 | int i; | ||
340 | |||
341 | for (i = 0; i < board->devices_count; i++, ptr++) { | ||
342 | ret = platform_device_register(*ptr); | ||
343 | |||
344 | if (ret) { | ||
345 | printk(KERN_ERR "s3c24xx: failed to add board device %s (%d) @%p\n", (*ptr)->name, ret, *ptr); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | /* mask any error, we may not need all these board | ||
350 | * devices */ | ||
351 | ret = 0; | ||
352 | } | ||
353 | |||
354 | return ret; | ||
355 | } | ||
356 | |||
357 | arch_initcall(s3c_arch_init); | ||
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c new file mode 100644 index 000000000000..6d46c4e2a4f7 --- /dev/null +++ b/arch/arm/plat-s3c24xx/devs.c | |||
@@ -0,0 +1,585 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/devs.c | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Base S3C24XX platform device definitions | ||
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 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/timer.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/serial_core.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | |||
23 | #include <asm/mach/arch.h> | ||
24 | #include <asm/mach/map.h> | ||
25 | #include <asm/mach/irq.h> | ||
26 | #include <asm/arch/fb.h> | ||
27 | #include <asm/hardware.h> | ||
28 | #include <asm/io.h> | ||
29 | #include <asm/irq.h> | ||
30 | |||
31 | #include <asm/arch/regs-serial.h> | ||
32 | |||
33 | #include <asm/plat-s3c24xx/devs.h> | ||
34 | #include <asm/plat-s3c24xx/cpu.h> | ||
35 | |||
36 | /* Serial port registrations */ | ||
37 | |||
38 | static struct resource s3c2410_uart0_resource[] = { | ||
39 | [0] = { | ||
40 | .start = S3C2410_PA_UART0, | ||
41 | .end = S3C2410_PA_UART0 + 0x3fff, | ||
42 | .flags = IORESOURCE_MEM, | ||
43 | }, | ||
44 | [1] = { | ||
45 | .start = IRQ_S3CUART_RX0, | ||
46 | .end = IRQ_S3CUART_ERR0, | ||
47 | .flags = IORESOURCE_IRQ, | ||
48 | } | ||
49 | }; | ||
50 | |||
51 | static struct resource s3c2410_uart1_resource[] = { | ||
52 | [0] = { | ||
53 | .start = S3C2410_PA_UART1, | ||
54 | .end = S3C2410_PA_UART1 + 0x3fff, | ||
55 | .flags = IORESOURCE_MEM, | ||
56 | }, | ||
57 | [1] = { | ||
58 | .start = IRQ_S3CUART_RX1, | ||
59 | .end = IRQ_S3CUART_ERR1, | ||
60 | .flags = IORESOURCE_IRQ, | ||
61 | } | ||
62 | }; | ||
63 | |||
64 | static struct resource s3c2410_uart2_resource[] = { | ||
65 | [0] = { | ||
66 | .start = S3C2410_PA_UART2, | ||
67 | .end = S3C2410_PA_UART2 + 0x3fff, | ||
68 | .flags = IORESOURCE_MEM, | ||
69 | }, | ||
70 | [1] = { | ||
71 | .start = IRQ_S3CUART_RX2, | ||
72 | .end = IRQ_S3CUART_ERR2, | ||
73 | .flags = IORESOURCE_IRQ, | ||
74 | } | ||
75 | }; | ||
76 | |||
77 | struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = { | ||
78 | [0] = { | ||
79 | .resources = s3c2410_uart0_resource, | ||
80 | .nr_resources = ARRAY_SIZE(s3c2410_uart0_resource), | ||
81 | }, | ||
82 | [1] = { | ||
83 | .resources = s3c2410_uart1_resource, | ||
84 | .nr_resources = ARRAY_SIZE(s3c2410_uart1_resource), | ||
85 | }, | ||
86 | [2] = { | ||
87 | .resources = s3c2410_uart2_resource, | ||
88 | .nr_resources = ARRAY_SIZE(s3c2410_uart2_resource), | ||
89 | }, | ||
90 | }; | ||
91 | |||
92 | /* yart devices */ | ||
93 | |||
94 | static struct platform_device s3c24xx_uart_device0 = { | ||
95 | .id = 0, | ||
96 | }; | ||
97 | |||
98 | static struct platform_device s3c24xx_uart_device1 = { | ||
99 | .id = 1, | ||
100 | }; | ||
101 | |||
102 | static struct platform_device s3c24xx_uart_device2 = { | ||
103 | .id = 2, | ||
104 | }; | ||
105 | |||
106 | struct platform_device *s3c24xx_uart_src[3] = { | ||
107 | &s3c24xx_uart_device0, | ||
108 | &s3c24xx_uart_device1, | ||
109 | &s3c24xx_uart_device2, | ||
110 | }; | ||
111 | |||
112 | struct platform_device *s3c24xx_uart_devs[3] = { | ||
113 | }; | ||
114 | |||
115 | /* USB Host Controller */ | ||
116 | |||
117 | static struct resource s3c_usb_resource[] = { | ||
118 | [0] = { | ||
119 | .start = S3C24XX_PA_USBHOST, | ||
120 | .end = S3C24XX_PA_USBHOST + S3C24XX_SZ_USBHOST - 1, | ||
121 | .flags = IORESOURCE_MEM, | ||
122 | }, | ||
123 | [1] = { | ||
124 | .start = IRQ_USBH, | ||
125 | .end = IRQ_USBH, | ||
126 | .flags = IORESOURCE_IRQ, | ||
127 | } | ||
128 | }; | ||
129 | |||
130 | static u64 s3c_device_usb_dmamask = 0xffffffffUL; | ||
131 | |||
132 | struct platform_device s3c_device_usb = { | ||
133 | .name = "s3c2410-ohci", | ||
134 | .id = -1, | ||
135 | .num_resources = ARRAY_SIZE(s3c_usb_resource), | ||
136 | .resource = s3c_usb_resource, | ||
137 | .dev = { | ||
138 | .dma_mask = &s3c_device_usb_dmamask, | ||
139 | .coherent_dma_mask = 0xffffffffUL | ||
140 | } | ||
141 | }; | ||
142 | |||
143 | EXPORT_SYMBOL(s3c_device_usb); | ||
144 | |||
145 | /* LCD Controller */ | ||
146 | |||
147 | static struct resource s3c_lcd_resource[] = { | ||
148 | [0] = { | ||
149 | .start = S3C24XX_PA_LCD, | ||
150 | .end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1, | ||
151 | .flags = IORESOURCE_MEM, | ||
152 | }, | ||
153 | [1] = { | ||
154 | .start = IRQ_LCD, | ||
155 | .end = IRQ_LCD, | ||
156 | .flags = IORESOURCE_IRQ, | ||
157 | } | ||
158 | |||
159 | }; | ||
160 | |||
161 | static u64 s3c_device_lcd_dmamask = 0xffffffffUL; | ||
162 | |||
163 | struct platform_device s3c_device_lcd = { | ||
164 | .name = "s3c2410-lcd", | ||
165 | .id = -1, | ||
166 | .num_resources = ARRAY_SIZE(s3c_lcd_resource), | ||
167 | .resource = s3c_lcd_resource, | ||
168 | .dev = { | ||
169 | .dma_mask = &s3c_device_lcd_dmamask, | ||
170 | .coherent_dma_mask = 0xffffffffUL | ||
171 | } | ||
172 | }; | ||
173 | |||
174 | EXPORT_SYMBOL(s3c_device_lcd); | ||
175 | |||
176 | void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd) | ||
177 | { | ||
178 | struct s3c2410fb_mach_info *npd; | ||
179 | |||
180 | npd = kmalloc(sizeof(*npd), GFP_KERNEL); | ||
181 | if (npd) { | ||
182 | memcpy(npd, pd, sizeof(*npd)); | ||
183 | s3c_device_lcd.dev.platform_data = npd; | ||
184 | } else { | ||
185 | printk(KERN_ERR "no memory for LCD platform data\n"); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | /* NAND Controller */ | ||
190 | |||
191 | static struct resource s3c_nand_resource[] = { | ||
192 | [0] = { | ||
193 | .start = S3C2410_PA_NAND, | ||
194 | .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1, | ||
195 | .flags = IORESOURCE_MEM, | ||
196 | } | ||
197 | }; | ||
198 | |||
199 | struct platform_device s3c_device_nand = { | ||
200 | .name = "s3c2410-nand", | ||
201 | .id = -1, | ||
202 | .num_resources = ARRAY_SIZE(s3c_nand_resource), | ||
203 | .resource = s3c_nand_resource, | ||
204 | }; | ||
205 | |||
206 | EXPORT_SYMBOL(s3c_device_nand); | ||
207 | |||
208 | /* USB Device (Gadget)*/ | ||
209 | |||
210 | static struct resource s3c_usbgadget_resource[] = { | ||
211 | [0] = { | ||
212 | .start = S3C24XX_PA_USBDEV, | ||
213 | .end = S3C24XX_PA_USBDEV + S3C24XX_SZ_USBDEV - 1, | ||
214 | .flags = IORESOURCE_MEM, | ||
215 | }, | ||
216 | [1] = { | ||
217 | .start = IRQ_USBD, | ||
218 | .end = IRQ_USBD, | ||
219 | .flags = IORESOURCE_IRQ, | ||
220 | } | ||
221 | |||
222 | }; | ||
223 | |||
224 | struct platform_device s3c_device_usbgadget = { | ||
225 | .name = "s3c2410-usbgadget", | ||
226 | .id = -1, | ||
227 | .num_resources = ARRAY_SIZE(s3c_usbgadget_resource), | ||
228 | .resource = s3c_usbgadget_resource, | ||
229 | }; | ||
230 | |||
231 | EXPORT_SYMBOL(s3c_device_usbgadget); | ||
232 | |||
233 | /* Watchdog */ | ||
234 | |||
235 | static struct resource s3c_wdt_resource[] = { | ||
236 | [0] = { | ||
237 | .start = S3C24XX_PA_WATCHDOG, | ||
238 | .end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1, | ||
239 | .flags = IORESOURCE_MEM, | ||
240 | }, | ||
241 | [1] = { | ||
242 | .start = IRQ_WDT, | ||
243 | .end = IRQ_WDT, | ||
244 | .flags = IORESOURCE_IRQ, | ||
245 | } | ||
246 | |||
247 | }; | ||
248 | |||
249 | struct platform_device s3c_device_wdt = { | ||
250 | .name = "s3c2410-wdt", | ||
251 | .id = -1, | ||
252 | .num_resources = ARRAY_SIZE(s3c_wdt_resource), | ||
253 | .resource = s3c_wdt_resource, | ||
254 | }; | ||
255 | |||
256 | EXPORT_SYMBOL(s3c_device_wdt); | ||
257 | |||
258 | /* I2C */ | ||
259 | |||
260 | static struct resource s3c_i2c_resource[] = { | ||
261 | [0] = { | ||
262 | .start = S3C24XX_PA_IIC, | ||
263 | .end = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1, | ||
264 | .flags = IORESOURCE_MEM, | ||
265 | }, | ||
266 | [1] = { | ||
267 | .start = IRQ_IIC, | ||
268 | .end = IRQ_IIC, | ||
269 | .flags = IORESOURCE_IRQ, | ||
270 | } | ||
271 | |||
272 | }; | ||
273 | |||
274 | struct platform_device s3c_device_i2c = { | ||
275 | .name = "s3c2410-i2c", | ||
276 | .id = -1, | ||
277 | .num_resources = ARRAY_SIZE(s3c_i2c_resource), | ||
278 | .resource = s3c_i2c_resource, | ||
279 | }; | ||
280 | |||
281 | EXPORT_SYMBOL(s3c_device_i2c); | ||
282 | |||
283 | /* IIS */ | ||
284 | |||
285 | static struct resource s3c_iis_resource[] = { | ||
286 | [0] = { | ||
287 | .start = S3C24XX_PA_IIS, | ||
288 | .end = S3C24XX_PA_IIS + S3C24XX_SZ_IIS -1, | ||
289 | .flags = IORESOURCE_MEM, | ||
290 | } | ||
291 | }; | ||
292 | |||
293 | static u64 s3c_device_iis_dmamask = 0xffffffffUL; | ||
294 | |||
295 | struct platform_device s3c_device_iis = { | ||
296 | .name = "s3c2410-iis", | ||
297 | .id = -1, | ||
298 | .num_resources = ARRAY_SIZE(s3c_iis_resource), | ||
299 | .resource = s3c_iis_resource, | ||
300 | .dev = { | ||
301 | .dma_mask = &s3c_device_iis_dmamask, | ||
302 | .coherent_dma_mask = 0xffffffffUL | ||
303 | } | ||
304 | }; | ||
305 | |||
306 | EXPORT_SYMBOL(s3c_device_iis); | ||
307 | |||
308 | /* RTC */ | ||
309 | |||
310 | static struct resource s3c_rtc_resource[] = { | ||
311 | [0] = { | ||
312 | .start = S3C24XX_PA_RTC, | ||
313 | .end = S3C24XX_PA_RTC + 0xff, | ||
314 | .flags = IORESOURCE_MEM, | ||
315 | }, | ||
316 | [1] = { | ||
317 | .start = IRQ_RTC, | ||
318 | .end = IRQ_RTC, | ||
319 | .flags = IORESOURCE_IRQ, | ||
320 | }, | ||
321 | [2] = { | ||
322 | .start = IRQ_TICK, | ||
323 | .end = IRQ_TICK, | ||
324 | .flags = IORESOURCE_IRQ | ||
325 | } | ||
326 | }; | ||
327 | |||
328 | struct platform_device s3c_device_rtc = { | ||
329 | .name = "s3c2410-rtc", | ||
330 | .id = -1, | ||
331 | .num_resources = ARRAY_SIZE(s3c_rtc_resource), | ||
332 | .resource = s3c_rtc_resource, | ||
333 | }; | ||
334 | |||
335 | EXPORT_SYMBOL(s3c_device_rtc); | ||
336 | |||
337 | /* ADC */ | ||
338 | |||
339 | static struct resource s3c_adc_resource[] = { | ||
340 | [0] = { | ||
341 | .start = S3C24XX_PA_ADC, | ||
342 | .end = S3C24XX_PA_ADC + S3C24XX_SZ_ADC - 1, | ||
343 | .flags = IORESOURCE_MEM, | ||
344 | }, | ||
345 | [1] = { | ||
346 | .start = IRQ_TC, | ||
347 | .end = IRQ_TC, | ||
348 | .flags = IORESOURCE_IRQ, | ||
349 | }, | ||
350 | [2] = { | ||
351 | .start = IRQ_ADC, | ||
352 | .end = IRQ_ADC, | ||
353 | .flags = IORESOURCE_IRQ, | ||
354 | } | ||
355 | |||
356 | }; | ||
357 | |||
358 | struct platform_device s3c_device_adc = { | ||
359 | .name = "s3c2410-adc", | ||
360 | .id = -1, | ||
361 | .num_resources = ARRAY_SIZE(s3c_adc_resource), | ||
362 | .resource = s3c_adc_resource, | ||
363 | }; | ||
364 | |||
365 | /* SDI */ | ||
366 | |||
367 | static struct resource s3c_sdi_resource[] = { | ||
368 | [0] = { | ||
369 | .start = S3C2410_PA_SDI, | ||
370 | .end = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1, | ||
371 | .flags = IORESOURCE_MEM, | ||
372 | }, | ||
373 | [1] = { | ||
374 | .start = IRQ_SDI, | ||
375 | .end = IRQ_SDI, | ||
376 | .flags = IORESOURCE_IRQ, | ||
377 | } | ||
378 | |||
379 | }; | ||
380 | |||
381 | struct platform_device s3c_device_sdi = { | ||
382 | .name = "s3c2410-sdi", | ||
383 | .id = -1, | ||
384 | .num_resources = ARRAY_SIZE(s3c_sdi_resource), | ||
385 | .resource = s3c_sdi_resource, | ||
386 | }; | ||
387 | |||
388 | EXPORT_SYMBOL(s3c_device_sdi); | ||
389 | |||
390 | /* SPI (0) */ | ||
391 | |||
392 | static struct resource s3c_spi0_resource[] = { | ||
393 | [0] = { | ||
394 | .start = S3C24XX_PA_SPI, | ||
395 | .end = S3C24XX_PA_SPI + 0x1f, | ||
396 | .flags = IORESOURCE_MEM, | ||
397 | }, | ||
398 | [1] = { | ||
399 | .start = IRQ_SPI0, | ||
400 | .end = IRQ_SPI0, | ||
401 | .flags = IORESOURCE_IRQ, | ||
402 | } | ||
403 | |||
404 | }; | ||
405 | |||
406 | static u64 s3c_device_spi0_dmamask = 0xffffffffUL; | ||
407 | |||
408 | struct platform_device s3c_device_spi0 = { | ||
409 | .name = "s3c2410-spi", | ||
410 | .id = 0, | ||
411 | .num_resources = ARRAY_SIZE(s3c_spi0_resource), | ||
412 | .resource = s3c_spi0_resource, | ||
413 | .dev = { | ||
414 | .dma_mask = &s3c_device_spi0_dmamask, | ||
415 | .coherent_dma_mask = 0xffffffffUL | ||
416 | } | ||
417 | }; | ||
418 | |||
419 | EXPORT_SYMBOL(s3c_device_spi0); | ||
420 | |||
421 | /* SPI (1) */ | ||
422 | |||
423 | static struct resource s3c_spi1_resource[] = { | ||
424 | [0] = { | ||
425 | .start = S3C24XX_PA_SPI + 0x20, | ||
426 | .end = S3C24XX_PA_SPI + 0x20 + 0x1f, | ||
427 | .flags = IORESOURCE_MEM, | ||
428 | }, | ||
429 | [1] = { | ||
430 | .start = IRQ_SPI1, | ||
431 | .end = IRQ_SPI1, | ||
432 | .flags = IORESOURCE_IRQ, | ||
433 | } | ||
434 | |||
435 | }; | ||
436 | |||
437 | static u64 s3c_device_spi1_dmamask = 0xffffffffUL; | ||
438 | |||
439 | struct platform_device s3c_device_spi1 = { | ||
440 | .name = "s3c2410-spi", | ||
441 | .id = 1, | ||
442 | .num_resources = ARRAY_SIZE(s3c_spi1_resource), | ||
443 | .resource = s3c_spi1_resource, | ||
444 | .dev = { | ||
445 | .dma_mask = &s3c_device_spi1_dmamask, | ||
446 | .coherent_dma_mask = 0xffffffffUL | ||
447 | } | ||
448 | }; | ||
449 | |||
450 | EXPORT_SYMBOL(s3c_device_spi1); | ||
451 | |||
452 | /* pwm timer blocks */ | ||
453 | |||
454 | static struct resource s3c_timer0_resource[] = { | ||
455 | [0] = { | ||
456 | .start = S3C24XX_PA_TIMER + 0x0C, | ||
457 | .end = S3C24XX_PA_TIMER + 0x0C + 0xB, | ||
458 | .flags = IORESOURCE_MEM, | ||
459 | }, | ||
460 | [1] = { | ||
461 | .start = IRQ_TIMER0, | ||
462 | .end = IRQ_TIMER0, | ||
463 | .flags = IORESOURCE_IRQ, | ||
464 | } | ||
465 | |||
466 | }; | ||
467 | |||
468 | struct platform_device s3c_device_timer0 = { | ||
469 | .name = "s3c2410-timer", | ||
470 | .id = 0, | ||
471 | .num_resources = ARRAY_SIZE(s3c_timer0_resource), | ||
472 | .resource = s3c_timer0_resource, | ||
473 | }; | ||
474 | |||
475 | EXPORT_SYMBOL(s3c_device_timer0); | ||
476 | |||
477 | /* timer 1 */ | ||
478 | |||
479 | static struct resource s3c_timer1_resource[] = { | ||
480 | [0] = { | ||
481 | .start = S3C24XX_PA_TIMER + 0x18, | ||
482 | .end = S3C24XX_PA_TIMER + 0x23, | ||
483 | .flags = IORESOURCE_MEM, | ||
484 | }, | ||
485 | [1] = { | ||
486 | .start = IRQ_TIMER1, | ||
487 | .end = IRQ_TIMER1, | ||
488 | .flags = IORESOURCE_IRQ, | ||
489 | } | ||
490 | |||
491 | }; | ||
492 | |||
493 | struct platform_device s3c_device_timer1 = { | ||
494 | .name = "s3c2410-timer", | ||
495 | .id = 1, | ||
496 | .num_resources = ARRAY_SIZE(s3c_timer1_resource), | ||
497 | .resource = s3c_timer1_resource, | ||
498 | }; | ||
499 | |||
500 | EXPORT_SYMBOL(s3c_device_timer1); | ||
501 | |||
502 | /* timer 2 */ | ||
503 | |||
504 | static struct resource s3c_timer2_resource[] = { | ||
505 | [0] = { | ||
506 | .start = S3C24XX_PA_TIMER + 0x24, | ||
507 | .end = S3C24XX_PA_TIMER + 0x2F, | ||
508 | .flags = IORESOURCE_MEM, | ||
509 | }, | ||
510 | [1] = { | ||
511 | .start = IRQ_TIMER2, | ||
512 | .end = IRQ_TIMER2, | ||
513 | .flags = IORESOURCE_IRQ, | ||
514 | } | ||
515 | |||
516 | }; | ||
517 | |||
518 | struct platform_device s3c_device_timer2 = { | ||
519 | .name = "s3c2410-timer", | ||
520 | .id = 2, | ||
521 | .num_resources = ARRAY_SIZE(s3c_timer2_resource), | ||
522 | .resource = s3c_timer2_resource, | ||
523 | }; | ||
524 | |||
525 | EXPORT_SYMBOL(s3c_device_timer2); | ||
526 | |||
527 | /* timer 3 */ | ||
528 | |||
529 | static struct resource s3c_timer3_resource[] = { | ||
530 | [0] = { | ||
531 | .start = S3C24XX_PA_TIMER + 0x30, | ||
532 | .end = S3C24XX_PA_TIMER + 0x3B, | ||
533 | .flags = IORESOURCE_MEM, | ||
534 | }, | ||
535 | [1] = { | ||
536 | .start = IRQ_TIMER3, | ||
537 | .end = IRQ_TIMER3, | ||
538 | .flags = IORESOURCE_IRQ, | ||
539 | } | ||
540 | |||
541 | }; | ||
542 | |||
543 | struct platform_device s3c_device_timer3 = { | ||
544 | .name = "s3c2410-timer", | ||
545 | .id = 3, | ||
546 | .num_resources = ARRAY_SIZE(s3c_timer3_resource), | ||
547 | .resource = s3c_timer3_resource, | ||
548 | }; | ||
549 | |||
550 | EXPORT_SYMBOL(s3c_device_timer3); | ||
551 | |||
552 | #ifdef CONFIG_CPU_S3C2440 | ||
553 | |||
554 | /* Camif Controller */ | ||
555 | |||
556 | static struct resource s3c_camif_resource[] = { | ||
557 | [0] = { | ||
558 | .start = S3C2440_PA_CAMIF, | ||
559 | .end = S3C2440_PA_CAMIF + S3C2440_SZ_CAMIF - 1, | ||
560 | .flags = IORESOURCE_MEM, | ||
561 | }, | ||
562 | [1] = { | ||
563 | .start = IRQ_CAM, | ||
564 | .end = IRQ_CAM, | ||
565 | .flags = IORESOURCE_IRQ, | ||
566 | } | ||
567 | |||
568 | }; | ||
569 | |||
570 | static u64 s3c_device_camif_dmamask = 0xffffffffUL; | ||
571 | |||
572 | struct platform_device s3c_device_camif = { | ||
573 | .name = "s3c2440-camif", | ||
574 | .id = -1, | ||
575 | .num_resources = ARRAY_SIZE(s3c_camif_resource), | ||
576 | .resource = s3c_camif_resource, | ||
577 | .dev = { | ||
578 | .dma_mask = &s3c_device_camif_dmamask, | ||
579 | .coherent_dma_mask = 0xffffffffUL | ||
580 | } | ||
581 | }; | ||
582 | |||
583 | EXPORT_SYMBOL(s3c_device_camif); | ||
584 | |||
585 | #endif // CONFIG_CPU_S32440 | ||
diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c new file mode 100644 index 000000000000..c784e1f816bb --- /dev/null +++ b/arch/arm/plat-s3c24xx/dma.c | |||
@@ -0,0 +1,1441 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/dma.c | ||
2 | * | ||
3 | * Copyright (c) 2003-2005,2006 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C2410 DMA core | ||
7 | * | ||
8 | * http://armlinux.simtec.co.uk/ | ||
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 version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | |||
16 | #ifdef CONFIG_S3C2410_DMA_DEBUG | ||
17 | #define DEBUG | ||
18 | #endif | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/sysdev.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/delay.h> | ||
29 | |||
30 | #include <asm/system.h> | ||
31 | #include <asm/irq.h> | ||
32 | #include <asm/hardware.h> | ||
33 | #include <asm/io.h> | ||
34 | #include <asm/dma.h> | ||
35 | |||
36 | #include <asm/mach/dma.h> | ||
37 | #include <asm/arch/map.h> | ||
38 | |||
39 | #include <asm/plat-s3c24xx/dma.h> | ||
40 | |||
41 | /* io map for dma */ | ||
42 | static void __iomem *dma_base; | ||
43 | static struct kmem_cache *dma_kmem; | ||
44 | |||
45 | struct s3c24xx_dma_selection dma_sel; | ||
46 | |||
47 | /* dma channel state information */ | ||
48 | struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; | ||
49 | |||
50 | /* debugging functions */ | ||
51 | |||
52 | #define BUF_MAGIC (0xcafebabe) | ||
53 | |||
54 | #define dmawarn(fmt...) printk(KERN_DEBUG fmt) | ||
55 | |||
56 | #define dma_regaddr(chan, reg) ((chan)->regs + (reg)) | ||
57 | |||
58 | #if 1 | ||
59 | #define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg)) | ||
60 | #else | ||
61 | static inline void | ||
62 | dma_wrreg(struct s3c2410_dma_chan *chan, int reg, unsigned long val) | ||
63 | { | ||
64 | pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg); | ||
65 | writel(val, dma_regaddr(chan, reg)); | ||
66 | } | ||
67 | #endif | ||
68 | |||
69 | #define dma_rdreg(chan, reg) readl((chan)->regs + (reg)) | ||
70 | |||
71 | /* captured register state for debug */ | ||
72 | |||
73 | struct s3c2410_dma_regstate { | ||
74 | unsigned long dcsrc; | ||
75 | unsigned long disrc; | ||
76 | unsigned long dstat; | ||
77 | unsigned long dcon; | ||
78 | unsigned long dmsktrig; | ||
79 | }; | ||
80 | |||
81 | #ifdef CONFIG_S3C2410_DMA_DEBUG | ||
82 | |||
83 | /* dmadbg_showregs | ||
84 | * | ||
85 | * simple debug routine to print the current state of the dma registers | ||
86 | */ | ||
87 | |||
88 | static void | ||
89 | dmadbg_capture(struct s3c2410_dma_chan *chan, struct s3c2410_dma_regstate *regs) | ||
90 | { | ||
91 | regs->dcsrc = dma_rdreg(chan, S3C2410_DMA_DCSRC); | ||
92 | regs->disrc = dma_rdreg(chan, S3C2410_DMA_DISRC); | ||
93 | regs->dstat = dma_rdreg(chan, S3C2410_DMA_DSTAT); | ||
94 | regs->dcon = dma_rdreg(chan, S3C2410_DMA_DCON); | ||
95 | regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | ||
96 | } | ||
97 | |||
98 | static void | ||
99 | dmadbg_dumpregs(const char *fname, int line, struct s3c2410_dma_chan *chan, | ||
100 | struct s3c2410_dma_regstate *regs) | ||
101 | { | ||
102 | printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n", | ||
103 | chan->number, fname, line, | ||
104 | regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig, | ||
105 | regs->dcon); | ||
106 | } | ||
107 | |||
108 | static void | ||
109 | dmadbg_showchan(const char *fname, int line, struct s3c2410_dma_chan *chan) | ||
110 | { | ||
111 | struct s3c2410_dma_regstate state; | ||
112 | |||
113 | dmadbg_capture(chan, &state); | ||
114 | |||
115 | printk(KERN_DEBUG "dma%d: %s:%d: ls=%d, cur=%p, %p %p\n", | ||
116 | chan->number, fname, line, chan->load_state, | ||
117 | chan->curr, chan->next, chan->end); | ||
118 | |||
119 | dmadbg_dumpregs(fname, line, chan, &state); | ||
120 | } | ||
121 | |||
122 | static void | ||
123 | dmadbg_showregs(const char *fname, int line, struct s3c2410_dma_chan *chan) | ||
124 | { | ||
125 | struct s3c2410_dma_regstate state; | ||
126 | |||
127 | dmadbg_capture(chan, &state); | ||
128 | dmadbg_dumpregs(fname, line, chan, &state); | ||
129 | } | ||
130 | |||
131 | #define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan)) | ||
132 | #define dbg_showchan(chan) dmadbg_showchan(__FUNCTION__, __LINE__, (chan)) | ||
133 | #else | ||
134 | #define dbg_showregs(chan) do { } while(0) | ||
135 | #define dbg_showchan(chan) do { } while(0) | ||
136 | #endif /* CONFIG_S3C2410_DMA_DEBUG */ | ||
137 | |||
138 | static struct s3c2410_dma_chan *dma_chan_map[DMACH_MAX]; | ||
139 | |||
140 | /* lookup_dma_channel | ||
141 | * | ||
142 | * change the dma channel number given into a real dma channel id | ||
143 | */ | ||
144 | |||
145 | static struct s3c2410_dma_chan *lookup_dma_channel(unsigned int channel) | ||
146 | { | ||
147 | if (channel & DMACH_LOW_LEVEL) | ||
148 | return &s3c2410_chans[channel & ~DMACH_LOW_LEVEL]; | ||
149 | else | ||
150 | return dma_chan_map[channel]; | ||
151 | } | ||
152 | |||
153 | /* s3c2410_dma_stats_timeout | ||
154 | * | ||
155 | * Update DMA stats from timeout info | ||
156 | */ | ||
157 | |||
158 | static void | ||
159 | s3c2410_dma_stats_timeout(struct s3c2410_dma_stats *stats, int val) | ||
160 | { | ||
161 | if (stats == NULL) | ||
162 | return; | ||
163 | |||
164 | if (val > stats->timeout_longest) | ||
165 | stats->timeout_longest = val; | ||
166 | if (val < stats->timeout_shortest) | ||
167 | stats->timeout_shortest = val; | ||
168 | |||
169 | stats->timeout_avg += val; | ||
170 | } | ||
171 | |||
172 | /* s3c2410_dma_waitforload | ||
173 | * | ||
174 | * wait for the DMA engine to load a buffer, and update the state accordingly | ||
175 | */ | ||
176 | |||
177 | static int | ||
178 | s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line) | ||
179 | { | ||
180 | int timeout = chan->load_timeout; | ||
181 | int took; | ||
182 | |||
183 | if (chan->load_state != S3C2410_DMALOAD_1LOADED) { | ||
184 | printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line); | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | if (chan->stats != NULL) | ||
189 | chan->stats->loads++; | ||
190 | |||
191 | while (--timeout > 0) { | ||
192 | if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) { | ||
193 | took = chan->load_timeout - timeout; | ||
194 | |||
195 | s3c2410_dma_stats_timeout(chan->stats, took); | ||
196 | |||
197 | switch (chan->load_state) { | ||
198 | case S3C2410_DMALOAD_1LOADED: | ||
199 | chan->load_state = S3C2410_DMALOAD_1RUNNING; | ||
200 | break; | ||
201 | |||
202 | default: | ||
203 | printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state); | ||
204 | } | ||
205 | |||
206 | return 1; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | if (chan->stats != NULL) { | ||
211 | chan->stats->timeout_failed++; | ||
212 | } | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | |||
218 | |||
219 | /* s3c2410_dma_loadbuffer | ||
220 | * | ||
221 | * load a buffer, and update the channel state | ||
222 | */ | ||
223 | |||
224 | static inline int | ||
225 | s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan, | ||
226 | struct s3c2410_dma_buf *buf) | ||
227 | { | ||
228 | unsigned long reload; | ||
229 | |||
230 | pr_debug("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n", | ||
231 | buf, (unsigned long)buf->data, buf->size); | ||
232 | |||
233 | if (buf == NULL) { | ||
234 | dmawarn("buffer is NULL\n"); | ||
235 | return -EINVAL; | ||
236 | } | ||
237 | |||
238 | /* check the state of the channel before we do anything */ | ||
239 | |||
240 | if (chan->load_state == S3C2410_DMALOAD_1LOADED) { | ||
241 | dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n"); | ||
242 | } | ||
243 | |||
244 | if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) { | ||
245 | dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n"); | ||
246 | } | ||
247 | |||
248 | /* it would seem sensible if we are the last buffer to not bother | ||
249 | * with the auto-reload bit, so that the DMA engine will not try | ||
250 | * and load another transfer after this one has finished... | ||
251 | */ | ||
252 | if (chan->load_state == S3C2410_DMALOAD_NONE) { | ||
253 | pr_debug("load_state is none, checking for noreload (next=%p)\n", | ||
254 | buf->next); | ||
255 | reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0; | ||
256 | } else { | ||
257 | //pr_debug("load_state is %d => autoreload\n", chan->load_state); | ||
258 | reload = S3C2410_DCON_AUTORELOAD; | ||
259 | } | ||
260 | |||
261 | if ((buf->data & 0xf0000000) != 0x30000000) { | ||
262 | dmawarn("dmaload: buffer is %p\n", (void *)buf->data); | ||
263 | } | ||
264 | |||
265 | writel(buf->data, chan->addr_reg); | ||
266 | |||
267 | dma_wrreg(chan, S3C2410_DMA_DCON, | ||
268 | chan->dcon | reload | (buf->size/chan->xfer_unit)); | ||
269 | |||
270 | chan->next = buf->next; | ||
271 | |||
272 | /* update the state of the channel */ | ||
273 | |||
274 | switch (chan->load_state) { | ||
275 | case S3C2410_DMALOAD_NONE: | ||
276 | chan->load_state = S3C2410_DMALOAD_1LOADED; | ||
277 | break; | ||
278 | |||
279 | case S3C2410_DMALOAD_1RUNNING: | ||
280 | chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING; | ||
281 | break; | ||
282 | |||
283 | default: | ||
284 | dmawarn("dmaload: unknown state %d in loadbuffer\n", | ||
285 | chan->load_state); | ||
286 | break; | ||
287 | } | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | /* s3c2410_dma_call_op | ||
293 | * | ||
294 | * small routine to call the op routine with the given op if it has been | ||
295 | * registered | ||
296 | */ | ||
297 | |||
298 | static void | ||
299 | s3c2410_dma_call_op(struct s3c2410_dma_chan *chan, enum s3c2410_chan_op op) | ||
300 | { | ||
301 | if (chan->op_fn != NULL) { | ||
302 | (chan->op_fn)(chan, op); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | /* s3c2410_dma_buffdone | ||
307 | * | ||
308 | * small wrapper to check if callback routine needs to be called, and | ||
309 | * if so, call it | ||
310 | */ | ||
311 | |||
312 | static inline void | ||
313 | s3c2410_dma_buffdone(struct s3c2410_dma_chan *chan, struct s3c2410_dma_buf *buf, | ||
314 | enum s3c2410_dma_buffresult result) | ||
315 | { | ||
316 | #if 0 | ||
317 | pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n", | ||
318 | chan->callback_fn, buf, buf->id, buf->size, result); | ||
319 | #endif | ||
320 | |||
321 | if (chan->callback_fn != NULL) { | ||
322 | (chan->callback_fn)(chan, buf->id, buf->size, result); | ||
323 | } | ||
324 | } | ||
325 | |||
326 | /* s3c2410_dma_start | ||
327 | * | ||
328 | * start a dma channel going | ||
329 | */ | ||
330 | |||
331 | static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) | ||
332 | { | ||
333 | unsigned long tmp; | ||
334 | unsigned long flags; | ||
335 | |||
336 | pr_debug("s3c2410_start_dma: channel=%d\n", chan->number); | ||
337 | |||
338 | local_irq_save(flags); | ||
339 | |||
340 | if (chan->state == S3C2410_DMA_RUNNING) { | ||
341 | pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state); | ||
342 | local_irq_restore(flags); | ||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | chan->state = S3C2410_DMA_RUNNING; | ||
347 | |||
348 | /* check wether there is anything to load, and if not, see | ||
349 | * if we can find anything to load | ||
350 | */ | ||
351 | |||
352 | if (chan->load_state == S3C2410_DMALOAD_NONE) { | ||
353 | if (chan->next == NULL) { | ||
354 | printk(KERN_ERR "dma%d: channel has nothing loaded\n", | ||
355 | chan->number); | ||
356 | chan->state = S3C2410_DMA_IDLE; | ||
357 | local_irq_restore(flags); | ||
358 | return -EINVAL; | ||
359 | } | ||
360 | |||
361 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
362 | } | ||
363 | |||
364 | dbg_showchan(chan); | ||
365 | |||
366 | /* enable the channel */ | ||
367 | |||
368 | if (!chan->irq_enabled) { | ||
369 | enable_irq(chan->irq); | ||
370 | chan->irq_enabled = 1; | ||
371 | } | ||
372 | |||
373 | /* start the channel going */ | ||
374 | |||
375 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | ||
376 | tmp &= ~S3C2410_DMASKTRIG_STOP; | ||
377 | tmp |= S3C2410_DMASKTRIG_ON; | ||
378 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); | ||
379 | |||
380 | pr_debug("dma%d: %08lx to DMASKTRIG\n", chan->number, tmp); | ||
381 | |||
382 | #if 0 | ||
383 | /* the dma buffer loads should take care of clearing the AUTO | ||
384 | * reloading feature */ | ||
385 | tmp = dma_rdreg(chan, S3C2410_DMA_DCON); | ||
386 | tmp &= ~S3C2410_DCON_NORELOAD; | ||
387 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); | ||
388 | #endif | ||
389 | |||
390 | s3c2410_dma_call_op(chan, S3C2410_DMAOP_START); | ||
391 | |||
392 | dbg_showchan(chan); | ||
393 | |||
394 | /* if we've only loaded one buffer onto the channel, then chec | ||
395 | * to see if we have another, and if so, try and load it so when | ||
396 | * the first buffer is finished, the new one will be loaded onto | ||
397 | * the channel */ | ||
398 | |||
399 | if (chan->next != NULL) { | ||
400 | if (chan->load_state == S3C2410_DMALOAD_1LOADED) { | ||
401 | |||
402 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
403 | pr_debug("%s: buff not yet loaded, no more todo\n", | ||
404 | __FUNCTION__); | ||
405 | } else { | ||
406 | chan->load_state = S3C2410_DMALOAD_1RUNNING; | ||
407 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
408 | } | ||
409 | |||
410 | } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { | ||
411 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
412 | } | ||
413 | } | ||
414 | |||
415 | |||
416 | local_irq_restore(flags); | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | /* s3c2410_dma_canload | ||
422 | * | ||
423 | * work out if we can queue another buffer into the DMA engine | ||
424 | */ | ||
425 | |||
426 | static int | ||
427 | s3c2410_dma_canload(struct s3c2410_dma_chan *chan) | ||
428 | { | ||
429 | if (chan->load_state == S3C2410_DMALOAD_NONE || | ||
430 | chan->load_state == S3C2410_DMALOAD_1RUNNING) | ||
431 | return 1; | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /* s3c2410_dma_enqueue | ||
437 | * | ||
438 | * queue an given buffer for dma transfer. | ||
439 | * | ||
440 | * id the device driver's id information for this buffer | ||
441 | * data the physical address of the buffer data | ||
442 | * size the size of the buffer in bytes | ||
443 | * | ||
444 | * If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART | ||
445 | * is checked, and if set, the channel is started. If this flag isn't set, | ||
446 | * then an error will be returned. | ||
447 | * | ||
448 | * It is possible to queue more than one DMA buffer onto a channel at | ||
449 | * once, and the code will deal with the re-loading of the next buffer | ||
450 | * when necessary. | ||
451 | */ | ||
452 | |||
453 | int s3c2410_dma_enqueue(unsigned int channel, void *id, | ||
454 | dma_addr_t data, int size) | ||
455 | { | ||
456 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); | ||
457 | struct s3c2410_dma_buf *buf; | ||
458 | unsigned long flags; | ||
459 | |||
460 | if (chan == NULL) | ||
461 | return -EINVAL; | ||
462 | |||
463 | pr_debug("%s: id=%p, data=%08x, size=%d\n", | ||
464 | __FUNCTION__, id, (unsigned int)data, size); | ||
465 | |||
466 | buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); | ||
467 | if (buf == NULL) { | ||
468 | pr_debug("%s: out of memory (%ld alloc)\n", | ||
469 | __FUNCTION__, (long)sizeof(*buf)); | ||
470 | return -ENOMEM; | ||
471 | } | ||
472 | |||
473 | //pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); | ||
474 | //dbg_showchan(chan); | ||
475 | |||
476 | buf->next = NULL; | ||
477 | buf->data = buf->ptr = data; | ||
478 | buf->size = size; | ||
479 | buf->id = id; | ||
480 | buf->magic = BUF_MAGIC; | ||
481 | |||
482 | local_irq_save(flags); | ||
483 | |||
484 | if (chan->curr == NULL) { | ||
485 | /* we've got nothing loaded... */ | ||
486 | pr_debug("%s: buffer %p queued onto empty channel\n", | ||
487 | __FUNCTION__, buf); | ||
488 | |||
489 | chan->curr = buf; | ||
490 | chan->end = buf; | ||
491 | chan->next = NULL; | ||
492 | } else { | ||
493 | pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n", | ||
494 | chan->number, __FUNCTION__, buf); | ||
495 | |||
496 | if (chan->end == NULL) | ||
497 | pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n", | ||
498 | chan->number, __FUNCTION__, chan); | ||
499 | |||
500 | chan->end->next = buf; | ||
501 | chan->end = buf; | ||
502 | } | ||
503 | |||
504 | /* if necessary, update the next buffer field */ | ||
505 | if (chan->next == NULL) | ||
506 | chan->next = buf; | ||
507 | |||
508 | /* check to see if we can load a buffer */ | ||
509 | if (chan->state == S3C2410_DMA_RUNNING) { | ||
510 | if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { | ||
511 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
512 | printk(KERN_ERR "dma%d: loadbuffer:" | ||
513 | "timeout loading buffer\n", | ||
514 | chan->number); | ||
515 | dbg_showchan(chan); | ||
516 | local_irq_restore(flags); | ||
517 | return -EINVAL; | ||
518 | } | ||
519 | } | ||
520 | |||
521 | while (s3c2410_dma_canload(chan) && chan->next != NULL) { | ||
522 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
523 | } | ||
524 | } else if (chan->state == S3C2410_DMA_IDLE) { | ||
525 | if (chan->flags & S3C2410_DMAF_AUTOSTART) { | ||
526 | s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START); | ||
527 | } | ||
528 | } | ||
529 | |||
530 | local_irq_restore(flags); | ||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | EXPORT_SYMBOL(s3c2410_dma_enqueue); | ||
535 | |||
536 | static inline void | ||
537 | s3c2410_dma_freebuf(struct s3c2410_dma_buf *buf) | ||
538 | { | ||
539 | int magicok = (buf->magic == BUF_MAGIC); | ||
540 | |||
541 | buf->magic = -1; | ||
542 | |||
543 | if (magicok) { | ||
544 | kmem_cache_free(dma_kmem, buf); | ||
545 | } else { | ||
546 | printk("s3c2410_dma_freebuf: buff %p with bad magic\n", buf); | ||
547 | } | ||
548 | } | ||
549 | |||
550 | /* s3c2410_dma_lastxfer | ||
551 | * | ||
552 | * called when the system is out of buffers, to ensure that the channel | ||
553 | * is prepared for shutdown. | ||
554 | */ | ||
555 | |||
556 | static inline void | ||
557 | s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan) | ||
558 | { | ||
559 | #if 0 | ||
560 | pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", | ||
561 | chan->number, chan->load_state); | ||
562 | #endif | ||
563 | |||
564 | switch (chan->load_state) { | ||
565 | case S3C2410_DMALOAD_NONE: | ||
566 | break; | ||
567 | |||
568 | case S3C2410_DMALOAD_1LOADED: | ||
569 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
570 | /* flag error? */ | ||
571 | printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", | ||
572 | chan->number, __FUNCTION__); | ||
573 | return; | ||
574 | } | ||
575 | break; | ||
576 | |||
577 | case S3C2410_DMALOAD_1LOADED_1RUNNING: | ||
578 | /* I belive in this case we do not have anything to do | ||
579 | * until the next buffer comes along, and we turn off the | ||
580 | * reload */ | ||
581 | return; | ||
582 | |||
583 | default: | ||
584 | pr_debug("dma%d: lastxfer: unhandled load_state %d with no next\n", | ||
585 | chan->number, chan->load_state); | ||
586 | return; | ||
587 | |||
588 | } | ||
589 | |||
590 | /* hopefully this'll shut the damned thing up after the transfer... */ | ||
591 | dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD); | ||
592 | } | ||
593 | |||
594 | |||
595 | #define dmadbg2(x...) | ||
596 | |||
597 | static irqreturn_t | ||
598 | s3c2410_dma_irq(int irq, void *devpw) | ||
599 | { | ||
600 | struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; | ||
601 | struct s3c2410_dma_buf *buf; | ||
602 | |||
603 | buf = chan->curr; | ||
604 | |||
605 | dbg_showchan(chan); | ||
606 | |||
607 | /* modify the channel state */ | ||
608 | |||
609 | switch (chan->load_state) { | ||
610 | case S3C2410_DMALOAD_1RUNNING: | ||
611 | /* TODO - if we are running only one buffer, we probably | ||
612 | * want to reload here, and then worry about the buffer | ||
613 | * callback */ | ||
614 | |||
615 | chan->load_state = S3C2410_DMALOAD_NONE; | ||
616 | break; | ||
617 | |||
618 | case S3C2410_DMALOAD_1LOADED: | ||
619 | /* iirc, we should go back to NONE loaded here, we | ||
620 | * had a buffer, and it was never verified as being | ||
621 | * loaded. | ||
622 | */ | ||
623 | |||
624 | chan->load_state = S3C2410_DMALOAD_NONE; | ||
625 | break; | ||
626 | |||
627 | case S3C2410_DMALOAD_1LOADED_1RUNNING: | ||
628 | /* we'll worry about checking to see if another buffer is | ||
629 | * ready after we've called back the owner. This should | ||
630 | * ensure we do not wait around too long for the DMA | ||
631 | * engine to start the next transfer | ||
632 | */ | ||
633 | |||
634 | chan->load_state = S3C2410_DMALOAD_1LOADED; | ||
635 | break; | ||
636 | |||
637 | case S3C2410_DMALOAD_NONE: | ||
638 | printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n", | ||
639 | chan->number); | ||
640 | break; | ||
641 | |||
642 | default: | ||
643 | printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n", | ||
644 | chan->number, chan->load_state); | ||
645 | break; | ||
646 | } | ||
647 | |||
648 | if (buf != NULL) { | ||
649 | /* update the chain to make sure that if we load any more | ||
650 | * buffers when we call the callback function, things should | ||
651 | * work properly */ | ||
652 | |||
653 | chan->curr = buf->next; | ||
654 | buf->next = NULL; | ||
655 | |||
656 | if (buf->magic != BUF_MAGIC) { | ||
657 | printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n", | ||
658 | chan->number, __FUNCTION__, buf); | ||
659 | return IRQ_HANDLED; | ||
660 | } | ||
661 | |||
662 | s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK); | ||
663 | |||
664 | /* free resouces */ | ||
665 | s3c2410_dma_freebuf(buf); | ||
666 | } else { | ||
667 | } | ||
668 | |||
669 | /* only reload if the channel is still running... our buffer done | ||
670 | * routine may have altered the state by requesting the dma channel | ||
671 | * to stop or shutdown... */ | ||
672 | |||
673 | /* todo: check that when the channel is shut-down from inside this | ||
674 | * function, we cope with unsetting reload, etc */ | ||
675 | |||
676 | if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) { | ||
677 | unsigned long flags; | ||
678 | |||
679 | switch (chan->load_state) { | ||
680 | case S3C2410_DMALOAD_1RUNNING: | ||
681 | /* don't need to do anything for this state */ | ||
682 | break; | ||
683 | |||
684 | case S3C2410_DMALOAD_NONE: | ||
685 | /* can load buffer immediately */ | ||
686 | break; | ||
687 | |||
688 | case S3C2410_DMALOAD_1LOADED: | ||
689 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
690 | /* flag error? */ | ||
691 | printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", | ||
692 | chan->number, __FUNCTION__); | ||
693 | return IRQ_HANDLED; | ||
694 | } | ||
695 | |||
696 | break; | ||
697 | |||
698 | case S3C2410_DMALOAD_1LOADED_1RUNNING: | ||
699 | goto no_load; | ||
700 | |||
701 | default: | ||
702 | printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n", | ||
703 | chan->number, chan->load_state); | ||
704 | return IRQ_HANDLED; | ||
705 | } | ||
706 | |||
707 | local_irq_save(flags); | ||
708 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
709 | local_irq_restore(flags); | ||
710 | } else { | ||
711 | s3c2410_dma_lastxfer(chan); | ||
712 | |||
713 | /* see if we can stop this channel.. */ | ||
714 | if (chan->load_state == S3C2410_DMALOAD_NONE) { | ||
715 | pr_debug("dma%d: end of transfer, stopping channel (%ld)\n", | ||
716 | chan->number, jiffies); | ||
717 | s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, | ||
718 | S3C2410_DMAOP_STOP); | ||
719 | } | ||
720 | } | ||
721 | |||
722 | no_load: | ||
723 | return IRQ_HANDLED; | ||
724 | } | ||
725 | |||
726 | static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel); | ||
727 | |||
728 | /* s3c2410_request_dma | ||
729 | * | ||
730 | * get control of an dma channel | ||
731 | */ | ||
732 | |||
733 | int s3c2410_dma_request(unsigned int channel, | ||
734 | struct s3c2410_dma_client *client, | ||
735 | void *dev) | ||
736 | { | ||
737 | struct s3c2410_dma_chan *chan; | ||
738 | unsigned long flags; | ||
739 | int err; | ||
740 | |||
741 | pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", | ||
742 | channel, client->name, dev); | ||
743 | |||
744 | local_irq_save(flags); | ||
745 | |||
746 | chan = s3c2410_dma_map_channel(channel); | ||
747 | if (chan == NULL) { | ||
748 | local_irq_restore(flags); | ||
749 | return -EBUSY; | ||
750 | } | ||
751 | |||
752 | dbg_showchan(chan); | ||
753 | |||
754 | chan->client = client; | ||
755 | chan->in_use = 1; | ||
756 | |||
757 | if (!chan->irq_claimed) { | ||
758 | pr_debug("dma%d: %s : requesting irq %d\n", | ||
759 | channel, __FUNCTION__, chan->irq); | ||
760 | |||
761 | chan->irq_claimed = 1; | ||
762 | local_irq_restore(flags); | ||
763 | |||
764 | err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, | ||
765 | client->name, (void *)chan); | ||
766 | |||
767 | local_irq_save(flags); | ||
768 | |||
769 | if (err) { | ||
770 | chan->in_use = 0; | ||
771 | chan->irq_claimed = 0; | ||
772 | local_irq_restore(flags); | ||
773 | |||
774 | printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", | ||
775 | client->name, chan->irq, chan->number); | ||
776 | return err; | ||
777 | } | ||
778 | |||
779 | chan->irq_enabled = 1; | ||
780 | } | ||
781 | |||
782 | local_irq_restore(flags); | ||
783 | |||
784 | /* need to setup */ | ||
785 | |||
786 | pr_debug("%s: channel initialised, %p\n", __FUNCTION__, chan); | ||
787 | |||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | EXPORT_SYMBOL(s3c2410_dma_request); | ||
792 | |||
793 | /* s3c2410_dma_free | ||
794 | * | ||
795 | * release the given channel back to the system, will stop and flush | ||
796 | * any outstanding transfers, and ensure the channel is ready for the | ||
797 | * next claimant. | ||
798 | * | ||
799 | * Note, although a warning is currently printed if the freeing client | ||
800 | * info is not the same as the registrant's client info, the free is still | ||
801 | * allowed to go through. | ||
802 | */ | ||
803 | |||
804 | int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *client) | ||
805 | { | ||
806 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); | ||
807 | unsigned long flags; | ||
808 | |||
809 | if (chan == NULL) | ||
810 | return -EINVAL; | ||
811 | |||
812 | local_irq_save(flags); | ||
813 | |||
814 | if (chan->client != client) { | ||
815 | printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n", | ||
816 | channel, chan->client, client); | ||
817 | } | ||
818 | |||
819 | /* sort out stopping and freeing the channel */ | ||
820 | |||
821 | if (chan->state != S3C2410_DMA_IDLE) { | ||
822 | pr_debug("%s: need to stop dma channel %p\n", | ||
823 | __FUNCTION__, chan); | ||
824 | |||
825 | /* possibly flush the channel */ | ||
826 | s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP); | ||
827 | } | ||
828 | |||
829 | chan->client = NULL; | ||
830 | chan->in_use = 0; | ||
831 | |||
832 | if (chan->irq_claimed) | ||
833 | free_irq(chan->irq, (void *)chan); | ||
834 | |||
835 | chan->irq_claimed = 0; | ||
836 | |||
837 | if (!(channel & DMACH_LOW_LEVEL)) | ||
838 | dma_chan_map[channel] = NULL; | ||
839 | |||
840 | local_irq_restore(flags); | ||
841 | |||
842 | return 0; | ||
843 | } | ||
844 | |||
845 | EXPORT_SYMBOL(s3c2410_dma_free); | ||
846 | |||
847 | static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) | ||
848 | { | ||
849 | unsigned long flags; | ||
850 | unsigned long tmp; | ||
851 | |||
852 | pr_debug("%s:\n", __FUNCTION__); | ||
853 | |||
854 | dbg_showchan(chan); | ||
855 | |||
856 | local_irq_save(flags); | ||
857 | |||
858 | s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP); | ||
859 | |||
860 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | ||
861 | tmp |= S3C2410_DMASKTRIG_STOP; | ||
862 | //tmp &= ~S3C2410_DMASKTRIG_ON; | ||
863 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); | ||
864 | |||
865 | #if 0 | ||
866 | /* should also clear interrupts, according to WinCE BSP */ | ||
867 | tmp = dma_rdreg(chan, S3C2410_DMA_DCON); | ||
868 | tmp |= S3C2410_DCON_NORELOAD; | ||
869 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); | ||
870 | #endif | ||
871 | |||
872 | /* should stop do this, or should we wait for flush? */ | ||
873 | chan->state = S3C2410_DMA_IDLE; | ||
874 | chan->load_state = S3C2410_DMALOAD_NONE; | ||
875 | |||
876 | local_irq_restore(flags); | ||
877 | |||
878 | return 0; | ||
879 | } | ||
880 | |||
881 | void s3c2410_dma_waitforstop(struct s3c2410_dma_chan *chan) | ||
882 | { | ||
883 | unsigned long tmp; | ||
884 | unsigned int timeout = 0x10000; | ||
885 | |||
886 | while (timeout-- > 0) { | ||
887 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | ||
888 | |||
889 | if (!(tmp & S3C2410_DMASKTRIG_ON)) | ||
890 | return; | ||
891 | } | ||
892 | |||
893 | pr_debug("dma%d: failed to stop?\n", chan->number); | ||
894 | } | ||
895 | |||
896 | |||
897 | /* s3c2410_dma_flush | ||
898 | * | ||
899 | * stop the channel, and remove all current and pending transfers | ||
900 | */ | ||
901 | |||
902 | static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan) | ||
903 | { | ||
904 | struct s3c2410_dma_buf *buf, *next; | ||
905 | unsigned long flags; | ||
906 | |||
907 | pr_debug("%s: chan %p (%d)\n", __FUNCTION__, chan, chan->number); | ||
908 | |||
909 | dbg_showchan(chan); | ||
910 | |||
911 | local_irq_save(flags); | ||
912 | |||
913 | if (chan->state != S3C2410_DMA_IDLE) { | ||
914 | pr_debug("%s: stopping channel...\n", __FUNCTION__ ); | ||
915 | s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); | ||
916 | } | ||
917 | |||
918 | buf = chan->curr; | ||
919 | if (buf == NULL) | ||
920 | buf = chan->next; | ||
921 | |||
922 | chan->curr = chan->next = chan->end = NULL; | ||
923 | |||
924 | if (buf != NULL) { | ||
925 | for ( ; buf != NULL; buf = next) { | ||
926 | next = buf->next; | ||
927 | |||
928 | pr_debug("%s: free buffer %p, next %p\n", | ||
929 | __FUNCTION__, buf, buf->next); | ||
930 | |||
931 | s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT); | ||
932 | s3c2410_dma_freebuf(buf); | ||
933 | } | ||
934 | } | ||
935 | |||
936 | dbg_showregs(chan); | ||
937 | |||
938 | s3c2410_dma_waitforstop(chan); | ||
939 | |||
940 | #if 0 | ||
941 | /* should also clear interrupts, according to WinCE BSP */ | ||
942 | { | ||
943 | unsigned long tmp; | ||
944 | |||
945 | tmp = dma_rdreg(chan, S3C2410_DMA_DCON); | ||
946 | tmp |= S3C2410_DCON_NORELOAD; | ||
947 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); | ||
948 | } | ||
949 | #endif | ||
950 | |||
951 | dbg_showregs(chan); | ||
952 | |||
953 | local_irq_restore(flags); | ||
954 | |||
955 | return 0; | ||
956 | } | ||
957 | |||
958 | int | ||
959 | s3c2410_dma_started(struct s3c2410_dma_chan *chan) | ||
960 | { | ||
961 | unsigned long flags; | ||
962 | |||
963 | local_irq_save(flags); | ||
964 | |||
965 | dbg_showchan(chan); | ||
966 | |||
967 | /* if we've only loaded one buffer onto the channel, then chec | ||
968 | * to see if we have another, and if so, try and load it so when | ||
969 | * the first buffer is finished, the new one will be loaded onto | ||
970 | * the channel */ | ||
971 | |||
972 | if (chan->next != NULL) { | ||
973 | if (chan->load_state == S3C2410_DMALOAD_1LOADED) { | ||
974 | |||
975 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
976 | pr_debug("%s: buff not yet loaded, no more todo\n", | ||
977 | __FUNCTION__); | ||
978 | } else { | ||
979 | chan->load_state = S3C2410_DMALOAD_1RUNNING; | ||
980 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
981 | } | ||
982 | |||
983 | } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { | ||
984 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
985 | } | ||
986 | } | ||
987 | |||
988 | |||
989 | local_irq_restore(flags); | ||
990 | |||
991 | return 0; | ||
992 | |||
993 | } | ||
994 | |||
995 | int | ||
996 | s3c2410_dma_ctrl(dmach_t channel, enum s3c2410_chan_op op) | ||
997 | { | ||
998 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); | ||
999 | |||
1000 | if (chan == NULL) | ||
1001 | return -EINVAL; | ||
1002 | |||
1003 | switch (op) { | ||
1004 | case S3C2410_DMAOP_START: | ||
1005 | return s3c2410_dma_start(chan); | ||
1006 | |||
1007 | case S3C2410_DMAOP_STOP: | ||
1008 | return s3c2410_dma_dostop(chan); | ||
1009 | |||
1010 | case S3C2410_DMAOP_PAUSE: | ||
1011 | case S3C2410_DMAOP_RESUME: | ||
1012 | return -ENOENT; | ||
1013 | |||
1014 | case S3C2410_DMAOP_FLUSH: | ||
1015 | return s3c2410_dma_flush(chan); | ||
1016 | |||
1017 | case S3C2410_DMAOP_STARTED: | ||
1018 | return s3c2410_dma_started(chan); | ||
1019 | |||
1020 | case S3C2410_DMAOP_TIMEOUT: | ||
1021 | return 0; | ||
1022 | |||
1023 | } | ||
1024 | |||
1025 | return -ENOENT; /* unknown, don't bother */ | ||
1026 | } | ||
1027 | |||
1028 | EXPORT_SYMBOL(s3c2410_dma_ctrl); | ||
1029 | |||
1030 | /* DMA configuration for each channel | ||
1031 | * | ||
1032 | * DISRCC -> source of the DMA (AHB,APB) | ||
1033 | * DISRC -> source address of the DMA | ||
1034 | * DIDSTC -> destination of the DMA (AHB,APD) | ||
1035 | * DIDST -> destination address of the DMA | ||
1036 | */ | ||
1037 | |||
1038 | /* s3c2410_dma_config | ||
1039 | * | ||
1040 | * xfersize: size of unit in bytes (1,2,4) | ||
1041 | * dcon: base value of the DCONx register | ||
1042 | */ | ||
1043 | |||
1044 | int s3c2410_dma_config(dmach_t channel, | ||
1045 | int xferunit, | ||
1046 | int dcon) | ||
1047 | { | ||
1048 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); | ||
1049 | |||
1050 | pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n", | ||
1051 | __FUNCTION__, channel, xferunit, dcon); | ||
1052 | |||
1053 | if (chan == NULL) | ||
1054 | return -EINVAL; | ||
1055 | |||
1056 | pr_debug("%s: Initial dcon is %08x\n", __FUNCTION__, dcon); | ||
1057 | |||
1058 | dcon |= chan->dcon & dma_sel.dcon_mask; | ||
1059 | |||
1060 | pr_debug("%s: New dcon is %08x\n", __FUNCTION__, dcon); | ||
1061 | |||
1062 | switch (xferunit) { | ||
1063 | case 1: | ||
1064 | dcon |= S3C2410_DCON_BYTE; | ||
1065 | break; | ||
1066 | |||
1067 | case 2: | ||
1068 | dcon |= S3C2410_DCON_HALFWORD; | ||
1069 | break; | ||
1070 | |||
1071 | case 4: | ||
1072 | dcon |= S3C2410_DCON_WORD; | ||
1073 | break; | ||
1074 | |||
1075 | default: | ||
1076 | pr_debug("%s: bad transfer size %d\n", __FUNCTION__, xferunit); | ||
1077 | return -EINVAL; | ||
1078 | } | ||
1079 | |||
1080 | dcon |= S3C2410_DCON_HWTRIG; | ||
1081 | dcon |= S3C2410_DCON_INTREQ; | ||
1082 | |||
1083 | pr_debug("%s: dcon now %08x\n", __FUNCTION__, dcon); | ||
1084 | |||
1085 | chan->dcon = dcon; | ||
1086 | chan->xfer_unit = xferunit; | ||
1087 | |||
1088 | return 0; | ||
1089 | } | ||
1090 | |||
1091 | EXPORT_SYMBOL(s3c2410_dma_config); | ||
1092 | |||
1093 | int s3c2410_dma_setflags(dmach_t channel, unsigned int flags) | ||
1094 | { | ||
1095 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); | ||
1096 | |||
1097 | if (chan == NULL) | ||
1098 | return -EINVAL; | ||
1099 | |||
1100 | pr_debug("%s: chan=%p, flags=%08x\n", __FUNCTION__, chan, flags); | ||
1101 | |||
1102 | chan->flags = flags; | ||
1103 | |||
1104 | return 0; | ||
1105 | } | ||
1106 | |||
1107 | EXPORT_SYMBOL(s3c2410_dma_setflags); | ||
1108 | |||
1109 | |||
1110 | /* do we need to protect the settings of the fields from | ||
1111 | * irq? | ||
1112 | */ | ||
1113 | |||
1114 | int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn) | ||
1115 | { | ||
1116 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); | ||
1117 | |||
1118 | if (chan == NULL) | ||
1119 | return -EINVAL; | ||
1120 | |||
1121 | pr_debug("%s: chan=%p, op rtn=%p\n", __FUNCTION__, chan, rtn); | ||
1122 | |||
1123 | chan->op_fn = rtn; | ||
1124 | |||
1125 | return 0; | ||
1126 | } | ||
1127 | |||
1128 | EXPORT_SYMBOL(s3c2410_dma_set_opfn); | ||
1129 | |||
1130 | int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn) | ||
1131 | { | ||
1132 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); | ||
1133 | |||
1134 | if (chan == NULL) | ||
1135 | return -EINVAL; | ||
1136 | |||
1137 | pr_debug("%s: chan=%p, callback rtn=%p\n", __FUNCTION__, chan, rtn); | ||
1138 | |||
1139 | chan->callback_fn = rtn; | ||
1140 | |||
1141 | return 0; | ||
1142 | } | ||
1143 | |||
1144 | EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn); | ||
1145 | |||
1146 | /* s3c2410_dma_devconfig | ||
1147 | * | ||
1148 | * configure the dma source/destination hardware type and address | ||
1149 | * | ||
1150 | * source: S3C2410_DMASRC_HW: source is hardware | ||
1151 | * S3C2410_DMASRC_MEM: source is memory | ||
1152 | * | ||
1153 | * hwcfg: the value for xxxSTCn register, | ||
1154 | * bit 0: 0=increment pointer, 1=leave pointer | ||
1155 | * bit 1: 0=soucre is AHB, 1=soucre is APB | ||
1156 | * | ||
1157 | * devaddr: physical address of the source | ||
1158 | */ | ||
1159 | |||
1160 | int s3c2410_dma_devconfig(int channel, | ||
1161 | enum s3c2410_dmasrc source, | ||
1162 | int hwcfg, | ||
1163 | unsigned long devaddr) | ||
1164 | { | ||
1165 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); | ||
1166 | |||
1167 | if (chan == NULL) | ||
1168 | return -EINVAL; | ||
1169 | |||
1170 | pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx\n", | ||
1171 | __FUNCTION__, (int)source, hwcfg, devaddr); | ||
1172 | |||
1173 | chan->source = source; | ||
1174 | chan->dev_addr = devaddr; | ||
1175 | |||
1176 | switch (source) { | ||
1177 | case S3C2410_DMASRC_HW: | ||
1178 | /* source is hardware */ | ||
1179 | pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n", | ||
1180 | __FUNCTION__, devaddr, hwcfg); | ||
1181 | dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3); | ||
1182 | dma_wrreg(chan, S3C2410_DMA_DISRC, devaddr); | ||
1183 | dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0)); | ||
1184 | |||
1185 | chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST); | ||
1186 | return 0; | ||
1187 | |||
1188 | case S3C2410_DMASRC_MEM: | ||
1189 | /* source is memory */ | ||
1190 | pr_debug( "%s: mem source, devaddr=%08lx, hwcfg=%d\n", | ||
1191 | __FUNCTION__, devaddr, hwcfg); | ||
1192 | dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0)); | ||
1193 | dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr); | ||
1194 | dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3); | ||
1195 | |||
1196 | chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC); | ||
1197 | return 0; | ||
1198 | } | ||
1199 | |||
1200 | printk(KERN_ERR "dma%d: invalid source type (%d)\n", channel, source); | ||
1201 | return -EINVAL; | ||
1202 | } | ||
1203 | |||
1204 | EXPORT_SYMBOL(s3c2410_dma_devconfig); | ||
1205 | |||
1206 | /* s3c2410_dma_getposition | ||
1207 | * | ||
1208 | * returns the current transfer points for the dma source and destination | ||
1209 | */ | ||
1210 | |||
1211 | int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst) | ||
1212 | { | ||
1213 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); | ||
1214 | |||
1215 | if (chan == NULL) | ||
1216 | return -EINVAL; | ||
1217 | |||
1218 | if (src != NULL) | ||
1219 | *src = dma_rdreg(chan, S3C2410_DMA_DCSRC); | ||
1220 | |||
1221 | if (dst != NULL) | ||
1222 | *dst = dma_rdreg(chan, S3C2410_DMA_DCDST); | ||
1223 | |||
1224 | return 0; | ||
1225 | } | ||
1226 | |||
1227 | EXPORT_SYMBOL(s3c2410_dma_getposition); | ||
1228 | |||
1229 | |||
1230 | /* system device class */ | ||
1231 | |||
1232 | #ifdef CONFIG_PM | ||
1233 | |||
1234 | static int s3c2410_dma_suspend(struct sys_device *dev, pm_message_t state) | ||
1235 | { | ||
1236 | struct s3c2410_dma_chan *cp = container_of(dev, struct s3c2410_dma_chan, dev); | ||
1237 | |||
1238 | printk(KERN_DEBUG "suspending dma channel %d\n", cp->number); | ||
1239 | |||
1240 | if (dma_rdreg(cp, S3C2410_DMA_DMASKTRIG) & S3C2410_DMASKTRIG_ON) { | ||
1241 | /* the dma channel is still working, which is probably | ||
1242 | * a bad thing to do over suspend/resume. We stop the | ||
1243 | * channel and assume that the client is either going to | ||
1244 | * retry after resume, or that it is broken. | ||
1245 | */ | ||
1246 | |||
1247 | printk(KERN_INFO "dma: stopping channel %d due to suspend\n", | ||
1248 | cp->number); | ||
1249 | |||
1250 | s3c2410_dma_dostop(cp); | ||
1251 | } | ||
1252 | |||
1253 | return 0; | ||
1254 | } | ||
1255 | |||
1256 | static int s3c2410_dma_resume(struct sys_device *dev) | ||
1257 | { | ||
1258 | return 0; | ||
1259 | } | ||
1260 | |||
1261 | #else | ||
1262 | #define s3c2410_dma_suspend NULL | ||
1263 | #define s3c2410_dma_resume NULL | ||
1264 | #endif /* CONFIG_PM */ | ||
1265 | |||
1266 | struct sysdev_class dma_sysclass = { | ||
1267 | set_kset_name("s3c24xx-dma"), | ||
1268 | .suspend = s3c2410_dma_suspend, | ||
1269 | .resume = s3c2410_dma_resume, | ||
1270 | }; | ||
1271 | |||
1272 | /* kmem cache implementation */ | ||
1273 | |||
1274 | static void s3c2410_dma_cache_ctor(void *p, struct kmem_cache *c, unsigned long f) | ||
1275 | { | ||
1276 | memset(p, 0, sizeof(struct s3c2410_dma_buf)); | ||
1277 | } | ||
1278 | |||
1279 | /* initialisation code */ | ||
1280 | |||
1281 | static int __init s3c2410_init_dma(void) | ||
1282 | { | ||
1283 | struct s3c2410_dma_chan *cp; | ||
1284 | int channel; | ||
1285 | int ret; | ||
1286 | |||
1287 | printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n"); | ||
1288 | |||
1289 | dma_base = ioremap(S3C24XX_PA_DMA, 0x200); | ||
1290 | if (dma_base == NULL) { | ||
1291 | printk(KERN_ERR "dma failed to remap register block\n"); | ||
1292 | return -ENOMEM; | ||
1293 | } | ||
1294 | |||
1295 | printk("Registering sysclass\n"); | ||
1296 | |||
1297 | ret = sysdev_class_register(&dma_sysclass); | ||
1298 | if (ret != 0) { | ||
1299 | printk(KERN_ERR "dma sysclass registration failed\n"); | ||
1300 | goto err; | ||
1301 | } | ||
1302 | |||
1303 | dma_kmem = kmem_cache_create("dma_desc", sizeof(struct s3c2410_dma_buf), 0, | ||
1304 | SLAB_HWCACHE_ALIGN, | ||
1305 | s3c2410_dma_cache_ctor, NULL); | ||
1306 | |||
1307 | if (dma_kmem == NULL) { | ||
1308 | printk(KERN_ERR "dma failed to make kmem cache\n"); | ||
1309 | ret = -ENOMEM; | ||
1310 | goto err; | ||
1311 | } | ||
1312 | |||
1313 | for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) { | ||
1314 | cp = &s3c2410_chans[channel]; | ||
1315 | |||
1316 | memset(cp, 0, sizeof(struct s3c2410_dma_chan)); | ||
1317 | |||
1318 | /* dma channel irqs are in order.. */ | ||
1319 | cp->number = channel; | ||
1320 | cp->irq = channel + IRQ_DMA0; | ||
1321 | cp->regs = dma_base + (channel*0x40); | ||
1322 | |||
1323 | /* point current stats somewhere */ | ||
1324 | cp->stats = &cp->stats_store; | ||
1325 | cp->stats_store.timeout_shortest = LONG_MAX; | ||
1326 | |||
1327 | /* basic channel configuration */ | ||
1328 | |||
1329 | cp->load_timeout = 1<<18; | ||
1330 | |||
1331 | /* register system device */ | ||
1332 | |||
1333 | cp->dev.cls = &dma_sysclass; | ||
1334 | cp->dev.id = channel; | ||
1335 | ret = sysdev_register(&cp->dev); | ||
1336 | |||
1337 | printk("DMA channel %d at %p, irq %d\n", | ||
1338 | cp->number, cp->regs, cp->irq); | ||
1339 | } | ||
1340 | |||
1341 | return 0; | ||
1342 | |||
1343 | err: | ||
1344 | kmem_cache_destroy(dma_kmem); | ||
1345 | iounmap(dma_base); | ||
1346 | dma_base = NULL; | ||
1347 | return ret; | ||
1348 | } | ||
1349 | |||
1350 | core_initcall(s3c2410_init_dma); | ||
1351 | |||
1352 | static inline int is_channel_valid(unsigned int channel) | ||
1353 | { | ||
1354 | return (channel & DMA_CH_VALID); | ||
1355 | } | ||
1356 | |||
1357 | /* s3c2410_dma_map_channel() | ||
1358 | * | ||
1359 | * turn the virtual channel number into a real, and un-used hardware | ||
1360 | * channel. | ||
1361 | * | ||
1362 | * currently this code uses first-free channel from the specified harware | ||
1363 | * map, not taking into account anything that the board setup code may | ||
1364 | * have to say about the likely peripheral set to be in use. | ||
1365 | */ | ||
1366 | |||
1367 | struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) | ||
1368 | { | ||
1369 | struct s3c24xx_dma_map *ch_map; | ||
1370 | struct s3c2410_dma_chan *dmach; | ||
1371 | int ch; | ||
1372 | |||
1373 | if (dma_sel.map == NULL || channel > dma_sel.map_size) | ||
1374 | return NULL; | ||
1375 | |||
1376 | ch_map = dma_sel.map + channel; | ||
1377 | |||
1378 | for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) { | ||
1379 | if (!is_channel_valid(ch_map->channels[ch])) | ||
1380 | continue; | ||
1381 | |||
1382 | if (s3c2410_chans[ch].in_use == 0) { | ||
1383 | printk("mapped channel %d to %d\n", channel, ch); | ||
1384 | break; | ||
1385 | } | ||
1386 | } | ||
1387 | |||
1388 | if (ch >= S3C2410_DMA_CHANNELS) | ||
1389 | return NULL; | ||
1390 | |||
1391 | /* update our channel mapping */ | ||
1392 | |||
1393 | dmach = &s3c2410_chans[ch]; | ||
1394 | dma_chan_map[channel] = dmach; | ||
1395 | |||
1396 | /* select the channel */ | ||
1397 | |||
1398 | (dma_sel.select)(dmach, ch_map); | ||
1399 | |||
1400 | return dmach; | ||
1401 | } | ||
1402 | |||
1403 | static void s3c24xx_dma_show_ch(struct s3c24xx_dma_map *map, int ch) | ||
1404 | { | ||
1405 | /* show the channel configuration */ | ||
1406 | |||
1407 | printk("%2d: %20s, channels %c%c%c%c\n", ch, map->name, | ||
1408 | (is_channel_valid(map->channels[0]) ? '0' : '-'), | ||
1409 | (is_channel_valid(map->channels[1]) ? '1' : '-'), | ||
1410 | (is_channel_valid(map->channels[2]) ? '2' : '-'), | ||
1411 | (is_channel_valid(map->channels[3]) ? '3' : '-')); | ||
1412 | } | ||
1413 | |||
1414 | static int s3c24xx_dma_check_entry(struct s3c24xx_dma_map *map, int ch) | ||
1415 | { | ||
1416 | if (1) | ||
1417 | s3c24xx_dma_show_ch(map, ch); | ||
1418 | |||
1419 | return 0; | ||
1420 | } | ||
1421 | |||
1422 | int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel) | ||
1423 | { | ||
1424 | struct s3c24xx_dma_map *nmap; | ||
1425 | size_t map_sz = sizeof(*nmap) * sel->map_size; | ||
1426 | int ptr; | ||
1427 | |||
1428 | nmap = kmalloc(map_sz, GFP_KERNEL); | ||
1429 | if (nmap == NULL) | ||
1430 | return -ENOMEM; | ||
1431 | |||
1432 | memcpy(nmap, sel->map, map_sz); | ||
1433 | memcpy(&dma_sel, sel, sizeof(*sel)); | ||
1434 | |||
1435 | dma_sel.map = nmap; | ||
1436 | |||
1437 | for (ptr = 0; ptr < sel->map_size; ptr++) | ||
1438 | s3c24xx_dma_check_entry(nmap+ptr, ptr); | ||
1439 | |||
1440 | return 0; | ||
1441 | } | ||
diff --git a/arch/arm/plat-s3c24xx/gpio.c b/arch/arm/plat-s3c24xx/gpio.c new file mode 100644 index 000000000000..ec3a09c4d181 --- /dev/null +++ b/arch/arm/plat-s3c24xx/gpio.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/gpio.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C24XX GPIO support | ||
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 as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/ioport.h> | ||
29 | |||
30 | #include <asm/hardware.h> | ||
31 | #include <asm/irq.h> | ||
32 | #include <asm/io.h> | ||
33 | |||
34 | #include <asm/arch/regs-gpio.h> | ||
35 | |||
36 | void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) | ||
37 | { | ||
38 | void __iomem *base = S3C24XX_GPIO_BASE(pin); | ||
39 | unsigned long mask; | ||
40 | unsigned long con; | ||
41 | unsigned long flags; | ||
42 | |||
43 | if (pin < S3C2410_GPIO_BANKB) { | ||
44 | mask = 1 << S3C2410_GPIO_OFFSET(pin); | ||
45 | } else { | ||
46 | mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; | ||
47 | } | ||
48 | |||
49 | switch (function) { | ||
50 | case S3C2410_GPIO_LEAVE: | ||
51 | mask = 0; | ||
52 | function = 0; | ||
53 | break; | ||
54 | |||
55 | case S3C2410_GPIO_INPUT: | ||
56 | case S3C2410_GPIO_OUTPUT: | ||
57 | case S3C2410_GPIO_SFN2: | ||
58 | case S3C2410_GPIO_SFN3: | ||
59 | if (pin < S3C2410_GPIO_BANKB) { | ||
60 | function -= 1; | ||
61 | function &= 1; | ||
62 | function <<= S3C2410_GPIO_OFFSET(pin); | ||
63 | } else { | ||
64 | function &= 3; | ||
65 | function <<= S3C2410_GPIO_OFFSET(pin)*2; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | /* modify the specified register wwith IRQs off */ | ||
70 | |||
71 | local_irq_save(flags); | ||
72 | |||
73 | con = __raw_readl(base + 0x00); | ||
74 | con &= ~mask; | ||
75 | con |= function; | ||
76 | |||
77 | __raw_writel(con, base + 0x00); | ||
78 | |||
79 | local_irq_restore(flags); | ||
80 | } | ||
81 | |||
82 | EXPORT_SYMBOL(s3c2410_gpio_cfgpin); | ||
83 | |||
84 | unsigned int s3c2410_gpio_getcfg(unsigned int pin) | ||
85 | { | ||
86 | void __iomem *base = S3C24XX_GPIO_BASE(pin); | ||
87 | unsigned long val = __raw_readl(base); | ||
88 | |||
89 | if (pin < S3C2410_GPIO_BANKB) { | ||
90 | val >>= S3C2410_GPIO_OFFSET(pin); | ||
91 | val &= 1; | ||
92 | val += 1; | ||
93 | } else { | ||
94 | val >>= S3C2410_GPIO_OFFSET(pin)*2; | ||
95 | val &= 3; | ||
96 | } | ||
97 | |||
98 | return val | S3C2410_GPIO_INPUT; | ||
99 | } | ||
100 | |||
101 | EXPORT_SYMBOL(s3c2410_gpio_getcfg); | ||
102 | |||
103 | void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) | ||
104 | { | ||
105 | void __iomem *base = S3C24XX_GPIO_BASE(pin); | ||
106 | unsigned long offs = S3C2410_GPIO_OFFSET(pin); | ||
107 | unsigned long flags; | ||
108 | unsigned long up; | ||
109 | |||
110 | if (pin < S3C2410_GPIO_BANKB) | ||
111 | return; | ||
112 | |||
113 | local_irq_save(flags); | ||
114 | |||
115 | up = __raw_readl(base + 0x08); | ||
116 | up &= ~(1L << offs); | ||
117 | up |= to << offs; | ||
118 | __raw_writel(up, base + 0x08); | ||
119 | |||
120 | local_irq_restore(flags); | ||
121 | } | ||
122 | |||
123 | EXPORT_SYMBOL(s3c2410_gpio_pullup); | ||
124 | |||
125 | void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) | ||
126 | { | ||
127 | void __iomem *base = S3C24XX_GPIO_BASE(pin); | ||
128 | unsigned long offs = S3C2410_GPIO_OFFSET(pin); | ||
129 | unsigned long flags; | ||
130 | unsigned long dat; | ||
131 | |||
132 | local_irq_save(flags); | ||
133 | |||
134 | dat = __raw_readl(base + 0x04); | ||
135 | dat &= ~(1 << offs); | ||
136 | dat |= to << offs; | ||
137 | __raw_writel(dat, base + 0x04); | ||
138 | |||
139 | local_irq_restore(flags); | ||
140 | } | ||
141 | |||
142 | EXPORT_SYMBOL(s3c2410_gpio_setpin); | ||
143 | |||
144 | unsigned int s3c2410_gpio_getpin(unsigned int pin) | ||
145 | { | ||
146 | void __iomem *base = S3C24XX_GPIO_BASE(pin); | ||
147 | unsigned long offs = S3C2410_GPIO_OFFSET(pin); | ||
148 | |||
149 | return __raw_readl(base + 0x04) & (1<< offs); | ||
150 | } | ||
151 | |||
152 | EXPORT_SYMBOL(s3c2410_gpio_getpin); | ||
153 | |||
154 | unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) | ||
155 | { | ||
156 | unsigned long flags; | ||
157 | unsigned long misccr; | ||
158 | |||
159 | local_irq_save(flags); | ||
160 | misccr = __raw_readl(S3C24XX_MISCCR); | ||
161 | misccr &= ~clear; | ||
162 | misccr ^= change; | ||
163 | __raw_writel(misccr, S3C24XX_MISCCR); | ||
164 | local_irq_restore(flags); | ||
165 | |||
166 | return misccr; | ||
167 | } | ||
168 | |||
169 | EXPORT_SYMBOL(s3c2410_modify_misccr); | ||
170 | |||
171 | int s3c2410_gpio_getirq(unsigned int pin) | ||
172 | { | ||
173 | if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15) | ||
174 | return -1; /* not valid interrupts */ | ||
175 | |||
176 | if (pin < S3C2410_GPG0 && pin > S3C2410_GPF7) | ||
177 | return -1; /* not valid pin */ | ||
178 | |||
179 | if (pin < S3C2410_GPF4) | ||
180 | return (pin - S3C2410_GPF0) + IRQ_EINT0; | ||
181 | |||
182 | if (pin < S3C2410_GPG0) | ||
183 | return (pin - S3C2410_GPF4) + IRQ_EINT4; | ||
184 | |||
185 | return (pin - S3C2410_GPG0) + IRQ_EINT8; | ||
186 | } | ||
187 | |||
188 | EXPORT_SYMBOL(s3c2410_gpio_getirq); | ||
diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c new file mode 100644 index 000000000000..ce186398e3fd --- /dev/null +++ b/arch/arm/plat-s3c24xx/irq.c | |||
@@ -0,0 +1,801 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/irq.c | ||
2 | * | ||
3 | * Copyright (c) 2003,2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@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 as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | * Changelog: | ||
21 | * | ||
22 | * 22-Jul-2004 Ben Dooks <ben@simtec.co.uk> | ||
23 | * Fixed compile warnings | ||
24 | * | ||
25 | * 22-Jul-2004 Roc Wu <cooloney@yahoo.com.cn> | ||
26 | * Fixed s3c_extirq_type | ||
27 | * | ||
28 | * 21-Jul-2004 Arnaud Patard (Rtp) <arnaud.patard@rtp-net.org> | ||
29 | * Addition of ADC/TC demux | ||
30 | * | ||
31 | * 04-Oct-2004 Klaus Fetscher <k.fetscher@fetron.de> | ||
32 | * Fix for set_irq_type() on low EINT numbers | ||
33 | * | ||
34 | * 05-Oct-2004 Ben Dooks <ben@simtec.co.uk> | ||
35 | * Tidy up KF's patch and sort out new release | ||
36 | * | ||
37 | * 05-Oct-2004 Ben Dooks <ben@simtec.co.uk> | ||
38 | * Add support for power management controls | ||
39 | * | ||
40 | * 04-Nov-2004 Ben Dooks | ||
41 | * Fix standard IRQ wake for EINT0..4 and RTC | ||
42 | * | ||
43 | * 22-Feb-2005 Ben Dooks | ||
44 | * Fixed edge-triggering on ADC IRQ | ||
45 | * | ||
46 | * 28-Jun-2005 Ben Dooks | ||
47 | * Mark IRQ_LCD valid | ||
48 | * | ||
49 | * 25-Jul-2005 Ben Dooks | ||
50 | * Split the S3C2440 IRQ code to seperate file | ||
51 | */ | ||
52 | |||
53 | #include <linux/init.h> | ||
54 | #include <linux/module.h> | ||
55 | #include <linux/interrupt.h> | ||
56 | #include <linux/ioport.h> | ||
57 | #include <linux/ptrace.h> | ||
58 | #include <linux/sysdev.h> | ||
59 | |||
60 | #include <asm/hardware.h> | ||
61 | #include <asm/irq.h> | ||
62 | #include <asm/io.h> | ||
63 | |||
64 | #include <asm/mach/irq.h> | ||
65 | |||
66 | #include <asm/arch/regs-irq.h> | ||
67 | #include <asm/arch/regs-gpio.h> | ||
68 | |||
69 | #include <asm/plat-s3c24xx/cpu.h> | ||
70 | #include <asm/plat-s3c24xx/pm.h> | ||
71 | #include <asm/plat-s3c24xx/irq.h> | ||
72 | |||
73 | /* wakeup irq control */ | ||
74 | |||
75 | #ifdef CONFIG_PM | ||
76 | |||
77 | /* state for IRQs over sleep */ | ||
78 | |||
79 | /* default is to allow for EINT0..EINT15, and IRQ_RTC as wakeup sources | ||
80 | * | ||
81 | * set bit to 1 in allow bitfield to enable the wakeup settings on it | ||
82 | */ | ||
83 | |||
84 | unsigned long s3c_irqwake_intallow = 1L << (IRQ_RTC - IRQ_EINT0) | 0xfL; | ||
85 | unsigned long s3c_irqwake_intmask = 0xffffffffL; | ||
86 | unsigned long s3c_irqwake_eintallow = 0x0000fff0L; | ||
87 | unsigned long s3c_irqwake_eintmask = 0xffffffffL; | ||
88 | |||
89 | int | ||
90 | s3c_irq_wake(unsigned int irqno, unsigned int state) | ||
91 | { | ||
92 | unsigned long irqbit = 1 << (irqno - IRQ_EINT0); | ||
93 | |||
94 | if (!(s3c_irqwake_intallow & irqbit)) | ||
95 | return -ENOENT; | ||
96 | |||
97 | printk(KERN_INFO "wake %s for irq %d\n", | ||
98 | state ? "enabled" : "disabled", irqno); | ||
99 | |||
100 | if (!state) | ||
101 | s3c_irqwake_intmask |= irqbit; | ||
102 | else | ||
103 | s3c_irqwake_intmask &= ~irqbit; | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int | ||
109 | s3c_irqext_wake(unsigned int irqno, unsigned int state) | ||
110 | { | ||
111 | unsigned long bit = 1L << (irqno - EXTINT_OFF); | ||
112 | |||
113 | if (!(s3c_irqwake_eintallow & bit)) | ||
114 | return -ENOENT; | ||
115 | |||
116 | printk(KERN_INFO "wake %s for irq %d\n", | ||
117 | state ? "enabled" : "disabled", irqno); | ||
118 | |||
119 | if (!state) | ||
120 | s3c_irqwake_eintmask |= bit; | ||
121 | else | ||
122 | s3c_irqwake_eintmask &= ~bit; | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | #else | ||
128 | #define s3c_irqext_wake NULL | ||
129 | #define s3c_irq_wake NULL | ||
130 | #endif | ||
131 | |||
132 | |||
133 | static void | ||
134 | s3c_irq_mask(unsigned int irqno) | ||
135 | { | ||
136 | unsigned long mask; | ||
137 | |||
138 | irqno -= IRQ_EINT0; | ||
139 | |||
140 | mask = __raw_readl(S3C2410_INTMSK); | ||
141 | mask |= 1UL << irqno; | ||
142 | __raw_writel(mask, S3C2410_INTMSK); | ||
143 | } | ||
144 | |||
145 | static inline void | ||
146 | s3c_irq_ack(unsigned int irqno) | ||
147 | { | ||
148 | unsigned long bitval = 1UL << (irqno - IRQ_EINT0); | ||
149 | |||
150 | __raw_writel(bitval, S3C2410_SRCPND); | ||
151 | __raw_writel(bitval, S3C2410_INTPND); | ||
152 | } | ||
153 | |||
154 | static inline void | ||
155 | s3c_irq_maskack(unsigned int irqno) | ||
156 | { | ||
157 | unsigned long bitval = 1UL << (irqno - IRQ_EINT0); | ||
158 | unsigned long mask; | ||
159 | |||
160 | mask = __raw_readl(S3C2410_INTMSK); | ||
161 | __raw_writel(mask|bitval, S3C2410_INTMSK); | ||
162 | |||
163 | __raw_writel(bitval, S3C2410_SRCPND); | ||
164 | __raw_writel(bitval, S3C2410_INTPND); | ||
165 | } | ||
166 | |||
167 | |||
168 | static void | ||
169 | s3c_irq_unmask(unsigned int irqno) | ||
170 | { | ||
171 | unsigned long mask; | ||
172 | |||
173 | if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23) | ||
174 | irqdbf2("s3c_irq_unmask %d\n", irqno); | ||
175 | |||
176 | irqno -= IRQ_EINT0; | ||
177 | |||
178 | mask = __raw_readl(S3C2410_INTMSK); | ||
179 | mask &= ~(1UL << irqno); | ||
180 | __raw_writel(mask, S3C2410_INTMSK); | ||
181 | } | ||
182 | |||
183 | struct irq_chip s3c_irq_level_chip = { | ||
184 | .name = "s3c-level", | ||
185 | .ack = s3c_irq_maskack, | ||
186 | .mask = s3c_irq_mask, | ||
187 | .unmask = s3c_irq_unmask, | ||
188 | .set_wake = s3c_irq_wake | ||
189 | }; | ||
190 | |||
191 | static struct irq_chip s3c_irq_chip = { | ||
192 | .name = "s3c", | ||
193 | .ack = s3c_irq_ack, | ||
194 | .mask = s3c_irq_mask, | ||
195 | .unmask = s3c_irq_unmask, | ||
196 | .set_wake = s3c_irq_wake | ||
197 | }; | ||
198 | |||
199 | static void | ||
200 | s3c_irqext_mask(unsigned int irqno) | ||
201 | { | ||
202 | unsigned long mask; | ||
203 | |||
204 | irqno -= EXTINT_OFF; | ||
205 | |||
206 | mask = __raw_readl(S3C24XX_EINTMASK); | ||
207 | mask |= ( 1UL << irqno); | ||
208 | __raw_writel(mask, S3C24XX_EINTMASK); | ||
209 | } | ||
210 | |||
211 | static void | ||
212 | s3c_irqext_ack(unsigned int irqno) | ||
213 | { | ||
214 | unsigned long req; | ||
215 | unsigned long bit; | ||
216 | unsigned long mask; | ||
217 | |||
218 | bit = 1UL << (irqno - EXTINT_OFF); | ||
219 | |||
220 | mask = __raw_readl(S3C24XX_EINTMASK); | ||
221 | |||
222 | __raw_writel(bit, S3C24XX_EINTPEND); | ||
223 | |||
224 | req = __raw_readl(S3C24XX_EINTPEND); | ||
225 | req &= ~mask; | ||
226 | |||
227 | /* not sure if we should be acking the parent irq... */ | ||
228 | |||
229 | if (irqno <= IRQ_EINT7 ) { | ||
230 | if ((req & 0xf0) == 0) | ||
231 | s3c_irq_ack(IRQ_EINT4t7); | ||
232 | } else { | ||
233 | if ((req >> 8) == 0) | ||
234 | s3c_irq_ack(IRQ_EINT8t23); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | static void | ||
239 | s3c_irqext_unmask(unsigned int irqno) | ||
240 | { | ||
241 | unsigned long mask; | ||
242 | |||
243 | irqno -= EXTINT_OFF; | ||
244 | |||
245 | mask = __raw_readl(S3C24XX_EINTMASK); | ||
246 | mask &= ~( 1UL << irqno); | ||
247 | __raw_writel(mask, S3C24XX_EINTMASK); | ||
248 | } | ||
249 | |||
250 | int | ||
251 | s3c_irqext_type(unsigned int irq, unsigned int type) | ||
252 | { | ||
253 | void __iomem *extint_reg; | ||
254 | void __iomem *gpcon_reg; | ||
255 | unsigned long gpcon_offset, extint_offset; | ||
256 | unsigned long newvalue = 0, value; | ||
257 | |||
258 | if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) | ||
259 | { | ||
260 | gpcon_reg = S3C2410_GPFCON; | ||
261 | extint_reg = S3C24XX_EXTINT0; | ||
262 | gpcon_offset = (irq - IRQ_EINT0) * 2; | ||
263 | extint_offset = (irq - IRQ_EINT0) * 4; | ||
264 | } | ||
265 | else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7)) | ||
266 | { | ||
267 | gpcon_reg = S3C2410_GPFCON; | ||
268 | extint_reg = S3C24XX_EXTINT0; | ||
269 | gpcon_offset = (irq - (EXTINT_OFF)) * 2; | ||
270 | extint_offset = (irq - (EXTINT_OFF)) * 4; | ||
271 | } | ||
272 | else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) | ||
273 | { | ||
274 | gpcon_reg = S3C2410_GPGCON; | ||
275 | extint_reg = S3C24XX_EXTINT1; | ||
276 | gpcon_offset = (irq - IRQ_EINT8) * 2; | ||
277 | extint_offset = (irq - IRQ_EINT8) * 4; | ||
278 | } | ||
279 | else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23)) | ||
280 | { | ||
281 | gpcon_reg = S3C2410_GPGCON; | ||
282 | extint_reg = S3C24XX_EXTINT2; | ||
283 | gpcon_offset = (irq - IRQ_EINT8) * 2; | ||
284 | extint_offset = (irq - IRQ_EINT16) * 4; | ||
285 | } else | ||
286 | return -1; | ||
287 | |||
288 | /* Set the GPIO to external interrupt mode */ | ||
289 | value = __raw_readl(gpcon_reg); | ||
290 | value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); | ||
291 | __raw_writel(value, gpcon_reg); | ||
292 | |||
293 | /* Set the external interrupt to pointed trigger type */ | ||
294 | switch (type) | ||
295 | { | ||
296 | case IRQT_NOEDGE: | ||
297 | printk(KERN_WARNING "No edge setting!\n"); | ||
298 | break; | ||
299 | |||
300 | case IRQT_RISING: | ||
301 | newvalue = S3C2410_EXTINT_RISEEDGE; | ||
302 | break; | ||
303 | |||
304 | case IRQT_FALLING: | ||
305 | newvalue = S3C2410_EXTINT_FALLEDGE; | ||
306 | break; | ||
307 | |||
308 | case IRQT_BOTHEDGE: | ||
309 | newvalue = S3C2410_EXTINT_BOTHEDGE; | ||
310 | break; | ||
311 | |||
312 | case IRQT_LOW: | ||
313 | newvalue = S3C2410_EXTINT_LOWLEV; | ||
314 | break; | ||
315 | |||
316 | case IRQT_HIGH: | ||
317 | newvalue = S3C2410_EXTINT_HILEV; | ||
318 | break; | ||
319 | |||
320 | default: | ||
321 | printk(KERN_ERR "No such irq type %d", type); | ||
322 | return -1; | ||
323 | } | ||
324 | |||
325 | value = __raw_readl(extint_reg); | ||
326 | value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); | ||
327 | __raw_writel(value, extint_reg); | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static struct irq_chip s3c_irqext_chip = { | ||
333 | .name = "s3c-ext", | ||
334 | .mask = s3c_irqext_mask, | ||
335 | .unmask = s3c_irqext_unmask, | ||
336 | .ack = s3c_irqext_ack, | ||
337 | .set_type = s3c_irqext_type, | ||
338 | .set_wake = s3c_irqext_wake | ||
339 | }; | ||
340 | |||
341 | static struct irq_chip s3c_irq_eint0t4 = { | ||
342 | .name = "s3c-ext0", | ||
343 | .ack = s3c_irq_ack, | ||
344 | .mask = s3c_irq_mask, | ||
345 | .unmask = s3c_irq_unmask, | ||
346 | .set_wake = s3c_irq_wake, | ||
347 | .set_type = s3c_irqext_type, | ||
348 | }; | ||
349 | |||
350 | /* mask values for the parent registers for each of the interrupt types */ | ||
351 | |||
352 | #define INTMSK_UART0 (1UL << (IRQ_UART0 - IRQ_EINT0)) | ||
353 | #define INTMSK_UART1 (1UL << (IRQ_UART1 - IRQ_EINT0)) | ||
354 | #define INTMSK_UART2 (1UL << (IRQ_UART2 - IRQ_EINT0)) | ||
355 | #define INTMSK_ADCPARENT (1UL << (IRQ_ADCPARENT - IRQ_EINT0)) | ||
356 | |||
357 | |||
358 | /* UART0 */ | ||
359 | |||
360 | static void | ||
361 | s3c_irq_uart0_mask(unsigned int irqno) | ||
362 | { | ||
363 | s3c_irqsub_mask(irqno, INTMSK_UART0, 7); | ||
364 | } | ||
365 | |||
366 | static void | ||
367 | s3c_irq_uart0_unmask(unsigned int irqno) | ||
368 | { | ||
369 | s3c_irqsub_unmask(irqno, INTMSK_UART0); | ||
370 | } | ||
371 | |||
372 | static void | ||
373 | s3c_irq_uart0_ack(unsigned int irqno) | ||
374 | { | ||
375 | s3c_irqsub_maskack(irqno, INTMSK_UART0, 7); | ||
376 | } | ||
377 | |||
378 | static struct irq_chip s3c_irq_uart0 = { | ||
379 | .name = "s3c-uart0", | ||
380 | .mask = s3c_irq_uart0_mask, | ||
381 | .unmask = s3c_irq_uart0_unmask, | ||
382 | .ack = s3c_irq_uart0_ack, | ||
383 | }; | ||
384 | |||
385 | /* UART1 */ | ||
386 | |||
387 | static void | ||
388 | s3c_irq_uart1_mask(unsigned int irqno) | ||
389 | { | ||
390 | s3c_irqsub_mask(irqno, INTMSK_UART1, 7 << 3); | ||
391 | } | ||
392 | |||
393 | static void | ||
394 | s3c_irq_uart1_unmask(unsigned int irqno) | ||
395 | { | ||
396 | s3c_irqsub_unmask(irqno, INTMSK_UART1); | ||
397 | } | ||
398 | |||
399 | static void | ||
400 | s3c_irq_uart1_ack(unsigned int irqno) | ||
401 | { | ||
402 | s3c_irqsub_maskack(irqno, INTMSK_UART1, 7 << 3); | ||
403 | } | ||
404 | |||
405 | static struct irq_chip s3c_irq_uart1 = { | ||
406 | .name = "s3c-uart1", | ||
407 | .mask = s3c_irq_uart1_mask, | ||
408 | .unmask = s3c_irq_uart1_unmask, | ||
409 | .ack = s3c_irq_uart1_ack, | ||
410 | }; | ||
411 | |||
412 | /* UART2 */ | ||
413 | |||
414 | static void | ||
415 | s3c_irq_uart2_mask(unsigned int irqno) | ||
416 | { | ||
417 | s3c_irqsub_mask(irqno, INTMSK_UART2, 7 << 6); | ||
418 | } | ||
419 | |||
420 | static void | ||
421 | s3c_irq_uart2_unmask(unsigned int irqno) | ||
422 | { | ||
423 | s3c_irqsub_unmask(irqno, INTMSK_UART2); | ||
424 | } | ||
425 | |||
426 | static void | ||
427 | s3c_irq_uart2_ack(unsigned int irqno) | ||
428 | { | ||
429 | s3c_irqsub_maskack(irqno, INTMSK_UART2, 7 << 6); | ||
430 | } | ||
431 | |||
432 | static struct irq_chip s3c_irq_uart2 = { | ||
433 | .name = "s3c-uart2", | ||
434 | .mask = s3c_irq_uart2_mask, | ||
435 | .unmask = s3c_irq_uart2_unmask, | ||
436 | .ack = s3c_irq_uart2_ack, | ||
437 | }; | ||
438 | |||
439 | /* ADC and Touchscreen */ | ||
440 | |||
441 | static void | ||
442 | s3c_irq_adc_mask(unsigned int irqno) | ||
443 | { | ||
444 | s3c_irqsub_mask(irqno, INTMSK_ADCPARENT, 3 << 9); | ||
445 | } | ||
446 | |||
447 | static void | ||
448 | s3c_irq_adc_unmask(unsigned int irqno) | ||
449 | { | ||
450 | s3c_irqsub_unmask(irqno, INTMSK_ADCPARENT); | ||
451 | } | ||
452 | |||
453 | static void | ||
454 | s3c_irq_adc_ack(unsigned int irqno) | ||
455 | { | ||
456 | s3c_irqsub_ack(irqno, INTMSK_ADCPARENT, 3 << 9); | ||
457 | } | ||
458 | |||
459 | static struct irq_chip s3c_irq_adc = { | ||
460 | .name = "s3c-adc", | ||
461 | .mask = s3c_irq_adc_mask, | ||
462 | .unmask = s3c_irq_adc_unmask, | ||
463 | .ack = s3c_irq_adc_ack, | ||
464 | }; | ||
465 | |||
466 | /* irq demux for adc */ | ||
467 | static void s3c_irq_demux_adc(unsigned int irq, | ||
468 | struct irq_desc *desc) | ||
469 | { | ||
470 | unsigned int subsrc, submsk; | ||
471 | unsigned int offset = 9; | ||
472 | struct irq_desc *mydesc; | ||
473 | |||
474 | /* read the current pending interrupts, and the mask | ||
475 | * for what it is available */ | ||
476 | |||
477 | subsrc = __raw_readl(S3C2410_SUBSRCPND); | ||
478 | submsk = __raw_readl(S3C2410_INTSUBMSK); | ||
479 | |||
480 | subsrc &= ~submsk; | ||
481 | subsrc >>= offset; | ||
482 | subsrc &= 3; | ||
483 | |||
484 | if (subsrc != 0) { | ||
485 | if (subsrc & 1) { | ||
486 | mydesc = irq_desc + IRQ_TC; | ||
487 | desc_handle_irq(IRQ_TC, mydesc); | ||
488 | } | ||
489 | if (subsrc & 2) { | ||
490 | mydesc = irq_desc + IRQ_ADC; | ||
491 | desc_handle_irq(IRQ_ADC, mydesc); | ||
492 | } | ||
493 | } | ||
494 | } | ||
495 | |||
496 | static void s3c_irq_demux_uart(unsigned int start) | ||
497 | { | ||
498 | unsigned int subsrc, submsk; | ||
499 | unsigned int offset = start - IRQ_S3CUART_RX0; | ||
500 | struct irq_desc *desc; | ||
501 | |||
502 | /* read the current pending interrupts, and the mask | ||
503 | * for what it is available */ | ||
504 | |||
505 | subsrc = __raw_readl(S3C2410_SUBSRCPND); | ||
506 | submsk = __raw_readl(S3C2410_INTSUBMSK); | ||
507 | |||
508 | irqdbf2("s3c_irq_demux_uart: start=%d (%d), subsrc=0x%08x,0x%08x\n", | ||
509 | start, offset, subsrc, submsk); | ||
510 | |||
511 | subsrc &= ~submsk; | ||
512 | subsrc >>= offset; | ||
513 | subsrc &= 7; | ||
514 | |||
515 | if (subsrc != 0) { | ||
516 | desc = irq_desc + start; | ||
517 | |||
518 | if (subsrc & 1) | ||
519 | desc_handle_irq(start, desc); | ||
520 | |||
521 | desc++; | ||
522 | |||
523 | if (subsrc & 2) | ||
524 | desc_handle_irq(start+1, desc); | ||
525 | |||
526 | desc++; | ||
527 | |||
528 | if (subsrc & 4) | ||
529 | desc_handle_irq(start+2, desc); | ||
530 | } | ||
531 | } | ||
532 | |||
533 | /* uart demux entry points */ | ||
534 | |||
535 | static void | ||
536 | s3c_irq_demux_uart0(unsigned int irq, | ||
537 | struct irq_desc *desc) | ||
538 | { | ||
539 | irq = irq; | ||
540 | s3c_irq_demux_uart(IRQ_S3CUART_RX0); | ||
541 | } | ||
542 | |||
543 | static void | ||
544 | s3c_irq_demux_uart1(unsigned int irq, | ||
545 | struct irq_desc *desc) | ||
546 | { | ||
547 | irq = irq; | ||
548 | s3c_irq_demux_uart(IRQ_S3CUART_RX1); | ||
549 | } | ||
550 | |||
551 | static void | ||
552 | s3c_irq_demux_uart2(unsigned int irq, | ||
553 | struct irq_desc *desc) | ||
554 | { | ||
555 | irq = irq; | ||
556 | s3c_irq_demux_uart(IRQ_S3CUART_RX2); | ||
557 | } | ||
558 | |||
559 | static void | ||
560 | s3c_irq_demux_extint8(unsigned int irq, | ||
561 | struct irq_desc *desc) | ||
562 | { | ||
563 | unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND); | ||
564 | unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK); | ||
565 | |||
566 | eintpnd &= ~eintmsk; | ||
567 | eintpnd &= ~0xff; /* ignore lower irqs */ | ||
568 | |||
569 | /* we may as well handle all the pending IRQs here */ | ||
570 | |||
571 | while (eintpnd) { | ||
572 | irq = __ffs(eintpnd); | ||
573 | eintpnd &= ~(1<<irq); | ||
574 | |||
575 | irq += (IRQ_EINT4 - 4); | ||
576 | desc_handle_irq(irq, irq_desc + irq); | ||
577 | } | ||
578 | |||
579 | } | ||
580 | |||
581 | static void | ||
582 | s3c_irq_demux_extint4t7(unsigned int irq, | ||
583 | struct irq_desc *desc) | ||
584 | { | ||
585 | unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND); | ||
586 | unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK); | ||
587 | |||
588 | eintpnd &= ~eintmsk; | ||
589 | eintpnd &= 0xff; /* only lower irqs */ | ||
590 | |||
591 | /* we may as well handle all the pending IRQs here */ | ||
592 | |||
593 | while (eintpnd) { | ||
594 | irq = __ffs(eintpnd); | ||
595 | eintpnd &= ~(1<<irq); | ||
596 | |||
597 | irq += (IRQ_EINT4 - 4); | ||
598 | |||
599 | desc_handle_irq(irq, irq_desc + irq); | ||
600 | } | ||
601 | } | ||
602 | |||
603 | #ifdef CONFIG_PM | ||
604 | |||
605 | static struct sleep_save irq_save[] = { | ||
606 | SAVE_ITEM(S3C2410_INTMSK), | ||
607 | SAVE_ITEM(S3C2410_INTSUBMSK), | ||
608 | }; | ||
609 | |||
610 | /* the extint values move between the s3c2410/s3c2440 and the s3c2412 | ||
611 | * so we use an array to hold them, and to calculate the address of | ||
612 | * the register at run-time | ||
613 | */ | ||
614 | |||
615 | static unsigned long save_extint[3]; | ||
616 | static unsigned long save_eintflt[4]; | ||
617 | static unsigned long save_eintmask; | ||
618 | |||
619 | int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state) | ||
620 | { | ||
621 | unsigned int i; | ||
622 | |||
623 | for (i = 0; i < ARRAY_SIZE(save_extint); i++) | ||
624 | save_extint[i] = __raw_readl(S3C24XX_EXTINT0 + (i*4)); | ||
625 | |||
626 | for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) | ||
627 | save_eintflt[i] = __raw_readl(S3C24XX_EINFLT0 + (i*4)); | ||
628 | |||
629 | s3c2410_pm_do_save(irq_save, ARRAY_SIZE(irq_save)); | ||
630 | save_eintmask = __raw_readl(S3C24XX_EINTMASK); | ||
631 | |||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | int s3c24xx_irq_resume(struct sys_device *dev) | ||
636 | { | ||
637 | unsigned int i; | ||
638 | |||
639 | for (i = 0; i < ARRAY_SIZE(save_extint); i++) | ||
640 | __raw_writel(save_extint[i], S3C24XX_EXTINT0 + (i*4)); | ||
641 | |||
642 | for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) | ||
643 | __raw_writel(save_eintflt[i], S3C24XX_EINFLT0 + (i*4)); | ||
644 | |||
645 | s3c2410_pm_do_restore(irq_save, ARRAY_SIZE(irq_save)); | ||
646 | __raw_writel(save_eintmask, S3C24XX_EINTMASK); | ||
647 | |||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | #else | ||
652 | #define s3c24xx_irq_suspend NULL | ||
653 | #define s3c24xx_irq_resume NULL | ||
654 | #endif | ||
655 | |||
656 | /* s3c24xx_init_irq | ||
657 | * | ||
658 | * Initialise S3C2410 IRQ system | ||
659 | */ | ||
660 | |||
661 | void __init s3c24xx_init_irq(void) | ||
662 | { | ||
663 | unsigned long pend; | ||
664 | unsigned long last; | ||
665 | int irqno; | ||
666 | int i; | ||
667 | |||
668 | irqdbf("s3c2410_init_irq: clearing interrupt status flags\n"); | ||
669 | |||
670 | /* first, clear all interrupts pending... */ | ||
671 | |||
672 | last = 0; | ||
673 | for (i = 0; i < 4; i++) { | ||
674 | pend = __raw_readl(S3C24XX_EINTPEND); | ||
675 | |||
676 | if (pend == 0 || pend == last) | ||
677 | break; | ||
678 | |||
679 | __raw_writel(pend, S3C24XX_EINTPEND); | ||
680 | printk("irq: clearing pending ext status %08x\n", (int)pend); | ||
681 | last = pend; | ||
682 | } | ||
683 | |||
684 | last = 0; | ||
685 | for (i = 0; i < 4; i++) { | ||
686 | pend = __raw_readl(S3C2410_INTPND); | ||
687 | |||
688 | if (pend == 0 || pend == last) | ||
689 | break; | ||
690 | |||
691 | __raw_writel(pend, S3C2410_SRCPND); | ||
692 | __raw_writel(pend, S3C2410_INTPND); | ||
693 | printk("irq: clearing pending status %08x\n", (int)pend); | ||
694 | last = pend; | ||
695 | } | ||
696 | |||
697 | last = 0; | ||
698 | for (i = 0; i < 4; i++) { | ||
699 | pend = __raw_readl(S3C2410_SUBSRCPND); | ||
700 | |||
701 | if (pend == 0 || pend == last) | ||
702 | break; | ||
703 | |||
704 | printk("irq: clearing subpending status %08x\n", (int)pend); | ||
705 | __raw_writel(pend, S3C2410_SUBSRCPND); | ||
706 | last = pend; | ||
707 | } | ||
708 | |||
709 | /* register the main interrupts */ | ||
710 | |||
711 | irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n"); | ||
712 | |||
713 | for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) { | ||
714 | /* set all the s3c2410 internal irqs */ | ||
715 | |||
716 | switch (irqno) { | ||
717 | /* deal with the special IRQs (cascaded) */ | ||
718 | |||
719 | case IRQ_EINT4t7: | ||
720 | case IRQ_EINT8t23: | ||
721 | case IRQ_UART0: | ||
722 | case IRQ_UART1: | ||
723 | case IRQ_UART2: | ||
724 | case IRQ_ADCPARENT: | ||
725 | set_irq_chip(irqno, &s3c_irq_level_chip); | ||
726 | set_irq_handler(irqno, handle_level_irq); | ||
727 | break; | ||
728 | |||
729 | case IRQ_RESERVED6: | ||
730 | case IRQ_RESERVED24: | ||
731 | /* no IRQ here */ | ||
732 | break; | ||
733 | |||
734 | default: | ||
735 | //irqdbf("registering irq %d (s3c irq)\n", irqno); | ||
736 | set_irq_chip(irqno, &s3c_irq_chip); | ||
737 | set_irq_handler(irqno, handle_edge_irq); | ||
738 | set_irq_flags(irqno, IRQF_VALID); | ||
739 | } | ||
740 | } | ||
741 | |||
742 | /* setup the cascade irq handlers */ | ||
743 | |||
744 | set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7); | ||
745 | set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8); | ||
746 | |||
747 | set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0); | ||
748 | set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1); | ||
749 | set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2); | ||
750 | set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc); | ||
751 | |||
752 | /* external interrupts */ | ||
753 | |||
754 | for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { | ||
755 | irqdbf("registering irq %d (ext int)\n", irqno); | ||
756 | set_irq_chip(irqno, &s3c_irq_eint0t4); | ||
757 | set_irq_handler(irqno, handle_edge_irq); | ||
758 | set_irq_flags(irqno, IRQF_VALID); | ||
759 | } | ||
760 | |||
761 | for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { | ||
762 | irqdbf("registering irq %d (extended s3c irq)\n", irqno); | ||
763 | set_irq_chip(irqno, &s3c_irqext_chip); | ||
764 | set_irq_handler(irqno, handle_edge_irq); | ||
765 | set_irq_flags(irqno, IRQF_VALID); | ||
766 | } | ||
767 | |||
768 | /* register the uart interrupts */ | ||
769 | |||
770 | irqdbf("s3c2410: registering external interrupts\n"); | ||
771 | |||
772 | for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) { | ||
773 | irqdbf("registering irq %d (s3c uart0 irq)\n", irqno); | ||
774 | set_irq_chip(irqno, &s3c_irq_uart0); | ||
775 | set_irq_handler(irqno, handle_level_irq); | ||
776 | set_irq_flags(irqno, IRQF_VALID); | ||
777 | } | ||
778 | |||
779 | for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) { | ||
780 | irqdbf("registering irq %d (s3c uart1 irq)\n", irqno); | ||
781 | set_irq_chip(irqno, &s3c_irq_uart1); | ||
782 | set_irq_handler(irqno, handle_level_irq); | ||
783 | set_irq_flags(irqno, IRQF_VALID); | ||
784 | } | ||
785 | |||
786 | for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) { | ||
787 | irqdbf("registering irq %d (s3c uart2 irq)\n", irqno); | ||
788 | set_irq_chip(irqno, &s3c_irq_uart2); | ||
789 | set_irq_handler(irqno, handle_level_irq); | ||
790 | set_irq_flags(irqno, IRQF_VALID); | ||
791 | } | ||
792 | |||
793 | for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) { | ||
794 | irqdbf("registering irq %d (s3c adc irq)\n", irqno); | ||
795 | set_irq_chip(irqno, &s3c_irq_adc); | ||
796 | set_irq_handler(irqno, handle_edge_irq); | ||
797 | set_irq_flags(irqno, IRQF_VALID); | ||
798 | } | ||
799 | |||
800 | irqdbf("s3c2410: registered interrupt handlers\n"); | ||
801 | } | ||
diff --git a/arch/arm/plat-s3c24xx/pm-simtec.c b/arch/arm/plat-s3c24xx/pm-simtec.c new file mode 100644 index 000000000000..bd965f2feeca --- /dev/null +++ b/arch/arm/plat-s3c24xx/pm-simtec.c | |||
@@ -0,0 +1,66 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/pm-simtec.c | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * http://armlinux.simtec.co.uk/ | ||
7 | * | ||
8 | * Power Management helpers for Simtec S3C24XX implementations | ||
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 version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/timer.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/device.h> | ||
22 | |||
23 | #include <asm/mach/arch.h> | ||
24 | #include <asm/mach/map.h> | ||
25 | |||
26 | #include <asm/hardware.h> | ||
27 | #include <asm/io.h> | ||
28 | |||
29 | #include <asm/arch/map.h> | ||
30 | #include <asm/arch/regs-gpio.h> | ||
31 | #include <asm/arch/regs-mem.h> | ||
32 | |||
33 | #include <asm/mach-types.h> | ||
34 | |||
35 | #include <asm/plat-s3c24xx/pm.h> | ||
36 | |||
37 | #define COPYRIGHT ", (c) 2005 Simtec Electronics" | ||
38 | |||
39 | /* pm_simtec_init | ||
40 | * | ||
41 | * enable the power management functions | ||
42 | */ | ||
43 | |||
44 | static __init int pm_simtec_init(void) | ||
45 | { | ||
46 | unsigned long gstatus4; | ||
47 | |||
48 | /* check which machine we are running on */ | ||
49 | |||
50 | if (!machine_is_bast() && !machine_is_vr1000() && | ||
51 | !machine_is_anubis() && !machine_is_osiris() && | ||
52 | !machine_is_aml_m5900()) | ||
53 | return 0; | ||
54 | |||
55 | printk(KERN_INFO "Simtec Board Power Manangement" COPYRIGHT "\n"); | ||
56 | |||
57 | gstatus4 = (__raw_readl(S3C2410_BANKCON7) & 0x3) << 30; | ||
58 | gstatus4 |= (__raw_readl(S3C2410_BANKCON6) & 0x3) << 28; | ||
59 | gstatus4 |= (__raw_readl(S3C2410_BANKSIZE) & S3C2410_BANKSIZE_MASK); | ||
60 | |||
61 | __raw_writel(gstatus4, S3C2410_GSTATUS4); | ||
62 | |||
63 | return s3c2410_pm_init(); | ||
64 | } | ||
65 | |||
66 | arch_initcall(pm_simtec_init); | ||
diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c new file mode 100644 index 000000000000..ecf68d611904 --- /dev/null +++ b/arch/arm/plat-s3c24xx/pm.c | |||
@@ -0,0 +1,659 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/pm.c | ||
2 | * | ||
3 | * Copyright (c) 2004,2006 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C24XX Power Manager (Suspend-To-RAM) support | ||
7 | * | ||
8 | * See Documentation/arm/Samsung-S3C24XX/Suspend.txt for more information | ||
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 | * Parts based on arch/arm/mach-pxa/pm.c | ||
25 | * | ||
26 | * Thanks to Dimitry Andric for debugging | ||
27 | */ | ||
28 | |||
29 | #include <linux/init.h> | ||
30 | #include <linux/suspend.h> | ||
31 | #include <linux/errno.h> | ||
32 | #include <linux/time.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/crc32.h> | ||
35 | #include <linux/ioport.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include <linux/serial_core.h> | ||
38 | |||
39 | #include <asm/cacheflush.h> | ||
40 | #include <asm/hardware.h> | ||
41 | #include <asm/io.h> | ||
42 | |||
43 | #include <asm/arch/regs-serial.h> | ||
44 | #include <asm/arch/regs-clock.h> | ||
45 | #include <asm/arch/regs-gpio.h> | ||
46 | #include <asm/arch/regs-mem.h> | ||
47 | #include <asm/arch/regs-irq.h> | ||
48 | |||
49 | #include <asm/mach/time.h> | ||
50 | |||
51 | #include <asm/plat-s3c24xx/pm.h> | ||
52 | |||
53 | /* for external use */ | ||
54 | |||
55 | unsigned long s3c_pm_flags; | ||
56 | |||
57 | #define PFX "s3c24xx-pm: " | ||
58 | |||
59 | static struct sleep_save core_save[] = { | ||
60 | SAVE_ITEM(S3C2410_LOCKTIME), | ||
61 | SAVE_ITEM(S3C2410_CLKCON), | ||
62 | |||
63 | /* we restore the timings here, with the proviso that the board | ||
64 | * brings the system up in an slower, or equal frequency setting | ||
65 | * to the original system. | ||
66 | * | ||
67 | * if we cannot guarantee this, then things are going to go very | ||
68 | * wrong here, as we modify the refresh and both pll settings. | ||
69 | */ | ||
70 | |||
71 | SAVE_ITEM(S3C2410_BWSCON), | ||
72 | SAVE_ITEM(S3C2410_BANKCON0), | ||
73 | SAVE_ITEM(S3C2410_BANKCON1), | ||
74 | SAVE_ITEM(S3C2410_BANKCON2), | ||
75 | SAVE_ITEM(S3C2410_BANKCON3), | ||
76 | SAVE_ITEM(S3C2410_BANKCON4), | ||
77 | SAVE_ITEM(S3C2410_BANKCON5), | ||
78 | |||
79 | SAVE_ITEM(S3C2410_CLKDIVN), | ||
80 | SAVE_ITEM(S3C2410_MPLLCON), | ||
81 | SAVE_ITEM(S3C2410_UPLLCON), | ||
82 | SAVE_ITEM(S3C2410_CLKSLOW), | ||
83 | SAVE_ITEM(S3C2410_REFRESH), | ||
84 | }; | ||
85 | |||
86 | static struct sleep_save gpio_save[] = { | ||
87 | SAVE_ITEM(S3C2410_GPACON), | ||
88 | SAVE_ITEM(S3C2410_GPADAT), | ||
89 | |||
90 | SAVE_ITEM(S3C2410_GPBCON), | ||
91 | SAVE_ITEM(S3C2410_GPBDAT), | ||
92 | SAVE_ITEM(S3C2410_GPBUP), | ||
93 | |||
94 | SAVE_ITEM(S3C2410_GPCCON), | ||
95 | SAVE_ITEM(S3C2410_GPCDAT), | ||
96 | SAVE_ITEM(S3C2410_GPCUP), | ||
97 | |||
98 | SAVE_ITEM(S3C2410_GPDCON), | ||
99 | SAVE_ITEM(S3C2410_GPDDAT), | ||
100 | SAVE_ITEM(S3C2410_GPDUP), | ||
101 | |||
102 | SAVE_ITEM(S3C2410_GPECON), | ||
103 | SAVE_ITEM(S3C2410_GPEDAT), | ||
104 | SAVE_ITEM(S3C2410_GPEUP), | ||
105 | |||
106 | SAVE_ITEM(S3C2410_GPFCON), | ||
107 | SAVE_ITEM(S3C2410_GPFDAT), | ||
108 | SAVE_ITEM(S3C2410_GPFUP), | ||
109 | |||
110 | SAVE_ITEM(S3C2410_GPGCON), | ||
111 | SAVE_ITEM(S3C2410_GPGDAT), | ||
112 | SAVE_ITEM(S3C2410_GPGUP), | ||
113 | |||
114 | SAVE_ITEM(S3C2410_GPHCON), | ||
115 | SAVE_ITEM(S3C2410_GPHDAT), | ||
116 | SAVE_ITEM(S3C2410_GPHUP), | ||
117 | |||
118 | SAVE_ITEM(S3C2410_DCLKCON), | ||
119 | }; | ||
120 | |||
121 | #ifdef CONFIG_S3C2410_PM_DEBUG | ||
122 | |||
123 | #define SAVE_UART(va) \ | ||
124 | SAVE_ITEM((va) + S3C2410_ULCON), \ | ||
125 | SAVE_ITEM((va) + S3C2410_UCON), \ | ||
126 | SAVE_ITEM((va) + S3C2410_UFCON), \ | ||
127 | SAVE_ITEM((va) + S3C2410_UMCON), \ | ||
128 | SAVE_ITEM((va) + S3C2410_UBRDIV) | ||
129 | |||
130 | static struct sleep_save uart_save[] = { | ||
131 | SAVE_UART(S3C24XX_VA_UART0), | ||
132 | SAVE_UART(S3C24XX_VA_UART1), | ||
133 | #ifndef CONFIG_CPU_S3C2400 | ||
134 | SAVE_UART(S3C24XX_VA_UART2), | ||
135 | #endif | ||
136 | }; | ||
137 | |||
138 | /* debug | ||
139 | * | ||
140 | * we send the debug to printascii() to allow it to be seen if the | ||
141 | * system never wakes up from the sleep | ||
142 | */ | ||
143 | |||
144 | extern void printascii(const char *); | ||
145 | |||
146 | void pm_dbg(const char *fmt, ...) | ||
147 | { | ||
148 | va_list va; | ||
149 | char buff[256]; | ||
150 | |||
151 | va_start(va, fmt); | ||
152 | vsprintf(buff, fmt, va); | ||
153 | va_end(va); | ||
154 | |||
155 | printascii(buff); | ||
156 | } | ||
157 | |||
158 | static void s3c2410_pm_debug_init(void) | ||
159 | { | ||
160 | unsigned long tmp = __raw_readl(S3C2410_CLKCON); | ||
161 | |||
162 | /* re-start uart clocks */ | ||
163 | tmp |= S3C2410_CLKCON_UART0; | ||
164 | tmp |= S3C2410_CLKCON_UART1; | ||
165 | tmp |= S3C2410_CLKCON_UART2; | ||
166 | |||
167 | __raw_writel(tmp, S3C2410_CLKCON); | ||
168 | udelay(10); | ||
169 | } | ||
170 | |||
171 | #define DBG(fmt...) pm_dbg(fmt) | ||
172 | #else | ||
173 | #define DBG(fmt...) printk(KERN_DEBUG fmt) | ||
174 | |||
175 | #define s3c2410_pm_debug_init() do { } while(0) | ||
176 | |||
177 | static struct sleep_save uart_save[] = {}; | ||
178 | #endif | ||
179 | |||
180 | #if defined(CONFIG_S3C2410_PM_CHECK) && CONFIG_S3C2410_PM_CHECK_CHUNKSIZE != 0 | ||
181 | |||
182 | /* suspend checking code... | ||
183 | * | ||
184 | * this next area does a set of crc checks over all the installed | ||
185 | * memory, so the system can verify if the resume was ok. | ||
186 | * | ||
187 | * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, | ||
188 | * increasing it will mean that the area corrupted will be less easy to spot, | ||
189 | * and reducing the size will cause the CRC save area to grow | ||
190 | */ | ||
191 | |||
192 | #define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024) | ||
193 | |||
194 | static u32 crc_size; /* size needed for the crc block */ | ||
195 | static u32 *crcs; /* allocated over suspend/resume */ | ||
196 | |||
197 | typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); | ||
198 | |||
199 | /* s3c2410_pm_run_res | ||
200 | * | ||
201 | * go thorugh the given resource list, and look for system ram | ||
202 | */ | ||
203 | |||
204 | static void s3c2410_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) | ||
205 | { | ||
206 | while (ptr != NULL) { | ||
207 | if (ptr->child != NULL) | ||
208 | s3c2410_pm_run_res(ptr->child, fn, arg); | ||
209 | |||
210 | if ((ptr->flags & IORESOURCE_MEM) && | ||
211 | strcmp(ptr->name, "System RAM") == 0) { | ||
212 | DBG("Found system RAM at %08lx..%08lx\n", | ||
213 | ptr->start, ptr->end); | ||
214 | arg = (fn)(ptr, arg); | ||
215 | } | ||
216 | |||
217 | ptr = ptr->sibling; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static void s3c2410_pm_run_sysram(run_fn_t fn, u32 *arg) | ||
222 | { | ||
223 | s3c2410_pm_run_res(&iomem_resource, fn, arg); | ||
224 | } | ||
225 | |||
226 | static u32 *s3c2410_pm_countram(struct resource *res, u32 *val) | ||
227 | { | ||
228 | u32 size = (u32)(res->end - res->start)+1; | ||
229 | |||
230 | size += CHECK_CHUNKSIZE-1; | ||
231 | size /= CHECK_CHUNKSIZE; | ||
232 | |||
233 | DBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size); | ||
234 | |||
235 | *val += size * sizeof(u32); | ||
236 | return val; | ||
237 | } | ||
238 | |||
239 | /* s3c2410_pm_prepare_check | ||
240 | * | ||
241 | * prepare the necessary information for creating the CRCs. This | ||
242 | * must be done before the final save, as it will require memory | ||
243 | * allocating, and thus touching bits of the kernel we do not | ||
244 | * know about. | ||
245 | */ | ||
246 | |||
247 | static void s3c2410_pm_check_prepare(void) | ||
248 | { | ||
249 | crc_size = 0; | ||
250 | |||
251 | s3c2410_pm_run_sysram(s3c2410_pm_countram, &crc_size); | ||
252 | |||
253 | DBG("s3c2410_pm_prepare_check: %u checks needed\n", crc_size); | ||
254 | |||
255 | crcs = kmalloc(crc_size+4, GFP_KERNEL); | ||
256 | if (crcs == NULL) | ||
257 | printk(KERN_ERR "Cannot allocated CRC save area\n"); | ||
258 | } | ||
259 | |||
260 | static u32 *s3c2410_pm_makecheck(struct resource *res, u32 *val) | ||
261 | { | ||
262 | unsigned long addr, left; | ||
263 | |||
264 | for (addr = res->start; addr < res->end; | ||
265 | addr += CHECK_CHUNKSIZE) { | ||
266 | left = res->end - addr; | ||
267 | |||
268 | if (left > CHECK_CHUNKSIZE) | ||
269 | left = CHECK_CHUNKSIZE; | ||
270 | |||
271 | *val = crc32_le(~0, phys_to_virt(addr), left); | ||
272 | val++; | ||
273 | } | ||
274 | |||
275 | return val; | ||
276 | } | ||
277 | |||
278 | /* s3c2410_pm_check_store | ||
279 | * | ||
280 | * compute the CRC values for the memory blocks before the final | ||
281 | * sleep. | ||
282 | */ | ||
283 | |||
284 | static void s3c2410_pm_check_store(void) | ||
285 | { | ||
286 | if (crcs != NULL) | ||
287 | s3c2410_pm_run_sysram(s3c2410_pm_makecheck, crcs); | ||
288 | } | ||
289 | |||
290 | /* in_region | ||
291 | * | ||
292 | * return TRUE if the area defined by ptr..ptr+size contatins the | ||
293 | * what..what+whatsz | ||
294 | */ | ||
295 | |||
296 | static inline int in_region(void *ptr, int size, void *what, size_t whatsz) | ||
297 | { | ||
298 | if ((what+whatsz) < ptr) | ||
299 | return 0; | ||
300 | |||
301 | if (what > (ptr+size)) | ||
302 | return 0; | ||
303 | |||
304 | return 1; | ||
305 | } | ||
306 | |||
307 | static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val) | ||
308 | { | ||
309 | void *save_at = phys_to_virt(s3c2410_sleep_save_phys); | ||
310 | unsigned long addr; | ||
311 | unsigned long left; | ||
312 | void *ptr; | ||
313 | u32 calc; | ||
314 | |||
315 | for (addr = res->start; addr < res->end; | ||
316 | addr += CHECK_CHUNKSIZE) { | ||
317 | left = res->end - addr; | ||
318 | |||
319 | if (left > CHECK_CHUNKSIZE) | ||
320 | left = CHECK_CHUNKSIZE; | ||
321 | |||
322 | ptr = phys_to_virt(addr); | ||
323 | |||
324 | if (in_region(ptr, left, crcs, crc_size)) { | ||
325 | DBG("skipping %08lx, has crc block in\n", addr); | ||
326 | goto skip_check; | ||
327 | } | ||
328 | |||
329 | if (in_region(ptr, left, save_at, 32*4 )) { | ||
330 | DBG("skipping %08lx, has save block in\n", addr); | ||
331 | goto skip_check; | ||
332 | } | ||
333 | |||
334 | /* calculate and check the checksum */ | ||
335 | |||
336 | calc = crc32_le(~0, ptr, left); | ||
337 | if (calc != *val) { | ||
338 | printk(KERN_ERR PFX "Restore CRC error at " | ||
339 | "%08lx (%08x vs %08x)\n", addr, calc, *val); | ||
340 | |||
341 | DBG("Restore CRC error at %08lx (%08x vs %08x)\n", | ||
342 | addr, calc, *val); | ||
343 | } | ||
344 | |||
345 | skip_check: | ||
346 | val++; | ||
347 | } | ||
348 | |||
349 | return val; | ||
350 | } | ||
351 | |||
352 | /* s3c2410_pm_check_restore | ||
353 | * | ||
354 | * check the CRCs after the restore event and free the memory used | ||
355 | * to hold them | ||
356 | */ | ||
357 | |||
358 | static void s3c2410_pm_check_restore(void) | ||
359 | { | ||
360 | if (crcs != NULL) { | ||
361 | s3c2410_pm_run_sysram(s3c2410_pm_runcheck, crcs); | ||
362 | kfree(crcs); | ||
363 | crcs = NULL; | ||
364 | } | ||
365 | } | ||
366 | |||
367 | #else | ||
368 | |||
369 | #define s3c2410_pm_check_prepare() do { } while(0) | ||
370 | #define s3c2410_pm_check_restore() do { } while(0) | ||
371 | #define s3c2410_pm_check_store() do { } while(0) | ||
372 | #endif | ||
373 | |||
374 | /* helper functions to save and restore register state */ | ||
375 | |||
376 | void s3c2410_pm_do_save(struct sleep_save *ptr, int count) | ||
377 | { | ||
378 | for (; count > 0; count--, ptr++) { | ||
379 | ptr->val = __raw_readl(ptr->reg); | ||
380 | DBG("saved %p value %08lx\n", ptr->reg, ptr->val); | ||
381 | } | ||
382 | } | ||
383 | |||
384 | /* s3c2410_pm_do_restore | ||
385 | * | ||
386 | * restore the system from the given list of saved registers | ||
387 | * | ||
388 | * Note, we do not use DBG() in here, as the system may not have | ||
389 | * restore the UARTs state yet | ||
390 | */ | ||
391 | |||
392 | void s3c2410_pm_do_restore(struct sleep_save *ptr, int count) | ||
393 | { | ||
394 | for (; count > 0; count--, ptr++) { | ||
395 | printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n", | ||
396 | ptr->reg, ptr->val, __raw_readl(ptr->reg)); | ||
397 | |||
398 | __raw_writel(ptr->val, ptr->reg); | ||
399 | } | ||
400 | } | ||
401 | |||
402 | /* s3c2410_pm_do_restore_core | ||
403 | * | ||
404 | * similar to s3c2410_pm_do_restore_core | ||
405 | * | ||
406 | * WARNING: Do not put any debug in here that may effect memory or use | ||
407 | * peripherals, as things may be changing! | ||
408 | */ | ||
409 | |||
410 | static void s3c2410_pm_do_restore_core(struct sleep_save *ptr, int count) | ||
411 | { | ||
412 | for (; count > 0; count--, ptr++) { | ||
413 | __raw_writel(ptr->val, ptr->reg); | ||
414 | } | ||
415 | } | ||
416 | |||
417 | /* s3c2410_pm_show_resume_irqs | ||
418 | * | ||
419 | * print any IRQs asserted at resume time (ie, we woke from) | ||
420 | */ | ||
421 | |||
422 | static void s3c2410_pm_show_resume_irqs(int start, unsigned long which, | ||
423 | unsigned long mask) | ||
424 | { | ||
425 | int i; | ||
426 | |||
427 | which &= ~mask; | ||
428 | |||
429 | for (i = 0; i <= 31; i++) { | ||
430 | if ((which) & (1L<<i)) { | ||
431 | DBG("IRQ %d asserted at resume\n", start+i); | ||
432 | } | ||
433 | } | ||
434 | } | ||
435 | |||
436 | /* s3c2410_pm_check_resume_pin | ||
437 | * | ||
438 | * check to see if the pin is configured correctly for sleep mode, and | ||
439 | * make any necessary adjustments if it is not | ||
440 | */ | ||
441 | |||
442 | static void s3c2410_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs) | ||
443 | { | ||
444 | unsigned long irqstate; | ||
445 | unsigned long pinstate; | ||
446 | int irq = s3c2410_gpio_getirq(pin); | ||
447 | |||
448 | if (irqoffs < 4) | ||
449 | irqstate = s3c_irqwake_intmask & (1L<<irqoffs); | ||
450 | else | ||
451 | irqstate = s3c_irqwake_eintmask & (1L<<irqoffs); | ||
452 | |||
453 | pinstate = s3c2410_gpio_getcfg(pin); | ||
454 | |||
455 | if (!irqstate) { | ||
456 | if (pinstate == S3C2410_GPIO_IRQ) | ||
457 | DBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin); | ||
458 | } else { | ||
459 | if (pinstate == S3C2410_GPIO_IRQ) { | ||
460 | DBG("Disabling IRQ %d (pin %d)\n", irq, pin); | ||
461 | s3c2410_gpio_cfgpin(pin, S3C2410_GPIO_INPUT); | ||
462 | } | ||
463 | } | ||
464 | } | ||
465 | |||
466 | /* s3c2410_pm_configure_extint | ||
467 | * | ||
468 | * configure all external interrupt pins | ||
469 | */ | ||
470 | |||
471 | static void s3c2410_pm_configure_extint(void) | ||
472 | { | ||
473 | int pin; | ||
474 | |||
475 | /* for each of the external interrupts (EINT0..EINT15) we | ||
476 | * need to check wether it is an external interrupt source, | ||
477 | * and then configure it as an input if it is not | ||
478 | */ | ||
479 | |||
480 | for (pin = S3C2410_GPF0; pin <= S3C2410_GPF7; pin++) { | ||
481 | s3c2410_pm_check_resume_pin(pin, pin - S3C2410_GPF0); | ||
482 | } | ||
483 | |||
484 | for (pin = S3C2410_GPG0; pin <= S3C2410_GPG7; pin++) { | ||
485 | s3c2410_pm_check_resume_pin(pin, (pin - S3C2410_GPG0)+8); | ||
486 | } | ||
487 | } | ||
488 | |||
489 | void (*pm_cpu_prep)(void); | ||
490 | void (*pm_cpu_sleep)(void); | ||
491 | |||
492 | #define any_allowed(mask, allow) (((mask) & (allow)) != (allow)) | ||
493 | |||
494 | /* s3c2410_pm_enter | ||
495 | * | ||
496 | * central control for sleep/resume process | ||
497 | */ | ||
498 | |||
499 | static int s3c2410_pm_enter(suspend_state_t state) | ||
500 | { | ||
501 | unsigned long regs_save[16]; | ||
502 | |||
503 | /* ensure the debug is initialised (if enabled) */ | ||
504 | |||
505 | s3c2410_pm_debug_init(); | ||
506 | |||
507 | DBG("s3c2410_pm_enter(%d)\n", state); | ||
508 | |||
509 | if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { | ||
510 | printk(KERN_ERR PFX "error: no cpu sleep functions set\n"); | ||
511 | return -EINVAL; | ||
512 | } | ||
513 | |||
514 | if (state != PM_SUSPEND_MEM) { | ||
515 | printk(KERN_ERR PFX "error: only PM_SUSPEND_MEM supported\n"); | ||
516 | return -EINVAL; | ||
517 | } | ||
518 | |||
519 | /* check if we have anything to wake-up with... bad things seem | ||
520 | * to happen if you suspend with no wakeup (system will often | ||
521 | * require a full power-cycle) | ||
522 | */ | ||
523 | |||
524 | if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) && | ||
525 | !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) { | ||
526 | printk(KERN_ERR PFX "No sources enabled for wake-up!\n"); | ||
527 | printk(KERN_ERR PFX "Aborting sleep\n"); | ||
528 | return -EINVAL; | ||
529 | } | ||
530 | |||
531 | /* prepare check area if configured */ | ||
532 | |||
533 | s3c2410_pm_check_prepare(); | ||
534 | |||
535 | /* store the physical address of the register recovery block */ | ||
536 | |||
537 | s3c2410_sleep_save_phys = virt_to_phys(regs_save); | ||
538 | |||
539 | DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys); | ||
540 | |||
541 | /* save all necessary core registers not covered by the drivers */ | ||
542 | |||
543 | s3c2410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save)); | ||
544 | s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save)); | ||
545 | s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save)); | ||
546 | |||
547 | /* set the irq configuration for wake */ | ||
548 | |||
549 | s3c2410_pm_configure_extint(); | ||
550 | |||
551 | DBG("sleep: irq wakeup masks: %08lx,%08lx\n", | ||
552 | s3c_irqwake_intmask, s3c_irqwake_eintmask); | ||
553 | |||
554 | __raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK); | ||
555 | __raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK); | ||
556 | |||
557 | /* ack any outstanding external interrupts before we go to sleep */ | ||
558 | |||
559 | __raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND); | ||
560 | __raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND); | ||
561 | __raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND); | ||
562 | |||
563 | /* call cpu specific preperation */ | ||
564 | |||
565 | pm_cpu_prep(); | ||
566 | |||
567 | /* flush cache back to ram */ | ||
568 | |||
569 | flush_cache_all(); | ||
570 | |||
571 | s3c2410_pm_check_store(); | ||
572 | |||
573 | /* send the cpu to sleep... */ | ||
574 | |||
575 | __raw_writel(0x00, S3C2410_CLKCON); /* turn off clocks over sleep */ | ||
576 | |||
577 | /* s3c2410_cpu_save will also act as our return point from when | ||
578 | * we resume as it saves its own register state, so use the return | ||
579 | * code to differentiate return from save and return from sleep */ | ||
580 | |||
581 | if (s3c2410_cpu_save(regs_save) == 0) { | ||
582 | flush_cache_all(); | ||
583 | pm_cpu_sleep(); | ||
584 | } | ||
585 | |||
586 | /* restore the cpu state */ | ||
587 | |||
588 | cpu_init(); | ||
589 | |||
590 | /* restore the system state */ | ||
591 | |||
592 | s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); | ||
593 | s3c2410_pm_do_restore(gpio_save, ARRAY_SIZE(gpio_save)); | ||
594 | s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); | ||
595 | |||
596 | s3c2410_pm_debug_init(); | ||
597 | |||
598 | /* check what irq (if any) restored the system */ | ||
599 | |||
600 | DBG("post sleep: IRQs 0x%08x, 0x%08x\n", | ||
601 | __raw_readl(S3C2410_SRCPND), | ||
602 | __raw_readl(S3C2410_EINTPEND)); | ||
603 | |||
604 | s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND), | ||
605 | s3c_irqwake_intmask); | ||
606 | |||
607 | s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND), | ||
608 | s3c_irqwake_eintmask); | ||
609 | |||
610 | DBG("post sleep, preparing to return\n"); | ||
611 | |||
612 | s3c2410_pm_check_restore(); | ||
613 | |||
614 | /* ok, let's return from sleep */ | ||
615 | |||
616 | DBG("S3C2410 PM Resume (post-restore)\n"); | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | /* | ||
621 | * Called after processes are frozen, but before we shut down devices. | ||
622 | */ | ||
623 | static int s3c2410_pm_prepare(suspend_state_t state) | ||
624 | { | ||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | /* | ||
629 | * Called after devices are re-setup, but before processes are thawed. | ||
630 | */ | ||
631 | static int s3c2410_pm_finish(suspend_state_t state) | ||
632 | { | ||
633 | return 0; | ||
634 | } | ||
635 | |||
636 | /* | ||
637 | * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. | ||
638 | */ | ||
639 | static struct pm_ops s3c2410_pm_ops = { | ||
640 | .pm_disk_mode = PM_DISK_FIRMWARE, | ||
641 | .prepare = s3c2410_pm_prepare, | ||
642 | .enter = s3c2410_pm_enter, | ||
643 | .finish = s3c2410_pm_finish, | ||
644 | }; | ||
645 | |||
646 | /* s3c2410_pm_init | ||
647 | * | ||
648 | * Attach the power management functions. This should be called | ||
649 | * from the board specific initialisation if the board supports | ||
650 | * it. | ||
651 | */ | ||
652 | |||
653 | int __init s3c2410_pm_init(void) | ||
654 | { | ||
655 | printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n"); | ||
656 | |||
657 | pm_set_ops(&s3c2410_pm_ops); | ||
658 | return 0; | ||
659 | } | ||
diff --git a/arch/arm/plat-s3c24xx/s3c244x-irq.c b/arch/arm/plat-s3c24xx/s3c244x-irq.c new file mode 100644 index 000000000000..a0e39d894014 --- /dev/null +++ b/arch/arm/plat-s3c24xx/s3c244x-irq.c | |||
@@ -0,0 +1,146 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/s3c244x-irq.c | ||
2 | * | ||
3 | * Copyright (c) 2003,2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@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 as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/init.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/ioport.h> | ||
26 | #include <linux/ptrace.h> | ||
27 | #include <linux/sysdev.h> | ||
28 | |||
29 | #include <asm/hardware.h> | ||
30 | #include <asm/irq.h> | ||
31 | #include <asm/io.h> | ||
32 | |||
33 | #include <asm/mach/irq.h> | ||
34 | |||
35 | #include <asm/arch/regs-irq.h> | ||
36 | #include <asm/arch/regs-gpio.h> | ||
37 | |||
38 | #include <asm/plat-s3c24xx/cpu.h> | ||
39 | #include <asm/plat-s3c24xx/pm.h> | ||
40 | #include <asm/plat-s3c24xx/irq.h> | ||
41 | |||
42 | /* camera irq */ | ||
43 | |||
44 | static void s3c_irq_demux_cam(unsigned int irq, | ||
45 | struct irq_desc *desc) | ||
46 | { | ||
47 | unsigned int subsrc, submsk; | ||
48 | struct irq_desc *mydesc; | ||
49 | |||
50 | /* read the current pending interrupts, and the mask | ||
51 | * for what it is available */ | ||
52 | |||
53 | subsrc = __raw_readl(S3C2410_SUBSRCPND); | ||
54 | submsk = __raw_readl(S3C2410_INTSUBMSK); | ||
55 | |||
56 | subsrc &= ~submsk; | ||
57 | subsrc >>= 11; | ||
58 | subsrc &= 3; | ||
59 | |||
60 | if (subsrc != 0) { | ||
61 | if (subsrc & 1) { | ||
62 | mydesc = irq_desc + IRQ_S3C2440_CAM_C; | ||
63 | desc_handle_irq(IRQ_S3C2440_CAM_C, mydesc); | ||
64 | } | ||
65 | if (subsrc & 2) { | ||
66 | mydesc = irq_desc + IRQ_S3C2440_CAM_P; | ||
67 | desc_handle_irq(IRQ_S3C2440_CAM_P, mydesc); | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #define INTMSK_CAM (1UL << (IRQ_CAM - IRQ_EINT0)) | ||
73 | |||
74 | static void | ||
75 | s3c_irq_cam_mask(unsigned int irqno) | ||
76 | { | ||
77 | s3c_irqsub_mask(irqno, INTMSK_CAM, 3<<11); | ||
78 | } | ||
79 | |||
80 | static void | ||
81 | s3c_irq_cam_unmask(unsigned int irqno) | ||
82 | { | ||
83 | s3c_irqsub_unmask(irqno, INTMSK_CAM); | ||
84 | } | ||
85 | |||
86 | static void | ||
87 | s3c_irq_cam_ack(unsigned int irqno) | ||
88 | { | ||
89 | s3c_irqsub_maskack(irqno, INTMSK_CAM, 3<<11); | ||
90 | } | ||
91 | |||
92 | static struct irq_chip s3c_irq_cam = { | ||
93 | .mask = s3c_irq_cam_mask, | ||
94 | .unmask = s3c_irq_cam_unmask, | ||
95 | .ack = s3c_irq_cam_ack, | ||
96 | }; | ||
97 | |||
98 | static int s3c244x_irq_add(struct sys_device *sysdev) | ||
99 | { | ||
100 | unsigned int irqno; | ||
101 | |||
102 | set_irq_chip(IRQ_NFCON, &s3c_irq_level_chip); | ||
103 | set_irq_handler(IRQ_NFCON, handle_level_irq); | ||
104 | set_irq_flags(IRQ_NFCON, IRQF_VALID); | ||
105 | |||
106 | /* add chained handler for camera */ | ||
107 | |||
108 | set_irq_chip(IRQ_CAM, &s3c_irq_level_chip); | ||
109 | set_irq_handler(IRQ_CAM, handle_level_irq); | ||
110 | set_irq_chained_handler(IRQ_CAM, s3c_irq_demux_cam); | ||
111 | |||
112 | for (irqno = IRQ_S3C2440_CAM_C; irqno <= IRQ_S3C2440_CAM_P; irqno++) { | ||
113 | set_irq_chip(irqno, &s3c_irq_cam); | ||
114 | set_irq_handler(irqno, handle_level_irq); | ||
115 | set_irq_flags(irqno, IRQF_VALID); | ||
116 | } | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static struct sysdev_driver s3c2440_irq_driver = { | ||
122 | .add = s3c244x_irq_add, | ||
123 | .suspend = s3c24xx_irq_suspend, | ||
124 | .resume = s3c24xx_irq_resume, | ||
125 | }; | ||
126 | |||
127 | static int s3c2440_irq_init(void) | ||
128 | { | ||
129 | return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_irq_driver); | ||
130 | } | ||
131 | |||
132 | arch_initcall(s3c2440_irq_init); | ||
133 | |||
134 | static struct sysdev_driver s3c2442_irq_driver = { | ||
135 | .add = s3c244x_irq_add, | ||
136 | .suspend = s3c24xx_irq_suspend, | ||
137 | .resume = s3c24xx_irq_resume, | ||
138 | }; | ||
139 | |||
140 | |||
141 | static int s3c2442_irq_init(void) | ||
142 | { | ||
143 | return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_irq_driver); | ||
144 | } | ||
145 | |||
146 | arch_initcall(s3c2442_irq_init); | ||
diff --git a/arch/arm/plat-s3c24xx/s3c244x.c b/arch/arm/plat-s3c24xx/s3c244x.c new file mode 100644 index 000000000000..87aace4c8f8c --- /dev/null +++ b/arch/arm/plat-s3c24xx/s3c244x.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/s3c244x.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2006 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Samsung S3C2440 and S3C2442 Mobile CPU support | ||
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 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/list.h> | ||
17 | #include <linux/timer.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/serial_core.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/sysdev.h> | ||
22 | #include <linux/clk.h> | ||
23 | |||
24 | #include <asm/mach/arch.h> | ||
25 | #include <asm/mach/map.h> | ||
26 | #include <asm/mach/irq.h> | ||
27 | |||
28 | #include <asm/hardware.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <asm/irq.h> | ||
31 | |||
32 | #include <asm/arch/regs-clock.h> | ||
33 | #include <asm/arch/regs-serial.h> | ||
34 | #include <asm/arch/regs-gpio.h> | ||
35 | #include <asm/arch/regs-gpioj.h> | ||
36 | #include <asm/arch/regs-dsc.h> | ||
37 | |||
38 | #include <asm/plat-s3c24xx/s3c2410.h> | ||
39 | #include <asm/plat-s3c24xx/s3c2440.h> | ||
40 | #include "s3c244x.h" | ||
41 | #include <asm/plat-s3c24xx/clock.h> | ||
42 | #include <asm/plat-s3c24xx/devs.h> | ||
43 | #include <asm/plat-s3c24xx/cpu.h> | ||
44 | #include <asm/plat-s3c24xx/pm.h> | ||
45 | |||
46 | static struct map_desc s3c244x_iodesc[] __initdata = { | ||
47 | IODESC_ENT(CLKPWR), | ||
48 | IODESC_ENT(TIMER), | ||
49 | IODESC_ENT(WATCHDOG), | ||
50 | IODESC_ENT(LCD), | ||
51 | }; | ||
52 | |||
53 | /* uart initialisation */ | ||
54 | |||
55 | void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no) | ||
56 | { | ||
57 | s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no); | ||
58 | } | ||
59 | |||
60 | void __init s3c244x_map_io(struct map_desc *mach_desc, int size) | ||
61 | { | ||
62 | /* register our io-tables */ | ||
63 | |||
64 | iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc)); | ||
65 | iotable_init(mach_desc, size); | ||
66 | |||
67 | /* rename any peripherals used differing from the s3c2410 */ | ||
68 | |||
69 | s3c_device_i2c.name = "s3c2440-i2c"; | ||
70 | s3c_device_nand.name = "s3c2440-nand"; | ||
71 | s3c_device_usbgadget.name = "s3c2440-usbgadget"; | ||
72 | } | ||
73 | |||
74 | void __init s3c244x_init_clocks(int xtal) | ||
75 | { | ||
76 | unsigned long clkdiv; | ||
77 | unsigned long camdiv; | ||
78 | unsigned long hclk, fclk, pclk; | ||
79 | int hdiv = 1; | ||
80 | |||
81 | /* now we've got our machine bits initialised, work out what | ||
82 | * clocks we've got */ | ||
83 | |||
84 | fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2; | ||
85 | |||
86 | clkdiv = __raw_readl(S3C2410_CLKDIVN); | ||
87 | camdiv = __raw_readl(S3C2440_CAMDIVN); | ||
88 | |||
89 | /* work out clock scalings */ | ||
90 | |||
91 | switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) { | ||
92 | case S3C2440_CLKDIVN_HDIVN_1: | ||
93 | hdiv = 1; | ||
94 | break; | ||
95 | |||
96 | case S3C2440_CLKDIVN_HDIVN_2: | ||
97 | hdiv = 2; | ||
98 | break; | ||
99 | |||
100 | case S3C2440_CLKDIVN_HDIVN_4_8: | ||
101 | hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4; | ||
102 | break; | ||
103 | |||
104 | case S3C2440_CLKDIVN_HDIVN_3_6: | ||
105 | hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3; | ||
106 | break; | ||
107 | } | ||
108 | |||
109 | hclk = fclk / hdiv; | ||
110 | pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN)? 2:1); | ||
111 | |||
112 | /* print brief summary of clocks, etc */ | ||
113 | |||
114 | printk("S3C244X: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", | ||
115 | print_mhz(fclk), print_mhz(hclk), print_mhz(pclk)); | ||
116 | |||
117 | /* initialise the clocks here, to allow other things like the | ||
118 | * console to use them, and to add new ones after the initialisation | ||
119 | */ | ||
120 | |||
121 | s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); | ||
122 | s3c2410_baseclk_add(); | ||
123 | } | ||
124 | |||
125 | #ifdef CONFIG_PM | ||
126 | |||
127 | static struct sleep_save s3c244x_sleep[] = { | ||
128 | SAVE_ITEM(S3C2440_DSC0), | ||
129 | SAVE_ITEM(S3C2440_DSC1), | ||
130 | SAVE_ITEM(S3C2440_GPJDAT), | ||
131 | SAVE_ITEM(S3C2440_GPJCON), | ||
132 | SAVE_ITEM(S3C2440_GPJUP) | ||
133 | }; | ||
134 | |||
135 | static int s3c244x_suspend(struct sys_device *dev, pm_message_t state) | ||
136 | { | ||
137 | s3c2410_pm_do_save(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep)); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static int s3c244x_resume(struct sys_device *dev) | ||
142 | { | ||
143 | s3c2410_pm_do_restore(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep)); | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | #else | ||
148 | #define s3c244x_suspend NULL | ||
149 | #define s3c244x_resume NULL | ||
150 | #endif | ||
151 | |||
152 | /* Since the S3C2442 and S3C2440 share items, put both sysclasses here */ | ||
153 | |||
154 | struct sysdev_class s3c2440_sysclass = { | ||
155 | set_kset_name("s3c2440-core"), | ||
156 | .suspend = s3c244x_suspend, | ||
157 | .resume = s3c244x_resume | ||
158 | }; | ||
159 | |||
160 | struct sysdev_class s3c2442_sysclass = { | ||
161 | set_kset_name("s3c2442-core"), | ||
162 | .suspend = s3c244x_suspend, | ||
163 | .resume = s3c244x_resume | ||
164 | }; | ||
165 | |||
166 | /* need to register class before we actually register the device, and | ||
167 | * we also need to ensure that it has been initialised before any of the | ||
168 | * drivers even try to use it (even if not on an s3c2440 based system) | ||
169 | * as a driver which may support both 2410 and 2440 may try and use it. | ||
170 | */ | ||
171 | |||
172 | static int __init s3c2440_core_init(void) | ||
173 | { | ||
174 | return sysdev_class_register(&s3c2440_sysclass); | ||
175 | } | ||
176 | |||
177 | core_initcall(s3c2440_core_init); | ||
178 | |||
179 | static int __init s3c2442_core_init(void) | ||
180 | { | ||
181 | return sysdev_class_register(&s3c2442_sysclass); | ||
182 | } | ||
183 | |||
184 | core_initcall(s3c2442_core_init); | ||
diff --git a/arch/arm/plat-s3c24xx/s3c244x.h b/arch/arm/plat-s3c24xx/s3c244x.h new file mode 100644 index 000000000000..f8ed17676a35 --- /dev/null +++ b/arch/arm/plat-s3c24xx/s3c244x.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/s3c244x.h | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Header file for S3C2440 and S3C2442 cpu support | ||
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 | |||
13 | #if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2442) | ||
14 | |||
15 | extern void s3c244x_map_io(struct map_desc *mach_desc, int size); | ||
16 | |||
17 | extern void s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no); | ||
18 | |||
19 | extern void s3c244x_init_clocks(int xtal); | ||
20 | |||
21 | #else | ||
22 | #define s3c244x_init_clocks NULL | ||
23 | #define s3c244x_init_uarts NULL | ||
24 | #define s3c244x_map_io NULL | ||
25 | #endif | ||
diff --git a/arch/arm/plat-s3c24xx/sleep.S b/arch/arm/plat-s3c24xx/sleep.S new file mode 100644 index 000000000000..2018c2e1dcc5 --- /dev/null +++ b/arch/arm/plat-s3c24xx/sleep.S | |||
@@ -0,0 +1,159 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/sleep.S | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C2410 Power Manager (Suspend-To-RAM) support | ||
7 | * | ||
8 | * Based on PXA/SA1100 sleep code by: | ||
9 | * Nicolas Pitre, (c) 2002 Monta Vista Software Inc | ||
10 | * Cliff Brake, (c) 2001 | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/linkage.h> | ||
28 | #include <asm/assembler.h> | ||
29 | #include <asm/hardware.h> | ||
30 | #include <asm/arch/map.h> | ||
31 | |||
32 | #include <asm/arch/regs-gpio.h> | ||
33 | #include <asm/arch/regs-clock.h> | ||
34 | #include <asm/arch/regs-mem.h> | ||
35 | #include <asm/arch/regs-serial.h> | ||
36 | |||
37 | /* CONFIG_DEBUG_RESUME is dangerous if your bootloader does not | ||
38 | * reset the UART configuration, only enable if you really need this! | ||
39 | */ | ||
40 | //#define CONFIG_DEBUG_RESUME | ||
41 | |||
42 | .text | ||
43 | |||
44 | /* s3c2410_cpu_save | ||
45 | * | ||
46 | * save enough of the CPU state to allow us to re-start | ||
47 | * pm.c code. as we store items like the sp/lr, we will | ||
48 | * end up returning from this function when the cpu resumes | ||
49 | * so the return value is set to mark this. | ||
50 | * | ||
51 | * This arangement means we avoid having to flush the cache | ||
52 | * from this code. | ||
53 | * | ||
54 | * entry: | ||
55 | * r0 = pointer to save block | ||
56 | * | ||
57 | * exit: | ||
58 | * r0 = 0 => we stored everything | ||
59 | * 1 => resumed from sleep | ||
60 | */ | ||
61 | |||
62 | ENTRY(s3c2410_cpu_save) | ||
63 | stmfd sp!, { r4 - r12, lr } | ||
64 | |||
65 | @@ store co-processor registers | ||
66 | |||
67 | mrc p15, 0, r4, c15, c1, 0 @ CP access register | ||
68 | mrc p15, 0, r5, c13, c0, 0 @ PID | ||
69 | mrc p15, 0, r6, c3, c0, 0 @ Domain ID | ||
70 | mrc p15, 0, r7, c2, c0, 0 @ translation table base address | ||
71 | mrc p15, 0, r8, c1, c0, 0 @ control register | ||
72 | |||
73 | stmia r0, { r4 - r13 } | ||
74 | |||
75 | mov r0, #0 | ||
76 | ldmfd sp, { r4 - r12, pc } | ||
77 | |||
78 | @@ return to the caller, after having the MMU | ||
79 | @@ turned on, this restores the last bits from the | ||
80 | @@ stack | ||
81 | resume_with_mmu: | ||
82 | mov r0, #1 | ||
83 | ldmfd sp!, { r4 - r12, pc } | ||
84 | |||
85 | .ltorg | ||
86 | |||
87 | @@ the next bits sit in the .data segment, even though they | ||
88 | @@ happen to be code... the s3c2410_sleep_save_phys needs to be | ||
89 | @@ accessed by the resume code before it can restore the MMU. | ||
90 | @@ This means that the variable has to be close enough for the | ||
91 | @@ code to read it... since the .text segment needs to be RO, | ||
92 | @@ the data segment can be the only place to put this code. | ||
93 | |||
94 | .data | ||
95 | |||
96 | .global s3c2410_sleep_save_phys | ||
97 | s3c2410_sleep_save_phys: | ||
98 | .word 0 | ||
99 | |||
100 | /* s3c2410_cpu_resume | ||
101 | * | ||
102 | * resume code entry for bootloader to call | ||
103 | * | ||
104 | * we must put this code here in the data segment as we have no | ||
105 | * other way of restoring the stack pointer after sleep, and we | ||
106 | * must not write to the code segment (code is read-only) | ||
107 | */ | ||
108 | |||
109 | ENTRY(s3c2410_cpu_resume) | ||
110 | mov r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE | ||
111 | msr cpsr_c, r0 | ||
112 | |||
113 | @@ load UART to allow us to print the two characters for | ||
114 | @@ resume debug | ||
115 | |||
116 | mov r2, #S3C24XX_PA_UART & 0xff000000 | ||
117 | orr r2, r2, #S3C24XX_PA_UART & 0xff000 | ||
118 | |||
119 | #if 0 | ||
120 | /* SMDK2440 LED set */ | ||
121 | mov r14, #S3C24XX_PA_GPIO | ||
122 | ldr r12, [ r14, #0x54 ] | ||
123 | bic r12, r12, #3<<4 | ||
124 | orr r12, r12, #1<<7 | ||
125 | str r12, [ r14, #0x54 ] | ||
126 | #endif | ||
127 | |||
128 | #ifdef CONFIG_DEBUG_RESUME | ||
129 | mov r3, #'L' | ||
130 | strb r3, [ r2, #S3C2410_UTXH ] | ||
131 | 1001: | ||
132 | ldrb r14, [ r3, #S3C2410_UTRSTAT ] | ||
133 | tst r14, #S3C2410_UTRSTAT_TXE | ||
134 | beq 1001b | ||
135 | #endif /* CONFIG_DEBUG_RESUME */ | ||
136 | |||
137 | mov r1, #0 | ||
138 | mcr p15, 0, r1, c8, c7, 0 @@ invalidate I & D TLBs | ||
139 | mcr p15, 0, r1, c7, c7, 0 @@ invalidate I & D caches | ||
140 | |||
141 | ldr r0, s3c2410_sleep_save_phys @ address of restore block | ||
142 | ldmia r0, { r4 - r13 } | ||
143 | |||
144 | mcr p15, 0, r4, c15, c1, 0 @ CP access register | ||
145 | mcr p15, 0, r5, c13, c0, 0 @ PID | ||
146 | mcr p15, 0, r6, c3, c0, 0 @ Domain ID | ||
147 | mcr p15, 0, r7, c2, c0, 0 @ translation table base | ||
148 | |||
149 | #ifdef CONFIG_DEBUG_RESUME | ||
150 | mov r3, #'R' | ||
151 | strb r3, [ r2, #S3C2410_UTXH ] | ||
152 | #endif | ||
153 | |||
154 | ldr r2, =resume_with_mmu | ||
155 | mcr p15, 0, r8, c1, c0, 0 @ turn on MMU, etc | ||
156 | nop @ second-to-last before mmu | ||
157 | mov pc, r2 @ go back to virtual address | ||
158 | |||
159 | .ltorg | ||
diff --git a/arch/arm/plat-s3c24xx/time.c b/arch/arm/plat-s3c24xx/time.c new file mode 100644 index 000000000000..c523d1c9cce5 --- /dev/null +++ b/arch/arm/plat-s3c24xx/time.c | |||
@@ -0,0 +1,262 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/time.c | ||
2 | * | ||
3 | * Copyright (C) 2003-2005 Simtec Electronics | ||
4 | * Ben Dooks, <ben@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 as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/err.h> | ||
27 | #include <linux/clk.h> | ||
28 | |||
29 | #include <asm/system.h> | ||
30 | #include <asm/leds.h> | ||
31 | #include <asm/mach-types.h> | ||
32 | |||
33 | #include <asm/io.h> | ||
34 | #include <asm/irq.h> | ||
35 | #include <asm/arch/map.h> | ||
36 | #include <asm/arch/regs-timer.h> | ||
37 | #include <asm/arch/regs-irq.h> | ||
38 | #include <asm/mach/time.h> | ||
39 | |||
40 | #include <asm/plat-s3c24xx/clock.h> | ||
41 | #include <asm/plat-s3c24xx/cpu.h> | ||
42 | |||
43 | static unsigned long timer_startval; | ||
44 | static unsigned long timer_usec_ticks; | ||
45 | |||
46 | #define TIMER_USEC_SHIFT 16 | ||
47 | |||
48 | /* we use the shifted arithmetic to work out the ratio of timer ticks | ||
49 | * to usecs, as often the peripheral clock is not a nice even multiple | ||
50 | * of 1MHz. | ||
51 | * | ||
52 | * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok | ||
53 | * for the current HZ value of 200 without producing overflows. | ||
54 | * | ||
55 | * Original patch by Dimitry Andric, updated by Ben Dooks | ||
56 | */ | ||
57 | |||
58 | |||
59 | /* timer_mask_usec_ticks | ||
60 | * | ||
61 | * given a clock and divisor, make the value to pass into timer_ticks_to_usec | ||
62 | * to scale the ticks into usecs | ||
63 | */ | ||
64 | |||
65 | static inline unsigned long | ||
66 | timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk) | ||
67 | { | ||
68 | unsigned long den = pclk / 1000; | ||
69 | |||
70 | return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; | ||
71 | } | ||
72 | |||
73 | /* timer_ticks_to_usec | ||
74 | * | ||
75 | * convert timer ticks to usec. | ||
76 | */ | ||
77 | |||
78 | static inline unsigned long timer_ticks_to_usec(unsigned long ticks) | ||
79 | { | ||
80 | unsigned long res; | ||
81 | |||
82 | res = ticks * timer_usec_ticks; | ||
83 | res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */ | ||
84 | |||
85 | return res >> TIMER_USEC_SHIFT; | ||
86 | } | ||
87 | |||
88 | /*** | ||
89 | * Returns microsecond since last clock interrupt. Note that interrupts | ||
90 | * will have been disabled by do_gettimeoffset() | ||
91 | * IRQs are disabled before entering here from do_gettimeofday() | ||
92 | */ | ||
93 | |||
94 | #define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0)) | ||
95 | |||
96 | static unsigned long s3c2410_gettimeoffset (void) | ||
97 | { | ||
98 | unsigned long tdone; | ||
99 | unsigned long irqpend; | ||
100 | unsigned long tval; | ||
101 | |||
102 | /* work out how many ticks have gone since last timer interrupt */ | ||
103 | |||
104 | tval = __raw_readl(S3C2410_TCNTO(4)); | ||
105 | tdone = timer_startval - tval; | ||
106 | |||
107 | /* check to see if there is an interrupt pending */ | ||
108 | |||
109 | irqpend = __raw_readl(S3C2410_SRCPND); | ||
110 | if (irqpend & SRCPND_TIMER4) { | ||
111 | /* re-read the timer, and try and fix up for the missed | ||
112 | * interrupt. Note, the interrupt may go off before the | ||
113 | * timer has re-loaded from wrapping. | ||
114 | */ | ||
115 | |||
116 | tval = __raw_readl(S3C2410_TCNTO(4)); | ||
117 | tdone = timer_startval - tval; | ||
118 | |||
119 | if (tval != 0) | ||
120 | tdone += timer_startval; | ||
121 | } | ||
122 | |||
123 | return timer_ticks_to_usec(tdone); | ||
124 | } | ||
125 | |||
126 | |||
127 | /* | ||
128 | * IRQ handler for the timer | ||
129 | */ | ||
130 | static irqreturn_t | ||
131 | s3c2410_timer_interrupt(int irq, void *dev_id) | ||
132 | { | ||
133 | write_seqlock(&xtime_lock); | ||
134 | timer_tick(); | ||
135 | write_sequnlock(&xtime_lock); | ||
136 | return IRQ_HANDLED; | ||
137 | } | ||
138 | |||
139 | static struct irqaction s3c2410_timer_irq = { | ||
140 | .name = "S3C2410 Timer Tick", | ||
141 | .flags = IRQF_DISABLED | IRQF_TIMER, | ||
142 | .handler = s3c2410_timer_interrupt, | ||
143 | }; | ||
144 | |||
145 | #define use_tclk1_12() ( \ | ||
146 | machine_is_bast() || \ | ||
147 | machine_is_vr1000() || \ | ||
148 | machine_is_anubis() || \ | ||
149 | machine_is_osiris() ) | ||
150 | |||
151 | /* | ||
152 | * Set up timer interrupt, and return the current time in seconds. | ||
153 | * | ||
154 | * Currently we only use timer4, as it is the only timer which has no | ||
155 | * other function that can be exploited externally | ||
156 | */ | ||
157 | static void s3c2410_timer_setup (void) | ||
158 | { | ||
159 | unsigned long tcon; | ||
160 | unsigned long tcnt; | ||
161 | unsigned long tcfg1; | ||
162 | unsigned long tcfg0; | ||
163 | |||
164 | tcnt = 0xffff; /* default value for tcnt */ | ||
165 | |||
166 | /* read the current timer configuration bits */ | ||
167 | |||
168 | tcon = __raw_readl(S3C2410_TCON); | ||
169 | tcfg1 = __raw_readl(S3C2410_TCFG1); | ||
170 | tcfg0 = __raw_readl(S3C2410_TCFG0); | ||
171 | |||
172 | /* configure the system for whichever machine is in use */ | ||
173 | |||
174 | if (use_tclk1_12()) { | ||
175 | /* timer is at 12MHz, scaler is 1 */ | ||
176 | timer_usec_ticks = timer_mask_usec_ticks(1, 12000000); | ||
177 | tcnt = 12000000 / HZ; | ||
178 | |||
179 | tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; | ||
180 | tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1; | ||
181 | } else { | ||
182 | unsigned long pclk; | ||
183 | struct clk *clk; | ||
184 | |||
185 | /* for the h1940 (and others), we use the pclk from the core | ||
186 | * to generate the timer values. since values around 50 to | ||
187 | * 70MHz are not values we can directly generate the timer | ||
188 | * value from, we need to pre-scale and divide before using it. | ||
189 | * | ||
190 | * for instance, using 50.7MHz and dividing by 6 gives 8.45MHz | ||
191 | * (8.45 ticks per usec) | ||
192 | */ | ||
193 | |||
194 | /* this is used as default if no other timer can be found */ | ||
195 | |||
196 | clk = clk_get(NULL, "timers"); | ||
197 | if (IS_ERR(clk)) | ||
198 | panic("failed to get clock for system timer"); | ||
199 | |||
200 | clk_enable(clk); | ||
201 | |||
202 | pclk = clk_get_rate(clk); | ||
203 | |||
204 | /* configure clock tick */ | ||
205 | |||
206 | timer_usec_ticks = timer_mask_usec_ticks(6, pclk); | ||
207 | |||
208 | tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; | ||
209 | tcfg1 |= S3C2410_TCFG1_MUX4_DIV2; | ||
210 | |||
211 | tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; | ||
212 | tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT; | ||
213 | |||
214 | tcnt = (pclk / 6) / HZ; | ||
215 | } | ||
216 | |||
217 | /* timers reload after counting zero, so reduce the count by 1 */ | ||
218 | |||
219 | tcnt--; | ||
220 | |||
221 | printk("timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx, usec %08lx\n", | ||
222 | tcon, tcnt, tcfg0, tcfg1, timer_usec_ticks); | ||
223 | |||
224 | /* check to see if timer is within 16bit range... */ | ||
225 | if (tcnt > 0xffff) { | ||
226 | panic("setup_timer: HZ is too small, cannot configure timer!"); | ||
227 | return; | ||
228 | } | ||
229 | |||
230 | __raw_writel(tcfg1, S3C2410_TCFG1); | ||
231 | __raw_writel(tcfg0, S3C2410_TCFG0); | ||
232 | |||
233 | timer_startval = tcnt; | ||
234 | __raw_writel(tcnt, S3C2410_TCNTB(4)); | ||
235 | |||
236 | /* ensure timer is stopped... */ | ||
237 | |||
238 | tcon &= ~(7<<20); | ||
239 | tcon |= S3C2410_TCON_T4RELOAD; | ||
240 | tcon |= S3C2410_TCON_T4MANUALUPD; | ||
241 | |||
242 | __raw_writel(tcon, S3C2410_TCON); | ||
243 | __raw_writel(tcnt, S3C2410_TCNTB(4)); | ||
244 | __raw_writel(tcnt, S3C2410_TCMPB(4)); | ||
245 | |||
246 | /* start the timer running */ | ||
247 | tcon |= S3C2410_TCON_T4START; | ||
248 | tcon &= ~S3C2410_TCON_T4MANUALUPD; | ||
249 | __raw_writel(tcon, S3C2410_TCON); | ||
250 | } | ||
251 | |||
252 | static void __init s3c2410_timer_init (void) | ||
253 | { | ||
254 | s3c2410_timer_setup(); | ||
255 | setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); | ||
256 | } | ||
257 | |||
258 | struct sys_timer s3c24xx_timer = { | ||
259 | .init = s3c2410_timer_init, | ||
260 | .offset = s3c2410_gettimeoffset, | ||
261 | .resume = s3c2410_timer_setup | ||
262 | }; | ||