* [PATCH 0/1] Motorola CPCAP PMIC RTC
@ 2017-02-20 7:35 Sebastian Reichel
2017-02-20 7:35 ` [PATCH 1/1] rtc: cpcap: new rtc driver Sebastian Reichel
` (3 more replies)
0 siblings, 4 replies; 20+ messages in thread
From: Sebastian Reichel @ 2017-02-20 7:35 UTC (permalink / raw)
To: Sebastian Reichel, Tony Lindgren, Alessandro Zummo, Alexandre Belloni
Cc: rtc-linux, linux-kernel
Hi,
Here is a driver for the RTC found inside of the
Motorola Droid 4 based on linux-next 2017021.
I tried to set & get the time using hwclock and
used ./tools/testing/selftests/timers/rtctest.c:
$ ./rtctest
RTC Driver Test Example.
Counting 5 update (1/sec) interrupts from reading /dev/rtc0: 1 2 3 4 5
Again, from using select(2) on /dev/rtc: 1 2 3 4 5
Current RTC date/time is 20-2-2017, 07:11:22.
Alarm time now set to 07:11:27.
Waiting 5 seconds for alarm... okay. Alarm rang.
Periodic IRQ rate is 1Hz.
Counting 20 interrupts at:
2Hz: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
4Hz: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
8Hz: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
16Hz: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
32Hz: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
64Hz: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
*** Test complete ***
I did not include a patch for omap4-droid4-xt894.dts,
since the CPCAP DT entry has not yet been added. The
following DT snippet can be used for testing on droid4:
&cpcap {
cpcap_rtc: rtc {
compatible = "motorola,cpcap-rtc";
interrupt-parent = <&cpcap>;
interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
};
};
-- Sebastian
Sebastian Reichel (1):
rtc: cpcap: new rtc driver
.../devicetree/bindings/rtc/cpcap-rtc.txt | 13 +
drivers/rtc/Kconfig | 7 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-cpcap.c | 318 +++++++++++++++++++++
4 files changed, 339 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
create mode 100644 drivers/rtc/rtc-cpcap.c
--
2.11.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 1/1] rtc: cpcap: new rtc driver
2017-02-20 7:35 [PATCH 0/1] Motorola CPCAP PMIC RTC Sebastian Reichel
@ 2017-02-20 7:35 ` Sebastian Reichel
2017-02-20 16:31 ` Tony Lindgren
2017-02-21 6:16 ` [PATCHv2] " Sebastian Reichel
` (2 subsequent siblings)
3 siblings, 1 reply; 20+ messages in thread
From: Sebastian Reichel @ 2017-02-20 7:35 UTC (permalink / raw)
To: Sebastian Reichel, Tony Lindgren, Alessandro Zummo, Alexandre Belloni
Cc: rtc-linux, linux-kernel
This driver supports the Motorola CPCAP PMIC found on
some of Motorola's mobile phones, such as the Droid 4.
Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
.../devicetree/bindings/rtc/cpcap-rtc.txt | 13 +
drivers/rtc/Kconfig | 7 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-cpcap.c | 318 +++++++++++++++++++++
4 files changed, 339 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
create mode 100644 drivers/rtc/rtc-cpcap.c
diff --git a/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
new file mode 100644
index 000000000000..2709c32baf2c
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
@@ -0,0 +1,13 @@
+Motorola CPCAP PMIC RTC
+------------------------------------
+
+Requires node properties:
+- compatible: should contain "motorola,cpcap-rtc"
+- interrupts: An interrupt specifier for alarm and 1 Hz irq
+
+Example:
+
+cpcap_rtc: rtc {
+ compatible = "motorola,cpcap-rtc";
+ interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
+};
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ee1b0e9dde79..050bec749fae 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1731,6 +1731,13 @@ config RTC_DRV_STM32
This driver can also be built as a module, if so, the module
will be called "rtc-stm32".
+config RTC_DRV_CPCAP
+ depends on MFD_CPCAP
+ tristate "Motorola CPCAP RTC"
+ help
+ Say y here for CPCAP rtc found on some Motorola phones
+ and tablets such as Droid 4.
+
comment "HID Sensor RTC drivers"
config RTC_DRV_HID_SENSOR_TIME
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index f07297b1460a..13857d2fce09 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
+obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o
obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c
new file mode 100644
index 000000000000..815beca843ce
--- /dev/null
+++ b/drivers/rtc/rtc-cpcap.c
@@ -0,0 +1,318 @@
+/*
+ * Motorola CPCAP PMIC RTC driver
+ *
+ * Based on cpcap-regulator.c from Motorola Linux kernel tree
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * Rewritten for mainline kernel
+ * - use DT
+ * - use regmap
+ * - use standard interrupt framework
+ * - use managed device resources
+ * - remove custom "secure clock daemon" helpers
+ *
+ * Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/mfd/motorola-cpcap.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#define SECS_PER_DAY 86400
+#define DAY_MASK 0x7FFF
+#define TOD1_MASK 0x00FF
+#define TOD2_MASK 0x01FF
+
+struct cpcap_time {
+ int day;
+ int tod1;
+ int tod2;
+};
+
+struct cpcap_rtc {
+ struct regmap *regmap;
+ struct rtc_device *rtc_dev;
+ u16 vendor;
+ int alarm_irq;
+ bool alarm_enabled;
+ int update_irq;
+ bool update_enabled;
+};
+
+static void cpcap2rtc_time(struct rtc_time *rtc, struct cpcap_time *cpcap)
+{
+ unsigned long int tod;
+ unsigned long int time;
+
+ tod = (cpcap->tod1 & TOD1_MASK) | ((cpcap->tod2 & TOD2_MASK) << 8);
+ time = tod + ((cpcap->day & DAY_MASK) * SECS_PER_DAY);
+
+ rtc_time_to_tm(time, rtc);
+}
+
+static void rtc2cpcap_time(struct cpcap_time *cpcap, struct rtc_time *rtc)
+{
+ unsigned long time;
+
+ rtc_tm_to_time(rtc, &time);
+
+ cpcap->day = time / SECS_PER_DAY;
+ time %= SECS_PER_DAY;
+ cpcap->tod2 = (time >> 8) & TOD2_MASK;
+ cpcap->tod1 = time & TOD1_MASK;
+}
+
+static int cpcap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct cpcap_rtc *rtc = dev_get_drvdata(dev);
+
+ if (rtc->alarm_enabled == enabled)
+ return 0;
+
+ if (enabled)
+ enable_irq(rtc->alarm_irq);
+ else
+ disable_irq(rtc->alarm_irq);
+
+ rtc->alarm_enabled = !!enabled;
+
+ return 0;
+}
+
+static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int temp_tod2;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ ret = regmap_read(rtc->regmap, CPCAP_REG_TOD2, &temp_tod2);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD1, &cpcap_tm.tod1);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD2, &cpcap_tm.tod2);
+
+ if (temp_tod2 > cpcap_tm.tod2)
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
+
+ if (ret) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ cpcap2rtc_time(tm, &cpcap_tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret = 0;
+
+ rtc = dev_get_drvdata(dev);
+
+ rtc2cpcap_time(&cpcap_tm, tm);
+
+ if (rtc->alarm_enabled)
+ disable_irq(rtc->alarm_irq);
+ if (rtc->update_enabled)
+ disable_irq(rtc->update_irq);
+
+ if (rtc->vendor == CPCAP_VENDOR_ST) {
+ /* The TOD1 and TOD2 registers MUST be written in this order
+ * for the change to properly set. */
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, cpcap_tm.tod1);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
+ TOD2_MASK, cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
+ DAY_MASK, cpcap_tm.day);
+ } else {
+ /* Clearing the upper lower 8 bits of the TOD guarantees that
+ * the upper half of TOD (TOD2) will not increment for 0xFF RTC
+ * ticks (255 seconds). During this time we can safely write
+ * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be
+ * synchronized to the exact time requested upon the final write
+ * to TOD1. */
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, 0);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
+ DAY_MASK, cpcap_tm.day);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
+ TOD2_MASK, cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, cpcap_tm.tod1);
+ }
+
+ if (rtc->update_enabled)
+ enable_irq(rtc->update_irq);
+ if (rtc->alarm_enabled)
+ enable_irq(rtc->alarm_irq);
+
+ return ret;
+}
+
+static int cpcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ alrm->enabled = rtc->alarm_enabled;
+
+ ret = regmap_read(rtc->regmap, CPCAP_REG_DAYA, &cpcap_tm.day);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA2, &cpcap_tm.tod2);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA1, &cpcap_tm.tod1);
+
+ if (ret) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ cpcap2rtc_time(&alrm->time, &cpcap_tm);
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int cpcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ rtc2cpcap_time(&cpcap_tm, &alrm->time);
+
+ if (rtc->alarm_enabled)
+ disable_irq(rtc->alarm_irq);
+
+ ret = regmap_update_bits(rtc->regmap, CPCAP_REG_DAYA, DAY_MASK,
+ cpcap_tm.day);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA2, TOD2_MASK,
+ cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA1, TOD1_MASK,
+ cpcap_tm.tod1);
+
+ if (!ret) {
+ enable_irq(rtc->alarm_irq);
+ rtc->alarm_enabled = true;
+ }
+
+ return ret;
+}
+
+static struct rtc_class_ops cpcap_rtc_ops = {
+ .read_time = cpcap_rtc_read_time,
+ .set_time = cpcap_rtc_set_time,
+ .read_alarm = cpcap_rtc_read_alarm,
+ .set_alarm = cpcap_rtc_set_alarm,
+ .alarm_irq_enable = cpcap_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t cpcap_rtc_alarm_irq(int irq, void *data)
+{
+ struct cpcap_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpcap_rtc_update_irq(int irq, void *data)
+{
+ struct cpcap_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static int cpcap_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cpcap_rtc *rtc;
+ int err;
+
+ rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!rtc->regmap)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, rtc);
+ rtc->rtc_dev = devm_rtc_device_register(dev, "cpcap_rtc",
+ &cpcap_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc->rtc_dev)) {
+ kfree(rtc);
+ return PTR_ERR(rtc->rtc_dev);
+ }
+
+ err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor);
+ if (err)
+ return err;
+
+ rtc->alarm_irq= platform_get_irq(pdev, 0);
+ err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
+ cpcap_rtc_alarm_irq, IRQ_NONE,
+ "rtc_alarm", rtc);
+ if (err) {
+ dev_err(dev, "Could not request alarm irq: %d\n", err);
+ return err;
+ }
+ disable_irq(rtc->alarm_irq);
+
+ rtc->update_irq= platform_get_irq(pdev, 1);
+ err = devm_request_threaded_irq(dev, rtc->update_irq, NULL,
+ cpcap_rtc_update_irq, IRQ_NONE,
+ "rtc_1hz", rtc);
+ if (err) {
+ dev_err(dev, "Could not request update irq: %d\n", err);
+ return err;
+ }
+ disable_irq(rtc->update_irq);
+
+ return 0;
+}
+
+static const struct of_device_id cpcap_rtc_of_match[] = {
+ { .compatible = "motorola,cpcap-rtc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpcap_rtc_of_match);
+
+static struct platform_driver cpcap_rtc_driver = {
+ .probe = cpcap_rtc_probe,
+ .driver = {
+ .name = "cpcap-rtc",
+ .of_match_table = cpcap_rtc_of_match,
+ },
+};
+
+module_platform_driver(cpcap_rtc_driver);
+
+MODULE_ALIAS("platform:cpcap-rtc");
+MODULE_DESCRIPTION("CPCAP RTC driver");
+MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
+MODULE_LICENSE("GPL");
--
2.11.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 1/1] rtc: cpcap: new rtc driver
2017-02-20 7:35 ` [PATCH 1/1] rtc: cpcap: new rtc driver Sebastian Reichel
@ 2017-02-20 16:31 ` Tony Lindgren
2017-02-20 16:38 ` Alexandre Belloni
0 siblings, 1 reply; 20+ messages in thread
From: Tony Lindgren @ 2017-02-20 16:31 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Alessandro Zummo, Alexandre Belloni, rtc-linux, linux-kernel
* Sebastian Reichel <sre@kernel.org> [170219 23:37]:
> This driver supports the Motorola CPCAP PMIC found on
> some of Motorola's mobile phones, such as the Droid 4.
Hey that's cool, works for me for reading and setting date :)
Just noticed one thing, see below.
> +Requires node properties:
> +- compatible: should contain "motorola,cpcap-rtc"
> +- interrupts: An interrupt specifier for alarm and 1 Hz irq
> +
> +Example:
> +
> +cpcap_rtc: rtc {
> + compatible = "motorola,cpcap-rtc";
> + interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
> +};
I tried rtcwake -s 3, but got the following:
rtcwake: cannot open /sys/class/rtc/rtc0/device/power/wakeup:
No such file or directory
rtcwake: /dev/rtc0 not enabled for wakeup events
Then tried adding "wakeup-source" to above but I think the driver
needs something, maybe probably just PM ops?
Regards,
Tony
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/1] rtc: cpcap: new rtc driver
2017-02-20 16:31 ` Tony Lindgren
@ 2017-02-20 16:38 ` Alexandre Belloni
2017-02-20 17:21 ` Tony Lindgren
0 siblings, 1 reply; 20+ messages in thread
From: Alexandre Belloni @ 2017-02-20 16:38 UTC (permalink / raw)
To: Tony Lindgren
Cc: Sebastian Reichel, Alessandro Zummo, rtc-linux, linux-kernel
On 20/02/2017 at 08:31:56 -0800, Tony Lindgren wrote:
> * Sebastian Reichel <sre@kernel.org> [170219 23:37]:
> > This driver supports the Motorola CPCAP PMIC found on
> > some of Motorola's mobile phones, such as the Droid 4.
>
> Hey that's cool, works for me for reading and setting date :)
> Just noticed one thing, see below.
>
> > +Requires node properties:
> > +- compatible: should contain "motorola,cpcap-rtc"
> > +- interrupts: An interrupt specifier for alarm and 1 Hz irq
> > +
> > +Example:
> > +
> > +cpcap_rtc: rtc {
> > + compatible = "motorola,cpcap-rtc";
> > + interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
> > +};
>
> I tried rtcwake -s 3, but got the following:
>
> rtcwake: cannot open /sys/class/rtc/rtc0/device/power/wakeup:
> No such file or directory
> rtcwake: /dev/rtc0 not enabled for wakeup events
>
> Then tried adding "wakeup-source" to above but I think the driver
> needs something, maybe probably just PM ops?
>
Probably device_init_wakeup(dev, 1) ?
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/1] rtc: cpcap: new rtc driver
2017-02-20 16:38 ` Alexandre Belloni
@ 2017-02-20 17:21 ` Tony Lindgren
2017-02-20 17:27 ` Tony Lindgren
0 siblings, 1 reply; 20+ messages in thread
From: Tony Lindgren @ 2017-02-20 17:21 UTC (permalink / raw)
To: Alexandre Belloni
Cc: Sebastian Reichel, Alessandro Zummo, rtc-linux, linux-kernel
* Alexandre Belloni <alexandre.belloni@free-electrons.com> [170220 08:40]:
> On 20/02/2017 at 08:31:56 -0800, Tony Lindgren wrote:
> > * Sebastian Reichel <sre@kernel.org> [170219 23:37]:
> > > This driver supports the Motorola CPCAP PMIC found on
> > > some of Motorola's mobile phones, such as the Droid 4.
> >
> > Hey that's cool, works for me for reading and setting date :)
> > Just noticed one thing, see below.
> >
> > > +Requires node properties:
> > > +- compatible: should contain "motorola,cpcap-rtc"
> > > +- interrupts: An interrupt specifier for alarm and 1 Hz irq
> > > +
> > > +Example:
> > > +
> > > +cpcap_rtc: rtc {
> > > + compatible = "motorola,cpcap-rtc";
> > > + interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
> > > +};
> >
> > I tried rtcwake -s 3, but got the following:
> >
> > rtcwake: cannot open /sys/class/rtc/rtc0/device/power/wakeup:
> > No such file or directory
> > rtcwake: /dev/rtc0 not enabled for wakeup events
> >
> > Then tried adding "wakeup-source" to above but I think the driver
> > needs something, maybe probably just PM ops?
> >
>
> Probably device_init_wakeup(dev, 1) ?
That gets us a bit closer but now produces:
rtcwake: write error
Regards,
Tony
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/1] rtc: cpcap: new rtc driver
2017-02-20 17:21 ` Tony Lindgren
@ 2017-02-20 17:27 ` Tony Lindgren
2017-02-20 19:35 ` Sebastian Reichel
0 siblings, 1 reply; 20+ messages in thread
From: Tony Lindgren @ 2017-02-20 17:27 UTC (permalink / raw)
To: Alexandre Belloni
Cc: Sebastian Reichel, Alessandro Zummo, rtc-linux, linux-kernel
* Tony Lindgren <tony@atomide.com> [170220 09:21]:
> * Alexandre Belloni <alexandre.belloni@free-electrons.com> [170220 08:40]:
> > On 20/02/2017 at 08:31:56 -0800, Tony Lindgren wrote:
> > > * Sebastian Reichel <sre@kernel.org> [170219 23:37]:
> > > > This driver supports the Motorola CPCAP PMIC found on
> > > > some of Motorola's mobile phones, such as the Droid 4.
> > >
> > > Hey that's cool, works for me for reading and setting date :)
> > > Just noticed one thing, see below.
> > >
> > > > +Requires node properties:
> > > > +- compatible: should contain "motorola,cpcap-rtc"
> > > > +- interrupts: An interrupt specifier for alarm and 1 Hz irq
> > > > +
> > > > +Example:
> > > > +
> > > > +cpcap_rtc: rtc {
> > > > + compatible = "motorola,cpcap-rtc";
> > > > + interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
> > > > +};
> > >
> > > I tried rtcwake -s 3, but got the following:
> > >
> > > rtcwake: cannot open /sys/class/rtc/rtc0/device/power/wakeup:
> > > No such file or directory
> > > rtcwake: /dev/rtc0 not enabled for wakeup events
> > >
> > > Then tried adding "wakeup-source" to above but I think the driver
> > > needs something, maybe probably just PM ops?
> > >
> >
> > Probably device_init_wakeup(dev, 1) ?
>
> That gets us a bit closer but now produces:
>
> rtcwake: write error
Oh user error.. It needs to be done with:
# rtcwake -s 3 -m mem
And with the addition of device_init_wakeup(dev, 1) to the probe
it works for me. So Sebastian, with that change, please feel free
to add:
Tested-by: Tony Lindgren <tony@atomide.com>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/1] rtc: cpcap: new rtc driver
2017-02-20 17:27 ` Tony Lindgren
@ 2017-02-20 19:35 ` Sebastian Reichel
0 siblings, 0 replies; 20+ messages in thread
From: Sebastian Reichel @ 2017-02-20 19:35 UTC (permalink / raw)
To: Tony Lindgren
Cc: Alexandre Belloni, Alessandro Zummo, rtc-linux, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 1924 bytes --]
Hi,
On Mon, Feb 20, 2017 at 09:27:37AM -0800, Tony Lindgren wrote:
> * Tony Lindgren <tony@atomide.com> [170220 09:21]:
> > * Alexandre Belloni <alexandre.belloni@free-electrons.com> [170220 08:40]:
> > > On 20/02/2017 at 08:31:56 -0800, Tony Lindgren wrote:
> > > > * Sebastian Reichel <sre@kernel.org> [170219 23:37]:
> > > > > This driver supports the Motorola CPCAP PMIC found on
> > > > > some of Motorola's mobile phones, such as the Droid 4.
> > > >
> > > > Hey that's cool, works for me for reading and setting date :)
> > > > Just noticed one thing, see below.
> > > >
> > > > > +Requires node properties:
> > > > > +- compatible: should contain "motorola,cpcap-rtc"
> > > > > +- interrupts: An interrupt specifier for alarm and 1 Hz irq
> > > > > +
> > > > > +Example:
> > > > > +
> > > > > +cpcap_rtc: rtc {
> > > > > + compatible = "motorola,cpcap-rtc";
> > > > > + interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
> > > > > +};
> > > >
> > > > I tried rtcwake -s 3, but got the following:
> > > >
> > > > rtcwake: cannot open /sys/class/rtc/rtc0/device/power/wakeup:
> > > > No such file or directory
> > > > rtcwake: /dev/rtc0 not enabled for wakeup events
> > > >
> > > > Then tried adding "wakeup-source" to above but I think the driver
> > > > needs something, maybe probably just PM ops?
> > > >
> > >
> > > Probably device_init_wakeup(dev, 1) ?
> >
> > That gets us a bit closer but now produces:
> >
> > rtcwake: write error
>
> Oh user error.. It needs to be done with:
>
> # rtcwake -s 3 -m mem
>
> And with the addition of device_init_wakeup(dev, 1) to the probe
> it works for me. So Sebastian, with that change, please feel free
> to add:
>
> Tested-by: Tony Lindgren <tony@atomide.com>
Nice. I actually forgot to mention, that its missing wakeup support
for now and now its already done :) I will resend later.
-- Sebastian
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCHv2] rtc: cpcap: new rtc driver
2017-02-20 7:35 [PATCH 0/1] Motorola CPCAP PMIC RTC Sebastian Reichel
2017-02-20 7:35 ` [PATCH 1/1] rtc: cpcap: new rtc driver Sebastian Reichel
@ 2017-02-21 6:16 ` Sebastian Reichel
2017-02-21 23:52 ` Alexandre Belloni
2017-02-23 1:03 ` [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola Sebastian Reichel
2017-03-02 0:27 ` [PATCHv4 " Sebastian Reichel
3 siblings, 1 reply; 20+ messages in thread
From: Sebastian Reichel @ 2017-02-21 6:16 UTC (permalink / raw)
To: Sebastian Reichel, Tony Lindgren, Alessandro Zummo, Alexandre Belloni
Cc: rtc-linux, linux-kernel
This driver supports the Motorola CPCAP PMIC found on
some of Motorola's mobile phones, such as the Droid 4.
Tested-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
Changes to PATCHv1:
- added device_init_wakeup() at the end of probe
- added Tested-by from Tony
---
.../devicetree/bindings/rtc/cpcap-rtc.txt | 13 +
drivers/rtc/Kconfig | 7 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-cpcap.c | 324 +++++++++++++++++++++
4 files changed, 345 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
create mode 100644 drivers/rtc/rtc-cpcap.c
diff --git a/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
new file mode 100644
index 000000000000..2709c32baf2c
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
@@ -0,0 +1,13 @@
+Motorola CPCAP PMIC RTC
+------------------------------------
+
+Requires node properties:
+- compatible: should contain "motorola,cpcap-rtc"
+- interrupts: An interrupt specifier for alarm and 1 Hz irq
+
+Example:
+
+cpcap_rtc: rtc {
+ compatible = "motorola,cpcap-rtc";
+ interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
+};
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ee1b0e9dde79..050bec749fae 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1731,6 +1731,13 @@ config RTC_DRV_STM32
This driver can also be built as a module, if so, the module
will be called "rtc-stm32".
+config RTC_DRV_CPCAP
+ depends on MFD_CPCAP
+ tristate "Motorola CPCAP RTC"
+ help
+ Say y here for CPCAP rtc found on some Motorola phones
+ and tablets such as Droid 4.
+
comment "HID Sensor RTC drivers"
config RTC_DRV_HID_SENSOR_TIME
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index f07297b1460a..13857d2fce09 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
+obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o
obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c
new file mode 100644
index 000000000000..d24c205b70e8
--- /dev/null
+++ b/drivers/rtc/rtc-cpcap.c
@@ -0,0 +1,324 @@
+/*
+ * Motorola CPCAP PMIC RTC driver
+ *
+ * Based on cpcap-regulator.c from Motorola Linux kernel tree
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * Rewritten for mainline kernel
+ * - use DT
+ * - use regmap
+ * - use standard interrupt framework
+ * - use managed device resources
+ * - remove custom "secure clock daemon" helpers
+ *
+ * Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/mfd/motorola-cpcap.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#define SECS_PER_DAY 86400
+#define DAY_MASK 0x7FFF
+#define TOD1_MASK 0x00FF
+#define TOD2_MASK 0x01FF
+
+struct cpcap_time {
+ int day;
+ int tod1;
+ int tod2;
+};
+
+struct cpcap_rtc {
+ struct regmap *regmap;
+ struct rtc_device *rtc_dev;
+ u16 vendor;
+ int alarm_irq;
+ bool alarm_enabled;
+ int update_irq;
+ bool update_enabled;
+};
+
+static void cpcap2rtc_time(struct rtc_time *rtc, struct cpcap_time *cpcap)
+{
+ unsigned long int tod;
+ unsigned long int time;
+
+ tod = (cpcap->tod1 & TOD1_MASK) | ((cpcap->tod2 & TOD2_MASK) << 8);
+ time = tod + ((cpcap->day & DAY_MASK) * SECS_PER_DAY);
+
+ rtc_time_to_tm(time, rtc);
+}
+
+static void rtc2cpcap_time(struct cpcap_time *cpcap, struct rtc_time *rtc)
+{
+ unsigned long time;
+
+ rtc_tm_to_time(rtc, &time);
+
+ cpcap->day = time / SECS_PER_DAY;
+ time %= SECS_PER_DAY;
+ cpcap->tod2 = (time >> 8) & TOD2_MASK;
+ cpcap->tod1 = time & TOD1_MASK;
+}
+
+static int cpcap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct cpcap_rtc *rtc = dev_get_drvdata(dev);
+
+ if (rtc->alarm_enabled == enabled)
+ return 0;
+
+ if (enabled)
+ enable_irq(rtc->alarm_irq);
+ else
+ disable_irq(rtc->alarm_irq);
+
+ rtc->alarm_enabled = !!enabled;
+
+ return 0;
+}
+
+static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int temp_tod2;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ ret = regmap_read(rtc->regmap, CPCAP_REG_TOD2, &temp_tod2);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD1, &cpcap_tm.tod1);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD2, &cpcap_tm.tod2);
+
+ if (temp_tod2 > cpcap_tm.tod2)
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
+
+ if (ret) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ cpcap2rtc_time(tm, &cpcap_tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret = 0;
+
+ rtc = dev_get_drvdata(dev);
+
+ rtc2cpcap_time(&cpcap_tm, tm);
+
+ if (rtc->alarm_enabled)
+ disable_irq(rtc->alarm_irq);
+ if (rtc->update_enabled)
+ disable_irq(rtc->update_irq);
+
+ if (rtc->vendor == CPCAP_VENDOR_ST) {
+ /* The TOD1 and TOD2 registers MUST be written in this order
+ * for the change to properly set. */
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, cpcap_tm.tod1);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
+ TOD2_MASK, cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
+ DAY_MASK, cpcap_tm.day);
+ } else {
+ /* Clearing the upper lower 8 bits of the TOD guarantees that
+ * the upper half of TOD (TOD2) will not increment for 0xFF RTC
+ * ticks (255 seconds). During this time we can safely write
+ * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be
+ * synchronized to the exact time requested upon the final write
+ * to TOD1. */
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, 0);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
+ DAY_MASK, cpcap_tm.day);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
+ TOD2_MASK, cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, cpcap_tm.tod1);
+ }
+
+ if (rtc->update_enabled)
+ enable_irq(rtc->update_irq);
+ if (rtc->alarm_enabled)
+ enable_irq(rtc->alarm_irq);
+
+ return ret;
+}
+
+static int cpcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ alrm->enabled = rtc->alarm_enabled;
+
+ ret = regmap_read(rtc->regmap, CPCAP_REG_DAYA, &cpcap_tm.day);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA2, &cpcap_tm.tod2);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA1, &cpcap_tm.tod1);
+
+ if (ret) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ cpcap2rtc_time(&alrm->time, &cpcap_tm);
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int cpcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ rtc2cpcap_time(&cpcap_tm, &alrm->time);
+
+ if (rtc->alarm_enabled)
+ disable_irq(rtc->alarm_irq);
+
+ ret = regmap_update_bits(rtc->regmap, CPCAP_REG_DAYA, DAY_MASK,
+ cpcap_tm.day);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA2, TOD2_MASK,
+ cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA1, TOD1_MASK,
+ cpcap_tm.tod1);
+
+ if (!ret) {
+ enable_irq(rtc->alarm_irq);
+ rtc->alarm_enabled = true;
+ }
+
+ return ret;
+}
+
+static struct rtc_class_ops cpcap_rtc_ops = {
+ .read_time = cpcap_rtc_read_time,
+ .set_time = cpcap_rtc_set_time,
+ .read_alarm = cpcap_rtc_read_alarm,
+ .set_alarm = cpcap_rtc_set_alarm,
+ .alarm_irq_enable = cpcap_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t cpcap_rtc_alarm_irq(int irq, void *data)
+{
+ struct cpcap_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpcap_rtc_update_irq(int irq, void *data)
+{
+ struct cpcap_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static int cpcap_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cpcap_rtc *rtc;
+ int err;
+
+ rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!rtc->regmap)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, rtc);
+ rtc->rtc_dev = devm_rtc_device_register(dev, "cpcap_rtc",
+ &cpcap_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc->rtc_dev)) {
+ kfree(rtc);
+ return PTR_ERR(rtc->rtc_dev);
+ }
+
+ err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor);
+ if (err)
+ return err;
+
+ rtc->alarm_irq= platform_get_irq(pdev, 0);
+ err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
+ cpcap_rtc_alarm_irq, IRQ_NONE,
+ "rtc_alarm", rtc);
+ if (err) {
+ dev_err(dev, "Could not request alarm irq: %d\n", err);
+ return err;
+ }
+ disable_irq(rtc->alarm_irq);
+
+ rtc->update_irq= platform_get_irq(pdev, 1);
+ err = devm_request_threaded_irq(dev, rtc->update_irq, NULL,
+ cpcap_rtc_update_irq, IRQ_NONE,
+ "rtc_1hz", rtc);
+ if (err) {
+ dev_err(dev, "Could not request update irq: %d\n", err);
+ return err;
+ }
+ disable_irq(rtc->update_irq);
+
+ err = device_init_wakeup(dev, 1);
+ if (err) {
+ dev_err(dev, "wakeup initialization failed (%d)\n", err);
+ /* ignore error and continue without wakeup support */
+ }
+
+ return 0;
+}
+
+static const struct of_device_id cpcap_rtc_of_match[] = {
+ { .compatible = "motorola,cpcap-rtc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpcap_rtc_of_match);
+
+static struct platform_driver cpcap_rtc_driver = {
+ .probe = cpcap_rtc_probe,
+ .driver = {
+ .name = "cpcap-rtc",
+ .of_match_table = cpcap_rtc_of_match,
+ },
+};
+
+module_platform_driver(cpcap_rtc_driver);
+
+MODULE_ALIAS("platform:cpcap-rtc");
+MODULE_DESCRIPTION("CPCAP RTC driver");
+MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
+MODULE_LICENSE("GPL");
--
2.11.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCHv2] rtc: cpcap: new rtc driver
2017-02-21 6:16 ` [PATCHv2] " Sebastian Reichel
@ 2017-02-21 23:52 ` Alexandre Belloni
2017-02-22 1:56 ` Sebastian Reichel
0 siblings, 1 reply; 20+ messages in thread
From: Alexandre Belloni @ 2017-02-21 23:52 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Tony Lindgren, Alessandro Zummo, rtc-linux, linux-kernel
Hi,
The patch has a few checkpatch issues. Some of those should really be
fixed. Can you do that?
Else, it is mostly fine, a few comments below.
On 21/02/2017 at 07:16:50 +0100, Sebastian Reichel wrote:
> +static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct cpcap_rtc *rtc;
> + struct cpcap_time cpcap_tm;
> + int ret = 0;
> +
> + rtc = dev_get_drvdata(dev);
> +
> + rtc2cpcap_time(&cpcap_tm, tm);
> +
> + if (rtc->alarm_enabled)
> + disable_irq(rtc->alarm_irq);
> + if (rtc->update_enabled)
> + disable_irq(rtc->update_irq);
> +
> + if (rtc->vendor == CPCAP_VENDOR_ST) {
> + /* The TOD1 and TOD2 registers MUST be written in this order
> + * for the change to properly set. */
Does this mean there is a race condition?
> + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
> + TOD1_MASK, cpcap_tm.tod1);
> + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
> + TOD2_MASK, cpcap_tm.tod2);
> + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
> + DAY_MASK, cpcap_tm.day);
> + } else {
> + /* Clearing the upper lower 8 bits of the TOD guarantees that
> + * the upper half of TOD (TOD2) will not increment for 0xFF RTC
> + * ticks (255 seconds). During this time we can safely write
> + * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be
> + * synchronized to the exact time requested upon the final write
> + * to TOD1. */
> + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
> + TOD1_MASK, 0);
> + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
> + DAY_MASK, cpcap_tm.day);
> + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
> + TOD2_MASK, cpcap_tm.tod2);
> + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
> + TOD1_MASK, cpcap_tm.tod1);
> + }
> +
> + err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor);
I think this means it depends on the mfd tree.
> + if (err)
> + return err;
> +
> + rtc->alarm_irq= platform_get_irq(pdev, 0);
> + err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
> + cpcap_rtc_alarm_irq, IRQ_NONE,
> + "rtc_alarm", rtc);
> + if (err) {
> + dev_err(dev, "Could not request alarm irq: %d\n", err);
> + return err;
> + }
> + disable_irq(rtc->alarm_irq);
> +
> + rtc->update_irq= platform_get_irq(pdev, 1);
> + err = devm_request_threaded_irq(dev, rtc->update_irq, NULL,
> + cpcap_rtc_update_irq, IRQ_NONE,
> + "rtc_1hz", rtc);
I don't think this IRQ is actually useful. It doesn't really harm but
the tests should pass without it.
> + if (err) {
> + dev_err(dev, "Could not request update irq: %d\n", err);
> + return err;
> + }
> + disable_irq(rtc->update_irq);
> +
> + err = device_init_wakeup(dev, 1);
If you use device_init_wakeup, I think it needs to be called before
devm_rtc_device_register() to properly work.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCHv2] rtc: cpcap: new rtc driver
2017-02-21 23:52 ` Alexandre Belloni
@ 2017-02-22 1:56 ` Sebastian Reichel
2017-02-22 8:18 ` Alexandre Belloni
0 siblings, 1 reply; 20+ messages in thread
From: Sebastian Reichel @ 2017-02-22 1:56 UTC (permalink / raw)
To: Alexandre Belloni
Cc: Tony Lindgren, Alessandro Zummo, rtc-linux, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 4169 bytes --]
Hi Alexandre,
On Wed, Feb 22, 2017 at 12:52:12AM +0100, Alexandre Belloni wrote:
> The patch has a few checkpatch issues. Some of those should really be
> fixed. Can you do that?
of course.
> Else, it is mostly fine, a few comments below.
>
> On 21/02/2017 at 07:16:50 +0100, Sebastian Reichel wrote:
> > +static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
> > +{
> > + struct cpcap_rtc *rtc;
> > + struct cpcap_time cpcap_tm;
> > + int ret = 0;
> > +
> > + rtc = dev_get_drvdata(dev);
> > +
> > + rtc2cpcap_time(&cpcap_tm, tm);
> > +
> > + if (rtc->alarm_enabled)
> > + disable_irq(rtc->alarm_irq);
> > + if (rtc->update_enabled)
> > + disable_irq(rtc->update_irq);
> > +
> > + if (rtc->vendor == CPCAP_VENDOR_ST) {
> > + /* The TOD1 and TOD2 registers MUST be written in this order
> > + * for the change to properly set. */
>
> Does this mean there is a race condition?
The logic (incl. comments) in this section are from the vendor
kernel driver and there is no documentation for CPCAP as far as
I know. I don't know if the hardware has logic to prevent a race
condition for the cpcap_tm.tod1 == 255 case.
> > + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
> > + TOD1_MASK, cpcap_tm.tod1);
> > + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
> > + TOD2_MASK, cpcap_tm.tod2);
> > + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
> > + DAY_MASK, cpcap_tm.day);
> > + } else {
> > + /* Clearing the upper lower 8 bits of the TOD guarantees that
> > + * the upper half of TOD (TOD2) will not increment for 0xFF RTC
> > + * ticks (255 seconds). During this time we can safely write
> > + * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be
> > + * synchronized to the exact time requested upon the final write
> > + * to TOD1. */
> > + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
> > + TOD1_MASK, 0);
> > + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
> > + DAY_MASK, cpcap_tm.day);
> > + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
> > + TOD2_MASK, cpcap_tm.tod2);
> > + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
> > + TOD1_MASK, cpcap_tm.tod1);
> > + }
> > +
>
> > + err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor);
> I think this means it depends on the mfd tree.
Yes, but cpcap_get_vendor should get into mainline with the
4.11 mfd pull request. So if you base your 4.12 for-next tree
on 4.11-rc1 everything should be fine.
> > + if (err)
> > + return err;
> > +
> > + rtc->alarm_irq= platform_get_irq(pdev, 0);
> > + err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
> > + cpcap_rtc_alarm_irq, IRQ_NONE,
> > + "rtc_alarm", rtc);
> > + if (err) {
> > + dev_err(dev, "Could not request alarm irq: %d\n", err);
> > + return err;
> > + }
> > + disable_irq(rtc->alarm_irq);
> > +
> > + rtc->update_irq= platform_get_irq(pdev, 1);
> > + err = devm_request_threaded_irq(dev, rtc->update_irq, NULL,
> > + cpcap_rtc_update_irq, IRQ_NONE,
> > + "rtc_1hz", rtc);
> I don't think this IRQ is actually useful. It doesn't really harm but
> the tests should pass without it.
Yes. RTC works perfectly fine with just the alarm irq. It also
works perfectly fine with just the 1 Hz irq (except for wakeup).
I would like to keep the irq in the driver, so that it's explicitly
disabled. On Droid 4 mainline kernel is booted via kexec from
Android (AKA bootloader) and Motorola's Android kernel uses the
1 Hz IRQ for some proprietary "secure clock daemon".
I will add a comment.
> > + if (err) {
> > + dev_err(dev, "Could not request update irq: %d\n", err);
> > + return err;
> > + }
> > + disable_irq(rtc->update_irq);
> > +
> > + err = device_init_wakeup(dev, 1);
>
> If you use device_init_wakeup, I think it needs to be called before
> devm_rtc_device_register() to properly work.
I successfully tested wakeup before sending this. But in case your
prefer it to be called before registering the RTC I can move the
call accordingly.
-- Sebastian
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCHv2] rtc: cpcap: new rtc driver
2017-02-22 1:56 ` Sebastian Reichel
@ 2017-02-22 8:18 ` Alexandre Belloni
0 siblings, 0 replies; 20+ messages in thread
From: Alexandre Belloni @ 2017-02-22 8:18 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Tony Lindgren, Alessandro Zummo, rtc-linux, linux-kernel
On 22/02/2017 at 02:56:34 +0100, Sebastian Reichel wrote:
> > Does this mean there is a race condition?
>
> The logic (incl. comments) in this section are from the vendor
> kernel driver and there is no documentation for CPCAP as far as
> I know. I don't know if the hardware has logic to prevent a race
> condition for the cpcap_tm.tod1 == 255 case.
>
That's fine, I was just curious :)
> > > + err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor);
> > I think this means it depends on the mfd tree.
>
> Yes, but cpcap_get_vendor should get into mainline with the
> 4.11 mfd pull request. So if you base your 4.12 for-next tree
> on 4.11-rc1 everything should be fine.
>
OK, I'll take it for 4.12 then
> > > + if (err)
> > > + return err;
> > > +
> > > + rtc->alarm_irq= platform_get_irq(pdev, 0);
> > > + err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
> > > + cpcap_rtc_alarm_irq, IRQ_NONE,
> > > + "rtc_alarm", rtc);
> > > + if (err) {
> > > + dev_err(dev, "Could not request alarm irq: %d\n", err);
> > > + return err;
> > > + }
> > > + disable_irq(rtc->alarm_irq);
> > > +
> > > + rtc->update_irq= platform_get_irq(pdev, 1);
> > > + err = devm_request_threaded_irq(dev, rtc->update_irq, NULL,
> > > + cpcap_rtc_update_irq, IRQ_NONE,
> > > + "rtc_1hz", rtc);
>
> > I don't think this IRQ is actually useful. It doesn't really harm but
> > the tests should pass without it.
>
> Yes. RTC works perfectly fine with just the alarm irq. It also
> works perfectly fine with just the 1 Hz irq (except for wakeup).
>
> I would like to keep the irq in the driver, so that it's explicitly
> disabled. On Droid 4 mainline kernel is booted via kexec from
> Android (AKA bootloader) and Motorola's Android kernel uses the
> 1 Hz IRQ for some proprietary "secure clock daemon".
>
> I will add a comment.
>
OK, my plan was to remove all the RTC_UF users. I'll give it more
thoughts.
> > > + if (err) {
> > > + dev_err(dev, "Could not request update irq: %d\n", err);
> > > + return err;
> > > + }
> > > + disable_irq(rtc->update_irq);
> > > +
> > > + err = device_init_wakeup(dev, 1);
> >
> > If you use device_init_wakeup, I think it needs to be called before
> > devm_rtc_device_register() to properly work.
>
> I successfully tested wakeup before sending this. But in case your
> prefer it to be called before registering the RTC I can move the
> call accordingly.
>
Then it is fine where it is.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola
2017-02-20 7:35 [PATCH 0/1] Motorola CPCAP PMIC RTC Sebastian Reichel
2017-02-20 7:35 ` [PATCH 1/1] rtc: cpcap: new rtc driver Sebastian Reichel
2017-02-21 6:16 ` [PATCHv2] " Sebastian Reichel
@ 2017-02-23 1:03 ` Sebastian Reichel
2017-02-23 1:03 ` [PATCHv3 2/2] rtc: cpcap: new rtc driver Sebastian Reichel
2017-02-27 23:48 ` [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola Rob Herring
2017-03-02 0:27 ` [PATCHv4 " Sebastian Reichel
3 siblings, 2 replies; 20+ messages in thread
From: Sebastian Reichel @ 2017-02-23 1:03 UTC (permalink / raw)
To: Sebastian Reichel, Tony Lindgren, Alessandro Zummo, Alexandre Belloni
Cc: Rob Herring, Mark Rutland, rtc-linux, devicetree, linux-kernel
Motorola was involved in semiconductor and mobile phone business.
The "motrola," prefix is already used by a couple of bindings:
* rtc/rtc-cmos.txt
* mfd/motorola-cpcap.txt
* regulator/cpcap-regulator.txt
Apart from that it is used in the DT file for the Droid 4 mobile
phone.
Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 1e4e84ee6a3d..1a21b378fa91 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -189,6 +189,7 @@ minix MINIX Technology Ltd.
miramems MiraMEMS Sensing Technology Co., Ltd.
mitsubishi Mitsubishi Electric Corporation
mosaixtech Mosaix Technologies, Inc.
+motorola Motorola, Inc.
moxa Moxa
mpl MPL AG
mqmaker mqmaker Inc.
--
2.11.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCHv3 2/2] rtc: cpcap: new rtc driver
2017-02-23 1:03 ` [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola Sebastian Reichel
@ 2017-02-23 1:03 ` Sebastian Reichel
2017-02-27 23:49 ` Rob Herring
2017-02-27 23:48 ` [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola Rob Herring
1 sibling, 1 reply; 20+ messages in thread
From: Sebastian Reichel @ 2017-02-23 1:03 UTC (permalink / raw)
To: Sebastian Reichel, Tony Lindgren, Alessandro Zummo, Alexandre Belloni
Cc: Rob Herring, Mark Rutland, rtc-linux, devicetree, linux-kernel
This driver supports the Motorola CPCAP PMIC found on
some of Motorola's mobile phones, such as the Droid 4.
Tested-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
Changes since PATCHv2:
- checkpatch fixes
- add comment for update irq
- introduced vendor prefix patch
Changes since PATCHv1:
- added device_init_wakeup() at the end of probe
- added Tested-by from Tony
---
.../devicetree/bindings/rtc/cpcap-rtc.txt | 13 +
drivers/rtc/Kconfig | 7 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-cpcap.c | 332 +++++++++++++++++++++
4 files changed, 353 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
create mode 100644 drivers/rtc/rtc-cpcap.c
diff --git a/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
new file mode 100644
index 000000000000..2709c32baf2c
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
@@ -0,0 +1,13 @@
+Motorola CPCAP PMIC RTC
+------------------------------------
+
+Requires node properties:
+- compatible: should contain "motorola,cpcap-rtc"
+- interrupts: An interrupt specifier for alarm and 1 Hz irq
+
+Example:
+
+cpcap_rtc: rtc {
+ compatible = "motorola,cpcap-rtc";
+ interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
+};
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ee1b0e9dde79..050bec749fae 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1731,6 +1731,13 @@ config RTC_DRV_STM32
This driver can also be built as a module, if so, the module
will be called "rtc-stm32".
+config RTC_DRV_CPCAP
+ depends on MFD_CPCAP
+ tristate "Motorola CPCAP RTC"
+ help
+ Say y here for CPCAP rtc found on some Motorola phones
+ and tablets such as Droid 4.
+
comment "HID Sensor RTC drivers"
config RTC_DRV_HID_SENSOR_TIME
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index f07297b1460a..13857d2fce09 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
+obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o
obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c
new file mode 100644
index 000000000000..7c6a3c3167bd
--- /dev/null
+++ b/drivers/rtc/rtc-cpcap.c
@@ -0,0 +1,332 @@
+/*
+ * Motorola CPCAP PMIC RTC driver
+ *
+ * Based on cpcap-regulator.c from Motorola Linux kernel tree
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * Rewritten for mainline kernel
+ * - use DT
+ * - use regmap
+ * - use standard interrupt framework
+ * - use managed device resources
+ * - remove custom "secure clock daemon" helpers
+ *
+ * Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/mfd/motorola-cpcap.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#define SECS_PER_DAY 86400
+#define DAY_MASK 0x7FFF
+#define TOD1_MASK 0x00FF
+#define TOD2_MASK 0x01FF
+
+struct cpcap_time {
+ int day;
+ int tod1;
+ int tod2;
+};
+
+struct cpcap_rtc {
+ struct regmap *regmap;
+ struct rtc_device *rtc_dev;
+ u16 vendor;
+ int alarm_irq;
+ bool alarm_enabled;
+ int update_irq;
+ bool update_enabled;
+};
+
+static void cpcap2rtc_time(struct rtc_time *rtc, struct cpcap_time *cpcap)
+{
+ unsigned long int tod;
+ unsigned long int time;
+
+ tod = (cpcap->tod1 & TOD1_MASK) | ((cpcap->tod2 & TOD2_MASK) << 8);
+ time = tod + ((cpcap->day & DAY_MASK) * SECS_PER_DAY);
+
+ rtc_time_to_tm(time, rtc);
+}
+
+static void rtc2cpcap_time(struct cpcap_time *cpcap, struct rtc_time *rtc)
+{
+ unsigned long time;
+
+ rtc_tm_to_time(rtc, &time);
+
+ cpcap->day = time / SECS_PER_DAY;
+ time %= SECS_PER_DAY;
+ cpcap->tod2 = (time >> 8) & TOD2_MASK;
+ cpcap->tod1 = time & TOD1_MASK;
+}
+
+static int cpcap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct cpcap_rtc *rtc = dev_get_drvdata(dev);
+
+ if (rtc->alarm_enabled == enabled)
+ return 0;
+
+ if (enabled)
+ enable_irq(rtc->alarm_irq);
+ else
+ disable_irq(rtc->alarm_irq);
+
+ rtc->alarm_enabled = !!enabled;
+
+ return 0;
+}
+
+static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int temp_tod2;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ ret = regmap_read(rtc->regmap, CPCAP_REG_TOD2, &temp_tod2);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD1, &cpcap_tm.tod1);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD2, &cpcap_tm.tod2);
+
+ if (temp_tod2 > cpcap_tm.tod2)
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
+
+ if (ret) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ cpcap2rtc_time(tm, &cpcap_tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret = 0;
+
+ rtc = dev_get_drvdata(dev);
+
+ rtc2cpcap_time(&cpcap_tm, tm);
+
+ if (rtc->alarm_enabled)
+ disable_irq(rtc->alarm_irq);
+ if (rtc->update_enabled)
+ disable_irq(rtc->update_irq);
+
+ if (rtc->vendor == CPCAP_VENDOR_ST) {
+ /* The TOD1 and TOD2 registers MUST be written in this order
+ * for the change to properly set.
+ */
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, cpcap_tm.tod1);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
+ TOD2_MASK, cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
+ DAY_MASK, cpcap_tm.day);
+ } else {
+ /* Clearing the upper lower 8 bits of the TOD guarantees that
+ * the upper half of TOD (TOD2) will not increment for 0xFF RTC
+ * ticks (255 seconds). During this time we can safely write
+ * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be
+ * synchronized to the exact time requested upon the final write
+ * to TOD1.
+ */
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, 0);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
+ DAY_MASK, cpcap_tm.day);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
+ TOD2_MASK, cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, cpcap_tm.tod1);
+ }
+
+ if (rtc->update_enabled)
+ enable_irq(rtc->update_irq);
+ if (rtc->alarm_enabled)
+ enable_irq(rtc->alarm_irq);
+
+ return ret;
+}
+
+static int cpcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ alrm->enabled = rtc->alarm_enabled;
+
+ ret = regmap_read(rtc->regmap, CPCAP_REG_DAYA, &cpcap_tm.day);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA2, &cpcap_tm.tod2);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA1, &cpcap_tm.tod1);
+
+ if (ret) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ cpcap2rtc_time(&alrm->time, &cpcap_tm);
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int cpcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ rtc2cpcap_time(&cpcap_tm, &alrm->time);
+
+ if (rtc->alarm_enabled)
+ disable_irq(rtc->alarm_irq);
+
+ ret = regmap_update_bits(rtc->regmap, CPCAP_REG_DAYA, DAY_MASK,
+ cpcap_tm.day);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA2, TOD2_MASK,
+ cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA1, TOD1_MASK,
+ cpcap_tm.tod1);
+
+ if (!ret) {
+ enable_irq(rtc->alarm_irq);
+ rtc->alarm_enabled = true;
+ }
+
+ return ret;
+}
+
+static const struct rtc_class_ops cpcap_rtc_ops = {
+ .read_time = cpcap_rtc_read_time,
+ .set_time = cpcap_rtc_set_time,
+ .read_alarm = cpcap_rtc_read_alarm,
+ .set_alarm = cpcap_rtc_set_alarm,
+ .alarm_irq_enable = cpcap_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t cpcap_rtc_alarm_irq(int irq, void *data)
+{
+ struct cpcap_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpcap_rtc_update_irq(int irq, void *data)
+{
+ struct cpcap_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static int cpcap_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cpcap_rtc *rtc;
+ int err;
+
+ rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!rtc->regmap)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, rtc);
+ rtc->rtc_dev = devm_rtc_device_register(dev, "cpcap_rtc",
+ &cpcap_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc->rtc_dev)) {
+ kfree(rtc);
+ return PTR_ERR(rtc->rtc_dev);
+ }
+
+ err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor);
+ if (err)
+ return err;
+
+ rtc->alarm_irq = platform_get_irq(pdev, 0);
+ err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
+ cpcap_rtc_alarm_irq, IRQ_NONE,
+ "rtc_alarm", rtc);
+ if (err) {
+ dev_err(dev, "Could not request alarm irq: %d\n", err);
+ return err;
+ }
+ disable_irq(rtc->alarm_irq);
+
+ /* Stock Android uses the 1 Hz interrupt for "secure clock daemon",
+ * which is not supported by the mainline kernel. The mainline kernel
+ * does not use the irq at the moment, but we explicitly request and
+ * disable it, so that its masked and does not wake up the processor
+ * every second.
+ */
+ rtc->update_irq = platform_get_irq(pdev, 1);
+ err = devm_request_threaded_irq(dev, rtc->update_irq, NULL,
+ cpcap_rtc_update_irq, IRQ_NONE,
+ "rtc_1hz", rtc);
+ if (err) {
+ dev_err(dev, "Could not request update irq: %d\n", err);
+ return err;
+ }
+ disable_irq(rtc->update_irq);
+
+ err = device_init_wakeup(dev, 1);
+ if (err) {
+ dev_err(dev, "wakeup initialization failed (%d)\n", err);
+ /* ignore error and continue without wakeup support */
+ }
+
+ return 0;
+}
+
+static const struct of_device_id cpcap_rtc_of_match[] = {
+ { .compatible = "motorola,cpcap-rtc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpcap_rtc_of_match);
+
+static struct platform_driver cpcap_rtc_driver = {
+ .probe = cpcap_rtc_probe,
+ .driver = {
+ .name = "cpcap-rtc",
+ .of_match_table = cpcap_rtc_of_match,
+ },
+};
+
+module_platform_driver(cpcap_rtc_driver);
+
+MODULE_ALIAS("platform:cpcap-rtc");
+MODULE_DESCRIPTION("CPCAP RTC driver");
+MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
+MODULE_LICENSE("GPL");
--
2.11.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola
2017-02-23 1:03 ` [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola Sebastian Reichel
2017-02-23 1:03 ` [PATCHv3 2/2] rtc: cpcap: new rtc driver Sebastian Reichel
@ 2017-02-27 23:48 ` Rob Herring
1 sibling, 0 replies; 20+ messages in thread
From: Rob Herring @ 2017-02-27 23:48 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Tony Lindgren, Alessandro Zummo, Alexandre Belloni, Mark Rutland,
rtc-linux, devicetree, linux-kernel
On Thu, Feb 23, 2017 at 02:03:52AM +0100, Sebastian Reichel wrote:
> Motorola was involved in semiconductor and mobile phone business.
> The "motrola," prefix is already used by a couple of bindings:
typo.
>
> * rtc/rtc-cmos.txt
> * mfd/motorola-cpcap.txt
> * regulator/cpcap-regulator.txt
>
> Apart from that it is used in the DT file for the Droid 4 mobile
> phone.
>
> Signed-off-by: Sebastian Reichel <sre@kernel.org>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> 1 file changed, 1 insertion(+)
Otherwise,
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCHv3 2/2] rtc: cpcap: new rtc driver
2017-02-23 1:03 ` [PATCHv3 2/2] rtc: cpcap: new rtc driver Sebastian Reichel
@ 2017-02-27 23:49 ` Rob Herring
0 siblings, 0 replies; 20+ messages in thread
From: Rob Herring @ 2017-02-27 23:49 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Tony Lindgren, Alessandro Zummo, Alexandre Belloni, Mark Rutland,
rtc-linux, devicetree, linux-kernel
On Thu, Feb 23, 2017 at 02:03:53AM +0100, Sebastian Reichel wrote:
> This driver supports the Motorola CPCAP PMIC found on
> some of Motorola's mobile phones, such as the Droid 4.
>
> Tested-by: Tony Lindgren <tony@atomide.com>
> Signed-off-by: Sebastian Reichel <sre@kernel.org>
> ---
>
> Changes since PATCHv2:
> - checkpatch fixes
> - add comment for update irq
> - introduced vendor prefix patch
> Changes since PATCHv1:
> - added device_init_wakeup() at the end of probe
> - added Tested-by from Tony
>
> ---
> .../devicetree/bindings/rtc/cpcap-rtc.txt | 13 +
> drivers/rtc/Kconfig | 7 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-cpcap.c | 332 +++++++++++++++++++++
> 4 files changed, 353 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
> create mode 100644 drivers/rtc/rtc-cpcap.c
>
> diff --git a/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
> new file mode 100644
> index 000000000000..2709c32baf2c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
> @@ -0,0 +1,13 @@
> +Motorola CPCAP PMIC RTC
> +------------------------------------
Need to state what this is a child of.
> +
> +Requires node properties:
> +- compatible: should contain "motorola,cpcap-rtc"
> +- interrupts: An interrupt specifier for alarm and 1 Hz irq
> +
> +Example:
> +
> +cpcap_rtc: rtc {
> + compatible = "motorola,cpcap-rtc";
> + interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
> +};
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCHv4 1/2] dt-bindings: Add vendor prefix for Motorola
2017-02-20 7:35 [PATCH 0/1] Motorola CPCAP PMIC RTC Sebastian Reichel
` (2 preceding siblings ...)
2017-02-23 1:03 ` [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola Sebastian Reichel
@ 2017-03-02 0:27 ` Sebastian Reichel
2017-03-02 0:27 ` [PATCHv4 2/2] rtc: cpcap: new rtc driver Sebastian Reichel
2017-03-09 0:33 ` [PATCHv4 1/2] dt-bindings: Add vendor prefix for Motorola Alexandre Belloni
3 siblings, 2 replies; 20+ messages in thread
From: Sebastian Reichel @ 2017-03-02 0:27 UTC (permalink / raw)
To: Sebastian Reichel, Tony Lindgren, Alessandro Zummo, Alexandre Belloni
Cc: Rob Herring, Mark Rutland, rtc-linux, devicetree, linux-kernel
Motorola was involved in semiconductor and mobile phone business.
The "motorola," prefix is already used by a couple of bindings:
* rtc/rtc-cmos.txt
* mfd/motorola-cpcap.txt
* regulator/cpcap-regulator.txt
Apart from that it is used in the DT file for the Droid 4 mobile
phone.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
Changes since PATCHv3:
- Fix typo
- Add Acked-By from Rob Herring
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index bd0ed3cb4994..81b538fbad22 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -190,6 +190,7 @@ minix MINIX Technology Ltd.
miramems MiraMEMS Sensing Technology Co., Ltd.
mitsubishi Mitsubishi Electric Corporation
mosaixtech Mosaix Technologies, Inc.
+motorola Motorola, Inc.
moxa Moxa
mpl MPL AG
mqmaker mqmaker Inc.
--
2.11.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCHv4 2/2] rtc: cpcap: new rtc driver
2017-03-02 0:27 ` [PATCHv4 " Sebastian Reichel
@ 2017-03-02 0:27 ` Sebastian Reichel
2017-03-02 14:11 ` Rob Herring
2017-03-09 0:34 ` Alexandre Belloni
2017-03-09 0:33 ` [PATCHv4 1/2] dt-bindings: Add vendor prefix for Motorola Alexandre Belloni
1 sibling, 2 replies; 20+ messages in thread
From: Sebastian Reichel @ 2017-03-02 0:27 UTC (permalink / raw)
To: Sebastian Reichel, Tony Lindgren, Alessandro Zummo, Alexandre Belloni
Cc: Rob Herring, Mark Rutland, rtc-linux, devicetree, linux-kernel
This driver supports the Motorola CPCAP PMIC found on
some of Motorola's mobile phones, such as the Droid 4.
Tested-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
Changes since PATCHv3:
- Modified DT binding document to mention parent device
---
.../devicetree/bindings/rtc/cpcap-rtc.txt | 18 ++
drivers/rtc/Kconfig | 7 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-cpcap.c | 332 +++++++++++++++++++++
4 files changed, 358 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
create mode 100644 drivers/rtc/rtc-cpcap.c
diff --git a/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
new file mode 100644
index 000000000000..45750ff3112d
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
@@ -0,0 +1,18 @@
+Motorola CPCAP PMIC RTC
+-----------------------
+
+This module is part of the CPCAP. For more details about the whole
+chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt.
+
+Requires node properties:
+- compatible: should contain "motorola,cpcap-rtc"
+- interrupts: An interrupt specifier for alarm and 1 Hz irq
+
+Example:
+
+&cpcap {
+ cpcap_rtc: rtc {
+ compatible = "motorola,cpcap-rtc";
+ interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
+ };
+};
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ee1b0e9dde79..050bec749fae 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1731,6 +1731,13 @@ config RTC_DRV_STM32
This driver can also be built as a module, if so, the module
will be called "rtc-stm32".
+config RTC_DRV_CPCAP
+ depends on MFD_CPCAP
+ tristate "Motorola CPCAP RTC"
+ help
+ Say y here for CPCAP rtc found on some Motorola phones
+ and tablets such as Droid 4.
+
comment "HID Sensor RTC drivers"
config RTC_DRV_HID_SENSOR_TIME
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index f07297b1460a..13857d2fce09 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
+obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o
obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c
new file mode 100644
index 000000000000..7c6a3c3167bd
--- /dev/null
+++ b/drivers/rtc/rtc-cpcap.c
@@ -0,0 +1,332 @@
+/*
+ * Motorola CPCAP PMIC RTC driver
+ *
+ * Based on cpcap-regulator.c from Motorola Linux kernel tree
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * Rewritten for mainline kernel
+ * - use DT
+ * - use regmap
+ * - use standard interrupt framework
+ * - use managed device resources
+ * - remove custom "secure clock daemon" helpers
+ *
+ * Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/mfd/motorola-cpcap.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#define SECS_PER_DAY 86400
+#define DAY_MASK 0x7FFF
+#define TOD1_MASK 0x00FF
+#define TOD2_MASK 0x01FF
+
+struct cpcap_time {
+ int day;
+ int tod1;
+ int tod2;
+};
+
+struct cpcap_rtc {
+ struct regmap *regmap;
+ struct rtc_device *rtc_dev;
+ u16 vendor;
+ int alarm_irq;
+ bool alarm_enabled;
+ int update_irq;
+ bool update_enabled;
+};
+
+static void cpcap2rtc_time(struct rtc_time *rtc, struct cpcap_time *cpcap)
+{
+ unsigned long int tod;
+ unsigned long int time;
+
+ tod = (cpcap->tod1 & TOD1_MASK) | ((cpcap->tod2 & TOD2_MASK) << 8);
+ time = tod + ((cpcap->day & DAY_MASK) * SECS_PER_DAY);
+
+ rtc_time_to_tm(time, rtc);
+}
+
+static void rtc2cpcap_time(struct cpcap_time *cpcap, struct rtc_time *rtc)
+{
+ unsigned long time;
+
+ rtc_tm_to_time(rtc, &time);
+
+ cpcap->day = time / SECS_PER_DAY;
+ time %= SECS_PER_DAY;
+ cpcap->tod2 = (time >> 8) & TOD2_MASK;
+ cpcap->tod1 = time & TOD1_MASK;
+}
+
+static int cpcap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct cpcap_rtc *rtc = dev_get_drvdata(dev);
+
+ if (rtc->alarm_enabled == enabled)
+ return 0;
+
+ if (enabled)
+ enable_irq(rtc->alarm_irq);
+ else
+ disable_irq(rtc->alarm_irq);
+
+ rtc->alarm_enabled = !!enabled;
+
+ return 0;
+}
+
+static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int temp_tod2;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ ret = regmap_read(rtc->regmap, CPCAP_REG_TOD2, &temp_tod2);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD1, &cpcap_tm.tod1);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD2, &cpcap_tm.tod2);
+
+ if (temp_tod2 > cpcap_tm.tod2)
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
+
+ if (ret) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ cpcap2rtc_time(tm, &cpcap_tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret = 0;
+
+ rtc = dev_get_drvdata(dev);
+
+ rtc2cpcap_time(&cpcap_tm, tm);
+
+ if (rtc->alarm_enabled)
+ disable_irq(rtc->alarm_irq);
+ if (rtc->update_enabled)
+ disable_irq(rtc->update_irq);
+
+ if (rtc->vendor == CPCAP_VENDOR_ST) {
+ /* The TOD1 and TOD2 registers MUST be written in this order
+ * for the change to properly set.
+ */
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, cpcap_tm.tod1);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
+ TOD2_MASK, cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
+ DAY_MASK, cpcap_tm.day);
+ } else {
+ /* Clearing the upper lower 8 bits of the TOD guarantees that
+ * the upper half of TOD (TOD2) will not increment for 0xFF RTC
+ * ticks (255 seconds). During this time we can safely write
+ * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be
+ * synchronized to the exact time requested upon the final write
+ * to TOD1.
+ */
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, 0);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
+ DAY_MASK, cpcap_tm.day);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
+ TOD2_MASK, cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
+ TOD1_MASK, cpcap_tm.tod1);
+ }
+
+ if (rtc->update_enabled)
+ enable_irq(rtc->update_irq);
+ if (rtc->alarm_enabled)
+ enable_irq(rtc->alarm_irq);
+
+ return ret;
+}
+
+static int cpcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ alrm->enabled = rtc->alarm_enabled;
+
+ ret = regmap_read(rtc->regmap, CPCAP_REG_DAYA, &cpcap_tm.day);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA2, &cpcap_tm.tod2);
+ ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA1, &cpcap_tm.tod1);
+
+ if (ret) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ cpcap2rtc_time(&alrm->time, &cpcap_tm);
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int cpcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct cpcap_rtc *rtc;
+ struct cpcap_time cpcap_tm;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ rtc2cpcap_time(&cpcap_tm, &alrm->time);
+
+ if (rtc->alarm_enabled)
+ disable_irq(rtc->alarm_irq);
+
+ ret = regmap_update_bits(rtc->regmap, CPCAP_REG_DAYA, DAY_MASK,
+ cpcap_tm.day);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA2, TOD2_MASK,
+ cpcap_tm.tod2);
+ ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA1, TOD1_MASK,
+ cpcap_tm.tod1);
+
+ if (!ret) {
+ enable_irq(rtc->alarm_irq);
+ rtc->alarm_enabled = true;
+ }
+
+ return ret;
+}
+
+static const struct rtc_class_ops cpcap_rtc_ops = {
+ .read_time = cpcap_rtc_read_time,
+ .set_time = cpcap_rtc_set_time,
+ .read_alarm = cpcap_rtc_read_alarm,
+ .set_alarm = cpcap_rtc_set_alarm,
+ .alarm_irq_enable = cpcap_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t cpcap_rtc_alarm_irq(int irq, void *data)
+{
+ struct cpcap_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpcap_rtc_update_irq(int irq, void *data)
+{
+ struct cpcap_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static int cpcap_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cpcap_rtc *rtc;
+ int err;
+
+ rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!rtc->regmap)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, rtc);
+ rtc->rtc_dev = devm_rtc_device_register(dev, "cpcap_rtc",
+ &cpcap_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc->rtc_dev)) {
+ kfree(rtc);
+ return PTR_ERR(rtc->rtc_dev);
+ }
+
+ err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor);
+ if (err)
+ return err;
+
+ rtc->alarm_irq = platform_get_irq(pdev, 0);
+ err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
+ cpcap_rtc_alarm_irq, IRQ_NONE,
+ "rtc_alarm", rtc);
+ if (err) {
+ dev_err(dev, "Could not request alarm irq: %d\n", err);
+ return err;
+ }
+ disable_irq(rtc->alarm_irq);
+
+ /* Stock Android uses the 1 Hz interrupt for "secure clock daemon",
+ * which is not supported by the mainline kernel. The mainline kernel
+ * does not use the irq at the moment, but we explicitly request and
+ * disable it, so that its masked and does not wake up the processor
+ * every second.
+ */
+ rtc->update_irq = platform_get_irq(pdev, 1);
+ err = devm_request_threaded_irq(dev, rtc->update_irq, NULL,
+ cpcap_rtc_update_irq, IRQ_NONE,
+ "rtc_1hz", rtc);
+ if (err) {
+ dev_err(dev, "Could not request update irq: %d\n", err);
+ return err;
+ }
+ disable_irq(rtc->update_irq);
+
+ err = device_init_wakeup(dev, 1);
+ if (err) {
+ dev_err(dev, "wakeup initialization failed (%d)\n", err);
+ /* ignore error and continue without wakeup support */
+ }
+
+ return 0;
+}
+
+static const struct of_device_id cpcap_rtc_of_match[] = {
+ { .compatible = "motorola,cpcap-rtc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpcap_rtc_of_match);
+
+static struct platform_driver cpcap_rtc_driver = {
+ .probe = cpcap_rtc_probe,
+ .driver = {
+ .name = "cpcap-rtc",
+ .of_match_table = cpcap_rtc_of_match,
+ },
+};
+
+module_platform_driver(cpcap_rtc_driver);
+
+MODULE_ALIAS("platform:cpcap-rtc");
+MODULE_DESCRIPTION("CPCAP RTC driver");
+MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
+MODULE_LICENSE("GPL");
--
2.11.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCHv4 2/2] rtc: cpcap: new rtc driver
2017-03-02 0:27 ` [PATCHv4 2/2] rtc: cpcap: new rtc driver Sebastian Reichel
@ 2017-03-02 14:11 ` Rob Herring
2017-03-09 0:34 ` Alexandre Belloni
1 sibling, 0 replies; 20+ messages in thread
From: Rob Herring @ 2017-03-02 14:11 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Tony Lindgren, Alessandro Zummo, Alexandre Belloni, Mark Rutland,
open list:REAL TIME CLOCK (RTC) SUBSYSTEM, devicetree,
linux-kernel
On Wed, Mar 1, 2017 at 6:27 PM, Sebastian Reichel <sre@kernel.org> wrote:
> This driver supports the Motorola CPCAP PMIC found on
> some of Motorola's mobile phones, such as the Droid 4.
>
> Tested-by: Tony Lindgren <tony@atomide.com>
> Signed-off-by: Sebastian Reichel <sre@kernel.org>
> ---
> Changes since PATCHv3:
> - Modified DT binding document to mention parent device
> ---
> .../devicetree/bindings/rtc/cpcap-rtc.txt | 18 ++
Acked-by: Rob Herring <robh@kernel.org>
> drivers/rtc/Kconfig | 7 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-cpcap.c | 332 +++++++++++++++++++++
> 4 files changed, 358 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
> create mode 100644 drivers/rtc/rtc-cpcap.c
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCHv4 1/2] dt-bindings: Add vendor prefix for Motorola
2017-03-02 0:27 ` [PATCHv4 " Sebastian Reichel
2017-03-02 0:27 ` [PATCHv4 2/2] rtc: cpcap: new rtc driver Sebastian Reichel
@ 2017-03-09 0:33 ` Alexandre Belloni
1 sibling, 0 replies; 20+ messages in thread
From: Alexandre Belloni @ 2017-03-09 0:33 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Tony Lindgren, Alessandro Zummo, Rob Herring, Mark Rutland,
rtc-linux, devicetree, linux-kernel
On 02/03/2017 at 01:27:08 +0100, Sebastian Reichel wrote:
> Motorola was involved in semiconductor and mobile phone business.
> The "motorola," prefix is already used by a couple of bindings:
>
> * rtc/rtc-cmos.txt
> * mfd/motorola-cpcap.txt
> * regulator/cpcap-regulator.txt
>
> Apart from that it is used in the DT file for the Droid 4 mobile
> phone.
>
> Acked-by: Rob Herring <robh@kernel.org>
> Signed-off-by: Sebastian Reichel <sre@kernel.org>
> ---
> Changes since PATCHv3:
> - Fix typo
> - Add Acked-By from Rob Herring
> ---
> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> 1 file changed, 1 insertion(+)
>
Applied, thanks.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCHv4 2/2] rtc: cpcap: new rtc driver
2017-03-02 0:27 ` [PATCHv4 2/2] rtc: cpcap: new rtc driver Sebastian Reichel
2017-03-02 14:11 ` Rob Herring
@ 2017-03-09 0:34 ` Alexandre Belloni
1 sibling, 0 replies; 20+ messages in thread
From: Alexandre Belloni @ 2017-03-09 0:34 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Tony Lindgren, Alessandro Zummo, Rob Herring, Mark Rutland,
rtc-linux, devicetree, linux-kernel
On 02/03/2017 at 01:27:09 +0100, Sebastian Reichel wrote:
> This driver supports the Motorola CPCAP PMIC found on
> some of Motorola's mobile phones, such as the Droid 4.
>
> Tested-by: Tony Lindgren <tony@atomide.com>
> Signed-off-by: Sebastian Reichel <sre@kernel.org>
> ---
> Changes since PATCHv3:
> - Modified DT binding document to mention parent device
> ---
> .../devicetree/bindings/rtc/cpcap-rtc.txt | 18 ++
> drivers/rtc/Kconfig | 7 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-cpcap.c | 332 +++++++++++++++++++++
> 4 files changed, 358 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
> create mode 100644 drivers/rtc/rtc-cpcap.c
>
Applied, thanks.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2017-03-09 0:42 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-20 7:35 [PATCH 0/1] Motorola CPCAP PMIC RTC Sebastian Reichel
2017-02-20 7:35 ` [PATCH 1/1] rtc: cpcap: new rtc driver Sebastian Reichel
2017-02-20 16:31 ` Tony Lindgren
2017-02-20 16:38 ` Alexandre Belloni
2017-02-20 17:21 ` Tony Lindgren
2017-02-20 17:27 ` Tony Lindgren
2017-02-20 19:35 ` Sebastian Reichel
2017-02-21 6:16 ` [PATCHv2] " Sebastian Reichel
2017-02-21 23:52 ` Alexandre Belloni
2017-02-22 1:56 ` Sebastian Reichel
2017-02-22 8:18 ` Alexandre Belloni
2017-02-23 1:03 ` [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola Sebastian Reichel
2017-02-23 1:03 ` [PATCHv3 2/2] rtc: cpcap: new rtc driver Sebastian Reichel
2017-02-27 23:49 ` Rob Herring
2017-02-27 23:48 ` [PATCHv3 1/2] dt-bindings: Add vendor prefix for Motorola Rob Herring
2017-03-02 0:27 ` [PATCHv4 " Sebastian Reichel
2017-03-02 0:27 ` [PATCHv4 2/2] rtc: cpcap: new rtc driver Sebastian Reichel
2017-03-02 14:11 ` Rob Herring
2017-03-09 0:34 ` Alexandre Belloni
2017-03-09 0:33 ` [PATCHv4 1/2] dt-bindings: Add vendor prefix for Motorola Alexandre Belloni
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).