Skip to content

Commit 0e735ea

Browse files
baxenoalexandrebelloni
authored andcommitted
rtc: pcf2127: add watchdog feature support
Add partial support for the watchdog functionality of both PCF2127 and PCF2129 chips. The programmable watchdog timer is currently using a fixed clock source of 1Hz. This result in a selectable range of 1-255 seconds, which covers most embedded Linux use-cases. Clock sources of 4096Hz, 64Hz and 1/60Hz is mostly useful in MCU use-cases. Countdown timer not available when using watchdog feature. Signed-off-by: Bruno Thomsen <bruno.thomsen@gmail.com> Acked-by: Guenter Roeck <linux@roeck-us.net> Link: https://lore.kernel.org/r/20190822131936.18772-4-bruno.thomsen@gmail.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
1 parent 7f43020 commit 0e735ea

File tree

2 files changed

+124
-1
lines changed

2 files changed

+124
-1
lines changed

drivers/rtc/Kconfig

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,12 @@ config RTC_DRV_PCF2127
888888
depends on RTC_I2C_AND_SPI
889889
help
890890
If you say yes here you get support for the NXP PCF2127/29 RTC
891-
chips.
891+
chips with integrated quartz crystal for industrial applications.
892+
Both chips also have watchdog timer and tamper switch detection
893+
features.
894+
895+
PCF2127 has an additional feature of 512 bytes battery backed
896+
memory that's accessible using nvmem interface.
892897

893898
This driver can also be built as a module. If so, the module
894899
will be called rtc-pcf2127.

drivers/rtc/rtc-pcf2127.c

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
*
66
* Author: Renaud Cerrato <r.cerrato@til-technologies.fr>
77
*
8+
* Watchdog and tamper functions
9+
* Author: Bruno Thomsen <bruno.thomsen@gmail.com>
10+
*
811
* based on the other drivers in this same directory.
912
*
1013
* Datasheet: http://cache.nxp.com/documents/data_sheet/PCF2127.pdf
@@ -18,6 +21,7 @@
1821
#include <linux/module.h>
1922
#include <linux/of.h>
2023
#include <linux/regmap.h>
24+
#include <linux/watchdog.h>
2125

2226
/* Control register 1 */
2327
#define PCF2127_REG_CTRL1 0x00
@@ -35,6 +39,13 @@
3539
#define PCF2127_REG_DW 0x07
3640
#define PCF2127_REG_MO 0x08
3741
#define PCF2127_REG_YR 0x09
42+
/* Watchdog registers */
43+
#define PCF2127_REG_WD_CTL 0x10
44+
#define PCF2127_BIT_WD_CTL_TF0 BIT(0)
45+
#define PCF2127_BIT_WD_CTL_TF1 BIT(1)
46+
#define PCF2127_BIT_WD_CTL_CD0 BIT(6)
47+
#define PCF2127_BIT_WD_CTL_CD1 BIT(7)
48+
#define PCF2127_REG_WD_VAL 0x11
3849
/*
3950
* RAM registers
4051
* PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
@@ -45,9 +56,15 @@
4556
#define PCF2127_REG_RAM_WRT_CMD 0x1C
4657
#define PCF2127_REG_RAM_RD_CMD 0x1D
4758

59+
/* Watchdog timer value constants */
60+
#define PCF2127_WD_VAL_STOP 0
61+
#define PCF2127_WD_VAL_MIN 2
62+
#define PCF2127_WD_VAL_MAX 255
63+
#define PCF2127_WD_VAL_DEFAULT 60
4864

4965
struct pcf2127 {
5066
struct rtc_device *rtc;
67+
struct watchdog_device wdd;
5168
struct regmap *regmap;
5269
};
5370

@@ -220,6 +237,74 @@ static int pcf2127_nvmem_write(void *priv, unsigned int offset,
220237
return ret ?: bytes;
221238
}
222239

240+
/* watchdog driver */
241+
242+
static int pcf2127_wdt_ping(struct watchdog_device *wdd)
243+
{
244+
struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd);
245+
246+
return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL, wdd->timeout);
247+
}
248+
249+
/*
250+
* Restart watchdog timer if feature is active.
251+
*
252+
* Note: Reading CTRL2 register causes watchdog to stop which is unfortunate,
253+
* since register also contain control/status flags for other features.
254+
* Always call this function after reading CTRL2 register.
255+
*/
256+
static int pcf2127_wdt_active_ping(struct watchdog_device *wdd)
257+
{
258+
int ret = 0;
259+
260+
if (watchdog_active(wdd)) {
261+
ret = pcf2127_wdt_ping(wdd);
262+
if (ret)
263+
dev_err(wdd->parent,
264+
"%s: watchdog restart failed, ret=%d\n",
265+
__func__, ret);
266+
}
267+
268+
return ret;
269+
}
270+
271+
static int pcf2127_wdt_start(struct watchdog_device *wdd)
272+
{
273+
return pcf2127_wdt_ping(wdd);
274+
}
275+
276+
static int pcf2127_wdt_stop(struct watchdog_device *wdd)
277+
{
278+
struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd);
279+
280+
return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL,
281+
PCF2127_WD_VAL_STOP);
282+
}
283+
284+
static int pcf2127_wdt_set_timeout(struct watchdog_device *wdd,
285+
unsigned int new_timeout)
286+
{
287+
dev_dbg(wdd->parent, "new watchdog timeout: %is (old: %is)\n",
288+
new_timeout, wdd->timeout);
289+
290+
wdd->timeout = new_timeout;
291+
292+
return pcf2127_wdt_active_ping(wdd);
293+
}
294+
295+
static const struct watchdog_info pcf2127_wdt_info = {
296+
.identity = "NXP PCF2127/PCF2129 Watchdog",
297+
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
298+
};
299+
300+
static const struct watchdog_ops pcf2127_watchdog_ops = {
301+
.owner = THIS_MODULE,
302+
.start = pcf2127_wdt_start,
303+
.stop = pcf2127_wdt_stop,
304+
.ping = pcf2127_wdt_ping,
305+
.set_timeout = pcf2127_wdt_set_timeout,
306+
};
307+
223308
static int pcf2127_probe(struct device *dev, struct regmap *regmap,
224309
const char *name, bool has_nvmem)
225310
{
@@ -242,6 +327,16 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
242327

243328
pcf2127->rtc->ops = &pcf2127_rtc_ops;
244329

330+
pcf2127->wdd.parent = dev;
331+
pcf2127->wdd.info = &pcf2127_wdt_info;
332+
pcf2127->wdd.ops = &pcf2127_watchdog_ops;
333+
pcf2127->wdd.min_timeout = PCF2127_WD_VAL_MIN;
334+
pcf2127->wdd.max_timeout = PCF2127_WD_VAL_MAX;
335+
pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT;
336+
pcf2127->wdd.min_hw_heartbeat_ms = 500;
337+
338+
watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
339+
245340
if (has_nvmem) {
246341
struct nvmem_config nvmem_cfg = {
247342
.priv = pcf2127,
@@ -253,6 +348,29 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
253348
ret = rtc_nvmem_register(pcf2127->rtc, &nvmem_cfg);
254349
}
255350

351+
/*
352+
* Watchdog timer enabled and reset pin /RST activated when timed out.
353+
* Select 1Hz clock source for watchdog timer.
354+
* Timer is not started until WD_VAL is loaded with a valid value.
355+
* Note: Countdown timer disabled and not available.
356+
*/
357+
ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_WD_CTL,
358+
PCF2127_BIT_WD_CTL_CD1 |
359+
PCF2127_BIT_WD_CTL_CD0 |
360+
PCF2127_BIT_WD_CTL_TF1 |
361+
PCF2127_BIT_WD_CTL_TF0,
362+
PCF2127_BIT_WD_CTL_CD1 |
363+
PCF2127_BIT_WD_CTL_CD0 |
364+
PCF2127_BIT_WD_CTL_TF1);
365+
if (ret) {
366+
dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__);
367+
return ret;
368+
}
369+
370+
ret = devm_watchdog_register_device(dev, &pcf2127->wdd);
371+
if (ret)
372+
return ret;
373+
256374
return rtc_register_device(pcf2127->rtc);
257375
}
258376

0 commit comments

Comments
 (0)