1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // Copyright 2016 Freescale Semiconductor, Inc. 4 // Copyright 2017 NXP 5 6 #include <linux/clk.h> 7 #include <linux/clockchips.h> 8 #include <linux/clocksource.h> 9 #include <linux/delay.h> 10 #include <linux/interrupt.h> 11 #include <linux/sched_clock.h> 12 13 #include "timer-of.h" 14 15 #define TPM_PARAM 0x4 16 #define TPM_PARAM_WIDTH_SHIFT 16 17 #define TPM_PARAM_WIDTH_MASK (0xff << 16) 18 #define TPM_SC 0x10 19 #define TPM_SC_CMOD_INC_PER_CNT (0x1 << 3) 20 #define TPM_SC_CMOD_DIV_DEFAULT 0x3 21 #define TPM_SC_CMOD_DIV_MAX 0x7 22 #define TPM_SC_TOF_MASK (0x1 << 7) 23 #define TPM_CNT 0x14 24 #define TPM_MOD 0x18 25 #define TPM_STATUS 0x1c 26 #define TPM_STATUS_CH0F BIT(0) 27 #define TPM_C0SC 0x20 28 #define TPM_C0SC_CHIE BIT(6) 29 #define TPM_C0SC_MODE_SHIFT 2 30 #define TPM_C0SC_MODE_MASK 0x3c 31 #define TPM_C0SC_MODE_SW_COMPARE 0x4 32 #define TPM_C0SC_CHF_MASK (0x1 << 7) 33 #define TPM_C0V 0x24 34 35 static int counter_width; 36 static void __iomem *timer_base; 37 38 static inline void tpm_timer_disable(void) 39 { 40 unsigned int val; 41 42 /* channel disable */ 43 val = readl(timer_base + TPM_C0SC); 44 val &= ~(TPM_C0SC_MODE_MASK | TPM_C0SC_CHIE); 45 writel(val, timer_base + TPM_C0SC); 46 } 47 48 static inline void tpm_timer_enable(void) 49 { 50 unsigned int val; 51 52 /* channel enabled in sw compare mode */ 53 val = readl(timer_base + TPM_C0SC); 54 val |= (TPM_C0SC_MODE_SW_COMPARE << TPM_C0SC_MODE_SHIFT) | 55 TPM_C0SC_CHIE; 56 writel(val, timer_base + TPM_C0SC); 57 } 58 59 static inline void tpm_irq_acknowledge(void) 60 { 61 writel(TPM_STATUS_CH0F, timer_base + TPM_STATUS); 62 } 63 64 static struct delay_timer tpm_delay_timer; 65 66 static inline unsigned long tpm_read_counter(void) 67 { 68 return readl(timer_base + TPM_CNT); 69 } 70 71 static unsigned long tpm_read_current_timer(void) 72 { 73 return tpm_read_counter(); 74 } 75 76 static u64 notrace tpm_read_sched_clock(void) 77 { 78 return tpm_read_counter(); 79 } 80 81 static int tpm_set_next_event(unsigned long delta, 82 struct clock_event_device *evt) 83 { 84 unsigned long next, now; 85 86 next = tpm_read_counter(); 87 next += delta; 88 writel(next, timer_base + TPM_C0V); 89 now = tpm_read_counter(); 90 91 /* 92 * NOTE: We observed in a very small probability, the bus fabric 93 * contention between GPU and A7 may results a few cycles delay 94 * of writing CNT registers which may cause the min_delta event got 95 * missed, so we need add a ETIME check here in case it happened. 96 */ 97 return (int)(next - now) <= 0 ? -ETIME : 0; 98 } 99 100 static int tpm_set_state_oneshot(struct clock_event_device *evt) 101 { 102 tpm_timer_enable(); 103 104 return 0; 105 } 106 107 static int tpm_set_state_shutdown(struct clock_event_device *evt) 108 { 109 tpm_timer_disable(); 110 111 return 0; 112 } 113 114 static irqreturn_t tpm_timer_interrupt(int irq, void *dev_id) 115 { 116 struct clock_event_device *evt = dev_id; 117 118 tpm_irq_acknowledge(); 119 120 evt->event_handler(evt); 121 122 return IRQ_HANDLED; 123 } 124 125 static struct timer_of to_tpm = { 126 .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK, 127 .clkevt = { 128 .name = "i.MX7ULP TPM Timer", 129 .rating = 200, 130 .features = CLOCK_EVT_FEAT_ONESHOT, 131 .set_state_shutdown = tpm_set_state_shutdown, 132 .set_state_oneshot = tpm_set_state_oneshot, 133 .set_next_event = tpm_set_next_event, 134 .cpumask = cpu_possible_mask, 135 }, 136 .of_irq = { 137 .handler = tpm_timer_interrupt, 138 .flags = IRQF_TIMER | IRQF_IRQPOLL, 139 }, 140 .of_clk = { 141 .name = "per", 142 }, 143 }; 144 145 static int __init tpm_clocksource_init(void) 146 { 147 tpm_delay_timer.read_current_timer = &tpm_read_current_timer; 148 tpm_delay_timer.freq = timer_of_rate(&to_tpm) >> 3; 149 register_current_timer_delay(&tpm_delay_timer); 150 151 sched_clock_register(tpm_read_sched_clock, counter_width, 152 timer_of_rate(&to_tpm) >> 3); 153 154 return clocksource_mmio_init(timer_base + TPM_CNT, 155 "imx-tpm", 156 timer_of_rate(&to_tpm) >> 3, 157 to_tpm.clkevt.rating, 158 counter_width, 159 clocksource_mmio_readl_up); 160 } 161 162 static void __init tpm_clockevent_init(void) 163 { 164 clockevents_config_and_register(&to_tpm.clkevt, 165 timer_of_rate(&to_tpm) >> 3, 166 300, 167 GENMASK(counter_width - 1, 168 1)); 169 } 170 171 static int __init tpm_timer_init(struct device_node *np) 172 { 173 struct clk *ipg; 174 int ret; 175 176 ipg = of_clk_get_by_name(np, "ipg"); 177 if (IS_ERR(ipg)) { 178 pr_err("tpm: failed to get ipg clk\n"); 179 return -ENODEV; 180 } 181 /* enable clk before accessing registers */ 182 ret = clk_prepare_enable(ipg); 183 if (ret) { 184 pr_err("tpm: ipg clock enable failed (%d)\n", ret); 185 clk_put(ipg); 186 return ret; 187 } 188 189 ret = timer_of_init(np, &to_tpm); 190 if (ret) 191 return ret; 192 193 timer_base = timer_of_base(&to_tpm); 194 195 counter_width = (readl(timer_base + TPM_PARAM) 196 & TPM_PARAM_WIDTH_MASK) >> TPM_PARAM_WIDTH_SHIFT; 197 /* use rating 200 for 32-bit counter and 150 for 16-bit counter */ 198 to_tpm.clkevt.rating = counter_width == 0x20 ? 200 : 150; 199 200 /* 201 * Initialize tpm module to a known state 202 * 1) Counter disabled 203 * 2) TPM counter operates in up counting mode 204 * 3) Timer Overflow Interrupt disabled 205 * 4) Channel0 disabled 206 * 5) DMA transfers disabled 207 */ 208 /* make sure counter is disabled */ 209 writel(0, timer_base + TPM_SC); 210 /* TOF is W1C */ 211 writel(TPM_SC_TOF_MASK, timer_base + TPM_SC); 212 writel(0, timer_base + TPM_CNT); 213 /* CHF is W1C */ 214 writel(TPM_C0SC_CHF_MASK, timer_base + TPM_C0SC); 215 216 /* 217 * increase per cnt, 218 * div 8 for 32-bit counter and div 128 for 16-bit counter 219 */ 220 writel(TPM_SC_CMOD_INC_PER_CNT | 221 (counter_width == 0x20 ? 222 TPM_SC_CMOD_DIV_DEFAULT : TPM_SC_CMOD_DIV_MAX), 223 timer_base + TPM_SC); 224 225 /* set MOD register to maximum for free running mode */ 226 writel(GENMASK(counter_width - 1, 0), timer_base + TPM_MOD); 227 228 tpm_clockevent_init(); 229 230 return tpm_clocksource_init(); 231 } 232 TIMER_OF_DECLARE(imx7ulp, "fsl,imx7ulp-tpm", tpm_timer_init); 233