From patchwork Mon Dec 5 08:29:45 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: sny@kamstrup.com X-Patchwork-Id: 702628 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-wj0-x23e.google.com (mail-wj0-x23e.google.com [IPv6:2a00:1450:400c:c01::23e]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3tXHyf5D7Bz9t0G for ; Mon, 5 Dec 2016 19:30:50 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.b="v7SF11jI"; dkim-atps=neutral Received: by mail-wj0-x23e.google.com with SMTP id xy5sf66110114wjc.0 for ; Mon, 05 Dec 2016 00:30:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20120806; h=sender:from:to:cc:subject:date:message-id:mime-version :x-original-sender:x-original-authentication-results:reply-to :precedence:mailing-list:list-id:x-spam-checked-in-group:list-post :list-help:list-archive:list-subscribe:list-unsubscribe; bh=Gem0A8kSdq/dpw9zv0xeNNHjOT3rWRuyt70g5lugx1U=; b=v7SF11jIm2cxubIibdMC536S5XHrS8EBsTQC6hSem3yd0wgj/j/UU9K/0VGbH7g7X4 6awFNavKzTLkH7qgd8Vje5hL8gFYq4aEKjuIGathXPWVpQHiO1oKnE4bgbH0steQbCO9 PzZYxkKeVevlkIPk76LJoar0gWtI8U85ua7w488tCngmTCw+Jw0hNpZhmoWxZz64y5vk 4eddWmpvLrSmlnH/Nz6CTc8C5AfN3yGbiljGj9IcFBF3pMz3zHZznZiAtuHFrESsbfjp Bw0Ui1uSnA9iPXEx98JFfX1Wa2qD7TSibNM6isIlMDmAOOSOZh6DyEu3tI/UJPvfHmL/ r/ew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :mime-version:x-original-sender:x-original-authentication-results :reply-to:precedence:mailing-list:list-id:x-spam-checked-in-group :list-post:list-help:list-archive:list-subscribe:list-unsubscribe; bh=Gem0A8kSdq/dpw9zv0xeNNHjOT3rWRuyt70g5lugx1U=; b=lE96LCZ3Ln6uFmaUwt+8LTAquZezQaOPfsyMisATgZNlxrbrVGywJU3DtEMKlWu7Wh avu8s14taqP3uVHX0KQsEiWhg9vsYIYMdvScEmuwBxv+IpqlYHP6EITXvAWBSBd1Rxd7 1cxqtTUquT8XT4WI2mEo5Yoy2Uc48TE0HGItmc9vv21+JeJeLDGfiKFmSB1Xb11SKdmV 8qlq5WyLoKSPyVRQmtSO68Ku6Ac0BVn/ImLHVJsYrihHK+/w9u54JLK7IWXb9I9IFhDO 6lCaFcvHyyZ478BgVt9cOJ0XPAJ9AU/DQK6VXp6czFqgzrLmtVclnJ+ffdGkzRchj2y0 3GDg== Sender: rtc-linux@googlegroups.com X-Gm-Message-State: AKaTC03wKtL6U7GqtLyxX1yPlOj35KRbI2gfwr8gi9v4nRo9WmWVKB9+FJ5A+tZbUwcAyA== X-Received: by 10.46.77.202 with SMTP id c71mr780333ljd.6.1480926647349; Mon, 05 Dec 2016 00:30:47 -0800 (PST) X-BeenThere: rtc-linux@googlegroups.com Received: by 10.46.9.70 with SMTP id 67ls2476034ljj.34.gmail; Mon, 05 Dec 2016 00:30:46 -0800 (PST) X-Received: by 10.46.69.136 with SMTP id s130mr8205049lja.31.1480926646582; Mon, 05 Dec 2016 00:30:46 -0800 (PST) Received: from mail.kamstrup.com (mail.kamstrup.com. [93.167.225.188]) by gmr-mx.google.com with ESMTPS id p138si122383wme.1.2016.12.05.00.30.46 for (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 05 Dec 2016 00:30:46 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of sny@kamstrup.com designates 93.167.225.188 as permitted sender) client-ip=93.167.225.188; Received: from mail.kamstrup.com (172.20.21.156) by mail.kamstrup.com (172.16.0.45) with Microsoft SMTP Server (TLS) id 14.3.169.1; Mon, 5 Dec 2016 09:30:44 +0100 From: To: CC: Sean Nyekjaer Subject: [rtc-linux] [RFC PATCH] rtc: pcf2127: add support for pcf2127 watchdog functionality Date: Mon, 5 Dec 2016 09:29:45 +0100 Message-ID: <20161205082945.27251-1-sny@kamstrup.com> X-Mailer: git-send-email 2.11.0 MIME-Version: 1.0 X-Originating-IP: [172.20.21.156] X-Original-Sender: sny@kamstrup.com X-Original-Authentication-Results: gmr-mx.google.com; spf=pass (google.com: best guess record for domain of sny@kamstrup.com designates 93.167.225.188 as permitted sender) smtp.mailfrom=sny@kamstrup.com Reply-To: rtc-linux@googlegroups.com Precedence: list Mailing-list: list rtc-linux@googlegroups.com; contact rtc-linux+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: rtc-linux@googlegroups.com X-Google-Group-Id: 712029733259 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , From: Sean Nyekjaer PCF2129 does not have watchdog functionality built-in so we are only allowing to enable watchdog for PCF2127. Watchdog functionality is done with great inspiration from the rtc-ds1374 driver. Signed-off-by: Sean Nyekjaer --- - Still missing devicetre documentation - Untested on real hardware :-/ hardware is ready in january - Is a compile option and devicetree the right choice? or would it be fine with only devicetree option? drivers/rtc/Kconfig | 7 ++ drivers/rtc/rtc-pcf2127.c | 171 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 177 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e859d148aba9..c8985be81d83 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -799,6 +799,13 @@ config RTC_DRV_PCF2127 This driver can also be built as a module. If so, the module will be called rtc-pcf2127. +config RTC_DRV_PCF2127_WDT + bool "NXP PCF2127 watchdog timer" + depends on RTC_DRV_PCF2127 + help + If you say Y here you will get support for the + watchdog timer in the NXP PCF2127 chip real-time clock chips. + config RTC_DRV_RV3029C2 tristate "Micro Crystal RV3029/3049" depends on RTC_I2C_AND_SPI diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c index 2bfdf638b673..b080a7973645 100644 --- a/drivers/rtc/rtc-pcf2127.c +++ b/drivers/rtc/rtc-pcf2127.c @@ -21,6 +21,13 @@ #include #include #include +#ifdef CONFIG_RTC_DRV_PCF2127_WDT +#include +#include +#include +#include +#include +#endif #define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */ #define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */ @@ -28,6 +35,13 @@ #define PCF2127_REG_CTRL3 (0x02) /* Control Register 3 */ #define PCF2127_REG_CTRL3_BLF BIT(2) +#define PCF2127_REG_WDG_TIMCTL (0x10) +#define PCF2127_REG_WDG_TIMCTL_CD (BIT(7) | BIT(6)) +#define PCF2127_REG_WDG_T_CD_EN_RST (BIT(7) | BIT(6))/* WD en,rst assert */ +#define PCF2127_REG_WDG_TIMCTL_TF (BIT(1) | BIT(0)) +#define PCF2127_REG_WDG_T_TF_1HZ BIT(1) /* Timer clock source */ +#define PCF2127_REG_WDG_TIMVAL (0x11) + #define PCF2127_REG_SC (0x03) /* datetime */ #define PCF2127_REG_MN (0x04) #define PCF2127_REG_HR (0x05) @@ -43,6 +57,137 @@ struct pcf2127 { struct regmap *regmap; }; +#ifdef CONFIG_RTC_DRV_PCF2127_WDT +static struct pcf2127 *save_pcf2127; + +/* default 32 sec timeout */ +#define WD_TIMO 32 + +static int wdt_margin = WD_TIMO; + +static const struct watchdog_info pcf2127_wdt_info = { + .identity = "PCF2127 WDT", + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, +}; + +static int pcf2127_wdt_enable(void) +{ + return regmap_write(save_pcf2127->regmap, PCF2127_REG_WDG_TIMCTL, + PCF2127_REG_WDG_T_CD_EN_RST | PCF2127_REG_WDG_T_TF_1HZ); +} + +static int pcf2127_wdt_settimeout(unsigned int timeout) +{ + int err; + + err = pcf2127_wdt_enable(); + if (err) + return err; + + err = regmap_write(save_pcf2127->regmap, PCF2127_REG_WDG_TIMVAL, + timeout); + if (err) + return err; + + return 0; +} + +static void pcf2127_wdt_ping(void) +{ + int ret = 0; + + ret = pcf2127_wdt_settimeout(wdt_margin); + if (ret) + pr_info("WD TICK FAIL!!!!!!!!!! %i\n", ret); +} + +static int pcf2127_wdt_disable(void) +{ + return regmap_write_bits(save_pcf2127->regmap, PCF2127_REG_WDG_TIMCTL, + PCF2127_REG_WDG_TIMCTL_CD, 0x00); +} + +static long pcf2127_wdt_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_margin, options; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, + &pcf2127_wdt_info, + sizeof(pcf2127_wdt_info)) ? -EFAULT : 0; + case WDIOC_SETOPTIONS: + if (copy_from_user(&options, (int __user *)arg, sizeof(int))) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + pr_info("disable watchdog\n"); + if (pcf2127_wdt_disable()) + return -EFAULT; + } + + if (options & WDIOS_ENABLECARD) { + pr_info("enable watchdog\n"); + pcf2127_wdt_settimeout(wdt_margin); + pcf2127_wdt_ping(); + } + + return -EINVAL; + case WDIOC_KEEPALIVE: + pcf2127_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + + if (new_margin < 1 || new_margin > 255) + return -EINVAL; + + wdt_margin = new_margin; + if (pcf2127_wdt_settimeout(new_margin)) + return -EFAULT; + pcf2127_wdt_ping(); + /* fallthrough */ + case WDIOC_GETTIMEOUT: + return put_user(wdt_margin, (int __user *)arg); + default: + return -ENOTTY; + } +} + +static ssize_t pcf2127_wdt_write(struct file *file, const char *data, + size_t len, loff_t *ppos) +{ + if (len) { + pcf2127_wdt_ping(); + return 1; + } + return 0; +} + +static ssize_t pcf2127_wdt_read(struct file *file, char __user *data, + size_t len, loff_t *ppos) +{ + return 0; +} + +static const struct file_operations pcf2127_wdt_fops = { + .owner = THIS_MODULE, + .read = pcf2127_wdt_read, + .unlocked_ioctl = pcf2127_wdt_unlocked_ioctl, + .write = pcf2127_wdt_write, + .llseek = no_llseek, +}; + +static struct miscdevice pcf2127_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pcf2127_wdt_fops, +}; + +#endif + /* * In the routines that deal directly with the pcf2127 hardware, we use * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. @@ -175,6 +320,9 @@ static const struct rtc_class_ops pcf2127_rtc_ops = { static int pcf2127_probe(struct device *dev, struct regmap *regmap, const char *name) { +#ifdef CONFIG_RTC_DRV_PCF2127_WDT + int ret; +#endif struct pcf2127 *pcf2127; dev_dbg(dev, "%s\n", __func__); @@ -190,7 +338,24 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap, pcf2127->rtc = devm_rtc_device_register(dev, name, &pcf2127_rtc_ops, THIS_MODULE); - return PTR_ERR_OR_ZERO(pcf2127->rtc); + if (IS_ERR(pcf2127->rtc)) + return PTR_ERR_OR_ZERO(pcf2127->rtc); + +#ifdef CONFIG_RTC_DRV_PCF2127_WDT + if (!of_device_is_compatible(dev->of_node, "nxp,pcf2127")) + return 0; + + save_pcf2127 = pcf2127; + + if (of_property_read_bool(dev->of_node, "watchdog")) { + ret = misc_register(&pcf2127_wdt_miscdev); + if (ret) + return ret; + } + pcf2127_wdt_settimeout(32); +#endif + + return 0; } #ifdef CONFIG_OF @@ -427,6 +592,10 @@ static void __exit pcf2127_exit(void) { pcf2127_spi_unregister_driver(); pcf2127_i2c_unregister_driver(); + +#ifdef CONFIG_RTC_DRV_PCF2127_WDT + misc_deregister(&pcf2127_wdt_miscdev); +#endif } module_exit(pcf2127_exit)