The truly boring timer and clocksource updates for 5.8:

- Not a single new clocksource or clockevent driver!
 
  - Device tree updates for various chips
  - Fixes and improvements and cleanups all over the place
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAl7WEEkTHHRnbHhAbGlu
 dXRyb25peC5kZQAKCRCmGPVMDXSYoZxrD/dy2y712euXcZLgclXXQYLDnlRyqLN2
 t/6XMeoVulah8UAHccCL+RjLLDHp2pJl1yGPe730GJe9Jxv9+ZVUtXak0VoIoQbO
 HpNdNLD/Clq2jv1mgoXh1gb5xnEkQsAWcIFAyH4frB+mWZaOhpwqf51JT6QuwnPf
 OAq09gGdRogslXKuliEuRgbHL/a6WjC5IG4gzzB/coxYPnjGpBc8eYY+QbISqALD
 D84KJaNu/rOTwh8fXawBmFz5GwSk6LiGkh+Br/vSodYh3T23SWjVPJRsNX0UVT/S
 m1tEx2njKBBzmyYjrCU2tF0cwdJ0AduARrCFPB11nrsldp4E36fuoRjs+bW5HZAO
 JDajzHrPunYMN3pmh/D5Qk4/4v4XpPFluybqaoBIAee9L/8ulNKUMrEmjJHbHe+N
 5pSmkUsxciRmRRyjfMK3xD8K1/P/J+dJnCY3qOEbDZ7O4nei7wdemY9awCIRdVWb
 vJYWZwkbGFEsicEUjGgBSqesWuSg+pJRC896PQboonarZ4DZFEZ3B7sODNIrzb82
 78nhJ/IGYNuJ4eL7yrxmNDllCOava4vwV87tedbWgAtguE4sDLljKb/V3abwOAOt
 ZcNvZLCkMiXKdKKEbVJu/WRtQ+lvO1HNM4yB6S6I4R3kegWZu3zqsUL/xzDhk7O5
 lrj0DKbKzUqn
 =FFm0
 -----END PGP SIGNATURE-----

Merge tag 'timers-core-2020-06-02' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull timer updates from Thomas Gleixner:
 "The truly boring timer and clocksource updates for 5.8:

   - Not a single new clocksource or clockevent driver!

   - Device tree updates for various chips

   - Fixes and improvements and cleanups all over the place"

* tag 'timers-core-2020-06-02' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (27 commits)
  dt-bindings: timer: Add renesas,em-sti bindings
  clocksource/drivers/timer-versatile: Clear OF_POPULATED flag
  clocksource: mips-gic-timer: Mark GIC timer as unstable if ref clock changes
  clocksource: mips-gic-timer: Register as sched_clock
  clocksource: dw_apb_timer_of: Fix missing clockevent timers
  clocksource: dw_apb_timer: Affiliate of-based timer with any CPU
  clocksource: dw_apb_timer: Make CPU-affiliation being optional
  dt-bindings: timer: Move snps,dw-apb-timer DT schema from rtc
  dt-bindings: rtc: Convert snps,dw-apb-timer to DT schema
  clocksource/drivers/timer-ti-dm: Do one override clock parent in prepare()
  clocksource/drivers/timer-ti-dm: Fix spelling mistake "detectt" -> "detect"
  clocksource/drivers/timer-ti-dm: Fix warning for set but not used
  clocksource/drivers/timer-ti-dm: Add clockevent and clocksource support
  clocksource/drivers/timer-ti-32k: Add support for initializing directly
  drivers/clocksource/arm_arch_timer: Remove duplicate error message
  clocksource/drivers/arc_timer: Remove duplicate error message
  clocksource/drivers/rda: drop redundant Kconfig dependency
  clocksource/drivers/timer-ti-dm: Fix warning for set but not used
  clocksource/drivers/timer-ti-dm: Add clockevent and clocksource support
  clocksource/drivers/timer-ti-32k: Add support for initializing directly
  ...
This commit is contained in:
Linus Torvalds 2020-06-03 10:10:07 -07:00
commit dabc4df27c
21 changed files with 992 additions and 75 deletions

View File

@ -1,32 +0,0 @@
* Designware APB timer
Required properties:
- compatible: One of:
"snps,dw-apb-timer"
"snps,dw-apb-timer-sp" <DEPRECATED>
"snps,dw-apb-timer-osc" <DEPRECATED>
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: IRQ line for the timer.
- either clocks+clock-names or clock-frequency properties
Optional properties:
- clocks : list of clock specifiers, corresponding to entries in
the clock-names property;
- clock-names : should contain "timer" and "pclk" entries, matching entries
in the clocks property.
- clock-frequency: The frequency in HZ of the timer.
- clock-freq: For backwards compatibility with picoxcell
If using the clock specifiers, the pclk clock is optional, as not all
systems may use one.
Example:
timer@ffe00000 {
compatible = "snps,dw-apb-timer";
interrupts = <0 170 4>;
reg = <0xffe00000 0x1000>;
clocks = <&timer_clk>, <&timer_pclk>;
clock-names = "timer", "pclk";
};

View File

@ -0,0 +1,46 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/renesas,em-sti.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas EMMA Mobile System Timer
maintainers:
- Magnus Damm <magnus.damm@gmail.com>
properties:
compatible:
const: renesas,em-sti
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-names:
const: sclk
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
timer@e0180000 {
compatible = "renesas,em-sti";
reg = <0xe0180000 0x54>;
interrupts = <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&sti_sclk>;
clock-names = "sclk";
};

View File

@ -0,0 +1,88 @@
# SPDX-License-Identifier: GPL-2.0-only
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/snps,dw-apb-timer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Synopsys DesignWare APB Timer
maintainers:
- Daniel Lezcano <daniel.lezcano@linaro.org>
properties:
compatible:
oneOf:
- const: snps,dw-apb-timer
- enum:
- snps,dw-apb-timer-sp
- snps,dw-apb-timer-osc
deprecated: true
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
minItems: 1
items:
- description: Timer ticks reference clock source
- description: APB interface clock source
clock-names:
minItems: 1
items:
- const: timer
- const: pclk
clock-frequency: true
clock-freq:
$ref: "/schemas/types.yaml#/definitions/uint32"
description: |
Has the same meaning as the 'clock-frequency' property - timer clock
frequency in HZ, but is defined only for the backwards compatibility
with the picoxcell platform.
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
oneOf:
- required:
- clocks
- clock-names
- required:
- clock-frequency
- required:
- clock-freq
examples:
- |
timer@ffe00000 {
compatible = "snps,dw-apb-timer";
interrupts = <0 170 4>;
reg = <0xffe00000 0x1000>;
clocks = <&timer_clk>, <&timer_pclk>;
clock-names = "timer", "pclk";
};
- |
timer@ffe00000 {
compatible = "snps,dw-apb-timer";
interrupts = <0 170 4>;
reg = <0xffe00000 0x1000>;
clocks = <&timer_clk>;
clock-names = "timer";
};
- |
timer@ffe00000 {
compatible = "snps,dw-apb-timer";
interrupts = <0 170 4>;
reg = <0xffe00000 0x1000>;
clock-frequency = <25000000>;
};
...

View File

@ -105,6 +105,7 @@
ti,timers = <&timer7>;
pinctrl-names = "default";
pinctrl-0 = <&dmtimer7_pins>;
ti,clock-source = <0x01>;
};
vmmcsd_fixed: regulator-3v3 {

View File

@ -156,6 +156,7 @@
pinctrl-0 = <&pwm_pins>;
ti,timers = <&timer11>;
#pwm-cells = <3>;
ti,clock-source = <0x01>;
};
/* HS USB Host PHY on PORT 1 */

View File

@ -65,6 +65,7 @@
pinctrl-0 = <&pwm_pins>;
ti,timers = <&timer10>;
#pwm-cells = <3>;
ti,clock-source = <0x01>;
};
};

View File

@ -150,6 +150,7 @@
compatible = "ti,omap-dmtimer-pwm";
ti,timers = <&timer11>;
#pwm-cells = <3>;
ti,clock-source = <0x01>;
};
hsusb2_phy: hsusb2_phy {

View File

@ -120,7 +120,6 @@ config OWL_TIMER
config RDA_TIMER
bool "RDA timer driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS
select CLKSRC_MMIO
select TIMER_OF
help
@ -562,16 +561,16 @@ config CLKSRC_VERSATILE
bool "ARM Versatile (Express) reference platforms clock source" if COMPILE_TEST
depends on GENERIC_SCHED_CLOCK && !ARCH_USES_GETTIMEOFFSET
select TIMER_OF
default y if MFD_VEXPRESS_SYSREG
default y if (ARCH_VEXPRESS || ARCH_VERSATILE) && ARM
help
This option enables clock source based on free running
counter available in the "System Registers" block of
ARM Versatile, RealView and Versatile Express reference
platforms.
ARM Versatile and Versatile Express reference platforms.
config CLKSRC_MIPS_GIC
bool
depends on MIPS_GIC
select CLOCKSOURCE_WATCHDOG
select TIMER_OF
config CLKSRC_TANGO_XTAL
@ -709,6 +708,7 @@ config MICROCHIP_PIT64B
bool "Microchip PIT64B support"
depends on OF || COMPILE_TEST
select CLKSRC_MMIO
select TIMER_OF
help
This option enables Microchip PIT64B timer for Atmel
based system. It supports the oneshot, the periodic

View File

@ -18,6 +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_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

View File

@ -334,10 +334,8 @@ static int __init arc_clockevent_setup(struct device_node *node)
}
ret = arc_get_timer_clk(node);
if (ret) {
pr_err("clockevent: missing clk\n");
if (ret)
return ret;
}
/* Needs apriori irq_set_percpu_devid() done in intc map function */
ret = request_percpu_irq(arc_timer_irq, timer_irq_handler,

View File

@ -1588,10 +1588,8 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
arch_timers_present |= ARCH_TIMER_TYPE_CP15;
ret = acpi_gtdt_init(table, &platform_timer_count);
if (ret) {
pr_err("Failed to init GTDT table.\n");
if (ret)
return ret;
}
arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI] =
acpi_gtdt_map_ppi(ARCH_TIMER_PHYS_NONSECURE_PPI);

View File

@ -222,7 +222,8 @@ static int apbt_next_event(unsigned long delta,
/**
* dw_apb_clockevent_init() - use an APB timer as a clock_event_device
*
* @cpu: The CPU the events will be targeted at.
* @cpu: The CPU the events will be targeted at or -1 if CPU affiliation
* isn't required.
* @name: The name used for the timer and the IRQ for it.
* @rating: The rating to give the timer.
* @base: I/O base for the timer registers.
@ -257,7 +258,7 @@ dw_apb_clockevent_init(int cpu, const char *name, unsigned rating,
dw_ced->ced.max_delta_ticks = 0x7fffffff;
dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced);
dw_ced->ced.min_delta_ticks = 5000;
dw_ced->ced.cpumask = cpumask_of(cpu);
dw_ced->ced.cpumask = cpu < 0 ? cpu_possible_mask : cpumask_of(cpu);
dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
dw_ced->ced.set_state_shutdown = apbt_shutdown;

View File

@ -73,7 +73,7 @@ static void __init add_clockevent(struct device_node *event_timer)
timer_get_base_and_rate(event_timer, &iobase, &rate);
ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq,
ced = dw_apb_clockevent_init(-1, event_timer->name, 300, iobase, irq,
rate);
if (!ced)
panic("Unable to initialise clockevent device");
@ -147,10 +147,6 @@ static int num_called;
static int __init dw_apb_timer_init(struct device_node *timer)
{
switch (num_called) {
case 0:
pr_debug("%s: found clockevent timer\n", __func__);
add_clockevent(timer);
break;
case 1:
pr_debug("%s: found clocksource timer\n", __func__);
add_clocksource(timer);
@ -161,6 +157,8 @@ static int __init dw_apb_timer_init(struct device_node *timer)
#endif
break;
default:
pr_debug("%s: found clockevent timer\n", __func__);
add_clockevent(timer);
break;
}

View File

@ -16,6 +16,7 @@
#include <linux/notifier.h>
#include <linux/of_irq.h>
#include <linux/percpu.h>
#include <linux/sched_clock.h>
#include <linux/smp.h>
#include <linux/time.h>
#include <asm/mips-cps.h>
@ -23,14 +24,14 @@
static DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device);
static int gic_timer_irq;
static unsigned int gic_frequency;
static bool __read_mostly gic_clock_unstable;
static u64 notrace gic_read_count(void)
static void gic_clocksource_unstable(char *reason);
static u64 notrace gic_read_count_2x32(void)
{
unsigned int hi, hi2, lo;
if (mips_cm_is64)
return read_gic_counter();
do {
hi = read_gic_counter_32h();
lo = read_gic_counter_32l();
@ -40,6 +41,19 @@ static u64 notrace gic_read_count(void)
return (((u64) hi) << 32) + lo;
}
static u64 notrace gic_read_count_64(void)
{
return read_gic_counter();
}
static u64 notrace gic_read_count(void)
{
if (mips_cm_is64)
return gic_read_count_64();
return gic_read_count_2x32();
}
static int gic_next_event(unsigned long delta, struct clock_event_device *evt)
{
int cpu = cpumask_first(evt->cpumask);
@ -114,8 +128,10 @@ static int gic_clk_notifier(struct notifier_block *nb, unsigned long action,
{
struct clk_notifier_data *cnd = data;
if (action == POST_RATE_CHANGE)
if (action == POST_RATE_CHANGE) {
gic_clocksource_unstable("ref clock rate change");
on_each_cpu(gic_update_frequency, (void *)cnd->new_rate, 1);
}
return NOTIFY_OK;
}
@ -161,6 +177,18 @@ static struct clocksource gic_clocksource = {
.vdso_clock_mode = VDSO_CLOCKMODE_GIC,
};
static void gic_clocksource_unstable(char *reason)
{
if (gic_clock_unstable)
return;
gic_clock_unstable = true;
pr_info("GIC timer is unstable due to %s\n", reason);
clocksource_mark_unstable(&gic_clocksource);
}
static int __init __gic_clocksource_init(void)
{
unsigned int count_width;
@ -228,6 +256,18 @@ static int __init gic_clocksource_of_init(struct device_node *node)
/* And finally start the counter */
clear_gic_config(GIC_CONFIG_COUNTSTOP);
/*
* It's safe to use the MIPS GIC timer as a sched clock source only if
* its ticks are stable, which is true on either the platforms with
* stable CPU frequency or on the platforms with CM3 and CPU frequency
* change performed by the CPC core clocks divider.
*/
if (mips_cm_revision() >= CM_REV_CM3 || !IS_ENABLED(CONFIG_CPU_FREQ)) {
sched_clock_register(mips_cm_is64 ?
gic_read_count_64 : gic_read_count_2x32,
64, gic_frequency);
}
return 0;
}
TIMER_OF_DECLARE(mips_gic_timer, "mti,gic-timer",

View File

@ -139,7 +139,6 @@ static int
clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev)
{
u32 alm;
int status = 0;
unsigned int val;
BUG_ON(delta < 2);
@ -163,7 +162,7 @@ clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev)
alm += delta;
regmap_write(regmap_st, AT91_ST_RTAR, alm);
return status;
return 0;
}
static struct clock_event_device clkevt = {

View File

@ -18,7 +18,7 @@
#include <clocksource/timer-davinci.h>
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt "\n", __func__
#define pr_fmt(fmt) "%s: " fmt, __func__
#define DAVINCI_TIMER_REG_TIM12 0x10
#define DAVINCI_TIMER_REG_TIM34 0x14
@ -250,31 +250,29 @@ int __init davinci_timer_register(struct clk *clk,
rv = clk_prepare_enable(clk);
if (rv) {
pr_err("Unable to prepare and enable the timer clock");
pr_err("Unable to prepare and enable the timer clock\n");
return rv;
}
if (!request_mem_region(timer_cfg->reg.start,
resource_size(&timer_cfg->reg),
"davinci-timer")) {
pr_err("Unable to request memory region");
pr_err("Unable to request memory region\n");
return -EBUSY;
}
base = ioremap(timer_cfg->reg.start, resource_size(&timer_cfg->reg));
if (!base) {
pr_err("Unable to map the register range");
pr_err("Unable to map the register range\n");
return -ENOMEM;
}
davinci_timer_init(base);
tick_rate = clk_get_rate(clk);
clockevent = kzalloc(sizeof(*clockevent), GFP_KERNEL | __GFP_NOFAIL);
if (!clockevent) {
pr_err("Error allocating memory for clockevent data");
clockevent = kzalloc(sizeof(*clockevent), GFP_KERNEL);
if (!clockevent)
return -ENOMEM;
}
clockevent->dev.name = "tim12";
clockevent->dev.features = CLOCK_EVT_FEAT_ONESHOT;
@ -298,7 +296,7 @@ int __init davinci_timer_register(struct clk *clk,
davinci_timer_irq_timer, IRQF_TIMER,
"clockevent/tim12", clockevent);
if (rv) {
pr_err("Unable to request the clockevent interrupt");
pr_err("Unable to request the clockevent interrupt\n");
return rv;
}
@ -325,7 +323,7 @@ int __init davinci_timer_register(struct clk *clk,
rv = clocksource_register_hz(&davinci_clocksource.dev, tick_rate);
if (rv) {
pr_err("Unable to register clocksource");
pr_err("Unable to register clocksource\n");
return rv;
}
@ -343,20 +341,20 @@ static int __init of_davinci_timer_register(struct device_node *np)
rv = of_address_to_resource(np, 0, &timer_cfg.reg);
if (rv) {
pr_err("Unable to get the register range for timer");
pr_err("Unable to get the register range for timer\n");
return rv;
}
rv = of_irq_to_resource_table(np, timer_cfg.irq,
DAVINCI_TIMER_NUM_IRQS);
if (rv != DAVINCI_TIMER_NUM_IRQS) {
pr_err("Unable to get the interrupts for timer");
pr_err("Unable to get the interrupts for timer\n");
return rv;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Unable to get the timer clock");
pr_err("Unable to get the timer clock\n");
return PTR_ERR(clk);
}

View File

@ -61,17 +61,19 @@ static inline void tpm_irq_acknowledge(void)
writel(TPM_STATUS_CH0F, timer_base + TPM_STATUS);
}
static struct delay_timer tpm_delay_timer;
static inline unsigned long tpm_read_counter(void)
{
return readl(timer_base + TPM_CNT);
}
#if defined(CONFIG_ARM)
static struct delay_timer tpm_delay_timer;
static unsigned long tpm_read_current_timer(void)
{
return tpm_read_counter();
}
#endif
static u64 notrace tpm_read_sched_clock(void)
{
@ -144,9 +146,11 @@ static struct timer_of to_tpm = {
static int __init tpm_clocksource_init(void)
{
#if defined(CONFIG_ARM)
tpm_delay_timer.read_current_timer = &tpm_read_current_timer;
tpm_delay_timer.freq = timer_of_rate(&to_tpm) >> 3;
register_current_timer_delay(&tpm_delay_timer);
#endif
sched_clock_register(tpm_read_sched_clock, counter_width,
timer_of_rate(&to_tpm) >> 3);

View File

@ -24,6 +24,7 @@
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
*/
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/sched_clock.h>
@ -76,6 +77,49 @@ static u64 notrace omap_32k_read_sched_clock(void)
return ti_32k_read_cycles(&ti_32k_timer.cs);
}
static void __init ti_32k_timer_enable_clock(struct device_node *np,
const char *name)
{
struct clk *clock;
int error;
clock = of_clk_get_by_name(np->parent, name);
if (IS_ERR(clock)) {
/* Only some SoCs have a separate interface clock */
if (PTR_ERR(clock) == -EINVAL && !strncmp("ick", name, 3))
return;
pr_warn("%s: could not get clock %s %li\n",
__func__, name, PTR_ERR(clock));
return;
}
error = clk_prepare_enable(clock);
if (error) {
pr_warn("%s: could not enable %s: %i\n",
__func__, name, error);
return;
}
}
static void __init ti_32k_timer_module_init(struct device_node *np,
void __iomem *base)
{
void __iomem *sysc = base + 4;
if (!of_device_is_compatible(np->parent, "ti,sysc"))
return;
ti_32k_timer_enable_clock(np, "fck");
ti_32k_timer_enable_clock(np, "ick");
/*
* Force idle module as wkup domain is active with MPU.
* No need to tag the module disabled for ti-sysc probe.
*/
writel_relaxed(0, sysc);
}
static int __init ti_32k_timer_init(struct device_node *np)
{
int ret;
@ -90,6 +134,7 @@ static int __init ti_32k_timer_init(struct device_node *np)
ti_32k_timer.cs.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;
ti_32k_timer.counter = ti_32k_timer.base;
ti_32k_timer_module_init(np, ti_32k_timer.base);
/*
* 32k sync Counter IP register offsets vary between the highlander
@ -104,6 +149,8 @@ static int __init ti_32k_timer_init(struct device_node *np)
else
ti_32k_timer.counter += OMAP2_32KSYNCNT_CR_OFF_LOW;
pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n");
ret = clocksource_register_hz(&ti_32k_timer.cs, 32768);
if (ret) {
pr_err("32k_counter: can't register clocksource\n");
@ -111,7 +158,6 @@ static int __init ti_32k_timer_init(struct device_node *np)
}
sched_clock_register(omap_32k_read_sched_clock, 32, 32768);
pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n");
return 0;
}

View File

@ -0,0 +1,727 @@
// SPDX-License-Identifier: GPL-2.0+
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <linux/clk/clk-conf.h>
#include <clocksource/timer-ti-dm.h>
#include <dt-bindings/bus/ti-sysc.h>
/* For type1, set SYSC_OMAP2_CLOCKACTIVITY for fck off on idle, l4 clock on */
#define DMTIMER_TYPE1_ENABLE ((1 << 9) | (SYSC_IDLE_SMART << 3) | \
SYSC_OMAP2_ENAWAKEUP | SYSC_OMAP2_AUTOIDLE)
#define DMTIMER_TYPE2_ENABLE (SYSC_IDLE_SMART_WKUP << 2)
#define DMTIMER_RESET_WAIT 100000
#define DMTIMER_INST_DONT_CARE ~0U
static int counter_32k;
static u32 clocksource;
static u32 clockevent;
/*
* Subset of the timer registers we use. Note that the register offsets
* depend on the timer revision detected.
*/
struct dmtimer_systimer {
void __iomem *base;
u8 sysc;
u8 irq_stat;
u8 irq_ena;
u8 pend;
u8 load;
u8 counter;
u8 ctrl;
u8 wakeup;
u8 ifctrl;
unsigned long rate;
};
struct dmtimer_clockevent {
struct clock_event_device dev;
struct dmtimer_systimer t;
u32 period;
};
struct dmtimer_clocksource {
struct clocksource dev;
struct dmtimer_systimer t;
unsigned int loadval;
};
/* Assumes v1 ip if bits [31:16] are zero */
static bool dmtimer_systimer_revision1(struct dmtimer_systimer *t)
{
u32 tidr = readl_relaxed(t->base);
return !(tidr >> 16);
}
static int __init dmtimer_systimer_type1_reset(struct dmtimer_systimer *t)
{
void __iomem *syss = t->base + OMAP_TIMER_V1_SYS_STAT_OFFSET;
int ret;
u32 l;
writel_relaxed(BIT(1) | BIT(2), t->base + t->ifctrl);
ret = readl_poll_timeout_atomic(syss, l, l & BIT(0), 100,
DMTIMER_RESET_WAIT);
return ret;
}
/* Note we must use io_base instead of func_base for type2 OCP regs */
static int __init dmtimer_systimer_type2_reset(struct dmtimer_systimer *t)
{
void __iomem *sysc = t->base + t->sysc;
u32 l;
l = readl_relaxed(sysc);
l |= BIT(0);
writel_relaxed(l, sysc);
return readl_poll_timeout_atomic(sysc, l, !(l & BIT(0)), 100,
DMTIMER_RESET_WAIT);
}
static int __init dmtimer_systimer_reset(struct dmtimer_systimer *t)
{
int ret;
if (dmtimer_systimer_revision1(t))
ret = dmtimer_systimer_type1_reset(t);
else
ret = dmtimer_systimer_type2_reset(t);
if (ret < 0) {
pr_err("%s failed with %i\n", __func__, ret);
return ret;
}
return 0;
}
static const struct of_device_id counter_match_table[] = {
{ .compatible = "ti,omap-counter32k" },
{ /* Sentinel */ },
};
/*
* Check if the SoC als has a usable working 32 KiHz counter. The 32 KiHz
* counter is handled by timer-ti-32k, but we need to detect it as it
* affects the preferred dmtimer system timer configuration. There is
* typically no use for a dmtimer clocksource if the 32 KiHz counter is
* present, except on am437x as described below.
*/
static void __init dmtimer_systimer_check_counter32k(void)
{
struct device_node *np;
if (counter_32k)
return;
np = of_find_matching_node(NULL, counter_match_table);
if (!np) {
counter_32k = -ENODEV;
return;
}
if (of_device_is_available(np))
counter_32k = 1;
else
counter_32k = -ENODEV;
of_node_put(np);
}
static const struct of_device_id dmtimer_match_table[] = {
{ .compatible = "ti,omap2420-timer", },
{ .compatible = "ti,omap3430-timer", },
{ .compatible = "ti,omap4430-timer", },
{ .compatible = "ti,omap5430-timer", },
{ .compatible = "ti,am335x-timer", },
{ .compatible = "ti,am335x-timer-1ms", },
{ .compatible = "ti,dm814-timer", },
{ .compatible = "ti,dm816-timer", },
{ /* Sentinel */ },
};
/*
* Checks that system timers are configured to not reset and idle during
* the generic timer-ti-dm device driver probe. And that the system timer
* source clocks are properly configured. Also, let's not hog any DSP and
* PWM capable timers unnecessarily as system timers.
*/
static bool __init dmtimer_is_preferred(struct device_node *np)
{
if (!of_device_is_available(np))
return false;
if (!of_property_read_bool(np->parent,
"ti,no-reset-on-init"))
return false;
if (!of_property_read_bool(np->parent, "ti,no-idle"))
return false;
/* Secure gptimer12 is always clocked with a fixed source */
if (!of_property_read_bool(np, "ti,timer-secure")) {
if (!of_property_read_bool(np, "assigned-clocks"))
return false;
if (!of_property_read_bool(np, "assigned-clock-parents"))
return false;
}
if (of_property_read_bool(np, "ti,timer-dsp"))
return false;
if (of_property_read_bool(np, "ti,timer-pwm"))
return false;
return true;
}
/*
* Finds the first available usable always-on timer, and assigns it to either
* clockevent or clocksource depending if the counter_32k is available on the
* SoC or not.
*
* Some omap3 boards with unreliable oscillator must not use the counter_32k
* or dmtimer1 with 32 KiHz source. Additionally, the boards with unreliable
* oscillator should really set counter_32k as disabled, and delete dmtimer1
* ti,always-on property, but let's not count on it. For these quirky cases,
* we prefer using the always-on secure dmtimer12 with the internal 32 KiHz
* clock as the clocksource, and any available dmtimer as clockevent.
*
* For am437x, we are using am335x style dmtimer clocksource. It is unclear
* if this quirk handling is really needed, but let's change it separately
* based on testing as it might cause side effects.
*/
static void __init dmtimer_systimer_assign_alwon(void)
{
struct device_node *np;
u32 pa = 0;
bool quirk_unreliable_oscillator = false;
/* Quirk unreliable 32 KiHz oscillator with incomplete dts */
if (of_machine_is_compatible("ti,omap3-beagle") ||
of_machine_is_compatible("timll,omap3-devkit8000")) {
quirk_unreliable_oscillator = true;
counter_32k = -ENODEV;
}
/* Quirk am437x using am335x style dmtimer clocksource */
if (of_machine_is_compatible("ti,am43"))
counter_32k = -ENODEV;
for_each_matching_node(np, dmtimer_match_table) {
if (!dmtimer_is_preferred(np))
continue;
if (of_property_read_bool(np, "ti,timer-alwon")) {
const __be32 *addr;
addr = of_get_address(np, 0, NULL, NULL);
pa = of_translate_address(np, addr);
if (pa) {
/* Quirky omap3 boards must use dmtimer12 */
if (quirk_unreliable_oscillator &&
pa == 0x48318000)
continue;
of_node_put(np);
break;
}
}
}
/* Usually no need for dmtimer clocksource if we have counter32 */
if (counter_32k >= 0) {
clockevent = pa;
clocksource = 0;
} else {
clocksource = pa;
clockevent = DMTIMER_INST_DONT_CARE;
}
}
/* Finds the first usable dmtimer, used for the don't care case */
static u32 __init dmtimer_systimer_find_first_available(void)
{
struct device_node *np;
const __be32 *addr;
u32 pa = 0;
for_each_matching_node(np, dmtimer_match_table) {
if (!dmtimer_is_preferred(np))
continue;
addr = of_get_address(np, 0, NULL, NULL);
pa = of_translate_address(np, addr);
if (pa) {
if (pa == clocksource || pa == clockevent) {
pa = 0;
continue;
}
of_node_put(np);
break;
}
}
return pa;
}
/* Selects the best clocksource and clockevent to use */
static void __init dmtimer_systimer_select_best(void)
{
dmtimer_systimer_check_counter32k();
dmtimer_systimer_assign_alwon();
if (clockevent == DMTIMER_INST_DONT_CARE)
clockevent = dmtimer_systimer_find_first_available();
pr_debug("%s: counter_32k: %i clocksource: %08x clockevent: %08x\n",
__func__, counter_32k, clocksource, clockevent);
}
/* Interface clocks are only available on some SoCs variants */
static int __init dmtimer_systimer_init_clock(struct device_node *np,
const char *name,
unsigned long *rate)
{
struct clk *clock;
unsigned long r;
int error;
clock = of_clk_get_by_name(np, name);
if ((PTR_ERR(clock) == -EINVAL) && !strncmp(name, "ick", 3))
return 0;
else if (IS_ERR(clock))
return PTR_ERR(clock);
error = clk_prepare_enable(clock);
if (error)
return error;
r = clk_get_rate(clock);
if (!r)
return -ENODEV;
*rate = r;
return 0;
}
static void dmtimer_systimer_enable(struct dmtimer_systimer *t)
{
u32 val;
if (dmtimer_systimer_revision1(t))
val = DMTIMER_TYPE1_ENABLE;
else
val = DMTIMER_TYPE2_ENABLE;
writel_relaxed(val, t->base + t->sysc);
}
static void dmtimer_systimer_disable(struct dmtimer_systimer *t)
{
writel_relaxed(0, t->base + t->sysc);
}
static int __init dmtimer_systimer_setup(struct device_node *np,
struct dmtimer_systimer *t)
{
unsigned long rate;
u8 regbase;
int error;
if (!of_device_is_compatible(np->parent, "ti,sysc"))
return -EINVAL;
t->base = of_iomap(np, 0);
if (!t->base)
return -ENXIO;
/*
* Enable optional assigned-clock-parents configured at the timer
* node level. For regular device drivers, this is done automatically
* by bus related code such as platform_drv_probe().
*/
error = of_clk_set_defaults(np, false);
if (error < 0)
pr_err("%s: clock source init failed: %i\n", __func__, error);
/* For ti-sysc, we have timer clocks at the parent module level */
error = dmtimer_systimer_init_clock(np->parent, "fck", &rate);
if (error)
goto err_unmap;
t->rate = rate;
error = dmtimer_systimer_init_clock(np->parent, "ick", &rate);
if (error)
goto err_unmap;
if (dmtimer_systimer_revision1(t)) {
t->irq_stat = OMAP_TIMER_V1_STAT_OFFSET;
t->irq_ena = OMAP_TIMER_V1_INT_EN_OFFSET;
t->pend = _OMAP_TIMER_WRITE_PEND_OFFSET;
regbase = 0;
} else {
t->irq_stat = OMAP_TIMER_V2_IRQSTATUS;
t->irq_ena = OMAP_TIMER_V2_IRQENABLE_SET;
regbase = OMAP_TIMER_V2_FUNC_OFFSET;
t->pend = regbase + _OMAP_TIMER_WRITE_PEND_OFFSET;
}
t->sysc = OMAP_TIMER_OCP_CFG_OFFSET;
t->load = regbase + _OMAP_TIMER_LOAD_OFFSET;
t->counter = regbase + _OMAP_TIMER_COUNTER_OFFSET;
t->ctrl = regbase + _OMAP_TIMER_CTRL_OFFSET;
t->wakeup = regbase + _OMAP_TIMER_WAKEUP_EN_OFFSET;
t->ifctrl = regbase + _OMAP_TIMER_IF_CTRL_OFFSET;
dmtimer_systimer_enable(t);
dmtimer_systimer_reset(t);
pr_debug("dmtimer rev %08x sysc %08x\n", readl_relaxed(t->base),
readl_relaxed(t->base + t->sysc));
return 0;
err_unmap:
iounmap(t->base);
return error;
}
/* Clockevent */
static struct dmtimer_clockevent *
to_dmtimer_clockevent(struct clock_event_device *clockevent)
{
return container_of(clockevent, struct dmtimer_clockevent, dev);
}
static irqreturn_t dmtimer_clockevent_interrupt(int irq, void *data)
{
struct dmtimer_clockevent *clkevt = data;
struct dmtimer_systimer *t = &clkevt->t;
writel_relaxed(OMAP_TIMER_INT_OVERFLOW, t->base + t->irq_stat);
clkevt->dev.event_handler(&clkevt->dev);
return IRQ_HANDLED;
}
static int dmtimer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
struct dmtimer_clockevent *clkevt = to_dmtimer_clockevent(evt);
struct dmtimer_systimer *t = &clkevt->t;
void __iomem *pend = t->base + t->pend;
writel_relaxed(0xffffffff - cycles, t->base + t->counter);
while (readl_relaxed(pend) & WP_TCRR)
cpu_relax();
writel_relaxed(OMAP_TIMER_CTRL_ST, t->base + t->ctrl);
while (readl_relaxed(pend) & WP_TCLR)
cpu_relax();
return 0;
}
static int dmtimer_clockevent_shutdown(struct clock_event_device *evt)
{
struct dmtimer_clockevent *clkevt = to_dmtimer_clockevent(evt);
struct dmtimer_systimer *t = &clkevt->t;
void __iomem *ctrl = t->base + t->ctrl;
u32 l;
l = readl_relaxed(ctrl);
if (l & OMAP_TIMER_CTRL_ST) {
l &= ~BIT(0);
writel_relaxed(l, ctrl);
/* Flush posted write */
l = readl_relaxed(ctrl);
/* Wait for functional clock period x 3.5 */
udelay(3500000 / t->rate + 1);
}
writel_relaxed(OMAP_TIMER_INT_OVERFLOW, t->base + t->irq_stat);
return 0;
}
static int dmtimer_set_periodic(struct clock_event_device *evt)
{
struct dmtimer_clockevent *clkevt = to_dmtimer_clockevent(evt);
struct dmtimer_systimer *t = &clkevt->t;
void __iomem *pend = t->base + t->pend;
dmtimer_clockevent_shutdown(evt);
/* Looks like we need to first set the load value separately */
writel_relaxed(clkevt->period, t->base + t->load);
while (readl_relaxed(pend) & WP_TLDR)
cpu_relax();
writel_relaxed(clkevt->period, t->base + t->counter);
while (readl_relaxed(pend) & WP_TCRR)
cpu_relax();
writel_relaxed(OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST,
t->base + t->ctrl);
while (readl_relaxed(pend) & WP_TCLR)
cpu_relax();
return 0;
}
static void omap_clockevent_idle(struct clock_event_device *evt)
{
struct dmtimer_clockevent *clkevt = to_dmtimer_clockevent(evt);
struct dmtimer_systimer *t = &clkevt->t;
dmtimer_systimer_disable(t);
}
static void omap_clockevent_unidle(struct clock_event_device *evt)
{
struct dmtimer_clockevent *clkevt = to_dmtimer_clockevent(evt);
struct dmtimer_systimer *t = &clkevt->t;
dmtimer_systimer_enable(t);
writel_relaxed(OMAP_TIMER_INT_OVERFLOW, t->base + t->irq_ena);
writel_relaxed(OMAP_TIMER_INT_OVERFLOW, t->base + t->wakeup);
}
static int __init dmtimer_clockevent_init(struct device_node *np)
{
struct dmtimer_clockevent *clkevt;
struct clock_event_device *dev;
struct dmtimer_systimer *t;
int error;
clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
if (!clkevt)
return -ENOMEM;
t = &clkevt->t;
dev = &clkevt->dev;
/*
* We mostly use cpuidle_coupled with ARM local timers for runtime,
* so there's probably no use for CLOCK_EVT_FEAT_DYNIRQ here.
*/
dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
dev->rating = 300;
dev->set_next_event = dmtimer_set_next_event;
dev->set_state_shutdown = dmtimer_clockevent_shutdown;
dev->set_state_periodic = dmtimer_set_periodic;
dev->set_state_oneshot = dmtimer_clockevent_shutdown;
dev->tick_resume = dmtimer_clockevent_shutdown;
dev->cpumask = cpu_possible_mask;
dev->irq = irq_of_parse_and_map(np, 0);
if (!dev->irq) {
error = -ENXIO;
goto err_out_free;
}
error = dmtimer_systimer_setup(np, &clkevt->t);
if (error)
goto err_out_free;
clkevt->period = 0xffffffff - DIV_ROUND_CLOSEST(t->rate, HZ);
/*
* For clock-event timers we never read the timer counter and
* so we are not impacted by errata i103 and i767. Therefore,
* we can safely ignore this errata for clock-event timers.
*/
writel_relaxed(OMAP_TIMER_CTRL_POSTED, t->base + t->ifctrl);
error = request_irq(dev->irq, dmtimer_clockevent_interrupt,
IRQF_TIMER, "clockevent", clkevt);
if (error)
goto err_out_unmap;
writel_relaxed(OMAP_TIMER_INT_OVERFLOW, t->base + t->irq_ena);
writel_relaxed(OMAP_TIMER_INT_OVERFLOW, t->base + t->wakeup);
pr_info("TI gptimer clockevent: %s%lu Hz at %pOF\n",
of_find_property(np, "ti,timer-alwon", NULL) ?
"always-on " : "", t->rate, np->parent);
clockevents_config_and_register(dev, t->rate,
3, /* Timer internal resynch latency */
0xffffffff);
if (of_device_is_compatible(np, "ti,am33xx") ||
of_device_is_compatible(np, "ti,am43")) {
dev->suspend = omap_clockevent_idle;
dev->resume = omap_clockevent_unidle;
}
return 0;
err_out_unmap:
iounmap(t->base);
err_out_free:
kfree(clkevt);
return error;
}
/* Clocksource */
static struct dmtimer_clocksource *
to_dmtimer_clocksource(struct clocksource *cs)
{
return container_of(cs, struct dmtimer_clocksource, dev);
}
static u64 dmtimer_clocksource_read_cycles(struct clocksource *cs)
{
struct dmtimer_clocksource *clksrc = to_dmtimer_clocksource(cs);
struct dmtimer_systimer *t = &clksrc->t;
return (u64)readl_relaxed(t->base + t->counter);
}
static void __iomem *dmtimer_sched_clock_counter;
static u64 notrace dmtimer_read_sched_clock(void)
{
return readl_relaxed(dmtimer_sched_clock_counter);
}
static void dmtimer_clocksource_suspend(struct clocksource *cs)
{
struct dmtimer_clocksource *clksrc = to_dmtimer_clocksource(cs);
struct dmtimer_systimer *t = &clksrc->t;
clksrc->loadval = readl_relaxed(t->base + t->counter);
dmtimer_systimer_disable(t);
}
static void dmtimer_clocksource_resume(struct clocksource *cs)
{
struct dmtimer_clocksource *clksrc = to_dmtimer_clocksource(cs);
struct dmtimer_systimer *t = &clksrc->t;
dmtimer_systimer_enable(t);
writel_relaxed(clksrc->loadval, t->base + t->counter);
writel_relaxed(OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR,
t->base + t->ctrl);
}
static int __init dmtimer_clocksource_init(struct device_node *np)
{
struct dmtimer_clocksource *clksrc;
struct dmtimer_systimer *t;
struct clocksource *dev;
int error;
clksrc = kzalloc(sizeof(*clksrc), GFP_KERNEL);
if (!clksrc)
return -ENOMEM;
dev = &clksrc->dev;
t = &clksrc->t;
error = dmtimer_systimer_setup(np, t);
if (error)
goto err_out_free;
dev->name = "dmtimer";
dev->rating = 300;
dev->read = dmtimer_clocksource_read_cycles;
dev->mask = CLOCKSOURCE_MASK(32);
dev->flags = CLOCK_SOURCE_IS_CONTINUOUS;
if (of_device_is_compatible(np, "ti,am33xx") ||
of_device_is_compatible(np, "ti,am43")) {
dev->suspend = dmtimer_clocksource_suspend;
dev->resume = dmtimer_clocksource_resume;
}
writel_relaxed(0, t->base + t->counter);
writel_relaxed(OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR,
t->base + t->ctrl);
pr_info("TI gptimer clocksource: %s%pOF\n",
of_find_property(np, "ti,timer-alwon", NULL) ?
"always-on " : "", np->parent);
if (!dmtimer_sched_clock_counter) {
dmtimer_sched_clock_counter = t->base + t->counter;
sched_clock_register(dmtimer_read_sched_clock, 32, t->rate);
}
if (clocksource_register_hz(dev, t->rate))
pr_err("Could not register clocksource %pOF\n", np);
return 0;
err_out_free:
kfree(clksrc);
return -ENODEV;
}
/*
* To detect between a clocksource and clockevent, we assume the device tree
* has no interrupts configured for a clocksource timer.
*/
static int __init dmtimer_systimer_init(struct device_node *np)
{
const __be32 *addr;
u32 pa;
/* One time init for the preferred timer configuration */
if (!clocksource && !clockevent)
dmtimer_systimer_select_best();
if (!clocksource && !clockevent) {
pr_err("%s: unable to detect system timers, update dtb?\n",
__func__);
return -EINVAL;
}
addr = of_get_address(np, 0, NULL, NULL);
pa = of_translate_address(np, addr);
if (!pa)
return -EINVAL;
if (counter_32k <= 0 && clocksource == pa)
return dmtimer_clocksource_init(np);
if (clockevent == pa)
return dmtimer_clockevent_init(np);
return 0;
}
TIMER_OF_DECLARE(systimer_omap2, "ti,omap2420-timer", dmtimer_systimer_init);
TIMER_OF_DECLARE(systimer_omap3, "ti,omap3430-timer", dmtimer_systimer_init);
TIMER_OF_DECLARE(systimer_omap4, "ti,omap4430-timer", dmtimer_systimer_init);
TIMER_OF_DECLARE(systimer_omap5, "ti,omap5430-timer", dmtimer_systimer_init);
TIMER_OF_DECLARE(systimer_am33x, "ti,am335x-timer", dmtimer_systimer_init);
TIMER_OF_DECLARE(systimer_am3ms, "ti,am335x-timer-1ms", dmtimer_systimer_init);
TIMER_OF_DECLARE(systimer_dm814, "ti,dm814-timer", dmtimer_systimer_init);
TIMER_OF_DECLARE(systimer_dm816, "ti,dm816-timer", dmtimer_systimer_init);

View File

@ -258,9 +258,7 @@ static int omap_dm_timer_prepare(struct omap_dm_timer *timer)
__omap_dm_timer_enable_posted(timer);
omap_dm_timer_disable(timer);
rc = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
return rc;
return 0;
}
static inline u32 omap_dm_timer_reserved_systimer(int id)

View File

@ -6,6 +6,7 @@
#include <linux/clocksource.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
@ -22,6 +23,8 @@ static int __init versatile_sched_clock_init(struct device_node *node)
{
void __iomem *base = of_iomap(node, 0);
of_node_clear_flag(node, OF_POPULATED);
if (!base)
return -ENXIO;