linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding
@ 2017-05-09 18:20 Moritz Fischer
  2017-05-09 18:20 ` [RFC/PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device Moritz Fischer
  2017-05-13  0:00 ` [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding Rob Herring
  0 siblings, 2 replies; 6+ messages in thread
From: Moritz Fischer @ 2017-05-09 18:20 UTC (permalink / raw)
  To: linux-watchdog
  Cc: rtc-linux, linux-kernel, lee.jones, devicetree, linux, wim,
	alexandre.belloni, a.zummo, mark.rutland, robh+dt,
	moritz.fischer, Moritz Fischer

This adds a binding for the Maxim/Dallas DS1374 MFD.

Signed-off-by: Moritz Fischer <mdf@kernel.org>
---

Hi all,

I'm not entirely sure aobut the binding, does anyone
have a better suggestion for the remap-wdt-reset property?

Thanks,

Moritz

---
 Documentation/devicetree/bindings/mfd/ds1374.txt   | 63 ++++++++++++++++++++++
 .../devicetree/bindings/trivial-devices.txt        |  1 -
 drivers/rtc/Kconfig                                |  2 +
 3 files changed, 65 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/ds1374.txt

diff --git a/Documentation/devicetree/bindings/mfd/ds1374.txt b/Documentation/devicetree/bindings/mfd/ds1374.txt
new file mode 100644
index 0000000..b22396f
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/ds1374.txt
@@ -0,0 +1,63 @@
+* Device tree bindings for Maxim/Dallas DS1374 Multi Function Device (MFD)
+
+The Maxim/Dallas DS1374 is a multi function device that combines rtc,
+watchdog or alarm, as well as trickle charger.
+
+The DS1374 is connected via I2C.
+
+Required properties:
+- compatible: "dallas,ds1374"
+- reg: I2C slave address
+- dallas,ds1374-mode: Should be one of the following values:
+			<0> for RTC
+			<1> for RTC + Alarm (Interrupt)
+			<2> for RTC + Watchdog
+
+Required child:
+A single available child device of type matching the "dallas,ds1374-mode"
+property.
+
+Optional properties (watchdog):
+- dallas,ds1374-remap-wdt-reset: Boolean describing whether the INT pin
+				 on the device is used as interrupt for
+				 the alarm
+
+See ../watchdog/* for generic watchdog bindings.
+
+Optional properties (real time clock):
+- interrupt: phandle to interrupt cell for the rtc's alarm feature
+
+See ../rtc/* for generic rtc bindings.
+
+Optional properties (trickle-charger):
+- dallas,trickle-resistor-ohms : Selected resistor for trickle charger
+	Values usable for ds1374 are 250, 2000, 4000
+	Should be given if trickle charger should be enabled
+- dallas,trickle-diode-disable : Do not use internal trickle charger diode
+	Should be given if internal trickle charger diode should be disabled
+
+Example for rtc with alarm mode and interrupt:
+
+i2c@12ca0000 {
+	rtc@68 {
+		compatible = "ds1374";
+		reg = <0x68>;
+		interrupts = <&gpio0 62>;
+		dallas,ds1374-mode = <2>
+
+		dallas,trickle-resistor-ohms = <250>;
+		dallas,trickle-diode-disable;
+	};
+};
+
+Example for rtc with watchdog and reset on timeout, with reset remapped
+to the INT pin:
+
+i2c@12ca0000 {
+	rtc@68 {
+		compatible = "ds1374";
+		reg = <0x68>;
+		dallas,ds1374-mode = <2>
+		dallas,ds1374-remap-wdt-reset;
+	};
+};
diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt
index 3e0a34c..f7a50e5 100644
--- a/Documentation/devicetree/bindings/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/trivial-devices.txt
@@ -29,7 +29,6 @@ cirrus,cs42l51		Cirrus Logic CS42L51 audio codec
 dallas,ds1307		64 x 8, Serial, I2C Real-Time Clock
 dallas,ds1338		I2C RTC with 56-Byte NV RAM
 dallas,ds1340		I2C RTC with Trickle Charger
-dallas,ds1374		I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output
 dallas,ds1631		High-Precision Digital Thermometer
 dallas,ds1682		Total-Elapsed-Time Recorder with Alarm
 dallas,ds1775		Tiny Digital Thermometer and Thermostat
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8d3b957..e6763fe 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -250,6 +250,8 @@ config RTC_DRV_DS1307_CENTURY
 
 config RTC_DRV_DS1374
 	tristate "Dallas/Maxim DS1374"
+	depends on MFD_DS1374
+	depends on REGMAP_I2C
 	help
 	  If you say yes here you get support for Dallas Semiconductor
 	  DS1374 real-time clock chips. If an interrupt is associated
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC/PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device
  2017-05-09 18:20 [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding Moritz Fischer
@ 2017-05-09 18:20 ` Moritz Fischer
  2017-05-09 23:48   ` Moritz Fischer
  2017-05-13  0:00 ` [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding Rob Herring
  1 sibling, 1 reply; 6+ messages in thread
From: Moritz Fischer @ 2017-05-09 18:20 UTC (permalink / raw)
  To: linux-watchdog
  Cc: rtc-linux, linux-kernel, lee.jones, devicetree, linux, wim,
	alexandre.belloni, a.zummo, mark.rutland, robh+dt,
	moritz.fischer, Moritz Fischer

From: Moritz Fischer <moritz.fischer@ettus.com>

Add support for the Maxim/Dallas DS1374 RTC/WDT with trickle charger.
The device can either be configured as simple RTC, as simple RTC with
Alarm (IRQ) as well as simple RTC with watchdog timer.

Break up the old monolithic driver in drivers/rtc/rtc-ds1374.c into:
- rtc part in drivers/rtc/rtc-ds1374.c
- watchdog part under drivers/watchdog/ds1374-wdt.c
- mfd part drivers/mfd/ds1374.c

The MFD part takes care of trickle charging and mode selection,
since the usage modes of a) RTC + Alarm or b) RTC + WDT
are mutually exclusive.

Signed-off-by: Moritz Fischer <mdf@kernel.org>
---

Hi all,

attached changeset is huge, I do realize that. I couldn't find a good
way of splitting it and maintaining bisectability.

Not sure if the way the rtc/wdt drivers access the parent is recommended,
or whether one should rather pass down just the 'mode' and regmap in platform
data.

Thanks for your time reviewing,

Moritz


---
 drivers/mfd/Kconfig           |  10 +
 drivers/mfd/Makefile          |   1 +
 drivers/mfd/ds1374.c          | 267 ++++++++++++++++++
 drivers/rtc/rtc-ds1374.c      | 640 ++++++++++--------------------------------
 drivers/watchdog/Kconfig      |  10 +
 drivers/watchdog/Makefile     |   1 +
 drivers/watchdog/ds1374-wdt.c | 214 ++++++++++++++
 include/linux/mfd/ds1374.h    |  59 ++++
 8 files changed, 714 insertions(+), 488 deletions(-)
 create mode 100644 drivers/mfd/ds1374.c
 create mode 100644 drivers/watchdog/ds1374-wdt.c
 create mode 100644 include/linux/mfd/ds1374.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3eb5c93..2dfef3c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -203,6 +203,16 @@ config MFD_CROS_EC_SPI
 	  response time cannot be guaranteed, we support ignoring
 	  'pre-amble' bytes before the response actually starts.
 
+config MFD_DS1374
+	tristate "Dallas/Maxim DS1374 RTC/WDT/ALARM (I2C)"
+	select MFD_CORE
+	depends on I2C
+	depends on REGMAP_I2C
+
+	 ---help---
+	  This driver supports the Dallas Maxim DS1374 multi function chip.
+	  The chip combines an RTC, trickle charger, Watchdog or Alarm.
+
 config MFD_ASIC3
 	bool "Compaq ASIC3"
 	depends on GPIOLIB && ARM
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index c16bf1e..b5cfcf4 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -15,6 +15,7 @@ cros_ec_core-$(CONFIG_ACPI)	+= cros_ec_acpi_gpe.o
 obj-$(CONFIG_MFD_CROS_EC)	+= cros_ec_core.o
 obj-$(CONFIG_MFD_CROS_EC_I2C)	+= cros_ec_i2c.o
 obj-$(CONFIG_MFD_CROS_EC_SPI)	+= cros_ec_spi.o
+obj-$(CONFIG_MFD_DS1374)	+= ds1374.o
 obj-$(CONFIG_MFD_EXYNOS_LPASS)	+= exynos-lpass.o
 
 rtsx_pci-objs			:= rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
diff --git a/drivers/mfd/ds1374.c b/drivers/mfd/ds1374.c
new file mode 100644
index 0000000..be82b38
--- /dev/null
+++ b/drivers/mfd/ds1374.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2017, National Instruments Corp.
+ *
+ * Dallas/Maxim DS1374 Multi Function Device Driver
+ *
+ * The trickle charger code was taken more ore less 1:1 from
+ * drivers/rtc/rtc-1390.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ds1374.h>
+
+#define DS1374_TRICKLE_CHARGER_ENABLE	0xa0
+#define DS1374_TRICKLE_CHARGER_ENABLE_MASK 0xe0
+
+#define DS1374_TRICKLE_CHARGER_250_OHM	0x01
+#define DS1374_TRICKLE_CHARGER_2K_OHM	0x02
+#define DS1374_TRICKLE_CHARGER_4K_OHM	0x03
+#define DS1374_TRICKLE_CHARGER_ROUT_MASK 0x03
+
+#define DS1374_TRICKLE_CHARGER_NO_DIODE	0x04
+#define DS1374_TRICKLE_CHARGER_DIODE	0x08
+#define DS1374_TRICKLE_CHARGER_DIODE_MASK 0xc
+
+static const struct regmap_range volatile_ranges[] = {
+	regmap_reg_range(DS1374_REG_TOD0, DS1374_REG_WDALM2),
+	regmap_reg_range(DS1374_REG_SR, DS1374_REG_SR),
+};
+
+static const struct regmap_access_table ds1374_volatile_table = {
+	.yes_ranges = volatile_ranges,
+	.n_yes_ranges = ARRAY_SIZE(volatile_ranges),
+};
+
+static struct regmap_config ds1374_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = DS1374_REG_TCR,
+	.volatile_table	= &ds1374_volatile_table,
+	.cache_type	= REGCACHE_RBTREE,
+};
+
+static struct mfd_cell ds1374_wdt_cell = {
+	.name = "ds1374-wdt",
+};
+
+static struct mfd_cell ds1374_rtc_cell = {
+	.name = "ds1374-rtc",
+};
+
+static int ds1374_add_device(struct ds1374 *chip,
+			     struct mfd_cell *cell)
+{
+	cell->platform_data = chip;
+	cell->pdata_size = sizeof(*chip);
+
+	return mfd_add_devices(&chip->client->dev, PLATFORM_DEVID_AUTO,
+			       cell, 1, NULL, 0, NULL);
+}
+
+static int ds1374_trickle_of_init(struct ds1374 *ds1374)
+{
+	u32 ohms = 0;
+	u8 value;
+	struct i2c_client *client = ds1374->client;
+
+	if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms",
+				 &ohms))
+		return 0;
+
+	/* Enable charger */
+	value = DS1374_TRICKLE_CHARGER_ENABLE;
+	if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
+		value |= DS1374_TRICKLE_CHARGER_NO_DIODE;
+	else
+		value |= DS1374_TRICKLE_CHARGER_DIODE;
+
+	/* Resistor select */
+	switch (ohms) {
+	case 250:
+		value |= DS1374_TRICKLE_CHARGER_250_OHM;
+		break;
+	case 2000:
+		value |= DS1374_TRICKLE_CHARGER_2K_OHM;
+		break;
+	case 4000:
+		value |= DS1374_TRICKLE_CHARGER_4K_OHM;
+		break;
+	default:
+		dev_warn(&client->dev,
+			 "Unsupported ohm value %02ux in dt\n", ohms);
+		return -EINVAL;
+	}
+	dev_dbg(&client->dev, "Trickle charge value is 0x%02x\n", value);
+
+	return regmap_write(ds1374->regmap, DS1374_REG_TCR, value);
+}
+
+int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes)
+{
+	u8 buf[4];
+	int ret;
+	int i;
+
+	if (WARN_ON(nbytes > 4))
+		return -EINVAL;
+
+	ret = regmap_bulk_read(ds1374->regmap, reg, buf, nbytes);
+	if (ret) {
+		dev_err(&ds1374->client->dev,
+			"Failed to bulkread n = %d at R%d\n",
+			nbytes, reg);
+		return ret;
+	}
+
+	for (i = nbytes - 1, *time = 0; i >= 0; i--)
+		*time = (*time << 8) | buf[i];
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ds1374_read_bulk);
+
+int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes)
+{
+	u8 buf[4];
+	int i;
+
+	if (nbytes > 4) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < nbytes; i++) {
+		buf[i] = time & 0xff;
+		time >>= 8;
+	}
+
+	return regmap_bulk_write(ds1374->regmap, reg, buf, nbytes);
+}
+EXPORT_SYMBOL_GPL(ds1374_write_bulk);
+
+static int ds1374_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct ds1374 *ds1374;
+	u32 mode;
+	int err;
+
+	ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
+	if (!ds1374)
+		return -ENOMEM;
+
+	ds1374->regmap = devm_regmap_init_i2c(client, &ds1374_regmap_config);
+	if (IS_ERR(ds1374->regmap))
+		return PTR_ERR(ds1374->regmap);
+
+	if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
+		err = of_property_read_u32(client->dev.of_node,
+					   "dallas,ds1374-mode", &mode);
+		if (err < 0) {
+			dev_err(&client->dev, "missing dallas,ds1374-mode property\n");
+			return -EINVAL;
+		}
+
+		ds1374->remapped_reset
+			= of_property_read_bool(client->dev.of_node,
+						"dallas,ds1374-remap-wdt-reset");
+
+		ds1374->mode = (enum ds1374_mode)mode;
+	} else if (IS_ENABLED(CONFIG_RTC_DRV_DS1374_WDT)) {
+		ds1374->mode = DS1374_MODE_RTC_WDT;
+	} else {
+		ds1374->mode = DS1374_MODE_RTC_ALM;
+	}
+
+	ds1374->client = client;
+	ds1374->irq = client->irq;
+	i2c_set_clientdata(client, ds1374);
+
+	/* check if we're supposed to trickle charge */
+	err = ds1374_trickle_of_init(ds1374);
+	if (err) {
+		dev_err(&client->dev, "Failed to init trickle charger!\n");
+		return err;
+	}
+
+	/* we always have a rtc */
+	err = ds1374_add_device(ds1374, &ds1374_rtc_cell);
+	if (err)
+		return err;
+
+	/* we might have a watchdog if configured that way */
+	if (ds1374->mode == DS1374_MODE_RTC_WDT)
+		return ds1374_add_device(ds1374, &ds1374_wdt_cell);
+
+	return err;
+}
+
+static const struct i2c_device_id ds1374_id[] = {
+	{ "ds1374", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ds1374_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id ds1374_of_match[] = {
+	{ .compatible = "dallas,ds1374" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ds1374_of_match);
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int ds1374_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int ds1374_resume(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
+
+static struct i2c_driver ds1374_driver = {
+	.driver = {
+		.name = "ds1374",
+		.of_match_table = of_match_ptr(ds1374_of_match),
+		.pm = &ds1374_pm,
+	},
+	.probe = ds1374_probe,
+	.id_table = ds1374_id,
+};
+
+static int __init ds1374_init(void)
+{
+	return i2c_add_driver(&ds1374_driver);
+}
+subsys_initcall(ds1374_init);
+
+static void __exit ds1374_exit(void)
+{
+	i2c_del_driver(&ds1374_driver);
+}
+module_exit(ds1374_exit);
+
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
+MODULE_DESCRIPTION("Maxim/Dallas DS1374 MFD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index 38a2e9e..7b2fbd9 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -1,9 +1,10 @@
 /*
- * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
+ * RTC driver for the Maxim/Dallas DS1374 Real-Time Clock via MFD
  *
  * Based on code by Randy Vinson <rvinson@mvista.com>,
  * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>.
  *
+ * Copyright (C) 2017 National Instruments Corp
  * Copyright (C) 2014 Rose Technology
  * Copyright (C) 2006-2007 Freescale Semiconductor
  *
@@ -12,64 +13,24 @@
  * is licensed "as is" without any warranty of any kind, whether express
  * or implied.
  */
-/*
- * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
- * recommened in .../Documentation/i2c/writing-clients section
- * "Sending and receiving", using SMBus level communication is preferred.
- */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
-#include <linux/i2c.h>
 #include <linux/rtc.h>
 #include <linux/bcd.h>
 #include <linux/workqueue.h>
 #include <linux/slab.h>
 #include <linux/pm.h>
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
-#include <linux/fs.h>
-#include <linux/ioctl.h>
-#include <linux/miscdevice.h>
-#include <linux/reboot.h>
-#include <linux/watchdog.h>
-#endif
-
-#define DS1374_REG_TOD0		0x00 /* Time of Day */
-#define DS1374_REG_TOD1		0x01
-#define DS1374_REG_TOD2		0x02
-#define DS1374_REG_TOD3		0x03
-#define DS1374_REG_WDALM0	0x04 /* Watchdog/Alarm */
-#define DS1374_REG_WDALM1	0x05
-#define DS1374_REG_WDALM2	0x06
-#define DS1374_REG_CR		0x07 /* Control */
-#define DS1374_REG_CR_AIE	0x01 /* Alarm Int. Enable */
-#define DS1374_REG_CR_WDALM	0x20 /* 1=Watchdog, 0=Alarm */
-#define DS1374_REG_CR_WACE	0x40 /* WD/Alarm counter enable */
-#define DS1374_REG_SR		0x08 /* Status */
-#define DS1374_REG_SR_OSF	0x80 /* Oscillator Stop Flag */
-#define DS1374_REG_SR_AF	0x01 /* Alarm Flag */
-#define DS1374_REG_TCR		0x09 /* Trickle Charge */
-
-static const struct i2c_device_id ds1374_id[] = {
-	{ "ds1374", 0 },
-	{ }
-};
-MODULE_DEVICE_TABLE(i2c, ds1374_id);
-
-#ifdef CONFIG_OF
-static const struct of_device_id ds1374_of_match[] = {
-	{ .compatible = "dallas,ds1374" },
-	{ }
-};
-MODULE_DEVICE_TABLE(of, ds1374_of_match);
-#endif
+#include <linux/regmap.h>
+#include <linux/mfd/ds1374.h>
+#include <linux/platform_device.h>
 
-struct ds1374 {
-	struct i2c_client *client;
+struct ds1374_rtc {
 	struct rtc_device *rtc;
+	struct ds1374 *chip;
 	struct work_struct work;
 
 	/* The mutex protects alarm operations, and prevents a race
@@ -80,89 +41,44 @@ struct ds1374 {
 	int exiting;
 };
 
-static struct i2c_driver ds1374_driver;
-
-static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
-			   int reg, int nbytes)
-{
-	u8 buf[4];
-	int ret;
-	int i;
-
-	if (WARN_ON(nbytes > 4))
-		return -EINVAL;
-
-	ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
-
-	if (ret < 0)
-		return ret;
-	if (ret < nbytes)
-		return -EIO;
-
-	for (i = nbytes - 1, *time = 0; i >= 0; i--)
-		*time = (*time << 8) | buf[i];
-
-	return 0;
-}
-
-static int ds1374_write_rtc(struct i2c_client *client, u32 time,
-			    int reg, int nbytes)
-{
-	u8 buf[4];
-	int i;
-
-	if (nbytes > 4) {
-		WARN_ON(1);
-		return -EINVAL;
-	}
-
-	for (i = 0; i < nbytes; i++) {
-		buf[i] = time & 0xff;
-		time >>= 8;
-	}
-
-	return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf);
-}
-
-static int ds1374_check_rtc_status(struct i2c_client *client)
+static int ds1374_check_rtc_status(struct ds1374_rtc *ds1374)
 {
 	int ret = 0;
-	int control, stat;
+	unsigned int control, stat;
 
-	stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
-	if (stat < 0)
+	ret = regmap_read(ds1374->chip->regmap, DS1374_REG_SR, &stat);
+	if (ret)
 		return stat;
 
 	if (stat & DS1374_REG_SR_OSF)
-		dev_warn(&client->dev,
+		dev_warn(&ds1374->chip->client->dev,
 			 "oscillator discontinuity flagged, time unreliable\n");
 
-	stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF);
-
-	ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
-	if (ret < 0)
+	ret = regmap_update_bits(ds1374->chip->regmap, DS1374_REG_SR,
+				 DS1374_REG_SR_OSF | DS1374_REG_SR_AF, 0);
+	if (ret)
 		return ret;
 
 	/* If the alarm is pending, clear it before requesting
 	 * the interrupt, so an interrupt event isn't reported
 	 * before everything is initialized.
 	 */
-
-	control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
-	if (control < 0)
-		return control;
+	ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &control);
+	if (ret)
+		return ret;
 
 	control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
-	return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
+	return regmap_write(ds1374->chip->regmap, DS1374_REG_CR, control);
 }
 
 static int ds1374_read_time(struct device *dev, struct rtc_time *time)
 {
-	struct i2c_client *client = to_i2c_client(dev);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
 	u32 itime;
 	int ret;
 
-	ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4);
+	ret = ds1374_read_bulk(ds1374_rtc->chip, &itime, DS1374_REG_TOD0, 4);
 	if (!ret)
 		rtc_time_to_tm(itime, time);
 
@@ -171,44 +87,47 @@ static int ds1374_read_time(struct device *dev, struct rtc_time *time)
 
 static int ds1374_set_time(struct device *dev, struct rtc_time *time)
 {
-	struct i2c_client *client = to_i2c_client(dev);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
 	unsigned long itime;
 
 	rtc_tm_to_time(time, &itime);
-	return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4);
+	return ds1374_write_bulk(ds1374_rtc->chip, itime, DS1374_REG_TOD0, 4);
 }
 
-#ifndef CONFIG_RTC_DRV_DS1374_WDT
 /* The ds1374 has a decrementer for an alarm, rather than a comparator.
  * If the time of day is changed, then the alarm will need to be
  * reset.
  */
 static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
-	struct i2c_client *client = to_i2c_client(dev);
-	struct ds1374 *ds1374 = i2c_get_clientdata(client);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
+	struct ds1374 *ds1374 = ds1374_rtc->chip;
+
 	u32 now, cur_alarm;
-	int cr, sr;
+	unsigned int cr, sr;
 	int ret = 0;
 
-	if (client->irq <= 0)
+	if (ds1374->irq <= 0)
 		return -EINVAL;
 
-	mutex_lock(&ds1374->mutex);
+	mutex_lock(&ds1374_rtc->mutex);
 
-	cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+	ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
 	if (ret < 0)
 		goto out;
 
-	sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
+	ret = regmap_read(ds1374->regmap, DS1374_REG_SR, &sr);
 	if (ret < 0)
 		goto out;
 
-	ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
+	ret = ds1374_read_bulk(ds1374_rtc->chip, &now, DS1374_REG_TOD0, 4);
 	if (ret)
 		goto out;
 
-	ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
+	ret = ds1374_read_bulk(ds1374_rtc->chip, &cur_alarm,
+			       DS1374_REG_WDALM0, 3);
 	if (ret)
 		goto out;
 
@@ -217,20 +136,21 @@ static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 	alarm->pending = !!(sr & DS1374_REG_SR_AF);
 
 out:
-	mutex_unlock(&ds1374->mutex);
+	mutex_unlock(&ds1374_rtc->mutex);
 	return ret;
 }
 
 static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
-	struct i2c_client *client = to_i2c_client(dev);
-	struct ds1374 *ds1374 = i2c_get_clientdata(client);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
+	struct ds1374 *ds1374 = ds1374_rtc->chip;
+
 	struct rtc_time now;
 	unsigned long new_alarm, itime;
-	int cr;
 	int ret = 0;
 
-	if (client->irq <= 0)
+	if (ds1374->irq <= 0)
 		return -EINVAL;
 
 	ret = ds1374_read_time(dev, &now);
@@ -251,435 +171,178 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 	else
 		new_alarm -= itime;
 
-	mutex_lock(&ds1374->mutex);
-
-	ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
-	if (ret < 0)
-		goto out;
+	mutex_lock(&ds1374_rtc->mutex);
 
 	/* Disable any existing alarm before setting the new one
-	 * (or lack thereof). */
-	cr &= ~DS1374_REG_CR_WACE;
-
-	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
-	if (ret < 0)
-		goto out;
+	 * (or lack thereof).
+	 */
+	ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
+				 DS1374_REG_CR_WACE, 0);
 
-	ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
+	ret = ds1374_write_bulk(ds1374_rtc->chip, new_alarm,
+				DS1374_REG_WDALM0, 3);
 	if (ret)
 		goto out;
 
 	if (alarm->enabled) {
-		cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
-		cr &= ~DS1374_REG_CR_WDALM;
-
-		ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
+		ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
+					 DS1374_REG_CR_WACE | DS1374_REG_CR_AIE
+					 | DS1374_REG_CR_WDALM,
+					 DS1374_REG_CR_WACE
+					 | DS1374_REG_CR_AIE);
 	}
 
 out:
-	mutex_unlock(&ds1374->mutex);
+	mutex_unlock(&ds1374_rtc->mutex);
 	return ret;
 }
-#endif
 
 static irqreturn_t ds1374_irq(int irq, void *dev_id)
 {
-	struct i2c_client *client = dev_id;
-	struct ds1374 *ds1374 = i2c_get_clientdata(client);
+	struct ds1374_rtc *ds1374_rtc = dev_id;
 
 	disable_irq_nosync(irq);
-	schedule_work(&ds1374->work);
+	schedule_work(&ds1374_rtc->work);
 	return IRQ_HANDLED;
 }
 
 static void ds1374_work(struct work_struct *work)
 {
-	struct ds1374 *ds1374 = container_of(work, struct ds1374, work);
-	struct i2c_client *client = ds1374->client;
-	int stat, control;
+	struct ds1374_rtc *ds1374_rtc = container_of(work, struct ds1374_rtc,
+						     work);
+	unsigned int stat;
+	int ret;
 
-	mutex_lock(&ds1374->mutex);
+	mutex_lock(&ds1374_rtc->mutex);
 
-	stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
-	if (stat < 0)
+	ret = regmap_read(ds1374_rtc->chip->regmap, DS1374_REG_SR, &stat);
+	if (ret)
 		goto unlock;
 
 	if (stat & DS1374_REG_SR_AF) {
-		stat &= ~DS1374_REG_SR_AF;
-		i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
-
-		control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
-		if (control < 0)
+		regmap_update_bits(ds1374_rtc->chip->regmap, DS1374_REG_SR,
+				   DS1374_REG_SR_AF, 0);
+
+		ret = regmap_update_bits(ds1374_rtc->chip->regmap,
+					 DS1374_REG_CR, DS1374_REG_CR_WACE
+					 | DS1374_REG_CR_AIE,
+					 0);
+		if (ret)
 			goto out;
 
-		control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
-		i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
-
-		rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF);
+		rtc_update_irq(ds1374_rtc->rtc, 1, RTC_AF | RTC_IRQF);
 	}
 
 out:
-	if (!ds1374->exiting)
-		enable_irq(client->irq);
+	if (!ds1374_rtc->exiting)
+		enable_irq(ds1374_rtc->chip->irq);
 unlock:
-	mutex_unlock(&ds1374->mutex);
+	mutex_unlock(&ds1374_rtc->mutex);
 }
 
-#ifndef CONFIG_RTC_DRV_DS1374_WDT
 static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
 {
-	struct i2c_client *client = to_i2c_client(dev);
-	struct ds1374 *ds1374 = i2c_get_clientdata(client);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ds1374_rtc *ds1374 = platform_get_drvdata(pdev);
+	unsigned int cr;
 	int ret;
 
 	mutex_lock(&ds1374->mutex);
 
-	ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+	ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &cr);
 	if (ret < 0)
 		goto out;
 
-	if (enabled) {
-		ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
-		ret &= ~DS1374_REG_CR_WDALM;
-	} else {
-		ret &= ~DS1374_REG_CR_WACE;
-	}
-	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
-
+	if (enabled)
+		regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
+				   DS1374_REG_CR_WACE | DS1374_REG_CR_AIE |
+			   DS1374_REG_CR_WDALM, DS1374_REG_CR_WACE |
+			   DS1374_REG_CR_AIE);
+	else
+		regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
+				   DS1374_REG_CR_WACE, 0);
 out:
 	mutex_unlock(&ds1374->mutex);
 	return ret;
 }
-#endif
 
-static const struct rtc_class_ops ds1374_rtc_ops = {
+static const struct rtc_class_ops ds1374_rtc_alm_ops = {
 	.read_time = ds1374_read_time,
 	.set_time = ds1374_set_time,
-#ifndef CONFIG_RTC_DRV_DS1374_WDT
 	.read_alarm = ds1374_read_alarm,
 	.set_alarm = ds1374_set_alarm,
 	.alarm_irq_enable = ds1374_alarm_irq_enable,
-#endif
-};
-
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
-/*
- *****************************************************************************
- *
- * Watchdog Driver
- *
- *****************************************************************************
- */
-static struct i2c_client *save_client;
-/* Default margin */
-#define WD_TIMO 131762
-
-#define DRV_NAME "DS1374 Watchdog"
-
-static int wdt_margin = WD_TIMO;
-static unsigned long wdt_is_open;
-module_param(wdt_margin, int, 0);
-MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)");
-
-static const struct watchdog_info ds1374_wdt_info = {
-	.identity       = "DS1374 WTD",
-	.options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
-						WDIOF_MAGICCLOSE,
 };
 
-static int ds1374_wdt_settimeout(unsigned int timeout)
-{
-	int ret = -ENOIOCTLCMD;
-	int cr;
-
-	ret = cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
-	if (ret < 0)
-		goto out;
-
-	/* Disable any existing watchdog/alarm before setting the new one */
-	cr &= ~DS1374_REG_CR_WACE;
-
-	ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
-	if (ret < 0)
-		goto out;
-
-	/* Set new watchdog time */
-	ret = ds1374_write_rtc(save_client, timeout, DS1374_REG_WDALM0, 3);
-	if (ret) {
-		pr_info("couldn't set new watchdog time\n");
-		goto out;
-	}
-
-	/* Enable watchdog timer */
-	cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM;
-	cr &= ~DS1374_REG_CR_AIE;
-
-	ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
-	if (ret < 0)
-		goto out;
-
-	return 0;
-out:
-	return ret;
-}
-
-
-/*
- * Reload the watchdog timer.  (ie, pat the watchdog)
- */
-static void ds1374_wdt_ping(void)
-{
-	u32 val;
-	int ret = 0;
-
-	ret = ds1374_read_rtc(save_client, &val, DS1374_REG_WDALM0, 3);
-	if (ret)
-		pr_info("WD TICK FAIL!!!!!!!!!! %i\n", ret);
-}
-
-static void ds1374_wdt_disable(void)
-{
-	int ret = -ENOIOCTLCMD;
-	int cr;
-
-	cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
-	/* Disable watchdog timer */
-	cr &= ~DS1374_REG_CR_WACE;
-
-	ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
-}
-
-/*
- * Watchdog device is opened, and watchdog starts running.
- */
-static int ds1374_wdt_open(struct inode *inode, struct file *file)
-{
-	struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
-
-	if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
-		mutex_lock(&ds1374->mutex);
-		if (test_and_set_bit(0, &wdt_is_open)) {
-			mutex_unlock(&ds1374->mutex);
-			return -EBUSY;
-		}
-		/*
-		 *      Activate
-		 */
-		wdt_is_open = 1;
-		mutex_unlock(&ds1374->mutex);
-		return nonseekable_open(inode, file);
-	}
-	return -ENODEV;
-}
-
-/*
- * Close the watchdog device.
- */
-static int ds1374_wdt_release(struct inode *inode, struct file *file)
-{
-	if (MINOR(inode->i_rdev) == WATCHDOG_MINOR)
-		clear_bit(0, &wdt_is_open);
-
-	return 0;
-}
-
-/*
- * Pat the watchdog whenever device is written to.
- */
-static ssize_t ds1374_wdt_write(struct file *file, const char __user *data,
-				size_t len, loff_t *ppos)
-{
-	if (len) {
-		ds1374_wdt_ping();
-		return 1;
-	}
-	return 0;
-}
-
-static ssize_t ds1374_wdt_read(struct file *file, char __user *data,
-				size_t len, loff_t *ppos)
-{
-	return 0;
-}
-
-/*
- * Handle commands from user-space.
- */
-static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd,
-							unsigned long arg)
-{
-	int new_margin, options;
-
-	switch (cmd) {
-	case WDIOC_GETSUPPORT:
-		return copy_to_user((struct watchdog_info __user *)arg,
-		&ds1374_wdt_info, sizeof(ds1374_wdt_info)) ? -EFAULT : 0;
-
-	case WDIOC_GETSTATUS:
-	case WDIOC_GETBOOTSTATUS:
-		return put_user(0, (int __user *)arg);
-	case WDIOC_KEEPALIVE:
-		ds1374_wdt_ping();
-		return 0;
-	case WDIOC_SETTIMEOUT:
-		if (get_user(new_margin, (int __user *)arg))
-			return -EFAULT;
-
-		/* the hardware's tick rate is 4096 Hz, so
-		 * the counter value needs to be scaled accordingly
-		 */
-		new_margin <<= 12;
-		if (new_margin < 1 || new_margin > 16777216)
-			return -EINVAL;
-
-		wdt_margin = new_margin;
-		ds1374_wdt_settimeout(new_margin);
-		ds1374_wdt_ping();
-		/* fallthrough */
-	case WDIOC_GETTIMEOUT:
-		/* when returning ... inverse is true */
-		return put_user((wdt_margin >> 12), (int __user *)arg);
-	case WDIOC_SETOPTIONS:
-		if (copy_from_user(&options, (int __user *)arg, sizeof(int)))
-			return -EFAULT;
-
-		if (options & WDIOS_DISABLECARD) {
-			pr_info("disable watchdog\n");
-			ds1374_wdt_disable();
-			return 0;
-		}
-
-		if (options & WDIOS_ENABLECARD) {
-			pr_info("enable watchdog\n");
-			ds1374_wdt_settimeout(wdt_margin);
-			ds1374_wdt_ping();
-			return 0;
-		}
-		return -EINVAL;
-	}
-	return -ENOTTY;
-}
-
-static long ds1374_wdt_unlocked_ioctl(struct file *file, unsigned int cmd,
-			unsigned long arg)
-{
-	int ret;
-	struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
-
-	mutex_lock(&ds1374->mutex);
-	ret = ds1374_wdt_ioctl(file, cmd, arg);
-	mutex_unlock(&ds1374->mutex);
-
-	return ret;
-}
-
-static int ds1374_wdt_notify_sys(struct notifier_block *this,
-			unsigned long code, void *unused)
-{
-	if (code == SYS_DOWN || code == SYS_HALT)
-		/* Disable Watchdog */
-		ds1374_wdt_disable();
-	return NOTIFY_DONE;
-}
-
-static const struct file_operations ds1374_wdt_fops = {
-	.owner			= THIS_MODULE,
-	.read			= ds1374_wdt_read,
-	.unlocked_ioctl		= ds1374_wdt_unlocked_ioctl,
-	.write			= ds1374_wdt_write,
-	.open                   = ds1374_wdt_open,
-	.release                = ds1374_wdt_release,
-	.llseek			= no_llseek,
-};
-
-static struct miscdevice ds1374_miscdev = {
-	.minor          = WATCHDOG_MINOR,
-	.name           = "watchdog",
-	.fops           = &ds1374_wdt_fops,
-};
-
-static struct notifier_block ds1374_wdt_notifier = {
-	.notifier_call = ds1374_wdt_notify_sys,
+static const struct rtc_class_ops ds1374_rtc_ops = {
+	.read_time = ds1374_read_time,
+	.set_time = ds1374_set_time,
 };
 
-#endif /*CONFIG_RTC_DRV_DS1374_WDT*/
-/*
- *****************************************************************************
- *
- *	Driver Interface
- *
- *****************************************************************************
- */
-static int ds1374_probe(struct i2c_client *client,
-			const struct i2c_device_id *id)
+static int ds1374_rtc_probe(struct platform_device *pdev)
 {
-	struct ds1374 *ds1374;
+	struct device *dev = &pdev->dev;
+	struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
+	struct ds1374_rtc *ds1374_rtc;
 	int ret;
 
-	ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
-	if (!ds1374)
+	ds1374_rtc = devm_kzalloc(dev, sizeof(*ds1374_rtc), GFP_KERNEL);
+	if (!ds1374_rtc)
 		return -ENOMEM;
+	ds1374_rtc->chip = ds1374;
 
-	ds1374->client = client;
-	i2c_set_clientdata(client, ds1374);
+	platform_set_drvdata(pdev, ds1374_rtc);
 
-	INIT_WORK(&ds1374->work, ds1374_work);
-	mutex_init(&ds1374->mutex);
+	INIT_WORK(&ds1374_rtc->work, ds1374_work);
+	mutex_init(&ds1374_rtc->mutex);
 
-	ret = ds1374_check_rtc_status(client);
-	if (ret)
+	ret = ds1374_check_rtc_status(ds1374_rtc);
+	if (ret) {
+		dev_err(dev, "Failed to check rtc status\n");
 		return ret;
+	}
 
-	if (client->irq > 0) {
-		ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0,
-					"ds1374", client);
+	/* if the mfd device indicates is configured to run with ALM
+	 * try to get the IRQ
+	 */
+	if (ds1374->mode == DS1374_MODE_RTC_ALM && ds1374->irq > 0) {
+		ret = devm_request_irq(dev, ds1374->irq,
+				       ds1374_irq, 0, "ds1374", ds1374_rtc);
 		if (ret) {
-			dev_err(&client->dev, "unable to request IRQ\n");
+			dev_err(dev, "unable to request IRQ\n");
 			return ret;
 		}
 
-		device_set_wakeup_capable(&client->dev, 1);
+		device_set_wakeup_capable(dev, 1);
+		ds1374_rtc->rtc = devm_rtc_device_register(dev,
+							   "ds1374-rtc",
+							   &ds1374_rtc_alm_ops,
+							   THIS_MODULE);
+	} else {
+		ds1374_rtc->rtc = devm_rtc_device_register(dev, "ds1374-rtc",
+							   &ds1374_rtc_ops,
+							   THIS_MODULE);
 	}
 
-	ds1374->rtc = devm_rtc_device_register(&client->dev, client->name,
-						&ds1374_rtc_ops, THIS_MODULE);
-	if (IS_ERR(ds1374->rtc)) {
-		dev_err(&client->dev, "unable to register the class device\n");
-		return PTR_ERR(ds1374->rtc);
+	if (IS_ERR(ds1374_rtc->rtc)) {
+		dev_err(dev, "unable to register the class device\n");
+		return PTR_ERR(ds1374_rtc->rtc);
 	}
-
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
-	save_client = client;
-	ret = misc_register(&ds1374_miscdev);
-	if (ret)
-		return ret;
-	ret = register_reboot_notifier(&ds1374_wdt_notifier);
-	if (ret) {
-		misc_deregister(&ds1374_miscdev);
-		return ret;
-	}
-	ds1374_wdt_settimeout(131072);
-#endif
-
 	return 0;
 }
 
-static int ds1374_remove(struct i2c_client *client)
+static int ds1374_rtc_remove(struct platform_device *pdev)
 {
-	struct ds1374 *ds1374 = i2c_get_clientdata(client);
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
-	misc_deregister(&ds1374_miscdev);
-	ds1374_miscdev.parent = NULL;
-	unregister_reboot_notifier(&ds1374_wdt_notifier);
-#endif
+	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
 
-	if (client->irq > 0) {
-		mutex_lock(&ds1374->mutex);
-		ds1374->exiting = 1;
-		mutex_unlock(&ds1374->mutex);
+	if (ds1374_rtc->chip->irq > 0) {
+		mutex_lock(&ds1374_rtc->mutex);
+		ds1374_rtc->exiting = 1;
+		mutex_unlock(&ds1374_rtc->mutex);
 
-		devm_free_irq(&client->dev, client->irq, client);
-		cancel_work_sync(&ds1374->work);
+		devm_free_irq(&pdev->dev, ds1374_rtc->chip->irq,
+			      ds1374_rtc);
+		cancel_work_sync(&ds1374_rtc->work);
 	}
 
 	return 0;
@@ -688,38 +351,39 @@ static int ds1374_remove(struct i2c_client *client)
 #ifdef CONFIG_PM_SLEEP
 static int ds1374_suspend(struct device *dev)
 {
-	struct i2c_client *client = to_i2c_client(dev);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
 
-	if (client->irq > 0 && device_may_wakeup(&client->dev))
-		enable_irq_wake(client->irq);
+	if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
+		enable_irq_wake(ds1374_rtc->chip->irq);
 	return 0;
 }
 
 static int ds1374_resume(struct device *dev)
 {
-	struct i2c_client *client = to_i2c_client(dev);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
 
-	if (client->irq > 0 && device_may_wakeup(&client->dev))
-		disable_irq_wake(client->irq);
+	if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
+		disable_irq_wake(ds1374_rtc->chip->irq);
 	return 0;
 }
 #endif
 
-static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
+static SIMPLE_DEV_PM_OPS(ds1374_rtc_pm, ds1374_rtc_suspend, ds1374_rtc_resume);
 
-static struct i2c_driver ds1374_driver = {
+static struct platform_driver ds1374_rtc_driver = {
 	.driver = {
-		.name = "rtc-ds1374",
-		.of_match_table = of_match_ptr(ds1374_of_match),
-		.pm = &ds1374_pm,
+		.name = "ds1374-rtc",
+		.pm = &ds1374_rtc_pm,
 	},
-	.probe = ds1374_probe,
-	.remove = ds1374_remove,
-	.id_table = ds1374_id,
+	.probe = ds1374_rtc_probe,
+	.remove = ds1374_rtc_remove,
 };
-
-module_i2c_driver(ds1374_driver);
+module_platform_driver(ds1374_rtc_driver);
 
 MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
 MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ds1374-rtc");
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 2696493..2ecf899 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -120,6 +120,16 @@ config DA9062_WATCHDOG
 
 	  This driver can be built as a module. The module name is da9062_wdt.
 
+config DS1374_WATCHDOG
+	tristate "Maxim/Dallas 1374 Watchdog"
+	depends on MFD_DS1374
+	depends on REGMAP_I2C
+	select WATCHDOG_CORE
+	help
+	  Support for the watchdog in the Maxim/Dallas DS1374 MFD.
+
+	  This driver can be built as a module. The module name is ds1374-wdt.
+
 config GPIO_WATCHDOG
 	tristate "Watchdog device controlled through GPIO-line"
 	depends on OF_GPIO
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 3aafd99..46aa950 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
 obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o
 obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
 obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
+obj-$(CONFIG_DS1374_WATCHDOG)	+= ds1374-wdt.o
 obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
 obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
 obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
diff --git a/drivers/watchdog/ds1374-wdt.c b/drivers/watchdog/ds1374-wdt.c
new file mode 100644
index 0000000..d078430
--- /dev/null
+++ b/drivers/watchdog/ds1374-wdt.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2017, National Instruments Corp.
+ *
+ * Dallas/Maxim DS1374 Watchdog Driver, heavily based on the older
+ * drivers/rtc/rtc-ds1374.c implementation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/watchdog.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ds1374.h>
+
+#define DS1374_WDT_RATE 4096 /* Hz */
+#define DS1374_WDT_MIN_TIMEOUT 1 /* seconds */
+#define DS1374_WDT_DEFAULT_TIMEOUT 30 /* seconds */
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0444);
+MODULE_PARM_DESC(nowayout,
+		 "Watchdog cannot be stopped once started (default="
+		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int timeout;
+module_param(timeout, int, 0444);
+MODULE_PARM_DESC(timeout, "Watchdog timeout");
+
+struct ds1374_wdt {
+	struct ds1374 *chip;
+	struct device *dev;
+	struct watchdog_device wdd;
+};
+
+static int ds1374_wdt_stop(struct watchdog_device *wdog)
+{
+	struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
+	int err;
+
+	err = regmap_update_bits(ds1374_wdt->chip->regmap, DS1374_REG_CR,
+				 DS1374_REG_CR_WACE, 0);
+	if (err)
+		return err;
+
+	if (ds1374_wdt->chip->remapped_reset)
+		return regmap_update_bits(ds1374_wdt->chip->regmap,
+					  DS1374_REG_CR, DS1374_REG_CR_WDSTR,
+					  0);
+
+	return 0;
+}
+
+static int ds1374_wdt_ping(struct watchdog_device *wdog)
+{
+	struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
+	u32 val;
+	int err;
+
+	err = ds1374_read_bulk(ds1374_wdt->chip, &val, DS1374_REG_WDALM0, 3);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int ds1374_wdt_set_timeout(struct watchdog_device *wdog,
+				  unsigned int t)
+{
+	struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
+	struct regmap *regmap = ds1374_wdt->chip->regmap;
+	unsigned int timeout = DS1374_WDT_RATE * t;
+	u8 remapped = ds1374_wdt->chip->remapped_reset
+		? DS1374_REG_CR_WDSTR : 0;
+	int err;
+
+	err = regmap_update_bits(regmap, DS1374_REG_CR,
+				 DS1374_REG_CR_WACE | DS1374_REG_CR_AIE, 0);
+
+	err = ds1374_write_bulk(ds1374_wdt->chip, timeout,
+				DS1374_REG_WDALM0, 3);
+	if (err) {
+		dev_err(ds1374_wdt->dev, "couldn't set new watchdog time\n");
+		return err;
+	}
+
+	ds1374_wdt->wdd.timeout = t;
+
+	return regmap_update_bits(regmap, DS1374_REG_CR,
+				  (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
+				   DS1374_REG_CR_AIE | DS1374_REG_CR_WDSTR),
+				  (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
+				   DS1374_REG_CR_AIE | remapped));
+}
+
+static int ds1374_wdt_start(struct watchdog_device *wdog)
+{
+	int err;
+	struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
+
+	err = ds1374_wdt_set_timeout(wdog, wdog->timeout);
+	if (err) {
+		dev_err(ds1374_wdt->dev, "%s: failed to set timeout (%d) %u\n",
+			__func__, err, wdog->timeout);
+		return err;
+	}
+
+	err = ds1374_wdt_ping(wdog);
+	if (err) {
+		dev_err(ds1374_wdt->dev, "%s: failed to ping (%d)\n", __func__,
+			err);
+		return err;
+	}
+
+	return 0;
+}
+
+static const struct watchdog_info ds1374_wdt_info = {
+	.identity       = "DS1374 WTD",
+	.options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+			| WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops ds1374_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= ds1374_wdt_start,
+	.stop		= ds1374_wdt_stop,
+	.set_timeout	= ds1374_wdt_set_timeout,
+	.ping		= ds1374_wdt_ping,
+};
+
+static int ds1374_wdt_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
+	struct ds1374_wdt *priv;
+	int err;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	priv->chip = ds1374;
+	platform_set_drvdata(pdev, priv);
+
+	priv->wdd.info		= &ds1374_wdt_info;
+	priv->wdd.ops		= &ds1374_wdt_ops;
+	priv->wdd.min_timeout	= DS1374_WDT_MIN_TIMEOUT;
+	priv->wdd.timeout	= DS1374_WDT_DEFAULT_TIMEOUT;
+	priv->wdd.max_timeout	= 0x1ffffff / DS1374_WDT_RATE;
+	priv->wdd.parent	= dev->parent;
+
+	watchdog_init_timeout(&priv->wdd, timeout, dev);
+	watchdog_set_nowayout(&priv->wdd, nowayout);
+	watchdog_stop_on_reboot(&priv->wdd);
+	watchdog_set_drvdata(&priv->wdd, priv);
+
+	err = devm_watchdog_register_device(dev, &priv->wdd);
+	if (err) {
+		dev_err(dev, "Failed to register watchdog device\n");
+		return err;
+	}
+
+	dev_info(dev, "Registered DS1374 Watchdog\n");
+
+	return 0;
+}
+
+static int ds1374_wdt_remove(struct platform_device *pdev)
+{
+	struct ds1374_wdt *priv = platform_get_drvdata(pdev);
+
+	if (!nowayout)
+		ds1374_wdt_stop(&priv->wdd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ds1374_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int ds1374_resume(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ds1374_wdt_pm, ds1374_wdt_suspend, ds1374_wdt_resume);
+
+static struct platform_driver ds1374_wdt_driver = {
+	.probe = ds1374_wdt_probe,
+	.remove = ds1374_wdt_remove,
+	.driver = {
+		.name = "ds1374-wdt",
+		.pm = &ds1374_wdt_pm,
+	},
+};
+module_platform_driver(ds1374_wdt_driver);
+
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
+MODULE_DESCRIPTION("Maxim/Dallas DS1374 WDT Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ds1374-wdt");
diff --git a/include/linux/mfd/ds1374.h b/include/linux/mfd/ds1374.h
new file mode 100644
index 0000000..7b697f8
--- /dev/null
+++ b/include/linux/mfd/ds1374.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, National Instruments Corp.
+ *
+ * Multi Function Device for Dallas/Maxim DS1374 RTC/WDT
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ */
+
+#ifndef MFD_DS1374_H
+#define MFD_DS1374_H
+
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+enum ds1374_mode {
+	DS1374_MODE_RTC_ONLY,
+	DS1374_MODE_RTC_ALM,
+	DS1374_MODE_RTC_WDT,
+};
+
+/* Register definitions to for all subdrivers
+ */
+#define DS1374_REG_TOD0		0x00 /* Time of Day */
+#define DS1374_REG_TOD1		0x01
+#define DS1374_REG_TOD2		0x02
+#define DS1374_REG_TOD3		0x03
+#define DS1374_REG_WDALM0	0x04 /* Watchdog/Alarm */
+#define DS1374_REG_WDALM1	0x05
+#define DS1374_REG_WDALM2	0x06
+#define DS1374_REG_CR		0x07 /* Control */
+#define DS1374_REG_CR_AIE	0x01 /* Alarm Int. Enable */
+#define DS1374_REG_CR_WDSTR	0x08 /* 1=Reset on INT, 0=Rreset on RST */
+#define DS1374_REG_CR_WDALM	0x20 /* 1=Watchdog, 0=Alarm */
+#define DS1374_REG_CR_WACE	0x40 /* WD/Alarm counter enable */
+#define DS1374_REG_SR		0x08 /* Status */
+#define DS1374_REG_SR_OSF	0x80 /* Oscillator Stop Flag */
+#define DS1374_REG_SR_AF	0x01 /* Alarm Flag */
+#define DS1374_REG_TCR		0x09 /* Trickle Charge */
+
+struct ds1374 {
+	struct i2c_client *client;
+	struct regmap *regmap;
+	int irq;
+	enum ds1374_mode mode;
+	bool remapped_reset;
+};
+
+int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes);
+
+int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes);
+
+#endif /* MFD_DS1374_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [RFC/PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device
  2017-05-09 18:20 ` [RFC/PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device Moritz Fischer
@ 2017-05-09 23:48   ` Moritz Fischer
  0 siblings, 0 replies; 6+ messages in thread
From: Moritz Fischer @ 2017-05-09 23:48 UTC (permalink / raw)
  To: linux-watchdog
  Cc: linux-watchdog, linux-kernel, lee.jones, devicetree, linux, wim,
	alexandre.belloni, a.zummo, mark.rutland, robh+dt

[-- Attachment #1: Type: text/plain, Size: 47514 bytes --]

On Tue, May 09, 2017 at 11:20:21AM -0700, Moritz Fischer wrote:
> From: Moritz Fischer <moritz.fischer@ettus.com>
> 
> Add support for the Maxim/Dallas DS1374 RTC/WDT with trickle charger.
> The device can either be configured as simple RTC, as simple RTC with
> Alarm (IRQ) as well as simple RTC with watchdog timer.
> 
> Break up the old monolithic driver in drivers/rtc/rtc-ds1374.c into:
> - rtc part in drivers/rtc/rtc-ds1374.c
> - watchdog part under drivers/watchdog/ds1374-wdt.c
> - mfd part drivers/mfd/ds1374.c
> 
> The MFD part takes care of trickle charging and mode selection,
> since the usage modes of a) RTC + Alarm or b) RTC + WDT
> are mutually exclusive.
> 
> Signed-off-by: Moritz Fischer <mdf@kernel.org>
> ---
> 
> Hi all,
> 
> attached changeset is huge, I do realize that. I couldn't find a good
> way of splitting it and maintaining bisectability.
> 
> Not sure if the way the rtc/wdt drivers access the parent is recommended,
> or whether one should rather pass down just the 'mode' and regmap in platform
> data.
> 
> Thanks for your time reviewing,
> 
> Moritz
> 
> 
> ---
>  drivers/mfd/Kconfig           |  10 +
>  drivers/mfd/Makefile          |   1 +
>  drivers/mfd/ds1374.c          | 267 ++++++++++++++++++
>  drivers/rtc/rtc-ds1374.c      | 640 ++++++++++--------------------------------
>  drivers/watchdog/Kconfig      |  10 +
>  drivers/watchdog/Makefile     |   1 +
>  drivers/watchdog/ds1374-wdt.c | 214 ++++++++++++++
>  include/linux/mfd/ds1374.h    |  59 ++++
>  8 files changed, 714 insertions(+), 488 deletions(-)
>  create mode 100644 drivers/mfd/ds1374.c
>  create mode 100644 drivers/watchdog/ds1374-wdt.c
>  create mode 100644 include/linux/mfd/ds1374.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 3eb5c93..2dfef3c 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -203,6 +203,16 @@ config MFD_CROS_EC_SPI
>  	  response time cannot be guaranteed, we support ignoring
>  	  'pre-amble' bytes before the response actually starts.
>  
> +config MFD_DS1374
> +	tristate "Dallas/Maxim DS1374 RTC/WDT/ALARM (I2C)"
> +	select MFD_CORE
> +	depends on I2C
> +	depends on REGMAP_I2C
> +
> +	 ---help---
> +	  This driver supports the Dallas Maxim DS1374 multi function chip.
> +	  The chip combines an RTC, trickle charger, Watchdog or Alarm.
> +
>  config MFD_ASIC3
>  	bool "Compaq ASIC3"
>  	depends on GPIOLIB && ARM
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index c16bf1e..b5cfcf4 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -15,6 +15,7 @@ cros_ec_core-$(CONFIG_ACPI)	+= cros_ec_acpi_gpe.o
>  obj-$(CONFIG_MFD_CROS_EC)	+= cros_ec_core.o
>  obj-$(CONFIG_MFD_CROS_EC_I2C)	+= cros_ec_i2c.o
>  obj-$(CONFIG_MFD_CROS_EC_SPI)	+= cros_ec_spi.o
> +obj-$(CONFIG_MFD_DS1374)	+= ds1374.o
>  obj-$(CONFIG_MFD_EXYNOS_LPASS)	+= exynos-lpass.o
>  
>  rtsx_pci-objs			:= rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
> diff --git a/drivers/mfd/ds1374.c b/drivers/mfd/ds1374.c
> new file mode 100644
> index 0000000..be82b38
> --- /dev/null
> +++ b/drivers/mfd/ds1374.c
> @@ -0,0 +1,267 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Dallas/Maxim DS1374 Multi Function Device Driver
> + *
> + * The trickle charger code was taken more ore less 1:1 from
> + * drivers/rtc/rtc-1390.c
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * 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/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/pm.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/ds1374.h>
> +
> +#define DS1374_TRICKLE_CHARGER_ENABLE	0xa0
> +#define DS1374_TRICKLE_CHARGER_ENABLE_MASK 0xe0
> +
> +#define DS1374_TRICKLE_CHARGER_250_OHM	0x01
> +#define DS1374_TRICKLE_CHARGER_2K_OHM	0x02
> +#define DS1374_TRICKLE_CHARGER_4K_OHM	0x03
> +#define DS1374_TRICKLE_CHARGER_ROUT_MASK 0x03
> +
> +#define DS1374_TRICKLE_CHARGER_NO_DIODE	0x04
> +#define DS1374_TRICKLE_CHARGER_DIODE	0x08
> +#define DS1374_TRICKLE_CHARGER_DIODE_MASK 0xc
> +
> +static const struct regmap_range volatile_ranges[] = {
> +	regmap_reg_range(DS1374_REG_TOD0, DS1374_REG_WDALM2),
> +	regmap_reg_range(DS1374_REG_SR, DS1374_REG_SR),
> +};
> +
> +static const struct regmap_access_table ds1374_volatile_table = {
> +	.yes_ranges = volatile_ranges,
> +	.n_yes_ranges = ARRAY_SIZE(volatile_ranges),
> +};
> +
> +static struct regmap_config ds1374_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = DS1374_REG_TCR,
> +	.volatile_table	= &ds1374_volatile_table,
> +	.cache_type	= REGCACHE_RBTREE,
> +};
> +
> +static struct mfd_cell ds1374_wdt_cell = {
> +	.name = "ds1374-wdt",
> +};
> +
> +static struct mfd_cell ds1374_rtc_cell = {
> +	.name = "ds1374-rtc",
> +};
> +
> +static int ds1374_add_device(struct ds1374 *chip,
> +			     struct mfd_cell *cell)
> +{
> +	cell->platform_data = chip;
> +	cell->pdata_size = sizeof(*chip);
> +
> +	return mfd_add_devices(&chip->client->dev, PLATFORM_DEVID_AUTO,
> +			       cell, 1, NULL, 0, NULL);
> +}
> +
> +static int ds1374_trickle_of_init(struct ds1374 *ds1374)
> +{
> +	u32 ohms = 0;
> +	u8 value;
> +	struct i2c_client *client = ds1374->client;
> +
> +	if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms",
> +				 &ohms))
> +		return 0;
> +
> +	/* Enable charger */
> +	value = DS1374_TRICKLE_CHARGER_ENABLE;
> +	if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
> +		value |= DS1374_TRICKLE_CHARGER_NO_DIODE;
> +	else
> +		value |= DS1374_TRICKLE_CHARGER_DIODE;
> +
> +	/* Resistor select */
> +	switch (ohms) {
> +	case 250:
> +		value |= DS1374_TRICKLE_CHARGER_250_OHM;
> +		break;
> +	case 2000:
> +		value |= DS1374_TRICKLE_CHARGER_2K_OHM;
> +		break;
> +	case 4000:
> +		value |= DS1374_TRICKLE_CHARGER_4K_OHM;
> +		break;
> +	default:
> +		dev_warn(&client->dev,
> +			 "Unsupported ohm value %02ux in dt\n", ohms);
> +		return -EINVAL;
> +	}
> +	dev_dbg(&client->dev, "Trickle charge value is 0x%02x\n", value);
> +
> +	return regmap_write(ds1374->regmap, DS1374_REG_TCR, value);
> +}
> +
> +int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes)
> +{
> +	u8 buf[4];
> +	int ret;
> +	int i;
> +
> +	if (WARN_ON(nbytes > 4))
> +		return -EINVAL;
> +
> +	ret = regmap_bulk_read(ds1374->regmap, reg, buf, nbytes);
> +	if (ret) {
> +		dev_err(&ds1374->client->dev,
> +			"Failed to bulkread n = %d at R%d\n",
> +			nbytes, reg);
> +		return ret;
> +	}
> +
> +	for (i = nbytes - 1, *time = 0; i >= 0; i--)
> +		*time = (*time << 8) | buf[i];
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(ds1374_read_bulk);
> +
> +int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes)
> +{
> +	u8 buf[4];
> +	int i;
> +
> +	if (nbytes > 4) {
> +		WARN_ON(1);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < nbytes; i++) {
> +		buf[i] = time & 0xff;
> +		time >>= 8;
> +	}
> +
> +	return regmap_bulk_write(ds1374->regmap, reg, buf, nbytes);
> +}
> +EXPORT_SYMBOL_GPL(ds1374_write_bulk);
> +
> +static int ds1374_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct ds1374 *ds1374;
> +	u32 mode;
> +	int err;
> +
> +	ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
> +	if (!ds1374)
> +		return -ENOMEM;
> +
> +	ds1374->regmap = devm_regmap_init_i2c(client, &ds1374_regmap_config);
> +	if (IS_ERR(ds1374->regmap))
> +		return PTR_ERR(ds1374->regmap);
> +
> +	if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
> +		err = of_property_read_u32(client->dev.of_node,
> +					   "dallas,ds1374-mode", &mode);
> +		if (err < 0) {
> +			dev_err(&client->dev, "missing dallas,ds1374-mode property\n");
> +			return -EINVAL;
> +		}
> +
> +		ds1374->remapped_reset
> +			= of_property_read_bool(client->dev.of_node,
> +						"dallas,ds1374-remap-wdt-reset");
> +
> +		ds1374->mode = (enum ds1374_mode)mode;
> +	} else if (IS_ENABLED(CONFIG_RTC_DRV_DS1374_WDT)) {
> +		ds1374->mode = DS1374_MODE_RTC_WDT;
> +	} else {
> +		ds1374->mode = DS1374_MODE_RTC_ALM;
> +	}
> +
> +	ds1374->client = client;
> +	ds1374->irq = client->irq;
> +	i2c_set_clientdata(client, ds1374);
> +
> +	/* check if we're supposed to trickle charge */
> +	err = ds1374_trickle_of_init(ds1374);
> +	if (err) {
> +		dev_err(&client->dev, "Failed to init trickle charger!\n");
> +		return err;
> +	}
> +
> +	/* we always have a rtc */
> +	err = ds1374_add_device(ds1374, &ds1374_rtc_cell);
> +	if (err)
> +		return err;
> +
> +	/* we might have a watchdog if configured that way */
> +	if (ds1374->mode == DS1374_MODE_RTC_WDT)
> +		return ds1374_add_device(ds1374, &ds1374_wdt_cell);
> +
> +	return err;
> +}
> +
> +static const struct i2c_device_id ds1374_id[] = {
> +	{ "ds1374", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, ds1374_id);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id ds1374_of_match[] = {
> +	{ .compatible = "dallas,ds1374" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, ds1374_of_match);
> +#endif
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ds1374_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int ds1374_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
> +
> +static struct i2c_driver ds1374_driver = {
> +	.driver = {
> +		.name = "ds1374",
> +		.of_match_table = of_match_ptr(ds1374_of_match),
> +		.pm = &ds1374_pm,
> +	},
> +	.probe = ds1374_probe,
> +	.id_table = ds1374_id,
> +};
> +
> +static int __init ds1374_init(void)
> +{
> +	return i2c_add_driver(&ds1374_driver);
> +}
> +subsys_initcall(ds1374_init);
> +
> +static void __exit ds1374_exit(void)
> +{
> +	i2c_del_driver(&ds1374_driver);
> +}
> +module_exit(ds1374_exit);
> +
> +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
> +MODULE_DESCRIPTION("Maxim/Dallas DS1374 MFD Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
> index 38a2e9e..7b2fbd9 100644
> --- a/drivers/rtc/rtc-ds1374.c
> +++ b/drivers/rtc/rtc-ds1374.c
> @@ -1,9 +1,10 @@
>  /*
> - * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
> + * RTC driver for the Maxim/Dallas DS1374 Real-Time Clock via MFD
>   *
>   * Based on code by Randy Vinson <rvinson@mvista.com>,
>   * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>.
>   *
> + * Copyright (C) 2017 National Instruments Corp
>   * Copyright (C) 2014 Rose Technology
>   * Copyright (C) 2006-2007 Freescale Semiconductor
>   *
> @@ -12,64 +13,24 @@
>   * is licensed "as is" without any warranty of any kind, whether express
>   * or implied.
>   */
> -/*
> - * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
> - * recommened in .../Documentation/i2c/writing-clients section
> - * "Sending and receiving", using SMBus level communication is preferred.
> - */
>  
>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>  
>  #include <linux/kernel.h>
>  #include <linux/module.h>
>  #include <linux/interrupt.h>
> -#include <linux/i2c.h>
>  #include <linux/rtc.h>
>  #include <linux/bcd.h>
>  #include <linux/workqueue.h>
>  #include <linux/slab.h>
>  #include <linux/pm.h>
> -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> -#include <linux/fs.h>
> -#include <linux/ioctl.h>
> -#include <linux/miscdevice.h>
> -#include <linux/reboot.h>
> -#include <linux/watchdog.h>
> -#endif
> -
> -#define DS1374_REG_TOD0		0x00 /* Time of Day */
> -#define DS1374_REG_TOD1		0x01
> -#define DS1374_REG_TOD2		0x02
> -#define DS1374_REG_TOD3		0x03
> -#define DS1374_REG_WDALM0	0x04 /* Watchdog/Alarm */
> -#define DS1374_REG_WDALM1	0x05
> -#define DS1374_REG_WDALM2	0x06
> -#define DS1374_REG_CR		0x07 /* Control */
> -#define DS1374_REG_CR_AIE	0x01 /* Alarm Int. Enable */
> -#define DS1374_REG_CR_WDALM	0x20 /* 1=Watchdog, 0=Alarm */
> -#define DS1374_REG_CR_WACE	0x40 /* WD/Alarm counter enable */
> -#define DS1374_REG_SR		0x08 /* Status */
> -#define DS1374_REG_SR_OSF	0x80 /* Oscillator Stop Flag */
> -#define DS1374_REG_SR_AF	0x01 /* Alarm Flag */
> -#define DS1374_REG_TCR		0x09 /* Trickle Charge */
> -
> -static const struct i2c_device_id ds1374_id[] = {
> -	{ "ds1374", 0 },
> -	{ }
> -};
> -MODULE_DEVICE_TABLE(i2c, ds1374_id);
> -
> -#ifdef CONFIG_OF
> -static const struct of_device_id ds1374_of_match[] = {
> -	{ .compatible = "dallas,ds1374" },
> -	{ }
> -};
> -MODULE_DEVICE_TABLE(of, ds1374_of_match);
> -#endif
> +#include <linux/regmap.h>
> +#include <linux/mfd/ds1374.h>
> +#include <linux/platform_device.h>
>  
> -struct ds1374 {
> -	struct i2c_client *client;
> +struct ds1374_rtc {
>  	struct rtc_device *rtc;
> +	struct ds1374 *chip;
>  	struct work_struct work;
>  
>  	/* The mutex protects alarm operations, and prevents a race
> @@ -80,89 +41,44 @@ struct ds1374 {
>  	int exiting;
>  };
>  
> -static struct i2c_driver ds1374_driver;
> -
> -static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
> -			   int reg, int nbytes)
> -{
> -	u8 buf[4];
> -	int ret;
> -	int i;
> -
> -	if (WARN_ON(nbytes > 4))
> -		return -EINVAL;
> -
> -	ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
> -
> -	if (ret < 0)
> -		return ret;
> -	if (ret < nbytes)
> -		return -EIO;
> -
> -	for (i = nbytes - 1, *time = 0; i >= 0; i--)
> -		*time = (*time << 8) | buf[i];
> -
> -	return 0;
> -}
> -
> -static int ds1374_write_rtc(struct i2c_client *client, u32 time,
> -			    int reg, int nbytes)
> -{
> -	u8 buf[4];
> -	int i;
> -
> -	if (nbytes > 4) {
> -		WARN_ON(1);
> -		return -EINVAL;
> -	}
> -
> -	for (i = 0; i < nbytes; i++) {
> -		buf[i] = time & 0xff;
> -		time >>= 8;
> -	}
> -
> -	return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf);
> -}
> -
> -static int ds1374_check_rtc_status(struct i2c_client *client)
> +static int ds1374_check_rtc_status(struct ds1374_rtc *ds1374)
>  {
>  	int ret = 0;
> -	int control, stat;
> +	unsigned int control, stat;
>  
> -	stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
> -	if (stat < 0)
> +	ret = regmap_read(ds1374->chip->regmap, DS1374_REG_SR, &stat);
> +	if (ret)
>  		return stat;
>  
>  	if (stat & DS1374_REG_SR_OSF)
> -		dev_warn(&client->dev,
> +		dev_warn(&ds1374->chip->client->dev,
>  			 "oscillator discontinuity flagged, time unreliable\n");
>  
> -	stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF);
> -
> -	ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
> -	if (ret < 0)
> +	ret = regmap_update_bits(ds1374->chip->regmap, DS1374_REG_SR,
> +				 DS1374_REG_SR_OSF | DS1374_REG_SR_AF, 0);
> +	if (ret)
>  		return ret;
>  
>  	/* If the alarm is pending, clear it before requesting
>  	 * the interrupt, so an interrupt event isn't reported
>  	 * before everything is initialized.
>  	 */
> -
> -	control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> -	if (control < 0)
> -		return control;
> +	ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &control);
> +	if (ret)
> +		return ret;
>  
>  	control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
> -	return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
> +	return regmap_write(ds1374->chip->regmap, DS1374_REG_CR, control);
>  }
>  
>  static int ds1374_read_time(struct device *dev, struct rtc_time *time)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
>  	u32 itime;
>  	int ret;
>  
> -	ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4);
> +	ret = ds1374_read_bulk(ds1374_rtc->chip, &itime, DS1374_REG_TOD0, 4);
>  	if (!ret)
>  		rtc_time_to_tm(itime, time);
>  
> @@ -171,44 +87,47 @@ static int ds1374_read_time(struct device *dev, struct rtc_time *time)
>  
>  static int ds1374_set_time(struct device *dev, struct rtc_time *time)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
>  	unsigned long itime;
>  
>  	rtc_tm_to_time(time, &itime);
> -	return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4);
> +	return ds1374_write_bulk(ds1374_rtc->chip, itime, DS1374_REG_TOD0, 4);
>  }
>  
> -#ifndef CONFIG_RTC_DRV_DS1374_WDT
>  /* The ds1374 has a decrementer for an alarm, rather than a comparator.
>   * If the time of day is changed, then the alarm will need to be
>   * reset.
>   */
>  static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct ds1374 *ds1374 = i2c_get_clientdata(client);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> +	struct ds1374 *ds1374 = ds1374_rtc->chip;
> +
>  	u32 now, cur_alarm;
> -	int cr, sr;
> +	unsigned int cr, sr;
>  	int ret = 0;
>  
> -	if (client->irq <= 0)
> +	if (ds1374->irq <= 0)
>  		return -EINVAL;
>  
> -	mutex_lock(&ds1374->mutex);
> +	mutex_lock(&ds1374_rtc->mutex);
>  
> -	cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> +	ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
>  	if (ret < 0)
>  		goto out;
>  
> -	sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
> +	ret = regmap_read(ds1374->regmap, DS1374_REG_SR, &sr);
>  	if (ret < 0)
>  		goto out;
>  
> -	ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
> +	ret = ds1374_read_bulk(ds1374_rtc->chip, &now, DS1374_REG_TOD0, 4);
>  	if (ret)
>  		goto out;
>  
> -	ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
> +	ret = ds1374_read_bulk(ds1374_rtc->chip, &cur_alarm,
> +			       DS1374_REG_WDALM0, 3);
>  	if (ret)
>  		goto out;
>  
> @@ -217,20 +136,21 @@ static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
>  	alarm->pending = !!(sr & DS1374_REG_SR_AF);
>  
>  out:
> -	mutex_unlock(&ds1374->mutex);
> +	mutex_unlock(&ds1374_rtc->mutex);
>  	return ret;
>  }
>  
>  static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct ds1374 *ds1374 = i2c_get_clientdata(client);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
> +	struct ds1374 *ds1374 = ds1374_rtc->chip;
> +
>  	struct rtc_time now;
>  	unsigned long new_alarm, itime;
> -	int cr;
>  	int ret = 0;
>  
> -	if (client->irq <= 0)
> +	if (ds1374->irq <= 0)
>  		return -EINVAL;
>  
>  	ret = ds1374_read_time(dev, &now);
> @@ -251,435 +171,178 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
>  	else
>  		new_alarm -= itime;
>  
> -	mutex_lock(&ds1374->mutex);
> -
> -	ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> -	if (ret < 0)
> -		goto out;
> +	mutex_lock(&ds1374_rtc->mutex);
>  
>  	/* Disable any existing alarm before setting the new one
> -	 * (or lack thereof). */
> -	cr &= ~DS1374_REG_CR_WACE;
> -
> -	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
> -	if (ret < 0)
> -		goto out;
> +	 * (or lack thereof).
> +	 */
> +	ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
> +				 DS1374_REG_CR_WACE, 0);
>  
> -	ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
> +	ret = ds1374_write_bulk(ds1374_rtc->chip, new_alarm,
> +				DS1374_REG_WDALM0, 3);
>  	if (ret)
>  		goto out;
>  
>  	if (alarm->enabled) {
> -		cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
> -		cr &= ~DS1374_REG_CR_WDALM;
> -
> -		ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
> +		ret = regmap_update_bits(ds1374->regmap, DS1374_REG_CR,
> +					 DS1374_REG_CR_WACE | DS1374_REG_CR_AIE
> +					 | DS1374_REG_CR_WDALM,
> +					 DS1374_REG_CR_WACE
> +					 | DS1374_REG_CR_AIE);
>  	}
>  
>  out:
> -	mutex_unlock(&ds1374->mutex);
> +	mutex_unlock(&ds1374_rtc->mutex);
>  	return ret;
>  }
> -#endif
>  
>  static irqreturn_t ds1374_irq(int irq, void *dev_id)
>  {
> -	struct i2c_client *client = dev_id;
> -	struct ds1374 *ds1374 = i2c_get_clientdata(client);
> +	struct ds1374_rtc *ds1374_rtc = dev_id;
>  
>  	disable_irq_nosync(irq);
> -	schedule_work(&ds1374->work);
> +	schedule_work(&ds1374_rtc->work);
>  	return IRQ_HANDLED;
>  }
>  
>  static void ds1374_work(struct work_struct *work)
>  {
> -	struct ds1374 *ds1374 = container_of(work, struct ds1374, work);
> -	struct i2c_client *client = ds1374->client;
> -	int stat, control;
> +	struct ds1374_rtc *ds1374_rtc = container_of(work, struct ds1374_rtc,
> +						     work);
> +	unsigned int stat;
> +	int ret;
>  
> -	mutex_lock(&ds1374->mutex);
> +	mutex_lock(&ds1374_rtc->mutex);
>  
> -	stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
> -	if (stat < 0)
> +	ret = regmap_read(ds1374_rtc->chip->regmap, DS1374_REG_SR, &stat);
> +	if (ret)
>  		goto unlock;
>  
>  	if (stat & DS1374_REG_SR_AF) {
> -		stat &= ~DS1374_REG_SR_AF;
> -		i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
> -
> -		control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> -		if (control < 0)
> +		regmap_update_bits(ds1374_rtc->chip->regmap, DS1374_REG_SR,
> +				   DS1374_REG_SR_AF, 0);
> +
> +		ret = regmap_update_bits(ds1374_rtc->chip->regmap,
> +					 DS1374_REG_CR, DS1374_REG_CR_WACE
> +					 | DS1374_REG_CR_AIE,
> +					 0);
> +		if (ret)
>  			goto out;
>  
> -		control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
> -		i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
> -
> -		rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF);
> +		rtc_update_irq(ds1374_rtc->rtc, 1, RTC_AF | RTC_IRQF);
>  	}
>  
>  out:
> -	if (!ds1374->exiting)
> -		enable_irq(client->irq);
> +	if (!ds1374_rtc->exiting)
> +		enable_irq(ds1374_rtc->chip->irq);
>  unlock:
> -	mutex_unlock(&ds1374->mutex);
> +	mutex_unlock(&ds1374_rtc->mutex);
>  }
>  
> -#ifndef CONFIG_RTC_DRV_DS1374_WDT
>  static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> -	struct ds1374 *ds1374 = i2c_get_clientdata(client);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ds1374_rtc *ds1374 = platform_get_drvdata(pdev);
> +	unsigned int cr;
>  	int ret;
>  
>  	mutex_lock(&ds1374->mutex);
>  
> -	ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
> +	ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &cr);
>  	if (ret < 0)
>  		goto out;
>  
> -	if (enabled) {
> -		ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
> -		ret &= ~DS1374_REG_CR_WDALM;
> -	} else {
> -		ret &= ~DS1374_REG_CR_WACE;
> -	}
> -	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
> -
> +	if (enabled)
> +		regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
> +				   DS1374_REG_CR_WACE | DS1374_REG_CR_AIE |
> +			   DS1374_REG_CR_WDALM, DS1374_REG_CR_WACE |
> +			   DS1374_REG_CR_AIE);
> +	else
> +		regmap_update_bits(ds1374->chip->regmap, DS1374_REG_CR,
> +				   DS1374_REG_CR_WACE, 0);
>  out:
>  	mutex_unlock(&ds1374->mutex);
>  	return ret;
>  }
> -#endif
>  
> -static const struct rtc_class_ops ds1374_rtc_ops = {
> +static const struct rtc_class_ops ds1374_rtc_alm_ops = {
>  	.read_time = ds1374_read_time,
>  	.set_time = ds1374_set_time,
> -#ifndef CONFIG_RTC_DRV_DS1374_WDT
>  	.read_alarm = ds1374_read_alarm,
>  	.set_alarm = ds1374_set_alarm,
>  	.alarm_irq_enable = ds1374_alarm_irq_enable,
> -#endif
> -};
> -
> -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> -/*
> - *****************************************************************************
> - *
> - * Watchdog Driver
> - *
> - *****************************************************************************
> - */
> -static struct i2c_client *save_client;
> -/* Default margin */
> -#define WD_TIMO 131762
> -
> -#define DRV_NAME "DS1374 Watchdog"
> -
> -static int wdt_margin = WD_TIMO;
> -static unsigned long wdt_is_open;
> -module_param(wdt_margin, int, 0);
> -MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)");
> -
> -static const struct watchdog_info ds1374_wdt_info = {
> -	.identity       = "DS1374 WTD",
> -	.options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
> -						WDIOF_MAGICCLOSE,
>  };
>  
> -static int ds1374_wdt_settimeout(unsigned int timeout)
> -{
> -	int ret = -ENOIOCTLCMD;
> -	int cr;
> -
> -	ret = cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
> -	if (ret < 0)
> -		goto out;
> -
> -	/* Disable any existing watchdog/alarm before setting the new one */
> -	cr &= ~DS1374_REG_CR_WACE;
> -
> -	ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
> -	if (ret < 0)
> -		goto out;
> -
> -	/* Set new watchdog time */
> -	ret = ds1374_write_rtc(save_client, timeout, DS1374_REG_WDALM0, 3);
> -	if (ret) {
> -		pr_info("couldn't set new watchdog time\n");
> -		goto out;
> -	}
> -
> -	/* Enable watchdog timer */
> -	cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM;
> -	cr &= ~DS1374_REG_CR_AIE;
> -
> -	ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
> -	if (ret < 0)
> -		goto out;
> -
> -	return 0;
> -out:
> -	return ret;
> -}
> -
> -
> -/*
> - * Reload the watchdog timer.  (ie, pat the watchdog)
> - */
> -static void ds1374_wdt_ping(void)
> -{
> -	u32 val;
> -	int ret = 0;
> -
> -	ret = ds1374_read_rtc(save_client, &val, DS1374_REG_WDALM0, 3);
> -	if (ret)
> -		pr_info("WD TICK FAIL!!!!!!!!!! %i\n", ret);
> -}
> -
> -static void ds1374_wdt_disable(void)
> -{
> -	int ret = -ENOIOCTLCMD;
> -	int cr;
> -
> -	cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
> -	/* Disable watchdog timer */
> -	cr &= ~DS1374_REG_CR_WACE;
> -
> -	ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
> -}
> -
> -/*
> - * Watchdog device is opened, and watchdog starts running.
> - */
> -static int ds1374_wdt_open(struct inode *inode, struct file *file)
> -{
> -	struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
> -
> -	if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
> -		mutex_lock(&ds1374->mutex);
> -		if (test_and_set_bit(0, &wdt_is_open)) {
> -			mutex_unlock(&ds1374->mutex);
> -			return -EBUSY;
> -		}
> -		/*
> -		 *      Activate
> -		 */
> -		wdt_is_open = 1;
> -		mutex_unlock(&ds1374->mutex);
> -		return nonseekable_open(inode, file);
> -	}
> -	return -ENODEV;
> -}
> -
> -/*
> - * Close the watchdog device.
> - */
> -static int ds1374_wdt_release(struct inode *inode, struct file *file)
> -{
> -	if (MINOR(inode->i_rdev) == WATCHDOG_MINOR)
> -		clear_bit(0, &wdt_is_open);
> -
> -	return 0;
> -}
> -
> -/*
> - * Pat the watchdog whenever device is written to.
> - */
> -static ssize_t ds1374_wdt_write(struct file *file, const char __user *data,
> -				size_t len, loff_t *ppos)
> -{
> -	if (len) {
> -		ds1374_wdt_ping();
> -		return 1;
> -	}
> -	return 0;
> -}
> -
> -static ssize_t ds1374_wdt_read(struct file *file, char __user *data,
> -				size_t len, loff_t *ppos)
> -{
> -	return 0;
> -}
> -
> -/*
> - * Handle commands from user-space.
> - */
> -static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd,
> -							unsigned long arg)
> -{
> -	int new_margin, options;
> -
> -	switch (cmd) {
> -	case WDIOC_GETSUPPORT:
> -		return copy_to_user((struct watchdog_info __user *)arg,
> -		&ds1374_wdt_info, sizeof(ds1374_wdt_info)) ? -EFAULT : 0;
> -
> -	case WDIOC_GETSTATUS:
> -	case WDIOC_GETBOOTSTATUS:
> -		return put_user(0, (int __user *)arg);
> -	case WDIOC_KEEPALIVE:
> -		ds1374_wdt_ping();
> -		return 0;
> -	case WDIOC_SETTIMEOUT:
> -		if (get_user(new_margin, (int __user *)arg))
> -			return -EFAULT;
> -
> -		/* the hardware's tick rate is 4096 Hz, so
> -		 * the counter value needs to be scaled accordingly
> -		 */
> -		new_margin <<= 12;
> -		if (new_margin < 1 || new_margin > 16777216)
> -			return -EINVAL;
> -
> -		wdt_margin = new_margin;
> -		ds1374_wdt_settimeout(new_margin);
> -		ds1374_wdt_ping();
> -		/* fallthrough */
> -	case WDIOC_GETTIMEOUT:
> -		/* when returning ... inverse is true */
> -		return put_user((wdt_margin >> 12), (int __user *)arg);
> -	case WDIOC_SETOPTIONS:
> -		if (copy_from_user(&options, (int __user *)arg, sizeof(int)))
> -			return -EFAULT;
> -
> -		if (options & WDIOS_DISABLECARD) {
> -			pr_info("disable watchdog\n");
> -			ds1374_wdt_disable();
> -			return 0;
> -		}
> -
> -		if (options & WDIOS_ENABLECARD) {
> -			pr_info("enable watchdog\n");
> -			ds1374_wdt_settimeout(wdt_margin);
> -			ds1374_wdt_ping();
> -			return 0;
> -		}
> -		return -EINVAL;
> -	}
> -	return -ENOTTY;
> -}
> -
> -static long ds1374_wdt_unlocked_ioctl(struct file *file, unsigned int cmd,
> -			unsigned long arg)
> -{
> -	int ret;
> -	struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
> -
> -	mutex_lock(&ds1374->mutex);
> -	ret = ds1374_wdt_ioctl(file, cmd, arg);
> -	mutex_unlock(&ds1374->mutex);
> -
> -	return ret;
> -}
> -
> -static int ds1374_wdt_notify_sys(struct notifier_block *this,
> -			unsigned long code, void *unused)
> -{
> -	if (code == SYS_DOWN || code == SYS_HALT)
> -		/* Disable Watchdog */
> -		ds1374_wdt_disable();
> -	return NOTIFY_DONE;
> -}
> -
> -static const struct file_operations ds1374_wdt_fops = {
> -	.owner			= THIS_MODULE,
> -	.read			= ds1374_wdt_read,
> -	.unlocked_ioctl		= ds1374_wdt_unlocked_ioctl,
> -	.write			= ds1374_wdt_write,
> -	.open                   = ds1374_wdt_open,
> -	.release                = ds1374_wdt_release,
> -	.llseek			= no_llseek,
> -};
> -
> -static struct miscdevice ds1374_miscdev = {
> -	.minor          = WATCHDOG_MINOR,
> -	.name           = "watchdog",
> -	.fops           = &ds1374_wdt_fops,
> -};
> -
> -static struct notifier_block ds1374_wdt_notifier = {
> -	.notifier_call = ds1374_wdt_notify_sys,
> +static const struct rtc_class_ops ds1374_rtc_ops = {
> +	.read_time = ds1374_read_time,
> +	.set_time = ds1374_set_time,
>  };
>  
> -#endif /*CONFIG_RTC_DRV_DS1374_WDT*/
> -/*
> - *****************************************************************************
> - *
> - *	Driver Interface
> - *
> - *****************************************************************************
> - */
> -static int ds1374_probe(struct i2c_client *client,
> -			const struct i2c_device_id *id)
> +static int ds1374_rtc_probe(struct platform_device *pdev)
>  {
> -	struct ds1374 *ds1374;
> +	struct device *dev = &pdev->dev;
> +	struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
> +	struct ds1374_rtc *ds1374_rtc;
>  	int ret;
>  
> -	ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
> -	if (!ds1374)
> +	ds1374_rtc = devm_kzalloc(dev, sizeof(*ds1374_rtc), GFP_KERNEL);
> +	if (!ds1374_rtc)
>  		return -ENOMEM;
> +	ds1374_rtc->chip = ds1374;
>  
> -	ds1374->client = client;
> -	i2c_set_clientdata(client, ds1374);
> +	platform_set_drvdata(pdev, ds1374_rtc);
>  
> -	INIT_WORK(&ds1374->work, ds1374_work);
> -	mutex_init(&ds1374->mutex);
> +	INIT_WORK(&ds1374_rtc->work, ds1374_work);
> +	mutex_init(&ds1374_rtc->mutex);
>  
> -	ret = ds1374_check_rtc_status(client);
> -	if (ret)
> +	ret = ds1374_check_rtc_status(ds1374_rtc);
> +	if (ret) {
> +		dev_err(dev, "Failed to check rtc status\n");
>  		return ret;
> +	}
>  
> -	if (client->irq > 0) {
> -		ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0,
> -					"ds1374", client);
> +	/* if the mfd device indicates is configured to run with ALM
> +	 * try to get the IRQ
> +	 */
> +	if (ds1374->mode == DS1374_MODE_RTC_ALM && ds1374->irq > 0) {
> +		ret = devm_request_irq(dev, ds1374->irq,
> +				       ds1374_irq, 0, "ds1374", ds1374_rtc);
>  		if (ret) {
> -			dev_err(&client->dev, "unable to request IRQ\n");
> +			dev_err(dev, "unable to request IRQ\n");
>  			return ret;
>  		}
>  
> -		device_set_wakeup_capable(&client->dev, 1);
> +		device_set_wakeup_capable(dev, 1);
> +		ds1374_rtc->rtc = devm_rtc_device_register(dev,
> +							   "ds1374-rtc",
> +							   &ds1374_rtc_alm_ops,
> +							   THIS_MODULE);
> +	} else {
> +		ds1374_rtc->rtc = devm_rtc_device_register(dev, "ds1374-rtc",
> +							   &ds1374_rtc_ops,
> +							   THIS_MODULE);
>  	}
>  
> -	ds1374->rtc = devm_rtc_device_register(&client->dev, client->name,
> -						&ds1374_rtc_ops, THIS_MODULE);
> -	if (IS_ERR(ds1374->rtc)) {
> -		dev_err(&client->dev, "unable to register the class device\n");
> -		return PTR_ERR(ds1374->rtc);
> +	if (IS_ERR(ds1374_rtc->rtc)) {
> +		dev_err(dev, "unable to register the class device\n");
> +		return PTR_ERR(ds1374_rtc->rtc);
>  	}
> -
> -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> -	save_client = client;
> -	ret = misc_register(&ds1374_miscdev);
> -	if (ret)
> -		return ret;
> -	ret = register_reboot_notifier(&ds1374_wdt_notifier);
> -	if (ret) {
> -		misc_deregister(&ds1374_miscdev);
> -		return ret;
> -	}
> -	ds1374_wdt_settimeout(131072);
> -#endif
> -
>  	return 0;
>  }
>  
> -static int ds1374_remove(struct i2c_client *client)
> +static int ds1374_rtc_remove(struct platform_device *pdev)
>  {
> -	struct ds1374 *ds1374 = i2c_get_clientdata(client);
> -#ifdef CONFIG_RTC_DRV_DS1374_WDT
> -	misc_deregister(&ds1374_miscdev);
> -	ds1374_miscdev.parent = NULL;
> -	unregister_reboot_notifier(&ds1374_wdt_notifier);
> -#endif
> +	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
>  
> -	if (client->irq > 0) {
> -		mutex_lock(&ds1374->mutex);
> -		ds1374->exiting = 1;
> -		mutex_unlock(&ds1374->mutex);
> +	if (ds1374_rtc->chip->irq > 0) {
> +		mutex_lock(&ds1374_rtc->mutex);
> +		ds1374_rtc->exiting = 1;
> +		mutex_unlock(&ds1374_rtc->mutex);
>  
> -		devm_free_irq(&client->dev, client->irq, client);
> -		cancel_work_sync(&ds1374->work);
> +		devm_free_irq(&pdev->dev, ds1374_rtc->chip->irq,
> +			      ds1374_rtc);
> +		cancel_work_sync(&ds1374_rtc->work);
>  	}
>  
>  	return 0;
> @@ -688,38 +351,39 @@ static int ds1374_remove(struct i2c_client *client)
>  #ifdef CONFIG_PM_SLEEP
>  static int ds1374_suspend(struct device *dev)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
>  
> -	if (client->irq > 0 && device_may_wakeup(&client->dev))
> -		enable_irq_wake(client->irq);
> +	if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
> +		enable_irq_wake(ds1374_rtc->chip->irq);
>  	return 0;
>  }
>  
>  static int ds1374_resume(struct device *dev)
>  {
> -	struct i2c_client *client = to_i2c_client(dev);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
>  
> -	if (client->irq > 0 && device_may_wakeup(&client->dev))
> -		disable_irq_wake(client->irq);
> +	if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
> +		disable_irq_wake(ds1374_rtc->chip->irq);
>  	return 0;
>  }
>  #endif
>  
> -static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
> +static SIMPLE_DEV_PM_OPS(ds1374_rtc_pm, ds1374_rtc_suspend, ds1374_rtc_resume);

0-Day kernel test caught that one, I'll fix that in the next round ...
and add building with / wihtout CONFIG_PM_SLEEP to my list.

>  
> -static struct i2c_driver ds1374_driver = {
> +static struct platform_driver ds1374_rtc_driver = {
>  	.driver = {
> -		.name = "rtc-ds1374",
> -		.of_match_table = of_match_ptr(ds1374_of_match),
> -		.pm = &ds1374_pm,
> +		.name = "ds1374-rtc",
> +		.pm = &ds1374_rtc_pm,
>  	},
> -	.probe = ds1374_probe,
> -	.remove = ds1374_remove,
> -	.id_table = ds1374_id,
> +	.probe = ds1374_rtc_probe,
> +	.remove = ds1374_rtc_remove,
>  };
> -
> -module_i2c_driver(ds1374_driver);
> +module_platform_driver(ds1374_rtc_driver);
>  
>  MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
> +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
>  MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
>  MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:ds1374-rtc");
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 2696493..2ecf899 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -120,6 +120,16 @@ config DA9062_WATCHDOG
>  
>  	  This driver can be built as a module. The module name is da9062_wdt.
>  
> +config DS1374_WATCHDOG
> +	tristate "Maxim/Dallas 1374 Watchdog"
> +	depends on MFD_DS1374
> +	depends on REGMAP_I2C
> +	select WATCHDOG_CORE
> +	help
> +	  Support for the watchdog in the Maxim/Dallas DS1374 MFD.
> +
> +	  This driver can be built as a module. The module name is ds1374-wdt.
> +
>  config GPIO_WATCHDOG
>  	tristate "Watchdog device controlled through GPIO-line"
>  	depends on OF_GPIO
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 3aafd99..46aa950 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -60,6 +60,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
>  obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o
>  obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
>  obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
> +obj-$(CONFIG_DS1374_WATCHDOG)	+= ds1374-wdt.o
>  obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
>  obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
>  obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
> diff --git a/drivers/watchdog/ds1374-wdt.c b/drivers/watchdog/ds1374-wdt.c
> new file mode 100644
> index 0000000..d078430
> --- /dev/null
> +++ b/drivers/watchdog/ds1374-wdt.c
> @@ -0,0 +1,214 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Dallas/Maxim DS1374 Watchdog Driver, heavily based on the older
> + * drivers/rtc/rtc-ds1374.c implementation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * 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/watchdog.h>
> +#include <linux/slab.h>
> +#include <linux/regmap.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/ds1374.h>
> +
> +#define DS1374_WDT_RATE 4096 /* Hz */
> +#define DS1374_WDT_MIN_TIMEOUT 1 /* seconds */
> +#define DS1374_WDT_DEFAULT_TIMEOUT 30 /* seconds */
> +
> +static bool nowayout = WATCHDOG_NOWAYOUT;
> +module_param(nowayout, bool, 0444);
> +MODULE_PARM_DESC(nowayout,
> +		 "Watchdog cannot be stopped once started (default="
> +		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
> +
> +static unsigned int timeout;
> +module_param(timeout, int, 0444);
> +MODULE_PARM_DESC(timeout, "Watchdog timeout");
> +
> +struct ds1374_wdt {
> +	struct ds1374 *chip;
> +	struct device *dev;
> +	struct watchdog_device wdd;
> +};
> +
> +static int ds1374_wdt_stop(struct watchdog_device *wdog)
> +{
> +	struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> +	int err;
> +
> +	err = regmap_update_bits(ds1374_wdt->chip->regmap, DS1374_REG_CR,
> +				 DS1374_REG_CR_WACE, 0);
> +	if (err)
> +		return err;
> +
> +	if (ds1374_wdt->chip->remapped_reset)
> +		return regmap_update_bits(ds1374_wdt->chip->regmap,
> +					  DS1374_REG_CR, DS1374_REG_CR_WDSTR,
> +					  0);
> +
> +	return 0;
> +}
> +
> +static int ds1374_wdt_ping(struct watchdog_device *wdog)
> +{
> +	struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> +	u32 val;
> +	int err;
> +
> +	err = ds1374_read_bulk(ds1374_wdt->chip, &val, DS1374_REG_WDALM0, 3);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int ds1374_wdt_set_timeout(struct watchdog_device *wdog,
> +				  unsigned int t)
> +{
> +	struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> +	struct regmap *regmap = ds1374_wdt->chip->regmap;
> +	unsigned int timeout = DS1374_WDT_RATE * t;
> +	u8 remapped = ds1374_wdt->chip->remapped_reset
> +		? DS1374_REG_CR_WDSTR : 0;
> +	int err;
> +
> +	err = regmap_update_bits(regmap, DS1374_REG_CR,
> +				 DS1374_REG_CR_WACE | DS1374_REG_CR_AIE, 0);
> +
> +	err = ds1374_write_bulk(ds1374_wdt->chip, timeout,
> +				DS1374_REG_WDALM0, 3);
> +	if (err) {
> +		dev_err(ds1374_wdt->dev, "couldn't set new watchdog time\n");
> +		return err;
> +	}
> +
> +	ds1374_wdt->wdd.timeout = t;
> +
> +	return regmap_update_bits(regmap, DS1374_REG_CR,
> +				  (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
> +				   DS1374_REG_CR_AIE | DS1374_REG_CR_WDSTR),
> +				  (DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM |
> +				   DS1374_REG_CR_AIE | remapped));
> +}
> +
> +static int ds1374_wdt_start(struct watchdog_device *wdog)
> +{
> +	int err;
> +	struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
> +
> +	err = ds1374_wdt_set_timeout(wdog, wdog->timeout);
> +	if (err) {
> +		dev_err(ds1374_wdt->dev, "%s: failed to set timeout (%d) %u\n",
> +			__func__, err, wdog->timeout);
> +		return err;
> +	}
> +
> +	err = ds1374_wdt_ping(wdog);
> +	if (err) {
> +		dev_err(ds1374_wdt->dev, "%s: failed to ping (%d)\n", __func__,
> +			err);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info ds1374_wdt_info = {
> +	.identity       = "DS1374 WTD",
> +	.options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> +			| WDIOF_MAGICCLOSE,
> +};
> +
> +static const struct watchdog_ops ds1374_wdt_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= ds1374_wdt_start,
> +	.stop		= ds1374_wdt_stop,
> +	.set_timeout	= ds1374_wdt_set_timeout,
> +	.ping		= ds1374_wdt_ping,
> +};
> +
> +static int ds1374_wdt_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct ds1374 *ds1374 = dev_get_drvdata(dev->parent);
> +	struct ds1374_wdt *priv;
> +	int err;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +	priv->chip = ds1374;
> +	platform_set_drvdata(pdev, priv);
> +
> +	priv->wdd.info		= &ds1374_wdt_info;
> +	priv->wdd.ops		= &ds1374_wdt_ops;
> +	priv->wdd.min_timeout	= DS1374_WDT_MIN_TIMEOUT;
> +	priv->wdd.timeout	= DS1374_WDT_DEFAULT_TIMEOUT;
> +	priv->wdd.max_timeout	= 0x1ffffff / DS1374_WDT_RATE;
> +	priv->wdd.parent	= dev->parent;
> +
> +	watchdog_init_timeout(&priv->wdd, timeout, dev);
> +	watchdog_set_nowayout(&priv->wdd, nowayout);
> +	watchdog_stop_on_reboot(&priv->wdd);
> +	watchdog_set_drvdata(&priv->wdd, priv);
> +
> +	err = devm_watchdog_register_device(dev, &priv->wdd);
> +	if (err) {
> +		dev_err(dev, "Failed to register watchdog device\n");
> +		return err;
> +	}
> +
> +	dev_info(dev, "Registered DS1374 Watchdog\n");
> +
> +	return 0;
> +}
> +
> +static int ds1374_wdt_remove(struct platform_device *pdev)
> +{
> +	struct ds1374_wdt *priv = platform_get_drvdata(pdev);
> +
> +	if (!nowayout)
> +		ds1374_wdt_stop(&priv->wdd);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ds1374_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int ds1374_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(ds1374_wdt_pm, ds1374_wdt_suspend, ds1374_wdt_resume);
> +
> +static struct platform_driver ds1374_wdt_driver = {
> +	.probe = ds1374_wdt_probe,
> +	.remove = ds1374_wdt_remove,
> +	.driver = {
> +		.name = "ds1374-wdt",
> +		.pm = &ds1374_wdt_pm,
> +	},
> +};
> +module_platform_driver(ds1374_wdt_driver);
> +
> +MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
> +MODULE_DESCRIPTION("Maxim/Dallas DS1374 WDT Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:ds1374-wdt");
> diff --git a/include/linux/mfd/ds1374.h b/include/linux/mfd/ds1374.h
> new file mode 100644
> index 0000000..7b697f8
> --- /dev/null
> +++ b/include/linux/mfd/ds1374.h
> @@ -0,0 +1,59 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Multi Function Device for Dallas/Maxim DS1374 RTC/WDT
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * 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.
> + */
> +
> +#ifndef MFD_DS1374_H
> +#define MFD_DS1374_H
> +
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +
> +enum ds1374_mode {
> +	DS1374_MODE_RTC_ONLY,
> +	DS1374_MODE_RTC_ALM,
> +	DS1374_MODE_RTC_WDT,
> +};
> +
> +/* Register definitions to for all subdrivers
> + */
> +#define DS1374_REG_TOD0		0x00 /* Time of Day */
> +#define DS1374_REG_TOD1		0x01
> +#define DS1374_REG_TOD2		0x02
> +#define DS1374_REG_TOD3		0x03
> +#define DS1374_REG_WDALM0	0x04 /* Watchdog/Alarm */
> +#define DS1374_REG_WDALM1	0x05
> +#define DS1374_REG_WDALM2	0x06
> +#define DS1374_REG_CR		0x07 /* Control */
> +#define DS1374_REG_CR_AIE	0x01 /* Alarm Int. Enable */
> +#define DS1374_REG_CR_WDSTR	0x08 /* 1=Reset on INT, 0=Rreset on RST */
> +#define DS1374_REG_CR_WDALM	0x20 /* 1=Watchdog, 0=Alarm */
> +#define DS1374_REG_CR_WACE	0x40 /* WD/Alarm counter enable */
> +#define DS1374_REG_SR		0x08 /* Status */
> +#define DS1374_REG_SR_OSF	0x80 /* Oscillator Stop Flag */
> +#define DS1374_REG_SR_AF	0x01 /* Alarm Flag */
> +#define DS1374_REG_TCR		0x09 /* Trickle Charge */
> +
> +struct ds1374 {
> +	struct i2c_client *client;
> +	struct regmap *regmap;
> +	int irq;
> +	enum ds1374_mode mode;
> +	bool remapped_reset;
> +};
> +
> +int ds1374_read_bulk(struct ds1374 *ds1374, u32 *time, int reg, int nbytes);
> +
> +int ds1374_write_bulk(struct ds1374 *ds1374, u32 time, int reg, int nbytes);
> +
> +#endif /* MFD_DS1374_H */
> -- 
> 2.7.4
> 

Will wait for some more feedback,

Moritz

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding
  2017-05-09 18:20 [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding Moritz Fischer
  2017-05-09 18:20 ` [RFC/PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device Moritz Fischer
@ 2017-05-13  0:00 ` Rob Herring
  2017-05-16  2:34   ` Moritz Fischer
  1 sibling, 1 reply; 6+ messages in thread
From: Rob Herring @ 2017-05-13  0:00 UTC (permalink / raw)
  To: Moritz Fischer
  Cc: linux-watchdog, rtc-linux, linux-kernel, lee.jones, devicetree,
	linux, wim, alexandre.belloni, a.zummo, mark.rutland,
	moritz.fischer

On Tue, May 09, 2017 at 11:20:20AM -0700, Moritz Fischer wrote:
> This adds a binding for the Maxim/Dallas DS1374 MFD.
> 
> Signed-off-by: Moritz Fischer <mdf@kernel.org>
> ---
> 
> Hi all,
> 
> I'm not entirely sure aobut the binding, does anyone
> have a better suggestion for the remap-wdt-reset property?
> 
> Thanks,
> 
> Moritz
> 
> ---
>  Documentation/devicetree/bindings/mfd/ds1374.txt   | 63 ++++++++++++++++++++++
>  .../devicetree/bindings/trivial-devices.txt        |  1 -
>  drivers/rtc/Kconfig                                |  2 +
>  3 files changed, 65 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/devicetree/bindings/mfd/ds1374.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/ds1374.txt b/Documentation/devicetree/bindings/mfd/ds1374.txt
> new file mode 100644
> index 0000000..b22396f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/ds1374.txt
> @@ -0,0 +1,63 @@
> +* Device tree bindings for Maxim/Dallas DS1374 Multi Function Device (MFD)
> +
> +The Maxim/Dallas DS1374 is a multi function device that combines rtc,
> +watchdog or alarm, as well as trickle charger.
> +
> +The DS1374 is connected via I2C.
> +
> +Required properties:
> +- compatible: "dallas,ds1374"
> +- reg: I2C slave address
> +- dallas,ds1374-mode: Should be one of the following values:

Just "dallas,mode" is sufficient.

> +			<0> for RTC
> +			<1> for RTC + Alarm (Interrupt)
> +			<2> for RTC + Watchdog
> +
> +Required child:
> +A single available child device of type matching the "dallas,ds1374-mode"
> +property.
> +
> +Optional properties (watchdog):
> +- dallas,ds1374-remap-wdt-reset: Boolean describing whether the INT pin
> +				 on the device is used as interrupt for
> +				 the alarm

Isn't presence of the interrupt property or not enough? It would be 
kind of useless to have no interrupt and also not do anything on 
timeout.

> +
> +See ../watchdog/* for generic watchdog bindings.
> +
> +Optional properties (real time clock):
> +- interrupt: phandle to interrupt cell for the rtc's alarm feature
> +
> +See ../rtc/* for generic rtc bindings.
> +
> +Optional properties (trickle-charger):
> +- dallas,trickle-resistor-ohms : Selected resistor for trickle charger
> +	Values usable for ds1374 are 250, 2000, 4000
> +	Should be given if trickle charger should be enabled
> +- dallas,trickle-diode-disable : Do not use internal trickle charger diode
> +	Should be given if internal trickle charger diode should be disabled
> +
> +Example for rtc with alarm mode and interrupt:
> +
> +i2c@12ca0000 {
> +	rtc@68 {
> +		compatible = "ds1374";
> +		reg = <0x68>;
> +		interrupts = <&gpio0 62>;
> +		dallas,ds1374-mode = <2>
> +
> +		dallas,trickle-resistor-ohms = <250>;
> +		dallas,trickle-diode-disable;
> +	};
> +};
> +
> +Example for rtc with watchdog and reset on timeout, with reset remapped
> +to the INT pin:
> +
> +i2c@12ca0000 {
> +	rtc@68 {
> +		compatible = "ds1374";
> +		reg = <0x68>;
> +		dallas,ds1374-mode = <2>
> +		dallas,ds1374-remap-wdt-reset;
> +	};
> +};
> diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt
> index 3e0a34c..f7a50e5 100644
> --- a/Documentation/devicetree/bindings/trivial-devices.txt
> +++ b/Documentation/devicetree/bindings/trivial-devices.txt
> @@ -29,7 +29,6 @@ cirrus,cs42l51		Cirrus Logic CS42L51 audio codec
>  dallas,ds1307		64 x 8, Serial, I2C Real-Time Clock
>  dallas,ds1338		I2C RTC with 56-Byte NV RAM
>  dallas,ds1340		I2C RTC with Trickle Charger
> -dallas,ds1374		I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output
>  dallas,ds1631		High-Precision Digital Thermometer
>  dallas,ds1682		Total-Elapsed-Time Recorder with Alarm
>  dallas,ds1775		Tiny Digital Thermometer and Thermostat
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 8d3b957..e6763fe 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -250,6 +250,8 @@ config RTC_DRV_DS1307_CENTURY
>  
>  config RTC_DRV_DS1374
>  	tristate "Dallas/Maxim DS1374"
> +	depends on MFD_DS1374
> +	depends on REGMAP_I2C
>  	help
>  	  If you say yes here you get support for Dallas Semiconductor
>  	  DS1374 real-time clock chips. If an interrupt is associated
> -- 
> 2.7.4
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding
  2017-05-13  0:00 ` [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding Rob Herring
@ 2017-05-16  2:34   ` Moritz Fischer
  2017-05-16  8:16     ` Alexandre Belloni
  0 siblings, 1 reply; 6+ messages in thread
From: Moritz Fischer @ 2017-05-16  2:34 UTC (permalink / raw)
  To: Rob Herring
  Cc: Moritz Fischer, linux-watchdog, rtc-linux, linux-kernel,
	lee.jones, devicetree, linux, wim, alexandre.belloni, a.zummo,
	mark.rutland

[-- Attachment #1: Type: text/plain, Size: 5151 bytes --]

On Fri, May 12, 2017 at 07:00:29PM -0500, Rob Herring wrote:
> On Tue, May 09, 2017 at 11:20:20AM -0700, Moritz Fischer wrote:
> > This adds a binding for the Maxim/Dallas DS1374 MFD.
> > 
> > Signed-off-by: Moritz Fischer <mdf@kernel.org>
> > ---
> > 
> > Hi all,
> > 
> > I'm not entirely sure aobut the binding, does anyone
> > have a better suggestion for the remap-wdt-reset property?
> > 
> > Thanks,
> > 
> > Moritz
> > 
> > ---
> >  Documentation/devicetree/bindings/mfd/ds1374.txt   | 63 ++++++++++++++++++++++
> >  .../devicetree/bindings/trivial-devices.txt        |  1 -
> >  drivers/rtc/Kconfig                                |  2 +
> >  3 files changed, 65 insertions(+), 1 deletion(-)
> >  create mode 100644 Documentation/devicetree/bindings/mfd/ds1374.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/mfd/ds1374.txt b/Documentation/devicetree/bindings/mfd/ds1374.txt
> > new file mode 100644
> > index 0000000..b22396f
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/ds1374.txt
> > @@ -0,0 +1,63 @@
> > +* Device tree bindings for Maxim/Dallas DS1374 Multi Function Device (MFD)
> > +
> > +The Maxim/Dallas DS1374 is a multi function device that combines rtc,
> > +watchdog or alarm, as well as trickle charger.
> > +
> > +The DS1374 is connected via I2C.
> > +
> > +Required properties:
> > +- compatible: "dallas,ds1374"
> > +- reg: I2C slave address
> > +- dallas,ds1374-mode: Should be one of the following values:
> 
> Just "dallas,mode" is sufficient.

Will fix.
> 
> > +			<0> for RTC
> > +			<1> for RTC + Alarm (Interrupt)
> > +			<2> for RTC + Watchdog
> > +
> > +Required child:
> > +A single available child device of type matching the "dallas,ds1374-mode"
> > +property.
> > +
> > +Optional properties (watchdog):
> > +- dallas,ds1374-remap-wdt-reset: Boolean describing whether the INT pin
> > +				 on the device is used as interrupt for
> > +				 the alarm
> 
> Isn't presence of the interrupt property or not enough? It would be 
> kind of useless to have no interrupt and also not do anything on 
> timeout.

So you're saying:

If interrupt not present AND mode = <0> ignore interrupt

If interrupt present AND mode = <1> (maybe define here would be nicer),
make it an interrupt.

If interrupt not present AND mode = <2> then this implies that reset
output is mapped to RST pin.

If Interrupt present AND mode = <2> then this implies that the reset
output is (re)mapped to INT pin?

> 
> > +
> > +See ../watchdog/* for generic watchdog bindings.
> > +
> > +Optional properties (real time clock):
> > +- interrupt: phandle to interrupt cell for the rtc's alarm feature
> > +
> > +See ../rtc/* for generic rtc bindings.
> > +
> > +Optional properties (trickle-charger):
> > +- dallas,trickle-resistor-ohms : Selected resistor for trickle charger
> > +	Values usable for ds1374 are 250, 2000, 4000
> > +	Should be given if trickle charger should be enabled
> > +- dallas,trickle-diode-disable : Do not use internal trickle charger diode
> > +	Should be given if internal trickle charger diode should be disabled
> > +
> > +Example for rtc with alarm mode and interrupt:
> > +
> > +i2c@12ca0000 {
> > +	rtc@68 {
> > +		compatible = "ds1374";
> > +		reg = <0x68>;
> > +		interrupts = <&gpio0 62>;
> > +		dallas,ds1374-mode = <2>
> > +
> > +		dallas,trickle-resistor-ohms = <250>;
> > +		dallas,trickle-diode-disable;
> > +	};
> > +};
> > +
> > +Example for rtc with watchdog and reset on timeout, with reset remapped
> > +to the INT pin:
> > +
> > +i2c@12ca0000 {
> > +	rtc@68 {
> > +		compatible = "ds1374";
> > +		reg = <0x68>;
> > +		dallas,ds1374-mode = <2>
> > +		dallas,ds1374-remap-wdt-reset;
> > +	};
> > +};
> > diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt
> > index 3e0a34c..f7a50e5 100644
> > --- a/Documentation/devicetree/bindings/trivial-devices.txt
> > +++ b/Documentation/devicetree/bindings/trivial-devices.txt
> > @@ -29,7 +29,6 @@ cirrus,cs42l51		Cirrus Logic CS42L51 audio codec
> >  dallas,ds1307		64 x 8, Serial, I2C Real-Time Clock
> >  dallas,ds1338		I2C RTC with 56-Byte NV RAM
> >  dallas,ds1340		I2C RTC with Trickle Charger
> > -dallas,ds1374		I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output
> >  dallas,ds1631		High-Precision Digital Thermometer
> >  dallas,ds1682		Total-Elapsed-Time Recorder with Alarm
> >  dallas,ds1775		Tiny Digital Thermometer and Thermostat
> > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> > index 8d3b957..e6763fe 100644
> > --- a/drivers/rtc/Kconfig
> > +++ b/drivers/rtc/Kconfig
> > @@ -250,6 +250,8 @@ config RTC_DRV_DS1307_CENTURY
> >  
> >  config RTC_DRV_DS1374
> >  	tristate "Dallas/Maxim DS1374"
> > +	depends on MFD_DS1374
> > +	depends on REGMAP_I2C
> >  	help
> >  	  If you say yes here you get support for Dallas Semiconductor
> >  	  DS1374 real-time clock chips. If an interrupt is associated
> > -- 
> > 2.7.4
> > 

Thanks for the review,

Moritz

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding
  2017-05-16  2:34   ` Moritz Fischer
@ 2017-05-16  8:16     ` Alexandre Belloni
  0 siblings, 0 replies; 6+ messages in thread
From: Alexandre Belloni @ 2017-05-16  8:16 UTC (permalink / raw)
  To: Moritz Fischer
  Cc: Rob Herring, linux-watchdog, rtc-linux, linux-kernel, lee.jones,
	devicetree, linux, wim, a.zummo, mark.rutland

On 15/05/2017 at 19:34:17 -0700, Moritz Fischer wrote:
> On Fri, May 12, 2017 at 07:00:29PM -0500, Rob Herring wrote:
> > On Tue, May 09, 2017 at 11:20:20AM -0700, Moritz Fischer wrote:
> > > +			<0> for RTC
> > > +			<1> for RTC + Alarm (Interrupt)
> > > +			<2> for RTC + Watchdog
> > > +
> > > +Required child:
> > > +A single available child device of type matching the "dallas,ds1374-mode"
> > > +property.
> > > +
> > > +Optional properties (watchdog):
> > > +- dallas,ds1374-remap-wdt-reset: Boolean describing whether the INT pin
> > > +				 on the device is used as interrupt for
> > > +				 the alarm
> > 
> > Isn't presence of the interrupt property or not enough? It would be 
> > kind of useless to have no interrupt and also not do anything on 
> > timeout.
> 
> So you're saying:
> 
> If interrupt not present AND mode = <0> ignore interrupt
> 
> If interrupt present AND mode = <1> (maybe define here would be nicer),
> make it an interrupt.
> 

If mode == 1, WDSTR doesn't matter so it will always be on the INT pin
anyway.

> If interrupt not present AND mode = <2> then this implies that reset
> output is mapped to RST pin.
> 
> If Interrupt present AND mode = <2> then this implies that the reset
> output is (re)mapped to INT pin?
> 

Yes, I think this is sufficient.



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

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-05-16  8:16 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-09 18:20 [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding Moritz Fischer
2017-05-09 18:20 ` [RFC/PATCH 2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device Moritz Fischer
2017-05-09 23:48   ` Moritz Fischer
2017-05-13  0:00 ` [RFC/PATCH 1/2] dt-binding: mfd: Add Maxim/Dallas DS1374 MFD device binding Rob Herring
2017-05-16  2:34   ` Moritz Fischer
2017-05-16  8:16     ` 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).