提交 dfea8482 编写于 作者: L Linus Torvalds

Merge tag 'timers-core-2022-08-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull timer updates from Thomas Gleixner:
 "Timers, timekeeping and related drivers update:

  Core:

   - Make wait_event_hrtimeout() aware of RT/DL tasks

  New drivers:

   - R-Car Gen4 timer

   - Tegra186 timer

   - Mediatek MT6795 CPUXGPT timer

  Updates:

   - Rework suspend/resume handling in timer drivers so it
     takes inactive clocks into account.

   - The usual device tree compatible add ons

   - Small fixed and cleanups all over the place"

* tag 'timers-core-2022-08-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (24 commits)
  wait: Fix __wait_event_hrtimeout for RT/DL tasks
  clocksource/drivers/sun5i: Remove unnecessary (void*) conversions
  dt-bindings: timer: allwinner,sun4i-a10-timer: Add D1 compatible
  dt-bindings: timer: ingenic,tcu: use absolute path to other schema
  clocksource/drivers/sun4i: Remove unnecessary (void*) conversions
  dt-bindings: timer: renesas,cmt: Fix R-Car Gen4 fall-out
  clocksource/drivers/tegra186: Put Kconfig option 'tristate' to 'bool'
  clocksource/drivers/timer-ti-dm: Make driver selection bool for TI K3
  clocksource/drivers/timer-ti-dm: Add compatible for am6 SoCs
  clocksource/drivers/timer-ti-dm: Make timer selectable for ARCH_K3
  clocksource/drivers/timer-ti-dm: Move inline functions to driver for am6
  clocksource/drivers/sh_cmt: Add R-Car Gen4 support
  dt-bindings: timer: renesas,cmt: R-Car V3U is R-Car Gen4
  dt-bindings: timer: renesas,cmt: Add r8a779f0 and generic Gen4 CMT support
  clocksource/drivers/timer-microchip-pit64b: Fix compilation warnings
  clocksource/drivers/timer-microchip-pit64b: Use mchp_pit64b_{suspend, resume}
  clocksource/drivers/timer-microchip-pit64b: Remove suspend/resume ops for ce
  thermal/drivers/rcar_gen3_thermal: Add r8a779f0 support
  clocksource/drivers/timer-mediatek: Implement CPUXGPT timers
  dt-bindings: timer: mediatek: Add CPUX System Timer and MT6795 compatible
  ...
......@@ -20,6 +20,7 @@ properties:
- allwinner,suniv-f1c100s-timer
- items:
- enum:
- allwinner,sun20i-d1-timer
- allwinner,sun50i-a64-timer
- allwinner,sun50i-h6-timer
- allwinner,sun50i-h616-timer
......
......@@ -113,7 +113,7 @@ properties:
patternProperties:
"^watchdog@[a-f0-9]+$":
type: object
$ref: ../watchdog/watchdog.yaml#
$ref: /schemas/watchdog/watchdog.yaml#
properties:
compatible:
oneOf:
......@@ -145,7 +145,7 @@ patternProperties:
"^pwm@[a-f0-9]+$":
type: object
$ref: ../pwm/pwm.yaml#
$ref: /schemas/pwm/pwm.yaml#
properties:
compatible:
oneOf:
......
MediaTek Timers
---------------
MediaTek SoCs have two different timers on different platforms,
MediaTek SoCs have different timers on different platforms,
- CPUX (ARM/ARM64 System Timer)
- GPT (General Purpose Timer)
- SYST (System Timer)
......@@ -29,6 +30,9 @@ Required properties:
* "mediatek,mt7629-timer" for MT7629 compatible timers (SYST)
* "mediatek,mt6765-timer" for MT6765 and all above compatible timers (SYST)
For those SoCs that use CPUX
* "mediatek,mt6795-systimer" for MT6795 compatible timers (CPUX)
- reg: Should contain location and length for timer register.
- clocks: Should contain system clock.
......
......@@ -80,7 +80,6 @@ properties:
- renesas,r8a77980-cmt0 # 32-bit CMT0 on R-Car V3H
- renesas,r8a77990-cmt0 # 32-bit CMT0 on R-Car E3
- renesas,r8a77995-cmt0 # 32-bit CMT0 on R-Car D3
- renesas,r8a779a0-cmt0 # 32-bit CMT0 on R-Car V3U
- const: renesas,rcar-gen3-cmt0 # 32-bit CMT0 on R-Car Gen3 and RZ/G2
- items:
......@@ -97,9 +96,20 @@ properties:
- renesas,r8a77980-cmt1 # 48-bit CMT on R-Car V3H
- renesas,r8a77990-cmt1 # 48-bit CMT on R-Car E3
- renesas,r8a77995-cmt1 # 48-bit CMT on R-Car D3
- renesas,r8a779a0-cmt1 # 48-bit CMT on R-Car V3U
- const: renesas,rcar-gen3-cmt1 # 48-bit CMT on R-Car Gen3 and RZ/G2
- items:
- enum:
- renesas,r8a779a0-cmt0 # 32-bit CMT0 on R-Car V3U
- renesas,r8a779f0-cmt0 # 32-bit CMT0 on R-Car S4-8
- const: renesas,rcar-gen4-cmt0 # 32-bit CMT0 on R-Car Gen4
- items:
- enum:
- renesas,r8a779a0-cmt1 # 48-bit CMT on R-Car V3U
- renesas,r8a779f0-cmt1 # 48-bit CMT on R-Car S4-8
- const: renesas,rcar-gen4-cmt1 # 48-bit CMT on R-Car Gen4
reg:
maxItems: 1
......@@ -135,6 +145,7 @@ allOf:
enum:
- renesas,rcar-gen2-cmt0
- renesas,rcar-gen3-cmt0
- renesas,rcar-gen4-cmt0
then:
properties:
interrupts:
......@@ -148,6 +159,7 @@ allOf:
enum:
- renesas,rcar-gen2-cmt1
- renesas,rcar-gen3-cmt1
- renesas,rcar-gen4-cmt1
then:
properties:
interrupts:
......
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2022 Linaro Ltd.
%YAML 1.2
---
$id: "http://devicetree.org/schemas/timer/st,nomadik-mtu.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: ST Microelectronics Nomadik Multi-Timer Unit MTU Timer
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
description: This timer is found in the ST Microelectronics Nomadik
SoCs STn8800, STn8810 and STn8815 as well as in ST-Ericsson DB8500.
properties:
compatible:
items:
- const: st,nomadik-mtu
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
description: The first clock named TIMCLK clocks the actual timers and
the second clock clocks the digital interface to the interconnect.
maxItems: 2
clock-names:
items:
- const: timclk
- const: apb_pclk
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/mfd/dbx500-prcmu.h>
timer@a03c6000 {
compatible = "st,nomadik-mtu";
reg = <0xa03c6000 0x1000>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&prcmu_clk PRCMU_TIMCLK>, <&prcc_pclk 6 6>;
clock-names = "timclk", "apb_pclk";
};
......@@ -105,6 +105,7 @@ config ARCH_OMAP2PLUS
select MACH_OMAP_GENERIC
select MEMORY
select MFD_SYSCON
select OMAP_DM_SYSTIMER
select OMAP_DM_TIMER
select OMAP_GPMC
select PINCTRL
......@@ -209,6 +210,7 @@ config SOC_OMAP2420
bool "OMAP2420 support"
depends on ARCH_OMAP2
default y
select OMAP_DM_SYSTIMER
select OMAP_DM_TIMER
select SOC_HAS_OMAP2_SDRC
......
......@@ -22,7 +22,7 @@ config CLKEVT_I8253
config I8253_LOCK
bool
config OMAP_DM_TIMER
config OMAP_DM_SYSTIMER
bool
select TIMER_OF
......@@ -56,6 +56,13 @@ config DIGICOLOR_TIMER
help
Enables the support for the digicolor timer driver.
config OMAP_DM_TIMER
bool "OMAP dual-mode timer driver" if ARCH_K3 || COMPILE_TEST
default y if ARCH_K3
select TIMER_OF
help
Enables the support for the TI dual-mode timer driver.
config DW_APB_TIMER
bool "DW APB timer driver" if COMPILE_TEST
help
......@@ -150,6 +157,14 @@ config TEGRA_TIMER
help
Enables support for the Tegra driver.
config TEGRA186_TIMER
bool "NVIDIA Tegra186 timer driver"
depends on ARCH_TEGRA || COMPILE_TEST
depends on WATCHDOG && WATCHDOG_CORE
help
Enables support for the timers and watchdogs found on NVIDIA
Tegra186 and later SoCs.
config VT8500_TIMER
bool "VT8500 timer driver" if COMPILE_TEST
depends on HAS_IOMEM
......@@ -367,7 +382,7 @@ config ARM_GT_INITIAL_PRESCALER_VAL
depends on ARM_GLOBAL_TIMER
help
When the ARM global timer initializes, its current rate is declared
to the kernel and maintained forever. Should it's parent clock
to the kernel and maintained forever. Should its parent clock
change, the driver tries to fix the timer's internal prescaler.
On some machs (i.e. Zynq) the initial prescaler value thus poses
bounds about how much the parent clock is allowed to decrease or
......
......@@ -18,7 +18,7 @@ obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
obj-$(CONFIG_DAVINCI_TIMER) += timer-davinci.o
obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o
obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm.o
obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm-systimer.o
obj-$(CONFIG_OMAP_DM_SYSTIMER) += timer-ti-dm-systimer.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
obj-$(CONFIG_FTTMR010_TIMER) += timer-fttmr010.o
......@@ -36,6 +36,7 @@ obj-$(CONFIG_SUN4I_TIMER) += timer-sun4i.o
obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o
obj-$(CONFIG_MESON6_TIMER) += timer-meson6.o
obj-$(CONFIG_TEGRA_TIMER) += timer-tegra.o
obj-$(CONFIG_TEGRA186_TIMER) += timer-tegra186.o
obj-$(CONFIG_VT8500_TIMER) += timer-vt8500.o
obj-$(CONFIG_NSPIRE_TIMER) += timer-zevio.o
obj-$(CONFIG_BCM_KONA_TIMER) += bcm_kona_timer.o
......
......@@ -981,6 +981,14 @@ static const struct of_device_id sh_cmt_of_table[] __maybe_unused = {
.compatible = "renesas,rcar-gen3-cmt1",
.data = &sh_cmt_info[SH_CMT1_RCAR_GEN2]
},
{
.compatible = "renesas,rcar-gen4-cmt0",
.data = &sh_cmt_info[SH_CMT0_RCAR_GEN2]
},
{
.compatible = "renesas,rcar-gen4-cmt1",
.data = &sh_cmt_info[SH_CMT1_RCAR_GEN2]
},
{ }
};
MODULE_DEVICE_TABLE(of, sh_cmt_of_table);
......
......@@ -22,6 +22,19 @@
#define TIMER_SYNC_TICKS (3)
/* cpux mcusys wrapper */
#define CPUX_CON_REG 0x0
#define CPUX_IDX_REG 0x4
/* cpux */
#define CPUX_IDX_GLOBAL_CTRL 0x0
#define CPUX_ENABLE BIT(0)
#define CPUX_CLK_DIV_MASK GENMASK(10, 8)
#define CPUX_CLK_DIV1 BIT(8)
#define CPUX_CLK_DIV2 BIT(9)
#define CPUX_CLK_DIV4 BIT(10)
#define CPUX_IDX_GLOBAL_IRQ 0x30
/* gpt */
#define GPT_IRQ_EN_REG 0x00
#define GPT_IRQ_ENABLE(val) BIT((val) - 1)
......@@ -72,6 +85,52 @@
static void __iomem *gpt_sched_reg __read_mostly;
static u32 mtk_cpux_readl(u32 reg_idx, struct timer_of *to)
{
writel(reg_idx, timer_of_base(to) + CPUX_IDX_REG);
return readl(timer_of_base(to) + CPUX_CON_REG);
}
static void mtk_cpux_writel(u32 val, u32 reg_idx, struct timer_of *to)
{
writel(reg_idx, timer_of_base(to) + CPUX_IDX_REG);
writel(val, timer_of_base(to) + CPUX_CON_REG);
}
static void mtk_cpux_set_irq(struct timer_of *to, bool enable)
{
const unsigned long *irq_mask = cpumask_bits(cpu_possible_mask);
u32 val;
val = mtk_cpux_readl(CPUX_IDX_GLOBAL_IRQ, to);
if (enable)
val |= *irq_mask;
else
val &= ~(*irq_mask);
mtk_cpux_writel(val, CPUX_IDX_GLOBAL_IRQ, to);
}
static int mtk_cpux_clkevt_shutdown(struct clock_event_device *clkevt)
{
/* Clear any irq */
mtk_cpux_set_irq(to_timer_of(clkevt), false);
/*
* Disabling CPUXGPT timer will crash the platform, especially
* if Trusted Firmware is using it (usually, for sleep states),
* so we only mask the IRQ and call it a day.
*/
return 0;
}
static int mtk_cpux_clkevt_resume(struct clock_event_device *clkevt)
{
mtk_cpux_set_irq(to_timer_of(clkevt), true);
return 0;
}
static void mtk_syst_ack_irq(struct timer_of *to)
{
/* Clear and disable interrupt */
......@@ -281,6 +340,60 @@ static struct timer_of to = {
},
};
static int __init mtk_cpux_init(struct device_node *node)
{
static struct timer_of to_cpux;
u32 freq, val;
int ret;
/*
* There are per-cpu interrupts for the CPUX General Purpose Timer
* but since this timer feeds the AArch64 System Timer we can rely
* on the CPU timer PPIs as well, so we don't declare TIMER_OF_IRQ.
*/
to_cpux.flags = TIMER_OF_BASE | TIMER_OF_CLOCK;
to_cpux.clkevt.name = "mtk-cpuxgpt";
to_cpux.clkevt.rating = 10;
to_cpux.clkevt.cpumask = cpu_possible_mask;
to_cpux.clkevt.set_state_shutdown = mtk_cpux_clkevt_shutdown;
to_cpux.clkevt.tick_resume = mtk_cpux_clkevt_resume;
/* If this fails, bad things are about to happen... */
ret = timer_of_init(node, &to_cpux);
if (ret) {
WARN(1, "Cannot start CPUX timers.\n");
return ret;
}
/*
* Check if we're given a clock with the right frequency for this
* timer, otherwise warn but keep going with the setup anyway, as
* that makes it possible to still boot the kernel, even though
* it may not work correctly (random lockups, etc).
* The reason behind this is that having an early UART may not be
* possible for everyone and this gives a chance to retrieve kmsg
* for eventual debugging even on consumer devices.
*/
freq = timer_of_rate(&to_cpux);
if (freq > 13000000)
WARN(1, "Requested unsupported timer frequency %u\n", freq);
/* Clock input is 26MHz, set DIV2 to achieve 13MHz clock */
val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, &to_cpux);
val &= ~CPUX_CLK_DIV_MASK;
val |= CPUX_CLK_DIV2;
mtk_cpux_writel(val, CPUX_IDX_GLOBAL_CTRL, &to_cpux);
/* Enable all CPUXGPT timers */
val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, &to_cpux);
mtk_cpux_writel(val | CPUX_ENABLE, CPUX_IDX_GLOBAL_CTRL, &to_cpux);
clockevents_config_and_register(&to_cpux.clkevt, timer_of_rate(&to_cpux),
TIMER_SYNC_TICKS, 0xffffffff);
return 0;
}
static int __init mtk_syst_init(struct device_node *node)
{
int ret;
......@@ -339,3 +452,4 @@ static int __init mtk_gpt_init(struct device_node *node)
}
TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init);
TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init);
TIMER_OF_DECLARE(mtk_mt6795, "mediatek,mt6795-systimer", mtk_cpux_init);
......@@ -61,7 +61,7 @@ struct mchp_pit64b_timer {
};
/**
* mchp_pit64b_clkevt - PIT64B clockevent data structure
* struct mchp_pit64b_clkevt - PIT64B clockevent data structure
* @timer: PIT64B timer
* @clkevt: clockevent
*/
......@@ -75,7 +75,7 @@ struct mchp_pit64b_clkevt {
struct mchp_pit64b_clkevt, clkevt))
/**
* mchp_pit64b_clksrc - PIT64B clocksource data structure
* struct mchp_pit64b_clksrc - PIT64B clocksource data structure
* @timer: PIT64B timer
* @clksrc: clocksource
*/
......@@ -173,7 +173,8 @@ static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev)
{
struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
if (!clockevent_state_detached(cedev))
mchp_pit64b_suspend(timer);
return 0;
}
......@@ -182,35 +183,37 @@ static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev)
{
struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
if (clockevent_state_shutdown(cedev))
mchp_pit64b_resume(timer);
mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_CONT,
MCHP_PIT64B_IER_PERIOD);
return 0;
}
static int mchp_pit64b_clkevt_set_next_event(unsigned long evt,
struct clock_event_device *cedev)
static int mchp_pit64b_clkevt_set_oneshot(struct clock_event_device *cedev)
{
struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT,
if (clockevent_state_shutdown(cedev))
mchp_pit64b_resume(timer);
mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_ONE_SHOT,
MCHP_PIT64B_IER_PERIOD);
return 0;
}
static void mchp_pit64b_clkevt_suspend(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 = clkevt_to_mchp_pit64b_timer(cedev);
mchp_pit64b_suspend(timer);
}
static void mchp_pit64b_clkevt_resume(struct clock_event_device *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);
mchp_pit64b_resume(timer);
return 0;
}
static irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id)
......@@ -242,8 +245,10 @@ static void __init mchp_pit64b_pres_compute(u32 *pres, u32 clk_rate,
}
/**
* mchp_pit64b_init_mode - prepare PIT64B mode register value to be used at
* runtime; this includes prescaler and SGCLK bit
* mchp_pit64b_init_mode() - prepare PIT64B mode register value to be used at
* runtime; this includes prescaler and SGCLK bit
* @timer: pointer to pit64b timer to init
* @max_rate: maximum rate that timer's clock could use
*
* PIT64B timer may be fed by gclk or pclk. When gclk is used its rate has to
* be at least 3 times lower that pclk's rate. pclk rate is fixed, gclk rate
......@@ -341,6 +346,7 @@ static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer,
if (!cs)
return -ENOMEM;
mchp_pit64b_resume(timer);
mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
mchp_pit64b_cs_base = timer->base;
......@@ -362,8 +368,7 @@ static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer,
pr_debug("clksrc: Failed to register PIT64B clocksource!\n");
/* Stop timer. */
writel_relaxed(MCHP_PIT64B_CR_SWRST,
timer->base + MCHP_PIT64B_CR);
mchp_pit64b_suspend(timer);
kfree(cs);
return ret;
......@@ -395,9 +400,8 @@ static int __init mchp_pit64b_init_clkevt(struct mchp_pit64b_timer *timer,
ce->clkevt.rating = 150;
ce->clkevt.set_state_shutdown = mchp_pit64b_clkevt_shutdown;
ce->clkevt.set_state_periodic = mchp_pit64b_clkevt_set_periodic;
ce->clkevt.set_state_oneshot = mchp_pit64b_clkevt_set_oneshot;
ce->clkevt.set_next_event = mchp_pit64b_clkevt_set_next_event;
ce->clkevt.suspend = mchp_pit64b_clkevt_suspend;
ce->clkevt.resume = mchp_pit64b_clkevt_resume;
ce->clkevt.cpumask = cpumask_of(0);
ce->clkevt.irq = irq;
......@@ -448,19 +452,10 @@ static int __init mchp_pit64b_dt_init_timer(struct device_node *node,
if (ret)
goto irq_unmap;
ret = clk_prepare_enable(timer.pclk);
if (ret)
goto irq_unmap;
if (timer.mode & MCHP_PIT64B_MR_SGCLK) {
ret = clk_prepare_enable(timer.gclk);
if (ret)
goto pclk_unprepare;
if (timer.mode & MCHP_PIT64B_MR_SGCLK)
clk_rate = clk_get_rate(timer.gclk);
} else {
else
clk_rate = clk_get_rate(timer.pclk);
}
clk_rate = clk_rate / (MCHP_PIT64B_MODE_TO_PRES(timer.mode) + 1);
if (clkevt)
......@@ -469,15 +464,10 @@ static int __init mchp_pit64b_dt_init_timer(struct device_node *node,
ret = mchp_pit64b_init_clksrc(&timer, clk_rate);
if (ret)
goto gclk_unprepare;
goto irq_unmap;
return 0;
gclk_unprepare:
if (timer.mode & MCHP_PIT64B_MR_SGCLK)
clk_disable_unprepare(timer.gclk);
pclk_unprepare:
clk_disable_unprepare(timer.pclk);
irq_unmap:
irq_dispose_mapping(irq);
io_unmap:
......
......@@ -128,7 +128,7 @@ static void sun4i_timer_clear_interrupt(void __iomem *base)
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
struct clock_event_device *evt = dev_id;
struct timer_of *to = to_timer_of(evt);
sun4i_timer_clear_interrupt(timer_of_base(to));
......
......@@ -142,7 +142,7 @@ static int sun5i_clkevt_next_event(unsigned long evt,
static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id)
{
struct sun5i_timer_clkevt *ce = (struct sun5i_timer_clkevt *)dev_id;
struct sun5i_timer_clkevt *ce = dev_id;
writel(0x1, ce->timer.base + TIMER_IRQ_ST_REG);
ce->clkevt.event_handler(&ce->clkevt);
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2020 NVIDIA Corporation. All rights reserved.
*/
#include <linux/clocksource.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/watchdog.h>
/* shared registers */
#define TKETSC0 0x000
#define TKETSC1 0x004
#define TKEUSEC 0x008
#define TKEOSC 0x00c
#define TKEIE(x) (0x100 + ((x) * 4))
#define TKEIE_WDT_MASK(x, y) ((y) << (16 + 4 * (x)))
/* timer registers */
#define TMRCR 0x000
#define TMRCR_ENABLE BIT(31)
#define TMRCR_PERIODIC BIT(30)
#define TMRCR_PTV(x) ((x) & 0x0fffffff)
#define TMRSR 0x004
#define TMRSR_INTR_CLR BIT(30)
#define TMRCSSR 0x008
#define TMRCSSR_SRC_USEC (0 << 0)
/* watchdog registers */
#define WDTCR 0x000
#define WDTCR_SYSTEM_POR_RESET_ENABLE BIT(16)
#define WDTCR_SYSTEM_DEBUG_RESET_ENABLE BIT(15)
#define WDTCR_REMOTE_INT_ENABLE BIT(14)
#define WDTCR_LOCAL_FIQ_ENABLE BIT(13)
#define WDTCR_LOCAL_INT_ENABLE BIT(12)
#define WDTCR_PERIOD_MASK (0xff << 4)
#define WDTCR_PERIOD(x) (((x) & 0xff) << 4)
#define WDTCR_TIMER_SOURCE_MASK 0xf
#define WDTCR_TIMER_SOURCE(x) ((x) & 0xf)
#define WDTCMDR 0x008
#define WDTCMDR_DISABLE_COUNTER BIT(1)
#define WDTCMDR_START_COUNTER BIT(0)
#define WDTUR 0x00c
#define WDTUR_UNLOCK_PATTERN 0x0000c45a
struct tegra186_timer_soc {
unsigned int num_timers;
unsigned int num_wdts;
};
struct tegra186_tmr {
struct tegra186_timer *parent;
void __iomem *regs;
unsigned int index;
unsigned int hwirq;
};
struct tegra186_wdt {
struct watchdog_device base;
void __iomem *regs;
unsigned int index;
bool locked;
struct tegra186_tmr *tmr;
};
static inline struct tegra186_wdt *to_tegra186_wdt(struct watchdog_device *wdd)
{
return container_of(wdd, struct tegra186_wdt, base);
}
struct tegra186_timer {
const struct tegra186_timer_soc *soc;
struct device *dev;
void __iomem *regs;
struct tegra186_wdt *wdt;
struct clocksource usec;
struct clocksource tsc;
struct clocksource osc;
};
static void tmr_writel(struct tegra186_tmr *tmr, u32 value, unsigned int offset)
{
writel_relaxed(value, tmr->regs + offset);
}
static void wdt_writel(struct tegra186_wdt *wdt, u32 value, unsigned int offset)
{
writel_relaxed(value, wdt->regs + offset);
}
static u32 wdt_readl(struct tegra186_wdt *wdt, unsigned int offset)
{
return readl_relaxed(wdt->regs + offset);
}
static struct tegra186_tmr *tegra186_tmr_create(struct tegra186_timer *tegra,
unsigned int index)
{
unsigned int offset = 0x10000 + index * 0x10000;
struct tegra186_tmr *tmr;
tmr = devm_kzalloc(tegra->dev, sizeof(*tmr), GFP_KERNEL);
if (!tmr)
return ERR_PTR(-ENOMEM);
tmr->parent = tegra;
tmr->regs = tegra->regs + offset;
tmr->index = index;
tmr->hwirq = 0;
return tmr;
}
static const struct watchdog_info tegra186_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.identity = "NVIDIA Tegra186 WDT",
};
static void tegra186_wdt_disable(struct tegra186_wdt *wdt)
{
/* unlock and disable the watchdog */
wdt_writel(wdt, WDTUR_UNLOCK_PATTERN, WDTUR);
wdt_writel(wdt, WDTCMDR_DISABLE_COUNTER, WDTCMDR);
/* disable timer */
tmr_writel(wdt->tmr, 0, TMRCR);
}
static void tegra186_wdt_enable(struct tegra186_wdt *wdt)
{
struct tegra186_timer *tegra = wdt->tmr->parent;
u32 value;
/* unmask hardware IRQ, this may have been lost across powergate */
value = TKEIE_WDT_MASK(wdt->index, 1);
writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq));
/* clear interrupt */
tmr_writel(wdt->tmr, TMRSR_INTR_CLR, TMRSR);
/* select microsecond source */
tmr_writel(wdt->tmr, TMRCSSR_SRC_USEC, TMRCSSR);
/* configure timer (system reset happens on the fifth expiration) */
value = TMRCR_PTV(wdt->base.timeout * USEC_PER_SEC / 5) |
TMRCR_PERIODIC | TMRCR_ENABLE;
tmr_writel(wdt->tmr, value, TMRCR);
if (!wdt->locked) {
value = wdt_readl(wdt, WDTCR);
/* select the proper timer source */
value &= ~WDTCR_TIMER_SOURCE_MASK;
value |= WDTCR_TIMER_SOURCE(wdt->tmr->index);
/* single timer period since that's already configured */
value &= ~WDTCR_PERIOD_MASK;
value |= WDTCR_PERIOD(1);
/* enable local interrupt for WDT petting */
value |= WDTCR_LOCAL_INT_ENABLE;
/* enable local FIQ and remote interrupt for debug dump */
if (0)
value |= WDTCR_REMOTE_INT_ENABLE |
WDTCR_LOCAL_FIQ_ENABLE;
/* enable system debug reset (doesn't properly reboot) */
if (0)
value |= WDTCR_SYSTEM_DEBUG_RESET_ENABLE;
/* enable system POR reset */
value |= WDTCR_SYSTEM_POR_RESET_ENABLE;
wdt_writel(wdt, value, WDTCR);
}
wdt_writel(wdt, WDTCMDR_START_COUNTER, WDTCMDR);
}
static int tegra186_wdt_start(struct watchdog_device *wdd)
{
struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
tegra186_wdt_enable(wdt);
return 0;
}
static int tegra186_wdt_stop(struct watchdog_device *wdd)
{
struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
tegra186_wdt_disable(wdt);
return 0;
}
static int tegra186_wdt_ping(struct watchdog_device *wdd)
{
struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
tegra186_wdt_disable(wdt);
tegra186_wdt_enable(wdt);
return 0;
}
static int tegra186_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
if (watchdog_active(&wdt->base))
tegra186_wdt_disable(wdt);
wdt->base.timeout = timeout;
if (watchdog_active(&wdt->base))
tegra186_wdt_enable(wdt);
return 0;
}
static const struct watchdog_ops tegra186_wdt_ops = {
.owner = THIS_MODULE,
.start = tegra186_wdt_start,
.stop = tegra186_wdt_stop,
.ping = tegra186_wdt_ping,
.set_timeout = tegra186_wdt_set_timeout,
};
static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra,
unsigned int index)
{
unsigned int offset = 0x10000, source;
struct tegra186_wdt *wdt;
u32 value;
int err;
offset += tegra->soc->num_timers * 0x10000 + index * 0x10000;
wdt = devm_kzalloc(tegra->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return ERR_PTR(-ENOMEM);
wdt->regs = tegra->regs + offset;
wdt->index = index;
/* read the watchdog configuration since it might be locked down */
value = wdt_readl(wdt, WDTCR);
if (value & WDTCR_LOCAL_INT_ENABLE)
wdt->locked = true;
source = value & WDTCR_TIMER_SOURCE_MASK;
wdt->tmr = tegra186_tmr_create(tegra, source);
if (IS_ERR(wdt->tmr))
return ERR_CAST(wdt->tmr);
wdt->base.info = &tegra186_wdt_info;
wdt->base.ops = &tegra186_wdt_ops;
wdt->base.min_timeout = 1;
wdt->base.max_timeout = 255;
wdt->base.parent = tegra->dev;
err = watchdog_init_timeout(&wdt->base, 5, tegra->dev);
if (err < 0) {
dev_err(tegra->dev, "failed to initialize timeout: %d\n", err);
return ERR_PTR(err);
}
err = devm_watchdog_register_device(tegra->dev, &wdt->base);
if (err < 0) {
dev_err(tegra->dev, "failed to register WDT: %d\n", err);
return ERR_PTR(err);
}
return wdt;
}
static u64 tegra186_timer_tsc_read(struct clocksource *cs)
{
struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,
tsc);
u32 hi, lo, ss;
hi = readl_relaxed(tegra->regs + TKETSC1);
/*
* The 56-bit value of the TSC is spread across two registers that are
* not synchronized. In order to read them atomically, ensure that the
* high 24 bits match before and after reading the low 32 bits.
*/
do {
/* snapshot the high 24 bits */
ss = hi;
lo = readl_relaxed(tegra->regs + TKETSC0);
hi = readl_relaxed(tegra->regs + TKETSC1);
} while (hi != ss);
return (u64)hi << 32 | lo;
}
static int tegra186_timer_tsc_init(struct tegra186_timer *tegra)
{
tegra->tsc.name = "tsc";
tegra->tsc.rating = 300;
tegra->tsc.read = tegra186_timer_tsc_read;
tegra->tsc.mask = CLOCKSOURCE_MASK(56);
tegra->tsc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
return clocksource_register_hz(&tegra->tsc, 31250000);
}
static u64 tegra186_timer_osc_read(struct clocksource *cs)
{
struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,
osc);
return readl_relaxed(tegra->regs + TKEOSC);
}
static int tegra186_timer_osc_init(struct tegra186_timer *tegra)
{
tegra->osc.name = "osc";
tegra->osc.rating = 300;
tegra->osc.read = tegra186_timer_osc_read;
tegra->osc.mask = CLOCKSOURCE_MASK(32);
tegra->osc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
return clocksource_register_hz(&tegra->osc, 38400000);
}
static u64 tegra186_timer_usec_read(struct clocksource *cs)
{
struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,
usec);
return readl_relaxed(tegra->regs + TKEUSEC);
}
static int tegra186_timer_usec_init(struct tegra186_timer *tegra)
{
tegra->usec.name = "usec";
tegra->usec.rating = 300;
tegra->usec.read = tegra186_timer_usec_read;
tegra->usec.mask = CLOCKSOURCE_MASK(32);
tegra->usec.flags = CLOCK_SOURCE_IS_CONTINUOUS;
return clocksource_register_hz(&tegra->usec, USEC_PER_SEC);
}
static irqreturn_t tegra186_timer_irq(int irq, void *data)
{
struct tegra186_timer *tegra = data;
if (watchdog_active(&tegra->wdt->base)) {
tegra186_wdt_disable(tegra->wdt);
tegra186_wdt_enable(tegra->wdt);
}
return IRQ_HANDLED;
}
static int tegra186_timer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra186_timer *tegra;
unsigned int irq;
int err;
tegra = devm_kzalloc(dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
tegra->soc = of_device_get_match_data(dev);
dev_set_drvdata(dev, tegra);
tegra->dev = dev;
tegra->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tegra->regs))
return PTR_ERR(tegra->regs);
err = platform_get_irq(pdev, 0);
if (err < 0)
return err;
irq = err;
/* create a watchdog using a preconfigured timer */
tegra->wdt = tegra186_wdt_create(tegra, 0);
if (IS_ERR(tegra->wdt)) {
err = PTR_ERR(tegra->wdt);
dev_err(dev, "failed to create WDT: %d\n", err);
return err;
}
err = tegra186_timer_tsc_init(tegra);
if (err < 0) {
dev_err(dev, "failed to register TSC counter: %d\n", err);
return err;
}
err = tegra186_timer_osc_init(tegra);
if (err < 0) {
dev_err(dev, "failed to register OSC counter: %d\n", err);
goto unregister_tsc;
}
err = tegra186_timer_usec_init(tegra);
if (err < 0) {
dev_err(dev, "failed to register USEC counter: %d\n", err);
goto unregister_osc;
}
err = devm_request_irq(dev, irq, tegra186_timer_irq, 0,
"tegra186-timer", tegra);
if (err < 0) {
dev_err(dev, "failed to request IRQ#%u: %d\n", irq, err);
goto unregister_usec;
}
return 0;
unregister_usec:
clocksource_unregister(&tegra->usec);
unregister_osc:
clocksource_unregister(&tegra->osc);
unregister_tsc:
clocksource_unregister(&tegra->tsc);
return err;
}
static int tegra186_timer_remove(struct platform_device *pdev)
{
struct tegra186_timer *tegra = platform_get_drvdata(pdev);
clocksource_unregister(&tegra->usec);
clocksource_unregister(&tegra->osc);
clocksource_unregister(&tegra->tsc);
return 0;
}
static int __maybe_unused tegra186_timer_suspend(struct device *dev)
{
struct tegra186_timer *tegra = dev_get_drvdata(dev);
if (watchdog_active(&tegra->wdt->base))
tegra186_wdt_disable(tegra->wdt);
return 0;
}
static int __maybe_unused tegra186_timer_resume(struct device *dev)
{
struct tegra186_timer *tegra = dev_get_drvdata(dev);
if (watchdog_active(&tegra->wdt->base))
tegra186_wdt_enable(tegra->wdt);
return 0;
}
static SIMPLE_DEV_PM_OPS(tegra186_timer_pm_ops, tegra186_timer_suspend,
tegra186_timer_resume);
static const struct tegra186_timer_soc tegra186_timer = {
.num_timers = 10,
.num_wdts = 3,
};
static const struct tegra186_timer_soc tegra234_timer = {
.num_timers = 16,
.num_wdts = 3,
};
static const struct of_device_id tegra186_timer_of_match[] = {
{ .compatible = "nvidia,tegra186-timer", .data = &tegra186_timer },
{ .compatible = "nvidia,tegra234-timer", .data = &tegra234_timer },
{ }
};
MODULE_DEVICE_TABLE(of, tegra186_timer_of_match);
static struct platform_driver tegra186_wdt_driver = {
.driver = {
.name = "tegra186-timer",
.pm = &tegra186_timer_pm_ops,
.of_match_table = tegra186_timer_of_match,
},
.probe = tegra186_timer_probe,
.remove = tegra186_timer_remove,
};
module_platform_driver(tegra186_wdt_driver);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra186 timers driver");
MODULE_LICENSE("GPL v2");
......@@ -44,6 +44,121 @@ enum {
REQUEST_BY_NODE,
};
static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg,
int posted)
{
if (posted)
while (readl_relaxed(timer->pend) & (reg >> WPSHIFT))
cpu_relax();
return readl_relaxed(timer->func_base + (reg & 0xff));
}
static inline void __omap_dm_timer_write(struct omap_dm_timer *timer,
u32 reg, u32 val, int posted)
{
if (posted)
while (readl_relaxed(timer->pend) & (reg >> WPSHIFT))
cpu_relax();
writel_relaxed(val, timer->func_base + (reg & 0xff));
}
static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
{
u32 tidr;
/* Assume v1 ip if bits [31:16] are zero */
tidr = readl_relaxed(timer->io_base);
if (!(tidr >> 16)) {
timer->revision = 1;
timer->irq_stat = timer->io_base + OMAP_TIMER_V1_STAT_OFFSET;
timer->irq_ena = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET;
timer->irq_dis = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET;
timer->pend = timer->io_base + _OMAP_TIMER_WRITE_PEND_OFFSET;
timer->func_base = timer->io_base;
} else {
timer->revision = 2;
timer->irq_stat = timer->io_base + OMAP_TIMER_V2_IRQSTATUS;
timer->irq_ena = timer->io_base + OMAP_TIMER_V2_IRQENABLE_SET;
timer->irq_dis = timer->io_base + OMAP_TIMER_V2_IRQENABLE_CLR;
timer->pend = timer->io_base +
_OMAP_TIMER_WRITE_PEND_OFFSET +
OMAP_TIMER_V2_FUNC_OFFSET;
timer->func_base = timer->io_base + OMAP_TIMER_V2_FUNC_OFFSET;
}
}
/*
* __omap_dm_timer_enable_posted - enables write posted mode
* @timer: pointer to timer instance handle
*
* Enables the write posted mode for the timer. When posted mode is enabled
* writes to certain timer registers are immediately acknowledged by the
* internal bus and hence prevents stalling the CPU waiting for the write to
* complete. Enabling this feature can improve performance for writing to the
* timer registers.
*/
static inline void __omap_dm_timer_enable_posted(struct omap_dm_timer *timer)
{
if (timer->posted)
return;
if (timer->errata & OMAP_TIMER_ERRATA_I103_I767) {
timer->posted = OMAP_TIMER_NONPOSTED;
__omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0, 0);
return;
}
__omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG,
OMAP_TIMER_CTRL_POSTED, 0);
timer->context.tsicr = OMAP_TIMER_CTRL_POSTED;
timer->posted = OMAP_TIMER_POSTED;
}
static inline void __omap_dm_timer_stop(struct omap_dm_timer *timer,
int posted, unsigned long rate)
{
u32 l;
l = __omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
if (l & OMAP_TIMER_CTRL_ST) {
l &= ~0x1;
__omap_dm_timer_write(timer, OMAP_TIMER_CTRL_REG, l, posted);
#ifdef CONFIG_ARCH_OMAP2PLUS
/* Readback to make sure write has completed */
__omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
/*
* Wait for functional clock period x 3.5 to make sure that
* timer is stopped
*/
udelay(3500000 / rate + 1);
#endif
}
/* Ack possibly pending interrupt */
writel_relaxed(OMAP_TIMER_INT_OVERFLOW, timer->irq_stat);
}
static inline void __omap_dm_timer_int_enable(struct omap_dm_timer *timer,
unsigned int value)
{
writel_relaxed(value, timer->irq_ena);
__omap_dm_timer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, value, 0);
}
static inline unsigned int
__omap_dm_timer_read_counter(struct omap_dm_timer *timer, int posted)
{
return __omap_dm_timer_read(timer, OMAP_TIMER_COUNTER_REG, posted);
}
static inline void __omap_dm_timer_write_status(struct omap_dm_timer *timer,
unsigned int value)
{
writel_relaxed(value, timer->irq_stat);
}
/**
* omap_dm_timer_read_reg - read timer registers in posted and non-posted mode
* @timer: timer pointer over which read operation to perform
......@@ -921,6 +1036,10 @@ static const struct dmtimer_platform_data omap3plus_pdata = {
.timer_ops = &dmtimer_ops,
};
static const struct dmtimer_platform_data am6_pdata = {
.timer_ops = &dmtimer_ops,
};
static const struct of_device_id omap_timer_match[] = {
{
.compatible = "ti,omap2420-timer",
......@@ -949,6 +1068,10 @@ static const struct of_device_id omap_timer_match[] = {
.compatible = "ti,dm816-timer",
.data = &omap3plus_pdata,
},
{
.compatible = "ti,am654-timer",
.data = &am6_pdata,
},
{},
};
MODULE_DEVICE_TABLE(of, omap_timer_match);
......
......@@ -399,6 +399,10 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
.compatible = "renesas,r8a779a0-thermal",
.data = &rcar_gen3_ths_tj_1,
},
{
.compatible = "renesas,r8a779f0-thermal",
.data = &rcar_gen3_ths_tj_1,
},
{},
};
MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
......
......@@ -247,148 +247,4 @@ int omap_dm_timers_active(void);
#define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \
(_OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | (WP_TOWR << WPSHIFT))
/*
* The below are inlined to optimize code size for system timers. Other code
* should not need these at all.
*/
#if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2PLUS)
static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg,
int posted)
{
if (posted)
while (readl_relaxed(timer->pend) & (reg >> WPSHIFT))
cpu_relax();
return readl_relaxed(timer->func_base + (reg & 0xff));
}
static inline void __omap_dm_timer_write(struct omap_dm_timer *timer,
u32 reg, u32 val, int posted)
{
if (posted)
while (readl_relaxed(timer->pend) & (reg >> WPSHIFT))
cpu_relax();
writel_relaxed(val, timer->func_base + (reg & 0xff));
}
static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
{
u32 tidr;
/* Assume v1 ip if bits [31:16] are zero */
tidr = readl_relaxed(timer->io_base);
if (!(tidr >> 16)) {
timer->revision = 1;
timer->irq_stat = timer->io_base + OMAP_TIMER_V1_STAT_OFFSET;
timer->irq_ena = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET;
timer->irq_dis = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET;
timer->pend = timer->io_base + _OMAP_TIMER_WRITE_PEND_OFFSET;
timer->func_base = timer->io_base;
} else {
timer->revision = 2;
timer->irq_stat = timer->io_base + OMAP_TIMER_V2_IRQSTATUS;
timer->irq_ena = timer->io_base + OMAP_TIMER_V2_IRQENABLE_SET;
timer->irq_dis = timer->io_base + OMAP_TIMER_V2_IRQENABLE_CLR;
timer->pend = timer->io_base +
_OMAP_TIMER_WRITE_PEND_OFFSET +
OMAP_TIMER_V2_FUNC_OFFSET;
timer->func_base = timer->io_base + OMAP_TIMER_V2_FUNC_OFFSET;
}
}
/*
* __omap_dm_timer_enable_posted - enables write posted mode
* @timer: pointer to timer instance handle
*
* Enables the write posted mode for the timer. When posted mode is enabled
* writes to certain timer registers are immediately acknowledged by the
* internal bus and hence prevents stalling the CPU waiting for the write to
* complete. Enabling this feature can improve performance for writing to the
* timer registers.
*/
static inline void __omap_dm_timer_enable_posted(struct omap_dm_timer *timer)
{
if (timer->posted)
return;
if (timer->errata & OMAP_TIMER_ERRATA_I103_I767) {
timer->posted = OMAP_TIMER_NONPOSTED;
__omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0, 0);
return;
}
__omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG,
OMAP_TIMER_CTRL_POSTED, 0);
timer->context.tsicr = OMAP_TIMER_CTRL_POSTED;
timer->posted = OMAP_TIMER_POSTED;
}
/**
* __omap_dm_timer_override_errata - override errata flags for a timer
* @timer: pointer to timer handle
* @errata: errata flags to be ignored
*
* For a given timer, override a timer errata by clearing the flags
* specified by the errata argument. A specific erratum should only be
* overridden for a timer if the timer is used in such a way the erratum
* has no impact.
*/
static inline void __omap_dm_timer_override_errata(struct omap_dm_timer *timer,
u32 errata)
{
timer->errata &= ~errata;
}
static inline void __omap_dm_timer_stop(struct omap_dm_timer *timer,
int posted, unsigned long rate)
{
u32 l;
l = __omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
if (l & OMAP_TIMER_CTRL_ST) {
l &= ~0x1;
__omap_dm_timer_write(timer, OMAP_TIMER_CTRL_REG, l, posted);
#ifdef CONFIG_ARCH_OMAP2PLUS
/* Readback to make sure write has completed */
__omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted);
/*
* Wait for functional clock period x 3.5 to make sure that
* timer is stopped
*/
udelay(3500000 / rate + 1);
#endif
}
/* Ack possibly pending interrupt */
writel_relaxed(OMAP_TIMER_INT_OVERFLOW, timer->irq_stat);
}
static inline void __omap_dm_timer_load_start(struct omap_dm_timer *timer,
u32 ctrl, unsigned int load,
int posted)
{
__omap_dm_timer_write(timer, OMAP_TIMER_COUNTER_REG, load, posted);
__omap_dm_timer_write(timer, OMAP_TIMER_CTRL_REG, ctrl, posted);
}
static inline void __omap_dm_timer_int_enable(struct omap_dm_timer *timer,
unsigned int value)
{
writel_relaxed(value, timer->irq_ena);
__omap_dm_timer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, value, 0);
}
static inline unsigned int
__omap_dm_timer_read_counter(struct omap_dm_timer *timer, int posted)
{
return __omap_dm_timer_read(timer, OMAP_TIMER_COUNTER_REG, posted);
}
static inline void __omap_dm_timer_write_status(struct omap_dm_timer *timer,
unsigned int value)
{
writel_relaxed(value, timer->irq_stat);
}
#endif /* CONFIG_ARCH_OMAP1 || CONFIG_ARCH_OMAP2PLUS */
#endif /* __CLOCKSOURCE_DMTIMER_H */
......@@ -544,10 +544,11 @@ do { \
\
hrtimer_init_sleeper_on_stack(&__t, CLOCK_MONOTONIC, \
HRTIMER_MODE_REL); \
if ((timeout) != KTIME_MAX) \
hrtimer_start_range_ns(&__t.timer, timeout, \
current->timer_slack_ns, \
HRTIMER_MODE_REL); \
if ((timeout) != KTIME_MAX) { \
hrtimer_set_expires_range_ns(&__t.timer, timeout, \
current->timer_slack_ns); \
hrtimer_sleeper_start_expires(&__t, HRTIMER_MODE_REL); \
} \
\
__ret = ___wait_event(wq_head, condition, state, 0, 0, \
if (!__t.task) { \
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册