From mboxrd@z Thu Jan 1 00:00:00 1970 From: Sangbeom Kim Subject: RE: [PATCH V2 1/3] ARM: S5P: Add s5p_timer support for HRT Date: Fri, 04 Mar 2011 16:06:34 +0900 Message-ID: <002501cbda3a$b6f37d00$24da7700$@com> References: <1299216099-7709-1-git-send-email-sbkim73@samsung.com> <1299216099-7709-2-git-send-email-sbkim73@samsung.com> Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: QUOTED-PRINTABLE Return-path: Received: from mailout4.samsung.com ([203.254.224.34]:26580 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751679Ab1CDHIY convert rfc822-to-8bit (ORCPT ); Fri, 4 Mar 2011 02:08:24 -0500 Received: from epmmp2 (mailout4.samsung.com [203.254.224.34]) by mailout4.samsung.com (Oracle Communications Messaging Exchange Server 7u4-19.01 64bit (built Sep 7 2010)) with ESMTP id <0LHI00AKEUF61K10@mailout4.samsung.com> for linux-samsung-soc@vger.kernel.org; Fri, 04 Mar 2011 16:06:42 +0900 (KST) Received: from DOSBKIM7301 ([12.23.103.65]) by mmp2.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTPA id <0LHI0096RUF6JP@mmp2.samsung.com> for linux-samsung-soc@vger.kernel.org; Fri, 04 Mar 2011 16:06:42 +0900 (KST) In-reply-to: Content-language: ko Sender: linux-samsung-soc-owner@vger.kernel.org List-Id: linux-samsung-soc@vger.kernel.org To: 'Kyungmin Park' Cc: kgene.kim@samsung.com, linux-samsung-soc@vger.kernel.org, ben-linux@fluff.org, linux-arm-kernel@lists.infradead.org Hi Kyungmin! I'm sorry, I don't test on exynos4 configuration. I will add S5P_HRT configuration in plat-s5p Kconfig. And It can be selected on each machine. After testing it, I will submit again. Thanks, S.B. Kim > On Friday, March 04, 2011 3:32 PM, Kyungmin Park wrote: > > This patch adds support HR-Timer(High Resolution Timer) and dynamic > > tick system for S5P SoCs. There are many clock sources for HR-Timer > > on S5P SoCs. The PWM timer, RTC, System Timer, and MCT can be used > > for clock source. > > This patch can only support PWM timer for clocksource of > > S5P64x0 and S5PV210. > > > > Signed-off-by: Sangbeom Kim > > --- > > =A0arch/arm/plat-s5p/Makefile =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =A0 = =A01 + > > =A0arch/arm/plat-s5p/include/plat/s5p-time.h | =A0 40 +++ > > =A0arch/arm/plat-s5p/s5p-time.c =A0 =A0 =A0 =A0 =A0 =A0 =A0| =A0452 > +++++++++++++++++++++++++++++ > > =A03 files changed, 493 insertions(+), 0 deletions(-) > > =A0create mode 100644 arch/arm/plat-s5p/include/plat/s5p-time.h > > =A0create mode 100644 arch/arm/plat-s5p/s5p-time.c > > > > diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefil= e > > index 4bd5cf9..3b8cc2f 100644 > > --- a/arch/arm/plat-s5p/Makefile > > +++ b/arch/arm/plat-s5p/Makefile > > @@ -22,6 +22,7 @@ obj-$(CONFIG_S5P_GPIO_INT) =A0 =A0+=3D irq-gpioin= t.o > > =A0obj-$(CONFIG_S5P_SYSTEM_MMU) =A0 +=3D sysmmu.o > > =A0obj-$(CONFIG_PM) =A0 =A0 =A0 =A0 =A0 =A0 =A0 +=3D pm.o > > =A0obj-$(CONFIG_PM) =A0 =A0 =A0 =A0 =A0 =A0 =A0 +=3D irq-pm.o > > +obj-$(CONFIG_GENERIC_CLOCKEVENTS) +=3D s5p-time.o >=20 > Do you test it with exynos4 configuration? it's affect the exynos com= pile. > First select SAMSUNG_DEV_PWM, it can't compile it since no > init_sched_clock > Second select HAVE_SCHED_CLOCK, it can't boot. >=20 > I used the for-exynos4 branch. >=20 > Thank you, > Kyungmin Park > > > > =A0# devices > > > > diff --git a/arch/arm/plat-s5p/include/plat/s5p-time.h b/arch/arm/p= lat- > s5p/include/plat/s5p-time.h > > new file mode 100644 > > index 0000000..9956136 > > --- /dev/null > > +++ b/arch/arm/plat-s5p/include/plat/s5p-time.h > > @@ -0,0 +1,40 @@ > > +/* linux/arch/arm/plat-s5p/include/plat/s5p-time.h > > + * > > + * Copyright 2011 Samsung Electronics Co., Ltd. > > + * =A0 =A0 =A0 =A0 =A0 =A0 http://www.samsung.com/ > > + * > > + * Header file for s5p time support > > + * > > + * This program is free software; you can redistribute it and/or m= odify > > + * it under the terms of the GNU General Public License version 2 = as > > + * published by the Free Software Foundation. > > +*/ > > + > > +#ifndef __ASM_PLAT_S5P_TIME_H > > +#define __ASM_PLAT_S5P_TIME_H __FILE__ > > + > > +/* S5P HR-Timer Clock mode */ > > +enum s5p_timer_mode { > > + =A0 =A0 =A0 S5P_PWM0, > > + =A0 =A0 =A0 S5P_PWM1, > > + =A0 =A0 =A0 S5P_PWM2, > > + =A0 =A0 =A0 S5P_PWM3, > > + =A0 =A0 =A0 S5P_PWM4, > > +}; > > + > > +struct s5p_timer_source { > > + =A0 =A0 =A0 unsigned int event_id; > > + =A0 =A0 =A0 unsigned int source_id; > > +}; > > + > > +/* Be able to sleep for atleast 4 seconds (usually more) */ > > +#define S5PTIMER_MIN_RANGE =A0 =A0 4 > > + > > +#define TCNT_MAX =A0 =A0 =A0 =A0 =A0 =A0 =A0 0xffffffff > > +#define NON_PERIODIC =A0 =A0 =A0 =A0 =A0 0 > > +#define PERIODIC =A0 =A0 =A0 =A0 =A0 =A0 =A0 1 > > + > > +extern void __init s5p_set_timer_source(enum s5p_timer_mode event, > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 enum s5p_timer_mode source); > > +extern struct sys_timer s5p_timer; > > +#endif /* __ASM_PLAT_S5P_TIME_H */ > > diff --git a/arch/arm/plat-s5p/s5p-time.c b/arch/arm/plat-s5p/s5p-t= ime.c > > new file mode 100644 > > index 0000000..1d2447e > > --- /dev/null > > +++ b/arch/arm/plat-s5p/s5p-time.c > > @@ -0,0 +1,452 @@ > > +/* linux/arch/arm/plat-s5p/s5p-time.c > > + * > > + * Copyright (c) 2011 Samsung Electronics Co., Ltd. > > + * =A0 =A0 =A0 =A0 =A0 =A0 http://www.samsung.com/ > > + * > > + * S5P - Common hr-timer support > > + * > > + * This program is free software; you can redistribute it and/or m= odify > > + * it under the terms of the GNU General Public License version 2 = as > > + * published by the Free Software Foundation. > > +*/ > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > + > > +static struct clk *tin_event; > > +static struct clk *tin_source; > > +static struct clk *tdiv_event; > > +static struct clk *tdiv_source; > > +static struct clk *timerclk; > > +static struct s5p_timer_source timer_source; > > +static unsigned long clock_count_per_tick; > > +static void s5p_timer_resume(void); > > + > > +static void s5p_time_stop(enum s5p_timer_mode mode) > > +{ > > + =A0 =A0 =A0 unsigned long tcon; > > + > > + =A0 =A0 =A0 tcon =3D __raw_readl(S3C2410_TCON); > > + > > + =A0 =A0 =A0 switch (mode) { > > + =A0 =A0 =A0 case S5P_PWM0: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T0START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM1: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T1START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM2: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T2START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM3: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T3START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM4: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T4START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 default: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_ERR "Invalid Timer %d\n",= mode); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + =A0 =A0 =A0 } > > + =A0 =A0 =A0 __raw_writel(tcon, S3C2410_TCON); > > +} > > + > > +static void s5p_time_setup(enum s5p_timer_mode mode, unsigned long tcnt) > > +{ > > + =A0 =A0 =A0 unsigned long tcon; > > + > > + =A0 =A0 =A0 tcon =3D __raw_readl(S3C2410_TCON); > > + > > + =A0 =A0 =A0 tcnt--; > > + > > + =A0 =A0 =A0 switch (mode) { > > + =A0 =A0 =A0 case S5P_PWM0: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~(0x0f << 0); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T0MANUALUPD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM1: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~(0x0f << 8); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T1MANUALUPD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM2: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~(0x0f << 12); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T2MANUALUPD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM3: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~(0x0f << 16); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T3MANUALUPD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM4: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~(0x07 << 20); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T4MANUALUPD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 default: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_ERR "Invalid Timer %d\n",= mode); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + =A0 =A0 =A0 } > > + > > + =A0 =A0 =A0 __raw_writel(tcnt, S3C2410_TCNTB(mode)); > > + =A0 =A0 =A0 __raw_writel(tcnt, S3C2410_TCMPB(mode)); > > + =A0 =A0 =A0 __raw_writel(tcon, S3C2410_TCON); > > +} > > + > > +static void s5p_time_start(enum s5p_timer_mode mode, bool periodic= ) > > +{ > > + =A0 =A0 =A0 unsigned long tcon; > > + > > + =A0 =A0 =A0 tcon =A0=3D __raw_readl(S3C2410_TCON); > > + > > + =A0 =A0 =A0 switch (mode) { > > + =A0 =A0 =A0 case S5P_PWM0: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T0START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T0MANUALUPD; > > + > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (periodic) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCO= N_T0RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TC= ON_T0RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM1: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T1START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T1MANUALUPD; > > + > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (periodic) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCO= N_T1RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TC= ON_T1RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM2: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T2START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T2MANUALUPD; > > + > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (periodic) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCO= N_T2RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TC= ON_T2RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM3: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T3START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T3MANUALUPD; > > + > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (periodic) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCO= N_T3RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TC= ON_T3RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM4: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCON_T4START; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TCON_T4MANUALUPD; > > + > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (periodic) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon |=3D S3C2410_TCO= N_T4RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcon &=3D ~S3C2410_TC= ON_T4RELOAD; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 default: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_ERR "Invalid Timer %d\n",= mode); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + =A0 =A0 =A0 } > > + =A0 =A0 =A0 __raw_writel(tcon, S3C2410_TCON); > > +} > > + > > +static int s5p_set_next_event(unsigned long cycles, > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struc= t clock_event_device *evt) > > +{ > > + =A0 =A0 =A0 s5p_time_setup(timer_source.event_id, cycles); > > + =A0 =A0 =A0 s5p_time_start(timer_source.event_id, NON_PERIODIC); > > + > > + =A0 =A0 =A0 return 0; > > +} > > + > > +static void s5p_set_mode(enum clock_event_mode mode, > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struc= t clock_event_device *evt) > > +{ > > + =A0 =A0 =A0 s5p_time_stop(timer_source.event_id); > > + > > + =A0 =A0 =A0 switch (mode) { > > + =A0 =A0 =A0 case CLOCK_EVT_MODE_PERIODIC: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s5p_time_setup(timer_source.event_id, > clock_count_per_tick); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s5p_time_start(timer_source.event_id,= PERIODIC); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case CLOCK_EVT_MODE_ONESHOT: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case CLOCK_EVT_MODE_UNUSED: > > + =A0 =A0 =A0 case CLOCK_EVT_MODE_SHUTDOWN: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case CLOCK_EVT_MODE_RESUME: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s5p_timer_resume(); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + =A0 =A0 =A0 } > > +} > > + > > +static void s5p_timer_resume(void) > > +{ > > + =A0 =A0 =A0 /* event timer restart */ > > + =A0 =A0 =A0 s5p_time_setup(timer_source.event_id, clock_count_per= _tick); > > + =A0 =A0 =A0 s5p_time_start(timer_source.event_id, PERIODIC); > > + > > + =A0 =A0 =A0 /* source timer restart */ > > + =A0 =A0 =A0 s5p_time_setup(timer_source.source_id, TCNT_MAX); > > + =A0 =A0 =A0 s5p_time_start(timer_source.source_id, PERIODIC); > > +} > > + > > +void __init s5p_set_timer_source(enum s5p_timer_mode event, > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0en= um s5p_timer_mode source) > > +{ > > + =A0 =A0 =A0 s3c_device_timer[event].dev.bus =3D &platform_bus_typ= e; > > + =A0 =A0 =A0 s3c_device_timer[source].dev.bus =3D &platform_bus_ty= pe; > > + > > + =A0 =A0 =A0 timer_source.event_id =3D event; > > + =A0 =A0 =A0 timer_source.source_id =3D source; > > +} > > + > > +static struct clock_event_device time_event_device =3D { > > + =A0 =A0 =A0 .name =A0 =A0 =A0 =A0 =A0 =3D "s5p_event_timer", > > + =A0 =A0 =A0 .features =A0 =A0 =A0 =3D CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, > > + =A0 =A0 =A0 .rating =A0 =A0 =A0 =A0 =3D 200, > > + =A0 =A0 =A0 .shift =A0 =A0 =A0 =A0 =A0=3D 32, > > + =A0 =A0 =A0 .set_next_event =3D s5p_set_next_event, > > + =A0 =A0 =A0 .set_mode =A0 =A0 =A0 =3D s5p_set_mode, > > +}; > > + > > +static irqreturn_t s5p_clock_event_isr(int irq, void *dev_id) > > +{ > > + =A0 =A0 =A0 struct clock_event_device *evt =3D dev_id; > > + > > + =A0 =A0 =A0 evt->event_handler(evt); > > + > > + =A0 =A0 =A0 return IRQ_HANDLED; > > +} > > + > > +static struct irqaction s5p_clock_event_irq =3D { > > + =A0 =A0 =A0 .name =A0 =A0 =A0 =A0 =A0 =3D "s5p_time_irq", > > + =A0 =A0 =A0 .flags =A0 =A0 =A0 =A0 =A0=3D IRQF_DISABLED | IRQF_TI= MER | IRQF_IRQPOLL, > > + =A0 =A0 =A0 .handler =A0 =A0 =A0 =A0=3D s5p_clock_event_isr, > > + =A0 =A0 =A0 .dev_id =A0 =A0 =A0 =A0 =3D &time_event_device, > > +}; > > + > > +static void __init s5p_clockevent_init(void) > > +{ > > + =A0 =A0 =A0 unsigned long pclk; > > + =A0 =A0 =A0 unsigned long clock_rate; > > + =A0 =A0 =A0 unsigned int irq_number; > > + =A0 =A0 =A0 struct clk *tscaler; > > + > > + =A0 =A0 =A0 pclk =3D clk_get_rate(timerclk); > > + > > + =A0 =A0 =A0 tscaler =3D clk_get_parent(tdiv_event); > > + > > + =A0 =A0 =A0 clk_set_rate(tscaler, pclk / 2); > > + =A0 =A0 =A0 clk_set_rate(tdiv_event, pclk / 2); > > + =A0 =A0 =A0 clk_set_parent(tin_event, tdiv_event); > > + > > + =A0 =A0 =A0 clock_rate =3D clk_get_rate(tin_event); > > + =A0 =A0 =A0 clock_count_per_tick =3D clock_rate / HZ; > > + > > + =A0 =A0 =A0 clockevents_calc_mult_shift(&time_event_device, > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= clock_rate, S5PTIMER_MIN_RANGE); > > + =A0 =A0 =A0 time_event_device.max_delta_ns =3D > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 clockevent_delta2ns(-1, &time_event_d= evice); > > + =A0 =A0 =A0 time_event_device.min_delta_ns =3D > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 clockevent_delta2ns(1, &time_event_de= vice); > > + > > + =A0 =A0 =A0 time_event_device.cpumask =3D cpumask_of(0); > > + =A0 =A0 =A0 clockevents_register_device(&time_event_device); > > + > > + =A0 =A0 =A0 irq_number =3D timer_source.event_id + IRQ_TIMER0; > > + =A0 =A0 =A0 setup_irq(irq_number, &s5p_clock_event_irq); > > +} > > + > > +static cycle_t s5p_timer_read(struct clocksource *cs) > > +{ > > + =A0 =A0 =A0 unsigned long offset =3D 0; > > + > > + =A0 =A0 =A0 switch (timer_source.source_id) { > > + =A0 =A0 =A0 case S5P_PWM0: > > + =A0 =A0 =A0 case S5P_PWM1: > > + =A0 =A0 =A0 case S5P_PWM2: > > + =A0 =A0 =A0 case S5P_PWM3: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 offset =3D (timer_source.source_id * = 0x0c) + 0x14; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM4: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 offset =3D 0x40; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 default: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_ERR "Invalid Timer %d\n", > timer_source.source_id); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0; > > + =A0 =A0 =A0 } > > + > > + =A0 =A0 =A0 return (cycle_t) ~__raw_readl(S3C_TIMERREG(offset)); > > +} > > + > > +/* > > + * Override the global weak sched_clock symbol with this > > + * local implementation which uses the clocksource to get some > > + * better resolution when scheduling the kernel. We accept that > > + * this wraps around for now, since it is just a relative time > > + * stamp. (Inspired by U300 implementation.) > > + */ > > +static DEFINE_CLOCK_DATA(cd); > > + > > +unsigned long long notrace sched_clock(void) > > +{ > > + =A0 =A0 =A0 u32 cyc; > > + =A0 =A0 =A0 unsigned long offset =3D 0; > > + > > + =A0 =A0 =A0 switch (timer_source.source_id) { > > + =A0 =A0 =A0 case S5P_PWM0: > > + =A0 =A0 =A0 case S5P_PWM1: > > + =A0 =A0 =A0 case S5P_PWM2: > > + =A0 =A0 =A0 case S5P_PWM3: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 offset =3D (timer_source.source_id * = 0x0c) + 0x14; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM4: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 offset =3D 0x40; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 default: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_ERR "Invalid Timer %d\n", > timer_source.source_id); > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0; > > + =A0 =A0 =A0 } > > + > > + =A0 =A0 =A0 cyc =3D ~__raw_readl(S3C_TIMERREG(offset)); > > + =A0 =A0 =A0 return cyc_to_sched_clock(&cd, cyc, (u32)~0); > > +} > > + > > +static void notrace s5p_update_sched_clock(void) > > +{ > > + =A0 =A0 =A0 u32 cyc; > > + =A0 =A0 =A0 unsigned long offset =3D 0; > > + > > + =A0 =A0 =A0 switch (timer_source.source_id) { > > + =A0 =A0 =A0 case S5P_PWM0: > > + =A0 =A0 =A0 case S5P_PWM1: > > + =A0 =A0 =A0 case S5P_PWM2: > > + =A0 =A0 =A0 case S5P_PWM3: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 offset =3D (timer_source.source_id * = 0x0c) + 0x14; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 case S5P_PWM4: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 offset =3D 0x40; > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > > + > > + =A0 =A0 =A0 default: > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 printk(KERN_ERR "Invalid Timer %d\n", > timer_source.source_id); > > + =A0 =A0 =A0 } > > + > > + =A0 =A0 =A0 cyc =3D ~__raw_readl(S3C_TIMERREG(offset)); > > + =A0 =A0 =A0 update_sched_clock(&cd, cyc, (u32)~0); > > +} > > + > > +struct clocksource time_clocksource =3D { > > + =A0 =A0 =A0 .name =A0 =A0 =A0 =A0 =A0 =3D "s5p_clocksource_timer"= , > > + =A0 =A0 =A0 .rating =A0 =A0 =A0 =A0 =3D 250, > > + =A0 =A0 =A0 .read =A0 =A0 =A0 =A0 =A0 =3D s5p_timer_read, > > + =A0 =A0 =A0 .mask =A0 =A0 =A0 =A0 =A0 =3D CLOCKSOURCE_MASK(32), > > + =A0 =A0 =A0 .flags =A0 =A0 =A0 =A0 =A0=3D CLOCK_SOURCE_IS_CONTINU= OUS, > > +}; > > + > > +static void __init s5p_clocksource_init(void) > > +{ > > + =A0 =A0 =A0 unsigned long pclk; > > + =A0 =A0 =A0 unsigned long clock_rate; > > + > > + =A0 =A0 =A0 pclk =3D clk_get_rate(timerclk); > > + > > + =A0 =A0 =A0 clk_set_rate(tdiv_source, pclk / 2); > > + =A0 =A0 =A0 clk_set_parent(tin_source, tdiv_source); > > + > > + =A0 =A0 =A0 clock_rate =3D clk_get_rate(tin_source); > > + > > + =A0 =A0 =A0 init_sched_clock(&cd, s5p_update_sched_clock, 32, clo= ck_rate); > > + > > + =A0 =A0 =A0 s5p_time_setup(timer_source.source_id, TCNT_MAX); > > + =A0 =A0 =A0 s5p_time_start(timer_source.source_id, PERIODIC); > > + > > + =A0 =A0 =A0 if (clocksource_register_hz(&time_clocksource, clock_= rate)) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 panic("%s: can't register clocksource= \n", > time_clocksource.name); > > +} > > + > > +static void __init s5p_timer_resources(void) > > +{ > > + > > + =A0 =A0 =A0 unsigned long event_id =3D timer_source.event_id; > > + =A0 =A0 =A0 unsigned long source_id =3D timer_source.source_id; > > + > > + =A0 =A0 =A0 timerclk =3D clk_get(NULL, "timers"); > > + > > + =A0 =A0 =A0 clk_enable(timerclk); > > + > > + =A0 =A0 =A0 if (IS_ERR(timerclk)) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 panic("failed to get timers clock for= timer"); > > + > > + =A0 =A0 =A0 tin_event =3D clk_get(&s3c_device_timer[event_id].dev= , "pwm-tin"); > > + > > + =A0 =A0 =A0 if (IS_ERR(tin_event)) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 panic("failed to get pwm-tin clock fo= r event timer"); > > + > > + =A0 =A0 =A0 tdiv_event =3D clk_get(&s3c_device_timer[event_id].de= v, "pwm-tdiv"); > > + > > + =A0 =A0 =A0 if (IS_ERR(tdiv_event)) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 panic("failed to get pwm-tdiv clock f= or event timer"); > > + > > + =A0 =A0 =A0 clk_enable(tin_event); > > + > > + =A0 =A0 =A0 tin_source =3D clk_get(&s3c_device_timer[source_id].d= ev, "pwm-tin"); > > + =A0 =A0 =A0 if (IS_ERR(tin_source)) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 panic("failed to get pwm-tin clock fo= r source timer"); > > + > > + =A0 =A0 =A0 tdiv_source =3D clk_get(&s3c_device_timer[source_id].= dev, "pwm- > tdiv"); > > + =A0 =A0 =A0 if (IS_ERR(tdiv_source)) > > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 panic("failed to get pwm-tdiv clock f= or source timer"); > > + > > + =A0 =A0 =A0 clk_enable(tin_source); > > +} > > + > > +static void __init s5p_timer_init(void) > > +{ > > + =A0 =A0 =A0 s5p_timer_resources(); > > + =A0 =A0 =A0 s5p_clockevent_init(); > > + =A0 =A0 =A0 s5p_clocksource_init(); > > +} > > + > > +struct sys_timer s5p_timer =3D { > > + =A0 =A0 =A0 .init =A0 =A0 =A0 =A0 =A0 =3D s5p_timer_init, > > +}; > > -- > > 1.7.1 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-sam= sung- > soc" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at =A0http://vger.kernel.org/majordomo-info.htm= l > > >=20 > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From mboxrd@z Thu Jan 1 00:00:00 1970 From: sbkim73@samsung.com (Sangbeom Kim) Date: Fri, 04 Mar 2011 16:06:34 +0900 Subject: [PATCH V2 1/3] ARM: S5P: Add s5p_timer support for HRT In-Reply-To: References: <1299216099-7709-1-git-send-email-sbkim73@samsung.com> <1299216099-7709-2-git-send-email-sbkim73@samsung.com> Message-ID: <002501cbda3a$b6f37d00$24da7700$@com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Hi Kyungmin! I'm sorry, I don't test on exynos4 configuration. I will add S5P_HRT configuration in plat-s5p Kconfig. And It can be selected on each machine. After testing it, I will submit again. Thanks, S.B. Kim > On Friday, March 04, 2011 3:32 PM, Kyungmin Park wrote: > > This patch adds support HR-Timer(High Resolution Timer) and dynamic > > tick system for S5P SoCs. There are many clock sources for HR-Timer > > on S5P SoCs. The PWM timer, RTC, System Timer, and MCT can be used > > for clock source. > > This patch can only support PWM timer for clocksource of > > S5P64x0 and S5PV210. > > > > Signed-off-by: Sangbeom Kim > > --- > > ?arch/arm/plat-s5p/Makefile ? ? ? ? ? ? ? ?| ? ?1 + > > ?arch/arm/plat-s5p/include/plat/s5p-time.h | ? 40 +++ > > ?arch/arm/plat-s5p/s5p-time.c ? ? ? ? ? ? ?| ?452 > +++++++++++++++++++++++++++++ > > ?3 files changed, 493 insertions(+), 0 deletions(-) > > ?create mode 100644 arch/arm/plat-s5p/include/plat/s5p-time.h > > ?create mode 100644 arch/arm/plat-s5p/s5p-time.c > > > > diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile > > index 4bd5cf9..3b8cc2f 100644 > > --- a/arch/arm/plat-s5p/Makefile > > +++ b/arch/arm/plat-s5p/Makefile > > @@ -22,6 +22,7 @@ obj-$(CONFIG_S5P_GPIO_INT) ? ?+= irq-gpioint.o > > ?obj-$(CONFIG_S5P_SYSTEM_MMU) ? += sysmmu.o > > ?obj-$(CONFIG_PM) ? ? ? ? ? ? ? += pm.o > > ?obj-$(CONFIG_PM) ? ? ? ? ? ? ? += irq-pm.o > > +obj-$(CONFIG_GENERIC_CLOCKEVENTS) += s5p-time.o > > Do you test it with exynos4 configuration? it's affect the exynos compile. > First select SAMSUNG_DEV_PWM, it can't compile it since no > init_sched_clock > Second select HAVE_SCHED_CLOCK, it can't boot. > > I used the for-exynos4 branch. > > Thank you, > Kyungmin Park > > > > ?# devices > > > > diff --git a/arch/arm/plat-s5p/include/plat/s5p-time.h b/arch/arm/plat- > s5p/include/plat/s5p-time.h > > new file mode 100644 > > index 0000000..9956136 > > --- /dev/null > > +++ b/arch/arm/plat-s5p/include/plat/s5p-time.h > > @@ -0,0 +1,40 @@ > > +/* linux/arch/arm/plat-s5p/include/plat/s5p-time.h > > + * > > + * Copyright 2011 Samsung Electronics Co., Ltd. > > + * ? ? ? ? ? ? http://www.samsung.com/ > > + * > > + * Header file for s5p time support > > + * > > + * 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. > > +*/ > > + > > +#ifndef __ASM_PLAT_S5P_TIME_H > > +#define __ASM_PLAT_S5P_TIME_H __FILE__ > > + > > +/* S5P HR-Timer Clock mode */ > > +enum s5p_timer_mode { > > + ? ? ? S5P_PWM0, > > + ? ? ? S5P_PWM1, > > + ? ? ? S5P_PWM2, > > + ? ? ? S5P_PWM3, > > + ? ? ? S5P_PWM4, > > +}; > > + > > +struct s5p_timer_source { > > + ? ? ? unsigned int event_id; > > + ? ? ? unsigned int source_id; > > +}; > > + > > +/* Be able to sleep for atleast 4 seconds (usually more) */ > > +#define S5PTIMER_MIN_RANGE ? ? 4 > > + > > +#define TCNT_MAX ? ? ? ? ? ? ? 0xffffffff > > +#define NON_PERIODIC ? ? ? ? ? 0 > > +#define PERIODIC ? ? ? ? ? ? ? 1 > > + > > +extern void __init s5p_set_timer_source(enum s5p_timer_mode event, > > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? enum s5p_timer_mode source); > > +extern struct sys_timer s5p_timer; > > +#endif /* __ASM_PLAT_S5P_TIME_H */ > > diff --git a/arch/arm/plat-s5p/s5p-time.c b/arch/arm/plat-s5p/s5p-time.c > > new file mode 100644 > > index 0000000..1d2447e > > --- /dev/null > > +++ b/arch/arm/plat-s5p/s5p-time.c > > @@ -0,0 +1,452 @@ > > +/* linux/arch/arm/plat-s5p/s5p-time.c > > + * > > + * Copyright (c) 2011 Samsung Electronics Co., Ltd. > > + * ? ? ? ? ? ? http://www.samsung.com/ > > + * > > + * S5P - Common hr-timer support > > + * > > + * 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. > > +*/ > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > + > > +static struct clk *tin_event; > > +static struct clk *tin_source; > > +static struct clk *tdiv_event; > > +static struct clk *tdiv_source; > > +static struct clk *timerclk; > > +static struct s5p_timer_source timer_source; > > +static unsigned long clock_count_per_tick; > > +static void s5p_timer_resume(void); > > + > > +static void s5p_time_stop(enum s5p_timer_mode mode) > > +{ > > + ? ? ? unsigned long tcon; > > + > > + ? ? ? tcon = __raw_readl(S3C2410_TCON); > > + > > + ? ? ? switch (mode) { > > + ? ? ? case S5P_PWM0: > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T0START; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM1: > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T1START; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM2: > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T2START; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM3: > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T3START; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM4: > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T4START; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? default: > > + ? ? ? ? ? ? ? printk(KERN_ERR "Invalid Timer %d\n", mode); > > + ? ? ? ? ? ? ? break; > > + ? ? ? } > > + ? ? ? __raw_writel(tcon, S3C2410_TCON); > > +} > > + > > +static void s5p_time_setup(enum s5p_timer_mode mode, unsigned long tcnt) > > +{ > > + ? ? ? unsigned long tcon; > > + > > + ? ? ? tcon = __raw_readl(S3C2410_TCON); > > + > > + ? ? ? tcnt--; > > + > > + ? ? ? switch (mode) { > > + ? ? ? case S5P_PWM0: > > + ? ? ? ? ? ? ? tcon &= ~(0x0f << 0); > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T0MANUALUPD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM1: > > + ? ? ? ? ? ? ? tcon &= ~(0x0f << 8); > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T1MANUALUPD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM2: > > + ? ? ? ? ? ? ? tcon &= ~(0x0f << 12); > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T2MANUALUPD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM3: > > + ? ? ? ? ? ? ? tcon &= ~(0x0f << 16); > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T3MANUALUPD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM4: > > + ? ? ? ? ? ? ? tcon &= ~(0x07 << 20); > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T4MANUALUPD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? default: > > + ? ? ? ? ? ? ? printk(KERN_ERR "Invalid Timer %d\n", mode); > > + ? ? ? ? ? ? ? break; > > + ? ? ? } > > + > > + ? ? ? __raw_writel(tcnt, S3C2410_TCNTB(mode)); > > + ? ? ? __raw_writel(tcnt, S3C2410_TCMPB(mode)); > > + ? ? ? __raw_writel(tcon, S3C2410_TCON); > > +} > > + > > +static void s5p_time_start(enum s5p_timer_mode mode, bool periodic) > > +{ > > + ? ? ? unsigned long tcon; > > + > > + ? ? ? tcon ?= __raw_readl(S3C2410_TCON); > > + > > + ? ? ? switch (mode) { > > + ? ? ? case S5P_PWM0: > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T0START; > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T0MANUALUPD; > > + > > + ? ? ? ? ? ? ? if (periodic) > > + ? ? ? ? ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T0RELOAD; > > + ? ? ? ? ? ? ? else > > + ? ? ? ? ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T0RELOAD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM1: > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T1START; > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T1MANUALUPD; > > + > > + ? ? ? ? ? ? ? if (periodic) > > + ? ? ? ? ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T1RELOAD; > > + ? ? ? ? ? ? ? else > > + ? ? ? ? ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T1RELOAD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM2: > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T2START; > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T2MANUALUPD; > > + > > + ? ? ? ? ? ? ? if (periodic) > > + ? ? ? ? ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T2RELOAD; > > + ? ? ? ? ? ? ? else > > + ? ? ? ? ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T2RELOAD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM3: > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T3START; > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T3MANUALUPD; > > + > > + ? ? ? ? ? ? ? if (periodic) > > + ? ? ? ? ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T3RELOAD; > > + ? ? ? ? ? ? ? else > > + ? ? ? ? ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T3RELOAD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM4: > > + ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T4START; > > + ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T4MANUALUPD; > > + > > + ? ? ? ? ? ? ? if (periodic) > > + ? ? ? ? ? ? ? ? ? ? ? tcon |= S3C2410_TCON_T4RELOAD; > > + ? ? ? ? ? ? ? else > > + ? ? ? ? ? ? ? ? ? ? ? tcon &= ~S3C2410_TCON_T4RELOAD; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? default: > > + ? ? ? ? ? ? ? printk(KERN_ERR "Invalid Timer %d\n", mode); > > + ? ? ? ? ? ? ? break; > > + ? ? ? } > > + ? ? ? __raw_writel(tcon, S3C2410_TCON); > > +} > > + > > +static int s5p_set_next_event(unsigned long cycles, > > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct clock_event_device *evt) > > +{ > > + ? ? ? s5p_time_setup(timer_source.event_id, cycles); > > + ? ? ? s5p_time_start(timer_source.event_id, NON_PERIODIC); > > + > > + ? ? ? return 0; > > +} > > + > > +static void s5p_set_mode(enum clock_event_mode mode, > > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct clock_event_device *evt) > > +{ > > + ? ? ? s5p_time_stop(timer_source.event_id); > > + > > + ? ? ? switch (mode) { > > + ? ? ? case CLOCK_EVT_MODE_PERIODIC: > > + ? ? ? ? ? ? ? s5p_time_setup(timer_source.event_id, > clock_count_per_tick); > > + ? ? ? ? ? ? ? s5p_time_start(timer_source.event_id, PERIODIC); > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case CLOCK_EVT_MODE_ONESHOT: > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case CLOCK_EVT_MODE_UNUSED: > > + ? ? ? case CLOCK_EVT_MODE_SHUTDOWN: > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case CLOCK_EVT_MODE_RESUME: > > + ? ? ? ? ? ? ? s5p_timer_resume(); > > + ? ? ? ? ? ? ? break; > > + ? ? ? } > > +} > > + > > +static void s5p_timer_resume(void) > > +{ > > + ? ? ? /* event timer restart */ > > + ? ? ? s5p_time_setup(timer_source.event_id, clock_count_per_tick); > > + ? ? ? s5p_time_start(timer_source.event_id, PERIODIC); > > + > > + ? ? ? /* source timer restart */ > > + ? ? ? s5p_time_setup(timer_source.source_id, TCNT_MAX); > > + ? ? ? s5p_time_start(timer_source.source_id, PERIODIC); > > +} > > + > > +void __init s5p_set_timer_source(enum s5p_timer_mode event, > > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?enum s5p_timer_mode source) > > +{ > > + ? ? ? s3c_device_timer[event].dev.bus = &platform_bus_type; > > + ? ? ? s3c_device_timer[source].dev.bus = &platform_bus_type; > > + > > + ? ? ? timer_source.event_id = event; > > + ? ? ? timer_source.source_id = source; > > +} > > + > > +static struct clock_event_device time_event_device = { > > + ? ? ? .name ? ? ? ? ? = "s5p_event_timer", > > + ? ? ? .features ? ? ? = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, > > + ? ? ? .rating ? ? ? ? = 200, > > + ? ? ? .shift ? ? ? ? ?= 32, > > + ? ? ? .set_next_event = s5p_set_next_event, > > + ? ? ? .set_mode ? ? ? = s5p_set_mode, > > +}; > > + > > +static irqreturn_t s5p_clock_event_isr(int irq, void *dev_id) > > +{ > > + ? ? ? struct clock_event_device *evt = dev_id; > > + > > + ? ? ? evt->event_handler(evt); > > + > > + ? ? ? return IRQ_HANDLED; > > +} > > + > > +static struct irqaction s5p_clock_event_irq = { > > + ? ? ? .name ? ? ? ? ? = "s5p_time_irq", > > + ? ? ? .flags ? ? ? ? ?= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, > > + ? ? ? .handler ? ? ? ?= s5p_clock_event_isr, > > + ? ? ? .dev_id ? ? ? ? = &time_event_device, > > +}; > > + > > +static void __init s5p_clockevent_init(void) > > +{ > > + ? ? ? unsigned long pclk; > > + ? ? ? unsigned long clock_rate; > > + ? ? ? unsigned int irq_number; > > + ? ? ? struct clk *tscaler; > > + > > + ? ? ? pclk = clk_get_rate(timerclk); > > + > > + ? ? ? tscaler = clk_get_parent(tdiv_event); > > + > > + ? ? ? clk_set_rate(tscaler, pclk / 2); > > + ? ? ? clk_set_rate(tdiv_event, pclk / 2); > > + ? ? ? clk_set_parent(tin_event, tdiv_event); > > + > > + ? ? ? clock_rate = clk_get_rate(tin_event); > > + ? ? ? clock_count_per_tick = clock_rate / HZ; > > + > > + ? ? ? clockevents_calc_mult_shift(&time_event_device, > > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? clock_rate, S5PTIMER_MIN_RANGE); > > + ? ? ? time_event_device.max_delta_ns = > > + ? ? ? ? ? ? ? clockevent_delta2ns(-1, &time_event_device); > > + ? ? ? time_event_device.min_delta_ns = > > + ? ? ? ? ? ? ? clockevent_delta2ns(1, &time_event_device); > > + > > + ? ? ? time_event_device.cpumask = cpumask_of(0); > > + ? ? ? clockevents_register_device(&time_event_device); > > + > > + ? ? ? irq_number = timer_source.event_id + IRQ_TIMER0; > > + ? ? ? setup_irq(irq_number, &s5p_clock_event_irq); > > +} > > + > > +static cycle_t s5p_timer_read(struct clocksource *cs) > > +{ > > + ? ? ? unsigned long offset = 0; > > + > > + ? ? ? switch (timer_source.source_id) { > > + ? ? ? case S5P_PWM0: > > + ? ? ? case S5P_PWM1: > > + ? ? ? case S5P_PWM2: > > + ? ? ? case S5P_PWM3: > > + ? ? ? ? ? ? ? offset = (timer_source.source_id * 0x0c) + 0x14; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM4: > > + ? ? ? ? ? ? ? offset = 0x40; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? default: > > + ? ? ? ? ? ? ? printk(KERN_ERR "Invalid Timer %d\n", > timer_source.source_id); > > + ? ? ? ? ? ? ? return 0; > > + ? ? ? } > > + > > + ? ? ? return (cycle_t) ~__raw_readl(S3C_TIMERREG(offset)); > > +} > > + > > +/* > > + * Override the global weak sched_clock symbol with this > > + * local implementation which uses the clocksource to get some > > + * better resolution when scheduling the kernel. We accept that > > + * this wraps around for now, since it is just a relative time > > + * stamp. (Inspired by U300 implementation.) > > + */ > > +static DEFINE_CLOCK_DATA(cd); > > + > > +unsigned long long notrace sched_clock(void) > > +{ > > + ? ? ? u32 cyc; > > + ? ? ? unsigned long offset = 0; > > + > > + ? ? ? switch (timer_source.source_id) { > > + ? ? ? case S5P_PWM0: > > + ? ? ? case S5P_PWM1: > > + ? ? ? case S5P_PWM2: > > + ? ? ? case S5P_PWM3: > > + ? ? ? ? ? ? ? offset = (timer_source.source_id * 0x0c) + 0x14; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM4: > > + ? ? ? ? ? ? ? offset = 0x40; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? default: > > + ? ? ? ? ? ? ? printk(KERN_ERR "Invalid Timer %d\n", > timer_source.source_id); > > + ? ? ? ? ? ? ? return 0; > > + ? ? ? } > > + > > + ? ? ? cyc = ~__raw_readl(S3C_TIMERREG(offset)); > > + ? ? ? return cyc_to_sched_clock(&cd, cyc, (u32)~0); > > +} > > + > > +static void notrace s5p_update_sched_clock(void) > > +{ > > + ? ? ? u32 cyc; > > + ? ? ? unsigned long offset = 0; > > + > > + ? ? ? switch (timer_source.source_id) { > > + ? ? ? case S5P_PWM0: > > + ? ? ? case S5P_PWM1: > > + ? ? ? case S5P_PWM2: > > + ? ? ? case S5P_PWM3: > > + ? ? ? ? ? ? ? offset = (timer_source.source_id * 0x0c) + 0x14; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? case S5P_PWM4: > > + ? ? ? ? ? ? ? offset = 0x40; > > + ? ? ? ? ? ? ? break; > > + > > + ? ? ? default: > > + ? ? ? ? ? ? ? printk(KERN_ERR "Invalid Timer %d\n", > timer_source.source_id); > > + ? ? ? } > > + > > + ? ? ? cyc = ~__raw_readl(S3C_TIMERREG(offset)); > > + ? ? ? update_sched_clock(&cd, cyc, (u32)~0); > > +} > > + > > +struct clocksource time_clocksource = { > > + ? ? ? .name ? ? ? ? ? = "s5p_clocksource_timer", > > + ? ? ? .rating ? ? ? ? = 250, > > + ? ? ? .read ? ? ? ? ? = s5p_timer_read, > > + ? ? ? .mask ? ? ? ? ? = CLOCKSOURCE_MASK(32), > > + ? ? ? .flags ? ? ? ? ?= CLOCK_SOURCE_IS_CONTINUOUS, > > +}; > > + > > +static void __init s5p_clocksource_init(void) > > +{ > > + ? ? ? unsigned long pclk; > > + ? ? ? unsigned long clock_rate; > > + > > + ? ? ? pclk = clk_get_rate(timerclk); > > + > > + ? ? ? clk_set_rate(tdiv_source, pclk / 2); > > + ? ? ? clk_set_parent(tin_source, tdiv_source); > > + > > + ? ? ? clock_rate = clk_get_rate(tin_source); > > + > > + ? ? ? init_sched_clock(&cd, s5p_update_sched_clock, 32, clock_rate); > > + > > + ? ? ? s5p_time_setup(timer_source.source_id, TCNT_MAX); > > + ? ? ? s5p_time_start(timer_source.source_id, PERIODIC); > > + > > + ? ? ? if (clocksource_register_hz(&time_clocksource, clock_rate)) > > + ? ? ? ? ? ? ? panic("%s: can't register clocksource\n", > time_clocksource.name); > > +} > > + > > +static void __init s5p_timer_resources(void) > > +{ > > + > > + ? ? ? unsigned long event_id = timer_source.event_id; > > + ? ? ? unsigned long source_id = timer_source.source_id; > > + > > + ? ? ? timerclk = clk_get(NULL, "timers"); > > + > > + ? ? ? clk_enable(timerclk); > > + > > + ? ? ? if (IS_ERR(timerclk)) > > + ? ? ? ? ? ? ? panic("failed to get timers clock for timer"); > > + > > + ? ? ? tin_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tin"); > > + > > + ? ? ? if (IS_ERR(tin_event)) > > + ? ? ? ? ? ? ? panic("failed to get pwm-tin clock for event timer"); > > + > > + ? ? ? tdiv_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tdiv"); > > + > > + ? ? ? if (IS_ERR(tdiv_event)) > > + ? ? ? ? ? ? ? panic("failed to get pwm-tdiv clock for event timer"); > > + > > + ? ? ? clk_enable(tin_event); > > + > > + ? ? ? tin_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tin"); > > + ? ? ? if (IS_ERR(tin_source)) > > + ? ? ? ? ? ? ? panic("failed to get pwm-tin clock for source timer"); > > + > > + ? ? ? tdiv_source = clk_get(&s3c_device_timer[source_id].dev, "pwm- > tdiv"); > > + ? ? ? if (IS_ERR(tdiv_source)) > > + ? ? ? ? ? ? ? panic("failed to get pwm-tdiv clock for source timer"); > > + > > + ? ? ? clk_enable(tin_source); > > +} > > + > > +static void __init s5p_timer_init(void) > > +{ > > + ? ? ? s5p_timer_resources(); > > + ? ? ? s5p_clockevent_init(); > > + ? ? ? s5p_clocksource_init(); > > +} > > + > > +struct sys_timer s5p_timer = { > > + ? ? ? .init ? ? ? ? ? = s5p_timer_init, > > +}; > > -- > > 1.7.1 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-samsung- > soc" in > > the body of a message to majordomo at vger.kernel.org > > More majordomo info at ?http://vger.kernel.org/majordomo-info.html > > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel