blob: 2c79f35813e0fb354e523b39dd33a69f31a084f6
1 | /* |
2 | * drivers/amlogic/vrtc/aml_vrtc.c |
3 | * |
4 | * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
14 | * more details. |
15 | * |
16 | */ |
17 | |
18 | #include <linux/module.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/rtc.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/delay.h> |
23 | #include <linux/io.h> |
24 | #include <linux/of.h> |
25 | #include <linux/amlogic/cpu_version.h> |
26 | #include <linux/pm_wakeup.h> |
27 | #include <linux/amlogic/scpi_protocol.h> |
28 | #include <linux/debugfs.h> |
29 | #include <linux/input.h> |
30 | #include <linux/amlogic/pm.h> |
31 | |
32 | static void __iomem *alarm_reg_vaddr; |
33 | static void __iomem *timere_low_vaddr, *timere_high_vaddr; |
34 | static unsigned int vrtc_init_date; |
35 | static int wakeup; |
36 | static int wakeup_time; |
37 | static struct input_dev *vinput_dev; |
38 | |
39 | static void send_power_btn_wakeup(void) |
40 | { |
41 | if ((get_resume_method() == RTC_WAKEUP) || |
42 | (get_resume_method() == AUTO_WAKEUP)) { |
43 | input_event(vinput_dev, EV_KEY, KEY_POWER, 1); |
44 | input_sync(vinput_dev); |
45 | input_event(vinput_dev, EV_KEY, KEY_POWER, 0); |
46 | input_sync(vinput_dev); |
47 | } |
48 | } |
49 | |
50 | static int vinput_dev_init(struct platform_device *pdev) |
51 | { |
52 | int r; |
53 | |
54 | vinput_dev = input_allocate_device(); |
55 | if (!vinput_dev) |
56 | return -ENOMEM; |
57 | |
58 | vinput_dev->name = "aml_vkeypad"; |
59 | vinput_dev->phys = "keypad/input0"; |
60 | vinput_dev->id.vendor = 0x0001; |
61 | vinput_dev->id.product = 0x0001; |
62 | vinput_dev->id.version = 0x0100; |
63 | set_bit(EV_KEY, vinput_dev->evbit); |
64 | set_bit(KEY_POWER, vinput_dev->keybit); |
65 | |
66 | vinput_dev->dev.parent = &pdev->dev; |
67 | device_init_wakeup(&vinput_dev->dev, 1); |
68 | |
69 | r = input_register_device(vinput_dev); |
70 | if (r) { |
71 | pr_err("failed to register power button: %d\n", r); |
72 | input_free_device(vinput_dev); |
73 | } |
74 | |
75 | return r; |
76 | } |
77 | |
78 | #define TIME_LEN 10 |
79 | static int parse_init_date(const char *date) |
80 | { |
81 | char local_str[TIME_LEN + 1]; |
82 | char *year_s, *month_s, *day_s, *str; |
83 | unsigned int year_d, month_d, day_d; |
84 | int ret; |
85 | |
86 | if (strlen(date) != 10) |
87 | return -1; |
88 | memset(local_str, 0, TIME_LEN + 1); |
89 | strncpy(local_str, date, TIME_LEN); |
90 | str = local_str; |
91 | year_s = strsep(&str, "/"); |
92 | if (!year_s) |
93 | return -1; |
94 | month_s = strsep(&str, "/"); |
95 | if (!month_s) |
96 | return -1; |
97 | day_s = str; |
98 | pr_debug("year: %s\nmonth: %s\nday: %s\n", year_s, month_s, day_s); |
99 | ret = kstrtou32(year_s, 10, &year_d); |
100 | if (ret < 0 || year_d > 2100 || year_d < 1900) |
101 | return -1; |
102 | ret = kstrtou32(month_s, 10, &month_d); |
103 | if (ret < 0 || month_d > 12) |
104 | return -1; |
105 | ret = kstrtou32(day_s, 10, &day_d); |
106 | if (ret < 0 || day_d > 31) |
107 | return -1; |
108 | vrtc_init_date = mktime(year_d, month_d, day_d, 0, 0, 0); |
109 | return 0; |
110 | } |
111 | |
112 | static u32 timere_read(void) |
113 | { |
114 | u32 time = 0; |
115 | unsigned long long te = 0, temp = 0; |
116 | |
117 | /*timeE high+low, first read low, second read high*/ |
118 | te = readl(timere_low_vaddr); |
119 | temp = readl(timere_high_vaddr); |
120 | te += (temp << 32); |
121 | do_div(te, 1000000); |
122 | time = (u32)te; |
123 | pr_debug("----------time_e: %us\n", time); |
124 | return time; |
125 | } |
126 | |
127 | static u32 read_te(void) |
128 | { |
129 | u32 time = 0; |
130 | |
131 | if (timere_low_vaddr && timere_high_vaddr) { |
132 | time = timere_read(); |
133 | time += vrtc_init_date; |
134 | } |
135 | return time; |
136 | } |
137 | |
138 | static int aml_vrtc_read_time(struct device *dev, struct rtc_time *tm) |
139 | { |
140 | u32 time_t = read_te(); |
141 | |
142 | rtc_time_to_tm(time_t, tm); |
143 | |
144 | return 0; |
145 | } |
146 | |
147 | static int aml_rtc_write_time(struct device *dev, struct rtc_time *tm) |
148 | { |
149 | unsigned long time_t; |
150 | unsigned int time = 0; |
151 | |
152 | rtc_tm_to_time(tm, &time_t); |
153 | pr_debug("aml_rtc : write the rtc time, time is %ld\n", time_t); |
154 | |
155 | if (timere_low_vaddr && timere_high_vaddr) |
156 | time = timere_read(); |
157 | vrtc_init_date = (unsigned int)time_t - time; |
158 | |
159 | return 0; |
160 | } |
161 | |
162 | static int set_wakeup_time(unsigned long time) |
163 | { |
164 | int ret = -1; |
165 | |
166 | if (alarm_reg_vaddr) { |
167 | writel(time, alarm_reg_vaddr); |
168 | ret = 0; |
169 | pr_debug("set_wakeup_time: %lu\n", time); |
170 | } |
171 | return ret; |
172 | } |
173 | |
174 | static int aml_vrtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
175 | { |
176 | unsigned long alarm_secs, cur_secs; |
177 | int ret; |
178 | struct rtc_device *vrtc; |
179 | |
180 | struct rtc_time cur_rtc_time; |
181 | |
182 | vrtc = dev_get_drvdata(dev); |
183 | |
184 | if (alarm->enabled) { |
185 | ret = rtc_tm_to_time(&alarm->time, &alarm_secs); |
186 | if (ret) |
187 | return ret; |
188 | aml_vrtc_read_time(dev, &cur_rtc_time); |
189 | ret = rtc_tm_to_time(&cur_rtc_time, &cur_secs); |
190 | if (alarm_secs >= cur_secs) { |
191 | alarm_secs = alarm_secs - cur_secs; |
192 | ret = set_wakeup_time(alarm_secs); |
193 | if (ret < 0) |
194 | return ret; |
195 | pr_debug("system will wakeup %lus later\n", alarm_secs); |
196 | } |
197 | } |
198 | return 0; |
199 | } |
200 | |
201 | static const struct rtc_class_ops aml_vrtc_ops = { |
202 | .read_time = aml_vrtc_read_time, |
203 | .set_time = aml_rtc_write_time, |
204 | .set_alarm = aml_vrtc_set_alarm, |
205 | }; |
206 | |
207 | static int aml_vrtc_probe(struct platform_device *pdev) |
208 | { |
209 | struct rtc_device *vrtc; |
210 | int ret; |
211 | u32 paddr = 0; |
212 | const char *str; |
213 | u32 vrtc_val; |
214 | |
215 | ret = of_property_read_u32(pdev->dev.of_node, |
216 | "alarm_reg_addr", &paddr); |
217 | if (!ret) { |
218 | pr_debug("alarm_reg_paddr: 0x%x\n", paddr); |
219 | alarm_reg_vaddr = ioremap(paddr, 0x4); |
220 | } |
221 | |
222 | ret = of_property_read_u32(pdev->dev.of_node, |
223 | "timer_e_addr", &paddr); |
224 | if (!ret) { |
225 | pr_debug("timer_e_paddr: 0x%x\n", paddr); |
226 | timere_low_vaddr = ioremap(paddr, 0x4); |
227 | timere_high_vaddr = ioremap(paddr + 4, 0x4); |
228 | } |
229 | |
230 | ret = of_property_read_u32(pdev->dev.of_node, |
231 | "timer_e_addr", &paddr); |
232 | ret = of_property_read_string(pdev->dev.of_node, "init_date", &str); |
233 | if (!ret) { |
234 | pr_debug("init_date: %s\n", str); |
235 | if (!scpi_get_vrtc(&vrtc_val)) { |
236 | if (!vrtc_val) |
237 | parse_init_date(str); |
238 | else { |
239 | vrtc_init_date = vrtc_val; |
240 | pr_debug("get vrtc: %us\n", vrtc_init_date); |
241 | } |
242 | } else |
243 | parse_init_date(str); |
244 | } |
245 | device_init_wakeup(&pdev->dev, 1); |
246 | vrtc = rtc_device_register("aml_vrtc", &pdev->dev, |
247 | &aml_vrtc_ops, THIS_MODULE); |
248 | if (!vrtc) |
249 | return -1; |
250 | platform_set_drvdata(pdev, vrtc); |
251 | |
252 | vinput_dev_init(pdev); |
253 | |
254 | debugfs_create_u32("wakeup", 0644, NULL, &wakeup); |
255 | debugfs_create_u32("wakeup_time", 0644, NULL, &wakeup_time); |
256 | |
257 | return 0; |
258 | } |
259 | |
260 | static int aml_vrtc_remove(struct platform_device *dev) |
261 | { |
262 | struct rtc_device *vrtc = platform_get_drvdata(dev); |
263 | |
264 | input_unregister_device(vinput_dev); |
265 | |
266 | rtc_device_unregister(vrtc); |
267 | |
268 | return 0; |
269 | } |
270 | |
271 | static int aml_vrtc_resume(struct platform_device *pdev) |
272 | { |
273 | set_wakeup_time(0); |
274 | |
275 | /* If timeE < 20, EE domain is thutdown, timerE is not |
276 | * work during suspend. we need get vrtc value. |
277 | */ |
278 | if (read_te() < 20) |
279 | if (!scpi_get_vrtc(&vrtc_init_date)) |
280 | pr_debug("get vrtc: %us\n", vrtc_init_date); |
281 | |
282 | if (wakeup > 0) { |
283 | pr_info("aml_vrtc_suspend wakeup=%d\n", wakeup); |
284 | send_power_btn_wakeup(); |
285 | } |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | static int aml_vrtc_suspend(struct platform_device *pdev, pm_message_t state) |
291 | { |
292 | u32 vrtc_val; |
293 | |
294 | if (wakeup_time > 0) { |
295 | pr_info("aml_vrtc_suspend wakeup_time=%d\n", wakeup_time); |
296 | set_wakeup_time(wakeup_time); |
297 | } |
298 | |
299 | vrtc_val = read_te(); |
300 | |
301 | if (scpi_set_vrtc(vrtc_val)) |
302 | pr_debug("vrtc setting fail.\n"); |
303 | |
304 | return 0; |
305 | } |
306 | |
307 | |
308 | static void aml_vrtc_shutdown(struct platform_device *pdev) |
309 | { |
310 | u32 vrtc_val; |
311 | struct timespec new_system; |
312 | |
313 | vrtc_val = read_te(); |
314 | getnstimeofday(&new_system); |
315 | |
316 | vrtc_val = (u32)new_system.tv_sec; |
317 | |
318 | if (scpi_set_vrtc(vrtc_val)) |
319 | pr_debug("vrtc setting fail.\n"); |
320 | } |
321 | |
322 | |
323 | static const struct of_device_id aml_vrtc_dt_match[] = { |
324 | { .compatible = "amlogic, aml_vrtc"}, |
325 | {}, |
326 | }; |
327 | |
328 | struct platform_driver aml_vrtc_driver = { |
329 | .driver = { |
330 | .name = "aml_vrtc", |
331 | .owner = THIS_MODULE, |
332 | .of_match_table = aml_vrtc_dt_match, |
333 | }, |
334 | .probe = aml_vrtc_probe, |
335 | .remove = aml_vrtc_remove, |
336 | .resume = aml_vrtc_resume, |
337 | .suspend = aml_vrtc_suspend, |
338 | .shutdown = aml_vrtc_shutdown, |
339 | }; |
340 | |
341 | static int __init aml_vrtc_init(void) |
342 | { |
343 | return platform_driver_register(&aml_vrtc_driver); |
344 | } |
345 | |
346 | static void __init aml_vrtc_exit(void) |
347 | { |
348 | return platform_driver_unregister(&aml_vrtc_driver); |
349 | } |
350 | |
351 | module_init(aml_vrtc_init); |
352 | module_exit(aml_vrtc_exit); |
353 | |
354 | MODULE_DESCRIPTION("Amlogic internal vrtc driver"); |
355 | MODULE_LICENSE("GPL"); |
356 |