[PATCH 1/2] kernel: rtc: rs5c372: fix alarm support

Daniel González Cabanelas dgcbueu at gmail.com
Tue Nov 24 10:06:37 EST 2020


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 at gmail.com>
---
 ...-rs5c372-support_alarms_up_to_1_week.patch | 96 +++++++++++++++++++
 ...he_alarm_to_be_used_as_wakeup_source.patch | 72 ++++++++++++++
 2 files changed, 168 insertions(+)
 create mode 100644 target/linux/generic/pending-5.4/190-rtc-rs5c372-support_alarms_up_to_1_week.patch
 create mode 100644 target/linux/generic/pending-5.4/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch

diff --git a/target/linux/generic/pending-5.4/190-rtc-rs5c372-support_alarms_up_to_1_week.patch b/target/linux/generic/pending-5.4/190-rtc-rs5c372-support_alarms_up_to_1_week.patch
new file mode 100644
index 0000000000..83cab4e6d7
--- /dev/null
+++ b/target/linux/generic/pending-5.4/190-rtc-rs5c372-support_alarms_up_to_1_week.patch
@@ -0,0 +1,96 @@
+From: Daniel González Cabanelas <dgcbueu at 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 at 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);
diff --git a/target/linux/generic/pending-5.4/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch b/target/linux/generic/pending-5.4/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch
new file mode 100644
index 0000000000..d5c8c23f65
--- /dev/null
+++ b/target/linux/generic/pending-5.4/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch
@@ -0,0 +1,72 @@
+From: Daniel González Cabanelas <dgcbueu at 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 at 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:
-- 
2.29.2







More information about the openwrt-devel mailing list