diff options
Diffstat (limited to 'arch/arm/mach-s3c2410')
34 files changed, 7722 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig new file mode 100644 index 000000000000..534df0c6c770 --- /dev/null +++ b/arch/arm/mach-s3c2410/Kconfig | |||
@@ -0,0 +1,169 @@ | |||
1 | if ARCH_S3C2410 | ||
2 | |||
3 | menu "S3C24XX Implementations" | ||
4 | |||
5 | config ARCH_BAST | ||
6 | bool "Simtec Electronics BAST (EB2410ITX)" | ||
7 | select CPU_S3C2410 | ||
8 | help | ||
9 | Say Y here if you are using the Simtec Electronics EB2410ITX | ||
10 | development board (also known as BAST) | ||
11 | |||
12 | Product page: <http://www.simtec.co.uk/products/EB2410ITX/>. | ||
13 | |||
14 | config ARCH_H1940 | ||
15 | bool "IPAQ H1940" | ||
16 | select CPU_S3C2410 | ||
17 | help | ||
18 | Say Y here if you are using the HP IPAQ H1940 | ||
19 | |||
20 | <http://www.handhelds.org/projects/h1940.html>. | ||
21 | |||
22 | config MACH_N30 | ||
23 | bool "Acer N30" | ||
24 | select CPU_S3C2410 | ||
25 | help | ||
26 | Say Y here if you are using the Acer N30 | ||
27 | |||
28 | <http://zoo.weinigel.se/n30>. | ||
29 | |||
30 | config ARCH_SMDK2410 | ||
31 | bool "SMDK2410/A9M2410" | ||
32 | select CPU_S3C2410 | ||
33 | help | ||
34 | Say Y here if you are using the SMDK2410 or the derived module A9M2410 | ||
35 | <http://www.fsforth.de> | ||
36 | |||
37 | config ARCH_S3C2440 | ||
38 | bool "SMDK2440" | ||
39 | select CPU_S3C2440 | ||
40 | help | ||
41 | Say Y here if you are using the SMDK2440. | ||
42 | |||
43 | config MACH_VR1000 | ||
44 | bool "Thorcom VR1000" | ||
45 | select CPU_S3C2410 | ||
46 | help | ||
47 | Say Y here if you are using the Thorcom VR1000 board. | ||
48 | |||
49 | This linux port is currently being maintained by Simtec, on behalf | ||
50 | of Thorcom. Any queries, please contact Thorcom first. | ||
51 | |||
52 | config MACH_RX3715 | ||
53 | bool "HP iPAQ rx3715" | ||
54 | select CPU_S3C2440 | ||
55 | help | ||
56 | Say Y here if you are using the HP iPAQ rx3715. | ||
57 | |||
58 | See <http://www.handhelds.org/projects/rx3715.html> for more | ||
59 | information on this project | ||
60 | |||
61 | config MACH_OTOM | ||
62 | bool "NexVision OTOM Board" | ||
63 | select CPU_S3C2410 | ||
64 | help | ||
65 | Say Y here if you are using the Nex Vision OTOM board | ||
66 | |||
67 | config MACH_NEXCODER_2440 | ||
68 | bool "NexVision NEXCODER 2440 Light Board" | ||
69 | select CPU_S3C2440 | ||
70 | help | ||
71 | Say Y here if you are using the Nex Vision NEXCODER 2440 Light Board | ||
72 | |||
73 | endmenu | ||
74 | |||
75 | config CPU_S3C2410 | ||
76 | bool | ||
77 | depends on ARCH_S3C2410 | ||
78 | help | ||
79 | Support for S3C2410 and S3C2410A family from the S3C24XX line | ||
80 | of Samsung Mobile CPUs. | ||
81 | |||
82 | config CPU_S3C2440 | ||
83 | bool | ||
84 | depends on ARCH_S3C2410 | ||
85 | help | ||
86 | Support for S3C2440 Samsung Mobile CPU based systems. | ||
87 | |||
88 | comment "S3C2410 Boot" | ||
89 | |||
90 | config S3C2410_BOOT_WATCHDOG | ||
91 | bool "S3C2410 Initialisation watchdog" | ||
92 | depends on ARCH_S3C2410 && S3C2410_WATCHDOG | ||
93 | help | ||
94 | Say y to enable the watchdog during the kernel decompression | ||
95 | stage. If the kernel fails to uncompress, then the watchdog | ||
96 | will trigger a reset and the system should restart. | ||
97 | |||
98 | Although this uses the same hardware unit as the kernel watchdog | ||
99 | driver, it is not a replacement for it. If you use this option, | ||
100 | you will have to use the watchdg driver to either stop the timeout | ||
101 | or restart it. If you do not, then your kernel will reboot after | ||
102 | startup. | ||
103 | |||
104 | The driver uses a fixed timeout value, so the exact time till the | ||
105 | system resets depends on the value of PCLK. The timeout on an | ||
106 | 200MHz s3c2410 should be about 30 seconds. | ||
107 | |||
108 | comment "S3C2410 Setup" | ||
109 | |||
110 | config S3C2410_DMA | ||
111 | bool "S3C2410 DMA support" | ||
112 | depends on ARCH_S3C2410 | ||
113 | help | ||
114 | S3C2410 DMA support. This is needed for drivers like sound which | ||
115 | use the S3C2410's DMA system to move data to and from the | ||
116 | peripheral blocks. | ||
117 | |||
118 | config S3C2410_DMA_DEBUG | ||
119 | bool "S3C2410 DMA support debug" | ||
120 | depends on ARCH_S3C2410 && S3C2410_DMA | ||
121 | help | ||
122 | Enable debugging output for the DMA code. This option sends info | ||
123 | to the kernel log, at priority KERN_DEBUG. | ||
124 | |||
125 | Note, it is easy to create and fill the log buffer in a small | ||
126 | amount of time, as well as using an significant percentage of | ||
127 | the CPU time doing so. | ||
128 | |||
129 | |||
130 | config S3C2410_PM_DEBUG | ||
131 | bool "S3C2410 PM Suspend debug" | ||
132 | depends on ARCH_S3C2410 && PM | ||
133 | help | ||
134 | Say Y here if you want verbose debugging from the PM Suspend and | ||
135 | Resume code. See `Documentation/arm/Samsing-S3C24XX/Suspend.txt` | ||
136 | for more information. | ||
137 | |||
138 | config S3C2410_PM_CHECK | ||
139 | bool "S3C2410 PM Suspend Memory CRC" | ||
140 | depends on ARCH_S3C2410 && PM && CRC32 | ||
141 | help | ||
142 | Enable the PM code's memory area checksum over sleep. This option | ||
143 | will generate CRCs of all blocks of memory, and store them before | ||
144 | going to sleep. The blocks are then checked on resume for any | ||
145 | errors. | ||
146 | |||
147 | config S3C2410_PM_CHECK_CHUNKSIZE | ||
148 | int "S3C2410 PM Suspend CRC Chunksize (KiB)" | ||
149 | depends on ARCH_S3C2410 && PM && S3C2410_PM_CHECK | ||
150 | default 64 | ||
151 | help | ||
152 | Set the chunksize in Kilobytes of the CRC for checking memory | ||
153 | corruption over suspend and resume. A smaller value will mean that | ||
154 | the CRC data block will take more memory, but wil identify any | ||
155 | faults with better precision. | ||
156 | |||
157 | config S3C2410_LOWLEVEL_UART_PORT | ||
158 | int "S3C2410 UART to use for low-level messages" | ||
159 | default 0 | ||
160 | help | ||
161 | Choice of which UART port to use for the low-level messages, | ||
162 | such as the `Uncompressing...` at start time. The value of | ||
163 | this configuration should be between zero and two. The port | ||
164 | must have been initialised by the boot-loader before use. | ||
165 | |||
166 | Note, this does not affect the port used by the debug messages, | ||
167 | which is a separate configuration. | ||
168 | |||
169 | endif | ||
diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile new file mode 100644 index 000000000000..7c379aad5d62 --- /dev/null +++ b/arch/arm/mach-s3c2410/Makefile | |||
@@ -0,0 +1,36 @@ | |||
1 | |||
2 | # | ||
3 | # Makefile for the linux kernel. | ||
4 | # | ||
5 | |||
6 | # Object file lists. | ||
7 | |||
8 | obj-y := cpu.o irq.o time.o gpio.o clock.o devs.o | ||
9 | obj-m := | ||
10 | obj-n := | ||
11 | obj- := | ||
12 | |||
13 | # S3C2410 support files | ||
14 | |||
15 | obj-$(CONFIG_CPU_S3C2410) += s3c2410.o | ||
16 | obj-$(CONFIG_S3C2410_DMA) += dma.o | ||
17 | |||
18 | # Power Management support | ||
19 | |||
20 | obj-$(CONFIG_PM) += pm.o sleep.o | ||
21 | |||
22 | # S3C2440 support | ||
23 | |||
24 | obj-$(CONFIG_CPU_S3C2440) += s3c2440.o s3c2440-dsc.o | ||
25 | |||
26 | # machine specific support | ||
27 | |||
28 | obj-$(CONFIG_ARCH_BAST) += mach-bast.o usb-simtec.o | ||
29 | obj-$(CONFIG_ARCH_H1940) += mach-h1940.o | ||
30 | obj-$(CONFIG_MACH_N30) += mach-n30.o | ||
31 | obj-$(CONFIG_ARCH_SMDK2410) += mach-smdk2410.o | ||
32 | obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o | ||
33 | obj-$(CONFIG_MACH_VR1000) += mach-vr1000.o usb-simtec.o | ||
34 | obj-$(CONFIG_MACH_RX3715) += mach-rx3715.o | ||
35 | obj-$(CONFIG_MACH_OTOM) += mach-otom.o | ||
36 | obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o | ||
diff --git a/arch/arm/mach-s3c2410/Makefile.boot b/arch/arm/mach-s3c2410/Makefile.boot new file mode 100644 index 000000000000..7dab2a0325b5 --- /dev/null +++ b/arch/arm/mach-s3c2410/Makefile.boot | |||
@@ -0,0 +1,3 @@ | |||
1 | zreladdr-y := 0x30008000 | ||
2 | params_phys-y := 0x30000100 | ||
3 | |||
diff --git a/arch/arm/mach-s3c2410/bast-irq.c b/arch/arm/mach-s3c2410/bast-irq.c new file mode 100644 index 000000000000..5e5bbe893cbb --- /dev/null +++ b/arch/arm/mach-s3c2410/bast-irq.c | |||
@@ -0,0 +1,132 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/bast-irq.c | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * http://www.simtec.co.uk/products/EB2410ITX/ | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * Modifications: | ||
23 | * 08-Jan-2003 BJD Moved from central IRQ code | ||
24 | */ | ||
25 | |||
26 | |||
27 | #include <linux/init.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/ioport.h> | ||
30 | #include <linux/ptrace.h> | ||
31 | #include <linux/sysdev.h> | ||
32 | |||
33 | #include <asm/hardware.h> | ||
34 | #include <asm/irq.h> | ||
35 | #include <asm/io.h> | ||
36 | |||
37 | #include <asm/mach/irq.h> | ||
38 | #include <asm/hardware/s3c2410/irq.h> | ||
39 | |||
40 | #if 0 | ||
41 | #include <asm/debug-ll.h> | ||
42 | #endif | ||
43 | |||
44 | #define irqdbf(x...) | ||
45 | #define irqdbf2(x...) | ||
46 | |||
47 | |||
48 | /* handle PC104 ISA interrupts from the system CPLD */ | ||
49 | |||
50 | /* table of ISA irq nos to the relevant mask... zero means | ||
51 | * the irq is not implemented | ||
52 | */ | ||
53 | static unsigned char bast_pc104_irqmasks[] = { | ||
54 | 0, /* 0 */ | ||
55 | 0, /* 1 */ | ||
56 | 0, /* 2 */ | ||
57 | 1, /* 3 */ | ||
58 | 0, /* 4 */ | ||
59 | 2, /* 5 */ | ||
60 | 0, /* 6 */ | ||
61 | 4, /* 7 */ | ||
62 | 0, /* 8 */ | ||
63 | 0, /* 9 */ | ||
64 | 8, /* 10 */ | ||
65 | 0, /* 11 */ | ||
66 | 0, /* 12 */ | ||
67 | 0, /* 13 */ | ||
68 | 0, /* 14 */ | ||
69 | 0, /* 15 */ | ||
70 | }; | ||
71 | |||
72 | static unsigned char bast_pc104_irqs[] = { 3, 5, 7, 10 }; | ||
73 | |||
74 | static void | ||
75 | bast_pc104_mask(unsigned int irqno) | ||
76 | { | ||
77 | unsigned long temp; | ||
78 | |||
79 | temp = __raw_readb(BAST_VA_PC104_IRQMASK); | ||
80 | temp &= ~bast_pc104_irqmasks[irqno]; | ||
81 | __raw_writeb(temp, BAST_VA_PC104_IRQMASK); | ||
82 | |||
83 | if (temp == 0) | ||
84 | bast_extint_mask(IRQ_ISA); | ||
85 | } | ||
86 | |||
87 | static void | ||
88 | bast_pc104_ack(unsigned int irqno) | ||
89 | { | ||
90 | bast_extint_ack(IRQ_ISA); | ||
91 | } | ||
92 | |||
93 | static void | ||
94 | bast_pc104_unmask(unsigned int irqno) | ||
95 | { | ||
96 | unsigned long temp; | ||
97 | |||
98 | temp = __raw_readb(BAST_VA_PC104_IRQMASK); | ||
99 | temp |= bast_pc104_irqmasks[irqno]; | ||
100 | __raw_writeb(temp, BAST_VA_PC104_IRQMASK); | ||
101 | |||
102 | bast_extint_unmask(IRQ_ISA); | ||
103 | } | ||
104 | |||
105 | static struct bast_pc104_chip = { | ||
106 | .mask = bast_pc104_mask, | ||
107 | .unmask = bast_pc104_unmask, | ||
108 | .ack = bast_pc104_ack | ||
109 | }; | ||
110 | |||
111 | static void | ||
112 | bast_irq_pc104_demux(unsigned int irq, | ||
113 | struct irqdesc *desc, | ||
114 | struct pt_regs *regs) | ||
115 | { | ||
116 | unsigned int stat; | ||
117 | unsigned int irqno; | ||
118 | int i; | ||
119 | |||
120 | stat = __raw_readb(BAST_VA_PC104_IRQREQ) & 0xf; | ||
121 | |||
122 | for (i = 0; i < 4 && stat != 0; i++) { | ||
123 | if (stat & 1) { | ||
124 | irqno = bast_pc104_irqs[i]; | ||
125 | desc = irq_desc + irqno; | ||
126 | |||
127 | desc->handle(irqno, desc, regs); | ||
128 | } | ||
129 | |||
130 | stat >>= 1; | ||
131 | } | ||
132 | } | ||
diff --git a/arch/arm/mach-s3c2410/bast.h b/arch/arm/mach-s3c2410/bast.h new file mode 100644 index 000000000000..e5d03311752c --- /dev/null +++ b/arch/arm/mach-s3c2410/bast.h | |||
@@ -0,0 +1,2 @@ | |||
1 | |||
2 | extern void bast_init_irq(void); | ||
diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c new file mode 100644 index 000000000000..e23f534d4e1d --- /dev/null +++ b/arch/arm/mach-s3c2410/clock.c | |||
@@ -0,0 +1,507 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/clock.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C2410 Clock control support | ||
7 | * | ||
8 | * Based on, and code from linux/arch/arm/mach-versatile/clock.c | ||
9 | ** | ||
10 | ** Copyright (C) 2004 ARM Limited. | ||
11 | ** Written by Deep Blue Solutions Limited. | ||
12 | * | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
27 | */ | ||
28 | |||
29 | #include <linux/init.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/list.h> | ||
33 | #include <linux/errno.h> | ||
34 | #include <linux/err.h> | ||
35 | #include <linux/device.h> | ||
36 | #include <linux/sysdev.h> | ||
37 | |||
38 | #include <linux/interrupt.h> | ||
39 | #include <linux/ioport.h> | ||
40 | |||
41 | #include <asm/hardware.h> | ||
42 | #include <asm/atomic.h> | ||
43 | #include <asm/irq.h> | ||
44 | #include <asm/io.h> | ||
45 | |||
46 | #include <asm/hardware/clock.h> | ||
47 | #include <asm/arch/regs-clock.h> | ||
48 | |||
49 | #include "clock.h" | ||
50 | #include "cpu.h" | ||
51 | |||
52 | /* clock information */ | ||
53 | |||
54 | static LIST_HEAD(clocks); | ||
55 | static DECLARE_MUTEX(clocks_sem); | ||
56 | |||
57 | /* old functions */ | ||
58 | |||
59 | void inline s3c24xx_clk_enable(unsigned int clocks, unsigned int enable) | ||
60 | { | ||
61 | unsigned long clkcon; | ||
62 | unsigned long flags; | ||
63 | |||
64 | local_irq_save(flags); | ||
65 | |||
66 | clkcon = __raw_readl(S3C2410_CLKCON); | ||
67 | clkcon &= ~clocks; | ||
68 | |||
69 | if (enable) | ||
70 | clkcon |= clocks; | ||
71 | |||
72 | /* ensure none of the special function bits set */ | ||
73 | clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); | ||
74 | |||
75 | __raw_writel(clkcon, S3C2410_CLKCON); | ||
76 | |||
77 | local_irq_restore(flags); | ||
78 | } | ||
79 | |||
80 | /* enable and disable calls for use with the clk struct */ | ||
81 | |||
82 | static int clk_null_enable(struct clk *clk, int enable) | ||
83 | { | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | int s3c24xx_clkcon_enable(struct clk *clk, int enable) | ||
88 | { | ||
89 | s3c24xx_clk_enable(clk->ctrlbit, enable); | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | /* Clock API calls */ | ||
94 | |||
95 | struct clk *clk_get(struct device *dev, const char *id) | ||
96 | { | ||
97 | struct clk *p; | ||
98 | struct clk *clk = ERR_PTR(-ENOENT); | ||
99 | int idno; | ||
100 | |||
101 | idno = (dev == NULL) ? -1 : to_platform_device(dev)->id; | ||
102 | |||
103 | down(&clocks_sem); | ||
104 | |||
105 | list_for_each_entry(p, &clocks, list) { | ||
106 | if (p->id == idno && | ||
107 | strcmp(id, p->name) == 0 && | ||
108 | try_module_get(p->owner)) { | ||
109 | clk = p; | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | /* check for the case where a device was supplied, but the | ||
115 | * clock that was being searched for is not device specific */ | ||
116 | |||
117 | if (IS_ERR(clk)) { | ||
118 | list_for_each_entry(p, &clocks, list) { | ||
119 | if (p->id == -1 && strcmp(id, p->name) == 0 && | ||
120 | try_module_get(p->owner)) { | ||
121 | clk = p; | ||
122 | break; | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | up(&clocks_sem); | ||
128 | return clk; | ||
129 | } | ||
130 | |||
131 | void clk_put(struct clk *clk) | ||
132 | { | ||
133 | module_put(clk->owner); | ||
134 | } | ||
135 | |||
136 | int clk_enable(struct clk *clk) | ||
137 | { | ||
138 | if (IS_ERR(clk)) | ||
139 | return -EINVAL; | ||
140 | |||
141 | return (clk->enable)(clk, 1); | ||
142 | } | ||
143 | |||
144 | void clk_disable(struct clk *clk) | ||
145 | { | ||
146 | if (!IS_ERR(clk)) | ||
147 | (clk->enable)(clk, 0); | ||
148 | } | ||
149 | |||
150 | |||
151 | int clk_use(struct clk *clk) | ||
152 | { | ||
153 | atomic_inc(&clk->used); | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | |||
158 | void clk_unuse(struct clk *clk) | ||
159 | { | ||
160 | atomic_dec(&clk->used); | ||
161 | } | ||
162 | |||
163 | unsigned long clk_get_rate(struct clk *clk) | ||
164 | { | ||
165 | if (IS_ERR(clk)) | ||
166 | return 0; | ||
167 | |||
168 | if (clk->rate != 0) | ||
169 | return clk->rate; | ||
170 | |||
171 | while (clk->parent != NULL && clk->rate == 0) | ||
172 | clk = clk->parent; | ||
173 | |||
174 | return clk->rate; | ||
175 | } | ||
176 | |||
177 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
178 | { | ||
179 | return rate; | ||
180 | } | ||
181 | |||
182 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
183 | { | ||
184 | return -EINVAL; | ||
185 | } | ||
186 | |||
187 | struct clk *clk_get_parent(struct clk *clk) | ||
188 | { | ||
189 | return clk->parent; | ||
190 | } | ||
191 | |||
192 | EXPORT_SYMBOL(clk_get); | ||
193 | EXPORT_SYMBOL(clk_put); | ||
194 | EXPORT_SYMBOL(clk_enable); | ||
195 | EXPORT_SYMBOL(clk_disable); | ||
196 | EXPORT_SYMBOL(clk_use); | ||
197 | EXPORT_SYMBOL(clk_unuse); | ||
198 | EXPORT_SYMBOL(clk_get_rate); | ||
199 | EXPORT_SYMBOL(clk_round_rate); | ||
200 | EXPORT_SYMBOL(clk_set_rate); | ||
201 | EXPORT_SYMBOL(clk_get_parent); | ||
202 | |||
203 | /* base clocks */ | ||
204 | |||
205 | static struct clk clk_xtal = { | ||
206 | .name = "xtal", | ||
207 | .id = -1, | ||
208 | .rate = 0, | ||
209 | .parent = NULL, | ||
210 | .ctrlbit = 0, | ||
211 | }; | ||
212 | |||
213 | static struct clk clk_f = { | ||
214 | .name = "fclk", | ||
215 | .id = -1, | ||
216 | .rate = 0, | ||
217 | .parent = NULL, | ||
218 | .ctrlbit = 0, | ||
219 | }; | ||
220 | |||
221 | static struct clk clk_h = { | ||
222 | .name = "hclk", | ||
223 | .id = -1, | ||
224 | .rate = 0, | ||
225 | .parent = NULL, | ||
226 | .ctrlbit = 0, | ||
227 | }; | ||
228 | |||
229 | static struct clk clk_p = { | ||
230 | .name = "pclk", | ||
231 | .id = -1, | ||
232 | .rate = 0, | ||
233 | .parent = NULL, | ||
234 | .ctrlbit = 0, | ||
235 | }; | ||
236 | |||
237 | /* clocks that could be registered by external code */ | ||
238 | |||
239 | struct clk s3c24xx_dclk0 = { | ||
240 | .name = "dclk0", | ||
241 | .id = -1, | ||
242 | }; | ||
243 | |||
244 | struct clk s3c24xx_dclk1 = { | ||
245 | .name = "dclk1", | ||
246 | .id = -1, | ||
247 | }; | ||
248 | |||
249 | struct clk s3c24xx_clkout0 = { | ||
250 | .name = "clkout0", | ||
251 | .id = -1, | ||
252 | }; | ||
253 | |||
254 | struct clk s3c24xx_clkout1 = { | ||
255 | .name = "clkout1", | ||
256 | .id = -1, | ||
257 | }; | ||
258 | |||
259 | struct clk s3c24xx_uclk = { | ||
260 | .name = "uclk", | ||
261 | .id = -1, | ||
262 | }; | ||
263 | |||
264 | |||
265 | /* clock definitions */ | ||
266 | |||
267 | static struct clk init_clocks[] = { | ||
268 | { .name = "nand", | ||
269 | .id = -1, | ||
270 | .parent = &clk_h, | ||
271 | .enable = s3c24xx_clkcon_enable, | ||
272 | .ctrlbit = S3C2410_CLKCON_NAND | ||
273 | }, | ||
274 | { .name = "lcd", | ||
275 | .id = -1, | ||
276 | .parent = &clk_h, | ||
277 | .enable = s3c24xx_clkcon_enable, | ||
278 | .ctrlbit = S3C2410_CLKCON_LCDC | ||
279 | }, | ||
280 | { .name = "usb-host", | ||
281 | .id = -1, | ||
282 | .parent = &clk_h, | ||
283 | .enable = s3c24xx_clkcon_enable, | ||
284 | .ctrlbit = S3C2410_CLKCON_USBH | ||
285 | }, | ||
286 | { .name = "usb-device", | ||
287 | .id = -1, | ||
288 | .parent = &clk_h, | ||
289 | .enable = s3c24xx_clkcon_enable, | ||
290 | .ctrlbit = S3C2410_CLKCON_USBD | ||
291 | }, | ||
292 | { .name = "timers", | ||
293 | .id = -1, | ||
294 | .parent = &clk_p, | ||
295 | .enable = s3c24xx_clkcon_enable, | ||
296 | .ctrlbit = S3C2410_CLKCON_PWMT | ||
297 | }, | ||
298 | { .name = "sdi", | ||
299 | .id = -1, | ||
300 | .parent = &clk_p, | ||
301 | .enable = s3c24xx_clkcon_enable, | ||
302 | .ctrlbit = S3C2410_CLKCON_SDI | ||
303 | }, | ||
304 | { .name = "uart", | ||
305 | .id = 0, | ||
306 | .parent = &clk_p, | ||
307 | .enable = s3c24xx_clkcon_enable, | ||
308 | .ctrlbit = S3C2410_CLKCON_UART0 | ||
309 | }, | ||
310 | { .name = "uart", | ||
311 | .id = 1, | ||
312 | .parent = &clk_p, | ||
313 | .enable = s3c24xx_clkcon_enable, | ||
314 | .ctrlbit = S3C2410_CLKCON_UART1 | ||
315 | }, | ||
316 | { .name = "uart", | ||
317 | .id = 2, | ||
318 | .parent = &clk_p, | ||
319 | .enable = s3c24xx_clkcon_enable, | ||
320 | .ctrlbit = S3C2410_CLKCON_UART2 | ||
321 | }, | ||
322 | { .name = "gpio", | ||
323 | .id = -1, | ||
324 | .parent = &clk_p, | ||
325 | .enable = s3c24xx_clkcon_enable, | ||
326 | .ctrlbit = S3C2410_CLKCON_GPIO | ||
327 | }, | ||
328 | { .name = "rtc", | ||
329 | .id = -1, | ||
330 | .parent = &clk_p, | ||
331 | .enable = s3c24xx_clkcon_enable, | ||
332 | .ctrlbit = S3C2410_CLKCON_RTC | ||
333 | }, | ||
334 | { .name = "adc", | ||
335 | .id = -1, | ||
336 | .parent = &clk_p, | ||
337 | .enable = s3c24xx_clkcon_enable, | ||
338 | .ctrlbit = S3C2410_CLKCON_ADC | ||
339 | }, | ||
340 | { .name = "i2c", | ||
341 | .id = -1, | ||
342 | .parent = &clk_p, | ||
343 | .enable = s3c24xx_clkcon_enable, | ||
344 | .ctrlbit = S3C2410_CLKCON_IIC | ||
345 | }, | ||
346 | { .name = "iis", | ||
347 | .id = -1, | ||
348 | .parent = &clk_p, | ||
349 | .enable = s3c24xx_clkcon_enable, | ||
350 | .ctrlbit = S3C2410_CLKCON_IIS | ||
351 | }, | ||
352 | { .name = "spi", | ||
353 | .id = -1, | ||
354 | .parent = &clk_p, | ||
355 | .enable = s3c24xx_clkcon_enable, | ||
356 | .ctrlbit = S3C2410_CLKCON_SPI | ||
357 | }, | ||
358 | { .name = "watchdog", | ||
359 | .id = -1, | ||
360 | .parent = &clk_p, | ||
361 | .ctrlbit = 0 | ||
362 | } | ||
363 | }; | ||
364 | |||
365 | /* initialise the clock system */ | ||
366 | |||
367 | int s3c24xx_register_clock(struct clk *clk) | ||
368 | { | ||
369 | clk->owner = THIS_MODULE; | ||
370 | atomic_set(&clk->used, 0); | ||
371 | |||
372 | if (clk->enable == NULL) | ||
373 | clk->enable = clk_null_enable; | ||
374 | |||
375 | /* add to the list of available clocks */ | ||
376 | |||
377 | down(&clocks_sem); | ||
378 | list_add(&clk->list, &clocks); | ||
379 | up(&clocks_sem); | ||
380 | |||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | /* initalise all the clocks */ | ||
385 | |||
386 | int __init s3c24xx_setup_clocks(unsigned long xtal, | ||
387 | unsigned long fclk, | ||
388 | unsigned long hclk, | ||
389 | unsigned long pclk) | ||
390 | { | ||
391 | struct clk *clkp = init_clocks; | ||
392 | int ptr; | ||
393 | int ret; | ||
394 | |||
395 | printk(KERN_INFO "S3C2410 Clocks, (c) 2004 Simtec Electronics\n"); | ||
396 | |||
397 | /* initialise the main system clocks */ | ||
398 | |||
399 | clk_xtal.rate = xtal; | ||
400 | |||
401 | clk_h.rate = hclk; | ||
402 | clk_p.rate = pclk; | ||
403 | clk_f.rate = fclk; | ||
404 | |||
405 | /* it looks like just setting the register here is not good | ||
406 | * enough, and causes the odd hang at initial boot time, so | ||
407 | * do all of them indivdually. | ||
408 | * | ||
409 | * I think disabling the LCD clock if the LCD is active is | ||
410 | * very dangerous, and therefore the bootloader should be | ||
411 | * careful to not enable the LCD clock if it is not needed. | ||
412 | * | ||
413 | * and of course, this looks neater | ||
414 | */ | ||
415 | |||
416 | s3c24xx_clk_enable(S3C2410_CLKCON_NAND, 0); | ||
417 | s3c24xx_clk_enable(S3C2410_CLKCON_USBH, 0); | ||
418 | s3c24xx_clk_enable(S3C2410_CLKCON_USBD, 0); | ||
419 | s3c24xx_clk_enable(S3C2410_CLKCON_ADC, 0); | ||
420 | s3c24xx_clk_enable(S3C2410_CLKCON_IIC, 0); | ||
421 | s3c24xx_clk_enable(S3C2410_CLKCON_SPI, 0); | ||
422 | |||
423 | /* assume uart clocks are correctly setup */ | ||
424 | |||
425 | /* register our clocks */ | ||
426 | |||
427 | if (s3c24xx_register_clock(&clk_xtal) < 0) | ||
428 | printk(KERN_ERR "failed to register master xtal\n"); | ||
429 | |||
430 | if (s3c24xx_register_clock(&clk_f) < 0) | ||
431 | printk(KERN_ERR "failed to register cpu fclk\n"); | ||
432 | |||
433 | if (s3c24xx_register_clock(&clk_h) < 0) | ||
434 | printk(KERN_ERR "failed to register cpu hclk\n"); | ||
435 | |||
436 | if (s3c24xx_register_clock(&clk_p) < 0) | ||
437 | printk(KERN_ERR "failed to register cpu pclk\n"); | ||
438 | |||
439 | /* register clocks from clock array */ | ||
440 | |||
441 | for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { | ||
442 | ret = s3c24xx_register_clock(clkp); | ||
443 | if (ret < 0) { | ||
444 | printk(KERN_ERR "Failed to register clock %s (%d)\n", | ||
445 | clkp->name, ret); | ||
446 | } | ||
447 | } | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | /* S3C2440 extended clock support */ | ||
453 | |||
454 | #ifdef CONFIG_CPU_S3C2440 | ||
455 | |||
456 | static struct clk s3c2440_clk_upll = { | ||
457 | .name = "upll", | ||
458 | .id = -1, | ||
459 | }; | ||
460 | |||
461 | static struct clk s3c2440_clk_cam = { | ||
462 | .name = "camif", | ||
463 | .parent = &clk_h, | ||
464 | .id = -1, | ||
465 | .enable = s3c24xx_clkcon_enable, | ||
466 | .ctrlbit = S3C2440_CLKCON_CAMERA, | ||
467 | }; | ||
468 | |||
469 | static struct clk s3c2440_clk_ac97 = { | ||
470 | .name = "ac97", | ||
471 | .parent = &clk_p, | ||
472 | .id = -1, | ||
473 | .enable = s3c24xx_clkcon_enable, | ||
474 | .ctrlbit = S3C2440_CLKCON_CAMERA, | ||
475 | }; | ||
476 | |||
477 | static int s3c2440_clk_add(struct sys_device *sysdev) | ||
478 | { | ||
479 | unsigned long upllcon = __raw_readl(S3C2410_UPLLCON); | ||
480 | |||
481 | s3c2440_clk_upll.rate = s3c2410_get_pll(upllcon, clk_xtal.rate) * 2; | ||
482 | |||
483 | printk("S3C2440: Clock Support, UPLL %ld.%03ld MHz\n", | ||
484 | print_mhz(s3c2440_clk_upll.rate)); | ||
485 | |||
486 | s3c24xx_register_clock(&s3c2440_clk_ac97); | ||
487 | s3c24xx_register_clock(&s3c2440_clk_cam); | ||
488 | s3c24xx_register_clock(&s3c2440_clk_upll); | ||
489 | |||
490 | clk_disable(&s3c2440_clk_ac97); | ||
491 | clk_disable(&s3c2440_clk_cam); | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | static struct sysdev_driver s3c2440_clk_driver = { | ||
497 | .add = s3c2440_clk_add, | ||
498 | }; | ||
499 | |||
500 | static int s3c24xx_clk_driver(void) | ||
501 | { | ||
502 | return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_clk_driver); | ||
503 | } | ||
504 | |||
505 | arch_initcall(s3c24xx_clk_driver); | ||
506 | |||
507 | #endif /* CONFIG_CPU_S3C2440 */ | ||
diff --git a/arch/arm/mach-s3c2410/clock.h b/arch/arm/mach-s3c2410/clock.h new file mode 100644 index 000000000000..7953b6f397b9 --- /dev/null +++ b/arch/arm/mach-s3c2410/clock.h | |||
@@ -0,0 +1,44 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-s3c2410/clock.h | ||
3 | * | ||
4 | * Copyright (c) 2004-2005 Simtec Electronics | ||
5 | * http://www.simtec.co.uk/products/SWLINUX/ | ||
6 | * Written by Ben Dooks, <ben@simtec.co.uk> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | struct clk { | ||
14 | struct list_head list; | ||
15 | struct module *owner; | ||
16 | struct clk *parent; | ||
17 | const char *name; | ||
18 | int id; | ||
19 | atomic_t used; | ||
20 | unsigned long rate; | ||
21 | unsigned long ctrlbit; | ||
22 | int (*enable)(struct clk *, int enable); | ||
23 | }; | ||
24 | |||
25 | /* other clocks which may be registered by board support */ | ||
26 | |||
27 | extern struct clk s3c24xx_dclk0; | ||
28 | extern struct clk s3c24xx_dclk1; | ||
29 | extern struct clk s3c24xx_clkout0; | ||
30 | extern struct clk s3c24xx_clkout1; | ||
31 | extern struct clk s3c24xx_uclk; | ||
32 | |||
33 | /* exports for arch/arm/mach-s3c2410 | ||
34 | * | ||
35 | * Please DO NOT use these outside of arch/arm/mach-s3c2410 | ||
36 | */ | ||
37 | |||
38 | extern int s3c24xx_clkcon_enable(struct clk *clk, int enable); | ||
39 | extern int s3c24xx_register_clock(struct clk *clk); | ||
40 | |||
41 | extern int s3c24xx_setup_clocks(unsigned long xtal, | ||
42 | unsigned long fclk, | ||
43 | unsigned long hclk, | ||
44 | unsigned long pclk); | ||
diff --git a/arch/arm/mach-s3c2410/cpu.c b/arch/arm/mach-s3c2410/cpu.c new file mode 100644 index 000000000000..ca366e9e264d --- /dev/null +++ b/arch/arm/mach-s3c2410/cpu.c | |||
@@ -0,0 +1,241 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/cpu.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * http://www.simtec.co.uk/products/SWLINUX/ | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * | ||
7 | * S3C24XX CPU Support | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | |||
25 | #include <linux/init.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/ioport.h> | ||
29 | #include <linux/device.h> | ||
30 | |||
31 | #include <asm/hardware.h> | ||
32 | #include <asm/irq.h> | ||
33 | #include <asm/io.h> | ||
34 | #include <asm/delay.h> | ||
35 | |||
36 | #include <asm/mach/arch.h> | ||
37 | #include <asm/mach/map.h> | ||
38 | |||
39 | #include <asm/arch/regs-gpio.h> | ||
40 | |||
41 | #include "cpu.h" | ||
42 | #include "clock.h" | ||
43 | #include "s3c2410.h" | ||
44 | #include "s3c2440.h" | ||
45 | |||
46 | struct cpu_table { | ||
47 | unsigned long idcode; | ||
48 | unsigned long idmask; | ||
49 | void (*map_io)(struct map_desc *mach_desc, int size); | ||
50 | void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); | ||
51 | void (*init_clocks)(int xtal); | ||
52 | int (*init)(void); | ||
53 | const char *name; | ||
54 | }; | ||
55 | |||
56 | /* table of supported CPUs */ | ||
57 | |||
58 | static const char name_s3c2410[] = "S3C2410"; | ||
59 | static const char name_s3c2440[] = "S3C2440"; | ||
60 | static const char name_s3c2410a[] = "S3C2410A"; | ||
61 | static const char name_s3c2440a[] = "S3C2440A"; | ||
62 | |||
63 | static struct cpu_table cpu_ids[] __initdata = { | ||
64 | { | ||
65 | .idcode = 0x32410000, | ||
66 | .idmask = 0xffffffff, | ||
67 | .map_io = s3c2410_map_io, | ||
68 | .init_clocks = s3c2410_init_clocks, | ||
69 | .init_uarts = s3c2410_init_uarts, | ||
70 | .init = s3c2410_init, | ||
71 | .name = name_s3c2410 | ||
72 | }, | ||
73 | { | ||
74 | .idcode = 0x32410002, | ||
75 | .idmask = 0xffffffff, | ||
76 | .map_io = s3c2410_map_io, | ||
77 | .init_clocks = s3c2410_init_clocks, | ||
78 | .init_uarts = s3c2410_init_uarts, | ||
79 | .init = s3c2410_init, | ||
80 | .name = name_s3c2410a | ||
81 | }, | ||
82 | { | ||
83 | .idcode = 0x32440000, | ||
84 | .idmask = 0xffffffff, | ||
85 | .map_io = s3c2440_map_io, | ||
86 | .init_clocks = s3c2440_init_clocks, | ||
87 | .init_uarts = s3c2440_init_uarts, | ||
88 | .init = s3c2440_init, | ||
89 | .name = name_s3c2440 | ||
90 | }, | ||
91 | { | ||
92 | .idcode = 0x32440001, | ||
93 | .idmask = 0xffffffff, | ||
94 | .map_io = s3c2440_map_io, | ||
95 | .init_clocks = s3c2440_init_clocks, | ||
96 | .init_uarts = s3c2440_init_uarts, | ||
97 | .init = s3c2440_init, | ||
98 | .name = name_s3c2440a | ||
99 | } | ||
100 | }; | ||
101 | |||
102 | /* minimal IO mapping */ | ||
103 | |||
104 | static struct map_desc s3c_iodesc[] __initdata = { | ||
105 | IODESC_ENT(GPIO), | ||
106 | IODESC_ENT(IRQ), | ||
107 | IODESC_ENT(MEMCTRL), | ||
108 | IODESC_ENT(UART) | ||
109 | }; | ||
110 | |||
111 | |||
112 | static struct cpu_table * | ||
113 | s3c_lookup_cpu(unsigned long idcode) | ||
114 | { | ||
115 | struct cpu_table *tab; | ||
116 | int count; | ||
117 | |||
118 | tab = cpu_ids; | ||
119 | for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) { | ||
120 | if ((idcode & tab->idmask) == tab->idcode) | ||
121 | return tab; | ||
122 | } | ||
123 | |||
124 | return NULL; | ||
125 | } | ||
126 | |||
127 | /* board information */ | ||
128 | |||
129 | static struct s3c24xx_board *board; | ||
130 | |||
131 | void s3c24xx_set_board(struct s3c24xx_board *b) | ||
132 | { | ||
133 | int i; | ||
134 | |||
135 | board = b; | ||
136 | |||
137 | if (b->clocks_count != 0) { | ||
138 | struct clk **ptr = b->clocks;; | ||
139 | |||
140 | for (i = b->clocks_count; i > 0; i--, ptr++) | ||
141 | s3c24xx_register_clock(*ptr); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /* cpu information */ | ||
146 | |||
147 | static struct cpu_table *cpu; | ||
148 | |||
149 | void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) | ||
150 | { | ||
151 | unsigned long idcode; | ||
152 | |||
153 | /* initialise the io descriptors we need for initialisation */ | ||
154 | iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); | ||
155 | |||
156 | idcode = __raw_readl(S3C2410_GSTATUS1); | ||
157 | cpu = s3c_lookup_cpu(idcode); | ||
158 | |||
159 | if (cpu == NULL) { | ||
160 | printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); | ||
161 | panic("Unknown S3C24XX CPU"); | ||
162 | } | ||
163 | |||
164 | if (cpu->map_io == NULL || cpu->init == NULL) { | ||
165 | printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); | ||
166 | panic("Unsupported S3C24XX CPU"); | ||
167 | } | ||
168 | |||
169 | printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); | ||
170 | |||
171 | (cpu->map_io)(mach_desc, size); | ||
172 | } | ||
173 | |||
174 | /* s3c24xx_init_clocks | ||
175 | * | ||
176 | * Initialise the clock subsystem and associated information from the | ||
177 | * given master crystal value. | ||
178 | * | ||
179 | * xtal = 0 -> use default PLL crystal value (normally 12MHz) | ||
180 | * != 0 -> PLL crystal value in Hz | ||
181 | */ | ||
182 | |||
183 | void __init s3c24xx_init_clocks(int xtal) | ||
184 | { | ||
185 | if (xtal == 0) | ||
186 | xtal = 12*1000*1000; | ||
187 | |||
188 | if (cpu == NULL) | ||
189 | panic("s3c24xx_init_clocks: no cpu setup?\n"); | ||
190 | |||
191 | if (cpu->init_clocks == NULL) | ||
192 | panic("s3c24xx_init_clocks: cpu has no clock init\n"); | ||
193 | else | ||
194 | (cpu->init_clocks)(xtal); | ||
195 | } | ||
196 | |||
197 | void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) | ||
198 | { | ||
199 | if (cpu == NULL) | ||
200 | return; | ||
201 | |||
202 | if (cpu->init_uarts == NULL) { | ||
203 | printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n"); | ||
204 | } else | ||
205 | (cpu->init_uarts)(cfg, no); | ||
206 | } | ||
207 | |||
208 | static int __init s3c_arch_init(void) | ||
209 | { | ||
210 | int ret; | ||
211 | |||
212 | // do the correct init for cpu | ||
213 | |||
214 | if (cpu == NULL) | ||
215 | panic("s3c_arch_init: NULL cpu\n"); | ||
216 | |||
217 | ret = (cpu->init)(); | ||
218 | if (ret != 0) | ||
219 | return ret; | ||
220 | |||
221 | if (board != NULL) { | ||
222 | struct platform_device **ptr = board->devices; | ||
223 | int i; | ||
224 | |||
225 | for (i = 0; i < board->devices_count; i++, ptr++) { | ||
226 | ret = platform_device_register(*ptr); | ||
227 | |||
228 | if (ret) { | ||
229 | printk(KERN_ERR "s3c24xx: failed to add board device %s (%d) @%p\n", (*ptr)->name, ret, *ptr); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | /* mask any error, we may not need all these board | ||
234 | * devices */ | ||
235 | ret = 0; | ||
236 | } | ||
237 | |||
238 | return ret; | ||
239 | } | ||
240 | |||
241 | arch_initcall(s3c_arch_init); | ||
diff --git a/arch/arm/mach-s3c2410/cpu.h b/arch/arm/mach-s3c2410/cpu.h new file mode 100644 index 000000000000..478c15c0e36a --- /dev/null +++ b/arch/arm/mach-s3c2410/cpu.h | |||
@@ -0,0 +1,69 @@ | |||
1 | /* arch/arm/mach-s3c2410/cpu.h | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Header file for S3C24XX CPU support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 24-Aug-2004 BJD Start of generic S3C24XX support | ||
14 | * 18-Oct-2004 BJD Moved board struct into this file | ||
15 | * 04-Jan-2005 BJD New uart initialisation | ||
16 | * 10-Jan-2005 BJD Moved generic init here, specific to cpu headers | ||
17 | * 14-Jan-2005 BJD Added s3c24xx_init_clocks() call | ||
18 | * 10-Mar-2005 LCVR Changed S3C2410_{VA,SZ} to S3C24XX_{VA,SZ} & IODESC_ENT | ||
19 | * 14-Mar-2005 BJD Updated for __iomem | ||
20 | */ | ||
21 | |||
22 | /* todo - fix when rmk changes iodescs to use `void __iomem *` */ | ||
23 | |||
24 | #define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, S3C2410_PA_##x, S3C24XX_SZ_##x, MT_DEVICE } | ||
25 | |||
26 | #ifndef MHZ | ||
27 | #define MHZ (1000*1000) | ||
28 | #endif | ||
29 | |||
30 | #define print_mhz(m) ((m) / MHZ), ((m / 1000) % 1000) | ||
31 | |||
32 | /* forward declaration */ | ||
33 | struct s3c2410_uartcfg; | ||
34 | struct map_desc; | ||
35 | |||
36 | /* core initialisation functions */ | ||
37 | |||
38 | extern void s3c24xx_init_irq(void); | ||
39 | |||
40 | extern void s3c24xx_init_io(struct map_desc *mach_desc, int size); | ||
41 | |||
42 | extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no); | ||
43 | |||
44 | extern void s3c24xx_init_clocks(int xtal); | ||
45 | |||
46 | /* the board structure is used at first initialsation time | ||
47 | * to get info such as the devices to register for this | ||
48 | * board. This is done because platfrom_add_devices() cannot | ||
49 | * be called from the map_io entry. | ||
50 | */ | ||
51 | |||
52 | struct s3c24xx_board { | ||
53 | struct platform_device **devices; | ||
54 | unsigned int devices_count; | ||
55 | |||
56 | struct clk **clocks; | ||
57 | unsigned int clocks_count; | ||
58 | }; | ||
59 | |||
60 | extern void s3c24xx_set_board(struct s3c24xx_board *board); | ||
61 | |||
62 | /* timer for 2410/2440 */ | ||
63 | |||
64 | struct sys_timer; | ||
65 | extern struct sys_timer s3c24xx_timer; | ||
66 | |||
67 | /* system device classes */ | ||
68 | |||
69 | extern struct sysdev_class s3c2440_sysclass; | ||
diff --git a/arch/arm/mach-s3c2410/devs.c b/arch/arm/mach-s3c2410/devs.c new file mode 100644 index 000000000000..64792f678668 --- /dev/null +++ b/arch/arm/mach-s3c2410/devs.c | |||
@@ -0,0 +1,485 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/devs.c | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Base S3C2410 platform device definitions | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 10-Mar-2005 LCVR Changed S3C2410_{VA,SZ} to S3C24XX_{VA,SZ} | ||
14 | * 10-Feb-2005 BJD Added camera from guillaume.gourat@nexvision.tv | ||
15 | * 29-Aug-2004 BJD Added timers 0 through 3 | ||
16 | * 29-Aug-2004 BJD Changed index of devices we only have one of to -1 | ||
17 | * 21-Aug-2004 BJD Added IRQ_TICK to RTC resources | ||
18 | * 18-Aug-2004 BJD Created initial version | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <linux/timer.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/device.h> | ||
28 | |||
29 | #include <asm/mach/arch.h> | ||
30 | #include <asm/mach/map.h> | ||
31 | #include <asm/mach/irq.h> | ||
32 | |||
33 | #include <asm/hardware.h> | ||
34 | #include <asm/io.h> | ||
35 | #include <asm/irq.h> | ||
36 | |||
37 | #include <asm/arch/regs-serial.h> | ||
38 | |||
39 | #include "devs.h" | ||
40 | |||
41 | /* Serial port registrations */ | ||
42 | |||
43 | struct platform_device *s3c24xx_uart_devs[3]; | ||
44 | |||
45 | /* USB Host Controller */ | ||
46 | |||
47 | static struct resource s3c_usb_resource[] = { | ||
48 | [0] = { | ||
49 | .start = S3C2410_PA_USBHOST, | ||
50 | .end = S3C2410_PA_USBHOST + S3C24XX_SZ_USBHOST, | ||
51 | .flags = IORESOURCE_MEM, | ||
52 | }, | ||
53 | [1] = { | ||
54 | .start = IRQ_USBH, | ||
55 | .end = IRQ_USBH, | ||
56 | .flags = IORESOURCE_IRQ, | ||
57 | } | ||
58 | }; | ||
59 | |||
60 | static u64 s3c_device_usb_dmamask = 0xffffffffUL; | ||
61 | |||
62 | struct platform_device s3c_device_usb = { | ||
63 | .name = "s3c2410-ohci", | ||
64 | .id = -1, | ||
65 | .num_resources = ARRAY_SIZE(s3c_usb_resource), | ||
66 | .resource = s3c_usb_resource, | ||
67 | .dev = { | ||
68 | .dma_mask = &s3c_device_usb_dmamask, | ||
69 | .coherent_dma_mask = 0xffffffffUL | ||
70 | } | ||
71 | }; | ||
72 | |||
73 | EXPORT_SYMBOL(s3c_device_usb); | ||
74 | |||
75 | /* LCD Controller */ | ||
76 | |||
77 | static struct resource s3c_lcd_resource[] = { | ||
78 | [0] = { | ||
79 | .start = S3C2410_PA_LCD, | ||
80 | .end = S3C2410_PA_LCD + S3C24XX_SZ_LCD, | ||
81 | .flags = IORESOURCE_MEM, | ||
82 | }, | ||
83 | [1] = { | ||
84 | .start = IRQ_LCD, | ||
85 | .end = IRQ_LCD, | ||
86 | .flags = IORESOURCE_IRQ, | ||
87 | } | ||
88 | |||
89 | }; | ||
90 | |||
91 | static u64 s3c_device_lcd_dmamask = 0xffffffffUL; | ||
92 | |||
93 | struct platform_device s3c_device_lcd = { | ||
94 | .name = "s3c2410-lcd", | ||
95 | .id = -1, | ||
96 | .num_resources = ARRAY_SIZE(s3c_lcd_resource), | ||
97 | .resource = s3c_lcd_resource, | ||
98 | .dev = { | ||
99 | .dma_mask = &s3c_device_lcd_dmamask, | ||
100 | .coherent_dma_mask = 0xffffffffUL | ||
101 | } | ||
102 | }; | ||
103 | |||
104 | EXPORT_SYMBOL(s3c_device_lcd); | ||
105 | |||
106 | /* NAND Controller */ | ||
107 | |||
108 | static struct resource s3c_nand_resource[] = { | ||
109 | [0] = { | ||
110 | .start = S3C2410_PA_NAND, | ||
111 | .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND, | ||
112 | .flags = IORESOURCE_MEM, | ||
113 | } | ||
114 | }; | ||
115 | |||
116 | struct platform_device s3c_device_nand = { | ||
117 | .name = "s3c2410-nand", | ||
118 | .id = -1, | ||
119 | .num_resources = ARRAY_SIZE(s3c_nand_resource), | ||
120 | .resource = s3c_nand_resource, | ||
121 | }; | ||
122 | |||
123 | EXPORT_SYMBOL(s3c_device_nand); | ||
124 | |||
125 | /* USB Device (Gadget)*/ | ||
126 | |||
127 | static struct resource s3c_usbgadget_resource[] = { | ||
128 | [0] = { | ||
129 | .start = S3C2410_PA_USBDEV, | ||
130 | .end = S3C2410_PA_USBDEV + S3C24XX_SZ_USBDEV, | ||
131 | .flags = IORESOURCE_MEM, | ||
132 | }, | ||
133 | [1] = { | ||
134 | .start = IRQ_USBD, | ||
135 | .end = IRQ_USBD, | ||
136 | .flags = IORESOURCE_IRQ, | ||
137 | } | ||
138 | |||
139 | }; | ||
140 | |||
141 | struct platform_device s3c_device_usbgadget = { | ||
142 | .name = "s3c2410-usbgadget", | ||
143 | .id = -1, | ||
144 | .num_resources = ARRAY_SIZE(s3c_usbgadget_resource), | ||
145 | .resource = s3c_usbgadget_resource, | ||
146 | }; | ||
147 | |||
148 | EXPORT_SYMBOL(s3c_device_usbgadget); | ||
149 | |||
150 | /* Watchdog */ | ||
151 | |||
152 | static struct resource s3c_wdt_resource[] = { | ||
153 | [0] = { | ||
154 | .start = S3C2410_PA_WATCHDOG, | ||
155 | .end = S3C2410_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG, | ||
156 | .flags = IORESOURCE_MEM, | ||
157 | }, | ||
158 | [1] = { | ||
159 | .start = IRQ_WDT, | ||
160 | .end = IRQ_WDT, | ||
161 | .flags = IORESOURCE_IRQ, | ||
162 | } | ||
163 | |||
164 | }; | ||
165 | |||
166 | struct platform_device s3c_device_wdt = { | ||
167 | .name = "s3c2410-wdt", | ||
168 | .id = -1, | ||
169 | .num_resources = ARRAY_SIZE(s3c_wdt_resource), | ||
170 | .resource = s3c_wdt_resource, | ||
171 | }; | ||
172 | |||
173 | EXPORT_SYMBOL(s3c_device_wdt); | ||
174 | |||
175 | /* I2C */ | ||
176 | |||
177 | static struct resource s3c_i2c_resource[] = { | ||
178 | [0] = { | ||
179 | .start = S3C2410_PA_IIC, | ||
180 | .end = S3C2410_PA_IIC + S3C24XX_SZ_IIC, | ||
181 | .flags = IORESOURCE_MEM, | ||
182 | }, | ||
183 | [1] = { | ||
184 | .start = IRQ_IIC, | ||
185 | .end = IRQ_IIC, | ||
186 | .flags = IORESOURCE_IRQ, | ||
187 | } | ||
188 | |||
189 | }; | ||
190 | |||
191 | struct platform_device s3c_device_i2c = { | ||
192 | .name = "s3c2410-i2c", | ||
193 | .id = -1, | ||
194 | .num_resources = ARRAY_SIZE(s3c_i2c_resource), | ||
195 | .resource = s3c_i2c_resource, | ||
196 | }; | ||
197 | |||
198 | EXPORT_SYMBOL(s3c_device_i2c); | ||
199 | |||
200 | /* IIS */ | ||
201 | |||
202 | static struct resource s3c_iis_resource[] = { | ||
203 | [0] = { | ||
204 | .start = S3C2410_PA_IIS, | ||
205 | .end = S3C2410_PA_IIS + S3C24XX_SZ_IIS, | ||
206 | .flags = IORESOURCE_MEM, | ||
207 | } | ||
208 | }; | ||
209 | |||
210 | static u64 s3c_device_iis_dmamask = 0xffffffffUL; | ||
211 | |||
212 | struct platform_device s3c_device_iis = { | ||
213 | .name = "s3c2410-iis", | ||
214 | .id = -1, | ||
215 | .num_resources = ARRAY_SIZE(s3c_iis_resource), | ||
216 | .resource = s3c_iis_resource, | ||
217 | .dev = { | ||
218 | .dma_mask = &s3c_device_iis_dmamask, | ||
219 | .coherent_dma_mask = 0xffffffffUL | ||
220 | } | ||
221 | }; | ||
222 | |||
223 | EXPORT_SYMBOL(s3c_device_iis); | ||
224 | |||
225 | /* RTC */ | ||
226 | |||
227 | static struct resource s3c_rtc_resource[] = { | ||
228 | [0] = { | ||
229 | .start = S3C2410_PA_RTC, | ||
230 | .end = S3C2410_PA_RTC + 0xff, | ||
231 | .flags = IORESOURCE_MEM, | ||
232 | }, | ||
233 | [1] = { | ||
234 | .start = IRQ_RTC, | ||
235 | .end = IRQ_RTC, | ||
236 | .flags = IORESOURCE_IRQ, | ||
237 | }, | ||
238 | [2] = { | ||
239 | .start = IRQ_TICK, | ||
240 | .end = IRQ_TICK, | ||
241 | .flags = IORESOURCE_IRQ | ||
242 | } | ||
243 | }; | ||
244 | |||
245 | struct platform_device s3c_device_rtc = { | ||
246 | .name = "s3c2410-rtc", | ||
247 | .id = -1, | ||
248 | .num_resources = ARRAY_SIZE(s3c_rtc_resource), | ||
249 | .resource = s3c_rtc_resource, | ||
250 | }; | ||
251 | |||
252 | EXPORT_SYMBOL(s3c_device_rtc); | ||
253 | |||
254 | /* ADC */ | ||
255 | |||
256 | static struct resource s3c_adc_resource[] = { | ||
257 | [0] = { | ||
258 | .start = S3C2410_PA_ADC, | ||
259 | .end = S3C2410_PA_ADC + S3C24XX_SZ_ADC, | ||
260 | .flags = IORESOURCE_MEM, | ||
261 | }, | ||
262 | [1] = { | ||
263 | .start = IRQ_TC, | ||
264 | .end = IRQ_ADC, | ||
265 | .flags = IORESOURCE_IRQ, | ||
266 | } | ||
267 | |||
268 | }; | ||
269 | |||
270 | struct platform_device s3c_device_adc = { | ||
271 | .name = "s3c2410-adc", | ||
272 | .id = -1, | ||
273 | .num_resources = ARRAY_SIZE(s3c_adc_resource), | ||
274 | .resource = s3c_adc_resource, | ||
275 | }; | ||
276 | |||
277 | /* SDI */ | ||
278 | |||
279 | static struct resource s3c_sdi_resource[] = { | ||
280 | [0] = { | ||
281 | .start = S3C2410_PA_SDI, | ||
282 | .end = S3C2410_PA_SDI + S3C24XX_SZ_SDI, | ||
283 | .flags = IORESOURCE_MEM, | ||
284 | }, | ||
285 | [1] = { | ||
286 | .start = IRQ_SDI, | ||
287 | .end = IRQ_SDI, | ||
288 | .flags = IORESOURCE_IRQ, | ||
289 | } | ||
290 | |||
291 | }; | ||
292 | |||
293 | struct platform_device s3c_device_sdi = { | ||
294 | .name = "s3c2410-sdi", | ||
295 | .id = -1, | ||
296 | .num_resources = ARRAY_SIZE(s3c_sdi_resource), | ||
297 | .resource = s3c_sdi_resource, | ||
298 | }; | ||
299 | |||
300 | EXPORT_SYMBOL(s3c_device_sdi); | ||
301 | |||
302 | /* SPI (0) */ | ||
303 | |||
304 | static struct resource s3c_spi0_resource[] = { | ||
305 | [0] = { | ||
306 | .start = S3C2410_PA_SPI, | ||
307 | .end = S3C2410_PA_SPI + 0x1f, | ||
308 | .flags = IORESOURCE_MEM, | ||
309 | }, | ||
310 | [1] = { | ||
311 | .start = IRQ_SPI0, | ||
312 | .end = IRQ_SPI0, | ||
313 | .flags = IORESOURCE_IRQ, | ||
314 | } | ||
315 | |||
316 | }; | ||
317 | |||
318 | struct platform_device s3c_device_spi0 = { | ||
319 | .name = "s3c2410-spi", | ||
320 | .id = 0, | ||
321 | .num_resources = ARRAY_SIZE(s3c_spi0_resource), | ||
322 | .resource = s3c_spi0_resource, | ||
323 | }; | ||
324 | |||
325 | EXPORT_SYMBOL(s3c_device_spi0); | ||
326 | |||
327 | /* SPI (1) */ | ||
328 | |||
329 | static struct resource s3c_spi1_resource[] = { | ||
330 | [0] = { | ||
331 | .start = S3C2410_PA_SPI + 0x20, | ||
332 | .end = S3C2410_PA_SPI + 0x20 + 0x1f, | ||
333 | .flags = IORESOURCE_MEM, | ||
334 | }, | ||
335 | [1] = { | ||
336 | .start = IRQ_SPI1, | ||
337 | .end = IRQ_SPI1, | ||
338 | .flags = IORESOURCE_IRQ, | ||
339 | } | ||
340 | |||
341 | }; | ||
342 | |||
343 | struct platform_device s3c_device_spi1 = { | ||
344 | .name = "s3c2410-spi", | ||
345 | .id = 1, | ||
346 | .num_resources = ARRAY_SIZE(s3c_spi1_resource), | ||
347 | .resource = s3c_spi1_resource, | ||
348 | }; | ||
349 | |||
350 | EXPORT_SYMBOL(s3c_device_spi1); | ||
351 | |||
352 | /* pwm timer blocks */ | ||
353 | |||
354 | static struct resource s3c_timer0_resource[] = { | ||
355 | [0] = { | ||
356 | .start = S3C2410_PA_TIMER + 0x0C, | ||
357 | .end = S3C2410_PA_TIMER + 0x0C + 0xB, | ||
358 | .flags = IORESOURCE_MEM, | ||
359 | }, | ||
360 | [1] = { | ||
361 | .start = IRQ_TIMER0, | ||
362 | .end = IRQ_TIMER0, | ||
363 | .flags = IORESOURCE_IRQ, | ||
364 | } | ||
365 | |||
366 | }; | ||
367 | |||
368 | struct platform_device s3c_device_timer0 = { | ||
369 | .name = "s3c2410-timer", | ||
370 | .id = 0, | ||
371 | .num_resources = ARRAY_SIZE(s3c_timer0_resource), | ||
372 | .resource = s3c_timer0_resource, | ||
373 | }; | ||
374 | |||
375 | EXPORT_SYMBOL(s3c_device_timer0); | ||
376 | |||
377 | /* timer 1 */ | ||
378 | |||
379 | static struct resource s3c_timer1_resource[] = { | ||
380 | [0] = { | ||
381 | .start = S3C2410_PA_TIMER + 0x18, | ||
382 | .end = S3C2410_PA_TIMER + 0x23, | ||
383 | .flags = IORESOURCE_MEM, | ||
384 | }, | ||
385 | [1] = { | ||
386 | .start = IRQ_TIMER1, | ||
387 | .end = IRQ_TIMER1, | ||
388 | .flags = IORESOURCE_IRQ, | ||
389 | } | ||
390 | |||
391 | }; | ||
392 | |||
393 | struct platform_device s3c_device_timer1 = { | ||
394 | .name = "s3c2410-timer", | ||
395 | .id = 1, | ||
396 | .num_resources = ARRAY_SIZE(s3c_timer1_resource), | ||
397 | .resource = s3c_timer1_resource, | ||
398 | }; | ||
399 | |||
400 | EXPORT_SYMBOL(s3c_device_timer1); | ||
401 | |||
402 | /* timer 2 */ | ||
403 | |||
404 | static struct resource s3c_timer2_resource[] = { | ||
405 | [0] = { | ||
406 | .start = S3C2410_PA_TIMER + 0x24, | ||
407 | .end = S3C2410_PA_TIMER + 0x2F, | ||
408 | .flags = IORESOURCE_MEM, | ||
409 | }, | ||
410 | [1] = { | ||
411 | .start = IRQ_TIMER2, | ||
412 | .end = IRQ_TIMER2, | ||
413 | .flags = IORESOURCE_IRQ, | ||
414 | } | ||
415 | |||
416 | }; | ||
417 | |||
418 | struct platform_device s3c_device_timer2 = { | ||
419 | .name = "s3c2410-timer", | ||
420 | .id = 2, | ||
421 | .num_resources = ARRAY_SIZE(s3c_timer2_resource), | ||
422 | .resource = s3c_timer2_resource, | ||
423 | }; | ||
424 | |||
425 | EXPORT_SYMBOL(s3c_device_timer2); | ||
426 | |||
427 | /* timer 3 */ | ||
428 | |||
429 | static struct resource s3c_timer3_resource[] = { | ||
430 | [0] = { | ||
431 | .start = S3C2410_PA_TIMER + 0x30, | ||
432 | .end = S3C2410_PA_TIMER + 0x3B, | ||
433 | .flags = IORESOURCE_MEM, | ||
434 | }, | ||
435 | [1] = { | ||
436 | .start = IRQ_TIMER3, | ||
437 | .end = IRQ_TIMER3, | ||
438 | .flags = IORESOURCE_IRQ, | ||
439 | } | ||
440 | |||
441 | }; | ||
442 | |||
443 | struct platform_device s3c_device_timer3 = { | ||
444 | .name = "s3c2410-timer", | ||
445 | .id = 3, | ||
446 | .num_resources = ARRAY_SIZE(s3c_timer3_resource), | ||
447 | .resource = s3c_timer3_resource, | ||
448 | }; | ||
449 | |||
450 | EXPORT_SYMBOL(s3c_device_timer3); | ||
451 | |||
452 | #ifdef CONFIG_CPU_S3C2440 | ||
453 | |||
454 | /* Camif Controller */ | ||
455 | |||
456 | static struct resource s3c_camif_resource[] = { | ||
457 | [0] = { | ||
458 | .start = S3C2440_PA_CAMIF, | ||
459 | .end = S3C2440_PA_CAMIF + S3C2440_SZ_CAMIF, | ||
460 | .flags = IORESOURCE_MEM, | ||
461 | }, | ||
462 | [1] = { | ||
463 | .start = IRQ_CAM, | ||
464 | .end = IRQ_CAM, | ||
465 | .flags = IORESOURCE_IRQ, | ||
466 | } | ||
467 | |||
468 | }; | ||
469 | |||
470 | static u64 s3c_device_camif_dmamask = 0xffffffffUL; | ||
471 | |||
472 | struct platform_device s3c_device_camif = { | ||
473 | .name = "s3c2440-camif", | ||
474 | .id = -1, | ||
475 | .num_resources = ARRAY_SIZE(s3c_camif_resource), | ||
476 | .resource = s3c_camif_resource, | ||
477 | .dev = { | ||
478 | .dma_mask = &s3c_device_camif_dmamask, | ||
479 | .coherent_dma_mask = 0xffffffffUL | ||
480 | } | ||
481 | }; | ||
482 | |||
483 | EXPORT_SYMBOL(s3c_device_camif); | ||
484 | |||
485 | #endif // CONFIG_CPU_S32440 | ||
diff --git a/arch/arm/mach-s3c2410/devs.h b/arch/arm/mach-s3c2410/devs.h new file mode 100644 index 000000000000..d6328f96728b --- /dev/null +++ b/arch/arm/mach-s3c2410/devs.h | |||
@@ -0,0 +1,48 @@ | |||
1 | /* arch/arm/mach-s3c2410/devs.h | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Header file for s3c2410 standard platform devices | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 18-Aug-2004 BJD Created initial version | ||
14 | * 27-Aug-2004 BJD Added timers 0 through 3 | ||
15 | * 10-Feb-2005 BJD Added camera from guillaume.gourat@nexvision.tv | ||
16 | */ | ||
17 | #include <linux/config.h> | ||
18 | |||
19 | extern struct platform_device *s3c24xx_uart_devs[]; | ||
20 | |||
21 | extern struct platform_device s3c_device_usb; | ||
22 | extern struct platform_device s3c_device_lcd; | ||
23 | extern struct platform_device s3c_device_wdt; | ||
24 | extern struct platform_device s3c_device_i2c; | ||
25 | extern struct platform_device s3c_device_iis; | ||
26 | extern struct platform_device s3c_device_rtc; | ||
27 | extern struct platform_device s3c_device_adc; | ||
28 | extern struct platform_device s3c_device_sdi; | ||
29 | |||
30 | extern struct platform_device s3c_device_spi0; | ||
31 | extern struct platform_device s3c_device_spi1; | ||
32 | |||
33 | extern struct platform_device s3c_device_nand; | ||
34 | |||
35 | extern struct platform_device s3c_device_timer0; | ||
36 | extern struct platform_device s3c_device_timer1; | ||
37 | extern struct platform_device s3c_device_timer2; | ||
38 | extern struct platform_device s3c_device_timer3; | ||
39 | |||
40 | extern struct platform_device s3c_device_usbgadget; | ||
41 | |||
42 | /* s3c2440 specific devices */ | ||
43 | |||
44 | #ifdef CONFIG_CPU_S3C2440 | ||
45 | |||
46 | extern struct platform_device s3c_device_camif; | ||
47 | |||
48 | #endif | ||
diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c new file mode 100644 index 000000000000..bc229fab86d4 --- /dev/null +++ b/arch/arm/mach-s3c2410/dma.c | |||
@@ -0,0 +1,1210 @@ | |||
1 | /* linux/arch/arm/mach-bast/dma.c | ||
2 | * | ||
3 | * (c) 2003-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C2410 DMA core | ||
7 | * | ||
8 | * http://www.simtec.co.uk/products/EB2410ITX/ | ||
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 | * Changelog: | ||
15 | * 27-Feb-2005 BJD Added kmem cache for dma descriptors | ||
16 | * 18-Nov-2004 BJD Removed error for loading onto stopped channel | ||
17 | * 10-Nov-2004 BJD Ensure all external symbols exported for modules | ||
18 | * 10-Nov-2004 BJD Use sys_device and sysdev_class for power management | ||
19 | * 08-Aug-2004 BJD Apply rmk's suggestions | ||
20 | * 21-Jul-2004 BJD Ported to linux 2.6 | ||
21 | * 12-Jul-2004 BJD Finished re-write and change of API | ||
22 | * 06-Jul-2004 BJD Rewrote dma code to try and cope with various problems | ||
23 | * 23-May-2003 BJD Created file | ||
24 | * 19-Aug-2003 BJD Cleanup, header fix, added URL | ||
25 | * | ||
26 | * This file is based on the Sangwook Lee/Samsung patches, re-written due | ||
27 | * to various ommisions from the code (such as flexible dma configuration) | ||
28 | * for use with the BAST system board. | ||
29 | * | ||
30 | * The re-write is pretty much complete, and should be good enough for any | ||
31 | * possible DMA function | ||
32 | */ | ||
33 | |||
34 | #include <linux/config.h> | ||
35 | |||
36 | #ifdef CONFIG_S3C2410_DMA_DEBUG | ||
37 | #define DEBUG | ||
38 | #endif | ||
39 | |||
40 | #include <linux/module.h> | ||
41 | #include <linux/init.h> | ||
42 | #include <linux/sched.h> | ||
43 | #include <linux/spinlock.h> | ||
44 | #include <linux/interrupt.h> | ||
45 | #include <linux/sysdev.h> | ||
46 | #include <linux/slab.h> | ||
47 | #include <linux/errno.h> | ||
48 | #include <linux/delay.h> | ||
49 | |||
50 | #include <asm/system.h> | ||
51 | #include <asm/irq.h> | ||
52 | #include <asm/hardware.h> | ||
53 | #include <asm/io.h> | ||
54 | #include <asm/dma.h> | ||
55 | |||
56 | #include <asm/mach/dma.h> | ||
57 | #include <asm/arch/map.h> | ||
58 | |||
59 | /* io map for dma */ | ||
60 | static void __iomem *dma_base; | ||
61 | static kmem_cache_t *dma_kmem; | ||
62 | |||
63 | /* dma channel state information */ | ||
64 | s3c2410_dma_chan_t s3c2410_chans[S3C2410_DMA_CHANNELS]; | ||
65 | |||
66 | /* debugging functions */ | ||
67 | |||
68 | #define BUF_MAGIC (0xcafebabe) | ||
69 | |||
70 | #define dmawarn(fmt...) printk(KERN_DEBUG fmt) | ||
71 | |||
72 | #define dma_regaddr(chan, reg) ((chan)->regs + (reg)) | ||
73 | |||
74 | #if 1 | ||
75 | #define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg)) | ||
76 | #else | ||
77 | static inline void | ||
78 | dma_wrreg(s3c2410_dma_chan_t *chan, int reg, unsigned long val) | ||
79 | { | ||
80 | pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg); | ||
81 | writel(val, dma_regaddr(chan, reg)); | ||
82 | } | ||
83 | |||
84 | #endif | ||
85 | |||
86 | #define dma_rdreg(chan, reg) readl((chan)->regs + (reg)) | ||
87 | |||
88 | /* captured register state for debug */ | ||
89 | |||
90 | struct s3c2410_dma_regstate { | ||
91 | unsigned long dcsrc; | ||
92 | unsigned long disrc; | ||
93 | unsigned long dstat; | ||
94 | unsigned long dcon; | ||
95 | unsigned long dmsktrig; | ||
96 | }; | ||
97 | |||
98 | #ifdef CONFIG_S3C2410_DMA_DEBUG | ||
99 | |||
100 | /* dmadbg_showregs | ||
101 | * | ||
102 | * simple debug routine to print the current state of the dma registers | ||
103 | */ | ||
104 | |||
105 | static void | ||
106 | dmadbg_capture(s3c2410_dma_chan_t *chan, struct s3c2410_dma_regstate *regs) | ||
107 | { | ||
108 | regs->dcsrc = dma_rdreg(chan, S3C2410_DMA_DCSRC); | ||
109 | regs->disrc = dma_rdreg(chan, S3C2410_DMA_DISRC); | ||
110 | regs->dstat = dma_rdreg(chan, S3C2410_DMA_DSTAT); | ||
111 | regs->dcon = dma_rdreg(chan, S3C2410_DMA_DCON); | ||
112 | regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | ||
113 | } | ||
114 | |||
115 | static void | ||
116 | dmadbg_showregs(const char *fname, int line, s3c2410_dma_chan_t *chan, | ||
117 | struct s3c2410_dma_regstate *regs) | ||
118 | { | ||
119 | printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n", | ||
120 | chan->number, fname, line, | ||
121 | regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig, | ||
122 | regs->dcon); | ||
123 | } | ||
124 | |||
125 | static void | ||
126 | dmadbg_showchan(const char *fname, int line, s3c2410_dma_chan_t *chan) | ||
127 | { | ||
128 | struct s3c2410_dma_regstate state; | ||
129 | |||
130 | dmadbg_capture(chan, &state); | ||
131 | |||
132 | printk(KERN_DEBUG "dma%d: %s:%d: ls=%d, cur=%p, %p %p\n", | ||
133 | chan->number, fname, line, chan->load_state, | ||
134 | chan->curr, chan->next, chan->end); | ||
135 | |||
136 | dmadbg_showregs(fname, line, chan, &state); | ||
137 | } | ||
138 | |||
139 | #define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan)) | ||
140 | #define dbg_showchan(chan) dmadbg_showchan(__FUNCTION__, __LINE__, (chan)) | ||
141 | #else | ||
142 | #define dbg_showregs(chan) do { } while(0) | ||
143 | #define dbg_showchan(chan) do { } while(0) | ||
144 | #endif /* CONFIG_S3C2410_DMA_DEBUG */ | ||
145 | |||
146 | #define check_channel(chan) \ | ||
147 | do { if ((chan) >= S3C2410_DMA_CHANNELS) { \ | ||
148 | printk(KERN_ERR "%s: invalid channel %d\n", __FUNCTION__, (chan)); \ | ||
149 | return -EINVAL; \ | ||
150 | } } while(0) | ||
151 | |||
152 | |||
153 | /* s3c2410_dma_stats_timeout | ||
154 | * | ||
155 | * Update DMA stats from timeout info | ||
156 | */ | ||
157 | |||
158 | static void | ||
159 | s3c2410_dma_stats_timeout(s3c2410_dma_stats_t *stats, int val) | ||
160 | { | ||
161 | if (stats == NULL) | ||
162 | return; | ||
163 | |||
164 | if (val > stats->timeout_longest) | ||
165 | stats->timeout_longest = val; | ||
166 | if (val < stats->timeout_shortest) | ||
167 | stats->timeout_shortest = val; | ||
168 | |||
169 | stats->timeout_avg += val; | ||
170 | } | ||
171 | |||
172 | /* s3c2410_dma_waitforload | ||
173 | * | ||
174 | * wait for the DMA engine to load a buffer, and update the state accordingly | ||
175 | */ | ||
176 | |||
177 | static int | ||
178 | s3c2410_dma_waitforload(s3c2410_dma_chan_t *chan, int line) | ||
179 | { | ||
180 | int timeout = chan->load_timeout; | ||
181 | int took; | ||
182 | |||
183 | if (chan->load_state != S3C2410_DMALOAD_1LOADED) { | ||
184 | printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line); | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | if (chan->stats != NULL) | ||
189 | chan->stats->loads++; | ||
190 | |||
191 | while (--timeout > 0) { | ||
192 | if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) { | ||
193 | took = chan->load_timeout - timeout; | ||
194 | |||
195 | s3c2410_dma_stats_timeout(chan->stats, took); | ||
196 | |||
197 | switch (chan->load_state) { | ||
198 | case S3C2410_DMALOAD_1LOADED: | ||
199 | chan->load_state = S3C2410_DMALOAD_1RUNNING; | ||
200 | break; | ||
201 | |||
202 | default: | ||
203 | printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state); | ||
204 | } | ||
205 | |||
206 | return 1; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | if (chan->stats != NULL) { | ||
211 | chan->stats->timeout_failed++; | ||
212 | } | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | |||
218 | |||
219 | /* s3c2410_dma_loadbuffer | ||
220 | * | ||
221 | * load a buffer, and update the channel state | ||
222 | */ | ||
223 | |||
224 | static inline int | ||
225 | s3c2410_dma_loadbuffer(s3c2410_dma_chan_t *chan, | ||
226 | s3c2410_dma_buf_t *buf) | ||
227 | { | ||
228 | unsigned long reload; | ||
229 | |||
230 | pr_debug("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n", | ||
231 | buf, (unsigned long)buf->data, buf->size); | ||
232 | |||
233 | if (buf == NULL) { | ||
234 | dmawarn("buffer is NULL\n"); | ||
235 | return -EINVAL; | ||
236 | } | ||
237 | |||
238 | /* check the state of the channel before we do anything */ | ||
239 | |||
240 | if (chan->load_state == S3C2410_DMALOAD_1LOADED) { | ||
241 | dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n"); | ||
242 | } | ||
243 | |||
244 | if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) { | ||
245 | dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n"); | ||
246 | } | ||
247 | |||
248 | /* it would seem sensible if we are the last buffer to not bother | ||
249 | * with the auto-reload bit, so that the DMA engine will not try | ||
250 | * and load another transfer after this one has finished... | ||
251 | */ | ||
252 | if (chan->load_state == S3C2410_DMALOAD_NONE) { | ||
253 | pr_debug("load_state is none, checking for noreload (next=%p)\n", | ||
254 | buf->next); | ||
255 | reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0; | ||
256 | } else { | ||
257 | pr_debug("load_state is %d => autoreload\n", chan->load_state); | ||
258 | reload = S3C2410_DCON_AUTORELOAD; | ||
259 | } | ||
260 | |||
261 | writel(buf->data, chan->addr_reg); | ||
262 | |||
263 | dma_wrreg(chan, S3C2410_DMA_DCON, | ||
264 | chan->dcon | reload | (buf->size/chan->xfer_unit)); | ||
265 | |||
266 | chan->next = buf->next; | ||
267 | |||
268 | /* update the state of the channel */ | ||
269 | |||
270 | switch (chan->load_state) { | ||
271 | case S3C2410_DMALOAD_NONE: | ||
272 | chan->load_state = S3C2410_DMALOAD_1LOADED; | ||
273 | break; | ||
274 | |||
275 | case S3C2410_DMALOAD_1RUNNING: | ||
276 | chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING; | ||
277 | break; | ||
278 | |||
279 | default: | ||
280 | dmawarn("dmaload: unknown state %d in loadbuffer\n", | ||
281 | chan->load_state); | ||
282 | break; | ||
283 | } | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | /* s3c2410_dma_call_op | ||
289 | * | ||
290 | * small routine to call the op routine with the given op if it has been | ||
291 | * registered | ||
292 | */ | ||
293 | |||
294 | static void | ||
295 | s3c2410_dma_call_op(s3c2410_dma_chan_t *chan, s3c2410_chan_op_t op) | ||
296 | { | ||
297 | if (chan->op_fn != NULL) { | ||
298 | (chan->op_fn)(chan, op); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | /* s3c2410_dma_buffdone | ||
303 | * | ||
304 | * small wrapper to check if callback routine needs to be called, and | ||
305 | * if so, call it | ||
306 | */ | ||
307 | |||
308 | static inline void | ||
309 | s3c2410_dma_buffdone(s3c2410_dma_chan_t *chan, s3c2410_dma_buf_t *buf, | ||
310 | s3c2410_dma_buffresult_t result) | ||
311 | { | ||
312 | pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n", | ||
313 | chan->callback_fn, buf, buf->id, buf->size, result); | ||
314 | |||
315 | if (chan->callback_fn != NULL) { | ||
316 | (chan->callback_fn)(chan, buf->id, buf->size, result); | ||
317 | } | ||
318 | } | ||
319 | |||
320 | /* s3c2410_dma_start | ||
321 | * | ||
322 | * start a dma channel going | ||
323 | */ | ||
324 | |||
325 | static int s3c2410_dma_start(s3c2410_dma_chan_t *chan) | ||
326 | { | ||
327 | unsigned long tmp; | ||
328 | unsigned long flags; | ||
329 | |||
330 | pr_debug("s3c2410_start_dma: channel=%d\n", chan->number); | ||
331 | |||
332 | local_irq_save(flags); | ||
333 | |||
334 | if (chan->state == S3C2410_DMA_RUNNING) { | ||
335 | pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state); | ||
336 | local_irq_restore(flags); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | chan->state = S3C2410_DMA_RUNNING; | ||
341 | |||
342 | /* check wether there is anything to load, and if not, see | ||
343 | * if we can find anything to load | ||
344 | */ | ||
345 | |||
346 | if (chan->load_state == S3C2410_DMALOAD_NONE) { | ||
347 | if (chan->next == NULL) { | ||
348 | printk(KERN_ERR "dma%d: channel has nothing loaded\n", | ||
349 | chan->number); | ||
350 | chan->state = S3C2410_DMA_IDLE; | ||
351 | local_irq_restore(flags); | ||
352 | return -EINVAL; | ||
353 | } | ||
354 | |||
355 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
356 | } | ||
357 | |||
358 | dbg_showchan(chan); | ||
359 | |||
360 | /* enable the channel */ | ||
361 | |||
362 | if (!chan->irq_enabled) { | ||
363 | enable_irq(chan->irq); | ||
364 | chan->irq_enabled = 1; | ||
365 | } | ||
366 | |||
367 | /* start the channel going */ | ||
368 | |||
369 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | ||
370 | tmp &= ~S3C2410_DMASKTRIG_STOP; | ||
371 | tmp |= S3C2410_DMASKTRIG_ON; | ||
372 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); | ||
373 | |||
374 | pr_debug("wrote %08lx to DMASKTRIG\n", tmp); | ||
375 | |||
376 | #if 0 | ||
377 | /* the dma buffer loads should take care of clearing the AUTO | ||
378 | * reloading feature */ | ||
379 | tmp = dma_rdreg(chan, S3C2410_DMA_DCON); | ||
380 | tmp &= ~S3C2410_DCON_NORELOAD; | ||
381 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); | ||
382 | #endif | ||
383 | |||
384 | s3c2410_dma_call_op(chan, S3C2410_DMAOP_START); | ||
385 | |||
386 | dbg_showchan(chan); | ||
387 | |||
388 | local_irq_restore(flags); | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | /* s3c2410_dma_canload | ||
393 | * | ||
394 | * work out if we can queue another buffer into the DMA engine | ||
395 | */ | ||
396 | |||
397 | static int | ||
398 | s3c2410_dma_canload(s3c2410_dma_chan_t *chan) | ||
399 | { | ||
400 | if (chan->load_state == S3C2410_DMALOAD_NONE || | ||
401 | chan->load_state == S3C2410_DMALOAD_1RUNNING) | ||
402 | return 1; | ||
403 | |||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | |||
408 | /* s3c2410_dma_enqueue | ||
409 | * | ||
410 | * queue an given buffer for dma transfer. | ||
411 | * | ||
412 | * id the device driver's id information for this buffer | ||
413 | * data the physical address of the buffer data | ||
414 | * size the size of the buffer in bytes | ||
415 | * | ||
416 | * If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART | ||
417 | * is checked, and if set, the channel is started. If this flag isn't set, | ||
418 | * then an error will be returned. | ||
419 | * | ||
420 | * It is possible to queue more than one DMA buffer onto a channel at | ||
421 | * once, and the code will deal with the re-loading of the next buffer | ||
422 | * when necessary. | ||
423 | */ | ||
424 | |||
425 | int s3c2410_dma_enqueue(unsigned int channel, void *id, | ||
426 | dma_addr_t data, int size) | ||
427 | { | ||
428 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
429 | s3c2410_dma_buf_t *buf; | ||
430 | unsigned long flags; | ||
431 | |||
432 | check_channel(channel); | ||
433 | |||
434 | pr_debug("%s: id=%p, data=%08x, size=%d\n", | ||
435 | __FUNCTION__, id, (unsigned int)data, size); | ||
436 | |||
437 | buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); | ||
438 | if (buf == NULL) { | ||
439 | pr_debug("%s: out of memory (%d alloc)\n", | ||
440 | __FUNCTION__, sizeof(*buf)); | ||
441 | return -ENOMEM; | ||
442 | } | ||
443 | |||
444 | pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); | ||
445 | |||
446 | //dbg_showchan(chan); | ||
447 | |||
448 | buf->next = NULL; | ||
449 | buf->data = buf->ptr = data; | ||
450 | buf->size = size; | ||
451 | buf->id = id; | ||
452 | buf->magic = BUF_MAGIC; | ||
453 | |||
454 | local_irq_save(flags); | ||
455 | |||
456 | if (chan->curr == NULL) { | ||
457 | /* we've got nothing loaded... */ | ||
458 | pr_debug("%s: buffer %p queued onto empty channel\n", | ||
459 | __FUNCTION__, buf); | ||
460 | |||
461 | chan->curr = buf; | ||
462 | chan->end = buf; | ||
463 | chan->next = NULL; | ||
464 | } else { | ||
465 | pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n", | ||
466 | chan->number, __FUNCTION__, buf); | ||
467 | |||
468 | if (chan->end == NULL) | ||
469 | pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n", | ||
470 | chan->number, __FUNCTION__, chan); | ||
471 | |||
472 | chan->end->next = buf; | ||
473 | chan->end = buf; | ||
474 | } | ||
475 | |||
476 | /* if necessary, update the next buffer field */ | ||
477 | if (chan->next == NULL) | ||
478 | chan->next = buf; | ||
479 | |||
480 | /* check to see if we can load a buffer */ | ||
481 | if (chan->state == S3C2410_DMA_RUNNING) { | ||
482 | if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { | ||
483 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
484 | printk(KERN_ERR "dma%d: loadbuffer:" | ||
485 | "timeout loading buffer\n", | ||
486 | chan->number); | ||
487 | dbg_showchan(chan); | ||
488 | local_irq_restore(flags); | ||
489 | return -EINVAL; | ||
490 | } | ||
491 | } | ||
492 | |||
493 | while (s3c2410_dma_canload(chan) && chan->next != NULL) { | ||
494 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
495 | } | ||
496 | } else if (chan->state == S3C2410_DMA_IDLE) { | ||
497 | if (chan->flags & S3C2410_DMAF_AUTOSTART) { | ||
498 | s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START); | ||
499 | } | ||
500 | } | ||
501 | |||
502 | local_irq_restore(flags); | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | EXPORT_SYMBOL(s3c2410_dma_enqueue); | ||
507 | |||
508 | static inline void | ||
509 | s3c2410_dma_freebuf(s3c2410_dma_buf_t *buf) | ||
510 | { | ||
511 | int magicok = (buf->magic == BUF_MAGIC); | ||
512 | |||
513 | buf->magic = -1; | ||
514 | |||
515 | if (magicok) { | ||
516 | kmem_cache_free(dma_kmem, buf); | ||
517 | } else { | ||
518 | printk("s3c2410_dma_freebuf: buff %p with bad magic\n", buf); | ||
519 | } | ||
520 | } | ||
521 | |||
522 | /* s3c2410_dma_lastxfer | ||
523 | * | ||
524 | * called when the system is out of buffers, to ensure that the channel | ||
525 | * is prepared for shutdown. | ||
526 | */ | ||
527 | |||
528 | static inline void | ||
529 | s3c2410_dma_lastxfer(s3c2410_dma_chan_t *chan) | ||
530 | { | ||
531 | pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", | ||
532 | chan->number, chan->load_state); | ||
533 | |||
534 | switch (chan->load_state) { | ||
535 | case S3C2410_DMALOAD_NONE: | ||
536 | break; | ||
537 | |||
538 | case S3C2410_DMALOAD_1LOADED: | ||
539 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
540 | /* flag error? */ | ||
541 | printk(KERN_ERR "dma%d: timeout waiting for load\n", | ||
542 | chan->number); | ||
543 | return; | ||
544 | } | ||
545 | break; | ||
546 | |||
547 | default: | ||
548 | pr_debug("dma%d: lastxfer: unhandled load_state %d with no next", | ||
549 | chan->number, chan->load_state); | ||
550 | return; | ||
551 | |||
552 | } | ||
553 | |||
554 | /* hopefully this'll shut the damned thing up after the transfer... */ | ||
555 | dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD); | ||
556 | } | ||
557 | |||
558 | |||
559 | #define dmadbg2(x...) | ||
560 | |||
561 | static irqreturn_t | ||
562 | s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) | ||
563 | { | ||
564 | s3c2410_dma_chan_t *chan = (s3c2410_dma_chan_t *)devpw; | ||
565 | s3c2410_dma_buf_t *buf; | ||
566 | |||
567 | buf = chan->curr; | ||
568 | |||
569 | dbg_showchan(chan); | ||
570 | |||
571 | /* modify the channel state */ | ||
572 | |||
573 | switch (chan->load_state) { | ||
574 | case S3C2410_DMALOAD_1RUNNING: | ||
575 | /* TODO - if we are running only one buffer, we probably | ||
576 | * want to reload here, and then worry about the buffer | ||
577 | * callback */ | ||
578 | |||
579 | chan->load_state = S3C2410_DMALOAD_NONE; | ||
580 | break; | ||
581 | |||
582 | case S3C2410_DMALOAD_1LOADED: | ||
583 | /* iirc, we should go back to NONE loaded here, we | ||
584 | * had a buffer, and it was never verified as being | ||
585 | * loaded. | ||
586 | */ | ||
587 | |||
588 | chan->load_state = S3C2410_DMALOAD_NONE; | ||
589 | break; | ||
590 | |||
591 | case S3C2410_DMALOAD_1LOADED_1RUNNING: | ||
592 | /* we'll worry about checking to see if another buffer is | ||
593 | * ready after we've called back the owner. This should | ||
594 | * ensure we do not wait around too long for the DMA | ||
595 | * engine to start the next transfer | ||
596 | */ | ||
597 | |||
598 | chan->load_state = S3C2410_DMALOAD_1LOADED; | ||
599 | break; | ||
600 | |||
601 | case S3C2410_DMALOAD_NONE: | ||
602 | printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n", | ||
603 | chan->number); | ||
604 | break; | ||
605 | |||
606 | default: | ||
607 | printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n", | ||
608 | chan->number, chan->load_state); | ||
609 | break; | ||
610 | } | ||
611 | |||
612 | if (buf != NULL) { | ||
613 | /* update the chain to make sure that if we load any more | ||
614 | * buffers when we call the callback function, things should | ||
615 | * work properly */ | ||
616 | |||
617 | chan->curr = buf->next; | ||
618 | buf->next = NULL; | ||
619 | |||
620 | if (buf->magic != BUF_MAGIC) { | ||
621 | printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n", | ||
622 | chan->number, __FUNCTION__, buf); | ||
623 | return IRQ_HANDLED; | ||
624 | } | ||
625 | |||
626 | s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK); | ||
627 | |||
628 | /* free resouces */ | ||
629 | s3c2410_dma_freebuf(buf); | ||
630 | } else { | ||
631 | } | ||
632 | |||
633 | if (chan->next != NULL) { | ||
634 | unsigned long flags; | ||
635 | |||
636 | switch (chan->load_state) { | ||
637 | case S3C2410_DMALOAD_1RUNNING: | ||
638 | /* don't need to do anything for this state */ | ||
639 | break; | ||
640 | |||
641 | case S3C2410_DMALOAD_NONE: | ||
642 | /* can load buffer immediately */ | ||
643 | break; | ||
644 | |||
645 | case S3C2410_DMALOAD_1LOADED: | ||
646 | if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { | ||
647 | /* flag error? */ | ||
648 | printk(KERN_ERR "dma%d: timeout waiting for load\n", | ||
649 | chan->number); | ||
650 | return IRQ_HANDLED; | ||
651 | } | ||
652 | |||
653 | break; | ||
654 | |||
655 | case S3C2410_DMALOAD_1LOADED_1RUNNING: | ||
656 | goto no_load; | ||
657 | |||
658 | default: | ||
659 | printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n", | ||
660 | chan->number, chan->load_state); | ||
661 | return IRQ_HANDLED; | ||
662 | } | ||
663 | |||
664 | local_irq_save(flags); | ||
665 | s3c2410_dma_loadbuffer(chan, chan->next); | ||
666 | local_irq_restore(flags); | ||
667 | } else { | ||
668 | s3c2410_dma_lastxfer(chan); | ||
669 | |||
670 | /* see if we can stop this channel.. */ | ||
671 | if (chan->load_state == S3C2410_DMALOAD_NONE) { | ||
672 | pr_debug("dma%d: end of transfer, stopping channel (%ld)\n", | ||
673 | chan->number, jiffies); | ||
674 | s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); | ||
675 | } | ||
676 | } | ||
677 | |||
678 | no_load: | ||
679 | return IRQ_HANDLED; | ||
680 | } | ||
681 | |||
682 | |||
683 | |||
684 | /* s3c2410_request_dma | ||
685 | * | ||
686 | * get control of an dma channel | ||
687 | */ | ||
688 | |||
689 | int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client, | ||
690 | void *dev) | ||
691 | { | ||
692 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
693 | unsigned long flags; | ||
694 | int err; | ||
695 | |||
696 | pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", | ||
697 | channel, client->name, dev); | ||
698 | |||
699 | check_channel(channel); | ||
700 | |||
701 | local_irq_save(flags); | ||
702 | |||
703 | dbg_showchan(chan); | ||
704 | |||
705 | if (chan->in_use) { | ||
706 | if (client != chan->client) { | ||
707 | printk(KERN_ERR "dma%d: already in use\n", channel); | ||
708 | local_irq_restore(flags); | ||
709 | return -EBUSY; | ||
710 | } else { | ||
711 | printk(KERN_ERR "dma%d: client already has channel\n", channel); | ||
712 | } | ||
713 | } | ||
714 | |||
715 | chan->client = client; | ||
716 | chan->in_use = 1; | ||
717 | |||
718 | if (!chan->irq_claimed) { | ||
719 | pr_debug("dma%d: %s : requesting irq %d\n", | ||
720 | channel, __FUNCTION__, chan->irq); | ||
721 | |||
722 | err = request_irq(chan->irq, s3c2410_dma_irq, SA_INTERRUPT, | ||
723 | client->name, (void *)chan); | ||
724 | |||
725 | if (err) { | ||
726 | chan->in_use = 0; | ||
727 | local_irq_restore(flags); | ||
728 | |||
729 | printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", | ||
730 | client->name, chan->irq, chan->number); | ||
731 | return err; | ||
732 | } | ||
733 | |||
734 | chan->irq_claimed = 1; | ||
735 | chan->irq_enabled = 1; | ||
736 | } | ||
737 | |||
738 | local_irq_restore(flags); | ||
739 | |||
740 | /* need to setup */ | ||
741 | |||
742 | pr_debug("%s: channel initialised, %p\n", __FUNCTION__, chan); | ||
743 | |||
744 | return 0; | ||
745 | } | ||
746 | |||
747 | EXPORT_SYMBOL(s3c2410_dma_request); | ||
748 | |||
749 | /* s3c2410_dma_free | ||
750 | * | ||
751 | * release the given channel back to the system, will stop and flush | ||
752 | * any outstanding transfers, and ensure the channel is ready for the | ||
753 | * next claimant. | ||
754 | * | ||
755 | * Note, although a warning is currently printed if the freeing client | ||
756 | * info is not the same as the registrant's client info, the free is still | ||
757 | * allowed to go through. | ||
758 | */ | ||
759 | |||
760 | int s3c2410_dma_free(dmach_t channel, s3c2410_dma_client_t *client) | ||
761 | { | ||
762 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
763 | unsigned long flags; | ||
764 | |||
765 | check_channel(channel); | ||
766 | |||
767 | local_irq_save(flags); | ||
768 | |||
769 | |||
770 | if (chan->client != client) { | ||
771 | printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n", | ||
772 | channel, chan->client, client); | ||
773 | } | ||
774 | |||
775 | /* sort out stopping and freeing the channel */ | ||
776 | |||
777 | if (chan->state != S3C2410_DMA_IDLE) { | ||
778 | pr_debug("%s: need to stop dma channel %p\n", | ||
779 | __FUNCTION__, chan); | ||
780 | |||
781 | /* possibly flush the channel */ | ||
782 | s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP); | ||
783 | } | ||
784 | |||
785 | chan->client = NULL; | ||
786 | chan->in_use = 0; | ||
787 | |||
788 | local_irq_restore(flags); | ||
789 | |||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | EXPORT_SYMBOL(s3c2410_dma_free); | ||
794 | |||
795 | static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) | ||
796 | { | ||
797 | unsigned long tmp; | ||
798 | unsigned long flags; | ||
799 | |||
800 | pr_debug("%s:\n", __FUNCTION__); | ||
801 | |||
802 | dbg_showchan(chan); | ||
803 | |||
804 | local_irq_save(flags); | ||
805 | |||
806 | s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP); | ||
807 | |||
808 | tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); | ||
809 | tmp |= S3C2410_DMASKTRIG_STOP; | ||
810 | dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); | ||
811 | |||
812 | #if 0 | ||
813 | /* should also clear interrupts, according to WinCE BSP */ | ||
814 | tmp = dma_rdreg(chan, S3C2410_DMA_DCON); | ||
815 | tmp |= S3C2410_DCON_NORELOAD; | ||
816 | dma_wrreg(chan, S3C2410_DMA_DCON, tmp); | ||
817 | #endif | ||
818 | |||
819 | chan->state = S3C2410_DMA_IDLE; | ||
820 | chan->load_state = S3C2410_DMALOAD_NONE; | ||
821 | |||
822 | local_irq_restore(flags); | ||
823 | |||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | /* s3c2410_dma_flush | ||
828 | * | ||
829 | * stop the channel, and remove all current and pending transfers | ||
830 | */ | ||
831 | |||
832 | static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan) | ||
833 | { | ||
834 | s3c2410_dma_buf_t *buf, *next; | ||
835 | unsigned long flags; | ||
836 | |||
837 | pr_debug("%s:\n", __FUNCTION__); | ||
838 | |||
839 | local_irq_save(flags); | ||
840 | |||
841 | if (chan->state != S3C2410_DMA_IDLE) { | ||
842 | pr_debug("%s: stopping channel...\n", __FUNCTION__ ); | ||
843 | s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); | ||
844 | } | ||
845 | |||
846 | buf = chan->curr; | ||
847 | if (buf == NULL) | ||
848 | buf = chan->next; | ||
849 | |||
850 | chan->curr = chan->next = chan->end = NULL; | ||
851 | |||
852 | if (buf != NULL) { | ||
853 | for ( ; buf != NULL; buf = next) { | ||
854 | next = buf->next; | ||
855 | |||
856 | pr_debug("%s: free buffer %p, next %p\n", | ||
857 | __FUNCTION__, buf, buf->next); | ||
858 | |||
859 | s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT); | ||
860 | s3c2410_dma_freebuf(buf); | ||
861 | } | ||
862 | } | ||
863 | |||
864 | local_irq_restore(flags); | ||
865 | |||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | |||
870 | int | ||
871 | s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op) | ||
872 | { | ||
873 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
874 | |||
875 | check_channel(channel); | ||
876 | |||
877 | switch (op) { | ||
878 | case S3C2410_DMAOP_START: | ||
879 | return s3c2410_dma_start(chan); | ||
880 | |||
881 | case S3C2410_DMAOP_STOP: | ||
882 | return s3c2410_dma_dostop(chan); | ||
883 | |||
884 | case S3C2410_DMAOP_PAUSE: | ||
885 | return -ENOENT; | ||
886 | |||
887 | case S3C2410_DMAOP_RESUME: | ||
888 | return -ENOENT; | ||
889 | |||
890 | case S3C2410_DMAOP_FLUSH: | ||
891 | return s3c2410_dma_flush(chan); | ||
892 | |||
893 | case S3C2410_DMAOP_TIMEOUT: | ||
894 | return 0; | ||
895 | |||
896 | } | ||
897 | |||
898 | return -ENOENT; /* unknown, don't bother */ | ||
899 | } | ||
900 | |||
901 | EXPORT_SYMBOL(s3c2410_dma_ctrl); | ||
902 | |||
903 | /* DMA configuration for each channel | ||
904 | * | ||
905 | * DISRCC -> source of the DMA (AHB,APB) | ||
906 | * DISRC -> source address of the DMA | ||
907 | * DIDSTC -> destination of the DMA (AHB,APD) | ||
908 | * DIDST -> destination address of the DMA | ||
909 | */ | ||
910 | |||
911 | /* s3c2410_dma_config | ||
912 | * | ||
913 | * xfersize: size of unit in bytes (1,2,4) | ||
914 | * dcon: base value of the DCONx register | ||
915 | */ | ||
916 | |||
917 | int s3c2410_dma_config(dmach_t channel, | ||
918 | int xferunit, | ||
919 | int dcon) | ||
920 | { | ||
921 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
922 | |||
923 | pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n", | ||
924 | __FUNCTION__, channel, xferunit, dcon); | ||
925 | |||
926 | check_channel(channel); | ||
927 | |||
928 | switch (xferunit) { | ||
929 | case 1: | ||
930 | dcon |= S3C2410_DCON_BYTE; | ||
931 | break; | ||
932 | |||
933 | case 2: | ||
934 | dcon |= S3C2410_DCON_HALFWORD; | ||
935 | break; | ||
936 | |||
937 | case 4: | ||
938 | dcon |= S3C2410_DCON_WORD; | ||
939 | break; | ||
940 | |||
941 | default: | ||
942 | pr_debug("%s: bad transfer size %d\n", __FUNCTION__, xferunit); | ||
943 | return -EINVAL; | ||
944 | } | ||
945 | |||
946 | dcon |= S3C2410_DCON_HWTRIG; | ||
947 | dcon |= S3C2410_DCON_INTREQ; | ||
948 | |||
949 | pr_debug("%s: dcon now %08x\n", __FUNCTION__, dcon); | ||
950 | |||
951 | chan->dcon = dcon; | ||
952 | chan->xfer_unit = xferunit; | ||
953 | |||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | EXPORT_SYMBOL(s3c2410_dma_config); | ||
958 | |||
959 | int s3c2410_dma_setflags(dmach_t channel, unsigned int flags) | ||
960 | { | ||
961 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
962 | |||
963 | check_channel(channel); | ||
964 | |||
965 | pr_debug("%s: chan=%p, flags=%08x\n", __FUNCTION__, chan, flags); | ||
966 | |||
967 | chan->flags = flags; | ||
968 | |||
969 | return 0; | ||
970 | } | ||
971 | |||
972 | EXPORT_SYMBOL(s3c2410_dma_setflags); | ||
973 | |||
974 | |||
975 | /* do we need to protect the settings of the fields from | ||
976 | * irq? | ||
977 | */ | ||
978 | |||
979 | int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn) | ||
980 | { | ||
981 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
982 | |||
983 | check_channel(channel); | ||
984 | |||
985 | pr_debug("%s: chan=%p, op rtn=%p\n", __FUNCTION__, chan, rtn); | ||
986 | |||
987 | chan->op_fn = rtn; | ||
988 | |||
989 | return 0; | ||
990 | } | ||
991 | |||
992 | EXPORT_SYMBOL(s3c2410_dma_set_opfn); | ||
993 | |||
994 | int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn) | ||
995 | { | ||
996 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
997 | |||
998 | check_channel(channel); | ||
999 | |||
1000 | pr_debug("%s: chan=%p, callback rtn=%p\n", __FUNCTION__, chan, rtn); | ||
1001 | |||
1002 | chan->callback_fn = rtn; | ||
1003 | |||
1004 | return 0; | ||
1005 | } | ||
1006 | |||
1007 | EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn); | ||
1008 | |||
1009 | /* s3c2410_dma_devconfig | ||
1010 | * | ||
1011 | * configure the dma source/destination hardware type and address | ||
1012 | * | ||
1013 | * source: S3C2410_DMASRC_HW: source is hardware | ||
1014 | * S3C2410_DMASRC_MEM: source is memory | ||
1015 | * | ||
1016 | * hwcfg: the value for xxxSTCn register, | ||
1017 | * bit 0: 0=increment pointer, 1=leave pointer | ||
1018 | * bit 1: 0=soucre is AHB, 1=soucre is APB | ||
1019 | * | ||
1020 | * devaddr: physical address of the source | ||
1021 | */ | ||
1022 | |||
1023 | int s3c2410_dma_devconfig(int channel, | ||
1024 | s3c2410_dmasrc_t source, | ||
1025 | int hwcfg, | ||
1026 | unsigned long devaddr) | ||
1027 | { | ||
1028 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
1029 | |||
1030 | check_channel(channel); | ||
1031 | |||
1032 | pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx\n", | ||
1033 | __FUNCTION__, (int)source, hwcfg, devaddr); | ||
1034 | |||
1035 | chan->source = source; | ||
1036 | chan->dev_addr = devaddr; | ||
1037 | |||
1038 | switch (source) { | ||
1039 | case S3C2410_DMASRC_HW: | ||
1040 | /* source is hardware */ | ||
1041 | pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n", | ||
1042 | __FUNCTION__, devaddr, hwcfg); | ||
1043 | dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3); | ||
1044 | dma_wrreg(chan, S3C2410_DMA_DISRC, devaddr); | ||
1045 | dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0)); | ||
1046 | |||
1047 | chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST); | ||
1048 | return 0; | ||
1049 | |||
1050 | case S3C2410_DMASRC_MEM: | ||
1051 | /* source is memory */ | ||
1052 | pr_debug( "%s: mem source, devaddr=%08lx, hwcfg=%d\n", | ||
1053 | __FUNCTION__, devaddr, hwcfg); | ||
1054 | dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0)); | ||
1055 | dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr); | ||
1056 | dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3); | ||
1057 | |||
1058 | chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC); | ||
1059 | return 0; | ||
1060 | } | ||
1061 | |||
1062 | printk(KERN_ERR "dma%d: invalid source type (%d)\n", channel, source); | ||
1063 | return -EINVAL; | ||
1064 | } | ||
1065 | |||
1066 | EXPORT_SYMBOL(s3c2410_dma_devconfig); | ||
1067 | |||
1068 | /* s3c2410_dma_getposition | ||
1069 | * | ||
1070 | * returns the current transfer points for the dma source and destination | ||
1071 | */ | ||
1072 | |||
1073 | int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst) | ||
1074 | { | ||
1075 | s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; | ||
1076 | |||
1077 | check_channel(channel); | ||
1078 | |||
1079 | if (src != NULL) | ||
1080 | *src = dma_rdreg(chan, S3C2410_DMA_DCSRC); | ||
1081 | |||
1082 | if (dst != NULL) | ||
1083 | *dst = dma_rdreg(chan, S3C2410_DMA_DCDST); | ||
1084 | |||
1085 | return 0; | ||
1086 | } | ||
1087 | |||
1088 | EXPORT_SYMBOL(s3c2410_dma_getposition); | ||
1089 | |||
1090 | |||
1091 | /* system device class */ | ||
1092 | |||
1093 | #ifdef CONFIG_PM | ||
1094 | |||
1095 | static int s3c2410_dma_suspend(struct sys_device *dev, pm_message_t state) | ||
1096 | { | ||
1097 | s3c2410_dma_chan_t *cp = container_of(dev, s3c2410_dma_chan_t, dev); | ||
1098 | |||
1099 | printk(KERN_DEBUG "suspending dma channel %d\n", cp->number); | ||
1100 | |||
1101 | if (dma_rdreg(cp, S3C2410_DMA_DMASKTRIG) & S3C2410_DMASKTRIG_ON) { | ||
1102 | /* the dma channel is still working, which is probably | ||
1103 | * a bad thing to do over suspend/resume. We stop the | ||
1104 | * channel and assume that the client is either going to | ||
1105 | * retry after resume, or that it is broken. | ||
1106 | */ | ||
1107 | |||
1108 | printk(KERN_INFO "dma: stopping channel %d due to suspend\n", | ||
1109 | cp->number); | ||
1110 | |||
1111 | s3c2410_dma_dostop(cp); | ||
1112 | } | ||
1113 | |||
1114 | return 0; | ||
1115 | } | ||
1116 | |||
1117 | static int s3c2410_dma_resume(struct sys_device *dev) | ||
1118 | { | ||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | #else | ||
1123 | #define s3c2410_dma_suspend NULL | ||
1124 | #define s3c2410_dma_resume NULL | ||
1125 | #endif /* CONFIG_PM */ | ||
1126 | |||
1127 | static struct sysdev_class dma_sysclass = { | ||
1128 | set_kset_name("s3c24xx-dma"), | ||
1129 | .suspend = s3c2410_dma_suspend, | ||
1130 | .resume = s3c2410_dma_resume, | ||
1131 | }; | ||
1132 | |||
1133 | /* kmem cache implementation */ | ||
1134 | |||
1135 | static void s3c2410_dma_cache_ctor(void *p, kmem_cache_t *c, unsigned long f) | ||
1136 | { | ||
1137 | memset(p, 0, sizeof(s3c2410_dma_buf_t)); | ||
1138 | } | ||
1139 | |||
1140 | |||
1141 | /* initialisation code */ | ||
1142 | |||
1143 | static int __init s3c2410_init_dma(void) | ||
1144 | { | ||
1145 | s3c2410_dma_chan_t *cp; | ||
1146 | int channel; | ||
1147 | int ret; | ||
1148 | |||
1149 | printk("S3C2410 DMA Driver, (c) 2003-2004 Simtec Electronics\n"); | ||
1150 | |||
1151 | dma_base = ioremap(S3C2410_PA_DMA, 0x200); | ||
1152 | if (dma_base == NULL) { | ||
1153 | printk(KERN_ERR "dma failed to remap register block\n"); | ||
1154 | return -ENOMEM; | ||
1155 | } | ||
1156 | |||
1157 | ret = sysdev_class_register(&dma_sysclass); | ||
1158 | if (ret != 0) { | ||
1159 | printk(KERN_ERR "dma sysclass registration failed\n"); | ||
1160 | goto err; | ||
1161 | } | ||
1162 | |||
1163 | dma_kmem = kmem_cache_create("dma_desc", sizeof(s3c2410_dma_buf_t), 0, | ||
1164 | SLAB_HWCACHE_ALIGN, | ||
1165 | s3c2410_dma_cache_ctor, NULL); | ||
1166 | |||
1167 | if (dma_kmem == NULL) { | ||
1168 | printk(KERN_ERR "dma failed to make kmem cache\n"); | ||
1169 | ret = -ENOMEM; | ||
1170 | goto err; | ||
1171 | } | ||
1172 | |||
1173 | for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) { | ||
1174 | cp = &s3c2410_chans[channel]; | ||
1175 | |||
1176 | memset(cp, 0, sizeof(s3c2410_dma_chan_t)); | ||
1177 | |||
1178 | /* dma channel irqs are in order.. */ | ||
1179 | cp->number = channel; | ||
1180 | cp->irq = channel + IRQ_DMA0; | ||
1181 | cp->regs = dma_base + (channel*0x40); | ||
1182 | |||
1183 | /* point current stats somewhere */ | ||
1184 | cp->stats = &cp->stats_store; | ||
1185 | cp->stats_store.timeout_shortest = LONG_MAX; | ||
1186 | |||
1187 | /* basic channel configuration */ | ||
1188 | |||
1189 | cp->load_timeout = 1<<18; | ||
1190 | |||
1191 | /* register system device */ | ||
1192 | |||
1193 | cp->dev.cls = &dma_sysclass; | ||
1194 | cp->dev.id = channel; | ||
1195 | ret = sysdev_register(&cp->dev); | ||
1196 | |||
1197 | printk("DMA channel %d at %p, irq %d\n", | ||
1198 | cp->number, cp->regs, cp->irq); | ||
1199 | } | ||
1200 | |||
1201 | return 0; | ||
1202 | |||
1203 | err: | ||
1204 | kmem_cache_destroy(dma_kmem); | ||
1205 | iounmap(dma_base); | ||
1206 | dma_base = NULL; | ||
1207 | return ret; | ||
1208 | } | ||
1209 | |||
1210 | __initcall(s3c2410_init_dma); | ||
diff --git a/arch/arm/mach-s3c2410/gpio.c b/arch/arm/mach-s3c2410/gpio.c new file mode 100644 index 000000000000..94f1776cf312 --- /dev/null +++ b/arch/arm/mach-s3c2410/gpio.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/gpio.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C2410 GPIO support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * Changelog | ||
23 | * 13-Sep-2004 BJD Implemented change of MISCCR | ||
24 | * 14-Sep-2004 BJD Added getpin call | ||
25 | * 14-Sep-2004 BJD Fixed bug in setpin() call | ||
26 | * 30-Sep-2004 BJD Fixed cfgpin() mask bug | ||
27 | * 01-Oct-2004 BJD Added getcfg() to get pin configuration | ||
28 | * 01-Oct-2004 BJD Fixed mask bug in pullup() call | ||
29 | * 01-Oct-2004 BJD Added getirq() to turn pin into irqno | ||
30 | * 04-Oct-2004 BJD Added irq filter controls for GPIO | ||
31 | * 05-Nov-2004 BJD EXPORT_SYMBOL() added for all code | ||
32 | * 13-Mar-2005 BJD Updates for __iomem | ||
33 | */ | ||
34 | |||
35 | |||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/module.h> | ||
39 | #include <linux/interrupt.h> | ||
40 | #include <linux/ioport.h> | ||
41 | |||
42 | #include <asm/hardware.h> | ||
43 | #include <asm/irq.h> | ||
44 | #include <asm/io.h> | ||
45 | |||
46 | #include <asm/arch/regs-gpio.h> | ||
47 | |||
48 | void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) | ||
49 | { | ||
50 | void __iomem *base = S3C2410_GPIO_BASE(pin); | ||
51 | unsigned long mask; | ||
52 | unsigned long con; | ||
53 | unsigned long flags; | ||
54 | |||
55 | if (pin < S3C2410_GPIO_BANKB) { | ||
56 | mask = 1 << S3C2410_GPIO_OFFSET(pin); | ||
57 | } else { | ||
58 | mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; | ||
59 | } | ||
60 | |||
61 | local_irq_save(flags); | ||
62 | |||
63 | con = __raw_readl(base + 0x00); | ||
64 | con &= ~mask; | ||
65 | con |= function; | ||
66 | |||
67 | __raw_writel(con, base + 0x00); | ||
68 | |||
69 | local_irq_restore(flags); | ||
70 | } | ||
71 | |||
72 | EXPORT_SYMBOL(s3c2410_gpio_cfgpin); | ||
73 | |||
74 | unsigned int s3c2410_gpio_getcfg(unsigned int pin) | ||
75 | { | ||
76 | void __iomem *base = S3C2410_GPIO_BASE(pin); | ||
77 | unsigned long mask; | ||
78 | |||
79 | if (pin < S3C2410_GPIO_BANKB) { | ||
80 | mask = 1 << S3C2410_GPIO_OFFSET(pin); | ||
81 | } else { | ||
82 | mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; | ||
83 | } | ||
84 | |||
85 | return __raw_readl(base) & mask; | ||
86 | } | ||
87 | |||
88 | EXPORT_SYMBOL(s3c2410_gpio_getcfg); | ||
89 | |||
90 | void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) | ||
91 | { | ||
92 | void __iomem *base = S3C2410_GPIO_BASE(pin); | ||
93 | unsigned long offs = S3C2410_GPIO_OFFSET(pin); | ||
94 | unsigned long flags; | ||
95 | unsigned long up; | ||
96 | |||
97 | if (pin < S3C2410_GPIO_BANKB) | ||
98 | return; | ||
99 | |||
100 | local_irq_save(flags); | ||
101 | |||
102 | up = __raw_readl(base + 0x08); | ||
103 | up &= ~(1L << offs); | ||
104 | up |= to << offs; | ||
105 | __raw_writel(up, base + 0x08); | ||
106 | |||
107 | local_irq_restore(flags); | ||
108 | } | ||
109 | |||
110 | EXPORT_SYMBOL(s3c2410_gpio_pullup); | ||
111 | |||
112 | void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) | ||
113 | { | ||
114 | void __iomem *base = S3C2410_GPIO_BASE(pin); | ||
115 | unsigned long offs = S3C2410_GPIO_OFFSET(pin); | ||
116 | unsigned long flags; | ||
117 | unsigned long dat; | ||
118 | |||
119 | local_irq_save(flags); | ||
120 | |||
121 | dat = __raw_readl(base + 0x04); | ||
122 | dat &= ~(1 << offs); | ||
123 | dat |= to << offs; | ||
124 | __raw_writel(dat, base + 0x04); | ||
125 | |||
126 | local_irq_restore(flags); | ||
127 | } | ||
128 | |||
129 | EXPORT_SYMBOL(s3c2410_gpio_setpin); | ||
130 | |||
131 | unsigned int s3c2410_gpio_getpin(unsigned int pin) | ||
132 | { | ||
133 | void __iomem *base = S3C2410_GPIO_BASE(pin); | ||
134 | unsigned long offs = S3C2410_GPIO_OFFSET(pin); | ||
135 | |||
136 | return __raw_readl(base + 0x04) & (1<< offs); | ||
137 | } | ||
138 | |||
139 | EXPORT_SYMBOL(s3c2410_gpio_getpin); | ||
140 | |||
141 | unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) | ||
142 | { | ||
143 | unsigned long flags; | ||
144 | unsigned long misccr; | ||
145 | |||
146 | local_irq_save(flags); | ||
147 | misccr = __raw_readl(S3C2410_MISCCR); | ||
148 | misccr &= ~clear; | ||
149 | misccr ^= change; | ||
150 | __raw_writel(misccr, S3C2410_MISCCR); | ||
151 | local_irq_restore(flags); | ||
152 | |||
153 | return misccr; | ||
154 | } | ||
155 | |||
156 | EXPORT_SYMBOL(s3c2410_modify_misccr); | ||
157 | |||
158 | int s3c2410_gpio_getirq(unsigned int pin) | ||
159 | { | ||
160 | if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15_EINT23) | ||
161 | return -1; /* not valid interrupts */ | ||
162 | |||
163 | if (pin < S3C2410_GPG0 && pin > S3C2410_GPF7) | ||
164 | return -1; /* not valid pin */ | ||
165 | |||
166 | if (pin < S3C2410_GPF4) | ||
167 | return (pin - S3C2410_GPF0) + IRQ_EINT0; | ||
168 | |||
169 | if (pin < S3C2410_GPG0) | ||
170 | return (pin - S3C2410_GPF4) + IRQ_EINT4; | ||
171 | |||
172 | return (pin - S3C2410_GPG0) + IRQ_EINT8; | ||
173 | } | ||
174 | |||
175 | EXPORT_SYMBOL(s3c2410_gpio_getirq); | ||
176 | |||
177 | int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, | ||
178 | unsigned int config) | ||
179 | { | ||
180 | void __iomem *reg = S3C2410_EINFLT0; | ||
181 | unsigned long flags; | ||
182 | unsigned long val; | ||
183 | |||
184 | if (pin < S3C2410_GPG8 || pin > S3C2410_GPG15) | ||
185 | return -1; | ||
186 | |||
187 | config &= 0xff; | ||
188 | |||
189 | pin -= S3C2410_GPG8_EINT16; | ||
190 | reg += pin & ~3; | ||
191 | |||
192 | local_irq_save(flags); | ||
193 | |||
194 | /* update filter width and clock source */ | ||
195 | |||
196 | val = __raw_readl(reg); | ||
197 | val &= ~(0xff << ((pin & 3) * 8)); | ||
198 | val |= config << ((pin & 3) * 8); | ||
199 | __raw_writel(val, reg); | ||
200 | |||
201 | /* update filter enable */ | ||
202 | |||
203 | val = __raw_readl(S3C2410_EXTINT2); | ||
204 | val &= ~(1 << ((pin * 4) + 3)); | ||
205 | val |= on << ((pin * 4) + 3); | ||
206 | __raw_writel(val, S3C2410_EXTINT2); | ||
207 | |||
208 | local_irq_restore(flags); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | EXPORT_SYMBOL(s3c2410_gpio_irqfilter); | ||
diff --git a/arch/arm/mach-s3c2410/irq.c b/arch/arm/mach-s3c2410/irq.c new file mode 100644 index 000000000000..b668c48f4399 --- /dev/null +++ b/arch/arm/mach-s3c2410/irq.c | |||
@@ -0,0 +1,966 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/irq.c | ||
2 | * | ||
3 | * Copyright (c) 2003,2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | * Changelog: | ||
21 | * | ||
22 | * 22-Jul-2004 Ben Dooks <ben@simtec.co.uk> | ||
23 | * Fixed compile warnings | ||
24 | * | ||
25 | * 22-Jul-2004 Roc Wu <cooloney@yahoo.com.cn> | ||
26 | * Fixed s3c_extirq_type | ||
27 | * | ||
28 | * 21-Jul-2004 Arnaud Patard (Rtp) <arnaud.patard@rtp-net.org> | ||
29 | * Addition of ADC/TC demux | ||
30 | * | ||
31 | * 04-Oct-2004 Klaus Fetscher <k.fetscher@fetron.de> | ||
32 | * Fix for set_irq_type() on low EINT numbers | ||
33 | * | ||
34 | * 05-Oct-2004 Ben Dooks <ben@simtec.co.uk> | ||
35 | * Tidy up KF's patch and sort out new release | ||
36 | * | ||
37 | * 05-Oct-2004 Ben Dooks <ben@simtec.co.uk> | ||
38 | * Add support for power management controls | ||
39 | * | ||
40 | * 04-Nov-2004 Ben Dooks | ||
41 | * Fix standard IRQ wake for EINT0..4 and RTC | ||
42 | * | ||
43 | * 22-Feb-2004 Ben Dooks | ||
44 | * Fixed edge-triggering on ADC IRQ | ||
45 | */ | ||
46 | |||
47 | #include <linux/init.h> | ||
48 | #include <linux/module.h> | ||
49 | #include <linux/interrupt.h> | ||
50 | #include <linux/ioport.h> | ||
51 | #include <linux/ptrace.h> | ||
52 | #include <linux/sysdev.h> | ||
53 | |||
54 | #include <asm/hardware.h> | ||
55 | #include <asm/irq.h> | ||
56 | #include <asm/io.h> | ||
57 | |||
58 | #include <asm/mach/irq.h> | ||
59 | |||
60 | #include <asm/arch/regs-irq.h> | ||
61 | #include <asm/arch/regs-gpio.h> | ||
62 | |||
63 | #include "cpu.h" | ||
64 | #include "pm.h" | ||
65 | |||
66 | #define irqdbf(x...) | ||
67 | #define irqdbf2(x...) | ||
68 | |||
69 | #define EXTINT_OFF (IRQ_EINT4 - 4) | ||
70 | |||
71 | /* wakeup irq control */ | ||
72 | |||
73 | #ifdef CONFIG_PM | ||
74 | |||
75 | /* state for IRQs over sleep */ | ||
76 | |||
77 | /* default is to allow for EINT0..EINT15, and IRQ_RTC as wakeup sources | ||
78 | * | ||
79 | * set bit to 1 in allow bitfield to enable the wakeup settings on it | ||
80 | */ | ||
81 | |||
82 | unsigned long s3c_irqwake_intallow = 1L << (IRQ_RTC - IRQ_EINT0) | 0xfL; | ||
83 | unsigned long s3c_irqwake_intmask = 0xffffffffL; | ||
84 | unsigned long s3c_irqwake_eintallow = 0x0000fff0L; | ||
85 | unsigned long s3c_irqwake_eintmask = 0xffffffffL; | ||
86 | |||
87 | static int | ||
88 | s3c_irq_wake(unsigned int irqno, unsigned int state) | ||
89 | { | ||
90 | unsigned long irqbit = 1 << (irqno - IRQ_EINT0); | ||
91 | |||
92 | if (!(s3c_irqwake_intallow & irqbit)) | ||
93 | return -ENOENT; | ||
94 | |||
95 | printk(KERN_INFO "wake %s for irq %d\n", | ||
96 | state ? "enabled" : "disabled", irqno); | ||
97 | |||
98 | if (!state) | ||
99 | s3c_irqwake_intmask |= irqbit; | ||
100 | else | ||
101 | s3c_irqwake_intmask &= ~irqbit; | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static int | ||
107 | s3c_irqext_wake(unsigned int irqno, unsigned int state) | ||
108 | { | ||
109 | unsigned long bit = 1L << (irqno - EXTINT_OFF); | ||
110 | |||
111 | if (!(s3c_irqwake_eintallow & bit)) | ||
112 | return -ENOENT; | ||
113 | |||
114 | printk(KERN_INFO "wake %s for irq %d\n", | ||
115 | state ? "enabled" : "disabled", irqno); | ||
116 | |||
117 | if (!state) | ||
118 | s3c_irqwake_eintmask |= bit; | ||
119 | else | ||
120 | s3c_irqwake_eintmask &= ~bit; | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | #else | ||
126 | #define s3c_irqext_wake NULL | ||
127 | #define s3c_irq_wake NULL | ||
128 | #endif | ||
129 | |||
130 | |||
131 | static void | ||
132 | s3c_irq_mask(unsigned int irqno) | ||
133 | { | ||
134 | unsigned long mask; | ||
135 | |||
136 | irqno -= IRQ_EINT0; | ||
137 | |||
138 | mask = __raw_readl(S3C2410_INTMSK); | ||
139 | mask |= 1UL << irqno; | ||
140 | __raw_writel(mask, S3C2410_INTMSK); | ||
141 | } | ||
142 | |||
143 | static inline void | ||
144 | s3c_irq_ack(unsigned int irqno) | ||
145 | { | ||
146 | unsigned long bitval = 1UL << (irqno - IRQ_EINT0); | ||
147 | |||
148 | __raw_writel(bitval, S3C2410_SRCPND); | ||
149 | __raw_writel(bitval, S3C2410_INTPND); | ||
150 | } | ||
151 | |||
152 | static inline void | ||
153 | s3c_irq_maskack(unsigned int irqno) | ||
154 | { | ||
155 | unsigned long bitval = 1UL << (irqno - IRQ_EINT0); | ||
156 | unsigned long mask; | ||
157 | |||
158 | mask = __raw_readl(S3C2410_INTMSK); | ||
159 | __raw_writel(mask|bitval, S3C2410_INTMSK); | ||
160 | |||
161 | __raw_writel(bitval, S3C2410_SRCPND); | ||
162 | __raw_writel(bitval, S3C2410_INTPND); | ||
163 | } | ||
164 | |||
165 | |||
166 | static void | ||
167 | s3c_irq_unmask(unsigned int irqno) | ||
168 | { | ||
169 | unsigned long mask; | ||
170 | |||
171 | if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23) | ||
172 | irqdbf2("s3c_irq_unmask %d\n", irqno); | ||
173 | |||
174 | irqno -= IRQ_EINT0; | ||
175 | |||
176 | mask = __raw_readl(S3C2410_INTMSK); | ||
177 | mask &= ~(1UL << irqno); | ||
178 | __raw_writel(mask, S3C2410_INTMSK); | ||
179 | } | ||
180 | |||
181 | static struct irqchip s3c_irq_level_chip = { | ||
182 | .ack = s3c_irq_maskack, | ||
183 | .mask = s3c_irq_mask, | ||
184 | .unmask = s3c_irq_unmask, | ||
185 | .wake = s3c_irq_wake | ||
186 | }; | ||
187 | |||
188 | static struct irqchip s3c_irq_chip = { | ||
189 | .ack = s3c_irq_ack, | ||
190 | .mask = s3c_irq_mask, | ||
191 | .unmask = s3c_irq_unmask, | ||
192 | .wake = s3c_irq_wake | ||
193 | }; | ||
194 | |||
195 | /* S3C2410_EINTMASK | ||
196 | * S3C2410_EINTPEND | ||
197 | */ | ||
198 | |||
199 | static void | ||
200 | s3c_irqext_mask(unsigned int irqno) | ||
201 | { | ||
202 | unsigned long mask; | ||
203 | |||
204 | irqno -= EXTINT_OFF; | ||
205 | |||
206 | mask = __raw_readl(S3C2410_EINTMASK); | ||
207 | mask |= ( 1UL << irqno); | ||
208 | __raw_writel(mask, S3C2410_EINTMASK); | ||
209 | |||
210 | if (irqno <= (IRQ_EINT7 - EXTINT_OFF)) { | ||
211 | /* check to see if all need masking */ | ||
212 | |||
213 | if ((mask & (0xf << 4)) == (0xf << 4)) { | ||
214 | /* all masked, mask the parent */ | ||
215 | s3c_irq_mask(IRQ_EINT4t7); | ||
216 | } | ||
217 | } else { | ||
218 | /* todo: the same check as above for the rest of the irq regs...*/ | ||
219 | |||
220 | } | ||
221 | } | ||
222 | |||
223 | static void | ||
224 | s3c_irqext_ack(unsigned int irqno) | ||
225 | { | ||
226 | unsigned long req; | ||
227 | unsigned long bit; | ||
228 | unsigned long mask; | ||
229 | |||
230 | bit = 1UL << (irqno - EXTINT_OFF); | ||
231 | |||
232 | |||
233 | mask = __raw_readl(S3C2410_EINTMASK); | ||
234 | |||
235 | __raw_writel(bit, S3C2410_EINTPEND); | ||
236 | |||
237 | req = __raw_readl(S3C2410_EINTPEND); | ||
238 | req &= ~mask; | ||
239 | |||
240 | /* not sure if we should be acking the parent irq... */ | ||
241 | |||
242 | if (irqno <= IRQ_EINT7 ) { | ||
243 | if ((req & 0xf0) == 0) | ||
244 | s3c_irq_ack(IRQ_EINT4t7); | ||
245 | } else { | ||
246 | if ((req >> 8) == 0) | ||
247 | s3c_irq_ack(IRQ_EINT8t23); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | static void | ||
252 | s3c_irqext_unmask(unsigned int irqno) | ||
253 | { | ||
254 | unsigned long mask; | ||
255 | |||
256 | irqno -= EXTINT_OFF; | ||
257 | |||
258 | mask = __raw_readl(S3C2410_EINTMASK); | ||
259 | mask &= ~( 1UL << irqno); | ||
260 | __raw_writel(mask, S3C2410_EINTMASK); | ||
261 | |||
262 | s3c_irq_unmask((irqno <= (IRQ_EINT7 - EXTINT_OFF)) ? IRQ_EINT4t7 : IRQ_EINT8t23); | ||
263 | } | ||
264 | |||
265 | static int | ||
266 | s3c_irqext_type(unsigned int irq, unsigned int type) | ||
267 | { | ||
268 | void __iomem *extint_reg; | ||
269 | void __iomem *gpcon_reg; | ||
270 | unsigned long gpcon_offset, extint_offset; | ||
271 | unsigned long newvalue = 0, value; | ||
272 | |||
273 | if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) | ||
274 | { | ||
275 | gpcon_reg = S3C2410_GPFCON; | ||
276 | extint_reg = S3C2410_EXTINT0; | ||
277 | gpcon_offset = (irq - IRQ_EINT0) * 2; | ||
278 | extint_offset = (irq - IRQ_EINT0) * 4; | ||
279 | } | ||
280 | else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7)) | ||
281 | { | ||
282 | gpcon_reg = S3C2410_GPFCON; | ||
283 | extint_reg = S3C2410_EXTINT0; | ||
284 | gpcon_offset = (irq - (EXTINT_OFF)) * 2; | ||
285 | extint_offset = (irq - (EXTINT_OFF)) * 4; | ||
286 | } | ||
287 | else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) | ||
288 | { | ||
289 | gpcon_reg = S3C2410_GPGCON; | ||
290 | extint_reg = S3C2410_EXTINT1; | ||
291 | gpcon_offset = (irq - IRQ_EINT8) * 2; | ||
292 | extint_offset = (irq - IRQ_EINT8) * 4; | ||
293 | } | ||
294 | else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23)) | ||
295 | { | ||
296 | gpcon_reg = S3C2410_GPGCON; | ||
297 | extint_reg = S3C2410_EXTINT2; | ||
298 | gpcon_offset = (irq - IRQ_EINT8) * 2; | ||
299 | extint_offset = (irq - IRQ_EINT16) * 4; | ||
300 | } else | ||
301 | return -1; | ||
302 | |||
303 | /* Set the GPIO to external interrupt mode */ | ||
304 | value = __raw_readl(gpcon_reg); | ||
305 | value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); | ||
306 | __raw_writel(value, gpcon_reg); | ||
307 | |||
308 | /* Set the external interrupt to pointed trigger type */ | ||
309 | switch (type) | ||
310 | { | ||
311 | case IRQT_NOEDGE: | ||
312 | printk(KERN_WARNING "No edge setting!\n"); | ||
313 | break; | ||
314 | |||
315 | case IRQT_RISING: | ||
316 | newvalue = S3C2410_EXTINT_RISEEDGE; | ||
317 | break; | ||
318 | |||
319 | case IRQT_FALLING: | ||
320 | newvalue = S3C2410_EXTINT_FALLEDGE; | ||
321 | break; | ||
322 | |||
323 | case IRQT_BOTHEDGE: | ||
324 | newvalue = S3C2410_EXTINT_BOTHEDGE; | ||
325 | break; | ||
326 | |||
327 | case IRQT_LOW: | ||
328 | newvalue = S3C2410_EXTINT_LOWLEV; | ||
329 | break; | ||
330 | |||
331 | case IRQT_HIGH: | ||
332 | newvalue = S3C2410_EXTINT_HILEV; | ||
333 | break; | ||
334 | |||
335 | default: | ||
336 | printk(KERN_ERR "No such irq type %d", type); | ||
337 | return -1; | ||
338 | } | ||
339 | |||
340 | value = __raw_readl(extint_reg); | ||
341 | value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); | ||
342 | __raw_writel(value, extint_reg); | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static struct irqchip s3c_irqext_chip = { | ||
348 | .mask = s3c_irqext_mask, | ||
349 | .unmask = s3c_irqext_unmask, | ||
350 | .ack = s3c_irqext_ack, | ||
351 | .type = s3c_irqext_type, | ||
352 | .wake = s3c_irqext_wake | ||
353 | }; | ||
354 | |||
355 | static struct irqchip s3c_irq_eint0t4 = { | ||
356 | .ack = s3c_irq_ack, | ||
357 | .mask = s3c_irq_mask, | ||
358 | .unmask = s3c_irq_unmask, | ||
359 | .wake = s3c_irq_wake, | ||
360 | .type = s3c_irqext_type, | ||
361 | }; | ||
362 | |||
363 | /* mask values for the parent registers for each of the interrupt types */ | ||
364 | |||
365 | #define INTMSK_UART0 (1UL << (IRQ_UART0 - IRQ_EINT0)) | ||
366 | #define INTMSK_UART1 (1UL << (IRQ_UART1 - IRQ_EINT0)) | ||
367 | #define INTMSK_UART2 (1UL << (IRQ_UART2 - IRQ_EINT0)) | ||
368 | #define INTMSK_ADCPARENT (1UL << (IRQ_ADCPARENT - IRQ_EINT0)) | ||
369 | #define INTMSK_LCD (1UL << (IRQ_LCD - IRQ_EINT0)) | ||
370 | |||
371 | static inline void | ||
372 | s3c_irqsub_mask(unsigned int irqno, unsigned int parentbit, | ||
373 | int subcheck) | ||
374 | { | ||
375 | unsigned long mask; | ||
376 | unsigned long submask; | ||
377 | |||
378 | submask = __raw_readl(S3C2410_INTSUBMSK); | ||
379 | mask = __raw_readl(S3C2410_INTMSK); | ||
380 | |||
381 | submask |= (1UL << (irqno - IRQ_S3CUART_RX0)); | ||
382 | |||
383 | /* check to see if we need to mask the parent IRQ */ | ||
384 | |||
385 | if ((submask & subcheck) == subcheck) { | ||
386 | __raw_writel(mask | parentbit, S3C2410_INTMSK); | ||
387 | } | ||
388 | |||
389 | /* write back masks */ | ||
390 | __raw_writel(submask, S3C2410_INTSUBMSK); | ||
391 | |||
392 | } | ||
393 | |||
394 | static inline void | ||
395 | s3c_irqsub_unmask(unsigned int irqno, unsigned int parentbit) | ||
396 | { | ||
397 | unsigned long mask; | ||
398 | unsigned long submask; | ||
399 | |||
400 | submask = __raw_readl(S3C2410_INTSUBMSK); | ||
401 | mask = __raw_readl(S3C2410_INTMSK); | ||
402 | |||
403 | submask &= ~(1UL << (irqno - IRQ_S3CUART_RX0)); | ||
404 | mask &= ~parentbit; | ||
405 | |||
406 | /* write back masks */ | ||
407 | __raw_writel(submask, S3C2410_INTSUBMSK); | ||
408 | __raw_writel(mask, S3C2410_INTMSK); | ||
409 | } | ||
410 | |||
411 | |||
412 | static inline void | ||
413 | s3c_irqsub_maskack(unsigned int irqno, unsigned int parentmask, unsigned int group) | ||
414 | { | ||
415 | unsigned int bit = 1UL << (irqno - IRQ_S3CUART_RX0); | ||
416 | |||
417 | s3c_irqsub_mask(irqno, parentmask, group); | ||
418 | |||
419 | __raw_writel(bit, S3C2410_SUBSRCPND); | ||
420 | |||
421 | /* only ack parent if we've got all the irqs (seems we must | ||
422 | * ack, all and hope that the irq system retriggers ok when | ||
423 | * the interrupt goes off again) | ||
424 | */ | ||
425 | |||
426 | if (1) { | ||
427 | __raw_writel(parentmask, S3C2410_SRCPND); | ||
428 | __raw_writel(parentmask, S3C2410_INTPND); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | static inline void | ||
433 | s3c_irqsub_ack(unsigned int irqno, unsigned int parentmask, unsigned int group) | ||
434 | { | ||
435 | unsigned int bit = 1UL << (irqno - IRQ_S3CUART_RX0); | ||
436 | |||
437 | __raw_writel(bit, S3C2410_SUBSRCPND); | ||
438 | |||
439 | /* only ack parent if we've got all the irqs (seems we must | ||
440 | * ack, all and hope that the irq system retriggers ok when | ||
441 | * the interrupt goes off again) | ||
442 | */ | ||
443 | |||
444 | if (1) { | ||
445 | __raw_writel(parentmask, S3C2410_SRCPND); | ||
446 | __raw_writel(parentmask, S3C2410_INTPND); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | /* UART0 */ | ||
451 | |||
452 | static void | ||
453 | s3c_irq_uart0_mask(unsigned int irqno) | ||
454 | { | ||
455 | s3c_irqsub_mask(irqno, INTMSK_UART0, 7); | ||
456 | } | ||
457 | |||
458 | static void | ||
459 | s3c_irq_uart0_unmask(unsigned int irqno) | ||
460 | { | ||
461 | s3c_irqsub_unmask(irqno, INTMSK_UART0); | ||
462 | } | ||
463 | |||
464 | static void | ||
465 | s3c_irq_uart0_ack(unsigned int irqno) | ||
466 | { | ||
467 | s3c_irqsub_maskack(irqno, INTMSK_UART0, 7); | ||
468 | } | ||
469 | |||
470 | static struct irqchip s3c_irq_uart0 = { | ||
471 | .mask = s3c_irq_uart0_mask, | ||
472 | .unmask = s3c_irq_uart0_unmask, | ||
473 | .ack = s3c_irq_uart0_ack, | ||
474 | }; | ||
475 | |||
476 | /* UART1 */ | ||
477 | |||
478 | static void | ||
479 | s3c_irq_uart1_mask(unsigned int irqno) | ||
480 | { | ||
481 | s3c_irqsub_mask(irqno, INTMSK_UART1, 7 << 3); | ||
482 | } | ||
483 | |||
484 | static void | ||
485 | s3c_irq_uart1_unmask(unsigned int irqno) | ||
486 | { | ||
487 | s3c_irqsub_unmask(irqno, INTMSK_UART1); | ||
488 | } | ||
489 | |||
490 | static void | ||
491 | s3c_irq_uart1_ack(unsigned int irqno) | ||
492 | { | ||
493 | s3c_irqsub_maskack(irqno, INTMSK_UART1, 7 << 3); | ||
494 | } | ||
495 | |||
496 | static struct irqchip s3c_irq_uart1 = { | ||
497 | .mask = s3c_irq_uart1_mask, | ||
498 | .unmask = s3c_irq_uart1_unmask, | ||
499 | .ack = s3c_irq_uart1_ack, | ||
500 | }; | ||
501 | |||
502 | /* UART2 */ | ||
503 | |||
504 | static void | ||
505 | s3c_irq_uart2_mask(unsigned int irqno) | ||
506 | { | ||
507 | s3c_irqsub_mask(irqno, INTMSK_UART2, 7 << 6); | ||
508 | } | ||
509 | |||
510 | static void | ||
511 | s3c_irq_uart2_unmask(unsigned int irqno) | ||
512 | { | ||
513 | s3c_irqsub_unmask(irqno, INTMSK_UART2); | ||
514 | } | ||
515 | |||
516 | static void | ||
517 | s3c_irq_uart2_ack(unsigned int irqno) | ||
518 | { | ||
519 | s3c_irqsub_maskack(irqno, INTMSK_UART2, 7 << 6); | ||
520 | } | ||
521 | |||
522 | static struct irqchip s3c_irq_uart2 = { | ||
523 | .mask = s3c_irq_uart2_mask, | ||
524 | .unmask = s3c_irq_uart2_unmask, | ||
525 | .ack = s3c_irq_uart2_ack, | ||
526 | }; | ||
527 | |||
528 | /* ADC and Touchscreen */ | ||
529 | |||
530 | static void | ||
531 | s3c_irq_adc_mask(unsigned int irqno) | ||
532 | { | ||
533 | s3c_irqsub_mask(irqno, INTMSK_ADCPARENT, 3 << 9); | ||
534 | } | ||
535 | |||
536 | static void | ||
537 | s3c_irq_adc_unmask(unsigned int irqno) | ||
538 | { | ||
539 | s3c_irqsub_unmask(irqno, INTMSK_ADCPARENT); | ||
540 | } | ||
541 | |||
542 | static void | ||
543 | s3c_irq_adc_ack(unsigned int irqno) | ||
544 | { | ||
545 | s3c_irqsub_ack(irqno, INTMSK_ADCPARENT, 3 << 9); | ||
546 | } | ||
547 | |||
548 | static struct irqchip s3c_irq_adc = { | ||
549 | .mask = s3c_irq_adc_mask, | ||
550 | .unmask = s3c_irq_adc_unmask, | ||
551 | .ack = s3c_irq_adc_ack, | ||
552 | }; | ||
553 | |||
554 | /* irq demux for adc */ | ||
555 | static void s3c_irq_demux_adc(unsigned int irq, | ||
556 | struct irqdesc *desc, | ||
557 | struct pt_regs *regs) | ||
558 | { | ||
559 | unsigned int subsrc, submsk; | ||
560 | unsigned int offset = 9; | ||
561 | struct irqdesc *mydesc; | ||
562 | |||
563 | /* read the current pending interrupts, and the mask | ||
564 | * for what it is available */ | ||
565 | |||
566 | subsrc = __raw_readl(S3C2410_SUBSRCPND); | ||
567 | submsk = __raw_readl(S3C2410_INTSUBMSK); | ||
568 | |||
569 | subsrc &= ~submsk; | ||
570 | subsrc >>= offset; | ||
571 | subsrc &= 3; | ||
572 | |||
573 | if (subsrc != 0) { | ||
574 | if (subsrc & 1) { | ||
575 | mydesc = irq_desc + IRQ_TC; | ||
576 | mydesc->handle( IRQ_TC, mydesc, regs); | ||
577 | } | ||
578 | if (subsrc & 2) { | ||
579 | mydesc = irq_desc + IRQ_ADC; | ||
580 | mydesc->handle(IRQ_ADC, mydesc, regs); | ||
581 | } | ||
582 | } | ||
583 | } | ||
584 | |||
585 | static void s3c_irq_demux_uart(unsigned int start, | ||
586 | struct pt_regs *regs) | ||
587 | { | ||
588 | unsigned int subsrc, submsk; | ||
589 | unsigned int offset = start - IRQ_S3CUART_RX0; | ||
590 | struct irqdesc *desc; | ||
591 | |||
592 | /* read the current pending interrupts, and the mask | ||
593 | * for what it is available */ | ||
594 | |||
595 | subsrc = __raw_readl(S3C2410_SUBSRCPND); | ||
596 | submsk = __raw_readl(S3C2410_INTSUBMSK); | ||
597 | |||
598 | irqdbf2("s3c_irq_demux_uart: start=%d (%d), subsrc=0x%08x,0x%08x\n", | ||
599 | start, offset, subsrc, submsk); | ||
600 | |||
601 | subsrc &= ~submsk; | ||
602 | subsrc >>= offset; | ||
603 | subsrc &= 7; | ||
604 | |||
605 | if (subsrc != 0) { | ||
606 | desc = irq_desc + start; | ||
607 | |||
608 | if (subsrc & 1) | ||
609 | desc->handle(start, desc, regs); | ||
610 | |||
611 | desc++; | ||
612 | |||
613 | if (subsrc & 2) | ||
614 | desc->handle(start+1, desc, regs); | ||
615 | |||
616 | desc++; | ||
617 | |||
618 | if (subsrc & 4) | ||
619 | desc->handle(start+2, desc, regs); | ||
620 | } | ||
621 | } | ||
622 | |||
623 | /* uart demux entry points */ | ||
624 | |||
625 | static void | ||
626 | s3c_irq_demux_uart0(unsigned int irq, | ||
627 | struct irqdesc *desc, | ||
628 | struct pt_regs *regs) | ||
629 | { | ||
630 | irq = irq; | ||
631 | s3c_irq_demux_uart(IRQ_S3CUART_RX0, regs); | ||
632 | } | ||
633 | |||
634 | static void | ||
635 | s3c_irq_demux_uart1(unsigned int irq, | ||
636 | struct irqdesc *desc, | ||
637 | struct pt_regs *regs) | ||
638 | { | ||
639 | irq = irq; | ||
640 | s3c_irq_demux_uart(IRQ_S3CUART_RX1, regs); | ||
641 | } | ||
642 | |||
643 | static void | ||
644 | s3c_irq_demux_uart2(unsigned int irq, | ||
645 | struct irqdesc *desc, | ||
646 | struct pt_regs *regs) | ||
647 | { | ||
648 | irq = irq; | ||
649 | s3c_irq_demux_uart(IRQ_S3CUART_RX2, regs); | ||
650 | } | ||
651 | |||
652 | |||
653 | /* s3c24xx_init_irq | ||
654 | * | ||
655 | * Initialise S3C2410 IRQ system | ||
656 | */ | ||
657 | |||
658 | void __init s3c24xx_init_irq(void) | ||
659 | { | ||
660 | unsigned long pend; | ||
661 | unsigned long last; | ||
662 | int irqno; | ||
663 | int i; | ||
664 | |||
665 | irqdbf("s3c2410_init_irq: clearing interrupt status flags\n"); | ||
666 | |||
667 | /* first, clear all interrupts pending... */ | ||
668 | |||
669 | last = 0; | ||
670 | for (i = 0; i < 4; i++) { | ||
671 | pend = __raw_readl(S3C2410_EINTPEND); | ||
672 | |||
673 | if (pend == 0 || pend == last) | ||
674 | break; | ||
675 | |||
676 | __raw_writel(pend, S3C2410_EINTPEND); | ||
677 | printk("irq: clearing pending ext status %08x\n", (int)pend); | ||
678 | last = pend; | ||
679 | } | ||
680 | |||
681 | last = 0; | ||
682 | for (i = 0; i < 4; i++) { | ||
683 | pend = __raw_readl(S3C2410_INTPND); | ||
684 | |||
685 | if (pend == 0 || pend == last) | ||
686 | break; | ||
687 | |||
688 | __raw_writel(pend, S3C2410_SRCPND); | ||
689 | __raw_writel(pend, S3C2410_INTPND); | ||
690 | printk("irq: clearing pending status %08x\n", (int)pend); | ||
691 | last = pend; | ||
692 | } | ||
693 | |||
694 | last = 0; | ||
695 | for (i = 0; i < 4; i++) { | ||
696 | pend = __raw_readl(S3C2410_SUBSRCPND); | ||
697 | |||
698 | if (pend == 0 || pend == last) | ||
699 | break; | ||
700 | |||
701 | printk("irq: clearing subpending status %08x\n", (int)pend); | ||
702 | __raw_writel(pend, S3C2410_SUBSRCPND); | ||
703 | last = pend; | ||
704 | } | ||
705 | |||
706 | /* register the main interrupts */ | ||
707 | |||
708 | irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n"); | ||
709 | |||
710 | for (irqno = IRQ_BATT_FLT; irqno <= IRQ_ADCPARENT; irqno++) { | ||
711 | /* set all the s3c2410 internal irqs */ | ||
712 | |||
713 | switch (irqno) { | ||
714 | /* deal with the special IRQs (cascaded) */ | ||
715 | |||
716 | case IRQ_UART0: | ||
717 | case IRQ_UART1: | ||
718 | case IRQ_UART2: | ||
719 | case IRQ_LCD: | ||
720 | case IRQ_ADCPARENT: | ||
721 | set_irq_chip(irqno, &s3c_irq_level_chip); | ||
722 | set_irq_handler(irqno, do_level_IRQ); | ||
723 | break; | ||
724 | |||
725 | case IRQ_RESERVED6: | ||
726 | case IRQ_RESERVED24: | ||
727 | /* no IRQ here */ | ||
728 | break; | ||
729 | |||
730 | default: | ||
731 | //irqdbf("registering irq %d (s3c irq)\n", irqno); | ||
732 | set_irq_chip(irqno, &s3c_irq_chip); | ||
733 | set_irq_handler(irqno, do_edge_IRQ); | ||
734 | set_irq_flags(irqno, IRQF_VALID); | ||
735 | } | ||
736 | } | ||
737 | |||
738 | /* setup the cascade irq handlers */ | ||
739 | |||
740 | set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0); | ||
741 | set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1); | ||
742 | set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2); | ||
743 | set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc); | ||
744 | |||
745 | |||
746 | /* external interrupts */ | ||
747 | |||
748 | for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { | ||
749 | irqdbf("registering irq %d (ext int)\n", irqno); | ||
750 | set_irq_chip(irqno, &s3c_irq_eint0t4); | ||
751 | set_irq_handler(irqno, do_edge_IRQ); | ||
752 | set_irq_flags(irqno, IRQF_VALID); | ||
753 | } | ||
754 | |||
755 | for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { | ||
756 | irqdbf("registering irq %d (extended s3c irq)\n", irqno); | ||
757 | set_irq_chip(irqno, &s3c_irqext_chip); | ||
758 | set_irq_handler(irqno, do_edge_IRQ); | ||
759 | set_irq_flags(irqno, IRQF_VALID); | ||
760 | } | ||
761 | |||
762 | /* register the uart interrupts */ | ||
763 | |||
764 | irqdbf("s3c2410: registering external interrupts\n"); | ||
765 | |||
766 | for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) { | ||
767 | irqdbf("registering irq %d (s3c uart0 irq)\n", irqno); | ||
768 | set_irq_chip(irqno, &s3c_irq_uart0); | ||
769 | set_irq_handler(irqno, do_level_IRQ); | ||
770 | set_irq_flags(irqno, IRQF_VALID); | ||
771 | } | ||
772 | |||
773 | for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) { | ||
774 | irqdbf("registering irq %d (s3c uart1 irq)\n", irqno); | ||
775 | set_irq_chip(irqno, &s3c_irq_uart1); | ||
776 | set_irq_handler(irqno, do_level_IRQ); | ||
777 | set_irq_flags(irqno, IRQF_VALID); | ||
778 | } | ||
779 | |||
780 | for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) { | ||
781 | irqdbf("registering irq %d (s3c uart2 irq)\n", irqno); | ||
782 | set_irq_chip(irqno, &s3c_irq_uart2); | ||
783 | set_irq_handler(irqno, do_level_IRQ); | ||
784 | set_irq_flags(irqno, IRQF_VALID); | ||
785 | } | ||
786 | |||
787 | for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) { | ||
788 | irqdbf("registering irq %d (s3c adc irq)\n", irqno); | ||
789 | set_irq_chip(irqno, &s3c_irq_adc); | ||
790 | set_irq_handler(irqno, do_edge_IRQ); | ||
791 | set_irq_flags(irqno, IRQF_VALID); | ||
792 | } | ||
793 | |||
794 | irqdbf("s3c2410: registered interrupt handlers\n"); | ||
795 | } | ||
796 | |||
797 | /* s3c2440 irq code | ||
798 | */ | ||
799 | |||
800 | #ifdef CONFIG_CPU_S3C2440 | ||
801 | |||
802 | /* WDT/AC97 */ | ||
803 | |||
804 | static void s3c_irq_demux_wdtac97(unsigned int irq, | ||
805 | struct irqdesc *desc, | ||
806 | struct pt_regs *regs) | ||
807 | { | ||
808 | unsigned int subsrc, submsk; | ||
809 | struct irqdesc *mydesc; | ||
810 | |||
811 | /* read the current pending interrupts, and the mask | ||
812 | * for what it is available */ | ||
813 | |||
814 | subsrc = __raw_readl(S3C2410_SUBSRCPND); | ||
815 | submsk = __raw_readl(S3C2410_INTSUBMSK); | ||
816 | |||
817 | subsrc &= ~submsk; | ||
818 | subsrc >>= 13; | ||
819 | subsrc &= 3; | ||
820 | |||
821 | if (subsrc != 0) { | ||
822 | if (subsrc & 1) { | ||
823 | mydesc = irq_desc + IRQ_S3C2440_WDT; | ||
824 | mydesc->handle( IRQ_S3C2440_WDT, mydesc, regs); | ||
825 | } | ||
826 | if (subsrc & 2) { | ||
827 | mydesc = irq_desc + IRQ_S3C2440_AC97; | ||
828 | mydesc->handle(IRQ_S3C2440_AC97, mydesc, regs); | ||
829 | } | ||
830 | } | ||
831 | } | ||
832 | |||
833 | |||
834 | #define INTMSK_WDT (1UL << (IRQ_WDT - IRQ_EINT0)) | ||
835 | |||
836 | static void | ||
837 | s3c_irq_wdtac97_mask(unsigned int irqno) | ||
838 | { | ||
839 | s3c_irqsub_mask(irqno, INTMSK_WDT, 3<<13); | ||
840 | } | ||
841 | |||
842 | static void | ||
843 | s3c_irq_wdtac97_unmask(unsigned int irqno) | ||
844 | { | ||
845 | s3c_irqsub_unmask(irqno, INTMSK_WDT); | ||
846 | } | ||
847 | |||
848 | static void | ||
849 | s3c_irq_wdtac97_ack(unsigned int irqno) | ||
850 | { | ||
851 | s3c_irqsub_maskack(irqno, INTMSK_WDT, 3<<13); | ||
852 | } | ||
853 | |||
854 | static struct irqchip s3c_irq_wdtac97 = { | ||
855 | .mask = s3c_irq_wdtac97_mask, | ||
856 | .unmask = s3c_irq_wdtac97_unmask, | ||
857 | .ack = s3c_irq_wdtac97_ack, | ||
858 | }; | ||
859 | |||
860 | /* camera irq */ | ||
861 | |||
862 | static void s3c_irq_demux_cam(unsigned int irq, | ||
863 | struct irqdesc *desc, | ||
864 | struct pt_regs *regs) | ||
865 | { | ||
866 | unsigned int subsrc, submsk; | ||
867 | struct irqdesc *mydesc; | ||
868 | |||
869 | /* read the current pending interrupts, and the mask | ||
870 | * for what it is available */ | ||
871 | |||
872 | subsrc = __raw_readl(S3C2410_SUBSRCPND); | ||
873 | submsk = __raw_readl(S3C2410_INTSUBMSK); | ||
874 | |||
875 | subsrc &= ~submsk; | ||
876 | subsrc >>= 11; | ||
877 | subsrc &= 3; | ||
878 | |||
879 | if (subsrc != 0) { | ||
880 | if (subsrc & 1) { | ||
881 | mydesc = irq_desc + IRQ_S3C2440_CAM_C; | ||
882 | mydesc->handle( IRQ_S3C2440_WDT, mydesc, regs); | ||
883 | } | ||
884 | if (subsrc & 2) { | ||
885 | mydesc = irq_desc + IRQ_S3C2440_CAM_P; | ||
886 | mydesc->handle(IRQ_S3C2440_AC97, mydesc, regs); | ||
887 | } | ||
888 | } | ||
889 | } | ||
890 | |||
891 | #define INTMSK_CAM (1UL << (IRQ_CAM - IRQ_EINT0)) | ||
892 | |||
893 | static void | ||
894 | s3c_irq_cam_mask(unsigned int irqno) | ||
895 | { | ||
896 | s3c_irqsub_mask(irqno, INTMSK_CAM, 3<<11); | ||
897 | } | ||
898 | |||
899 | static void | ||
900 | s3c_irq_cam_unmask(unsigned int irqno) | ||
901 | { | ||
902 | s3c_irqsub_unmask(irqno, INTMSK_CAM); | ||
903 | } | ||
904 | |||
905 | static void | ||
906 | s3c_irq_cam_ack(unsigned int irqno) | ||
907 | { | ||
908 | s3c_irqsub_maskack(irqno, INTMSK_CAM, 3<<11); | ||
909 | } | ||
910 | |||
911 | static struct irqchip s3c_irq_cam = { | ||
912 | .mask = s3c_irq_cam_mask, | ||
913 | .unmask = s3c_irq_cam_unmask, | ||
914 | .ack = s3c_irq_cam_ack, | ||
915 | }; | ||
916 | |||
917 | static int s3c2440_irq_add(struct sys_device *sysdev) | ||
918 | { | ||
919 | unsigned int irqno; | ||
920 | |||
921 | printk("S3C2440: IRQ Support\n"); | ||
922 | |||
923 | set_irq_chip(IRQ_NFCON, &s3c_irq_level_chip); | ||
924 | set_irq_handler(IRQ_NFCON, do_level_IRQ); | ||
925 | set_irq_flags(IRQ_NFCON, IRQF_VALID); | ||
926 | |||
927 | /* add new chained handler for wdt, ac7 */ | ||
928 | |||
929 | set_irq_chip(IRQ_WDT, &s3c_irq_level_chip); | ||
930 | set_irq_handler(IRQ_WDT, do_level_IRQ); | ||
931 | set_irq_chained_handler(IRQ_WDT, s3c_irq_demux_wdtac97); | ||
932 | |||
933 | for (irqno = IRQ_S3C2440_WDT; irqno <= IRQ_S3C2440_AC97; irqno++) { | ||
934 | set_irq_chip(irqno, &s3c_irq_wdtac97); | ||
935 | set_irq_handler(irqno, do_level_IRQ); | ||
936 | set_irq_flags(irqno, IRQF_VALID); | ||
937 | } | ||
938 | |||
939 | /* add chained handler for camera */ | ||
940 | |||
941 | set_irq_chip(IRQ_CAM, &s3c_irq_level_chip); | ||
942 | set_irq_handler(IRQ_CAM, do_level_IRQ); | ||
943 | set_irq_chained_handler(IRQ_CAM, s3c_irq_demux_cam); | ||
944 | |||
945 | for (irqno = IRQ_S3C2440_CAM_C; irqno <= IRQ_S3C2440_CAM_P; irqno++) { | ||
946 | set_irq_chip(irqno, &s3c_irq_cam); | ||
947 | set_irq_handler(irqno, do_level_IRQ); | ||
948 | set_irq_flags(irqno, IRQF_VALID); | ||
949 | } | ||
950 | |||
951 | return 0; | ||
952 | } | ||
953 | |||
954 | static struct sysdev_driver s3c2440_irq_driver = { | ||
955 | .add = s3c2440_irq_add, | ||
956 | }; | ||
957 | |||
958 | static int s3c24xx_irq_driver(void) | ||
959 | { | ||
960 | return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_irq_driver); | ||
961 | } | ||
962 | |||
963 | arch_initcall(s3c24xx_irq_driver); | ||
964 | |||
965 | #endif /* CONFIG_CPU_S3C2440 */ | ||
966 | |||
diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c new file mode 100644 index 000000000000..3bb97eb6e693 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-bast.c | |||
@@ -0,0 +1,409 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/mach-bast.c | ||
2 | * | ||
3 | * Copyright (c) 2003-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * http://www.simtec.co.uk/products/EB2410ITX/ | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 14-Sep-2004 BJD USB power control | ||
14 | * 20-Aug-2004 BJD Added s3c2410_board struct | ||
15 | * 18-Aug-2004 BJD Added platform devices from default set | ||
16 | * 16-May-2003 BJD Created initial version | ||
17 | * 16-Aug-2003 BJD Fixed header files and copyright, added URL | ||
18 | * 05-Sep-2003 BJD Moved to v2.6 kernel | ||
19 | * 06-Jan-2003 BJD Updates for <arch/map.h> | ||
20 | * 18-Jan-2003 BJD Added serial port configuration | ||
21 | * 05-Oct-2004 BJD Power management code | ||
22 | * 04-Nov-2004 BJD Updated serial port clocks | ||
23 | * 04-Jan-2005 BJD New uart init call | ||
24 | * 10-Jan-2005 BJD Removed include of s3c2410.h | ||
25 | * 14-Jan-2005 BJD Add support for muitlple NAND devices | ||
26 | * 03-Mar-2005 BJD Ensured that bast-cpld.h is included | ||
27 | * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA | ||
28 | * 14-Mar-2006 BJD Updated for __iomem changes | ||
29 | */ | ||
30 | |||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/list.h> | ||
35 | #include <linux/timer.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/device.h> | ||
38 | |||
39 | #include <asm/mach/arch.h> | ||
40 | #include <asm/mach/map.h> | ||
41 | #include <asm/mach/irq.h> | ||
42 | |||
43 | #include <asm/arch/bast-map.h> | ||
44 | #include <asm/arch/bast-irq.h> | ||
45 | #include <asm/arch/bast-cpld.h> | ||
46 | |||
47 | #include <asm/hardware.h> | ||
48 | #include <asm/io.h> | ||
49 | #include <asm/irq.h> | ||
50 | #include <asm/mach-types.h> | ||
51 | |||
52 | //#include <asm/debug-ll.h> | ||
53 | #include <asm/arch/regs-serial.h> | ||
54 | #include <asm/arch/regs-gpio.h> | ||
55 | #include <asm/arch/regs-mem.h> | ||
56 | #include <asm/arch/nand.h> | ||
57 | |||
58 | #include <linux/mtd/mtd.h> | ||
59 | #include <linux/mtd/nand.h> | ||
60 | #include <linux/mtd/nand_ecc.h> | ||
61 | #include <linux/mtd/partitions.h> | ||
62 | |||
63 | #include "clock.h" | ||
64 | #include "devs.h" | ||
65 | #include "cpu.h" | ||
66 | #include "usb-simtec.h" | ||
67 | #include "pm.h" | ||
68 | |||
69 | #define COPYRIGHT ", (c) 2004-2005 Simtec Electronics" | ||
70 | |||
71 | /* macros for virtual address mods for the io space entries */ | ||
72 | #define VA_C5(item) ((unsigned long)(item) + BAST_VAM_CS5) | ||
73 | #define VA_C4(item) ((unsigned long)(item) + BAST_VAM_CS4) | ||
74 | #define VA_C3(item) ((unsigned long)(item) + BAST_VAM_CS3) | ||
75 | #define VA_C2(item) ((unsigned long)(item) + BAST_VAM_CS2) | ||
76 | |||
77 | /* macros to modify the physical addresses for io space */ | ||
78 | |||
79 | #define PA_CS2(item) ((item) + S3C2410_CS2) | ||
80 | #define PA_CS3(item) ((item) + S3C2410_CS3) | ||
81 | #define PA_CS4(item) ((item) + S3C2410_CS4) | ||
82 | #define PA_CS5(item) ((item) + S3C2410_CS5) | ||
83 | |||
84 | static struct map_desc bast_iodesc[] __initdata = { | ||
85 | /* ISA IO areas */ | ||
86 | |||
87 | { (u32)S3C24XX_VA_ISA_BYTE, PA_CS2(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, | ||
88 | { (u32)S3C24XX_VA_ISA_WORD, PA_CS3(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, | ||
89 | |||
90 | /* we could possibly compress the next set down into a set of smaller tables | ||
91 | * pagetables, but that would mean using an L2 section, and it still means | ||
92 | * we cannot actually feed the same register to an LDR due to 16K spacing | ||
93 | */ | ||
94 | |||
95 | /* bast CPLD control registers, and external interrupt controls */ | ||
96 | { (u32)BAST_VA_CTRL1, BAST_PA_CTRL1, SZ_1M, MT_DEVICE }, | ||
97 | { (u32)BAST_VA_CTRL2, BAST_PA_CTRL2, SZ_1M, MT_DEVICE }, | ||
98 | { (u32)BAST_VA_CTRL3, BAST_PA_CTRL3, SZ_1M, MT_DEVICE }, | ||
99 | { (u32)BAST_VA_CTRL4, BAST_PA_CTRL4, SZ_1M, MT_DEVICE }, | ||
100 | |||
101 | /* PC104 IRQ mux */ | ||
102 | { (u32)BAST_VA_PC104_IRQREQ, BAST_PA_PC104_IRQREQ, SZ_1M, MT_DEVICE }, | ||
103 | { (u32)BAST_VA_PC104_IRQRAW, BAST_PA_PC104_IRQRAW, SZ_1M, MT_DEVICE }, | ||
104 | { (u32)BAST_VA_PC104_IRQMASK, BAST_PA_PC104_IRQMASK, SZ_1M, MT_DEVICE }, | ||
105 | |||
106 | /* peripheral space... one for each of fast/slow/byte/16bit */ | ||
107 | /* note, ide is only decoded in word space, even though some registers | ||
108 | * are only 8bit */ | ||
109 | |||
110 | /* slow, byte */ | ||
111 | { VA_C2(BAST_VA_ISAIO), PA_CS2(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, | ||
112 | { VA_C2(BAST_VA_ISAMEM), PA_CS2(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, | ||
113 | { VA_C2(BAST_VA_ASIXNET), PA_CS3(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, | ||
114 | { VA_C2(BAST_VA_SUPERIO), PA_CS2(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, | ||
115 | { VA_C2(BAST_VA_DM9000), PA_CS2(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, | ||
116 | { VA_C2(BAST_VA_IDEPRI), PA_CS3(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, | ||
117 | { VA_C2(BAST_VA_IDESEC), PA_CS3(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, | ||
118 | { VA_C2(BAST_VA_IDEPRIAUX), PA_CS3(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, | ||
119 | { VA_C2(BAST_VA_IDESECAUX), PA_CS3(BAST_PA_IDESECAUX), SZ_1M, MT_DEVICE }, | ||
120 | |||
121 | /* slow, word */ | ||
122 | { VA_C3(BAST_VA_ISAIO), PA_CS3(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, | ||
123 | { VA_C3(BAST_VA_ISAMEM), PA_CS3(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, | ||
124 | { VA_C3(BAST_VA_ASIXNET), PA_CS3(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, | ||
125 | { VA_C3(BAST_VA_SUPERIO), PA_CS3(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, | ||
126 | { VA_C3(BAST_VA_DM9000), PA_CS3(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, | ||
127 | { VA_C3(BAST_VA_IDEPRI), PA_CS3(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, | ||
128 | { VA_C3(BAST_VA_IDESEC), PA_CS3(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, | ||
129 | { VA_C3(BAST_VA_IDEPRIAUX), PA_CS3(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, | ||
130 | { VA_C3(BAST_VA_IDESECAUX), PA_CS3(BAST_PA_IDESECAUX), SZ_1M, MT_DEVICE }, | ||
131 | |||
132 | /* fast, byte */ | ||
133 | { VA_C4(BAST_VA_ISAIO), PA_CS4(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, | ||
134 | { VA_C4(BAST_VA_ISAMEM), PA_CS4(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, | ||
135 | { VA_C4(BAST_VA_ASIXNET), PA_CS5(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, | ||
136 | { VA_C4(BAST_VA_SUPERIO), PA_CS4(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, | ||
137 | { VA_C4(BAST_VA_DM9000), PA_CS4(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, | ||
138 | { VA_C4(BAST_VA_IDEPRI), PA_CS5(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, | ||
139 | { VA_C4(BAST_VA_IDESEC), PA_CS5(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, | ||
140 | { VA_C4(BAST_VA_IDEPRIAUX), PA_CS5(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, | ||
141 | { VA_C4(BAST_VA_IDESECAUX), PA_CS5(BAST_PA_IDESECAUX), SZ_1M, MT_DEVICE }, | ||
142 | |||
143 | /* fast, word */ | ||
144 | { VA_C5(BAST_VA_ISAIO), PA_CS5(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, | ||
145 | { VA_C5(BAST_VA_ISAMEM), PA_CS5(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, | ||
146 | { VA_C5(BAST_VA_ASIXNET), PA_CS5(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, | ||
147 | { VA_C5(BAST_VA_SUPERIO), PA_CS5(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, | ||
148 | { VA_C5(BAST_VA_DM9000), PA_CS5(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, | ||
149 | { VA_C5(BAST_VA_IDEPRI), PA_CS5(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, | ||
150 | { VA_C5(BAST_VA_IDESEC), PA_CS5(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, | ||
151 | { VA_C5(BAST_VA_IDEPRIAUX), PA_CS5(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, | ||
152 | { VA_C5(BAST_VA_IDESECAUX), PA_CS5(BAST_PA_IDESECAUX), SZ_1M, MT_DEVICE }, | ||
153 | }; | ||
154 | |||
155 | #define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK | ||
156 | #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB | ||
157 | #define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE | ||
158 | |||
159 | static struct s3c24xx_uart_clksrc bast_serial_clocks[] = { | ||
160 | [0] = { | ||
161 | .name = "uclk", | ||
162 | .divisor = 1, | ||
163 | .min_baud = 0, | ||
164 | .max_baud = 0, | ||
165 | }, | ||
166 | [1] = { | ||
167 | .name = "pclk", | ||
168 | .divisor = 1, | ||
169 | .min_baud = 0, | ||
170 | .max_baud = 0. | ||
171 | } | ||
172 | }; | ||
173 | |||
174 | |||
175 | static struct s3c2410_uartcfg bast_uartcfgs[] = { | ||
176 | [0] = { | ||
177 | .hwport = 0, | ||
178 | .flags = 0, | ||
179 | .ucon = UCON, | ||
180 | .ulcon = ULCON, | ||
181 | .ufcon = UFCON, | ||
182 | .clocks = bast_serial_clocks, | ||
183 | .clocks_size = ARRAY_SIZE(bast_serial_clocks) | ||
184 | }, | ||
185 | [1] = { | ||
186 | .hwport = 1, | ||
187 | .flags = 0, | ||
188 | .ucon = UCON, | ||
189 | .ulcon = ULCON, | ||
190 | .ufcon = UFCON, | ||
191 | .clocks = bast_serial_clocks, | ||
192 | .clocks_size = ARRAY_SIZE(bast_serial_clocks) | ||
193 | }, | ||
194 | /* port 2 is not actually used */ | ||
195 | [2] = { | ||
196 | .hwport = 2, | ||
197 | .flags = 0, | ||
198 | .ucon = UCON, | ||
199 | .ulcon = ULCON, | ||
200 | .ufcon = UFCON, | ||
201 | .clocks = bast_serial_clocks, | ||
202 | .clocks_size = ARRAY_SIZE(bast_serial_clocks) | ||
203 | } | ||
204 | }; | ||
205 | |||
206 | /* NOR Flash on BAST board */ | ||
207 | |||
208 | static struct resource bast_nor_resource[] = { | ||
209 | [0] = { | ||
210 | .start = S3C2410_CS1 + 0x4000000, | ||
211 | .end = S3C2410_CS1 + 0x4000000 + (32*1024*1024) - 1, | ||
212 | .flags = IORESOURCE_MEM, | ||
213 | } | ||
214 | }; | ||
215 | |||
216 | static struct platform_device bast_device_nor = { | ||
217 | .name = "bast-nor", | ||
218 | .id = -1, | ||
219 | .num_resources = ARRAY_SIZE(bast_nor_resource), | ||
220 | .resource = bast_nor_resource, | ||
221 | }; | ||
222 | |||
223 | /* NAND Flash on BAST board */ | ||
224 | |||
225 | |||
226 | static int smartmedia_map[] = { 0 }; | ||
227 | static int chip0_map[] = { 1 }; | ||
228 | static int chip1_map[] = { 2 }; | ||
229 | static int chip2_map[] = { 3 }; | ||
230 | |||
231 | struct mtd_partition bast_default_nand_part[] = { | ||
232 | [0] = { | ||
233 | .name = "Boot Agent", | ||
234 | .size = SZ_16K, | ||
235 | .offset = 0 | ||
236 | }, | ||
237 | [1] = { | ||
238 | .name = "/boot", | ||
239 | .size = SZ_4M - SZ_16K, | ||
240 | .offset = SZ_16K, | ||
241 | }, | ||
242 | [2] = { | ||
243 | .name = "user", | ||
244 | .offset = SZ_4M, | ||
245 | .size = MTDPART_SIZ_FULL, | ||
246 | } | ||
247 | }; | ||
248 | |||
249 | /* the bast has 4 selectable slots for nand-flash, the three | ||
250 | * on-board chip areas, as well as the external SmartMedia | ||
251 | * slot. | ||
252 | * | ||
253 | * Note, there is no current hot-plug support for the SmartMedia | ||
254 | * socket. | ||
255 | */ | ||
256 | |||
257 | static struct s3c2410_nand_set bast_nand_sets[] = { | ||
258 | [0] = { | ||
259 | .name = "SmartMedia", | ||
260 | .nr_chips = 1, | ||
261 | .nr_map = smartmedia_map, | ||
262 | .nr_partitions = ARRAY_SIZE(bast_default_nand_part), | ||
263 | .partitions = bast_default_nand_part | ||
264 | }, | ||
265 | [1] = { | ||
266 | .name = "chip0", | ||
267 | .nr_chips = 1, | ||
268 | .nr_map = chip0_map, | ||
269 | .nr_partitions = ARRAY_SIZE(bast_default_nand_part), | ||
270 | .partitions = bast_default_nand_part | ||
271 | }, | ||
272 | [2] = { | ||
273 | .name = "chip1", | ||
274 | .nr_chips = 1, | ||
275 | .nr_map = chip1_map, | ||
276 | .nr_partitions = ARRAY_SIZE(bast_default_nand_part), | ||
277 | .partitions = bast_default_nand_part | ||
278 | }, | ||
279 | [3] = { | ||
280 | .name = "chip2", | ||
281 | .nr_chips = 1, | ||
282 | .nr_map = chip2_map, | ||
283 | .nr_partitions = ARRAY_SIZE(bast_default_nand_part), | ||
284 | .partitions = bast_default_nand_part | ||
285 | } | ||
286 | }; | ||
287 | |||
288 | static void bast_nand_select(struct s3c2410_nand_set *set, int slot) | ||
289 | { | ||
290 | unsigned int tmp; | ||
291 | |||
292 | slot = set->nr_map[slot] & 3; | ||
293 | |||
294 | pr_debug("bast_nand: selecting slot %d (set %p,%p)\n", | ||
295 | slot, set, set->nr_map); | ||
296 | |||
297 | tmp = __raw_readb(BAST_VA_CTRL2); | ||
298 | tmp &= BAST_CPLD_CTLR2_IDERST; | ||
299 | tmp |= slot; | ||
300 | tmp |= BAST_CPLD_CTRL2_WNAND; | ||
301 | |||
302 | pr_debug("bast_nand: ctrl2 now %02x\n", tmp); | ||
303 | |||
304 | __raw_writeb(tmp, BAST_VA_CTRL2); | ||
305 | } | ||
306 | |||
307 | static struct s3c2410_platform_nand bast_nand_info = { | ||
308 | .tacls = 80, | ||
309 | .twrph0 = 80, | ||
310 | .twrph1 = 80, | ||
311 | .nr_sets = ARRAY_SIZE(bast_nand_sets), | ||
312 | .sets = bast_nand_sets, | ||
313 | .select_chip = bast_nand_select, | ||
314 | }; | ||
315 | |||
316 | |||
317 | /* Standard BAST devices */ | ||
318 | |||
319 | static struct platform_device *bast_devices[] __initdata = { | ||
320 | &s3c_device_usb, | ||
321 | &s3c_device_lcd, | ||
322 | &s3c_device_wdt, | ||
323 | &s3c_device_i2c, | ||
324 | &s3c_device_iis, | ||
325 | &s3c_device_rtc, | ||
326 | &s3c_device_nand, | ||
327 | &bast_device_nor | ||
328 | }; | ||
329 | |||
330 | static struct clk *bast_clocks[] = { | ||
331 | &s3c24xx_dclk0, | ||
332 | &s3c24xx_dclk1, | ||
333 | &s3c24xx_clkout0, | ||
334 | &s3c24xx_clkout1, | ||
335 | &s3c24xx_uclk, | ||
336 | }; | ||
337 | |||
338 | static struct s3c24xx_board bast_board __initdata = { | ||
339 | .devices = bast_devices, | ||
340 | .devices_count = ARRAY_SIZE(bast_devices), | ||
341 | .clocks = bast_clocks, | ||
342 | .clocks_count = ARRAY_SIZE(bast_clocks) | ||
343 | }; | ||
344 | |||
345 | void __init bast_map_io(void) | ||
346 | { | ||
347 | /* initialise the clocks */ | ||
348 | |||
349 | s3c24xx_dclk0.parent = NULL; | ||
350 | s3c24xx_dclk0.rate = 12*1000*1000; | ||
351 | |||
352 | s3c24xx_dclk1.parent = NULL; | ||
353 | s3c24xx_dclk1.rate = 24*1000*1000; | ||
354 | |||
355 | s3c24xx_clkout0.parent = &s3c24xx_dclk0; | ||
356 | s3c24xx_clkout1.parent = &s3c24xx_dclk1; | ||
357 | |||
358 | s3c24xx_uclk.parent = &s3c24xx_clkout1; | ||
359 | |||
360 | s3c_device_nand.dev.platform_data = &bast_nand_info; | ||
361 | |||
362 | s3c24xx_init_io(bast_iodesc, ARRAY_SIZE(bast_iodesc)); | ||
363 | s3c24xx_init_clocks(0); | ||
364 | s3c24xx_init_uarts(bast_uartcfgs, ARRAY_SIZE(bast_uartcfgs)); | ||
365 | s3c24xx_set_board(&bast_board); | ||
366 | usb_simtec_init(); | ||
367 | } | ||
368 | |||
369 | void __init bast_init_irq(void) | ||
370 | { | ||
371 | s3c24xx_init_irq(); | ||
372 | } | ||
373 | |||
374 | #ifdef CONFIG_PM | ||
375 | |||
376 | /* bast_init_machine | ||
377 | * | ||
378 | * enable the power management functions for the EB2410ITX | ||
379 | */ | ||
380 | |||
381 | static __init void bast_init_machine(void) | ||
382 | { | ||
383 | unsigned long gstatus4; | ||
384 | |||
385 | printk(KERN_INFO "BAST Power Manangement" COPYRIGHT "\n"); | ||
386 | |||
387 | gstatus4 = (__raw_readl(S3C2410_BANKCON7) & 0x3) << 30; | ||
388 | gstatus4 |= (__raw_readl(S3C2410_BANKCON6) & 0x3) << 28; | ||
389 | gstatus4 |= (__raw_readl(S3C2410_BANKSIZE) & S3C2410_BANKSIZE_MASK); | ||
390 | |||
391 | __raw_writel(gstatus4, S3C2410_GSTATUS4); | ||
392 | |||
393 | s3c2410_pm_init(); | ||
394 | } | ||
395 | |||
396 | #else | ||
397 | #define bast_init_machine NULL | ||
398 | #endif | ||
399 | |||
400 | |||
401 | MACHINE_START(BAST, "Simtec-BAST") | ||
402 | MAINTAINER("Ben Dooks <ben@simtec.co.uk>") | ||
403 | BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) | ||
404 | BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) | ||
405 | MAPIO(bast_map_io) | ||
406 | INITIRQ(bast_init_irq) | ||
407 | .init_machine = bast_init_machine, | ||
408 | .timer = &s3c24xx_timer, | ||
409 | MACHINE_END | ||
diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c new file mode 100644 index 000000000000..2924afc068a4 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-h1940.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/mach-h1940.c | ||
2 | * | ||
3 | * Copyright (c) 2003-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * http://www.handhelds.org/projects/h1940.html | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 16-May-2003 BJD Created initial version | ||
14 | * 16-Aug-2003 BJD Fixed header files and copyright, added URL | ||
15 | * 05-Sep-2003 BJD Moved to v2.6 kernel | ||
16 | * 06-Jan-2003 BJD Updates for <arch/map.h> | ||
17 | * 18-Jan-2003 BJD Added serial port configuration | ||
18 | * 17-Feb-2003 BJD Copied to mach-ipaq.c | ||
19 | * 21-Aug-2004 BJD Added struct s3c2410_board | ||
20 | * 04-Sep-2004 BJD Changed uart init, renamed ipaq_ -> h1940_ | ||
21 | * 18-Oct-2004 BJD Updated new board structure name | ||
22 | * 04-Nov-2004 BJD Change for new serial clock | ||
23 | * 04-Jan-2005 BJD Updated uart init call | ||
24 | * 10-Jan-2005 BJD Removed include of s3c2410.h | ||
25 | * 14-Jan-2005 BJD Added clock init | ||
26 | * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA | ||
27 | */ | ||
28 | |||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/list.h> | ||
33 | #include <linux/timer.h> | ||
34 | #include <linux/init.h> | ||
35 | |||
36 | #include <asm/mach/arch.h> | ||
37 | #include <asm/mach/map.h> | ||
38 | #include <asm/mach/irq.h> | ||
39 | |||
40 | #include <asm/hardware.h> | ||
41 | #include <asm/hardware/iomd.h> | ||
42 | #include <asm/io.h> | ||
43 | #include <asm/irq.h> | ||
44 | #include <asm/mach-types.h> | ||
45 | |||
46 | //#include <asm/debug-ll.h> | ||
47 | #include <asm/arch/regs-serial.h> | ||
48 | |||
49 | #include <linux/serial_core.h> | ||
50 | |||
51 | #include "clock.h" | ||
52 | #include "devs.h" | ||
53 | #include "cpu.h" | ||
54 | |||
55 | static struct map_desc h1940_iodesc[] __initdata = { | ||
56 | /* nothing here yet */ | ||
57 | }; | ||
58 | |||
59 | #define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK | ||
60 | #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB | ||
61 | #define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE | ||
62 | |||
63 | static struct s3c2410_uartcfg h1940_uartcfgs[] = { | ||
64 | [0] = { | ||
65 | .hwport = 0, | ||
66 | .flags = 0, | ||
67 | .ucon = 0x3c5, | ||
68 | .ulcon = 0x03, | ||
69 | .ufcon = 0x51, | ||
70 | }, | ||
71 | [1] = { | ||
72 | .hwport = 1, | ||
73 | .flags = 0, | ||
74 | .ucon = 0x245, | ||
75 | .ulcon = 0x03, | ||
76 | .ufcon = 0x00, | ||
77 | }, | ||
78 | /* IR port */ | ||
79 | [2] = { | ||
80 | .hwport = 2, | ||
81 | .flags = 0, | ||
82 | .uart_flags = UPF_CONS_FLOW, | ||
83 | .ucon = 0x3c5, | ||
84 | .ulcon = 0x43, | ||
85 | .ufcon = 0x51, | ||
86 | } | ||
87 | }; | ||
88 | |||
89 | |||
90 | |||
91 | |||
92 | static struct platform_device *h1940_devices[] __initdata = { | ||
93 | &s3c_device_usb, | ||
94 | &s3c_device_lcd, | ||
95 | &s3c_device_wdt, | ||
96 | &s3c_device_i2c, | ||
97 | &s3c_device_iis, | ||
98 | }; | ||
99 | |||
100 | static struct s3c24xx_board h1940_board __initdata = { | ||
101 | .devices = h1940_devices, | ||
102 | .devices_count = ARRAY_SIZE(h1940_devices) | ||
103 | }; | ||
104 | |||
105 | void __init h1940_map_io(void) | ||
106 | { | ||
107 | s3c24xx_init_io(h1940_iodesc, ARRAY_SIZE(h1940_iodesc)); | ||
108 | s3c24xx_init_clocks(0); | ||
109 | s3c24xx_init_uarts(h1940_uartcfgs, ARRAY_SIZE(h1940_uartcfgs)); | ||
110 | s3c24xx_set_board(&h1940_board); | ||
111 | } | ||
112 | |||
113 | void __init h1940_init_irq(void) | ||
114 | { | ||
115 | s3c24xx_init_irq(); | ||
116 | |||
117 | } | ||
118 | |||
119 | MACHINE_START(H1940, "IPAQ-H1940") | ||
120 | MAINTAINER("Ben Dooks <ben@fluff.org>") | ||
121 | BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) | ||
122 | BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) | ||
123 | MAPIO(h1940_map_io) | ||
124 | INITIRQ(h1940_init_irq) | ||
125 | .timer = &s3c24xx_timer, | ||
126 | MACHINE_END | ||
diff --git a/arch/arm/mach-s3c2410/mach-n30.c b/arch/arm/mach-s3c2410/mach-n30.c new file mode 100644 index 000000000000..bd15998c129b --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-n30.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/mach-n30.c | ||
2 | * | ||
3 | * Copyright (c) 2003-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Copyright (c) 2005 Christer Weinigel <christer@weinigel.se> | ||
7 | * | ||
8 | * There is a wiki with more information about the n30 port at | ||
9 | * http://handhelds.org/moin/moin.cgi/AcerN30Documentation . | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/list.h> | ||
20 | #include <linux/timer.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/device.h> | ||
24 | #include <linux/kthread.h> | ||
25 | |||
26 | #include <asm/mach/arch.h> | ||
27 | #include <asm/mach/map.h> | ||
28 | #include <asm/mach/irq.h> | ||
29 | |||
30 | #include <asm/hardware.h> | ||
31 | #include <asm/hardware/iomd.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/mach-types.h> | ||
35 | |||
36 | #include <asm/arch/regs-serial.h> | ||
37 | #include <asm/arch/regs-gpio.h> | ||
38 | #include <asm/arch/iic.h> | ||
39 | |||
40 | #include <linux/serial_core.h> | ||
41 | |||
42 | #include "s3c2410.h" | ||
43 | #include "clock.h" | ||
44 | #include "devs.h" | ||
45 | #include "cpu.h" | ||
46 | |||
47 | static struct map_desc n30_iodesc[] __initdata = { | ||
48 | /* nothing here yet */ | ||
49 | }; | ||
50 | |||
51 | static struct s3c2410_uartcfg n30_uartcfgs[] = { | ||
52 | /* Normal serial port */ | ||
53 | [0] = { | ||
54 | .hwport = 0, | ||
55 | .flags = 0, | ||
56 | .ucon = 0x2c5, | ||
57 | .ulcon = 0x03, | ||
58 | .ufcon = 0x51, | ||
59 | }, | ||
60 | /* IR port */ | ||
61 | [1] = { | ||
62 | .hwport = 1, | ||
63 | .flags = 0, | ||
64 | .uart_flags = UPF_CONS_FLOW, | ||
65 | .ucon = 0x2c5, | ||
66 | .ulcon = 0x43, | ||
67 | .ufcon = 0x51, | ||
68 | }, | ||
69 | /* The BlueTooth controller is connected to port 2 */ | ||
70 | [2] = { | ||
71 | .hwport = 2, | ||
72 | .flags = 0, | ||
73 | .ucon = 0x2c5, | ||
74 | .ulcon = 0x03, | ||
75 | .ufcon = 0x51, | ||
76 | }, | ||
77 | }; | ||
78 | |||
79 | static struct platform_device *n30_devices[] __initdata = { | ||
80 | &s3c_device_usb, | ||
81 | &s3c_device_lcd, | ||
82 | &s3c_device_wdt, | ||
83 | &s3c_device_i2c, | ||
84 | &s3c_device_iis, | ||
85 | &s3c_device_usbgadget, | ||
86 | }; | ||
87 | |||
88 | static struct s3c2410_platform_i2c n30_i2ccfg = { | ||
89 | .flags = 0, | ||
90 | .slave_addr = 0x10, | ||
91 | .bus_freq = 10*1000, | ||
92 | .max_freq = 10*1000, | ||
93 | }; | ||
94 | |||
95 | static struct s3c24xx_board n30_board __initdata = { | ||
96 | .devices = n30_devices, | ||
97 | .devices_count = ARRAY_SIZE(n30_devices) | ||
98 | }; | ||
99 | |||
100 | void __init n30_map_io(void) | ||
101 | { | ||
102 | s3c24xx_init_io(n30_iodesc, ARRAY_SIZE(n30_iodesc)); | ||
103 | s3c24xx_init_clocks(0); | ||
104 | s3c24xx_init_uarts(n30_uartcfgs, ARRAY_SIZE(n30_uartcfgs)); | ||
105 | s3c24xx_set_board(&n30_board); | ||
106 | } | ||
107 | |||
108 | void __init n30_init_irq(void) | ||
109 | { | ||
110 | s3c24xx_init_irq(); | ||
111 | } | ||
112 | |||
113 | |||
114 | static int n30_usbstart_thread(void *unused) | ||
115 | { | ||
116 | /* Turn off suspend on both USB ports, and switch the | ||
117 | * selectable USB port to USB device mode. */ | ||
118 | writel(readl(S3C2410_MISCCR) & ~0x00003008, S3C2410_MISCCR); | ||
119 | |||
120 | /* Turn off the D+ pull up for 3 seconds so that the USB host | ||
121 | * at the other end will do a rescan of the USB bus. */ | ||
122 | s3c2410_gpio_setpin(S3C2410_GPB3, 0); | ||
123 | |||
124 | msleep_interruptible(3*HZ); | ||
125 | |||
126 | s3c2410_gpio_setpin(S3C2410_GPB3, 1); | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | |||
132 | void __init n30_init(void) | ||
133 | { | ||
134 | s3c_device_i2c.dev.platform_data = &n30_i2ccfg; | ||
135 | |||
136 | kthread_run(n30_usbstart_thread, NULL, "n30_usbstart"); | ||
137 | } | ||
138 | |||
139 | MACHINE_START(N30, "Acer-N30") | ||
140 | MAINTAINER("Christer Weinigel <christer@weinigel.se>, Ben Dooks <ben-linux@fluff.org>") | ||
141 | BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) | ||
142 | BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) | ||
143 | |||
144 | .timer = &s3c24xx_timer, | ||
145 | .init_machine = n30_init, | ||
146 | .init_irq = n30_init_irq, | ||
147 | .map_io = n30_map_io, | ||
148 | MACHINE_END | ||
149 | |||
150 | /* | ||
151 | Local variables: | ||
152 | compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.." | ||
153 | c-basic-offset: 8 | ||
154 | End: | ||
155 | */ | ||
diff --git a/arch/arm/mach-s3c2410/mach-nexcoder.c b/arch/arm/mach-s3c2410/mach-nexcoder.c new file mode 100644 index 000000000000..70487bf4b71e --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-nexcoder.c | |||
@@ -0,0 +1,156 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/mach-nexcoder.c | ||
2 | * | ||
3 | * Copyright (c) 2004 Nex Vision | ||
4 | * Guillaume GOURAT <guillaume.gourat@nexvision.tv> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * Modifications: | ||
11 | * 15-10-2004 GG Created initial version | ||
12 | * 12-03-2005 BJD Updated for release | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/timer.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/string.h> | ||
22 | #include <linux/device.h> | ||
23 | |||
24 | #include <linux/mtd/map.h> | ||
25 | |||
26 | #include <asm/mach/arch.h> | ||
27 | #include <asm/mach/map.h> | ||
28 | #include <asm/mach/irq.h> | ||
29 | |||
30 | #include <asm/setup.h> | ||
31 | #include <asm/hardware.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/mach-types.h> | ||
35 | |||
36 | //#include <asm/debug-ll.h> | ||
37 | #include <asm/arch/regs-gpio.h> | ||
38 | #include <asm/arch/regs-serial.h> | ||
39 | |||
40 | #include "s3c2410.h" | ||
41 | #include "s3c2440.h" | ||
42 | #include "clock.h" | ||
43 | #include "devs.h" | ||
44 | #include "cpu.h" | ||
45 | |||
46 | static struct map_desc nexcoder_iodesc[] __initdata = { | ||
47 | /* nothing here yet */ | ||
48 | }; | ||
49 | |||
50 | #define UCON S3C2410_UCON_DEFAULT | ||
51 | #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB | ||
52 | #define UFCON S3C2410_UFCON_RXTRIG12 | S3C2410_UFCON_FIFOMODE | ||
53 | |||
54 | static struct s3c2410_uartcfg nexcoder_uartcfgs[] = { | ||
55 | [0] = { | ||
56 | .hwport = 0, | ||
57 | .flags = 0, | ||
58 | .ucon = UCON, | ||
59 | .ulcon = ULCON, | ||
60 | .ufcon = UFCON, | ||
61 | }, | ||
62 | [1] = { | ||
63 | .hwport = 1, | ||
64 | .flags = 0, | ||
65 | .ucon = UCON, | ||
66 | .ulcon = ULCON, | ||
67 | .ufcon = UFCON, | ||
68 | }, | ||
69 | [2] = { | ||
70 | .hwport = 2, | ||
71 | .flags = 0, | ||
72 | .ucon = UCON, | ||
73 | .ulcon = ULCON, | ||
74 | .ufcon = UFCON, | ||
75 | } | ||
76 | }; | ||
77 | |||
78 | /* NOR Flash on NexVision NexCoder 2440 board */ | ||
79 | |||
80 | static struct resource nexcoder_nor_resource[] = { | ||
81 | [0] = { | ||
82 | .start = S3C2410_CS0, | ||
83 | .end = S3C2410_CS0 + (8*1024*1024) - 1, | ||
84 | .flags = IORESOURCE_MEM, | ||
85 | } | ||
86 | }; | ||
87 | |||
88 | static struct map_info nexcoder_nor_map = { | ||
89 | .bankwidth = 2, | ||
90 | }; | ||
91 | |||
92 | static struct platform_device nexcoder_device_nor = { | ||
93 | .name = "mtd-flash", | ||
94 | .id = -1, | ||
95 | .num_resources = ARRAY_SIZE(nexcoder_nor_resource), | ||
96 | .resource = nexcoder_nor_resource, | ||
97 | .dev = | ||
98 | { | ||
99 | .platform_data = &nexcoder_nor_map, | ||
100 | } | ||
101 | }; | ||
102 | |||
103 | /* Standard Nexcoder devices */ | ||
104 | |||
105 | static struct platform_device *nexcoder_devices[] __initdata = { | ||
106 | &s3c_device_usb, | ||
107 | &s3c_device_lcd, | ||
108 | &s3c_device_wdt, | ||
109 | &s3c_device_i2c, | ||
110 | &s3c_device_iis, | ||
111 | &s3c_device_rtc, | ||
112 | &s3c_device_camif, | ||
113 | &s3c_device_spi0, | ||
114 | &s3c_device_spi1, | ||
115 | &nexcoder_device_nor, | ||
116 | }; | ||
117 | |||
118 | static struct s3c24xx_board nexcoder_board __initdata = { | ||
119 | .devices = nexcoder_devices, | ||
120 | .devices_count = ARRAY_SIZE(nexcoder_devices), | ||
121 | }; | ||
122 | |||
123 | |||
124 | static void __init nexcoder_sensorboard_init(void) | ||
125 | { | ||
126 | // Initialize SCCB bus | ||
127 | s3c2410_gpio_setpin(S3C2410_GPE14, 1); // IICSCL | ||
128 | s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_OUTP); | ||
129 | s3c2410_gpio_setpin(S3C2410_GPE15, 1); // IICSDA | ||
130 | s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_OUTP); | ||
131 | |||
132 | // Power up the sensor board | ||
133 | s3c2410_gpio_setpin(S3C2410_GPF1, 1); | ||
134 | s3c2410_gpio_cfgpin(S3C2410_GPF1, S3C2410_GPF1_OUTP); // CAM_GPIO7 => nLDO_PWRDN | ||
135 | s3c2410_gpio_setpin(S3C2410_GPF2, 0); | ||
136 | s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_OUTP); // CAM_GPIO6 => CAM_PWRDN | ||
137 | } | ||
138 | |||
139 | void __init nexcoder_map_io(void) | ||
140 | { | ||
141 | s3c24xx_init_io(nexcoder_iodesc, ARRAY_SIZE(nexcoder_iodesc)); | ||
142 | s3c24xx_init_clocks(0); | ||
143 | s3c24xx_init_uarts(nexcoder_uartcfgs, ARRAY_SIZE(nexcoder_uartcfgs)); | ||
144 | s3c24xx_set_board(&nexcoder_board); | ||
145 | nexcoder_sensorboard_init(); | ||
146 | } | ||
147 | |||
148 | |||
149 | MACHINE_START(NEXCODER_2440, "NexVision - Nexcoder 2440") | ||
150 | MAINTAINER("Guillaume GOURAT <guillaume.gourat@nexvision.tv>") | ||
151 | BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) | ||
152 | BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) | ||
153 | .map_io = nexcoder_map_io, | ||
154 | .init_irq = s3c24xx_init_irq, | ||
155 | .timer = &s3c24xx_timer, | ||
156 | MACHINE_END | ||
diff --git a/arch/arm/mach-s3c2410/mach-otom.c b/arch/arm/mach-s3c2410/mach-otom.c new file mode 100644 index 000000000000..67d8ce8fb00f --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-otom.c | |||
@@ -0,0 +1,124 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/mach-otom.c | ||
2 | * | ||
3 | * Copyright (c) 2004 Nex Vision | ||
4 | * Guillaume GOURAT <guillaume.gourat@nexvision.fr> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/list.h> | ||
16 | #include <linux/timer.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/device.h> | ||
19 | |||
20 | #include <asm/mach/arch.h> | ||
21 | #include <asm/mach/map.h> | ||
22 | #include <asm/mach/irq.h> | ||
23 | |||
24 | #include <asm/arch/otom-map.h> | ||
25 | |||
26 | #include <asm/hardware.h> | ||
27 | #include <asm/io.h> | ||
28 | #include <asm/irq.h> | ||
29 | #include <asm/mach-types.h> | ||
30 | |||
31 | #include <asm/arch/regs-serial.h> | ||
32 | #include <asm/arch/regs-gpio.h> | ||
33 | |||
34 | #include "s3c2410.h" | ||
35 | #include "clock.h" | ||
36 | #include "devs.h" | ||
37 | #include "cpu.h" | ||
38 | |||
39 | static struct map_desc otom11_iodesc[] __initdata = { | ||
40 | /* Device area */ | ||
41 | { (u32)OTOM_VA_CS8900A_BASE, OTOM_PA_CS8900A_BASE, SZ_16M, MT_DEVICE }, | ||
42 | }; | ||
43 | |||
44 | #define UCON S3C2410_UCON_DEFAULT | ||
45 | #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB | ||
46 | #define UFCON S3C2410_UFCON_RXTRIG12 | S3C2410_UFCON_FIFOMODE | ||
47 | |||
48 | static struct s3c2410_uartcfg otom11_uartcfgs[] = { | ||
49 | [0] = { | ||
50 | .hwport = 0, | ||
51 | .flags = 0, | ||
52 | .ucon = UCON, | ||
53 | .ulcon = ULCON, | ||
54 | .ufcon = UFCON, | ||
55 | }, | ||
56 | [1] = { | ||
57 | .hwport = 1, | ||
58 | .flags = 0, | ||
59 | .ucon = UCON, | ||
60 | .ulcon = ULCON, | ||
61 | .ufcon = UFCON, | ||
62 | }, | ||
63 | /* port 2 is not actually used */ | ||
64 | [2] = { | ||
65 | .hwport = 2, | ||
66 | .flags = 0, | ||
67 | .ucon = UCON, | ||
68 | .ulcon = ULCON, | ||
69 | .ufcon = UFCON, | ||
70 | } | ||
71 | }; | ||
72 | |||
73 | /* NOR Flash on NexVision OTOM board */ | ||
74 | |||
75 | static struct resource otom_nor_resource[] = { | ||
76 | [0] = { | ||
77 | .start = S3C2410_CS0, | ||
78 | .end = S3C2410_CS0 + (4*1024*1024) - 1, | ||
79 | .flags = IORESOURCE_MEM, | ||
80 | } | ||
81 | }; | ||
82 | |||
83 | static struct platform_device otom_device_nor = { | ||
84 | .name = "mtd-flash", | ||
85 | .id = -1, | ||
86 | .num_resources = ARRAY_SIZE(otom_nor_resource), | ||
87 | .resource = otom_nor_resource, | ||
88 | }; | ||
89 | |||
90 | /* Standard OTOM devices */ | ||
91 | |||
92 | static struct platform_device *otom11_devices[] __initdata = { | ||
93 | &s3c_device_usb, | ||
94 | &s3c_device_lcd, | ||
95 | &s3c_device_wdt, | ||
96 | &s3c_device_i2c, | ||
97 | &s3c_device_iis, | ||
98 | &s3c_device_rtc, | ||
99 | &otom_device_nor, | ||
100 | }; | ||
101 | |||
102 | static struct s3c24xx_board otom11_board __initdata = { | ||
103 | .devices = otom11_devices, | ||
104 | .devices_count = ARRAY_SIZE(otom11_devices) | ||
105 | }; | ||
106 | |||
107 | |||
108 | void __init otom11_map_io(void) | ||
109 | { | ||
110 | s3c24xx_init_io(otom11_iodesc, ARRAY_SIZE(otom11_iodesc)); | ||
111 | s3c24xx_init_clocks(0); | ||
112 | s3c24xx_init_uarts(otom11_uartcfgs, ARRAY_SIZE(otom11_uartcfgs)); | ||
113 | s3c24xx_set_board(&otom11_board); | ||
114 | } | ||
115 | |||
116 | |||
117 | MACHINE_START(OTOM, "Nex Vision - Otom 1.1") | ||
118 | MAINTAINER("Guillaume GOURAT <guillaume.gourat@nexvision.tv>") | ||
119 | BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) | ||
120 | BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) | ||
121 | .map_io = otom11_map_io, | ||
122 | .init_irq = s3c24xx_init_irq, | ||
123 | .timer = &s3c24xx_timer, | ||
124 | MACHINE_END | ||
diff --git a/arch/arm/mach-s3c2410/mach-rx3715.c b/arch/arm/mach-s3c2410/mach-rx3715.c new file mode 100644 index 000000000000..f8d3a9784e71 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-rx3715.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/mach-rx3715.c | ||
2 | * | ||
3 | * Copyright (c) 2003,2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * http://www.handhelds.org/projects/rx3715.html | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 16-Sep-2004 BJD Copied from mach-h1940.c | ||
14 | * 25-Oct-2004 BJD Updates for 2.6.10-rc1 | ||
15 | * 10-Jan-2005 BJD Removed include of s3c2410.h s3c2440.h | ||
16 | * 14-Jan-2005 BJD Added new clock init | ||
17 | * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA | ||
18 | * 14-Mar-2005 BJD Fixed __iomem warnings | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <linux/timer.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/tty.h> | ||
28 | #include <linux/console.h> | ||
29 | #include <linux/serial_core.h> | ||
30 | #include <linux/serial.h> | ||
31 | |||
32 | #include <asm/mach/arch.h> | ||
33 | #include <asm/mach/map.h> | ||
34 | #include <asm/mach/irq.h> | ||
35 | |||
36 | #include <asm/hardware.h> | ||
37 | #include <asm/hardware/iomd.h> | ||
38 | #include <asm/io.h> | ||
39 | #include <asm/irq.h> | ||
40 | #include <asm/mach-types.h> | ||
41 | |||
42 | #include <asm/arch/regs-serial.h> | ||
43 | #include <asm/arch/regs-gpio.h> | ||
44 | |||
45 | #include "clock.h" | ||
46 | #include "devs.h" | ||
47 | #include "cpu.h" | ||
48 | #include "pm.h" | ||
49 | |||
50 | static struct map_desc rx3715_iodesc[] __initdata = { | ||
51 | /* dump ISA space somewhere unused */ | ||
52 | |||
53 | { (u32)S3C24XX_VA_ISA_WORD, S3C2410_CS3, SZ_16M, MT_DEVICE }, | ||
54 | { (u32)S3C24XX_VA_ISA_BYTE, S3C2410_CS3, SZ_16M, MT_DEVICE }, | ||
55 | }; | ||
56 | |||
57 | |||
58 | static struct s3c24xx_uart_clksrc rx3715_serial_clocks[] = { | ||
59 | [0] = { | ||
60 | .name = "fclk", | ||
61 | .divisor = 0, | ||
62 | .min_baud = 0, | ||
63 | .max_baud = 0, | ||
64 | } | ||
65 | }; | ||
66 | |||
67 | static struct s3c2410_uartcfg rx3715_uartcfgs[] = { | ||
68 | [0] = { | ||
69 | .hwport = 0, | ||
70 | .flags = 0, | ||
71 | .ucon = 0x3c5, | ||
72 | .ulcon = 0x03, | ||
73 | .ufcon = 0x51, | ||
74 | .clocks = rx3715_serial_clocks, | ||
75 | .clocks_size = ARRAY_SIZE(rx3715_serial_clocks), | ||
76 | }, | ||
77 | [1] = { | ||
78 | .hwport = 1, | ||
79 | .flags = 0, | ||
80 | .ucon = 0x3c5, | ||
81 | .ulcon = 0x03, | ||
82 | .ufcon = 0x00, | ||
83 | .clocks = rx3715_serial_clocks, | ||
84 | .clocks_size = ARRAY_SIZE(rx3715_serial_clocks), | ||
85 | }, | ||
86 | /* IR port */ | ||
87 | [2] = { | ||
88 | .hwport = 2, | ||
89 | .uart_flags = UPF_CONS_FLOW, | ||
90 | .ucon = 0x3c5, | ||
91 | .ulcon = 0x43, | ||
92 | .ufcon = 0x51, | ||
93 | .clocks = rx3715_serial_clocks, | ||
94 | .clocks_size = ARRAY_SIZE(rx3715_serial_clocks), | ||
95 | } | ||
96 | }; | ||
97 | |||
98 | static struct platform_device *rx3715_devices[] __initdata = { | ||
99 | &s3c_device_usb, | ||
100 | &s3c_device_lcd, | ||
101 | &s3c_device_wdt, | ||
102 | &s3c_device_i2c, | ||
103 | &s3c_device_iis, | ||
104 | }; | ||
105 | |||
106 | static struct s3c24xx_board rx3715_board __initdata = { | ||
107 | .devices = rx3715_devices, | ||
108 | .devices_count = ARRAY_SIZE(rx3715_devices) | ||
109 | }; | ||
110 | |||
111 | void __init rx3715_map_io(void) | ||
112 | { | ||
113 | s3c24xx_init_io(rx3715_iodesc, ARRAY_SIZE(rx3715_iodesc)); | ||
114 | s3c24xx_init_clocks(16934000); | ||
115 | s3c24xx_init_uarts(rx3715_uartcfgs, ARRAY_SIZE(rx3715_uartcfgs)); | ||
116 | s3c24xx_set_board(&rx3715_board); | ||
117 | } | ||
118 | |||
119 | void __init rx3715_init_irq(void) | ||
120 | { | ||
121 | s3c24xx_init_irq(); | ||
122 | } | ||
123 | |||
124 | #ifdef CONFIG_PM | ||
125 | static void __init rx3715_init_machine(void) | ||
126 | { | ||
127 | s3c2410_pm_init(); | ||
128 | } | ||
129 | #else | ||
130 | #define rx3715_init_machine NULL | ||
131 | #endif | ||
132 | |||
133 | MACHINE_START(RX3715, "IPAQ-RX3715") | ||
134 | MAINTAINER("Ben Dooks <ben@fluff.org>") | ||
135 | BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) | ||
136 | BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) | ||
137 | MAPIO(rx3715_map_io) | ||
138 | INITIRQ(rx3715_init_irq) | ||
139 | INIT_MACHINE(rx3715_init_machine) | ||
140 | .timer = &s3c24xx_timer, | ||
141 | MACHINE_END | ||
diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c new file mode 100644 index 000000000000..c1a4a1420ea0 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-smdk2410.c | |||
@@ -0,0 +1,123 @@ | |||
1 | /*********************************************************************** | ||
2 | * | ||
3 | * linux/arch/arm/mach-s3c2410/mach-smdk2410.c | ||
4 | * | ||
5 | * Copyright (C) 2004 by FS Forth-Systeme GmbH | ||
6 | * All rights reserved. | ||
7 | * | ||
8 | * $Id: mach-smdk2410.c,v 1.1 2004/05/11 14:15:38 mpietrek Exp $ | ||
9 | * @Author: Jonas Dietsche | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License as | ||
13 | * published by the Free Software Foundation; either version 2 of | ||
14 | * the License, or (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
24 | * MA 02111-1307 USA | ||
25 | * | ||
26 | * @History: | ||
27 | * derived from linux/arch/arm/mach-s3c2410/mach-bast.c, written by | ||
28 | * Ben Dooks <ben@simtec.co.uk> | ||
29 | * | ||
30 | * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA | ||
31 | * | ||
32 | ***********************************************************************/ | ||
33 | |||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/types.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | #include <linux/list.h> | ||
38 | #include <linux/timer.h> | ||
39 | #include <linux/init.h> | ||
40 | |||
41 | #include <asm/mach/arch.h> | ||
42 | #include <asm/mach/map.h> | ||
43 | #include <asm/mach/irq.h> | ||
44 | |||
45 | #include <asm/hardware.h> | ||
46 | #include <asm/io.h> | ||
47 | #include <asm/irq.h> | ||
48 | #include <asm/mach-types.h> | ||
49 | |||
50 | #include <asm/arch/regs-serial.h> | ||
51 | |||
52 | #include "devs.h" | ||
53 | #include "cpu.h" | ||
54 | |||
55 | static struct map_desc smdk2410_iodesc[] __initdata = { | ||
56 | /* nothing here yet */ | ||
57 | }; | ||
58 | |||
59 | #define UCON S3C2410_UCON_DEFAULT | ||
60 | #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB | ||
61 | #define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE | ||
62 | |||
63 | static struct s3c2410_uartcfg smdk2410_uartcfgs[] = { | ||
64 | [0] = { | ||
65 | .hwport = 0, | ||
66 | .flags = 0, | ||
67 | .ucon = UCON, | ||
68 | .ulcon = ULCON, | ||
69 | .ufcon = UFCON, | ||
70 | }, | ||
71 | [1] = { | ||
72 | .hwport = 1, | ||
73 | .flags = 0, | ||
74 | .ucon = UCON, | ||
75 | .ulcon = ULCON, | ||
76 | .ufcon = UFCON, | ||
77 | }, | ||
78 | [2] = { | ||
79 | .hwport = 2, | ||
80 | .flags = 0, | ||
81 | .ucon = UCON, | ||
82 | .ulcon = ULCON, | ||
83 | .ufcon = UFCON, | ||
84 | } | ||
85 | }; | ||
86 | |||
87 | static struct platform_device *smdk2410_devices[] __initdata = { | ||
88 | &s3c_device_usb, | ||
89 | &s3c_device_lcd, | ||
90 | &s3c_device_wdt, | ||
91 | &s3c_device_i2c, | ||
92 | &s3c_device_iis, | ||
93 | }; | ||
94 | |||
95 | static struct s3c24xx_board smdk2410_board __initdata = { | ||
96 | .devices = smdk2410_devices, | ||
97 | .devices_count = ARRAY_SIZE(smdk2410_devices) | ||
98 | }; | ||
99 | |||
100 | void __init smdk2410_map_io(void) | ||
101 | { | ||
102 | s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc)); | ||
103 | s3c24xx_init_clocks(0); | ||
104 | s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs)); | ||
105 | s3c24xx_set_board(&smdk2410_board); | ||
106 | } | ||
107 | |||
108 | void __init smdk2410_init_irq(void) | ||
109 | { | ||
110 | s3c24xx_init_irq(); | ||
111 | } | ||
112 | |||
113 | MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch | ||
114 | * to SMDK2410 */ | ||
115 | MAINTAINER("Jonas Dietsche") | ||
116 | BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) | ||
117 | BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) | ||
118 | MAPIO(smdk2410_map_io) | ||
119 | INITIRQ(smdk2410_init_irq) | ||
120 | .timer = &s3c24xx_timer, | ||
121 | MACHINE_END | ||
122 | |||
123 | |||
diff --git a/arch/arm/mach-s3c2410/mach-smdk2440.c b/arch/arm/mach-s3c2410/mach-smdk2440.c new file mode 100644 index 000000000000..7857176d9bcb --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-smdk2440.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/mach-smdk2440.c | ||
2 | * | ||
3 | * Copyright (c) 2004,2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * http://www.fluff.org/ben/smdk2440/ | ||
7 | * | ||
8 | * Thanks to Dimity Andric and TomTom for the loan of an SMDK2440. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * Modifications: | ||
15 | * 01-Nov-2004 BJD Initial version | ||
16 | * 12-Nov-2004 BJD Updated for release | ||
17 | * 04-Jan-2005 BJD Fixes for pre-release | ||
18 | * 22-Feb-2005 BJD Updated for 2.6.11-rc5 relesa | ||
19 | * 10-Mar-2005 LCVR Replaced S3C2410_VA by S3C24XX_VA | ||
20 | * 14-Mar-2005 BJD void __iomem fixes | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/list.h> | ||
27 | #include <linux/timer.h> | ||
28 | #include <linux/init.h> | ||
29 | |||
30 | #include <asm/mach/arch.h> | ||
31 | #include <asm/mach/map.h> | ||
32 | #include <asm/mach/irq.h> | ||
33 | |||
34 | #include <asm/hardware.h> | ||
35 | #include <asm/hardware/iomd.h> | ||
36 | #include <asm/io.h> | ||
37 | #include <asm/irq.h> | ||
38 | #include <asm/mach-types.h> | ||
39 | |||
40 | //#include <asm/debug-ll.h> | ||
41 | #include <asm/arch/regs-serial.h> | ||
42 | #include <asm/arch/regs-gpio.h> | ||
43 | #include <asm/arch/idle.h> | ||
44 | |||
45 | #include "s3c2410.h" | ||
46 | #include "s3c2440.h" | ||
47 | #include "clock.h" | ||
48 | #include "devs.h" | ||
49 | #include "cpu.h" | ||
50 | #include "pm.h" | ||
51 | |||
52 | static struct map_desc smdk2440_iodesc[] __initdata = { | ||
53 | /* ISA IO Space map (memory space selected by A24) */ | ||
54 | |||
55 | { (u32)S3C24XX_VA_ISA_WORD, S3C2410_CS2, SZ_16M, MT_DEVICE }, | ||
56 | { (u32)S3C24XX_VA_ISA_BYTE, S3C2410_CS2, SZ_16M, MT_DEVICE }, | ||
57 | }; | ||
58 | |||
59 | #define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK | ||
60 | #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB | ||
61 | #define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE | ||
62 | |||
63 | static struct s3c2410_uartcfg smdk2440_uartcfgs[] = { | ||
64 | [0] = { | ||
65 | .hwport = 0, | ||
66 | .flags = 0, | ||
67 | .ucon = 0x3c5, | ||
68 | .ulcon = 0x03, | ||
69 | .ufcon = 0x51, | ||
70 | }, | ||
71 | [1] = { | ||
72 | .hwport = 1, | ||
73 | .flags = 0, | ||
74 | .ucon = 0x3c5, | ||
75 | .ulcon = 0x03, | ||
76 | .ufcon = 0x51, | ||
77 | }, | ||
78 | /* IR port */ | ||
79 | [2] = { | ||
80 | .hwport = 2, | ||
81 | .flags = 0, | ||
82 | .ucon = 0x3c5, | ||
83 | .ulcon = 0x43, | ||
84 | .ufcon = 0x51, | ||
85 | } | ||
86 | }; | ||
87 | |||
88 | static struct platform_device *smdk2440_devices[] __initdata = { | ||
89 | &s3c_device_usb, | ||
90 | &s3c_device_lcd, | ||
91 | &s3c_device_wdt, | ||
92 | &s3c_device_i2c, | ||
93 | &s3c_device_iis, | ||
94 | }; | ||
95 | |||
96 | static struct s3c24xx_board smdk2440_board __initdata = { | ||
97 | .devices = smdk2440_devices, | ||
98 | .devices_count = ARRAY_SIZE(smdk2440_devices) | ||
99 | }; | ||
100 | |||
101 | void __init smdk2440_map_io(void) | ||
102 | { | ||
103 | s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc)); | ||
104 | s3c24xx_init_clocks(16934400); | ||
105 | s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs)); | ||
106 | s3c24xx_set_board(&smdk2440_board); | ||
107 | } | ||
108 | |||
109 | void __init smdk2440_machine_init(void) | ||
110 | { | ||
111 | /* Configure the LEDs (even if we have no LED support)*/ | ||
112 | |||
113 | s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); | ||
114 | s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); | ||
115 | s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); | ||
116 | s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP); | ||
117 | |||
118 | s3c2410_gpio_setpin(S3C2410_GPF4, 0); | ||
119 | s3c2410_gpio_setpin(S3C2410_GPF5, 0); | ||
120 | s3c2410_gpio_setpin(S3C2410_GPF6, 0); | ||
121 | s3c2410_gpio_setpin(S3C2410_GPF7, 0); | ||
122 | |||
123 | s3c2410_pm_init(); | ||
124 | } | ||
125 | |||
126 | MACHINE_START(S3C2440, "SMDK2440") | ||
127 | MAINTAINER("Ben Dooks <ben@fluff.org>") | ||
128 | BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) | ||
129 | BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) | ||
130 | |||
131 | .init_irq = s3c24xx_init_irq, | ||
132 | .map_io = smdk2440_map_io, | ||
133 | .init_machine = smdk2440_machine_init, | ||
134 | .timer = &s3c24xx_timer, | ||
135 | MACHINE_END | ||
diff --git a/arch/arm/mach-s3c2410/mach-vr1000.c b/arch/arm/mach-s3c2410/mach-vr1000.c new file mode 100644 index 000000000000..5512146b1ce4 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-vr1000.c | |||
@@ -0,0 +1,317 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/mach-vr1000.c | ||
2 | * | ||
3 | * Copyright (c) 2003-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Machine support for Thorcom VR1000 board. Designed for Thorcom by | ||
7 | * Simtec Electronics, http://www.simtec.co.uk/ | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * Modifications: | ||
14 | * 14-Sep-2004 BJD USB Power control | ||
15 | * 04-Sep-2004 BJD Added new uart init, and io init | ||
16 | * 21-Aug-2004 BJD Added struct s3c2410_board | ||
17 | * 06-Aug-2004 BJD Fixed call to time initialisation | ||
18 | * 05-Apr-2004 BJD Copied to make mach-vr1000.c | ||
19 | * 18-Oct-2004 BJD Updated board struct | ||
20 | * 04-Nov-2004 BJD Clock and serial configuration update | ||
21 | * | ||
22 | * 04-Jan-2005 BJD Updated uart init call | ||
23 | * 10-Jan-2005 BJD Removed include of s3c2410.h | ||
24 | * 14-Jan-2005 BJD Added clock init | ||
25 | * 15-Jan-2005 BJD Add serial port device definition | ||
26 | * 20-Jan-2005 BJD Use UPF_IOREMAP for ports | ||
27 | * 10-Feb-2005 BJD Added power-off capability | ||
28 | * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA | ||
29 | * 14-Mar-2006 BJD void __iomem fixes | ||
30 | */ | ||
31 | |||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | #include <linux/list.h> | ||
36 | #include <linux/timer.h> | ||
37 | #include <linux/init.h> | ||
38 | |||
39 | #include <linux/serial.h> | ||
40 | #include <linux/tty.h> | ||
41 | #include <linux/serial_8250.h> | ||
42 | #include <linux/serial_reg.h> | ||
43 | |||
44 | #include <asm/mach/arch.h> | ||
45 | #include <asm/mach/map.h> | ||
46 | #include <asm/mach/irq.h> | ||
47 | |||
48 | #include <asm/arch/bast-map.h> | ||
49 | #include <asm/arch/vr1000-map.h> | ||
50 | #include <asm/arch/vr1000-irq.h> | ||
51 | #include <asm/arch/vr1000-cpld.h> | ||
52 | |||
53 | #include <asm/hardware.h> | ||
54 | #include <asm/io.h> | ||
55 | #include <asm/irq.h> | ||
56 | #include <asm/mach-types.h> | ||
57 | |||
58 | #include <asm/arch/regs-serial.h> | ||
59 | #include <asm/arch/regs-gpio.h> | ||
60 | |||
61 | #include "clock.h" | ||
62 | #include "devs.h" | ||
63 | #include "cpu.h" | ||
64 | #include "usb-simtec.h" | ||
65 | |||
66 | /* macros for virtual address mods for the io space entries */ | ||
67 | #define VA_C5(item) ((unsigned long)(item) + BAST_VAM_CS5) | ||
68 | #define VA_C4(item) ((unsigned long)(item) + BAST_VAM_CS4) | ||
69 | #define VA_C3(item) ((unsigned long)(item) + BAST_VAM_CS3) | ||
70 | #define VA_C2(item) ((unsigned long)(item) + BAST_VAM_CS2) | ||
71 | |||
72 | /* macros to modify the physical addresses for io space */ | ||
73 | |||
74 | #define PA_CS2(item) ((item) + S3C2410_CS2) | ||
75 | #define PA_CS3(item) ((item) + S3C2410_CS3) | ||
76 | #define PA_CS4(item) ((item) + S3C2410_CS4) | ||
77 | #define PA_CS5(item) ((item) + S3C2410_CS5) | ||
78 | |||
79 | static struct map_desc vr1000_iodesc[] __initdata = { | ||
80 | /* ISA IO areas */ | ||
81 | |||
82 | { (u32)S3C24XX_VA_ISA_BYTE, PA_CS2(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, | ||
83 | { (u32)S3C24XX_VA_ISA_WORD, PA_CS3(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, | ||
84 | |||
85 | /* we could possibly compress the next set down into a set of smaller tables | ||
86 | * pagetables, but that would mean using an L2 section, and it still means | ||
87 | * we cannot actually feed the same register to an LDR due to 16K spacing | ||
88 | */ | ||
89 | |||
90 | /* bast CPLD control registers, and external interrupt controls */ | ||
91 | { (u32)VR1000_VA_CTRL1, VR1000_PA_CTRL1, SZ_1M, MT_DEVICE }, | ||
92 | { (u32)VR1000_VA_CTRL2, VR1000_PA_CTRL2, SZ_1M, MT_DEVICE }, | ||
93 | { (u32)VR1000_VA_CTRL3, VR1000_PA_CTRL3, SZ_1M, MT_DEVICE }, | ||
94 | { (u32)VR1000_VA_CTRL4, VR1000_PA_CTRL4, SZ_1M, MT_DEVICE }, | ||
95 | |||
96 | /* peripheral space... one for each of fast/slow/byte/16bit */ | ||
97 | /* note, ide is only decoded in word space, even though some registers | ||
98 | * are only 8bit */ | ||
99 | |||
100 | /* slow, byte */ | ||
101 | { VA_C2(VR1000_VA_DM9000), PA_CS2(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, | ||
102 | { VA_C2(VR1000_VA_IDEPRI), PA_CS3(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, | ||
103 | { VA_C2(VR1000_VA_IDESEC), PA_CS3(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, | ||
104 | { VA_C2(VR1000_VA_IDEPRIAUX), PA_CS3(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, | ||
105 | { VA_C2(VR1000_VA_IDESECAUX), PA_CS3(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, | ||
106 | |||
107 | /* slow, word */ | ||
108 | { VA_C3(VR1000_VA_DM9000), PA_CS3(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, | ||
109 | { VA_C3(VR1000_VA_IDEPRI), PA_CS3(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, | ||
110 | { VA_C3(VR1000_VA_IDESEC), PA_CS3(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, | ||
111 | { VA_C3(VR1000_VA_IDEPRIAUX), PA_CS3(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, | ||
112 | { VA_C3(VR1000_VA_IDESECAUX), PA_CS3(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, | ||
113 | |||
114 | /* fast, byte */ | ||
115 | { VA_C4(VR1000_VA_DM9000), PA_CS4(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, | ||
116 | { VA_C4(VR1000_VA_IDEPRI), PA_CS5(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, | ||
117 | { VA_C4(VR1000_VA_IDESEC), PA_CS5(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, | ||
118 | { VA_C4(VR1000_VA_IDEPRIAUX), PA_CS5(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, | ||
119 | { VA_C4(VR1000_VA_IDESECAUX), PA_CS5(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, | ||
120 | |||
121 | /* fast, word */ | ||
122 | { VA_C5(VR1000_VA_DM9000), PA_CS5(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, | ||
123 | { VA_C5(VR1000_VA_IDEPRI), PA_CS5(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, | ||
124 | { VA_C5(VR1000_VA_IDESEC), PA_CS5(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, | ||
125 | { VA_C5(VR1000_VA_IDEPRIAUX), PA_CS5(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, | ||
126 | { VA_C5(VR1000_VA_IDESECAUX), PA_CS5(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, | ||
127 | }; | ||
128 | |||
129 | #define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK | ||
130 | #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB | ||
131 | #define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE | ||
132 | |||
133 | /* uart clock source(s) */ | ||
134 | |||
135 | static struct s3c24xx_uart_clksrc vr1000_serial_clocks[] = { | ||
136 | [0] = { | ||
137 | .name = "uclk", | ||
138 | .divisor = 1, | ||
139 | .min_baud = 0, | ||
140 | .max_baud = 0, | ||
141 | }, | ||
142 | [1] = { | ||
143 | .name = "pclk", | ||
144 | .divisor = 1, | ||
145 | .min_baud = 0, | ||
146 | .max_baud = 0. | ||
147 | } | ||
148 | }; | ||
149 | |||
150 | static struct s3c2410_uartcfg vr1000_uartcfgs[] = { | ||
151 | [0] = { | ||
152 | .hwport = 0, | ||
153 | .flags = 0, | ||
154 | .ucon = UCON, | ||
155 | .ulcon = ULCON, | ||
156 | .ufcon = UFCON, | ||
157 | .clocks = vr1000_serial_clocks, | ||
158 | .clocks_size = ARRAY_SIZE(vr1000_serial_clocks), | ||
159 | }, | ||
160 | [1] = { | ||
161 | .hwport = 1, | ||
162 | .flags = 0, | ||
163 | .ucon = UCON, | ||
164 | .ulcon = ULCON, | ||
165 | .ufcon = UFCON, | ||
166 | .clocks = vr1000_serial_clocks, | ||
167 | .clocks_size = ARRAY_SIZE(vr1000_serial_clocks), | ||
168 | }, | ||
169 | /* port 2 is not actually used */ | ||
170 | [2] = { | ||
171 | .hwport = 2, | ||
172 | .flags = 0, | ||
173 | .ucon = UCON, | ||
174 | .ulcon = ULCON, | ||
175 | .ufcon = UFCON, | ||
176 | .clocks = vr1000_serial_clocks, | ||
177 | .clocks_size = ARRAY_SIZE(vr1000_serial_clocks), | ||
178 | |||
179 | } | ||
180 | }; | ||
181 | |||
182 | /* definitions for the vr1000 extra 16550 serial ports */ | ||
183 | |||
184 | #define VR1000_BAUDBASE (3692307) | ||
185 | |||
186 | #define VR1000_SERIAL_MAPBASE(x) (VR1000_PA_SERIAL + 0x80 + ((x) << 5)) | ||
187 | |||
188 | static struct plat_serial8250_port serial_platform_data[] = { | ||
189 | [0] = { | ||
190 | .mapbase = VR1000_SERIAL_MAPBASE(0), | ||
191 | .irq = IRQ_VR1000_SERIAL + 0, | ||
192 | .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, | ||
193 | .iotype = UPIO_MEM, | ||
194 | .regshift = 0, | ||
195 | .uartclk = VR1000_BAUDBASE, | ||
196 | }, | ||
197 | [1] = { | ||
198 | .mapbase = VR1000_SERIAL_MAPBASE(1), | ||
199 | .irq = IRQ_VR1000_SERIAL + 1, | ||
200 | .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, | ||
201 | .iotype = UPIO_MEM, | ||
202 | .regshift = 0, | ||
203 | .uartclk = VR1000_BAUDBASE, | ||
204 | }, | ||
205 | [2] = { | ||
206 | .mapbase = VR1000_SERIAL_MAPBASE(2), | ||
207 | .irq = IRQ_VR1000_SERIAL + 2, | ||
208 | .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, | ||
209 | .iotype = UPIO_MEM, | ||
210 | .regshift = 0, | ||
211 | .uartclk = VR1000_BAUDBASE, | ||
212 | }, | ||
213 | [3] = { | ||
214 | .mapbase = VR1000_SERIAL_MAPBASE(3), | ||
215 | .irq = IRQ_VR1000_SERIAL + 3, | ||
216 | .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, | ||
217 | .iotype = UPIO_MEM, | ||
218 | .regshift = 0, | ||
219 | .uartclk = VR1000_BAUDBASE, | ||
220 | }, | ||
221 | { }, | ||
222 | }; | ||
223 | |||
224 | static struct platform_device serial_device = { | ||
225 | .name = "serial8250", | ||
226 | .id = 0, | ||
227 | .dev = { | ||
228 | .platform_data = serial_platform_data, | ||
229 | }, | ||
230 | }; | ||
231 | |||
232 | /* MTD NOR Flash */ | ||
233 | |||
234 | static struct resource vr1000_nor_resource[] = { | ||
235 | [0] = { | ||
236 | .start = S3C2410_CS1 + 0x4000000, | ||
237 | .end = S3C2410_CS1 + 0x4000000 + SZ_16M - 1, | ||
238 | .flags = IORESOURCE_MEM, | ||
239 | } | ||
240 | }; | ||
241 | |||
242 | static struct platform_device vr1000_nor = { | ||
243 | .name = "bast-nor", | ||
244 | .id = -1, | ||
245 | .num_resources = ARRAY_SIZE(vr1000_nor_resource), | ||
246 | .resource = vr1000_nor_resource, | ||
247 | }; | ||
248 | |||
249 | |||
250 | static struct platform_device *vr1000_devices[] __initdata = { | ||
251 | &s3c_device_usb, | ||
252 | &s3c_device_lcd, | ||
253 | &s3c_device_wdt, | ||
254 | &s3c_device_i2c, | ||
255 | &s3c_device_iis, | ||
256 | &serial_device, | ||
257 | &vr1000_nor, | ||
258 | }; | ||
259 | |||
260 | static struct clk *vr1000_clocks[] = { | ||
261 | &s3c24xx_dclk0, | ||
262 | &s3c24xx_dclk1, | ||
263 | &s3c24xx_clkout0, | ||
264 | &s3c24xx_clkout1, | ||
265 | &s3c24xx_uclk, | ||
266 | }; | ||
267 | |||
268 | static struct s3c24xx_board vr1000_board __initdata = { | ||
269 | .devices = vr1000_devices, | ||
270 | .devices_count = ARRAY_SIZE(vr1000_devices), | ||
271 | .clocks = vr1000_clocks, | ||
272 | .clocks_count = ARRAY_SIZE(vr1000_clocks), | ||
273 | }; | ||
274 | |||
275 | static void vr1000_power_off(void) | ||
276 | { | ||
277 | s3c2410_gpio_cfgpin(S3C2410_GPB9, S3C2410_GPB9_OUTP); | ||
278 | s3c2410_gpio_setpin(S3C2410_GPB9, 1); | ||
279 | } | ||
280 | |||
281 | void __init vr1000_map_io(void) | ||
282 | { | ||
283 | /* initialise clock sources */ | ||
284 | |||
285 | s3c24xx_dclk0.parent = NULL; | ||
286 | s3c24xx_dclk0.rate = 12*1000*1000; | ||
287 | |||
288 | s3c24xx_dclk1.parent = NULL; | ||
289 | s3c24xx_dclk1.rate = 3692307; | ||
290 | |||
291 | s3c24xx_clkout0.parent = &s3c24xx_dclk0; | ||
292 | s3c24xx_clkout1.parent = &s3c24xx_dclk1; | ||
293 | |||
294 | s3c24xx_uclk.parent = &s3c24xx_clkout1; | ||
295 | |||
296 | pm_power_off = vr1000_power_off; | ||
297 | |||
298 | s3c24xx_init_io(vr1000_iodesc, ARRAY_SIZE(vr1000_iodesc)); | ||
299 | s3c24xx_init_clocks(0); | ||
300 | s3c24xx_init_uarts(vr1000_uartcfgs, ARRAY_SIZE(vr1000_uartcfgs)); | ||
301 | s3c24xx_set_board(&vr1000_board); | ||
302 | usb_simtec_init(); | ||
303 | } | ||
304 | |||
305 | void __init vr1000_init_irq(void) | ||
306 | { | ||
307 | s3c24xx_init_irq(); | ||
308 | } | ||
309 | |||
310 | MACHINE_START(VR1000, "Thorcom-VR1000") | ||
311 | MAINTAINER("Ben Dooks <ben@simtec.co.uk>") | ||
312 | BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) | ||
313 | BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) | ||
314 | MAPIO(vr1000_map_io) | ||
315 | INITIRQ(vr1000_init_irq) | ||
316 | .timer = &s3c24xx_timer, | ||
317 | MACHINE_END | ||
diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c new file mode 100644 index 000000000000..13a48ee77484 --- /dev/null +++ b/arch/arm/mach-s3c2410/pm.c | |||
@@ -0,0 +1,672 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/pm.c | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C2410 Power Manager (Suspend-To-RAM) support | ||
7 | * | ||
8 | * See Documentation/arm/Samsung-S3C24XX/Suspend.txt for more information | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | * Parts based on arch/arm/mach-pxa/pm.c | ||
25 | * | ||
26 | * Thanks to Dimitry Andric for debugging | ||
27 | * | ||
28 | * Modifications: | ||
29 | * 10-Mar-2005 LCVR Changed S3C2410_VA_UART to S3C24XX_VA_UART | ||
30 | */ | ||
31 | |||
32 | #include <linux/config.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/suspend.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/time.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/crc32.h> | ||
39 | #include <linux/ioport.h> | ||
40 | #include <linux/delay.h> | ||
41 | |||
42 | #include <asm/hardware.h> | ||
43 | #include <asm/io.h> | ||
44 | |||
45 | #include <asm/arch/regs-serial.h> | ||
46 | #include <asm/arch/regs-clock.h> | ||
47 | #include <asm/arch/regs-gpio.h> | ||
48 | #include <asm/arch/regs-mem.h> | ||
49 | #include <asm/arch/regs-irq.h> | ||
50 | |||
51 | #include <asm/mach/time.h> | ||
52 | |||
53 | #include "pm.h" | ||
54 | |||
55 | /* for external use */ | ||
56 | |||
57 | unsigned long s3c_pm_flags; | ||
58 | |||
59 | /* cache functions from arch/arm/mm/proc-arm920.S */ | ||
60 | |||
61 | extern void arm920_flush_kern_cache_all(void); | ||
62 | |||
63 | #define PFX "s3c24xx-pm: " | ||
64 | |||
65 | static struct sleep_save core_save[] = { | ||
66 | SAVE_ITEM(S3C2410_LOCKTIME), | ||
67 | SAVE_ITEM(S3C2410_CLKCON), | ||
68 | |||
69 | /* we restore the timings here, with the proviso that the board | ||
70 | * brings the system up in an slower, or equal frequency setting | ||
71 | * to the original system. | ||
72 | * | ||
73 | * if we cannot guarantee this, then things are going to go very | ||
74 | * wrong here, as we modify the refresh and both pll settings. | ||
75 | */ | ||
76 | |||
77 | SAVE_ITEM(S3C2410_BWSCON), | ||
78 | SAVE_ITEM(S3C2410_BANKCON0), | ||
79 | SAVE_ITEM(S3C2410_BANKCON1), | ||
80 | SAVE_ITEM(S3C2410_BANKCON2), | ||
81 | SAVE_ITEM(S3C2410_BANKCON3), | ||
82 | SAVE_ITEM(S3C2410_BANKCON4), | ||
83 | SAVE_ITEM(S3C2410_BANKCON5), | ||
84 | |||
85 | SAVE_ITEM(S3C2410_CLKDIVN), | ||
86 | SAVE_ITEM(S3C2410_MPLLCON), | ||
87 | SAVE_ITEM(S3C2410_UPLLCON), | ||
88 | SAVE_ITEM(S3C2410_CLKSLOW), | ||
89 | SAVE_ITEM(S3C2410_REFRESH), | ||
90 | }; | ||
91 | |||
92 | /* this lot should be really saved by the IRQ code */ | ||
93 | static struct sleep_save irq_save[] = { | ||
94 | SAVE_ITEM(S3C2410_EXTINT0), | ||
95 | SAVE_ITEM(S3C2410_EXTINT1), | ||
96 | SAVE_ITEM(S3C2410_EXTINT2), | ||
97 | SAVE_ITEM(S3C2410_EINFLT0), | ||
98 | SAVE_ITEM(S3C2410_EINFLT1), | ||
99 | SAVE_ITEM(S3C2410_EINFLT2), | ||
100 | SAVE_ITEM(S3C2410_EINFLT3), | ||
101 | SAVE_ITEM(S3C2410_EINTMASK), | ||
102 | SAVE_ITEM(S3C2410_INTMSK) | ||
103 | }; | ||
104 | |||
105 | static struct sleep_save gpio_save[] = { | ||
106 | SAVE_ITEM(S3C2410_GPACON), | ||
107 | SAVE_ITEM(S3C2410_GPADAT), | ||
108 | |||
109 | SAVE_ITEM(S3C2410_GPBCON), | ||
110 | SAVE_ITEM(S3C2410_GPBDAT), | ||
111 | SAVE_ITEM(S3C2410_GPBUP), | ||
112 | |||
113 | SAVE_ITEM(S3C2410_GPCCON), | ||
114 | SAVE_ITEM(S3C2410_GPCDAT), | ||
115 | SAVE_ITEM(S3C2410_GPCUP), | ||
116 | |||
117 | SAVE_ITEM(S3C2410_GPDCON), | ||
118 | SAVE_ITEM(S3C2410_GPDDAT), | ||
119 | SAVE_ITEM(S3C2410_GPDUP), | ||
120 | |||
121 | SAVE_ITEM(S3C2410_GPECON), | ||
122 | SAVE_ITEM(S3C2410_GPEDAT), | ||
123 | SAVE_ITEM(S3C2410_GPEUP), | ||
124 | |||
125 | SAVE_ITEM(S3C2410_GPFCON), | ||
126 | SAVE_ITEM(S3C2410_GPFDAT), | ||
127 | SAVE_ITEM(S3C2410_GPFUP), | ||
128 | |||
129 | SAVE_ITEM(S3C2410_GPGCON), | ||
130 | SAVE_ITEM(S3C2410_GPGDAT), | ||
131 | SAVE_ITEM(S3C2410_GPGUP), | ||
132 | |||
133 | SAVE_ITEM(S3C2410_GPHCON), | ||
134 | SAVE_ITEM(S3C2410_GPHDAT), | ||
135 | SAVE_ITEM(S3C2410_GPHUP), | ||
136 | |||
137 | SAVE_ITEM(S3C2410_DCLKCON), | ||
138 | }; | ||
139 | |||
140 | #ifdef CONFIG_S3C2410_PM_DEBUG | ||
141 | |||
142 | #define SAVE_UART(va) \ | ||
143 | SAVE_ITEM((va) + S3C2410_ULCON), \ | ||
144 | SAVE_ITEM((va) + S3C2410_UCON), \ | ||
145 | SAVE_ITEM((va) + S3C2410_UFCON), \ | ||
146 | SAVE_ITEM((va) + S3C2410_UMCON), \ | ||
147 | SAVE_ITEM((va) + S3C2410_UBRDIV) | ||
148 | |||
149 | static struct sleep_save uart_save[] = { | ||
150 | SAVE_UART(S3C24XX_VA_UART0), | ||
151 | SAVE_UART(S3C24XX_VA_UART1), | ||
152 | #ifndef CONFIG_CPU_S3C2400 | ||
153 | SAVE_UART(S3C24XX_VA_UART2), | ||
154 | #endif | ||
155 | }; | ||
156 | |||
157 | /* debug | ||
158 | * | ||
159 | * we send the debug to printascii() to allow it to be seen if the | ||
160 | * system never wakes up from the sleep | ||
161 | */ | ||
162 | |||
163 | extern void printascii(const char *); | ||
164 | |||
165 | static void pm_dbg(const char *fmt, ...) | ||
166 | { | ||
167 | va_list va; | ||
168 | char buff[256]; | ||
169 | |||
170 | va_start(va, fmt); | ||
171 | vsprintf(buff, fmt, va); | ||
172 | va_end(va); | ||
173 | |||
174 | printascii(buff); | ||
175 | } | ||
176 | |||
177 | static void s3c2410_pm_debug_init(void) | ||
178 | { | ||
179 | unsigned long tmp = __raw_readl(S3C2410_CLKCON); | ||
180 | |||
181 | /* re-start uart clocks */ | ||
182 | tmp |= S3C2410_CLKCON_UART0; | ||
183 | tmp |= S3C2410_CLKCON_UART1; | ||
184 | tmp |= S3C2410_CLKCON_UART2; | ||
185 | |||
186 | __raw_writel(tmp, S3C2410_CLKCON); | ||
187 | udelay(10); | ||
188 | } | ||
189 | |||
190 | #define DBG(fmt...) pm_dbg(fmt) | ||
191 | #else | ||
192 | #define DBG(fmt...) printk(KERN_DEBUG fmt) | ||
193 | |||
194 | #define s3c2410_pm_debug_init() do { } while(0) | ||
195 | |||
196 | static struct sleep_save uart_save[] = {}; | ||
197 | #endif | ||
198 | |||
199 | #if defined(CONFIG_S3C2410_PM_CHECK) && CONFIG_S3C2410_PM_CHECK_CHUNKSIZE != 0 | ||
200 | |||
201 | /* suspend checking code... | ||
202 | * | ||
203 | * this next area does a set of crc checks over all the installed | ||
204 | * memory, so the system can verify if the resume was ok. | ||
205 | * | ||
206 | * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, | ||
207 | * increasing it will mean that the area corrupted will be less easy to spot, | ||
208 | * and reducing the size will cause the CRC save area to grow | ||
209 | */ | ||
210 | |||
211 | #define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024) | ||
212 | |||
213 | static u32 crc_size; /* size needed for the crc block */ | ||
214 | static u32 *crcs; /* allocated over suspend/resume */ | ||
215 | |||
216 | typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); | ||
217 | |||
218 | /* s3c2410_pm_run_res | ||
219 | * | ||
220 | * go thorugh the given resource list, and look for system ram | ||
221 | */ | ||
222 | |||
223 | static void s3c2410_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) | ||
224 | { | ||
225 | while (ptr != NULL) { | ||
226 | if (ptr->child != NULL) | ||
227 | s3c2410_pm_run_res(ptr->child, fn, arg); | ||
228 | |||
229 | if ((ptr->flags & IORESOURCE_MEM) && | ||
230 | strcmp(ptr->name, "System RAM") == 0) { | ||
231 | DBG("Found system RAM at %08lx..%08lx\n", | ||
232 | ptr->start, ptr->end); | ||
233 | arg = (fn)(ptr, arg); | ||
234 | } | ||
235 | |||
236 | ptr = ptr->sibling; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | static void s3c2410_pm_run_sysram(run_fn_t fn, u32 *arg) | ||
241 | { | ||
242 | s3c2410_pm_run_res(&iomem_resource, fn, arg); | ||
243 | } | ||
244 | |||
245 | static u32 *s3c2410_pm_countram(struct resource *res, u32 *val) | ||
246 | { | ||
247 | u32 size = (u32)(res->end - res->start)+1; | ||
248 | |||
249 | size += CHECK_CHUNKSIZE-1; | ||
250 | size /= CHECK_CHUNKSIZE; | ||
251 | |||
252 | DBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size); | ||
253 | |||
254 | *val += size * sizeof(u32); | ||
255 | return val; | ||
256 | } | ||
257 | |||
258 | /* s3c2410_pm_prepare_check | ||
259 | * | ||
260 | * prepare the necessary information for creating the CRCs. This | ||
261 | * must be done before the final save, as it will require memory | ||
262 | * allocating, and thus touching bits of the kernel we do not | ||
263 | * know about. | ||
264 | */ | ||
265 | |||
266 | static void s3c2410_pm_check_prepare(void) | ||
267 | { | ||
268 | crc_size = 0; | ||
269 | |||
270 | s3c2410_pm_run_sysram(s3c2410_pm_countram, &crc_size); | ||
271 | |||
272 | DBG("s3c2410_pm_prepare_check: %u checks needed\n", crc_size); | ||
273 | |||
274 | crcs = kmalloc(crc_size+4, GFP_KERNEL); | ||
275 | if (crcs == NULL) | ||
276 | printk(KERN_ERR "Cannot allocated CRC save area\n"); | ||
277 | } | ||
278 | |||
279 | static u32 *s3c2410_pm_makecheck(struct resource *res, u32 *val) | ||
280 | { | ||
281 | unsigned long addr, left; | ||
282 | |||
283 | for (addr = res->start; addr < res->end; | ||
284 | addr += CHECK_CHUNKSIZE) { | ||
285 | left = res->end - addr; | ||
286 | |||
287 | if (left > CHECK_CHUNKSIZE) | ||
288 | left = CHECK_CHUNKSIZE; | ||
289 | |||
290 | *val = crc32_le(~0, phys_to_virt(addr), left); | ||
291 | val++; | ||
292 | } | ||
293 | |||
294 | return val; | ||
295 | } | ||
296 | |||
297 | /* s3c2410_pm_check_store | ||
298 | * | ||
299 | * compute the CRC values for the memory blocks before the final | ||
300 | * sleep. | ||
301 | */ | ||
302 | |||
303 | static void s3c2410_pm_check_store(void) | ||
304 | { | ||
305 | if (crcs != NULL) | ||
306 | s3c2410_pm_run_sysram(s3c2410_pm_makecheck, crcs); | ||
307 | } | ||
308 | |||
309 | /* in_region | ||
310 | * | ||
311 | * return TRUE if the area defined by ptr..ptr+size contatins the | ||
312 | * what..what+whatsz | ||
313 | */ | ||
314 | |||
315 | static inline int in_region(void *ptr, int size, void *what, size_t whatsz) | ||
316 | { | ||
317 | if ((what+whatsz) < ptr) | ||
318 | return 0; | ||
319 | |||
320 | if (what > (ptr+size)) | ||
321 | return 0; | ||
322 | |||
323 | return 1; | ||
324 | } | ||
325 | |||
326 | static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val) | ||
327 | { | ||
328 | void *save_at = phys_to_virt(s3c2410_sleep_save_phys); | ||
329 | unsigned long addr; | ||
330 | unsigned long left; | ||
331 | void *ptr; | ||
332 | u32 calc; | ||
333 | |||
334 | for (addr = res->start; addr < res->end; | ||
335 | addr += CHECK_CHUNKSIZE) { | ||
336 | left = res->end - addr; | ||
337 | |||
338 | if (left > CHECK_CHUNKSIZE) | ||
339 | left = CHECK_CHUNKSIZE; | ||
340 | |||
341 | ptr = phys_to_virt(addr); | ||
342 | |||
343 | if (in_region(ptr, left, crcs, crc_size)) { | ||
344 | DBG("skipping %08lx, has crc block in\n", addr); | ||
345 | goto skip_check; | ||
346 | } | ||
347 | |||
348 | if (in_region(ptr, left, save_at, 32*4 )) { | ||
349 | DBG("skipping %08lx, has save block in\n", addr); | ||
350 | goto skip_check; | ||
351 | } | ||
352 | |||
353 | /* calculate and check the checksum */ | ||
354 | |||
355 | calc = crc32_le(~0, ptr, left); | ||
356 | if (calc != *val) { | ||
357 | printk(KERN_ERR PFX "Restore CRC error at " | ||
358 | "%08lx (%08x vs %08x)\n", addr, calc, *val); | ||
359 | |||
360 | DBG("Restore CRC error at %08lx (%08x vs %08x)\n", | ||
361 | addr, calc, *val); | ||
362 | } | ||
363 | |||
364 | skip_check: | ||
365 | val++; | ||
366 | } | ||
367 | |||
368 | return val; | ||
369 | } | ||
370 | |||
371 | /* s3c2410_pm_check_restore | ||
372 | * | ||
373 | * check the CRCs after the restore event and free the memory used | ||
374 | * to hold them | ||
375 | */ | ||
376 | |||
377 | static void s3c2410_pm_check_restore(void) | ||
378 | { | ||
379 | if (crcs != NULL) { | ||
380 | s3c2410_pm_run_sysram(s3c2410_pm_runcheck, crcs); | ||
381 | kfree(crcs); | ||
382 | crcs = NULL; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | #else | ||
387 | |||
388 | #define s3c2410_pm_check_prepare() do { } while(0) | ||
389 | #define s3c2410_pm_check_restore() do { } while(0) | ||
390 | #define s3c2410_pm_check_store() do { } while(0) | ||
391 | #endif | ||
392 | |||
393 | /* helper functions to save and restore register state */ | ||
394 | |||
395 | void s3c2410_pm_do_save(struct sleep_save *ptr, int count) | ||
396 | { | ||
397 | for (; count > 0; count--, ptr++) { | ||
398 | ptr->val = __raw_readl(ptr->reg); | ||
399 | DBG("saved %p value %08lx\n", ptr->reg, ptr->val); | ||
400 | } | ||
401 | } | ||
402 | |||
403 | /* s3c2410_pm_do_restore | ||
404 | * | ||
405 | * restore the system from the given list of saved registers | ||
406 | * | ||
407 | * Note, we do not use DBG() in here, as the system may not have | ||
408 | * restore the UARTs state yet | ||
409 | */ | ||
410 | |||
411 | void s3c2410_pm_do_restore(struct sleep_save *ptr, int count) | ||
412 | { | ||
413 | for (; count > 0; count--, ptr++) { | ||
414 | printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n", | ||
415 | ptr->reg, ptr->val, __raw_readl(ptr->reg)); | ||
416 | |||
417 | __raw_writel(ptr->val, ptr->reg); | ||
418 | } | ||
419 | } | ||
420 | |||
421 | /* s3c2410_pm_do_restore_core | ||
422 | * | ||
423 | * similar to s3c2410_pm_do_restore_core | ||
424 | * | ||
425 | * WARNING: Do not put any debug in here that may effect memory or use | ||
426 | * peripherals, as things may be changing! | ||
427 | */ | ||
428 | |||
429 | static void s3c2410_pm_do_restore_core(struct sleep_save *ptr, int count) | ||
430 | { | ||
431 | for (; count > 0; count--, ptr++) { | ||
432 | __raw_writel(ptr->val, ptr->reg); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | /* s3c2410_pm_show_resume_irqs | ||
437 | * | ||
438 | * print any IRQs asserted at resume time (ie, we woke from) | ||
439 | */ | ||
440 | |||
441 | static void s3c2410_pm_show_resume_irqs(int start, unsigned long which, | ||
442 | unsigned long mask) | ||
443 | { | ||
444 | int i; | ||
445 | |||
446 | which &= ~mask; | ||
447 | |||
448 | for (i = 0; i <= 31; i++) { | ||
449 | if ((which) & (1L<<i)) { | ||
450 | DBG("IRQ %d asserted at resume\n", start+i); | ||
451 | } | ||
452 | } | ||
453 | } | ||
454 | |||
455 | /* s3c2410_pm_check_resume_pin | ||
456 | * | ||
457 | * check to see if the pin is configured correctly for sleep mode, and | ||
458 | * make any necessary adjustments if it is not | ||
459 | */ | ||
460 | |||
461 | static void s3c2410_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs) | ||
462 | { | ||
463 | unsigned long irqstate; | ||
464 | unsigned long pinstate; | ||
465 | int irq = s3c2410_gpio_getirq(pin); | ||
466 | |||
467 | if (irqoffs < 4) | ||
468 | irqstate = s3c_irqwake_intmask & (1L<<irqoffs); | ||
469 | else | ||
470 | irqstate = s3c_irqwake_eintmask & (1L<<irqoffs); | ||
471 | |||
472 | pinstate = s3c2410_gpio_getcfg(pin); | ||
473 | pinstate >>= S3C2410_GPIO_OFFSET(pin)*2; | ||
474 | |||
475 | if (!irqstate) { | ||
476 | if (pinstate == 0x02) | ||
477 | DBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin); | ||
478 | } else { | ||
479 | if (pinstate == 0x02) { | ||
480 | DBG("Disabling IRQ %d (pin %d)\n", irq, pin); | ||
481 | s3c2410_gpio_cfgpin(pin, 0x00); | ||
482 | } | ||
483 | } | ||
484 | } | ||
485 | |||
486 | /* s3c2410_pm_configure_extint | ||
487 | * | ||
488 | * configure all external interrupt pins | ||
489 | */ | ||
490 | |||
491 | static void s3c2410_pm_configure_extint(void) | ||
492 | { | ||
493 | int pin; | ||
494 | |||
495 | /* for each of the external interrupts (EINT0..EINT15) we | ||
496 | * need to check wether it is an external interrupt source, | ||
497 | * and then configure it as an input if it is not | ||
498 | */ | ||
499 | |||
500 | for (pin = S3C2410_GPF0; pin <= S3C2410_GPF7; pin++) { | ||
501 | s3c2410_pm_check_resume_pin(pin, pin - S3C2410_GPF0); | ||
502 | } | ||
503 | |||
504 | for (pin = S3C2410_GPG0; pin <= S3C2410_GPG7; pin++) { | ||
505 | s3c2410_pm_check_resume_pin(pin, (pin - S3C2410_GPG0)+8); | ||
506 | } | ||
507 | } | ||
508 | |||
509 | #define any_allowed(mask, allow) (((mask) & (allow)) != (allow)) | ||
510 | |||
511 | /* s3c2410_pm_enter | ||
512 | * | ||
513 | * central control for sleep/resume process | ||
514 | */ | ||
515 | |||
516 | static int s3c2410_pm_enter(suspend_state_t state) | ||
517 | { | ||
518 | unsigned long regs_save[16]; | ||
519 | unsigned long tmp; | ||
520 | |||
521 | /* ensure the debug is initialised (if enabled) */ | ||
522 | |||
523 | s3c2410_pm_debug_init(); | ||
524 | |||
525 | DBG("s3c2410_pm_enter(%d)\n", state); | ||
526 | |||
527 | if (state != PM_SUSPEND_MEM) { | ||
528 | printk(KERN_ERR PFX "error: only PM_SUSPEND_MEM supported\n"); | ||
529 | return -EINVAL; | ||
530 | } | ||
531 | |||
532 | /* check if we have anything to wake-up with... bad things seem | ||
533 | * to happen if you suspend with no wakeup (system will often | ||
534 | * require a full power-cycle) | ||
535 | */ | ||
536 | |||
537 | if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) && | ||
538 | !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) { | ||
539 | printk(KERN_ERR PFX "No sources enabled for wake-up!\n"); | ||
540 | printk(KERN_ERR PFX "Aborting sleep\n"); | ||
541 | return -EINVAL; | ||
542 | } | ||
543 | |||
544 | /* prepare check area if configured */ | ||
545 | |||
546 | s3c2410_pm_check_prepare(); | ||
547 | |||
548 | /* store the physical address of the register recovery block */ | ||
549 | |||
550 | s3c2410_sleep_save_phys = virt_to_phys(regs_save); | ||
551 | |||
552 | DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys); | ||
553 | |||
554 | /* ensure at least GESTATUS3 has the resume address */ | ||
555 | |||
556 | __raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3); | ||
557 | |||
558 | DBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3)); | ||
559 | DBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4)); | ||
560 | |||
561 | /* save all necessary core registers not covered by the drivers */ | ||
562 | |||
563 | s3c2410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save)); | ||
564 | s3c2410_pm_do_save(irq_save, ARRAY_SIZE(irq_save)); | ||
565 | s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save)); | ||
566 | s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save)); | ||
567 | |||
568 | /* set the irq configuration for wake */ | ||
569 | |||
570 | s3c2410_pm_configure_extint(); | ||
571 | |||
572 | DBG("sleep: irq wakeup masks: %08lx,%08lx\n", | ||
573 | s3c_irqwake_intmask, s3c_irqwake_eintmask); | ||
574 | |||
575 | __raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK); | ||
576 | __raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK); | ||
577 | |||
578 | /* ack any outstanding external interrupts before we go to sleep */ | ||
579 | |||
580 | __raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND); | ||
581 | |||
582 | /* flush cache back to ram */ | ||
583 | |||
584 | arm920_flush_kern_cache_all(); | ||
585 | |||
586 | s3c2410_pm_check_store(); | ||
587 | |||
588 | // need to make some form of time-delta | ||
589 | |||
590 | /* send the cpu to sleep... */ | ||
591 | |||
592 | __raw_writel(0x00, S3C2410_CLKCON); /* turn off clocks over sleep */ | ||
593 | |||
594 | s3c2410_cpu_suspend(regs_save); | ||
595 | |||
596 | /* unset the return-from-sleep flag, to ensure reset */ | ||
597 | |||
598 | tmp = __raw_readl(S3C2410_GSTATUS2); | ||
599 | tmp &= S3C2410_GSTATUS2_OFFRESET; | ||
600 | __raw_writel(tmp, S3C2410_GSTATUS2); | ||
601 | |||
602 | /* restore the system state */ | ||
603 | |||
604 | s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); | ||
605 | s3c2410_pm_do_restore(gpio_save, ARRAY_SIZE(gpio_save)); | ||
606 | s3c2410_pm_do_restore(irq_save, ARRAY_SIZE(irq_save)); | ||
607 | s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); | ||
608 | |||
609 | s3c2410_pm_debug_init(); | ||
610 | |||
611 | /* check what irq (if any) restored the system */ | ||
612 | |||
613 | DBG("post sleep: IRQs 0x%08x, 0x%08x\n", | ||
614 | __raw_readl(S3C2410_SRCPND), | ||
615 | __raw_readl(S3C2410_EINTPEND)); | ||
616 | |||
617 | s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND), | ||
618 | s3c_irqwake_intmask); | ||
619 | |||
620 | s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND), | ||
621 | s3c_irqwake_eintmask); | ||
622 | |||
623 | DBG("post sleep, preparing to return\n"); | ||
624 | |||
625 | s3c2410_pm_check_restore(); | ||
626 | |||
627 | /* ok, let's return from sleep */ | ||
628 | |||
629 | DBG("S3C2410 PM Resume (post-restore)\n"); | ||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | /* | ||
634 | * Called after processes are frozen, but before we shut down devices. | ||
635 | */ | ||
636 | static int s3c2410_pm_prepare(suspend_state_t state) | ||
637 | { | ||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | /* | ||
642 | * Called after devices are re-setup, but before processes are thawed. | ||
643 | */ | ||
644 | static int s3c2410_pm_finish(suspend_state_t state) | ||
645 | { | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | /* | ||
650 | * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. | ||
651 | */ | ||
652 | static struct pm_ops s3c2410_pm_ops = { | ||
653 | .pm_disk_mode = PM_DISK_FIRMWARE, | ||
654 | .prepare = s3c2410_pm_prepare, | ||
655 | .enter = s3c2410_pm_enter, | ||
656 | .finish = s3c2410_pm_finish, | ||
657 | }; | ||
658 | |||
659 | /* s3c2410_pm_init | ||
660 | * | ||
661 | * Attach the power management functions. This should be called | ||
662 | * from the board specific initialisation if the board supports | ||
663 | * it. | ||
664 | */ | ||
665 | |||
666 | int __init s3c2410_pm_init(void) | ||
667 | { | ||
668 | printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n"); | ||
669 | |||
670 | pm_set_ops(&s3c2410_pm_ops); | ||
671 | return 0; | ||
672 | } | ||
diff --git a/arch/arm/mach-s3c2410/pm.h b/arch/arm/mach-s3c2410/pm.h new file mode 100644 index 000000000000..7a5e714c7386 --- /dev/null +++ b/arch/arm/mach-s3c2410/pm.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/pm.h | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Written by Ben Dooks, <ben@simtec.co.uk> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | /* s3c2410_pm_init | ||
12 | * | ||
13 | * called from board at initialisation time to setup the power | ||
14 | * management | ||
15 | */ | ||
16 | |||
17 | #ifdef CONFIG_PM | ||
18 | |||
19 | extern __init int s3c2410_pm_init(void); | ||
20 | |||
21 | #else | ||
22 | |||
23 | static inline int s3c2410_pm_init(void) | ||
24 | { | ||
25 | return 0; | ||
26 | } | ||
27 | #endif | ||
28 | |||
29 | /* configuration for the IRQ mask over sleep */ | ||
30 | extern unsigned long s3c_irqwake_intmask; | ||
31 | extern unsigned long s3c_irqwake_eintmask; | ||
32 | |||
33 | /* IRQ masks for IRQs allowed to go to sleep (see irq.c) */ | ||
34 | extern unsigned long s3c_irqwake_intallow; | ||
35 | extern unsigned long s3c_irqwake_eintallow; | ||
36 | |||
37 | /* Flags for PM Control */ | ||
38 | |||
39 | extern unsigned long s3c_pm_flags; | ||
40 | |||
41 | /* from sleep.S */ | ||
42 | |||
43 | extern void s3c2410_cpu_suspend(unsigned long *saveblk); | ||
44 | extern void s3c2410_cpu_resume(void); | ||
45 | |||
46 | extern unsigned long s3c2410_sleep_save_phys; | ||
47 | |||
48 | /* sleep save info */ | ||
49 | |||
50 | struct sleep_save { | ||
51 | void __iomem *reg; | ||
52 | unsigned long val; | ||
53 | }; | ||
54 | |||
55 | #define SAVE_ITEM(x) \ | ||
56 | { .reg = (x) } | ||
57 | |||
58 | extern void s3c2410_pm_do_save(struct sleep_save *ptr, int count); | ||
59 | extern void s3c2410_pm_do_restore(struct sleep_save *ptr, int count); | ||
diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c new file mode 100644 index 000000000000..ff2f25409e44 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2410.c | |||
@@ -0,0 +1,200 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/s3c2410.c | ||
2 | * | ||
3 | * Copyright (c) 2003-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * http://www.simtec.co.uk/products/EB2410ITX/ | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 16-May-2003 BJD Created initial version | ||
14 | * 16-Aug-2003 BJD Fixed header files and copyright, added URL | ||
15 | * 05-Sep-2003 BJD Moved to kernel v2.6 | ||
16 | * 18-Jan-2004 BJD Added serial port configuration | ||
17 | * 21-Aug-2004 BJD Added new struct s3c2410_board handler | ||
18 | * 28-Sep-2004 BJD Updates for new serial port bits | ||
19 | * 04-Nov-2004 BJD Updated UART configuration process | ||
20 | * 10-Jan-2005 BJD Removed s3c2410_clock_tick_rate | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/list.h> | ||
27 | #include <linux/timer.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/device.h> | ||
30 | |||
31 | #include <asm/mach/arch.h> | ||
32 | #include <asm/mach/map.h> | ||
33 | #include <asm/mach/irq.h> | ||
34 | |||
35 | #include <asm/hardware.h> | ||
36 | #include <asm/io.h> | ||
37 | #include <asm/irq.h> | ||
38 | |||
39 | #include <asm/arch/regs-clock.h> | ||
40 | #include <asm/arch/regs-serial.h> | ||
41 | |||
42 | #include "s3c2410.h" | ||
43 | #include "cpu.h" | ||
44 | #include "clock.h" | ||
45 | |||
46 | /* Initial IO mappings */ | ||
47 | |||
48 | static struct map_desc s3c2410_iodesc[] __initdata = { | ||
49 | IODESC_ENT(USBHOST), | ||
50 | IODESC_ENT(CLKPWR), | ||
51 | IODESC_ENT(LCD), | ||
52 | IODESC_ENT(UART), | ||
53 | IODESC_ENT(TIMER), | ||
54 | IODESC_ENT(ADC), | ||
55 | IODESC_ENT(WATCHDOG) | ||
56 | }; | ||
57 | |||
58 | static struct resource s3c_uart0_resource[] = { | ||
59 | [0] = { | ||
60 | .start = S3C2410_PA_UART0, | ||
61 | .end = S3C2410_PA_UART0 + 0x3fff, | ||
62 | .flags = IORESOURCE_MEM, | ||
63 | }, | ||
64 | [1] = { | ||
65 | .start = IRQ_S3CUART_RX0, | ||
66 | .end = IRQ_S3CUART_ERR0, | ||
67 | .flags = IORESOURCE_IRQ, | ||
68 | } | ||
69 | |||
70 | }; | ||
71 | |||
72 | static struct resource s3c_uart1_resource[] = { | ||
73 | [0] = { | ||
74 | .start = S3C2410_PA_UART1, | ||
75 | .end = S3C2410_PA_UART1 + 0x3fff, | ||
76 | .flags = IORESOURCE_MEM, | ||
77 | }, | ||
78 | [1] = { | ||
79 | .start = IRQ_S3CUART_RX1, | ||
80 | .end = IRQ_S3CUART_ERR1, | ||
81 | .flags = IORESOURCE_IRQ, | ||
82 | } | ||
83 | }; | ||
84 | |||
85 | static struct resource s3c_uart2_resource[] = { | ||
86 | [0] = { | ||
87 | .start = S3C2410_PA_UART2, | ||
88 | .end = S3C2410_PA_UART2 + 0x3fff, | ||
89 | .flags = IORESOURCE_MEM, | ||
90 | }, | ||
91 | [1] = { | ||
92 | .start = IRQ_S3CUART_RX2, | ||
93 | .end = IRQ_S3CUART_ERR2, | ||
94 | .flags = IORESOURCE_IRQ, | ||
95 | } | ||
96 | }; | ||
97 | |||
98 | /* our uart devices */ | ||
99 | |||
100 | static struct platform_device s3c_uart0 = { | ||
101 | .name = "s3c2410-uart", | ||
102 | .id = 0, | ||
103 | .num_resources = ARRAY_SIZE(s3c_uart0_resource), | ||
104 | .resource = s3c_uart0_resource, | ||
105 | }; | ||
106 | |||
107 | |||
108 | static struct platform_device s3c_uart1 = { | ||
109 | .name = "s3c2410-uart", | ||
110 | .id = 1, | ||
111 | .num_resources = ARRAY_SIZE(s3c_uart1_resource), | ||
112 | .resource = s3c_uart1_resource, | ||
113 | }; | ||
114 | |||
115 | static struct platform_device s3c_uart2 = { | ||
116 | .name = "s3c2410-uart", | ||
117 | .id = 2, | ||
118 | .num_resources = ARRAY_SIZE(s3c_uart2_resource), | ||
119 | .resource = s3c_uart2_resource, | ||
120 | }; | ||
121 | |||
122 | static struct platform_device *uart_devices[] __initdata = { | ||
123 | &s3c_uart0, | ||
124 | &s3c_uart1, | ||
125 | &s3c_uart2 | ||
126 | }; | ||
127 | |||
128 | /* store our uart devices for the serial driver console */ | ||
129 | struct platform_device *s3c2410_uart_devices[3]; | ||
130 | |||
131 | static int s3c2410_uart_count = 0; | ||
132 | |||
133 | /* uart registration process */ | ||
134 | |||
135 | void __init s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no) | ||
136 | { | ||
137 | struct platform_device *platdev; | ||
138 | int uart; | ||
139 | |||
140 | for (uart = 0; uart < no; uart++, cfg++) { | ||
141 | platdev = uart_devices[cfg->hwport]; | ||
142 | |||
143 | s3c24xx_uart_devs[uart] = platdev; | ||
144 | platdev->dev.platform_data = cfg; | ||
145 | } | ||
146 | |||
147 | s3c2410_uart_count = uart; | ||
148 | } | ||
149 | |||
150 | /* s3c2410_map_io | ||
151 | * | ||
152 | * register the standard cpu IO areas, and any passed in from the | ||
153 | * machine specific initialisation. | ||
154 | */ | ||
155 | |||
156 | void __init s3c2410_map_io(struct map_desc *mach_desc, int mach_size) | ||
157 | { | ||
158 | /* register our io-tables */ | ||
159 | |||
160 | iotable_init(s3c2410_iodesc, ARRAY_SIZE(s3c2410_iodesc)); | ||
161 | iotable_init(mach_desc, mach_size); | ||
162 | } | ||
163 | |||
164 | void __init s3c2410_init_clocks(int xtal) | ||
165 | { | ||
166 | unsigned long tmp; | ||
167 | unsigned long fclk; | ||
168 | unsigned long hclk; | ||
169 | unsigned long pclk; | ||
170 | |||
171 | /* now we've got our machine bits initialised, work out what | ||
172 | * clocks we've got */ | ||
173 | |||
174 | fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal); | ||
175 | |||
176 | tmp = __raw_readl(S3C2410_CLKDIVN); | ||
177 | |||
178 | /* work out clock scalings */ | ||
179 | |||
180 | hclk = fclk / ((tmp & S3C2410_CLKDIVN_HDIVN) ? 2 : 1); | ||
181 | pclk = hclk / ((tmp & S3C2410_CLKDIVN_PDIVN) ? 2 : 1); | ||
182 | |||
183 | /* print brieft summary of clocks, etc */ | ||
184 | |||
185 | printk("S3C2410: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", | ||
186 | print_mhz(fclk), print_mhz(hclk), print_mhz(pclk)); | ||
187 | |||
188 | /* initialise the clocks here, to allow other things like the | ||
189 | * console to use them | ||
190 | */ | ||
191 | |||
192 | s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); | ||
193 | } | ||
194 | |||
195 | int __init s3c2410_init(void) | ||
196 | { | ||
197 | printk("S3C2410: Initialising architecture\n"); | ||
198 | |||
199 | return platform_add_devices(s3c24xx_uart_devs, s3c2410_uart_count); | ||
200 | } | ||
diff --git a/arch/arm/mach-s3c2410/s3c2410.h b/arch/arm/mach-s3c2410/s3c2410.h new file mode 100644 index 000000000000..4d5312a48209 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2410.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* arch/arm/mach-s3c2410/s3c2410.h | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Header file for s3c2410 machine directory | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 18-Aug-2004 BJD Created initial version | ||
14 | * 20-Aug-2004 BJD Added s3c2410_board struct | ||
15 | * 04-Sep-2004 BJD Added s3c2410_init_uarts() call | ||
16 | * 17-Oct-2004 BJD Moved board out to cpu | ||
17 | * 04-Jan-2005 BJD Changed uart init | ||
18 | * 10-Jan-2005 BJD Removed timer to cpu.h, moved 2410 specific bits here | ||
19 | * 14-Jan-2005 BJD Added s3c2410_init_clocks call | ||
20 | */ | ||
21 | |||
22 | #ifdef CONFIG_CPU_S3C2410 | ||
23 | |||
24 | extern int s3c2410_init(void); | ||
25 | |||
26 | extern void s3c2410_map_io(struct map_desc *mach_desc, int size); | ||
27 | |||
28 | extern void s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no); | ||
29 | |||
30 | extern void s3c2410_init_clocks(int xtal); | ||
31 | |||
32 | #else | ||
33 | #define s3c2410_init_clocks NULL | ||
34 | #define s3c2410_init_uarts NULL | ||
35 | #define s3c2410_map_io NULL | ||
36 | #define s3c2410_init NULL | ||
37 | #endif | ||
diff --git a/arch/arm/mach-s3c2410/s3c2440-dsc.c b/arch/arm/mach-s3c2410/s3c2440-dsc.c new file mode 100644 index 000000000000..16fa2a3b38fa --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2440-dsc.c | |||
@@ -0,0 +1,59 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/s3c2440-dsc.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Samsung S3C2440 Drive Strength Control support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 29-Aug-2004 BJD Start of drive-strength control | ||
14 | * 09-Nov-2004 BJD Added symbol export | ||
15 | * 11-Jan-2005 BJD Include fix | ||
16 | */ | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/module.h> | ||
23 | |||
24 | #include <asm/mach/arch.h> | ||
25 | #include <asm/mach/map.h> | ||
26 | #include <asm/mach/irq.h> | ||
27 | |||
28 | #include <asm/hardware.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <asm/irq.h> | ||
31 | |||
32 | #include <asm/arch/regs-gpio.h> | ||
33 | #include <asm/arch/regs-dsc.h> | ||
34 | |||
35 | #include "cpu.h" | ||
36 | #include "s3c2440.h" | ||
37 | |||
38 | int s3c2440_set_dsc(unsigned int pin, unsigned int value) | ||
39 | { | ||
40 | void __iomem *base; | ||
41 | unsigned long val; | ||
42 | unsigned long flags; | ||
43 | unsigned long mask; | ||
44 | |||
45 | base = (pin & S3C2440_SELECT_DSC1) ? S3C2440_DSC1 : S3C2440_DSC0; | ||
46 | mask = 3 << S3C2440_DSC_GETSHIFT(pin); | ||
47 | |||
48 | local_irq_save(flags); | ||
49 | |||
50 | val = __raw_readl(base); | ||
51 | val &= ~mask; | ||
52 | val |= value & mask; | ||
53 | __raw_writel(val, base); | ||
54 | |||
55 | local_irq_restore(flags); | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | EXPORT_SYMBOL(s3c2440_set_dsc); | ||
diff --git a/arch/arm/mach-s3c2410/s3c2440.c b/arch/arm/mach-s3c2410/s3c2440.c new file mode 100644 index 000000000000..9a8cc5ae2255 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2440.c | |||
@@ -0,0 +1,281 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/s3c2440.c | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Samsung S3C2440 Mobile CPU support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 24-Aug-2004 BJD Start of s3c2440 support | ||
14 | * 12-Oct-2004 BJD Moved clock info out to clock.c | ||
15 | * 01-Nov-2004 BJD Fixed clock build code | ||
16 | * 09-Nov-2004 BJD Added sysdev for power management | ||
17 | * 04-Nov-2004 BJD New serial registration | ||
18 | * 15-Nov-2004 BJD Rename the i2c device for the s3c2440 | ||
19 | * 14-Jan-2005 BJD Moved clock init code into seperate function | ||
20 | * 14-Jan-2005 BJD Removed un-used clock bits | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/list.h> | ||
27 | #include <linux/timer.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/device.h> | ||
30 | #include <linux/sysdev.h> | ||
31 | |||
32 | #include <asm/mach/arch.h> | ||
33 | #include <asm/mach/map.h> | ||
34 | #include <asm/mach/irq.h> | ||
35 | |||
36 | #include <asm/hardware.h> | ||
37 | #include <asm/io.h> | ||
38 | #include <asm/irq.h> | ||
39 | #include <asm/hardware/clock.h> | ||
40 | |||
41 | #include <asm/arch/regs-clock.h> | ||
42 | #include <asm/arch/regs-serial.h> | ||
43 | #include <asm/arch/regs-gpio.h> | ||
44 | #include <asm/arch/regs-gpioj.h> | ||
45 | #include <asm/arch/regs-dsc.h> | ||
46 | |||
47 | #include "s3c2440.h" | ||
48 | #include "clock.h" | ||
49 | #include "devs.h" | ||
50 | #include "cpu.h" | ||
51 | #include "pm.h" | ||
52 | |||
53 | |||
54 | static struct map_desc s3c2440_iodesc[] __initdata = { | ||
55 | IODESC_ENT(USBHOST), | ||
56 | IODESC_ENT(CLKPWR), | ||
57 | IODESC_ENT(LCD), | ||
58 | IODESC_ENT(TIMER), | ||
59 | IODESC_ENT(ADC), | ||
60 | IODESC_ENT(WATCHDOG), | ||
61 | }; | ||
62 | |||
63 | static struct resource s3c_uart0_resource[] = { | ||
64 | [0] = { | ||
65 | .start = S3C2410_PA_UART0, | ||
66 | .end = S3C2410_PA_UART0 + 0x3fff, | ||
67 | .flags = IORESOURCE_MEM, | ||
68 | }, | ||
69 | [1] = { | ||
70 | .start = IRQ_S3CUART_RX0, | ||
71 | .end = IRQ_S3CUART_ERR0, | ||
72 | .flags = IORESOURCE_IRQ, | ||
73 | } | ||
74 | |||
75 | }; | ||
76 | |||
77 | static struct resource s3c_uart1_resource[] = { | ||
78 | [0] = { | ||
79 | .start = S3C2410_PA_UART1, | ||
80 | .end = S3C2410_PA_UART1 + 0x3fff, | ||
81 | .flags = IORESOURCE_MEM, | ||
82 | }, | ||
83 | [1] = { | ||
84 | .start = IRQ_S3CUART_RX1, | ||
85 | .end = IRQ_S3CUART_ERR1, | ||
86 | .flags = IORESOURCE_IRQ, | ||
87 | } | ||
88 | }; | ||
89 | |||
90 | static struct resource s3c_uart2_resource[] = { | ||
91 | [0] = { | ||
92 | .start = S3C2410_PA_UART2, | ||
93 | .end = S3C2410_PA_UART2 + 0x3fff, | ||
94 | .flags = IORESOURCE_MEM, | ||
95 | }, | ||
96 | [1] = { | ||
97 | .start = IRQ_S3CUART_RX2, | ||
98 | .end = IRQ_S3CUART_ERR2, | ||
99 | .flags = IORESOURCE_IRQ, | ||
100 | } | ||
101 | }; | ||
102 | |||
103 | /* our uart devices */ | ||
104 | |||
105 | static struct platform_device s3c_uart0 = { | ||
106 | .name = "s3c2440-uart", | ||
107 | .id = 0, | ||
108 | .num_resources = ARRAY_SIZE(s3c_uart0_resource), | ||
109 | .resource = s3c_uart0_resource, | ||
110 | }; | ||
111 | |||
112 | static struct platform_device s3c_uart1 = { | ||
113 | .name = "s3c2440-uart", | ||
114 | .id = 1, | ||
115 | .num_resources = ARRAY_SIZE(s3c_uart1_resource), | ||
116 | .resource = s3c_uart1_resource, | ||
117 | }; | ||
118 | |||
119 | static struct platform_device s3c_uart2 = { | ||
120 | .name = "s3c2440-uart", | ||
121 | .id = 2, | ||
122 | .num_resources = ARRAY_SIZE(s3c_uart2_resource), | ||
123 | .resource = s3c_uart2_resource, | ||
124 | }; | ||
125 | |||
126 | static struct platform_device *uart_devices[] __initdata = { | ||
127 | &s3c_uart0, | ||
128 | &s3c_uart1, | ||
129 | &s3c_uart2 | ||
130 | }; | ||
131 | |||
132 | /* uart initialisation */ | ||
133 | |||
134 | static int __initdata s3c2440_uart_count; | ||
135 | |||
136 | void __init s3c2440_init_uarts(struct s3c2410_uartcfg *cfg, int no) | ||
137 | { | ||
138 | struct platform_device *platdev; | ||
139 | int uart; | ||
140 | |||
141 | for (uart = 0; uart < no; uart++, cfg++) { | ||
142 | platdev = uart_devices[cfg->hwport]; | ||
143 | |||
144 | s3c24xx_uart_devs[uart] = platdev; | ||
145 | platdev->dev.platform_data = cfg; | ||
146 | } | ||
147 | |||
148 | s3c2440_uart_count = uart; | ||
149 | } | ||
150 | |||
151 | |||
152 | #ifdef CONFIG_PM | ||
153 | |||
154 | struct sleep_save s3c2440_sleep[] = { | ||
155 | SAVE_ITEM(S3C2440_DSC0), | ||
156 | SAVE_ITEM(S3C2440_DSC1), | ||
157 | SAVE_ITEM(S3C2440_GPJDAT), | ||
158 | SAVE_ITEM(S3C2440_GPJCON), | ||
159 | SAVE_ITEM(S3C2440_GPJUP) | ||
160 | }; | ||
161 | |||
162 | static int s3c2440_suspend(struct sys_device *dev, pm_message_t state) | ||
163 | { | ||
164 | s3c2410_pm_do_save(s3c2440_sleep, ARRAY_SIZE(s3c2440_sleep)); | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int s3c2440_resume(struct sys_device *dev) | ||
169 | { | ||
170 | s3c2410_pm_do_restore(s3c2440_sleep, ARRAY_SIZE(s3c2440_sleep)); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | #else | ||
175 | #define s3c2440_suspend NULL | ||
176 | #define s3c2440_resume NULL | ||
177 | #endif | ||
178 | |||
179 | struct sysdev_class s3c2440_sysclass = { | ||
180 | set_kset_name("s3c2440-core"), | ||
181 | .suspend = s3c2440_suspend, | ||
182 | .resume = s3c2440_resume | ||
183 | }; | ||
184 | |||
185 | static struct sys_device s3c2440_sysdev = { | ||
186 | .cls = &s3c2440_sysclass, | ||
187 | }; | ||
188 | |||
189 | void __init s3c2440_map_io(struct map_desc *mach_desc, int size) | ||
190 | { | ||
191 | /* register our io-tables */ | ||
192 | |||
193 | iotable_init(s3c2440_iodesc, ARRAY_SIZE(s3c2440_iodesc)); | ||
194 | iotable_init(mach_desc, size); | ||
195 | /* rename any peripherals used differing from the s3c2410 */ | ||
196 | |||
197 | s3c_device_i2c.name = "s3c2440-i2c"; | ||
198 | |||
199 | /* change irq for watchdog */ | ||
200 | |||
201 | s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT; | ||
202 | s3c_device_wdt.resource[1].end = IRQ_S3C2440_WDT; | ||
203 | } | ||
204 | |||
205 | void __init s3c2440_init_clocks(int xtal) | ||
206 | { | ||
207 | unsigned long clkdiv; | ||
208 | unsigned long camdiv; | ||
209 | unsigned long hclk, fclk, pclk; | ||
210 | int hdiv = 1; | ||
211 | |||
212 | /* now we've got our machine bits initialised, work out what | ||
213 | * clocks we've got */ | ||
214 | |||
215 | fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2; | ||
216 | |||
217 | clkdiv = __raw_readl(S3C2410_CLKDIVN); | ||
218 | camdiv = __raw_readl(S3C2440_CAMDIVN); | ||
219 | |||
220 | /* work out clock scalings */ | ||
221 | |||
222 | switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) { | ||
223 | case S3C2440_CLKDIVN_HDIVN_1: | ||
224 | hdiv = 1; | ||
225 | break; | ||
226 | |||
227 | case S3C2440_CLKDIVN_HDIVN_2: | ||
228 | hdiv = 1; | ||
229 | break; | ||
230 | |||
231 | case S3C2440_CLKDIVN_HDIVN_4_8: | ||
232 | hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4; | ||
233 | break; | ||
234 | |||
235 | case S3C2440_CLKDIVN_HDIVN_3_6: | ||
236 | hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3; | ||
237 | break; | ||
238 | } | ||
239 | |||
240 | hclk = fclk / hdiv; | ||
241 | pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN)? 2:1); | ||
242 | |||
243 | /* print brief summary of clocks, etc */ | ||
244 | |||
245 | printk("S3C2440: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", | ||
246 | print_mhz(fclk), print_mhz(hclk), print_mhz(pclk)); | ||
247 | |||
248 | /* initialise the clocks here, to allow other things like the | ||
249 | * console to use them, and to add new ones after the initialisation | ||
250 | */ | ||
251 | |||
252 | s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); | ||
253 | } | ||
254 | |||
255 | /* need to register class before we actually register the device, and | ||
256 | * we also need to ensure that it has been initialised before any of the | ||
257 | * drivers even try to use it (even if not on an s3c2440 based system) | ||
258 | * as a driver which may support both 2410 and 2440 may try and use it. | ||
259 | */ | ||
260 | |||
261 | int __init s3c2440_core_init(void) | ||
262 | { | ||
263 | return sysdev_class_register(&s3c2440_sysclass); | ||
264 | } | ||
265 | |||
266 | core_initcall(s3c2440_core_init); | ||
267 | |||
268 | int __init s3c2440_init(void) | ||
269 | { | ||
270 | int ret; | ||
271 | |||
272 | printk("S3C2440: Initialising architecture\n"); | ||
273 | |||
274 | ret = sysdev_register(&s3c2440_sysdev); | ||
275 | if (ret != 0) | ||
276 | printk(KERN_ERR "failed to register sysdev for s3c2440\n"); | ||
277 | else | ||
278 | ret = platform_add_devices(s3c24xx_uart_devs, s3c2440_uart_count); | ||
279 | |||
280 | return ret; | ||
281 | } | ||
diff --git a/arch/arm/mach-s3c2410/s3c2440.h b/arch/arm/mach-s3c2410/s3c2440.h new file mode 100644 index 000000000000..29cb6df65a48 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2440.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* arch/arm/mach-s3c2410/s3c2440.h | ||
2 | * | ||
3 | * Copyright (c) 2004-2005 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Header file for s3c2440 cpu support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Modifications: | ||
13 | * 24-Aug-2004 BJD Start of S3C2440 CPU support | ||
14 | * 04-Nov-2004 BJD Added s3c2440_init_uarts() | ||
15 | * 04-Jan-2005 BJD Moved uart init to cpu code | ||
16 | * 10-Jan-2005 BJD Moved 2440 specific init here | ||
17 | * 14-Jan-2005 BJD Split the clock initialisation code | ||
18 | */ | ||
19 | |||
20 | #ifdef CONFIG_CPU_S3C2440 | ||
21 | |||
22 | extern int s3c2440_init(void); | ||
23 | |||
24 | extern void s3c2440_map_io(struct map_desc *mach_desc, int size); | ||
25 | |||
26 | extern void s3c2440_init_uarts(struct s3c2410_uartcfg *cfg, int no); | ||
27 | |||
28 | extern void s3c2440_init_clocks(int xtal); | ||
29 | |||
30 | #else | ||
31 | #define s3c2440_init_clocks NULL | ||
32 | #define s3c2440_init_uarts NULL | ||
33 | #define s3c2440_map_io NULL | ||
34 | #define s3c2440_init NULL | ||
35 | #endif | ||
diff --git a/arch/arm/mach-s3c2410/sleep.S b/arch/arm/mach-s3c2410/sleep.S new file mode 100644 index 000000000000..61768dac7fee --- /dev/null +++ b/arch/arm/mach-s3c2410/sleep.S | |||
@@ -0,0 +1,180 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/sleep.S | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C2410 Power Manager (Suspend-To-RAM) support | ||
7 | * | ||
8 | * Based on PXA/SA1100 sleep code by: | ||
9 | * Nicolas Pitre, (c) 2002 Monta Vista Software Inc | ||
10 | * Cliff Brake, (c) 2001 | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/config.h> | ||
28 | #include <linux/linkage.h> | ||
29 | #include <asm/assembler.h> | ||
30 | #include <asm/hardware.h> | ||
31 | #include <asm/arch/map.h> | ||
32 | |||
33 | #include <asm/arch/regs-gpio.h> | ||
34 | #include <asm/arch/regs-clock.h> | ||
35 | #include <asm/arch/regs-mem.h> | ||
36 | #include <asm/arch/regs-serial.h> | ||
37 | |||
38 | /* CONFIG_DEBUG_RESUME is dangerous if your bootloader does not | ||
39 | * reset the UART configuration, only enable if you really need this! | ||
40 | */ | ||
41 | //#define CONFIG_DEBUG_RESUME | ||
42 | |||
43 | .text | ||
44 | |||
45 | /* s3c2410_cpu_suspend | ||
46 | * | ||
47 | * put the cpu into sleep mode | ||
48 | * | ||
49 | * entry: | ||
50 | * r0 = sleep save block | ||
51 | */ | ||
52 | |||
53 | ENTRY(s3c2410_cpu_suspend) | ||
54 | stmfd sp!, { r4 - r12, lr } | ||
55 | |||
56 | @@ store co-processor registers | ||
57 | |||
58 | mrc p15, 0, r4, c15, c1, 0 @ CP access register | ||
59 | mrc p15, 0, r5, c13, c0, 0 @ PID | ||
60 | mrc p15, 0, r6, c3, c0, 0 @ Domain ID | ||
61 | mrc p15, 0, r7, c2, c0, 0 @ translation table base address | ||
62 | mrc p15, 0, r8, c2, c0, 0 @ auxiliary control register | ||
63 | mrc p15, 0, r9, c1, c0, 0 @ control register | ||
64 | |||
65 | stmia r0, { r4 - r13 } | ||
66 | |||
67 | @@ flush the caches to ensure everything is back out to | ||
68 | @@ SDRAM before the core powers down | ||
69 | |||
70 | bl arm920_flush_kern_cache_all | ||
71 | |||
72 | @@ prepare cpu to sleep | ||
73 | |||
74 | ldr r4, =S3C2410_REFRESH | ||
75 | ldr r5, =S3C2410_MISCCR | ||
76 | ldr r6, =S3C2410_CLKCON | ||
77 | ldr r7, [ r4 ] @ get REFRESH (and ensure in TLB) | ||
78 | ldr r8, [ r5 ] @ get MISCCR (and ensure in TLB) | ||
79 | ldr r9, [ r6 ] @ get CLKCON (and ensure in TLB) | ||
80 | |||
81 | orr r7, r7, #S3C2410_REFRESH_SELF @ SDRAM sleep command | ||
82 | orr r8, r8, #S3C2410_MISCCR_SDSLEEP @ SDRAM power-down signals | ||
83 | orr r9, r9, #S3C2410_CLKCON_POWER @ power down command | ||
84 | |||
85 | teq pc, #0 @ first as a trial-run to load cache | ||
86 | bl s3c2410_do_sleep | ||
87 | teq r0, r0 @ now do it for real | ||
88 | b s3c2410_do_sleep @ | ||
89 | |||
90 | @@ align next bit of code to cache line | ||
91 | .align 8 | ||
92 | s3c2410_do_sleep: | ||
93 | streq r7, [ r4 ] @ SDRAM sleep command | ||
94 | streq r8, [ r5 ] @ SDRAM power-down config | ||
95 | streq r9, [ r6 ] @ CPU sleep | ||
96 | 1: beq 1b | ||
97 | mov pc, r14 | ||
98 | |||
99 | @@ return to the caller, after having the MMU | ||
100 | @@ turned on, this restores the last bits from the | ||
101 | @@ stack | ||
102 | resume_with_mmu: | ||
103 | ldmfd sp!, { r4 - r12, pc } | ||
104 | |||
105 | .ltorg | ||
106 | |||
107 | @@ the next bits sit in the .data segment, even though they | ||
108 | @@ happen to be code... the s3c2410_sleep_save_phys needs to be | ||
109 | @@ accessed by the resume code before it can restore the MMU. | ||
110 | @@ This means that the variable has to be close enough for the | ||
111 | @@ code to read it... since the .text segment needs to be RO, | ||
112 | @@ the data segment can be the only place to put this code. | ||
113 | |||
114 | .data | ||
115 | |||
116 | .global s3c2410_sleep_save_phys | ||
117 | s3c2410_sleep_save_phys: | ||
118 | .word 0 | ||
119 | |||
120 | /* s3c2410_cpu_resume | ||
121 | * | ||
122 | * resume code entry for bootloader to call | ||
123 | * | ||
124 | * we must put this code here in the data segment as we have no | ||
125 | * other way of restoring the stack pointer after sleep, and we | ||
126 | * must not write to the code segment (code is read-only) | ||
127 | */ | ||
128 | |||
129 | ENTRY(s3c2410_cpu_resume) | ||
130 | mov r0, #PSR_I_BIT | PSR_F_BIT | MODE_SVC | ||
131 | msr cpsr_c, r0 | ||
132 | |||
133 | @@ load UART to allow us to print the two characters for | ||
134 | @@ resume debug | ||
135 | |||
136 | mov r2, #S3C2410_PA_UART & 0xff000000 | ||
137 | orr r2, r2, #S3C2410_PA_UART & 0xff000 | ||
138 | |||
139 | #if 0 | ||
140 | /* SMDK2440 LED set */ | ||
141 | mov r14, #S3C2410_PA_GPIO | ||
142 | ldr r12, [ r14, #0x54 ] | ||
143 | bic r12, r12, #3<<4 | ||
144 | orr r12, r12, #1<<7 | ||
145 | str r12, [ r14, #0x54 ] | ||
146 | #endif | ||
147 | |||
148 | #ifdef CONFIG_DEBUG_RESUME | ||
149 | mov r3, #'L' | ||
150 | strb r3, [ r2, #S3C2410_UTXH ] | ||
151 | 1001: | ||
152 | ldrb r14, [ r3, #S3C2410_UTRSTAT ] | ||
153 | tst r14, #S3C2410_UTRSTAT_TXE | ||
154 | beq 1001b | ||
155 | #endif /* CONFIG_DEBUG_RESUME */ | ||
156 | |||
157 | mov r1, #0 | ||
158 | mcr p15, 0, r1, c8, c7, 0 @@ invalidate I & D TLBs | ||
159 | mcr p15, 0, r1, c7, c7, 0 @@ invalidate I & D caches | ||
160 | |||
161 | ldr r0, s3c2410_sleep_save_phys @ address of restore block | ||
162 | ldmia r0, { r4 - r13 } | ||
163 | |||
164 | mcr p15, 0, r4, c15, c1, 0 @ CP access register | ||
165 | mcr p15, 0, r5, c13, c0, 0 @ PID | ||
166 | mcr p15, 0, r6, c3, c0, 0 @ Domain ID | ||
167 | mcr p15, 0, r7, c2, c0, 0 @ translation table base | ||
168 | mcr p15, 0, r8, c1, c1, 0 @ auxilliary control | ||
169 | |||
170 | #ifdef CONFIG_DEBUG_RESUME | ||
171 | mov r3, #'R' | ||
172 | strb r3, [ r2, #S3C2410_UTXH ] | ||
173 | #endif | ||
174 | |||
175 | ldr r2, =resume_with_mmu | ||
176 | mcr p15, 0, r9, c1, c0, 0 @ turn on MMU, etc | ||
177 | nop @ second-to-last before mmu | ||
178 | mov pc, r2 @ go back to virtual address | ||
179 | |||
180 | .ltorg | ||
diff --git a/arch/arm/mach-s3c2410/time.c b/arch/arm/mach-s3c2410/time.c new file mode 100644 index 000000000000..179f0e031af4 --- /dev/null +++ b/arch/arm/mach-s3c2410/time.c | |||
@@ -0,0 +1,256 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/time.c | ||
2 | * | ||
3 | * Copyright (C) 2003-2005 Simtec Electronics | ||
4 | * Ben Dooks, <ben@simtec.co.uk> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/config.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/sched.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/err.h> | ||
27 | |||
28 | #include <asm/system.h> | ||
29 | #include <asm/leds.h> | ||
30 | #include <asm/mach-types.h> | ||
31 | |||
32 | #include <asm/io.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/arch/map.h> | ||
35 | #include <asm/arch/regs-timer.h> | ||
36 | #include <asm/arch/regs-irq.h> | ||
37 | #include <asm/mach/time.h> | ||
38 | #include <asm/hardware/clock.h> | ||
39 | |||
40 | #include "clock.h" | ||
41 | |||
42 | static unsigned long timer_startval; | ||
43 | static unsigned long timer_usec_ticks; | ||
44 | |||
45 | #define TIMER_USEC_SHIFT 16 | ||
46 | |||
47 | /* we use the shifted arithmetic to work out the ratio of timer ticks | ||
48 | * to usecs, as often the peripheral clock is not a nice even multiple | ||
49 | * of 1MHz. | ||
50 | * | ||
51 | * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok | ||
52 | * for the current HZ value of 200 without producing overflows. | ||
53 | * | ||
54 | * Original patch by Dimitry Andric, updated by Ben Dooks | ||
55 | */ | ||
56 | |||
57 | |||
58 | /* timer_mask_usec_ticks | ||
59 | * | ||
60 | * given a clock and divisor, make the value to pass into timer_ticks_to_usec | ||
61 | * to scale the ticks into usecs | ||
62 | */ | ||
63 | |||
64 | static inline unsigned long | ||
65 | timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk) | ||
66 | { | ||
67 | unsigned long den = pclk / 1000; | ||
68 | |||
69 | return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; | ||
70 | } | ||
71 | |||
72 | /* timer_ticks_to_usec | ||
73 | * | ||
74 | * convert timer ticks to usec. | ||
75 | */ | ||
76 | |||
77 | static inline unsigned long timer_ticks_to_usec(unsigned long ticks) | ||
78 | { | ||
79 | unsigned long res; | ||
80 | |||
81 | res = ticks * timer_usec_ticks; | ||
82 | res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */ | ||
83 | |||
84 | return res >> TIMER_USEC_SHIFT; | ||
85 | } | ||
86 | |||
87 | /*** | ||
88 | * Returns microsecond since last clock interrupt. Note that interrupts | ||
89 | * will have been disabled by do_gettimeoffset() | ||
90 | * IRQs are disabled before entering here from do_gettimeofday() | ||
91 | */ | ||
92 | |||
93 | #define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0)) | ||
94 | |||
95 | static unsigned long s3c2410_gettimeoffset (void) | ||
96 | { | ||
97 | unsigned long tdone; | ||
98 | unsigned long irqpend; | ||
99 | unsigned long tval; | ||
100 | |||
101 | /* work out how many ticks have gone since last timer interrupt */ | ||
102 | |||
103 | tval = __raw_readl(S3C2410_TCNTO(4)); | ||
104 | tdone = timer_startval - tval; | ||
105 | |||
106 | /* check to see if there is an interrupt pending */ | ||
107 | |||
108 | irqpend = __raw_readl(S3C2410_SRCPND); | ||
109 | if (irqpend & SRCPND_TIMER4) { | ||
110 | /* re-read the timer, and try and fix up for the missed | ||
111 | * interrupt. Note, the interrupt may go off before the | ||
112 | * timer has re-loaded from wrapping. | ||
113 | */ | ||
114 | |||
115 | tval = __raw_readl(S3C2410_TCNTO(4)); | ||
116 | tdone = timer_startval - tval; | ||
117 | |||
118 | if (tval != 0) | ||
119 | tdone += timer_startval; | ||
120 | } | ||
121 | |||
122 | return timer_ticks_to_usec(tdone); | ||
123 | } | ||
124 | |||
125 | |||
126 | /* | ||
127 | * IRQ handler for the timer | ||
128 | */ | ||
129 | static irqreturn_t | ||
130 | s3c2410_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
131 | { | ||
132 | write_seqlock(&xtime_lock); | ||
133 | timer_tick(regs); | ||
134 | write_sequnlock(&xtime_lock); | ||
135 | return IRQ_HANDLED; | ||
136 | } | ||
137 | |||
138 | static struct irqaction s3c2410_timer_irq = { | ||
139 | .name = "S3C2410 Timer Tick", | ||
140 | .flags = SA_INTERRUPT, | ||
141 | .handler = s3c2410_timer_interrupt | ||
142 | }; | ||
143 | |||
144 | /* | ||
145 | * Set up timer interrupt, and return the current time in seconds. | ||
146 | * | ||
147 | * Currently we only use timer4, as it is the only timer which has no | ||
148 | * other function that can be exploited externally | ||
149 | */ | ||
150 | static void s3c2410_timer_setup (void) | ||
151 | { | ||
152 | unsigned long tcon; | ||
153 | unsigned long tcnt; | ||
154 | unsigned long tcfg1; | ||
155 | unsigned long tcfg0; | ||
156 | |||
157 | tcnt = 0xffff; /* default value for tcnt */ | ||
158 | |||
159 | /* read the current timer configuration bits */ | ||
160 | |||
161 | tcon = __raw_readl(S3C2410_TCON); | ||
162 | tcfg1 = __raw_readl(S3C2410_TCFG1); | ||
163 | tcfg0 = __raw_readl(S3C2410_TCFG0); | ||
164 | |||
165 | /* configure the system for whichever machine is in use */ | ||
166 | |||
167 | if (machine_is_bast() || machine_is_vr1000()) { | ||
168 | /* timer is at 12MHz, scaler is 1 */ | ||
169 | timer_usec_ticks = timer_mask_usec_ticks(1, 12000000); | ||
170 | tcnt = 12000000 / HZ; | ||
171 | |||
172 | tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; | ||
173 | tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1; | ||
174 | } else { | ||
175 | unsigned long pclk; | ||
176 | struct clk *clk; | ||
177 | |||
178 | /* for the h1940 (and others), we use the pclk from the core | ||
179 | * to generate the timer values. since values around 50 to | ||
180 | * 70MHz are not values we can directly generate the timer | ||
181 | * value from, we need to pre-scale and divide before using it. | ||
182 | * | ||
183 | * for instance, using 50.7MHz and dividing by 6 gives 8.45MHz | ||
184 | * (8.45 ticks per usec) | ||
185 | */ | ||
186 | |||
187 | /* this is used as default if no other timer can be found */ | ||
188 | |||
189 | clk = clk_get(NULL, "timers"); | ||
190 | if (IS_ERR(clk)) | ||
191 | panic("failed to get clock for system timer"); | ||
192 | |||
193 | clk_use(clk); | ||
194 | clk_enable(clk); | ||
195 | |||
196 | pclk = clk_get_rate(clk); | ||
197 | |||
198 | /* configure clock tick */ | ||
199 | |||
200 | timer_usec_ticks = timer_mask_usec_ticks(6, pclk); | ||
201 | |||
202 | tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; | ||
203 | tcfg1 |= S3C2410_TCFG1_MUX4_DIV2; | ||
204 | |||
205 | tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; | ||
206 | tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT; | ||
207 | |||
208 | tcnt = (pclk / 6) / HZ; | ||
209 | } | ||
210 | |||
211 | /* timers reload after counting zero, so reduce the count by 1 */ | ||
212 | |||
213 | tcnt--; | ||
214 | |||
215 | printk("timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx, usec %08lx\n", | ||
216 | tcon, tcnt, tcfg0, tcfg1, timer_usec_ticks); | ||
217 | |||
218 | /* check to see if timer is within 16bit range... */ | ||
219 | if (tcnt > 0xffff) { | ||
220 | panic("setup_timer: HZ is too small, cannot configure timer!"); | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | __raw_writel(tcfg1, S3C2410_TCFG1); | ||
225 | __raw_writel(tcfg0, S3C2410_TCFG0); | ||
226 | |||
227 | timer_startval = tcnt; | ||
228 | __raw_writel(tcnt, S3C2410_TCNTB(4)); | ||
229 | |||
230 | /* ensure timer is stopped... */ | ||
231 | |||
232 | tcon &= ~(7<<20); | ||
233 | tcon |= S3C2410_TCON_T4RELOAD; | ||
234 | tcon |= S3C2410_TCON_T4MANUALUPD; | ||
235 | |||
236 | __raw_writel(tcon, S3C2410_TCON); | ||
237 | __raw_writel(tcnt, S3C2410_TCNTB(4)); | ||
238 | __raw_writel(tcnt, S3C2410_TCMPB(4)); | ||
239 | |||
240 | /* start the timer running */ | ||
241 | tcon |= S3C2410_TCON_T4START; | ||
242 | tcon &= ~S3C2410_TCON_T4MANUALUPD; | ||
243 | __raw_writel(tcon, S3C2410_TCON); | ||
244 | } | ||
245 | |||
246 | static void __init s3c2410_timer_init (void) | ||
247 | { | ||
248 | s3c2410_timer_setup(); | ||
249 | setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); | ||
250 | } | ||
251 | |||
252 | struct sys_timer s3c24xx_timer = { | ||
253 | .init = s3c2410_timer_init, | ||
254 | .offset = s3c2410_gettimeoffset, | ||
255 | .resume = s3c2410_timer_setup | ||
256 | }; | ||
diff --git a/arch/arm/mach-s3c2410/usb-simtec.c b/arch/arm/mach-s3c2410/usb-simtec.c new file mode 100644 index 000000000000..7f2b61362976 --- /dev/null +++ b/arch/arm/mach-s3c2410/usb-simtec.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/usb-simtec.c | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * http://www.simtec.co.uk/products/EB2410ITX/ | ||
7 | * | ||
8 | * Simtec BAST and Thorcom VR1000 USB port support functions | ||
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 | * Modifications: | ||
15 | * 14-Sep-2004 BJD Created | ||
16 | * 18-Oct-2004 BJD Cleanups, and added code to report OC cleared | ||
17 | */ | ||
18 | |||
19 | #define DEBUG | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <linux/timer.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/device.h> | ||
28 | |||
29 | #include <asm/mach/arch.h> | ||
30 | #include <asm/mach/map.h> | ||
31 | #include <asm/mach/irq.h> | ||
32 | |||
33 | #include <asm/arch/bast-map.h> | ||
34 | #include <asm/arch/bast-irq.h> | ||
35 | #include <asm/arch/usb-control.h> | ||
36 | #include <asm/arch/regs-gpio.h> | ||
37 | |||
38 | #include <asm/hardware.h> | ||
39 | #include <asm/io.h> | ||
40 | #include <asm/irq.h> | ||
41 | #include <asm/mach-types.h> | ||
42 | |||
43 | #include "devs.h" | ||
44 | #include "usb-simtec.h" | ||
45 | |||
46 | /* control power and monitor over-current events on various Simtec | ||
47 | * designed boards. | ||
48 | */ | ||
49 | |||
50 | static void | ||
51 | usb_simtec_powercontrol(int port, int to) | ||
52 | { | ||
53 | pr_debug("usb_simtec_powercontrol(%d,%d)\n", port, to); | ||
54 | |||
55 | if (port == 1) | ||
56 | s3c2410_gpio_setpin(S3C2410_GPB4, to ? 0:1); | ||
57 | } | ||
58 | |||
59 | static irqreturn_t | ||
60 | usb_simtec_ocirq(int irq, void *pw, struct pt_regs *regs) | ||
61 | { | ||
62 | struct s3c2410_hcd_info *info = (struct s3c2410_hcd_info *)pw; | ||
63 | |||
64 | if (s3c2410_gpio_getpin(S3C2410_GPG10) == 0) { | ||
65 | pr_debug("usb_simtec: over-current irq (oc detected)\n"); | ||
66 | s3c2410_report_oc(info, 3); | ||
67 | } else { | ||
68 | pr_debug("usb_simtec: over-current irq (oc cleared)\n"); | ||
69 | s3c2410_report_oc(info, 0); | ||
70 | } | ||
71 | |||
72 | return IRQ_HANDLED; | ||
73 | } | ||
74 | |||
75 | static void usb_simtec_enableoc(struct s3c2410_hcd_info *info, int on) | ||
76 | { | ||
77 | int ret; | ||
78 | |||
79 | if (on) { | ||
80 | ret = request_irq(IRQ_USBOC, usb_simtec_ocirq, SA_INTERRUPT, | ||
81 | "USB Over-current", info); | ||
82 | if (ret != 0) { | ||
83 | printk(KERN_ERR "failed to request usb oc irq\n"); | ||
84 | } | ||
85 | |||
86 | set_irq_type(IRQ_USBOC, IRQT_BOTHEDGE); | ||
87 | } else { | ||
88 | free_irq(IRQ_USBOC, info); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | static struct s3c2410_hcd_info usb_simtec_info = { | ||
93 | .port[0] = { | ||
94 | .flags = S3C_HCDFLG_USED | ||
95 | }, | ||
96 | .port[1] = { | ||
97 | .flags = S3C_HCDFLG_USED | ||
98 | }, | ||
99 | |||
100 | .power_control = usb_simtec_powercontrol, | ||
101 | .enable_oc = usb_simtec_enableoc, | ||
102 | }; | ||
103 | |||
104 | |||
105 | int usb_simtec_init(void) | ||
106 | { | ||
107 | printk("USB Power Control, (c) 2004 Simtec Electronics\n"); | ||
108 | s3c_device_usb.dev.platform_data = &usb_simtec_info; | ||
109 | |||
110 | s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP); | ||
111 | s3c2410_gpio_setpin(S3C2410_GPB4, 1); | ||
112 | return 0; | ||
113 | } | ||
diff --git a/arch/arm/mach-s3c2410/usb-simtec.h b/arch/arm/mach-s3c2410/usb-simtec.h new file mode 100644 index 000000000000..92c0cc83aeec --- /dev/null +++ b/arch/arm/mach-s3c2410/usb-simtec.h | |||
@@ -0,0 +1,19 @@ | |||
1 | /* linux/arch/arm/mach-s3c2410/usb-simtec.c | ||
2 | * | ||
3 | * Copyright (c) 2004 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * http://www.simtec.co.uk/products/EB2410ITX/ | ||
7 | * | ||
8 | * Simtec BAST and Thorcom VR1000 USB port support functions | ||
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 | * Modifications: | ||
15 | * 20-Aug-2004 BJD Created | ||
16 | */ | ||
17 | |||
18 | extern int usb_simtec_init(void); | ||
19 | |||