18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France
68c2ecf20Sopenharmony_ci * Revision	 2005 M. Nicolas Diremdjian, ATMEL Rousset, France
78c2ecf20Sopenharmony_ci * Converted to ClockSource/ClockEvents by David Brownell.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	"AT91: PIT: " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/clk.h>
138c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/irq.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <linux/of_address.h>
198c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define AT91_PIT_MR		0x00			/* Mode Register */
238c2ecf20Sopenharmony_ci#define AT91_PIT_PITIEN			BIT(25)			/* Timer Interrupt Enable */
248c2ecf20Sopenharmony_ci#define AT91_PIT_PITEN			BIT(24)			/* Timer Enabled */
258c2ecf20Sopenharmony_ci#define AT91_PIT_PIV			GENMASK(19, 0)		/* Periodic Interval Value */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define AT91_PIT_SR		0x04			/* Status Register */
288c2ecf20Sopenharmony_ci#define AT91_PIT_PITS			BIT(0)			/* Timer Status */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define AT91_PIT_PIVR		0x08			/* Periodic Interval Value Register */
318c2ecf20Sopenharmony_ci#define AT91_PIT_PIIR		0x0c			/* Periodic Interval Image Register */
328c2ecf20Sopenharmony_ci#define AT91_PIT_PICNT			GENMASK(31, 20)		/* Interval Counter */
338c2ecf20Sopenharmony_ci#define AT91_PIT_CPIV			GENMASK(19, 0)		/* Inverval Value */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define PIT_CPIV(x)	((x) & AT91_PIT_CPIV)
368c2ecf20Sopenharmony_ci#define PIT_PICNT(x)	(((x) & AT91_PIT_PICNT) >> 20)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct pit_data {
398c2ecf20Sopenharmony_ci	struct clock_event_device	clkevt;
408c2ecf20Sopenharmony_ci	struct clocksource		clksrc;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	void __iomem	*base;
438c2ecf20Sopenharmony_ci	u32		cycle;
448c2ecf20Sopenharmony_ci	u32		cnt;
458c2ecf20Sopenharmony_ci	unsigned int	irq;
468c2ecf20Sopenharmony_ci	struct clk	*mck;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	return container_of(clksrc, struct pit_data, clksrc);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	return container_of(clkevt, struct pit_data, clkevt);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	return readl_relaxed(base + reg_offset);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	writel_relaxed(value, base + reg_offset);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * Clocksource:  just a monotonic counter of MCK/16 cycles.
718c2ecf20Sopenharmony_ci * We don't care whether or not PIT irqs are enabled.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistatic u64 read_pit_clk(struct clocksource *cs)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct pit_data *data = clksrc_to_pit_data(cs);
768c2ecf20Sopenharmony_ci	unsigned long flags;
778c2ecf20Sopenharmony_ci	u32 elapsed;
788c2ecf20Sopenharmony_ci	u32 t;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	raw_local_irq_save(flags);
818c2ecf20Sopenharmony_ci	elapsed = data->cnt;
828c2ecf20Sopenharmony_ci	t = pit_read(data->base, AT91_PIT_PIIR);
838c2ecf20Sopenharmony_ci	raw_local_irq_restore(flags);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	elapsed += PIT_PICNT(t) * data->cycle;
868c2ecf20Sopenharmony_ci	elapsed += PIT_CPIV(t);
878c2ecf20Sopenharmony_ci	return elapsed;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int pit_clkevt_shutdown(struct clock_event_device *dev)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct pit_data *data = clkevt_to_pit_data(dev);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* disable irq, leaving the clocksource active */
958c2ecf20Sopenharmony_ci	pit_write(data->base, AT91_PIT_MR, (data->cycle - 1) | AT91_PIT_PITEN);
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/*
1008c2ecf20Sopenharmony_ci * Clockevent device:  interrupts every 1/HZ (== pit_cycles * MCK/16)
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic int pit_clkevt_set_periodic(struct clock_event_device *dev)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct pit_data *data = clkevt_to_pit_data(dev);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* update clocksource counter */
1078c2ecf20Sopenharmony_ci	data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
1088c2ecf20Sopenharmony_ci	pit_write(data->base, AT91_PIT_MR,
1098c2ecf20Sopenharmony_ci		  (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN);
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic void at91sam926x_pit_suspend(struct clock_event_device *cedev)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct pit_data *data = clkevt_to_pit_data(cedev);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* Disable timer */
1188c2ecf20Sopenharmony_ci	pit_write(data->base, AT91_PIT_MR, 0);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void at91sam926x_pit_reset(struct pit_data *data)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	/* Disable timer and irqs */
1248c2ecf20Sopenharmony_ci	pit_write(data->base, AT91_PIT_MR, 0);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* Clear any pending interrupts, wait for PIT to stop counting */
1278c2ecf20Sopenharmony_ci	while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0)
1288c2ecf20Sopenharmony_ci		cpu_relax();
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Start PIT but don't enable IRQ */
1318c2ecf20Sopenharmony_ci	pit_write(data->base, AT91_PIT_MR,
1328c2ecf20Sopenharmony_ci		  (data->cycle - 1) | AT91_PIT_PITEN);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void at91sam926x_pit_resume(struct clock_event_device *cedev)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct pit_data *data = clkevt_to_pit_data(cedev);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	at91sam926x_pit_reset(data);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/*
1438c2ecf20Sopenharmony_ci * IRQ handler for the timer.
1448c2ecf20Sopenharmony_ci */
1458c2ecf20Sopenharmony_cistatic irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct pit_data *data = dev_id;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* The PIT interrupt may be disabled, and is shared */
1508c2ecf20Sopenharmony_ci	if (clockevent_state_periodic(&data->clkevt) &&
1518c2ecf20Sopenharmony_ci	    (pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) {
1528c2ecf20Sopenharmony_ci		/* Get number of ticks performed before irq, and ack it */
1538c2ecf20Sopenharmony_ci		data->cnt += data->cycle * PIT_PICNT(pit_read(data->base,
1548c2ecf20Sopenharmony_ci							      AT91_PIT_PIVR));
1558c2ecf20Sopenharmony_ci		data->clkevt.event_handler(&data->clkevt);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return IRQ_NONE;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/*
1648c2ecf20Sopenharmony_ci * Set up both clocksource and clockevent support.
1658c2ecf20Sopenharmony_ci */
1668c2ecf20Sopenharmony_cistatic int __init at91sam926x_pit_dt_init(struct device_node *node)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	unsigned long   pit_rate;
1698c2ecf20Sopenharmony_ci	unsigned        bits;
1708c2ecf20Sopenharmony_ci	int             ret;
1718c2ecf20Sopenharmony_ci	struct pit_data *data;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
1748c2ecf20Sopenharmony_ci	if (!data)
1758c2ecf20Sopenharmony_ci		return -ENOMEM;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	data->base = of_iomap(node, 0);
1788c2ecf20Sopenharmony_ci	if (!data->base) {
1798c2ecf20Sopenharmony_ci		pr_err("Could not map PIT address\n");
1808c2ecf20Sopenharmony_ci		ret = -ENXIO;
1818c2ecf20Sopenharmony_ci		goto exit;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	data->mck = of_clk_get(node, 0);
1858c2ecf20Sopenharmony_ci	if (IS_ERR(data->mck)) {
1868c2ecf20Sopenharmony_ci		pr_err("Unable to get mck clk\n");
1878c2ecf20Sopenharmony_ci		ret = PTR_ERR(data->mck);
1888c2ecf20Sopenharmony_ci		goto exit;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(data->mck);
1928c2ecf20Sopenharmony_ci	if (ret) {
1938c2ecf20Sopenharmony_ci		pr_err("Unable to enable mck\n");
1948c2ecf20Sopenharmony_ci		goto exit;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* Get the interrupts property */
1988c2ecf20Sopenharmony_ci	data->irq = irq_of_parse_and_map(node, 0);
1998c2ecf20Sopenharmony_ci	if (!data->irq) {
2008c2ecf20Sopenharmony_ci		pr_err("Unable to get IRQ from DT\n");
2018c2ecf20Sopenharmony_ci		ret = -EINVAL;
2028c2ecf20Sopenharmony_ci		goto exit;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/*
2068c2ecf20Sopenharmony_ci	 * Use our actual MCK to figure out how many MCK/16 ticks per
2078c2ecf20Sopenharmony_ci	 * 1/HZ period (instead of a compile-time constant LATCH).
2088c2ecf20Sopenharmony_ci	 */
2098c2ecf20Sopenharmony_ci	pit_rate = clk_get_rate(data->mck) / 16;
2108c2ecf20Sopenharmony_ci	data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ);
2118c2ecf20Sopenharmony_ci	WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* Initialize and enable the timer */
2148c2ecf20Sopenharmony_ci	at91sam926x_pit_reset(data);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/*
2178c2ecf20Sopenharmony_ci	 * Register clocksource.  The high order bits of PIV are unused,
2188c2ecf20Sopenharmony_ci	 * so this isn't a 32-bit counter unless we get clockevent irqs.
2198c2ecf20Sopenharmony_ci	 */
2208c2ecf20Sopenharmony_ci	bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */;
2218c2ecf20Sopenharmony_ci	data->clksrc.mask = CLOCKSOURCE_MASK(bits);
2228c2ecf20Sopenharmony_ci	data->clksrc.name = "pit";
2238c2ecf20Sopenharmony_ci	data->clksrc.rating = 175;
2248c2ecf20Sopenharmony_ci	data->clksrc.read = read_pit_clk;
2258c2ecf20Sopenharmony_ci	data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	ret = clocksource_register_hz(&data->clksrc, pit_rate);
2288c2ecf20Sopenharmony_ci	if (ret) {
2298c2ecf20Sopenharmony_ci		pr_err("Failed to register clocksource\n");
2308c2ecf20Sopenharmony_ci		goto exit;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Set up irq handler */
2348c2ecf20Sopenharmony_ci	ret = request_irq(data->irq, at91sam926x_pit_interrupt,
2358c2ecf20Sopenharmony_ci			  IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
2368c2ecf20Sopenharmony_ci			  "at91_tick", data);
2378c2ecf20Sopenharmony_ci	if (ret) {
2388c2ecf20Sopenharmony_ci		pr_err("Unable to setup IRQ\n");
2398c2ecf20Sopenharmony_ci		clocksource_unregister(&data->clksrc);
2408c2ecf20Sopenharmony_ci		goto exit;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* Set up and register clockevents */
2448c2ecf20Sopenharmony_ci	data->clkevt.name = "pit";
2458c2ecf20Sopenharmony_ci	data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
2468c2ecf20Sopenharmony_ci	data->clkevt.shift = 32;
2478c2ecf20Sopenharmony_ci	data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift);
2488c2ecf20Sopenharmony_ci	data->clkevt.rating = 100;
2498c2ecf20Sopenharmony_ci	data->clkevt.cpumask = cpumask_of(0);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	data->clkevt.set_state_shutdown = pit_clkevt_shutdown;
2528c2ecf20Sopenharmony_ci	data->clkevt.set_state_periodic = pit_clkevt_set_periodic;
2538c2ecf20Sopenharmony_ci	data->clkevt.resume = at91sam926x_pit_resume;
2548c2ecf20Sopenharmony_ci	data->clkevt.suspend = at91sam926x_pit_suspend;
2558c2ecf20Sopenharmony_ci	clockevents_register_device(&data->clkevt);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ciexit:
2608c2ecf20Sopenharmony_ci	kfree(data);
2618c2ecf20Sopenharmony_ci	return ret;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
2648c2ecf20Sopenharmony_ci		       at91sam926x_pit_dt_init);
265