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:
commit
09cbbf0c16
|
@ -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>;
|
||||
};
|
|
@ -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";
|
||||
};
|
|
@ -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>;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem
|
|||
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;
|
||||
|
|
|
@ -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 @@ static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
|
|||
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
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
Loading…
Reference in New Issue