Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Watchdog driver for Ricoh RN5T618 PMIC
0004  *
0005  * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
0006  */
0007 
0008 #include <linux/device.h>
0009 #include <linux/mfd/rn5t618.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/watchdog.h>
0013 
0014 #define DRIVER_NAME "rn5t618-wdt"
0015 
0016 static bool nowayout = WATCHDOG_NOWAYOUT;
0017 static unsigned int timeout;
0018 
0019 module_param(timeout, uint, 0);
0020 MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds");
0021 
0022 module_param(nowayout, bool, 0);
0023 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0024          __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0025 
0026 struct rn5t618_wdt {
0027     struct watchdog_device wdt_dev;
0028     struct rn5t618 *rn5t618;
0029 };
0030 
0031 /*
0032  * This array encodes the values of WDOGTIM field for the supported
0033  * watchdog expiration times. If the watchdog is not accessed before
0034  * the timer expiration, the PMU generates an interrupt and if the CPU
0035  * doesn't clear it within one second the system is restarted.
0036  */
0037 static const struct {
0038     u8 reg_val;
0039     unsigned int time;
0040 } rn5t618_wdt_map[] = {
0041     { 0, 1 },
0042     { 1, 8 },
0043     { 2, 32 },
0044     { 3, 128 },
0045 };
0046 
0047 static int rn5t618_wdt_set_timeout(struct watchdog_device *wdt_dev,
0048                    unsigned int t)
0049 {
0050     struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
0051     int ret, i;
0052 
0053     for (i = 0; i < ARRAY_SIZE(rn5t618_wdt_map); i++) {
0054         if (rn5t618_wdt_map[i].time + 1 >= t)
0055             break;
0056     }
0057 
0058     if (i == ARRAY_SIZE(rn5t618_wdt_map))
0059         return -EINVAL;
0060 
0061     ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
0062                  RN5T618_WATCHDOG_WDOGTIM_M,
0063                  rn5t618_wdt_map[i].reg_val);
0064     if (!ret)
0065         wdt_dev->timeout = rn5t618_wdt_map[i].time;
0066 
0067     return ret;
0068 }
0069 
0070 static int rn5t618_wdt_start(struct watchdog_device *wdt_dev)
0071 {
0072     struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
0073     int ret;
0074 
0075     ret = rn5t618_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
0076     if (ret)
0077         return ret;
0078 
0079     /* enable repower-on */
0080     ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_REPCNT,
0081                  RN5T618_REPCNT_REPWRON,
0082                  RN5T618_REPCNT_REPWRON);
0083     if (ret)
0084         return ret;
0085 
0086     /* enable watchdog */
0087     ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
0088                  RN5T618_WATCHDOG_WDOGEN,
0089                  RN5T618_WATCHDOG_WDOGEN);
0090     if (ret)
0091         return ret;
0092 
0093     /* enable watchdog interrupt */
0094     return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIREN,
0095                   RN5T618_PWRIRQ_IR_WDOG,
0096                   RN5T618_PWRIRQ_IR_WDOG);
0097 }
0098 
0099 static int rn5t618_wdt_stop(struct watchdog_device *wdt_dev)
0100 {
0101     struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
0102 
0103     return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
0104                   RN5T618_WATCHDOG_WDOGEN, 0);
0105 }
0106 
0107 static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev)
0108 {
0109     struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
0110     unsigned int val;
0111     int ret;
0112 
0113     /* The counter is restarted after a R/W access to watchdog register */
0114     ret = regmap_read(wdt->rn5t618->regmap, RN5T618_WATCHDOG, &val);
0115     if (ret)
0116         return ret;
0117 
0118     ret = regmap_write(wdt->rn5t618->regmap, RN5T618_WATCHDOG, val);
0119     if (ret)
0120         return ret;
0121 
0122     /* Clear pending watchdog interrupt */
0123     return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIRQ,
0124                   RN5T618_PWRIRQ_IR_WDOG, 0);
0125 }
0126 
0127 static const struct watchdog_info rn5t618_wdt_info = {
0128     .options    = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
0129               WDIOF_KEEPALIVEPING,
0130     .identity   = DRIVER_NAME,
0131 };
0132 
0133 static const struct watchdog_ops rn5t618_wdt_ops = {
0134     .owner          = THIS_MODULE,
0135     .start          = rn5t618_wdt_start,
0136     .stop           = rn5t618_wdt_stop,
0137     .ping           = rn5t618_wdt_ping,
0138     .set_timeout    = rn5t618_wdt_set_timeout,
0139 };
0140 
0141 static int rn5t618_wdt_probe(struct platform_device *pdev)
0142 {
0143     struct device *dev = &pdev->dev;
0144     struct rn5t618 *rn5t618 = dev_get_drvdata(dev->parent);
0145     struct rn5t618_wdt *wdt;
0146     int min_timeout, max_timeout;
0147 
0148     wdt = devm_kzalloc(dev, sizeof(struct rn5t618_wdt), GFP_KERNEL);
0149     if (!wdt)
0150         return -ENOMEM;
0151 
0152     min_timeout = rn5t618_wdt_map[0].time;
0153     max_timeout = rn5t618_wdt_map[ARRAY_SIZE(rn5t618_wdt_map) - 1].time;
0154 
0155     wdt->rn5t618 = rn5t618;
0156     wdt->wdt_dev.info = &rn5t618_wdt_info;
0157     wdt->wdt_dev.ops = &rn5t618_wdt_ops;
0158     wdt->wdt_dev.min_timeout = min_timeout;
0159     wdt->wdt_dev.max_timeout = max_timeout;
0160     wdt->wdt_dev.timeout = max_timeout;
0161     wdt->wdt_dev.parent = dev;
0162 
0163     watchdog_set_drvdata(&wdt->wdt_dev, wdt);
0164     watchdog_init_timeout(&wdt->wdt_dev, timeout, dev);
0165     watchdog_set_nowayout(&wdt->wdt_dev, nowayout);
0166 
0167     platform_set_drvdata(pdev, wdt);
0168 
0169     return watchdog_register_device(&wdt->wdt_dev);
0170 }
0171 
0172 static int rn5t618_wdt_remove(struct platform_device *pdev)
0173 {
0174     struct rn5t618_wdt *wdt = platform_get_drvdata(pdev);
0175 
0176     watchdog_unregister_device(&wdt->wdt_dev);
0177 
0178     return 0;
0179 }
0180 
0181 static struct platform_driver rn5t618_wdt_driver = {
0182     .probe = rn5t618_wdt_probe,
0183     .remove = rn5t618_wdt_remove,
0184     .driver = {
0185         .name   = DRIVER_NAME,
0186     },
0187 };
0188 
0189 module_platform_driver(rn5t618_wdt_driver);
0190 
0191 MODULE_ALIAS("platform:rn5t618-wdt");
0192 MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
0193 MODULE_DESCRIPTION("RN5T618 watchdog driver");
0194 MODULE_LICENSE("GPL v2");