122b1c841SBeniamino Galvani /* 222b1c841SBeniamino Galvani * Watchdog driver for Ricoh RN5T618 PMIC 322b1c841SBeniamino Galvani * 422b1c841SBeniamino Galvani * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> 522b1c841SBeniamino Galvani * 622b1c841SBeniamino Galvani * This program is free software; you can redistribute it and/or 722b1c841SBeniamino Galvani * modify it under the terms of the GNU General Public License 822b1c841SBeniamino Galvani * version 2 as published by the Free Software Foundation. 922b1c841SBeniamino Galvani * 1022b1c841SBeniamino Galvani * You should have received a copy of the GNU General Public License 1122b1c841SBeniamino Galvani * along with this program. If not, see <http://www.gnu.org/licenses/>. 1222b1c841SBeniamino Galvani */ 1322b1c841SBeniamino Galvani 1422b1c841SBeniamino Galvani #include <linux/device.h> 1522b1c841SBeniamino Galvani #include <linux/mfd/rn5t618.h> 1622b1c841SBeniamino Galvani #include <linux/module.h> 1722b1c841SBeniamino Galvani #include <linux/platform_device.h> 1822b1c841SBeniamino Galvani #include <linux/watchdog.h> 1922b1c841SBeniamino Galvani 2022b1c841SBeniamino Galvani #define DRIVER_NAME "rn5t618-wdt" 2122b1c841SBeniamino Galvani 2222b1c841SBeniamino Galvani static bool nowayout = WATCHDOG_NOWAYOUT; 2322b1c841SBeniamino Galvani static unsigned int timeout; 2422b1c841SBeniamino Galvani 2522b1c841SBeniamino Galvani module_param(timeout, uint, 0); 2622b1c841SBeniamino Galvani MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds"); 2722b1c841SBeniamino Galvani 2822b1c841SBeniamino Galvani module_param(nowayout, bool, 0); 2922b1c841SBeniamino Galvani MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 3022b1c841SBeniamino Galvani __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 3122b1c841SBeniamino Galvani 3222b1c841SBeniamino Galvani struct rn5t618_wdt { 3322b1c841SBeniamino Galvani struct watchdog_device wdt_dev; 3422b1c841SBeniamino Galvani struct rn5t618 *rn5t618; 3522b1c841SBeniamino Galvani }; 3622b1c841SBeniamino Galvani 3722b1c841SBeniamino Galvani /* 3822b1c841SBeniamino Galvani * This array encodes the values of WDOGTIM field for the supported 3922b1c841SBeniamino Galvani * watchdog expiration times. If the watchdog is not accessed before 4022b1c841SBeniamino Galvani * the timer expiration, the PMU generates an interrupt and if the CPU 4122b1c841SBeniamino Galvani * doesn't clear it within one second the system is restarted. 4222b1c841SBeniamino Galvani */ 4322b1c841SBeniamino Galvani static const struct { 4422b1c841SBeniamino Galvani u8 reg_val; 4522b1c841SBeniamino Galvani unsigned int time; 4622b1c841SBeniamino Galvani } rn5t618_wdt_map[] = { 4722b1c841SBeniamino Galvani { 0, 1 }, 4822b1c841SBeniamino Galvani { 1, 8 }, 4922b1c841SBeniamino Galvani { 2, 32 }, 5022b1c841SBeniamino Galvani { 3, 128 }, 5122b1c841SBeniamino Galvani }; 5222b1c841SBeniamino Galvani 5322b1c841SBeniamino Galvani static int rn5t618_wdt_set_timeout(struct watchdog_device *wdt_dev, 5422b1c841SBeniamino Galvani unsigned int t) 5522b1c841SBeniamino Galvani { 5622b1c841SBeniamino Galvani struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); 5722b1c841SBeniamino Galvani int ret, i; 5822b1c841SBeniamino Galvani 5922b1c841SBeniamino Galvani for (i = 0; i < ARRAY_SIZE(rn5t618_wdt_map); i++) { 6022b1c841SBeniamino Galvani if (rn5t618_wdt_map[i].time + 1 >= t) 6122b1c841SBeniamino Galvani break; 6222b1c841SBeniamino Galvani } 6322b1c841SBeniamino Galvani 6422b1c841SBeniamino Galvani if (i == ARRAY_SIZE(rn5t618_wdt_map)) 6522b1c841SBeniamino Galvani return -EINVAL; 6622b1c841SBeniamino Galvani 6722b1c841SBeniamino Galvani ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, 6822b1c841SBeniamino Galvani RN5T618_WATCHDOG_WDOGTIM_M, 6922b1c841SBeniamino Galvani rn5t618_wdt_map[i].reg_val); 7022b1c841SBeniamino Galvani if (!ret) 7122b1c841SBeniamino Galvani wdt_dev->timeout = rn5t618_wdt_map[i].time; 7222b1c841SBeniamino Galvani 7322b1c841SBeniamino Galvani return ret; 7422b1c841SBeniamino Galvani } 7522b1c841SBeniamino Galvani 7622b1c841SBeniamino Galvani static int rn5t618_wdt_start(struct watchdog_device *wdt_dev) 7722b1c841SBeniamino Galvani { 7822b1c841SBeniamino Galvani struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); 7922b1c841SBeniamino Galvani int ret; 8022b1c841SBeniamino Galvani 8122b1c841SBeniamino Galvani ret = rn5t618_wdt_set_timeout(wdt_dev, wdt_dev->timeout); 8222b1c841SBeniamino Galvani if (ret) 8322b1c841SBeniamino Galvani return ret; 8422b1c841SBeniamino Galvani 8522b1c841SBeniamino Galvani /* enable repower-on */ 8622b1c841SBeniamino Galvani ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_REPCNT, 8722b1c841SBeniamino Galvani RN5T618_REPCNT_REPWRON, 8822b1c841SBeniamino Galvani RN5T618_REPCNT_REPWRON); 8922b1c841SBeniamino Galvani if (ret) 9022b1c841SBeniamino Galvani return ret; 9122b1c841SBeniamino Galvani 9222b1c841SBeniamino Galvani /* enable watchdog */ 9322b1c841SBeniamino Galvani ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, 9422b1c841SBeniamino Galvani RN5T618_WATCHDOG_WDOGEN, 9522b1c841SBeniamino Galvani RN5T618_WATCHDOG_WDOGEN); 9622b1c841SBeniamino Galvani if (ret) 9722b1c841SBeniamino Galvani return ret; 9822b1c841SBeniamino Galvani 9922b1c841SBeniamino Galvani /* enable watchdog interrupt */ 10022b1c841SBeniamino Galvani return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIREN, 10122b1c841SBeniamino Galvani RN5T618_PWRIRQ_IR_WDOG, 10222b1c841SBeniamino Galvani RN5T618_PWRIRQ_IR_WDOG); 10322b1c841SBeniamino Galvani } 10422b1c841SBeniamino Galvani 10522b1c841SBeniamino Galvani static int rn5t618_wdt_stop(struct watchdog_device *wdt_dev) 10622b1c841SBeniamino Galvani { 10722b1c841SBeniamino Galvani struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); 10822b1c841SBeniamino Galvani 10922b1c841SBeniamino Galvani return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, 11022b1c841SBeniamino Galvani RN5T618_WATCHDOG_WDOGEN, 0); 11122b1c841SBeniamino Galvani } 11222b1c841SBeniamino Galvani 11322b1c841SBeniamino Galvani static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev) 11422b1c841SBeniamino Galvani { 11522b1c841SBeniamino Galvani struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); 11622b1c841SBeniamino Galvani unsigned int val; 11722b1c841SBeniamino Galvani int ret; 11822b1c841SBeniamino Galvani 11922b1c841SBeniamino Galvani /* The counter is restarted after a R/W access to watchdog register */ 12022b1c841SBeniamino Galvani ret = regmap_read(wdt->rn5t618->regmap, RN5T618_WATCHDOG, &val); 12122b1c841SBeniamino Galvani if (ret) 12222b1c841SBeniamino Galvani return ret; 12322b1c841SBeniamino Galvani 12422b1c841SBeniamino Galvani ret = regmap_write(wdt->rn5t618->regmap, RN5T618_WATCHDOG, val); 12522b1c841SBeniamino Galvani if (ret) 12622b1c841SBeniamino Galvani return ret; 12722b1c841SBeniamino Galvani 12822b1c841SBeniamino Galvani /* Clear pending watchdog interrupt */ 12922b1c841SBeniamino Galvani return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIRQ, 13022b1c841SBeniamino Galvani RN5T618_PWRIRQ_IR_WDOG, 0); 13122b1c841SBeniamino Galvani } 13222b1c841SBeniamino Galvani 1336c368932SBhumika Goyal static const struct watchdog_info rn5t618_wdt_info = { 13422b1c841SBeniamino Galvani .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | 13522b1c841SBeniamino Galvani WDIOF_KEEPALIVEPING, 13622b1c841SBeniamino Galvani .identity = DRIVER_NAME, 13722b1c841SBeniamino Galvani }; 13822b1c841SBeniamino Galvani 13985f15cfcSJulia Lawall static const struct watchdog_ops rn5t618_wdt_ops = { 14022b1c841SBeniamino Galvani .owner = THIS_MODULE, 14122b1c841SBeniamino Galvani .start = rn5t618_wdt_start, 14222b1c841SBeniamino Galvani .stop = rn5t618_wdt_stop, 14322b1c841SBeniamino Galvani .ping = rn5t618_wdt_ping, 14422b1c841SBeniamino Galvani .set_timeout = rn5t618_wdt_set_timeout, 14522b1c841SBeniamino Galvani }; 14622b1c841SBeniamino Galvani 14722b1c841SBeniamino Galvani static int rn5t618_wdt_probe(struct platform_device *pdev) 14822b1c841SBeniamino Galvani { 14922b1c841SBeniamino Galvani struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent); 15022b1c841SBeniamino Galvani struct rn5t618_wdt *wdt; 15122b1c841SBeniamino Galvani int min_timeout, max_timeout; 15222b1c841SBeniamino Galvani 15322b1c841SBeniamino Galvani wdt = devm_kzalloc(&pdev->dev, sizeof(struct rn5t618_wdt), GFP_KERNEL); 15422b1c841SBeniamino Galvani if (!wdt) 15522b1c841SBeniamino Galvani return -ENOMEM; 15622b1c841SBeniamino Galvani 15722b1c841SBeniamino Galvani min_timeout = rn5t618_wdt_map[0].time; 15822b1c841SBeniamino Galvani max_timeout = rn5t618_wdt_map[ARRAY_SIZE(rn5t618_wdt_map) - 1].time; 15922b1c841SBeniamino Galvani 16022b1c841SBeniamino Galvani wdt->rn5t618 = rn5t618; 16122b1c841SBeniamino Galvani wdt->wdt_dev.info = &rn5t618_wdt_info; 16222b1c841SBeniamino Galvani wdt->wdt_dev.ops = &rn5t618_wdt_ops; 16322b1c841SBeniamino Galvani wdt->wdt_dev.min_timeout = min_timeout; 16422b1c841SBeniamino Galvani wdt->wdt_dev.max_timeout = max_timeout; 16522b1c841SBeniamino Galvani wdt->wdt_dev.timeout = max_timeout; 16622b1c841SBeniamino Galvani wdt->wdt_dev.parent = &pdev->dev; 16722b1c841SBeniamino Galvani 16822b1c841SBeniamino Galvani watchdog_set_drvdata(&wdt->wdt_dev, wdt); 16922b1c841SBeniamino Galvani watchdog_init_timeout(&wdt->wdt_dev, timeout, &pdev->dev); 17022b1c841SBeniamino Galvani watchdog_set_nowayout(&wdt->wdt_dev, nowayout); 17122b1c841SBeniamino Galvani 17222b1c841SBeniamino Galvani platform_set_drvdata(pdev, wdt); 17322b1c841SBeniamino Galvani 17422b1c841SBeniamino Galvani return watchdog_register_device(&wdt->wdt_dev); 17522b1c841SBeniamino Galvani } 17622b1c841SBeniamino Galvani 17722b1c841SBeniamino Galvani static int rn5t618_wdt_remove(struct platform_device *pdev) 17822b1c841SBeniamino Galvani { 17922b1c841SBeniamino Galvani struct rn5t618_wdt *wdt = platform_get_drvdata(pdev); 18022b1c841SBeniamino Galvani 18122b1c841SBeniamino Galvani watchdog_unregister_device(&wdt->wdt_dev); 18222b1c841SBeniamino Galvani 18322b1c841SBeniamino Galvani return 0; 18422b1c841SBeniamino Galvani } 18522b1c841SBeniamino Galvani 18622b1c841SBeniamino Galvani static struct platform_driver rn5t618_wdt_driver = { 18722b1c841SBeniamino Galvani .probe = rn5t618_wdt_probe, 18822b1c841SBeniamino Galvani .remove = rn5t618_wdt_remove, 18922b1c841SBeniamino Galvani .driver = { 19022b1c841SBeniamino Galvani .name = DRIVER_NAME, 19122b1c841SBeniamino Galvani }, 19222b1c841SBeniamino Galvani }; 19322b1c841SBeniamino Galvani 19422b1c841SBeniamino Galvani module_platform_driver(rn5t618_wdt_driver); 19522b1c841SBeniamino Galvani 19622b1c841SBeniamino Galvani MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); 19722b1c841SBeniamino Galvani MODULE_DESCRIPTION("RN5T618 watchdog driver"); 19822b1c841SBeniamino Galvani MODULE_LICENSE("GPL v2"); 199