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

mlx_wdt.c (9175B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Mellanox watchdog driver
      4 *
      5 * Copyright (C) 2019 Mellanox Technologies
      6 * Copyright (C) 2019 Michael Shych <mshych@mellanox.com>
      7 */
      8
      9#include <linux/bitops.h>
     10#include <linux/device.h>
     11#include <linux/errno.h>
     12#include <linux/log2.h>
     13#include <linux/module.h>
     14#include <linux/platform_data/mlxreg.h>
     15#include <linux/platform_device.h>
     16#include <linux/regmap.h>
     17#include <linux/spinlock.h>
     18#include <linux/types.h>
     19#include <linux/watchdog.h>
     20
     21#define MLXREG_WDT_CLOCK_SCALE		1000
     22#define MLXREG_WDT_MAX_TIMEOUT_TYPE1	32
     23#define MLXREG_WDT_MAX_TIMEOUT_TYPE2	255
     24#define MLXREG_WDT_MAX_TIMEOUT_TYPE3	65535
     25#define MLXREG_WDT_MIN_TIMEOUT		1
     26#define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \
     27				 WDIOF_SETTIMEOUT)
     28
     29/**
     30 * struct mlxreg_wdt - wd private data:
     31 *
     32 * @wdd:	watchdog device;
     33 * @device:	basic device;
     34 * @pdata:	data received from platform driver;
     35 * @regmap:	register map of parent device;
     36 * @timeout:	defined timeout in sec.;
     37 * @action_idx:	index for direct access to action register;
     38 * @timeout_idx:index for direct access to TO register;
     39 * @tleft_idx:	index for direct access to time left register;
     40 * @ping_idx:	index for direct access to ping register;
     41 * @reset_idx:	index for direct access to reset cause register;
     42 * @wd_type:	watchdog HW type;
     43 */
     44struct mlxreg_wdt {
     45	struct watchdog_device wdd;
     46	struct mlxreg_core_platform_data *pdata;
     47	void *regmap;
     48	int action_idx;
     49	int timeout_idx;
     50	int tleft_idx;
     51	int ping_idx;
     52	int reset_idx;
     53	int regmap_val_sz;
     54	enum mlxreg_wdt_type wdt_type;
     55};
     56
     57static void mlxreg_wdt_check_card_reset(struct mlxreg_wdt *wdt)
     58{
     59	struct mlxreg_core_data *reg_data;
     60	u32 regval;
     61	int rc;
     62
     63	if (wdt->reset_idx == -EINVAL)
     64		return;
     65
     66	if (!(wdt->wdd.info->options & WDIOF_CARDRESET))
     67		return;
     68
     69	reg_data = &wdt->pdata->data[wdt->reset_idx];
     70	rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
     71	if (!rc) {
     72		if (regval & ~reg_data->mask) {
     73			wdt->wdd.bootstatus = WDIOF_CARDRESET;
     74			dev_info(wdt->wdd.parent,
     75				 "watchdog previously reset the CPU\n");
     76		}
     77	}
     78}
     79
     80static int mlxreg_wdt_start(struct watchdog_device *wdd)
     81{
     82	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
     83	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
     84
     85	return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
     86				  BIT(reg_data->bit));
     87}
     88
     89static int mlxreg_wdt_stop(struct watchdog_device *wdd)
     90{
     91	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
     92	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
     93
     94	return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
     95				  ~BIT(reg_data->bit));
     96}
     97
     98static int mlxreg_wdt_ping(struct watchdog_device *wdd)
     99{
    100	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
    101	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx];
    102
    103	return regmap_write_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
    104				 BIT(reg_data->bit));
    105}
    106
    107static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
    108				  unsigned int timeout)
    109{
    110	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
    111	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx];
    112	u32 regval, set_time, hw_timeout;
    113	int rc;
    114
    115	switch (wdt->wdt_type) {
    116	case MLX_WDT_TYPE1:
    117		rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
    118		if (rc)
    119			return rc;
    120
    121		hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE);
    122		regval = (regval & reg_data->mask) | hw_timeout;
    123		/* Rowndown to actual closest number of sec. */
    124		set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE;
    125		rc = regmap_write(wdt->regmap, reg_data->reg, regval);
    126		break;
    127	case MLX_WDT_TYPE2:
    128		set_time = timeout;
    129		rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
    130		break;
    131	case MLX_WDT_TYPE3:
    132		/* WD_TYPE3 has 2B set time register */
    133		set_time = timeout;
    134		if (wdt->regmap_val_sz == 1) {
    135			regval = timeout & 0xff;
    136			rc = regmap_write(wdt->regmap, reg_data->reg, regval);
    137			if (!rc) {
    138				regval = (timeout & 0xff00) >> 8;
    139				rc = regmap_write(wdt->regmap,
    140						reg_data->reg + 1, regval);
    141			}
    142		} else {
    143			rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
    144		}
    145		break;
    146	default:
    147		return -EINVAL;
    148	}
    149
    150	wdd->timeout = set_time;
    151	if (!rc) {
    152		/*
    153		 * Restart watchdog with new timeout period
    154		 * if watchdog is already started.
    155		 */
    156		if (watchdog_active(wdd)) {
    157			rc = mlxreg_wdt_stop(wdd);
    158			if (!rc)
    159				rc = mlxreg_wdt_start(wdd);
    160		}
    161	}
    162
    163	return rc;
    164}
    165
    166static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd)
    167{
    168	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
    169	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
    170	u32 regval, msb, lsb;
    171	int rc;
    172
    173	if (wdt->wdt_type == MLX_WDT_TYPE2) {
    174		rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
    175	} else {
    176		/* WD_TYPE3 has 2 byte timeleft register */
    177		if (wdt->regmap_val_sz == 1) {
    178			rc = regmap_read(wdt->regmap, reg_data->reg, &lsb);
    179			if (!rc) {
    180				rc = regmap_read(wdt->regmap,
    181						reg_data->reg + 1, &msb);
    182				regval = (msb & 0xff) << 8 | (lsb & 0xff);
    183			}
    184		} else {
    185			rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
    186		}
    187	}
    188
    189	/* Return 0 timeleft in case of failure register read. */
    190	return rc == 0 ? regval : 0;
    191}
    192
    193static const struct watchdog_ops mlxreg_wdt_ops_type1 = {
    194	.start		= mlxreg_wdt_start,
    195	.stop		= mlxreg_wdt_stop,
    196	.ping		= mlxreg_wdt_ping,
    197	.set_timeout	= mlxreg_wdt_set_timeout,
    198	.owner		= THIS_MODULE,
    199};
    200
    201static const struct watchdog_ops mlxreg_wdt_ops_type2 = {
    202	.start		= mlxreg_wdt_start,
    203	.stop		= mlxreg_wdt_stop,
    204	.ping		= mlxreg_wdt_ping,
    205	.set_timeout	= mlxreg_wdt_set_timeout,
    206	.get_timeleft	= mlxreg_wdt_get_timeleft,
    207	.owner		= THIS_MODULE,
    208};
    209
    210static const struct watchdog_info mlxreg_wdt_main_info = {
    211	.options	= MLXREG_WDT_OPTIONS_BASE
    212			| WDIOF_CARDRESET,
    213	.identity	= "mlx-wdt-main",
    214};
    215
    216static const struct watchdog_info mlxreg_wdt_aux_info = {
    217	.options	= MLXREG_WDT_OPTIONS_BASE
    218			| WDIOF_ALARMONLY,
    219	.identity	= "mlx-wdt-aux",
    220};
    221
    222static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
    223			      struct mlxreg_core_platform_data *pdata)
    224{
    225	struct mlxreg_core_data *data = pdata->data;
    226	int i;
    227
    228	wdt->reset_idx = -EINVAL;
    229	for (i = 0; i < pdata->counter; i++, data++) {
    230		if (strnstr(data->label, "action", sizeof(data->label)))
    231			wdt->action_idx = i;
    232		else if (strnstr(data->label, "timeout", sizeof(data->label)))
    233			wdt->timeout_idx = i;
    234		else if (strnstr(data->label, "timeleft", sizeof(data->label)))
    235			wdt->tleft_idx = i;
    236		else if (strnstr(data->label, "ping", sizeof(data->label)))
    237			wdt->ping_idx = i;
    238		else if (strnstr(data->label, "reset", sizeof(data->label)))
    239			wdt->reset_idx = i;
    240	}
    241
    242	wdt->pdata = pdata;
    243	if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity,
    244		    sizeof(mlxreg_wdt_main_info.identity)))
    245		wdt->wdd.info = &mlxreg_wdt_main_info;
    246	else
    247		wdt->wdd.info = &mlxreg_wdt_aux_info;
    248
    249	wdt->wdt_type = pdata->version;
    250	switch (wdt->wdt_type) {
    251	case MLX_WDT_TYPE1:
    252		wdt->wdd.ops = &mlxreg_wdt_ops_type1;
    253		wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1;
    254		break;
    255	case MLX_WDT_TYPE2:
    256		wdt->wdd.ops = &mlxreg_wdt_ops_type2;
    257		wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
    258		break;
    259	case MLX_WDT_TYPE3:
    260		wdt->wdd.ops = &mlxreg_wdt_ops_type2;
    261		wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE3;
    262		break;
    263	default:
    264		break;
    265	}
    266
    267	wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
    268}
    269
    270static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt,
    271				   struct mlxreg_core_platform_data *pdata)
    272{
    273	u32 timeout;
    274
    275	timeout = pdata->data[wdt->timeout_idx].health_cntr;
    276	return mlxreg_wdt_set_timeout(&wdt->wdd, timeout);
    277}
    278
    279static int mlxreg_wdt_probe(struct platform_device *pdev)
    280{
    281	struct device *dev = &pdev->dev;
    282	struct mlxreg_core_platform_data *pdata;
    283	struct mlxreg_wdt *wdt;
    284	int rc;
    285
    286	pdata = dev_get_platdata(dev);
    287	if (!pdata) {
    288		dev_err(dev, "Failed to get platform data.\n");
    289		return -EINVAL;
    290	}
    291	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
    292	if (!wdt)
    293		return -ENOMEM;
    294
    295	wdt->wdd.parent = dev;
    296	wdt->regmap = pdata->regmap;
    297	rc = regmap_get_val_bytes(wdt->regmap);
    298	if (rc < 0)
    299		return -EINVAL;
    300
    301	wdt->regmap_val_sz = rc;
    302	mlxreg_wdt_config(wdt, pdata);
    303
    304	if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
    305		watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
    306	watchdog_stop_on_reboot(&wdt->wdd);
    307	watchdog_stop_on_unregister(&wdt->wdd);
    308	watchdog_set_drvdata(&wdt->wdd, wdt);
    309	rc = mlxreg_wdt_init_timeout(wdt, pdata);
    310	if (rc)
    311		goto register_error;
    312
    313	if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) {
    314		rc = mlxreg_wdt_start(&wdt->wdd);
    315		if (rc)
    316			goto register_error;
    317		set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
    318	}
    319	mlxreg_wdt_check_card_reset(wdt);
    320	rc = devm_watchdog_register_device(dev, &wdt->wdd);
    321
    322register_error:
    323	if (rc)
    324		dev_err(dev, "Cannot register watchdog device (err=%d)\n", rc);
    325	return rc;
    326}
    327
    328static struct platform_driver mlxreg_wdt_driver = {
    329	.probe	= mlxreg_wdt_probe,
    330	.driver	= {
    331			.name = "mlx-wdt",
    332	},
    333};
    334
    335module_platform_driver(mlxreg_wdt_driver);
    336
    337MODULE_AUTHOR("Michael Shych <michaelsh@mellanox.com>");
    338MODULE_DESCRIPTION("Mellanox watchdog driver");
    339MODULE_LICENSE("GPL");
    340MODULE_ALIAS("platform:mlx-wdt");