18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Spreadtrum Communications Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "timer-of.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define TIMER_NAME "sprd_timer" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define TIMER_LOAD_LO 0x0 148c2ecf20Sopenharmony_ci#define TIMER_LOAD_HI 0x4 158c2ecf20Sopenharmony_ci#define TIMER_VALUE_LO 0x8 168c2ecf20Sopenharmony_ci#define TIMER_VALUE_HI 0xc 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define TIMER_CTL 0x10 198c2ecf20Sopenharmony_ci#define TIMER_CTL_PERIOD_MODE BIT(0) 208c2ecf20Sopenharmony_ci#define TIMER_CTL_ENABLE BIT(1) 218c2ecf20Sopenharmony_ci#define TIMER_CTL_64BIT_WIDTH BIT(16) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define TIMER_INT 0x14 248c2ecf20Sopenharmony_ci#define TIMER_INT_EN BIT(0) 258c2ecf20Sopenharmony_ci#define TIMER_INT_RAW_STS BIT(1) 268c2ecf20Sopenharmony_ci#define TIMER_INT_MASK_STS BIT(2) 278c2ecf20Sopenharmony_ci#define TIMER_INT_CLR BIT(3) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define TIMER_VALUE_SHDW_LO 0x18 308c2ecf20Sopenharmony_ci#define TIMER_VALUE_SHDW_HI 0x1c 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define TIMER_VALUE_LO_MASK GENMASK(31, 0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void sprd_timer_enable(void __iomem *base, u32 flag) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci u32 val = readl_relaxed(base + TIMER_CTL); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci val |= TIMER_CTL_ENABLE; 398c2ecf20Sopenharmony_ci if (flag & TIMER_CTL_64BIT_WIDTH) 408c2ecf20Sopenharmony_ci val |= TIMER_CTL_64BIT_WIDTH; 418c2ecf20Sopenharmony_ci else 428c2ecf20Sopenharmony_ci val &= ~TIMER_CTL_64BIT_WIDTH; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (flag & TIMER_CTL_PERIOD_MODE) 458c2ecf20Sopenharmony_ci val |= TIMER_CTL_PERIOD_MODE; 468c2ecf20Sopenharmony_ci else 478c2ecf20Sopenharmony_ci val &= ~TIMER_CTL_PERIOD_MODE; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci writel_relaxed(val, base + TIMER_CTL); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void sprd_timer_disable(void __iomem *base) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci u32 val = readl_relaxed(base + TIMER_CTL); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci val &= ~TIMER_CTL_ENABLE; 578c2ecf20Sopenharmony_ci writel_relaxed(val, base + TIMER_CTL); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void sprd_timer_update_counter(void __iomem *base, unsigned long cycles) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci writel_relaxed(cycles & TIMER_VALUE_LO_MASK, base + TIMER_LOAD_LO); 638c2ecf20Sopenharmony_ci writel_relaxed(0, base + TIMER_LOAD_HI); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void sprd_timer_enable_interrupt(void __iomem *base) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci writel_relaxed(TIMER_INT_EN, base + TIMER_INT); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void sprd_timer_clear_interrupt(void __iomem *base) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci u32 val = readl_relaxed(base + TIMER_INT); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci val |= TIMER_INT_CLR; 768c2ecf20Sopenharmony_ci writel_relaxed(val, base + TIMER_INT); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int sprd_timer_set_next_event(unsigned long cycles, 808c2ecf20Sopenharmony_ci struct clock_event_device *ce) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct timer_of *to = to_timer_of(ce); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci sprd_timer_disable(timer_of_base(to)); 858c2ecf20Sopenharmony_ci sprd_timer_update_counter(timer_of_base(to), cycles); 868c2ecf20Sopenharmony_ci sprd_timer_enable(timer_of_base(to), 0); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int sprd_timer_set_periodic(struct clock_event_device *ce) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct timer_of *to = to_timer_of(ce); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci sprd_timer_disable(timer_of_base(to)); 968c2ecf20Sopenharmony_ci sprd_timer_update_counter(timer_of_base(to), timer_of_period(to)); 978c2ecf20Sopenharmony_ci sprd_timer_enable(timer_of_base(to), TIMER_CTL_PERIOD_MODE); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int sprd_timer_shutdown(struct clock_event_device *ce) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct timer_of *to = to_timer_of(ce); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci sprd_timer_disable(timer_of_base(to)); 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic irqreturn_t sprd_timer_interrupt(int irq, void *dev_id) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct clock_event_device *ce = (struct clock_event_device *)dev_id; 1138c2ecf20Sopenharmony_ci struct timer_of *to = to_timer_of(ce); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci sprd_timer_clear_interrupt(timer_of_base(to)); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (clockevent_state_oneshot(ce)) 1188c2ecf20Sopenharmony_ci sprd_timer_disable(timer_of_base(to)); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ce->event_handler(ce); 1218c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic struct timer_of to = { 1258c2ecf20Sopenharmony_ci .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci .clkevt = { 1288c2ecf20Sopenharmony_ci .name = TIMER_NAME, 1298c2ecf20Sopenharmony_ci .rating = 300, 1308c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_DYNIRQ | CLOCK_EVT_FEAT_PERIODIC | 1318c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 1328c2ecf20Sopenharmony_ci .set_state_shutdown = sprd_timer_shutdown, 1338c2ecf20Sopenharmony_ci .set_state_periodic = sprd_timer_set_periodic, 1348c2ecf20Sopenharmony_ci .set_next_event = sprd_timer_set_next_event, 1358c2ecf20Sopenharmony_ci .cpumask = cpu_possible_mask, 1368c2ecf20Sopenharmony_ci }, 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci .of_irq = { 1398c2ecf20Sopenharmony_ci .handler = sprd_timer_interrupt, 1408c2ecf20Sopenharmony_ci .flags = IRQF_TIMER | IRQF_IRQPOLL, 1418c2ecf20Sopenharmony_ci }, 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int __init sprd_timer_init(struct device_node *np) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci int ret; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ret = timer_of_init(np, &to); 1498c2ecf20Sopenharmony_ci if (ret) 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci sprd_timer_enable_interrupt(timer_of_base(&to)); 1538c2ecf20Sopenharmony_ci clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 1548c2ecf20Sopenharmony_ci 1, UINT_MAX); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic struct timer_of suspend_to = { 1608c2ecf20Sopenharmony_ci .flags = TIMER_OF_BASE | TIMER_OF_CLOCK, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic u64 sprd_suspend_timer_read(struct clocksource *cs) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci return ~(u64)readl_relaxed(timer_of_base(&suspend_to) + 1668c2ecf20Sopenharmony_ci TIMER_VALUE_SHDW_LO) & cs->mask; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int sprd_suspend_timer_enable(struct clocksource *cs) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci sprd_timer_update_counter(timer_of_base(&suspend_to), 1728c2ecf20Sopenharmony_ci TIMER_VALUE_LO_MASK); 1738c2ecf20Sopenharmony_ci sprd_timer_enable(timer_of_base(&suspend_to), TIMER_CTL_PERIOD_MODE); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void sprd_suspend_timer_disable(struct clocksource *cs) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci sprd_timer_disable(timer_of_base(&suspend_to)); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic struct clocksource suspend_clocksource = { 1848c2ecf20Sopenharmony_ci .name = "sprd_suspend_timer", 1858c2ecf20Sopenharmony_ci .rating = 200, 1868c2ecf20Sopenharmony_ci .read = sprd_suspend_timer_read, 1878c2ecf20Sopenharmony_ci .enable = sprd_suspend_timer_enable, 1888c2ecf20Sopenharmony_ci .disable = sprd_suspend_timer_disable, 1898c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 1908c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int __init sprd_suspend_timer_init(struct device_node *np) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = timer_of_init(np, &suspend_to); 1988c2ecf20Sopenharmony_ci if (ret) 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci clocksource_register_hz(&suspend_clocksource, 2028c2ecf20Sopenharmony_ci timer_of_rate(&suspend_to)); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(sc9860_timer, "sprd,sc9860-timer", sprd_timer_init); 2088c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(sc9860_persistent_timer, "sprd,sc9860-suspend-timer", 2098c2ecf20Sopenharmony_ci sprd_suspend_timer_init); 210