Linux RTC
 help / color / mirror / Atom feed
* [rtc-linux] [PATCH v4 0/2] Add support for cros-ec-rtc driver.
From: Enric Balletbo i Serra @ 2017-11-10 21:55 UTC (permalink / raw)
  To: Lee Jones, bleung
  Cc: Guenter Roeck, Gwendal Grignou, linux-kernel, linux-iio,
	rtc-linux

Dear all,

This is an attempt to revive some patches from that [1] patchset, some
of them are still under discussion but I think there is no reason to not
have the other two in this fourth version to land upstream meanwhile we
discuss about the others.

[1] https://lkml.org/lkml/2017/7/12/182

Changes since v3:
* Rebased an retested with current mainline using a Samsung Chromebook Plus
* Removed from patchset
  * 1/4 mfd: cros_ec: Get rid of cros_ec_check_features from cros_ec_dev.
  * 4/4 mfd: cros_ec: add RTC as mfd subdevice.

Changes since v2:
- Rebase on top of mainline.
- Removed patch 'mfd: cros-ec: Fix host command buffer size' from series
as was already picked.

Changes since v1:
- Removed patch 'iio: cros_ec_sensors: Fix return value to get raw and
calibbias data' from series as was already picked.
- Removed patch 'iio: cros_ec_sensors: Fix return value to get raw and
calibbias data' from series as was already picked.
- Patch 2/5: Acked-by: Jonathan Cameron <***@kernel.org>

Best regards,

 Enric

Stephen Barber (2):
  mfd: cros_ec: Introduce RTC commands and events definitions.
  rtc: cros-ec: add cros-ec-rtc driver.

 drivers/rtc/Kconfig                  |  10 +
 drivers/rtc/Makefile                 |   1 +
 drivers/rtc/rtc-cros-ec.c            | 413 +++++++++++++++++++++++++++++++++++
 include/linux/mfd/cros_ec_commands.h |   8 +
 4 files changed, 432 insertions(+)
 create mode 100644 drivers/rtc/rtc-cros-ec.c

-- 
2.9.3

-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

^ permalink raw reply

* Re: [PATCH 2/2] rtc: at91rm9200: fix reading alarm value
From: Nicolas Ferre @ 2017-11-10 14:55 UTC (permalink / raw)
  To: Alexandre Belloni, linux-rtc; +Cc: linux-arm-kernel, linux-kernel
In-Reply-To: <20171110085931.32347-2-alexandre.belloni@free-electrons.com>

On 10/11/2017 at 09:59, Alexandre Belloni wrote:
> When alarm value is read at boot time, at91_alarm_year is not yet set to
> the proper value so the year is always set to 1900.
> 
> This results in that kind of message at boot:
> rtc rtc0: invalid alarm value: 1900-1-14 2:11:39
> 
> There is no way to recover from that as the alarm is now only read when
> booting.
> 
> Instead, rely on the rtc core to figure out the proper year.
> 
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>

> ---
>  drivers/rtc/rtc-at91rm9200.c | 18 ++++++------------
>  1 file changed, 6 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
> index e84f5ec4faf6..de81ecedd571 100644
> --- a/drivers/rtc/rtc-at91rm9200.c
> +++ b/drivers/rtc/rtc-at91rm9200.c
> @@ -42,8 +42,6 @@
>  #define at91_rtc_write(field, val) \
>  	writel_relaxed((val), at91_rtc_regs + field)
>  
> -#define AT91_RTC_EPOCH		1900UL	/* just like arch/arm/common/rtctime.c */
> -
>  struct at91_rtc_config {
>  	bool use_shadow_imr;
>  };
> @@ -51,7 +49,6 @@ struct at91_rtc_config {
>  static const struct at91_rtc_config *at91_rtc_config;
>  static DECLARE_COMPLETION(at91_rtc_updated);
>  static DECLARE_COMPLETION(at91_rtc_upd_rdy);
> -static unsigned int at91_alarm_year = AT91_RTC_EPOCH;
>  static void __iomem *at91_rtc_regs;
>  static int irq;
>  static DEFINE_SPINLOCK(at91_rtc_lock);
> @@ -131,8 +128,7 @@ static void at91_rtc_decodetime(unsigned int timereg, unsigned int calreg,
>  
>  	/*
>  	 * The Calendar Alarm register does not have a field for
> -	 * the year - so these will return an invalid value.  When an
> -	 * alarm is set, at91_alarm_year will store the current year.
> +	 * the year - so these will return an invalid value.
>  	 */
>  	tm->tm_year  = bcd2bin(date & AT91_RTC_CENT) * 100;	/* century */
>  	tm->tm_year += bcd2bin((date & AT91_RTC_YEAR) >> 8);	/* year */
> @@ -208,14 +204,14 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	struct rtc_time *tm = &alrm->time;
>  
>  	at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm);
> -	tm->tm_year = at91_alarm_year - 1900;
> +	tm->tm_year = -1;
>  
>  	alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM)
>  			? 1 : 0;
>  
> -	dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
> -		1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
> -		tm->tm_hour, tm->tm_min, tm->tm_sec);
> +	dev_dbg(dev, "%s(): %02d-%02d %02d:%02d:%02d %sabled\n", __func__,
> +		tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
> +		alrm->enabled ? "en" : "dis");
>  
>  	return 0;
>  }
> @@ -229,8 +225,6 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  
>  	at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm);
>  
> -	at91_alarm_year = tm.tm_year;
> -
>  	tm.tm_mon = alrm->time.tm_mon;
>  	tm.tm_mday = alrm->time.tm_mday;
>  	tm.tm_hour = alrm->time.tm_hour;
> @@ -254,7 +248,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	}
>  
>  	dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
> -		at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
> +		tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
>  		tm.tm_min, tm.tm_sec);
>  
>  	return 0;
> 


-- 
Nicolas Ferre

^ permalink raw reply

* Re: [PATCH 1/2] rtc: at91rm9200: stop calculating yday in at91_rtc_readalarm
From: Nicolas Ferre @ 2017-11-10 13:41 UTC (permalink / raw)
  To: Alexandre Belloni, linux-rtc; +Cc: linux-arm-kernel, linux-kernel
In-Reply-To: <20171110085931.32347-1-alexandre.belloni@free-electrons.com>

On 10/11/2017 at 09:59, Alexandre Belloni wrote:
> Calculating yday in the read_alarm callback is useless as this value is
> never used later. Also, it was buggy anyway because at the time this is
> done, tm_year is always 0 as the alarm register doesn't hold the year.
> 
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>

> ---
>  drivers/rtc/rtc-at91rm9200.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
> index e221b78b6f10..e84f5ec4faf6 100644
> --- a/drivers/rtc/rtc-at91rm9200.c
> +++ b/drivers/rtc/rtc-at91rm9200.c
> @@ -208,7 +208,6 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	struct rtc_time *tm = &alrm->time;
>  
>  	at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm);
> -	tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
>  	tm->tm_year = at91_alarm_year - 1900;
>  
>  	alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM)
> 


-- 
Nicolas Ferre

^ permalink raw reply

* [PATCH 2/2] rtc: at91rm9200: fix reading alarm value
From: Alexandre Belloni @ 2017-11-10  8:59 UTC (permalink / raw)
  To: linux-rtc
  Cc: Nicolas Ferre, linux-arm-kernel, linux-kernel, Alexandre Belloni
In-Reply-To: <20171110085931.32347-1-alexandre.belloni@free-electrons.com>

When alarm value is read at boot time, at91_alarm_year is not yet set to
the proper value so the year is always set to 1900.

This results in that kind of message at boot:
rtc rtc0: invalid alarm value: 1900-1-14 2:11:39

There is no way to recover from that as the alarm is now only read when
booting.

Instead, rely on the rtc core to figure out the proper year.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/rtc/rtc-at91rm9200.c | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
index e84f5ec4faf6..de81ecedd571 100644
--- a/drivers/rtc/rtc-at91rm9200.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -42,8 +42,6 @@
 #define at91_rtc_write(field, val) \
 	writel_relaxed((val), at91_rtc_regs + field)
 
-#define AT91_RTC_EPOCH		1900UL	/* just like arch/arm/common/rtctime.c */
-
 struct at91_rtc_config {
 	bool use_shadow_imr;
 };
@@ -51,7 +49,6 @@ struct at91_rtc_config {
 static const struct at91_rtc_config *at91_rtc_config;
 static DECLARE_COMPLETION(at91_rtc_updated);
 static DECLARE_COMPLETION(at91_rtc_upd_rdy);
-static unsigned int at91_alarm_year = AT91_RTC_EPOCH;
 static void __iomem *at91_rtc_regs;
 static int irq;
 static DEFINE_SPINLOCK(at91_rtc_lock);
@@ -131,8 +128,7 @@ static void at91_rtc_decodetime(unsigned int timereg, unsigned int calreg,
 
 	/*
 	 * The Calendar Alarm register does not have a field for
-	 * the year - so these will return an invalid value.  When an
-	 * alarm is set, at91_alarm_year will store the current year.
+	 * the year - so these will return an invalid value.
 	 */
 	tm->tm_year  = bcd2bin(date & AT91_RTC_CENT) * 100;	/* century */
 	tm->tm_year += bcd2bin((date & AT91_RTC_YEAR) >> 8);	/* year */
@@ -208,14 +204,14 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	struct rtc_time *tm = &alrm->time;
 
 	at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm);
-	tm->tm_year = at91_alarm_year - 1900;
+	tm->tm_year = -1;
 
 	alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM)
 			? 1 : 0;
 
-	dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
-		1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
-		tm->tm_hour, tm->tm_min, tm->tm_sec);
+	dev_dbg(dev, "%s(): %02d-%02d %02d:%02d:%02d %sabled\n", __func__,
+		tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
+		alrm->enabled ? "en" : "dis");
 
 	return 0;
 }
@@ -229,8 +225,6 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 
 	at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm);
 
-	at91_alarm_year = tm.tm_year;
-
 	tm.tm_mon = alrm->time.tm_mon;
 	tm.tm_mday = alrm->time.tm_mday;
 	tm.tm_hour = alrm->time.tm_hour;
@@ -254,7 +248,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	}
 
 	dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
-		at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
+		tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
 		tm.tm_min, tm.tm_sec);
 
 	return 0;
-- 
2.15.0

^ permalink raw reply related

* [PATCH 1/2] rtc: at91rm9200: stop calculating yday in at91_rtc_readalarm
From: Alexandre Belloni @ 2017-11-10  8:59 UTC (permalink / raw)
  To: linux-rtc
  Cc: Nicolas Ferre, linux-arm-kernel, linux-kernel, Alexandre Belloni

Calculating yday in the read_alarm callback is useless as this value is
never used later. Also, it was buggy anyway because at the time this is
done, tm_year is always 0 as the alarm register doesn't hold the year.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/rtc/rtc-at91rm9200.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
index e221b78b6f10..e84f5ec4faf6 100644
--- a/drivers/rtc/rtc-at91rm9200.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -208,7 +208,6 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	struct rtc_time *tm = &alrm->time;
 
 	at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm);
-	tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
 	tm->tm_year = at91_alarm_year - 1900;
 
 	alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM)
-- 
2.15.0

^ permalink raw reply related

* Re: [PATCH] rtc: Use time64_t variables to set time/alarm from sysfs
From: Alexandre Belloni @ 2017-11-10  8:58 UTC (permalink / raw)
  To: Baolin Wang; +Cc: a.zummo, linux-rtc, linux-kernel, arnd, broonie
In-Reply-To: <672d468478f3f67ef22e027460c59918dc581818.1510211132.git.baolin.wang@linaro.org>

On 09/11/2017 at 15:09:20 +0800, Baolin Wang wrote:
> Use time64_t variables and related APIs for sysfs interfaces to
> support setting time or alarm after the year 2038 on 32-bit system.
> 
> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
> ---
>  drivers/rtc/rtc-sysfs.c |   25 +++++++++++++------------
>  1 file changed, 13 insertions(+), 12 deletions(-)
> 
Applied, thanks.

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH] rtc: Use time64_t variables to set time/alarm from sysfs
From: Arnd Bergmann @ 2017-11-09 10:38 UTC (permalink / raw)
  To: Baolin Wang
  Cc: Alessandro Zummo, Alexandre Belloni, linux-rtc,
	Linux Kernel Mailing List, Mark Brown
In-Reply-To: <672d468478f3f67ef22e027460c59918dc581818.1510211132.git.baolin.wang@linaro.org>

On Thu, Nov 9, 2017 at 8:09 AM, Baolin Wang <baolin.wang@linaro.org> wrote:
> Use time64_t variables and related APIs for sysfs interfaces to
> support setting time or alarm after the year 2038 on 32-bit system.
>
> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>


Looks good,

Reviewed-by: Arnd Bergmann <arnd@arndb.de>

^ permalink raw reply

* RE: DryIce , RTC not working on imx53.
From: Patrick Brünn @ 2017-11-09  9:59 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Vellemans, Noel, linux-arm-kernel@lists.infradead.org,
	linux-rtc@vger.kernel.org
In-Reply-To: <20171109030327.anzo47xayxckn47r@piout.net>


>From: Alexandre Belloni [mailto:alexandre.belloni@free-electrons.com]
>Sent: Donnerstag, 9. November 2017 04:03
>
>hwclock times out because the alarm is not working properly. Can you
>check whether rtctest is working?
>
You mean "tools/testing/selftests/timers/rtctest", right?

I just run it and it's stuck at "Counting 5 update (1/sec) interrupts from reading /dev/rtc0:"

Kernel log shows a new message for each try I restart rtctest.
[ 1032.349562] imxdi_rtc 53fa4000.srtc: Write-wait timeout val = 0x5a042418 reg = 0x00000008"


Regards, Patrick

Beckhoff Automation GmbH & Co. KG | Managing Director: Dipl. Phys. Hans Beckhoff
Registered office: Verl, Germany | Register court: Guetersloh HRA 7075

^ permalink raw reply

* [PATCH] rtc: Use time64_t variables to set time/alarm from sysfs
From: Baolin Wang @ 2017-11-09  7:09 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni
  Cc: linux-rtc, linux-kernel, arnd, broonie, baolin.wang

Use time64_t variables and related APIs for sysfs interfaces to
support setting time or alarm after the year 2038 on 32-bit system.

Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
 drivers/rtc/rtc-sysfs.c |   25 +++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index e364550..92ff2ed 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -72,9 +72,10 @@
 
 	retval = rtc_read_time(to_rtc_device(dev), &tm);
 	if (retval == 0) {
-		unsigned long time;
-		rtc_tm_to_time(&tm, &time);
-		retval = sprintf(buf, "%lu\n", time);
+		time64_t time;
+
+		time = rtc_tm_to_time64(&tm);
+		retval = sprintf(buf, "%lld\n", time);
 	}
 
 	return retval;
@@ -132,7 +133,7 @@
 wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	ssize_t retval;
-	unsigned long alarm;
+	time64_t alarm;
 	struct rtc_wkalrm alm;
 
 	/* Don't show disabled alarms.  For uniformity, RTC alarms are
@@ -145,8 +146,8 @@
 	 */
 	retval = rtc_read_alarm(to_rtc_device(dev), &alm);
 	if (retval == 0 && alm.enabled) {
-		rtc_tm_to_time(&alm.time, &alarm);
-		retval = sprintf(buf, "%lu\n", alarm);
+		alarm = rtc_tm_to_time64(&alm.time);
+		retval = sprintf(buf, "%lld\n", alarm);
 	}
 
 	return retval;
@@ -157,8 +158,8 @@
 		const char *buf, size_t n)
 {
 	ssize_t retval;
-	unsigned long now, alarm;
-	unsigned long push = 0;
+	time64_t now, alarm;
+	time64_t push = 0;
 	struct rtc_wkalrm alm;
 	struct rtc_device *rtc = to_rtc_device(dev);
 	const char *buf_ptr;
@@ -170,7 +171,7 @@
 	retval = rtc_read_time(rtc, &alm.time);
 	if (retval < 0)
 		return retval;
-	rtc_tm_to_time(&alm.time, &now);
+	now = rtc_tm_to_time64(&alm.time);
 
 	buf_ptr = buf;
 	if (*buf_ptr == '+') {
@@ -181,7 +182,7 @@
 		} else
 			adjust = 1;
 	}
-	retval = kstrtoul(buf_ptr, 0, &alarm);
+	retval = kstrtos64(buf_ptr, 0, &alarm);
 	if (retval)
 		return retval;
 	if (adjust) {
@@ -197,7 +198,7 @@
 			return retval;
 		if (alm.enabled) {
 			if (push) {
-				rtc_tm_to_time(&alm.time, &push);
+				push = rtc_tm_to_time64(&alm.time);
 				alarm += push;
 			} else
 				return -EBUSY;
@@ -212,7 +213,7 @@
 		 */
 		alarm = now + 300;
 	}
-	rtc_time_to_tm(alarm, &alm.time);
+	rtc_time64_to_tm(alarm, &alm.time);
 
 	retval = rtc_set_alarm(rtc, &alm);
 	return (retval < 0) ? retval : n;
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v3 1/2] dt-bindings: rtc: Add Spreadtrum SC27xx RTC documentation
From: Baolin Wang @ 2017-11-09  3:34 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, mark.rutland
  Cc: devicetree, linux-kernel, linux-rtc, broonie, baolin.wang,
	baolin.wang

This patch adds the binding documentation for Spreadtrum SC27xx series
RTC device.

Signed-off-by: Baolin Wang <baolin.wang@spreadtrum.com>
---
Changes since v2:
 - No updates.

Changes since v1:
 - Use a specific chip name as compatible string.
 - Remove useless alias.
 - Add the parent MFD node.
---
 .../devicetree/bindings/rtc/sprd,sc27xx-rtc.txt    |   27 ++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt

diff --git a/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt b/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt
new file mode 100644
index 0000000..7c170da
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt
@@ -0,0 +1,27 @@
+Spreadtrum SC27xx Real Time Clock
+
+Required properties:
+- compatible: should be "sprd,sc2731-rtc".
+- reg: address offset of rtc register.
+- interrupt-parent: phandle for the interrupt controller.
+- interrupts: rtc alarm interrupt.
+
+Example:
+
+	sc2731_pmic: pmic@0 {
+		compatible = "sprd,sc2731";
+		reg = <0>;
+		spi-max-frequency = <26000000>;
+		interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		rtc@280 {
+			compatible = "sprd,sc2731-rtc";
+			reg = <0x280>;
+			interrupt-parent = <&sc2731_pmic>;
+			interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
+		};
+	};
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v3 2/2] rtc: sprd: Add Spreadtrum RTC driver
From: Baolin Wang @ 2017-11-09  3:34 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, mark.rutland
  Cc: devicetree, linux-kernel, linux-rtc, broonie, baolin.wang,
	baolin.wang
In-Reply-To: <c65b61e5e37cb43214973cb3e479ce06c4a73395.1510198341.git.baolin.wang@spreadtrum.com>

This patch adds the Spreadtrum RTC driver, which embedded in the
Spreadtrum SC27xx series PMICs.

Signed-off-by: Baolin Wang <baolin.wang@spreadtrum.com>
---
Changes since v2:
 - Add checking if RTC values are valid.

Changes since v1:
 - Change file name.
 - Should depend on the MFD parent.
 - Add more help documentation.
 - Remove regmap wrapper functions.
 - Modify the mothod of setting alarm.
 - Remove set_mmss64() method.
 - Remove checking RTC power-down.
 - Optimize lock/unlock alarm.
---
 drivers/rtc/Kconfig      |   11 +
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-sc27xx.c |  662 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 674 insertions(+)
 create mode 100644 drivers/rtc/rtc-sc27xx.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e0e58f3..8c240e7 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1174,6 +1174,17 @@ config RTC_DRV_WM8350
 	  This driver can also be built as a module. If so, the module
 	  will be called "rtc-wm8350".
 
+config RTC_DRV_SC27XX
+	tristate "Spreadtrum SC27xx RTC"
+	depends on MFD_SC27XX_PMIC || COMPILE_TEST
+	help
+	  If you say Y here you will get support for the RTC subsystem
+	  of the Spreadtrum SC27xx series PMICs. The SC27xx series PMICs
+	  includes the SC2720, SC2721, SC2723, SC2730 and SC2731 chips.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-sc27xx.
+
 config RTC_DRV_SPEAR
 	tristate "SPEAR ST RTC"
 	depends on PLAT_SPEAR || COMPILE_TEST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 7230014..f9b857f 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -143,6 +143,7 @@ obj-$(CONFIG_RTC_DRV_S35390A)	+= rtc-s35390a.o
 obj-$(CONFIG_RTC_DRV_S3C)	+= rtc-s3c.o
 obj-$(CONFIG_RTC_DRV_S5M)	+= rtc-s5m.o
 obj-$(CONFIG_RTC_DRV_SA1100)	+= rtc-sa1100.o
+obj-$(CONFIG_RTC_DRV_SC27XX)	+= rtc-sc27xx.o
 obj-$(CONFIG_RTC_DRV_SH)	+= rtc-sh.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
 obj-$(CONFIG_RTC_DRV_SNVS)	+= rtc-snvs.o
diff --git a/drivers/rtc/rtc-sc27xx.c b/drivers/rtc/rtc-sc27xx.c
new file mode 100644
index 0000000..d544d52
--- /dev/null
+++ b/drivers/rtc/rtc-sc27xx.c
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) 2017 Spreadtrum Communications Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+
+#define SPRD_RTC_SEC_CNT_VALUE		0x0
+#define SPRD_RTC_MIN_CNT_VALUE		0x4
+#define SPRD_RTC_HOUR_CNT_VALUE		0x8
+#define SPRD_RTC_DAY_CNT_VALUE		0xc
+#define SPRD_RTC_SEC_CNT_UPD		0x10
+#define SPRD_RTC_MIN_CNT_UPD		0x14
+#define SPRD_RTC_HOUR_CNT_UPD		0x18
+#define SPRD_RTC_DAY_CNT_UPD		0x1c
+#define SPRD_RTC_SEC_ALM_UPD		0x20
+#define SPRD_RTC_MIN_ALM_UPD		0x24
+#define SPRD_RTC_HOUR_ALM_UPD		0x28
+#define SPRD_RTC_DAY_ALM_UPD		0x2c
+#define SPRD_RTC_INT_EN			0x30
+#define SPRD_RTC_INT_RAW_STS		0x34
+#define SPRD_RTC_INT_CLR		0x38
+#define SPRD_RTC_INT_MASK_STS		0x3C
+#define SPRD_RTC_SEC_ALM_VALUE		0x40
+#define SPRD_RTC_MIN_ALM_VALUE		0x44
+#define SPRD_RTC_HOUR_ALM_VALUE		0x48
+#define SPRD_RTC_DAY_ALM_VALUE		0x4c
+#define SPRD_RTC_SPG_VALUE		0x50
+#define SPRD_RTC_SPG_UPD		0x54
+#define SPRD_RTC_SEC_AUXALM_UPD		0x60
+#define SPRD_RTC_MIN_AUXALM_UPD		0x64
+#define SPRD_RTC_HOUR_AUXALM_UPD	0x68
+#define SPRD_RTC_DAY_AUXALM_UPD		0x6c
+
+/* BIT & MASK definition for SPRD_RTC_INT_* registers */
+#define SPRD_RTC_SEC_EN			BIT(0)
+#define SPRD_RTC_MIN_EN			BIT(1)
+#define SPRD_RTC_HOUR_EN		BIT(2)
+#define SPRD_RTC_DAY_EN			BIT(3)
+#define SPRD_RTC_ALARM_EN		BIT(4)
+#define SPRD_RTC_HRS_FORMAT_EN		BIT(5)
+#define SPRD_RTC_AUXALM_EN		BIT(6)
+#define SPRD_RTC_SPG_UPD_EN		BIT(7)
+#define SPRD_RTC_SEC_UPD_EN		BIT(8)
+#define SPRD_RTC_MIN_UPD_EN		BIT(9)
+#define SPRD_RTC_HOUR_UPD_EN		BIT(10)
+#define SPRD_RTC_DAY_UPD_EN		BIT(11)
+#define SPRD_RTC_ALMSEC_UPD_EN		BIT(12)
+#define SPRD_RTC_ALMMIN_UPD_EN		BIT(13)
+#define SPRD_RTC_ALMHOUR_UPD_EN		BIT(14)
+#define SPRD_RTC_ALMDAY_UPD_EN		BIT(15)
+#define SPRD_RTC_INT_MASK		GENMASK(15, 0)
+
+#define SPRD_RTC_TIME_INT_MASK				\
+	(SPRD_RTC_SEC_UPD_EN | SPRD_RTC_MIN_UPD_EN |	\
+	 SPRD_RTC_HOUR_UPD_EN | SPRD_RTC_DAY_UPD_EN)
+
+#define SPRD_RTC_ALMTIME_INT_MASK				\
+	(SPRD_RTC_ALMSEC_UPD_EN | SPRD_RTC_ALMMIN_UPD_EN |	\
+	 SPRD_RTC_ALMHOUR_UPD_EN | SPRD_RTC_ALMDAY_UPD_EN)
+
+#define SPRD_RTC_ALM_INT_MASK			\
+	(SPRD_RTC_SEC_EN | SPRD_RTC_MIN_EN |	\
+	 SPRD_RTC_HOUR_EN | SPRD_RTC_DAY_EN |	\
+	 SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN)
+
+/* second/minute/hour/day values mask definition */
+#define SPRD_RTC_SEC_MASK		GENMASK(5, 0)
+#define SPRD_RTC_MIN_MASK		GENMASK(5, 0)
+#define SPRD_RTC_HOUR_MASK		GENMASK(4, 0)
+#define SPRD_RTC_DAY_MASK		GENMASK(15, 0)
+
+/* alarm lock definition for SPRD_RTC_SPG_UPD register */
+#define SPRD_RTC_ALMLOCK_MASK		GENMASK(7, 0)
+#define SPRD_RTC_ALM_UNLOCK		0xa5
+#define SPRD_RTC_ALM_LOCK		(~SPRD_RTC_ALM_UNLOCK &	\
+					 SPRD_RTC_ALMLOCK_MASK)
+
+/* SPG values definition for SPRD_RTC_SPG_UPD register */
+#define SPRD_RTC_POWEROFF_ALM_FLAG	BIT(8)
+#define SPRD_RTC_POWER_RESET_FLAG	BIT(9)
+
+/* timeout of synchronizing time and alarm registers (us) */
+#define SPRD_RTC_POLL_TIMEOUT		200000
+#define SPRD_RTC_POLL_DELAY_US		20000
+
+struct sprd_rtc {
+	struct rtc_device	*rtc;
+	struct regmap		*regmap;
+	struct device		*dev;
+	u32			base;
+	int			irq;
+	bool			valid;
+};
+
+/*
+ * The Spreadtrum RTC controller has 3 groups registers, including time, normal
+ * alarm and auxiliary alarm. The time group registers are used to set RTC time,
+ * the normal alarm registers are used to set normal alarm, and the auxiliary
+ * alarm registers are used to set auxiliary alarm. Both alarm event and
+ * auxiliary alarm event can wake up system from deep sleep, but only alarm
+ * event can power up system from power down status.
+ */
+enum sprd_rtc_reg_types {
+	SPRD_RTC_TIME,
+	SPRD_RTC_ALARM,
+	SPRD_RTC_AUX_ALARM,
+};
+
+static int sprd_rtc_clear_alarm_ints(struct sprd_rtc *rtc)
+{
+	return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			    SPRD_RTC_ALM_INT_MASK);
+}
+
+static int sprd_rtc_disable_ints(struct sprd_rtc *rtc)
+{
+	int ret;
+
+	ret = regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN,
+				 SPRD_RTC_INT_MASK, 0);
+	if (ret)
+		return ret;
+
+	return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			    SPRD_RTC_INT_MASK);
+}
+
+static int sprd_rtc_lock_alarm(struct sprd_rtc *rtc, bool lock)
+{
+	int ret;
+	u32 val;
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val);
+	if (ret)
+		return ret;
+
+	val &= ~(SPRD_RTC_ALMLOCK_MASK | SPRD_RTC_POWEROFF_ALM_FLAG);
+	if (lock)
+		val |= SPRD_RTC_ALM_LOCK;
+	else
+		val |= SPRD_RTC_ALM_UNLOCK | SPRD_RTC_POWEROFF_ALM_FLAG;
+
+	ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_SPG_UPD, val);
+	if (ret)
+		return ret;
+
+	/* wait until the SPG value is updated successfully */
+	ret = regmap_read_poll_timeout(rtc->regmap,
+				       rtc->base + SPRD_RTC_INT_RAW_STS, val,
+				       (val & SPRD_RTC_SPG_UPD_EN),
+				       SPRD_RTC_POLL_DELAY_US,
+				       SPRD_RTC_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(rtc->dev, "failed to update SPG value:%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int sprd_rtc_get_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type,
+			     time64_t *secs)
+{
+	u32 sec_reg, min_reg, hour_reg, day_reg;
+	u32 val, sec, min, hour, day;
+	int ret;
+
+	switch (type) {
+	case SPRD_RTC_TIME:
+		sec_reg = SPRD_RTC_SEC_CNT_VALUE;
+		min_reg = SPRD_RTC_MIN_CNT_VALUE;
+		hour_reg = SPRD_RTC_HOUR_CNT_VALUE;
+		day_reg = SPRD_RTC_DAY_CNT_VALUE;
+		break;
+	case SPRD_RTC_ALARM:
+		sec_reg = SPRD_RTC_SEC_ALM_VALUE;
+		min_reg = SPRD_RTC_MIN_ALM_VALUE;
+		hour_reg = SPRD_RTC_HOUR_ALM_VALUE;
+		day_reg = SPRD_RTC_DAY_ALM_VALUE;
+		break;
+	case SPRD_RTC_AUX_ALARM:
+		sec_reg = SPRD_RTC_SEC_AUXALM_UPD;
+		min_reg = SPRD_RTC_MIN_AUXALM_UPD;
+		hour_reg = SPRD_RTC_HOUR_AUXALM_UPD;
+		day_reg = SPRD_RTC_DAY_AUXALM_UPD;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_read(rtc->regmap, rtc->base + sec_reg, &val);
+	if (ret)
+		return ret;
+
+	sec = val & SPRD_RTC_SEC_MASK;
+
+	ret = regmap_read(rtc->regmap, rtc->base + min_reg, &val);
+	if (ret)
+		return ret;
+
+	min = val & SPRD_RTC_MIN_MASK;
+
+	ret = regmap_read(rtc->regmap, rtc->base + hour_reg, &val);
+	if (ret)
+		return ret;
+
+	hour = val & SPRD_RTC_HOUR_MASK;
+
+	ret = regmap_read(rtc->regmap, rtc->base + day_reg, &val);
+	if (ret)
+		return ret;
+
+	day = val & SPRD_RTC_DAY_MASK;
+	*secs = (((time64_t)(day * 24) + hour) * 60 + min) * 60 + sec;
+	return 0;
+}
+
+static int sprd_rtc_set_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type,
+			     time64_t secs)
+{
+	u32 sec_reg, min_reg, hour_reg, day_reg, sts_mask;
+	u32 sec, min, hour, day, val;
+	int ret, rem;
+
+	/* convert seconds to RTC time format */
+	day = div_s64_rem(secs, 86400, &rem);
+	hour = rem / 3600;
+	rem -= hour * 3600;
+	min = rem / 60;
+	sec = rem - min * 60;
+
+	switch (type) {
+	case SPRD_RTC_TIME:
+		sec_reg = SPRD_RTC_SEC_CNT_UPD;
+		min_reg = SPRD_RTC_MIN_CNT_UPD;
+		hour_reg = SPRD_RTC_HOUR_CNT_UPD;
+		day_reg = SPRD_RTC_DAY_CNT_UPD;
+		sts_mask = SPRD_RTC_TIME_INT_MASK;
+		break;
+	case SPRD_RTC_ALARM:
+		sec_reg = SPRD_RTC_SEC_ALM_UPD;
+		min_reg = SPRD_RTC_MIN_ALM_UPD;
+		hour_reg = SPRD_RTC_HOUR_ALM_UPD;
+		day_reg = SPRD_RTC_DAY_ALM_UPD;
+		sts_mask = SPRD_RTC_ALMTIME_INT_MASK;
+		break;
+	case SPRD_RTC_AUX_ALARM:
+		sec_reg = SPRD_RTC_SEC_AUXALM_UPD;
+		min_reg = SPRD_RTC_MIN_AUXALM_UPD;
+		hour_reg = SPRD_RTC_HOUR_AUXALM_UPD;
+		day_reg = SPRD_RTC_DAY_AUXALM_UPD;
+		sts_mask = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_write(rtc->regmap, rtc->base + sec_reg, sec);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(rtc->regmap, rtc->base + min_reg, min);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(rtc->regmap, rtc->base + hour_reg, hour);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(rtc->regmap, rtc->base + day_reg, day);
+	if (ret)
+		return ret;
+
+	if (type == SPRD_RTC_AUX_ALARM)
+		return 0;
+
+	/*
+	 * Since the time and normal alarm registers are put in always-power-on
+	 * region supplied by VDDRTC, then these registers changing time will
+	 * be very long, about 125ms. Thus here we should wait until all
+	 * values are updated successfully.
+	 */
+	ret = regmap_read_poll_timeout(rtc->regmap,
+				       rtc->base + SPRD_RTC_INT_RAW_STS, val,
+				       ((val & sts_mask) == sts_mask),
+				       SPRD_RTC_POLL_DELAY_US,
+				       SPRD_RTC_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(rtc->dev, "set time/alarm values timeout\n");
+		return ret;
+	}
+
+	return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			    sts_mask);
+}
+
+static int sprd_rtc_read_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs;
+	u32 val;
+	int ret;
+
+	ret = sprd_rtc_get_secs(rtc, SPRD_RTC_AUX_ALARM, &secs);
+	if (ret)
+		return ret;
+
+	rtc_time64_to_tm(secs, &alrm->time);
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val);
+	if (ret)
+		return ret;
+
+	alrm->enabled = !!(val & SPRD_RTC_AUXALM_EN);
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val);
+	if (ret)
+		return ret;
+
+	alrm->pending = !!(val & SPRD_RTC_AUXALM_EN);
+	return 0;
+}
+
+static int sprd_rtc_set_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs = rtc_tm_to_time64(&alrm->time);
+	int ret;
+
+	/* clear the auxiliary alarm interrupt status */
+	ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			   SPRD_RTC_AUXALM_EN);
+	if (ret)
+		return ret;
+
+	ret = sprd_rtc_set_secs(rtc, SPRD_RTC_AUX_ALARM, secs);
+	if (ret)
+		return ret;
+
+	if (alrm->enabled) {
+		ret = regmap_update_bits(rtc->regmap,
+					 rtc->base + SPRD_RTC_INT_EN,
+					 SPRD_RTC_AUXALM_EN,
+					 SPRD_RTC_AUXALM_EN);
+	} else {
+		ret = regmap_update_bits(rtc->regmap,
+					 rtc->base + SPRD_RTC_INT_EN,
+					 SPRD_RTC_AUXALM_EN, 0);
+	}
+
+	return ret;
+}
+
+static int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs;
+	int ret;
+
+	if (!rtc->valid) {
+		dev_warn(dev, "RTC values are invalid\n");
+		return -EINVAL;
+	}
+
+	ret = sprd_rtc_get_secs(rtc, SPRD_RTC_TIME, &secs);
+	if (ret)
+		return ret;
+
+	rtc_time64_to_tm(secs, tm);
+	return rtc_valid_tm(tm);
+}
+
+static int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs = rtc_tm_to_time64(tm);
+	u32 val;
+	int ret;
+
+	ret = sprd_rtc_set_secs(rtc, SPRD_RTC_TIME, secs);
+	if (ret)
+		return ret;
+
+	if (!rtc->valid) {
+		/*
+		 * Set SPRD_RTC_POWER_RESET_FLAG to indicate now RTC has valid
+		 * time values.
+		 */
+		ret = regmap_update_bits(rtc->regmap,
+					 rtc->base + SPRD_RTC_SPG_UPD,
+					 SPRD_RTC_POWER_RESET_FLAG,
+					 SPRD_RTC_POWER_RESET_FLAG);
+		if (ret)
+			return ret;
+
+		ret = regmap_read_poll_timeout(rtc->regmap,
+					       rtc->base + SPRD_RTC_INT_RAW_STS,
+					       val, (val & SPRD_RTC_SPG_UPD_EN),
+					       SPRD_RTC_POLL_DELAY_US,
+					       SPRD_RTC_POLL_TIMEOUT);
+		if (ret) {
+			dev_err(rtc->dev, "failed to update SPG value:%d\n",
+				ret);
+			return ret;
+		}
+
+		rtc->valid = true;
+	}
+
+	return 0;
+}
+
+static int sprd_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs;
+	int ret;
+	u32 val;
+
+	/*
+	 * If aie_timer is enabled, we should get the normal alarm time.
+	 * Otherwise we should get auxiliary alarm time.
+	 */
+	if (rtc->rtc && rtc->rtc->aie_timer.enabled == 0)
+		return sprd_rtc_read_aux_alarm(dev, alrm);
+
+	ret = sprd_rtc_get_secs(rtc, SPRD_RTC_ALARM, &secs);
+	if (ret)
+		return ret;
+
+	rtc_time64_to_tm(secs, &alrm->time);
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val);
+	if (ret)
+		return ret;
+
+	alrm->enabled = !!(val & SPRD_RTC_ALARM_EN);
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val);
+	if (ret)
+		return ret;
+
+	alrm->pending = !!(val & SPRD_RTC_ALARM_EN);
+	return 0;
+}
+
+static int sprd_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs = rtc_tm_to_time64(&alrm->time);
+	struct rtc_time aie_time =
+		rtc_ktime_to_tm(rtc->rtc->aie_timer.node.expires);
+	int ret;
+
+	/*
+	 * We have 2 groups alarms: normal alarm and auxiliary alarm. Since
+	 * both normal alarm event and auxiliary alarm event can wake up system
+	 * from deep sleep, but only alarm event can power up system from power
+	 * down status. Moreover we do not need to poll about 125ms when
+	 * updating auxiliary alarm registers. Thus we usually set auxiliary
+	 * alarm when wake up system from deep sleep, and for other scenarios,
+	 * we should set normal alarm with polling status.
+	 *
+	 * So here we check if the alarm time is set by aie_timer, if yes, we
+	 * should set normal alarm, if not, we should set auxiliary alarm which
+	 * means it is just a wake event.
+	 */
+	if (!rtc->rtc->aie_timer.enabled || rtc_tm_sub(&aie_time, &alrm->time))
+		return sprd_rtc_set_aux_alarm(dev, alrm);
+
+	/* clear the alarm interrupt status firstly */
+	ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			   SPRD_RTC_ALARM_EN);
+	if (ret)
+		return ret;
+
+	ret = sprd_rtc_set_secs(rtc, SPRD_RTC_ALARM, secs);
+	if (ret)
+		return ret;
+
+	if (alrm->enabled) {
+		ret = regmap_update_bits(rtc->regmap,
+					 rtc->base + SPRD_RTC_INT_EN,
+					 SPRD_RTC_ALARM_EN,
+					 SPRD_RTC_ALARM_EN);
+		if (ret)
+			return ret;
+
+		/* unlock the alarm to enable the alarm function. */
+		ret = sprd_rtc_lock_alarm(rtc, false);
+	} else {
+		regmap_update_bits(rtc->regmap,
+				   rtc->base + SPRD_RTC_INT_EN,
+				   SPRD_RTC_ALARM_EN, 0);
+
+		/*
+		 * Lock the alarm function in case fake alarm event will power
+		 * up systems.
+		 */
+		ret = sprd_rtc_lock_alarm(rtc, true);
+	}
+
+	return ret;
+}
+
+static int sprd_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	int ret;
+
+	if (enabled) {
+		ret = regmap_update_bits(rtc->regmap,
+					 rtc->base + SPRD_RTC_INT_EN,
+					 SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN,
+					 SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN);
+		if (ret)
+			return ret;
+
+		ret = sprd_rtc_lock_alarm(rtc, false);
+	} else {
+		regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN,
+				   SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, 0);
+
+		ret = sprd_rtc_lock_alarm(rtc, true);
+	}
+
+	return ret;
+}
+
+static const struct rtc_class_ops sprd_rtc_ops = {
+	.read_time = sprd_rtc_read_time,
+	.set_time = sprd_rtc_set_time,
+	.read_alarm = sprd_rtc_read_alarm,
+	.set_alarm = sprd_rtc_set_alarm,
+	.alarm_irq_enable = sprd_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t sprd_rtc_handler(int irq, void *dev_id)
+{
+	struct sprd_rtc *rtc = dev_id;
+	int ret;
+
+	ret = sprd_rtc_clear_alarm_ints(rtc);
+	if (ret)
+		return IRQ_RETVAL(ret);
+
+	rtc_update_irq(rtc->rtc, 1, RTC_AF | RTC_IRQF);
+	return IRQ_HANDLED;
+}
+
+static int sprd_rtc_check_power_down(struct sprd_rtc *rtc)
+{
+	u32 val;
+	int ret;
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val);
+	if (ret)
+		return ret;
+
+	/*
+	 * If the SPRD_RTC_POWER_RESET_FLAG was not set, which means the RTC has
+	 * been powered down, so the RTC time values are invalid.
+	 */
+	rtc->valid = (val & SPRD_RTC_POWER_RESET_FLAG) ? true : false;
+	return 0;
+}
+
+static int sprd_rtc_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct sprd_rtc *rtc;
+	int ret;
+
+	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+	if (!rtc)
+		return -ENOMEM;
+
+	rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!rtc->regmap)
+		return -ENODEV;
+
+	ret = of_property_read_u32(node, "reg", &rtc->base);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to get RTC base address\n");
+		return ret;
+	}
+
+	rtc->irq = platform_get_irq(pdev, 0);
+	if (rtc->irq < 0) {
+		dev_err(&pdev->dev, "failed to get RTC irq number\n");
+		return rtc->irq;
+	}
+
+	rtc->dev = &pdev->dev;
+	platform_set_drvdata(pdev, rtc);
+
+	/* clear all RTC interrupts and disable all RTC interrupts */
+	ret = sprd_rtc_disable_ints(rtc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to disable RTC interrupts\n");
+		return ret;
+	}
+
+	/* check if RTC time values are valid */
+	ret = sprd_rtc_check_power_down(rtc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to check RTC time values\n");
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
+					sprd_rtc_handler,
+					IRQF_ONESHOT | IRQF_EARLY_RESUME,
+					pdev->name, rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to request RTC irq\n");
+		return ret;
+	}
+
+	rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+					    &sprd_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc->rtc))
+		return PTR_ERR(rtc->rtc);
+
+	device_init_wakeup(&pdev->dev, 1);
+	return 0;
+}
+
+static int sprd_rtc_remove(struct platform_device *pdev)
+{
+	device_init_wakeup(&pdev->dev, 0);
+	return 0;
+}
+
+static const struct of_device_id sprd_rtc_of_match[] = {
+	{ .compatible = "sprd,sc2731-rtc", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sprd_rtc_of_match);
+
+static struct platform_driver sprd_rtc_driver = {
+	.driver = {
+		.name = "sprd-rtc",
+		.of_match_table = sprd_rtc_of_match,
+	},
+	.probe	= sprd_rtc_probe,
+	.remove = sprd_rtc_remove,
+};
+module_platform_driver(sprd_rtc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Spreadtrum RTC Device Driver");
+MODULE_AUTHOR("Baolin Wang <baolin.wang@spreadtrum.com>");
-- 
1.7.9.5

^ permalink raw reply related

* Re: DryIce , RTC not working on imx53.
From: Alexandre Belloni @ 2017-11-09  3:03 UTC (permalink / raw)
  To: Patrick Brünn
  Cc: Vellemans, Noel, linux-arm-kernel@lists.infradead.org,
	linux-rtc@vger.kernel.org
In-Reply-To: <3BB206AB2B1BD448954845CE6FF69A8E01B8E754AA@NT-Mail07.beckhoff.com>

On 12/10/2017 at 07:50:41 +0000, Patrick Brünn wrote:
> I am seeing a lot of interrupts with kernel 4.14-rc4 (jessie and stretch), but they seem to be unhandled:
> root@CX9020:~# uname -a
> Linux CX9020 4.14.0-rc4+ #151 PREEMPT Wed Oct 11 10:40:34 CEST 2017 armv7l GNU/Linux
> root@CX9020:~# hwclock -D -r
> hwclock from util-linux 2.29.2
> Using the /dev interface to the clock.
> Last drift adjustment done at 1490885082 seconds after 1969
> Last calibration done at 1490885082 seconds after 1969
> Hardware clock is on UTC time
> Assuming hardware clock is kept in UTC time.
> Waiting for clock tick...
> [   26.795437] irq 40: nobody cared (try booting with the "irqpoll" option)
> [   26.802696] handlers:
> [   26.805031] [<c06029e8>] dryice_irq
> [   26.808584] Disabling IRQ #40
> select() to /dev/rtc to wait for clock tick timed out...synchronization failed
> root@CX9020:~# cat /proc/interrupts
>            CPU0
>  17:       4276      tzic   1 Edge      mmc0
>  18:         52      tzic   2 Edge      mmc1
>  22:          0      tzic   6 Edge      sdma
>  30:        176      tzic  14 Edge      53f80200.usb
>  40:     100000      tzic  24 Edge      53fa4000.srtc
>  48:        268      tzic  32 Edge      53fc0000.serial
>  55:       2288      tzic  39 Edge      i.MX Timer Tick
>  74:          0      tzic  58 Edge      53f98000.wdog
>  79:        278      tzic  63 Edge      63fc4000.i2c
>  93:          0      tzic  77 Edge      arm-pmu
> 103:        662      tzic  87 Edge      63fec000.ethernet
> 145:          0  gpio-mxc   1 Edge      50004000.esdhc cd
> 148:          0  gpio-mxc   4 Edge      50008000.esdhc cd
> 368:        674       IPU  23 Edge      imx_drm
> 369:          0       IPU  28 Edge      imx_drm
> Err:          0
> 
> I added some tracing to dryice_irq() and saw that most of the time (if not all the time) dsr == DSR_MCO /* monotonic clock overflow */ with dier vary between 0x110, 0x10 and even 0x0.
> I don't know what's the right thing to do, to recover from DSR_MCO. " return IRQ_HANDLED" will stop the nobody cared message but hwclock still times out.
> 

hwclock times out because the alarm is not working properly. Can you
check whether rtctest is working?


-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH v2 2/2] rtc: sprd: Add Spreadtrum RTC driver
From: Baolin Wang @ 2017-11-09  1:53 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Baolin Wang, Alessandro Zummo, Rob Herring, Mark Rutland,
	devicetree, LKML, linux-rtc, Mark Brown
In-Reply-To: <20171109012653.32zkqyh5p4jqhftr@piout.net>

Hi Alexandre,

On 9 November 2017 at 09:26, Alexandre Belloni
<alexandre.belloni@free-electrons.com> wrote:
> Hi,
>
> On 08/11/2017 at 17:16:15 +0800, Baolin Wang wrote:
>> +static int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm)
>> +{
>> +     struct sprd_rtc *rtc = dev_get_drvdata(dev);
>> +     time64_t secs;
>> +     int ret;
>> +
>
> I would have expected a check for SPRD_RTC_POWER_RESET_FLAG here.

Actually I want to create one separate patch to add this feature. But
like you suggested, I can add this checking in here in next version.
Thanks for your suggestion.

>
>
>> +     ret = sprd_rtc_get_secs(rtc, SPRD_RTC_TIME, &secs);
>> +     if (ret)
>> +             return ret;
>> +
>> +     rtc_time64_to_tm(secs, tm);
>> +     return rtc_valid_tm(tm);
>> +}
>> +
>> +static int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm)
>> +{
>> +     struct sprd_rtc *rtc = dev_get_drvdata(dev);
>> +     time64_t secs = rtc_tm_to_time64(tm);
>> +
>
> And you need to reset SPRD_RTC_POWER_RESET_FLAG here
>
>> +     return sprd_rtc_set_secs(rtc, SPRD_RTC_TIME, secs);
>> +}
>> +

-- 
Baolin.wang
Best Regards

^ permalink raw reply

* Re: [PATCH v2 2/2] rtc: sprd: Add Spreadtrum RTC driver
From: Alexandre Belloni @ 2017-11-09  1:26 UTC (permalink / raw)
  To: Baolin Wang
  Cc: a.zummo, robh+dt, mark.rutland, devicetree, linux-kernel,
	linux-rtc, broonie, baolin.wang
In-Reply-To: <6482283a3ae8c6ec35d0a7a078420151a69b8aca.1510132279.git.baolin.wang@spreadtrum.com>

Hi,

On 08/11/2017 at 17:16:15 +0800, Baolin Wang wrote:
> +static int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct sprd_rtc *rtc = dev_get_drvdata(dev);
> +	time64_t secs;
> +	int ret;
> +

I would have expected a check for SPRD_RTC_POWER_RESET_FLAG here.


> +	ret = sprd_rtc_get_secs(rtc, SPRD_RTC_TIME, &secs);
> +	if (ret)
> +		return ret;
> +
> +	rtc_time64_to_tm(secs, tm);
> +	return rtc_valid_tm(tm);
> +}
> +
> +static int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct sprd_rtc *rtc = dev_get_drvdata(dev);
> +	time64_t secs = rtc_tm_to_time64(tm);
> +

And you need to reset SPRD_RTC_POWER_RESET_FLAG here

> +	return sprd_rtc_set_secs(rtc, SPRD_RTC_TIME, secs);
> +}
> +
-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH v2] rtc: rx8010: Fix for incorrect return value
From: Alexandre Belloni @ 2017-11-09  0:18 UTC (permalink / raw)
  To: Akshay Bhat; +Cc: a.zummo, linux-rtc, linux-kernel
In-Reply-To: <1510171094-4859-1-git-send-email-akshay.bhat@timesys.com>

On 08/11/2017 at 14:58:14 -0500, Akshay Bhat wrote:
> The err variable is not being reset after a successful read. Explicitly
> return 0 at the end of function call to account for all return paths.
> 
> Reported-by: Jens-Peter Oswald <oswald@lre.de>
> Signed-off-by: Akshay Bhat <akshay.bhat@timesys.com>
> ---
> v2: Address comments from Alexandre Belloni
> - Return 0 at end of function instead of resetting err variable
> 
>  drivers/rtc/rtc-rx8010.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
Applied, thanks.

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH] rtc: x-gene: mark PM functions as __maybe_unused
From: Alexandre Belloni @ 2017-11-09  0:17 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Alessandro Zummo, Loc Ho, Mark Brown, linux-rtc, linux-kernel
In-Reply-To: <20171108120815.3175571-1-arnd@arndb.de>

On 08/11/2017 at 13:08:10 +0100, Arnd Bergmann wrote:
> The new xgene_rtc_alarm_irq_enabled() function is only accessed
> from PM code, which is inside of an #ifdef; this causes a harmless
> build warning when CONFIG_PM is disabled:
> 
> drivers/rtc/rtc-xgene.c:108:12: error: 'xgene_rtc_alarm_irq_enabled' defined but not used [-Werror=unused-function]
> 
> Just remove the #ifdef and use __maybe_unused annotations instead,
> to make the code more robust here.
> 
> Fixes: 9f8f1c8b727d ("rtc: Fix suspend/resume for APM X-Gene SoC RTC driver")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
>  drivers/rtc/rtc-xgene.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
> 
Applied, thanks.

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH 3/3] rtc: rx8010: Fix for incorrect return value
From: Akshay Bhat @ 2017-11-08 20:00 UTC (permalink / raw)
  To: Alexandre Belloni; +Cc: Alessandro Zummo, linux-rtc, linux-kernel
In-Reply-To: <20171108023000.4oepp5pyz32c7sr3@piout.net>

On Tue, Nov 7, 2017 at 9:30 PM, Alexandre Belloni
<alexandre.belloni@free-electrons.com> wrote:
> On 03/11/2017 at 13:32:41 -0400, Akshay Bhat wrote:
>>       if (err != 2)
>>               return err < 0 ? err : -EIO;
>> +     err = 0;
>
> Isn't it simpler to make the function return 0 instead of err at the end?

Makes sense, fixed in v2 patch, thanks.

^ permalink raw reply

* [PATCH v2] rtc: rx8010: Fix for incorrect return value
From: Akshay Bhat @ 2017-11-08 19:58 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni; +Cc: linux-rtc, linux-kernel, Akshay Bhat

The err variable is not being reset after a successful read. Explicitly
return 0 at the end of function call to account for all return paths.

Reported-by: Jens-Peter Oswald <oswald@lre.de>
Signed-off-by: Akshay Bhat <akshay.bhat@timesys.com>
---
v2: Address comments from Alexandre Belloni
- Return 0 at end of function instead of resetting err variable

 drivers/rtc/rtc-rx8010.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c
index 2e06e5f..5c5938a 100644
--- a/drivers/rtc/rtc-rx8010.c
+++ b/drivers/rtc/rtc-rx8010.c
@@ -247,7 +247,7 @@ static int rx8010_init_client(struct i2c_client *client)
 
 	rx8010->ctrlreg = (ctrl[1] & ~RX8010_CTRL_TEST);
 
-	return err;
+	return 0;
 }
 
 static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
@@ -276,7 +276,7 @@ static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
 	t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE);
 	t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled;
 
-	return err;
+	return 0;
 }
 
 static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH] rtc: x-gene: mark PM functions as __maybe_unused
From: Loc Ho @ 2017-11-08 17:26 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Alessandro Zummo, Alexandre Belloni, Mark Brown, linux-rtc,
	Linux Kernel Mailing List
In-Reply-To: <20171108120815.3175571-1-arnd@arndb.de>

Hi,

> The new xgene_rtc_alarm_irq_enabled() function is only accessed
> from PM code, which is inside of an #ifdef; this causes a harmless
> build warning when CONFIG_PM is disabled:
>
> drivers/rtc/rtc-xgene.c:108:12: error: 'xgene_rtc_alarm_irq_enabled' defined but not used [-Werror=unused-function]
>
> Just remove the #ifdef and use __maybe_unused annotations instead,
> to make the code more robust here.

It sounds like desire to merge in the suspend/resume support as this
is in linux-next 14 hours ago.

Reviewed-by: Loc Ho <lho@apm.com>

-Loc

^ permalink raw reply

* [PATCH] rtc: x-gene: mark PM functions as __maybe_unused
From: Arnd Bergmann @ 2017-11-08 12:08 UTC (permalink / raw)
  To: Alessandro Zummo, Alexandre Belloni
  Cc: Arnd Bergmann, Loc Ho, Mark Brown, linux-rtc, linux-kernel

The new xgene_rtc_alarm_irq_enabled() function is only accessed
from PM code, which is inside of an #ifdef; this causes a harmless
build warning when CONFIG_PM is disabled:

drivers/rtc/rtc-xgene.c:108:12: error: 'xgene_rtc_alarm_irq_enabled' defined but not used [-Werror=unused-function]

Just remove the #ifdef and use __maybe_unused annotations instead,
to make the code more robust here.

Fixes: 9f8f1c8b727d ("rtc: Fix suspend/resume for APM X-Gene SoC RTC driver")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 drivers/rtc/rtc-xgene.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c
index 360eae24a8c8..0c34d3b81279 100644
--- a/drivers/rtc/rtc-xgene.c
+++ b/drivers/rtc/rtc-xgene.c
@@ -221,8 +221,7 @@ static int xgene_rtc_remove(struct platform_device *pdev)
 	return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int xgene_rtc_suspend(struct device *dev)
+static int __maybe_unused xgene_rtc_suspend(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
@@ -246,7 +245,7 @@ static int xgene_rtc_suspend(struct device *dev)
 	return 0;
 }
 
-static int xgene_rtc_resume(struct device *dev)
+static int __maybe_unused xgene_rtc_resume(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
@@ -271,7 +270,6 @@ static int xgene_rtc_resume(struct device *dev)
 
 	return 0;
 }
-#endif
 
 static SIMPLE_DEV_PM_OPS(xgene_rtc_pm_ops, xgene_rtc_suspend, xgene_rtc_resume);
 
-- 
2.9.0

^ permalink raw reply related

* [PATCH v2 2/2] rtc: sprd: Add Spreadtrum RTC driver
From: Baolin Wang @ 2017-11-08  9:16 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, mark.rutland
  Cc: devicetree, linux-kernel, linux-rtc, broonie, baolin.wang,
	baolin.wang
In-Reply-To: <c65b61e5e37cb43214973cb3e479ce06c4a73395.1510132279.git.baolin.wang@spreadtrum.com>

This patch adds the Spreadtrum RTC driver, which embedded in the
Spreadtrum SC27xx series PMICs.

Signed-off-by: Baolin Wang <baolin.wang@spreadtrum.com>
---
Changes since v1:
 - Change file name.
 - Should depend on the MFD parent.
 - Add more help documentation.
 - Remove regmap wrapper functions.
 - Modify the mothod of setting alarm.
 - Remove set_mmss64() method.
 - Remove checking RTC power-down.
 - Optimize lock/unlock alarm.
---
 drivers/rtc/Kconfig      |   11 +
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-sc27xx.c |  600 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 612 insertions(+)
 create mode 100644 drivers/rtc/rtc-sc27xx.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e0e58f3..8c240e7 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1174,6 +1174,17 @@ config RTC_DRV_WM8350
 	  This driver can also be built as a module. If so, the module
 	  will be called "rtc-wm8350".
 
+config RTC_DRV_SC27XX
+	tristate "Spreadtrum SC27xx RTC"
+	depends on MFD_SC27XX_PMIC || COMPILE_TEST
+	help
+	  If you say Y here you will get support for the RTC subsystem
+	  of the Spreadtrum SC27xx series PMICs. The SC27xx series PMICs
+	  includes the SC2720, SC2721, SC2723, SC2730 and SC2731 chips.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-sc27xx.
+
 config RTC_DRV_SPEAR
 	tristate "SPEAR ST RTC"
 	depends on PLAT_SPEAR || COMPILE_TEST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 7230014..f9b857f 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -143,6 +143,7 @@ obj-$(CONFIG_RTC_DRV_S35390A)	+= rtc-s35390a.o
 obj-$(CONFIG_RTC_DRV_S3C)	+= rtc-s3c.o
 obj-$(CONFIG_RTC_DRV_S5M)	+= rtc-s5m.o
 obj-$(CONFIG_RTC_DRV_SA1100)	+= rtc-sa1100.o
+obj-$(CONFIG_RTC_DRV_SC27XX)	+= rtc-sc27xx.o
 obj-$(CONFIG_RTC_DRV_SH)	+= rtc-sh.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
 obj-$(CONFIG_RTC_DRV_SNVS)	+= rtc-snvs.o
diff --git a/drivers/rtc/rtc-sc27xx.c b/drivers/rtc/rtc-sc27xx.c
new file mode 100644
index 0000000..f1b673d
--- /dev/null
+++ b/drivers/rtc/rtc-sc27xx.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2017 Spreadtrum Communications Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+
+#define SPRD_RTC_SEC_CNT_VALUE		0x0
+#define SPRD_RTC_MIN_CNT_VALUE		0x4
+#define SPRD_RTC_HOUR_CNT_VALUE		0x8
+#define SPRD_RTC_DAY_CNT_VALUE		0xc
+#define SPRD_RTC_SEC_CNT_UPD		0x10
+#define SPRD_RTC_MIN_CNT_UPD		0x14
+#define SPRD_RTC_HOUR_CNT_UPD		0x18
+#define SPRD_RTC_DAY_CNT_UPD		0x1c
+#define SPRD_RTC_SEC_ALM_UPD		0x20
+#define SPRD_RTC_MIN_ALM_UPD		0x24
+#define SPRD_RTC_HOUR_ALM_UPD		0x28
+#define SPRD_RTC_DAY_ALM_UPD		0x2c
+#define SPRD_RTC_INT_EN			0x30
+#define SPRD_RTC_INT_RAW_STS		0x34
+#define SPRD_RTC_INT_CLR		0x38
+#define SPRD_RTC_INT_MASK_STS		0x3C
+#define SPRD_RTC_SEC_ALM_VALUE		0x40
+#define SPRD_RTC_MIN_ALM_VALUE		0x44
+#define SPRD_RTC_HOUR_ALM_VALUE		0x48
+#define SPRD_RTC_DAY_ALM_VALUE		0x4c
+#define SPRD_RTC_SPG_VALUE		0x50
+#define SPRD_RTC_SPG_UPD		0x54
+#define SPRD_RTC_SEC_AUXALM_UPD		0x60
+#define SPRD_RTC_MIN_AUXALM_UPD		0x64
+#define SPRD_RTC_HOUR_AUXALM_UPD	0x68
+#define SPRD_RTC_DAY_AUXALM_UPD		0x6c
+
+/* BIT & MASK definition for SPRD_RTC_INT_* registers */
+#define SPRD_RTC_SEC_EN			BIT(0)
+#define SPRD_RTC_MIN_EN			BIT(1)
+#define SPRD_RTC_HOUR_EN		BIT(2)
+#define SPRD_RTC_DAY_EN			BIT(3)
+#define SPRD_RTC_ALARM_EN		BIT(4)
+#define SPRD_RTC_HRS_FORMAT_EN		BIT(5)
+#define SPRD_RTC_AUXALM_EN		BIT(6)
+#define SPRD_RTC_SPG_UPD_EN		BIT(7)
+#define SPRD_RTC_SEC_UPD_EN		BIT(8)
+#define SPRD_RTC_MIN_UPD_EN		BIT(9)
+#define SPRD_RTC_HOUR_UPD_EN		BIT(10)
+#define SPRD_RTC_DAY_UPD_EN		BIT(11)
+#define SPRD_RTC_ALMSEC_UPD_EN		BIT(12)
+#define SPRD_RTC_ALMMIN_UPD_EN		BIT(13)
+#define SPRD_RTC_ALMHOUR_UPD_EN		BIT(14)
+#define SPRD_RTC_ALMDAY_UPD_EN		BIT(15)
+#define SPRD_RTC_INT_MASK		GENMASK(15, 0)
+
+#define SPRD_RTC_TIME_INT_MASK				\
+	(SPRD_RTC_SEC_UPD_EN | SPRD_RTC_MIN_UPD_EN |	\
+	 SPRD_RTC_HOUR_UPD_EN | SPRD_RTC_DAY_UPD_EN)
+
+#define SPRD_RTC_ALMTIME_INT_MASK				\
+	(SPRD_RTC_ALMSEC_UPD_EN | SPRD_RTC_ALMMIN_UPD_EN |	\
+	 SPRD_RTC_ALMHOUR_UPD_EN | SPRD_RTC_ALMDAY_UPD_EN)
+
+#define SPRD_RTC_ALM_INT_MASK			\
+	(SPRD_RTC_SEC_EN | SPRD_RTC_MIN_EN |	\
+	 SPRD_RTC_HOUR_EN | SPRD_RTC_DAY_EN |	\
+	 SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN)
+
+/* second/minute/hour/day values mask definition */
+#define SPRD_RTC_SEC_MASK		GENMASK(5, 0)
+#define SPRD_RTC_MIN_MASK		GENMASK(5, 0)
+#define SPRD_RTC_HOUR_MASK		GENMASK(4, 0)
+#define SPRD_RTC_DAY_MASK		GENMASK(15, 0)
+
+/* alarm lock definition for SPRD_RTC_SPG_UPD register */
+#define SPRD_RTC_ALMLOCK_MASK		GENMASK(7, 0)
+#define SPRD_RTC_ALM_UNLOCK		0xa5
+#define SPRD_RTC_ALM_LOCK		(~SPRD_RTC_ALM_UNLOCK &	\
+					 SPRD_RTC_ALMLOCK_MASK)
+
+/* SPG values definition for SPRD_RTC_SPG_UPD register */
+#define SPRD_RTC_POWEROFF_ALM_FLAG	BIT(8)
+#define SPRD_RTC_POWER_RESET_FLAG	BIT(9)
+
+/* timeout of synchronizing time and alarm registers (us) */
+#define SPRD_RTC_POLL_TIMEOUT		200000
+#define SPRD_RTC_POLL_DELAY_US		20000
+
+struct sprd_rtc {
+	struct rtc_device	*rtc;
+	struct regmap		*regmap;
+	struct device		*dev;
+	u32			base;
+	int			irq;
+};
+
+/*
+ * The Spreadtrum RTC controller has 3 groups registers, including time, normal
+ * alarm and auxiliary alarm. The time group registers are used to set RTC time,
+ * the normal alarm registers are used to set normal alarm, and the auxiliary
+ * alarm registers are used to set auxiliary alarm. Both alarm event and
+ * auxiliary alarm event can wake up system from deep sleep, but only alarm
+ * event can power up system from power down status.
+ */
+enum sprd_rtc_reg_types {
+	SPRD_RTC_TIME,
+	SPRD_RTC_ALARM,
+	SPRD_RTC_AUX_ALARM,
+};
+
+static int sprd_rtc_clear_alarm_ints(struct sprd_rtc *rtc)
+{
+	return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			    SPRD_RTC_ALM_INT_MASK);
+}
+
+static int sprd_rtc_disable_ints(struct sprd_rtc *rtc)
+{
+	int ret;
+
+	ret = regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN,
+				 SPRD_RTC_INT_MASK, 0);
+	if (ret)
+		return ret;
+
+	return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			    SPRD_RTC_INT_MASK);
+}
+
+static int sprd_rtc_lock_alarm(struct sprd_rtc *rtc, bool lock)
+{
+	int ret;
+	u32 val;
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val);
+	if (ret)
+		return ret;
+
+	val &= ~(SPRD_RTC_ALMLOCK_MASK | SPRD_RTC_POWEROFF_ALM_FLAG);
+	if (lock)
+		val |= SPRD_RTC_ALM_LOCK;
+	else
+		val |= SPRD_RTC_ALM_UNLOCK | SPRD_RTC_POWEROFF_ALM_FLAG;
+
+	ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_SPG_UPD, val);
+	if (ret)
+		return ret;
+
+	/* wait until the SPG value is updated successfully */
+	ret = regmap_read_poll_timeout(rtc->regmap,
+				       rtc->base + SPRD_RTC_INT_RAW_STS, val,
+				       (val & SPRD_RTC_SPG_UPD_EN),
+				       SPRD_RTC_POLL_DELAY_US,
+				       SPRD_RTC_POLL_TIMEOUT);
+	if (ret) {
+		dev_err(rtc->dev, "failed to update SPG value:%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int sprd_rtc_get_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type,
+			     time64_t *secs)
+{
+	u32 sec_reg, min_reg, hour_reg, day_reg;
+	u32 val, sec, min, hour, day;
+	int ret;
+
+	switch (type) {
+	case SPRD_RTC_TIME:
+		sec_reg = SPRD_RTC_SEC_CNT_VALUE;
+		min_reg = SPRD_RTC_MIN_CNT_VALUE;
+		hour_reg = SPRD_RTC_HOUR_CNT_VALUE;
+		day_reg = SPRD_RTC_DAY_CNT_VALUE;
+		break;
+	case SPRD_RTC_ALARM:
+		sec_reg = SPRD_RTC_SEC_ALM_VALUE;
+		min_reg = SPRD_RTC_MIN_ALM_VALUE;
+		hour_reg = SPRD_RTC_HOUR_ALM_VALUE;
+		day_reg = SPRD_RTC_DAY_ALM_VALUE;
+		break;
+	case SPRD_RTC_AUX_ALARM:
+		sec_reg = SPRD_RTC_SEC_AUXALM_UPD;
+		min_reg = SPRD_RTC_MIN_AUXALM_UPD;
+		hour_reg = SPRD_RTC_HOUR_AUXALM_UPD;
+		day_reg = SPRD_RTC_DAY_AUXALM_UPD;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_read(rtc->regmap, rtc->base + sec_reg, &val);
+	if (ret)
+		return ret;
+
+	sec = val & SPRD_RTC_SEC_MASK;
+
+	ret = regmap_read(rtc->regmap, rtc->base + min_reg, &val);
+	if (ret)
+		return ret;
+
+	min = val & SPRD_RTC_MIN_MASK;
+
+	ret = regmap_read(rtc->regmap, rtc->base + hour_reg, &val);
+	if (ret)
+		return ret;
+
+	hour = val & SPRD_RTC_HOUR_MASK;
+
+	ret = regmap_read(rtc->regmap, rtc->base + day_reg, &val);
+	if (ret)
+		return ret;
+
+	day = val & SPRD_RTC_DAY_MASK;
+	*secs = (((time64_t)(day * 24) + hour) * 60 + min) * 60 + sec;
+	return 0;
+}
+
+static int sprd_rtc_set_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type,
+			     time64_t secs)
+{
+	u32 sec_reg, min_reg, hour_reg, day_reg, sts_mask;
+	u32 sec, min, hour, day, val;
+	int ret, rem;
+
+	/* convert seconds to RTC time format */
+	day = div_s64_rem(secs, 86400, &rem);
+	hour = rem / 3600;
+	rem -= hour * 3600;
+	min = rem / 60;
+	sec = rem - min * 60;
+
+	switch (type) {
+	case SPRD_RTC_TIME:
+		sec_reg = SPRD_RTC_SEC_CNT_UPD;
+		min_reg = SPRD_RTC_MIN_CNT_UPD;
+		hour_reg = SPRD_RTC_HOUR_CNT_UPD;
+		day_reg = SPRD_RTC_DAY_CNT_UPD;
+		sts_mask = SPRD_RTC_TIME_INT_MASK;
+		break;
+	case SPRD_RTC_ALARM:
+		sec_reg = SPRD_RTC_SEC_ALM_UPD;
+		min_reg = SPRD_RTC_MIN_ALM_UPD;
+		hour_reg = SPRD_RTC_HOUR_ALM_UPD;
+		day_reg = SPRD_RTC_DAY_ALM_UPD;
+		sts_mask = SPRD_RTC_ALMTIME_INT_MASK;
+		break;
+	case SPRD_RTC_AUX_ALARM:
+		sec_reg = SPRD_RTC_SEC_AUXALM_UPD;
+		min_reg = SPRD_RTC_MIN_AUXALM_UPD;
+		hour_reg = SPRD_RTC_HOUR_AUXALM_UPD;
+		day_reg = SPRD_RTC_DAY_AUXALM_UPD;
+		sts_mask = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_write(rtc->regmap, rtc->base + sec_reg, sec);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(rtc->regmap, rtc->base + min_reg, min);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(rtc->regmap, rtc->base + hour_reg, hour);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(rtc->regmap, rtc->base + day_reg, day);
+	if (ret)
+		return ret;
+
+	if (type == SPRD_RTC_AUX_ALARM)
+		return 0;
+
+	/*
+	 * Since the time and normal alarm registers are put in always-power-on
+	 * region supplied by VDDRTC, then these registers changing time will
+	 * be very long, about 125ms. Thus here we should wait until all
+	 * values are updated successfully.
+	 */
+	ret = regmap_read_poll_timeout(rtc->regmap,
+				       rtc->base + SPRD_RTC_INT_RAW_STS, val,
+				       ((val & sts_mask) == sts_mask),
+				       SPRD_RTC_POLL_DELAY_US,
+				       SPRD_RTC_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(rtc->dev, "set time/alarm values timeout\n");
+		return ret;
+	}
+
+	return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			    sts_mask);
+}
+
+static int sprd_rtc_read_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs;
+	u32 val;
+	int ret;
+
+	ret = sprd_rtc_get_secs(rtc, SPRD_RTC_AUX_ALARM, &secs);
+	if (ret)
+		return ret;
+
+	rtc_time64_to_tm(secs, &alrm->time);
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val);
+	if (ret)
+		return ret;
+
+	alrm->enabled = !!(val & SPRD_RTC_AUXALM_EN);
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val);
+	if (ret)
+		return ret;
+
+	alrm->pending = !!(val & SPRD_RTC_AUXALM_EN);
+	return 0;
+}
+
+static int sprd_rtc_set_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs = rtc_tm_to_time64(&alrm->time);
+	int ret;
+
+	/* clear the auxiliary alarm interrupt status */
+	ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			   SPRD_RTC_AUXALM_EN);
+	if (ret)
+		return ret;
+
+	ret = sprd_rtc_set_secs(rtc, SPRD_RTC_AUX_ALARM, secs);
+	if (ret)
+		return ret;
+
+	if (alrm->enabled) {
+		ret = regmap_update_bits(rtc->regmap,
+					 rtc->base + SPRD_RTC_INT_EN,
+					 SPRD_RTC_AUXALM_EN,
+					 SPRD_RTC_AUXALM_EN);
+	} else {
+		ret = regmap_update_bits(rtc->regmap,
+					 rtc->base + SPRD_RTC_INT_EN,
+					 SPRD_RTC_AUXALM_EN, 0);
+	}
+
+	return ret;
+}
+
+static int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs;
+	int ret;
+
+	ret = sprd_rtc_get_secs(rtc, SPRD_RTC_TIME, &secs);
+	if (ret)
+		return ret;
+
+	rtc_time64_to_tm(secs, tm);
+	return rtc_valid_tm(tm);
+}
+
+static int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs = rtc_tm_to_time64(tm);
+
+	return sprd_rtc_set_secs(rtc, SPRD_RTC_TIME, secs);
+}
+
+static int sprd_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs;
+	int ret;
+	u32 val;
+
+	/*
+	 * If aie_timer is enabled, we should get the normal alarm time.
+	 * Otherwise we should get auxiliary alarm time.
+	 */
+	if (rtc->rtc && rtc->rtc->aie_timer.enabled == 0)
+		return sprd_rtc_read_aux_alarm(dev, alrm);
+
+	ret = sprd_rtc_get_secs(rtc, SPRD_RTC_ALARM, &secs);
+	if (ret)
+		return ret;
+
+	rtc_time64_to_tm(secs, &alrm->time);
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val);
+	if (ret)
+		return ret;
+
+	alrm->enabled = !!(val & SPRD_RTC_ALARM_EN);
+
+	ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val);
+	if (ret)
+		return ret;
+
+	alrm->pending = !!(val & SPRD_RTC_ALARM_EN);
+	return 0;
+}
+
+static int sprd_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	time64_t secs = rtc_tm_to_time64(&alrm->time);
+	struct rtc_time aie_time =
+		rtc_ktime_to_tm(rtc->rtc->aie_timer.node.expires);
+	int ret;
+
+	/*
+	 * We have 2 groups alarms: normal alarm and auxiliary alarm. Since
+	 * both normal alarm event and auxiliary alarm event can wake up system
+	 * from deep sleep, but only alarm event can power up system from power
+	 * down status. Moreover we do not need to poll about 125ms when
+	 * updating auxiliary alarm registers. Thus we usually set auxiliary
+	 * alarm when wake up system from deep sleep, and for other scenarios,
+	 * we should set normal alarm with polling status.
+	 *
+	 * So here we check if the alarm time is set by aie_timer, if yes, we
+	 * should set normal alarm, if not, we should set auxiliary alarm which
+	 * means it is just a wake event.
+	 */
+	if (!rtc->rtc->aie_timer.enabled || rtc_tm_sub(&aie_time, &alrm->time))
+		return sprd_rtc_set_aux_alarm(dev, alrm);
+
+	/* clear the alarm interrupt status firstly */
+	ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
+			   SPRD_RTC_ALARM_EN);
+	if (ret)
+		return ret;
+
+	ret = sprd_rtc_set_secs(rtc, SPRD_RTC_ALARM, secs);
+	if (ret)
+		return ret;
+
+	if (alrm->enabled) {
+		ret = regmap_update_bits(rtc->regmap,
+					 rtc->base + SPRD_RTC_INT_EN,
+					 SPRD_RTC_ALARM_EN,
+					 SPRD_RTC_ALARM_EN);
+		if (ret)
+			return ret;
+
+		/* unlock the alarm to enable the alarm function. */
+		ret = sprd_rtc_lock_alarm(rtc, false);
+	} else {
+		regmap_update_bits(rtc->regmap,
+				   rtc->base + SPRD_RTC_INT_EN,
+				   SPRD_RTC_ALARM_EN, 0);
+
+		/*
+		 * Lock the alarm function in case fake alarm event will power
+		 * up systems.
+		 */
+		ret = sprd_rtc_lock_alarm(rtc, true);
+	}
+
+	return ret;
+}
+
+static int sprd_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct sprd_rtc *rtc = dev_get_drvdata(dev);
+	int ret;
+
+	if (enabled) {
+		ret = regmap_update_bits(rtc->regmap,
+					 rtc->base + SPRD_RTC_INT_EN,
+					 SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN,
+					 SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN);
+		if (ret)
+			return ret;
+
+		ret = sprd_rtc_lock_alarm(rtc, false);
+	} else {
+		regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN,
+				   SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, 0);
+
+		ret = sprd_rtc_lock_alarm(rtc, true);
+	}
+
+	return ret;
+}
+
+static const struct rtc_class_ops sprd_rtc_ops = {
+	.read_time = sprd_rtc_read_time,
+	.set_time = sprd_rtc_set_time,
+	.read_alarm = sprd_rtc_read_alarm,
+	.set_alarm = sprd_rtc_set_alarm,
+	.alarm_irq_enable = sprd_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t sprd_rtc_handler(int irq, void *dev_id)
+{
+	struct sprd_rtc *rtc = dev_id;
+	int ret;
+
+	ret = sprd_rtc_clear_alarm_ints(rtc);
+	if (ret)
+		return IRQ_RETVAL(ret);
+
+	rtc_update_irq(rtc->rtc, 1, RTC_AF | RTC_IRQF);
+	return IRQ_HANDLED;
+}
+
+static int sprd_rtc_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct sprd_rtc *rtc;
+	int ret;
+
+	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+	if (!rtc)
+		return -ENOMEM;
+
+	rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!rtc->regmap)
+		return -ENODEV;
+
+	ret = of_property_read_u32(node, "reg", &rtc->base);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to get RTC base address\n");
+		return ret;
+	}
+
+	rtc->irq = platform_get_irq(pdev, 0);
+	if (rtc->irq < 0) {
+		dev_err(&pdev->dev, "failed to get RTC irq number\n");
+		return rtc->irq;
+	}
+
+	rtc->dev = &pdev->dev;
+	platform_set_drvdata(pdev, rtc);
+
+	/* Clear all RTC interrupts and disable all RTC interrupts */
+	ret = sprd_rtc_disable_ints(rtc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to disable RTC interrupts\n");
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
+					sprd_rtc_handler,
+					IRQF_ONESHOT | IRQF_EARLY_RESUME,
+					pdev->name, rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to request RTC irq\n");
+		return ret;
+	}
+
+	rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+					    &sprd_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc->rtc))
+		return PTR_ERR(rtc->rtc);
+
+	device_init_wakeup(&pdev->dev, 1);
+	return 0;
+}
+
+static int sprd_rtc_remove(struct platform_device *pdev)
+{
+	device_init_wakeup(&pdev->dev, 0);
+	return 0;
+}
+
+static const struct of_device_id sprd_rtc_of_match[] = {
+	{ .compatible = "sprd,sc2731-rtc", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sprd_rtc_of_match);
+
+static struct platform_driver sprd_rtc_driver = {
+	.driver = {
+		.name = "sprd-rtc",
+		.of_match_table = sprd_rtc_of_match,
+	},
+	.probe	= sprd_rtc_probe,
+	.remove = sprd_rtc_remove,
+};
+module_platform_driver(sprd_rtc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Spreadtrum RTC Device Driver");
+MODULE_AUTHOR("Baolin Wang <baolin.wang@spreadtrum.com>");
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v2 1/2] dt-bindings: rtc: Add Spreadtrum SC27xx RTC documentation
From: Baolin Wang @ 2017-11-08  9:16 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, mark.rutland
  Cc: devicetree, linux-kernel, linux-rtc, broonie, baolin.wang,
	baolin.wang

This patch adds the binding documentation for Spreadtrum SC27xx series
RTC device.

Signed-off-by: Baolin Wang <baolin.wang@spreadtrum.com>
---
Changes since v1:
 - Use a specific chip name as compatible string.
 - Remove useless alias.
 - Add the parent MFD node.
---
 .../devicetree/bindings/rtc/sprd,sc27xx-rtc.txt    |   27 ++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt

diff --git a/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt b/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt
new file mode 100644
index 0000000..7c170da
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt
@@ -0,0 +1,27 @@
+Spreadtrum SC27xx Real Time Clock
+
+Required properties:
+- compatible: should be "sprd,sc2731-rtc".
+- reg: address offset of rtc register.
+- interrupt-parent: phandle for the interrupt controller.
+- interrupts: rtc alarm interrupt.
+
+Example:
+
+	sc2731_pmic: pmic@0 {
+		compatible = "sprd,sc2731";
+		reg = <0>;
+		spi-max-frequency = <26000000>;
+		interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		rtc@280 {
+			compatible = "sprd,sc2731-rtc";
+			reg = <0x280>;
+			interrupt-parent = <&sc2731_pmic>;
+			interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
+		};
+	};
-- 
1.7.9.5

^ permalink raw reply related

* Re: [PATCH] rtc: omap: Support scratch registers
From: Keerthy @ 2017-11-08  8:48 UTC (permalink / raw)
  To: Alexandre Belloni; +Cc: Sekhar Nori, linux-rtc, linux-kernel, Linux OMAP List
In-Reply-To: <20171108083533.5ibx43biwk7exlnc@piout.net>



On Wednesday 08 November 2017 02:05 PM, Alexandre Belloni wrote:
> On 08/11/2017 at 14:02:31 +0530, Keerthy wrote:
>>
>>
>> On Wednesday 08 November 2017 02:01 PM, Keerthy wrote:
>>>
>>>
>>> On Wednesday 08 November 2017 01:51 PM, Alexandre Belloni wrote:
>>>> On 08/11/2017 at 13:36:15 +0530, Keerthy wrote:
>>>>>
>>>>>
>>>>> On Wednesday 08 November 2017 12:46 PM, Alexandre Belloni wrote:
>>>>>> On 08/11/2017 at 12:38:05 +0530, Keerthy wrote:
>>>>>>>
>>>>>>>
>>>>>>> On Wednesday 08 November 2017 11:57 AM, Alexandre Belloni wrote:
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> On 08/11/2017 at 11:30:45 +0530, Keerthy wrote:
>>>>>>>>>>>> +static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val,
>>>>>>>>>>>> +				 size_t bytes)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +	struct omap_rtc	*rtc = priv;
>>>>>>>>>>>> +	u32 *val = _val;
>>>>>>>>>>>> +	int i;
>>>>>>>>>>>> +
>>>>>>>>>>>> +	for (i = 0; i < bytes / 4; i++)
>>>>>>>>>>>> +		val[i] = rtc_readl(rtc,
>>>>>>>>>>>> +				   OMAP_RTC_SCRATCH0_REG + offset + (i * 4));
>>>>>>>>>
>>>>>>>>> Can the offset be the Scratch register number instead of bytes offset?
>>>>>>>>> More intuitive to me.
>>>>>>>>>
>>>>>>>>> So that one can request using offset as 0, 1, 2 instead of 0, 4, 8?
>>>>>>>>>
>>>>>>>>
>>>>>>>> Well, the offset is coming from the nvmem core, itself getting it from
>>>>>>>> the Linux file API (and it is in bytes). However, you have the guarantee
>>>>>>>> that it will be aligned on a word, see:
>>>>>>>> http://elixir.free-electrons.com/linux/latest/source/drivers/nvmem/core.c#L88
>>>>>>>
>>>>>>> Okay Alexandre. Thanks for clarifying. Looks good to me.
>>>>>>> I have tested on AM437X-GP-EVM.
>>>>>>>
>>>>>>
>>>>>> If needed, you can define nvmem cells (and I guess that is what you
>>>>>> want):
>>>>>> http://elixir.free-electrons.com/linux/latest/source/Documentation/devicetree/bindings/nvmem/nvmem.txt
>>>>>
>>>>> With this patch we cannot still use nvmem_device_write and
>>>>> nvmem_device_read as the nvmem_device is still not registered.
>>>>>
>>>>> How can a driver write to scratch pad registers? Should we be calling
>>>>> nvmem_register in the probe?
>>>>
>>>> It is not needed, it is registered by rtc_nvmem_register().
>>>
>>> Let me go through the chain:
>>>
>>> rtc_register_device -> __rtc_register_device --> rtc_nvmem_register ->
>>> rtc_nvram_register
>>>
>>> I am seeing that for rtc-omap driver there is rtc_register_device call
>>
>> I mean there NO rtc_register_device call.
>>
>>> and i see that rtc_nvmem_register is not getting called.
>>>
>>> Should we add rtc_register_device in probe of rtc-omap?
>>>
> 
> Do you also have that patch:
> https://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git/commit/?h=rtc-next&id=57072758623fa4f3019bce65e2b00f24af8dfdd7
> 
> ?

Ah! Now everything falls in place :-). I did not receive this patch. I
believe i need to add a patch so linux-omap list gets Cc'd whenever
rtc-omap driver is touched.

Thanks a bunch!

> 
>>>>
>>>> nvmem_device_read/write are working because that is what I'm using to
>>>> implement the old sysfs ABI with rtc_nvram_read/write.
>>>>
> 

^ permalink raw reply

* Re: [PATCH] rtc: omap: Support scratch registers
From: Alexandre Belloni @ 2017-11-08  8:35 UTC (permalink / raw)
  To: Keerthy; +Cc: Sekhar Nori, linux-rtc, linux-kernel, Linux OMAP List
In-Reply-To: <c24e76cc-ba52-3d26-7dce-5c9ed7e68db1@ti.com>

On 08/11/2017 at 14:02:31 +0530, Keerthy wrote:
> 
> 
> On Wednesday 08 November 2017 02:01 PM, Keerthy wrote:
> > 
> > 
> > On Wednesday 08 November 2017 01:51 PM, Alexandre Belloni wrote:
> >> On 08/11/2017 at 13:36:15 +0530, Keerthy wrote:
> >>>
> >>>
> >>> On Wednesday 08 November 2017 12:46 PM, Alexandre Belloni wrote:
> >>>> On 08/11/2017 at 12:38:05 +0530, Keerthy wrote:
> >>>>>
> >>>>>
> >>>>> On Wednesday 08 November 2017 11:57 AM, Alexandre Belloni wrote:
> >>>>>> Hi,
> >>>>>>
> >>>>>> On 08/11/2017 at 11:30:45 +0530, Keerthy wrote:
> >>>>>>>>>> +static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val,
> >>>>>>>>>> +				 size_t bytes)
> >>>>>>>>>> +{
> >>>>>>>>>> +	struct omap_rtc	*rtc = priv;
> >>>>>>>>>> +	u32 *val = _val;
> >>>>>>>>>> +	int i;
> >>>>>>>>>> +
> >>>>>>>>>> +	for (i = 0; i < bytes / 4; i++)
> >>>>>>>>>> +		val[i] = rtc_readl(rtc,
> >>>>>>>>>> +				   OMAP_RTC_SCRATCH0_REG + offset + (i * 4));
> >>>>>>>
> >>>>>>> Can the offset be the Scratch register number instead of bytes offset?
> >>>>>>> More intuitive to me.
> >>>>>>>
> >>>>>>> So that one can request using offset as 0, 1, 2 instead of 0, 4, 8?
> >>>>>>>
> >>>>>>
> >>>>>> Well, the offset is coming from the nvmem core, itself getting it from
> >>>>>> the Linux file API (and it is in bytes). However, you have the guarantee
> >>>>>> that it will be aligned on a word, see:
> >>>>>> http://elixir.free-electrons.com/linux/latest/source/drivers/nvmem/core.c#L88
> >>>>>
> >>>>> Okay Alexandre. Thanks for clarifying. Looks good to me.
> >>>>> I have tested on AM437X-GP-EVM.
> >>>>>
> >>>>
> >>>> If needed, you can define nvmem cells (and I guess that is what you
> >>>> want):
> >>>> http://elixir.free-electrons.com/linux/latest/source/Documentation/devicetree/bindings/nvmem/nvmem.txt
> >>>
> >>> With this patch we cannot still use nvmem_device_write and
> >>> nvmem_device_read as the nvmem_device is still not registered.
> >>>
> >>> How can a driver write to scratch pad registers? Should we be calling
> >>> nvmem_register in the probe?
> >>
> >> It is not needed, it is registered by rtc_nvmem_register().
> > 
> > Let me go through the chain:
> > 
> > rtc_register_device -> __rtc_register_device --> rtc_nvmem_register ->
> > rtc_nvram_register
> > 
> > I am seeing that for rtc-omap driver there is rtc_register_device call
> 
> I mean there NO rtc_register_device call.
> 
> > and i see that rtc_nvmem_register is not getting called.
> > 
> > Should we add rtc_register_device in probe of rtc-omap?
> > 

Do you also have that patch:
https://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git/commit/?h=rtc-next&id=57072758623fa4f3019bce65e2b00f24af8dfdd7

?

> >>
> >> nvmem_device_read/write are working because that is what I'm using to
> >> implement the old sysfs ABI with rtc_nvram_read/write.
> >>

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH] rtc: omap: Support scratch registers
From: Keerthy @ 2017-11-08  8:32 UTC (permalink / raw)
  To: Alexandre Belloni; +Cc: Sekhar Nori, linux-rtc, linux-kernel, Linux OMAP List
In-Reply-To: <79f52c27-1c6a-c4ec-9635-d05eec635661@ti.com>



On Wednesday 08 November 2017 02:01 PM, Keerthy wrote:
> 
> 
> On Wednesday 08 November 2017 01:51 PM, Alexandre Belloni wrote:
>> On 08/11/2017 at 13:36:15 +0530, Keerthy wrote:
>>>
>>>
>>> On Wednesday 08 November 2017 12:46 PM, Alexandre Belloni wrote:
>>>> On 08/11/2017 at 12:38:05 +0530, Keerthy wrote:
>>>>>
>>>>>
>>>>> On Wednesday 08 November 2017 11:57 AM, Alexandre Belloni wrote:
>>>>>> Hi,
>>>>>>
>>>>>> On 08/11/2017 at 11:30:45 +0530, Keerthy wrote:
>>>>>>>>>> +static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val,
>>>>>>>>>> +				 size_t bytes)
>>>>>>>>>> +{
>>>>>>>>>> +	struct omap_rtc	*rtc = priv;
>>>>>>>>>> +	u32 *val = _val;
>>>>>>>>>> +	int i;
>>>>>>>>>> +
>>>>>>>>>> +	for (i = 0; i < bytes / 4; i++)
>>>>>>>>>> +		val[i] = rtc_readl(rtc,
>>>>>>>>>> +				   OMAP_RTC_SCRATCH0_REG + offset + (i * 4));
>>>>>>>
>>>>>>> Can the offset be the Scratch register number instead of bytes offset?
>>>>>>> More intuitive to me.
>>>>>>>
>>>>>>> So that one can request using offset as 0, 1, 2 instead of 0, 4, 8?
>>>>>>>
>>>>>>
>>>>>> Well, the offset is coming from the nvmem core, itself getting it from
>>>>>> the Linux file API (and it is in bytes). However, you have the guarantee
>>>>>> that it will be aligned on a word, see:
>>>>>> http://elixir.free-electrons.com/linux/latest/source/drivers/nvmem/core.c#L88
>>>>>
>>>>> Okay Alexandre. Thanks for clarifying. Looks good to me.
>>>>> I have tested on AM437X-GP-EVM.
>>>>>
>>>>
>>>> If needed, you can define nvmem cells (and I guess that is what you
>>>> want):
>>>> http://elixir.free-electrons.com/linux/latest/source/Documentation/devicetree/bindings/nvmem/nvmem.txt
>>>
>>> With this patch we cannot still use nvmem_device_write and
>>> nvmem_device_read as the nvmem_device is still not registered.
>>>
>>> How can a driver write to scratch pad registers? Should we be calling
>>> nvmem_register in the probe?
>>
>> It is not needed, it is registered by rtc_nvmem_register().
> 
> Let me go through the chain:
> 
> rtc_register_device -> __rtc_register_device --> rtc_nvmem_register ->
> rtc_nvram_register
> 
> I am seeing that for rtc-omap driver there is rtc_register_device call

I mean there NO rtc_register_device call.

> and i see that rtc_nvmem_register is not getting called.
> 
> Should we add rtc_register_device in probe of rtc-omap?
> 
>>
>> nvmem_device_read/write are working because that is what I'm using to
>> implement the old sysfs ABI with rtc_nvram_read/write.
>>

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox