diff options
author | Ashish Jangam <ashish.jangam@kpitcummins.com> | 2012-03-23 18:02:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-23 19:58:40 -0400 |
commit | fef931ff98fe78bea804d9b4c49d410a7a97988c (patch) | |
tree | c205ae155861d391e9268b98ece55fcc54ee8799 /drivers/rtc/rtc-da9052.c | |
parent | fad0738dcf6feccf601f5a24b8ccd3b26894b337 (diff) |
rtc: driver for DA9052/53 PMIC v1
RTC Driver for Dialog Semiconductor DA9052/53 PMICs.
This patch is functionally tested on Samsung SMDKV6410.
[akpm@linux-foundation.org: clean up file header layout, remove unneeded initialisation of local arrays]
Signed-off-by: David Dajun Chen <dchen@diasemi.com>
Signed-off-by: Ashish Jangam <ashish.jangam@kpitcummins.com>
Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
Cc: David Dajun Chen <dchen@diasemi.com>
Cc: Samuel Ortiz <sameo@linux.intel.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc/rtc-da9052.c')
-rw-r--r-- | drivers/rtc/rtc-da9052.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c new file mode 100644 index 000000000000..da6ab5291a41 --- /dev/null +++ b/drivers/rtc/rtc-da9052.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /* | ||
2 | * Real time clock driver for DA9052 | ||
3 | * | ||
4 | * Copyright(c) 2012 Dialog Semiconductor Ltd. | ||
5 | * | ||
6 | * Author: Dajun Dajun Chen <dajun.chen@diasemi.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/rtc.h> | ||
18 | |||
19 | #include <linux/mfd/da9052/da9052.h> | ||
20 | #include <linux/mfd/da9052/reg.h> | ||
21 | |||
22 | #define rtc_err(da9052, fmt, ...) \ | ||
23 | dev_err(da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__) | ||
24 | |||
25 | struct da9052_rtc { | ||
26 | struct rtc_device *rtc; | ||
27 | struct da9052 *da9052; | ||
28 | int irq; | ||
29 | }; | ||
30 | |||
31 | static int da9052_rtc_enable_alarm(struct da9052 *da9052, bool enable) | ||
32 | { | ||
33 | int ret; | ||
34 | if (enable) { | ||
35 | ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, | ||
36 | DA9052_ALARM_Y_ALARM_ON, | ||
37 | DA9052_ALARM_Y_ALARM_ON); | ||
38 | if (ret != 0) | ||
39 | rtc_err(da9052, "Failed to enable ALM: %d\n", ret); | ||
40 | } else { | ||
41 | ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, | ||
42 | DA9052_ALARM_Y_ALARM_ON, 0); | ||
43 | if (ret != 0) | ||
44 | rtc_err(da9052, "Write error: %d\n", ret); | ||
45 | } | ||
46 | return ret; | ||
47 | } | ||
48 | |||
49 | static irqreturn_t da9052_rtc_irq(int irq, void *data) | ||
50 | { | ||
51 | struct da9052_rtc *rtc = data; | ||
52 | int ret; | ||
53 | |||
54 | ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_MI_REG); | ||
55 | if (ret < 0) { | ||
56 | rtc_err(rtc->da9052, "Read error: %d\n", ret); | ||
57 | return IRQ_NONE; | ||
58 | } | ||
59 | |||
60 | if (ret & DA9052_ALARMMI_ALARMTYPE) { | ||
61 | da9052_rtc_enable_alarm(rtc->da9052, 0); | ||
62 | rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); | ||
63 | } else | ||
64 | rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_PF); | ||
65 | |||
66 | return IRQ_HANDLED; | ||
67 | } | ||
68 | |||
69 | static int da9052_read_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) | ||
70 | { | ||
71 | int ret; | ||
72 | uint8_t v[5]; | ||
73 | |||
74 | ret = da9052_group_read(da9052, DA9052_ALARM_MI_REG, 5, v); | ||
75 | if (ret != 0) { | ||
76 | rtc_err(da9052, "Failed to group read ALM: %d\n", ret); | ||
77 | return ret; | ||
78 | } | ||
79 | |||
80 | rtc_tm->tm_year = (v[4] & DA9052_RTC_YEAR) + 100; | ||
81 | rtc_tm->tm_mon = (v[3] & DA9052_RTC_MONTH) - 1; | ||
82 | rtc_tm->tm_mday = v[2] & DA9052_RTC_DAY; | ||
83 | rtc_tm->tm_hour = v[1] & DA9052_RTC_HOUR; | ||
84 | rtc_tm->tm_min = v[0] & DA9052_RTC_MIN; | ||
85 | |||
86 | ret = rtc_valid_tm(rtc_tm); | ||
87 | if (ret != 0) | ||
88 | return ret; | ||
89 | return ret; | ||
90 | } | ||
91 | |||
92 | static int da9052_set_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) | ||
93 | { | ||
94 | int ret; | ||
95 | uint8_t v[3]; | ||
96 | |||
97 | rtc_tm->tm_year -= 100; | ||
98 | rtc_tm->tm_mon += 1; | ||
99 | |||
100 | ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG, | ||
101 | DA9052_RTC_MIN, rtc_tm->tm_min); | ||
102 | if (ret != 0) { | ||
103 | rtc_err(da9052, "Failed to write ALRM MIN: %d\n", ret); | ||
104 | return ret; | ||
105 | } | ||
106 | |||
107 | v[0] = rtc_tm->tm_hour; | ||
108 | v[1] = rtc_tm->tm_mday; | ||
109 | v[2] = rtc_tm->tm_mon; | ||
110 | |||
111 | ret = da9052_group_write(da9052, DA9052_ALARM_H_REG, 3, v); | ||
112 | if (ret < 0) | ||
113 | return ret; | ||
114 | |||
115 | ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, | ||
116 | DA9052_RTC_YEAR, rtc_tm->tm_year); | ||
117 | if (ret != 0) | ||
118 | rtc_err(da9052, "Failed to write ALRM YEAR: %d\n", ret); | ||
119 | |||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | static int da9052_rtc_get_alarm_status(struct da9052 *da9052) | ||
124 | { | ||
125 | int ret; | ||
126 | |||
127 | ret = da9052_reg_read(da9052, DA9052_ALARM_Y_REG); | ||
128 | if (ret < 0) { | ||
129 | rtc_err(da9052, "Failed to read ALM: %d\n", ret); | ||
130 | return ret; | ||
131 | } | ||
132 | ret &= DA9052_ALARM_Y_ALARM_ON; | ||
133 | return (ret > 0) ? 1 : 0; | ||
134 | } | ||
135 | |||
136 | static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) | ||
137 | { | ||
138 | struct da9052_rtc *rtc = dev_get_drvdata(dev); | ||
139 | uint8_t v[6]; | ||
140 | int ret; | ||
141 | |||
142 | ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, v); | ||
143 | if (ret < 0) { | ||
144 | rtc_err(rtc->da9052, "Failed to read RTC time : %d\n", ret); | ||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | rtc_tm->tm_year = (v[5] & DA9052_RTC_YEAR) + 100; | ||
149 | rtc_tm->tm_mon = (v[4] & DA9052_RTC_MONTH) - 1; | ||
150 | rtc_tm->tm_mday = v[3] & DA9052_RTC_DAY; | ||
151 | rtc_tm->tm_hour = v[2] & DA9052_RTC_HOUR; | ||
152 | rtc_tm->tm_min = v[1] & DA9052_RTC_MIN; | ||
153 | rtc_tm->tm_sec = v[0] & DA9052_RTC_SEC; | ||
154 | |||
155 | ret = rtc_valid_tm(rtc_tm); | ||
156 | if (ret != 0) { | ||
157 | rtc_err(rtc->da9052, "rtc_valid_tm failed: %d\n", ret); | ||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
165 | { | ||
166 | struct da9052_rtc *rtc; | ||
167 | uint8_t v[6]; | ||
168 | |||
169 | rtc = dev_get_drvdata(dev); | ||
170 | |||
171 | v[0] = tm->tm_sec; | ||
172 | v[1] = tm->tm_min; | ||
173 | v[2] = tm->tm_hour; | ||
174 | v[3] = tm->tm_mday; | ||
175 | v[4] = tm->tm_mon + 1; | ||
176 | v[5] = tm->tm_year - 100; | ||
177 | |||
178 | return da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v); | ||
179 | } | ||
180 | |||
181 | static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
182 | { | ||
183 | int ret; | ||
184 | struct rtc_time *tm = &alrm->time; | ||
185 | struct da9052_rtc *rtc = dev_get_drvdata(dev); | ||
186 | |||
187 | ret = da9052_read_alarm(rtc->da9052, tm); | ||
188 | |||
189 | if (ret) | ||
190 | return ret; | ||
191 | |||
192 | alrm->enabled = da9052_rtc_get_alarm_status(rtc->da9052); | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int da9052_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
198 | { | ||
199 | int ret; | ||
200 | struct rtc_time *tm = &alrm->time; | ||
201 | struct da9052_rtc *rtc = dev_get_drvdata(dev); | ||
202 | |||
203 | ret = da9052_rtc_enable_alarm(rtc->da9052, 0); | ||
204 | if (ret < 0) | ||
205 | return ret; | ||
206 | |||
207 | ret = da9052_set_alarm(rtc->da9052, tm); | ||
208 | if (ret) | ||
209 | return ret; | ||
210 | |||
211 | ret = da9052_rtc_enable_alarm(rtc->da9052, 1); | ||
212 | |||
213 | return ret; | ||
214 | } | ||
215 | |||
216 | static int da9052_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
217 | { | ||
218 | struct da9052_rtc *rtc = dev_get_drvdata(dev); | ||
219 | |||
220 | return da9052_rtc_enable_alarm(rtc->da9052, enabled); | ||
221 | } | ||
222 | |||
223 | static const struct rtc_class_ops da9052_rtc_ops = { | ||
224 | .read_time = da9052_rtc_read_time, | ||
225 | .set_time = da9052_rtc_set_time, | ||
226 | .read_alarm = da9052_rtc_read_alarm, | ||
227 | .set_alarm = da9052_rtc_set_alarm, | ||
228 | .alarm_irq_enable = da9052_rtc_alarm_irq_enable, | ||
229 | }; | ||
230 | |||
231 | static int __devinit da9052_rtc_probe(struct platform_device *pdev) | ||
232 | { | ||
233 | struct da9052_rtc *rtc; | ||
234 | int ret; | ||
235 | |||
236 | rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9052_rtc), GFP_KERNEL); | ||
237 | if (!rtc) | ||
238 | return -ENOMEM; | ||
239 | |||
240 | rtc->da9052 = dev_get_drvdata(pdev->dev.parent); | ||
241 | platform_set_drvdata(pdev, rtc); | ||
242 | rtc->irq = platform_get_irq_byname(pdev, "ALM"); | ||
243 | ret = request_threaded_irq(rtc->irq, NULL, da9052_rtc_irq, | ||
244 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | ||
245 | "ALM", rtc); | ||
246 | if (ret != 0) { | ||
247 | rtc_err(rtc->da9052, "irq registration failed: %d\n", ret); | ||
248 | goto err_mem; | ||
249 | } | ||
250 | |||
251 | rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, | ||
252 | &da9052_rtc_ops, THIS_MODULE); | ||
253 | if (IS_ERR(rtc->rtc)) { | ||
254 | ret = PTR_ERR(rtc->rtc); | ||
255 | goto err_free_irq; | ||
256 | } | ||
257 | |||
258 | return 0; | ||
259 | |||
260 | err_free_irq: | ||
261 | free_irq(rtc->irq, rtc); | ||
262 | err_mem: | ||
263 | devm_kfree(&pdev->dev, rtc); | ||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | static int __devexit da9052_rtc_remove(struct platform_device *pdev) | ||
268 | { | ||
269 | struct da9052_rtc *rtc = pdev->dev.platform_data; | ||
270 | |||
271 | rtc_device_unregister(rtc->rtc); | ||
272 | free_irq(rtc->irq, rtc); | ||
273 | platform_set_drvdata(pdev, NULL); | ||
274 | devm_kfree(&pdev->dev, rtc); | ||
275 | |||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static struct platform_driver da9052_rtc_driver = { | ||
280 | .probe = da9052_rtc_probe, | ||
281 | .remove = __devexit_p(da9052_rtc_remove), | ||
282 | .driver = { | ||
283 | .name = "da9052-rtc", | ||
284 | .owner = THIS_MODULE, | ||
285 | }, | ||
286 | }; | ||
287 | |||
288 | module_platform_driver(da9052_rtc_driver); | ||
289 | |||
290 | MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); | ||
291 | MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC"); | ||
292 | MODULE_LICENSE("GPL"); | ||
293 | MODULE_ALIAS("platform:da9052-rtc"); | ||