public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
From: tomasz.figa@gmail.com (Tomasz Figa)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers.
Date: Tue, 11 Oct 2011 15:32:55 +0200	[thread overview]
Message-ID: <2566281.CQdhe8ms60@flatron> (raw)
In-Reply-To: <039d01cc8733$16c5ded0$44519c70$%kim@samsung.com>

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.
> 

  reply	other threads:[~2011-10-11 13:32 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-08-31 12:33 [PATCH 0/5] Add high resolution generic time support for S3C64xx Tomasz Figa
2011-08-31 12:34 ` [PATCH 1/5] ARM: Samsung: PWM: Allow to differentiate SoCs based on platform device name Tomasz Figa
2011-08-31 12:44   ` Mark Brown
2011-08-31 13:30     ` Tomasz Figa
2011-09-01  2:26       ` Kukjin Kim
2011-09-01  3:33         ` Kyungmin Park
2011-09-01 11:18           ` Tomasz Figa
2011-09-01 12:32             ` Mark Brown
2011-09-06 10:41             ` Tomasz Figa
2011-09-06 11:03               ` Russell King - ARM Linux
2011-09-06 11:35                 ` Tomasz Figa
2011-09-06 12:04                   ` Russell King - ARM Linux
2011-09-07 19:01                     ` Mark Brown
2011-10-10  9:58                       ` Kukjin Kim
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 [this message]
2011-10-13 14:22   ` Russell King - ARM Linux
2011-08-31 12:35 ` [PATCH 3/5] ARM: Samsung: pwm: Exclude timer 3 on S3C64xx if generic time is enabled Tomasz Figa
2011-08-31 12:35 ` [PATCH 4/5] ARM: s3c64xx: Move all machines to s3c64xx_timer Tomasz Figa
2011-08-31 12:36 ` [PATCH 5/5] ARM: s3c64xx: Add optional local sched_clock implementation Tomasz Figa
  -- strict thread matches above, loose matches on Subject: below --
2011-08-28  0:45 [PATCH 2/5] ARM: s3c64xx: Add generic high resolution time support using PWM timers Tomasz Figa

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2566281.CQdhe8ms60@flatron \
    --to=tomasz.figa@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox