brintos

brintos / linux-shallow public Read only

0
0
Text · 17.0 KiB · dc5f295 Raw
741 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 *	Industrial Computer Source PCI-WDT500/501 driver4 *5 *	(c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,6 *						All Rights Reserved.7 *8 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide9 *	warranty for any of this software. This material is provided10 *	"AS-IS" and at no charge.11 *12 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>13 *14 *	Release 0.10.15 *16 *	Fixes17 *		Dave Gregorich	:	Modularisation and minor bugs18 *		Alan Cox	:	Added the watchdog ioctl() stuff19 *		Alan Cox	:	Fixed the reboot problem (as noted by20 *					Matt Crocker).21 *		Alan Cox	:	Added wdt= boot option22 *		Alan Cox	:	Cleaned up copy/user stuff23 *		Tim Hockin	:	Added insmod parameters, comment cleanup24 *					Parameterized timeout25 *		JP Nollmann	:	Added support for PCI wdt501p26 *		Alan Cox	:	Split ISA and PCI cards into two drivers27 *		Jeff Garzik	:	PCI cleanups28 *		Tigran Aivazian	:	Restructured wdtpci_init_one() to handle29 *					failures30 *		Joel Becker	:	Added WDIOC_GET/SETTIMEOUT31 *		Zwane Mwaikambo	:	Magic char closing, locking changes,32 *					cleanups33 *		Matt Domsch	:	nowayout module option34 */35 36#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt37 38#include <linux/interrupt.h>39#include <linux/module.h>40#include <linux/moduleparam.h>41#include <linux/types.h>42#include <linux/miscdevice.h>43#include <linux/watchdog.h>44#include <linux/ioport.h>45#include <linux/delay.h>46#include <linux/notifier.h>47#include <linux/reboot.h>48#include <linux/fs.h>49#include <linux/pci.h>50#include <linux/io.h>51#include <linux/uaccess.h>52 53 54#define WDT_IS_PCI55#include "wd501p.h"56 57/* We can only use 1 card due to the /dev/watchdog restriction */58static int dev_count;59 60static unsigned long open_lock;61static DEFINE_SPINLOCK(wdtpci_lock);62static char expect_close;63 64static resource_size_t io;65static int irq;66 67/* Default timeout */68#define WD_TIMO 60			/* Default heartbeat = 60 seconds */69 70static int heartbeat = WD_TIMO;71static int wd_heartbeat;72module_param(heartbeat, int, 0);73MODULE_PARM_DESC(heartbeat,74		"Watchdog heartbeat in seconds. (0<heartbeat<65536, default="75				__MODULE_STRING(WD_TIMO) ")");76 77static bool nowayout = WATCHDOG_NOWAYOUT;78module_param(nowayout, bool, 0);79MODULE_PARM_DESC(nowayout,80		"Watchdog cannot be stopped once started (default="81				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");82 83/* Support for the Fan Tachometer on the PCI-WDT501 */84static int tachometer;85module_param(tachometer, int, 0);86MODULE_PARM_DESC(tachometer,87		"PCI-WDT501 Fan Tachometer support (0=disable, default=0)");88 89static int type = 500;90module_param(type, int, 0);91MODULE_PARM_DESC(type,92		"PCI-WDT501 Card type (500 or 501 , default=500)");93 94/*95 *	Programming support96 */97 98static void wdtpci_ctr_mode(int ctr, int mode)99{100	ctr <<= 6;101	ctr |= 0x30;102	ctr |= (mode << 1);103	outb(ctr, WDT_CR);104	udelay(8);105}106 107static void wdtpci_ctr_load(int ctr, int val)108{109	outb(val & 0xFF, WDT_COUNT0 + ctr);110	udelay(8);111	outb(val >> 8, WDT_COUNT0 + ctr);112	udelay(8);113}114 115/**116 *	wdtpci_start:117 *118 *	Start the watchdog driver.119 */120 121static int wdtpci_start(void)122{123	unsigned long flags;124 125	spin_lock_irqsave(&wdtpci_lock, flags);126 127	/*128	 * "pet" the watchdog, as Access says.129	 * This resets the clock outputs.130	 */131	inb(WDT_DC);			/* Disable watchdog */132	udelay(8);133	wdtpci_ctr_mode(2, 0);		/* Program CTR2 for Mode 0:134						Pulse on Terminal Count */135	outb(0, WDT_DC);		/* Enable watchdog */136	udelay(8);137	inb(WDT_DC);			/* Disable watchdog */138	udelay(8);139	outb(0, WDT_CLOCK);		/* 2.0833MHz clock */140	udelay(8);141	inb(WDT_BUZZER);		/* disable */142	udelay(8);143	inb(WDT_OPTONOTRST);		/* disable */144	udelay(8);145	inb(WDT_OPTORST);		/* disable */146	udelay(8);147	inb(WDT_PROGOUT);		/* disable */148	udelay(8);149	wdtpci_ctr_mode(0, 3);		/* Program CTR0 for Mode 3:150						Square Wave Generator */151	wdtpci_ctr_mode(1, 2);		/* Program CTR1 for Mode 2:152						Rate Generator */153	wdtpci_ctr_mode(2, 1);		/* Program CTR2 for Mode 1:154						Retriggerable One-Shot */155	wdtpci_ctr_load(0, 20833);	/* count at 100Hz */156	wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */157	/* DO NOT LOAD CTR2 on PCI card! -- JPN */158	outb(0, WDT_DC);		/* Enable watchdog */159	udelay(8);160 161	spin_unlock_irqrestore(&wdtpci_lock, flags);162	return 0;163}164 165/**166 *	wdtpci_stop:167 *168 *	Stop the watchdog driver.169 */170 171static int wdtpci_stop(void)172{173	unsigned long flags;174 175	/* Turn the card off */176	spin_lock_irqsave(&wdtpci_lock, flags);177	inb(WDT_DC);			/* Disable watchdog */178	udelay(8);179	wdtpci_ctr_load(2, 0);		/* 0 length reset pulses now */180	spin_unlock_irqrestore(&wdtpci_lock, flags);181	return 0;182}183 184/**185 *	wdtpci_ping:186 *187 *	Reload counter one with the watchdog heartbeat. We don't bother188 *	reloading the cascade counter.189 */190 191static int wdtpci_ping(void)192{193	unsigned long flags;194 195	spin_lock_irqsave(&wdtpci_lock, flags);196	/* Write a watchdog value */197	inb(WDT_DC);			/* Disable watchdog */198	udelay(8);199	wdtpci_ctr_mode(1, 2);		/* Re-Program CTR1 for Mode 2:200							Rate Generator */201	wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */202	outb(0, WDT_DC);		/* Enable watchdog */203	udelay(8);204	spin_unlock_irqrestore(&wdtpci_lock, flags);205	return 0;206}207 208/**209 *	wdtpci_set_heartbeat:210 *	@t:		the new heartbeat value that needs to be set.211 *212 *	Set a new heartbeat value for the watchdog device. If the heartbeat213 *	value is incorrect we keep the old value and return -EINVAL.214 *	If successful we return 0.215 */216static int wdtpci_set_heartbeat(int t)217{218	/* Arbitrary, can't find the card's limits */219	if (t < 1 || t > 65535)220		return -EINVAL;221 222	heartbeat = t;223	wd_heartbeat = t * 100;224	return 0;225}226 227/**228 *	wdtpci_get_status:229 *	@status:		the new status.230 *231 *	Extract the status information from a WDT watchdog device. There are232 *	several board variants so we have to know which bits are valid. Some233 *	bits default to one and some to zero in order to be maximally painful.234 *235 *	we then map the bits onto the status ioctl flags.236 */237 238static int wdtpci_get_status(int *status)239{240	unsigned char new_status;241	unsigned long flags;242 243	spin_lock_irqsave(&wdtpci_lock, flags);244	new_status = inb(WDT_SR);245	spin_unlock_irqrestore(&wdtpci_lock, flags);246 247	*status = 0;248	if (new_status & WDC_SR_ISOI0)249		*status |= WDIOF_EXTERN1;250	if (new_status & WDC_SR_ISII1)251		*status |= WDIOF_EXTERN2;252	if (type == 501) {253		if (!(new_status & WDC_SR_TGOOD))254			*status |= WDIOF_OVERHEAT;255		if (!(new_status & WDC_SR_PSUOVER))256			*status |= WDIOF_POWEROVER;257		if (!(new_status & WDC_SR_PSUUNDR))258			*status |= WDIOF_POWERUNDER;259		if (tachometer) {260			if (!(new_status & WDC_SR_FANGOOD))261				*status |= WDIOF_FANFAULT;262		}263	}264	return 0;265}266 267/**268 *	wdtpci_get_temperature:269 *270 *	Reports the temperature in degrees Fahrenheit. The API is in271 *	farenheit. It was designed by an imperial measurement luddite.272 */273 274static int wdtpci_get_temperature(int *temperature)275{276	unsigned short c;277	unsigned long flags;278	spin_lock_irqsave(&wdtpci_lock, flags);279	c = inb(WDT_RT);280	udelay(8);281	spin_unlock_irqrestore(&wdtpci_lock, flags);282	*temperature = (c * 11 / 15) + 7;283	return 0;284}285 286/**287 *	wdtpci_interrupt:288 *	@irq:		Interrupt number289 *	@dev_id:	Unused as we don't allow multiple devices.290 *291 *	Handle an interrupt from the board. These are raised when the status292 *	map changes in what the board considers an interesting way. That means293 *	a failure condition occurring.294 */295 296static irqreturn_t wdtpci_interrupt(int irq, void *dev_id)297{298	/*299	 *	Read the status register see what is up and300	 *	then printk it.301	 */302	unsigned char status;303 304	spin_lock(&wdtpci_lock);305 306	status = inb(WDT_SR);307	udelay(8);308 309	pr_crit("status %d\n", status);310 311	if (type == 501) {312		if (!(status & WDC_SR_TGOOD)) {313			pr_crit("Overheat alarm (%d)\n", inb(WDT_RT));314			udelay(8);315		}316		if (!(status & WDC_SR_PSUOVER))317			pr_crit("PSU over voltage\n");318		if (!(status & WDC_SR_PSUUNDR))319			pr_crit("PSU under voltage\n");320		if (tachometer) {321			if (!(status & WDC_SR_FANGOOD))322				pr_crit("Possible fan fault\n");323		}324	}325	if (!(status & WDC_SR_WCCR)) {326#ifdef SOFTWARE_REBOOT327#ifdef ONLY_TESTING328		pr_crit("Would Reboot\n");329#else330		pr_crit("Initiating system reboot\n");331		emergency_restart();332#endif333#else334		pr_crit("Reset in 5ms\n");335#endif336	}337	spin_unlock(&wdtpci_lock);338	return IRQ_HANDLED;339}340 341 342/**343 *	wdtpci_write:344 *	@file: file handle to the watchdog345 *	@buf: buffer to write (unused as data does not matter here346 *	@count: count of bytes347 *	@ppos: pointer to the position to write. No seeks allowed348 *349 *	A write to a watchdog device is defined as a keepalive signal. Any350 *	write of data will do, as we we don't define content meaning.351 */352 353static ssize_t wdtpci_write(struct file *file, const char __user *buf,354						size_t count, loff_t *ppos)355{356	if (count) {357		if (!nowayout) {358			size_t i;359 360			/* In case it was set long ago */361			expect_close = 0;362 363			for (i = 0; i != count; i++) {364				char c;365				if (get_user(c, buf + i))366					return -EFAULT;367				if (c == 'V')368					expect_close = 42;369			}370		}371		wdtpci_ping();372	}373	return count;374}375 376/**377 *	wdtpci_ioctl:378 *	@file: file handle to the device379 *	@cmd: watchdog command380 *	@arg: argument pointer381 *382 *	The watchdog API defines a common set of functions for all watchdogs383 *	according to their available features. We only actually usefully support384 *	querying capabilities and current status.385 */386 387static long wdtpci_ioctl(struct file *file, unsigned int cmd,388							unsigned long arg)389{390	void __user *argp = (void __user *)arg;391	int __user *p = argp;392	int new_heartbeat;393	int status;394 395	struct watchdog_info ident = {396		.options =		WDIOF_SETTIMEOUT|397					WDIOF_MAGICCLOSE|398					WDIOF_KEEPALIVEPING,399		.firmware_version =	1,400		.identity =		"PCI-WDT500/501",401	};402 403	/* Add options according to the card we have */404	ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);405	if (type == 501) {406		ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|407							WDIOF_POWEROVER);408		if (tachometer)409			ident.options |= WDIOF_FANFAULT;410	}411 412	switch (cmd) {413	case WDIOC_GETSUPPORT:414		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;415	case WDIOC_GETSTATUS:416		wdtpci_get_status(&status);417		return put_user(status, p);418	case WDIOC_GETBOOTSTATUS:419		return put_user(0, p);420	case WDIOC_KEEPALIVE:421		wdtpci_ping();422		return 0;423	case WDIOC_SETTIMEOUT:424		if (get_user(new_heartbeat, p))425			return -EFAULT;426		if (wdtpci_set_heartbeat(new_heartbeat))427			return -EINVAL;428		wdtpci_ping();429		fallthrough;430	case WDIOC_GETTIMEOUT:431		return put_user(heartbeat, p);432	default:433		return -ENOTTY;434	}435}436 437/**438 *	wdtpci_open:439 *	@inode: inode of device440 *	@file: file handle to device441 *442 *	The watchdog device has been opened. The watchdog device is single443 *	open and on opening we load the counters. Counter zero is a 100Hz444 *	cascade, into counter 1 which downcounts to reboot. When the counter445 *	triggers counter 2 downcounts the length of the reset pulse which446 *	set set to be as long as possible.447 */448 449static int wdtpci_open(struct inode *inode, struct file *file)450{451	if (test_and_set_bit(0, &open_lock))452		return -EBUSY;453 454	if (nowayout)455		__module_get(THIS_MODULE);456	/*457	 *	Activate458	 */459	wdtpci_start();460	return stream_open(inode, file);461}462 463/**464 *	wdtpci_release:465 *	@inode: inode to board466 *	@file: file handle to board467 *468 *	The watchdog has a configurable API. There is a religious dispute469 *	between people who want their watchdog to be able to shut down and470 *	those who want to be sure if the watchdog manager dies the machine471 *	reboots. In the former case we disable the counters, in the latter472 *	case you have to open it again very soon.473 */474 475static int wdtpci_release(struct inode *inode, struct file *file)476{477	if (expect_close == 42) {478		wdtpci_stop();479	} else {480		pr_crit("Unexpected close, not stopping timer!\n");481		wdtpci_ping();482	}483	expect_close = 0;484	clear_bit(0, &open_lock);485	return 0;486}487 488/**489 *	wdtpci_temp_read:490 *	@file: file handle to the watchdog board491 *	@buf: buffer to write 1 byte into492 *	@count: length of buffer493 *	@ptr: offset (no seek allowed)494 *495 *	Read reports the temperature in degrees Fahrenheit. The API is in496 *	fahrenheit. It was designed by an imperial measurement luddite.497 */498 499static ssize_t wdtpci_temp_read(struct file *file, char __user *buf,500						size_t count, loff_t *ptr)501{502	int temperature;503 504	if (wdtpci_get_temperature(&temperature))505		return -EFAULT;506 507	if (copy_to_user(buf, &temperature, 1))508		return -EFAULT;509 510	return 1;511}512 513/**514 *	wdtpci_temp_open:515 *	@inode: inode of device516 *	@file: file handle to device517 *518 *	The temperature device has been opened.519 */520 521static int wdtpci_temp_open(struct inode *inode, struct file *file)522{523	return stream_open(inode, file);524}525 526/**527 *	wdtpci_temp_release:528 *	@inode: inode to board529 *	@file: file handle to board530 *531 *	The temperature device has been closed.532 */533 534static int wdtpci_temp_release(struct inode *inode, struct file *file)535{536	return 0;537}538 539/**540 *	wdtpci_notify_sys:541 *	@this: our notifier block542 *	@code: the event being reported543 *	@unused: unused544 *545 *	Our notifier is called on system shutdowns. We want to turn the card546 *	off at reboot otherwise the machine will reboot again during memory547 *	test or worse yet during the following fsck. This would suck, in fact548 *	trust me - if it happens it does suck.549 */550 551static int wdtpci_notify_sys(struct notifier_block *this, unsigned long code,552							void *unused)553{554	if (code == SYS_DOWN || code == SYS_HALT)555		wdtpci_stop();556	return NOTIFY_DONE;557}558 559/*560 *	Kernel Interfaces561 */562 563 564static const struct file_operations wdtpci_fops = {565	.owner		= THIS_MODULE,566	.write		= wdtpci_write,567	.unlocked_ioctl	= wdtpci_ioctl,568	.compat_ioctl	= compat_ptr_ioctl,569	.open		= wdtpci_open,570	.release	= wdtpci_release,571};572 573static struct miscdevice wdtpci_miscdev = {574	.minor	= WATCHDOG_MINOR,575	.name	= "watchdog",576	.fops	= &wdtpci_fops,577};578 579static const struct file_operations wdtpci_temp_fops = {580	.owner		= THIS_MODULE,581	.read		= wdtpci_temp_read,582	.open		= wdtpci_temp_open,583	.release	= wdtpci_temp_release,584};585 586static struct miscdevice temp_miscdev = {587	.minor	= TEMP_MINOR,588	.name	= "temperature",589	.fops	= &wdtpci_temp_fops,590};591 592/*593 *	The WDT card needs to learn about soft shutdowns in order to594 *	turn the timebomb registers off.595 */596 597static struct notifier_block wdtpci_notifier = {598	.notifier_call = wdtpci_notify_sys,599};600 601 602static int wdtpci_init_one(struct pci_dev *dev,603					const struct pci_device_id *ent)604{605	int ret = -EIO;606 607	dev_count++;608	if (dev_count > 1) {609		pr_err("This driver only supports one device\n");610		return -ENODEV;611	}612 613	if (type != 500 && type != 501) {614		pr_err("unknown card type '%d'\n", type);615		return -ENODEV;616	}617 618	if (pci_enable_device(dev)) {619		pr_err("Not possible to enable PCI Device\n");620		return -ENODEV;621	}622 623	if (pci_resource_start(dev, 2) == 0x0000) {624		pr_err("No I/O-Address for card detected\n");625		ret = -ENODEV;626		goto out_pci;627	}628 629	if (pci_request_region(dev, 2, "wdt_pci")) {630		pr_err("I/O address 0x%llx already in use\n",631		       (unsigned long long)pci_resource_start(dev, 2));632		goto out_pci;633	}634 635	irq = dev->irq;636	io = pci_resource_start(dev, 2);637 638	if (request_irq(irq, wdtpci_interrupt, IRQF_SHARED,639			 "wdt_pci", &wdtpci_miscdev)) {640		pr_err("IRQ %d is not free\n", irq);641		goto out_reg;642	}643 644	pr_info("PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n",645		(unsigned long long)io, irq);646 647	/* Check that the heartbeat value is within its range;648	   if not reset to the default */649	if (wdtpci_set_heartbeat(heartbeat)) {650		wdtpci_set_heartbeat(WD_TIMO);651		pr_info("heartbeat value must be 0 < heartbeat < 65536, using %d\n",652			WD_TIMO);653	}654 655	ret = register_reboot_notifier(&wdtpci_notifier);656	if (ret) {657		pr_err("cannot register reboot notifier (err=%d)\n", ret);658		goto out_irq;659	}660 661	if (type == 501) {662		ret = misc_register(&temp_miscdev);663		if (ret) {664			pr_err("cannot register miscdev on minor=%d (err=%d)\n",665			       TEMP_MINOR, ret);666			goto out_rbt;667		}668	}669 670	ret = misc_register(&wdtpci_miscdev);671	if (ret) {672		pr_err("cannot register miscdev on minor=%d (err=%d)\n",673		       WATCHDOG_MINOR, ret);674		goto out_misc;675	}676 677	pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",678		heartbeat, nowayout);679	if (type == 501)680		pr_info("Fan Tachometer is %s\n",681			tachometer ? "Enabled" : "Disabled");682 683	ret = 0;684out:685	return ret;686 687out_misc:688	if (type == 501)689		misc_deregister(&temp_miscdev);690out_rbt:691	unregister_reboot_notifier(&wdtpci_notifier);692out_irq:693	free_irq(irq, &wdtpci_miscdev);694out_reg:695	pci_release_region(dev, 2);696out_pci:697	pci_disable_device(dev);698	goto out;699}700 701 702static void wdtpci_remove_one(struct pci_dev *pdev)703{704	/* here we assume only one device will ever have705	 * been picked up and registered by probe function */706	misc_deregister(&wdtpci_miscdev);707	if (type == 501)708		misc_deregister(&temp_miscdev);709	unregister_reboot_notifier(&wdtpci_notifier);710	free_irq(irq, &wdtpci_miscdev);711	pci_release_region(pdev, 2);712	pci_disable_device(pdev);713	dev_count--;714}715 716 717static const struct pci_device_id wdtpci_pci_tbl[] = {718	{719		.vendor	   = PCI_VENDOR_ID_ACCESSIO,720		.device	   = PCI_DEVICE_ID_ACCESSIO_WDG_CSM,721		.subvendor = PCI_ANY_ID,722		.subdevice = PCI_ANY_ID,723	},724	{ 0, }, /* terminate list */725};726MODULE_DEVICE_TABLE(pci, wdtpci_pci_tbl);727 728 729static struct pci_driver wdtpci_driver = {730	.name		= "wdt_pci",731	.id_table	= wdtpci_pci_tbl,732	.probe		= wdtpci_init_one,733	.remove		= wdtpci_remove_one,734};735 736module_pci_driver(wdtpci_driver);737 738MODULE_AUTHOR("JP Nollmann, Alan Cox");739MODULE_DESCRIPTION("Driver for the ICS PCI-WDT500/501 watchdog cards");740MODULE_LICENSE("GPL");741