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

wm831x_wdt.c (6333B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Watchdog driver for the wm831x PMICs
      4 *
      5 * Copyright (C) 2009 Wolfson Microelectronics
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/moduleparam.h>
     10#include <linux/types.h>
     11#include <linux/kernel.h>
     12#include <linux/slab.h>
     13#include <linux/platform_device.h>
     14#include <linux/watchdog.h>
     15#include <linux/uaccess.h>
     16
     17#include <linux/mfd/wm831x/core.h>
     18#include <linux/mfd/wm831x/pdata.h>
     19#include <linux/mfd/wm831x/watchdog.h>
     20
     21static bool nowayout = WATCHDOG_NOWAYOUT;
     22module_param(nowayout, bool, 0);
     23MODULE_PARM_DESC(nowayout,
     24		 "Watchdog cannot be stopped once started (default="
     25		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     26
     27struct wm831x_wdt_drvdata {
     28	struct watchdog_device wdt;
     29	struct wm831x *wm831x;
     30	struct mutex lock;
     31	int update_state;
     32};
     33
     34/* We can't use the sub-second values here but they're included
     35 * for completeness.  */
     36static struct {
     37	unsigned int time;  /* Seconds */
     38	u16 val;            /* WDOG_TO value */
     39} wm831x_wdt_cfgs[] = {
     40	{  1, 2 },
     41	{  2, 3 },
     42	{  4, 4 },
     43	{  8, 5 },
     44	{ 16, 6 },
     45	{ 32, 7 },
     46	{ 33, 7 },  /* Actually 32.768s so include both, others round down */
     47};
     48
     49static int wm831x_wdt_start(struct watchdog_device *wdt_dev)
     50{
     51	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
     52	struct wm831x *wm831x = driver_data->wm831x;
     53	int ret;
     54
     55	mutex_lock(&driver_data->lock);
     56
     57	ret = wm831x_reg_unlock(wm831x);
     58	if (ret == 0) {
     59		ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
     60				      WM831X_WDOG_ENA, WM831X_WDOG_ENA);
     61		wm831x_reg_lock(wm831x);
     62	} else {
     63		dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
     64			ret);
     65	}
     66
     67	mutex_unlock(&driver_data->lock);
     68
     69	return ret;
     70}
     71
     72static int wm831x_wdt_stop(struct watchdog_device *wdt_dev)
     73{
     74	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
     75	struct wm831x *wm831x = driver_data->wm831x;
     76	int ret;
     77
     78	mutex_lock(&driver_data->lock);
     79
     80	ret = wm831x_reg_unlock(wm831x);
     81	if (ret == 0) {
     82		ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
     83				      WM831X_WDOG_ENA, 0);
     84		wm831x_reg_lock(wm831x);
     85	} else {
     86		dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
     87			ret);
     88	}
     89
     90	mutex_unlock(&driver_data->lock);
     91
     92	return ret;
     93}
     94
     95static int wm831x_wdt_ping(struct watchdog_device *wdt_dev)
     96{
     97	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
     98	struct wm831x *wm831x = driver_data->wm831x;
     99	int ret;
    100	u16 reg;
    101
    102	mutex_lock(&driver_data->lock);
    103
    104	reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
    105
    106	if (!(reg & WM831X_WDOG_RST_SRC)) {
    107		dev_err(wm831x->dev, "Hardware watchdog update unsupported\n");
    108		ret = -EINVAL;
    109		goto out;
    110	}
    111
    112	reg |= WM831X_WDOG_RESET;
    113
    114	ret = wm831x_reg_unlock(wm831x);
    115	if (ret == 0) {
    116		ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
    117		wm831x_reg_lock(wm831x);
    118	} else {
    119		dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
    120			ret);
    121	}
    122
    123out:
    124	mutex_unlock(&driver_data->lock);
    125
    126	return ret;
    127}
    128
    129static int wm831x_wdt_set_timeout(struct watchdog_device *wdt_dev,
    130				  unsigned int timeout)
    131{
    132	struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
    133	struct wm831x *wm831x = driver_data->wm831x;
    134	int ret, i;
    135
    136	for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
    137		if (wm831x_wdt_cfgs[i].time == timeout)
    138			break;
    139	if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
    140		return -EINVAL;
    141
    142	ret = wm831x_reg_unlock(wm831x);
    143	if (ret == 0) {
    144		ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
    145				      WM831X_WDOG_TO_MASK,
    146				      wm831x_wdt_cfgs[i].val);
    147		wm831x_reg_lock(wm831x);
    148	} else {
    149		dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
    150			ret);
    151	}
    152
    153	wdt_dev->timeout = timeout;
    154
    155	return ret;
    156}
    157
    158static const struct watchdog_info wm831x_wdt_info = {
    159	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
    160	.identity = "WM831x Watchdog",
    161};
    162
    163static const struct watchdog_ops wm831x_wdt_ops = {
    164	.owner = THIS_MODULE,
    165	.start = wm831x_wdt_start,
    166	.stop = wm831x_wdt_stop,
    167	.ping = wm831x_wdt_ping,
    168	.set_timeout = wm831x_wdt_set_timeout,
    169};
    170
    171static int wm831x_wdt_probe(struct platform_device *pdev)
    172{
    173	struct device *dev = &pdev->dev;
    174	struct wm831x *wm831x = dev_get_drvdata(dev->parent);
    175	struct wm831x_pdata *chip_pdata = dev_get_platdata(dev->parent);
    176	struct wm831x_watchdog_pdata *pdata;
    177	struct wm831x_wdt_drvdata *driver_data;
    178	struct watchdog_device *wm831x_wdt;
    179	int reg, ret, i;
    180
    181	ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
    182	if (ret < 0) {
    183		dev_err(wm831x->dev, "Failed to read watchdog status: %d\n",
    184			ret);
    185		return ret;
    186	}
    187	reg = ret;
    188
    189	if (reg & WM831X_WDOG_DEBUG)
    190		dev_warn(wm831x->dev, "Watchdog is paused\n");
    191
    192	driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL);
    193	if (!driver_data)
    194		return -ENOMEM;
    195
    196	mutex_init(&driver_data->lock);
    197	driver_data->wm831x = wm831x;
    198
    199	wm831x_wdt = &driver_data->wdt;
    200
    201	wm831x_wdt->info = &wm831x_wdt_info;
    202	wm831x_wdt->ops = &wm831x_wdt_ops;
    203	wm831x_wdt->parent = dev;
    204	watchdog_set_nowayout(wm831x_wdt, nowayout);
    205	watchdog_set_drvdata(wm831x_wdt, driver_data);
    206
    207	reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
    208	reg &= WM831X_WDOG_TO_MASK;
    209	for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
    210		if (wm831x_wdt_cfgs[i].val == reg)
    211			break;
    212	if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
    213		dev_warn(wm831x->dev,
    214			 "Unknown watchdog timeout: %x\n", reg);
    215	else
    216		wm831x_wdt->timeout = wm831x_wdt_cfgs[i].time;
    217
    218	/* Apply any configuration */
    219	if (chip_pdata)
    220		pdata = chip_pdata->watchdog;
    221	else
    222		pdata = NULL;
    223
    224	if (pdata) {
    225		reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK |
    226			 WM831X_WDOG_RST_SRC);
    227
    228		reg |= pdata->primary << WM831X_WDOG_PRIMACT_SHIFT;
    229		reg |= pdata->secondary << WM831X_WDOG_SECACT_SHIFT;
    230		reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT;
    231
    232		ret = wm831x_reg_unlock(wm831x);
    233		if (ret == 0) {
    234			ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
    235			wm831x_reg_lock(wm831x);
    236		} else {
    237			dev_err(wm831x->dev,
    238				"Failed to unlock security key: %d\n", ret);
    239			return ret;
    240		}
    241	}
    242
    243	return devm_watchdog_register_device(dev, &driver_data->wdt);
    244}
    245
    246static struct platform_driver wm831x_wdt_driver = {
    247	.probe = wm831x_wdt_probe,
    248	.driver = {
    249		.name = "wm831x-watchdog",
    250	},
    251};
    252
    253module_platform_driver(wm831x_wdt_driver);
    254
    255MODULE_AUTHOR("Mark Brown");
    256MODULE_DESCRIPTION("WM831x Watchdog");
    257MODULE_LICENSE("GPL");
    258MODULE_ALIAS("platform:wm831x-watchdog");