162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/clocksource/zevio-timer.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/irq.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/of_irq.h> 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/clockchips.h> 1562306a36Sopenharmony_ci#include <linux/cpumask.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define IO_CURRENT_VAL 0x00 2062306a36Sopenharmony_ci#define IO_DIVIDER 0x04 2162306a36Sopenharmony_ci#define IO_CONTROL 0x08 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define IO_TIMER1 0x00 2462306a36Sopenharmony_ci#define IO_TIMER2 0x0C 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define IO_MATCH_BEGIN 0x18 2762306a36Sopenharmony_ci#define IO_MATCH(x) (IO_MATCH_BEGIN + ((x) << 2)) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define IO_INTR_STS 0x00 3062306a36Sopenharmony_ci#define IO_INTR_ACK 0x00 3162306a36Sopenharmony_ci#define IO_INTR_MSK 0x04 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define CNTL_STOP_TIMER (1 << 4) 3462306a36Sopenharmony_ci#define CNTL_RUN_TIMER (0 << 4) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define CNTL_INC (1 << 3) 3762306a36Sopenharmony_ci#define CNTL_DEC (0 << 3) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define CNTL_TOZERO 0 4062306a36Sopenharmony_ci#define CNTL_MATCH(x) ((x) + 1) 4162306a36Sopenharmony_ci#define CNTL_FOREVER 7 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* There are 6 match registers but we only use one. */ 4462306a36Sopenharmony_ci#define TIMER_MATCH 0 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define TIMER_INTR_MSK (1 << (TIMER_MATCH)) 4762306a36Sopenharmony_ci#define TIMER_INTR_ALL 0x3F 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct zevio_timer { 5062306a36Sopenharmony_ci void __iomem *base; 5162306a36Sopenharmony_ci void __iomem *timer1, *timer2; 5262306a36Sopenharmony_ci void __iomem *interrupt_regs; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci struct clk *clk; 5562306a36Sopenharmony_ci struct clock_event_device clkevt; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci char clocksource_name[64]; 5862306a36Sopenharmony_ci char clockevent_name[64]; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int zevio_timer_set_event(unsigned long delta, 6262306a36Sopenharmony_ci struct clock_event_device *dev) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct zevio_timer *timer = container_of(dev, struct zevio_timer, 6562306a36Sopenharmony_ci clkevt); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci writel(delta, timer->timer1 + IO_CURRENT_VAL); 6862306a36Sopenharmony_ci writel(CNTL_RUN_TIMER | CNTL_DEC | CNTL_MATCH(TIMER_MATCH), 6962306a36Sopenharmony_ci timer->timer1 + IO_CONTROL); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int zevio_timer_shutdown(struct clock_event_device *dev) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct zevio_timer *timer = container_of(dev, struct zevio_timer, 7762306a36Sopenharmony_ci clkevt); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Disable timer interrupts */ 8062306a36Sopenharmony_ci writel(0, timer->interrupt_regs + IO_INTR_MSK); 8162306a36Sopenharmony_ci writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK); 8262306a36Sopenharmony_ci /* Stop timer */ 8362306a36Sopenharmony_ci writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL); 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int zevio_timer_set_oneshot(struct clock_event_device *dev) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct zevio_timer *timer = container_of(dev, struct zevio_timer, 9062306a36Sopenharmony_ci clkevt); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Enable timer interrupts */ 9362306a36Sopenharmony_ci writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_MSK); 9462306a36Sopenharmony_ci writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK); 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic irqreturn_t zevio_timer_interrupt(int irq, void *dev_id) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct zevio_timer *timer = dev_id; 10162306a36Sopenharmony_ci u32 intr; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci intr = readl(timer->interrupt_regs + IO_INTR_ACK); 10462306a36Sopenharmony_ci if (!(intr & TIMER_INTR_MSK)) 10562306a36Sopenharmony_ci return IRQ_NONE; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_ACK); 10862306a36Sopenharmony_ci writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (timer->clkevt.event_handler) 11162306a36Sopenharmony_ci timer->clkevt.event_handler(&timer->clkevt); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return IRQ_HANDLED; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int __init zevio_timer_add(struct device_node *node) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct zevio_timer *timer; 11962306a36Sopenharmony_ci struct resource res; 12062306a36Sopenharmony_ci int irqnr, ret; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci timer = kzalloc(sizeof(*timer), GFP_KERNEL); 12362306a36Sopenharmony_ci if (!timer) 12462306a36Sopenharmony_ci return -ENOMEM; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci timer->base = of_iomap(node, 0); 12762306a36Sopenharmony_ci if (!timer->base) { 12862306a36Sopenharmony_ci ret = -EINVAL; 12962306a36Sopenharmony_ci goto error_free; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci timer->timer1 = timer->base + IO_TIMER1; 13262306a36Sopenharmony_ci timer->timer2 = timer->base + IO_TIMER2; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci timer->clk = of_clk_get(node, 0); 13562306a36Sopenharmony_ci if (IS_ERR(timer->clk)) { 13662306a36Sopenharmony_ci ret = PTR_ERR(timer->clk); 13762306a36Sopenharmony_ci pr_err("Timer clock not found! (error %d)\n", ret); 13862306a36Sopenharmony_ci goto error_unmap; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci timer->interrupt_regs = of_iomap(node, 1); 14262306a36Sopenharmony_ci irqnr = irq_of_parse_and_map(node, 0); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci of_address_to_resource(node, 0, &res); 14562306a36Sopenharmony_ci scnprintf(timer->clocksource_name, sizeof(timer->clocksource_name), 14662306a36Sopenharmony_ci "%llx.%pOFn_clocksource", 14762306a36Sopenharmony_ci (unsigned long long)res.start, node); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci scnprintf(timer->clockevent_name, sizeof(timer->clockevent_name), 15062306a36Sopenharmony_ci "%llx.%pOFn_clockevent", 15162306a36Sopenharmony_ci (unsigned long long)res.start, node); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (timer->interrupt_regs && irqnr) { 15462306a36Sopenharmony_ci timer->clkevt.name = timer->clockevent_name; 15562306a36Sopenharmony_ci timer->clkevt.set_next_event = zevio_timer_set_event; 15662306a36Sopenharmony_ci timer->clkevt.set_state_shutdown = zevio_timer_shutdown; 15762306a36Sopenharmony_ci timer->clkevt.set_state_oneshot = zevio_timer_set_oneshot; 15862306a36Sopenharmony_ci timer->clkevt.tick_resume = zevio_timer_set_oneshot; 15962306a36Sopenharmony_ci timer->clkevt.rating = 200; 16062306a36Sopenharmony_ci timer->clkevt.cpumask = cpu_possible_mask; 16162306a36Sopenharmony_ci timer->clkevt.features = CLOCK_EVT_FEAT_ONESHOT; 16262306a36Sopenharmony_ci timer->clkevt.irq = irqnr; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL); 16562306a36Sopenharmony_ci writel(0, timer->timer1 + IO_DIVIDER); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Start with timer interrupts disabled */ 16862306a36Sopenharmony_ci writel(0, timer->interrupt_regs + IO_INTR_MSK); 16962306a36Sopenharmony_ci writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Interrupt to occur when timer value matches 0 */ 17262306a36Sopenharmony_ci writel(0, timer->base + IO_MATCH(TIMER_MATCH)); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (request_irq(irqnr, zevio_timer_interrupt, 17562306a36Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, 17662306a36Sopenharmony_ci timer->clockevent_name, timer)) { 17762306a36Sopenharmony_ci pr_err("%s: request_irq() failed\n", 17862306a36Sopenharmony_ci timer->clockevent_name); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci clockevents_config_and_register(&timer->clkevt, 18262306a36Sopenharmony_ci clk_get_rate(timer->clk), 0x0001, 0xffff); 18362306a36Sopenharmony_ci pr_info("Added %s as clockevent\n", timer->clockevent_name); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci writel(CNTL_STOP_TIMER, timer->timer2 + IO_CONTROL); 18762306a36Sopenharmony_ci writel(0, timer->timer2 + IO_CURRENT_VAL); 18862306a36Sopenharmony_ci writel(0, timer->timer2 + IO_DIVIDER); 18962306a36Sopenharmony_ci writel(CNTL_RUN_TIMER | CNTL_FOREVER | CNTL_INC, 19062306a36Sopenharmony_ci timer->timer2 + IO_CONTROL); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci clocksource_mmio_init(timer->timer2 + IO_CURRENT_VAL, 19362306a36Sopenharmony_ci timer->clocksource_name, 19462306a36Sopenharmony_ci clk_get_rate(timer->clk), 19562306a36Sopenharmony_ci 200, 16, 19662306a36Sopenharmony_ci clocksource_mmio_readw_up); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci pr_info("Added %s as clocksource\n", timer->clocksource_name); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_cierror_unmap: 20262306a36Sopenharmony_ci iounmap(timer->base); 20362306a36Sopenharmony_cierror_free: 20462306a36Sopenharmony_ci kfree(timer); 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int __init zevio_timer_init(struct device_node *node) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return zevio_timer_add(node); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciTIMER_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_init); 214