aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-s3c64xx
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2010-01-25 23:41:30 -0500
committerBen Dooks <ben-linux@fluff.org>2010-02-20 17:33:54 -0500
commitf7be9abaa5f4a64fdcca6808bb7eacb3547e574e (patch)
tree0c14f12dcf4828bcdb8096faf0a577b3cc0c942e /arch/arm/mach-s3c64xx
parent88fc68a280709f3fb9488986ab39eac330d17b6d (diff)
ARM: S3C64XX: Move core support to mach-s3c64xx
Move the core S3C64XX support to mach-s3c64xx as it is unlikely to be used outside of this directory. Also move the SoC header files in with it. This includes the clock, cpu, cpufreq, dma, gpiolib and pll support. Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/mach-s3c64xx')
-rw-r--r--arch/arm/mach-s3c64xx/Kconfig4
-rw-r--r--arch/arm/mach-s3c64xx/Makefile15
-rw-r--r--arch/arm/mach-s3c64xx/clock.c306
-rw-r--r--arch/arm/mach-s3c64xx/cpu.c161
-rw-r--r--arch/arm/mach-s3c64xx/cpufreq.c270
-rw-r--r--arch/arm/mach-s3c64xx/dma.c750
-rw-r--r--arch/arm/mach-s3c64xx/gpiolib.c288
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/pll.h74
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/s3c6400.h35
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/s3c6410.h29
-rw-r--r--arch/arm/mach-s3c64xx/mach-anw6410.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-hmt.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-ncp.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-smdk6400.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-smdk6410.c2
-rw-r--r--arch/arm/mach-s3c64xx/pm.c173
-rw-r--r--arch/arm/mach-s3c64xx/s3c6400.c2
-rw-r--r--arch/arm/mach-s3c64xx/s3c6410.c4
-rw-r--r--arch/arm/mach-s3c64xx/sleep.S144
19 files changed, 2257 insertions, 8 deletions
diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig
index 15e065ef19a5..7c9cd9a9901a 100644
--- a/arch/arm/mach-s3c64xx/Kconfig
+++ b/arch/arm/mach-s3c64xx/Kconfig
@@ -19,6 +19,10 @@ config CPU_S3C6410
19 help 19 help
20 Enable S3C6410 CPU support 20 Enable S3C6410 CPU support
21 21
22config S3C64XX_DMA
23 bool "S3C64XX DMA"
24 select S3C_DMA
25
22config S3C64XX_SETUP_SDHCI 26config S3C64XX_SETUP_SDHCI
23 select S3C64XX_SETUP_SDHCI_GPIO 27 select S3C64XX_SETUP_SDHCI_GPIO
24 bool 28 bool
diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile
index 49b71d5f2e59..4417f1ad99b6 100644
--- a/arch/arm/mach-s3c64xx/Makefile
+++ b/arch/arm/mach-s3c64xx/Makefile
@@ -10,6 +10,11 @@ obj-m :=
10obj-n := 10obj-n :=
11obj- := 11obj- :=
12 12
13# Core files
14obj-y += cpu.o
15obj-y += clock.o
16obj-y += gpiolib.o
17
13# Core support for S3C6400 system 18# Core support for S3C6400 system
14 19
15obj-$(CONFIG_CPU_S3C6400) += s3c6400.o 20obj-$(CONFIG_CPU_S3C6400) += s3c6400.o
@@ -18,6 +23,14 @@ obj-$(CONFIG_CPU_S3C6410) += s3c6410.o
18obj-y += irq.o 23obj-y += irq.o
19obj-y += irq-eint.o 24obj-y += irq-eint.o
20 25
26# CPU frequency scaling
27
28obj-$(CONFIG_CPU_FREQ_S3C64XX) += cpufreq.o
29
30# DMA support
31
32obj-$(CONFIG_S3C64XX_DMA) += dma.o
33
21# Device setup 34# Device setup
22 35
23obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o 36obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o
@@ -28,6 +41,8 @@ obj-$(CONFIG_S3C64XX_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o
28 41
29# PM 42# PM
30 43
44obj-$(CONFIG_PM) += pm.o
45obj-$(CONFIG_PM) += sleep.o
31obj-$(CONFIG_PM) += irq-pm.o 46obj-$(CONFIG_PM) += irq-pm.o
32 47
33# Machine support 48# Machine support
diff --git a/arch/arm/mach-s3c64xx/clock.c b/arch/arm/mach-s3c64xx/clock.c
new file mode 100644
index 000000000000..229bb3bcc54f
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/clock.c
@@ -0,0 +1,306 @@
1/* linux/arch/arm/plat-s3c64xx/clock.c
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C64XX Base clock support
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/init.h>
16#include <linux/module.h>
17#include <linux/interrupt.h>
18#include <linux/ioport.h>
19#include <linux/io.h>
20
21#include <mach/hardware.h>
22#include <mach/map.h>
23
24#include <mach/regs-sys.h>
25#include <mach/regs-clock.h>
26#include <mach/pll.h>
27
28#include <plat/cpu.h>
29#include <plat/devs.h>
30#include <plat/clock.h>
31
32struct clk clk_h2 = {
33 .name = "hclk2",
34 .id = -1,
35 .rate = 0,
36};
37
38struct clk clk_27m = {
39 .name = "clk_27m",
40 .id = -1,
41 .rate = 27000000,
42};
43
44static int clk_48m_ctrl(struct clk *clk, int enable)
45{
46 unsigned long flags;
47 u32 val;
48
49 /* can't rely on clock lock, this register has other usages */
50 local_irq_save(flags);
51
52 val = __raw_readl(S3C64XX_OTHERS);
53 if (enable)
54 val |= S3C64XX_OTHERS_USBMASK;
55 else
56 val &= ~S3C64XX_OTHERS_USBMASK;
57
58 __raw_writel(val, S3C64XX_OTHERS);
59 local_irq_restore(flags);
60
61 return 0;
62}
63
64struct clk clk_48m = {
65 .name = "clk_48m",
66 .id = -1,
67 .rate = 48000000,
68 .enable = clk_48m_ctrl,
69};
70
71static int inline s3c64xx_gate(void __iomem *reg,
72 struct clk *clk,
73 int enable)
74{
75 unsigned int ctrlbit = clk->ctrlbit;
76 u32 con;
77
78 con = __raw_readl(reg);
79
80 if (enable)
81 con |= ctrlbit;
82 else
83 con &= ~ctrlbit;
84
85 __raw_writel(con, reg);
86 return 0;
87}
88
89static int s3c64xx_pclk_ctrl(struct clk *clk, int enable)
90{
91 return s3c64xx_gate(S3C_PCLK_GATE, clk, enable);
92}
93
94static int s3c64xx_hclk_ctrl(struct clk *clk, int enable)
95{
96 return s3c64xx_gate(S3C_HCLK_GATE, clk, enable);
97}
98
99int s3c64xx_sclk_ctrl(struct clk *clk, int enable)
100{
101 return s3c64xx_gate(S3C_SCLK_GATE, clk, enable);
102}
103
104static struct clk init_clocks_disable[] = {
105 {
106 .name = "nand",
107 .id = -1,
108 .parent = &clk_h,
109 }, {
110 .name = "adc",
111 .id = -1,
112 .parent = &clk_p,
113 .enable = s3c64xx_pclk_ctrl,
114 .ctrlbit = S3C_CLKCON_PCLK_TSADC,
115 }, {
116 .name = "i2c",
117 .id = -1,
118 .parent = &clk_p,
119 .enable = s3c64xx_pclk_ctrl,
120 .ctrlbit = S3C_CLKCON_PCLK_IIC,
121 }, {
122 .name = "iis",
123 .id = 0,
124 .parent = &clk_p,
125 .enable = s3c64xx_pclk_ctrl,
126 .ctrlbit = S3C_CLKCON_PCLK_IIS0,
127 }, {
128 .name = "iis",
129 .id = 1,
130 .parent = &clk_p,
131 .enable = s3c64xx_pclk_ctrl,
132 .ctrlbit = S3C_CLKCON_PCLK_IIS1,
133 }, {
134 .name = "spi",
135 .id = 0,
136 .parent = &clk_p,
137 .enable = s3c64xx_pclk_ctrl,
138 .ctrlbit = S3C_CLKCON_PCLK_SPI0,
139 }, {
140 .name = "spi",
141 .id = 1,
142 .parent = &clk_p,
143 .enable = s3c64xx_pclk_ctrl,
144 .ctrlbit = S3C_CLKCON_PCLK_SPI1,
145 }, {
146 .name = "spi_48m",
147 .id = 0,
148 .parent = &clk_48m,
149 .enable = s3c64xx_sclk_ctrl,
150 .ctrlbit = S3C_CLKCON_SCLK_SPI0_48,
151 }, {
152 .name = "spi_48m",
153 .id = 1,
154 .parent = &clk_48m,
155 .enable = s3c64xx_sclk_ctrl,
156 .ctrlbit = S3C_CLKCON_SCLK_SPI1_48,
157 }, {
158 .name = "48m",
159 .id = 0,
160 .parent = &clk_48m,
161 .enable = s3c64xx_sclk_ctrl,
162 .ctrlbit = S3C_CLKCON_SCLK_MMC0_48,
163 }, {
164 .name = "48m",
165 .id = 1,
166 .parent = &clk_48m,
167 .enable = s3c64xx_sclk_ctrl,
168 .ctrlbit = S3C_CLKCON_SCLK_MMC1_48,
169 }, {
170 .name = "48m",
171 .id = 2,
172 .parent = &clk_48m,
173 .enable = s3c64xx_sclk_ctrl,
174 .ctrlbit = S3C_CLKCON_SCLK_MMC2_48,
175 }, {
176 .name = "dma0",
177 .id = -1,
178 .parent = &clk_h,
179 .enable = s3c64xx_hclk_ctrl,
180 .ctrlbit = S3C_CLKCON_HCLK_DMA0,
181 }, {
182 .name = "dma1",
183 .id = -1,
184 .parent = &clk_h,
185 .enable = s3c64xx_hclk_ctrl,
186 .ctrlbit = S3C_CLKCON_HCLK_DMA1,
187 },
188};
189
190static struct clk init_clocks[] = {
191 {
192 .name = "lcd",
193 .id = -1,
194 .parent = &clk_h,
195 .enable = s3c64xx_hclk_ctrl,
196 .ctrlbit = S3C_CLKCON_HCLK_LCD,
197 }, {
198 .name = "gpio",
199 .id = -1,
200 .parent = &clk_p,
201 .enable = s3c64xx_pclk_ctrl,
202 .ctrlbit = S3C_CLKCON_PCLK_GPIO,
203 }, {
204 .name = "usb-host",
205 .id = -1,
206 .parent = &clk_h,
207 .enable = s3c64xx_hclk_ctrl,
208 .ctrlbit = S3C_CLKCON_HCLK_UHOST,
209 }, {
210 .name = "hsmmc",
211 .id = 0,
212 .parent = &clk_h,
213 .enable = s3c64xx_hclk_ctrl,
214 .ctrlbit = S3C_CLKCON_HCLK_HSMMC0,
215 }, {
216 .name = "hsmmc",
217 .id = 1,
218 .parent = &clk_h,
219 .enable = s3c64xx_hclk_ctrl,
220 .ctrlbit = S3C_CLKCON_HCLK_HSMMC1,
221 }, {
222 .name = "hsmmc",
223 .id = 2,
224 .parent = &clk_h,
225 .enable = s3c64xx_hclk_ctrl,
226 .ctrlbit = S3C_CLKCON_HCLK_HSMMC2,
227 }, {
228 .name = "timers",
229 .id = -1,
230 .parent = &clk_p,
231 .enable = s3c64xx_pclk_ctrl,
232 .ctrlbit = S3C_CLKCON_PCLK_PWM,
233 }, {
234 .name = "uart",
235 .id = 0,
236 .parent = &clk_p,
237 .enable = s3c64xx_pclk_ctrl,
238 .ctrlbit = S3C_CLKCON_PCLK_UART0,
239 }, {
240 .name = "uart",
241 .id = 1,
242 .parent = &clk_p,
243 .enable = s3c64xx_pclk_ctrl,
244 .ctrlbit = S3C_CLKCON_PCLK_UART1,
245 }, {
246 .name = "uart",
247 .id = 2,
248 .parent = &clk_p,
249 .enable = s3c64xx_pclk_ctrl,
250 .ctrlbit = S3C_CLKCON_PCLK_UART2,
251 }, {
252 .name = "uart",
253 .id = 3,
254 .parent = &clk_p,
255 .enable = s3c64xx_pclk_ctrl,
256 .ctrlbit = S3C_CLKCON_PCLK_UART3,
257 }, {
258 .name = "rtc",
259 .id = -1,
260 .parent = &clk_p,
261 .enable = s3c64xx_pclk_ctrl,
262 .ctrlbit = S3C_CLKCON_PCLK_RTC,
263 }, {
264 .name = "watchdog",
265 .id = -1,
266 .parent = &clk_p,
267 .ctrlbit = S3C_CLKCON_PCLK_WDT,
268 }, {
269 .name = "ac97",
270 .id = -1,
271 .parent = &clk_p,
272 .ctrlbit = S3C_CLKCON_PCLK_AC97,
273 }
274};
275
276static struct clk *clks[] __initdata = {
277 &clk_ext,
278 &clk_epll,
279 &clk_27m,
280 &clk_48m,
281 &clk_h2,
282};
283
284void __init s3c64xx_register_clocks(void)
285{
286 struct clk *clkp;
287 int ret;
288 int ptr;
289
290 s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
291 s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
292
293 clkp = init_clocks_disable;
294 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
295
296 ret = s3c24xx_register_clock(clkp);
297 if (ret < 0) {
298 printk(KERN_ERR "Failed to register clock %s (%d)\n",
299 clkp->name, ret);
300 }
301
302 (clkp->enable)(clkp, 0);
303 }
304
305 s3c_pwmclk_init();
306}
diff --git a/arch/arm/mach-s3c64xx/cpu.c b/arch/arm/mach-s3c64xx/cpu.c
new file mode 100644
index 000000000000..410d688a6910
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/cpu.c
@@ -0,0 +1,161 @@
1/* linux/arch/arm/plat-s3c64xx/cpu.c
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C64XX CPU Support
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/init.h>
16#include <linux/module.h>
17#include <linux/interrupt.h>
18#include <linux/ioport.h>
19#include <linux/sysdev.h>
20#include <linux/serial_core.h>
21#include <linux/platform_device.h>
22#include <linux/io.h>
23
24#include <mach/hardware.h>
25#include <mach/map.h>
26
27#include <asm/mach/arch.h>
28#include <asm/mach/map.h>
29
30#include <plat/regs-serial.h>
31
32#include <plat/cpu.h>
33#include <plat/devs.h>
34#include <plat/clock.h>
35
36#include <mach/s3c6400.h>
37#include <mach/s3c6410.h>
38
39/* table of supported CPUs */
40
41static const char name_s3c6400[] = "S3C6400";
42static const char name_s3c6410[] = "S3C6410";
43
44static struct cpu_table cpu_ids[] __initdata = {
45 {
46 .idcode = 0x36400000,
47 .idmask = 0xfffff000,
48 .map_io = s3c6400_map_io,
49 .init_clocks = s3c6400_init_clocks,
50 .init_uarts = s3c6400_init_uarts,
51 .init = s3c6400_init,
52 .name = name_s3c6400,
53 }, {
54 .idcode = 0x36410100,
55 .idmask = 0xffffff00,
56 .map_io = s3c6410_map_io,
57 .init_clocks = s3c6410_init_clocks,
58 .init_uarts = s3c6410_init_uarts,
59 .init = s3c6410_init,
60 .name = name_s3c6410,
61 },
62};
63
64/* minimal IO mapping */
65
66/* see notes on uart map in arch/arm/mach-s3c6400/include/mach/debug-macro.S */
67#define UART_OFFS (S3C_PA_UART & 0xfffff)
68
69static struct map_desc s3c_iodesc[] __initdata = {
70 {
71 .virtual = (unsigned long)S3C_VA_SYS,
72 .pfn = __phys_to_pfn(S3C64XX_PA_SYSCON),
73 .length = SZ_4K,
74 .type = MT_DEVICE,
75 }, {
76 .virtual = (unsigned long)S3C_VA_MEM,
77 .pfn = __phys_to_pfn(S3C64XX_PA_SROM),
78 .length = SZ_4K,
79 .type = MT_DEVICE,
80 }, {
81 .virtual = (unsigned long)(S3C_VA_UART + UART_OFFS),
82 .pfn = __phys_to_pfn(S3C_PA_UART),
83 .length = SZ_4K,
84 .type = MT_DEVICE,
85 }, {
86 .virtual = (unsigned long)VA_VIC0,
87 .pfn = __phys_to_pfn(S3C64XX_PA_VIC0),
88 .length = SZ_16K,
89 .type = MT_DEVICE,
90 }, {
91 .virtual = (unsigned long)VA_VIC1,
92 .pfn = __phys_to_pfn(S3C64XX_PA_VIC1),
93 .length = SZ_16K,
94 .type = MT_DEVICE,
95 }, {
96 .virtual = (unsigned long)S3C_VA_TIMER,
97 .pfn = __phys_to_pfn(S3C_PA_TIMER),
98 .length = SZ_16K,
99 .type = MT_DEVICE,
100 }, {
101 .virtual = (unsigned long)S3C64XX_VA_GPIO,
102 .pfn = __phys_to_pfn(S3C64XX_PA_GPIO),
103 .length = SZ_4K,
104 .type = MT_DEVICE,
105 }, {
106 .virtual = (unsigned long)S3C64XX_VA_MODEM,
107 .pfn = __phys_to_pfn(S3C64XX_PA_MODEM),
108 .length = SZ_4K,
109 .type = MT_DEVICE,
110 }, {
111 .virtual = (unsigned long)S3C_VA_WATCHDOG,
112 .pfn = __phys_to_pfn(S3C64XX_PA_WATCHDOG),
113 .length = SZ_4K,
114 .type = MT_DEVICE,
115 }, {
116 .virtual = (unsigned long)S3C_VA_USB_HSPHY,
117 .pfn = __phys_to_pfn(S3C64XX_PA_USB_HSPHY),
118 .length = SZ_1K,
119 .type = MT_DEVICE,
120 },
121};
122
123
124struct sysdev_class s3c64xx_sysclass = {
125 .name = "s3c64xx-core",
126};
127
128static struct sys_device s3c64xx_sysdev = {
129 .cls = &s3c64xx_sysclass,
130};
131
132
133/* read cpu identification code */
134
135void __init s3c64xx_init_io(struct map_desc *mach_desc, int size)
136{
137 unsigned long idcode;
138
139 /* initialise the io descriptors we need for initialisation */
140 iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));
141 iotable_init(mach_desc, size);
142
143 idcode = __raw_readl(S3C_VA_SYS + 0x118);
144 if (!idcode) {
145 /* S3C6400 has the ID register in a different place,
146 * and needs a write before it can be read. */
147
148 __raw_writel(0x0, S3C_VA_SYS + 0xA1C);
149 idcode = __raw_readl(S3C_VA_SYS + 0xA1C);
150 }
151
152 s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
153}
154
155static __init int s3c64xx_sysdev_init(void)
156{
157 sysdev_class_register(&s3c64xx_sysclass);
158 return sysdev_register(&s3c64xx_sysdev);
159}
160
161core_initcall(s3c64xx_sysdev_init);
diff --git a/arch/arm/mach-s3c64xx/cpufreq.c b/arch/arm/mach-s3c64xx/cpufreq.c
new file mode 100644
index 000000000000..74c0e8347de5
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/cpufreq.c
@@ -0,0 +1,270 @@
1/* linux/arch/arm/plat-s3c64xx/cpufreq.c
2 *
3 * Copyright 2009 Wolfson Microelectronics plc
4 *
5 * S3C64xx CPUfreq Support
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/kernel.h>
13#include <linux/types.h>
14#include <linux/init.h>
15#include <linux/cpufreq.h>
16#include <linux/clk.h>
17#include <linux/err.h>
18#include <linux/regulator/consumer.h>
19
20static struct clk *armclk;
21static struct regulator *vddarm;
22static unsigned long regulator_latency;
23
24#ifdef CONFIG_CPU_S3C6410
25struct s3c64xx_dvfs {
26 unsigned int vddarm_min;
27 unsigned int vddarm_max;
28};
29
30static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = {
31 [0] = { 1000000, 1150000 },
32 [1] = { 1050000, 1150000 },
33 [2] = { 1100000, 1150000 },
34 [3] = { 1200000, 1350000 },
35};
36
37static struct cpufreq_frequency_table s3c64xx_freq_table[] = {
38 { 0, 66000 },
39 { 0, 133000 },
40 { 1, 222000 },
41 { 1, 266000 },
42 { 2, 333000 },
43 { 2, 400000 },
44 { 2, 532000 },
45 { 2, 533000 },
46 { 3, 667000 },
47 { 0, CPUFREQ_TABLE_END },
48};
49#endif
50
51static int s3c64xx_cpufreq_verify_speed(struct cpufreq_policy *policy)
52{
53 if (policy->cpu != 0)
54 return -EINVAL;
55
56 return cpufreq_frequency_table_verify(policy, s3c64xx_freq_table);
57}
58
59static unsigned int s3c64xx_cpufreq_get_speed(unsigned int cpu)
60{
61 if (cpu != 0)
62 return 0;
63
64 return clk_get_rate(armclk) / 1000;
65}
66
67static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
68 unsigned int target_freq,
69 unsigned int relation)
70{
71 int ret;
72 unsigned int i;
73 struct cpufreq_freqs freqs;
74 struct s3c64xx_dvfs *dvfs;
75
76 ret = cpufreq_frequency_table_target(policy, s3c64xx_freq_table,
77 target_freq, relation, &i);
78 if (ret != 0)
79 return ret;
80
81 freqs.cpu = 0;
82 freqs.old = clk_get_rate(armclk) / 1000;
83 freqs.new = s3c64xx_freq_table[i].frequency;
84 freqs.flags = 0;
85 dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].index];
86
87 if (freqs.old == freqs.new)
88 return 0;
89
90 pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new);
91
92 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
93
94#ifdef CONFIG_REGULATOR
95 if (vddarm && freqs.new > freqs.old) {
96 ret = regulator_set_voltage(vddarm,
97 dvfs->vddarm_min,
98 dvfs->vddarm_max);
99 if (ret != 0) {
100 pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n",
101 freqs.new, ret);
102 goto err;
103 }
104 }
105#endif
106
107 ret = clk_set_rate(armclk, freqs.new * 1000);
108 if (ret < 0) {
109 pr_err("cpufreq: Failed to set rate %dkHz: %d\n",
110 freqs.new, ret);
111 goto err;
112 }
113
114#ifdef CONFIG_REGULATOR
115 if (vddarm && freqs.new < freqs.old) {
116 ret = regulator_set_voltage(vddarm,
117 dvfs->vddarm_min,
118 dvfs->vddarm_max);
119 if (ret != 0) {
120 pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n",
121 freqs.new, ret);
122 goto err_clk;
123 }
124 }
125#endif
126
127 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
128
129 pr_debug("cpufreq: Set actual frequency %lukHz\n",
130 clk_get_rate(armclk) / 1000);
131
132 return 0;
133
134err_clk:
135 if (clk_set_rate(armclk, freqs.old * 1000) < 0)
136 pr_err("Failed to restore original clock rate\n");
137err:
138 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
139
140 return ret;
141}
142
143#ifdef CONFIG_REGULATOR
144static void __init s3c64xx_cpufreq_config_regulator(void)
145{
146 int count, v, i, found;
147 struct cpufreq_frequency_table *freq;
148 struct s3c64xx_dvfs *dvfs;
149
150 count = regulator_count_voltages(vddarm);
151 if (count < 0) {
152 pr_err("cpufreq: Unable to check supported voltages\n");
153 }
154
155 freq = s3c64xx_freq_table;
156 while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
157 if (freq->frequency == CPUFREQ_ENTRY_INVALID)
158 continue;
159
160 dvfs = &s3c64xx_dvfs_table[freq->index];
161 found = 0;
162
163 for (i = 0; i < count; i++) {
164 v = regulator_list_voltage(vddarm, i);
165 if (v >= dvfs->vddarm_min && v <= dvfs->vddarm_max)
166 found = 1;
167 }
168
169 if (!found) {
170 pr_debug("cpufreq: %dkHz unsupported by regulator\n",
171 freq->frequency);
172 freq->frequency = CPUFREQ_ENTRY_INVALID;
173 }
174
175 freq++;
176 }
177
178 /* Guess based on having to do an I2C/SPI write; in future we
179 * will be able to query the regulator performance here. */
180 regulator_latency = 1 * 1000 * 1000;
181}
182#endif
183
184static int __init s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
185{
186 int ret;
187 struct cpufreq_frequency_table *freq;
188
189 if (policy->cpu != 0)
190 return -EINVAL;
191
192 if (s3c64xx_freq_table == NULL) {
193 pr_err("cpufreq: No frequency information for this CPU\n");
194 return -ENODEV;
195 }
196
197 armclk = clk_get(NULL, "armclk");
198 if (IS_ERR(armclk)) {
199 pr_err("cpufreq: Unable to obtain ARMCLK: %ld\n",
200 PTR_ERR(armclk));
201 return PTR_ERR(armclk);
202 }
203
204#ifdef CONFIG_REGULATOR
205 vddarm = regulator_get(NULL, "vddarm");
206 if (IS_ERR(vddarm)) {
207 ret = PTR_ERR(vddarm);
208 pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
209 pr_err("cpufreq: Only frequency scaling available\n");
210 vddarm = NULL;
211 } else {
212 s3c64xx_cpufreq_config_regulator();
213 }
214#endif
215
216 freq = s3c64xx_freq_table;
217 while (freq->frequency != CPUFREQ_TABLE_END) {
218 unsigned long r;
219
220 /* Check for frequencies we can generate */
221 r = clk_round_rate(armclk, freq->frequency * 1000);
222 r /= 1000;
223 if (r != freq->frequency) {
224 pr_debug("cpufreq: %dkHz unsupported by clock\n",
225 freq->frequency);
226 freq->frequency = CPUFREQ_ENTRY_INVALID;
227 }
228
229 /* If we have no regulator then assume startup
230 * frequency is the maximum we can support. */
231 if (!vddarm && freq->frequency > s3c64xx_cpufreq_get_speed(0))
232 freq->frequency = CPUFREQ_ENTRY_INVALID;
233
234 freq++;
235 }
236
237 policy->cur = clk_get_rate(armclk) / 1000;
238
239 /* Datasheet says PLL stabalisation time (if we were to use
240 * the PLLs, which we don't currently) is ~300us worst case,
241 * but add some fudge.
242 */
243 policy->cpuinfo.transition_latency = (500 * 1000) + regulator_latency;
244
245 ret = cpufreq_frequency_table_cpuinfo(policy, s3c64xx_freq_table);
246 if (ret != 0) {
247 pr_err("cpufreq: Failed to configure frequency table: %d\n",
248 ret);
249 regulator_put(vddarm);
250 clk_put(armclk);
251 }
252
253 return ret;
254}
255
256static struct cpufreq_driver s3c64xx_cpufreq_driver = {
257 .owner = THIS_MODULE,
258 .flags = 0,
259 .verify = s3c64xx_cpufreq_verify_speed,
260 .target = s3c64xx_cpufreq_set_target,
261 .get = s3c64xx_cpufreq_get_speed,
262 .init = s3c64xx_cpufreq_driver_init,
263 .name = "s3c",
264};
265
266static int __init s3c64xx_cpufreq_init(void)
267{
268 return cpufreq_register_driver(&s3c64xx_cpufreq_driver);
269}
270module_init(s3c64xx_cpufreq_init);
diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c
new file mode 100644
index 000000000000..0e0edf75e8ed
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/dma.c
@@ -0,0 +1,750 @@
1/* linux/arch/arm/plat-s3c64xx/dma.c
2 *
3 * Copyright 2009 Openmoko, Inc.
4 * Copyright 2009 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C64XX DMA core
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/module.h>
17#include <linux/interrupt.h>
18#include <linux/dmapool.h>
19#include <linux/sysdev.h>
20#include <linux/errno.h>
21#include <linux/delay.h>
22#include <linux/clk.h>
23#include <linux/err.h>
24#include <linux/io.h>
25
26#include <mach/dma.h>
27#include <mach/map.h>
28#include <mach/irqs.h>
29
30#include <plat/dma-plat.h>
31#include <mach/regs-sys.h>
32
33#include <asm/hardware/pl080.h>
34
35/* dma channel state information */
36
37struct s3c64xx_dmac {
38 struct sys_device sysdev;
39 struct clk *clk;
40 void __iomem *regs;
41 struct s3c2410_dma_chan *channels;
42 enum dma_ch chanbase;
43};
44
45/* pool to provide LLI buffers */
46static struct dma_pool *dma_pool;
47
48/* Debug configuration and code */
49
50static unsigned char debug_show_buffs = 0;
51
52static void dbg_showchan(struct s3c2410_dma_chan *chan)
53{
54 pr_debug("DMA%d: %08x->%08x L %08x C %08x,%08x S %08x\n",
55 chan->number,
56 readl(chan->regs + PL080_CH_SRC_ADDR),
57 readl(chan->regs + PL080_CH_DST_ADDR),
58 readl(chan->regs + PL080_CH_LLI),
59 readl(chan->regs + PL080_CH_CONTROL),
60 readl(chan->regs + PL080S_CH_CONTROL2),
61 readl(chan->regs + PL080S_CH_CONFIG));
62}
63
64static void show_lli(struct pl080s_lli *lli)
65{
66 pr_debug("LLI[%p] %08x->%08x, NL %08x C %08x,%08x\n",
67 lli, lli->src_addr, lli->dst_addr, lli->next_lli,
68 lli->control0, lli->control1);
69}
70
71static void dbg_showbuffs(struct s3c2410_dma_chan *chan)
72{
73 struct s3c64xx_dma_buff *ptr;
74 struct s3c64xx_dma_buff *end;
75
76 pr_debug("DMA%d: buffs next %p, curr %p, end %p\n",
77 chan->number, chan->next, chan->curr, chan->end);
78
79 ptr = chan->next;
80 end = chan->end;
81
82 if (debug_show_buffs) {
83 for (; ptr != NULL; ptr = ptr->next) {
84 pr_debug("DMA%d: %08x ",
85 chan->number, ptr->lli_dma);
86 show_lli(ptr->lli);
87 }
88 }
89}
90
91/* End of Debug */
92
93static struct s3c2410_dma_chan *s3c64xx_dma_map_channel(unsigned int channel)
94{
95 struct s3c2410_dma_chan *chan;
96 unsigned int start, offs;
97
98 start = 0;
99
100 if (channel >= DMACH_PCM1_TX)
101 start = 8;
102
103 for (offs = 0; offs < 8; offs++) {
104 chan = &s3c2410_chans[start + offs];
105 if (!chan->in_use)
106 goto found;
107 }
108
109 return NULL;
110
111found:
112 s3c_dma_chan_map[channel] = chan;
113 return chan;
114}
115
116int s3c2410_dma_config(unsigned int channel, int xferunit)
117{
118 struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
119
120 if (chan == NULL)
121 return -EINVAL;
122
123 switch (xferunit) {
124 case 1:
125 chan->hw_width = 0;
126 break;
127 case 2:
128 chan->hw_width = 1;
129 break;
130 case 4:
131 chan->hw_width = 2;
132 break;
133 default:
134 printk(KERN_ERR "%s: illegal width %d\n", __func__, xferunit);
135 return -EINVAL;
136 }
137
138 return 0;
139}
140EXPORT_SYMBOL(s3c2410_dma_config);
141
142static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan,
143 struct pl080s_lli *lli,
144 dma_addr_t data, int size)
145{
146 dma_addr_t src, dst;
147 u32 control0, control1;
148
149 switch (chan->source) {
150 case S3C2410_DMASRC_HW:
151 src = chan->dev_addr;
152 dst = data;
153 control0 = PL080_CONTROL_SRC_AHB2;
154 control0 |= PL080_CONTROL_DST_INCR;
155 break;
156
157 case S3C2410_DMASRC_MEM:
158 src = data;
159 dst = chan->dev_addr;
160 control0 = PL080_CONTROL_DST_AHB2;
161 control0 |= PL080_CONTROL_SRC_INCR;
162 break;
163 default:
164 BUG();
165 }
166
167 /* note, we do not currently setup any of the burst controls */
168
169 control1 = size >> chan->hw_width; /* size in no of xfers */
170 control0 |= PL080_CONTROL_PROT_SYS; /* always in priv. mode */
171 control0 |= PL080_CONTROL_TC_IRQ_EN; /* always fire IRQ */
172 control0 |= (u32)chan->hw_width << PL080_CONTROL_DWIDTH_SHIFT;
173 control0 |= (u32)chan->hw_width << PL080_CONTROL_SWIDTH_SHIFT;
174
175 lli->src_addr = src;
176 lli->dst_addr = dst;
177 lli->next_lli = 0;
178 lli->control0 = control0;
179 lli->control1 = control1;
180}
181
182static void s3c64xx_lli_to_regs(struct s3c2410_dma_chan *chan,
183 struct pl080s_lli *lli)
184{
185 void __iomem *regs = chan->regs;
186
187 pr_debug("%s: LLI %p => regs\n", __func__, lli);
188 show_lli(lli);
189
190 writel(lli->src_addr, regs + PL080_CH_SRC_ADDR);
191 writel(lli->dst_addr, regs + PL080_CH_DST_ADDR);
192 writel(lli->next_lli, regs + PL080_CH_LLI);
193 writel(lli->control0, regs + PL080_CH_CONTROL);
194 writel(lli->control1, regs + PL080S_CH_CONTROL2);
195}
196
197static int s3c64xx_dma_start(struct s3c2410_dma_chan *chan)
198{
199 struct s3c64xx_dmac *dmac = chan->dmac;
200 u32 config;
201 u32 bit = chan->bit;
202
203 dbg_showchan(chan);
204
205 pr_debug("%s: clearing interrupts\n", __func__);
206
207 /* clear interrupts */
208 writel(bit, dmac->regs + PL080_TC_CLEAR);
209 writel(bit, dmac->regs + PL080_ERR_CLEAR);
210
211 pr_debug("%s: starting channel\n", __func__);
212
213 config = readl(chan->regs + PL080S_CH_CONFIG);
214 config |= PL080_CONFIG_ENABLE;
215
216 pr_debug("%s: writing config %08x\n", __func__, config);
217 writel(config, chan->regs + PL080S_CH_CONFIG);
218
219 return 0;
220}
221
222static int s3c64xx_dma_stop(struct s3c2410_dma_chan *chan)
223{
224 u32 config;
225 int timeout;
226
227 pr_debug("%s: stopping channel\n", __func__);
228
229 dbg_showchan(chan);
230
231 config = readl(chan->regs + PL080S_CH_CONFIG);
232 config |= PL080_CONFIG_HALT;
233 writel(config, chan->regs + PL080S_CH_CONFIG);
234
235 timeout = 1000;
236 do {
237 config = readl(chan->regs + PL080S_CH_CONFIG);
238 pr_debug("%s: %d - config %08x\n", __func__, timeout, config);
239 if (config & PL080_CONFIG_ACTIVE)
240 udelay(10);
241 else
242 break;
243 } while (--timeout > 0);
244
245 if (config & PL080_CONFIG_ACTIVE) {
246 printk(KERN_ERR "%s: channel still active\n", __func__);
247 return -EFAULT;
248 }
249
250 config = readl(chan->regs + PL080S_CH_CONFIG);
251 config &= ~PL080_CONFIG_ENABLE;
252 writel(config, chan->regs + PL080S_CH_CONFIG);
253
254 return 0;
255}
256
257static inline void s3c64xx_dma_bufffdone(struct s3c2410_dma_chan *chan,
258 struct s3c64xx_dma_buff *buf,
259 enum s3c2410_dma_buffresult result)
260{
261 if (chan->callback_fn != NULL)
262 (chan->callback_fn)(chan, buf->pw, 0, result);
263}
264
265static void s3c64xx_dma_freebuff(struct s3c64xx_dma_buff *buff)
266{
267 dma_pool_free(dma_pool, buff->lli, buff->lli_dma);
268 kfree(buff);
269}
270
271static int s3c64xx_dma_flush(struct s3c2410_dma_chan *chan)
272{
273 struct s3c64xx_dma_buff *buff, *next;
274 u32 config;
275
276 dbg_showchan(chan);
277
278 pr_debug("%s: flushing channel\n", __func__);
279
280 config = readl(chan->regs + PL080S_CH_CONFIG);
281 config &= ~PL080_CONFIG_ENABLE;
282 writel(config, chan->regs + PL080S_CH_CONFIG);
283
284 /* dump all the buffers associated with this channel */
285
286 for (buff = chan->curr; buff != NULL; buff = next) {
287 next = buff->next;
288 pr_debug("%s: buff %p (next %p)\n", __func__, buff, buff->next);
289
290 s3c64xx_dma_bufffdone(chan, buff, S3C2410_RES_ABORT);
291 s3c64xx_dma_freebuff(buff);
292 }
293
294 chan->curr = chan->next = chan->end = NULL;
295
296 return 0;
297}
298
299int s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op op)
300{
301 struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
302
303 WARN_ON(!chan);
304 if (!chan)
305 return -EINVAL;
306
307 switch (op) {
308 case S3C2410_DMAOP_START:
309 return s3c64xx_dma_start(chan);
310
311 case S3C2410_DMAOP_STOP:
312 return s3c64xx_dma_stop(chan);
313
314 case S3C2410_DMAOP_FLUSH:
315 return s3c64xx_dma_flush(chan);
316
317 /* belive PAUSE/RESUME are no-ops */
318 case S3C2410_DMAOP_PAUSE:
319 case S3C2410_DMAOP_RESUME:
320 case S3C2410_DMAOP_STARTED:
321 case S3C2410_DMAOP_TIMEOUT:
322 return 0;
323 }
324
325 return -ENOENT;
326}
327EXPORT_SYMBOL(s3c2410_dma_ctrl);
328
329/* s3c2410_dma_enque
330 *
331 */
332
333int s3c2410_dma_enqueue(unsigned int channel, void *id,
334 dma_addr_t data, int size)
335{
336 struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
337 struct s3c64xx_dma_buff *next;
338 struct s3c64xx_dma_buff *buff;
339 struct pl080s_lli *lli;
340 unsigned long flags;
341 int ret;
342
343 WARN_ON(!chan);
344 if (!chan)
345 return -EINVAL;
346
347 buff = kzalloc(sizeof(struct s3c64xx_dma_buff), GFP_ATOMIC);
348 if (!buff) {
349 printk(KERN_ERR "%s: no memory for buffer\n", __func__);
350 return -ENOMEM;
351 }
352
353 lli = dma_pool_alloc(dma_pool, GFP_ATOMIC, &buff->lli_dma);
354 if (!lli) {
355 printk(KERN_ERR "%s: no memory for lli\n", __func__);
356 ret = -ENOMEM;
357 goto err_buff;
358 }
359
360 pr_debug("%s: buff %p, dp %08x lli (%p, %08x) %d\n",
361 __func__, buff, data, lli, (u32)buff->lli_dma, size);
362
363 buff->lli = lli;
364 buff->pw = id;
365
366 s3c64xx_dma_fill_lli(chan, lli, data, size);
367
368 local_irq_save(flags);
369
370 if ((next = chan->next) != NULL) {
371 struct s3c64xx_dma_buff *end = chan->end;
372 struct pl080s_lli *endlli = end->lli;
373
374 pr_debug("enquing onto channel\n");
375
376 end->next = buff;
377 endlli->next_lli = buff->lli_dma;
378
379 if (chan->flags & S3C2410_DMAF_CIRCULAR) {
380 struct s3c64xx_dma_buff *curr = chan->curr;
381 lli->next_lli = curr->lli_dma;
382 }
383
384 if (next == chan->curr) {
385 writel(buff->lli_dma, chan->regs + PL080_CH_LLI);
386 chan->next = buff;
387 }
388
389 show_lli(endlli);
390 chan->end = buff;
391 } else {
392 pr_debug("enquing onto empty channel\n");
393
394 chan->curr = buff;
395 chan->next = buff;
396 chan->end = buff;
397
398 s3c64xx_lli_to_regs(chan, lli);
399 }
400
401 local_irq_restore(flags);
402
403 show_lli(lli);
404
405 dbg_showchan(chan);
406 dbg_showbuffs(chan);
407 return 0;
408
409err_buff:
410 kfree(buff);
411 return ret;
412}
413
414EXPORT_SYMBOL(s3c2410_dma_enqueue);
415
416
417int s3c2410_dma_devconfig(int channel,
418 enum s3c2410_dmasrc source,
419 unsigned long devaddr)
420{
421 struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
422 u32 peripheral;
423 u32 config = 0;
424
425 pr_debug("%s: channel %d, source %d, dev %08lx, chan %p\n",
426 __func__, channel, source, devaddr, chan);
427
428 WARN_ON(!chan);
429 if (!chan)
430 return -EINVAL;
431
432 peripheral = (chan->peripheral & 0xf);
433 chan->source = source;
434 chan->dev_addr = devaddr;
435
436 pr_debug("%s: peripheral %d\n", __func__, peripheral);
437
438 switch (source) {
439 case S3C2410_DMASRC_HW:
440 config = 2 << PL080_CONFIG_FLOW_CONTROL_SHIFT;
441 config |= peripheral << PL080_CONFIG_SRC_SEL_SHIFT;
442 break;
443 case S3C2410_DMASRC_MEM:
444 config = 1 << PL080_CONFIG_FLOW_CONTROL_SHIFT;
445 config |= peripheral << PL080_CONFIG_DST_SEL_SHIFT;
446 break;
447 default:
448 printk(KERN_ERR "%s: bad source\n", __func__);
449 return -EINVAL;
450 }
451
452 /* allow TC and ERR interrupts */
453 config |= PL080_CONFIG_TC_IRQ_MASK;
454 config |= PL080_CONFIG_ERR_IRQ_MASK;
455
456 pr_debug("%s: config %08x\n", __func__, config);
457
458 writel(config, chan->regs + PL080S_CH_CONFIG);
459
460 return 0;
461}
462EXPORT_SYMBOL(s3c2410_dma_devconfig);
463
464
465int s3c2410_dma_getposition(unsigned int channel,
466 dma_addr_t *src, dma_addr_t *dst)
467{
468 struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
469
470 WARN_ON(!chan);
471 if (!chan)
472 return -EINVAL;
473
474 if (src != NULL)
475 *src = readl(chan->regs + PL080_CH_SRC_ADDR);
476
477 if (dst != NULL)
478 *dst = readl(chan->regs + PL080_CH_DST_ADDR);
479
480 return 0;
481}
482EXPORT_SYMBOL(s3c2410_dma_getposition);
483
484/* s3c2410_request_dma
485 *
486 * get control of an dma channel
487*/
488
489int s3c2410_dma_request(unsigned int channel,
490 struct s3c2410_dma_client *client,
491 void *dev)
492{
493 struct s3c2410_dma_chan *chan;
494 unsigned long flags;
495
496 pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n",
497 channel, client->name, dev);
498
499 local_irq_save(flags);
500
501 chan = s3c64xx_dma_map_channel(channel);
502 if (chan == NULL) {
503 local_irq_restore(flags);
504 return -EBUSY;
505 }
506
507 dbg_showchan(chan);
508
509 chan->client = client;
510 chan->in_use = 1;
511 chan->peripheral = channel;
512
513 local_irq_restore(flags);
514
515 /* need to setup */
516
517 pr_debug("%s: channel initialised, %p\n", __func__, chan);
518
519 return chan->number | DMACH_LOW_LEVEL;
520}
521
522EXPORT_SYMBOL(s3c2410_dma_request);
523
524/* s3c2410_dma_free
525 *
526 * release the given channel back to the system, will stop and flush
527 * any outstanding transfers, and ensure the channel is ready for the
528 * next claimant.
529 *
530 * Note, although a warning is currently printed if the freeing client
531 * info is not the same as the registrant's client info, the free is still
532 * allowed to go through.
533*/
534
535int s3c2410_dma_free(unsigned int channel, struct s3c2410_dma_client *client)
536{
537 struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
538 unsigned long flags;
539
540 if (chan == NULL)
541 return -EINVAL;
542
543 local_irq_save(flags);
544
545 if (chan->client != client) {
546 printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n",
547 channel, chan->client, client);
548 }
549
550 /* sort out stopping and freeing the channel */
551
552
553 chan->client = NULL;
554 chan->in_use = 0;
555
556 if (!(channel & DMACH_LOW_LEVEL))
557 s3c_dma_chan_map[channel] = NULL;
558
559 local_irq_restore(flags);
560
561 return 0;
562}
563
564EXPORT_SYMBOL(s3c2410_dma_free);
565
566static irqreturn_t s3c64xx_dma_irq(int irq, void *pw)
567{
568 struct s3c64xx_dmac *dmac = pw;
569 struct s3c2410_dma_chan *chan;
570 enum s3c2410_dma_buffresult res;
571 u32 tcstat, errstat;
572 u32 bit;
573 int offs;
574
575 tcstat = readl(dmac->regs + PL080_TC_STATUS);
576 errstat = readl(dmac->regs + PL080_ERR_STATUS);
577
578 for (offs = 0, bit = 1; offs < 8; offs++, bit <<= 1) {
579 struct s3c64xx_dma_buff *buff;
580
581 if (!(errstat & bit) && !(tcstat & bit))
582 continue;
583
584 chan = dmac->channels + offs;
585 res = S3C2410_RES_ERR;
586
587 if (tcstat & bit) {
588 writel(bit, dmac->regs + PL080_TC_CLEAR);
589 res = S3C2410_RES_OK;
590 }
591
592 if (errstat & bit)
593 writel(bit, dmac->regs + PL080_ERR_CLEAR);
594
595 /* 'next' points to the buffer that is next to the
596 * currently active buffer.
597 * For CIRCULAR queues, 'next' will be same as 'curr'
598 * when 'end' is the active buffer.
599 */
600 buff = chan->curr;
601 while (buff && buff != chan->next
602 && buff->next != chan->next)
603 buff = buff->next;
604
605 if (!buff)
606 BUG();
607
608 if (buff == chan->next)
609 buff = chan->end;
610
611 s3c64xx_dma_bufffdone(chan, buff, res);
612
613 /* Free the node and update curr, if non-circular queue */
614 if (!(chan->flags & S3C2410_DMAF_CIRCULAR)) {
615 chan->curr = buff->next;
616 s3c64xx_dma_freebuff(buff);
617 }
618
619 /* Update 'next' */
620 buff = chan->next;
621 if (chan->next == chan->end) {
622 chan->next = chan->curr;
623 if (!(chan->flags & S3C2410_DMAF_CIRCULAR))
624 chan->end = NULL;
625 } else {
626 chan->next = buff->next;
627 }
628 }
629
630 return IRQ_HANDLED;
631}
632
633static struct sysdev_class dma_sysclass = {
634 .name = "s3c64xx-dma",
635};
636
637static int s3c64xx_dma_init1(int chno, enum dma_ch chbase,
638 int irq, unsigned int base)
639{
640 struct s3c2410_dma_chan *chptr = &s3c2410_chans[chno];
641 struct s3c64xx_dmac *dmac;
642 char clkname[16];
643 void __iomem *regs;
644 void __iomem *regptr;
645 int err, ch;
646
647 dmac = kzalloc(sizeof(struct s3c64xx_dmac), GFP_KERNEL);
648 if (!dmac) {
649 printk(KERN_ERR "%s: failed to alloc mem\n", __func__);
650 return -ENOMEM;
651 }
652
653 dmac->sysdev.id = chno / 8;
654 dmac->sysdev.cls = &dma_sysclass;
655
656 err = sysdev_register(&dmac->sysdev);
657 if (err) {
658 printk(KERN_ERR "%s: failed to register sysdevice\n", __func__);
659 goto err_alloc;
660 }
661
662 regs = ioremap(base, 0x200);
663 if (!regs) {
664 printk(KERN_ERR "%s: failed to ioremap()\n", __func__);
665 err = -ENXIO;
666 goto err_dev;
667 }
668
669 snprintf(clkname, sizeof(clkname), "dma%d", dmac->sysdev.id);
670
671 dmac->clk = clk_get(NULL, clkname);
672 if (IS_ERR(dmac->clk)) {
673 printk(KERN_ERR "%s: failed to get clock %s\n", __func__, clkname);
674 err = PTR_ERR(dmac->clk);
675 goto err_map;
676 }
677
678 clk_enable(dmac->clk);
679
680 dmac->regs = regs;
681 dmac->chanbase = chbase;
682 dmac->channels = chptr;
683
684 err = request_irq(irq, s3c64xx_dma_irq, 0, "DMA", dmac);
685 if (err < 0) {
686 printk(KERN_ERR "%s: failed to get irq\n", __func__);
687 goto err_clk;
688 }
689
690 regptr = regs + PL080_Cx_BASE(0);
691
692 for (ch = 0; ch < 8; ch++, chno++, chptr++) {
693 printk(KERN_INFO "%s: registering DMA %d (%p)\n",
694 __func__, chno, regptr);
695
696 chptr->bit = 1 << ch;
697 chptr->number = chno;
698 chptr->dmac = dmac;
699 chptr->regs = regptr;
700 regptr += PL008_Cx_STRIDE;
701 }
702
703 /* for the moment, permanently enable the controller */
704 writel(PL080_CONFIG_ENABLE, regs + PL080_CONFIG);
705
706 printk(KERN_INFO "PL080: IRQ %d, at %p\n", irq, regs);
707
708 return 0;
709
710err_clk:
711 clk_disable(dmac->clk);
712 clk_put(dmac->clk);
713err_map:
714 iounmap(regs);
715err_dev:
716 sysdev_unregister(&dmac->sysdev);
717err_alloc:
718 kfree(dmac);
719 return err;
720}
721
722static int __init s3c64xx_dma_init(void)
723{
724 int ret;
725
726 printk(KERN_INFO "%s: Registering DMA channels\n", __func__);
727
728 dma_pool = dma_pool_create("DMA-LLI", NULL, sizeof(struct pl080s_lli), 16, 0);
729 if (!dma_pool) {
730 printk(KERN_ERR "%s: failed to create pool\n", __func__);
731 return -ENOMEM;
732 }
733
734 ret = sysdev_class_register(&dma_sysclass);
735 if (ret) {
736 printk(KERN_ERR "%s: failed to create sysclass\n", __func__);
737 return -ENOMEM;
738 }
739
740 /* Set all DMA configuration to be DMA, not SDMA */
741 writel(0xffffff, S3C_SYSREG(0x110));
742
743 /* Register standard DMA controlers */
744 s3c64xx_dma_init1(0, DMACH_UART0, IRQ_DMA0, 0x75000000);
745 s3c64xx_dma_init1(8, DMACH_PCM1_TX, IRQ_DMA1, 0x75100000);
746
747 return 0;
748}
749
750arch_initcall(s3c64xx_dma_init);
diff --git a/arch/arm/mach-s3c64xx/gpiolib.c b/arch/arm/mach-s3c64xx/gpiolib.c
new file mode 100644
index 000000000000..66e6794481d2
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/gpiolib.c
@@ -0,0 +1,288 @@
1/* arch/arm/plat-s3c64xx/gpiolib.c
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C64XX - GPIOlib support
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/irq.h>
17#include <linux/io.h>
18
19#include <mach/map.h>
20#include <mach/gpio.h>
21
22#include <plat/gpio-core.h>
23#include <plat/gpio-cfg.h>
24#include <plat/gpio-cfg-helpers.h>
25#include <mach/regs-gpio.h>
26
27/* GPIO bank summary:
28 *
29 * Bank GPIOs Style SlpCon ExtInt Group
30 * A 8 4Bit Yes 1
31 * B 7 4Bit Yes 1
32 * C 8 4Bit Yes 2
33 * D 5 4Bit Yes 3
34 * E 5 4Bit Yes None
35 * F 16 2Bit Yes 4 [1]
36 * G 7 4Bit Yes 5
37 * H 10 4Bit[2] Yes 6
38 * I 16 2Bit Yes None
39 * J 12 2Bit Yes None
40 * K 16 4Bit[2] No None
41 * L 15 4Bit[2] No None
42 * M 6 4Bit No IRQ_EINT
43 * N 16 2Bit No IRQ_EINT
44 * O 16 2Bit Yes 7
45 * P 15 2Bit Yes 8
46 * Q 9 2Bit Yes 9
47 *
48 * [1] BANKF pins 14,15 do not form part of the external interrupt sources
49 * [2] BANK has two control registers, GPxCON0 and GPxCON1
50 */
51
52static struct s3c_gpio_cfg gpio_4bit_cfg_noint = {
53 .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
54 .set_pull = s3c_gpio_setpull_updown,
55 .get_pull = s3c_gpio_getpull_updown,
56};
57
58static struct s3c_gpio_cfg gpio_4bit_cfg_eint0111 = {
59 .cfg_eint = 7,
60 .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
61 .set_pull = s3c_gpio_setpull_updown,
62 .get_pull = s3c_gpio_getpull_updown,
63};
64
65static struct s3c_gpio_cfg gpio_4bit_cfg_eint0011 = {
66 .cfg_eint = 3,
67 .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
68 .set_pull = s3c_gpio_setpull_updown,
69 .get_pull = s3c_gpio_getpull_updown,
70};
71
72int s3c64xx_gpio2int_gpm(struct gpio_chip *chip, unsigned pin)
73{
74 return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO;
75}
76
77static struct s3c_gpio_chip gpio_4bit[] = {
78 {
79 .base = S3C64XX_GPA_BASE,
80 .config = &gpio_4bit_cfg_eint0111,
81 .chip = {
82 .base = S3C64XX_GPA(0),
83 .ngpio = S3C64XX_GPIO_A_NR,
84 .label = "GPA",
85 },
86 }, {
87 .base = S3C64XX_GPB_BASE,
88 .config = &gpio_4bit_cfg_eint0111,
89 .chip = {
90 .base = S3C64XX_GPB(0),
91 .ngpio = S3C64XX_GPIO_B_NR,
92 .label = "GPB",
93 },
94 }, {
95 .base = S3C64XX_GPC_BASE,
96 .config = &gpio_4bit_cfg_eint0111,
97 .chip = {
98 .base = S3C64XX_GPC(0),
99 .ngpio = S3C64XX_GPIO_C_NR,
100 .label = "GPC",
101 },
102 }, {
103 .base = S3C64XX_GPD_BASE,
104 .config = &gpio_4bit_cfg_eint0111,
105 .chip = {
106 .base = S3C64XX_GPD(0),
107 .ngpio = S3C64XX_GPIO_D_NR,
108 .label = "GPD",
109 },
110 }, {
111 .base = S3C64XX_GPE_BASE,
112 .config = &gpio_4bit_cfg_noint,
113 .chip = {
114 .base = S3C64XX_GPE(0),
115 .ngpio = S3C64XX_GPIO_E_NR,
116 .label = "GPE",
117 },
118 }, {
119 .base = S3C64XX_GPG_BASE,
120 .config = &gpio_4bit_cfg_eint0111,
121 .chip = {
122 .base = S3C64XX_GPG(0),
123 .ngpio = S3C64XX_GPIO_G_NR,
124 .label = "GPG",
125 },
126 }, {
127 .base = S3C64XX_GPM_BASE,
128 .config = &gpio_4bit_cfg_eint0011,
129 .chip = {
130 .base = S3C64XX_GPM(0),
131 .ngpio = S3C64XX_GPIO_M_NR,
132 .label = "GPM",
133 .to_irq = s3c64xx_gpio2int_gpm,
134 },
135 },
136};
137
138int s3c64xx_gpio2int_gpl(struct gpio_chip *chip, unsigned pin)
139{
140 return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO;
141}
142
143static struct s3c_gpio_chip gpio_4bit2[] = {
144 {
145 .base = S3C64XX_GPH_BASE + 0x4,
146 .config = &gpio_4bit_cfg_eint0111,
147 .chip = {
148 .base = S3C64XX_GPH(0),
149 .ngpio = S3C64XX_GPIO_H_NR,
150 .label = "GPH",
151 },
152 }, {
153 .base = S3C64XX_GPK_BASE + 0x4,
154 .config = &gpio_4bit_cfg_noint,
155 .chip = {
156 .base = S3C64XX_GPK(0),
157 .ngpio = S3C64XX_GPIO_K_NR,
158 .label = "GPK",
159 },
160 }, {
161 .base = S3C64XX_GPL_BASE + 0x4,
162 .config = &gpio_4bit_cfg_eint0011,
163 .chip = {
164 .base = S3C64XX_GPL(0),
165 .ngpio = S3C64XX_GPIO_L_NR,
166 .label = "GPL",
167 .to_irq = s3c64xx_gpio2int_gpl,
168 },
169 },
170};
171
172static struct s3c_gpio_cfg gpio_2bit_cfg_noint = {
173 .set_config = s3c_gpio_setcfg_s3c24xx,
174 .set_pull = s3c_gpio_setpull_updown,
175 .get_pull = s3c_gpio_getpull_updown,
176};
177
178static struct s3c_gpio_cfg gpio_2bit_cfg_eint10 = {
179 .cfg_eint = 2,
180 .set_config = s3c_gpio_setcfg_s3c24xx,
181 .set_pull = s3c_gpio_setpull_updown,
182 .get_pull = s3c_gpio_getpull_updown,
183};
184
185static struct s3c_gpio_cfg gpio_2bit_cfg_eint11 = {
186 .cfg_eint = 3,
187 .set_config = s3c_gpio_setcfg_s3c24xx,
188 .set_pull = s3c_gpio_setpull_updown,
189 .get_pull = s3c_gpio_getpull_updown,
190};
191
192int s3c64xx_gpio2int_gpn(struct gpio_chip *chip, unsigned pin)
193{
194 return IRQ_EINT(0) + pin;
195}
196
197static struct s3c_gpio_chip gpio_2bit[] = {
198 {
199 .base = S3C64XX_GPF_BASE,
200 .config = &gpio_2bit_cfg_eint11,
201 .chip = {
202 .base = S3C64XX_GPF(0),
203 .ngpio = S3C64XX_GPIO_F_NR,
204 .label = "GPF",
205 },
206 }, {
207 .base = S3C64XX_GPI_BASE,
208 .config = &gpio_2bit_cfg_noint,
209 .chip = {
210 .base = S3C64XX_GPI(0),
211 .ngpio = S3C64XX_GPIO_I_NR,
212 .label = "GPI",
213 },
214 }, {
215 .base = S3C64XX_GPJ_BASE,
216 .config = &gpio_2bit_cfg_noint,
217 .chip = {
218 .base = S3C64XX_GPJ(0),
219 .ngpio = S3C64XX_GPIO_J_NR,
220 .label = "GPJ",
221 },
222 }, {
223 .base = S3C64XX_GPN_BASE,
224 .config = &gpio_2bit_cfg_eint10,
225 .chip = {
226 .base = S3C64XX_GPN(0),
227 .ngpio = S3C64XX_GPIO_N_NR,
228 .label = "GPN",
229 .to_irq = s3c64xx_gpio2int_gpn,
230 },
231 }, {
232 .base = S3C64XX_GPO_BASE,
233 .config = &gpio_2bit_cfg_eint11,
234 .chip = {
235 .base = S3C64XX_GPO(0),
236 .ngpio = S3C64XX_GPIO_O_NR,
237 .label = "GPO",
238 },
239 }, {
240 .base = S3C64XX_GPP_BASE,
241 .config = &gpio_2bit_cfg_eint11,
242 .chip = {
243 .base = S3C64XX_GPP(0),
244 .ngpio = S3C64XX_GPIO_P_NR,
245 .label = "GPP",
246 },
247 }, {
248 .base = S3C64XX_GPQ_BASE,
249 .config = &gpio_2bit_cfg_eint11,
250 .chip = {
251 .base = S3C64XX_GPQ(0),
252 .ngpio = S3C64XX_GPIO_Q_NR,
253 .label = "GPQ",
254 },
255 },
256};
257
258static __init void s3c64xx_gpiolib_add_2bit(struct s3c_gpio_chip *chip)
259{
260 chip->pm = __gpio_pm(&s3c_gpio_pm_2bit);
261}
262
263static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips,
264 int nr_chips,
265 void (*fn)(struct s3c_gpio_chip *))
266{
267 for (; nr_chips > 0; nr_chips--, chips++) {
268 if (fn)
269 (fn)(chips);
270 s3c_gpiolib_add(chips);
271 }
272}
273
274static __init int s3c64xx_gpiolib_init(void)
275{
276 s3c64xx_gpiolib_add(gpio_4bit, ARRAY_SIZE(gpio_4bit),
277 samsung_gpiolib_add_4bit);
278
279 s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2),
280 samsung_gpiolib_add_4bit2);
281
282 s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit),
283 s3c64xx_gpiolib_add_2bit);
284
285 return 0;
286}
287
288core_initcall(s3c64xx_gpiolib_init);
diff --git a/arch/arm/mach-s3c64xx/include/mach/pll.h b/arch/arm/mach-s3c64xx/include/mach/pll.h
new file mode 100644
index 000000000000..90bbd72fdc4e
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/include/mach/pll.h
@@ -0,0 +1,74 @@
1/* arch/arm/plat-s3c64xx/include/plat/pll.h
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C64XX PLL code
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#define S3C6400_PLL_MDIV_MASK ((1 << (25-16+1)) - 1)
16#define S3C6400_PLL_PDIV_MASK ((1 << (13-8+1)) - 1)
17#define S3C6400_PLL_SDIV_MASK ((1 << (2-0+1)) - 1)
18#define S3C6400_PLL_MDIV_SHIFT (16)
19#define S3C6400_PLL_PDIV_SHIFT (8)
20#define S3C6400_PLL_SDIV_SHIFT (0)
21
22#include <asm/div64.h>
23
24static inline unsigned long s3c6400_get_pll(unsigned long baseclk,
25 u32 pllcon)
26{
27 u32 mdiv, pdiv, sdiv;
28 u64 fvco = baseclk;
29
30 mdiv = (pllcon >> S3C6400_PLL_MDIV_SHIFT) & S3C6400_PLL_MDIV_MASK;
31 pdiv = (pllcon >> S3C6400_PLL_PDIV_SHIFT) & S3C6400_PLL_PDIV_MASK;
32 sdiv = (pllcon >> S3C6400_PLL_SDIV_SHIFT) & S3C6400_PLL_SDIV_MASK;
33
34 fvco *= mdiv;
35 do_div(fvco, (pdiv << sdiv));
36
37 return (unsigned long)fvco;
38}
39
40#define S3C6400_EPLL_MDIV_MASK ((1 << (23-16)) - 1)
41#define S3C6400_EPLL_PDIV_MASK ((1 << (13-8)) - 1)
42#define S3C6400_EPLL_SDIV_MASK ((1 << (2-0)) - 1)
43#define S3C6400_EPLL_MDIV_SHIFT (16)
44#define S3C6400_EPLL_PDIV_SHIFT (8)
45#define S3C6400_EPLL_SDIV_SHIFT (0)
46#define S3C6400_EPLL_KDIV_MASK (0xffff)
47
48static inline unsigned long s3c6400_get_epll(unsigned long baseclk)
49{
50 unsigned long result;
51 u32 epll0 = __raw_readl(S3C_EPLL_CON0);
52 u32 epll1 = __raw_readl(S3C_EPLL_CON1);
53 u32 mdiv, pdiv, sdiv, kdiv;
54 u64 tmp;
55
56 mdiv = (epll0 >> S3C6400_EPLL_MDIV_SHIFT) & S3C6400_EPLL_MDIV_MASK;
57 pdiv = (epll0 >> S3C6400_EPLL_PDIV_SHIFT) & S3C6400_EPLL_PDIV_MASK;
58 sdiv = (epll0 >> S3C6400_EPLL_SDIV_SHIFT) & S3C6400_EPLL_SDIV_MASK;
59 kdiv = epll1 & S3C6400_EPLL_KDIV_MASK;
60
61 /* We need to multiple baseclk by mdiv (the integer part) and kdiv
62 * which is in 2^16ths, so shift mdiv up (does not overflow) and
63 * add kdiv before multiplying. The use of tmp is to avoid any
64 * overflows before shifting bac down into result when multipling
65 * by the mdiv and kdiv pair.
66 */
67
68 tmp = baseclk;
69 tmp *= (mdiv << 16) + kdiv;
70 do_div(tmp, (pdiv << sdiv));
71 result = tmp >> 16;
72
73 return result;
74}
diff --git a/arch/arm/mach-s3c64xx/include/mach/s3c6400.h b/arch/arm/mach-s3c64xx/include/mach/s3c6400.h
new file mode 100644
index 000000000000..2bc7c07a928f
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/include/mach/s3c6400.h
@@ -0,0 +1,35 @@
1/* arch/arm/mach-s3c64xx/include/macht/s3c6400.h
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * Header file for s3c6400 cpu support
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/* Common init code for S3C6400 related SoCs */
16
17extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no);
18extern void s3c6400_register_clocks(unsigned armclk_divlimit);
19extern void s3c6400_setup_clocks(void);
20
21#ifdef CONFIG_CPU_S3C6400
22
23extern int s3c6400_init(void);
24extern void s3c6400_init_irq(void);
25extern void s3c6400_map_io(void);
26extern void s3c6400_init_clocks(int xtal);
27
28#define s3c6400_init_uarts s3c6400_common_init_uarts
29
30#else
31#define s3c6400_init_clocks NULL
32#define s3c6400_init_uarts NULL
33#define s3c6400_map_io NULL
34#define s3c6400_init NULL
35#endif
diff --git a/arch/arm/mach-s3c64xx/include/mach/s3c6410.h b/arch/arm/mach-s3c64xx/include/mach/s3c6410.h
new file mode 100644
index 000000000000..24f1141ffcb7
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/include/mach/s3c6410.h
@@ -0,0 +1,29 @@
1/* arch/arm/mach-s3c64xx/include/mach/s3c6410.h
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * Header file for s3c6410 cpu support
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#ifdef CONFIG_CPU_S3C6410
16
17extern int s3c6410_init(void);
18extern void s3c6410_init_irq(void);
19extern void s3c6410_map_io(void);
20extern void s3c6410_init_clocks(int xtal);
21
22#define s3c6410_init_uarts s3c6400_common_init_uarts
23
24#else
25#define s3c6410_init_clocks NULL
26#define s3c6410_init_uarts NULL
27#define s3c6410_map_io NULL
28#define s3c6410_init NULL
29#endif
diff --git a/arch/arm/mach-s3c64xx/mach-anw6410.c b/arch/arm/mach-s3c64xx/mach-anw6410.c
index 06d8fe579e10..4a0bb243d14a 100644
--- a/arch/arm/mach-s3c64xx/mach-anw6410.c
+++ b/arch/arm/mach-s3c64xx/mach-anw6410.c
@@ -45,7 +45,7 @@
45#include <plat/iic.h> 45#include <plat/iic.h>
46#include <plat/fb.h> 46#include <plat/fb.h>
47 47
48#include <plat/s3c6410.h> 48#include <mach/s3c6410.h>
49#include <plat/clock.h> 49#include <plat/clock.h>
50#include <plat/devs.h> 50#include <plat/devs.h>
51#include <plat/cpu.h> 51#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c64xx/mach-hmt.c b/arch/arm/mach-s3c64xx/mach-hmt.c
index 284886c26a28..a6d91c39f22e 100644
--- a/arch/arm/mach-s3c64xx/mach-hmt.c
+++ b/arch/arm/mach-s3c64xx/mach-hmt.c
@@ -38,7 +38,7 @@
38#include <plat/fb.h> 38#include <plat/fb.h>
39#include <plat/nand.h> 39#include <plat/nand.h>
40 40
41#include <plat/s3c6410.h> 41#include <mach/s3c6410.h>
42#include <plat/clock.h> 42#include <plat/clock.h>
43#include <plat/devs.h> 43#include <plat/devs.h>
44#include <plat/cpu.h> 44#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c64xx/mach-ncp.c b/arch/arm/mach-s3c64xx/mach-ncp.c
index 9be92ddd2176..bf65747ea68e 100644
--- a/arch/arm/mach-s3c64xx/mach-ncp.c
+++ b/arch/arm/mach-s3c64xx/mach-ncp.c
@@ -40,7 +40,7 @@
40#include <plat/iic.h> 40#include <plat/iic.h>
41#include <plat/fb.h> 41#include <plat/fb.h>
42 42
43#include <plat/s3c6410.h> 43#include <mach/s3c6410.h>
44#include <plat/clock.h> 44#include <plat/clock.h>
45#include <plat/devs.h> 45#include <plat/devs.h>
46#include <plat/cpu.h> 46#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c64xx/mach-smdk6400.c b/arch/arm/mach-s3c64xx/mach-smdk6400.c
index ba8a052a6142..f7b18983950c 100644
--- a/arch/arm/mach-s3c64xx/mach-smdk6400.c
+++ b/arch/arm/mach-s3c64xx/mach-smdk6400.c
@@ -31,7 +31,7 @@
31 31
32#include <plat/regs-serial.h> 32#include <plat/regs-serial.h>
33 33
34#include <plat/s3c6400.h> 34#include <mach/s3c6400.h>
35#include <plat/clock.h> 35#include <plat/clock.h>
36#include <plat/devs.h> 36#include <plat/devs.h>
37#include <plat/cpu.h> 37#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c64xx/mach-smdk6410.c b/arch/arm/mach-s3c64xx/mach-smdk6410.c
index 021670e39d3e..fdf8f7539a12 100644
--- a/arch/arm/mach-s3c64xx/mach-smdk6410.c
+++ b/arch/arm/mach-s3c64xx/mach-smdk6410.c
@@ -54,7 +54,7 @@
54#include <plat/fb.h> 54#include <plat/fb.h>
55#include <plat/gpio-cfg.h> 55#include <plat/gpio-cfg.h>
56 56
57#include <plat/s3c6410.h> 57#include <mach/s3c6410.h>
58#include <plat/clock.h> 58#include <plat/clock.h>
59#include <plat/devs.h> 59#include <plat/devs.h>
60#include <plat/cpu.h> 60#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
new file mode 100644
index 000000000000..b8ac4597fad7
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/pm.c
@@ -0,0 +1,173 @@
1/* linux/arch/arm/plat-s3c64xx/pm.c
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C64XX CPU PM support.
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/init.h>
16#include <linux/suspend.h>
17#include <linux/serial_core.h>
18#include <linux/io.h>
19
20#include <mach/map.h>
21
22#include <plat/pm.h>
23#include <mach/regs-sys.h>
24#include <mach/regs-gpio.h>
25#include <mach/regs-clock.h>
26#include <mach/regs-syscon-power.h>
27#include <mach/regs-gpio-memport.h>
28
29#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
30#include <mach/gpio-bank-n.h>
31
32void s3c_pm_debug_smdkled(u32 set, u32 clear)
33{
34 unsigned long flags;
35 u32 reg;
36
37 local_irq_save(flags);
38 reg = __raw_readl(S3C64XX_GPNCON);
39 reg &= ~(S3C64XX_GPN_CONMASK(12) | S3C64XX_GPN_CONMASK(13) |
40 S3C64XX_GPN_CONMASK(14) | S3C64XX_GPN_CONMASK(15));
41 reg |= S3C64XX_GPN_OUTPUT(12) | S3C64XX_GPN_OUTPUT(13) |
42 S3C64XX_GPN_OUTPUT(14) | S3C64XX_GPN_OUTPUT(15);
43 __raw_writel(reg, S3C64XX_GPNCON);
44
45 reg = __raw_readl(S3C64XX_GPNDAT);
46 reg &= ~(clear << 12);
47 reg |= set << 12;
48 __raw_writel(reg, S3C64XX_GPNDAT);
49
50 local_irq_restore(flags);
51}
52#endif
53
54static struct sleep_save core_save[] = {
55 SAVE_ITEM(S3C_APLL_LOCK),
56 SAVE_ITEM(S3C_MPLL_LOCK),
57 SAVE_ITEM(S3C_EPLL_LOCK),
58 SAVE_ITEM(S3C_CLK_SRC),
59 SAVE_ITEM(S3C_CLK_DIV0),
60 SAVE_ITEM(S3C_CLK_DIV1),
61 SAVE_ITEM(S3C_CLK_DIV2),
62 SAVE_ITEM(S3C_CLK_OUT),
63 SAVE_ITEM(S3C_HCLK_GATE),
64 SAVE_ITEM(S3C_PCLK_GATE),
65 SAVE_ITEM(S3C_SCLK_GATE),
66 SAVE_ITEM(S3C_MEM0_GATE),
67
68 SAVE_ITEM(S3C_EPLL_CON1),
69 SAVE_ITEM(S3C_EPLL_CON0),
70
71 SAVE_ITEM(S3C64XX_MEM0DRVCON),
72 SAVE_ITEM(S3C64XX_MEM1DRVCON),
73
74#ifndef CONFIG_CPU_FREQ
75 SAVE_ITEM(S3C_APLL_CON),
76 SAVE_ITEM(S3C_MPLL_CON),
77#endif
78};
79
80static struct sleep_save misc_save[] = {
81 SAVE_ITEM(S3C64XX_AHB_CON0),
82 SAVE_ITEM(S3C64XX_AHB_CON1),
83 SAVE_ITEM(S3C64XX_AHB_CON2),
84
85 SAVE_ITEM(S3C64XX_SPCON),
86
87 SAVE_ITEM(S3C64XX_MEM0CONSTOP),
88 SAVE_ITEM(S3C64XX_MEM1CONSTOP),
89 SAVE_ITEM(S3C64XX_MEM0CONSLP0),
90 SAVE_ITEM(S3C64XX_MEM0CONSLP1),
91 SAVE_ITEM(S3C64XX_MEM1CONSLP),
92};
93
94void s3c_pm_configure_extint(void)
95{
96 __raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK);
97}
98
99void s3c_pm_restore_core(void)
100{
101 __raw_writel(0, S3C64XX_EINT_MASK);
102
103 s3c_pm_debug_smdkled(1 << 2, 0);
104
105 s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
106 s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
107}
108
109void s3c_pm_save_core(void)
110{
111 s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
112 s3c_pm_do_save(core_save, ARRAY_SIZE(core_save));
113}
114
115/* since both s3c6400 and s3c6410 share the same sleep pm calls, we
116 * put the per-cpu code in here until any new cpu comes along and changes
117 * this.
118 */
119
120static void s3c64xx_cpu_suspend(void)
121{
122 unsigned long tmp;
123
124 /* set our standby method to sleep */
125
126 tmp = __raw_readl(S3C64XX_PWR_CFG);
127 tmp &= ~S3C64XX_PWRCFG_CFG_WFI_MASK;
128 tmp |= S3C64XX_PWRCFG_CFG_WFI_SLEEP;
129 __raw_writel(tmp, S3C64XX_PWR_CFG);
130
131 /* clear any old wakeup */
132
133 __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT),
134 S3C64XX_WAKEUP_STAT);
135
136 /* set the LED state to 0110 over sleep */
137 s3c_pm_debug_smdkled(3 << 1, 0xf);
138
139 /* issue the standby signal into the pm unit. Note, we
140 * issue a write-buffer drain just in case */
141
142 tmp = 0;
143
144 asm("b 1f\n\t"
145 ".align 5\n\t"
146 "1:\n\t"
147 "mcr p15, 0, %0, c7, c10, 5\n\t"
148 "mcr p15, 0, %0, c7, c10, 4\n\t"
149 "mcr p15, 0, %0, c7, c0, 4" :: "r" (tmp));
150
151 /* we should never get past here */
152
153 panic("sleep resumed to originator?");
154}
155
156static void s3c64xx_pm_prepare(void)
157{
158 /* store address of resume. */
159 __raw_writel(virt_to_phys(s3c_cpu_resume), S3C64XX_INFORM0);
160
161 /* ensure previous wakeup state is cleared before sleeping */
162 __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), S3C64XX_WAKEUP_STAT);
163}
164
165static int s3c64xx_pm_init(void)
166{
167 pm_cpu_prep = s3c64xx_pm_prepare;
168 pm_cpu_sleep = s3c64xx_cpu_suspend;
169 pm_uart_udivslot = 1;
170 return 0;
171}
172
173arch_initcall(s3c64xx_pm_init);
diff --git a/arch/arm/mach-s3c64xx/s3c6400.c b/arch/arm/mach-s3c64xx/s3c6400.c
index 2fba1b263fed..720d0d1f3bfc 100644
--- a/arch/arm/mach-s3c64xx/s3c6400.c
+++ b/arch/arm/mach-s3c64xx/s3c6400.c
@@ -37,7 +37,7 @@
37#include <plat/clock.h> 37#include <plat/clock.h>
38#include <plat/sdhci.h> 38#include <plat/sdhci.h>
39#include <plat/iic-core.h> 39#include <plat/iic-core.h>
40#include <plat/s3c6400.h> 40#include <mach/s3c6400.h>
41 41
42void __init s3c6400_map_io(void) 42void __init s3c6400_map_io(void)
43{ 43{
diff --git a/arch/arm/mach-s3c64xx/s3c6410.c b/arch/arm/mach-s3c64xx/s3c6410.c
index b881d6a50b11..fd457cc3ab87 100644
--- a/arch/arm/mach-s3c64xx/s3c6410.c
+++ b/arch/arm/mach-s3c64xx/s3c6410.c
@@ -38,8 +38,8 @@
38#include <plat/clock.h> 38#include <plat/clock.h>
39#include <plat/sdhci.h> 39#include <plat/sdhci.h>
40#include <plat/iic-core.h> 40#include <plat/iic-core.h>
41#include <plat/s3c6400.h> 41#include <mach/s3c6400.h>
42#include <plat/s3c6410.h> 42#include <mach/s3c6410.h>
43 43
44void __init s3c6410_map_io(void) 44void __init s3c6410_map_io(void)
45{ 45{
diff --git a/arch/arm/mach-s3c64xx/sleep.S b/arch/arm/mach-s3c64xx/sleep.S
new file mode 100644
index 000000000000..b2ef44317368
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/sleep.S
@@ -0,0 +1,144 @@
1/* linux/arch/arm/plat-s3c64xx/sleep.S
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C64XX CPU sleep code
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/linkage.h>
16#include <asm/assembler.h>
17#include <mach/map.h>
18
19#undef S3C64XX_VA_GPIO
20#define S3C64XX_VA_GPIO (0x0)
21
22#include <mach/regs-gpio.h>
23#include <mach/gpio-bank-n.h>
24
25#define LL_UART (S3C_PA_UART + (0x400 * CONFIG_S3C_LOWLEVEL_UART_PORT))
26
27 .text
28
29 /* s3c_cpu_save
30 *
31 * Save enough processor state to allow the restart of the pm.c
32 * code after resume.
33 *
34 * entry:
35 * r0 = pointer to the save block
36 */
37
38ENTRY(s3c_cpu_save)
39 stmfd sp!, { r4 - r12, lr }
40
41 mrc p15, 0, r4, c13, c0, 0 @ FCSE/PID
42 mrc p15, 0, r5, c3, c0, 0 @ Domain ID
43 mrc p15, 0, r6, c2, c0, 0 @ Translation Table BASE0
44 mrc p15, 0, r7, c2, c0, 1 @ Translation Table BASE1
45 mrc p15, 0, r8, c2, c0, 2 @ Translation Table Control
46 mrc p15, 0, r9, c1, c0, 0 @ Control register
47 mrc p15, 0, r10, c1, c0, 1 @ Auxiliary control register
48 mrc p15, 0, r11, c1, c0, 2 @ Co-processor access controls
49
50 stmia r0, { r4 - r13 } @ Save CP registers and SP
51
52 @@ save our state to ram
53 bl s3c_pm_cb_flushcache
54
55 @@ call final suspend code
56 ldr r0, =pm_cpu_sleep
57 ldr pc, [r0]
58
59 @@ return to the caller, after the MMU is turned on.
60 @@ restore the last bits of the stack and return.
61resume_with_mmu:
62 ldmfd sp!, { r4 - r12, pc } @ return, from sp from s3c_cpu_save
63
64 .data
65
66 /* the next bit is code, but it requires easy access to the
67 * s3c_sleep_save_phys data before the MMU is switched on, so
68 * we store the code that needs this variable in the .data where
69 * the value can be written to (the .text segment is RO).
70 */
71
72 .global s3c_sleep_save_phys
73s3c_sleep_save_phys:
74 .word 0
75
76 /* Sleep magic, the word before the resume entry point so that the
77 * bootloader can check for a resumeable image. */
78
79 .word 0x2bedf00d
80
81 /* s3c_cpu_reusme
82 *
83 * This is the entry point, stored by whatever method the bootloader
84 * requires to get the kernel runnign again. This code expects to be
85 * entered with no caches live and the MMU disabled. It will then
86 * restore the MMU and other basic CP registers saved and restart
87 * the kernel C code to finish the resume code.
88 */
89
90ENTRY(s3c_cpu_resume)
91 msr cpsr_c, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
92 ldr r2, =LL_UART /* for debug */
93
94#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
95 /* Initialise the GPIO state if we are debugging via the SMDK LEDs,
96 * as the uboot version supplied resets these to inputs during the
97 * resume checks.
98 */
99
100 ldr r3, =S3C64XX_PA_GPIO
101 ldr r0, [ r3, #S3C64XX_GPNCON ]
102 bic r0, r0, #(S3C64XX_GPN_CONMASK(12) | S3C64XX_GPN_CONMASK(13) | \
103 S3C64XX_GPN_CONMASK(14) | S3C64XX_GPN_CONMASK(15))
104 orr r0, r0, #(S3C64XX_GPN_OUTPUT(12) | S3C64XX_GPN_OUTPUT(13) | \
105 S3C64XX_GPN_OUTPUT(14) | S3C64XX_GPN_OUTPUT(15))
106 str r0, [ r3, #S3C64XX_GPNCON ]
107
108 ldr r0, [ r3, #S3C64XX_GPNDAT ]
109 bic r0, r0, #0xf << 12 @ GPN12..15
110 orr r0, r0, #1 << 15 @ GPN15
111 str r0, [ r3, #S3C64XX_GPNDAT ]
112#endif
113
114 /* __v6_setup from arch/arm/mm/proc-v6.S, ensure that the caches
115 * are thoroughly cleaned just in case the bootloader didn't do it
116 * for us. */
117 mov r0, #0
118 mcr p15, 0, r0, c7, c14, 0 @ clean+invalidate D cache
119 mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache
120 mcr p15, 0, r0, c7, c15, 0 @ clean+invalidate cache
121 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
122 @@mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs
123 @@mcr p15, 0, r0, c7, c7, 0 @ Invalidate I + D caches
124
125 ldr r0, s3c_sleep_save_phys
126 ldmia r0, { r4 - r13 }
127
128 mcr p15, 0, r4, c13, c0, 0 @ FCSE/PID
129 mcr p15, 0, r5, c3, c0, 0 @ Domain ID
130 mcr p15, 0, r6, c2, c0, 0 @ Translation Table BASE0
131 mcr p15, 0, r7, c2, c0, 1 @ Translation Table BASE1
132 mcr p15, 0, r8, c2, c0, 2 @ Translation Table Control
133 mcr p15, 0, r10, c1, c0, 1 @ Auxiliary control register
134
135 mov r0, #0 @ restore copro access controls
136 mcr p15, 0, r11, c1, c0, 2 @ Co-processor access controls
137 mcr p15, 0, r0, c7, c5, 4
138
139 ldr r2, =resume_with_mmu
140 mcr p15, 0, r9, c1, c0, 0 /* turn mmu back on */
141 nop
142 mov pc, r2 /* jump back */
143
144 .end