Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 296202
b: refs/heads/master
c: 8e315a7
h: refs/heads/master
v: v3
  • Loading branch information
Nicolas Ferre committed Mar 1, 2012
1 parent b96d781 commit bf23645
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 29 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 3a61a5dae49bf3d1afb7f75c8acb3607f26565af
refs/heads/master: 8e315a7b0c082c6743a6636ead5674a2265638d3
90 changes: 62 additions & 28 deletions trunk/drivers/clocksource/tcb_clksrc.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
* - Two channels combine to create a free-running 32 bit counter
* with a base rate of 5+ MHz, packaged as a clocksource (with
* resolution better than 200 nsec).
* - Some chips support 32 bit counter. A single channel is used for
* this 32 bit free-running counter. the second channel is not used.
*
* - The third channel may be used to provide a 16-bit clockevent
* source, used in either periodic or oneshot mode. This runs
Expand Down Expand Up @@ -54,6 +56,11 @@ static cycle_t tc_get_cycles(struct clocksource *cs)
return (upper << 16) | lower;
}

static cycle_t tc_get_cycles32(struct clocksource *cs)
{
return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
}

static struct clocksource clksrc = {
.name = "tcb_clksrc",
.rating = 200,
Expand Down Expand Up @@ -209,6 +216,48 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)

#endif

static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx)
{
/* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */
__raw_writel(mck_divisor_idx /* likely divide-by-8 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP /* free-run */
| ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */
| ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */
tcaddr + ATMEL_TC_REG(0, CMR));
__raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
__raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC));
__raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
__raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));

/* channel 1: waveform mode, input TIOA0 */
__raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP, /* free-run */
tcaddr + ATMEL_TC_REG(1, CMR));
__raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */
__raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR));

/* chain channel 0 to channel 1*/
__raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR);
/* then reset all the timers */
__raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
}

static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx)
{
/* channel 0: waveform mode, input mclk/8 */
__raw_writel(mck_divisor_idx /* likely divide-by-8 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP, /* free-run */
tcaddr + ATMEL_TC_REG(0, CMR));
__raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
__raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));

/* then reset all the timers */
__raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
}

static int __init tcb_clksrc_init(void)
{
static char bootinfo[] __initdata
Expand Down Expand Up @@ -260,34 +309,19 @@ static int __init tcb_clksrc_init(void)
divided_rate / 1000000,
((divided_rate + 500000) % 1000000) / 1000);

/* tclib will give us three clocks no matter what the
* underlying platform supports.
*/
clk_enable(tc->clk[1]);

/* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */
__raw_writel(best_divisor_idx /* likely divide-by-8 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP /* free-run */
| ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */
| ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */
tcaddr + ATMEL_TC_REG(0, CMR));
__raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
__raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC));
__raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
__raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));

/* channel 1: waveform mode, input TIOA0 */
__raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP, /* free-run */
tcaddr + ATMEL_TC_REG(1, CMR));
__raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */
__raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR));

/* chain channel 0 to channel 1, then reset all the timers */
__raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR);
__raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
if (tc->tcb_config && tc->tcb_config->counter_width == 32) {
/* use apropriate function to read 32 bit counter */
clksrc.read = tc_get_cycles32;
/* setup ony channel 0 */
tcb_setup_single_chan(tc, best_divisor_idx);
} else {
/* tclib will give us three clocks no matter what the
* underlying platform supports.
*/
clk_enable(tc->clk[1]);
/* setup both channel 0 & 1 */
tcb_setup_dual_chan(tc, best_divisor_idx);
}

/* and away we go! */
clocksource_register_hz(&clksrc, divided_rate);
Expand Down
20 changes: 20 additions & 0 deletions trunk/drivers/misc/atmel_tclib.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,21 @@ void atmel_tc_free(struct atmel_tc *tc)
EXPORT_SYMBOL_GPL(atmel_tc_free);

#if defined(CONFIG_OF)
static struct atmel_tcb_config tcb_rm9200_config = {
.counter_width = 16,
};

static struct atmel_tcb_config tcb_sam9x5_config = {
.counter_width = 32,
};

static const struct of_device_id atmel_tcb_dt_ids[] = {
{
.compatible = "atmel,at91rm9200-tcb",
.data = &tcb_rm9200_config,
}, {
.compatible = "atmel,at91sam9x5-tcb",
.data = &tcb_sam9x5_config,
}, {
/* sentinel */
}
Expand Down Expand Up @@ -150,6 +162,14 @@ static int __init tc_probe(struct platform_device *pdev)
return -EINVAL;
}

/* Now take SoC information if available */
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node);
if (match)
tc->tcb_config = match->data;
}

tc->clk[0] = clk;
tc->clk[1] = clk_get(&pdev->dev, "t1_clk");
if (IS_ERR(tc->clk[1]))
Expand Down
10 changes: 10 additions & 0 deletions trunk/include/linux/atmel_tc.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,20 @@

struct clk;

/**
* struct atmel_tcb_config - SoC data for a Timer/Counter Block
* @counter_width: size in bits of a timer counter register
*/
struct atmel_tcb_config {
size_t counter_width;
};

/**
* struct atmel_tc - information about a Timer/Counter Block
* @pdev: physical device
* @iomem: resource associated with the I/O register
* @regs: mapping through which the I/O registers can be accessed
* @tcb_config: configuration data from SoC
* @irq: irq for each of the three channels
* @clk: internal clock source for each of the three channels
* @node: list node, for tclib internal use
Expand All @@ -54,6 +63,7 @@ struct atmel_tc {
struct platform_device *pdev;
struct resource *iomem;
void __iomem *regs;
struct atmel_tcb_config *tcb_config;
int irq[3];
struct clk *clk[3];
struct list_head node;
Expand Down

0 comments on commit bf23645

Please sign in to comment.