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

armada_37xx_wdt.c (10208B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Watchdog driver for Marvell Armada 37xx SoCs
      4 *
      5 * Author: Marek BehĂșn <kabel@kernel.org>
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/err.h>
     10#include <linux/interrupt.h>
     11#include <linux/io.h>
     12#include <linux/kernel.h>
     13#include <linux/mfd/syscon.h>
     14#include <linux/module.h>
     15#include <linux/moduleparam.h>
     16#include <linux/of.h>
     17#include <linux/of_device.h>
     18#include <linux/platform_device.h>
     19#include <linux/regmap.h>
     20#include <linux/types.h>
     21#include <linux/watchdog.h>
     22
     23/*
     24 * There are four counters that can be used for watchdog on Armada 37xx.
     25 * The addresses for counter control registers are register base plus ID*0x10,
     26 * where ID is 0, 1, 2 or 3.
     27 *
     28 * In this driver we use IDs 0 and 1. Counter ID 1 is used as watchdog counter,
     29 * while counter ID 0 is used to implement pinging the watchdog: counter ID 1 is
     30 * set to restart counting from initial value on counter ID 0 end count event.
     31 * Pinging is done by forcing immediate end count event on counter ID 0.
     32 * If only one counter was used, pinging would have to be implemented by
     33 * disabling and enabling the counter, leaving the system in a vulnerable state
     34 * for a (really) short period of time.
     35 *
     36 * Counters ID 2 and 3 are enabled by default even before U-Boot loads,
     37 * therefore this driver does not provide a way to use them, eg. by setting a
     38 * property in device tree.
     39 */
     40
     41#define CNTR_ID_RETRIGGER		0
     42#define CNTR_ID_WDOG			1
     43
     44/* relative to cpu_misc */
     45#define WDT_TIMER_SELECT		0x64
     46#define WDT_TIMER_SELECT_MASK		0xf
     47#define WDT_TIMER_SELECT_VAL		BIT(CNTR_ID_WDOG)
     48
     49/* relative to reg */
     50#define CNTR_CTRL(id)			((id) * 0x10)
     51#define CNTR_CTRL_ENABLE		0x0001
     52#define CNTR_CTRL_ACTIVE		0x0002
     53#define CNTR_CTRL_MODE_MASK		0x000c
     54#define CNTR_CTRL_MODE_ONESHOT		0x0000
     55#define CNTR_CTRL_MODE_HWSIG		0x000c
     56#define CNTR_CTRL_TRIG_SRC_MASK		0x00f0
     57#define CNTR_CTRL_TRIG_SRC_PREV_CNTR	0x0050
     58#define CNTR_CTRL_PRESCALE_MASK		0xff00
     59#define CNTR_CTRL_PRESCALE_MIN		2
     60#define CNTR_CTRL_PRESCALE_SHIFT	8
     61
     62#define CNTR_COUNT_LOW(id)		(CNTR_CTRL(id) + 0x4)
     63#define CNTR_COUNT_HIGH(id)		(CNTR_CTRL(id) + 0x8)
     64
     65#define WATCHDOG_TIMEOUT		120
     66
     67static unsigned int timeout;
     68module_param(timeout, int, 0);
     69MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
     70
     71static bool nowayout = WATCHDOG_NOWAYOUT;
     72module_param(nowayout, bool, 0);
     73MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
     74			   __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     75
     76struct armada_37xx_watchdog {
     77	struct watchdog_device wdt;
     78	struct regmap *cpu_misc;
     79	void __iomem *reg;
     80	u64 timeout; /* in clock ticks */
     81	unsigned long clk_rate;
     82	struct clk *clk;
     83};
     84
     85static u64 get_counter_value(struct armada_37xx_watchdog *dev, int id)
     86{
     87	u64 val;
     88
     89	/*
     90	 * when low is read, high is latched into flip-flops so that it can be
     91	 * read consistently without using software debouncing
     92	 */
     93	val = readl(dev->reg + CNTR_COUNT_LOW(id));
     94	val |= ((u64)readl(dev->reg + CNTR_COUNT_HIGH(id))) << 32;
     95
     96	return val;
     97}
     98
     99static void set_counter_value(struct armada_37xx_watchdog *dev, int id, u64 val)
    100{
    101	writel(val & 0xffffffff, dev->reg + CNTR_COUNT_LOW(id));
    102	writel(val >> 32, dev->reg + CNTR_COUNT_HIGH(id));
    103}
    104
    105static void counter_enable(struct armada_37xx_watchdog *dev, int id)
    106{
    107	u32 reg;
    108
    109	reg = readl(dev->reg + CNTR_CTRL(id));
    110	reg |= CNTR_CTRL_ENABLE;
    111	writel(reg, dev->reg + CNTR_CTRL(id));
    112}
    113
    114static void counter_disable(struct armada_37xx_watchdog *dev, int id)
    115{
    116	u32 reg;
    117
    118	reg = readl(dev->reg + CNTR_CTRL(id));
    119	reg &= ~CNTR_CTRL_ENABLE;
    120	writel(reg, dev->reg + CNTR_CTRL(id));
    121}
    122
    123static void init_counter(struct armada_37xx_watchdog *dev, int id, u32 mode,
    124			 u32 trig_src)
    125{
    126	u32 reg;
    127
    128	reg = readl(dev->reg + CNTR_CTRL(id));
    129
    130	reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK |
    131		 CNTR_CTRL_TRIG_SRC_MASK);
    132
    133	/* set mode */
    134	reg |= mode & CNTR_CTRL_MODE_MASK;
    135
    136	/* set prescaler to the min value */
    137	reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT;
    138
    139	/* set trigger source */
    140	reg |= trig_src & CNTR_CTRL_TRIG_SRC_MASK;
    141
    142	writel(reg, dev->reg + CNTR_CTRL(id));
    143}
    144
    145static int armada_37xx_wdt_ping(struct watchdog_device *wdt)
    146{
    147	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
    148
    149	/* counter 1 is retriggered by forcing end count on counter 0 */
    150	counter_disable(dev, CNTR_ID_RETRIGGER);
    151	counter_enable(dev, CNTR_ID_RETRIGGER);
    152
    153	return 0;
    154}
    155
    156static unsigned int armada_37xx_wdt_get_timeleft(struct watchdog_device *wdt)
    157{
    158	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
    159	u64 res;
    160
    161	res = get_counter_value(dev, CNTR_ID_WDOG) * CNTR_CTRL_PRESCALE_MIN;
    162	do_div(res, dev->clk_rate);
    163
    164	return res;
    165}
    166
    167static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt,
    168				       unsigned int timeout)
    169{
    170	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
    171
    172	wdt->timeout = timeout;
    173
    174	/*
    175	 * Compute the timeout in clock rate. We use smallest possible
    176	 * prescaler, which divides the clock rate by 2
    177	 * (CNTR_CTRL_PRESCALE_MIN).
    178	 */
    179	dev->timeout = (u64)dev->clk_rate * timeout;
    180	do_div(dev->timeout, CNTR_CTRL_PRESCALE_MIN);
    181
    182	return 0;
    183}
    184
    185static bool armada_37xx_wdt_is_running(struct armada_37xx_watchdog *dev)
    186{
    187	u32 reg;
    188
    189	regmap_read(dev->cpu_misc, WDT_TIMER_SELECT, &reg);
    190	if ((reg & WDT_TIMER_SELECT_MASK) != WDT_TIMER_SELECT_VAL)
    191		return false;
    192
    193	reg = readl(dev->reg + CNTR_CTRL(CNTR_ID_WDOG));
    194	return !!(reg & CNTR_CTRL_ACTIVE);
    195}
    196
    197static int armada_37xx_wdt_start(struct watchdog_device *wdt)
    198{
    199	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
    200
    201	/* select counter 1 as watchdog counter */
    202	regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, WDT_TIMER_SELECT_VAL);
    203
    204	/* init counter 0 as retrigger counter for counter 1 */
    205	init_counter(dev, CNTR_ID_RETRIGGER, CNTR_CTRL_MODE_ONESHOT, 0);
    206	set_counter_value(dev, CNTR_ID_RETRIGGER, 0);
    207
    208	/* init counter 1 to be retriggerable by counter 0 end count */
    209	init_counter(dev, CNTR_ID_WDOG, CNTR_CTRL_MODE_HWSIG,
    210		     CNTR_CTRL_TRIG_SRC_PREV_CNTR);
    211	set_counter_value(dev, CNTR_ID_WDOG, dev->timeout);
    212
    213	/* enable counter 1 */
    214	counter_enable(dev, CNTR_ID_WDOG);
    215
    216	/* start counter 1 by forcing immediate end count on counter 0 */
    217	counter_enable(dev, CNTR_ID_RETRIGGER);
    218
    219	return 0;
    220}
    221
    222static int armada_37xx_wdt_stop(struct watchdog_device *wdt)
    223{
    224	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
    225
    226	counter_disable(dev, CNTR_ID_WDOG);
    227	counter_disable(dev, CNTR_ID_RETRIGGER);
    228	regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, 0);
    229
    230	return 0;
    231}
    232
    233static const struct watchdog_info armada_37xx_wdt_info = {
    234	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
    235	.identity = "Armada 37xx Watchdog",
    236};
    237
    238static const struct watchdog_ops armada_37xx_wdt_ops = {
    239	.owner = THIS_MODULE,
    240	.start = armada_37xx_wdt_start,
    241	.stop = armada_37xx_wdt_stop,
    242	.ping = armada_37xx_wdt_ping,
    243	.set_timeout = armada_37xx_wdt_set_timeout,
    244	.get_timeleft = armada_37xx_wdt_get_timeleft,
    245};
    246
    247static void armada_clk_disable_unprepare(void *data)
    248{
    249	clk_disable_unprepare(data);
    250}
    251
    252static int armada_37xx_wdt_probe(struct platform_device *pdev)
    253{
    254	struct armada_37xx_watchdog *dev;
    255	struct resource *res;
    256	struct regmap *regmap;
    257	int ret;
    258
    259	dev = devm_kzalloc(&pdev->dev, sizeof(struct armada_37xx_watchdog),
    260			   GFP_KERNEL);
    261	if (!dev)
    262		return -ENOMEM;
    263
    264	dev->wdt.info = &armada_37xx_wdt_info;
    265	dev->wdt.ops = &armada_37xx_wdt_ops;
    266
    267	regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
    268						 "marvell,system-controller");
    269	if (IS_ERR(regmap))
    270		return PTR_ERR(regmap);
    271	dev->cpu_misc = regmap;
    272
    273	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    274	if (!res)
    275		return -ENODEV;
    276	dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
    277
    278	/* init clock */
    279	dev->clk = devm_clk_get(&pdev->dev, NULL);
    280	if (IS_ERR(dev->clk))
    281		return PTR_ERR(dev->clk);
    282
    283	ret = clk_prepare_enable(dev->clk);
    284	if (ret)
    285		return ret;
    286	ret = devm_add_action_or_reset(&pdev->dev,
    287				       armada_clk_disable_unprepare, dev->clk);
    288	if (ret)
    289		return ret;
    290
    291	dev->clk_rate = clk_get_rate(dev->clk);
    292	if (!dev->clk_rate)
    293		return -EINVAL;
    294
    295	/*
    296	 * Since the timeout in seconds is given as 32 bit unsigned int, and
    297	 * the counters hold 64 bit values, even after multiplication by clock
    298	 * rate the counter can hold timeout of UINT_MAX seconds.
    299	 */
    300	dev->wdt.min_timeout = 1;
    301	dev->wdt.max_timeout = UINT_MAX;
    302	dev->wdt.parent = &pdev->dev;
    303
    304	/* default value, possibly override by module parameter or dtb */
    305	dev->wdt.timeout = WATCHDOG_TIMEOUT;
    306	watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev);
    307
    308	platform_set_drvdata(pdev, &dev->wdt);
    309	watchdog_set_drvdata(&dev->wdt, dev);
    310
    311	armada_37xx_wdt_set_timeout(&dev->wdt, dev->wdt.timeout);
    312
    313	if (armada_37xx_wdt_is_running(dev))
    314		set_bit(WDOG_HW_RUNNING, &dev->wdt.status);
    315
    316	watchdog_set_nowayout(&dev->wdt, nowayout);
    317	watchdog_stop_on_reboot(&dev->wdt);
    318	ret = devm_watchdog_register_device(&pdev->dev, &dev->wdt);
    319	if (ret)
    320		return ret;
    321
    322	dev_info(&pdev->dev, "Initial timeout %d sec%s\n",
    323		 dev->wdt.timeout, nowayout ? ", nowayout" : "");
    324
    325	return 0;
    326}
    327
    328static int __maybe_unused armada_37xx_wdt_suspend(struct device *dev)
    329{
    330	struct watchdog_device *wdt = dev_get_drvdata(dev);
    331
    332	return armada_37xx_wdt_stop(wdt);
    333}
    334
    335static int __maybe_unused armada_37xx_wdt_resume(struct device *dev)
    336{
    337	struct watchdog_device *wdt = dev_get_drvdata(dev);
    338
    339	if (watchdog_active(wdt))
    340		return armada_37xx_wdt_start(wdt);
    341
    342	return 0;
    343}
    344
    345static const struct dev_pm_ops armada_37xx_wdt_dev_pm_ops = {
    346	SET_SYSTEM_SLEEP_PM_OPS(armada_37xx_wdt_suspend,
    347				armada_37xx_wdt_resume)
    348};
    349
    350#ifdef CONFIG_OF
    351static const struct of_device_id armada_37xx_wdt_match[] = {
    352	{ .compatible = "marvell,armada-3700-wdt", },
    353	{},
    354};
    355MODULE_DEVICE_TABLE(of, armada_37xx_wdt_match);
    356#endif
    357
    358static struct platform_driver armada_37xx_wdt_driver = {
    359	.probe		= armada_37xx_wdt_probe,
    360	.driver		= {
    361		.name	= "armada_37xx_wdt",
    362		.of_match_table = of_match_ptr(armada_37xx_wdt_match),
    363		.pm = &armada_37xx_wdt_dev_pm_ops,
    364	},
    365};
    366
    367module_platform_driver(armada_37xx_wdt_driver);
    368
    369MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
    370MODULE_DESCRIPTION("Armada 37xx CPU Watchdog");
    371
    372MODULE_LICENSE("GPL v2");
    373MODULE_ALIAS("platform:armada_37xx_wdt");