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