diff options
Diffstat (limited to 'arch/arm/mach-omap2/gpmc-onenand.c')
-rw-r--r-- | arch/arm/mach-omap2/gpmc-onenand.c | 113 |
1 files changed, 82 insertions, 31 deletions
diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c index 3a7d25fb00ef..d776ded9830d 100644 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ b/arch/arm/mach-omap2/gpmc-onenand.c | |||
@@ -94,7 +94,7 @@ static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base) | |||
94 | } | 94 | } |
95 | 95 | ||
96 | static void set_onenand_cfg(void __iomem *onenand_base, int latency, | 96 | static void set_onenand_cfg(void __iomem *onenand_base, int latency, |
97 | int sync_read, int sync_write, int hf) | 97 | int sync_read, int sync_write, int hf, int vhf) |
98 | { | 98 | { |
99 | u32 reg; | 99 | u32 reg; |
100 | 100 | ||
@@ -114,12 +114,57 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency, | |||
114 | reg |= ONENAND_SYS_CFG1_HF; | 114 | reg |= ONENAND_SYS_CFG1_HF; |
115 | else | 115 | else |
116 | reg &= ~ONENAND_SYS_CFG1_HF; | 116 | reg &= ~ONENAND_SYS_CFG1_HF; |
117 | if (vhf) | ||
118 | reg |= ONENAND_SYS_CFG1_VHF; | ||
119 | else | ||
120 | reg &= ~ONENAND_SYS_CFG1_VHF; | ||
117 | writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); | 121 | writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); |
118 | } | 122 | } |
119 | 123 | ||
124 | static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg, | ||
125 | void __iomem *onenand_base, bool *clk_dep) | ||
126 | { | ||
127 | u16 ver = readw(onenand_base + ONENAND_REG_VERSION_ID); | ||
128 | int freq = 0; | ||
129 | |||
130 | if (cfg->get_freq) { | ||
131 | struct onenand_freq_info fi; | ||
132 | |||
133 | fi.maf_id = readw(onenand_base + ONENAND_REG_MANUFACTURER_ID); | ||
134 | fi.dev_id = readw(onenand_base + ONENAND_REG_DEVICE_ID); | ||
135 | fi.ver_id = ver; | ||
136 | freq = cfg->get_freq(&fi, clk_dep); | ||
137 | if (freq) | ||
138 | return freq; | ||
139 | } | ||
140 | |||
141 | switch ((ver >> 4) & 0xf) { | ||
142 | case 0: | ||
143 | freq = 40; | ||
144 | break; | ||
145 | case 1: | ||
146 | freq = 54; | ||
147 | break; | ||
148 | case 2: | ||
149 | freq = 66; | ||
150 | break; | ||
151 | case 3: | ||
152 | freq = 83; | ||
153 | break; | ||
154 | case 4: | ||
155 | freq = 104; | ||
156 | break; | ||
157 | default: | ||
158 | freq = 54; | ||
159 | break; | ||
160 | } | ||
161 | |||
162 | return freq; | ||
163 | } | ||
164 | |||
120 | static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, | 165 | static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, |
121 | void __iomem *onenand_base, | 166 | void __iomem *onenand_base, |
122 | int freq) | 167 | int *freq_ptr) |
123 | { | 168 | { |
124 | struct gpmc_timings t; | 169 | struct gpmc_timings t; |
125 | const int t_cer = 15; | 170 | const int t_cer = 15; |
@@ -130,10 +175,11 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, | |||
130 | const int t_wph = 30; | 175 | const int t_wph = 30; |
131 | int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo; | 176 | int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo; |
132 | int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency; | 177 | int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency; |
133 | int first_time = 0, hf = 0, sync_read = 0, sync_write = 0; | 178 | int first_time = 0, hf = 0, vhf = 0, sync_read = 0, sync_write = 0; |
134 | int err, ticks_cez; | 179 | int err, ticks_cez; |
135 | int cs = cfg->cs; | 180 | int cs = cfg->cs, freq = *freq_ptr; |
136 | u32 reg; | 181 | u32 reg; |
182 | bool clk_dep = false; | ||
137 | 183 | ||
138 | if (cfg->flags & ONENAND_SYNC_READ) { | 184 | if (cfg->flags & ONENAND_SYNC_READ) { |
139 | sync_read = 1; | 185 | sync_read = 1; |
@@ -148,27 +194,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, | |||
148 | err = omap2_onenand_set_async_mode(cs, onenand_base); | 194 | err = omap2_onenand_set_async_mode(cs, onenand_base); |
149 | if (err) | 195 | if (err) |
150 | return err; | 196 | return err; |
151 | reg = readw(onenand_base + ONENAND_REG_VERSION_ID); | 197 | freq = omap2_onenand_get_freq(cfg, onenand_base, &clk_dep); |
152 | switch ((reg >> 4) & 0xf) { | ||
153 | case 0: | ||
154 | freq = 40; | ||
155 | break; | ||
156 | case 1: | ||
157 | freq = 54; | ||
158 | break; | ||
159 | case 2: | ||
160 | freq = 66; | ||
161 | break; | ||
162 | case 3: | ||
163 | freq = 83; | ||
164 | break; | ||
165 | case 4: | ||
166 | freq = 104; | ||
167 | break; | ||
168 | default: | ||
169 | freq = 54; | ||
170 | break; | ||
171 | } | ||
172 | first_time = 1; | 198 | first_time = 1; |
173 | } | 199 | } |
174 | 200 | ||
@@ -180,7 +206,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, | |||
180 | t_avdh = 2; | 206 | t_avdh = 2; |
181 | t_ach = 3; | 207 | t_ach = 3; |
182 | t_aavdh = 6; | 208 | t_aavdh = 6; |
183 | t_rdyo = 9; | 209 | t_rdyo = 6; |
184 | break; | 210 | break; |
185 | case 83: | 211 | case 83: |
186 | min_gpmc_clk_period = 12000; /* 83 MHz */ | 212 | min_gpmc_clk_period = 12000; /* 83 MHz */ |
@@ -217,16 +243,36 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, | |||
217 | gpmc_clk_ns = gpmc_ticks_to_ns(div); | 243 | gpmc_clk_ns = gpmc_ticks_to_ns(div); |
218 | if (gpmc_clk_ns < 15) /* >66Mhz */ | 244 | if (gpmc_clk_ns < 15) /* >66Mhz */ |
219 | hf = 1; | 245 | hf = 1; |
220 | if (hf) | 246 | if (gpmc_clk_ns < 12) /* >83Mhz */ |
247 | vhf = 1; | ||
248 | if (vhf) | ||
249 | latency = 8; | ||
250 | else if (hf) | ||
221 | latency = 6; | 251 | latency = 6; |
222 | else if (gpmc_clk_ns >= 25) /* 40 MHz*/ | 252 | else if (gpmc_clk_ns >= 25) /* 40 MHz*/ |
223 | latency = 3; | 253 | latency = 3; |
224 | else | 254 | else |
225 | latency = 4; | 255 | latency = 4; |
226 | 256 | ||
257 | if (clk_dep) { | ||
258 | if (gpmc_clk_ns < 12) { /* >83Mhz */ | ||
259 | t_ces = 3; | ||
260 | t_avds = 4; | ||
261 | } else if (gpmc_clk_ns < 15) { /* >66Mhz */ | ||
262 | t_ces = 5; | ||
263 | t_avds = 4; | ||
264 | } else if (gpmc_clk_ns < 25) { /* >40Mhz */ | ||
265 | t_ces = 6; | ||
266 | t_avds = 5; | ||
267 | } else { | ||
268 | t_ces = 7; | ||
269 | t_avds = 7; | ||
270 | } | ||
271 | } | ||
272 | |||
227 | if (first_time) | 273 | if (first_time) |
228 | set_onenand_cfg(onenand_base, latency, | 274 | set_onenand_cfg(onenand_base, latency, |
229 | sync_read, sync_write, hf); | 275 | sync_read, sync_write, hf, vhf); |
230 | 276 | ||
231 | if (div == 1) { | 277 | if (div == 1) { |
232 | reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2); | 278 | reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2); |
@@ -264,6 +310,9 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, | |||
264 | /* Read */ | 310 | /* Read */ |
265 | t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh)); | 311 | t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh)); |
266 | t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach)); | 312 | t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach)); |
313 | /* Force at least 1 clk between AVD High to OE Low */ | ||
314 | if (t.oe_on <= t.adv_rd_off) | ||
315 | t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(1); | ||
267 | t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div); | 316 | t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div); |
268 | t.oe_off = t.access + gpmc_round_ns_to_ticks(1); | 317 | t.oe_off = t.access + gpmc_round_ns_to_ticks(1); |
269 | t.cs_rd_off = t.oe_off; | 318 | t.cs_rd_off = t.oe_off; |
@@ -317,18 +366,20 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, | |||
317 | if (err) | 366 | if (err) |
318 | return err; | 367 | return err; |
319 | 368 | ||
320 | set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf); | 369 | set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf, vhf); |
370 | |||
371 | *freq_ptr = freq; | ||
321 | 372 | ||
322 | return 0; | 373 | return 0; |
323 | } | 374 | } |
324 | 375 | ||
325 | static int gpmc_onenand_setup(void __iomem *onenand_base, int freq) | 376 | static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr) |
326 | { | 377 | { |
327 | struct device *dev = &gpmc_onenand_device.dev; | 378 | struct device *dev = &gpmc_onenand_device.dev; |
328 | 379 | ||
329 | /* Set sync timings in GPMC */ | 380 | /* Set sync timings in GPMC */ |
330 | if (omap2_onenand_set_sync_mode(gpmc_onenand_data, onenand_base, | 381 | if (omap2_onenand_set_sync_mode(gpmc_onenand_data, onenand_base, |
331 | freq) < 0) { | 382 | freq_ptr) < 0) { |
332 | dev_err(dev, "Unable to set synchronous mode\n"); | 383 | dev_err(dev, "Unable to set synchronous mode\n"); |
333 | return -EINVAL; | 384 | return -EINVAL; |
334 | } | 385 | } |