Commit 442489c2 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull timer updates from Thomas Gleixner:
 "Time, timers and related driver updates:

   - Prevent unnecessary timer softirq invocations by extending the
     tracking of the next expiring timer in the timer wheel beyond the
     existing NOHZ functionality.

     The tracking overhead at enqueue time is within the noise, but on
     sensitive workloads the avoidance of the soft interrupt invocation
     is a measurable improvement.

   - The obligatory new clocksource driver for Ingenic X100 OST

   - The usual fixes, improvements, cleanups and extensions for newer
     chip variants all over the driver space"

* tag 'timers-core-2020-08-04' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (28 commits)
  timers: Recalculate next timer interrupt only when necessary
  clocksource/drivers/ingenic: Add support for the Ingenic X1000 OST.
  dt-bindings: timer: Add Ingenic X1000 OST bindings.
  clocksource/drivers: Replace HTTP links with HTTPS ones
  clocksource/drivers/nomadik-mtu: Handle 32kHz clock
  clocksource/drivers/sh_cmt: Use "kHz" for kilohertz
  clocksource/drivers/imx: Add support for i.MX TPM driver with ARM64
  clocksource/drivers/ingenic: Add high resolution timer support for SMP/SMT.
  timers: Lower base clock forwarding threshold
  timers: Remove must_forward_clk
  timers: Spare timer softirq until next expiry
  timers: Expand clk forward logic beyond nohz
  timers: Reuse next expiry cache after nohz exit
  timers: Always keep track of next expiry
  timers: Optimize _next_timer_interrupt() level iteration
  timers: Add comments about calc_index() ceiling work
  timers: Move trigger_dyntick_cpu() to enqueue_timer()
  timers: Use only bucket expiry for base->next_expiry value
  timers: Preserve higher bits of expiration on index calculation
  clocksource/drivers/timer-atmel-tcb: Add sama5d2 support
  ...
parents f8b036a7 31cd0e11
* Device tree bindings for Atmel Timer Counter Blocks
- compatible: Should be "atmel,<chip>-tcb", "simple-mfd", "syscon".
<chip> can be "at91rm9200" or "at91sam9x5"
- reg: Should contain registers location and length
- #address-cells: has to be 1
- #size-cells: has to be 0
- interrupts: Should contain all interrupts for the TC block
Note that you can specify several interrupt cells if the TC
block has one interrupt per channel.
- clock-names: tuple listing input clock names.
Required elements: "t0_clk", "slow_clk"
Optional elements: "t1_clk", "t2_clk"
- clocks: phandles to input clocks.
The TCB can expose multiple subdevices:
* a timer
- compatible: Should be "atmel,tcb-timer"
- reg: Should contain the TCB channels to be used. If the
counter width is 16 bits (at91rm9200-tcb), two consecutive
channels are needed. Else, only one channel will be used.
Examples:
One interrupt per TC block:
tcb0: timer@fff7c000 {
compatible = "atmel,at91rm9200-tcb", "simple-mfd", "syscon";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xfff7c000 0x100>;
interrupts = <18 4>;
clocks = <&tcb0_clk>, <&clk32k>;
clock-names = "t0_clk", "slow_clk";
timer@0 {
compatible = "atmel,tcb-timer";
reg = <0>, <1>;
};
timer@2 {
compatible = "atmel,tcb-timer";
reg = <2>;
};
};
One interrupt per TC channel in a TC block:
tcb1: timer@fffdc000 {
compatible = "atmel,at91rm9200-tcb", "simple-mfd", "syscon";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xfffdc000 0x100>;
interrupts = <26 4>, <27 4>, <28 4>;
clocks = <&tcb1_clk>, <&clk32k>;
clock-names = "t0_clk", "slow_clk";
};
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/soc/microchip/atmel,at91rm9200-tcb.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Atmel Timer Counter Block
maintainers:
- Alexandre Belloni <alexandre.belloni@bootlin.com>
description: |
The Atmel (now Microchip) SoCs have timers named Timer Counter Block. Each
timer has three channels with two counters each.
properties:
compatible:
items:
- enum:
- atmel,at91rm9200-tcb
- atmel,at91sam9x5-tcb
- atmel,sama5d2-tcb
- const: simple-mfd
- const: syscon
reg:
maxItems: 1
interrupts:
description:
List of interrupts. One interrupt per TCB channel if available or one
interrupt for the TC block
minItems: 1
maxItems: 3
clock-names:
description:
List of clock names. Always includes t0_clk and slow clk. Also includes
t1_clk and t2_clk if a clock per channel is available.
minItems: 2
maxItems: 4
clocks:
minItems: 2
maxItems: 4
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^timer@[0-2]$":
description: The timer block channels that are used as timers.
type: object
properties:
compatible:
const: atmel,tcb-timer
reg:
description:
List of channels to use for this particular timer.
minItems: 1
maxItems: 3
required:
- compatible
- reg
allOf:
- if:
properties:
compatible:
contains:
const: atmel,sama5d2-tcb
then:
properties:
clocks:
minItems: 3
maxItems: 3
clock-names:
items:
- const: t0_clk
- const: gclk
- const: slow_clk
else:
properties:
clocks:
minItems: 2
maxItems: 4
clock-names:
oneOf:
- items:
- const: t0_clk
- const: slow_clk
- items:
- const: t0_clk
- const: t1_clk
- const: t2_clk
- const: slow_clk
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- '#address-cells'
- '#size-cells'
additionalProperties: false
examples:
- |
/* One interrupt per TC block: */
tcb0: timer@fff7c000 {
compatible = "atmel,at91rm9200-tcb", "simple-mfd", "syscon";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xfff7c000 0x100>;
interrupts = <18 4>;
clocks = <&tcb0_clk>, <&clk32k>;
clock-names = "t0_clk", "slow_clk";
timer@0 {
compatible = "atmel,tcb-timer";
reg = <0>, <1>;
};
timer@2 {
compatible = "atmel,tcb-timer";
reg = <2>;
};
};
/* One interrupt per TC channel in a TC block: */
tcb1: timer@fffdc000 {
compatible = "atmel,at91rm9200-tcb", "simple-mfd", "syscon";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xfffdc000 0x100>;
interrupts = <26 4>, <27 4>, <28 4>;
clocks = <&tcb1_clk>, <&clk32k>;
clock-names = "t0_clk", "slow_clk";
timer@0 {
compatible = "atmel,tcb-timer";
reg = <0>;
};
timer@1 {
compatible = "atmel,tcb-timer";
reg = <1>;
};
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/ingenic,sysost.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Bindings for SYSOST in Ingenic XBurst family SoCs
maintainers:
- 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
description:
The SYSOST in an Ingenic SoC provides one 64bit timer for clocksource
and one or more 32bit timers for clockevent.
properties:
"#clock-cells":
const: 1
compatible:
enum:
- ingenic,x1000-ost
- ingenic,x2000-ost
reg:
maxItems: 1
clocks:
maxItems: 1
clock-names:
const: ost
interrupts:
maxItems: 1
required:
- "#clock-cells"
- compatible
- reg
- clocks
- clock-names
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/x1000-cgu.h>
ost: timer@12000000 {
compatible = "ingenic,x1000-ost";
reg = <0x12000000 0x3c>;
#clock-cells = <1>;
clocks = <&cgu X1000_CLK_OST>;
clock-names = "ost";
interrupt-parent = <&cpuintc>;
interrupts = <3>;
};
...
...@@ -10,7 +10,7 @@ It is global timer is a free running up-counter and can generate interrupt ...@@ -10,7 +10,7 @@ It is global timer is a free running up-counter and can generate interrupt
when the counter reaches preset counter values. when the counter reaches preset counter values.
Documentation: Documentation:
http://www.ti.com/lit/ug/sprugv5a/sprugv5a.pdf https://www.ti.com/lit/ug/sprugv5a/sprugv5a.pdf
Required properties: Required properties:
......
...@@ -375,23 +375,23 @@ macb0: ethernet@f8008000 { ...@@ -375,23 +375,23 @@ macb0: ethernet@f8008000 {
}; };
tcb0: timer@f800c000 { tcb0: timer@f800c000 {
compatible = "atmel,at91sam9x5-tcb", "simple-mfd", "syscon"; compatible = "atmel,sama5d2-tcb", "simple-mfd", "syscon";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
reg = <0xf800c000 0x100>; reg = <0xf800c000 0x100>;
interrupts = <35 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <35 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 35>, <&clk32k>; clocks = <&pmc PMC_TYPE_PERIPHERAL 35>, <&pmc PMC_TYPE_GCK 35>, <&clk32k>;
clock-names = "t0_clk", "slow_clk"; clock-names = "t0_clk", "gclk", "slow_clk";
}; };
tcb1: timer@f8010000 { tcb1: timer@f8010000 {
compatible = "atmel,at91sam9x5-tcb", "simple-mfd", "syscon"; compatible = "atmel,sama5d2-tcb", "simple-mfd", "syscon";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
reg = <0xf8010000 0x100>; reg = <0xf8010000 0x100>;
interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>; interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 36>, <&clk32k>; clocks = <&pmc PMC_TYPE_PERIPHERAL 36>, <&pmc PMC_TYPE_GCK 36>, <&clk32k>;
clock-names = "t0_clk", "slow_clk"; clock-names = "t0_clk", "gclk", "slow_clk";
}; };
hsmc: hsmc@f8014000 { hsmc: hsmc@f8014000 {
......
...@@ -616,8 +616,9 @@ config CLKSRC_IMX_GPT ...@@ -616,8 +616,9 @@ config CLKSRC_IMX_GPT
config CLKSRC_IMX_TPM config CLKSRC_IMX_TPM
bool "Clocksource using i.MX TPM" if COMPILE_TEST bool "Clocksource using i.MX TPM" if COMPILE_TEST
depends on ARM && CLKDEV_LOOKUP depends on (ARM || ARM64) && CLKDEV_LOOKUP
select CLKSRC_MMIO select CLKSRC_MMIO
select TIMER_OF
help help
Enable this option to use IMX Timer/PWM Module (TPM) timer as Enable this option to use IMX Timer/PWM Module (TPM) timer as
clocksource. clocksource.
...@@ -696,8 +697,18 @@ config INGENIC_TIMER ...@@ -696,8 +697,18 @@ config INGENIC_TIMER
help help
Support for the timer/counter unit of the Ingenic JZ SoCs. Support for the timer/counter unit of the Ingenic JZ SoCs.
config INGENIC_SYSOST
bool "Clocksource/timer using the SYSOST in Ingenic X SoCs"
depends on MIPS || COMPILE_TEST
depends on COMMON_CLK
select MFD_SYSCON
select TIMER_OF
select IRQ_DOMAIN
help
Support for the SYSOST of the Ingenic X Series SoCs.
config INGENIC_OST config INGENIC_OST
bool "Clocksource for Ingenic OS Timer" bool "Clocksource using the OST in Ingenic JZ SoCs"
depends on MIPS || COMPILE_TEST depends on MIPS || COMPILE_TEST
depends on COMMON_CLK depends on COMMON_CLK
select MFD_SYSCON select MFD_SYSCON
......
...@@ -82,6 +82,7 @@ obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o ...@@ -82,6 +82,7 @@ obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o
obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
obj-$(CONFIG_INGENIC_OST) += ingenic-ost.o obj-$(CONFIG_INGENIC_OST) += ingenic-ost.o
obj-$(CONFIG_INGENIC_SYSOST) += ingenic-sysost.o
obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
obj-$(CONFIG_X86_NUMACHIP) += numachip.o obj-$(CONFIG_X86_NUMACHIP) += numachip.o
......
This diff is collapsed.
This diff is collapsed.
...@@ -186,6 +186,7 @@ static int __init nmdk_timer_init(void __iomem *base, int irq, ...@@ -186,6 +186,7 @@ static int __init nmdk_timer_init(void __iomem *base, int irq,
{ {
unsigned long rate; unsigned long rate;
int ret; int ret;
int min_ticks;
mtu_base = base; mtu_base = base;
...@@ -194,7 +195,8 @@ static int __init nmdk_timer_init(void __iomem *base, int irq, ...@@ -194,7 +195,8 @@ static int __init nmdk_timer_init(void __iomem *base, int irq,
/* /*
* Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz * Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz
* for ux500. * for ux500, and in one specific Ux500 case 32768 Hz.
*
* Use a divide-by-16 counter if the tick rate is more than 32MHz. * Use a divide-by-16 counter if the tick rate is more than 32MHz.
* At 32 MHz, the timer (with 32 bit counter) can be programmed * At 32 MHz, the timer (with 32 bit counter) can be programmed
* to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer * to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer
...@@ -230,7 +232,12 @@ static int __init nmdk_timer_init(void __iomem *base, int irq, ...@@ -230,7 +232,12 @@ static int __init nmdk_timer_init(void __iomem *base, int irq,
pr_err("%s: request_irq() failed\n", "Nomadik Timer Tick"); pr_err("%s: request_irq() failed\n", "Nomadik Timer Tick");
nmdk_clkevt.cpumask = cpumask_of(0); nmdk_clkevt.cpumask = cpumask_of(0);
nmdk_clkevt.irq = irq; nmdk_clkevt.irq = irq;
clockevents_config_and_register(&nmdk_clkevt, rate, 2, 0xffffffffU); if (rate < 100000)
min_ticks = 5;
else
min_ticks = 2;
clockevents_config_and_register(&nmdk_clkevt, rate, min_ticks,
0xffffffffU);
mtu_delay_timer.read_current_timer = &nmdk_timer_read_current_timer; mtu_delay_timer.read_current_timer = &nmdk_timer_read_current_timer;
mtu_delay_timer.freq = rate; mtu_delay_timer.freq = rate;
......
...@@ -349,7 +349,7 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch) ...@@ -349,7 +349,7 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch)
/* /*
* According to the sh73a0 user's manual, as CMCNT can be operated * According to the sh73a0 user's manual, as CMCNT can be operated
* only by the RCLK (Pseudo 32 KHz), there's one restriction on * only by the RCLK (Pseudo 32 kHz), there's one restriction on
* modifying CMCNT register; two RCLK cycles are necessary before * modifying CMCNT register; two RCLK cycles are necessary before
* this register is either read or any modification of the value * this register is either read or any modification of the value
* it holds is reflected in the LSI's actual operation. * it holds is reflected in the LSI's actual operation.
......
...@@ -27,9 +27,10 @@ ...@@ -27,9 +27,10 @@
* - Some chips support 32 bit counter. A single channel is used for * - Some chips support 32 bit counter. A single channel is used for
* this 32 bit free-running counter. the second channel is not used. * this 32 bit free-running counter. the second channel is not used.
* *
* - The third channel may be used to provide a 16-bit clockevent * - The third channel may be used to provide a clockevent source, used in
* source, used in either periodic or oneshot mode. This runs * either periodic or oneshot mode. For 16-bit counter its runs at 32 KiHZ,
* at 32 KiHZ, and can handle delays of up to two seconds. * and can handle delays of up to two seconds. For 32-bit counters, it runs at
* the same rate as the clocksource
* *
* REVISIT behavior during system suspend states... we should disable * REVISIT behavior during system suspend states... we should disable
* all clocks and save the power. Easily done for clockevent devices, * all clocks and save the power. Easily done for clockevent devices,
...@@ -47,6 +48,8 @@ static struct ...@@ -47,6 +48,8 @@ static struct
} tcb_cache[3]; } tcb_cache[3];
static u32 bmr_cache; static u32 bmr_cache;
static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128 };
static u64 tc_get_cycles(struct clocksource *cs) static u64 tc_get_cycles(struct clocksource *cs)
{ {
unsigned long flags; unsigned long flags;
...@@ -143,6 +146,7 @@ static unsigned long notrace tc_delay_timer_read32(void) ...@@ -143,6 +146,7 @@ static unsigned long notrace tc_delay_timer_read32(void)
struct tc_clkevt_device { struct tc_clkevt_device {
struct clock_event_device clkevt; struct clock_event_device clkevt;
struct clk *clk; struct clk *clk;
u32 rate;
void __iomem *regs; void __iomem *regs;
}; };
...@@ -151,13 +155,6 @@ static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt) ...@@ -151,13 +155,6 @@ static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
return container_of(clkevt, struct tc_clkevt_device, clkevt); return container_of(clkevt, struct tc_clkevt_device, clkevt);
} }
/* For now, we always use the 32K clock ... this optimizes for NO_HZ,
* because using one of the divided clocks would usually mean the
* tick rate can never be less than several dozen Hz (vs 0.5 Hz).
*
* A divided clock could be good for high resolution timers, since
* 30.5 usec resolution can seem "low".
*/
static u32 timer_clock; static u32 timer_clock;
static int tc_shutdown(struct clock_event_device *d) static int tc_shutdown(struct clock_event_device *d)
...@@ -183,7 +180,7 @@ static int tc_set_oneshot(struct clock_event_device *d) ...@@ -183,7 +180,7 @@ static int tc_set_oneshot(struct clock_event_device *d)
clk_enable(tcd->clk); clk_enable(tcd->clk);
/* slow clock, count up to RC, then irq and stop */ /* count up to RC, then irq and stop */
writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE | writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE |
ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR)); ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR));
writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
...@@ -205,10 +202,10 @@ static int tc_set_periodic(struct clock_event_device *d) ...@@ -205,10 +202,10 @@ static int tc_set_periodic(struct clock_event_device *d)
*/ */
clk_enable(tcd->clk); clk_enable(tcd->clk);
/* slow clock, count up to RC, then irq and restart */ /* count up to RC, then irq and restart */
writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
regs + ATMEL_TC_REG(2, CMR)); regs + ATMEL_TC_REG(2, CMR));
writel((32768 + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); writel((tcd->rate + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
/* Enable clock and interrupts on RC compare */ /* Enable clock and interrupts on RC compare */
writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
...@@ -256,47 +253,55 @@ static irqreturn_t ch2_irq(int irq, void *handle) ...@@ -256,47 +253,55 @@ static irqreturn_t ch2_irq(int irq, void *handle)
return IRQ_NONE; return IRQ_NONE;
} }
static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
{ {
int ret; int ret;
struct clk *t2_clk = tc->clk[2]; struct clk *t2_clk = tc->clk[2];
int irq = tc->irq[2]; int irq = tc->irq[2];
int bits = tc->tcb_config->counter_width;
ret = clk_prepare_enable(tc->slow_clk); /* try to enable t2 clk to avoid future errors in mode change */
ret = clk_prepare_enable(t2_clk);
if (ret) if (ret)
return ret; return ret;
/* try to enable t2 clk to avoid future errors in mode change */ clkevt.regs = tc->regs;
ret = clk_prepare_enable(t2_clk); clkevt.clk = t2_clk;
if (bits == 32) {
timer_clock = divisor_idx;
clkevt.rate = clk_get_rate(t2_clk) / atmel_tcb_divisors[divisor_idx];
} else {
ret = clk_prepare_enable(tc->slow_clk);
if (ret) { if (ret) {
clk_disable_unprepare(tc->slow_clk); clk_disable_unprepare(t2_clk);
return ret; return ret;
} }
clk_disable(t2_clk); clkevt.rate = clk_get_rate(tc->slow_clk);
timer_clock = ATMEL_TC_TIMER_CLOCK5;
clkevt.regs = tc->regs; }
clkevt.clk = t2_clk;
timer_clock = clk32k_divisor_idx; clk_disable(t2_clk);
clkevt.clkevt.cpumask = cpumask_of(0); clkevt.clkevt.cpumask = cpumask_of(0);
ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt); ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt);
if (ret) { if (ret) {
clk_unprepare(t2_clk); clk_unprepare(t2_clk);
if (bits != 32)
clk_disable_unprepare(tc->slow_clk); clk_disable_unprepare(tc->slow_clk);
return ret; return ret;
} }
clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); clockevents_config_and_register(&clkevt.clkevt, clkevt.rate, 1, BIT(bits) - 1);
return ret; return ret;
} }
#else /* !CONFIG_GENERIC_CLOCKEVENTS */ #else /* !CONFIG_GENERIC_CLOCKEVENTS */
static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx)
{ {
/* NOTHING */ /* NOTHING */
return 0; return 0;
...@@ -346,11 +351,23 @@ static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_id ...@@ -346,11 +351,23 @@ static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_id
writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
} }
static const u8 atmel_tcb_divisors[5] = { 2, 8, 32, 128, 0, }; static struct atmel_tcb_config tcb_rm9200_config = {
.counter_width = 16,
};
static struct atmel_tcb_config tcb_sam9x5_config = {
.counter_width = 32,
};
static struct atmel_tcb_config tcb_sama5d2_config = {
.counter_width = 32,
.has_gclk = 1,
};
static const struct of_device_id atmel_tcb_of_match[] = { static const struct of_device_id atmel_tcb_of_match[] = {
{ .compatible = "atmel,at91rm9200-tcb", .data = (void *)16, }, { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
{ .compatible = "atmel,at91sam9x5-tcb", .data = (void *)32, }, { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
{ /* sentinel */ } { /* sentinel */ }
}; };
...@@ -362,7 +379,6 @@ static int __init tcb_clksrc_init(struct device_node *node) ...@@ -362,7 +379,6 @@ static int __init tcb_clksrc_init(struct device_node *node)
u64 (*tc_sched_clock)(void); u64 (*tc_sched_clock)(void);
u32 rate, divided_rate = 0; u32 rate, divided_rate = 0;
int best_divisor_idx = -1; int best_divisor_idx = -1;
int clk32k_divisor_idx = -1;
int bits; int bits;
int i; int i;
int ret; int ret;
...@@ -399,7 +415,11 @@ static int __init tcb_clksrc_init(struct device_node *node) ...@@ -399,7 +415,11 @@ static int __init tcb_clksrc_init(struct device_node *node)
} }
match = of_match_node(atmel_tcb_of_match, node->parent); match = of_match_node(atmel_tcb_of_match, node->parent);
bits = (uintptr_t)match->data; if (!match)
return -ENODEV;
tc.tcb_config = match->data;
bits = tc.tcb_config->counter_width;
for (i = 0; i < ARRAY_SIZE(tc.irq); i++) for (i = 0; i < ARRAY_SIZE(tc.irq); i++)
writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR)); writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR));
...@@ -412,22 +432,17 @@ static int __init tcb_clksrc_init(struct device_node *node) ...@@ -412,22 +432,17 @@ static int __init tcb_clksrc_init(struct device_node *node)
/* How fast will we be counting? Pick something over 5 MHz. */ /* How fast will we be counting? Pick something over 5 MHz. */
rate = (u32) clk_get_rate(t0_clk); rate = (u32) clk_get_rate(t0_clk);
for (i = 0; i < ARRAY_SIZE(atmel_tcb_divisors); i++) { i = 0;
if (tc.tcb_config->has_gclk)
i = 1;
for (; i < ARRAY_SIZE(atmel_tcb_divisors); i++) {
unsigned divisor = atmel_tcb_divisors[i]; unsigned divisor = atmel_tcb_divisors[i];
unsigned tmp; unsigned tmp;
/* remember 32 KiHz clock for later */
if (!divisor) {
clk32k_divisor_idx = i;
continue;
}
tmp = rate / divisor; tmp = rate / divisor;
pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp); pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp);
if (best_divisor_idx > 0) { if ((best_divisor_idx >= 0) && (tmp < 5 * 1000 * 1000))
if (tmp < 5 * 1000 * 1000) break;
continue;
}
divided_rate = tmp; divided_rate = tmp;
best_divisor_idx = i; best_divisor_idx = i;
} }
...@@ -467,7 +482,7 @@ static int __init tcb_clksrc_init(struct device_node *node) ...@@ -467,7 +482,7 @@ static int __init tcb_clksrc_init(struct device_node *node)
goto err_disable_t1; goto err_disable_t1;
/* channel 2: periodic and oneshot timer support */ /* channel 2: periodic and oneshot timer support */
ret = setup_clkevents(&tc, clk32k_divisor_idx); ret = setup_clkevents(&tc, best_divisor_idx);
if (ret) if (ret)
goto err_unregister_clksrc; goto err_unregister_clksrc;
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* Roughly modelled after the OMAP1 MPU timer code. * Roughly modelled after the OMAP1 MPU timer code.
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
* *
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
*/ */
#include <linux/clk.h> #include <linux/clk.h>
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* *
* OMAP Dual-Mode Timers * OMAP Dual-Mode Timers
* *
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/
* Tarun Kanti DebBarma <tarun.kanti@ti.com> * Tarun Kanti DebBarma <tarun.kanti@ti.com>
* Thara Gopinath <thara@ti.com> * Thara Gopinath <thara@ti.com>
* *
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This header provides clock numbers for the ingenic,tcu DT binding.
*/
#ifndef __DT_BINDINGS_CLOCK_INGENIC_OST_H__
#define __DT_BINDINGS_CLOCK_INGENIC_OST_H__
#define OST_CLK_PERCPU_TIMER 0
#define OST_CLK_GLOBAL_TIMER 1
#endif /* __DT_BINDINGS_CLOCK_INGENIC_OST_H__ */
...@@ -36,9 +36,14 @@ struct clk; ...@@ -36,9 +36,14 @@ struct clk;
/** /**
* struct atmel_tcb_config - SoC data for a Timer/Counter Block * struct atmel_tcb_config - SoC data for a Timer/Counter Block
* @counter_width: size in bits of a timer counter register * @counter_width: size in bits of a timer counter register
* @has_gclk: boolean indicating if a timer counter has a generic clock
* @has_qdec: boolean indicating if a timer counter has a quadrature
* decoder.
*/ */
struct atmel_tcb_config { struct atmel_tcb_config {
size_t counter_width; size_t counter_width;
bool has_gclk;
bool has_qdec;
}; };
/** /**
......
This diff is collapsed.
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