18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright 2016 Freescale Semiconductor, Inc. 48c2ecf20Sopenharmony_ci// Copyright 2017 NXP 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/clk.h> 78c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 88c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "timer-of.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define TPM_PARAM 0x4 168c2ecf20Sopenharmony_ci#define TPM_PARAM_WIDTH_SHIFT 16 178c2ecf20Sopenharmony_ci#define TPM_PARAM_WIDTH_MASK (0xff << 16) 188c2ecf20Sopenharmony_ci#define TPM_SC 0x10 198c2ecf20Sopenharmony_ci#define TPM_SC_CMOD_INC_PER_CNT (0x1 << 3) 208c2ecf20Sopenharmony_ci#define TPM_SC_CMOD_DIV_DEFAULT 0x3 218c2ecf20Sopenharmony_ci#define TPM_SC_CMOD_DIV_MAX 0x7 228c2ecf20Sopenharmony_ci#define TPM_SC_TOF_MASK (0x1 << 7) 238c2ecf20Sopenharmony_ci#define TPM_CNT 0x14 248c2ecf20Sopenharmony_ci#define TPM_MOD 0x18 258c2ecf20Sopenharmony_ci#define TPM_STATUS 0x1c 268c2ecf20Sopenharmony_ci#define TPM_STATUS_CH0F BIT(0) 278c2ecf20Sopenharmony_ci#define TPM_C0SC 0x20 288c2ecf20Sopenharmony_ci#define TPM_C0SC_CHIE BIT(6) 298c2ecf20Sopenharmony_ci#define TPM_C0SC_MODE_SHIFT 2 308c2ecf20Sopenharmony_ci#define TPM_C0SC_MODE_MASK 0x3c 318c2ecf20Sopenharmony_ci#define TPM_C0SC_MODE_SW_COMPARE 0x4 328c2ecf20Sopenharmony_ci#define TPM_C0SC_CHF_MASK (0x1 << 7) 338c2ecf20Sopenharmony_ci#define TPM_C0V 0x24 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int counter_width; 368c2ecf20Sopenharmony_cistatic void __iomem *timer_base; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline void tpm_timer_disable(void) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci unsigned int val; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* channel disable */ 438c2ecf20Sopenharmony_ci val = readl(timer_base + TPM_C0SC); 448c2ecf20Sopenharmony_ci val &= ~(TPM_C0SC_MODE_MASK | TPM_C0SC_CHIE); 458c2ecf20Sopenharmony_ci writel(val, timer_base + TPM_C0SC); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic inline void tpm_timer_enable(void) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci unsigned int val; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* channel enabled in sw compare mode */ 538c2ecf20Sopenharmony_ci val = readl(timer_base + TPM_C0SC); 548c2ecf20Sopenharmony_ci val |= (TPM_C0SC_MODE_SW_COMPARE << TPM_C0SC_MODE_SHIFT) | 558c2ecf20Sopenharmony_ci TPM_C0SC_CHIE; 568c2ecf20Sopenharmony_ci writel(val, timer_base + TPM_C0SC); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline void tpm_irq_acknowledge(void) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci writel(TPM_STATUS_CH0F, timer_base + TPM_STATUS); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline unsigned long tpm_read_counter(void) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci return readl(timer_base + TPM_CNT); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM) 708c2ecf20Sopenharmony_cistatic struct delay_timer tpm_delay_timer; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic unsigned long tpm_read_current_timer(void) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return tpm_read_counter(); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci#endif 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic u64 notrace tpm_read_sched_clock(void) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci return tpm_read_counter(); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int tpm_set_next_event(unsigned long delta, 848c2ecf20Sopenharmony_ci struct clock_event_device *evt) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci unsigned long next, now; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci next = tpm_read_counter(); 898c2ecf20Sopenharmony_ci next += delta; 908c2ecf20Sopenharmony_ci writel(next, timer_base + TPM_C0V); 918c2ecf20Sopenharmony_ci now = tpm_read_counter(); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * NOTE: We observed in a very small probability, the bus fabric 958c2ecf20Sopenharmony_ci * contention between GPU and A7 may results a few cycles delay 968c2ecf20Sopenharmony_ci * of writing CNT registers which may cause the min_delta event got 978c2ecf20Sopenharmony_ci * missed, so we need add a ETIME check here in case it happened. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci return (int)(next - now) <= 0 ? -ETIME : 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int tpm_set_state_oneshot(struct clock_event_device *evt) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci tpm_timer_enable(); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int tpm_set_state_shutdown(struct clock_event_device *evt) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci tpm_timer_disable(); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic irqreturn_t tpm_timer_interrupt(int irq, void *dev_id) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct clock_event_device *evt = dev_id; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci tpm_irq_acknowledge(); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci evt->event_handler(evt); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct timer_of to_tpm = { 1288c2ecf20Sopenharmony_ci .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, 1298c2ecf20Sopenharmony_ci .clkevt = { 1308c2ecf20Sopenharmony_ci .name = "i.MX7ULP TPM Timer", 1318c2ecf20Sopenharmony_ci .rating = 200, 1328c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT, 1338c2ecf20Sopenharmony_ci .set_state_shutdown = tpm_set_state_shutdown, 1348c2ecf20Sopenharmony_ci .set_state_oneshot = tpm_set_state_oneshot, 1358c2ecf20Sopenharmony_ci .set_next_event = tpm_set_next_event, 1368c2ecf20Sopenharmony_ci .cpumask = cpu_possible_mask, 1378c2ecf20Sopenharmony_ci }, 1388c2ecf20Sopenharmony_ci .of_irq = { 1398c2ecf20Sopenharmony_ci .handler = tpm_timer_interrupt, 1408c2ecf20Sopenharmony_ci .flags = IRQF_TIMER | IRQF_IRQPOLL, 1418c2ecf20Sopenharmony_ci }, 1428c2ecf20Sopenharmony_ci .of_clk = { 1438c2ecf20Sopenharmony_ci .name = "per", 1448c2ecf20Sopenharmony_ci }, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int __init tpm_clocksource_init(void) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM) 1508c2ecf20Sopenharmony_ci tpm_delay_timer.read_current_timer = &tpm_read_current_timer; 1518c2ecf20Sopenharmony_ci tpm_delay_timer.freq = timer_of_rate(&to_tpm) >> 3; 1528c2ecf20Sopenharmony_ci register_current_timer_delay(&tpm_delay_timer); 1538c2ecf20Sopenharmony_ci#endif 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci sched_clock_register(tpm_read_sched_clock, counter_width, 1568c2ecf20Sopenharmony_ci timer_of_rate(&to_tpm) >> 3); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return clocksource_mmio_init(timer_base + TPM_CNT, 1598c2ecf20Sopenharmony_ci "imx-tpm", 1608c2ecf20Sopenharmony_ci timer_of_rate(&to_tpm) >> 3, 1618c2ecf20Sopenharmony_ci to_tpm.clkevt.rating, 1628c2ecf20Sopenharmony_ci counter_width, 1638c2ecf20Sopenharmony_ci clocksource_mmio_readl_up); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void __init tpm_clockevent_init(void) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci clockevents_config_and_register(&to_tpm.clkevt, 1698c2ecf20Sopenharmony_ci timer_of_rate(&to_tpm) >> 3, 1708c2ecf20Sopenharmony_ci 300, 1718c2ecf20Sopenharmony_ci GENMASK(counter_width - 1, 1728c2ecf20Sopenharmony_ci 1)); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int __init tpm_timer_init(struct device_node *np) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct clk *ipg; 1788c2ecf20Sopenharmony_ci int ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ipg = of_clk_get_by_name(np, "ipg"); 1818c2ecf20Sopenharmony_ci if (IS_ERR(ipg)) { 1828c2ecf20Sopenharmony_ci pr_err("tpm: failed to get ipg clk\n"); 1838c2ecf20Sopenharmony_ci return -ENODEV; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci /* enable clk before accessing registers */ 1868c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ipg); 1878c2ecf20Sopenharmony_ci if (ret) { 1888c2ecf20Sopenharmony_ci pr_err("tpm: ipg clock enable failed (%d)\n", ret); 1898c2ecf20Sopenharmony_ci clk_put(ipg); 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = timer_of_init(np, &to_tpm); 1948c2ecf20Sopenharmony_ci if (ret) 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci timer_base = timer_of_base(&to_tpm); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci counter_width = (readl(timer_base + TPM_PARAM) 2008c2ecf20Sopenharmony_ci & TPM_PARAM_WIDTH_MASK) >> TPM_PARAM_WIDTH_SHIFT; 2018c2ecf20Sopenharmony_ci /* use rating 200 for 32-bit counter and 150 for 16-bit counter */ 2028c2ecf20Sopenharmony_ci to_tpm.clkevt.rating = counter_width == 0x20 ? 200 : 150; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * Initialize tpm module to a known state 2068c2ecf20Sopenharmony_ci * 1) Counter disabled 2078c2ecf20Sopenharmony_ci * 2) TPM counter operates in up counting mode 2088c2ecf20Sopenharmony_ci * 3) Timer Overflow Interrupt disabled 2098c2ecf20Sopenharmony_ci * 4) Channel0 disabled 2108c2ecf20Sopenharmony_ci * 5) DMA transfers disabled 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci /* make sure counter is disabled */ 2138c2ecf20Sopenharmony_ci writel(0, timer_base + TPM_SC); 2148c2ecf20Sopenharmony_ci /* TOF is W1C */ 2158c2ecf20Sopenharmony_ci writel(TPM_SC_TOF_MASK, timer_base + TPM_SC); 2168c2ecf20Sopenharmony_ci writel(0, timer_base + TPM_CNT); 2178c2ecf20Sopenharmony_ci /* CHF is W1C */ 2188c2ecf20Sopenharmony_ci writel(TPM_C0SC_CHF_MASK, timer_base + TPM_C0SC); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * increase per cnt, 2228c2ecf20Sopenharmony_ci * div 8 for 32-bit counter and div 128 for 16-bit counter 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci writel(TPM_SC_CMOD_INC_PER_CNT | 2258c2ecf20Sopenharmony_ci (counter_width == 0x20 ? 2268c2ecf20Sopenharmony_ci TPM_SC_CMOD_DIV_DEFAULT : TPM_SC_CMOD_DIV_MAX), 2278c2ecf20Sopenharmony_ci timer_base + TPM_SC); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* set MOD register to maximum for free running mode */ 2308c2ecf20Sopenharmony_ci writel(GENMASK(counter_width - 1, 0), timer_base + TPM_MOD); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci tpm_clockevent_init(); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return tpm_clocksource_init(); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx7ulp, "fsl,imx7ulp-tpm", tpm_timer_init); 237