// SPDX-License-Identifier: GPL-2.0-only/* * ARM Generic Memory Mapped Timer support * * Split from drivers/clocksource/arm_arch_timer.c * * Copyright (C) 2011 ARM Ltd. * All Rights Reserved */ #define pr_fmt(fmt) "arch_timer_mmio: " fmt #include <linux/clockchips.h>#include <linux/interrupt.h>#include <linux/io-64-nonatomic-lo-hi.h>#include <linux/of_irq.h>#include <linux/of_address.h>#include <linux/platform_device.h> #include <clocksource/arm_arch_timer.h> #define CNTTIDR 0x08#define CNTTIDR_VIRT(n) (BIT(1) << ((n) * 4)) #define CNTACR(n) (0x40 + ((n) * 4))#define CNTACR_RPCT BIT(0)#define CNTACR_RVCT BIT(1)#define CNTACR_RFRQ BIT(2)#define CNTACR_RVOFF BIT(3)#define CNTACR_RWVT BIT(4)#define CNTACR_RWPT BIT(5) #define CNTPCT_LO 0x00#define CNTVCT_LO 0x08#define CNTFRQ 0x10#define CNTP_CVAL_LO 0x20#define CNTP_CTL 0x2c#define CNTV_CVAL_LO 0x30#define CNTV_CTL 0x3c enum arch_timer_access { PHYS_ACCESS, VIRT_ACCESS,}; struct arch_timer { struct clock_event_device evt; struct clocksource cs; struct arch_timer_mem *gt_block; void __iomem *base; enum arch_timer_access access; u32 rate;}; #define evt_to_arch_timer(e) container_of(e, struct arch_timer, evt)#define cs_to_arch_timer(c) container_of(c, struct arch_timer, cs) static void arch_timer_mmio_write(struct arch_timer *timer, enum arch_timer_reg reg, u64 val){ switch (timer->access) { case PHYS_ACCESS: switch (reg) { case ARCH_TIMER_REG_CTRL: writel_relaxed((u32)val, timer->base + CNTP_CTL); return; case ARCH_TIMER_REG_CVAL: /* * Not guaranteed to be atomic, so the timer * must be disabled at this point. */ writeq_relaxed(val, timer->base + CNTP_CVAL_LO); return; } break; case VIRT_ACCESS: switch (reg) { case ARCH_TIMER_REG_CTRL: writel_relaxed((u32)val, timer->base + CNTV_CTL); return; case ARCH_TIMER_REG_CVAL: /* Same restriction as above */ writeq_relaxed(val, timer->base + CNTV_CVAL_LO); return; } break; } /* Should never be here */ WARN_ON_ONCE(1);} static u32 arch_timer_mmio_read(struct arch_timer *timer, enum arch_timer_reg reg){ switch (timer->access) { case PHYS_ACCESS: switch (reg) { case ARCH_TIMER_REG_CTRL: return readl_relaxed(timer->base + CNTP_CTL); default: break; } break; case VIRT_ACCESS: switch (reg) { case ARCH_TIMER_REG_CTRL: return readl_relaxed(timer->base + CNTV_CTL); default: break; } break; } /* Should never be here */ WARN_ON_ONCE(1); return 0;} static noinstr u64 arch_counter_mmio_get_cnt(struct arch_timer *t){ int offset_lo = t->access == VIRT_ACCESS ? CNTVCT_LO : CNTPCT_LO; u32 cnt_lo, cnt_hi, tmp_hi; do { cnt_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4)); cnt_lo = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo)); tmp_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4)); } while (cnt_hi != tmp_hi); return ((u64) cnt_hi << 32) | cnt_lo;} static u64 arch_mmio_counter_read(struct clocksource *cs){ struct arch_timer *at = cs_to_arch_timer(cs); return arch_counter_mmio_get_cnt(at);} static int arch_timer_mmio_shutdown(struct clock_event_device *clk){ struct arch_timer *at = evt_to_arch_timer(clk); unsigned long ctrl; ctrl = arch_timer_mmio_read(at, ARCH_TIMER_REG_CTRL); ctrl &= ~ARCH_TIMER_CTRL_ENABLE; arch_timer_mmio_write(at, ARCH_TIMER_REG_CTRL, ctrl); return 0;} static int arch_timer_mmio_set_next_event(unsigned long evt, struct clock_event_device *clk){ struct arch_timer *timer = evt_to_arch_timer(clk); unsigned long ctrl; u64 cnt; ctrl = arch_timer_mmio_read(timer, ARCH_TIMER_REG_CTRL); /* Timer must be disabled before programming CVAL */ if (ctrl & ARCH_TIMER_CTRL_ENABLE) { ctrl &= ~ARCH_TIMER_CTRL_ENABLE; arch_timer_mmio_write(timer, ARCH_TIMER_REG_CTRL, ctrl); } ctrl |= ARCH_TIMER_CTRL_ENABLE; ctrl &= ~ARCH_TIMER_CTRL_IT_MASK; cnt = arch_counter_mmio_get_cnt(timer); arch_timer_mmio_write(timer, ARCH_TIMER_REG_CVAL, evt + cnt); arch_timer_mmio_write(timer, ARCH_TIMER_REG_CTRL, ctrl); return 0;} static irqreturn_t arch_timer_mmio_handler(int irq, void *dev_id){ struct clock_event_device *evt = dev_id; struct arch_timer *at = evt_to_arch_timer(evt); unsigned long ctrl; ctrl = arch_timer_mmio_read(at, ARCH_TIMER_REG_CTRL); if (ctrl & ARCH_TIMER_CTRL_IT_STAT) { ctrl |= ARCH_TIMER_CTRL_IT_MASK; arch_timer_mmio_write(at, ARCH_TIMER_REG_CTRL, ctrl); evt->event_handler(evt); return IRQ_HANDLED; } return IRQ_NONE;} static struct arch_timer_mem_frame *find_best_frame(struct platform_device *pdev){ struct arch_timer_mem_frame *frame, *best_frame = NULL; struct arch_timer *at = platform_get_drvdata(pdev); void __iomem *cntctlbase; u32 cnttidr; cntctlbase = ioremap(at->gt_block->cntctlbase, at->gt_block->size); if (!cntctlbase) { dev_err(&pdev->dev, "Can't map CNTCTLBase @ %pa\n", &at->gt_block->cntctlbase); return NULL; } cnttidr = readl_relaxed(cntctlbase + CNTTIDR); /* * Try to find a virtual capable frame. Otherwise fall back to a * physical capable frame. */ for (int i = 0; i < ARCH_TIMER_MEM_MAX_FRAMES; i++) { u32 cntacr = CNTACR_RFRQ | CNTACR_RWPT | CNTACR_RPCT | CNTACR_RWVT | CNTACR_RVOFF | CNTACR_RVCT; frame = &at->gt_block->frame[i]; if (!frame->valid) continue; /* Try enabling everything, and see what sticks */ writel_relaxed(cntacr, cntctlbase + CNTACR(i)); cntacr = readl_relaxed(cntctlbase + CNTACR(i)); /* Pick a suitable frame for which we have an IRQ */ if ((cnttidr & CNTTIDR_VIRT(i)) && !(~cntacr & (CNTACR_RWVT | CNTACR_RVCT)) && frame->virt_irq) { best_frame = frame; at->access = VIRT_ACCESS; break; } if ((~cntacr & (CNTACR_RWPT | CNTACR_RPCT)) || !frame->phys_irq) continue; at->access = PHYS_ACCESS; best_frame = frame; } iounmap(cntctlbase); return best_frame;} static void arch_timer_mmio_setup(struct arch_timer *at, int irq){ at->evt = (struct clock_event_device) { .features = (CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ), .name = "arch_mem_timer", .rating = 400, .cpumask = cpu_possible_mask, .irq = irq, .set_next_event = arch_timer_mmio_set_next_event, .set_state_oneshot_stopped = arch_timer_mmio_shutdown, .set_state_shutdown = arch_timer_mmio_shutdown, }; at->evt.set_state_shutdown(&at->evt); clockevents_config_and_register(&at->evt, at->rate, 0xf, (unsigned long)CLOCKSOURCE_MASK(56)); enable_irq(at->evt.irq); at->cs = (struct clocksource) { .name = "arch_mmio_counter", .rating = 300, .read = arch_mmio_counter_read, .mask = CLOCKSOURCE_MASK(56), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; clocksource_register_hz(&at->cs, at->rate);} static int arch_timer_mmio_frame_register(struct platform_device *pdev, struct arch_timer_mem_frame *frame){ struct arch_timer *at = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; int ret, irq; u32 rate; if (!devm_request_mem_region(&pdev->dev, frame->cntbase, frame->size, "arch_mem_timer")) return -EBUSY; at->base = devm_ioremap(&pdev->dev, frame->cntbase, frame->size); if (!at->base) { dev_err(&pdev->dev, "Can't map frame's registers\n"); return -ENXIO; } /* * Allow "clock-frequency" to override the probed rate. If neither * lead to something useful, use the CPU timer frequency as the * fallback. The nice thing about that last point is that we woudn't * made it here if we didn't have a valid frequency. */ rate = readl_relaxed(at->base + CNTFRQ); if (!np || of_property_read_u32(np, "clock-frequency", &at->rate)) at->rate = rate; if (!at->rate) at->rate = arch_timer_get_rate(); irq = at->access == VIRT_ACCESS ? frame->virt_irq : frame->phys_irq; ret = devm_request_irq(&pdev->dev, irq, arch_timer_mmio_handler, IRQF_TIMER | IRQF_NO_AUTOEN, "arch_mem_timer", &at->evt); if (ret) return ret; /* Afer this point, we're not allowed to fail anymore */ arch_timer_mmio_setup(at, irq); return 0;} static int of_populate_gt_block(struct platform_device *pdev, struct arch_timer *at){ struct resource res; if (of_address_to_resource(pdev->dev.of_node, 0, &res)) return -EINVAL; at->gt_block->cntctlbase = res.start; at->gt_block->size = resource_size(&res); for_each_available_child_of_node_scoped(pdev->dev.of_node, frame_node) { struct arch_timer_mem_frame *frame; u32 n; if (of_property_read_u32(frame_node, "frame-number", &n)) { dev_err(&pdev->dev, FW_BUG "Missing frame-number\n"); return -EINVAL; } if (n >= ARCH_TIMER_MEM_MAX_FRAMES) { dev_err(&pdev->dev, FW_BUG "Wrong frame-number, only 0-%u are permitted\n", ARCH_TIMER_MEM_MAX_FRAMES - 1); return -EINVAL; } frame = &at->gt_block->frame[n]; if (frame->valid) { dev_err(&pdev->dev, FW_BUG "Duplicated frame-number\n"); return -EINVAL; } if (of_address_to_resource(frame_node, 0, &res)) return -EINVAL; frame->cntbase = res.start; frame->size = resource_size(&res); frame->phys_irq = irq_of_parse_and_map(frame_node, 0); frame->virt_irq = irq_of_parse_and_map(frame_node, 1); frame->valid = true; } return 0;} static int arch_timer_mmio_probe(struct platform_device *pdev){ struct arch_timer_mem_frame *frame; struct arch_timer *at; struct device_node *np; int ret; np = pdev->dev.of_node; at = devm_kmalloc(&pdev->dev, sizeof(*at), GFP_KERNEL | __GFP_ZERO); if (!at) return -ENOMEM; if (np) { at->gt_block = devm_kmalloc(&pdev->dev, sizeof(*at->gt_block), GFP_KERNEL | __GFP_ZERO); if (!at->gt_block) return -ENOMEM; ret = of_populate_gt_block(pdev, at); if (ret) return ret; } else { at->gt_block = dev_get_platdata(&pdev->dev); } platform_set_drvdata(pdev, at); frame = find_best_frame(pdev); if (!frame) { dev_err(&pdev->dev, "Unable to find a suitable frame in timer @ %pa\n", &at->gt_block->cntctlbase); return -EINVAL; } ret = arch_timer_mmio_frame_register(pdev, frame); if (!ret) dev_info(&pdev->dev, "mmio timer running at %lu.%02luMHz (%s)\n", (unsigned long)at->rate / 1000000, (unsigned long)(at->rate / 10000) % 100, at->access == VIRT_ACCESS ? "virt" : "phys"); return ret;} static const struct of_device_id arch_timer_mmio_of_table[] = { { .compatible = "arm,armv7-timer-mem", }, {}}; static struct platform_driver arch_timer_mmio_drv = { .driver = { .name = "arch-timer-mmio", .of_match_table = arch_timer_mmio_of_table, .suppress_bind_attrs = true, }, .probe = arch_timer_mmio_probe,};builtin_platform_driver(arch_timer_mmio_drv); static struct platform_driver arch_timer_mmio_acpi_drv = { .driver = { .name = "gtdt-arm-mmio-timer", .suppress_bind_attrs = true, }, .probe = arch_timer_mmio_probe,};builtin_platform_driver(arch_timer_mmio_acpi_drv);
// SPDX-License-Identifier: GPL-2.0-only
/*
* ARM Generic Memory Mapped Timer support
*
* Split from drivers/clocksource/arm_arch_timer.c
*
* Copyright (C) 2011 ARM Ltd.
* All Rights Reserved
*/
#define pr_fmt(fmt) "arch_timer_mmio: " fmt
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <clocksource/arm_arch_timer.h>
#define CNTTIDR 0x08
#define CNTTIDR_VIRT(n) (BIT(1) << ((n) * 4))
#define CNTACR(n) (0x40 + ((n) * 4))
#define CNTACR_RPCT BIT(0)
#define CNTACR_RVCT BIT(1)
#define CNTACR_RFRQ BIT(2)
#define CNTACR_RVOFF BIT(3)
#define CNTACR_RWVT BIT(4)
#define CNTACR_RWPT BIT(5)
#define CNTPCT_LO 0x00
#define CNTVCT_LO 0x08
#define CNTFRQ 0x10
#define CNTP_CVAL_LO 0x20
#define CNTP_CTL 0x2c
#define CNTV_CVAL_LO 0x30
#define CNTV_CTL 0x3c
enum arch_timer_access {
PHYS_ACCESS,
VIRT_ACCESS,
};
struct arch_timer {
struct clock_event_device evt;
struct clocksource cs;
struct arch_timer_mem *gt_block;
void __iomem *base;
enum arch_timer_access access;
u32 rate;
};
#define evt_to_arch_timer(e) container_of(e, struct arch_timer, evt)
#define cs_to_arch_timer(c) container_of(c, struct arch_timer, cs)
static void arch_timer_mmio_write(struct arch_timer *timer,
enum arch_timer_reg reg, u64 val)
{
switch (timer->access) {
case PHYS_ACCESS:
switch (reg) {
case ARCH_TIMER_REG_CTRL:
writel_relaxed((u32)val, timer->base + CNTP_CTL);
return;
case ARCH_TIMER_REG_CVAL:
/*
* Not guaranteed to be atomic, so the timer
* must be disabled at this point.
*/
writeq_relaxed(val, timer->base + CNTP_CVAL_LO);
return;
}
break;
case VIRT_ACCESS:
switch (reg) {
case ARCH_TIMER_REG_CTRL:
writel_relaxed((u32)val, timer->base + CNTV_CTL);
return;
case ARCH_TIMER_REG_CVAL:
/* Same restriction as above */
writeq_relaxed(val, timer->base + CNTV_CVAL_LO);
return;
}
break;
}
/* Should never be here */
WARN_ON_ONCE(1);
}
static u32 arch_timer_mmio_read(struct arch_timer *timer, enum arch_timer_reg reg)
{
switch (timer->access) {
case PHYS_ACCESS:
switch (reg) {
case ARCH_TIMER_REG_CTRL:
return readl_relaxed(timer->base + CNTP_CTL);
default:
break;
}
break;
case VIRT_ACCESS:
switch (reg) {
case ARCH_TIMER_REG_CTRL:
return readl_relaxed(timer->base + CNTV_CTL);
default:
break;
}
break;
}
/* Should never be here */
WARN_ON_ONCE(1);
return 0;
}
static noinstr u64 arch_counter_mmio_get_cnt(struct arch_timer *t)
{
int offset_lo = t->access == VIRT_ACCESS ? CNTVCT_LO : CNTPCT_LO;
u32 cnt_lo, cnt_hi, tmp_hi;
do {
cnt_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4));
cnt_lo = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo));
tmp_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4));
} while (cnt_hi != tmp_hi);
return ((u64) cnt_hi << 32) | cnt_lo;
}
static u64 arch_mmio_counter_read(struct clocksource *cs)
{
struct arch_timer *at = cs_to_arch_timer(cs);
return arch_counter_mmio_get_cnt(at);
}
static int arch_timer_mmio_shutdown(struct clock_event_device *clk)
{
struct arch_timer *at = evt_to_arch_timer(clk);
unsigned long ctrl;
ctrl = arch_timer_mmio_read(at, ARCH_TIMER_REG_CTRL);
ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
arch_timer_mmio_write(at, ARCH_TIMER_REG_CTRL, ctrl);
return 0;
}
static int arch_timer_mmio_set_next_event(unsigned long evt,
struct clock_event_device *clk)
{
struct arch_timer *timer = evt_to_arch_timer(clk);
unsigned long ctrl;
u64 cnt;
ctrl = arch_timer_mmio_read(timer, ARCH_TIMER_REG_CTRL);
/* Timer must be disabled before programming CVAL */
if (ctrl & ARCH_TIMER_CTRL_ENABLE) {
ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
arch_timer_mmio_write(timer, ARCH_TIMER_REG_CTRL, ctrl);
}
ctrl |= ARCH_TIMER_CTRL_ENABLE;
ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
cnt = arch_counter_mmio_get_cnt(timer);
arch_timer_mmio_write(timer, ARCH_TIMER_REG_CVAL, evt + cnt);
arch_timer_mmio_write(timer, ARCH_TIMER_REG_CTRL, ctrl);
return 0;
}
static irqreturn_t arch_timer_mmio_handler(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
struct arch_timer *at = evt_to_arch_timer(evt);
unsigned long ctrl;
ctrl = arch_timer_mmio_read(at, ARCH_TIMER_REG_CTRL);
if (ctrl & ARCH_TIMER_CTRL_IT_STAT) {
ctrl |= ARCH_TIMER_CTRL_IT_MASK;
arch_timer_mmio_write(at, ARCH_TIMER_REG_CTRL, ctrl);
evt->event_handler(evt);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static struct arch_timer_mem_frame *find_best_frame(struct platform_device *pdev)
{
struct arch_timer_mem_frame *frame, *best_frame = NULL;
struct arch_timer *at = platform_get_drvdata(pdev);
void __iomem *cntctlbase;
u32 cnttidr;
cntctlbase = ioremap(at->gt_block->cntctlbase, at->gt_block->size);
if (!cntctlbase) {
dev_err(&pdev->dev, "Can't map CNTCTLBase @ %pa\n",
&at->gt_block->cntctlbase);
return NULL;
}
cnttidr = readl_relaxed(cntctlbase + CNTTIDR);
/*
* Try to find a virtual capable frame. Otherwise fall back to a
* physical capable frame.
*/
for (int i = 0; i < ARCH_TIMER_MEM_MAX_FRAMES; i++) {
u32 cntacr = CNTACR_RFRQ | CNTACR_RWPT | CNTACR_RPCT |
CNTACR_RWVT | CNTACR_RVOFF | CNTACR_RVCT;
frame = &at->gt_block->frame[i];
if (!frame->valid)
continue;
/* Try enabling everything, and see what sticks */
writel_relaxed(cntacr, cntctlbase + CNTACR(i));
cntacr = readl_relaxed(cntctlbase + CNTACR(i));
/* Pick a suitable frame for which we have an IRQ */
if ((cnttidr & CNTTIDR_VIRT(i)) &&
!(~cntacr & (CNTACR_RWVT | CNTACR_RVCT)) &&
frame->virt_irq) {
best_frame = frame;
at->access = VIRT_ACCESS;
break;
}
if ((~cntacr & (CNTACR_RWPT | CNTACR_RPCT)) ||
!frame->phys_irq)
continue;
at->access = PHYS_ACCESS;
best_frame = frame;
}
iounmap(cntctlbase);
return best_frame;
}
static void arch_timer_mmio_setup(struct arch_timer *at, int irq)
{
at->evt = (struct clock_event_device) {
.features = (CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_DYNIRQ),
.name = "arch_mem_timer",
.rating = 400,
.cpumask = cpu_possible_mask,
.irq = irq,
.set_next_event = arch_timer_mmio_set_next_event,
.set_state_oneshot_stopped = arch_timer_mmio_shutdown,
.set_state_shutdown = arch_timer_mmio_shutdown,
};
at->evt.set_state_shutdown(&at->evt);
clockevents_config_and_register(&at->evt, at->rate, 0xf,
(unsigned long)CLOCKSOURCE_MASK(56));
enable_irq(at->evt.irq);
at->cs = (struct clocksource) {
.name = "arch_mmio_counter",
.rating = 300,
.read = arch_mmio_counter_read,
.mask = CLOCKSOURCE_MASK(56),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
clocksource_register_hz(&at->cs, at->rate);
}
static int arch_timer_mmio_frame_register(struct platform_device *pdev,
struct arch_timer_mem_frame *frame)
{
struct arch_timer *at = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node;
int ret, irq;
u32 rate;
if (!devm_request_mem_region(&pdev->dev, frame->cntbase, frame->size,
"arch_mem_timer"))
return -EBUSY;
at->base = devm_ioremap(&pdev->dev, frame->cntbase, frame->size);
if (!at->base) {
dev_err(&pdev->dev, "Can't map frame's registers\n");
return -ENXIO;
}
/*
* Allow "clock-frequency" to override the probed rate. If neither
* lead to something useful, use the CPU timer frequency as the
* fallback. The nice thing about that last point is that we woudn't
* made it here if we didn't have a valid frequency.
*/
rate = readl_relaxed(at->base + CNTFRQ);
if (!np || of_property_read_u32(np, "clock-frequency", &at->rate))
at->rate = rate;
if (!at->rate)
at->rate = arch_timer_get_rate();
irq = at->access == VIRT_ACCESS ? frame->virt_irq : frame->phys_irq;
ret = devm_request_irq(&pdev->dev, irq, arch_timer_mmio_handler,
IRQF_TIMER | IRQF_NO_AUTOEN, "arch_mem_timer",
&at->evt);
if (ret)
return ret;
/* Afer this point, we're not allowed to fail anymore */
arch_timer_mmio_setup(at, irq);
return 0;
}
static int of_populate_gt_block(struct platform_device *pdev,
struct arch_timer *at)
{
struct resource res;
if (of_address_to_resource(pdev->dev.of_node, 0, &res))
return -EINVAL;
at->gt_block->cntctlbase = res.start;
at->gt_block->size = resource_size(&res);
for_each_available_child_of_node_scoped(pdev->dev.of_node, frame_node) {
struct arch_timer_mem_frame *frame;
u32 n;
if (of_property_read_u32(frame_node, "frame-number", &n)) {
dev_err(&pdev->dev, FW_BUG "Missing frame-number\n");
return -EINVAL;
}
if (n >= ARCH_TIMER_MEM_MAX_FRAMES) {
dev_err(&pdev->dev,
FW_BUG "Wrong frame-number, only 0-%u are permitted\n",
ARCH_TIMER_MEM_MAX_FRAMES - 1);
return -EINVAL;
}
frame = &at->gt_block->frame[n];
if (frame->valid) {
dev_err(&pdev->dev, FW_BUG "Duplicated frame-number\n");
return -EINVAL;
}
if (of_address_to_resource(frame_node, 0, &res))
return -EINVAL;
frame->cntbase = res.start;
frame->size = resource_size(&res);
frame->phys_irq = irq_of_parse_and_map(frame_node, 0);
frame->virt_irq = irq_of_parse_and_map(frame_node, 1);
frame->valid = true;
}
return 0;
}
static int arch_timer_mmio_probe(struct platform_device *pdev)
{
struct arch_timer_mem_frame *frame;
struct arch_timer *at;
struct device_node *np;
int ret;
np = pdev->dev.of_node;
at = devm_kmalloc(&pdev->dev, sizeof(*at), GFP_KERNEL | __GFP_ZERO);
if (!at)
return -ENOMEM;
if (np) {
at->gt_block = devm_kmalloc(&pdev->dev, sizeof(*at->gt_block),
GFP_KERNEL | __GFP_ZERO);
if (!at->gt_block)
return -ENOMEM;
ret = of_populate_gt_block(pdev, at);
if (ret)
return ret;
} else {
at->gt_block = dev_get_platdata(&pdev->dev);
}
platform_set_drvdata(pdev, at);
frame = find_best_frame(pdev);
if (!frame) {
dev_err(&pdev->dev,
"Unable to find a suitable frame in timer @ %pa\n",
&at->gt_block->cntctlbase);
return -EINVAL;
}
ret = arch_timer_mmio_frame_register(pdev, frame);
if (!ret)
dev_info(&pdev->dev,
"mmio timer running at %lu.%02luMHz (%s)\n",
(unsigned long)at->rate / 1000000,
(unsigned long)(at->rate / 10000) % 100,
at->access == VIRT_ACCESS ? "virt" : "phys");
return ret;
}
static const struct of_device_id arch_timer_mmio_of_table[] = {
{ .compatible = "arm,armv7-timer-mem", },
{}
};
static struct platform_driver arch_timer_mmio_drv = {
.driver = {
.name = "arch-timer-mmio",
.of_match_table = arch_timer_mmio_of_table,
.suppress_bind_attrs = true,
},
.probe = arch_timer_mmio_probe,
};
builtin_platform_driver(arch_timer_mmio_drv);
static struct platform_driver arch_timer_mmio_acpi_drv = {
.driver = {
.name = "gtdt-arm-mmio-timer",
.suppress_bind_attrs = true,
},
.probe = arch_timer_mmio_probe,
};
builtin_platform_driver(arch_timer_mmio_acpi_drv);