Esempio n. 1
0
static void
pnpacpi_parse_allocated_irqresource(struct pnp_resource_table *res, u32 gsi,
	int triggering, int polarity, int shareable)
{
	int i = 0;
	int irq;

	if (!valid_IRQ(gsi))
		return;

	while (!(res->irq_resource[i].flags & IORESOURCE_UNSET) &&
			i < PNP_MAX_IRQ)
		i++;
	if (i >= PNP_MAX_IRQ)
		return;

	res->irq_resource[i].flags = IORESOURCE_IRQ;  // Also clears _UNSET flag
	irq = acpi_register_gsi(gsi, triggering, polarity);
	if (irq < 0) {
		res->irq_resource[i].flags |= IORESOURCE_DISABLED;
		return;
	}

	if (shareable)
		res->irq_resource[i].flags |= IORESOURCE_IRQ_SHAREABLE;

	res->irq_resource[i].start = irq;
	res->irq_resource[i].end = irq;
	pcibios_penalize_isa_irq(irq, 1);
}
Esempio n. 2
0
static void pnpacpi_parse_allocated_irqresource(struct pnp_dev *dev,
						u32 gsi, int triggering,
						int polarity, int shareable)
{
	int irq, flags;
	int p, t;

	if (!valid_IRQ(gsi)) {
		pnp_add_irq_resource(dev, gsi, IORESOURCE_DISABLED);
		return;
	}

	
	if (!acpi_get_override_irq(gsi, &t, &p)) {
		t = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
		p = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH;

		if (triggering != t || polarity != p) {
			dev_warn(&dev->dev, "IRQ %d override to %s, %s\n",
				gsi, t ? "edge":"level", p ? "low":"high");
			triggering = t;
			polarity = p;
		}
	}

	flags = irq_flags(triggering, polarity, shareable);
	irq = acpi_register_gsi(&dev->dev, gsi, triggering, polarity);
	if (irq >= 0)
		pcibios_penalize_isa_irq(irq, 1);
	else
		flags |= IORESOURCE_DISABLED;

	pnp_add_irq_resource(dev, irq, flags);
}
Esempio n. 3
0
static void
pnpacpi_parse_allocated_irqresource(struct pnp_resource_table * res, u32 gsi,
	int edge_level, int active_high_low)
{
	int i = 0;
	int irq;

	if (!valid_IRQ(gsi))
		return;

	while (!(res->irq_resource[i].flags & IORESOURCE_UNSET) &&
			i < PNP_MAX_IRQ)
		i++;
	if (i >= PNP_MAX_IRQ)
		return;

	res->irq_resource[i].flags = IORESOURCE_IRQ;  // Also clears _UNSET flag
	irq = acpi_register_gsi(gsi, edge_level, active_high_low);
	if (irq < 0) {
		res->irq_resource[i].flags |= IORESOURCE_DISABLED;
		return;
	}

	res->irq_resource[i].start = irq;
	res->irq_resource[i].end = irq;
	pcibios_penalize_isa_irq(irq, 1);
}
Esempio n. 4
0
static int __init acpi_csrt_parse_shared_info(struct platform_device *pdev,
        const struct acpi_csrt_group *grp)
{
    const struct acpi_csrt_shared_info *si;
    struct resource res[3];
    size_t nres;
    int ret;

    memset(res, 0, sizeof(res));
    nres = 0;

    si = (const struct acpi_csrt_shared_info *)&grp[1];
    /*
     * The peripherals that are listed on CSRT typically support only
     * 32-bit addresses so we only use the low part of MMIO base for
     * now.
     */
    if (!si->mmio_base_high && si->mmio_base_low) {
        /*
         * There is no size of the memory resource in shared_info
         * so we assume that it is 4k here.
         */
        res[nres].start = si->mmio_base_low;
        res[nres].end = res[0].start + SZ_4K - 1;
        res[nres++].flags = IORESOURCE_MEM;
    }

    if (si->gsi_interrupt) {
        int irq = acpi_register_gsi(NULL, si->gsi_interrupt,
                                    si->interrupt_mode,
                                    si->interrupt_polarity);
        res[nres].start = irq;
        res[nres].end = irq;
        res[nres++].flags = IORESOURCE_IRQ;
    }

    if (si->base_request_line || si->num_handshake_signals) {
        /*
         * We pass the driver a DMA resource describing the range
         * of request lines the device supports.
         */
        res[nres].start = si->base_request_line;
        res[nres].end = res[nres].start + si->num_handshake_signals - 1;
        res[nres++].flags = IORESOURCE_DMA;
    }

    ret = platform_device_add_resources(pdev, res, nres);
    if (ret) {
        if (si->gsi_interrupt)
            acpi_unregister_gsi(si->gsi_interrupt);
        return ret;
    }

    return 0;
}
Esempio n. 5
0
static int __init acpi_parse_fadt(struct acpi_table_header *table)
{
	struct acpi_table_header *fadt_header;
	struct acpi_table_fadt *fadt;

	fadt_header = (struct acpi_table_header *)table;
	if (fadt_header->revision != 3)
		return -ENODEV;	/* Only deal with ACPI 2.0 FADT */

	fadt = (struct acpi_table_fadt *)fadt_header;

	acpi_register_gsi(NULL, fadt->sci_interrupt, ACPI_LEVEL_SENSITIVE,
				 ACPI_ACTIVE_LOW);
	return 0;
}
Esempio n. 6
0
static int __init map_gt_gsi(u32 interrupt, u32 flags)
{
	int trigger, polarity;

	if (!interrupt)
		return 0;

	trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
			: ACPI_LEVEL_SENSITIVE;

	polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
			: ACPI_ACTIVE_HIGH;

	return acpi_register_gsi(NULL, interrupt, trigger, polarity);
}
Esempio n. 7
0
static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
				     u8 triggering, u8 polarity, u8 shareable,
				     bool legacy)
{
	int irq, p, t;

	if (!valid_IRQ(gsi)) {
		acpi_dev_irqresource_disabled(res, gsi);
		return;
	}

	/*
	 * In IO-APIC mode, use overrided attribute. Two reasons:
	 * 1. BIOS bug in DSDT
	 * 2. BIOS uses IO-APIC mode Interrupt Source Override
	 *
	 * We do this only if we are dealing with IRQ() or IRQNoFlags()
	 * resource (the legacy ISA resources). With modern ACPI 5 devices
	 * using extended IRQ descriptors we take the IRQ configuration
	 * from _CRS directly.
	 */
	if (legacy && !acpi_get_override_irq(gsi, &t, &p)) {
		u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
		u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH;

		if (triggering != trig || polarity != pol) {
			pr_warning("ACPI: IRQ %d override to %s, %s\n", gsi,
				   t ? "level" : "edge", p ? "low" : "high");
			triggering = trig;
			polarity = pol;
		}
	}

	res->flags = acpi_dev_irq_flags(triggering, polarity, shareable);
	irq = acpi_register_gsi(NULL, gsi, triggering, polarity);
	if (irq >= 0) {
		res->start = irq;
		res->end = irq;
	} else {
		acpi_dev_irqresource_disabled(res, gsi);
	}
}
Esempio n. 8
0
static int arm_pmu_acpi_register_irq(int cpu)
{
	struct acpi_madt_generic_interrupt *gicc;
	int gsi, trigger;

	gicc = acpi_cpu_get_madt_gicc(cpu);
	if (WARN_ON(!gicc))
		return -EINVAL;

	gsi = gicc->performance_interrupt;

	/*
	 * Per the ACPI spec, the MADT cannot describe a PMU that doesn't
	 * have an interrupt. QEMU advertises this by using a GSI of zero,
	 * which is not known to be valid on any hardware despite being
	 * valid per the spec. Take the pragmatic approach and reject a
	 * GSI of zero for now.
	 */
	if (!gsi)
		return 0;

	if (gicc->flags & ACPI_MADT_PERFORMANCE_IRQ_MODE)
		trigger = ACPI_EDGE_SENSITIVE;
	else
		trigger = ACPI_LEVEL_SENSITIVE;

	/*
	 * Helpfully, the MADT GICC doesn't have a polarity flag for the
	 * "performance interrupt". Luckily, on compliant GICs the polarity is
	 * a fixed value in HW (for both SPIs and PPIs) that we cannot change
	 * from SW.
	 *
	 * Here we pass in ACPI_ACTIVE_HIGH to keep the core code happy. This
	 * may not match the real polarity, but that should not matter.
	 *
	 * Other interrupt controllers are not supported with ACPI.
	 */
	return acpi_register_gsi(NULL, gsi, trigger, ACPI_ACTIVE_HIGH);
}
Esempio n. 9
0
static int __init acpi_parse_fadt(unsigned long phys_addr, unsigned long size)
{
	struct acpi_table_header *fadt_header;
	struct fadt_descriptor *fadt;

	if (!phys_addr || !size)
		return -EINVAL;

	fadt_header = (struct acpi_table_header *)__va(phys_addr);
	if (fadt_header->revision != 3)
		return -ENODEV;	/* Only deal with ACPI 2.0 FADT */

	fadt = (struct fadt_descriptor *)fadt_header;

	if (!(fadt->iapc_boot_arch & BAF_8042_KEYBOARD_CONTROLLER))
		acpi_kbd_controller_present = 0;

	if (fadt->iapc_boot_arch & BAF_LEGACY_DEVICES)
		acpi_legacy_devices = 1;

	acpi_register_gsi(fadt->sci_int, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW);
	return 0;
}
Esempio n. 10
0
static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
	void *data)
{
	struct pnp_resource_table * res_table = (struct pnp_resource_table *)data;

	switch (res->id) {
	case ACPI_RSTYPE_IRQ:
		if ((res->data.irq.number_of_interrupts > 0) &&
			valid_IRQ(res->data.irq.interrupts[0])) {
			pnpacpi_parse_allocated_irqresource(res_table, 
				acpi_register_gsi(res->data.irq.interrupts[0],
					res->data.irq.edge_level,
					res->data.irq.active_high_low));
			pcibios_penalize_isa_irq(res->data.irq.interrupts[0]);
		}
		break;

	case ACPI_RSTYPE_EXT_IRQ:
		if ((res->data.extended_irq.number_of_interrupts > 0) &&
			valid_IRQ(res->data.extended_irq.interrupts[0])) {
			pnpacpi_parse_allocated_irqresource(res_table, 
				acpi_register_gsi(res->data.extended_irq.interrupts[0],
					res->data.extended_irq.edge_level,
					res->data.extended_irq.active_high_low));
			pcibios_penalize_isa_irq(res->data.extended_irq.interrupts[0]);
		}
		break;
	case ACPI_RSTYPE_DMA:
		if (res->data.dma.number_of_channels > 0)
			pnpacpi_parse_allocated_dmaresource(res_table, 
					res->data.dma.channels[0]);
		break;
	case ACPI_RSTYPE_IO:
		pnpacpi_parse_allocated_ioresource(res_table, 
				res->data.io.min_base_address, 
				res->data.io.range_length);
		break;
	case ACPI_RSTYPE_FIXED_IO:
		pnpacpi_parse_allocated_ioresource(res_table, 
				res->data.fixed_io.base_address, 
				res->data.fixed_io.range_length);
		break;
	case ACPI_RSTYPE_MEM24:
		pnpacpi_parse_allocated_memresource(res_table, 
				res->data.memory24.min_base_address, 
				res->data.memory24.range_length);
		break;
	case ACPI_RSTYPE_MEM32:
		pnpacpi_parse_allocated_memresource(res_table, 
				res->data.memory32.min_base_address, 
				res->data.memory32.range_length);
		break;
	case ACPI_RSTYPE_FIXED_MEM32:
		pnpacpi_parse_allocated_memresource(res_table, 
				res->data.fixed_memory32.range_base_address, 
				res->data.fixed_memory32.range_length);
		break;
	case ACPI_RSTYPE_ADDRESS16:
		pnpacpi_parse_allocated_memresource(res_table, 
				res->data.address16.min_address_range, 
				res->data.address16.address_length);
		break;
	case ACPI_RSTYPE_ADDRESS32:
		pnpacpi_parse_allocated_memresource(res_table, 
				res->data.address32.min_address_range, 
				res->data.address32.address_length);
		break;
	case ACPI_RSTYPE_ADDRESS64:
		pnpacpi_parse_allocated_memresource(res_table, 
		res->data.address64.min_address_range, 
		res->data.address64.address_length);
		break;
	default:
		pnp_warn("PnPACPI: Alloc type : %d not handle", 
				res->id);
		return AE_ERROR;
	}
			
	return AE_OK;
}
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)
{
	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;
}