18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2013 Pengutronix
48c2ecf20Sopenharmony_ci * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
118c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
128c2ecf20Sopenharmony_ci#include <linux/irq.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/of_address.h>
168c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
178c2ecf20Sopenharmony_ci#include <linux/clk.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define TIMERn_CTRL			0x00
208c2ecf20Sopenharmony_ci#define TIMERn_CTRL_PRESC(val)			(((val) & 0xf) << 24)
218c2ecf20Sopenharmony_ci#define TIMERn_CTRL_PRESC_1024			TIMERn_CTRL_PRESC(10)
228c2ecf20Sopenharmony_ci#define TIMERn_CTRL_CLKSEL(val)			(((val) & 0x3) << 16)
238c2ecf20Sopenharmony_ci#define TIMERn_CTRL_CLKSEL_PRESCHFPERCLK	TIMERn_CTRL_CLKSEL(0)
248c2ecf20Sopenharmony_ci#define TIMERn_CTRL_OSMEN			0x00000010
258c2ecf20Sopenharmony_ci#define TIMERn_CTRL_MODE(val)			(((val) & 0x3) <<  0)
268c2ecf20Sopenharmony_ci#define TIMERn_CTRL_MODE_UP			TIMERn_CTRL_MODE(0)
278c2ecf20Sopenharmony_ci#define TIMERn_CTRL_MODE_DOWN			TIMERn_CTRL_MODE(1)
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define TIMERn_CMD			0x04
308c2ecf20Sopenharmony_ci#define TIMERn_CMD_START			0x00000001
318c2ecf20Sopenharmony_ci#define TIMERn_CMD_STOP				0x00000002
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define TIMERn_IEN			0x0c
348c2ecf20Sopenharmony_ci#define TIMERn_IF			0x10
358c2ecf20Sopenharmony_ci#define TIMERn_IFS			0x14
368c2ecf20Sopenharmony_ci#define TIMERn_IFC			0x18
378c2ecf20Sopenharmony_ci#define TIMERn_IRQ_UF				0x00000002
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define TIMERn_TOP			0x1c
408c2ecf20Sopenharmony_ci#define TIMERn_CNT			0x24
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct efm32_clock_event_ddata {
438c2ecf20Sopenharmony_ci	struct clock_event_device evtdev;
448c2ecf20Sopenharmony_ci	void __iomem *base;
458c2ecf20Sopenharmony_ci	unsigned periodic_top;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int efm32_clock_event_shutdown(struct clock_event_device *evtdev)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct efm32_clock_event_ddata *ddata =
518c2ecf20Sopenharmony_ci		container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
548c2ecf20Sopenharmony_ci	return 0;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int efm32_clock_event_set_oneshot(struct clock_event_device *evtdev)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct efm32_clock_event_ddata *ddata =
608c2ecf20Sopenharmony_ci		container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
638c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CTRL_PRESC_1024 |
648c2ecf20Sopenharmony_ci		       TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
658c2ecf20Sopenharmony_ci		       TIMERn_CTRL_OSMEN |
668c2ecf20Sopenharmony_ci		       TIMERn_CTRL_MODE_DOWN,
678c2ecf20Sopenharmony_ci		       ddata->base + TIMERn_CTRL);
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int efm32_clock_event_set_periodic(struct clock_event_device *evtdev)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct efm32_clock_event_ddata *ddata =
748c2ecf20Sopenharmony_ci		container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
778c2ecf20Sopenharmony_ci	writel_relaxed(ddata->periodic_top, ddata->base + TIMERn_TOP);
788c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CTRL_PRESC_1024 |
798c2ecf20Sopenharmony_ci		       TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
808c2ecf20Sopenharmony_ci		       TIMERn_CTRL_MODE_DOWN,
818c2ecf20Sopenharmony_ci		       ddata->base + TIMERn_CTRL);
828c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CMD_START, ddata->base + TIMERn_CMD);
838c2ecf20Sopenharmony_ci	return 0;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int efm32_clock_event_set_next_event(unsigned long evt,
878c2ecf20Sopenharmony_ci					    struct clock_event_device *evtdev)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct efm32_clock_event_ddata *ddata =
908c2ecf20Sopenharmony_ci		container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
938c2ecf20Sopenharmony_ci	writel_relaxed(evt, ddata->base + TIMERn_CNT);
948c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CMD_START, ddata->base + TIMERn_CMD);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic irqreturn_t efm32_clock_event_handler(int irq, void *dev_id)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct efm32_clock_event_ddata *ddata = dev_id;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_IRQ_UF, ddata->base + TIMERn_IFC);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	ddata->evtdev.event_handler(&ddata->evtdev);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic struct efm32_clock_event_ddata clock_event_ddata = {
1118c2ecf20Sopenharmony_ci	.evtdev = {
1128c2ecf20Sopenharmony_ci		.name = "efm32 clockevent",
1138c2ecf20Sopenharmony_ci		.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
1148c2ecf20Sopenharmony_ci		.set_state_shutdown = efm32_clock_event_shutdown,
1158c2ecf20Sopenharmony_ci		.set_state_periodic = efm32_clock_event_set_periodic,
1168c2ecf20Sopenharmony_ci		.set_state_oneshot = efm32_clock_event_set_oneshot,
1178c2ecf20Sopenharmony_ci		.set_next_event = efm32_clock_event_set_next_event,
1188c2ecf20Sopenharmony_ci		.rating = 200,
1198c2ecf20Sopenharmony_ci	},
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int __init efm32_clocksource_init(struct device_node *np)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct clk *clk;
1258c2ecf20Sopenharmony_ci	void __iomem *base;
1268c2ecf20Sopenharmony_ci	unsigned long rate;
1278c2ecf20Sopenharmony_ci	int ret;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	clk = of_clk_get(np, 0);
1308c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
1318c2ecf20Sopenharmony_ci		ret = PTR_ERR(clk);
1328c2ecf20Sopenharmony_ci		pr_err("failed to get clock for clocksource (%d)\n", ret);
1338c2ecf20Sopenharmony_ci		goto err_clk_get;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(clk);
1378c2ecf20Sopenharmony_ci	if (ret) {
1388c2ecf20Sopenharmony_ci		pr_err("failed to enable timer clock for clocksource (%d)\n",
1398c2ecf20Sopenharmony_ci		       ret);
1408c2ecf20Sopenharmony_ci		goto err_clk_enable;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci	rate = clk_get_rate(clk);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	base = of_iomap(np, 0);
1458c2ecf20Sopenharmony_ci	if (!base) {
1468c2ecf20Sopenharmony_ci		ret = -EADDRNOTAVAIL;
1478c2ecf20Sopenharmony_ci		pr_err("failed to map registers for clocksource\n");
1488c2ecf20Sopenharmony_ci		goto err_iomap;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CTRL_PRESC_1024 |
1528c2ecf20Sopenharmony_ci		       TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
1538c2ecf20Sopenharmony_ci		       TIMERn_CTRL_MODE_UP, base + TIMERn_CTRL);
1548c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_CMD_START, base + TIMERn_CMD);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = clocksource_mmio_init(base + TIMERn_CNT, "efm32 timer",
1578c2ecf20Sopenharmony_ci				    DIV_ROUND_CLOSEST(rate, 1024), 200, 16,
1588c2ecf20Sopenharmony_ci				    clocksource_mmio_readl_up);
1598c2ecf20Sopenharmony_ci	if (ret) {
1608c2ecf20Sopenharmony_ci		pr_err("failed to init clocksource (%d)\n", ret);
1618c2ecf20Sopenharmony_ci		goto err_clocksource_init;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cierr_clocksource_init:
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	iounmap(base);
1698c2ecf20Sopenharmony_cierr_iomap:
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	clk_disable_unprepare(clk);
1728c2ecf20Sopenharmony_cierr_clk_enable:
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	clk_put(clk);
1758c2ecf20Sopenharmony_cierr_clk_get:
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return ret;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int __init efm32_clockevent_init(struct device_node *np)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct clk *clk;
1838c2ecf20Sopenharmony_ci	void __iomem *base;
1848c2ecf20Sopenharmony_ci	unsigned long rate;
1858c2ecf20Sopenharmony_ci	int irq;
1868c2ecf20Sopenharmony_ci	int ret;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	clk = of_clk_get(np, 0);
1898c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
1908c2ecf20Sopenharmony_ci		ret = PTR_ERR(clk);
1918c2ecf20Sopenharmony_ci		pr_err("failed to get clock for clockevent (%d)\n", ret);
1928c2ecf20Sopenharmony_ci		goto err_clk_get;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(clk);
1968c2ecf20Sopenharmony_ci	if (ret) {
1978c2ecf20Sopenharmony_ci		pr_err("failed to enable timer clock for clockevent (%d)\n",
1988c2ecf20Sopenharmony_ci		       ret);
1998c2ecf20Sopenharmony_ci		goto err_clk_enable;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci	rate = clk_get_rate(clk);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	base = of_iomap(np, 0);
2048c2ecf20Sopenharmony_ci	if (!base) {
2058c2ecf20Sopenharmony_ci		ret = -EADDRNOTAVAIL;
2068c2ecf20Sopenharmony_ci		pr_err("failed to map registers for clockevent\n");
2078c2ecf20Sopenharmony_ci		goto err_iomap;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	irq = irq_of_parse_and_map(np, 0);
2118c2ecf20Sopenharmony_ci	if (!irq) {
2128c2ecf20Sopenharmony_ci		ret = -ENOENT;
2138c2ecf20Sopenharmony_ci		pr_err("failed to get irq for clockevent\n");
2148c2ecf20Sopenharmony_ci		goto err_get_irq;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	writel_relaxed(TIMERn_IRQ_UF, base + TIMERn_IEN);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	clock_event_ddata.base = base;
2208c2ecf20Sopenharmony_ci	clock_event_ddata.periodic_top = DIV_ROUND_CLOSEST(rate, 1024 * HZ);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	clockevents_config_and_register(&clock_event_ddata.evtdev,
2238c2ecf20Sopenharmony_ci					DIV_ROUND_CLOSEST(rate, 1024),
2248c2ecf20Sopenharmony_ci					0xf, 0xffff);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	ret = request_irq(irq, efm32_clock_event_handler, IRQF_TIMER,
2278c2ecf20Sopenharmony_ci			  "efm32 clockevent", &clock_event_ddata);
2288c2ecf20Sopenharmony_ci	if (ret) {
2298c2ecf20Sopenharmony_ci		pr_err("Failed setup irq\n");
2308c2ecf20Sopenharmony_ci		goto err_setup_irq;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cierr_setup_irq:
2368c2ecf20Sopenharmony_cierr_get_irq:
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	iounmap(base);
2398c2ecf20Sopenharmony_cierr_iomap:
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	clk_disable_unprepare(clk);
2428c2ecf20Sopenharmony_cierr_clk_enable:
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	clk_put(clk);
2458c2ecf20Sopenharmony_cierr_clk_get:
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return ret;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/*
2518c2ecf20Sopenharmony_ci * This function asserts that we have exactly one clocksource and one
2528c2ecf20Sopenharmony_ci * clock_event_device in the end.
2538c2ecf20Sopenharmony_ci */
2548c2ecf20Sopenharmony_cistatic int __init efm32_timer_init(struct device_node *np)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	static int has_clocksource, has_clockevent;
2578c2ecf20Sopenharmony_ci	int ret = 0;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (!has_clocksource) {
2608c2ecf20Sopenharmony_ci		ret = efm32_clocksource_init(np);
2618c2ecf20Sopenharmony_ci		if (!ret) {
2628c2ecf20Sopenharmony_ci			has_clocksource = 1;
2638c2ecf20Sopenharmony_ci			return 0;
2648c2ecf20Sopenharmony_ci		}
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (!has_clockevent) {
2688c2ecf20Sopenharmony_ci		ret = efm32_clockevent_init(np);
2698c2ecf20Sopenharmony_ci		if (!ret) {
2708c2ecf20Sopenharmony_ci			has_clockevent = 1;
2718c2ecf20Sopenharmony_ci			return 0;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return ret;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init);
2788c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init);
279