summaryrefslogtreecommitdiff
path: root/drivers/clocksource
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig7
-rw-r--r--drivers/clocksource/Makefile2
-rw-r--r--drivers/clocksource/arm_arch_timer.c64
-rw-r--r--drivers/clocksource/cadence_ttc_timer.c15
-rw-r--r--drivers/clocksource/meson6_timer.c167
-rw-r--r--drivers/clocksource/tcb_clksrc.c15
-rw-r--r--drivers/clocksource/timer-atmel-pit.c296
-rw-r--r--drivers/clocksource/timer-marco.c5
-rw-r--r--drivers/clocksource/vf_pit_timer.c4
9 files changed, 553 insertions, 22 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index cfd6519df661..90420600e1eb 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -30,6 +30,9 @@ config ARMADA_370_XP_TIMER
bool
select CLKSRC_OF
+config MESON6_TIMER
+ bool
+
config ORION_TIMER
select CLKSRC_OF
select CLKSRC_MMIO
@@ -120,6 +123,10 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
help
Use ARM global timer clock source as sched_clock
+config ATMEL_PIT
+ select CLKSRC_OF if OF
+ def_bool SOC_AT91SAM9 || SOC_SAMA5
+
config CLKSRC_METAG_GENERIC
def_bool y if METAG
help
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 7fd9fd1dff42..756f6f10efa0 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o
+obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o
obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
@@ -25,6 +26,7 @@ obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
obj-$(CONFIG_ARCH_U300) += timer-u300.o
obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o
obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o
+obj-$(CONFIG_MESON6_TIMER) += meson6_timer.o
obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o
obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 5163ec13429d..2133f9d59d06 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -299,6 +299,21 @@ static void __arch_timer_setup(unsigned type,
clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff);
}
+static void arch_timer_evtstrm_enable(int divider)
+{
+ u32 cntkctl = arch_timer_get_cntkctl();
+
+ cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK;
+ /* Set the divider and enable virtual event stream */
+ cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT)
+ | ARCH_TIMER_VIRT_EVT_EN;
+ arch_timer_set_cntkctl(cntkctl);
+ elf_hwcap |= HWCAP_EVTSTRM;
+#ifdef CONFIG_COMPAT
+ compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM;
+#endif
+}
+
static void arch_timer_configure_evtstream(void)
{
int evt_stream_div, pos;
@@ -312,6 +327,23 @@ static void arch_timer_configure_evtstream(void)
arch_timer_evtstrm_enable(min(pos, 15));
}
+static void arch_counter_set_user_access(void)
+{
+ u32 cntkctl = arch_timer_get_cntkctl();
+
+ /* Disable user access to the timers and the physical counter */
+ /* Also disable virtual event stream */
+ cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
+ | ARCH_TIMER_USR_VT_ACCESS_EN
+ | ARCH_TIMER_VIRT_EVT_EN
+ | ARCH_TIMER_USR_PCT_ACCESS_EN);
+
+ /* Enable user access to the virtual counter */
+ cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN;
+
+ arch_timer_set_cntkctl(cntkctl);
+}
+
static int arch_timer_setup(struct clock_event_device *clk)
{
__arch_timer_setup(ARCH_CP15_TIMER, clk);
@@ -429,11 +461,19 @@ static void __init arch_counter_register(unsigned type)
u64 start_count;
/* Register the CP15 based counter if we have one */
- if (type & ARCH_CP15_TIMER)
+ if (type & ARCH_CP15_TIMER) {
arch_timer_read_counter = arch_counter_get_cntvct;
- else
+ } else {
arch_timer_read_counter = arch_counter_get_cntvct_mem;
+ /* If the clocksource name is "arch_sys_counter" the
+ * VDSO will attempt to read the CP15-based counter.
+ * Ensure this does not happen when CP15-based
+ * counter is not available.
+ */
+ clocksource_counter.name = "arch_mem_counter";
+ }
+
start_count = arch_timer_read_counter();
clocksource_register_hz(&clocksource_counter, arch_timer_rate);
cyclecounter.mult = clocksource_counter.mult;
@@ -616,17 +656,29 @@ static const struct of_device_id arch_timer_mem_of_match[] __initconst = {
{},
};
+static bool __init
+arch_timer_probed(int type, const struct of_device_id *matches)
+{
+ struct device_node *dn;
+ bool probed = false;
+
+ dn = of_find_matching_node(NULL, matches);
+ if (dn && of_device_is_available(dn) && (arch_timers_present & type))
+ probed = true;
+ of_node_put(dn);
+
+ return probed;
+}
+
static void __init arch_timer_common_init(void)
{
unsigned mask = ARCH_CP15_TIMER | ARCH_MEM_TIMER;
/* Wait until both nodes are probed if we have two timers */
if ((arch_timers_present & mask) != mask) {
- if (of_find_matching_node(NULL, arch_timer_mem_of_match) &&
- !(arch_timers_present & ARCH_MEM_TIMER))
+ if (!arch_timer_probed(ARCH_MEM_TIMER, arch_timer_mem_of_match))
return;
- if (of_find_matching_node(NULL, arch_timer_of_match) &&
- !(arch_timers_present & ARCH_CP15_TIMER))
+ if (!arch_timer_probed(ARCH_CP15_TIMER, arch_timer_of_match))
return;
}
diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
index 7a08811df9aa..510c8a1d37b3 100644
--- a/drivers/clocksource/cadence_ttc_timer.c
+++ b/drivers/clocksource/cadence_ttc_timer.c
@@ -25,7 +25,7 @@
#include <linux/sched_clock.h>
/*
- * This driver configures the 2 16-bit count-up timers as follows:
+ * This driver configures the 2 16/32-bit count-up timers as follows:
*
* T1: Timer 1, clocksource for generic timekeeping
* T2: Timer 2, clockevent source for hrtimers
@@ -321,7 +321,8 @@ static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
return NOTIFY_DONE;
}
-static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
+static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base,
+ u32 timer_width)
{
struct ttc_timer_clocksource *ttccs;
int err;
@@ -351,7 +352,7 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
ttccs->cs.name = "ttc_clocksource";
ttccs->cs.rating = 200;
ttccs->cs.read = __ttc_clocksource_read;
- ttccs->cs.mask = CLOCKSOURCE_MASK(16);
+ ttccs->cs.mask = CLOCKSOURCE_MASK(timer_width);
ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
/*
@@ -372,7 +373,8 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
}
ttc_sched_clock_val_reg = base + TTC_COUNT_VAL_OFFSET;
- sched_clock_register(ttc_sched_clock_read, 16, ttccs->ttc.freq / PRESCALE);
+ sched_clock_register(ttc_sched_clock_read, timer_width,
+ ttccs->ttc.freq / PRESCALE);
}
static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
@@ -467,6 +469,7 @@ static void __init ttc_timer_init(struct device_node *timer)
struct clk *clk_cs, *clk_ce;
static int initialized;
int clksel;
+ u32 timer_width = 16;
if (initialized)
return;
@@ -490,6 +493,8 @@ static void __init ttc_timer_init(struct device_node *timer)
BUG();
}
+ of_property_read_u32(timer, "timer-width", &timer_width);
+
clksel = readl_relaxed(timer_baseaddr + TTC_CLK_CNTRL_OFFSET);
clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
clk_cs = of_clk_get(timer, clksel);
@@ -506,7 +511,7 @@ static void __init ttc_timer_init(struct device_node *timer)
BUG();
}
- ttc_setup_clocksource(clk_cs, timer_baseaddr);
+ ttc_setup_clocksource(clk_cs, timer_baseaddr, timer_width);
ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq);
pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
diff --git a/drivers/clocksource/meson6_timer.c b/drivers/clocksource/meson6_timer.c
new file mode 100644
index 000000000000..5c15cba41dca
--- /dev/null
+++ b/drivers/clocksource/meson6_timer.c
@@ -0,0 +1,167 @@
+/*
+ * Amlogic Meson6 SoCs timer handling.
+ *
+ * Copyright (C) 2014 Carlo Caione <carlo@caione.org>
+ *
+ * Based on code from Amlogic, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/sched_clock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define CED_ID 0
+#define CSD_ID 4
+
+#define TIMER_ISA_MUX 0
+#define TIMER_ISA_VAL(t) (((t) + 1) << 2)
+
+#define TIMER_INPUT_BIT(t) (2 * (t))
+#define TIMER_ENABLE_BIT(t) (16 + (t))
+#define TIMER_PERIODIC_BIT(t) (12 + (t))
+
+#define TIMER_CED_INPUT_MASK (3UL << TIMER_INPUT_BIT(CED_ID))
+#define TIMER_CSD_INPUT_MASK (7UL << TIMER_INPUT_BIT(CSD_ID))
+
+#define TIMER_CED_UNIT_1US 0
+#define TIMER_CSD_UNIT_1US 1
+
+static void __iomem *timer_base;
+
+static u64 notrace meson6_timer_sched_read(void)
+{
+ return (u64)readl(timer_base + TIMER_ISA_VAL(CSD_ID));
+}
+
+static void meson6_clkevt_time_stop(unsigned char timer)
+{
+ u32 val = readl(timer_base + TIMER_ISA_MUX);
+
+ writel(val & ~TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
+}
+
+static void meson6_clkevt_time_setup(unsigned char timer, unsigned long delay)
+{
+ writel(delay, timer_base + TIMER_ISA_VAL(timer));
+}
+
+static void meson6_clkevt_time_start(unsigned char timer, bool periodic)
+{
+ u32 val = readl(timer_base + TIMER_ISA_MUX);
+
+ if (periodic)
+ val |= TIMER_PERIODIC_BIT(timer);
+ else
+ val &= ~TIMER_PERIODIC_BIT(timer);
+
+ writel(val | TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
+}
+
+static void meson6_clkevt_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ meson6_clkevt_time_stop(CED_ID);
+ meson6_clkevt_time_setup(CED_ID, USEC_PER_SEC/HZ - 1);
+ meson6_clkevt_time_start(CED_ID, true);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ meson6_clkevt_time_stop(CED_ID);
+ meson6_clkevt_time_start(CED_ID, false);
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ default:
+ meson6_clkevt_time_stop(CED_ID);
+ break;
+ }
+}
+
+static int meson6_clkevt_next_event(unsigned long evt,
+ struct clock_event_device *unused)
+{
+ meson6_clkevt_time_stop(CED_ID);
+ meson6_clkevt_time_setup(CED_ID, evt);
+ meson6_clkevt_time_start(CED_ID, false);
+
+ return 0;
+}
+
+static struct clock_event_device meson6_clockevent = {
+ .name = "meson6_tick",
+ .rating = 400,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = meson6_clkevt_mode,
+ .set_next_event = meson6_clkevt_next_event,
+};
+
+static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = (struct clock_event_device *)dev_id;
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction meson6_timer_irq = {
+ .name = "meson6_timer",
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = meson6_timer_interrupt,
+ .dev_id = &meson6_clockevent,
+};
+
+static void __init meson6_timer_init(struct device_node *node)
+{
+ u32 val;
+ int ret, irq;
+
+ timer_base = of_io_request_and_map(node, 0, "meson6-timer");
+ if (IS_ERR(timer_base))
+ panic("Can't map registers");
+
+ irq = irq_of_parse_and_map(node, 0);
+ if (irq <= 0)
+ panic("Can't parse IRQ");
+
+ /* Set 1us for timer E */
+ val = readl(timer_base + TIMER_ISA_MUX);
+ val &= ~TIMER_CSD_INPUT_MASK;
+ val |= TIMER_CSD_UNIT_1US << TIMER_INPUT_BIT(CSD_ID);
+ writel(val, timer_base + TIMER_ISA_MUX);
+
+ sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC);
+ clocksource_mmio_init(timer_base + TIMER_ISA_VAL(CSD_ID), node->name,
+ 1000 * 1000, 300, 32, clocksource_mmio_readl_up);
+
+ /* Timer A base 1us */
+ val &= ~TIMER_CED_INPUT_MASK;
+ val |= TIMER_CED_UNIT_1US << TIMER_INPUT_BIT(CED_ID);
+ writel(val, timer_base + TIMER_ISA_MUX);
+
+ /* Stop the timer A */
+ meson6_clkevt_time_stop(CED_ID);
+
+ ret = setup_irq(irq, &meson6_timer_irq);
+ if (ret)
+ pr_warn("failed to setup irq %d\n", irq);
+
+ meson6_clockevent.cpumask = cpu_possible_mask;
+ meson6_clockevent.irq = irq;
+
+ clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC,
+ 1, 0xfffe);
+}
+CLOCKSOURCE_OF_DECLARE(meson6, "amlogic,meson6-timer",
+ meson6_timer_init);
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
index a8d7ea14f183..8bdbc45c6dad 100644
--- a/drivers/clocksource/tcb_clksrc.c
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -178,12 +178,6 @@ static irqreturn_t ch2_irq(int irq, void *handle)
return IRQ_NONE;
}
-static struct irqaction tc_irqaction = {
- .name = "tc_clkevt",
- .flags = IRQF_TIMER,
- .handler = ch2_irq,
-};
-
static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
{
int ret;
@@ -198,15 +192,16 @@ static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
clkevt.regs = tc->regs;
clkevt.clk = t2_clk;
- tc_irqaction.dev_id = &clkevt;
timer_clock = clk32k_divisor_idx;
clkevt.clkevt.cpumask = cpumask_of(0);
- ret = setup_irq(irq, &tc_irqaction);
- if (ret)
+ ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt);
+ if (ret) {
+ clk_disable_unprepare(t2_clk);
return ret;
+ }
clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff);
@@ -279,7 +274,7 @@ static int __init tcb_clksrc_init(void)
int i;
int ret;
- tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK, clksrc.name);
+ tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK);
if (!tc) {
pr_debug("can't alloc TC for clocksource\n");
return -ENODEV;
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
new file mode 100644
index 000000000000..d5289098b3df
--- /dev/null
+++ b/drivers/clocksource/timer-atmel-pit.c
@@ -0,0 +1,296 @@
+/*
+ * at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x
+ *
+ * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France
+ * Revision 2005 M. Nicolas Diremdjian, ATMEL Rousset, France
+ * Converted to ClockSource/ClockEvents by David Brownell.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "AT91: PIT: " fmt
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+
+#define AT91_PIT_MR 0x00 /* Mode Register */
+#define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */
+#define AT91_PIT_PITEN BIT(24) /* Timer Enabled */
+#define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */
+
+#define AT91_PIT_SR 0x04 /* Status Register */
+#define AT91_PIT_PITS BIT(0) /* Timer Status */
+
+#define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */
+#define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */
+#define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */
+#define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */
+
+#define PIT_CPIV(x) ((x) & AT91_PIT_CPIV)
+#define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20)
+
+struct pit_data {
+ struct clock_event_device clkevt;
+ struct clocksource clksrc;
+
+ void __iomem *base;
+ u32 cycle;
+ u32 cnt;
+ unsigned int irq;
+ struct clk *mck;
+};
+
+static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc)
+{
+ return container_of(clksrc, struct pit_data, clksrc);
+}
+
+static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt)
+{
+ return container_of(clkevt, struct pit_data, clkevt);
+}
+
+static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset)
+{
+ return __raw_readl(base + reg_offset);
+}
+
+static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value)
+{
+ __raw_writel(value, base + reg_offset);
+}
+
+/*
+ * Clocksource: just a monotonic counter of MCK/16 cycles.
+ * We don't care whether or not PIT irqs are enabled.
+ */
+static cycle_t read_pit_clk(struct clocksource *cs)
+{
+ struct pit_data *data = clksrc_to_pit_data(cs);
+ unsigned long flags;
+ u32 elapsed;
+ u32 t;
+
+ raw_local_irq_save(flags);
+ elapsed = data->cnt;
+ t = pit_read(data->base, AT91_PIT_PIIR);
+ raw_local_irq_restore(flags);
+
+ elapsed += PIT_PICNT(t) * data->cycle;
+ elapsed += PIT_CPIV(t);
+ return elapsed;
+}
+
+/*
+ * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16)
+ */
+static void
+pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
+{
+ struct pit_data *data = clkevt_to_pit_data(dev);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ /* update clocksource counter */
+ data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
+ pit_write(data->base, AT91_PIT_MR,
+ (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ BUG();
+ /* FALLTHROUGH */
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ /* disable irq, leaving the clocksource active */
+ pit_write(data->base, AT91_PIT_MR,
+ (data->cycle - 1) | AT91_PIT_PITEN);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ break;
+ }
+}
+
+static void at91sam926x_pit_suspend(struct clock_event_device *cedev)
+{
+ struct pit_data *data = clkevt_to_pit_data(cedev);
+
+ /* Disable timer */
+ pit_write(data->base, AT91_PIT_MR, 0);
+}
+
+static void at91sam926x_pit_reset(struct pit_data *data)
+{
+ /* Disable timer and irqs */
+ pit_write(data->base, AT91_PIT_MR, 0);
+
+ /* Clear any pending interrupts, wait for PIT to stop counting */
+ while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0)
+ cpu_relax();
+
+ /* Start PIT but don't enable IRQ */
+ pit_write(data->base, AT91_PIT_MR,
+ (data->cycle - 1) | AT91_PIT_PITEN);
+}
+
+static void at91sam926x_pit_resume(struct clock_event_device *cedev)
+{
+ struct pit_data *data = clkevt_to_pit_data(cedev);
+
+ at91sam926x_pit_reset(data);
+}
+
+/*
+ * IRQ handler for the timer.
+ */
+static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
+{
+ struct pit_data *data = dev_id;
+
+ /*
+ * irqs should be disabled here, but as the irq is shared they are only
+ * guaranteed to be off if the timer irq is registered first.
+ */
+ WARN_ON_ONCE(!irqs_disabled());
+
+ /* The PIT interrupt may be disabled, and is shared */
+ if ((data->clkevt.mode == CLOCK_EVT_MODE_PERIODIC) &&
+ (pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) {
+ unsigned nr_ticks;
+
+ /* Get number of ticks performed before irq, and ack it */
+ nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
+ do {
+ data->cnt += data->cycle;
+ data->clkevt.event_handler(&data->clkevt);
+ nr_ticks--;
+ } while (nr_ticks);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+/*
+ * Set up both clocksource and clockevent support.
+ */
+static void __init at91sam926x_pit_common_init(struct pit_data *data)
+{
+ unsigned long pit_rate;
+ unsigned bits;
+ int ret;
+
+ /*
+ * Use our actual MCK to figure out how many MCK/16 ticks per
+ * 1/HZ period (instead of a compile-time constant LATCH).
+ */
+ pit_rate = clk_get_rate(data->mck) / 16;
+ data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ);
+ WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0);
+
+ /* Initialize and enable the timer */
+ at91sam926x_pit_reset(data);
+
+ /*
+ * Register clocksource. The high order bits of PIV are unused,
+ * so this isn't a 32-bit counter unless we get clockevent irqs.
+ */
+ bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */;
+ data->clksrc.mask = CLOCKSOURCE_MASK(bits);
+ data->clksrc.name = "pit";
+ data->clksrc.rating = 175;
+ data->clksrc.read = read_pit_clk,
+ data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ clocksource_register_hz(&data->clksrc, pit_rate);
+
+ /* Set up irq handler */
+ ret = request_irq(data->irq, at91sam926x_pit_interrupt,
+ IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
+ "at91_tick", data);
+ if (ret)
+ panic(pr_fmt("Unable to setup IRQ\n"));
+
+ /* Set up and register clockevents */
+ data->clkevt.name = "pit";
+ data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
+ data->clkevt.shift = 32;
+ data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift);
+ data->clkevt.rating = 100;
+ data->clkevt.cpumask = cpumask_of(0);
+
+ data->clkevt.set_mode = pit_clkevt_mode;
+ data->clkevt.resume = at91sam926x_pit_resume;
+ data->clkevt.suspend = at91sam926x_pit_suspend;
+ clockevents_register_device(&data->clkevt);
+}
+
+static void __init at91sam926x_pit_dt_init(struct device_node *node)
+{
+ struct pit_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ panic(pr_fmt("Unable to allocate memory\n"));
+
+ data->base = of_iomap(node, 0);
+ if (!data->base)
+ panic(pr_fmt("Could not map PIT address\n"));
+
+ data->mck = of_clk_get(node, 0);
+ if (IS_ERR(data->mck))
+ /* Fallback on clkdev for !CCF-based boards */
+ data->mck = clk_get(NULL, "mck");
+
+ if (IS_ERR(data->mck))
+ panic(pr_fmt("Unable to get mck clk\n"));
+
+ /* Get the interrupts property */
+ data->irq = irq_of_parse_and_map(node, 0);
+ if (!data->irq)
+ panic(pr_fmt("Unable to get IRQ from DT\n"));
+
+ at91sam926x_pit_common_init(data);
+}
+CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
+ at91sam926x_pit_dt_init);
+
+static void __iomem *pit_base_addr;
+
+void __init at91sam926x_pit_init(int irq)
+{
+ struct pit_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ panic(pr_fmt("Unable to allocate memory\n"));
+
+ data->base = pit_base_addr;
+
+ data->mck = clk_get(NULL, "mck");
+ if (IS_ERR(data->mck))
+ panic(pr_fmt("Unable to get mck clk\n"));
+
+ data->irq = irq;
+
+ at91sam926x_pit_common_init(data);
+}
+
+void __init at91sam926x_ioremap_pit(u32 addr)
+{
+ if (of_have_populated_dt())
+ return;
+
+ pit_base_addr = ioremap(addr, 16);
+
+ if (!pit_base_addr)
+ panic(pr_fmt("Impossible to ioremap PIT\n"));
+}
diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c
index 330e93064692..caf7a2030461 100644
--- a/drivers/clocksource/timer-marco.c
+++ b/drivers/clocksource/timer-marco.c
@@ -63,7 +63,7 @@ static inline void sirfsoc_timer_count_disable(int 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,
+ writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x3,
sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
}
@@ -103,6 +103,9 @@ static int sirfsoc_timer_set_next_event(unsigned long delta,
{
int cpu = smp_processor_id();
+ /* disable timer first, then modify the related registers */
+ sirfsoc_timer_count_disable(cpu);
+
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 +
4 * cpu);
writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 +
diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c
index a918bc481c52..b45ac6229b57 100644
--- a/drivers/clocksource/vf_pit_timer.c
+++ b/drivers/clocksource/vf_pit_timer.c
@@ -93,6 +93,10 @@ static void pit_set_mode(enum clock_event_mode mode,
case CLOCK_EVT_MODE_PERIODIC:
pit_set_next_event(cycle_per_jiffy, evt);
break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ pit_timer_disable();
+ break;
default:
break;
}