12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29d8d47eaSDaniel Lezcano /*
39d8d47eaSDaniel Lezcano * Actions Semi Owl timer
49d8d47eaSDaniel Lezcano *
59d8d47eaSDaniel Lezcano * Copyright 2012 Actions Semi Inc.
69d8d47eaSDaniel Lezcano * Author: Actions Semi, Inc.
79d8d47eaSDaniel Lezcano *
89d8d47eaSDaniel Lezcano * Copyright (c) 2017 SUSE Linux GmbH
99d8d47eaSDaniel Lezcano * Author: Andreas Färber
109d8d47eaSDaniel Lezcano */
119d8d47eaSDaniel Lezcano
129d8d47eaSDaniel Lezcano #include <linux/clk.h>
139d8d47eaSDaniel Lezcano #include <linux/clockchips.h>
149d8d47eaSDaniel Lezcano #include <linux/interrupt.h>
159d8d47eaSDaniel Lezcano #include <linux/irq.h>
169d8d47eaSDaniel Lezcano #include <linux/irqreturn.h>
179d8d47eaSDaniel Lezcano #include <linux/sched_clock.h>
189d8d47eaSDaniel Lezcano #include <linux/of.h>
199d8d47eaSDaniel Lezcano #include <linux/of_address.h>
209d8d47eaSDaniel Lezcano #include <linux/of_irq.h>
219d8d47eaSDaniel Lezcano
229d8d47eaSDaniel Lezcano #define OWL_Tx_CTL 0x0
239d8d47eaSDaniel Lezcano #define OWL_Tx_CMP 0x4
249d8d47eaSDaniel Lezcano #define OWL_Tx_VAL 0x8
259d8d47eaSDaniel Lezcano
269d8d47eaSDaniel Lezcano #define OWL_Tx_CTL_PD BIT(0)
279d8d47eaSDaniel Lezcano #define OWL_Tx_CTL_INTEN BIT(1)
289d8d47eaSDaniel Lezcano #define OWL_Tx_CTL_EN BIT(2)
299d8d47eaSDaniel Lezcano
309d8d47eaSDaniel Lezcano static void __iomem *owl_timer_base;
319d8d47eaSDaniel Lezcano static void __iomem *owl_clksrc_base;
329d8d47eaSDaniel Lezcano static void __iomem *owl_clkevt_base;
339d8d47eaSDaniel Lezcano
owl_timer_reset(void __iomem * base)349d8d47eaSDaniel Lezcano static inline void owl_timer_reset(void __iomem *base)
359d8d47eaSDaniel Lezcano {
369d8d47eaSDaniel Lezcano writel(0, base + OWL_Tx_CTL);
379d8d47eaSDaniel Lezcano writel(0, base + OWL_Tx_VAL);
389d8d47eaSDaniel Lezcano writel(0, base + OWL_Tx_CMP);
399d8d47eaSDaniel Lezcano }
409d8d47eaSDaniel Lezcano
owl_timer_set_enabled(void __iomem * base,bool enabled)419d8d47eaSDaniel Lezcano static inline void owl_timer_set_enabled(void __iomem *base, bool enabled)
429d8d47eaSDaniel Lezcano {
439d8d47eaSDaniel Lezcano u32 ctl = readl(base + OWL_Tx_CTL);
449d8d47eaSDaniel Lezcano
459d8d47eaSDaniel Lezcano /* PD bit is cleared when set */
469d8d47eaSDaniel Lezcano ctl &= ~OWL_Tx_CTL_PD;
479d8d47eaSDaniel Lezcano
489d8d47eaSDaniel Lezcano if (enabled)
499d8d47eaSDaniel Lezcano ctl |= OWL_Tx_CTL_EN;
509d8d47eaSDaniel Lezcano else
519d8d47eaSDaniel Lezcano ctl &= ~OWL_Tx_CTL_EN;
529d8d47eaSDaniel Lezcano
539d8d47eaSDaniel Lezcano writel(ctl, base + OWL_Tx_CTL);
549d8d47eaSDaniel Lezcano }
559d8d47eaSDaniel Lezcano
owl_timer_sched_read(void)569d8d47eaSDaniel Lezcano static u64 notrace owl_timer_sched_read(void)
579d8d47eaSDaniel Lezcano {
589d8d47eaSDaniel Lezcano return (u64)readl(owl_clksrc_base + OWL_Tx_VAL);
599d8d47eaSDaniel Lezcano }
609d8d47eaSDaniel Lezcano
owl_timer_set_state_shutdown(struct clock_event_device * evt)619d8d47eaSDaniel Lezcano static int owl_timer_set_state_shutdown(struct clock_event_device *evt)
629d8d47eaSDaniel Lezcano {
639d8d47eaSDaniel Lezcano owl_timer_set_enabled(owl_clkevt_base, false);
649d8d47eaSDaniel Lezcano
659d8d47eaSDaniel Lezcano return 0;
669d8d47eaSDaniel Lezcano }
679d8d47eaSDaniel Lezcano
owl_timer_set_state_oneshot(struct clock_event_device * evt)689d8d47eaSDaniel Lezcano static int owl_timer_set_state_oneshot(struct clock_event_device *evt)
699d8d47eaSDaniel Lezcano {
709d8d47eaSDaniel Lezcano owl_timer_reset(owl_clkevt_base);
719d8d47eaSDaniel Lezcano
729d8d47eaSDaniel Lezcano return 0;
739d8d47eaSDaniel Lezcano }
749d8d47eaSDaniel Lezcano
owl_timer_tick_resume(struct clock_event_device * evt)759d8d47eaSDaniel Lezcano static int owl_timer_tick_resume(struct clock_event_device *evt)
769d8d47eaSDaniel Lezcano {
779d8d47eaSDaniel Lezcano return 0;
789d8d47eaSDaniel Lezcano }
799d8d47eaSDaniel Lezcano
owl_timer_set_next_event(unsigned long evt,struct clock_event_device * ev)809d8d47eaSDaniel Lezcano static int owl_timer_set_next_event(unsigned long evt,
819d8d47eaSDaniel Lezcano struct clock_event_device *ev)
829d8d47eaSDaniel Lezcano {
839d8d47eaSDaniel Lezcano void __iomem *base = owl_clkevt_base;
849d8d47eaSDaniel Lezcano
859d8d47eaSDaniel Lezcano owl_timer_set_enabled(base, false);
869d8d47eaSDaniel Lezcano writel(OWL_Tx_CTL_INTEN, base + OWL_Tx_CTL);
879d8d47eaSDaniel Lezcano writel(0, base + OWL_Tx_VAL);
889d8d47eaSDaniel Lezcano writel(evt, base + OWL_Tx_CMP);
899d8d47eaSDaniel Lezcano owl_timer_set_enabled(base, true);
909d8d47eaSDaniel Lezcano
919d8d47eaSDaniel Lezcano return 0;
929d8d47eaSDaniel Lezcano }
939d8d47eaSDaniel Lezcano
949d8d47eaSDaniel Lezcano static struct clock_event_device owl_clockevent = {
959d8d47eaSDaniel Lezcano .name = "owl_tick",
969d8d47eaSDaniel Lezcano .rating = 200,
979d8d47eaSDaniel Lezcano .features = CLOCK_EVT_FEAT_ONESHOT |
989d8d47eaSDaniel Lezcano CLOCK_EVT_FEAT_DYNIRQ,
999d8d47eaSDaniel Lezcano .set_state_shutdown = owl_timer_set_state_shutdown,
1009d8d47eaSDaniel Lezcano .set_state_oneshot = owl_timer_set_state_oneshot,
1019d8d47eaSDaniel Lezcano .tick_resume = owl_timer_tick_resume,
1029d8d47eaSDaniel Lezcano .set_next_event = owl_timer_set_next_event,
1039d8d47eaSDaniel Lezcano };
1049d8d47eaSDaniel Lezcano
owl_timer1_interrupt(int irq,void * dev_id)1059d8d47eaSDaniel Lezcano static irqreturn_t owl_timer1_interrupt(int irq, void *dev_id)
1069d8d47eaSDaniel Lezcano {
1079d8d47eaSDaniel Lezcano struct clock_event_device *evt = (struct clock_event_device *)dev_id;
1089d8d47eaSDaniel Lezcano
1099d8d47eaSDaniel Lezcano writel(OWL_Tx_CTL_PD, owl_clkevt_base + OWL_Tx_CTL);
1109d8d47eaSDaniel Lezcano
1119d8d47eaSDaniel Lezcano evt->event_handler(evt);
1129d8d47eaSDaniel Lezcano
1139d8d47eaSDaniel Lezcano return IRQ_HANDLED;
1149d8d47eaSDaniel Lezcano }
1159d8d47eaSDaniel Lezcano
owl_timer_init(struct device_node * node)1169d8d47eaSDaniel Lezcano static int __init owl_timer_init(struct device_node *node)
1179d8d47eaSDaniel Lezcano {
1189d8d47eaSDaniel Lezcano struct clk *clk;
1199d8d47eaSDaniel Lezcano unsigned long rate;
1209d8d47eaSDaniel Lezcano int timer1_irq, ret;
1219d8d47eaSDaniel Lezcano
1229d8d47eaSDaniel Lezcano owl_timer_base = of_io_request_and_map(node, 0, "owl-timer");
1239d8d47eaSDaniel Lezcano if (IS_ERR(owl_timer_base)) {
1249d8d47eaSDaniel Lezcano pr_err("Can't map timer registers\n");
1259d8d47eaSDaniel Lezcano return PTR_ERR(owl_timer_base);
1269d8d47eaSDaniel Lezcano }
1279d8d47eaSDaniel Lezcano
1289d8d47eaSDaniel Lezcano owl_clksrc_base = owl_timer_base + 0x08;
1299d8d47eaSDaniel Lezcano owl_clkevt_base = owl_timer_base + 0x14;
1309d8d47eaSDaniel Lezcano
1319d8d47eaSDaniel Lezcano timer1_irq = of_irq_get_byname(node, "timer1");
1329d8d47eaSDaniel Lezcano if (timer1_irq <= 0) {
1339d8d47eaSDaniel Lezcano pr_err("Can't parse timer1 IRQ\n");
1349d8d47eaSDaniel Lezcano return -EINVAL;
1359d8d47eaSDaniel Lezcano }
1369d8d47eaSDaniel Lezcano
1379d8d47eaSDaniel Lezcano clk = of_clk_get(node, 0);
138*ad1ded9dSMatheus Castello if (IS_ERR(clk)) {
139*ad1ded9dSMatheus Castello ret = PTR_ERR(clk);
140*ad1ded9dSMatheus Castello pr_err("Failed to get clock for clocksource (%d)\n", ret);
141*ad1ded9dSMatheus Castello return ret;
142*ad1ded9dSMatheus Castello }
1439d8d47eaSDaniel Lezcano
1449d8d47eaSDaniel Lezcano rate = clk_get_rate(clk);
1459d8d47eaSDaniel Lezcano
1469d8d47eaSDaniel Lezcano owl_timer_reset(owl_clksrc_base);
1479d8d47eaSDaniel Lezcano owl_timer_set_enabled(owl_clksrc_base, true);
1489d8d47eaSDaniel Lezcano
1499d8d47eaSDaniel Lezcano sched_clock_register(owl_timer_sched_read, 32, rate);
150*ad1ded9dSMatheus Castello ret = clocksource_mmio_init(owl_clksrc_base + OWL_Tx_VAL, node->name,
1519d8d47eaSDaniel Lezcano rate, 200, 32, clocksource_mmio_readl_up);
152*ad1ded9dSMatheus Castello if (ret) {
153*ad1ded9dSMatheus Castello pr_err("Failed to register clocksource (%d)\n", ret);
154*ad1ded9dSMatheus Castello return ret;
155*ad1ded9dSMatheus Castello }
1569d8d47eaSDaniel Lezcano
1579d8d47eaSDaniel Lezcano owl_timer_reset(owl_clkevt_base);
1589d8d47eaSDaniel Lezcano
1599d8d47eaSDaniel Lezcano ret = request_irq(timer1_irq, owl_timer1_interrupt, IRQF_TIMER,
1609d8d47eaSDaniel Lezcano "owl-timer", &owl_clockevent);
1619d8d47eaSDaniel Lezcano if (ret) {
1629d8d47eaSDaniel Lezcano pr_err("failed to request irq %d\n", timer1_irq);
1639d8d47eaSDaniel Lezcano return ret;
1649d8d47eaSDaniel Lezcano }
1659d8d47eaSDaniel Lezcano
1669d8d47eaSDaniel Lezcano owl_clockevent.cpumask = cpumask_of(0);
1679d8d47eaSDaniel Lezcano owl_clockevent.irq = timer1_irq;
1689d8d47eaSDaniel Lezcano
1699d8d47eaSDaniel Lezcano clockevents_config_and_register(&owl_clockevent, rate,
1709d8d47eaSDaniel Lezcano 0xf, 0xffffffff);
1719d8d47eaSDaniel Lezcano
1729d8d47eaSDaniel Lezcano return 0;
1739d8d47eaSDaniel Lezcano }
1749d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(owl_s500, "actions,s500-timer", owl_timer_init);
1759d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(owl_s700, "actions,s700-timer", owl_timer_init);
1769d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(owl_s900, "actions,s900-timer", owl_timer_init);
177