aboutsummaryrefslogtreecommitdiff
path: root/drivers/clocksource
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig16
-rw-r--r--drivers/clocksource/Makefile2
-rw-r--r--drivers/clocksource/arm_arch_timer.c6
-rw-r--r--drivers/clocksource/arm_global_timer.c5
-rw-r--r--drivers/clocksource/cadence_ttc_timer.c54
-rw-r--r--drivers/clocksource/clksrc-of.c2
-rw-r--r--drivers/clocksource/dw_apb_timer_of.c2
-rw-r--r--drivers/clocksource/em_sti.c4
-rw-r--r--drivers/clocksource/exynos_mct.c47
-rw-r--r--drivers/clocksource/fsl_ftm_timer.c367
-rw-r--r--drivers/clocksource/mmio.c8
-rw-r--r--drivers/clocksource/qcom-timer.c13
-rw-r--r--drivers/clocksource/sh_cmt.c4
-rw-r--r--drivers/clocksource/sh_mtu2.c490
-rw-r--r--drivers/clocksource/sh_tmu.c281
-rw-r--r--drivers/clocksource/tcb_clksrc.c8
-rw-r--r--drivers/clocksource/time-efm32.c3
-rw-r--r--drivers/clocksource/timer-marco.c10
-rw-r--r--drivers/clocksource/timer-prima2.c47
-rw-r--r--drivers/clocksource/timer-sun5i.c6
-rw-r--r--drivers/clocksource/versatile.c40
-rw-r--r--drivers/clocksource/zevio-timer.c7
22 files changed, 1097 insertions, 325 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 96918e1f26a..065131cbfcc 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -136,6 +136,11 @@ config CLKSRC_SAMSUNG_PWM
for all devicetree enabled platforms. This driver will be
needed only on systems that do not have the Exynos MCT available.
+config FSL_FTM_TIMER
+ bool
+ help
+ Support for Freescale FlexTimer Module (FTM) timer.
+
config VF_PIT_TIMER
bool
help
@@ -191,3 +196,14 @@ config EM_TIMER_STI
config CLKSRC_QCOM
bool
+
+config CLKSRC_VERSATILE
+ bool "ARM Versatile (Express) reference platforms clock source"
+ depends on GENERIC_SCHED_CLOCK && !ARCH_USES_GETTIMEOFFSET
+ select CLKSRC_OF
+ default y if MFD_VEXPRESS_SYSREG
+ help
+ This option enables clock source based on free running
+ counter available in the "System Registers" block of
+ ARM Versatile, RealView and Versatile Express reference
+ platforms.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 98cb6c51aa8..800b1303c23 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
+obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o
obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
@@ -39,3 +40,4 @@ obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o
+obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 57e823c44d2..5163ec13429 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -66,6 +66,7 @@ static int arch_timer_ppi[MAX_TIMER_PPI];
static struct clock_event_device __percpu *arch_timer_evt;
static bool arch_timer_use_virtual = true;
+static bool arch_timer_c3stop;
static bool arch_timer_mem_use_virtual;
/*
@@ -263,7 +264,8 @@ static void __arch_timer_setup(unsigned type,
clk->features = CLOCK_EVT_FEAT_ONESHOT;
if (type == ARCH_CP15_TIMER) {
- clk->features |= CLOCK_EVT_FEAT_C3STOP;
+ if (arch_timer_c3stop)
+ clk->features |= CLOCK_EVT_FEAT_C3STOP;
clk->name = "arch_sys_timer";
clk->rating = 450;
clk->cpumask = cpumask_of(smp_processor_id());
@@ -665,6 +667,8 @@ static void __init arch_timer_init(struct device_node *np)
}
}
+ arch_timer_c3stop = !of_property_read_bool(np, "always-on");
+
arch_timer_register();
arch_timer_common_init();
}
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
index 0fc31d029e5..60e5a170c4d 100644
--- a/drivers/clocksource/arm_global_timer.c
+++ b/drivers/clocksource/arm_global_timer.c
@@ -246,11 +246,12 @@ static void __init global_timer_of_register(struct device_node *np)
int err = 0;
/*
- * In r2p0 the comparators for each processor with the global timer
+ * In A9 r2p0 the comparators for each processor with the global timer
* fire when the timer value is greater than or equal to. In previous
* revisions the comparators fired when the timer value was equal to.
*/
- if ((read_cpuid_id() & 0xf0000f) < 0x200000) {
+ if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9
+ && (read_cpuid_id() & 0xf0000f) < 0x200000) {
pr_warn("global-timer: non support for this cpu version.\n");
return;
}
diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
index 49fbe2847c8..7a08811df9a 100644
--- a/drivers/clocksource/cadence_ttc_timer.c
+++ b/drivers/clocksource/cadence_ttc_timer.c
@@ -118,11 +118,11 @@ static void ttc_set_interval(struct ttc_timer *timer,
u32 ctrl_reg;
/* Disable the counter, set the counter value and re-enable counter */
- ctrl_reg = __raw_readl(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
+ ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
- __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
+ writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
- __raw_writel(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET);
+ writel_relaxed(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET);
/*
* Reset the counter (0x10) so that it starts from 0, one-shot
@@ -130,7 +130,7 @@ static void ttc_set_interval(struct ttc_timer *timer,
*/
ctrl_reg |= CNT_CNTRL_RESET;
ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
- __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
+ writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
}
/**
@@ -147,7 +147,7 @@ static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id)
struct ttc_timer *timer = &ttce->ttc;
/* Acknowledge the interrupt and call event handler */
- __raw_readl(timer->base_addr + TTC_ISR_OFFSET);
+ readl_relaxed(timer->base_addr + TTC_ISR_OFFSET);
ttce->ce.event_handler(&ttce->ce);
@@ -163,13 +163,13 @@ static cycle_t __ttc_clocksource_read(struct clocksource *cs)
{
struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc;
- return (cycle_t)__raw_readl(timer->base_addr +
+ return (cycle_t)readl_relaxed(timer->base_addr +
TTC_COUNT_VAL_OFFSET);
}
static u64 notrace ttc_sched_clock_read(void)
{
- return __raw_readl(ttc_sched_clock_val_reg);
+ return readl_relaxed(ttc_sched_clock_val_reg);
}
/**
@@ -211,17 +211,17 @@ static void ttc_set_mode(enum clock_event_mode mode,
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
- ctrl_reg = __raw_readl(timer->base_addr +
+ ctrl_reg = readl_relaxed(timer->base_addr +
TTC_CNT_CNTRL_OFFSET);
ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
- __raw_writel(ctrl_reg,
+ writel_relaxed(ctrl_reg,
timer->base_addr + TTC_CNT_CNTRL_OFFSET);
break;
case CLOCK_EVT_MODE_RESUME:
- ctrl_reg = __raw_readl(timer->base_addr +
+ ctrl_reg = readl_relaxed(timer->base_addr +
TTC_CNT_CNTRL_OFFSET);
ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
- __raw_writel(ctrl_reg,
+ writel_relaxed(ctrl_reg,
timer->base_addr + TTC_CNT_CNTRL_OFFSET);
break;
}
@@ -266,8 +266,8 @@ static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
* of an abort.
*/
ttccs->scale_clk_ctrl_reg_old =
- __raw_readl(ttccs->ttc.base_addr +
- TTC_CLK_CNTRL_OFFSET);
+ readl_relaxed(ttccs->ttc.base_addr +
+ TTC_CLK_CNTRL_OFFSET);
psv = (ttccs->scale_clk_ctrl_reg_old &
TTC_CLK_CNTRL_PSV_MASK) >>
@@ -291,8 +291,8 @@ static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
return NOTIFY_DONE;
/* scale up: adjust divider now - before frequency change */
- __raw_writel(ttccs->scale_clk_ctrl_reg_new,
- ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
+ writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
+ ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
break;
}
case POST_RATE_CHANGE:
@@ -301,8 +301,8 @@ static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
return NOTIFY_OK;
/* scale down: adjust divider now - after frequency change */
- __raw_writel(ttccs->scale_clk_ctrl_reg_new,
- ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
+ writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
+ ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
break;
case ABORT_RATE_CHANGE:
@@ -311,8 +311,8 @@ static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
return NOTIFY_OK;
/* restore original register value */
- __raw_writel(ttccs->scale_clk_ctrl_reg_old,
- ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
+ writel_relaxed(ttccs->scale_clk_ctrl_reg_old,
+ ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
/* fall through */
default:
return NOTIFY_DONE;
@@ -359,10 +359,10 @@ static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base)
* with no interrupt and it rolls over at 0xFFFF. Pre-scale
* it by 32 also. Let it start running now.
*/
- __raw_writel(0x0, ttccs->ttc.base_addr + TTC_IER_OFFSET);
- __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
+ writel_relaxed(0x0, ttccs->ttc.base_addr + TTC_IER_OFFSET);
+ writel_relaxed(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
- __raw_writel(CNT_CNTRL_RESET,
+ writel_relaxed(CNT_CNTRL_RESET,
ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
err = clocksource_register_hz(&ttccs->cs, ttccs->ttc.freq / PRESCALE);
@@ -438,10 +438,10 @@ static void __init ttc_setup_clockevent(struct clk *clk,
* is prescaled by 32 using the interval interrupt. Leave it
* disabled for now.
*/
- __raw_writel(0x23, ttcce->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
- __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
+ writel_relaxed(0x23, ttcce->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
+ writel_relaxed(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
- __raw_writel(0x1, ttcce->ttc.base_addr + TTC_IER_OFFSET);
+ writel_relaxed(0x1, ttcce->ttc.base_addr + TTC_IER_OFFSET);
err = request_irq(irq, ttc_clock_event_interrupt,
IRQF_TIMER, ttcce->ce.name, ttcce);
@@ -490,7 +490,7 @@ static void __init ttc_timer_init(struct device_node *timer)
BUG();
}
- clksel = __raw_readl(timer_baseaddr + TTC_CLK_CNTRL_OFFSET);
+ clksel = readl_relaxed(timer_baseaddr + TTC_CLK_CNTRL_OFFSET);
clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
clk_cs = of_clk_get(timer, clksel);
if (IS_ERR(clk_cs)) {
@@ -498,7 +498,7 @@ static void __init ttc_timer_init(struct device_node *timer)
BUG();
}
- clksel = __raw_readl(timer_baseaddr + 4 + TTC_CLK_CNTRL_OFFSET);
+ clksel = readl_relaxed(timer_baseaddr + 4 + TTC_CLK_CNTRL_OFFSET);
clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
clk_ce = of_clk_get(timer, clksel);
if (IS_ERR(clk_ce)) {
diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c
index ae2e4278c42..0093a8e49e1 100644
--- a/drivers/clocksource/clksrc-of.c
+++ b/drivers/clocksource/clksrc-of.c
@@ -27,7 +27,7 @@ void __init clocksource_of_init(void)
{
struct device_node *np;
const struct of_device_id *match;
- clocksource_of_init_fn init_func;
+ of_init_fn_1 init_func;
unsigned clocksources = 0;
for_each_matching_node_and_match(np, __clksrc_of_table, &match) {
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index 2a2ea2717f3..d305fb08976 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -106,7 +106,7 @@ static void __init add_clocksource(struct device_node *source_timer)
sched_rate = rate;
}
-static u64 read_sched_clock(void)
+static u64 notrace read_sched_clock(void)
{
return ~__raw_readl(sched_io_base);
}
diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c
index 9d170834fcf..d0a7bd66b8b 100644
--- a/drivers/clocksource/em_sti.c
+++ b/drivers/clocksource/em_sti.c
@@ -318,10 +318,8 @@ static int em_sti_probe(struct platform_device *pdev)
int irq;
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
- if (p == NULL) {
- dev_err(&pdev->dev, "failed to allocate driver data\n");
+ if (p == NULL)
return -ENOMEM;
- }
p->pdev = pdev;
platform_set_drvdata(pdev, p);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index a6ee6d7cd63..ab51bf20a3e 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -24,6 +24,7 @@
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/clocksource.h>
+#include <linux/sched_clock.h>
#define EXYNOS4_MCTREG(x) (x)
#define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100)
@@ -152,19 +153,16 @@ static void exynos4_mct_write(unsigned int value, unsigned long offset)
}
/* Clocksource handling */
-static void exynos4_mct_frc_start(u32 hi, u32 lo)
+static void exynos4_mct_frc_start(void)
{
u32 reg;
- exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L);
- exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U);
-
reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
reg |= MCT_G_TCON_START;
exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
}
-static cycle_t exynos4_frc_read(struct clocksource *cs)
+static cycle_t notrace _exynos4_frc_read(void)
{
unsigned int lo, hi;
u32 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
@@ -178,9 +176,14 @@ static cycle_t exynos4_frc_read(struct clocksource *cs)
return ((cycle_t)hi << 32) | lo;
}
+static cycle_t exynos4_frc_read(struct clocksource *cs)
+{
+ return _exynos4_frc_read();
+}
+
static void exynos4_frc_resume(struct clocksource *cs)
{
- exynos4_mct_frc_start(0, 0);
+ exynos4_mct_frc_start();
}
struct clocksource mct_frc = {
@@ -192,12 +195,30 @@ struct clocksource mct_frc = {
.resume = exynos4_frc_resume,
};
+static u64 notrace exynos4_read_sched_clock(void)
+{
+ return _exynos4_frc_read();
+}
+
+static struct delay_timer exynos4_delay_timer;
+
+static cycles_t exynos4_read_current_timer(void)
+{
+ return _exynos4_frc_read();
+}
+
static void __init exynos4_clocksource_init(void)
{
- exynos4_mct_frc_start(0, 0);
+ exynos4_mct_frc_start();
+
+ exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer;
+ exynos4_delay_timer.freq = clk_rate;
+ register_current_timer_delay(&exynos4_delay_timer);
if (clocksource_register_hz(&mct_frc, clk_rate))
panic("%s: can't register clocksource\n", mct_frc.name);
+
+ sched_clock_register(exynos4_read_sched_clock, 64, clk_rate);
}
static void exynos4_mct_comp0_stop(void)
@@ -416,8 +437,6 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt)
evt->set_mode = exynos4_tick_set_mode;
evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
evt->rating = 450;
- clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
- 0xf, 0x7fffffff);
exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
@@ -430,9 +449,12 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt)
evt->irq);
return -EIO;
}
+ irq_force_affinity(mct_irqs[MCT_L0_IRQ + cpu], cpumask_of(cpu));
} else {
enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
}
+ clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
+ 0xf, 0x7fffffff);
return 0;
}
@@ -450,7 +472,6 @@ static int exynos4_mct_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
struct mct_clock_event_device *mevt;
- unsigned int cpu;
/*
* Grab cpu pointer in each case to avoid spurious
@@ -461,12 +482,6 @@ static int exynos4_mct_cpu_notify(struct notifier_block *self,
mevt = this_cpu_ptr(&percpu_mct_tick);
exynos4_local_timer_setup(&mevt->evt);
break;
- case CPU_ONLINE:
- cpu = (unsigned long)hcpu;
- if (mct_int_type == MCT_INT_SPI)
- irq_set_affinity(mct_irqs[MCT_L0_IRQ + cpu],
- cpumask_of(cpu));
- break;
case CPU_DYING:
mevt = this_cpu_ptr(&percpu_mct_tick);
exynos4_local_timer_stop(&mevt->evt);
diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c
new file mode 100644
index 00000000000..454227d4f89
--- /dev/null
+++ b/drivers/clocksource/fsl_ftm_timer.c
@@ -0,0 +1,367 @@
+/*
+ * Freescale FlexTimer Module (FTM) timer driver.
+ *
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+#include <linux/slab.h>
+
+#define FTM_SC 0x00
+#define FTM_SC_CLK_SHIFT 3
+#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT)
+#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT)
+#define FTM_SC_PS_MASK 0x7
+#define FTM_SC_TOIE BIT(6)
+#define FTM_SC_TOF BIT(7)
+
+#define FTM_CNT 0x04
+#define FTM_MOD 0x08
+#define FTM_CNTIN 0x4C
+
+#define FTM_PS_MAX 7
+
+struct ftm_clock_device {
+ void __iomem *clksrc_base;
+ void __iomem *clkevt_base;
+ unsigned long periodic_cyc;
+ unsigned long ps;
+ bool big_endian;
+};
+
+static struct ftm_clock_device *priv;
+
+static inline u32 ftm_readl(void __iomem *addr)
+{
+ if (priv->big_endian)
+ return ioread32be(addr);
+ else
+ return ioread32(addr);
+}
+
+static inline void ftm_writel(u32 val, void __iomem *addr)
+{
+ if (priv->big_endian)
+ iowrite32be(val, addr);
+ else
+ iowrite32(val, addr);
+}
+
+static inline void ftm_counter_enable(void __iomem *base)
+{
+ u32 val;
+
+ /* select and enable counter clock source */
+ val = ftm_readl(base + FTM_SC);
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
+ val |= priv->ps | FTM_SC_CLK(1);
+ ftm_writel(val, base + FTM_SC);
+}
+
+static inline void ftm_counter_disable(void __iomem *base)
+{
+ u32 val;
+
+ /* disable counter clock source */
+ val = ftm_readl(base + FTM_SC);
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
+ ftm_writel(val, base + FTM_SC);
+}
+
+static inline void ftm_irq_acknowledge(void __iomem *base)
+{
+ u32 val;
+
+ val = ftm_readl(base + FTM_SC);
+ val &= ~FTM_SC_TOF;
+ ftm_writel(val, base + FTM_SC);
+}
+
+static inline void ftm_irq_enable(void __iomem *base)
+{
+ u32 val;
+
+ val = ftm_readl(base + FTM_SC);
+ val |= FTM_SC_TOIE;
+ ftm_writel(val, base + FTM_SC);
+}
+
+static inline void ftm_irq_disable(void __iomem *base)
+{
+ u32 val;
+
+ val = ftm_readl(base + FTM_SC);
+ val &= ~FTM_SC_TOIE;
+ ftm_writel(val, base + FTM_SC);
+}
+
+static inline void ftm_reset_counter(void __iomem *base)
+{
+ /*
+ * The CNT register contains the FTM counter value.
+ * Reset clears the CNT register. Writing any value to COUNT
+ * updates the counter with its initial value, CNTIN.
+ */
+ ftm_writel(0x00, base + FTM_CNT);
+}
+
+static u64 ftm_read_sched_clock(void)
+{
+ return ftm_readl(priv->clksrc_base + FTM_CNT);
+}
+
+static int ftm_set_next_event(unsigned long delta,
+ struct clock_event_device *unused)
+{
+ /*
+ * The CNNIN and MOD are all double buffer registers, writing
+ * to the MOD register latches the value into a buffer. The MOD
+ * register is updated with the value of its write buffer with
+ * the following scenario:
+ * a, the counter source clock is diabled.
+ */
+ ftm_counter_disable(priv->clkevt_base);
+
+ /* Force the value of CNTIN to be loaded into the FTM counter */
+ ftm_reset_counter(priv->clkevt_base);
+
+ /*
+ * The counter increments until the value of MOD is reached,
+ * at which point the counter is reloaded with the value of CNTIN.
+ * The TOF (the overflow flag) bit is set when the FTM counter
+ * changes from MOD to CNTIN. So we should using the delta - 1.
+ */
+ ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD);
+
+ ftm_counter_enable(priv->clkevt_base);
+
+ ftm_irq_enable(priv->clkevt_base);
+
+ return 0;
+}
+
+static void ftm_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ ftm_set_next_event(priv->periodic_cyc, evt);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ ftm_counter_disable(priv->clkevt_base);
+ break;
+ default:
+ return;
+ }
+}
+
+static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+
+ ftm_irq_acknowledge(priv->clkevt_base);
+
+ if (likely(evt->mode == CLOCK_EVT_MODE_ONESHOT)) {
+ ftm_irq_disable(priv->clkevt_base);
+ ftm_counter_disable(priv->clkevt_base);
+ }
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct clock_event_device ftm_clockevent = {
+ .name = "Freescale ftm timer",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = ftm_set_mode,
+ .set_next_event = ftm_set_next_event,
+ .rating = 300,
+};
+
+static struct irqaction ftm_timer_irq = {
+ .name = "Freescale ftm timer",
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = ftm_evt_interrupt,
+ .dev_id = &ftm_clockevent,
+};
+
+static int __init ftm_clockevent_init(unsigned long freq, int irq)
+{
+ int err;
+
+ ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN);
+ ftm_writel(~0UL, priv->clkevt_base + FTM_MOD);
+
+ ftm_reset_counter(priv->clkevt_base);
+
+ err = setup_irq(irq, &ftm_timer_irq);
+ if (err) {
+ pr_err("ftm: setup irq failed: %d\n", err);
+ return err;
+ }
+
+ ftm_clockevent.cpumask = cpumask_of(0);
+ ftm_clockevent.irq = irq;
+
+ clockevents_config_and_register(&ftm_clockevent,
+ freq / (1 << priv->ps),
+ 1, 0xffff);
+
+ ftm_counter_enable(priv->clkevt_base);
+
+ return 0;
+}
+
+static int __init ftm_clocksource_init(unsigned long freq)
+{
+ int err;
+
+ ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN);
+ ftm_writel(~0UL, priv->clksrc_base + FTM_MOD);
+
+ ftm_reset_counter(priv->clksrc_base);
+
+ sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps));
+ err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm",
+ freq / (1 << priv->ps), 300, 16,
+ clocksource_mmio_readl_up);
+ if (err) {
+ pr_err("ftm: init clock source mmio failed: %d\n", err);
+ return err;
+ }
+
+ ftm_counter_enable(priv->clksrc_base);
+
+ return 0;
+}
+
+static int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
+ char *ftm_name)
+{
+ struct clk *clk;
+ int err;
+
+ clk = of_clk_get_by_name(np, cnt_name);
+ if (IS_ERR(clk)) {
+ pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+ err = clk_prepare_enable(clk);
+ if (err) {
+ pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
+ cnt_name, err);
+ return err;
+ }
+
+ clk = of_clk_get_by_name(np, ftm_name);
+ if (IS_ERR(clk)) {
+ pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+ err = clk_prepare_enable(clk);
+ if (err)
+ pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
+ ftm_name, err);
+
+ return clk_get_rate(clk);
+}
+
+static unsigned long __init ftm_clk_init(struct device_node *np)
+{
+ unsigned long freq;
+
+ freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
+ if (freq <= 0)
+ return 0;
+
+ freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src");
+ if (freq <= 0)
+ return 0;
+
+ return freq;
+}
+
+static int __init ftm_calc_closest_round_cyc(unsigned long freq)
+{
+ priv->ps = 0;
+
+ /* The counter register is only using the lower 16 bits, and
+ * if the 'freq' value is to big here, then the periodic_cyc
+ * may exceed 0xFFFF.
+ */
+ do {
+ priv->periodic_cyc = DIV_ROUND_CLOSEST(freq,
+ HZ * (1 << priv->ps++));
+ } while (priv->periodic_cyc > 0xFFFF);
+
+ if (priv->ps > FTM_PS_MAX) {
+ pr_err("ftm: the prescaler is %lu > %d\n",
+ priv->ps, FTM_PS_MAX);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void __init ftm_timer_init(struct device_node *np)
+{
+ unsigned long freq;
+ int irq;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return;
+
+ priv->clkevt_base = of_iomap(np, 0);
+ if (!priv->clkevt_base) {
+ pr_err("ftm: unable to map event timer registers\n");
+ goto err;
+ }
+
+ priv->clksrc_base = of_iomap(np, 1);
+ if (!priv->clksrc_base) {
+ pr_err("ftm: unable to map source timer registers\n");
+ goto err;
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq <= 0) {
+ pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
+ goto err;
+ }
+
+ priv->big_endian = of_property_read_bool(np, "big-endian");
+
+ freq = ftm_clk_init(np);
+ if (!freq)
+ goto err;
+
+ if (ftm_calc_closest_round_cyc(freq))
+ goto err;
+
+ if (ftm_clocksource_init(freq))
+ goto err;
+
+ if (ftm_clockevent_init(freq, irq))
+ goto err;
+
+ return;
+
+err:
+ kfree(priv);
+}
+CLOCKSOURCE_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);
diff --git a/drivers/clocksource/mmio.c b/drivers/clocksource/mmio.c
index c0e25125a55..1593ade2a81 100644
--- a/drivers/clocksource/mmio.c
+++ b/drivers/clocksource/mmio.c
@@ -22,22 +22,22 @@ static inline struct clocksource_mmio *to_mmio_clksrc(struct clocksource *c)
cycle_t clocksource_mmio_readl_up(struct clocksource *c)
{
- return readl_relaxed(to_mmio_clksrc(c)->reg);
+ return (cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg);
}
cycle_t clocksource_mmio_readl_down(struct clocksource *c)
{
- return ~readl_relaxed(to_mmio_clksrc(c)->reg);
+ return ~(cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg) & c->mask;
}
cycle_t clocksource_mmio_readw_up(struct clocksource *c)
{
- return readw_relaxed(to_mmio_clksrc(c)->reg);
+ return (cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg);
}
cycle_t clocksource_mmio_readw_down(struct clocksource *c)
{
- return ~(unsigned)readw_relaxed(to_mmio_clksrc(c)->reg);
+ return ~(cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg) & c->mask;
}
/**
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
index e807acf4c66..8d115db1e65 100644
--- a/drivers/clocksource/qcom-timer.c
+++ b/drivers/clocksource/qcom-timer.c
@@ -26,6 +26,8 @@
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
+#include <asm/delay.h>
+
#define TIMER_MATCH_VAL 0x0000
#define TIMER_COUNT_VAL 0x0004
#define TIMER_ENABLE 0x0008
@@ -179,6 +181,15 @@ static u64 notrace msm_sched_clock_read(void)
return msm_clocksource.read(&msm_clocksource);
}
+static unsigned long msm_read_current_timer(void)
+{
+ return msm_clocksource.read(&msm_clocksource);
+}
+
+static struct delay_timer msm_delay_timer = {
+ .read_current_timer = msm_read_current_timer,
+};
+
static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
bool percpu)
{
@@ -217,6 +228,8 @@ err:
if (res)
pr_err("clocksource_register failed\n");
sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
+ msm_delay_timer.freq = dgt_hz;
+ register_current_timer_delay(&msm_delay_timer);
}
#ifdef CONFIG_ARCH_QCOM
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index bc8d025ce86..dfa780396b9 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -1106,10 +1106,8 @@ static int sh_cmt_probe(struct platform_device *pdev)
}
cmt = kzalloc(sizeof(*cmt), GFP_KERNEL);
- if (cmt == NULL) {
- dev_err(&pdev->dev, "failed to allocate driver data\n");
+ if (cmt == NULL)
return -ENOMEM;
- }
ret = sh_cmt_setup(cmt, pdev);
if (ret) {
diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c
index e30d76e0a6f..188d4e092ef 100644
--- a/drivers/clocksource/sh_mtu2.c
+++ b/drivers/clocksource/sh_mtu2.c
@@ -11,37 +11,48 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/clk.h>
+#include <linux/ioport.h>
#include <linux/irq.h>
-#include <linux/err.h>
-#include <linux/clockchips.h>
-#include <linux/sh_timer.h>
-#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/sh_timer.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+struct sh_mtu2_device;
+
+struct sh_mtu2_channel {
+ struct sh_mtu2_device *mtu;
+ unsigned int index;
+
+ void __iomem *base;
+ int irq;
+
+ struct clock_event_device ced;
+};
+
+struct sh_mtu2_device {
+ struct platform_device *pdev;
-struct sh_mtu2_priv {
void __iomem *mapbase;
struct clk *clk;
- struct irqaction irqaction;
- struct platform_device *pdev;
- unsigned long rate;
- unsigned long periodic;
- struct clock_event_device ced;
+
+ struct sh_mtu2_channel *channels;
+ unsigned int num_channels;
+
+ bool legacy;
+ bool has_clockevent;
};
static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
@@ -55,6 +66,88 @@ static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
#define TCNT 5 /* channel register */
#define TGR 6 /* channel register */
+#define TCR_CCLR_NONE (0 << 5)
+#define TCR_CCLR_TGRA (1 << 5)
+#define TCR_CCLR_TGRB (2 << 5)
+#define TCR_CCLR_SYNC (3 << 5)
+#define TCR_CCLR_TGRC (5 << 5)
+#define TCR_CCLR_TGRD (6 << 5)
+#define TCR_CCLR_MASK (7 << 5)
+#define TCR_CKEG_RISING (0 << 3)
+#define TCR_CKEG_FALLING (1 << 3)
+#define TCR_CKEG_BOTH (2 << 3)
+#define TCR_CKEG_MASK (3 << 3)
+/* Values 4 to 7 are channel-dependent */
+#define TCR_TPSC_P1 (0 << 0)
+#define TCR_TPSC_P4 (1 << 0)
+#define TCR_TPSC_P16 (2 << 0)
+#define TCR_TPSC_P64 (3 << 0)
+#define TCR_TPSC_CH0_TCLKA (4 << 0)
+#define TCR_TPSC_CH0_TCLKB (5 << 0)
+#define TCR_TPSC_CH0_TCLKC (6 << 0)
+#define TCR_TPSC_CH0_TCLKD (7 << 0)
+#define TCR_TPSC_CH1_TCLKA (4 << 0)
+#define TCR_TPSC_CH1_TCLKB (5 << 0)
+#define TCR_TPSC_CH1_P256 (6 << 0)
+#define TCR_TPSC_CH1_TCNT2 (7 << 0)
+#define TCR_TPSC_CH2_TCLKA (4 << 0)
+#define TCR_TPSC_CH2_TCLKB (5 << 0)
+#define TCR_TPSC_CH2_TCLKC (6 << 0)
+#define TCR_TPSC_CH2_P1024 (7 << 0)
+#define TCR_TPSC_CH34_P256 (4 << 0)
+#define TCR_TPSC_CH34_P1024 (5 << 0)
+#define TCR_TPSC_CH34_TCLKA (6 << 0)
+#define TCR_TPSC_CH34_TCLKB (7 << 0)
+#define TCR_TPSC_MASK (7 << 0)
+
+#define TMDR_BFE (1 << 6)
+#define TMDR_BFB (1 << 5)
+#define TMDR_BFA (1 << 4)
+#define TMDR_MD_NORMAL (0 << 0)
+#define TMDR_MD_PWM_1 (2 << 0)
+#define TMDR_MD_PWM_2 (3 << 0)
+#define TMDR_MD_PHASE_1 (4 << 0)
+#define TMDR_MD_PHASE_2 (5 << 0)
+#define TMDR_MD_PHASE_3 (6 << 0)
+#define TMDR_MD_PHASE_4 (7 << 0)
+#define TMDR_MD_PWM_SYNC (8 << 0)
+#define TMDR_MD_PWM_COMP_CREST (13 << 0)
+#define TMDR_MD_PWM_COMP_TROUGH (14 << 0)
+#define TMDR_MD_PWM_COMP_BOTH (15 << 0)
+#define TMDR_MD_MASK (15 << 0)
+
+#define TIOC_IOCH(n) ((n) << 4)
+#define TIOC_IOCL(n) ((n) << 0)
+#define TIOR_OC_RETAIN (0 << 0)
+#define TIOR_OC_0_CLEAR (1 << 0)
+#define TIOR_OC_0_SET (2 << 0)
+#define TIOR_OC_0_TOGGLE (3 << 0)
+#define TIOR_OC_1_CLEAR (5 << 0)
+#define TIOR_OC_1_SET (6 << 0)
+#define TIOR_OC_1_TOGGLE (7 << 0)
+#define TIOR_IC_RISING (8 << 0)
+#define TIOR_IC_FALLING (9 << 0)
+#define TIOR_IC_BOTH (10 << 0)
+#define TIOR_IC_TCNT (12 << 0)
+#define TIOR_MASK (15 << 0)
+
+#define TIER_TTGE (1 << 7)
+#define TIER_TTGE2 (1 << 6)
+#define TIER_TCIEU (1 << 5)
+#define TIER_TCIEV (1 << 4)
+#define TIER_TGIED (1 << 3)
+#define TIER_TGIEC (1 << 2)
+#define TIER_TGIEB (1 << 1)
+#define TIER_TGIEA (1 << 0)
+
+#define TSR_TCFD (1 << 7)
+#define TSR_TCFU (1 << 5)
+#define TSR_TCFV (1 << 4)
+#define TSR_TGFD (1 << 3)
+#define TSR_TGFC (1 << 2)
+#define TSR_TGFB (1 << 1)
+#define TSR_TGFA (1 << 0)
+
static unsigned long mtu2_reg_offs[] = {
[TCR] = 0,
[TMDR] = 1,
@@ -65,135 +158,143 @@ static unsigned long mtu2_reg_offs[] = {
[TGR] = 8,
};
-static inline unsigned long sh_mtu2_read(struct sh_mtu2_priv *p, int reg_nr)
+static inline unsigned long sh_mtu2_read(struct sh_mtu2_channel *ch, int reg_nr)
{
- struct sh_timer_config *cfg = p->pdev->dev.platform_data;
- void __iomem *base = p->mapbase;
unsigned long offs;
- if (reg_nr == TSTR)
- return ioread8(base + cfg->channel_offset);
+ if (reg_nr == TSTR) {
+ if (ch->mtu->legacy)
+ return ioread8(ch->mtu->mapbase);
+ else
+ return ioread8(ch->mtu->mapbase + 0x280);
+ }
offs = mtu2_reg_offs[reg_nr];
if ((reg_nr == TCNT) || (reg_nr == TGR))
- return ioread16(base + offs);
+ return ioread16(ch->base + offs);
else
- return ioread8(base + offs);
+ return ioread8(ch->base + offs);
}
-static inline void sh_mtu2_write(struct sh_mtu2_priv *p, int reg_nr,
+static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr,
unsigned long value)
{
- struct sh_timer_config *cfg = p->pdev->dev.platform_data;
- void __iomem *base = p->mapbase;
unsigned long offs;
if (reg_nr == TSTR) {
- iowrite8(value, base + cfg->channel_offset);
- return;
+ if (ch->mtu->legacy)
+ return iowrite8(value, ch->mtu->mapbase);
+ else
+ return iowrite8(value, ch->mtu->mapbase + 0x280);
}
offs = mtu2_reg_offs[reg_nr];
if ((reg_nr == TCNT) || (reg_nr == TGR))
- iowrite16(value, base + offs);
+ iowrite16(value, ch->base + offs);
else
- iowrite8(value, base + offs);
+ iowrite8(value, ch->base + offs);
}
-static void sh_mtu2_start_stop_ch(struct sh_mtu2_priv *p, int start)
+static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start)
{
- struct sh_timer_config *cfg = p->pdev->dev.platform_data;
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&sh_mtu2_lock, flags);
- value = sh_mtu2_read(p, TSTR);
+ value = sh_mtu2_read(ch, TSTR);
if (start)
- value |= 1 << cfg->timer_bit;
+ value |= 1 << ch->index;
else
- value &= ~(1 << cfg->timer_bit);
+ value &= ~(1 << ch->index);
- sh_mtu2_write(p, TSTR, value);
+ sh_mtu2_write(ch, TSTR, value);
raw_spin_unlock_irqrestore(&sh_mtu2_lock, flags);
}
-static int sh_mtu2_enable(struct sh_mtu2_priv *p)
+static int sh_mtu2_enable(struct sh_mtu2_channel *ch)
{
+ unsigned long periodic;
+ unsigned long rate;
int ret;
- pm_runtime_get_sync(&p->pdev->dev);
- dev_pm_syscore_device(&p->pdev->dev, true);
+ pm_runtime_get_sync(&ch->mtu->pdev->dev);
+ dev_pm_syscore_device(&ch->mtu->pdev->dev, true);
/* enable clock */
- ret = clk_enable(p->clk);
+ ret = clk_enable(ch->mtu->clk);
if (ret) {
- dev_err(&p->pdev->dev, "cannot enable clock\n");
+ dev_err(&ch->mtu->pdev->dev, "ch%u: cannot enable clock\n",
+ ch->index);
return ret;
}
/* make sure channel is disabled */
- sh_mtu2_start_stop_ch(p, 0);
-
- p->rate = clk_get_rate(p->clk) / 64;
- p->periodic = (p->rate + HZ/2) / HZ;
-
- /* "Periodic Counter Operation" */
- sh_mtu2_write(p, TCR, 0x23); /* TGRA clear, divide clock by 64 */
- sh_mtu2_write(p, TIOR, 0);
- sh_mtu2_write(p, TGR, p->periodic);
- sh_mtu2_write(p, TCNT, 0);
- sh_mtu2_write(p, TMDR, 0);
- sh_mtu2_write(p, TIER, 0x01);
+ sh_mtu2_start_stop_ch(ch, 0);
+
+ rate = clk_get_rate(ch->mtu->clk) / 64;
+ periodic = (rate + HZ/2) / HZ;
+
+ /*
+ * "Periodic Counter Operation"
+ * Clear on TGRA compare match, divide clock by 64.
+ */
+ sh_mtu2_write(ch, TCR, TCR_CCLR_TGRA | TCR_TPSC_P64);
+ sh_mtu2_write(ch, TIOR, TIOC_IOCH(TIOR_OC_0_CLEAR) |
+ TIOC_IOCL(TIOR_OC_0_CLEAR));
+ sh_mtu2_write(ch, TGR, periodic);
+ sh_mtu2_write(ch, TCNT, 0);
+ sh_mtu2_write(ch, TMDR, TMDR_MD_NORMAL);
+ sh_mtu2_write(ch, TIER, TIER_TGIEA);
/* enable channel */
- sh_mtu2_start_stop_ch(p, 1);
+ sh_mtu2_start_stop_ch(ch, 1);
return 0;
}
-static void sh_mtu2_disable(struct sh_mtu2_priv *p)
+static void sh_mtu2_disable(struct sh_mtu2_channel *ch)
{
/* disable channel */
- sh_mtu2_start_stop_ch(p, 0);
+ sh_mtu2_start_stop_ch(ch, 0);
/* stop clock */
- clk_disable(p->clk);
+ clk_disable(ch->mtu->clk);
- dev_pm_syscore_device(&p->pdev->dev, false);
- pm_runtime_put(&p->pdev->dev);
+ dev_pm_syscore_device(&ch->mtu->pdev->dev, false);
+ pm_runtime_put(&ch->mtu->pdev->dev);
}
static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id)
{
- struct sh_mtu2_priv *p = dev_id;
+ struct sh_mtu2_channel *ch = dev_id;
/* acknowledge interrupt */
- sh_mtu2_read(p, TSR);
- sh_mtu2_write(p, TSR, 0xfe);
+ sh_mtu2_read(ch, TSR);
+ sh_mtu2_write(ch, TSR, ~TSR_TGFA);
/* notify clockevent layer */
- p->ced.event_handler(&p->ced);
+ ch->ced.event_handler(&ch->ced);
return IRQ_HANDLED;
}
-static struct sh_mtu2_priv *ced_to_sh_mtu2(struct clock_event_device *ced)
+static struct sh_mtu2_channel *ced_to_sh_mtu2(struct clock_event_device *ced)
{
- return container_of(ced, struct sh_mtu2_priv, ced);
+ return container_of(ced, struct sh_mtu2_channel, ced);
}
static void sh_mtu2_clock_event_mode(enum clock_event_mode mode,
struct clock_event_device *ced)
{
- struct sh_mtu2_priv *p = ced_to_sh_mtu2(ced);
+ struct sh_mtu2_channel *ch = ced_to_sh_mtu2(ced);
int disabled = 0;
/* deal with old setting first */
switch (ced->mode) {
case CLOCK_EVT_MODE_PERIODIC:
- sh_mtu2_disable(p);
+ sh_mtu2_disable(ch);
disabled = 1;
break;
default:
@@ -202,12 +303,13 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode,
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- dev_info(&p->pdev->dev, "used for periodic clock events\n");
- sh_mtu2_enable(p);
+ dev_info(&ch->mtu->pdev->dev,
+ "ch%u: used for periodic clock events\n", ch->index);
+ sh_mtu2_enable(ch);
break;
case CLOCK_EVT_MODE_UNUSED:
if (!disabled)
- sh_mtu2_disable(p);
+ sh_mtu2_disable(ch);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
default:
@@ -217,125 +319,207 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode,
static void sh_mtu2_clock_event_suspend(struct clock_event_device *ced)
{
- pm_genpd_syscore_poweroff(&ced_to_sh_mtu2(ced)->pdev->dev);
+ pm_genpd_syscore_poweroff(&ced_to_sh_mtu2(ced)->mtu->pdev->dev);
}
static void sh_mtu2_clock_event_resume(struct clock_event_device *ced)
{
- pm_genpd_syscore_poweron(&ced_to_sh_mtu2(ced)->pdev->dev);
+ pm_genpd_syscore_poweron(&ced_to_sh_mtu2(ced)->mtu->pdev->dev);
}
-static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p,
- char *name, unsigned long rating)
+static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
+ const char *name)
{
- struct clock_event_device *ced = &p->ced;
+ struct clock_event_device *ced = &ch->ced;
int ret;
- memset(ced, 0, sizeof(*ced));
-
ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC;
- ced->rating = rating;
- ced->cpumask = cpumask_of(0);
+ ced->rating = 200;
+ ced->cpumask = cpu_possible_mask;
ced->set_mode = sh_mtu2_clock_event_mode;
ced->suspend = sh_mtu2_clock_event_suspend;
ced->resume = sh_mtu2_clock_event_resume;
- dev_info(&p->pdev->dev, "used for clock events\n");
+ dev_info(&ch->mtu->pdev->dev, "ch%u: used for clock events\n",
+ ch->index);
clockevents_register_device(ced);
- ret = setup_irq(p->irqaction.irq, &p->irqaction);
+ ret = request_irq(ch->irq, sh_mtu2_interrupt,
+ IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
+ dev_name(&ch->mtu->pdev->dev), ch);
if (ret) {
- dev_err(&p->pdev->dev, "failed to request irq %d\n",
- p->irqaction.irq);
+ dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n",
+ ch->index, ch->irq);
return;
}
}
-static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name,
- unsigned long clockevent_rating)
+static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name,
+ bool clockevent)
{
- if (clockevent_rating)
- sh_mtu2_register_clockevent(p, name, clockevent_rating);
+ if (clockevent) {
+ ch->mtu->has_clockevent = true;
+ sh_mtu2_register_clockevent(ch, name);
+ }
return 0;
}
-static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev)
+static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index,
+ struct sh_mtu2_device *mtu)
{
- struct sh_timer_config *cfg = pdev->dev.platform_data;
- struct resource *res;
- int irq, ret;
- ret = -ENXIO;
+ static const unsigned int channel_offsets[] = {
+ 0x300, 0x380, 0x000,
+ };
+ bool clockevent;
+
+ ch->mtu = mtu;
+
+ if (mtu->legacy) {
+ struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
+
+ clockevent = cfg->clockevent_rating != 0;
- memset(p, 0, sizeof(*p));
- p->pdev = pdev;
+ ch->irq = platform_get_irq(mtu->pdev, 0);
+ ch->base = mtu->mapbase - cfg->channel_offset;
+ ch->index = cfg->timer_bit;
+ } else {
+ char name[6];
- if (!cfg) {
- dev_err(&p->pdev->dev, "missing platform data\n");
- goto err0;
+ clockevent = true;
+
+ sprintf(name, "tgi%ua", index);
+ ch->irq = platform_get_irq_byname(mtu->pdev, name);
+ ch->base = mtu->mapbase + channel_offsets[index];
+ ch->index = index;
}
- platform_set_drvdata(pdev, p);
+ if (ch->irq < 0) {
+ /* Skip channels with no declared interrupt. */
+ if (!mtu->legacy)
+ return 0;
+
+ dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n",
+ ch->index);
+ return ch->irq;
+ }
+
+ return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), clockevent);
+}
- res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
+static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu)
+{
+ struct resource *res;
+
+ res = platform_get_resource(mtu->pdev, IORESOURCE_MEM, 0);
if (!res) {
- dev_err(&p->pdev->dev, "failed to get I/O memory\n");
- goto err0;
+ dev_err(&mtu->pdev->dev, "failed to get I/O memory\n");
+ return -ENXIO;
+ }
+
+ mtu->mapbase = ioremap_nocache(res->start, resource_size(res));
+ if (mtu->mapbase == NULL)
+ return -ENXIO;
+
+ /*
+ * In legacy platform device configuration (with one device per channel)
+ * the resource points to the channel base address.
+ */
+ if (mtu->legacy) {
+ struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
+ mtu->mapbase += cfg->channel_offset;
}
- irq = platform_get_irq(p->pdev, 0);
- if (irq < 0) {
- dev_err(&p->pdev->dev, "failed to get irq\n");
- goto err0;
+ return 0;
+}
+
+static void sh_mtu2_unmap_memory(struct sh_mtu2_device *mtu)
+{
+ if (mtu->legacy) {
+ struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
+ mtu->mapbase -= cfg->channel_offset;
}
- /* map memory, let mapbase point to our channel */
- p->mapbase = ioremap_nocache(res->start, resource_size(res));
- if (p->mapbase == NULL) {
- dev_err(&p->pdev->dev, "failed to remap I/O memory\n");
- goto err0;
+ iounmap(mtu->mapbase);
+}
+
+static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
+ struct platform_device *pdev)
+{
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+ const struct platform_device_id *id = pdev->id_entry;
+ unsigned int i;
+ int ret;
+
+ mtu->pdev = pdev;
+ mtu->legacy = id->driver_data;
+
+ if (mtu->legacy && !cfg) {
+ dev_err(&mtu->pdev->dev, "missing platform data\n");
+ return -ENXIO;
}
- /* setup data for setup_irq() (too early for request_irq()) */
- p->irqaction.name = dev_name(&p->pdev->dev);
- p->irqaction.handler = sh_mtu2_interrupt;
- p->irqaction.dev_id = p;
- p->irqaction.irq = irq;
- p->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING;
-
- /* get hold of clock */
- p->clk = clk_get(&p->pdev->dev, "mtu2_fck");
- if (IS_ERR(p->clk)) {
- dev_err(&p->pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(p->clk);
- goto err1;
+ /* Get hold of clock. */
+ mtu->clk = clk_get(&mtu->pdev->dev, mtu->legacy ? "mtu2_fck" : "fck");
+ if (IS_ERR(mtu->clk)) {
+ dev_err(&mtu->pdev->dev, "cannot get clock\n");
+ return PTR_ERR(mtu->clk);
}
- ret = clk_prepare(p->clk);
+ ret = clk_prepare(mtu->clk);
if (ret < 0)
- goto err2;
+ goto err_clk_put;
- ret = sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev),
- cfg->clockevent_rating);
- if (ret < 0)
- goto err3;
+ /* Map the memory resource. */
+ ret = sh_mtu2_map_memory(mtu);
+ if (ret < 0) {
+ dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n");
+ goto err_clk_unprepare;
+ }
+
+ /* Allocate and setup the channels. */
+ if (mtu->legacy)
+ mtu->num_channels = 1;
+ else
+ mtu->num_channels = 3;
+
+ mtu->channels = kzalloc(sizeof(*mtu->channels) * mtu->num_channels,
+ GFP_KERNEL);
+ if (mtu->channels == NULL) {
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ if (mtu->legacy) {
+ ret = sh_mtu2_setup_channel(&mtu->channels[0], 0, mtu);
+ if (ret < 0)
+ goto err_unmap;
+ } else {
+ for (i = 0; i < mtu->num_channels; ++i) {
+ ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu);
+ if (ret < 0)
+ goto err_unmap;
+ }
+ }
+
+ platform_set_drvdata(pdev, mtu);
return 0;
- err3:
- clk_unprepare(p->clk);
- err2:
- clk_put(p->clk);
- err1:
- iounmap(p->mapbase);
- err0:
+
+err_unmap:
+ kfree(mtu->channels);
+ sh_mtu2_unmap_memory(mtu);
+err_clk_unprepare:
+ clk_unprepare(mtu->clk);
+err_clk_put:
+ clk_put(mtu->clk);
return ret;
}
static int sh_mtu2_probe(struct platform_device *pdev)
{
- struct sh_mtu2_priv *p = platform_get_drvdata(pdev);
- struct sh_timer_config *cfg = pdev->dev.platform_data;
+ struct sh_mtu2_device *mtu = platform_get_drvdata(pdev);
int ret;
if (!is_early_platform_device(pdev)) {
@@ -343,20 +527,18 @@ static int sh_mtu2_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
}
- if (p) {
+ if (mtu) {
dev_info(&pdev->dev, "kept as earlytimer\n");
goto out;
}
- p = kmalloc(sizeof(*p), GFP_KERNEL);
- if (p == NULL) {
- dev_err(&pdev->dev, "failed to allocate driver data\n");
+ mtu = kzalloc(sizeof(*mtu), GFP_KERNEL);
+ if (mtu == NULL)
return -ENOMEM;
- }
- ret = sh_mtu2_setup(p, pdev);
+ ret = sh_mtu2_setup(mtu, pdev);
if (ret) {
- kfree(p);
+ kfree(mtu);
pm_runtime_idle(&pdev->dev);
return ret;
}
@@ -364,7 +546,7 @@ static int sh_mtu2_probe(struct platform_device *pdev)
return 0;
out:
- if (cfg->clockevent_rating)
+ if (mtu->has_clockevent)
pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
@@ -377,12 +559,20 @@ static int sh_mtu2_remove(struct platform_device *pdev)
return -EBUSY; /* cannot unregister clockevent */
}
+static const struct platform_device_id sh_mtu2_id_table[] = {
+ { "sh_mtu2", 1 },
+ { "sh-mtu2", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table);
+
static struct platform_driver sh_mtu2_device_driver = {
.probe = sh_mtu2_probe,
.remove = sh_mtu2_remove,
.driver = {
.name = "sh_mtu2",
- }
+ },
+ .id_table = sh_mtu2_id_table,
};
static int __init sh_mtu2_init(void)
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c
index 26a9f7dadfb..6bd17a8f3dd 100644
--- a/drivers/clocksource/sh_tmu.c
+++ b/drivers/clocksource/sh_tmu.c
@@ -11,29 +11,31 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/clk.h>
+#include <linux/ioport.h>
#include <linux/irq.h>
-#include <linux/err.h>
-#include <linux/clocksource.h>
-#include <linux/clockchips.h>
-#include <linux/sh_timer.h>
-#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/sh_timer.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+enum sh_tmu_model {
+ SH_TMU_LEGACY,
+ SH_TMU,
+ SH_TMU_SH3,
+};
struct sh_tmu_device;
@@ -58,7 +60,13 @@ struct sh_tmu_device {
void __iomem *mapbase;
struct clk *clk;
- struct sh_tmu_channel channel;
+ enum sh_tmu_model model;
+
+ struct sh_tmu_channel *channels;
+ unsigned int num_channels;
+
+ bool has_clockevent;
+ bool has_clocksource;
};
static DEFINE_RAW_SPINLOCK(sh_tmu_lock);
@@ -68,12 +76,29 @@ static DEFINE_RAW_SPINLOCK(sh_tmu_lock);
#define TCNT 1 /* channel register */
#define TCR 2 /* channel register */
+#define TCR_UNF (1 << 8)
+#define TCR_UNIE (1 << 5)
+#define TCR_TPSC_CLK4 (0 << 0)
+#define TCR_TPSC_CLK16 (1 << 0)
+#define TCR_TPSC_CLK64 (2 << 0)
+#define TCR_TPSC_CLK256 (3 << 0)
+#define TCR_TPSC_CLK1024 (4 << 0)
+#define TCR_TPSC_MASK (7 << 0)
+
static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr)
{
unsigned long offs;
- if (reg_nr == TSTR)
- return ioread8(ch->tmu->mapbase);
+ if (reg_nr == TSTR) {
+ switch (ch->tmu->model) {
+ case SH_TMU_LEGACY:
+ return ioread8(ch->tmu->mapbase);
+ case SH_TMU_SH3:
+ return ioread8(ch->tmu->mapbase + 2);
+ case SH_TMU:
+ return ioread8(ch->tmu->mapbase + 4);
+ }
+ }
offs = reg_nr << 2;
@@ -89,8 +114,14 @@ static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr,
unsigned long offs;
if (reg_nr == TSTR) {
- iowrite8(value, ch->tmu->mapbase);
- return;
+ switch (ch->tmu->model) {
+ case SH_TMU_LEGACY:
+ return iowrite8(value, ch->tmu->mapbase);
+ case SH_TMU_SH3:
+ return iowrite8(value, ch->tmu->mapbase + 2);
+ case SH_TMU:
+ return iowrite8(value, ch->tmu->mapbase + 4);
+ }
}
offs = reg_nr << 2;
@@ -139,7 +170,7 @@ static int __sh_tmu_enable(struct sh_tmu_channel *ch)
/* configure channel to parent clock / 4, irq off */
ch->rate = clk_get_rate(ch->tmu->clk) / 4;
- sh_tmu_write(ch, TCR, 0x0000);
+ sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
/* enable channel */
sh_tmu_start_stop_ch(ch, 1);
@@ -164,7 +195,7 @@ static void __sh_tmu_disable(struct sh_tmu_channel *ch)
sh_tmu_start_stop_ch(ch, 0);
/* disable interrupts in TMU block */
- sh_tmu_write(ch, TCR, 0x0000);
+ sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
/* stop clock */
clk_disable(ch->tmu->clk);
@@ -194,7 +225,7 @@ static void sh_tmu_set_next(struct sh_tmu_channel *ch, unsigned long delta,
sh_tmu_read(ch, TCR);
/* enable interrupt */
- sh_tmu_write(ch, TCR, 0x0020);
+ sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4);
/* reload delta value in case of periodic timer */
if (periodic)
@@ -214,9 +245,9 @@ static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id)
/* disable or acknowledge interrupt */
if (ch->ced.mode == CLOCK_EVT_MODE_ONESHOT)
- sh_tmu_write(ch, TCR, 0x0000);
+ sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
else
- sh_tmu_write(ch, TCR, 0x0020);
+ sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4);
/* notify clockevent layer */
ch->ced.event_handler(&ch->ced);
@@ -290,12 +321,12 @@ static void sh_tmu_clocksource_resume(struct clocksource *cs)
}
static int sh_tmu_register_clocksource(struct sh_tmu_channel *ch,
- const char *name, unsigned long rating)
+ const char *name)
{
struct clocksource *cs = &ch->cs;
cs->name = name;
- cs->rating = rating;
+ cs->rating = 200;
cs->read = sh_tmu_clocksource_read;
cs->enable = sh_tmu_clocksource_enable;
cs->disable = sh_tmu_clocksource_disable;
@@ -392,7 +423,7 @@ static void sh_tmu_clock_event_resume(struct clock_event_device *ced)
}
static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
- const char *name, unsigned long rating)
+ const char *name)
{
struct clock_event_device *ced = &ch->ced;
int ret;
@@ -400,7 +431,7 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC;
ced->features |= CLOCK_EVT_FEAT_ONESHOT;
- ced->rating = rating;
+ ced->rating = 200;
ced->cpumask = cpumask_of(0);
ced->set_next_event = sh_tmu_clock_event_next;
ced->set_mode = sh_tmu_clock_event_mode;
@@ -423,34 +454,51 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
}
static int sh_tmu_register(struct sh_tmu_channel *ch, const char *name,
- unsigned long clockevent_rating,
- unsigned long clocksource_rating)
+ bool clockevent, bool clocksource)
{
- if (clockevent_rating)
- sh_tmu_register_clockevent(ch, name, clockevent_rating);
- else if (clocksource_rating)
- sh_tmu_register_clocksource(ch, name, clocksource_rating);
+ if (clockevent) {
+ ch->tmu->has_clockevent = true;
+ sh_tmu_register_clockevent(ch, name);
+ } else if (clocksource) {
+ ch->tmu->has_clocksource = true;
+ sh_tmu_register_clocksource(ch, name);
+ }
return 0;
}
-static int sh_tmu_channel_setup(struct sh_tmu_channel *ch,
+static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index,
+ bool clockevent, bool clocksource,
struct sh_tmu_device *tmu)
{
- struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
+ /* Skip unused channels. */
+ if (!clockevent && !clocksource)
+ return 0;
ch->tmu = tmu;
- /*
- * The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps channel
- * registers blocks at base + 2 + 12 * index, while all other variants
- * map them at base + 4 + 12 * index. We can compute the index by just
- * dividing by 12, the 2 bytes or 4 bytes offset being hidden by the
- * integer division.
- */
- ch->index = cfg->channel_offset / 12;
+ if (tmu->model == SH_TMU_LEGACY) {
+ struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
+
+ /*
+ * The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps
+ * channel registers blocks at base + 2 + 12 * index, while all
+ * other variants map them at base + 4 + 12 * index. We can
+ * compute the index by just dividing by 12, the 2 bytes or 4
+ * bytes offset being hidden by the integer division.
+ */
+ ch->index = cfg->channel_offset / 12;
+ ch->base = tmu->mapbase + cfg->channel_offset;
+ } else {
+ ch->index = index;
+
+ if (tmu->model == SH_TMU_SH3)
+ ch->base = tmu->mapbase + 4 + ch->index * 12;
+ else
+ ch->base = tmu->mapbase + 8 + ch->index * 12;
+ }
- ch->irq = platform_get_irq(tmu->pdev, 0);
+ ch->irq = platform_get_irq(tmu->pdev, index);
if (ch->irq < 0) {
dev_err(&tmu->pdev->dev, "ch%u: failed to get irq\n",
ch->index);
@@ -461,76 +509,128 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch,
ch->enable_count = 0;
return sh_tmu_register(ch, dev_name(&tmu->pdev->dev),
- cfg->clockevent_rating,
- cfg->clocksource_rating);
+ clockevent, clocksource);
}
-static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
+static int sh_tmu_map_memory(struct sh_tmu_device *tmu)
{
- struct sh_timer_config *cfg = pdev->dev.platform_data;
struct resource *res;
- int ret;
- ret = -ENXIO;
-
- tmu->pdev = pdev;
-
- if (!cfg) {
- dev_err(&tmu->pdev->dev, "missing platform data\n");
- goto err0;
- }
-
- platform_set_drvdata(pdev, tmu);
res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&tmu->pdev->dev, "failed to get I/O memory\n");
- goto err0;
+ return -ENXIO;
}
+ tmu->mapbase = ioremap_nocache(res->start, resource_size(res));
+ if (tmu->mapbase == NULL)
+ return -ENXIO;
+
/*
- * Map memory, let channel.base point to our channel and mapbase to the
- * start/stop shared register.
+ * In legacy platform device configuration (with one device per channel)
+ * the resource points to the channel base address.
*/
- tmu->channel.base = ioremap_nocache(res->start, resource_size(res));
- if (tmu->channel.base == NULL) {
- dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n");
- goto err0;
+ if (tmu->model == SH_TMU_LEGACY) {
+ struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
+ tmu->mapbase -= cfg->channel_offset;
+ }
+
+ return 0;
+}
+
+static void sh_tmu_unmap_memory(struct sh_tmu_device *tmu)
+{
+ if (tmu->model == SH_TMU_LEGACY) {
+ struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
+ tmu->mapbase += cfg->channel_offset;
+ }
+
+ iounmap(tmu->mapbase);
+}
+
+static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
+{
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+ const struct platform_device_id *id = pdev->id_entry;
+ unsigned int i;
+ int ret;
+
+ if (!cfg) {
+ dev_err(&tmu->pdev->dev, "missing platform data\n");
+ return -ENXIO;
}
- tmu->mapbase = tmu->channel.base - cfg->channel_offset;
+ tmu->pdev = pdev;
+ tmu->model = id->driver_data;
- /* get hold of clock */
- tmu->clk = clk_get(&tmu->pdev->dev, "tmu_fck");
+ /* Get hold of clock. */
+ tmu->clk = clk_get(&tmu->pdev->dev,
+ tmu->model == SH_TMU_LEGACY ? "tmu_fck" : "fck");
if (IS_ERR(tmu->clk)) {
dev_err(&tmu->pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(tmu->clk);
- goto err1;
+ return PTR_ERR(tmu->clk);
}
ret = clk_prepare(tmu->clk);
if (ret < 0)
- goto err2;
+ goto err_clk_put;
- ret = sh_tmu_channel_setup(&tmu->channel, tmu);
- if (ret < 0)
- goto err3;
+ /* Map the memory resource. */
+ ret = sh_tmu_map_memory(tmu);
+ if (ret < 0) {
+ dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n");
+ goto err_clk_unprepare;
+ }
+
+ /* Allocate and setup the channels. */
+ if (tmu->model == SH_TMU_LEGACY)
+ tmu->num_channels = 1;
+ else
+ tmu->num_channels = hweight8(cfg->channels_mask);
+
+ tmu->channels = kzalloc(sizeof(*tmu->channels) * tmu->num_channels,
+ GFP_KERNEL);
+ if (tmu->channels == NULL) {
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ if (tmu->model == SH_TMU_LEGACY) {
+ ret = sh_tmu_channel_setup(&tmu->channels[0], 0,
+ cfg->clockevent_rating != 0,
+ cfg->clocksource_rating != 0, tmu);
+ if (ret < 0)
+ goto err_unmap;
+ } else {
+ /*
+ * Use the first channel as a clock event device and the second
+ * channel as a clock source.
+ */
+ for (i = 0; i < tmu->num_channels; ++i) {
+ ret = sh_tmu_channel_setup(&tmu->channels[i], i,
+ i == 0, i == 1, tmu);
+ if (ret < 0)
+ goto err_unmap;
+ }
+ }
+
+ platform_set_drvdata(pdev, tmu);
return 0;
- err3:
+err_unmap:
+ kfree(tmu->channels);
+ sh_tmu_unmap_memory(tmu);
+err_clk_unprepare:
clk_unprepare(tmu->clk);
- err2:
+err_clk_put:
clk_put(tmu->clk);
- err1:
- iounmap(tmu->channel.base);
- err0:
return ret;
}
static int sh_tmu_probe(struct platform_device *pdev)
{
struct sh_tmu_device *tmu = platform_get_drvdata(pdev);
- struct sh_timer_config *cfg = pdev->dev.platform_data;
int ret;
if (!is_early_platform_device(pdev)) {
@@ -544,10 +644,8 @@ static int sh_tmu_probe(struct platform_device *pdev)
}
tmu = kzalloc(sizeof(*tmu), GFP_KERNEL);
- if (tmu == NULL) {
- dev_err(&pdev->dev, "failed to allocate driver data\n");
+ if (tmu == NULL)
return -ENOMEM;
- }
ret = sh_tmu_setup(tmu, pdev);
if (ret) {
@@ -559,7 +657,7 @@ static int sh_tmu_probe(struct platform_device *pdev)
return 0;
out:
- if (cfg->clockevent_rating || cfg->clocksource_rating)
+ if (tmu->has_clockevent || tmu->has_clocksource)
pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
@@ -572,12 +670,21 @@ static int sh_tmu_remove(struct platform_device *pdev)
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
+static const struct platform_device_id sh_tmu_id_table[] = {
+ { "sh_tmu", SH_TMU_LEGACY },
+ { "sh-tmu", SH_TMU },
+ { "sh-tmu-sh3", SH_TMU_SH3 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, sh_tmu_id_table);
+
static struct platform_driver sh_tmu_device_driver = {
.probe = sh_tmu_probe,
.remove = sh_tmu_remove,
.driver = {
.name = "sh_tmu",
- }
+ },
+ .id_table = sh_tmu_id_table,
};
static int __init sh_tmu_init(void)
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
index 00fdd117028..a8d7ea14f18 100644
--- a/drivers/clocksource/tcb_clksrc.c
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -100,7 +100,7 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
|| tcd->clkevt.mode == CLOCK_EVT_MODE_ONESHOT) {
__raw_writel(0xff, regs + ATMEL_TC_REG(2, IDR));
__raw_writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR));
- clk_disable_unprepare(tcd->clk);
+ clk_disable(tcd->clk);
}
switch (m) {
@@ -109,7 +109,7 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
* of oneshot, we get lower overhead and improved accuracy.
*/
case CLOCK_EVT_MODE_PERIODIC:
- clk_prepare_enable(tcd->clk);
+ clk_enable(tcd->clk);
/* slow clock, count up to RC, then irq and restart */
__raw_writel(timer_clock
@@ -126,7 +126,7 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d)
break;
case CLOCK_EVT_MODE_ONESHOT:
- clk_prepare_enable(tcd->clk);
+ clk_enable(tcd->clk);
/* slow clock, count up to RC, then irq and stop */
__raw_writel(timer_clock | ATMEL_TC_CPCSTOP
@@ -194,7 +194,7 @@ static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
ret = clk_prepare_enable(t2_clk);
if (ret)
return ret;
- clk_disable_unprepare(t2_clk);
+ clk_disable(t2_clk);
clkevt.regs = tc->regs;
clkevt.clk = t2_clk;
diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c
index 1a6205b7bed..bba62f9deef 100644
--- a/drivers/clocksource/time-efm32.c
+++ b/drivers/clocksource/time-efm32.c
@@ -272,4 +272,5 @@ static void __init efm32_timer_init(struct device_node *np)
}
}
}
-CLOCKSOURCE_OF_DECLARE(efm32, "efm32,timer", efm32_timer_init);
+CLOCKSOURCE_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init);
+CLOCKSOURCE_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init);
diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c
index b52e1c078b9..dbd30398222 100644
--- a/drivers/clocksource/timer-marco.c
+++ b/drivers/clocksource/timer-marco.c
@@ -199,7 +199,7 @@ static int sirfsoc_local_timer_setup(struct clock_event_device *ce)
action->dev_id = ce;
BUG_ON(setup_irq(ce->irq, action));
- irq_set_affinity(action->irq, cpumask_of(cpu));
+ irq_force_affinity(action->irq, cpumask_of(cpu));
clockevents_register_device(ce);
return 0;
@@ -252,15 +252,13 @@ static void __init sirfsoc_clockevent_init(void)
}
/* initialize the kernel jiffy timer source */
-static void __init sirfsoc_marco_timer_init(void)
+static void __init sirfsoc_marco_timer_init(struct device_node *np)
{
unsigned long rate;
u32 timer_div;
struct clk *clk;
- /* timer's input clock is io clock */
- clk = clk_get_sys("io", NULL);
-
+ clk = of_clk_get(np, 0);
BUG_ON(IS_ERR(clk));
rate = clk_get_rate(clk);
@@ -303,6 +301,6 @@ static void __init sirfsoc_of_timer_init(struct device_node *np)
if (!sirfsoc_timer1_irq.irq)
panic("No irq passed for timer1 via DT\n");
- sirfsoc_marco_timer_init();
+ sirfsoc_marco_timer_init(np);
}
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
index 1a6b2d6356d..a722aac7ac0 100644
--- a/drivers/clocksource/timer-prima2.c
+++ b/drivers/clocksource/timer-prima2.c
@@ -61,7 +61,8 @@ 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)));
+ 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);
@@ -77,9 +78,11 @@ 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);
+ 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);
+ cycles = (cycles << 32) |
+ readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
return cycles;
}
@@ -89,11 +92,13 @@ static int sirfsoc_timer_set_next_event(unsigned long delta,
{
unsigned long now, next;
- writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
+ 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);
+ 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;
@@ -108,10 +113,12 @@ static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
WARN_ON(1);
break;
case CLOCK_EVT_MODE_ONESHOT:
- writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
+ 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);
+ writel_relaxed(val & ~BIT(0),
+ sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_RESUME:
@@ -123,10 +130,13 @@ static void sirfsoc_clocksource_suspend(struct clocksource *cs)
{
int i;
- writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
+ 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]);
+ sirfsoc_timer_reg_val[i] =
+ readl_relaxed(sirfsoc_timer_base +
+ sirfsoc_timer_reg_list[i]);
}
static void sirfsoc_clocksource_resume(struct clocksource *cs)
@@ -134,10 +144,13 @@ 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[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);
+ 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 = {
@@ -185,11 +198,8 @@ 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);
-
+ clk = of_clk_get(np, 0);
BUG_ON(IS_ERR(clk));
-
rate = clk_get_rate(clk);
BUG_ON(rate < PRIMA2_CLOCK_FREQ);
@@ -202,7 +212,7 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1,
- sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
+ 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);
@@ -216,4 +226,5 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
sirfsoc_clockevent_init();
}
-CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer, "sirf,prima2-tick", sirfsoc_prima2_timer_init);
+CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer,
+ "sirf,prima2-tick", sirfsoc_prima2_timer_init);
diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c
index deebcd6469f..02268448dc8 100644
--- a/drivers/clocksource/timer-sun5i.c
+++ b/drivers/clocksource/timer-sun5i.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqreturn.h>
+#include <linux/reset.h>
#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -143,6 +144,7 @@ static u64 sun5i_timer_sched_read(void)
static void __init sun5i_timer_init(struct device_node *node)
{
+ struct reset_control *rstc;
unsigned long rate;
struct clk *clk;
int ret, irq;
@@ -162,6 +164,10 @@ static void __init sun5i_timer_init(struct device_node *node)
clk_prepare_enable(clk);
rate = clk_get_rate(clk);
+ rstc = of_reset_control_get(node, NULL);
+ if (!IS_ERR(rstc))
+ reset_control_deassert(rstc);
+
writel(~0, timer_base + TIMER_INTVAL_LO_REG(1));
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
timer_base + TIMER_CTL_REG(1));
diff --git a/drivers/clocksource/versatile.c b/drivers/clocksource/versatile.c
new file mode 100644
index 00000000000..2798e749223
--- /dev/null
+++ b/drivers/clocksource/versatile.c
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2014 ARM Limited
+ */
+
+#include <linux/clocksource.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/sched_clock.h>
+
+#define SYS_24MHZ 0x05c
+
+static void __iomem *versatile_sys_24mhz;
+
+static u64 notrace versatile_sys_24mhz_read(void)
+{
+ return readl(versatile_sys_24mhz);
+}
+
+static void __init versatile_sched_clock_init(struct device_node *node)
+{
+ void __iomem *base = of_iomap(node, 0);
+
+ if (!base)
+ return;
+
+ versatile_sys_24mhz = base + SYS_24MHZ;
+
+ sched_clock_register(versatile_sys_24mhz_read, 32, 24000000);
+}
+CLOCKSOURCE_OF_DECLARE(versatile, "arm,vexpress-sysreg",
+ versatile_sched_clock_init);
diff --git a/drivers/clocksource/zevio-timer.c b/drivers/clocksource/zevio-timer.c
index ca81809d159..7ce442148c3 100644
--- a/drivers/clocksource/zevio-timer.c
+++ b/drivers/clocksource/zevio-timer.c
@@ -212,4 +212,9 @@ error_free:
return ret;
}
-CLOCKSOURCE_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_add);
+static void __init zevio_timer_init(struct device_node *node)
+{
+ BUG_ON(zevio_timer_add(node));
+}
+
+CLOCKSOURCE_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_init);