mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
clocksource/drivers: Add support for the Renesas RZ/N1 timers
The Renesas RZ/N1 timer block controller is the controller in charge of timers available in the Renesas RZ/N1 SoCs family. This controller handles 8 timers: - 6 16-bit timers - 2 32-bit timers Each timer has its own interrupt, its own prescaler that can be used to divide the clock by 25 and all of them can work in either one-shot or periodic mode. Signed-off-by: Herve Codina (Schneider Electric) <herve.codina@bootlin.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Link: https://patch.msgid.link/20261001082225.125956-3-herve.codina@bootlin.com
This commit is contained in:
parent
9fea79f594
commit
435167b4f3
|
|
@ -152,6 +152,17 @@ config REALTEK_OTTO_TIMER
|
|||
RT8391, RTL8392, RTL8393 and RTL8396 and chips of the RTL930x series
|
||||
such as RTL9301, RTL9302 or RTL9303.
|
||||
|
||||
config RZN1_TIMER
|
||||
bool "Renesas RZ/N1 Timer"
|
||||
depends on HAS_IOMEM && COMMON_CLK
|
||||
depends on ARCH_RZN1 || COMPILE_TEST
|
||||
help
|
||||
Enables support for RZ/N1 SoC timers.
|
||||
A timers block in RZ/N1 SoCs is composed of 8 timers
|
||||
- 6 16-bit timers
|
||||
- 2 32-bit timers
|
||||
Two timers blocks are available in RZ/N1 SoCs.
|
||||
|
||||
config SUN4I_TIMER
|
||||
bool "Sun4i timer driver" if COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
|
|
|
|||
|
|
@ -96,3 +96,4 @@ obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o
|
|||
obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o
|
||||
obj-$(CONFIG_NXP_STM_TIMER) += timer-nxp-stm.o
|
||||
obj-$(CONFIG_RTK_SYSTIMER) += timer-realtek.o
|
||||
obj-$(CONFIG_RZN1_TIMER) += timer-rzn1.o
|
||||
|
|
|
|||
483
drivers/clocksource/timer-rzn1.c
Normal file
483
drivers/clocksource/timer-rzn1.c
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* RZ/N1 timers driver
|
||||
*
|
||||
* Copyright (C) 2026 Schneider-Electric
|
||||
*
|
||||
* Author: Herve Codina <herve.codina@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
/* Per-timer register offsets */
|
||||
#define RZN1_TIMER_LOAD_COUNT_REG 0x00
|
||||
#define RZN1_TIMER_CURRENT_COUNT_REG 0x04
|
||||
#define RZN1_TIMER_CONTROL_REG 0x08
|
||||
#define RZN1_TIMER_CONTROL_INT_UNMASK BIT(3)
|
||||
#define RZN1_TIMER_CONTROL_ENABLE BIT(2)
|
||||
#define RZN1_TIMER_CONTROL_AUTORELOAD BIT(1)
|
||||
#define RZN1_TIMER_CONTROL_PRESCALER_DIV25 BIT(0)
|
||||
#define RZN1_TIMER_CLEAR_INT_REG 0x0c
|
||||
#define RZN1_TIMER_STATUS_INT0_REG 0x10
|
||||
#define RZN1_TIMER_STATUS_INT1_REG 0x14
|
||||
|
||||
/*
|
||||
* 8 timers are available. Among those 8 timers, the first 6 timers are 16-bit
|
||||
* timers and the last two ones are 32-bit timers.
|
||||
*/
|
||||
#define RZN1_TIMER_BASE_INDEX_16BIT_TIMERS 0
|
||||
#define RZN1_TIMER_NB_16BIT_TIMERS 6
|
||||
#define RZN1_TIMER_LAST_INDEX_16BIT_TIMERS (RZN1_TIMER_BASE_INDEX_16BIT_TIMERS + \
|
||||
RZN1_TIMER_NB_16BIT_TIMERS - 1)
|
||||
|
||||
#define RZN1_TIMER_BASE_INDEX_32BIT_TIMERS 6
|
||||
#define RZN1_TIMER_NB_32BIT_TIMERS 2
|
||||
#define RZN1_TIMER_LAST_INDEX_32BIT_TIMERS (RZN1_TIMER_BASE_INDEX_32BIT_TIMERS + \
|
||||
RZN1_TIMER_NB_32BIT_TIMERS - 1)
|
||||
|
||||
#define RZN1_TIMER_IS_16BIT_TIMER(_i) ((_i) < RZN1_TIMER_BASE_INDEX_32BIT_TIMERS)
|
||||
|
||||
/* Total numbers of timers */
|
||||
#define RZN1_TIMER_NB_TIMERS (RZN1_TIMER_NB_16BIT_TIMERS + \
|
||||
RZN1_TIMER_NB_32BIT_TIMERS)
|
||||
|
||||
/* Offset of registers related to the timer number _i in the timers regs area */
|
||||
#define RZN1_TIMER_REGS_OFFSET(_i) (0x20 * (_i))
|
||||
|
||||
struct rzn1_timer {
|
||||
void __iomem *base;
|
||||
unsigned int width;
|
||||
unsigned long rate;
|
||||
unsigned long hz_period;
|
||||
struct clock_event_device ced;
|
||||
};
|
||||
|
||||
static int rzn1_timer_config(struct rzn1_timer *timer, u32 period, bool is_periodic)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = readl(timer->base + RZN1_TIMER_CONTROL_REG);
|
||||
|
||||
/* Disable the timer */
|
||||
writel(ctrl & ~RZN1_TIMER_CONTROL_ENABLE,
|
||||
timer->base + RZN1_TIMER_CONTROL_REG);
|
||||
|
||||
/* Set the new period */
|
||||
writel(period, timer->base + RZN1_TIMER_LOAD_COUNT_REG);
|
||||
|
||||
/* Prepare the timer mode */
|
||||
if (is_periodic)
|
||||
ctrl |= RZN1_TIMER_CONTROL_AUTORELOAD;
|
||||
else
|
||||
ctrl &= ~RZN1_TIMER_CONTROL_AUTORELOAD;
|
||||
|
||||
/*
|
||||
* Enable the timer.
|
||||
*
|
||||
* This automatically resets the CURRENT_COUNT register.
|
||||
*/
|
||||
ctrl |= RZN1_TIMER_CONTROL_ENABLE;
|
||||
writel(ctrl, timer->base + RZN1_TIMER_CONTROL_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rzn1_timer_disable(struct rzn1_timer *timer)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = readl(timer->base + RZN1_TIMER_CONTROL_REG);
|
||||
ctrl &= ~RZN1_TIMER_CONTROL_ENABLE;
|
||||
writel(ctrl, timer->base + RZN1_TIMER_CONTROL_REG);
|
||||
}
|
||||
|
||||
static void rzn1_timer_int_enable(struct rzn1_timer *timer)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = readl(timer->base + RZN1_TIMER_CONTROL_REG);
|
||||
ctrl |= RZN1_TIMER_CONTROL_INT_UNMASK;
|
||||
writel(ctrl, timer->base + RZN1_TIMER_CONTROL_REG);
|
||||
}
|
||||
|
||||
static irqreturn_t rzn1_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct rzn1_timer *timer = dev_id;
|
||||
|
||||
/* Ack the interrupt */
|
||||
readl(timer->base + RZN1_TIMER_CLEAR_INT_REG);
|
||||
|
||||
if (timer->ced.event_handler)
|
||||
timer->ced.event_handler(&timer->ced);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rzn1_clkevt_set_next_event(unsigned long cycles,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
struct rzn1_timer *timer = container_of(evt, struct rzn1_timer, ced);
|
||||
|
||||
return rzn1_timer_config(timer, cycles, false);
|
||||
}
|
||||
|
||||
static int rzn1_clkevt_shutdown(struct clock_event_device *evt)
|
||||
{
|
||||
struct rzn1_timer *timer = container_of(evt, struct rzn1_timer, ced);
|
||||
|
||||
rzn1_timer_disable(timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzn1_clkevt_set_periodic(struct clock_event_device *evt)
|
||||
{
|
||||
struct rzn1_timer *timer = container_of(evt, struct rzn1_timer, ced);
|
||||
|
||||
rzn1_timer_config(timer, timer->hz_period, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This global lock is used to prevent race conditions during global timers
|
||||
* test and set operation in case the driver is using the async probe option.
|
||||
*/
|
||||
static DEFINE_MUTEX(rzn1_global_timers_lock);
|
||||
|
||||
static int rzn1_timer_instance;
|
||||
|
||||
/* scheduler and clocksource */
|
||||
static struct rzn1_timer *rzn1_sched_clock;
|
||||
|
||||
static u64 notrace rzn1_sched_read(void)
|
||||
{
|
||||
return readl_relaxed(rzn1_sched_clock->base + RZN1_TIMER_CURRENT_COUNT_REG);
|
||||
}
|
||||
|
||||
static u64 rzn1_clksrc_read(struct clocksource *cs)
|
||||
{
|
||||
return readl_relaxed(rzn1_sched_clock->base + RZN1_TIMER_CURRENT_COUNT_REG);
|
||||
}
|
||||
|
||||
static struct clocksource rzn1_clocksource = {
|
||||
.name = "rzn1_timer_clocksource",
|
||||
.rating = 200,
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
.read = rzn1_clksrc_read,
|
||||
};
|
||||
|
||||
/* Timers table where 16-bit timers will be assigned per-cpu */
|
||||
static struct rzn1_timer *rzn1_tab_timers;
|
||||
|
||||
static int rzn1_local_timer_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
struct rzn1_timer *timer;
|
||||
|
||||
/* We assign the first and second 16-bit timer as per-cpu timers. */
|
||||
if (cpu >= 2)
|
||||
return 0;
|
||||
|
||||
timer = &rzn1_tab_timers[cpu + RZN1_TIMER_BASE_INDEX_16BIT_TIMERS];
|
||||
|
||||
timer->ced.cpumask = cpumask_of(cpu);
|
||||
irq_force_affinity(timer->ced.irq, cpumask_of(cpu));
|
||||
|
||||
clockevents_config_and_register(&timer->ced, timer->rate, 1,
|
||||
GENMASK(timer->width - 1, 0));
|
||||
/*
|
||||
* Time to enable the interrupt even if no interrupt can be fired yet.
|
||||
* Indeed, the timer is still globally disabled.
|
||||
* It will be enabled when rzn1_timer_config() is called
|
||||
*/
|
||||
enable_irq(timer->ced.irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzn1_local_timer_dying_cpu(unsigned int cpu)
|
||||
{
|
||||
struct rzn1_timer *timer;
|
||||
|
||||
if (cpu >= 2)
|
||||
return 0;
|
||||
|
||||
timer = &rzn1_tab_timers[cpu + RZN1_TIMER_BASE_INDEX_16BIT_TIMERS];
|
||||
|
||||
rzn1_timer_disable(timer);
|
||||
disable_irq_nosync(timer->ced.irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rzn1_timer_init(struct rzn1_timer *timer, unsigned int index,
|
||||
void __iomem *timers_base, unsigned long clock_rate)
|
||||
{
|
||||
timer->base = timers_base + RZN1_TIMER_REGS_OFFSET(index);
|
||||
|
||||
/* Disable the timer */
|
||||
writel(0, timer->base + RZN1_TIMER_CONTROL_REG);
|
||||
|
||||
/* Clear potential interrupts */
|
||||
readl(timer->base + RZN1_TIMER_CLEAR_INT_REG);
|
||||
|
||||
if (RZN1_TIMER_IS_16BIT_TIMER(index)) {
|
||||
timer->width = 16;
|
||||
/*
|
||||
* Enable pre-scaler for 16-bit timers.
|
||||
*
|
||||
* Timers are fed by a 25MHz clock. With this pre-scaler (1/25)
|
||||
* enabled, the 16-bit timers resolution is 1 microsecond.
|
||||
*/
|
||||
writel(RZN1_TIMER_CONTROL_PRESCALER_DIV25,
|
||||
timer->base + RZN1_TIMER_CONTROL_REG);
|
||||
timer->rate = clock_rate / 25;
|
||||
} else {
|
||||
timer->width = 32;
|
||||
timer->rate = clock_rate;
|
||||
}
|
||||
timer->hz_period = DIV_ROUND_UP(timer->rate, HZ);
|
||||
}
|
||||
|
||||
static void rzn1_timer_clkevt_init_ced(struct rzn1_timer *timer, const char *name,
|
||||
int irq)
|
||||
{
|
||||
timer->ced.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
|
||||
timer->ced.set_next_event = rzn1_clkevt_set_next_event;
|
||||
timer->ced.set_state_shutdown = rzn1_clkevt_shutdown;
|
||||
timer->ced.set_state_periodic = rzn1_clkevt_set_periodic;
|
||||
timer->ced.cpumask = cpu_possible_mask;
|
||||
|
||||
/*
|
||||
* 32-bit timers are fed with a 25 MHz clock directly derived from the
|
||||
* main PLL (1 GHz). The arch timer, ARMv7 CP15 in RZ/N1 SoCs, is fed
|
||||
* with to a 6.25 MHz clock.
|
||||
*
|
||||
* The clock of the RZ/N1 timers block is a stable one and the RZ/N1
|
||||
* timers have a higher resolution than the ARM CP15 one.
|
||||
*
|
||||
* Use a high rating for the 32-bit RZ/N1 timers.
|
||||
*/
|
||||
timer->ced.rating = timer->width == 16 ? 200 : 460;
|
||||
timer->ced.owner = THIS_MODULE;
|
||||
timer->ced.name = name;
|
||||
timer->ced.irq = irq;
|
||||
}
|
||||
|
||||
static int rzn1_timer_setup_timers(struct platform_device *pdev,
|
||||
struct rzn1_timer *tab_timers,
|
||||
unsigned int first_timer_index,
|
||||
unsigned int last_timer_index,
|
||||
void __iomem *base, unsigned long clock_rate)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rzn1_timer *timer;
|
||||
unsigned int i;
|
||||
char *name;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
for (i = first_timer_index; i <= last_timer_index; i++) {
|
||||
timer = &tab_timers[i];
|
||||
|
||||
rzn1_timer_init(timer, i, base, clock_rate);
|
||||
|
||||
irq = platform_get_irq(pdev, i);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
name = devm_kasprintf(dev, GFP_KERNEL, "%s-%u", dev_name(dev), i);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
rzn1_timer_clkevt_init_ced(timer, name, irq);
|
||||
|
||||
ret = devm_request_irq(dev, timer->ced.irq, rzn1_timer_interrupt,
|
||||
IRQF_TIMER | IRQF_NO_AUTOEN,
|
||||
timer->ced.name, timer);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "timer%u: Failed to request IRQ\n", i);
|
||||
|
||||
rzn1_timer_int_enable(timer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rzn1_timer_register_clkevt(struct rzn1_timer *tab_timers,
|
||||
unsigned int first_timer_index,
|
||||
unsigned int last_timer_index)
|
||||
{
|
||||
struct rzn1_timer *timer;
|
||||
unsigned int i;
|
||||
|
||||
for (i = first_timer_index; i <= last_timer_index; i++) {
|
||||
timer = &tab_timers[i];
|
||||
clockevents_config_and_register(&timer->ced, timer->rate,
|
||||
1, GENMASK(timer->width - 1, 0));
|
||||
/*
|
||||
* Time to enable the interrupt even if no interrupt can be
|
||||
* fired yet. Indeed, the timer is still globally disabled.
|
||||
* It will be enabled when rzn1_timer_config() is called
|
||||
*/
|
||||
enable_irq(timer->ced.irq);
|
||||
}
|
||||
}
|
||||
|
||||
static int rzn1_timer_probe_first(struct platform_device *pdev,
|
||||
struct rzn1_timer *tab_timers,
|
||||
void __iomem *base, unsigned long clock_rate)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rzn1_timer *timer;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Each timer instance has 6 x 16-bit timers and 2 x 32-bit timers.
|
||||
*
|
||||
* Probe the first instance. In that case, timers are assigned as
|
||||
* follow:
|
||||
* - First and second 16-bit timers: clock events per CPU
|
||||
* - Last (second one) 32-bit timer: clocksource and sched_clock
|
||||
* - Other timers: clock events for all possible CPUs
|
||||
*
|
||||
* First step, perform all operation that could fail without calling
|
||||
* clockevents_config_and_register(), sched_clock_register() nor
|
||||
* cpuhp_setup_state(). Those operation don't have unregister nor
|
||||
* teardown counterparts and so, once called, we cannot remove the
|
||||
* related resource.
|
||||
*/
|
||||
|
||||
/* First step for 16-bit timers and the first 32-bit timers. */
|
||||
ret = rzn1_timer_setup_timers(pdev, tab_timers,
|
||||
RZN1_TIMER_BASE_INDEX_16BIT_TIMERS,
|
||||
RZN1_TIMER_BASE_INDEX_32BIT_TIMERS,
|
||||
base, clock_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Second step, almost all operations that can fail have been called.
|
||||
* Timers are ready to work. Start with the last operation that can fail,
|
||||
* installing and invoking hotplug callbacks
|
||||
*/
|
||||
rzn1_tab_timers = tab_timers;
|
||||
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
|
||||
"clockevents/rzn1/global_timer:starting",
|
||||
rzn1_local_timer_starting_cpu,
|
||||
rzn1_local_timer_dying_cpu);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Setup CPU hotplug state failed\n");
|
||||
|
||||
/*
|
||||
* At this point, no more operations can fail. Perform remaining
|
||||
* operations. Starting by handling the last (second one) 32-bit timer
|
||||
*/
|
||||
timer = &tab_timers[RZN1_TIMER_LAST_INDEX_32BIT_TIMERS];
|
||||
|
||||
rzn1_timer_init(timer, RZN1_TIMER_LAST_INDEX_32BIT_TIMERS, base, clock_rate);
|
||||
rzn1_timer_config(timer, GENMASK(timer->width - 1, 0), true);
|
||||
|
||||
rzn1_sched_clock = timer;
|
||||
|
||||
sched_clock_register(rzn1_sched_read, rzn1_sched_clock->width, rzn1_sched_clock->rate);
|
||||
|
||||
rzn1_clocksource.mask = CLOCKSOURCE_MASK(rzn1_sched_clock->width);
|
||||
clocksource_register_hz(&rzn1_clocksource, rzn1_sched_clock->rate);
|
||||
|
||||
/*
|
||||
* Register clockevents for 16-bit timers (except the first and second
|
||||
* ones) and for the first 32-bit timer. The first and second 16-bit
|
||||
* timers clockevents are registered by CPU hotplug startup function set
|
||||
* previously by the cpuhp_setup_state() call.
|
||||
*/
|
||||
rzn1_timer_register_clkevt(tab_timers,
|
||||
RZN1_TIMER_BASE_INDEX_16BIT_TIMERS + 2,
|
||||
RZN1_TIMER_BASE_INDEX_32BIT_TIMERS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzn1_timer_probe_other(struct platform_device *pdev,
|
||||
struct rzn1_timer *tab_timers,
|
||||
void __iomem *base, unsigned long clock_rate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Probe other instance(s), i.e. not the first one. In that case,
|
||||
* all timers are used as clock events and available for all possible
|
||||
* CPUs
|
||||
*
|
||||
* First step, perform all operation that could fail without calling
|
||||
* clockevents_config_and_register(). Unregister counterpart does not
|
||||
* exist and so, once called, we cannot remove the related resource.
|
||||
*/
|
||||
ret = rzn1_timer_setup_timers(pdev, tab_timers,
|
||||
RZN1_TIMER_BASE_INDEX_16BIT_TIMERS,
|
||||
RZN1_TIMER_LAST_INDEX_32BIT_TIMERS,
|
||||
base, clock_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Second step, all operation that can fail have been called. We can
|
||||
* register our timers.
|
||||
*/
|
||||
rzn1_timer_register_clkevt(tab_timers,
|
||||
RZN1_TIMER_BASE_INDEX_16BIT_TIMERS,
|
||||
RZN1_TIMER_LAST_INDEX_32BIT_TIMERS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzn1_timer_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rzn1_timer *tab_timers;
|
||||
unsigned long clock_rate;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
tab_timers = devm_kcalloc(dev, RZN1_TIMER_NB_TIMERS, sizeof(*tab_timers),
|
||||
GFP_KERNEL);
|
||||
if (!tab_timers)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
clk = devm_clk_get_enabled(dev, "pclk");
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "Failed to get pclk\n");
|
||||
|
||||
clock_rate = clk_get_rate(clk);
|
||||
|
||||
scoped_guard(mutex, &rzn1_global_timers_lock) {
|
||||
ret = !rzn1_timer_instance ?
|
||||
rzn1_timer_probe_first(pdev, tab_timers, base, clock_rate) :
|
||||
rzn1_timer_probe_other(pdev, tab_timers, base, clock_rate);
|
||||
if (!ret)
|
||||
rzn1_timer_instance++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id rzn1_timer_of_match[] = {
|
||||
{ .compatible = "renesas,rzn1-timer", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver rzn1_timer_driver = {
|
||||
.driver = {
|
||||
.name = "rzn1_timer",
|
||||
.of_match_table = rzn1_timer_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver_probe(rzn1_timer_driver, rzn1_timer_probe);
|
||||
Loading…
Reference in New Issue
Block a user