Ejemplo n.º 1
0
Archivo: ltp_tpci.c Proyecto: 1587/ltp
/*
 * test_match_device
 *	make call to pci_match_device, returns a
 *	pci_device_id pointer
 */
static int test_match_device(void)
{
	struct pci_dev *dev = ltp_pci.dev;
	struct pci_driver *drv;
	const struct pci_device_id *id;

	prk_info("test pci_device_id()");

	drv = pci_dev_driver(dev);

	if (!drv) {
		prk_err("driver pointer not allocated for pci_dev");
		return TFAIL;
	}

	id = pci_match_id(drv->id_table, dev);

	if (id) {
		prk_info("match device success");
		return TPASS;
	}

	prk_err("failed return pci_device_id");
	return TFAIL;
}
static int __init pmc_atom_init(void)
{
	int err = -ENODEV;
	struct pci_dev *pdev = NULL;
	const struct pci_device_id *ent;

	/* We look for our device - PCU PMC
	 * we assume that there is max. one device.
	 *
	 * We can't use plain pci_driver mechanism,
	 * as the device is really a multiple function device,
	 * main driver that binds to the pci_device is lpc_ich
	 * and have to find & bind to the device this way.
	 */
	for_each_pci_dev(pdev) {
		ent = pci_match_id(pmc_pci_ids, pdev);
		if (ent) {
			err = pmc_setup_dev(pdev);
			goto out;
		}
	}
	/* Device not found. */
out:
	return err;
}
Ejemplo n.º 3
0
static int ohci_pci_reset (struct usb_hcd *hcd)
{
	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
	int ret = 0;

	if (hcd->self.controller) {
		const struct pci_device_id *quirk_id;

		quirk_id = pci_match_id(ohci_pci_quirks, pdev);
		if (quirk_id != NULL) {
			int (*quirk)(struct usb_hcd *ohci);
			quirk = (void *)quirk_id->driver_data;
			ret = quirk(hcd);
		}
	}

	if (ret == 0)
		ret = ohci_setup(hcd);
	/*
	* After ohci setup RWC may not be set for add-in PCI cards.
	* This transfers PCI PM wakeup capabilities.
	*/
	if (device_can_wakeup(&pdev->dev))
		ohci->hc_control |= OHCI_CTRL_RWC;
	return ret;
}
Ejemplo n.º 4
0
Archivo: led.c Proyecto: 3null/linux
int ath5k_init_leds(struct ath5k_hw *ah)
{
	int ret = 0;
	struct ieee80211_hw *hw = ah->hw;
	struct pci_dev *pdev = ah->pdev;
	char name[ATH5K_LED_MAX_NAME_LEN + 1];
	const struct pci_device_id *match;

	if (!ah->pdev)
		return 0;

	match = pci_match_id(&ath5k_led_devices[0], pdev);
	if (match) {
		__set_bit(ATH_STAT_LEDSOFT, ah->status);
		ah->led_pin = ATH_PIN(match->driver_data);
		ah->led_on = ATH_POLARITY(match->driver_data);
	}

	if (!test_bit(ATH_STAT_LEDSOFT, ah->status))
		goto out;

	ath5k_led_enable(ah);

	snprintf(name, sizeof(name), "ath5k-%s::rx", wiphy_name(hw->wiphy));
	ret = ath5k_register_led(ah, &ah->rx_led, name,
		ieee80211_get_rx_led_name(hw));
	if (ret)
		goto out;

	snprintf(name, sizeof(name), "ath5k-%s::tx", wiphy_name(hw->wiphy));
	ret = ath5k_register_led(ah, &ah->tx_led, name,
		ieee80211_get_tx_led_name(hw));
out:
	return ret;
}
Ejemplo n.º 5
0
/*
 * Driver data implementation
 */
static const struct pci_device_id *nvidiabl_match_id(struct pci_dev *dev)
{
	/* Search id in table */
	const struct pci_device_id *id = pci_match_id(nvidiabl_device_table, dev);

	return id;
}
Ejemplo n.º 6
0
static struct pci_dev *next_k8_northbridge(struct pci_dev *dev)
{
	do {
		dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
		if (!dev)
			break;
	} while (!pci_match_id(&k8_nb_ids[0], dev));
	return dev;
}
Ejemplo n.º 7
0
static struct pci_dev *next_northbridge(struct pci_dev *dev,
					struct pci_device_id *ids)
{
	do {
		dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
		if (!dev)
			break;
	} while (!pci_match_id(ids, dev));
	return dev;
}
Ejemplo n.º 8
0
static int __init amd_gpio_init(void)
{
	int err = -ENODEV;
	struct pci_dev *pdev = NULL;
	const struct pci_device_id *ent;


	/* We look for our device - AMD South Bridge
	 * I don't know about a system with two such bridges,
	 * so we can assume that there is max. one device.
	 *
	 * We can't use plain pci_driver mechanism,
	 * as the device is really a multiple function device,
	 * main driver that binds to the pci_device is an smbus
	 * driver and have to find & bind to the device this way.
	 */
	for_each_pci_dev(pdev) {
		ent = pci_match_id(pci_tbl, pdev);
		if (ent)
			goto found;
	}
	/* Device not found. */
	goto out;

found:
	err = pci_read_config_dword(pdev, 0x58, &gp.pmbase);
	if (err)
		goto out;
	err = -EIO;
	gp.pmbase &= 0x0000FF00;
	if (gp.pmbase == 0)
		goto out;
	if (!request_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE, "AMD GPIO")) {
		dev_err(&pdev->dev, "AMD GPIO region 0x%x already in use!\n",
			gp.pmbase + PMBASE_OFFSET);
		err = -EBUSY;
		goto out;
	}
	gp.pm = ioport_map(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
	gp.pdev = pdev;
	gp.chip.dev = &pdev->dev;

	spin_lock_init(&gp.lock);

	printk(KERN_INFO "AMD-8111 GPIO detected\n");
	err = gpiochip_add(&gp.chip);
	if (err) {
		printk(KERN_ERR "GPIO registering failed (%d)\n",
		       err);
		release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
		goto out;
	}
out:
	return err;
}
Ejemplo n.º 9
0
int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc)
{
	const struct pci_device_id *id_table = umc_drv->match_data;
	struct pci_dev *pci;

	if (umc->dev.parent->bus != &pci_bus_type)
		return 0;

	pci = to_pci_dev(umc->dev.parent);
	return pci_match_id(id_table, pci) != NULL;
}
Ejemplo n.º 10
0
static int __init mod_init(void)
{
	int err = -ENODEV;
	struct pci_dev *pdev = NULL;
	const struct pci_device_id *ent;
	u32 pmbase;
	struct amd768_priv *priv;

	for_each_pci_dev(pdev) {
		ent = pci_match_id(pci_tbl, pdev);
		if (ent)
			goto found;
	}
	/* Device not found. */
	return -ENODEV;

found:
	err = pci_read_config_dword(pdev, 0x58, &pmbase);
	if (err)
		return err;

	pmbase &= 0x0000FF00;
	if (pmbase == 0)
		return -EIO;

	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	if (!devm_request_region(&pdev->dev, pmbase + PMBASE_OFFSET,
				PMBASE_SIZE, DRV_NAME)) {
		dev_err(&pdev->dev, DRV_NAME " region 0x%x already in use!\n",
			pmbase + 0xF0);
		return -EBUSY;
	}

	priv->iobase = devm_ioport_map(&pdev->dev, pmbase + PMBASE_OFFSET,
			PMBASE_SIZE);
	if (!priv->iobase) {
		pr_err(DRV_NAME "Cannot map ioport\n");
		return -ENOMEM;
	}

	amd_rng.priv = (unsigned long)priv;
	priv->pcidev = pdev;

	pr_info(DRV_NAME " detected\n");
	return devm_hwrng_register(&pdev->dev, &amd_rng);
}
Ejemplo n.º 11
0
static int gpio_apu2_probe (struct platform_device *dev)
{
	int ret = 0;
	int i;
	struct pci_dev *pci_dev = NULL;

	/* Match the PCI device */
	for_each_pci_dev (pci_dev) {
		if (pci_match_id (gpio_apu2_pci_tbl, pci_dev) != NULL) {
			gpio_apu2_pci = pci_dev;
			break;
		}
	}

	if (!gpio_apu2_pci)
		return -ENODEV;

	pr_info ("%s: PCI Revision ID: 0x%x\n", DEVNAME, gpio_apu2_pci->revision);

	/* Determine type of southbridge chipset */
	if (gpio_apu2_pci->revision < 0x40) {
		return -EACCES;
	}

	/* Request memory region for GPIO's */
	if (!devm_request_mem_region (&dev->dev, FCH_GPIO_BASE,
		FCH_GPIO_SIZE, DEVNAME)){
		pr_err ("%s: request GPIO mem region failed\n", DEVNAME);
		return -ENXIO;
	}

	/* Map IO's for GPIO's */
	for (i = 0; i < APU_NUM_GPIO; i++) {
		gpio_addr[i] = devm_ioremap (&dev->dev,
			FCH_GPIO_BASE + (gpio_offset[i] * sizeof (u32)), sizeof (u32));
		if (!gpio_addr[i]) {
			pr_err ("%s: map GPIO%d address failed\n", DEVNAME, gpio_offset[i]);
			return -ENXIO;
		}
	}

	gpio_apu2_chip.dev = &dev->dev;
	ret = gpiochip_add (&gpio_apu2_chip);
	if (ret) {
		pr_err ("%s: adding gpiochip failed\n", DEVNAME);
	}

	return ret;
}
Ejemplo n.º 12
0
static int __init mod_init(void)
{
	int err = -ENODEV;
	struct pci_dev *pdev = NULL;
	const struct pci_device_id *ent;
	u32 pmbase;

	for_each_pci_dev(pdev) {
		ent = pci_match_id(pci_tbl, pdev);
		if (ent)
			goto found;
	}
	/* Device not found. */
	goto out;

found:
	err = pci_read_config_dword(pdev, 0x58, &pmbase);
	if (err)
		goto out;
	err = -EIO;
	pmbase &= 0x0000FF00;
	if (pmbase == 0)
		goto out;
	if (!request_region(pmbase + 0xF0, 8, "AMD HWRNG")) {
		dev_err(&pdev->dev, "AMD HWRNG region 0x%x already in use!\n",
			pmbase + 0xF0);
		err = -EBUSY;
		goto out;
	}
	amd_rng.priv = (unsigned long)pmbase;
	amd_pdev = pdev;

	printk(KERN_INFO "AMD768 RNG detected\n");
	err = hwrng_register(&amd_rng);
	if (err) {
		printk(KERN_ERR PFX "RNG registering failed (%d)\n",
		       err);
		release_region(pmbase + 0xF0, 8);
		goto out;
	}
out:
	return err;
}
Ejemplo n.º 13
0
int ath5k_init_leds(struct ath5k_softc *sc)
{
	int ret = 0;
	struct ieee80211_hw *hw = sc->hw;
#ifndef CONFIG_ATHEROS_AR231X
	struct pci_dev *pdev = sc->pdev;
#endif
	char name[ATH5K_LED_MAX_NAME_LEN + 1];
	const struct pci_device_id *match;

	if (!sc->pdev)
		return 0;

#ifdef CONFIG_ATHEROS_AR231X
	match = NULL;
#else
	match = pci_match_id(&ath5k_led_devices[0], pdev);
#endif
	if (match) {
		__set_bit(ATH_STAT_LEDSOFT, sc->status);
		sc->led_pin = ATH_PIN(match->driver_data);
		sc->led_on = ATH_POLARITY(match->driver_data);
	}

	if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
		goto out;

	ath5k_led_enable(sc);

	snprintf(name, sizeof(name), "ath5k-%s::rx", wiphy_name(hw->wiphy));
	ret = ath5k_register_led(sc, &sc->rx_led, name,
		ieee80211_get_rx_led_name(hw));
	if (ret)
		goto out;

	snprintf(name, sizeof(name), "ath5k-%s::tx", wiphy_name(hw->wiphy));
	ret = ath5k_register_led(sc, &sc->tx_led, name,
		ieee80211_get_tx_led_name(hw));
out:
	return ret;
}
Ejemplo n.º 14
0
static int __init mod_init(void)
{
	int err = -ENODEV;
	struct pci_dev *pdev = NULL;
	const struct pci_device_id *ent;
	void __iomem *mem;
	unsigned long rng_base;

	for_each_pci_dev(pdev) {
		ent = pci_match_id(pci_tbl, pdev);
		if (ent)
			goto found;
	}
	
	goto out;

found:
	rng_base = pci_resource_start(pdev, 0);
	if (rng_base == 0)
		goto out;
	err = -ENOMEM;
	mem = ioremap(rng_base, 0x58);
	if (!mem)
		goto out;
	geode_rng.priv = (unsigned long)mem;

	printk(KERN_INFO "AMD Geode RNG detected\n");
	err = hwrng_register(&geode_rng);
	if (err) {
		printk(KERN_ERR PFX "RNG registering failed (%d)\n",
		       err);
		goto err_unmap;
	}
out:
	return err;

err_unmap:
	iounmap(mem);
	goto out;
}
Ejemplo n.º 15
0
static int ohci_pci_reset (struct usb_hcd *hcd)
{
	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
	int ret = 0;

	if (hcd->self.controller) {
		struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
		const struct pci_device_id *quirk_id;

		quirk_id = pci_match_id(ohci_pci_quirks, pdev);
		if (quirk_id != NULL) {
			int (*quirk)(struct usb_hcd *ohci);
			quirk = (void *)quirk_id->driver_data;
			ret = quirk(hcd);
		}
	}
	if (ret == 0) {
		ohci_hcd_init (ohci);
		return ohci_init (ohci);
	}
	return ret;
}
static int iTCO_wdt_probe(struct platform_device *dev)
{
	int ret = -ENODEV;
	int found = 0;
	struct pci_dev *pdev = NULL;
	const struct pci_device_id *ent;

	spin_lock_init(&iTCO_wdt_private.io_lock);

	for_each_pci_dev(pdev) {
		ent = pci_match_id(iTCO_wdt_pci_tbl, pdev);
		if (ent) {
			found++;
			ret = iTCO_wdt_init(pdev, ent, dev);
#ifdef CONFIG_DEBUG_FS
			iTCO_debugfs_dir = debugfs_create_dir("iTCO", NULL);
			debugfs_create_file("timeleft", S_IRUSR,
					iTCO_debugfs_dir, NULL, &tl_fops);
			debugfs_create_file("reset_type", S_IRUSR | S_IWUSR,
					iTCO_debugfs_dir, NULL, &iTCO_wdt_reset_type_fops);
			debugfs_create_file("trigger", S_IWUSR,
					iTCO_debugfs_dir, NULL, &iTCO_wdt_trigger_fops);
			debugfs_create_bool("panic_reboot_notifier", S_IRUSR | S_IWUSR,
					    iTCO_debugfs_dir,
					    (u32 *)&iTCO_wdt_private.panic_reboot_notifier);
#endif /* CONFIG_DEBUG_FS */
			if (!ret)
				break;
		}
	}

	if (!found)
		pr_info("No device detected\n");

	return ret;
}
Ejemplo n.º 17
0
static int __init mod_init(void)
{
	int err = -ENODEV;
	struct pci_dev *pdev = NULL;
	const struct pci_device_id *ent;
	u32 pmbase;

	for_each_pci_dev(pdev) {
		ent = pci_match_id(pci_tbl, pdev);
		if (ent)
			goto found;
	}
	
	goto out;

found:
	err = pci_read_config_dword(pdev, 0x58, &pmbase);
	if (err)
		goto out;
	err = -EIO;
	pmbase &= 0x0000FF00;
	if (pmbase == 0)
		goto out;
	amd_rng.priv = (unsigned long)pmbase;
	amd_pdev = pdev;

	printk(KERN_INFO "AMD768 RNG detected\n");
	err = hwrng_register(&amd_rng);
	if (err) {
		printk(KERN_ERR PFX "RNG registering failed (%d)\n",
		       err);
		goto out;
	}
out:
	return err;
}
Ejemplo n.º 18
0
static int __init applicom_init(void)
{
	int i, numisa = 0;
	struct pci_dev *dev = NULL;
	void __iomem *RamIO;
	int boardno, ret;

	printk(KERN_INFO "Applicom driver: $Id$\n");

	/* No mem and irq given - check for a PCI card */

	while ( (dev = pci_get_class(PCI_CLASS_OTHERS << 16, dev))) {

		if (!pci_match_id(applicom_pci_tbl, dev))
			continue;
		
		if (pci_enable_device(dev))
			return -EIO;

		RamIO = ioremap_nocache(pci_resource_start(dev, 0), LEN_RAM_IO);

		if (!RamIO) {
			printk(KERN_INFO "ac.o: Failed to ioremap PCI memory "
				"space at 0x%llx\n",
				(unsigned long long)pci_resource_start(dev, 0));
			pci_disable_device(dev);
			return -EIO;
		}

		printk(KERN_INFO "Applicom %s found at mem 0x%llx, irq %d\n",
		       applicom_pci_devnames[dev->device-1],
			   (unsigned long long)pci_resource_start(dev, 0),
		       dev->irq);

		boardno = ac_register_board(pci_resource_start(dev, 0),
				RamIO, 0);
		if (!boardno) {
			printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n");
			iounmap(RamIO);
			pci_disable_device(dev);
			continue;
		}

		if (request_irq(dev->irq, &ac_interrupt, IRQF_SHARED, "Applicom PCI", &dummy)) {
			printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq);
			iounmap(RamIO);
			pci_disable_device(dev);
			apbs[boardno - 1].RamIO = NULL;
			continue;
		}

		/* Enable interrupts. */

		writeb(0x40, apbs[boardno - 1].RamIO + RAM_IT_FROM_PC);

		apbs[boardno - 1].irq = dev->irq;
	}

	/* Finished with PCI cards. If none registered, 
	 * and there was no mem/irq specified, exit */

	if (!mem || !irq) {
		if (numboards)
			goto fin;
		else {
			printk(KERN_INFO "ac.o: No PCI boards found.\n");
			printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.\n");
			return -ENXIO;
		}
	}

	/* Now try the specified ISA cards */

	for (i = 0; i < MAX_ISA_BOARD; i++) {
		RamIO = ioremap_nocache(mem + (LEN_RAM_IO * i), LEN_RAM_IO);

		if (!RamIO) {
			printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n", i + 1);
			continue;
		}

		if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i),
						  RamIO,i+1))) {
			iounmap(RamIO);
			continue;
		}

		printk(KERN_NOTICE "Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO*i), irq);

		if (!numisa) {
			if (request_irq(irq, &ac_interrupt, IRQF_SHARED, "Applicom ISA", &dummy)) {
				printk(KERN_WARNING "Could not allocate IRQ %d for ISA Applicom device.\n", irq);
				iounmap(RamIO);
				apbs[boardno - 1].RamIO = NULL;
			}
			else
				apbs[boardno - 1].irq = irq;
		}
		else
			apbs[boardno - 1].irq = 0;

		numisa++;
	}

	if (!numisa)
		printk(KERN_WARNING "ac.o: No valid ISA Applicom boards found "
				"at mem 0x%lx\n", mem);

 fin:
	init_waitqueue_head(&FlagSleepRec);

	WriteErrorCount = 0;
	ReadErrorCount = 0;
	DeviceErrorCount = 0;

	if (numboards) {
		ret = misc_register(&ac_miscdev);
		if (ret) {
			printk(KERN_WARNING "ac.o: Unable to register misc device\n");
			goto out;
		}
		for (i = 0; i < MAX_BOARD; i++) {
			int serial;
			char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];

			if (!apbs[i].RamIO)
				continue;

			for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
				boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);

			boardname[serial] = 0;


			printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d",
			       i+1, boardname,
			       (int)(readb(apbs[i].RamIO + VERS) >> 4),
			       (int)(readb(apbs[i].RamIO + VERS) & 0xF));
			
			serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + 
				(readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + 
				(readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );

			if (serial != 0)
				printk(" S/N %d\n", serial);
			else
				printk("\n");
		}
		return 0;
	}

	else
		return -ENXIO;
Ejemplo n.º 19
0
static long sp5100_tco_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 =		TCO_MODULE_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) {
            tco_timer_stop();
            retval = 0;
        }
        if (new_options & WDIOS_ENABLECARD) {
            tco_timer_start();
            tco_timer_keepalive();
            retval = 0;
        }
        return retval;
    case WDIOC_KEEPALIVE:
        tco_timer_keepalive();
        return 0;
    case WDIOC_SETTIMEOUT:
        if (get_user(new_heartbeat, p))
            return -EFAULT;
        if (tco_timer_set_heartbeat(new_heartbeat))
            return -EINVAL;
        tco_timer_keepalive();
    /* Fall through */
    case WDIOC_GETTIMEOUT:
        return put_user(heartbeat, p);
    default:
        return -ENOTTY;
    }
}

/*
 * Kernel Interfaces
 */

static const struct file_operations sp5100_tco_fops = {
    .owner =		THIS_MODULE,
    .llseek =		no_llseek,
    .write =		sp5100_tco_write,
    .unlocked_ioctl =	sp5100_tco_ioctl,
    .open =			sp5100_tco_open,
    .release =		sp5100_tco_release,
};

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

/*
 * Data for PCI driver interface
 *
 * This data only exists for exporting the supported
 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
 * register a pci_driver, because someone else might
 * want to register another driver on the same PCI id.
 */
static DEFINE_PCI_DEVICE_TABLE(sp5100_tco_pci_tbl) = {
    {   PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID,
        PCI_ANY_ID,
    },
    { 0, },			/* End of list */
};
MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl);

/*
 * Init & exit routines
 */

static unsigned char __devinit sp5100_tco_setupdevice(void)
{
    struct pci_dev *dev = NULL;
    u32 val;

    /* Match the PCI device */
    for_each_pci_dev(dev) {
        if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) {
            sp5100_tco_pci = dev;
            break;
        }
    }

    if (!sp5100_tco_pci)
        return 0;

    /* Request the IO ports used by this driver */
    pm_iobase = SP5100_IO_PM_INDEX_REG;
    if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, "SP5100 TCO")) {
        pr_err("I/O address 0x%04x already in use\n", pm_iobase);
        goto exit;
    }

    /* Find the watchdog base address. */
    outb(SP5100_PM_WATCHDOG_BASE3, SP5100_IO_PM_INDEX_REG);
    val = inb(SP5100_IO_PM_DATA_REG);
    outb(SP5100_PM_WATCHDOG_BASE2, SP5100_IO_PM_INDEX_REG);
    val = val << 8 | inb(SP5100_IO_PM_DATA_REG);
    outb(SP5100_PM_WATCHDOG_BASE1, SP5100_IO_PM_INDEX_REG);
    val = val << 8 | inb(SP5100_IO_PM_DATA_REG);
    outb(SP5100_PM_WATCHDOG_BASE0, SP5100_IO_PM_INDEX_REG);
    /* Low three bits of BASE0 are reserved. */
    val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8);

    if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
                                      "SP5100 TCO")) {
        pr_err("mmio address 0x%04x already in use\n", val);
        goto unreg_region;
    }
    tcobase_phys = val;

    tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE);
    if (!tcobase) {
        pr_err("failed to get tcobase address\n");
        goto unreg_mem_region;
    }

    /* Enable watchdog decode bit */
    pci_read_config_dword(sp5100_tco_pci,
                          SP5100_PCI_WATCHDOG_MISC_REG,
                          &val);

    val |= SP5100_PCI_WATCHDOG_DECODE_EN;

    pci_write_config_dword(sp5100_tco_pci,
                           SP5100_PCI_WATCHDOG_MISC_REG,
                           val);

    /* Enable Watchdog timer and set the resolution to 1 sec. */
    outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG);
    val = inb(SP5100_IO_PM_DATA_REG);
    val |= SP5100_PM_WATCHDOG_SECOND_RES;
    val &= ~SP5100_PM_WATCHDOG_DISABLE;
    outb(val, SP5100_IO_PM_DATA_REG);

    /* Check that the watchdog action is set to reset the system. */
    val = readl(SP5100_WDT_CONTROL(tcobase));
    val &= ~SP5100_PM_WATCHDOG_ACTION_RESET;
    writel(val, SP5100_WDT_CONTROL(tcobase));

    /* Set a reasonable heartbeat before we stop the timer */
    tco_timer_set_heartbeat(heartbeat);

    /*
     * Stop the TCO before we change anything so we don't race with
     * a zeroed timer.
     */
    tco_timer_stop();

    /* Done */
    return 1;

unreg_mem_region:
    release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
unreg_region:
    release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
exit:
    return 0;
}

static int __devinit sp5100_tco_init(struct platform_device *dev)
{
    int ret;
    u32 val;

    /* Check whether or not the hardware watchdog is there. If found, then
     * set it up.
     */
    if (!sp5100_tco_setupdevice())
        return -ENODEV;

    /* Check to see if last reboot was due to watchdog timeout */
    pr_info("Watchdog reboot %sdetected\n",
            readl(SP5100_WDT_CONTROL(tcobase)) & SP5100_PM_WATCHDOG_FIRED ?
            "" : "not ");

    /* Clear out the old status */
    val = readl(SP5100_WDT_CONTROL(tcobase));
    val &= ~SP5100_PM_WATCHDOG_FIRED;
    writel(val, SP5100_WDT_CONTROL(tcobase));

    /*
     * Check that the heartbeat value is within it's range.
     * If not, reset to the default.
     */
    if (tco_timer_set_heartbeat(heartbeat)) {
        heartbeat = WATCHDOG_HEARTBEAT;
        tco_timer_set_heartbeat(heartbeat);
    }

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

    clear_bit(0, &timer_alive);

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

    return 0;

exit:
    iounmap(tcobase);
    release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
    release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
    return ret;
}

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

    /* Deregister */
    misc_deregister(&sp5100_tco_miscdev);
    iounmap(tcobase);
    release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
    release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
}

static int __devexit sp5100_tco_remove(struct platform_device *dev)
{
    if (tcobase)
        sp5100_tco_cleanup();
    return 0;
}

static void sp5100_tco_shutdown(struct platform_device *dev)
{
    tco_timer_stop();
}

static struct platform_driver sp5100_tco_driver = {
    .probe		= sp5100_tco_init,
    .remove		= __devexit_p(sp5100_tco_remove),
    .shutdown	= sp5100_tco_shutdown,
    .driver		= {
        .owner	= THIS_MODULE,
        .name	= TCO_MODULE_NAME,
    },
};

static int __init sp5100_tco_init_module(void)
{
    int err;

    pr_info("SP5100 TCO WatchDog Timer Driver v%s\n", TCO_VERSION);

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

    sp5100_tco_platform_device = platform_device_register_simple(
                                     TCO_MODULE_NAME, -1, NULL, 0);
    if (IS_ERR(sp5100_tco_platform_device)) {
        err = PTR_ERR(sp5100_tco_platform_device);
        goto unreg_platform_driver;
    }

    return 0;

unreg_platform_driver:
    platform_driver_unregister(&sp5100_tco_driver);
    return err;
}
Ejemplo n.º 20
0
static int ipmi_pci_probe(struct pci_dev *pdev,
				    const struct pci_device_id *ent)
{
	int rv;
	struct si_sm_io io;

	if (pci_match_id(ipmi_pci_blacklist, pdev))
		return -ENODEV;

	memset(&io, 0, sizeof(io));
	io.addr_source = SI_PCI;
	dev_info(&pdev->dev, "probing via PCI");

	switch (pdev->class) {
	case PCI_CLASS_SERIAL_IPMI_SMIC:
		io.si_type = SI_SMIC;
		break;

	case PCI_CLASS_SERIAL_IPMI_KCS:
		io.si_type = SI_KCS;
		break;

	case PCI_CLASS_SERIAL_IPMI_BT:
		io.si_type = SI_BT;
		break;

	default:
		dev_info(&pdev->dev, "Unknown IPMI class: %x\n", pdev->class);
		return -ENOMEM;
	}

	rv = pci_enable_device(pdev);
	if (rv) {
		dev_err(&pdev->dev, "couldn't enable PCI device\n");
		return rv;
	}

	io.addr_source_cleanup = ipmi_pci_cleanup;
	io.addr_source_data = pdev;

	if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
		io.addr_space = IPMI_IO_ADDR_SPACE;
		io.io_setup = ipmi_si_port_setup;
	} else {
		io.addr_space = IPMI_MEM_ADDR_SPACE;
		io.io_setup = ipmi_si_mem_setup;
	}
	io.addr_data = pci_resource_start(pdev, 0);

	io.dev = &pdev->dev;

	io.regspacing = ipmi_pci_probe_regspacing(&io);
	io.regsize = DEFAULT_REGSIZE;
	io.regshift = 0;

	io.irq = pdev->irq;
	if (io.irq)
		io.irq_setup = ipmi_std_irq_setup;

	dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n",
		 &pdev->resource[0], io.regsize, io.regspacing, io.irq);

	rv = ipmi_si_add_smi(&io);
	if (rv)
		pci_disable_device(pdev);

	return rv;
}
Ejemplo n.º 21
0
static long sp5100_tco_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 =		TCO_MODULE_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) {
			tco_timer_stop();
			retval = 0;
		}
		if (new_options & WDIOS_ENABLECARD) {
			tco_timer_start();
			tco_timer_keepalive();
			retval = 0;
		}
		return retval;
	case WDIOC_KEEPALIVE:
		tco_timer_keepalive();
		return 0;
	case WDIOC_SETTIMEOUT:
		if (get_user(new_heartbeat, p))
			return -EFAULT;
		if (tco_timer_set_heartbeat(new_heartbeat))
			return -EINVAL;
		tco_timer_keepalive();
		/* Fall through */
	case WDIOC_GETTIMEOUT:
		return put_user(heartbeat, p);
	default:
		return -ENOTTY;
	}
}

/*
 * Kernel Interfaces
 */

static const struct file_operations sp5100_tco_fops = {
	.owner =		THIS_MODULE,
	.llseek =		no_llseek,
	.write =		sp5100_tco_write,
	.unlocked_ioctl =	sp5100_tco_ioctl,
	.open =			sp5100_tco_open,
	.release =		sp5100_tco_release,
};

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

/*
 * Data for PCI driver interface
 *
 * This data only exists for exporting the supported
 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
 * register a pci_driver, because someone else might
 * want to register another driver on the same PCI id.
 */
static DEFINE_PCI_DEVICE_TABLE(sp5100_tco_pci_tbl) = {
	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID,
	  PCI_ANY_ID, },
	{ 0, },			/* End of list */
};
MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl);

/*
 * Init & exit routines
 */
static unsigned char sp5100_tco_setupdevice(void)
{
	struct pci_dev *dev = NULL;
	const char *dev_name = NULL;
	u32 val;
	u32 index_reg, data_reg, base_addr;

	/* Match the PCI device */
	for_each_pci_dev(dev) {
		if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) {
			sp5100_tco_pci = dev;
			break;
		}
	}

	if (!sp5100_tco_pci)
		return 0;

	pr_info("PCI Revision ID: 0x%x\n", sp5100_tco_pci->revision);

	/*
	 * Determine type of southbridge chipset.
	 */
	if (sp5100_tco_pci->revision >= 0x40) {
		dev_name = SB800_DEVNAME;
		index_reg = SB800_IO_PM_INDEX_REG;
		data_reg = SB800_IO_PM_DATA_REG;
		base_addr = SB800_PM_WATCHDOG_BASE;
	} else {
		dev_name = SP5100_DEVNAME;
		index_reg = SP5100_IO_PM_INDEX_REG;
		data_reg = SP5100_IO_PM_DATA_REG;
		base_addr = SP5100_PM_WATCHDOG_BASE;
	}

	/* Request the IO ports used by this driver */
	pm_iobase = SP5100_IO_PM_INDEX_REG;
	if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, dev_name)) {
		pr_err("I/O address 0x%04x already in use\n", pm_iobase);
		goto exit;
	}

	/*
	 * First, Find the watchdog timer MMIO address from indirect I/O.
	 */
	outb(base_addr+3, index_reg);
	val = inb(data_reg);
	outb(base_addr+2, index_reg);
	val = val << 8 | inb(data_reg);
	outb(base_addr+1, index_reg);
	val = val << 8 | inb(data_reg);
	outb(base_addr+0, index_reg);
	/* Low three bits of BASE are reserved */
	val = val << 8 | (inb(data_reg) & 0xf8);

	pr_debug("Got 0x%04x from indirect I/O\n", val);

	/* Check MMIO address conflict */
	if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
								dev_name))
		goto setup_wdt;
	else
		pr_debug("MMIO address 0x%04x already in use\n", val);

	/*
	 * Secondly, Find the watchdog timer MMIO address
	 * from SBResource_MMIO register.
	 */
	if (sp5100_tco_pci->revision >= 0x40) {
		/* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
		outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG);
		val = inb(SB800_IO_PM_DATA_REG);
		outb(SB800_PM_ACPI_MMIO_EN+2, SB800_IO_PM_INDEX_REG);
		val = val << 8 | inb(SB800_IO_PM_DATA_REG);
		outb(SB800_PM_ACPI_MMIO_EN+1, SB800_IO_PM_INDEX_REG);
		val = val << 8 | inb(SB800_IO_PM_DATA_REG);
		outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG);
		val = val << 8 | inb(SB800_IO_PM_DATA_REG);
	} else {
		/* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
		pci_read_config_dword(sp5100_tco_pci,
				      SP5100_SB_RESOURCE_MMIO_BASE, &val);
	}

	/* The SBResource_MMIO is enabled and mapped memory space? */
	if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) ==
						  SB800_ACPI_MMIO_DECODE_EN) {
		/* Clear unnecessary the low twelve bits */
		val &= ~0xFFF;
		/* Add the Watchdog Timer offset to base address. */
		val += SB800_PM_WDT_MMIO_OFFSET;
		/* Check MMIO address conflict */
		if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
								   dev_name)) {
			pr_debug("Got 0x%04x from SBResource_MMIO register\n",
				val);
			goto setup_wdt;
		} else
			pr_debug("MMIO address 0x%04x already in use\n", val);
	} else
		pr_debug("SBResource_MMIO is disabled(0x%04x)\n", val);

	pr_notice("failed to find MMIO address, giving up.\n");
	goto  unreg_region;

setup_wdt:
	tcobase_phys = val;

	tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE);
	if (!tcobase) {
		pr_err("failed to get tcobase address\n");
		goto unreg_mem_region;
	}

	pr_info("Using 0x%04x for watchdog MMIO address\n", val);

	/* Setup the watchdog timer */
	tco_timer_enable();

	/* Check that the watchdog action is set to reset the system */
	val = readl(SP5100_WDT_CONTROL(tcobase));
	/*
	 * Save WatchDogFired status, because WatchDogFired flag is
	 * cleared here.
	 */
	tco_wdt_fired = val & SP5100_PM_WATCHDOG_FIRED;
	val &= ~SP5100_PM_WATCHDOG_ACTION_RESET;
	writel(val, SP5100_WDT_CONTROL(tcobase));

	/* Set a reasonable heartbeat before we stop the timer */
	tco_timer_set_heartbeat(heartbeat);

	/*
	 * Stop the TCO before we change anything so we don't race with
	 * a zeroed timer.
	 */
	tco_timer_stop();

	/* Done */
	return 1;

unreg_mem_region:
	release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
unreg_region:
	release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
exit:
	return 0;
}

static int sp5100_tco_init(struct platform_device *dev)
{
	int ret;

	/*
	 * Check whether or not the hardware watchdog is there. If found, then
	 * set it up.
	 */
	if (!sp5100_tco_setupdevice())
		return -ENODEV;

	/* Check to see if last reboot was due to watchdog timeout */
	pr_info("Last reboot was %striggered by watchdog.\n",
		tco_wdt_fired ? "" : "not ");

	/*
	 * Check that the heartbeat value is within it's range.
	 * If not, reset to the default.
	 */
	if (tco_timer_set_heartbeat(heartbeat)) {
		heartbeat = WATCHDOG_HEARTBEAT;
		tco_timer_set_heartbeat(heartbeat);
	}

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

	clear_bit(0, &timer_alive);

	/* Show module parameters */
	pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
		tcobase, heartbeat, nowayout);

	return 0;

exit:
	iounmap(tcobase);
	release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
	release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
	return ret;
}

static void sp5100_tco_cleanup(void)
{
	/* Stop the timer before we leave */
	if (!nowayout)
		tco_timer_stop();

	/* Deregister */
	misc_deregister(&sp5100_tco_miscdev);
	iounmap(tcobase);
	release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
	release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
}

static int sp5100_tco_remove(struct platform_device *dev)
{
	if (tcobase)
		sp5100_tco_cleanup();
	return 0;
}

static void sp5100_tco_shutdown(struct platform_device *dev)
{
	tco_timer_stop();
}

static struct platform_driver sp5100_tco_driver = {
	.probe		= sp5100_tco_init,
	.remove		= sp5100_tco_remove,
	.shutdown	= sp5100_tco_shutdown,
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= TCO_MODULE_NAME,
	},
};

static int __init sp5100_tco_init_module(void)
{
	int err;

	pr_info("SP5100/SB800 TCO WatchDog Timer Driver v%s\n", TCO_VERSION);

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

	sp5100_tco_platform_device = platform_device_register_simple(
					TCO_MODULE_NAME, -1, NULL, 0);
	if (IS_ERR(sp5100_tco_platform_device)) {
		err = PTR_ERR(sp5100_tco_platform_device);
		goto unreg_platform_driver;
	}

	return 0;

unreg_platform_driver:
	platform_driver_unregister(&sp5100_tco_driver);
	return err;
}
static long nv_tco_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 =		TCO_MODULE_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) {
            tco_timer_stop();
            retval = 0;
        }
        if (new_options & WDIOS_ENABLECARD) {
            tco_timer_keepalive();
            tco_timer_start();
            retval = 0;
        }
        return retval;
    case WDIOC_KEEPALIVE:
        tco_timer_keepalive();
        return 0;
    case WDIOC_SETTIMEOUT:
        if (get_user(new_heartbeat, p))
            return -EFAULT;
        if (tco_timer_set_heartbeat(new_heartbeat))
            return -EINVAL;
        tco_timer_keepalive();
    /* Fall through */
    case WDIOC_GETTIMEOUT:
        return put_user(heartbeat, p);
    default:
        return -ENOTTY;
    }
}

/*
 *	Kernel Interfaces
 */

static const struct file_operations nv_tco_fops = {
    .owner =		THIS_MODULE,
    .llseek =		no_llseek,
    .write =		nv_tco_write,
    .unlocked_ioctl =	nv_tco_ioctl,
    .open =			nv_tco_open,
    .release =		nv_tco_release,
};

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

/*
 * Data for PCI driver interface
 *
 * This data only exists for exporting the supported
 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
 * register a pci_driver, because someone else might one day
 * want to register another driver on the same PCI id.
 */
static struct pci_device_id tco_pci_tbl[] = {
    {   PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS,
        PCI_ANY_ID, PCI_ANY_ID,
    },
    {   PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
        PCI_ANY_ID, PCI_ANY_ID,
    },
    { 0, },			/* End of list */
};
MODULE_DEVICE_TABLE(pci, tco_pci_tbl);

/*
 *	Init & exit routines
 */

static unsigned char __init nv_tco_getdevice(void)
{
    struct pci_dev *dev = NULL;
    u32 val;

    /* Find the PCI device */
    for_each_pci_dev(dev) {
        if (pci_match_id(tco_pci_tbl, dev) != NULL) {
            tco_pci = dev;
            break;
        }
    }

    if (!tco_pci)
        return 0;

    /* Find the base io port */
    pci_read_config_dword(tco_pci, 0x64, &val);
    val &= 0xffff;
    if (val == 0x0001 || val == 0x0000) {
        /* Something is wrong here, bar isn't setup */
        printk(KERN_ERR PFX "failed to get tcobase address\n");
        return 0;
    }
    val &= 0xff00;
    tcobase = val + 0x40;

    if (!request_region(tcobase, 0x10, "NV TCO")) {
        printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
               tcobase);
        return 0;
    }

    /* Set a reasonable heartbeat before we stop the timer */
    tco_timer_set_heartbeat(30);

    /*
     * Stop the TCO before we change anything so we don't race with
     * a zeroed timer.
     */
    tco_timer_keepalive();
    tco_timer_stop();

    /* Disable SMI caused by TCO */
    if (!request_region(MCP51_SMI_EN(tcobase), 4, "NV TCO")) {
        printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
               MCP51_SMI_EN(tcobase));
        goto out;
    }
    val = inl(MCP51_SMI_EN(tcobase));
    val &= ~MCP51_SMI_EN_TCO;
    outl(val, MCP51_SMI_EN(tcobase));
    val = inl(MCP51_SMI_EN(tcobase));
    release_region(MCP51_SMI_EN(tcobase), 4);
    if (val & MCP51_SMI_EN_TCO) {
        printk(KERN_ERR PFX "Could not disable SMI caused by TCO\n");
        goto out;
    }

    /* Check chipset's NO_REBOOT bit */
    pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
    val |= MCP51_SMBUS_SETUP_B_TCO_REBOOT;
    pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
    pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
    if (!(val & MCP51_SMBUS_SETUP_B_TCO_REBOOT)) {
        printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot "
               "disabled by hardware\n");
        goto out;
    }

    return 1;
out:
    release_region(tcobase, 0x10);
    return 0;
}

static int __devinit nv_tco_init(struct platform_device *dev)
{
    int ret;

    /* Check whether or not the hardware watchdog is there */
    if (!nv_tco_getdevice())
        return -ENODEV;

    /* Check to see if last reboot was due to watchdog timeout */
    printk(KERN_INFO PFX "Watchdog reboot %sdetected.\n",
           inl(TCO_STS(tcobase)) & TCO_STS_TCO2TO_STS ? "" : "not ");

    /* Clear out the old status */
    outl(TCO_STS_RESET, TCO_STS(tcobase));

    /*
     * Check that the heartbeat value is within it's range.
     * If not, reset to the default.
     */
    if (tco_timer_set_heartbeat(heartbeat)) {
        heartbeat = WATCHDOG_HEARTBEAT;
        tco_timer_set_heartbeat(heartbeat);
        printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39, "
               "using %d\n", heartbeat);
    }

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

    clear_bit(0, &timer_alive);

    tco_timer_stop();

    printk(KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec "
           "(nowayout=%d)\n", tcobase, heartbeat, nowayout);

    return 0;

unreg_region:
    release_region(tcobase, 0x10);
    return ret;
}

static void __devexit nv_tco_cleanup(void)
{
    u32 val;

    /* Stop the timer before we leave */
    if (!nowayout)
        tco_timer_stop();

    /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
    pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
    val &= ~MCP51_SMBUS_SETUP_B_TCO_REBOOT;
    pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
    pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
    if (val & MCP51_SMBUS_SETUP_B_TCO_REBOOT) {
        printk(KERN_CRIT PFX "Couldn't unset REBOOT bit.  Machine may "
               "soon reset\n");
    }

    /* Deregister */
    misc_deregister(&nv_tco_miscdev);
    release_region(tcobase, 0x10);
}