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

cadence_wdt.c (11971B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Cadence WDT driver - Used by Xilinx Zynq
      4 *
      5 * Copyright (C) 2010 - 2014 Xilinx, Inc.
      6 *
      7 */
      8
      9#include <linux/clk.h>
     10#include <linux/init.h>
     11#include <linux/interrupt.h>
     12#include <linux/io.h>
     13#include <linux/irq.h>
     14#include <linux/kernel.h>
     15#include <linux/module.h>
     16#include <linux/of.h>
     17#include <linux/platform_device.h>
     18#include <linux/watchdog.h>
     19
     20#define CDNS_WDT_DEFAULT_TIMEOUT	10
     21/* Supports 1 - 516 sec */
     22#define CDNS_WDT_MIN_TIMEOUT	1
     23#define CDNS_WDT_MAX_TIMEOUT	516
     24
     25/* Restart key */
     26#define CDNS_WDT_RESTART_KEY 0x00001999
     27
     28/* Counter register access key */
     29#define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000
     30
     31/* Counter value divisor */
     32#define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000
     33
     34/* Clock prescaler value and selection */
     35#define CDNS_WDT_PRESCALE_64	64
     36#define CDNS_WDT_PRESCALE_512	512
     37#define CDNS_WDT_PRESCALE_4096	4096
     38#define CDNS_WDT_PRESCALE_SELECT_64	1
     39#define CDNS_WDT_PRESCALE_SELECT_512	2
     40#define CDNS_WDT_PRESCALE_SELECT_4096	3
     41
     42/* Input clock frequency */
     43#define CDNS_WDT_CLK_10MHZ	10000000
     44#define CDNS_WDT_CLK_75MHZ	75000000
     45
     46/* Counter maximum value */
     47#define CDNS_WDT_COUNTER_MAX 0xFFF
     48
     49static int wdt_timeout;
     50static int nowayout = WATCHDOG_NOWAYOUT;
     51
     52module_param(wdt_timeout, int, 0644);
     53MODULE_PARM_DESC(wdt_timeout,
     54		 "Watchdog time in seconds. (default="
     55		 __MODULE_STRING(CDNS_WDT_DEFAULT_TIMEOUT) ")");
     56
     57module_param(nowayout, int, 0644);
     58MODULE_PARM_DESC(nowayout,
     59		 "Watchdog cannot be stopped once started (default="
     60		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     61
     62/**
     63 * struct cdns_wdt - Watchdog device structure
     64 * @regs: baseaddress of device
     65 * @rst: reset flag
     66 * @clk: struct clk * of a clock source
     67 * @prescaler: for saving prescaler value
     68 * @ctrl_clksel: counter clock prescaler selection
     69 * @io_lock: spinlock for IO register access
     70 * @cdns_wdt_device: watchdog device structure
     71 *
     72 * Structure containing parameters specific to cadence watchdog.
     73 */
     74struct cdns_wdt {
     75	void __iomem		*regs;
     76	bool			rst;
     77	struct clk		*clk;
     78	u32			prescaler;
     79	u32			ctrl_clksel;
     80	spinlock_t		io_lock;
     81	struct watchdog_device	cdns_wdt_device;
     82};
     83
     84/* Write access to Registers */
     85static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val)
     86{
     87	writel_relaxed(val, wdt->regs + offset);
     88}
     89
     90/*************************Register Map**************************************/
     91
     92/* Register Offsets for the WDT */
     93#define CDNS_WDT_ZMR_OFFSET	0x0	/* Zero Mode Register */
     94#define CDNS_WDT_CCR_OFFSET	0x4	/* Counter Control Register */
     95#define CDNS_WDT_RESTART_OFFSET	0x8	/* Restart Register */
     96#define CDNS_WDT_SR_OFFSET	0xC	/* Status Register */
     97
     98/*
     99 * Zero Mode Register - This register controls how the time out is indicated
    100 * and also contains the access code to allow writes to the register (0xABC).
    101 */
    102#define CDNS_WDT_ZMR_WDEN_MASK	0x00000001 /* Enable the WDT */
    103#define CDNS_WDT_ZMR_RSTEN_MASK	0x00000002 /* Enable the reset output */
    104#define CDNS_WDT_ZMR_IRQEN_MASK	0x00000004 /* Enable IRQ output */
    105#define CDNS_WDT_ZMR_RSTLEN_16	0x00000030 /* Reset pulse of 16 pclk cycles */
    106#define CDNS_WDT_ZMR_ZKEY_VAL	0x00ABC000 /* Access key, 0xABC << 12 */
    107/*
    108 * Counter Control register - This register controls how fast the timer runs
    109 * and the reset value and also contains the access code to allow writes to
    110 * the register.
    111 */
    112#define CDNS_WDT_CCR_CRV_MASK	0x00003FFC /* Counter reset value */
    113
    114/**
    115 * cdns_wdt_stop - Stop the watchdog.
    116 *
    117 * @wdd: watchdog device
    118 *
    119 * Read the contents of the ZMR register, clear the WDEN bit
    120 * in the register and set the access key for successful write.
    121 *
    122 * Return: always 0
    123 */
    124static int cdns_wdt_stop(struct watchdog_device *wdd)
    125{
    126	struct cdns_wdt *wdt = watchdog_get_drvdata(wdd);
    127
    128	spin_lock(&wdt->io_lock);
    129	cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET,
    130			  CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK));
    131	spin_unlock(&wdt->io_lock);
    132
    133	return 0;
    134}
    135
    136/**
    137 * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog).
    138 *
    139 * @wdd: watchdog device
    140 *
    141 * Write the restart key value (0x00001999) to the restart register.
    142 *
    143 * Return: always 0
    144 */
    145static int cdns_wdt_reload(struct watchdog_device *wdd)
    146{
    147	struct cdns_wdt *wdt = watchdog_get_drvdata(wdd);
    148
    149	spin_lock(&wdt->io_lock);
    150	cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET,
    151			  CDNS_WDT_RESTART_KEY);
    152	spin_unlock(&wdt->io_lock);
    153
    154	return 0;
    155}
    156
    157/**
    158 * cdns_wdt_start - Enable and start the watchdog.
    159 *
    160 * @wdd: watchdog device
    161 *
    162 * The counter value is calculated according to the formula:
    163 *		calculated count = (timeout * clock) / prescaler + 1.
    164 * The calculated count is divided by 0x1000 to obtain the field value
    165 * to write to counter control register.
    166 * Clears the contents of prescaler and counter reset value. Sets the
    167 * prescaler to 4096 and the calculated count and access key
    168 * to write to CCR Register.
    169 * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit)
    170 * or Interrupt signal(IRQEN) with a specified cycles and the access
    171 * key to write to ZMR Register.
    172 *
    173 * Return: always 0
    174 */
    175static int cdns_wdt_start(struct watchdog_device *wdd)
    176{
    177	struct cdns_wdt *wdt = watchdog_get_drvdata(wdd);
    178	unsigned int data = 0;
    179	unsigned short count;
    180	unsigned long clock_f = clk_get_rate(wdt->clk);
    181
    182	/*
    183	 * Counter value divisor to obtain the value of
    184	 * counter reset to be written to control register.
    185	 */
    186	count = (wdd->timeout * (clock_f / wdt->prescaler)) /
    187		 CDNS_WDT_COUNTER_VALUE_DIVISOR + 1;
    188
    189	if (count > CDNS_WDT_COUNTER_MAX)
    190		count = CDNS_WDT_COUNTER_MAX;
    191
    192	spin_lock(&wdt->io_lock);
    193	cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET,
    194			  CDNS_WDT_ZMR_ZKEY_VAL);
    195
    196	count = (count << 2) & CDNS_WDT_CCR_CRV_MASK;
    197
    198	/* Write counter access key first to be able write to register */
    199	data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel;
    200	cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data);
    201	data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 |
    202	       CDNS_WDT_ZMR_ZKEY_VAL;
    203
    204	/* Reset on timeout if specified in device tree. */
    205	if (wdt->rst) {
    206		data |= CDNS_WDT_ZMR_RSTEN_MASK;
    207		data &= ~CDNS_WDT_ZMR_IRQEN_MASK;
    208	} else {
    209		data &= ~CDNS_WDT_ZMR_RSTEN_MASK;
    210		data |= CDNS_WDT_ZMR_IRQEN_MASK;
    211	}
    212	cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data);
    213	cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET,
    214			  CDNS_WDT_RESTART_KEY);
    215	spin_unlock(&wdt->io_lock);
    216
    217	return 0;
    218}
    219
    220/**
    221 * cdns_wdt_settimeout - Set a new timeout value for the watchdog device.
    222 *
    223 * @wdd: watchdog device
    224 * @new_time: new timeout value that needs to be set
    225 * Return: 0 on success
    226 *
    227 * Update the watchdog_device timeout with new value which is used when
    228 * cdns_wdt_start is called.
    229 */
    230static int cdns_wdt_settimeout(struct watchdog_device *wdd,
    231			       unsigned int new_time)
    232{
    233	wdd->timeout = new_time;
    234
    235	return cdns_wdt_start(wdd);
    236}
    237
    238/**
    239 * cdns_wdt_irq_handler - Notifies of watchdog timeout.
    240 *
    241 * @irq: interrupt number
    242 * @dev_id: pointer to a platform device structure
    243 * Return: IRQ_HANDLED
    244 *
    245 * The handler is invoked when the watchdog times out and a
    246 * reset on timeout has not been enabled.
    247 */
    248static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id)
    249{
    250	struct platform_device *pdev = dev_id;
    251
    252	dev_info(&pdev->dev,
    253		 "Watchdog timed out. Internal reset not enabled\n");
    254
    255	return IRQ_HANDLED;
    256}
    257
    258/*
    259 * Info structure used to indicate the features supported by the device
    260 * to the upper layers. This is defined in watchdog.h header file.
    261 */
    262static const struct watchdog_info cdns_wdt_info = {
    263	.identity	= "cdns_wdt watchdog",
    264	.options	= WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
    265			  WDIOF_MAGICCLOSE,
    266};
    267
    268/* Watchdog Core Ops */
    269static const struct watchdog_ops cdns_wdt_ops = {
    270	.owner = THIS_MODULE,
    271	.start = cdns_wdt_start,
    272	.stop = cdns_wdt_stop,
    273	.ping = cdns_wdt_reload,
    274	.set_timeout = cdns_wdt_settimeout,
    275};
    276
    277static void cdns_clk_disable_unprepare(void *data)
    278{
    279	clk_disable_unprepare(data);
    280}
    281
    282/************************Platform Operations*****************************/
    283/**
    284 * cdns_wdt_probe - Probe call for the device.
    285 *
    286 * @pdev: handle to the platform device structure.
    287 * Return: 0 on success, negative error otherwise.
    288 *
    289 * It does all the memory allocation and registration for the device.
    290 */
    291static int cdns_wdt_probe(struct platform_device *pdev)
    292{
    293	struct device *dev = &pdev->dev;
    294	int ret, irq;
    295	unsigned long clock_f;
    296	struct cdns_wdt *wdt;
    297	struct watchdog_device *cdns_wdt_device;
    298
    299	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
    300	if (!wdt)
    301		return -ENOMEM;
    302
    303	cdns_wdt_device = &wdt->cdns_wdt_device;
    304	cdns_wdt_device->info = &cdns_wdt_info;
    305	cdns_wdt_device->ops = &cdns_wdt_ops;
    306	cdns_wdt_device->timeout = CDNS_WDT_DEFAULT_TIMEOUT;
    307	cdns_wdt_device->min_timeout = CDNS_WDT_MIN_TIMEOUT;
    308	cdns_wdt_device->max_timeout = CDNS_WDT_MAX_TIMEOUT;
    309
    310	wdt->regs = devm_platform_ioremap_resource(pdev, 0);
    311	if (IS_ERR(wdt->regs))
    312		return PTR_ERR(wdt->regs);
    313
    314	/* Register the interrupt */
    315	wdt->rst = of_property_read_bool(dev->of_node, "reset-on-timeout");
    316	irq = platform_get_irq(pdev, 0);
    317	if (!wdt->rst && irq >= 0) {
    318		ret = devm_request_irq(dev, irq, cdns_wdt_irq_handler, 0,
    319				       pdev->name, pdev);
    320		if (ret) {
    321			dev_err(dev,
    322				"cannot register interrupt handler err=%d\n",
    323				ret);
    324			return ret;
    325		}
    326	}
    327
    328	/* Initialize the members of cdns_wdt structure */
    329	cdns_wdt_device->parent = dev;
    330
    331	watchdog_init_timeout(cdns_wdt_device, wdt_timeout, dev);
    332	watchdog_set_nowayout(cdns_wdt_device, nowayout);
    333	watchdog_stop_on_reboot(cdns_wdt_device);
    334	watchdog_set_drvdata(cdns_wdt_device, wdt);
    335
    336	wdt->clk = devm_clk_get(dev, NULL);
    337	if (IS_ERR(wdt->clk))
    338		return dev_err_probe(dev, PTR_ERR(wdt->clk),
    339				     "input clock not found\n");
    340
    341	ret = clk_prepare_enable(wdt->clk);
    342	if (ret) {
    343		dev_err(dev, "unable to enable clock\n");
    344		return ret;
    345	}
    346	ret = devm_add_action_or_reset(dev, cdns_clk_disable_unprepare,
    347				       wdt->clk);
    348	if (ret)
    349		return ret;
    350
    351	clock_f = clk_get_rate(wdt->clk);
    352	if (clock_f <= CDNS_WDT_CLK_75MHZ) {
    353		wdt->prescaler = CDNS_WDT_PRESCALE_512;
    354		wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512;
    355	} else {
    356		wdt->prescaler = CDNS_WDT_PRESCALE_4096;
    357		wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096;
    358	}
    359
    360	spin_lock_init(&wdt->io_lock);
    361
    362	watchdog_stop_on_reboot(cdns_wdt_device);
    363	watchdog_stop_on_unregister(cdns_wdt_device);
    364	ret = devm_watchdog_register_device(dev, cdns_wdt_device);
    365	if (ret)
    366		return ret;
    367	platform_set_drvdata(pdev, wdt);
    368
    369	dev_info(dev, "Xilinx Watchdog Timer with timeout %ds%s\n",
    370		 cdns_wdt_device->timeout, nowayout ? ", nowayout" : "");
    371
    372	return 0;
    373}
    374
    375/**
    376 * cdns_wdt_suspend - Stop the device.
    377 *
    378 * @dev: handle to the device structure.
    379 * Return: 0 always.
    380 */
    381static int __maybe_unused cdns_wdt_suspend(struct device *dev)
    382{
    383	struct cdns_wdt *wdt = dev_get_drvdata(dev);
    384
    385	if (watchdog_active(&wdt->cdns_wdt_device)) {
    386		cdns_wdt_stop(&wdt->cdns_wdt_device);
    387		clk_disable_unprepare(wdt->clk);
    388	}
    389
    390	return 0;
    391}
    392
    393/**
    394 * cdns_wdt_resume - Resume the device.
    395 *
    396 * @dev: handle to the device structure.
    397 * Return: 0 on success, errno otherwise.
    398 */
    399static int __maybe_unused cdns_wdt_resume(struct device *dev)
    400{
    401	int ret;
    402	struct cdns_wdt *wdt = dev_get_drvdata(dev);
    403
    404	if (watchdog_active(&wdt->cdns_wdt_device)) {
    405		ret = clk_prepare_enable(wdt->clk);
    406		if (ret) {
    407			dev_err(dev, "unable to enable clock\n");
    408			return ret;
    409		}
    410		cdns_wdt_start(&wdt->cdns_wdt_device);
    411	}
    412
    413	return 0;
    414}
    415
    416static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume);
    417
    418static const struct of_device_id cdns_wdt_of_match[] = {
    419	{ .compatible = "cdns,wdt-r1p2", },
    420	{ /* end of table */ }
    421};
    422MODULE_DEVICE_TABLE(of, cdns_wdt_of_match);
    423
    424/* Driver Structure */
    425static struct platform_driver cdns_wdt_driver = {
    426	.probe		= cdns_wdt_probe,
    427	.driver		= {
    428		.name	= "cdns-wdt",
    429		.of_match_table = cdns_wdt_of_match,
    430		.pm	= &cdns_wdt_pm_ops,
    431	},
    432};
    433
    434module_platform_driver(cdns_wdt_driver);
    435
    436MODULE_AUTHOR("Xilinx, Inc.");
    437MODULE_DESCRIPTION("Watchdog driver for Cadence WDT");
    438MODULE_LICENSE("GPL");