root/drivers/clocksource/timer-imx-tpm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tpm_timer_disable
  2. tpm_timer_enable
  3. tpm_irq_acknowledge
  4. tpm_read_counter
  5. tpm_read_current_timer
  6. tpm_read_sched_clock
  7. tpm_set_next_event
  8. tpm_set_state_oneshot
  9. tpm_set_state_shutdown
  10. tpm_timer_interrupt
  11. tpm_clocksource_init
  12. tpm_clockevent_init
  13. tpm_timer_init

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

/* [<][>][^][v][top][bottom][index][help] */