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); }
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); }
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); }
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; }
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; }
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); }
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); } }
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); }
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; }
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; }