mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 17:41:44 +00:00
- Drop dead code on efm32 (Uwe Kleine-König)
- Move pr_fmt() before the includes on davinci driver (Bartosz Golaszewski) - Clarified timer interrupt must be specified on nuvoton DT bindings (Jonathan Neuschäfer) - Remove tango, sirf, u300 and atlas timer drivers (Arnd Bergman) - Add suspend/resume on pit64b (Claudiu Beznea) -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEGn3N4YVz0WNVyHskqDIjiipP6E8FAmAhpWEACgkQqDIjiipP 6E8gLgf+Jq+yzWCYtBn4qcmgSaivqd5Z43dqQ3nQFad9LxM78Nmg6GlnJu8hUkz3 56Ho32kucjd8Y3OvYBWlXkYYgUAOOrrXTSfpNqwpisSab0+9u2Usw0727YbRUNSk PRqSqiDjZo7RfbZFiq8zqUoba9kRjxTyS94DlFR5NA7zg/ctC7f6oXN8RALRg0ft m2p4p+k+ekZ6XdzXIxMgJqk9UWjGUbfv631pRvvqvjTSTpkmKr2ffVWE0nlaz02P IIlps+d/WMu7AypK+WydW/HFi40EdXu91WQvG4MKOt1So/WkBcsRF6EEqks8UczM SE/89j8AoRcGyQRnIyaFwu7tIFJ23g== =fxqm -----END PGP SIGNATURE----- Merge tag 'timers-v5.12-rc1' of https://git.linaro.org/people/daniel.lezcano/linux into timers/core Pull clocksource/events updates from Daniel Lezcano: - Drop dead code on efm32 (Uwe Kleine-König) - Move pr_fmt() before the includes on davinci driver (Bartosz Golaszewski) - Clarified timer interrupt must be specified on nuvoton DT bindings (Jonathan Neuschäfer) - Remove tango, sirf, u300 and atlas timer drivers (Arnd Bergman) - Add suspend/resume on pit64b (Claudiu Beznea) Link: https://lore.kernel.org/r/3747fbde-134f-5e1d-47d5-8776c1a52aa1@linaro.org
This commit is contained in:
commit
11db5710d4
@ -6,8 +6,7 @@ timer counters.
|
||||
Required properties:
|
||||
- compatible : "nuvoton,npcm750-timer" for Poleg NPCM750.
|
||||
- reg : Offset and length of the register set for the device.
|
||||
- interrupts : Contain the timer interrupt with flags for
|
||||
falling edge.
|
||||
- interrupts : Contain the timer interrupt of timer 0.
|
||||
- clocks : phandle of timer reference clock (usually a 25 MHz clock).
|
||||
|
||||
Example:
|
||||
|
@ -1,18 +0,0 @@
|
||||
ST-Ericsson U300 apptimer
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "stericsson,u300-apptimer"
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
- interrupts : A list of 4 interrupts; one for each subtimer. These
|
||||
are, in order: OS (operating system), DD (device driver) both
|
||||
adopted for EPOC/Symbian with two specific IRQs for these tasks,
|
||||
then GP1 and GP2, which are general-purpose timers.
|
||||
|
||||
Example:
|
||||
|
||||
timer {
|
||||
compatible = "stericsson,u300-apptimer";
|
||||
reg = <0xc0014000 0x1000>;
|
||||
interrupts = <24 25 26 27>;
|
||||
};
|
@ -197,12 +197,6 @@ config CLPS711X_TIMER
|
||||
help
|
||||
Enables support for the Cirrus Logic PS711 timer.
|
||||
|
||||
config ATLAS7_TIMER
|
||||
bool "Atlas7 timer driver" if COMPILE_TEST
|
||||
select CLKSRC_MMIO
|
||||
help
|
||||
Enables support for the Atlas7 timer.
|
||||
|
||||
config MXS_TIMER
|
||||
bool "MXS timer driver" if COMPILE_TEST
|
||||
select CLKSRC_MMIO
|
||||
@ -210,19 +204,6 @@ config MXS_TIMER
|
||||
help
|
||||
Enables support for the MXS timer.
|
||||
|
||||
config PRIMA2_TIMER
|
||||
bool "Prima2 timer driver" if COMPILE_TEST
|
||||
select CLKSRC_MMIO
|
||||
help
|
||||
Enables support for the Prima2 timer.
|
||||
|
||||
config U300_TIMER
|
||||
bool "U300 timer driver" if COMPILE_TEST
|
||||
depends on ARM
|
||||
select CLKSRC_MMIO
|
||||
help
|
||||
Enables support for the U300 timer.
|
||||
|
||||
config NSPIRE_TIMER
|
||||
bool "NSpire timer driver" if COMPILE_TEST
|
||||
select CLKSRC_MMIO
|
||||
@ -242,15 +223,6 @@ config INTEGRATOR_AP_TIMER
|
||||
help
|
||||
Enables support for the Integrator-AP timer.
|
||||
|
||||
config CLKSRC_EFM32
|
||||
bool "Clocksource for Energy Micro's EFM32 SoCs" if !ARCH_EFM32
|
||||
depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST)
|
||||
select CLKSRC_MMIO
|
||||
default ARCH_EFM32
|
||||
help
|
||||
Support to use the timers of EFM32 SoCs as clock source and clock
|
||||
event device.
|
||||
|
||||
config CLKSRC_LPC32XX
|
||||
bool "Clocksource for LPC32XX" if COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
@ -567,14 +539,6 @@ config CLKSRC_MIPS_GIC
|
||||
select CLOCKSOURCE_WATCHDOG
|
||||
select TIMER_OF
|
||||
|
||||
config CLKSRC_TANGO_XTAL
|
||||
bool "Clocksource for Tango SoC" if COMPILE_TEST
|
||||
depends on ARM
|
||||
select TIMER_OF
|
||||
select CLKSRC_MMIO
|
||||
help
|
||||
This enables the clocksource for Tango SoC.
|
||||
|
||||
config CLKSRC_PXA
|
||||
bool "Clocksource for PXA or SA-11x0 platform" if COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
|
@ -30,11 +30,8 @@ obj-$(CONFIG_ARMADA_370_XP_TIMER) += timer-armada-370-xp.o
|
||||
obj-$(CONFIG_ORION_TIMER) += timer-orion.o
|
||||
obj-$(CONFIG_BCM2835_TIMER) += bcm2835_timer.o
|
||||
obj-$(CONFIG_CLPS711X_TIMER) += clps711x-timer.o
|
||||
obj-$(CONFIG_ATLAS7_TIMER) += timer-atlas7.o
|
||||
obj-$(CONFIG_MXS_TIMER) += mxs_timer.o
|
||||
obj-$(CONFIG_CLKSRC_PXA) += timer-pxa.o
|
||||
obj-$(CONFIG_PRIMA2_TIMER) += timer-prima2.o
|
||||
obj-$(CONFIG_U300_TIMER) += timer-u300.o
|
||||
obj-$(CONFIG_SUN4I_TIMER) += timer-sun4i.o
|
||||
obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o
|
||||
obj-$(CONFIG_MESON6_TIMER) += timer-meson6.o
|
||||
@ -43,7 +40,6 @@ obj-$(CONFIG_VT8500_TIMER) += timer-vt8500.o
|
||||
obj-$(CONFIG_NSPIRE_TIMER) += timer-zevio.o
|
||||
obj-$(CONFIG_BCM_KONA_TIMER) += bcm_kona_timer.o
|
||||
obj-$(CONFIG_CADENCE_TTC_TIMER) += timer-cadence-ttc.o
|
||||
obj-$(CONFIG_CLKSRC_EFM32) += timer-efm32.o
|
||||
obj-$(CONFIG_CLKSRC_STM32) += timer-stm32.o
|
||||
obj-$(CONFIG_CLKSRC_STM32_LP) += timer-stm32-lp.o
|
||||
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
|
||||
@ -73,7 +69,6 @@ obj-$(CONFIG_KEYSTONE_TIMER) += timer-keystone.o
|
||||
obj-$(CONFIG_INTEGRATOR_AP_TIMER) += timer-integrator-ap.o
|
||||
obj-$(CONFIG_CLKSRC_VERSATILE) += timer-versatile.o
|
||||
obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o
|
||||
obj-$(CONFIG_CLKSRC_TANGO_XTAL) += timer-tango-xtal.o
|
||||
obj-$(CONFIG_CLKSRC_IMX_GPT) += timer-imx-gpt.o
|
||||
obj-$(CONFIG_CLKSRC_IMX_TPM) += timer-imx-tpm.o
|
||||
obj-$(CONFIG_TIMER_IMX_SYS_CTR) += timer-imx-sysctr.o
|
||||
|
@ -1,281 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* System timer for CSR SiRFprimaII
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
#define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000
|
||||
#define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004
|
||||
#define SIRFSOC_TIMER_MATCH_0 0x0018
|
||||
#define SIRFSOC_TIMER_MATCH_1 0x001c
|
||||
#define SIRFSOC_TIMER_COUNTER_0 0x0048
|
||||
#define SIRFSOC_TIMER_COUNTER_1 0x004c
|
||||
#define SIRFSOC_TIMER_INTR_STATUS 0x0060
|
||||
#define SIRFSOC_TIMER_WATCHDOG_EN 0x0064
|
||||
#define SIRFSOC_TIMER_64COUNTER_CTRL 0x0068
|
||||
#define SIRFSOC_TIMER_64COUNTER_LO 0x006c
|
||||
#define SIRFSOC_TIMER_64COUNTER_HI 0x0070
|
||||
#define SIRFSOC_TIMER_64COUNTER_LOAD_LO 0x0074
|
||||
#define SIRFSOC_TIMER_64COUNTER_LOAD_HI 0x0078
|
||||
#define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO 0x007c
|
||||
#define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI 0x0080
|
||||
|
||||
#define SIRFSOC_TIMER_REG_CNT 6
|
||||
|
||||
static unsigned long atlas7_timer_rate;
|
||||
|
||||
static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
|
||||
SIRFSOC_TIMER_WATCHDOG_EN,
|
||||
SIRFSOC_TIMER_32COUNTER_0_CTRL,
|
||||
SIRFSOC_TIMER_32COUNTER_1_CTRL,
|
||||
SIRFSOC_TIMER_64COUNTER_CTRL,
|
||||
SIRFSOC_TIMER_64COUNTER_RLATCHED_LO,
|
||||
SIRFSOC_TIMER_64COUNTER_RLATCHED_HI,
|
||||
};
|
||||
|
||||
static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
|
||||
|
||||
static void __iomem *sirfsoc_timer_base;
|
||||
|
||||
/* disable count and interrupt */
|
||||
static inline void sirfsoc_timer_count_disable(int idx)
|
||||
{
|
||||
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7,
|
||||
sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
|
||||
}
|
||||
|
||||
/* enable count and interrupt */
|
||||
static inline void sirfsoc_timer_count_enable(int idx)
|
||||
{
|
||||
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x3,
|
||||
sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
|
||||
}
|
||||
|
||||
/* timer interrupt handler */
|
||||
static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *ce = dev_id;
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
/* clear timer interrupt */
|
||||
writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
|
||||
|
||||
if (clockevent_state_oneshot(ce))
|
||||
sirfsoc_timer_count_disable(cpu);
|
||||
|
||||
ce->event_handler(ce);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* read 64-bit timer counter */
|
||||
static u64 sirfsoc_timer_read(struct clocksource *cs)
|
||||
{
|
||||
u64 cycles;
|
||||
|
||||
writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
|
||||
BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
|
||||
|
||||
cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI);
|
||||
cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO);
|
||||
|
||||
return cycles;
|
||||
}
|
||||
|
||||
static int sirfsoc_timer_set_next_event(unsigned long delta,
|
||||
struct clock_event_device *ce)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
/* disable timer first, then modify the related registers */
|
||||
sirfsoc_timer_count_disable(cpu);
|
||||
|
||||
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 +
|
||||
4 * cpu);
|
||||
writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 +
|
||||
4 * cpu);
|
||||
|
||||
/* enable the tick */
|
||||
sirfsoc_timer_count_enable(cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Oneshot is enabled in set_next_event */
|
||||
static int sirfsoc_timer_shutdown(struct clock_event_device *evt)
|
||||
{
|
||||
sirfsoc_timer_count_disable(smp_processor_id());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sirfsoc_clocksource_suspend(struct clocksource *cs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
|
||||
sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
|
||||
}
|
||||
|
||||
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[SIRFSOC_TIMER_REG_CNT - 2],
|
||||
sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
|
||||
writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
|
||||
sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
|
||||
|
||||
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
|
||||
BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
|
||||
}
|
||||
|
||||
static struct clock_event_device __percpu *sirfsoc_clockevent;
|
||||
|
||||
static struct clocksource sirfsoc_clocksource = {
|
||||
.name = "sirfsoc_clocksource",
|
||||
.rating = 200,
|
||||
.mask = CLOCKSOURCE_MASK(64),
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
.read = sirfsoc_timer_read,
|
||||
.suspend = sirfsoc_clocksource_suspend,
|
||||
.resume = sirfsoc_clocksource_resume,
|
||||
};
|
||||
|
||||
static unsigned int sirfsoc_timer_irq, sirfsoc_timer1_irq;
|
||||
|
||||
static int sirfsoc_local_timer_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
struct clock_event_device *ce = per_cpu_ptr(sirfsoc_clockevent, cpu);
|
||||
unsigned int irq;
|
||||
const char *name;
|
||||
|
||||
if (cpu == 0) {
|
||||
irq = sirfsoc_timer_irq;
|
||||
name = "sirfsoc_timer0";
|
||||
} else {
|
||||
irq = sirfsoc_timer1_irq;
|
||||
name = "sirfsoc_timer1";
|
||||
}
|
||||
|
||||
ce->irq = irq;
|
||||
ce->name = "local_timer";
|
||||
ce->features = CLOCK_EVT_FEAT_ONESHOT;
|
||||
ce->rating = 200;
|
||||
ce->set_state_shutdown = sirfsoc_timer_shutdown;
|
||||
ce->set_state_oneshot = sirfsoc_timer_shutdown;
|
||||
ce->tick_resume = sirfsoc_timer_shutdown;
|
||||
ce->set_next_event = sirfsoc_timer_set_next_event;
|
||||
clockevents_calc_mult_shift(ce, atlas7_timer_rate, 60);
|
||||
ce->max_delta_ns = clockevent_delta2ns(-2, ce);
|
||||
ce->max_delta_ticks = (unsigned long)-2;
|
||||
ce->min_delta_ns = clockevent_delta2ns(2, ce);
|
||||
ce->min_delta_ticks = 2;
|
||||
ce->cpumask = cpumask_of(cpu);
|
||||
|
||||
BUG_ON(request_irq(ce->irq, sirfsoc_timer_interrupt,
|
||||
IRQF_TIMER | IRQF_NOBALANCING, name, ce));
|
||||
irq_force_affinity(ce->irq, cpumask_of(cpu));
|
||||
|
||||
clockevents_register_device(ce);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sirfsoc_local_timer_dying_cpu(unsigned int cpu)
|
||||
{
|
||||
struct clock_event_device *ce = per_cpu_ptr(sirfsoc_clockevent, cpu);
|
||||
|
||||
sirfsoc_timer_count_disable(1);
|
||||
|
||||
if (cpu == 0)
|
||||
free_irq(sirfsoc_timer_irq, ce);
|
||||
else
|
||||
free_irq(sirfsoc_timer1_irq, ce);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init sirfsoc_clockevent_init(void)
|
||||
{
|
||||
sirfsoc_clockevent = alloc_percpu(struct clock_event_device);
|
||||
BUG_ON(!sirfsoc_clockevent);
|
||||
|
||||
/* Install and invoke hotplug callbacks */
|
||||
return cpuhp_setup_state(CPUHP_AP_MARCO_TIMER_STARTING,
|
||||
"clockevents/marco:starting",
|
||||
sirfsoc_local_timer_starting_cpu,
|
||||
sirfsoc_local_timer_dying_cpu);
|
||||
}
|
||||
|
||||
/* initialize the kernel jiffy timer source */
|
||||
static int __init sirfsoc_atlas7_timer_init(struct device_node *np)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
||||
clk = of_clk_get(np, 0);
|
||||
BUG_ON(IS_ERR(clk));
|
||||
|
||||
BUG_ON(clk_prepare_enable(clk));
|
||||
|
||||
atlas7_timer_rate = clk_get_rate(clk);
|
||||
|
||||
/* timer dividers: 0, not divided */
|
||||
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
|
||||
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
|
||||
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
|
||||
|
||||
/* Initialize timer counters to 0 */
|
||||
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
|
||||
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
|
||||
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
|
||||
BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
|
||||
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0);
|
||||
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1);
|
||||
|
||||
/* Clear all interrupts */
|
||||
writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
|
||||
|
||||
BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, atlas7_timer_rate));
|
||||
|
||||
return sirfsoc_clockevent_init();
|
||||
}
|
||||
|
||||
static int __init sirfsoc_of_timer_init(struct device_node *np)
|
||||
{
|
||||
sirfsoc_timer_base = of_iomap(np, 0);
|
||||
if (!sirfsoc_timer_base) {
|
||||
pr_err("unable to map timer cpu registers\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
sirfsoc_timer_irq = irq_of_parse_and_map(np, 0);
|
||||
if (!sirfsoc_timer_irq) {
|
||||
pr_err("No irq passed for timer0 via DT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sirfsoc_timer1_irq = irq_of_parse_and_map(np, 1);
|
||||
if (!sirfsoc_timer1_irq) {
|
||||
pr_err("No irq passed for timer1 via DT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return sirfsoc_atlas7_timer_init(np);
|
||||
}
|
||||
TIMER_OF_DECLARE(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
|
@ -7,6 +7,8 @@
|
||||
* (with tiny parts adopted from code by Kevin Hilman <khilman@baylibre.com>)
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -17,9 +19,6 @@
|
||||
|
||||
#include <clocksource/timer-davinci.h>
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
#define DAVINCI_TIMER_REG_TIM12 0x10
|
||||
#define DAVINCI_TIMER_REG_TIM34 0x14
|
||||
#define DAVINCI_TIMER_REG_PRD12 0x18
|
||||
|
@ -1,278 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2013 Pengutronix
|
||||
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#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>
|
||||
|
||||
#define TIMERn_CTRL 0x00
|
||||
#define TIMERn_CTRL_PRESC(val) (((val) & 0xf) << 24)
|
||||
#define TIMERn_CTRL_PRESC_1024 TIMERn_CTRL_PRESC(10)
|
||||
#define TIMERn_CTRL_CLKSEL(val) (((val) & 0x3) << 16)
|
||||
#define TIMERn_CTRL_CLKSEL_PRESCHFPERCLK TIMERn_CTRL_CLKSEL(0)
|
||||
#define TIMERn_CTRL_OSMEN 0x00000010
|
||||
#define TIMERn_CTRL_MODE(val) (((val) & 0x3) << 0)
|
||||
#define TIMERn_CTRL_MODE_UP TIMERn_CTRL_MODE(0)
|
||||
#define TIMERn_CTRL_MODE_DOWN TIMERn_CTRL_MODE(1)
|
||||
|
||||
#define TIMERn_CMD 0x04
|
||||
#define TIMERn_CMD_START 0x00000001
|
||||
#define TIMERn_CMD_STOP 0x00000002
|
||||
|
||||
#define TIMERn_IEN 0x0c
|
||||
#define TIMERn_IF 0x10
|
||||
#define TIMERn_IFS 0x14
|
||||
#define TIMERn_IFC 0x18
|
||||
#define TIMERn_IRQ_UF 0x00000002
|
||||
|
||||
#define TIMERn_TOP 0x1c
|
||||
#define TIMERn_CNT 0x24
|
||||
|
||||
struct efm32_clock_event_ddata {
|
||||
struct clock_event_device evtdev;
|
||||
void __iomem *base;
|
||||
unsigned periodic_top;
|
||||
};
|
||||
|
||||
static int efm32_clock_event_shutdown(struct clock_event_device *evtdev)
|
||||
{
|
||||
struct efm32_clock_event_ddata *ddata =
|
||||
container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
|
||||
|
||||
writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efm32_clock_event_set_oneshot(struct clock_event_device *evtdev)
|
||||
{
|
||||
struct efm32_clock_event_ddata *ddata =
|
||||
container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
|
||||
|
||||
writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
|
||||
writel_relaxed(TIMERn_CTRL_PRESC_1024 |
|
||||
TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
|
||||
TIMERn_CTRL_OSMEN |
|
||||
TIMERn_CTRL_MODE_DOWN,
|
||||
ddata->base + TIMERn_CTRL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efm32_clock_event_set_periodic(struct clock_event_device *evtdev)
|
||||
{
|
||||
struct efm32_clock_event_ddata *ddata =
|
||||
container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
|
||||
|
||||
writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
|
||||
writel_relaxed(ddata->periodic_top, ddata->base + TIMERn_TOP);
|
||||
writel_relaxed(TIMERn_CTRL_PRESC_1024 |
|
||||
TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
|
||||
TIMERn_CTRL_MODE_DOWN,
|
||||
ddata->base + TIMERn_CTRL);
|
||||
writel_relaxed(TIMERn_CMD_START, ddata->base + TIMERn_CMD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efm32_clock_event_set_next_event(unsigned long evt,
|
||||
struct clock_event_device *evtdev)
|
||||
{
|
||||
struct efm32_clock_event_ddata *ddata =
|
||||
container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
|
||||
|
||||
writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
|
||||
writel_relaxed(evt, ddata->base + TIMERn_CNT);
|
||||
writel_relaxed(TIMERn_CMD_START, ddata->base + TIMERn_CMD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t efm32_clock_event_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct efm32_clock_event_ddata *ddata = dev_id;
|
||||
|
||||
writel_relaxed(TIMERn_IRQ_UF, ddata->base + TIMERn_IFC);
|
||||
|
||||
ddata->evtdev.event_handler(&ddata->evtdev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct efm32_clock_event_ddata clock_event_ddata = {
|
||||
.evtdev = {
|
||||
.name = "efm32 clockevent",
|
||||
.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
|
||||
.set_state_shutdown = efm32_clock_event_shutdown,
|
||||
.set_state_periodic = efm32_clock_event_set_periodic,
|
||||
.set_state_oneshot = efm32_clock_event_set_oneshot,
|
||||
.set_next_event = efm32_clock_event_set_next_event,
|
||||
.rating = 200,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init efm32_clocksource_init(struct device_node *np)
|
||||
{
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
unsigned long rate;
|
||||
int ret;
|
||||
|
||||
clk = of_clk_get(np, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
pr_err("failed to get clock for clocksource (%d)\n", ret);
|
||||
goto err_clk_get;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
pr_err("failed to enable timer clock for clocksource (%d)\n",
|
||||
ret);
|
||||
goto err_clk_enable;
|
||||
}
|
||||
rate = clk_get_rate(clk);
|
||||
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
ret = -EADDRNOTAVAIL;
|
||||
pr_err("failed to map registers for clocksource\n");
|
||||
goto err_iomap;
|
||||
}
|
||||
|
||||
writel_relaxed(TIMERn_CTRL_PRESC_1024 |
|
||||
TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
|
||||
TIMERn_CTRL_MODE_UP, base + TIMERn_CTRL);
|
||||
writel_relaxed(TIMERn_CMD_START, base + TIMERn_CMD);
|
||||
|
||||
ret = clocksource_mmio_init(base + TIMERn_CNT, "efm32 timer",
|
||||
DIV_ROUND_CLOSEST(rate, 1024), 200, 16,
|
||||
clocksource_mmio_readl_up);
|
||||
if (ret) {
|
||||
pr_err("failed to init clocksource (%d)\n", ret);
|
||||
goto err_clocksource_init;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clocksource_init:
|
||||
|
||||
iounmap(base);
|
||||
err_iomap:
|
||||
|
||||
clk_disable_unprepare(clk);
|
||||
err_clk_enable:
|
||||
|
||||
clk_put(clk);
|
||||
err_clk_get:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init efm32_clockevent_init(struct device_node *np)
|
||||
{
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
unsigned long rate;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
ret = -EADDRNOTAVAIL;
|
||||
pr_err("failed to map registers for clockevent\n");
|
||||
goto err_iomap;
|
||||
}
|
||||
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
if (!irq) {
|
||||
ret = -ENOENT;
|
||||
pr_err("failed to get irq for clockevent\n");
|
||||
goto err_get_irq;
|
||||
}
|
||||
|
||||
writel_relaxed(TIMERn_IRQ_UF, base + TIMERn_IEN);
|
||||
|
||||
clock_event_ddata.base = base;
|
||||
clock_event_ddata.periodic_top = DIV_ROUND_CLOSEST(rate, 1024 * HZ);
|
||||
|
||||
clockevents_config_and_register(&clock_event_ddata.evtdev,
|
||||
DIV_ROUND_CLOSEST(rate, 1024),
|
||||
0xf, 0xffff);
|
||||
|
||||
ret = request_irq(irq, efm32_clock_event_handler, IRQF_TIMER,
|
||||
"efm32 clockevent", &clock_event_ddata);
|
||||
if (ret) {
|
||||
pr_err("Failed setup irq\n");
|
||||
goto err_setup_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_setup_irq:
|
||||
err_get_irq:
|
||||
|
||||
iounmap(base);
|
||||
err_iomap:
|
||||
|
||||
clk_disable_unprepare(clk);
|
||||
err_clk_enable:
|
||||
|
||||
clk_put(clk);
|
||||
err_clk_get:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function asserts that we have exactly one clocksource and one
|
||||
* clock_event_device in the end.
|
||||
*/
|
||||
static int __init efm32_timer_init(struct device_node *np)
|
||||
{
|
||||
static int has_clocksource, has_clockevent;
|
||||
int ret = 0;
|
||||
|
||||
if (!has_clocksource) {
|
||||
ret = efm32_clocksource_init(np);
|
||||
if (!ret) {
|
||||
has_clocksource = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_clockevent) {
|
||||
ret = efm32_clockevent_init(np);
|
||||
if (!ret) {
|
||||
has_clockevent = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
TIMER_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init);
|
||||
TIMER_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init);
|
@ -71,10 +71,24 @@ struct mchp_pit64b_clkevt {
|
||||
struct clock_event_device clkevt;
|
||||
};
|
||||
|
||||
#define to_mchp_pit64b_timer(x) \
|
||||
#define clkevt_to_mchp_pit64b_timer(x) \
|
||||
((struct mchp_pit64b_timer *)container_of(x,\
|
||||
struct mchp_pit64b_clkevt, clkevt))
|
||||
|
||||
/**
|
||||
* mchp_pit64b_clksrc - PIT64B clocksource data structure
|
||||
* @timer: PIT64B timer
|
||||
* @clksrc: clocksource
|
||||
*/
|
||||
struct mchp_pit64b_clksrc {
|
||||
struct mchp_pit64b_timer timer;
|
||||
struct clocksource clksrc;
|
||||
};
|
||||
|
||||
#define clksrc_to_mchp_pit64b_timer(x) \
|
||||
((struct mchp_pit64b_timer *)container_of(x,\
|
||||
struct mchp_pit64b_clksrc, clksrc))
|
||||
|
||||
/* Base address for clocksource timer. */
|
||||
static void __iomem *mchp_pit64b_cs_base;
|
||||
/* Default cycles for clockevent timer. */
|
||||
@ -116,6 +130,36 @@ static inline void mchp_pit64b_reset(struct mchp_pit64b_timer *timer,
|
||||
writel_relaxed(MCHP_PIT64B_CR_START, timer->base + MCHP_PIT64B_CR);
|
||||
}
|
||||
|
||||
static void mchp_pit64b_suspend(struct mchp_pit64b_timer *timer)
|
||||
{
|
||||
writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
|
||||
if (timer->mode & MCHP_PIT64B_MR_SGCLK)
|
||||
clk_disable_unprepare(timer->gclk);
|
||||
clk_disable_unprepare(timer->pclk);
|
||||
}
|
||||
|
||||
static void mchp_pit64b_resume(struct mchp_pit64b_timer *timer)
|
||||
{
|
||||
clk_prepare_enable(timer->pclk);
|
||||
if (timer->mode & MCHP_PIT64B_MR_SGCLK)
|
||||
clk_prepare_enable(timer->gclk);
|
||||
}
|
||||
|
||||
static void mchp_pit64b_clksrc_suspend(struct clocksource *cs)
|
||||
{
|
||||
struct mchp_pit64b_timer *timer = clksrc_to_mchp_pit64b_timer(cs);
|
||||
|
||||
mchp_pit64b_suspend(timer);
|
||||
}
|
||||
|
||||
static void mchp_pit64b_clksrc_resume(struct clocksource *cs)
|
||||
{
|
||||
struct mchp_pit64b_timer *timer = clksrc_to_mchp_pit64b_timer(cs);
|
||||
|
||||
mchp_pit64b_resume(timer);
|
||||
mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
|
||||
}
|
||||
|
||||
static u64 mchp_pit64b_clksrc_read(struct clocksource *cs)
|
||||
{
|
||||
return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
|
||||
@ -128,7 +172,7 @@ static u64 mchp_pit64b_sched_read_clk(void)
|
||||
|
||||
static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev)
|
||||
{
|
||||
struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
|
||||
struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
|
||||
|
||||
writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
|
||||
|
||||
@ -137,7 +181,7 @@ static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev)
|
||||
|
||||
static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev)
|
||||
{
|
||||
struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
|
||||
struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
|
||||
|
||||
mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_CONT,
|
||||
MCHP_PIT64B_IER_PERIOD);
|
||||
@ -148,7 +192,7 @@ static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev)
|
||||
static int mchp_pit64b_clkevt_set_next_event(unsigned long evt,
|
||||
struct clock_event_device *cedev)
|
||||
{
|
||||
struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
|
||||
struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
|
||||
|
||||
mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT,
|
||||
MCHP_PIT64B_IER_PERIOD);
|
||||
@ -158,21 +202,16 @@ static int mchp_pit64b_clkevt_set_next_event(unsigned long evt,
|
||||
|
||||
static void mchp_pit64b_clkevt_suspend(struct clock_event_device *cedev)
|
||||
{
|
||||
struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
|
||||
struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
|
||||
|
||||
writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
|
||||
if (timer->mode & MCHP_PIT64B_MR_SGCLK)
|
||||
clk_disable_unprepare(timer->gclk);
|
||||
clk_disable_unprepare(timer->pclk);
|
||||
mchp_pit64b_suspend(timer);
|
||||
}
|
||||
|
||||
static void mchp_pit64b_clkevt_resume(struct clock_event_device *cedev)
|
||||
{
|
||||
struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
|
||||
struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
|
||||
|
||||
clk_prepare_enable(timer->pclk);
|
||||
if (timer->mode & MCHP_PIT64B_MR_SGCLK)
|
||||
clk_prepare_enable(timer->gclk);
|
||||
mchp_pit64b_resume(timer);
|
||||
}
|
||||
|
||||
static irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id)
|
||||
@ -296,20 +335,37 @@ done:
|
||||
static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer,
|
||||
u32 clk_rate)
|
||||
{
|
||||
struct mchp_pit64b_clksrc *cs;
|
||||
int ret;
|
||||
|
||||
cs = kzalloc(sizeof(*cs), GFP_KERNEL);
|
||||
if (!cs)
|
||||
return -ENOMEM;
|
||||
|
||||
mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
|
||||
|
||||
mchp_pit64b_cs_base = timer->base;
|
||||
|
||||
ret = clocksource_mmio_init(timer->base, MCHP_PIT64B_NAME, clk_rate,
|
||||
210, 64, mchp_pit64b_clksrc_read);
|
||||
cs->timer.base = timer->base;
|
||||
cs->timer.pclk = timer->pclk;
|
||||
cs->timer.gclk = timer->gclk;
|
||||
cs->timer.mode = timer->mode;
|
||||
cs->clksrc.name = MCHP_PIT64B_NAME;
|
||||
cs->clksrc.mask = CLOCKSOURCE_MASK(64);
|
||||
cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
|
||||
cs->clksrc.rating = 210;
|
||||
cs->clksrc.read = mchp_pit64b_clksrc_read;
|
||||
cs->clksrc.suspend = mchp_pit64b_clksrc_suspend;
|
||||
cs->clksrc.resume = mchp_pit64b_clksrc_resume;
|
||||
|
||||
ret = clocksource_register_hz(&cs->clksrc, clk_rate);
|
||||
if (ret) {
|
||||
pr_debug("clksrc: Failed to register PIT64B clocksource!\n");
|
||||
|
||||
/* Stop timer. */
|
||||
writel_relaxed(MCHP_PIT64B_CR_SWRST,
|
||||
timer->base + MCHP_PIT64B_CR);
|
||||
kfree(cs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1,242 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* System timer for CSR SiRFprimaII
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
#define PRIMA2_CLOCK_FREQ 1000000
|
||||
|
||||
#define SIRFSOC_TIMER_COUNTER_LO 0x0000
|
||||
#define SIRFSOC_TIMER_COUNTER_HI 0x0004
|
||||
#define SIRFSOC_TIMER_MATCH_0 0x0008
|
||||
#define SIRFSOC_TIMER_MATCH_1 0x000C
|
||||
#define SIRFSOC_TIMER_MATCH_2 0x0010
|
||||
#define SIRFSOC_TIMER_MATCH_3 0x0014
|
||||
#define SIRFSOC_TIMER_MATCH_4 0x0018
|
||||
#define SIRFSOC_TIMER_MATCH_5 0x001C
|
||||
#define SIRFSOC_TIMER_STATUS 0x0020
|
||||
#define SIRFSOC_TIMER_INT_EN 0x0024
|
||||
#define SIRFSOC_TIMER_WATCHDOG_EN 0x0028
|
||||
#define SIRFSOC_TIMER_DIV 0x002C
|
||||
#define SIRFSOC_TIMER_LATCH 0x0030
|
||||
#define SIRFSOC_TIMER_LATCHED_LO 0x0034
|
||||
#define SIRFSOC_TIMER_LATCHED_HI 0x0038
|
||||
|
||||
#define SIRFSOC_TIMER_WDT_INDEX 5
|
||||
|
||||
#define SIRFSOC_TIMER_LATCH_BIT BIT(0)
|
||||
|
||||
#define SIRFSOC_TIMER_REG_CNT 11
|
||||
|
||||
static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
|
||||
SIRFSOC_TIMER_MATCH_0, SIRFSOC_TIMER_MATCH_1, SIRFSOC_TIMER_MATCH_2,
|
||||
SIRFSOC_TIMER_MATCH_3, SIRFSOC_TIMER_MATCH_4, SIRFSOC_TIMER_MATCH_5,
|
||||
SIRFSOC_TIMER_INT_EN, SIRFSOC_TIMER_WATCHDOG_EN, SIRFSOC_TIMER_DIV,
|
||||
SIRFSOC_TIMER_LATCHED_LO, SIRFSOC_TIMER_LATCHED_HI,
|
||||
};
|
||||
|
||||
static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
|
||||
|
||||
static void __iomem *sirfsoc_timer_base;
|
||||
|
||||
/* timer0 interrupt handler */
|
||||
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)));
|
||||
|
||||
/* clear timer0 interrupt */
|
||||
writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
|
||||
|
||||
ce->event_handler(ce);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* read 64-bit timer counter */
|
||||
static u64 notrace 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);
|
||||
cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI);
|
||||
cycles = (cycles << 32) |
|
||||
readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
|
||||
|
||||
return cycles;
|
||||
}
|
||||
|
||||
static int sirfsoc_timer_set_next_event(unsigned long delta,
|
||||
struct clock_event_device *ce)
|
||||
{
|
||||
unsigned long now, next;
|
||||
|
||||
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);
|
||||
now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
|
||||
|
||||
return next - now > delta ? -ETIME : 0;
|
||||
}
|
||||
|
||||
static int sirfsoc_timer_shutdown(struct clock_event_device *evt)
|
||||
{
|
||||
u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
|
||||
|
||||
writel_relaxed(val & ~BIT(0),
|
||||
sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sirfsoc_timer_set_oneshot(struct clock_event_device *evt)
|
||||
{
|
||||
u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
|
||||
|
||||
writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sirfsoc_clocksource_suspend(struct clocksource *cs)
|
||||
{
|
||||
int i;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
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[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 = {
|
||||
.name = "sirfsoc_clockevent",
|
||||
.rating = 200,
|
||||
.features = CLOCK_EVT_FEAT_ONESHOT,
|
||||
.set_state_shutdown = sirfsoc_timer_shutdown,
|
||||
.set_state_oneshot = sirfsoc_timer_set_oneshot,
|
||||
.set_next_event = sirfsoc_timer_set_next_event,
|
||||
};
|
||||
|
||||
static struct clocksource sirfsoc_clocksource = {
|
||||
.name = "sirfsoc_clocksource",
|
||||
.rating = 200,
|
||||
.mask = CLOCKSOURCE_MASK(64),
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
.read = sirfsoc_timer_read,
|
||||
.suspend = sirfsoc_clocksource_suspend,
|
||||
.resume = sirfsoc_clocksource_resume,
|
||||
};
|
||||
|
||||
/* Overwrite weak default sched_clock with more precise one */
|
||||
static u64 notrace sirfsoc_read_sched_clock(void)
|
||||
{
|
||||
return sirfsoc_timer_read(NULL);
|
||||
}
|
||||
|
||||
static void __init sirfsoc_clockevent_init(void)
|
||||
{
|
||||
sirfsoc_clockevent.cpumask = cpumask_of(0);
|
||||
clockevents_config_and_register(&sirfsoc_clockevent, PRIMA2_CLOCK_FREQ,
|
||||
2, -2);
|
||||
}
|
||||
|
||||
/* initialize the kernel jiffy timer source */
|
||||
static int __init sirfsoc_prima2_timer_init(struct device_node *np)
|
||||
{
|
||||
unsigned long rate;
|
||||
unsigned int irq;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = of_clk_get(np, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("Failed to get clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
pr_err("Failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
|
||||
if (rate < PRIMA2_CLOCK_FREQ || rate % PRIMA2_CLOCK_FREQ) {
|
||||
pr_err("Invalid clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sirfsoc_timer_base = of_iomap(np, 0);
|
||||
if (!sirfsoc_timer_base) {
|
||||
pr_err("unable to map timer cpu registers\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
|
||||
writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1,
|
||||
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);
|
||||
|
||||
ret = clocksource_register_hz(&sirfsoc_clocksource, PRIMA2_CLOCK_FREQ);
|
||||
if (ret) {
|
||||
pr_err("Failed to register clocksource\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sched_clock_register(sirfsoc_read_sched_clock, 64, PRIMA2_CLOCK_FREQ);
|
||||
|
||||
ret = request_irq(irq, sirfsoc_timer_interrupt, IRQF_TIMER,
|
||||
"sirfsoc_timer0", &sirfsoc_clockevent);
|
||||
if (ret) {
|
||||
pr_err("Failed to setup irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sirfsoc_clockevent_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
TIMER_OF_DECLARE(sirfsoc_prima2_timer,
|
||||
"sirf,prima2-tick", sirfsoc_prima2_timer_init);
|
@ -1,57 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/sched_clock.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
static void __iomem *xtal_in_cnt;
|
||||
static struct delay_timer delay_timer;
|
||||
|
||||
static unsigned long notrace read_xtal_counter(void)
|
||||
{
|
||||
return readl_relaxed(xtal_in_cnt);
|
||||
}
|
||||
|
||||
static u64 notrace read_sched_clock(void)
|
||||
{
|
||||
return read_xtal_counter();
|
||||
}
|
||||
|
||||
static int __init tango_clocksource_init(struct device_node *np)
|
||||
{
|
||||
struct clk *clk;
|
||||
int xtal_freq, ret;
|
||||
|
||||
xtal_in_cnt = of_iomap(np, 0);
|
||||
if (xtal_in_cnt == NULL) {
|
||||
pr_err("%pOF: invalid address\n", np);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
clk = of_clk_get(np, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%pOF: invalid clock\n", np);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
xtal_freq = clk_get_rate(clk);
|
||||
delay_timer.freq = xtal_freq;
|
||||
delay_timer.read_current_timer = read_xtal_counter;
|
||||
|
||||
ret = clocksource_mmio_init(xtal_in_cnt, "tango-xtal", xtal_freq, 350,
|
||||
32, clocksource_mmio_readl_up);
|
||||
if (ret) {
|
||||
pr_err("%pOF: registration failed\n", np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sched_clock_register(read_sched_clock, 32, xtal_freq);
|
||||
register_current_timer_delay(&delay_timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TIMER_OF_DECLARE(tango, "sigma,tick-counter", tango_clocksource_init);
|
@ -1,457 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2007-2009 ST-Ericsson AB
|
||||
* Timer COH 901 328, runs the OS timer interrupt.
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
*/
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
/* Generic stuff */
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/time.h>
|
||||
|
||||
/*
|
||||
* APP side special timer registers
|
||||
* This timer contains four timers which can fire an interrupt each.
|
||||
* OS (operating system) timer @ 32768 Hz
|
||||
* DD (device driver) timer @ 1 kHz
|
||||
* GP1 (general purpose 1) timer @ 1MHz
|
||||
* GP2 (general purpose 2) timer @ 1MHz
|
||||
*/
|
||||
|
||||
/* Reset OS Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_ROST (0x0000)
|
||||
#define U300_TIMER_APP_ROST_TIMER_RESET (0x00000000)
|
||||
/* Enable OS Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_EOST (0x0004)
|
||||
#define U300_TIMER_APP_EOST_TIMER_ENABLE (0x00000000)
|
||||
/* Disable OS Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_DOST (0x0008)
|
||||
#define U300_TIMER_APP_DOST_TIMER_DISABLE (0x00000000)
|
||||
/* OS Timer Mode Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_SOSTM (0x000c)
|
||||
#define U300_TIMER_APP_SOSTM_MODE_CONTINUOUS (0x00000000)
|
||||
#define U300_TIMER_APP_SOSTM_MODE_ONE_SHOT (0x00000001)
|
||||
/* OS Timer Status Register 32bit (R/-) */
|
||||
#define U300_TIMER_APP_OSTS (0x0010)
|
||||
#define U300_TIMER_APP_OSTS_TIMER_STATE_MASK (0x0000000F)
|
||||
#define U300_TIMER_APP_OSTS_TIMER_STATE_IDLE (0x00000001)
|
||||
#define U300_TIMER_APP_OSTS_TIMER_STATE_ACTIVE (0x00000002)
|
||||
#define U300_TIMER_APP_OSTS_ENABLE_IND (0x00000010)
|
||||
#define U300_TIMER_APP_OSTS_MODE_MASK (0x00000020)
|
||||
#define U300_TIMER_APP_OSTS_MODE_CONTINUOUS (0x00000000)
|
||||
#define U300_TIMER_APP_OSTS_MODE_ONE_SHOT (0x00000020)
|
||||
#define U300_TIMER_APP_OSTS_IRQ_ENABLED_IND (0x00000040)
|
||||
#define U300_TIMER_APP_OSTS_IRQ_PENDING_IND (0x00000080)
|
||||
/* OS Timer Current Count Register 32bit (R/-) */
|
||||
#define U300_TIMER_APP_OSTCC (0x0014)
|
||||
/* OS Timer Terminal Count Register 32bit (R/W) */
|
||||
#define U300_TIMER_APP_OSTTC (0x0018)
|
||||
/* OS Timer Interrupt Enable Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_OSTIE (0x001c)
|
||||
#define U300_TIMER_APP_OSTIE_IRQ_DISABLE (0x00000000)
|
||||
#define U300_TIMER_APP_OSTIE_IRQ_ENABLE (0x00000001)
|
||||
/* OS Timer Interrupt Acknowledge Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_OSTIA (0x0020)
|
||||
#define U300_TIMER_APP_OSTIA_IRQ_ACK (0x00000080)
|
||||
|
||||
/* Reset DD Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_RDDT (0x0040)
|
||||
#define U300_TIMER_APP_RDDT_TIMER_RESET (0x00000000)
|
||||
/* Enable DD Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_EDDT (0x0044)
|
||||
#define U300_TIMER_APP_EDDT_TIMER_ENABLE (0x00000000)
|
||||
/* Disable DD Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_DDDT (0x0048)
|
||||
#define U300_TIMER_APP_DDDT_TIMER_DISABLE (0x00000000)
|
||||
/* DD Timer Mode Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_SDDTM (0x004c)
|
||||
#define U300_TIMER_APP_SDDTM_MODE_CONTINUOUS (0x00000000)
|
||||
#define U300_TIMER_APP_SDDTM_MODE_ONE_SHOT (0x00000001)
|
||||
/* DD Timer Status Register 32bit (R/-) */
|
||||
#define U300_TIMER_APP_DDTS (0x0050)
|
||||
#define U300_TIMER_APP_DDTS_TIMER_STATE_MASK (0x0000000F)
|
||||
#define U300_TIMER_APP_DDTS_TIMER_STATE_IDLE (0x00000001)
|
||||
#define U300_TIMER_APP_DDTS_TIMER_STATE_ACTIVE (0x00000002)
|
||||
#define U300_TIMER_APP_DDTS_ENABLE_IND (0x00000010)
|
||||
#define U300_TIMER_APP_DDTS_MODE_MASK (0x00000020)
|
||||
#define U300_TIMER_APP_DDTS_MODE_CONTINUOUS (0x00000000)
|
||||
#define U300_TIMER_APP_DDTS_MODE_ONE_SHOT (0x00000020)
|
||||
#define U300_TIMER_APP_DDTS_IRQ_ENABLED_IND (0x00000040)
|
||||
#define U300_TIMER_APP_DDTS_IRQ_PENDING_IND (0x00000080)
|
||||
/* DD Timer Current Count Register 32bit (R/-) */
|
||||
#define U300_TIMER_APP_DDTCC (0x0054)
|
||||
/* DD Timer Terminal Count Register 32bit (R/W) */
|
||||
#define U300_TIMER_APP_DDTTC (0x0058)
|
||||
/* DD Timer Interrupt Enable Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_DDTIE (0x005c)
|
||||
#define U300_TIMER_APP_DDTIE_IRQ_DISABLE (0x00000000)
|
||||
#define U300_TIMER_APP_DDTIE_IRQ_ENABLE (0x00000001)
|
||||
/* DD Timer Interrupt Acknowledge Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_DDTIA (0x0060)
|
||||
#define U300_TIMER_APP_DDTIA_IRQ_ACK (0x00000080)
|
||||
|
||||
/* Reset GP1 Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_RGPT1 (0x0080)
|
||||
#define U300_TIMER_APP_RGPT1_TIMER_RESET (0x00000000)
|
||||
/* Enable GP1 Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_EGPT1 (0x0084)
|
||||
#define U300_TIMER_APP_EGPT1_TIMER_ENABLE (0x00000000)
|
||||
/* Disable GP1 Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_DGPT1 (0x0088)
|
||||
#define U300_TIMER_APP_DGPT1_TIMER_DISABLE (0x00000000)
|
||||
/* GP1 Timer Mode Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_SGPT1M (0x008c)
|
||||
#define U300_TIMER_APP_SGPT1M_MODE_CONTINUOUS (0x00000000)
|
||||
#define U300_TIMER_APP_SGPT1M_MODE_ONE_SHOT (0x00000001)
|
||||
/* GP1 Timer Status Register 32bit (R/-) */
|
||||
#define U300_TIMER_APP_GPT1S (0x0090)
|
||||
#define U300_TIMER_APP_GPT1S_TIMER_STATE_MASK (0x0000000F)
|
||||
#define U300_TIMER_APP_GPT1S_TIMER_STATE_IDLE (0x00000001)
|
||||
#define U300_TIMER_APP_GPT1S_TIMER_STATE_ACTIVE (0x00000002)
|
||||
#define U300_TIMER_APP_GPT1S_ENABLE_IND (0x00000010)
|
||||
#define U300_TIMER_APP_GPT1S_MODE_MASK (0x00000020)
|
||||
#define U300_TIMER_APP_GPT1S_MODE_CONTINUOUS (0x00000000)
|
||||
#define U300_TIMER_APP_GPT1S_MODE_ONE_SHOT (0x00000020)
|
||||
#define U300_TIMER_APP_GPT1S_IRQ_ENABLED_IND (0x00000040)
|
||||
#define U300_TIMER_APP_GPT1S_IRQ_PENDING_IND (0x00000080)
|
||||
/* GP1 Timer Current Count Register 32bit (R/-) */
|
||||
#define U300_TIMER_APP_GPT1CC (0x0094)
|
||||
/* GP1 Timer Terminal Count Register 32bit (R/W) */
|
||||
#define U300_TIMER_APP_GPT1TC (0x0098)
|
||||
/* GP1 Timer Interrupt Enable Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_GPT1IE (0x009c)
|
||||
#define U300_TIMER_APP_GPT1IE_IRQ_DISABLE (0x00000000)
|
||||
#define U300_TIMER_APP_GPT1IE_IRQ_ENABLE (0x00000001)
|
||||
/* GP1 Timer Interrupt Acknowledge Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_GPT1IA (0x00a0)
|
||||
#define U300_TIMER_APP_GPT1IA_IRQ_ACK (0x00000080)
|
||||
|
||||
/* Reset GP2 Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_RGPT2 (0x00c0)
|
||||
#define U300_TIMER_APP_RGPT2_TIMER_RESET (0x00000000)
|
||||
/* Enable GP2 Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_EGPT2 (0x00c4)
|
||||
#define U300_TIMER_APP_EGPT2_TIMER_ENABLE (0x00000000)
|
||||
/* Disable GP2 Timer 32bit (-/W) */
|
||||
#define U300_TIMER_APP_DGPT2 (0x00c8)
|
||||
#define U300_TIMER_APP_DGPT2_TIMER_DISABLE (0x00000000)
|
||||
/* GP2 Timer Mode Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_SGPT2M (0x00cc)
|
||||
#define U300_TIMER_APP_SGPT2M_MODE_CONTINUOUS (0x00000000)
|
||||
#define U300_TIMER_APP_SGPT2M_MODE_ONE_SHOT (0x00000001)
|
||||
/* GP2 Timer Status Register 32bit (R/-) */
|
||||
#define U300_TIMER_APP_GPT2S (0x00d0)
|
||||
#define U300_TIMER_APP_GPT2S_TIMER_STATE_MASK (0x0000000F)
|
||||
#define U300_TIMER_APP_GPT2S_TIMER_STATE_IDLE (0x00000001)
|
||||
#define U300_TIMER_APP_GPT2S_TIMER_STATE_ACTIVE (0x00000002)
|
||||
#define U300_TIMER_APP_GPT2S_ENABLE_IND (0x00000010)
|
||||
#define U300_TIMER_APP_GPT2S_MODE_MASK (0x00000020)
|
||||
#define U300_TIMER_APP_GPT2S_MODE_CONTINUOUS (0x00000000)
|
||||
#define U300_TIMER_APP_GPT2S_MODE_ONE_SHOT (0x00000020)
|
||||
#define U300_TIMER_APP_GPT2S_IRQ_ENABLED_IND (0x00000040)
|
||||
#define U300_TIMER_APP_GPT2S_IRQ_PENDING_IND (0x00000080)
|
||||
/* GP2 Timer Current Count Register 32bit (R/-) */
|
||||
#define U300_TIMER_APP_GPT2CC (0x00d4)
|
||||
/* GP2 Timer Terminal Count Register 32bit (R/W) */
|
||||
#define U300_TIMER_APP_GPT2TC (0x00d8)
|
||||
/* GP2 Timer Interrupt Enable Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_GPT2IE (0x00dc)
|
||||
#define U300_TIMER_APP_GPT2IE_IRQ_DISABLE (0x00000000)
|
||||
#define U300_TIMER_APP_GPT2IE_IRQ_ENABLE (0x00000001)
|
||||
/* GP2 Timer Interrupt Acknowledge Register 32bit (-/W) */
|
||||
#define U300_TIMER_APP_GPT2IA (0x00e0)
|
||||
#define U300_TIMER_APP_GPT2IA_IRQ_ACK (0x00000080)
|
||||
|
||||
/* Clock request control register - all four timers */
|
||||
#define U300_TIMER_APP_CRC (0x100)
|
||||
#define U300_TIMER_APP_CRC_CLOCK_REQUEST_ENABLE (0x00000001)
|
||||
|
||||
static void __iomem *u300_timer_base;
|
||||
|
||||
struct u300_clockevent_data {
|
||||
struct clock_event_device cevd;
|
||||
unsigned ticks_per_jiffy;
|
||||
};
|
||||
|
||||
static int u300_shutdown(struct clock_event_device *evt)
|
||||
{
|
||||
/* Disable interrupts on GP1 */
|
||||
writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_GPT1IE);
|
||||
/* Disable GP1 */
|
||||
writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_DGPT1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have oneshot timer active, the oneshot scheduling function
|
||||
* u300_set_next_event() is called immediately after.
|
||||
*/
|
||||
static int u300_set_oneshot(struct clock_event_device *evt)
|
||||
{
|
||||
/* Just return; here? */
|
||||
/*
|
||||
* The actual event will be programmed by the next event hook,
|
||||
* so we just set a dummy value somewhere at the end of the
|
||||
* universe here.
|
||||
*/
|
||||
/* Disable interrupts on GPT1 */
|
||||
writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_GPT1IE);
|
||||
/* Disable GP1 while we're reprogramming it. */
|
||||
writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_DGPT1);
|
||||
/*
|
||||
* Expire far in the future, u300_set_next_event() will be
|
||||
* called soon...
|
||||
*/
|
||||
writel(0xFFFFFFFF, u300_timer_base + U300_TIMER_APP_GPT1TC);
|
||||
/* We run one shot per tick here! */
|
||||
writel(U300_TIMER_APP_SGPT1M_MODE_ONE_SHOT,
|
||||
u300_timer_base + U300_TIMER_APP_SGPT1M);
|
||||
/* Enable interrupts for this timer */
|
||||
writel(U300_TIMER_APP_GPT1IE_IRQ_ENABLE,
|
||||
u300_timer_base + U300_TIMER_APP_GPT1IE);
|
||||
/* Enable timer */
|
||||
writel(U300_TIMER_APP_EGPT1_TIMER_ENABLE,
|
||||
u300_timer_base + U300_TIMER_APP_EGPT1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int u300_set_periodic(struct clock_event_device *evt)
|
||||
{
|
||||
struct u300_clockevent_data *cevdata =
|
||||
container_of(evt, struct u300_clockevent_data, cevd);
|
||||
|
||||
/* Disable interrupts on GPT1 */
|
||||
writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_GPT1IE);
|
||||
/* Disable GP1 while we're reprogramming it. */
|
||||
writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_DGPT1);
|
||||
/*
|
||||
* Set the periodic mode to a certain number of ticks per
|
||||
* jiffy.
|
||||
*/
|
||||
writel(cevdata->ticks_per_jiffy,
|
||||
u300_timer_base + U300_TIMER_APP_GPT1TC);
|
||||
/*
|
||||
* Set continuous mode, so the timer keeps triggering
|
||||
* interrupts.
|
||||
*/
|
||||
writel(U300_TIMER_APP_SGPT1M_MODE_CONTINUOUS,
|
||||
u300_timer_base + U300_TIMER_APP_SGPT1M);
|
||||
/* Enable timer interrupts */
|
||||
writel(U300_TIMER_APP_GPT1IE_IRQ_ENABLE,
|
||||
u300_timer_base + U300_TIMER_APP_GPT1IE);
|
||||
/* Then enable the OS timer again */
|
||||
writel(U300_TIMER_APP_EGPT1_TIMER_ENABLE,
|
||||
u300_timer_base + U300_TIMER_APP_EGPT1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The app timer in one shot mode obviously has to be reprogrammed
|
||||
* in EXACTLY this sequence to work properly. Do NOT try to e.g. replace
|
||||
* the interrupt disable + timer disable commands with a reset command,
|
||||
* it will fail miserably. Apparently (and I found this the hard way)
|
||||
* the timer is very sensitive to the instruction order, though you don't
|
||||
* get that impression from the data sheet.
|
||||
*/
|
||||
static int u300_set_next_event(unsigned long cycles,
|
||||
struct clock_event_device *evt)
|
||||
|
||||
{
|
||||
/* Disable interrupts on GPT1 */
|
||||
writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_GPT1IE);
|
||||
/* Disable GP1 while we're reprogramming it. */
|
||||
writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_DGPT1);
|
||||
/* Reset the General Purpose timer 1. */
|
||||
writel(U300_TIMER_APP_RGPT1_TIMER_RESET,
|
||||
u300_timer_base + U300_TIMER_APP_RGPT1);
|
||||
/* IRQ in n * cycles */
|
||||
writel(cycles, u300_timer_base + U300_TIMER_APP_GPT1TC);
|
||||
/*
|
||||
* We run one shot per tick here! (This is necessary to reconfigure,
|
||||
* the timer will tilt if you don't!)
|
||||
*/
|
||||
writel(U300_TIMER_APP_SGPT1M_MODE_ONE_SHOT,
|
||||
u300_timer_base + U300_TIMER_APP_SGPT1M);
|
||||
/* Enable timer interrupts */
|
||||
writel(U300_TIMER_APP_GPT1IE_IRQ_ENABLE,
|
||||
u300_timer_base + U300_TIMER_APP_GPT1IE);
|
||||
/* Then enable the OS timer again */
|
||||
writel(U300_TIMER_APP_EGPT1_TIMER_ENABLE,
|
||||
u300_timer_base + U300_TIMER_APP_EGPT1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct u300_clockevent_data u300_clockevent_data = {
|
||||
/* Use general purpose timer 1 as clock event */
|
||||
.cevd = {
|
||||
.name = "GPT1",
|
||||
/* Reasonably fast and accurate clock event */
|
||||
.rating = 300,
|
||||
.features = CLOCK_EVT_FEAT_PERIODIC |
|
||||
CLOCK_EVT_FEAT_ONESHOT,
|
||||
.set_next_event = u300_set_next_event,
|
||||
.set_state_shutdown = u300_shutdown,
|
||||
.set_state_periodic = u300_set_periodic,
|
||||
.set_state_oneshot = u300_set_oneshot,
|
||||
},
|
||||
};
|
||||
|
||||
/* Clock event timer interrupt handler */
|
||||
static irqreturn_t u300_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *evt = &u300_clockevent_data.cevd;
|
||||
/* ACK/Clear timer IRQ for the APP GPT1 Timer */
|
||||
|
||||
writel(U300_TIMER_APP_GPT1IA_IRQ_ACK,
|
||||
u300_timer_base + U300_TIMER_APP_GPT1IA);
|
||||
evt->event_handler(evt);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Override the global weak sched_clock symbol with this
|
||||
* local implementation which uses the clocksource to get some
|
||||
* better resolution when scheduling the kernel. We accept that
|
||||
* this wraps around for now, since it is just a relative time
|
||||
* stamp. (Inspired by OMAP implementation.)
|
||||
*/
|
||||
|
||||
static u64 notrace u300_read_sched_clock(void)
|
||||
{
|
||||
return readl(u300_timer_base + U300_TIMER_APP_GPT2CC);
|
||||
}
|
||||
|
||||
static unsigned long u300_read_current_timer(void)
|
||||
{
|
||||
return readl(u300_timer_base + U300_TIMER_APP_GPT2CC);
|
||||
}
|
||||
|
||||
static struct delay_timer u300_delay_timer;
|
||||
|
||||
/*
|
||||
* This sets up the system timers, clock source and clock event.
|
||||
*/
|
||||
static int __init u300_timer_init_of(struct device_node *np)
|
||||
{
|
||||
unsigned int irq;
|
||||
struct clk *clk;
|
||||
unsigned long rate;
|
||||
int ret;
|
||||
|
||||
u300_timer_base = of_iomap(np, 0);
|
||||
if (!u300_timer_base) {
|
||||
pr_err("could not ioremap system timer\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Get the IRQ for the GP1 timer */
|
||||
irq = irq_of_parse_and_map(np, 2);
|
||||
if (!irq) {
|
||||
pr_err("no IRQ for system timer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_info("U300 GP1 timer @ base: %p, IRQ: %u\n", u300_timer_base, irq);
|
||||
|
||||
/* Clock the interrupt controller */
|
||||
clk = of_clk_get(np, 0);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
|
||||
u300_clockevent_data.ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ);
|
||||
|
||||
sched_clock_register(u300_read_sched_clock, 32, rate);
|
||||
|
||||
u300_delay_timer.read_current_timer = &u300_read_current_timer;
|
||||
u300_delay_timer.freq = rate;
|
||||
register_current_timer_delay(&u300_delay_timer);
|
||||
|
||||
/*
|
||||
* Disable the "OS" and "DD" timers - these are designed for Symbian!
|
||||
* Example usage in cnh1601578 cpu subsystem pd_timer_app.c
|
||||
*/
|
||||
writel(U300_TIMER_APP_CRC_CLOCK_REQUEST_ENABLE,
|
||||
u300_timer_base + U300_TIMER_APP_CRC);
|
||||
writel(U300_TIMER_APP_ROST_TIMER_RESET,
|
||||
u300_timer_base + U300_TIMER_APP_ROST);
|
||||
writel(U300_TIMER_APP_DOST_TIMER_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_DOST);
|
||||
writel(U300_TIMER_APP_RDDT_TIMER_RESET,
|
||||
u300_timer_base + U300_TIMER_APP_RDDT);
|
||||
writel(U300_TIMER_APP_DDDT_TIMER_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_DDDT);
|
||||
|
||||
/* Reset the General Purpose timer 1. */
|
||||
writel(U300_TIMER_APP_RGPT1_TIMER_RESET,
|
||||
u300_timer_base + U300_TIMER_APP_RGPT1);
|
||||
|
||||
/* Set up the IRQ handler */
|
||||
ret = request_irq(irq, u300_timer_interrupt,
|
||||
IRQF_TIMER | IRQF_IRQPOLL, "U300 Timer Tick", NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Reset the General Purpose timer 2 */
|
||||
writel(U300_TIMER_APP_RGPT2_TIMER_RESET,
|
||||
u300_timer_base + U300_TIMER_APP_RGPT2);
|
||||
/* Set this timer to run around forever */
|
||||
writel(0xFFFFFFFFU, u300_timer_base + U300_TIMER_APP_GPT2TC);
|
||||
/* Set continuous mode so it wraps around */
|
||||
writel(U300_TIMER_APP_SGPT2M_MODE_CONTINUOUS,
|
||||
u300_timer_base + U300_TIMER_APP_SGPT2M);
|
||||
/* Disable timer interrupts */
|
||||
writel(U300_TIMER_APP_GPT2IE_IRQ_DISABLE,
|
||||
u300_timer_base + U300_TIMER_APP_GPT2IE);
|
||||
/* Then enable the GP2 timer to use as a free running us counter */
|
||||
writel(U300_TIMER_APP_EGPT2_TIMER_ENABLE,
|
||||
u300_timer_base + U300_TIMER_APP_EGPT2);
|
||||
|
||||
/* Use general purpose timer 2 as clock source */
|
||||
ret = clocksource_mmio_init(u300_timer_base + U300_TIMER_APP_GPT2CC,
|
||||
"GPT2", rate, 300, 32, clocksource_mmio_readl_up);
|
||||
if (ret) {
|
||||
pr_err("timer: failed to initialize U300 clock source\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure and register the clockevent */
|
||||
clockevents_config_and_register(&u300_clockevent_data.cevd, rate,
|
||||
1, 0xffffffff);
|
||||
|
||||
/*
|
||||
* TODO: init and register the rest of the timers too, they can be
|
||||
* used by hrtimers!
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
TIMER_OF_DECLARE(u300_timer, "stericsson,u300-apptimer",
|
||||
u300_timer_init_of);
|
Loading…
Reference in New Issue
Block a user