aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-s3c64xx
diff options
context:
space:
mode:
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 15e065ef19a..7c9cd9a9901 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 49b71d5f2e5..4417f1ad99b 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 00000000000..229bb3bcc54
--- /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 00000000000..410d688a691
--- /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 00000000000..74c0e8347de
--- /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 00000000000..0e0edf75e8e
--- /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 00000000000..66e6794481d
--- /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 00000000000..90bbd72fdc4
--- /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 00000000000..2bc7c07a928
--- /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 00000000000..24f1141ffcb
--- /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 06d8fe579e1..4a0bb243d14 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 284886c26a2..a6d91c39f22 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 9be92ddd217..bf65747ea68 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 ba8a052a614..f7b18983950 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 021670e39d3..fdf8f7539a1 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 00000000000..b8ac4597fad
--- /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 2fba1b263fe..720d0d1f3bf 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 b881d6a50b1..fd457cc3ab8 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 00000000000..b2ef4431736
--- /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