kernel: rtc: rs5c372: fix alarm support
The rs5c372 RTC driver has alarm support, but it can't be enabled and only can handle 24 hours in the future spite the chip is 1 week capable. Provide these two patches: - Support alarms up to 1 week - Let the wakealarm to be used as a wakeup source This patch makes the alarm wakeup feature to be available in the Buffallo Linkstation LS421DE (mvebu target) and should also work with any other device if the hardware has the proper capability. Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
This commit is contained in:
parent
3d12b47985
commit
8336b0df5d
@ -0,0 +1,96 @@
|
|||||||
|
From: Daniel González Cabanelas <dgcbueu@gmail.com>
|
||||||
|
Subject: [PATCH 1/2] rtc: rs5c372: support alarms up to 1 week
|
||||||
|
|
||||||
|
The Ricoh R2221x, R2223x, RS5C372, RV5C387A chips can handle 1 week
|
||||||
|
alarms.
|
||||||
|
|
||||||
|
Read the "wday" alarm register and convert it to a date to support up 1
|
||||||
|
week in our driver.
|
||||||
|
|
||||||
|
Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/rtc/rtc-rs5c372.c | 48 ++++++++++++++++++++++++++++++++++-----
|
||||||
|
1 file changed, 42 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
|
||||||
|
index 3bd6eaa0d..94b778c6e 100644
|
||||||
|
--- a/drivers/rtc/rtc-rs5c372.c
|
||||||
|
+++ b/drivers/rtc/rtc-rs5c372.c
|
||||||
|
@@ -393,7 +393,9 @@ static int rs5c_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct rs5c372 *rs5c = i2c_get_clientdata(client);
|
||||||
|
- int status;
|
||||||
|
+ int status, wday_offs;
|
||||||
|
+ struct rtc_time rtc;
|
||||||
|
+ unsigned long alarm_secs;
|
||||||
|
|
||||||
|
status = rs5c_get_regs(rs5c);
|
||||||
|
if (status < 0)
|
||||||
|
@@ -403,6 +405,30 @@ static int rs5c_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||||
|
t->time.tm_sec = 0;
|
||||||
|
t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f);
|
||||||
|
t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]);
|
||||||
|
+ t->time.tm_wday = ffs(rs5c->regs[RS5C_REG_ALARM_A_WDAY] & 0x7f) - 1;
|
||||||
|
+
|
||||||
|
+ /* determine the day, month and year based on alarm wday, taking as a
|
||||||
|
+ * reference the current time from the rtc
|
||||||
|
+ */
|
||||||
|
+ status = rs5c372_rtc_read_time(dev, &rtc);
|
||||||
|
+ if (status < 0)
|
||||||
|
+ return status;
|
||||||
|
+
|
||||||
|
+ wday_offs = t->time.tm_wday - rtc.tm_wday;
|
||||||
|
+ alarm_secs = mktime64(rtc.tm_year + 1900,
|
||||||
|
+ rtc.tm_mon + 1,
|
||||||
|
+ rtc.tm_mday + wday_offs,
|
||||||
|
+ t->time.tm_hour,
|
||||||
|
+ t->time.tm_min,
|
||||||
|
+ t->time.tm_sec);
|
||||||
|
+
|
||||||
|
+ if (wday_offs < 0 || (wday_offs == 0 &&
|
||||||
|
+ (t->time.tm_hour < rtc.tm_hour ||
|
||||||
|
+ (t->time.tm_hour == rtc.tm_hour &&
|
||||||
|
+ t->time.tm_min <= rtc.tm_min))))
|
||||||
|
+ alarm_secs += 7 * 86400;
|
||||||
|
+
|
||||||
|
+ rtc_time64_to_tm(alarm_secs, &t->time);
|
||||||
|
|
||||||
|
/* ... and status */
|
||||||
|
t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE);
|
||||||
|
@@ -417,12 +443,20 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||||
|
struct rs5c372 *rs5c = i2c_get_clientdata(client);
|
||||||
|
int status, addr, i;
|
||||||
|
unsigned char buf[3];
|
||||||
|
+ struct rtc_time rtc_tm;
|
||||||
|
+ unsigned long rtc_secs, alarm_secs;
|
||||||
|
|
||||||
|
- /* only handle up to 24 hours in the future, like RTC_ALM_SET */
|
||||||
|
- if (t->time.tm_mday != -1
|
||||||
|
- || t->time.tm_mon != -1
|
||||||
|
- || t->time.tm_year != -1)
|
||||||
|
+ /* chip only can handle alarms up to one week in the future*/
|
||||||
|
+ status = rs5c372_rtc_read_time(dev, &rtc_tm);
|
||||||
|
+ if (status)
|
||||||
|
+ return status;
|
||||||
|
+ rtc_secs = rtc_tm_to_time64(&rtc_tm);
|
||||||
|
+ alarm_secs = rtc_tm_to_time64(&t->time);
|
||||||
|
+ if (alarm_secs >= rtc_secs + 7 * 86400) {
|
||||||
|
+ dev_err(dev, "%s: alarm maximum is one week in the future (%d)\n",
|
||||||
|
+ __func__, status);
|
||||||
|
return -EINVAL;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
/* REVISIT: round up tm_sec */
|
||||||
|
|
||||||
|
@@ -443,7 +477,9 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||||
|
/* set alarm */
|
||||||
|
buf[0] = bin2bcd(t->time.tm_min);
|
||||||
|
buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour);
|
||||||
|
- buf[2] = 0x7f; /* any/all days */
|
||||||
|
+ /* each bit is the day of the week, 0x7f means all days */
|
||||||
|
+ buf[2] = (t->time.tm_wday >= 0 && t->time.tm_wday < 7) ?
|
||||||
|
+ BIT(t->time.tm_wday) : 0x7f;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(buf); i++) {
|
||||||
|
addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);
|
@ -0,0 +1,72 @@
|
|||||||
|
From: Daniel González Cabanelas <dgcbueu@gmail.com>
|
||||||
|
Subject: [PATCH 2/2] rtc: rs5c372: let the alarm to be used as wakeup source
|
||||||
|
|
||||||
|
Currently there is no use for the interrupts on the rs5c372 RTC and the
|
||||||
|
wakealarm isn't enabled. There are some devices like NASes which use this
|
||||||
|
RTC to wake up from the power off state when the INTR pin is activated by
|
||||||
|
the alarm clock.
|
||||||
|
|
||||||
|
Enable the alarm and let to be used as a wakeup source.
|
||||||
|
|
||||||
|
Tested on a Buffalo LS421DE NAS.
|
||||||
|
|
||||||
|
Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/rtc/rtc-rs5c372.c | 16 ++++++++++++++++
|
||||||
|
1 file changed, 16 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
|
||||||
|
index 94b778c6e..76775d66e 100644
|
||||||
|
--- a/drivers/rtc/rtc-rs5c372.c
|
||||||
|
+++ b/drivers/rtc/rtc-rs5c372.c
|
||||||
|
@@ -654,6 +654,7 @@ static int rs5c372_probe(struct i2c_client *client,
|
||||||
|
int err = 0;
|
||||||
|
int smbus_mode = 0;
|
||||||
|
struct rs5c372 *rs5c372;
|
||||||
|
+ bool rs5c372_can_wakeup_device = false;
|
||||||
|
|
||||||
|
dev_dbg(&client->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
@@ -689,6 +690,12 @@ static int rs5c372_probe(struct i2c_client *client,
|
||||||
|
else
|
||||||
|
rs5c372->type = id->driver_data;
|
||||||
|
|
||||||
|
+#ifdef CONFIG_OF
|
||||||
|
+ if(of_property_read_bool(client->dev.of_node,
|
||||||
|
+ "wakeup-source"))
|
||||||
|
+ rs5c372_can_wakeup_device = true;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
/* we read registers 0x0f then 0x00-0x0f; skip the first one */
|
||||||
|
rs5c372->regs = &rs5c372->buf[1];
|
||||||
|
rs5c372->smbus = smbus_mode;
|
||||||
|
@@ -722,6 +729,8 @@ static int rs5c372_probe(struct i2c_client *client,
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ rs5c372->has_irq = 1;
|
||||||
|
+
|
||||||
|
/* if the oscillator lost power and no other software (like
|
||||||
|
* the bootloader) set it up, do it here.
|
||||||
|
*
|
||||||
|
@@ -748,6 +757,10 @@ static int rs5c372_probe(struct i2c_client *client,
|
||||||
|
);
|
||||||
|
|
||||||
|
/* REVISIT use client->irq to register alarm irq ... */
|
||||||
|
+ if (rs5c372_can_wakeup_device) {
|
||||||
|
+ device_init_wakeup(&client->dev, true);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
rs5c372->rtc = devm_rtc_device_register(&client->dev,
|
||||||
|
rs5c372_driver.driver.name,
|
||||||
|
&rs5c372_rtc_ops, THIS_MODULE);
|
||||||
|
@@ -761,6 +774,9 @@ static int rs5c372_probe(struct i2c_client *client,
|
||||||
|
if (err)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
+ /* the rs5c372 alarm only supports a minute accuracy */
|
||||||
|
+ rs5c372->rtc->uie_unsupported = 1;
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit:
|
Loading…
Reference in New Issue
Block a user