diff mbox

[2/2] mfd: ds1374: Add Dallas/Maxim DS1374 Multi Function Device

Message ID 1499975665-14581-2-git-send-email-mdf@kernel.org
State Changes Requested
Headers show

Commit Message

Moritz Fischer July 13, 2017, 7:54 p.m. UTC
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>
---
 drivers/mfd/Kconfig              |  10 +
 drivers/mfd/Makefile             |   1 +
 drivers/mfd/ds1374.c             | 260 ++++++++++++++++
 drivers/rtc/rtc-ds1374.c         | 639 ++++++++++-----------------------------
 drivers/watchdog/Kconfig         |  10 +
 drivers/watchdog/Makefile        |   1 +
 drivers/watchdog/ds1374-wdt.c    | 208 +++++++++++++
 include/dt-bindings/mfd/ds1374.h |  17 ++
 include/linux/mfd/ds1374.h       |  59 ++++
 9 files changed, 722 insertions(+), 483 deletions(-)
 create mode 100644 drivers/mfd/ds1374.c
 create mode 100644 drivers/watchdog/ds1374-wdt.c
 create mode 100644 include/dt-bindings/mfd/ds1374.h
 create mode 100644 include/linux/mfd/ds1374.h

Comments

Guenter Roeck July 14, 2017, 3:57 a.m. UTC | #1
On 07/13/2017 12:54 PM, 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>

[ Only reviewing watchdog part ]

> ---
>   drivers/mfd/Kconfig              |  10 +
>   drivers/mfd/Makefile             |   1 +
>   drivers/mfd/ds1374.c             | 260 ++++++++++++++++
>   drivers/rtc/rtc-ds1374.c         | 639 ++++++++++-----------------------------
>   drivers/watchdog/Kconfig         |  10 +
>   drivers/watchdog/Makefile        |   1 +
>   drivers/watchdog/ds1374-wdt.c    | 208 +++++++++++++
>   include/dt-bindings/mfd/ds1374.h |  17 ++
>   include/linux/mfd/ds1374.h       |  59 ++++
>   9 files changed, 722 insertions(+), 483 deletions(-)
>   create mode 100644 drivers/mfd/ds1374.c
>   create mode 100644 drivers/watchdog/ds1374-wdt.c
>   create mode 100644 include/dt-bindings/mfd/ds1374.h
>   create mode 100644 include/linux/mfd/ds1374.h
> 

If possible, it might be better to split the patch into multiple parts, one per subsystem.

> 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..a0cfa1b
> --- /dev/null
> +++ b/drivers/mfd/ds1374.c
> @@ -0,0 +1,260 @@
> +/*
> + * 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
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
> +
> +#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];
> +

I think those functions should go away; the calling code can use standard
endianness conversion functions and call regmap_bulk_{read,write} directly.

> +	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,mode", &mode);
> +		if (err < 0) {
> +			dev_err(&client->dev, "missing dallas,mode property\n");
> +			return -EINVAL;
> +		}
> +
> +		ds1374->remapped_reset
> +			= of_property_read_bool(client->dev.of_node,
> +						"dallas,remap-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 52429f0..29ce1a9 100644
> --- a/drivers/rtc/rtc-ds1374.c
> +++ b/drivers/rtc/rtc-ds1374.c
> @@ -1,75 +1,38 @@
>   /*
> - * 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
>    *
> + * SPDX-License-Identifier: GPL-2.0
> + *
>    * 2005 (c) MontaVista Software, Inc. This file is licensed under
>    * the terms of the GNU General Public License version 2. This program
>    * 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);
> +#include <linux/regmap.h>
> +#include <linux/mfd/ds1374.h>
> +#include <linux/platform_device.h>
>   
> -#ifdef CONFIG_OF
> -static const struct of_device_id ds1374_of_match[] = {
> -	{ .compatible = "dallas,ds1374" },
> -	{ }
> -};
> -MODULE_DEVICE_TABLE(of, ds1374_of_match);
> -#endif
> -
> -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 +43,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 +89,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 +138,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,468 +173,219 @@ 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;
> -
> -		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:
> -		return put_user(wdt_margin, (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();
> -		}
> -
> -		if (options & WDIOS_ENABLECARD) {
> -			pr_info("enable watchdog\n");
> -			ds1374_wdt_settimeout(wdt_margin);
> -			ds1374_wdt_ping();
> -		}
> -
> -		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;
>   }
>   
>   #ifdef CONFIG_PM_SLEEP
> -static int ds1374_suspend(struct device *dev)
> +static int ds1374_rtc_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)
> +static int ds1374_rtc_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",
> -		.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 52a70ee..1703611 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

depends on I2C
select REGMAP_I2C

but doesn't the mfd driver already depend on that ?

> +	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 a2126e2..5b3b053 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..d221560
> --- /dev/null
> +++ b/drivers/watchdog/ds1374-wdt.c
> @@ -0,0 +1,208 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Dallas/Maxim DS1374 Watchdog Driver, heavily based on the older
> + * drivers/rtc/rtc-ds1374.c implementation
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + *
> + */
> +
> +#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>
> +
alphabetic order, please.

> +#define DS1374_WDT_RATE 4096 /* Hz */
> +#define DS1374_WDT_MIN_TIMEOUT 1 /* seconds */
> +#define DS1374_WDT_DEFAULT_TIMEOUT 30 /* seconds */
> +
Please use tabs

> +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);

Why not just regmap_bulk_read() ?

> +	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;

I personally prefer to split initialization from declaration if the initialization
requires continuation lines.

> +	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);

Why not just regmap_bulk_write() ?

The mixed use of mfd driver functions and regmap functions is confusing.
I think it would be better to avoid it.

> +	if (err) {
> +		dev_err(ds1374_wdt->dev, "couldn't set new watchdog time\n");

Is that noise necessary ? Same elsewhere. The error is returned to user space,
which should be sufficient.

> +		return err;
> +	}
> +
> +	ds1374_wdt->wdd.timeout = t;

         wdog->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));

Some unnecessary ( )

> +}
> +
> +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);
> +

This may bypass MAGICCLOSE: If the watchdog daemon is killed and the module removed,
the watchdog will be stopped. Is this really what you want ? If so, why set MAGICCLOSE
in the first place ?

> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int ds1374_wdt_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int ds1374_wdt_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +#endif
> +
Those functions are quite pointless.

> +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/dt-bindings/mfd/ds1374.h b/include/dt-bindings/mfd/ds1374.h
> new file mode 100644
> index 0000000..b33cd5e
> --- /dev/null
> +++ b/include/dt-bindings/mfd/ds1374.h
> @@ -0,0 +1,17 @@
> +/*
> + * This header provides macros for Maxim/Dallas DS1374 DT bindings
> + *
> + * Copyright (C) 2017 National Instruments Corp
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + *
> + */
> +
> +#ifndef __DT_BINDINGS_MFD_DS1374_H__
> +#define __DT_BINDINGS_MFD_DS1374_H__
> +
> +#define DALLAS_MODE_RTC		0
> +#define DALLAS_MODE_ALM		1
> +#define DALLAS_MODE_WDT		2
> +
> +#endif /* __DT_BINDINGS_MFD_DS1374_H__ */
> 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 */
>
Moritz Fischer July 14, 2017, 4:54 p.m. UTC | #2
Hi Guenter,

On Thu, Jul 13, 2017 at 08:57:52PM -0700, Guenter Roeck wrote:
> On 07/13/2017 12:54 PM, 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>
> 
> [ Only reviewing watchdog part ]
> 
> > ---
> >   drivers/mfd/Kconfig              |  10 +
> >   drivers/mfd/Makefile             |   1 +
> >   drivers/mfd/ds1374.c             | 260 ++++++++++++++++
> >   drivers/rtc/rtc-ds1374.c         | 639 ++++++++++-----------------------------
> >   drivers/watchdog/Kconfig         |  10 +
> >   drivers/watchdog/Makefile        |   1 +
> >   drivers/watchdog/ds1374-wdt.c    | 208 +++++++++++++
> >   include/dt-bindings/mfd/ds1374.h |  17 ++
> >   include/linux/mfd/ds1374.h       |  59 ++++
> >   9 files changed, 722 insertions(+), 483 deletions(-)
> >   create mode 100644 drivers/mfd/ds1374.c
> >   create mode 100644 drivers/watchdog/ds1374-wdt.c
> >   create mode 100644 include/dt-bindings/mfd/ds1374.h
> >   create mode 100644 include/linux/mfd/ds1374.h
> > 
> 
> If possible, it might be better to split the patch into multiple parts, one per subsystem.

I'll have to think about that some more ;-)
> 
> > 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..a0cfa1b
> > --- /dev/null
> > +++ b/drivers/mfd/ds1374.c
> > @@ -0,0 +1,260 @@
> > +/*
> > + * 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
> > + *
> > + * SPDX-License-Identifier: GPL-2.0
> > + */
> > +
> > +#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];
> > +
> 
> I think those functions should go away; the calling code can use standard
> endianness conversion functions and call regmap_bulk_{read,write} directly.

Sounds good. I just ported over the code that had been there, but
cleaning up is always a good idea :)

> 
> > +	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,mode", &mode);
> > +		if (err < 0) {
> > +			dev_err(&client->dev, "missing dallas,mode property\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		ds1374->remapped_reset
> > +			= of_property_read_bool(client->dev.of_node,
> > +						"dallas,remap-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 52429f0..29ce1a9 100644
> > --- a/drivers/rtc/rtc-ds1374.c
> > +++ b/drivers/rtc/rtc-ds1374.c
> > @@ -1,75 +1,38 @@
> >   /*
> > - * 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
> >    *
> > + * SPDX-License-Identifier: GPL-2.0
> > + *
> >    * 2005 (c) MontaVista Software, Inc. This file is licensed under
> >    * the terms of the GNU General Public License version 2. This program
> >    * 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);
> > +#include <linux/regmap.h>
> > +#include <linux/mfd/ds1374.h>
> > +#include <linux/platform_device.h>
> > -#ifdef CONFIG_OF
> > -static const struct of_device_id ds1374_of_match[] = {
> > -	{ .compatible = "dallas,ds1374" },
> > -	{ }
> > -};
> > -MODULE_DEVICE_TABLE(of, ds1374_of_match);
> > -#endif
> > -
> > -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 +43,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 +89,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 +138,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,468 +173,219 @@ 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;
> > -
> > -		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:
> > -		return put_user(wdt_margin, (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();
> > -		}
> > -
> > -		if (options & WDIOS_ENABLECARD) {
> > -			pr_info("enable watchdog\n");
> > -			ds1374_wdt_settimeout(wdt_margin);
> > -			ds1374_wdt_ping();
> > -		}
> > -
> > -		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;
> >   }
> >   #ifdef CONFIG_PM_SLEEP
> > -static int ds1374_suspend(struct device *dev)
> > +static int ds1374_rtc_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)
> > +static int ds1374_rtc_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",
> > -		.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 52a70ee..1703611 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
> 
> depends on I2C
> select REGMAP_I2C
> 
> but doesn't the mfd driver already depend on that ?

Yeah, will fix that.
> 
> > +	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 a2126e2..5b3b053 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..d221560
> > --- /dev/null
> > +++ b/drivers/watchdog/ds1374-wdt.c
> > @@ -0,0 +1,208 @@
> > +/*
> > + * Copyright (c) 2017, National Instruments Corp.
> > + *
> > + * Dallas/Maxim DS1374 Watchdog Driver, heavily based on the older
> > + * drivers/rtc/rtc-ds1374.c implementation
> > + *
> > + * SPDX-License-Identifier: GPL-2.0
> > + *
> > + */
> > +
> > +#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>
> > +
> alphabetic order, please.

Ok.
> 
> > +#define DS1374_WDT_RATE 4096 /* Hz */
> > +#define DS1374_WDT_MIN_TIMEOUT 1 /* seconds */
> > +#define DS1374_WDT_DEFAULT_TIMEOUT 30 /* seconds */
> > +
> Please use tabs
> 

Will do.
> > +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);
> 
> Why not just regmap_bulk_read() ?

Good catch.
> 
> > +	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;
> 
> I personally prefer to split initialization from declaration if the initialization
> requires continuation lines.

Yeah, it's kludgy. Will redo it.
> 
> > +	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);
> 
> Why not just regmap_bulk_write() ?

Agreed.
> 
> The mixed use of mfd driver functions and regmap functions is confusing.
> I think it would be better to avoid it.
> 
> > +	if (err) {
> > +		dev_err(ds1374_wdt->dev, "couldn't set new watchdog time\n");
> 
> Is that noise necessary ? Same elsewhere. The error is returned to user space,
> which should be sufficient.

Ok, will get rid of it.
> 
> > +		return err;
> > +	}
> > +
> > +	ds1374_wdt->wdd.timeout = t;
> 
>         wdog->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));
> 
> Some unnecessary ( )

Agreed.
> 
> > +}
> > +
> > +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);

I assume you'd want to get rid of that one too?
> > +		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);
> > +
> 
> This may bypass MAGICCLOSE: If the watchdog daemon is killed and the module removed,
> the watchdog will be stopped. Is this really what you want ? If so, why set MAGICCLOSE
> in the first place ?

So your suggestion would be:

- if (!nowayout)
-	ds1374_wdt_stop(&priv->wdd)

> 
> > +	return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int ds1374_wdt_suspend(struct device *dev)
> > +{
> > +	return 0;
> > +}
> > +
> > +static int ds1374_wdt_resume(struct device *dev)
> > +{
> > +	return 0;
> > +}
> > +#endif
> > +
> Those functions are quite pointless.

Agreed.

> 
> > +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/dt-bindings/mfd/ds1374.h b/include/dt-bindings/mfd/ds1374.h
> > new file mode 100644
> > index 0000000..b33cd5e
> > --- /dev/null
> > +++ b/include/dt-bindings/mfd/ds1374.h
> > @@ -0,0 +1,17 @@
> > +/*
> > + * This header provides macros for Maxim/Dallas DS1374 DT bindings
> > + *
> > + * Copyright (C) 2017 National Instruments Corp
> > + *
> > + * SPDX-License-Identifier: GPL-2.0
> > + *
> > + */
> > +
> > +#ifndef __DT_BINDINGS_MFD_DS1374_H__
> > +#define __DT_BINDINGS_MFD_DS1374_H__
> > +
> > +#define DALLAS_MODE_RTC		0
> > +#define DALLAS_MODE_ALM		1
> > +#define DALLAS_MODE_WDT		2
> > +
> > +#endif /* __DT_BINDINGS_MFD_DS1374_H__ */
> > 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 */
> > 
> 

Thanks for your review!

Moritz

PS: I haven't forgotten about the cros-ec-hwmon, I'll get back to that at
one point ;-)
Guenter Roeck July 14, 2017, 7:27 p.m. UTC | #3
On Fri, Jul 14, 2017 at 09:54:23AM -0700, Moritz Fischer wrote:
> Hi Guenter,
> 
> On Thu, Jul 13, 2017 at 08:57:52PM -0700, Guenter Roeck wrote:
> > On 07/13/2017 12:54 PM, 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>
> > 
> > [ Only reviewing watchdog part ]
> > 
[ ... ]

> > > +}
> > > +
> > > +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);
> 
> I assume you'd want to get rid of that one too?

Yes.

> > 
> > This may bypass MAGICCLOSE: If the watchdog daemon is killed and the module removed,
> > the watchdog will be stopped. Is this really what you want ? If so, why set MAGICCLOSE
> > in the first place ?
> 
> So your suggestion would be:
> 
> - if (!nowayout)
> -	ds1374_wdt_stop(&priv->wdd)
> 
Correct. Just drop the remove function.

Thanks,
Guenter
Lee Jones July 17, 2017, 7:51 a.m. UTC | #4
On Thu, 13 Jul 2017, 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>
> ---
>  drivers/mfd/Kconfig              |  10 +
>  drivers/mfd/Makefile             |   1 +
>  drivers/mfd/ds1374.c             | 260 ++++++++++++++++
>  drivers/rtc/rtc-ds1374.c         | 639 ++++++++++-----------------------------

It looks like this should now depend on MFD_DS1374, right?

>  drivers/watchdog/Kconfig         |  10 +
>  drivers/watchdog/Makefile        |   1 +
>  drivers/watchdog/ds1374-wdt.c    | 208 +++++++++++++

The RTC and Watchdog drivers need to be split out of this patch and
placed into their own.  Then we can take them through their respective
subsystem trees and do not have to rely on immutable branches for
unification.

>  include/dt-bindings/mfd/ds1374.h |  17 ++
>  include/linux/mfd/ds1374.h       |  59 ++++
>  9 files changed, 722 insertions(+), 483 deletions(-)
>  create mode 100644 drivers/mfd/ds1374.c
>  create mode 100644 drivers/watchdog/ds1374-wdt.c
>  create mode 100644 include/dt-bindings/mfd/ds1374.h
>  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 is an old style of help.  Please remove the '-'s.

> +	  This driver supports the Dallas Maxim DS1374 multi function chip.

"Multi-Functional"

> +	  The chip combines an RTC, trickle charger, Watchdog or Alarm.

Why is "trickle charger" not capitalised?

> +
>  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..a0cfa1b
> --- /dev/null
> +++ b/drivers/mfd/ds1374.c
> @@ -0,0 +1,260 @@
> +/*
> + * Copyright (c) 2017, National Instruments Corp.
> + *
> + * Dallas/Maxim DS1374 Multi Function Device Driver

"Functional"

> + * The trickle charger code was taken more ore less 1:1 from

"or"

> + * drivers/rtc/rtc-1390.c

Why does this need to be documented in this file?

If the Trickle Charger code is in here (I haven't been down that far
yet) you need to move it out into a more appropriate subsystem.

> + * SPDX-License-Identifier: GPL-2.0
> + */
> +
> +#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>

Alphabetical.

> +#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

Are these tabs or spaces, or a mixture?

Did you run checkpatch.pl?

> +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 = {

Genuine question: Can this be const?

> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = DS1374_REG_TCR,
> +	.volatile_table	= &ds1374_volatile_table,
> +	.cache_type	= REGCACHE_RBTREE,

It might just be the patch format, but can you check if this is
tabs/spaces?  If you're using tabs, please ensure they are all
aligned.

> +};
> +
> +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);
> +}

This function appears to serve no purpose.  Why don't you just use
mfd_add_devices() instead?

> +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];

You need at least a comment to explain what's happening here.

> +	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;
> +	}

Same here.

> +	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,mode", &mode);
> +		if (err < 0) {
> +			dev_err(&client->dev, "missing dallas,mode property\n");
> +			return -EINVAL;
> +		}
> +
> +		ds1374->remapped_reset
> +			= of_property_read_bool(client->dev.of_node,
> +						"dallas,remap-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;
> +	}

This is non-standard.  So if OF is enabled, you're taking the 'mode'
from platform data (DT) and if it's not, you're relying on Kconfig.
May I suggest that you pick platform data OR Kconfig, but not mix the
two.

> +	ds1374->client = client;
> +	ds1374->irq = client->irq;
> +	i2c_set_clientdata(client, ds1374);
> +
> +	/* check if we're supposed to trickle charge */

"Check".

> +	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 */

"We", "an RTC".

Or

"The RTC is always available."

> +	err = ds1374_add_device(ds1374, &ds1374_rtc_cell);
> +	if (err)
> +		return err;
> +
> +	/* we might have a watchdog if configured that way */

"We"

> +	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

These seem pointless.

I'm sure there will be a nice MACRO you can use instead.

> +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");

This conflicts with your header.

[...]

> diff --git a/include/dt-bindings/mfd/ds1374.h b/include/dt-bindings/mfd/ds1374.h
> new file mode 100644
> index 0000000..b33cd5e
> --- /dev/null
> +++ b/include/dt-bindings/mfd/ds1374.h
> @@ -0,0 +1,17 @@
> +/*
> + * This header provides macros for Maxim/Dallas DS1374 DT bindings
> + *
> + * Copyright (C) 2017 National Instruments Corp
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + *

This line is superfluous.

> + */
> +
> +#ifndef __DT_BINDINGS_MFD_DS1374_H__
> +#define __DT_BINDINGS_MFD_DS1374_H__
> +
> +#define DALLAS_MODE_RTC		0
> +#define DALLAS_MODE_ALM		1
> +#define DALLAS_MODE_WDT		2

What's stopping these becoming out of line with the #defines in the
other header file?  You should probably use these for comparison
instead.

> +#endif /* __DT_BINDINGS_MFD_DS1374_H__ */
> 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

"Functional"

> + * 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.

Do you have to use the long licence here?

> + */
> +
> +#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,
> +};

Please see above.

> +/* 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;
> +};

This could do with a KernelDoc header.

> +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);

Do these two just read time?

If so, the nomenclature could be improved.

> +#endif /* MFD_DS1374_H */
Moritz Fischer July 17, 2017, 6:14 p.m. UTC | #5
Hi Lee,

On Mon, Jul 17, 2017 at 08:51:17AM +0100, Lee Jones wrote:
> On Thu, 13 Jul 2017, 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>
> > ---
> >  drivers/mfd/Kconfig              |  10 +
> >  drivers/mfd/Makefile             |   1 +
> >  drivers/mfd/ds1374.c             | 260 ++++++++++++++++
> >  drivers/rtc/rtc-ds1374.c         | 639 ++++++++++-----------------------------
> 
> It looks like this should now depend on MFD_DS1374, right?
> 
> >  drivers/watchdog/Kconfig         |  10 +
> >  drivers/watchdog/Makefile        |   1 +
> >  drivers/watchdog/ds1374-wdt.c    | 208 +++++++++++++
> 
> The RTC and Watchdog drivers need to be split out of this patch and
> placed into their own.  Then we can take them through their respective
> subsystem trees and do not have to rely on immutable branches for
> unification.

Ok, I haven't found a good way to keep this bisectable in that case. I'm
open to suggestions. If the consensus ends up being to split it up, I'm
more than happy to separate it out into multiple patches.

> 
> >  include/dt-bindings/mfd/ds1374.h |  17 ++
> >  include/linux/mfd/ds1374.h       |  59 ++++
> >  9 files changed, 722 insertions(+), 483 deletions(-)
> >  create mode 100644 drivers/mfd/ds1374.c
> >  create mode 100644 drivers/watchdog/ds1374-wdt.c
> >  create mode 100644 include/dt-bindings/mfd/ds1374.h
> >  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 is an old style of help.  Please remove the '-'s.
Will do.

> 
> > +	  This driver supports the Dallas Maxim DS1374 multi function chip.
> 
> "Multi-Functional"
Ok.
> 
> > +	  The chip combines an RTC, trickle charger, Watchdog or Alarm.
> 
> Why is "trickle charger" not capitalised?

No particular reason. Will fix.
> 
> > +
> >  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..a0cfa1b
> > --- /dev/null
> > +++ b/drivers/mfd/ds1374.c
> > @@ -0,0 +1,260 @@
> > +/*
> > + * Copyright (c) 2017, National Instruments Corp.
> > + *
> > + * Dallas/Maxim DS1374 Multi Function Device Driver
> 
> "Functional"
> 
> > + * The trickle charger code was taken more ore less 1:1 from
> 
> "or"
> 
> > + * drivers/rtc/rtc-1390.c
> 
> Why does this need to be documented in this file?
> 
> If the Trickle Charger code is in here (I haven't been down that far
> yet) you need to move it out into a more appropriate subsystem.

Any suggestions? Most of the RTC drivers keep the trickle charge code in
RTC drivers. I can either do that, or see if somwhere under
drivers/power/supply makes sense.

> 
> > + * SPDX-License-Identifier: GPL-2.0
> > + */
> > +
> > +#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>
> 
> Alphabetical.

Ok, will do.
> 
> > +#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
> 
> Are these tabs or spaces, or a mixture?

It's a mix, I'll fix that.
> 
> Did you run checkpatch.pl?

I did.
> 
> > +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 = {
> 
> Genuine question: Can this be const?

Yes.
> 
> > +	.reg_bits = 8,
> > +	.val_bits = 8,
> > +	.max_register = DS1374_REG_TCR,
> > +	.volatile_table	= &ds1374_volatile_table,
> > +	.cache_type	= REGCACHE_RBTREE,
> 
> It might just be the patch format, but can you check if this is
> tabs/spaces?  If you're using tabs, please ensure they are all
> aligned.

Will fix.
> 
> > +};
> > +
> > +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);
> > +}
> 
> This function appears to serve no purpose.  Why don't you just use
> mfd_add_devices() instead?

Yeah, can do.
> 
> > +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];
> 
> You need at least a comment to explain what's happening here.

It's an endian conversion. Can probably be replaced with normal
endian conversion functions as Guenter suggested.

I'll try to get rid of the function and use regmap_bulk_read/write
> 
> > +	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;
> > +	}
> 
> Same here.
> 
> > +	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,mode", &mode);
> > +		if (err < 0) {
> > +			dev_err(&client->dev, "missing dallas,mode property\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		ds1374->remapped_reset
> > +			= of_property_read_bool(client->dev.of_node,
> > +						"dallas,remap-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;
> > +	}
> 
> This is non-standard.  So if OF is enabled, you're taking the 'mode'
> from platform data (DT) and if it's not, you're relying on Kconfig.
> May I suggest that you pick platform data OR Kconfig, but not mix the
> two.

Yeah was a (failed) attempt to not break people that relied on the
Kconfig option. But I'm fine with just making it OF based.
> 
> > +	ds1374->client = client;
> > +	ds1374->irq = client->irq;
> > +	i2c_set_clientdata(client, ds1374);
> > +
> > +	/* check if we're supposed to trickle charge */
> 
> "Check".
> 
> > +	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 */
> 
> "We", "an RTC".
> 
> Or
> 
> "The RTC is always available."
> 
> > +	err = ds1374_add_device(ds1374, &ds1374_rtc_cell);
> > +	if (err)
> > +		return err;
> > +
> > +	/* we might have a watchdog if configured that way */
> 
> "We"
> 
> > +	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
> 
> These seem pointless.
> 
> I'm sure there will be a nice MACRO you can use instead.

Yeah, will fix.
> 
> > +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");
> 
> This conflicts with your header.
> 
> [...]
> 
> > diff --git a/include/dt-bindings/mfd/ds1374.h b/include/dt-bindings/mfd/ds1374.h
> > new file mode 100644
> > index 0000000..b33cd5e
> > --- /dev/null
> > +++ b/include/dt-bindings/mfd/ds1374.h
> > @@ -0,0 +1,17 @@
> > +/*
> > + * This header provides macros for Maxim/Dallas DS1374 DT bindings
> > + *
> > + * Copyright (C) 2017 National Instruments Corp
> > + *
> > + * SPDX-License-Identifier: GPL-2.0
> > + *
> 
> This line is superfluous.
> 
> > + */
> > +
> > +#ifndef __DT_BINDINGS_MFD_DS1374_H__
> > +#define __DT_BINDINGS_MFD_DS1374_H__
> > +
> > +#define DALLAS_MODE_RTC		0
> > +#define DALLAS_MODE_ALM		1
> > +#define DALLAS_MODE_WDT		2
> 
> What's stopping these becoming out of line with the #defines in the
> other header file?  You should probably use these for comparison
> instead.
> 
> > +#endif /* __DT_BINDINGS_MFD_DS1374_H__ */
> > 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
> 
> "Functional"
> 
> > + * 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.
> 
> Do you have to use the long licence here?
Nope, will replace with SPDX.
> 
> > + */
> > +
> > +#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,
> > +};
> 
> Please see above.

Yeah, will get rid of them.
> 
> > +/* 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;
> > +};
> 
> This could do with a KernelDoc header.
> 
> > +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);
> 
> Do these two just read time?
> 
> If so, the nomenclature could be improved.

The can most likely be replaced completely by regmap_bulk_read()/write()
> 
> > +#endif /* MFD_DS1374_H */
> 
> -- 
> Lee Jones
> Linaro STMicroelectronics Landing Team Lead
> Linaro.org │ Open source software for ARM SoCs
> Follow Linaro: Facebook | Twitter | Blog

Thanks for your feedback,

Moritz
Lee Jones July 18, 2017, 9:22 a.m. UTC | #6
On Mon, 17 Jul 2017, Moritz Fischer wrote:

> Hi Lee,
> 
> On Mon, Jul 17, 2017 at 08:51:17AM +0100, Lee Jones wrote:
> > On Thu, 13 Jul 2017, 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>
> > > ---
> > >  drivers/mfd/Kconfig              |  10 +
> > >  drivers/mfd/Makefile             |   1 +
> > >  drivers/mfd/ds1374.c             | 260 ++++++++++++++++
> > >  drivers/rtc/rtc-ds1374.c         | 639 ++++++++++-----------------------------
> > 
> > It looks like this should now depend on MFD_DS1374, right?
> > 
> > >  drivers/watchdog/Kconfig         |  10 +
> > >  drivers/watchdog/Makefile        |   1 +
> > >  drivers/watchdog/ds1374-wdt.c    | 208 +++++++++++++
> > 
> > The RTC and Watchdog drivers need to be split out of this patch and
> > placed into their own.  Then we can take them through their respective
> > subsystem trees and do not have to rely on immutable branches for
> > unification.
> 
> Ok, I haven't found a good way to keep this bisectable in that case. I'm
> open to suggestions. If the consensus ends up being to split it up, I'm
> more than happy to separate it out into multiple patches.

If it's genuinely not bisectable, then leave it as is.
Alexandre Belloni July 29, 2017, 11:07 a.m. UTC | #7
On 17/07/2017 at 08:51:17 +0100, Lee Jones wrote:
> > +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,mode", &mode);
> > +		if (err < 0) {
> > +			dev_err(&client->dev, "missing dallas,mode property\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		ds1374->remapped_reset
> > +			= of_property_read_bool(client->dev.of_node,
> > +						"dallas,remap-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;
> > +	}
> 
> This is non-standard.  So if OF is enabled, you're taking the 'mode'
> from platform data (DT) and if it's not, you're relying on Kconfig.
> May I suggest that you pick platform data OR Kconfig, but not mix the
> two.
> 

I've explicitly asked for that. CONFIG_RTC_DRV_DS1374_WDT has been in
the kernel since 2014. Requiring the use of dallas,mode now would break
the functionality for people not updating their DT.

However, my suggestion was that if OF is enabled and dallas,mode is not
present, then rely on CONFIG_RTC_DRV_DS1374_WDT to set the mode.

I personally don't care too much about breaking the DT ABI but this one
can be a bit tricky to discover and I still think it can be avoided.
At least, we should silently ignore CONFIG_RTC_DRV_DS1374_WDT.
Alexandre Belloni July 29, 2017, 10:26 p.m. UTC | #8
On 13/07/2017 at 12:54:25 -0700, Moritz Fischer wrote:
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 52a70ee..1703611 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"

If you want to keep the backward compatibility, this probably needs to
be defaulting to RTC_DRV_DS1374 when RTC_DRV_DS1374_WDT is selected.

Also, maybe we need to make RTC_DRV_DS1374_WDT not user selectable.

If you don't think backward compatibility can be achieved, then we can
drop everything.

Else, the RTC part seems fine to me but the driver may be cleaned up a
bit further afterwards.

> +	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 mbox

Patch

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..a0cfa1b
--- /dev/null
+++ b/drivers/mfd/ds1374.c
@@ -0,0 +1,260 @@ 
+/*
+ * 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
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#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,mode", &mode);
+		if (err < 0) {
+			dev_err(&client->dev, "missing dallas,mode property\n");
+			return -EINVAL;
+		}
+
+		ds1374->remapped_reset
+			= of_property_read_bool(client->dev.of_node,
+						"dallas,remap-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 52429f0..29ce1a9 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -1,75 +1,38 @@ 
 /*
- * 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
  *
+ * SPDX-License-Identifier: GPL-2.0
+ *
  * 2005 (c) MontaVista Software, Inc. This file is licensed under
  * the terms of the GNU General Public License version 2. This program
  * 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);
+#include <linux/regmap.h>
+#include <linux/mfd/ds1374.h>
+#include <linux/platform_device.h>
 
-#ifdef CONFIG_OF
-static const struct of_device_id ds1374_of_match[] = {
-	{ .compatible = "dallas,ds1374" },
-	{ }
-};
-MODULE_DEVICE_TABLE(of, ds1374_of_match);
-#endif
-
-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 +43,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 +89,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 +138,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,468 +173,219 @@  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;
-
-		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:
-		return put_user(wdt_margin, (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();
-		}
-
-		if (options & WDIOS_ENABLECARD) {
-			pr_info("enable watchdog\n");
-			ds1374_wdt_settimeout(wdt_margin);
-			ds1374_wdt_ping();
-		}
-
-		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;
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int ds1374_suspend(struct device *dev)
+static int ds1374_rtc_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)
+static int ds1374_rtc_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",
-		.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 52a70ee..1703611 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 a2126e2..5b3b053 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..d221560
--- /dev/null
+++ b/drivers/watchdog/ds1374-wdt.c
@@ -0,0 +1,208 @@ 
+/*
+ * Copyright (c) 2017, National Instruments Corp.
+ *
+ * Dallas/Maxim DS1374 Watchdog Driver, heavily based on the older
+ * drivers/rtc/rtc-ds1374.c implementation
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ */
+
+#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_wdt_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int ds1374_wdt_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/dt-bindings/mfd/ds1374.h b/include/dt-bindings/mfd/ds1374.h
new file mode 100644
index 0000000..b33cd5e
--- /dev/null
+++ b/include/dt-bindings/mfd/ds1374.h
@@ -0,0 +1,17 @@ 
+/*
+ * This header provides macros for Maxim/Dallas DS1374 DT bindings
+ *
+ * Copyright (C) 2017 National Instruments Corp
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ */
+
+#ifndef __DT_BINDINGS_MFD_DS1374_H__
+#define __DT_BINDINGS_MFD_DS1374_H__
+
+#define DALLAS_MODE_RTC		0
+#define DALLAS_MODE_ALM		1
+#define DALLAS_MODE_WDT		2
+
+#endif /* __DT_BINDINGS_MFD_DS1374_H__ */
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 */