diff options
author | Ben Dooks <ben@simtec.co.uk> | 2009-07-30 18:23:37 -0400 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-07-30 18:22:55 -0400 |
commit | 140780ab5a2bc04ccff77337c3a27f3b44182a91 (patch) | |
tree | 719cc3986597f78ee9197404e31738b484de11c7 /arch/arm/plat-s3c24xx/s3c2412-iotiming.c | |
parent | 25400036867fa7a135fca17810555400d176acaa (diff) |
ARM: S3C24XX: CPUFREQ: S3C2412/S3C2443 IO timing support
Add IO bank timing support for S3C2412/S3C2443.
Signed-off-by: Ben Dooks <ben@simtec.co.uk>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/plat-s3c24xx/s3c2412-iotiming.c')
-rw-r--r-- | arch/arm/plat-s3c24xx/s3c2412-iotiming.c | 261 |
1 files changed, 261 insertions, 0 deletions
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 | } | ||