Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0cadc27 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog:
  watchdog: iTCO_wdt.c: remove extra pci_dev_put()'s from init code
  watchdog: add support for Broadcom BCM63xx built-in watchdog
  watchdog: f71808e_wdt: add support for the F71889FG
  watchdog: MachZ: fix debug macro
  watchdog: it8712f_wdt: Add module parameter for alternative reset sources
  watchdog: it8712f_wdt: Add comments for config/control register names
  watchdog: it87_wdt: Add support for watchdogs with 8b timers
  watchdog: it87_wdt: Add support for IT8720F watchdog
  watchdog:  Use static const char * const where possible
  watchdog: iTCO_wdt: Cleanup warning messages
  watchdog: iTCO_wdt: TCO Watchdog patch for Intel Patsburg DeviceIDs
parents 671f837a ad1d3a26
Loading
Loading
Loading
Loading
+17 −6
Original line number Diff line number Diff line
@@ -409,11 +409,11 @@ config ALIM7101_WDT
	  Most people will say N.

config F71808E_WDT
	tristate "Fintek F71808E and F71882FG Watchdog"
	tristate "Fintek F71808E, F71882FG and F71889FG Watchdog"
	depends on X86 && EXPERIMENTAL
	help
	  This is the driver for the hardware watchdog on the Fintek
	  F71808E and F71882FG Super I/O controllers.
	  F71808E, F71882FG and F71889FG Super I/O controllers.

	  You can compile this driver directly into the kernel, or use
	  it as a module.  The module will be called f71808e_wdt.
@@ -565,10 +565,11 @@ config IT87_WDT
	tristate "IT87 Watchdog Timer"
	depends on X86 && EXPERIMENTAL
	---help---
	  This is the driver for the hardware watchdog on the ITE IT8716,
	  IT8718, IT8726, IT8712(Version J,K) Super I/O chips. This watchdog
	  simply watches your kernel to make sure it doesn't freeze, and if
	  it does, it reboots your computer after a certain amount of time.
	  This is the driver for the hardware watchdog on the ITE IT8702,
	  IT8712, IT8716, IT8718, IT8720, IT8726, IT8712 Super I/O chips.
	  This watchdog simply watches your kernel to make sure it doesn't
	  freeze, and if it does, it reboots your computer after a certain
	  amount of time.

	  To compile this driver as a module, choose M here: the module will
	  be called it87_wdt.
@@ -916,6 +917,16 @@ config OCTEON_WDT
	  from the first interrupt, it is then only poked when the
	  device is written.

config BCM63XX_WDT
	tristate "Broadcom BCM63xx hardware watchdog"
	depends on BCM63XX
	help
	  Watchdog driver for the built in watchdog hardware in Broadcom
	  BCM63xx SoC.

	  To compile this driver as a loadable module, choose M here.
	  The module will be called bcm63xx_wdt.

# PARISC Architecture

# POWERPC Architecture
+1 −0
Original line number Diff line number Diff line
@@ -109,6 +109,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o

# MIPS Architecture
obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
obj-$(CONFIG_BCM63XX_WDT) += bcm63xx_wdt.o
obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o
obj-$(CONFIG_INDYDOG) += indydog.o
obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
+350 −0
Original line number Diff line number Diff line
/*
 *  Broadcom BCM63xx SoC watchdog driver
 *
 *  Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
 *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version
 *  2 of the License, or (at your option) any later version.
 */

#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/reboot.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/resource.h>
#include <linux/platform_device.h>

#include <bcm63xx_cpu.h>
#include <bcm63xx_io.h>
#include <bcm63xx_regs.h>
#include <bcm63xx_timer.h>

#define PFX KBUILD_MODNAME

#define WDT_HZ		50000000 /* Fclk */
#define WDT_DEFAULT_TIME	30      /* seconds */
#define WDT_MAX_TIME		256     /* seconds */

static struct {
	void __iomem *regs;
	struct timer_list timer;
	int default_ticks;
	unsigned long inuse;
	atomic_t ticks;
} bcm63xx_wdt_device;

static int expect_close;

static int wdt_time = WDT_DEFAULT_TIME;
static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");

/* HW functions */
static void bcm63xx_wdt_hw_start(void)
{
	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
}

static void bcm63xx_wdt_hw_stop(void)
{
	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
}

static void bcm63xx_wdt_isr(void *data)
{
	struct pt_regs *regs = get_irq_regs();

	die(PFX " fire", regs);
}

static void bcm63xx_timer_tick(unsigned long unused)
{
	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
		bcm63xx_wdt_hw_start();
		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
	} else
		printk(KERN_CRIT PFX ": watchdog will restart system\n");
}

static void bcm63xx_wdt_pet(void)
{
	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
}

static void bcm63xx_wdt_start(void)
{
	bcm63xx_wdt_pet();
	bcm63xx_timer_tick(0);
}

static void bcm63xx_wdt_pause(void)
{
	del_timer_sync(&bcm63xx_wdt_device.timer);
	bcm63xx_wdt_hw_stop();
}

static int bcm63xx_wdt_settimeout(int new_time)
{
	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
		return -EINVAL;

	wdt_time = new_time;

	return 0;
}

static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
{
	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
		return -EBUSY;

	bcm63xx_wdt_start();
	return nonseekable_open(inode, file);
}

static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
{
	if (expect_close == 42)
		bcm63xx_wdt_pause();
	else {
		printk(KERN_CRIT PFX
			": Unexpected close, not stopping watchdog!\n");
		bcm63xx_wdt_start();
	}
	clear_bit(0, &bcm63xx_wdt_device.inuse);
	expect_close = 0;
	return 0;
}

static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
				size_t len, loff_t *ppos)
{
	if (len) {
		if (!nowayout) {
			size_t i;

			/* In case it was set long ago */
			expect_close = 0;

			for (i = 0; i != len; i++) {
				char c;
				if (get_user(c, data + i))
					return -EFAULT;
				if (c == 'V')
					expect_close = 42;
			}
		}
		bcm63xx_wdt_pet();
	}
	return len;
}

static struct watchdog_info bcm63xx_wdt_info = {
	.identity       = PFX,
	.options        = WDIOF_SETTIMEOUT |
				WDIOF_KEEPALIVEPING |
				WDIOF_MAGICCLOSE,
};


static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
				unsigned long arg)
{
	void __user *argp = (void __user *)arg;
	int __user *p = argp;
	int new_value, retval = -EINVAL;

	switch (cmd) {
	case WDIOC_GETSUPPORT:
		return copy_to_user(argp, &bcm63xx_wdt_info,
			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;

	case WDIOC_GETSTATUS:
	case WDIOC_GETBOOTSTATUS:
		return put_user(0, p);

	case WDIOC_SETOPTIONS:
		if (get_user(new_value, p))
			return -EFAULT;

		if (new_value & WDIOS_DISABLECARD) {
			bcm63xx_wdt_pause();
			retval = 0;
		}
		if (new_value & WDIOS_ENABLECARD) {
			bcm63xx_wdt_start();
			retval = 0;
		}

		return retval;

	case WDIOC_KEEPALIVE:
		bcm63xx_wdt_pet();
		return 0;

	case WDIOC_SETTIMEOUT:
		if (get_user(new_value, p))
			return -EFAULT;

		if (bcm63xx_wdt_settimeout(new_value))
			return -EINVAL;

		bcm63xx_wdt_pet();

	case WDIOC_GETTIMEOUT:
		return put_user(wdt_time, p);

	default:
		return -ENOTTY;

	}
}

static int bcm63xx_wdt_notify_sys(struct notifier_block *this,
				unsigned long code, void *unused)
{
	if (code == SYS_DOWN || code == SYS_HALT)
		bcm63xx_wdt_pause();
	return NOTIFY_DONE;
}

static const struct file_operations bcm63xx_wdt_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.write		= bcm63xx_wdt_write,
	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
	.open		= bcm63xx_wdt_open,
	.release	= bcm63xx_wdt_release,
};

static struct miscdevice bcm63xx_wdt_miscdev = {
	.minor	= WATCHDOG_MINOR,
	.name	= "watchdog",
	.fops	= &bcm63xx_wdt_fops,
};

static struct notifier_block bcm63xx_wdt_notifier = {
	.notifier_call = bcm63xx_wdt_notify_sys,
};


static int bcm63xx_wdt_probe(struct platform_device *pdev)
{
	int ret;
	struct resource *r;

	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r) {
		dev_err(&pdev->dev, "failed to get resources\n");
		return -ENODEV;
	}

	bcm63xx_wdt_device.regs = ioremap_nocache(r->start, r->end - r->start);
	if (!bcm63xx_wdt_device.regs) {
		dev_err(&pdev->dev, "failed to remap I/O resources\n");
		return -ENXIO;
	}

	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
		goto unmap;
	}

	if (bcm63xx_wdt_settimeout(wdt_time)) {
		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
		dev_info(&pdev->dev,
			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
			wdt_time);
	}

	ret = register_reboot_notifier(&bcm63xx_wdt_notifier);
	if (ret) {
		dev_err(&pdev->dev, "failed to register reboot_notifier\n");
		goto unregister_timer;
	}

	ret = misc_register(&bcm63xx_wdt_miscdev);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to register watchdog device\n");
		goto unregister_reboot_notifier;
	}

	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
						WDT_DEFAULT_TIME);

	return 0;

unregister_reboot_notifier:
	unregister_reboot_notifier(&bcm63xx_wdt_notifier);
unregister_timer:
	bcm63xx_timer_unregister(TIMER_WDT_ID);
unmap:
	iounmap(bcm63xx_wdt_device.regs);
	return ret;
}

static int bcm63xx_wdt_remove(struct platform_device *pdev)
{
	if (!nowayout)
		bcm63xx_wdt_pause();

	misc_deregister(&bcm63xx_wdt_miscdev);

	iounmap(bcm63xx_wdt_device.regs);

	unregister_reboot_notifier(&bcm63xx_wdt_notifier);
	bcm63xx_timer_unregister(TIMER_WDT_ID);

	return 0;
}

static struct platform_driver bcm63xx_wdt = {
	.probe	= bcm63xx_wdt_probe,
	.remove = bcm63xx_wdt_remove,
	.driver = {
		.name = "bcm63xx-wdt",
	}
};

static int __init bcm63xx_wdt_init(void)
{
	return platform_driver_register(&bcm63xx_wdt);
}

static void __exit bcm63xx_wdt_exit(void)
{
	platform_driver_unregister(&bcm63xx_wdt);
}

module_init(bcm63xx_wdt_init);
module_exit(bcm63xx_wdt_exit);

MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:bcm63xx-wdt");
+9 −1
Original line number Diff line number Diff line
@@ -308,6 +308,12 @@ static int watchdog_start(void)
		superio_set_bit(watchdog.sioaddr, 0x29, 1);
		break;

	case f71889fg:
		/* set pin 40 to WDTRST# */
		superio_outb(watchdog.sioaddr, 0x2b,
				superio_inb(watchdog.sioaddr, 0x2b) & 0xcf);
		break;

	default:
		/*
		 * 'default' label to shut up the compiler and catch
@@ -708,8 +714,10 @@ static int __init f71808e_find(int sioaddr)
	case SIO_F71882_ID:
		watchdog.type = f71882fg;
		break;
	case SIO_F71862_ID:
	case SIO_F71889_ID:
		watchdog.type = f71889fg;
		break;
	case SIO_F71862_ID:
		/* These have a watchdog, though it isn't implemented (yet). */
		err = -ENOSYS;
		goto exit;
+15 −11
Original line number Diff line number Diff line
@@ -146,6 +146,7 @@ enum iTCO_chipsets {
	TCO_CPT29,	/* Cougar Point */
	TCO_CPT30,	/* Cougar Point */
	TCO_CPT31,	/* Cougar Point */
	TCO_PBG,	/* Patsburg */
};

static struct {
@@ -233,6 +234,7 @@ static struct {
	{"Cougar Point", 2},
	{"Cougar Point", 2},
	{"Cougar Point", 2},
	{"Patsburg", 2},
	{NULL, 0}
};

@@ -348,6 +350,7 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = {
	{ ITCO_PCI_DEVICE(0x1c5d,				TCO_CPT29)},
	{ ITCO_PCI_DEVICE(0x1c5e,				TCO_CPT30)},
	{ ITCO_PCI_DEVICE(0x1c5f,				TCO_CPT31)},
	{ ITCO_PCI_DEVICE(0x1d40,				TCO_PBG)},
	{ 0, },			/* End of list */
};
MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
@@ -374,7 +377,7 @@ static char expect_release;
static struct {		/* this is private data for the iTCO_wdt device */
	/* TCO version/generation */
	unsigned int iTCO_version;
	/* The cards ACPIBASE address (TCOBASE = ACPIBASE+0x60) */
	/* The device's ACPIBASE address (TCOBASE = ACPIBASE+0x60) */
	unsigned long ACPIBASE;
	/* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
	unsigned long __iomem *gcs;
@@ -467,7 +470,7 @@ static int iTCO_wdt_start(void)
	if (iTCO_wdt_unset_NO_REBOOT_bit()) {
		spin_unlock(&iTCO_wdt_private.io_lock);
		printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, "
					"reboot disabled by hardware\n");
					"reboot disabled by hardware/BIOS\n");
		return -EIO;
	}

@@ -781,8 +784,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
	base_address &= 0x0000ff80;
	if (base_address == 0x00000000) {
		/* Something's wrong here, ACPIBASE has to be set */
		printk(KERN_ERR PFX "failed to get TCOBASE address\n");
		pci_dev_put(pdev);
		printk(KERN_ERR PFX "failed to get TCOBASE address, "
					"device disabled by hardware/BIOS\n");
		return -ENODEV;
	}
	iTCO_wdt_private.iTCO_version =
@@ -797,7 +800,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
	if (iTCO_wdt_private.iTCO_version == 2) {
		pci_read_config_dword(pdev, 0xf0, &base_address);
		if ((base_address & 1) == 0) {
			printk(KERN_ERR PFX "RCBA is disabled by hardware\n");
			printk(KERN_ERR PFX "RCBA is disabled by hardware"
						"/BIOS, device disabled\n");
			ret = -ENODEV;
			goto out;
		}
@@ -808,7 +812,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
	/* Check chipset's NO_REBOOT bit */
	if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
		printk(KERN_INFO PFX "unable to reset NO_REBOOT flag, "
					"platform may have disabled it\n");
					"device disabled by hardware/BIOS\n");
		ret = -ENODEV;	/* Cannot reset NO_REBOOT bit */
		goto out_unmap;
	}
@@ -819,7 +823,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
	/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
	if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
		printk(KERN_ERR PFX
			"I/O address 0x%04lx already in use\n", SMI_EN);
			"I/O address 0x%04lx already in use, "
						"device disabled\n", SMI_EN);
		ret = -EIO;
		goto out_unmap;
	}
@@ -831,8 +836,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
	/* The TCO I/O registers reside in a 32-byte range pointed to
	   by the TCOBASE value */
	if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
		printk(KERN_ERR PFX "I/O address 0x%04lx already in use\n",
			TCOBASE);
		printk(KERN_ERR PFX "I/O address 0x%04lx already in use "
						"device disabled\n", TCOBASE);
		ret = -EIO;
		goto unreg_smi_en;
	}
@@ -880,7 +885,6 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
	if (iTCO_wdt_private.iTCO_version == 2)
		iounmap(iTCO_wdt_private.gcs);
out:
	pci_dev_put(iTCO_wdt_private.pdev);
	iTCO_wdt_private.ACPIBASE = 0;
	return ret;
}
@@ -921,7 +925,7 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev)
	}

	if (!found)
		printk(KERN_INFO PFX "No card detected\n");
		printk(KERN_INFO PFX "No device detected.\n");

	return ret;
}
Loading