cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

da9063_wdt.c (7629B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Watchdog driver for DA9063 PMICs.
      4 *
      5 * Copyright(c) 2012 Dialog Semiconductor Ltd.
      6 *
      7 * Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>
      8 *
      9 */
     10
     11#include <linux/kernel.h>
     12#include <linux/module.h>
     13#include <linux/watchdog.h>
     14#include <linux/platform_device.h>
     15#include <linux/uaccess.h>
     16#include <linux/slab.h>
     17#include <linux/i2c.h>
     18#include <linux/delay.h>
     19#include <linux/mfd/da9063/registers.h>
     20#include <linux/mfd/da9063/core.h>
     21#include <linux/property.h>
     22#include <linux/regmap.h>
     23
     24/*
     25 * Watchdog selector to timeout in seconds.
     26 *   0: WDT disabled;
     27 *   others: timeout = 2048 ms * 2^(TWDSCALE-1).
     28 */
     29static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
     30static bool use_sw_pm;
     31
     32#define DA9063_TWDSCALE_DISABLE		0
     33#define DA9063_TWDSCALE_MIN		1
     34#define DA9063_TWDSCALE_MAX		(ARRAY_SIZE(wdt_timeout) - 1)
     35#define DA9063_WDT_MIN_TIMEOUT		wdt_timeout[DA9063_TWDSCALE_MIN]
     36#define DA9063_WDT_MAX_TIMEOUT		wdt_timeout[DA9063_TWDSCALE_MAX]
     37#define DA9063_WDG_TIMEOUT		wdt_timeout[3]
     38#define DA9063_RESET_PROTECTION_MS	256
     39
     40static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs)
     41{
     42	unsigned int i;
     43
     44	for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) {
     45		if (wdt_timeout[i] >= secs)
     46			return i;
     47	}
     48
     49	return DA9063_TWDSCALE_MAX;
     50}
     51
     52/*
     53 * Read the currently active timeout.
     54 * Zero means the watchdog is disabled.
     55 */
     56static unsigned int da9063_wdt_read_timeout(struct da9063 *da9063)
     57{
     58	unsigned int val;
     59
     60	regmap_read(da9063->regmap, DA9063_REG_CONTROL_D, &val);
     61
     62	return wdt_timeout[val & DA9063_TWDSCALE_MASK];
     63}
     64
     65static int da9063_wdt_disable_timer(struct da9063 *da9063)
     66{
     67	return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
     68				  DA9063_TWDSCALE_MASK,
     69				  DA9063_TWDSCALE_DISABLE);
     70}
     71
     72static int
     73da9063_wdt_update_timeout(struct da9063 *da9063, unsigned int timeout)
     74{
     75	unsigned int regval;
     76	int ret;
     77
     78	/*
     79	 * The watchdog triggers a reboot if a timeout value is already
     80	 * programmed because the timeout value combines two functions
     81	 * in one: indicating the counter limit and starting the watchdog.
     82	 * The watchdog must be disabled to be able to change the timeout
     83	 * value if the watchdog is already running. Then we can set the
     84	 * new timeout value which enables the watchdog again.
     85	 */
     86	ret = da9063_wdt_disable_timer(da9063);
     87	if (ret)
     88		return ret;
     89
     90	usleep_range(150, 300);
     91	regval = da9063_wdt_timeout_to_sel(timeout);
     92
     93	return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
     94				  DA9063_TWDSCALE_MASK, regval);
     95}
     96
     97static int da9063_wdt_start(struct watchdog_device *wdd)
     98{
     99	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
    100	int ret;
    101
    102	ret = da9063_wdt_update_timeout(da9063, wdd->timeout);
    103	if (ret)
    104		dev_err(da9063->dev, "Watchdog failed to start (err = %d)\n",
    105			ret);
    106
    107	return ret;
    108}
    109
    110static int da9063_wdt_stop(struct watchdog_device *wdd)
    111{
    112	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
    113	int ret;
    114
    115	ret = da9063_wdt_disable_timer(da9063);
    116	if (ret)
    117		dev_alert(da9063->dev, "Watchdog failed to stop (err = %d)\n",
    118			  ret);
    119
    120	return ret;
    121}
    122
    123static int da9063_wdt_ping(struct watchdog_device *wdd)
    124{
    125	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
    126	int ret;
    127
    128	/*
    129	 * Prevent pings from occurring late in system poweroff/reboot sequence
    130	 * and possibly locking out restart handler from accessing i2c bus.
    131	 */
    132	if (system_state > SYSTEM_RUNNING)
    133		return 0;
    134
    135	ret = regmap_write(da9063->regmap, DA9063_REG_CONTROL_F,
    136			   DA9063_WATCHDOG);
    137	if (ret)
    138		dev_alert(da9063->dev, "Failed to ping the watchdog (err = %d)\n",
    139			  ret);
    140
    141	return ret;
    142}
    143
    144static int da9063_wdt_set_timeout(struct watchdog_device *wdd,
    145				  unsigned int timeout)
    146{
    147	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
    148	int ret = 0;
    149
    150	/*
    151	 * There are two cases when a set_timeout() will be called:
    152	 * 1. The watchdog is off and someone wants to set the timeout for the
    153	 *    further use.
    154	 * 2. The watchdog is already running and a new timeout value should be
    155	 *    set.
    156	 *
    157	 * The watchdog can't store a timeout value not equal zero without
    158	 * enabling the watchdog, so the timeout must be buffered by the driver.
    159	 */
    160	if (watchdog_active(wdd))
    161		ret = da9063_wdt_update_timeout(da9063, timeout);
    162
    163	if (ret)
    164		dev_err(da9063->dev, "Failed to set watchdog timeout (err = %d)\n",
    165			ret);
    166	else
    167		wdd->timeout = wdt_timeout[da9063_wdt_timeout_to_sel(timeout)];
    168
    169	return ret;
    170}
    171
    172static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action,
    173			      void *data)
    174{
    175	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
    176	struct i2c_client *client = to_i2c_client(da9063->dev);
    177	int ret;
    178
    179	/* Don't use regmap because it is not atomic safe */
    180	ret = i2c_smbus_write_byte_data(client, DA9063_REG_CONTROL_F,
    181					DA9063_SHUTDOWN);
    182	if (ret < 0)
    183		dev_alert(da9063->dev, "Failed to shutdown (err = %d)\n",
    184			  ret);
    185
    186	/* wait for reset to assert... */
    187	mdelay(500);
    188
    189	return ret;
    190}
    191
    192static const struct watchdog_info da9063_watchdog_info = {
    193	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
    194	.identity = "DA9063 Watchdog",
    195};
    196
    197static const struct watchdog_ops da9063_watchdog_ops = {
    198	.owner = THIS_MODULE,
    199	.start = da9063_wdt_start,
    200	.stop = da9063_wdt_stop,
    201	.ping = da9063_wdt_ping,
    202	.set_timeout = da9063_wdt_set_timeout,
    203	.restart = da9063_wdt_restart,
    204};
    205
    206static int da9063_wdt_probe(struct platform_device *pdev)
    207{
    208	struct device *dev = &pdev->dev;
    209	struct da9063 *da9063;
    210	struct watchdog_device *wdd;
    211	unsigned int timeout;
    212
    213	if (!dev->parent)
    214		return -EINVAL;
    215
    216	da9063 = dev_get_drvdata(dev->parent);
    217	if (!da9063)
    218		return -EINVAL;
    219
    220	wdd = devm_kzalloc(dev, sizeof(*wdd), GFP_KERNEL);
    221	if (!wdd)
    222		return -ENOMEM;
    223
    224	use_sw_pm = device_property_present(dev, "dlg,use-sw-pm");
    225
    226	wdd->info = &da9063_watchdog_info;
    227	wdd->ops = &da9063_watchdog_ops;
    228	wdd->min_timeout = DA9063_WDT_MIN_TIMEOUT;
    229	wdd->max_timeout = DA9063_WDT_MAX_TIMEOUT;
    230	wdd->min_hw_heartbeat_ms = DA9063_RESET_PROTECTION_MS;
    231	wdd->parent = dev;
    232	wdd->status = WATCHDOG_NOWAYOUT_INIT_STATUS;
    233
    234	watchdog_set_restart_priority(wdd, 128);
    235	watchdog_set_drvdata(wdd, da9063);
    236	dev_set_drvdata(dev, wdd);
    237
    238	wdd->timeout = DA9063_WDG_TIMEOUT;
    239
    240	/* Use pre-configured timeout if watchdog is already running. */
    241	timeout = da9063_wdt_read_timeout(da9063);
    242	if (timeout)
    243		wdd->timeout = timeout;
    244
    245	/* Set timeout, maybe override it with DT value, scale it */
    246	watchdog_init_timeout(wdd, 0, dev);
    247	da9063_wdt_set_timeout(wdd, wdd->timeout);
    248
    249	/* Update timeout if the watchdog is already running. */
    250	if (timeout) {
    251		da9063_wdt_update_timeout(da9063, wdd->timeout);
    252		set_bit(WDOG_HW_RUNNING, &wdd->status);
    253	}
    254
    255	return devm_watchdog_register_device(dev, wdd);
    256}
    257
    258static int __maybe_unused da9063_wdt_suspend(struct device *dev)
    259{
    260	struct watchdog_device *wdd = dev_get_drvdata(dev);
    261
    262	if (!use_sw_pm)
    263		return 0;
    264
    265	if (watchdog_active(wdd))
    266		return da9063_wdt_stop(wdd);
    267
    268	return 0;
    269}
    270
    271static int __maybe_unused da9063_wdt_resume(struct device *dev)
    272{
    273	struct watchdog_device *wdd = dev_get_drvdata(dev);
    274
    275	if (!use_sw_pm)
    276		return 0;
    277
    278	if (watchdog_active(wdd))
    279		return da9063_wdt_start(wdd);
    280
    281	return 0;
    282}
    283
    284static SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops,
    285			da9063_wdt_suspend, da9063_wdt_resume);
    286
    287static struct platform_driver da9063_wdt_driver = {
    288	.probe = da9063_wdt_probe,
    289	.driver = {
    290		.name = DA9063_DRVNAME_WATCHDOG,
    291		.pm = &da9063_wdt_pm_ops,
    292	},
    293};
    294module_platform_driver(da9063_wdt_driver);
    295
    296MODULE_AUTHOR("Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>");
    297MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063");
    298MODULE_LICENSE("GPL");
    299MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG);