diff options
author | Robert ABEL <rabel@cit-ec.uni-bielefeld.de> | 2015-02-27 10:56:54 -0500 |
---|---|---|
committer | Roger Quadros <rogerq@ti.com> | 2015-03-06 05:46:06 -0500 |
commit | 7f2e8c58ae9e35240e5924c63163c07a506d0d12 (patch) | |
tree | 27c51437df1950442adeec4d0c47a153d04b58f1 | |
parent | 2e67690137f3a7bac660edd548f8846709c55381 (diff) |
ARM OMAP2+ GPMC: fix WAITMONITORINGTIME divider bug
The WAITMONITORINGTIME is expressed as a number of GPMC_CLK clock cycles,
even though the access is defined as asynchronous, and no GPMC_CLK clock
is provided to the external device. Still, GPMCFCLKDIVIDER is used as a divider
for the GPMC clock, so it must be programmed to define the
correct WAITMONITORINGTIME delay.
This patch correctly computes WAITMONITORINGTIME in GPMC_CLK cycles instead of GPMC_FCLK cycles,
both during programming (gpmc_cs_set_timings) and during retrieval (gpmc_cs_show_timings).
Signed-off-by: Robert ABEL <rabel@cit-ec.uni-bielefeld.de>
Acked-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Roger Quadros <rogerq@ti.com>
-rw-r--r-- | drivers/memory/omap-gpmc.c | 130 |
1 files changed, 103 insertions, 27 deletions
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 768bab233196..6c076b7c9b6f 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c | |||
@@ -179,6 +179,11 @@ | |||
179 | */ | 179 | */ |
180 | #define GPMC_NR_IRQ 2 | 180 | #define GPMC_NR_IRQ 2 |
181 | 181 | ||
182 | enum gpmc_clk_domain { | ||
183 | GPMC_CD_FCLK, | ||
184 | GPMC_CD_CLK | ||
185 | }; | ||
186 | |||
182 | struct gpmc_cs_data { | 187 | struct gpmc_cs_data { |
183 | const char *name; | 188 | const char *name; |
184 | 189 | ||
@@ -277,16 +282,55 @@ static unsigned long gpmc_get_fclk_period(void) | |||
277 | return rate; | 282 | return rate; |
278 | } | 283 | } |
279 | 284 | ||
280 | static unsigned int gpmc_ns_to_ticks(unsigned int time_ns) | 285 | /** |
286 | * gpmc_get_clk_period - get period of selected clock domain in ps | ||
287 | * @cs Chip Select Region. | ||
288 | * @cd Clock Domain. | ||
289 | * | ||
290 | * GPMC_CS_CONFIG1 GPMCFCLKDIVIDER for cs has to be setup | ||
291 | * prior to calling this function with GPMC_CD_CLK. | ||
292 | */ | ||
293 | static unsigned long gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd) | ||
294 | { | ||
295 | |||
296 | unsigned long tick_ps = gpmc_get_fclk_period(); | ||
297 | u32 l; | ||
298 | int div; | ||
299 | |||
300 | switch (cd) { | ||
301 | case GPMC_CD_CLK: | ||
302 | /* get current clk divider */ | ||
303 | l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); | ||
304 | div = (l & 0x03) + 1; | ||
305 | /* get GPMC_CLK period */ | ||
306 | tick_ps *= div; | ||
307 | break; | ||
308 | case GPMC_CD_FCLK: | ||
309 | /* FALL-THROUGH */ | ||
310 | default: | ||
311 | break; | ||
312 | } | ||
313 | |||
314 | return tick_ps; | ||
315 | |||
316 | } | ||
317 | |||
318 | static unsigned int gpmc_ns_to_clk_ticks(unsigned int time_ns, int cs, | ||
319 | enum gpmc_clk_domain cd) | ||
281 | { | 320 | { |
282 | unsigned long tick_ps; | 321 | unsigned long tick_ps; |
283 | 322 | ||
284 | /* Calculate in picosecs to yield more exact results */ | 323 | /* Calculate in picosecs to yield more exact results */ |
285 | tick_ps = gpmc_get_fclk_period(); | 324 | tick_ps = gpmc_get_clk_period(cs, cd); |
286 | 325 | ||
287 | return (time_ns * 1000 + tick_ps - 1) / tick_ps; | 326 | return (time_ns * 1000 + tick_ps - 1) / tick_ps; |
288 | } | 327 | } |
289 | 328 | ||
329 | static unsigned int gpmc_ns_to_ticks(unsigned int time_ns) | ||
330 | { | ||
331 | return gpmc_ns_to_clk_ticks(time_ns, /* any CS */ 0, GPMC_CD_FCLK); | ||
332 | } | ||
333 | |||
290 | static unsigned int gpmc_ps_to_ticks(unsigned int time_ps) | 334 | static unsigned int gpmc_ps_to_ticks(unsigned int time_ps) |
291 | { | 335 | { |
292 | unsigned long tick_ps; | 336 | unsigned long tick_ps; |
@@ -297,9 +341,15 @@ static unsigned int gpmc_ps_to_ticks(unsigned int time_ps) | |||
297 | return (time_ps + tick_ps - 1) / tick_ps; | 341 | return (time_ps + tick_ps - 1) / tick_ps; |
298 | } | 342 | } |
299 | 343 | ||
344 | unsigned int gpmc_clk_ticks_to_ns(unsigned ticks, int cs, | ||
345 | enum gpmc_clk_domain cd) | ||
346 | { | ||
347 | return ticks * gpmc_get_clk_period(cs, cd) / 1000; | ||
348 | } | ||
349 | |||
300 | unsigned int gpmc_ticks_to_ns(unsigned int ticks) | 350 | unsigned int gpmc_ticks_to_ns(unsigned int ticks) |
301 | { | 351 | { |
302 | return ticks * gpmc_get_fclk_period() / 1000; | 352 | return gpmc_clk_ticks_to_ns(ticks, /* any CS */ 0, GPMC_CD_FCLK); |
303 | } | 353 | } |
304 | 354 | ||
305 | static unsigned int gpmc_ticks_to_ps(unsigned int ticks) | 355 | static unsigned int gpmc_ticks_to_ps(unsigned int ticks) |
@@ -355,18 +405,24 @@ static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p) | |||
355 | * @st_bit: Start Bit | 405 | * @st_bit: Start Bit |
356 | * @end_bit: End Bit. Must be >= @st_bit. | 406 | * @end_bit: End Bit. Must be >= @st_bit. |
357 | * @name: DTS node name, w/o "gpmc," | 407 | * @name: DTS node name, w/o "gpmc," |
408 | * @cd: Clock Domain of timing parameter. | ||
409 | * @shift: Parameter value left shifts @shift, which is then printed instead of value. | ||
358 | * @raw: Raw Format Option. | 410 | * @raw: Raw Format Option. |
359 | * raw format: gpmc,name = <value> | 411 | * raw format: gpmc,name = <value> |
360 | * tick format: gpmc,name = <value> /‍* x ns -- y ns; x ticks *‍/ | 412 | * tick format: gpmc,name = <value> /‍* x ns -- y ns; x ticks *‍/ |
361 | * Where x ns -- y ns result in the same tick value. | 413 | * Where x ns -- y ns result in the same tick value. |
362 | * @noval: Parameter values equal to 0 are not printed. | 414 | * @noval: Parameter values equal to 0 are not printed. |
363 | * @shift: Parameter value left shifts @shift, which is then printed instead of value. | ||
364 | * @return: Specified timing parameter (after optional @shift). | 415 | * @return: Specified timing parameter (after optional @shift). |
365 | * | 416 | * |
366 | */ | 417 | */ |
367 | static int get_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, | 418 | static int get_gpmc_timing_reg( |
368 | bool raw, bool noval, int shift, | 419 | /* timing specifiers */ |
369 | const char *name) | 420 | int cs, int reg, int st_bit, int end_bit, |
421 | const char *name, const enum gpmc_clk_domain cd, | ||
422 | /* value transform */ | ||
423 | int shift, | ||
424 | /* format specifiers */ | ||
425 | bool raw, bool noval) | ||
370 | { | 426 | { |
371 | u32 l; | 427 | u32 l; |
372 | int nr_bits; | 428 | int nr_bits; |
@@ -386,8 +442,8 @@ static int get_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, | |||
386 | unsigned int time_ns_min = 0; | 442 | unsigned int time_ns_min = 0; |
387 | 443 | ||
388 | if (l) | 444 | if (l) |
389 | time_ns_min = gpmc_ticks_to_ns(l - 1) + 1; | 445 | time_ns_min = gpmc_clk_ticks_to_ns(l - 1, cs, cd) + 1; |
390 | time_ns = gpmc_ticks_to_ns(l); | 446 | time_ns = gpmc_clk_ticks_to_ns(l, cs, cd); |
391 | pr_info("gpmc,%s = <%u> /* %u ns - %u ns; %i ticks */\n", | 447 | pr_info("gpmc,%s = <%u> /* %u ns - %u ns; %i ticks */\n", |
392 | name, time_ns, time_ns_min, time_ns, l); | 448 | name, time_ns, time_ns_min, time_ns, l); |
393 | } else { | 449 | } else { |
@@ -402,13 +458,15 @@ static int get_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, | |||
402 | pr_info("cs%i %s: 0x%08x\n", cs, #config, \ | 458 | pr_info("cs%i %s: 0x%08x\n", cs, #config, \ |
403 | gpmc_cs_read_reg(cs, config)) | 459 | gpmc_cs_read_reg(cs, config)) |
404 | #define GPMC_GET_RAW(reg, st, end, field) \ | 460 | #define GPMC_GET_RAW(reg, st, end, field) \ |
405 | get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 0, 0, field) | 461 | get_gpmc_timing_reg(cs, (reg), (st), (end), field, GPMC_CD_FCLK, 0, 1, 0) |
406 | #define GPMC_GET_RAW_BOOL(reg, st, end, field) \ | 462 | #define GPMC_GET_RAW_BOOL(reg, st, end, field) \ |
407 | get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 1, 0, field) | 463 | get_gpmc_timing_reg(cs, (reg), (st), (end), field, GPMC_CD_FCLK, 0, 1, 1) |
408 | #define GPMC_GET_RAW_SHIFT(reg, st, end, shift, field) \ | 464 | #define GPMC_GET_RAW_SHIFT(reg, st, end, shift, field) \ |
409 | get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 1, (shift), field) | 465 | get_gpmc_timing_reg(cs, (reg), (st), (end), field, GPMC_CD_FCLK, (shift), 1, 1) |
410 | #define GPMC_GET_TICKS(reg, st, end, field) \ | 466 | #define GPMC_GET_TICKS(reg, st, end, field) \ |
411 | get_gpmc_timing_reg(cs, (reg), (st), (end), 0, 0, 0, field) | 467 | get_gpmc_timing_reg(cs, (reg), (st), (end), field, GPMC_CD_FCLK, 0, 0, 0) |
468 | #define GPMC_GET_TICKS_CD(reg, st, end, field, cd) \ | ||
469 | get_gpmc_timing_reg(cs, (reg), (st), (end), field, (cd), 0, 0, 0) | ||
412 | 470 | ||
413 | static void gpmc_show_regs(int cs, const char *desc) | 471 | static void gpmc_show_regs(int cs, const char *desc) |
414 | { | 472 | { |
@@ -476,7 +534,7 @@ static void gpmc_cs_show_timings(int cs, const char *desc) | |||
476 | GPMC_GET_TICKS(GPMC_CS_CONFIG6, 0, 3, "bus-turnaround-ns"); | 534 | GPMC_GET_TICKS(GPMC_CS_CONFIG6, 0, 3, "bus-turnaround-ns"); |
477 | GPMC_GET_TICKS(GPMC_CS_CONFIG6, 8, 11, "cycle2cycle-delay-ns"); | 535 | GPMC_GET_TICKS(GPMC_CS_CONFIG6, 8, 11, "cycle2cycle-delay-ns"); |
478 | 536 | ||
479 | GPMC_GET_TICKS(GPMC_CS_CONFIG1, 18, 19, "wait-monitoring-ns"); | 537 | GPMC_GET_TICKS_CD(GPMC_CS_CONFIG1, 18, 19, "wait-monitoring-ns", GPMC_CD_CLK); |
480 | GPMC_GET_TICKS(GPMC_CS_CONFIG1, 25, 26, "clk-activation-ns"); | 538 | GPMC_GET_TICKS(GPMC_CS_CONFIG1, 25, 26, "clk-activation-ns"); |
481 | 539 | ||
482 | GPMC_GET_TICKS(GPMC_CS_CONFIG6, 16, 19, "wr-data-mux-bus-ns"); | 540 | GPMC_GET_TICKS(GPMC_CS_CONFIG6, 16, 19, "wr-data-mux-bus-ns"); |
@@ -488,8 +546,22 @@ static inline void gpmc_cs_show_timings(int cs, const char *desc) | |||
488 | } | 546 | } |
489 | #endif | 547 | #endif |
490 | 548 | ||
549 | /** | ||
550 | * set_gpmc_timing_reg - set a single timing parameter for Chip Select Region. | ||
551 | * Caller is expected to have initialized CONFIG1 GPMCFCLKDIVIDER | ||
552 | * prior to calling this function with @cd equal to GPMC_CD_CLK. | ||
553 | * | ||
554 | * @cs: Chip Select Region. | ||
555 | * @reg: GPMC_CS_CONFIGn register offset. | ||
556 | * @st_bit: Start Bit | ||
557 | * @end_bit: End Bit. Must be >= @st_bit. | ||
558 | * @time: Timing parameter in ns. | ||
559 | * @cd: Timing parameter clock domain. | ||
560 | * @name: Timing parameter name. | ||
561 | * @return: 0 on success, -1 on error. | ||
562 | */ | ||
491 | static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, | 563 | static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, |
492 | int time, const char *name) | 564 | int time, enum gpmc_clk_domain cd, const char *name) |
493 | { | 565 | { |
494 | u32 l; | 566 | u32 l; |
495 | int ticks, mask, nr_bits; | 567 | int ticks, mask, nr_bits; |
@@ -497,12 +569,12 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, | |||
497 | if (time == 0) | 569 | if (time == 0) |
498 | ticks = 0; | 570 | ticks = 0; |
499 | else | 571 | else |
500 | ticks = gpmc_ns_to_ticks(time); | 572 | ticks = gpmc_ns_to_clk_ticks(time, cs, cd); |
501 | nr_bits = end_bit - st_bit + 1; | 573 | nr_bits = end_bit - st_bit + 1; |
502 | mask = (1 << nr_bits) - 1; | 574 | mask = (1 << nr_bits) - 1; |
503 | 575 | ||
504 | if (ticks > mask) { | 576 | if (ticks > mask) { |
505 | pr_err("%s: GPMC error! CS%d: %s: %d ns, %d ticks > %d\n", | 577 | pr_err("%s: GPMC CS%d: %s %d ns, %d ticks > %d ticks\n", |
506 | __func__, cs, name, time, ticks, mask); | 578 | __func__, cs, name, time, ticks, mask); |
507 | 579 | ||
508 | return -1; | 580 | return -1; |
@@ -512,7 +584,7 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, | |||
512 | #ifdef DEBUG | 584 | #ifdef DEBUG |
513 | pr_info( | 585 | pr_info( |
514 | "GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n", | 586 | "GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n", |
515 | cs, name, ticks, gpmc_get_fclk_period() * ticks / 1000, | 587 | cs, name, ticks, gpmc_get_clk_period(cs, cd) * ticks / 1000, |
516 | (l >> st_bit) & mask, time); | 588 | (l >> st_bit) & mask, time); |
517 | #endif | 589 | #endif |
518 | l &= ~(mask << st_bit); | 590 | l &= ~(mask << st_bit); |
@@ -522,11 +594,14 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, | |||
522 | return 0; | 594 | return 0; |
523 | } | 595 | } |
524 | 596 | ||
525 | #define GPMC_SET_ONE(reg, st, end, field) \ | 597 | #define GPMC_SET_ONE_CD(reg, st, end, field, cd) \ |
526 | if (set_gpmc_timing_reg(cs, (reg), (st), (end), \ | 598 | if (set_gpmc_timing_reg(cs, (reg), (st), (end), \ |
527 | t->field, #field) < 0) \ | 599 | t->field, (cd), #field) < 0) \ |
528 | return -1 | 600 | return -1 |
529 | 601 | ||
602 | #define GPMC_SET_ONE(reg, st, end, field) \ | ||
603 | GPMC_SET_ONE_CD(reg, st, end, field, GPMC_CD_FCLK) | ||
604 | |||
530 | /** | 605 | /** |
531 | * gpmc_calc_waitmonitoring_divider - calculate proper GPMCFCLKDIVIDER based on WAITMONITORINGTIME | 606 | * gpmc_calc_waitmonitoring_divider - calculate proper GPMCFCLKDIVIDER based on WAITMONITORINGTIME |
532 | * WAITMONITORINGTIME will be _at least_ as long as desired, i.e. | 607 | * WAITMONITORINGTIME will be _at least_ as long as desired, i.e. |
@@ -644,22 +719,23 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t, | |||
644 | GPMC_SET_ONE(GPMC_CS_CONFIG6, 0, 3, bus_turnaround); | 719 | GPMC_SET_ONE(GPMC_CS_CONFIG6, 0, 3, bus_turnaround); |
645 | GPMC_SET_ONE(GPMC_CS_CONFIG6, 8, 11, cycle2cycle_delay); | 720 | GPMC_SET_ONE(GPMC_CS_CONFIG6, 8, 11, cycle2cycle_delay); |
646 | 721 | ||
647 | GPMC_SET_ONE(GPMC_CS_CONFIG1, 18, 19, wait_monitoring); | ||
648 | GPMC_SET_ONE(GPMC_CS_CONFIG1, 25, 26, clk_activation); | ||
649 | |||
650 | if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) | 722 | if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) |
651 | GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus); | 723 | GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus); |
652 | if (gpmc_capability & GPMC_HAS_WR_ACCESS) | 724 | if (gpmc_capability & GPMC_HAS_WR_ACCESS) |
653 | GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access); | 725 | GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access); |
654 | 726 | ||
655 | l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); | 727 | l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); |
728 | l &= ~0x03; | ||
729 | l |= (div - 1); | ||
730 | gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l); | ||
731 | |||
732 | GPMC_SET_ONE_CD(GPMC_CS_CONFIG1, 18, 19, wait_monitoring, GPMC_CD_CLK); | ||
733 | GPMC_SET_ONE(GPMC_CS_CONFIG1, 25, 26, clk_activation); | ||
734 | |||
656 | #ifdef DEBUG | 735 | #ifdef DEBUG |
657 | pr_info("GPMC CS%d CLK period is %lu ns (div %d)\n", | 736 | pr_info("GPMC CS%d CLK period is %lu ns (div %d)\n", |
658 | cs, (div * gpmc_get_fclk_period()) / 1000, div); | 737 | cs, (div * gpmc_get_fclk_period()) / 1000, div); |
659 | #endif | 738 | #endif |
660 | l &= ~0x03; | ||
661 | l |= (div - 1); | ||
662 | gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l); | ||
663 | 739 | ||
664 | gpmc_cs_bool_timings(cs, &t->bool_timings); | 740 | gpmc_cs_bool_timings(cs, &t->bool_timings); |
665 | gpmc_cs_show_timings(cs, "after gpmc_cs_set_timings"); | 741 | gpmc_cs_show_timings(cs, "after gpmc_cs_set_timings"); |