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

Configure Feed

Select the types of activity you want to include in your feed.

clocksource/drivers/sh_cmt: Access registers according to spec

Documentation for most CMTs say that it takes two input clocks before
changes propagate to the timer. This is especially relevant when the timer
is stopped to change further settings.

Implement the delays according to the spec. To avoid unnecessary delays in
atomic mode, also check if the to-be-written value actually differs.

CMCNT is a bit special because testing showed that it requires 3 cycles to
propagate, which affects all CMTs. Also, the WRFLAG needs to be checked
before writing. This fixes "cannot clear CMCNT" messages which occur often
on R-Car Gen4 SoCs, but only very rarely on older SoCs for some reason.

Fixes: 81b3b2711072 ("clocksource: sh_cmt: Add support for multiple channels per device")
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20221130210609.7718-1-wsa+renesas@sang-engineering.com

authored by

Wolfram Sang and committed by
Thomas Gleixner
3f44f715 d6c494e8

+55 -33
+55 -33
drivers/clocksource/sh_cmt.c
··· 13 13 #include <linux/init.h> 14 14 #include <linux/interrupt.h> 15 15 #include <linux/io.h> 16 + #include <linux/iopoll.h> 16 17 #include <linux/ioport.h> 17 18 #include <linux/irq.h> 18 19 #include <linux/module.h> ··· 117 116 void __iomem *mapbase; 118 117 struct clk *clk; 119 118 unsigned long rate; 119 + unsigned int reg_delay; 120 120 121 121 raw_spinlock_t lock; /* Protect the shared start/stop register */ 122 122 ··· 249 247 250 248 static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, u32 value) 251 249 { 252 - if (ch->iostart) 253 - ch->cmt->info->write_control(ch->iostart, 0, value); 254 - else 255 - ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); 250 + u32 old_value = sh_cmt_read_cmstr(ch); 251 + 252 + if (value != old_value) { 253 + if (ch->iostart) { 254 + ch->cmt->info->write_control(ch->iostart, 0, value); 255 + udelay(ch->cmt->reg_delay); 256 + } else { 257 + ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); 258 + udelay(ch->cmt->reg_delay); 259 + } 260 + } 256 261 } 257 262 258 263 static inline u32 sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) ··· 269 260 270 261 static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, u32 value) 271 262 { 272 - ch->cmt->info->write_control(ch->ioctrl, CMCSR, value); 263 + u32 old_value = sh_cmt_read_cmcsr(ch); 264 + 265 + if (value != old_value) { 266 + ch->cmt->info->write_control(ch->ioctrl, CMCSR, value); 267 + udelay(ch->cmt->reg_delay); 268 + } 273 269 } 274 270 275 271 static inline u32 sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) ··· 282 268 return ch->cmt->info->read_count(ch->ioctrl, CMCNT); 283 269 } 284 270 285 - static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, u32 value) 271 + static inline int sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, u32 value) 286 272 { 273 + /* Tests showed that we need to wait 3 clocks here */ 274 + unsigned int cmcnt_delay = DIV_ROUND_UP(3 * ch->cmt->reg_delay, 2); 275 + u32 reg; 276 + 277 + if (ch->cmt->info->model > SH_CMT_16BIT) { 278 + int ret = read_poll_timeout_atomic(sh_cmt_read_cmcsr, reg, 279 + !(reg & SH_CMT32_CMCSR_WRFLG), 280 + 1, cmcnt_delay, false, ch); 281 + if (ret < 0) 282 + return ret; 283 + } 284 + 287 285 ch->cmt->info->write_count(ch->ioctrl, CMCNT, value); 286 + udelay(cmcnt_delay); 287 + return 0; 288 288 } 289 289 290 290 static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, u32 value) 291 291 { 292 - ch->cmt->info->write_count(ch->ioctrl, CMCOR, value); 292 + u32 old_value = ch->cmt->info->read_count(ch->ioctrl, CMCOR); 293 + 294 + if (value != old_value) { 295 + ch->cmt->info->write_count(ch->ioctrl, CMCOR, value); 296 + udelay(ch->cmt->reg_delay); 297 + } 293 298 } 294 299 295 300 static u32 sh_cmt_get_counter(struct sh_cmt_channel *ch, u32 *has_wrapped) ··· 352 319 353 320 static int sh_cmt_enable(struct sh_cmt_channel *ch) 354 321 { 355 - int k, ret; 322 + int ret; 356 323 357 324 dev_pm_syscore_device(&ch->cmt->pdev->dev, true); 358 325 ··· 380 347 } 381 348 382 349 sh_cmt_write_cmcor(ch, 0xffffffff); 383 - sh_cmt_write_cmcnt(ch, 0); 350 + ret = sh_cmt_write_cmcnt(ch, 0); 384 351 385 - /* 386 - * According to the sh73a0 user's manual, as CMCNT can be operated 387 - * only by the RCLK (Pseudo 32 kHz), there's one restriction on 388 - * modifying CMCNT register; two RCLK cycles are necessary before 389 - * this register is either read or any modification of the value 390 - * it holds is reflected in the LSI's actual operation. 391 - * 392 - * While at it, we're supposed to clear out the CMCNT as of this 393 - * moment, so make sure it's processed properly here. This will 394 - * take RCLKx2 at maximum. 395 - */ 396 - for (k = 0; k < 100; k++) { 397 - if (!sh_cmt_read_cmcnt(ch)) 398 - break; 399 - udelay(1); 400 - } 401 - 402 - if (sh_cmt_read_cmcnt(ch)) { 352 + if (ret || sh_cmt_read_cmcnt(ch)) { 403 353 dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n", 404 354 ch->index); 405 355 ret = -ETIMEDOUT; ··· 1011 995 1012 996 static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) 1013 997 { 1014 - unsigned int mask; 1015 - unsigned int i; 998 + unsigned int mask, i; 999 + unsigned long rate; 1016 1000 int ret; 1017 1001 1018 1002 cmt->pdev = pdev; ··· 1048 1032 if (ret < 0) 1049 1033 goto err_clk_unprepare; 1050 1034 1051 - if (cmt->info->width == 16) 1052 - cmt->rate = clk_get_rate(cmt->clk) / 512; 1053 - else 1054 - cmt->rate = clk_get_rate(cmt->clk) / 8; 1035 + rate = clk_get_rate(cmt->clk); 1036 + if (!rate) { 1037 + ret = -EINVAL; 1038 + goto err_clk_disable; 1039 + } 1040 + 1041 + /* We shall wait 2 input clks after register writes */ 1042 + if (cmt->info->model >= SH_CMT_48BIT) 1043 + cmt->reg_delay = DIV_ROUND_UP(2UL * USEC_PER_SEC, rate); 1044 + cmt->rate = rate / (cmt->info->width == 16 ? 512 : 8); 1055 1045 1056 1046 /* Map the memory resource(s). */ 1057 1047 ret = sh_cmt_map_memory(cmt);