1*b3a9ce9dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0
29b8bb773SBaruch Siach /*
39b8bb773SBaruch Siach  * Conexant Digicolor timer driver
49b8bb773SBaruch Siach  *
59b8bb773SBaruch Siach  * Author: Baruch Siach <[email protected]>
69b8bb773SBaruch Siach  *
79b8bb773SBaruch Siach  * Copyright (C) 2014 Paradox Innovation Ltd.
89b8bb773SBaruch Siach  *
99b8bb773SBaruch Siach  * Based on:
109b8bb773SBaruch Siach  *	Allwinner SoCs hstimer driver
119b8bb773SBaruch Siach  *
129b8bb773SBaruch Siach  * Copyright (C) 2013 Maxime Ripard
139b8bb773SBaruch Siach  *
149b8bb773SBaruch Siach  * Maxime Ripard <[email protected]>
159b8bb773SBaruch Siach  */
169b8bb773SBaruch Siach 
179b8bb773SBaruch Siach /*
189b8bb773SBaruch Siach  * Conexant Digicolor SoCs have 8 configurable timers, named from "Timer A" to
199b8bb773SBaruch Siach  * "Timer H". Timer A is the only one with watchdog support, so it is dedicated
209b8bb773SBaruch Siach  * to the watchdog driver. This driver uses Timer B for sched_clock(), and
219b8bb773SBaruch Siach  * Timer C for clockevents.
229b8bb773SBaruch Siach  */
239b8bb773SBaruch Siach 
249b8bb773SBaruch Siach #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
259b8bb773SBaruch Siach 
269b8bb773SBaruch Siach #include <linux/clk.h>
279b8bb773SBaruch Siach #include <linux/clockchips.h>
289b8bb773SBaruch Siach #include <linux/interrupt.h>
299b8bb773SBaruch Siach #include <linux/irq.h>
309b8bb773SBaruch Siach #include <linux/irqreturn.h>
31e6017571SIngo Molnar #include <linux/sched/clock.h>
329b8bb773SBaruch Siach #include <linux/sched_clock.h>
339b8bb773SBaruch Siach #include <linux/of.h>
349b8bb773SBaruch Siach #include <linux/of_address.h>
359b8bb773SBaruch Siach #include <linux/of_irq.h>
369b8bb773SBaruch Siach 
379b8bb773SBaruch Siach enum {
389b8bb773SBaruch Siach 	TIMER_A,
399b8bb773SBaruch Siach 	TIMER_B,
409b8bb773SBaruch Siach 	TIMER_C,
419b8bb773SBaruch Siach 	TIMER_D,
429b8bb773SBaruch Siach 	TIMER_E,
439b8bb773SBaruch Siach 	TIMER_F,
449b8bb773SBaruch Siach 	TIMER_G,
459b8bb773SBaruch Siach 	TIMER_H,
469b8bb773SBaruch Siach };
479b8bb773SBaruch Siach 
489b8bb773SBaruch Siach #define CONTROL(t)	((t)*8)
499b8bb773SBaruch Siach #define COUNT(t)	((t)*8 + 4)
509b8bb773SBaruch Siach 
519b8bb773SBaruch Siach #define CONTROL_DISABLE		0
529b8bb773SBaruch Siach #define CONTROL_ENABLE		BIT(0)
539b8bb773SBaruch Siach #define CONTROL_MODE(m)		((m) << 4)
549b8bb773SBaruch Siach #define CONTROL_MODE_ONESHOT	CONTROL_MODE(1)
559b8bb773SBaruch Siach #define CONTROL_MODE_PERIODIC	CONTROL_MODE(2)
569b8bb773SBaruch Siach 
579b8bb773SBaruch Siach struct digicolor_timer {
589b8bb773SBaruch Siach 	struct clock_event_device ce;
599b8bb773SBaruch Siach 	void __iomem *base;
609b8bb773SBaruch Siach 	u32 ticks_per_jiffy;
619b8bb773SBaruch Siach 	int timer_id; /* one of TIMER_* */
629b8bb773SBaruch Siach };
639b8bb773SBaruch Siach 
dc_timer(struct clock_event_device * ce)6448419b1bSBen Dooks static struct digicolor_timer *dc_timer(struct clock_event_device *ce)
659b8bb773SBaruch Siach {
669b8bb773SBaruch Siach 	return container_of(ce, struct digicolor_timer, ce);
679b8bb773SBaruch Siach }
689b8bb773SBaruch Siach 
dc_timer_disable(struct clock_event_device * ce)699b8bb773SBaruch Siach static inline void dc_timer_disable(struct clock_event_device *ce)
709b8bb773SBaruch Siach {
719b8bb773SBaruch Siach 	struct digicolor_timer *dt = dc_timer(ce);
729b8bb773SBaruch Siach 	writeb(CONTROL_DISABLE, dt->base + CONTROL(dt->timer_id));
739b8bb773SBaruch Siach }
749b8bb773SBaruch Siach 
dc_timer_enable(struct clock_event_device * ce,u32 mode)759b8bb773SBaruch Siach static inline void dc_timer_enable(struct clock_event_device *ce, u32 mode)
769b8bb773SBaruch Siach {
779b8bb773SBaruch Siach 	struct digicolor_timer *dt = dc_timer(ce);
789b8bb773SBaruch Siach 	writeb(CONTROL_ENABLE | mode, dt->base + CONTROL(dt->timer_id));
799b8bb773SBaruch Siach }
809b8bb773SBaruch Siach 
dc_timer_set_count(struct clock_event_device * ce,unsigned long count)819b8bb773SBaruch Siach static inline void dc_timer_set_count(struct clock_event_device *ce,
829b8bb773SBaruch Siach 				      unsigned long count)
839b8bb773SBaruch Siach {
849b8bb773SBaruch Siach 	struct digicolor_timer *dt = dc_timer(ce);
859b8bb773SBaruch Siach 	writel(count, dt->base + COUNT(dt->timer_id));
869b8bb773SBaruch Siach }
879b8bb773SBaruch Siach 
digicolor_clkevt_shutdown(struct clock_event_device * ce)88e0d1ca33SViresh Kumar static int digicolor_clkevt_shutdown(struct clock_event_device *ce)
89e0d1ca33SViresh Kumar {
90e0d1ca33SViresh Kumar 	dc_timer_disable(ce);
91e0d1ca33SViresh Kumar 	return 0;
92e0d1ca33SViresh Kumar }
93e0d1ca33SViresh Kumar 
digicolor_clkevt_set_oneshot(struct clock_event_device * ce)94e0d1ca33SViresh Kumar static int digicolor_clkevt_set_oneshot(struct clock_event_device *ce)
95e0d1ca33SViresh Kumar {
96e0d1ca33SViresh Kumar 	dc_timer_disable(ce);
97e0d1ca33SViresh Kumar 	dc_timer_enable(ce, CONTROL_MODE_ONESHOT);
98e0d1ca33SViresh Kumar 	return 0;
99e0d1ca33SViresh Kumar }
100e0d1ca33SViresh Kumar 
digicolor_clkevt_set_periodic(struct clock_event_device * ce)101e0d1ca33SViresh Kumar static int digicolor_clkevt_set_periodic(struct clock_event_device *ce)
1029b8bb773SBaruch Siach {
1039b8bb773SBaruch Siach 	struct digicolor_timer *dt = dc_timer(ce);
1049b8bb773SBaruch Siach 
1059b8bb773SBaruch Siach 	dc_timer_disable(ce);
1069b8bb773SBaruch Siach 	dc_timer_set_count(ce, dt->ticks_per_jiffy);
1079b8bb773SBaruch Siach 	dc_timer_enable(ce, CONTROL_MODE_PERIODIC);
108e0d1ca33SViresh Kumar 	return 0;
1099b8bb773SBaruch Siach }
1109b8bb773SBaruch Siach 
digicolor_clkevt_next_event(unsigned long evt,struct clock_event_device * ce)1119b8bb773SBaruch Siach static int digicolor_clkevt_next_event(unsigned long evt,
1129b8bb773SBaruch Siach 				       struct clock_event_device *ce)
1139b8bb773SBaruch Siach {
1149b8bb773SBaruch Siach 	dc_timer_disable(ce);
1159b8bb773SBaruch Siach 	dc_timer_set_count(ce, evt);
1169b8bb773SBaruch Siach 	dc_timer_enable(ce, CONTROL_MODE_ONESHOT);
1179b8bb773SBaruch Siach 
1189b8bb773SBaruch Siach 	return 0;
1199b8bb773SBaruch Siach }
1209b8bb773SBaruch Siach 
1219b8bb773SBaruch Siach static struct digicolor_timer dc_timer_dev = {
1229b8bb773SBaruch Siach 	.ce = {
1239b8bb773SBaruch Siach 		.name = "digicolor_tick",
1249b8bb773SBaruch Siach 		.rating = 340,
1259b8bb773SBaruch Siach 		.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
126e0d1ca33SViresh Kumar 		.set_state_shutdown = digicolor_clkevt_shutdown,
127e0d1ca33SViresh Kumar 		.set_state_periodic = digicolor_clkevt_set_periodic,
128e0d1ca33SViresh Kumar 		.set_state_oneshot = digicolor_clkevt_set_oneshot,
129e0d1ca33SViresh Kumar 		.tick_resume = digicolor_clkevt_shutdown,
1309b8bb773SBaruch Siach 		.set_next_event = digicolor_clkevt_next_event,
1319b8bb773SBaruch Siach 	},
1329b8bb773SBaruch Siach 	.timer_id = TIMER_C,
1339b8bb773SBaruch Siach };
1349b8bb773SBaruch Siach 
digicolor_timer_interrupt(int irq,void * dev_id)1359b8bb773SBaruch Siach static irqreturn_t digicolor_timer_interrupt(int irq, void *dev_id)
1369b8bb773SBaruch Siach {
1379b8bb773SBaruch Siach 	struct clock_event_device *evt = dev_id;
1389b8bb773SBaruch Siach 
1399b8bb773SBaruch Siach 	evt->event_handler(evt);
1409b8bb773SBaruch Siach 
1419b8bb773SBaruch Siach 	return IRQ_HANDLED;
1429b8bb773SBaruch Siach }
1439b8bb773SBaruch Siach 
digicolor_timer_sched_read(void)14476b1ba7fSJisheng Zhang static u64 notrace digicolor_timer_sched_read(void)
1459b8bb773SBaruch Siach {
1469b8bb773SBaruch Siach 	return ~readl(dc_timer_dev.base + COUNT(TIMER_B));
1479b8bb773SBaruch Siach }
1489b8bb773SBaruch Siach 
digicolor_timer_init(struct device_node * node)149c77b9d44SDaniel Lezcano static int __init digicolor_timer_init(struct device_node *node)
1509b8bb773SBaruch Siach {
1519b8bb773SBaruch Siach 	unsigned long rate;
1529b8bb773SBaruch Siach 	struct clk *clk;
1539b8bb773SBaruch Siach 	int ret, irq;
1549b8bb773SBaruch Siach 
1559b8bb773SBaruch Siach 	/*
1569b8bb773SBaruch Siach 	 * timer registers are shared with the watchdog timer;
1579b8bb773SBaruch Siach 	 * don't map exclusively
1589b8bb773SBaruch Siach 	 */
1599b8bb773SBaruch Siach 	dc_timer_dev.base = of_iomap(node, 0);
1609b8bb773SBaruch Siach 	if (!dc_timer_dev.base) {
161ac9ce6d1SRafał Miłecki 		pr_err("Can't map registers\n");
162c77b9d44SDaniel Lezcano 		return -ENXIO;
1639b8bb773SBaruch Siach 	}
1649b8bb773SBaruch Siach 
1659b8bb773SBaruch Siach 	irq = irq_of_parse_and_map(node, dc_timer_dev.timer_id);
1669b8bb773SBaruch Siach 	if (irq <= 0) {
167ac9ce6d1SRafał Miłecki 		pr_err("Can't parse IRQ\n");
168c77b9d44SDaniel Lezcano 		return -EINVAL;
1699b8bb773SBaruch Siach 	}
1709b8bb773SBaruch Siach 
1719b8bb773SBaruch Siach 	clk = of_clk_get(node, 0);
1729b8bb773SBaruch Siach 	if (IS_ERR(clk)) {
173ac9ce6d1SRafał Miłecki 		pr_err("Can't get timer clock\n");
174c77b9d44SDaniel Lezcano 		return PTR_ERR(clk);
1759b8bb773SBaruch Siach 	}
1769b8bb773SBaruch Siach 	clk_prepare_enable(clk);
1779b8bb773SBaruch Siach 	rate = clk_get_rate(clk);
1789b8bb773SBaruch Siach 	dc_timer_dev.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
1799b8bb773SBaruch Siach 
1809b8bb773SBaruch Siach 	writeb(CONTROL_DISABLE, dc_timer_dev.base + CONTROL(TIMER_B));
1819b8bb773SBaruch Siach 	writel(UINT_MAX, dc_timer_dev.base + COUNT(TIMER_B));
1829b8bb773SBaruch Siach 	writeb(CONTROL_ENABLE, dc_timer_dev.base + CONTROL(TIMER_B));
1839b8bb773SBaruch Siach 
1849b8bb773SBaruch Siach 	sched_clock_register(digicolor_timer_sched_read, 32, rate);
1859b8bb773SBaruch Siach 	clocksource_mmio_init(dc_timer_dev.base + COUNT(TIMER_B), node->name,
1869b8bb773SBaruch Siach 			      rate, 340, 32, clocksource_mmio_readl_down);
1879b8bb773SBaruch Siach 
1889b8bb773SBaruch Siach 	ret = request_irq(irq, digicolor_timer_interrupt,
1899b8bb773SBaruch Siach 			  IRQF_TIMER | IRQF_IRQPOLL, "digicolor_timerC",
1909b8bb773SBaruch Siach 			  &dc_timer_dev.ce);
191c77b9d44SDaniel Lezcano 	if (ret) {
1929b8bb773SBaruch Siach 		pr_warn("request of timer irq %d failed (%d)\n", irq, ret);
193c77b9d44SDaniel Lezcano 		return ret;
194c77b9d44SDaniel Lezcano 	}
1959b8bb773SBaruch Siach 
1969b8bb773SBaruch Siach 	dc_timer_dev.ce.cpumask = cpu_possible_mask;
1979b8bb773SBaruch Siach 	dc_timer_dev.ce.irq = irq;
1989b8bb773SBaruch Siach 
1999b8bb773SBaruch Siach 	clockevents_config_and_register(&dc_timer_dev.ce, rate, 0, 0xffffffff);
200c77b9d44SDaniel Lezcano 
201c77b9d44SDaniel Lezcano 	return 0;
2029b8bb773SBaruch Siach }
20317273395SDaniel Lezcano TIMER_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer",
2049b8bb773SBaruch Siach 		       digicolor_timer_init);
205