diff options
Diffstat (limited to 'arch/arm/plat-s3c24xx')
-rw-r--r-- | arch/arm/plat-s3c24xx/Kconfig | 9 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h | 52 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/s3c2412-iotiming.c | 261 |
4 files changed, 322 insertions, 1 deletions
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig index ec9921b6848b..f0b9c73fe008 100644 --- a/arch/arm/plat-s3c24xx/Kconfig +++ b/arch/arm/plat-s3c24xx/Kconfig | |||
@@ -157,6 +157,15 @@ config S3C2410_CPUFREQ_UTILS | |||
157 | Internal node to select timing code that is common to the s3c2410 | 157 | Internal node to select timing code that is common to the s3c2410 |
158 | and s3c2440/s3c244 cpu frequency support. | 158 | and s3c2440/s3c244 cpu frequency support. |
159 | 159 | ||
160 | # cpu frequency support common to s3c2412, s3c2413 and s3c2442 | ||
161 | |||
162 | config S3C2412_IOTIMING | ||
163 | bool | ||
164 | depends on CPU_FREQ_S3C24XX && (CPU_S3C2412 || CPU_S3C2443) | ||
165 | help | ||
166 | Intel node to select io timing code that is common to the s3c2412 | ||
167 | and the s3c2443. | ||
168 | |||
160 | config MACH_SMDK | 169 | config MACH_SMDK |
161 | bool | 170 | bool |
162 | help | 171 | help |
diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index c60317b3491b..8759c070fd9a 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile | |||
@@ -40,6 +40,7 @@ obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o | |||
40 | obj-$(CONFIG_S3C2410_DMA) += dma.o | 40 | obj-$(CONFIG_S3C2410_DMA) += dma.o |
41 | obj-$(CONFIG_S3C24XX_ADC) += adc.o | 41 | obj-$(CONFIG_S3C24XX_ADC) += adc.o |
42 | obj-$(CONFIG_S3C2410_IOTIMING) += s3c2410-iotiming.o | 42 | obj-$(CONFIG_S3C2410_IOTIMING) += s3c2410-iotiming.o |
43 | obj-$(CONFIG_S3C2412_IOTIMING) += s3c2412-iotiming.o | ||
43 | obj-$(CONFIG_S3C2410_CPUFREQ_UTILS) += s3c2410-cpufreq-utils.o | 44 | obj-$(CONFIG_S3C2410_CPUFREQ_UTILS) += s3c2410-cpufreq-utils.o |
44 | 45 | ||
45 | # device specific setup and/or initialisation | 46 | # device specific setup and/or initialisation |
diff --git a/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h b/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h index 7938fb0bc387..f02b3c06c1e0 100644 --- a/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h +++ b/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h | |||
@@ -42,8 +42,44 @@ struct s3c2410_iobank_timing { | |||
42 | unsigned char nwait_en; /* nWait enabled for bank. */ | 42 | unsigned char nwait_en; /* nWait enabled for bank. */ |
43 | }; | 43 | }; |
44 | 44 | ||
45 | /** | ||
46 | * struct s3c2412_iobank_timing - io timings for PL092 (S3C2412) style IO | ||
47 | * @idcy: The idle cycle time between transactions. | ||
48 | * @wstrd: nCS release to end of read cycle. | ||
49 | * @wstwr: nCS release to end of write cycle. | ||
50 | * @wstoen: nCS assertion to nOE assertion time. | ||
51 | * @wstwen: nCS assertion to nWE assertion time. | ||
52 | * @wstbrd: Burst ready delay. | ||
53 | * @smbidcyr: Register cache for smbidcyr value. | ||
54 | * @smbwstrd: Register cache for smbwstrd value. | ||
55 | * @smbwstwr: Register cache for smbwstwr value. | ||
56 | * @smbwstoen: Register cache for smbwstoen value. | ||
57 | * @smbwstwen: Register cache for smbwstwen value. | ||
58 | * @smbwstbrd: Register cache for smbwstbrd value. | ||
59 | * | ||
60 | * Timing information for a IO bank on an S3C2412 or similar system which | ||
61 | * uses a PL093 block. | ||
62 | */ | ||
63 | struct s3c2412_iobank_timing { | ||
64 | unsigned int idcy; | ||
65 | unsigned int wstrd; | ||
66 | unsigned int wstwr; | ||
67 | unsigned int wstoen; | ||
68 | unsigned int wstwen; | ||
69 | unsigned int wstbrd; | ||
70 | |||
71 | /* register cache */ | ||
72 | unsigned char smbidcyr; | ||
73 | unsigned char smbwstrd; | ||
74 | unsigned char smbwstwr; | ||
75 | unsigned char smbwstoen; | ||
76 | unsigned char smbwstwen; | ||
77 | unsigned char smbwstbrd; | ||
78 | }; | ||
79 | |||
45 | union s3c_iobank { | 80 | union s3c_iobank { |
46 | struct s3c2410_iobank_timing *io_2410; | 81 | struct s3c2410_iobank_timing *io_2410; |
82 | struct s3c2412_iobank_timing *io_2412; | ||
47 | }; | 83 | }; |
48 | 84 | ||
49 | /** | 85 | /** |
@@ -174,6 +210,20 @@ extern void s3c2410_iotiming_set(struct s3c_cpufreq_config *cfg, | |||
174 | 210 | ||
175 | extern void s3c2410_set_fvco(struct s3c_cpufreq_config *cfg); | 211 | extern void s3c2410_set_fvco(struct s3c_cpufreq_config *cfg); |
176 | 212 | ||
213 | /* S3C2412 compatible routines */ | ||
214 | |||
215 | extern int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg, | ||
216 | struct s3c_iotimings *timings); | ||
217 | |||
218 | extern int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg, | ||
219 | struct s3c_iotimings *timings); | ||
220 | |||
221 | extern int s3c2412_iotiming_calc(struct s3c_cpufreq_config *cfg, | ||
222 | struct s3c_iotimings *iot); | ||
223 | |||
224 | extern void s3c2412_iotiming_set(struct s3c_cpufreq_config *cfg, | ||
225 | struct s3c_iotimings *iot); | ||
226 | |||
177 | #ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUG | 227 | #ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUG |
178 | #define s3c_freq_dbg(x...) printk(KERN_INFO x) | 228 | #define s3c_freq_dbg(x...) printk(KERN_INFO x) |
179 | #else | 229 | #else |
diff --git a/arch/arm/plat-s3c24xx/s3c2412-iotiming.c b/arch/arm/plat-s3c24xx/s3c2412-iotiming.c new file mode 100644 index 000000000000..a3648cba0eb9 --- /dev/null +++ b/arch/arm/plat-s3c24xx/s3c2412-iotiming.c | |||
@@ -0,0 +1,261 @@ | |||
1 | /* linux/arch/arm/plat-s3c24xx/s3c2412-iotiming.c | ||
2 | * | ||
3 | * Copyright (c) 2006,2008 Simtec Electronics | ||
4 | * http://armlinux.simtec.co.uk/ | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * | ||
7 | * S3C2412/S3C2443 (PL093 based) IO timing 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 version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/ioport.h> | ||
18 | #include <linux/cpufreq.h> | ||
19 | #include <linux/sysdev.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/err.h> | ||
23 | |||
24 | #include <linux/amba/pl093.h> | ||
25 | |||
26 | #include <asm/mach/arch.h> | ||
27 | #include <asm/mach/map.h> | ||
28 | |||
29 | #include <mach/regs-s3c2412-mem.h> | ||
30 | |||
31 | #include <plat/cpu.h> | ||
32 | #include <plat/cpu-freq-core.h> | ||
33 | #include <plat/clock.h> | ||
34 | |||
35 | #define print_ns(x) ((x) / 10), ((x) % 10) | ||
36 | |||
37 | /** | ||
38 | * s3c2412_print_timing - print timing infromation via printk. | ||
39 | * @pfx: The prefix to print each line with. | ||
40 | * @iot: The IO timing information | ||
41 | */ | ||
42 | static void s3c2412_print_timing(const char *pfx, struct s3c_iotimings *iot) | ||
43 | { | ||
44 | struct s3c2412_iobank_timing *bt; | ||
45 | unsigned int bank; | ||
46 | |||
47 | for (bank = 0; bank < MAX_BANKS; bank++) { | ||
48 | bt = iot->bank[bank].io_2412; | ||
49 | if (!bt) | ||
50 | continue; | ||
51 | |||
52 | printk(KERN_DEBUG "%s: %d: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d" | ||
53 | "wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d\n", pfx, bank, | ||
54 | print_ns(bt->idcy), | ||
55 | print_ns(bt->wstrd), | ||
56 | print_ns(bt->wstwr), | ||
57 | print_ns(bt->wstoen), | ||
58 | print_ns(bt->wstwen), | ||
59 | print_ns(bt->wstbrd)); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * to_div - turn a cycle length into a divisor setting. | ||
65 | * @cyc_tns: The cycle time in 10ths of nanoseconds. | ||
66 | * @clk_tns: The clock period in 10ths of nanoseconds. | ||
67 | */ | ||
68 | static inline unsigned int to_div(unsigned int cyc_tns, unsigned int clk_tns) | ||
69 | { | ||
70 | return cyc_tns ? DIV_ROUND_UP(cyc_tns, clk_tns) : 0; | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * calc_timing - calculate timing divisor value and check in range. | ||
75 | * @hwtm: The hardware timing in 10ths of nanoseconds. | ||
76 | * @clk_tns: The clock period in 10ths of nanoseconds. | ||
77 | * @err: Pointer to err variable to update in event of failure. | ||
78 | */ | ||
79 | static unsigned int calc_timing(unsigned int hwtm, unsigned int clk_tns, | ||
80 | unsigned int *err) | ||
81 | { | ||
82 | unsigned int ret = to_div(hwtm, clk_tns); | ||
83 | |||
84 | if (ret > 0xf) | ||
85 | *err = -EINVAL; | ||
86 | |||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * s3c2412_calc_bank - calculate the bank divisor settings. | ||
92 | * @cfg: The current frequency configuration. | ||
93 | * @bt: The bank timing. | ||
94 | */ | ||
95 | static int s3c2412_calc_bank(struct s3c_cpufreq_config *cfg, | ||
96 | struct s3c2412_iobank_timing *bt) | ||
97 | { | ||
98 | unsigned int hclk = cfg->freq.hclk_tns; | ||
99 | int err = 0; | ||
100 | |||
101 | bt->smbidcyr = calc_timing(bt->idcy, hclk, &err); | ||
102 | bt->smbwstrd = calc_timing(bt->wstrd, hclk, &err); | ||
103 | bt->smbwstwr = calc_timing(bt->wstwr, hclk, &err); | ||
104 | bt->smbwstoen = calc_timing(bt->wstoen, hclk, &err); | ||
105 | bt->smbwstwen = calc_timing(bt->wstwen, hclk, &err); | ||
106 | bt->smbwstbrd = calc_timing(bt->wstbrd, hclk, &err); | ||
107 | |||
108 | return err; | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * s3c2412_iotiming_calc - calculate all the bank divisor settings. | ||
113 | * @cfg: The current frequency configuration. | ||
114 | * @iot: The bank timing information. | ||
115 | * | ||
116 | * Calculate the timing information for all the banks that are | ||
117 | * configured as IO, using s3c2412_calc_bank(). | ||
118 | */ | ||
119 | int s3c2412_iotiming_calc(struct s3c_cpufreq_config *cfg, | ||
120 | struct s3c_iotimings *iot) | ||
121 | { | ||
122 | struct s3c2412_iobank_timing *bt; | ||
123 | int bank; | ||
124 | int ret; | ||
125 | |||
126 | for (bank = 0; bank < MAX_BANKS; bank++) { | ||
127 | bt = iot->bank[bank].io_2412; | ||
128 | if (!bt) | ||
129 | continue; | ||
130 | |||
131 | ret = s3c2412_calc_bank(cfg, bt); | ||
132 | if (ret) { | ||
133 | printk(KERN_ERR "%s: cannot calculate bank %d io\n", | ||
134 | __func__, bank); | ||
135 | goto err; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | err: | ||
141 | return ret; | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * s3c2412_iotiming_set - set the timing information | ||
146 | * @cfg: The current frequency configuration. | ||
147 | * @iot: The bank timing information. | ||
148 | * | ||
149 | * Set the IO bank information from the details calculated earlier from | ||
150 | * calling s3c2412_iotiming_calc(). | ||
151 | */ | ||
152 | void s3c2412_iotiming_set(struct s3c_cpufreq_config *cfg, | ||
153 | struct s3c_iotimings *iot) | ||
154 | { | ||
155 | struct s3c2412_iobank_timing *bt; | ||
156 | void __iomem *regs; | ||
157 | int bank; | ||
158 | |||
159 | /* set the io timings from the specifier */ | ||
160 | |||
161 | for (bank = 0; bank < MAX_BANKS; bank++) { | ||
162 | bt = iot->bank[bank].io_2412; | ||
163 | if (!bt) | ||
164 | continue; | ||
165 | |||
166 | regs = S3C2412_SSMC_BANK(bank); | ||
167 | |||
168 | __raw_writel(bt->smbidcyr, regs + SMBIDCYR); | ||
169 | __raw_writel(bt->smbwstrd, regs + SMBWSTRDR); | ||
170 | __raw_writel(bt->smbwstwr, regs + SMBWSTWRR); | ||
171 | __raw_writel(bt->smbwstoen, regs + SMBWSTOENR); | ||
172 | __raw_writel(bt->smbwstwen, regs + SMBWSTWENR); | ||
173 | __raw_writel(bt->smbwstbrd, regs + SMBWSTBRDR); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | static inline unsigned int s3c2412_decode_timing(unsigned int clock, u32 reg) | ||
178 | { | ||
179 | return (reg & 0xf) * clock; | ||
180 | } | ||
181 | |||
182 | static void s3c2412_iotiming_getbank(struct s3c_cpufreq_config *cfg, | ||
183 | struct s3c2412_iobank_timing *bt, | ||
184 | unsigned int bank) | ||
185 | { | ||
186 | unsigned long clk = cfg->freq.hclk_tns; /* ssmc clock??? */ | ||
187 | void __iomem *regs = S3C2412_SSMC_BANK(bank); | ||
188 | |||
189 | bt->idcy = s3c2412_decode_timing(clk, __raw_readl(regs + SMBIDCYR)); | ||
190 | bt->wstrd = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTRDR)); | ||
191 | bt->wstoen = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTOENR)); | ||
192 | bt->wstwen = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTWENR)); | ||
193 | bt->wstbrd = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTBRDR)); | ||
194 | } | ||
195 | |||
196 | /** | ||
197 | * bank_is_io - return true if bank is (possibly) IO. | ||
198 | * @bank: The bank number. | ||
199 | * @bankcfg: The value of S3C2412_EBI_BANKCFG. | ||
200 | */ | ||
201 | static inline bool bank_is_io(unsigned int bank, u32 bankcfg) | ||
202 | { | ||
203 | if (bank < 2) | ||
204 | return true; | ||
205 | |||
206 | return !(bankcfg & (1 << bank)); | ||
207 | } | ||
208 | |||
209 | int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg, | ||
210 | struct s3c_iotimings *timings) | ||
211 | { | ||
212 | struct s3c2412_iobank_timing *bt; | ||
213 | u32 bankcfg = __raw_readl(S3C2412_EBI_BANKCFG); | ||
214 | unsigned int bank; | ||
215 | |||
216 | /* look through all banks to see what is currently set. */ | ||
217 | |||
218 | for (bank = 0; bank < MAX_BANKS; bank++) { | ||
219 | if (!bank_is_io(bank, bankcfg)) | ||
220 | continue; | ||
221 | |||
222 | bt = kzalloc(sizeof(struct s3c2412_iobank_timing), GFP_KERNEL); | ||
223 | if (!bt) { | ||
224 | printk(KERN_ERR "%s: no memory for bank\n", __func__); | ||
225 | return -ENOMEM; | ||
226 | } | ||
227 | |||
228 | timings->bank[bank].io_2412 = bt; | ||
229 | s3c2412_iotiming_getbank(cfg, bt, bank); | ||
230 | } | ||
231 | |||
232 | s3c2412_print_timing("get", timings); | ||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | /* this is in here as it is so small, it doesn't currently warrant a file | ||
237 | * to itself. We expect that any s3c24xx needing this is going to also | ||
238 | * need the iotiming support. | ||
239 | */ | ||
240 | void s3c2412_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg) | ||
241 | { | ||
242 | struct s3c_cpufreq_board *board = cfg->board; | ||
243 | u32 refresh; | ||
244 | |||
245 | WARN_ON(board == NULL); | ||
246 | |||
247 | /* Reduce both the refresh time (in ns) and the frequency (in MHz) | ||
248 | * down to ensure that we do not overflow 32 bit numbers. | ||
249 | * | ||
250 | * This should work for HCLK up to 133MHz and refresh period up | ||
251 | * to 30usec. | ||
252 | */ | ||
253 | |||
254 | refresh = (cfg->freq.hclk / 100) * (board->refresh / 10); | ||
255 | refresh = DIV_ROUND_UP(refresh, (1000 * 1000)); /* apply scale */ | ||
256 | refresh &= ((1 << 16) - 1); | ||
257 | |||
258 | s3c_freq_dbg("%s: refresh value %u\n", __func__, (unsigned int)refresh); | ||
259 | |||
260 | __raw_writel(refresh, S3C2412_REFRESH); | ||
261 | } | ||