aboutsummaryrefslogtreecommitdiff
path: root/drivers/rtc/rtc-twl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc/rtc-twl.c')
-rw-r--r--drivers/rtc/rtc-twl.c269
1 files changed, 140 insertions, 129 deletions
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index ed1b8682812..1915464e4cd 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -27,6 +27,7 @@
#include <linux/bcd.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
#include <linux/i2c/twl.h>
@@ -112,6 +113,7 @@ static const u8 twl6030_rtc_reg_map[] = {
#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
+#define BIT_RTC_CTRL_REG_RTC_V_OPT 0x80
/* RTC_STATUS_REG bitfields */
#define BIT_RTC_STATUS_REG_RUN_M 0x02
@@ -176,6 +178,10 @@ static int set_rtc_irq_bit(unsigned char bit)
unsigned char val;
int ret;
+ /* if the bit is set, return from here */
+ if (rtc_irq_bits & bit)
+ return 0;
+
val = rtc_irq_bits | bit;
val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M;
ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
@@ -193,6 +199,10 @@ static int mask_rtc_irq_bit(unsigned char bit)
unsigned char val;
int ret;
+ /* if the bit is clear, return from here */
+ if (!(rtc_irq_bits & bit))
+ return 0;
+
val = rtc_irq_bits & ~bit;
ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
if (ret == 0)
@@ -203,24 +213,24 @@ static int mask_rtc_irq_bit(unsigned char bit)
static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
{
+ struct platform_device *pdev = to_platform_device(dev);
+ int irq = platform_get_irq(pdev, 0);
+ static bool twl_rtc_wake_enabled;
int ret;
- if (enabled)
+ if (enabled) {
ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
- else
+ if (device_can_wakeup(dev) && !twl_rtc_wake_enabled) {
+ enable_irq_wake(irq);
+ twl_rtc_wake_enabled = true;
+ }
+ } else {
ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
-
- return ret;
-}
-
-static int twl_rtc_update_irq_enable(struct device *dev, unsigned enabled)
-{
- int ret;
-
- if (enabled)
- ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
- else
- ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ if (twl_rtc_wake_enabled) {
+ disable_irq_wake(irq);
+ twl_rtc_wake_enabled = false;
+ }
+ }
return ret;
}
@@ -236,28 +246,60 @@ static int twl_rtc_update_irq_enable(struct device *dev, unsigned enabled)
*/
static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- unsigned char rtc_data[ALL_TIME_REGS + 1];
+ unsigned char rtc_data[ALL_TIME_REGS];
int ret;
u8 save_control;
+ u8 rtc_control;
ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(dev, "%s: reading CTRL_REG, error %d\n", __func__, ret);
return ret;
+ }
+ /* for twl6030/32 make sure BIT_RTC_CTRL_REG_GET_TIME_M is clear */
+ if (twl_class_is_6030()) {
+ if (save_control & BIT_RTC_CTRL_REG_GET_TIME_M) {
+ save_control &= ~BIT_RTC_CTRL_REG_GET_TIME_M;
+ ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ if (ret < 0) {
+ dev_err(dev, "%s clr GET_TIME, error %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ }
- save_control |= BIT_RTC_CTRL_REG_GET_TIME_M;
+ /* Copy RTC counting registers to static registers or latches */
+ rtc_control = save_control | BIT_RTC_CTRL_REG_GET_TIME_M;
- ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
- if (ret < 0)
+ /* for twl6030/32 enable read access to static shadowed registers */
+ if (twl_class_is_6030())
+ rtc_control |= BIT_RTC_CTRL_REG_RTC_V_OPT;
+
+ ret = twl_rtc_write_u8(rtc_control, REG_RTC_CTRL_REG);
+ if (ret < 0) {
+ dev_err(dev, "%s: writing CTRL_REG, error %d\n", __func__, ret);
return ret;
+ }
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
(rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
if (ret < 0) {
- dev_err(dev, "rtc_read_time error %d\n", ret);
+ dev_err(dev, "%s: reading data, error %d\n", __func__, ret);
return ret;
}
+ /* for twl6030 restore original state of rtc control register */
+ if (twl_class_is_6030()) {
+ ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ if (ret < 0) {
+ dev_err(dev, "%s: restore CTRL_REG, error %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
tm->tm_sec = bcd2bin(rtc_data[0]);
tm->tm_min = bcd2bin(rtc_data[1]);
tm->tm_hour = bcd2bin(rtc_data[2]);
@@ -271,15 +313,15 @@ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned char save_control;
- unsigned char rtc_data[ALL_TIME_REGS + 1];
+ unsigned char rtc_data[ALL_TIME_REGS];
int ret;
- rtc_data[1] = bin2bcd(tm->tm_sec);
- rtc_data[2] = bin2bcd(tm->tm_min);
- rtc_data[3] = bin2bcd(tm->tm_hour);
- rtc_data[4] = bin2bcd(tm->tm_mday);
- rtc_data[5] = bin2bcd(tm->tm_mon + 1);
- rtc_data[6] = bin2bcd(tm->tm_year - 100);
+ 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);
/* Stop RTC while updating the TC registers */
ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
@@ -287,7 +329,7 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
goto out;
save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
- twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
if (ret < 0)
goto out;
@@ -312,7 +354,7 @@ out:
*/
static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
- unsigned char rtc_data[ALL_TIME_REGS + 1];
+ unsigned char rtc_data[ALL_TIME_REGS];
int ret;
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
@@ -339,19 +381,19 @@ static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
- unsigned char alarm_data[ALL_TIME_REGS + 1];
+ unsigned char alarm_data[ALL_TIME_REGS];
int ret;
ret = twl_rtc_alarm_irq_enable(dev, 0);
if (ret)
goto out;
- alarm_data[1] = bin2bcd(alm->time.tm_sec);
- alarm_data[2] = bin2bcd(alm->time.tm_min);
- alarm_data[3] = bin2bcd(alm->time.tm_hour);
- alarm_data[4] = bin2bcd(alm->time.tm_mday);
- alarm_data[5] = bin2bcd(alm->time.tm_mon + 1);
- alarm_data[6] = bin2bcd(alm->time.tm_year - 100);
+ 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);
/* update all the alarm registers in one shot */
ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data,
@@ -369,19 +411,11 @@ out:
static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
{
- unsigned long events = 0;
+ unsigned long events;
int ret = IRQ_NONE;
int res;
u8 rd_reg;
-#ifdef CONFIG_LOCKDEP
- /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
- * we don't want and can't tolerate. Although it might be
- * friendlier not to borrow this thread context...
- */
- local_irq_enable();
-#endif
-
res = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
if (res)
goto out;
@@ -392,11 +426,11 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
* by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM]
*/
if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M)
- events |= RTC_IRQF | RTC_AF;
+ events = RTC_IRQF | RTC_AF;
else
- events |= RTC_IRQF | RTC_UF;
+ events = RTC_IRQF | RTC_PF;
- res = twl_rtc_write_u8(rd_reg | BIT_RTC_STATUS_REG_ALARM_M,
+ res = twl_rtc_write_u8(BIT_RTC_STATUS_REG_ALARM_M,
REG_RTC_STATUS_REG);
if (res)
goto out;
@@ -433,36 +467,29 @@ static struct rtc_class_ops twl_rtc_ops = {
.read_alarm = twl_rtc_read_alarm,
.set_alarm = twl_rtc_set_alarm,
.alarm_irq_enable = twl_rtc_alarm_irq_enable,
- .update_irq_enable = twl_rtc_update_irq_enable,
};
/*----------------------------------------------------------------------*/
-static int __devinit twl_rtc_probe(struct platform_device *pdev)
+static int twl_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
- int ret = 0;
+ int ret = -EINVAL;
int irq = platform_get_irq(pdev, 0);
u8 rd_reg;
if (irq <= 0)
- return -EINVAL;
-
- rtc = rtc_device_register(pdev->name,
- &pdev->dev, &twl_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
- dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
- PTR_ERR(rtc));
- goto out0;
-
- }
+ return ret;
- platform_set_drvdata(pdev, rtc);
+ /* Initialize the register map */
+ if (twl_class_is_4030())
+ rtc_reg_map = (u8 *)twl4030_rtc_reg_map;
+ else
+ rtc_reg_map = (u8 *)twl6030_rtc_reg_map;
ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
if (ret < 0)
- goto out1;
+ return ret;
if (rd_reg & BIT_RTC_STATUS_REG_POWER_UP_M)
dev_warn(&pdev->dev, "Power up reset detected.\n");
@@ -473,15 +500,7 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
/* Clear RTC Power up reset and pending alarm interrupts */
ret = twl_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG);
if (ret < 0)
- goto out1;
-
- ret = request_irq(irq, twl_rtc_interrupt,
- IRQF_TRIGGER_RISING,
- dev_name(&rtc->dev), rtc);
- if (ret < 0) {
- dev_err(&pdev->dev, "IRQ is not free.\n");
- goto out1;
- }
+ return ret;
if (twl_class_is_6030()) {
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
@@ -490,44 +509,51 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
REG_INT_MSK_STS_A);
}
- /* Check RTC module status, Enable if it is off */
- ret = twl_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
+ dev_info(&pdev->dev, "Enabling TWL-RTC\n");
+ ret = twl_rtc_write_u8(BIT_RTC_CTRL_REG_STOP_RTC_M, REG_RTC_CTRL_REG);
if (ret < 0)
- goto out2;
-
- if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) {
- dev_info(&pdev->dev, "Enabling TWL-RTC.\n");
- rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M;
- ret = twl_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
- if (ret < 0)
- goto out2;
- }
+ return ret;
+
+ /* ensure interrupts are disabled, bootloaders can be strange */
+ ret = twl_rtc_write_u8(0, REG_RTC_INTERRUPTS_REG);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "unable to disable interrupt\n");
/* init cached IRQ enable bits */
ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
if (ret < 0)
- goto out2;
+ return ret;
- return ret;
+ device_init_wakeup(&pdev->dev, 1);
-out2:
- free_irq(irq, rtc);
-out1:
- rtc_device_unregister(rtc);
-out0:
- return ret;
+ rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &twl_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
+ PTR_ERR(rtc));
+ return PTR_ERR(rtc);
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ twl_rtc_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(&rtc->dev), rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "IRQ is not free.\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+ return 0;
}
/*
* Disable all TWL RTC module interrupts.
* Sets status flag to free.
*/
-static int __devexit twl_rtc_remove(struct platform_device *pdev)
+static int twl_rtc_remove(struct platform_device *pdev)
{
/* leave rtc running, but disable irqs */
- struct rtc_device *rtc = platform_get_drvdata(pdev);
- int irq = platform_get_irq(pdev, 0);
-
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
if (twl_class_is_6030()) {
@@ -537,11 +563,6 @@ static int __devexit twl_rtc_remove(struct platform_device *pdev)
REG_INT_MSK_STS_A);
}
-
- free_irq(irq, rtc);
-
- rtc_device_unregister(rtc);
- platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -552,11 +573,10 @@ static void twl_rtc_shutdown(struct platform_device *pdev)
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
}
-#ifdef CONFIG_PM
-
+#ifdef CONFIG_PM_SLEEP
static unsigned char irqstat;
-static int twl_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+static int twl_rtc_suspend(struct device *dev)
{
irqstat = rtc_irq_bits;
@@ -564,47 +584,38 @@ static int twl_rtc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int twl_rtc_resume(struct platform_device *pdev)
+static int twl_rtc_resume(struct device *dev)
{
set_rtc_irq_bit(irqstat);
return 0;
}
+#endif
-#else
-#define twl_rtc_suspend NULL
-#define twl_rtc_resume NULL
+static SIMPLE_DEV_PM_OPS(twl_rtc_pm_ops, twl_rtc_suspend, twl_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl_rtc_of_match[] = {
+ {.compatible = "ti,twl4030-rtc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, twl_rtc_of_match);
#endif
MODULE_ALIAS("platform:twl_rtc");
static struct platform_driver twl4030rtc_driver = {
.probe = twl_rtc_probe,
- .remove = __devexit_p(twl_rtc_remove),
+ .remove = twl_rtc_remove,
.shutdown = twl_rtc_shutdown,
- .suspend = twl_rtc_suspend,
- .resume = twl_rtc_resume,
.driver = {
- .owner = THIS_MODULE,
- .name = "twl_rtc",
+ .owner = THIS_MODULE,
+ .name = "twl_rtc",
+ .pm = &twl_rtc_pm_ops,
+ .of_match_table = of_match_ptr(twl_rtc_of_match),
},
};
-static int __init twl_rtc_init(void)
-{
- if (twl_class_is_4030())
- rtc_reg_map = (u8 *) twl4030_rtc_reg_map;
- else
- rtc_reg_map = (u8 *) twl6030_rtc_reg_map;
-
- return platform_driver_register(&twl4030rtc_driver);
-}
-module_init(twl_rtc_init);
-
-static void __exit twl_rtc_exit(void)
-{
- platform_driver_unregister(&twl4030rtc_driver);
-}
-module_exit(twl_rtc_exit);
+module_platform_driver(twl4030rtc_driver);
MODULE_AUTHOR("Texas Instruments, MontaVista Software");
MODULE_LICENSE("GPL");