diff options
Diffstat (limited to 'drivers/clocksource')
| -rw-r--r-- | drivers/clocksource/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/clocksource/Makefile | 6 | ||||
| -rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 33 | ||||
| -rw-r--r-- | drivers/clocksource/bcm_kona_timer.c | 211 | ||||
| -rw-r--r-- | drivers/clocksource/clksrc-dbx500-prcmu.c | 3 | ||||
| -rw-r--r-- | drivers/clocksource/exynos_mct.c | 1 | ||||
| -rw-r--r-- | drivers/clocksource/mxs_timer.c | 304 | ||||
| -rw-r--r-- | drivers/clocksource/nomadik-mtu.c | 4 | ||||
| -rw-r--r-- | drivers/clocksource/sun4i_timer.c (renamed from drivers/clocksource/sunxi_timer.c) | 94 | ||||
| -rw-r--r-- | drivers/clocksource/timer-marco.c | 299 | ||||
| -rw-r--r-- | drivers/clocksource/timer-prima2.c | 215 | 
11 files changed, 1094 insertions, 79 deletions
| diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 5d9bab2b501..f151c6cf27c 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -25,7 +25,7 @@ config DW_APB_TIMER_OF  config ARMADA_370_XP_TIMER  	bool -config SUNXI_TIMER +config SUN4I_TIMER  	bool  config VT8500_TIMER @@ -65,6 +65,7 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK  config ARM_ARCH_TIMER  	bool +	select CLKSRC_OF if OF  config CLKSRC_METAG_GENERIC  	def_bool y if METAG diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 2289f0cb4c4..8d979c72aa9 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -16,9 +16,13 @@ obj-$(CONFIG_CLKSRC_NOMADIK_MTU)	+= nomadik-mtu.o  obj-$(CONFIG_CLKSRC_DBX500_PRCMU)	+= clksrc-dbx500-prcmu.o  obj-$(CONFIG_ARMADA_370_XP_TIMER)	+= time-armada-370-xp.o  obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o -obj-$(CONFIG_SUNXI_TIMER)	+= sunxi_timer.o +obj-$(CONFIG_ARCH_MARCO)	+= timer-marco.o +obj-$(CONFIG_ARCH_MXS)		+= mxs_timer.o +obj-$(CONFIG_ARCH_PRIMA2)	+= timer-prima2.o +obj-$(CONFIG_SUN4I_TIMER)	+= sun4i_timer.o  obj-$(CONFIG_ARCH_TEGRA)	+= tegra20_timer.o  obj-$(CONFIG_VT8500_TIMER)	+= vt8500_timer.o +obj-$(CONFIG_ARCH_BCM)		+= bcm_kona_timer.o  obj-$(CONFIG_CADENCE_TTC_TIMER)	+= cadence_ttc_timer.o  obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o  obj-$(CONFIG_CLKSRC_SAMSUNG_PWM)	+= samsung_pwm_timer.o diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index d7ad425ab9b..a2b25418978 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -248,14 +248,16 @@ static void __cpuinit arch_timer_stop(struct clock_event_device *clk)  static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,  					   unsigned long action, void *hcpu)  { -	struct clock_event_device *evt = this_cpu_ptr(arch_timer_evt); - +	/* +	 * Grab cpu pointer in each case to avoid spurious +	 * preemptible warnings +	 */  	switch (action & ~CPU_TASKS_FROZEN) {  	case CPU_STARTING: -		arch_timer_setup(evt); +		arch_timer_setup(this_cpu_ptr(arch_timer_evt));  		break;  	case CPU_DYING: -		arch_timer_stop(evt); +		arch_timer_stop(this_cpu_ptr(arch_timer_evt));  		break;  	} @@ -337,22 +339,14 @@ out:  	return err;  } -static const struct of_device_id arch_timer_of_match[] __initconst = { -	{ .compatible	= "arm,armv7-timer",	}, -	{ .compatible	= "arm,armv8-timer",	}, -	{}, -}; - -int __init arch_timer_init(void) +static void __init arch_timer_init(struct device_node *np)  { -	struct device_node *np;  	u32 freq;  	int i; -	np = of_find_matching_node(NULL, arch_timer_of_match); -	if (!np) { -		pr_err("arch_timer: can't find DT node\n"); -		return -ENODEV; +	if (arch_timer_get_rate()) { +		pr_warn("arch_timer: multiple nodes in dt, skipping\n"); +		return;  	}  	/* Try to determine the frequency from the device tree or CNTFRQ */ @@ -378,7 +372,7 @@ int __init arch_timer_init(void)  		if (!arch_timer_ppi[PHYS_SECURE_PPI] ||  		    !arch_timer_ppi[PHYS_NONSECURE_PPI]) {  			pr_warn("arch_timer: No interrupt available, giving up\n"); -			return -EINVAL; +			return;  		}  	} @@ -387,5 +381,8 @@ int __init arch_timer_init(void)  	else  		arch_timer_read_counter = arch_counter_get_cntpct; -	return arch_timer_register(); +	arch_timer_register(); +	arch_timer_arch_init();  } +CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init); +CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init); diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c new file mode 100644 index 00000000000..350f4935645 --- /dev/null +++ b/drivers/clocksource/bcm_kona_timer.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2012 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/clockchips.h> +#include <linux/types.h> + +#include <linux/io.h> +#include <asm/mach/time.h> + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + + +#define KONA_GPTIMER_STCS_OFFSET			0x00000000 +#define KONA_GPTIMER_STCLO_OFFSET			0x00000004 +#define KONA_GPTIMER_STCHI_OFFSET			0x00000008 +#define KONA_GPTIMER_STCM0_OFFSET			0x0000000C + +#define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT		0 +#define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT		4 + +struct kona_bcm_timers { +	int tmr_irq; +	void __iomem *tmr_regs; +}; + +static struct kona_bcm_timers timers; + +static u32 arch_timer_rate; + +/* + * We use the peripheral timers for system tick, the cpu global timer for + * profile tick + */ +static void kona_timer_disable_and_clear(void __iomem *base) +{ +	uint32_t reg; + +	/* +	 * clear and disable interrupts +	 * We are using compare/match register 0 for our system interrupts +	 */ +	reg = readl(base + KONA_GPTIMER_STCS_OFFSET); + +	/* Clear compare (0) interrupt */ +	reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT; +	/* disable compare */ +	reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT); + +	writel(reg, base + KONA_GPTIMER_STCS_OFFSET); + +} + +static void +kona_timer_get_counter(void *timer_base, uint32_t *msw, uint32_t *lsw) +{ +	void __iomem *base = IOMEM(timer_base); +	int loop_limit = 4; + +	/* +	 * Read 64-bit free running counter +	 * 1. Read hi-word +	 * 2. Read low-word +	 * 3. Read hi-word again +	 * 4.1 +	 *      if new hi-word is not equal to previously read hi-word, then +	 *      start from #1 +	 * 4.2 +	 *      if new hi-word is equal to previously read hi-word then stop. +	 */ + +	while (--loop_limit) { +		*msw = readl(base + KONA_GPTIMER_STCHI_OFFSET); +		*lsw = readl(base + KONA_GPTIMER_STCLO_OFFSET); +		if (*msw == readl(base + KONA_GPTIMER_STCHI_OFFSET)) +			break; +	} +	if (!loop_limit) { +		pr_err("bcm_kona_timer: getting counter failed.\n"); +		pr_err(" Timer will be impacted\n"); +	} + +	return; +} + +static const struct of_device_id bcm_timer_ids[] __initconst = { +	{.compatible = "bcm,kona-timer"}, +	{}, +}; + +static void __init kona_timers_init(void) +{ +	struct device_node *node; +	u32 freq; + +	node = of_find_matching_node(NULL, bcm_timer_ids); + +	if (!node) +		panic("No timer"); + +	if (!of_property_read_u32(node, "clock-frequency", &freq)) +		arch_timer_rate = freq; +	else +		panic("clock-frequency not set in the .dts file"); + +	/* Setup IRQ numbers */ +	timers.tmr_irq = irq_of_parse_and_map(node, 0); + +	/* Setup IO addresses */ +	timers.tmr_regs = of_iomap(node, 0); + +	kona_timer_disable_and_clear(timers.tmr_regs); +} + +static int kona_timer_set_next_event(unsigned long clc, +				  struct clock_event_device *unused) +{ +	/* +	 * timer (0) is disabled by the timer interrupt already +	 * so, here we reload the next event value and re-enable +	 * the timer. +	 * +	 * This way, we are potentially losing the time between +	 * timer-interrupt->set_next_event. CPU local timers, when +	 * they come in should get rid of skew. +	 */ + +	uint32_t lsw, msw; +	uint32_t reg; + +	kona_timer_get_counter(timers.tmr_regs, &msw, &lsw); + +	/* Load the "next" event tick value */ +	writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET); + +	/* Enable compare */ +	reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET); +	reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT); +	writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET); + +	return 0; +} + +static void kona_timer_set_mode(enum clock_event_mode mode, +			     struct clock_event_device *unused) +{ +	switch (mode) { +	case CLOCK_EVT_MODE_ONESHOT: +		/* by default mode is one shot don't do any thing */ +		break; +	case CLOCK_EVT_MODE_UNUSED: +	case CLOCK_EVT_MODE_SHUTDOWN: +	default: +		kona_timer_disable_and_clear(timers.tmr_regs); +	} +} + +static struct clock_event_device kona_clockevent_timer = { +	.name = "timer 1", +	.features = CLOCK_EVT_FEAT_ONESHOT, +	.set_next_event = kona_timer_set_next_event, +	.set_mode = kona_timer_set_mode +}; + +static void __init kona_timer_clockevents_init(void) +{ +	kona_clockevent_timer.cpumask = cpumask_of(0); +	clockevents_config_and_register(&kona_clockevent_timer, +		arch_timer_rate, 6, 0xffffffff); +} + +static irqreturn_t kona_timer_interrupt(int irq, void *dev_id) +{ +	struct clock_event_device *evt = &kona_clockevent_timer; + +	kona_timer_disable_and_clear(timers.tmr_regs); +	evt->event_handler(evt); +	return IRQ_HANDLED; +} + +static struct irqaction kona_timer_irq = { +	.name = "Kona Timer Tick", +	.flags = IRQF_TIMER, +	.handler = kona_timer_interrupt, +}; + +static void __init kona_timer_init(void) +{ +	kona_timers_init(); +	kona_timer_clockevents_init(); +	setup_irq(timers.tmr_irq, &kona_timer_irq); +	kona_timer_set_next_event((arch_timer_rate / HZ), NULL); +} + +CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer", +	kona_timer_init); diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c index c26c369eb9e..54f3d119d99 100644 --- a/drivers/clocksource/clksrc-dbx500-prcmu.c +++ b/drivers/clocksource/clksrc-dbx500-prcmu.c @@ -17,9 +17,6 @@  #include <asm/sched_clock.h> -#include <mach/setup.h> -#include <mach/hardware.h> -  #define RATE_32K		32768  #define TIMER_MODE_CONTINOUS	0x1 diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index a6ca0fb0693..662fcc06582 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -24,7 +24,6 @@  #include <linux/of_address.h>  #include <linux/clocksource.h> -#include <asm/arch_timer.h>  #include <asm/localtimer.h>  #include <asm/mach/time.h> diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c new file mode 100644 index 00000000000..02af4204af8 --- /dev/null +++ b/drivers/clocksource/mxs_timer.c @@ -0,0 +1,304 @@ +/* + *  Copyright (C) 2000-2001 Deep Blue Solutions + *  Copyright (C) 2002 Shane Nay (shane@minirl.com) + *  Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com) + *  Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) + *  Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/clockchips.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/stmp_device.h> + +#include <asm/mach/time.h> +#include <asm/sched_clock.h> + +/* + * There are 2 versions of the timrot on Freescale MXS-based SoCs. + * The v1 on MX23 only gets 16 bits counter, while v2 on MX28 + * extends the counter to 32 bits. + * + * The implementation uses two timers, one for clock_event and + * another for clocksource. MX28 uses timrot 0 and 1, while MX23 + * uses 0 and 2. + */ + +#define MX23_TIMROT_VERSION_OFFSET	0x0a0 +#define MX28_TIMROT_VERSION_OFFSET	0x120 +#define BP_TIMROT_MAJOR_VERSION		24 +#define BV_TIMROT_VERSION_1		0x01 +#define BV_TIMROT_VERSION_2		0x02 +#define timrot_is_v1()	(timrot_major_version == BV_TIMROT_VERSION_1) + +/* + * There are 4 registers for each timrotv2 instance, and 2 registers + * for each timrotv1. So address step 0x40 in macros below strides + * one instance of timrotv2 while two instances of timrotv1. + * + * As the result, HW_TIMROT_XXXn(1) defines the address of timrot1 + * on MX28 while timrot2 on MX23. + */ +/* common between v1 and v2 */ +#define HW_TIMROT_ROTCTRL		0x00 +#define HW_TIMROT_TIMCTRLn(n)		(0x20 + (n) * 0x40) +/* v1 only */ +#define HW_TIMROT_TIMCOUNTn(n)		(0x30 + (n) * 0x40) +/* v2 only */ +#define HW_TIMROT_RUNNING_COUNTn(n)	(0x30 + (n) * 0x40) +#define HW_TIMROT_FIXED_COUNTn(n)	(0x40 + (n) * 0x40) + +#define BM_TIMROT_TIMCTRLn_RELOAD	(1 << 6) +#define BM_TIMROT_TIMCTRLn_UPDATE	(1 << 7) +#define BM_TIMROT_TIMCTRLn_IRQ_EN	(1 << 14) +#define BM_TIMROT_TIMCTRLn_IRQ		(1 << 15) +#define BP_TIMROT_TIMCTRLn_SELECT	0 +#define BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL		0x8 +#define BV_TIMROTv2_TIMCTRLn_SELECT__32KHZ_XTAL		0xb +#define BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS	0xf + +static struct clock_event_device mxs_clockevent_device; +static enum clock_event_mode mxs_clockevent_mode = CLOCK_EVT_MODE_UNUSED; + +static void __iomem *mxs_timrot_base; +static u32 timrot_major_version; + +static inline void timrot_irq_disable(void) +{ +	__raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base + +		     HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR); +} + +static inline void timrot_irq_enable(void) +{ +	__raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base + +		     HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_SET); +} + +static void timrot_irq_acknowledge(void) +{ +	__raw_writel(BM_TIMROT_TIMCTRLn_IRQ, mxs_timrot_base + +		     HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR); +} + +static cycle_t timrotv1_get_cycles(struct clocksource *cs) +{ +	return ~((__raw_readl(mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1)) +			& 0xffff0000) >> 16); +} + +static int timrotv1_set_next_event(unsigned long evt, +					struct clock_event_device *dev) +{ +	/* timrot decrements the count */ +	__raw_writel(evt, mxs_timrot_base + HW_TIMROT_TIMCOUNTn(0)); + +	return 0; +} + +static int timrotv2_set_next_event(unsigned long evt, +					struct clock_event_device *dev) +{ +	/* timrot decrements the count */ +	__raw_writel(evt, mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(0)); + +	return 0; +} + +static irqreturn_t mxs_timer_interrupt(int irq, void *dev_id) +{ +	struct clock_event_device *evt = dev_id; + +	timrot_irq_acknowledge(); +	evt->event_handler(evt); + +	return IRQ_HANDLED; +} + +static struct irqaction mxs_timer_irq = { +	.name		= "MXS Timer Tick", +	.dev_id		= &mxs_clockevent_device, +	.flags		= IRQF_TIMER | IRQF_IRQPOLL, +	.handler	= mxs_timer_interrupt, +}; + +#ifdef DEBUG +static const char *clock_event_mode_label[] const = { +	[CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC", +	[CLOCK_EVT_MODE_ONESHOT]  = "CLOCK_EVT_MODE_ONESHOT", +	[CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN", +	[CLOCK_EVT_MODE_UNUSED]   = "CLOCK_EVT_MODE_UNUSED" +}; +#endif /* DEBUG */ + +static void mxs_set_mode(enum clock_event_mode mode, +				struct clock_event_device *evt) +{ +	/* Disable interrupt in timer module */ +	timrot_irq_disable(); + +	if (mode != mxs_clockevent_mode) { +		/* Set event time into the furthest future */ +		if (timrot_is_v1()) +			__raw_writel(0xffff, +				mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1)); +		else +			__raw_writel(0xffffffff, +				mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1)); + +		/* Clear pending interrupt */ +		timrot_irq_acknowledge(); +	} + +#ifdef DEBUG +	pr_info("%s: changing mode from %s to %s\n", __func__, +		clock_event_mode_label[mxs_clockevent_mode], +		clock_event_mode_label[mode]); +#endif /* DEBUG */ + +	/* Remember timer mode */ +	mxs_clockevent_mode = mode; + +	switch (mode) { +	case CLOCK_EVT_MODE_PERIODIC: +		pr_err("%s: Periodic mode is not implemented\n", __func__); +		break; +	case CLOCK_EVT_MODE_ONESHOT: +		timrot_irq_enable(); +		break; +	case CLOCK_EVT_MODE_SHUTDOWN: +	case CLOCK_EVT_MODE_UNUSED: +	case CLOCK_EVT_MODE_RESUME: +		/* Left event sources disabled, no more interrupts appear */ +		break; +	} +} + +static struct clock_event_device mxs_clockevent_device = { +	.name		= "mxs_timrot", +	.features	= CLOCK_EVT_FEAT_ONESHOT, +	.set_mode	= mxs_set_mode, +	.set_next_event	= timrotv2_set_next_event, +	.rating		= 200, +}; + +static int __init mxs_clockevent_init(struct clk *timer_clk) +{ +	if (timrot_is_v1()) +		mxs_clockevent_device.set_next_event = timrotv1_set_next_event; +	mxs_clockevent_device.cpumask = cpumask_of(0); +	clockevents_config_and_register(&mxs_clockevent_device, +					clk_get_rate(timer_clk), +					timrot_is_v1() ? 0xf : 0x2, +					timrot_is_v1() ? 0xfffe : 0xfffffffe); + +	return 0; +} + +static struct clocksource clocksource_mxs = { +	.name		= "mxs_timer", +	.rating		= 200, +	.read		= timrotv1_get_cycles, +	.mask		= CLOCKSOURCE_MASK(16), +	.flags		= CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static u32 notrace mxs_read_sched_clock_v2(void) +{ +	return ~readl_relaxed(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1)); +} + +static int __init mxs_clocksource_init(struct clk *timer_clk) +{ +	unsigned int c = clk_get_rate(timer_clk); + +	if (timrot_is_v1()) +		clocksource_register_hz(&clocksource_mxs, c); +	else { +		clocksource_mmio_init(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1), +			"mxs_timer", c, 200, 32, clocksource_mmio_readl_down); +		setup_sched_clock(mxs_read_sched_clock_v2, 32, c); +	} + +	return 0; +} + +static void __init mxs_timer_init(struct device_node *np) +{ +	struct clk *timer_clk; +	int irq; + +	mxs_timrot_base = of_iomap(np, 0); +	WARN_ON(!mxs_timrot_base); + +	timer_clk = of_clk_get(np, 0); +	if (IS_ERR(timer_clk)) { +		pr_err("%s: failed to get clk\n", __func__); +		return; +	} + +	clk_prepare_enable(timer_clk); + +	/* +	 * Initialize timers to a known state +	 */ +	stmp_reset_block(mxs_timrot_base + HW_TIMROT_ROTCTRL); + +	/* get timrot version */ +	timrot_major_version = __raw_readl(mxs_timrot_base + +			(of_device_is_compatible(np, "fsl,imx23-timrot") ? +						MX23_TIMROT_VERSION_OFFSET : +						MX28_TIMROT_VERSION_OFFSET)); +	timrot_major_version >>= BP_TIMROT_MAJOR_VERSION; + +	/* one for clock_event */ +	__raw_writel((timrot_is_v1() ? +			BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL : +			BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) | +			BM_TIMROT_TIMCTRLn_UPDATE | +			BM_TIMROT_TIMCTRLn_IRQ_EN, +			mxs_timrot_base + HW_TIMROT_TIMCTRLn(0)); + +	/* another for clocksource */ +	__raw_writel((timrot_is_v1() ? +			BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL : +			BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) | +			BM_TIMROT_TIMCTRLn_RELOAD, +			mxs_timrot_base + HW_TIMROT_TIMCTRLn(1)); + +	/* set clocksource timer fixed count to the maximum */ +	if (timrot_is_v1()) +		__raw_writel(0xffff, +			mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1)); +	else +		__raw_writel(0xffffffff, +			mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1)); + +	/* init and register the timer to the framework */ +	mxs_clocksource_init(timer_clk); +	mxs_clockevent_init(timer_clk); + +	/* Make irqs happen */ +	irq = irq_of_parse_and_map(np, 0); +	setup_irq(irq, &mxs_timer_irq); +} +CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init); diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c index 071f6eadfea..e405531e1cc 100644 --- a/drivers/clocksource/nomadik-mtu.c +++ b/drivers/clocksource/nomadik-mtu.c @@ -67,7 +67,7 @@ static u32 clk_prescale;  static u32 nmdk_cycle;		/* write-once */  static struct delay_timer mtu_delay_timer; -#ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK +#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK  /*   * Override the global weak sched_clock symbol with this   * local implementation which uses the clocksource to get some @@ -233,7 +233,7 @@ void __init nmdk_timer_init(void __iomem *base, int irq)  		pr_err("timer: failed to initialize clock source %s\n",  		       "mtu_0"); -#ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK +#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK  	setup_sched_clock(nomadik_read_sched_clock, 32, rate);  #endif diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sun4i_timer.c index 0ce85e29769..d4674e78ef3 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -22,66 +22,64 @@  #include <linux/of.h>  #include <linux/of_address.h>  #include <linux/of_irq.h> -#include <linux/sunxi_timer.h> -#include <linux/clk/sunxi.h> -#define TIMER_CTL_REG		0x00 -#define TIMER_CTL_ENABLE		(1 << 0) +#define TIMER_IRQ_EN_REG	0x00 +#define TIMER_IRQ_EN(val)		(1 << val)  #define TIMER_IRQ_ST_REG	0x04 -#define TIMER0_CTL_REG		0x10 -#define TIMER0_CTL_ENABLE		(1 << 0) -#define TIMER0_CTL_AUTORELOAD		(1 << 1) -#define TIMER0_CTL_ONESHOT		(1 << 7) -#define TIMER0_INTVAL_REG	0x14 -#define TIMER0_CNTVAL_REG	0x18 +#define TIMER_CTL_REG(val)	(0x10 * val + 0x10) +#define TIMER_CTL_ENABLE		(1 << 0) +#define TIMER_CTL_AUTORELOAD		(1 << 1) +#define TIMER_CTL_ONESHOT		(1 << 7) +#define TIMER_INTVAL_REG(val)	(0x10 * val + 0x14) +#define TIMER_CNTVAL_REG(val)	(0x10 * val + 0x18)  #define TIMER_SCAL		16  static void __iomem *timer_base; -static void sunxi_clkevt_mode(enum clock_event_mode mode, +static void sun4i_clkevt_mode(enum clock_event_mode mode,  			      struct clock_event_device *clk)  { -	u32 u = readl(timer_base + TIMER0_CTL_REG); +	u32 u = readl(timer_base + TIMER_CTL_REG(0));  	switch (mode) {  	case CLOCK_EVT_MODE_PERIODIC: -		u &= ~(TIMER0_CTL_ONESHOT); -		writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG); +		u &= ~(TIMER_CTL_ONESHOT); +		writel(u | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(0));  		break;  	case CLOCK_EVT_MODE_ONESHOT: -		writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG); +		writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0));  		break;  	case CLOCK_EVT_MODE_UNUSED:  	case CLOCK_EVT_MODE_SHUTDOWN:  	default: -		writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG); +		writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0));  		break;  	}  } -static int sunxi_clkevt_next_event(unsigned long evt, +static int sun4i_clkevt_next_event(unsigned long evt,  				   struct clock_event_device *unused)  { -	u32 u = readl(timer_base + TIMER0_CTL_REG); -	writel(evt, timer_base + TIMER0_CNTVAL_REG); -	writel(u | TIMER0_CTL_ENABLE | TIMER0_CTL_AUTORELOAD, -	       timer_base + TIMER0_CTL_REG); +	u32 u = readl(timer_base + TIMER_CTL_REG(0)); +	writel(evt, timer_base + TIMER_CNTVAL_REG(0)); +	writel(u | TIMER_CTL_ENABLE | TIMER_CTL_AUTORELOAD, +	       timer_base + TIMER_CTL_REG(0));  	return 0;  } -static struct clock_event_device sunxi_clockevent = { -	.name = "sunxi_tick", +static struct clock_event_device sun4i_clockevent = { +	.name = "sun4i_tick",  	.rating = 300,  	.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, -	.set_mode = sunxi_clkevt_mode, -	.set_next_event = sunxi_clkevt_next_event, +	.set_mode = sun4i_clkevt_mode, +	.set_next_event = sun4i_clkevt_next_event,  }; -static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) +static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)  {  	struct clock_event_device *evt = (struct clock_event_device *)dev_id; @@ -91,30 +89,20 @@ static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id)  	return IRQ_HANDLED;  } -static struct irqaction sunxi_timer_irq = { -	.name = "sunxi_timer0", +static struct irqaction sun4i_timer_irq = { +	.name = "sun4i_timer0",  	.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, -	.handler = sunxi_timer_interrupt, -	.dev_id = &sunxi_clockevent, -}; - -static struct of_device_id sunxi_timer_dt_ids[] = { -	{ .compatible = "allwinner,sunxi-timer" }, -	{ } +	.handler = sun4i_timer_interrupt, +	.dev_id = &sun4i_clockevent,  }; -void __init sunxi_timer_init(void) +static void __init sun4i_timer_init(struct device_node *node)  { -	struct device_node *node;  	unsigned long rate = 0;  	struct clk *clk;  	int ret, irq;  	u32 val; -	node = of_find_matching_node(NULL, sunxi_timer_dt_ids); -	if (!node) -		panic("No sunxi timer node"); -  	timer_base = of_iomap(node, 0);  	if (!timer_base)  		panic("Can't map registers"); @@ -123,8 +111,6 @@ void __init sunxi_timer_init(void)  	if (irq <= 0)  		panic("Can't parse IRQ"); -	sunxi_init_clocks(); -  	clk = of_clk_get(node, 0);  	if (IS_ERR(clk))  		panic("Can't get timer clock"); @@ -132,29 +118,31 @@ void __init sunxi_timer_init(void)  	rate = clk_get_rate(clk);  	writel(rate / (TIMER_SCAL * HZ), -	       timer_base + TIMER0_INTVAL_REG); +	       timer_base + TIMER_INTVAL_REG(0));  	/* set clock source to HOSC, 16 pre-division */ -	val = readl(timer_base + TIMER0_CTL_REG); +	val = readl(timer_base + TIMER_CTL_REG(0));  	val &= ~(0x07 << 4);  	val &= ~(0x03 << 2);  	val |= (4 << 4) | (1 << 2); -	writel(val, timer_base + TIMER0_CTL_REG); +	writel(val, timer_base + TIMER_CTL_REG(0));  	/* set mode to auto reload */ -	val = readl(timer_base + TIMER0_CTL_REG); -	writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG); +	val = readl(timer_base + TIMER_CTL_REG(0)); +	writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0)); -	ret = setup_irq(irq, &sunxi_timer_irq); +	ret = setup_irq(irq, &sun4i_timer_irq);  	if (ret)  		pr_warn("failed to setup irq %d\n", irq);  	/* Enable timer0 interrupt */ -	val = readl(timer_base + TIMER_CTL_REG); -	writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG); +	val = readl(timer_base + TIMER_IRQ_EN_REG); +	writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); -	sunxi_clockevent.cpumask = cpumask_of(0); +	sun4i_clockevent.cpumask = cpumask_of(0); -	clockevents_config_and_register(&sunxi_clockevent, rate / TIMER_SCAL, +	clockevents_config_and_register(&sun4i_clockevent, rate / TIMER_SCAL,  					0x1, 0xff);  } +CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer", +		       sun4i_timer_init); diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c new file mode 100644 index 00000000000..97738dbf3e3 --- /dev/null +++ b/drivers/clocksource/timer-marco.c @@ -0,0 +1,299 @@ +/* + * System timer for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/clockchips.h> +#include <linux/clocksource.h> +#include <linux/bitops.h> +#include <linux/irq.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <asm/sched_clock.h> +#include <asm/localtimer.h> +#include <asm/mach/time.h> + +#define SIRFSOC_TIMER_32COUNTER_0_CTRL			0x0000 +#define SIRFSOC_TIMER_32COUNTER_1_CTRL			0x0004 +#define SIRFSOC_TIMER_MATCH_0				0x0018 +#define SIRFSOC_TIMER_MATCH_1				0x001c +#define SIRFSOC_TIMER_COUNTER_0				0x0048 +#define SIRFSOC_TIMER_COUNTER_1				0x004c +#define SIRFSOC_TIMER_INTR_STATUS			0x0060 +#define SIRFSOC_TIMER_WATCHDOG_EN			0x0064 +#define SIRFSOC_TIMER_64COUNTER_CTRL			0x0068 +#define SIRFSOC_TIMER_64COUNTER_LO			0x006c +#define SIRFSOC_TIMER_64COUNTER_HI			0x0070 +#define SIRFSOC_TIMER_64COUNTER_LOAD_LO			0x0074 +#define SIRFSOC_TIMER_64COUNTER_LOAD_HI			0x0078 +#define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO		0x007c +#define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI		0x0080 + +#define SIRFSOC_TIMER_REG_CNT 6 + +static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = { +	SIRFSOC_TIMER_WATCHDOG_EN, +	SIRFSOC_TIMER_32COUNTER_0_CTRL, +	SIRFSOC_TIMER_32COUNTER_1_CTRL, +	SIRFSOC_TIMER_64COUNTER_CTRL, +	SIRFSOC_TIMER_64COUNTER_RLATCHED_LO, +	SIRFSOC_TIMER_64COUNTER_RLATCHED_HI, +}; + +static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT]; + +static void __iomem *sirfsoc_timer_base; + +/* disable count and interrupt */ +static inline void sirfsoc_timer_count_disable(int idx) +{ +	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7, +		sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx); +} + +/* enable count and interrupt */ +static inline void sirfsoc_timer_count_enable(int idx) +{ +	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7, +		sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx); +} + +/* timer interrupt handler */ +static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id) +{ +	struct clock_event_device *ce = dev_id; +	int cpu = smp_processor_id(); + +	/* clear timer interrupt */ +	writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); + +	if (ce->mode == CLOCK_EVT_MODE_ONESHOT) +		sirfsoc_timer_count_disable(cpu); + +	ce->event_handler(ce); + +	return IRQ_HANDLED; +} + +/* read 64-bit timer counter */ +static cycle_t sirfsoc_timer_read(struct clocksource *cs) +{ +	u64 cycles; + +	writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) | +			BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); + +	cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI); +	cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO); + +	return cycles; +} + +static int sirfsoc_timer_set_next_event(unsigned long delta, +	struct clock_event_device *ce) +{ +	int cpu = smp_processor_id(); + +	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 + +		4 * cpu); +	writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 + +		4 * cpu); + +	/* enable the tick */ +	sirfsoc_timer_count_enable(cpu); + +	return 0; +} + +static void sirfsoc_timer_set_mode(enum clock_event_mode mode, +	struct clock_event_device *ce) +{ +	switch (mode) { +	case CLOCK_EVT_MODE_ONESHOT: +		/* enable in set_next_event */ +		break; +	default: +		break; +	} + +	sirfsoc_timer_count_disable(smp_processor_id()); +} + +static void sirfsoc_clocksource_suspend(struct clocksource *cs) +{ +	int i; + +	for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++) +		sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); +} + +static void sirfsoc_clocksource_resume(struct clocksource *cs) +{ +	int i; + +	for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++) +		writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); + +	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], +		sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO); +	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], +		sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI); + +	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) | +		BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); +} + +static struct clock_event_device sirfsoc_clockevent = { +	.name = "sirfsoc_clockevent", +	.rating = 200, +	.features = CLOCK_EVT_FEAT_ONESHOT, +	.set_mode = sirfsoc_timer_set_mode, +	.set_next_event = sirfsoc_timer_set_next_event, +}; + +static struct clocksource sirfsoc_clocksource = { +	.name = "sirfsoc_clocksource", +	.rating = 200, +	.mask = CLOCKSOURCE_MASK(64), +	.flags = CLOCK_SOURCE_IS_CONTINUOUS, +	.read = sirfsoc_timer_read, +	.suspend = sirfsoc_clocksource_suspend, +	.resume = sirfsoc_clocksource_resume, +}; + +static struct irqaction sirfsoc_timer_irq = { +	.name = "sirfsoc_timer0", +	.flags = IRQF_TIMER | IRQF_NOBALANCING, +	.handler = sirfsoc_timer_interrupt, +	.dev_id = &sirfsoc_clockevent, +}; + +#ifdef CONFIG_LOCAL_TIMERS + +static struct irqaction sirfsoc_timer1_irq = { +	.name = "sirfsoc_timer1", +	.flags = IRQF_TIMER | IRQF_NOBALANCING, +	.handler = sirfsoc_timer_interrupt, +}; + +static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce) +{ +	/* Use existing clock_event for cpu 0 */ +	if (!smp_processor_id()) +		return 0; + +	ce->irq = sirfsoc_timer1_irq.irq; +	ce->name = "local_timer"; +	ce->features = sirfsoc_clockevent.features; +	ce->rating = sirfsoc_clockevent.rating; +	ce->set_mode = sirfsoc_timer_set_mode; +	ce->set_next_event = sirfsoc_timer_set_next_event; +	ce->shift = sirfsoc_clockevent.shift; +	ce->mult = sirfsoc_clockevent.mult; +	ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns; +	ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns; + +	sirfsoc_timer1_irq.dev_id = ce; +	BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq)); +	irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1)); + +	clockevents_register_device(ce); +	return 0; +} + +static void sirfsoc_local_timer_stop(struct clock_event_device *ce) +{ +	sirfsoc_timer_count_disable(1); + +	remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq); +} + +static struct local_timer_ops sirfsoc_local_timer_ops __cpuinitdata = { +	.setup	= sirfsoc_local_timer_setup, +	.stop	= sirfsoc_local_timer_stop, +}; +#endif /* CONFIG_LOCAL_TIMERS */ + +static void __init sirfsoc_clockevent_init(void) +{ +	clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60); + +	sirfsoc_clockevent.max_delta_ns = +		clockevent_delta2ns(-2, &sirfsoc_clockevent); +	sirfsoc_clockevent.min_delta_ns = +		clockevent_delta2ns(2, &sirfsoc_clockevent); + +	sirfsoc_clockevent.cpumask = cpumask_of(0); +	clockevents_register_device(&sirfsoc_clockevent); +#ifdef CONFIG_LOCAL_TIMERS +	local_timer_register(&sirfsoc_local_timer_ops); +#endif +} + +/* initialize the kernel jiffy timer source */ +static void __init sirfsoc_marco_timer_init(void) +{ +	unsigned long rate; +	u32 timer_div; +	struct clk *clk; + +	/* timer's input clock is io clock */ +	clk = clk_get_sys("io", NULL); + +	BUG_ON(IS_ERR(clk)); +	rate = clk_get_rate(clk); + +	BUG_ON(rate < CLOCK_TICK_RATE); +	BUG_ON(rate % CLOCK_TICK_RATE); + +	/* Initialize the timer dividers */ +	timer_div = rate / CLOCK_TICK_RATE - 1; +	writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); +	writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL); +	writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL); + +	/* Initialize timer counters to 0 */ +	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO); +	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI); +	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) | +		BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); +	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0); +	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1); + +	/* Clear all interrupts */ +	writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); + +	BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE)); + +	BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq)); + +	sirfsoc_clockevent_init(); +} + +static void __init sirfsoc_of_timer_init(struct device_node *np) +{ +	sirfsoc_timer_base = of_iomap(np, 0); +	if (!sirfsoc_timer_base) +		panic("unable to map timer cpu registers\n"); + +	sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0); +	if (!sirfsoc_timer_irq.irq) +		panic("No irq passed for timer0 via DT\n"); + +#ifdef CONFIG_LOCAL_TIMERS +	sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1); +	if (!sirfsoc_timer1_irq.irq) +		panic("No irq passed for timer1 via DT\n"); +#endif + +	sirfsoc_marco_timer_init(); +} +CLOCKSOURCE_OF_DECLARE(sirfsoc_marco_timer, "sirf,marco-tick", sirfsoc_of_timer_init ); diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c new file mode 100644 index 00000000000..760882665d7 --- /dev/null +++ b/drivers/clocksource/timer-prima2.c @@ -0,0 +1,215 @@ +/* + * System timer for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/clockchips.h> +#include <linux/clocksource.h> +#include <linux/bitops.h> +#include <linux/irq.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <asm/sched_clock.h> +#include <asm/mach/time.h> + +#define SIRFSOC_TIMER_COUNTER_LO	0x0000 +#define SIRFSOC_TIMER_COUNTER_HI	0x0004 +#define SIRFSOC_TIMER_MATCH_0		0x0008 +#define SIRFSOC_TIMER_MATCH_1		0x000C +#define SIRFSOC_TIMER_MATCH_2		0x0010 +#define SIRFSOC_TIMER_MATCH_3		0x0014 +#define SIRFSOC_TIMER_MATCH_4		0x0018 +#define SIRFSOC_TIMER_MATCH_5		0x001C +#define SIRFSOC_TIMER_STATUS		0x0020 +#define SIRFSOC_TIMER_INT_EN		0x0024 +#define SIRFSOC_TIMER_WATCHDOG_EN	0x0028 +#define SIRFSOC_TIMER_DIV		0x002C +#define SIRFSOC_TIMER_LATCH		0x0030 +#define SIRFSOC_TIMER_LATCHED_LO	0x0034 +#define SIRFSOC_TIMER_LATCHED_HI	0x0038 + +#define SIRFSOC_TIMER_WDT_INDEX		5 + +#define SIRFSOC_TIMER_LATCH_BIT	 BIT(0) + +#define SIRFSOC_TIMER_REG_CNT 11 + +static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = { +	SIRFSOC_TIMER_MATCH_0, SIRFSOC_TIMER_MATCH_1, SIRFSOC_TIMER_MATCH_2, +	SIRFSOC_TIMER_MATCH_3, SIRFSOC_TIMER_MATCH_4, SIRFSOC_TIMER_MATCH_5, +	SIRFSOC_TIMER_INT_EN, SIRFSOC_TIMER_WATCHDOG_EN, SIRFSOC_TIMER_DIV, +	SIRFSOC_TIMER_LATCHED_LO, SIRFSOC_TIMER_LATCHED_HI, +}; + +static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT]; + +static void __iomem *sirfsoc_timer_base; + +/* timer0 interrupt handler */ +static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id) +{ +	struct clock_event_device *ce = dev_id; + +	WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0))); + +	/* clear timer0 interrupt */ +	writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS); + +	ce->event_handler(ce); + +	return IRQ_HANDLED; +} + +/* read 64-bit timer counter */ +static cycle_t sirfsoc_timer_read(struct clocksource *cs) +{ +	u64 cycles; + +	/* latch the 64-bit timer counter */ +	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); +	cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI); +	cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); + +	return cycles; +} + +static int sirfsoc_timer_set_next_event(unsigned long delta, +	struct clock_event_device *ce) +{ +	unsigned long now, next; + +	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); +	now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); +	next = now + delta; +	writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0); +	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); +	now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO); + +	return next - now > delta ? -ETIME : 0; +} + +static void sirfsoc_timer_set_mode(enum clock_event_mode mode, +	struct clock_event_device *ce) +{ +	u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); +	switch (mode) { +	case CLOCK_EVT_MODE_PERIODIC: +		WARN_ON(1); +		break; +	case CLOCK_EVT_MODE_ONESHOT: +		writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); +		break; +	case CLOCK_EVT_MODE_SHUTDOWN: +		writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN); +		break; +	case CLOCK_EVT_MODE_UNUSED: +	case CLOCK_EVT_MODE_RESUME: +		break; +	} +} + +static void sirfsoc_clocksource_suspend(struct clocksource *cs) +{ +	int i; + +	writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH); + +	for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++) +		sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); +} + +static void sirfsoc_clocksource_resume(struct clocksource *cs) +{ +	int i; + +	for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++) +		writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); + +	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO); +	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI); +} + +static struct clock_event_device sirfsoc_clockevent = { +	.name = "sirfsoc_clockevent", +	.rating = 200, +	.features = CLOCK_EVT_FEAT_ONESHOT, +	.set_mode = sirfsoc_timer_set_mode, +	.set_next_event = sirfsoc_timer_set_next_event, +}; + +static struct clocksource sirfsoc_clocksource = { +	.name = "sirfsoc_clocksource", +	.rating = 200, +	.mask = CLOCKSOURCE_MASK(64), +	.flags = CLOCK_SOURCE_IS_CONTINUOUS, +	.read = sirfsoc_timer_read, +	.suspend = sirfsoc_clocksource_suspend, +	.resume = sirfsoc_clocksource_resume, +}; + +static struct irqaction sirfsoc_timer_irq = { +	.name = "sirfsoc_timer0", +	.flags = IRQF_TIMER, +	.irq = 0, +	.handler = sirfsoc_timer_interrupt, +	.dev_id = &sirfsoc_clockevent, +}; + +/* Overwrite weak default sched_clock with more precise one */ +static u32 notrace sirfsoc_read_sched_clock(void) +{ +	return (u32)(sirfsoc_timer_read(NULL) & 0xffffffff); +} + +static void __init sirfsoc_clockevent_init(void) +{ +	sirfsoc_clockevent.cpumask = cpumask_of(0); +	clockevents_config_and_register(&sirfsoc_clockevent, CLOCK_TICK_RATE, +					2, -2); +} + +/* initialize the kernel jiffy timer source */ +static void __init sirfsoc_prima2_timer_init(struct device_node *np) +{ +	unsigned long rate; +	struct clk *clk; + +	/* timer's input clock is io clock */ +	clk = clk_get_sys("io", NULL); + +	BUG_ON(IS_ERR(clk)); + +	rate = clk_get_rate(clk); + +	BUG_ON(rate < CLOCK_TICK_RATE); +	BUG_ON(rate % CLOCK_TICK_RATE); + +	sirfsoc_timer_base = of_iomap(np, 0); +	if (!sirfsoc_timer_base) +		panic("unable to map timer cpu registers\n"); + +	sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0); + +	writel_relaxed(rate / CLOCK_TICK_RATE / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV); +	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO); +	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI); +	writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS); + +	BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE)); + +	setup_sched_clock(sirfsoc_read_sched_clock, 32, CLOCK_TICK_RATE); + +	BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq)); + +	sirfsoc_clockevent_init(); +} +CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer, "sirf,prima2-tick", sirfsoc_prima2_timer_init); |