From d2def9ee17c73ea4f222bd8b185c17544a6532c8 Mon Sep 17 00:00:00 2001 From: Min-wuk Lee Date: Fri, 4 Nov 2011 16:54:42 +0900 Subject: rtc: max77663: Update driver for alarm interrupt Updated rtc-max77663 driver for alarm interrupt. - Enable alarm for sec, min, hour, mday, month and year except wday, because sometimes wday value is not matched with requested alarm time. - Set alarm to wake-up event from sleep mode. - Add max77663_rtc_irq_mask and max77663_rtc_irq_unmask functions. - Fix incorrected wday calculation. - Clean-up the codes. Bug 849360 Original Author: Jinyoung Park Reviewed-on: http://git-master/r/60655 (cherry picked from commit 80acd66deffa20a391e5324fc9038b7fab42d08d) Change-Id: Ibd637eac7f94ebc920873c652f7776072e2832d1 Reviewed-on: http://git-master/r/62380 Reviewed-on: http://git-master/r/63763 Reviewed-by: Min-wuk Lee Tested-by: Min-wuk Lee Reviewed-by: Bharat Nihalani Rebase-Id: Rfc8fb0c75d7514a4f0aeca69e9478a0d6572fa99 --- drivers/rtc/rtc-max77663.c | 239 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 183 insertions(+), 56 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-max77663.c b/drivers/rtc/rtc-max77663.c index 617659a3651f..348387ce9347 100644 --- a/drivers/rtc/rtc-max77663.c +++ b/drivers/rtc/rtc-max77663.c @@ -19,7 +19,7 @@ #include #include -/* Registers */ +/* RTC Registers */ #define MAX77663_RTC_IRQ 0x00 #define MAX77663_RTC_IRQ_MASK 0x01 #define MAX77663_RTC_CTRL_MODE 0x02 @@ -74,6 +74,11 @@ #define RTC_YEAR_BASE 100 #define RTC_YEAR_MAX 99 +/* ON/OFF Registers */ +#define MAX77663_REG_ONOFF_CFG2 0x42 + +#define ONOFF_WK_ALARM1_MASK (1 << 2) + enum { RTC_SEC, RTC_MIN, @@ -103,11 +108,13 @@ static inline int max77663_rtc_update_buffer(struct max77663_rtc *rtc, int write) { struct device *parent = _to_parent(rtc); - u8 val = FLAG_AUTO_CLEAR_MASK | RTC_WAKE_MASK | RB_UPDATE_MASK; + u8 val = FLAG_AUTO_CLEAR_MASK | RTC_WAKE_MASK; int ret; if (write) - val = FLAG_AUTO_CLEAR_MASK | RTC_WAKE_MASK | WB_UPDATE_MASK; + val |= WB_UPDATE_MASK; + else + val |= RB_UPDATE_MASK; dev_dbg(rtc->dev, "rtc_update_buffer: write=%d, addr=0x%x, val=0x%x\n", write, MAX77663_RTC_UPDATE0, val); @@ -192,18 +199,7 @@ static inline int max77663_rtc_reg_to_tm(struct max77663_rtc *rtc, u8 *buf, tm->tm_mday = (int)(buf[RTC_MONTHDAY] & MONTHDAY_MASK); tm->tm_mon = (int)(buf[RTC_MONTH] & MONTH_MASK) - 1; tm->tm_year = (int)(buf[RTC_YEAR] & YEAR_MASK) + RTC_YEAR_BASE; - tm->tm_wday = ffs(wday); - - dev_dbg(rtc->dev, "rtc_reg_to_tm: buf sec=%u, min=%u, hour=%u, " - "mday=%u, month=%u, year=%u, wday=%u\n", - buf[RTC_SEC] & SEC_MASK, buf[RTC_MIN] & MIN_MASK, - buf[RTC_HOUR] & HOUR_MASK, buf[RTC_MONTHDAY] & MONTHDAY_MASK, - buf[RTC_MONTH] & MONTH_MASK, buf[RTC_YEAR] & YEAR_MASK, - buf[RTC_WEEKDAY] & WEEKDAY_MASK); - dev_dbg(rtc->dev, "rtc_reg_to_tm: tm_sec=%d, tm_min=%d, tm_hour=%d, " - "tm_mday=%d, tm_mon=%d, tm_year=%d, tm_wday=%d\n", - tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, - tm->tm_year, tm->tm_wday); + tm->tm_wday = ffs(wday) - 1; return 0; } @@ -226,38 +222,83 @@ static inline int max77663_rtc_tm_to_reg(struct max77663_rtc *rtc, u8 *buf, buf[RTC_MONTHDAY] = tm->tm_mday | alarm_mask; buf[RTC_MONTH] = (tm->tm_mon + 1) | alarm_mask; buf[RTC_YEAR] = (tm->tm_year - RTC_YEAR_BASE) | alarm_mask; - buf[RTC_WEEKDAY] = (1 << tm->tm_wday) | alarm_mask; - - dev_dbg(rtc->dev, "rtc_tm_to_reg: tm_sec=%d, tm_min=%d, tm_hour=%d, " - "tm_mday=%d, tm_mon=%d, tm_year=%d, tm_wday=%d, alarm=%d\n", - tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, - tm->tm_year, tm->tm_wday, alarm); - dev_dbg(rtc->dev, "rtc_tm_to_reg: buf sec=%u, min=%u, hour=%u, " - "mday=%u, month=%u, year=%u, wday=%u\n", - buf[RTC_SEC] & SEC_MASK, buf[RTC_MIN] & MIN_MASK, - buf[RTC_HOUR] & HOUR_MASK, buf[RTC_MONTHDAY] & MONTHDAY_MASK, - buf[RTC_MONTH] & MONTH_MASK, buf[RTC_YEAR] & YEAR_MASK, - buf[RTC_WEEKDAY] & WEEKDAY_MASK); + + /* The wday is configured only when disabled alarm. */ + if (!alarm) + buf[RTC_WEEKDAY] = (1 << tm->tm_wday); + else + buf[RTC_WEEKDAY] = 0; return 0; } -static irqreturn_t max77663_rtc_irq(int irq, void *data) +static inline int max77663_rtc_irq_mask(struct max77663_rtc *rtc, u8 irq) { - struct max77663_rtc *rtc = (struct max77663_rtc *)data; - u8 val = 0; + struct device *parent = _to_parent(rtc); + u8 irq_mask = rtc->irq_mask | irq; + int ret = 0; + + ret = max77663_write(parent, MAX77663_RTC_IRQ_MASK, &irq_mask, 1, 1); + if (ret < 0) { + dev_err(rtc->dev, "rtc_irq_mask: Failed to set rtc irq mask\n"); + goto out; + } + rtc->irq_mask = irq_mask; + +out: + return ret; +} + +static inline int max77663_rtc_irq_unmask(struct max77663_rtc *rtc, u8 irq) +{ + struct device *parent = _to_parent(rtc); + u8 irq_mask = rtc->irq_mask & ~irq; + int ret = 0; + + ret = max77663_write(parent, MAX77663_RTC_IRQ_MASK, &irq_mask, 1, 1); + if (ret < 0) { + dev_err(rtc->dev, + "rtc_irq_unmask: Failed to set rtc irq mask\n"); + goto out; + } + rtc->irq_mask = irq_mask; + +out: + return ret; +} + +static inline int max77663_rtc_do_irq(struct max77663_rtc *rtc) +{ + struct device *parent = _to_parent(rtc); + u8 irq_status; int ret; - ret = max77663_rtc_read(rtc, MAX77663_RTC_IRQ, &val, 1, 0); - if (ret < 0) + ret = max77663_read(parent, MAX77663_RTC_IRQ, &irq_status, 1, 1); + if (ret < 0) { dev_err(rtc->dev, "rtc_irq: Failed to get rtc irq status\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_do_irq: irq_mask=0x%02x, irq_status=0x%02x\n", + rtc->irq_mask, irq_status); - if (val & RTC_IRQ_ALARM1_MASK) + if (!(rtc->irq_mask & RTC_IRQ_ALARM1_MASK) && + (irq_status & RTC_IRQ_ALARM1_MASK)) rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); - if (val & RTC_IRQ_1SEC_MASK) + if (!(rtc->irq_mask & RTC_IRQ_1SEC_MASK) && + (irq_status & RTC_IRQ_1SEC_MASK)) rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF); + return ret; +} + +static irqreturn_t max77663_rtc_irq(int irq, void *data) +{ + struct max77663_rtc *rtc = (struct max77663_rtc *)data; + + max77663_rtc_do_irq(rtc); + return IRQ_HANDLED; } @@ -265,33 +306,54 @@ static int max77663_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct max77663_rtc *rtc = dev_get_drvdata(dev); - struct device *parent = _to_parent(rtc); - u8 val = 0; int ret = 0; if (rtc->irq < 0) return -ENXIO; mutex_lock(&rtc->io_lock); + + /* Handle pending interrupt */ + ret = max77663_rtc_do_irq(rtc); + if (ret < 0) + goto out; + + /* Config alarm interrupt */ if (enabled) - val = rtc->irq_mask & ~RTC_IRQ_ALARM1_MASK; + max77663_rtc_irq_unmask(rtc, RTC_IRQ_ALARM1_MASK); else - val = rtc->irq_mask | RTC_IRQ_ALARM1_MASK; - - if (val != rtc->irq_mask) { - ret = max77663_write(parent, MAX77663_RTC_IRQ_MASK, &val, 1, 1); - if (ret < 0) { - dev_err(rtc->dev, "rtc_alarm_irq_enable: " - "Failed to set rtc irq mask\n"); - mutex_unlock(&rtc->io_lock); - return ret; - } - - rtc->irq_mask = val; - } + max77663_rtc_irq_mask(rtc, RTC_IRQ_ALARM1_MASK); + +out: mutex_unlock(&rtc->io_lock); + return ret; +} - return 0; +static int max77663_rtc_update_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct max77663_rtc *rtc = dev_get_drvdata(dev); + int ret = 0; + + if (rtc->irq < 0) + return -ENXIO; + + mutex_lock(&rtc->io_lock); + + /* Handle pending interrupt */ + ret = max77663_rtc_do_irq(rtc); + if (ret < 0) + goto out; + + /* Config update interrupt */ + if (enabled) + max77663_rtc_irq_unmask(rtc, RTC_IRQ_1SEC_MASK); + else + max77663_rtc_irq_mask(rtc, RTC_IRQ_1SEC_MASK); + +out: + mutex_unlock(&rtc->io_lock); + return ret; } static int max77663_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -306,7 +368,24 @@ static int max77663_rtc_read_time(struct device *dev, struct rtc_time *tm) return ret; } - return max77663_rtc_reg_to_tm(rtc, buf, tm); + dev_dbg(rtc->dev, "rtc_read_time: " + "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY], + buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]); + + ret = max77663_rtc_reg_to_tm(rtc, buf, tm); + if (ret < 0) { + dev_err(rtc->dev, "rtc_read_time: " + "Failed to convert register format into time format\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_read_time: " + "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec, tm->tm_wday); + + return ret; } static int max77663_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -315,6 +394,11 @@ static int max77663_rtc_set_time(struct device *dev, struct rtc_time *tm) u8 buf[RTC_NR]; int ret; + dev_dbg(rtc->dev, "rtc_set_time: " + "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec, tm->tm_wday); + ret = max77663_rtc_tm_to_reg(rtc, buf, tm, 0); if (ret < 0) { dev_err(rtc->dev, "rtc_set_time: " @@ -322,6 +406,11 @@ static int max77663_rtc_set_time(struct device *dev, struct rtc_time *tm) return ret; } + dev_dbg(rtc->dev, "rtc_set_time: " + "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY], + buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]); + return max77663_rtc_write(rtc, MAX77663_RTC_SEC, buf, sizeof(buf), 1); } @@ -339,6 +428,11 @@ static int max77663_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } + dev_dbg(rtc->dev, "rtc_read_alarm: " + "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY], + buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]); + ret = max77663_rtc_reg_to_tm(rtc, buf, &alrm->time); if (ret < 0) { dev_err(rtc->dev, "rtc_read_alarm: " @@ -346,6 +440,12 @@ static int max77663_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } + dev_dbg(rtc->dev, "rtc_read_alarm: " + "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n", + alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday, + alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec, + alrm->time.tm_wday); + if (rtc->irq_mask & RTC_IRQ_ALARM1_MASK) alrm->enabled = 1; else @@ -360,6 +460,19 @@ static int max77663_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) u8 buf[RTC_NR]; int ret; + ret = max77663_rtc_alarm_irq_enable(dev, 0); + if (ret < 0) { + dev_err(rtc->dev, + "rtc_set_alarm: Failed to disable rtc alarm\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_set_alarm: " + "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n", + alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday, + alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec, + alrm->time.tm_wday); + ret = max77663_rtc_tm_to_reg(rtc, buf, &alrm->time, 1); if (ret < 0) { dev_err(rtc->dev, "rtc_set_alarm: " @@ -367,6 +480,11 @@ static int max77663_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } + dev_dbg(rtc->dev, "rtc_set_alarm: " + "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY], + buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]); + ret = max77663_rtc_write(rtc, MAX77663_RTC_ALARM_SEC1, buf, sizeof(buf), 1); if (ret < 0) { @@ -375,14 +493,14 @@ static int max77663_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } - ret = max77663_rtc_alarm_irq_enable(dev, alrm->enabled); + ret = max77663_rtc_alarm_irq_enable(dev, 1); if (ret < 0) { dev_err(rtc->dev, - "rtc_set_alarm: Failed to set rtc alarm irq\n"); + "rtc_set_alarm: Failed to enable rtc alarm\n"); return ret; } - return 0; + return ret; } static const struct rtc_class_ops max77663_rtc_ops = { @@ -395,11 +513,12 @@ static const struct rtc_class_ops max77663_rtc_ops = { static int max77663_rtc_preinit(struct max77663_rtc *rtc) { + struct device *parent = _to_parent(rtc); u8 val; int ret; /* Mask all interrupts */ - rtc->irq_mask = RTC_IRQ_MASK; + rtc->irq_mask = 0xFF; ret = max77663_rtc_write(rtc, MAX77663_RTC_IRQ_MASK, &rtc->irq_mask, 1, 0); if (ret < 0) { @@ -415,6 +534,14 @@ static int max77663_rtc_preinit(struct max77663_rtc *rtc) return ret; } + /* Set alarm to wake-up event from sleep */ + ret = max77663_set_bits(parent, MAX77663_REG_ONOFF_CFG2, + ONOFF_WK_ALARM1_MASK, ONOFF_WK_ALARM1_MASK, 0); + if (ret < 0) { + dev_err(rtc->dev, "preinit: Failed to set onoff cfg2\n"); + return ret; + } + return 0; } -- cgit v1.2.3