Commit | Line | Data |
---|---|---|
90829c08 VB |
1 | /* |
2 | * rtc-rc5t583.c -- RICOH RC5T583 Real Time Clock | |
3 | * | |
4 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | |
5 | * Author: Venu Byravarasu <vbyravarasu@nvidia.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms and conditions of the GNU General Public License, | |
9 | * version 2, as published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope 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 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
18 | ||
19 | #include <linux/kernel.h> | |
20 | #include <linux/errno.h> | |
21 | #include <linux/init.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/types.h> | |
24 | #include <linux/rtc.h> | |
25 | #include <linux/bcd.h> | |
26 | #include <linux/platform_device.h> | |
27 | #include <linux/interrupt.h> | |
28 | #include <linux/mfd/rc5t583.h> | |
29 | ||
30 | struct rc5t583_rtc { | |
31 | struct rtc_device *rtc; | |
32 | /* To store the list of enabled interrupts, during system suspend */ | |
33 | u32 irqen; | |
34 | }; | |
35 | ||
36 | /* Total number of RTC registers needed to set time*/ | |
37 | #define NUM_TIME_REGS (RC5T583_RTC_YEAR - RC5T583_RTC_SEC + 1) | |
38 | ||
39 | /* Total number of RTC registers needed to set Y-Alarm*/ | |
40 | #define NUM_YAL_REGS (RC5T583_RTC_AY_YEAR - RC5T583_RTC_AY_MIN + 1) | |
41 | ||
42 | /* Set Y-Alarm interrupt */ | |
43 | #define SET_YAL BIT(5) | |
44 | ||
45 | /* Get Y-Alarm interrupt status*/ | |
46 | #define GET_YAL_STATUS BIT(3) | |
47 | ||
48 | static int rc5t583_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) | |
49 | { | |
50 | struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); | |
51 | u8 val; | |
52 | ||
53 | /* Set Y-Alarm, based on 'enabled' */ | |
54 | val = enabled ? SET_YAL : 0; | |
55 | ||
56 | return regmap_update_bits(rc5t583->regmap, RC5T583_RTC_CTL1, SET_YAL, | |
57 | val); | |
58 | } | |
59 | ||
60 | /* | |
61 | * Gets current rc5t583 RTC time and date parameters. | |
62 | * | |
63 | * The RTC's time/alarm representation is not what gmtime(3) requires | |
64 | * Linux to use: | |
65 | * | |
66 | * - Months are 1..12 vs Linux 0-11 | |
67 | * - Years are 0..99 vs Linux 1900..N (we assume 21st century) | |
68 | */ | |
69 | static int rc5t583_rtc_read_time(struct device *dev, struct rtc_time *tm) | |
70 | { | |
71 | struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); | |
72 | u8 rtc_data[NUM_TIME_REGS]; | |
73 | int ret; | |
74 | ||
75 | ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data, | |
76 | NUM_TIME_REGS); | |
77 | if (ret < 0) { | |
78 | dev_err(dev, "RTC read time failed with err:%d\n", ret); | |
79 | return ret; | |
80 | } | |
81 | ||
82 | tm->tm_sec = bcd2bin(rtc_data[0]); | |
83 | tm->tm_min = bcd2bin(rtc_data[1]); | |
84 | tm->tm_hour = bcd2bin(rtc_data[2]); | |
85 | tm->tm_wday = bcd2bin(rtc_data[3]); | |
86 | tm->tm_mday = bcd2bin(rtc_data[4]); | |
87 | tm->tm_mon = bcd2bin(rtc_data[5]) - 1; | |
88 | tm->tm_year = bcd2bin(rtc_data[6]) + 100; | |
89 | ||
90 | return ret; | |
91 | } | |
92 | ||
93 | static int rc5t583_rtc_set_time(struct device *dev, struct rtc_time *tm) | |
94 | { | |
95 | struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); | |
96 | unsigned char rtc_data[NUM_TIME_REGS]; | |
97 | int ret; | |
98 | ||
99 | rtc_data[0] = bin2bcd(tm->tm_sec); | |
100 | rtc_data[1] = bin2bcd(tm->tm_min); | |
101 | rtc_data[2] = bin2bcd(tm->tm_hour); | |
102 | rtc_data[3] = bin2bcd(tm->tm_wday); | |
103 | rtc_data[4] = bin2bcd(tm->tm_mday); | |
104 | rtc_data[5] = bin2bcd(tm->tm_mon + 1); | |
105 | rtc_data[6] = bin2bcd(tm->tm_year - 100); | |
106 | ||
107 | ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data, | |
108 | NUM_TIME_REGS); | |
109 | if (ret < 0) { | |
110 | dev_err(dev, "RTC set time failed with error %d\n", ret); | |
111 | return ret; | |
112 | } | |
113 | ||
114 | return ret; | |
115 | } | |
116 | ||
117 | static int rc5t583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) | |
118 | { | |
119 | struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); | |
120 | unsigned char alarm_data[NUM_YAL_REGS]; | |
121 | u32 interrupt_enable; | |
122 | int ret; | |
123 | ||
124 | ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data, | |
125 | NUM_YAL_REGS); | |
126 | if (ret < 0) { | |
127 | dev_err(dev, "rtc_read_alarm error %d\n", ret); | |
128 | return ret; | |
129 | } | |
130 | ||
131 | alm->time.tm_min = bcd2bin(alarm_data[0]); | |
132 | alm->time.tm_hour = bcd2bin(alarm_data[1]); | |
133 | alm->time.tm_mday = bcd2bin(alarm_data[2]); | |
134 | alm->time.tm_mon = bcd2bin(alarm_data[3]) - 1; | |
135 | alm->time.tm_year = bcd2bin(alarm_data[4]) + 100; | |
136 | ||
137 | ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, &interrupt_enable); | |
138 | if (ret < 0) | |
139 | return ret; | |
140 | ||
141 | /* check if YALE is set */ | |
142 | if (interrupt_enable & SET_YAL) | |
143 | alm->enabled = 1; | |
144 | ||
145 | return ret; | |
146 | } | |
147 | ||
148 | static int rc5t583_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) | |
149 | { | |
150 | struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); | |
151 | unsigned char alarm_data[NUM_YAL_REGS]; | |
152 | int ret; | |
153 | ||
154 | ret = rc5t583_rtc_alarm_irq_enable(dev, 0); | |
155 | if (ret) | |
156 | return ret; | |
157 | ||
158 | alarm_data[0] = bin2bcd(alm->time.tm_min); | |
159 | alarm_data[1] = bin2bcd(alm->time.tm_hour); | |
160 | alarm_data[2] = bin2bcd(alm->time.tm_mday); | |
161 | alarm_data[3] = bin2bcd(alm->time.tm_mon + 1); | |
162 | alarm_data[4] = bin2bcd(alm->time.tm_year - 100); | |
163 | ||
164 | ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data, | |
165 | NUM_YAL_REGS); | |
166 | if (ret) { | |
167 | dev_err(dev, "rtc_set_alarm error %d\n", ret); | |
168 | return ret; | |
169 | } | |
170 | ||
171 | if (alm->enabled) | |
172 | ret = rc5t583_rtc_alarm_irq_enable(dev, 1); | |
173 | ||
174 | return ret; | |
175 | } | |
176 | ||
177 | static irqreturn_t rc5t583_rtc_interrupt(int irq, void *rtc) | |
178 | { | |
179 | struct device *dev = rtc; | |
180 | struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); | |
181 | struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev); | |
182 | unsigned long events = 0; | |
183 | int ret; | |
184 | u32 rtc_reg; | |
185 | ||
186 | ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL2, &rtc_reg); | |
187 | if (ret < 0) | |
188 | return IRQ_NONE; | |
189 | ||
190 | if (rtc_reg & GET_YAL_STATUS) { | |
191 | events = RTC_IRQF | RTC_AF; | |
192 | /* clear pending Y-alarm interrupt bit */ | |
193 | rtc_reg &= ~GET_YAL_STATUS; | |
194 | } | |
195 | ||
196 | ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, rtc_reg); | |
197 | if (ret) | |
198 | return IRQ_NONE; | |
199 | ||
200 | /* Notify RTC core on event */ | |
201 | rtc_update_irq(rc5t583_rtc->rtc, 1, events); | |
202 | ||
203 | return IRQ_HANDLED; | |
204 | } | |
205 | ||
206 | static const struct rtc_class_ops rc5t583_rtc_ops = { | |
207 | .read_time = rc5t583_rtc_read_time, | |
208 | .set_time = rc5t583_rtc_set_time, | |
209 | .read_alarm = rc5t583_rtc_read_alarm, | |
210 | .set_alarm = rc5t583_rtc_set_alarm, | |
211 | .alarm_irq_enable = rc5t583_rtc_alarm_irq_enable, | |
212 | }; | |
213 | ||
5a167f45 | 214 | static int rc5t583_rtc_probe(struct platform_device *pdev) |
90829c08 VB |
215 | { |
216 | struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); | |
217 | struct rc5t583_rtc *ricoh_rtc; | |
218 | struct rc5t583_platform_data *pmic_plat_data; | |
219 | int ret; | |
220 | int irq; | |
221 | ||
222 | ricoh_rtc = devm_kzalloc(&pdev->dev, sizeof(struct rc5t583_rtc), | |
223 | GFP_KERNEL); | |
224 | if (!ricoh_rtc) | |
225 | return -ENOMEM; | |
226 | ||
227 | platform_set_drvdata(pdev, ricoh_rtc); | |
228 | ||
229 | /* Clear pending interrupts */ | |
230 | ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, 0); | |
231 | if (ret < 0) | |
232 | return ret; | |
233 | ||
234 | /* clear RTC Adjust register */ | |
235 | ret = regmap_write(rc5t583->regmap, RC5T583_RTC_ADJ, 0); | |
236 | if (ret < 0) { | |
237 | dev_err(&pdev->dev, "unable to program rtc_adjust reg\n"); | |
238 | return -EBUSY; | |
239 | } | |
240 | ||
241 | pmic_plat_data = dev_get_platdata(rc5t583->dev); | |
242 | irq = pmic_plat_data->irq_base; | |
243 | if (irq <= 0) { | |
244 | dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n", | |
245 | irq); | |
246 | return ret; | |
247 | } | |
248 | ||
249 | irq += RC5T583_IRQ_YALE; | |
250 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, | |
251 | rc5t583_rtc_interrupt, IRQF_TRIGGER_LOW, | |
252 | "rtc-rc5t583", &pdev->dev); | |
253 | if (ret < 0) { | |
254 | dev_err(&pdev->dev, "IRQ is not free.\n"); | |
255 | return ret; | |
256 | } | |
257 | device_init_wakeup(&pdev->dev, 1); | |
258 | ||
8b7980db | 259 | ricoh_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, |
90829c08 VB |
260 | &rc5t583_rtc_ops, THIS_MODULE); |
261 | if (IS_ERR(ricoh_rtc->rtc)) { | |
262 | ret = PTR_ERR(ricoh_rtc->rtc); | |
263 | dev_err(&pdev->dev, "RTC device register: err %d\n", ret); | |
264 | return ret; | |
265 | } | |
266 | ||
267 | return 0; | |
268 | } | |
269 | ||
270 | /* | |
271 | * Disable rc5t583 RTC interrupts. | |
272 | * Sets status flag to free. | |
273 | */ | |
5a167f45 | 274 | static int rc5t583_rtc_remove(struct platform_device *pdev) |
90829c08 | 275 | { |
e1c2f989 | 276 | struct rc5t583_rtc *rc5t583_rtc = platform_get_drvdata(pdev); |
90829c08 VB |
277 | |
278 | rc5t583_rtc_alarm_irq_enable(&rc5t583_rtc->rtc->dev, 0); | |
90829c08 VB |
279 | return 0; |
280 | } | |
281 | ||
282 | #ifdef CONFIG_PM_SLEEP | |
90829c08 VB |
283 | static int rc5t583_rtc_suspend(struct device *dev) |
284 | { | |
285 | struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); | |
286 | struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev); | |
287 | int ret; | |
288 | ||
289 | /* Store current list of enabled interrupts*/ | |
290 | ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, | |
291 | &rc5t583_rtc->irqen); | |
292 | return ret; | |
293 | } | |
294 | ||
295 | static int rc5t583_rtc_resume(struct device *dev) | |
296 | { | |
297 | struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); | |
298 | struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev); | |
299 | ||
300 | /* Restore list of enabled interrupts before suspend */ | |
301 | return regmap_write(rc5t583->regmap, RC5T583_RTC_CTL1, | |
302 | rc5t583_rtc->irqen); | |
303 | } | |
90829c08 VB |
304 | #endif |
305 | ||
29756a52 JH |
306 | static SIMPLE_DEV_PM_OPS(rc5t583_rtc_pm_ops, rc5t583_rtc_suspend, |
307 | rc5t583_rtc_resume); | |
308 | ||
90829c08 VB |
309 | static struct platform_driver rc5t583_rtc_driver = { |
310 | .probe = rc5t583_rtc_probe, | |
5a167f45 | 311 | .remove = rc5t583_rtc_remove, |
90829c08 | 312 | .driver = { |
90829c08 | 313 | .name = "rtc-rc5t583", |
29756a52 | 314 | .pm = &rc5t583_rtc_pm_ops, |
90829c08 VB |
315 | }, |
316 | }; | |
317 | ||
318 | module_platform_driver(rc5t583_rtc_driver); | |
319 | MODULE_ALIAS("platform:rtc-rc5t583"); | |
320 | MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>"); | |
321 | MODULE_LICENSE("GPL v2"); |