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
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
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
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
4965struct 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+
223308static 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