diff options
author | Robert ABEL <rabel@cit-ec.uni-bielefeld.de> | 2015-02-27 10:56:53 -0500 |
---|---|---|
committer | Roger Quadros <rogerq@ti.com> | 2015-03-06 05:39:48 -0500 |
commit | 2e67690137f3a7bac660edd548f8846709c55381 (patch) | |
tree | 33262ac06753bd02df92a27fe520f24761dd1847 | |
parent | f585070b91950a0269eb20f497cceeee0aeefe0e (diff) |
ARM OMAP2+ GPMC: calculate GPMCFCLKDIVIDER based on WAITMONITORINGTIME
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.
Calculate GPMCFCLKDIVIDER independent of gpmc,sync-clk-ps in DT for
pure asynchronous accesses, i.e. both read and write asynchronous.
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-- | arch/arm/mach-omap2/gpmc-nand.c | 18 | ||||
-rw-r--r-- | arch/arm/mach-omap2/gpmc-onenand.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-omap2/usb-tusb6010.c | 4 | ||||
-rw-r--r-- | drivers/memory/omap-gpmc.c | 86 | ||||
-rw-r--r-- | include/linux/omap-gpmc.h | 3 |
5 files changed, 95 insertions, 20 deletions
diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index d5951b17b736..72918c4973ea 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c | |||
@@ -96,14 +96,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, | |||
96 | gpmc_nand_res[1].start = gpmc_get_client_irq(GPMC_IRQ_FIFOEVENTENABLE); | 96 | gpmc_nand_res[1].start = gpmc_get_client_irq(GPMC_IRQ_FIFOEVENTENABLE); |
97 | gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT); | 97 | gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT); |
98 | 98 | ||
99 | if (gpmc_t) { | ||
100 | err = gpmc_cs_set_timings(gpmc_nand_data->cs, gpmc_t); | ||
101 | if (err < 0) { | ||
102 | pr_err("omap2-gpmc: Unable to set gpmc timings: %d\n", err); | ||
103 | return err; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | memset(&s, 0, sizeof(struct gpmc_settings)); | 99 | memset(&s, 0, sizeof(struct gpmc_settings)); |
108 | if (gpmc_nand_data->of_node) | 100 | if (gpmc_nand_data->of_node) |
109 | gpmc_read_settings_dt(gpmc_nand_data->of_node, &s); | 101 | gpmc_read_settings_dt(gpmc_nand_data->of_node, &s); |
@@ -111,6 +103,16 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, | |||
111 | gpmc_set_legacy(gpmc_nand_data, &s); | 103 | gpmc_set_legacy(gpmc_nand_data, &s); |
112 | 104 | ||
113 | s.device_nand = true; | 105 | s.device_nand = true; |
106 | |||
107 | if (gpmc_t) { | ||
108 | err = gpmc_cs_set_timings(gpmc_nand_data->cs, gpmc_t, &s); | ||
109 | if (err < 0) { | ||
110 | pr_err("omap2-gpmc: Unable to set gpmc timings: %d\n", | ||
111 | err); | ||
112 | return err; | ||
113 | } | ||
114 | } | ||
115 | |||
114 | err = gpmc_cs_program_settings(gpmc_nand_data->cs, &s); | 116 | err = gpmc_cs_program_settings(gpmc_nand_data->cs, &s); |
115 | if (err < 0) | 117 | if (err < 0) |
116 | goto out_free_cs; | 118 | goto out_free_cs; |
diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c index 53d197e0c1f3..f899e77ff5e6 100644 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ b/arch/arm/mach-omap2/gpmc-onenand.c | |||
@@ -293,7 +293,7 @@ static int omap2_onenand_setup_async(void __iomem *onenand_base) | |||
293 | if (ret < 0) | 293 | if (ret < 0) |
294 | return ret; | 294 | return ret; |
295 | 295 | ||
296 | ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t); | 296 | ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t, &onenand_async); |
297 | if (ret < 0) | 297 | if (ret < 0) |
298 | return ret; | 298 | return ret; |
299 | 299 | ||
@@ -331,7 +331,7 @@ static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr) | |||
331 | if (ret < 0) | 331 | if (ret < 0) |
332 | return ret; | 332 | return ret; |
333 | 333 | ||
334 | ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t); | 334 | ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t, &onenand_sync); |
335 | if (ret < 0) | 335 | if (ret < 0) |
336 | return ret; | 336 | return ret; |
337 | 337 | ||
diff --git a/arch/arm/mach-omap2/usb-tusb6010.c b/arch/arm/mach-omap2/usb-tusb6010.c index 8333400898fb..e554d9e66a1c 100644 --- a/arch/arm/mach-omap2/usb-tusb6010.c +++ b/arch/arm/mach-omap2/usb-tusb6010.c | |||
@@ -71,7 +71,7 @@ static int tusb_set_async_mode(unsigned sysclk_ps) | |||
71 | 71 | ||
72 | gpmc_calc_timings(&t, &tusb_async, &dev_t); | 72 | gpmc_calc_timings(&t, &tusb_async, &dev_t); |
73 | 73 | ||
74 | return gpmc_cs_set_timings(async_cs, &t); | 74 | return gpmc_cs_set_timings(async_cs, &t, &tusb_async); |
75 | } | 75 | } |
76 | 76 | ||
77 | static int tusb_set_sync_mode(unsigned sysclk_ps) | 77 | static int tusb_set_sync_mode(unsigned sysclk_ps) |
@@ -98,7 +98,7 @@ static int tusb_set_sync_mode(unsigned sysclk_ps) | |||
98 | 98 | ||
99 | gpmc_calc_timings(&t, &tusb_sync, &dev_t); | 99 | gpmc_calc_timings(&t, &tusb_sync, &dev_t); |
100 | 100 | ||
101 | return gpmc_cs_set_timings(sync_cs, &t); | 101 | return gpmc_cs_set_timings(sync_cs, &t, &tusb_sync); |
102 | } | 102 | } |
103 | 103 | ||
104 | /* tusb driver calls this when it changes the chip's clocking */ | 104 | /* tusb driver calls this when it changes the chip's clocking */ |
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 5c36ff397b73..768bab233196 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c | |||
@@ -138,7 +138,9 @@ | |||
138 | #define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23) | 138 | #define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23) |
139 | #define GPMC_CONFIG1_WAIT_READ_MON (1 << 22) | 139 | #define GPMC_CONFIG1_WAIT_READ_MON (1 << 22) |
140 | #define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21) | 140 | #define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21) |
141 | #define GPMC_CONFIG1_WAIT_MON_IIME(val) ((val & 3) << 18) | 141 | #define GPMC_CONFIG1_WAIT_MON_TIME(val) ((val & 3) << 18) |
142 | /** WAITMONITORINGTIME Max Ticks */ | ||
143 | #define GPMC_CONFIG1_WAITMONITORINGTIME_MAX 2 | ||
142 | #define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16) | 144 | #define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16) |
143 | #define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12) | 145 | #define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12) |
144 | #define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1) | 146 | #define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1) |
@@ -525,13 +527,48 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, | |||
525 | t->field, #field) < 0) \ | 527 | t->field, #field) < 0) \ |
526 | return -1 | 528 | return -1 |
527 | 529 | ||
530 | /** | ||
531 | * gpmc_calc_waitmonitoring_divider - calculate proper GPMCFCLKDIVIDER based on WAITMONITORINGTIME | ||
532 | * WAITMONITORINGTIME will be _at least_ as long as desired, i.e. | ||
533 | * read --> don't sample bus too early | ||
534 | * write --> data is longer on bus | ||
535 | * | ||
536 | * Formula: | ||
537 | * gpmc_clk_div + 1 = ceil(ceil(waitmonitoringtime_ns / gpmc_fclk_ns) | ||
538 | * / waitmonitoring_ticks) | ||
539 | * WAITMONITORINGTIME resulting in 0 or 1 tick with div = 1 are caught by | ||
540 | * div <= 0 check. | ||
541 | * | ||
542 | * @wait_monitoring: WAITMONITORINGTIME in ns. | ||
543 | * @return: -1 on failure to scale, else proper divider > 0. | ||
544 | */ | ||
545 | static int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring) | ||
546 | { | ||
547 | |||
548 | int div = gpmc_ns_to_ticks(wait_monitoring); | ||
549 | |||
550 | div += GPMC_CONFIG1_WAITMONITORINGTIME_MAX - 1; | ||
551 | div /= GPMC_CONFIG1_WAITMONITORINGTIME_MAX; | ||
552 | |||
553 | if (div > 4) | ||
554 | return -1; | ||
555 | if (div <= 0) | ||
556 | div = 1; | ||
557 | |||
558 | return div; | ||
559 | |||
560 | } | ||
561 | |||
562 | /** | ||
563 | * gpmc_calc_divider - calculate GPMC_FCLK divider for sync_clk GPMC_CLK period. | ||
564 | * @sync_clk: GPMC_CLK period in ps. | ||
565 | * @return: Returns at least 1 if GPMC_FCLK can be divided to GPMC_CLK. | ||
566 | * Else, returns -1. | ||
567 | */ | ||
528 | int gpmc_calc_divider(unsigned int sync_clk) | 568 | int gpmc_calc_divider(unsigned int sync_clk) |
529 | { | 569 | { |
530 | int div; | 570 | int div = gpmc_ps_to_ticks(sync_clk); |
531 | u32 l; | ||
532 | 571 | ||
533 | l = sync_clk + (gpmc_get_fclk_period() - 1); | ||
534 | div = l / gpmc_get_fclk_period(); | ||
535 | if (div > 4) | 572 | if (div > 4) |
536 | return -1; | 573 | return -1; |
537 | if (div <= 0) | 574 | if (div <= 0) |
@@ -540,7 +577,15 @@ int gpmc_calc_divider(unsigned int sync_clk) | |||
540 | return div; | 577 | return div; |
541 | } | 578 | } |
542 | 579 | ||
543 | int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) | 580 | /** |
581 | * gpmc_cs_set_timings - program timing parameters for Chip Select Region. | ||
582 | * @cs: Chip Select Region. | ||
583 | * @t: GPMC timing parameters. | ||
584 | * @s: GPMC timing settings. | ||
585 | * @return: 0 on success, -1 on error. | ||
586 | */ | ||
587 | int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t, | ||
588 | const struct gpmc_settings *s) | ||
544 | { | 589 | { |
545 | int div; | 590 | int div; |
546 | u32 l; | 591 | u32 l; |
@@ -550,6 +595,33 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) | |||
550 | if (div < 0) | 595 | if (div < 0) |
551 | return div; | 596 | return div; |
552 | 597 | ||
598 | /* | ||
599 | * See if we need to change the divider for waitmonitoringtime. | ||
600 | * | ||
601 | * Calculate GPMCFCLKDIVIDER independent of gpmc,sync-clk-ps in DT for | ||
602 | * pure asynchronous accesses, i.e. both read and write asynchronous. | ||
603 | * However, only do so if WAITMONITORINGTIME is actually used, i.e. | ||
604 | * either WAITREADMONITORING or WAITWRITEMONITORING is set. | ||
605 | * | ||
606 | * This statement must not change div to scale async WAITMONITORINGTIME | ||
607 | * to protect mixed synchronous and asynchronous accesses. | ||
608 | * | ||
609 | * We raise an error later if WAITMONITORINGTIME does not fit. | ||
610 | */ | ||
611 | if (!s->sync_read && !s->sync_write && | ||
612 | (s->wait_on_read || s->wait_on_write) | ||
613 | ) { | ||
614 | |||
615 | div = gpmc_calc_waitmonitoring_divider(t->wait_monitoring); | ||
616 | if (div < 0) { | ||
617 | pr_err("%s: waitmonitoringtime %3d ns too large for greatest gpmcfclkdivider.\n", | ||
618 | __func__, | ||
619 | t->wait_monitoring | ||
620 | ); | ||
621 | return -1; | ||
622 | } | ||
623 | } | ||
624 | |||
553 | GPMC_SET_ONE(GPMC_CS_CONFIG2, 0, 3, cs_on); | 625 | GPMC_SET_ONE(GPMC_CS_CONFIG2, 0, 3, cs_on); |
554 | GPMC_SET_ONE(GPMC_CS_CONFIG2, 8, 12, cs_rd_off); | 626 | GPMC_SET_ONE(GPMC_CS_CONFIG2, 8, 12, cs_rd_off); |
555 | GPMC_SET_ONE(GPMC_CS_CONFIG2, 16, 20, cs_wr_off); | 627 | GPMC_SET_ONE(GPMC_CS_CONFIG2, 16, 20, cs_wr_off); |
@@ -1810,7 +1882,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, | |||
1810 | if (ret < 0) | 1882 | if (ret < 0) |
1811 | goto err; | 1883 | goto err; |
1812 | 1884 | ||
1813 | ret = gpmc_cs_set_timings(cs, &gpmc_t); | 1885 | ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s); |
1814 | if (ret) { | 1886 | if (ret) { |
1815 | dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n", | 1887 | dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n", |
1816 | child->name); | 1888 | child->name); |
diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index c2080eebbb47..7dee00143afd 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h | |||
@@ -163,7 +163,8 @@ extern unsigned int gpmc_ticks_to_ns(unsigned int ticks); | |||
163 | 163 | ||
164 | extern void gpmc_cs_write_reg(int cs, int idx, u32 val); | 164 | extern void gpmc_cs_write_reg(int cs, int idx, u32 val); |
165 | extern int gpmc_calc_divider(unsigned int sync_clk); | 165 | extern int gpmc_calc_divider(unsigned int sync_clk); |
166 | extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t); | 166 | extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t, |
167 | const struct gpmc_settings *s); | ||
167 | extern int gpmc_cs_program_settings(int cs, struct gpmc_settings *p); | 168 | extern int gpmc_cs_program_settings(int cs, struct gpmc_settings *p); |
168 | extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); | 169 | extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); |
169 | extern void gpmc_cs_free(int cs); | 170 | extern void gpmc_cs_free(int cs); |