diff options
Diffstat (limited to 'drivers/clocksource/tcb_clksrc.c')
-rw-r--r-- | drivers/clocksource/tcb_clksrc.c | 108 |
1 files changed, 68 insertions, 40 deletions
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 138a12090149..bf68504da94a 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -9,7 +9,8 @@ #include <linux/err.h> #include <linux/ioport.h> #include <linux/io.h> -#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/syscore_ops.h> #include <soc/at91/atmel_tcb.h> @@ -28,13 +29,6 @@ * source, used in either periodic or oneshot mode. This runs * at 32 KiHZ, and can handle delays of up to two seconds. * - * A boot clocksource and clockevent source are also currently needed, - * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so - * this code can be used when init_timers() is called, well before most - * devices are set up. (Some low end AT91 parts, which can run uClinux, - * have only the timers in one TC block... they currently don't support - * the tclib code, because of that initialization issue.) - * * REVISIT behavior during system suspend states... we should disable * all clocks and save the power. Easily done for clockevent devices, * but clocksources won't necessarily get the needed notifications. @@ -112,7 +106,6 @@ void tc_clksrc_resume(struct clocksource *cs) } static struct clocksource clksrc = { - .name = "tcb_clksrc", .rating = 200, .read = tc_get_cycles, .mask = CLOCKSOURCE_MASK(32), @@ -214,7 +207,6 @@ static int tc_next_event(unsigned long delta, struct clock_event_device *d) static struct tc_clkevt_device clkevt = { .clkevt = { - .name = "tc_clkevt", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, /* Should be lower than at91rm9200's system timer */ @@ -330,39 +322,73 @@ static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_id writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); } -static int __init tcb_clksrc_init(void) -{ - static char bootinfo[] __initdata - = KERN_DEBUG "%s: tc%d at %d.%03d MHz\n"; +static const u8 atmel_tcb_divisors[5] = { 2, 8, 32, 128, 0, }; + +static const struct of_device_id atmel_tcb_of_match[] = { + { .compatible = "atmel,at91rm9200-tcb", .data = (void *)16, }, + { .compatible = "atmel,at91sam9x5-tcb", .data = (void *)32, }, + { /* sentinel */ } +}; - struct platform_device *pdev; - struct atmel_tc *tc; +static int __init tcb_clksrc_init(struct device_node *node) +{ + struct atmel_tc tc; struct clk *t0_clk; + const struct of_device_id *match; u32 rate, divided_rate = 0; int best_divisor_idx = -1; int clk32k_divisor_idx = -1; + int bits; int i; int ret; - tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK); - if (!tc) { - pr_debug("can't alloc TC for clocksource\n"); - return -ENODEV; + /* Protect against multiple calls */ + if (tcaddr) + return 0; + + tc.regs = of_iomap(node->parent, 0); + if (!tc.regs) + return -ENXIO; + + t0_clk = of_clk_get_by_name(node->parent, "t0_clk"); + if (IS_ERR(t0_clk)) + return PTR_ERR(t0_clk); + + tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk"); + if (IS_ERR(tc.slow_clk)) + return PTR_ERR(tc.slow_clk); + + tc.clk[0] = t0_clk; + tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk"); + if (IS_ERR(tc.clk[1])) + tc.clk[1] = t0_clk; + tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk"); + if (IS_ERR(tc.clk[2])) + tc.clk[2] = t0_clk; + + tc.irq[2] = of_irq_get(node->parent, 2); + if (tc.irq[2] <= 0) { + tc.irq[2] = of_irq_get(node->parent, 0); + if (tc.irq[2] <= 0) + return -EINVAL; } - tcaddr = tc->regs; - pdev = tc->pdev; - t0_clk = tc->clk[0]; + match = of_match_node(atmel_tcb_of_match, node->parent); + bits = (uintptr_t)match->data; + + for (i = 0; i < ARRAY_SIZE(tc.irq); i++) + writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR)); + ret = clk_prepare_enable(t0_clk); if (ret) { pr_debug("can't enable T0 clk\n"); - goto err_free_tc; + return ret; } /* How fast will we be counting? Pick something over 5 MHz. */ rate = (u32) clk_get_rate(t0_clk); - for (i = 0; i < 5; i++) { - unsigned divisor = atmel_tc_divisors[i]; + for (i = 0; i < ARRAY_SIZE(atmel_tcb_divisors); i++) { + unsigned divisor = atmel_tcb_divisors[i]; unsigned tmp; /* remember 32 KiHz clock for later */ @@ -381,27 +407,29 @@ static int __init tcb_clksrc_init(void) best_divisor_idx = i; } - - printk(bootinfo, clksrc.name, CONFIG_ATMEL_TCB_CLKSRC_BLOCK, - divided_rate / 1000000, + clksrc.name = kbasename(node->parent->full_name); + clkevt.clkevt.name = kbasename(node->parent->full_name); + pr_debug("%s at %d.%03d MHz\n", clksrc.name, divided_rate / 1000000, ((divided_rate % 1000000) + 500) / 1000); - if (tc->tcb_config && tc->tcb_config->counter_width == 32) { + tcaddr = tc.regs; + + if (bits == 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); + tcb_setup_single_chan(&tc, best_divisor_idx); } else { - /* tclib will give us three clocks no matter what the + /* we have three clocks no matter what the * underlying platform supports. */ - ret = clk_prepare_enable(tc->clk[1]); + ret = clk_prepare_enable(tc.clk[1]); if (ret) { pr_debug("can't enable T1 clk\n"); goto err_disable_t0; } /* setup both channel 0 & 1 */ - tcb_setup_dual_chan(tc, best_divisor_idx); + tcb_setup_dual_chan(&tc, best_divisor_idx); } /* and away we go! */ @@ -410,7 +438,7 @@ static int __init tcb_clksrc_init(void) goto err_disable_t1; /* channel 2: periodic and oneshot timer support */ - ret = setup_clkevents(tc, clk32k_divisor_idx); + ret = setup_clkevents(&tc, clk32k_divisor_idx); if (ret) goto err_unregister_clksrc; @@ -420,14 +448,14 @@ err_unregister_clksrc: clocksource_unregister(&clksrc); err_disable_t1: - if (!tc->tcb_config || tc->tcb_config->counter_width != 32) - clk_disable_unprepare(tc->clk[1]); + if (bits != 32) + clk_disable_unprepare(tc.clk[1]); err_disable_t0: clk_disable_unprepare(t0_clk); -err_free_tc: - atmel_tc_free(tc); + tcaddr = NULL; + return ret; } -arch_initcall(tcb_clksrc_init); +TIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init); |