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

clocksource: sh_cmt: wait for CMCNT on init V2

Add code to the CMT driver to wait for CMCNT V2. This to let
the register value settle before starting the timer channel.
Makes the driver more robust.

Needed for CMT2 on sh7372 and certain CMT channels on sh73a0.

Signed-off-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>

authored by

Magnus Damm and committed by
Paul Mundt
3f7e5e24 cf6ace16

+32 -2
+32 -2
drivers/clocksource/sh_cmt.c
··· 26 26 #include <linux/clk.h> 27 27 #include <linux/irq.h> 28 28 #include <linux/err.h> 29 + #include <linux/delay.h> 29 30 #include <linux/clocksource.h> 30 31 #include <linux/clockchips.h> 31 32 #include <linux/sh_timer.h> ··· 151 150 152 151 static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) 153 152 { 154 - int ret; 153 + int k, ret; 155 154 156 155 /* enable clock */ 157 156 ret = clk_enable(p->clk); 158 157 if (ret) { 159 158 dev_err(&p->pdev->dev, "cannot enable clock\n"); 160 - return ret; 159 + goto err0; 161 160 } 162 161 163 162 /* make sure channel is disabled */ ··· 175 174 sh_cmt_write(p, CMCOR, 0xffffffff); 176 175 sh_cmt_write(p, CMCNT, 0); 177 176 177 + /* 178 + * According to the sh73a0 user's manual, as CMCNT can be operated 179 + * only by the RCLK (Pseudo 32 KHz), there's one restriction on 180 + * modifying CMCNT register; two RCLK cycles are necessary before 181 + * this register is either read or any modification of the value 182 + * it holds is reflected in the LSI's actual operation. 183 + * 184 + * While at it, we're supposed to clear out the CMCNT as of this 185 + * moment, so make sure it's processed properly here. This will 186 + * take RCLKx2 at maximum. 187 + */ 188 + for (k = 0; k < 100; k++) { 189 + if (!sh_cmt_read(p, CMCNT)) 190 + break; 191 + udelay(1); 192 + } 193 + 194 + if (sh_cmt_read(p, CMCNT)) { 195 + dev_err(&p->pdev->dev, "cannot clear CMCNT\n"); 196 + ret = -ETIMEDOUT; 197 + goto err1; 198 + } 199 + 178 200 /* enable channel */ 179 201 sh_cmt_start_stop_ch(p, 1); 180 202 return 0; 203 + err1: 204 + /* stop clock */ 205 + clk_disable(p->clk); 206 + 207 + err0: 208 + return ret; 181 209 } 182 210 183 211 static void sh_cmt_disable(struct sh_cmt_priv *p)