summaryrefslogtreecommitdiff
path: root/drivers/amlogic/vrtc/aml_vrtc.c (plain)
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
29static void __iomem *alarm_reg_vaddr;
30static void __iomem *timere_low_vaddr, *timere_high_vaddr;
31static unsigned int vrtc_init_date;
32
33#define TIME_LEN 10
34static 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
67static 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
82static 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
93static 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
102static 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
117static 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
129static 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
156static 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
162static 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
210static 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
219static 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
233static 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
246static 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
261static const struct of_device_id aml_vrtc_dt_match[] = {
262 { .compatible = "amlogic, aml_vrtc"},
263 {},
264};
265
266struct 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
279static int __init aml_vrtc_init(void)
280{
281 return platform_driver_register(&aml_vrtc_driver);
282}
283
284static void __init aml_vrtc_exit(void)
285{
286 return platform_driver_unregister(&aml_vrtc_driver);
287}
288
289module_init(aml_vrtc_init);
290module_exit(aml_vrtc_exit);
291
292MODULE_DESCRIPTION("Amlogic internal vrtc driver");
293MODULE_LICENSE("GPL");
294