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