]> Git Repo - linux.git/blob - drivers/clocksource/timer-imx-sysctr.c
ACPI: CPPC: Adjust debug messages in amd_set_max_freq_ratio() to warn
[linux.git] / drivers / clocksource / timer-imx-sysctr.c
1 // SPDX-License-Identifier: GPL-2.0+
2 //
3 // Copyright 2017-2019 NXP
4
5 #include <linux/interrupt.h>
6 #include <linux/clockchips.h>
7 #include <linux/slab.h>
8
9 #include "timer-of.h"
10
11 #define CMP_OFFSET      0x10000
12 #define RD_OFFSET       0x20000
13
14 #define CNTCV_LO        0x8
15 #define CNTCV_HI        0xc
16 #define CMPCV_LO        (CMP_OFFSET + 0x20)
17 #define CMPCV_HI        (CMP_OFFSET + 0x24)
18 #define CMPCR           (CMP_OFFSET + 0x2c)
19 #define CNTCV_LO_IMX95  (RD_OFFSET + 0x8)
20 #define CNTCV_HI_IMX95  (RD_OFFSET + 0xc)
21
22 #define SYS_CTR_EN              0x1
23 #define SYS_CTR_IRQ_MASK        0x2
24
25 #define SYS_CTR_CLK_DIV         0x3
26
27 struct sysctr_private {
28         u32 cmpcr;
29         u32 lo_off;
30         u32 hi_off;
31 };
32
33 static void sysctr_timer_enable(struct clock_event_device *evt, bool enable)
34 {
35         struct timer_of *to = to_timer_of(evt);
36         struct sysctr_private *priv = to->private_data;
37         void __iomem *base = timer_of_base(to);
38
39         writel(enable ? priv->cmpcr | SYS_CTR_EN : priv->cmpcr, base + CMPCR);
40 }
41
42 static void sysctr_irq_acknowledge(struct clock_event_device *evt)
43 {
44         /*
45          * clear the enable bit(EN =0) will clear
46          * the status bit(ISTAT = 0), then the interrupt
47          * signal will be negated(acknowledged).
48          */
49         sysctr_timer_enable(evt, false);
50 }
51
52 static inline u64 sysctr_read_counter(struct clock_event_device *evt)
53 {
54         struct timer_of *to = to_timer_of(evt);
55         struct sysctr_private *priv = to->private_data;
56         void __iomem *base = timer_of_base(to);
57         u32 cnt_hi, tmp_hi, cnt_lo;
58
59         do {
60                 cnt_hi = readl_relaxed(base + priv->hi_off);
61                 cnt_lo = readl_relaxed(base + priv->lo_off);
62                 tmp_hi = readl_relaxed(base + priv->hi_off);
63         } while (tmp_hi != cnt_hi);
64
65         return  ((u64) cnt_hi << 32) | cnt_lo;
66 }
67
68 static int sysctr_set_next_event(unsigned long delta,
69                                  struct clock_event_device *evt)
70 {
71         struct timer_of *to = to_timer_of(evt);
72         void __iomem *base = timer_of_base(to);
73         u32 cmp_hi, cmp_lo;
74         u64 next;
75
76         sysctr_timer_enable(evt, false);
77
78         next = sysctr_read_counter(evt);
79
80         next += delta;
81
82         cmp_hi = (next >> 32) & 0x00fffff;
83         cmp_lo = next & 0xffffffff;
84
85         writel_relaxed(cmp_hi, base + CMPCV_HI);
86         writel_relaxed(cmp_lo, base + CMPCV_LO);
87
88         sysctr_timer_enable(evt, true);
89
90         return 0;
91 }
92
93 static int sysctr_set_state_oneshot(struct clock_event_device *evt)
94 {
95         return 0;
96 }
97
98 static int sysctr_set_state_shutdown(struct clock_event_device *evt)
99 {
100         sysctr_timer_enable(evt, false);
101
102         return 0;
103 }
104
105 static irqreturn_t sysctr_timer_interrupt(int irq, void *dev_id)
106 {
107         struct clock_event_device *evt = dev_id;
108
109         sysctr_irq_acknowledge(evt);
110
111         evt->event_handler(evt);
112
113         return IRQ_HANDLED;
114 }
115
116 static struct timer_of to_sysctr = {
117         .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
118         .clkevt = {
119                 .name                   = "i.MX system counter timer",
120                 .features               = CLOCK_EVT_FEAT_ONESHOT |
121                                                 CLOCK_EVT_FEAT_DYNIRQ,
122                 .set_state_oneshot      = sysctr_set_state_oneshot,
123                 .set_next_event         = sysctr_set_next_event,
124                 .set_state_shutdown     = sysctr_set_state_shutdown,
125                 .rating                 = 200,
126         },
127         .of_irq = {
128                 .handler                = sysctr_timer_interrupt,
129                 .flags                  = IRQF_TIMER,
130         },
131         .of_clk = {
132                 .name = "per",
133         },
134 };
135
136 static int __init __sysctr_timer_init(struct device_node *np)
137 {
138         struct sysctr_private *priv;
139         void __iomem *base;
140         int ret;
141
142         priv = kzalloc(sizeof(struct sysctr_private), GFP_KERNEL);
143         if (!priv)
144                 return -ENOMEM;
145
146         ret = timer_of_init(np, &to_sysctr);
147         if (ret) {
148                 kfree(priv);
149                 return ret;
150         }
151
152         if (!of_property_read_bool(np, "nxp,no-divider")) {
153                 /* system counter clock is divided by 3 internally */
154                 to_sysctr.of_clk.rate /= SYS_CTR_CLK_DIV;
155         }
156
157         to_sysctr.clkevt.cpumask = cpu_possible_mask;
158         to_sysctr.private_data = priv;
159
160         base = timer_of_base(&to_sysctr);
161         priv->cmpcr = readl(base + CMPCR) & ~SYS_CTR_EN;
162
163         return 0;
164 }
165
166 static int __init sysctr_timer_init(struct device_node *np)
167 {
168         struct sysctr_private *priv;
169         int ret;
170
171         ret = __sysctr_timer_init(np);
172         if (ret)
173                 return ret;
174
175         priv = to_sysctr.private_data;
176         priv->lo_off = CNTCV_LO;
177         priv->hi_off = CNTCV_HI;
178
179         clockevents_config_and_register(&to_sysctr.clkevt,
180                                         timer_of_rate(&to_sysctr),
181                                         0xff, 0x7fffffff);
182
183         return 0;
184 }
185
186 static int __init sysctr_timer_imx95_init(struct device_node *np)
187 {
188         struct sysctr_private *priv;
189         int ret;
190
191         ret = __sysctr_timer_init(np);
192         if (ret)
193                 return ret;
194
195         priv = to_sysctr.private_data;
196         priv->lo_off = CNTCV_LO_IMX95;
197         priv->hi_off = CNTCV_HI_IMX95;
198
199         clockevents_config_and_register(&to_sysctr.clkevt,
200                                         timer_of_rate(&to_sysctr),
201                                         0xff, 0x7fffffff);
202
203         return 0;
204 }
205
206 TIMER_OF_DECLARE(sysctr_timer, "nxp,sysctr-timer", sysctr_timer_init);
207 TIMER_OF_DECLARE(sysctr_timer_imx95, "nxp,imx95-sysctr-timer", sysctr_timer_imx95_init);
This page took 0.04321 seconds and 4 git commands to generate.