162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Actions Semi Owl timer 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2012 Actions Semi Inc. 662306a36Sopenharmony_ci * Author: Actions Semi, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (c) 2017 SUSE Linux GmbH 962306a36Sopenharmony_ci * Author: Andreas Färber 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/clockchips.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/irqreturn.h> 1762306a36Sopenharmony_ci#include <linux/sched_clock.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_address.h> 2062306a36Sopenharmony_ci#include <linux/of_irq.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define OWL_Tx_CTL 0x0 2362306a36Sopenharmony_ci#define OWL_Tx_CMP 0x4 2462306a36Sopenharmony_ci#define OWL_Tx_VAL 0x8 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define OWL_Tx_CTL_PD BIT(0) 2762306a36Sopenharmony_ci#define OWL_Tx_CTL_INTEN BIT(1) 2862306a36Sopenharmony_ci#define OWL_Tx_CTL_EN BIT(2) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void __iomem *owl_timer_base; 3162306a36Sopenharmony_cistatic void __iomem *owl_clksrc_base; 3262306a36Sopenharmony_cistatic void __iomem *owl_clkevt_base; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic inline void owl_timer_reset(void __iomem *base) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci writel(0, base + OWL_Tx_CTL); 3762306a36Sopenharmony_ci writel(0, base + OWL_Tx_VAL); 3862306a36Sopenharmony_ci writel(0, base + OWL_Tx_CMP); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic inline void owl_timer_set_enabled(void __iomem *base, bool enabled) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci u32 ctl = readl(base + OWL_Tx_CTL); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* PD bit is cleared when set */ 4662306a36Sopenharmony_ci ctl &= ~OWL_Tx_CTL_PD; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (enabled) 4962306a36Sopenharmony_ci ctl |= OWL_Tx_CTL_EN; 5062306a36Sopenharmony_ci else 5162306a36Sopenharmony_ci ctl &= ~OWL_Tx_CTL_EN; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci writel(ctl, base + OWL_Tx_CTL); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic u64 notrace owl_timer_sched_read(void) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return (u64)readl(owl_clksrc_base + OWL_Tx_VAL); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int owl_timer_set_state_shutdown(struct clock_event_device *evt) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci owl_timer_set_enabled(owl_clkevt_base, false); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int owl_timer_set_state_oneshot(struct clock_event_device *evt) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci owl_timer_reset(owl_clkevt_base); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int owl_timer_tick_resume(struct clock_event_device *evt) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int owl_timer_set_next_event(unsigned long evt, 8162306a36Sopenharmony_ci struct clock_event_device *ev) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci void __iomem *base = owl_clkevt_base; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci owl_timer_set_enabled(base, false); 8662306a36Sopenharmony_ci writel(OWL_Tx_CTL_INTEN, base + OWL_Tx_CTL); 8762306a36Sopenharmony_ci writel(0, base + OWL_Tx_VAL); 8862306a36Sopenharmony_ci writel(evt, base + OWL_Tx_CMP); 8962306a36Sopenharmony_ci owl_timer_set_enabled(base, true); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct clock_event_device owl_clockevent = { 9562306a36Sopenharmony_ci .name = "owl_tick", 9662306a36Sopenharmony_ci .rating = 200, 9762306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT | 9862306a36Sopenharmony_ci CLOCK_EVT_FEAT_DYNIRQ, 9962306a36Sopenharmony_ci .set_state_shutdown = owl_timer_set_state_shutdown, 10062306a36Sopenharmony_ci .set_state_oneshot = owl_timer_set_state_oneshot, 10162306a36Sopenharmony_ci .tick_resume = owl_timer_tick_resume, 10262306a36Sopenharmony_ci .set_next_event = owl_timer_set_next_event, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic irqreturn_t owl_timer1_interrupt(int irq, void *dev_id) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct clock_event_device *evt = (struct clock_event_device *)dev_id; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci writel(OWL_Tx_CTL_PD, owl_clkevt_base + OWL_Tx_CTL); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci evt->event_handler(evt); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return IRQ_HANDLED; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int __init owl_timer_init(struct device_node *node) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct clk *clk; 11962306a36Sopenharmony_ci unsigned long rate; 12062306a36Sopenharmony_ci int timer1_irq, ret; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci owl_timer_base = of_io_request_and_map(node, 0, "owl-timer"); 12362306a36Sopenharmony_ci if (IS_ERR(owl_timer_base)) { 12462306a36Sopenharmony_ci pr_err("Can't map timer registers\n"); 12562306a36Sopenharmony_ci return PTR_ERR(owl_timer_base); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci owl_clksrc_base = owl_timer_base + 0x08; 12962306a36Sopenharmony_ci owl_clkevt_base = owl_timer_base + 0x14; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci timer1_irq = of_irq_get_byname(node, "timer1"); 13262306a36Sopenharmony_ci if (timer1_irq <= 0) { 13362306a36Sopenharmony_ci pr_err("Can't parse timer1 IRQ\n"); 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci clk = of_clk_get(node, 0); 13862306a36Sopenharmony_ci if (IS_ERR(clk)) { 13962306a36Sopenharmony_ci ret = PTR_ERR(clk); 14062306a36Sopenharmony_ci pr_err("Failed to get clock for clocksource (%d)\n", ret); 14162306a36Sopenharmony_ci return ret; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci rate = clk_get_rate(clk); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci owl_timer_reset(owl_clksrc_base); 14762306a36Sopenharmony_ci owl_timer_set_enabled(owl_clksrc_base, true); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci sched_clock_register(owl_timer_sched_read, 32, rate); 15062306a36Sopenharmony_ci ret = clocksource_mmio_init(owl_clksrc_base + OWL_Tx_VAL, node->name, 15162306a36Sopenharmony_ci rate, 200, 32, clocksource_mmio_readl_up); 15262306a36Sopenharmony_ci if (ret) { 15362306a36Sopenharmony_ci pr_err("Failed to register clocksource (%d)\n", ret); 15462306a36Sopenharmony_ci return ret; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci owl_timer_reset(owl_clkevt_base); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = request_irq(timer1_irq, owl_timer1_interrupt, IRQF_TIMER, 16062306a36Sopenharmony_ci "owl-timer", &owl_clockevent); 16162306a36Sopenharmony_ci if (ret) { 16262306a36Sopenharmony_ci pr_err("failed to request irq %d\n", timer1_irq); 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci owl_clockevent.cpumask = cpumask_of(0); 16762306a36Sopenharmony_ci owl_clockevent.irq = timer1_irq; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci clockevents_config_and_register(&owl_clockevent, rate, 17062306a36Sopenharmony_ci 0xf, 0xffffffff); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ciTIMER_OF_DECLARE(owl_s500, "actions,s500-timer", owl_timer_init); 17562306a36Sopenharmony_ciTIMER_OF_DECLARE(owl_s700, "actions,s700-timer", owl_timer_init); 17662306a36Sopenharmony_ciTIMER_OF_DECLARE(owl_s900, "actions,s900-timer", owl_timer_init); 177