Commit | Line | Data |
---|---|---|
bc157b75 HS |
1 | /* |
2 | * Static Memory Controller for AT32 chips | |
3 | * | |
4 | * Copyright (C) 2006 Atmel Corporation | |
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 | */ | |
bc157b75 HS |
10 | #include <linux/clk.h> |
11 | #include <linux/err.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/platform_device.h> | |
5a0e3ad6 | 15 | #include <linux/slab.h> |
bc157b75 HS |
16 | |
17 | #include <asm/io.h> | |
3663b736 | 18 | #include <mach/smc.h> |
bc157b75 HS |
19 | |
20 | #include "hsmc.h" | |
21 | ||
22 | #define NR_CHIP_SELECTS 6 | |
23 | ||
24 | struct hsmc { | |
25 | void __iomem *regs; | |
26 | struct clk *pclk; | |
27 | struct clk *mck; | |
28 | }; | |
29 | ||
30 | static struct hsmc *hsmc; | |
31 | ||
af818471 KNG |
32 | void smc_set_timing(struct smc_config *config, |
33 | const struct smc_timing *timing) | |
bc157b75 | 34 | { |
af818471 KNG |
35 | int recover; |
36 | int cycle; | |
37 | ||
bc157b75 | 38 | unsigned long mul; |
bc157b75 | 39 | |
af818471 KNG |
40 | /* Reset all SMC timings */ |
41 | config->ncs_read_setup = 0; | |
42 | config->nrd_setup = 0; | |
43 | config->ncs_write_setup = 0; | |
44 | config->nwe_setup = 0; | |
45 | config->ncs_read_pulse = 0; | |
46 | config->nrd_pulse = 0; | |
47 | config->ncs_write_pulse = 0; | |
48 | config->nwe_pulse = 0; | |
49 | config->read_cycle = 0; | |
50 | config->write_cycle = 0; | |
bc157b75 HS |
51 | |
52 | /* | |
53 | * cycles = x / T = x * f | |
54 | * = ((x * 1000000000) * ((f * 65536) / 1000000000)) / 65536 | |
55 | * = ((x * 1000000000) * (((f / 10000) * 65536) / 100000)) / 65536 | |
56 | */ | |
57 | mul = (clk_get_rate(hsmc->mck) / 10000) << 16; | |
58 | mul /= 100000; | |
59 | ||
60 | #define ns2cyc(x) ((((x) * mul) + 65535) >> 16) | |
61 | ||
af818471 KNG |
62 | if (timing->ncs_read_setup > 0) |
63 | config->ncs_read_setup = ns2cyc(timing->ncs_read_setup); | |
64 | ||
65 | if (timing->nrd_setup > 0) | |
66 | config->nrd_setup = ns2cyc(timing->nrd_setup); | |
67 | ||
68 | if (timing->ncs_write_setup > 0) | |
69 | config->ncs_write_setup = ns2cyc(timing->ncs_write_setup); | |
70 | ||
71 | if (timing->nwe_setup > 0) | |
72 | config->nwe_setup = ns2cyc(timing->nwe_setup); | |
73 | ||
74 | if (timing->ncs_read_pulse > 0) | |
75 | config->ncs_read_pulse = ns2cyc(timing->ncs_read_pulse); | |
76 | ||
77 | if (timing->nrd_pulse > 0) | |
78 | config->nrd_pulse = ns2cyc(timing->nrd_pulse); | |
79 | ||
80 | if (timing->ncs_write_pulse > 0) | |
81 | config->ncs_write_pulse = ns2cyc(timing->ncs_write_pulse); | |
82 | ||
83 | if (timing->nwe_pulse > 0) | |
84 | config->nwe_pulse = ns2cyc(timing->nwe_pulse); | |
85 | ||
86 | if (timing->read_cycle > 0) | |
87 | config->read_cycle = ns2cyc(timing->read_cycle); | |
88 | ||
89 | if (timing->write_cycle > 0) | |
90 | config->write_cycle = ns2cyc(timing->write_cycle); | |
91 | ||
92 | /* Extend read cycle in needed */ | |
93 | if (timing->ncs_read_recover > 0) | |
94 | recover = ns2cyc(timing->ncs_read_recover); | |
95 | else | |
96 | recover = 1; | |
97 | ||
98 | cycle = config->ncs_read_setup + config->ncs_read_pulse + recover; | |
99 | ||
100 | if (config->read_cycle < cycle) | |
101 | config->read_cycle = cycle; | |
102 | ||
103 | /* Extend read cycle in needed */ | |
104 | if (timing->nrd_recover > 0) | |
105 | recover = ns2cyc(timing->nrd_recover); | |
106 | else | |
107 | recover = 1; | |
108 | ||
109 | cycle = config->nrd_setup + config->nrd_pulse + recover; | |
110 | ||
111 | if (config->read_cycle < cycle) | |
112 | config->read_cycle = cycle; | |
113 | ||
114 | /* Extend write cycle in needed */ | |
115 | if (timing->ncs_write_recover > 0) | |
116 | recover = ns2cyc(timing->ncs_write_recover); | |
117 | else | |
118 | recover = 1; | |
119 | ||
120 | cycle = config->ncs_write_setup + config->ncs_write_pulse + recover; | |
121 | ||
122 | if (config->write_cycle < cycle) | |
123 | config->write_cycle = cycle; | |
124 | ||
125 | /* Extend write cycle in needed */ | |
126 | if (timing->nwe_recover > 0) | |
127 | recover = ns2cyc(timing->nwe_recover); | |
128 | else | |
129 | recover = 1; | |
130 | ||
131 | cycle = config->nwe_setup + config->nwe_pulse + recover; | |
132 | ||
133 | if (config->write_cycle < cycle) | |
134 | config->write_cycle = cycle; | |
135 | } | |
136 | EXPORT_SYMBOL(smc_set_timing); | |
137 | ||
138 | int smc_set_configuration(int cs, const struct smc_config *config) | |
139 | { | |
140 | unsigned long offset; | |
141 | u32 setup, pulse, cycle, mode; | |
142 | ||
143 | if (!hsmc) | |
144 | return -ENODEV; | |
145 | if (cs >= NR_CHIP_SELECTS) | |
146 | return -EINVAL; | |
147 | ||
148 | setup = (HSMC_BF(NWE_SETUP, config->nwe_setup) | |
149 | | HSMC_BF(NCS_WR_SETUP, config->ncs_write_setup) | |
150 | | HSMC_BF(NRD_SETUP, config->nrd_setup) | |
151 | | HSMC_BF(NCS_RD_SETUP, config->ncs_read_setup)); | |
152 | pulse = (HSMC_BF(NWE_PULSE, config->nwe_pulse) | |
153 | | HSMC_BF(NCS_WR_PULSE, config->ncs_write_pulse) | |
154 | | HSMC_BF(NRD_PULSE, config->nrd_pulse) | |
155 | | HSMC_BF(NCS_RD_PULSE, config->ncs_read_pulse)); | |
156 | cycle = (HSMC_BF(NWE_CYCLE, config->write_cycle) | |
157 | | HSMC_BF(NRD_CYCLE, config->read_cycle)); | |
bc157b75 HS |
158 | |
159 | switch (config->bus_width) { | |
160 | case 1: | |
161 | mode = HSMC_BF(DBW, HSMC_DBW_8_BITS); | |
162 | break; | |
163 | case 2: | |
164 | mode = HSMC_BF(DBW, HSMC_DBW_16_BITS); | |
165 | break; | |
166 | case 4: | |
167 | mode = HSMC_BF(DBW, HSMC_DBW_32_BITS); | |
168 | break; | |
169 | default: | |
170 | return -EINVAL; | |
171 | } | |
172 | ||
068d9f6e HCE |
173 | switch (config->nwait_mode) { |
174 | case 0: | |
175 | mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_DISABLED); | |
176 | break; | |
177 | case 1: | |
178 | mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_RESERVED); | |
179 | break; | |
180 | case 2: | |
181 | mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_FROZEN); | |
182 | break; | |
183 | case 3: | |
184 | mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_READY); | |
185 | break; | |
186 | default: | |
187 | return -EINVAL; | |
188 | } | |
189 | ||
190 | if (config->tdf_cycles) { | |
191 | mode |= HSMC_BF(TDF_CYCLES, config->tdf_cycles); | |
192 | } | |
193 | ||
bc157b75 HS |
194 | if (config->nrd_controlled) |
195 | mode |= HSMC_BIT(READ_MODE); | |
196 | if (config->nwe_controlled) | |
197 | mode |= HSMC_BIT(WRITE_MODE); | |
198 | if (config->byte_write) | |
199 | mode |= HSMC_BIT(BAT); | |
068d9f6e HCE |
200 | if (config->tdf_mode) |
201 | mode |= HSMC_BIT(TDF_MODE); | |
bc157b75 HS |
202 | |
203 | pr_debug("smc cs%d: setup/%08x pulse/%08x cycle/%08x mode/%08x\n", | |
204 | cs, setup, pulse, cycle, mode); | |
205 | ||
206 | offset = cs * 0x10; | |
207 | hsmc_writel(hsmc, SETUP0 + offset, setup); | |
208 | hsmc_writel(hsmc, PULSE0 + offset, pulse); | |
209 | hsmc_writel(hsmc, CYCLE0 + offset, cycle); | |
210 | hsmc_writel(hsmc, MODE0 + offset, mode); | |
211 | hsmc_readl(hsmc, MODE0); /* I/O barrier */ | |
212 | ||
213 | return 0; | |
214 | } | |
215 | EXPORT_SYMBOL(smc_set_configuration); | |
216 | ||
217 | static int hsmc_probe(struct platform_device *pdev) | |
218 | { | |
219 | struct resource *regs; | |
220 | struct clk *pclk, *mck; | |
221 | int ret; | |
222 | ||
223 | if (hsmc) | |
224 | return -EBUSY; | |
225 | ||
226 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
227 | if (!regs) | |
228 | return -ENXIO; | |
229 | pclk = clk_get(&pdev->dev, "pclk"); | |
230 | if (IS_ERR(pclk)) | |
231 | return PTR_ERR(pclk); | |
232 | mck = clk_get(&pdev->dev, "mck"); | |
233 | if (IS_ERR(mck)) { | |
234 | ret = PTR_ERR(mck); | |
235 | goto out_put_pclk; | |
236 | } | |
237 | ||
238 | ret = -ENOMEM; | |
239 | hsmc = kzalloc(sizeof(struct hsmc), GFP_KERNEL); | |
240 | if (!hsmc) | |
241 | goto out_put_clocks; | |
242 | ||
243 | clk_enable(pclk); | |
244 | clk_enable(mck); | |
245 | ||
246 | hsmc->pclk = pclk; | |
247 | hsmc->mck = mck; | |
248 | hsmc->regs = ioremap(regs->start, regs->end - regs->start + 1); | |
249 | if (!hsmc->regs) | |
250 | goto out_disable_clocks; | |
251 | ||
252 | dev_info(&pdev->dev, "Atmel Static Memory Controller at 0x%08lx\n", | |
253 | (unsigned long)regs->start); | |
254 | ||
255 | platform_set_drvdata(pdev, hsmc); | |
256 | ||
257 | return 0; | |
258 | ||
259 | out_disable_clocks: | |
260 | clk_disable(mck); | |
261 | clk_disable(pclk); | |
262 | kfree(hsmc); | |
263 | out_put_clocks: | |
264 | clk_put(mck); | |
265 | out_put_pclk: | |
266 | clk_put(pclk); | |
267 | hsmc = NULL; | |
268 | return ret; | |
269 | } | |
270 | ||
271 | static struct platform_driver hsmc_driver = { | |
272 | .probe = hsmc_probe, | |
273 | .driver = { | |
274 | .name = "smc", | |
275 | }, | |
276 | }; | |
277 | ||
278 | static int __init hsmc_init(void) | |
279 | { | |
280 | return platform_driver_register(&hsmc_driver); | |
281 | } | |
e1677ce4 | 282 | core_initcall(hsmc_init); |