1
0
Fork 0

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

Pull clockevents/clocksource changes from Daniel Lezcano:

  - Removed dead code in the files related to mach-msm for qcom (Stephen Boyd)
  - Cleaned up code for exynos_mct (Krzysztof Kozlowski)
  - Added the new timer lpc3220 (Joachim Eastwood)
  - Added the new timer STM32 and ARM system timer (Maxime Coquelin)
This commit is contained in:
Thomas Gleixner 2015-06-02 16:57:47 +02:00
commit 09cbbf0c16
10 changed files with 632 additions and 78 deletions

View file

@ -0,0 +1,26 @@
* ARMv7M System Timer
ARMv7-M includes a system timer, known as SysTick. Current driver only
implements the clocksource feature.
Required properties:
- compatible : Should be "arm,armv7m-systick"
- reg : The address range of the timer
Required clocking property, have to be one of:
- clocks : The input clock of the timer
- clock-frequency : The rate in HZ in input of the ARM SysTick
Examples:
systick: timer@e000e010 {
compatible = "arm,armv7m-systick";
reg = <0xe000e010 0x10>;
clocks = <&clk_systick>;
};
systick: timer@e000e010 {
compatible = "arm,armv7m-systick";
reg = <0xe000e010 0x10>;
clock-frequency = <90000000>;
};

View file

@ -0,0 +1,26 @@
* NXP LPC3220 timer
The NXP LPC3220 timer is used on a wide range of NXP SoCs. This
includes LPC32xx, LPC178x, LPC18xx and LPC43xx parts.
Required properties:
- compatible:
Should be "nxp,lpc3220-timer".
- reg:
Address and length of the register set.
- interrupts:
Reference to the timer interrupt
- clocks:
Should contain a reference to timer clock.
- clock-names:
Should contain "timerclk".
Example:
timer1: timer@40085000 {
compatible = "nxp,lpc3220-timer";
reg = <0x40085000 0x1000>;
interrupts = <13>;
clocks = <&ccu1 CLK_CPU_TIMER1>;
clock-names = "timerclk";
};

View file

@ -0,0 +1,22 @@
. STMicroelectronics STM32 timer
The STM32 MCUs family has several general-purpose 16 and 32 bits timers.
Required properties:
- compatible : Should be "st,stm32-timer"
- reg : Address and length of the register set
- clocks : Reference on the timer input clock
- interrupts : Reference to the timer interrupt
Optional properties:
- resets: Reference to a reset controller asserting the timer
Example:
timer5: timer@40000c00 {
compatible = "st,stm32-timer";
reg = <0x40000c00 0x400>;
interrupts = <50>;
resets = <&rrc 259>;
clocks = <&clk_pmtr1>;
};

View file

@ -106,6 +106,16 @@ config CLKSRC_EFM32
Support to use the timers of EFM32 SoCs as clock source and clock
event device.
config CLKSRC_LPC32XX
bool
select CLKSRC_MMIO
select CLKSRC_OF
config CLKSRC_STM32
bool "Clocksource for STM32 SoCs" if COMPILE_TEST
depends on OF
select CLKSRC_MMIO
config ARM_ARCH_TIMER
bool
select CLKSRC_OF if OF
@ -139,6 +149,13 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
help
Use ARM global timer clock source as sched_clock
config ARMV7M_SYSTICK
bool
select CLKSRC_OF if OF
select CLKSRC_MMIO
help
This options enables support for the ARMv7M system timer unit
config ATMEL_PIT
select CLKSRC_OF if OF
def_bool SOC_AT91SAM9 || SOC_SAMA5

View file

@ -36,7 +36,9 @@ obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o
obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm_kona_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
obj-$(CONFIG_CLKSRC_STM32) += timer-stm32.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
obj-$(CONFIG_CLKSRC_LPC32XX) += time-lpc32xx.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
@ -45,6 +47,7 @@ obj-$(CONFIG_MTK_TIMER) += mtk_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
obj-$(CONFIG_ARMV7M_SYSTICK) += armv7m_systick.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

View file

@ -0,0 +1,79 @@
/*
* Copyright (C) Maxime Coquelin 2015
* Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/kernel.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk.h>
#include <linux/bitops.h>
#define SYST_CSR 0x00
#define SYST_RVR 0x04
#define SYST_CVR 0x08
#define SYST_CALIB 0x0c
#define SYST_CSR_ENABLE BIT(0)
#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
static void __init system_timer_of_register(struct device_node *np)
{
struct clk *clk = NULL;
void __iomem *base;
u32 rate;
int ret;
base = of_iomap(np, 0);
if (!base) {
pr_warn("system-timer: invalid base address\n");
return;
}
ret = of_property_read_u32(np, "clock-frequency", &rate);
if (ret) {
clk = of_clk_get(np, 0);
if (IS_ERR(clk))
goto out_unmap;
ret = clk_prepare_enable(clk);
if (ret)
goto out_clk_put;
rate = clk_get_rate(clk);
if (!rate)
goto out_clk_disable;
}
writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate,
200, 24, clocksource_mmio_readl_down);
if (ret) {
pr_err("failed to init clocksource (%d)\n", ret);
if (clk)
goto out_clk_disable;
else
goto out_unmap;
}
pr_info("ARM System timer initialized as clocksource\n");
return;
out_clk_disable:
clk_disable_unprepare(clk);
out_clk_put:
clk_put(clk);
out_unmap:
iounmap(base);
pr_warn("ARM System timer register failed (%d)\n", ret);
}
CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
system_timer_of_register);

View file

@ -209,7 +209,7 @@ static void exynos4_frc_resume(struct clocksource *cs)
exynos4_mct_frc_start();
}
struct clocksource mct_frc = {
static struct clocksource mct_frc = {
.name = "mct-frc",
.rating = 400,
.read = exynos4_frc_read,
@ -413,7 +413,7 @@ static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
}
}
static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
static void exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
{
struct clock_event_device *evt = &mevt->evt;
@ -426,12 +426,8 @@ static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
exynos4_mct_tick_stop(mevt);
/* Clear the MCT tick interrupt */
if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1)
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
return 1;
} else {
return 0;
}
}
static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
@ -564,18 +560,6 @@ out_irq:
free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick);
}
void __init mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1)
{
mct_irqs[MCT_G0_IRQ] = irq_g0;
mct_irqs[MCT_L0_IRQ] = irq_l0;
mct_irqs[MCT_L1_IRQ] = irq_l1;
mct_int_type = MCT_INT_SPI;
exynos4_timer_resources(NULL, base);
exynos4_clocksource_init();
exynos4_clockevent_init();
}
static void __init mct_init_dt(struct device_node *np, unsigned int int_type)
{
u32 nr_irqs, i;

View file

@ -40,8 +40,6 @@
#define GPT_HZ 32768
#define MSM_DGT_SHIFT 5
static void __iomem *event_base;
static void __iomem *sts_base;
@ -232,7 +230,6 @@ err:
register_current_timer_delay(&msm_delay_timer);
}
#ifdef CONFIG_ARCH_QCOM
static void __init msm_dt_timer_init(struct device_node *np)
{
u32 freq;
@ -285,59 +282,3 @@ static void __init msm_dt_timer_init(struct device_node *np)
}
CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
#else
static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
u32 sts)
{
void __iomem *base;
base = ioremap(addr, SZ_256);
if (!base) {
pr_err("Failed to map timer base\n");
return -ENOMEM;
}
event_base = base + event;
source_base = base + source;
if (sts)
sts_base = base + sts;
return 0;
}
static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
{
/*
* Shift timer count down by a constant due to unreliable lower bits
* on some targets.
*/
return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
}
void __init msm7x01_timer_init(void)
{
struct clocksource *cs = &msm_clocksource;
if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
return;
cs->read = msm_read_timer_count_shift;
cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
/* 600 KHz */
msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
false);
}
void __init msm7x30_timer_init(void)
{
if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
return;
msm_timer_init(24576000 / 4, 32, 1, false);
}
void __init qsd8x50_timer_init(void)
{
if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
return;
msm_timer_init(19200000 / 4, 32, 7, false);
}
#endif

View file

@ -0,0 +1,272 @@
/*
* Clocksource driver for NXP LPC32xx/18xx/43xx timer
*
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
*
* Based on:
* time-efm32 Copyright (C) 2013 Pengutronix
* mach-lpc32xx/timer.c Copyright (C) 2009 - 2010 NXP Semiconductors
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#define LPC32XX_TIMER_IR 0x000
#define LPC32XX_TIMER_IR_MR0INT BIT(0)
#define LPC32XX_TIMER_TCR 0x004
#define LPC32XX_TIMER_TCR_CEN BIT(0)
#define LPC32XX_TIMER_TCR_CRST BIT(1)
#define LPC32XX_TIMER_TC 0x008
#define LPC32XX_TIMER_PR 0x00c
#define LPC32XX_TIMER_MCR 0x014
#define LPC32XX_TIMER_MCR_MR0I BIT(0)
#define LPC32XX_TIMER_MCR_MR0R BIT(1)
#define LPC32XX_TIMER_MCR_MR0S BIT(2)
#define LPC32XX_TIMER_MR0 0x018
#define LPC32XX_TIMER_CTCR 0x070
struct lpc32xx_clock_event_ddata {
struct clock_event_device evtdev;
void __iomem *base;
};
/* Needed for the sched clock */
static void __iomem *clocksource_timer_counter;
static u64 notrace lpc32xx_read_sched_clock(void)
{
return readl(clocksource_timer_counter);
}
static int lpc32xx_clkevt_next_event(unsigned long delta,
struct clock_event_device *evtdev)
{
struct lpc32xx_clock_event_ddata *ddata =
container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
/*
* Place timer in reset and program the delta in the prescale
* register (PR). When the prescale counter matches the value
* in PR the counter register is incremented and the compare
* match will trigger. After setup the timer is released from
* reset and enabled.
*/
writel_relaxed(LPC32XX_TIMER_TCR_CRST, ddata->base + LPC32XX_TIMER_TCR);
writel_relaxed(delta, ddata->base + LPC32XX_TIMER_PR);
writel_relaxed(LPC32XX_TIMER_TCR_CEN, ddata->base + LPC32XX_TIMER_TCR);
return 0;
}
static int lpc32xx_clkevt_shutdown(struct clock_event_device *evtdev)
{
struct lpc32xx_clock_event_ddata *ddata =
container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
/* Disable the timer */
writel_relaxed(0, ddata->base + LPC32XX_TIMER_TCR);
return 0;
}
static int lpc32xx_clkevt_oneshot(struct clock_event_device *evtdev)
{
/*
* When using oneshot, we must also disable the timer
* to wait for the first call to set_next_event().
*/
return lpc32xx_clkevt_shutdown(evtdev);
}
static irqreturn_t lpc32xx_clock_event_handler(int irq, void *dev_id)
{
struct lpc32xx_clock_event_ddata *ddata = dev_id;
/* Clear match on channel 0 */
writel_relaxed(LPC32XX_TIMER_IR_MR0INT, ddata->base + LPC32XX_TIMER_IR);
ddata->evtdev.event_handler(&ddata->evtdev);
return IRQ_HANDLED;
}
static struct lpc32xx_clock_event_ddata lpc32xx_clk_event_ddata = {
.evtdev = {
.name = "lpc3220 clockevent",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 300,
.set_next_event = lpc32xx_clkevt_next_event,
.set_state_shutdown = lpc32xx_clkevt_shutdown,
.set_state_oneshot = lpc32xx_clkevt_oneshot,
},
};
static int __init lpc32xx_clocksource_init(struct device_node *np)
{
void __iomem *base;
unsigned long rate;
struct clk *clk;
int ret;
clk = of_clk_get_by_name(np, "timerclk");
if (IS_ERR(clk)) {
pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("clock enable failed (%d)\n", ret);
goto err_clk_enable;
}
base = of_iomap(np, 0);
if (!base) {
pr_err("unable to map registers\n");
ret = -EADDRNOTAVAIL;
goto err_iomap;
}
/*
* Disable and reset timer then set it to free running timer
* mode (CTCR) with no prescaler (PR) or match operations (MCR).
* After setup the timer is released from reset and enabled.
*/
writel_relaxed(LPC32XX_TIMER_TCR_CRST, base + LPC32XX_TIMER_TCR);
writel_relaxed(0, base + LPC32XX_TIMER_PR);
writel_relaxed(0, base + LPC32XX_TIMER_MCR);
writel_relaxed(0, base + LPC32XX_TIMER_CTCR);
writel_relaxed(LPC32XX_TIMER_TCR_CEN, base + LPC32XX_TIMER_TCR);
rate = clk_get_rate(clk);
ret = clocksource_mmio_init(base + LPC32XX_TIMER_TC, "lpc3220 timer",
rate, 300, 32, clocksource_mmio_readl_up);
if (ret) {
pr_err("failed to init clocksource (%d)\n", ret);
goto err_clocksource_init;
}
clocksource_timer_counter = base + LPC32XX_TIMER_TC;
sched_clock_register(lpc32xx_read_sched_clock, 32, rate);
return 0;
err_clocksource_init:
iounmap(base);
err_iomap:
clk_disable_unprepare(clk);
err_clk_enable:
clk_put(clk);
return ret;
}
static int __init lpc32xx_clockevent_init(struct device_node *np)
{
void __iomem *base;
unsigned long rate;
struct clk *clk;
int ret, irq;
clk = of_clk_get_by_name(np, "timerclk");
if (IS_ERR(clk)) {
pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("clock enable failed (%d)\n", ret);
goto err_clk_enable;
}
base = of_iomap(np, 0);
if (!base) {
pr_err("unable to map registers\n");
ret = -EADDRNOTAVAIL;
goto err_iomap;
}
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
pr_err("get irq failed\n");
ret = -ENOENT;
goto err_irq;
}
/*
* Disable timer and clear any pending interrupt (IR) on match
* channel 0 (MR0). Configure a compare match value of 1 on MR0
* and enable interrupt, reset on match and stop on match (MCR).
*/
writel_relaxed(0, base + LPC32XX_TIMER_TCR);
writel_relaxed(0, base + LPC32XX_TIMER_CTCR);
writel_relaxed(LPC32XX_TIMER_IR_MR0INT, base + LPC32XX_TIMER_IR);
writel_relaxed(1, base + LPC32XX_TIMER_MR0);
writel_relaxed(LPC32XX_TIMER_MCR_MR0I | LPC32XX_TIMER_MCR_MR0R |
LPC32XX_TIMER_MCR_MR0S, base + LPC32XX_TIMER_MCR);
rate = clk_get_rate(clk);
lpc32xx_clk_event_ddata.base = base;
clockevents_config_and_register(&lpc32xx_clk_event_ddata.evtdev,
rate, 1, -1);
ret = request_irq(irq, lpc32xx_clock_event_handler,
IRQF_TIMER | IRQF_IRQPOLL, "lpc3220 clockevent",
&lpc32xx_clk_event_ddata);
if (ret) {
pr_err("request irq failed\n");
goto err_irq;
}
return 0;
err_irq:
iounmap(base);
err_iomap:
clk_disable_unprepare(clk);
err_clk_enable:
clk_put(clk);
return ret;
}
/*
* This function asserts that we have exactly one clocksource and one
* clock_event_device in the end.
*/
static void __init lpc32xx_timer_init(struct device_node *np)
{
static int has_clocksource, has_clockevent;
int ret;
if (!has_clocksource) {
ret = lpc32xx_clocksource_init(np);
if (!ret) {
has_clocksource = 1;
return;
}
}
if (!has_clockevent) {
ret = lpc32xx_clockevent_init(np);
if (!ret) {
has_clockevent = 1;
return;
}
}
}
CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);

View file

@ -0,0 +1,184 @@
/*
* Copyright (C) Maxime Coquelin 2015
* Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
* License terms: GNU General Public License (GPL), version 2
*
* Inspired by time-efm32.c from Uwe Kleine-Koenig
*/
#include <linux/kernel.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/clk.h>
#include <linux/reset.h>
#define TIM_CR1 0x00
#define TIM_DIER 0x0c
#define TIM_SR 0x10
#define TIM_EGR 0x14
#define TIM_PSC 0x28
#define TIM_ARR 0x2c
#define TIM_CR1_CEN BIT(0)
#define TIM_CR1_OPM BIT(3)
#define TIM_CR1_ARPE BIT(7)
#define TIM_DIER_UIE BIT(0)
#define TIM_SR_UIF BIT(0)
#define TIM_EGR_UG BIT(0)
struct stm32_clock_event_ddata {
struct clock_event_device evtdev;
unsigned periodic_top;
void __iomem *base;
};
static void stm32_clock_event_set_mode(enum clock_event_mode mode,
struct clock_event_device *evtdev)
{
struct stm32_clock_event_ddata *data =
container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
void *base = data->base;
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
writel_relaxed(data->periodic_top, base + TIM_ARR);
writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, base + TIM_CR1);
break;
case CLOCK_EVT_MODE_ONESHOT:
default:
writel_relaxed(0, base + TIM_CR1);
break;
}
}
static int stm32_clock_event_set_next_event(unsigned long evt,
struct clock_event_device *evtdev)
{
struct stm32_clock_event_ddata *data =
container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
writel_relaxed(evt, data->base + TIM_ARR);
writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN,
data->base + TIM_CR1);
return 0;
}
static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
{
struct stm32_clock_event_ddata *data = dev_id;
writel_relaxed(0, data->base + TIM_SR);
data->evtdev.event_handler(&data->evtdev);
return IRQ_HANDLED;
}
static struct stm32_clock_event_ddata clock_event_ddata = {
.evtdev = {
.name = "stm32 clockevent",
.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
.set_mode = stm32_clock_event_set_mode,
.set_next_event = stm32_clock_event_set_next_event,
.rating = 200,
},
};
static void __init stm32_clockevent_init(struct device_node *np)
{
struct stm32_clock_event_ddata *data = &clock_event_ddata;
struct clk *clk;
struct reset_control *rstc;
unsigned long rate, max_delta;
int irq, ret, bits, prescaler = 1;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
pr_err("failed to get clock for clockevent (%d)\n", ret);
goto err_clk_get;
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("failed to enable timer clock for clockevent (%d)\n",
ret);
goto err_clk_enable;
}
rate = clk_get_rate(clk);
rstc = of_reset_control_get(np, NULL);
if (!IS_ERR(rstc)) {
reset_control_assert(rstc);
reset_control_deassert(rstc);
}
data->base = of_iomap(np, 0);
if (!data->base) {
pr_err("failed to map registers for clockevent\n");
goto err_iomap;
}
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
pr_err("%s: failed to get irq.\n", np->full_name);
goto err_get_irq;
}
/* Detect whether the timer is 16 or 32 bits */
writel_relaxed(~0U, data->base + TIM_ARR);
max_delta = readl_relaxed(data->base + TIM_ARR);
if (max_delta == ~0U) {
prescaler = 1;
bits = 32;
} else {
prescaler = 1024;
bits = 16;
}
writel_relaxed(0, data->base + TIM_ARR);
writel_relaxed(prescaler - 1, data->base + TIM_PSC);
writel_relaxed(TIM_EGR_UG, data->base + TIM_EGR);
writel_relaxed(TIM_DIER_UIE, data->base + TIM_DIER);
writel_relaxed(0, data->base + TIM_SR);
data->periodic_top = DIV_ROUND_CLOSEST(rate, prescaler * HZ);
clockevents_config_and_register(&data->evtdev,
DIV_ROUND_CLOSEST(rate, prescaler),
0x1, max_delta);
ret = request_irq(irq, stm32_clock_event_handler, IRQF_TIMER,
"stm32 clockevent", data);
if (ret) {
pr_err("%s: failed to request irq.\n", np->full_name);
goto err_get_irq;
}
pr_info("%s: STM32 clockevent driver initialized (%d bits)\n",
np->full_name, bits);
return;
err_get_irq:
iounmap(data->base);
err_iomap:
clk_disable_unprepare(clk);
err_clk_enable:
clk_put(clk);
err_clk_get:
return;
}
CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);