mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Merge tag 'timers-clocksource-2025-11-30' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull clocksource updates from Thomas Gleixner:
"Updates for clocksource and clockevent drivers:
- A new driver for the Realtel system timer
- Prevent the unbinding of timers when the drivers do not support
that
- Expand the timer counter readout for the SPRD driver to 64 bit
to allow IOT devices suspend times of more than 36 hours, which
is the current limit of the 32-bi readout
- The usual small cleanups, fixes and enhancements all over the
place"
* tag 'timers-clocksource-2025-11-30' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
clocksource/drivers: Add Realtek system timer driver
dt-bindings: timer: Add Realtek SYSTIMER
clocksource/drivers/stm32-lp: Drop unused module alias
clocksource/drivers/rda: Add sched_clock_register for RDA8810PL SoC
clocksource/drivers/nxp-stm: Prevent driver unbind
clocksource/drivers/nxp-pit: Prevent driver unbind
clocksource/drivers/arm_arch_timer_mmio: Prevent driver unbind
clocksource/drivers/nxp-stm: Fix section mismatches
clocksource/drivers/sh_cmt: Always leave device running after probe
clocksource/drivers/stm: Fix double deregistration on probe failure
clocksource/drivers/ralink: Fix resource leaks in init error path
clocksource/drivers/timer-sp804: Fix read_current_timer() issue when clock source is not registered
clocksource/drivers/sprd: Enable register for timer counter from 32 bit to 64 bit
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/timer/realtek,rtd1625-systimer.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Realtek System Timer
|
||||
|
||||
maintainers:
|
||||
- Hao-Wen Ting <haowen.ting@realtek.com>
|
||||
|
||||
description:
|
||||
The Realtek SYSTIMER (System Timer) is a 64-bit global hardware counter operating
|
||||
at a fixed 1MHz frequency. Thanks to its compare match interrupt capability,
|
||||
the timer natively supports oneshot mode for tick broadcast functionality.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: realtek,rtd1625-systimer
|
||||
- items:
|
||||
- const: realtek,rtd1635-systimer
|
||||
- const: realtek,rtd1625-systimer
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
timer@89420 {
|
||||
compatible = "realtek,rtd1635-systimer",
|
||||
"realtek,rtd1625-systimer";
|
||||
reg = <0x89420 0x18>;
|
||||
interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
@@ -21681,6 +21681,11 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
|
||||
F: drivers/spi/spi-realtek-rtl-snand.c
|
||||
|
||||
REALTEK SYSTIMER DRIVER
|
||||
M: Hao-Wen Ting <haowen.ting@realtek.com>
|
||||
S: Maintained
|
||||
F: drivers/clocksource/timer-realtek.c
|
||||
|
||||
REALTEK WIRELESS DRIVER (rtlwifi family)
|
||||
M: Ping-Ke Shih <pkshih@realtek.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
|
||||
@@ -782,4 +782,15 @@ config NXP_STM_TIMER
|
||||
Enables the support for NXP System Timer Module found in the
|
||||
s32g NXP platform series.
|
||||
|
||||
config RTK_SYSTIMER
|
||||
bool "Realtek SYSTIMER support"
|
||||
depends on ARM || ARM64
|
||||
depends on ARCH_REALTEK || COMPILE_TEST
|
||||
select TIMER_OF
|
||||
help
|
||||
This option enables the driver that registers the global 1 MHz hardware
|
||||
counter as a clock event device on Realtek SoCs. Make sure to enable
|
||||
this option only when building for a Realtek platform or for compilation
|
||||
testing.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -95,3 +95,4 @@ obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o
|
||||
obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o
|
||||
obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o
|
||||
obj-$(CONFIG_NXP_STM_TIMER) += timer-nxp-stm.o
|
||||
obj-$(CONFIG_RTK_SYSTIMER) += timer-realtek.o
|
||||
|
||||
@@ -426,6 +426,7 @@ static struct platform_driver arch_timer_mmio_drv = {
|
||||
.driver = {
|
||||
.name = "arch-timer-mmio",
|
||||
.of_match_table = arch_timer_mmio_of_table,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = arch_timer_mmio_probe,
|
||||
};
|
||||
@@ -434,6 +435,7 @@ builtin_platform_driver(arch_timer_mmio_drv);
|
||||
static struct platform_driver arch_timer_mmio_acpi_drv = {
|
||||
.driver = {
|
||||
.name = "gtdt-arm-mmio-timer",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = arch_timer_mmio_probe,
|
||||
};
|
||||
|
||||
@@ -355,14 +355,6 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch)
|
||||
|
||||
dev_pm_syscore_device(&ch->cmt->pdev->dev, true);
|
||||
|
||||
/* enable clock */
|
||||
ret = clk_enable(ch->cmt->clk);
|
||||
if (ret) {
|
||||
dev_err(&ch->cmt->pdev->dev, "ch%u: cannot enable clock\n",
|
||||
ch->index);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
/* make sure channel is disabled */
|
||||
sh_cmt_start_stop_ch(ch, 0);
|
||||
|
||||
@@ -384,19 +376,12 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch)
|
||||
if (ret || sh_cmt_read_cmcnt(ch)) {
|
||||
dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n",
|
||||
ch->index);
|
||||
ret = -ETIMEDOUT;
|
||||
goto err1;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* enable channel */
|
||||
sh_cmt_start_stop_ch(ch, 1);
|
||||
return 0;
|
||||
err1:
|
||||
/* stop clock */
|
||||
clk_disable(ch->cmt->clk);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sh_cmt_disable(struct sh_cmt_channel *ch)
|
||||
@@ -407,9 +392,6 @@ static void sh_cmt_disable(struct sh_cmt_channel *ch)
|
||||
/* disable interrupts in CMT block */
|
||||
sh_cmt_write_cmcsr(ch, 0);
|
||||
|
||||
/* stop clock */
|
||||
clk_disable(ch->cmt->clk);
|
||||
|
||||
dev_pm_syscore_device(&ch->cmt->pdev->dev, false);
|
||||
}
|
||||
|
||||
@@ -583,8 +565,6 @@ static int sh_cmt_start_clocksource(struct sh_cmt_channel *ch)
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
pm_runtime_get_sync(&ch->cmt->pdev->dev);
|
||||
|
||||
raw_spin_lock_irqsave(&ch->lock, flags);
|
||||
|
||||
if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
|
||||
@@ -619,8 +599,6 @@ static void sh_cmt_stop_clocksource(struct sh_cmt_channel *ch)
|
||||
sh_cmt_disable(ch);
|
||||
|
||||
raw_spin_unlock_irqrestore(&ch->lock, flags);
|
||||
|
||||
pm_runtime_put(&ch->cmt->pdev->dev);
|
||||
}
|
||||
|
||||
static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch)
|
||||
@@ -630,10 +608,8 @@ static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch)
|
||||
|
||||
raw_spin_lock_irqsave(&ch->lock, flags);
|
||||
|
||||
if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) {
|
||||
pm_runtime_get_sync(&ch->cmt->pdev->dev);
|
||||
if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
|
||||
ret = sh_cmt_enable(ch);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
@@ -656,10 +632,8 @@ static void sh_cmt_stop_clockevent(struct sh_cmt_channel *ch)
|
||||
|
||||
ch->flags &= ~FLAG_CLOCKEVENT;
|
||||
|
||||
if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) {
|
||||
if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
|
||||
sh_cmt_disable(ch);
|
||||
pm_runtime_put(&ch->cmt->pdev->dev);
|
||||
}
|
||||
|
||||
/* adjust the timeout to maximum if only clocksource left */
|
||||
if (ch->flags & FLAG_CLOCKSOURCE)
|
||||
@@ -1134,8 +1108,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
|
||||
mask &= ~(1 << hwidx);
|
||||
}
|
||||
|
||||
clk_disable(cmt->clk);
|
||||
|
||||
platform_set_drvdata(pdev, cmt);
|
||||
|
||||
return 0;
|
||||
@@ -1183,8 +1155,6 @@ static int sh_cmt_probe(struct platform_device *pdev)
|
||||
out:
|
||||
if (cmt->has_clockevent || cmt->has_clocksource)
|
||||
pm_runtime_irq_safe(&pdev->dev);
|
||||
else
|
||||
pm_runtime_idle(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -374,9 +374,10 @@ static struct platform_driver nxp_pit_driver = {
|
||||
.driver = {
|
||||
.name = "nxp-pit",
|
||||
.of_match_table = pit_timer_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = pit_timer_probe,
|
||||
};
|
||||
module_platform_driver(nxp_pit_driver);
|
||||
builtin_platform_driver(nxp_pit_driver);
|
||||
|
||||
TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);
|
||||
|
||||
@@ -177,15 +177,15 @@ static void nxp_stm_clocksource_resume(struct clocksource *cs)
|
||||
nxp_stm_clocksource_enable(cs);
|
||||
}
|
||||
|
||||
static void __init devm_clocksource_unregister(void *data)
|
||||
static void devm_clocksource_unregister(void *data)
|
||||
{
|
||||
struct stm_timer *stm_timer = data;
|
||||
|
||||
clocksource_unregister(&stm_timer->cs);
|
||||
}
|
||||
|
||||
static int __init nxp_stm_clocksource_init(struct device *dev, struct stm_timer *stm_timer,
|
||||
const char *name, void __iomem *base, struct clk *clk)
|
||||
static int nxp_stm_clocksource_init(struct device *dev, struct stm_timer *stm_timer,
|
||||
const char *name, void __iomem *base, struct clk *clk)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -208,10 +208,8 @@ static int __init nxp_stm_clocksource_init(struct device *dev, struct stm_timer
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, devm_clocksource_unregister, stm_timer);
|
||||
if (ret) {
|
||||
clocksource_unregister(&stm_timer->cs);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
stm_sched_clock = stm_timer;
|
||||
|
||||
@@ -298,9 +296,9 @@ static void nxp_stm_clockevent_resume(struct clock_event_device *ced)
|
||||
nxp_stm_module_get(stm_timer);
|
||||
}
|
||||
|
||||
static int __init nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm_timer *stm_timer,
|
||||
const char *name, void __iomem *base, int irq,
|
||||
struct clk *clk, int cpu)
|
||||
static int nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm_timer *stm_timer,
|
||||
const char *name, void __iomem *base, int irq,
|
||||
struct clk *clk, int cpu)
|
||||
{
|
||||
stm_timer->base = base;
|
||||
stm_timer->rate = clk_get_rate(clk);
|
||||
@@ -388,7 +386,7 @@ static irqreturn_t nxp_stm_module_interrupt(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __init nxp_stm_timer_probe(struct platform_device *pdev)
|
||||
static int nxp_stm_timer_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stm_timer *stm_timer;
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -484,14 +482,15 @@ static const struct of_device_id nxp_stm_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, nxp_stm_of_match);
|
||||
|
||||
static struct platform_driver nxp_stm_probe = {
|
||||
static struct platform_driver nxp_stm_driver = {
|
||||
.probe = nxp_stm_timer_probe,
|
||||
.driver = {
|
||||
.name = "nxp-stm",
|
||||
.of_match_table = nxp_stm_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
module_platform_driver(nxp_stm_probe);
|
||||
builtin_platform_driver(nxp_stm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("NXP System Timer Module driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -130,14 +130,15 @@ static int __init ralink_systick_init(struct device_node *np)
|
||||
systick.dev.irq = irq_of_parse_and_map(np, 0);
|
||||
if (!systick.dev.irq) {
|
||||
pr_err("%pOFn: request_irq failed", np);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
|
||||
SYSTICK_FREQ, 301, 16,
|
||||
clocksource_mmio_readl_up);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_free_irq;
|
||||
|
||||
clockevents_register_device(&systick.dev);
|
||||
|
||||
@@ -145,6 +146,12 @@ static int __init ralink_systick_init(struct device_node *np)
|
||||
np, systick.dev.mult, systick.dev.shift);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
irq_dispose_mapping(systick.dev.irq);
|
||||
err_iounmap:
|
||||
iounmap(systick.membase);
|
||||
return ret;
|
||||
}
|
||||
|
||||
TIMER_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
#include "timer-of.h"
|
||||
|
||||
@@ -153,7 +154,7 @@ static struct timer_of rda_ostimer_of = {
|
||||
},
|
||||
};
|
||||
|
||||
static u64 rda_hwtimer_read(struct clocksource *cs)
|
||||
static u64 rda_hwtimer_clocksource_read(void)
|
||||
{
|
||||
void __iomem *base = timer_of_base(&rda_ostimer_of);
|
||||
u32 lo, hi;
|
||||
@@ -167,6 +168,11 @@ static u64 rda_hwtimer_read(struct clocksource *cs)
|
||||
return ((u64)hi << 32) | lo;
|
||||
}
|
||||
|
||||
static u64 rda_hwtimer_read(struct clocksource *cs)
|
||||
{
|
||||
return rda_hwtimer_clocksource_read();
|
||||
}
|
||||
|
||||
static struct clocksource rda_hwtimer_clocksource = {
|
||||
.name = "rda-timer",
|
||||
.rating = 400,
|
||||
@@ -185,6 +191,7 @@ static int __init rda_timer_init(struct device_node *np)
|
||||
return ret;
|
||||
|
||||
clocksource_register_hz(&rda_hwtimer_clocksource, rate);
|
||||
sched_clock_register(rda_hwtimer_clocksource_read, 64, rate);
|
||||
|
||||
clockevents_config_and_register(&rda_ostimer_of.clkevt, rate,
|
||||
0x2, UINT_MAX);
|
||||
|
||||
150
drivers/clocksource/timer-realtek.c
Normal file
150
drivers/clocksource/timer-realtek.c
Normal file
@@ -0,0 +1,150 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2025 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include "timer-of.h"
|
||||
|
||||
#define ENBL 1
|
||||
#define DSBL 0
|
||||
|
||||
#define SYSTIMER_RATE 1000000
|
||||
#define SYSTIMER_MIN_DELTA 0x64
|
||||
#define SYSTIMER_MAX_DELTA ULONG_MAX
|
||||
|
||||
/* SYSTIMER Register Offset (RTK Internal Use) */
|
||||
#define TS_LW_OFST 0x0
|
||||
#define TS_HW_OFST 0x4
|
||||
#define TS_CMP_VAL_LW_OFST 0x8
|
||||
#define TS_CMP_VAL_HW_OFST 0xC
|
||||
#define TS_CMP_CTRL_OFST 0x10
|
||||
#define TS_CMP_STAT_OFST 0x14
|
||||
|
||||
/* SYSTIMER CMP CTRL REG Mask */
|
||||
#define TS_CMP_EN_MASK 0x1
|
||||
#define TS_WR_EN0_MASK 0x2
|
||||
|
||||
static void __iomem *systimer_base;
|
||||
|
||||
static u64 rtk_ts64_read(void)
|
||||
{
|
||||
u32 low, high;
|
||||
u64 ts;
|
||||
|
||||
/* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */
|
||||
low = readl(systimer_base + TS_LW_OFST);
|
||||
high = readl(systimer_base + TS_HW_OFST);
|
||||
ts = ((u64)high << 32) | low;
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
static void rtk_cmp_value_write(u64 value)
|
||||
{
|
||||
u32 high, low;
|
||||
|
||||
low = value & 0xFFFFFFFF;
|
||||
high = value >> 32;
|
||||
|
||||
writel(high, systimer_base + TS_CMP_VAL_HW_OFST);
|
||||
writel(low, systimer_base + TS_CMP_VAL_LW_OFST);
|
||||
}
|
||||
|
||||
static inline void rtk_cmp_en_write(bool cmp_en)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = TS_WR_EN0_MASK;
|
||||
if (cmp_en == ENBL)
|
||||
val |= TS_CMP_EN_MASK;
|
||||
|
||||
writel(val, systimer_base + TS_CMP_CTRL_OFST);
|
||||
}
|
||||
|
||||
static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt)
|
||||
{
|
||||
u64 cmp_val;
|
||||
|
||||
rtk_cmp_en_write(DSBL);
|
||||
cmp_val = rtk_ts64_read();
|
||||
|
||||
/* Set CMP value to current timestamp plus delta_us */
|
||||
rtk_cmp_value_write(cmp_val + cycles);
|
||||
rtk_cmp_en_write(ENBL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *clkevt = dev_id;
|
||||
void __iomem *reg_base;
|
||||
u32 val;
|
||||
|
||||
/* Disable TS CMP Match */
|
||||
rtk_cmp_en_write(DSBL);
|
||||
|
||||
/* Clear TS CMP INTR */
|
||||
reg_base = systimer_base + TS_CMP_STAT_OFST;
|
||||
val = readl(reg_base) & TS_CMP_EN_MASK;
|
||||
writel(val | TS_CMP_EN_MASK, reg_base);
|
||||
clkevt->event_handler(clkevt);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rtk_syst_shutdown(struct clock_event_device *clkevt)
|
||||
{
|
||||
void __iomem *reg_base;
|
||||
u64 cmp_val = 0;
|
||||
|
||||
/* Disable TS CMP Match */
|
||||
rtk_cmp_en_write(DSBL);
|
||||
/* Set compare value to 0 */
|
||||
rtk_cmp_value_write(cmp_val);
|
||||
|
||||
/* Clear TS CMP INTR */
|
||||
reg_base = systimer_base + TS_CMP_STAT_OFST;
|
||||
writel(TS_CMP_EN_MASK, reg_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct timer_of rtk_timer_to = {
|
||||
.flags = TIMER_OF_IRQ | TIMER_OF_BASE,
|
||||
|
||||
.clkevt = {
|
||||
.name = "rtk-clkevt",
|
||||
.rating = 300,
|
||||
.cpumask = cpu_possible_mask,
|
||||
.features = CLOCK_EVT_FEAT_DYNIRQ |
|
||||
CLOCK_EVT_FEAT_ONESHOT,
|
||||
.set_next_event = rtk_syst_clkevt_next_event,
|
||||
.set_state_oneshot = rtk_syst_shutdown,
|
||||
.set_state_shutdown = rtk_syst_shutdown,
|
||||
},
|
||||
|
||||
.of_irq = {
|
||||
.flags = IRQF_TIMER | IRQF_IRQPOLL,
|
||||
.handler = rtk_ts_match_intr_handler,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init rtk_systimer_init(struct device_node *node)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = timer_of_init(node, &rtk_timer_to);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
systimer_base = timer_of_base(&rtk_timer_to);
|
||||
clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE,
|
||||
SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);
|
||||
@@ -21,6 +21,10 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
#include <linux/delay.h>
|
||||
#endif
|
||||
|
||||
#include "timer-sp.h"
|
||||
|
||||
/* Hisilicon 64-bit timer(a variant of ARM SP804) */
|
||||
@@ -102,6 +106,23 @@ static u64 notrace sp804_read(void)
|
||||
return ~readl_relaxed(sched_clkevt->value);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
static struct delay_timer delay;
|
||||
static unsigned long sp804_read_delay_timer_read(void)
|
||||
{
|
||||
return sp804_read();
|
||||
}
|
||||
|
||||
static void sp804_register_delay_timer(int freq)
|
||||
{
|
||||
delay.freq = freq;
|
||||
delay.read_current_timer = sp804_read_delay_timer_read;
|
||||
register_current_timer_delay(&delay);
|
||||
}
|
||||
#else
|
||||
static inline void sp804_register_delay_timer(int freq) {}
|
||||
#endif
|
||||
|
||||
static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
|
||||
const char *name,
|
||||
struct clk *clk,
|
||||
@@ -114,6 +135,8 @@ static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
|
||||
if (rate < 0)
|
||||
return -EINVAL;
|
||||
|
||||
sp804_register_delay_timer(rate);
|
||||
|
||||
clkevt = sp804_clkevt_get(base);
|
||||
|
||||
writel(0, clkevt->ctrl);
|
||||
@@ -318,6 +341,7 @@ static int __init sp804_of_init(struct device_node *np, struct sp804_timer *time
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#define TIMER_VALUE_SHDW_HI 0x1c
|
||||
|
||||
#define TIMER_VALUE_LO_MASK GENMASK(31, 0)
|
||||
#define TIMER_VALUE_HI_MASK GENMASK(31, 0)
|
||||
|
||||
static void sprd_timer_enable(void __iomem *base, u32 flag)
|
||||
{
|
||||
@@ -162,15 +163,26 @@ static struct timer_of suspend_to = {
|
||||
|
||||
static u64 sprd_suspend_timer_read(struct clocksource *cs)
|
||||
{
|
||||
return ~(u64)readl_relaxed(timer_of_base(&suspend_to) +
|
||||
TIMER_VALUE_SHDW_LO) & cs->mask;
|
||||
u32 lo, hi;
|
||||
|
||||
do {
|
||||
hi = readl_relaxed(timer_of_base(&suspend_to) +
|
||||
TIMER_VALUE_SHDW_HI);
|
||||
lo = readl_relaxed(timer_of_base(&suspend_to) +
|
||||
TIMER_VALUE_SHDW_LO);
|
||||
} while (hi != readl_relaxed(timer_of_base(&suspend_to) + TIMER_VALUE_SHDW_HI));
|
||||
|
||||
return ~(((u64)hi << 32) | lo);
|
||||
}
|
||||
|
||||
static int sprd_suspend_timer_enable(struct clocksource *cs)
|
||||
{
|
||||
sprd_timer_update_counter(timer_of_base(&suspend_to),
|
||||
TIMER_VALUE_LO_MASK);
|
||||
sprd_timer_enable(timer_of_base(&suspend_to), TIMER_CTL_PERIOD_MODE);
|
||||
writel_relaxed(TIMER_VALUE_LO_MASK,
|
||||
timer_of_base(&suspend_to) + TIMER_LOAD_LO);
|
||||
writel_relaxed(TIMER_VALUE_HI_MASK,
|
||||
timer_of_base(&suspend_to) + TIMER_LOAD_HI);
|
||||
sprd_timer_enable(timer_of_base(&suspend_to),
|
||||
TIMER_CTL_PERIOD_MODE|TIMER_CTL_64BIT_WIDTH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -186,7 +198,7 @@ static struct clocksource suspend_clocksource = {
|
||||
.read = sprd_suspend_timer_read,
|
||||
.enable = sprd_suspend_timer_enable,
|
||||
.disable = sprd_suspend_timer_disable,
|
||||
.mask = CLOCKSOURCE_MASK(32),
|
||||
.mask = CLOCKSOURCE_MASK(64),
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP,
|
||||
};
|
||||
|
||||
|
||||
@@ -289,5 +289,4 @@ static struct platform_driver stm32_clkevent_lp_driver = {
|
||||
};
|
||||
module_platform_driver(stm32_clkevent_lp_driver);
|
||||
|
||||
MODULE_ALIAS("platform:stm32-lptimer-timer");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 clockevent low power driver");
|
||||
|
||||
Reference in New Issue
Block a user