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

ath79_wdt.c (7389B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Atheros AR71XX/AR724X/AR913X built-in hardware watchdog timer.
      4 *
      5 * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
      6 * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
      7 *
      8 * This driver was based on: drivers/watchdog/ixp4xx_wdt.c
      9 *	Author: Deepak Saxena <dsaxena@plexity.net>
     10 *	Copyright 2004 (c) MontaVista, Software, Inc.
     11 *
     12 * which again was based on sa1100 driver,
     13 *	Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
     14 */
     15
     16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     17
     18#include <linux/bitops.h>
     19#include <linux/delay.h>
     20#include <linux/errno.h>
     21#include <linux/fs.h>
     22#include <linux/io.h>
     23#include <linux/kernel.h>
     24#include <linux/miscdevice.h>
     25#include <linux/module.h>
     26#include <linux/moduleparam.h>
     27#include <linux/platform_device.h>
     28#include <linux/types.h>
     29#include <linux/watchdog.h>
     30#include <linux/clk.h>
     31#include <linux/err.h>
     32#include <linux/of.h>
     33#include <linux/of_platform.h>
     34#include <linux/uaccess.h>
     35
     36#define DRIVER_NAME	"ath79-wdt"
     37
     38#define WDT_TIMEOUT	15	/* seconds */
     39
     40#define WDOG_REG_CTRL		0x00
     41#define WDOG_REG_TIMER		0x04
     42
     43#define WDOG_CTRL_LAST_RESET	BIT(31)
     44#define WDOG_CTRL_ACTION_MASK	3
     45#define WDOG_CTRL_ACTION_NONE	0	/* no action */
     46#define WDOG_CTRL_ACTION_GPI	1	/* general purpose interrupt */
     47#define WDOG_CTRL_ACTION_NMI	2	/* NMI */
     48#define WDOG_CTRL_ACTION_FCR	3	/* full chip reset */
     49
     50static bool nowayout = WATCHDOG_NOWAYOUT;
     51module_param(nowayout, bool, 0);
     52MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
     53			   "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     54
     55static int timeout = WDT_TIMEOUT;
     56module_param(timeout, int, 0);
     57MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
     58			  "(default=" __MODULE_STRING(WDT_TIMEOUT) "s)");
     59
     60static unsigned long wdt_flags;
     61
     62#define WDT_FLAGS_BUSY		0
     63#define WDT_FLAGS_EXPECT_CLOSE	1
     64
     65static struct clk *wdt_clk;
     66static unsigned long wdt_freq;
     67static int boot_status;
     68static int max_timeout;
     69static void __iomem *wdt_base;
     70
     71static inline void ath79_wdt_wr(unsigned reg, u32 val)
     72{
     73	iowrite32(val, wdt_base + reg);
     74}
     75
     76static inline u32 ath79_wdt_rr(unsigned reg)
     77{
     78	return ioread32(wdt_base + reg);
     79}
     80
     81static inline void ath79_wdt_keepalive(void)
     82{
     83	ath79_wdt_wr(WDOG_REG_TIMER, wdt_freq * timeout);
     84	/* flush write */
     85	ath79_wdt_rr(WDOG_REG_TIMER);
     86}
     87
     88static inline void ath79_wdt_enable(void)
     89{
     90	ath79_wdt_keepalive();
     91
     92	/*
     93	 * Updating the TIMER register requires a few microseconds
     94	 * on the AR934x SoCs at least. Use a small delay to ensure
     95	 * that the TIMER register is updated within the hardware
     96	 * before enabling the watchdog.
     97	 */
     98	udelay(2);
     99
    100	ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_FCR);
    101	/* flush write */
    102	ath79_wdt_rr(WDOG_REG_CTRL);
    103}
    104
    105static inline void ath79_wdt_disable(void)
    106{
    107	ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_NONE);
    108	/* flush write */
    109	ath79_wdt_rr(WDOG_REG_CTRL);
    110}
    111
    112static int ath79_wdt_set_timeout(int val)
    113{
    114	if (val < 1 || val > max_timeout)
    115		return -EINVAL;
    116
    117	timeout = val;
    118	ath79_wdt_keepalive();
    119
    120	return 0;
    121}
    122
    123static int ath79_wdt_open(struct inode *inode, struct file *file)
    124{
    125	if (test_and_set_bit(WDT_FLAGS_BUSY, &wdt_flags))
    126		return -EBUSY;
    127
    128	clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
    129	ath79_wdt_enable();
    130
    131	return stream_open(inode, file);
    132}
    133
    134static int ath79_wdt_release(struct inode *inode, struct file *file)
    135{
    136	if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags))
    137		ath79_wdt_disable();
    138	else {
    139		pr_crit("device closed unexpectedly, watchdog timer will not stop!\n");
    140		ath79_wdt_keepalive();
    141	}
    142
    143	clear_bit(WDT_FLAGS_BUSY, &wdt_flags);
    144	clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
    145
    146	return 0;
    147}
    148
    149static ssize_t ath79_wdt_write(struct file *file, const char *data,
    150				size_t len, loff_t *ppos)
    151{
    152	if (len) {
    153		if (!nowayout) {
    154			size_t i;
    155
    156			clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
    157
    158			for (i = 0; i != len; i++) {
    159				char c;
    160
    161				if (get_user(c, data + i))
    162					return -EFAULT;
    163
    164				if (c == 'V')
    165					set_bit(WDT_FLAGS_EXPECT_CLOSE,
    166						&wdt_flags);
    167			}
    168		}
    169
    170		ath79_wdt_keepalive();
    171	}
    172
    173	return len;
    174}
    175
    176static const struct watchdog_info ath79_wdt_info = {
    177	.options		= WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
    178				  WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
    179	.firmware_version	= 0,
    180	.identity		= "ATH79 watchdog",
    181};
    182
    183static long ath79_wdt_ioctl(struct file *file, unsigned int cmd,
    184			    unsigned long arg)
    185{
    186	void __user *argp = (void __user *)arg;
    187	int __user *p = argp;
    188	int err;
    189	int t;
    190
    191	switch (cmd) {
    192	case WDIOC_GETSUPPORT:
    193		err = copy_to_user(argp, &ath79_wdt_info,
    194				   sizeof(ath79_wdt_info)) ? -EFAULT : 0;
    195		break;
    196
    197	case WDIOC_GETSTATUS:
    198		err = put_user(0, p);
    199		break;
    200
    201	case WDIOC_GETBOOTSTATUS:
    202		err = put_user(boot_status, p);
    203		break;
    204
    205	case WDIOC_KEEPALIVE:
    206		ath79_wdt_keepalive();
    207		err = 0;
    208		break;
    209
    210	case WDIOC_SETTIMEOUT:
    211		err = get_user(t, p);
    212		if (err)
    213			break;
    214
    215		err = ath79_wdt_set_timeout(t);
    216		if (err)
    217			break;
    218		fallthrough;
    219
    220	case WDIOC_GETTIMEOUT:
    221		err = put_user(timeout, p);
    222		break;
    223
    224	default:
    225		err = -ENOTTY;
    226		break;
    227	}
    228
    229	return err;
    230}
    231
    232static const struct file_operations ath79_wdt_fops = {
    233	.owner		= THIS_MODULE,
    234	.llseek		= no_llseek,
    235	.write		= ath79_wdt_write,
    236	.unlocked_ioctl	= ath79_wdt_ioctl,
    237	.compat_ioctl	= compat_ptr_ioctl,
    238	.open		= ath79_wdt_open,
    239	.release	= ath79_wdt_release,
    240};
    241
    242static struct miscdevice ath79_wdt_miscdev = {
    243	.minor = WATCHDOG_MINOR,
    244	.name = "watchdog",
    245	.fops = &ath79_wdt_fops,
    246};
    247
    248static int ath79_wdt_probe(struct platform_device *pdev)
    249{
    250	u32 ctrl;
    251	int err;
    252
    253	if (wdt_base)
    254		return -EBUSY;
    255
    256	wdt_base = devm_platform_ioremap_resource(pdev, 0);
    257	if (IS_ERR(wdt_base))
    258		return PTR_ERR(wdt_base);
    259
    260	wdt_clk = devm_clk_get(&pdev->dev, "wdt");
    261	if (IS_ERR(wdt_clk))
    262		return PTR_ERR(wdt_clk);
    263
    264	err = clk_prepare_enable(wdt_clk);
    265	if (err)
    266		return err;
    267
    268	wdt_freq = clk_get_rate(wdt_clk);
    269	if (!wdt_freq) {
    270		err = -EINVAL;
    271		goto err_clk_disable;
    272	}
    273
    274	max_timeout = (0xfffffffful / wdt_freq);
    275	if (timeout < 1 || timeout > max_timeout) {
    276		timeout = max_timeout;
    277		dev_info(&pdev->dev,
    278			"timeout value must be 0 < timeout < %d, using %d\n",
    279			max_timeout, timeout);
    280	}
    281
    282	ctrl = ath79_wdt_rr(WDOG_REG_CTRL);
    283	boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0;
    284
    285	err = misc_register(&ath79_wdt_miscdev);
    286	if (err) {
    287		dev_err(&pdev->dev,
    288			"unable to register misc device, err=%d\n", err);
    289		goto err_clk_disable;
    290	}
    291
    292	return 0;
    293
    294err_clk_disable:
    295	clk_disable_unprepare(wdt_clk);
    296	return err;
    297}
    298
    299static int ath79_wdt_remove(struct platform_device *pdev)
    300{
    301	misc_deregister(&ath79_wdt_miscdev);
    302	clk_disable_unprepare(wdt_clk);
    303	return 0;
    304}
    305
    306static void ath79_wdt_shutdown(struct platform_device *pdev)
    307{
    308	ath79_wdt_disable();
    309}
    310
    311#ifdef CONFIG_OF
    312static const struct of_device_id ath79_wdt_match[] = {
    313	{ .compatible = "qca,ar7130-wdt" },
    314	{},
    315};
    316MODULE_DEVICE_TABLE(of, ath79_wdt_match);
    317#endif
    318
    319static struct platform_driver ath79_wdt_driver = {
    320	.probe		= ath79_wdt_probe,
    321	.remove		= ath79_wdt_remove,
    322	.shutdown	= ath79_wdt_shutdown,
    323	.driver		= {
    324		.name	= DRIVER_NAME,
    325		.of_match_table = of_match_ptr(ath79_wdt_match),
    326	},
    327};
    328
    329module_platform_driver(ath79_wdt_driver);
    330
    331MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver");
    332MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
    333MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org");
    334MODULE_LICENSE("GPL v2");
    335MODULE_ALIAS("platform:" DRIVER_NAME);