blob: 1f102f36fbcd10c51aa845c097fa61fc10ac0f1c
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 | |
29 | static void __iomem *alarm_reg_vaddr; |
30 | static void __iomem *timere_low_vaddr, *timere_high_vaddr; |
31 | static unsigned int vrtc_init_date; |
32 | |
33 | #define TIME_LEN 10 |
34 | static int parse_init_date(const char *date) |
35 | { |
36 | char local_str[TIME_LEN + 1]; |
37 | char *year_s, *month_s, *day_s, *str; |
38 | unsigned int year_d, month_d, day_d; |
39 | int ret; |
40 | |
41 | if (strlen(date) != 10) |
42 | return -1; |
43 | memset(local_str, 0, TIME_LEN + 1); |
44 | strncpy(local_str, date, TIME_LEN); |
45 | str = local_str; |
46 | year_s = strsep(&str, "/"); |
47 | if (!year_s) |
48 | return -1; |
49 | month_s = strsep(&str, "/"); |
50 | if (!month_s) |
51 | return -1; |
52 | day_s = str; |
53 | pr_debug("year: %s\nmonth: %s\nday: %s\n", year_s, month_s, day_s); |
54 | ret = kstrtou32(year_s, 10, &year_d); |
55 | if (ret < 0 || year_d > 2100 || year_d < 1900) |
56 | return -1; |
57 | ret = kstrtou32(month_s, 10, &month_d); |
58 | if (ret < 0 || month_d > 12) |
59 | return -1; |
60 | ret = kstrtou32(day_s, 10, &day_d); |
61 | if (ret < 0 || day_d > 31) |
62 | return -1; |
63 | vrtc_init_date = mktime(year_d, month_d, day_d, 0, 0, 0); |
64 | return 0; |
65 | } |
66 | |
67 | static u32 timere_read(void) |
68 | { |
69 | u32 time = 0; |
70 | unsigned long long te = 0, temp = 0; |
71 | |
72 | /*timeE high+low, first read low, second read high*/ |
73 | te = readl(timere_low_vaddr); |
74 | temp = readl(timere_high_vaddr); |
75 | te += (temp << 32); |
76 | do_div(te, 1000000); |
77 | time = (u32)te; |
78 | pr_debug("----------time_e: %us\n", time); |
79 | return time; |
80 | } |
81 | |
82 | static u32 read_te(void) |
83 | { |
84 | u32 time = 0; |
85 | |
86 | if (timere_low_vaddr && timere_high_vaddr) { |
87 | time = timere_read(); |
88 | time += vrtc_init_date; |
89 | } |
90 | return time; |
91 | } |
92 | |
93 | static int aml_vrtc_read_time(struct device *dev, struct rtc_time *tm) |
94 | { |
95 | u32 time_t = read_te(); |
96 | |
97 | rtc_time_to_tm(time_t, tm); |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | static int aml_rtc_write_time(struct device *dev, struct rtc_time *tm) |
103 | { |
104 | unsigned long time_t; |
105 | unsigned int time = 0; |
106 | |
107 | rtc_tm_to_time(tm, &time_t); |
108 | pr_debug("aml_rtc : write the rtc time, time is %ld\n", time_t); |
109 | |
110 | if (timere_low_vaddr && timere_high_vaddr) |
111 | time = timere_read(); |
112 | vrtc_init_date = (unsigned int)time_t - time; |
113 | |
114 | return 0; |
115 | } |
116 | |
117 | static int set_wakeup_time(unsigned long time) |
118 | { |
119 | int ret = -1; |
120 | |
121 | if (alarm_reg_vaddr) { |
122 | writel(time, alarm_reg_vaddr); |
123 | ret = 0; |
124 | pr_debug("set_wakeup_time: %lu\n", time); |
125 | } |
126 | return ret; |
127 | } |
128 | |
129 | static int aml_vrtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
130 | { |
131 | unsigned long alarm_secs, cur_secs; |
132 | int ret; |
133 | struct rtc_device *vrtc; |
134 | |
135 | struct rtc_time cur_rtc_time; |
136 | |
137 | vrtc = dev_get_drvdata(dev); |
138 | |
139 | if (alarm->enabled) { |
140 | ret = rtc_tm_to_time(&alarm->time, &alarm_secs); |
141 | if (ret) |
142 | return ret; |
143 | aml_vrtc_read_time(dev, &cur_rtc_time); |
144 | ret = rtc_tm_to_time(&cur_rtc_time, &cur_secs); |
145 | if (alarm_secs >= cur_secs) { |
146 | alarm_secs = alarm_secs - cur_secs; |
147 | ret = set_wakeup_time(alarm_secs); |
148 | if (ret < 0) |
149 | return ret; |
150 | pr_debug("system will wakeup %lus later\n", alarm_secs); |
151 | } |
152 | } |
153 | return 0; |
154 | } |
155 | |
156 | static const struct rtc_class_ops aml_vrtc_ops = { |
157 | .read_time = aml_vrtc_read_time, |
158 | .set_time = aml_rtc_write_time, |
159 | .set_alarm = aml_vrtc_set_alarm, |
160 | }; |
161 | |
162 | static int aml_vrtc_probe(struct platform_device *pdev) |
163 | { |
164 | struct rtc_device *vrtc; |
165 | int ret; |
166 | u32 paddr = 0; |
167 | const char *str; |
168 | u32 vrtc_val; |
169 | |
170 | ret = of_property_read_u32(pdev->dev.of_node, |
171 | "alarm_reg_addr", &paddr); |
172 | if (!ret) { |
173 | pr_debug("alarm_reg_paddr: 0x%x\n", paddr); |
174 | alarm_reg_vaddr = ioremap(paddr, 0x4); |
175 | } |
176 | |
177 | ret = of_property_read_u32(pdev->dev.of_node, |
178 | "timer_e_addr", &paddr); |
179 | if (!ret) { |
180 | pr_debug("timer_e_paddr: 0x%x\n", paddr); |
181 | timere_low_vaddr = ioremap(paddr, 0x4); |
182 | timere_high_vaddr = ioremap(paddr + 4, 0x4); |
183 | } |
184 | |
185 | ret = of_property_read_u32(pdev->dev.of_node, |
186 | "timer_e_addr", &paddr); |
187 | ret = of_property_read_string(pdev->dev.of_node, "init_date", &str); |
188 | if (!ret) { |
189 | pr_debug("init_date: %s\n", str); |
190 | if (!scpi_get_vrtc(&vrtc_val)) { |
191 | if (!vrtc_val) |
192 | parse_init_date(str); |
193 | else { |
194 | vrtc_init_date = vrtc_val; |
195 | pr_debug("get vrtc: %us\n", vrtc_init_date); |
196 | } |
197 | } else |
198 | parse_init_date(str); |
199 | } |
200 | device_init_wakeup(&pdev->dev, 1); |
201 | vrtc = rtc_device_register("aml_vrtc", &pdev->dev, |
202 | &aml_vrtc_ops, THIS_MODULE); |
203 | if (!vrtc) |
204 | return -1; |
205 | platform_set_drvdata(pdev, vrtc); |
206 | |
207 | return 0; |
208 | } |
209 | |
210 | static int aml_vrtc_remove(struct platform_device *dev) |
211 | { |
212 | struct rtc_device *vrtc = platform_get_drvdata(dev); |
213 | |
214 | rtc_device_unregister(vrtc); |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | static int aml_vrtc_resume(struct platform_device *pdev) |
220 | { |
221 | set_wakeup_time(0); |
222 | |
223 | /* If timeE < 20, EE domain is thutdown, timerE is not |
224 | * work during suspend. we need get vrtc value. |
225 | */ |
226 | if (read_te() < 20) |
227 | if (!scpi_get_vrtc(&vrtc_init_date)) |
228 | pr_debug("get vrtc: %us\n", vrtc_init_date); |
229 | |
230 | return 0; |
231 | } |
232 | |
233 | static int aml_vrtc_suspend(struct platform_device *pdev, pm_message_t state) |
234 | { |
235 | u32 vrtc_val; |
236 | |
237 | vrtc_val = read_te(); |
238 | |
239 | if (scpi_set_vrtc(vrtc_val)) |
240 | pr_debug("vrtc setting fail.\n"); |
241 | |
242 | return 0; |
243 | } |
244 | |
245 | |
246 | static void aml_vrtc_shutdown(struct platform_device *pdev) |
247 | { |
248 | u32 vrtc_val; |
249 | struct timespec new_system; |
250 | |
251 | vrtc_val = read_te(); |
252 | getnstimeofday(&new_system); |
253 | |
254 | vrtc_val = (u32)new_system.tv_sec; |
255 | |
256 | if (scpi_set_vrtc(vrtc_val)) |
257 | pr_debug("vrtc setting fail.\n"); |
258 | } |
259 | |
260 | |
261 | static const struct of_device_id aml_vrtc_dt_match[] = { |
262 | { .compatible = "amlogic, aml_vrtc"}, |
263 | {}, |
264 | }; |
265 | |
266 | struct platform_driver aml_vrtc_driver = { |
267 | .driver = { |
268 | .name = "aml_vrtc", |
269 | .owner = THIS_MODULE, |
270 | .of_match_table = aml_vrtc_dt_match, |
271 | }, |
272 | .probe = aml_vrtc_probe, |
273 | .remove = aml_vrtc_remove, |
274 | .resume = aml_vrtc_resume, |
275 | .suspend = aml_vrtc_suspend, |
276 | .shutdown = aml_vrtc_shutdown, |
277 | }; |
278 | |
279 | static int __init aml_vrtc_init(void) |
280 | { |
281 | return platform_driver_register(&aml_vrtc_driver); |
282 | } |
283 | |
284 | static void __init aml_vrtc_exit(void) |
285 | { |
286 | return platform_driver_unregister(&aml_vrtc_driver); |
287 | } |
288 | |
289 | module_init(aml_vrtc_init); |
290 | module_exit(aml_vrtc_exit); |
291 | |
292 | MODULE_DESCRIPTION("Amlogic internal vrtc driver"); |
293 | MODULE_LICENSE("GPL"); |
294 |