Commit d2b32be7 authored by Thomas Gleixner's avatar Thomas Gleixner

Merge tag 'timers-v6.5-rc1' of https://git.linaro.org/people/daniel.lezcano/linux into timers/core

Pull clockevent/source updates from Daniel Lezcano:

  - Fix memory leak on Cadence TTC at probe time (Feng Mingxi)

  - Use the pm_sleep_ptr macro for the Ingenic driver (Paul Cercueil)

  - Relocate the PMW timer Loongson from the mips arch directory to the
    drivers/clocksource (Keguang Zhang)

  - Use the same function names instead of using aliases and move data
    defined in the header to the driver directly as this one is the only
    user of the header file and remove this one on i.MX GPT (Uwe
    Kleine-König)

  - Convert Broadcom Kona family timer bindings to DT schema (Michael
     Kelley)

  - Add DT bindings for Ralink SoCs timer (Sergio Paracuellos)
parents ccaa4926 8b5bf64c
Broadcom Kona Family timer
-----------------------------------------------------
This timer is used in the following Broadcom SoCs:
BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
Required properties:
- compatible : "brcm,kona-timer"
- DEPRECATED: compatible : "bcm,kona-timer"
- reg : Register range for the timer
- interrupts : interrupt for the timer
- clocks: phandle + clock specifier pair of the external clock
- clock-frequency: frequency that the clock operates
Only one of clocks or clock-frequency should be specified.
Refer to clocks/clock-bindings.txt for generic clock consumer properties.
Example:
timer@35006000 {
compatible = "brcm,kona-timer";
reg = <0x35006000 0x1000>;
interrupts = <0x0 7 0x4>;
clocks = <&hub_timer_clk>;
};
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/brcm,kona-timer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom Kona family timer
maintainers:
- Florian Fainelli <f.fainelli@gmail.com>
properties:
compatible:
const: brcm,kona-timer
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-frequency: true
oneOf:
- required:
- clocks
- required:
- clock-frequency
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/bcm281xx.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
timer@35006000 {
compatible = "brcm,kona-timer";
reg = <0x35006000 0x1000>;
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&aon_ccu BCM281XX_AON_CCU_HUB_TIMER>;
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/loongson,ls1x-pwmtimer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Loongson-1 PWM timer
maintainers:
- Keguang Zhang <keguang.zhang@gmail.com>
description:
Loongson-1 PWM timer can be used for system clock source
and clock event timers.
properties:
compatible:
const: loongson,ls1b-pwmtimer
reg:
maxItems: 1
clocks:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- clocks
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/loongson,ls1x-clk.h>
#include <dt-bindings/interrupt-controller/irq.h>
clocksource: timer@1fe5c030 {
compatible = "loongson,ls1b-pwmtimer";
reg = <0x1fe5c030 0x10>;
clocks = <&clkc LS1X_CLKID_APB>;
interrupt-parent = <&intc0>;
interrupts = <20 IRQ_TYPE_LEVEL_HIGH>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/ralink,rt2880-timer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Timer present in Ralink family SoCs
maintainers:
- Sergio Paracuellos <sergio.paracuellos@gmail.com>
properties:
compatible:
const: ralink,rt2880-timer
reg:
maxItems: 1
clocks:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- clocks
- interrupts
additionalProperties: false
examples:
- |
timer@100 {
compatible = "ralink,rt2880-timer";
reg = <0x100 0x20>;
clocks = <&sysc 3>;
interrupt-parent = <&intc>;
interrupts = <1>;
};
...
......@@ -47,7 +47,6 @@
#include <regs-clk.h>
#include <regs-mux.h>
#include <regs-pwm.h>
#include <regs-rtc.h>
#include <regs-wdt.h>
......
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2014 Zhang, Keguang <keguang.zhang@gmail.com>
*
* Loongson 1 PWM Register Definitions.
*/
#ifndef __ASM_MACH_LOONGSON32_REGS_PWM_H
#define __ASM_MACH_LOONGSON32_REGS_PWM_H
/* Loongson 1 PWM Timer Register Definitions */
#define PWM_CNT 0x0
#define PWM_HRC 0x4
#define PWM_LRC 0x8
#define PWM_CTRL 0xc
/* PWM Control Register Bits */
#define CNT_RST BIT(7)
#define INT_SR BIT(6)
#define INT_EN BIT(5)
#define PWM_SINGLE BIT(4)
#define PWM_OE BIT(3)
#define CNT_EN BIT(0)
#endif /* __ASM_MACH_LOONGSON32_REGS_PWM_H */
......@@ -35,41 +35,4 @@ config LOONGSON1_LS1C
select COMMON_CLK
endchoice
menuconfig CEVT_CSRC_LS1X
bool "Use PWM Timer for clockevent/clocksource"
select MIPS_EXTERNAL_TIMER
depends on CPU_LOONGSON32
help
This option changes the default clockevent/clocksource to PWM Timer,
and is required by Loongson1 CPUFreq support.
If unsure, say N.
choice
prompt "Select clockevent/clocksource"
depends on CEVT_CSRC_LS1X
default TIMER_USE_PWM0
config TIMER_USE_PWM0
bool "Use PWM Timer 0"
help
Use PWM Timer 0 as the default clockevent/clocksourcer.
config TIMER_USE_PWM1
bool "Use PWM Timer 1"
help
Use PWM Timer 1 as the default clockevent/clocksourcer.
config TIMER_USE_PWM2
bool "Use PWM Timer 2"
help
Use PWM Timer 2 as the default clockevent/clocksourcer.
config TIMER_USE_PWM3
bool "Use PWM Timer 3"
help
Use PWM Timer 3 as the default clockevent/clocksourcer.
endchoice
endif # MACH_LOONGSON32
......@@ -5,208 +5,8 @@
#include <linux/clk.h>
#include <linux/of_clk.h>
#include <linux/interrupt.h>
#include <linux/sizes.h>
#include <asm/time.h>
#include <loongson1.h>
#include <platform.h>
#ifdef CONFIG_CEVT_CSRC_LS1X
#if defined(CONFIG_TIMER_USE_PWM1)
#define LS1X_TIMER_BASE LS1X_PWM1_BASE
#define LS1X_TIMER_IRQ LS1X_PWM1_IRQ
#elif defined(CONFIG_TIMER_USE_PWM2)
#define LS1X_TIMER_BASE LS1X_PWM2_BASE
#define LS1X_TIMER_IRQ LS1X_PWM2_IRQ
#elif defined(CONFIG_TIMER_USE_PWM3)
#define LS1X_TIMER_BASE LS1X_PWM3_BASE
#define LS1X_TIMER_IRQ LS1X_PWM3_IRQ
#else
#define LS1X_TIMER_BASE LS1X_PWM0_BASE
#define LS1X_TIMER_IRQ LS1X_PWM0_IRQ
#endif
DEFINE_RAW_SPINLOCK(ls1x_timer_lock);
static void __iomem *timer_reg_base;
static uint32_t ls1x_jiffies_per_tick;
static inline void ls1x_pwmtimer_set_period(uint32_t period)
{
__raw_writel(period, timer_reg_base + PWM_HRC);
__raw_writel(period, timer_reg_base + PWM_LRC);
}
static inline void ls1x_pwmtimer_restart(void)
{
__raw_writel(0x0, timer_reg_base + PWM_CNT);
__raw_writel(INT_EN | CNT_EN, timer_reg_base + PWM_CTRL);
}
void __init ls1x_pwmtimer_init(void)
{
timer_reg_base = ioremap(LS1X_TIMER_BASE, SZ_16);
if (!timer_reg_base)
panic("Failed to remap timer registers");
ls1x_jiffies_per_tick = DIV_ROUND_CLOSEST(mips_hpt_frequency, HZ);
ls1x_pwmtimer_set_period(ls1x_jiffies_per_tick);
ls1x_pwmtimer_restart();
}
static u64 ls1x_clocksource_read(struct clocksource *cs)
{
unsigned long flags;
int count;
u32 jifs;
static int old_count;
static u32 old_jifs;
raw_spin_lock_irqsave(&ls1x_timer_lock, flags);
/*
* Although our caller may have the read side of xtime_lock,
* this is now a seqlock, and we are cheating in this routine
* by having side effects on state that we cannot undo if
* there is a collision on the seqlock and our caller has to
* retry. (Namely, old_jifs and old_count.) So we must treat
* jiffies as volatile despite the lock. We read jiffies
* before latching the timer count to guarantee that although
* the jiffies value might be older than the count (that is,
* the counter may underflow between the last point where
* jiffies was incremented and the point where we latch the
* count), it cannot be newer.
*/
jifs = jiffies;
/* read the count */
count = __raw_readl(timer_reg_base + PWM_CNT);
/*
* It's possible for count to appear to go the wrong way for this
* reason:
*
* The timer counter underflows, but we haven't handled the resulting
* interrupt and incremented jiffies yet.
*
* Previous attempts to handle these cases intelligently were buggy, so
* we just do the simple thing now.
*/
if (count < old_count && jifs == old_jifs)
count = old_count;
old_count = count;
old_jifs = jifs;
raw_spin_unlock_irqrestore(&ls1x_timer_lock, flags);
return (u64) (jifs * ls1x_jiffies_per_tick) + count;
}
static struct clocksource ls1x_clocksource = {
.name = "ls1x-pwmtimer",
.read = ls1x_clocksource_read,
.mask = CLOCKSOURCE_MASK(24),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static irqreturn_t ls1x_clockevent_isr(int irq, void *devid)
{
struct clock_event_device *cd = devid;
ls1x_pwmtimer_restart();
cd->event_handler(cd);
return IRQ_HANDLED;
}
static int ls1x_clockevent_set_state_periodic(struct clock_event_device *cd)
{
raw_spin_lock(&ls1x_timer_lock);
ls1x_pwmtimer_set_period(ls1x_jiffies_per_tick);
ls1x_pwmtimer_restart();
__raw_writel(INT_EN | CNT_EN, timer_reg_base + PWM_CTRL);
raw_spin_unlock(&ls1x_timer_lock);
return 0;
}
static int ls1x_clockevent_tick_resume(struct clock_event_device *cd)
{
raw_spin_lock(&ls1x_timer_lock);
__raw_writel(INT_EN | CNT_EN, timer_reg_base + PWM_CTRL);
raw_spin_unlock(&ls1x_timer_lock);
return 0;
}
static int ls1x_clockevent_set_state_shutdown(struct clock_event_device *cd)
{
raw_spin_lock(&ls1x_timer_lock);
__raw_writel(__raw_readl(timer_reg_base + PWM_CTRL) & ~CNT_EN,
timer_reg_base + PWM_CTRL);
raw_spin_unlock(&ls1x_timer_lock);
return 0;
}
static int ls1x_clockevent_set_next(unsigned long evt,
struct clock_event_device *cd)
{
raw_spin_lock(&ls1x_timer_lock);
ls1x_pwmtimer_set_period(evt);
ls1x_pwmtimer_restart();
raw_spin_unlock(&ls1x_timer_lock);
return 0;
}
static struct clock_event_device ls1x_clockevent = {
.name = "ls1x-pwmtimer",
.features = CLOCK_EVT_FEAT_PERIODIC,
.rating = 300,
.irq = LS1X_TIMER_IRQ,
.set_next_event = ls1x_clockevent_set_next,
.set_state_shutdown = ls1x_clockevent_set_state_shutdown,
.set_state_periodic = ls1x_clockevent_set_state_periodic,
.set_state_oneshot = ls1x_clockevent_set_state_shutdown,
.tick_resume = ls1x_clockevent_tick_resume,
};
static void __init ls1x_time_init(void)
{
struct clock_event_device *cd = &ls1x_clockevent;
int ret;
if (!mips_hpt_frequency)
panic("Invalid timer clock rate");
ls1x_pwmtimer_init();
clockevent_set_clock(cd, mips_hpt_frequency);
cd->max_delta_ns = clockevent_delta2ns(0xffffff, cd);
cd->max_delta_ticks = 0xffffff;
cd->min_delta_ns = clockevent_delta2ns(0x000300, cd);
cd->min_delta_ticks = 0x000300;
cd->cpumask = cpumask_of(smp_processor_id());
clockevents_register_device(cd);
ls1x_clocksource.rating = 200 + mips_hpt_frequency / 10000000;
ret = clocksource_register_hz(&ls1x_clocksource, mips_hpt_frequency);
if (ret)
panic(KERN_ERR "Failed to register clocksource: %d\n", ret);
if (request_irq(LS1X_TIMER_IRQ, ls1x_clockevent_isr,
IRQF_PERCPU | IRQF_TIMER, "ls1x-pwmtimer",
&ls1x_clockevent))
pr_err("Failed to register ls1x-pwmtimer interrupt\n");
}
#endif /* CONFIG_CEVT_CSRC_LS1X */
void __init plat_time_init(void)
{
struct clk *clk = NULL;
......@@ -214,20 +14,10 @@ void __init plat_time_init(void)
/* initialize LS1X clocks */
of_clk_init(NULL);
#ifdef CONFIG_CEVT_CSRC_LS1X
/* setup LS1X PWM timer */
clk = clk_get(NULL, "ls1x-pwmtimer");
if (IS_ERR(clk))
panic("unable to get timer clock, err=%ld", PTR_ERR(clk));
mips_hpt_frequency = clk_get_rate(clk);
ls1x_time_init();
#else
/* setup mips r4k timer */
clk = clk_get(NULL, "cpu_clk");
if (IS_ERR(clk))
panic("unable to get cpu clock, err=%ld", PTR_ERR(clk));
mips_hpt_frequency = clk_get_rate(clk) / 2;
#endif /* CONFIG_CEVT_CSRC_LS1X */
}
......@@ -10,7 +10,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <dt-bindings/clock/imx1-clock.h>
#include <soc/imx/timer.h>
#include <asm/irq.h>
#include "clk.h"
......
......@@ -8,7 +8,6 @@
#include <linux/of_address.h>
#include <dt-bindings/clock/imx27-clock.h>
#include <soc/imx/revision.h>
#include <soc/imx/timer.h>
#include <asm/irq.h>
#include "clk.h"
......
......@@ -11,7 +11,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <soc/imx/revision.h>
#include <soc/imx/timer.h>
#include <asm/irq.h>
#include "clk.h"
......
......@@ -10,7 +10,6 @@
#include <linux/of.h>
#include <linux/err.h>
#include <soc/imx/revision.h>
#include <soc/imx/timer.h>
#include <asm/irq.h>
#include "clk.h"
......
......@@ -612,6 +612,15 @@ config TIMER_IMX_SYS_CTR
Enable this option to use i.MX system counter timer as a
clockevent.
config CLKSRC_LOONGSON1_PWM
bool "Clocksource using Loongson1 PWM"
depends on MACH_LOONGSON32 || COMPILE_TEST
select MIPS_EXTERNAL_TIMER
select TIMER_OF
help
Enable this option to use Loongson1 PWM timer as clocksource
instead of the performance counter.
config CLKSRC_ST_LPC
bool "Low power clocksource found in the LPC" if COMPILE_TEST
select TIMER_OF if OF
......
......@@ -89,3 +89,4 @@ obj-$(CONFIG_MICROCHIP_PIT64B) += timer-microchip-pit64b.o
obj-$(CONFIG_MSC313E_TIMER) += timer-msc313e.o
obj-$(CONFIG_GOLDFISH_TIMER) += timer-goldfish.o
obj-$(CONFIG_GXP_TIMER) += timer-gxp.o
obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o
......@@ -475,15 +475,9 @@ static u64 notrace read_hv_clock_msr_cs(struct clocksource *arg)
return read_hv_clock_msr();
}
static u64 notrace read_hv_sched_clock_msr(void)
{
return (read_hv_clock_msr() - hv_sched_clock_offset) *
(NSEC_PER_SEC / HV_CLOCK_HZ);
}
static struct clocksource hyperv_cs_msr = {
.name = "hyperv_clocksource_msr",
.rating = 500,
.rating = 495,
.read = read_hv_clock_msr_cs,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
......@@ -513,7 +507,7 @@ static __always_inline void hv_setup_sched_clock(void *sched_clock)
static __always_inline void hv_setup_sched_clock(void *sched_clock) {}
#endif /* CONFIG_GENERIC_SCHED_CLOCK */
static bool __init hv_init_tsc_clocksource(void)
static void __init hv_init_tsc_clocksource(void)
{
union hv_reference_tsc_msr tsc_msr;
......@@ -524,17 +518,14 @@ static bool __init hv_init_tsc_clocksource(void)
* Hyper-V Reference TSC rating, causing the generic TSC to be used.
* TSC_INVARIANT is not offered on ARM64, so the Hyper-V Reference
* TSC will be preferred over the virtualized ARM64 arch counter.
* While the Hyper-V MSR clocksource won't be used since the
* Reference TSC clocksource is present, change its rating as
* well for consistency.
*/
if (ms_hyperv.features & HV_ACCESS_TSC_INVARIANT) {
hyperv_cs_tsc.rating = 250;
hyperv_cs_msr.rating = 250;
hyperv_cs_msr.rating = 245;
}
if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
return false;
return;
hv_read_reference_counter = read_hv_clock_tsc;
......@@ -565,33 +556,34 @@ static bool __init hv_init_tsc_clocksource(void)
clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100);
/*
* If TSC is invariant, then let it stay as the sched clock since it
* will be faster than reading the TSC page. But if not invariant, use
* the TSC page so that live migrations across hosts with different
* frequencies is handled correctly.
*/
if (!(ms_hyperv.features & HV_ACCESS_TSC_INVARIANT)) {
hv_sched_clock_offset = hv_read_reference_counter();
hv_setup_sched_clock(read_hv_sched_clock_tsc);
return true;
}
}
void __init hv_init_clocksource(void)
{
/*
* Try to set up the TSC page clocksource. If it succeeds, we're
* done. Otherwise, set up the MSR clocksource. At least one of
* these will always be available except on very old versions of
* Hyper-V on x86. In that case we won't have a Hyper-V
* Try to set up the TSC page clocksource, then the MSR clocksource.
* At least one of these will always be available except on very old
* versions of Hyper-V on x86. In that case we won't have a Hyper-V
* clocksource, but Linux will still run with a clocksource based
* on the emulated PIT or LAPIC timer.
*
* Never use the MSR clocksource as sched clock. It's too slow.
* Better to use the native sched clock as the fallback.
*/
if (hv_init_tsc_clocksource())
return;
hv_init_tsc_clocksource();
if (!(ms_hyperv.features & HV_MSR_TIME_REF_COUNT_AVAILABLE))
return;
hv_read_reference_counter = read_hv_clock_msr;
if (ms_hyperv.features & HV_MSR_TIME_REF_COUNT_AVAILABLE)
clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100);
hv_sched_clock_offset = hv_read_reference_counter();
hv_setup_sched_clock(read_hv_sched_clock_msr);
}
void __init hv_remap_tsc_clocksource(void)
......
......@@ -369,7 +369,7 @@ static int __init ingenic_tcu_probe(struct platform_device *pdev)
return 0;
}
static int __maybe_unused ingenic_tcu_suspend(struct device *dev)
static int ingenic_tcu_suspend(struct device *dev)
{
struct ingenic_tcu *tcu = dev_get_drvdata(dev);
unsigned int cpu;
......@@ -382,7 +382,7 @@ static int __maybe_unused ingenic_tcu_suspend(struct device *dev)
return 0;
}
static int __maybe_unused ingenic_tcu_resume(struct device *dev)
static int ingenic_tcu_resume(struct device *dev)
{
struct ingenic_tcu *tcu = dev_get_drvdata(dev);
unsigned int cpu;
......@@ -406,7 +406,7 @@ static int __maybe_unused ingenic_tcu_resume(struct device *dev)
return ret;
}
static const struct dev_pm_ops __maybe_unused ingenic_tcu_pm_ops = {
static const struct dev_pm_ops ingenic_tcu_pm_ops = {
/* _noirq: We want the TCU clocks to be gated last / ungated first */
.suspend_noirq = ingenic_tcu_suspend,
.resume_noirq = ingenic_tcu_resume,
......@@ -415,9 +415,7 @@ static const struct dev_pm_ops __maybe_unused ingenic_tcu_pm_ops = {
static struct platform_driver ingenic_tcu_driver = {
.driver = {
.name = "ingenic-tcu-timer",
#ifdef CONFIG_PM_SLEEP
.pm = &ingenic_tcu_pm_ops,
#endif
.pm = pm_sleep_ptr(&ingenic_tcu_pm_ops),
.of_match_table = ingenic_tcu_of_match,
},
};
......
......@@ -486,10 +486,10 @@ static int __init ttc_timer_probe(struct platform_device *pdev)
* and use it. Note that the event timer uses the interrupt and it's the
* 2nd TTC hence the irq_of_parse_and_map(,1)
*/
timer_baseaddr = of_iomap(timer, 0);
if (!timer_baseaddr) {
timer_baseaddr = devm_of_iomap(&pdev->dev, timer, 0, NULL);
if (IS_ERR(timer_baseaddr)) {
pr_err("ERROR: invalid timer base address\n");
return -ENXIO;
return PTR_ERR(timer_baseaddr);
}
irq = irq_of_parse_and_map(timer, 1);
......@@ -513,20 +513,27 @@ static int __init ttc_timer_probe(struct platform_device *pdev)
clk_ce = of_clk_get(timer, clksel);
if (IS_ERR(clk_ce)) {
pr_err("ERROR: timer input clock not found\n");
return PTR_ERR(clk_ce);
ret = PTR_ERR(clk_ce);
goto put_clk_cs;
}
ret = ttc_setup_clocksource(clk_cs, timer_baseaddr, timer_width);
if (ret)
return ret;
goto put_clk_ce;
ret = ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq);
if (ret)
return ret;
goto put_clk_ce;
pr_info("%pOFn #0 at %p, irq=%d\n", timer, timer_baseaddr, irq);
return 0;
put_clk_ce:
clk_put(clk_ce);
put_clk_cs:
clk_put(clk_cs);
return ret;
}
static const struct of_device_id ttc_timer_of_match[] = {
......
......@@ -16,7 +16,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <soc/imx/timer.h>
/*
* There are 4 versions of the timer hardware on Freescale MXC hardware.
......@@ -25,6 +24,12 @@
* - MX25, MX31, MX35, MX37, MX51, MX6Q(rev1.0)
* - MX6DL, MX6SX, MX6Q(rev1.1+)
*/
enum imx_gpt_type {
GPT_TYPE_IMX1, /* i.MX1 */
GPT_TYPE_IMX21, /* i.MX21/27 */
GPT_TYPE_IMX31, /* i.MX31/35/25/37/51/6Q */
GPT_TYPE_IMX6DL, /* i.MX6DL/SX/SL */
};
/* defines common for all i.MX */
#define MXC_TCTL 0x00
......@@ -93,13 +98,11 @@ static void imx1_gpt_irq_disable(struct imx_timer *imxtm)
tmp = readl_relaxed(imxtm->base + MXC_TCTL);
writel_relaxed(tmp & ~MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
}
#define imx21_gpt_irq_disable imx1_gpt_irq_disable
static void imx31_gpt_irq_disable(struct imx_timer *imxtm)
{
writel_relaxed(0, imxtm->base + V2_IR);
}
#define imx6dl_gpt_irq_disable imx31_gpt_irq_disable
static void imx1_gpt_irq_enable(struct imx_timer *imxtm)
{
......@@ -108,13 +111,11 @@ static void imx1_gpt_irq_enable(struct imx_timer *imxtm)
tmp = readl_relaxed(imxtm->base + MXC_TCTL);
writel_relaxed(tmp | MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
}
#define imx21_gpt_irq_enable imx1_gpt_irq_enable
static void imx31_gpt_irq_enable(struct imx_timer *imxtm)
{
writel_relaxed(1<<0, imxtm->base + V2_IR);
}
#define imx6dl_gpt_irq_enable imx31_gpt_irq_enable
static void imx1_gpt_irq_acknowledge(struct imx_timer *imxtm)
{
......@@ -131,7 +132,6 @@ static void imx31_gpt_irq_acknowledge(struct imx_timer *imxtm)
{
writel_relaxed(V2_TSTAT_OF1, imxtm->base + V2_TSTAT);
}
#define imx6dl_gpt_irq_acknowledge imx31_gpt_irq_acknowledge
static void __iomem *sched_clock_reg;
......@@ -296,7 +296,6 @@ static void imx1_gpt_setup_tctl(struct imx_timer *imxtm)
tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN;
writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
}
#define imx21_gpt_setup_tctl imx1_gpt_setup_tctl
static void imx31_gpt_setup_tctl(struct imx_timer *imxtm)
{
......@@ -343,10 +342,10 @@ static const struct imx_gpt_data imx21_gpt_data = {
.reg_tstat = MX1_2_TSTAT,
.reg_tcn = MX1_2_TCN,
.reg_tcmp = MX1_2_TCMP,
.gpt_irq_enable = imx21_gpt_irq_enable,
.gpt_irq_disable = imx21_gpt_irq_disable,
.gpt_irq_enable = imx1_gpt_irq_enable,
.gpt_irq_disable = imx1_gpt_irq_disable,
.gpt_irq_acknowledge = imx21_gpt_irq_acknowledge,
.gpt_setup_tctl = imx21_gpt_setup_tctl,
.gpt_setup_tctl = imx1_gpt_setup_tctl,
.set_next_event = mx1_2_set_next_event,
};
......@@ -365,9 +364,9 @@ static const struct imx_gpt_data imx6dl_gpt_data = {
.reg_tstat = V2_TSTAT,
.reg_tcn = V2_TCN,
.reg_tcmp = V2_TCMP,
.gpt_irq_enable = imx6dl_gpt_irq_enable,
.gpt_irq_disable = imx6dl_gpt_irq_disable,
.gpt_irq_acknowledge = imx6dl_gpt_irq_acknowledge,
.gpt_irq_enable = imx31_gpt_irq_enable,
.gpt_irq_disable = imx31_gpt_irq_disable,
.gpt_irq_acknowledge = imx31_gpt_irq_acknowledge,
.gpt_setup_tctl = imx6dl_gpt_setup_tctl,
.set_next_event = v2_set_next_event,
};
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Clocksource driver for Loongson-1 SoC
*
* Copyright (c) 2023 Keguang Zhang <keguang.zhang@gmail.com>
*/
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/sizes.h>
#include "timer-of.h"
/* Loongson-1 PWM Timer Register Definitions */
#define PWM_CNTR 0x0
#define PWM_HRC 0x4
#define PWM_LRC 0x8
#define PWM_CTRL 0xc
/* PWM Control Register Bits */
#define INT_LRC_EN BIT(11)
#define INT_HRC_EN BIT(10)
#define CNTR_RST BIT(7)
#define INT_SR BIT(6)
#define INT_EN BIT(5)
#define PWM_SINGLE BIT(4)
#define PWM_OE BIT(3)
#define CNT_EN BIT(0)
#define CNTR_WIDTH 24
DEFINE_RAW_SPINLOCK(ls1x_timer_lock);
struct ls1x_clocksource {
void __iomem *reg_base;
unsigned long ticks_per_jiffy;
struct clocksource clksrc;
};
static inline struct ls1x_clocksource *to_ls1x_clksrc(struct clocksource *c)
{
return container_of(c, struct ls1x_clocksource, clksrc);
}
static inline void ls1x_pwmtimer_set_period(unsigned int period,
struct timer_of *to)
{
writel(period, timer_of_base(to) + PWM_LRC);
writel(period, timer_of_base(to) + PWM_HRC);
}
static inline void ls1x_pwmtimer_clear(struct timer_of *to)
{
writel(0, timer_of_base(to) + PWM_CNTR);
}
static inline void ls1x_pwmtimer_start(struct timer_of *to)
{
writel((INT_EN | PWM_OE | CNT_EN), timer_of_base(to) + PWM_CTRL);
}
static inline void ls1x_pwmtimer_stop(struct timer_of *to)
{
writel(0, timer_of_base(to) + PWM_CTRL);
}
static inline void ls1x_pwmtimer_irq_ack(struct timer_of *to)
{
int val;
val = readl(timer_of_base(to) + PWM_CTRL);
val |= INT_SR;
writel(val, timer_of_base(to) + PWM_CTRL);
}
static irqreturn_t ls1x_clockevent_isr(int irq, void *dev_id)
{
struct clock_event_device *clkevt = dev_id;
struct timer_of *to = to_timer_of(clkevt);
ls1x_pwmtimer_irq_ack(to);
ls1x_pwmtimer_clear(to);
ls1x_pwmtimer_start(to);
clkevt->event_handler(clkevt);
return IRQ_HANDLED;
}
static int ls1x_clockevent_set_state_periodic(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
raw_spin_lock(&ls1x_timer_lock);
ls1x_pwmtimer_set_period(timer_of_period(to), to);
ls1x_pwmtimer_clear(to);
ls1x_pwmtimer_start(to);
raw_spin_unlock(&ls1x_timer_lock);
return 0;
}
static int ls1x_clockevent_tick_resume(struct clock_event_device *clkevt)
{
raw_spin_lock(&ls1x_timer_lock);
ls1x_pwmtimer_start(to_timer_of(clkevt));
raw_spin_unlock(&ls1x_timer_lock);
return 0;
}
static int ls1x_clockevent_set_state_shutdown(struct clock_event_device *clkevt)
{
raw_spin_lock(&ls1x_timer_lock);
ls1x_pwmtimer_stop(to_timer_of(clkevt));
raw_spin_unlock(&ls1x_timer_lock);
return 0;
}
static int ls1x_clockevent_set_next(unsigned long evt,
struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
raw_spin_lock(&ls1x_timer_lock);
ls1x_pwmtimer_set_period(evt, to);
ls1x_pwmtimer_clear(to);
ls1x_pwmtimer_start(to);
raw_spin_unlock(&ls1x_timer_lock);
return 0;
}
static struct timer_of ls1x_to = {
.flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
.clkevt = {
.name = "ls1x-pwmtimer",
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.rating = 300,
.set_next_event = ls1x_clockevent_set_next,
.set_state_periodic = ls1x_clockevent_set_state_periodic,
.set_state_oneshot = ls1x_clockevent_set_state_shutdown,
.set_state_shutdown = ls1x_clockevent_set_state_shutdown,
.tick_resume = ls1x_clockevent_tick_resume,
},
.of_irq = {
.handler = ls1x_clockevent_isr,
.flags = IRQF_TIMER,
},
};
/*
* Since the PWM timer overflows every two ticks, its not very useful
* to just read by itself. So use jiffies to emulate a free
* running counter:
*/
static u64 ls1x_clocksource_read(struct clocksource *cs)
{
struct ls1x_clocksource *ls1x_cs = to_ls1x_clksrc(cs);
unsigned long flags;
int count;
u32 jifs;
static int old_count;
static u32 old_jifs;
raw_spin_lock_irqsave(&ls1x_timer_lock, flags);
/*
* Although our caller may have the read side of xtime_lock,
* this is now a seqlock, and we are cheating in this routine
* by having side effects on state that we cannot undo if
* there is a collision on the seqlock and our caller has to
* retry. (Namely, old_jifs and old_count.) So we must treat
* jiffies as volatile despite the lock. We read jiffies
* before latching the timer count to guarantee that although
* the jiffies value might be older than the count (that is,
* the counter may underflow between the last point where
* jiffies was incremented and the point where we latch the
* count), it cannot be newer.
*/
jifs = jiffies;
/* read the count */
count = readl(ls1x_cs->reg_base + PWM_CNTR);
/*
* It's possible for count to appear to go the wrong way for this
* reason:
*
* The timer counter underflows, but we haven't handled the resulting
* interrupt and incremented jiffies yet.
*
* Previous attempts to handle these cases intelligently were buggy, so
* we just do the simple thing now.
*/
if (count < old_count && jifs == old_jifs)
count = old_count;
old_count = count;
old_jifs = jifs;
raw_spin_unlock_irqrestore(&ls1x_timer_lock, flags);
return (u64)(jifs * ls1x_cs->ticks_per_jiffy) + count;
}
static struct ls1x_clocksource ls1x_clocksource = {
.clksrc = {
.name = "ls1x-pwmtimer",
.rating = 300,
.read = ls1x_clocksource_read,
.mask = CLOCKSOURCE_MASK(CNTR_WIDTH),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
},
};
static int __init ls1x_pwm_clocksource_init(struct device_node *np)
{
struct timer_of *to = &ls1x_to;
int ret;
ret = timer_of_init(np, to);
if (ret)
return ret;
clockevents_config_and_register(&to->clkevt, timer_of_rate(to),
0x1, GENMASK(CNTR_WIDTH - 1, 0));
ls1x_clocksource.reg_base = timer_of_base(to);
ls1x_clocksource.ticks_per_jiffy = timer_of_period(to);
return clocksource_register_hz(&ls1x_clocksource.clksrc,
timer_of_rate(to));
}
TIMER_OF_DECLARE(ls1x_pwm_clocksource, "loongson,ls1b-pwmtimer",
ls1x_pwm_clocksource_init);
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2015 Linaro Ltd.
*/
#ifndef __SOC_IMX_TIMER_H__
#define __SOC_IMX_TIMER_H__
enum imx_gpt_type {
GPT_TYPE_IMX1, /* i.MX1 */
GPT_TYPE_IMX21, /* i.MX21/27 */
GPT_TYPE_IMX31, /* i.MX31/35/25/37/51/6Q */
GPT_TYPE_IMX6DL, /* i.MX6DL/SX/SL */
};
#endif /* __SOC_IMX_TIMER_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment