Skip to content

Commit fd90d48

Browse files
cpackham-atlnzalexandrebelloni
authored andcommitted
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 <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexandre Belloni <[email protected]>
1 parent 06c4e10 commit fd90d48

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

drivers/rtc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ config RTC_DRV_AS3722
238238
config RTC_DRV_DS1307
239239
tristate "Dallas/Maxim DS1307/37/38/39/40/41, ST M41T00, EPSON RX-8025, ISL12057"
240240
select REGMAP_I2C
241+
select WATCHDOG_CORE if WATCHDOG
241242
help
242243
If you say yes here you get support for various compatible RTC
243244
chips (often with battery backup) connected with I2C. This driver

drivers/rtc/rtc-ds1307.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <linux/hwmon-sysfs.h>
2323
#include <linux/clk-provider.h>
2424
#include <linux/regmap.h>
25+
#include <linux/watchdog.h>
2526

2627
/*
2728
* We can't determine type by probing, but if we expect pre-Linux code
@@ -144,8 +145,15 @@ enum ds_type {
144145
# define M41TXX_BIT_CALIB_SIGN BIT(5)
145146
# define M41TXX_M_CALIBRATION GENMASK(4, 0)
146147

148+
#define DS1388_REG_WDOG_HUN_SECS 0x08
149+
#define DS1388_REG_WDOG_SECS 0x09
147150
#define DS1388_REG_FLAG 0x0b
151+
# define DS1388_BIT_WF BIT(6)
148152
# define DS1388_BIT_OSF BIT(7)
153+
#define DS1388_REG_CONTROL 0x0c
154+
# define DS1388_BIT_RST BIT(0)
155+
# define DS1388_BIT_WDE BIT(1)
156+
149157
/* negative offset step is -2.034ppm */
150158
#define M41TXX_NEG_OFFSET_STEP_PPB 2034
151159
/* positive offset step is +4.068ppm */
@@ -854,6 +862,72 @@ static int m41txx_rtc_set_offset(struct device *dev, long offset)
854862
ctrl_reg);
855863
}
856864

865+
#ifdef CONFIG_WATCHDOG_CORE
866+
static int ds1388_wdt_start(struct watchdog_device *wdt_dev)
867+
{
868+
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
869+
u8 regs[2];
870+
int ret;
871+
872+
ret = regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
873+
DS1388_BIT_WF, 0);
874+
if (ret)
875+
return ret;
876+
877+
ret = regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL,
878+
DS1388_BIT_WDE | DS1388_BIT_RST, 0);
879+
if (ret)
880+
return ret;
881+
882+
/*
883+
* watchdog timeouts are measured in seconds. So ignore hundredths of
884+
* seconds field.
885+
*/
886+
regs[0] = 0;
887+
regs[1] = bin2bcd(wdt_dev->timeout);
888+
889+
ret = regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs,
890+
sizeof(regs));
891+
if (ret)
892+
return ret;
893+
894+
return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL,
895+
DS1388_BIT_WDE | DS1388_BIT_RST,
896+
DS1388_BIT_WDE | DS1388_BIT_RST);
897+
}
898+
899+
static int ds1388_wdt_stop(struct watchdog_device *wdt_dev)
900+
{
901+
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
902+
903+
return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL,
904+
DS1388_BIT_WDE | DS1388_BIT_RST, 0);
905+
}
906+
907+
static int ds1388_wdt_ping(struct watchdog_device *wdt_dev)
908+
{
909+
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
910+
u8 regs[2];
911+
912+
return regmap_bulk_read(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs,
913+
sizeof(regs));
914+
}
915+
916+
static int ds1388_wdt_set_timeout(struct watchdog_device *wdt_dev,
917+
unsigned int val)
918+
{
919+
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
920+
u8 regs[2];
921+
922+
wdt_dev->timeout = val;
923+
regs[0] = 0;
924+
regs[1] = bin2bcd(wdt_dev->timeout);
925+
926+
return regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs,
927+
sizeof(regs));
928+
}
929+
#endif
930+
857931
static const struct rtc_class_ops rx8130_rtc_ops = {
858932
.read_time = ds1307_get_time,
859933
.set_time = ds1307_set_time,
@@ -1576,6 +1650,46 @@ static void ds1307_clks_register(struct ds1307 *ds1307)
15761650

15771651
#endif /* CONFIG_COMMON_CLK */
15781652

1653+
#ifdef CONFIG_WATCHDOG_CORE
1654+
static const struct watchdog_info ds1388_wdt_info = {
1655+
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
1656+
.identity = "DS1388 watchdog",
1657+
};
1658+
1659+
static const struct watchdog_ops ds1388_wdt_ops = {
1660+
.owner = THIS_MODULE,
1661+
.start = ds1388_wdt_start,
1662+
.stop = ds1388_wdt_stop,
1663+
.ping = ds1388_wdt_ping,
1664+
.set_timeout = ds1388_wdt_set_timeout,
1665+
1666+
};
1667+
1668+
static void ds1307_wdt_register(struct ds1307 *ds1307)
1669+
{
1670+
struct watchdog_device *wdt;
1671+
1672+
if (ds1307->type != ds_1388)
1673+
return;
1674+
1675+
wdt = devm_kzalloc(ds1307->dev, sizeof(*wdt), GFP_KERNEL);
1676+
1677+
wdt->info = &ds1388_wdt_info;
1678+
wdt->ops = &ds1388_wdt_ops;
1679+
wdt->timeout = 99;
1680+
wdt->max_timeout = 99;
1681+
wdt->min_timeout = 1;
1682+
1683+
watchdog_init_timeout(wdt, 0, ds1307->dev);
1684+
watchdog_set_drvdata(wdt, ds1307);
1685+
devm_watchdog_register_device(ds1307->dev, wdt);
1686+
}
1687+
#else
1688+
static void ds1307_wdt_register(struct ds1307 *ds1307)
1689+
{
1690+
}
1691+
#endif /* CONFIG_WATCHDOG_CORE */
1692+
15791693
static const struct regmap_config regmap_config = {
15801694
.reg_bits = 8,
15811695
.val_bits = 8,
@@ -1865,6 +1979,7 @@ static int ds1307_probe(struct i2c_client *client,
18651979

18661980
ds1307_hwmon_register(ds1307);
18671981
ds1307_clks_register(ds1307);
1982+
ds1307_wdt_register(ds1307);
18681983

18691984
return 0;
18701985

0 commit comments

Comments
 (0)