* [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers.
@ 2011-08-28 0:45 Tomasz Figa
0 siblings, 0 replies; 5+ messages in thread
From: Tomasz Figa @ 2011-08-28 0:45 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds a sys_timer implementing generic clock source and clock event
device on S3C64xx using PWM timers 3 and 4. It can be enabled by a Kconfig
option, based on which either the new sys_timer is used as s3c64xx_timer or
s3c64xx_timer is defined to s3c24xx_timer which is the old tick timer.
Signed-off-by: Tomasz Figa <tomasz.figa@gmail.com>
---
arch/arm/Kconfig | 1 -
arch/arm/mach-s3c64xx/Kconfig | 12 ++
arch/arm/mach-s3c64xx/Makefile | 3 +
arch/arm/mach-s3c64xx/time.c | 320 ++++++++++++++++++++++++++++++
arch/arm/plat-samsung/include/plat/cpu.h | 7 +
5 files changed, 342 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-s3c64xx/time.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9adc278..05941f6 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -700,7 +700,6 @@ config ARCH_S3C64XX
select ARM_VIC
select HAVE_CLK
select NO_IOPORT
- select ARCH_USES_GETTIMEOFFSET
select ARCH_HAS_CPUFREQ
select ARCH_REQUIRE_GPIOLIB
select SAMSUNG_CLKSRC
diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig
index e4177e2..2db76fd 100644
--- a/arch/arm/mach-s3c64xx/Kconfig
+++ b/arch/arm/mach-s3c64xx/Kconfig
@@ -25,6 +25,18 @@ config CPU_S3C6410
help
Enable S3C6410 CPU support
+config S3C64XX_GENERIC_CLOCKEVENTS
+ bool "Enable high resolution generic time support using PWM timers"
+ select GENERIC_CLOCKEVENTS
+ help
+ This option enables high resolution generic time support
+ on S3C6400/S3C6410 SoCs using PWM timers 3 and 4 instead
+ of standard periodic tick using PWM timer 4.
+
+config S3C64XX_ARCH_USES_GETTIMEOFFSET
+ def_bool y if !S3C64XX_GENERIC_CLOCKEVENTS
+ select ARCH_USES_GETTIMEOFFSET
+
config S3C64XX_DMA
bool "S3C64XX DMA"
select S3C_DMA
diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile
index 4657363..a152002 100644
--- a/arch/arm/mach-s3c64xx/Makefile
+++ b/arch/arm/mach-s3c64xx/Makefile
@@ -15,6 +15,9 @@ obj-y += cpu.o
obj-y += clock.o
obj-y += gpiolib.o
+# Generic clockevents
+obj-$(CONFIG_S3C64XX_GENERIC_CLOCKEVENTS) += time.o
+
# Core support for S3C6400 system
obj-$(CONFIG_CPU_S3C6400) += s3c6400.o
diff --git a/arch/arm/mach-s3c64xx/time.c b/arch/arm/mach-s3c64xx/time.c
new file mode 100644
index 0000000..4fa57f0
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/time.c
@@ -0,0 +1,320 @@
+/* linux/arch/arm/mach-s3c64xx/time.c
+ *
+ * Copyright (c) 2011 Tomasz Figa <tomasz.figa@gmail.com>
+ *
+ * based on linux/arch/arm/plat-samsung/s5p-time.c
+ *
+ * S3C64XX generic high resolution time support using PWM 3 and PWM 4 timers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Notes:
+ * - Frequency divisors are currently hardcoded to make both timers operate
+ * at 1/6 the frequency of PCLK, so with the usual PCLK frequency of 66 MHz,
+ * the timers are clocked at 11 MHz giving us the operating range from
+ * 90 nsec to 386 sec.
+ * - PWM registers, especially TCON, are shared with PWM driver, but since
+ * all the clock event callbacks are run in atomic context synchronization
+ * is not needed.
+ */
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/platform_device.h>
+
+#include <mach/map.h>
+#include <plat/regs-timer.h>
+#include <asm/mach/time.h>
+
+#define PWM_EVENT (3)
+#define PWM_SOURCE (4)
+
+#define ONESHOT (0)
+#define PERIODIC (1)
+
+#define TCNT_MAX (0xffffffff)
+
+static struct clk *timerclk;
+
+/*
+ * PWM timers setup code
+ */
+
+static inline void s3c64xx_pwm_stop(unsigned int pwm_id)
+{
+ unsigned long tcon = __raw_readl(S3C2410_TCON);
+
+ switch (pwm_id) {
+ case 3:
+ tcon &= ~S3C2410_TCON_T3START;
+ break;
+ case 4:
+ tcon &= ~S3C2410_TCON_T4START;
+ break;
+ }
+
+ __raw_writel(tcon, S3C2410_TCON);
+}
+
+static inline void s3c64xx_pwm_init(unsigned int pwm_id, unsigned long tcnt)
+{
+ unsigned long tcon = __raw_readl(S3C2410_TCON);
+
+ /* timers reload after counting zero, so reduce the count by 1 */
+ --tcnt;
+
+ /* stop the timer and ask it to load the new value */
+ switch (pwm_id) {
+ case 3:
+ tcon &= ~(0xf<<16);
+ tcon |= S3C2410_TCON_T3MANUALUPD;
+ break;
+ case 4:
+ tcon &= ~(7<<20);
+ tcon |= S3C2410_TCON_T4MANUALUPD;
+ break;
+ }
+
+ __raw_writel(tcnt, S3C2410_TCNTB(pwm_id));
+ __raw_writel(tcnt, S3C2410_TCMPB(pwm_id));
+ __raw_writel(tcon, S3C2410_TCON);
+}
+
+static inline void s3c64xx_pwm_start(unsigned int pwm_id, bool periodic)
+{
+ unsigned long tcon = __raw_readl(S3C2410_TCON);
+
+ switch (pwm_id) {
+ case 3:
+ tcon |= S3C2410_TCON_T3START;
+ tcon &= ~S3C2410_TCON_T3MANUALUPD;
+
+ if (periodic)
+ tcon |= S3C2410_TCON_T3RELOAD;
+ else
+ tcon &= ~S3C2410_TCON_T3RELOAD;
+ break;
+ case 4:
+ tcon |= S3C2410_TCON_T4START;
+ tcon &= ~S3C2410_TCON_T4MANUALUPD;
+
+ if (periodic)
+ tcon |= S3C2410_TCON_T4RELOAD;
+ else
+ tcon &= ~S3C2410_TCON_T4RELOAD;
+ break;
+ }
+
+ __raw_writel(tcon, S3C2410_TCON);
+}
+
+/*
+ * Clock event
+ */
+
+static struct clk *event_in;
+static struct clk *event_div;
+
+static unsigned long clock_count_per_tick;
+
+static int s3c64xx_clock_event_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ s3c64xx_pwm_init(PWM_EVENT, cycles);
+ s3c64xx_pwm_start(PWM_EVENT, ONESHOT);
+ return 0;
+}
+
+static void s3c64xx_clock_event_resume(void)
+{
+ unsigned long pclk;
+ struct clk *tscaler;
+
+ pclk = clk_get_rate(timerclk);
+ tscaler = clk_get_parent(event_div);
+ clk_set_rate(tscaler, pclk / 3);
+
+ clk_set_rate(event_div, pclk / 6);
+ clk_set_parent(event_in, event_div);
+
+ s3c64xx_pwm_init(PWM_EVENT, clock_count_per_tick);
+ s3c64xx_pwm_start(PWM_EVENT, PERIODIC);
+}
+
+static void s3c64xx_clock_event_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ s3c64xx_pwm_stop(PWM_EVENT);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ s3c64xx_pwm_init(PWM_EVENT, clock_count_per_tick);
+ s3c64xx_pwm_start(PWM_EVENT, PERIODIC);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ s3c64xx_clock_event_resume();
+ break;
+ }
+}
+
+static struct clock_event_device s3c64xx_clock_event_device = {
+ .name = "s3c64xx_clkevt",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 200,
+ .shift = 32,
+ .set_next_event = s3c64xx_clock_event_set_next_event,
+ .set_mode = s3c64xx_clock_event_set_mode,
+};
+
+static irqreturn_t s3c64xx_clock_event_isr(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = &s3c64xx_clock_event_device;
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction s3c64xx_clock_event_irq = {
+ .name = "s3c64xx_clkevt_irq",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = s3c64xx_clock_event_isr,
+};
+
+static void __init s3c64xx_clock_event_init(void)
+{
+ unsigned long clock_rate;
+
+ clock_rate = clk_get_rate(event_in);
+
+ clock_count_per_tick = clock_rate / HZ;
+
+ s3c64xx_clock_event_device.mult = div_sc(clock_rate,
+ NSEC_PER_SEC, s3c64xx_clock_event_device.shift);
+ s3c64xx_clock_event_device.max_delta_ns =
+ clockevent_delta2ns(-1, &s3c64xx_clock_event_device);
+ s3c64xx_clock_event_device.min_delta_ns =
+ clockevent_delta2ns(1, &s3c64xx_clock_event_device);
+
+ s3c64xx_clock_event_device.cpumask = cpumask_of(0);
+ clockevents_register_device(&s3c64xx_clock_event_device);
+
+ setup_irq(IRQ_TIMER3, &s3c64xx_clock_event_irq);
+}
+
+/*
+ * Clock source
+ */
+
+static struct clk *source_in;
+static struct clk *source_div;
+
+static cycle_t s3c64xx_clocksource_read(struct clocksource *cs)
+{
+ return (cycle_t) ~__raw_readl(S3C2410_TCNTO(PWM_SOURCE));
+}
+
+static void s3c64xx_clocksource_resume(struct clocksource *cs)
+{
+ unsigned long pclk;
+ struct clk *tscaler;
+
+ pclk = clk_get_rate(timerclk);
+ tscaler = clk_get_parent(source_div);
+ clk_set_rate(tscaler, pclk / 3);
+
+ clk_set_rate(source_div, pclk / 6);
+ clk_set_parent(source_in, source_div);
+
+ s3c64xx_pwm_init(PWM_SOURCE, TCNT_MAX);
+ s3c64xx_pwm_start(PWM_SOURCE, PERIODIC);
+}
+
+static struct clocksource pwm_clocksource = {
+ .name = "s3c64xx_clksrc",
+ .rating = 250,
+ .read = s3c64xx_clocksource_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .resume = s3c64xx_clocksource_resume,
+};
+
+static void __init s3c64xx_clocksource_init(void)
+{
+ unsigned long clock_rate;
+
+ clock_rate = clk_get_rate(source_in);
+
+ s3c64xx_pwm_init(PWM_SOURCE, TCNT_MAX);
+ s3c64xx_pwm_start(PWM_SOURCE, PERIODIC);
+
+ if (clocksource_register_hz(&pwm_clocksource, clock_rate))
+ panic("%s: can't register clocksource\n", pwm_clocksource.name);
+}
+
+static void __init s3c64xx_timer_init_common(void)
+{
+ struct platform_device tmpdev;
+ unsigned long pclk;
+ struct clk *tscaler;
+
+ tmpdev.dev.bus = &platform_bus_type;
+
+ timerclk = clk_get(NULL, "timers");
+ if (IS_ERR(timerclk))
+ panic("failed to get timers clock for system timer");
+
+ tmpdev.id = PWM_EVENT;
+ event_in = clk_get(&tmpdev.dev, "pwm-tin");
+ if (IS_ERR(event_in))
+ panic("failed to get pwm-event_in clock for system timer");
+
+ event_div = clk_get(&tmpdev.dev, "pwm-tdiv");
+ if (IS_ERR(event_div))
+ panic("failed to get pwm-event_div clock for system timer");
+
+ tmpdev.id = PWM_SOURCE;
+ source_in = clk_get(&tmpdev.dev, "pwm-tin");
+ if (IS_ERR(source_in))
+ panic("failed to get pwm-source_in clock for system timer");
+
+ source_div = clk_get(&tmpdev.dev, "pwm-tdiv");
+ if (IS_ERR(source_div))
+ panic("failed to get pwm-source_div clock for system timer");
+
+ pclk = clk_get_rate(timerclk);
+ tscaler = clk_get_parent(event_div);
+ clk_set_rate(tscaler, pclk / 3);
+
+ clk_set_rate(event_div, pclk / 6);
+ clk_set_parent(event_in, event_div);
+
+ clk_set_rate(source_div, pclk / 6);
+ clk_set_parent(source_in, source_div);
+
+ clk_enable(timerclk);
+ clk_enable(event_in);
+ clk_enable(source_in);
+}
+
+static void __init s3c64xx_timer_init(void)
+{
+ s3c64xx_timer_init_common();
+ s3c64xx_clock_event_init();
+ s3c64xx_clocksource_init();
+}
+
+struct sys_timer s3c64xx_timer = {
+ .init = s3c64xx_timer_init,
+};
diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-samsung/include/plat/cpu.h
index c0a5741..e1fcad0 100644
--- a/arch/arm/plat-samsung/include/plat/cpu.h
+++ b/arch/arm/plat-samsung/include/plat/cpu.h
@@ -74,6 +74,13 @@ extern struct syscore_ops s3c2416_pm_syscore_ops;
extern struct syscore_ops s3c244x_pm_syscore_ops;
extern struct syscore_ops s3c64xx_irq_syscore_ops;
+/* timer for 64xx */
+#ifdef CONFIG_S3C64XX_GENERIC_CLOCKEVENTS
+extern struct sys_timer s3c64xx_timer;
+#else
+#define s3c64xx_timer s3c24xx_timer
+#endif
+
/* system device classes */
extern struct sysdev_class s3c2410_sysclass;
--
1.7.6.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers.
2011-08-31 12:33 [PATCH 0/5] Add high resolution generic time support for S3C64xx Tomasz Figa
@ 2011-08-31 12:34 ` Tomasz Figa
2011-10-10 9:57 ` Kukjin Kim
2011-10-13 14:22 ` Russell King - ARM Linux
0 siblings, 2 replies; 5+ messages in thread
From: Tomasz Figa @ 2011-08-31 12:34 UTC (permalink / raw)
To: linux-arm-kernel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers.
2011-08-31 12:34 ` [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers Tomasz Figa
@ 2011-10-10 9:57 ` Kukjin Kim
2011-10-11 13:32 ` Tomasz Figa
2011-10-13 14:22 ` Russell King - ARM Linux
1 sibling, 1 reply; 5+ messages in thread
From: Kukjin Kim @ 2011-10-10 9:57 UTC (permalink / raw)
To: linux-arm-kernel
Tomasz Figa wrote:
>
> From d1fa581f09d8f8a3cc24d05420e9e65112fd8c12 Mon Sep 17 00:00:00 2001
> From: Tomasz Figa <tomasz.figa@gmail.com>
> Date: Sun, 28 Aug 2011 02:45:06 +0200
> Subject: [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time
support
> using PWM timers.
>
> This patch adds a sys_timer implementing generic clock source and clock
event
> device on S3C64xx using PWM timers 3 and 4. It can be enabled by a Kconfig
> option, based on which either the new sys_timer is used as s3c64xx_timer
or
> s3c64xx_timer is defined to s3c24xx_timer which is the old tick timer.
>
> Signed-off-by: Tomasz Figa <tomasz.figa@gmail.com>
> ---
> arch/arm/Kconfig | 1 -
> arch/arm/mach-s3c64xx/Kconfig | 12 ++
> arch/arm/mach-s3c64xx/Makefile | 3 +
> arch/arm/mach-s3c64xx/time.c | 320
> ++++++++++++++++++++++++++++++
> arch/arm/plat-samsung/include/plat/cpu.h | 7 +
> 5 files changed, 342 insertions(+), 1 deletions(-)
> create mode 100644 arch/arm/mach-s3c64xx/time.c
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 9adc278..05941f6 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -700,7 +700,6 @@ config ARCH_S3C64XX
> select ARM_VIC
> select HAVE_CLK
> select NO_IOPORT
> - select ARCH_USES_GETTIMEOFFSET
> select ARCH_HAS_CPUFREQ
> select ARCH_REQUIRE_GPIOLIB
> select SAMSUNG_CLKSRC
> diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig
> index e4177e2..2db76fd 100644
> --- a/arch/arm/mach-s3c64xx/Kconfig
> +++ b/arch/arm/mach-s3c64xx/Kconfig
> @@ -25,6 +25,18 @@ config CPU_S3C6410
> help
> Enable S3C6410 CPU support
>
> +config S3C64XX_GENERIC_CLOCKEVENTS
> + bool "Enable high resolution generic time support using PWM timers"
> + select GENERIC_CLOCKEVENTS
> + help
> + This option enables high resolution generic time support
> + on S3C6400/S3C6410 SoCs using PWM timers 3 and 4 instead
> + of standard periodic tick using PWM timer 4.
> +
> +config S3C64XX_ARCH_USES_GETTIMEOFFSET
> + def_bool y if !S3C64XX_GENERIC_CLOCKEVENTS
> + select ARCH_USES_GETTIMEOFFSET
> +
> config S3C64XX_DMA
> bool "S3C64XX DMA"
> select S3C_DMA
> diff --git a/arch/arm/mach-s3c64xx/Makefile
b/arch/arm/mach-s3c64xx/Makefile
> index 4657363..a152002 100644
> --- a/arch/arm/mach-s3c64xx/Makefile
> +++ b/arch/arm/mach-s3c64xx/Makefile
> @@ -15,6 +15,9 @@ obj-y += cpu.o
> obj-y += clock.o
> obj-y += gpiolib.o
>
> +# Generic clockevents
> +obj-$(CONFIG_S3C64XX_GENERIC_CLOCKEVENTS) += time.o
> +
> # Core support for S3C6400 system
>
> obj-$(CONFIG_CPU_S3C6400) += s3c6400.o
> diff --git a/arch/arm/mach-s3c64xx/time.c b/arch/arm/mach-s3c64xx/time.c
> new file mode 100644
> index 0000000..4fa57f0
> --- /dev/null
> +++ b/arch/arm/mach-s3c64xx/time.c
> @@ -0,0 +1,320 @@
> +/* linux/arch/arm/mach-s3c64xx/time.c
> + *
> + * Copyright (c) 2011 Tomasz Figa <tomasz.figa@gmail.com>
> + *
> + * based on linux/arch/arm/plat-samsung/s5p-time.c
> + *
> + * S3C64XX generic high resolution time support using PWM 3 and PWM 4
timers.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Notes:
> + * - Frequency divisors are currently hardcoded to make both timers
operate
> + * at 1/6 the frequency of PCLK, so with the usual PCLK frequency of 66
MHz,
> + * the timers are clocked at 11 MHz giving us the operating range from
> + * 90 nsec to 386 sec.
> + * - PWM registers, especially TCON, are shared with PWM driver, but
since
> + * all the clock event callbacks are run in atomic context
synchronization
> + * is not needed.
> + */
> +
> +#include <linux/sched.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/clockchips.h>
> +#include <linux/platform_device.h>
> +
> +#include <mach/map.h>
> +#include <plat/regs-timer.h>
> +#include <asm/mach/time.h>
> +
> +#define PWM_EVENT (3)
> +#define PWM_SOURCE (4)
> +
> +#define ONESHOT (0)
> +#define PERIODIC (1)
> +
> +#define TCNT_MAX (0xffffffff)
> +
> +static struct clk *timerclk;
> +
> +/*
> + * PWM timers setup code
> + */
> +
> +static inline void s3c64xx_pwm_stop(unsigned int pwm_id)
> +{
> + unsigned long tcon = __raw_readl(S3C2410_TCON);
> +
> + switch (pwm_id) {
> + case 3:
> + tcon &= ~S3C2410_TCON_T3START;
> + break;
> + case 4:
> + tcon &= ~S3C2410_TCON_T4START;
> + break;
> + }
> +
> + __raw_writel(tcon, S3C2410_TCON);
> +}
> +
> +static inline void s3c64xx_pwm_init(unsigned int pwm_id, unsigned long
tcnt)
> +{
> + unsigned long tcon = __raw_readl(S3C2410_TCON);
> +
> + /* timers reload after counting zero, so reduce the count by 1 */
> + --tcnt;
> +
> + /* stop the timer and ask it to load the new value */
> + switch (pwm_id) {
> + case 3:
> + tcon &= ~(0xf<<16);
> + tcon |= S3C2410_TCON_T3MANUALUPD;
> + break;
> + case 4:
> + tcon &= ~(7<<20);
> + tcon |= S3C2410_TCON_T4MANUALUPD;
> + break;
> + }
> +
> + __raw_writel(tcnt, S3C2410_TCNTB(pwm_id));
> + __raw_writel(tcnt, S3C2410_TCMPB(pwm_id));
> + __raw_writel(tcon, S3C2410_TCON);
> +}
> +
> +static inline void s3c64xx_pwm_start(unsigned int pwm_id, bool periodic)
> +{
> + unsigned long tcon = __raw_readl(S3C2410_TCON);
> +
> + switch (pwm_id) {
> + case 3:
> + tcon |= S3C2410_TCON_T3START;
> + tcon &= ~S3C2410_TCON_T3MANUALUPD;
> +
> + if (periodic)
> + tcon |= S3C2410_TCON_T3RELOAD;
> + else
> + tcon &= ~S3C2410_TCON_T3RELOAD;
> + break;
> + case 4:
> + tcon |= S3C2410_TCON_T4START;
> + tcon &= ~S3C2410_TCON_T4MANUALUPD;
> +
> + if (periodic)
> + tcon |= S3C2410_TCON_T4RELOAD;
> + else
> + tcon &= ~S3C2410_TCON_T4RELOAD;
> + break;
> + }
> +
> + __raw_writel(tcon, S3C2410_TCON);
> +}
> +
> +/*
> + * Clock event
> + */
> +
> +static struct clk *event_in;
> +static struct clk *event_div;
> +
> +static unsigned long clock_count_per_tick;
> +
> +static int s3c64xx_clock_event_set_next_event(unsigned long cycles,
> + struct clock_event_device
> *evt)
> +{
> + s3c64xx_pwm_init(PWM_EVENT, cycles);
> + s3c64xx_pwm_start(PWM_EVENT, ONESHOT);
> + return 0;
> +}
> +
> +static void s3c64xx_clock_event_resume(void)
> +{
> + unsigned long pclk;
> + struct clk *tscaler;
> +
> + pclk = clk_get_rate(timerclk);
> + tscaler = clk_get_parent(event_div);
> + clk_set_rate(tscaler, pclk / 3);
> +
> + clk_set_rate(event_div, pclk / 6);
> + clk_set_parent(event_in, event_div);
> +
> + s3c64xx_pwm_init(PWM_EVENT, clock_count_per_tick);
> + s3c64xx_pwm_start(PWM_EVENT, PERIODIC);
> +}
> +
> +static void s3c64xx_clock_event_set_mode(enum clock_event_mode mode,
> + struct clock_event_device
> *evt)
> +{
> + s3c64xx_pwm_stop(PWM_EVENT);
> +
> + switch (mode) {
> + case CLOCK_EVT_MODE_PERIODIC:
> + s3c64xx_pwm_init(PWM_EVENT, clock_count_per_tick);
> + s3c64xx_pwm_start(PWM_EVENT, PERIODIC);
> + break;
> + case CLOCK_EVT_MODE_ONESHOT:
> + break;
> + case CLOCK_EVT_MODE_UNUSED:
> + case CLOCK_EVT_MODE_SHUTDOWN:
> + break;
> + case CLOCK_EVT_MODE_RESUME:
> + s3c64xx_clock_event_resume();
> + break;
> + }
> +}
> +
> +static struct clock_event_device s3c64xx_clock_event_device = {
> + .name = "s3c64xx_clkevt",
> + .features = CLOCK_EVT_FEAT_PERIODIC |
> CLOCK_EVT_FEAT_ONESHOT,
> + .rating = 200,
> + .shift = 32,
> + .set_next_event = s3c64xx_clock_event_set_next_event,
> + .set_mode = s3c64xx_clock_event_set_mode,
> +};
> +
> +static irqreturn_t s3c64xx_clock_event_isr(int irq, void *dev_id)
> +{
> + struct clock_event_device *evt = &s3c64xx_clock_event_device;
> +
> + evt->event_handler(evt);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static struct irqaction s3c64xx_clock_event_irq = {
> + .name = "s3c64xx_clkevt_irq",
> + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
> + .handler = s3c64xx_clock_event_isr,
> +};
> +
> +static void __init s3c64xx_clock_event_init(void)
> +{
> + unsigned long clock_rate;
> +
> + clock_rate = clk_get_rate(event_in);
> +
> + clock_count_per_tick = clock_rate / HZ;
> +
> + s3c64xx_clock_event_device.mult = div_sc(clock_rate,
> + NSEC_PER_SEC, s3c64xx_clock_event_device.shift);
> + s3c64xx_clock_event_device.max_delta_ns =
> + clockevent_delta2ns(-1,
&s3c64xx_clock_event_device);
> + s3c64xx_clock_event_device.min_delta_ns =
> + clockevent_delta2ns(1, &s3c64xx_clock_event_device);
> +
> + s3c64xx_clock_event_device.cpumask = cpumask_of(0);
> + clockevents_register_device(&s3c64xx_clock_event_device);
> +
> + setup_irq(IRQ_TIMER3, &s3c64xx_clock_event_irq);
> +}
> +
> +/*
> + * Clock source
> + */
> +
> +static struct clk *source_in;
> +static struct clk *source_div;
> +
> +static cycle_t s3c64xx_clocksource_read(struct clocksource *cs)
> +{
> + return (cycle_t) ~__raw_readl(S3C2410_TCNTO(PWM_SOURCE));
> +}
> +
> +static void s3c64xx_clocksource_resume(struct clocksource *cs)
> +{
> + unsigned long pclk;
> + struct clk *tscaler;
> +
> + pclk = clk_get_rate(timerclk);
> + tscaler = clk_get_parent(source_div);
> + clk_set_rate(tscaler, pclk / 3);
> +
> + clk_set_rate(source_div, pclk / 6);
> + clk_set_parent(source_in, source_div);
> +
> + s3c64xx_pwm_init(PWM_SOURCE, TCNT_MAX);
> + s3c64xx_pwm_start(PWM_SOURCE, PERIODIC);
> +}
> +
> +static struct clocksource pwm_clocksource = {
> + .name = "s3c64xx_clksrc",
> + .rating = 250,
> + .read = s3c64xx_clocksource_read,
> + .mask = CLOCKSOURCE_MASK(32),
> + .flags = CLOCK_SOURCE_IS_CONTINUOUS,
> + .resume = s3c64xx_clocksource_resume,
> +};
> +
> +static void __init s3c64xx_clocksource_init(void)
> +{
> + unsigned long clock_rate;
> +
> + clock_rate = clk_get_rate(source_in);
> +
> + s3c64xx_pwm_init(PWM_SOURCE, TCNT_MAX);
> + s3c64xx_pwm_start(PWM_SOURCE, PERIODIC);
> +
> + if (clocksource_register_hz(&pwm_clocksource, clock_rate))
> + panic("%s: can't register clocksource\n",
> pwm_clocksource.name);
> +}
> +
> +static void __init s3c64xx_timer_init_common(void)
> +{
> + struct platform_device tmpdev;
> + unsigned long pclk;
> + struct clk *tscaler;
> +
> + tmpdev.dev.bus = &platform_bus_type;
> +
> + timerclk = clk_get(NULL, "timers");
> + if (IS_ERR(timerclk))
> + panic("failed to get timers clock for system timer");
> +
> + tmpdev.id = PWM_EVENT;
> + event_in = clk_get(&tmpdev.dev, "pwm-tin");
> + if (IS_ERR(event_in))
> + panic("failed to get pwm-event_in clock for system timer");
> +
> + event_div = clk_get(&tmpdev.dev, "pwm-tdiv");
> + if (IS_ERR(event_div))
> + panic("failed to get pwm-event_div clock for system timer");
> +
> + tmpdev.id = PWM_SOURCE;
> + source_in = clk_get(&tmpdev.dev, "pwm-tin");
> + if (IS_ERR(source_in))
> + panic("failed to get pwm-source_in clock for system timer");
> +
> + source_div = clk_get(&tmpdev.dev, "pwm-tdiv");
> + if (IS_ERR(source_div))
> + panic("failed to get pwm-source_div clock for system
timer");
> +
> + pclk = clk_get_rate(timerclk);
> + tscaler = clk_get_parent(event_div);
> + clk_set_rate(tscaler, pclk / 3);
> +
> + clk_set_rate(event_div, pclk / 6);
> + clk_set_parent(event_in, event_div);
> +
> + clk_set_rate(source_div, pclk / 6);
> + clk_set_parent(source_in, source_div);
> +
> + clk_enable(timerclk);
> + clk_enable(event_in);
> + clk_enable(source_in);
> +}
> +
> +static void __init s3c64xx_timer_init(void)
> +{
> + s3c64xx_timer_init_common();
> + s3c64xx_clock_event_init();
> + s3c64xx_clocksource_init();
> +}
> +
> +struct sys_timer s3c64xx_timer = {
> + .init = s3c64xx_timer_init,
> +};
> diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-
> samsung/include/plat/cpu.h
> index c0a5741..e1fcad0 100644
> --- a/arch/arm/plat-samsung/include/plat/cpu.h
> +++ b/arch/arm/plat-samsung/include/plat/cpu.h
> @@ -74,6 +74,13 @@ extern struct syscore_ops s3c2416_pm_syscore_ops;
> extern struct syscore_ops s3c244x_pm_syscore_ops;
> extern struct syscore_ops s3c64xx_irq_syscore_ops;
>
> +/* timer for 64xx */
> +#ifdef CONFIG_S3C64XX_GENERIC_CLOCKEVENTS
> +extern struct sys_timer s3c64xx_timer;
> +#else
> +#define s3c64xx_timer s3c24xx_timer
> +#endif
> +
> /* system device classes */
>
> extern struct sysdev_class s3c2410_sysclass;
> --
> 1.7.6.1
Hi Tomasz Figa,
Sorry for late response, probably I'm missing this......and I couldn't
review this in detail :(
But as you know above mach-s3c64xx/time.c is similar with
plat-s5p/s5p-time.c so I'd prefer that you could consolidate them,
samsung_timer into plat-samsung directory. In addition, if you need to
implement SoC specific feature, you can use soc_is_xxxx() like
soc_is_s3c64xx() there.
If any problems, please let me know.
Thanks.
Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers.
2011-10-10 9:57 ` Kukjin Kim
@ 2011-10-11 13:32 ` Tomasz Figa
0 siblings, 0 replies; 5+ messages in thread
From: Tomasz Figa @ 2011-10-11 13:32 UTC (permalink / raw)
To: linux-arm-kernel
On Monday 10 of October 2011 at 18:57:58, Kukjin Kim wrote:
> Tomasz Figa wrote:
> >
> > From d1fa581f09d8f8a3cc24d05420e9e65112fd8c12 Mon Sep 17 00:00:00 2001
> > From: Tomasz Figa <tomasz.figa@gmail.com>
> > Date: Sun, 28 Aug 2011 02:45:06 +0200
> > Subject: [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time
> support
> > using PWM timers.
> >
> > This patch adds a sys_timer implementing generic clock source and clock
> event
> > device on S3C64xx using PWM timers 3 and 4. It can be enabled by a Kconfig
> > option, based on which either the new sys_timer is used as s3c64xx_timer
> or
> > s3c64xx_timer is defined to s3c24xx_timer which is the old tick timer.
> >
> > Signed-off-by: Tomasz Figa <tomasz.figa@gmail.com>
> > ---
> > arch/arm/Kconfig | 1 -
> > arch/arm/mach-s3c64xx/Kconfig | 12 ++
> > arch/arm/mach-s3c64xx/Makefile | 3 +
> > arch/arm/mach-s3c64xx/time.c | 320
> > ++++++++++++++++++++++++++++++
> > arch/arm/plat-samsung/include/plat/cpu.h | 7 +
> > 5 files changed, 342 insertions(+), 1 deletions(-)
> > create mode 100644 arch/arm/mach-s3c64xx/time.c
> >
> > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> > index 9adc278..05941f6 100644
> > --- a/arch/arm/Kconfig
> > +++ b/arch/arm/Kconfig
> > @@ -700,7 +700,6 @@ config ARCH_S3C64XX
> > select ARM_VIC
> > select HAVE_CLK
> > select NO_IOPORT
> > - select ARCH_USES_GETTIMEOFFSET
> > select ARCH_HAS_CPUFREQ
> > select ARCH_REQUIRE_GPIOLIB
> > select SAMSUNG_CLKSRC
> > diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig
> > index e4177e2..2db76fd 100644
> > --- a/arch/arm/mach-s3c64xx/Kconfig
> > +++ b/arch/arm/mach-s3c64xx/Kconfig
> > @@ -25,6 +25,18 @@ config CPU_S3C6410
> > help
> > Enable S3C6410 CPU support
> >
> > +config S3C64XX_GENERIC_CLOCKEVENTS
> > + bool "Enable high resolution generic time support using PWM timers"
> > + select GENERIC_CLOCKEVENTS
> > + help
> > + This option enables high resolution generic time support
> > + on S3C6400/S3C6410 SoCs using PWM timers 3 and 4 instead
> > + of standard periodic tick using PWM timer 4.
> > +
> > +config S3C64XX_ARCH_USES_GETTIMEOFFSET
> > + def_bool y if !S3C64XX_GENERIC_CLOCKEVENTS
> > + select ARCH_USES_GETTIMEOFFSET
> > +
> > config S3C64XX_DMA
> > bool "S3C64XX DMA"
> > select S3C_DMA
> > diff --git a/arch/arm/mach-s3c64xx/Makefile
> b/arch/arm/mach-s3c64xx/Makefile
> > index 4657363..a152002 100644
> > --- a/arch/arm/mach-s3c64xx/Makefile
> > +++ b/arch/arm/mach-s3c64xx/Makefile
> > @@ -15,6 +15,9 @@ obj-y += cpu.o
> > obj-y += clock.o
> > obj-y += gpiolib.o
> >
> > +# Generic clockevents
> > +obj-$(CONFIG_S3C64XX_GENERIC_CLOCKEVENTS) += time.o
> > +
> > # Core support for S3C6400 system
> >
> > obj-$(CONFIG_CPU_S3C6400) += s3c6400.o
> > diff --git a/arch/arm/mach-s3c64xx/time.c b/arch/arm/mach-s3c64xx/time.c
> > new file mode 100644
> > index 0000000..4fa57f0
> > --- /dev/null
> > +++ b/arch/arm/mach-s3c64xx/time.c
> > @@ -0,0 +1,320 @@
> > +/* linux/arch/arm/mach-s3c64xx/time.c
> > + *
> > + * Copyright (c) 2011 Tomasz Figa <tomasz.figa@gmail.com>
> > + *
> > + * based on linux/arch/arm/plat-samsung/s5p-time.c
> > + *
> > + * S3C64XX generic high resolution time support using PWM 3 and PWM 4
> timers.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * Notes:
> > + * - Frequency divisors are currently hardcoded to make both timers
> operate
> > + * at 1/6 the frequency of PCLK, so with the usual PCLK frequency of 66
> MHz,
> > + * the timers are clocked at 11 MHz giving us the operating range from
> > + * 90 nsec to 386 sec.
> > + * - PWM registers, especially TCON, are shared with PWM driver, but
> since
> > + * all the clock event callbacks are run in atomic context
> synchronization
> > + * is not needed.
> > + */
> > +
> > +#include <linux/sched.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/err.h>
> > +#include <linux/clk.h>
> > +#include <linux/clockchips.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include <mach/map.h>
> > +#include <plat/regs-timer.h>
> > +#include <asm/mach/time.h>
> > +
> > +#define PWM_EVENT (3)
> > +#define PWM_SOURCE (4)
> > +
> > +#define ONESHOT (0)
> > +#define PERIODIC (1)
> > +
> > +#define TCNT_MAX (0xffffffff)
> > +
> > +static struct clk *timerclk;
> > +
> > +/*
> > + * PWM timers setup code
> > + */
> > +
> > +static inline void s3c64xx_pwm_stop(unsigned int pwm_id)
> > +{
> > + unsigned long tcon = __raw_readl(S3C2410_TCON);
> > +
> > + switch (pwm_id) {
> > + case 3:
> > + tcon &= ~S3C2410_TCON_T3START;
> > + break;
> > + case 4:
> > + tcon &= ~S3C2410_TCON_T4START;
> > + break;
> > + }
> > +
> > + __raw_writel(tcon, S3C2410_TCON);
> > +}
> > +
> > +static inline void s3c64xx_pwm_init(unsigned int pwm_id, unsigned long
> tcnt)
> > +{
> > + unsigned long tcon = __raw_readl(S3C2410_TCON);
> > +
> > + /* timers reload after counting zero, so reduce the count by 1 */
> > + --tcnt;
> > +
> > + /* stop the timer and ask it to load the new value */
> > + switch (pwm_id) {
> > + case 3:
> > + tcon &= ~(0xf<<16);
> > + tcon |= S3C2410_TCON_T3MANUALUPD;
> > + break;
> > + case 4:
> > + tcon &= ~(7<<20);
> > + tcon |= S3C2410_TCON_T4MANUALUPD;
> > + break;
> > + }
> > +
> > + __raw_writel(tcnt, S3C2410_TCNTB(pwm_id));
> > + __raw_writel(tcnt, S3C2410_TCMPB(pwm_id));
> > + __raw_writel(tcon, S3C2410_TCON);
> > +}
> > +
> > +static inline void s3c64xx_pwm_start(unsigned int pwm_id, bool periodic)
> > +{
> > + unsigned long tcon = __raw_readl(S3C2410_TCON);
> > +
> > + switch (pwm_id) {
> > + case 3:
> > + tcon |= S3C2410_TCON_T3START;
> > + tcon &= ~S3C2410_TCON_T3MANUALUPD;
> > +
> > + if (periodic)
> > + tcon |= S3C2410_TCON_T3RELOAD;
> > + else
> > + tcon &= ~S3C2410_TCON_T3RELOAD;
> > + break;
> > + case 4:
> > + tcon |= S3C2410_TCON_T4START;
> > + tcon &= ~S3C2410_TCON_T4MANUALUPD;
> > +
> > + if (periodic)
> > + tcon |= S3C2410_TCON_T4RELOAD;
> > + else
> > + tcon &= ~S3C2410_TCON_T4RELOAD;
> > + break;
> > + }
> > +
> > + __raw_writel(tcon, S3C2410_TCON);
> > +}
> > +
> > +/*
> > + * Clock event
> > + */
> > +
> > +static struct clk *event_in;
> > +static struct clk *event_div;
> > +
> > +static unsigned long clock_count_per_tick;
> > +
> > +static int s3c64xx_clock_event_set_next_event(unsigned long cycles,
> > + struct clock_event_device
> > *evt)
> > +{
> > + s3c64xx_pwm_init(PWM_EVENT, cycles);
> > + s3c64xx_pwm_start(PWM_EVENT, ONESHOT);
> > + return 0;
> > +}
> > +
> > +static void s3c64xx_clock_event_resume(void)
> > +{
> > + unsigned long pclk;
> > + struct clk *tscaler;
> > +
> > + pclk = clk_get_rate(timerclk);
> > + tscaler = clk_get_parent(event_div);
> > + clk_set_rate(tscaler, pclk / 3);
> > +
> > + clk_set_rate(event_div, pclk / 6);
> > + clk_set_parent(event_in, event_div);
> > +
> > + s3c64xx_pwm_init(PWM_EVENT, clock_count_per_tick);
> > + s3c64xx_pwm_start(PWM_EVENT, PERIODIC);
> > +}
> > +
> > +static void s3c64xx_clock_event_set_mode(enum clock_event_mode mode,
> > + struct clock_event_device
> > *evt)
> > +{
> > + s3c64xx_pwm_stop(PWM_EVENT);
> > +
> > + switch (mode) {
> > + case CLOCK_EVT_MODE_PERIODIC:
> > + s3c64xx_pwm_init(PWM_EVENT, clock_count_per_tick);
> > + s3c64xx_pwm_start(PWM_EVENT, PERIODIC);
> > + break;
> > + case CLOCK_EVT_MODE_ONESHOT:
> > + break;
> > + case CLOCK_EVT_MODE_UNUSED:
> > + case CLOCK_EVT_MODE_SHUTDOWN:
> > + break;
> > + case CLOCK_EVT_MODE_RESUME:
> > + s3c64xx_clock_event_resume();
> > + break;
> > + }
> > +}
> > +
> > +static struct clock_event_device s3c64xx_clock_event_device = {
> > + .name = "s3c64xx_clkevt",
> > + .features = CLOCK_EVT_FEAT_PERIODIC |
> > CLOCK_EVT_FEAT_ONESHOT,
> > + .rating = 200,
> > + .shift = 32,
> > + .set_next_event = s3c64xx_clock_event_set_next_event,
> > + .set_mode = s3c64xx_clock_event_set_mode,
> > +};
> > +
> > +static irqreturn_t s3c64xx_clock_event_isr(int irq, void *dev_id)
> > +{
> > + struct clock_event_device *evt = &s3c64xx_clock_event_device;
> > +
> > + evt->event_handler(evt);
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static struct irqaction s3c64xx_clock_event_irq = {
> > + .name = "s3c64xx_clkevt_irq",
> > + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
> > + .handler = s3c64xx_clock_event_isr,
> > +};
> > +
> > +static void __init s3c64xx_clock_event_init(void)
> > +{
> > + unsigned long clock_rate;
> > +
> > + clock_rate = clk_get_rate(event_in);
> > +
> > + clock_count_per_tick = clock_rate / HZ;
> > +
> > + s3c64xx_clock_event_device.mult = div_sc(clock_rate,
> > + NSEC_PER_SEC, s3c64xx_clock_event_device.shift);
> > + s3c64xx_clock_event_device.max_delta_ns =
> > + clockevent_delta2ns(-1,
> &s3c64xx_clock_event_device);
> > + s3c64xx_clock_event_device.min_delta_ns =
> > + clockevent_delta2ns(1, &s3c64xx_clock_event_device);
> > +
> > + s3c64xx_clock_event_device.cpumask = cpumask_of(0);
> > + clockevents_register_device(&s3c64xx_clock_event_device);
> > +
> > + setup_irq(IRQ_TIMER3, &s3c64xx_clock_event_irq);
> > +}
> > +
> > +/*
> > + * Clock source
> > + */
> > +
> > +static struct clk *source_in;
> > +static struct clk *source_div;
> > +
> > +static cycle_t s3c64xx_clocksource_read(struct clocksource *cs)
> > +{
> > + return (cycle_t) ~__raw_readl(S3C2410_TCNTO(PWM_SOURCE));
> > +}
> > +
> > +static void s3c64xx_clocksource_resume(struct clocksource *cs)
> > +{
> > + unsigned long pclk;
> > + struct clk *tscaler;
> > +
> > + pclk = clk_get_rate(timerclk);
> > + tscaler = clk_get_parent(source_div);
> > + clk_set_rate(tscaler, pclk / 3);
> > +
> > + clk_set_rate(source_div, pclk / 6);
> > + clk_set_parent(source_in, source_div);
> > +
> > + s3c64xx_pwm_init(PWM_SOURCE, TCNT_MAX);
> > + s3c64xx_pwm_start(PWM_SOURCE, PERIODIC);
> > +}
> > +
> > +static struct clocksource pwm_clocksource = {
> > + .name = "s3c64xx_clksrc",
> > + .rating = 250,
> > + .read = s3c64xx_clocksource_read,
> > + .mask = CLOCKSOURCE_MASK(32),
> > + .flags = CLOCK_SOURCE_IS_CONTINUOUS,
> > + .resume = s3c64xx_clocksource_resume,
> > +};
> > +
> > +static void __init s3c64xx_clocksource_init(void)
> > +{
> > + unsigned long clock_rate;
> > +
> > + clock_rate = clk_get_rate(source_in);
> > +
> > + s3c64xx_pwm_init(PWM_SOURCE, TCNT_MAX);
> > + s3c64xx_pwm_start(PWM_SOURCE, PERIODIC);
> > +
> > + if (clocksource_register_hz(&pwm_clocksource, clock_rate))
> > + panic("%s: can't register clocksource\n",
> > pwm_clocksource.name);
> > +}
> > +
> > +static void __init s3c64xx_timer_init_common(void)
> > +{
> > + struct platform_device tmpdev;
> > + unsigned long pclk;
> > + struct clk *tscaler;
> > +
> > + tmpdev.dev.bus = &platform_bus_type;
> > +
> > + timerclk = clk_get(NULL, "timers");
> > + if (IS_ERR(timerclk))
> > + panic("failed to get timers clock for system timer");
> > +
> > + tmpdev.id = PWM_EVENT;
> > + event_in = clk_get(&tmpdev.dev, "pwm-tin");
> > + if (IS_ERR(event_in))
> > + panic("failed to get pwm-event_in clock for system timer");
> > +
> > + event_div = clk_get(&tmpdev.dev, "pwm-tdiv");
> > + if (IS_ERR(event_div))
> > + panic("failed to get pwm-event_div clock for system timer");
> > +
> > + tmpdev.id = PWM_SOURCE;
> > + source_in = clk_get(&tmpdev.dev, "pwm-tin");
> > + if (IS_ERR(source_in))
> > + panic("failed to get pwm-source_in clock for system timer");
> > +
> > + source_div = clk_get(&tmpdev.dev, "pwm-tdiv");
> > + if (IS_ERR(source_div))
> > + panic("failed to get pwm-source_div clock for system
> timer");
> > +
> > + pclk = clk_get_rate(timerclk);
> > + tscaler = clk_get_parent(event_div);
> > + clk_set_rate(tscaler, pclk / 3);
> > +
> > + clk_set_rate(event_div, pclk / 6);
> > + clk_set_parent(event_in, event_div);
> > +
> > + clk_set_rate(source_div, pclk / 6);
> > + clk_set_parent(source_in, source_div);
> > +
> > + clk_enable(timerclk);
> > + clk_enable(event_in);
> > + clk_enable(source_in);
> > +}
> > +
> > +static void __init s3c64xx_timer_init(void)
> > +{
> > + s3c64xx_timer_init_common();
> > + s3c64xx_clock_event_init();
> > + s3c64xx_clocksource_init();
> > +}
> > +
> > +struct sys_timer s3c64xx_timer = {
> > + .init = s3c64xx_timer_init,
> > +};
> > diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-
> > samsung/include/plat/cpu.h
> > index c0a5741..e1fcad0 100644
> > --- a/arch/arm/plat-samsung/include/plat/cpu.h
> > +++ b/arch/arm/plat-samsung/include/plat/cpu.h
> > @@ -74,6 +74,13 @@ extern struct syscore_ops s3c2416_pm_syscore_ops;
> > extern struct syscore_ops s3c244x_pm_syscore_ops;
> > extern struct syscore_ops s3c64xx_irq_syscore_ops;
> >
> > +/* timer for 64xx */
> > +#ifdef CONFIG_S3C64XX_GENERIC_CLOCKEVENTS
> > +extern struct sys_timer s3c64xx_timer;
> > +#else
> > +#define s3c64xx_timer s3c24xx_timer
> > +#endif
> > +
> > /* system device classes */
> >
> > extern struct sysdev_class s3c2410_sysclass;
> > --
> > 1.7.6.1
>
> Hi Tomasz Figa,
>
> Sorry for late response, probably I'm missing this......and I couldn't
> review this in detail :(
Hi,
Same on my side, sorry for my absence on the list. I've been very busy, with
no time for mainline submissions.
> But as you know above mach-s3c64xx/time.c is similar with
> plat-s5p/s5p-time.c so I'd prefer that you could consolidate them,
> samsung_timer into plat-samsung directory. In addition, if you need to
> implement SoC specific feature, you can use soc_is_xxxx() like
> soc_is_s3c64xx() there.
OK, I will do that.
> If any problems, please let me know.
OK, thanks.
Best regards,
Tom
> Thanks.
>
> Best regards,
> Kgene.
> --
> Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
> SW Solution Development Team, Samsung Electronics Co., Ltd.
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers.
2011-08-31 12:34 ` [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers Tomasz Figa
2011-10-10 9:57 ` Kukjin Kim
@ 2011-10-13 14:22 ` Russell King - ARM Linux
1 sibling, 0 replies; 5+ messages in thread
From: Russell King - ARM Linux @ 2011-10-13 14:22 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Aug 31, 2011 at 02:34:52PM +0200, Tomasz Figa wrote:
> +/*
> + * Clock source
> + */
> +
> +static struct clk *source_in;
> +static struct clk *source_div;
> +
> +static cycle_t s3c64xx_clocksource_read(struct clocksource *cs)
> +{
> + return (cycle_t) ~__raw_readl(S3C2410_TCNTO(PWM_SOURCE));
> +}
This looks like a very simple MMIO clocksource... This looks the
same as clocksource_mmio_readl_down().
> +
> +static void s3c64xx_clocksource_resume(struct clocksource *cs)
> +{
> + unsigned long pclk;
> + struct clk *tscaler;
> +
> + pclk = clk_get_rate(timerclk);
> + tscaler = clk_get_parent(source_div);
> + clk_set_rate(tscaler, pclk / 3);
> +
> + clk_set_rate(source_div, pclk / 6);
> + clk_set_parent(source_in, source_div);
> +
> + s3c64xx_pwm_init(PWM_SOURCE, TCNT_MAX);
> + s3c64xx_pwm_start(PWM_SOURCE, PERIODIC);
> +}
> +
> +static struct clocksource pwm_clocksource = {
> + .name = "s3c64xx_clksrc",
> + .rating = 250,
> + .read = s3c64xx_clocksource_read,
> + .mask = CLOCKSOURCE_MASK(32),
> + .flags = CLOCK_SOURCE_IS_CONTINUOUS,
> + .resume = s3c64xx_clocksource_resume,
> +};
> +
> +static void __init s3c64xx_clocksource_init(void)
> +{
> + unsigned long clock_rate;
> +
> + clock_rate = clk_get_rate(source_in);
> +
> + s3c64xx_pwm_init(PWM_SOURCE, TCNT_MAX);
> + s3c64xx_pwm_start(PWM_SOURCE, PERIODIC);
> +
> + if (clocksource_register_hz(&pwm_clocksource, clock_rate))
> + panic("%s: can't register clocksource\n", pwm_clocksource.name);
Apart from the resume entry, this looks like it could use
drivers/clocksource/mmio.c - and so avoid yet another clocksource
creation.
Maybe adding some kind of resume support to mmio.c would be a good
idea?
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2011-10-13 14:22 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-08-28 0:45 [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers Tomasz Figa
-- strict thread matches above, loose matches on Subject: below --
2011-08-31 12:33 [PATCH 0/5] Add high resolution generic time support for S3C64xx Tomasz Figa
2011-08-31 12:34 ` [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers Tomasz Figa
2011-10-10 9:57 ` Kukjin Kim
2011-10-11 13:32 ` Tomasz Figa
2011-10-13 14:22 ` Russell King - ARM Linux
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).