cregit-Linux how code gets into the kernel

Release 4.7 drivers/watchdog/ts72xx_wdt.c

Directory: drivers/watchdog
/*
 * Watchdog driver for Technologic Systems TS-72xx based SBCs
 * (TS-7200, TS-7250 and TS-7260). These boards have external
 * glue logic CPLD chip, which includes programmable watchdog
 * timer.
 *
 * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi>
 *
 * This driver is based on ep93xx_wdt and wm831x_wdt drivers.
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */

#include <linux/fs.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>


#define TS72XX_WDT_FEED_VAL		0x05

#define TS72XX_WDT_DEFAULT_TIMEOUT	8


static int timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. "
			  "(1 <= timeout <= 8, default="
			  __MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT)
			  ")");


static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");

/**
 * struct ts72xx_wdt - watchdog control structure
 * @lock: lock that protects this structure
 * @regval: watchdog timeout value suitable for control register
 * @flags: flags controlling watchdog device state
 * @control_reg: watchdog control register
 * @feed_reg: watchdog feed register
 * @pdev: back pointer to platform dev
 */

struct ts72xx_wdt {
	
struct mutex	lock;
	
int		regval;


#define TS72XX_WDT_BUSY_FLAG		1

#define TS72XX_WDT_EXPECT_CLOSE_FLAG	2
	
int		flags;

	
void __iomem	*control_reg;
	
void __iomem	*feed_reg;

	
struct platform_device *pdev;
};


static struct platform_device *ts72xx_wdt_pdev;

/*
 * TS-72xx Watchdog supports following timeouts (value written
 * to control register):
 *      value   description
 *      -------------------------
 *      0x00    watchdog disabled
 *      0x01    250ms
 *      0x02    500ms
 *      0x03    1s
 *      0x04    reserved
 *      0x05    2s
 *      0x06    4s
 *      0x07    8s
 *
 * Timeouts below 1s are not very usable so we don't
 * allow them at all.
 *
 * We provide two functions that convert between these:
 * timeout_to_regval() and regval_to_timeout().
 */
static const struct {
	
int	timeout;
	
int	regval;
} 
ts72xx_wdt_map[] = {
	{ 1, 3 },
	{ 2, 5 },
	{ 4, 6 },
	{ 8, 7 },
};

/**
 * timeout_to_regval() - converts given timeout to control register value
 * @new_timeout: timeout in seconds to be converted
 *
 * Function converts given @new_timeout into valid value that can
 * be programmed into watchdog control register. When conversion is
 * not possible, function returns %-EINVAL.
 */

static int timeout_to_regval(int new_timeout) { int i; /* first limit it to 1 - 8 seconds */ new_timeout = clamp_val(new_timeout, 1, 8); for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) { if (ts72xx_wdt_map[i].timeout >= new_timeout) return ts72xx_wdt_map[i].regval; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg65100.00%1100.00%
Total65100.00%1100.00%

/** * regval_to_timeout() - converts control register value to timeout * @regval: control register value to be converted * * Function converts given @regval to timeout in seconds (1, 2, 4 or 8). * If @regval cannot be converted, function returns %-EINVAL. */
static int regval_to_timeout(int regval) { int i; for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) { if (ts72xx_wdt_map[i].regval == regval) return ts72xx_wdt_map[i].timeout; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg53100.00%1100.00%
Total53100.00%1100.00%

/** * ts72xx_wdt_kick() - kick the watchdog * @wdt: watchdog to be kicked * * Called with @wdt->lock held. */
static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt) { __raw_writeb(TS72XX_WDT_FEED_VAL, wdt->feed_reg); }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg21100.00%1100.00%
Total21100.00%1100.00%

/** * ts72xx_wdt_start() - starts the watchdog timer * @wdt: watchdog to be started * * This function programs timeout to watchdog timer * and starts it. * * Called with @wdt->lock held. */
static void ts72xx_wdt_start(struct ts72xx_wdt *wdt) { /* * To program the wdt, it first must be "fed" and * only after that (within 30 usecs) the configuration * can be changed. */ ts72xx_wdt_kick(wdt); __raw_writeb((u8)wdt->regval, wdt->control_reg); }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg31100.00%1100.00%
Total31100.00%1100.00%

/** * ts72xx_wdt_stop() - stops the watchdog timer * @wdt: watchdog to be stopped * * Called with @wdt->lock held. */
static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt) { ts72xx_wdt_kick(wdt); __raw_writeb(0, wdt->control_reg); }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg25100.00%1100.00%
Total25100.00%1100.00%


static int ts72xx_wdt_open(struct inode *inode, struct file *file) { struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev); int regval; /* * Try to convert default timeout to valid register * value first. */ regval = timeout_to_regval(timeout); if (regval < 0) { dev_err(&wdt->pdev->dev, "failed to convert timeout (%d) to register value\n", timeout); return regval; } if (mutex_lock_interruptible(&wdt->lock)) return -ERESTARTSYS; if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) { mutex_unlock(&wdt->lock); return -EBUSY; } wdt->flags = TS72XX_WDT_BUSY_FLAG; wdt->regval = regval; file->private_data = wdt; ts72xx_wdt_start(wdt); mutex_unlock(&wdt->lock); return nonseekable_open(inode, file); }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg14099.29%150.00%
guenter roeckguenter roeck10.71%150.00%
Total141100.00%2100.00%


static int ts72xx_wdt_release(struct inode *inode, struct file *file) { struct ts72xx_wdt *wdt = file->private_data; if (mutex_lock_interruptible(&wdt->lock)) return -ERESTARTSYS; if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) { ts72xx_wdt_stop(wdt); } else { dev_warn(&wdt->pdev->dev, "TS-72XX WDT device closed unexpectly. " "Watchdog timer will not stop!\n"); /* * Kick it one more time, to give userland some time * to recover (for example, respawning the kicker * daemon). */ ts72xx_wdt_kick(wdt); } wdt->flags = 0; mutex_unlock(&wdt->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg97100.00%1100.00%
Total97100.00%1100.00%


static ssize_t ts72xx_wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { struct ts72xx_wdt *wdt = file->private_data; if (!len) return 0; if (mutex_lock_interruptible(&wdt->lock)) return -ERESTARTSYS; ts72xx_wdt_kick(wdt); /* * Support for magic character closing. User process * writes 'V' into the device, just before it is closed. * This means that we know that the wdt timer can be * stopped after user closes the device. */ if (!nowayout) { int i; for (i = 0; i < len; i++) { char c; /* In case it was set long ago */ wdt->flags &= ~TS72XX_WDT_EXPECT_CLOSE_FLAG; if (get_user(c, data + i)) { mutex_unlock(&wdt->lock); return -EFAULT; } if (c == 'V') { wdt->flags |= TS72XX_WDT_EXPECT_CLOSE_FLAG; break; } } } mutex_unlock(&wdt->lock); return len; }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg148100.00%1100.00%
Total148100.00%1100.00%

static const struct watchdog_info winfo = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "TS-72XX WDT", };
static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ts72xx_wdt *wdt = file->private_data; void __user *argp = (void __user *)arg; int __user *p = (int __user *)argp; int error = 0; if (mutex_lock_interruptible(&wdt->lock)) return -ERESTARTSYS; switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user(argp, &winfo, sizeof(winfo))) error = -EFAULT; break; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: error = put_user(0, p); break; case WDIOC_KEEPALIVE: ts72xx_wdt_kick(wdt); break; case WDIOC_SETOPTIONS: { int options; error = get_user(options, p); if (error) break; error = -EINVAL; if ((options & WDIOS_DISABLECARD) != 0) { ts72xx_wdt_stop(wdt); error = 0; } if ((options & WDIOS_ENABLECARD) != 0) { ts72xx_wdt_start(wdt); error = 0; } break; } case WDIOC_SETTIMEOUT: { int new_timeout; int regval; error = get_user(new_timeout, p); if (error) break; regval = timeout_to_regval(new_timeout); if (regval < 0) { error = regval; break; } ts72xx_wdt_stop(wdt); wdt->regval = regval; ts72xx_wdt_start(wdt); /*FALLTHROUGH*/ } case WDIOC_GETTIMEOUT: error = put_user(regval_to_timeout(wdt->regval), p); break; default: error = -ENOTTY; break; } mutex_unlock(&wdt->lock); return error; }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg25185.37%125.00%
dan carpenterdan carpenter3110.54%250.00%
wim van sebroeckwim van sebroeck124.08%125.00%
Total294100.00%4100.00%

static const struct file_operations ts72xx_wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .open = ts72xx_wdt_open, .release = ts72xx_wdt_release, .write = ts72xx_wdt_write, .unlocked_ioctl = ts72xx_wdt_ioctl, }; static struct miscdevice ts72xx_wdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &ts72xx_wdt_fops, };
static int ts72xx_wdt_probe(struct platform_device *pdev) { struct ts72xx_wdt *wdt; struct resource *r1, *r2; int error = 0; wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL); if (!wdt) return -ENOMEM; r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1); if (IS_ERR(wdt->control_reg)) return PTR_ERR(wdt->control_reg); r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2); if (IS_ERR(wdt->feed_reg)) return PTR_ERR(wdt->feed_reg); platform_set_drvdata(pdev, wdt); ts72xx_wdt_pdev = pdev; wdt->pdev = pdev; mutex_init(&wdt->lock); /* make sure that the watchdog is disabled */ ts72xx_wdt_stop(wdt); error = misc_register(&ts72xx_wdt_miscdev); if (error) { dev_err(&pdev->dev, "failed to register miscdev\n"); return error; } dev_info(&pdev->dev, "TS-72xx Watchdog driver\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg17884.36%266.67%
jingoo hanjingoo han3315.64%133.33%
Total211100.00%3100.00%


static int ts72xx_wdt_remove(struct platform_device *pdev) { misc_deregister(&ts72xx_wdt_miscdev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg1785.00%150.00%
greg kroah-hartmangreg kroah-hartman315.00%150.00%
Total20100.00%2100.00%

static struct platform_driver ts72xx_wdt_driver = { .probe = ts72xx_wdt_probe, .remove = ts72xx_wdt_remove, .driver = { .name = "ts72xx-wdt", }, }; module_platform_driver(ts72xx_wdt_driver); MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>"); MODULE_DESCRIPTION("TS-72xx SBC Watchdog"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:ts72xx-wdt");

Overall Contributors

PersonTokensPropCommitsCommitProp
mika westerbergmika westerberg133893.83%215.38%
jingoo hanjingoo han342.38%215.38%
dan carpenterdan carpenter312.17%215.38%
wim van sebroeckwim van sebroeck151.05%323.08%
tejun heotejun heo30.21%17.69%
greg kroah-hartmangreg kroah-hartman30.21%17.69%
guenter roeckguenter roeck10.07%17.69%
axel linaxel lin10.07%17.69%
Total1426100.00%13100.00%
Directory: drivers/watchdog
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}