Skip to content

Commit

Permalink
clocksource: sh_cmt: wait for CMCNT on init V2
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Magnus Damm authored and Paul Mundt committed Jul 21, 2011
1 parent cf6ace1 commit 3f7e5e2
Showing 1 changed file with 32 additions and 2 deletions.
34 changes: 32 additions & 2 deletions drivers/clocksource/sh_cmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/sh_timer.h>
Expand Down Expand Up @@ -150,13 +151,13 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)

static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
{
int ret;
int k, ret;

/* enable clock */
ret = clk_enable(p->clk);
if (ret) {
dev_err(&p->pdev->dev, "cannot enable clock\n");
return ret;
goto err0;
}

/* make sure channel is disabled */
Expand All @@ -174,9 +175,38 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
sh_cmt_write(p, CMCOR, 0xffffffff);
sh_cmt_write(p, CMCNT, 0);

/*
* According to the sh73a0 user's manual, as CMCNT can be operated
* only by the RCLK (Pseudo 32 KHz), there's one restriction on
* modifying CMCNT register; two RCLK cycles are necessary before
* this register is either read or any modification of the value
* it holds is reflected in the LSI's actual operation.
*
* While at it, we're supposed to clear out the CMCNT as of this
* moment, so make sure it's processed properly here. This will
* take RCLKx2 at maximum.
*/
for (k = 0; k < 100; k++) {
if (!sh_cmt_read(p, CMCNT))
break;
udelay(1);
}

if (sh_cmt_read(p, CMCNT)) {
dev_err(&p->pdev->dev, "cannot clear CMCNT\n");
ret = -ETIMEDOUT;
goto err1;
}

/* enable channel */
sh_cmt_start_stop_ch(p, 1);
return 0;
err1:
/* stop clock */
clk_disable(p->clk);

err0:
return ret;
}

static void sh_cmt_disable(struct sh_cmt_priv *p)
Expand Down

0 comments on commit 3f7e5e2

Please sign in to comment.