diff options
Diffstat (limited to 'arch/arm/mach-s3c64xx')
-rw-r--r-- | arch/arm/mach-s3c64xx/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/Makefile | 15 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/clock.c | 306 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/cpu.c | 161 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/cpufreq.c | 270 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/dma.c | 750 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/gpiolib.c | 288 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/include/mach/pll.h | 74 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/include/mach/s3c6400.h | 35 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/include/mach/s3c6410.h | 29 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/mach-anw6410.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/mach-hmt.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/mach-ncp.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/mach-smdk6400.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/mach-smdk6410.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/pm.c | 173 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/s3c6400.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/s3c6410.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/sleep.S | 144 |
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 | ||
22 | config S3C64XX_DMA | ||
23 | bool "S3C64XX DMA" | ||
24 | select S3C_DMA | ||
25 | |||
22 | config S3C64XX_SETUP_SDHCI | 26 | config 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 := | |||
10 | obj-n := | 10 | obj-n := |
11 | obj- := | 11 | obj- := |
12 | 12 | ||
13 | # Core files | ||
14 | obj-y += cpu.o | ||
15 | obj-y += clock.o | ||
16 | obj-y += gpiolib.o | ||
17 | |||
13 | # Core support for S3C6400 system | 18 | # Core support for S3C6400 system |
14 | 19 | ||
15 | obj-$(CONFIG_CPU_S3C6400) += s3c6400.o | 20 | obj-$(CONFIG_CPU_S3C6400) += s3c6400.o |
@@ -18,6 +23,14 @@ obj-$(CONFIG_CPU_S3C6410) += s3c6410.o | |||
18 | obj-y += irq.o | 23 | obj-y += irq.o |
19 | obj-y += irq-eint.o | 24 | obj-y += irq-eint.o |
20 | 25 | ||
26 | # CPU frequency scaling | ||
27 | |||
28 | obj-$(CONFIG_CPU_FREQ_S3C64XX) += cpufreq.o | ||
29 | |||
30 | # DMA support | ||
31 | |||
32 | obj-$(CONFIG_S3C64XX_DMA) += dma.o | ||
33 | |||
21 | # Device setup | 34 | # Device setup |
22 | 35 | ||
23 | obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o | 36 | obj-$(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 | ||
44 | obj-$(CONFIG_PM) += pm.o | ||
45 | obj-$(CONFIG_PM) += sleep.o | ||
31 | obj-$(CONFIG_PM) += irq-pm.o | 46 | obj-$(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 | |||
32 | struct clk clk_h2 = { | ||
33 | .name = "hclk2", | ||
34 | .id = -1, | ||
35 | .rate = 0, | ||
36 | }; | ||
37 | |||
38 | struct clk clk_27m = { | ||
39 | .name = "clk_27m", | ||
40 | .id = -1, | ||
41 | .rate = 27000000, | ||
42 | }; | ||
43 | |||
44 | static 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 | |||
64 | struct clk clk_48m = { | ||
65 | .name = "clk_48m", | ||
66 | .id = -1, | ||
67 | .rate = 48000000, | ||
68 | .enable = clk_48m_ctrl, | ||
69 | }; | ||
70 | |||
71 | static 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 | |||
89 | static int s3c64xx_pclk_ctrl(struct clk *clk, int enable) | ||
90 | { | ||
91 | return s3c64xx_gate(S3C_PCLK_GATE, clk, enable); | ||
92 | } | ||
93 | |||
94 | static int s3c64xx_hclk_ctrl(struct clk *clk, int enable) | ||
95 | { | ||
96 | return s3c64xx_gate(S3C_HCLK_GATE, clk, enable); | ||
97 | } | ||
98 | |||
99 | int s3c64xx_sclk_ctrl(struct clk *clk, int enable) | ||
100 | { | ||
101 | return s3c64xx_gate(S3C_SCLK_GATE, clk, enable); | ||
102 | } | ||
103 | |||
104 | static 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 | |||
190 | static 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 | |||
276 | static struct clk *clks[] __initdata = { | ||
277 | &clk_ext, | ||
278 | &clk_epll, | ||
279 | &clk_27m, | ||
280 | &clk_48m, | ||
281 | &clk_h2, | ||
282 | }; | ||
283 | |||
284 | void __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 | |||
41 | static const char name_s3c6400[] = "S3C6400"; | ||
42 | static const char name_s3c6410[] = "S3C6410"; | ||
43 | |||
44 | static 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 | |||
69 | static 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 | |||
124 | struct sysdev_class s3c64xx_sysclass = { | ||
125 | .name = "s3c64xx-core", | ||
126 | }; | ||
127 | |||
128 | static struct sys_device s3c64xx_sysdev = { | ||
129 | .cls = &s3c64xx_sysclass, | ||
130 | }; | ||
131 | |||
132 | |||
133 | /* read cpu identification code */ | ||
134 | |||
135 | void __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 | |||
155 | static __init int s3c64xx_sysdev_init(void) | ||
156 | { | ||
157 | sysdev_class_register(&s3c64xx_sysclass); | ||
158 | return sysdev_register(&s3c64xx_sysdev); | ||
159 | } | ||
160 | |||
161 | core_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 | |||
20 | static struct clk *armclk; | ||
21 | static struct regulator *vddarm; | ||
22 | static unsigned long regulator_latency; | ||
23 | |||
24 | #ifdef CONFIG_CPU_S3C6410 | ||
25 | struct s3c64xx_dvfs { | ||
26 | unsigned int vddarm_min; | ||
27 | unsigned int vddarm_max; | ||
28 | }; | ||
29 | |||
30 | static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = { | ||
31 | [0] = { 1000000, 1150000 }, | ||
32 | [1] = { 1050000, 1150000 }, | ||
33 | [2] = { 1100000, 1150000 }, | ||
34 | [3] = { 1200000, 1350000 }, | ||
35 | }; | ||
36 | |||
37 | static 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 | |||
51 | static 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 | |||
59 | static 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 | |||
67 | static 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 | |||
134 | err_clk: | ||
135 | if (clk_set_rate(armclk, freqs.old * 1000) < 0) | ||
136 | pr_err("Failed to restore original clock rate\n"); | ||
137 | err: | ||
138 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
139 | |||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | #ifdef CONFIG_REGULATOR | ||
144 | static 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 | |||
184 | static 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 | |||
256 | static 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 | |||
266 | static int __init s3c64xx_cpufreq_init(void) | ||
267 | { | ||
268 | return cpufreq_register_driver(&s3c64xx_cpufreq_driver); | ||
269 | } | ||
270 | module_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 | |||
37 | struct 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 */ | ||
46 | static struct dma_pool *dma_pool; | ||
47 | |||
48 | /* Debug configuration and code */ | ||
49 | |||
50 | static unsigned char debug_show_buffs = 0; | ||
51 | |||
52 | static 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 | |||
64 | static 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 | |||
71 | static 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 | |||
93 | static 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 | |||
111 | found: | ||
112 | s3c_dma_chan_map[channel] = chan; | ||
113 | return chan; | ||
114 | } | ||
115 | |||
116 | int 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 | } | ||
140 | EXPORT_SYMBOL(s3c2410_dma_config); | ||
141 | |||
142 | static 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 | |||
182 | static 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 | |||
197 | static 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 | |||
222 | static 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 | |||
257 | static 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 | |||
265 | static 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 | |||
271 | static 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 | |||
299 | int 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 | } | ||
327 | EXPORT_SYMBOL(s3c2410_dma_ctrl); | ||
328 | |||
329 | /* s3c2410_dma_enque | ||
330 | * | ||
331 | */ | ||
332 | |||
333 | int 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 | |||
409 | err_buff: | ||
410 | kfree(buff); | ||
411 | return ret; | ||
412 | } | ||
413 | |||
414 | EXPORT_SYMBOL(s3c2410_dma_enqueue); | ||
415 | |||
416 | |||
417 | int 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 | } | ||
462 | EXPORT_SYMBOL(s3c2410_dma_devconfig); | ||
463 | |||
464 | |||
465 | int 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 | } | ||
482 | EXPORT_SYMBOL(s3c2410_dma_getposition); | ||
483 | |||
484 | /* s3c2410_request_dma | ||
485 | * | ||
486 | * get control of an dma channel | ||
487 | */ | ||
488 | |||
489 | int 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 | |||
522 | EXPORT_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 | |||
535 | int 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 | |||
564 | EXPORT_SYMBOL(s3c2410_dma_free); | ||
565 | |||
566 | static 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 | |||
633 | static struct sysdev_class dma_sysclass = { | ||
634 | .name = "s3c64xx-dma", | ||
635 | }; | ||
636 | |||
637 | static 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 | |||
710 | err_clk: | ||
711 | clk_disable(dmac->clk); | ||
712 | clk_put(dmac->clk); | ||
713 | err_map: | ||
714 | iounmap(regs); | ||
715 | err_dev: | ||
716 | sysdev_unregister(&dmac->sysdev); | ||
717 | err_alloc: | ||
718 | kfree(dmac); | ||
719 | return err; | ||
720 | } | ||
721 | |||
722 | static 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 | |||
750 | arch_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 | |||
52 | static 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 | |||
58 | static 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 | |||
65 | static 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 | |||
72 | int s3c64xx_gpio2int_gpm(struct gpio_chip *chip, unsigned pin) | ||
73 | { | ||
74 | return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO; | ||
75 | } | ||
76 | |||
77 | static 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 | |||
138 | int s3c64xx_gpio2int_gpl(struct gpio_chip *chip, unsigned pin) | ||
139 | { | ||
140 | return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO; | ||
141 | } | ||
142 | |||
143 | static 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 | |||
172 | static 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 | |||
178 | static 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 | |||
185 | static 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 | |||
192 | int s3c64xx_gpio2int_gpn(struct gpio_chip *chip, unsigned pin) | ||
193 | { | ||
194 | return IRQ_EINT(0) + pin; | ||
195 | } | ||
196 | |||
197 | static 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 | |||
258 | static __init void s3c64xx_gpiolib_add_2bit(struct s3c_gpio_chip *chip) | ||
259 | { | ||
260 | chip->pm = __gpio_pm(&s3c_gpio_pm_2bit); | ||
261 | } | ||
262 | |||
263 | static __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 | |||
274 | static __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 | |||
288 | core_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 | |||
24 | static 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 | |||
48 | static 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 | |||
17 | extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); | ||
18 | extern void s3c6400_register_clocks(unsigned armclk_divlimit); | ||
19 | extern void s3c6400_setup_clocks(void); | ||
20 | |||
21 | #ifdef CONFIG_CPU_S3C6400 | ||
22 | |||
23 | extern int s3c6400_init(void); | ||
24 | extern void s3c6400_init_irq(void); | ||
25 | extern void s3c6400_map_io(void); | ||
26 | extern 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 | |||
17 | extern int s3c6410_init(void); | ||
18 | extern void s3c6410_init_irq(void); | ||
19 | extern void s3c6410_map_io(void); | ||
20 | extern 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 | |||
32 | void 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 | |||
54 | static 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 | |||
80 | static 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 | |||
94 | void s3c_pm_configure_extint(void) | ||
95 | { | ||
96 | __raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK); | ||
97 | } | ||
98 | |||
99 | void 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 | |||
109 | void 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 | |||
120 | static 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 | |||
156 | static 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 | |||
165 | static 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 | |||
173 | arch_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 | ||
42 | void __init s3c6400_map_io(void) | 42 | void __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 | ||
44 | void __init s3c6410_map_io(void) | 44 | void __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 | |||
38 | ENTRY(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. | ||
61 | resume_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 | ||
73 | s3c_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 | |||
90 | ENTRY(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 | ||