예제 #1
0
파일: iTCO_wdt.c 프로젝트: AK101111/linux
static void iTCO_wdt_cleanup(void)
{
	/* Stop the timer before we leave */
	if (!nowayout)
		iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);

	/* Deregister */
	watchdog_unregister_device(&iTCO_wdt_watchdog_dev);

	/* release resources */
	release_region(iTCO_wdt_private.tco_res->start,
			resource_size(iTCO_wdt_private.tco_res));
	release_region(iTCO_wdt_private.smi_res->start,
			resource_size(iTCO_wdt_private.smi_res));
	if (iTCO_wdt_private.iTCO_version >= 2) {
		iounmap(iTCO_wdt_private.gcs_pmc);
		release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
				resource_size(iTCO_wdt_private.gcs_pmc_res));
	}

	iTCO_wdt_private.tco_res = NULL;
	iTCO_wdt_private.smi_res = NULL;
	iTCO_wdt_private.gcs_pmc_res = NULL;
	iTCO_wdt_private.gcs_pmc = NULL;
}
예제 #2
0
파일: iTCO_wdt.c 프로젝트: AK101111/linux
static int iTCO_wdt_suspend_noirq(struct device *dev)
{
	int ret = 0;

	iTCO_wdt_private.suspended = false;
	if (watchdog_active(&iTCO_wdt_watchdog_dev) && need_suspend()) {
		ret = iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
		if (!ret)
			iTCO_wdt_private.suspended = true;
	}
	return ret;
}
예제 #3
0
static int iTCO_wdt_release(struct inode *inode, struct file *file)
{
    /*
     *      Shut off the timer.
     */
    if (expect_release == 42) {
        iTCO_wdt_stop();
    } else {
        pr_crit("Unexpected close, not stopping watchdog!\n");
        iTCO_wdt_keepalive();
    }
    clear_bit(0, &is_active);
    expect_release = 0;
    return 0;
}
static void iTCO_wdt_cleanup(void)
{
	/* Stop the timer before we leave */
	if (!nowayout)
		iTCO_wdt_stop();

	/* Deregister */
	misc_deregister(&iTCO_wdt_miscdev);
	release_region(TCOBASE, 0x20);
	release_region(SMI_EN, 4);
	release_region(SMI_STS, 4);
	unregister_reboot_notifier(&reboot_notifier);
	iTCO_wdt_private.ACPIBASE = 0;
#ifdef CONFIG_DEBUG_FS
	debugfs_remove_recursive(iTCO_debugfs_dir);
#endif /* CONFIG_DEBUG_FS */
}
예제 #5
0
static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd,
                           unsigned long arg)
{
    int new_options, retval = -EINVAL;
    int new_heartbeat;
    void __user *argp = (void __user *)arg;
    int __user *p = argp;
    static struct watchdog_info ident = {
        .options =		WDIOF_SETTIMEOUT |
        WDIOF_KEEPALIVEPING |
        WDIOF_MAGICCLOSE,
        .firmware_version =	0,
        .identity =		DRV_NAME,
    };

    switch (cmd) {
    case WDIOC_GETSUPPORT:
        return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
    case WDIOC_GETSTATUS:
    case WDIOC_GETBOOTSTATUS:
        return put_user(0, p);

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

        if (new_options & WDIOS_DISABLECARD) {
            iTCO_wdt_stop();
            retval = 0;
        }
        if (new_options & WDIOS_ENABLECARD) {
            iTCO_wdt_keepalive();
            iTCO_wdt_start();
            retval = 0;
        }
        return retval;
    }
    case WDIOC_KEEPALIVE:
        iTCO_wdt_keepalive();
        return 0;

    case WDIOC_SETTIMEOUT:
    {
        if (get_user(new_heartbeat, p))
            return -EFAULT;
        if (iTCO_wdt_set_heartbeat(new_heartbeat))
            return -EINVAL;
        iTCO_wdt_keepalive();
        /* Fall */
    }
    case WDIOC_GETTIMEOUT:
        return put_user(heartbeat, p);
    case WDIOC_GETTIMELEFT:
    {
        int time_left;
        if (iTCO_wdt_get_timeleft(&time_left))
            return -EINVAL;
        return put_user(time_left, p);
    }
    default:
        return -ENOTTY;
    }
}

/*
 *	Kernel Interfaces
 */

static const struct file_operations iTCO_wdt_fops = {
    .owner =		THIS_MODULE,
    .llseek =		no_llseek,
    .write =		iTCO_wdt_write,
    .unlocked_ioctl =	iTCO_wdt_ioctl,
    .open =			iTCO_wdt_open,
    .release =		iTCO_wdt_release,
};

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

/*
 *	Init & exit routines
 */

static void __devexit iTCO_wdt_cleanup(void)
{
    /* Stop the timer before we leave */
    if (!nowayout)
        iTCO_wdt_stop();

    /* Deregister */
    misc_deregister(&iTCO_wdt_miscdev);

    /* release resources */
    release_region(iTCO_wdt_private.tco_res->start,
                   resource_size(iTCO_wdt_private.tco_res));
    release_region(iTCO_wdt_private.smi_res->start,
                   resource_size(iTCO_wdt_private.smi_res));
    if (iTCO_wdt_private.iTCO_version == 2) {
        iounmap(iTCO_wdt_private.gcs);
        release_mem_region(iTCO_wdt_private.gcs_res->start,
                           resource_size(iTCO_wdt_private.gcs_res));
    }

    iTCO_wdt_private.tco_res = NULL;
    iTCO_wdt_private.smi_res = NULL;
    iTCO_wdt_private.gcs_res = NULL;
    iTCO_wdt_private.gcs = NULL;
}

static int __devinit iTCO_wdt_probe(struct platform_device *dev)
{
    int ret = -ENODEV;
    unsigned long val32;
    struct lpc_ich_info *ich_info = dev->dev.platform_data;

    if (!ich_info)
        goto out;

    spin_lock_init(&iTCO_wdt_private.io_lock);

    iTCO_wdt_private.tco_res =
        platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO);
    if (!iTCO_wdt_private.tco_res)
        goto out;

    iTCO_wdt_private.smi_res =
        platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI);
    if (!iTCO_wdt_private.smi_res)
        goto out;

    iTCO_wdt_private.iTCO_version = ich_info->iTCO_version;
    iTCO_wdt_private.dev = dev;
    iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);

    /*
     * Get the Memory-Mapped GCS register, we need it for the
     * NO_REBOOT flag (TCO v2).
     */
    if (iTCO_wdt_private.iTCO_version == 2) {
        iTCO_wdt_private.gcs_res = platform_get_resource(dev,
                                   IORESOURCE_MEM,
                                   ICH_RES_MEM_GCS);

        if (!iTCO_wdt_private.gcs_res)
            goto out;

        if (!request_mem_region(iTCO_wdt_private.gcs_res->start,
                                resource_size(iTCO_wdt_private.gcs_res), dev->name)) {
            ret = -EBUSY;
            goto out;
        }
        iTCO_wdt_private.gcs = ioremap(iTCO_wdt_private.gcs_res->start,
                                       resource_size(iTCO_wdt_private.gcs_res));
        if (!iTCO_wdt_private.gcs) {
            ret = -EIO;
            goto unreg_gcs;
        }
    }

    /* Check chipset's NO_REBOOT bit */
    if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
        pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
        ret = -ENODEV;	/* Cannot reset NO_REBOOT bit */
        goto unmap_gcs;
    }

    /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
    iTCO_wdt_set_NO_REBOOT_bit();

    /* The TCO logic uses the TCO_EN bit in the SMI_EN register */
    if (!request_region(iTCO_wdt_private.smi_res->start,
                        resource_size(iTCO_wdt_private.smi_res), dev->name)) {
        pr_err("I/O address 0x%04llx already in use, device disabled\n",
               SMI_EN);
        ret = -EBUSY;
        goto unmap_gcs;
    }
    if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
        /*
         * Bit 13: TCO_EN -> 0
         * Disables TCO logic generating an SMI#
         */
        val32 = inl(SMI_EN);
        val32 &= 0xffffdfff;	/* Turn off SMI clearing watchdog */
        outl(val32, SMI_EN);
    }

    if (!request_region(iTCO_wdt_private.tco_res->start,
                        resource_size(iTCO_wdt_private.tco_res), dev->name)) {
        pr_err("I/O address 0x%04llx already in use, device disabled\n",
               TCOBASE);
        ret = -EBUSY;
        goto unreg_smi;
    }

    pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n",
            ich_info->name, ich_info->iTCO_version, TCOBASE);

    /* Clear out the (probably old) status */
    outw(0x0008, TCO1_STS);	/* Clear the Time Out Status bit */
    outw(0x0002, TCO2_STS);	/* Clear SECOND_TO_STS bit */
    outw(0x0004, TCO2_STS);	/* Clear BOOT_STS bit */

    /* Make sure the watchdog is not running */
    iTCO_wdt_stop();

    /* Check that the heartbeat value is within it's range;
       if not reset to the default */
    if (iTCO_wdt_set_heartbeat(heartbeat)) {
        iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT);
        pr_info("timeout value out of range, using %d\n", heartbeat);
    }

    ret = misc_register(&iTCO_wdt_miscdev);
    if (ret != 0) {
        pr_err("cannot register miscdev on minor=%d (err=%d)\n",
               WATCHDOG_MINOR, ret);
        goto unreg_tco;
    }

    pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
            heartbeat, nowayout);

    return 0;

unreg_tco:
    release_region(iTCO_wdt_private.tco_res->start,
                   resource_size(iTCO_wdt_private.tco_res));
unreg_smi:
    release_region(iTCO_wdt_private.smi_res->start,
                   resource_size(iTCO_wdt_private.smi_res));
unmap_gcs:
    if (iTCO_wdt_private.iTCO_version == 2)
        iounmap(iTCO_wdt_private.gcs);
unreg_gcs:
    if (iTCO_wdt_private.iTCO_version == 2)
        release_mem_region(iTCO_wdt_private.gcs_res->start,
                           resource_size(iTCO_wdt_private.gcs_res));
out:
    iTCO_wdt_private.tco_res = NULL;
    iTCO_wdt_private.smi_res = NULL;
    iTCO_wdt_private.gcs_res = NULL;
    iTCO_wdt_private.gcs = NULL;

    return ret;
}

static int __devexit iTCO_wdt_remove(struct platform_device *dev)
{
    if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res)
        iTCO_wdt_cleanup();

    return 0;
}

static void iTCO_wdt_shutdown(struct platform_device *dev)
{
    iTCO_wdt_stop();
}

#define iTCO_wdt_suspend NULL
#define iTCO_wdt_resume  NULL

static struct platform_driver iTCO_wdt_driver = {
    .probe          = iTCO_wdt_probe,
    .remove         = __devexit_p(iTCO_wdt_remove),
    .shutdown       = iTCO_wdt_shutdown,
    .suspend        = iTCO_wdt_suspend,
    .resume         = iTCO_wdt_resume,
    .driver         = {
        .owner  = THIS_MODULE,
        .name   = DRV_NAME,
    },
};

static int __init iTCO_wdt_init_module(void)
{
    int err;

    pr_info("Intel TCO WatchDog Timer Driver v%s\n", DRV_VERSION);

    err = platform_driver_register(&iTCO_wdt_driver);
    if (err)
        return err;

    return 0;
}
예제 #6
0
파일: iTCO_wdt.c 프로젝트: AK101111/linux
static void iTCO_wdt_shutdown(struct platform_device *dev)
{
	iTCO_wdt_stop(NULL);
}
예제 #7
0
파일: iTCO_wdt.c 프로젝트: AK101111/linux
static int iTCO_wdt_probe(struct platform_device *dev)
{
	int ret = -ENODEV;
	unsigned long val32;
	struct itco_wdt_platform_data *pdata = dev_get_platdata(&dev->dev);

	if (!pdata)
		goto out;

	spin_lock_init(&iTCO_wdt_private.io_lock);

	iTCO_wdt_private.tco_res =
		platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO);
	if (!iTCO_wdt_private.tco_res)
		goto out;

	iTCO_wdt_private.smi_res =
		platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI);
	if (!iTCO_wdt_private.smi_res)
		goto out;

	iTCO_wdt_private.iTCO_version = pdata->version;
	iTCO_wdt_private.dev = dev;
	iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);

	/*
	 * Get the Memory-Mapped GCS or PMC register, we need it for the
	 * NO_REBOOT flag (TCO v2 and v3).
	 */
	if (iTCO_wdt_private.iTCO_version >= 2) {
		iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev,
							IORESOURCE_MEM,
							ICH_RES_MEM_GCS_PMC);

		if (!iTCO_wdt_private.gcs_pmc_res)
			goto out;

		if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
			resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) {
			ret = -EBUSY;
			goto out;
		}
		iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start,
			resource_size(iTCO_wdt_private.gcs_pmc_res));
		if (!iTCO_wdt_private.gcs_pmc) {
			ret = -EIO;
			goto unreg_gcs_pmc;
		}
	}

	/* Check chipset's NO_REBOOT bit */
	if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
		pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
		ret = -ENODEV;	/* Cannot reset NO_REBOOT bit */
		goto unmap_gcs_pmc;
	}

	/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
	iTCO_wdt_set_NO_REBOOT_bit();

	/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
	if (!request_region(iTCO_wdt_private.smi_res->start,
			resource_size(iTCO_wdt_private.smi_res), dev->name)) {
		pr_err("I/O address 0x%04llx already in use, device disabled\n",
		       (u64)SMI_EN);
		ret = -EBUSY;
		goto unmap_gcs_pmc;
	}
	if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
		/*
		 * Bit 13: TCO_EN -> 0
		 * Disables TCO logic generating an SMI#
		 */
		val32 = inl(SMI_EN);
		val32 &= 0xffffdfff;	/* Turn off SMI clearing watchdog */
		outl(val32, SMI_EN);
	}

	if (!request_region(iTCO_wdt_private.tco_res->start,
			resource_size(iTCO_wdt_private.tco_res), dev->name)) {
		pr_err("I/O address 0x%04llx already in use, device disabled\n",
		       (u64)TCOBASE);
		ret = -EBUSY;
		goto unreg_smi;
	}

	pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n",
		pdata->name, pdata->version, (u64)TCOBASE);

	/* Clear out the (probably old) status */
	switch (iTCO_wdt_private.iTCO_version) {
	case 5:
	case 4:
		outw(0x0008, TCO1_STS);	/* Clear the Time Out Status bit */
		outw(0x0002, TCO2_STS);	/* Clear SECOND_TO_STS bit */
		break;
	case 3:
		outl(0x20008, TCO1_STS);
		break;
	case 2:
	case 1:
	default:
		outw(0x0008, TCO1_STS);	/* Clear the Time Out Status bit */
		outw(0x0002, TCO2_STS);	/* Clear SECOND_TO_STS bit */
		outw(0x0004, TCO2_STS);	/* Clear BOOT_STS bit */
		break;
	}

	iTCO_wdt_watchdog_dev.bootstatus = 0;
	iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT;
	watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout);
	iTCO_wdt_watchdog_dev.parent = &dev->dev;

	/* Make sure the watchdog is not running */
	iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);

	/* Check that the heartbeat value is within it's range;
	   if not reset to the default */
	if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) {
		iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT);
		pr_info("timeout value out of range, using %d\n",
			WATCHDOG_TIMEOUT);
	}

	ret = watchdog_register_device(&iTCO_wdt_watchdog_dev);
	if (ret != 0) {
		pr_err("cannot register watchdog device (err=%d)\n", ret);
		goto unreg_tco;
	}

	pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
		heartbeat, nowayout);

	return 0;

unreg_tco:
	release_region(iTCO_wdt_private.tco_res->start,
			resource_size(iTCO_wdt_private.tco_res));
unreg_smi:
	release_region(iTCO_wdt_private.smi_res->start,
			resource_size(iTCO_wdt_private.smi_res));
unmap_gcs_pmc:
	if (iTCO_wdt_private.iTCO_version >= 2)
		iounmap(iTCO_wdt_private.gcs_pmc);
unreg_gcs_pmc:
	if (iTCO_wdt_private.iTCO_version >= 2)
		release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
				resource_size(iTCO_wdt_private.gcs_pmc_res));
out:
	iTCO_wdt_private.tco_res = NULL;
	iTCO_wdt_private.smi_res = NULL;
	iTCO_wdt_private.gcs_pmc_res = NULL;
	iTCO_wdt_private.gcs_pmc = NULL;

	return ret;
}
static int iTCO_wdt_suspend(struct platform_device *dev, pm_message_t state)
{
	return iTCO_wdt_stop();
}
static int iTCO_wdt_init(struct platform_device *pdev)
{
	int ret;
	u32 base_address;
	unsigned long val32;
	struct pci_dev *parent;
	struct resource *irq;

	if (!pdev->dev.parent || !dev_is_pci(pdev->dev.parent)) {
		pr_err("Unqualified parent device.\n");
		return -EINVAL;
	}

	parent = to_pci_dev(pdev->dev.parent);

	/*
	 *      Find the ACPI/PM base I/O address which is the base
	 *      for the TCO registers (TCOBASE=ACPIBASE + 0x60)
	 *      ACPIBASE is bits [15:7] from 0x40-0x43
	 */
	pci_read_config_dword(parent, 0x40, &base_address);
	base_address &= 0x0000ff80;
	if (base_address == 0x00000000) {
		/* Something's wrong here, ACPIBASE has to be set */
		pr_err("failed to get TCOBASE address, device disabled by hardware/BIOS\n");
		return -ENODEV;
	}
	iTCO_wdt_private.ACPIBASE = base_address;

	pci_read_config_dword(parent, 0x44, &pmc_base_address);
	pmc_base_address &= 0xFFFFFE00;

	/*
	 * Disable watchdog on command-line demand
	 */
	if (strstr(saved_command_line, "disable_kernel_watchdog=1")) {
		pr_warn("disable_kernel_watchdog=1 watchdog will not be started\n");
		iTCO_wdt_private.enable = false;
		/* Set the NO_REBOOT bit to prevent later reboots */
		iTCO_wdt_set_NO_REBOOT_bit();
		/* Ensure Wdt is well stopped in case started by IAFW */
		iTCO_wdt_stop();
	} else {
		iTCO_wdt_private.enable = true;
		/* Check chipset's NO_REBOOT bit */
		if (iTCO_wdt_unset_NO_REBOOT_bit()) {
			pr_err("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
			ret = -ENODEV;	/* Cannot reset NO_REBOOT bit */
			goto out;
		}
	}

	/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
	if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
		pr_err("I/O address 0x%04lx already in use, device disabled\n",
		       SMI_EN);
		ret = -EIO;
		goto out;
	}
	if (!request_region(SMI_STS, 4,	"iTCO_wdt")) {
		pr_err("I/O address 0x%04lx already in use, device disabled\n",
				SMI_STS);
		ret = -EIO;
		goto unreg_smi_sts;
	}

	/* The TCO I/O registers reside in a 32-byte range pointed to
	   by the TCOBASE value */
	if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
		pr_err("I/O address 0x%04lx already in use, device disabled\n",
		       TCOBASE);
		ret = -EIO;
		goto unreg_smi_en;
	}

	pr_info("Found a TCO device (TCOBASE=0x%04lx)\n", TCOBASE);

	/* Check that the heartbeat value is within it's range;
	   if not reset to the default */
	if (iTCO_wdt_set_heartbeat(heartbeat)) {
		iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT);
		pr_info("timeout value out of range, using %d\n", heartbeat);
	}

	ret = misc_register(&iTCO_wdt_miscdev);
	if (ret != 0) {
		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
		       WATCHDOG_MINOR, ret);
		goto unreg_region;
	}

	pr_info("initialized. heartbeat=%d sec (nowayout=%d) policy=0x%x\n",
		heartbeat, nowayout, iTCO_wdt_get_current_ospolicy());

	/* Reset OS policy */
	iTCO_wdt_set_reset_type(TCO_POLICY_NORM);

	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (!irq) {
		pr_err("No warning interrupt resource found\n");
		goto misc_unreg;
	}

	ret = acpi_register_gsi(NULL, irq->start,
				irq->flags & (IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE)
				? ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE,
				irq->flags & (IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_HIGHLEVEL)
				? ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW);
	if (ret < 0) {
		pr_err("failed to configure TCO warning IRQ %d\n", (int)irq->start);
		goto misc_unreg;
	}

	ret = request_irq(irq->start, tco_irq_handler, 0, "tco_watchdog", NULL);
	if (ret < 0) {
		pr_err("failed to request TCO warning IRQ %d\n", (int)irq->start);
		goto gsi_unreg;
	}

	/* Clear old TCO timeout status */
	val32 = TCO_TIMEOUT_BIT | SECOND_TO_STS_BIT;
	outl(val32, TCO1_STS);
	/* Clear the SMI status */
	outl(TCO_STS_BIT, SMI_STS);

	/* Enable SMI for TCO */
	val32 = inl(SMI_EN);
	val32 |= TCO_EN_BIT;
	outl(val32, SMI_EN);
	/* then ensure that PMC is ready to handle next SMI */
	val32 |= EOS_BIT;
	outl(val32, SMI_EN);

	reboot_notifier.notifier_call = TCO_reboot_notifier;
	reboot_notifier.priority = 1;
	ret = register_reboot_notifier(&reboot_notifier);
	if (ret)
		/* We continue as reboot notifier is not critical for
			 * watchdog */
		pr_err("cannot register reboot notifier %d\n", ret);

	return 0;

gsi_unreg:
	acpi_unregister_gsi((int)(irq->start));
misc_unreg:
	misc_deregister(&iTCO_wdt_miscdev);
unreg_region:
	release_region(TCOBASE, 0x20);
unreg_smi_sts:
	release_region(SMI_STS, 4);
unreg_smi_en:
	release_region(SMI_EN, 4);
out:
	iTCO_wdt_private.ACPIBASE = 0;
	return ret;
}
static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd,
							unsigned long arg)
{
	int new_options, retval = -EINVAL;
	int new_heartbeat;
	void __user *argp = (void __user *)arg;
	int __user *p = argp;
	static const struct watchdog_info ident = {
		.options =		WDIOF_SETTIMEOUT |
					WDIOF_KEEPALIVEPING |
					WDIOF_MAGICCLOSE,
		.firmware_version =	0,
		.identity =		DRV_NAME,
	};

	switch (cmd) {
	case WDIOC_GETSUPPORT:
		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
	case WDIOC_GETSTATUS:
	case WDIOC_GETBOOTSTATUS:
		return put_user(0, p);

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

		if (new_options & WDIOS_DISABLECARD) {
			iTCO_wdt_stop();
			retval = 0;
		}
		if (new_options & WDIOS_ENABLECARD) {
			iTCO_wdt_keepalive();
			iTCO_wdt_start();
			retval = 0;
		}
		return retval;
	}
	case WDIOC_KEEPALIVE:
		iTCO_wdt_keepalive();
		return 0;

	case WDIOC_SETTIMEOUT:
	{
		if (get_user(new_heartbeat, p))
			return -EFAULT;
		if (iTCO_wdt_set_heartbeat(new_heartbeat))
			return -EINVAL;
		iTCO_wdt_keepalive();
		/* Fall */
	}
	case WDIOC_GETTIMEOUT:
		return put_user(heartbeat, p);
	case WDIOC_GETTIMELEFT:
	{
		int time_left;
		if (iTCO_wdt_get_timeleft(&time_left))
			return -EINVAL;
		return put_user(time_left, p);
	}
	default:
		return -ENOTTY;
	}
}

/*
 *	Kernel Interfaces
 */

static const struct file_operations iTCO_wdt_fops = {
	.owner =		THIS_MODULE,
	.llseek =		no_llseek,
	.write =		iTCO_wdt_write,
	.unlocked_ioctl =	iTCO_wdt_ioctl,
	.open =			iTCO_wdt_open,
	.release =		iTCO_wdt_release,
};

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

/*
 *	Reboot notifier
 */

static int TCO_reboot_notifier(struct notifier_block *this,
			   unsigned long code,
			   void *another_unused)
{
	if (code == SYS_HALT || code == SYS_POWER_OFF) {
		iTCO_wdt_set_reset_type(TCO_POLICY_HALT);
	}

	iTCO_wdt_last_kick(5);

#ifdef CONFIG_DEBUG_FS
	if (iTCO_wdt_private.panic_reboot_notifier) {
		BUG();
	}
#endif

	return NOTIFY_DONE;
}

static irqreturn_t tco_irq_handler(int irq, void *arg)
{
	pr_warn("[SHTDWN] %s, WATCHDOG TIMEOUT HANDLER!\n", __func__);

	/* reduce the timeout to the minimum, but sufficient for tracing */
	bypass_keepalive = false;
	iTCO_wdt_last_kick(15);

	trigger_all_cpu_backtrace();

	/* Let the watchdog reset the board */
	panic_timeout = 0;
	panic("Kernel Watchdog");

	/* This code should not be reached */

	return IRQ_HANDLED;
}


static ssize_t shutdown_ongoing_store(struct device *dev,
			struct device_attribute *attr, const char *buf, size_t size)
{
	iTCO_wdt_set_reset_type(TCO_POLICY_HALT);
	return size;
}
static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd,
							unsigned long arg)
{
	int new_options, retval = -EINVAL;
	int new_heartbeat;
	void __user *argp = (void __user *)arg;
	int __user *p = argp;
	static const struct watchdog_info ident = {
		.options =		WDIOF_SETTIMEOUT |
					WDIOF_KEEPALIVEPING |
					WDIOF_MAGICCLOSE,
		.firmware_version =	0,
		.identity =		DRV_NAME,
	};

	switch (cmd) {
	case WDIOC_GETSUPPORT:
		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
	case WDIOC_GETSTATUS:
	case WDIOC_GETBOOTSTATUS:
		return put_user(0, p);

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

		if (new_options & WDIOS_DISABLECARD) {
			iTCO_wdt_stop();
			retval = 0;
		}
		if (new_options & WDIOS_ENABLECARD) {
			iTCO_wdt_keepalive();
			iTCO_wdt_start();
			retval = 0;
		}
		return retval;
	}
	case WDIOC_KEEPALIVE:
		iTCO_wdt_keepalive();
		return 0;

	case WDIOC_SETTIMEOUT:
	{
		if (get_user(new_heartbeat, p))
			return -EFAULT;
		if (iTCO_wdt_set_heartbeat(new_heartbeat))
			return -EINVAL;
		iTCO_wdt_keepalive();
		/* Fall */
	}
	case WDIOC_GETTIMEOUT:
		return put_user(heartbeat, p);
	case WDIOC_GETTIMELEFT:
	{
		int time_left;
		if (iTCO_wdt_get_timeleft(&time_left))
			return -EINVAL;
		return put_user(time_left, p);
	}
	default:
		return -ENOTTY;
	}
}

/*
 *	Kernel Interfaces
 */

static const struct file_operations iTCO_wdt_fops = {
	.owner =		THIS_MODULE,
	.llseek =		no_llseek,
	.write =		iTCO_wdt_write,
	.unlocked_ioctl =	iTCO_wdt_ioctl,
	.open =			iTCO_wdt_open,
	.release =		iTCO_wdt_release,
};

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

/*
 *	Reboot notifier
 */

static int TCO_reboot_notifier(struct notifier_block *this,
			   unsigned long code,
			   void *another_unused)
{
	if (code == SYS_HALT || code == SYS_POWER_OFF) {
		iTCO_wdt_set_reset_type(TCO_POLICY_HALT);
	}

	iTCO_wdt_last_kick(5);

#ifdef CONFIG_DEBUG_FS
	if (iTCO_wdt_private.panic_reboot_notifier) {
		BUG();
	}
#endif

	return NOTIFY_DONE;
}

static irqreturn_t tco_irq_handler(int irq, void *arg)
{
	unsigned long val32;

	pr_warn("[SHTDWN] %s, WATCHDOG TIMEOUT HANDLER!\n", __func__);

	/* reduce the timeout to the minimum, but sufficient for tracing */
	iTCO_wdt_set_heartbeat(15);
	iTCO_wdt_keepalive();

	trigger_all_cpu_backtrace();
	panic("Kernel Watchdog");

	/* This code should not be reached */

	return IRQ_HANDLED;
}

/*
 *	Init & exit routines
 */

static int iTCO_wdt_init(struct pci_dev *pdev,
		const struct pci_device_id *ent, struct platform_device *dev)
{
	int ret;
	u32 base_address;
	unsigned long val32;

	/*
	 *      Find the ACPI/PM base I/O address which is the base
	 *      for the TCO registers (TCOBASE=ACPIBASE + 0x60)
	 *      ACPIBASE is bits [15:7] from 0x40-0x43
	 */
	pci_read_config_dword(pdev, 0x40, &base_address);
	base_address &= 0x0000ff80;
	if (base_address == 0x00000000) {
		/* Something's wrong here, ACPIBASE has to be set */
		pr_err("failed to get TCOBASE address, device disabled by hardware/BIOS\n");
		return -ENODEV;
	}
	iTCO_wdt_private.iTCO_version =
			iTCO_chipset_info[ent->driver_data].iTCO_version;
	iTCO_wdt_private.ACPIBASE = base_address;
	iTCO_wdt_private.pdev = pdev;

	pci_read_config_dword(pdev, 0x44, &pmc_base_address);
	pmc_base_address &= 0xFFFFFE00;

	/*
	 * Disable watchdog on command-line demand
	 */
	if (strstr(saved_command_line, "disable_kernel_watchdog=1")) {
		pr_warn("disable_kernel_watchdog=1 watchdog will not be started\n");
		iTCO_wdt_private.enable = false;
		/* Set the NO_REBOOT bit to prevent later reboots */
		iTCO_wdt_set_NO_REBOOT_bit();
		/* Ensure Wdt is well stopped in case started by IAFW */
		iTCO_wdt_stop();
	} else {
		iTCO_wdt_private.enable = true;
		/* Check chipset's NO_REBOOT bit */
		if (iTCO_wdt_unset_NO_REBOOT_bit()) {
			pr_err("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
			ret = -ENODEV;	/* Cannot reset NO_REBOOT bit */
			goto out;
		}
	}

	/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
	if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
		pr_err("I/O address 0x%04lx already in use, device disabled\n",
		       SMI_EN);
		ret = -EIO;
		goto out;
	}
	if (!request_region(SMI_STS, 4,	"iTCO_wdt")) {
		pr_err("I/O address 0x%04lx already in use, device disabled\n",
				SMI_STS);
		ret = -EIO;
		goto unreg_smi_sts;
	}

	if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
		/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
		val32 = inl(SMI_EN);
		val32 &= ~TCO_EN_BIT;	/* Turn off TCO watchdog timer */
		outl(val32, SMI_EN);
	}

	/* The TCO I/O registers reside in a 32-byte range pointed to
	   by the TCOBASE value */
	if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
		pr_err("I/O address 0x%04lx already in use, device disabled\n",
		       TCOBASE);
		ret = -EIO;
		goto unreg_smi_en;
	}

	pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n",
		iTCO_chipset_info[ent->driver_data].name,
		iTCO_chipset_info[ent->driver_data].iTCO_version,
		TCOBASE);

	/* Check that the heartbeat value is within it's range;
	   if not reset to the default */
	if (iTCO_wdt_set_heartbeat(heartbeat)) {
		iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT);
		pr_info("timeout value out of range, using %d\n", heartbeat);
	}

	ret = misc_register(&iTCO_wdt_miscdev);
	if (ret != 0) {
		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
		       WATCHDOG_MINOR, ret);
		goto unreg_region;
	}

	pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
		heartbeat, nowayout);

	/* Reset OS policy */
	iTCO_wdt_set_reset_type(TCO_POLICY_NORM);

	ret = acpi_register_gsi(NULL, TCO_WARNING_IRQ,
				ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_HIGH);
	if (ret < 0) {
		pr_err("failed to configure TCO warning IRQ %d\n", TCO_WARNING_IRQ);
		goto misc_unreg;
	}
	ret = request_irq(TCO_WARNING_IRQ, tco_irq_handler, 0, "tco_watchdog", NULL);
	if (ret < 0) {
		pr_err("failed to request TCO warning IRQ %d\n", TCO_WARNING_IRQ);
		goto gsi_unreg;
	}

	/* Clear old TCO timeout status */
	val32 = TCO_TIMEOUT_BIT | SECOND_TO_STS_BIT;
	outl(val32, TCO1_STS);
	/* Clear the SMI status */
	outl(TCO_STS_BIT, SMI_STS);

	/* Enable SMI for TCO */
	val32 = inl(SMI_EN);
	val32 |= TCO_EN_BIT;
	outl(val32, SMI_EN);
	/* then ensure that PMC is ready to handle next SMI */
	val32 |= EOS_BIT;
	outl(val32, SMI_EN);

	reboot_notifier.notifier_call = TCO_reboot_notifier;
	reboot_notifier.priority = 1;
	ret = register_reboot_notifier(&reboot_notifier);
	if (ret)
		/* We continue as reboot notifier is not critical for
			 * watchdog */
		pr_err("cannot register reboot notifier %d\n", ret);

	return 0;

gsi_unreg:
	acpi_unregister_gsi(TCO_WARNING_IRQ);
misc_unreg:
	misc_deregister(&iTCO_wdt_miscdev);
unreg_region:
	release_region(TCOBASE, 0x20);
unreg_smi_sts:
	release_region(SMI_STS, 4);
unreg_smi_en:
	release_region(SMI_EN, 4);
out:
	iTCO_wdt_private.ACPIBASE = 0;
	return ret;
}