В сообщении от 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,
 };

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to