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

imgpdc_wdt.c (9008B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Imagination Technologies PowerDown Controller Watchdog Timer.
      4 *
      5 * Copyright (c) 2014 Imagination Technologies Ltd.
      6 *
      7 * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione
      8 *                                                     2012 Henrik Nordstrom
      9 *
     10 * Notes
     11 * -----
     12 * The timeout value is rounded to the next power of two clock cycles.
     13 * This is configured using the PDC_WDT_CONFIG register, according to this
     14 * formula:
     15 *
     16 *     timeout = 2^(delay + 1) clock cycles
     17 *
     18 * Where 'delay' is the value written in PDC_WDT_CONFIG register.
     19 *
     20 * Therefore, the hardware only allows to program watchdog timeouts, expressed
     21 * as a power of two number of watchdog clock cycles. The current implementation
     22 * guarantees that the actual watchdog timeout will be _at least_ the value
     23 * programmed in the imgpdg_wdt driver.
     24 *
     25 * The following table shows how the user-configured timeout relates
     26 * to the actual hardware timeout (watchdog clock @ 40000 Hz):
     27 *
     28 * input timeout | WD_DELAY | actual timeout
     29 * -----------------------------------
     30 *      10       |   18     |  13 seconds
     31 *      20       |   19     |  26 seconds
     32 *      30       |   20     |  52 seconds
     33 *      60       |   21     |  104 seconds
     34 *
     35 * Albeit coarse, this granularity would suffice most watchdog uses.
     36 * If the platform allows it, the user should be able to change the watchdog
     37 * clock rate and achieve a finer timeout granularity.
     38 */
     39
     40#include <linux/clk.h>
     41#include <linux/io.h>
     42#include <linux/log2.h>
     43#include <linux/module.h>
     44#include <linux/mod_devicetable.h>
     45#include <linux/platform_device.h>
     46#include <linux/slab.h>
     47#include <linux/watchdog.h>
     48
     49/* registers */
     50#define PDC_WDT_SOFT_RESET		0x00
     51#define PDC_WDT_CONFIG			0x04
     52  #define PDC_WDT_CONFIG_ENABLE		BIT(31)
     53  #define PDC_WDT_CONFIG_DELAY_MASK	0x1f
     54
     55#define PDC_WDT_TICKLE1			0x08
     56#define PDC_WDT_TICKLE1_MAGIC		0xabcd1234
     57#define PDC_WDT_TICKLE2			0x0c
     58#define PDC_WDT_TICKLE2_MAGIC		0x4321dcba
     59
     60#define PDC_WDT_TICKLE_STATUS_MASK	0x7
     61#define PDC_WDT_TICKLE_STATUS_SHIFT	0
     62#define PDC_WDT_TICKLE_STATUS_HRESET	0x0  /* Hard reset */
     63#define PDC_WDT_TICKLE_STATUS_TIMEOUT	0x1  /* Timeout */
     64#define PDC_WDT_TICKLE_STATUS_TICKLE	0x2  /* Tickled incorrectly */
     65#define PDC_WDT_TICKLE_STATUS_SRESET	0x3  /* Soft reset */
     66#define PDC_WDT_TICKLE_STATUS_USER	0x4  /* User reset */
     67
     68/* Timeout values are in seconds */
     69#define PDC_WDT_MIN_TIMEOUT		1
     70#define PDC_WDT_DEF_TIMEOUT		64
     71
     72static int heartbeat;
     73module_param(heartbeat, int, 0);
     74MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds "
     75	"(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
     76
     77static bool nowayout = WATCHDOG_NOWAYOUT;
     78module_param(nowayout, bool, 0);
     79MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
     80	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     81
     82struct pdc_wdt_dev {
     83	struct watchdog_device wdt_dev;
     84	struct clk *wdt_clk;
     85	struct clk *sys_clk;
     86	void __iomem *base;
     87};
     88
     89static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev)
     90{
     91	struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
     92
     93	writel(PDC_WDT_TICKLE1_MAGIC, wdt->base + PDC_WDT_TICKLE1);
     94	writel(PDC_WDT_TICKLE2_MAGIC, wdt->base + PDC_WDT_TICKLE2);
     95
     96	return 0;
     97}
     98
     99static int pdc_wdt_stop(struct watchdog_device *wdt_dev)
    100{
    101	unsigned int val;
    102	struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
    103
    104	val = readl(wdt->base + PDC_WDT_CONFIG);
    105	val &= ~PDC_WDT_CONFIG_ENABLE;
    106	writel(val, wdt->base + PDC_WDT_CONFIG);
    107
    108	/* Must tickle to finish the stop */
    109	pdc_wdt_keepalive(wdt_dev);
    110
    111	return 0;
    112}
    113
    114static void __pdc_wdt_set_timeout(struct pdc_wdt_dev *wdt)
    115{
    116	unsigned long clk_rate = clk_get_rate(wdt->wdt_clk);
    117	unsigned int val;
    118
    119	val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK;
    120	val |= order_base_2(wdt->wdt_dev.timeout * clk_rate) - 1;
    121	writel(val, wdt->base + PDC_WDT_CONFIG);
    122}
    123
    124static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev,
    125			       unsigned int new_timeout)
    126{
    127	struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
    128
    129	wdt->wdt_dev.timeout = new_timeout;
    130
    131	__pdc_wdt_set_timeout(wdt);
    132
    133	return 0;
    134}
    135
    136/* Start the watchdog timer (delay should already be set) */
    137static int pdc_wdt_start(struct watchdog_device *wdt_dev)
    138{
    139	unsigned int val;
    140	struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
    141
    142	__pdc_wdt_set_timeout(wdt);
    143
    144	val = readl(wdt->base + PDC_WDT_CONFIG);
    145	val |= PDC_WDT_CONFIG_ENABLE;
    146	writel(val, wdt->base + PDC_WDT_CONFIG);
    147
    148	return 0;
    149}
    150
    151static int pdc_wdt_restart(struct watchdog_device *wdt_dev,
    152			   unsigned long action, void *data)
    153{
    154	struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
    155
    156	/* Assert SOFT_RESET */
    157	writel(0x1, wdt->base + PDC_WDT_SOFT_RESET);
    158
    159	return 0;
    160}
    161
    162static const struct watchdog_info pdc_wdt_info = {
    163	.identity	= "IMG PDC Watchdog",
    164	.options	= WDIOF_SETTIMEOUT |
    165			  WDIOF_KEEPALIVEPING |
    166			  WDIOF_MAGICCLOSE,
    167};
    168
    169static const struct watchdog_ops pdc_wdt_ops = {
    170	.owner		= THIS_MODULE,
    171	.start		= pdc_wdt_start,
    172	.stop		= pdc_wdt_stop,
    173	.ping		= pdc_wdt_keepalive,
    174	.set_timeout	= pdc_wdt_set_timeout,
    175	.restart        = pdc_wdt_restart,
    176};
    177
    178static void pdc_clk_disable_unprepare(void *data)
    179{
    180	clk_disable_unprepare(data);
    181}
    182
    183static int pdc_wdt_probe(struct platform_device *pdev)
    184{
    185	struct device *dev = &pdev->dev;
    186	u64 div;
    187	int ret, val;
    188	unsigned long clk_rate;
    189	struct pdc_wdt_dev *pdc_wdt;
    190
    191	pdc_wdt = devm_kzalloc(dev, sizeof(*pdc_wdt), GFP_KERNEL);
    192	if (!pdc_wdt)
    193		return -ENOMEM;
    194
    195	pdc_wdt->base = devm_platform_ioremap_resource(pdev, 0);
    196	if (IS_ERR(pdc_wdt->base))
    197		return PTR_ERR(pdc_wdt->base);
    198
    199	pdc_wdt->sys_clk = devm_clk_get(dev, "sys");
    200	if (IS_ERR(pdc_wdt->sys_clk)) {
    201		dev_err(dev, "failed to get the sys clock\n");
    202		return PTR_ERR(pdc_wdt->sys_clk);
    203	}
    204
    205	pdc_wdt->wdt_clk = devm_clk_get(dev, "wdt");
    206	if (IS_ERR(pdc_wdt->wdt_clk)) {
    207		dev_err(dev, "failed to get the wdt clock\n");
    208		return PTR_ERR(pdc_wdt->wdt_clk);
    209	}
    210
    211	ret = clk_prepare_enable(pdc_wdt->sys_clk);
    212	if (ret) {
    213		dev_err(dev, "could not prepare or enable sys clock\n");
    214		return ret;
    215	}
    216	ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare,
    217				       pdc_wdt->sys_clk);
    218	if (ret)
    219		return ret;
    220
    221	ret = clk_prepare_enable(pdc_wdt->wdt_clk);
    222	if (ret) {
    223		dev_err(dev, "could not prepare or enable wdt clock\n");
    224		return ret;
    225	}
    226	ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare,
    227				       pdc_wdt->wdt_clk);
    228	if (ret)
    229		return ret;
    230
    231	/* We use the clock rate to calculate the max timeout */
    232	clk_rate = clk_get_rate(pdc_wdt->wdt_clk);
    233	if (clk_rate == 0) {
    234		dev_err(dev, "failed to get clock rate\n");
    235		return -EINVAL;
    236	}
    237
    238	if (order_base_2(clk_rate) > PDC_WDT_CONFIG_DELAY_MASK + 1) {
    239		dev_err(dev, "invalid clock rate\n");
    240		return -EINVAL;
    241	}
    242
    243	if (order_base_2(clk_rate) == 0)
    244		pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT + 1;
    245	else
    246		pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT;
    247
    248	pdc_wdt->wdt_dev.info = &pdc_wdt_info;
    249	pdc_wdt->wdt_dev.ops = &pdc_wdt_ops;
    250
    251	div = 1ULL << (PDC_WDT_CONFIG_DELAY_MASK + 1);
    252	do_div(div, clk_rate);
    253	pdc_wdt->wdt_dev.max_timeout = div;
    254	pdc_wdt->wdt_dev.timeout = PDC_WDT_DEF_TIMEOUT;
    255	pdc_wdt->wdt_dev.parent = dev;
    256	watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
    257
    258	watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, dev);
    259
    260	pdc_wdt_stop(&pdc_wdt->wdt_dev);
    261
    262	/* Find what caused the last reset */
    263	val = readl(pdc_wdt->base + PDC_WDT_TICKLE1);
    264	val = (val & PDC_WDT_TICKLE_STATUS_MASK) >> PDC_WDT_TICKLE_STATUS_SHIFT;
    265	switch (val) {
    266	case PDC_WDT_TICKLE_STATUS_TICKLE:
    267	case PDC_WDT_TICKLE_STATUS_TIMEOUT:
    268		pdc_wdt->wdt_dev.bootstatus |= WDIOF_CARDRESET;
    269		dev_info(dev, "watchdog module last reset due to timeout\n");
    270		break;
    271	case PDC_WDT_TICKLE_STATUS_HRESET:
    272		dev_info(dev,
    273			 "watchdog module last reset due to hard reset\n");
    274		break;
    275	case PDC_WDT_TICKLE_STATUS_SRESET:
    276		dev_info(dev,
    277			 "watchdog module last reset due to soft reset\n");
    278		break;
    279	case PDC_WDT_TICKLE_STATUS_USER:
    280		dev_info(dev,
    281			 "watchdog module last reset due to user reset\n");
    282		break;
    283	default:
    284		dev_info(dev, "contains an illegal status code (%08x)\n", val);
    285		break;
    286	}
    287
    288	watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout);
    289	watchdog_set_restart_priority(&pdc_wdt->wdt_dev, 128);
    290
    291	platform_set_drvdata(pdev, pdc_wdt);
    292
    293	watchdog_stop_on_reboot(&pdc_wdt->wdt_dev);
    294	watchdog_stop_on_unregister(&pdc_wdt->wdt_dev);
    295	return devm_watchdog_register_device(dev, &pdc_wdt->wdt_dev);
    296}
    297
    298static const struct of_device_id pdc_wdt_match[] = {
    299	{ .compatible = "img,pdc-wdt" },
    300	{}
    301};
    302MODULE_DEVICE_TABLE(of, pdc_wdt_match);
    303
    304static struct platform_driver pdc_wdt_driver = {
    305	.driver = {
    306		.name = "imgpdc-wdt",
    307		.of_match_table	= pdc_wdt_match,
    308	},
    309	.probe = pdc_wdt_probe,
    310};
    311module_platform_driver(pdc_wdt_driver);
    312
    313MODULE_AUTHOR("Jude Abraham <Jude.Abraham@imgtec.com>");
    314MODULE_AUTHOR("Naidu Tellapati <Naidu.Tellapati@imgtec.com>");
    315MODULE_DESCRIPTION("Imagination Technologies PDC Watchdog Timer Driver");
    316MODULE_LICENSE("GPL v2");