Commit | Line | Data |
---|---|---|
7b0b551d BL |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Freescale FlexTimer Module (FTM) alarm device driver. | |
4 | * | |
5 | * Copyright 2014 Freescale Semiconductor, Inc. | |
0d982de3 | 6 | * Copyright 2019-2020 NXP |
7b0b551d BL |
7 | * |
8 | */ | |
9 | ||
10 | #include <linux/device.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/io.h> | |
14 | #include <linux/of_address.h> | |
15 | #include <linux/of_irq.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/of.h> | |
18 | #include <linux/of_device.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/fsl/ftm.h> | |
21 | #include <linux/rtc.h> | |
22 | #include <linux/time.h> | |
929a3270 | 23 | #include <linux/acpi.h> |
3a8ce46c | 24 | #include <linux/pm_wakeirq.h> |
7b0b551d BL |
25 | |
26 | #define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_MASK_SHIFT) | |
27 | ||
28 | /* | |
29 | * Select Fixed frequency clock (32KHz) as clock source | |
30 | * of FlexTimer Module | |
31 | */ | |
32 | #define FTM_SC_CLKS_FIXED_FREQ 0x02 | |
33 | #define FIXED_FREQ_CLK 32000 | |
34 | ||
35 | /* Select 128 (2^7) as divider factor */ | |
36 | #define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK) | |
37 | ||
38 | /* Maximum counter value in FlexTimer's CNT registers */ | |
39 | #define MAX_COUNT_VAL 0xffff | |
40 | ||
41 | struct ftm_rtc { | |
42 | struct rtc_device *rtc_dev; | |
43 | void __iomem *base; | |
44 | bool big_endian; | |
45 | u32 alarm_freq; | |
46 | }; | |
47 | ||
48 | static inline u32 rtc_readl(struct ftm_rtc *dev, u32 reg) | |
49 | { | |
50 | if (dev->big_endian) | |
51 | return ioread32be(dev->base + reg); | |
52 | else | |
53 | return ioread32(dev->base + reg); | |
54 | } | |
55 | ||
56 | static inline void rtc_writel(struct ftm_rtc *dev, u32 reg, u32 val) | |
57 | { | |
58 | if (dev->big_endian) | |
59 | iowrite32be(val, dev->base + reg); | |
60 | else | |
61 | iowrite32(val, dev->base + reg); | |
62 | } | |
63 | ||
64 | static inline void ftm_counter_enable(struct ftm_rtc *rtc) | |
65 | { | |
66 | u32 val; | |
67 | ||
68 | /* select and enable counter clock source */ | |
69 | val = rtc_readl(rtc, FTM_SC); | |
70 | val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); | |
71 | val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ)); | |
72 | rtc_writel(rtc, FTM_SC, val); | |
73 | } | |
74 | ||
75 | static inline void ftm_counter_disable(struct ftm_rtc *rtc) | |
76 | { | |
77 | u32 val; | |
78 | ||
79 | /* disable counter clock source */ | |
80 | val = rtc_readl(rtc, FTM_SC); | |
81 | val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); | |
82 | rtc_writel(rtc, FTM_SC, val); | |
83 | } | |
84 | ||
85 | static inline void ftm_irq_acknowledge(struct ftm_rtc *rtc) | |
86 | { | |
87 | unsigned int timeout = 100; | |
88 | ||
89 | /* | |
90 | *Fix errata A-007728 for flextimer | |
91 | * If the FTM counter reaches the FTM_MOD value between | |
92 | * the reading of the TOF bit and the writing of 0 to | |
93 | * the TOF bit, the process of clearing the TOF bit | |
94 | * does not work as expected when FTMx_CONF[NUMTOF] != 0 | |
95 | * and the current TOF count is less than FTMx_CONF[NUMTOF]. | |
96 | * If the above condition is met, the TOF bit remains set. | |
97 | * If the TOF interrupt is enabled (FTMx_SC[TOIE] = 1),the | |
98 | * TOF interrupt also remains asserted. | |
99 | * | |
100 | * Above is the errata discription | |
101 | * | |
102 | * In one word: software clearing TOF bit not works when | |
103 | * FTMx_CONF[NUMTOF] was seted as nonzero and FTM counter | |
104 | * reaches the FTM_MOD value. | |
105 | * | |
106 | * The workaround is clearing TOF bit until it works | |
107 | * (FTM counter doesn't always reache the FTM_MOD anyway), | |
108 | * which may cost some cycles. | |
109 | */ | |
110 | while ((FTM_SC_TOF & rtc_readl(rtc, FTM_SC)) && timeout--) | |
111 | rtc_writel(rtc, FTM_SC, rtc_readl(rtc, FTM_SC) & (~FTM_SC_TOF)); | |
112 | } | |
113 | ||
114 | static inline void ftm_irq_enable(struct ftm_rtc *rtc) | |
115 | { | |
116 | u32 val; | |
117 | ||
118 | val = rtc_readl(rtc, FTM_SC); | |
119 | val |= FTM_SC_TOIE; | |
120 | rtc_writel(rtc, FTM_SC, val); | |
121 | } | |
122 | ||
123 | static inline void ftm_irq_disable(struct ftm_rtc *rtc) | |
124 | { | |
125 | u32 val; | |
126 | ||
127 | val = rtc_readl(rtc, FTM_SC); | |
128 | val &= ~FTM_SC_TOIE; | |
129 | rtc_writel(rtc, FTM_SC, val); | |
130 | } | |
131 | ||
132 | static inline void ftm_reset_counter(struct ftm_rtc *rtc) | |
133 | { | |
134 | /* | |
135 | * The CNT register contains the FTM counter value. | |
136 | * Reset clears the CNT register. Writing any value to COUNT | |
137 | * updates the counter with its initial value, CNTIN. | |
138 | */ | |
139 | rtc_writel(rtc, FTM_CNT, 0x00); | |
140 | } | |
141 | ||
142 | static void ftm_clean_alarm(struct ftm_rtc *rtc) | |
143 | { | |
144 | ftm_counter_disable(rtc); | |
145 | ||
146 | rtc_writel(rtc, FTM_CNTIN, 0x00); | |
147 | rtc_writel(rtc, FTM_MOD, ~0U); | |
148 | ||
149 | ftm_reset_counter(rtc); | |
150 | } | |
151 | ||
152 | static irqreturn_t ftm_rtc_alarm_interrupt(int irq, void *dev) | |
153 | { | |
154 | struct ftm_rtc *rtc = dev; | |
155 | ||
9c328c9d BL |
156 | rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); |
157 | ||
7b0b551d BL |
158 | ftm_irq_acknowledge(rtc); |
159 | ftm_irq_disable(rtc); | |
160 | ftm_clean_alarm(rtc); | |
161 | ||
162 | return IRQ_HANDLED; | |
163 | } | |
164 | ||
165 | static int ftm_rtc_alarm_irq_enable(struct device *dev, | |
166 | unsigned int enabled) | |
167 | { | |
168 | struct ftm_rtc *rtc = dev_get_drvdata(dev); | |
169 | ||
170 | if (enabled) | |
171 | ftm_irq_enable(rtc); | |
172 | else | |
173 | ftm_irq_disable(rtc); | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
178 | /* | |
179 | * Note: | |
180 | * The function is not really getting time from the RTC | |
181 | * since FlexTimer is not a RTC device, but we need to | |
182 | * get time to setup alarm, so we are using system time | |
183 | * for now. | |
184 | */ | |
185 | static int ftm_rtc_read_time(struct device *dev, struct rtc_time *tm) | |
186 | { | |
9323e963 | 187 | rtc_time64_to_tm(ktime_get_real_seconds(), tm); |
7b0b551d BL |
188 | |
189 | return 0; | |
190 | } | |
191 | ||
192 | static int ftm_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) | |
193 | { | |
194 | return 0; | |
195 | } | |
196 | ||
197 | /* | |
198 | * 1. Select fixed frequency clock (32KHz) as clock source; | |
199 | * 2. Select 128 (2^7) as divider factor; | |
200 | * So clock is 250 Hz (32KHz/128). | |
201 | * | |
202 | * 3. FlexTimer's CNT register is a 32bit register, | |
203 | * but the register's 16 bit as counter value,it's other 16 bit | |
204 | * is reserved.So minimum counter value is 0x0,maximum counter | |
205 | * value is 0xffff. | |
206 | * So max alarm value is 262 (65536 / 250) seconds | |
207 | */ | |
208 | static int ftm_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) | |
209 | { | |
bb451661 | 210 | time64_t alm_time; |
9323e963 | 211 | unsigned long long cycle; |
7b0b551d BL |
212 | struct ftm_rtc *rtc = dev_get_drvdata(dev); |
213 | ||
9323e963 | 214 | alm_time = rtc_tm_to_time64(&alm->time); |
7b0b551d BL |
215 | |
216 | ftm_clean_alarm(rtc); | |
bb451661 | 217 | cycle = (alm_time - ktime_get_real_seconds()) * rtc->alarm_freq; |
7b0b551d BL |
218 | if (cycle > MAX_COUNT_VAL) { |
219 | pr_err("Out of alarm range {0~262} seconds.\n"); | |
220 | return -ERANGE; | |
221 | } | |
222 | ||
223 | ftm_irq_disable(rtc); | |
224 | ||
225 | /* | |
226 | * The counter increments until the value of MOD is reached, | |
227 | * at which point the counter is reloaded with the value of CNTIN. | |
228 | * The TOF (the overflow flag) bit is set when the FTM counter | |
229 | * changes from MOD to CNTIN. So we should using the cycle - 1. | |
230 | */ | |
231 | rtc_writel(rtc, FTM_MOD, cycle - 1); | |
232 | ||
233 | ftm_counter_enable(rtc); | |
234 | ftm_irq_enable(rtc); | |
235 | ||
236 | return 0; | |
237 | ||
238 | } | |
239 | ||
240 | static const struct rtc_class_ops ftm_rtc_ops = { | |
241 | .read_time = ftm_rtc_read_time, | |
242 | .read_alarm = ftm_rtc_read_alarm, | |
243 | .set_alarm = ftm_rtc_set_alarm, | |
244 | .alarm_irq_enable = ftm_rtc_alarm_irq_enable, | |
245 | }; | |
246 | ||
247 | static int ftm_rtc_probe(struct platform_device *pdev) | |
248 | { | |
7b0b551d BL |
249 | int irq; |
250 | int ret; | |
251 | struct ftm_rtc *rtc; | |
252 | ||
253 | rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); | |
254 | if (unlikely(!rtc)) { | |
255 | dev_err(&pdev->dev, "cannot alloc memory for rtc\n"); | |
256 | return -ENOMEM; | |
257 | } | |
258 | ||
259 | platform_set_drvdata(pdev, rtc); | |
260 | ||
261 | rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); | |
262 | if (IS_ERR(rtc->rtc_dev)) | |
263 | return PTR_ERR(rtc->rtc_dev); | |
264 | ||
89576beb | 265 | rtc->base = devm_platform_ioremap_resource(pdev, 0); |
7b0b551d BL |
266 | if (IS_ERR(rtc->base)) { |
267 | dev_err(&pdev->dev, "cannot ioremap resource for rtc\n"); | |
268 | return PTR_ERR(rtc->base); | |
269 | } | |
270 | ||
929a3270 | 271 | irq = platform_get_irq(pdev, 0); |
944ed452 | 272 | if (irq < 0) |
929a3270 | 273 | return irq; |
7b0b551d BL |
274 | |
275 | ret = devm_request_irq(&pdev->dev, irq, ftm_rtc_alarm_interrupt, | |
3a8ce46c | 276 | 0, dev_name(&pdev->dev), rtc); |
7b0b551d BL |
277 | if (ret < 0) { |
278 | dev_err(&pdev->dev, "failed to request irq\n"); | |
279 | return ret; | |
280 | } | |
281 | ||
929a3270 PM |
282 | rtc->big_endian = |
283 | device_property_read_bool(&pdev->dev, "big-endian"); | |
284 | ||
7b0b551d BL |
285 | rtc->alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV; |
286 | rtc->rtc_dev->ops = &ftm_rtc_ops; | |
287 | ||
288 | device_init_wakeup(&pdev->dev, true); | |
3a8ce46c RW |
289 | ret = dev_pm_set_wake_irq(&pdev->dev, irq); |
290 | if (ret) | |
291 | dev_err(&pdev->dev, "failed to enable irq wake\n"); | |
7b0b551d | 292 | |
fdcfd854 | 293 | ret = devm_rtc_register_device(rtc->rtc_dev); |
7b0b551d BL |
294 | if (ret) { |
295 | dev_err(&pdev->dev, "can't register rtc device\n"); | |
296 | return ret; | |
297 | } | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
302 | static const struct of_device_id ftm_rtc_match[] = { | |
303 | { .compatible = "fsl,ls1012a-ftm-alarm", }, | |
304 | { .compatible = "fsl,ls1021a-ftm-alarm", }, | |
305 | { .compatible = "fsl,ls1028a-ftm-alarm", }, | |
306 | { .compatible = "fsl,ls1043a-ftm-alarm", }, | |
307 | { .compatible = "fsl,ls1046a-ftm-alarm", }, | |
308 | { .compatible = "fsl,ls1088a-ftm-alarm", }, | |
309 | { .compatible = "fsl,ls208xa-ftm-alarm", }, | |
310 | { .compatible = "fsl,lx2160a-ftm-alarm", }, | |
311 | { }, | |
312 | }; | |
7fcb8618 | 313 | MODULE_DEVICE_TABLE(of, ftm_rtc_match); |
7b0b551d | 314 | |
929a3270 | 315 | static const struct acpi_device_id ftm_imx_acpi_ids[] = { |
0d982de3 | 316 | {"NXP0014",}, |
929a3270 PM |
317 | { } |
318 | }; | |
319 | MODULE_DEVICE_TABLE(acpi, ftm_imx_acpi_ids); | |
320 | ||
7b0b551d BL |
321 | static struct platform_driver ftm_rtc_driver = { |
322 | .probe = ftm_rtc_probe, | |
323 | .driver = { | |
324 | .name = "ftm-alarm", | |
325 | .of_match_table = ftm_rtc_match, | |
929a3270 | 326 | .acpi_match_table = ACPI_PTR(ftm_imx_acpi_ids), |
7b0b551d BL |
327 | }, |
328 | }; | |
329 | ||
1ff56edf | 330 | module_platform_driver(ftm_rtc_driver); |
7b0b551d BL |
331 | |
332 | MODULE_DESCRIPTION("NXP/Freescale FlexTimer alarm driver"); | |
333 | MODULE_AUTHOR("Biwen Li <biwen.li@nxp.com>"); | |
334 | MODULE_LICENSE("GPL"); |