xref: /openbmc/linux/drivers/clocksource/timer-owl.c (revision ead5d1f4d877e92c051e1a1ade623d0d30e71619)
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