Blame view
drivers/rtc/rtc-twl.c
17 KB
f96411ab7 mfd: rtc-twl4030 ... |
1 |
/* |
ef3b7d0d3 mfd: Rename twl40... |
2 |
* rtc-twl.c -- TWL Real Time Clock interface |
f96411ab7 mfd: rtc-twl4030 ... |
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
* * Copyright (C) 2007 MontaVista Software, Inc * Author: Alexandre Rusev <source@mvista.com> * * Based on original TI driver twl4030-rtc.c * Copyright (C) 2006 Texas Instruments, Inc. * * Based on rtc-omap.c * Copyright (C) 2003 MontaVista Software, Inc. * Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com> * Copyright (C) 2006 David Brownell * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ |
a737e835e rtc: use more sta... |
20 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
f96411ab7 mfd: rtc-twl4030 ... |
21 |
#include <linux/kernel.h> |
2fac6674d rtc: bunch of dri... |
22 |
#include <linux/errno.h> |
f96411ab7 mfd: rtc-twl4030 ... |
23 24 25 26 27 28 29 |
#include <linux/init.h> #include <linux/module.h> #include <linux/types.h> #include <linux/rtc.h> #include <linux/bcd.h> #include <linux/platform_device.h> #include <linux/interrupt.h> |
c8a6046e1 drivers/rtc: use ... |
30 |
#include <linux/of.h> |
f96411ab7 mfd: rtc-twl4030 ... |
31 |
|
a20542565 mfd: twl: Move he... |
32 |
#include <linux/mfd/twl.h> |
f96411ab7 mfd: rtc-twl4030 ... |
33 |
|
e3e7f95bc rtc: twl: kill st... |
34 35 36 37 |
enum twl_class { TWL_4030 = 0, TWL_6030, }; |
f96411ab7 mfd: rtc-twl4030 ... |
38 39 40 41 |
/* * RTC block register offsets (use TWL_MODULE_RTC) */ |
a6b49ffd2 rtc: Add twl6030 ... |
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
enum { REG_SECONDS_REG = 0, REG_MINUTES_REG, REG_HOURS_REG, REG_DAYS_REG, REG_MONTHS_REG, REG_YEARS_REG, REG_WEEKS_REG, REG_ALARM_SECONDS_REG, REG_ALARM_MINUTES_REG, REG_ALARM_HOURS_REG, REG_ALARM_DAYS_REG, REG_ALARM_MONTHS_REG, REG_ALARM_YEARS_REG, REG_RTC_CTRL_REG, REG_RTC_STATUS_REG, REG_RTC_INTERRUPTS_REG, REG_RTC_COMP_LSB_REG, REG_RTC_COMP_MSB_REG, }; |
2e84067b6 rtc-twl: Storage ... |
65 |
static const u8 twl4030_rtc_reg_map[] = { |
a6b49ffd2 rtc: Add twl6030 ... |
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
[REG_SECONDS_REG] = 0x00, [REG_MINUTES_REG] = 0x01, [REG_HOURS_REG] = 0x02, [REG_DAYS_REG] = 0x03, [REG_MONTHS_REG] = 0x04, [REG_YEARS_REG] = 0x05, [REG_WEEKS_REG] = 0x06, [REG_ALARM_SECONDS_REG] = 0x07, [REG_ALARM_MINUTES_REG] = 0x08, [REG_ALARM_HOURS_REG] = 0x09, [REG_ALARM_DAYS_REG] = 0x0A, [REG_ALARM_MONTHS_REG] = 0x0B, [REG_ALARM_YEARS_REG] = 0x0C, [REG_RTC_CTRL_REG] = 0x0D, [REG_RTC_STATUS_REG] = 0x0E, [REG_RTC_INTERRUPTS_REG] = 0x0F, [REG_RTC_COMP_LSB_REG] = 0x10, [REG_RTC_COMP_MSB_REG] = 0x11, }; |
2e84067b6 rtc-twl: Storage ... |
88 |
static const u8 twl6030_rtc_reg_map[] = { |
a6b49ffd2 rtc: Add twl6030 ... |
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
[REG_SECONDS_REG] = 0x00, [REG_MINUTES_REG] = 0x01, [REG_HOURS_REG] = 0x02, [REG_DAYS_REG] = 0x03, [REG_MONTHS_REG] = 0x04, [REG_YEARS_REG] = 0x05, [REG_WEEKS_REG] = 0x06, [REG_ALARM_SECONDS_REG] = 0x08, [REG_ALARM_MINUTES_REG] = 0x09, [REG_ALARM_HOURS_REG] = 0x0A, [REG_ALARM_DAYS_REG] = 0x0B, [REG_ALARM_MONTHS_REG] = 0x0C, [REG_ALARM_YEARS_REG] = 0x0D, [REG_RTC_CTRL_REG] = 0x10, [REG_RTC_STATUS_REG] = 0x11, [REG_RTC_INTERRUPTS_REG] = 0x12, [REG_RTC_COMP_LSB_REG] = 0x13, [REG_RTC_COMP_MSB_REG] = 0x14, }; |
f96411ab7 mfd: rtc-twl4030 ... |
111 112 113 114 115 116 117 118 119 |
/* RTC_CTRL_REG bitfields */ #define BIT_RTC_CTRL_REG_STOP_RTC_M 0x01 #define BIT_RTC_CTRL_REG_ROUND_30S_M 0x02 #define BIT_RTC_CTRL_REG_AUTO_COMP_M 0x04 #define BIT_RTC_CTRL_REG_MODE_12_24_M 0x08 #define BIT_RTC_CTRL_REG_TEST_MODE_M 0x10 #define BIT_RTC_CTRL_REG_SET_32_COUNTER_M 0x20 #define BIT_RTC_CTRL_REG_GET_TIME_M 0x40 |
f3ec434c6 drivers/rtc/rtc-t... |
120 |
#define BIT_RTC_CTRL_REG_RTC_V_OPT 0x80 |
f96411ab7 mfd: rtc-twl4030 ... |
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
/* RTC_STATUS_REG bitfields */ #define BIT_RTC_STATUS_REG_RUN_M 0x02 #define BIT_RTC_STATUS_REG_1S_EVENT_M 0x04 #define BIT_RTC_STATUS_REG_1M_EVENT_M 0x08 #define BIT_RTC_STATUS_REG_1H_EVENT_M 0x10 #define BIT_RTC_STATUS_REG_1D_EVENT_M 0x20 #define BIT_RTC_STATUS_REG_ALARM_M 0x40 #define BIT_RTC_STATUS_REG_POWER_UP_M 0x80 /* RTC_INTERRUPTS_REG bitfields */ #define BIT_RTC_INTERRUPTS_REG_EVERY_M 0x03 #define BIT_RTC_INTERRUPTS_REG_IT_TIMER_M 0x04 #define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M 0x08 /* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */ #define ALL_TIME_REGS 6 /*----------------------------------------------------------------------*/ |
e3e7f95bc rtc: twl: kill st... |
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
struct twl_rtc { struct device *dev; struct rtc_device *rtc; u8 *reg_map; /* * Cache the value for timer/alarm interrupts register; this is * only changed by callers holding rtc ops lock (or resume). */ unsigned char rtc_irq_bits; bool wake_enabled; #ifdef CONFIG_PM_SLEEP unsigned char irqstat; #endif enum twl_class class; }; |
f96411ab7 mfd: rtc-twl4030 ... |
156 157 |
/* |
ef3b7d0d3 mfd: Rename twl40... |
158 |
* Supports 1 byte read from TWL RTC register. |
f96411ab7 mfd: rtc-twl4030 ... |
159 |
*/ |
e3e7f95bc rtc: twl: kill st... |
160 |
static int twl_rtc_read_u8(struct twl_rtc *twl_rtc, u8 *data, u8 reg) |
f96411ab7 mfd: rtc-twl4030 ... |
161 162 |
{ int ret; |
e3e7f95bc rtc: twl: kill st... |
163 |
ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (twl_rtc->reg_map[reg])); |
f96411ab7 mfd: rtc-twl4030 ... |
164 |
if (ret < 0) |
a737e835e rtc: use more sta... |
165 166 |
pr_err("Could not read TWL register %X - error %d ", reg, ret); |
f96411ab7 mfd: rtc-twl4030 ... |
167 168 169 170 |
return ret; } /* |
ef3b7d0d3 mfd: Rename twl40... |
171 |
* Supports 1 byte write to TWL RTC registers. |
f96411ab7 mfd: rtc-twl4030 ... |
172 |
*/ |
e3e7f95bc rtc: twl: kill st... |
173 |
static int twl_rtc_write_u8(struct twl_rtc *twl_rtc, u8 data, u8 reg) |
f96411ab7 mfd: rtc-twl4030 ... |
174 175 |
{ int ret; |
e3e7f95bc rtc: twl: kill st... |
176 |
ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (twl_rtc->reg_map[reg])); |
f96411ab7 mfd: rtc-twl4030 ... |
177 |
if (ret < 0) |
a737e835e rtc: use more sta... |
178 179 180 |
pr_err("Could not write TWL register %X - error %d ", reg, ret); |
f96411ab7 mfd: rtc-twl4030 ... |
181 182 183 184 |
return ret; } /* |
a748384bb rtc: tw4030 add a... |
185 |
* Enable 1/second update and/or alarm interrupts. |
f96411ab7 mfd: rtc-twl4030 ... |
186 |
*/ |
e3e7f95bc rtc: twl: kill st... |
187 |
static int set_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit) |
f96411ab7 mfd: rtc-twl4030 ... |
188 189 190 |
{ unsigned char val; int ret; |
ce9f65063 drivers/rtc/rtc-t... |
191 |
/* if the bit is set, return from here */ |
e3e7f95bc rtc: twl: kill st... |
192 |
if (twl_rtc->rtc_irq_bits & bit) |
ce9f65063 drivers/rtc/rtc-t... |
193 |
return 0; |
e3e7f95bc rtc: twl: kill st... |
194 |
val = twl_rtc->rtc_irq_bits | bit; |
a748384bb rtc: tw4030 add a... |
195 |
val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M; |
e3e7f95bc rtc: twl: kill st... |
196 |
ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
197 |
if (ret == 0) |
e3e7f95bc rtc: twl: kill st... |
198 |
twl_rtc->rtc_irq_bits = val; |
f96411ab7 mfd: rtc-twl4030 ... |
199 200 201 202 203 |
return ret; } /* |
a748384bb rtc: tw4030 add a... |
204 |
* Disable update and/or alarm interrupts. |
f96411ab7 mfd: rtc-twl4030 ... |
205 |
*/ |
e3e7f95bc rtc: twl: kill st... |
206 |
static int mask_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit) |
f96411ab7 mfd: rtc-twl4030 ... |
207 208 209 |
{ unsigned char val; int ret; |
ce9f65063 drivers/rtc/rtc-t... |
210 |
/* if the bit is clear, return from here */ |
e3e7f95bc rtc: twl: kill st... |
211 |
if (!(twl_rtc->rtc_irq_bits & bit)) |
ce9f65063 drivers/rtc/rtc-t... |
212 |
return 0; |
e3e7f95bc rtc: twl: kill st... |
213 214 |
val = twl_rtc->rtc_irq_bits & ~bit; ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
215 |
if (ret == 0) |
e3e7f95bc rtc: twl: kill st... |
216 |
twl_rtc->rtc_irq_bits = val; |
f96411ab7 mfd: rtc-twl4030 ... |
217 218 219 |
return ret; } |
ef3b7d0d3 mfd: Rename twl40... |
220 |
static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) |
f96411ab7 mfd: rtc-twl4030 ... |
221 |
{ |
ae8458949 drivers/rtc/rtc-t... |
222 |
struct platform_device *pdev = to_platform_device(dev); |
e3e7f95bc rtc: twl: kill st... |
223 |
struct twl_rtc *twl_rtc = dev_get_drvdata(dev); |
ae8458949 drivers/rtc/rtc-t... |
224 |
int irq = platform_get_irq(pdev, 0); |
f96411ab7 mfd: rtc-twl4030 ... |
225 |
int ret; |
ae8458949 drivers/rtc/rtc-t... |
226 |
if (enabled) { |
e3e7f95bc rtc: twl: kill st... |
227 228 229 |
ret = set_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); if (device_can_wakeup(dev) && !twl_rtc->wake_enabled) { |
ae8458949 drivers/rtc/rtc-t... |
230 |
enable_irq_wake(irq); |
e3e7f95bc rtc: twl: kill st... |
231 |
twl_rtc->wake_enabled = true; |
ae8458949 drivers/rtc/rtc-t... |
232 233 |
} } else { |
e3e7f95bc rtc: twl: kill st... |
234 235 236 |
ret = mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); if (twl_rtc->wake_enabled) { |
ae8458949 drivers/rtc/rtc-t... |
237 |
disable_irq_wake(irq); |
e3e7f95bc rtc: twl: kill st... |
238 |
twl_rtc->wake_enabled = false; |
ae8458949 drivers/rtc/rtc-t... |
239 240 |
} } |
f96411ab7 mfd: rtc-twl4030 ... |
241 242 243 |
return ret; } |
f96411ab7 mfd: rtc-twl4030 ... |
244 |
/* |
ef3b7d0d3 mfd: Rename twl40... |
245 |
* Gets current TWL RTC time and date parameters. |
f96411ab7 mfd: rtc-twl4030 ... |
246 247 248 249 250 251 252 |
* * The RTC's time/alarm representation is not what gmtime(3) requires * Linux to use: * * - Months are 1..12 vs Linux 0-11 * - Years are 0..99 vs Linux 1900..N (we assume 21st century) */ |
ef3b7d0d3 mfd: Rename twl40... |
253 |
static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm) |
f96411ab7 mfd: rtc-twl4030 ... |
254 |
{ |
e3e7f95bc rtc: twl: kill st... |
255 |
struct twl_rtc *twl_rtc = dev_get_drvdata(dev); |
14591d888 mfd/rtc/gpio: twl... |
256 |
unsigned char rtc_data[ALL_TIME_REGS]; |
f96411ab7 mfd: rtc-twl4030 ... |
257 258 |
int ret; u8 save_control; |
f3ec434c6 drivers/rtc/rtc-t... |
259 |
u8 rtc_control; |
f96411ab7 mfd: rtc-twl4030 ... |
260 |
|
e3e7f95bc rtc: twl: kill st... |
261 |
ret = twl_rtc_read_u8(twl_rtc, &save_control, REG_RTC_CTRL_REG); |
f3ec434c6 drivers/rtc/rtc-t... |
262 263 264 |
if (ret < 0) { dev_err(dev, "%s: reading CTRL_REG, error %d ", __func__, ret); |
f96411ab7 mfd: rtc-twl4030 ... |
265 |
return ret; |
f3ec434c6 drivers/rtc/rtc-t... |
266 267 |
} /* for twl6030/32 make sure BIT_RTC_CTRL_REG_GET_TIME_M is clear */ |
e3e7f95bc rtc: twl: kill st... |
268 |
if (twl_rtc->class == TWL_6030) { |
f3ec434c6 drivers/rtc/rtc-t... |
269 270 |
if (save_control & BIT_RTC_CTRL_REG_GET_TIME_M) { save_control &= ~BIT_RTC_CTRL_REG_GET_TIME_M; |
e3e7f95bc rtc: twl: kill st... |
271 272 |
ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); |
f3ec434c6 drivers/rtc/rtc-t... |
273 274 275 276 277 278 279 280 |
if (ret < 0) { dev_err(dev, "%s clr GET_TIME, error %d ", __func__, ret); return ret; } } } |
f96411ab7 mfd: rtc-twl4030 ... |
281 |
|
f3ec434c6 drivers/rtc/rtc-t... |
282 283 |
/* Copy RTC counting registers to static registers or latches */ rtc_control = save_control | BIT_RTC_CTRL_REG_GET_TIME_M; |
f96411ab7 mfd: rtc-twl4030 ... |
284 |
|
f3ec434c6 drivers/rtc/rtc-t... |
285 |
/* for twl6030/32 enable read access to static shadowed registers */ |
e3e7f95bc rtc: twl: kill st... |
286 |
if (twl_rtc->class == TWL_6030) |
f3ec434c6 drivers/rtc/rtc-t... |
287 |
rtc_control |= BIT_RTC_CTRL_REG_RTC_V_OPT; |
e3e7f95bc rtc: twl: kill st... |
288 |
ret = twl_rtc_write_u8(twl_rtc, rtc_control, REG_RTC_CTRL_REG); |
f3ec434c6 drivers/rtc/rtc-t... |
289 290 291 |
if (ret < 0) { dev_err(dev, "%s: writing CTRL_REG, error %d ", __func__, ret); |
f96411ab7 mfd: rtc-twl4030 ... |
292 |
return ret; |
f3ec434c6 drivers/rtc/rtc-t... |
293 |
} |
f96411ab7 mfd: rtc-twl4030 ... |
294 |
|
ef3b7d0d3 mfd: Rename twl40... |
295 |
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data, |
e3e7f95bc rtc: twl: kill st... |
296 |
(twl_rtc->reg_map[REG_SECONDS_REG]), ALL_TIME_REGS); |
f96411ab7 mfd: rtc-twl4030 ... |
297 298 |
if (ret < 0) { |
f3ec434c6 drivers/rtc/rtc-t... |
299 300 |
dev_err(dev, "%s: reading data, error %d ", __func__, ret); |
f96411ab7 mfd: rtc-twl4030 ... |
301 302 |
return ret; } |
f3ec434c6 drivers/rtc/rtc-t... |
303 |
/* for twl6030 restore original state of rtc control register */ |
e3e7f95bc rtc: twl: kill st... |
304 305 |
if (twl_rtc->class == TWL_6030) { ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); |
f3ec434c6 drivers/rtc/rtc-t... |
306 307 308 309 310 311 312 |
if (ret < 0) { dev_err(dev, "%s: restore CTRL_REG, error %d ", __func__, ret); return ret; } } |
f96411ab7 mfd: rtc-twl4030 ... |
313 314 315 316 317 318 319 320 321 |
tm->tm_sec = bcd2bin(rtc_data[0]); tm->tm_min = bcd2bin(rtc_data[1]); tm->tm_hour = bcd2bin(rtc_data[2]); tm->tm_mday = bcd2bin(rtc_data[3]); tm->tm_mon = bcd2bin(rtc_data[4]) - 1; tm->tm_year = bcd2bin(rtc_data[5]) + 100; return ret; } |
ef3b7d0d3 mfd: Rename twl40... |
322 |
static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm) |
f96411ab7 mfd: rtc-twl4030 ... |
323 |
{ |
e3e7f95bc rtc: twl: kill st... |
324 |
struct twl_rtc *twl_rtc = dev_get_drvdata(dev); |
f96411ab7 mfd: rtc-twl4030 ... |
325 |
unsigned char save_control; |
14591d888 mfd/rtc/gpio: twl... |
326 |
unsigned char rtc_data[ALL_TIME_REGS]; |
f96411ab7 mfd: rtc-twl4030 ... |
327 |
int ret; |
14591d888 mfd/rtc/gpio: twl... |
328 329 330 331 332 333 |
rtc_data[0] = bin2bcd(tm->tm_sec); rtc_data[1] = bin2bcd(tm->tm_min); rtc_data[2] = bin2bcd(tm->tm_hour); rtc_data[3] = bin2bcd(tm->tm_mday); rtc_data[4] = bin2bcd(tm->tm_mon + 1); rtc_data[5] = bin2bcd(tm->tm_year - 100); |
f96411ab7 mfd: rtc-twl4030 ... |
334 335 |
/* Stop RTC while updating the TC registers */ |
e3e7f95bc rtc: twl: kill st... |
336 |
ret = twl_rtc_read_u8(twl_rtc, &save_control, REG_RTC_CTRL_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
337 338 339 340 |
if (ret < 0) goto out; save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M; |
e3e7f95bc rtc: twl: kill st... |
341 |
ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
342 343 344 345 |
if (ret < 0) goto out; /* update all the time registers in one shot */ |
ef3b7d0d3 mfd: Rename twl40... |
346 |
ret = twl_i2c_write(TWL_MODULE_RTC, rtc_data, |
e3e7f95bc rtc: twl: kill st... |
347 |
(twl_rtc->reg_map[REG_SECONDS_REG]), ALL_TIME_REGS); |
f96411ab7 mfd: rtc-twl4030 ... |
348 349 350 351 352 353 354 355 |
if (ret < 0) { dev_err(dev, "rtc_set_time error %d ", ret); goto out; } /* Start back RTC */ save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M; |
e3e7f95bc rtc: twl: kill st... |
356 |
ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
357 358 359 360 361 362 |
out: return ret; } /* |
ef3b7d0d3 mfd: Rename twl40... |
363 |
* Gets current TWL RTC alarm time. |
f96411ab7 mfd: rtc-twl4030 ... |
364 |
*/ |
ef3b7d0d3 mfd: Rename twl40... |
365 |
static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) |
f96411ab7 mfd: rtc-twl4030 ... |
366 |
{ |
e3e7f95bc rtc: twl: kill st... |
367 |
struct twl_rtc *twl_rtc = dev_get_drvdata(dev); |
14591d888 mfd/rtc/gpio: twl... |
368 |
unsigned char rtc_data[ALL_TIME_REGS]; |
f96411ab7 mfd: rtc-twl4030 ... |
369 |
int ret; |
ef3b7d0d3 mfd: Rename twl40... |
370 |
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data, |
e3e7f95bc rtc: twl: kill st... |
371 |
twl_rtc->reg_map[REG_ALARM_SECONDS_REG], ALL_TIME_REGS); |
f96411ab7 mfd: rtc-twl4030 ... |
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
if (ret < 0) { dev_err(dev, "rtc_read_alarm error %d ", ret); return ret; } /* some of these fields may be wildcard/"match all" */ alm->time.tm_sec = bcd2bin(rtc_data[0]); alm->time.tm_min = bcd2bin(rtc_data[1]); alm->time.tm_hour = bcd2bin(rtc_data[2]); alm->time.tm_mday = bcd2bin(rtc_data[3]); alm->time.tm_mon = bcd2bin(rtc_data[4]) - 1; alm->time.tm_year = bcd2bin(rtc_data[5]) + 100; /* report cached alarm enable state */ |
e3e7f95bc rtc: twl: kill st... |
387 |
if (twl_rtc->rtc_irq_bits & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) |
f96411ab7 mfd: rtc-twl4030 ... |
388 389 390 391 |
alm->enabled = 1; return ret; } |
ef3b7d0d3 mfd: Rename twl40... |
392 |
static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) |
f96411ab7 mfd: rtc-twl4030 ... |
393 |
{ |
e3e7f95bc rtc: twl: kill st... |
394 |
struct twl_rtc *twl_rtc = dev_get_drvdata(dev); |
14591d888 mfd/rtc/gpio: twl... |
395 |
unsigned char alarm_data[ALL_TIME_REGS]; |
f96411ab7 mfd: rtc-twl4030 ... |
396 |
int ret; |
ef3b7d0d3 mfd: Rename twl40... |
397 |
ret = twl_rtc_alarm_irq_enable(dev, 0); |
f96411ab7 mfd: rtc-twl4030 ... |
398 399 |
if (ret) goto out; |
14591d888 mfd/rtc/gpio: twl... |
400 401 402 403 404 405 |
alarm_data[0] = bin2bcd(alm->time.tm_sec); alarm_data[1] = bin2bcd(alm->time.tm_min); alarm_data[2] = bin2bcd(alm->time.tm_hour); alarm_data[3] = bin2bcd(alm->time.tm_mday); alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); alarm_data[5] = bin2bcd(alm->time.tm_year - 100); |
f96411ab7 mfd: rtc-twl4030 ... |
406 407 |
/* update all the alarm registers in one shot */ |
ef3b7d0d3 mfd: Rename twl40... |
408 |
ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data, |
e3e7f95bc rtc: twl: kill st... |
409 |
twl_rtc->reg_map[REG_ALARM_SECONDS_REG], ALL_TIME_REGS); |
f96411ab7 mfd: rtc-twl4030 ... |
410 411 412 413 414 415 416 |
if (ret) { dev_err(dev, "rtc_set_alarm error %d ", ret); goto out; } if (alm->enabled) |
ef3b7d0d3 mfd: Rename twl40... |
417 |
ret = twl_rtc_alarm_irq_enable(dev, 1); |
f96411ab7 mfd: rtc-twl4030 ... |
418 419 420 |
out: return ret; } |
e3e7f95bc rtc: twl: kill st... |
421 |
static irqreturn_t twl_rtc_interrupt(int irq, void *data) |
f96411ab7 mfd: rtc-twl4030 ... |
422 |
{ |
e3e7f95bc rtc: twl: kill st... |
423 |
struct twl_rtc *twl_rtc = data; |
2778ebcc0 drivers/rtc/rtc-t... |
424 |
unsigned long events; |
f96411ab7 mfd: rtc-twl4030 ... |
425 426 427 |
int ret = IRQ_NONE; int res; u8 rd_reg; |
e3e7f95bc rtc: twl: kill st... |
428 |
res = twl_rtc_read_u8(twl_rtc, &rd_reg, REG_RTC_STATUS_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
429 430 431 432 433 434 435 436 437 |
if (res) goto out; /* * Figure out source of interrupt: ALARM or TIMER in RTC_STATUS_REG. * only one (ALARM or RTC) interrupt source may be enabled * at time, we also could check our results * by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM] */ if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M) |
2778ebcc0 drivers/rtc/rtc-t... |
438 |
events = RTC_IRQF | RTC_AF; |
f96411ab7 mfd: rtc-twl4030 ... |
439 |
else |
2778ebcc0 drivers/rtc/rtc-t... |
440 |
events = RTC_IRQF | RTC_PF; |
f96411ab7 mfd: rtc-twl4030 ... |
441 |
|
e3e7f95bc rtc: twl: kill st... |
442 443 |
res = twl_rtc_write_u8(twl_rtc, BIT_RTC_STATUS_REG_ALARM_M, REG_RTC_STATUS_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
444 445 |
if (res) goto out; |
e3e7f95bc rtc: twl: kill st... |
446 |
if (twl_rtc->class == TWL_4030) { |
a6b49ffd2 rtc: Add twl6030 ... |
447 448 449 450 451 452 453 454 455 456 457 458 |
/* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1 * needs 2 reads to clear the interrupt. One read is done in * do_twl_pwrirq(). Doing the second read, to clear * the bit. * * FIXME the reason PWR_ISR1 needs an extra read is that * RTC_IF retriggered until we cleared REG_ALARM_M above. * But re-reading like this is a bad hack; by doing so we * risk wrongly clearing status for some other IRQ (losing * the interrupt). Be smarter about handling RTC_UF ... */ res = twl_i2c_read_u8(TWL4030_MODULE_INT, |
f96411ab7 mfd: rtc-twl4030 ... |
459 |
&rd_reg, TWL4030_INT_PWR_ISR1); |
a6b49ffd2 rtc: Add twl6030 ... |
460 461 462 |
if (res) goto out; } |
f96411ab7 mfd: rtc-twl4030 ... |
463 464 |
/* Notify RTC core on event */ |
e3e7f95bc rtc: twl: kill st... |
465 |
rtc_update_irq(twl_rtc->rtc, 1, events); |
f96411ab7 mfd: rtc-twl4030 ... |
466 467 468 469 470 |
ret = IRQ_HANDLED; out: return ret; } |
34c7b3ac4 rtc: constify rtc... |
471 |
static const struct rtc_class_ops twl_rtc_ops = { |
ef3b7d0d3 mfd: Rename twl40... |
472 473 474 475 476 |
.read_time = twl_rtc_read_time, .set_time = twl_rtc_set_time, .read_alarm = twl_rtc_read_alarm, .set_alarm = twl_rtc_set_alarm, .alarm_irq_enable = twl_rtc_alarm_irq_enable, |
f96411ab7 mfd: rtc-twl4030 ... |
477 478 479 |
}; /*----------------------------------------------------------------------*/ |
5a167f454 Drivers: rtc: rem... |
480 |
static int twl_rtc_probe(struct platform_device *pdev) |
f96411ab7 mfd: rtc-twl4030 ... |
481 |
{ |
e3e7f95bc rtc: twl: kill st... |
482 |
struct twl_rtc *twl_rtc; |
1c02cbfec rtc: twl: make dr... |
483 |
struct device_node *np = pdev->dev.of_node; |
7e72c6863 rtc: twl: Fix reg... |
484 |
int ret = -EINVAL; |
f96411ab7 mfd: rtc-twl4030 ... |
485 486 |
int irq = platform_get_irq(pdev, 0); u8 rd_reg; |
1c02cbfec rtc: twl: make dr... |
487 488 489 490 491 |
if (!np) { dev_err(&pdev->dev, "no DT info "); return -EINVAL; } |
2fac6674d rtc: bunch of dri... |
492 |
if (irq <= 0) |
f53eeb853 drivers/rtc/rtc-t... |
493 |
return ret; |
f96411ab7 mfd: rtc-twl4030 ... |
494 |
|
e3e7f95bc rtc: twl: kill st... |
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
twl_rtc = devm_kzalloc(&pdev->dev, sizeof(*twl_rtc), GFP_KERNEL); if (!twl_rtc) return -ENOMEM; if (twl_class_is_4030()) { twl_rtc->class = TWL_4030; twl_rtc->reg_map = (u8 *)twl4030_rtc_reg_map; } else if (twl_class_is_6030()) { twl_rtc->class = TWL_6030; twl_rtc->reg_map = (u8 *)twl6030_rtc_reg_map; } else { dev_err(&pdev->dev, "TWL Class not supported. "); return -EINVAL; } |
d3869ff68 drivers/rtc/rtc-t... |
510 |
|
e3e7f95bc rtc: twl: kill st... |
511 |
ret = twl_rtc_read_u8(twl_rtc, &rd_reg, REG_RTC_STATUS_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
512 |
if (ret < 0) |
f53eeb853 drivers/rtc/rtc-t... |
513 |
return ret; |
f96411ab7 mfd: rtc-twl4030 ... |
514 515 516 517 518 519 520 521 522 523 |
if (rd_reg & BIT_RTC_STATUS_REG_POWER_UP_M) dev_warn(&pdev->dev, "Power up reset detected. "); if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M) dev_warn(&pdev->dev, "Pending Alarm interrupt detected. "); /* Clear RTC Power up reset and pending alarm interrupts */ |
e3e7f95bc rtc: twl: kill st... |
524 |
ret = twl_rtc_write_u8(twl_rtc, rd_reg, REG_RTC_STATUS_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
525 |
if (ret < 0) |
f53eeb853 drivers/rtc/rtc-t... |
526 |
return ret; |
f96411ab7 mfd: rtc-twl4030 ... |
527 |
|
e3e7f95bc rtc: twl: kill st... |
528 |
if (twl_rtc->class == TWL_6030) { |
a6b49ffd2 rtc: Add twl6030 ... |
529 530 531 532 533 |
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK, REG_INT_MSK_LINE_A); twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK, REG_INT_MSK_STS_A); } |
f7439bcb7 drivers/rtc/rtc-t... |
534 535 |
dev_info(&pdev->dev, "Enabling TWL-RTC "); |
e3e7f95bc rtc: twl: kill st... |
536 537 |
ret = twl_rtc_write_u8(twl_rtc, BIT_RTC_CTRL_REG_STOP_RTC_M, REG_RTC_CTRL_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
538 |
if (ret < 0) |
f53eeb853 drivers/rtc/rtc-t... |
539 |
return ret; |
f96411ab7 mfd: rtc-twl4030 ... |
540 |
|
8dcebaa9a drivers/rtc/rtc-t... |
541 |
/* ensure interrupts are disabled, bootloaders can be strange */ |
e3e7f95bc rtc: twl: kill st... |
542 |
ret = twl_rtc_write_u8(twl_rtc, 0, REG_RTC_INTERRUPTS_REG); |
8dcebaa9a drivers/rtc/rtc-t... |
543 544 545 |
if (ret < 0) dev_warn(&pdev->dev, "unable to disable interrupt "); |
f96411ab7 mfd: rtc-twl4030 ... |
546 |
/* init cached IRQ enable bits */ |
e3e7f95bc rtc: twl: kill st... |
547 548 |
ret = twl_rtc_read_u8(twl_rtc, &twl_rtc->rtc_irq_bits, REG_RTC_INTERRUPTS_REG); |
f96411ab7 mfd: rtc-twl4030 ... |
549 |
if (ret < 0) |
f53eeb853 drivers/rtc/rtc-t... |
550 |
return ret; |
7e72c6863 rtc: twl: Fix reg... |
551 |
|
e3e7f95bc rtc: twl: kill st... |
552 |
platform_set_drvdata(pdev, twl_rtc); |
b99b94b52 drivers/rtc/rtc-t... |
553 |
device_init_wakeup(&pdev->dev, 1); |
e3e7f95bc rtc: twl: kill st... |
554 |
twl_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, |
f53eeb853 drivers/rtc/rtc-t... |
555 |
&twl_rtc_ops, THIS_MODULE); |
e3e7f95bc rtc: twl: kill st... |
556 |
if (IS_ERR(twl_rtc->rtc)) { |
7e72c6863 rtc: twl: Fix reg... |
557 558 |
dev_err(&pdev->dev, "can't register RTC device, err %ld ", |
e3e7f95bc rtc: twl: kill st... |
559 560 |
PTR_ERR(twl_rtc->rtc)); return PTR_ERR(twl_rtc->rtc); |
7e72c6863 rtc: twl: Fix reg... |
561 |
} |
f53eeb853 drivers/rtc/rtc-t... |
562 563 564 |
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, twl_rtc_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
e3e7f95bc rtc: twl: kill st... |
565 |
dev_name(&twl_rtc->rtc->dev), twl_rtc); |
7e72c6863 rtc: twl: Fix reg... |
566 567 568 |
if (ret < 0) { dev_err(&pdev->dev, "IRQ is not free. "); |
f53eeb853 drivers/rtc/rtc-t... |
569 |
return ret; |
7e72c6863 rtc: twl: Fix reg... |
570 |
} |
f96411ab7 mfd: rtc-twl4030 ... |
571 |
|
7e72c6863 rtc: twl: Fix reg... |
572 |
return 0; |
f96411ab7 mfd: rtc-twl4030 ... |
573 574 575 |
} /* |
ef3b7d0d3 mfd: Rename twl40... |
576 |
* Disable all TWL RTC module interrupts. |
f96411ab7 mfd: rtc-twl4030 ... |
577 578 |
* Sets status flag to free. */ |
5a167f454 Drivers: rtc: rem... |
579 |
static int twl_rtc_remove(struct platform_device *pdev) |
f96411ab7 mfd: rtc-twl4030 ... |
580 |
{ |
e3e7f95bc rtc: twl: kill st... |
581 |
struct twl_rtc *twl_rtc = platform_get_drvdata(pdev); |
f96411ab7 mfd: rtc-twl4030 ... |
582 |
/* leave rtc running, but disable irqs */ |
e3e7f95bc rtc: twl: kill st... |
583 584 585 |
mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); if (twl_rtc->class == TWL_6030) { |
a6b49ffd2 rtc: Add twl6030 ... |
586 587 588 589 590 |
twl6030_interrupt_mask(TWL6030_RTC_INT_MASK, REG_INT_MSK_LINE_A); twl6030_interrupt_mask(TWL6030_RTC_INT_MASK, REG_INT_MSK_STS_A); } |
f96411ab7 mfd: rtc-twl4030 ... |
591 592 |
return 0; } |
ef3b7d0d3 mfd: Rename twl40... |
593 |
static void twl_rtc_shutdown(struct platform_device *pdev) |
f96411ab7 mfd: rtc-twl4030 ... |
594 |
{ |
e3e7f95bc rtc: twl: kill st... |
595 |
struct twl_rtc *twl_rtc = platform_get_drvdata(pdev); |
cafa1d8b0 rtc: rtc-twl4030 ... |
596 597 |
/* mask timer interrupts, but leave alarm interrupts on to enable power-on when alarm is triggered */ |
e3e7f95bc rtc: twl: kill st... |
598 |
mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); |
f96411ab7 mfd: rtc-twl4030 ... |
599 |
} |
b9d8c4603 rtc: rtc-twl: con... |
600 |
#ifdef CONFIG_PM_SLEEP |
b9d8c4603 rtc: rtc-twl: con... |
601 |
static int twl_rtc_suspend(struct device *dev) |
f96411ab7 mfd: rtc-twl4030 ... |
602 |
{ |
e3e7f95bc rtc: twl: kill st... |
603 |
struct twl_rtc *twl_rtc = dev_get_drvdata(dev); |
f96411ab7 mfd: rtc-twl4030 ... |
604 |
|
e3e7f95bc rtc: twl: kill st... |
605 606 607 |
twl_rtc->irqstat = twl_rtc->rtc_irq_bits; mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); |
f96411ab7 mfd: rtc-twl4030 ... |
608 609 |
return 0; } |
b9d8c4603 rtc: rtc-twl: con... |
610 |
static int twl_rtc_resume(struct device *dev) |
f96411ab7 mfd: rtc-twl4030 ... |
611 |
{ |
e3e7f95bc rtc: twl: kill st... |
612 613 614 |
struct twl_rtc *twl_rtc = dev_get_drvdata(dev); set_rtc_irq_bit(twl_rtc, twl_rtc->irqstat); |
f96411ab7 mfd: rtc-twl4030 ... |
615 616 |
return 0; } |
f96411ab7 mfd: rtc-twl4030 ... |
617 |
#endif |
b9d8c4603 rtc: rtc-twl: con... |
618 |
static SIMPLE_DEV_PM_OPS(twl_rtc_pm_ops, twl_rtc_suspend, twl_rtc_resume); |
948170f89 drivers/rtc/rtc-t... |
619 620 621 622 623 |
static const struct of_device_id twl_rtc_of_match[] = { {.compatible = "ti,twl4030-rtc", }, { }, }; MODULE_DEVICE_TABLE(of, twl_rtc_of_match); |
f96411ab7 mfd: rtc-twl4030 ... |
624 625 |
static struct platform_driver twl4030rtc_driver = { |
ef3b7d0d3 mfd: Rename twl40... |
626 |
.probe = twl_rtc_probe, |
5a167f454 Drivers: rtc: rem... |
627 |
.remove = twl_rtc_remove, |
ef3b7d0d3 mfd: Rename twl40... |
628 |
.shutdown = twl_rtc_shutdown, |
f96411ab7 mfd: rtc-twl4030 ... |
629 |
.driver = { |
948170f89 drivers/rtc/rtc-t... |
630 |
.name = "twl_rtc", |
b9d8c4603 rtc: rtc-twl: con... |
631 |
.pm = &twl_rtc_pm_ops, |
1c02cbfec rtc: twl: make dr... |
632 |
.of_match_table = twl_rtc_of_match, |
f96411ab7 mfd: rtc-twl4030 ... |
633 634 |
}, }; |
5ee67484d drivers/rtc/rtc-t... |
635 |
module_platform_driver(twl4030rtc_driver); |
f96411ab7 mfd: rtc-twl4030 ... |
636 637 638 |
MODULE_AUTHOR("Texas Instruments, MontaVista Software"); MODULE_LICENSE("GPL"); |