1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * rtc-palmas.c -- Palmas Real Time Clock driver. |
4 | |
5 | * RTC driver for TI Palma series devices like TPS65913, |
6 | * TPS65914 power management IC. |
7 | * |
8 | * Copyright (c) 2012, NVIDIA Corporation. |
9 | * |
10 | * Author: Laxman Dewangan <ldewangan@nvidia.com> |
11 | */ |
12 | |
13 | #include <linux/bcd.h> |
14 | #include <linux/errno.h> |
15 | #include <linux/init.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/mfd/palmas.h> |
19 | #include <linux/module.h> |
20 | #include <linux/of.h> |
21 | #include <linux/rtc.h> |
22 | #include <linux/types.h> |
23 | #include <linux/platform_device.h> |
24 | #include <linux/pm.h> |
25 | |
26 | struct palmas_rtc { |
27 | struct rtc_device *rtc; |
28 | struct device *dev; |
29 | unsigned int irq; |
30 | }; |
31 | |
32 | /* Total number of RTC registers needed to set time*/ |
33 | #define PALMAS_NUM_TIME_REGS (PALMAS_YEARS_REG - PALMAS_SECONDS_REG + 1) |
34 | |
35 | static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm) |
36 | { |
37 | unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; |
38 | struct palmas *palmas = dev_get_drvdata(dev: dev->parent); |
39 | int ret; |
40 | |
41 | /* Copy RTC counting registers to static registers or latches */ |
42 | ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, |
43 | PALMAS_RTC_CTRL_REG_GET_TIME, PALMAS_RTC_CTRL_REG_GET_TIME); |
44 | if (ret < 0) { |
45 | dev_err(dev, "RTC CTRL reg update failed, err: %d\n" , ret); |
46 | return ret; |
47 | } |
48 | |
49 | ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, |
50 | val: rtc_data, PALMAS_NUM_TIME_REGS); |
51 | if (ret < 0) { |
52 | dev_err(dev, "RTC_SECONDS reg read failed, err = %d\n" , ret); |
53 | return ret; |
54 | } |
55 | |
56 | tm->tm_sec = bcd2bin(rtc_data[0]); |
57 | tm->tm_min = bcd2bin(rtc_data[1]); |
58 | tm->tm_hour = bcd2bin(rtc_data[2]); |
59 | tm->tm_mday = bcd2bin(rtc_data[3]); |
60 | tm->tm_mon = bcd2bin(rtc_data[4]) - 1; |
61 | tm->tm_year = bcd2bin(rtc_data[5]) + 100; |
62 | |
63 | return ret; |
64 | } |
65 | |
66 | static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm) |
67 | { |
68 | unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; |
69 | struct palmas *palmas = dev_get_drvdata(dev: dev->parent); |
70 | int ret; |
71 | |
72 | rtc_data[0] = bin2bcd(tm->tm_sec); |
73 | rtc_data[1] = bin2bcd(tm->tm_min); |
74 | rtc_data[2] = bin2bcd(tm->tm_hour); |
75 | rtc_data[3] = bin2bcd(tm->tm_mday); |
76 | rtc_data[4] = bin2bcd(tm->tm_mon + 1); |
77 | rtc_data[5] = bin2bcd(tm->tm_year - 100); |
78 | |
79 | /* Stop RTC while updating the RTC time registers */ |
80 | ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, |
81 | PALMAS_RTC_CTRL_REG_STOP_RTC, val: 0); |
82 | if (ret < 0) { |
83 | dev_err(dev, "RTC stop failed, err = %d\n" , ret); |
84 | return ret; |
85 | } |
86 | |
87 | ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, |
88 | val: rtc_data, PALMAS_NUM_TIME_REGS); |
89 | if (ret < 0) { |
90 | dev_err(dev, "RTC_SECONDS reg write failed, err = %d\n" , ret); |
91 | return ret; |
92 | } |
93 | |
94 | /* Start back RTC */ |
95 | ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, |
96 | PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC); |
97 | if (ret < 0) |
98 | dev_err(dev, "RTC start failed, err = %d\n" , ret); |
99 | return ret; |
100 | } |
101 | |
102 | static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) |
103 | { |
104 | struct palmas *palmas = dev_get_drvdata(dev: dev->parent); |
105 | u8 val; |
106 | |
107 | val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0; |
108 | return palmas_write(palmas, PALMAS_RTC_BASE, |
109 | PALMAS_RTC_INTERRUPTS_REG, value: val); |
110 | } |
111 | |
112 | static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) |
113 | { |
114 | unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; |
115 | u32 int_val; |
116 | struct palmas *palmas = dev_get_drvdata(dev: dev->parent); |
117 | int ret; |
118 | |
119 | ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, |
120 | PALMAS_ALARM_SECONDS_REG, |
121 | val: alarm_data, PALMAS_NUM_TIME_REGS); |
122 | if (ret < 0) { |
123 | dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n" , ret); |
124 | return ret; |
125 | } |
126 | |
127 | alm->time.tm_sec = bcd2bin(alarm_data[0]); |
128 | alm->time.tm_min = bcd2bin(alarm_data[1]); |
129 | alm->time.tm_hour = bcd2bin(alarm_data[2]); |
130 | alm->time.tm_mday = bcd2bin(alarm_data[3]); |
131 | alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; |
132 | alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; |
133 | |
134 | ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, |
135 | val: &int_val); |
136 | if (ret < 0) { |
137 | dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n" , ret); |
138 | return ret; |
139 | } |
140 | |
141 | if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM) |
142 | alm->enabled = 1; |
143 | return ret; |
144 | } |
145 | |
146 | static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) |
147 | { |
148 | unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; |
149 | struct palmas *palmas = dev_get_drvdata(dev: dev->parent); |
150 | int ret; |
151 | |
152 | ret = palmas_rtc_alarm_irq_enable(dev, enabled: 0); |
153 | if (ret < 0) { |
154 | dev_err(dev, "Disable RTC alarm failed\n" ); |
155 | return ret; |
156 | } |
157 | |
158 | alarm_data[0] = bin2bcd(alm->time.tm_sec); |
159 | alarm_data[1] = bin2bcd(alm->time.tm_min); |
160 | alarm_data[2] = bin2bcd(alm->time.tm_hour); |
161 | alarm_data[3] = bin2bcd(alm->time.tm_mday); |
162 | alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); |
163 | alarm_data[5] = bin2bcd(alm->time.tm_year - 100); |
164 | |
165 | ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, |
166 | PALMAS_ALARM_SECONDS_REG, val: alarm_data, PALMAS_NUM_TIME_REGS); |
167 | if (ret < 0) { |
168 | dev_err(dev, "ALARM_SECONDS_REG write failed, err = %d\n" , ret); |
169 | return ret; |
170 | } |
171 | |
172 | if (alm->enabled) |
173 | ret = palmas_rtc_alarm_irq_enable(dev, enabled: 1); |
174 | return ret; |
175 | } |
176 | |
177 | static int palmas_clear_interrupts(struct device *dev) |
178 | { |
179 | struct palmas *palmas = dev_get_drvdata(dev: dev->parent); |
180 | unsigned int rtc_reg; |
181 | int ret; |
182 | |
183 | ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, |
184 | val: &rtc_reg); |
185 | if (ret < 0) { |
186 | dev_err(dev, "RTC_STATUS read failed, err = %d\n" , ret); |
187 | return ret; |
188 | } |
189 | |
190 | ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, |
191 | value: rtc_reg); |
192 | if (ret < 0) { |
193 | dev_err(dev, "RTC_STATUS write failed, err = %d\n" , ret); |
194 | return ret; |
195 | } |
196 | return 0; |
197 | } |
198 | |
199 | static irqreturn_t palmas_rtc_interrupt(int irq, void *context) |
200 | { |
201 | struct palmas_rtc *palmas_rtc = context; |
202 | struct device *dev = palmas_rtc->dev; |
203 | int ret; |
204 | |
205 | ret = palmas_clear_interrupts(dev); |
206 | if (ret < 0) { |
207 | dev_err(dev, "RTC interrupt clear failed, err = %d\n" , ret); |
208 | return IRQ_NONE; |
209 | } |
210 | |
211 | rtc_update_irq(rtc: palmas_rtc->rtc, num: 1, RTC_IRQF | RTC_AF); |
212 | return IRQ_HANDLED; |
213 | } |
214 | |
215 | static const struct rtc_class_ops palmas_rtc_ops = { |
216 | .read_time = palmas_rtc_read_time, |
217 | .set_time = palmas_rtc_set_time, |
218 | .read_alarm = palmas_rtc_read_alarm, |
219 | .set_alarm = palmas_rtc_set_alarm, |
220 | .alarm_irq_enable = palmas_rtc_alarm_irq_enable, |
221 | }; |
222 | |
223 | static int palmas_rtc_probe(struct platform_device *pdev) |
224 | { |
225 | struct palmas *palmas = dev_get_drvdata(dev: pdev->dev.parent); |
226 | struct palmas_rtc *palmas_rtc = NULL; |
227 | int ret; |
228 | bool enable_bb_charging = false; |
229 | bool high_bb_charging = false; |
230 | |
231 | if (pdev->dev.of_node) { |
232 | enable_bb_charging = of_property_read_bool(np: pdev->dev.of_node, |
233 | propname: "ti,backup-battery-chargeable" ); |
234 | high_bb_charging = of_property_read_bool(np: pdev->dev.of_node, |
235 | propname: "ti,backup-battery-charge-high-current" ); |
236 | } |
237 | |
238 | palmas_rtc = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct palmas_rtc), |
239 | GFP_KERNEL); |
240 | if (!palmas_rtc) |
241 | return -ENOMEM; |
242 | |
243 | /* Clear pending interrupts */ |
244 | ret = palmas_clear_interrupts(dev: &pdev->dev); |
245 | if (ret < 0) { |
246 | dev_err(&pdev->dev, "clear RTC int failed, err = %d\n" , ret); |
247 | return ret; |
248 | } |
249 | |
250 | palmas_rtc->dev = &pdev->dev; |
251 | platform_set_drvdata(pdev, data: palmas_rtc); |
252 | |
253 | if (enable_bb_charging) { |
254 | unsigned reg = PALMAS_BACKUP_BATTERY_CTRL_BBS_BBC_LOW_ICHRG; |
255 | |
256 | if (high_bb_charging) |
257 | reg = 0; |
258 | |
259 | ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, |
260 | PALMAS_BACKUP_BATTERY_CTRL, |
261 | PALMAS_BACKUP_BATTERY_CTRL_BBS_BBC_LOW_ICHRG, val: reg); |
262 | if (ret < 0) { |
263 | dev_err(&pdev->dev, |
264 | "BACKUP_BATTERY_CTRL update failed, %d\n" , ret); |
265 | return ret; |
266 | } |
267 | |
268 | ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, |
269 | PALMAS_BACKUP_BATTERY_CTRL, |
270 | PALMAS_BACKUP_BATTERY_CTRL_BB_CHG_EN, |
271 | PALMAS_BACKUP_BATTERY_CTRL_BB_CHG_EN); |
272 | if (ret < 0) { |
273 | dev_err(&pdev->dev, |
274 | "BACKUP_BATTERY_CTRL update failed, %d\n" , ret); |
275 | return ret; |
276 | } |
277 | } |
278 | |
279 | /* Start RTC */ |
280 | ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, |
281 | PALMAS_RTC_CTRL_REG_STOP_RTC, |
282 | PALMAS_RTC_CTRL_REG_STOP_RTC); |
283 | if (ret < 0) { |
284 | dev_err(&pdev->dev, "RTC_CTRL write failed, err = %d\n" , ret); |
285 | return ret; |
286 | } |
287 | |
288 | palmas_rtc->irq = platform_get_irq(pdev, 0); |
289 | |
290 | device_init_wakeup(dev: &pdev->dev, enable: 1); |
291 | palmas_rtc->rtc = devm_rtc_device_register(dev: &pdev->dev, name: pdev->name, |
292 | ops: &palmas_rtc_ops, THIS_MODULE); |
293 | if (IS_ERR(ptr: palmas_rtc->rtc)) { |
294 | ret = PTR_ERR(ptr: palmas_rtc->rtc); |
295 | dev_err(&pdev->dev, "RTC register failed, err = %d\n" , ret); |
296 | return ret; |
297 | } |
298 | |
299 | ret = devm_request_threaded_irq(dev: &pdev->dev, irq: palmas_rtc->irq, NULL, |
300 | thread_fn: palmas_rtc_interrupt, |
301 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
302 | devname: dev_name(dev: &pdev->dev), dev_id: palmas_rtc); |
303 | if (ret < 0) { |
304 | dev_err(&pdev->dev, "IRQ request failed, err = %d\n" , ret); |
305 | return ret; |
306 | } |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | static void palmas_rtc_remove(struct platform_device *pdev) |
312 | { |
313 | palmas_rtc_alarm_irq_enable(dev: &pdev->dev, enabled: 0); |
314 | } |
315 | |
316 | #ifdef CONFIG_PM_SLEEP |
317 | static int palmas_rtc_suspend(struct device *dev) |
318 | { |
319 | struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); |
320 | |
321 | if (device_may_wakeup(dev)) |
322 | enable_irq_wake(irq: palmas_rtc->irq); |
323 | return 0; |
324 | } |
325 | |
326 | static int palmas_rtc_resume(struct device *dev) |
327 | { |
328 | struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); |
329 | |
330 | if (device_may_wakeup(dev)) |
331 | disable_irq_wake(irq: palmas_rtc->irq); |
332 | return 0; |
333 | } |
334 | #endif |
335 | |
336 | static SIMPLE_DEV_PM_OPS(palmas_rtc_pm_ops, palmas_rtc_suspend, |
337 | palmas_rtc_resume); |
338 | |
339 | #ifdef CONFIG_OF |
340 | static const struct of_device_id of_palmas_rtc_match[] = { |
341 | { .compatible = "ti,palmas-rtc" }, |
342 | { }, |
343 | }; |
344 | MODULE_DEVICE_TABLE(of, of_palmas_rtc_match); |
345 | #endif |
346 | |
347 | static struct platform_driver palmas_rtc_driver = { |
348 | .probe = palmas_rtc_probe, |
349 | .remove_new = palmas_rtc_remove, |
350 | .driver = { |
351 | .name = "palmas-rtc" , |
352 | .pm = &palmas_rtc_pm_ops, |
353 | .of_match_table = of_match_ptr(of_palmas_rtc_match), |
354 | }, |
355 | }; |
356 | |
357 | module_platform_driver(palmas_rtc_driver); |
358 | |
359 | MODULE_ALIAS("platform:palmas_rtc" ); |
360 | MODULE_DESCRIPTION("TI PALMAS series RTC driver" ); |
361 | MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>" ); |
362 | MODULE_LICENSE("GPL v2" ); |
363 | |