rtc: ds1307: add support for watchdog timer on ds1388

The DS1388 variant has watchdog timer capabilities. When using a DS1388
and having enabled CONFIG_WATCHDOG_CORE register a watchdog device for
the DS1388.

Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20200330025500.6991-1-chris.packham@alliedtelesis.co.nz
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
Chris Packham 2020-03-30 15:55:00 +13:00 committed by Alexandre Belloni
parent 06c4e103bf
commit fd90d48db0
2 changed files with 116 additions and 0 deletions

View file

@ -238,6 +238,7 @@ config RTC_DRV_AS3722
config RTC_DRV_DS1307 config RTC_DRV_DS1307
tristate "Dallas/Maxim DS1307/37/38/39/40/41, ST M41T00, EPSON RX-8025, ISL12057" tristate "Dallas/Maxim DS1307/37/38/39/40/41, ST M41T00, EPSON RX-8025, ISL12057"
select REGMAP_I2C select REGMAP_I2C
select WATCHDOG_CORE if WATCHDOG
help help
If you say yes here you get support for various compatible RTC If you say yes here you get support for various compatible RTC
chips (often with battery backup) connected with I2C. This driver chips (often with battery backup) connected with I2C. This driver

View file

@ -22,6 +22,7 @@
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/watchdog.h>
/* /*
* We can't determine type by probing, but if we expect pre-Linux code * We can't determine type by probing, but if we expect pre-Linux code
@ -144,8 +145,15 @@ enum ds_type {
# define M41TXX_BIT_CALIB_SIGN BIT(5) # define M41TXX_BIT_CALIB_SIGN BIT(5)
# define M41TXX_M_CALIBRATION GENMASK(4, 0) # define M41TXX_M_CALIBRATION GENMASK(4, 0)
#define DS1388_REG_WDOG_HUN_SECS 0x08
#define DS1388_REG_WDOG_SECS 0x09
#define DS1388_REG_FLAG 0x0b #define DS1388_REG_FLAG 0x0b
# define DS1388_BIT_WF BIT(6)
# define DS1388_BIT_OSF BIT(7) # define DS1388_BIT_OSF BIT(7)
#define DS1388_REG_CONTROL 0x0c
# define DS1388_BIT_RST BIT(0)
# define DS1388_BIT_WDE BIT(1)
/* negative offset step is -2.034ppm */ /* negative offset step is -2.034ppm */
#define M41TXX_NEG_OFFSET_STEP_PPB 2034 #define M41TXX_NEG_OFFSET_STEP_PPB 2034
/* positive offset step is +4.068ppm */ /* positive offset step is +4.068ppm */
@ -854,6 +862,72 @@ static int m41txx_rtc_set_offset(struct device *dev, long offset)
ctrl_reg); ctrl_reg);
} }
#ifdef CONFIG_WATCHDOG_CORE
static int ds1388_wdt_start(struct watchdog_device *wdt_dev)
{
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
u8 regs[2];
int ret;
ret = regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
DS1388_BIT_WF, 0);
if (ret)
return ret;
ret = regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL,
DS1388_BIT_WDE | DS1388_BIT_RST, 0);
if (ret)
return ret;
/*
* watchdog timeouts are measured in seconds. So ignore hundredths of
* seconds field.
*/
regs[0] = 0;
regs[1] = bin2bcd(wdt_dev->timeout);
ret = regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs,
sizeof(regs));
if (ret)
return ret;
return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL,
DS1388_BIT_WDE | DS1388_BIT_RST,
DS1388_BIT_WDE | DS1388_BIT_RST);
}
static int ds1388_wdt_stop(struct watchdog_device *wdt_dev)
{
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL,
DS1388_BIT_WDE | DS1388_BIT_RST, 0);
}
static int ds1388_wdt_ping(struct watchdog_device *wdt_dev)
{
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
u8 regs[2];
return regmap_bulk_read(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs,
sizeof(regs));
}
static int ds1388_wdt_set_timeout(struct watchdog_device *wdt_dev,
unsigned int val)
{
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
u8 regs[2];
wdt_dev->timeout = val;
regs[0] = 0;
regs[1] = bin2bcd(wdt_dev->timeout);
return regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs,
sizeof(regs));
}
#endif
static const struct rtc_class_ops rx8130_rtc_ops = { static const struct rtc_class_ops rx8130_rtc_ops = {
.read_time = ds1307_get_time, .read_time = ds1307_get_time,
.set_time = ds1307_set_time, .set_time = ds1307_set_time,
@ -1576,6 +1650,46 @@ static void ds1307_clks_register(struct ds1307 *ds1307)
#endif /* CONFIG_COMMON_CLK */ #endif /* CONFIG_COMMON_CLK */
#ifdef CONFIG_WATCHDOG_CORE
static const struct watchdog_info ds1388_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "DS1388 watchdog",
};
static const struct watchdog_ops ds1388_wdt_ops = {
.owner = THIS_MODULE,
.start = ds1388_wdt_start,
.stop = ds1388_wdt_stop,
.ping = ds1388_wdt_ping,
.set_timeout = ds1388_wdt_set_timeout,
};
static void ds1307_wdt_register(struct ds1307 *ds1307)
{
struct watchdog_device *wdt;
if (ds1307->type != ds_1388)
return;
wdt = devm_kzalloc(ds1307->dev, sizeof(*wdt), GFP_KERNEL);
wdt->info = &ds1388_wdt_info;
wdt->ops = &ds1388_wdt_ops;
wdt->timeout = 99;
wdt->max_timeout = 99;
wdt->min_timeout = 1;
watchdog_init_timeout(wdt, 0, ds1307->dev);
watchdog_set_drvdata(wdt, ds1307);
devm_watchdog_register_device(ds1307->dev, wdt);
}
#else
static void ds1307_wdt_register(struct ds1307 *ds1307)
{
}
#endif /* CONFIG_WATCHDOG_CORE */
static const struct regmap_config regmap_config = { static const struct regmap_config regmap_config = {
.reg_bits = 8, .reg_bits = 8,
.val_bits = 8, .val_bits = 8,
@ -1865,6 +1979,7 @@ static int ds1307_probe(struct i2c_client *client,
ds1307_hwmon_register(ds1307); ds1307_hwmon_register(ds1307);
ds1307_clks_register(ds1307); ds1307_clks_register(ds1307);
ds1307_wdt_register(ds1307);
return 0; return 0;