diff options
Diffstat (limited to 'arch/arm/plat-omap/dmtimer.c')
-rw-r--r-- | arch/arm/plat-omap/dmtimer.c | 212 |
1 files changed, 169 insertions, 43 deletions
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 302ad8dff2cb..f22506af0e67 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c | |||
@@ -38,34 +38,113 @@ | |||
38 | #include <asm/arch/irqs.h> | 38 | #include <asm/arch/irqs.h> |
39 | 39 | ||
40 | /* register offsets */ | 40 | /* register offsets */ |
41 | #define OMAP_TIMER_ID_REG 0x00 | 41 | #define _OMAP_TIMER_ID_OFFSET 0x00 |
42 | #define OMAP_TIMER_OCP_CFG_REG 0x10 | 42 | #define _OMAP_TIMER_OCP_CFG_OFFSET 0x10 |
43 | #define OMAP_TIMER_SYS_STAT_REG 0x14 | 43 | #define _OMAP_TIMER_SYS_STAT_OFFSET 0x14 |
44 | #define OMAP_TIMER_STAT_REG 0x18 | 44 | #define _OMAP_TIMER_STAT_OFFSET 0x18 |
45 | #define OMAP_TIMER_INT_EN_REG 0x1c | 45 | #define _OMAP_TIMER_INT_EN_OFFSET 0x1c |
46 | #define OMAP_TIMER_WAKEUP_EN_REG 0x20 | 46 | #define _OMAP_TIMER_WAKEUP_EN_OFFSET 0x20 |
47 | #define OMAP_TIMER_CTRL_REG 0x24 | 47 | #define _OMAP_TIMER_CTRL_OFFSET 0x24 |
48 | #define OMAP_TIMER_COUNTER_REG 0x28 | 48 | #define OMAP_TIMER_CTRL_GPOCFG (1 << 14) |
49 | #define OMAP_TIMER_LOAD_REG 0x2c | 49 | #define OMAP_TIMER_CTRL_CAPTMODE (1 << 13) |
50 | #define OMAP_TIMER_TRIGGER_REG 0x30 | 50 | #define OMAP_TIMER_CTRL_PT (1 << 12) |
51 | #define OMAP_TIMER_WRITE_PEND_REG 0x34 | 51 | #define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8) |
52 | #define OMAP_TIMER_MATCH_REG 0x38 | 52 | #define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8) |
53 | #define OMAP_TIMER_CAPTURE_REG 0x3c | 53 | #define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8) |
54 | #define OMAP_TIMER_IF_CTRL_REG 0x40 | 54 | #define OMAP_TIMER_CTRL_SCPWM (1 << 7) |
55 | 55 | #define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */ | |
56 | /* timer control reg bits */ | 56 | #define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */ |
57 | #define OMAP_TIMER_CTRL_GPOCFG (1 << 14) | 57 | #define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* prescaler value shift */ |
58 | #define OMAP_TIMER_CTRL_CAPTMODE (1 << 13) | 58 | #define OMAP_TIMER_CTRL_POSTED (1 << 2) |
59 | #define OMAP_TIMER_CTRL_PT (1 << 12) | 59 | #define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */ |
60 | #define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8) | 60 | #define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */ |
61 | #define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8) | 61 | #define _OMAP_TIMER_COUNTER_OFFSET 0x28 |
62 | #define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8) | 62 | #define _OMAP_TIMER_LOAD_OFFSET 0x2c |
63 | #define OMAP_TIMER_CTRL_SCPWM (1 << 7) | 63 | #define _OMAP_TIMER_TRIGGER_OFFSET 0x30 |
64 | #define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */ | 64 | #define _OMAP_TIMER_WRITE_PEND_OFFSET 0x34 |
65 | #define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */ | 65 | #define WP_NONE 0 /* no write pending bit */ |
66 | #define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* how much to shift the prescaler value */ | 66 | #define WP_TCLR (1 << 0) |
67 | #define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */ | 67 | #define WP_TCRR (1 << 1) |
68 | #define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */ | 68 | #define WP_TLDR (1 << 2) |
69 | #define WP_TTGR (1 << 3) | ||
70 | #define WP_TMAR (1 << 4) | ||
71 | #define WP_TPIR (1 << 5) | ||
72 | #define WP_TNIR (1 << 6) | ||
73 | #define WP_TCVR (1 << 7) | ||
74 | #define WP_TOCR (1 << 8) | ||
75 | #define WP_TOWR (1 << 9) | ||
76 | #define _OMAP_TIMER_MATCH_OFFSET 0x38 | ||
77 | #define _OMAP_TIMER_CAPTURE_OFFSET 0x3c | ||
78 | #define _OMAP_TIMER_IF_CTRL_OFFSET 0x40 | ||
79 | #define _OMAP_TIMER_CAPTURE2_OFFSET 0x44 /* TCAR2, 34xx only */ | ||
80 | #define _OMAP_TIMER_TICK_POS_OFFSET 0x48 /* TPIR, 34xx only */ | ||
81 | #define _OMAP_TIMER_TICK_NEG_OFFSET 0x4c /* TNIR, 34xx only */ | ||
82 | #define _OMAP_TIMER_TICK_COUNT_OFFSET 0x50 /* TCVR, 34xx only */ | ||
83 | #define _OMAP_TIMER_TICK_INT_MASK_SET_OFFSET 0x54 /* TOCR, 34xx only */ | ||
84 | #define _OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET 0x58 /* TOWR, 34xx only */ | ||
85 | |||
86 | /* register offsets with the write pending bit encoded */ | ||
87 | #define WPSHIFT 16 | ||
88 | |||
89 | #define OMAP_TIMER_ID_REG (_OMAP_TIMER_ID_OFFSET \ | ||
90 | | (WP_NONE << WPSHIFT)) | ||
91 | |||
92 | #define OMAP_TIMER_OCP_CFG_REG (_OMAP_TIMER_OCP_CFG_OFFSET \ | ||
93 | | (WP_NONE << WPSHIFT)) | ||
94 | |||
95 | #define OMAP_TIMER_SYS_STAT_REG (_OMAP_TIMER_SYS_STAT_OFFSET \ | ||
96 | | (WP_NONE << WPSHIFT)) | ||
97 | |||
98 | #define OMAP_TIMER_STAT_REG (_OMAP_TIMER_STAT_OFFSET \ | ||
99 | | (WP_NONE << WPSHIFT)) | ||
100 | |||
101 | #define OMAP_TIMER_INT_EN_REG (_OMAP_TIMER_INT_EN_OFFSET \ | ||
102 | | (WP_NONE << WPSHIFT)) | ||
103 | |||
104 | #define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \ | ||
105 | | (WP_NONE << WPSHIFT)) | ||
106 | |||
107 | #define OMAP_TIMER_CTRL_REG (_OMAP_TIMER_CTRL_OFFSET \ | ||
108 | | (WP_TCLR << WPSHIFT)) | ||
109 | |||
110 | #define OMAP_TIMER_COUNTER_REG (_OMAP_TIMER_COUNTER_OFFSET \ | ||
111 | | (WP_TCRR << WPSHIFT)) | ||
112 | |||
113 | #define OMAP_TIMER_LOAD_REG (_OMAP_TIMER_LOAD_OFFSET \ | ||
114 | | (WP_TLDR << WPSHIFT)) | ||
115 | |||
116 | #define OMAP_TIMER_TRIGGER_REG (_OMAP_TIMER_TRIGGER_OFFSET \ | ||
117 | | (WP_TTGR << WPSHIFT)) | ||
118 | |||
119 | #define OMAP_TIMER_WRITE_PEND_REG (_OMAP_TIMER_WRITE_PEND_OFFSET \ | ||
120 | | (WP_NONE << WPSHIFT)) | ||
121 | |||
122 | #define OMAP_TIMER_MATCH_REG (_OMAP_TIMER_MATCH_OFFSET \ | ||
123 | | (WP_TMAR << WPSHIFT)) | ||
124 | |||
125 | #define OMAP_TIMER_CAPTURE_REG (_OMAP_TIMER_CAPTURE_OFFSET \ | ||
126 | | (WP_NONE << WPSHIFT)) | ||
127 | |||
128 | #define OMAP_TIMER_IF_CTRL_REG (_OMAP_TIMER_IF_CTRL_OFFSET \ | ||
129 | | (WP_NONE << WPSHIFT)) | ||
130 | |||
131 | #define OMAP_TIMER_CAPTURE2_REG (_OMAP_TIMER_CAPTURE2_OFFSET \ | ||
132 | | (WP_NONE << WPSHIFT)) | ||
133 | |||
134 | #define OMAP_TIMER_TICK_POS_REG (_OMAP_TIMER_TICK_POS_OFFSET \ | ||
135 | | (WP_TPIR << WPSHIFT)) | ||
136 | |||
137 | #define OMAP_TIMER_TICK_NEG_REG (_OMAP_TIMER_TICK_NEG_OFFSET \ | ||
138 | | (WP_TNIR << WPSHIFT)) | ||
139 | |||
140 | #define OMAP_TIMER_TICK_COUNT_REG (_OMAP_TIMER_TICK_COUNT_OFFSET \ | ||
141 | | (WP_TCVR << WPSHIFT)) | ||
142 | |||
143 | #define OMAP_TIMER_TICK_INT_MASK_SET_REG \ | ||
144 | (_OMAP_TIMER_TICK_INT_MASK_SET_OFFSET | (WP_TOCR << WPSHIFT)) | ||
145 | |||
146 | #define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \ | ||
147 | (_OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | (WP_TOWR << WPSHIFT)) | ||
69 | 148 | ||
70 | struct omap_dm_timer { | 149 | struct omap_dm_timer { |
71 | unsigned long phys_base; | 150 | unsigned long phys_base; |
@@ -76,6 +155,7 @@ struct omap_dm_timer { | |||
76 | void __iomem *io_base; | 155 | void __iomem *io_base; |
77 | unsigned reserved:1; | 156 | unsigned reserved:1; |
78 | unsigned enabled:1; | 157 | unsigned enabled:1; |
158 | unsigned posted:1; | ||
79 | }; | 159 | }; |
80 | 160 | ||
81 | #ifdef CONFIG_ARCH_OMAP1 | 161 | #ifdef CONFIG_ARCH_OMAP1 |
@@ -181,16 +261,34 @@ static struct clk **dm_source_clocks; | |||
181 | 261 | ||
182 | static spinlock_t dm_timer_lock; | 262 | static spinlock_t dm_timer_lock; |
183 | 263 | ||
184 | static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg) | 264 | /* |
265 | * Reads timer registers in posted and non-posted mode. The posted mode bit | ||
266 | * is encoded in reg. Note that in posted mode write pending bit must be | ||
267 | * checked. Otherwise a read of a non completed write will produce an error. | ||
268 | */ | ||
269 | static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg) | ||
185 | { | 270 | { |
186 | return readl(timer->io_base + reg); | 271 | if (timer->posted) |
272 | while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)) | ||
273 | & (reg >> WPSHIFT)) | ||
274 | cpu_relax(); | ||
275 | return readl(timer->io_base + (reg & 0xff)); | ||
187 | } | 276 | } |
188 | 277 | ||
189 | static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value) | 278 | /* |
279 | * Writes timer registers in posted and non-posted mode. The posted mode bit | ||
280 | * is encoded in reg. Note that in posted mode the write pending bit must be | ||
281 | * checked. Otherwise a write on a register which has a pending write will be | ||
282 | * lost. | ||
283 | */ | ||
284 | static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, | ||
285 | u32 value) | ||
190 | { | 286 | { |
191 | writel(value, timer->io_base + reg); | 287 | if (timer->posted) |
192 | while (omap_dm_timer_read_reg(timer, OMAP_TIMER_WRITE_PEND_REG)) | 288 | while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)) |
193 | ; | 289 | & (reg >> WPSHIFT)) |
290 | cpu_relax(); | ||
291 | writel(value, timer->io_base + (reg & 0xff)); | ||
194 | } | 292 | } |
195 | 293 | ||
196 | static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer) | 294 | static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer) |
@@ -217,17 +315,23 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer) | |||
217 | } | 315 | } |
218 | omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); | 316 | omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); |
219 | 317 | ||
220 | /* Set to smart-idle mode */ | ||
221 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_OCP_CFG_REG); | 318 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_OCP_CFG_REG); |
222 | l |= 0x02 << 3; | 319 | l |= 0x02 << 3; /* Set to smart-idle mode */ |
223 | 320 | l |= 0x2 << 8; /* Set clock activity to perserve f-clock on idle */ | |
224 | if (cpu_class_is_omap2() && timer == &dm_timers[0]) { | 321 | |
225 | /* Enable wake-up only for GPT1 on OMAP2 CPUs*/ | 322 | /* |
323 | * Enable wake-up only for GPT1 on OMAP2 CPUs. | ||
324 | * FIXME: All timers should have wake-up enabled and clear | ||
325 | * PRCM status. | ||
326 | */ | ||
327 | if (cpu_class_is_omap2() && (timer == &dm_timers[0])) | ||
226 | l |= 1 << 2; | 328 | l |= 1 << 2; |
227 | /* Non-posted mode */ | ||
228 | omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0); | ||
229 | } | ||
230 | omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l); | 329 | omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l); |
330 | |||
331 | /* Match hardware reset default of posted mode */ | ||
332 | omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, | ||
333 | OMAP_TIMER_CTRL_POSTED); | ||
334 | timer->posted = 1; | ||
231 | } | 335 | } |
232 | 336 | ||
233 | static void omap_dm_timer_prepare(struct omap_dm_timer *timer) | 337 | static void omap_dm_timer_prepare(struct omap_dm_timer *timer) |
@@ -434,9 +538,32 @@ void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, | |||
434 | l &= ~OMAP_TIMER_CTRL_AR; | 538 | l &= ~OMAP_TIMER_CTRL_AR; |
435 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); | 539 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
436 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); | 540 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); |
541 | |||
542 | /* REVISIT: hw feature, ttgr overtaking tldr? */ | ||
543 | while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff))) | ||
544 | cpu_relax(); | ||
545 | |||
437 | omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); | 546 | omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); |
438 | } | 547 | } |
439 | 548 | ||
549 | /* Optimized set_load which removes costly spin wait in timer_start */ | ||
550 | void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, | ||
551 | unsigned int load) | ||
552 | { | ||
553 | u32 l; | ||
554 | |||
555 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | ||
556 | if (autoreload) | ||
557 | l |= OMAP_TIMER_CTRL_AR; | ||
558 | else | ||
559 | l &= ~OMAP_TIMER_CTRL_AR; | ||
560 | l |= OMAP_TIMER_CTRL_ST; | ||
561 | |||
562 | omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, load); | ||
563 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); | ||
564 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); | ||
565 | } | ||
566 | |||
440 | void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, | 567 | void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, |
441 | unsigned int match) | 568 | unsigned int match) |
442 | { | 569 | { |
@@ -451,7 +578,6 @@ void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, | |||
451 | omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); | 578 | omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); |
452 | } | 579 | } |
453 | 580 | ||
454 | |||
455 | void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, | 581 | void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, |
456 | int toggle, int trigger) | 582 | int toggle, int trigger) |
457 | { | 583 | { |