Commit f4febfdb authored by Thomas Gleixner's avatar Thomas Gleixner

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

Pull timer driver updates from Daniel Lezcano:

  - Fix DT bindings typos, readability and wrong information about
    underflow and overflow interrupts for the RZ/G2L MTU3a driver (Biju
    Das)

  - Fix a memory leak in the error path when probing the i.MX GPT timer
    (Jacky Bai)

  - Don't use clk_get_rate() in atomic context as the function might
    sleep. Store the clock and use notifiers to receive a clocke rate
    change notification (Ivaylo Dimitrov)

  - Remove superfluous error message when platform_get_irq() fails
    because the underlying function already prints one (Yang Li)

  - Add wakeup capability flag for the risc-V ACPI timer (Sunil V L)

  - Fix initialization of the TCB timers which are in cascade as the
    second timer is reset after the first wraps up leading to
    inconsistent scheduler behavior (Ronald Wahl)

  - Add DT bindings and driver for Cirrus Logic EP93xx (Nikita Shubin)
parents 8ceea12d c28ca80b
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/cirrus,ep9301-timer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Cirrus Logic EP93xx timer
maintainers:
- Alexander Sverdlin <alexander.sverdlin@gmail.com>
- Nikita Shubin <nikita.shubin@maquefel.me>
properties:
compatible:
oneOf:
- const: cirrus,ep9301-timer
- items:
- enum:
- cirrus,ep9302-timer
- cirrus,ep9307-timer
- cirrus,ep9312-timer
- cirrus,ep9315-timer
- const: cirrus,ep9301-timer
reg:
maxItems: 1
interrupts:
maxItems: 1
resets:
maxItems: 1
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
timer@80810000 {
compatible = "cirrus,ep9301-timer";
reg = <0x80810000 0x100>;
interrupt-parent = <&vic1>;
interrupts = <19>;
};
...
...@@ -11,8 +11,8 @@ maintainers: ...@@ -11,8 +11,8 @@ maintainers:
description: | description: |
This hardware block consists of eight 16-bit timer channels and one This hardware block consists of eight 16-bit timer channels and one
32- bit timer channel. It supports the following specifications: 32-bit timer channel. It supports the following specifications:
- Pulse input/output: 28 lines max. - Pulse input/output: 28 lines max
- Pulse input 3 lines - Pulse input 3 lines
- Count clock 11 clocks for each channel (14 clocks for MTU0, 12 clocks - Count clock 11 clocks for each channel (14 clocks for MTU0, 12 clocks
for MTU2, and 10 clocks for MTU5, four clocks for MTU1-MTU2 combination for MTU2, and 10 clocks for MTU5, four clocks for MTU1-MTU2 combination
...@@ -23,11 +23,11 @@ description: | ...@@ -23,11 +23,11 @@ description: |
- Input capture function (noise filter setting available) - Input capture function (noise filter setting available)
- Counter-clearing operation - Counter-clearing operation
- Simultaneous writing to multiple timer counters (TCNT) - Simultaneous writing to multiple timer counters (TCNT)
(excluding MTU8). (excluding MTU8)
- Simultaneous clearing on compare match or input capture - Simultaneous clearing on compare match or input capture
(excluding MTU8). (excluding MTU8)
- Simultaneous input and output to registers in synchronization with - Simultaneous input and output to registers in synchronization with
counter operations (excluding MTU8). counter operations (excluding MTU8)
- Up to 12-phase PWM output in combination with synchronous operation - Up to 12-phase PWM output in combination with synchronous operation
(excluding MTU8) (excluding MTU8)
- [MTU0 MTU3, MTU4, MTU6, MTU7, and MTU8] - [MTU0 MTU3, MTU4, MTU6, MTU7, and MTU8]
...@@ -40,26 +40,26 @@ description: | ...@@ -40,26 +40,26 @@ description: |
- [MTU3, MTU4, MTU6, and MTU7] - [MTU3, MTU4, MTU6, and MTU7]
- Through interlocked operation of MTU3/4 and MTU6/7, the positive and - Through interlocked operation of MTU3/4 and MTU6/7, the positive and
negative signals in six phases (12 phases in total) can be output in negative signals in six phases (12 phases in total) can be output in
complementary PWM and reset-synchronized PWM operation. complementary PWM and reset-synchronized PWM operation
- In complementary PWM mode, values can be transferred from buffer - In complementary PWM mode, values can be transferred from buffer
registers to temporary registers at crests and troughs of the timer- registers to temporary registers at crests and troughs of the timer-
counter values or when the buffer registers (TGRD registers in MTU4 counter values or when the buffer registers (TGRD registers in MTU4
and MTU7) are written to. and MTU7) are written to
- Double-buffering selectable in complementary PWM mode. - Double-buffering selectable in complementary PWM mode
- [MTU3 and MTU4] - [MTU3 and MTU4]
- Through interlocking with MTU0, a mode for driving AC synchronous - Through interlocking with MTU0, a mode for driving AC synchronous
motors (brushless DC motors) by using complementary PWM output and motors (brushless DC motors) by using complementary PWM output and
reset-synchronized PWM output is settable and allows the selection reset-synchronized PWM output is settable and allows the selection
of two types of waveform output (chopping or level). of two types of waveform output (chopping or level)
- [MTU5] - [MTU5]
- Capable of operation as a dead-time compensation counter. - Capable of operation as a dead-time compensation counter
- [MTU0/MTU5, MTU1, MTU2, and MTU8] - [MTU0/MTU5, MTU1, MTU2, and MTU8]
- 32-bit phase counting mode specifiable by combining MTU1 and MTU2 and - 32-bit phase counting mode specifiable by combining MTU1 and MTU2 and
through interlocked operation with MTU0/MTU5 and MTU8. through interlocked operation with MTU0/MTU5 and MTU8
- Interrupt-skipping function - Interrupt-skipping function
- In complementary PWM mode, interrupts on crests and troughs of counter - In complementary PWM mode, interrupts on crests and troughs of counter
values and triggers to start conversion by the A/D converter can be values and triggers to start conversion by the A/D converter can be
skipped. skipped
- Interrupt sources: 43 sources. - Interrupt sources: 43 sources.
- Buffer operation: - Buffer operation:
- Automatic transfer of register data (transfer from the buffer - Automatic transfer of register data (transfer from the buffer
...@@ -68,9 +68,9 @@ description: | ...@@ -68,9 +68,9 @@ description: |
- A/D converter start triggers can be generated - A/D converter start triggers can be generated
- A/D converter start request delaying function enables A/D converter - A/D converter start request delaying function enables A/D converter
to be started with any desired timing and to be synchronized with to be started with any desired timing and to be synchronized with
PWM output. PWM output
- Low power consumption function - Low power consumption function
- The MTU3a can be placed in the module-stop state. - The MTU3a can be placed in the module-stop state
There are two phase counting modes. 16-bit phase counting mode in which There are two phase counting modes. 16-bit phase counting mode in which
MTU1 and MTU2 operate independently, and cascade connection 32-bit phase MTU1 and MTU2 operate independently, and cascade connection 32-bit phase
...@@ -109,6 +109,7 @@ properties: ...@@ -109,6 +109,7 @@ properties:
compatible: compatible:
items: items:
- enum: - enum:
- renesas,r9a07g043-mtu3 # RZ/{G2UL,Five}
- renesas,r9a07g044-mtu3 # RZ/G2{L,LC} - renesas,r9a07g044-mtu3 # RZ/G2{L,LC}
- renesas,r9a07g054-mtu3 # RZ/V2L - renesas,r9a07g054-mtu3 # RZ/V2L
- const: renesas,rz-mtu3 - const: renesas,rz-mtu3
...@@ -169,27 +170,27 @@ properties: ...@@ -169,27 +170,27 @@ properties:
- const: tgib0 - const: tgib0
- const: tgic0 - const: tgic0
- const: tgid0 - const: tgid0
- const: tgiv0 - const: tciv0
- const: tgie0 - const: tgie0
- const: tgif0 - const: tgif0
- const: tgia1 - const: tgia1
- const: tgib1 - const: tgib1
- const: tgiv1 - const: tciv1
- const: tgiu1 - const: tciu1
- const: tgia2 - const: tgia2
- const: tgib2 - const: tgib2
- const: tgiv2 - const: tciv2
- const: tgiu2 - const: tciu2
- const: tgia3 - const: tgia3
- const: tgib3 - const: tgib3
- const: tgic3 - const: tgic3
- const: tgid3 - const: tgid3
- const: tgiv3 - const: tciv3
- const: tgia4 - const: tgia4
- const: tgib4 - const: tgib4
- const: tgic4 - const: tgic4
- const: tgid4 - const: tgid4
- const: tgiv4 - const: tciv4
- const: tgiu5 - const: tgiu5
- const: tgiv5 - const: tgiv5
- const: tgiw5 - const: tgiw5
...@@ -197,18 +198,18 @@ properties: ...@@ -197,18 +198,18 @@ properties:
- const: tgib6 - const: tgib6
- const: tgic6 - const: tgic6
- const: tgid6 - const: tgid6
- const: tgiv6 - const: tciv6
- const: tgia7 - const: tgia7
- const: tgib7 - const: tgib7
- const: tgic7 - const: tgic7
- const: tgid7 - const: tgid7
- const: tgiv7 - const: tciv7
- const: tgia8 - const: tgia8
- const: tgib8 - const: tgib8
- const: tgic8 - const: tgic8
- const: tgid8 - const: tgid8
- const: tgiv8 - const: tciv8
- const: tgiu8 - const: tciu8
clocks: clocks:
maxItems: 1 maxItems: 1
...@@ -285,16 +286,16 @@ examples: ...@@ -285,16 +286,16 @@ examples:
<GIC_SPI 211 IRQ_TYPE_EDGE_RISING>, <GIC_SPI 211 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 212 IRQ_TYPE_EDGE_RISING>, <GIC_SPI 212 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 213 IRQ_TYPE_EDGE_RISING>; <GIC_SPI 213 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "tgia0", "tgib0", "tgic0", "tgid0", "tgiv0", "tgie0", interrupt-names = "tgia0", "tgib0", "tgic0", "tgid0", "tciv0", "tgie0",
"tgif0", "tgif0",
"tgia1", "tgib1", "tgiv1", "tgiu1", "tgia1", "tgib1", "tciv1", "tciu1",
"tgia2", "tgib2", "tgiv2", "tgiu2", "tgia2", "tgib2", "tciv2", "tciu2",
"tgia3", "tgib3", "tgic3", "tgid3", "tgiv3", "tgia3", "tgib3", "tgic3", "tgid3", "tciv3",
"tgia4", "tgib4", "tgic4", "tgid4", "tgiv4", "tgia4", "tgib4", "tgic4", "tgid4", "tciv4",
"tgiu5", "tgiv5", "tgiw5", "tgiu5", "tgiv5", "tgiw5",
"tgia6", "tgib6", "tgic6", "tgid6", "tgiv6", "tgia6", "tgib6", "tgic6", "tgid6", "tciv6",
"tgia7", "tgib7", "tgic7", "tgid7", "tgiv7", "tgia7", "tgib7", "tgic7", "tgid7", "tciv7",
"tgia8", "tgib8", "tgic8", "tgid8", "tgiv8", "tgiu8"; "tgia8", "tgib8", "tgic8", "tgid8", "tciv8", "tciu8";
clocks = <&cpg CPG_MOD R9A07G044_MTU_X_MCK_MTU3>; clocks = <&cpg CPG_MOD R9A07G044_MTU_X_MCK_MTU3>;
power-domains = <&cpg>; power-domains = <&cpg>;
resets = <&cpg R9A07G044_MTU_X_PRESET_MTU3>; resets = <&cpg R9A07G044_MTU_X_PRESET_MTU3>;
......
...@@ -732,4 +732,15 @@ config GOLDFISH_TIMER ...@@ -732,4 +732,15 @@ config GOLDFISH_TIMER
help help
Support for the timer/counter of goldfish-rtc Support for the timer/counter of goldfish-rtc
config EP93XX_TIMER
bool "Cirrus Logic ep93xx timer driver" if COMPILE_TEST
depends on ARCH_EP93XX
depends on GENERIC_CLOCKEVENTS
depends on HAS_IOMEM
select CLKSRC_MMIO
select TIMER_OF
help
Enables support for the Cirrus Logic timer block
EP93XX.
endmenu endmenu
...@@ -89,3 +89,4 @@ obj-$(CONFIG_MSC313E_TIMER) += timer-msc313e.o ...@@ -89,3 +89,4 @@ obj-$(CONFIG_MSC313E_TIMER) += timer-msc313e.o
obj-$(CONFIG_GOLDFISH_TIMER) += timer-goldfish.o obj-$(CONFIG_GOLDFISH_TIMER) += timer-goldfish.o
obj-$(CONFIG_GXP_TIMER) += timer-gxp.o obj-$(CONFIG_GXP_TIMER) += timer-gxp.o
obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o
obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o
...@@ -315,6 +315,7 @@ static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) ...@@ -315,6 +315,7 @@ static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx)
writel(mck_divisor_idx /* likely divide-by-8 */ writel(mck_divisor_idx /* likely divide-by-8 */
| ATMEL_TC_WAVE | ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP /* free-run */ | ATMEL_TC_WAVESEL_UP /* free-run */
| ATMEL_TC_ASWTRG_SET /* TIOA0 rises at software trigger */
| ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */
| ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */
tcaddr + ATMEL_TC_REG(0, CMR)); tcaddr + ATMEL_TC_REG(0, CMR));
......
// SPDX-License-Identifier: GPL-2.0
/*
* Cirrus Logic EP93xx timer driver.
* Copyright (C) 2021 Nikita Shubin <nikita.shubin@maquefel.me>
*
* Based on a rewrite of arch/arm/mach-ep93xx/timer.c:
*/
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <asm/mach/time.h>
/*************************************************************************
* Timer handling for EP93xx
*************************************************************************
* The ep93xx has four internal timers. Timers 1, 2 (both 16 bit) and
* 3 (32 bit) count down at 508 kHz, are self-reloading, and can generate
* an interrupt on underflow. Timer 4 (40 bit) counts down at 983.04 kHz,
* is free-running, and can't generate interrupts.
*
* The 508 kHz timers are ideal for use for the timer interrupt, as the
* most common values of HZ divide 508 kHz nicely. We pick the 32 bit
* timer (timer 3) to get as long sleep intervals as possible when using
* CONFIG_NO_HZ.
*
* The higher clock rate of timer 4 makes it a better choice than the
* other timers for use as clock source and for sched_clock(), providing
* a stable 40 bit time base.
*************************************************************************
*/
#define EP93XX_TIMER1_LOAD 0x00
#define EP93XX_TIMER1_VALUE 0x04
#define EP93XX_TIMER1_CONTROL 0x08
#define EP93XX_TIMER123_CONTROL_ENABLE BIT(7)
#define EP93XX_TIMER123_CONTROL_MODE BIT(6)
#define EP93XX_TIMER123_CONTROL_CLKSEL BIT(3)
#define EP93XX_TIMER1_CLEAR 0x0c
#define EP93XX_TIMER2_LOAD 0x20
#define EP93XX_TIMER2_VALUE 0x24
#define EP93XX_TIMER2_CONTROL 0x28
#define EP93XX_TIMER2_CLEAR 0x2c
/*
* This read-only register contains the low word of the time stamp debug timer
* ( Timer4). When this register is read, the high byte of the Timer4 counter is
* saved in the Timer4ValueHigh register.
*/
#define EP93XX_TIMER4_VALUE_LOW 0x60
#define EP93XX_TIMER4_VALUE_HIGH 0x64
#define EP93XX_TIMER4_VALUE_HIGH_ENABLE BIT(8)
#define EP93XX_TIMER3_LOAD 0x80
#define EP93XX_TIMER3_VALUE 0x84
#define EP93XX_TIMER3_CONTROL 0x88
#define EP93XX_TIMER3_CLEAR 0x8c
#define EP93XX_TIMER123_RATE 508469
#define EP93XX_TIMER4_RATE 983040
struct ep93xx_tcu {
void __iomem *base;
};
static struct ep93xx_tcu *ep93xx_tcu;
static u64 ep93xx_clocksource_read(struct clocksource *c)
{
struct ep93xx_tcu *tcu = ep93xx_tcu;
return lo_hi_readq(tcu->base + EP93XX_TIMER4_VALUE_LOW) & GENMASK_ULL(39, 0);
}
static u64 notrace ep93xx_read_sched_clock(void)
{
return ep93xx_clocksource_read(NULL);
}
static int ep93xx_clkevt_set_next_event(unsigned long next,
struct clock_event_device *evt)
{
struct ep93xx_tcu *tcu = ep93xx_tcu;
/* Default mode: periodic, off, 508 kHz */
u32 tmode = EP93XX_TIMER123_CONTROL_MODE |
EP93XX_TIMER123_CONTROL_CLKSEL;
/* Clear timer */
writel(tmode, tcu->base + EP93XX_TIMER3_CONTROL);
/* Set next event */
writel(next, tcu->base + EP93XX_TIMER3_LOAD);
writel(tmode | EP93XX_TIMER123_CONTROL_ENABLE,
tcu->base + EP93XX_TIMER3_CONTROL);
return 0;
}
static int ep93xx_clkevt_shutdown(struct clock_event_device *evt)
{
struct ep93xx_tcu *tcu = ep93xx_tcu;
/* Disable timer */
writel(0, tcu->base + EP93XX_TIMER3_CONTROL);
return 0;
}
static struct clock_event_device ep93xx_clockevent = {
.name = "timer1",
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = ep93xx_clkevt_shutdown,
.set_state_oneshot = ep93xx_clkevt_shutdown,
.tick_resume = ep93xx_clkevt_shutdown,
.set_next_event = ep93xx_clkevt_set_next_event,
.rating = 300,
};
static irqreturn_t ep93xx_timer_interrupt(int irq, void *dev_id)
{
struct ep93xx_tcu *tcu = ep93xx_tcu;
struct clock_event_device *evt = dev_id;
/* Writing any value clears the timer interrupt */
writel(1, tcu->base + EP93XX_TIMER3_CLEAR);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int __init ep93xx_timer_of_init(struct device_node *np)
{
int irq;
unsigned long flags = IRQF_TIMER | IRQF_IRQPOLL;
struct ep93xx_tcu *tcu;
int ret;
tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);
if (!tcu)
return -ENOMEM;
tcu->base = of_iomap(np, 0);
if (!tcu->base) {
pr_err("Can't remap registers\n");
ret = -ENXIO;
goto out_free;
}
ep93xx_tcu = tcu;
irq = irq_of_parse_and_map(np, 0);
if (irq == 0)
irq = -EINVAL;
if (irq < 0) {
pr_err("EP93XX Timer Can't parse IRQ %d", irq);
goto out_free;
}
/* Enable and register clocksource and sched_clock on timer 4 */
writel(EP93XX_TIMER4_VALUE_HIGH_ENABLE,
tcu->base + EP93XX_TIMER4_VALUE_HIGH);
clocksource_mmio_init(NULL, "timer4",
EP93XX_TIMER4_RATE, 200, 40,
ep93xx_clocksource_read);
sched_clock_register(ep93xx_read_sched_clock, 40,
EP93XX_TIMER4_RATE);
/* Set up clockevent on timer 3 */
if (request_irq(irq, ep93xx_timer_interrupt, flags, "ep93xx timer",
&ep93xx_clockevent))
pr_err("Failed to request irq %d (ep93xx timer)\n", irq);
clockevents_config_and_register(&ep93xx_clockevent,
EP93XX_TIMER123_RATE,
1,
UINT_MAX);
return 0;
out_free:
kfree(tcu);
return ret;
}
TIMER_OF_DECLARE(ep93xx_timer, "cirrus,ep9301-timer", ep93xx_timer_of_init);
...@@ -434,12 +434,16 @@ static int __init mxc_timer_init_dt(struct device_node *np, enum imx_gpt_type t ...@@ -434,12 +434,16 @@ static int __init mxc_timer_init_dt(struct device_node *np, enum imx_gpt_type t
return -ENOMEM; return -ENOMEM;
imxtm->base = of_iomap(np, 0); imxtm->base = of_iomap(np, 0);
if (!imxtm->base) if (!imxtm->base) {
return -ENXIO; ret = -ENXIO;
goto err_kfree;
}
imxtm->irq = irq_of_parse_and_map(np, 0); imxtm->irq = irq_of_parse_and_map(np, 0);
if (imxtm->irq <= 0) if (imxtm->irq <= 0) {
return -EINVAL; ret = -EINVAL;
goto err_kfree;
}
imxtm->clk_ipg = of_clk_get_by_name(np, "ipg"); imxtm->clk_ipg = of_clk_get_by_name(np, "ipg");
...@@ -452,11 +456,15 @@ static int __init mxc_timer_init_dt(struct device_node *np, enum imx_gpt_type t ...@@ -452,11 +456,15 @@ static int __init mxc_timer_init_dt(struct device_node *np, enum imx_gpt_type t
ret = _mxc_timer_init(imxtm); ret = _mxc_timer_init(imxtm);
if (ret) if (ret)
return ret; goto err_kfree;
initialized = 1; initialized = 1;
return 0; return 0;
err_kfree:
kfree(imxtm);
return ret;
} }
static int __init imx1_timer_init_dt(struct device_node *np) static int __init imx1_timer_init_dt(struct device_node *np)
......
...@@ -212,6 +212,10 @@ TIMER_OF_DECLARE(riscv_timer, "riscv", riscv_timer_init_dt); ...@@ -212,6 +212,10 @@ TIMER_OF_DECLARE(riscv_timer, "riscv", riscv_timer_init_dt);
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static int __init riscv_timer_acpi_init(struct acpi_table_header *table) static int __init riscv_timer_acpi_init(struct acpi_table_header *table)
{ {
struct acpi_table_rhct *rhct = (struct acpi_table_rhct *)table;
riscv_timer_cannot_wake_cpu = rhct->flags & ACPI_RHCT_TIMER_CANNOT_WAKEUP_CPU;
return riscv_timer_init_common(); return riscv_timer_init_common();
} }
......
...@@ -256,10 +256,8 @@ static int sun5i_timer_probe(struct platform_device *pdev) ...@@ -256,10 +256,8 @@ static int sun5i_timer_probe(struct platform_device *pdev)
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0)
dev_err(dev, "Can't get IRQ\n");
return irq; return irq;
}
clk = devm_clk_get_enabled(dev, NULL); clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk)) { if (IS_ERR(clk)) {
......
...@@ -140,6 +140,8 @@ struct dmtimer { ...@@ -140,6 +140,8 @@ struct dmtimer {
struct platform_device *pdev; struct platform_device *pdev;
struct list_head node; struct list_head node;
struct notifier_block nb; struct notifier_block nb;
struct notifier_block fclk_nb;
unsigned long fclk_rate;
}; };
static u32 omap_reserved_systimers; static u32 omap_reserved_systimers;
...@@ -253,8 +255,7 @@ static inline void __omap_dm_timer_enable_posted(struct dmtimer *timer) ...@@ -253,8 +255,7 @@ static inline void __omap_dm_timer_enable_posted(struct dmtimer *timer)
timer->posted = OMAP_TIMER_POSTED; timer->posted = OMAP_TIMER_POSTED;
} }
static inline void __omap_dm_timer_stop(struct dmtimer *timer, static inline void __omap_dm_timer_stop(struct dmtimer *timer)
unsigned long rate)
{ {
u32 l; u32 l;
...@@ -269,7 +270,7 @@ static inline void __omap_dm_timer_stop(struct dmtimer *timer, ...@@ -269,7 +270,7 @@ static inline void __omap_dm_timer_stop(struct dmtimer *timer,
* Wait for functional clock period x 3.5 to make sure that * Wait for functional clock period x 3.5 to make sure that
* timer is stopped * timer is stopped
*/ */
udelay(3500000 / rate + 1); udelay(3500000 / timer->fclk_rate + 1);
#endif #endif
} }
...@@ -348,6 +349,21 @@ static int omap_timer_context_notifier(struct notifier_block *nb, ...@@ -348,6 +349,21 @@ static int omap_timer_context_notifier(struct notifier_block *nb,
return NOTIFY_OK; return NOTIFY_OK;
} }
static int omap_timer_fclk_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct clk_notifier_data *clk_data = data;
struct dmtimer *timer = container_of(nb, struct dmtimer, fclk_nb);
switch (event) {
case POST_RATE_CHANGE:
timer->fclk_rate = clk_data->new_rate;
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
static int omap_dm_timer_reset(struct dmtimer *timer) static int omap_dm_timer_reset(struct dmtimer *timer)
{ {
u32 l, timeout = 100000; u32 l, timeout = 100000;
...@@ -754,7 +770,6 @@ static int omap_dm_timer_stop(struct omap_dm_timer *cookie) ...@@ -754,7 +770,6 @@ static int omap_dm_timer_stop(struct omap_dm_timer *cookie)
{ {
struct dmtimer *timer; struct dmtimer *timer;
struct device *dev; struct device *dev;
unsigned long rate = 0;
timer = to_dmtimer(cookie); timer = to_dmtimer(cookie);
if (unlikely(!timer)) if (unlikely(!timer))
...@@ -762,10 +777,7 @@ static int omap_dm_timer_stop(struct omap_dm_timer *cookie) ...@@ -762,10 +777,7 @@ static int omap_dm_timer_stop(struct omap_dm_timer *cookie)
dev = &timer->pdev->dev; dev = &timer->pdev->dev;
if (!timer->omap1) __omap_dm_timer_stop(timer);
rate = clk_get_rate(timer->fclk);
__omap_dm_timer_stop(timer, rate);
pm_runtime_put_sync(dev); pm_runtime_put_sync(dev);
...@@ -1124,6 +1136,14 @@ static int omap_dm_timer_probe(struct platform_device *pdev) ...@@ -1124,6 +1136,14 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
timer->fclk = devm_clk_get(dev, "fck"); timer->fclk = devm_clk_get(dev, "fck");
if (IS_ERR(timer->fclk)) if (IS_ERR(timer->fclk))
return PTR_ERR(timer->fclk); return PTR_ERR(timer->fclk);
timer->fclk_nb.notifier_call = omap_timer_fclk_notifier;
ret = devm_clk_notifier_register(dev, timer->fclk,
&timer->fclk_nb);
if (ret)
return ret;
timer->fclk_rate = clk_get_rate(timer->fclk);
} else { } else {
timer->fclk = ERR_PTR(-ENODEV); timer->fclk = ERR_PTR(-ENODEV);
} }
......
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