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

m54xx_wdt.c (5176B)


      1/*
      2 * drivers/watchdog/m54xx_wdt.c
      3 *
      4 * Watchdog driver for ColdFire MCF547x & MCF548x processors
      5 * Copyright 2010 (c) Philippe De Muyter <phdm@macqel.be>
      6 *
      7 * Adapted from the IXP4xx watchdog driver, which carries these notices:
      8 *
      9 *  Author: Deepak Saxena <dsaxena@plexity.net>
     10 *
     11 *  Copyright 2004 (c) MontaVista, Software, Inc.
     12 *  Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
     13 *
     14 * This file is licensed under  the terms of the GNU General Public
     15 * License version 2. This program is licensed "as is" without any
     16 * warranty of any kind, whether express or implied.
     17 */
     18
     19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     20
     21#include <linux/module.h>
     22#include <linux/moduleparam.h>
     23#include <linux/types.h>
     24#include <linux/kernel.h>
     25#include <linux/fs.h>
     26#include <linux/miscdevice.h>
     27#include <linux/watchdog.h>
     28#include <linux/init.h>
     29#include <linux/bitops.h>
     30#include <linux/ioport.h>
     31#include <linux/uaccess.h>
     32#include <linux/io.h>
     33
     34#include <asm/coldfire.h>
     35#include <asm/m54xxsim.h>
     36#include <asm/m54xxgpt.h>
     37
     38static bool nowayout = WATCHDOG_NOWAYOUT;
     39static unsigned int heartbeat = 30;	/* (secs) Default is 0.5 minute */
     40static unsigned long wdt_status;
     41
     42#define	WDT_IN_USE		0
     43#define	WDT_OK_TO_CLOSE		1
     44
     45static void wdt_enable(void)
     46{
     47	unsigned int gms0;
     48
     49	/* preserve GPIO usage, if any */
     50	gms0 = __raw_readl(MCF_GPT_GMS0);
     51	if (gms0 & MCF_GPT_GMS_TMS_GPIO)
     52		gms0 &= (MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_GPIO_MASK
     53							| MCF_GPT_GMS_OD);
     54	else
     55		gms0 = MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_OD;
     56	__raw_writel(gms0, MCF_GPT_GMS0);
     57	__raw_writel(MCF_GPT_GCIR_PRE(heartbeat*(MCF_BUSCLK/0xffff)) |
     58			MCF_GPT_GCIR_CNT(0xffff), MCF_GPT_GCIR0);
     59	gms0 |= MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE;
     60	__raw_writel(gms0, MCF_GPT_GMS0);
     61}
     62
     63static void wdt_disable(void)
     64{
     65	unsigned int gms0;
     66
     67	/* disable watchdog */
     68	gms0 = __raw_readl(MCF_GPT_GMS0);
     69	gms0 &= ~(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE);
     70	__raw_writel(gms0, MCF_GPT_GMS0);
     71}
     72
     73static void wdt_keepalive(void)
     74{
     75	unsigned int gms0;
     76
     77	gms0 = __raw_readl(MCF_GPT_GMS0);
     78	gms0 |= MCF_GPT_GMS_OCPW(0xA5);
     79	__raw_writel(gms0, MCF_GPT_GMS0);
     80}
     81
     82static int m54xx_wdt_open(struct inode *inode, struct file *file)
     83{
     84	if (test_and_set_bit(WDT_IN_USE, &wdt_status))
     85		return -EBUSY;
     86
     87	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
     88	wdt_enable();
     89	return stream_open(inode, file);
     90}
     91
     92static ssize_t m54xx_wdt_write(struct file *file, const char *data,
     93						size_t len, loff_t *ppos)
     94{
     95	if (len) {
     96		if (!nowayout) {
     97			size_t i;
     98
     99			clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
    100
    101			for (i = 0; i != len; i++) {
    102				char c;
    103
    104				if (get_user(c, data + i))
    105					return -EFAULT;
    106				if (c == 'V')
    107					set_bit(WDT_OK_TO_CLOSE, &wdt_status);
    108			}
    109		}
    110		wdt_keepalive();
    111	}
    112	return len;
    113}
    114
    115static const struct watchdog_info ident = {
    116	.options	= WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
    117				WDIOF_KEEPALIVEPING,
    118	.identity	= "Coldfire M54xx Watchdog",
    119};
    120
    121static long m54xx_wdt_ioctl(struct file *file, unsigned int cmd,
    122							 unsigned long arg)
    123{
    124	int ret = -ENOTTY;
    125	int time;
    126
    127	switch (cmd) {
    128	case WDIOC_GETSUPPORT:
    129		ret = copy_to_user((struct watchdog_info *)arg, &ident,
    130				   sizeof(ident)) ? -EFAULT : 0;
    131		break;
    132
    133	case WDIOC_GETSTATUS:
    134		ret = put_user(0, (int *)arg);
    135		break;
    136
    137	case WDIOC_GETBOOTSTATUS:
    138		ret = put_user(0, (int *)arg);
    139		break;
    140
    141	case WDIOC_KEEPALIVE:
    142		wdt_keepalive();
    143		ret = 0;
    144		break;
    145
    146	case WDIOC_SETTIMEOUT:
    147		ret = get_user(time, (int *)arg);
    148		if (ret)
    149			break;
    150
    151		if (time <= 0 || time > 30) {
    152			ret = -EINVAL;
    153			break;
    154		}
    155
    156		heartbeat = time;
    157		wdt_enable();
    158		fallthrough;
    159
    160	case WDIOC_GETTIMEOUT:
    161		ret = put_user(heartbeat, (int *)arg);
    162		break;
    163	}
    164	return ret;
    165}
    166
    167static int m54xx_wdt_release(struct inode *inode, struct file *file)
    168{
    169	if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
    170		wdt_disable();
    171	else {
    172		pr_crit("Device closed unexpectedly - timer will not stop\n");
    173		wdt_keepalive();
    174	}
    175	clear_bit(WDT_IN_USE, &wdt_status);
    176	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
    177
    178	return 0;
    179}
    180
    181
    182static const struct file_operations m54xx_wdt_fops = {
    183	.owner		= THIS_MODULE,
    184	.llseek		= no_llseek,
    185	.write		= m54xx_wdt_write,
    186	.unlocked_ioctl	= m54xx_wdt_ioctl,
    187	.compat_ioctl	= compat_ptr_ioctl,
    188	.open		= m54xx_wdt_open,
    189	.release	= m54xx_wdt_release,
    190};
    191
    192static struct miscdevice m54xx_wdt_miscdev = {
    193	.minor		= WATCHDOG_MINOR,
    194	.name		= "watchdog",
    195	.fops		= &m54xx_wdt_fops,
    196};
    197
    198static int __init m54xx_wdt_init(void)
    199{
    200	if (!request_mem_region(MCF_GPT_GCIR0, 4, "Coldfire M54xx Watchdog")) {
    201		pr_warn("I/O region busy\n");
    202		return -EBUSY;
    203	}
    204	pr_info("driver is loaded\n");
    205
    206	return misc_register(&m54xx_wdt_miscdev);
    207}
    208
    209static void __exit m54xx_wdt_exit(void)
    210{
    211	misc_deregister(&m54xx_wdt_miscdev);
    212	release_mem_region(MCF_GPT_GCIR0, 4);
    213}
    214
    215module_init(m54xx_wdt_init);
    216module_exit(m54xx_wdt_exit);
    217
    218MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
    219MODULE_DESCRIPTION("Coldfire M54xx Watchdog");
    220
    221module_param(heartbeat, int, 0);
    222MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 30s)");
    223
    224module_param(nowayout, bool, 0);
    225MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
    226
    227MODULE_LICENSE("GPL");