В сообщении от 25 августа 2010 11:45:29 автор Riccardo Magliocchetti написал: > Hello, > > does anybody know what happened to this patch? it should be the premise > to have NO_HZ working on freerunner which in should mean less power > consumption. > > http://marc.info/?l=linux-arm-kernel&m=122390982102692&w=2 > > The patch does not apply to 2.6.29 and 2.6.32 and it looks it has never > been applied to openmoko kernel. > > Is someone with some clue willing to take a look at it?
Here's version that applies on 2.6.35-rc1, however this patch breaks suspend, and I was lazy to fix it. Maybe someone wants to continue this work? Regards Vasily
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9adc7e5..a6fde84 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -618,6 +618,8 @@ config ARCH_SA1100 config ARCH_S3C2410 bool "Samsung S3C2410, S3C2412, S3C2413, S3C2440, S3C2442, S3C2443" select GENERIC_GPIO + select GENERIC_TIME + select GENERIC_CLOCKEVENTS select ARCH_HAS_CPUFREQ select HAVE_CLK help diff --git a/arch/arm/plat-samsung/time.c b/arch/arm/plat-samsung/time.c index 2231d80..6555cdd 100644 --- a/arch/arm/plat-samsung/time.c +++ b/arch/arm/plat-samsung/time.c @@ -1,5 +1,8 @@ /* linux/arch/arm/plat-samsung/time.c * + * Copyright (C) 2008 Andrzej Zaborowski <bal...@xxxxxxxxx> + * + * Previous version: * Copyright (C) 2003-2005 Simtec Electronics * Ben Dooks, <[email protected]> * @@ -19,7 +22,6 @@ */ #include <linux/kernel.h> -#include <linux/sched.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/irq.h> @@ -27,259 +29,455 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/clockchips.h> +#include <linux/stringify.h> #include <asm/system.h> -#include <asm/leds.h> #include <asm/mach-types.h> - #include <asm/irq.h> -#include <mach/map.h> -#include <plat/regs-timer.h> -#include <mach/regs-irq.h> #include <asm/mach/time.h> -#include <mach/tick.h> +#include <plat/irq.h> #include <plat/clock.h> #include <plat/cpu.h> +#include <plat/regs-watchdog.h> +#include <plat/regs-timer.h> -static unsigned long timer_startval; -static unsigned long timer_usec_ticks; +#include <mach/tick.h> -#ifndef TICK_MAX -#define TICK_MAX (0xffff) +/* GTA02 uses all PWM outputs (0-3) for LEDs */ +#define use_pwm_clocksource() !machine_is_neo1973_gta02() +#ifdef CONFIG_S3C2410_WATCHDOG +#define use_wdt_clocksource() 0 +#else +#define use_wdt_clocksource() 1 #endif -#define TIMER_USEC_SHIFT 16 +static int s3c24xx_pwm_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + uint32_t tcfg1 = __raw_readl(S3C2410_TCFG1); + uint32_t tcon = __raw_readl(S3C2410_TCON); + + tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; + + /* Should add likely()ies/unlikely()ies based on usage stats */ + if (cycles < (1 << 16)) { + tcfg1 |= S3C2410_TCFG1_MUX4_DIV2; + } else if (cycles < (1 << 17)) { + tcfg1 |= S3C2410_TCFG1_MUX4_DIV4; + cycles >>= 1; + } else if (cycles < (1 << 18)) { + tcfg1 |= S3C2410_TCFG1_MUX4_DIV8; + cycles >>= 2; + } else if (cycles < (1 << 19)) { + tcfg1 |= S3C2410_TCFG1_MUX4_DIV16; + cycles >>= 3; + } else + panic("delta > max_delta_ns!\n"); + + /* + * Note that the interrupt is requested when the counter + * reaches 0 while the reset value is loaded one tick + * later, not at the same time as the interrupt. So in + * auto-reload mode we could support timeouts one cycle longer + * than in one-shot mode. + */ + if (evt->mode == CLOCK_EVT_MODE_PERIODIC) + cycles--; -/* we use the shifted arithmetic to work out the ratio of timer ticks - * to usecs, as often the peripheral clock is not a nice even multiple - * of 1MHz. - * - * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok - * for the current HZ value of 200 without producing overflows. - * - * Original patch by Dimitry Andric, updated by Ben Dooks -*/ + __raw_writel(tcfg1, S3C2410_TCFG1); + __raw_writel(cycles, S3C2410_TCNTB(4)); + tcon &= ~(S3C2410_TCON_T4START | S3C2410_TCON_T4MANUALUPD); + __raw_writel(tcon | S3C2410_TCON_T4MANUALUPD, S3C2410_TCON); + __raw_writel(tcon | S3C2410_TCON_T4START, S3C2410_TCON); + + return 0; +} -/* timer_mask_usec_ticks - * - * given a clock and divisor, make the value to pass into timer_ticks_to_usec - * to scale the ticks into usecs -*/ +static void s3c24xx_pwm_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) + { + uint32_t tcon = __raw_readl(S3C2410_TCON); + + tcon &= ~S3C2410_TCON_T4START; + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + tcon |= S3C2410_TCON_T4RELOAD; + __raw_writel(tcon, S3C2410_TCON); + break; + case CLOCK_EVT_MODE_ONESHOT: + tcon &= ~S3C2410_TCON_T4RELOAD; + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + break; + case CLOCK_EVT_MODE_RESUME: + /* REVISIT */ + break; + } -static inline unsigned long -timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk) -{ - unsigned long den = pclk / 1000; + __raw_writel(tcon, S3C2410_TCON); - return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; + if (mode == CLOCK_EVT_MODE_PERIODIC) { + evt->mode = mode; + evt->set_next_event(((uint64_t) NSEC_PER_SEC / HZ * + evt->mult) >> evt->shift, evt); + } } -/* timer_ticks_to_usec - * - * convert timer ticks to usec. -*/ - -static inline unsigned long timer_ticks_to_usec(unsigned long ticks) +static irqreturn_t s3c24xx_timer_interrupt(int irq, void *dev_id) { - unsigned long res; + struct clock_event_device *evt = dev_id; - res = ticks * timer_usec_ticks; - res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */ + evt->event_handler(evt); - return res >> TIMER_USEC_SHIFT; + return IRQ_HANDLED; } -/*** - * Returns microsecond since last clock interrupt. Note that interrupts - * will have been disabled by do_gettimeoffset() - * IRQs are disabled before entering here from do_gettimeofday() - */ - -static unsigned long s3c2410_gettimeoffset (void) -{ - unsigned long tdone; - unsigned long tval; - - /* work out how many ticks have gone since last timer interrupt */ - - tval = __raw_readl(S3C2410_TCNTO(4)); - tdone = timer_startval - tval; +static struct clock_event_device s3c24xx_pwm_timer4 = { + .name = "pwm_timer4", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .shift = 14, + .rating = 150, + .set_next_event = s3c24xx_pwm_set_next_event, + .set_mode = s3c24xx_pwm_set_mode, +}; - /* check to see if there is an interrupt pending */ +static struct irqaction s3c24xx_timer4_irq = { + .name = "pwm_timer4 tick", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = s3c24xx_timer_interrupt, + .dev_id = &s3c24xx_pwm_timer4, +}; - if (s3c24xx_ostimer_pending()) { - /* re-read the timer, and try and fix up for the missed - * interrupt. Note, the interrupt may go off before the - * timer has re-loaded from wrapping. - */ +static void __init s3c24xx_pwm_clockevent_init(unsigned long rate, int pre) +{ + setup_irq(IRQ_TIMER4, &s3c24xx_timer4_irq); - tval = __raw_readl(S3C2410_TCNTO(4)); - tdone = timer_startval - tval; + rate = (rate + pre / 2) / pre; + rate >>= 1; - if (tval != 0) - tdone += timer_startval; - } + s3c24xx_pwm_timer4.mult = + div_sc(rate, NSEC_PER_SEC, s3c24xx_pwm_timer4.shift); + s3c24xx_pwm_timer4.min_delta_ns = + clockevent_delta2ns(1, &s3c24xx_pwm_timer4); + s3c24xx_pwm_timer4.max_delta_ns = + clockevent_delta2ns(0xffff * 8, &s3c24xx_pwm_timer4); - return timer_ticks_to_usec(tdone); + clockevents_register_device(&s3c24xx_pwm_timer4); } +#define CS_TIMER_NUM 2 +#define CS_TIMER_REG(pre, post) pre ## 2 ## post -/* - * IRQ handler for the timer - */ -static irqreturn_t -s3c2410_timer_interrupt(int irq, void *dev_id) +static cycle_t s3c24xx_pwm_read(struct clocksource *cs) { - timer_tick(); - return IRQ_HANDLED; + return __raw_readl(S3C2410_TCNTO(CS_TIMER_NUM)) ^ 0xffff; } -static struct irqaction s3c2410_timer_irq = { - .name = "S3C2410 Timer Tick", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = s3c2410_timer_interrupt, +static struct clocksource s3c24xx_pwm_timer_n = { + .name = "pwm_timer" __stringify(CS_TIMER_NUM), + .rating = 150, + .read = s3c24xx_pwm_read, + .mask = CLOCKSOURCE_MASK(16), + .shift = 14, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; -#define use_tclk1_12() ( \ - machine_is_bast() || \ - machine_is_vr1000() || \ - machine_is_anubis() || \ - machine_is_osiris()) - -static struct clk *tin; -static struct clk *tdiv; -static struct clk *timerclk; -/* - * Set up timer interrupt, and return the current time in seconds. - * - * Currently we only use timer4, as it is the only timer which has no - * other function that can be exploited externally - */ -static void s3c2410_timer_setup (void) +static void __init s3c24xx_pwm_clocksource_init(unsigned long rate, int pre) { - unsigned long tcon; - unsigned long tcnt; - unsigned long tcfg1; - unsigned long tcfg0; + uint32_t tcon = __raw_readl(S3C2410_TCON); + uint32_t tcfg1 = __raw_readl(S3C2410_TCFG1); + int shift = s3c24xx_pwm_timer_n.shift; + + /* + * We want the timer resolution as high as possible but we're + * limited by the clockevents resolution: the clocksource + * interval must not be shorter than the max clockevent timeout + * in one-shot mode. Set it to about 4 seconds then. + */ + tcfg1 &= ~CS_TIMER_REG(S3C2410_TCFG1_MUX, _MASK); + tcfg1 |= CS_TIMER_REG(S3C2410_TCFG1_MUX, _DIV16); + __raw_writel(tcfg1, S3C2410_TCFG1); - tcnt = TICK_MAX; /* default value for tcnt */ + rate = (rate + pre / 2) / pre; + shift += 4; - /* configure the system for whichever machine is in use */ + __raw_writel(0xffff, S3C2410_TCNTB(CS_TIMER_NUM)); - if (use_tclk1_12()) { - /* timer is at 12MHz, scaler is 1 */ - timer_usec_ticks = timer_mask_usec_ticks(1, 12000000); - tcnt = 12000000 / HZ; + tcon |= CS_TIMER_REG(S3C2410_TCON_T, RELOAD); + __raw_writel(tcon | CS_TIMER_REG(S3C2410_TCON_T, MANUALUPD), + S3C2410_TCON); + __raw_writel(tcon | CS_TIMER_REG(S3C2410_TCON_T, START), + S3C2410_TCON); - tcfg1 = __raw_readl(S3C2410_TCFG1); - tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; - tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1; - __raw_writel(tcfg1, S3C2410_TCFG1); - } else { - unsigned long pclk; - struct clk *tscaler; - - /* for the h1940 (and others), we use the pclk from the core - * to generate the timer values. since values around 50 to - * 70MHz are not values we can directly generate the timer - * value from, we need to pre-scale and divide before using it. - * - * for instance, using 50.7MHz and dividing by 6 gives 8.45MHz - * (8.45 ticks per usec) - */ - - pclk = clk_get_rate(timerclk); + s3c24xx_pwm_timer_n.mult = clocksource_hz2mult(rate, shift); + clocksource_register(&s3c24xx_pwm_timer_n); +} - /* configure clock tick */ +/* + * If the watchdog timer and PWM4 are both available we combine them + * to make a 27-bit monotonic timer running at pclk / 2 rate, to use + * as a clocksource. The watchdog timer serves as a one-shot capable + * clockevent at the same time. + */ +static uint32_t combined_start_lo; +static uint32_t combined_start_hi; +static uint32_t combined_val; +static int combined_shift; +static int combined_reloaded; +static inline void s3c24xx_combined_update(void) +{ + uint32_t delta_hi, delta_lo; - timer_usec_ticks = timer_mask_usec_ticks(6, pclk); + /* Current value */ + uint32_t lo = __raw_readl(S3C2410_TCNTO(4)); + uint32_t hi = __raw_readl(S3C2410_WTCNT); - tscaler = clk_get_parent(tdiv); + /* Sometimes the timer reloads before the interrupt arrives :-( */ + if (hi > combined_start_hi) { + combined_start_hi += 0x10000; + combined_reloaded = 1; + } - clk_set_rate(tscaler, pclk / 3); - clk_set_rate(tdiv, pclk / 6); - clk_set_parent(tin, tdiv); + /* Difference since last update */ + delta_lo = (combined_start_lo - lo) & 0xffff; + delta_hi = (combined_start_hi - hi) << (combined_shift + 3); + + /* + * Now delta_hi may be inaccurate but it never overflows, delta_lo + * however has the accurate offset in ticks but may have wrapped + * around multiple times, so try to round the delta_hi value up + * or down to the nearest delta_lo + a multiple of 0x10000. + */ + if (delta_lo > delta_hi + 0x8000) + delta_hi = 0; + else + delta_hi = (delta_hi + 0x8000 - delta_lo) & ~0xffff; + + combined_val += delta_hi | delta_lo; + combined_start_lo = lo; + combined_start_hi = hi; +} - tcnt = clk_get_rate(tin) / HZ; +static int s3c24xx_combined_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + uint32_t wtcon = S3C2410_WTCON_INTEN | S3C2410_WTCON_ENABLE; + int new_shift = 0; + + /* Should add likely()ies/unlikely()ies based on usage stats */ + if (cycles >= (1 << 23)) { + cycles >>= 8; + new_shift = 8; + wtcon |= S3C2410_WTCON_PRESCALE(255); + } else if (cycles >= (1 << 19)) { + cycles >>= 4; + new_shift = 4; + wtcon |= S3C2410_WTCON_PRESCALE(15); } - tcon = __raw_readl(S3C2410_TCON); - tcfg0 = __raw_readl(S3C2410_TCFG0); - tcfg1 = __raw_readl(S3C2410_TCFG1); + if (cycles < (1 << 16)) { + wtcon |= S3C2410_WTCON_DIV16; + } else if (cycles < (1 << 17)) { + wtcon |= S3C2410_WTCON_DIV32; + cycles >>= 1; + new_shift += 1; + } else if (cycles < (1 << 18)) { + wtcon |= S3C2410_WTCON_DIV64; + cycles >>= 2; + new_shift += 2; + } else if (cycles < (1 << 19)) { + wtcon |= S3C2410_WTCON_DIV128; + cycles >>= 3; + new_shift += 3; + } else + panic("delta > max_delta_ns!\n"); + + s3c24xx_combined_update(); + + __raw_writel(0, S3C2410_WTCON); /* Stop */ + __raw_writel(cycles, S3C2410_WTCNT); + __raw_writel(wtcon, S3C2410_WTCON); /* Start */ + + combined_shift = new_shift; + combined_start_hi = cycles; + combined_reloaded = 0; + + return 0; +} - /* timers reload after counting zero, so reduce the count by 1 */ +static void s3c24xx_combined_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_PERIODIC: + /* Only one-shot supported */ + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + /* Don't shut down because our timers are still used for + * the clocksource */ + break; + case CLOCK_EVT_MODE_RESUME: + /* REVISIT */ + break; + } +} - tcnt--; +static irqreturn_t s3c24xx_combined_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + /* + * Note: needs to be done at the same time the timer is reloaded and + * when this happens is not specified very well. It seems that the + * interrupt fires much too late sometimes. + */ + if (!combined_reloaded) + combined_start_hi += 0x10000; + combined_reloaded = 0; + + if (combined_start_hi < 0x20000) + evt->event_handler(evt); - printk(KERN_DEBUG "timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx, usec %08lx\n", - tcon, tcnt, tcfg0, tcfg1, timer_usec_ticks); + return IRQ_HANDLED; +} - /* check to see if timer is within 16bit range... */ - if (tcnt > TICK_MAX) { - panic("setup_timer: HZ is too small, cannot configure timer!"); - return; - } +static cycle_t s3c24xx_combined_read(struct clocksource *cs) +{ + s3c24xx_combined_update(); - __raw_writel(tcfg1, S3C2410_TCFG1); - __raw_writel(tcfg0, S3C2410_TCFG0); + return combined_val; +} - timer_startval = tcnt; - __raw_writel(tcnt, S3C2410_TCNTB(4)); +static struct clock_event_device s3c24xx_combined_clockevent = { + .name = "wd_timer", + .shift = 32, + .rating = 300, + .features = CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = s3c24xx_combined_set_next_event, + .set_mode = s3c24xx_combined_set_mode, +}; + +static struct clocksource s3c24xx_combined_clocksource = { + .name = "pwm_timer", + .shift = 24, + .rating = 300, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .read = s3c24xx_combined_read, +}; - /* ensure timer is stopped... */ +static struct irqaction s3c24xx_combined_irq = { + .name = "wd_timer tick", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = s3c24xx_combined_interrupt, + .dev_id = &s3c24xx_combined_clockevent, +}; - tcon &= ~(7<<20); - tcon |= S3C2410_TCON_T4RELOAD; - tcon |= S3C2410_TCON_T4MANUALUPD; +static void __init s3c24xx_pwm_wdt_combined_init(unsigned long rate) +{ + uint32_t tcfg0, tcfg1, tcon; + /* Make sure Watchdog and PWM4 timers are stopped */ + __raw_writel(0, S3C2410_WTCON); + tcon = __raw_readl(S3C2410_TCON); + tcon &= ~(S3C2410_TCON_T4START | S3C2410_TCON_T4MANUALUPD); __raw_writel(tcon, S3C2410_TCON); - __raw_writel(tcnt, S3C2410_TCNTB(4)); - __raw_writel(tcnt, S3C2410_TCMPB(4)); - /* start the timer running */ - tcon |= S3C2410_TCON_T4START; - tcon &= ~S3C2410_TCON_T4MANUALUPD; + setup_irq(IRQ_WDT, &s3c24xx_combined_irq); + + /* Set the PWM4 at the highest rate possible */ + tcfg0 = __raw_readl(S3C2410_TCFG0); + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 |= 0 << S3C2410_TCFG_PRESCALER1_SHIFT; + __raw_writel(tcfg0, S3C2410_TCFG0); + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; + tcfg1 |= S3C2410_TCFG1_MUX4_DIV2; + __raw_writel(tcfg1, S3C2410_TCFG1); + + /* Reload the timers always with the max values */ + __raw_writel(0xffff, S3C2410_WTDAT); + __raw_writel(0xffff, S3C2410_TCNTB(4)); + + /* Register the creature */ + s3c24xx_combined_clockevent.mult = + div_sc(rate, NSEC_PER_SEC, + s3c24xx_combined_clockevent.shift - 4); + s3c24xx_combined_clockevent.min_delta_ns = + clockevent_delta2ns(1, &s3c24xx_combined_clockevent); + s3c24xx_combined_clockevent.max_delta_ns = + clockevent_delta2ns(0xffff << 11, + &s3c24xx_combined_clockevent); + clockevents_register_device(&s3c24xx_combined_clockevent); + + s3c24xx_combined_clocksource.mult = + clocksource_hz2mult(rate, + s3c24xx_combined_clocksource.shift + 1); + clocksource_register(&s3c24xx_combined_clocksource); + + /* Start the clocksource */ + tcon |= S3C2410_TCON_T4START | S3C2410_TCON_T4RELOAD; __raw_writel(tcon, S3C2410_TCON); + + printk(KERN_INFO "Combined WDT and PWM4 timer runs at %li Hz\n", + rate / 2); } -static void __init s3c2410_timer_resources(void) +static void __init s3c24xx_timer_init(void) { - struct platform_device tmpdev; - - tmpdev.dev.bus = &platform_bus_type; - tmpdev.id = 4; + unsigned long clock_tick_rate, prescaler; + struct clk *clk; + uint32_t tcfg0, tcon; - timerclk = clk_get(NULL, "timers"); - if (IS_ERR(timerclk)) + clk = clk_get(NULL, "timers"); + if (IS_ERR(clk)) panic("failed to get clock for system timer"); - clk_enable(timerclk); + clk_enable(clk); + clock_tick_rate = clk_get_rate(clk); - if (!use_tclk1_12()) { - tin = clk_get(&tmpdev.dev, "pwm-tin"); - if (IS_ERR(tin)) - panic("failed to get pwm-tin clock for system timer"); - - tdiv = clk_get(&tmpdev.dev, "pwm-tdiv"); - if (IS_ERR(tdiv)) - panic("failed to get pwm-tdiv clock for system timer"); + if (use_wdt_clocksource()) { + s3c24xx_combined_clockevent.cpumask = cpumask_of(0); + s3c24xx_pwm_wdt_combined_init(clock_tick_rate); + } else { + s3c24xx_pwm_timer4.cpumask = cpumask_of(0); + /* + * We will use PWM timer CS_TIMER_NUM and PWM timer 4 for + * clocksource and clockevents respectively. Set a prescaler + * such that the clocksource, which is 16-bit only, can + * overflow at 1Hz with divider set to 1/4. The clockevents + * will then support timeouts up to 4s with divider of 1/16. + */ + prescaler = (clock_tick_rate >> 16) / 4 + 1; + + tcfg0 = __raw_readl(S3C2410_TCFG0); + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 |= (prescaler - 1) << S3C2410_TCFG_PRESCALER1_SHIFT; + __raw_writel(tcfg0, S3C2410_TCFG0); + printk(KERN_INFO "Prescaler for PWM timers set to %li\n", + prescaler); + + /* Make sure PWM timers CS_TIMER_NUM and 4 are stopped */ + tcon = __raw_readl(S3C2410_TCON); + tcon &= ~(CS_TIMER_REG(S3C2410_TCON_T, START) | + CS_TIMER_REG(S3C2410_TCON_T, MANUALUPD)); + tcon &= ~(S3C2410_TCON_T4START | S3C2410_TCON_T4MANUALUPD); + __raw_writel(tcon, S3C2410_TCON); + + s3c24xx_pwm_clockevent_init(clock_tick_rate, prescaler); + if (use_pwm_clocksource()) + s3c24xx_pwm_clocksource_init(clock_tick_rate, + prescaler); } - - clk_enable(tin); -} - -static void __init s3c2410_timer_init(void) -{ - s3c2410_timer_resources(); - s3c2410_timer_setup(); - setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); } struct sys_timer s3c24xx_timer = { - .init = s3c2410_timer_init, - .offset = s3c2410_gettimeoffset, - .resume = s3c2410_timer_setup + .init = s3c24xx_timer_init, };
signature.asc
Description: This is a digitally signed message part.
