Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * System monitoring driver for DA9052 PMICs.
0004  *
0005  * Copyright(c) 2012 Dialog Semiconductor Ltd.
0006  *
0007  * Author: Anthony Olech <Anthony.Olech@diasemi.com>
0008  *
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/delay.h>
0013 #include <linux/uaccess.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/time.h>
0016 #include <linux/watchdog.h>
0017 #include <linux/types.h>
0018 #include <linux/kernel.h>
0019 #include <linux/jiffies.h>
0020 
0021 #include <linux/mfd/da9052/reg.h>
0022 #include <linux/mfd/da9052/da9052.h>
0023 
0024 #define DA9052_DEF_TIMEOUT  4
0025 #define DA9052_TWDMIN       256
0026 
0027 struct da9052_wdt_data {
0028     struct watchdog_device wdt;
0029     struct da9052 *da9052;
0030     unsigned long jpast;
0031 };
0032 
0033 static const struct {
0034     u8 reg_val;
0035     int time;  /* Seconds */
0036 } da9052_wdt_maps[] = {
0037     { 1, 2 },
0038     { 2, 4 },
0039     { 3, 8 },
0040     { 4, 16 },
0041     { 5, 32 },
0042     { 5, 33 },  /* Actual time  32.768s so included both 32s and 33s */
0043     { 6, 65 },
0044     { 6, 66 },  /* Actual time 65.536s so include both, 65s and 66s */
0045     { 7, 131 },
0046 };
0047 
0048 
0049 static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev,
0050                   unsigned int timeout)
0051 {
0052     struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
0053     struct da9052 *da9052 = driver_data->da9052;
0054     int ret, i;
0055 
0056     /*
0057      * Disable the Watchdog timer before setting
0058      * new time out.
0059      */
0060     ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
0061                 DA9052_CONTROLD_TWDSCALE, 0);
0062     if (ret < 0) {
0063         dev_err(da9052->dev, "Failed to disable watchdog bit, %d\n",
0064             ret);
0065         return ret;
0066     }
0067     if (timeout) {
0068         /*
0069          * To change the timeout, da9052 needs to
0070          * be disabled for at least 150 us.
0071          */
0072         udelay(150);
0073 
0074         /* Set the desired timeout */
0075         for (i = 0; i < ARRAY_SIZE(da9052_wdt_maps); i++)
0076             if (da9052_wdt_maps[i].time == timeout)
0077                 break;
0078 
0079         if (i == ARRAY_SIZE(da9052_wdt_maps))
0080             ret = -EINVAL;
0081         else
0082             ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
0083                         DA9052_CONTROLD_TWDSCALE,
0084                         da9052_wdt_maps[i].reg_val);
0085         if (ret < 0) {
0086             dev_err(da9052->dev,
0087                 "Failed to update timescale bit, %d\n", ret);
0088             return ret;
0089         }
0090 
0091         wdt_dev->timeout = timeout;
0092         driver_data->jpast = jiffies;
0093     }
0094 
0095     return 0;
0096 }
0097 
0098 static int da9052_wdt_start(struct watchdog_device *wdt_dev)
0099 {
0100     return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
0101 }
0102 
0103 static int da9052_wdt_stop(struct watchdog_device *wdt_dev)
0104 {
0105     return da9052_wdt_set_timeout(wdt_dev, 0);
0106 }
0107 
0108 static int da9052_wdt_ping(struct watchdog_device *wdt_dev)
0109 {
0110     struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
0111     struct da9052 *da9052 = driver_data->da9052;
0112     unsigned long msec, jnow = jiffies;
0113     int ret;
0114 
0115     /*
0116      * We have a minimum time for watchdog window called TWDMIN. A write
0117      * to the watchdog before this elapsed time should cause an error.
0118      */
0119     msec = (jnow - driver_data->jpast) * 1000/HZ;
0120     if (msec < DA9052_TWDMIN)
0121         mdelay(msec);
0122 
0123     /* Reset the watchdog timer */
0124     ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
0125                 DA9052_CONTROLD_WATCHDOG, 1 << 7);
0126     if (ret < 0)
0127         return ret;
0128 
0129     /*
0130      * FIXME: Reset the watchdog core, in general PMIC
0131      * is supposed to do this
0132      */
0133     return da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
0134                  DA9052_CONTROLD_WATCHDOG, 0 << 7);
0135 }
0136 
0137 static const struct watchdog_info da9052_wdt_info = {
0138     .options    = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
0139     .identity   = "DA9052 Watchdog",
0140 };
0141 
0142 static const struct watchdog_ops da9052_wdt_ops = {
0143     .owner = THIS_MODULE,
0144     .start = da9052_wdt_start,
0145     .stop = da9052_wdt_stop,
0146     .ping = da9052_wdt_ping,
0147     .set_timeout = da9052_wdt_set_timeout,
0148 };
0149 
0150 
0151 static int da9052_wdt_probe(struct platform_device *pdev)
0152 {
0153     struct device *dev = &pdev->dev;
0154     struct da9052 *da9052 = dev_get_drvdata(dev->parent);
0155     struct da9052_wdt_data *driver_data;
0156     struct watchdog_device *da9052_wdt;
0157     int ret;
0158 
0159     driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL);
0160     if (!driver_data)
0161         return -ENOMEM;
0162     driver_data->da9052 = da9052;
0163 
0164     da9052_wdt = &driver_data->wdt;
0165 
0166     da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
0167     da9052_wdt->info = &da9052_wdt_info;
0168     da9052_wdt->ops = &da9052_wdt_ops;
0169     da9052_wdt->parent = dev;
0170     watchdog_set_drvdata(da9052_wdt, driver_data);
0171 
0172     ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
0173                 DA9052_CONTROLD_TWDSCALE, 0);
0174     if (ret < 0) {
0175         dev_err(dev, "Failed to disable watchdog bits, %d\n", ret);
0176         return ret;
0177     }
0178 
0179     return devm_watchdog_register_device(dev, &driver_data->wdt);
0180 }
0181 
0182 static struct platform_driver da9052_wdt_driver = {
0183     .probe = da9052_wdt_probe,
0184     .driver = {
0185         .name   = "da9052-watchdog",
0186     },
0187 };
0188 
0189 module_platform_driver(da9052_wdt_driver);
0190 
0191 MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
0192 MODULE_DESCRIPTION("DA9052 SM Device Driver");
0193 MODULE_LICENSE("GPL");
0194 MODULE_ALIAS("platform:da9052-watchdog");