162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright 2016 Freescale Semiconductor, Inc. 462306a36Sopenharmony_ci// Copyright 2017 NXP 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/clockchips.h> 862306a36Sopenharmony_ci#include <linux/clocksource.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/sched_clock.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "timer-of.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define TPM_PARAM 0x4 1662306a36Sopenharmony_ci#define TPM_PARAM_WIDTH_SHIFT 16 1762306a36Sopenharmony_ci#define TPM_PARAM_WIDTH_MASK (0xff << 16) 1862306a36Sopenharmony_ci#define TPM_SC 0x10 1962306a36Sopenharmony_ci#define TPM_SC_CMOD_INC_PER_CNT (0x1 << 3) 2062306a36Sopenharmony_ci#define TPM_SC_CMOD_DIV_DEFAULT 0x3 2162306a36Sopenharmony_ci#define TPM_SC_CMOD_DIV_MAX 0x7 2262306a36Sopenharmony_ci#define TPM_SC_TOF_MASK (0x1 << 7) 2362306a36Sopenharmony_ci#define TPM_CNT 0x14 2462306a36Sopenharmony_ci#define TPM_MOD 0x18 2562306a36Sopenharmony_ci#define TPM_STATUS 0x1c 2662306a36Sopenharmony_ci#define TPM_STATUS_CH0F BIT(0) 2762306a36Sopenharmony_ci#define TPM_C0SC 0x20 2862306a36Sopenharmony_ci#define TPM_C0SC_CHIE BIT(6) 2962306a36Sopenharmony_ci#define TPM_C0SC_MODE_SHIFT 2 3062306a36Sopenharmony_ci#define TPM_C0SC_MODE_MASK 0x3c 3162306a36Sopenharmony_ci#define TPM_C0SC_MODE_SW_COMPARE 0x4 3262306a36Sopenharmony_ci#define TPM_C0SC_CHF_MASK (0x1 << 7) 3362306a36Sopenharmony_ci#define TPM_C0V 0x24 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int counter_width __ro_after_init; 3662306a36Sopenharmony_cistatic void __iomem *timer_base __ro_after_init; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic inline void tpm_timer_disable(void) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci unsigned int val; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* channel disable */ 4362306a36Sopenharmony_ci val = readl(timer_base + TPM_C0SC); 4462306a36Sopenharmony_ci val &= ~(TPM_C0SC_MODE_MASK | TPM_C0SC_CHIE); 4562306a36Sopenharmony_ci writel(val, timer_base + TPM_C0SC); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic inline void tpm_timer_enable(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci unsigned int val; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* channel enabled in sw compare mode */ 5362306a36Sopenharmony_ci val = readl(timer_base + TPM_C0SC); 5462306a36Sopenharmony_ci val |= (TPM_C0SC_MODE_SW_COMPARE << TPM_C0SC_MODE_SHIFT) | 5562306a36Sopenharmony_ci TPM_C0SC_CHIE; 5662306a36Sopenharmony_ci writel(val, timer_base + TPM_C0SC); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic inline void tpm_irq_acknowledge(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci writel(TPM_STATUS_CH0F, timer_base + TPM_STATUS); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic inline unsigned long tpm_read_counter(void) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return readl(timer_base + TPM_CNT); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#if defined(CONFIG_ARM) 7062306a36Sopenharmony_cistatic struct delay_timer tpm_delay_timer; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic unsigned long tpm_read_current_timer(void) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci return tpm_read_counter(); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic u64 notrace tpm_read_sched_clock(void) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci return tpm_read_counter(); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci#endif 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int tpm_set_next_event(unsigned long delta, 8462306a36Sopenharmony_ci struct clock_event_device *evt) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci unsigned long next, now; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci next = tpm_read_counter(); 8962306a36Sopenharmony_ci next += delta; 9062306a36Sopenharmony_ci writel(next, timer_base + TPM_C0V); 9162306a36Sopenharmony_ci now = tpm_read_counter(); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * NOTE: We observed in a very small probability, the bus fabric 9562306a36Sopenharmony_ci * contention between GPU and A7 may results a few cycles delay 9662306a36Sopenharmony_ci * of writing CNT registers which may cause the min_delta event got 9762306a36Sopenharmony_ci * missed, so we need add a ETIME check here in case it happened. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci return (int)(next - now) <= 0 ? -ETIME : 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int tpm_set_state_oneshot(struct clock_event_device *evt) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci tpm_timer_enable(); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int tpm_set_state_shutdown(struct clock_event_device *evt) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci tpm_timer_disable(); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic irqreturn_t tpm_timer_interrupt(int irq, void *dev_id) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct clock_event_device *evt = dev_id; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci tpm_irq_acknowledge(); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci evt->event_handler(evt); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return IRQ_HANDLED; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct timer_of to_tpm = { 12862306a36Sopenharmony_ci .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, 12962306a36Sopenharmony_ci .clkevt = { 13062306a36Sopenharmony_ci .name = "i.MX TPM Timer", 13162306a36Sopenharmony_ci .rating = 200, 13262306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ, 13362306a36Sopenharmony_ci .set_state_shutdown = tpm_set_state_shutdown, 13462306a36Sopenharmony_ci .set_state_oneshot = tpm_set_state_oneshot, 13562306a36Sopenharmony_ci .set_next_event = tpm_set_next_event, 13662306a36Sopenharmony_ci .cpumask = cpu_possible_mask, 13762306a36Sopenharmony_ci }, 13862306a36Sopenharmony_ci .of_irq = { 13962306a36Sopenharmony_ci .handler = tpm_timer_interrupt, 14062306a36Sopenharmony_ci .flags = IRQF_TIMER, 14162306a36Sopenharmony_ci }, 14262306a36Sopenharmony_ci .of_clk = { 14362306a36Sopenharmony_ci .name = "per", 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int __init tpm_clocksource_init(void) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci#if defined(CONFIG_ARM) 15062306a36Sopenharmony_ci tpm_delay_timer.read_current_timer = &tpm_read_current_timer; 15162306a36Sopenharmony_ci tpm_delay_timer.freq = timer_of_rate(&to_tpm) >> 3; 15262306a36Sopenharmony_ci register_current_timer_delay(&tpm_delay_timer); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci sched_clock_register(tpm_read_sched_clock, counter_width, 15562306a36Sopenharmony_ci timer_of_rate(&to_tpm) >> 3); 15662306a36Sopenharmony_ci#endif 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return clocksource_mmio_init(timer_base + TPM_CNT, 15962306a36Sopenharmony_ci "imx-tpm", 16062306a36Sopenharmony_ci timer_of_rate(&to_tpm) >> 3, 16162306a36Sopenharmony_ci to_tpm.clkevt.rating, 16262306a36Sopenharmony_ci counter_width, 16362306a36Sopenharmony_ci clocksource_mmio_readl_up); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void __init tpm_clockevent_init(void) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci clockevents_config_and_register(&to_tpm.clkevt, 16962306a36Sopenharmony_ci timer_of_rate(&to_tpm) >> 3, 17062306a36Sopenharmony_ci 300, 17162306a36Sopenharmony_ci GENMASK(counter_width - 1, 17262306a36Sopenharmony_ci 1)); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int __init tpm_timer_init(struct device_node *np) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct clk *ipg; 17862306a36Sopenharmony_ci int ret; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci ipg = of_clk_get_by_name(np, "ipg"); 18162306a36Sopenharmony_ci if (IS_ERR(ipg)) { 18262306a36Sopenharmony_ci pr_err("tpm: failed to get ipg clk\n"); 18362306a36Sopenharmony_ci return -ENODEV; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci /* enable clk before accessing registers */ 18662306a36Sopenharmony_ci ret = clk_prepare_enable(ipg); 18762306a36Sopenharmony_ci if (ret) { 18862306a36Sopenharmony_ci pr_err("tpm: ipg clock enable failed (%d)\n", ret); 18962306a36Sopenharmony_ci clk_put(ipg); 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = timer_of_init(np, &to_tpm); 19462306a36Sopenharmony_ci if (ret) 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci timer_base = timer_of_base(&to_tpm); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci counter_width = (readl(timer_base + TPM_PARAM) 20062306a36Sopenharmony_ci & TPM_PARAM_WIDTH_MASK) >> TPM_PARAM_WIDTH_SHIFT; 20162306a36Sopenharmony_ci /* use rating 200 for 32-bit counter and 150 for 16-bit counter */ 20262306a36Sopenharmony_ci to_tpm.clkevt.rating = counter_width == 0x20 ? 200 : 150; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * Initialize tpm module to a known state 20662306a36Sopenharmony_ci * 1) Counter disabled 20762306a36Sopenharmony_ci * 2) TPM counter operates in up counting mode 20862306a36Sopenharmony_ci * 3) Timer Overflow Interrupt disabled 20962306a36Sopenharmony_ci * 4) Channel0 disabled 21062306a36Sopenharmony_ci * 5) DMA transfers disabled 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci /* make sure counter is disabled */ 21362306a36Sopenharmony_ci writel(0, timer_base + TPM_SC); 21462306a36Sopenharmony_ci /* TOF is W1C */ 21562306a36Sopenharmony_ci writel(TPM_SC_TOF_MASK, timer_base + TPM_SC); 21662306a36Sopenharmony_ci writel(0, timer_base + TPM_CNT); 21762306a36Sopenharmony_ci /* CHF is W1C */ 21862306a36Sopenharmony_ci writel(TPM_C0SC_CHF_MASK, timer_base + TPM_C0SC); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * increase per cnt, 22262306a36Sopenharmony_ci * div 8 for 32-bit counter and div 128 for 16-bit counter 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci writel(TPM_SC_CMOD_INC_PER_CNT | 22562306a36Sopenharmony_ci (counter_width == 0x20 ? 22662306a36Sopenharmony_ci TPM_SC_CMOD_DIV_DEFAULT : TPM_SC_CMOD_DIV_MAX), 22762306a36Sopenharmony_ci timer_base + TPM_SC); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* set MOD register to maximum for free running mode */ 23062306a36Sopenharmony_ci writel(GENMASK(counter_width - 1, 0), timer_base + TPM_MOD); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci tpm_clockevent_init(); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return tpm_clocksource_init(); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ciTIMER_OF_DECLARE(imx7ulp, "fsl,imx7ulp-tpm", tpm_timer_init); 237