| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- // 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) {
- dev_err(&pdev->dev, "Failed to request mem timer irq\n");
- 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);
|