Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: OMAP: DMTimer: Use posted mode

This patch adds the use of write posting for the timer. Previously, every
write could lock the requestor for almost 3x32KHz cycles. This patch only
synchronizes before writes and reads instead of after them and it does
it on per register basis. Doing it this way there is some chance to hide
some of the sync latency. It also removes some needless reads when
non-posted mode is there. With out this fix the read/writes take almost
2% CPU load @500MHz just waiting on tick timer registers.

Also define new 34xx only registers.

Signed-off-by: Richard Woodruff <r-woodruff2@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>

authored by

Richard Woodruff and committed by
Tony Lindgren
0f0d0807 4621d588

+149 -40
+149 -40
arch/arm/plat-omap/dmtimer.c
··· 38 38 #include <asm/arch/irqs.h> 39 39 40 40 /* register offsets */ 41 - #define OMAP_TIMER_ID_REG 0x00 42 - #define OMAP_TIMER_OCP_CFG_REG 0x10 43 - #define OMAP_TIMER_SYS_STAT_REG 0x14 44 - #define OMAP_TIMER_STAT_REG 0x18 45 - #define OMAP_TIMER_INT_EN_REG 0x1c 46 - #define OMAP_TIMER_WAKEUP_EN_REG 0x20 47 - #define OMAP_TIMER_CTRL_REG 0x24 48 - #define OMAP_TIMER_COUNTER_REG 0x28 49 - #define OMAP_TIMER_LOAD_REG 0x2c 50 - #define OMAP_TIMER_TRIGGER_REG 0x30 51 - #define OMAP_TIMER_WRITE_PEND_REG 0x34 52 - #define OMAP_TIMER_MATCH_REG 0x38 53 - #define OMAP_TIMER_CAPTURE_REG 0x3c 54 - #define OMAP_TIMER_IF_CTRL_REG 0x40 41 + #define _OMAP_TIMER_ID_OFFSET 0x00 42 + #define _OMAP_TIMER_OCP_CFG_OFFSET 0x10 43 + #define _OMAP_TIMER_SYS_STAT_OFFSET 0x14 44 + #define _OMAP_TIMER_STAT_OFFSET 0x18 45 + #define _OMAP_TIMER_INT_EN_OFFSET 0x1c 46 + #define _OMAP_TIMER_WAKEUP_EN_OFFSET 0x20 47 + #define _OMAP_TIMER_CTRL_OFFSET 0x24 48 + #define OMAP_TIMER_CTRL_GPOCFG (1 << 14) 49 + #define OMAP_TIMER_CTRL_CAPTMODE (1 << 13) 50 + #define OMAP_TIMER_CTRL_PT (1 << 12) 51 + #define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8) 52 + #define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8) 53 + #define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8) 54 + #define OMAP_TIMER_CTRL_SCPWM (1 << 7) 55 + #define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */ 56 + #define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */ 57 + #define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* prescaler value shift */ 58 + #define OMAP_TIMER_CTRL_POSTED (1 << 2) 59 + #define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */ 60 + #define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */ 61 + #define _OMAP_TIMER_COUNTER_OFFSET 0x28 62 + #define _OMAP_TIMER_LOAD_OFFSET 0x2c 63 + #define _OMAP_TIMER_TRIGGER_OFFSET 0x30 64 + #define _OMAP_TIMER_WRITE_PEND_OFFSET 0x34 65 + #define WP_NONE 0 /* no write pending bit */ 66 + #define WP_TCLR (1 << 0) 67 + #define WP_TCRR (1 << 1) 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 */ 55 85 56 - /* timer control reg bits */ 57 - #define OMAP_TIMER_CTRL_GPOCFG (1 << 14) 58 - #define OMAP_TIMER_CTRL_CAPTMODE (1 << 13) 59 - #define OMAP_TIMER_CTRL_PT (1 << 12) 60 - #define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8) 61 - #define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8) 62 - #define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8) 63 - #define OMAP_TIMER_CTRL_SCPWM (1 << 7) 64 - #define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */ 65 - #define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */ 66 - #define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* how much to shift the prescaler value */ 67 - #define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */ 68 - #define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */ 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 149 struct omap_dm_timer { 71 150 unsigned long phys_base; ··· 155 76 void __iomem *io_base; 156 77 unsigned reserved:1; 157 78 unsigned enabled:1; 79 + unsigned posted:1; 158 80 }; 159 81 160 82 #ifdef CONFIG_ARCH_OMAP1 ··· 261 181 262 182 static spinlock_t dm_timer_lock; 263 183 264 - static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg) 184 + /* 185 + * Reads timer registers in posted and non-posted mode. The posted mode bit 186 + * is encoded in reg. Note that in posted mode write pending bit must be 187 + * checked. Otherwise a read of a non completed write will produce an error. 188 + */ 189 + static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg) 265 190 { 266 - return readl(timer->io_base + reg); 191 + if (timer->posted) 192 + while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)) 193 + & (reg >> WPSHIFT)) 194 + cpu_relax(); 195 + return readl(timer->io_base + (reg & 0xff)); 267 196 } 268 197 269 - static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value) 198 + /* 199 + * Writes timer registers in posted and non-posted mode. The posted mode bit 200 + * is encoded in reg. Note that in posted mode the write pending bit must be 201 + * checked. Otherwise a write on a register which has a pending write will be 202 + * lost. 203 + */ 204 + static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, 205 + u32 value) 270 206 { 271 - writel(value, timer->io_base + reg); 272 - while (omap_dm_timer_read_reg(timer, OMAP_TIMER_WRITE_PEND_REG)) 273 - ; 207 + if (timer->posted) 208 + while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)) 209 + & (reg >> WPSHIFT)) 210 + cpu_relax(); 211 + writel(value, timer->io_base + (reg & 0xff)); 274 212 } 275 213 276 214 static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer) ··· 315 217 } 316 218 omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); 317 219 318 - /* Set to smart-idle mode */ 319 220 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_OCP_CFG_REG); 320 - l |= 0x02 << 3; 221 + l |= 0x02 << 3; /* Set to smart-idle mode */ 222 + l |= 0x2 << 8; /* Set clock activity to perserve f-clock on idle */ 321 223 322 - if (cpu_class_is_omap2() && timer == &dm_timers[0]) { 323 - /* Enable wake-up only for GPT1 on OMAP2 CPUs*/ 224 + /* 225 + * Enable wake-up only for GPT1 on OMAP2 CPUs. 226 + * FIXME: All timers should have wake-up enabled and clear 227 + * PRCM status. 228 + */ 229 + if (cpu_class_is_omap2() && (timer == &dm_timers[0])) 324 230 l |= 1 << 2; 325 - /* Non-posted mode */ 326 - omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0); 327 - } 328 231 omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l); 232 + 233 + /* Match hardware reset default of posted mode */ 234 + omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 235 + OMAP_TIMER_CTRL_POSTED); 236 + timer->posted = 1; 329 237 } 330 238 331 239 static void omap_dm_timer_prepare(struct omap_dm_timer *timer) ··· 538 434 l &= ~OMAP_TIMER_CTRL_AR; 539 435 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); 540 436 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); 437 + 438 + /* REVISIT: hw feature, ttgr overtaking tldr? */ 439 + while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff))) 440 + cpu_relax(); 441 + 541 442 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); 542 443 } 543 444