aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMoritz Fischer <mdf@kernel.org>2017-04-24 10:32:31 -0700
committerMoritz Fischer <mdf@kernel.org>2017-04-25 14:28:38 -0700
commit1cd5585580932f332c61cc3a821f4107642992d8 (patch)
treed4e2440f389618977534b5f00a9e797369fdb1cf
parent1e500aad440cdf1a0a1a84e31353560585807dfd (diff)
downloadlinux-wip/mfd-ds1374-rfc.tar.gz
rtc/wdt/mfd: ds1374: Split up DS1374 into a wdt and a rtc partwip/mfd-ds1374-rfc
This splits up the DS1374 into a watchdog and rtc part with separate drivers in drivers/rtc and drivers/watchdog respectively. Note: This can most likely be broken up better into individual commits. Signed-off-by: Moritz Fischer <mdf@kernel.org>
-rw-r--r--drivers/rtc/rtc-ds1374.c512
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/ds1374-wdt.c264
4 files changed, 389 insertions, 397 deletions
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index cac9d321cb1ae..3a7c8f7c171e4 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -1,9 +1,10 @@
/*
- * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
+ * RTC driver for the Maxim/Dallas DS1374 Real-Time Clock via MFD
*
* Based on code by Randy Vinson <rvinson@mvista.com>,
* which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>.
*
+ * Copyright (C) 2017 National Instruments Corp
* Copyright (C) 2014 Rose Technology
* Copyright (C) 2006-2007 Freescale Semiconductor
*
@@ -12,126 +13,24 @@
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
-/*
- * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
- * recommened in .../Documentation/i2c/writing-clients section
- * "Sending and receiving", using SMBus level communication is preferred.
- */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
-#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/regmap.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_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 */
-
-#define DS1374_TRICKLE_CHARGER_ENABLE 0xA0
-#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_NO_DIODE 0x04
-#define DS1374_TRICKLE_CHARGER_DIODE 0x08
-
-#define WDT_MIN_TIMEOUT 1 /* seconds */
-#define WDT_DEFAULT_TIMEOUT 30 /* seconds */
-
-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 const struct regmap_range write_ranges[] = {
- regmap_reg_range(DS1374_REG_TOD0, DS1374_REG_TCR),
-};
-
-static const struct regmap_access_table ds1374_write_table = {
- .yes_ranges = write_ranges,
- .n_yes_ranges = ARRAY_SIZE(write_ranges),
-};
-
-static const struct regmap_range read_ranges[] = {
- regmap_reg_range(DS1374_REG_TOD0, DS1374_REG_TCR),
-};
-
-static const struct regmap_access_table ds1374_read_table = {
- .yes_ranges = read_ranges,
- .n_yes_ranges = ARRAY_SIZE(read_ranges),
-};
+#include <linux/mfd/ds1374.h>
+#include <linux/platform_device.h>
-static struct regmap_config ds1374_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = DS1374_REG_TCR,
- .wr_table = &ds1374_write_table,
- .rd_table = &ds1374_read_table,
- .volatile_table = &ds1374_volatile_table,
- .cache_type = REGCACHE_RBTREE,
-};
-
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
-static bool nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout,
- "Watchdog cannot be stopped once started (default="
- __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-
-static unsigned int timeout;
-module_param(timeout, int, 0);
-MODULE_PARM_DESC(timeout, "Watchdog timeout");
-#endif
-
-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
-
-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
@@ -140,22 +39,11 @@ struct ds1374 {
*/
struct mutex mutex;
int exiting;
-
- struct regmap *regmap;
-
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
- struct watchdog_device wdd;
- u32 rate;
- bool remapped_reset;
-#endif
};
-static struct i2c_driver ds1374_driver;
-
-static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
+static int ds1374_read_rtc(struct ds1374_rtc *ds1374, u32 *time,
int reg, int nbytes)
{
- struct ds1374 *ds1374 = i2c_get_clientdata(client);
u8 buf[4];
int ret;
int i;
@@ -163,9 +51,9 @@ static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
if (WARN_ON(nbytes > 4))
return -EINVAL;
- ret = regmap_bulk_read(ds1374->regmap, reg, buf, nbytes);
+ ret = regmap_bulk_read(ds1374->chip->regmap, reg, buf, nbytes);
if (ret) {
- dev_err(&client->dev, "Failed to bulkread n = %d at R%d\n",
+ dev_err(&ds1374->chip->client->dev, "Failed to bulkread n = %d at R%d\n",
nbytes, reg);
return ret;
}
@@ -176,10 +64,9 @@ static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
return 0;
}
-static int ds1374_write_rtc(struct i2c_client *client, u32 time,
+static int ds1374_write_rtc(struct ds1374_rtc *ds1374, u32 time,
int reg, int nbytes)
{
- struct ds1374 *ds1374 = i2c_get_clientdata(client);
u8 buf[4];
int i;
@@ -193,26 +80,26 @@ static int ds1374_write_rtc(struct i2c_client *client, u32 time,
time >>= 8;
}
- return regmap_bulk_write(ds1374->regmap, reg, buf, nbytes);
+ return regmap_bulk_write(ds1374->chip->regmap, reg, buf, nbytes);
}
-static int ds1374_check_rtc_status(struct ds1374 *ds1374)
+static int ds1374_check_rtc_status(struct ds1374_rtc *ds1374)
{
int ret = 0;
unsigned int control, stat;
- ret = regmap_read(ds1374->regmap, DS1374_REG_SR, &stat);
- if (ret < 0)
+ ret = regmap_read(ds1374->chip->regmap, DS1374_REG_SR, &stat);
+ if (ret)
return stat;
if (stat & DS1374_REG_SR_OSF)
- dev_warn(&ds1374->client->dev,
+ dev_warn(&ds1374->chip->client->dev,
"oscillator discontinuity flagged, time unreliable\n");
stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF);
- ret = regmap_write(ds1374->regmap, DS1374_REG_SR, stat);
- if (ret < 0)
+ ret = regmap_write(ds1374->chip->regmap, DS1374_REG_SR, stat);
+ if (ret)
return ret;
/* If the alarm is pending, clear it before requesting
@@ -220,21 +107,22 @@ static int ds1374_check_rtc_status(struct ds1374 *ds1374)
* before everything is initialized.
*/
- ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &control);
- if (ret < 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 regmap_write(ds1374->regmap, 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_rtc(ds1374_rtc, &itime, DS1374_REG_TOD0, 4);
if (!ret)
rtc_time_to_tm(itime, time);
@@ -243,11 +131,12 @@ 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_rtc(ds1374_rtc, itime, DS1374_REG_TOD0, 4);
}
#ifndef CONFIG_RTC_DRV_DS1374_WDT
@@ -257,16 +146,18 @@ static int ds1374_set_time(struct device *dev, struct rtc_time *time)
*/
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;
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);
ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
if (ret < 0)
@@ -276,11 +167,11 @@ static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
if (ret < 0)
goto out;
- ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
+ ret = ds1374_read_rtc(ds1374_rtc, &now, DS1374_REG_TOD0, 4);
if (ret)
goto out;
- ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
+ ret = ds1374_read_rtc(ds1374_rtc, &cur_alarm, DS1374_REG_WDALM0, 3);
if (ret)
goto out;
@@ -289,20 +180,22 @@ 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);
@@ -323,21 +216,22 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
else
new_alarm -= itime;
- mutex_lock(&ds1374->mutex);
+ mutex_lock(&ds1374_rtc->mutex);
ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
if (ret < 0)
goto out;
/* Disable any existing alarm before setting the new one
- * (or lack thereof). */
+ * (or lack thereof).
+ */
cr &= ~DS1374_REG_CR_WACE;
ret = regmap_write(ds1374->regmap, DS1374_REG_CR, cr);
if (ret < 0)
goto out;
- ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
+ ret = ds1374_write_rtc(ds1374_rtc, new_alarm, DS1374_REG_WDALM0, 3);
if (ret)
goto out;
@@ -349,66 +243,66 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
}
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;
+ struct ds1374_rtc *ds1374_rtc = container_of(work, struct ds1374_rtc,
+ work);
unsigned int stat, control;
int ret;
- mutex_lock(&ds1374->mutex);
+ mutex_lock(&ds1374_rtc->mutex);
- ret = regmap_read(ds1374->regmap, DS1374_REG_SR, &stat);
+ ret = regmap_read(ds1374_rtc->chip->regmap, DS1374_REG_SR, &stat);
if (ret < 0)
goto unlock;
if (stat & DS1374_REG_SR_AF) {
stat &= ~DS1374_REG_SR_AF;
- regmap_write(ds1374->regmap, DS1374_REG_SR, stat);
+ regmap_write(ds1374_rtc->chip->regmap, DS1374_REG_SR, stat);
- ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &control);
+ ret = regmap_read(ds1374_rtc->chip->regmap, DS1374_REG_CR,
+ &control);
if (ret < 0)
goto out;
control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
- regmap_write(ds1374->regmap, DS1374_REG_CR, control);
+ regmap_write(ds1374_rtc->chip->regmap, 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 = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
+ ret = regmap_read(ds1374->chip->regmap, DS1374_REG_CR, &cr);
if (ret < 0)
goto out;
@@ -418,7 +312,7 @@ static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
} else {
cr &= ~DS1374_REG_CR_WACE;
}
- ret = regmap_write(ds1374->regmap, DS1374_REG_CR, cr);
+ ret = regmap_write(ds1374->chip->regmap, DS1374_REG_CR, cr);
out:
mutex_unlock(&ds1374->mutex);
@@ -436,148 +330,6 @@ static const struct rtc_class_ops ds1374_rtc_ops = {
#endif
};
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
-static const struct watchdog_info ds1374_wdt_info = {
- .identity = "DS1374 WTD",
- .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
- WDIOF_MAGICCLOSE,
-};
-
-static int ds1374_wdt_stop(struct watchdog_device *wdog)
-{
- struct ds1374 *ds1374 = watchdog_get_drvdata(wdog);
- unsigned int cr;
- int ret;
-
- ret = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
- /* Disable watchdog timer */
- cr &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_WDSTR);
-
- return regmap_write(ds1374->regmap, DS1374_REG_CR, cr);
-}
-
-static int ds1374_wdt_set_timeout(struct watchdog_device *wdog,
- unsigned int t)
-{
- struct ds1374 *ds1374 = watchdog_get_drvdata(wdog);
- unsigned int timeout = ds1374->rate * t;
- int cr, err;
-
- err = regmap_read(ds1374->regmap, DS1374_REG_CR, &cr);
- if (err < 0)
- return 0;
-
- /* Disable any existing watchdog/alarm before setting the new one */
- cr &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
-
- err = regmap_write(ds1374->regmap, DS1374_REG_CR, cr);
- if (err < 0)
- return err;
-
- err = ds1374_write_rtc(ds1374->client, timeout, DS1374_REG_WDALM0, 3);
- if (err) {
- pr_err("couldn't set new watchdog time\n");
- return err;
- }
-
- /* Enable watchdog timer */
- cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM | DS1374_REG_CR_AIE;
-
- if (ds1374->remapped_reset)
- cr |= DS1374_REG_CR_WDSTR;
-
- err = regmap_write(ds1374->regmap, DS1374_REG_CR, cr);
- if (err < 0)
- return err;
-
- /* WHY? */
- ds1374->wdd.timeout = t;
-
- return err;
-}
-
-static int ds1374_wdt_ping(struct watchdog_device *wdog)
-{
- struct ds1374 *ds1374 = watchdog_get_drvdata(wdog);
- u32 val;
- int err;
-
- err = ds1374_read_rtc(ds1374->client, &val, DS1374_REG_WDALM0, 3);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int ds1374_wdt_start(struct watchdog_device *wdog)
-{
- int err;
-
- err = ds1374_wdt_set_timeout(wdog, wdog->timeout);
- if (err) {
- pr_err("%s: failed to set timeout (%d) %u\n", __func__, err,
- wdog->timeout);
- return err;
- }
-
- err = ds1374_wdt_ping(wdog);
- if (err) {
- pr_err("%s: failed to ping (%d)\n", __func__, err);
- return err;
- }
-
- return 0;
-}
-
-
-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,
-};
-
-#endif /*CONFIG_RTC_DRV_DS1374_WDT*/
-
-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);
-}
-
/*
*****************************************************************************
*
@@ -585,97 +337,62 @@ static int ds1374_trickle_of_init(struct ds1374 *ds1374)
*
*****************************************************************************
*/
-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->regmap = devm_regmap_init_i2c(client,
- &ds1374_regmap_config);
- if (IS_ERR(ds1374->regmap))
- return PTR_ERR(ds1374->regmap);
+ platform_set_drvdata(pdev, ds1374_rtc);
- ds1374->client = client;
- i2c_set_clientdata(client, ds1374);
+ INIT_WORK(&ds1374_rtc->work, ds1374_work);
+ mutex_init(&ds1374_rtc->mutex);
- INIT_WORK(&ds1374->work, ds1374_work);
- mutex_init(&ds1374->mutex);
-
- ret = ds1374_trickle_of_init(ds1374);
- if (ret)
- return ret;
-
- ret = ds1374_check_rtc_status(ds1374);
- 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 (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);
- }
-
- 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);
+ device_set_wakeup_capable(dev, 1);
}
-#ifdef CONFIG_RTC_DRV_DS1374_WDT
- ds1374->remapped_reset = true;
-
- ds1374->rate = 4096;
- ds1374->wdd.info = &ds1374_wdt_info;
- ds1374->wdd.ops = &ds1374_wdt_ops;
- ds1374->wdd.min_timeout = WDT_MIN_TIMEOUT;
- ds1374->wdd.timeout = WDT_DEFAULT_TIMEOUT;
- ds1374->wdd.max_timeout = 0x1ffffff / ds1374->rate;
- ds1374->wdd.parent = &client->dev;
-
- watchdog_init_timeout(&ds1374->wdd, timeout, &client->dev);
- watchdog_set_nowayout(&ds1374->wdd, nowayout);
- watchdog_stop_on_reboot(&ds1374->wdd);
- watchdog_set_drvdata(&ds1374->wdd, ds1374);
-
- ret = watchdog_register_device(&ds1374->wdd);
- if (ret) {
- dev_err(&client->dev, "Failed to register watchdog device\n");
- return ret;
+ ds1374_rtc->rtc = devm_rtc_device_register(dev, "ds1374-rtc",
+ &ds1374_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(ds1374_rtc->rtc)) {
+ dev_err(dev, "unable to register the class device\n");
+ return PTR_ERR(ds1374_rtc->rtc);
}
-
- dev_info(&client->dev, "Registered DS1374 Watchdog\n");
-#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
- if (!nowayout)
- ds1374_wdt_stop(&ds1374->wdd);
- watchdog_unregister_device(&ds1374->wdd);
-#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;
@@ -684,38 +401,39 @@ static int ds1374_remove(struct i2c_client *client)
#ifdef CONFIG_PM_SLEEP
static int ds1374_suspend(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
- if (client->irq > 0 && device_may_wakeup(&client->dev))
- enable_irq_wake(client->irq);
+ if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
+ enable_irq_wake(ds1374_rtc->chip->irq);
return 0;
}
static int ds1374_resume(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1374_rtc *ds1374_rtc = platform_get_drvdata(pdev);
- if (client->irq > 0 && device_may_wakeup(&client->dev))
- disable_irq_wake(client->irq);
+ if (ds1374_rtc->chip->irq > 0 && device_may_wakeup(&pdev->dev))
+ disable_irq_wake(ds1374_rtc->chip->irq);
return 0;
}
#endif
-static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
+static SIMPLE_DEV_PM_OPS(ds1374_rtc_pm, ds1374_rtc_suspend, ds1374_rtc_resume);
-static struct i2c_driver ds1374_driver = {
+static struct platform_driver ds1374_rtc_driver = {
.driver = {
- .name = "rtc-ds1374",
- .of_match_table = of_match_ptr(ds1374_of_match),
- .pm = &ds1374_pm,
+ .name = "ds1374-rtc",
+ .pm = &ds1374_rtc_pm,
},
- .probe = ds1374_probe,
- .remove = ds1374_remove,
- .id_table = ds1374_id,
+ .probe = ds1374_rtc_probe,
+ .remove = ds1374_rtc_remove,
};
-
-module_i2c_driver(ds1374_driver);
+module_platform_driver(ds1374_rtc_driver);
MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ds1374-rtc");
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 2696493c808b6..b16066c51c673 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -120,6 +120,15 @@ 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 || COMPILE_TEST
+ 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 3aafd997917a4..46aa9505fbe1d 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 0000000000000..583d2fba5b474
--- /dev/null
+++ b/drivers/watchdog/ds1374-wdt.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2017, National Instruments Corp.
+ *
+ * Dallas/Maxim DS1374 Watchdog Driver, heavily based on the older
+ * drivers/rtc/rtc-ds1374.c implementation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ds1374.h>
+
+#define DS1374_WDT_RATE 4096 /* Hz */
+#define DS1374_WDT_MIN_TIMEOUT 1 /* seconds */
+#define DS1374_WDT_DEFAULT_TIMEOUT 30 /* seconds */
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int timeout;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout");
+
+struct ds1374_wdt {
+ struct ds1374 *chip;
+ struct device *dev;
+ struct watchdog_device wdd;
+ int remapped_reset;
+};
+
+static int ds1374_read_counter(struct ds1374_wdt *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->chip->regmap, reg, buf, nbytes);
+ if (ret) {
+ dev_err(ds1374->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;
+}
+
+static int ds1374_write_counter(struct ds1374_wdt *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->chip->regmap, reg, buf, nbytes);
+}
+
+
+static int ds1374_wdt_stop(struct watchdog_device *wdog)
+{
+ struct ds1374_wdt *ds1374_wdt = watchdog_get_drvdata(wdog);
+ unsigned int cr;
+ int ret;
+
+ ret = regmap_read(ds1374_wdt->chip->regmap, DS1374_REG_CR, &cr);
+ /* Disable watchdog timer */
+ cr &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_WDSTR);
+
+ return regmap_write(ds1374_wdt->chip->regmap, DS1374_REG_CR, cr);
+}
+
+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_counter(ds1374_wdt, &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);
+ unsigned int timeout = DS1374_WDT_RATE * t;
+ int cr, err;
+
+ err = regmap_read(ds1374_wdt->chip->regmap, DS1374_REG_CR, &cr);
+ if (err)
+ return 0;
+
+ /* Disable any existing watchdog/alarm before setting the new one */
+ cr &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
+
+ err = regmap_write(ds1374_wdt->chip->regmap, DS1374_REG_CR, cr);
+ if (err) {
+ dev_err(ds1374_wdt->dev, "couldn't disable wdt to set new time\n");
+ return err;
+ }
+
+ err = ds1374_write_counter(ds1374_wdt, 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;
+
+ /* Enable watchdog timer */
+ cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM | DS1374_REG_CR_AIE;
+
+ if (ds1374_wdt->remapped_reset)
+ cr |= DS1374_REG_CR_WDSTR;
+
+ return regmap_write(ds1374_wdt->chip->regmap, DS1374_REG_CR, cr);
+}
+
+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 *ds1374_wdt;
+ int err;
+
+ ds1374_wdt = devm_kzalloc(dev, sizeof(*ds1374_wdt), GFP_KERNEL);
+ if (!ds1374_wdt)
+ return -ENOMEM;
+ ds1374_wdt->chip = ds1374;
+ platform_set_drvdata(pdev, ds1374_wdt);
+
+ ds1374_wdt->wdd.info = &ds1374_wdt_info;
+ ds1374_wdt->wdd.ops = &ds1374_wdt_ops;
+ ds1374_wdt->wdd.min_timeout = DS1374_WDT_MIN_TIMEOUT;
+ ds1374_wdt->wdd.timeout = DS1374_WDT_DEFAULT_TIMEOUT;
+ ds1374_wdt->wdd.max_timeout = 0x1ffffff / DS1374_WDT_RATE;
+ ds1374_wdt->wdd.parent = dev->parent;
+
+ watchdog_init_timeout(&ds1374_wdt->wdd, timeout, dev);
+ watchdog_set_nowayout(&ds1374_wdt->wdd, nowayout);
+ watchdog_stop_on_reboot(&ds1374_wdt->wdd);
+ watchdog_set_drvdata(&ds1374_wdt->wdd, ds1374_wdt);
+
+ /* TODO: */
+ ds1374_wdt->remapped_reset = 1;
+
+ err = devm_watchdog_register_device(dev, &ds1374_wdt->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 *ds1374_wdt = platform_get_drvdata(pdev);
+
+ if (!nowayout)
+ ds1374_wdt_stop(&ds1374_wdt->wdd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ds1374_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int ds1374_resume(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ds1374_wdt_pm, ds1374_wdt_suspend, ds1374_wdt_resume);
+
+static struct platform_driver ds1374_wdt_driver = {
+ .probe = ds1374_wdt_probe,
+ .remove = ds1374_wdt_remove,
+ .driver = {
+ .name = "ds1374-wdt",
+ .pm = &ds1374_wdt_pm,
+ },
+};
+module_platform_driver(ds1374_wdt_driver);
+
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
+MODULE_DESCRIPTION("Maxim/Dallas DS1374 WDT Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ds1374-wdt");