提交 331b483f 编写于 作者: T Thomas Gleixner

Merge branch 'clockevents/3.16' of...

Merge branch 'clockevents/3.16' of git://git.linaro.org/people/daniel.lezcano/linux into timers/core

This pull request contains the following changes:

* Laurent Pinchart did a lot of modifications to prepare the DT
  support.  These modifications include a lot of cleanup (structure
  renaming, preparation to support multiple channel, kzalloc usage,
  ...) and then finishes to drop the old code to the new one.

* Jingoo Han removed the dev_err when an allocation fails because this
  error is already given by the mm subsystems.

* Matthew Leach added the ARM global timer with vexpress, enabled the
  ARM global timer with the A5 and added the definition in the DT. He
  also fixed a invalid check when looking for an usable ARM global
  timer for A9

* Maxime Ripard added the support for AllWinner A31 for sun4i and made
  the timer reset optional through the DT

* Stephen Boyd used the msm timer for the udelay

* Uwe Kleine-König fixed the non-standard 'compatible' binding for efm32

* Xiubo Li clarified the types for the clocksource_mmio_read* and
  added a new Flextimer Module (FTM) with its bindings

* Yang Wei added the 'notrace' attribute to 'read_sched_clock' for the
  dw_apb_timer
...@@ -4,8 +4,11 @@ ...@@ -4,8 +4,11 @@
** Timer node required properties: ** Timer node required properties:
- compatible : Should be "arm,cortex-a9-global-timer" - compatible : should contain
Driver supports versions r2p0 and above. * "arm,cortex-a5-global-timer" for Cortex-A5 global timers.
* "arm,cortex-a9-global-timer" for Cortex-A9 global
timers or any compatible implementation. Note: driver
supports versions r2p0 and above.
- interrupts : One interrupt to each core - interrupts : One interrupt to each core
......
...@@ -9,6 +9,9 @@ Required properties: ...@@ -9,6 +9,9 @@ Required properties:
one) one)
- clocks: phandle to the source clock (usually the AHB clock) - clocks: phandle to the source clock (usually the AHB clock)
Optionnal properties:
- resets: phandle to a reset controller asserting the timer
Example: Example:
timer@01c60000 { timer@01c60000 {
...@@ -19,4 +22,5 @@ timer@01c60000 { ...@@ -19,4 +22,5 @@ timer@01c60000 {
<0 53 1>, <0 53 1>,
<0 54 1>; <0 54 1>;
clocks = <&ahb1_gates 19>; clocks = <&ahb1_gates 19>;
resets = <&ahb1rst 19>;
}; };
...@@ -6,7 +6,7 @@ channels and can be used as PWM or Quadrature Decoder. Available clock sources ...@@ -6,7 +6,7 @@ channels and can be used as PWM or Quadrature Decoder. Available clock sources
are the cpu's HFPERCLK (with a 10-bit prescaler) or an external pin. are the cpu's HFPERCLK (with a 10-bit prescaler) or an external pin.
Required properties: Required properties:
- compatible : Should be efm32,timer - compatible : Should be "energymicro,efm32-timer"
- reg : Address and length of the register set - reg : Address and length of the register set
- clocks : Should contain a reference to the HFPERCLK - clocks : Should contain a reference to the HFPERCLK
...@@ -16,7 +16,7 @@ Optional properties: ...@@ -16,7 +16,7 @@ Optional properties:
Example: Example:
timer@40010c00 { timer@40010c00 {
compatible = "efm32,timer"; compatible = "energymicro,efm32-timer";
reg = <0x40010c00 0x400>; reg = <0x40010c00 0x400>;
interrupts = <14>; interrupts = <14>;
clocks = <&cmu clk_HFPERCLKTIMER3>; clocks = <&cmu clk_HFPERCLKTIMER3>;
......
Freescale FlexTimer Module (FTM) Timer
Required properties:
- compatible : should be "fsl,ftm-timer"
- reg : Specifies base physical address and size of the register sets for the
clock event device and clock source device.
- interrupts : Should be the clock event device interrupt.
- clocks : The clocks provided by the SoC to drive the timer, must contain an
entry for each entry in clock-names.
- clock-names : Must include the following entries:
o "ftm-evt"
o "ftm-src"
o "ftm-evt-counter-en"
o "ftm-src-counter-en"
- big-endian: One boolean property, the big endian mode will be in use if it is
present, or the little endian mode will be in use for all the device registers.
Example:
ftm: ftm@400b8000 {
compatible = "fsl,ftm-timer";
reg = <0x400b8000 0x1000 0x400b9000 0x1000>;
interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "ftm-evt", "ftm-src",
"ftm-evt-counter-en", "ftm-src-counter-en";
clocks = <&clks VF610_CLK_FTM2>,
<&clks VF610_CLK_FTM3>,
<&clks VF610_CLK_FTM2_EXT_FIX_EN>,
<&clks VF610_CLK_FTM3_EXT_FIX_EN>;
big-endian;
};
...@@ -428,6 +428,17 @@ ...@@ -428,6 +428,17 @@
status = "disabled"; status = "disabled";
}; };
timer@01c60000 {
compatible = "allwinner,sun6i-a31-hstimer", "allwinner,sun7i-a20-hstimer";
reg = <0x01c60000 0x1000>;
interrupts = <0 51 4>,
<0 52 4>,
<0 53 4>,
<0 54 4>;
clocks = <&ahb1_gates 19>;
resets = <&ahb1_rst 19>;
};
spi0: spi@01c68000 { spi0: spi@01c68000 {
compatible = "allwinner,sun6i-a31-spi"; compatible = "allwinner,sun6i-a31-spi";
reg = <0x01c68000 0x1000>; reg = <0x01c68000 0x1000>;
......
...@@ -88,6 +88,14 @@ ...@@ -88,6 +88,14 @@
interrupts = <1 13 0x304>; interrupts = <1 13 0x304>;
}; };
timer@2c000200 {
compatible = "arm,cortex-a5-global-timer",
"arm,cortex-a9-global-timer";
reg = <0x2c000200 0x20>;
interrupts = <1 11 0x304>;
clocks = <&oscclk0>;
};
watchdog@2c000620 { watchdog@2c000620 {
compatible = "arm,cortex-a5-twd-wdt"; compatible = "arm,cortex-a5-twd-wdt";
reg = <0x2c000620 0x20>; reg = <0x2c000620 0x20>;
...@@ -120,7 +128,7 @@ ...@@ -120,7 +128,7 @@
compatible = "arm,vexpress,config-bus"; compatible = "arm,vexpress,config-bus";
arm,vexpress,config-bridge = <&v2m_sysreg>; arm,vexpress,config-bridge = <&v2m_sysreg>;
osc@0 { oscclk0: osc@0 {
/* CPU and internal AXI reference clock */ /* CPU and internal AXI reference clock */
compatible = "arm,vexpress-osc"; compatible = "arm,vexpress-osc";
arm,vexpress-sysreg,func = <1 0>; arm,vexpress-sysreg,func = <1 0>;
......
...@@ -347,6 +347,19 @@ ...@@ -347,6 +347,19 @@
status = "disabled"; status = "disabled";
}; };
ftm: ftm@400b8000 {
compatible = "fsl,ftm-timer";
reg = <0x400b8000 0x1000 0x400b9000 0x1000>;
interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "ftm-evt", "ftm-src",
"ftm-evt-counter-en", "ftm-src-counter-en";
clocks = <&clks VF610_CLK_FTM2>,
<&clks VF610_CLK_FTM3>,
<&clks VF610_CLK_FTM2_EXT_FIX_EN>,
<&clks VF610_CLK_FTM3_EXT_FIX_EN>;
status = "disabled";
};
fec0: ethernet@400d0000 { fec0: ethernet@400d0000 {
compatible = "fsl,mvf600-fec"; compatible = "fsl,mvf600-fec";
reg = <0x400d0000 0x1000>; reg = <0x400d0000 0x1000>;
......
...@@ -4,6 +4,7 @@ config ARCH_VEXPRESS ...@@ -4,6 +4,7 @@ config ARCH_VEXPRESS
select ARCH_SUPPORTS_BIG_ENDIAN select ARCH_SUPPORTS_BIG_ENDIAN
select ARM_AMBA select ARM_AMBA
select ARM_GIC select ARM_GIC
select ARM_GLOBAL_TIMER
select ARM_TIMER_SP804 select ARM_TIMER_SP804
select COMMON_CLK_VERSATILE select COMMON_CLK_VERSATILE
select HAVE_ARM_SCU if SMP select HAVE_ARM_SCU if SMP
......
...@@ -136,6 +136,11 @@ config CLKSRC_SAMSUNG_PWM ...@@ -136,6 +136,11 @@ config CLKSRC_SAMSUNG_PWM
for all devicetree enabled platforms. This driver will be for all devicetree enabled platforms. This driver will be
needed only on systems that do not have the Exynos MCT available. 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 config VF_PIT_TIMER
bool bool
help help
......
...@@ -31,6 +31,7 @@ obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o ...@@ -31,6 +31,7 @@ obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.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_VF_PIT_TIMER) += vf_pit_timer.o
obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
......
...@@ -246,11 +246,12 @@ static void __init global_timer_of_register(struct device_node *np) ...@@ -246,11 +246,12 @@ static void __init global_timer_of_register(struct device_node *np)
int err = 0; 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 * fire when the timer value is greater than or equal to. In previous
* revisions the comparators fired when the timer value was equal to. * 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"); pr_warn("global-timer: non support for this cpu version.\n");
return; return;
} }
......
...@@ -106,7 +106,7 @@ static void __init add_clocksource(struct device_node *source_timer) ...@@ -106,7 +106,7 @@ static void __init add_clocksource(struct device_node *source_timer)
sched_rate = rate; sched_rate = rate;
} }
static u64 read_sched_clock(void) static u64 notrace read_sched_clock(void)
{ {
return ~__raw_readl(sched_io_base); return ~__raw_readl(sched_io_base);
} }
......
...@@ -318,10 +318,8 @@ static int em_sti_probe(struct platform_device *pdev) ...@@ -318,10 +318,8 @@ static int em_sti_probe(struct platform_device *pdev)
int irq; int irq;
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
if (p == NULL) { if (p == NULL)
dev_err(&pdev->dev, "failed to allocate driver data\n");
return -ENOMEM; return -ENOMEM;
}
p->pdev = pdev; p->pdev = pdev;
platform_set_drvdata(pdev, p); platform_set_drvdata(pdev, p);
......
/*
* 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);
...@@ -22,22 +22,22 @@ static inline struct clocksource_mmio *to_mmio_clksrc(struct clocksource *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) 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) 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) 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) 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;
} }
/** /**
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/sched_clock.h> #include <linux/sched_clock.h>
#include <asm/delay.h>
#define TIMER_MATCH_VAL 0x0000 #define TIMER_MATCH_VAL 0x0000
#define TIMER_COUNT_VAL 0x0004 #define TIMER_COUNT_VAL 0x0004
#define TIMER_ENABLE 0x0008 #define TIMER_ENABLE 0x0008
...@@ -179,6 +181,15 @@ static u64 notrace msm_sched_clock_read(void) ...@@ -179,6 +181,15 @@ static u64 notrace msm_sched_clock_read(void)
return msm_clocksource.read(&msm_clocksource); 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, static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
bool percpu) bool percpu)
{ {
...@@ -217,6 +228,8 @@ static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, ...@@ -217,6 +228,8 @@ static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
if (res) if (res)
pr_err("clocksource_register failed\n"); pr_err("clocksource_register failed\n");
sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); 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 #ifdef CONFIG_ARCH_QCOM
......
此差异已折叠。
...@@ -11,37 +11,48 @@ ...@@ -11,37 +11,48 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * 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/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/clk.h> #include <linux/ioport.h>
#include <linux/irq.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/module.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/pm_runtime.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; void __iomem *mapbase;
struct clk *clk; struct clk *clk;
struct irqaction irqaction;
struct platform_device *pdev; struct sh_mtu2_channel *channels;
unsigned long rate; unsigned int num_channels;
unsigned long periodic;
struct clock_event_device ced; bool legacy;
bool has_clockevent;
}; };
static DEFINE_RAW_SPINLOCK(sh_mtu2_lock); static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
...@@ -55,6 +66,88 @@ static DEFINE_RAW_SPINLOCK(sh_mtu2_lock); ...@@ -55,6 +66,88 @@ static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
#define TCNT 5 /* channel register */ #define TCNT 5 /* channel register */
#define TGR 6 /* 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[] = { static unsigned long mtu2_reg_offs[] = {
[TCR] = 0, [TCR] = 0,
[TMDR] = 1, [TMDR] = 1,
...@@ -65,135 +158,143 @@ static unsigned long mtu2_reg_offs[] = { ...@@ -65,135 +158,143 @@ static unsigned long mtu2_reg_offs[] = {
[TGR] = 8, [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; unsigned long offs;
if (reg_nr == TSTR) if (reg_nr == TSTR) {
return ioread8(base + cfg->channel_offset); if (ch->mtu->legacy)
return ioread8(ch->mtu->mapbase);
else
return ioread8(ch->mtu->mapbase + 0x280);
}
offs = mtu2_reg_offs[reg_nr]; offs = mtu2_reg_offs[reg_nr];
if ((reg_nr == TCNT) || (reg_nr == TGR)) if ((reg_nr == TCNT) || (reg_nr == TGR))
return ioread16(base + offs); return ioread16(ch->base + offs);
else 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) unsigned long value)
{ {
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
void __iomem *base = p->mapbase;
unsigned long offs; unsigned long offs;
if (reg_nr == TSTR) { if (reg_nr == TSTR) {
iowrite8(value, base + cfg->channel_offset); if (ch->mtu->legacy)
return; return iowrite8(value, ch->mtu->mapbase);
else
return iowrite8(value, ch->mtu->mapbase + 0x280);
} }
offs = mtu2_reg_offs[reg_nr]; offs = mtu2_reg_offs[reg_nr];
if ((reg_nr == TCNT) || (reg_nr == TGR)) if ((reg_nr == TCNT) || (reg_nr == TGR))
iowrite16(value, base + offs); iowrite16(value, ch->base + offs);
else 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; unsigned long flags, value;
/* start stop register shared by multiple timer channels */ /* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&sh_mtu2_lock, flags); raw_spin_lock_irqsave(&sh_mtu2_lock, flags);
value = sh_mtu2_read(p, TSTR); value = sh_mtu2_read(ch, TSTR);
if (start) if (start)
value |= 1 << cfg->timer_bit; value |= 1 << ch->index;
else 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); 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; int ret;
pm_runtime_get_sync(&p->pdev->dev); pm_runtime_get_sync(&ch->mtu->pdev->dev);
dev_pm_syscore_device(&p->pdev->dev, true); dev_pm_syscore_device(&ch->mtu->pdev->dev, true);
/* enable clock */ /* enable clock */
ret = clk_enable(p->clk); ret = clk_enable(ch->mtu->clk);
if (ret) { 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; return ret;
} }
/* make sure channel is disabled */ /* make sure channel is disabled */
sh_mtu2_start_stop_ch(p, 0); sh_mtu2_start_stop_ch(ch, 0);
p->rate = clk_get_rate(p->clk) / 64; rate = clk_get_rate(ch->mtu->clk) / 64;
p->periodic = (p->rate + HZ/2) / HZ; periodic = (rate + HZ/2) / HZ;
/* "Periodic Counter Operation" */ /*
sh_mtu2_write(p, TCR, 0x23); /* TGRA clear, divide clock by 64 */ * "Periodic Counter Operation"
sh_mtu2_write(p, TIOR, 0); * Clear on TGRA compare match, divide clock by 64.
sh_mtu2_write(p, TGR, p->periodic); */
sh_mtu2_write(p, TCNT, 0); sh_mtu2_write(ch, TCR, TCR_CCLR_TGRA | TCR_TPSC_P64);
sh_mtu2_write(p, TMDR, 0); sh_mtu2_write(ch, TIOR, TIOC_IOCH(TIOR_OC_0_CLEAR) |
sh_mtu2_write(p, TIER, 0x01); 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 */ /* enable channel */
sh_mtu2_start_stop_ch(p, 1); sh_mtu2_start_stop_ch(ch, 1);
return 0; return 0;
} }
static void sh_mtu2_disable(struct sh_mtu2_priv *p) static void sh_mtu2_disable(struct sh_mtu2_channel *ch)
{ {
/* disable channel */ /* disable channel */
sh_mtu2_start_stop_ch(p, 0); sh_mtu2_start_stop_ch(ch, 0);
/* stop clock */ /* stop clock */
clk_disable(p->clk); clk_disable(ch->mtu->clk);
dev_pm_syscore_device(&p->pdev->dev, false); dev_pm_syscore_device(&ch->mtu->pdev->dev, false);
pm_runtime_put(&p->pdev->dev); pm_runtime_put(&ch->mtu->pdev->dev);
} }
static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id) 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 */ /* acknowledge interrupt */
sh_mtu2_read(p, TSR); sh_mtu2_read(ch, TSR);
sh_mtu2_write(p, TSR, 0xfe); sh_mtu2_write(ch, TSR, ~TSR_TGFA);
/* notify clockevent layer */ /* notify clockevent layer */
p->ced.event_handler(&p->ced); ch->ced.event_handler(&ch->ced);
return IRQ_HANDLED; 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, static void sh_mtu2_clock_event_mode(enum clock_event_mode mode,
struct clock_event_device *ced) 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; int disabled = 0;
/* deal with old setting first */ /* deal with old setting first */
switch (ced->mode) { switch (ced->mode) {
case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_PERIODIC:
sh_mtu2_disable(p); sh_mtu2_disable(ch);
disabled = 1; disabled = 1;
break; break;
default: default:
...@@ -202,12 +303,13 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode, ...@@ -202,12 +303,13 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode,
switch (mode) { switch (mode) {
case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_PERIODIC:
dev_info(&p->pdev->dev, "used for periodic clock events\n"); dev_info(&ch->mtu->pdev->dev,
sh_mtu2_enable(p); "ch%u: used for periodic clock events\n", ch->index);
sh_mtu2_enable(ch);
break; break;
case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_UNUSED:
if (!disabled) if (!disabled)
sh_mtu2_disable(p); sh_mtu2_disable(ch);
break; break;
case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_SHUTDOWN:
default: default:
...@@ -217,125 +319,207 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode, ...@@ -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) 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) 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, static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
char *name, unsigned long rating) const char *name)
{ {
struct clock_event_device *ced = &p->ced; struct clock_event_device *ced = &ch->ced;
int ret; int ret;
memset(ced, 0, sizeof(*ced));
ced->name = name; ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->features = CLOCK_EVT_FEAT_PERIODIC;
ced->rating = rating; ced->rating = 200;
ced->cpumask = cpumask_of(0); ced->cpumask = cpu_possible_mask;
ced->set_mode = sh_mtu2_clock_event_mode; ced->set_mode = sh_mtu2_clock_event_mode;
ced->suspend = sh_mtu2_clock_event_suspend; ced->suspend = sh_mtu2_clock_event_suspend;
ced->resume = sh_mtu2_clock_event_resume; 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); 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) { if (ret) {
dev_err(&p->pdev->dev, "failed to request irq %d\n", dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n",
p->irqaction.irq); ch->index, ch->irq);
return; return;
} }
} }
static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name, static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name,
unsigned long clockevent_rating) bool clockevent)
{ {
if (clockevent_rating) if (clockevent) {
sh_mtu2_register_clockevent(p, name, clockevent_rating); ch->mtu->has_clockevent = true;
sh_mtu2_register_clockevent(ch, name);
}
return 0; 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; static const unsigned int channel_offsets[] = {
struct resource *res; 0x300, 0x380, 0x000,
int irq, ret; };
ret = -ENXIO; bool clockevent;
ch->mtu = mtu;
if (mtu->legacy) {
struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
memset(p, 0, sizeof(*p)); clockevent = cfg->clockevent_rating != 0;
p->pdev = pdev;
if (!cfg) { ch->irq = platform_get_irq(mtu->pdev, 0);
dev_err(&p->pdev->dev, "missing platform data\n"); ch->base = mtu->mapbase - cfg->channel_offset;
goto err0; ch->index = cfg->timer_bit;
} else {
char name[6];
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);
}
static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu)
{
struct resource *res;
res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0); res = platform_get_resource(mtu->pdev, IORESOURCE_MEM, 0);
if (!res) { if (!res) {
dev_err(&p->pdev->dev, "failed to get I/O memory\n"); dev_err(&mtu->pdev->dev, "failed to get I/O memory\n");
goto err0; return -ENXIO;
} }
irq = platform_get_irq(p->pdev, 0); mtu->mapbase = ioremap_nocache(res->start, resource_size(res));
if (irq < 0) { if (mtu->mapbase == NULL)
dev_err(&p->pdev->dev, "failed to get irq\n"); return -ENXIO;
goto err0;
/*
* 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;
} }
/* map memory, let mapbase point to our channel */ return 0;
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"); static void sh_mtu2_unmap_memory(struct sh_mtu2_device *mtu)
goto err0; {
if (mtu->legacy) {
struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
mtu->mapbase -= cfg->channel_offset;
}
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()) */ /* Get hold of clock. */
p->irqaction.name = dev_name(&p->pdev->dev); mtu->clk = clk_get(&mtu->pdev->dev, mtu->legacy ? "mtu2_fck" : "fck");
p->irqaction.handler = sh_mtu2_interrupt; if (IS_ERR(mtu->clk)) {
p->irqaction.dev_id = p; dev_err(&mtu->pdev->dev, "cannot get clock\n");
p->irqaction.irq = irq; return PTR_ERR(mtu->clk);
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;
} }
ret = clk_prepare(p->clk); ret = clk_prepare(mtu->clk);
if (ret < 0) if (ret < 0)
goto err2; goto err_clk_put;
/* 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;
}
ret = sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev), if (mtu->legacy) {
cfg->clockevent_rating); 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) if (ret < 0)
goto err3; goto err_unmap;
}
}
platform_set_drvdata(pdev, mtu);
return 0; return 0;
err3:
clk_unprepare(p->clk); err_unmap:
err2: kfree(mtu->channels);
clk_put(p->clk); sh_mtu2_unmap_memory(mtu);
err1: err_clk_unprepare:
iounmap(p->mapbase); clk_unprepare(mtu->clk);
err0: err_clk_put:
clk_put(mtu->clk);
return ret; return ret;
} }
static int sh_mtu2_probe(struct platform_device *pdev) static int sh_mtu2_probe(struct platform_device *pdev)
{ {
struct sh_mtu2_priv *p = platform_get_drvdata(pdev); struct sh_mtu2_device *mtu = platform_get_drvdata(pdev);
struct sh_timer_config *cfg = pdev->dev.platform_data;
int ret; int ret;
if (!is_early_platform_device(pdev)) { if (!is_early_platform_device(pdev)) {
...@@ -343,20 +527,18 @@ static int sh_mtu2_probe(struct platform_device *pdev) ...@@ -343,20 +527,18 @@ static int sh_mtu2_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
} }
if (p) { if (mtu) {
dev_info(&pdev->dev, "kept as earlytimer\n"); dev_info(&pdev->dev, "kept as earlytimer\n");
goto out; goto out;
} }
p = kmalloc(sizeof(*p), GFP_KERNEL); mtu = kzalloc(sizeof(*mtu), GFP_KERNEL);
if (p == NULL) { if (mtu == NULL)
dev_err(&pdev->dev, "failed to allocate driver data\n");
return -ENOMEM; return -ENOMEM;
}
ret = sh_mtu2_setup(p, pdev); ret = sh_mtu2_setup(mtu, pdev);
if (ret) { if (ret) {
kfree(p); kfree(mtu);
pm_runtime_idle(&pdev->dev); pm_runtime_idle(&pdev->dev);
return ret; return ret;
} }
...@@ -364,7 +546,7 @@ static int sh_mtu2_probe(struct platform_device *pdev) ...@@ -364,7 +546,7 @@ static int sh_mtu2_probe(struct platform_device *pdev)
return 0; return 0;
out: out:
if (cfg->clockevent_rating) if (mtu->has_clockevent)
pm_runtime_irq_safe(&pdev->dev); pm_runtime_irq_safe(&pdev->dev);
else else
pm_runtime_idle(&pdev->dev); pm_runtime_idle(&pdev->dev);
...@@ -377,12 +559,20 @@ static int sh_mtu2_remove(struct platform_device *pdev) ...@@ -377,12 +559,20 @@ static int sh_mtu2_remove(struct platform_device *pdev)
return -EBUSY; /* cannot unregister clockevent */ 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 = { static struct platform_driver sh_mtu2_device_driver = {
.probe = sh_mtu2_probe, .probe = sh_mtu2_probe,
.remove = sh_mtu2_remove, .remove = sh_mtu2_remove,
.driver = { .driver = {
.name = "sh_mtu2", .name = "sh_mtu2",
} },
.id_table = sh_mtu2_id_table,
}; };
static int __init sh_mtu2_init(void) static int __init sh_mtu2_init(void)
......
此差异已折叠。
...@@ -272,4 +272,5 @@ static void __init efm32_timer_init(struct device_node *np) ...@@ -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);
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
#include <linux/reset.h>
#include <linux/sched_clock.h> #include <linux/sched_clock.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
...@@ -143,6 +144,7 @@ static u64 sun5i_timer_sched_read(void) ...@@ -143,6 +144,7 @@ static u64 sun5i_timer_sched_read(void)
static void __init sun5i_timer_init(struct device_node *node) static void __init sun5i_timer_init(struct device_node *node)
{ {
struct reset_control *rstc;
unsigned long rate; unsigned long rate;
struct clk *clk; struct clk *clk;
int ret, irq; int ret, irq;
...@@ -162,6 +164,10 @@ static void __init sun5i_timer_init(struct device_node *node) ...@@ -162,6 +164,10 @@ static void __init sun5i_timer_init(struct device_node *node)
clk_prepare_enable(clk); clk_prepare_enable(clk);
rate = clk_get_rate(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(~0, timer_base + TIMER_INTVAL_LO_REG(1));
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
timer_base + TIMER_CTL_REG(1)); timer_base + TIMER_CTL_REG(1));
......
...@@ -7,6 +7,7 @@ struct sh_timer_config { ...@@ -7,6 +7,7 @@ struct sh_timer_config {
int timer_bit; int timer_bit;
unsigned long clockevent_rating; unsigned long clockevent_rating;
unsigned long clocksource_rating; unsigned long clocksource_rating;
unsigned int channels_mask;
}; };
#endif /* __SH_TIMER_H__ */ #endif /* __SH_TIMER_H__ */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册