aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorroot <root@artemis.panaceas.org>2015-12-25 04:40:36 +0000
committerroot <root@artemis.panaceas.org>2015-12-25 04:40:36 +0000
commit849369d6c66d3054688672f97d31fceb8e8230fb (patch)
tree6135abc790ca67dedbe07c39806591e70eda81ce /drivers/watchdog
downloadlinux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.tar.gz
linux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.tar.bz2
linux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.zip
initial_commit
Diffstat (limited to 'drivers/watchdog')
-rwxr-xr-xdrivers/watchdog/Kconfig1260
-rwxr-xr-xdrivers/watchdog/Makefile162
-rw-r--r--drivers/watchdog/acquirewdt.c344
-rw-r--r--drivers/watchdog/advantechwdt.c355
-rw-r--r--drivers/watchdog/adx_wdt.c355
-rw-r--r--drivers/watchdog/alim1535_wdt.c458
-rw-r--r--drivers/watchdog/alim7101_wdt.c444
-rw-r--r--drivers/watchdog/ar7_wdt.c369
-rw-r--r--drivers/watchdog/at32ap700x_wdt.c454
-rw-r--r--drivers/watchdog/at91rm9200_wdt.c290
-rw-r--r--drivers/watchdog/at91sam9_wdt.c330
-rw-r--r--drivers/watchdog/ath79_wdt.c305
-rw-r--r--drivers/watchdog/bcm47xx_wdt.c286
-rw-r--r--drivers/watchdog/bcm63xx_wdt.c332
-rw-r--r--drivers/watchdog/bfin_wdt.c485
-rw-r--r--drivers/watchdog/booke_wdt.c299
-rw-r--r--drivers/watchdog/coh901327_wdt.c548
-rw-r--r--drivers/watchdog/cpu5wdt.c300
-rw-r--r--drivers/watchdog/cpwd.c701
-rw-r--r--drivers/watchdog/da9052_wdt.c542
-rw-r--r--drivers/watchdog/davinci_wdt.c298
-rw-r--r--drivers/watchdog/ep93xx_wdt.c255
-rw-r--r--drivers/watchdog/eurotechwdt.c485
-rw-r--r--drivers/watchdog/f71808e_wdt.c824
-rw-r--r--drivers/watchdog/gef_wdt.c332
-rw-r--r--drivers/watchdog/geodewdt.c299
-rw-r--r--drivers/watchdog/hpwdt.c858
-rw-r--r--drivers/watchdog/i6300esb.c515
-rw-r--r--drivers/watchdog/iTCO_vendor.h15
-rw-r--r--drivers/watchdog/iTCO_vendor_support.c375
-rw-r--r--drivers/watchdog/iTCO_wdt.c1109
-rw-r--r--drivers/watchdog/ib700wdt.c392
-rw-r--r--drivers/watchdog/ibmasr.c426
-rw-r--r--drivers/watchdog/imx2_wdt.c426
-rw-r--r--drivers/watchdog/indydog.c223
-rw-r--r--drivers/watchdog/intel_scu_watchdog.c571
-rw-r--r--drivers/watchdog/intel_scu_watchdog.h66
-rw-r--r--drivers/watchdog/iop_wdt.c264
-rw-r--r--drivers/watchdog/it8712f_wdt.c429
-rw-r--r--drivers/watchdog/it87_wdt.c769
-rw-r--r--drivers/watchdog/ixp2000_wdt.c215
-rw-r--r--drivers/watchdog/ixp4xx_wdt.c214
-rw-r--r--drivers/watchdog/jz4740_wdt.c322
-rw-r--r--drivers/watchdog/ks8695_wdt.c314
-rw-r--r--drivers/watchdog/lantiq_wdt.c261
-rw-r--r--drivers/watchdog/m54xx_wdt.c227
-rw-r--r--drivers/watchdog/machzwd.c461
-rw-r--r--drivers/watchdog/max63xx_wdt.c399
-rw-r--r--drivers/watchdog/mixcomwd.c323
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c331
-rw-r--r--drivers/watchdog/mpcore_wdt.c454
-rw-r--r--drivers/watchdog/mtx-1_wdt.c273
-rw-r--r--drivers/watchdog/mv64x60_wdt.c328
-rw-r--r--drivers/watchdog/nuc900_wdt.c354
-rw-r--r--drivers/watchdog/nv_tco.c512
-rw-r--r--drivers/watchdog/nv_tco.h64
-rw-r--r--drivers/watchdog/octeon-wdt-main.c746
-rw-r--r--drivers/watchdog/octeon-wdt-nmi.S64
-rw-r--r--drivers/watchdog/omap_wdt.c457
-rw-r--r--drivers/watchdog/omap_wdt.h54
-rw-r--r--drivers/watchdog/orion_wdt.c322
-rw-r--r--drivers/watchdog/pc87413_wdt.c600
-rw-r--r--drivers/watchdog/pcwd.c1040
-rw-r--r--drivers/watchdog/pcwd_pci.c855
-rw-r--r--drivers/watchdog/pcwd_usb.c863
-rw-r--r--drivers/watchdog/pika_wdt.c301
-rw-r--r--drivers/watchdog/pnx4008_wdt.c365
-rw-r--r--drivers/watchdog/pnx833x_wdt.c285
-rw-r--r--drivers/watchdog/rc32434_wdt.c352
-rw-r--r--drivers/watchdog/rdc321x_wdt.c312
-rw-r--r--drivers/watchdog/riowd.c261
-rw-r--r--drivers/watchdog/s3c2410_wdt.c628
-rw-r--r--drivers/watchdog/sa1100_wdt.c193
-rw-r--r--drivers/watchdog/sb_wdog.c362
-rw-r--r--drivers/watchdog/sbc60xxwdt.c398
-rw-r--r--drivers/watchdog/sbc7240_wdt.c324
-rw-r--r--drivers/watchdog/sbc8360.c412
-rw-r--r--drivers/watchdog/sbc_epx_c3.c226
-rw-r--r--drivers/watchdog/sbc_fitpc2_wdt.c269
-rw-r--r--drivers/watchdog/sc1200wdt.c483
-rw-r--r--drivers/watchdog/sc520_wdt.c444
-rw-r--r--drivers/watchdog/sch311x_wdt.c578
-rw-r--r--drivers/watchdog/scx200_wdt.c273
-rw-r--r--drivers/watchdog/shwdt.c487
-rw-r--r--drivers/watchdog/smsc37b787_wdt.c627
-rw-r--r--drivers/watchdog/softdog.c328
-rw-r--r--drivers/watchdog/sp5100_tco.c492
-rw-r--r--drivers/watchdog/sp5100_tco.h41
-rw-r--r--drivers/watchdog/sp805_wdt.c387
-rw-r--r--drivers/watchdog/stmp3xxx_wdt.c296
-rw-r--r--drivers/watchdog/ts72xx_wdt.c524
-rw-r--r--drivers/watchdog/twl4030_wdt.c275
-rw-r--r--drivers/watchdog/txx9wdt.c269
-rw-r--r--drivers/watchdog/w83627hf_wdt.c385
-rw-r--r--drivers/watchdog/w83697hf_wdt.c469
-rw-r--r--drivers/watchdog/w83697ug_wdt.c403
-rw-r--r--drivers/watchdog/w83877f_wdt.c416
-rw-r--r--drivers/watchdog/w83977f_wdt.c537
-rw-r--r--drivers/watchdog/wafer5823wdt.c333
-rw-r--r--drivers/watchdog/wd501p.h51
-rw-r--r--drivers/watchdog/wdrtas.c676
-rw-r--r--drivers/watchdog/wdt.c673
-rw-r--r--drivers/watchdog/wdt285.c232
-rw-r--r--drivers/watchdog/wdt977.c517
-rw-r--r--drivers/watchdog/wdt_pci.c787
-rw-r--r--drivers/watchdog/wm831x_wdt.c446
-rw-r--r--drivers/watchdog/wm8350_wdt.c329
-rw-r--r--drivers/watchdog/xen_wdt.c359
108 files changed, 44408 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
new file mode 100755
index 00000000..45097440
--- /dev/null
+++ b/drivers/watchdog/Kconfig
@@ -0,0 +1,1260 @@
+#
+# Watchdog device configuration
+#
+
+menuconfig WATCHDOG
+ bool "Watchdog Timer Support"
+ ---help---
+ If you say Y here (and to one of the following options) and create a
+ character special file /dev/watchdog with major number 10 and minor
+ number 130 using mknod ("man mknod"), you will get a watchdog, i.e.:
+ subsequently opening the file and then failing to write to it for
+ longer than 1 minute will result in rebooting the machine. This
+ could be useful for a networked machine that needs to come back
+ on-line as fast as possible after a lock-up. There's both a watchdog
+ implementation entirely in software (which can sometimes fail to
+ reboot the machine) and a driver for hardware watchdog boards, which
+ are more robust and can also keep track of the temperature inside
+ your computer. For details, read
+ <file:Documentation/watchdog/watchdog-api.txt> in the kernel source.
+
+ The watchdog is usually used together with the watchdog daemon
+ which is available from
+ <ftp://ibiblio.org/pub/Linux/system/daemons/watchdog/>. This daemon can
+ also monitor NFS connections and can reboot the machine when the process
+ table is full.
+
+ If unsure, say N.
+
+if WATCHDOG
+
+config WATCHDOG_NOWAYOUT
+ bool "Disable watchdog shutdown on close"
+ help
+ The default watchdog behaviour (which you get if you say N here) is
+ to stop the timer if the process managing it closes the file
+ /dev/watchdog. It's always remotely possible that this process might
+ get killed. If you say Y here, the watchdog cannot be stopped once
+ it has been started.
+
+#
+# General Watchdog drivers
+#
+
+comment "Watchdog Device Drivers"
+
+# Architecture Independent
+
+config SOFT_WATCHDOG
+ tristate "Software watchdog"
+ help
+ A software monitoring watchdog. This will fail to reboot your system
+ from some situations that the hardware watchdog will recover
+ from. Equally it's a lot cheaper to install.
+
+ To compile this driver as a module, choose M here: the
+ module will be called softdog.
+
+config WM831X_WATCHDOG
+ tristate "WM831x watchdog"
+ depends on MFD_WM831X
+ help
+ Support for the watchdog in the WM831x AudioPlus PMICs. When
+ the watchdog triggers the system will be reset.
+
+config WM8350_WATCHDOG
+ tristate "WM8350 watchdog"
+ depends on MFD_WM8350
+ help
+ Support for the watchdog in the WM8350 AudioPlus PMIC. When
+ the watchdog triggers the system will be reset.
+
+# ALPHA Architecture
+
+# ARM Architecture
+
+config ARM_SP805_WATCHDOG
+ tristate "ARM SP805 Watchdog"
+ depends on ARM_AMBA
+ help
+ ARM Primecell SP805 Watchdog timer. This will reboot your system when
+ the timeout is reached.
+
+config AT91RM9200_WATCHDOG
+ tristate "AT91RM9200 watchdog"
+ depends on ARCH_AT91RM9200
+ help
+ Watchdog timer embedded into AT91RM9200 chips. This will reboot your
+ system when the timeout is reached.
+
+config AT91SAM9X_WATCHDOG
+ tristate "AT91SAM9X / AT91CAP9 watchdog"
+ depends on ARCH_AT91 && !ARCH_AT91RM9200
+ help
+ Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will
+ reboot your system when the timeout is reached.
+
+config 21285_WATCHDOG
+ tristate "DC21285 watchdog"
+ depends on FOOTBRIDGE
+ help
+ The Intel Footbridge chip contains a built-in watchdog circuit. Say Y
+ here if you wish to use this. Alternatively say M to compile the
+ driver as a module, which will be called wdt285.
+
+ This driver does not work on all machines. In particular, early CATS
+ boards have hardware problems that will cause the machine to simply
+ lock up if the watchdog fires.
+
+ "If in doubt, leave it out" - say N.
+
+config 977_WATCHDOG
+ tristate "NetWinder WB83C977 watchdog"
+ depends on FOOTBRIDGE && ARCH_NETWINDER
+ help
+ Say Y here to include support for the WB977 watchdog included in
+ NetWinder machines. Alternatively say M to compile the driver as
+ a module, which will be called wdt977.
+
+ Not sure? It's safe to say N.
+
+config IXP2000_WATCHDOG
+ tristate "IXP2000 Watchdog"
+ depends on ARCH_IXP2000
+ help
+ Say Y here if to include support for the watchdog timer
+ in the Intel IXP2000(2400, 2800, 2850) network processors.
+ This driver can be built as a module by choosing M. The module
+ will be called ixp2000_wdt.
+
+ Say N if you are unsure.
+
+config IXP4XX_WATCHDOG
+ tristate "IXP4xx Watchdog"
+ depends on ARCH_IXP4XX
+ help
+ Say Y here if to include support for the watchdog timer
+ in the Intel IXP4xx network processors. This driver can
+ be built as a module by choosing M. The module will
+ be called ixp4xx_wdt.
+
+ Note: The internal IXP4xx watchdog does a soft CPU reset
+ which doesn't reset any peripherals. There are circumstances
+ where the watchdog will fail to reset the board correctly
+ (e.g., if the boot ROM is in an unreadable state).
+
+ Say N if you are unsure.
+
+config KS8695_WATCHDOG
+ tristate "KS8695 watchdog"
+ depends on ARCH_KS8695
+ help
+ Watchdog timer embedded into KS8695 processor. This will reboot your
+ system when the timeout is reached.
+
+config HAVE_S3C2410_WATCHDOG
+ bool
+ help
+ This will include watchdog timer support for Samsung SoCs. If
+ you want to include watchdog support for any machine, kindly
+ select this in the respective mach-XXXX/Kconfig file.
+
+config S3C2410_WATCHDOG
+ tristate "S3C2410 Watchdog"
+ depends on ARCH_S3C2410 || HAVE_S3C2410_WATCHDOG
+ help
+ Watchdog timer block in the Samsung SoCs. This will reboot
+ the system when the timer expires with the watchdog enabled.
+
+ The driver is limited by the speed of the system's PCLK
+ signal, so with reasonably fast systems (PCLK around 50-66MHz)
+ then watchdog intervals of over approximately 20seconds are
+ unavailable.
+
+ The driver can be built as a module by choosing M, and will
+ be called s3c2410_wdt
+
+config SA1100_WATCHDOG
+ tristate "SA1100/PXA2xx watchdog"
+ depends on ARCH_SA1100 || ARCH_PXA
+ help
+ Watchdog timer embedded into SA11x0 and PXA2xx chips. This will
+ reboot your system when timeout is reached.
+
+ NOTE: once enabled, this timer cannot be disabled.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sa1100_wdt.
+
+config MPCORE_WATCHDOG
+ tristate "MPcore watchdog"
+ depends on HAVE_ARM_TWD
+ help
+ Watchdog timer embedded into the MPcore system.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mpcore_wdt.
+
+config EP93XX_WATCHDOG
+ tristate "EP93xx Watchdog"
+ depends on ARCH_EP93XX
+ help
+ Say Y here if to include support for the watchdog timer
+ embedded in the Cirrus Logic EP93xx family of devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ep93xx_wdt.
+
+config OMAP_WATCHDOG
+ tristate "OMAP Watchdog"
+ depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS
+ help
+ Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog. Say 'Y'
+ here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer.
+
+config PNX4008_WATCHDOG
+ tristate "PNX4008 and LPC32XX Watchdog"
+ depends on ARCH_PNX4008 || ARCH_LPC32XX
+ help
+ Say Y here if to include support for the watchdog timer
+ in the PNX4008 or LPC32XX processor.
+ This driver can be built as a module by choosing M. The module
+ will be called pnx4008_wdt.
+
+ Say N if you are unsure.
+
+config IOP_WATCHDOG
+ tristate "IOP Watchdog"
+ depends on PLAT_IOP
+ select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X)
+ help
+ Say Y here if to include support for the watchdog timer
+ in the Intel IOP3XX & IOP13XX I/O Processors. This driver can
+ be built as a module by choosing M. The module will
+ be called iop_wdt.
+
+ Note: The IOP13XX watchdog does an Internal Bus Reset which will
+ affect both cores and the peripherals of the IOP. The ATU-X
+ and/or ATUe configuration registers will remain intact, but if
+ operating as an Root Complex and/or Central Resource, the PCI-X
+ and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER.
+
+config DAVINCI_WATCHDOG
+ tristate "DaVinci watchdog"
+ depends on ARCH_DAVINCI
+ help
+ Say Y here if to include support for the watchdog timer
+ in the DaVinci DM644x/DM646x processors.
+ To compile this driver as a module, choose M here: the
+ module will be called davinci_wdt.
+
+ NOTE: once enabled, this timer cannot be disabled.
+ Say N if you are unsure.
+
+config ORION_WATCHDOG
+ tristate "Orion watchdog"
+ depends on ARCH_ORION5X || ARCH_KIRKWOOD
+ help
+ Say Y here if to include support for the watchdog timer
+ in the Marvell Orion5x and Kirkwood ARM SoCs.
+ To compile this driver as a module, choose M here: the
+ module will be called orion_wdt.
+
+config COH901327_WATCHDOG
+ bool "ST-Ericsson COH 901 327 watchdog"
+ depends on ARCH_U300
+ default y if MACH_U300
+ help
+ Say Y here to include Watchdog timer support for the
+ watchdog embedded into the ST-Ericsson U300 series platforms.
+ This watchdog is used to reset the system and thus cannot be
+ compiled as a module.
+
+config TWL4030_WATCHDOG
+ tristate "TWL4030 Watchdog"
+ depends on TWL4030_CORE
+ help
+ Support for TI TWL4030 watchdog. Say 'Y' here to enable the
+ watchdog timer support for TWL4030 chips.
+
+config STMP3XXX_WATCHDOG
+ tristate "Freescale STMP3XXX watchdog"
+ depends on ARCH_STMP3XXX
+ help
+ Say Y here if to include support for the watchdog timer
+ for the Sigmatel STMP37XX/378X SoC.
+ To compile this driver as a module, choose M here: the
+ module will be called stmp3xxx_wdt.
+
+config NUC900_WATCHDOG
+ tristate "Nuvoton NUC900 watchdog"
+ depends on ARCH_W90X900
+ help
+ Say Y here if to include support for the watchdog timer
+ for the Nuvoton NUC900 series SoCs.
+ To compile this driver as a module, choose M here: the
+ module will be called nuc900_wdt.
+
+config ADX_WATCHDOG
+ tristate "Avionic Design Xanthos watchdog"
+ depends on ARCH_PXA_ADX
+ help
+ Say Y here if you want support for the watchdog timer on Avionic
+ Design Xanthos boards.
+
+config TS72XX_WATCHDOG
+ tristate "TS-72XX SBC Watchdog"
+ depends on MACH_TS72XX
+ help
+ Technologic Systems TS-7200, TS-7250 and TS-7260 boards have
+ watchdog timer implemented in a external CPLD chip. Say Y here
+ if you want to support for the watchdog timer on TS-72XX boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ts72xx_wdt.
+
+config MAX63XX_WATCHDOG
+ tristate "Max63xx watchdog"
+ depends on ARM && HAS_IOMEM
+ help
+ Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
+
+config IMX2_WDT
+ tristate "IMX2+ Watchdog"
+ depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX5 || ARCH_MX6
+ help
+ This is the driver for the hardware watchdog
+ on the Freescale IMX2 and later processors.
+ If you have one of these processors and wish to have
+ watchdog support enabled, say Y, otherwise say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx2_wdt.
+
+# AVR32 Architecture
+
+config AT32AP700X_WDT
+ tristate "AT32AP700x watchdog"
+ depends on CPU_AT32AP700X
+ help
+ Watchdog timer embedded into AT32AP700x devices. This will reboot
+ your system when the timeout is reached.
+
+# BLACKFIN Architecture
+
+config BFIN_WDT
+ tristate "Blackfin On-Chip Watchdog Timer"
+ depends on BLACKFIN
+ ---help---
+ If you say yes here you will get support for the Blackfin On-Chip
+ Watchdog Timer. If you have one of these processors and wish to
+ have watchdog support enabled, say Y, otherwise say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_wdt.
+
+# CRIS Architecture
+
+# FRV Architecture
+
+# H8300 Architecture
+
+# X86 (i386 + ia64 + x86_64) Architecture
+
+config ACQUIRE_WDT
+ tristate "Acquire SBC Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on Single Board
+ Computers produced by Acquire Inc (and others). This watchdog
+ simply watches your kernel to make sure it doesn't freeze, and if
+ it does, it reboots your computer after a certain amount of time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called acquirewdt.
+
+ Most people will say N.
+
+config ADVANTECH_WDT
+ tristate "Advantech SBC Watchdog Timer"
+ depends on X86
+ help
+ If you are configuring a Linux kernel for the Advantech single-board
+ computer, say `Y' here to support its built-in watchdog timer
+ feature. More information can be found at
+ <http://www.advantech.com.tw/products/>
+
+config ALIM1535_WDT
+ tristate "ALi M1535 PMU Watchdog Timer"
+ depends on X86 && PCI
+ ---help---
+ This is the driver for the hardware watchdog on the ALi M1535 PMU.
+
+ To compile this driver as a module, choose M here: the
+ module will be called alim1535_wdt.
+
+ Most people will say N.
+
+config ALIM7101_WDT
+ tristate "ALi M7101 PMU Computer Watchdog"
+ depends on PCI
+ help
+ This is the driver for the hardware watchdog on the ALi M7101 PMU
+ as used in the x86 Cobalt servers and also found in some
+ SPARC Netra servers too.
+
+ To compile this driver as a module, choose M here: the
+ module will be called alim7101_wdt.
+
+ Most people will say N.
+
+config F71808E_WDT
+ tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog"
+ depends on X86 && EXPERIMENTAL
+ help
+ This is the driver for the hardware watchdog on the Fintek
+ F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers.
+
+ You can compile this driver directly into the kernel, or use
+ it as a module. The module will be called f71808e_wdt.
+
+config SP5100_TCO
+ tristate "AMD/ATI SP5100 TCO Timer/Watchdog"
+ depends on X86 && PCI
+ ---help---
+ Hardware watchdog driver for the AMD/ATI SP5100 chipset. The TCO
+ (Total Cost of Ownership) timer is a watchdog timer that will reboot
+ the machine after its expiration. The expiration time can be
+ configured with the "heartbeat" parameter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sp5100_tco.
+
+config GEODE_WDT
+ tristate "AMD Geode CS5535/CS5536 Watchdog"
+ depends on CS5535_MFGPT
+ help
+ This driver enables a watchdog capability built into the
+ CS5535/CS5536 companion chips for the AMD Geode GX and LX
+ processors. This watchdog watches your kernel to make sure
+ it doesn't freeze, and if it does, it reboots your computer after
+ a certain amount of time.
+
+ You can compile this driver directly into the kernel, or use
+ it as a module. The module will be called geodewdt.
+
+config SC520_WDT
+ tristate "AMD Elan SC520 processor Watchdog"
+ depends on X86
+ help
+ This is the driver for the hardware watchdog built in to the
+ AMD "Elan" SC520 microcomputer commonly used in embedded systems.
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
+
+ You can compile this driver directly into the kernel, or use
+ it as a module. The module will be called sc520_wdt.
+
+config SBC_FITPC2_WATCHDOG
+ tristate "Compulab SBC-FITPC2 watchdog"
+ depends on X86
+ ---help---
+ This is the driver for the built-in watchdog timer on the fit-PC2,
+ fit-PC2i, CM-iAM single-board computers made by Compulab.
+
+ It`s possible to enable watchdog timer either from BIOS (F2) or from booted Linux.
+ When "Watchdog Timer Value" enabled one can set 31-255 s operational range.
+
+ Entering BIOS setup temporary disables watchdog operation regardless to current state,
+ so system will not be restarted while user in BIOS setup.
+
+ Once watchdog was enabled the system will be restarted every
+ "Watchdog Timer Value" period, so to prevent it user can restart or
+ disable the watchdog.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sbc_fitpc2_wdt.
+
+ Most people will say N.
+
+config EUROTECH_WDT
+ tristate "Eurotech CPU-1220/1410 Watchdog Timer"
+ depends on X86
+ help
+ Enable support for the watchdog timer on the Eurotech CPU-1220 and
+ CPU-1410 cards. These are PC/104 SBCs. Spec sheets and product
+ information are at <http://www.eurotech.it/>.
+
+config IB700_WDT
+ tristate "IB700 SBC Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on the IB700 Single
+ Board Computer produced by TMC Technology (www.tmc-uk.com). This watchdog
+ simply watches your kernel to make sure it doesn't freeze, and if
+ it does, it reboots your computer after a certain amount of time.
+
+ This driver is like the WDT501 driver but for slightly different hardware.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ib700wdt.
+
+ Most people will say N.
+
+config IBMASR
+ tristate "IBM Automatic Server Restart"
+ depends on X86
+ help
+ This is the driver for the IBM Automatic Server Restart watchdog
+ timer built-in into some eServer xSeries machines.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ibmasr.
+
+config WAFER_WDT
+ tristate "ICP Single Board Computer Watchdog Timer"
+ depends on X86
+ help
+ This is a driver for the hardware watchdog on the ICP Single
+ Board Computer. This driver is working on (at least) the following
+ IPC SBC's: Wafer 5823, Rocky 4783, Rocky 3703 and Rocky 3782.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wafer5823wdt.
+
+config I6300ESB_WDT
+ tristate "Intel 6300ESB Timer/Watchdog"
+ depends on X86 && PCI
+ ---help---
+ Hardware driver for the watchdog timer built into the Intel
+ 6300ESB controller hub.
+
+ To compile this driver as a module, choose M here: the
+ module will be called i6300esb.
+
+config INTEL_SCU_WATCHDOG
+ bool "Intel SCU Watchdog for Mobile Platforms"
+ depends on X86_MRST
+ ---help---
+ Hardware driver for the watchdog time built into the Intel SCU
+ for Intel Mobile Platforms.
+
+ To compile this driver as a module, choose M here.
+
+config ITCO_WDT
+ tristate "Intel TCO Timer/Watchdog"
+ depends on (X86 || IA64) && PCI
+ ---help---
+ Hardware driver for the intel TCO timer based watchdog devices.
+ These drivers are included in the Intel 82801 I/O Controller
+ Hub family (from ICH0 up to ICH10) and in the Intel 63xxESB
+ controller hub.
+
+ The TCO (Total Cost of Ownership) timer is a watchdog timer
+ that will reboot the machine after its second expiration. The
+ expiration time can be configured with the "heartbeat" parameter.
+
+ On some motherboards the driver may fail to reset the chipset's
+ NO_REBOOT flag which prevents the watchdog from rebooting the
+ machine. If this is the case you will get a kernel message like
+ "failed to reset NO_REBOOT flag, reboot disabled by hardware".
+
+ To compile this driver as a module, choose M here: the
+ module will be called iTCO_wdt.
+
+config ITCO_VENDOR_SUPPORT
+ bool "Intel TCO Timer/Watchdog Specific Vendor Support"
+ depends on ITCO_WDT
+ ---help---
+ Add vendor specific support to the intel TCO timer based watchdog
+ devices. At this moment we only have additional support for some
+ SuperMicro Inc. motherboards.
+
+config IT8712F_WDT
+ tristate "IT8712F (Smart Guardian) Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the built-in watchdog timer on the IT8712F
+ Super I/0 chipset used on many motherboards.
+
+ If the driver does not work, then make sure that the game port in
+ the BIOS is enabled.
+
+ To compile this driver as a module, choose M here: the
+ module will be called it8712f_wdt.
+
+config IT87_WDT
+ tristate "IT87 Watchdog Timer"
+ depends on X86 && EXPERIMENTAL
+ ---help---
+ This is the driver for the hardware watchdog on the ITE IT8702,
+ IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 Super I/O chips.
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
+
+ To compile this driver as a module, choose M here: the module will
+ be called it87_wdt.
+
+config HP_WATCHDOG
+ tristate "HP ProLiant iLO2+ Hardware Watchdog Timer"
+ depends on X86 && PCI
+ help
+ A software monitoring watchdog and NMI sourcing driver. This driver
+ will detect lockups and provide a stack trace. This is a driver that
+ will only load on an HP ProLiant system with a minimum of iLO2 support.
+ To compile this driver as a module, choose M here: the module will be
+ called hpwdt.
+
+config HPWDT_NMI_DECODING
+ bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
+ depends on HP_WATCHDOG
+ default y
+ help
+ When an NMI occurs this feature will make the necessary BIOS calls to
+ log the cause of the NMI.
+
+config SC1200_WDT
+ tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
+ depends on X86
+ help
+ This is a driver for National Semiconductor PC87307/PC97307 hardware
+ watchdog cards as found on the SC1200. This watchdog is mainly used
+ for power management purposes and can be used to power down the device
+ during inactivity periods (includes interrupt activity monitoring).
+
+ To compile this driver as a module, choose M here: the
+ module will be called sc1200wdt.
+
+ Most people will say N.
+
+config SCx200_WDT
+ tristate "National Semiconductor SCx200 Watchdog"
+ depends on SCx200 && PCI
+ help
+ Enable the built-in watchdog timer support on the National
+ Semiconductor SCx200 processors.
+
+ If compiled as a module, it will be called scx200_wdt.
+
+config PC87413_WDT
+ tristate "NS PC87413 watchdog"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on the PC87413 chipset
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pc87413_wdt.
+
+ Most people will say N.
+
+config NV_TCO
+ tristate "nVidia TCO Timer/Watchdog"
+ depends on X86 && PCI
+ ---help---
+ Hardware driver for the TCO timer built into the nVidia Hub family
+ (such as the MCP51). The TCO (Total Cost of Ownership) timer is a
+ watchdog timer that will reboot the machine after its second
+ expiration. The expiration time can be configured with the
+ "heartbeat" parameter.
+
+ On some motherboards the driver may fail to reset the chipset's
+ NO_REBOOT flag which prevents the watchdog from rebooting the
+ machine. If this is the case you will get a kernel message like
+ "failed to reset NO_REBOOT flag, reboot disabled by hardware".
+
+ To compile this driver as a module, choose M here: the
+ module will be called nv_tco.
+
+config RDC321X_WDT
+ tristate "RDC R-321x SoC watchdog"
+ depends on X86_RDC321X
+ help
+ This is the driver for the built in hardware watchdog
+ in the RDC R-321x SoC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rdc321x_wdt.
+
+config 60XX_WDT
+ tristate "SBC-60XX Watchdog Timer"
+ depends on X86
+ help
+ This driver can be used with the watchdog timer found on some
+ single board computers, namely the 6010 PII based computer.
+ It may well work with other cards. It reads port 0x443 to enable
+ and re-set the watchdog timer, and reads port 0x45 to disable
+ the watchdog. If you have a card that behave in similar ways,
+ you can probably make this driver work with your card as well.
+
+ You can compile this driver directly into the kernel, or use
+ it as a module. The module will be called sbc60xxwdt.
+
+config SBC8360_WDT
+ tristate "SBC8360 Watchdog Timer"
+ depends on X86
+ ---help---
+
+ This is the driver for the hardware watchdog on the SBC8360 Single
+ Board Computer produced by Axiomtek Co., Ltd. (www.axiomtek.com).
+
+ To compile this driver as a module, choose M here: the
+ module will be called sbc8360.
+
+ Most people will say N.
+
+config SBC7240_WDT
+ tristate "SBC Nano 7240 Watchdog Timer"
+ depends on X86_32
+ ---help---
+ This is the driver for the hardware watchdog found on the IEI
+ single board computers EPIC Nano 7240 (and likely others). This
+ watchdog simply watches your kernel to make sure it doesn't freeze,
+ and if it does, it reboots your computer after a certain amount of
+ time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sbc7240_wdt.
+
+config CPU5_WDT
+ tristate "SMA CPU5 Watchdog"
+ depends on X86
+ ---help---
+ TBD.
+ To compile this driver as a module, choose M here: the
+ module will be called cpu5wdt.
+
+config SMSC_SCH311X_WDT
+ tristate "SMSC SCH311X Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog timer on the
+ SMSC SCH3112, SCH3114 and SCH3116 Super IO chipset
+ (LPC IO with 8042 KBC, Reset Generation, HWM and multiple
+ serial ports).
+
+ To compile this driver as a module, choose M here: the
+ module will be called sch311x_wdt.
+
+config SMSC37B787_WDT
+ tristate "Winbond SMsC37B787 Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog component on the
+ Winbond SMsC37B787 chipset as used on the NetRunner Mainboard
+ from Vision Systems and maybe others.
+
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
+
+ Usually a userspace daemon will notify the kernel WDT driver that
+ userspace is still alive, at regular intervals.
+
+ To compile this driver as a module, choose M here: the
+ module will be called smsc37b787_wdt.
+
+ Most people will say N.
+
+config W83627HF_WDT
+ tristate "W83627HF/W83627DHG Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on the W83627HF chipset
+ as used in Advantech PC-9578 and Tyan S2721-533 motherboards
+ (and likely others). The driver also supports the W83627DHG chip.
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w83627hf_wdt.
+
+ Most people will say N.
+
+config W83697HF_WDT
+ tristate "W83697HF/W83697HG Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on the W83697HF/HG
+ chipset as used in Dedibox/VIA motherboards (and likely others).
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w83697hf_wdt.
+
+ Most people will say N.
+
+config W83697UG_WDT
+ tristate "W83697UG/W83697UF Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on the W83697UG/UF
+ chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others).
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w83697ug_wdt.
+
+ Most people will say N.
+
+config W83877F_WDT
+ tristate "W83877F (EMACS) Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on the W83877F chipset
+ as used in EMACS PC-104 motherboards (and likely others). This
+ watchdog simply watches your kernel to make sure it doesn't freeze,
+ and if it does, it reboots your computer after a certain amount of
+ time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w83877f_wdt.
+
+ Most people will say N.
+
+config W83977F_WDT
+ tristate "W83977F (PCM-5335) Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on the W83977F I/O chip
+ as used in AAEON's PCM-5335 SBC (and likely others). This
+ watchdog simply watches your kernel to make sure it doesn't freeze,
+ and if it does, it reboots your computer after a certain amount of
+ time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w83977f_wdt.
+
+config MACHZ_WDT
+ tristate "ZF MachZ Watchdog"
+ depends on X86
+ ---help---
+ If you are using a ZF Micro MachZ processor, say Y here, otherwise
+ N. This is the driver for the watchdog timer built-in on that
+ processor using ZF-Logic interface. This watchdog simply watches
+ your kernel to make sure it doesn't freeze, and if it does, it
+ reboots your computer after a certain amount of time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called machzwd.
+
+config SBC_EPX_C3_WATCHDOG
+ tristate "Winsystems SBC EPX-C3 watchdog"
+ depends on X86
+ ---help---
+ This is the driver for the built-in watchdog timer on the EPX-C3
+ Single-board computer made by Winsystems, Inc.
+
+ *Note*: This hardware watchdog is not probeable and thus there
+ is no way to know if writing to its IO address will corrupt
+ your system or have any real effect. The only way to be sure
+ that this driver does what you want is to make sure you
+ are running it on an EPX-C3 from Winsystems with the watchdog
+ timer at IO address 0x1ee and 0x1ef. It will write to both those
+ IO ports. Basically, the assumption is made that if you compile
+ this driver into your kernel and/or load it as a module, that you
+ know what you are doing and that you are in fact running on an
+ EPX-C3 board!
+
+ To compile this driver as a module, choose M here: the
+ module will be called sbc_epx_c3.
+
+# M32R Architecture
+
+# M68K Architecture
+
+config M54xx_WATCHDOG
+ tristate "MCF54xx watchdog support"
+ depends on M548x
+ help
+ To compile this driver as a module, choose M here: the
+ module will be called m54xx_wdt.
+
+# MIPS Architecture
+
+config ATH79_WDT
+ tristate "Atheros AR71XX/AR724X/AR913X hardware watchdog"
+ depends on ATH79
+ help
+ Hardware driver for the built-in watchdog timer on the Atheros
+ AR71XX/AR724X/AR913X SoCs.
+
+config BCM47XX_WDT
+ tristate "Broadcom BCM47xx Watchdog Timer"
+ depends on BCM47XX
+ help
+ Hardware driver for the Broadcom BCM47xx Watchog Timer.
+
+config RC32434_WDT
+ tristate "IDT RC32434 SoC Watchdog Timer"
+ depends on MIKROTIK_RB532
+ help
+ Hardware driver for the IDT RC32434 SoC built-in
+ watchdog timer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rc32434_wdt.
+
+config INDYDOG
+ tristate "Indy/I2 Hardware Watchdog"
+ depends on SGI_HAS_INDYDOG
+ help
+ Hardware driver for the Indy's/I2's watchdog. This is a
+ watchdog timer that will reboot the machine after a 60 second
+ timer expired and no process has written to /dev/watchdog during
+ that time.
+
+config JZ4740_WDT
+ tristate "Ingenic jz4740 SoC hardware watchdog"
+ depends on MACH_JZ4740
+ help
+ Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs.
+
+config WDT_MTX1
+ tristate "MTX-1 Hardware Watchdog"
+ depends on MIPS_MTX1
+ help
+ Hardware driver for the MTX-1 boards. This is a watchdog timer that
+ will reboot the machine after a 100 seconds timer expired.
+
+config PNX833X_WDT
+ tristate "PNX833x Hardware Watchdog"
+ depends on SOC_PNX8335
+ help
+ Hardware driver for the PNX833x's watchdog. This is a
+ watchdog timer that will reboot the machine after a programmable
+ timer has expired and no process has written to /dev/watchdog during
+ that time.
+
+config SIBYTE_WDOG
+ tristate "Sibyte SoC hardware watchdog"
+ depends on CPU_SB1
+ help
+ Watchdog driver for the built in watchdog hardware in Sibyte
+ SoC processors. There are apparently two watchdog timers
+ on such processors; this driver supports only the first one,
+ because currently Linux only supports exporting one watchdog
+ to userspace.
+
+ To compile this driver as a loadable module, choose M here.
+ The module will be called sb_wdog.
+
+config AR7_WDT
+ tristate "TI AR7 Watchdog Timer"
+ depends on AR7
+ help
+ Hardware driver for the TI AR7 Watchdog Timer.
+
+config TXX9_WDT
+ tristate "Toshiba TXx9 Watchdog Timer"
+ depends on CPU_TX39XX || CPU_TX49XX
+ help
+ Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs.
+
+config OCTEON_WDT
+ tristate "Cavium OCTEON SOC family Watchdog Timer"
+ depends on CPU_CAVIUM_OCTEON
+ default y
+ select EXPORT_UASM if OCTEON_WDT = m
+ help
+ Hardware driver for OCTEON's on chip watchdog timer.
+ Enables the watchdog for all cores running Linux. It
+ installs a NMI handler and pokes the watchdog based on an
+ interrupt. On first expiration of the watchdog, the
+ interrupt handler pokes it. The second expiration causes an
+ NMI that prints a message. The third expiration causes a
+ global soft reset.
+
+ When userspace has /dev/watchdog open, no poking is done
+ from the first interrupt, it is then only poked when the
+ device is written.
+
+config BCM63XX_WDT
+ tristate "Broadcom BCM63xx hardware watchdog"
+ depends on BCM63XX
+ help
+ Watchdog driver for the built in watchdog hardware in Broadcom
+ BCM63xx SoC.
+
+ To compile this driver as a loadable module, choose M here.
+ The module will be called bcm63xx_wdt.
+
+config LANTIQ_WDT
+ tristate "Lantiq SoC watchdog"
+ depends on LANTIQ
+ help
+ Hardware driver for the Lantiq SoC Watchdog Timer.
+
+# PARISC Architecture
+
+# POWERPC Architecture
+
+config GEF_WDT
+ tristate "GE Watchdog Timer"
+ depends on GEF_SBC610 || GEF_SBC310 || GEF_PPC9A
+ ---help---
+ Watchdog timer found in a number of GE single board computers.
+
+config MPC5200_WDT
+ bool "MPC52xx Watchdog Timer"
+ depends on PPC_MPC52xx
+ help
+ Use General Purpose Timer (GPT) 0 on the MPC5200 as Watchdog.
+
+config 8xxx_WDT
+ tristate "MPC8xxx Platform Watchdog Timer"
+ depends on PPC_8xx || PPC_83xx || PPC_86xx
+ help
+ This driver is for a SoC level watchdog that exists on some
+ Freescale PowerPC processors. So far this driver supports:
+ - MPC8xx watchdogs
+ - MPC83xx watchdogs
+ - MPC86xx watchdogs
+
+ For BookE processors (MPC85xx) use the BOOKE_WDT driver instead.
+
+config MV64X60_WDT
+ tristate "MV64X60 (Marvell Discovery) Watchdog Timer"
+ depends on MV64X60
+
+config PIKA_WDT
+ tristate "PIKA FPGA Watchdog"
+ depends on WARP
+ default y
+ help
+ This enables the watchdog in the PIKA FPGA. Currently used on
+ the Warp platform.
+
+config BOOKE_WDT
+ tristate "PowerPC Book-E Watchdog Timer"
+ depends on BOOKE || 4xx
+ ---help---
+ Watchdog driver for PowerPC Book-E chips, such as the Freescale
+ MPC85xx SOCs and the IBM PowerPC 440.
+
+ Please see Documentation/watchdog/watchdog-api.txt for
+ more information.
+
+config BOOKE_WDT_DEFAULT_TIMEOUT
+ int "PowerPC Book-E Watchdog Timer Default Timeout"
+ depends on BOOKE_WDT
+ default 38 if FSL_BOOKE
+ range 0 63 if FSL_BOOKE
+ default 3 if !FSL_BOOKE
+ range 0 3 if !FSL_BOOKE
+ help
+ Select the default watchdog timer period to be used by the PowerPC
+ Book-E watchdog driver. A watchdog "event" occurs when the bit
+ position represented by this number transitions from zero to one.
+
+ For Freescale Book-E processors, this is a number between 0 and 63.
+ For other Book-E processors, this is a number between 0 and 3.
+
+ The value can be overidden by the wdt_period command-line parameter.
+
+# PPC64 Architecture
+
+config WATCHDOG_RTAS
+ tristate "RTAS watchdog"
+ depends on PPC_RTAS
+ help
+ This driver adds watchdog support for the RTAS watchdog.
+
+ To compile this driver as a module, choose M here. The module
+ will be called wdrtas.
+
+# S390 Architecture
+
+config ZVM_WATCHDOG
+ tristate "z/VM Watchdog Timer"
+ depends on S390
+ help
+ IBM s/390 and zSeries machines running under z/VM 5.1 or later
+ provide a virtual watchdog timer to their guest that cause a
+ user define Control Program command to be executed after a
+ timeout.
+
+ To compile this driver as a module, choose M here. The module
+ will be called vmwatchdog.
+
+# SUPERH (sh + sh64) Architecture
+
+config SH_WDT
+ tristate "SuperH Watchdog"
+ depends on SUPERH && (CPU_SH3 || CPU_SH4)
+ help
+ This driver adds watchdog support for the integrated watchdog in the
+ SuperH processors. If you have one of these processors and wish
+ to have watchdog support enabled, say Y, otherwise say N.
+
+ As a side note, saying Y here will automatically boost HZ to 1000
+ so that the timer has a chance to clear the overflow counter. On
+ slower systems (such as the SH-2 and SH-3) this will likely yield
+ some performance issues. As such, the WDT should be avoided here
+ unless it is absolutely necessary.
+
+ To compile this driver as a module, choose M here: the
+ module will be called shwdt.
+
+# SPARC Architecture
+
+# SPARC64 Architecture
+
+config WATCHDOG_CP1XXX
+ tristate "CP1XXX Hardware Watchdog support"
+ depends on SPARC64 && PCI
+ ---help---
+ This is the driver for the hardware watchdog timers present on
+ Sun Microsystems CompactPCI models CP1400 and CP1500.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cpwatchdog.
+
+ If you do not have a CompactPCI model CP1400 or CP1500, or
+ another UltraSPARC-IIi-cEngine boardset with hardware watchdog,
+ you should say N to this option.
+
+config WATCHDOG_RIO
+ tristate "RIO Hardware Watchdog support"
+ depends on SPARC64 && PCI
+ help
+ Say Y here to support the hardware watchdog capability on Sun RIO
+ machines. The watchdog timeout period is normally one minute but
+ can be changed with a boot-time parameter.
+
+# XTENSA Architecture
+
+# Xen Architecture
+
+config XEN_WDT
+ tristate "Xen Watchdog support"
+ depends on XEN
+ help
+ Say Y here to support the hypervisor watchdog capability provided
+ by Xen 4.0 and newer. The watchdog timeout period is normally one
+ minute but can be changed with a boot-time parameter.
+
+#
+# ISA-based Watchdog Cards
+#
+
+comment "ISA-based Watchdog Cards"
+ depends on ISA
+
+config PCWATCHDOG
+ tristate "Berkshire Products ISA-PC Watchdog"
+ depends on ISA
+ ---help---
+ This is the driver for the Berkshire Products ISA-PC Watchdog card.
+ This card simply watches your kernel to make sure it doesn't freeze,
+ and if it does, it reboots your computer after a certain amount of
+ time. This driver is like the WDT501 driver but for different
+ hardware. Please read <file:Documentation/watchdog/pcwd-watchdog.txt>. The PC
+ watchdog cards can be ordered from <http://www.berkprod.com/>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcwd.
+
+ Most people will say N.
+
+config MIXCOMWD
+ tristate "Mixcom Watchdog"
+ depends on ISA
+ ---help---
+ This is a driver for the Mixcom hardware watchdog cards. This
+ watchdog simply watches your kernel to make sure it doesn't freeze,
+ and if it does, it reboots your computer after a certain amount of
+ time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mixcomwd.
+
+ Most people will say N.
+
+config WDT
+ tristate "WDT Watchdog timer"
+ depends on ISA
+ ---help---
+ If you have a WDT500P or WDT501P watchdog board, say Y here,
+ otherwise N. It is not possible to probe for this board, which means
+ that you have to inform the kernel about the IO port and IRQ that
+ is needed (you can do this via the io and irq parameters)
+
+ To compile this driver as a module, choose M here: the
+ module will be called wdt.
+
+#
+# PCI-based Watchdog Cards
+#
+
+comment "PCI-based Watchdog Cards"
+ depends on PCI
+
+config PCIPCWATCHDOG
+ tristate "Berkshire Products PCI-PC Watchdog"
+ depends on PCI
+ ---help---
+ This is the driver for the Berkshire Products PCI-PC Watchdog card.
+ This card simply watches your kernel to make sure it doesn't freeze,
+ and if it does, it reboots your computer after a certain amount of
+ time. The card can also monitor the internal temperature of the PC.
+ More info is available at <http://www.berkprod.com/pci_pc_watchdog.htm>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcwd_pci.
+
+ Most people will say N.
+
+config WDTPCI
+ tristate "PCI-WDT500/501 Watchdog timer"
+ depends on PCI
+ ---help---
+ If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N.
+
+ If you have a PCI-WDT501 watchdog board then you can enable the
+ temperature sensor by setting the type parameter to 501.
+
+ If you want to enable the Fan Tachometer on the PCI-WDT501, then you
+ can do this via the tachometer parameter. Only do this if you have a
+ fan tachometer actually set up.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wdt_pci.
+
+config DA9052_WATCHDOG
+ tristate "Dialog DA9052 Watchdog"
+ depends on PMIC_DIALOG
+ help
+ Support for the watchdog in the DA9052 PMIC.
+#
+# USB-based Watchdog Cards
+#
+
+comment "USB-based Watchdog Cards"
+ depends on USB
+
+config USBPCWATCHDOG
+ tristate "Berkshire Products USB-PC Watchdog"
+ depends on USB
+ ---help---
+ This is the driver for the Berkshire Products USB-PC Watchdog card.
+ This card simply watches your kernel to make sure it doesn't freeze,
+ and if it does, it reboots your computer after a certain amount of
+ time. The card can also monitor the internal temperature of the PC.
+ More info is available at <http://www.berkprod.com/usb_pc_watchdog.htm>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcwd_usb.
+
+ Most people will say N.
+
+endif # WATCHDOG
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
new file mode 100755
index 00000000..fae2cfcc
--- /dev/null
+++ b/drivers/watchdog/Makefile
@@ -0,0 +1,162 @@
+#
+# Makefile for the WatchDog device drivers.
+#
+
+# Only one watchdog can succeed. We probe the ISA/PCI/USB based
+# watchdog-cards first, then the architecture specific watchdog
+# drivers and then the architecture independent "softdog" driver.
+# This means that if your ISA/PCI/USB card isn't detected that
+# you can fall back to an architecture specific driver and if
+# that also fails then you can fall back to the software watchdog
+# to give you some cover.
+
+# ISA-based Watchdog Cards
+obj-$(CONFIG_PCWATCHDOG) += pcwd.o
+obj-$(CONFIG_MIXCOMWD) += mixcomwd.o
+obj-$(CONFIG_WDT) += wdt.o
+
+# PCI-based Watchdog Cards
+obj-$(CONFIG_PCIPCWATCHDOG) += pcwd_pci.o
+obj-$(CONFIG_WDTPCI) += wdt_pci.o
+
+# USB-based Watchdog Cards
+obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
+
+# ALPHA Architecture
+
+# ARM Architecture
+obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
+obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
+obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
+obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o
+obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
+obj-$(CONFIG_977_WATCHDOG) += wdt977.o
+obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
+obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
+obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
+obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
+obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
+obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
+obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
+obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
+obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
+obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
+obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
+obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
+obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
+obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
+obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o
+obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
+obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
+
+# AVR32 Architecture
+obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
+
+# BLACKFIN Architecture
+obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o
+
+# CRIS Architecture
+
+# FRV Architecture
+
+# H8300 Architecture
+
+# X86 (i386 + ia64 + x86_64) Architecture
+obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
+obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
+obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
+obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
+obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
+obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o
+obj-$(CONFIG_GEODE_WDT) += geodewdt.o
+obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
+obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o
+obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o
+obj-$(CONFIG_IB700_WDT) += ib700wdt.o
+obj-$(CONFIG_IBMASR) += ibmasr.o
+obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
+obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o
+obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o
+ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y)
+obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o
+endif
+obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o
+obj-$(CONFIG_IT87_WDT) += it87_wdt.o
+obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o
+obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
+obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
+obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o
+obj-$(CONFIG_NV_TCO) += nv_tco.o
+obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o
+obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
+obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
+obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
+obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
+obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
+obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
+obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
+obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
+obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o
+obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
+obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
+obj-$(CONFIG_MACHZ_WDT) += machzwd.o
+obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
+obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
+
+# M32R Architecture
+
+# M68K Architecture
+obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o
+
+# MIPS Architecture
+obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o
+obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
+obj-$(CONFIG_BCM63XX_WDT) += bcm63xx_wdt.o
+obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o
+obj-$(CONFIG_INDYDOG) += indydog.o
+obj-$(CONFIG_JZ4740_WDT) += jz4740_wdt.o
+obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
+obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
+obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
+obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
+obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
+octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
+obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o
+
+# PARISC Architecture
+
+# POWERPC Architecture
+obj-$(CONFIG_GEF_WDT) += gef_wdt.o
+obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o
+obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
+obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
+obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+
+# PPC64 Architecture
+obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
+
+# S390 Architecture
+
+# SUPERH (sh + sh64) Architecture
+obj-$(CONFIG_SH_WDT) += shwdt.o
+
+# SPARC Architecture
+
+# SPARC64 Architecture
+
+obj-$(CONFIG_WATCHDOG_RIO) += riowd.o
+obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
+
+# XTENSA Architecture
+
+# Xen
+obj-$(CONFIG_XEN_WDT) += xen_wdt.o
+
+# Architecture Independent
+obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
+obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
+obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
+obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
diff --git a/drivers/watchdog/acquirewdt.c b/drivers/watchdog/acquirewdt.c
new file mode 100644
index 00000000..b6a2b58c
--- /dev/null
+++ b/drivers/watchdog/acquirewdt.c
@@ -0,0 +1,344 @@
+/*
+ * Acquire Single Board Computer Watchdog Timer driver
+ *
+ * Based on wdt.c. Original copyright messages:
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
+ * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ * Can't add timeout - driver doesn't allow changing value
+ */
+
+/*
+ * Theory of Operation:
+ * The Watch-Dog Timer is provided to ensure that standalone
+ * Systems can always recover from catastrophic conditions that
+ * caused the CPU to crash. This condition may have occurred by
+ * external EMI or a software bug. When the CPU stops working
+ * correctly, hardware on the board will either perform a hardware
+ * reset (cold boot) or a non-maskable interrupt (NMI) to bring the
+ * system back to a known state.
+ *
+ * The Watch-Dog Timer is controlled by two I/O Ports.
+ * 443 hex - Read - Enable or refresh the Watch-Dog Timer
+ * 043 hex - Read - Disable the Watch-Dog Timer
+ *
+ * To enable the Watch-Dog Timer, a read from I/O port 443h must
+ * be performed. This will enable and activate the countdown timer
+ * which will eventually time out and either reset the CPU or cause
+ * an NMI depending on the setting of a jumper. To ensure that this
+ * reset condition does not occur, the Watch-Dog Timer must be
+ * periodically refreshed by reading the same I/O port 443h.
+ * The Watch-Dog Timer is disabled by reading I/O port 043h.
+ *
+ * The Watch-Dog Timer Time-Out Period is set via jumpers.
+ * It can be 1, 2, 10, 20, 110 or 220 seconds.
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+/* Includes */
+#include <linux/module.h> /* For module specific items */
+#include <linux/moduleparam.h> /* For new moduleparam's */
+#include <linux/types.h> /* For standard types (like size_t) */
+#include <linux/errno.h> /* For the -ENODEV/... values */
+#include <linux/kernel.h> /* For printk/panic/... */
+#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
+ (WATCHDOG_MINOR) */
+#include <linux/watchdog.h> /* For the watchdog specific items */
+#include <linux/fs.h> /* For file operations */
+#include <linux/ioport.h> /* For io-port access */
+#include <linux/platform_device.h> /* For platform_driver framework */
+#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+#include <linux/io.h> /* For inb/outb/... */
+
+/* Module information */
+#define DRV_NAME "acquirewdt"
+#define PFX DRV_NAME ": "
+#define WATCHDOG_NAME "Acquire WDT"
+/* There is no way to see what the correct time-out period is */
+#define WATCHDOG_HEARTBEAT 0
+
+/* internal variables */
+/* the watchdog platform device */
+static struct platform_device *acq_platform_device;
+static unsigned long acq_is_open;
+static char expect_close;
+
+/* module parameters */
+/* You must set this - there is no sane way to probe for this board. */
+static int wdt_stop = 0x43;
+module_param(wdt_stop, int, 0);
+MODULE_PARM_DESC(wdt_stop, "Acquire WDT 'stop' io port (default 0x43)");
+
+/* You must set this - there is no sane way to probe for this board. */
+static int wdt_start = 0x443;
+module_param(wdt_start, int, 0);
+MODULE_PARM_DESC(wdt_start, "Acquire WDT 'start' io port (default 0x443)");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Watchdog Operations
+ */
+
+static void acq_keepalive(void)
+{
+ /* Write a watchdog value */
+ inb_p(wdt_start);
+}
+
+static void acq_stop(void)
+{
+ /* Turn the card off */
+ inb_p(wdt_stop);
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t acq_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+ /* note: just in case someone wrote the magic character
+ five months ago... */
+ expect_close = 0;
+ /* scan to see whether or not we got the
+ magic character */
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ /* Well, anyhow someone wrote to us, we should
+ return that favour */
+ acq_keepalive();
+ }
+ return count;
+}
+
+static long acq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int options, retval = -EINVAL;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = WATCHDOG_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(options, p))
+ return -EFAULT;
+ if (options & WDIOS_DISABLECARD) {
+ acq_stop();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ acq_keepalive();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ acq_keepalive();
+ return 0;
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(WATCHDOG_HEARTBEAT, p);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int acq_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &acq_is_open))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Activate */
+ acq_keepalive();
+ return nonseekable_open(inode, file);
+}
+
+static int acq_close(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ acq_stop();
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ acq_keepalive();
+ }
+ clear_bit(0, &acq_is_open);
+ expect_close = 0;
+ return 0;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations acq_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = acq_write,
+ .unlocked_ioctl = acq_ioctl,
+ .open = acq_open,
+ .release = acq_close,
+};
+
+static struct miscdevice acq_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &acq_fops,
+};
+
+/*
+ * Init & exit routines
+ */
+
+static int __devinit acq_probe(struct platform_device *dev)
+{
+ int ret;
+
+ if (wdt_stop != wdt_start) {
+ if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX
+ "I/O address 0x%04x already in use\n", wdt_stop);
+ ret = -EIO;
+ goto out;
+ }
+ }
+
+ if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ wdt_start);
+ ret = -EIO;
+ goto unreg_stop;
+ }
+ ret = misc_register(&acq_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto unreg_regions;
+ }
+ printk(KERN_INFO PFX "initialized. (nowayout=%d)\n", nowayout);
+
+ return 0;
+unreg_regions:
+ release_region(wdt_start, 1);
+unreg_stop:
+ if (wdt_stop != wdt_start)
+ release_region(wdt_stop, 1);
+out:
+ return ret;
+}
+
+static int __devexit acq_remove(struct platform_device *dev)
+{
+ misc_deregister(&acq_miscdev);
+ release_region(wdt_start, 1);
+ if (wdt_stop != wdt_start)
+ release_region(wdt_stop, 1);
+
+ return 0;
+}
+
+static void acq_shutdown(struct platform_device *dev)
+{
+ /* Turn the WDT off if we have a soft shutdown */
+ acq_stop();
+}
+
+static struct platform_driver acquirewdt_driver = {
+ .probe = acq_probe,
+ .remove = __devexit_p(acq_remove),
+ .shutdown = acq_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+static int __init acq_init(void)
+{
+ int err;
+
+ printk(KERN_INFO
+ "WDT driver for Acquire single board computer initialising.\n");
+
+ err = platform_driver_register(&acquirewdt_driver);
+ if (err)
+ return err;
+
+ acq_platform_device = platform_device_register_simple(DRV_NAME,
+ -1, NULL, 0);
+ if (IS_ERR(acq_platform_device)) {
+ err = PTR_ERR(acq_platform_device);
+ goto unreg_platform_driver;
+ }
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&acquirewdt_driver);
+ return err;
+}
+
+static void __exit acq_exit(void)
+{
+ platform_device_unregister(acq_platform_device);
+ platform_driver_unregister(&acquirewdt_driver);
+ printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+}
+
+module_init(acq_init);
+module_exit(acq_exit);
+
+MODULE_AUTHOR("David Woodhouse");
+MODULE_DESCRIPTION("Acquire Inc. Single Board Computer Watchdog Timer driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c
new file mode 100644
index 00000000..4d40965d
--- /dev/null
+++ b/drivers/watchdog/advantechwdt.c
@@ -0,0 +1,355 @@
+/*
+ * Advantech Single Board Computer WDT driver
+ *
+ * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ * Based on acquirewdt.c which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
+ * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ *
+ * 16-Oct-2002 Rob Radez <rob@osinvestor.com>
+ * Clean up ioctls, clean up init + exit, add expect close support,
+ * add wdt_start and wdt_stop as parameters.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define DRV_NAME "advantechwdt"
+#define PFX DRV_NAME ": "
+#define WATCHDOG_NAME "Advantech WDT"
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+
+/* the watchdog platform device */
+static struct platform_device *advwdt_platform_device;
+static unsigned long advwdt_is_open;
+static char adv_expect_close;
+
+/*
+ * You must set these - there is no sane way to probe for this board.
+ *
+ * To enable or restart, write the timeout value in seconds (1 to 63)
+ * to I/O port wdt_start. To disable, read I/O port wdt_stop.
+ * Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but
+ * check your manual (at least the PCA-6159 seems to be different -
+ * the manual says wdt_stop is 0x43, not 0x443).
+ * (0x43 is also a write-only control register for the 8254 timer!)
+ */
+
+static int wdt_stop = 0x443;
+module_param(wdt_stop, int, 0);
+MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)");
+
+static int wdt_start = 0x443;
+module_param(wdt_start, int, 0);
+MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)");
+
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <=63, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Watchdog Operations
+ */
+
+static void advwdt_ping(void)
+{
+ /* Write a watchdog value */
+ outb_p(timeout, wdt_start);
+}
+
+static void advwdt_disable(void)
+{
+ inb_p(wdt_stop);
+}
+
+static int advwdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > 63)
+ return -EINVAL;
+ timeout = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t advwdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ adv_expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ adv_expect_close = 42;
+ }
+ }
+ advwdt_ping();
+ }
+ return count;
+}
+
+static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int new_timeout;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = WATCHDOG_NAME,
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+ if (options & WDIOS_DISABLECARD) {
+ advwdt_disable();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ advwdt_ping();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ advwdt_ping();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (advwdt_set_heartbeat(new_timeout))
+ return -EINVAL;
+ advwdt_ping();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int advwdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &advwdt_is_open))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+
+ advwdt_ping();
+ return nonseekable_open(inode, file);
+}
+
+static int advwdt_close(struct inode *inode, struct file *file)
+{
+ if (adv_expect_close == 42) {
+ advwdt_disable();
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ advwdt_ping();
+ }
+ clear_bit(0, &advwdt_is_open);
+ adv_expect_close = 0;
+ return 0;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations advwdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = advwdt_write,
+ .unlocked_ioctl = advwdt_ioctl,
+ .open = advwdt_open,
+ .release = advwdt_close,
+};
+
+static struct miscdevice advwdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &advwdt_fops,
+};
+
+/*
+ * Init & exit routines
+ */
+
+static int __devinit advwdt_probe(struct platform_device *dev)
+{
+ int ret;
+
+ if (wdt_stop != wdt_start) {
+ if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX
+ "I/O address 0x%04x already in use\n",
+ wdt_stop);
+ ret = -EIO;
+ goto out;
+ }
+ }
+
+ if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX
+ "I/O address 0x%04x already in use\n",
+ wdt_start);
+ ret = -EIO;
+ goto unreg_stop;
+ }
+
+ /* Check that the heartbeat value is within it's range ;
+ * if not reset to the default */
+ if (advwdt_set_heartbeat(timeout)) {
+ advwdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 1<=x<=63, using %d\n", timeout);
+ }
+
+ ret = misc_register(&advwdt_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto unreg_regions;
+ }
+ printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+out:
+ return ret;
+unreg_regions:
+ release_region(wdt_start, 1);
+unreg_stop:
+ if (wdt_stop != wdt_start)
+ release_region(wdt_stop, 1);
+ goto out;
+}
+
+static int __devexit advwdt_remove(struct platform_device *dev)
+{
+ misc_deregister(&advwdt_miscdev);
+ release_region(wdt_start, 1);
+ if (wdt_stop != wdt_start)
+ release_region(wdt_stop, 1);
+
+ return 0;
+}
+
+static void advwdt_shutdown(struct platform_device *dev)
+{
+ /* Turn the WDT off if we have a soft shutdown */
+ advwdt_disable();
+}
+
+static struct platform_driver advwdt_driver = {
+ .probe = advwdt_probe,
+ .remove = __devexit_p(advwdt_remove),
+ .shutdown = advwdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+static int __init advwdt_init(void)
+{
+ int err;
+
+ printk(KERN_INFO
+ "WDT driver for Advantech single board computer initialising.\n");
+
+ err = platform_driver_register(&advwdt_driver);
+ if (err)
+ return err;
+
+ advwdt_platform_device = platform_device_register_simple(DRV_NAME,
+ -1, NULL, 0);
+ if (IS_ERR(advwdt_platform_device)) {
+ err = PTR_ERR(advwdt_platform_device);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&advwdt_driver);
+ return err;
+}
+
+static void __exit advwdt_exit(void)
+{
+ platform_device_unregister(advwdt_platform_device);
+ platform_driver_unregister(&advwdt_driver);
+ printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+}
+
+module_init(advwdt_init);
+module_exit(advwdt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
+MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/adx_wdt.c b/drivers/watchdog/adx_wdt.c
new file mode 100644
index 00000000..af6e6b16
--- /dev/null
+++ b/drivers/watchdog/adx_wdt.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2008-2009 Avionic Design GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/fs.h>
+#include <linux/gfp.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define WATCHDOG_NAME "adx-wdt"
+
+/* register offsets */
+#define ADX_WDT_CONTROL 0x00
+#define ADX_WDT_CONTROL_ENABLE (1 << 0)
+#define ADX_WDT_CONTROL_nRESET (1 << 1)
+#define ADX_WDT_TIMEOUT 0x08
+
+static struct platform_device *adx_wdt_dev;
+static unsigned long driver_open;
+
+#define WDT_STATE_STOP 0
+#define WDT_STATE_START 1
+
+struct adx_wdt {
+ void __iomem *base;
+ unsigned long timeout;
+ unsigned int state;
+ unsigned int wake;
+ spinlock_t lock;
+};
+
+static const struct watchdog_info adx_wdt_info = {
+ .identity = "Avionic Design Xanthos Watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+static void adx_wdt_start_locked(struct adx_wdt *wdt)
+{
+ u32 ctrl;
+
+ ctrl = readl(wdt->base + ADX_WDT_CONTROL);
+ ctrl |= ADX_WDT_CONTROL_ENABLE;
+ writel(ctrl, wdt->base + ADX_WDT_CONTROL);
+ wdt->state = WDT_STATE_START;
+}
+
+static void adx_wdt_start(struct adx_wdt *wdt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt->lock, flags);
+ adx_wdt_start_locked(wdt);
+ spin_unlock_irqrestore(&wdt->lock, flags);
+}
+
+static void adx_wdt_stop_locked(struct adx_wdt *wdt)
+{
+ u32 ctrl;
+
+ ctrl = readl(wdt->base + ADX_WDT_CONTROL);
+ ctrl &= ~ADX_WDT_CONTROL_ENABLE;
+ writel(ctrl, wdt->base + ADX_WDT_CONTROL);
+ wdt->state = WDT_STATE_STOP;
+}
+
+static void adx_wdt_stop(struct adx_wdt *wdt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt->lock, flags);
+ adx_wdt_stop_locked(wdt);
+ spin_unlock_irqrestore(&wdt->lock, flags);
+}
+
+static void adx_wdt_set_timeout(struct adx_wdt *wdt, unsigned long seconds)
+{
+ unsigned long timeout = seconds * 1000;
+ unsigned long flags;
+ unsigned int state;
+
+ spin_lock_irqsave(&wdt->lock, flags);
+ state = wdt->state;
+ adx_wdt_stop_locked(wdt);
+ writel(timeout, wdt->base + ADX_WDT_TIMEOUT);
+
+ if (state == WDT_STATE_START)
+ adx_wdt_start_locked(wdt);
+
+ wdt->timeout = timeout;
+ spin_unlock_irqrestore(&wdt->lock, flags);
+}
+
+static void adx_wdt_get_timeout(struct adx_wdt *wdt, unsigned long *seconds)
+{
+ *seconds = wdt->timeout / 1000;
+}
+
+static void adx_wdt_keepalive(struct adx_wdt *wdt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt->lock, flags);
+ writel(wdt->timeout, wdt->base + ADX_WDT_TIMEOUT);
+ spin_unlock_irqrestore(&wdt->lock, flags);
+}
+
+static int adx_wdt_open(struct inode *inode, struct file *file)
+{
+ struct adx_wdt *wdt = platform_get_drvdata(adx_wdt_dev);
+
+ if (test_and_set_bit(0, &driver_open))
+ return -EBUSY;
+
+ file->private_data = wdt;
+ adx_wdt_set_timeout(wdt, 30);
+ adx_wdt_start(wdt);
+
+ return nonseekable_open(inode, file);
+}
+
+static int adx_wdt_release(struct inode *inode, struct file *file)
+{
+ struct adx_wdt *wdt = file->private_data;
+
+ adx_wdt_stop(wdt);
+ clear_bit(0, &driver_open);
+
+ return 0;
+}
+
+static long adx_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct adx_wdt *wdt = file->private_data;
+ void __user *argp = (void __user *)arg;
+ unsigned long __user *p = argp;
+ unsigned long seconds = 0;
+ unsigned int options;
+ long ret = -EINVAL;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &adx_wdt_info, sizeof(adx_wdt_info)))
+ return -EFAULT;
+ else
+ return 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_KEEPALIVE:
+ adx_wdt_keepalive(wdt);
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(seconds, p))
+ return -EFAULT;
+
+ adx_wdt_set_timeout(wdt, seconds);
+
+ /* fallthrough */
+ case WDIOC_GETTIMEOUT:
+ adx_wdt_get_timeout(wdt, &seconds);
+ return put_user(seconds, p);
+
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&options, argp, sizeof(options)))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ adx_wdt_stop(wdt);
+ ret = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ adx_wdt_start(wdt);
+ ret = 0;
+ }
+
+ return ret;
+
+ default:
+ break;
+ }
+
+ return -ENOTTY;
+}
+
+static ssize_t adx_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ struct adx_wdt *wdt = file->private_data;
+
+ if (len)
+ adx_wdt_keepalive(wdt);
+
+ return len;
+}
+
+static const struct file_operations adx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = adx_wdt_open,
+ .release = adx_wdt_release,
+ .unlocked_ioctl = adx_wdt_ioctl,
+ .write = adx_wdt_write,
+};
+
+static struct miscdevice adx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &adx_wdt_fops,
+};
+
+static int __devinit adx_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct adx_wdt *wdt;
+ int ret = 0;
+ u32 ctrl;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt) {
+ dev_err(&pdev->dev, "cannot allocate WDT structure\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&wdt->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot obtain I/O memory region\n");
+ return -ENXIO;
+ }
+
+ res = devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), res->name);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot request I/O memory region\n");
+ return -ENXIO;
+ }
+
+ wdt->base = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
+ if (!wdt->base) {
+ dev_err(&pdev->dev, "cannot remap I/O memory region\n");
+ return -ENXIO;
+ }
+
+ /* disable watchdog and reboot on timeout */
+ ctrl = readl(wdt->base + ADX_WDT_CONTROL);
+ ctrl &= ~ADX_WDT_CONTROL_ENABLE;
+ ctrl &= ~ADX_WDT_CONTROL_nRESET;
+ writel(ctrl, wdt->base + ADX_WDT_CONTROL);
+
+ platform_set_drvdata(pdev, wdt);
+ adx_wdt_dev = pdev;
+
+ ret = misc_register(&adx_wdt_miscdev);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot register miscdev on minor %d "
+ "(err=%d)\n", WATCHDOG_MINOR, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit adx_wdt_remove(struct platform_device *pdev)
+{
+ struct adx_wdt *wdt = platform_get_drvdata(pdev);
+
+ misc_deregister(&adx_wdt_miscdev);
+ adx_wdt_stop(wdt);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static void adx_wdt_shutdown(struct platform_device *pdev)
+{
+ struct adx_wdt *wdt = platform_get_drvdata(pdev);
+ adx_wdt_stop(wdt);
+}
+
+#ifdef CONFIG_PM
+static int adx_wdt_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct adx_wdt *wdt = platform_get_drvdata(pdev);
+
+ wdt->wake = (wdt->state == WDT_STATE_START) ? 1 : 0;
+ adx_wdt_stop(wdt);
+
+ return 0;
+}
+
+static int adx_wdt_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct adx_wdt *wdt = platform_get_drvdata(pdev);
+
+ if (wdt->wake)
+ adx_wdt_start(wdt);
+
+ return 0;
+}
+
+static const struct dev_pm_ops adx_wdt_pm_ops = {
+ .suspend = adx_wdt_suspend,
+ .resume = adx_wdt_resume,
+};
+
+# define ADX_WDT_PM_OPS (&adx_wdt_pm_ops)
+#else
+# define ADX_WDT_PM_OPS NULL
+#endif
+
+static struct platform_driver adx_wdt_driver = {
+ .probe = adx_wdt_probe,
+ .remove = __devexit_p(adx_wdt_remove),
+ .shutdown = adx_wdt_shutdown,
+ .driver = {
+ .name = WATCHDOG_NAME,
+ .owner = THIS_MODULE,
+ .pm = ADX_WDT_PM_OPS,
+ },
+};
+
+static int __init adx_wdt_init(void)
+{
+ return platform_driver_register(&adx_wdt_driver);
+}
+
+static void __exit adx_wdt_exit(void)
+{
+ platform_driver_unregister(&adx_wdt_driver);
+}
+
+module_init(adx_wdt_init);
+module_exit(adx_wdt_exit);
+
+MODULE_DESCRIPTION("Avionic Design Xanthos Watchdog Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c
new file mode 100644
index 00000000..f16dcbd4
--- /dev/null
+++ b/drivers/watchdog/alim1535_wdt.c
@@ -0,0 +1,458 @@
+/*
+ * Watchdog for the 7101 PMU version found in the ALi M1535 chipsets
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#define WATCHDOG_NAME "ALi_M1535"
+#define PFX WATCHDOG_NAME ": "
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+
+/* internal variables */
+static unsigned long ali_is_open;
+static char ali_expect_release;
+static struct pci_dev *ali_pci;
+static u32 ali_timeout_bits; /* stores the computed timeout */
+static DEFINE_SPINLOCK(ali_lock); /* Guards the hardware */
+
+/* module parameters */
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (0 < timeout < 18000, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * ali_start - start watchdog countdown
+ *
+ * Starts the timer running providing the timer has a counter
+ * configuration set.
+ */
+
+static void ali_start(void)
+{
+ u32 val;
+
+ spin_lock(&ali_lock);
+
+ pci_read_config_dword(ali_pci, 0xCC, &val);
+ val &= ~0x3F; /* Mask count */
+ val |= (1 << 25) | ali_timeout_bits;
+ pci_write_config_dword(ali_pci, 0xCC, val);
+
+ spin_unlock(&ali_lock);
+}
+
+/*
+ * ali_stop - stop the timer countdown
+ *
+ * Stop the ALi watchdog countdown
+ */
+
+static void ali_stop(void)
+{
+ u32 val;
+
+ spin_lock(&ali_lock);
+
+ pci_read_config_dword(ali_pci, 0xCC, &val);
+ val &= ~0x3F; /* Mask count to zero (disabled) */
+ val &= ~(1 << 25); /* and for safety mask the reset enable */
+ pci_write_config_dword(ali_pci, 0xCC, val);
+
+ spin_unlock(&ali_lock);
+}
+
+/*
+ * ali_keepalive - send a keepalive to the watchdog
+ *
+ * Send a keepalive to the timer (actually we restart the timer).
+ */
+
+static void ali_keepalive(void)
+{
+ ali_start();
+}
+
+/*
+ * ali_settimer - compute the timer reload value
+ * @t: time in seconds
+ *
+ * Computes the timeout values needed
+ */
+
+static int ali_settimer(int t)
+{
+ if (t < 0)
+ return -EINVAL;
+ else if (t < 60)
+ ali_timeout_bits = t|(1 << 6);
+ else if (t < 3600)
+ ali_timeout_bits = (t / 60)|(1 << 7);
+ else if (t < 18000)
+ ali_timeout_bits = (t / 300)|(1 << 6)|(1 << 7);
+ else
+ return -EINVAL;
+
+ timeout = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+/*
+ * ali_write - writes to ALi watchdog
+ * @file: file from VFS
+ * @data: user address of data
+ * @len: length of data
+ * @ppos: pointer to the file offset
+ *
+ * Handle a write to the ALi watchdog. Writing to the file pings
+ * the watchdog and resets it. Writing the magic 'V' sequence allows
+ * the next close to turn off the watchdog.
+ */
+
+static ssize_t ali_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* note: just in case someone wrote the
+ magic character five months ago... */
+ ali_expect_release = 0;
+
+ /* scan to see whether or not we got
+ the magic character */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ ali_expect_release = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ ali_start();
+ }
+ return len;
+}
+
+/*
+ * ali_ioctl - handle watchdog ioctls
+ * @file: VFS file pointer
+ * @cmd: ioctl number
+ * @arg: arguments to the ioctl
+ *
+ * Handle the watchdog ioctls supported by the ALi driver. Really
+ * we want an extension to enable irq ack monitoring and the like
+ */
+
+static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = "ALi M1535 WatchDog Timer",
+ };
+
+ 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:
+ {
+ int new_options, retval = -EINVAL;
+
+ if (get_user(new_options, p))
+ return -EFAULT;
+ if (new_options & WDIOS_DISABLECARD) {
+ ali_stop();
+ retval = 0;
+ }
+ if (new_options & WDIOS_ENABLECARD) {
+ ali_start();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ ali_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ {
+ int new_timeout;
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (ali_settimer(new_timeout))
+ return -EINVAL;
+ ali_keepalive();
+ /* Fall */
+ }
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/*
+ * ali_open - handle open of ali watchdog
+ * @inode: inode from VFS
+ * @file: file from VFS
+ *
+ * Open the ALi watchdog device. Ensure only one person opens it
+ * at a time. Also start the watchdog running.
+ */
+
+static int ali_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &ali_is_open))
+ return -EBUSY;
+
+ /* Activate */
+ ali_start();
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * ali_release - close an ALi watchdog
+ * @inode: inode from VFS
+ * @file: file from VFS
+ *
+ * Close the ALi watchdog device. Actual shutdown of the timer
+ * only occurs if the magic sequence has been set.
+ */
+
+static int ali_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ */
+ if (ali_expect_release == 42)
+ ali_stop();
+ else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ ali_keepalive();
+ }
+ clear_bit(0, &ali_is_open);
+ ali_expect_release = 0;
+ return 0;
+}
+
+/*
+ * ali_notify_sys - System down notifier
+ *
+ * Notifier for system down
+ */
+
+
+static int ali_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ ali_stop(); /* Turn the WDT off */
+ return NOTIFY_DONE;
+}
+
+/*
+ * 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 DEFINE_PCI_DEVICE_TABLE(ali_pci_tbl) __used = {
+ { PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,},
+ { PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,},
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, ali_pci_tbl);
+
+/*
+ * ali_find_watchdog - find a 1535 and 7101
+ *
+ * Scans the PCI hardware for a 1535 series bridge and matching 7101
+ * watchdog device. This may be overtight but it is better to be safe
+ */
+
+static int __init ali_find_watchdog(void)
+{
+ struct pci_dev *pdev;
+ u32 wdog;
+
+ /* Check for a 1533/1535 series bridge */
+ pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1535, NULL);
+ if (pdev == NULL)
+ pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1533, NULL);
+ if (pdev == NULL)
+ return -ENODEV;
+ pci_dev_put(pdev);
+
+ /* Check for the a 7101 PMU */
+ pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x7101, NULL);
+ if (pdev == NULL)
+ return -ENODEV;
+
+ if (pci_enable_device(pdev)) {
+ pci_dev_put(pdev);
+ return -EIO;
+ }
+
+ ali_pci = pdev;
+
+ /*
+ * Initialize the timer bits
+ */
+ pci_read_config_dword(pdev, 0xCC, &wdog);
+
+ /* Timer bits */
+ wdog &= ~0x3F;
+ /* Issued events */
+ wdog &= ~((1 << 27)|(1 << 26)|(1 << 25)|(1 << 24));
+ /* No monitor bits */
+ wdog &= ~((1 << 16)|(1 << 13)|(1 << 12)|(1 << 11)|(1 << 10)|(1 << 9));
+
+ pci_write_config_dword(pdev, 0xCC, wdog);
+
+ return 0;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations ali_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ali_write,
+ .unlocked_ioctl = ali_ioctl,
+ .open = ali_open,
+ .release = ali_release,
+};
+
+static struct miscdevice ali_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ali_fops,
+};
+
+static struct notifier_block ali_notifier = {
+ .notifier_call = ali_notify_sys,
+};
+
+/*
+ * watchdog_init - module initialiser
+ *
+ * Scan for a suitable watchdog and if so initialize it. Return an error
+ * if we cannot, the error causes the module to unload
+ */
+
+static int __init watchdog_init(void)
+{
+ int ret;
+
+ /* Check whether or not the hardware watchdog is there */
+ if (ali_find_watchdog() != 0)
+ return -ENODEV;
+
+ /* Check that the timeout value is within it's range;
+ if not reset to the default */
+ if (timeout < 1 || timeout >= 18000) {
+ timeout = WATCHDOG_TIMEOUT;
+ printk(KERN_INFO PFX
+ "timeout value must be 0 < timeout < 18000, using %d\n",
+ timeout);
+ }
+
+ /* Calculate the watchdog's timeout */
+ ali_settimer(timeout);
+
+ ret = register_reboot_notifier(&ali_notifier);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ goto out;
+ }
+
+ ret = misc_register(&ali_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto unreg_reboot;
+ }
+
+ printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+out:
+ return ret;
+unreg_reboot:
+ unregister_reboot_notifier(&ali_notifier);
+ goto out;
+}
+
+/*
+ * watchdog_exit - module de-initialiser
+ *
+ * Called while unloading a successfully installed watchdog module.
+ */
+
+static void __exit watchdog_exit(void)
+{
+ /* Stop the timer before we leave */
+ ali_stop();
+
+ /* Deregister */
+ misc_deregister(&ali_miscdev);
+ unregister_reboot_notifier(&ali_notifier);
+ pci_dev_put(ali_pci);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("ALi M1535 PMU Watchdog Timer driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c
new file mode 100644
index 00000000..46f4b85b
--- /dev/null
+++ b/drivers/watchdog/alim7101_wdt.c
@@ -0,0 +1,444 @@
+/*
+ * ALi M7101 PMU Computer Watchdog Timer driver
+ *
+ * Based on w83877f_wdt.c by Scott Jennings <linuxdrivers@oro.net>
+ * and the Cobalt kernel WDT timer driver by Tim Hockin
+ * <thockin@cobaltnet.com>
+ *
+ * (c)2002 Steve Hill <steve@navaho.co.uk>
+ *
+ * This WDT driver is different from most other Linux WDT
+ * drivers in that the driver will ping the watchdog by itself,
+ * because this particular WDT has a very short timeout (1.6
+ * seconds) and it would be insane to count on any userspace
+ * daemon always getting scheduled within that time frame.
+ *
+ * Additions:
+ * Aug 23, 2004 - Added use_gpio module parameter for use on revision a1d PMUs
+ * found on very old cobalt hardware.
+ * -- Mike Waychison <michael.waychison@sun.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define OUR_NAME "alim7101_wdt"
+#define PFX OUR_NAME ": "
+
+#define WDT_ENABLE 0x9C
+#define WDT_DISABLE 0x8C
+
+#define ALI_7101_WDT 0x92
+#define ALI_7101_GPIO 0x7D
+#define ALI_7101_GPIO_O 0x7E
+#define ALI_WDT_ARM 0x01
+
+/*
+ * We're going to use a 1 second timeout.
+ * If we reset the watchdog every ~250ms we should be safe. */
+
+#define WDT_INTERVAL (HZ/4+1)
+
+/*
+ * We must not require too good response from the userspace daemon.
+ * Here we require the userspace daemon to send us a heartbeat
+ * char to /dev/watchdog every 30 seconds.
+ */
+
+#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */
+/* in seconds, will be multiplied by HZ to get seconds to wait for a ping */
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (1<=timeout<=3600, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int use_gpio; /* Use the pic (for a1d revision alim7101) */
+module_param(use_gpio, int, 0);
+MODULE_PARM_DESC(use_gpio,
+ "Use the gpio watchdog (required by old cobalt boards).");
+
+static void wdt_timer_ping(unsigned long);
+static DEFINE_TIMER(timer, wdt_timer_ping, 0, 1);
+static unsigned long next_heartbeat;
+static unsigned long wdt_is_open;
+static char wdt_expect_close;
+static struct pci_dev *alim7101_pmu;
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Whack the dog
+ */
+
+static void wdt_timer_ping(unsigned long data)
+{
+ /* If we got a heartbeat pulse within the WDT_US_INTERVAL
+ * we agree to ping the WDT
+ */
+ char tmp;
+
+ if (time_before(jiffies, next_heartbeat)) {
+ /* Ping the WDT (this is actually a disarm/arm sequence) */
+ pci_read_config_byte(alim7101_pmu, 0x92, &tmp);
+ pci_write_config_byte(alim7101_pmu,
+ ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
+ pci_write_config_byte(alim7101_pmu,
+ ALI_7101_WDT, (tmp | ALI_WDT_ARM));
+ if (use_gpio) {
+ pci_read_config_byte(alim7101_pmu,
+ ALI_7101_GPIO_O, &tmp);
+ pci_write_config_byte(alim7101_pmu,
+ ALI_7101_GPIO_O, tmp | 0x20);
+ pci_write_config_byte(alim7101_pmu,
+ ALI_7101_GPIO_O, tmp & ~0x20);
+ }
+ } else {
+ printk(KERN_WARNING PFX
+ "Heartbeat lost! Will not ping the watchdog\n");
+ }
+ /* Re-set the timer interval */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+}
+
+/*
+ * Utility routines
+ */
+
+static void wdt_change(int writeval)
+{
+ char tmp;
+
+ pci_read_config_byte(alim7101_pmu, ALI_7101_WDT, &tmp);
+ if (writeval == WDT_ENABLE) {
+ pci_write_config_byte(alim7101_pmu,
+ ALI_7101_WDT, (tmp | ALI_WDT_ARM));
+ if (use_gpio) {
+ pci_read_config_byte(alim7101_pmu,
+ ALI_7101_GPIO_O, &tmp);
+ pci_write_config_byte(alim7101_pmu,
+ ALI_7101_GPIO_O, tmp & ~0x20);
+ }
+
+ } else {
+ pci_write_config_byte(alim7101_pmu,
+ ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
+ if (use_gpio) {
+ pci_read_config_byte(alim7101_pmu,
+ ALI_7101_GPIO_O, &tmp);
+ pci_write_config_byte(alim7101_pmu,
+ ALI_7101_GPIO_O, tmp | 0x20);
+ }
+ }
+}
+
+static void wdt_startup(void)
+{
+ next_heartbeat = jiffies + (timeout * HZ);
+
+ /* We must enable before we kick off the timer in case the timer
+ occurs as we ping it */
+
+ wdt_change(WDT_ENABLE);
+
+ /* Start the timer */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+
+ printk(KERN_INFO PFX "Watchdog timer is now enabled.\n");
+}
+
+static void wdt_turnoff(void)
+{
+ /* Stop the timer */
+ del_timer_sync(&timer);
+ wdt_change(WDT_DISABLE);
+ printk(KERN_INFO PFX "Watchdog timer is now disabled...\n");
+}
+
+static void wdt_keepalive(void)
+{
+ /* user land ping */
+ next_heartbeat = jiffies + (timeout * HZ);
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t fop_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (count) {
+ if (!nowayout) {
+ size_t ofs;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ wdt_expect_close = 0;
+
+ /* now scan */
+ for (ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if (c == 'V')
+ wdt_expect_close = 42;
+ }
+ }
+ /* someone wrote to us, we should restart timer */
+ wdt_keepalive();
+ }
+ return count;
+}
+
+static int fop_open(struct inode *inode, struct file *file)
+{
+ /* Just in case we're already talking to someone... */
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ /* Good, fire up the show */
+ wdt_startup();
+ return nonseekable_open(inode, file);
+}
+
+static int fop_close(struct inode *inode, struct file *file)
+{
+ if (wdt_expect_close == 42)
+ wdt_turnoff();
+ else {
+ /* wim: shouldn't there be a: del_timer(&timer); */
+ printk(KERN_CRIT PFX
+ "device file closed unexpectedly. Will not stop the WDT!\n");
+ }
+ clear_bit(0, &wdt_is_open);
+ wdt_expect_close = 0;
+ return 0;
+}
+
+static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
+ | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "ALiM7101",
+ };
+
+ 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:
+ {
+ int new_options, retval = -EINVAL;
+
+ if (get_user(new_options, p))
+ return -EFAULT;
+ if (new_options & WDIOS_DISABLECARD) {
+ wdt_turnoff();
+ retval = 0;
+ }
+ if (new_options & WDIOS_ENABLECARD) {
+ wdt_startup();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ {
+ int new_timeout;
+
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ /* arbitrary upper limit */
+ if (new_timeout < 1 || new_timeout > 3600)
+ return -EINVAL;
+ timeout = new_timeout;
+ wdt_keepalive();
+ /* Fall through */
+ }
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = fop_write,
+ .open = fop_open,
+ .release = fop_close,
+ .unlocked_ioctl = fop_ioctl,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_turnoff();
+
+ if (code == SYS_RESTART) {
+ /*
+ * Cobalt devices have no way of rebooting themselves other
+ * than getting the watchdog to pull reset, so we restart the
+ * watchdog on reboot with no heartbeat
+ */
+ wdt_change(WDT_ENABLE);
+ printk(KERN_INFO PFX "Watchdog timer is now enabled "
+ "with no heartbeat - should reboot in ~1 second.\n");
+ }
+ return NOTIFY_DONE;
+}
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static void __exit alim7101_wdt_unload(void)
+{
+ wdt_turnoff();
+ /* Deregister */
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ pci_dev_put(alim7101_pmu);
+}
+
+static int __init alim7101_wdt_init(void)
+{
+ int rc = -EBUSY;
+ struct pci_dev *ali1543_south;
+ char tmp;
+
+ printk(KERN_INFO PFX "Steve Hill <steve@navaho.co.uk>.\n");
+ alim7101_pmu = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,
+ NULL);
+ if (!alim7101_pmu) {
+ printk(KERN_INFO PFX
+ "ALi M7101 PMU not present - WDT not set\n");
+ return -EBUSY;
+ }
+
+ /* Set the WDT in the PMU to 1 second */
+ pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02);
+
+ ali1543_south = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533,
+ NULL);
+ if (!ali1543_south) {
+ printk(KERN_INFO PFX
+ "ALi 1543 South-Bridge not present - WDT not set\n");
+ goto err_out;
+ }
+ pci_read_config_byte(ali1543_south, 0x5e, &tmp);
+ pci_dev_put(ali1543_south);
+ if ((tmp & 0x1e) == 0x00) {
+ if (!use_gpio) {
+ printk(KERN_INFO PFX
+ "Detected old alim7101 revision 'a1d'. "
+ "If this is a cobalt board, set the 'use_gpio' "
+ "module parameter.\n");
+ goto err_out;
+ }
+ nowayout = 1;
+ } else if ((tmp & 0x1e) != 0x12 && (tmp & 0x1e) != 0x00) {
+ printk(KERN_INFO PFX
+ "ALi 1543 South-Bridge does not have the correct "
+ "revision number (???1001?) - WDT not set\n");
+ goto err_out;
+ }
+
+ if (timeout < 1 || timeout > 3600) {
+ /* arbitrary upper limit */
+ timeout = WATCHDOG_TIMEOUT;
+ printk(KERN_INFO PFX
+ "timeout value must be 1 <= x <= 3600, using %d\n",
+ timeout);
+ }
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out;
+ }
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
+ goto err_out_reboot;
+ }
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ printk(KERN_INFO PFX "WDT driver for ALi M7101 initialised. "
+ "timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+ return 0;
+
+err_out_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+err_out:
+ pci_dev_put(alim7101_pmu);
+ return rc;
+}
+
+module_init(alim7101_wdt_init);
+module_exit(alim7101_wdt_unload);
+
+static DEFINE_PCI_DEVICE_TABLE(alim7101_pci_tbl) __used = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, alim7101_pci_tbl);
+
+MODULE_AUTHOR("Steve Hill");
+MODULE_DESCRIPTION("ALi M7101 PMU Computer Watchdog Timer driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
new file mode 100644
index 00000000..b2922178
--- /dev/null
+++ b/drivers/watchdog/ar7_wdt.c
@@ -0,0 +1,369 @@
+/*
+ * drivers/watchdog/ar7_wdt.c
+ *
+ * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org>
+ * Copyright (c) 2005 Enrik Berkhan <Enrik.Berkhan@akk.org>
+ *
+ * Some code taken from:
+ * National Semiconductor SCx200 Watchdog support
+ * Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+
+#include <asm/addrspace.h>
+#include <asm/mach-ar7/ar7.h>
+
+#define DRVNAME "ar7_wdt"
+#define LONGNAME "TI AR7 Watchdog Timer"
+
+MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
+MODULE_DESCRIPTION(LONGNAME);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+static int margin = 60;
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+#define READ_REG(x) readl((void __iomem *)&(x))
+#define WRITE_REG(x, v) writel((v), (void __iomem *)&(x))
+
+struct ar7_wdt {
+ u32 kick_lock;
+ u32 kick;
+ u32 change_lock;
+ u32 change;
+ u32 disable_lock;
+ u32 disable;
+ u32 prescale_lock;
+ u32 prescale;
+};
+
+static unsigned long wdt_is_open;
+static spinlock_t wdt_lock;
+static unsigned expect_close;
+
+/* XXX currently fixed, allows max margin ~68.72 secs */
+#define prescale_value 0xffff
+
+/* Resource of the WDT registers */
+static struct resource *ar7_regs_wdt;
+/* Pointer to the remapped WDT IO space */
+static struct ar7_wdt *ar7_wdt;
+
+static struct clk *vbus_clk;
+
+static void ar7_wdt_kick(u32 value)
+{
+ WRITE_REG(ar7_wdt->kick_lock, 0x5555);
+ if ((READ_REG(ar7_wdt->kick_lock) & 3) == 1) {
+ WRITE_REG(ar7_wdt->kick_lock, 0xaaaa);
+ if ((READ_REG(ar7_wdt->kick_lock) & 3) == 3) {
+ WRITE_REG(ar7_wdt->kick, value);
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT kick reg\n");
+}
+
+static void ar7_wdt_prescale(u32 value)
+{
+ WRITE_REG(ar7_wdt->prescale_lock, 0x5a5a);
+ if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 1) {
+ WRITE_REG(ar7_wdt->prescale_lock, 0xa5a5);
+ if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 3) {
+ WRITE_REG(ar7_wdt->prescale, value);
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT prescale reg\n");
+}
+
+static void ar7_wdt_change(u32 value)
+{
+ WRITE_REG(ar7_wdt->change_lock, 0x6666);
+ if ((READ_REG(ar7_wdt->change_lock) & 3) == 1) {
+ WRITE_REG(ar7_wdt->change_lock, 0xbbbb);
+ if ((READ_REG(ar7_wdt->change_lock) & 3) == 3) {
+ WRITE_REG(ar7_wdt->change, value);
+ return;
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT change reg\n");
+}
+
+static void ar7_wdt_disable(u32 value)
+{
+ WRITE_REG(ar7_wdt->disable_lock, 0x7777);
+ if ((READ_REG(ar7_wdt->disable_lock) & 3) == 1) {
+ WRITE_REG(ar7_wdt->disable_lock, 0xcccc);
+ if ((READ_REG(ar7_wdt->disable_lock) & 3) == 2) {
+ WRITE_REG(ar7_wdt->disable_lock, 0xdddd);
+ if ((READ_REG(ar7_wdt->disable_lock) & 3) == 3) {
+ WRITE_REG(ar7_wdt->disable, value);
+ return;
+ }
+ }
+ }
+ printk(KERN_ERR DRVNAME ": failed to unlock WDT disable reg\n");
+}
+
+static void ar7_wdt_update_margin(int new_margin)
+{
+ u32 change;
+ u32 vbus_rate;
+
+ vbus_rate = clk_get_rate(vbus_clk);
+ change = new_margin * (vbus_rate / prescale_value);
+ if (change < 1)
+ change = 1;
+ if (change > 0xffff)
+ change = 0xffff;
+ ar7_wdt_change(change);
+ margin = change * prescale_value / vbus_rate;
+ printk(KERN_INFO DRVNAME
+ ": timer margin %d seconds (prescale %d, change %d, freq %d)\n",
+ margin, prescale_value, change, vbus_rate);
+}
+
+static void ar7_wdt_enable_wdt(void)
+{
+ printk(KERN_DEBUG DRVNAME ": enabling watchdog timer\n");
+ ar7_wdt_disable(1);
+ ar7_wdt_kick(1);
+}
+
+static void ar7_wdt_disable_wdt(void)
+{
+ printk(KERN_DEBUG DRVNAME ": disabling watchdog timer\n");
+ ar7_wdt_disable(0);
+}
+
+static int ar7_wdt_open(struct inode *inode, struct file *file)
+{
+ /* only allow one at a time */
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ ar7_wdt_enable_wdt();
+ expect_close = 0;
+
+ return nonseekable_open(inode, file);
+}
+
+static int ar7_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!expect_close)
+ printk(KERN_WARNING DRVNAME
+ ": watchdog device closed unexpectedly,"
+ "will not disable the watchdog timer\n");
+ else if (!nowayout)
+ ar7_wdt_disable_wdt();
+ clear_bit(0, &wdt_is_open);
+ return 0;
+}
+
+static ssize_t ar7_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ /* check for a magic close character */
+ if (len) {
+ size_t i;
+
+ spin_lock(&wdt_lock);
+ ar7_wdt_kick(1);
+ spin_unlock(&wdt_lock);
+
+ expect_close = 0;
+ for (i = 0; i < len; ++i) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 1;
+ }
+
+ }
+ return len;
+}
+
+static long ar7_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ static const struct watchdog_info ident = {
+ .identity = LONGNAME,
+ .firmware_version = 1,
+ .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE),
+ };
+ int new_margin;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)))
+ return -EFAULT;
+ return 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int *)arg))
+ return -EFAULT;
+ return 0;
+ case WDIOC_KEEPALIVE:
+ ar7_wdt_kick(1);
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int *)arg))
+ return -EFAULT;
+ if (new_margin < 1)
+ return -EINVAL;
+
+ spin_lock(&wdt_lock);
+ ar7_wdt_update_margin(new_margin);
+ ar7_wdt_kick(1);
+ spin_unlock(&wdt_lock);
+
+ case WDIOC_GETTIMEOUT:
+ if (put_user(margin, (int *)arg))
+ return -EFAULT;
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations ar7_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = ar7_wdt_write,
+ .unlocked_ioctl = ar7_wdt_ioctl,
+ .open = ar7_wdt_open,
+ .release = ar7_wdt_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice ar7_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ar7_wdt_fops,
+};
+
+static int __devinit ar7_wdt_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ spin_lock_init(&wdt_lock);
+
+ ar7_regs_wdt =
+ platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ if (!ar7_regs_wdt) {
+ printk(KERN_ERR DRVNAME ": could not get registers resource\n");
+ rc = -ENODEV;
+ goto out;
+ }
+
+ if (!request_mem_region(ar7_regs_wdt->start,
+ resource_size(ar7_regs_wdt), LONGNAME)) {
+ printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
+ rc = -EBUSY;
+ goto out;
+ }
+
+ ar7_wdt = ioremap(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
+ if (!ar7_wdt) {
+ printk(KERN_ERR DRVNAME ": could not ioremap registers\n");
+ rc = -ENXIO;
+ goto out_mem_region;
+ }
+
+ vbus_clk = clk_get(NULL, "vbus");
+ if (IS_ERR(vbus_clk)) {
+ printk(KERN_ERR DRVNAME ": could not get vbus clock\n");
+ rc = PTR_ERR(vbus_clk);
+ goto out_mem_region;
+ }
+
+ ar7_wdt_disable_wdt();
+ ar7_wdt_prescale(prescale_value);
+ ar7_wdt_update_margin(margin);
+
+ rc = misc_register(&ar7_wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR DRVNAME ": unable to register misc device\n");
+ goto out_alloc;
+ }
+ goto out;
+
+out_alloc:
+ iounmap(ar7_wdt);
+out_mem_region:
+ release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
+out:
+ return rc;
+}
+
+static int __devexit ar7_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&ar7_wdt_miscdev);
+ iounmap(ar7_wdt);
+ release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
+
+ return 0;
+}
+
+static void ar7_wdt_shutdown(struct platform_device *pdev)
+{
+ if (!nowayout)
+ ar7_wdt_disable_wdt();
+}
+
+static struct platform_driver ar7_wdt_driver = {
+ .probe = ar7_wdt_probe,
+ .remove = __devexit_p(ar7_wdt_remove),
+ .shutdown = ar7_wdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ar7_wdt",
+ },
+};
+
+static int __init ar7_wdt_init(void)
+{
+ return platform_driver_register(&ar7_wdt_driver);
+}
+
+static void __exit ar7_wdt_cleanup(void)
+{
+ platform_driver_unregister(&ar7_wdt_driver);
+}
+
+module_init(ar7_wdt_init);
+module_exit(ar7_wdt_cleanup);
diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c
new file mode 100644
index 00000000..4ca5d403
--- /dev/null
+++ b/drivers/watchdog/at32ap700x_wdt.c
@@ -0,0 +1,454 @@
+/*
+ * Watchdog driver for Atmel AT32AP700X devices
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Errata: WDT Clear is blocked after WDT Reset
+ *
+ * A watchdog timer event will, after reset, block writes to the WDT_CLEAR
+ * register, preventing the program to clear the next Watchdog Timer Reset.
+ *
+ * If you still want to use the WDT after a WDT reset a small code can be
+ * insterted at the startup checking the AVR32_PM.rcause register for WDT reset
+ * and use a GPIO pin to reset the system. This method requires that one of the
+ * GPIO pins are available and connected externally to the RESET_N pin. After
+ * the GPIO pin has pulled down the reset line the GPIO will be reset and leave
+ * the pin tristated with pullup.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+#define TIMEOUT_MIN 1
+#define TIMEOUT_MAX 2
+#define TIMEOUT_DEFAULT TIMEOUT_MAX
+
+/* module parameters */
+static int timeout = TIMEOUT_DEFAULT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Timeout value. Limited to be 1 or 2 seconds. (default="
+ __MODULE_STRING(TIMEOUT_DEFAULT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/* Watchdog registers and write/read macro */
+#define WDT_CTRL 0x00
+#define WDT_CTRL_EN 0
+#define WDT_CTRL_PSEL 8
+#define WDT_CTRL_KEY 24
+
+#define WDT_CLR 0x04
+
+#define WDT_RCAUSE 0x10
+#define WDT_RCAUSE_POR 0
+#define WDT_RCAUSE_EXT 2
+#define WDT_RCAUSE_WDT 3
+#define WDT_RCAUSE_JTAG 4
+#define WDT_RCAUSE_SERP 5
+
+#define WDT_BIT(name) (1 << WDT_##name)
+#define WDT_BF(name, value) ((value) << WDT_##name)
+
+#define wdt_readl(dev, reg) \
+ __raw_readl((dev)->regs + WDT_##reg)
+#define wdt_writel(dev, reg, value) \
+ __raw_writel((value), (dev)->regs + WDT_##reg)
+
+struct wdt_at32ap700x {
+ void __iomem *regs;
+ spinlock_t io_lock;
+ int timeout;
+ int boot_status;
+ unsigned long users;
+ struct miscdevice miscdev;
+};
+
+static struct wdt_at32ap700x *wdt;
+static char expect_release;
+
+/*
+ * Disable the watchdog.
+ */
+static inline void at32_wdt_stop(void)
+{
+ unsigned long psel;
+
+ spin_lock(&wdt->io_lock);
+ psel = wdt_readl(wdt, CTRL) & WDT_BF(CTRL_PSEL, 0x0f);
+ wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0x55));
+ wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0xaa));
+ spin_unlock(&wdt->io_lock);
+}
+
+/*
+ * Enable and reset the watchdog.
+ */
+static inline void at32_wdt_start(void)
+{
+ /* 0xf is 2^16 divider = 2 sec, 0xe is 2^15 divider = 1 sec */
+ unsigned long psel = (wdt->timeout > 1) ? 0xf : 0xe;
+
+ spin_lock(&wdt->io_lock);
+ wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
+ | WDT_BF(CTRL_PSEL, psel)
+ | WDT_BF(CTRL_KEY, 0x55));
+ wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
+ | WDT_BF(CTRL_PSEL, psel)
+ | WDT_BF(CTRL_KEY, 0xaa));
+ spin_unlock(&wdt->io_lock);
+}
+
+/*
+ * Pat the watchdog timer.
+ */
+static inline void at32_wdt_pat(void)
+{
+ spin_lock(&wdt->io_lock);
+ wdt_writel(wdt, CLR, 0x42);
+ spin_unlock(&wdt->io_lock);
+}
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int at32_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(1, &wdt->users))
+ return -EBUSY;
+
+ at32_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * Close the watchdog device.
+ */
+static int at32_wdt_close(struct inode *inode, struct file *file)
+{
+ if (expect_release == 42) {
+ at32_wdt_stop();
+ } else {
+ dev_dbg(wdt->miscdev.parent,
+ "unexpected close, not stopping watchdog!\n");
+ at32_wdt_pat();
+ }
+ clear_bit(1, &wdt->users);
+ expect_release = 0;
+ return 0;
+}
+
+/*
+ * Change the watchdog time interval.
+ */
+static int at32_wdt_settimeout(int time)
+{
+ /*
+ * All counting occurs at 1 / SLOW_CLOCK (32 kHz) and max prescaler is
+ * 2 ^ 16 allowing up to 2 seconds timeout.
+ */
+ if ((time < TIMEOUT_MIN) || (time > TIMEOUT_MAX))
+ return -EINVAL;
+
+ /*
+ * Set new watchdog time. It will be used when at32_wdt_start() is
+ * called.
+ */
+ wdt->timeout = time;
+ return 0;
+}
+
+/*
+ * Get the watchdog status.
+ */
+static int at32_wdt_get_status(void)
+{
+ int rcause;
+ int status = 0;
+
+ rcause = wdt_readl(wdt, RCAUSE);
+
+ switch (rcause) {
+ case WDT_BIT(RCAUSE_EXT):
+ status = WDIOF_EXTERN1;
+ break;
+ case WDT_BIT(RCAUSE_WDT):
+ status = WDIOF_CARDRESET;
+ break;
+ case WDT_BIT(RCAUSE_POR): /* fall through */
+ case WDT_BIT(RCAUSE_JTAG): /* fall through */
+ case WDT_BIT(RCAUSE_SERP): /* fall through */
+ default:
+ break;
+ }
+
+ return status;
+}
+
+static const struct watchdog_info at32_wdt_info = {
+ .identity = "at32ap700x watchdog",
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+/*
+ * Handle commands from user-space.
+ */
+static long at32_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user(argp, &at32_wdt_info,
+ sizeof(at32_wdt_info)) ? -EFAULT : 0;
+ break;
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, p);
+ break;
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(wdt->boot_status, p);
+ break;
+ case WDIOC_SETOPTIONS:
+ ret = get_user(time, p);
+ if (ret)
+ break;
+ if (time & WDIOS_DISABLECARD)
+ at32_wdt_stop();
+ if (time & WDIOS_ENABLECARD)
+ at32_wdt_start();
+ ret = 0;
+ break;
+ case WDIOC_KEEPALIVE:
+ at32_wdt_pat();
+ ret = 0;
+ break;
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, p);
+ if (ret)
+ break;
+ ret = at32_wdt_settimeout(time);
+ if (ret)
+ break;
+ /* Enable new time value */
+ at32_wdt_start();
+ /* fall through */
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(wdt->timeout, p);
+ break;
+ }
+
+ return ret;
+}
+
+static ssize_t at32_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /*
+ * note: just in case someone wrote the magic
+ * character five months ago...
+ */
+ expect_release = 0;
+
+ /*
+ * scan to see whether or not we got the magic
+ * character
+ */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_release = 42;
+ }
+ }
+ /* someone wrote to us, we should pat the watchdog */
+ at32_wdt_pat();
+ }
+ return len;
+}
+
+static const struct file_operations at32_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = at32_wdt_ioctl,
+ .open = at32_wdt_open,
+ .release = at32_wdt_close,
+ .write = at32_wdt_write,
+};
+
+static int __init at32_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *regs;
+ int ret;
+
+ if (wdt) {
+ dev_dbg(&pdev->dev, "only 1 wdt instance supported.\n");
+ return -EBUSY;
+ }
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs) {
+ dev_dbg(&pdev->dev, "missing mmio resource\n");
+ return -ENXIO;
+ }
+
+ wdt = kzalloc(sizeof(struct wdt_at32ap700x), GFP_KERNEL);
+ if (!wdt) {
+ dev_dbg(&pdev->dev, "no memory for wdt structure\n");
+ return -ENOMEM;
+ }
+
+ wdt->regs = ioremap(regs->start, resource_size(regs));
+ if (!wdt->regs) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "could not map I/O memory\n");
+ goto err_free;
+ }
+
+ spin_lock_init(&wdt->io_lock);
+ wdt->boot_status = at32_wdt_get_status();
+
+ /* Work-around for watchdog silicon errata. */
+ if (wdt->boot_status & WDIOF_CARDRESET) {
+ dev_info(&pdev->dev, "CPU must be reset with external "
+ "reset or POR due to silicon errata.\n");
+ ret = -EIO;
+ goto err_iounmap;
+ } else {
+ wdt->users = 0;
+ }
+
+ wdt->miscdev.minor = WATCHDOG_MINOR;
+ wdt->miscdev.name = "watchdog";
+ wdt->miscdev.fops = &at32_wdt_fops;
+ wdt->miscdev.parent = &pdev->dev;
+
+ platform_set_drvdata(pdev, wdt);
+
+ if (at32_wdt_settimeout(timeout)) {
+ at32_wdt_settimeout(TIMEOUT_DEFAULT);
+ dev_dbg(&pdev->dev,
+ "default timeout invalid, set to %d sec.\n",
+ TIMEOUT_DEFAULT);
+ }
+
+ ret = misc_register(&wdt->miscdev);
+ if (ret) {
+ dev_dbg(&pdev->dev, "failed to register wdt miscdev\n");
+ goto err_register;
+ }
+
+ dev_info(&pdev->dev,
+ "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n",
+ wdt->regs, wdt->timeout, nowayout);
+
+ return 0;
+
+err_register:
+ platform_set_drvdata(pdev, NULL);
+err_iounmap:
+ iounmap(wdt->regs);
+err_free:
+ kfree(wdt);
+ wdt = NULL;
+ return ret;
+}
+
+static int __exit at32_wdt_remove(struct platform_device *pdev)
+{
+ if (wdt && platform_get_drvdata(pdev) == wdt) {
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ at32_wdt_stop();
+
+ misc_deregister(&wdt->miscdev);
+ iounmap(wdt->regs);
+ kfree(wdt);
+ wdt = NULL;
+ platform_set_drvdata(pdev, NULL);
+ }
+ return 0;
+}
+
+static void at32_wdt_shutdown(struct platform_device *pdev)
+{
+ at32_wdt_stop();
+}
+
+#ifdef CONFIG_PM
+static int at32_wdt_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ at32_wdt_stop();
+ return 0;
+}
+
+static int at32_wdt_resume(struct platform_device *pdev)
+{
+ if (wdt->users)
+ at32_wdt_start();
+ return 0;
+}
+#else
+#define at32_wdt_suspend NULL
+#define at32_wdt_resume NULL
+#endif
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:at32_wdt");
+
+static struct platform_driver at32_wdt_driver = {
+ .remove = __exit_p(at32_wdt_remove),
+ .suspend = at32_wdt_suspend,
+ .resume = at32_wdt_resume,
+ .driver = {
+ .name = "at32_wdt",
+ .owner = THIS_MODULE,
+ },
+ .shutdown = at32_wdt_shutdown,
+};
+
+static int __init at32_wdt_init(void)
+{
+ return platform_driver_probe(&at32_wdt_driver, at32_wdt_probe);
+}
+module_init(at32_wdt_init);
+
+static void __exit at32_wdt_exit(void)
+{
+ platform_driver_unregister(&at32_wdt_driver);
+}
+module_exit(at32_wdt_exit);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
+MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c
new file mode 100644
index 00000000..b3046dc4
--- /dev/null
+++ b/drivers/watchdog/at91rm9200_wdt.c
@@ -0,0 +1,290 @@
+/*
+ * Watchdog driver for Atmel AT91RM9200 (Thunder)
+ *
+ * Copyright (C) 2003 SAN People (Pty) Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <mach/at91_st.h>
+
+#define WDT_DEFAULT_TIME 5 /* seconds */
+#define WDT_MAX_TIME 256 /* seconds */
+
+static int wdt_time = WDT_DEFAULT_TIME;
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+module_param(wdt_time, int, 0);
+MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
+ __MODULE_STRING(WDT_DEFAULT_TIME) ")");
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+#endif
+
+
+static unsigned long at91wdt_busy;
+
+/* ......................................................................... */
+
+/*
+ * Disable the watchdog.
+ */
+static inline void at91_wdt_stop(void)
+{
+ at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN);
+}
+
+/*
+ * Enable and reset the watchdog.
+ */
+static inline void at91_wdt_start(void)
+{
+ at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN |
+ (((65536 * wdt_time) >> 8) & AT91_ST_WDV));
+ at91_sys_write(AT91_ST_CR, AT91_ST_WDRST);
+}
+
+/*
+ * Reload the watchdog timer. (ie, pat the watchdog)
+ */
+static inline void at91_wdt_reload(void)
+{
+ at91_sys_write(AT91_ST_CR, AT91_ST_WDRST);
+}
+
+/* ......................................................................... */
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int at91_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &at91wdt_busy))
+ return -EBUSY;
+
+ at91_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * Close the watchdog device.
+ * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
+ * disabled.
+ */
+static int at91_wdt_close(struct inode *inode, struct file *file)
+{
+ /* Disable the watchdog when file is closed */
+ if (!nowayout)
+ at91_wdt_stop();
+
+ clear_bit(0, &at91wdt_busy);
+ return 0;
+}
+
+/*
+ * Change the watchdog time interval.
+ */
+static int at91_wdt_settimeout(int new_time)
+{
+ /*
+ * All counting occurs at SLOW_CLOCK / 128 = 256 Hz
+ *
+ * Since WDV is a 16-bit counter, the maximum period is
+ * 65536 / 256 = 256 seconds.
+ */
+ if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
+ return -EINVAL;
+
+ /* Set new watchdog time. It will be used when
+ at91_wdt_start() is called. */
+ wdt_time = new_time;
+ return 0;
+}
+
+static const struct watchdog_info at91_wdt_info = {
+ .identity = "at91 watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+/*
+ * Handle commands from user-space.
+ */
+static long at91_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &at91_wdt_info,
+ sizeof(at91_wdt_info)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ if (new_value & WDIOS_DISABLECARD)
+ at91_wdt_stop();
+ if (new_value & WDIOS_ENABLECARD)
+ at91_wdt_start();
+ return 0;
+ case WDIOC_KEEPALIVE:
+ at91_wdt_reload(); /* pat the watchdog */
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ if (at91_wdt_settimeout(new_value))
+ return -EINVAL;
+ /* Enable new time value */
+ at91_wdt_start();
+ /* Return current value */
+ return put_user(wdt_time, p);
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdt_time, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t at91_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ at91_wdt_reload(); /* pat the watchdog */
+ return len;
+}
+
+/* ......................................................................... */
+
+static const struct file_operations at91wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = at91_wdt_ioctl,
+ .open = at91_wdt_open,
+ .release = at91_wdt_close,
+ .write = at91_wdt_write,
+};
+
+static struct miscdevice at91wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &at91wdt_fops,
+};
+
+static int __devinit at91wdt_probe(struct platform_device *pdev)
+{
+ int res;
+
+ if (at91wdt_miscdev.parent)
+ return -EBUSY;
+ at91wdt_miscdev.parent = &pdev->dev;
+
+ res = misc_register(&at91wdt_miscdev);
+ if (res)
+ return res;
+
+ printk(KERN_INFO "AT91 Watchdog Timer enabled (%d seconds%s)\n",
+ wdt_time, nowayout ? ", nowayout" : "");
+ return 0;
+}
+
+static int __devexit at91wdt_remove(struct platform_device *pdev)
+{
+ int res;
+
+ res = misc_deregister(&at91wdt_miscdev);
+ if (!res)
+ at91wdt_miscdev.parent = NULL;
+
+ return res;
+}
+
+static void at91wdt_shutdown(struct platform_device *pdev)
+{
+ at91_wdt_stop();
+}
+
+#ifdef CONFIG_PM
+
+static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ at91_wdt_stop();
+ return 0;
+}
+
+static int at91wdt_resume(struct platform_device *pdev)
+{
+ if (at91wdt_busy)
+ at91_wdt_start();
+ return 0;
+}
+
+#else
+#define at91wdt_suspend NULL
+#define at91wdt_resume NULL
+#endif
+
+static struct platform_driver at91wdt_driver = {
+ .probe = at91wdt_probe,
+ .remove = __devexit_p(at91wdt_remove),
+ .shutdown = at91wdt_shutdown,
+ .suspend = at91wdt_suspend,
+ .resume = at91wdt_resume,
+ .driver = {
+ .name = "at91_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init at91_wdt_init(void)
+{
+ /* Check that the heartbeat value is within range;
+ if not reset to the default */
+ if (at91_wdt_settimeout(wdt_time)) {
+ at91_wdt_settimeout(WDT_DEFAULT_TIME);
+ pr_info("at91_wdt: wdt_time value must be 1 <= wdt_time <= 256"
+ ", using %d\n", wdt_time);
+ }
+
+ return platform_driver_register(&at91wdt_driver);
+}
+
+static void __exit at91_wdt_exit(void)
+{
+ platform_driver_unregister(&at91wdt_driver);
+}
+
+module_init(at91_wdt_init);
+module_exit(at91_wdt_exit);
+
+MODULE_AUTHOR("Andrew Victor");
+MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:at91_wdt");
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
new file mode 100644
index 00000000..eac26021
--- /dev/null
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -0,0 +1,330 @@
+/*
+ * Watchdog driver for Atmel AT91SAM9x processors.
+ *
+ * Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * The Watchdog Timer Mode Register can be only written to once. If the
+ * timeout need to be set from Linux, be sure that the bootstrap or the
+ * bootloader doesn't write to this register.
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+
+#include <mach/at91_wdt.h>
+
+#define DRV_NAME "AT91SAM9 Watchdog"
+
+/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
+ * use this to convert a watchdog
+ * value from/to milliseconds.
+ */
+#define ms_to_ticks(t) (((t << 8) / 1000) - 1)
+#define ticks_to_ms(t) (((t + 1) * 1000) >> 8)
+
+/* Hardware timeout in seconds */
+#define WDT_HW_TIMEOUT 2
+
+/* Timer heartbeat (500ms) */
+#define WDT_TIMEOUT (HZ/2)
+
+/* User land timeout */
+#define WDT_HEARTBEAT 15
+static int heartbeat = WDT_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
+ "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static void at91_ping(unsigned long data);
+
+static struct {
+ unsigned long next_heartbeat; /* the next_heartbeat for the timer */
+ unsigned long open;
+ char expect_close;
+ struct timer_list timer; /* The timer that pings the watchdog */
+} at91wdt_private;
+
+/* ......................................................................... */
+
+
+/*
+ * Reload the watchdog timer. (ie, pat the watchdog)
+ */
+static inline void at91_wdt_reset(void)
+{
+ at91_sys_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
+}
+
+/*
+ * Timer tick
+ */
+static void at91_ping(unsigned long data)
+{
+ if (time_before(jiffies, at91wdt_private.next_heartbeat) ||
+ (!nowayout && !at91wdt_private.open)) {
+ at91_wdt_reset();
+ mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+ } else
+ printk(KERN_CRIT DRV_NAME": I will reset your machine !\n");
+}
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int at91_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &at91wdt_private.open))
+ return -EBUSY;
+
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * Close the watchdog device.
+ */
+static int at91_wdt_close(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &at91wdt_private.open);
+
+ /* stop internal ping */
+ if (!at91wdt_private.expect_close)
+ del_timer(&at91wdt_private.timer);
+
+ at91wdt_private.expect_close = 0;
+ return 0;
+}
+
+/*
+ * Set the watchdog time interval in 1/256Hz (write-once)
+ * Counter is 12 bit.
+ */
+static int at91_wdt_settimeout(unsigned int timeout)
+{
+ unsigned int reg;
+ unsigned int mr;
+
+ /* Check if disabled */
+ mr = at91_sys_read(AT91_WDT_MR);
+ if (mr & AT91_WDT_WDDIS) {
+ printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n");
+ return -EIO;
+ }
+
+ /*
+ * All counting occurs at SLOW_CLOCK / 128 = 256 Hz
+ *
+ * Since WDV is a 12-bit counter, the maximum period is
+ * 4096 / 256 = 16 seconds.
+ */
+ reg = AT91_WDT_WDRSTEN /* causes watchdog reset */
+ /* | AT91_WDT_WDRPROC causes processor reset only */
+ | AT91_WDT_WDDBGHLT /* disabled in debug mode */
+ | AT91_WDT_WDD /* restart at any time */
+ | (timeout & AT91_WDT_WDV); /* timer value */
+ at91_sys_write(AT91_WDT_MR, reg);
+
+ return 0;
+}
+
+static const struct watchdog_info at91_wdt_info = {
+ .identity = DRV_NAME,
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+/*
+ * Handle commands from user-space.
+ */
+static long at91_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &at91_wdt_info,
+ sizeof(at91_wdt_info)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_KEEPALIVE:
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ heartbeat = new_value;
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+
+ return put_user(new_value, p); /* return current value */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ }
+ return -ENOTTY;
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len,
+ loff_t *ppos)
+{
+ if (!len)
+ return 0;
+
+ /* Scan for magic character */
+ if (!nowayout) {
+ size_t i;
+
+ at91wdt_private.expect_close = 0;
+
+ for (i = 0; i < len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V') {
+ at91wdt_private.expect_close = 42;
+ break;
+ }
+ }
+ }
+
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+
+ return len;
+}
+
+/* ......................................................................... */
+
+static const struct file_operations at91wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = at91_wdt_ioctl,
+ .open = at91_wdt_open,
+ .release = at91_wdt_close,
+ .write = at91_wdt_write,
+};
+
+static struct miscdevice at91wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &at91wdt_fops,
+};
+
+static int __init at91wdt_probe(struct platform_device *pdev)
+{
+ int res;
+
+ if (at91wdt_miscdev.parent)
+ return -EBUSY;
+ at91wdt_miscdev.parent = &pdev->dev;
+
+ /* Set watchdog */
+ res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000));
+ if (res)
+ return res;
+
+ res = misc_register(&at91wdt_miscdev);
+ if (res)
+ return res;
+
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ setup_timer(&at91wdt_private.timer, at91_ping, 0);
+ mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+
+ printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n",
+ heartbeat, nowayout);
+
+ return 0;
+}
+
+static int __exit at91wdt_remove(struct platform_device *pdev)
+{
+ int res;
+
+ res = misc_deregister(&at91wdt_miscdev);
+ if (!res)
+ at91wdt_miscdev.parent = NULL;
+
+ return res;
+}
+
+#ifdef CONFIG_PM
+
+static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ return 0;
+}
+
+static int at91wdt_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#else
+#define at91wdt_suspend NULL
+#define at91wdt_resume NULL
+#endif
+
+static struct platform_driver at91wdt_driver = {
+ .remove = __exit_p(at91wdt_remove),
+ .suspend = at91wdt_suspend,
+ .resume = at91wdt_resume,
+ .driver = {
+ .name = "at91_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init at91sam_wdt_init(void)
+{
+ return platform_driver_probe(&at91wdt_driver, at91wdt_probe);
+}
+
+static void __exit at91sam_wdt_exit(void)
+{
+ platform_driver_unregister(&at91wdt_driver);
+}
+
+module_init(at91sam_wdt_init);
+module_exit(at91sam_wdt_exit);
+
+MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>");
+MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c
new file mode 100644
index 00000000..725c84bf
--- /dev/null
+++ b/drivers/watchdog/ath79_wdt.c
@@ -0,0 +1,305 @@
+/*
+ * Atheros AR71XX/AR724X/AR913X built-in hardware watchdog timer.
+ *
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * This driver was based on: drivers/watchdog/ixp4xx_wdt.c
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ * Copyright 2004 (c) MontaVista, Software, Inc.
+ *
+ * which again was based on sa1100 driver,
+ * Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+
+#define DRIVER_NAME "ath79-wdt"
+
+#define WDT_TIMEOUT 15 /* seconds */
+
+#define WDOG_CTRL_LAST_RESET BIT(31)
+#define WDOG_CTRL_ACTION_MASK 3
+#define WDOG_CTRL_ACTION_NONE 0 /* no action */
+#define WDOG_CTRL_ACTION_GPI 1 /* general purpose interrupt */
+#define WDOG_CTRL_ACTION_NMI 2 /* NMI */
+#define WDOG_CTRL_ACTION_FCR 3 /* full chip reset */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int timeout = WDT_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
+ "(default=" __MODULE_STRING(WDT_TIMEOUT) "s)");
+
+static unsigned long wdt_flags;
+
+#define WDT_FLAGS_BUSY 0
+#define WDT_FLAGS_EXPECT_CLOSE 1
+
+static struct clk *wdt_clk;
+static unsigned long wdt_freq;
+static int boot_status;
+static int max_timeout;
+
+static inline void ath79_wdt_keepalive(void)
+{
+ ath79_reset_wr(AR71XX_RESET_REG_WDOG, wdt_freq * timeout);
+}
+
+static inline void ath79_wdt_enable(void)
+{
+ ath79_wdt_keepalive();
+ ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_FCR);
+}
+
+static inline void ath79_wdt_disable(void)
+{
+ ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_NONE);
+}
+
+static int ath79_wdt_set_timeout(int val)
+{
+ if (val < 1 || val > max_timeout)
+ return -EINVAL;
+
+ timeout = val;
+ ath79_wdt_keepalive();
+
+ return 0;
+}
+
+static int ath79_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_FLAGS_BUSY, &wdt_flags))
+ return -EBUSY;
+
+ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
+ ath79_wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static int ath79_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags))
+ ath79_wdt_disable();
+ else {
+ pr_crit(DRIVER_NAME ": device closed unexpectedly, "
+ "watchdog timer will not stop!\n");
+ ath79_wdt_keepalive();
+ }
+
+ clear_bit(WDT_FLAGS_BUSY, &wdt_flags);
+ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
+
+ return 0;
+}
+
+static ssize_t ath79_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+
+ if (c == 'V')
+ set_bit(WDT_FLAGS_EXPECT_CLOSE,
+ &wdt_flags);
+ }
+ }
+
+ ath79_wdt_keepalive();
+ }
+
+ return len;
+}
+
+static const struct watchdog_info ath79_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
+ .firmware_version = 0,
+ .identity = "ATH79 watchdog",
+};
+
+static long ath79_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int err;
+ int t;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ err = copy_to_user(argp, &ath79_wdt_info,
+ sizeof(ath79_wdt_info)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ err = put_user(0, p);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ err = put_user(boot_status, p);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ ath79_wdt_keepalive();
+ err = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ err = get_user(t, p);
+ if (err)
+ break;
+
+ err = ath79_wdt_set_timeout(t);
+ if (err)
+ break;
+
+ /* fallthrough */
+ case WDIOC_GETTIMEOUT:
+ err = put_user(timeout, p);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ return err;
+}
+
+static const struct file_operations ath79_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ath79_wdt_write,
+ .unlocked_ioctl = ath79_wdt_ioctl,
+ .open = ath79_wdt_open,
+ .release = ath79_wdt_release,
+};
+
+static struct miscdevice ath79_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ath79_wdt_fops,
+};
+
+static int __devinit ath79_wdt_probe(struct platform_device *pdev)
+{
+ u32 ctrl;
+ int err;
+
+ wdt_clk = clk_get(&pdev->dev, "wdt");
+ if (IS_ERR(wdt_clk))
+ return PTR_ERR(wdt_clk);
+
+ err = clk_enable(wdt_clk);
+ if (err)
+ goto err_clk_put;
+
+ wdt_freq = clk_get_rate(wdt_clk);
+ if (!wdt_freq) {
+ err = -EINVAL;
+ goto err_clk_disable;
+ }
+
+ max_timeout = (0xfffffffful / wdt_freq);
+ if (timeout < 1 || timeout > max_timeout) {
+ timeout = max_timeout;
+ dev_info(&pdev->dev,
+ "timeout value must be 0 < timeout < %d, using %d\n",
+ max_timeout, timeout);
+ }
+
+ ctrl = ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
+ boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0;
+
+ err = misc_register(&ath79_wdt_miscdev);
+ if (err) {
+ dev_err(&pdev->dev,
+ "unable to register misc device, err=%d\n", err);
+ goto err_clk_disable;
+ }
+
+ return 0;
+
+err_clk_disable:
+ clk_disable(wdt_clk);
+err_clk_put:
+ clk_put(wdt_clk);
+ return err;
+}
+
+static int __devexit ath79_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&ath79_wdt_miscdev);
+ clk_disable(wdt_clk);
+ clk_put(wdt_clk);
+ return 0;
+}
+
+static void ath97_wdt_shutdown(struct platform_device *pdev)
+{
+ ath79_wdt_disable();
+}
+
+static struct platform_driver ath79_wdt_driver = {
+ .remove = __devexit_p(ath79_wdt_remove),
+ .shutdown = ath97_wdt_shutdown,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ath79_wdt_init(void)
+{
+ return platform_driver_probe(&ath79_wdt_driver, ath79_wdt_probe);
+}
+module_init(ath79_wdt_init);
+
+static void __exit ath79_wdt_exit(void)
+{
+ platform_driver_unregister(&ath79_wdt_driver);
+}
+module_exit(ath79_wdt_exit);
+
+MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
+MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
new file mode 100644
index 00000000..bd44417c
--- /dev/null
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -0,0 +1,286 @@
+/*
+ * Watchdog driver for Broadcom BCM47XX
+ *
+ * Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs>
+ * Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/reboot.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/ssb/ssb_embedded.h>
+#include <asm/mach-bcm47xx/bcm47xx.h>
+
+#define DRV_NAME "bcm47xx_wdt"
+
+#define WDT_DEFAULT_TIME 30 /* seconds */
+#define WDT_MAX_TIME 255 /* seconds */
+
+static int wdt_time = WDT_DEFAULT_TIME;
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+module_param(wdt_time, int, 0);
+MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
+ __MODULE_STRING(WDT_DEFAULT_TIME) ")");
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+#endif
+
+static unsigned long bcm47xx_wdt_busy;
+static char expect_release;
+static struct timer_list wdt_timer;
+static atomic_t ticks;
+
+static inline void bcm47xx_wdt_hw_start(void)
+{
+ /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */
+ ssb_watchdog_timer_set(&ssb_bcm47xx, 0xfffffff);
+}
+
+static inline int bcm47xx_wdt_hw_stop(void)
+{
+ return ssb_watchdog_timer_set(&ssb_bcm47xx, 0);
+}
+
+static void bcm47xx_timer_tick(unsigned long unused)
+{
+ if (!atomic_dec_and_test(&ticks)) {
+ bcm47xx_wdt_hw_start();
+ mod_timer(&wdt_timer, jiffies + HZ);
+ } else {
+ printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n");
+ }
+}
+
+static inline void bcm47xx_wdt_pet(void)
+{
+ atomic_set(&ticks, wdt_time);
+}
+
+static void bcm47xx_wdt_start(void)
+{
+ bcm47xx_wdt_pet();
+ bcm47xx_timer_tick(0);
+}
+
+static void bcm47xx_wdt_pause(void)
+{
+ del_timer_sync(&wdt_timer);
+ bcm47xx_wdt_hw_stop();
+}
+
+static void bcm47xx_wdt_stop(void)
+{
+ bcm47xx_wdt_pause();
+}
+
+static int bcm47xx_wdt_settimeout(int new_time)
+{
+ if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
+ return -EINVAL;
+
+ wdt_time = new_time;
+ return 0;
+}
+
+static int bcm47xx_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &bcm47xx_wdt_busy))
+ return -EBUSY;
+
+ bcm47xx_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int bcm47xx_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_release == 42) {
+ bcm47xx_wdt_stop();
+ } else {
+ printk(KERN_CRIT DRV_NAME
+ ": Unexpected close, not stopping watchdog!\n");
+ bcm47xx_wdt_start();
+ }
+
+ clear_bit(0, &bcm47xx_wdt_busy);
+ expect_release = 0;
+ return 0;
+}
+
+static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_release = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_release = 42;
+ }
+ }
+ bcm47xx_wdt_pet();
+ }
+ return len;
+}
+
+static const struct watchdog_info bcm47xx_wdt_info = {
+ .identity = DRV_NAME,
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+static long bcm47xx_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value, retval = -EINVAL;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &bcm47xx_wdt_info,
+ sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ if (new_value & WDIOS_DISABLECARD) {
+ bcm47xx_wdt_stop();
+ retval = 0;
+ }
+
+ if (new_value & WDIOS_ENABLECARD) {
+ bcm47xx_wdt_start();
+ retval = 0;
+ }
+
+ return retval;
+
+ case WDIOC_KEEPALIVE:
+ bcm47xx_wdt_pet();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ if (bcm47xx_wdt_settimeout(new_value))
+ return -EINVAL;
+
+ bcm47xx_wdt_pet();
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdt_time, p);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ bcm47xx_wdt_stop();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations bcm47xx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = bcm47xx_wdt_ioctl,
+ .open = bcm47xx_wdt_open,
+ .release = bcm47xx_wdt_release,
+ .write = bcm47xx_wdt_write,
+};
+
+static struct miscdevice bcm47xx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &bcm47xx_wdt_fops,
+};
+
+static struct notifier_block bcm47xx_wdt_notifier = {
+ .notifier_call = bcm47xx_wdt_notify_sys,
+};
+
+static int __init bcm47xx_wdt_init(void)
+{
+ int ret;
+
+ if (bcm47xx_wdt_hw_stop() < 0)
+ return -ENODEV;
+
+ setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
+
+ if (bcm47xx_wdt_settimeout(wdt_time)) {
+ bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);
+ printk(KERN_INFO DRV_NAME ": "
+ "wdt_time value must be 0 < wdt_time < %d, using %d\n",
+ (WDT_MAX_TIME + 1), wdt_time);
+ }
+
+ ret = register_reboot_notifier(&bcm47xx_wdt_notifier);
+ if (ret)
+ return ret;
+
+ ret = misc_register(&bcm47xx_wdt_miscdev);
+ if (ret) {
+ unregister_reboot_notifier(&bcm47xx_wdt_notifier);
+ return ret;
+ }
+
+ printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
+ wdt_time, nowayout ? ", nowayout" : "");
+ return 0;
+}
+
+static void __exit bcm47xx_wdt_exit(void)
+{
+ if (!nowayout)
+ bcm47xx_wdt_stop();
+
+ misc_deregister(&bcm47xx_wdt_miscdev);
+
+ unregister_reboot_notifier(&bcm47xx_wdt_notifier);
+}
+
+module_init(bcm47xx_wdt_init);
+module_exit(bcm47xx_wdt_exit);
+
+MODULE_AUTHOR("Aleksandar Radovanovic");
+MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
new file mode 100644
index 00000000..5064e831
--- /dev/null
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -0,0 +1,332 @@
+/*
+ * Broadcom BCM63xx SoC watchdog driver
+ *
+ * Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
+ * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/resource.h>
+#include <linux/platform_device.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_timer.h>
+
+#define PFX KBUILD_MODNAME
+
+#define WDT_HZ 50000000 /* Fclk */
+#define WDT_DEFAULT_TIME 30 /* seconds */
+#define WDT_MAX_TIME 256 /* seconds */
+
+static struct {
+ void __iomem *regs;
+ struct timer_list timer;
+ int default_ticks;
+ unsigned long inuse;
+ atomic_t ticks;
+} bcm63xx_wdt_device;
+
+static int expect_close;
+
+static int wdt_time = WDT_DEFAULT_TIME;
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/* HW functions */
+static void bcm63xx_wdt_hw_start(void)
+{
+ bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
+ bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+ bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+}
+
+static void bcm63xx_wdt_hw_stop(void)
+{
+ bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+ bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+}
+
+static void bcm63xx_wdt_isr(void *data)
+{
+ struct pt_regs *regs = get_irq_regs();
+
+ die(PFX " fire", regs);
+}
+
+static void bcm63xx_timer_tick(unsigned long unused)
+{
+ if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
+ bcm63xx_wdt_hw_start();
+ mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
+ } else
+ printk(KERN_CRIT PFX ": watchdog will restart system\n");
+}
+
+static void bcm63xx_wdt_pet(void)
+{
+ atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
+}
+
+static void bcm63xx_wdt_start(void)
+{
+ bcm63xx_wdt_pet();
+ bcm63xx_timer_tick(0);
+}
+
+static void bcm63xx_wdt_pause(void)
+{
+ del_timer_sync(&bcm63xx_wdt_device.timer);
+ bcm63xx_wdt_hw_stop();
+}
+
+static int bcm63xx_wdt_settimeout(int new_time)
+{
+ if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
+ return -EINVAL;
+
+ wdt_time = new_time;
+
+ return 0;
+}
+
+static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
+ return -EBUSY;
+
+ bcm63xx_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ bcm63xx_wdt_pause();
+ else {
+ printk(KERN_CRIT PFX
+ ": Unexpected close, not stopping watchdog!\n");
+ bcm63xx_wdt_start();
+ }
+ clear_bit(0, &bcm63xx_wdt_device.inuse);
+ expect_close = 0;
+ return 0;
+}
+
+static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ bcm63xx_wdt_pet();
+ }
+ return len;
+}
+
+static struct watchdog_info bcm63xx_wdt_info = {
+ .identity = PFX,
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+
+static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value, retval = -EINVAL;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &bcm63xx_wdt_info,
+ sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ if (new_value & WDIOS_DISABLECARD) {
+ bcm63xx_wdt_pause();
+ retval = 0;
+ }
+ if (new_value & WDIOS_ENABLECARD) {
+ bcm63xx_wdt_start();
+ retval = 0;
+ }
+
+ return retval;
+
+ case WDIOC_KEEPALIVE:
+ bcm63xx_wdt_pet();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ if (bcm63xx_wdt_settimeout(new_value))
+ return -EINVAL;
+
+ bcm63xx_wdt_pet();
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdt_time, p);
+
+ default:
+ return -ENOTTY;
+
+ }
+}
+
+static const struct file_operations bcm63xx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = bcm63xx_wdt_write,
+ .unlocked_ioctl = bcm63xx_wdt_ioctl,
+ .open = bcm63xx_wdt_open,
+ .release = bcm63xx_wdt_release,
+};
+
+static struct miscdevice bcm63xx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &bcm63xx_wdt_fops,
+};
+
+
+static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+
+ setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get resources\n");
+ return -ENODEV;
+ }
+
+ bcm63xx_wdt_device.regs = ioremap_nocache(r->start, resource_size(r));
+ if (!bcm63xx_wdt_device.regs) {
+ dev_err(&pdev->dev, "failed to remap I/O resources\n");
+ return -ENXIO;
+ }
+
+ ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register wdt timer isr\n");
+ goto unmap;
+ }
+
+ if (bcm63xx_wdt_settimeout(wdt_time)) {
+ bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
+ dev_info(&pdev->dev,
+ ": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
+ wdt_time);
+ }
+
+ ret = misc_register(&bcm63xx_wdt_miscdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register watchdog device\n");
+ goto unregister_timer;
+ }
+
+ dev_info(&pdev->dev, " started, timer margin: %d sec\n",
+ WDT_DEFAULT_TIME);
+
+ return 0;
+
+unregister_timer:
+ bcm63xx_timer_unregister(TIMER_WDT_ID);
+unmap:
+ iounmap(bcm63xx_wdt_device.regs);
+ return ret;
+}
+
+static int __devexit bcm63xx_wdt_remove(struct platform_device *pdev)
+{
+ if (!nowayout)
+ bcm63xx_wdt_pause();
+
+ misc_deregister(&bcm63xx_wdt_miscdev);
+ bcm63xx_timer_unregister(TIMER_WDT_ID);
+ iounmap(bcm63xx_wdt_device.regs);
+ return 0;
+}
+
+static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
+{
+ bcm63xx_wdt_pause();
+}
+
+static struct platform_driver bcm63xx_wdt = {
+ .probe = bcm63xx_wdt_probe,
+ .remove = __devexit_p(bcm63xx_wdt_remove),
+ .shutdown = bcm63xx_wdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bcm63xx-wdt",
+ }
+};
+
+static int __init bcm63xx_wdt_init(void)
+{
+ return platform_driver_register(&bcm63xx_wdt);
+}
+
+static void __exit bcm63xx_wdt_exit(void)
+{
+ platform_driver_unregister(&bcm63xx_wdt);
+}
+
+module_init(bcm63xx_wdt_init);
+module_exit(bcm63xx_wdt_exit);
+
+MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:bcm63xx-wdt");
diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c
new file mode 100644
index 00000000..b9fa9b71
--- /dev/null
+++ b/drivers/watchdog/bfin_wdt.c
@@ -0,0 +1,485 @@
+/*
+ * Blackfin On-Chip Watchdog Driver
+ *
+ * Originally based on softdog.c
+ * Copyright 2006-2010 Analog Devices Inc.
+ * Copyright 2006-2007 Michele d'Amico
+ * Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <asm/blackfin.h>
+#include <asm/bfin_watchdog.h>
+
+#define stamp(fmt, args...) \
+ pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args)
+#define stampit() stamp("here i am")
+#define pr_devinit(fmt, args...) \
+ ({ static const __devinitconst char __fmt[] = fmt; \
+ printk(__fmt, ## args); })
+#define pr_init(fmt, args...) \
+ ({ static const __initconst char __fmt[] = fmt; \
+ printk(__fmt, ## args); })
+
+#define WATCHDOG_NAME "bfin-wdt"
+#define PFX WATCHDOG_NAME ": "
+
+/* The BF561 has two watchdogs (one per core), but since Linux
+ * only runs on core A, we'll just work with that one.
+ */
+#ifdef BF561_FAMILY
+# define bfin_read_WDOG_CTL() bfin_read_WDOGA_CTL()
+# define bfin_read_WDOG_CNT() bfin_read_WDOGA_CNT()
+# define bfin_read_WDOG_STAT() bfin_read_WDOGA_STAT()
+# define bfin_write_WDOG_CTL(x) bfin_write_WDOGA_CTL(x)
+# define bfin_write_WDOG_CNT(x) bfin_write_WDOGA_CNT(x)
+# define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x)
+#endif
+
+/* some defaults */
+#define WATCHDOG_TIMEOUT 20
+
+static unsigned int timeout = WATCHDOG_TIMEOUT;
+static int nowayout = WATCHDOG_NOWAYOUT;
+static const struct watchdog_info bfin_wdt_info;
+static unsigned long open_check;
+static char expect_close;
+static DEFINE_SPINLOCK(bfin_wdt_spinlock);
+
+/**
+ * bfin_wdt_keepalive - Keep the Userspace Watchdog Alive
+ *
+ * The Userspace watchdog got a KeepAlive: schedule the next timeout.
+ */
+static int bfin_wdt_keepalive(void)
+{
+ stampit();
+ bfin_write_WDOG_STAT(0);
+ return 0;
+}
+
+/**
+ * bfin_wdt_stop - Stop the Watchdog
+ *
+ * Stops the on-chip watchdog.
+ */
+static int bfin_wdt_stop(void)
+{
+ stampit();
+ bfin_write_WDOG_CTL(WDEN_DISABLE);
+ return 0;
+}
+
+/**
+ * bfin_wdt_start - Start the Watchdog
+ *
+ * Starts the on-chip watchdog. Automatically loads WDOG_CNT
+ * into WDOG_STAT for us.
+ */
+static int bfin_wdt_start(void)
+{
+ stampit();
+ bfin_write_WDOG_CTL(WDEN_ENABLE | ICTL_RESET);
+ return 0;
+}
+
+/**
+ * bfin_wdt_running - Check Watchdog status
+ *
+ * See if the watchdog is running.
+ */
+static int bfin_wdt_running(void)
+{
+ stampit();
+ return ((bfin_read_WDOG_CTL() & WDEN_MASK) != WDEN_DISABLE);
+}
+
+/**
+ * bfin_wdt_set_timeout - Set the Userspace Watchdog timeout
+ * @t: new timeout value (in seconds)
+ *
+ * Translate the specified timeout in seconds into System Clock
+ * terms which is what the on-chip Watchdog requires.
+ */
+static int bfin_wdt_set_timeout(unsigned long t)
+{
+ u32 cnt, max_t, sclk;
+ unsigned long flags;
+
+ sclk = get_sclk();
+ max_t = -1 / sclk;
+ cnt = t * sclk;
+ stamp("maxtimeout=%us newtimeout=%lus (cnt=%#x)", max_t, t, cnt);
+
+ if (t > max_t) {
+ printk(KERN_WARNING PFX "timeout value is too large\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bfin_wdt_spinlock, flags);
+ {
+ int run = bfin_wdt_running();
+ bfin_wdt_stop();
+ bfin_write_WDOG_CNT(cnt);
+ if (run)
+ bfin_wdt_start();
+ }
+ spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
+
+ timeout = t;
+
+ return 0;
+}
+
+/**
+ * bfin_wdt_open - Open the Device
+ * @inode: inode of device
+ * @file: file handle of device
+ *
+ * Watchdog device is opened and started.
+ */
+static int bfin_wdt_open(struct inode *inode, struct file *file)
+{
+ stampit();
+
+ if (test_and_set_bit(0, &open_check))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ bfin_wdt_keepalive();
+ bfin_wdt_start();
+
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * bfin_wdt_close - Close the Device
+ * @inode: inode of device
+ * @file: file handle of device
+ *
+ * Watchdog device is closed and stopped.
+ */
+static int bfin_wdt_release(struct inode *inode, struct file *file)
+{
+ stampit();
+
+ if (expect_close == 42)
+ bfin_wdt_stop();
+ else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ bfin_wdt_keepalive();
+ }
+ expect_close = 0;
+ clear_bit(0, &open_check);
+ return 0;
+}
+
+/**
+ * bfin_wdt_write - Write to Device
+ * @file: file handle of device
+ * @buf: buffer to write
+ * @count: length of buffer
+ * @ppos: offset
+ *
+ * Pings the watchdog on write.
+ */
+static ssize_t bfin_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ stampit();
+
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ bfin_wdt_keepalive();
+ }
+
+ return len;
+}
+
+/**
+ * bfin_wdt_ioctl - Query Device
+ * @file: file handle of device
+ * @cmd: watchdog command
+ * @arg: argument
+ *
+ * Query basic information from the device or ping it, as outlined by the
+ * watchdog API.
+ */
+static long bfin_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+
+ stampit();
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info)))
+ return -EFAULT;
+ else
+ return 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p);
+ case WDIOC_SETOPTIONS: {
+ unsigned long flags;
+ int options, ret = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ spin_lock_irqsave(&bfin_wdt_spinlock, flags);
+ if (options & WDIOS_DISABLECARD) {
+ bfin_wdt_stop();
+ ret = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ bfin_wdt_start();
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
+ return ret;
+ }
+ case WDIOC_KEEPALIVE:
+ bfin_wdt_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT: {
+ int new_timeout;
+
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (bfin_wdt_set_timeout(new_timeout))
+ return -EINVAL;
+ }
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+#ifdef CONFIG_PM
+static int state_before_suspend;
+
+/**
+ * bfin_wdt_suspend - suspend the watchdog
+ * @pdev: device being suspended
+ * @state: requested suspend state
+ *
+ * Remember if the watchdog was running and stop it.
+ * TODO: is this even right? Doesn't seem to be any
+ * standard in the watchdog world ...
+ */
+static int bfin_wdt_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ stampit();
+
+ state_before_suspend = bfin_wdt_running();
+ bfin_wdt_stop();
+
+ return 0;
+}
+
+/**
+ * bfin_wdt_resume - resume the watchdog
+ * @pdev: device being resumed
+ *
+ * If the watchdog was running, turn it back on.
+ */
+static int bfin_wdt_resume(struct platform_device *pdev)
+{
+ stampit();
+
+ if (state_before_suspend) {
+ bfin_wdt_set_timeout(timeout);
+ bfin_wdt_start();
+ }
+
+ return 0;
+}
+#else
+# define bfin_wdt_suspend NULL
+# define bfin_wdt_resume NULL
+#endif
+
+static const struct file_operations bfin_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = bfin_wdt_write,
+ .unlocked_ioctl = bfin_wdt_ioctl,
+ .open = bfin_wdt_open,
+ .release = bfin_wdt_release,
+};
+
+static struct miscdevice bfin_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &bfin_wdt_fops,
+};
+
+static const struct watchdog_info bfin_wdt_info = {
+ .identity = "Blackfin Watchdog",
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+/**
+ * bfin_wdt_probe - Initialize module
+ *
+ * Registers the misc device. Actual device
+ * initialization is handled by bfin_wdt_open().
+ */
+static int __devinit bfin_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = misc_register(&bfin_wdt_miscdev);
+ if (ret) {
+ pr_devinit(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ return ret;
+ }
+
+ pr_devinit(KERN_INFO PFX "initialized: timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+ return 0;
+}
+
+/**
+ * bfin_wdt_remove - Initialize module
+ *
+ * Unregisters the misc device. Actual device
+ * deinitialization is handled by bfin_wdt_close().
+ */
+static int __devexit bfin_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&bfin_wdt_miscdev);
+ return 0;
+}
+
+/**
+ * bfin_wdt_shutdown - Soft Shutdown Handler
+ *
+ * Handles the soft shutdown event.
+ */
+static void bfin_wdt_shutdown(struct platform_device *pdev)
+{
+ stampit();
+
+ bfin_wdt_stop();
+}
+
+static struct platform_device *bfin_wdt_device;
+
+static struct platform_driver bfin_wdt_driver = {
+ .probe = bfin_wdt_probe,
+ .remove = __devexit_p(bfin_wdt_remove),
+ .shutdown = bfin_wdt_shutdown,
+ .suspend = bfin_wdt_suspend,
+ .resume = bfin_wdt_resume,
+ .driver = {
+ .name = WATCHDOG_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/**
+ * bfin_wdt_init - Initialize module
+ *
+ * Checks the module params and registers the platform device & driver.
+ * Real work is in the platform probe function.
+ */
+static int __init bfin_wdt_init(void)
+{
+ int ret;
+
+ stampit();
+
+ /* Check that the timeout value is within range */
+ if (bfin_wdt_set_timeout(timeout))
+ return -EINVAL;
+
+ /* Since this is an on-chip device and needs no board-specific
+ * resources, we'll handle all the platform device stuff here.
+ */
+ ret = platform_driver_register(&bfin_wdt_driver);
+ if (ret) {
+ pr_init(KERN_ERR PFX "unable to register driver\n");
+ return ret;
+ }
+
+ bfin_wdt_device = platform_device_register_simple(WATCHDOG_NAME,
+ -1, NULL, 0);
+ if (IS_ERR(bfin_wdt_device)) {
+ pr_init(KERN_ERR PFX "unable to register device\n");
+ platform_driver_unregister(&bfin_wdt_driver);
+ return PTR_ERR(bfin_wdt_device);
+ }
+
+ return 0;
+}
+
+/**
+ * bfin_wdt_exit - Deinitialize module
+ *
+ * Back out the platform device & driver steps. Real work is in the
+ * platform remove function.
+ */
+static void __exit bfin_wdt_exit(void)
+{
+ platform_device_unregister(bfin_wdt_device);
+ platform_driver_unregister(&bfin_wdt_driver);
+}
+
+module_init(bfin_wdt_init);
+module_exit(bfin_wdt_exit);
+
+MODULE_AUTHOR("Michele d'Amico, Mike Frysinger <vapier@gentoo.org>");
+MODULE_DESCRIPTION("Blackfin Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
new file mode 100644
index 00000000..337265b4
--- /dev/null
+++ b/drivers/watchdog/booke_wdt.c
@@ -0,0 +1,299 @@
+/*
+ * Watchdog timer for PowerPC Book-E systems
+ *
+ * Author: Matthew McClintock
+ * Maintainer: Kumar Gala <galak@kernel.crashing.org>
+ *
+ * Copyright 2005, 2008, 2010-2011 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/smp.h>
+#include <linux/miscdevice.h>
+#include <linux/notifier.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+
+#include <asm/reg_booke.h>
+#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/div64.h>
+
+/* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
+ * Also, the wdt_period sets the watchdog timer period timeout.
+ * For E500 cpus the wdt_period sets which bit changing from 0->1 will
+ * trigger a watchog timeout. This watchdog timeout will occur 3 times, the
+ * first time nothing will happen, the second time a watchdog exception will
+ * occur, and the final time the board will reset.
+ */
+
+u32 booke_wdt_enabled;
+u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT;
+
+#ifdef CONFIG_FSL_BOOKE
+#define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15))
+#define WDTP_MASK (WDTP(0x3f))
+#else
+#define WDTP(x) (TCR_WP(x))
+#define WDTP_MASK (TCR_WP_MASK)
+#endif
+
+static DEFINE_SPINLOCK(booke_wdt_lock);
+
+/* For the specified period, determine the number of seconds
+ * corresponding to the reset time. There will be a watchdog
+ * exception at approximately 3/5 of this time.
+ *
+ * The formula to calculate this is given by:
+ * 2.5 * (2^(63-period+1)) / timebase_freq
+ *
+ * In order to simplify things, we assume that period is
+ * at least 1. This will still result in a very long timeout.
+ */
+static unsigned long long period_to_sec(unsigned int period)
+{
+ unsigned long long tmp = 1ULL << (64 - period);
+ unsigned long tmp2 = ppc_tb_freq;
+
+ /* tmp may be a very large number and we don't want to overflow,
+ * so divide the timebase freq instead of multiplying tmp
+ */
+ tmp2 = tmp2 / 5 * 2;
+
+ do_div(tmp, tmp2);
+ return tmp;
+}
+
+/*
+ * This procedure will find the highest period which will give a timeout
+ * greater than the one required. e.g. for a bus speed of 66666666 and
+ * and a parameter of 2 secs, then this procedure will return a value of 38.
+ */
+static unsigned int sec_to_period(unsigned int secs)
+{
+ unsigned int period;
+ for (period = 63; period > 0; period--) {
+ if (period_to_sec(period) >= secs)
+ return period;
+ }
+ return 0;
+}
+
+static void __booke_wdt_set(void *data)
+{
+ u32 val;
+
+ val = mfspr(SPRN_TCR);
+ val &= ~WDTP_MASK;
+ val |= WDTP(booke_wdt_period);
+
+ mtspr(SPRN_TCR, val);
+}
+
+static void booke_wdt_set(void)
+{
+ on_each_cpu(__booke_wdt_set, NULL, 0);
+}
+
+static void __booke_wdt_ping(void *data)
+{
+ mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
+}
+
+static void booke_wdt_ping(void)
+{
+ on_each_cpu(__booke_wdt_ping, NULL, 0);
+}
+
+static void __booke_wdt_enable(void *data)
+{
+ u32 val;
+
+ /* clear status before enabling watchdog */
+ __booke_wdt_ping(NULL);
+ val = mfspr(SPRN_TCR);
+ val &= ~WDTP_MASK;
+ val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period));
+
+ mtspr(SPRN_TCR, val);
+}
+
+/**
+ * booke_wdt_disable - disable the watchdog on the given CPU
+ *
+ * This function is called on each CPU. It disables the watchdog on that CPU.
+ *
+ * TCR[WRC] cannot be changed once it has been set to non-zero, but we can
+ * effectively disable the watchdog by setting its period to the maximum value.
+ */
+static void __booke_wdt_disable(void *data)
+{
+ u32 val;
+
+ val = mfspr(SPRN_TCR);
+ val &= ~(TCR_WIE | WDTP_MASK);
+ mtspr(SPRN_TCR, val);
+
+ /* clear status to make sure nothing is pending */
+ __booke_wdt_ping(NULL);
+
+}
+
+static ssize_t booke_wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ booke_wdt_ping();
+ return count;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "PowerPC Book-E Watchdog",
+};
+
+static long booke_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ u32 tmp = 0;
+ u32 __user *p = (u32 __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((void *)arg, &ident, sizeof(ident)))
+ return -EFAULT;
+ case WDIOC_GETSTATUS:
+ return put_user(0, p);
+ case WDIOC_GETBOOTSTATUS:
+ /* XXX: something is clearing TSR */
+ tmp = mfspr(SPRN_TSR) & TSR_WRS(3);
+ /* returns CARDRESET if last reset was caused by the WDT */
+ return (tmp ? WDIOF_CARDRESET : 0);
+ case WDIOC_SETOPTIONS:
+ if (get_user(tmp, p))
+ return -EINVAL;
+ if (tmp == WDIOS_ENABLECARD) {
+ booke_wdt_ping();
+ break;
+ } else
+ return -EINVAL;
+ return 0;
+ case WDIOC_KEEPALIVE:
+ booke_wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(tmp, p))
+ return -EFAULT;
+#ifdef CONFIG_FSL_BOOKE
+ /* period of 1 gives the largest possible timeout */
+ if (tmp > period_to_sec(1))
+ return -EINVAL;
+ booke_wdt_period = sec_to_period(tmp);
+#else
+ booke_wdt_period = tmp;
+#endif
+ booke_wdt_set();
+ return 0;
+ case WDIOC_GETTIMEOUT:
+ return put_user(booke_wdt_period, p);
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+/* wdt_is_active stores wether or not the /dev/watchdog device is opened */
+static unsigned long wdt_is_active;
+
+static int booke_wdt_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &wdt_is_active))
+ return -EBUSY;
+
+ spin_lock(&booke_wdt_lock);
+ if (booke_wdt_enabled == 0) {
+ booke_wdt_enabled = 1;
+ on_each_cpu(__booke_wdt_enable, NULL, 0);
+ pr_debug("booke_wdt: watchdog enabled (timeout = %llu sec)\n",
+ period_to_sec(booke_wdt_period));
+ }
+ spin_unlock(&booke_wdt_lock);
+
+ return nonseekable_open(inode, file);
+}
+
+static int booke_wdt_release(struct inode *inode, struct file *file)
+{
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ /* Normally, the watchdog is disabled when /dev/watchdog is closed, but
+ * if CONFIG_WATCHDOG_NOWAYOUT is defined, then it means that the
+ * watchdog should remain enabled. So we disable it only if
+ * CONFIG_WATCHDOG_NOWAYOUT is not defined.
+ */
+ on_each_cpu(__booke_wdt_disable, NULL, 0);
+ booke_wdt_enabled = 0;
+ pr_debug("booke_wdt: watchdog disabled\n");
+#endif
+
+ clear_bit(0, &wdt_is_active);
+
+ return 0;
+}
+
+static const struct file_operations booke_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = booke_wdt_write,
+ .unlocked_ioctl = booke_wdt_ioctl,
+ .open = booke_wdt_open,
+ .release = booke_wdt_release,
+};
+
+static struct miscdevice booke_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &booke_wdt_fops,
+};
+
+static void __exit booke_wdt_exit(void)
+{
+ misc_deregister(&booke_wdt_miscdev);
+}
+
+static int __init booke_wdt_init(void)
+{
+ int ret = 0;
+
+ pr_info("booke_wdt: powerpc book-e watchdog driver loaded\n");
+ ident.firmware_version = cur_cpu_spec->pvr_value;
+
+ ret = misc_register(&booke_wdt_miscdev);
+ if (ret) {
+ pr_err("booke_wdt: cannot register device (minor=%u, ret=%i)\n",
+ WATCHDOG_MINOR, ret);
+ return ret;
+ }
+
+ spin_lock(&booke_wdt_lock);
+ if (booke_wdt_enabled == 1) {
+ pr_info("booke_wdt: watchdog enabled (timeout = %llu sec)\n",
+ period_to_sec(booke_wdt_period));
+ on_each_cpu(__booke_wdt_enable, NULL, 0);
+ }
+ spin_unlock(&booke_wdt_lock);
+
+ return ret;
+}
+
+module_init(booke_wdt_init);
+module_exit(booke_wdt_exit);
+
+MODULE_DESCRIPTION("PowerPC Book-E watchdog driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
new file mode 100644
index 00000000..9291506b
--- /dev/null
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -0,0 +1,548 @@
+/*
+ * coh901327_wdt.c
+ *
+ * Copyright (C) 2008-2009 ST-Ericsson AB
+ * License terms: GNU General Public License (GPL) version 2
+ * Watchdog driver for the ST-Ericsson AB COH 901 327 IP core
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ */
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#define DRV_NAME "WDOG COH 901 327"
+
+/*
+ * COH 901 327 register definitions
+ */
+
+/* WDOG_FEED Register 32bit (-/W) */
+#define U300_WDOG_FR 0x00
+#define U300_WDOG_FR_FEED_RESTART_TIMER 0xFEEDU
+/* WDOG_TIMEOUT Register 32bit (R/W) */
+#define U300_WDOG_TR 0x04
+#define U300_WDOG_TR_TIMEOUT_MASK 0x7FFFU
+/* WDOG_DISABLE1 Register 32bit (-/W) */
+#define U300_WDOG_D1R 0x08
+#define U300_WDOG_D1R_DISABLE1_DISABLE_TIMER 0x2BADU
+/* WDOG_DISABLE2 Register 32bit (R/W) */
+#define U300_WDOG_D2R 0x0C
+#define U300_WDOG_D2R_DISABLE2_DISABLE_TIMER 0xCAFEU
+#define U300_WDOG_D2R_DISABLE_STATUS_DISABLED 0xDABEU
+#define U300_WDOG_D2R_DISABLE_STATUS_ENABLED 0x0000U
+/* WDOG_STATUS Register 32bit (R/W) */
+#define U300_WDOG_SR 0x10
+#define U300_WDOG_SR_STATUS_TIMED_OUT 0xCFE8U
+#define U300_WDOG_SR_STATUS_NORMAL 0x0000U
+#define U300_WDOG_SR_RESET_STATUS_RESET 0xE8B4U
+/* WDOG_COUNT Register 32bit (R/-) */
+#define U300_WDOG_CR 0x14
+#define U300_WDOG_CR_VALID_IND 0x8000U
+#define U300_WDOG_CR_VALID_STABLE 0x0000U
+#define U300_WDOG_CR_COUNT_VALUE_MASK 0x7FFFU
+/* WDOG_JTAGOVR Register 32bit (R/W) */
+#define U300_WDOG_JOR 0x18
+#define U300_WDOG_JOR_JTAG_MODE_IND 0x0002U
+#define U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE 0x0001U
+/* WDOG_RESTART Register 32bit (-/W) */
+#define U300_WDOG_RR 0x1C
+#define U300_WDOG_RR_RESTART_VALUE_RESUME 0xACEDU
+/* WDOG_IRQ_EVENT Register 32bit (R/W) */
+#define U300_WDOG_IER 0x20
+#define U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND 0x0001U
+#define U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE 0x0001U
+/* WDOG_IRQ_MASK Register 32bit (R/W) */
+#define U300_WDOG_IMR 0x24
+#define U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE 0x0001U
+/* WDOG_IRQ_FORCE Register 32bit (R/W) */
+#define U300_WDOG_IFR 0x28
+#define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U
+
+/* Default timeout in seconds = 1 minute */
+static int margin = 60;
+static resource_size_t phybase;
+static resource_size_t physize;
+static int irq;
+static void __iomem *virtbase;
+static unsigned long coh901327_users;
+static unsigned long boot_status;
+static u16 wdogenablestore;
+static u16 irqmaskstore;
+static struct device *parent;
+
+/*
+ * The watchdog block is of course always clocked, the
+ * clk_enable()/clk_disable() calls are mainly for performing reference
+ * counting higher up in the clock hierarchy.
+ */
+static struct clk *clk;
+
+/*
+ * Enabling and disabling functions.
+ */
+static void coh901327_enable(u16 timeout)
+{
+ u16 val;
+ unsigned long freq;
+ unsigned long delay_ns;
+
+ clk_enable(clk);
+ /* Restart timer if it is disabled */
+ val = readw(virtbase + U300_WDOG_D2R);
+ if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED)
+ writew(U300_WDOG_RR_RESTART_VALUE_RESUME,
+ virtbase + U300_WDOG_RR);
+ /* Acknowledge any pending interrupt so it doesn't just fire off */
+ writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE,
+ virtbase + U300_WDOG_IER);
+ /*
+ * The interrupt is cleared in the 32 kHz clock domain.
+ * Wait 3 32 kHz cycles for it to take effect
+ */
+ freq = clk_get_rate(clk);
+ delay_ns = DIV_ROUND_UP(1000000000, freq); /* Freq to ns and round up */
+ delay_ns = 3 * delay_ns; /* Wait 3 cycles */
+ ndelay(delay_ns);
+ /* Enable the watchdog interrupt */
+ writew(U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE, virtbase + U300_WDOG_IMR);
+ /* Activate the watchdog timer */
+ writew(timeout, virtbase + U300_WDOG_TR);
+ /* Start the watchdog timer */
+ writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR);
+ /*
+ * Extra read so that this change propagate in the watchdog.
+ */
+ (void) readw(virtbase + U300_WDOG_CR);
+ val = readw(virtbase + U300_WDOG_D2R);
+ clk_disable(clk);
+ if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED)
+ dev_err(parent,
+ "%s(): watchdog not enabled! D2R value %04x\n",
+ __func__, val);
+}
+
+static void coh901327_disable(void)
+{
+ u16 val;
+
+ clk_enable(clk);
+ /* Disable the watchdog interrupt if it is active */
+ writew(0x0000U, virtbase + U300_WDOG_IMR);
+ /* If the watchdog is currently enabled, attempt to disable it */
+ val = readw(virtbase + U300_WDOG_D2R);
+ if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) {
+ writew(U300_WDOG_D1R_DISABLE1_DISABLE_TIMER,
+ virtbase + U300_WDOG_D1R);
+ writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER,
+ virtbase + U300_WDOG_D2R);
+ /* Write this twice (else problems occur) */
+ writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER,
+ virtbase + U300_WDOG_D2R);
+ }
+ val = readw(virtbase + U300_WDOG_D2R);
+ clk_disable(clk);
+ if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED)
+ dev_err(parent,
+ "%s(): watchdog not disabled! D2R value %04x\n",
+ __func__, val);
+}
+
+static void coh901327_start(void)
+{
+ coh901327_enable(margin * 100);
+}
+
+static void coh901327_keepalive(void)
+{
+ clk_enable(clk);
+ /* Feed the watchdog */
+ writew(U300_WDOG_FR_FEED_RESTART_TIMER,
+ virtbase + U300_WDOG_FR);
+ clk_disable(clk);
+}
+
+static int coh901327_settimeout(int time)
+{
+ /*
+ * Max margin is 327 since the 10ms
+ * timeout register is max
+ * 0x7FFF = 327670ms ~= 327s.
+ */
+ if (time <= 0 || time > 327)
+ return -EINVAL;
+
+ margin = time;
+ clk_enable(clk);
+ /* Set new timeout value */
+ writew(margin * 100, virtbase + U300_WDOG_TR);
+ /* Feed the dog */
+ writew(U300_WDOG_FR_FEED_RESTART_TIMER,
+ virtbase + U300_WDOG_FR);
+ clk_disable(clk);
+ return 0;
+}
+
+/*
+ * This interrupt occurs 10 ms before the watchdog WILL bark.
+ */
+static irqreturn_t coh901327_interrupt(int irq, void *data)
+{
+ u16 val;
+
+ /*
+ * Ack IRQ? If this occurs we're FUBAR anyway, so
+ * just acknowledge, disable the interrupt and await the imminent end.
+ * If you at some point need a host of callbacks to be called
+ * when the system is about to watchdog-reset, add them here!
+ *
+ * NOTE: on future versions of this IP-block, it will be possible
+ * to prevent a watchdog reset by feeding the watchdog at this
+ * point.
+ */
+ clk_enable(clk);
+ val = readw(virtbase + U300_WDOG_IER);
+ if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND)
+ writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE,
+ virtbase + U300_WDOG_IER);
+ writew(0x0000U, virtbase + U300_WDOG_IMR);
+ clk_disable(clk);
+ dev_crit(parent, "watchdog is barking!\n");
+ return IRQ_HANDLED;
+}
+
+/*
+ * Allow only one user (daemon) to open the watchdog
+ */
+static int coh901327_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(1, &coh901327_users))
+ return -EBUSY;
+ coh901327_start();
+ return nonseekable_open(inode, file);
+}
+
+static int coh901327_release(struct inode *inode, struct file *file)
+{
+ clear_bit(1, &coh901327_users);
+ coh901327_disable();
+ return 0;
+}
+
+static ssize_t coh901327_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len)
+ coh901327_keepalive();
+ return len;
+}
+
+static long coh901327_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ u16 val;
+ int time;
+ int new_options;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET |
+ WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "COH 901 327 Watchdog",
+ .firmware_version = 1,
+ };
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user(uarg.ident, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, uarg.i);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(boot_status, uarg.i);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ ret = get_user(new_options, uarg.i);
+ if (ret)
+ break;
+ if (new_options & WDIOS_DISABLECARD)
+ coh901327_disable();
+ if (new_options & WDIOS_ENABLECARD)
+ coh901327_start();
+ ret = 0;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ coh901327_keepalive();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, uarg.i);
+ if (ret)
+ break;
+
+ ret = coh901327_settimeout(time);
+ if (ret)
+ break;
+ /* Then fall through to return set value */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(margin, uarg.i);
+ break;
+
+ case WDIOC_GETTIMELEFT:
+ clk_enable(clk);
+ /* Read repeatedly until the value is stable! */
+ val = readw(virtbase + U300_WDOG_CR);
+ while (val & U300_WDOG_CR_VALID_IND)
+ val = readw(virtbase + U300_WDOG_CR);
+ val &= U300_WDOG_CR_COUNT_VALUE_MASK;
+ clk_disable(clk);
+ if (val != 0)
+ val /= 100;
+ ret = put_user(val, uarg.i);
+ break;
+ }
+ return ret;
+}
+
+static const struct file_operations coh901327_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = coh901327_write,
+ .unlocked_ioctl = coh901327_ioctl,
+ .open = coh901327_open,
+ .release = coh901327_release,
+};
+
+static struct miscdevice coh901327_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &coh901327_fops,
+};
+
+static int __exit coh901327_remove(struct platform_device *pdev)
+{
+ misc_deregister(&coh901327_miscdev);
+ coh901327_disable();
+ free_irq(irq, pdev);
+ clk_put(clk);
+ iounmap(virtbase);
+ release_mem_region(phybase, physize);
+ return 0;
+}
+
+
+static int __init coh901327_probe(struct platform_device *pdev)
+{
+ int ret;
+ u16 val;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+
+ parent = &pdev->dev;
+ physize = resource_size(res);
+ phybase = res->start;
+
+ if (request_mem_region(phybase, physize, DRV_NAME) == NULL) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ virtbase = ioremap(phybase, physize);
+ if (!virtbase) {
+ ret = -ENOMEM;
+ goto out_no_remap;
+ }
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(&pdev->dev, "could not get clock\n");
+ goto out_no_clk;
+ }
+ ret = clk_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "could not enable clock\n");
+ goto out_no_clk_enable;
+ }
+
+ val = readw(virtbase + U300_WDOG_SR);
+ switch (val) {
+ case U300_WDOG_SR_STATUS_TIMED_OUT:
+ dev_info(&pdev->dev,
+ "watchdog timed out since last chip reset!\n");
+ boot_status = WDIOF_CARDRESET;
+ /* Status will be cleared below */
+ break;
+ case U300_WDOG_SR_STATUS_NORMAL:
+ dev_info(&pdev->dev,
+ "in normal status, no timeouts have occurred.\n");
+ break;
+ default:
+ dev_info(&pdev->dev,
+ "contains an illegal status code (%08x)\n", val);
+ break;
+ }
+
+ val = readw(virtbase + U300_WDOG_D2R);
+ switch (val) {
+ case U300_WDOG_D2R_DISABLE_STATUS_DISABLED:
+ dev_info(&pdev->dev, "currently disabled.\n");
+ break;
+ case U300_WDOG_D2R_DISABLE_STATUS_ENABLED:
+ dev_info(&pdev->dev,
+ "currently enabled! (disabling it now)\n");
+ coh901327_disable();
+ break;
+ default:
+ dev_err(&pdev->dev,
+ "contains an illegal enable/disable code (%08x)\n",
+ val);
+ break;
+ }
+
+ /* Reset the watchdog */
+ writew(U300_WDOG_SR_RESET_STATUS_RESET, virtbase + U300_WDOG_SR);
+
+ irq = platform_get_irq(pdev, 0);
+ if (request_irq(irq, coh901327_interrupt, IRQF_DISABLED,
+ DRV_NAME " Bark", pdev)) {
+ ret = -EIO;
+ goto out_no_irq;
+ }
+
+ clk_disable(clk);
+
+ ret = misc_register(&coh901327_miscdev);
+ if (ret == 0)
+ dev_info(&pdev->dev,
+ "initialized. timer margin=%d sec\n", margin);
+ else
+ goto out_no_wdog;
+
+ return 0;
+
+out_no_wdog:
+ free_irq(irq, pdev);
+out_no_irq:
+ clk_disable(clk);
+out_no_clk_enable:
+ clk_put(clk);
+out_no_clk:
+ iounmap(virtbase);
+out_no_remap:
+ release_mem_region(phybase, SZ_4K);
+out:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int coh901327_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ irqmaskstore = readw(virtbase + U300_WDOG_IMR) & 0x0001U;
+ wdogenablestore = readw(virtbase + U300_WDOG_D2R);
+ /* If watchdog is on, disable it here and now */
+ if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED)
+ coh901327_disable();
+ return 0;
+}
+
+static int coh901327_resume(struct platform_device *pdev)
+{
+ /* Restore the watchdog interrupt */
+ writew(irqmaskstore, virtbase + U300_WDOG_IMR);
+ if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) {
+ /* Restart the watchdog timer */
+ writew(U300_WDOG_RR_RESTART_VALUE_RESUME,
+ virtbase + U300_WDOG_RR);
+ writew(U300_WDOG_FR_FEED_RESTART_TIMER,
+ virtbase + U300_WDOG_FR);
+ }
+ return 0;
+}
+#else
+#define coh901327_suspend NULL
+#define coh901327_resume NULL
+#endif
+
+/*
+ * Mistreating the watchdog is the only way to perform a software reset of the
+ * system on EMP platforms. So we implement this and export a symbol for it.
+ */
+void coh901327_watchdog_reset(void)
+{
+ /* Enable even if on JTAG too */
+ writew(U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE,
+ virtbase + U300_WDOG_JOR);
+ /*
+ * Timeout = 5s, we have to wait for the watchdog reset to
+ * actually take place: the watchdog will be reloaded with the
+ * default value immediately, so we HAVE to reboot and get back
+ * into the kernel in 30s, or the device will reboot again!
+ * The boot loader will typically deactivate the watchdog, so we
+ * need time enough for the boot loader to get to the point of
+ * deactivating the watchdog before it is shut down by it.
+ *
+ * NOTE: on future versions of the watchdog, this restriction is
+ * gone: the watchdog will be reloaded with a default value (1 min)
+ * instead of last value, and you can conveniently set the watchdog
+ * timeout to 10ms (value = 1) without any problems.
+ */
+ coh901327_enable(500);
+ /* Return and await doom */
+}
+
+static struct platform_driver coh901327_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "coh901327_wdog",
+ },
+ .remove = __exit_p(coh901327_remove),
+ .suspend = coh901327_suspend,
+ .resume = coh901327_resume,
+};
+
+static int __init coh901327_init(void)
+{
+ return platform_driver_probe(&coh901327_driver, coh901327_probe);
+}
+module_init(coh901327_init);
+
+static void __exit coh901327_exit(void)
+{
+ platform_driver_unregister(&coh901327_driver);
+}
+module_exit(coh901327_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+MODULE_DESCRIPTION("COH 901 327 Watchdog");
+
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c
new file mode 100644
index 00000000..edd3475f
--- /dev/null
+++ b/drivers/watchdog/cpu5wdt.c
@@ -0,0 +1,300 @@
+/*
+ * sma cpu5 watchdog driver
+ *
+ * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/completion.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+/* adjustable parameters */
+
+static int verbose;
+static int port = 0x91;
+static int ticks = 10000;
+static spinlock_t cpu5wdt_lock;
+
+#define PFX "cpu5wdt: "
+
+#define CPU5WDT_EXTENT 0x0A
+
+#define CPU5WDT_STATUS_REG 0x00
+#define CPU5WDT_TIME_A_REG 0x02
+#define CPU5WDT_TIME_B_REG 0x03
+#define CPU5WDT_MODE_REG 0x04
+#define CPU5WDT_TRIGGER_REG 0x07
+#define CPU5WDT_ENABLE_REG 0x08
+#define CPU5WDT_RESET_REG 0x09
+
+#define CPU5WDT_INTERVAL (HZ/10+1)
+
+/* some device data */
+
+static struct {
+ struct completion stop;
+ int running;
+ struct timer_list timer;
+ int queue;
+ int default_ticks;
+ unsigned long inuse;
+} cpu5wdt_device;
+
+/* generic helper functions */
+
+static void cpu5wdt_trigger(unsigned long unused)
+{
+ if (verbose > 2)
+ printk(KERN_DEBUG PFX "trigger at %i ticks\n", ticks);
+
+ if (cpu5wdt_device.running)
+ ticks--;
+
+ spin_lock(&cpu5wdt_lock);
+ /* keep watchdog alive */
+ outb(1, port + CPU5WDT_TRIGGER_REG);
+
+ /* requeue?? */
+ if (cpu5wdt_device.queue && ticks)
+ mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
+ else {
+ /* ticks doesn't matter anyway */
+ complete(&cpu5wdt_device.stop);
+ }
+ spin_unlock(&cpu5wdt_lock);
+
+}
+
+static void cpu5wdt_reset(void)
+{
+ ticks = cpu5wdt_device.default_ticks;
+
+ if (verbose)
+ printk(KERN_DEBUG PFX "reset (%i ticks)\n", (int) ticks);
+
+}
+
+static void cpu5wdt_start(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cpu5wdt_lock, flags);
+ if (!cpu5wdt_device.queue) {
+ cpu5wdt_device.queue = 1;
+ outb(0, port + CPU5WDT_TIME_A_REG);
+ outb(0, port + CPU5WDT_TIME_B_REG);
+ outb(1, port + CPU5WDT_MODE_REG);
+ outb(0, port + CPU5WDT_RESET_REG);
+ outb(0, port + CPU5WDT_ENABLE_REG);
+ mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
+ }
+ /* if process dies, counter is not decremented */
+ cpu5wdt_device.running++;
+ spin_unlock_irqrestore(&cpu5wdt_lock, flags);
+}
+
+static int cpu5wdt_stop(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cpu5wdt_lock, flags);
+ if (cpu5wdt_device.running)
+ cpu5wdt_device.running = 0;
+ ticks = cpu5wdt_device.default_ticks;
+ spin_unlock_irqrestore(&cpu5wdt_lock, flags);
+ if (verbose)
+ printk(KERN_CRIT PFX "stop not possible\n");
+ return -EIO;
+}
+
+/* filesystem operations */
+
+static int cpu5wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &cpu5wdt_device.inuse))
+ return -EBUSY;
+ return nonseekable_open(inode, file);
+}
+
+static int cpu5wdt_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &cpu5wdt_device.inuse);
+ return 0;
+}
+
+static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ unsigned int value;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET,
+ .identity = "CPU5 WDT",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+ case WDIOC_GETSTATUS:
+ value = inb(port + CPU5WDT_STATUS_REG);
+ value = (value >> 2) & 1;
+ return put_user(value, p);
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_SETOPTIONS:
+ if (get_user(value, p))
+ return -EFAULT;
+ if (value & WDIOS_ENABLECARD)
+ cpu5wdt_start();
+ if (value & WDIOS_DISABLECARD)
+ cpu5wdt_stop();
+ break;
+ case WDIOC_KEEPALIVE:
+ cpu5wdt_reset();
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (!count)
+ return -EIO;
+ cpu5wdt_reset();
+ return count;
+}
+
+static const struct file_operations cpu5wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = cpu5wdt_ioctl,
+ .open = cpu5wdt_open,
+ .write = cpu5wdt_write,
+ .release = cpu5wdt_release,
+};
+
+static struct miscdevice cpu5wdt_misc = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &cpu5wdt_fops,
+};
+
+/* init/exit function */
+
+static int __devinit cpu5wdt_init(void)
+{
+ unsigned int val;
+ int err;
+
+ if (verbose)
+ printk(KERN_DEBUG PFX
+ "port=0x%x, verbose=%i\n", port, verbose);
+
+ init_completion(&cpu5wdt_device.stop);
+ spin_lock_init(&cpu5wdt_lock);
+ cpu5wdt_device.queue = 0;
+ setup_timer(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
+ cpu5wdt_device.default_ticks = ticks;
+
+ if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
+ printk(KERN_ERR PFX "request_region failed\n");
+ err = -EBUSY;
+ goto no_port;
+ }
+
+ /* watchdog reboot? */
+ val = inb(port + CPU5WDT_STATUS_REG);
+ val = (val >> 2) & 1;
+ if (!val)
+ printk(KERN_INFO PFX "sorry, was my fault\n");
+
+ err = misc_register(&cpu5wdt_misc);
+ if (err < 0) {
+ printk(KERN_ERR PFX "misc_register failed\n");
+ goto no_misc;
+ }
+
+
+ printk(KERN_INFO PFX "init success\n");
+ return 0;
+
+no_misc:
+ release_region(port, CPU5WDT_EXTENT);
+no_port:
+ return err;
+}
+
+static int __devinit cpu5wdt_init_module(void)
+{
+ return cpu5wdt_init();
+}
+
+static void __devexit cpu5wdt_exit(void)
+{
+ if (cpu5wdt_device.queue) {
+ cpu5wdt_device.queue = 0;
+ wait_for_completion(&cpu5wdt_device.stop);
+ }
+
+ misc_deregister(&cpu5wdt_misc);
+
+ release_region(port, CPU5WDT_EXTENT);
+
+}
+
+static void __devexit cpu5wdt_exit_module(void)
+{
+ cpu5wdt_exit();
+}
+
+/* module entry points */
+
+module_init(cpu5wdt_init_module);
+module_exit(cpu5wdt_exit_module);
+
+MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
+MODULE_DESCRIPTION("sma cpu5 watchdog driver");
+MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+module_param(port, int, 0);
+MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
+
+module_param(verbose, int, 0);
+MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
+
+module_param(ticks, int, 0);
+MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c
new file mode 100644
index 00000000..1e013e84
--- /dev/null
+++ b/drivers/watchdog/cpwd.c
@@ -0,0 +1,701 @@
+/* cpwd.c - driver implementation for hardware watchdog
+ * timers found on Sun Microsystems CP1400 and CP1500 boards.
+ *
+ * This device supports both the generic Linux watchdog
+ * interface and Solaris-compatible ioctls as best it is
+ * able.
+ *
+ * NOTE: CP1400 systems appear to have a defective intr_mask
+ * register on the PLD, preventing the disabling of
+ * timer interrupts. We use a timer to periodically
+ * reset 'stopped' watchdogs on affected platforms.
+ *
+ * Copyright (c) 2000 Eric Brower (ebrower@usa.net)
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/uaccess.h>
+
+#include <asm/irq.h>
+#include <asm/watchdog.h>
+
+#define DRIVER_NAME "cpwd"
+#define PFX DRIVER_NAME ": "
+
+#define WD_OBPNAME "watchdog"
+#define WD_BADMODEL "SUNW,501-5336"
+#define WD_BTIMEOUT (jiffies + (HZ * 1000))
+#define WD_BLIMIT 0xFFFF
+
+#define WD0_MINOR 212
+#define WD1_MINOR 213
+#define WD2_MINOR 214
+
+/* Internal driver definitions. */
+#define WD0_ID 0
+#define WD1_ID 1
+#define WD2_ID 2
+#define WD_NUMDEVS 3
+
+#define WD_INTR_OFF 0
+#define WD_INTR_ON 1
+
+#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */
+#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */
+#define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */
+
+/* Register value definitions
+ */
+#define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */
+#define WD1_INTR_MASK 0x02
+#define WD2_INTR_MASK 0x04
+
+#define WD_S_RUNNING 0x01 /* Watchdog device status running */
+#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */
+
+struct cpwd {
+ void __iomem *regs;
+ spinlock_t lock;
+
+ unsigned int irq;
+
+ unsigned long timeout;
+ bool enabled;
+ bool reboot;
+ bool broken;
+ bool initialized;
+
+ struct {
+ struct miscdevice misc;
+ void __iomem *regs;
+ u8 intr_mask;
+ u8 runstatus;
+ u16 timeout;
+ } devs[WD_NUMDEVS];
+};
+
+static DEFINE_MUTEX(cpwd_mutex);
+static struct cpwd *cpwd_device;
+
+/* Sun uses Altera PLD EPF8820ATC144-4
+ * providing three hardware watchdogs:
+ *
+ * 1) RIC - sends an interrupt when triggered
+ * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU
+ * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board
+ *
+ *** Timer register block definition (struct wd_timer_regblk)
+ *
+ * dcntr and limit registers (halfword access):
+ * -------------------
+ * | 15 | ...| 1 | 0 |
+ * -------------------
+ * |- counter val -|
+ * -------------------
+ * dcntr - Current 16-bit downcounter value.
+ * When downcounter reaches '0' watchdog expires.
+ * Reading this register resets downcounter with
+ * 'limit' value.
+ * limit - 16-bit countdown value in 1/10th second increments.
+ * Writing this register begins countdown with input value.
+ * Reading from this register does not affect counter.
+ * NOTES: After watchdog reset, dcntr and limit contain '1'
+ *
+ * status register (byte access):
+ * ---------------------------
+ * | 7 | ... | 2 | 1 | 0 |
+ * --------------+------------
+ * |- UNUSED -| EXP | RUN |
+ * ---------------------------
+ * status- Bit 0 - Watchdog is running
+ * Bit 1 - Watchdog has expired
+ *
+ *** PLD register block definition (struct wd_pld_regblk)
+ *
+ * intr_mask register (byte access):
+ * ---------------------------------
+ * | 7 | ... | 3 | 2 | 1 | 0 |
+ * +-------------+------------------
+ * |- UNUSED -| WD3 | WD2 | WD1 |
+ * ---------------------------------
+ * WD3 - 1 == Interrupt disabled for watchdog 3
+ * WD2 - 1 == Interrupt disabled for watchdog 2
+ * WD1 - 1 == Interrupt disabled for watchdog 1
+ *
+ * pld_status register (byte access):
+ * UNKNOWN, MAGICAL MYSTERY REGISTER
+ *
+ */
+#define WD_TIMER_REGSZ 16
+#define WD0_OFF 0
+#define WD1_OFF (WD_TIMER_REGSZ * 1)
+#define WD2_OFF (WD_TIMER_REGSZ * 2)
+#define PLD_OFF (WD_TIMER_REGSZ * 3)
+
+#define WD_DCNTR 0x00
+#define WD_LIMIT 0x04
+#define WD_STATUS 0x08
+
+#define PLD_IMASK (PLD_OFF + 0x00)
+#define PLD_STATUS (PLD_OFF + 0x04)
+
+static struct timer_list cpwd_timer;
+
+static int wd0_timeout;
+static int wd1_timeout;
+static int wd2_timeout;
+
+module_param(wd0_timeout, int, 0);
+MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
+module_param(wd1_timeout, int, 0);
+MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
+module_param(wd2_timeout, int, 0);
+MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
+
+MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
+MODULE_DESCRIPTION("Hardware watchdog driver for Sun Microsystems CP1400/1500");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("watchdog");
+
+static void cpwd_writew(u16 val, void __iomem *addr)
+{
+ writew(cpu_to_le16(val), addr);
+}
+static u16 cpwd_readw(void __iomem *addr)
+{
+ u16 val = readw(addr);
+
+ return le16_to_cpu(val);
+}
+
+static void cpwd_writeb(u8 val, void __iomem *addr)
+{
+ writeb(val, addr);
+}
+
+static u8 cpwd_readb(void __iomem *addr)
+{
+ return readb(addr);
+}
+
+/* Enable or disable watchdog interrupts
+ * Because of the CP1400 defect this should only be
+ * called during initialzation or by wd_[start|stop]timer()
+ *
+ * index - sub-device index, or -1 for 'all'
+ * enable - non-zero to enable interrupts, zero to disable
+ */
+static void cpwd_toggleintr(struct cpwd *p, int index, int enable)
+{
+ unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK);
+ unsigned char setregs =
+ (index == -1) ?
+ (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
+ (p->devs[index].intr_mask);
+
+ if (enable == WD_INTR_ON)
+ curregs &= ~setregs;
+ else
+ curregs |= setregs;
+
+ cpwd_writeb(curregs, p->regs + PLD_IMASK);
+}
+
+/* Restarts timer with maximum limit value and
+ * does not unset 'brokenstop' value.
+ */
+static void cpwd_resetbrokentimer(struct cpwd *p, int index)
+{
+ cpwd_toggleintr(p, index, WD_INTR_ON);
+ cpwd_writew(WD_BLIMIT, p->devs[index].regs + WD_LIMIT);
+}
+
+/* Timer method called to reset stopped watchdogs--
+ * because of the PLD bug on CP1400, we cannot mask
+ * interrupts within the PLD so me must continually
+ * reset the timers ad infinitum.
+ */
+static void cpwd_brokentimer(unsigned long data)
+{
+ struct cpwd *p = (struct cpwd *) data;
+ int id, tripped = 0;
+
+ /* kill a running timer instance, in case we
+ * were called directly instead of by kernel timer
+ */
+ if (timer_pending(&cpwd_timer))
+ del_timer(&cpwd_timer);
+
+ for (id = 0; id < WD_NUMDEVS; id++) {
+ if (p->devs[id].runstatus & WD_STAT_BSTOP) {
+ ++tripped;
+ cpwd_resetbrokentimer(p, id);
+ }
+ }
+
+ if (tripped) {
+ /* there is at least one timer brokenstopped-- reschedule */
+ cpwd_timer.expires = WD_BTIMEOUT;
+ add_timer(&cpwd_timer);
+ }
+}
+
+/* Reset countdown timer with 'limit' value and continue countdown.
+ * This will not start a stopped timer.
+ */
+static void cpwd_pingtimer(struct cpwd *p, int index)
+{
+ if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING)
+ cpwd_readw(p->devs[index].regs + WD_DCNTR);
+}
+
+/* Stop a running watchdog timer-- the timer actually keeps
+ * running, but the interrupt is masked so that no action is
+ * taken upon expiration.
+ */
+static void cpwd_stoptimer(struct cpwd *p, int index)
+{
+ if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) {
+ cpwd_toggleintr(p, index, WD_INTR_OFF);
+
+ if (p->broken) {
+ p->devs[index].runstatus |= WD_STAT_BSTOP;
+ cpwd_brokentimer((unsigned long) p);
+ }
+ }
+}
+
+/* Start a watchdog timer with the specified limit value
+ * If the watchdog is running, it will be restarted with
+ * the provided limit value.
+ *
+ * This function will enable interrupts on the specified
+ * watchdog.
+ */
+static void cpwd_starttimer(struct cpwd *p, int index)
+{
+ if (p->broken)
+ p->devs[index].runstatus &= ~WD_STAT_BSTOP;
+
+ p->devs[index].runstatus &= ~WD_STAT_SVCD;
+
+ cpwd_writew(p->devs[index].timeout, p->devs[index].regs + WD_LIMIT);
+ cpwd_toggleintr(p, index, WD_INTR_ON);
+}
+
+static int cpwd_getstatus(struct cpwd *p, int index)
+{
+ unsigned char stat = cpwd_readb(p->devs[index].regs + WD_STATUS);
+ unsigned char intr = cpwd_readb(p->devs[index].regs + PLD_IMASK);
+ unsigned char ret = WD_STOPPED;
+
+ /* determine STOPPED */
+ if (!stat)
+ return ret;
+
+ /* determine EXPIRED vs FREERUN vs RUNNING */
+ else if (WD_S_EXPIRED & stat) {
+ ret = WD_EXPIRED;
+ } else if (WD_S_RUNNING & stat) {
+ if (intr & p->devs[index].intr_mask) {
+ ret = WD_FREERUN;
+ } else {
+ /* Fudge WD_EXPIRED status for defective CP1400--
+ * IF timer is running
+ * AND brokenstop is set
+ * AND an interrupt has been serviced
+ * we are WD_EXPIRED.
+ *
+ * IF timer is running
+ * AND brokenstop is set
+ * AND no interrupt has been serviced
+ * we are WD_FREERUN.
+ */
+ if (p->broken &&
+ (p->devs[index].runstatus & WD_STAT_BSTOP)) {
+ if (p->devs[index].runstatus & WD_STAT_SVCD) {
+ ret = WD_EXPIRED;
+ } else {
+ /* we could as well pretend
+ * we are expired */
+ ret = WD_FREERUN;
+ }
+ } else {
+ ret = WD_RUNNING;
+ }
+ }
+ }
+
+ /* determine SERVICED */
+ if (p->devs[index].runstatus & WD_STAT_SVCD)
+ ret |= WD_SERVICED;
+
+ return ret;
+}
+
+static irqreturn_t cpwd_interrupt(int irq, void *dev_id)
+{
+ struct cpwd *p = dev_id;
+
+ /* Only WD0 will interrupt-- others are NMI and we won't
+ * see them here....
+ */
+ spin_lock_irq(&p->lock);
+
+ cpwd_stoptimer(p, WD0_ID);
+ p->devs[WD0_ID].runstatus |= WD_STAT_SVCD;
+
+ spin_unlock_irq(&p->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int cpwd_open(struct inode *inode, struct file *f)
+{
+ struct cpwd *p = cpwd_device;
+
+ mutex_lock(&cpwd_mutex);
+ switch (iminor(inode)) {
+ case WD0_MINOR:
+ case WD1_MINOR:
+ case WD2_MINOR:
+ break;
+
+ default:
+ mutex_unlock(&cpwd_mutex);
+ return -ENODEV;
+ }
+
+ /* Register IRQ on first open of device */
+ if (!p->initialized) {
+ if (request_irq(p->irq, &cpwd_interrupt,
+ IRQF_SHARED, DRIVER_NAME, p)) {
+ printk(KERN_ERR PFX "Cannot register IRQ %d\n",
+ p->irq);
+ mutex_unlock(&cpwd_mutex);
+ return -EBUSY;
+ }
+ p->initialized = true;
+ }
+
+ mutex_unlock(&cpwd_mutex);
+
+ return nonseekable_open(inode, f);
+}
+
+static int cpwd_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ static const struct watchdog_info info = {
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 1,
+ .identity = DRIVER_NAME,
+ };
+ void __user *argp = (void __user *)arg;
+ struct inode *inode = file->f_path.dentry->d_inode;
+ int index = iminor(inode) - WD0_MINOR;
+ struct cpwd *p = cpwd_device;
+ int setopt = 0;
+
+ switch (cmd) {
+ /* Generic Linux IOCTLs */
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &info, sizeof(struct watchdog_info)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int __user *)argp))
+ return -EFAULT;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ cpwd_pingtimer(p, index);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&setopt, argp, sizeof(unsigned int)))
+ return -EFAULT;
+
+ if (setopt & WDIOS_DISABLECARD) {
+ if (p->enabled)
+ return -EINVAL;
+ cpwd_stoptimer(p, index);
+ } else if (setopt & WDIOS_ENABLECARD) {
+ cpwd_starttimer(p, index);
+ } else {
+ return -EINVAL;
+ }
+ break;
+
+ /* Solaris-compatible IOCTLs */
+ case WIOCGSTAT:
+ setopt = cpwd_getstatus(p, index);
+ if (copy_to_user(argp, &setopt, sizeof(unsigned int)))
+ return -EFAULT;
+ break;
+
+ case WIOCSTART:
+ cpwd_starttimer(p, index);
+ break;
+
+ case WIOCSTOP:
+ if (p->enabled)
+ return -EINVAL;
+
+ cpwd_stoptimer(p, index);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rval = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ /* solaris ioctls are specific to this driver */
+ case WIOCSTART:
+ case WIOCSTOP:
+ case WIOCGSTAT:
+ mutex_lock(&cpwd_mutex);
+ rval = cpwd_ioctl(file, cmd, arg);
+ mutex_unlock(&cpwd_mutex);
+ break;
+
+ /* everything else is handled by the generic compat layer */
+ default:
+ break;
+ }
+
+ return rval;
+}
+
+static ssize_t cpwd_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct cpwd *p = cpwd_device;
+ int index = iminor(inode);
+
+ if (count) {
+ cpwd_pingtimer(p, index);
+ return 1;
+ }
+
+ return 0;
+}
+
+static ssize_t cpwd_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static const struct file_operations cpwd_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = cpwd_ioctl,
+ .compat_ioctl = cpwd_compat_ioctl,
+ .open = cpwd_open,
+ .write = cpwd_write,
+ .read = cpwd_read,
+ .release = cpwd_release,
+ .llseek = no_llseek,
+};
+
+static int __devinit cpwd_probe(struct platform_device *op)
+{
+ struct device_node *options;
+ const char *str_prop;
+ const void *prop_val;
+ int i, err = -EINVAL;
+ struct cpwd *p;
+
+ if (cpwd_device)
+ return -EINVAL;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ err = -ENOMEM;
+ if (!p) {
+ printk(KERN_ERR PFX "Unable to allocate struct cpwd.\n");
+ goto out;
+ }
+
+ p->irq = op->archdata.irqs[0];
+
+ spin_lock_init(&p->lock);
+
+ p->regs = of_ioremap(&op->resource[0], 0,
+ 4 * WD_TIMER_REGSZ, DRIVER_NAME);
+ if (!p->regs) {
+ printk(KERN_ERR PFX "Unable to map registers.\n");
+ goto out_free;
+ }
+
+ options = of_find_node_by_path("/options");
+ err = -ENODEV;
+ if (!options) {
+ printk(KERN_ERR PFX "Unable to find /options node.\n");
+ goto out_iounmap;
+ }
+
+ prop_val = of_get_property(options, "watchdog-enable?", NULL);
+ p->enabled = (prop_val ? true : false);
+
+ prop_val = of_get_property(options, "watchdog-reboot?", NULL);
+ p->reboot = (prop_val ? true : false);
+
+ str_prop = of_get_property(options, "watchdog-timeout", NULL);
+ if (str_prop)
+ p->timeout = simple_strtoul(str_prop, NULL, 10);
+
+ /* CP1400s seem to have broken PLD implementations-- the
+ * interrupt_mask register cannot be written, so no timer
+ * interrupts can be masked within the PLD.
+ */
+ str_prop = of_get_property(op->dev.of_node, "model", NULL);
+ p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));
+
+ if (!p->enabled)
+ cpwd_toggleintr(p, -1, WD_INTR_OFF);
+
+ for (i = 0; i < WD_NUMDEVS; i++) {
+ static const char *cpwd_names[] = { "RIC", "XIR", "POR" };
+ static int *parms[] = { &wd0_timeout,
+ &wd1_timeout,
+ &wd2_timeout };
+ struct miscdevice *mp = &p->devs[i].misc;
+
+ mp->minor = WD0_MINOR + i;
+ mp->name = cpwd_names[i];
+ mp->fops = &cpwd_fops;
+
+ p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ);
+ p->devs[i].intr_mask = (WD0_INTR_MASK << i);
+ p->devs[i].runstatus &= ~WD_STAT_BSTOP;
+ p->devs[i].runstatus |= WD_STAT_INIT;
+ p->devs[i].timeout = p->timeout;
+ if (*parms[i])
+ p->devs[i].timeout = *parms[i];
+
+ err = misc_register(&p->devs[i].misc);
+ if (err) {
+ printk(KERN_ERR "Could not register misc device for "
+ "dev %d\n", i);
+ goto out_unregister;
+ }
+ }
+
+ if (p->broken) {
+ init_timer(&cpwd_timer);
+ cpwd_timer.function = cpwd_brokentimer;
+ cpwd_timer.data = (unsigned long) p;
+ cpwd_timer.expires = WD_BTIMEOUT;
+
+ printk(KERN_INFO PFX "PLD defect workaround enabled for "
+ "model " WD_BADMODEL ".\n");
+ }
+
+ dev_set_drvdata(&op->dev, p);
+ cpwd_device = p;
+ err = 0;
+
+out:
+ return err;
+
+out_unregister:
+ for (i--; i >= 0; i--)
+ misc_deregister(&p->devs[i].misc);
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
+
+out_free:
+ kfree(p);
+ goto out;
+}
+
+static int __devexit cpwd_remove(struct platform_device *op)
+{
+ struct cpwd *p = dev_get_drvdata(&op->dev);
+ int i;
+
+ for (i = 0; i < WD_NUMDEVS; i++) {
+ misc_deregister(&p->devs[i].misc);
+
+ if (!p->enabled) {
+ cpwd_stoptimer(p, i);
+ if (p->devs[i].runstatus & WD_STAT_BSTOP)
+ cpwd_resetbrokentimer(p, i);
+ }
+ }
+
+ if (p->broken)
+ del_timer_sync(&cpwd_timer);
+
+ if (p->initialized)
+ free_irq(p->irq, p);
+
+ of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
+ kfree(p);
+
+ cpwd_device = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id cpwd_match[] = {
+ {
+ .name = "watchdog",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpwd_match);
+
+static struct platform_driver cpwd_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = cpwd_match,
+ },
+ .probe = cpwd_probe,
+ .remove = __devexit_p(cpwd_remove),
+};
+
+static int __init cpwd_init(void)
+{
+ return platform_driver_register(&cpwd_driver);
+}
+
+static void __exit cpwd_exit(void)
+{
+ platform_driver_unregister(&cpwd_driver);
+}
+
+module_init(cpwd_init);
+module_exit(cpwd_exit);
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
new file mode 100644
index 00000000..11e1d752
--- /dev/null
+++ b/drivers/watchdog/da9052_wdt.c
@@ -0,0 +1,542 @@
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/time.h>
+#include <linux/watchdog.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/wdt.h>
+
+#define DRIVER_NAME "da9052-wdt"
+
+#define DA9052_STROBING_FILTER_ENABLE 0x0001
+#define DA9052_STROBING_FILTER_DISABLE 0x0002
+#define DA9052_SET_STROBING_MODE_MANUAL 0x0004
+#define DA9052_SET_STROBING_MODE_AUTO 0x0008
+
+#define KERNEL_MODULE 0
+#define ENABLE 1
+#define DISABLE 0
+
+static u8 sm_strobe_filter_flag = DISABLE;
+static u8 sm_strobe_mode_flag = DA9052_STROBE_MANUAL;
+static u32 sm_mon_interval = DA9052_ADC_TWDMIN_TIME;
+static u8 sm_str_req = DISABLE;
+static u8 da9052_sm_scale = DA9052_WDT_DISABLE;
+module_param(sm_strobe_filter_flag, byte, 0);
+MODULE_PARM_DESC(sm_strobe_filter_flag,
+ "DA9052 SM driver strobe filter flag default = DISABLE");
+
+module_param(sm_strobe_mode_flag, byte, 0);
+MODULE_PARM_DESC(sm_strobe_mode_flag,
+ "DA9052 SM driver watchdog strobing mode default\
+ = DA9052_STROBE_MANUAL");
+
+module_param(da9052_sm_scale, byte, 0);
+MODULE_PARM_DESC(da9052_sm_scale,
+ "DA9052 SM driver scaling value used to calculate the\
+ time for the strobing filter default = 0");
+
+module_param(sm_str_req, byte, 0);
+MODULE_PARM_DESC(sm_str_req,
+ "DA9052 SM driver strobe request flag default = DISABLE");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static struct timer_list *monitoring_timer;
+
+struct da9052_wdt {
+ struct platform_device *pdev;
+ struct da9052 *da9052;
+};
+static struct miscdevice da9052_wdt_miscdev;
+static unsigned long da9052_wdt_users;
+static int da9052_wdt_expect_close;
+
+static struct da9052_wdt *get_wdt_da9052(void)
+{
+ /*return dev_get_drvdata(da9052_wdt_miscdev.parent);*/
+ return platform_get_drvdata
+ (to_platform_device(da9052_wdt_miscdev.parent));
+}
+
+void start_strobing(struct work_struct *work)
+{
+ struct da9052_ssc_msg msg;
+ int ret;
+ struct da9052_wdt *wdt = get_wdt_da9052();
+
+
+ if (NULL == wdt) {
+ mod_timer(monitoring_timer, jiffies + sm_mon_interval);
+ return;
+ }
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret) {
+ da9052_unlock(wdt->da9052);
+ return;
+ }
+ da9052_unlock(wdt->da9052);
+
+ msg.data = (msg.data | DA9052_CONTROLD_WATCHDOG);
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret) {
+ da9052_unlock(wdt->da9052);
+ return;
+ }
+ da9052_unlock(wdt->da9052);
+
+ sm_str_req = DISABLE;
+
+ mod_timer(monitoring_timer, jiffies + sm_mon_interval);
+ return;
+}
+
+
+void timer_callback(void)
+{
+ if (((sm_strobe_mode_flag) &&
+ (sm_strobe_mode_flag == DA9052_STROBE_MANUAL)) ||
+ (sm_strobe_mode_flag == DA9052_STROBE_AUTO)) {
+ schedule_work(&strobing_action);
+ } else {
+ if (sm_strobe_mode_flag == DA9052_STROBE_MANUAL) {
+ mod_timer(monitoring_timer, jiffies +
+ sm_mon_interval);
+ }
+ }
+}
+
+static int da9052_sm_hw_init(struct da9052_wdt *wdt)
+{
+ /* Create timer structure */
+ monitoring_timer = kzalloc(sizeof(struct timer_list), GFP_KERNEL);
+ if (!monitoring_timer)
+ return -ENOMEM;
+
+ init_timer(monitoring_timer);
+ monitoring_timer->expires = jiffies + sm_mon_interval;
+ monitoring_timer->function = (void *)&timer_callback;
+
+ sm_strobe_filter_flag = DA9052_SM_STROBE_CONF;
+ sm_strobe_mode_flag = DA9052_STROBE_MANUAL;
+
+ return 0;
+}
+
+static int da9052_sm_hw_deinit(struct da9052_wdt *wdt)
+{
+ struct da9052_ssc_msg msg;
+ int ret;
+
+ if (monitoring_timer != NULL)
+ del_timer(monitoring_timer);
+ kfree(monitoring_timer);
+
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ msg.data = (msg.data & ~(DA9052_CONTROLD_TWDSCALE));
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ return 0;
+ssc_err:
+ da9052_unlock(wdt->da9052);
+ return -EIO;
+}
+
+ s32 da9052_sm_set_strobing_filter(struct da9052_wdt *wdt,
+ u8 strobing_filter_state)
+ {
+ struct da9052_ssc_msg msg;
+ int ret = 0;
+
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ msg.data = (msg.data & DA9052_CONTROLD_TWDSCALE);
+
+ if (strobing_filter_state == ENABLE) {
+ sm_strobe_filter_flag = ENABLE;
+ if (DA9052_WDT_DISABLE == msg.data) {
+ sm_str_req = DISABLE;
+ del_timer(monitoring_timer);
+ return 0;
+ }
+ if (DA9052_SCALE_64X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X64_WINDOW);
+ else if (DA9052_SCALE_32X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X32_WINDOW);
+ else if (DA9052_SCALE_16X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X16_WINDOW);
+ else if (DA9052_SCALE_8X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X8_WINDOW);
+ else if (DA9052_SCALE_4X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X4_WINDOW);
+ else if (DA9052_SCALE_2X == msg.data)
+ sm_mon_interval = msecs_to_jiffies(DA9052_X2_WINDOW);
+ else
+ sm_mon_interval = msecs_to_jiffies(DA9052_X1_WINDOW);
+
+ } else if (strobing_filter_state == DISABLE) {
+ sm_strobe_filter_flag = DISABLE;
+ sm_mon_interval = msecs_to_jiffies(DA9052_ADC_TWDMIN_TIME);
+ if (DA9052_WDT_DISABLE == msg.data) {
+ sm_str_req = DISABLE;
+ del_timer(monitoring_timer);
+ return 0;
+ }
+ } else {
+ return STROBING_FILTER_ERROR;
+ }
+ mod_timer(monitoring_timer, jiffies + sm_mon_interval);
+
+ return 0;
+ssc_err:
+ da9052_unlock(wdt->da9052);
+ return -EIO;
+}
+
+int da9052_sm_set_strobing_mode(u8 strobing_mode_state)
+{
+ if (strobing_mode_state == DA9052_STROBE_AUTO)
+ sm_strobe_mode_flag = DA9052_STROBE_AUTO;
+ else if (strobing_mode_state == DA9052_STROBE_MANUAL)
+ sm_strobe_mode_flag = DA9052_STROBE_MANUAL;
+ else
+ return STROBING_MODE_ERROR;
+
+ return 0;
+}
+
+int da9052_sm_strobe_wdt(void)
+{
+ sm_str_req = ENABLE;
+ return 0;
+}
+
+ s32 da9052_sm_set_wdt(struct da9052_wdt *wdt, u8 wdt_scaling)
+{
+ struct da9052_ssc_msg msg;
+ int ret = 0;
+
+
+ if (wdt_scaling > DA9052_SCALE_64X)
+ return INVALID_SCALING_VALUE;
+
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ if (!((DA9052_WDT_DISABLE == (msg.data & DA9052_CONTROLD_TWDSCALE)) &&
+ (DA9052_WDT_DISABLE == wdt_scaling))) {
+ msg.data = (msg.data & ~(DA9052_CONTROLD_TWDSCALE));
+ msg.addr = DA9052_CONTROLD_REG;
+
+
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ msleep(1);
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+
+ msg.data |= wdt_scaling;
+
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret)
+ goto ssc_err;
+ da9052_unlock(wdt->da9052);
+
+ sm_str_req = DISABLE;
+ if (DA9052_WDT_DISABLE == wdt_scaling) {
+ del_timer(monitoring_timer);
+ return 0;
+ }
+ if (sm_strobe_filter_flag == ENABLE) {
+ if (DA9052_SCALE_64X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X64_WINDOW);
+ } else if (DA9052_SCALE_32X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X32_WINDOW);
+ } else if (DA9052_SCALE_16X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X16_WINDOW);
+ } else if (DA9052_SCALE_8X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X8_WINDOW);
+ } else if (DA9052_SCALE_4X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X4_WINDOW);
+ } else if (DA9052_SCALE_2X == wdt_scaling) {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X2_WINDOW);
+ } else {
+ sm_mon_interval =
+ msecs_to_jiffies(DA9052_X1_WINDOW);
+ }
+ } else {
+ sm_mon_interval = msecs_to_jiffies(
+ DA9052_ADC_TWDMIN_TIME);
+ }
+ mod_timer(monitoring_timer, jiffies + sm_mon_interval);
+ }
+
+ return 0;
+ssc_err:
+ da9052_unlock(wdt->da9052);
+ return -EIO;
+}
+
+static int da9052_wdt_open(struct inode *inode, struct file *file)
+{
+ struct da9052_wdt *wdt = get_wdt_da9052();
+ int ret;
+ printk(KERN_INFO"IN WDT OPEN \n");
+
+ if (!wdt) {
+ printk(KERN_INFO"Returning no device\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO"IN WDT OPEN 1\n");
+
+ if (test_and_set_bit(0, &da9052_wdt_users))
+ return -EBUSY;
+
+ ret = da9052_sm_hw_init(wdt);
+ if (ret != 0) {
+ printk(KERN_ERR "Watchdog hw init failed\n");
+ return ret;
+ }
+
+ return nonseekable_open(inode, file);
+}
+
+static int da9052_wdt_release(struct inode *inode, struct file *file)
+{
+ struct da9052_wdt *wdt = get_wdt_da9052();
+
+ if (da9052_wdt_expect_close == 42)
+ da9052_sm_hw_deinit(wdt);
+ else
+ da9052_sm_strobe_wdt();
+ da9052_wdt_expect_close = 0;
+ clear_bit(0, &da9052_wdt_users);
+ return 0;
+}
+
+static ssize_t da9052_wdt_write(struct file *file,
+ const char __user *data, size_t count,
+ loff_t *ppos)
+{
+ size_t i;
+
+ if (count) {
+ if (!nowayout) {
+ /* In case it was set long ago */
+ da9052_wdt_expect_close = 0;
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ da9052_wdt_expect_close = 42;
+ }
+ }
+ da9052_sm_strobe_wdt();
+ }
+ return count;
+}
+
+static struct watchdog_info da9052_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "DA9052_SM Watchdog",
+};
+
+static long da9052_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct da9052_wdt *wdt = get_wdt_da9052();
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ unsigned char new_value;
+
+ switch (cmd) {
+
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &da9052_wdt_info,
+ sizeof(da9052_wdt_info)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ if (new_value & DA9052_STROBING_FILTER_ENABLE)
+ da9052_sm_set_strobing_filter(wdt, ENABLE);
+ if (new_value & DA9052_STROBING_FILTER_DISABLE)
+ da9052_sm_set_strobing_filter(wdt, DISABLE);
+ if (new_value & DA9052_SET_STROBING_MODE_MANUAL)
+ da9052_sm_set_strobing_mode(DA9052_STROBE_MANUAL);
+ if (new_value & DA9052_SET_STROBING_MODE_AUTO)
+ da9052_sm_set_strobing_mode(DA9052_STROBE_AUTO);
+ return 0;
+ case WDIOC_KEEPALIVE:
+ if (da9052_sm_strobe_wdt())
+ return -EFAULT;
+ else
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ da9052_sm_scale = new_value;
+ if (da9052_sm_set_wdt(wdt, da9052_sm_scale))
+ return -EFAULT;
+ case WDIOC_GETTIMEOUT:
+ return put_user(sm_mon_interval, p);
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static const struct file_operations da9052_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = da9052_wdt_ioctl,
+ .write = da9052_wdt_write,
+ .open = da9052_wdt_open,
+ .release = da9052_wdt_release,
+};
+
+static struct miscdevice da9052_wdt_miscdev = {
+ .minor = 255,
+ .name = "da9052-wdt",
+ .fops = &da9052_wdt_fops,
+};
+
+static int __devinit da9052_sm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct da9052_wdt *wdt;
+ struct da9052_ssc_msg msg;
+
+ wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->da9052 = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, wdt);
+
+ msg.addr = DA9052_CONTROLD_REG;
+ msg.data = 0;
+
+ da9052_lock(wdt->da9052);
+ ret = wdt->da9052->read(wdt->da9052, &msg);
+ if (ret) {
+ da9052_unlock(wdt->da9052);
+ goto err_ssc_comm;
+ }
+ printk(KERN_INFO"DA9052 SM probe - 0 \n");
+
+ msg.data = (msg.data & ~(DA9052_CONTROLD_TWDSCALE));
+ ret = wdt->da9052->write(wdt->da9052, &msg);
+ if (ret) {
+ da9052_unlock(wdt->da9052);
+ goto err_ssc_comm;
+ }
+ da9052_unlock(wdt->da9052);
+
+ da9052_wdt_miscdev.parent = &pdev->dev;
+
+ ret = misc_register(&da9052_wdt_miscdev);
+ if (ret != 0) {
+ platform_set_drvdata(pdev, NULL);
+ kfree(wdt);
+ return -EFAULT;
+ }
+ return 0;
+err_ssc_comm:
+ platform_set_drvdata(pdev, NULL);
+ kfree(wdt);
+ return -EIO;
+}
+
+static int __devexit da9052_sm_remove(struct platform_device *dev)
+{
+ misc_deregister(&da9052_wdt_miscdev);
+
+ return 0;
+}
+
+static struct platform_driver da9052_sm_driver = {
+ .probe = da9052_sm_probe,
+ .remove = __devexit_p(da9052_sm_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init da9052_sm_init(void)
+{
+ return platform_driver_register(&da9052_sm_driver);
+}
+module_init(da9052_sm_init);
+
+static void __exit da9052_sm_exit(void)
+{
+ platform_driver_unregister(&da9052_sm_driver);
+}
+module_exit(da9052_sm_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>")
+MODULE_DESCRIPTION("DA9052 SM Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
new file mode 100644
index 00000000..51b5551b
--- /dev/null
+++ b/drivers/watchdog/davinci_wdt.c
@@ -0,0 +1,298 @@
+/*
+ * drivers/char/watchdog/davinci_wdt.c
+ *
+ * Watchdog driver for DaVinci DM644x/DM646x processors
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ *
+ * 2007 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#define MODULE_NAME "DAVINCI-WDT: "
+
+#define DEFAULT_HEARTBEAT 60
+#define MAX_HEARTBEAT 600 /* really the max margin is 264/27MHz*/
+
+/* Timer register set definition */
+#define PID12 (0x0)
+#define EMUMGT (0x4)
+#define TIM12 (0x10)
+#define TIM34 (0x14)
+#define PRD12 (0x18)
+#define PRD34 (0x1C)
+#define TCR (0x20)
+#define TGCR (0x24)
+#define WDTCR (0x28)
+
+/* TCR bit definitions */
+#define ENAMODE12_DISABLED (0 << 6)
+#define ENAMODE12_ONESHOT (1 << 6)
+#define ENAMODE12_PERIODIC (2 << 6)
+
+/* TGCR bit definitions */
+#define TIM12RS_UNRESET (1 << 0)
+#define TIM34RS_UNRESET (1 << 1)
+#define TIMMODE_64BIT_WDOG (2 << 2)
+
+/* WDTCR bit definitions */
+#define WDEN (1 << 14)
+#define WDFLAG (1 << 15)
+#define WDKEY_SEQ0 (0xa5c6 << 16)
+#define WDKEY_SEQ1 (0xda7e << 16)
+
+static int heartbeat = DEFAULT_HEARTBEAT;
+
+static DEFINE_SPINLOCK(io_lock);
+static unsigned long wdt_status;
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+#define WDT_REGION_INITED 2
+#define WDT_DEVICE_INITED 3
+
+static struct resource *wdt_mem;
+static void __iomem *wdt_base;
+struct clk *wdt_clk;
+
+static void wdt_service(void)
+{
+ spin_lock(&io_lock);
+
+ /* put watchdog in service state */
+ iowrite32(WDKEY_SEQ0, wdt_base + WDTCR);
+ /* put watchdog in active state */
+ iowrite32(WDKEY_SEQ1, wdt_base + WDTCR);
+
+ spin_unlock(&io_lock);
+}
+
+static void wdt_enable(void)
+{
+ u32 tgcr;
+ u32 timer_margin;
+ unsigned long wdt_freq;
+
+ wdt_freq = clk_get_rate(wdt_clk);
+
+ spin_lock(&io_lock);
+
+ /* disable, internal clock source */
+ iowrite32(0, wdt_base + TCR);
+ /* reset timer, set mode to 64-bit watchdog, and unreset */
+ iowrite32(0, wdt_base + TGCR);
+ tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
+ iowrite32(tgcr, wdt_base + TGCR);
+ /* clear counter regs */
+ iowrite32(0, wdt_base + TIM12);
+ iowrite32(0, wdt_base + TIM34);
+ /* set timeout period */
+ timer_margin = (((u64)heartbeat * wdt_freq) & 0xffffffff);
+ iowrite32(timer_margin, wdt_base + PRD12);
+ timer_margin = (((u64)heartbeat * wdt_freq) >> 32);
+ iowrite32(timer_margin, wdt_base + PRD34);
+ /* enable run continuously */
+ iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR);
+ /* Once the WDT is in pre-active state write to
+ * TIM12, TIM34, PRD12, PRD34, TCR, TGCR, WDTCR are
+ * write protected (except for the WDKEY field)
+ */
+ /* put watchdog in pre-active state */
+ iowrite32(WDKEY_SEQ0 | WDEN, wdt_base + WDTCR);
+ /* put watchdog in active state */
+ iowrite32(WDKEY_SEQ1 | WDEN, wdt_base + WDTCR);
+
+ spin_unlock(&io_lock);
+}
+
+static int davinci_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t
+davinci_wdt_write(struct file *file, const char *data, size_t len,
+ loff_t *ppos)
+{
+ if (len)
+ wdt_service();
+
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING,
+ .identity = "DaVinci Watchdog",
+};
+
+static long davinci_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_service();
+ ret = 0;
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int davinci_wdt_release(struct inode *inode, struct file *file)
+{
+ wdt_service();
+ clear_bit(WDT_IN_USE, &wdt_status);
+
+ return 0;
+}
+
+static const struct file_operations davinci_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = davinci_wdt_write,
+ .unlocked_ioctl = davinci_wdt_ioctl,
+ .open = davinci_wdt_open,
+ .release = davinci_wdt_release,
+};
+
+static struct miscdevice davinci_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &davinci_wdt_fops,
+};
+
+static int __devinit davinci_wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0, size;
+ struct device *dev = &pdev->dev;
+
+ wdt_clk = clk_get(dev, NULL);
+ if (WARN_ON(IS_ERR(wdt_clk)))
+ return PTR_ERR(wdt_clk);
+
+ clk_enable(wdt_clk);
+
+ if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
+ heartbeat = DEFAULT_HEARTBEAT;
+
+ dev_info(dev, "heartbeat %d sec\n", heartbeat);
+
+ wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (wdt_mem == NULL) {
+ dev_err(dev, "failed to get memory region resource\n");
+ return -ENOENT;
+ }
+
+ size = resource_size(wdt_mem);
+ if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
+ dev_err(dev, "failed to get memory region\n");
+ return -ENOENT;
+ }
+
+ wdt_base = ioremap(wdt_mem->start, size);
+ if (!wdt_base) {
+ dev_err(dev, "failed to map memory region\n");
+ release_mem_region(wdt_mem->start, size);
+ wdt_mem = NULL;
+ return -ENOMEM;
+ }
+
+ ret = misc_register(&davinci_wdt_miscdev);
+ if (ret < 0) {
+ dev_err(dev, "cannot register misc device\n");
+ release_mem_region(wdt_mem->start, size);
+ wdt_mem = NULL;
+ } else {
+ set_bit(WDT_DEVICE_INITED, &wdt_status);
+ }
+
+ iounmap(wdt_base);
+ return ret;
+}
+
+static int __devexit davinci_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&davinci_wdt_miscdev);
+ if (wdt_mem) {
+ release_mem_region(wdt_mem->start, resource_size(wdt_mem));
+ wdt_mem = NULL;
+ }
+
+ clk_disable(wdt_clk);
+ clk_put(wdt_clk);
+
+ return 0;
+}
+
+static struct platform_driver platform_wdt_driver = {
+ .driver = {
+ .name = "watchdog",
+ .owner = THIS_MODULE,
+ },
+ .probe = davinci_wdt_probe,
+ .remove = __devexit_p(davinci_wdt_remove),
+};
+
+static int __init davinci_wdt_init(void)
+{
+ return platform_driver_register(&platform_wdt_driver);
+}
+
+static void __exit davinci_wdt_exit(void)
+{
+ platform_driver_unregister(&platform_wdt_driver);
+}
+
+module_init(davinci_wdt_init);
+module_exit(davinci_wdt_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("DaVinci Watchdog Driver");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat period in seconds from 1 to "
+ __MODULE_STRING(MAX_HEARTBEAT) ", default "
+ __MODULE_STRING(DEFAULT_HEARTBEAT));
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:watchdog");
diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c
new file mode 100644
index 00000000..726b7df6
--- /dev/null
+++ b/drivers/watchdog/ep93xx_wdt.c
@@ -0,0 +1,255 @@
+/*
+ * Watchdog driver for Cirrus Logic EP93xx family of devices.
+ *
+ * Copyright (c) 2004 Ray Lehtiniemi
+ * Copyright (c) 2006 Tower Technologies
+ * Based on ep93xx driver, bits from alim7101_wdt.c
+ *
+ * Authors: Ray Lehtiniemi <rayl@mail.com>,
+ * Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * This watchdog fires after 250msec, which is a too short interval
+ * for us to rely on the user space daemon alone. So we ping the
+ * wdt each ~200msec and eventually stop doing it if the user space
+ * daemon dies.
+ *
+ * TODO:
+ *
+ * - Test last reset from watchdog status
+ * - Add a few missing ioctls
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+
+#define WDT_VERSION "0.3"
+#define PFX "ep93xx_wdt: "
+
+/* default timeout (secs) */
+#define WDT_TIMEOUT 30
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static int timeout = WDT_TIMEOUT;
+
+static struct timer_list timer;
+static unsigned long next_heartbeat;
+static unsigned long wdt_status;
+static unsigned long boot_status;
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+#define EP93XX_WDT_REG(x) (EP93XX_WATCHDOG_BASE + (x))
+#define EP93XX_WDT_WATCHDOG EP93XX_WDT_REG(0x00)
+#define EP93XX_WDT_WDSTATUS EP93XX_WDT_REG(0x04)
+
+/* reset the wdt every ~200ms */
+#define WDT_INTERVAL (HZ/5)
+
+static void wdt_enable(void)
+{
+ __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG);
+}
+
+static void wdt_disable(void)
+{
+ __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG);
+}
+
+static inline void wdt_ping(void)
+{
+ __raw_writew(0x5555, EP93XX_WDT_WATCHDOG);
+}
+
+static void wdt_startup(void)
+{
+ next_heartbeat = jiffies + (timeout * HZ);
+
+ wdt_enable();
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+}
+
+static void wdt_shutdown(void)
+{
+ del_timer_sync(&timer);
+ wdt_disable();
+}
+
+static void wdt_keepalive(void)
+{
+ /* user land ping */
+ next_heartbeat = jiffies + (timeout * HZ);
+}
+
+static int ep93xx_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ wdt_startup();
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t
+ep93xx_wdt_write(struct file *file, const char __user *data, size_t len,
+ loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ else
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_keepalive();
+ }
+
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE,
+ .identity = "EP93xx Watchdog",
+};
+
+static long ep93xx_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int __user *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(boot_status, (int __user *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ ret = 0;
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ /* actually, it is 0.250 seconds.... */
+ ret = put_user(1, (int __user *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int ep93xx_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ wdt_shutdown();
+ else
+ printk(KERN_CRIT PFX
+ "Device closed unexpectedly - timer will not stop\n");
+
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+static const struct file_operations ep93xx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = ep93xx_wdt_write,
+ .unlocked_ioctl = ep93xx_wdt_ioctl,
+ .open = ep93xx_wdt_open,
+ .release = ep93xx_wdt_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice ep93xx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ep93xx_wdt_fops,
+};
+
+static void ep93xx_timer_ping(unsigned long data)
+{
+ if (time_before(jiffies, next_heartbeat))
+ wdt_ping();
+
+ /* Re-set the timer interval */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+}
+
+static int __init ep93xx_wdt_init(void)
+{
+ int err;
+
+ err = misc_register(&ep93xx_wdt_miscdev);
+
+ boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0;
+
+ printk(KERN_INFO PFX "EP93XX watchdog, driver version "
+ WDT_VERSION "%s\n",
+ (__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08)
+ ? " (nCS1 disable detected)" : "");
+
+ if (timeout < 1 || timeout > 3600) {
+ timeout = WDT_TIMEOUT;
+ printk(KERN_INFO PFX
+ "timeout value must be 1<=x<=3600, using %d\n",
+ timeout);
+ }
+
+ setup_timer(&timer, ep93xx_timer_ping, 1);
+ return err;
+}
+
+static void __exit ep93xx_wdt_exit(void)
+{
+ wdt_shutdown();
+ misc_deregister(&ep93xx_wdt_miscdev);
+}
+
+module_init(ep93xx_wdt_init);
+module_exit(ep93xx_wdt_exit);
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (1<=timeout<=3600, default="
+ __MODULE_STRING(WDT_TIMEOUT) ")");
+
+MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>,"
+ "Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("EP93xx Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(WDT_VERSION);
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c
new file mode 100644
index 00000000..f1d1da66
--- /dev/null
+++ b/drivers/watchdog/eurotechwdt.c
@@ -0,0 +1,485 @@
+/*
+ * Eurotech CPU-1220/1410/1420 on board WDT driver
+ *
+ * (c) Copyright 2001 Ascensit <support@ascensit.com>
+ * (c) Copyright 2001 Rodolfo Giometti <giometti@ascensit.com>
+ * (c) Copyright 2002 Rob Radez <rob@osinvestor.com>
+ *
+ * Based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>*
+ */
+
+/* Changelog:
+ *
+ * 2001 - Rodolfo Giometti
+ * Initial release
+ *
+ * 2002/04/25 - Rob Radez
+ * clean up #includes
+ * clean up locking
+ * make __setup param unique
+ * proper options in watchdog_info
+ * add WDIOC_GETSTATUS and WDIOC_SETOPTIONS ioctls
+ * add expect_close support
+ *
+ * 2002.05.30 - Joel Becker <joel.becker@oracle.com>
+ * Added Matt Domsch's nowayout module option.
+ */
+
+/*
+ * The eurotech CPU-1220/1410/1420's watchdog is a part
+ * of the on-board SUPER I/O device SMSC FDC 37B782.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+static unsigned long eurwdt_is_open;
+static int eurwdt_timeout;
+static char eur_expect_close;
+static spinlock_t eurwdt_lock;
+
+/*
+ * You must set these - there is no sane way to probe for this board.
+ */
+
+static int io = 0x3f0;
+static int irq = 10;
+static char *ev = "int";
+
+#define WDT_TIMEOUT 60 /* 1 minute */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Some symbolic names
+ */
+
+#define WDT_CTRL_REG 0x30
+#define WDT_OUTPIN_CFG 0xe2
+#define WDT_EVENT_INT 0x00
+#define WDT_EVENT_REBOOT 0x08
+#define WDT_UNIT_SEL 0xf1
+#define WDT_UNIT_SECS 0x80
+#define WDT_TIMEOUT_VAL 0xf2
+#define WDT_TIMER_CFG 0xf3
+
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "Eurotech WDT io port (default=0x3f0)");
+module_param(irq, int, 0);
+MODULE_PARM_DESC(irq, "Eurotech WDT irq (default=10)");
+module_param(ev, charp, 0);
+MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `int')");
+
+
+/*
+ * Programming support
+ */
+
+static inline void eurwdt_write_reg(u8 index, u8 data)
+{
+ outb(index, io);
+ outb(data, io+1);
+}
+
+static inline void eurwdt_lock_chip(void)
+{
+ outb(0xaa, io);
+}
+
+static inline void eurwdt_unlock_chip(void)
+{
+ outb(0x55, io);
+ eurwdt_write_reg(0x07, 0x08); /* set the logical device */
+}
+
+static inline void eurwdt_set_timeout(int timeout)
+{
+ eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout);
+}
+
+static inline void eurwdt_disable_timer(void)
+{
+ eurwdt_set_timeout(0);
+}
+
+static void eurwdt_activate_timer(void)
+{
+ eurwdt_disable_timer();
+ eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */
+ eurwdt_write_reg(WDT_OUTPIN_CFG,
+ !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT);
+
+ /* Setting interrupt line */
+ if (irq == 2 || irq > 15 || irq < 0) {
+ printk(KERN_ERR ": invalid irq number\n");
+ irq = 0; /* if invalid we disable interrupt */
+ }
+ if (irq == 0)
+ printk(KERN_INFO ": interrupt disabled\n");
+
+ eurwdt_write_reg(WDT_TIMER_CFG, irq << 4);
+
+ eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */
+ eurwdt_set_timeout(0); /* the default timeout */
+}
+
+
+/*
+ * Kernel methods.
+ */
+
+static irqreturn_t eurwdt_interrupt(int irq, void *dev_id)
+{
+ printk(KERN_CRIT "timeout WDT timeout\n");
+
+#ifdef ONLY_TESTING
+ printk(KERN_CRIT "Would Reboot.\n");
+#else
+ printk(KERN_CRIT "Initiating system reboot.\n");
+ emergency_restart();
+#endif
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * eurwdt_ping:
+ *
+ * Reload counter one with the watchdog timeout.
+ */
+
+static void eurwdt_ping(void)
+{
+ /* Write the watchdog default value */
+ eurwdt_set_timeout(eurwdt_timeout);
+}
+
+/**
+ * eurwdt_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write (unused as data does not matter here
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t eurwdt_write(struct file *file, const char __user *buf,
+size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ eur_expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ eur_expect_close = 42;
+ }
+ }
+ spin_lock(&eurwdt_lock);
+ eurwdt_ping(); /* the default timeout */
+ spin_unlock(&eurwdt_lock);
+ }
+ return count;
+}
+
+/**
+ * eurwdt_ioctl:
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
+ */
+
+static long eurwdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
+ | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "WDT Eurotech CPU-1220/1410",
+ };
+
+ int time;
+ int options, retval = -EINVAL;
+
+ 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(options, p))
+ return -EFAULT;
+ spin_lock(&eurwdt_lock);
+ if (options & WDIOS_DISABLECARD) {
+ eurwdt_disable_timer();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ eurwdt_activate_timer();
+ eurwdt_ping();
+ retval = 0;
+ }
+ spin_unlock(&eurwdt_lock);
+ return retval;
+
+ case WDIOC_KEEPALIVE:
+ spin_lock(&eurwdt_lock);
+ eurwdt_ping();
+ spin_unlock(&eurwdt_lock);
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (copy_from_user(&time, p, sizeof(int)))
+ return -EFAULT;
+
+ /* Sanity check */
+ if (time < 0 || time > 255)
+ return -EINVAL;
+
+ spin_lock(&eurwdt_lock);
+ eurwdt_timeout = time;
+ eurwdt_set_timeout(time);
+ spin_unlock(&eurwdt_lock);
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(eurwdt_timeout, p);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+/**
+ * eurwdt_open:
+ * @inode: inode of device
+ * @file: file handle to device
+ *
+ * The misc device has been opened. The watchdog device is single
+ * open and on opening we load the counter.
+ */
+
+static int eurwdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &eurwdt_is_open))
+ return -EBUSY;
+ eurwdt_timeout = WDT_TIMEOUT; /* initial timeout */
+ /* Activate the WDT */
+ eurwdt_activate_timer();
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * eurwdt_release:
+ * @inode: inode to board
+ * @file: file handle to board
+ *
+ * The watchdog has a configurable API. There is a religious dispute
+ * between people who want their watchdog to be able to shut down and
+ * those who want to be sure if the watchdog manager dies the machine
+ * reboots. In the former case we disable the counters, in the latter
+ * case you have to open it again very soon.
+ */
+
+static int eurwdt_release(struct inode *inode, struct file *file)
+{
+ if (eur_expect_close == 42)
+ eurwdt_disable_timer();
+ else {
+ printk(KERN_CRIT
+ "eurwdt: Unexpected close, not stopping watchdog!\n");
+ eurwdt_ping();
+ }
+ clear_bit(0, &eurwdt_is_open);
+ eur_expect_close = 0;
+ return 0;
+}
+
+/**
+ * eurwdt_notify_sys:
+ * @this: our notifier block
+ * @code: the event being reported
+ * @unused: unused
+ *
+ * Our notifier is called on system shutdowns. We want to turn the card
+ * off at reboot otherwise the machine will reboot again during memory
+ * test or worse yet during the following fsck. This would suck, in fact
+ * trust me - if it happens it does suck.
+ */
+
+static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ eurwdt_disable_timer(); /* Turn the card off */
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+
+static const struct file_operations eurwdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = eurwdt_write,
+ .unlocked_ioctl = eurwdt_ioctl,
+ .open = eurwdt_open,
+ .release = eurwdt_release,
+};
+
+static struct miscdevice eurwdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &eurwdt_fops,
+};
+
+/*
+ * The WDT card needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block eurwdt_notifier = {
+ .notifier_call = eurwdt_notify_sys,
+};
+
+/**
+ * cleanup_module:
+ *
+ * Unload the watchdog. You cannot do this with any file handles open.
+ * If your watchdog is set to continue ticking on close and you unload
+ * it, well it keeps ticking. We won't get the interrupt but the board
+ * will not touch PC memory so all is fine. You just have to load a new
+ * module in 60 seconds or reboot.
+ */
+
+static void __exit eurwdt_exit(void)
+{
+ eurwdt_lock_chip();
+
+ misc_deregister(&eurwdt_miscdev);
+
+ unregister_reboot_notifier(&eurwdt_notifier);
+ release_region(io, 2);
+ free_irq(irq, NULL);
+}
+
+/**
+ * eurwdt_init:
+ *
+ * Set up the WDT watchdog board. After grabbing the resources
+ * we require we need also to unlock the device.
+ * The open() function will actually kick the board off.
+ */
+
+static int __init eurwdt_init(void)
+{
+ int ret;
+
+ ret = request_irq(irq, eurwdt_interrupt, IRQF_DISABLED, "eurwdt", NULL);
+ if (ret) {
+ printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
+ goto out;
+ }
+
+ if (!request_region(io, 2, "eurwdt")) {
+ printk(KERN_ERR "eurwdt: IO %X is not free.\n", io);
+ ret = -EBUSY;
+ goto outirq;
+ }
+
+ ret = register_reboot_notifier(&eurwdt_notifier);
+ if (ret) {
+ printk(KERN_ERR
+ "eurwdt: can't register reboot notifier (err=%d)\n", ret);
+ goto outreg;
+ }
+
+ spin_lock_init(&eurwdt_lock);
+
+ ret = misc_register(&eurwdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n",
+ WATCHDOG_MINOR);
+ goto outreboot;
+ }
+
+ eurwdt_unlock_chip();
+
+ ret = 0;
+ printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
+ " - timeout event: %s\n",
+ io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
+
+out:
+ return ret;
+
+outreboot:
+ unregister_reboot_notifier(&eurwdt_notifier);
+
+outreg:
+ release_region(io, 2);
+
+outirq:
+ free_irq(irq, NULL);
+ goto out;
+}
+
+module_init(eurwdt_init);
+module_exit(eurwdt_exit);
+
+MODULE_AUTHOR("Rodolfo Giometti");
+MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
new file mode 100644
index 00000000..d4d8d1fd
--- /dev/null
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -0,0 +1,824 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> *
+ * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> *
+ * Copyright (C) 2010 Giel van Schijndel <me@mortis.eu> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define DRVNAME "f71808e_wdt"
+
+#define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical device */
+#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_DEVREV 0x22 /* Device revision */
+#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
+#define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */
+#define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */
+#define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */
+#define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+
+#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
+#define SIO_F71808_ID 0x0901 /* Chipset ID */
+#define SIO_F71858_ID 0x0507 /* Chipset ID */
+#define SIO_F71862_ID 0x0601 /* Chipset ID */
+#define SIO_F71869_ID 0x0814 /* Chipset ID */
+#define SIO_F71882_ID 0x0541 /* Chipset ID */
+#define SIO_F71889_ID 0x0723 /* Chipset ID */
+
+#define F71808FG_REG_WDO_CONF 0xf0
+#define F71808FG_REG_WDT_CONF 0xf5
+#define F71808FG_REG_WD_TIME 0xf6
+
+#define F71808FG_FLAG_WDOUT_EN 7
+
+#define F71808FG_FLAG_WDTMOUT_STS 5
+#define F71808FG_FLAG_WD_EN 5
+#define F71808FG_FLAG_WD_PULSE 4
+#define F71808FG_FLAG_WD_UNIT 3
+
+/* Default values */
+#define WATCHDOG_TIMEOUT 60 /* 1 minute default timeout */
+#define WATCHDOG_MAX_TIMEOUT (60 * 255)
+#define WATCHDOG_PULSE_WIDTH 125 /* 125 ms, default pulse width for
+ watchdog signal */
+#define WATCHDOG_F71862FG_PIN 63 /* default watchdog reset output
+ pin number 63 */
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static const int max_timeout = WATCHDOG_MAX_TIMEOUT;
+static int timeout = WATCHDOG_TIMEOUT; /* default timeout in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <="
+ __MODULE_STRING(WATCHDOG_MAX_TIMEOUT) " (default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
+module_param(pulse_width, uint, 0);
+MODULE_PARM_DESC(pulse_width,
+ "Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms"
+ " (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");
+
+static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN;
+module_param(f71862fg_pin, uint, 0);
+MODULE_PARM_DESC(f71862fg_pin,
+ "Watchdog f71862fg reset output pin configuration. Choose pin 56 or 63"
+ " (default=" __MODULE_STRING(WATCHDOG_F71862FG_PIN)")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0444);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+static unsigned int start_withtimeout;
+module_param(start_withtimeout, uint, 0);
+MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
+ " given initial timeout. Zero (default) disables this feature.");
+
+enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg };
+
+static const char *f71808e_names[] = {
+ "f71808fg",
+ "f71858fg",
+ "f71862fg",
+ "f71869",
+ "f71882fg",
+ "f71889fg",
+};
+
+/* Super-I/O Function prototypes */
+static inline int superio_inb(int base, int reg);
+static inline int superio_inw(int base, int reg);
+static inline void superio_outb(int base, int reg, u8 val);
+static inline void superio_set_bit(int base, int reg, int bit);
+static inline void superio_clear_bit(int base, int reg, int bit);
+static inline int superio_enter(int base);
+static inline void superio_select(int base, int ld);
+static inline void superio_exit(int base);
+
+struct watchdog_data {
+ unsigned short sioaddr;
+ enum chips type;
+ unsigned long opened;
+ struct mutex lock;
+ char expect_close;
+ struct watchdog_info ident;
+
+ unsigned short timeout;
+ u8 timer_val; /* content for the wd_time register */
+ char minutes_mode;
+ u8 pulse_val; /* pulse width flag */
+ char pulse_mode; /* enable pulse output mode? */
+ char caused_reboot; /* last reboot was by the watchdog */
+};
+
+static struct watchdog_data watchdog = {
+ .lock = __MUTEX_INITIALIZER(watchdog.lock),
+};
+
+/* Super I/O functions */
+static inline int superio_inb(int base, int reg)
+{
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static int superio_inw(int base, int reg)
+{
+ int val;
+ val = superio_inb(base, reg) << 8;
+ val |= superio_inb(base, reg + 1);
+ return val;
+}
+
+static inline void superio_outb(int base, int reg, u8 val)
+{
+ outb(reg, base);
+ outb(val, base + 1);
+}
+
+static inline void superio_set_bit(int base, int reg, int bit)
+{
+ unsigned long val = superio_inb(base, reg);
+ __set_bit(bit, &val);
+ superio_outb(base, reg, val);
+}
+
+static inline void superio_clear_bit(int base, int reg, int bit)
+{
+ unsigned long val = superio_inb(base, reg);
+ __clear_bit(bit, &val);
+ superio_outb(base, reg, val);
+}
+
+static inline int superio_enter(int base)
+{
+ /* Don't step on other drivers' I/O space by accident */
+ if (!request_muxed_region(base, 2, DRVNAME)) {
+ printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n",
+ (int)base);
+ return -EBUSY;
+ }
+
+ /* according to the datasheet the key must be send twice! */
+ outb(SIO_UNLOCK_KEY, base);
+ outb(SIO_UNLOCK_KEY, base);
+
+ return 0;
+}
+
+static inline void superio_select(int base, int ld)
+{
+ outb(SIO_REG_LDSEL, base);
+ outb(ld, base + 1);
+}
+
+static inline void superio_exit(int base)
+{
+ outb(SIO_LOCK_KEY, base);
+ release_region(base, 2);
+}
+
+static int watchdog_set_timeout(int timeout)
+{
+ if (timeout <= 0
+ || timeout > max_timeout) {
+ printk(KERN_ERR DRVNAME ": watchdog timeout out of range\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&watchdog.lock);
+
+ watchdog.timeout = timeout;
+ if (timeout > 0xff) {
+ watchdog.timer_val = DIV_ROUND_UP(timeout, 60);
+ watchdog.minutes_mode = true;
+ } else {
+ watchdog.timer_val = timeout;
+ watchdog.minutes_mode = false;
+ }
+
+ mutex_unlock(&watchdog.lock);
+
+ return 0;
+}
+
+static int watchdog_set_pulse_width(unsigned int pw)
+{
+ int err = 0;
+
+ mutex_lock(&watchdog.lock);
+
+ if (pw <= 1) {
+ watchdog.pulse_val = 0;
+ } else if (pw <= 25) {
+ watchdog.pulse_val = 1;
+ } else if (pw <= 125) {
+ watchdog.pulse_val = 2;
+ } else if (pw <= 5000) {
+ watchdog.pulse_val = 3;
+ } else {
+ printk(KERN_ERR DRVNAME ": pulse width out of range\n");
+ err = -EINVAL;
+ goto exit_unlock;
+ }
+
+ watchdog.pulse_mode = pw;
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+ return err;
+}
+
+static int watchdog_keepalive(void)
+{
+ int err = 0;
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(watchdog.sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ if (watchdog.minutes_mode)
+ /* select minutes for timer units */
+ superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+ else
+ /* select seconds for timer units */
+ superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+
+ /* Set timer value */
+ superio_outb(watchdog.sioaddr, F71808FG_REG_WD_TIME,
+ watchdog.timer_val);
+
+ superio_exit(watchdog.sioaddr);
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+ return err;
+}
+
+static int f71862fg_pin_configure(unsigned short ioaddr)
+{
+ /* When ioaddr is non-zero the calling function has to take care of
+ mutex handling and superio preparation! */
+
+ if (f71862fg_pin == 63) {
+ if (ioaddr) {
+ /* SPI must be disabled first to use this pin! */
+ superio_clear_bit(ioaddr, SIO_REG_ROM_ADDR_SEL, 6);
+ superio_set_bit(ioaddr, SIO_REG_MFUNCT3, 4);
+ }
+ } else if (f71862fg_pin == 56) {
+ if (ioaddr)
+ superio_set_bit(ioaddr, SIO_REG_MFUNCT1, 1);
+ } else {
+ printk(KERN_ERR DRVNAME ": Invalid argument f71862fg_pin=%d\n",
+ f71862fg_pin);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int watchdog_start(void)
+{
+ /* Make sure we don't die as soon as the watchdog is enabled below */
+ int err = watchdog_keepalive();
+ if (err)
+ return err;
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(watchdog.sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ /* Watchdog pin configuration */
+ switch (watchdog.type) {
+ case f71808fg:
+ /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
+ superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT2, 3);
+ superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 3);
+ break;
+
+ case f71862fg:
+ err = f71862fg_pin_configure(watchdog.sioaddr);
+ if (err)
+ goto exit_superio;
+ break;
+
+ case f71869:
+ /* GPIO14 --> WDTRST# */
+ superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4);
+ break;
+
+ case f71882fg:
+ /* Set pin 56 to WDTRST# */
+ superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1);
+ break;
+
+ case f71889fg:
+ /* set pin 40 to WDTRST# */
+ superio_outb(watchdog.sioaddr, SIO_REG_MFUNCT3,
+ superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf);
+ break;
+
+ default:
+ /*
+ * 'default' label to shut up the compiler and catch
+ * programmer errors
+ */
+ err = -ENODEV;
+ goto exit_superio;
+ }
+
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+ superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0);
+ superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF,
+ F71808FG_FLAG_WDOUT_EN);
+
+ superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_EN);
+
+ if (watchdog.pulse_mode) {
+ /* Select "pulse" output mode with given duration */
+ u8 wdt_conf = superio_inb(watchdog.sioaddr,
+ F71808FG_REG_WDT_CONF);
+
+ /* Set WD_PSWIDTH bits (1:0) */
+ wdt_conf = (wdt_conf & 0xfc) | (watchdog.pulse_val & 0x03);
+ /* Set WD_PULSE to "pulse" mode */
+ wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE);
+
+ superio_outb(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ wdt_conf);
+ } else {
+ /* Select "level" output mode */
+ superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_PULSE);
+ }
+
+exit_superio:
+ superio_exit(watchdog.sioaddr);
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+
+ return err;
+}
+
+static int watchdog_stop(void)
+{
+ int err = 0;
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(watchdog.sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_EN);
+
+ superio_exit(watchdog.sioaddr);
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+
+ return err;
+}
+
+static int watchdog_get_status(void)
+{
+ int status = 0;
+
+ mutex_lock(&watchdog.lock);
+ status = (watchdog.caused_reboot) ? WDIOF_CARDRESET : 0;
+ mutex_unlock(&watchdog.lock);
+
+ return status;
+}
+
+static bool watchdog_is_running(void)
+{
+ /*
+ * if we fail to determine the watchdog's status assume it to be
+ * running to be on the safe side
+ */
+ bool is_running = true;
+
+ mutex_lock(&watchdog.lock);
+ if (superio_enter(watchdog.sioaddr))
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0))
+ && (superio_inb(watchdog.sioaddr, F71808FG_REG_WDT_CONF)
+ & F71808FG_FLAG_WD_EN);
+
+ superio_exit(watchdog.sioaddr);
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+ return is_running;
+}
+
+/* /dev/watchdog api */
+
+static int watchdog_open(struct inode *inode, struct file *file)
+{
+ int err;
+
+ /* If the watchdog is alive we don't need to start it again */
+ if (test_and_set_bit(0, &watchdog.opened))
+ return -EBUSY;
+
+ err = watchdog_start();
+ if (err) {
+ clear_bit(0, &watchdog.opened);
+ return err;
+ }
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ watchdog.expect_close = 0;
+ return nonseekable_open(inode, file);
+}
+
+static int watchdog_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &watchdog.opened);
+
+ if (!watchdog.expect_close) {
+ watchdog_keepalive();
+ printk(KERN_CRIT DRVNAME
+ ": Unexpected close, not stopping watchdog!\n");
+ } else if (!nowayout) {
+ watchdog_stop();
+ }
+ return 0;
+}
+
+/*
+ * watchdog_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t watchdog_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ bool expect_close = false;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ expect_close = (c == 'V');
+ }
+
+ /* Properly order writes across fork()ed processes */
+ mutex_lock(&watchdog.lock);
+ watchdog.expect_close = expect_close;
+ mutex_unlock(&watchdog.lock);
+ }
+
+ /* someone wrote to us, we should restart timer */
+ watchdog_keepalive();
+ }
+ return count;
+}
+
+/*
+ * watchdog_ioctl:
+ * @inode: inode of the device
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
+ */
+static long watchdog_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int status;
+ int new_options;
+ int new_timeout;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident, &watchdog.ident,
+ sizeof(watchdog.ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ status = watchdog_get_status();
+ if (status < 0)
+ return status;
+ return put_user(status, uarg.i);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, uarg.i))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD)
+ watchdog_stop();
+
+ if (new_options & WDIOS_ENABLECARD)
+ return watchdog_start();
+
+
+ case WDIOC_KEEPALIVE:
+ watchdog_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+
+ if (watchdog_set_timeout(new_timeout))
+ return -EINVAL;
+
+ watchdog_keepalive();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(watchdog.timeout, uarg.i);
+
+ default:
+ return -ENOTTY;
+
+ }
+}
+
+static int watchdog_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ watchdog_stop();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations watchdog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = watchdog_open,
+ .release = watchdog_release,
+ .write = watchdog_write,
+ .unlocked_ioctl = watchdog_ioctl,
+};
+
+static struct miscdevice watchdog_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &watchdog_fops,
+};
+
+static struct notifier_block watchdog_notifier = {
+ .notifier_call = watchdog_notify_sys,
+};
+
+static int __init watchdog_init(int sioaddr)
+{
+ int wdt_conf, err = 0;
+
+ /* No need to lock watchdog.lock here because no entry points
+ * into the module have been registered yet.
+ */
+ watchdog.sioaddr = sioaddr;
+ watchdog.ident.options = WDIOC_SETTIMEOUT
+ | WDIOF_MAGICCLOSE
+ | WDIOF_KEEPALIVEPING;
+
+ snprintf(watchdog.ident.identity,
+ sizeof(watchdog.ident.identity), "%s watchdog",
+ f71808e_names[watchdog.type]);
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF);
+ watchdog.caused_reboot = wdt_conf & F71808FG_FLAG_WDTMOUT_STS;
+
+ superio_exit(sioaddr);
+
+ err = watchdog_set_timeout(timeout);
+ if (err)
+ return err;
+ err = watchdog_set_pulse_width(pulse_width);
+ if (err)
+ return err;
+
+ err = register_reboot_notifier(&watchdog_notifier);
+ if (err)
+ return err;
+
+ err = misc_register(&watchdog_miscdev);
+ if (err) {
+ printk(KERN_ERR DRVNAME
+ ": cannot register miscdev on minor=%d\n",
+ watchdog_miscdev.minor);
+ goto exit_reboot;
+ }
+
+ if (start_withtimeout) {
+ if (start_withtimeout <= 0
+ || start_withtimeout > max_timeout) {
+ printk(KERN_ERR DRVNAME
+ ": starting timeout out of range\n");
+ err = -EINVAL;
+ goto exit_miscdev;
+ }
+
+ err = watchdog_start();
+ if (err) {
+ printk(KERN_ERR DRVNAME
+ ": cannot start watchdog timer\n");
+ goto exit_miscdev;
+ }
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ if (start_withtimeout > 0xff) {
+ /* select minutes for timer units */
+ superio_set_bit(sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+ superio_outb(sioaddr, F71808FG_REG_WD_TIME,
+ DIV_ROUND_UP(start_withtimeout, 60));
+ } else {
+ /* select seconds for timer units */
+ superio_clear_bit(sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+ superio_outb(sioaddr, F71808FG_REG_WD_TIME,
+ start_withtimeout);
+ }
+
+ superio_exit(sioaddr);
+ mutex_unlock(&watchdog.lock);
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ printk(KERN_INFO DRVNAME
+ ": watchdog started with initial timeout of %u sec\n",
+ start_withtimeout);
+ }
+
+ return 0;
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+exit_miscdev:
+ misc_deregister(&watchdog_miscdev);
+exit_reboot:
+ unregister_reboot_notifier(&watchdog_notifier);
+
+ return err;
+}
+
+static int __init f71808e_find(int sioaddr)
+{
+ u16 devid;
+ int err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ devid = superio_inw(sioaddr, SIO_REG_MANID);
+ if (devid != SIO_FINTEK_ID) {
+ pr_debug(DRVNAME ": Not a Fintek device\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
+ switch (devid) {
+ case SIO_F71808_ID:
+ watchdog.type = f71808fg;
+ break;
+ case SIO_F71862_ID:
+ watchdog.type = f71862fg;
+ err = f71862fg_pin_configure(0); /* validate module parameter */
+ break;
+ case SIO_F71869_ID:
+ watchdog.type = f71869;
+ break;
+ case SIO_F71882_ID:
+ watchdog.type = f71882fg;
+ break;
+ case SIO_F71889_ID:
+ watchdog.type = f71889fg;
+ break;
+ case SIO_F71858_ID:
+ /* Confirmed (by datasheet) not to have a watchdog. */
+ err = -ENODEV;
+ goto exit;
+ default:
+ printk(KERN_INFO DRVNAME ": Unrecognized Fintek device: %04x\n",
+ (unsigned int)devid);
+ err = -ENODEV;
+ goto exit;
+ }
+
+ printk(KERN_INFO DRVNAME ": Found %s watchdog chip, revision %d\n",
+ f71808e_names[watchdog.type],
+ (int)superio_inb(sioaddr, SIO_REG_DEVREV));
+exit:
+ superio_exit(sioaddr);
+ return err;
+}
+
+static int __init f71808e_init(void)
+{
+ static const unsigned short addrs[] = { 0x2e, 0x4e };
+ int err = -ENODEV;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(addrs); i++) {
+ err = f71808e_find(addrs[i]);
+ if (err == 0)
+ break;
+ }
+ if (i == ARRAY_SIZE(addrs))
+ return err;
+
+ return watchdog_init(addrs[i]);
+}
+
+static void __exit f71808e_exit(void)
+{
+ if (watchdog_is_running()) {
+ printk(KERN_WARNING DRVNAME
+ ": Watchdog timer still running, stopping it\n");
+ watchdog_stop();
+ }
+ misc_deregister(&watchdog_miscdev);
+ unregister_reboot_notifier(&watchdog_notifier);
+}
+
+MODULE_DESCRIPTION("F71808E Watchdog Driver");
+MODULE_AUTHOR("Giel van Schijndel <me@mortis.eu>");
+MODULE_LICENSE("GPL");
+
+module_init(f71808e_init);
+module_exit(f71808e_exit);
diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c
new file mode 100644
index 00000000..b146082b
--- /dev/null
+++ b/drivers/watchdog/gef_wdt.c
@@ -0,0 +1,332 @@
+/*
+ * GE watchdog userspace interface
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ *
+ * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Based on: mv64x60_wdt.c (MV64X60 watchdog userspace interface)
+ * Author: James Chapman <jchapman@katalix.com>
+ */
+
+/* TODO:
+ * This driver does not provide support for the hardwares capability of sending
+ * an interrupt at a programmable threshold.
+ *
+ * This driver currently can only support 1 watchdog - there are 2 in the
+ * hardware that this driver supports. Thus one could be configured as a
+ * process-based watchdog (via /dev/watchdog), the second (using the interrupt
+ * capabilities) a kernel-based watchdog.
+ */
+
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <sysdev/fsl_soc.h>
+
+/*
+ * The watchdog configuration register contains a pair of 2-bit fields,
+ * 1. a reload field, bits 27-26, which triggers a reload of
+ * the countdown register, and
+ * 2. an enable field, bits 25-24, which toggles between
+ * enabling and disabling the watchdog timer.
+ * Bit 31 is a read-only field which indicates whether the
+ * watchdog timer is currently enabled.
+ *
+ * The low 24 bits contain the timer reload value.
+ */
+#define GEF_WDC_ENABLE_SHIFT 24
+#define GEF_WDC_SERVICE_SHIFT 26
+#define GEF_WDC_ENABLED_SHIFT 31
+
+#define GEF_WDC_ENABLED_TRUE 1
+#define GEF_WDC_ENABLED_FALSE 0
+
+/* Flags bits */
+#define GEF_WDOG_FLAG_OPENED 0
+
+static unsigned long wdt_flags;
+static int wdt_status;
+static void __iomem *gef_wdt_regs;
+static int gef_wdt_timeout;
+static int gef_wdt_count;
+static unsigned int bus_clk;
+static char expect_close;
+static DEFINE_SPINLOCK(gef_wdt_spinlock);
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+
+static int gef_wdt_toggle_wdc(int enabled_predicate, int field_shift)
+{
+ u32 data;
+ u32 enabled;
+ int ret = 0;
+
+ spin_lock(&gef_wdt_spinlock);
+ data = ioread32be(gef_wdt_regs);
+ enabled = (data >> GEF_WDC_ENABLED_SHIFT) & 1;
+
+ /* only toggle the requested field if enabled state matches predicate */
+ if ((enabled ^ enabled_predicate) == 0) {
+ /* We write a 1, then a 2 -- to the appropriate field */
+ data = (1 << field_shift) | gef_wdt_count;
+ iowrite32be(data, gef_wdt_regs);
+
+ data = (2 << field_shift) | gef_wdt_count;
+ iowrite32be(data, gef_wdt_regs);
+ ret = 1;
+ }
+ spin_unlock(&gef_wdt_spinlock);
+
+ return ret;
+}
+
+static void gef_wdt_service(void)
+{
+ gef_wdt_toggle_wdc(GEF_WDC_ENABLED_TRUE,
+ GEF_WDC_SERVICE_SHIFT);
+}
+
+static void gef_wdt_handler_enable(void)
+{
+ if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_FALSE,
+ GEF_WDC_ENABLE_SHIFT)) {
+ gef_wdt_service();
+ printk(KERN_NOTICE "gef_wdt: watchdog activated\n");
+ }
+}
+
+static void gef_wdt_handler_disable(void)
+{
+ if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_TRUE,
+ GEF_WDC_ENABLE_SHIFT))
+ printk(KERN_NOTICE "gef_wdt: watchdog deactivated\n");
+}
+
+static void gef_wdt_set_timeout(unsigned int timeout)
+{
+ /* maximum bus cycle count is 0xFFFFFFFF */
+ if (timeout > 0xFFFFFFFF / bus_clk)
+ timeout = 0xFFFFFFFF / bus_clk;
+
+ /* Register only holds upper 24 bits, bit shifted into lower 24 */
+ gef_wdt_count = (timeout * bus_clk) >> 8;
+ gef_wdt_timeout = timeout;
+}
+
+
+static ssize_t gef_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ gef_wdt_service();
+ }
+
+ return len;
+}
+
+static long gef_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int timeout;
+ int options;
+ void __user *argp = (void __user *)arg;
+ static const struct watchdog_info info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .firmware_version = 0,
+ .identity = "GE watchdog",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(wdt_status, (int __user *)argp))
+ return -EFAULT;
+ wdt_status &= ~WDIOF_KEEPALIVEPING;
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(options, (int __user *)argp))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD)
+ gef_wdt_handler_disable();
+
+ if (options & WDIOS_ENABLECARD)
+ gef_wdt_handler_enable();
+ break;
+
+ case WDIOC_KEEPALIVE:
+ gef_wdt_service();
+ wdt_status |= WDIOF_KEEPALIVEPING;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(timeout, (int __user *)argp))
+ return -EFAULT;
+ gef_wdt_set_timeout(timeout);
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ if (put_user(gef_wdt_timeout, (int __user *)argp))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static int gef_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(GEF_WDOG_FLAG_OPENED, &wdt_flags))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ gef_wdt_handler_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static int gef_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ gef_wdt_handler_disable();
+ else {
+ printk(KERN_CRIT
+ "gef_wdt: unexpected close, not stopping timer!\n");
+ gef_wdt_service();
+ }
+ expect_close = 0;
+
+ clear_bit(GEF_WDOG_FLAG_OPENED, &wdt_flags);
+
+ return 0;
+}
+
+static const struct file_operations gef_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = gef_wdt_write,
+ .unlocked_ioctl = gef_wdt_ioctl,
+ .open = gef_wdt_open,
+ .release = gef_wdt_release,
+};
+
+static struct miscdevice gef_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &gef_wdt_fops,
+};
+
+
+static int __devinit gef_wdt_probe(struct platform_device *dev)
+{
+ int timeout = 10;
+ u32 freq;
+
+ bus_clk = 133; /* in MHz */
+
+ freq = fsl_get_sys_freq();
+ if (freq != -1)
+ bus_clk = freq;
+
+ /* Map devices registers into memory */
+ gef_wdt_regs = of_iomap(dev->dev.of_node, 0);
+ if (gef_wdt_regs == NULL)
+ return -ENOMEM;
+
+ gef_wdt_set_timeout(timeout);
+
+ gef_wdt_handler_disable(); /* in case timer was already running */
+
+ return misc_register(&gef_wdt_miscdev);
+}
+
+static int __devexit gef_wdt_remove(struct platform_device *dev)
+{
+ misc_deregister(&gef_wdt_miscdev);
+
+ gef_wdt_handler_disable();
+
+ iounmap(gef_wdt_regs);
+
+ return 0;
+}
+
+static const struct of_device_id gef_wdt_ids[] = {
+ {
+ .compatible = "gef,fpga-wdt",
+ },
+ {},
+};
+
+static struct platform_driver gef_wdt_driver = {
+ .driver = {
+ .name = "gef_wdt",
+ .owner = THIS_MODULE,
+ .of_match_table = gef_wdt_ids,
+ },
+ .probe = gef_wdt_probe,
+};
+
+static int __init gef_wdt_init(void)
+{
+ printk(KERN_INFO "GE watchdog driver\n");
+ return platform_driver_register(&gef_wdt_driver);
+}
+
+static void __exit gef_wdt_exit(void)
+{
+ platform_driver_unregister(&gef_wdt_driver);
+}
+
+module_init(gef_wdt_init);
+module_exit(gef_wdt_exit);
+
+MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
+MODULE_DESCRIPTION("GE watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:gef_wdt");
diff --git a/drivers/watchdog/geodewdt.c b/drivers/watchdog/geodewdt.c
new file mode 100644
index 00000000..9b49b125
--- /dev/null
+++ b/drivers/watchdog/geodewdt.c
@@ -0,0 +1,299 @@
+/* Watchdog timer for machines with the CS5535/CS5536 companion chip
+ *
+ * Copyright (C) 2006-2007, Advanced Micro Devices, Inc.
+ * Copyright (C) 2009 Andres Salomon <dilinger@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+
+#include <linux/cs5535.h>
+
+#define GEODEWDT_HZ 500
+#define GEODEWDT_SCALE 6
+#define GEODEWDT_MAX_SECONDS 131
+
+#define WDT_FLAGS_OPEN 1
+#define WDT_FLAGS_ORPHAN 2
+
+#define DRV_NAME "geodewdt"
+#define WATCHDOG_NAME "Geode GX/LX WDT"
+#define WATCHDOG_TIMEOUT 60
+
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <=131, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static struct platform_device *geodewdt_platform_device;
+static unsigned long wdt_flags;
+static struct cs5535_mfgpt_timer *wdt_timer;
+static int safe_close;
+
+static void geodewdt_ping(void)
+{
+ /* Stop the counter */
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);
+
+ /* Reset the counter */
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);
+
+ /* Enable the counter */
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);
+}
+
+static void geodewdt_disable(void)
+{
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);
+}
+
+static int geodewdt_set_heartbeat(int val)
+{
+ if (val < 1 || val > GEODEWDT_MAX_SECONDS)
+ return -EINVAL;
+
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, val * GEODEWDT_HZ);
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);
+
+ timeout = val;
+ return 0;
+}
+
+static int geodewdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_FLAGS_OPEN, &wdt_flags))
+ return -EBUSY;
+
+ if (!test_and_clear_bit(WDT_FLAGS_ORPHAN, &wdt_flags))
+ __module_get(THIS_MODULE);
+
+ geodewdt_ping();
+ return nonseekable_open(inode, file);
+}
+
+static int geodewdt_release(struct inode *inode, struct file *file)
+{
+ if (safe_close) {
+ geodewdt_disable();
+ module_put(THIS_MODULE);
+ } else {
+ printk(KERN_CRIT "Unexpected close - watchdog is not stopping.\n");
+ geodewdt_ping();
+
+ set_bit(WDT_FLAGS_ORPHAN, &wdt_flags);
+ }
+
+ clear_bit(WDT_FLAGS_OPEN, &wdt_flags);
+ safe_close = 0;
+ return 0;
+}
+
+static ssize_t geodewdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+ safe_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+
+ if (c == 'V')
+ safe_close = 1;
+ }
+ }
+
+ geodewdt_ping();
+ }
+ return len;
+}
+
+static long geodewdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int interval;
+
+ static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
+ | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = WATCHDOG_NAME,
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, ret = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ geodewdt_disable();
+ ret = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ geodewdt_ping();
+ ret = 0;
+ }
+
+ return ret;
+ }
+ case WDIOC_KEEPALIVE:
+ geodewdt_ping();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(interval, p))
+ return -EFAULT;
+
+ if (geodewdt_set_heartbeat(interval))
+ return -EINVAL;
+ /* Fall through */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static const struct file_operations geodewdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = geodewdt_write,
+ .unlocked_ioctl = geodewdt_ioctl,
+ .open = geodewdt_open,
+ .release = geodewdt_release,
+};
+
+static struct miscdevice geodewdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &geodewdt_fops,
+};
+
+static int __devinit geodewdt_probe(struct platform_device *dev)
+{
+ int ret;
+
+ wdt_timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
+ if (!wdt_timer) {
+ printk(KERN_ERR "geodewdt: No timers were available\n");
+ return -ENODEV;
+ }
+
+ /* Set up the timer */
+
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP,
+ GEODEWDT_SCALE | (3 << 8));
+
+ /* Set up comparator 2 to reset when the event fires */
+ cs5535_mfgpt_toggle_event(wdt_timer, MFGPT_CMP2, MFGPT_EVENT_RESET, 1);
+
+ /* Set up the initial timeout */
+
+ cs5535_mfgpt_write(wdt_timer, MFGPT_REG_CMP2,
+ timeout * GEODEWDT_HZ);
+
+ ret = misc_register(&geodewdt_miscdev);
+
+ return ret;
+}
+
+static int __devexit geodewdt_remove(struct platform_device *dev)
+{
+ misc_deregister(&geodewdt_miscdev);
+ return 0;
+}
+
+static void geodewdt_shutdown(struct platform_device *dev)
+{
+ geodewdt_disable();
+}
+
+static struct platform_driver geodewdt_driver = {
+ .probe = geodewdt_probe,
+ .remove = __devexit_p(geodewdt_remove),
+ .shutdown = geodewdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+static int __init geodewdt_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&geodewdt_driver);
+ if (ret)
+ return ret;
+
+ geodewdt_platform_device = platform_device_register_simple(DRV_NAME,
+ -1, NULL, 0);
+ if (IS_ERR(geodewdt_platform_device)) {
+ ret = PTR_ERR(geodewdt_platform_device);
+ goto err;
+ }
+
+ return 0;
+err:
+ platform_driver_unregister(&geodewdt_driver);
+ return ret;
+}
+
+static void __exit geodewdt_exit(void)
+{
+ platform_device_unregister(geodewdt_platform_device);
+ platform_driver_unregister(&geodewdt_driver);
+}
+
+module_init(geodewdt_init);
+module_exit(geodewdt_exit);
+
+MODULE_AUTHOR("Advanced Micro Devices, Inc");
+MODULE_DESCRIPTION("Geode GX/LX Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
new file mode 100644
index 00000000..d4ab797c
--- /dev/null
+++ b/drivers/watchdog/hpwdt.c
@@ -0,0 +1,858 @@
+/*
+ * HP WatchDog Driver
+ * based on
+ *
+ * SoftDog 0.05: A Software Watchdog Device
+ *
+ * (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
+ * Thomas Mingarelli <thomas.mingarelli@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#ifdef CONFIG_HPWDT_NMI_DECODING
+#include <linux/dmi.h>
+#include <linux/spinlock.h>
+#include <linux/nmi.h>
+#include <linux/kdebug.h>
+#include <linux/notifier.h>
+#include <asm/cacheflush.h>
+#endif /* CONFIG_HPWDT_NMI_DECODING */
+
+#define HPWDT_VERSION "1.2.0"
+#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
+#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
+#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
+#define DEFAULT_MARGIN 30
+
+static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
+static unsigned int reload; /* the computed soft_margin */
+static int nowayout = WATCHDOG_NOWAYOUT;
+static char expect_release;
+static unsigned long hpwdt_is_open;
+
+static void __iomem *pci_mem_addr; /* the PCI-memory address */
+static unsigned long __iomem *hpwdt_timer_reg;
+static unsigned long __iomem *hpwdt_timer_con;
+
+static DEFINE_PCI_DEVICE_TABLE(hpwdt_devices) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */
+ { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */
+ {0}, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, hpwdt_devices);
+
+#ifdef CONFIG_HPWDT_NMI_DECODING
+#define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */
+#define CRU_BIOS_SIGNATURE_VALUE 0x55524324
+#define PCI_BIOS32_PARAGRAPH_LEN 16
+#define PCI_ROM_BASE1 0x000F0000
+#define ROM_SIZE 0x10000
+
+struct bios32_service_dir {
+ u32 signature;
+ u32 entry_point;
+ u8 revision;
+ u8 length;
+ u8 checksum;
+ u8 reserved[5];
+};
+
+/* type 212 */
+struct smbios_cru64_info {
+ u8 type;
+ u8 byte_length;
+ u16 handle;
+ u32 signature;
+ u64 physical_address;
+ u32 double_length;
+ u32 double_offset;
+};
+#define SMBIOS_CRU64_INFORMATION 212
+
+struct cmn_registers {
+ union {
+ struct {
+ u8 ral;
+ u8 rah;
+ u16 rea2;
+ };
+ u32 reax;
+ } u1;
+ union {
+ struct {
+ u8 rbl;
+ u8 rbh;
+ u8 reb2l;
+ u8 reb2h;
+ };
+ u32 rebx;
+ } u2;
+ union {
+ struct {
+ u8 rcl;
+ u8 rch;
+ u16 rec2;
+ };
+ u32 recx;
+ } u3;
+ union {
+ struct {
+ u8 rdl;
+ u8 rdh;
+ u16 red2;
+ };
+ u32 redx;
+ } u4;
+
+ u32 resi;
+ u32 redi;
+ u16 rds;
+ u16 res;
+ u32 reflags;
+} __attribute__((packed));
+
+static unsigned int hpwdt_nmi_decoding;
+static unsigned int allow_kdump;
+static unsigned int priority; /* hpwdt at end of die_notify list */
+static DEFINE_SPINLOCK(rom_lock);
+static void *cru_rom_addr;
+static struct cmn_registers cmn_regs;
+
+extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs,
+ unsigned long *pRomEntry);
+
+#ifdef CONFIG_X86_32
+/* --32 Bit Bios------------------------------------------------------------ */
+
+#define HPWDT_ARCH 32
+
+asm(".text \n\t"
+ ".align 4 \n"
+ "asminline_call: \n\t"
+ "pushl %ebp \n\t"
+ "movl %esp, %ebp \n\t"
+ "pusha \n\t"
+ "pushf \n\t"
+ "push %es \n\t"
+ "push %ds \n\t"
+ "pop %es \n\t"
+ "movl 8(%ebp),%eax \n\t"
+ "movl 4(%eax),%ebx \n\t"
+ "movl 8(%eax),%ecx \n\t"
+ "movl 12(%eax),%edx \n\t"
+ "movl 16(%eax),%esi \n\t"
+ "movl 20(%eax),%edi \n\t"
+ "movl (%eax),%eax \n\t"
+ "push %cs \n\t"
+ "call *12(%ebp) \n\t"
+ "pushf \n\t"
+ "pushl %eax \n\t"
+ "movl 8(%ebp),%eax \n\t"
+ "movl %ebx,4(%eax) \n\t"
+ "movl %ecx,8(%eax) \n\t"
+ "movl %edx,12(%eax) \n\t"
+ "movl %esi,16(%eax) \n\t"
+ "movl %edi,20(%eax) \n\t"
+ "movw %ds,24(%eax) \n\t"
+ "movw %es,26(%eax) \n\t"
+ "popl %ebx \n\t"
+ "movl %ebx,(%eax) \n\t"
+ "popl %ebx \n\t"
+ "movl %ebx,28(%eax) \n\t"
+ "pop %es \n\t"
+ "popf \n\t"
+ "popa \n\t"
+ "leave \n\t"
+ "ret \n\t"
+ ".previous");
+
+
+/*
+ * cru_detect
+ *
+ * Routine Description:
+ * This function uses the 32-bit BIOS Service Directory record to
+ * search for a $CRU record.
+ *
+ * Return Value:
+ * 0 : SUCCESS
+ * <0 : FAILURE
+ */
+static int __devinit cru_detect(unsigned long map_entry,
+ unsigned long map_offset)
+{
+ void *bios32_map;
+ unsigned long *bios32_entrypoint;
+ unsigned long cru_physical_address;
+ unsigned long cru_length;
+ unsigned long physical_bios_base = 0;
+ unsigned long physical_bios_offset = 0;
+ int retval = -ENODEV;
+
+ bios32_map = ioremap(map_entry, (2 * PAGE_SIZE));
+
+ if (bios32_map == NULL)
+ return -ENODEV;
+
+ bios32_entrypoint = bios32_map + map_offset;
+
+ cmn_regs.u1.reax = CRU_BIOS_SIGNATURE_VALUE;
+
+ set_memory_x((unsigned long)bios32_map, 2);
+ asminline_call(&cmn_regs, bios32_entrypoint);
+
+ if (cmn_regs.u1.ral != 0) {
+ printk(KERN_WARNING
+ "hpwdt: Call succeeded but with an error: 0x%x\n",
+ cmn_regs.u1.ral);
+ } else {
+ physical_bios_base = cmn_regs.u2.rebx;
+ physical_bios_offset = cmn_regs.u4.redx;
+ cru_length = cmn_regs.u3.recx;
+ cru_physical_address =
+ physical_bios_base + physical_bios_offset;
+
+ /* If the values look OK, then map it in. */
+ if ((physical_bios_base + physical_bios_offset)) {
+ cru_rom_addr =
+ ioremap(cru_physical_address, cru_length);
+ if (cru_rom_addr) {
+ set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK,
+ (cru_length + PAGE_SIZE - 1) >> PAGE_SHIFT);
+ retval = 0;
+ }
+ }
+
+ printk(KERN_DEBUG "hpwdt: CRU Base Address: 0x%lx\n",
+ physical_bios_base);
+ printk(KERN_DEBUG "hpwdt: CRU Offset Address: 0x%lx\n",
+ physical_bios_offset);
+ printk(KERN_DEBUG "hpwdt: CRU Length: 0x%lx\n",
+ cru_length);
+ printk(KERN_DEBUG "hpwdt: CRU Mapped Address: %p\n",
+ &cru_rom_addr);
+ }
+ iounmap(bios32_map);
+ return retval;
+}
+
+/*
+ * bios_checksum
+ */
+static int __devinit bios_checksum(const char __iomem *ptr, int len)
+{
+ char sum = 0;
+ int i;
+
+ /*
+ * calculate checksum of size bytes. This should add up
+ * to zero if we have a valid header.
+ */
+ for (i = 0; i < len; i++)
+ sum += ptr[i];
+
+ return ((sum == 0) && (len > 0));
+}
+
+/*
+ * bios32_present
+ *
+ * Routine Description:
+ * This function finds the 32-bit BIOS Service Directory
+ *
+ * Return Value:
+ * 0 : SUCCESS
+ * <0 : FAILURE
+ */
+static int __devinit bios32_present(const char __iomem *p)
+{
+ struct bios32_service_dir *bios_32_ptr;
+ int length;
+ unsigned long map_entry, map_offset;
+
+ bios_32_ptr = (struct bios32_service_dir *) p;
+
+ /*
+ * Search for signature by checking equal to the swizzled value
+ * instead of calling another routine to perform a strcmp.
+ */
+ if (bios_32_ptr->signature == PCI_BIOS32_SD_VALUE) {
+ length = bios_32_ptr->length * PCI_BIOS32_PARAGRAPH_LEN;
+ if (bios_checksum(p, length)) {
+ /*
+ * According to the spec, we're looking for the
+ * first 4KB-aligned address below the entrypoint
+ * listed in the header. The Service Directory code
+ * is guaranteed to occupy no more than 2 4KB pages.
+ */
+ map_entry = bios_32_ptr->entry_point & ~(PAGE_SIZE - 1);
+ map_offset = bios_32_ptr->entry_point - map_entry;
+
+ return cru_detect(map_entry, map_offset);
+ }
+ }
+ return -ENODEV;
+}
+
+static int __devinit detect_cru_service(void)
+{
+ char __iomem *p, *q;
+ int rc = -1;
+
+ /*
+ * Search from 0x0f0000 through 0x0fffff, inclusive.
+ */
+ p = ioremap(PCI_ROM_BASE1, ROM_SIZE);
+ if (p == NULL)
+ return -ENOMEM;
+
+ for (q = p; q < p + ROM_SIZE; q += 16) {
+ rc = bios32_present(q);
+ if (!rc)
+ break;
+ }
+ iounmap(p);
+ return rc;
+}
+/* ------------------------------------------------------------------------- */
+#endif /* CONFIG_X86_32 */
+#ifdef CONFIG_X86_64
+/* --64 Bit Bios------------------------------------------------------------ */
+
+#define HPWDT_ARCH 64
+
+asm(".text \n\t"
+ ".align 4 \n"
+ "asminline_call: \n\t"
+ "pushq %rbp \n\t"
+ "movq %rsp, %rbp \n\t"
+ "pushq %rax \n\t"
+ "pushq %rbx \n\t"
+ "pushq %rdx \n\t"
+ "pushq %r12 \n\t"
+ "pushq %r9 \n\t"
+ "movq %rsi, %r12 \n\t"
+ "movq %rdi, %r9 \n\t"
+ "movl 4(%r9),%ebx \n\t"
+ "movl 8(%r9),%ecx \n\t"
+ "movl 12(%r9),%edx \n\t"
+ "movl 16(%r9),%esi \n\t"
+ "movl 20(%r9),%edi \n\t"
+ "movl (%r9),%eax \n\t"
+ "call *%r12 \n\t"
+ "pushfq \n\t"
+ "popq %r12 \n\t"
+ "movl %eax, (%r9) \n\t"
+ "movl %ebx, 4(%r9) \n\t"
+ "movl %ecx, 8(%r9) \n\t"
+ "movl %edx, 12(%r9) \n\t"
+ "movl %esi, 16(%r9) \n\t"
+ "movl %edi, 20(%r9) \n\t"
+ "movq %r12, %rax \n\t"
+ "movl %eax, 28(%r9) \n\t"
+ "popq %r9 \n\t"
+ "popq %r12 \n\t"
+ "popq %rdx \n\t"
+ "popq %rbx \n\t"
+ "popq %rax \n\t"
+ "leave \n\t"
+ "ret \n\t"
+ ".previous");
+
+/*
+ * dmi_find_cru
+ *
+ * Routine Description:
+ * This function checks whether or not a SMBIOS/DMI record is
+ * the 64bit CRU info or not
+ */
+static void __devinit dmi_find_cru(const struct dmi_header *dm, void *dummy)
+{
+ struct smbios_cru64_info *smbios_cru64_ptr;
+ unsigned long cru_physical_address;
+
+ if (dm->type == SMBIOS_CRU64_INFORMATION) {
+ smbios_cru64_ptr = (struct smbios_cru64_info *) dm;
+ if (smbios_cru64_ptr->signature == CRU_BIOS_SIGNATURE_VALUE) {
+ cru_physical_address =
+ smbios_cru64_ptr->physical_address +
+ smbios_cru64_ptr->double_offset;
+ cru_rom_addr = ioremap(cru_physical_address,
+ smbios_cru64_ptr->double_length);
+ set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK,
+ smbios_cru64_ptr->double_length >> PAGE_SHIFT);
+ }
+ }
+}
+
+static int __devinit detect_cru_service(void)
+{
+ cru_rom_addr = NULL;
+
+ dmi_walk(dmi_find_cru, NULL);
+
+ /* if cru_rom_addr has been set then we found a CRU service */
+ return ((cru_rom_addr != NULL) ? 0 : -ENODEV);
+}
+/* ------------------------------------------------------------------------- */
+#endif /* CONFIG_X86_64 */
+#endif /* CONFIG_HPWDT_NMI_DECODING */
+
+/*
+ * Watchdog operations
+ */
+static void hpwdt_start(void)
+{
+ reload = SECS_TO_TICKS(soft_margin);
+ iowrite16(reload, hpwdt_timer_reg);
+ iowrite16(0x85, hpwdt_timer_con);
+}
+
+static void hpwdt_stop(void)
+{
+ unsigned long data;
+
+ data = ioread16(hpwdt_timer_con);
+ data &= 0xFE;
+ iowrite16(data, hpwdt_timer_con);
+}
+
+static void hpwdt_ping(void)
+{
+ iowrite16(reload, hpwdt_timer_reg);
+}
+
+static int hpwdt_change_timer(int new_margin)
+{
+ if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) {
+ printk(KERN_WARNING
+ "hpwdt: New value passed in is invalid: %d seconds.\n",
+ new_margin);
+ return -EINVAL;
+ }
+
+ soft_margin = new_margin;
+ printk(KERN_DEBUG
+ "hpwdt: New timer passed in is %d seconds.\n",
+ new_margin);
+ reload = SECS_TO_TICKS(soft_margin);
+
+ return 0;
+}
+
+static int hpwdt_time_left(void)
+{
+ return TICKS_TO_SECS(ioread16(hpwdt_timer_reg));
+}
+
+#ifdef CONFIG_HPWDT_NMI_DECODING
+/*
+ * NMI Handler
+ */
+static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason,
+ void *data)
+{
+ unsigned long rom_pl;
+ static int die_nmi_called;
+
+ if (ulReason != DIE_NMIUNKNOWN)
+ goto out;
+
+ if (!hpwdt_nmi_decoding)
+ goto out;
+
+ spin_lock_irqsave(&rom_lock, rom_pl);
+ if (!die_nmi_called)
+ asminline_call(&cmn_regs, cru_rom_addr);
+ die_nmi_called = 1;
+ spin_unlock_irqrestore(&rom_lock, rom_pl);
+ if (cmn_regs.u1.ral == 0) {
+ printk(KERN_WARNING "hpwdt: An NMI occurred, "
+ "but unable to determine source.\n");
+ } else {
+ if (allow_kdump)
+ hpwdt_stop();
+ panic("An NMI occurred, please see the Integrated "
+ "Management Log for details.\n");
+ }
+out:
+ return NOTIFY_OK;
+}
+#endif /* CONFIG_HPWDT_NMI_DECODING */
+
+/*
+ * /dev/watchdog handling
+ */
+static int hpwdt_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &hpwdt_is_open))
+ return -EBUSY;
+
+ /* Start the watchdog */
+ hpwdt_start();
+ hpwdt_ping();
+
+ return nonseekable_open(inode, file);
+}
+
+static int hpwdt_release(struct inode *inode, struct file *file)
+{
+ /* Stop the watchdog */
+ if (expect_release == 42) {
+ hpwdt_stop();
+ } else {
+ printk(KERN_CRIT
+ "hpwdt: Unexpected close, not stopping watchdog!\n");
+ hpwdt_ping();
+ }
+
+ expect_release = 0;
+
+ /* /dev/watchdog is being closed, make sure it can be re-opened */
+ clear_bit(0, &hpwdt_is_open);
+
+ return 0;
+}
+
+static ssize_t hpwdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ expect_release = 0;
+
+ /* scan to see whether or not we got the magic char. */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_release = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ hpwdt_ping();
+ }
+
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .identity = "HP iLO2+ HW Watchdog Timer",
+};
+
+static long hpwdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_margin;
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = 0;
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ ret = -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, p);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ hpwdt_ping();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(new_margin, p);
+ if (ret)
+ break;
+
+ ret = hpwdt_change_timer(new_margin);
+ if (ret)
+ break;
+
+ hpwdt_ping();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(soft_margin, p);
+ break;
+
+ case WDIOC_GETTIMELEFT:
+ ret = put_user(hpwdt_time_left(), p);
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Kernel interfaces
+ */
+static const struct file_operations hpwdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = hpwdt_write,
+ .unlocked_ioctl = hpwdt_ioctl,
+ .open = hpwdt_open,
+ .release = hpwdt_release,
+};
+
+static struct miscdevice hpwdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &hpwdt_fops,
+};
+
+#ifdef CONFIG_HPWDT_NMI_DECODING
+static struct notifier_block die_notifier = {
+ .notifier_call = hpwdt_pretimeout,
+ .priority = 0,
+};
+#endif /* CONFIG_HPWDT_NMI_DECODING */
+
+/*
+ * Init & Exit
+ */
+
+#ifdef CONFIG_HPWDT_NMI_DECODING
+#ifdef CONFIG_X86_LOCAL_APIC
+static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
+{
+ /*
+ * If nmi_watchdog is turned off then we can turn on
+ * our nmi decoding capability.
+ */
+ hpwdt_nmi_decoding = 1;
+}
+#else
+static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
+{
+ dev_warn(&dev->dev, "NMI decoding is disabled. "
+ "Your kernel does not support a NMI Watchdog.\n");
+}
+#endif /* CONFIG_X86_LOCAL_APIC */
+
+static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
+{
+ int retval;
+
+ /*
+ * We need to map the ROM to get the CRU service.
+ * For 32 bit Operating Systems we need to go through the 32 Bit
+ * BIOS Service Directory
+ * For 64 bit Operating Systems we get that service through SMBIOS.
+ */
+ retval = detect_cru_service();
+ if (retval < 0) {
+ dev_warn(&dev->dev,
+ "Unable to detect the %d Bit CRU Service.\n",
+ HPWDT_ARCH);
+ return retval;
+ }
+
+ /*
+ * We know this is the only CRU call we need to make so lets keep as
+ * few instructions as possible once the NMI comes in.
+ */
+ cmn_regs.u1.rah = 0x0D;
+ cmn_regs.u1.ral = 0x02;
+
+ /*
+ * If the priority is set to 1, then we will be put first on the
+ * die notify list to handle a critical NMI. The default is to
+ * be last so other users of the NMI signal can function.
+ */
+ if (priority)
+ die_notifier.priority = 0x7FFFFFFF;
+
+ retval = register_die_notifier(&die_notifier);
+ if (retval != 0) {
+ dev_warn(&dev->dev,
+ "Unable to register a die notifier (err=%d).\n",
+ retval);
+ if (cru_rom_addr)
+ iounmap(cru_rom_addr);
+ }
+
+ dev_info(&dev->dev,
+ "HP Watchdog Timer Driver: NMI decoding initialized"
+ ", allow kernel dump: %s (default = 0/OFF)"
+ ", priority: %s (default = 0/LAST).\n",
+ (allow_kdump == 0) ? "OFF" : "ON",
+ (priority == 0) ? "LAST" : "FIRST");
+ return 0;
+}
+
+static void hpwdt_exit_nmi_decoding(void)
+{
+ unregister_die_notifier(&die_notifier);
+ if (cru_rom_addr)
+ iounmap(cru_rom_addr);
+}
+#else /* !CONFIG_HPWDT_NMI_DECODING */
+static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
+{
+}
+
+static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
+{
+ return 0;
+}
+
+static void hpwdt_exit_nmi_decoding(void)
+{
+}
+#endif /* CONFIG_HPWDT_NMI_DECODING */
+
+static int __devinit hpwdt_init_one(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ int retval;
+
+ /*
+ * Check if we can do NMI decoding or not
+ */
+ hpwdt_check_nmi_decoding(dev);
+
+ /*
+ * First let's find out if we are on an iLO2+ server. We will
+ * not run on a legacy ASM box.
+ * So we only support the G5 ProLiant servers and higher.
+ */
+ if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) {
+ dev_warn(&dev->dev,
+ "This server does not have an iLO2+ ASIC.\n");
+ return -ENODEV;
+ }
+
+ if (pci_enable_device(dev)) {
+ dev_warn(&dev->dev,
+ "Not possible to enable PCI Device: 0x%x:0x%x.\n",
+ ent->vendor, ent->device);
+ return -ENODEV;
+ }
+
+ pci_mem_addr = pci_iomap(dev, 1, 0x80);
+ if (!pci_mem_addr) {
+ dev_warn(&dev->dev,
+ "Unable to detect the iLO2+ server memory.\n");
+ retval = -ENOMEM;
+ goto error_pci_iomap;
+ }
+ hpwdt_timer_reg = pci_mem_addr + 0x70;
+ hpwdt_timer_con = pci_mem_addr + 0x72;
+
+ /* Make sure that we have a valid soft_margin */
+ if (hpwdt_change_timer(soft_margin))
+ hpwdt_change_timer(DEFAULT_MARGIN);
+
+ /* Initialize NMI Decoding functionality */
+ retval = hpwdt_init_nmi_decoding(dev);
+ if (retval != 0)
+ goto error_init_nmi_decoding;
+
+ retval = misc_register(&hpwdt_miscdev);
+ if (retval < 0) {
+ dev_warn(&dev->dev,
+ "Unable to register miscdev on minor=%d (err=%d).\n",
+ WATCHDOG_MINOR, retval);
+ goto error_misc_register;
+ }
+
+ dev_info(&dev->dev, "HP Watchdog Timer Driver: %s"
+ ", timer margin: %d seconds (nowayout=%d).\n",
+ HPWDT_VERSION, soft_margin, nowayout);
+ return 0;
+
+error_misc_register:
+ hpwdt_exit_nmi_decoding();
+error_init_nmi_decoding:
+ pci_iounmap(dev, pci_mem_addr);
+error_pci_iomap:
+ pci_disable_device(dev);
+ return retval;
+}
+
+static void __devexit hpwdt_exit(struct pci_dev *dev)
+{
+ if (!nowayout)
+ hpwdt_stop();
+
+ misc_deregister(&hpwdt_miscdev);
+ hpwdt_exit_nmi_decoding();
+ pci_iounmap(dev, pci_mem_addr);
+ pci_disable_device(dev);
+}
+
+static struct pci_driver hpwdt_driver = {
+ .name = "hpwdt",
+ .id_table = hpwdt_devices,
+ .probe = hpwdt_init_one,
+ .remove = __devexit_p(hpwdt_exit),
+};
+
+static void __exit hpwdt_cleanup(void)
+{
+ pci_unregister_driver(&hpwdt_driver);
+}
+
+static int __init hpwdt_init(void)
+{
+ return pci_register_driver(&hpwdt_driver);
+}
+
+MODULE_AUTHOR("Tom Mingarelli");
+MODULE_DESCRIPTION("hp watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(HPWDT_VERSION);
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+module_param(soft_margin, int, 0);
+MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#ifdef CONFIG_HPWDT_NMI_DECODING
+module_param(allow_kdump, int, 0);
+MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
+
+module_param(priority, int, 0);
+MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last"
+ " (default = 0/Last)\n");
+#endif /* !CONFIG_HPWDT_NMI_DECODING */
+
+module_init(hpwdt_init);
+module_exit(hpwdt_cleanup);
diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c
new file mode 100644
index 00000000..db45091e
--- /dev/null
+++ b/drivers/watchdog/i6300esb.c
@@ -0,0 +1,515 @@
+/*
+ * i6300esb: Watchdog timer driver for Intel 6300ESB chipset
+ *
+ * (c) Copyright 2004 Google Inc.
+ * (c) Copyright 2005 David Härdeman <david@2gen.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * based on i810-tco.c which is in turn based on softdog.c
+ *
+ * The timer is implemented in the following I/O controller hubs:
+ * (See the intel documentation on http://developer.intel.com.)
+ * 6300ESB chip : document number 300641-004
+ *
+ * 2004YYZZ Ross Biro
+ * Initial version 0.01
+ * 2004YYZZ Ross Biro
+ * Version 0.02
+ * 20050210 David Härdeman <david@2gen.com>
+ * Ported driver to kernel 2.6
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+/* Module and version information */
+#define ESB_VERSION "0.05"
+#define ESB_MODULE_NAME "i6300ESB timer"
+#define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION
+#define PFX ESB_MODULE_NAME ": "
+
+/* PCI configuration registers */
+#define ESB_CONFIG_REG 0x60 /* Config register */
+#define ESB_LOCK_REG 0x68 /* WDT lock register */
+
+/* Memory mapped registers */
+#define ESB_TIMER1_REG (BASEADDR + 0x00)/* Timer1 value after each reset */
+#define ESB_TIMER2_REG (BASEADDR + 0x04)/* Timer2 value after each reset */
+#define ESB_GINTSR_REG (BASEADDR + 0x08)/* General Interrupt Status Register */
+#define ESB_RELOAD_REG (BASEADDR + 0x0c)/* Reload register */
+
+/* Lock register bits */
+#define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */
+#define ESB_WDT_ENABLE (0x01 << 1) /* Enable WDT */
+#define ESB_WDT_LOCK (0x01 << 0) /* Lock (nowayout) */
+
+/* Config register bits */
+#define ESB_WDT_REBOOT (0x01 << 5) /* Enable reboot on timeout */
+#define ESB_WDT_FREQ (0x01 << 2) /* Decrement frequency */
+#define ESB_WDT_INTTYPE (0x03 << 0) /* Interrupt type on timer1 timeout */
+
+/* Reload register bits */
+#define ESB_WDT_TIMEOUT (0x01 << 9) /* Watchdog timed out */
+#define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */
+
+/* Magic constants */
+#define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */
+#define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */
+
+/* internal variables */
+static void __iomem *BASEADDR;
+static DEFINE_SPINLOCK(esb_lock); /* Guards the hardware */
+static unsigned long timer_alive;
+static struct pci_dev *esb_pci;
+static unsigned short triggered; /* The status of the watchdog upon boot */
+static char esb_expect_close;
+
+/* We can only use 1 card due to the /dev/watchdog restriction */
+static int cards_found;
+
+/* module parameters */
+/* 30 sec default heartbeat (1 < heartbeat < 2*1023) */
+#define WATCHDOG_HEARTBEAT 30
+static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat in seconds. (1<heartbeat<2046, default="
+ __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Some i6300ESB specific functions
+ */
+
+/*
+ * Prepare for reloading the timer by unlocking the proper registers.
+ * This is performed by first writing 0x80 followed by 0x86 to the
+ * reload register. After this the appropriate registers can be written
+ * to once before they need to be unlocked again.
+ */
+static inline void esb_unlock_registers(void)
+{
+ writew(ESB_UNLOCK1, ESB_RELOAD_REG);
+ writew(ESB_UNLOCK2, ESB_RELOAD_REG);
+}
+
+static int esb_timer_start(void)
+{
+ u8 val;
+
+ spin_lock(&esb_lock);
+ esb_unlock_registers();
+ writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
+ /* Enable or Enable + Lock? */
+ val = ESB_WDT_ENABLE | (nowayout ? ESB_WDT_LOCK : 0x00);
+ pci_write_config_byte(esb_pci, ESB_LOCK_REG, val);
+ spin_unlock(&esb_lock);
+ return 0;
+}
+
+static int esb_timer_stop(void)
+{
+ u8 val;
+
+ spin_lock(&esb_lock);
+ /* First, reset timers as suggested by the docs */
+ esb_unlock_registers();
+ writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
+ /* Then disable the WDT */
+ pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x0);
+ pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val);
+ spin_unlock(&esb_lock);
+
+ /* Returns 0 if the timer was disabled, non-zero otherwise */
+ return val & ESB_WDT_ENABLE;
+}
+
+static void esb_timer_keepalive(void)
+{
+ spin_lock(&esb_lock);
+ esb_unlock_registers();
+ writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
+ /* FIXME: Do we need to flush anything here? */
+ spin_unlock(&esb_lock);
+}
+
+static int esb_timer_set_heartbeat(int time)
+{
+ u32 val;
+
+ if (time < 0x1 || time > (2 * 0x03ff))
+ return -EINVAL;
+
+ spin_lock(&esb_lock);
+
+ /* We shift by 9, so if we are passed a value of 1 sec,
+ * val will be 1 << 9 = 512, then write that to two
+ * timers => 2 * 512 = 1024 (which is decremented at 1KHz)
+ */
+ val = time << 9;
+
+ /* Write timer 1 */
+ esb_unlock_registers();
+ writel(val, ESB_TIMER1_REG);
+
+ /* Write timer 2 */
+ esb_unlock_registers();
+ writel(val, ESB_TIMER2_REG);
+
+ /* Reload */
+ esb_unlock_registers();
+ writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
+
+ /* FIXME: Do we need to flush everything out? */
+
+ /* Done */
+ heartbeat = time;
+ spin_unlock(&esb_lock);
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int esb_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &timer_alive))
+ return -EBUSY;
+
+ /* Reload and activate timer */
+ esb_timer_start();
+
+ return nonseekable_open(inode, file);
+}
+
+static int esb_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer. */
+ if (esb_expect_close == 42)
+ esb_timer_stop();
+ else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ esb_timer_keepalive();
+ }
+ clear_bit(0, &timer_alive);
+ esb_expect_close = 0;
+ return 0;
+}
+
+static ssize_t esb_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ esb_expect_close = 0;
+
+ /* scan to see whether or not we got the
+ * magic character */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ esb_expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ esb_timer_keepalive();
+ }
+ return len;
+}
+
+static long esb_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 = ESB_MODULE_NAME,
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(triggered, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ if (get_user(new_options, p))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD) {
+ esb_timer_stop();
+ retval = 0;
+ }
+
+ if (new_options & WDIOS_ENABLECARD) {
+ esb_timer_start();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ esb_timer_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ {
+ if (get_user(new_heartbeat, p))
+ return -EFAULT;
+ if (esb_timer_set_heartbeat(new_heartbeat))
+ return -EINVAL;
+ esb_timer_keepalive();
+ /* Fall */
+ }
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations esb_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = esb_write,
+ .unlocked_ioctl = esb_ioctl,
+ .open = esb_open,
+ .release = esb_release,
+};
+
+static struct miscdevice esb_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &esb_fops,
+};
+
+/*
+ * Data for PCI driver interface
+ */
+static DEFINE_PCI_DEVICE_TABLE(esb_pci_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), },
+ { 0, }, /* End of list */
+};
+MODULE_DEVICE_TABLE(pci, esb_pci_tbl);
+
+/*
+ * Init & exit routines
+ */
+
+static unsigned char __devinit esb_getdevice(struct pci_dev *pdev)
+{
+ if (pci_enable_device(pdev)) {
+ printk(KERN_ERR PFX "failed to enable device\n");
+ goto err_devput;
+ }
+
+ if (pci_request_region(pdev, 0, ESB_MODULE_NAME)) {
+ printk(KERN_ERR PFX "failed to request region\n");
+ goto err_disable;
+ }
+
+ BASEADDR = pci_ioremap_bar(pdev, 0);
+ if (BASEADDR == NULL) {
+ /* Something's wrong here, BASEADDR has to be set */
+ printk(KERN_ERR PFX "failed to get BASEADDR\n");
+ goto err_release;
+ }
+
+ /* Done */
+ esb_pci = pdev;
+ return 1;
+
+err_release:
+ pci_release_region(pdev, 0);
+err_disable:
+ pci_disable_device(pdev);
+err_devput:
+ return 0;
+}
+
+static void __devinit esb_initdevice(void)
+{
+ u8 val1;
+ u16 val2;
+
+ /*
+ * Config register:
+ * Bit 5 : 0 = Enable WDT_OUTPUT
+ * Bit 2 : 0 = set the timer frequency to the PCI clock
+ * divided by 2^15 (approx 1KHz).
+ * Bits 1:0 : 11 = WDT_INT_TYPE Disabled.
+ * The watchdog has two timers, it can be setup so that the
+ * expiry of timer1 results in an interrupt and the expiry of
+ * timer2 results in a reboot. We set it to not generate
+ * any interrupts as there is not much we can do with it
+ * right now.
+ */
+ pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003);
+
+ /* Check that the WDT isn't already locked */
+ pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1);
+ if (val1 & ESB_WDT_LOCK)
+ printk(KERN_WARNING PFX "nowayout already set\n");
+
+ /* Set the timer to watchdog mode and disable it for now */
+ pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00);
+
+ /* Check if the watchdog was previously triggered */
+ esb_unlock_registers();
+ val2 = readw(ESB_RELOAD_REG);
+ if (val2 & ESB_WDT_TIMEOUT)
+ triggered = WDIOF_CARDRESET;
+
+ /* Reset WDT_TIMEOUT flag and timers */
+ esb_unlock_registers();
+ writew((ESB_WDT_TIMEOUT | ESB_WDT_RELOAD), ESB_RELOAD_REG);
+
+ /* And set the correct timeout value */
+ esb_timer_set_heartbeat(heartbeat);
+}
+
+static int __devinit esb_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int ret;
+
+ cards_found++;
+ if (cards_found == 1)
+ printk(KERN_INFO PFX "Intel 6300ESB WatchDog Timer Driver v%s\n",
+ ESB_VERSION);
+
+ if (cards_found > 1) {
+ printk(KERN_ERR PFX "This driver only supports 1 device\n");
+ return -ENODEV;
+ }
+
+ /* Check whether or not the hardware watchdog is there */
+ if (!esb_getdevice(pdev) || esb_pci == NULL)
+ return -ENODEV;
+
+ /* Check that the heartbeat value is within it's range;
+ if not reset to the default */
+ if (heartbeat < 0x1 || heartbeat > 2 * 0x03ff) {
+ heartbeat = WATCHDOG_HEARTBEAT;
+ printk(KERN_INFO PFX
+ "heartbeat value must be 1<heartbeat<2046, using %d\n",
+ heartbeat);
+ }
+
+ /* Initialize the watchdog and make sure it does not run */
+ esb_initdevice();
+
+ /* Register the watchdog so that userspace has access to it */
+ ret = misc_register(&esb_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto err_unmap;
+ }
+ printk(KERN_INFO PFX
+ "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
+ BASEADDR, heartbeat, nowayout);
+ return 0;
+
+err_unmap:
+ iounmap(BASEADDR);
+ pci_release_region(esb_pci, 0);
+ pci_disable_device(esb_pci);
+ esb_pci = NULL;
+ return ret;
+}
+
+static void __devexit esb_remove(struct pci_dev *pdev)
+{
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ esb_timer_stop();
+
+ /* Deregister */
+ misc_deregister(&esb_miscdev);
+ iounmap(BASEADDR);
+ pci_release_region(esb_pci, 0);
+ pci_disable_device(esb_pci);
+ esb_pci = NULL;
+}
+
+static void esb_shutdown(struct pci_dev *pdev)
+{
+ esb_timer_stop();
+}
+
+static struct pci_driver esb_driver = {
+ .name = ESB_MODULE_NAME,
+ .id_table = esb_pci_tbl,
+ .probe = esb_probe,
+ .remove = __devexit_p(esb_remove),
+ .shutdown = esb_shutdown,
+};
+
+static int __init watchdog_init(void)
+{
+ return pci_register_driver(&esb_driver);
+}
+
+static void __exit watchdog_cleanup(void)
+{
+ pci_unregister_driver(&esb_driver);
+ printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_cleanup);
+
+MODULE_AUTHOR("Ross Biro and David Härdeman");
+MODULE_DESCRIPTION("Watchdog driver for Intel 6300ESB chipsets");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/iTCO_vendor.h b/drivers/watchdog/iTCO_vendor.h
new file mode 100644
index 00000000..9e27e642
--- /dev/null
+++ b/drivers/watchdog/iTCO_vendor.h
@@ -0,0 +1,15 @@
+/* iTCO Vendor Specific Support hooks */
+#ifdef CONFIG_ITCO_VENDOR_SUPPORT
+extern void iTCO_vendor_pre_start(unsigned long, unsigned int);
+extern void iTCO_vendor_pre_stop(unsigned long);
+extern void iTCO_vendor_pre_keepalive(unsigned long, unsigned int);
+extern void iTCO_vendor_pre_set_heartbeat(unsigned int);
+extern int iTCO_vendor_check_noreboot_on(void);
+#else
+#define iTCO_vendor_pre_start(acpibase, heartbeat) {}
+#define iTCO_vendor_pre_stop(acpibase) {}
+#define iTCO_vendor_pre_keepalive(acpibase, heartbeat) {}
+#define iTCO_vendor_pre_set_heartbeat(heartbeat) {}
+#define iTCO_vendor_check_noreboot_on() 1
+ /* 1=check noreboot; 0=don't check */
+#endif
diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c
new file mode 100644
index 00000000..481d1ad4
--- /dev/null
+++ b/drivers/watchdog/iTCO_vendor_support.c
@@ -0,0 +1,375 @@
+/*
+ * intel TCO vendor specific watchdog driver support
+ *
+ * (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
+ * provide warranty for any of this software. This material is
+ * provided "AS-IS" and at no charge.
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+/* Module and version information */
+#define DRV_NAME "iTCO_vendor_support"
+#define DRV_VERSION "1.04"
+#define PFX DRV_NAME ": "
+
+/* Includes */
+#include <linux/module.h> /* For module specific items */
+#include <linux/moduleparam.h> /* For new moduleparam's */
+#include <linux/types.h> /* For standard types (like size_t) */
+#include <linux/errno.h> /* For the -ENODEV/... values */
+#include <linux/kernel.h> /* For printk/panic/... */
+#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/ioport.h> /* For io-port access */
+#include <linux/io.h> /* For inb/outb/... */
+
+#include "iTCO_vendor.h"
+
+/* iTCO defines */
+#define SMI_EN (acpibase + 0x30) /* SMI Control and Enable Register */
+#define TCOBASE (acpibase + 0x60) /* TCO base address */
+#define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */
+
+/* List of vendor support modes */
+/* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */
+#define SUPERMICRO_OLD_BOARD 1
+/* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems */
+#define SUPERMICRO_NEW_BOARD 2
+/* Broken BIOS */
+#define BROKEN_BIOS 911
+
+static int vendorsupport;
+module_param(vendorsupport, int, 0);
+MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default="
+ "0 (none), 1=SuperMicro Pent3, 2=SuperMicro Pent4+, "
+ "911=Broken SMI BIOS");
+
+/*
+ * Vendor Specific Support
+ */
+
+/*
+ * Vendor Support: 1
+ * Board: Super Micro Computer Inc. 370SSE+-OEM1/P3TSSE
+ * iTCO chipset: ICH2
+ *
+ * Code contributed by: R. Seretny <lkpatches@paypc.com>
+ * Documentation obtained by R. Seretny from SuperMicro Technical Support
+ *
+ * To enable Watchdog function:
+ * BIOS setup -> Power -> TCO Logic SMI Enable -> Within5Minutes
+ * This setting enables SMI to clear the watchdog expired flag.
+ * If BIOS or CPU fail which may cause SMI hang, then system will
+ * reboot. When application starts to use watchdog function,
+ * application has to take over the control from SMI.
+ *
+ * For P3TSSE, J36 jumper needs to be removed to enable the Watchdog
+ * function.
+ *
+ * Note: The system will reboot when Expire Flag is set TWICE.
+ * So, if the watchdog timer is 20 seconds, then the maximum hang
+ * time is about 40 seconds, and the minimum hang time is about
+ * 20.6 seconds.
+ */
+
+static void supermicro_old_pre_start(unsigned long acpibase)
+{
+ unsigned long val32;
+
+ /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
+ val32 = inl(SMI_EN);
+ val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
+ outl(val32, SMI_EN); /* Needed to activate watchdog */
+}
+
+static void supermicro_old_pre_stop(unsigned long acpibase)
+{
+ unsigned long val32;
+
+ /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */
+ val32 = inl(SMI_EN);
+ val32 |= 0x00002000; /* Turn on SMI clearing watchdog */
+ outl(val32, SMI_EN); /* Needed to deactivate watchdog */
+}
+
+/*
+ * Vendor Support: 2
+ * Board: Super Micro Computer Inc. P4SBx, P4DPx
+ * iTCO chipset: ICH4
+ *
+ * Code contributed by: R. Seretny <lkpatches@paypc.com>
+ * Documentation obtained by R. Seretny from SuperMicro Technical Support
+ *
+ * To enable Watchdog function:
+ * 1. BIOS
+ * For P4SBx:
+ * BIOS setup -> Advanced -> Integrated Peripherals -> Watch Dog Feature
+ * For P4DPx:
+ * BIOS setup -> Advanced -> I/O Device Configuration -> Watch Dog
+ * This setting enables or disables Watchdog function. When enabled, the
+ * default watchdog timer is set to be 5 minutes (about 4m35s). It is
+ * enough to load and run the OS. The application (service or driver) has
+ * to take over the control once OS is running up and before watchdog
+ * expires.
+ *
+ * 2. JUMPER
+ * For P4SBx: JP39
+ * For P4DPx: JP37
+ * This jumper is used for safety. Closed is enabled. This jumper
+ * prevents user enables watchdog in BIOS by accident.
+ *
+ * To enable Watch Dog function, both BIOS and JUMPER must be enabled.
+ *
+ * The documentation lists motherboards P4SBx and P4DPx series as of
+ * 20-March-2002. However, this code works flawlessly with much newer
+ * motherboards, such as my X6DHR-8G2 (SuperServer 6014H-82).
+ *
+ * The original iTCO driver as written does not actually reset the
+ * watchdog timer on these machines, as a result they reboot after five
+ * minutes.
+ *
+ * NOTE: You may leave the Watchdog function disabled in the SuperMicro
+ * BIOS to avoid a "boot-race"... This driver will enable watchdog
+ * functionality even if it's disabled in the BIOS once the /dev/watchdog
+ * file is opened.
+ */
+
+/* I/O Port's */
+#define SM_REGINDEX 0x2e /* SuperMicro ICH4+ Register Index */
+#define SM_DATAIO 0x2f /* SuperMicro ICH4+ Register Data I/O */
+
+/* Control Register's */
+#define SM_CTLPAGESW 0x07 /* SuperMicro ICH4+ Control Page Switch */
+#define SM_CTLPAGE 0x08 /* SuperMicro ICH4+ Control Page Num */
+
+#define SM_WATCHENABLE 0x30 /* Watchdog enable: Bit 0: 0=off, 1=on */
+
+#define SM_WATCHPAGE 0x87 /* Watchdog unlock control page */
+
+#define SM_ENDWATCH 0xAA /* Watchdog lock control page */
+
+#define SM_COUNTMODE 0xf5 /* Watchdog count mode select */
+ /* (Bit 3: 0 = seconds, 1 = minutes */
+
+#define SM_WATCHTIMER 0xf6 /* 8-bits, Watchdog timer counter (RW) */
+
+#define SM_RESETCONTROL 0xf7 /* Watchdog reset control */
+ /* Bit 6: timer is reset by kbd interrupt */
+ /* Bit 7: timer is reset by mouse interrupt */
+
+static void supermicro_new_unlock_watchdog(void)
+{
+ /* Write 0x87 to port 0x2e twice */
+ outb(SM_WATCHPAGE, SM_REGINDEX);
+ outb(SM_WATCHPAGE, SM_REGINDEX);
+ /* Switch to watchdog control page */
+ outb(SM_CTLPAGESW, SM_REGINDEX);
+ outb(SM_CTLPAGE, SM_DATAIO);
+}
+
+static void supermicro_new_lock_watchdog(void)
+{
+ outb(SM_ENDWATCH, SM_REGINDEX);
+}
+
+static void supermicro_new_pre_start(unsigned int heartbeat)
+{
+ unsigned int val;
+
+ supermicro_new_unlock_watchdog();
+
+ /* Watchdog timer setting needs to be in seconds*/
+ outb(SM_COUNTMODE, SM_REGINDEX);
+ val = inb(SM_DATAIO);
+ val &= 0xF7;
+ outb(val, SM_DATAIO);
+
+ /* Write heartbeat interval to WDOG */
+ outb(SM_WATCHTIMER, SM_REGINDEX);
+ outb((heartbeat & 255), SM_DATAIO);
+
+ /* Make sure keyboard/mouse interrupts don't interfere */
+ outb(SM_RESETCONTROL, SM_REGINDEX);
+ val = inb(SM_DATAIO);
+ val &= 0x3f;
+ outb(val, SM_DATAIO);
+
+ /* enable watchdog by setting bit 0 of Watchdog Enable to 1 */
+ outb(SM_WATCHENABLE, SM_REGINDEX);
+ val = inb(SM_DATAIO);
+ val |= 0x01;
+ outb(val, SM_DATAIO);
+
+ supermicro_new_lock_watchdog();
+}
+
+static void supermicro_new_pre_stop(void)
+{
+ unsigned int val;
+
+ supermicro_new_unlock_watchdog();
+
+ /* disable watchdog by setting bit 0 of Watchdog Enable to 0 */
+ outb(SM_WATCHENABLE, SM_REGINDEX);
+ val = inb(SM_DATAIO);
+ val &= 0xFE;
+ outb(val, SM_DATAIO);
+
+ supermicro_new_lock_watchdog();
+}
+
+static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat)
+{
+ supermicro_new_unlock_watchdog();
+
+ /* reset watchdog timeout to heartveat value */
+ outb(SM_WATCHTIMER, SM_REGINDEX);
+ outb((heartbeat & 255), SM_DATAIO);
+
+ supermicro_new_lock_watchdog();
+}
+
+/*
+ * Vendor Support: 911
+ * Board: Some Intel ICHx based motherboards
+ * iTCO chipset: ICH7+
+ *
+ * Some Intel motherboards have a broken BIOS implementation: i.e.
+ * the SMI handler clear's the TIMEOUT bit in the TC01_STS register
+ * and does not reload the time. Thus the TCO watchdog does not reboot
+ * the system.
+ *
+ * These are the conclusions of Andriy Gapon <avg@icyb.net.ua> after
+ * debugging: the SMI handler is quite simple - it tests value in
+ * TCO1_CNT against 0x800, i.e. checks TCO_TMR_HLT. If the bit is set
+ * the handler goes into an infinite loop, apparently to allow the
+ * second timeout and reboot. Otherwise it simply clears TIMEOUT bit
+ * in TCO1_STS and that's it.
+ * So the logic seems to be reversed, because it is hard to see how
+ * TIMEOUT can get set to 1 and SMI generated when TCO_TMR_HLT is set
+ * (other than a transitional effect).
+ *
+ * The only fix found to get the motherboard(s) to reboot is to put
+ * the glb_smi_en bit to 0. This is a dirty hack that bypasses the
+ * broken code by disabling Global SMI.
+ *
+ * WARNING: globally disabling SMI could possibly lead to dramatic
+ * problems, especially on laptops! I.e. various ACPI things where
+ * SMI is used for communication between OS and firmware.
+ *
+ * Don't use this fix if you don't need to!!!
+ */
+
+static void broken_bios_start(unsigned long acpibase)
+{
+ unsigned long val32;
+
+ val32 = inl(SMI_EN);
+ /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI#
+ Bit 0: GBL_SMI_EN -> 0 = No SMI# will be generated by ICH. */
+ val32 &= 0xffffdffe;
+ outl(val32, SMI_EN);
+}
+
+static void broken_bios_stop(unsigned long acpibase)
+{
+ unsigned long val32;
+
+ val32 = inl(SMI_EN);
+ /* Bit 13: TCO_EN -> 1 = Enables TCO logic generating an SMI#
+ Bit 0: GBL_SMI_EN -> 1 = Turn global SMI on again. */
+ val32 |= 0x00002001;
+ outl(val32, SMI_EN);
+}
+
+/*
+ * Generic Support Functions
+ */
+
+void iTCO_vendor_pre_start(unsigned long acpibase,
+ unsigned int heartbeat)
+{
+ switch (vendorsupport) {
+ case SUPERMICRO_OLD_BOARD:
+ supermicro_old_pre_start(acpibase);
+ break;
+ case SUPERMICRO_NEW_BOARD:
+ supermicro_new_pre_start(heartbeat);
+ break;
+ case BROKEN_BIOS:
+ broken_bios_start(acpibase);
+ break;
+ }
+}
+EXPORT_SYMBOL(iTCO_vendor_pre_start);
+
+void iTCO_vendor_pre_stop(unsigned long acpibase)
+{
+ switch (vendorsupport) {
+ case SUPERMICRO_OLD_BOARD:
+ supermicro_old_pre_stop(acpibase);
+ break;
+ case SUPERMICRO_NEW_BOARD:
+ supermicro_new_pre_stop();
+ break;
+ case BROKEN_BIOS:
+ broken_bios_stop(acpibase);
+ break;
+ }
+}
+EXPORT_SYMBOL(iTCO_vendor_pre_stop);
+
+void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat)
+{
+ if (vendorsupport == SUPERMICRO_NEW_BOARD)
+ supermicro_new_pre_set_heartbeat(heartbeat);
+}
+EXPORT_SYMBOL(iTCO_vendor_pre_keepalive);
+
+void iTCO_vendor_pre_set_heartbeat(unsigned int heartbeat)
+{
+ if (vendorsupport == SUPERMICRO_NEW_BOARD)
+ supermicro_new_pre_set_heartbeat(heartbeat);
+}
+EXPORT_SYMBOL(iTCO_vendor_pre_set_heartbeat);
+
+int iTCO_vendor_check_noreboot_on(void)
+{
+ switch (vendorsupport) {
+ case SUPERMICRO_OLD_BOARD:
+ return 0;
+ default:
+ return 1;
+ }
+}
+EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on);
+
+static int __init iTCO_vendor_init_module(void)
+{
+ printk(KERN_INFO PFX "vendor-support=%d\n", vendorsupport);
+ return 0;
+}
+
+static void __exit iTCO_vendor_exit_module(void)
+{
+ printk(KERN_INFO PFX "Module Unloaded\n");
+}
+
+module_init(iTCO_vendor_init_module);
+module_exit(iTCO_vendor_exit_module);
+
+MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>, "
+ "R. Seretny <lkpatches@paypc.com>");
+MODULE_DESCRIPTION("Intel TCO Vendor Specific WatchDog Timer Driver Support");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
new file mode 100644
index 00000000..5fd020da
--- /dev/null
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -0,0 +1,1109 @@
+/*
+ * intel TCO Watchdog Driver
+ *
+ * (c) Copyright 2006-2010 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
+ * provide warranty for any of this software. This material is
+ * provided "AS-IS" and at no charge.
+ *
+ * The TCO watchdog is implemented in the following I/O controller hubs:
+ * (See the intel documentation on http://developer.intel.com.)
+ * document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO)
+ * document number 290687-002, 298242-027: 82801BA (ICH2)
+ * document number 290733-003, 290739-013: 82801CA (ICH3-S)
+ * document number 290716-001, 290718-007: 82801CAM (ICH3-M)
+ * document number 290744-001, 290745-025: 82801DB (ICH4)
+ * document number 252337-001, 252663-008: 82801DBM (ICH4-M)
+ * document number 273599-001, 273645-002: 82801E (C-ICH)
+ * document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R)
+ * document number 300641-004, 300884-013: 6300ESB
+ * document number 301473-002, 301474-026: 82801F (ICH6)
+ * document number 313082-001, 313075-006: 631xESB, 632xESB
+ * document number 307013-003, 307014-024: 82801G (ICH7)
+ * document number 322896-001, 322897-001: NM10
+ * document number 313056-003, 313057-017: 82801H (ICH8)
+ * document number 316972-004, 316973-012: 82801I (ICH9)
+ * document number 319973-002, 319974-002: 82801J (ICH10)
+ * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH)
+ * document number 320066-003, 320257-008: EP80597 (IICH)
+ * document number 324645-001, 324646-001: Cougar Point (CPT)
+ * document number TBD : Patsburg (PBG)
+ * document number TBD : DH89xxCC
+ * document number TBD : Panther Point
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+/* Module and version information */
+#define DRV_NAME "iTCO_wdt"
+#define DRV_VERSION "1.06"
+#define PFX DRV_NAME ": "
+
+/* Includes */
+#include <linux/module.h> /* For module specific items */
+#include <linux/moduleparam.h> /* For new moduleparam's */
+#include <linux/types.h> /* For standard types (like size_t) */
+#include <linux/errno.h> /* For the -ENODEV/... values */
+#include <linux/kernel.h> /* For printk/panic/... */
+#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
+ (WATCHDOG_MINOR) */
+#include <linux/watchdog.h> /* For the watchdog specific items */
+#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/fs.h> /* For file operations */
+#include <linux/platform_device.h> /* For platform_driver framework */
+#include <linux/pci.h> /* For pci functions */
+#include <linux/ioport.h> /* For io-port access */
+#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
+#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+#include <linux/io.h> /* For inb/outb/... */
+
+#include "iTCO_vendor.h"
+
+/* TCO related info */
+enum iTCO_chipsets {
+ TCO_ICH = 0, /* ICH */
+ TCO_ICH0, /* ICH0 */
+ TCO_ICH2, /* ICH2 */
+ TCO_ICH2M, /* ICH2-M */
+ TCO_ICH3, /* ICH3-S */
+ TCO_ICH3M, /* ICH3-M */
+ TCO_ICH4, /* ICH4 */
+ TCO_ICH4M, /* ICH4-M */
+ TCO_CICH, /* C-ICH */
+ TCO_ICH5, /* ICH5 & ICH5R */
+ TCO_6300ESB, /* 6300ESB */
+ TCO_ICH6, /* ICH6 & ICH6R */
+ TCO_ICH6M, /* ICH6-M */
+ TCO_ICH6W, /* ICH6W & ICH6RW */
+ TCO_631XESB, /* 631xESB/632xESB */
+ TCO_ICH7, /* ICH7 & ICH7R */
+ TCO_ICH7DH, /* ICH7DH */
+ TCO_ICH7M, /* ICH7-M & ICH7-U */
+ TCO_ICH7MDH, /* ICH7-M DH */
+ TCO_NM10, /* NM10 */
+ TCO_ICH8, /* ICH8 & ICH8R */
+ TCO_ICH8DH, /* ICH8DH */
+ TCO_ICH8DO, /* ICH8DO */
+ TCO_ICH8M, /* ICH8M */
+ TCO_ICH8ME, /* ICH8M-E */
+ TCO_ICH9, /* ICH9 */
+ TCO_ICH9R, /* ICH9R */
+ TCO_ICH9DH, /* ICH9DH */
+ TCO_ICH9DO, /* ICH9DO */
+ TCO_ICH9M, /* ICH9M */
+ TCO_ICH9ME, /* ICH9M-E */
+ TCO_ICH10, /* ICH10 */
+ TCO_ICH10R, /* ICH10R */
+ TCO_ICH10D, /* ICH10D */
+ TCO_ICH10DO, /* ICH10DO */
+ TCO_PCH, /* PCH Desktop Full Featured */
+ TCO_PCHM, /* PCH Mobile Full Featured */
+ TCO_P55, /* P55 */
+ TCO_PM55, /* PM55 */
+ TCO_H55, /* H55 */
+ TCO_QM57, /* QM57 */
+ TCO_H57, /* H57 */
+ TCO_HM55, /* HM55 */
+ TCO_Q57, /* Q57 */
+ TCO_HM57, /* HM57 */
+ TCO_PCHMSFF, /* PCH Mobile SFF Full Featured */
+ TCO_QS57, /* QS57 */
+ TCO_3400, /* 3400 */
+ TCO_3420, /* 3420 */
+ TCO_3450, /* 3450 */
+ TCO_EP80579, /* EP80579 */
+ TCO_CPT1, /* Cougar Point */
+ TCO_CPT2, /* Cougar Point Desktop */
+ TCO_CPT3, /* Cougar Point Mobile */
+ TCO_CPT4, /* Cougar Point */
+ TCO_CPT5, /* Cougar Point */
+ TCO_CPT6, /* Cougar Point */
+ TCO_CPT7, /* Cougar Point */
+ TCO_CPT8, /* Cougar Point */
+ TCO_CPT9, /* Cougar Point */
+ TCO_CPT10, /* Cougar Point */
+ TCO_CPT11, /* Cougar Point */
+ TCO_CPT12, /* Cougar Point */
+ TCO_CPT13, /* Cougar Point */
+ TCO_CPT14, /* Cougar Point */
+ TCO_CPT15, /* Cougar Point */
+ TCO_CPT16, /* Cougar Point */
+ TCO_CPT17, /* Cougar Point */
+ TCO_CPT18, /* Cougar Point */
+ TCO_CPT19, /* Cougar Point */
+ TCO_CPT20, /* Cougar Point */
+ TCO_CPT21, /* Cougar Point */
+ TCO_CPT22, /* Cougar Point */
+ TCO_CPT23, /* Cougar Point */
+ TCO_CPT24, /* Cougar Point */
+ TCO_CPT25, /* Cougar Point */
+ TCO_CPT26, /* Cougar Point */
+ TCO_CPT27, /* Cougar Point */
+ TCO_CPT28, /* Cougar Point */
+ TCO_CPT29, /* Cougar Point */
+ TCO_CPT30, /* Cougar Point */
+ TCO_CPT31, /* Cougar Point */
+ TCO_PBG1, /* Patsburg */
+ TCO_PBG2, /* Patsburg */
+ TCO_DH89XXCC, /* DH89xxCC */
+ TCO_PPT0, /* Panther Point */
+ TCO_PPT1, /* Panther Point */
+ TCO_PPT2, /* Panther Point */
+ TCO_PPT3, /* Panther Point */
+ TCO_PPT4, /* Panther Point */
+ TCO_PPT5, /* Panther Point */
+ TCO_PPT6, /* Panther Point */
+ TCO_PPT7, /* Panther Point */
+ TCO_PPT8, /* Panther Point */
+ TCO_PPT9, /* Panther Point */
+ TCO_PPT10, /* Panther Point */
+ TCO_PPT11, /* Panther Point */
+ TCO_PPT12, /* Panther Point */
+ TCO_PPT13, /* Panther Point */
+ TCO_PPT14, /* Panther Point */
+ TCO_PPT15, /* Panther Point */
+ TCO_PPT16, /* Panther Point */
+ TCO_PPT17, /* Panther Point */
+ TCO_PPT18, /* Panther Point */
+ TCO_PPT19, /* Panther Point */
+ TCO_PPT20, /* Panther Point */
+ TCO_PPT21, /* Panther Point */
+ TCO_PPT22, /* Panther Point */
+ TCO_PPT23, /* Panther Point */
+ TCO_PPT24, /* Panther Point */
+ TCO_PPT25, /* Panther Point */
+ TCO_PPT26, /* Panther Point */
+ TCO_PPT27, /* Panther Point */
+ TCO_PPT28, /* Panther Point */
+ TCO_PPT29, /* Panther Point */
+ TCO_PPT30, /* Panther Point */
+ TCO_PPT31, /* Panther Point */
+};
+
+static struct {
+ char *name;
+ unsigned int iTCO_version;
+} iTCO_chipset_info[] __devinitdata = {
+ {"ICH", 1},
+ {"ICH0", 1},
+ {"ICH2", 1},
+ {"ICH2-M", 1},
+ {"ICH3-S", 1},
+ {"ICH3-M", 1},
+ {"ICH4", 1},
+ {"ICH4-M", 1},
+ {"C-ICH", 1},
+ {"ICH5 or ICH5R", 1},
+ {"6300ESB", 1},
+ {"ICH6 or ICH6R", 2},
+ {"ICH6-M", 2},
+ {"ICH6W or ICH6RW", 2},
+ {"631xESB/632xESB", 2},
+ {"ICH7 or ICH7R", 2},
+ {"ICH7DH", 2},
+ {"ICH7-M or ICH7-U", 2},
+ {"ICH7-M DH", 2},
+ {"NM10", 2},
+ {"ICH8 or ICH8R", 2},
+ {"ICH8DH", 2},
+ {"ICH8DO", 2},
+ {"ICH8M", 2},
+ {"ICH8M-E", 2},
+ {"ICH9", 2},
+ {"ICH9R", 2},
+ {"ICH9DH", 2},
+ {"ICH9DO", 2},
+ {"ICH9M", 2},
+ {"ICH9M-E", 2},
+ {"ICH10", 2},
+ {"ICH10R", 2},
+ {"ICH10D", 2},
+ {"ICH10DO", 2},
+ {"PCH Desktop Full Featured", 2},
+ {"PCH Mobile Full Featured", 2},
+ {"P55", 2},
+ {"PM55", 2},
+ {"H55", 2},
+ {"QM57", 2},
+ {"H57", 2},
+ {"HM55", 2},
+ {"Q57", 2},
+ {"HM57", 2},
+ {"PCH Mobile SFF Full Featured", 2},
+ {"QS57", 2},
+ {"3400", 2},
+ {"3420", 2},
+ {"3450", 2},
+ {"EP80579", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Cougar Point", 2},
+ {"Patsburg", 2},
+ {"Patsburg", 2},
+ {"DH89xxCC", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {NULL, 0}
+};
+
+#define ITCO_PCI_DEVICE(dev, data) \
+ .vendor = PCI_VENDOR_ID_INTEL, \
+ .device = dev, \
+ .subvendor = PCI_ANY_ID, \
+ .subdevice = PCI_ANY_ID, \
+ .class = 0, \
+ .class_mask = 0, \
+ .driver_data = data
+
+/*
+ * This data only exists for exporting the supported PCI ids
+ * via MODULE_DEVICE_TABLE. We do not actually register a
+ * pci_driver, because the I/O Controller Hub has also other
+ * functions that probably will be registered by other drivers.
+ */
+static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = {
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AA_0, TCO_ICH)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AB_0, TCO_ICH0)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_0, TCO_ICH2)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_10, TCO_ICH2M)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_0, TCO_ICH3)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_12, TCO_ICH3M)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_0, TCO_ICH4)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_12, TCO_ICH4M)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801E_0, TCO_CICH)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801EB_0, TCO_ICH5)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB_1, TCO_6300ESB)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_0, TCO_ICH6)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_1, TCO_ICH6M)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_2, TCO_ICH6W)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB2_0, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x2671, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x2672, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x2673, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x2674, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x2675, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x2676, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x2677, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x2678, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x2679, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x267a, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x267b, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x267c, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x267d, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x267e, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(0x267f, TCO_631XESB)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0, TCO_ICH7)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)},
+ { ITCO_PCI_DEVICE(0x27bc, TCO_NM10)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4, TCO_ICH8M)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1, TCO_ICH8ME)},
+ { ITCO_PCI_DEVICE(0x2918, TCO_ICH9)},
+ { ITCO_PCI_DEVICE(0x2916, TCO_ICH9R)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2, TCO_ICH9DH)},
+ { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4, TCO_ICH9DO)},
+ { ITCO_PCI_DEVICE(0x2919, TCO_ICH9M)},
+ { ITCO_PCI_DEVICE(0x2917, TCO_ICH9ME)},
+ { ITCO_PCI_DEVICE(0x3a18, TCO_ICH10)},
+ { ITCO_PCI_DEVICE(0x3a16, TCO_ICH10R)},
+ { ITCO_PCI_DEVICE(0x3a1a, TCO_ICH10D)},
+ { ITCO_PCI_DEVICE(0x3a14, TCO_ICH10DO)},
+ { ITCO_PCI_DEVICE(0x3b00, TCO_PCH)},
+ { ITCO_PCI_DEVICE(0x3b01, TCO_PCHM)},
+ { ITCO_PCI_DEVICE(0x3b02, TCO_P55)},
+ { ITCO_PCI_DEVICE(0x3b03, TCO_PM55)},
+ { ITCO_PCI_DEVICE(0x3b06, TCO_H55)},
+ { ITCO_PCI_DEVICE(0x3b07, TCO_QM57)},
+ { ITCO_PCI_DEVICE(0x3b08, TCO_H57)},
+ { ITCO_PCI_DEVICE(0x3b09, TCO_HM55)},
+ { ITCO_PCI_DEVICE(0x3b0a, TCO_Q57)},
+ { ITCO_PCI_DEVICE(0x3b0b, TCO_HM57)},
+ { ITCO_PCI_DEVICE(0x3b0d, TCO_PCHMSFF)},
+ { ITCO_PCI_DEVICE(0x3b0f, TCO_QS57)},
+ { ITCO_PCI_DEVICE(0x3b12, TCO_3400)},
+ { ITCO_PCI_DEVICE(0x3b14, TCO_3420)},
+ { ITCO_PCI_DEVICE(0x3b16, TCO_3450)},
+ { ITCO_PCI_DEVICE(0x5031, TCO_EP80579)},
+ { ITCO_PCI_DEVICE(0x1c41, TCO_CPT1)},
+ { ITCO_PCI_DEVICE(0x1c42, TCO_CPT2)},
+ { ITCO_PCI_DEVICE(0x1c43, TCO_CPT3)},
+ { ITCO_PCI_DEVICE(0x1c44, TCO_CPT4)},
+ { ITCO_PCI_DEVICE(0x1c45, TCO_CPT5)},
+ { ITCO_PCI_DEVICE(0x1c46, TCO_CPT6)},
+ { ITCO_PCI_DEVICE(0x1c47, TCO_CPT7)},
+ { ITCO_PCI_DEVICE(0x1c48, TCO_CPT8)},
+ { ITCO_PCI_DEVICE(0x1c49, TCO_CPT9)},
+ { ITCO_PCI_DEVICE(0x1c4a, TCO_CPT10)},
+ { ITCO_PCI_DEVICE(0x1c4b, TCO_CPT11)},
+ { ITCO_PCI_DEVICE(0x1c4c, TCO_CPT12)},
+ { ITCO_PCI_DEVICE(0x1c4d, TCO_CPT13)},
+ { ITCO_PCI_DEVICE(0x1c4e, TCO_CPT14)},
+ { ITCO_PCI_DEVICE(0x1c4f, TCO_CPT15)},
+ { ITCO_PCI_DEVICE(0x1c50, TCO_CPT16)},
+ { ITCO_PCI_DEVICE(0x1c51, TCO_CPT17)},
+ { ITCO_PCI_DEVICE(0x1c52, TCO_CPT18)},
+ { ITCO_PCI_DEVICE(0x1c53, TCO_CPT19)},
+ { ITCO_PCI_DEVICE(0x1c54, TCO_CPT20)},
+ { ITCO_PCI_DEVICE(0x1c55, TCO_CPT21)},
+ { ITCO_PCI_DEVICE(0x1c56, TCO_CPT22)},
+ { ITCO_PCI_DEVICE(0x1c57, TCO_CPT23)},
+ { ITCO_PCI_DEVICE(0x1c58, TCO_CPT24)},
+ { ITCO_PCI_DEVICE(0x1c59, TCO_CPT25)},
+ { ITCO_PCI_DEVICE(0x1c5a, TCO_CPT26)},
+ { ITCO_PCI_DEVICE(0x1c5b, TCO_CPT27)},
+ { ITCO_PCI_DEVICE(0x1c5c, TCO_CPT28)},
+ { ITCO_PCI_DEVICE(0x1c5d, TCO_CPT29)},
+ { ITCO_PCI_DEVICE(0x1c5e, TCO_CPT30)},
+ { ITCO_PCI_DEVICE(0x1c5f, TCO_CPT31)},
+ { ITCO_PCI_DEVICE(0x1d40, TCO_PBG1)},
+ { ITCO_PCI_DEVICE(0x1d41, TCO_PBG2)},
+ { ITCO_PCI_DEVICE(0x2310, TCO_DH89XXCC)},
+ { ITCO_PCI_DEVICE(0x1e40, TCO_PPT0)},
+ { ITCO_PCI_DEVICE(0x1e41, TCO_PPT1)},
+ { ITCO_PCI_DEVICE(0x1e42, TCO_PPT2)},
+ { ITCO_PCI_DEVICE(0x1e43, TCO_PPT3)},
+ { ITCO_PCI_DEVICE(0x1e44, TCO_PPT4)},
+ { ITCO_PCI_DEVICE(0x1e45, TCO_PPT5)},
+ { ITCO_PCI_DEVICE(0x1e46, TCO_PPT6)},
+ { ITCO_PCI_DEVICE(0x1e47, TCO_PPT7)},
+ { ITCO_PCI_DEVICE(0x1e48, TCO_PPT8)},
+ { ITCO_PCI_DEVICE(0x1e49, TCO_PPT9)},
+ { ITCO_PCI_DEVICE(0x1e4a, TCO_PPT10)},
+ { ITCO_PCI_DEVICE(0x1e4b, TCO_PPT11)},
+ { ITCO_PCI_DEVICE(0x1e4c, TCO_PPT12)},
+ { ITCO_PCI_DEVICE(0x1e4d, TCO_PPT13)},
+ { ITCO_PCI_DEVICE(0x1e4e, TCO_PPT14)},
+ { ITCO_PCI_DEVICE(0x1e4f, TCO_PPT15)},
+ { ITCO_PCI_DEVICE(0x1e50, TCO_PPT16)},
+ { ITCO_PCI_DEVICE(0x1e51, TCO_PPT17)},
+ { ITCO_PCI_DEVICE(0x1e52, TCO_PPT18)},
+ { ITCO_PCI_DEVICE(0x1e53, TCO_PPT19)},
+ { ITCO_PCI_DEVICE(0x1e54, TCO_PPT20)},
+ { ITCO_PCI_DEVICE(0x1e55, TCO_PPT21)},
+ { ITCO_PCI_DEVICE(0x1e56, TCO_PPT22)},
+ { ITCO_PCI_DEVICE(0x1e57, TCO_PPT23)},
+ { ITCO_PCI_DEVICE(0x1e58, TCO_PPT24)},
+ { ITCO_PCI_DEVICE(0x1e59, TCO_PPT25)},
+ { ITCO_PCI_DEVICE(0x1e5a, TCO_PPT26)},
+ { ITCO_PCI_DEVICE(0x1e5b, TCO_PPT27)},
+ { ITCO_PCI_DEVICE(0x1e5c, TCO_PPT28)},
+ { ITCO_PCI_DEVICE(0x1e5d, TCO_PPT29)},
+ { ITCO_PCI_DEVICE(0x1e5e, TCO_PPT30)},
+ { ITCO_PCI_DEVICE(0x1e5f, TCO_PPT31)},
+ { 0, }, /* End of list */
+};
+MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
+
+/* Address definitions for the TCO */
+/* TCO base address */
+#define TCOBASE (iTCO_wdt_private.ACPIBASE + 0x60)
+/* SMI Control and Enable Register */
+#define SMI_EN (iTCO_wdt_private.ACPIBASE + 0x30)
+
+#define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */
+#define TCOv1_TMR (TCOBASE + 0x01) /* TCOv1 Timer Initial Value */
+#define TCO_DAT_IN (TCOBASE + 0x02) /* TCO Data In Register */
+#define TCO_DAT_OUT (TCOBASE + 0x03) /* TCO Data Out Register */
+#define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */
+#define TCO2_STS (TCOBASE + 0x06) /* TCO2 Status Register */
+#define TCO1_CNT (TCOBASE + 0x08) /* TCO1 Control Register */
+#define TCO2_CNT (TCOBASE + 0x0a) /* TCO2 Control Register */
+#define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */
+
+/* internal variables */
+static unsigned long is_active;
+static char expect_release;
+static struct { /* this is private data for the iTCO_wdt device */
+ /* TCO version/generation */
+ unsigned int iTCO_version;
+ /* The device's ACPIBASE address (TCOBASE = ACPIBASE+0x60) */
+ unsigned long ACPIBASE;
+ /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
+ unsigned long __iomem *gcs;
+ /* the lock for io operations */
+ spinlock_t io_lock;
+ /* the PCI-device */
+ struct pci_dev *pdev;
+} iTCO_wdt_private;
+
+/* the watchdog platform device */
+static struct platform_device *iTCO_wdt_platform_device;
+
+/* module parameters */
+#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
+static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. "
+ "5..76 (TCO v1) or 3..614 (TCO v2), default="
+ __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Some TCO specific functions
+ */
+
+static inline unsigned int seconds_to_ticks(int seconds)
+{
+ /* the internal timer is stored as ticks which decrement
+ * every 0.6 seconds */
+ return (seconds * 10) / 6;
+}
+
+static void iTCO_wdt_set_NO_REBOOT_bit(void)
+{
+ u32 val32;
+
+ /* Set the NO_REBOOT bit: this disables reboots */
+ if (iTCO_wdt_private.iTCO_version == 2) {
+ val32 = readl(iTCO_wdt_private.gcs);
+ val32 |= 0x00000020;
+ writel(val32, iTCO_wdt_private.gcs);
+ } else if (iTCO_wdt_private.iTCO_version == 1) {
+ pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
+ val32 |= 0x00000002;
+ pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
+ }
+}
+
+static int iTCO_wdt_unset_NO_REBOOT_bit(void)
+{
+ int ret = 0;
+ u32 val32;
+
+ /* Unset the NO_REBOOT bit: this enables reboots */
+ if (iTCO_wdt_private.iTCO_version == 2) {
+ val32 = readl(iTCO_wdt_private.gcs);
+ val32 &= 0xffffffdf;
+ writel(val32, iTCO_wdt_private.gcs);
+
+ val32 = readl(iTCO_wdt_private.gcs);
+ if (val32 & 0x00000020)
+ ret = -EIO;
+ } else if (iTCO_wdt_private.iTCO_version == 1) {
+ pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
+ val32 &= 0xfffffffd;
+ pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
+
+ pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
+ if (val32 & 0x00000002)
+ ret = -EIO;
+ }
+
+ return ret; /* returns: 0 = OK, -EIO = Error */
+}
+
+static int iTCO_wdt_start(void)
+{
+ unsigned int val;
+
+ spin_lock(&iTCO_wdt_private.io_lock);
+
+ iTCO_vendor_pre_start(iTCO_wdt_private.ACPIBASE, heartbeat);
+
+ /* disable chipset's NO_REBOOT bit */
+ if (iTCO_wdt_unset_NO_REBOOT_bit()) {
+ spin_unlock(&iTCO_wdt_private.io_lock);
+ printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, "
+ "reboot disabled by hardware/BIOS\n");
+ return -EIO;
+ }
+
+ /* Force the timer to its reload value by writing to the TCO_RLD
+ register */
+ if (iTCO_wdt_private.iTCO_version == 2)
+ outw(0x01, TCO_RLD);
+ else if (iTCO_wdt_private.iTCO_version == 1)
+ outb(0x01, TCO_RLD);
+
+ /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
+ val = inw(TCO1_CNT);
+ val &= 0xf7ff;
+ outw(val, TCO1_CNT);
+ val = inw(TCO1_CNT);
+ spin_unlock(&iTCO_wdt_private.io_lock);
+
+ if (val & 0x0800)
+ return -1;
+ return 0;
+}
+
+static int iTCO_wdt_stop(void)
+{
+ unsigned int val;
+
+ spin_lock(&iTCO_wdt_private.io_lock);
+
+ iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE);
+
+ /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
+ val = inw(TCO1_CNT);
+ val |= 0x0800;
+ outw(val, TCO1_CNT);
+ val = inw(TCO1_CNT);
+
+ /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
+ iTCO_wdt_set_NO_REBOOT_bit();
+
+ spin_unlock(&iTCO_wdt_private.io_lock);
+
+ if ((val & 0x0800) == 0)
+ return -1;
+ return 0;
+}
+
+static int iTCO_wdt_keepalive(void)
+{
+ spin_lock(&iTCO_wdt_private.io_lock);
+
+ iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat);
+
+ /* Reload the timer by writing to the TCO Timer Counter register */
+ if (iTCO_wdt_private.iTCO_version == 2)
+ outw(0x01, TCO_RLD);
+ else if (iTCO_wdt_private.iTCO_version == 1) {
+ /* Reset the timeout status bit so that the timer
+ * needs to count down twice again before rebooting */
+ outw(0x0008, TCO1_STS); /* write 1 to clear bit */
+
+ outb(0x01, TCO_RLD);
+ }
+
+ spin_unlock(&iTCO_wdt_private.io_lock);
+ return 0;
+}
+
+static int iTCO_wdt_set_heartbeat(int t)
+{
+ unsigned int val16;
+ unsigned char val8;
+ unsigned int tmrval;
+
+ tmrval = seconds_to_ticks(t);
+
+ /* For TCO v1 the timer counts down twice before rebooting */
+ if (iTCO_wdt_private.iTCO_version == 1)
+ tmrval /= 2;
+
+ /* from the specs: */
+ /* "Values of 0h-3h are ignored and should not be attempted" */
+ if (tmrval < 0x04)
+ return -EINVAL;
+ if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) ||
+ ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f)))
+ return -EINVAL;
+
+ iTCO_vendor_pre_set_heartbeat(tmrval);
+
+ /* Write new heartbeat to watchdog */
+ if (iTCO_wdt_private.iTCO_version == 2) {
+ spin_lock(&iTCO_wdt_private.io_lock);
+ val16 = inw(TCOv2_TMR);
+ val16 &= 0xfc00;
+ val16 |= tmrval;
+ outw(val16, TCOv2_TMR);
+ val16 = inw(TCOv2_TMR);
+ spin_unlock(&iTCO_wdt_private.io_lock);
+
+ if ((val16 & 0x3ff) != tmrval)
+ return -EINVAL;
+ } else if (iTCO_wdt_private.iTCO_version == 1) {
+ spin_lock(&iTCO_wdt_private.io_lock);
+ val8 = inb(TCOv1_TMR);
+ val8 &= 0xc0;
+ val8 |= (tmrval & 0xff);
+ outb(val8, TCOv1_TMR);
+ val8 = inb(TCOv1_TMR);
+ spin_unlock(&iTCO_wdt_private.io_lock);
+
+ if ((val8 & 0x3f) != tmrval)
+ return -EINVAL;
+ }
+
+ heartbeat = t;
+ return 0;
+}
+
+static int iTCO_wdt_get_timeleft(int *time_left)
+{
+ unsigned int val16;
+ unsigned char val8;
+
+ /* read the TCO Timer */
+ if (iTCO_wdt_private.iTCO_version == 2) {
+ spin_lock(&iTCO_wdt_private.io_lock);
+ val16 = inw(TCO_RLD);
+ val16 &= 0x3ff;
+ spin_unlock(&iTCO_wdt_private.io_lock);
+
+ *time_left = (val16 * 6) / 10;
+ } else if (iTCO_wdt_private.iTCO_version == 1) {
+ spin_lock(&iTCO_wdt_private.io_lock);
+ val8 = inb(TCO_RLD);
+ val8 &= 0x3f;
+ if (!(inw(TCO1_STS) & 0x0008))
+ val8 += (inb(TCOv1_TMR) & 0x3f);
+ spin_unlock(&iTCO_wdt_private.io_lock);
+
+ *time_left = (val8 * 6) / 10;
+ } else
+ return -EINVAL;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int iTCO_wdt_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &is_active))
+ return -EBUSY;
+
+ /*
+ * Reload and activate timer
+ */
+ iTCO_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int iTCO_wdt_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ */
+ if (expect_release == 42) {
+ iTCO_wdt_stop();
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ iTCO_wdt_keepalive();
+ }
+ clear_bit(0, &is_active);
+ expect_release = 0;
+ return 0;
+}
+
+static ssize_t iTCO_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* note: just in case someone wrote the magic
+ character five months ago... */
+ expect_release = 0;
+
+ /* scan to see whether or not we got the
+ magic character */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_release = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ iTCO_wdt_keepalive();
+ }
+ return len;
+}
+
+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,
+};
+
+/*
+ * Init & exit routines
+ */
+
+static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
+ const struct pci_device_id *ent, struct platform_device *dev)
+{
+ int ret;
+ u32 base_address;
+ unsigned long RCBA;
+ 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 */
+ printk(KERN_ERR PFX "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;
+
+ /* Get the Memory-Mapped GCS register, we need it for the
+ NO_REBOOT flag (TCO v2). To get access to it you have to
+ read RCBA from PCI Config space 0xf0 and use it as base.
+ GCS = RCBA + ICH6_GCS(0x3410). */
+ if (iTCO_wdt_private.iTCO_version == 2) {
+ pci_read_config_dword(pdev, 0xf0, &base_address);
+ if ((base_address & 1) == 0) {
+ printk(KERN_ERR PFX "RCBA is disabled by hardware"
+ "/BIOS, device disabled\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ RCBA = base_address & 0xffffc000;
+ iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4);
+ }
+
+ /* Check chipset's NO_REBOOT bit */
+ if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
+ printk(KERN_INFO PFX "unable to reset NO_REBOOT flag, "
+ "device disabled by hardware/BIOS\n");
+ ret = -ENODEV; /* Cannot reset NO_REBOOT bit */
+ goto out_unmap;
+ }
+
+ /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
+ iTCO_wdt_set_NO_REBOOT_bit();
+
+ /* The TCO logic uses the TCO_EN bit in the SMI_EN register */
+ if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
+ printk(KERN_ERR PFX
+ "I/O address 0x%04lx already in use, "
+ "device disabled\n", SMI_EN);
+ ret = -EIO;
+ goto out_unmap;
+ }
+ /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
+ val32 = inl(SMI_EN);
+ val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
+ outl(val32, SMI_EN);
+
+ /* The TCO I/O registers reside in a 32-byte range pointed to
+ by the TCOBASE value */
+ if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
+ printk(KERN_ERR PFX "I/O address 0x%04lx already in use "
+ "device disabled\n", TCOBASE);
+ ret = -EIO;
+ goto unreg_smi_en;
+ }
+
+ printk(KERN_INFO PFX
+ "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);
+
+ /* Clear out the (probably old) status */
+ outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
+ outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
+ outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
+
+ /* Make sure the watchdog is not running */
+ iTCO_wdt_stop();
+
+ /* Check that the heartbeat value is within it's range;
+ if not reset to the default */
+ if (iTCO_wdt_set_heartbeat(heartbeat)) {
+ iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT);
+ printk(KERN_INFO PFX
+ "timeout value out of range, using %d\n", heartbeat);
+ }
+
+ ret = misc_register(&iTCO_wdt_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto unreg_region;
+ }
+
+ printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ heartbeat, nowayout);
+
+ return 0;
+
+unreg_region:
+ release_region(TCOBASE, 0x20);
+unreg_smi_en:
+ release_region(SMI_EN, 4);
+out_unmap:
+ if (iTCO_wdt_private.iTCO_version == 2)
+ iounmap(iTCO_wdt_private.gcs);
+out:
+ iTCO_wdt_private.ACPIBASE = 0;
+ return ret;
+}
+
+static void __devexit iTCO_wdt_cleanup(void)
+{
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ iTCO_wdt_stop();
+
+ /* Deregister */
+ misc_deregister(&iTCO_wdt_miscdev);
+ release_region(TCOBASE, 0x20);
+ release_region(SMI_EN, 4);
+ if (iTCO_wdt_private.iTCO_version == 2)
+ iounmap(iTCO_wdt_private.gcs);
+ pci_dev_put(iTCO_wdt_private.pdev);
+ iTCO_wdt_private.ACPIBASE = 0;
+}
+
+static int __devinit 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);
+ if (!ret)
+ break;
+ }
+ }
+
+ if (!found)
+ printk(KERN_INFO PFX "No device detected.\n");
+
+ return ret;
+}
+
+static int __devexit iTCO_wdt_remove(struct platform_device *dev)
+{
+ if (iTCO_wdt_private.ACPIBASE)
+ iTCO_wdt_cleanup();
+
+ return 0;
+}
+
+static void iTCO_wdt_shutdown(struct platform_device *dev)
+{
+ iTCO_wdt_stop();
+}
+
+#define iTCO_wdt_suspend NULL
+#define iTCO_wdt_resume NULL
+
+static struct platform_driver iTCO_wdt_driver = {
+ .probe = iTCO_wdt_probe,
+ .remove = __devexit_p(iTCO_wdt_remove),
+ .shutdown = iTCO_wdt_shutdown,
+ .suspend = iTCO_wdt_suspend,
+ .resume = iTCO_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+static int __init iTCO_wdt_init_module(void)
+{
+ int err;
+
+ printk(KERN_INFO PFX "Intel TCO WatchDog Timer Driver v%s\n",
+ DRV_VERSION);
+
+ err = platform_driver_register(&iTCO_wdt_driver);
+ if (err)
+ return err;
+
+ iTCO_wdt_platform_device = platform_device_register_simple(DRV_NAME,
+ -1, NULL, 0);
+ if (IS_ERR(iTCO_wdt_platform_device)) {
+ err = PTR_ERR(iTCO_wdt_platform_device);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&iTCO_wdt_driver);
+ return err;
+}
+
+static void __exit iTCO_wdt_cleanup_module(void)
+{
+ platform_device_unregister(iTCO_wdt_platform_device);
+ platform_driver_unregister(&iTCO_wdt_driver);
+ printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+}
+
+module_init(iTCO_wdt_init_module);
+module_exit(iTCO_wdt_cleanup_module);
+
+MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
+MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c
new file mode 100644
index 00000000..0149d8df
--- /dev/null
+++ b/drivers/watchdog/ib700wdt.c
@@ -0,0 +1,392 @@
+/*
+ * IB700 Single Board Computer WDT driver
+ *
+ * (c) Copyright 2001 Charles Howes <chowes@vsol.net>
+ *
+ * Based on advantechwdt.c which is based on acquirewdt.c which
+ * is based on wdt.c.
+ *
+ * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ * Based on acquirewdt.c which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
+ * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ * Added timeout module option to override default
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+static struct platform_device *ibwdt_platform_device;
+static unsigned long ibwdt_is_open;
+static DEFINE_SPINLOCK(ibwdt_lock);
+static char expect_close;
+
+/* Module information */
+#define DRV_NAME "ib700wdt"
+#define PFX DRV_NAME ": "
+
+/*
+ *
+ * Watchdog Timer Configuration
+ *
+ * The function of the watchdog timer is to reset the system
+ * automatically and is defined at I/O port 0443H. To enable the
+ * watchdog timer and allow the system to reset, write I/O port 0443H.
+ * To disable the timer, write I/O port 0441H for the system to stop the
+ * watchdog function. The timer has a tolerance of 20% for its
+ * intervals.
+ *
+ * The following describes how the timer should be programmed.
+ *
+ * Enabling Watchdog:
+ * MOV AX,000FH (Choose the values from 0 to F)
+ * MOV DX,0443H
+ * OUT DX,AX
+ *
+ * Disabling Watchdog:
+ * MOV AX,000FH (Any value is fine.)
+ * MOV DX,0441H
+ * OUT DX,AX
+ *
+ * Watchdog timer control table:
+ * Level Value Time/sec | Level Value Time/sec
+ * 1 F 0 | 9 7 16
+ * 2 E 2 | 10 6 18
+ * 3 D 4 | 11 5 20
+ * 4 C 6 | 12 4 22
+ * 5 B 8 | 13 3 24
+ * 6 A 10 | 14 2 26
+ * 7 9 12 | 15 1 28
+ * 8 8 14 | 16 0 30
+ *
+ */
+
+#define WDT_STOP 0x441
+#define WDT_START 0x443
+
+/* Default timeout */
+#define WATCHDOG_TIMEOUT 30 /* 30 seconds +/- 20% */
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 0<= timeout <=30, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+
+/*
+ * Watchdog Operations
+ */
+
+static void ibwdt_ping(void)
+{
+ int wd_margin = 15 - ((timeout + 1) / 2);
+
+ spin_lock(&ibwdt_lock);
+
+ /* Write a watchdog value */
+ outb_p(wd_margin, WDT_START);
+
+ spin_unlock(&ibwdt_lock);
+}
+
+static void ibwdt_disable(void)
+{
+ spin_lock(&ibwdt_lock);
+ outb_p(0, WDT_STOP);
+ spin_unlock(&ibwdt_lock);
+}
+
+static int ibwdt_set_heartbeat(int t)
+{
+ if (t < 0 || t > 30)
+ return -EINVAL;
+
+ timeout = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t ibwdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ ibwdt_ping();
+ }
+ return count;
+}
+
+static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int new_margin;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
+ | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "IB700 WDT",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ ibwdt_disable();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ ibwdt_ping();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ ibwdt_ping();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, p))
+ return -EFAULT;
+ if (ibwdt_set_heartbeat(new_margin))
+ return -EINVAL;
+ ibwdt_ping();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int ibwdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &ibwdt_is_open))
+ return -EBUSY;
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Activate */
+ ibwdt_ping();
+ return nonseekable_open(inode, file);
+}
+
+static int ibwdt_close(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ ibwdt_disable();
+ } else {
+ printk(KERN_CRIT PFX
+ "WDT device closed unexpectedly. WDT will not stop!\n");
+ ibwdt_ping();
+ }
+ clear_bit(0, &ibwdt_is_open);
+ expect_close = 0;
+ return 0;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations ibwdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ibwdt_write,
+ .unlocked_ioctl = ibwdt_ioctl,
+ .open = ibwdt_open,
+ .release = ibwdt_close,
+};
+
+static struct miscdevice ibwdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ibwdt_fops,
+};
+
+/*
+ * Init & exit routines
+ */
+
+static int __devinit ibwdt_probe(struct platform_device *dev)
+{
+ int res;
+
+#if WDT_START != WDT_STOP
+ if (!request_region(WDT_STOP, 1, "IB700 WDT")) {
+ printk(KERN_ERR PFX "STOP method I/O %X is not available.\n",
+ WDT_STOP);
+ res = -EIO;
+ goto out_nostopreg;
+ }
+#endif
+
+ if (!request_region(WDT_START, 1, "IB700 WDT")) {
+ printk(KERN_ERR PFX "START method I/O %X is not available.\n",
+ WDT_START);
+ res = -EIO;
+ goto out_nostartreg;
+ }
+
+ /* Check that the heartbeat value is within it's range ;
+ * if not reset to the default */
+ if (ibwdt_set_heartbeat(timeout)) {
+ ibwdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 0<=x<=30, using %d\n", timeout);
+ }
+
+ res = misc_register(&ibwdt_miscdev);
+ if (res) {
+ printk(KERN_ERR PFX "failed to register misc device\n");
+ goto out_nomisc;
+ }
+ return 0;
+
+out_nomisc:
+ release_region(WDT_START, 1);
+out_nostartreg:
+#if WDT_START != WDT_STOP
+ release_region(WDT_STOP, 1);
+#endif
+out_nostopreg:
+ return res;
+}
+
+static int __devexit ibwdt_remove(struct platform_device *dev)
+{
+ misc_deregister(&ibwdt_miscdev);
+ release_region(WDT_START, 1);
+#if WDT_START != WDT_STOP
+ release_region(WDT_STOP, 1);
+#endif
+ return 0;
+}
+
+static void ibwdt_shutdown(struct platform_device *dev)
+{
+ /* Turn the WDT off if we have a soft shutdown */
+ ibwdt_disable();
+}
+
+static struct platform_driver ibwdt_driver = {
+ .probe = ibwdt_probe,
+ .remove = __devexit_p(ibwdt_remove),
+ .shutdown = ibwdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+static int __init ibwdt_init(void)
+{
+ int err;
+
+ printk(KERN_INFO PFX
+ "WDT driver for IB700 single board computer initialising.\n");
+
+ err = platform_driver_register(&ibwdt_driver);
+ if (err)
+ return err;
+
+ ibwdt_platform_device = platform_device_register_simple(DRV_NAME,
+ -1, NULL, 0);
+ if (IS_ERR(ibwdt_platform_device)) {
+ err = PTR_ERR(ibwdt_platform_device);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&ibwdt_driver);
+ return err;
+}
+
+static void __exit ibwdt_exit(void)
+{
+ platform_device_unregister(ibwdt_platform_device);
+ platform_driver_unregister(&ibwdt_driver);
+ printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+}
+
+module_init(ibwdt_init);
+module_exit(ibwdt_exit);
+
+MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
+MODULE_DESCRIPTION("IB700 SBC watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+/* end of ib700wdt.c */
diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c
new file mode 100644
index 00000000..195e0f79
--- /dev/null
+++ b/drivers/watchdog/ibmasr.c
@@ -0,0 +1,426 @@
+/*
+ * IBM Automatic Server Restart driver.
+ *
+ * Copyright (c) 2005 Andrey Panin <pazke@donpac.ru>
+ *
+ * Based on driver written by Pete Reynolds.
+ * Copyright (c) IBM Corporation, 1998-2004.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/dmi.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+
+enum {
+ ASMTYPE_UNKNOWN,
+ ASMTYPE_TOPAZ,
+ ASMTYPE_JASPER,
+ ASMTYPE_PEARL,
+ ASMTYPE_JUNIPER,
+ ASMTYPE_SPRUCE,
+};
+
+#define PFX "ibmasr: "
+
+#define TOPAZ_ASR_REG_OFFSET 4
+#define TOPAZ_ASR_TOGGLE 0x40
+#define TOPAZ_ASR_DISABLE 0x80
+
+/* PEARL ASR S/W REGISTER SUPERIO PORT ADDRESSES */
+#define PEARL_BASE 0xe04
+#define PEARL_WRITE 0xe06
+#define PEARL_READ 0xe07
+
+#define PEARL_ASR_DISABLE_MASK 0x80 /* bit 7: disable = 1, enable = 0 */
+#define PEARL_ASR_TOGGLE_MASK 0x40 /* bit 6: 0, then 1, then 0 */
+
+/* JASPER OFFSET FROM SIO BASE ADDR TO ASR S/W REGISTERS. */
+#define JASPER_ASR_REG_OFFSET 0x38
+
+#define JASPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1, enable = 0 */
+#define JASPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */
+
+#define JUNIPER_BASE_ADDRESS 0x54b /* Base address of Juniper ASR */
+#define JUNIPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1 enable = 0 */
+#define JUNIPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */
+
+#define SPRUCE_BASE_ADDRESS 0x118e /* Base address of Spruce ASR */
+#define SPRUCE_ASR_DISABLE_MASK 0x01 /* bit 1: disable = 1 enable = 0 */
+#define SPRUCE_ASR_TOGGLE_MASK 0x02 /* bit 0: 0, then 1, then 0 */
+
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+static unsigned long asr_is_open;
+static char asr_expect_close;
+
+static unsigned int asr_type, asr_base, asr_length;
+static unsigned int asr_read_addr, asr_write_addr;
+static unsigned char asr_toggle_mask, asr_disable_mask;
+static spinlock_t asr_lock;
+
+static void __asr_toggle(void)
+{
+ unsigned char reg;
+
+ reg = inb(asr_read_addr);
+
+ outb(reg & ~asr_toggle_mask, asr_write_addr);
+ reg = inb(asr_read_addr);
+
+ outb(reg | asr_toggle_mask, asr_write_addr);
+ reg = inb(asr_read_addr);
+
+ outb(reg & ~asr_toggle_mask, asr_write_addr);
+ reg = inb(asr_read_addr);
+}
+
+static void asr_toggle(void)
+{
+ spin_lock(&asr_lock);
+ __asr_toggle();
+ spin_unlock(&asr_lock);
+}
+
+static void asr_enable(void)
+{
+ unsigned char reg;
+
+ spin_lock(&asr_lock);
+ if (asr_type == ASMTYPE_TOPAZ) {
+ /* asr_write_addr == asr_read_addr */
+ reg = inb(asr_read_addr);
+ outb(reg & ~(TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE),
+ asr_read_addr);
+ } else {
+ /*
+ * First make sure the hardware timer is reset by toggling
+ * ASR hardware timer line.
+ */
+ __asr_toggle();
+
+ reg = inb(asr_read_addr);
+ outb(reg & ~asr_disable_mask, asr_write_addr);
+ }
+ reg = inb(asr_read_addr);
+ spin_unlock(&asr_lock);
+}
+
+static void asr_disable(void)
+{
+ unsigned char reg;
+
+ spin_lock(&asr_lock);
+ reg = inb(asr_read_addr);
+
+ if (asr_type == ASMTYPE_TOPAZ)
+ /* asr_write_addr == asr_read_addr */
+ outb(reg | TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE,
+ asr_read_addr);
+ else {
+ outb(reg | asr_toggle_mask, asr_write_addr);
+ reg = inb(asr_read_addr);
+
+ outb(reg | asr_disable_mask, asr_write_addr);
+ }
+ reg = inb(asr_read_addr);
+ spin_unlock(&asr_lock);
+}
+
+static int __init asr_get_base_address(void)
+{
+ unsigned char low, high;
+ const char *type = "";
+
+ asr_length = 1;
+
+ switch (asr_type) {
+ case ASMTYPE_TOPAZ:
+ /* SELECT SuperIO CHIP FOR QUERYING
+ (WRITE 0x07 TO BOTH 0x2E and 0x2F) */
+ outb(0x07, 0x2e);
+ outb(0x07, 0x2f);
+
+ /* SELECT AND READ THE HIGH-NIBBLE OF THE GPIO BASE ADDRESS */
+ outb(0x60, 0x2e);
+ high = inb(0x2f);
+
+ /* SELECT AND READ THE LOW-NIBBLE OF THE GPIO BASE ADDRESS */
+ outb(0x61, 0x2e);
+ low = inb(0x2f);
+
+ asr_base = (high << 16) | low;
+ asr_read_addr = asr_write_addr =
+ asr_base + TOPAZ_ASR_REG_OFFSET;
+ asr_length = 5;
+
+ break;
+
+ case ASMTYPE_JASPER:
+ type = "Jaspers ";
+#if 0
+ u32 r;
+ /* Suggested fix */
+ pdev = pci_get_bus_and_slot(0, DEVFN(0x1f, 0));
+ if (pdev == NULL)
+ return -ENODEV;
+ pci_read_config_dword(pdev, 0x58, &r);
+ asr_base = r & 0xFFFE;
+ pci_dev_put(pdev);
+#else
+ /* FIXME: need to use pci_config_lock here,
+ but it's not exported */
+
+/* spin_lock_irqsave(&pci_config_lock, flags);*/
+
+ /* Select the SuperIO chip in the PCI I/O port register */
+ outl(0x8000f858, 0xcf8);
+
+ /* BUS 0, Slot 1F, fnc 0, offset 58 */
+
+ /*
+ * Read the base address for the SuperIO chip.
+ * Only the lower 16 bits are valid, but the address is word
+ * aligned so the last bit must be masked off.
+ */
+ asr_base = inl(0xcfc) & 0xfffe;
+
+/* spin_unlock_irqrestore(&pci_config_lock, flags);*/
+#endif
+ asr_read_addr = asr_write_addr =
+ asr_base + JASPER_ASR_REG_OFFSET;
+ asr_toggle_mask = JASPER_ASR_TOGGLE_MASK;
+ asr_disable_mask = JASPER_ASR_DISABLE_MASK;
+ asr_length = JASPER_ASR_REG_OFFSET + 1;
+
+ break;
+
+ case ASMTYPE_PEARL:
+ type = "Pearls ";
+ asr_base = PEARL_BASE;
+ asr_read_addr = PEARL_READ;
+ asr_write_addr = PEARL_WRITE;
+ asr_toggle_mask = PEARL_ASR_TOGGLE_MASK;
+ asr_disable_mask = PEARL_ASR_DISABLE_MASK;
+ asr_length = 4;
+ break;
+
+ case ASMTYPE_JUNIPER:
+ type = "Junipers ";
+ asr_base = JUNIPER_BASE_ADDRESS;
+ asr_read_addr = asr_write_addr = asr_base;
+ asr_toggle_mask = JUNIPER_ASR_TOGGLE_MASK;
+ asr_disable_mask = JUNIPER_ASR_DISABLE_MASK;
+ break;
+
+ case ASMTYPE_SPRUCE:
+ type = "Spruce's ";
+ asr_base = SPRUCE_BASE_ADDRESS;
+ asr_read_addr = asr_write_addr = asr_base;
+ asr_toggle_mask = SPRUCE_ASR_TOGGLE_MASK;
+ asr_disable_mask = SPRUCE_ASR_DISABLE_MASK;
+ break;
+ }
+
+ if (!request_region(asr_base, asr_length, "ibmasr")) {
+ printk(KERN_ERR PFX "address %#x already in use\n",
+ asr_base);
+ return -EBUSY;
+ }
+
+ printk(KERN_INFO PFX "found %sASR @ addr %#x\n", type, asr_base);
+
+ return 0;
+}
+
+
+static ssize_t asr_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ asr_expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ asr_expect_close = 42;
+ }
+ }
+ asr_toggle();
+ }
+ return count;
+}
+
+static long asr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .identity = "IBM ASR",
+ };
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int heartbeat;
+
+ 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:
+ {
+ int new_options, retval = -EINVAL;
+ if (get_user(new_options, p))
+ return -EFAULT;
+ if (new_options & WDIOS_DISABLECARD) {
+ asr_disable();
+ retval = 0;
+ }
+ if (new_options & WDIOS_ENABLECARD) {
+ asr_enable();
+ asr_toggle();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ asr_toggle();
+ return 0;
+ /*
+ * The hardware has a fixed timeout value, so no WDIOC_SETTIMEOUT
+ * and WDIOC_GETTIMEOUT always returns 256.
+ */
+ case WDIOC_GETTIMEOUT:
+ heartbeat = 256;
+ return put_user(heartbeat, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int asr_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &asr_is_open))
+ return -EBUSY;
+
+ asr_toggle();
+ asr_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static int asr_release(struct inode *inode, struct file *file)
+{
+ if (asr_expect_close == 42)
+ asr_disable();
+ else {
+ printk(KERN_CRIT PFX
+ "unexpected close, not stopping watchdog!\n");
+ asr_toggle();
+ }
+ clear_bit(0, &asr_is_open);
+ asr_expect_close = 0;
+ return 0;
+}
+
+static const struct file_operations asr_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = asr_write,
+ .unlocked_ioctl = asr_ioctl,
+ .open = asr_open,
+ .release = asr_release,
+};
+
+static struct miscdevice asr_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &asr_fops,
+};
+
+
+struct ibmasr_id {
+ const char *desc;
+ int type;
+};
+
+static struct ibmasr_id __initdata ibmasr_id_table[] = {
+ { "IBM Automatic Server Restart - eserver xSeries 220", ASMTYPE_TOPAZ },
+ { "IBM Automatic Server Restart - Machine Type 8673", ASMTYPE_PEARL },
+ { "IBM Automatic Server Restart - Machine Type 8480", ASMTYPE_JASPER },
+ { "IBM Automatic Server Restart - Machine Type 8482", ASMTYPE_JUNIPER },
+ { "IBM Automatic Server Restart - Machine Type 8648", ASMTYPE_SPRUCE },
+ { NULL }
+};
+
+static int __init ibmasr_init(void)
+{
+ struct ibmasr_id *id;
+ int rc;
+
+ for (id = ibmasr_id_table; id->desc; id++) {
+ if (dmi_find_device(DMI_DEV_TYPE_OTHER, id->desc, NULL)) {
+ asr_type = id->type;
+ break;
+ }
+ }
+
+ if (!asr_type)
+ return -ENODEV;
+
+ spin_lock_init(&asr_lock);
+
+ rc = asr_get_base_address();
+ if (rc)
+ return rc;
+
+ rc = misc_register(&asr_miscdev);
+ if (rc < 0) {
+ release_region(asr_base, asr_length);
+ printk(KERN_ERR PFX "failed to register misc device\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static void __exit ibmasr_exit(void)
+{
+ if (!nowayout)
+ asr_disable();
+
+ misc_deregister(&asr_miscdev);
+
+ release_region(asr_base, asr_length);
+}
+
+module_init(ibmasr_init);
+module_exit(ibmasr_exit);
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+MODULE_DESCRIPTION("IBM Automatic Server Restart driver");
+MODULE_AUTHOR("Andrey Panin");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
new file mode 100644
index 00000000..e4de28b4
--- /dev/null
+++ b/drivers/watchdog/imx2_wdt.c
@@ -0,0 +1,426 @@
+/*
+ * Watchdog driver for IMX2 and later processors
+ *
+ * Copyright (C) 2010 Wolfram Sang, Pengutronix e.K. <w.sang@pengutronix.de>
+ *
+ * some parts adapted by similar drivers from Darius Augulis and Vladimir
+ * Zapolskiy, additional improvements by Wim Van Sebroeck.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * NOTE: MX1 has a slightly different Watchdog than MX2 and later:
+ *
+ * MX1: MX2+:
+ * ---- -----
+ * Registers: 32-bit 16-bit
+ * Stopable timer: Yes No
+ * Need to enable clk: No Yes
+ * Halt on suspend: Manual Can be automatic
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+#define DRIVER_NAME "imx2-wdt"
+
+#define IMX2_WDT_WCR 0x00 /* Control Register */
+#define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */
+#define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */
+#define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */
+#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */
+
+
+#define IMX2_WDT_WSR 0x02 /* Service Register */
+#define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */
+#define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */
+
+#define IMX2_WDT_WICR 0x06 /*Interrupt Control Register*/
+#define IMX2_WDT_WICR_WIE (1 << 15) /* -> Interrupt Enable */
+#define IMX2_WDT_WICR_WTIS (1 << 14) /* -> Interrupt Status */
+#define IMX2_WDT_WICR_WICT (0xFF << 0) /* -> Watchdog Interrupt Timeout Field */
+
+#define IMX2_WDT_MAX_TIME 128
+#define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */
+
+#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
+#define WDOG_SEC_TO_PRECOUNT(s) (s * 2) /* set WDOG pre timeout count*/
+
+#define IMX2_WDT_STATUS_OPEN 0
+#define IMX2_WDT_STATUS_STARTED 1
+#define IMX2_WDT_EXPECT_CLOSE 2
+
+static struct {
+ struct clk *clk;
+ void __iomem *base;
+ unsigned timeout;
+ unsigned pretimeout;
+ unsigned long status;
+ struct timer_list timer; /* Pings the watchdog when closed */
+} imx2_wdt;
+
+static struct miscdevice imx2_wdt_miscdev;
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+
+static unsigned timeout = IMX2_WDT_DEFAULT_TIME;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
+ __MODULE_STRING(IMX2_WDT_DEFAULT_TIME) ")");
+
+static const struct watchdog_info imx2_wdt_info = {
+ .identity = "imx2+ watchdog",
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_PRETIMEOUT,
+};
+
+static inline void imx2_wdt_setup(void)
+{
+ u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
+
+ /* Suspend watch dog timer in low power mode, write once-only */
+ val |= IMX2_WDT_WCR_WDZST;
+ /* Strip the old watchdog Time-Out value */
+ val &= ~IMX2_WDT_WCR_WT;
+ /* Generate reset if WDOG times out */
+ val &= ~IMX2_WDT_WCR_WRE;
+ /* Keep Watchdog Disabled */
+ val &= ~IMX2_WDT_WCR_WDE;
+ /* Set the watchdog's Time-Out value */
+ val |= WDOG_SEC_TO_COUNT(imx2_wdt.timeout);
+
+ __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
+
+ /* enable the watchdog */
+ val |= IMX2_WDT_WCR_WDE;
+ __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
+}
+
+static inline void imx2_wdt_ping(void)
+{
+ __raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR);
+ __raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR);
+}
+
+static void imx2_wdt_timer_ping(unsigned long arg)
+{
+ /* ping it every imx2_wdt.timeout / 2 seconds to prevent reboot */
+ imx2_wdt_ping();
+ mod_timer(&imx2_wdt.timer, jiffies + imx2_wdt.timeout * HZ / 2);
+}
+
+static void imx2_wdt_start(void)
+{
+ if (!test_and_set_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
+ /* at our first start we enable clock and do initialisations */
+ clk_enable(imx2_wdt.clk);
+
+ imx2_wdt_setup();
+ } else /* delete the timer that pings the watchdog after close */
+ del_timer_sync(&imx2_wdt.timer);
+
+ /* Watchdog is enabled - time to reload the timeout value */
+ imx2_wdt_ping();
+}
+
+static void imx2_wdt_stop(void)
+{
+ /* we don't need a clk_disable, it cannot be disabled once started.
+ * We use a timer to ping the watchdog while /dev/watchdog is closed */
+ imx2_wdt_timer_ping(0);
+}
+
+static void imx2_wdt_set_timeout(int new_timeout)
+{
+ u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
+
+ /* set the new timeout value in the WSR */
+ val &= ~IMX2_WDT_WCR_WT;
+ val |= WDOG_SEC_TO_COUNT(new_timeout);
+ __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
+}
+
+
+static int imx2_wdt_check_pretimeout_set(void)
+{
+ u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICR);
+ return (val & IMX2_WDT_WICR_WIE) ? 1 : 0;
+}
+
+static void imx2_wdt_set_pretimeout(int new_timeout)
+{
+ u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICR);
+
+ /* set the new pre-timeout value in the WSR */
+ val &= ~IMX2_WDT_WICR_WICT;
+ val |= WDOG_SEC_TO_PRECOUNT(new_timeout);
+
+ if (!imx2_wdt_check_pretimeout_set())
+ val |= IMX2_WDT_WICR_WIE; /*enable*/
+ __raw_writew(val, imx2_wdt.base + IMX2_WDT_WICR);
+}
+
+static irqreturn_t imx2_wdt_isr(int irq, void *dev_id)
+{
+ u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WICR);
+ if (val & IMX2_WDT_WICR_WTIS) {
+ /*clear interrupt status bit*/
+ __raw_writew(val, imx2_wdt.base + IMX2_WDT_WICR);
+ printk(KERN_INFO "watchdog pre-timeout:%d, %d Seconds remained\n", \
+ imx2_wdt.pretimeout, imx2_wdt.timeout-imx2_wdt.pretimeout);
+ }
+ return IRQ_HANDLED;
+}
+
+static int imx2_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
+ return -EBUSY;
+
+ imx2_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int imx2_wdt_close(struct inode *inode, struct file *file)
+{
+ if (test_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status) && !nowayout)
+ imx2_wdt_stop();
+ else {
+ dev_crit(imx2_wdt_miscdev.parent,
+ "Unexpected close: Expect reboot!\n");
+ imx2_wdt_ping();
+ }
+
+ clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
+ clear_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status);
+ return 0;
+}
+
+static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &imx2_wdt_info,
+ sizeof(struct watchdog_info)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_KEEPALIVE:
+ imx2_wdt_ping();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ if ((new_value < 1) || (new_value > IMX2_WDT_MAX_TIME))
+ return -EINVAL;
+ imx2_wdt_set_timeout(new_value);
+ imx2_wdt.timeout = new_value;
+ imx2_wdt_ping();
+
+ /* Fallthrough to return current value */
+ case WDIOC_GETTIMEOUT:
+ return put_user(imx2_wdt.timeout, p);
+
+ case WDIOC_SETPRETIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ if ((new_value < 0) || (new_value >= imx2_wdt.timeout))
+ return -EINVAL;
+ imx2_wdt_set_pretimeout(new_value);
+ imx2_wdt.pretimeout = new_value;
+
+ case WDIOC_GETPRETIMEOUT:
+ return put_user(imx2_wdt.pretimeout, p);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ size_t i;
+ char c;
+
+ if (len == 0) /* Can we see this even ? */
+ return 0;
+
+ clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
+ /* scan to see whether or not we got the magic character */
+ for (i = 0; i != len; i++) {
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
+ }
+
+ imx2_wdt_ping();
+ return len;
+}
+
+static const struct file_operations imx2_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = imx2_wdt_ioctl,
+ .open = imx2_wdt_open,
+ .release = imx2_wdt_close,
+ .write = imx2_wdt_write,
+};
+
+static struct miscdevice imx2_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &imx2_wdt_fops,
+};
+
+static int __init imx2_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+ int res_size;
+ int irq;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "can't get device resources\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "can't get device irq number\n");
+ return -ENODEV;
+ }
+
+ res_size = resource_size(res);
+ if (!devm_request_mem_region(&pdev->dev, res->start, res_size,
+ res->name)) {
+ dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n",
+ res_size, res->start);
+ return -ENOMEM;
+ }
+
+ imx2_wdt.base = devm_ioremap_nocache(&pdev->dev, res->start, res_size);
+ if (!imx2_wdt.base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ imx2_wdt.clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(imx2_wdt.clk)) {
+ dev_err(&pdev->dev, "can't get Watchdog clock\n");
+ return PTR_ERR(imx2_wdt.clk);
+ }
+
+ ret = request_irq(irq, imx2_wdt_isr, 0, pdev->name, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "can't claim irq %d\n", irq);
+ goto fail;
+ }
+
+ imx2_wdt.timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
+ if (imx2_wdt.timeout != timeout)
+ dev_warn(&pdev->dev, "Initial timeout out of range! "
+ "Clamped from %u to %u\n", timeout, imx2_wdt.timeout);
+
+ setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
+
+ imx2_wdt_miscdev.parent = &pdev->dev;
+ ret = misc_register(&imx2_wdt_miscdev);
+ if (ret)
+ goto fail;
+
+ dev_info(&pdev->dev,
+ "IMX2+ Watchdog Timer enabled. timeout=%ds (nowayout=%d)\n",
+ imx2_wdt.timeout, nowayout);
+ return 0;
+
+fail:
+ imx2_wdt_miscdev.parent = NULL;
+ clk_put(imx2_wdt.clk);
+ return ret;
+}
+
+static int __exit imx2_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&imx2_wdt_miscdev);
+
+ if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
+ del_timer_sync(&imx2_wdt.timer);
+
+ dev_crit(imx2_wdt_miscdev.parent,
+ "Device removed: Expect reboot!\n");
+ } else
+ clk_put(imx2_wdt.clk);
+
+ imx2_wdt_miscdev.parent = NULL;
+ return 0;
+}
+
+static void imx2_wdt_shutdown(struct platform_device *pdev)
+{
+ if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
+ /* we are running, we need to delete the timer but will give
+ * max timeout before reboot will take place */
+ del_timer_sync(&imx2_wdt.timer);
+ imx2_wdt_set_timeout(IMX2_WDT_MAX_TIME);
+ imx2_wdt_ping();
+
+ dev_crit(imx2_wdt_miscdev.parent,
+ "Device shutdown: Expect reboot!\n");
+ }
+}
+
+static struct platform_driver imx2_wdt_driver = {
+ .remove = __exit_p(imx2_wdt_remove),
+ .shutdown = imx2_wdt_shutdown,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init imx2_wdt_init(void)
+{
+ return platform_driver_probe(&imx2_wdt_driver, imx2_wdt_probe);
+}
+module_init(imx2_wdt_init);
+
+static void __exit imx2_wdt_exit(void)
+{
+ platform_driver_unregister(&imx2_wdt_driver);
+}
+module_exit(imx2_wdt_exit);
+
+MODULE_AUTHOR("Wolfram Sang");
+MODULE_DESCRIPTION("Watchdog driver for IMX2 and later");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/watchdog/indydog.c b/drivers/watchdog/indydog.c
new file mode 100644
index 00000000..1cc56096
--- /dev/null
+++ b/drivers/watchdog/indydog.c
@@ -0,0 +1,223 @@
+/*
+ * IndyDog 0.3 A Hardware Watchdog Device for SGI IP22
+ *
+ * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <asm/sgi/mc.h>
+
+#define PFX "indydog: "
+static unsigned long indydog_alive;
+static spinlock_t indydog_lock;
+
+#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static void indydog_start(void)
+{
+ u32 mc_ctrl0;
+
+ spin_lock(&indydog_lock);
+ mc_ctrl0 = sgimc->cpuctrl0;
+ mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG;
+ sgimc->cpuctrl0 = mc_ctrl0;
+ spin_unlock(&indydog_lock);
+}
+
+static void indydog_stop(void)
+{
+ u32 mc_ctrl0;
+
+ spin_lock(&indydog_lock);
+
+ mc_ctrl0 = sgimc->cpuctrl0;
+ mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG;
+ sgimc->cpuctrl0 = mc_ctrl0;
+ spin_unlock(&indydog_lock);
+
+ printk(KERN_INFO PFX "Stopped watchdog timer.\n");
+}
+
+static void indydog_ping(void)
+{
+ sgimc->watchdogt = 0;
+}
+
+/*
+ * Allow only one person to hold it open
+ */
+static int indydog_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &indydog_alive))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Activate timer */
+ indydog_start();
+ indydog_ping();
+
+ printk(KERN_INFO "Started watchdog timer.\n");
+
+ return nonseekable_open(inode, file);
+}
+
+static int indydog_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer.
+ * Lock it in if it's a module and we defined ...NOWAYOUT */
+ if (!nowayout)
+ indydog_stop(); /* Turn the WDT off */
+ clear_bit(0, &indydog_alive);
+ return 0;
+}
+
+static ssize_t indydog_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ /* Refresh the timer. */
+ if (len)
+ indydog_ping();
+ return len;
+}
+
+static long indydog_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int options, retval = -EINVAL;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING,
+ .firmware_version = 0,
+ .identity = "Hardware Watchdog for SGI IP22",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg,
+ &ident, sizeof(ident)))
+ return -EFAULT;
+ return 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, (int *)arg);
+ case WDIOC_SETOPTIONS:
+ {
+ if (get_user(options, (int *)arg))
+ return -EFAULT;
+ if (options & WDIOS_DISABLECARD) {
+ indydog_stop();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ indydog_start();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ indydog_ping();
+ return 0;
+ case WDIOC_GETTIMEOUT:
+ return put_user(WATCHDOG_TIMEOUT, (int *)arg);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int indydog_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ indydog_stop(); /* Turn the WDT off */
+
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations indydog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = indydog_write,
+ .unlocked_ioctl = indydog_ioctl,
+ .open = indydog_open,
+ .release = indydog_release,
+};
+
+static struct miscdevice indydog_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &indydog_fops,
+};
+
+static struct notifier_block indydog_notifier = {
+ .notifier_call = indydog_notify_sys,
+};
+
+static char banner[] __initdata =
+ KERN_INFO PFX "Hardware Watchdog Timer for SGI IP22: 0.3\n";
+
+static int __init watchdog_init(void)
+{
+ int ret;
+
+ spin_lock_init(&indydog_lock);
+
+ ret = register_reboot_notifier(&indydog_notifier);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ return ret;
+ }
+
+ ret = misc_register(&indydog_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ unregister_reboot_notifier(&indydog_notifier);
+ return ret;
+ }
+
+ printk(banner);
+
+ return 0;
+}
+
+static void __exit watchdog_exit(void)
+{
+ misc_deregister(&indydog_miscdev);
+ unregister_reboot_notifier(&indydog_notifier);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+
+MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
+MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c
new file mode 100644
index 00000000..ba438606
--- /dev/null
+++ b/drivers/watchdog/intel_scu_watchdog.c
@@ -0,0 +1,571 @@
+/*
+ * Intel_SCU 0.2: An Intel SCU IOH Based Watchdog Device
+ * for Intel part #(s):
+ * - AF82MP20 PCH
+ *
+ * Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General
+ * Public License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * The full GNU General Public License is included in this
+ * distribution in the file called COPYING.
+ *
+ */
+
+#include <linux/compiler.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/sfi.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+#include <asm/intel_scu_ipc.h>
+#include <asm/apb_timer.h>
+#include <asm/mrst.h>
+
+#include "intel_scu_watchdog.h"
+
+/* Bounds number of times we will retry loading time count */
+/* This retry is a work around for a silicon bug. */
+#define MAX_RETRY 16
+
+#define IPC_SET_WATCHDOG_TIMER 0xF8
+
+static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN;
+module_param(timer_margin, int, 0);
+MODULE_PARM_DESC(timer_margin,
+ "Watchdog timer margin"
+ "Time between interrupt and resetting the system"
+ "The range is from 1 to 160"
+ "This is the time for all keep alives to arrive");
+
+static int timer_set = DEFAULT_TIME;
+module_param(timer_set, int, 0);
+MODULE_PARM_DESC(timer_set,
+ "Default Watchdog timer setting"
+ "Complete cycle time"
+ "The range is from 1 to 170"
+ "This is the time for all keep alives to arrive");
+
+/* After watchdog device is closed, check force_boot. If:
+ * force_boot == 0, then force boot on next watchdog interrupt after close,
+ * force_boot == 1, then force boot immediately when device is closed.
+ */
+static int force_boot;
+module_param(force_boot, int, 0);
+MODULE_PARM_DESC(force_boot,
+ "A value of 1 means that the driver will reboot"
+ "the system immediately if the /dev/watchdog device is closed"
+ "A value of 0 means that when /dev/watchdog device is closed"
+ "the watchdog timer will be refreshed for one more interval"
+ "of length: timer_set. At the end of this interval, the"
+ "watchdog timer will reset the system."
+ );
+
+/* there is only one device in the system now; this can be made into
+ * an array in the future if we have more than one device */
+
+static struct intel_scu_watchdog_dev watchdog_device;
+
+/* Forces restart, if force_reboot is set */
+static void watchdog_fire(void)
+{
+ if (force_boot) {
+ printk(KERN_CRIT PFX "Initiating system reboot.\n");
+ emergency_restart();
+ printk(KERN_CRIT PFX "Reboot didn't ?????\n");
+ }
+
+ else {
+ printk(KERN_CRIT PFX "Immediate Reboot Disabled\n");
+ printk(KERN_CRIT PFX
+ "System will reset when watchdog timer times out!\n");
+ }
+}
+
+static int check_timer_margin(int new_margin)
+{
+ if ((new_margin < MIN_TIME_CYCLE) ||
+ (new_margin > MAX_TIME - timer_set)) {
+ pr_debug("Watchdog timer: value of new_margin %d is out of the range %d to %d\n",
+ new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * IPC operations
+ */
+static int watchdog_set_ipc(int soft_threshold, int threshold)
+{
+ u32 *ipc_wbuf;
+ u8 cbuf[16] = { '\0' };
+ int ipc_ret = 0;
+
+ ipc_wbuf = (u32 *)&cbuf;
+ ipc_wbuf[0] = soft_threshold;
+ ipc_wbuf[1] = threshold;
+
+ ipc_ret = intel_scu_ipc_command(
+ IPC_SET_WATCHDOG_TIMER,
+ 0,
+ ipc_wbuf,
+ 2,
+ NULL,
+ 0);
+
+ if (ipc_ret != 0)
+ pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
+
+ return ipc_ret;
+};
+
+/*
+ * Intel_SCU operations
+ */
+
+/* timer interrupt handler */
+static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id)
+{
+ int int_status;
+ int_status = ioread32(watchdog_device.timer_interrupt_status_addr);
+
+ pr_debug("Watchdog timer: irq, int_status: %x\n", int_status);
+
+ if (int_status != 0)
+ return IRQ_NONE;
+
+ /* has the timer been started? If not, then this is spurious */
+ if (watchdog_device.timer_started == 0) {
+ pr_debug("Watchdog timer: spurious interrupt received\n");
+ return IRQ_HANDLED;
+ }
+
+ /* temporarily disable the timer */
+ iowrite32(0x00000002, watchdog_device.timer_control_addr);
+
+ /* set the timer to the threshold */
+ iowrite32(watchdog_device.threshold,
+ watchdog_device.timer_load_count_addr);
+
+ /* allow the timer to run */
+ iowrite32(0x00000003, watchdog_device.timer_control_addr);
+
+ return IRQ_HANDLED;
+}
+
+static int intel_scu_keepalive(void)
+{
+
+ /* read eoi register - clears interrupt */
+ ioread32(watchdog_device.timer_clear_interrupt_addr);
+
+ /* temporarily disable the timer */
+ iowrite32(0x00000002, watchdog_device.timer_control_addr);
+
+ /* set the timer to the soft_threshold */
+ iowrite32(watchdog_device.soft_threshold,
+ watchdog_device.timer_load_count_addr);
+
+ /* allow the timer to run */
+ iowrite32(0x00000003, watchdog_device.timer_control_addr);
+
+ return 0;
+}
+
+static int intel_scu_stop(void)
+{
+ iowrite32(0, watchdog_device.timer_control_addr);
+ return 0;
+}
+
+static int intel_scu_set_heartbeat(u32 t)
+{
+ int ipc_ret;
+ int retry_count;
+ u32 soft_value;
+ u32 hw_pre_value;
+ u32 hw_value;
+
+ watchdog_device.timer_set = t;
+ watchdog_device.threshold =
+ timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
+ watchdog_device.soft_threshold =
+ (watchdog_device.timer_set - timer_margin)
+ * watchdog_device.timer_tbl_ptr->freq_hz;
+
+ pr_debug("Watchdog timer: set_heartbeat: timer freq is %d\n",
+ watchdog_device.timer_tbl_ptr->freq_hz);
+ pr_debug("Watchdog timer: set_heartbeat: timer_set is %x (hex)\n",
+ watchdog_device.timer_set);
+ pr_debug("Watchdog timer: set_hearbeat: timer_margin is %x (hex)\n",
+ timer_margin);
+ pr_debug("Watchdog timer: set_heartbeat: threshold is %x (hex)\n",
+ watchdog_device.threshold);
+ pr_debug("Watchdog timer: set_heartbeat: soft_threshold is %x (hex)\n",
+ watchdog_device.soft_threshold);
+
+ /* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */
+ /* watchdog timing come out right. */
+ watchdog_device.threshold =
+ watchdog_device.threshold / FREQ_ADJUSTMENT;
+ watchdog_device.soft_threshold =
+ watchdog_device.soft_threshold / FREQ_ADJUSTMENT;
+
+ /* temporarily disable the timer */
+ iowrite32(0x00000002, watchdog_device.timer_control_addr);
+
+ /* send the threshold and soft_threshold via IPC to the processor */
+ ipc_ret = watchdog_set_ipc(watchdog_device.soft_threshold,
+ watchdog_device.threshold);
+
+ if (ipc_ret != 0) {
+ /* Make sure the watchdog timer is stopped */
+ intel_scu_stop();
+ return ipc_ret;
+ }
+
+ /* Soft Threshold set loop. Early versions of silicon did */
+ /* not always set this count correctly. This loop checks */
+ /* the value and retries if it was not set correctly. */
+
+ retry_count = 0;
+ soft_value = watchdog_device.soft_threshold & 0xFFFF0000;
+ do {
+
+ /* Make sure timer is stopped */
+ intel_scu_stop();
+
+ if (MAX_RETRY < retry_count++) {
+ /* Unable to set timer value */
+ pr_err("Watchdog timer: Unable to set timer\n");
+ return -ENODEV;
+ }
+
+ /* set the timer to the soft threshold */
+ iowrite32(watchdog_device.soft_threshold,
+ watchdog_device.timer_load_count_addr);
+
+ /* read count value before starting timer */
+ hw_pre_value = ioread32(watchdog_device.timer_load_count_addr);
+ hw_pre_value = hw_pre_value & 0xFFFF0000;
+
+ /* Start the timer */
+ iowrite32(0x00000003, watchdog_device.timer_control_addr);
+
+ /* read the value the time loaded into its count reg */
+ hw_value = ioread32(watchdog_device.timer_load_count_addr);
+ hw_value = hw_value & 0xFFFF0000;
+
+
+ } while (soft_value != hw_value);
+
+ watchdog_device.timer_started = 1;
+
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int intel_scu_open(struct inode *inode, struct file *file)
+{
+
+ /* Set flag to indicate that watchdog device is open */
+ if (test_and_set_bit(0, &watchdog_device.driver_open))
+ return -EBUSY;
+
+ /* Check for reopen of driver. Reopens are not allowed */
+ if (watchdog_device.driver_closed)
+ return -EPERM;
+
+ return nonseekable_open(inode, file);
+}
+
+static int intel_scu_release(struct inode *inode, struct file *file)
+{
+ /*
+ * This watchdog should not be closed, after the timer
+ * is started with the WDIPC_SETTIMEOUT ioctl
+ * If force_boot is set watchdog_fire() will cause an
+ * immediate reset. If force_boot is not set, the watchdog
+ * timer is refreshed for one more interval. At the end
+ * of that interval, the watchdog timer will reset the system.
+ */
+
+ if (!test_and_clear_bit(0, &watchdog_device.driver_open)) {
+ pr_debug("Watchdog timer: intel_scu_release, without open\n");
+ return -ENOTTY;
+ }
+
+ if (!watchdog_device.timer_started) {
+ /* Just close, since timer has not been started */
+ pr_debug("Watchdog timer: closed, without starting timer\n");
+ return 0;
+ }
+
+ printk(KERN_CRIT PFX
+ "Unexpected close of /dev/watchdog!\n");
+
+ /* Since the timer was started, prevent future reopens */
+ watchdog_device.driver_closed = 1;
+
+ /* Refresh the timer for one more interval */
+ intel_scu_keepalive();
+
+ /* Reboot system (if force_boot is set) */
+ watchdog_fire();
+
+ /* We should only reach this point if force_boot is not set */
+ return 0;
+}
+
+static ssize_t intel_scu_write(struct file *file,
+ char const *data,
+ size_t len,
+ loff_t *ppos)
+{
+
+ if (watchdog_device.timer_started)
+ /* Watchdog already started, keep it alive */
+ intel_scu_keepalive();
+ else
+ /* Start watchdog with timer value set by init */
+ intel_scu_set_heartbeat(watchdog_device.timer_set);
+
+ return len;
+}
+
+static long intel_scu_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ u32 __user *p = argp;
+ u32 new_margin;
+
+
+ static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT
+ | WDIOF_KEEPALIVEPING,
+ .firmware_version = 0, /* @todo Get from SCU via
+ ipc_get_scu_fw_version()? */
+ .identity = "Intel_SCU IOH Watchdog" /* len < 32 */
+ };
+
+ 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_KEEPALIVE:
+ intel_scu_keepalive();
+
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, p))
+ return -EFAULT;
+
+ if (check_timer_margin(new_margin))
+ return -EINVAL;
+
+ if (intel_scu_set_heartbeat(new_margin))
+ return -EINVAL;
+ return 0;
+ case WDIOC_GETTIMEOUT:
+ return put_user(watchdog_device.soft_threshold, p);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+/*
+ * Notifier for system down
+ */
+static int intel_scu_notify_sys(struct notifier_block *this,
+ unsigned long code,
+ void *another_unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ /* Turn off the watchdog timer. */
+ intel_scu_stop();
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+static const struct file_operations intel_scu_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = intel_scu_write,
+ .unlocked_ioctl = intel_scu_ioctl,
+ .open = intel_scu_open,
+ .release = intel_scu_release,
+};
+
+static int __init intel_scu_watchdog_init(void)
+{
+ int ret;
+ u32 __iomem *tmp_addr;
+
+ /*
+ * We don't really need to check this as the SFI timer get will fail
+ * but if we do so we can exit with a clearer reason and no noise.
+ *
+ * If it isn't an intel MID device then it doesn't have this watchdog
+ */
+ if (!mrst_identify_cpu())
+ return -ENODEV;
+
+ /* Check boot parameters to verify that their initial values */
+ /* are in range. */
+ /* Check value of timer_set boot parameter */
+ if ((timer_set < MIN_TIME_CYCLE) ||
+ (timer_set > MAX_TIME - MIN_TIME_CYCLE)) {
+ pr_err("Watchdog timer: value of timer_set %x (hex) "
+ "is out of range from %x to %x (hex)\n",
+ timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
+ return -EINVAL;
+ }
+
+ /* Check value of timer_margin boot parameter */
+ if (check_timer_margin(timer_margin))
+ return -EINVAL;
+
+ watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
+
+ if (watchdog_device.timer_tbl_ptr == NULL) {
+ pr_debug("Watchdog timer - Intel SCU watchdog: timer is not available\n");
+ return -ENODEV;
+ }
+ /* make sure the timer exists */
+ if (watchdog_device.timer_tbl_ptr->phys_addr == 0) {
+ pr_debug("Watchdog timer - Intel SCU watchdog - timer %d does not have valid physical memory\n",
+ sfi_mtimer_num);
+ return -ENODEV;
+ }
+
+ if (watchdog_device.timer_tbl_ptr->irq == 0) {
+ pr_debug("Watchdog timer: timer %d invalid irq\n",
+ sfi_mtimer_num);
+ return -ENODEV;
+ }
+
+ tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr,
+ 20);
+
+ if (tmp_addr == NULL) {
+ pr_debug("Watchdog timer: timer unable to ioremap\n");
+ return -ENOMEM;
+ }
+
+ watchdog_device.timer_load_count_addr = tmp_addr++;
+ watchdog_device.timer_current_value_addr = tmp_addr++;
+ watchdog_device.timer_control_addr = tmp_addr++;
+ watchdog_device.timer_clear_interrupt_addr = tmp_addr++;
+ watchdog_device.timer_interrupt_status_addr = tmp_addr++;
+
+ /* Set the default time values in device structure */
+
+ watchdog_device.timer_set = timer_set;
+ watchdog_device.threshold =
+ timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
+ watchdog_device.soft_threshold =
+ (watchdog_device.timer_set - timer_margin)
+ * watchdog_device.timer_tbl_ptr->freq_hz;
+
+
+ watchdog_device.intel_scu_notifier.notifier_call =
+ intel_scu_notify_sys;
+
+ ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
+ if (ret) {
+ pr_err("Watchdog timer: cannot register notifier %d)\n", ret);
+ goto register_reboot_error;
+ }
+
+ watchdog_device.miscdev.minor = WATCHDOG_MINOR;
+ watchdog_device.miscdev.name = "watchdog";
+ watchdog_device.miscdev.fops = &intel_scu_fops;
+
+ ret = misc_register(&watchdog_device.miscdev);
+ if (ret) {
+ pr_err("Watchdog timer: cannot register miscdev %d err =%d\n",
+ WATCHDOG_MINOR, ret);
+ goto misc_register_error;
+ }
+
+ ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq,
+ watchdog_timer_interrupt,
+ IRQF_SHARED, "watchdog",
+ &watchdog_device.timer_load_count_addr);
+ if (ret) {
+ pr_err("Watchdog timer: error requesting irq %d\n", ret);
+ goto request_irq_error;
+ }
+ /* Make sure timer is disabled before returning */
+ intel_scu_stop();
+ return 0;
+
+/* error cleanup */
+
+request_irq_error:
+ misc_deregister(&watchdog_device.miscdev);
+misc_register_error:
+ unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
+register_reboot_error:
+ intel_scu_stop();
+ iounmap(watchdog_device.timer_load_count_addr);
+ return ret;
+}
+
+static void __exit intel_scu_watchdog_exit(void)
+{
+
+ misc_deregister(&watchdog_device.miscdev);
+ unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
+ /* disable the timer */
+ iowrite32(0x00000002, watchdog_device.timer_control_addr);
+ iounmap(watchdog_device.timer_load_count_addr);
+}
+
+late_initcall(intel_scu_watchdog_init);
+module_exit(intel_scu_watchdog_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_VERSION(WDT_VER);
diff --git a/drivers/watchdog/intel_scu_watchdog.h b/drivers/watchdog/intel_scu_watchdog.h
new file mode 100644
index 00000000..d2b074a8
--- /dev/null
+++ b/drivers/watchdog/intel_scu_watchdog.h
@@ -0,0 +1,66 @@
+/*
+ * Intel_SCU 0.2: An Intel SCU IOH Based Watchdog Device
+ * for Intel part #(s):
+ * - AF82MP20 PCH
+ *
+ * Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General
+ * Public License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * The full GNU General Public License is included in this
+ * distribution in the file called COPYING.
+ *
+ */
+
+#ifndef __INTEL_SCU_WATCHDOG_H
+#define __INTEL_SCU_WATCHDOG_H
+
+#define PFX "Intel_SCU: "
+#define WDT_VER "0.3"
+
+/* minimum time between interrupts */
+#define MIN_TIME_CYCLE 1
+
+/* Time from warning to reboot is 2 seconds */
+#define DEFAULT_SOFT_TO_HARD_MARGIN 2
+
+#define MAX_TIME 170
+
+#define DEFAULT_TIME 5
+
+#define MAX_SOFT_TO_HARD_MARGIN (MAX_TIME-MIN_TIME_CYCLE)
+
+/* Ajustment to clock tick frequency to make timing come out right */
+#define FREQ_ADJUSTMENT 8
+
+struct intel_scu_watchdog_dev {
+ ulong driver_open;
+ ulong driver_closed;
+ u32 timer_started;
+ u32 timer_set;
+ u32 threshold;
+ u32 soft_threshold;
+ u32 __iomem *timer_load_count_addr;
+ u32 __iomem *timer_current_value_addr;
+ u32 __iomem *timer_control_addr;
+ u32 __iomem *timer_clear_interrupt_addr;
+ u32 __iomem *timer_interrupt_status_addr;
+ struct sfi_timer_table_entry *timer_tbl_ptr;
+ struct notifier_block intel_scu_notifier;
+ struct miscdevice miscdev;
+};
+
+extern int sfi_mtimer_num;
+
+/* extern struct sfi_timer_table_entry *sfi_get_mtmr(int hint); */
+#endif /* __INTEL_SCU_WATCHDOG_H */
diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c
new file mode 100644
index 00000000..aef94789
--- /dev/null
+++ b/drivers/watchdog/iop_wdt.c
@@ -0,0 +1,264 @@
+/*
+ * drivers/char/watchdog/iop_wdt.c
+ *
+ * WDT driver for Intel I/O Processors
+ * Copyright (C) 2005, Intel Corporation.
+ *
+ * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Curt E Bruns <curt.e.bruns@intel.com>
+ * Peter Milne <peter.milne@d-tacq.com>
+ * Dan Williams <dan.j.williams@intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <mach/hardware.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static unsigned long wdt_status;
+static unsigned long boot_status;
+static spinlock_t wdt_lock;
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+#define WDT_ENABLED 2
+
+static unsigned long iop_watchdog_timeout(void)
+{
+ return (0xffffffffUL / get_iop_tick_rate());
+}
+
+/**
+ * wdt_supports_disable - determine if we are accessing a iop13xx watchdog
+ * or iop3xx by whether it has a disable command
+ */
+static int wdt_supports_disable(void)
+{
+ int can_disable;
+
+ if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM)
+ can_disable = 1;
+ else
+ can_disable = 0;
+
+ return can_disable;
+}
+
+static void wdt_enable(void)
+{
+ /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF
+ * Takes approx. 10.7s to timeout
+ */
+ spin_lock(&wdt_lock);
+ write_wdtcr(IOP_WDTCR_EN_ARM);
+ write_wdtcr(IOP_WDTCR_EN);
+ spin_unlock(&wdt_lock);
+}
+
+/* returns 0 if the timer was successfully disabled */
+static int wdt_disable(void)
+{
+ /* Stop Counting */
+ if (wdt_supports_disable()) {
+ spin_lock(&wdt_lock);
+ write_wdtcr(IOP_WDTCR_DIS_ARM);
+ write_wdtcr(IOP_WDTCR_DIS);
+ clear_bit(WDT_ENABLED, &wdt_status);
+ spin_unlock(&wdt_lock);
+ printk(KERN_INFO "WATCHDOG: Disabled\n");
+ return 0;
+ } else
+ return 1;
+}
+
+static int iop_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ wdt_enable();
+ set_bit(WDT_ENABLED, &wdt_status);
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t iop_wdt_write(struct file *file, const char *data, size_t len,
+ loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_enable();
+ }
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+ .identity = "iop watchdog",
+};
+
+static long iop_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int options;
+ int ret = -ENOTTY;
+ int __user *argp = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, argp);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(boot_status, argp);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(options, (int *)arg))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ if (!nowayout) {
+ if (wdt_disable() == 0) {
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ ret = 0;
+ } else
+ ret = -ENXIO;
+ } else
+ ret = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ wdt_enable();
+ ret = 0;
+ }
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(iop_watchdog_timeout(), argp);
+ break;
+ }
+ return ret;
+}
+
+static int iop_wdt_release(struct inode *inode, struct file *file)
+{
+ int state = 1;
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ if (test_bit(WDT_ENABLED, &wdt_status))
+ state = wdt_disable();
+
+ /* if the timer is not disabled reload and notify that we are still
+ * going down
+ */
+ if (state != 0) {
+ wdt_enable();
+ printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
+ "reset in %lu seconds\n", iop_watchdog_timeout());
+ }
+
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+static const struct file_operations iop_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = iop_wdt_write,
+ .unlocked_ioctl = iop_wdt_ioctl,
+ .open = iop_wdt_open,
+ .release = iop_wdt_release,
+};
+
+static struct miscdevice iop_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &iop_wdt_fops,
+};
+
+static int __init iop_wdt_init(void)
+{
+ int ret;
+
+ spin_lock_init(&wdt_lock);
+
+
+ /* check if the reset was caused by the watchdog timer */
+ boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0;
+
+ /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset
+ * NOTE: An IB Reset will Reset both cores in the IOP342
+ */
+ write_wdtsr(IOP13XX_WDTCR_IB_RESET);
+
+ /* Register after we have the device set up so we cannot race
+ with an open */
+ ret = misc_register(&iop_wdt_miscdev);
+ if (ret == 0)
+ printk(KERN_INFO "iop watchdog timer: timeout %lu sec\n",
+ iop_watchdog_timeout());
+
+ return ret;
+}
+
+static void __exit iop_wdt_exit(void)
+{
+ misc_deregister(&iop_wdt_miscdev);
+}
+
+module_init(iop_wdt_init);
+module_exit(iop_wdt_exit);
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>");
+MODULE_DESCRIPTION("iop watchdog timer driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c
new file mode 100644
index 00000000..6143f52b
--- /dev/null
+++ b/drivers/watchdog/it8712f_wdt.c
@@ -0,0 +1,429 @@
+/*
+ * IT8712F "Smart Guardian" Watchdog support
+ *
+ * Copyright (c) 2006-2007 Jorge Boncompte - DTI2 <jorge@dti2.net>
+ *
+ * Based on info and code taken from:
+ *
+ * drivers/char/watchdog/scx200_wdt.c
+ * drivers/hwmon/it87.c
+ * IT8712F EC-LPC I/O Preliminary Specification 0.8.2
+ * IT8712F EC-LPC I/O Preliminary Specification 0.9.3
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The author(s) of this software shall not be held liable for damages
+ * of any nature resulting due to the use of this software. This
+ * software is provided AS-IS with no warranties.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#define NAME "it8712f_wdt"
+
+MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>");
+MODULE_DESCRIPTION("IT8712F Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+static int max_units = 255;
+static int margin = 60; /* in seconds */
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+static unsigned long wdt_open;
+static unsigned expect_close;
+static spinlock_t io_lock;
+static unsigned char revision;
+
+/* Dog Food address - We use the game port address */
+static unsigned short address;
+
+#define REG 0x2e /* The register to read/write */
+#define VAL 0x2f /* The value to read/write */
+
+#define LDN 0x07 /* Register: Logical device select */
+#define DEVID 0x20 /* Register: Device ID */
+#define DEVREV 0x22 /* Register: Device Revision */
+#define ACT_REG 0x30 /* LDN Register: Activation */
+#define BASE_REG 0x60 /* LDN Register: Base address */
+
+#define IT8712F_DEVID 0x8712
+
+#define LDN_GPIO 0x07 /* GPIO and Watch Dog Timer */
+#define LDN_GAME 0x09 /* Game Port */
+
+#define WDT_CONTROL 0x71 /* WDT Register: Control */
+#define WDT_CONFIG 0x72 /* WDT Register: Configuration */
+#define WDT_TIMEOUT 0x73 /* WDT Register: Timeout Value */
+
+#define WDT_RESET_GAME 0x10 /* Reset timer on read or write to game port */
+#define WDT_RESET_KBD 0x20 /* Reset timer on keyboard interrupt */
+#define WDT_RESET_MOUSE 0x40 /* Reset timer on mouse interrupt */
+#define WDT_RESET_CIR 0x80 /* Reset timer on consumer IR interrupt */
+
+#define WDT_UNIT_SEC 0x80 /* If 0 in MINUTES */
+
+#define WDT_OUT_PWROK 0x10 /* Pulse PWROK on timeout */
+#define WDT_OUT_KRST 0x40 /* Pulse reset on timeout */
+
+static int wdt_control_reg = WDT_RESET_GAME;
+module_param(wdt_control_reg, int, 0);
+MODULE_PARM_DESC(wdt_control_reg, "Value to write to watchdog control "
+ "register. The default WDT_RESET_GAME resets the timer on "
+ "game port reads that this driver generates. You can also "
+ "use KBD, MOUSE or CIR if you have some external way to "
+ "generate those interrupts.");
+
+static int superio_inb(int reg)
+{
+ outb(reg, REG);
+ return inb(VAL);
+}
+
+static void superio_outb(int val, int reg)
+{
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+static int superio_inw(int reg)
+{
+ int val;
+ outb(reg++, REG);
+ val = inb(VAL) << 8;
+ outb(reg, REG);
+ val |= inb(VAL);
+ return val;
+}
+
+static inline void superio_select(int ldn)
+{
+ outb(LDN, REG);
+ outb(ldn, VAL);
+}
+
+static inline void superio_enter(void)
+{
+ spin_lock(&io_lock);
+ outb(0x87, REG);
+ outb(0x01, REG);
+ outb(0x55, REG);
+ outb(0x55, REG);
+}
+
+static inline void superio_exit(void)
+{
+ outb(0x02, REG);
+ outb(0x02, VAL);
+ spin_unlock(&io_lock);
+}
+
+static inline void it8712f_wdt_ping(void)
+{
+ if (wdt_control_reg & WDT_RESET_GAME)
+ inb(address);
+}
+
+static void it8712f_wdt_update_margin(void)
+{
+ int config = WDT_OUT_KRST | WDT_OUT_PWROK;
+ int units = margin;
+
+ /* Switch to minutes precision if the configured margin
+ * value does not fit within the register width.
+ */
+ if (units <= max_units) {
+ config |= WDT_UNIT_SEC; /* else UNIT is MINUTES */
+ printk(KERN_INFO NAME ": timer margin %d seconds\n", units);
+ } else {
+ units /= 60;
+ printk(KERN_INFO NAME ": timer margin %d minutes\n", units);
+ }
+ superio_outb(config, WDT_CONFIG);
+
+ if (revision >= 0x08)
+ superio_outb(units >> 8, WDT_TIMEOUT + 1);
+ superio_outb(units, WDT_TIMEOUT);
+}
+
+static int it8712f_wdt_get_status(void)
+{
+ if (superio_inb(WDT_CONTROL) & 0x01)
+ return WDIOF_CARDRESET;
+ else
+ return 0;
+}
+
+static void it8712f_wdt_enable(void)
+{
+ printk(KERN_DEBUG NAME ": enabling watchdog timer\n");
+ superio_enter();
+ superio_select(LDN_GPIO);
+
+ superio_outb(wdt_control_reg, WDT_CONTROL);
+
+ it8712f_wdt_update_margin();
+
+ superio_exit();
+
+ it8712f_wdt_ping();
+}
+
+static void it8712f_wdt_disable(void)
+{
+ printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
+
+ superio_enter();
+ superio_select(LDN_GPIO);
+
+ superio_outb(0, WDT_CONFIG);
+ superio_outb(0, WDT_CONTROL);
+ if (revision >= 0x08)
+ superio_outb(0, WDT_TIMEOUT + 1);
+ superio_outb(0, WDT_TIMEOUT);
+
+ superio_exit();
+}
+
+static int it8712f_wdt_notify(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_HALT || code == SYS_POWER_OFF)
+ if (!nowayout)
+ it8712f_wdt_disable();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block it8712f_wdt_notifier = {
+ .notifier_call = it8712f_wdt_notify,
+};
+
+static ssize_t it8712f_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* check for a magic close character */
+ if (len) {
+ size_t i;
+
+ it8712f_wdt_ping();
+
+ expect_close = 0;
+ for (i = 0; i < len; ++i) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+
+ return len;
+}
+
+static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .identity = "IT8712F Watchdog",
+ .firmware_version = 1,
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ };
+ int value;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ return 0;
+ case WDIOC_GETSTATUS:
+ superio_enter();
+ superio_select(LDN_GPIO);
+
+ value = it8712f_wdt_get_status();
+
+ superio_exit();
+
+ return put_user(value, p);
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_KEEPALIVE:
+ it8712f_wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(value, p))
+ return -EFAULT;
+ if (value < 1)
+ return -EINVAL;
+ if (value > (max_units * 60))
+ return -EINVAL;
+ margin = value;
+ superio_enter();
+ superio_select(LDN_GPIO);
+
+ it8712f_wdt_update_margin();
+
+ superio_exit();
+ it8712f_wdt_ping();
+ /* Fall through */
+ case WDIOC_GETTIMEOUT:
+ if (put_user(margin, p))
+ return -EFAULT;
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int it8712f_wdt_open(struct inode *inode, struct file *file)
+{
+ /* only allow one at a time */
+ if (test_and_set_bit(0, &wdt_open))
+ return -EBUSY;
+ it8712f_wdt_enable();
+ return nonseekable_open(inode, file);
+}
+
+static int it8712f_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close != 42) {
+ printk(KERN_WARNING NAME
+ ": watchdog device closed unexpectedly, will not"
+ " disable the watchdog timer\n");
+ } else if (!nowayout) {
+ it8712f_wdt_disable();
+ }
+ expect_close = 0;
+ clear_bit(0, &wdt_open);
+
+ return 0;
+}
+
+static const struct file_operations it8712f_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = it8712f_wdt_write,
+ .unlocked_ioctl = it8712f_wdt_ioctl,
+ .open = it8712f_wdt_open,
+ .release = it8712f_wdt_release,
+};
+
+static struct miscdevice it8712f_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &it8712f_wdt_fops,
+};
+
+static int __init it8712f_wdt_find(unsigned short *address)
+{
+ int err = -ENODEV;
+ int chip_type;
+
+ superio_enter();
+ chip_type = superio_inw(DEVID);
+ if (chip_type != IT8712F_DEVID)
+ goto exit;
+
+ superio_select(LDN_GAME);
+ superio_outb(1, ACT_REG);
+ if (!(superio_inb(ACT_REG) & 0x01)) {
+ printk(KERN_ERR NAME ": Device not activated, skipping\n");
+ goto exit;
+ }
+
+ *address = superio_inw(BASE_REG);
+ if (*address == 0) {
+ printk(KERN_ERR NAME ": Base address not set, skipping\n");
+ goto exit;
+ }
+
+ err = 0;
+ revision = superio_inb(DEVREV) & 0x0f;
+
+ /* Later revisions have 16-bit values per datasheet 0.9.1 */
+ if (revision >= 0x08)
+ max_units = 65535;
+
+ if (margin > (max_units * 60))
+ margin = (max_units * 60);
+
+ printk(KERN_INFO NAME ": Found IT%04xF chip revision %d - "
+ "using DogFood address 0x%x\n",
+ chip_type, revision, *address);
+
+exit:
+ superio_exit();
+ return err;
+}
+
+static int __init it8712f_wdt_init(void)
+{
+ int err = 0;
+
+ spin_lock_init(&io_lock);
+
+ if (it8712f_wdt_find(&address))
+ return -ENODEV;
+
+ if (!request_region(address, 1, "IT8712F Watchdog")) {
+ printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
+ return -EBUSY;
+ }
+
+ it8712f_wdt_disable();
+
+ err = register_reboot_notifier(&it8712f_wdt_notifier);
+ if (err) {
+ printk(KERN_ERR NAME ": unable to register reboot notifier\n");
+ goto out;
+ }
+
+ err = misc_register(&it8712f_wdt_miscdev);
+ if (err) {
+ printk(KERN_ERR NAME
+ ": cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, err);
+ goto reboot_out;
+ }
+
+ return 0;
+
+
+reboot_out:
+ unregister_reboot_notifier(&it8712f_wdt_notifier);
+out:
+ release_region(address, 1);
+ return err;
+}
+
+static void __exit it8712f_wdt_exit(void)
+{
+ misc_deregister(&it8712f_wdt_miscdev);
+ unregister_reboot_notifier(&it8712f_wdt_notifier);
+ release_region(address, 1);
+}
+
+module_init(it8712f_wdt_init);
+module_exit(it8712f_wdt_exit);
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c
new file mode 100644
index 00000000..b1bc72f9
--- /dev/null
+++ b/drivers/watchdog/it87_wdt.c
@@ -0,0 +1,769 @@
+/*
+ * Watchdog Timer Driver
+ * for ITE IT87xx Environment Control - Low Pin Count Input / Output
+ *
+ * (c) Copyright 2007 Oliver Schuster <olivers137@aol.com>
+ *
+ * Based on softdog.c by Alan Cox,
+ * 83977f_wdt.c by Jose Goncalves,
+ * it87.c by Chris Gauthron, Jean Delvare
+ *
+ * Data-sheets: Publicly available at the ITE website
+ * http://www.ite.com.tw/
+ *
+ * Support of the watchdog timers, which are available on
+ * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721 and IT8726.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+
+#define WATCHDOG_VERSION "1.14"
+#define WATCHDOG_NAME "IT87 WDT"
+#define PFX WATCHDOG_NAME ": "
+#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
+#define WD_MAGIC 'V'
+
+/* Defaults for Module Parameter */
+#define DEFAULT_NOGAMEPORT 0
+#define DEFAULT_EXCLUSIVE 1
+#define DEFAULT_TIMEOUT 60
+#define DEFAULT_TESTMODE 0
+#define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT
+
+/* IO Ports */
+#define REG 0x2e
+#define VAL 0x2f
+
+/* Logical device Numbers LDN */
+#define GPIO 0x07
+#define GAMEPORT 0x09
+#define CIR 0x0a
+
+/* Configuration Registers and Functions */
+#define LDNREG 0x07
+#define CHIPID 0x20
+#define CHIPREV 0x22
+#define ACTREG 0x30
+#define BASEREG 0x60
+
+/* Chip Id numbers */
+#define NO_DEV_ID 0xffff
+#define IT8702_ID 0x8702
+#define IT8705_ID 0x8705
+#define IT8712_ID 0x8712
+#define IT8716_ID 0x8716
+#define IT8718_ID 0x8718
+#define IT8720_ID 0x8720
+#define IT8721_ID 0x8721
+#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
+
+/* GPIO Configuration Registers LDN=0x07 */
+#define WDTCTRL 0x71
+#define WDTCFG 0x72
+#define WDTVALLSB 0x73
+#define WDTVALMSB 0x74
+
+/* GPIO Bits WDTCTRL */
+#define WDT_CIRINT 0x80
+#define WDT_MOUSEINT 0x40
+#define WDT_KYBINT 0x20
+#define WDT_GAMEPORT 0x10 /* not in it8718, it8720, it8721 */
+#define WDT_FORCE 0x02
+#define WDT_ZERO 0x01
+
+/* GPIO Bits WDTCFG */
+#define WDT_TOV1 0x80
+#define WDT_KRST 0x40
+#define WDT_TOVE 0x20
+#define WDT_PWROK 0x10 /* not in it8721 */
+#define WDT_INT_MASK 0x0f
+
+/* CIR Configuration Register LDN=0x0a */
+#define CIR_ILS 0x70
+
+/* The default Base address is not always available, we use this */
+#define CIR_BASE 0x0208
+
+/* CIR Controller */
+#define CIR_DR(b) (b)
+#define CIR_IER(b) (b + 1)
+#define CIR_RCR(b) (b + 2)
+#define CIR_TCR1(b) (b + 3)
+#define CIR_TCR2(b) (b + 4)
+#define CIR_TSR(b) (b + 5)
+#define CIR_RSR(b) (b + 6)
+#define CIR_BDLR(b) (b + 5)
+#define CIR_BDHR(b) (b + 6)
+#define CIR_IIR(b) (b + 7)
+
+/* Default Base address of Game port */
+#define GP_BASE_DEFAULT 0x0201
+
+/* wdt_status */
+#define WDTS_TIMER_RUN 0
+#define WDTS_DEV_OPEN 1
+#define WDTS_KEEPALIVE 2
+#define WDTS_LOCKED 3
+#define WDTS_USE_GP 4
+#define WDTS_EXPECTED 5
+
+static unsigned int base, gpact, ciract, max_units, chip_type;
+static unsigned long wdt_status;
+static DEFINE_SPINLOCK(spinlock);
+
+static int nogameport = DEFAULT_NOGAMEPORT;
+static int exclusive = DEFAULT_EXCLUSIVE;
+static int timeout = DEFAULT_TIMEOUT;
+static int testmode = DEFAULT_TESTMODE;
+static int nowayout = DEFAULT_NOWAYOUT;
+
+module_param(nogameport, int, 0);
+MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
+ __MODULE_STRING(DEFAULT_NOGAMEPORT));
+module_param(exclusive, int, 0);
+MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default="
+ __MODULE_STRING(DEFAULT_EXCLUSIVE));
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
+ __MODULE_STRING(DEFAULT_TIMEOUT));
+module_param(testmode, int, 0);
+MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default="
+ __MODULE_STRING(DEFAULT_TESTMODE));
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT));
+
+/* Superio Chip */
+
+static inline void superio_enter(void)
+{
+ outb(0x87, REG);
+ outb(0x01, REG);
+ outb(0x55, REG);
+ outb(0x55, REG);
+}
+
+static inline void superio_exit(void)
+{
+ outb(0x02, REG);
+ outb(0x02, VAL);
+}
+
+static inline void superio_select(int ldn)
+{
+ outb(LDNREG, REG);
+ outb(ldn, VAL);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, REG);
+ return inb(VAL);
+}
+
+static inline void superio_outb(int val, int reg)
+{
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+static inline int superio_inw(int reg)
+{
+ int val;
+ outb(reg++, REG);
+ val = inb(VAL) << 8;
+ outb(reg, REG);
+ val |= inb(VAL);
+ return val;
+}
+
+static inline void superio_outw(int val, int reg)
+{
+ outb(reg++, REG);
+ outb(val >> 8, VAL);
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+/* Internal function, should be called after superio_select(GPIO) */
+static void wdt_update_timeout(void)
+{
+ unsigned char cfg = WDT_KRST;
+ int tm = timeout;
+
+ if (testmode)
+ cfg = 0;
+
+ if (tm <= max_units)
+ cfg |= WDT_TOV1;
+ else
+ tm /= 60;
+
+ if (chip_type != IT8721_ID)
+ cfg |= WDT_PWROK;
+
+ superio_outb(cfg, WDTCFG);
+ superio_outb(tm, WDTVALLSB);
+ if (max_units > 255)
+ superio_outb(tm>>8, WDTVALMSB);
+}
+
+static int wdt_round_time(int t)
+{
+ t += 59;
+ t -= t % 60;
+ return t;
+}
+
+/* watchdog timer handling */
+
+static void wdt_keepalive(void)
+{
+ if (test_bit(WDTS_USE_GP, &wdt_status))
+ inb(base);
+ else
+ /* The timer reloads with around 5 msec delay */
+ outb(0x55, CIR_DR(base));
+ set_bit(WDTS_KEEPALIVE, &wdt_status);
+}
+
+static void wdt_start(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(GPIO);
+ if (test_bit(WDTS_USE_GP, &wdt_status))
+ superio_outb(WDT_GAMEPORT, WDTCTRL);
+ else
+ superio_outb(WDT_CIRINT, WDTCTRL);
+ wdt_update_timeout();
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+}
+
+static void wdt_stop(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(GPIO);
+ superio_outb(0x00, WDTCTRL);
+ superio_outb(WDT_TOV1, WDTCFG);
+ superio_outb(0x00, WDTVALLSB);
+ if (max_units > 255)
+ superio_outb(0x00, WDTVALMSB);
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+}
+
+/**
+ * wdt_set_timeout - set a new timeout value with watchdog ioctl
+ * @t: timeout value in seconds
+ *
+ * The hardware device has a 8 or 16 bit watchdog timer (depends on
+ * chip version) that can be configured to count seconds or minutes.
+ *
+ * Used within WDIOC_SETTIMEOUT watchdog device ioctl.
+ */
+
+static int wdt_set_timeout(int t)
+{
+ unsigned long flags;
+
+ if (t < 1 || t > max_units * 60)
+ return -EINVAL;
+
+ if (t > max_units)
+ timeout = wdt_round_time(t);
+ else
+ timeout = t;
+
+ spin_lock_irqsave(&spinlock, flags);
+ if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
+ superio_enter();
+ superio_select(GPIO);
+ wdt_update_timeout();
+ superio_exit();
+ }
+ spin_unlock_irqrestore(&spinlock, flags);
+ return 0;
+}
+
+/**
+ * wdt_get_status - determines the status supported by watchdog ioctl
+ * @status: status returned to user space
+ *
+ * The status bit of the device does not allow to distinguish
+ * between a regular system reset and a watchdog forced reset.
+ * But, in test mode it is useful, so it is supported through
+ * WDIOC_GETSTATUS watchdog ioctl. Additionally the driver
+ * reports the keepalive signal and the acception of the magic.
+ *
+ * Used within WDIOC_GETSTATUS watchdog device ioctl.
+ */
+
+static int wdt_get_status(int *status)
+{
+ unsigned long flags;
+
+ *status = 0;
+ if (testmode) {
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(GPIO);
+ if (superio_inb(WDTCTRL) & WDT_ZERO) {
+ superio_outb(0x00, WDTCTRL);
+ clear_bit(WDTS_TIMER_RUN, &wdt_status);
+ *status |= WDIOF_CARDRESET;
+ }
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+ if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
+ *status |= WDIOF_KEEPALIVEPING;
+ if (test_bit(WDTS_EXPECTED, &wdt_status))
+ *status |= WDIOF_MAGICCLOSE;
+ return 0;
+}
+
+/* /dev/watchdog handling */
+
+/**
+ * wdt_open - watchdog file_operations .open
+ * @inode: inode of the device
+ * @file: file handle to the device
+ *
+ * The watchdog timer starts by opening the device.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
+ return -EBUSY;
+ if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
+ if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
+ __module_get(THIS_MODULE);
+ wdt_start();
+ }
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * wdt_release - watchdog file_operations .release
+ * @inode: inode of the device
+ * @file: file handle to the device
+ *
+ * Closing the watchdog device either stops the watchdog timer
+ * or in the case, that nowayout is set or the magic character
+ * wasn't written, a critical warning about an running watchdog
+ * timer is given.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static int wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
+ if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
+ wdt_stop();
+ clear_bit(WDTS_TIMER_RUN, &wdt_status);
+ } else {
+ wdt_keepalive();
+ printk(KERN_CRIT PFX
+ "unexpected close, not stopping watchdog!\n");
+ }
+ }
+ clear_bit(WDTS_DEV_OPEN, &wdt_status);
+ return 0;
+}
+
+/**
+ * wdt_write - watchdog file_operations .write
+ * @file: file handle to the watchdog
+ * @buf: buffer to write
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we don't define content meaning.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ clear_bit(WDTS_EXPECTED, &wdt_status);
+ wdt_keepalive();
+ }
+ if (!nowayout) {
+ size_t ofs;
+
+ /* note: just in case someone wrote the magic character long ago */
+ for (ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if (c == WD_MAGIC)
+ set_bit(WDTS_EXPECTED, &wdt_status);
+ }
+ }
+ return count;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = WATCHDOG_NAME,
+};
+
+/**
+ * wdt_ioctl - watchdog file_operations .unlocked_ioctl
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rc = 0, status, new_options, new_timeout;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident,
+ &ident, sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ wdt_get_status(&status);
+ return put_user(status, uarg.i);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ return 0;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, uarg.i))
+ return -EFAULT;
+
+ switch (new_options) {
+ case WDIOS_DISABLECARD:
+ if (test_bit(WDTS_TIMER_RUN, &wdt_status))
+ wdt_stop();
+ clear_bit(WDTS_TIMER_RUN, &wdt_status);
+ return 0;
+
+ case WDIOS_ENABLECARD:
+ if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status))
+ wdt_start();
+ return 0;
+
+ default:
+ return -EFAULT;
+ }
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+ rc = wdt_set_timeout(new_timeout);
+ case WDIOC_GETTIMEOUT:
+ if (put_user(timeout, uarg.i))
+ return -EFAULT;
+ return rc;
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_stop();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdt_write,
+ .unlocked_ioctl = wdt_ioctl,
+ .open = wdt_open,
+ .release = wdt_release,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static int __init it87_wdt_init(void)
+{
+ int rc = 0;
+ int try_gameport = !nogameport;
+ u8 chip_rev;
+ unsigned long flags;
+
+ wdt_status = 0;
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ chip_type = superio_inw(CHIPID);
+ chip_rev = superio_inb(CHIPREV) & 0x0f;
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ switch (chip_type) {
+ case IT8702_ID:
+ max_units = 255;
+ break;
+ case IT8712_ID:
+ max_units = (chip_rev < 8) ? 255 : 65535;
+ break;
+ case IT8716_ID:
+ case IT8726_ID:
+ max_units = 65535;
+ break;
+ case IT8718_ID:
+ case IT8720_ID:
+ case IT8721_ID:
+ max_units = 65535;
+ try_gameport = 0;
+ break;
+ case IT8705_ID:
+ printk(KERN_ERR PFX
+ "Unsupported Chip found, Chip %04x Revision %02x\n",
+ chip_type, chip_rev);
+ return -ENODEV;
+ case NO_DEV_ID:
+ printk(KERN_ERR PFX "no device\n");
+ return -ENODEV;
+ default:
+ printk(KERN_ERR PFX
+ "Unknown Chip found, Chip %04x Revision %04x\n",
+ chip_type, chip_rev);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(GPIO);
+ superio_outb(WDT_TOV1, WDTCFG);
+ superio_outb(0x00, WDTCTRL);
+
+ /* First try to get Gameport support */
+ if (try_gameport) {
+ superio_select(GAMEPORT);
+ base = superio_inw(BASEREG);
+ if (!base) {
+ base = GP_BASE_DEFAULT;
+ superio_outw(base, BASEREG);
+ }
+ gpact = superio_inb(ACTREG);
+ superio_outb(0x01, ACTREG);
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ if (request_region(base, 1, WATCHDOG_NAME))
+ set_bit(WDTS_USE_GP, &wdt_status);
+ else
+ rc = -EIO;
+ } else {
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+
+ /* If we haven't Gameport support, try to get CIR support */
+ if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
+ if (rc == -EIO)
+ printk(KERN_ERR PFX
+ "I/O Address 0x%04x and 0x%04x"
+ " already in use\n", base, CIR_BASE);
+ else
+ printk(KERN_ERR PFX
+ "I/O Address 0x%04x already in use\n",
+ CIR_BASE);
+ rc = -EIO;
+ goto err_out;
+ }
+ base = CIR_BASE;
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(CIR);
+ superio_outw(base, BASEREG);
+ superio_outb(0x00, CIR_ILS);
+ ciract = superio_inb(ACTREG);
+ superio_outb(0x01, ACTREG);
+ if (rc == -EIO) {
+ superio_select(GAMEPORT);
+ superio_outb(gpact, ACTREG);
+ }
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+
+ if (timeout < 1 || timeout > max_units * 60) {
+ timeout = DEFAULT_TIMEOUT;
+ printk(KERN_WARNING PFX
+ "Timeout value out of range, use default %d sec\n",
+ DEFAULT_TIMEOUT);
+ }
+
+ if (timeout > max_units)
+ timeout = wdt_round_time(timeout);
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "Cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out_region;
+ }
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "Cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
+ goto err_out_reboot;
+ }
+
+ /* Initialize CIR to use it as keepalive source */
+ if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ outb(0x00, CIR_RCR(base));
+ outb(0xc0, CIR_TCR1(base));
+ outb(0x5c, CIR_TCR2(base));
+ outb(0x10, CIR_IER(base));
+ outb(0x00, CIR_BDHR(base));
+ outb(0x01, CIR_BDLR(base));
+ outb(0x09, CIR_IER(base));
+ }
+
+ printk(KERN_INFO PFX "Chip IT%04x revision %d initialized. "
+ "timeout=%d sec (nowayout=%d testmode=%d exclusive=%d "
+ "nogameport=%d)\n", chip_type, chip_rev, timeout,
+ nowayout, testmode, exclusive, nogameport);
+
+ return 0;
+
+err_out_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+err_out_region:
+ release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
+ if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(CIR);
+ superio_outb(ciract, ACTREG);
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+err_out:
+ if (try_gameport) {
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(GAMEPORT);
+ superio_outb(gpact, ACTREG);
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+
+ return rc;
+}
+
+static void __exit it87_wdt_exit(void)
+{
+ unsigned long flags;
+ int nolock;
+
+ nolock = !spin_trylock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(GPIO);
+ superio_outb(0x00, WDTCTRL);
+ superio_outb(0x00, WDTCFG);
+ superio_outb(0x00, WDTVALLSB);
+ if (max_units > 255)
+ superio_outb(0x00, WDTVALMSB);
+ if (test_bit(WDTS_USE_GP, &wdt_status)) {
+ superio_select(GAMEPORT);
+ superio_outb(gpact, ACTREG);
+ } else {
+ superio_select(CIR);
+ superio_outb(ciract, ACTREG);
+ }
+ superio_exit();
+ if (!nolock)
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
+}
+
+module_init(it87_wdt_init);
+module_exit(it87_wdt_exit);
+
+MODULE_AUTHOR("Oliver Schuster");
+MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ixp2000_wdt.c b/drivers/watchdog/ixp2000_wdt.c
new file mode 100644
index 00000000..e86952a7
--- /dev/null
+++ b/drivers/watchdog/ixp2000_wdt.c
@@ -0,0 +1,215 @@
+/*
+ * drivers/char/watchdog/ixp2000_wdt.c
+ *
+ * Watchdog driver for Intel IXP2000 network processors
+ *
+ * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek.
+ * The original version carries these notices:
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2004 (c) MontaVista, Software, Inc.
+ * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <mach/hardware.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static unsigned int heartbeat = 60; /* (secs) Default is 1 minute */
+static unsigned long wdt_status;
+static spinlock_t wdt_lock;
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+static unsigned long wdt_tick_rate;
+
+static void wdt_enable(void)
+{
+ spin_lock(&wdt_lock);
+ ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE);
+ ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE);
+ ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
+ ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE);
+ spin_unlock(&wdt_lock);
+}
+
+static void wdt_disable(void)
+{
+ spin_lock(&wdt_lock);
+ ixp2000_reg_write(IXP2000_T4_CTL, 0);
+ spin_unlock(&wdt_lock);
+}
+
+static void wdt_keepalive(void)
+{
+ spin_lock(&wdt_lock);
+ ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
+ spin_unlock(&wdt_lock);
+}
+
+static int ixp2000_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t ixp2000_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_keepalive();
+ }
+
+ return len;
+}
+
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "IXP2000 Watchdog",
+};
+
+static long ixp2000_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time <= 0 || time > 60) {
+ ret = -EINVAL;
+ break;
+ }
+
+ heartbeat = time;
+ wdt_keepalive();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+ }
+
+ return ret;
+}
+
+static int ixp2000_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ wdt_disable();
+ else
+ printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
+ "timer will not stop\n");
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static const struct file_operations ixp2000_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ixp2000_wdt_write,
+ .unlocked_ioctl = ixp2000_wdt_ioctl,
+ .open = ixp2000_wdt_open,
+ .release = ixp2000_wdt_release,
+};
+
+static struct miscdevice ixp2000_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ixp2000_wdt_fops,
+};
+
+static int __init ixp2000_wdt_init(void)
+{
+ if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) {
+ printk(KERN_INFO "Unable to use IXP2000 watchdog due to IXP2800 erratum #25.\n");
+ return -EIO;
+ }
+ wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256;
+ spin_lock_init(&wdt_lock);
+ return misc_register(&ixp2000_wdt_miscdev);
+}
+
+static void __exit ixp2000_wdt_exit(void)
+{
+ misc_deregister(&ixp2000_wdt_miscdev);
+}
+
+module_init(ixp2000_wdt_init);
+module_exit(ixp2000_wdt_exit);
+
+MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
+MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c
new file mode 100644
index 00000000..e02c0ecd
--- /dev/null
+++ b/drivers/watchdog/ixp4xx_wdt.c
@@ -0,0 +1,214 @@
+/*
+ * drivers/char/watchdog/ixp4xx_wdt.c
+ *
+ * Watchdog driver for Intel IXP4xx network processors
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2004 (c) MontaVista, Software, Inc.
+ * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <mach/hardware.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static int heartbeat = 60; /* (secs) Default is 1 minute */
+static unsigned long wdt_status;
+static unsigned long boot_status;
+static DEFINE_SPINLOCK(wdt_lock);
+
+#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL)
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+static void wdt_enable(void)
+{
+ spin_lock(&wdt_lock);
+ *IXP4XX_OSWK = IXP4XX_WDT_KEY;
+ *IXP4XX_OSWE = 0;
+ *IXP4XX_OSWT = WDT_TICK_RATE * heartbeat;
+ *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE;
+ *IXP4XX_OSWK = 0;
+ spin_unlock(&wdt_lock);
+}
+
+static void wdt_disable(void)
+{
+ spin_lock(&wdt_lock);
+ *IXP4XX_OSWK = IXP4XX_WDT_KEY;
+ *IXP4XX_OSWE = 0;
+ *IXP4XX_OSWK = 0;
+ spin_unlock(&wdt_lock);
+}
+
+static int ixp4xx_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ wdt_enable();
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t
+ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_enable();
+ }
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
+ WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "IXP4xx Watchdog",
+};
+
+
+static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(boot_status, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time <= 0 || time > 60) {
+ ret = -EINVAL;
+ break;
+ }
+
+ heartbeat = time;
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int ixp4xx_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ wdt_disable();
+ else
+ printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
+ "timer will not stop\n");
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static const struct file_operations ixp4xx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ixp4xx_wdt_write,
+ .unlocked_ioctl = ixp4xx_wdt_ioctl,
+ .open = ixp4xx_wdt_open,
+ .release = ixp4xx_wdt_release,
+};
+
+static struct miscdevice ixp4xx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ixp4xx_wdt_fops,
+};
+
+static int __init ixp4xx_wdt_init(void)
+{
+ int ret;
+
+ if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) {
+ printk(KERN_ERR "IXP4XXX Watchdog: Rev. A0 IXP42x CPU detected"
+ " - watchdog disabled\n");
+
+ return -ENODEV;
+ }
+ spin_lock_init(&wdt_lock);
+ boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ?
+ WDIOF_CARDRESET : 0;
+ ret = misc_register(&ixp4xx_wdt_miscdev);
+ if (ret == 0)
+ printk(KERN_INFO "IXP4xx Watchdog Timer: heartbeat %d sec\n",
+ heartbeat);
+ return ret;
+}
+
+static void __exit ixp4xx_wdt_exit(void)
+{
+ misc_deregister(&ixp4xx_wdt_miscdev);
+}
+
+
+module_init(ixp4xx_wdt_init);
+module_exit(ixp4xx_wdt_exit);
+
+MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
+MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
new file mode 100644
index 00000000..684ba01f
--- /dev/null
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net>
+ * JZ4740 Watchdog driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <asm/mach-jz4740/timer.h>
+
+#define JZ_REG_WDT_TIMER_DATA 0x0
+#define JZ_REG_WDT_COUNTER_ENABLE 0x4
+#define JZ_REG_WDT_TIMER_COUNTER 0x8
+#define JZ_REG_WDT_TIMER_CONTROL 0xC
+
+#define JZ_WDT_CLOCK_PCLK 0x1
+#define JZ_WDT_CLOCK_RTC 0x2
+#define JZ_WDT_CLOCK_EXT 0x4
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+#define JZ_WDT_CLOCK_DIV_SHIFT 3
+
+#define JZ_WDT_CLOCK_DIV_1 (0 << JZ_WDT_CLOCK_DIV_SHIFT)
+#define JZ_WDT_CLOCK_DIV_4 (1 << JZ_WDT_CLOCK_DIV_SHIFT)
+#define JZ_WDT_CLOCK_DIV_16 (2 << JZ_WDT_CLOCK_DIV_SHIFT)
+#define JZ_WDT_CLOCK_DIV_64 (3 << JZ_WDT_CLOCK_DIV_SHIFT)
+#define JZ_WDT_CLOCK_DIV_256 (4 << JZ_WDT_CLOCK_DIV_SHIFT)
+#define JZ_WDT_CLOCK_DIV_1024 (5 << JZ_WDT_CLOCK_DIV_SHIFT)
+
+#define DEFAULT_HEARTBEAT 5
+#define MAX_HEARTBEAT 2048
+
+static struct {
+ void __iomem *base;
+ struct resource *mem;
+ struct clk *rtc_clk;
+ unsigned long status;
+} jz4740_wdt;
+
+static int heartbeat = DEFAULT_HEARTBEAT;
+
+
+static void jz4740_wdt_service(void)
+{
+ writew(0x0, jz4740_wdt.base + JZ_REG_WDT_TIMER_COUNTER);
+}
+
+static void jz4740_wdt_set_heartbeat(int new_heartbeat)
+{
+ unsigned int rtc_clk_rate;
+ unsigned int timeout_value;
+ unsigned short clock_div = JZ_WDT_CLOCK_DIV_1;
+
+ heartbeat = new_heartbeat;
+
+ rtc_clk_rate = clk_get_rate(jz4740_wdt.rtc_clk);
+
+ timeout_value = rtc_clk_rate * heartbeat;
+ while (timeout_value > 0xffff) {
+ if (clock_div == JZ_WDT_CLOCK_DIV_1024) {
+ /* Requested timeout too high;
+ * use highest possible value. */
+ timeout_value = 0xffff;
+ break;
+ }
+ timeout_value >>= 2;
+ clock_div += (1 << JZ_WDT_CLOCK_DIV_SHIFT);
+ }
+
+ writeb(0x0, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE);
+ writew(clock_div, jz4740_wdt.base + JZ_REG_WDT_TIMER_CONTROL);
+
+ writew((u16)timeout_value, jz4740_wdt.base + JZ_REG_WDT_TIMER_DATA);
+ writew(0x0, jz4740_wdt.base + JZ_REG_WDT_TIMER_COUNTER);
+ writew(clock_div | JZ_WDT_CLOCK_RTC,
+ jz4740_wdt.base + JZ_REG_WDT_TIMER_CONTROL);
+
+ writeb(0x1, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE);
+}
+
+static void jz4740_wdt_enable(void)
+{
+ jz4740_timer_enable_watchdog();
+ jz4740_wdt_set_heartbeat(heartbeat);
+}
+
+static void jz4740_wdt_disable(void)
+{
+ jz4740_timer_disable_watchdog();
+ writeb(0x0, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE);
+}
+
+static int jz4740_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &jz4740_wdt.status))
+ return -EBUSY;
+
+ jz4740_wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t jz4740_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status);
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status);
+ }
+ jz4740_wdt_service();
+ }
+
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING,
+ .identity = "jz4740 Watchdog",
+};
+
+static long jz4740_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int heartbeat_seconds;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ jz4740_wdt_service();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(heartbeat_seconds, (int __user *)arg))
+ return -EFAULT;
+
+ jz4740_wdt_set_heartbeat(heartbeat_seconds);
+ return 0;
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, (int *)arg);
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int jz4740_wdt_release(struct inode *inode, struct file *file)
+{
+ jz4740_wdt_service();
+
+ if (test_and_clear_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status))
+ jz4740_wdt_disable();
+
+ clear_bit(WDT_IN_USE, &jz4740_wdt.status);
+ return 0;
+}
+
+static const struct file_operations jz4740_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = jz4740_wdt_write,
+ .unlocked_ioctl = jz4740_wdt_ioctl,
+ .open = jz4740_wdt_open,
+ .release = jz4740_wdt_release,
+};
+
+static struct miscdevice jz4740_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &jz4740_wdt_fops,
+};
+
+static int __devinit jz4740_wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0, size;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "failed to get memory region resource\n");
+ return -ENXIO;
+ }
+
+ size = resource_size(res);
+ jz4740_wdt.mem = request_mem_region(res->start, size, pdev->name);
+ if (jz4740_wdt.mem == NULL) {
+ dev_err(dev, "failed to get memory region\n");
+ return -EBUSY;
+ }
+
+ jz4740_wdt.base = ioremap_nocache(res->start, size);
+ if (jz4740_wdt.base == NULL) {
+ dev_err(dev, "failed to map memory region\n");
+ ret = -EBUSY;
+ goto err_release_region;
+ }
+
+ jz4740_wdt.rtc_clk = clk_get(NULL, "rtc");
+ if (IS_ERR(jz4740_wdt.rtc_clk)) {
+ dev_err(dev, "cannot find RTC clock\n");
+ ret = PTR_ERR(jz4740_wdt.rtc_clk);
+ goto err_iounmap;
+ }
+
+ ret = misc_register(&jz4740_wdt_miscdev);
+ if (ret < 0) {
+ dev_err(dev, "cannot register misc device\n");
+ goto err_disable_clk;
+ }
+
+ return 0;
+
+err_disable_clk:
+ clk_put(jz4740_wdt.rtc_clk);
+err_iounmap:
+ iounmap(jz4740_wdt.base);
+err_release_region:
+ release_mem_region(jz4740_wdt.mem->start,
+ resource_size(jz4740_wdt.mem));
+ return ret;
+}
+
+
+static int __devexit jz4740_wdt_remove(struct platform_device *pdev)
+{
+ jz4740_wdt_disable();
+ misc_deregister(&jz4740_wdt_miscdev);
+ clk_put(jz4740_wdt.rtc_clk);
+
+ iounmap(jz4740_wdt.base);
+ jz4740_wdt.base = NULL;
+
+ release_mem_region(jz4740_wdt.mem->start,
+ resource_size(jz4740_wdt.mem));
+ jz4740_wdt.mem = NULL;
+
+ return 0;
+}
+
+
+static struct platform_driver jz4740_wdt_driver = {
+ .probe = jz4740_wdt_probe,
+ .remove = __devexit_p(jz4740_wdt_remove),
+ .driver = {
+ .name = "jz4740-wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+
+static int __init jz4740_wdt_init(void)
+{
+ return platform_driver_register(&jz4740_wdt_driver);
+}
+module_init(jz4740_wdt_init);
+
+static void __exit jz4740_wdt_exit(void)
+{
+ platform_driver_unregister(&jz4740_wdt_driver);
+}
+module_exit(jz4740_wdt_exit);
+
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_DESCRIPTION("jz4740 Watchdog Driver");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat period in seconds from 1 to "
+ __MODULE_STRING(MAX_HEARTBEAT) ", default "
+ __MODULE_STRING(DEFAULT_HEARTBEAT));
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:jz4740-wdt");
diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c
new file mode 100644
index 00000000..81147190
--- /dev/null
+++ b/drivers/watchdog/ks8695_wdt.c
@@ -0,0 +1,314 @@
+/*
+ * Watchdog driver for Kendin/Micrel KS8695.
+ *
+ * (C) 2007 Andrew Victor
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <mach/hardware.h>
+#include <mach/regs-timer.h>
+
+#define WDT_DEFAULT_TIME 5 /* seconds */
+#define WDT_MAX_TIME 171 /* seconds */
+
+static int wdt_time = WDT_DEFAULT_TIME;
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+module_param(wdt_time, int, 0);
+MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
+ __MODULE_STRING(WDT_DEFAULT_TIME) ")");
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+#endif
+
+
+static unsigned long ks8695wdt_busy;
+static spinlock_t ks8695_lock;
+
+/* ......................................................................... */
+
+/*
+ * Disable the watchdog.
+ */
+static inline void ks8695_wdt_stop(void)
+{
+ unsigned long tmcon;
+
+ spin_lock(&ks8695_lock);
+ /* disable timer0 */
+ tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
+ __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
+ spin_unlock(&ks8695_lock);
+}
+
+/*
+ * Enable and reset the watchdog.
+ */
+static inline void ks8695_wdt_start(void)
+{
+ unsigned long tmcon;
+ unsigned long tval = wdt_time * KS8695_CLOCK_RATE;
+
+ spin_lock(&ks8695_lock);
+ /* disable timer0 */
+ tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
+ __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
+
+ /* program timer0 */
+ __raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC);
+
+ /* re-enable timer0 */
+ tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
+ __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
+ spin_unlock(&ks8695_lock);
+}
+
+/*
+ * Reload the watchdog timer. (ie, pat the watchdog)
+ */
+static inline void ks8695_wdt_reload(void)
+{
+ unsigned long tmcon;
+
+ spin_lock(&ks8695_lock);
+ /* disable, then re-enable timer0 */
+ tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
+ __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
+ __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
+ spin_unlock(&ks8695_lock);
+}
+
+/*
+ * Change the watchdog time interval.
+ */
+static int ks8695_wdt_settimeout(int new_time)
+{
+ /*
+ * All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz
+ *
+ * Since WDV is a 16-bit counter, the maximum period is
+ * 65536 / 0.256 = 256 seconds.
+ */
+ if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
+ return -EINVAL;
+
+ /* Set new watchdog time. It will be used when
+ ks8695_wdt_start() is called. */
+ wdt_time = new_time;
+ return 0;
+}
+
+/* ......................................................................... */
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int ks8695_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &ks8695wdt_busy))
+ return -EBUSY;
+
+ ks8695_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * Close the watchdog device.
+ * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
+ * disabled.
+ */
+static int ks8695_wdt_close(struct inode *inode, struct file *file)
+{
+ /* Disable the watchdog when file is closed */
+ if (!nowayout)
+ ks8695_wdt_stop();
+ clear_bit(0, &ks8695wdt_busy);
+ return 0;
+}
+
+static const struct watchdog_info ks8695_wdt_info = {
+ .identity = "ks8695 watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+/*
+ * Handle commands from user-space.
+ */
+static long ks8695_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ks8695_wdt_info,
+ sizeof(ks8695_wdt_info)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ if (new_value & WDIOS_DISABLECARD)
+ ks8695_wdt_stop();
+ if (new_value & WDIOS_ENABLECARD)
+ ks8695_wdt_start();
+ return 0;
+ case WDIOC_KEEPALIVE:
+ ks8695_wdt_reload(); /* pat the watchdog */
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ if (ks8695_wdt_settimeout(new_value))
+ return -EINVAL;
+ /* Enable new time value */
+ ks8695_wdt_start();
+ /* Return current value */
+ return put_user(wdt_time, p);
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdt_time, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t ks8695_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ ks8695_wdt_reload(); /* pat the watchdog */
+ return len;
+}
+
+/* ......................................................................... */
+
+static const struct file_operations ks8695wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = ks8695_wdt_ioctl,
+ .open = ks8695_wdt_open,
+ .release = ks8695_wdt_close,
+ .write = ks8695_wdt_write,
+};
+
+static struct miscdevice ks8695wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ks8695wdt_fops,
+};
+
+static int __devinit ks8695wdt_probe(struct platform_device *pdev)
+{
+ int res;
+
+ if (ks8695wdt_miscdev.parent)
+ return -EBUSY;
+ ks8695wdt_miscdev.parent = &pdev->dev;
+
+ res = misc_register(&ks8695wdt_miscdev);
+ if (res)
+ return res;
+
+ printk(KERN_INFO "KS8695 Watchdog Timer enabled (%d seconds%s)\n",
+ wdt_time, nowayout ? ", nowayout" : "");
+ return 0;
+}
+
+static int __devexit ks8695wdt_remove(struct platform_device *pdev)
+{
+ int res;
+
+ res = misc_deregister(&ks8695wdt_miscdev);
+ if (!res)
+ ks8695wdt_miscdev.parent = NULL;
+
+ return res;
+}
+
+static void ks8695wdt_shutdown(struct platform_device *pdev)
+{
+ ks8695_wdt_stop();
+}
+
+#ifdef CONFIG_PM
+
+static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ ks8695_wdt_stop();
+ return 0;
+}
+
+static int ks8695wdt_resume(struct platform_device *pdev)
+{
+ if (ks8695wdt_busy)
+ ks8695_wdt_start();
+ return 0;
+}
+
+#else
+#define ks8695wdt_suspend NULL
+#define ks8695wdt_resume NULL
+#endif
+
+static struct platform_driver ks8695wdt_driver = {
+ .probe = ks8695wdt_probe,
+ .remove = __devexit_p(ks8695wdt_remove),
+ .shutdown = ks8695wdt_shutdown,
+ .suspend = ks8695wdt_suspend,
+ .resume = ks8695wdt_resume,
+ .driver = {
+ .name = "ks8695_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ks8695_wdt_init(void)
+{
+ spin_lock_init(&ks8695_lock);
+ /* Check that the heartbeat value is within range;
+ if not reset to the default */
+ if (ks8695_wdt_settimeout(wdt_time)) {
+ ks8695_wdt_settimeout(WDT_DEFAULT_TIME);
+ pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i"
+ ", using %d\n", wdt_time, WDT_MAX_TIME);
+ }
+ return platform_driver_register(&ks8695wdt_driver);
+}
+
+static void __exit ks8695_wdt_exit(void)
+{
+ platform_driver_unregister(&ks8695wdt_driver);
+}
+
+module_init(ks8695_wdt_init);
+module_exit(ks8695_wdt_exit);
+
+MODULE_AUTHOR("Andrew Victor");
+MODULE_DESCRIPTION("Watchdog driver for KS8695");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:ks8695_wdt");
diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c
new file mode 100644
index 00000000..7d82adac
--- /dev/null
+++ b/drivers/watchdog/lantiq_wdt.c
@@ -0,0 +1,261 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ * Based on EP93xx wdt driver
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <lantiq.h>
+
+/* Section 3.4 of the datasheet
+ * The password sequence protects the WDT control register from unintended
+ * write actions, which might cause malfunction of the WDT.
+ *
+ * essentially the following two magic passwords need to be written to allow
+ * IO access to the WDT core
+ */
+#define LTQ_WDT_PW1 0x00BE0000
+#define LTQ_WDT_PW2 0x00DC0000
+
+#define LTQ_WDT_CR 0x0 /* watchdog control register */
+#define LTQ_WDT_SR 0x8 /* watchdog status register */
+
+#define LTQ_WDT_SR_EN (0x1 << 31) /* enable bit */
+#define LTQ_WDT_SR_PWD (0x3 << 26) /* turn on power */
+#define LTQ_WDT_SR_CLKDIV (0x3 << 24) /* turn on clock and set */
+ /* divider to 0x40000 */
+#define LTQ_WDT_DIVIDER 0x40000
+#define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+static void __iomem *ltq_wdt_membase;
+static unsigned long ltq_io_region_clk_rate;
+
+static unsigned long ltq_wdt_bootstatus;
+static unsigned long ltq_wdt_in_use;
+static int ltq_wdt_timeout = 30;
+static int ltq_wdt_ok_to_close;
+
+static void
+ltq_wdt_enable(void)
+{
+ ltq_wdt_timeout = ltq_wdt_timeout *
+ (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000;
+ if (ltq_wdt_timeout > LTQ_MAX_TIMEOUT)
+ ltq_wdt_timeout = LTQ_MAX_TIMEOUT;
+
+ /* write the first password magic */
+ ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
+ /* write the second magic plus the configuration and new timeout */
+ ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV |
+ LTQ_WDT_PW2 | ltq_wdt_timeout, ltq_wdt_membase + LTQ_WDT_CR);
+}
+
+static void
+ltq_wdt_disable(void)
+{
+ /* write the first password magic */
+ ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
+ /* write the second password magic with no config
+ * this turns the watchdog off
+ */
+ ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR);
+}
+
+static ssize_t
+ltq_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ ltq_wdt_ok_to_close = 0;
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ ltq_wdt_ok_to_close = 1;
+ else
+ ltq_wdt_ok_to_close = 0;
+ }
+ }
+ ltq_wdt_enable();
+ }
+
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_CARDRESET,
+ .identity = "ltq_wdt",
+};
+
+static long
+ltq_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(ltq_wdt_bootstatus, (int __user *)arg);
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int __user *)arg);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(ltq_wdt_timeout, (int __user *)arg);
+ if (!ret)
+ ltq_wdt_enable();
+ /* intentional drop through */
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(ltq_wdt_timeout, (int __user *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ ltq_wdt_enable();
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static int
+ltq_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &ltq_wdt_in_use))
+ return -EBUSY;
+ ltq_wdt_in_use = 1;
+ ltq_wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static int
+ltq_wdt_release(struct inode *inode, struct file *file)
+{
+ if (ltq_wdt_ok_to_close)
+ ltq_wdt_disable();
+ else
+ pr_err("ltq_wdt: watchdog closed without warning\n");
+ ltq_wdt_ok_to_close = 0;
+ clear_bit(0, &ltq_wdt_in_use);
+
+ return 0;
+}
+
+static const struct file_operations ltq_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = ltq_wdt_write,
+ .unlocked_ioctl = ltq_wdt_ioctl,
+ .open = ltq_wdt_open,
+ .release = ltq_wdt_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice ltq_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ltq_wdt_fops,
+};
+
+static int __init
+ltq_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct clk *clk;
+
+ if (!res) {
+ dev_err(&pdev->dev, "cannot obtain I/O memory region");
+ return -ENOENT;
+ }
+ res = devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), dev_name(&pdev->dev));
+ if (!res) {
+ dev_err(&pdev->dev, "cannot request I/O memory region");
+ return -EBUSY;
+ }
+ ltq_wdt_membase = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
+ if (!ltq_wdt_membase) {
+ dev_err(&pdev->dev, "cannot remap I/O memory region\n");
+ return -ENOMEM;
+ }
+
+ /* we do not need to enable the clock as it is always running */
+ clk = clk_get(&pdev->dev, "io");
+ WARN_ON(!clk);
+ ltq_io_region_clk_rate = clk_get_rate(clk);
+ clk_put(clk);
+
+ if (ltq_reset_cause() == LTQ_RST_CAUSE_WDTRST)
+ ltq_wdt_bootstatus = WDIOF_CARDRESET;
+
+ return misc_register(&ltq_wdt_miscdev);
+}
+
+static int __devexit
+ltq_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&ltq_wdt_miscdev);
+
+ if (ltq_wdt_membase)
+ iounmap(ltq_wdt_membase);
+
+ return 0;
+}
+
+
+static struct platform_driver ltq_wdt_driver = {
+ .remove = __devexit_p(ltq_wdt_remove),
+ .driver = {
+ .name = "ltq_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init
+init_ltq_wdt(void)
+{
+ return platform_driver_probe(&ltq_wdt_driver, ltq_wdt_probe);
+}
+
+static void __exit
+exit_ltq_wdt(void)
+{
+ return platform_driver_unregister(&ltq_wdt_driver);
+}
+
+module_init(init_ltq_wdt);
+module_exit(exit_ltq_wdt);
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("Lantiq SoC Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/m54xx_wdt.c b/drivers/watchdog/m54xx_wdt.c
new file mode 100644
index 00000000..4d432860
--- /dev/null
+++ b/drivers/watchdog/m54xx_wdt.c
@@ -0,0 +1,227 @@
+/*
+ * drivers/watchdog/m54xx_wdt.c
+ *
+ * Watchdog driver for ColdFire MCF547x & MCF548x processors
+ * Copyright 2010 (c) Philippe De Muyter <phdm@macqel.be>
+ *
+ * Adapted from the IXP4xx watchdog driver, which carries these notices:
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2004 (c) MontaVista, Software, Inc.
+ * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/ioport.h>
+#include <linux/uaccess.h>
+
+#include <asm/coldfire.h>
+#include <asm/m54xxsim.h>
+#include <asm/m54xxgpt.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static unsigned int heartbeat = 30; /* (secs) Default is 0.5 minute */
+static unsigned long wdt_status;
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+static void wdt_enable(void)
+{
+ unsigned int gms0;
+
+ /* preserve GPIO usage, if any */
+ gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0);
+ if (gms0 & MCF_GPT_GMS_TMS_GPIO)
+ gms0 &= (MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_GPIO_MASK
+ | MCF_GPT_GMS_OD);
+ else
+ gms0 = MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_OD;
+ __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+ __raw_writel(MCF_GPT_GCIR_PRE(heartbeat*(MCF_BUSCLK/0xffff)) |
+ MCF_GPT_GCIR_CNT(0xffff), MCF_MBAR + MCF_GPT_GCIR0);
+ gms0 |= MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE;
+ __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+}
+
+static void wdt_disable(void)
+{
+ unsigned int gms0;
+
+ /* disable watchdog */
+ gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0);
+ gms0 &= ~(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE);
+ __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+}
+
+static void wdt_keepalive(void)
+{
+ unsigned int gms0;
+
+ gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0);
+ gms0 |= MCF_GPT_GMS_OCPW(0xA5);
+ __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0);
+}
+
+static int m54xx_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ wdt_enable();
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t m54xx_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_keepalive();
+ }
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "Coldfire M54xx Watchdog",
+};
+
+static long m54xx_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time <= 0 || time > 30) {
+ ret = -EINVAL;
+ break;
+ }
+
+ heartbeat = time;
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int m54xx_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ wdt_disable();
+ else {
+ printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
+ "timer will not stop\n");
+ wdt_keepalive();
+ }
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static const struct file_operations m54xx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = m54xx_wdt_write,
+ .unlocked_ioctl = m54xx_wdt_ioctl,
+ .open = m54xx_wdt_open,
+ .release = m54xx_wdt_release,
+};
+
+static struct miscdevice m54xx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &m54xx_wdt_fops,
+};
+
+static int __init m54xx_wdt_init(void)
+{
+ if (!request_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4,
+ "Coldfire M54xx Watchdog")) {
+ printk(KERN_WARNING
+ "Coldfire M54xx Watchdog : I/O region busy\n");
+ return -EBUSY;
+ }
+ printk(KERN_INFO "ColdFire watchdog driver is loaded.\n");
+
+ return misc_register(&m54xx_wdt_miscdev);
+}
+
+static void __exit m54xx_wdt_exit(void)
+{
+ misc_deregister(&m54xx_wdt_miscdev);
+ release_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4);
+}
+
+module_init(m54xx_wdt_init);
+module_exit(m54xx_wdt_exit);
+
+MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
+MODULE_DESCRIPTION("Coldfire M54xx Watchdog");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 30s)");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c
new file mode 100644
index 00000000..1332b838
--- /dev/null
+++ b/drivers/watchdog/machzwd.c
@@ -0,0 +1,461 @@
+/*
+ * MachZ ZF-Logic Watchdog Timer driver for Linux
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * The author does NOT admit liability nor provide warranty for
+ * any of this software. This material is provided "AS-IS" in
+ * the hope that it may be useful for others.
+ *
+ * Author: Fernando Fuganti <fuganti@conectiva.com.br>
+ *
+ * Based on sbc60xxwdt.c by Jakob Oestergaard
+ *
+ *
+ * We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the
+ * following periods:
+ * wd#1 - 2 seconds;
+ * wd#2 - 7.2 ms;
+ * After the expiration of wd#1, it can generate a NMI, SCI, SMI, or
+ * a system RESET and it starts wd#2 that unconditionally will RESET
+ * the system when the counter reaches zero.
+ *
+ * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
+ * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+/* ports */
+#define ZF_IOBASE 0x218
+#define INDEX 0x218
+#define DATA_B 0x219
+#define DATA_W 0x21A
+#define DATA_D 0x21A
+
+/* indexes */ /* size */
+#define ZFL_VERSION 0x02 /* 16 */
+#define CONTROL 0x10 /* 16 */
+#define STATUS 0x12 /* 8 */
+#define COUNTER_1 0x0C /* 16 */
+#define COUNTER_2 0x0E /* 8 */
+#define PULSE_LEN 0x0F /* 8 */
+
+/* controls */
+#define ENABLE_WD1 0x0001
+#define ENABLE_WD2 0x0002
+#define RESET_WD1 0x0010
+#define RESET_WD2 0x0020
+#define GEN_SCI 0x0100
+#define GEN_NMI 0x0200
+#define GEN_SMI 0x0400
+#define GEN_RESET 0x0800
+
+
+/* utilities */
+
+#define WD1 0
+#define WD2 1
+
+#define zf_writew(port, data) { outb(port, INDEX); outw(data, DATA_W); }
+#define zf_writeb(port, data) { outb(port, INDEX); outb(data, DATA_B); }
+#define zf_get_ZFL_version() zf_readw(ZFL_VERSION)
+
+
+static unsigned short zf_readw(unsigned char port)
+{
+ outb(port, INDEX);
+ return inw(DATA_W);
+}
+
+
+MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>");
+MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#define PFX "machzwd"
+
+static const struct watchdog_info zf_info = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "ZF-Logic watchdog",
+};
+
+
+/*
+ * action refers to action taken when watchdog resets
+ * 0 = GEN_RESET
+ * 1 = GEN_SMI
+ * 2 = GEN_NMI
+ * 3 = GEN_SCI
+ * defaults to GEN_RESET (0)
+ */
+static int action;
+module_param(action, int, 0);
+MODULE_PARM_DESC(action, "after watchdog resets, generate: "
+ "0 = RESET(*) 1 = SMI 2 = NMI 3 = SCI");
+
+static void zf_ping(unsigned long data);
+
+static int zf_action = GEN_RESET;
+static unsigned long zf_is_open;
+static char zf_expect_close;
+static DEFINE_SPINLOCK(zf_port_lock);
+static DEFINE_TIMER(zf_timer, zf_ping, 0, 0);
+static unsigned long next_heartbeat;
+
+
+/* timeout for user land heart beat (10 seconds) */
+#define ZF_USER_TIMEO (HZ*10)
+
+/* timeout for hardware watchdog (~500ms) */
+#define ZF_HW_TIMEO (HZ/2)
+
+/* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */
+#define ZF_CTIMEOUT 0xffff
+
+#ifndef ZF_DEBUG
+# define dprintk(format, args...)
+#else
+# define dprintk(format, args...) printk(KERN_DEBUG PFX \
+ ":%s:%d: " format, __func__, __LINE__ , ## args)
+#endif
+
+
+static inline void zf_set_status(unsigned char new)
+{
+ zf_writeb(STATUS, new);
+}
+
+
+/* CONTROL register functions */
+
+static inline unsigned short zf_get_control(void)
+{
+ return zf_readw(CONTROL);
+}
+
+static inline void zf_set_control(unsigned short new)
+{
+ zf_writew(CONTROL, new);
+}
+
+
+/* WD#? counter functions */
+/*
+ * Just set counter value
+ */
+
+static inline void zf_set_timer(unsigned short new, unsigned char n)
+{
+ switch (n) {
+ case WD1:
+ zf_writew(COUNTER_1, new);
+ case WD2:
+ zf_writeb(COUNTER_2, new > 0xff ? 0xff : new);
+ default:
+ return;
+ }
+}
+
+/*
+ * stop hardware timer
+ */
+static void zf_timer_off(void)
+{
+ unsigned int ctrl_reg = 0;
+ unsigned long flags;
+
+ /* stop internal ping */
+ del_timer_sync(&zf_timer);
+
+ spin_lock_irqsave(&zf_port_lock, flags);
+ /* stop watchdog timer */
+ ctrl_reg = zf_get_control();
+ ctrl_reg |= (ENABLE_WD1|ENABLE_WD2); /* disable wd1 and wd2 */
+ ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2);
+ zf_set_control(ctrl_reg);
+ spin_unlock_irqrestore(&zf_port_lock, flags);
+
+ printk(KERN_INFO PFX ": Watchdog timer is now disabled\n");
+}
+
+
+/*
+ * start hardware timer
+ */
+static void zf_timer_on(void)
+{
+ unsigned int ctrl_reg = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&zf_port_lock, flags);
+
+ zf_writeb(PULSE_LEN, 0xff);
+
+ zf_set_timer(ZF_CTIMEOUT, WD1);
+
+ /* user land ping */
+ next_heartbeat = jiffies + ZF_USER_TIMEO;
+
+ /* start the timer for internal ping */
+ mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO);
+
+ /* start watchdog timer */
+ ctrl_reg = zf_get_control();
+ ctrl_reg |= (ENABLE_WD1|zf_action);
+ zf_set_control(ctrl_reg);
+ spin_unlock_irqrestore(&zf_port_lock, flags);
+
+ printk(KERN_INFO PFX ": Watchdog timer is now enabled\n");
+}
+
+
+static void zf_ping(unsigned long data)
+{
+ unsigned int ctrl_reg = 0;
+ unsigned long flags;
+
+ zf_writeb(COUNTER_2, 0xff);
+
+ if (time_before(jiffies, next_heartbeat)) {
+ dprintk("time_before: %ld\n", next_heartbeat - jiffies);
+ /*
+ * reset event is activated by transition from 0 to 1 on
+ * RESET_WD1 bit and we assume that it is already zero...
+ */
+
+ spin_lock_irqsave(&zf_port_lock, flags);
+ ctrl_reg = zf_get_control();
+ ctrl_reg |= RESET_WD1;
+ zf_set_control(ctrl_reg);
+
+ /* ...and nothing changes until here */
+ ctrl_reg &= ~(RESET_WD1);
+ zf_set_control(ctrl_reg);
+ spin_unlock_irqrestore(&zf_port_lock, flags);
+
+ mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO);
+ } else
+ printk(KERN_CRIT PFX ": I will reset your machine\n");
+}
+
+static ssize_t zf_write(struct file *file, const char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ /* See if we got the magic character */
+ if (count) {
+ /*
+ * no need to check for close confirmation
+ * no way to disable watchdog ;)
+ */
+ if (!nowayout) {
+ size_t ofs;
+ /*
+ * note: just in case someone wrote the magic character
+ * five months ago...
+ */
+ zf_expect_close = 0;
+
+ /* now scan */
+ for (ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if (c == 'V') {
+ zf_expect_close = 42;
+ dprintk("zf_expect_close = 42\n");
+ }
+ }
+ }
+
+ /*
+ * Well, anyhow someone wrote to us,
+ * we should return that favour
+ */
+ next_heartbeat = jiffies + ZF_USER_TIMEO;
+ dprintk("user ping at %ld\n", jiffies);
+ }
+ return count;
+}
+
+static long zf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &zf_info, sizeof(zf_info)))
+ return -EFAULT;
+ break;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_KEEPALIVE:
+ zf_ping(0);
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int zf_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &zf_is_open))
+ return -EBUSY;
+ if (nowayout)
+ __module_get(THIS_MODULE);
+ zf_timer_on();
+ return nonseekable_open(inode, file);
+}
+
+static int zf_close(struct inode *inode, struct file *file)
+{
+ if (zf_expect_close == 42)
+ zf_timer_off();
+ else {
+ del_timer(&zf_timer);
+ printk(KERN_ERR PFX ": device file closed unexpectedly. "
+ "Will not stop the WDT!\n");
+ }
+ clear_bit(0, &zf_is_open);
+ zf_expect_close = 0;
+ return 0;
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int zf_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ zf_timer_off();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations zf_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = zf_write,
+ .unlocked_ioctl = zf_ioctl,
+ .open = zf_open,
+ .release = zf_close,
+};
+
+static struct miscdevice zf_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &zf_fops,
+};
+
+
+/*
+ * The device needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+static struct notifier_block zf_notifier = {
+ .notifier_call = zf_notify_sys,
+};
+
+static void __init zf_show_action(int act)
+{
+ static const char * const str[] = { "RESET", "SMI", "NMI", "SCI" };
+
+ printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]);
+}
+
+static int __init zf_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO PFX
+ ": MachZ ZF-Logic Watchdog driver initializing.\n");
+
+ ret = zf_get_ZFL_version();
+ if (!ret || ret == 0xffff) {
+ printk(KERN_WARNING PFX ": no ZF-Logic found\n");
+ return -ENODEV;
+ }
+
+ if (action <= 3 && action >= 0)
+ zf_action = zf_action >> action;
+ else
+ action = 0;
+
+ zf_show_action(action);
+
+ if (!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")) {
+ printk(KERN_ERR "cannot reserve I/O ports at %d\n",
+ ZF_IOBASE);
+ ret = -EBUSY;
+ goto no_region;
+ }
+
+ ret = register_reboot_notifier(&zf_notifier);
+ if (ret) {
+ printk(KERN_ERR "can't register reboot notifier (err=%d)\n",
+ ret);
+ goto no_reboot;
+ }
+
+ ret = misc_register(&zf_miscdev);
+ if (ret) {
+ printk(KERN_ERR "can't misc_register on minor=%d\n",
+ WATCHDOG_MINOR);
+ goto no_misc;
+ }
+
+ zf_set_status(0);
+ zf_set_control(0);
+
+ return 0;
+
+no_misc:
+ unregister_reboot_notifier(&zf_notifier);
+no_reboot:
+ release_region(ZF_IOBASE, 3);
+no_region:
+ return ret;
+}
+
+
+static void __exit zf_exit(void)
+{
+ zf_timer_off();
+
+ misc_deregister(&zf_miscdev);
+ unregister_reboot_notifier(&zf_notifier);
+ release_region(ZF_IOBASE, 3);
+}
+
+module_init(zf_init);
+module_exit(zf_exit);
diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c
new file mode 100644
index 00000000..73ba2fd8
--- /dev/null
+++ b/drivers/watchdog/max63xx_wdt.c
@@ -0,0 +1,399 @@
+/*
+ * drivers/char/watchdog/max63xx_wdt.c
+ *
+ * Driver for max63{69,70,71,72,73,74} watchdog timers
+ *
+ * Copyright (C) 2009 Marc Zyngier <maz@misterjones.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * This driver assumes the watchdog pins are memory mapped (as it is
+ * the case for the Arcom Zeus). Should it be connected over GPIOs or
+ * another interface, some abstraction will have to be introduced.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#define DEFAULT_HEARTBEAT 60
+#define MAX_HEARTBEAT 60
+
+static int heartbeat = DEFAULT_HEARTBEAT;
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+/*
+ * Memory mapping: a single byte, 3 first lower bits to select bit 3
+ * to ping the watchdog.
+ */
+#define MAX6369_WDSET (7 << 0)
+#define MAX6369_WDI (1 << 3)
+
+static DEFINE_SPINLOCK(io_lock);
+
+static unsigned long wdt_status;
+#define WDT_IN_USE 0
+#define WDT_RUNNING 1
+#define WDT_OK_TO_CLOSE 2
+
+static int nodelay;
+static struct resource *wdt_mem;
+static void __iomem *wdt_base;
+static struct platform_device *max63xx_pdev;
+
+/*
+ * The timeout values used are actually the absolute minimum the chip
+ * offers. Typical values on my board are slightly over twice as long
+ * (10s setting ends up with a 25s timeout), and can be up to 3 times
+ * the nominal setting (according to the datasheet). So please take
+ * these values with a grain of salt. Same goes for the initial delay
+ * "feature". Only max6373/74 have a few settings without this initial
+ * delay (selected with the "nodelay" parameter).
+ *
+ * I also decided to remove from the tables any timeout smaller than a
+ * second, as it looked completly overkill...
+ */
+
+/* Timeouts in second */
+struct max63xx_timeout {
+ u8 wdset;
+ u8 tdelay;
+ u8 twd;
+};
+
+static struct max63xx_timeout max6369_table[] = {
+ { 5, 1, 1 },
+ { 6, 10, 10 },
+ { 7, 60, 60 },
+ { },
+};
+
+static struct max63xx_timeout max6371_table[] = {
+ { 6, 60, 3 },
+ { 7, 60, 60 },
+ { },
+};
+
+static struct max63xx_timeout max6373_table[] = {
+ { 2, 60, 1 },
+ { 5, 0, 1 },
+ { 1, 3, 3 },
+ { 7, 60, 10 },
+ { 6, 0, 10 },
+ { },
+};
+
+static struct max63xx_timeout *current_timeout;
+
+static struct max63xx_timeout *
+max63xx_select_timeout(struct max63xx_timeout *table, int value)
+{
+ while (table->twd) {
+ if (value <= table->twd) {
+ if (nodelay && table->tdelay == 0)
+ return table;
+
+ if (!nodelay)
+ return table;
+ }
+
+ table++;
+ }
+
+ return NULL;
+}
+
+static void max63xx_wdt_ping(void)
+{
+ u8 val;
+
+ spin_lock(&io_lock);
+
+ val = __raw_readb(wdt_base);
+
+ __raw_writeb(val | MAX6369_WDI, wdt_base);
+ __raw_writeb(val & ~MAX6369_WDI, wdt_base);
+
+ spin_unlock(&io_lock);
+}
+
+static void max63xx_wdt_enable(struct max63xx_timeout *entry)
+{
+ u8 val;
+
+ if (test_and_set_bit(WDT_RUNNING, &wdt_status))
+ return;
+
+ spin_lock(&io_lock);
+
+ val = __raw_readb(wdt_base);
+ val &= ~MAX6369_WDSET;
+ val |= entry->wdset;
+ __raw_writeb(val, wdt_base);
+
+ spin_unlock(&io_lock);
+
+ /* check for a edge triggered startup */
+ if (entry->tdelay == 0)
+ max63xx_wdt_ping();
+}
+
+static void max63xx_wdt_disable(void)
+{
+ u8 val;
+
+ spin_lock(&io_lock);
+
+ val = __raw_readb(wdt_base);
+ val &= ~MAX6369_WDSET;
+ val |= 3;
+ __raw_writeb(val, wdt_base);
+
+ spin_unlock(&io_lock);
+
+ clear_bit(WDT_RUNNING, &wdt_status);
+}
+
+static int max63xx_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ max63xx_wdt_enable(current_timeout);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t max63xx_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+
+ max63xx_wdt_ping();
+ }
+
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+ .identity = "max63xx Watchdog",
+};
+
+static long max63xx_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ max63xx_wdt_ping();
+ ret = 0;
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int max63xx_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ max63xx_wdt_disable();
+ else
+ dev_crit(&max63xx_pdev->dev,
+ "device closed unexpectedly - timer will not stop\n");
+
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+static const struct file_operations max63xx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = max63xx_wdt_write,
+ .unlocked_ioctl = max63xx_wdt_ioctl,
+ .open = max63xx_wdt_open,
+ .release = max63xx_wdt_release,
+};
+
+static struct miscdevice max63xx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &max63xx_wdt_fops,
+};
+
+static int __devinit max63xx_wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int size;
+ struct device *dev = &pdev->dev;
+ struct max63xx_timeout *table;
+
+ table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
+
+ if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
+ heartbeat = DEFAULT_HEARTBEAT;
+
+ dev_info(dev, "requesting %ds heartbeat\n", heartbeat);
+ current_timeout = max63xx_select_timeout(table, heartbeat);
+
+ if (!current_timeout) {
+ dev_err(dev, "unable to satisfy heartbeat request\n");
+ return -EINVAL;
+ }
+
+ dev_info(dev, "using %ds heartbeat with %ds initial delay\n",
+ current_timeout->twd, current_timeout->tdelay);
+
+ heartbeat = current_timeout->twd;
+
+ max63xx_pdev = pdev;
+
+ wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (wdt_mem == NULL) {
+ dev_err(dev, "failed to get memory region resource\n");
+ return -ENOENT;
+ }
+
+ size = resource_size(wdt_mem);
+ if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
+ dev_err(dev, "failed to get memory region\n");
+ return -ENOENT;
+ }
+
+ wdt_base = ioremap(wdt_mem->start, size);
+ if (!wdt_base) {
+ dev_err(dev, "failed to map memory region\n");
+ ret = -ENOMEM;
+ goto out_request;
+ }
+
+ ret = misc_register(&max63xx_wdt_miscdev);
+ if (ret < 0) {
+ dev_err(dev, "cannot register misc device\n");
+ goto out_unmap;
+ }
+
+ return 0;
+
+out_unmap:
+ iounmap(wdt_base);
+out_request:
+ release_mem_region(wdt_mem->start, size);
+ wdt_mem = NULL;
+
+ return ret;
+}
+
+static int __devexit max63xx_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&max63xx_wdt_miscdev);
+ if (wdt_mem) {
+ release_mem_region(wdt_mem->start, resource_size(wdt_mem));
+ wdt_mem = NULL;
+ }
+
+ if (wdt_base)
+ iounmap(wdt_base);
+
+ return 0;
+}
+
+static struct platform_device_id max63xx_id_table[] = {
+ { "max6369_wdt", (kernel_ulong_t)max6369_table, },
+ { "max6370_wdt", (kernel_ulong_t)max6369_table, },
+ { "max6371_wdt", (kernel_ulong_t)max6371_table, },
+ { "max6372_wdt", (kernel_ulong_t)max6371_table, },
+ { "max6373_wdt", (kernel_ulong_t)max6373_table, },
+ { "max6374_wdt", (kernel_ulong_t)max6373_table, },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, max63xx_id_table);
+
+static struct platform_driver max63xx_wdt_driver = {
+ .probe = max63xx_wdt_probe,
+ .remove = __devexit_p(max63xx_wdt_remove),
+ .id_table = max63xx_id_table,
+ .driver = {
+ .name = "max63xx_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init max63xx_wdt_init(void)
+{
+ return platform_driver_register(&max63xx_wdt_driver);
+}
+
+static void __exit max63xx_wdt_exit(void)
+{
+ platform_driver_unregister(&max63xx_wdt_driver);
+}
+
+module_init(max63xx_wdt_init);
+module_exit(max63xx_wdt_exit);
+
+MODULE_AUTHOR("Marc Zyngier <maz@misterjones.org>");
+MODULE_DESCRIPTION("max63xx Watchdog Driver");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat period in seconds from 1 to "
+ __MODULE_STRING(MAX_HEARTBEAT) ", default "
+ __MODULE_STRING(DEFAULT_HEARTBEAT));
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+module_param(nodelay, int, 0);
+MODULE_PARM_DESC(nodelay,
+ "Force selection of a timeout setting without initial delay "
+ "(max6373/74 only, default=0)");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mixcomwd.c b/drivers/watchdog/mixcomwd.c
new file mode 100644
index 00000000..bc820d16
--- /dev/null
+++ b/drivers/watchdog/mixcomwd.c
@@ -0,0 +1,323 @@
+/*
+ * MixCom Watchdog: A Simple Hardware Watchdog Device
+ * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (c) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.1 (99/04/15):
+ * - first version
+ *
+ * Version 0.2 (99/06/16):
+ * - added kernel timer watchdog ping after close
+ * since the hardware does not support watchdog shutdown
+ *
+ * Version 0.3 (99/06/21):
+ * - added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls
+ *
+ * Version 0.3.1 (99/06/22):
+ * - allow module removal while internal timer is active,
+ * print warning about probable reset
+ *
+ * Version 0.4 (99/11/15):
+ * - support for one more type board
+ *
+ * Version 0.5 (2001/12/14) Matt Domsch <Matt_Domsch@dell.com>
+ * - added nowayout module option to override
+ * CONFIG_WATCHDOG_NOWAYOUT
+ *
+ * Version 0.6 (2002/04/12): Rob Radez <rob@osinvestor.com>
+ * - make mixcomwd_opened unsigned,
+ * removed lock_kernel/unlock_kernel from mixcomwd_release,
+ * modified ioctl a bit to conform to API
+ *
+ */
+
+#define VERSION "0.6"
+#define WATCHDOG_NAME "mixcomwd"
+#define PFX WATCHDOG_NAME ": "
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+/*
+ * We have two types of cards that can be probed:
+ * 1) The Mixcom cards: these cards can be found at addresses
+ * 0x180, 0x280, 0x380 with an additional offset of 0xc10.
+ * (Or 0xd90, 0xe90, 0xf90).
+ * 2) The FlashCOM cards: these cards can be set up at
+ * 0x300 -> 0x378, in 0x8 jumps with an offset of 0x04.
+ * (Or 0x304 -> 0x37c in 0x8 jumps).
+ * Each card has it's own ID.
+ */
+#define MIXCOM_ID 0x11
+#define FLASHCOM_ID 0x18
+static struct {
+ int ioport;
+ int id;
+} mixcomwd_io_info[] __devinitdata = {
+ /* The Mixcom cards */
+ {0x0d90, MIXCOM_ID},
+ {0x0e90, MIXCOM_ID},
+ {0x0f90, MIXCOM_ID},
+ /* The FlashCOM cards */
+ {0x0304, FLASHCOM_ID},
+ {0x030c, FLASHCOM_ID},
+ {0x0314, FLASHCOM_ID},
+ {0x031c, FLASHCOM_ID},
+ {0x0324, FLASHCOM_ID},
+ {0x032c, FLASHCOM_ID},
+ {0x0334, FLASHCOM_ID},
+ {0x033c, FLASHCOM_ID},
+ {0x0344, FLASHCOM_ID},
+ {0x034c, FLASHCOM_ID},
+ {0x0354, FLASHCOM_ID},
+ {0x035c, FLASHCOM_ID},
+ {0x0364, FLASHCOM_ID},
+ {0x036c, FLASHCOM_ID},
+ {0x0374, FLASHCOM_ID},
+ {0x037c, FLASHCOM_ID},
+ /* The end of the list */
+ {0x0000, 0},
+};
+
+static void mixcomwd_timerfun(unsigned long d);
+
+static unsigned long mixcomwd_opened; /* long req'd for setbit --RR */
+
+static int watchdog_port;
+static int mixcomwd_timer_alive;
+static DEFINE_TIMER(mixcomwd_timer, mixcomwd_timerfun, 0, 0);
+static char expect_close;
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static void mixcomwd_ping(void)
+{
+ outb_p(55, watchdog_port);
+ return;
+}
+
+static void mixcomwd_timerfun(unsigned long d)
+{
+ mixcomwd_ping();
+ mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
+}
+
+/*
+ * Allow only one person to hold it open
+ */
+
+static int mixcomwd_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &mixcomwd_opened))
+ return -EBUSY;
+
+ mixcomwd_ping();
+
+ if (nowayout)
+ /*
+ * fops_get() code via open() has already done
+ * a try_module_get() so it is safe to do the
+ * __module_get().
+ */
+ __module_get(THIS_MODULE);
+ else {
+ if (mixcomwd_timer_alive) {
+ del_timer(&mixcomwd_timer);
+ mixcomwd_timer_alive = 0;
+ }
+ }
+ return nonseekable_open(inode, file);
+}
+
+static int mixcomwd_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ if (mixcomwd_timer_alive) {
+ printk(KERN_ERR PFX
+ "release called while internal timer alive");
+ return -EBUSY;
+ }
+ mixcomwd_timer_alive = 1;
+ mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
+ } else
+ printk(KERN_CRIT PFX
+ "WDT device closed unexpectedly. WDT will not stop!\n");
+
+ clear_bit(0, &mixcomwd_opened);
+ expect_close = 0;
+ return 0;
+}
+
+
+static ssize_t mixcomwd_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ mixcomwd_ping();
+ }
+ return len;
+}
+
+static long mixcomwd_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int status;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "MixCOM watchdog",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+ case WDIOC_GETSTATUS:
+ status = mixcomwd_opened;
+ if (!nowayout)
+ status |= mixcomwd_timer_alive;
+ return put_user(status, p);
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_KEEPALIVE:
+ mixcomwd_ping();
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static const struct file_operations mixcomwd_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = mixcomwd_write,
+ .unlocked_ioctl = mixcomwd_ioctl,
+ .open = mixcomwd_open,
+ .release = mixcomwd_release,
+};
+
+static struct miscdevice mixcomwd_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &mixcomwd_fops,
+};
+
+static int __init checkcard(int port, int card_id)
+{
+ int id;
+
+ if (!request_region(port, 1, "MixCOM watchdog"))
+ return 0;
+
+ id = inb_p(port);
+ if (card_id == MIXCOM_ID)
+ id &= 0x3f;
+
+ if (id != card_id) {
+ release_region(port, 1);
+ return 0;
+ }
+ return 1;
+}
+
+static int __init mixcomwd_init(void)
+{
+ int i, ret, found = 0;
+
+ for (i = 0; !found && mixcomwd_io_info[i].ioport != 0; i++) {
+ if (checkcard(mixcomwd_io_info[i].ioport,
+ mixcomwd_io_info[i].id)) {
+ found = 1;
+ watchdog_port = mixcomwd_io_info[i].ioport;
+ }
+ }
+
+ if (!found) {
+ printk(KERN_ERR PFX
+ "No card detected, or port not available.\n");
+ return -ENODEV;
+ }
+
+ ret = misc_register(&mixcomwd_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto error_misc_register_watchdog;
+ }
+
+ printk(KERN_INFO
+ "MixCOM watchdog driver v%s, watchdog port at 0x%3x\n",
+ VERSION, watchdog_port);
+
+ return 0;
+
+error_misc_register_watchdog:
+ release_region(watchdog_port, 1);
+ watchdog_port = 0x0000;
+ return ret;
+}
+
+static void __exit mixcomwd_exit(void)
+{
+ if (!nowayout) {
+ if (mixcomwd_timer_alive) {
+ printk(KERN_WARNING PFX "I quit now, hardware will"
+ " probably reboot!\n");
+ del_timer_sync(&mixcomwd_timer);
+ mixcomwd_timer_alive = 0;
+ }
+ }
+ misc_deregister(&mixcomwd_miscdev);
+ release_region(watchdog_port, 1);
+}
+
+module_init(mixcomwd_init);
+module_exit(mixcomwd_exit);
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("MixCom Watchdog driver");
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
new file mode 100644
index 00000000..eed5436f
--- /dev/null
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -0,0 +1,331 @@
+/*
+ * mpc8xxx_wdt.c - MPC8xx/MPC83xx/MPC86xx watchdog userspace interface
+ *
+ * Authors: Dave Updegraff <dave@cray.org>
+ * Kumar Gala <galak@kernel.crashing.org>
+ * Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org>
+ * ..and from sc520_wdt
+ * Copyright (c) 2008 MontaVista Software, Inc.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * Note: it appears that you can only actually ENABLE or DISABLE the thing
+ * once after POR. Once enabled, you cannot disable, and vice versa.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <sysdev/fsl_soc.h>
+
+struct mpc8xxx_wdt {
+ __be32 res0;
+ __be32 swcrr; /* System watchdog control register */
+#define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
+#define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */
+#define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/
+#define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */
+ __be32 swcnr; /* System watchdog count register */
+ u8 res1[2];
+ __be16 swsrr; /* System watchdog service register */
+ u8 res2[0xF0];
+};
+
+struct mpc8xxx_wdt_type {
+ int prescaler;
+ bool hw_enabled;
+};
+
+static struct mpc8xxx_wdt __iomem *wd_base;
+static int mpc8xxx_wdt_init_late(void);
+
+static u16 timeout = 0xffff;
+module_param(timeout, ushort, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in ticks. (0<timeout<65536, default=65535)");
+
+static int reset = 1;
+module_param(reset, bool, 0);
+MODULE_PARM_DESC(reset,
+ "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * We always prescale, but if someone really doesn't want to they can set this
+ * to 0
+ */
+static int prescale = 1;
+static unsigned int timeout_sec;
+
+static unsigned long wdt_is_open;
+static DEFINE_SPINLOCK(wdt_spinlock);
+
+static void mpc8xxx_wdt_keepalive(void)
+{
+ /* Ping the WDT */
+ spin_lock(&wdt_spinlock);
+ out_be16(&wd_base->swsrr, 0x556c);
+ out_be16(&wd_base->swsrr, 0xaa39);
+ spin_unlock(&wdt_spinlock);
+}
+
+static void mpc8xxx_wdt_timer_ping(unsigned long arg);
+static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0, 0);
+
+static void mpc8xxx_wdt_timer_ping(unsigned long arg)
+{
+ mpc8xxx_wdt_keepalive();
+ /* We're pinging it twice faster than needed, just to be sure. */
+ mod_timer(&wdt_timer, jiffies + HZ * timeout_sec / 2);
+}
+
+static void mpc8xxx_wdt_pr_warn(const char *msg)
+{
+ pr_crit("mpc8xxx_wdt: %s, expect the %s soon!\n", msg,
+ reset ? "reset" : "machine check exception");
+}
+
+static ssize_t mpc8xxx_wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count)
+ mpc8xxx_wdt_keepalive();
+ return count;
+}
+
+static int mpc8xxx_wdt_open(struct inode *inode, struct file *file)
+{
+ u32 tmp = SWCRR_SWEN;
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+
+ /* Once we start the watchdog we can't stop it */
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Good, fire up the show */
+ if (prescale)
+ tmp |= SWCRR_SWPR;
+ if (reset)
+ tmp |= SWCRR_SWRI;
+
+ tmp |= timeout << 16;
+
+ out_be32(&wd_base->swcrr, tmp);
+
+ del_timer_sync(&wdt_timer);
+
+ return nonseekable_open(inode, file);
+}
+
+static int mpc8xxx_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!nowayout)
+ mpc8xxx_wdt_timer_ping(0);
+ else
+ mpc8xxx_wdt_pr_warn("watchdog closed");
+ clear_bit(0, &wdt_is_open);
+ return 0;
+}
+
+static long mpc8xxx_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = "MPC8xxx",
+ };
+
+ 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_KEEPALIVE:
+ mpc8xxx_wdt_keepalive();
+ return 0;
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout_sec, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations mpc8xxx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = mpc8xxx_wdt_write,
+ .unlocked_ioctl = mpc8xxx_wdt_ioctl,
+ .open = mpc8xxx_wdt_open,
+ .release = mpc8xxx_wdt_release,
+};
+
+static struct miscdevice mpc8xxx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &mpc8xxx_wdt_fops,
+};
+
+static const struct of_device_id mpc8xxx_wdt_match[];
+static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev)
+{
+ int ret;
+ const struct of_device_id *match;
+ struct device_node *np = ofdev->dev.of_node;
+ struct mpc8xxx_wdt_type *wdt_type;
+ u32 freq = fsl_get_sys_freq();
+ bool enabled;
+
+ match = of_match_device(mpc8xxx_wdt_match, &ofdev->dev);
+ if (!match)
+ return -EINVAL;
+ wdt_type = match->data;
+
+ if (!freq || freq == -1)
+ return -EINVAL;
+
+ wd_base = of_iomap(np, 0);
+ if (!wd_base)
+ return -ENOMEM;
+
+ enabled = in_be32(&wd_base->swcrr) & SWCRR_SWEN;
+ if (!enabled && wdt_type->hw_enabled) {
+ pr_info("mpc8xxx_wdt: could not be enabled in software\n");
+ ret = -ENOSYS;
+ goto err_unmap;
+ }
+
+ /* Calculate the timeout in seconds */
+ if (prescale)
+ timeout_sec = (timeout * wdt_type->prescaler) / freq;
+ else
+ timeout_sec = timeout / freq;
+
+#ifdef MODULE
+ ret = mpc8xxx_wdt_init_late();
+ if (ret)
+ goto err_unmap;
+#endif
+
+ pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d "
+ "(%d seconds)\n", reset ? "reset" : "interrupt", timeout,
+ timeout_sec);
+
+ /*
+ * If the watchdog was previously enabled or we're running on
+ * MPC8xxx, we should ping the wdt from the kernel until the
+ * userspace handles it.
+ */
+ if (enabled)
+ mpc8xxx_wdt_timer_ping(0);
+ return 0;
+err_unmap:
+ iounmap(wd_base);
+ wd_base = NULL;
+ return ret;
+}
+
+static int __devexit mpc8xxx_wdt_remove(struct platform_device *ofdev)
+{
+ mpc8xxx_wdt_pr_warn("watchdog removed");
+ del_timer_sync(&wdt_timer);
+ misc_deregister(&mpc8xxx_wdt_miscdev);
+ iounmap(wd_base);
+
+ return 0;
+}
+
+static const struct of_device_id mpc8xxx_wdt_match[] = {
+ {
+ .compatible = "mpc83xx_wdt",
+ .data = &(struct mpc8xxx_wdt_type) {
+ .prescaler = 0x10000,
+ },
+ },
+ {
+ .compatible = "fsl,mpc8610-wdt",
+ .data = &(struct mpc8xxx_wdt_type) {
+ .prescaler = 0x10000,
+ .hw_enabled = true,
+ },
+ },
+ {
+ .compatible = "fsl,mpc823-wdt",
+ .data = &(struct mpc8xxx_wdt_type) {
+ .prescaler = 0x800,
+ },
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mpc8xxx_wdt_match);
+
+static struct platform_driver mpc8xxx_wdt_driver = {
+ .probe = mpc8xxx_wdt_probe,
+ .remove = __devexit_p(mpc8xxx_wdt_remove),
+ .driver = {
+ .name = "mpc8xxx_wdt",
+ .owner = THIS_MODULE,
+ .of_match_table = mpc8xxx_wdt_match,
+ },
+};
+
+/*
+ * We do wdt initialization in two steps: arch_initcall probes the wdt
+ * very early to start pinging the watchdog (misc devices are not yet
+ * available), and later module_init() just registers the misc device.
+ */
+static int mpc8xxx_wdt_init_late(void)
+{
+ int ret;
+
+ if (!wd_base)
+ return -ENODEV;
+
+ ret = misc_register(&mpc8xxx_wdt_miscdev);
+ if (ret) {
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ return ret;
+ }
+ return 0;
+}
+#ifndef MODULE
+module_init(mpc8xxx_wdt_init_late);
+#endif
+
+static int __init mpc8xxx_wdt_init(void)
+{
+ return platform_driver_register(&mpc8xxx_wdt_driver);
+}
+arch_initcall(mpc8xxx_wdt_init);
+
+static void __exit mpc8xxx_wdt_exit(void)
+{
+ platform_driver_unregister(&mpc8xxx_wdt_driver);
+}
+module_exit(mpc8xxx_wdt_exit);
+
+MODULE_AUTHOR("Dave Updegraff, Kumar Gala");
+MODULE_DESCRIPTION("Driver for watchdog timer in MPC8xx/MPC83xx/MPC86xx "
+ "uProcessors");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c
new file mode 100644
index 00000000..2b4af222
--- /dev/null
+++ b/drivers/watchdog/mpcore_wdt.c
@@ -0,0 +1,454 @@
+/*
+ * Watchdog driver for the mpcore watchdog timer
+ *
+ * (c) Copyright 2004 ARM Limited
+ *
+ * Based on the SoftDog driver:
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <asm/smp_twd.h>
+
+struct mpcore_wdt {
+ unsigned long timer_alive;
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ unsigned int perturb;
+ char expect_close;
+};
+
+static struct platform_device *mpcore_wdt_dev;
+static DEFINE_SPINLOCK(wdt_lock);
+
+#define TIMER_MARGIN 60
+static int mpcore_margin = TIMER_MARGIN;
+module_param(mpcore_margin, int, 0);
+MODULE_PARM_DESC(mpcore_margin,
+ "MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
+ __MODULE_STRING(TIMER_MARGIN) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#define ONLY_TESTING 0
+static int mpcore_noboot = ONLY_TESTING;
+module_param(mpcore_noboot, int, 0);
+MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
+ "set to 1 to ignore reboots, 0 to reboot (default="
+ __MODULE_STRING(ONLY_TESTING) ")");
+
+/*
+ * This is the interrupt handler. Note that we only use this
+ * in testing mode, so don't actually do a reboot here.
+ */
+static irqreturn_t mpcore_wdt_fire(int irq, void *arg)
+{
+ struct mpcore_wdt *wdt = arg;
+
+ /* Check it really was our interrupt */
+ if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
+ dev_printk(KERN_CRIT, wdt->dev,
+ "Triggered - Reboot ignored.\n");
+ /* Clear the interrupt on the watchdog */
+ writel(1, wdt->base + TWD_WDOG_INTSTAT);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/*
+ * mpcore_wdt_keepalive - reload the timer
+ *
+ * Note that the spec says a DIFFERENT value must be written to the reload
+ * register each time. The "perturb" variable deals with this by adding 1
+ * to the count every other time the function is called.
+ */
+static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
+{
+ unsigned long count;
+
+ spin_lock(&wdt_lock);
+ /* Assume prescale is set to 256 */
+ count = __raw_readl(wdt->base + TWD_WDOG_COUNTER);
+ count = (0xFFFFFFFFU - count) * (HZ / 5);
+ count = (count / 256) * mpcore_margin;
+
+ /* Reload the counter */
+ writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
+ wdt->perturb = wdt->perturb ? 0 : 1;
+ spin_unlock(&wdt_lock);
+}
+
+static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
+{
+ spin_lock(&wdt_lock);
+ writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
+ writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
+ writel(0x0, wdt->base + TWD_WDOG_CONTROL);
+ spin_unlock(&wdt_lock);
+}
+
+static void mpcore_wdt_start(struct mpcore_wdt *wdt)
+{
+ dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n");
+
+ /* This loads the count register but does NOT start the count yet */
+ mpcore_wdt_keepalive(wdt);
+
+ if (mpcore_noboot) {
+ /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
+ writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
+ } else {
+ /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
+ writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
+ }
+}
+
+static int mpcore_wdt_set_heartbeat(int t)
+{
+ if (t < 0x0001 || t > 0xFFFF)
+ return -EINVAL;
+
+ mpcore_margin = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+static int mpcore_wdt_open(struct inode *inode, struct file *file)
+{
+ struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev);
+
+ if (test_and_set_bit(0, &wdt->timer_alive))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ file->private_data = wdt;
+
+ /*
+ * Activate timer
+ */
+ mpcore_wdt_start(wdt);
+
+ return nonseekable_open(inode, file);
+}
+
+static int mpcore_wdt_release(struct inode *inode, struct file *file)
+{
+ struct mpcore_wdt *wdt = file->private_data;
+
+ /*
+ * Shut off the timer.
+ * Lock it in if it's a module and we set nowayout
+ */
+ if (wdt->expect_close == 42)
+ mpcore_wdt_stop(wdt);
+ else {
+ dev_printk(KERN_CRIT, wdt->dev,
+ "unexpected close, not stopping watchdog!\n");
+ mpcore_wdt_keepalive(wdt);
+ }
+ clear_bit(0, &wdt->timer_alive);
+ wdt->expect_close = 0;
+ return 0;
+}
+
+static ssize_t mpcore_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ struct mpcore_wdt *wdt = file->private_data;
+
+ /*
+ * Refresh the timer.
+ */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ wdt->expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ wdt->expect_close = 42;
+ }
+ }
+ mpcore_wdt_keepalive(wdt);
+ }
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .identity = "MPcore Watchdog",
+};
+
+static long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct mpcore_wdt *wdt = file->private_data;
+ int ret;
+ union {
+ struct watchdog_info ident;
+ int i;
+ } uarg;
+
+ if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
+ return -ENOTTY;
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
+ if (ret)
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ uarg.ident = ident;
+ ret = 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ uarg.i = 0;
+ ret = 0;
+ break;
+
+ case WDIOC_SETOPTIONS:
+ ret = -EINVAL;
+ if (uarg.i & WDIOS_DISABLECARD) {
+ mpcore_wdt_stop(wdt);
+ ret = 0;
+ }
+ if (uarg.i & WDIOS_ENABLECARD) {
+ mpcore_wdt_start(wdt);
+ ret = 0;
+ }
+ break;
+
+ case WDIOC_KEEPALIVE:
+ mpcore_wdt_keepalive(wdt);
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = mpcore_wdt_set_heartbeat(uarg.i);
+ if (ret)
+ break;
+
+ mpcore_wdt_keepalive(wdt);
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ uarg.i = mpcore_margin;
+ ret = 0;
+ break;
+
+ default:
+ return -ENOTTY;
+ }
+
+ if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
+ ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
+ if (ret)
+ ret = -EFAULT;
+ }
+ return ret;
+}
+
+/*
+ * System shutdown handler. Turn off the watchdog if we're
+ * restarting or halting the system.
+ */
+static void mpcore_wdt_shutdown(struct platform_device *dev)
+{
+ struct mpcore_wdt *wdt = platform_get_drvdata(dev);
+
+ if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
+ mpcore_wdt_stop(wdt);
+}
+
+/*
+ * Kernel Interfaces
+ */
+static const struct file_operations mpcore_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = mpcore_wdt_write,
+ .unlocked_ioctl = mpcore_wdt_ioctl,
+ .open = mpcore_wdt_open,
+ .release = mpcore_wdt_release,
+};
+
+static struct miscdevice mpcore_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &mpcore_wdt_fops,
+};
+
+static int __devinit mpcore_wdt_probe(struct platform_device *dev)
+{
+ struct mpcore_wdt *wdt;
+ struct resource *res;
+ int ret;
+
+ /* We only accept one device, and it must have an id of -1 */
+ if (dev->id != -1)
+ return -ENODEV;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ wdt = kzalloc(sizeof(struct mpcore_wdt), GFP_KERNEL);
+ if (!wdt) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ wdt->dev = &dev->dev;
+ wdt->irq = platform_get_irq(dev, 0);
+ if (wdt->irq < 0) {
+ ret = -ENXIO;
+ goto err_free;
+ }
+ wdt->base = ioremap(res->start, resource_size(res));
+ if (!wdt->base) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ mpcore_wdt_miscdev.parent = &dev->dev;
+ ret = misc_register(&mpcore_wdt_miscdev);
+ if (ret) {
+ dev_printk(KERN_ERR, wdt->dev,
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto err_misc;
+ }
+
+ ret = request_irq(wdt->irq, mpcore_wdt_fire, IRQF_DISABLED,
+ "mpcore_wdt", wdt);
+ if (ret) {
+ dev_printk(KERN_ERR, wdt->dev,
+ "cannot register IRQ%d for watchdog\n", wdt->irq);
+ goto err_irq;
+ }
+
+ mpcore_wdt_stop(wdt);
+ platform_set_drvdata(dev, wdt);
+ mpcore_wdt_dev = dev;
+
+ return 0;
+
+err_irq:
+ misc_deregister(&mpcore_wdt_miscdev);
+err_misc:
+ iounmap(wdt->base);
+err_free:
+ kfree(wdt);
+err_out:
+ return ret;
+}
+
+static int __devexit mpcore_wdt_remove(struct platform_device *dev)
+{
+ struct mpcore_wdt *wdt = platform_get_drvdata(dev);
+
+ platform_set_drvdata(dev, NULL);
+
+ misc_deregister(&mpcore_wdt_miscdev);
+
+ mpcore_wdt_dev = NULL;
+
+ free_irq(wdt->irq, wdt);
+ iounmap(wdt->base);
+ kfree(wdt);
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:mpcore_wdt");
+
+static struct platform_driver mpcore_wdt_driver = {
+ .probe = mpcore_wdt_probe,
+ .remove = __devexit_p(mpcore_wdt_remove),
+ .shutdown = mpcore_wdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mpcore_wdt",
+ },
+};
+
+static char banner[] __initdata = KERN_INFO "MPcore Watchdog Timer: 0.1. "
+ "mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n";
+
+static int __init mpcore_wdt_init(void)
+{
+ /*
+ * Check that the margin value is within it's range;
+ * if not reset to the default
+ */
+ if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
+ mpcore_wdt_set_heartbeat(TIMER_MARGIN);
+ printk(KERN_INFO "mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
+ TIMER_MARGIN);
+ }
+
+ printk(banner, mpcore_noboot, mpcore_margin, nowayout);
+
+ return platform_driver_register(&mpcore_wdt_driver);
+}
+
+static void __exit mpcore_wdt_exit(void)
+{
+ platform_driver_unregister(&mpcore_wdt_driver);
+}
+
+module_init(mpcore_wdt_init);
+module_exit(mpcore_wdt_exit);
+
+MODULE_AUTHOR("ARM Limited");
+MODULE_DESCRIPTION("MPcore Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c
new file mode 100644
index 00000000..0430e093
--- /dev/null
+++ b/drivers/watchdog/mtx-1_wdt.c
@@ -0,0 +1,273 @@
+/*
+ * Driver for the MTX-1 Watchdog.
+ *
+ * (C) Copyright 2005 4G Systems <info@4g-systems.biz>,
+ * All Rights Reserved.
+ * http://www.4g-systems.biz
+ *
+ * (C) Copyright 2007 OpenWrt.org, Florian Fainelli <florian@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Michael Stickel nor 4G Systems admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 2005 4G Systems <info@4g-systems.biz>
+ *
+ * Release 0.01.
+ * Author: Michael Stickel michael.stickel@4g-systems.biz
+ *
+ * Release 0.02.
+ * Author: Florian Fainelli florian@openwrt.org
+ * use the Linux watchdog/timer APIs
+ *
+ * The Watchdog is configured to reset the MTX-1
+ * if it is not triggered for 100 seconds.
+ * It should not be triggered more often than 1.6 seconds.
+ *
+ * A timer triggers the watchdog every 5 seconds, until
+ * it is opened for the first time. After the first open
+ * it MUST be triggered every 2..95 seconds.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/completion.h>
+#include <linux/jiffies.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-au1x00/au1000.h>
+
+#define MTX1_WDT_INTERVAL (5 * HZ)
+
+static int ticks = 100 * HZ;
+
+static struct {
+ struct completion stop;
+ spinlock_t lock;
+ int running;
+ struct timer_list timer;
+ int queue;
+ int default_ticks;
+ unsigned long inuse;
+ unsigned gpio;
+ unsigned int gstate;
+} mtx1_wdt_device;
+
+static void mtx1_wdt_trigger(unsigned long unused)
+{
+ spin_lock(&mtx1_wdt_device.lock);
+ if (mtx1_wdt_device.running)
+ ticks--;
+
+ /* toggle wdt gpio */
+ mtx1_wdt_device.gstate = !mtx1_wdt_device.gstate;
+ gpio_set_value(mtx1_wdt_device.gpio, mtx1_wdt_device.gstate);
+
+ if (mtx1_wdt_device.queue && ticks)
+ mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
+ else
+ complete(&mtx1_wdt_device.stop);
+ spin_unlock(&mtx1_wdt_device.lock);
+}
+
+static void mtx1_wdt_reset(void)
+{
+ ticks = mtx1_wdt_device.default_ticks;
+}
+
+
+static void mtx1_wdt_start(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
+ if (!mtx1_wdt_device.queue) {
+ mtx1_wdt_device.queue = 1;
+ mtx1_wdt_device.gstate = 1;
+ gpio_set_value(mtx1_wdt_device.gpio, 1);
+ mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
+ }
+ mtx1_wdt_device.running++;
+ spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
+}
+
+static int mtx1_wdt_stop(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
+ if (mtx1_wdt_device.queue) {
+ mtx1_wdt_device.queue = 0;
+ mtx1_wdt_device.gstate = 0;
+ gpio_set_value(mtx1_wdt_device.gpio, 0);
+ }
+ ticks = mtx1_wdt_device.default_ticks;
+ spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
+ return 0;
+}
+
+/* Filesystem functions */
+
+static int mtx1_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &mtx1_wdt_device.inuse))
+ return -EBUSY;
+ return nonseekable_open(inode, file);
+}
+
+
+static int mtx1_wdt_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &mtx1_wdt_device.inuse);
+ return 0;
+}
+
+static long mtx1_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = (int __user *)argp;
+ unsigned int value;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET,
+ .identity = "MTX-1 WDT",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ put_user(0, p);
+ break;
+ case WDIOC_SETOPTIONS:
+ if (get_user(value, p))
+ return -EFAULT;
+ if (value & WDIOS_ENABLECARD)
+ mtx1_wdt_start();
+ else if (value & WDIOS_DISABLECARD)
+ mtx1_wdt_stop();
+ else
+ return -EINVAL;
+ return 0;
+ case WDIOC_KEEPALIVE:
+ mtx1_wdt_reset();
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+
+static ssize_t mtx1_wdt_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ if (!count)
+ return -EIO;
+ mtx1_wdt_reset();
+ return count;
+}
+
+static const struct file_operations mtx1_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = mtx1_wdt_ioctl,
+ .open = mtx1_wdt_open,
+ .write = mtx1_wdt_write,
+ .release = mtx1_wdt_release,
+};
+
+
+static struct miscdevice mtx1_wdt_misc = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &mtx1_wdt_fops,
+};
+
+
+static int __devinit mtx1_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ mtx1_wdt_device.gpio = pdev->resource[0].start;
+ ret = gpio_request_one(mtx1_wdt_device.gpio,
+ GPIOF_OUT_INIT_HIGH, "mtx1-wdt");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request gpio");
+ return ret;
+ }
+
+ spin_lock_init(&mtx1_wdt_device.lock);
+ init_completion(&mtx1_wdt_device.stop);
+ mtx1_wdt_device.queue = 0;
+ clear_bit(0, &mtx1_wdt_device.inuse);
+ setup_timer(&mtx1_wdt_device.timer, mtx1_wdt_trigger, 0L);
+ mtx1_wdt_device.default_ticks = ticks;
+
+ ret = misc_register(&mtx1_wdt_misc);
+ if (ret < 0) {
+ printk(KERN_ERR " mtx-1_wdt : failed to register\n");
+ return ret;
+ }
+ mtx1_wdt_start();
+ printk(KERN_INFO "MTX-1 Watchdog driver\n");
+ return 0;
+}
+
+static int __devexit mtx1_wdt_remove(struct platform_device *pdev)
+{
+ /* FIXME: do we need to lock this test ? */
+ if (mtx1_wdt_device.queue) {
+ mtx1_wdt_device.queue = 0;
+ wait_for_completion(&mtx1_wdt_device.stop);
+ }
+
+ gpio_free(mtx1_wdt_device.gpio);
+ misc_deregister(&mtx1_wdt_misc);
+ return 0;
+}
+
+static struct platform_driver mtx1_wdt_driver = {
+ .probe = mtx1_wdt_probe,
+ .remove = __devexit_p(mtx1_wdt_remove),
+ .driver.name = "mtx1-wdt",
+ .driver.owner = THIS_MODULE,
+};
+
+static int __init mtx1_wdt_init(void)
+{
+ return platform_driver_register(&mtx1_wdt_driver);
+}
+
+static void __exit mtx1_wdt_exit(void)
+{
+ platform_driver_unregister(&mtx1_wdt_driver);
+}
+
+module_init(mtx1_wdt_init);
+module_exit(mtx1_wdt_exit);
+
+MODULE_AUTHOR("Michael Stickel, Florian Fainelli");
+MODULE_DESCRIPTION("Driver for the MTX-1 watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:mtx1-wdt");
diff --git a/drivers/watchdog/mv64x60_wdt.c b/drivers/watchdog/mv64x60_wdt.c
new file mode 100644
index 00000000..97f8a48d
--- /dev/null
+++ b/drivers/watchdog/mv64x60_wdt.c
@@ -0,0 +1,328 @@
+/*
+ * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface
+ *
+ * Author: James Chapman <jchapman@katalix.com>
+ *
+ * Platform-specific setup code should configure the dog to generate
+ * interrupt or reset as required. This code only enables/disables
+ * and services the watchdog.
+ *
+ * Derived from mpc8xx_wdt.c, with the following copyright.
+ *
+ * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/mv643xx.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#define MV64x60_WDT_WDC_OFFSET 0
+
+/*
+ * The watchdog configuration register contains a pair of 2-bit fields,
+ * 1. a reload field, bits 27-26, which triggers a reload of
+ * the countdown register, and
+ * 2. an enable field, bits 25-24, which toggles between
+ * enabling and disabling the watchdog timer.
+ * Bit 31 is a read-only field which indicates whether the
+ * watchdog timer is currently enabled.
+ *
+ * The low 24 bits contain the timer reload value.
+ */
+#define MV64x60_WDC_ENABLE_SHIFT 24
+#define MV64x60_WDC_SERVICE_SHIFT 26
+#define MV64x60_WDC_ENABLED_SHIFT 31
+
+#define MV64x60_WDC_ENABLED_TRUE 1
+#define MV64x60_WDC_ENABLED_FALSE 0
+
+/* Flags bits */
+#define MV64x60_WDOG_FLAG_OPENED 0
+
+static unsigned long wdt_flags;
+static int wdt_status;
+static void __iomem *mv64x60_wdt_regs;
+static int mv64x60_wdt_timeout;
+static int mv64x60_wdt_count;
+static unsigned int bus_clk;
+static char expect_close;
+static DEFINE_SPINLOCK(mv64x60_wdt_spinlock);
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int mv64x60_wdt_toggle_wdc(int enabled_predicate, int field_shift)
+{
+ u32 data;
+ u32 enabled;
+ int ret = 0;
+
+ spin_lock(&mv64x60_wdt_spinlock);
+ data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
+ enabled = (data >> MV64x60_WDC_ENABLED_SHIFT) & 1;
+
+ /* only toggle the requested field if enabled state matches predicate */
+ if ((enabled ^ enabled_predicate) == 0) {
+ /* We write a 1, then a 2 -- to the appropriate field */
+ data = (1 << field_shift) | mv64x60_wdt_count;
+ writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
+
+ data = (2 << field_shift) | mv64x60_wdt_count;
+ writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
+ ret = 1;
+ }
+ spin_unlock(&mv64x60_wdt_spinlock);
+
+ return ret;
+}
+
+static void mv64x60_wdt_service(void)
+{
+ mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,
+ MV64x60_WDC_SERVICE_SHIFT);
+}
+
+static void mv64x60_wdt_handler_enable(void)
+{
+ if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_FALSE,
+ MV64x60_WDC_ENABLE_SHIFT)) {
+ mv64x60_wdt_service();
+ printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n");
+ }
+}
+
+static void mv64x60_wdt_handler_disable(void)
+{
+ if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,
+ MV64x60_WDC_ENABLE_SHIFT))
+ printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n");
+}
+
+static void mv64x60_wdt_set_timeout(unsigned int timeout)
+{
+ /* maximum bus cycle count is 0xFFFFFFFF */
+ if (timeout > 0xFFFFFFFF / bus_clk)
+ timeout = 0xFFFFFFFF / bus_clk;
+
+ mv64x60_wdt_count = timeout * bus_clk >> 8;
+ mv64x60_wdt_timeout = timeout;
+}
+
+static int mv64x60_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ mv64x60_wdt_handler_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static int mv64x60_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ mv64x60_wdt_handler_disable();
+ else {
+ printk(KERN_CRIT
+ "mv64x60_wdt: unexpected close, not stopping timer!\n");
+ mv64x60_wdt_service();
+ }
+ expect_close = 0;
+
+ clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags);
+
+ return 0;
+}
+
+static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ mv64x60_wdt_service();
+ }
+
+ return len;
+}
+
+static long mv64x60_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int timeout;
+ int options;
+ void __user *argp = (void __user *)arg;
+ static const struct watchdog_info info = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .firmware_version = 0,
+ .identity = "MV64x60 watchdog",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(wdt_status, (int __user *)argp))
+ return -EFAULT;
+ wdt_status &= ~WDIOF_KEEPALIVEPING;
+ break;
+
+ case WDIOC_GETTEMP:
+ return -EOPNOTSUPP;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(options, (int __user *)argp))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD)
+ mv64x60_wdt_handler_disable();
+
+ if (options & WDIOS_ENABLECARD)
+ mv64x60_wdt_handler_enable();
+ break;
+
+ case WDIOC_KEEPALIVE:
+ mv64x60_wdt_service();
+ wdt_status |= WDIOF_KEEPALIVEPING;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(timeout, (int __user *)argp))
+ return -EFAULT;
+ mv64x60_wdt_set_timeout(timeout);
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ if (put_user(mv64x60_wdt_timeout, (int __user *)argp))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static const struct file_operations mv64x60_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = mv64x60_wdt_write,
+ .unlocked_ioctl = mv64x60_wdt_ioctl,
+ .open = mv64x60_wdt_open,
+ .release = mv64x60_wdt_release,
+};
+
+static struct miscdevice mv64x60_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &mv64x60_wdt_fops,
+};
+
+static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
+{
+ struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
+ struct resource *r;
+ int timeout = 10;
+
+ bus_clk = 133; /* in MHz */
+ if (pdata) {
+ timeout = pdata->timeout;
+ bus_clk = pdata->bus_clk;
+ }
+
+ /* Since bus_clk is truncated MHz, actual frequency could be
+ * up to 1MHz higher. Round up, since it's better to time out
+ * too late than too soon.
+ */
+ bus_clk++;
+ bus_clk *= 1000000; /* convert to Hz */
+
+ r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!r)
+ return -ENODEV;
+
+ mv64x60_wdt_regs = ioremap(r->start, resource_size(r));
+ if (mv64x60_wdt_regs == NULL)
+ return -ENOMEM;
+
+ mv64x60_wdt_set_timeout(timeout);
+
+ mv64x60_wdt_handler_disable(); /* in case timer was already running */
+
+ return misc_register(&mv64x60_wdt_miscdev);
+}
+
+static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
+{
+ misc_deregister(&mv64x60_wdt_miscdev);
+
+ mv64x60_wdt_handler_disable();
+
+ iounmap(mv64x60_wdt_regs);
+
+ return 0;
+}
+
+static struct platform_driver mv64x60_wdt_driver = {
+ .probe = mv64x60_wdt_probe,
+ .remove = __devexit_p(mv64x60_wdt_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MV64x60_WDT_NAME,
+ },
+};
+
+static int __init mv64x60_wdt_init(void)
+{
+ printk(KERN_INFO "MV64x60 watchdog driver\n");
+
+ return platform_driver_register(&mv64x60_wdt_driver);
+}
+
+static void __exit mv64x60_wdt_exit(void)
+{
+ platform_driver_unregister(&mv64x60_wdt_driver);
+}
+
+module_init(mv64x60_wdt_init);
+module_exit(mv64x60_wdt_exit);
+
+MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
+MODULE_DESCRIPTION("MV64x60 watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:" MV64x60_WDT_NAME);
diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c
new file mode 100644
index 00000000..6cee33d4
--- /dev/null
+++ b/drivers/watchdog/nuc900_wdt.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+
+#define REG_WTCR 0x1c
+#define WTCLK (0x01 << 10)
+#define WTE (0x01 << 7) /*wdt enable*/
+#define WTIS (0x03 << 4)
+#define WTIF (0x01 << 3)
+#define WTRF (0x01 << 2)
+#define WTRE (0x01 << 1)
+#define WTR (0x01 << 0)
+/*
+ * The watchdog time interval can be calculated via following formula:
+ * WTIS real time interval (formula)
+ * 0x00 ((2^ 14 ) * ((external crystal freq) / 256))seconds
+ * 0x01 ((2^ 16 ) * ((external crystal freq) / 256))seconds
+ * 0x02 ((2^ 18 ) * ((external crystal freq) / 256))seconds
+ * 0x03 ((2^ 20 ) * ((external crystal freq) / 256))seconds
+ *
+ * The external crystal freq is 15Mhz in the nuc900 evaluation board.
+ * So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
+ * 0x03 = +- 16.92 seconds..
+ */
+#define WDT_HW_TIMEOUT 0x02
+#define WDT_TIMEOUT (HZ/2)
+#define WDT_HEARTBEAT 15
+
+static int heartbeat = WDT_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
+ "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct nuc900_wdt {
+ struct resource *res;
+ struct clk *wdt_clock;
+ struct platform_device *pdev;
+ void __iomem *wdt_base;
+ char expect_close;
+ struct timer_list timer;
+ spinlock_t wdt_lock;
+ unsigned long next_heartbeat;
+};
+
+static unsigned long nuc900wdt_busy;
+struct nuc900_wdt *nuc900_wdt;
+
+static inline void nuc900_wdt_keepalive(void)
+{
+ unsigned int val;
+
+ spin_lock(&nuc900_wdt->wdt_lock);
+
+ val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
+ val |= (WTR | WTIF);
+ __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
+
+ spin_unlock(&nuc900_wdt->wdt_lock);
+}
+
+static inline void nuc900_wdt_start(void)
+{
+ unsigned int val;
+
+ spin_lock(&nuc900_wdt->wdt_lock);
+
+ val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
+ val |= (WTRE | WTE | WTR | WTCLK | WTIF);
+ val &= ~WTIS;
+ val |= (WDT_HW_TIMEOUT << 0x04);
+ __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
+
+ spin_unlock(&nuc900_wdt->wdt_lock);
+
+ nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
+ mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
+}
+
+static inline void nuc900_wdt_stop(void)
+{
+ unsigned int val;
+
+ del_timer(&nuc900_wdt->timer);
+
+ spin_lock(&nuc900_wdt->wdt_lock);
+
+ val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
+ val &= ~WTE;
+ __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
+
+ spin_unlock(&nuc900_wdt->wdt_lock);
+}
+
+static inline void nuc900_wdt_ping(void)
+{
+ nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
+}
+
+static int nuc900_wdt_open(struct inode *inode, struct file *file)
+{
+
+ if (test_and_set_bit(0, &nuc900wdt_busy))
+ return -EBUSY;
+
+ nuc900_wdt_start();
+
+ return nonseekable_open(inode, file);
+}
+
+static int nuc900_wdt_close(struct inode *inode, struct file *file)
+{
+ if (nuc900_wdt->expect_close == 42)
+ nuc900_wdt_stop();
+ else {
+ dev_crit(&nuc900_wdt->pdev->dev,
+ "Unexpected close, not stopping watchdog!\n");
+ nuc900_wdt_ping();
+ }
+
+ nuc900_wdt->expect_close = 0;
+ clear_bit(0, &nuc900wdt_busy);
+ return 0;
+}
+
+static const struct watchdog_info nuc900_wdt_info = {
+ .identity = "nuc900 watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+static long nuc900_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &nuc900_wdt_info,
+ sizeof(nuc900_wdt_info)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_KEEPALIVE:
+ nuc900_wdt_ping();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ heartbeat = new_value;
+ nuc900_wdt_ping();
+
+ return put_user(new_value, p);
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static ssize_t nuc900_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (!len)
+ return 0;
+
+ /* Scan for magic character */
+ if (!nowayout) {
+ size_t i;
+
+ nuc900_wdt->expect_close = 0;
+
+ for (i = 0; i < len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V') {
+ nuc900_wdt->expect_close = 42;
+ break;
+ }
+ }
+ }
+
+ nuc900_wdt_ping();
+ return len;
+}
+
+static void nuc900_wdt_timer_ping(unsigned long data)
+{
+ if (time_before(jiffies, nuc900_wdt->next_heartbeat)) {
+ nuc900_wdt_keepalive();
+ mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
+ } else
+ dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n");
+}
+
+static const struct file_operations nuc900wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = nuc900_wdt_ioctl,
+ .open = nuc900_wdt_open,
+ .release = nuc900_wdt_close,
+ .write = nuc900_wdt_write,
+};
+
+static struct miscdevice nuc900wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &nuc900wdt_fops,
+};
+
+static int __devinit nuc900wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ nuc900_wdt = kzalloc(sizeof(struct nuc900_wdt), GFP_KERNEL);
+ if (!nuc900_wdt)
+ return -ENOMEM;
+
+ nuc900_wdt->pdev = pdev;
+
+ spin_lock_init(&nuc900_wdt->wdt_lock);
+
+ nuc900_wdt->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (nuc900_wdt->res == NULL) {
+ dev_err(&pdev->dev, "no memory resource specified\n");
+ ret = -ENOENT;
+ goto err_get;
+ }
+
+ if (!request_mem_region(nuc900_wdt->res->start,
+ resource_size(nuc900_wdt->res), pdev->name)) {
+ dev_err(&pdev->dev, "failed to get memory region\n");
+ ret = -ENOENT;
+ goto err_get;
+ }
+
+ nuc900_wdt->wdt_base = ioremap(nuc900_wdt->res->start,
+ resource_size(nuc900_wdt->res));
+ if (nuc900_wdt->wdt_base == NULL) {
+ dev_err(&pdev->dev, "failed to ioremap() region\n");
+ ret = -EINVAL;
+ goto err_req;
+ }
+
+ nuc900_wdt->wdt_clock = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(nuc900_wdt->wdt_clock)) {
+ dev_err(&pdev->dev, "failed to find watchdog clock source\n");
+ ret = PTR_ERR(nuc900_wdt->wdt_clock);
+ goto err_map;
+ }
+
+ clk_enable(nuc900_wdt->wdt_clock);
+
+ setup_timer(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0);
+
+ if (misc_register(&nuc900wdt_miscdev)) {
+ dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto err_clk;
+ }
+
+ return 0;
+
+err_clk:
+ clk_disable(nuc900_wdt->wdt_clock);
+ clk_put(nuc900_wdt->wdt_clock);
+err_map:
+ iounmap(nuc900_wdt->wdt_base);
+err_req:
+ release_mem_region(nuc900_wdt->res->start,
+ resource_size(nuc900_wdt->res));
+err_get:
+ kfree(nuc900_wdt);
+ return ret;
+}
+
+static int __devexit nuc900wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&nuc900wdt_miscdev);
+
+ clk_disable(nuc900_wdt->wdt_clock);
+ clk_put(nuc900_wdt->wdt_clock);
+
+ iounmap(nuc900_wdt->wdt_base);
+
+ release_mem_region(nuc900_wdt->res->start,
+ resource_size(nuc900_wdt->res));
+
+ kfree(nuc900_wdt);
+
+ return 0;
+}
+
+static struct platform_driver nuc900wdt_driver = {
+ .probe = nuc900wdt_probe,
+ .remove = __devexit_p(nuc900wdt_remove),
+ .driver = {
+ .name = "nuc900-wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init nuc900_wdt_init(void)
+{
+ return platform_driver_register(&nuc900wdt_driver);
+}
+
+static void __exit nuc900_wdt_exit(void)
+{
+ platform_driver_unregister(&nuc900wdt_driver);
+}
+
+module_init(nuc900_wdt_init);
+module_exit(nuc900_wdt_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("Watchdog driver for NUC900");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:nuc900-wdt");
diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c
new file mode 100644
index 00000000..afa78a54
--- /dev/null
+++ b/drivers/watchdog/nv_tco.c
@@ -0,0 +1,512 @@
+/*
+ * nv_tco 0.01: TCO timer driver for NV chipsets
+ *
+ * (c) Copyright 2005 Google Inc., All Rights Reserved.
+ *
+ * Based off i8xx_tco.c:
+ * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
+ * Reserved.
+ * http://www.kernelconcepts.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * TCO timer driver for NV chipsets
+ * based on softdog.c by Alan Cox <alan@redhat.com>
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "nv_tco.h"
+
+/* Module and version information */
+#define TCO_VERSION "0.01"
+#define TCO_MODULE_NAME "NV_TCO"
+#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
+#define PFX TCO_MODULE_NAME ": "
+
+/* internal variables */
+static unsigned int tcobase;
+static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */
+static unsigned long timer_alive;
+static char tco_expect_close;
+static struct pci_dev *tco_pci;
+
+/* the watchdog platform device */
+static struct platform_device *nv_tco_platform_device;
+
+/* module parameters */
+#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (2<heartbeat<39) */
+static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, "
+ "default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
+ " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Some TCO specific functions
+ */
+static inline unsigned char seconds_to_ticks(int seconds)
+{
+ /* the internal timer is stored as ticks which decrement
+ * every 0.6 seconds */
+ return (seconds * 10) / 6;
+}
+
+static void tco_timer_start(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = inl(TCO_CNT(tcobase));
+ val &= ~TCO_CNT_TCOHALT;
+ outl(val, TCO_CNT(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static void tco_timer_stop(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = inl(TCO_CNT(tcobase));
+ val |= TCO_CNT_TCOHALT;
+ outl(val, TCO_CNT(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static void tco_timer_keepalive(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ outb(0x01, TCO_RLD(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static int tco_timer_set_heartbeat(int t)
+{
+ int ret = 0;
+ unsigned char tmrval;
+ unsigned long flags;
+ u8 val;
+
+ /*
+ * note seconds_to_ticks(t) > t, so if t > 0x3f, so is
+ * tmrval=seconds_to_ticks(t). Check that the count in seconds isn't
+ * out of range on it's own (to avoid overflow in tmrval).
+ */
+ if (t < 0 || t > 0x3f)
+ return -EINVAL;
+ tmrval = seconds_to_ticks(t);
+
+ /* "Values of 0h-3h are ignored and should not be attempted" */
+ if (tmrval > 0x3f || tmrval < 0x04)
+ return -EINVAL;
+
+ /* Write new heartbeat to watchdog */
+ spin_lock_irqsave(&tco_lock, flags);
+ val = inb(TCO_TMR(tcobase));
+ val &= 0xc0;
+ val |= tmrval;
+ outb(val, TCO_TMR(tcobase));
+ val = inb(TCO_TMR(tcobase));
+
+ if ((val & 0x3f) != tmrval)
+ ret = -EINVAL;
+ spin_unlock_irqrestore(&tco_lock, flags);
+
+ if (ret)
+ return ret;
+
+ heartbeat = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int nv_tco_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &timer_alive))
+ return -EBUSY;
+
+ /* Reload and activate timer */
+ tco_timer_keepalive();
+ tco_timer_start();
+ return nonseekable_open(inode, file);
+}
+
+static int nv_tco_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer */
+ if (tco_expect_close == 42) {
+ tco_timer_stop();
+ } else {
+ printk(KERN_CRIT PFX "Unexpected close, not stopping "
+ "watchdog!\n");
+ tco_timer_keepalive();
+ }
+ clear_bit(0, &timer_alive);
+ tco_expect_close = 0;
+ return 0;
+}
+
+static ssize_t nv_tco_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /*
+ * note: just in case someone wrote the magic character
+ * five months ago...
+ */
+ tco_expect_close = 0;
+
+ /*
+ * scan to see whether or not we got the magic
+ * character
+ */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ tco_expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ tco_timer_keepalive();
+ }
+ return len;
+}
+
+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 DEFINE_PCI_DEVICE_TABLE(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 __devinit 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);
+}
+
+static int __devexit nv_tco_remove(struct platform_device *dev)
+{
+ if (tcobase)
+ nv_tco_cleanup();
+
+ return 0;
+}
+
+static void nv_tco_shutdown(struct platform_device *dev)
+{
+ tco_timer_stop();
+}
+
+static struct platform_driver nv_tco_driver = {
+ .probe = nv_tco_init,
+ .remove = __devexit_p(nv_tco_remove),
+ .shutdown = nv_tco_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = TCO_MODULE_NAME,
+ },
+};
+
+static int __init nv_tco_init_module(void)
+{
+ int err;
+
+ printk(KERN_INFO PFX "NV TCO WatchDog Timer Driver v%s\n",
+ TCO_VERSION);
+
+ err = platform_driver_register(&nv_tco_driver);
+ if (err)
+ return err;
+
+ nv_tco_platform_device = platform_device_register_simple(
+ TCO_MODULE_NAME, -1, NULL, 0);
+ if (IS_ERR(nv_tco_platform_device)) {
+ err = PTR_ERR(nv_tco_platform_device);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&nv_tco_driver);
+ return err;
+}
+
+static void __exit nv_tco_cleanup_module(void)
+{
+ platform_device_unregister(nv_tco_platform_device);
+ platform_driver_unregister(&nv_tco_driver);
+ printk(KERN_INFO PFX "NV TCO Watchdog Module Unloaded.\n");
+}
+
+module_init(nv_tco_init_module);
+module_exit(nv_tco_cleanup_module);
+
+MODULE_AUTHOR("Mike Waychison");
+MODULE_DESCRIPTION("TCO timer driver for NV chipsets");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/nv_tco.h b/drivers/watchdog/nv_tco.h
new file mode 100644
index 00000000..c2d1d04e
--- /dev/null
+++ b/drivers/watchdog/nv_tco.h
@@ -0,0 +1,64 @@
+/*
+ * nv_tco: TCO timer driver for nVidia chipsets.
+ *
+ * (c) Copyright 2005 Google Inc., All Rights Reserved.
+ *
+ * Supported Chipsets:
+ * - MCP51/MCP55
+ *
+ * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
+ * Reserved.
+ * http://www.kernelconcepts.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither kernel concepts nor Nils Faerber admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>
+ * developed for
+ * Jentro AG, Haar/Munich (Germany)
+ *
+ * TCO timer driver for NV chipsets
+ * based on softdog.c by Alan Cox <alan@redhat.com>
+ */
+
+/*
+ * Some address definitions for the TCO
+ */
+
+#define TCO_RLD(base) ((base) + 0x00) /* TCO Timer Reload and Current Value */
+#define TCO_TMR(base) ((base) + 0x01) /* TCO Timer Initial Value */
+
+#define TCO_STS(base) ((base) + 0x04) /* TCO Status Register */
+/*
+ * TCO Boot Status bit: set on TCO reset, reset by software or standby
+ * power-good (survives reboots), unfortunately this bit is never
+ * set.
+ */
+# define TCO_STS_BOOT_STS (1 << 9)
+/*
+ * first and 2nd timeout status bits, these also survive a warm boot,
+ * and they work, so we use them.
+ */
+# define TCO_STS_TCO_INT_STS (1 << 1)
+# define TCO_STS_TCO2TO_STS (1 << 10)
+# define TCO_STS_RESET (TCO_STS_BOOT_STS | TCO_STS_TCO2TO_STS | \
+ TCO_STS_TCO_INT_STS)
+
+#define TCO_CNT(base) ((base) + 0x08) /* TCO Control Register */
+# define TCO_CNT_TCOHALT (1 << 12)
+
+#define MCP51_SMBUS_SETUP_B 0xe8
+# define MCP51_SMBUS_SETUP_B_TCO_REBOOT (1 << 25)
+
+/*
+ * The SMI_EN register is at the base io address + 0x04,
+ * while TCOBASE is + 0x40.
+ */
+#define MCP51_SMI_EN(base) ((base) - 0x40 + 0x04)
+# define MCP51_SMI_EN_TCO ((1 << 4) | (1 << 5))
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c
new file mode 100644
index 00000000..945ee830
--- /dev/null
+++ b/drivers/watchdog/octeon-wdt-main.c
@@ -0,0 +1,746 @@
+/*
+ * Octeon Watchdog driver
+ *
+ * Copyright (C) 2007, 2008, 2009, 2010 Cavium Networks
+ *
+ * Some parts derived from wdt.c
+ *
+ * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ *
+ * The OCTEON watchdog has a maximum timeout of 2^32 * io_clock.
+ * For most systems this is less than 10 seconds, so to allow for
+ * software to request longer watchdog heartbeats, we maintain software
+ * counters to count multiples of the base rate. If the system locks
+ * up in such a manner that we can not run the software counters, the
+ * only result is a watchdog reset sooner than was requested. But
+ * that is OK, because in this case userspace would likely not be able
+ * to do anything anyhow.
+ *
+ * The hardware watchdog interval we call the period. The OCTEON
+ * watchdog goes through several stages, after the first period an
+ * irq is asserted, then if it is not reset, after the next period NMI
+ * is asserted, then after an additional period a chip wide soft reset.
+ * So for the software counters, we reset watchdog after each period
+ * and decrement the counter. But for the last two periods we need to
+ * let the watchdog progress to the NMI stage so we disable the irq
+ * and let it proceed. Once in the NMI, we print the register state
+ * to the serial port and then wait for the reset.
+ *
+ * A watchdog is maintained for each CPU in the system, that way if
+ * one CPU suffers a lockup, we also get a register dump and reset.
+ * The userspace ping resets the watchdog on all CPUs.
+ *
+ * Before userspace opens the watchdog device, we still run the
+ * watchdogs to catch any lockups that may be kernel related.
+ *
+ */
+
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/watchdog.h>
+#include <linux/cpumask.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/fs.h>
+#include <linux/irq.h>
+
+#include <asm/mipsregs.h>
+#include <asm/uasm.h>
+
+#include <asm/octeon/octeon.h>
+
+/* The count needed to achieve timeout_sec. */
+static unsigned int timeout_cnt;
+
+/* The maximum period supported. */
+static unsigned int max_timeout_sec;
+
+/* The current period. */
+static unsigned int timeout_sec;
+
+/* Set to non-zero when userspace countdown mode active */
+static int do_coundown;
+static unsigned int countdown_reset;
+static unsigned int per_cpu_countdown[NR_CPUS];
+
+static cpumask_t irq_enabled_cpus;
+
+#define WD_TIMO 60 /* Default heartbeat = 60 seconds */
+
+static int heartbeat = WD_TIMO;
+module_param(heartbeat, int, S_IRUGO);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat in seconds. (0 < heartbeat, default="
+ __MODULE_STRING(WD_TIMO) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, S_IRUGO);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned long octeon_wdt_is_open;
+static char expect_close;
+
+static u32 __initdata nmi_stage1_insns[64];
+/* We need one branch and therefore one relocation per target label. */
+static struct uasm_label __initdata labels[5];
+static struct uasm_reloc __initdata relocs[5];
+
+enum lable_id {
+ label_enter_bootloader = 1
+};
+
+/* Some CP0 registers */
+#define K0 26
+#define C0_CVMMEMCTL 11, 7
+#define C0_STATUS 12, 0
+#define C0_EBASE 15, 1
+#define C0_DESAVE 31, 0
+
+void octeon_wdt_nmi_stage2(void);
+
+static void __init octeon_wdt_build_stage1(void)
+{
+ int i;
+ int len;
+ u32 *p = nmi_stage1_insns;
+#ifdef CONFIG_HOTPLUG_CPU
+ struct uasm_label *l = labels;
+ struct uasm_reloc *r = relocs;
+#endif
+
+ /*
+ * For the next few instructions running the debugger may
+ * cause corruption of k0 in the saved registers. Since we're
+ * about to crash, nobody probably cares.
+ *
+ * Save K0 into the debug scratch register
+ */
+ uasm_i_dmtc0(&p, K0, C0_DESAVE);
+
+ uasm_i_mfc0(&p, K0, C0_STATUS);
+#ifdef CONFIG_HOTPLUG_CPU
+ uasm_il_bbit0(&p, &r, K0, ilog2(ST0_NMI), label_enter_bootloader);
+#endif
+ /* Force 64-bit addressing enabled */
+ uasm_i_ori(&p, K0, K0, ST0_UX | ST0_SX | ST0_KX);
+ uasm_i_mtc0(&p, K0, C0_STATUS);
+
+#ifdef CONFIG_HOTPLUG_CPU
+ uasm_i_mfc0(&p, K0, C0_EBASE);
+ /* Coreid number in K0 */
+ uasm_i_andi(&p, K0, K0, 0xf);
+ /* 8 * coreid in bits 16-31 */
+ uasm_i_dsll_safe(&p, K0, K0, 3 + 16);
+ uasm_i_ori(&p, K0, K0, 0x8001);
+ uasm_i_dsll_safe(&p, K0, K0, 16);
+ uasm_i_ori(&p, K0, K0, 0x0700);
+ uasm_i_drotr_safe(&p, K0, K0, 32);
+ /*
+ * Should result in: 0x8001,0700,0000,8*coreid which is
+ * CVMX_CIU_WDOGX(coreid) - 0x0500
+ *
+ * Now ld K0, CVMX_CIU_WDOGX(coreid)
+ */
+ uasm_i_ld(&p, K0, 0x500, K0);
+ /*
+ * If bit one set handle the NMI as a watchdog event.
+ * otherwise transfer control to bootloader.
+ */
+ uasm_il_bbit0(&p, &r, K0, 1, label_enter_bootloader);
+ uasm_i_nop(&p);
+#endif
+
+ /* Clear Dcache so cvmseg works right. */
+ uasm_i_cache(&p, 1, 0, 0);
+
+ /* Use K0 to do a read/modify/write of CVMMEMCTL */
+ uasm_i_dmfc0(&p, K0, C0_CVMMEMCTL);
+ /* Clear out the size of CVMSEG */
+ uasm_i_dins(&p, K0, 0, 0, 6);
+ /* Set CVMSEG to its largest value */
+ uasm_i_ori(&p, K0, K0, 0x1c0 | 54);
+ /* Store the CVMMEMCTL value */
+ uasm_i_dmtc0(&p, K0, C0_CVMMEMCTL);
+
+ /* Load the address of the second stage handler */
+ UASM_i_LA(&p, K0, (long)octeon_wdt_nmi_stage2);
+ uasm_i_jr(&p, K0);
+ uasm_i_dmfc0(&p, K0, C0_DESAVE);
+
+#ifdef CONFIG_HOTPLUG_CPU
+ uasm_build_label(&l, p, label_enter_bootloader);
+ /* Jump to the bootloader and restore K0 */
+ UASM_i_LA(&p, K0, (long)octeon_bootloader_entry_addr);
+ uasm_i_jr(&p, K0);
+ uasm_i_dmfc0(&p, K0, C0_DESAVE);
+#endif
+ uasm_resolve_relocs(relocs, labels);
+
+ len = (int)(p - nmi_stage1_insns);
+ pr_debug("Synthesized NMI stage 1 handler (%d instructions).\n", len);
+
+ pr_debug("\t.set push\n");
+ pr_debug("\t.set noreorder\n");
+ for (i = 0; i < len; i++)
+ pr_debug("\t.word 0x%08x\n", nmi_stage1_insns[i]);
+ pr_debug("\t.set pop\n");
+
+ if (len > 32)
+ panic("NMI stage 1 handler exceeds 32 instructions, was %d\n", len);
+}
+
+static int cpu2core(int cpu)
+{
+#ifdef CONFIG_SMP
+ return cpu_logical_map(cpu);
+#else
+ return cvmx_get_core_num();
+#endif
+}
+
+static int core2cpu(int coreid)
+{
+#ifdef CONFIG_SMP
+ return cpu_number_map(coreid);
+#else
+ return 0;
+#endif
+}
+
+/**
+ * Poke the watchdog when an interrupt is received
+ *
+ * @cpl:
+ * @dev_id:
+ *
+ * Returns
+ */
+static irqreturn_t octeon_wdt_poke_irq(int cpl, void *dev_id)
+{
+ unsigned int core = cvmx_get_core_num();
+ int cpu = core2cpu(core);
+
+ if (do_coundown) {
+ if (per_cpu_countdown[cpu] > 0) {
+ /* We're alive, poke the watchdog */
+ cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1);
+ per_cpu_countdown[cpu]--;
+ } else {
+ /* Bad news, you are about to reboot. */
+ disable_irq_nosync(cpl);
+ cpumask_clear_cpu(cpu, &irq_enabled_cpus);
+ }
+ } else {
+ /* Not open, just ping away... */
+ cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1);
+ }
+ return IRQ_HANDLED;
+}
+
+/* From setup.c */
+extern int prom_putchar(char c);
+
+/**
+ * Write a string to the uart
+ *
+ * @str: String to write
+ */
+static void octeon_wdt_write_string(const char *str)
+{
+ /* Just loop writing one byte at a time */
+ while (*str)
+ prom_putchar(*str++);
+}
+
+/**
+ * Write a hex number out of the uart
+ *
+ * @value: Number to display
+ * @digits: Number of digits to print (1 to 16)
+ */
+static void octeon_wdt_write_hex(u64 value, int digits)
+{
+ int d;
+ int v;
+ for (d = 0; d < digits; d++) {
+ v = (value >> ((digits - d - 1) * 4)) & 0xf;
+ if (v >= 10)
+ prom_putchar('a' + v - 10);
+ else
+ prom_putchar('0' + v);
+ }
+}
+
+const char *reg_name[] = {
+ "$0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+/**
+ * NMI stage 3 handler. NMIs are handled in the following manner:
+ * 1) The first NMI handler enables CVMSEG and transfers from
+ * the bootbus region into normal memory. It is careful to not
+ * destroy any registers.
+ * 2) The second stage handler uses CVMSEG to save the registers
+ * and create a stack for C code. It then calls the third level
+ * handler with one argument, a pointer to the register values.
+ * 3) The third, and final, level handler is the following C
+ * function that prints out some useful infomration.
+ *
+ * @reg: Pointer to register state before the NMI
+ */
+void octeon_wdt_nmi_stage3(u64 reg[32])
+{
+ u64 i;
+
+ unsigned int coreid = cvmx_get_core_num();
+ /*
+ * Save status and cause early to get them before any changes
+ * might happen.
+ */
+ u64 cp0_cause = read_c0_cause();
+ u64 cp0_status = read_c0_status();
+ u64 cp0_error_epc = read_c0_errorepc();
+ u64 cp0_epc = read_c0_epc();
+
+ /* Delay so output from all cores output is not jumbled together. */
+ __delay(100000000ull * coreid);
+
+ octeon_wdt_write_string("\r\n*** NMI Watchdog interrupt on Core 0x");
+ octeon_wdt_write_hex(coreid, 1);
+ octeon_wdt_write_string(" ***\r\n");
+ for (i = 0; i < 32; i++) {
+ octeon_wdt_write_string("\t");
+ octeon_wdt_write_string(reg_name[i]);
+ octeon_wdt_write_string("\t0x");
+ octeon_wdt_write_hex(reg[i], 16);
+ if (i & 1)
+ octeon_wdt_write_string("\r\n");
+ }
+ octeon_wdt_write_string("\terr_epc\t0x");
+ octeon_wdt_write_hex(cp0_error_epc, 16);
+
+ octeon_wdt_write_string("\tepc\t0x");
+ octeon_wdt_write_hex(cp0_epc, 16);
+ octeon_wdt_write_string("\r\n");
+
+ octeon_wdt_write_string("\tstatus\t0x");
+ octeon_wdt_write_hex(cp0_status, 16);
+ octeon_wdt_write_string("\tcause\t0x");
+ octeon_wdt_write_hex(cp0_cause, 16);
+ octeon_wdt_write_string("\r\n");
+
+ octeon_wdt_write_string("\tsum0\t0x");
+ octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_SUM0(coreid * 2)), 16);
+ octeon_wdt_write_string("\ten0\t0x");
+ octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)), 16);
+ octeon_wdt_write_string("\r\n");
+
+ octeon_wdt_write_string("*** Chip soft reset soon ***\r\n");
+}
+
+static void octeon_wdt_disable_interrupt(int cpu)
+{
+ unsigned int core;
+ unsigned int irq;
+ union cvmx_ciu_wdogx ciu_wdog;
+
+ core = cpu2core(cpu);
+
+ irq = OCTEON_IRQ_WDOG0 + core;
+
+ /* Poke the watchdog to clear out its state */
+ cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1);
+
+ /* Disable the hardware. */
+ ciu_wdog.u64 = 0;
+ cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64);
+
+ free_irq(irq, octeon_wdt_poke_irq);
+}
+
+static void octeon_wdt_setup_interrupt(int cpu)
+{
+ unsigned int core;
+ unsigned int irq;
+ union cvmx_ciu_wdogx ciu_wdog;
+
+ core = cpu2core(cpu);
+
+ /* Disable it before doing anything with the interrupts. */
+ ciu_wdog.u64 = 0;
+ cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64);
+
+ per_cpu_countdown[cpu] = countdown_reset;
+
+ irq = OCTEON_IRQ_WDOG0 + core;
+
+ if (request_irq(irq, octeon_wdt_poke_irq,
+ IRQF_DISABLED, "octeon_wdt", octeon_wdt_poke_irq))
+ panic("octeon_wdt: Couldn't obtain irq %d", irq);
+
+ cpumask_set_cpu(cpu, &irq_enabled_cpus);
+
+ /* Poke the watchdog to clear out its state */
+ cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1);
+
+ /* Finally enable the watchdog now that all handlers are installed */
+ ciu_wdog.u64 = 0;
+ ciu_wdog.s.len = timeout_cnt;
+ ciu_wdog.s.mode = 3; /* 3 = Interrupt + NMI + Soft-Reset */
+ cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64);
+}
+
+static int octeon_wdt_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+
+ switch (action) {
+ case CPU_DOWN_PREPARE:
+ octeon_wdt_disable_interrupt(cpu);
+ break;
+ case CPU_ONLINE:
+ case CPU_DOWN_FAILED:
+ octeon_wdt_setup_interrupt(cpu);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static void octeon_wdt_ping(void)
+{
+ int cpu;
+ int coreid;
+
+ for_each_online_cpu(cpu) {
+ coreid = cpu2core(cpu);
+ cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1);
+ per_cpu_countdown[cpu] = countdown_reset;
+ if ((countdown_reset || !do_coundown) &&
+ !cpumask_test_cpu(cpu, &irq_enabled_cpus)) {
+ /* We have to enable the irq */
+ int irq = OCTEON_IRQ_WDOG0 + coreid;
+ enable_irq(irq);
+ cpumask_set_cpu(cpu, &irq_enabled_cpus);
+ }
+ }
+}
+
+static void octeon_wdt_calc_parameters(int t)
+{
+ unsigned int periods;
+
+ timeout_sec = max_timeout_sec;
+
+
+ /*
+ * Find the largest interrupt period, that can evenly divide
+ * the requested heartbeat time.
+ */
+ while ((t % timeout_sec) != 0)
+ timeout_sec--;
+
+ periods = t / timeout_sec;
+
+ /*
+ * The last two periods are after the irq is disabled, and
+ * then to the nmi, so we subtract them off.
+ */
+
+ countdown_reset = periods > 2 ? periods - 2 : 0;
+ heartbeat = t;
+ timeout_cnt = ((octeon_get_io_clock_rate() >> 8) * timeout_sec) >> 8;
+}
+
+static int octeon_wdt_set_heartbeat(int t)
+{
+ int cpu;
+ int coreid;
+ union cvmx_ciu_wdogx ciu_wdog;
+
+ if (t <= 0)
+ return -1;
+
+ octeon_wdt_calc_parameters(t);
+
+ for_each_online_cpu(cpu) {
+ coreid = cpu2core(cpu);
+ cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1);
+ ciu_wdog.u64 = 0;
+ ciu_wdog.s.len = timeout_cnt;
+ ciu_wdog.s.mode = 3; /* 3 = Interrupt + NMI + Soft-Reset */
+ cvmx_write_csr(CVMX_CIU_WDOGX(coreid), ciu_wdog.u64);
+ cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1);
+ }
+ octeon_wdt_ping(); /* Get the irqs back on. */
+ return 0;
+}
+
+/**
+ * octeon_wdt_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write (unused as data does not matter here
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t octeon_wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 1;
+ }
+ }
+ octeon_wdt_ping();
+ }
+ return count;
+}
+
+/**
+ * octeon_wdt_ioctl:
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all
+ * watchdogs according to their available features. We only
+ * actually usefully support querying capabilities and setting
+ * the timeout.
+ */
+
+static long octeon_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_heartbeat;
+
+ static struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT|
+ WDIOF_MAGICCLOSE|
+ WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = "OCTEON",
+ };
+
+ 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_KEEPALIVE:
+ octeon_wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_heartbeat, p))
+ return -EFAULT;
+ if (octeon_wdt_set_heartbeat(new_heartbeat))
+ return -EINVAL;
+ /* Fall through. */
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/**
+ * octeon_wdt_open:
+ * @inode: inode of device
+ * @file: file handle to device
+ *
+ * The watchdog device has been opened. The watchdog device is single
+ * open and on opening we do a ping to reset the counters.
+ */
+
+static int octeon_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &octeon_wdt_is_open))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+ octeon_wdt_ping();
+ do_coundown = 1;
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * octeon_wdt_release:
+ * @inode: inode to board
+ * @file: file handle to board
+ *
+ * The watchdog has a configurable API. There is a religious dispute
+ * between people who want their watchdog to be able to shut down and
+ * those who want to be sure if the watchdog manager dies the machine
+ * reboots. In the former case we disable the counters, in the latter
+ * case you have to open it again very soon.
+ */
+
+static int octeon_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close) {
+ do_coundown = 0;
+ octeon_wdt_ping();
+ } else {
+ pr_crit("octeon_wdt: WDT device closed unexpectedly. WDT will not stop!\n");
+ }
+ clear_bit(0, &octeon_wdt_is_open);
+ expect_close = 0;
+ return 0;
+}
+
+static const struct file_operations octeon_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = octeon_wdt_write,
+ .unlocked_ioctl = octeon_wdt_ioctl,
+ .open = octeon_wdt_open,
+ .release = octeon_wdt_release,
+};
+
+static struct miscdevice octeon_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &octeon_wdt_fops,
+};
+
+static struct notifier_block octeon_wdt_cpu_notifier = {
+ .notifier_call = octeon_wdt_cpu_callback,
+};
+
+
+/**
+ * Module/ driver initialization.
+ *
+ * Returns Zero on success
+ */
+static int __init octeon_wdt_init(void)
+{
+ int i;
+ int ret;
+ int cpu;
+ u64 *ptr;
+
+ /*
+ * Watchdog time expiration length = The 16 bits of LEN
+ * represent the most significant bits of a 24 bit decrementer
+ * that decrements every 256 cycles.
+ *
+ * Try for a timeout of 5 sec, if that fails a smaller number
+ * of even seconds,
+ */
+ max_timeout_sec = 6;
+ do {
+ max_timeout_sec--;
+ timeout_cnt = ((octeon_get_io_clock_rate() >> 8) * max_timeout_sec) >> 8;
+ } while (timeout_cnt > 65535);
+
+ BUG_ON(timeout_cnt == 0);
+
+ octeon_wdt_calc_parameters(heartbeat);
+
+ pr_info("octeon_wdt: Initial granularity %d Sec.\n", timeout_sec);
+
+ ret = misc_register(&octeon_wdt_miscdev);
+ if (ret) {
+ pr_err("octeon_wdt: cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto out;
+ }
+
+ /* Build the NMI handler ... */
+ octeon_wdt_build_stage1();
+
+ /* ... and install it. */
+ ptr = (u64 *) nmi_stage1_insns;
+ for (i = 0; i < 16; i++) {
+ cvmx_write_csr(CVMX_MIO_BOOT_LOC_ADR, i * 8);
+ cvmx_write_csr(CVMX_MIO_BOOT_LOC_DAT, ptr[i]);
+ }
+ cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0x81fc0000);
+
+ cpumask_clear(&irq_enabled_cpus);
+
+ for_each_online_cpu(cpu)
+ octeon_wdt_setup_interrupt(cpu);
+
+ register_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+out:
+ return ret;
+}
+
+/**
+ * Module / driver shutdown
+ */
+static void __exit octeon_wdt_cleanup(void)
+{
+ int cpu;
+
+ misc_deregister(&octeon_wdt_miscdev);
+
+ unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+
+ for_each_online_cpu(cpu) {
+ int core = cpu2core(cpu);
+ /* Disable the watchdog */
+ cvmx_write_csr(CVMX_CIU_WDOGX(core), 0);
+ /* Free the interrupt handler */
+ free_irq(OCTEON_IRQ_WDOG0 + core, octeon_wdt_poke_irq);
+ }
+ /*
+ * Disable the boot-bus memory, the code it points to is soon
+ * to go missing.
+ */
+ cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>");
+MODULE_DESCRIPTION("Cavium Networks Octeon Watchdog driver.");
+module_init(octeon_wdt_init);
+module_exit(octeon_wdt_cleanup);
diff --git a/drivers/watchdog/octeon-wdt-nmi.S b/drivers/watchdog/octeon-wdt-nmi.S
new file mode 100644
index 00000000..8a900a5e
--- /dev/null
+++ b/drivers/watchdog/octeon-wdt-nmi.S
@@ -0,0 +1,64 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2007 Cavium Networks
+ */
+#include <asm/asm.h>
+#include <asm/regdef.h>
+
+#define SAVE_REG(r) sd $r, -32768+6912-(32-r)*8($0)
+
+ NESTED(octeon_wdt_nmi_stage2, 0, sp)
+ .set push
+ .set noreorder
+ .set noat
+ /* Save all registers to the top CVMSEG. This shouldn't
+ * corrupt any state used by the kernel. Also all registers
+ * should have the value right before the NMI. */
+ SAVE_REG(0)
+ SAVE_REG(1)
+ SAVE_REG(2)
+ SAVE_REG(3)
+ SAVE_REG(4)
+ SAVE_REG(5)
+ SAVE_REG(6)
+ SAVE_REG(7)
+ SAVE_REG(8)
+ SAVE_REG(9)
+ SAVE_REG(10)
+ SAVE_REG(11)
+ SAVE_REG(12)
+ SAVE_REG(13)
+ SAVE_REG(14)
+ SAVE_REG(15)
+ SAVE_REG(16)
+ SAVE_REG(17)
+ SAVE_REG(18)
+ SAVE_REG(19)
+ SAVE_REG(20)
+ SAVE_REG(21)
+ SAVE_REG(22)
+ SAVE_REG(23)
+ SAVE_REG(24)
+ SAVE_REG(25)
+ SAVE_REG(26)
+ SAVE_REG(27)
+ SAVE_REG(28)
+ SAVE_REG(29)
+ SAVE_REG(30)
+ SAVE_REG(31)
+ /* Set the stack to begin right below the registers */
+ li sp, -32768+6912-32*8
+ /* Load the address of the third stage handler */
+ dla a0, octeon_wdt_nmi_stage3
+ /* Call the third stage handler */
+ jal a0
+ /* a0 is the address of the saved registers */
+ move a0, sp
+ /* Loop forvever if we get here. */
+1: b 1b
+ nop
+ .set pop
+ END(octeon_wdt_nmi_stage2)
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
new file mode 100644
index 00000000..2b4acb86
--- /dev/null
+++ b/drivers/watchdog/omap_wdt.c
@@ -0,0 +1,457 @@
+/*
+ * omap_wdt.c
+ *
+ * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog
+ *
+ * Author: MontaVista Software, Inc.
+ * <gdavis@mvista.com> or <source@mvista.com>
+ *
+ * 2003 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * History:
+ *
+ * 20030527: George G. Davis <gdavis@mvista.com>
+ * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c
+ * (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
+ * Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * Copyright (c) 2004 Texas Instruments.
+ * 1. Modified to support OMAP1610 32-KHz watchdog timer
+ * 2. Ported to 2.6 kernel
+ *
+ * Copyright (c) 2005 David Brownell
+ * Use the driver model and standard identifiers; handle bigger timeouts.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <mach/hardware.h>
+#include <plat/prcm.h>
+
+#include "omap_wdt.h"
+
+static struct platform_device *omap_wdt_dev;
+
+static unsigned timer_margin;
+module_param(timer_margin, uint, 0);
+MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
+
+static unsigned int wdt_trgr_pattern = 0x1234;
+static spinlock_t wdt_lock;
+
+struct omap_wdt_dev {
+ void __iomem *base; /* physical */
+ struct device *dev;
+ int omap_wdt_users;
+ struct resource *mem;
+ struct miscdevice omap_wdt_miscdev;
+};
+
+static void omap_wdt_ping(struct omap_wdt_dev *wdev)
+{
+ void __iomem *base = wdev->base;
+
+ /* wait for posted write to complete */
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
+ cpu_relax();
+
+ wdt_trgr_pattern = ~wdt_trgr_pattern;
+ __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
+
+ /* wait for posted write to complete */
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
+ cpu_relax();
+ /* reloaded WCRR from WLDR */
+}
+
+static void omap_wdt_enable(struct omap_wdt_dev *wdev)
+{
+ void __iomem *base = wdev->base;
+
+ /* Sequence to enable the watchdog */
+ __raw_writel(0xBBBB, base + OMAP_WATCHDOG_SPR);
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
+ cpu_relax();
+
+ __raw_writel(0x4444, base + OMAP_WATCHDOG_SPR);
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
+ cpu_relax();
+}
+
+static void omap_wdt_disable(struct omap_wdt_dev *wdev)
+{
+ void __iomem *base = wdev->base;
+
+ /* sequence required to disable watchdog */
+ __raw_writel(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
+ cpu_relax();
+
+ __raw_writel(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
+ cpu_relax();
+}
+
+static void omap_wdt_adjust_timeout(unsigned new_timeout)
+{
+ if (new_timeout < TIMER_MARGIN_MIN)
+ new_timeout = TIMER_MARGIN_DEFAULT;
+ if (new_timeout > TIMER_MARGIN_MAX)
+ new_timeout = TIMER_MARGIN_MAX;
+ timer_margin = new_timeout;
+}
+
+static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
+{
+ u32 pre_margin = GET_WLDR_VAL(timer_margin);
+ void __iomem *base = wdev->base;
+
+ pm_runtime_get_sync(wdev->dev);
+
+ /* just count up at 32 KHz */
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
+ cpu_relax();
+
+ __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR);
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
+ cpu_relax();
+
+ pm_runtime_put_sync(wdev->dev);
+}
+
+/*
+ * Allow only one task to hold it open
+ */
+static int omap_wdt_open(struct inode *inode, struct file *file)
+{
+ struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev);
+ void __iomem *base = wdev->base;
+
+ if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users)))
+ return -EBUSY;
+
+ pm_runtime_get_sync(wdev->dev);
+
+ /* initialize prescaler */
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
+ cpu_relax();
+
+ __raw_writel((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
+ cpu_relax();
+
+ file->private_data = (void *) wdev;
+
+ omap_wdt_set_timeout(wdev);
+ omap_wdt_ping(wdev); /* trigger loading of new timeout value */
+ omap_wdt_enable(wdev);
+
+ pm_runtime_put_sync(wdev->dev);
+
+ return nonseekable_open(inode, file);
+}
+
+static int omap_wdt_release(struct inode *inode, struct file *file)
+{
+ struct omap_wdt_dev *wdev = file->private_data;
+
+ /*
+ * Shut off the timer unless NOWAYOUT is defined.
+ */
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ pm_runtime_get_sync(wdev->dev);
+
+ omap_wdt_disable(wdev);
+
+ pm_runtime_put_sync(wdev->dev);
+#else
+ printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n");
+#endif
+ wdev->omap_wdt_users = 0;
+
+ return 0;
+}
+
+static ssize_t omap_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ struct omap_wdt_dev *wdev = file->private_data;
+
+ /* Refresh LOAD_TIME. */
+ if (len) {
+ pm_runtime_get_sync(wdev->dev);
+ spin_lock(&wdt_lock);
+ omap_wdt_ping(wdev);
+ spin_unlock(&wdt_lock);
+ pm_runtime_put_sync(wdev->dev);
+ }
+ return len;
+}
+
+static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct omap_wdt_dev *wdev;
+ int new_margin;
+ static const struct watchdog_info ident = {
+ .identity = "OMAP Watchdog",
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 0,
+ };
+
+ wdev = file->private_data;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident));
+ case WDIOC_GETSTATUS:
+ return put_user(0, (int __user *)arg);
+ case WDIOC_GETBOOTSTATUS:
+ if (cpu_is_omap16xx())
+ return put_user(__raw_readw(ARM_SYSST),
+ (int __user *)arg);
+ if (cpu_is_omap24xx())
+ return put_user(omap_prcm_get_reset_sources(),
+ (int __user *)arg);
+ case WDIOC_KEEPALIVE:
+ pm_runtime_get_sync(wdev->dev);
+ spin_lock(&wdt_lock);
+ omap_wdt_ping(wdev);
+ spin_unlock(&wdt_lock);
+ pm_runtime_put_sync(wdev->dev);
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)arg))
+ return -EFAULT;
+ omap_wdt_adjust_timeout(new_margin);
+
+ pm_runtime_get_sync(wdev->dev);
+ spin_lock(&wdt_lock);
+ omap_wdt_disable(wdev);
+ omap_wdt_set_timeout(wdev);
+ omap_wdt_enable(wdev);
+
+ omap_wdt_ping(wdev);
+ spin_unlock(&wdt_lock);
+ pm_runtime_put_sync(wdev->dev);
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timer_margin, (int __user *)arg);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations omap_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = omap_wdt_write,
+ .unlocked_ioctl = omap_wdt_ioctl,
+ .open = omap_wdt_open,
+ .release = omap_wdt_release,
+ .llseek = no_llseek,
+};
+
+static int __devinit omap_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res, *mem;
+ struct omap_wdt_dev *wdev;
+ int ret;
+
+ /* reserve static register mappings */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENOENT;
+ goto err_get_resource;
+ }
+
+ if (omap_wdt_dev) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ mem = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!mem) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL);
+ if (!wdev) {
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ wdev->omap_wdt_users = 0;
+ wdev->mem = mem;
+ wdev->dev = &pdev->dev;
+
+ wdev->base = ioremap(res->start, resource_size(res));
+ if (!wdev->base) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ platform_set_drvdata(pdev, wdev);
+
+ pm_runtime_enable(wdev->dev);
+ pm_runtime_get_sync(wdev->dev);
+
+ omap_wdt_disable(wdev);
+ omap_wdt_adjust_timeout(timer_margin);
+
+ wdev->omap_wdt_miscdev.parent = &pdev->dev;
+ wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR;
+ wdev->omap_wdt_miscdev.name = "watchdog";
+ wdev->omap_wdt_miscdev.fops = &omap_wdt_fops;
+
+ ret = misc_register(&(wdev->omap_wdt_miscdev));
+ if (ret)
+ goto err_misc;
+
+ pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
+ __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
+ timer_margin);
+
+ pm_runtime_put_sync(wdev->dev);
+
+ omap_wdt_dev = pdev;
+
+ return 0;
+
+err_misc:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(wdev->base);
+
+err_ioremap:
+ wdev->base = NULL;
+ kfree(wdev);
+
+err_kzalloc:
+ release_mem_region(res->start, resource_size(res));
+
+err_busy:
+err_get_resource:
+
+ return ret;
+}
+
+static void omap_wdt_shutdown(struct platform_device *pdev)
+{
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+
+ if (wdev->omap_wdt_users) {
+ pm_runtime_get_sync(wdev->dev);
+ omap_wdt_disable(wdev);
+ pm_runtime_put_sync(wdev->dev);
+ }
+}
+
+static int __devexit omap_wdt_remove(struct platform_device *pdev)
+{
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENOENT;
+
+ misc_deregister(&(wdev->omap_wdt_miscdev));
+ release_mem_region(res->start, resource_size(res));
+ platform_set_drvdata(pdev, NULL);
+
+ iounmap(wdev->base);
+
+ kfree(wdev);
+ omap_wdt_dev = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/* REVISIT ... not clear this is the best way to handle system suspend; and
+ * it's very inappropriate for selective device suspend (e.g. suspending this
+ * through sysfs rather than by stopping the watchdog daemon). Also, this
+ * may not play well enough with NOWAYOUT...
+ */
+
+static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+
+ if (wdev->omap_wdt_users) {
+ pm_runtime_get_sync(wdev->dev);
+ omap_wdt_disable(wdev);
+ pm_runtime_put_sync(wdev->dev);
+ }
+
+ return 0;
+}
+
+static int omap_wdt_resume(struct platform_device *pdev)
+{
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+
+ if (wdev->omap_wdt_users) {
+ pm_runtime_get_sync(wdev->dev);
+ omap_wdt_enable(wdev);
+ omap_wdt_ping(wdev);
+ pm_runtime_put_sync(wdev->dev);
+ }
+
+ return 0;
+}
+
+#else
+#define omap_wdt_suspend NULL
+#define omap_wdt_resume NULL
+#endif
+
+static struct platform_driver omap_wdt_driver = {
+ .probe = omap_wdt_probe,
+ .remove = __devexit_p(omap_wdt_remove),
+ .shutdown = omap_wdt_shutdown,
+ .suspend = omap_wdt_suspend,
+ .resume = omap_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "omap_wdt",
+ },
+};
+
+static int __init omap_wdt_init(void)
+{
+ spin_lock_init(&wdt_lock);
+ return platform_driver_register(&omap_wdt_driver);
+}
+
+static void __exit omap_wdt_exit(void)
+{
+ platform_driver_unregister(&omap_wdt_driver);
+}
+
+module_init(omap_wdt_init);
+module_exit(omap_wdt_exit);
+
+MODULE_AUTHOR("George G. Davis");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:omap_wdt");
diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h
new file mode 100644
index 00000000..09b774cf
--- /dev/null
+++ b/drivers/watchdog/omap_wdt.h
@@ -0,0 +1,54 @@
+/*
+ * linux/drivers/char/watchdog/omap_wdt.h
+ *
+ * BRIEF MODULE DESCRIPTION
+ * OMAP Watchdog timer register definitions
+ *
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _OMAP_WATCHDOG_H
+#define _OMAP_WATCHDOG_H
+
+#define OMAP_WATCHDOG_REV (0x00)
+#define OMAP_WATCHDOG_SYS_CONFIG (0x10)
+#define OMAP_WATCHDOG_STATUS (0x14)
+#define OMAP_WATCHDOG_CNTRL (0x24)
+#define OMAP_WATCHDOG_CRR (0x28)
+#define OMAP_WATCHDOG_LDR (0x2c)
+#define OMAP_WATCHDOG_TGR (0x30)
+#define OMAP_WATCHDOG_WPS (0x34)
+#define OMAP_WATCHDOG_SPR (0x48)
+
+/* Using the prescaler, the OMAP watchdog could go for many
+ * months before firing. These limits work without scaling,
+ * with the 60 second default assumed by most tools and docs.
+ */
+#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */
+#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */
+#define TIMER_MARGIN_MIN 1
+
+#define PTV 0 /* prescale */
+#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1)
+
+#endif /* _OMAP_WATCHDOG_H */
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
new file mode 100644
index 00000000..2d9fb96a
--- /dev/null
+++ b/drivers/watchdog/orion_wdt.c
@@ -0,0 +1,322 @@
+/*
+ * drivers/watchdog/orion_wdt.c
+ *
+ * Watchdog driver for Orion/Kirkwood processors
+ *
+ * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <mach/bridge-regs.h>
+#include <plat/orion_wdt.h>
+
+/*
+ * Watchdog timer block registers.
+ */
+#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000)
+#define WDT_EN 0x0010
+#define WDT_VAL (TIMER_VIRT_BASE + 0x0024)
+
+#define WDT_MAX_CYCLE_COUNT 0xffffffff
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static int heartbeat = -1; /* module parameter (seconds) */
+static unsigned int wdt_max_duration; /* (seconds) */
+static unsigned int wdt_tclk;
+static unsigned long wdt_status;
+static spinlock_t wdt_lock;
+
+static void orion_wdt_ping(void)
+{
+ spin_lock(&wdt_lock);
+
+ /* Reload watchdog duration */
+ writel(wdt_tclk * heartbeat, WDT_VAL);
+
+ spin_unlock(&wdt_lock);
+}
+
+static void orion_wdt_enable(void)
+{
+ u32 reg;
+
+ spin_lock(&wdt_lock);
+
+ /* Set watchdog duration */
+ writel(wdt_tclk * heartbeat, WDT_VAL);
+
+ /* Clear watchdog timer interrupt */
+ reg = readl(BRIDGE_CAUSE);
+ reg &= ~WDT_INT_REQ;
+ writel(reg, BRIDGE_CAUSE);
+
+ /* Enable watchdog timer */
+ reg = readl(TIMER_CTRL);
+ reg |= WDT_EN;
+ writel(reg, TIMER_CTRL);
+
+ /* Enable reset on watchdog */
+ reg = readl(RSTOUTn_MASK);
+ reg |= WDT_RESET_OUT_EN;
+ writel(reg, RSTOUTn_MASK);
+
+ spin_unlock(&wdt_lock);
+}
+
+static void orion_wdt_disable(void)
+{
+ u32 reg;
+
+ spin_lock(&wdt_lock);
+
+ /* Disable reset on watchdog */
+ reg = readl(RSTOUTn_MASK);
+ reg &= ~WDT_RESET_OUT_EN;
+ writel(reg, RSTOUTn_MASK);
+
+ /* Disable watchdog timer */
+ reg = readl(TIMER_CTRL);
+ reg &= ~WDT_EN;
+ writel(reg, TIMER_CTRL);
+
+ spin_unlock(&wdt_lock);
+}
+
+static int orion_wdt_get_timeleft(int *time_left)
+{
+ spin_lock(&wdt_lock);
+ *time_left = readl(WDT_VAL) / wdt_tclk;
+ spin_unlock(&wdt_lock);
+ return 0;
+}
+
+static int orion_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ orion_wdt_enable();
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t orion_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ orion_wdt_ping();
+ }
+ return len;
+}
+
+static int orion_wdt_settimeout(int new_time)
+{
+ if ((new_time <= 0) || (new_time > wdt_max_duration))
+ return -EINVAL;
+
+ /* Set new watchdog time to be used when
+ * orion_wdt_enable() or orion_wdt_ping() is called. */
+ heartbeat = new_time;
+ return 0;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "Orion Watchdog",
+};
+
+static long orion_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ orion_wdt_ping();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (orion_wdt_settimeout(time)) {
+ ret = -EINVAL;
+ break;
+ }
+ orion_wdt_ping();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+
+ case WDIOC_GETTIMELEFT:
+ if (orion_wdt_get_timeleft(&time)) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = put_user(time, (int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int orion_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ orion_wdt_disable();
+ else
+ printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
+ "timer will not stop\n");
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static const struct file_operations orion_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = orion_wdt_write,
+ .unlocked_ioctl = orion_wdt_ioctl,
+ .open = orion_wdt_open,
+ .release = orion_wdt_release,
+};
+
+static struct miscdevice orion_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &orion_wdt_fops,
+};
+
+static int __devinit orion_wdt_probe(struct platform_device *pdev)
+{
+ struct orion_wdt_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+
+ if (pdata) {
+ wdt_tclk = pdata->tclk;
+ } else {
+ printk(KERN_ERR "Orion Watchdog misses platform data\n");
+ return -ENODEV;
+ }
+
+ if (orion_wdt_miscdev.parent)
+ return -EBUSY;
+ orion_wdt_miscdev.parent = &pdev->dev;
+
+ wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
+ if (orion_wdt_settimeout(heartbeat))
+ heartbeat = wdt_max_duration;
+
+ ret = misc_register(&orion_wdt_miscdev);
+ if (ret)
+ return ret;
+
+ printk(KERN_INFO "Orion Watchdog Timer: Initial timeout %d sec%s\n",
+ heartbeat, nowayout ? ", nowayout" : "");
+ return 0;
+}
+
+static int __devexit orion_wdt_remove(struct platform_device *pdev)
+{
+ int ret;
+
+ if (test_bit(WDT_IN_USE, &wdt_status)) {
+ orion_wdt_disable();
+ clear_bit(WDT_IN_USE, &wdt_status);
+ }
+
+ ret = misc_deregister(&orion_wdt_miscdev);
+ if (!ret)
+ orion_wdt_miscdev.parent = NULL;
+
+ return ret;
+}
+
+static void orion_wdt_shutdown(struct platform_device *pdev)
+{
+ if (test_bit(WDT_IN_USE, &wdt_status))
+ orion_wdt_disable();
+}
+
+static struct platform_driver orion_wdt_driver = {
+ .probe = orion_wdt_probe,
+ .remove = __devexit_p(orion_wdt_remove),
+ .shutdown = orion_wdt_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "orion_wdt",
+ },
+};
+
+static int __init orion_wdt_init(void)
+{
+ spin_lock_init(&wdt_lock);
+ return platform_driver_register(&orion_wdt_driver);
+}
+
+static void __exit orion_wdt_exit(void)
+{
+ platform_driver_unregister(&orion_wdt_driver);
+}
+
+module_init(orion_wdt_init);
+module_exit(orion_wdt_exit);
+
+MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
+MODULE_DESCRIPTION("Orion Processor Watchdog");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c
new file mode 100644
index 00000000..b7c13905
--- /dev/null
+++ b/drivers/watchdog/pc87413_wdt.c
@@ -0,0 +1,600 @@
+/*
+ * NS pc87413-wdt Watchdog Timer driver for Linux 2.6.x.x
+ *
+ * This code is based on wdt.c with original copyright.
+ *
+ * (C) Copyright 2006 Sven Anders, <anders@anduras.de>
+ * and Marcus Junker, <junker@anduras.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Sven Anders, Marcus Junker nor ANDURAS AG
+ * admit liability nor provide warranty for any of this software.
+ * This material is provided "AS-IS" and at no charge.
+ *
+ * Release 1.1
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+/* #define DEBUG 1 */
+
+#define DEFAULT_TIMEOUT 1 /* 1 minute */
+#define MAX_TIMEOUT 255
+
+#define VERSION "1.1"
+#define MODNAME "pc87413 WDT"
+#define PFX MODNAME ": "
+#define DPFX MODNAME " - DEBUG: "
+
+#define WDT_INDEX_IO_PORT (io+0) /* I/O port base (index register) */
+#define WDT_DATA_IO_PORT (WDT_INDEX_IO_PORT+1)
+#define SWC_LDN 0x04
+#define SIOCFG2 0x22 /* Serial IO register */
+#define WDCTL 0x10 /* Watchdog-Timer-Control-Register */
+#define WDTO 0x11 /* Watchdog timeout register */
+#define WDCFG 0x12 /* Watchdog config register */
+
+#define IO_DEFAULT 0x2E /* Address used on Portwell Boards */
+
+static int io = IO_DEFAULT;
+
+static int timeout = DEFAULT_TIMEOUT; /* timeout value */
+static unsigned long timer_enabled; /* is the timer enabled? */
+
+static char expect_close; /* is the close expected? */
+
+static DEFINE_SPINLOCK(io_lock); /* to guard us from io races */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+/* -- Low level function ----------------------------------------*/
+
+/* Select pins for Watchdog output */
+
+static inline void pc87413_select_wdt_out(void)
+{
+ unsigned int cr_data = 0;
+
+ /* Step 1: Select multiple pin,pin55,as WDT output */
+
+ outb_p(SIOCFG2, WDT_INDEX_IO_PORT);
+
+ cr_data = inb(WDT_DATA_IO_PORT);
+
+ cr_data |= 0x80; /* Set Bit7 to 1*/
+ outb_p(SIOCFG2, WDT_INDEX_IO_PORT);
+
+ outb_p(cr_data, WDT_DATA_IO_PORT);
+
+#ifdef DEBUG
+ printk(KERN_INFO DPFX
+ "Select multiple pin,pin55,as WDT output: Bit7 to 1: %d\n",
+ cr_data);
+#endif
+}
+
+/* Enable SWC functions */
+
+static inline void pc87413_enable_swc(void)
+{
+ unsigned int cr_data = 0;
+
+ /* Step 2: Enable SWC functions */
+
+ outb_p(0x07, WDT_INDEX_IO_PORT); /* Point SWC_LDN (LDN=4) */
+ outb_p(SWC_LDN, WDT_DATA_IO_PORT);
+
+ outb_p(0x30, WDT_INDEX_IO_PORT); /* Read Index 0x30 First */
+ cr_data = inb(WDT_DATA_IO_PORT);
+ cr_data |= 0x01; /* Set Bit0 to 1 */
+ outb_p(0x30, WDT_INDEX_IO_PORT);
+ outb_p(cr_data, WDT_DATA_IO_PORT); /* Index0x30_bit0P1 */
+
+#ifdef DEBUG
+ printk(KERN_INFO DPFX "pc87413 - Enable SWC functions\n");
+#endif
+}
+
+/* Read SWC I/O base address */
+
+static inline unsigned int pc87413_get_swc_base(void)
+{
+ unsigned int swc_base_addr = 0;
+ unsigned char addr_l, addr_h = 0;
+
+ /* Step 3: Read SWC I/O Base Address */
+
+ outb_p(0x60, WDT_INDEX_IO_PORT); /* Read Index 0x60 */
+ addr_h = inb(WDT_DATA_IO_PORT);
+
+ outb_p(0x61, WDT_INDEX_IO_PORT); /* Read Index 0x61 */
+
+ addr_l = inb(WDT_DATA_IO_PORT);
+
+ swc_base_addr = (addr_h << 8) + addr_l;
+#ifdef DEBUG
+ printk(KERN_INFO DPFX
+ "Read SWC I/O Base Address: low %d, high %d, res %d\n",
+ addr_l, addr_h, swc_base_addr);
+#endif
+ return swc_base_addr;
+}
+
+/* Select Bank 3 of SWC */
+
+static inline void pc87413_swc_bank3(unsigned int swc_base_addr)
+{
+ /* Step 4: Select Bank3 of SWC */
+ outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f);
+#ifdef DEBUG
+ printk(KERN_INFO DPFX "Select Bank3 of SWC\n");
+#endif
+}
+
+/* Set watchdog timeout to x minutes */
+
+static inline void pc87413_programm_wdto(unsigned int swc_base_addr,
+ char pc87413_time)
+{
+ /* Step 5: Programm WDTO, Twd. */
+ outb_p(pc87413_time, swc_base_addr + WDTO);
+#ifdef DEBUG
+ printk(KERN_INFO DPFX "Set WDTO to %d minutes\n", pc87413_time);
+#endif
+}
+
+/* Enable WDEN */
+
+static inline void pc87413_enable_wden(unsigned int swc_base_addr)
+{
+ /* Step 6: Enable WDEN */
+ outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL);
+#ifdef DEBUG
+ printk(KERN_INFO DPFX "Enable WDEN\n");
+#endif
+}
+
+/* Enable SW_WD_TREN */
+static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr)
+{
+ /* Enable SW_WD_TREN */
+ outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG);
+#ifdef DEBUG
+ printk(KERN_INFO DPFX "Enable SW_WD_TREN\n");
+#endif
+}
+
+/* Disable SW_WD_TREN */
+
+static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr)
+{
+ /* Disable SW_WD_TREN */
+ outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG);
+#ifdef DEBUG
+ printk(KERN_INFO DPFX "pc87413 - Disable SW_WD_TREN\n");
+#endif
+}
+
+/* Enable SW_WD_TRG */
+
+static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr)
+{
+ /* Enable SW_WD_TRG */
+ outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL);
+#ifdef DEBUG
+ printk(KERN_INFO DPFX "pc87413 - Enable SW_WD_TRG\n");
+#endif
+}
+
+/* Disable SW_WD_TRG */
+
+static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr)
+{
+ /* Disable SW_WD_TRG */
+ outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL);
+#ifdef DEBUG
+ printk(KERN_INFO DPFX "Disable SW_WD_TRG\n");
+#endif
+}
+
+/* -- Higher level functions ------------------------------------*/
+
+/* Enable the watchdog */
+
+static void pc87413_enable(void)
+{
+ unsigned int swc_base_addr;
+
+ spin_lock(&io_lock);
+
+ pc87413_select_wdt_out();
+ pc87413_enable_swc();
+ swc_base_addr = pc87413_get_swc_base();
+ pc87413_swc_bank3(swc_base_addr);
+ pc87413_programm_wdto(swc_base_addr, timeout);
+ pc87413_enable_wden(swc_base_addr);
+ pc87413_enable_sw_wd_tren(swc_base_addr);
+ pc87413_enable_sw_wd_trg(swc_base_addr);
+
+ spin_unlock(&io_lock);
+}
+
+/* Disable the watchdog */
+
+static void pc87413_disable(void)
+{
+ unsigned int swc_base_addr;
+
+ spin_lock(&io_lock);
+
+ pc87413_select_wdt_out();
+ pc87413_enable_swc();
+ swc_base_addr = pc87413_get_swc_base();
+ pc87413_swc_bank3(swc_base_addr);
+ pc87413_disable_sw_wd_tren(swc_base_addr);
+ pc87413_disable_sw_wd_trg(swc_base_addr);
+ pc87413_programm_wdto(swc_base_addr, 0);
+
+ spin_unlock(&io_lock);
+}
+
+/* Refresh the watchdog */
+
+static void pc87413_refresh(void)
+{
+ unsigned int swc_base_addr;
+
+ spin_lock(&io_lock);
+
+ pc87413_select_wdt_out();
+ pc87413_enable_swc();
+ swc_base_addr = pc87413_get_swc_base();
+ pc87413_swc_bank3(swc_base_addr);
+ pc87413_disable_sw_wd_tren(swc_base_addr);
+ pc87413_disable_sw_wd_trg(swc_base_addr);
+ pc87413_programm_wdto(swc_base_addr, timeout);
+ pc87413_enable_wden(swc_base_addr);
+ pc87413_enable_sw_wd_tren(swc_base_addr);
+ pc87413_enable_sw_wd_trg(swc_base_addr);
+
+ spin_unlock(&io_lock);
+}
+
+/* -- File operations -------------------------------------------*/
+
+/**
+ * pc87413_open:
+ * @inode: inode of device
+ * @file: file handle to device
+ *
+ */
+
+static int pc87413_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+
+ if (test_and_set_bit(0, &timer_enabled))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Reload and activate timer */
+ pc87413_refresh();
+
+ printk(KERN_INFO MODNAME
+ "Watchdog enabled. Timeout set to %d minute(s).\n", timeout);
+
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * pc87413_release:
+ * @inode: inode to board
+ * @file: file handle to board
+ *
+ * The watchdog has a configurable API. There is a religious dispute
+ * between people who want their watchdog to be able to shut down and
+ * those who want to be sure if the watchdog manager dies the machine
+ * reboots. In the former case we disable the counters, in the latter
+ * case you have to open it again very soon.
+ */
+
+static int pc87413_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer. */
+
+ if (expect_close == 42) {
+ pc87413_disable();
+ printk(KERN_INFO MODNAME
+ "Watchdog disabled, sleeping again...\n");
+ } else {
+ printk(KERN_CRIT MODNAME
+ "Unexpected close, not stopping watchdog!\n");
+ pc87413_refresh();
+ }
+ clear_bit(0, &timer_enabled);
+ expect_close = 0;
+ return 0;
+}
+
+/**
+ * pc87413_status:
+ *
+ * return, if the watchdog is enabled (timeout is set...)
+ */
+
+
+static int pc87413_status(void)
+{
+ return 0; /* currently not supported */
+}
+
+/**
+ * pc87413_write:
+ * @file: file handle to the watchdog
+ * @data: data buffer to write
+ * @len: length in bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t pc87413_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* reset expect flag */
+ expect_close = 0;
+
+ /* scan to see whether or not we got the
+ magic character */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ pc87413_refresh();
+ }
+ return len;
+}
+
+/**
+ * pc87413_ioctl:
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features. We only actually usefully support
+ * querying capabilities and current status.
+ */
+
+static long pc87413_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_timeout;
+
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "PC87413(HF/F) watchdog",
+ };
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ return put_user(pc87413_status(), uarg.i);
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+ if (get_user(options, uarg.i))
+ return -EFAULT;
+ if (options & WDIOS_DISABLECARD) {
+ pc87413_disable();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ pc87413_enable();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ pc87413_refresh();
+#ifdef DEBUG
+ printk(KERN_INFO DPFX "keepalive\n");
+#endif
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+ /* the API states this is given in secs */
+ new_timeout /= 60;
+ if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
+ return -EINVAL;
+ timeout = new_timeout;
+ pc87413_refresh();
+ /* fall through and return the new timeout... */
+ case WDIOC_GETTIMEOUT:
+ new_timeout = timeout * 60;
+ return put_user(new_timeout, uarg.i);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/* -- Notifier funtions -----------------------------------------*/
+
+/**
+ * notify_sys:
+ * @this: our notifier block
+ * @code: the event being reported
+ * @unused: unused
+ *
+ * Our notifier is called on system shutdowns. We want to turn the card
+ * off at reboot otherwise the machine will reboot again during memory
+ * test or worse yet during the following fsck. This would suck, in fact
+ * trust me - if it happens it does suck.
+ */
+
+static int pc87413_notify_sys(struct notifier_block *this,
+ unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ /* Turn the card off */
+ pc87413_disable();
+ return NOTIFY_DONE;
+}
+
+/* -- Module's structures ---------------------------------------*/
+
+static const struct file_operations pc87413_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = pc87413_write,
+ .unlocked_ioctl = pc87413_ioctl,
+ .open = pc87413_open,
+ .release = pc87413_release,
+};
+
+static struct notifier_block pc87413_notifier = {
+ .notifier_call = pc87413_notify_sys,
+};
+
+static struct miscdevice pc87413_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &pc87413_fops,
+};
+
+/* -- Module init functions -------------------------------------*/
+
+/**
+ * pc87413_init: module's "constructor"
+ *
+ * Set up the WDT watchdog board. All we have to do is grab the
+ * resources we require and bitch if anyone beat us to them.
+ * The open() function will actually kick the board off.
+ */
+
+static int __init pc87413_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n",
+ WDT_INDEX_IO_PORT);
+
+ /* request_region(io, 2, "pc87413"); */
+
+ ret = register_reboot_notifier(&pc87413_notifier);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ }
+
+ ret = misc_register(&pc87413_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ unregister_reboot_notifier(&pc87413_notifier);
+ return ret;
+ }
+ printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout);
+ pc87413_enable();
+ return 0;
+}
+
+/**
+ * pc87413_exit: module's "destructor"
+ *
+ * Unload the watchdog. You cannot do this with any file handles open.
+ * If your watchdog is set to continue ticking on close and you unload
+ * it, well it keeps ticking. We won't get the interrupt but the board
+ * will not touch PC memory so all is fine. You just have to load a new
+ * module in 60 seconds or reboot.
+ */
+
+static void __exit pc87413_exit(void)
+{
+ /* Stop the timer before we leave */
+ if (!nowayout) {
+ pc87413_disable();
+ printk(KERN_INFO MODNAME "Watchdog disabled.\n");
+ }
+
+ misc_deregister(&pc87413_miscdev);
+ unregister_reboot_notifier(&pc87413_notifier);
+ /* release_region(io, 2); */
+
+ printk(KERN_INFO MODNAME " watchdog component driver removed.\n");
+}
+
+module_init(pc87413_init);
+module_exit(pc87413_exit);
+
+MODULE_AUTHOR("Sven Anders <anders@anduras.de>, "
+ "Marcus Junker <junker@anduras.de>,");
+MODULE_DESCRIPTION("PC87413 WDT driver");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, MODNAME " I/O port (default: "
+ __MODULE_STRING(IO_DEFAULT) ").");
+
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in minutes (default="
+ __MODULE_STRING(DEFAULT_TIMEOUT) ").");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c
new file mode 100644
index 00000000..06f79226
--- /dev/null
+++ b/drivers/watchdog/pcwd.c
@@ -0,0 +1,1040 @@
+/*
+ * PC Watchdog Driver
+ * by Ken Hollis (khollis@bitgate.com)
+ *
+ * Permission granted from Simon Machell (smachell@berkprod.com)
+ * Written for the Linux Kernel, and GPLed by Ken Hollis
+ *
+ * 960107 Added request_region routines, modulized the whole thing.
+ * 960108 Fixed end-of-file pointer (Thanks to Dan Hollis), added
+ * WD_TIMEOUT define.
+ * 960216 Added eof marker on the file, and changed verbose messages.
+ * 960716 Made functional and cosmetic changes to the source for
+ * inclusion in Linux 2.0.x kernels, thanks to Alan Cox.
+ * 960717 Removed read/seek routines, replaced with ioctl. Also, added
+ * check_region command due to Alan's suggestion.
+ * 960821 Made changes to compile in newer 2.0.x kernels. Added
+ * "cold reboot sense" entry.
+ * 960825 Made a few changes to code, deleted some defines and made
+ * typedefs to replace them. Made heartbeat reset only available
+ * via ioctl, and removed the write routine.
+ * 960828 Added new items for PC Watchdog Rev.C card.
+ * 960829 Changed around all of the IOCTLs, added new features,
+ * added watchdog disable/re-enable routines. Added firmware
+ * version reporting. Added read routine for temperature.
+ * Removed some extra defines, added an autodetect Revision
+ * routine.
+ * 961006 Revised some documentation, fixed some cosmetic bugs. Made
+ * drivers to panic the system if it's overheating at bootup.
+ * 961118 Changed some verbiage on some of the output, tidied up
+ * code bits, and added compatibility to 2.1.x.
+ * 970912 Enabled board on open and disable on close.
+ * 971107 Took account of recent VFS changes (broke read).
+ * 971210 Disable board on initialisation in case board already ticking.
+ * 971222 Changed open/close for temperature handling
+ * Michael Meskes <meskes@debian.org>.
+ * 980112 Used minor numbers from include/linux/miscdevice.h
+ * 990403 Clear reset status after reading control status register in
+ * pcwd_showprevstate(). [Marc Boucher <marc@mbsi.ca>]
+ * 990605 Made changes to code to support Firmware 1.22a, added
+ * fairly useless proc entry.
+ * 990610 removed said useless proc code for the merge <alan>
+ * 000403 Removed last traces of proc code. <davej>
+ * 011214 Added nowayout module option to override
+ * CONFIG_WATCHDOG_NOWAYOUT <Matt_Domsch@dell.com>
+ * Added timeout module option to override default
+ */
+
+/*
+ * A bells and whistles driver is available from http://www.pcwd.de/
+ * More info available at http://www.berkprod.com/ or
+ * http://www.pcwatchdog.com/
+ */
+
+#include <linux/module.h> /* For module specific items */
+#include <linux/moduleparam.h> /* For new moduleparam's */
+#include <linux/types.h> /* For standard types (like size_t) */
+#include <linux/errno.h> /* For the -ENODEV/... values */
+#include <linux/kernel.h> /* For printk/panic/... */
+#include <linux/delay.h> /* For mdelay function */
+#include <linux/timer.h> /* For timer related operations */
+#include <linux/jiffies.h> /* For jiffies stuff */
+#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/watchdog.h> /* For the watchdog specific items */
+#include <linux/reboot.h> /* For kernel_power_off() */
+#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/fs.h> /* For file operations */
+#include <linux/isa.h> /* For isa devices */
+#include <linux/ioport.h> /* For io-port access */
+#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
+#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+#include <linux/io.h> /* For inb/outb/... */
+
+/* Module and version information */
+#define WATCHDOG_VERSION "1.20"
+#define WATCHDOG_DATE "18 Feb 2007"
+#define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
+#define WATCHDOG_NAME "pcwd"
+#define PFX WATCHDOG_NAME ": "
+#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION "\n"
+
+/*
+ * It should be noted that PCWD_REVISION_B was removed because A and B
+ * are essentially the same types of card, with the exception that B
+ * has temperature reporting. Since I didn't receive a Rev.B card,
+ * the Rev.B card is not supported. (It's a good thing too, as they
+ * are no longer in production.)
+ */
+#define PCWD_REVISION_A 1
+#define PCWD_REVISION_C 2
+
+/*
+ * These are the auto-probe addresses available.
+ *
+ * Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350.
+ * Revision A has an address range of 2 addresses, while Revision C has 4.
+ */
+#define PCWD_ISA_NR_CARDS 3
+static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
+
+/*
+ * These are the defines that describe the control status bits for the
+ * PCI-PC Watchdog card.
+*/
+/* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */
+#define WD_WDRST 0x01 /* Previously reset state */
+#define WD_T110 0x02 /* Temperature overheat sense */
+#define WD_HRTBT 0x04 /* Heartbeat sense */
+#define WD_RLY2 0x08 /* External relay triggered */
+#define WD_SRLY2 0x80 /* Software external relay triggered */
+/* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */
+#define WD_REVC_WTRP 0x01 /* Watchdog Trip status */
+#define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */
+#define WD_REVC_TTRP 0x04 /* Temperature Trip status */
+#define WD_REVC_RL2A 0x08 /* Relay 2 activated by
+ on-board processor */
+#define WD_REVC_RL1A 0x10 /* Relay 1 active */
+#define WD_REVC_R2DS 0x40 /* Relay 2 disable */
+#define WD_REVC_RLY2 0x80 /* Relay 2 activated? */
+/* Port 2 : Control Status #2 */
+#define WD_WDIS 0x10 /* Watchdog Disabled */
+#define WD_ENTP 0x20 /* Watchdog Enable Temperature Trip */
+#define WD_SSEL 0x40 /* Watchdog Switch Select
+ (1:SW1 <-> 0:SW2) */
+#define WD_WCMD 0x80 /* Watchdog Command Mode */
+
+/* max. time we give an ISA watchdog card to process a command */
+/* 500ms for each 4 bit response (according to spec.) */
+#define ISA_COMMAND_TIMEOUT 1000
+
+/* Watchdog's internal commands */
+#define CMD_ISA_IDLE 0x00
+#define CMD_ISA_VERSION_INTEGER 0x01
+#define CMD_ISA_VERSION_TENTH 0x02
+#define CMD_ISA_VERSION_HUNDRETH 0x03
+#define CMD_ISA_VERSION_MINOR 0x04
+#define CMD_ISA_SWITCH_SETTINGS 0x05
+#define CMD_ISA_RESET_PC 0x06
+#define CMD_ISA_ARM_0 0x07
+#define CMD_ISA_ARM_30 0x08
+#define CMD_ISA_ARM_60 0x09
+#define CMD_ISA_DELAY_TIME_2SECS 0x0A
+#define CMD_ISA_DELAY_TIME_4SECS 0x0B
+#define CMD_ISA_DELAY_TIME_8SECS 0x0C
+#define CMD_ISA_RESET_RELAYS 0x0D
+
+/* Watchdog's Dip Switch heartbeat values */
+static const int heartbeat_tbl[] = {
+ 20, /* OFF-OFF-OFF = 20 Sec */
+ 40, /* OFF-OFF-ON = 40 Sec */
+ 60, /* OFF-ON-OFF = 1 Min */
+ 300, /* OFF-ON-ON = 5 Min */
+ 600, /* ON-OFF-OFF = 10 Min */
+ 1800, /* ON-OFF-ON = 30 Min */
+ 3600, /* ON-ON-OFF = 1 Hour */
+ 7200, /* ON-ON-ON = 2 hour */
+};
+
+/*
+ * We are using an kernel timer to do the pinging of the watchdog
+ * every ~500ms. We try to set the internal heartbeat of the
+ * watchdog to 2 ms.
+ */
+
+#define WDT_INTERVAL (HZ/2+1)
+
+/* We can only use 1 card due to the /dev/watchdog restriction */
+static int cards_found;
+
+/* internal variables */
+static unsigned long open_allowed;
+static char expect_close;
+static int temp_panic;
+
+/* this is private data for each ISA-PC watchdog card */
+static struct {
+ char fw_ver_str[6]; /* The cards firmware version */
+ int revision; /* The card's revision */
+ int supports_temp; /* Whether or not the card has
+ a temperature device */
+ int command_mode; /* Whether or not the card is in
+ command mode */
+ int boot_status; /* The card's boot status */
+ int io_addr; /* The cards I/O address */
+ spinlock_t io_lock; /* the lock for io operations */
+ struct timer_list timer; /* The timer that pings the watchdog */
+ unsigned long next_heartbeat; /* the next_heartbeat for the timer */
+} pcwd_private;
+
+/* module parameters */
+#define QUIET 0 /* Default */
+#define VERBOSE 1 /* Verbose */
+#define DEBUG 2 /* print fancy stuff too */
+static int debug = QUIET;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug,
+ "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)");
+
+/* default heartbeat = delay-time from dip-switches */
+#define WATCHDOG_HEARTBEAT 0
+static int heartbeat = WATCHDOG_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
+ "(2 <= heartbeat <= 7200 or 0=delay-time from dip-switches, default="
+ __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Internal functions
+ */
+
+static int send_isa_command(int cmd)
+{
+ int i;
+ int control_status;
+ int port0, last_port0; /* Double read for stabilising */
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "sending following data cmd=0x%02x\n",
+ cmd);
+
+ /* The WCMD bit must be 1 and the command is only 4 bits in size */
+ control_status = (cmd & 0x0F) | WD_WCMD;
+ outb_p(control_status, pcwd_private.io_addr + 2);
+ udelay(ISA_COMMAND_TIMEOUT);
+
+ port0 = inb_p(pcwd_private.io_addr);
+ for (i = 0; i < 25; ++i) {
+ last_port0 = port0;
+ port0 = inb_p(pcwd_private.io_addr);
+
+ if (port0 == last_port0)
+ break; /* Data is stable */
+
+ udelay(250);
+ }
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "received following data for "
+ "cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n",
+ cmd, port0, last_port0);
+
+ return port0;
+}
+
+static int set_command_mode(void)
+{
+ int i, found = 0, count = 0;
+
+ /* Set the card into command mode */
+ spin_lock(&pcwd_private.io_lock);
+ while ((!found) && (count < 3)) {
+ i = send_isa_command(CMD_ISA_IDLE);
+
+ if (i == 0x00)
+ found = 1;
+ else if (i == 0xF3) {
+ /* Card does not like what we've done to it */
+ outb_p(0x00, pcwd_private.io_addr + 2);
+ udelay(1200); /* Spec says wait 1ms */
+ outb_p(0x00, pcwd_private.io_addr + 2);
+ udelay(ISA_COMMAND_TIMEOUT);
+ }
+ count++;
+ }
+ spin_unlock(&pcwd_private.io_lock);
+ pcwd_private.command_mode = found;
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "command_mode=%d\n",
+ pcwd_private.command_mode);
+
+ return found;
+}
+
+static void unset_command_mode(void)
+{
+ /* Set the card into normal mode */
+ spin_lock(&pcwd_private.io_lock);
+ outb_p(0x00, pcwd_private.io_addr + 2);
+ udelay(ISA_COMMAND_TIMEOUT);
+ spin_unlock(&pcwd_private.io_lock);
+
+ pcwd_private.command_mode = 0;
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "command_mode=%d\n",
+ pcwd_private.command_mode);
+}
+
+static inline void pcwd_check_temperature_support(void)
+{
+ if (inb(pcwd_private.io_addr) != 0xF0)
+ pcwd_private.supports_temp = 1;
+}
+
+static inline void pcwd_get_firmware(void)
+{
+ int one, ten, hund, minor;
+
+ strcpy(pcwd_private.fw_ver_str, "ERROR");
+
+ if (set_command_mode()) {
+ one = send_isa_command(CMD_ISA_VERSION_INTEGER);
+ ten = send_isa_command(CMD_ISA_VERSION_TENTH);
+ hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH);
+ minor = send_isa_command(CMD_ISA_VERSION_MINOR);
+ sprintf(pcwd_private.fw_ver_str, "%c.%c%c%c",
+ one, ten, hund, minor);
+ }
+ unset_command_mode();
+
+ return;
+}
+
+static inline int pcwd_get_option_switches(void)
+{
+ int option_switches = 0;
+
+ if (set_command_mode()) {
+ /* Get switch settings */
+ option_switches = send_isa_command(CMD_ISA_SWITCH_SETTINGS);
+ }
+
+ unset_command_mode();
+ return option_switches;
+}
+
+static void pcwd_show_card_info(void)
+{
+ int option_switches;
+
+ /* Get some extra info from the hardware (in command/debug/diag mode) */
+ if (pcwd_private.revision == PCWD_REVISION_A)
+ printk(KERN_INFO PFX
+ "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n",
+ pcwd_private.io_addr);
+ else if (pcwd_private.revision == PCWD_REVISION_C) {
+ pcwd_get_firmware();
+ printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port "
+ "0x%04x (Firmware version: %s)\n",
+ pcwd_private.io_addr, pcwd_private.fw_ver_str);
+ option_switches = pcwd_get_option_switches();
+ printk(KERN_INFO PFX "Option switches (0x%02x): "
+ "Temperature Reset Enable=%s, Power On Delay=%s\n",
+ option_switches,
+ ((option_switches & 0x10) ? "ON" : "OFF"),
+ ((option_switches & 0x08) ? "ON" : "OFF"));
+
+ /* Reprogram internal heartbeat to 2 seconds */
+ if (set_command_mode()) {
+ send_isa_command(CMD_ISA_DELAY_TIME_2SECS);
+ unset_command_mode();
+ }
+ }
+
+ if (pcwd_private.supports_temp)
+ printk(KERN_INFO PFX "Temperature Option Detected\n");
+
+ if (pcwd_private.boot_status & WDIOF_CARDRESET)
+ printk(KERN_INFO PFX
+ "Previous reboot was caused by the card\n");
+
+ if (pcwd_private.boot_status & WDIOF_OVERHEAT) {
+ printk(KERN_EMERG PFX
+ "Card senses a CPU Overheat. Panicking!\n");
+ printk(KERN_EMERG PFX
+ "CPU Overheat\n");
+ }
+
+ if (pcwd_private.boot_status == 0)
+ printk(KERN_INFO PFX
+ "No previous trip detected - Cold boot or reset\n");
+}
+
+static void pcwd_timer_ping(unsigned long data)
+{
+ int wdrst_stat;
+
+ /* If we got a heartbeat pulse within the WDT_INTERVAL
+ * we agree to ping the WDT */
+ if (time_before(jiffies, pcwd_private.next_heartbeat)) {
+ /* Ping the watchdog */
+ spin_lock(&pcwd_private.io_lock);
+ if (pcwd_private.revision == PCWD_REVISION_A) {
+ /* Rev A cards are reset by setting the
+ WD_WDRST bit in register 1 */
+ wdrst_stat = inb_p(pcwd_private.io_addr);
+ wdrst_stat &= 0x0F;
+ wdrst_stat |= WD_WDRST;
+
+ outb_p(wdrst_stat, pcwd_private.io_addr + 1);
+ } else {
+ /* Re-trigger watchdog by writing to port 0 */
+ outb_p(0x00, pcwd_private.io_addr);
+ }
+
+ /* Re-set the timer interval */
+ mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
+
+ spin_unlock(&pcwd_private.io_lock);
+ } else {
+ printk(KERN_WARNING PFX
+ "Heartbeat lost! Will not ping the watchdog\n");
+ }
+}
+
+static int pcwd_start(void)
+{
+ int stat_reg;
+
+ pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
+
+ /* Start the timer */
+ mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
+
+ /* Enable the port */
+ if (pcwd_private.revision == PCWD_REVISION_C) {
+ spin_lock(&pcwd_private.io_lock);
+ outb_p(0x00, pcwd_private.io_addr + 3);
+ udelay(ISA_COMMAND_TIMEOUT);
+ stat_reg = inb_p(pcwd_private.io_addr + 2);
+ spin_unlock(&pcwd_private.io_lock);
+ if (stat_reg & WD_WDIS) {
+ printk(KERN_INFO PFX "Could not start watchdog\n");
+ return -EIO;
+ }
+ }
+
+ if (debug >= VERBOSE)
+ printk(KERN_DEBUG PFX "Watchdog started\n");
+
+ return 0;
+}
+
+static int pcwd_stop(void)
+{
+ int stat_reg;
+
+ /* Stop the timer */
+ del_timer(&pcwd_private.timer);
+
+ /* Disable the board */
+ if (pcwd_private.revision == PCWD_REVISION_C) {
+ spin_lock(&pcwd_private.io_lock);
+ outb_p(0xA5, pcwd_private.io_addr + 3);
+ udelay(ISA_COMMAND_TIMEOUT);
+ outb_p(0xA5, pcwd_private.io_addr + 3);
+ udelay(ISA_COMMAND_TIMEOUT);
+ stat_reg = inb_p(pcwd_private.io_addr + 2);
+ spin_unlock(&pcwd_private.io_lock);
+ if ((stat_reg & WD_WDIS) == 0) {
+ printk(KERN_INFO PFX "Could not stop watchdog\n");
+ return -EIO;
+ }
+ }
+
+ if (debug >= VERBOSE)
+ printk(KERN_DEBUG PFX "Watchdog stopped\n");
+
+ return 0;
+}
+
+static int pcwd_keepalive(void)
+{
+ /* user land ping */
+ pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n");
+
+ return 0;
+}
+
+static int pcwd_set_heartbeat(int t)
+{
+ if (t < 2 || t > 7200) /* arbitrary upper limit */
+ return -EINVAL;
+
+ heartbeat = t;
+
+ if (debug >= VERBOSE)
+ printk(KERN_DEBUG PFX "New heartbeat: %d\n",
+ heartbeat);
+
+ return 0;
+}
+
+static int pcwd_get_status(int *status)
+{
+ int control_status;
+
+ *status = 0;
+ spin_lock(&pcwd_private.io_lock);
+ if (pcwd_private.revision == PCWD_REVISION_A)
+ /* Rev A cards return status information from
+ * the base register, which is used for the
+ * temperature in other cards. */
+ control_status = inb(pcwd_private.io_addr);
+ else {
+ /* Rev C cards return card status in the base
+ * address + 1 register. And use different bits
+ * to indicate a card initiated reset, and an
+ * over-temperature condition. And the reboot
+ * status can be reset. */
+ control_status = inb(pcwd_private.io_addr + 1);
+ }
+ spin_unlock(&pcwd_private.io_lock);
+
+ if (pcwd_private.revision == PCWD_REVISION_A) {
+ if (control_status & WD_WDRST)
+ *status |= WDIOF_CARDRESET;
+
+ if (control_status & WD_T110) {
+ *status |= WDIOF_OVERHEAT;
+ if (temp_panic) {
+ printk(KERN_INFO PFX
+ "Temperature overheat trip!\n");
+ kernel_power_off();
+ }
+ }
+ } else {
+ if (control_status & WD_REVC_WTRP)
+ *status |= WDIOF_CARDRESET;
+
+ if (control_status & WD_REVC_TTRP) {
+ *status |= WDIOF_OVERHEAT;
+ if (temp_panic) {
+ printk(KERN_INFO PFX
+ "Temperature overheat trip!\n");
+ kernel_power_off();
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int pcwd_clear_status(void)
+{
+ int control_status;
+
+ if (pcwd_private.revision == PCWD_REVISION_C) {
+ spin_lock(&pcwd_private.io_lock);
+
+ if (debug >= VERBOSE)
+ printk(KERN_INFO PFX
+ "clearing watchdog trip status\n");
+
+ control_status = inb_p(pcwd_private.io_addr + 1);
+
+ if (debug >= DEBUG) {
+ printk(KERN_DEBUG PFX "status was: 0x%02x\n",
+ control_status);
+ printk(KERN_DEBUG PFX "sending: 0x%02x\n",
+ (control_status & WD_REVC_R2DS));
+ }
+
+ /* clear reset status & Keep Relay 2 disable state as it is */
+ outb_p((control_status & WD_REVC_R2DS),
+ pcwd_private.io_addr + 1);
+
+ spin_unlock(&pcwd_private.io_lock);
+ }
+ return 0;
+}
+
+static int pcwd_get_temperature(int *temperature)
+{
+ /* check that port 0 gives temperature info and no command results */
+ if (pcwd_private.command_mode)
+ return -1;
+
+ *temperature = 0;
+ if (!pcwd_private.supports_temp)
+ return -ENODEV;
+
+ /*
+ * Convert celsius to fahrenheit, since this was
+ * the decided 'standard' for this return value.
+ */
+ spin_lock(&pcwd_private.io_lock);
+ *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32;
+ spin_unlock(&pcwd_private.io_lock);
+
+ if (debug >= DEBUG) {
+ printk(KERN_DEBUG PFX "temperature is: %d F\n",
+ *temperature);
+ }
+
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static long pcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rv;
+ int status;
+ int temperature;
+ int new_heartbeat;
+ int __user *argp = (int __user *)arg;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_OVERHEAT |
+ WDIOF_CARDRESET |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "PCWD",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ return 0;
+
+ case WDIOC_GETSTATUS:
+ pcwd_get_status(&status);
+ return put_user(status, argp);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(pcwd_private.boot_status, argp);
+
+ case WDIOC_GETTEMP:
+ if (pcwd_get_temperature(&temperature))
+ return -EFAULT;
+
+ return put_user(temperature, argp);
+
+ case WDIOC_SETOPTIONS:
+ if (pcwd_private.revision == PCWD_REVISION_C) {
+ if (get_user(rv, argp))
+ return -EFAULT;
+
+ if (rv & WDIOS_DISABLECARD) {
+ status = pcwd_stop();
+ if (status < 0)
+ return status;
+ }
+ if (rv & WDIOS_ENABLECARD) {
+ status = pcwd_start();
+ if (status < 0)
+ return status;
+ }
+ if (rv & WDIOS_TEMPPANIC)
+ temp_panic = 1;
+ }
+ return -EINVAL;
+
+ case WDIOC_KEEPALIVE:
+ pcwd_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_heartbeat, argp))
+ return -EFAULT;
+
+ if (pcwd_set_heartbeat(new_heartbeat))
+ return -EINVAL;
+
+ pcwd_keepalive();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, argp);
+
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static ssize_t pcwd_write(struct file *file, const char __user *buf, size_t len,
+ loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ pcwd_keepalive();
+ }
+ return len;
+}
+
+static int pcwd_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &open_allowed))
+ return -EBUSY;
+ if (nowayout)
+ __module_get(THIS_MODULE);
+ /* Activate */
+ pcwd_start();
+ pcwd_keepalive();
+ return nonseekable_open(inode, file);
+}
+
+static int pcwd_close(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ pcwd_stop();
+ else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ pcwd_keepalive();
+ }
+ expect_close = 0;
+ clear_bit(0, &open_allowed);
+ return 0;
+}
+
+/*
+ * /dev/temperature handling
+ */
+
+static ssize_t pcwd_temp_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int temperature;
+
+ if (pcwd_get_temperature(&temperature))
+ return -EFAULT;
+
+ if (copy_to_user(buf, &temperature, 1))
+ return -EFAULT;
+
+ return 1;
+}
+
+static int pcwd_temp_open(struct inode *inode, struct file *file)
+{
+ if (!pcwd_private.supports_temp)
+ return -ENODEV;
+
+ return nonseekable_open(inode, file);
+}
+
+static int pcwd_temp_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations pcwd_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = pcwd_write,
+ .unlocked_ioctl = pcwd_ioctl,
+ .open = pcwd_open,
+ .release = pcwd_close,
+};
+
+static struct miscdevice pcwd_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &pcwd_fops,
+};
+
+static const struct file_operations pcwd_temp_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = pcwd_temp_read,
+ .open = pcwd_temp_open,
+ .release = pcwd_temp_close,
+};
+
+static struct miscdevice temp_miscdev = {
+ .minor = TEMP_MINOR,
+ .name = "temperature",
+ .fops = &pcwd_temp_fops,
+};
+
+/*
+ * Init & exit routines
+ */
+
+static inline int get_revision(void)
+{
+ int r = PCWD_REVISION_C;
+
+ spin_lock(&pcwd_private.io_lock);
+ /* REV A cards use only 2 io ports; test
+ * presumes a floating bus reads as 0xff. */
+ if ((inb(pcwd_private.io_addr + 2) == 0xFF) ||
+ (inb(pcwd_private.io_addr + 3) == 0xFF))
+ r = PCWD_REVISION_A;
+ spin_unlock(&pcwd_private.io_lock);
+
+ return r;
+}
+
+/*
+ * The ISA cards have a heartbeat bit in one of the registers, which
+ * register is card dependent. The heartbeat bit is monitored, and if
+ * found, is considered proof that a Berkshire card has been found.
+ * The initial rate is once per second at board start up, then twice
+ * per second for normal operation.
+ */
+static int __devinit pcwd_isa_match(struct device *dev, unsigned int id)
+{
+ int base_addr = pcwd_ioports[id];
+ int port0, last_port0; /* Reg 0, in case it's REV A */
+ int port1, last_port1; /* Register 1 for REV C cards */
+ int i;
+ int retval;
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "pcwd_isa_match id=%d\n",
+ id);
+
+ if (!request_region(base_addr, 4, "PCWD")) {
+ printk(KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr);
+ return 0;
+ }
+
+ retval = 0;
+
+ port0 = inb_p(base_addr); /* For REV A boards */
+ port1 = inb_p(base_addr + 1); /* For REV C boards */
+ if (port0 != 0xff || port1 != 0xff) {
+ /* Not an 'ff' from a floating bus, so must be a card! */
+ for (i = 0; i < 4; ++i) {
+
+ msleep(500);
+
+ last_port0 = port0;
+ last_port1 = port1;
+
+ port0 = inb_p(base_addr);
+ port1 = inb_p(base_addr + 1);
+
+ /* Has either hearbeat bit changed? */
+ if ((port0 ^ last_port0) & WD_HRTBT ||
+ (port1 ^ last_port1) & WD_REVC_HRBT) {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ release_region(base_addr, 4);
+
+ return retval;
+}
+
+static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id)
+{
+ int ret;
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "pcwd_isa_probe id=%d\n",
+ id);
+
+ cards_found++;
+ if (cards_found == 1)
+ printk(KERN_INFO PFX "v%s Ken Hollis (kenji@bitgate.com)\n",
+ WATCHDOG_VERSION);
+
+ if (cards_found > 1) {
+ printk(KERN_ERR PFX "This driver only supports 1 device\n");
+ return -ENODEV;
+ }
+
+ if (pcwd_ioports[id] == 0x0000) {
+ printk(KERN_ERR PFX "No I/O-Address for card detected\n");
+ return -ENODEV;
+ }
+ pcwd_private.io_addr = pcwd_ioports[id];
+
+ spin_lock_init(&pcwd_private.io_lock);
+
+ /* Check card's revision */
+ pcwd_private.revision = get_revision();
+
+ if (!request_region(pcwd_private.io_addr,
+ (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ pcwd_private.io_addr);
+ ret = -EIO;
+ goto error_request_region;
+ }
+
+ /* Initial variables */
+ pcwd_private.supports_temp = 0;
+ temp_panic = 0;
+ pcwd_private.boot_status = 0x0000;
+
+ /* get the boot_status */
+ pcwd_get_status(&pcwd_private.boot_status);
+
+ /* clear the "card caused reboot" flag */
+ pcwd_clear_status();
+
+ setup_timer(&pcwd_private.timer, pcwd_timer_ping, 0);
+
+ /* Disable the board */
+ pcwd_stop();
+
+ /* Check whether or not the card supports the temperature device */
+ pcwd_check_temperature_support();
+
+ /* Show info about the card itself */
+ pcwd_show_card_info();
+
+ /* If heartbeat = 0 then we use the heartbeat from the dip-switches */
+ if (heartbeat == 0)
+ heartbeat = heartbeat_tbl[(pcwd_get_option_switches() & 0x07)];
+
+ /* Check that the heartbeat value is within it's range;
+ if not reset to the default */
+ if (pcwd_set_heartbeat(heartbeat)) {
+ pcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
+ printk(KERN_INFO PFX
+ "heartbeat value must be 2 <= heartbeat <= 7200, using %d\n",
+ WATCHDOG_HEARTBEAT);
+ }
+
+ if (pcwd_private.supports_temp) {
+ ret = misc_register(&temp_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ TEMP_MINOR, ret);
+ goto error_misc_register_temp;
+ }
+ }
+
+ ret = misc_register(&pcwd_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto error_misc_register_watchdog;
+ }
+
+ printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ heartbeat, nowayout);
+
+ return 0;
+
+error_misc_register_watchdog:
+ if (pcwd_private.supports_temp)
+ misc_deregister(&temp_miscdev);
+error_misc_register_temp:
+ release_region(pcwd_private.io_addr,
+ (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
+error_request_region:
+ pcwd_private.io_addr = 0x0000;
+ cards_found--;
+ return ret;
+}
+
+static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id)
+{
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "pcwd_isa_remove id=%d\n",
+ id);
+
+ if (!pcwd_private.io_addr)
+ return 1;
+
+ /* Disable the board */
+ if (!nowayout)
+ pcwd_stop();
+
+ /* Deregister */
+ misc_deregister(&pcwd_miscdev);
+ if (pcwd_private.supports_temp)
+ misc_deregister(&temp_miscdev);
+ release_region(pcwd_private.io_addr,
+ (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
+ pcwd_private.io_addr = 0x0000;
+ cards_found--;
+
+ return 0;
+}
+
+static void pcwd_isa_shutdown(struct device *dev, unsigned int id)
+{
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "pcwd_isa_shutdown id=%d\n",
+ id);
+
+ pcwd_stop();
+}
+
+static struct isa_driver pcwd_isa_driver = {
+ .match = pcwd_isa_match,
+ .probe = pcwd_isa_probe,
+ .remove = __devexit_p(pcwd_isa_remove),
+ .shutdown = pcwd_isa_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = WATCHDOG_NAME,
+ },
+};
+
+static int __init pcwd_init_module(void)
+{
+ return isa_register_driver(&pcwd_isa_driver, PCWD_ISA_NR_CARDS);
+}
+
+static void __exit pcwd_cleanup_module(void)
+{
+ isa_unregister_driver(&pcwd_isa_driver);
+ printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+}
+
+module_init(pcwd_init_module);
+module_exit(pcwd_cleanup_module);
+
+MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, "
+ "Wim Van Sebroeck <wim@iguana.be>");
+MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
+MODULE_VERSION(WATCHDOG_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS_MISCDEV(TEMP_MINOR);
diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c
new file mode 100644
index 00000000..b8d14f88
--- /dev/null
+++ b/drivers/watchdog/pcwd_pci.c
@@ -0,0 +1,855 @@
+/*
+ * Berkshire PCI-PC Watchdog Card Driver
+ *
+ * (c) Copyright 2003-2007 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ * Based on source code of the following authors:
+ * Ken Hollis <kenji@bitgate.com>,
+ * Lindsay Harris <lindsay@bluegum.com>,
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * Matt Domsch <Matt_Domsch@dell.com>,
+ * Rob Radez <rob@osinvestor.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
+ * provide warranty for any of this software. This material is
+ * provided "AS-IS" and at no charge.
+ */
+
+/*
+ * A bells and whistles driver is available from:
+ * http://www.kernel.org/pub/linux/kernel/people/wim/pcwd/pcwd_pci/
+ *
+ * More info available at
+ * http://www.berkprod.com/ or http://www.pcwatchdog.com/
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+#include <linux/module.h> /* For module specific items */
+#include <linux/moduleparam.h> /* For new moduleparam's */
+#include <linux/types.h> /* For standard types (like size_t) */
+#include <linux/errno.h> /* For the -ENODEV/... values */
+#include <linux/kernel.h> /* For printk/panic/... */
+#include <linux/delay.h> /* For mdelay function */
+#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/watchdog.h> /* For the watchdog specific items */
+#include <linux/notifier.h> /* For notifier support */
+#include <linux/reboot.h> /* For reboot_notifier stuff */
+#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/fs.h> /* For file operations */
+#include <linux/pci.h> /* For pci functions */
+#include <linux/ioport.h> /* For io-port access */
+#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
+#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+#include <linux/io.h> /* For inb/outb/... */
+
+/* Module and version information */
+#define WATCHDOG_VERSION "1.03"
+#define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog"
+#define WATCHDOG_NAME "pcwd_pci"
+#define PFX WATCHDOG_NAME ": "
+#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION "\n"
+
+/* Stuff for the PCI ID's */
+#ifndef PCI_VENDOR_ID_QUICKLOGIC
+#define PCI_VENDOR_ID_QUICKLOGIC 0x11e3
+#endif
+
+#ifndef PCI_DEVICE_ID_WATCHDOG_PCIPCWD
+#define PCI_DEVICE_ID_WATCHDOG_PCIPCWD 0x5030
+#endif
+
+/*
+ * These are the defines that describe the control status bits for the
+ * PCI-PC Watchdog card.
+ */
+/* Port 1 : Control Status #1 */
+#define WD_PCI_WTRP 0x01 /* Watchdog Trip status */
+#define WD_PCI_HRBT 0x02 /* Watchdog Heartbeat */
+#define WD_PCI_TTRP 0x04 /* Temperature Trip status */
+#define WD_PCI_RL2A 0x08 /* Relay 2 Active */
+#define WD_PCI_RL1A 0x10 /* Relay 1 Active */
+#define WD_PCI_R2DS 0x40 /* Relay 2 Disable Temperature-trip /
+ reset */
+#define WD_PCI_RLY2 0x80 /* Activate Relay 2 on the board */
+/* Port 2 : Control Status #2 */
+#define WD_PCI_WDIS 0x10 /* Watchdog Disable */
+#define WD_PCI_ENTP 0x20 /* Enable Temperature Trip Reset */
+#define WD_PCI_WRSP 0x40 /* Watchdog wrote response */
+#define WD_PCI_PCMD 0x80 /* PC has sent command */
+
+/* according to documentation max. time to process a command for the pci
+ * watchdog card is 100 ms, so we give it 150 ms to do it's job */
+#define PCI_COMMAND_TIMEOUT 150
+
+/* Watchdog's internal commands */
+#define CMD_GET_STATUS 0x04
+#define CMD_GET_FIRMWARE_VERSION 0x08
+#define CMD_READ_WATCHDOG_TIMEOUT 0x18
+#define CMD_WRITE_WATCHDOG_TIMEOUT 0x19
+#define CMD_GET_CLEAR_RESET_COUNT 0x84
+
+/* Watchdog's Dip Switch heartbeat values */
+static const int heartbeat_tbl[] = {
+ 5, /* OFF-OFF-OFF = 5 Sec */
+ 10, /* OFF-OFF-ON = 10 Sec */
+ 30, /* OFF-ON-OFF = 30 Sec */
+ 60, /* OFF-ON-ON = 1 Min */
+ 300, /* ON-OFF-OFF = 5 Min */
+ 600, /* ON-OFF-ON = 10 Min */
+ 1800, /* ON-ON-OFF = 30 Min */
+ 3600, /* ON-ON-ON = 1 hour */
+};
+
+/* We can only use 1 card due to the /dev/watchdog restriction */
+static int cards_found;
+
+/* internal variables */
+static int temp_panic;
+static unsigned long is_active;
+static char expect_release;
+/* this is private data for each PCI-PC watchdog card */
+static struct {
+ /* Wether or not the card has a temperature device */
+ int supports_temp;
+ /* The card's boot status */
+ int boot_status;
+ /* The cards I/O address */
+ unsigned long io_addr;
+ /* the lock for io operations */
+ spinlock_t io_lock;
+ /* the PCI-device */
+ struct pci_dev *pdev;
+} pcipcwd_private;
+
+/* module parameters */
+#define QUIET 0 /* Default */
+#define VERBOSE 1 /* Verbose */
+#define DEBUG 2 /* print fancy stuff too */
+static int debug = QUIET;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)");
+
+#define WATCHDOG_HEARTBEAT 0 /* default heartbeat =
+ delay-time from dip-switches */
+static int heartbeat = WATCHDOG_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
+ "(0<heartbeat<65536 or 0=delay-time from dip-switches, default="
+ __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Internal functions
+ */
+
+static int send_command(int cmd, int *msb, int *lsb)
+{
+ int got_response, count;
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "sending following data "
+ "cmd=0x%02x msb=0x%02x lsb=0x%02x\n", cmd, *msb, *lsb);
+
+ spin_lock(&pcipcwd_private.io_lock);
+ /* If a command requires data it should be written first.
+ * Data for commands with 8 bits of data should be written to port 4.
+ * Commands with 16 bits of data, should be written as LSB to port 4
+ * and MSB to port 5.
+ * After the required data has been written then write the command to
+ * port 6. */
+ outb_p(*lsb, pcipcwd_private.io_addr + 4);
+ outb_p(*msb, pcipcwd_private.io_addr + 5);
+ outb_p(cmd, pcipcwd_private.io_addr + 6);
+
+ /* wait till the pci card processed the command, signaled by
+ * the WRSP bit in port 2 and give it a max. timeout of
+ * PCI_COMMAND_TIMEOUT to process */
+ got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP;
+ for (count = 0; (count < PCI_COMMAND_TIMEOUT) && (!got_response);
+ count++) {
+ mdelay(1);
+ got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP;
+ }
+
+ if (debug >= DEBUG) {
+ if (got_response) {
+ printk(KERN_DEBUG PFX
+ "time to process command was: %d ms\n",
+ count);
+ } else {
+ printk(KERN_DEBUG PFX
+ "card did not respond on command!\n");
+ }
+ }
+
+ if (got_response) {
+ /* read back response */
+ *lsb = inb_p(pcipcwd_private.io_addr + 4);
+ *msb = inb_p(pcipcwd_private.io_addr + 5);
+
+ /* clear WRSP bit */
+ inb_p(pcipcwd_private.io_addr + 6);
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "received following data for "
+ "cmd=0x%02x: msb=0x%02x lsb=0x%02x\n",
+ cmd, *msb, *lsb);
+ }
+
+ spin_unlock(&pcipcwd_private.io_lock);
+
+ return got_response;
+}
+
+static inline void pcipcwd_check_temperature_support(void)
+{
+ if (inb_p(pcipcwd_private.io_addr) != 0xF0)
+ pcipcwd_private.supports_temp = 1;
+}
+
+static int pcipcwd_get_option_switches(void)
+{
+ int option_switches;
+
+ option_switches = inb_p(pcipcwd_private.io_addr + 3);
+ return option_switches;
+}
+
+static void pcipcwd_show_card_info(void)
+{
+ int got_fw_rev, fw_rev_major, fw_rev_minor;
+ char fw_ver_str[20]; /* The cards firmware version */
+ int option_switches;
+
+ got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major,
+ &fw_rev_minor);
+ if (got_fw_rev)
+ sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor);
+ else
+ sprintf(fw_ver_str, "<card no answer>");
+
+ /* Get switch settings */
+ option_switches = pcipcwd_get_option_switches();
+
+ printk(KERN_INFO PFX "Found card at port "
+ "0x%04x (Firmware: %s) %s temp option\n",
+ (int) pcipcwd_private.io_addr, fw_ver_str,
+ (pcipcwd_private.supports_temp ? "with" : "without"));
+
+ printk(KERN_INFO PFX "Option switches (0x%02x): "
+ "Temperature Reset Enable=%s, Power On Delay=%s\n",
+ option_switches,
+ ((option_switches & 0x10) ? "ON" : "OFF"),
+ ((option_switches & 0x08) ? "ON" : "OFF"));
+
+ if (pcipcwd_private.boot_status & WDIOF_CARDRESET)
+ printk(KERN_INFO PFX
+ "Previous reset was caused by the Watchdog card\n");
+
+ if (pcipcwd_private.boot_status & WDIOF_OVERHEAT)
+ printk(KERN_INFO PFX "Card sensed a CPU Overheat\n");
+
+ if (pcipcwd_private.boot_status == 0)
+ printk(KERN_INFO PFX
+ "No previous trip detected - Cold boot or reset\n");
+}
+
+static int pcipcwd_start(void)
+{
+ int stat_reg;
+
+ spin_lock(&pcipcwd_private.io_lock);
+ outb_p(0x00, pcipcwd_private.io_addr + 3);
+ udelay(1000);
+
+ stat_reg = inb_p(pcipcwd_private.io_addr + 2);
+ spin_unlock(&pcipcwd_private.io_lock);
+
+ if (stat_reg & WD_PCI_WDIS) {
+ printk(KERN_ERR PFX "Card timer not enabled\n");
+ return -1;
+ }
+
+ if (debug >= VERBOSE)
+ printk(KERN_DEBUG PFX "Watchdog started\n");
+
+ return 0;
+}
+
+static int pcipcwd_stop(void)
+{
+ int stat_reg;
+
+ spin_lock(&pcipcwd_private.io_lock);
+ outb_p(0xA5, pcipcwd_private.io_addr + 3);
+ udelay(1000);
+
+ outb_p(0xA5, pcipcwd_private.io_addr + 3);
+ udelay(1000);
+
+ stat_reg = inb_p(pcipcwd_private.io_addr + 2);
+ spin_unlock(&pcipcwd_private.io_lock);
+
+ if (!(stat_reg & WD_PCI_WDIS)) {
+ printk(KERN_ERR PFX
+ "Card did not acknowledge disable attempt\n");
+ return -1;
+ }
+
+ if (debug >= VERBOSE)
+ printk(KERN_DEBUG PFX "Watchdog stopped\n");
+
+ return 0;
+}
+
+static int pcipcwd_keepalive(void)
+{
+ /* Re-trigger watchdog by writing to port 0 */
+ spin_lock(&pcipcwd_private.io_lock);
+ outb_p(0x42, pcipcwd_private.io_addr); /* send out any data */
+ spin_unlock(&pcipcwd_private.io_lock);
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n");
+
+ return 0;
+}
+
+static int pcipcwd_set_heartbeat(int t)
+{
+ int t_msb = t / 256;
+ int t_lsb = t % 256;
+
+ if ((t < 0x0001) || (t > 0xFFFF))
+ return -EINVAL;
+
+ /* Write new heartbeat to watchdog */
+ send_command(CMD_WRITE_WATCHDOG_TIMEOUT, &t_msb, &t_lsb);
+
+ heartbeat = t;
+ if (debug >= VERBOSE)
+ printk(KERN_DEBUG PFX "New heartbeat: %d\n",
+ heartbeat);
+
+ return 0;
+}
+
+static int pcipcwd_get_status(int *status)
+{
+ int control_status;
+
+ *status = 0;
+ control_status = inb_p(pcipcwd_private.io_addr + 1);
+ if (control_status & WD_PCI_WTRP)
+ *status |= WDIOF_CARDRESET;
+ if (control_status & WD_PCI_TTRP) {
+ *status |= WDIOF_OVERHEAT;
+ if (temp_panic)
+ panic(PFX "Temperature overheat trip!\n");
+ }
+
+ if (debug >= DEBUG)
+ printk(KERN_DEBUG PFX "Control Status #1: 0x%02x\n",
+ control_status);
+
+ return 0;
+}
+
+static int pcipcwd_clear_status(void)
+{
+ int control_status;
+ int msb;
+ int reset_counter;
+
+ if (debug >= VERBOSE)
+ printk(KERN_INFO PFX "clearing watchdog trip status & LED\n");
+
+ control_status = inb_p(pcipcwd_private.io_addr + 1);
+
+ if (debug >= DEBUG) {
+ printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status);
+ printk(KERN_DEBUG PFX "sending: 0x%02x\n",
+ (control_status & WD_PCI_R2DS) | WD_PCI_WTRP);
+ }
+
+ /* clear trip status & LED and keep mode of relay 2 */
+ outb_p((control_status & WD_PCI_R2DS) | WD_PCI_WTRP,
+ pcipcwd_private.io_addr + 1);
+
+ /* clear reset counter */
+ msb = 0;
+ reset_counter = 0xff;
+ send_command(CMD_GET_CLEAR_RESET_COUNT, &msb, &reset_counter);
+
+ if (debug >= DEBUG) {
+ printk(KERN_DEBUG PFX "reset count was: 0x%02x\n",
+ reset_counter);
+ }
+
+ return 0;
+}
+
+static int pcipcwd_get_temperature(int *temperature)
+{
+ *temperature = 0;
+ if (!pcipcwd_private.supports_temp)
+ return -ENODEV;
+
+ spin_lock(&pcipcwd_private.io_lock);
+ *temperature = inb_p(pcipcwd_private.io_addr);
+ spin_unlock(&pcipcwd_private.io_lock);
+
+ /*
+ * Convert celsius to fahrenheit, since this was
+ * the decided 'standard' for this return value.
+ */
+ *temperature = (*temperature * 9 / 5) + 32;
+
+ if (debug >= DEBUG) {
+ printk(KERN_DEBUG PFX "temperature is: %d F\n",
+ *temperature);
+ }
+
+ return 0;
+}
+
+static int pcipcwd_get_timeleft(int *time_left)
+{
+ int msb;
+ int lsb;
+
+ /* Read the time that's left before rebooting */
+ /* Note: if the board is not yet armed then we will read 0xFFFF */
+ send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb);
+
+ *time_left = (msb << 8) + lsb;
+
+ if (debug >= VERBOSE)
+ printk(KERN_DEBUG PFX "Time left before next reboot: %d\n",
+ *time_left);
+
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t pcipcwd_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ expect_release = 0;
+
+ /* scan to see whether or not we got the
+ * magic character */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_release = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ pcipcwd_keepalive();
+ }
+ return len;
+}
+
+static long pcipcwd_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_OVERHEAT |
+ WDIOF_CARDRESET |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = WATCHDOG_DRIVER_NAME,
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ {
+ int status;
+ pcipcwd_get_status(&status);
+ return put_user(status, p);
+ }
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(pcipcwd_private.boot_status, p);
+
+ case WDIOC_GETTEMP:
+ {
+ int temperature;
+
+ if (pcipcwd_get_temperature(&temperature))
+ return -EFAULT;
+
+ return put_user(temperature, p);
+ }
+
+ case WDIOC_SETOPTIONS:
+ {
+ int new_options, retval = -EINVAL;
+
+ if (get_user(new_options, p))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD) {
+ if (pcipcwd_stop())
+ return -EIO;
+ retval = 0;
+ }
+
+ if (new_options & WDIOS_ENABLECARD) {
+ if (pcipcwd_start())
+ return -EIO;
+ retval = 0;
+ }
+
+ if (new_options & WDIOS_TEMPPANIC) {
+ temp_panic = 1;
+ retval = 0;
+ }
+
+ return retval;
+ }
+
+ case WDIOC_KEEPALIVE:
+ pcipcwd_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ {
+ int new_heartbeat;
+
+ if (get_user(new_heartbeat, p))
+ return -EFAULT;
+
+ if (pcipcwd_set_heartbeat(new_heartbeat))
+ return -EINVAL;
+
+ pcipcwd_keepalive();
+ /* Fall */
+ }
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+
+ case WDIOC_GETTIMELEFT:
+ {
+ int time_left;
+
+ if (pcipcwd_get_timeleft(&time_left))
+ return -EFAULT;
+
+ return put_user(time_left, p);
+ }
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int pcipcwd_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &is_active)) {
+ if (debug >= VERBOSE)
+ printk(KERN_ERR PFX
+ "Attempt to open already opened device.\n");
+ return -EBUSY;
+ }
+
+ /* Activate */
+ pcipcwd_start();
+ pcipcwd_keepalive();
+ return nonseekable_open(inode, file);
+}
+
+static int pcipcwd_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ */
+ if (expect_release == 42) {
+ pcipcwd_stop();
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ pcipcwd_keepalive();
+ }
+ expect_release = 0;
+ clear_bit(0, &is_active);
+ return 0;
+}
+
+/*
+ * /dev/temperature handling
+ */
+
+static ssize_t pcipcwd_temp_read(struct file *file, char __user *data,
+ size_t len, loff_t *ppos)
+{
+ int temperature;
+
+ if (pcipcwd_get_temperature(&temperature))
+ return -EFAULT;
+
+ if (copy_to_user(data, &temperature, 1))
+ return -EFAULT;
+
+ return 1;
+}
+
+static int pcipcwd_temp_open(struct inode *inode, struct file *file)
+{
+ if (!pcipcwd_private.supports_temp)
+ return -ENODEV;
+
+ return nonseekable_open(inode, file);
+}
+
+static int pcipcwd_temp_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/*
+ * Notify system
+ */
+
+static int pcipcwd_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ pcipcwd_stop(); /* Turn the WDT off */
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations pcipcwd_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = pcipcwd_write,
+ .unlocked_ioctl = pcipcwd_ioctl,
+ .open = pcipcwd_open,
+ .release = pcipcwd_release,
+};
+
+static struct miscdevice pcipcwd_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &pcipcwd_fops,
+};
+
+static const struct file_operations pcipcwd_temp_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = pcipcwd_temp_read,
+ .open = pcipcwd_temp_open,
+ .release = pcipcwd_temp_release,
+};
+
+static struct miscdevice pcipcwd_temp_miscdev = {
+ .minor = TEMP_MINOR,
+ .name = "temperature",
+ .fops = &pcipcwd_temp_fops,
+};
+
+static struct notifier_block pcipcwd_notifier = {
+ .notifier_call = pcipcwd_notify_sys,
+};
+
+/*
+ * Init & exit routines
+ */
+
+static int __devinit pcipcwd_card_init(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int ret = -EIO;
+
+ cards_found++;
+ if (cards_found == 1)
+ printk(KERN_INFO PFX DRIVER_VERSION);
+
+ if (cards_found > 1) {
+ printk(KERN_ERR PFX "This driver only supports 1 device\n");
+ return -ENODEV;
+ }
+
+ if (pci_enable_device(pdev)) {
+ printk(KERN_ERR PFX "Not possible to enable PCI Device\n");
+ return -ENODEV;
+ }
+
+ if (pci_resource_start(pdev, 0) == 0x0000) {
+ printk(KERN_ERR PFX "No I/O-Address for card detected\n");
+ ret = -ENODEV;
+ goto err_out_disable_device;
+ }
+
+ pcipcwd_private.pdev = pdev;
+ pcipcwd_private.io_addr = pci_resource_start(pdev, 0);
+
+ if (pci_request_regions(pdev, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ (int) pcipcwd_private.io_addr);
+ ret = -EIO;
+ goto err_out_disable_device;
+ }
+
+ /* get the boot_status */
+ pcipcwd_get_status(&pcipcwd_private.boot_status);
+
+ /* clear the "card caused reboot" flag */
+ pcipcwd_clear_status();
+
+ /* disable card */
+ pcipcwd_stop();
+
+ /* Check whether or not the card supports the temperature device */
+ pcipcwd_check_temperature_support();
+
+ /* Show info about the card itself */
+ pcipcwd_show_card_info();
+
+ /* If heartbeat = 0 then we use the heartbeat from the dip-switches */
+ if (heartbeat == 0)
+ heartbeat =
+ heartbeat_tbl[(pcipcwd_get_option_switches() & 0x07)];
+
+ /* Check that the heartbeat value is within it's range ;
+ * if not reset to the default */
+ if (pcipcwd_set_heartbeat(heartbeat)) {
+ pcipcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
+ printk(KERN_INFO PFX
+ "heartbeat value must be 0<heartbeat<65536, using %d\n",
+ WATCHDOG_HEARTBEAT);
+ }
+
+ ret = register_reboot_notifier(&pcipcwd_notifier);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ goto err_out_release_region;
+ }
+
+ if (pcipcwd_private.supports_temp) {
+ ret = misc_register(&pcipcwd_temp_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX "cannot register miscdev on "
+ "minor=%d (err=%d)\n", TEMP_MINOR, ret);
+ goto err_out_unregister_reboot;
+ }
+ }
+
+ ret = misc_register(&pcipcwd_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto err_out_misc_deregister;
+ }
+
+ printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ heartbeat, nowayout);
+
+ return 0;
+
+err_out_misc_deregister:
+ if (pcipcwd_private.supports_temp)
+ misc_deregister(&pcipcwd_temp_miscdev);
+err_out_unregister_reboot:
+ unregister_reboot_notifier(&pcipcwd_notifier);
+err_out_release_region:
+ pci_release_regions(pdev);
+err_out_disable_device:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void __devexit pcipcwd_card_exit(struct pci_dev *pdev)
+{
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ pcipcwd_stop();
+
+ /* Deregister */
+ misc_deregister(&pcipcwd_miscdev);
+ if (pcipcwd_private.supports_temp)
+ misc_deregister(&pcipcwd_temp_miscdev);
+ unregister_reboot_notifier(&pcipcwd_notifier);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ cards_found--;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(pcipcwd_pci_tbl) = {
+ { PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_WATCHDOG_PCIPCWD,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { 0 }, /* End of list */
+};
+MODULE_DEVICE_TABLE(pci, pcipcwd_pci_tbl);
+
+static struct pci_driver pcipcwd_driver = {
+ .name = WATCHDOG_NAME,
+ .id_table = pcipcwd_pci_tbl,
+ .probe = pcipcwd_card_init,
+ .remove = __devexit_p(pcipcwd_card_exit),
+};
+
+static int __init pcipcwd_init_module(void)
+{
+ spin_lock_init(&pcipcwd_private.io_lock);
+
+ return pci_register_driver(&pcipcwd_driver);
+}
+
+static void __exit pcipcwd_cleanup_module(void)
+{
+ pci_unregister_driver(&pcipcwd_driver);
+
+ printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+}
+
+module_init(pcipcwd_init_module);
+module_exit(pcipcwd_cleanup_module);
+
+MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
+MODULE_DESCRIPTION("Berkshire PCI-PC Watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS_MISCDEV(TEMP_MINOR);
diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c
new file mode 100644
index 00000000..748a74bd
--- /dev/null
+++ b/drivers/watchdog/pcwd_usb.c
@@ -0,0 +1,863 @@
+/*
+ * Berkshire USB-PC Watchdog Card Driver
+ *
+ * (c) Copyright 2004-2007 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ * Based on source code of the following authors:
+ * Ken Hollis <kenji@bitgate.com>,
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * Matt Domsch <Matt_Domsch@dell.com>,
+ * Rob Radez <rob@osinvestor.com>,
+ * Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
+ * provide warranty for any of this software. This material is
+ * provided "AS-IS" and at no charge.
+ *
+ * Thanks also to Simon Machell at Berkshire Products Inc. for
+ * providing the test hardware. More info is available at
+ * http://www.berkprod.com/ or http://www.pcwatchdog.com/
+ */
+
+#include <linux/module.h> /* For module specific items */
+#include <linux/moduleparam.h> /* For new moduleparam's */
+#include <linux/types.h> /* For standard types (like size_t) */
+#include <linux/errno.h> /* For the -ENODEV/... values */
+#include <linux/kernel.h> /* For printk/panic/... */
+#include <linux/delay.h> /* For mdelay function */
+#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
+#include <linux/watchdog.h> /* For the watchdog specific items */
+#include <linux/notifier.h> /* For notifier support */
+#include <linux/reboot.h> /* For reboot_notifier stuff */
+#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/fs.h> /* For file operations */
+#include <linux/usb.h> /* For USB functions */
+#include <linux/slab.h> /* For kmalloc, ... */
+#include <linux/mutex.h> /* For mutex locking */
+#include <linux/hid.h> /* For HID_REQ_SET_REPORT & HID_DT_REPORT */
+#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+
+
+#ifdef CONFIG_USB_DEBUG
+ static int debug = 1;
+#else
+ static int debug;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(format, arg...) \
+ do { if (debug) printk(KERN_DEBUG PFX format "\n" , ## arg); } while (0)
+
+/* Module and Version Information */
+#define DRIVER_VERSION "1.02"
+#define DRIVER_AUTHOR "Wim Van Sebroeck <wim@iguana.be>"
+#define DRIVER_DESC "Berkshire USB-PC Watchdog driver"
+#define DRIVER_LICENSE "GPL"
+#define DRIVER_NAME "pcwd_usb"
+#define PFX DRIVER_NAME ": "
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS_MISCDEV(TEMP_MINOR);
+
+/* Module Parameters */
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+#define WATCHDOG_HEARTBEAT 0 /* default heartbeat =
+ delay-time from dip-switches */
+static int heartbeat = WATCHDOG_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
+ "(0<heartbeat<65536 or 0=delay-time from dip-switches, default="
+ __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/* The vendor and product id's for the USB-PC Watchdog card */
+#define USB_PCWD_VENDOR_ID 0x0c98
+#define USB_PCWD_PRODUCT_ID 0x1140
+
+/* table of devices that work with this driver */
+static struct usb_device_id usb_pcwd_table[] = {
+ { USB_DEVICE(USB_PCWD_VENDOR_ID, USB_PCWD_PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, usb_pcwd_table);
+
+/* according to documentation max. time to process a command for the USB
+ * watchdog card is 100 or 200 ms, so we give it 250 ms to do it's job */
+#define USB_COMMAND_TIMEOUT 250
+
+/* Watchdog's internal commands */
+#define CMD_READ_TEMP 0x02 /* Read Temperature;
+ Re-trigger Watchdog */
+#define CMD_TRIGGER CMD_READ_TEMP
+#define CMD_GET_STATUS 0x04 /* Get Status Information */
+#define CMD_GET_FIRMWARE_VERSION 0x08 /* Get Firmware Version */
+#define CMD_GET_DIP_SWITCH_SETTINGS 0x0c /* Get Dip Switch Settings */
+#define CMD_READ_WATCHDOG_TIMEOUT 0x18 /* Read Current Watchdog Time */
+#define CMD_WRITE_WATCHDOG_TIMEOUT 0x19 /* Write Current WatchdogTime */
+#define CMD_ENABLE_WATCHDOG 0x30 /* Enable / Disable Watchdog */
+#define CMD_DISABLE_WATCHDOG CMD_ENABLE_WATCHDOG
+
+/* Watchdog's Dip Switch heartbeat values */
+static const int heartbeat_tbl[] = {
+ 5, /* OFF-OFF-OFF = 5 Sec */
+ 10, /* OFF-OFF-ON = 10 Sec */
+ 30, /* OFF-ON-OFF = 30 Sec */
+ 60, /* OFF-ON-ON = 1 Min */
+ 300, /* ON-OFF-OFF = 5 Min */
+ 600, /* ON-OFF-ON = 10 Min */
+ 1800, /* ON-ON-OFF = 30 Min */
+ 3600, /* ON-ON-ON = 1 hour */
+};
+
+/* We can only use 1 card due to the /dev/watchdog restriction */
+static int cards_found;
+
+/* some internal variables */
+static unsigned long is_active;
+static char expect_release;
+
+/* Structure to hold all of our device specific stuff */
+struct usb_pcwd_private {
+ /* save off the usb device pointer */
+ struct usb_device *udev;
+ /* the interface for this device */
+ struct usb_interface *interface;
+
+ /* the interface number used for cmd's */
+ unsigned int interface_number;
+
+ /* the buffer to intr data */
+ unsigned char *intr_buffer;
+ /* the dma address for the intr buffer */
+ dma_addr_t intr_dma;
+ /* the size of the intr buffer */
+ size_t intr_size;
+ /* the urb used for the intr pipe */
+ struct urb *intr_urb;
+
+ /* The command that is reported back */
+ unsigned char cmd_command;
+ /* The data MSB that is reported back */
+ unsigned char cmd_data_msb;
+ /* The data LSB that is reported back */
+ unsigned char cmd_data_lsb;
+ /* true if we received a report after a command */
+ atomic_t cmd_received;
+
+ /* Wether or not the device exists */
+ int exists;
+ /* locks this structure */
+ struct mutex mtx;
+};
+static struct usb_pcwd_private *usb_pcwd_device;
+
+/* prevent races between open() and disconnect() */
+static DEFINE_MUTEX(disconnect_mutex);
+
+/* local function prototypes */
+static int usb_pcwd_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void usb_pcwd_disconnect(struct usb_interface *interface);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver usb_pcwd_driver = {
+ .name = DRIVER_NAME,
+ .probe = usb_pcwd_probe,
+ .disconnect = usb_pcwd_disconnect,
+ .id_table = usb_pcwd_table,
+};
+
+
+static void usb_pcwd_intr_done(struct urb *urb)
+{
+ struct usb_pcwd_private *usb_pcwd =
+ (struct usb_pcwd_private *)urb->context;
+ unsigned char *data = usb_pcwd->intr_buffer;
+ int retval;
+
+ switch (urb->status) {
+ case 0: /* success */
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __func__,
+ urb->status);
+ return;
+ /* -EPIPE: should clear the halt */
+ default: /* error */
+ dbg("%s - nonzero urb status received: %d", __func__,
+ urb->status);
+ goto resubmit;
+ }
+
+ dbg("received following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
+ data[0], data[1], data[2]);
+
+ usb_pcwd->cmd_command = data[0];
+ usb_pcwd->cmd_data_msb = data[1];
+ usb_pcwd->cmd_data_lsb = data[2];
+
+ /* notify anyone waiting that the cmd has finished */
+ atomic_set(&usb_pcwd->cmd_received, 1);
+
+resubmit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ printk(KERN_ERR PFX "can't resubmit intr, "
+ "usb_submit_urb failed with result %d\n", retval);
+}
+
+static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd,
+ unsigned char cmd, unsigned char *msb, unsigned char *lsb)
+{
+ int got_response, count;
+ unsigned char buf[6];
+
+ /* We will not send any commands if the USB PCWD device does
+ * not exist */
+ if ((!usb_pcwd) || (!usb_pcwd->exists))
+ return -1;
+
+ /* The USB PC Watchdog uses a 6 byte report format.
+ * The board currently uses only 3 of the six bytes of the report. */
+ buf[0] = cmd; /* Byte 0 = CMD */
+ buf[1] = *msb; /* Byte 1 = Data MSB */
+ buf[2] = *lsb; /* Byte 2 = Data LSB */
+ buf[3] = buf[4] = buf[5] = 0; /* All other bytes not used */
+
+ dbg("sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
+ buf[0], buf[1], buf[2]);
+
+ atomic_set(&usb_pcwd->cmd_received, 0);
+
+ if (usb_control_msg(usb_pcwd->udev, usb_sndctrlpipe(usb_pcwd->udev, 0),
+ HID_REQ_SET_REPORT, HID_DT_REPORT,
+ 0x0200, usb_pcwd->interface_number, buf, sizeof(buf),
+ USB_COMMAND_TIMEOUT) != sizeof(buf)) {
+ dbg("usb_pcwd_send_command: error in usb_control_msg for "
+ "cmd 0x%x 0x%x 0x%x\n", cmd, *msb, *lsb);
+ }
+ /* wait till the usb card processed the command,
+ * with a max. timeout of USB_COMMAND_TIMEOUT */
+ got_response = 0;
+ for (count = 0; (count < USB_COMMAND_TIMEOUT) && (!got_response);
+ count++) {
+ mdelay(1);
+ if (atomic_read(&usb_pcwd->cmd_received))
+ got_response = 1;
+ }
+
+ if ((got_response) && (cmd == usb_pcwd->cmd_command)) {
+ /* read back response */
+ *msb = usb_pcwd->cmd_data_msb;
+ *lsb = usb_pcwd->cmd_data_lsb;
+ }
+
+ return got_response;
+}
+
+static int usb_pcwd_start(struct usb_pcwd_private *usb_pcwd)
+{
+ unsigned char msb = 0x00;
+ unsigned char lsb = 0x00;
+ int retval;
+
+ /* Enable Watchdog */
+ retval = usb_pcwd_send_command(usb_pcwd, CMD_ENABLE_WATCHDOG,
+ &msb, &lsb);
+
+ if ((retval == 0) || (lsb == 0)) {
+ printk(KERN_ERR PFX
+ "Card did not acknowledge enable attempt\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int usb_pcwd_stop(struct usb_pcwd_private *usb_pcwd)
+{
+ unsigned char msb = 0xA5;
+ unsigned char lsb = 0xC3;
+ int retval;
+
+ /* Disable Watchdog */
+ retval = usb_pcwd_send_command(usb_pcwd, CMD_DISABLE_WATCHDOG,
+ &msb, &lsb);
+
+ if ((retval == 0) || (lsb != 0)) {
+ printk(KERN_ERR PFX
+ "Card did not acknowledge disable attempt\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int usb_pcwd_keepalive(struct usb_pcwd_private *usb_pcwd)
+{
+ unsigned char dummy;
+
+ /* Re-trigger Watchdog */
+ usb_pcwd_send_command(usb_pcwd, CMD_TRIGGER, &dummy, &dummy);
+
+ return 0;
+}
+
+static int usb_pcwd_set_heartbeat(struct usb_pcwd_private *usb_pcwd, int t)
+{
+ unsigned char msb = t / 256;
+ unsigned char lsb = t % 256;
+
+ if ((t < 0x0001) || (t > 0xFFFF))
+ return -EINVAL;
+
+ /* Write new heartbeat to watchdog */
+ usb_pcwd_send_command(usb_pcwd, CMD_WRITE_WATCHDOG_TIMEOUT, &msb, &lsb);
+
+ heartbeat = t;
+ return 0;
+}
+
+static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd,
+ int *temperature)
+{
+ unsigned char msb, lsb;
+
+ usb_pcwd_send_command(usb_pcwd, CMD_READ_TEMP, &msb, &lsb);
+
+ /*
+ * Convert celsius to fahrenheit, since this was
+ * the decided 'standard' for this return value.
+ */
+ *temperature = (lsb * 9 / 5) + 32;
+
+ return 0;
+}
+
+static int usb_pcwd_get_timeleft(struct usb_pcwd_private *usb_pcwd,
+ int *time_left)
+{
+ unsigned char msb, lsb;
+
+ /* Read the time that's left before rebooting */
+ /* Note: if the board is not yet armed then we will read 0xFFFF */
+ usb_pcwd_send_command(usb_pcwd, CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb);
+
+ *time_left = (msb << 8) + lsb;
+
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t usb_pcwd_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ expect_release = 0;
+
+ /* scan to see whether or not we got the
+ * magic character */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_release = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ usb_pcwd_keepalive(usb_pcwd_device);
+ }
+ return len;
+}
+
+static long usb_pcwd_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = DRIVER_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_GETTEMP:
+ {
+ int temperature;
+
+ if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature))
+ return -EFAULT;
+
+ return put_user(temperature, p);
+ }
+
+ case WDIOC_SETOPTIONS:
+ {
+ int new_options, retval = -EINVAL;
+
+ if (get_user(new_options, p))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD) {
+ usb_pcwd_stop(usb_pcwd_device);
+ retval = 0;
+ }
+
+ if (new_options & WDIOS_ENABLECARD) {
+ usb_pcwd_start(usb_pcwd_device);
+ retval = 0;
+ }
+
+ return retval;
+ }
+
+ case WDIOC_KEEPALIVE:
+ usb_pcwd_keepalive(usb_pcwd_device);
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ {
+ int new_heartbeat;
+
+ if (get_user(new_heartbeat, p))
+ return -EFAULT;
+
+ if (usb_pcwd_set_heartbeat(usb_pcwd_device, new_heartbeat))
+ return -EINVAL;
+
+ usb_pcwd_keepalive(usb_pcwd_device);
+ /* Fall */
+ }
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+
+ case WDIOC_GETTIMELEFT:
+ {
+ int time_left;
+
+ if (usb_pcwd_get_timeleft(usb_pcwd_device, &time_left))
+ return -EFAULT;
+
+ return put_user(time_left, p);
+ }
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int usb_pcwd_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &is_active))
+ return -EBUSY;
+
+ /* Activate */
+ usb_pcwd_start(usb_pcwd_device);
+ usb_pcwd_keepalive(usb_pcwd_device);
+ return nonseekable_open(inode, file);
+}
+
+static int usb_pcwd_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ */
+ if (expect_release == 42) {
+ usb_pcwd_stop(usb_pcwd_device);
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ usb_pcwd_keepalive(usb_pcwd_device);
+ }
+ expect_release = 0;
+ clear_bit(0, &is_active);
+ return 0;
+}
+
+/*
+ * /dev/temperature handling
+ */
+
+static ssize_t usb_pcwd_temperature_read(struct file *file, char __user *data,
+ size_t len, loff_t *ppos)
+{
+ int temperature;
+
+ if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature))
+ return -EFAULT;
+
+ if (copy_to_user(data, &temperature, 1))
+ return -EFAULT;
+
+ return 1;
+}
+
+static int usb_pcwd_temperature_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+static int usb_pcwd_temperature_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/*
+ * Notify system
+ */
+
+static int usb_pcwd_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ usb_pcwd_stop(usb_pcwd_device); /* Turn the WDT off */
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations usb_pcwd_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = usb_pcwd_write,
+ .unlocked_ioctl = usb_pcwd_ioctl,
+ .open = usb_pcwd_open,
+ .release = usb_pcwd_release,
+};
+
+static struct miscdevice usb_pcwd_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &usb_pcwd_fops,
+};
+
+static const struct file_operations usb_pcwd_temperature_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = usb_pcwd_temperature_read,
+ .open = usb_pcwd_temperature_open,
+ .release = usb_pcwd_temperature_release,
+};
+
+static struct miscdevice usb_pcwd_temperature_miscdev = {
+ .minor = TEMP_MINOR,
+ .name = "temperature",
+ .fops = &usb_pcwd_temperature_fops,
+};
+
+static struct notifier_block usb_pcwd_notifier = {
+ .notifier_call = usb_pcwd_notify_sys,
+};
+
+/**
+ * usb_pcwd_delete
+ */
+static inline void usb_pcwd_delete(struct usb_pcwd_private *usb_pcwd)
+{
+ usb_free_urb(usb_pcwd->intr_urb);
+ if (usb_pcwd->intr_buffer != NULL)
+ usb_free_coherent(usb_pcwd->udev, usb_pcwd->intr_size,
+ usb_pcwd->intr_buffer, usb_pcwd->intr_dma);
+ kfree(usb_pcwd);
+}
+
+/**
+ * usb_pcwd_probe
+ *
+ * Called by the usb core when a new device is connected that it thinks
+ * this driver might be interested in.
+ */
+static int usb_pcwd_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_pcwd_private *usb_pcwd = NULL;
+ int pipe, maxp;
+ int retval = -ENOMEM;
+ int got_fw_rev;
+ unsigned char fw_rev_major, fw_rev_minor;
+ char fw_ver_str[20];
+ unsigned char option_switches, dummy;
+
+ cards_found++;
+ if (cards_found > 1) {
+ printk(KERN_ERR PFX "This driver only supports 1 device\n");
+ return -ENODEV;
+ }
+
+ /* get the active interface descriptor */
+ iface_desc = interface->cur_altsetting;
+
+ /* check out that we have a HID device */
+ if (!(iface_desc->desc.bInterfaceClass == USB_CLASS_HID)) {
+ printk(KERN_ERR PFX
+ "The device isn't a Human Interface Device\n");
+ return -ENODEV;
+ }
+
+ /* check out the endpoint: it has to be Interrupt & IN */
+ endpoint = &iface_desc->endpoint[0].desc;
+
+ if (!usb_endpoint_is_int_in(endpoint)) {
+ /* we didn't find a Interrupt endpoint with direction IN */
+ printk(KERN_ERR PFX "Couldn't find an INTR & IN endpoint\n");
+ return -ENODEV;
+ }
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+
+ /* allocate memory for our device and initialize it */
+ usb_pcwd = kzalloc(sizeof(struct usb_pcwd_private), GFP_KERNEL);
+ if (usb_pcwd == NULL) {
+ printk(KERN_ERR PFX "Out of memory\n");
+ goto error;
+ }
+
+ usb_pcwd_device = usb_pcwd;
+
+ mutex_init(&usb_pcwd->mtx);
+ usb_pcwd->udev = udev;
+ usb_pcwd->interface = interface;
+ usb_pcwd->interface_number = iface_desc->desc.bInterfaceNumber;
+ usb_pcwd->intr_size = (le16_to_cpu(endpoint->wMaxPacketSize) > 8 ?
+ le16_to_cpu(endpoint->wMaxPacketSize) : 8);
+
+ /* set up the memory buffer's */
+ usb_pcwd->intr_buffer = usb_alloc_coherent(udev, usb_pcwd->intr_size,
+ GFP_ATOMIC, &usb_pcwd->intr_dma);
+ if (!usb_pcwd->intr_buffer) {
+ printk(KERN_ERR PFX "Out of memory\n");
+ goto error;
+ }
+
+ /* allocate the urb's */
+ usb_pcwd->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usb_pcwd->intr_urb) {
+ printk(KERN_ERR PFX "Out of memory\n");
+ goto error;
+ }
+
+ /* initialise the intr urb's */
+ usb_fill_int_urb(usb_pcwd->intr_urb, udev, pipe,
+ usb_pcwd->intr_buffer, usb_pcwd->intr_size,
+ usb_pcwd_intr_done, usb_pcwd, endpoint->bInterval);
+ usb_pcwd->intr_urb->transfer_dma = usb_pcwd->intr_dma;
+ usb_pcwd->intr_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* register our interrupt URB with the USB system */
+ if (usb_submit_urb(usb_pcwd->intr_urb, GFP_KERNEL)) {
+ printk(KERN_ERR PFX "Problem registering interrupt URB\n");
+ retval = -EIO; /* failure */
+ goto error;
+ }
+
+ /* The device exists and can be communicated with */
+ usb_pcwd->exists = 1;
+
+ /* disable card */
+ usb_pcwd_stop(usb_pcwd);
+
+ /* Get the Firmware Version */
+ got_fw_rev = usb_pcwd_send_command(usb_pcwd, CMD_GET_FIRMWARE_VERSION,
+ &fw_rev_major, &fw_rev_minor);
+ if (got_fw_rev)
+ sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor);
+ else
+ sprintf(fw_ver_str, "<card no answer>");
+
+ printk(KERN_INFO PFX "Found card (Firmware: %s) with temp option\n",
+ fw_ver_str);
+
+ /* Get switch settings */
+ usb_pcwd_send_command(usb_pcwd, CMD_GET_DIP_SWITCH_SETTINGS, &dummy,
+ &option_switches);
+
+ printk(KERN_INFO PFX "Option switches (0x%02x): "
+ "Temperature Reset Enable=%s, Power On Delay=%s\n",
+ option_switches,
+ ((option_switches & 0x10) ? "ON" : "OFF"),
+ ((option_switches & 0x08) ? "ON" : "OFF"));
+
+ /* If heartbeat = 0 then we use the heartbeat from the dip-switches */
+ if (heartbeat == 0)
+ heartbeat = heartbeat_tbl[(option_switches & 0x07)];
+
+ /* Check that the heartbeat value is within it's range ;
+ * if not reset to the default */
+ if (usb_pcwd_set_heartbeat(usb_pcwd, heartbeat)) {
+ usb_pcwd_set_heartbeat(usb_pcwd, WATCHDOG_HEARTBEAT);
+ printk(KERN_INFO PFX
+ "heartbeat value must be 0<heartbeat<65536, using %d\n",
+ WATCHDOG_HEARTBEAT);
+ }
+
+ retval = register_reboot_notifier(&usb_pcwd_notifier);
+ if (retval != 0) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n",
+ retval);
+ goto error;
+ }
+
+ retval = misc_register(&usb_pcwd_temperature_miscdev);
+ if (retval != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ TEMP_MINOR, retval);
+ goto err_out_unregister_reboot;
+ }
+
+ retval = misc_register(&usb_pcwd_miscdev);
+ if (retval != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, retval);
+ goto err_out_misc_deregister;
+ }
+
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata(interface, usb_pcwd);
+
+ printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ heartbeat, nowayout);
+
+ return 0;
+
+err_out_misc_deregister:
+ misc_deregister(&usb_pcwd_temperature_miscdev);
+err_out_unregister_reboot:
+ unregister_reboot_notifier(&usb_pcwd_notifier);
+error:
+ if (usb_pcwd)
+ usb_pcwd_delete(usb_pcwd);
+ usb_pcwd_device = NULL;
+ return retval;
+}
+
+
+/**
+ * usb_pcwd_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ *
+ * This routine guarantees that the driver will not submit any more urbs
+ * by clearing dev->udev.
+ */
+static void usb_pcwd_disconnect(struct usb_interface *interface)
+{
+ struct usb_pcwd_private *usb_pcwd;
+
+ /* prevent races with open() */
+ mutex_lock(&disconnect_mutex);
+
+ usb_pcwd = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ mutex_lock(&usb_pcwd->mtx);
+
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ usb_pcwd_stop(usb_pcwd);
+
+ /* We should now stop communicating with the USB PCWD device */
+ usb_pcwd->exists = 0;
+
+ /* Deregister */
+ misc_deregister(&usb_pcwd_miscdev);
+ misc_deregister(&usb_pcwd_temperature_miscdev);
+ unregister_reboot_notifier(&usb_pcwd_notifier);
+
+ mutex_unlock(&usb_pcwd->mtx);
+
+ /* Delete the USB PCWD device */
+ usb_pcwd_delete(usb_pcwd);
+
+ cards_found--;
+
+ mutex_unlock(&disconnect_mutex);
+
+ printk(KERN_INFO PFX "USB PC Watchdog disconnected\n");
+}
+
+
+
+/**
+ * usb_pcwd_init
+ */
+static int __init usb_pcwd_init(void)
+{
+ int result;
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&usb_pcwd_driver);
+ if (result) {
+ printk(KERN_ERR PFX "usb_register failed. Error number %d\n",
+ result);
+ return result;
+ }
+
+ printk(KERN_INFO PFX DRIVER_DESC " v" DRIVER_VERSION "\n");
+ return 0;
+}
+
+
+/**
+ * usb_pcwd_exit
+ */
+static void __exit usb_pcwd_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&usb_pcwd_driver);
+}
+
+
+module_init(usb_pcwd_init);
+module_exit(usb_pcwd_exit);
diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c
new file mode 100644
index 00000000..2d22e996
--- /dev/null
+++ b/drivers/watchdog/pika_wdt.c
@@ -0,0 +1,301 @@
+/*
+ * PIKA FPGA based Watchdog Timer
+ *
+ * Copyright (c) 2008 PIKA Technologies
+ * Sean MacLennan <smaclennan@pikatech.com>
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+
+#define DRV_NAME "PIKA-WDT"
+#define PFX DRV_NAME ": "
+
+/* Hardware timeout in seconds */
+#define WDT_HW_TIMEOUT 2
+
+/* Timer heartbeat (500ms) */
+#define WDT_TIMEOUT (HZ/2)
+
+/* User land timeout */
+#define WDT_HEARTBEAT 15
+static int heartbeat = WDT_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
+ "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static struct {
+ void __iomem *fpga;
+ unsigned long next_heartbeat; /* the next_heartbeat for the timer */
+ unsigned long open;
+ char expect_close;
+ int bootstatus;
+ struct timer_list timer; /* The timer that pings the watchdog */
+} pikawdt_private;
+
+static struct watchdog_info ident = {
+ .identity = DRV_NAME,
+ .options = WDIOF_CARDRESET |
+ WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+/*
+ * Reload the watchdog timer. (ie, pat the watchdog)
+ */
+static inline void pikawdt_reset(void)
+{
+ /* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) --
+ * Bit 7, WTCHDG_EN: When set to 1, the watchdog timer is enabled.
+ * Once enabled, it cannot be disabled. The watchdog can be
+ * kicked by performing any write access to the reset
+ * control register (this register).
+ * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in
+ * seconds. Valid ranges are 1 to 15 seconds. The value can
+ * be modified dynamically.
+ */
+ unsigned reset = in_be32(pikawdt_private.fpga + 0x14);
+ /* enable with max timeout - 15 seconds */
+ reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8);
+ out_be32(pikawdt_private.fpga + 0x14, reset);
+}
+
+/*
+ * Timer tick
+ */
+static void pikawdt_ping(unsigned long data)
+{
+ if (time_before(jiffies, pikawdt_private.next_heartbeat) ||
+ (!nowayout && !pikawdt_private.open)) {
+ pikawdt_reset();
+ mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
+ } else
+ printk(KERN_CRIT PFX "I will reset your machine !\n");
+}
+
+
+static void pikawdt_keepalive(void)
+{
+ pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+}
+
+static void pikawdt_start(void)
+{
+ pikawdt_keepalive();
+ mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
+}
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int pikawdt_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &pikawdt_private.open))
+ return -EBUSY;
+
+ pikawdt_start();
+
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * Close the watchdog device.
+ */
+static int pikawdt_release(struct inode *inode, struct file *file)
+{
+ /* stop internal ping */
+ if (!pikawdt_private.expect_close)
+ del_timer(&pikawdt_private.timer);
+
+ clear_bit(0, &pikawdt_private.open);
+ pikawdt_private.expect_close = 0;
+ return 0;
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t pikawdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (!len)
+ return 0;
+
+ /* Scan for magic character */
+ if (!nowayout) {
+ size_t i;
+
+ pikawdt_private.expect_close = 0;
+
+ for (i = 0; i < len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V') {
+ pikawdt_private.expect_close = 42;
+ break;
+ }
+ }
+ }
+
+ pikawdt_keepalive();
+
+ return len;
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long pikawdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(pikawdt_private.bootstatus, p);
+
+ case WDIOC_KEEPALIVE:
+ pikawdt_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ heartbeat = new_value;
+ pikawdt_keepalive();
+
+ return put_user(new_value, p); /* return current value */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ }
+ return -ENOTTY;
+}
+
+
+static const struct file_operations pikawdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = pikawdt_open,
+ .release = pikawdt_release,
+ .write = pikawdt_write,
+ .unlocked_ioctl = pikawdt_ioctl,
+};
+
+static struct miscdevice pikawdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &pikawdt_fops,
+};
+
+static int __init pikawdt_init(void)
+{
+ struct device_node *np;
+ void __iomem *fpga;
+ static u32 post1;
+ int ret;
+
+ np = of_find_compatible_node(NULL, NULL, "pika,fpga");
+ if (np == NULL) {
+ printk(KERN_ERR PFX "Unable to find fpga.\n");
+ return -ENOENT;
+ }
+
+ pikawdt_private.fpga = of_iomap(np, 0);
+ of_node_put(np);
+ if (pikawdt_private.fpga == NULL) {
+ printk(KERN_ERR PFX "Unable to map fpga.\n");
+ return -ENOMEM;
+ }
+
+ ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff;
+
+ /* POST information is in the sd area. */
+ np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd");
+ if (np == NULL) {
+ printk(KERN_ERR PFX "Unable to find fpga-sd.\n");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ fpga = of_iomap(np, 0);
+ of_node_put(np);
+ if (fpga == NULL) {
+ printk(KERN_ERR PFX "Unable to map fpga-sd.\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) --
+ * Bit 31, WDOG: Set to 1 when the last reset was caused by a watchdog
+ * timeout.
+ */
+ post1 = in_be32(fpga + 0x40);
+ if (post1 & 0x80000000)
+ pikawdt_private.bootstatus = WDIOF_CARDRESET;
+
+ iounmap(fpga);
+
+ setup_timer(&pikawdt_private.timer, pikawdt_ping, 0);
+
+ ret = misc_register(&pikawdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX "Unable to register miscdev.\n");
+ goto out;
+ }
+
+ printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ heartbeat, nowayout);
+ return 0;
+
+out:
+ iounmap(pikawdt_private.fpga);
+ return ret;
+}
+
+static void __exit pikawdt_exit(void)
+{
+ misc_deregister(&pikawdt_miscdev);
+
+ iounmap(pikawdt_private.fpga);
+}
+
+module_init(pikawdt_init);
+module_exit(pikawdt_exit);
+
+MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>");
+MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
new file mode 100644
index 00000000..61493322
--- /dev/null
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -0,0 +1,365 @@
+/*
+ * drivers/char/watchdog/pnx4008_wdt.c
+ *
+ * Watchdog driver for PNX4008 board
+ *
+ * Authors: Dmitry Chigirev <source@mvista.com>,
+ * Vitaly Wool <vitalywool@gmail.com>
+ * Based on sa1100 driver,
+ * Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <mach/hardware.h>
+
+#define MODULE_NAME "PNX4008-WDT: "
+
+/* WatchDog Timer - Chapter 23 Page 207 */
+
+#define DEFAULT_HEARTBEAT 19
+#define MAX_HEARTBEAT 60
+
+/* Watchdog timer register set definition */
+#define WDTIM_INT(p) ((p) + 0x0)
+#define WDTIM_CTRL(p) ((p) + 0x4)
+#define WDTIM_COUNTER(p) ((p) + 0x8)
+#define WDTIM_MCTRL(p) ((p) + 0xC)
+#define WDTIM_MATCH0(p) ((p) + 0x10)
+#define WDTIM_EMR(p) ((p) + 0x14)
+#define WDTIM_PULSE(p) ((p) + 0x18)
+#define WDTIM_RES(p) ((p) + 0x1C)
+
+/* WDTIM_INT bit definitions */
+#define MATCH_INT 1
+
+/* WDTIM_CTRL bit definitions */
+#define COUNT_ENAB 1
+#define RESET_COUNT (1 << 1)
+#define DEBUG_EN (1 << 2)
+
+/* WDTIM_MCTRL bit definitions */
+#define MR0_INT 1
+#undef RESET_COUNT0
+#define RESET_COUNT0 (1 << 2)
+#define STOP_COUNT0 (1 << 2)
+#define M_RES1 (1 << 3)
+#define M_RES2 (1 << 4)
+#define RESFRC1 (1 << 5)
+#define RESFRC2 (1 << 6)
+
+/* WDTIM_EMR bit definitions */
+#define EXT_MATCH0 1
+#define MATCH_OUTPUT_HIGH (2 << 4) /*a MATCH_CTRL setting */
+
+/* WDTIM_RES bit definitions */
+#define WDOG_RESET 1 /* read only */
+
+#define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static int heartbeat = DEFAULT_HEARTBEAT;
+
+static DEFINE_SPINLOCK(io_lock);
+static unsigned long wdt_status;
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+#define WDT_REGION_INITED 2
+#define WDT_DEVICE_INITED 3
+
+static unsigned long boot_status;
+
+static struct resource *wdt_mem;
+static void __iomem *wdt_base;
+struct clk *wdt_clk;
+
+static void wdt_enable(void)
+{
+ spin_lock(&io_lock);
+
+ /* stop counter, initiate counter reset */
+ __raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base));
+ /*wait for reset to complete. 100% guarantee event */
+ while (__raw_readl(WDTIM_COUNTER(wdt_base)))
+ cpu_relax();
+ /* internal and external reset, stop after that */
+ __raw_writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0,
+ WDTIM_MCTRL(wdt_base));
+ /* configure match output */
+ __raw_writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base));
+ /* clear interrupt, just in case */
+ __raw_writel(MATCH_INT, WDTIM_INT(wdt_base));
+ /* the longest pulse period 65541/(13*10^6) seconds ~ 5 ms. */
+ __raw_writel(0xFFFF, WDTIM_PULSE(wdt_base));
+ __raw_writel(heartbeat * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base));
+ /*enable counter, stop when debugger active */
+ __raw_writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base));
+
+ spin_unlock(&io_lock);
+}
+
+static void wdt_disable(void)
+{
+ spin_lock(&io_lock);
+
+ __raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */
+
+ spin_unlock(&io_lock);
+}
+
+static int pnx4008_wdt_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ ret = clk_enable(wdt_clk);
+ if (ret) {
+ clear_bit(WDT_IN_USE, &wdt_status);
+ return ret;
+ }
+
+ wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t pnx4008_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_enable();
+ }
+
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
+ WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "PNX4008 Watchdog",
+};
+
+static long pnx4008_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(boot_status, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time <= 0 || time > MAX_HEARTBEAT) {
+ ret = -EINVAL;
+ break;
+ }
+
+ heartbeat = time;
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int pnx4008_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n");
+
+ wdt_disable();
+ clk_disable(wdt_clk);
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+static const struct file_operations pnx4008_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = pnx4008_wdt_write,
+ .unlocked_ioctl = pnx4008_wdt_ioctl,
+ .open = pnx4008_wdt_open,
+ .release = pnx4008_wdt_release,
+};
+
+static struct miscdevice pnx4008_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &pnx4008_wdt_fops,
+};
+
+static int __devinit pnx4008_wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0, size;
+
+ if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
+ heartbeat = DEFAULT_HEARTBEAT;
+
+ printk(KERN_INFO MODULE_NAME
+ "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat);
+
+ wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (wdt_mem == NULL) {
+ printk(KERN_INFO MODULE_NAME
+ "failed to get memory region resouce\n");
+ return -ENOENT;
+ }
+
+ size = resource_size(wdt_mem);
+
+ if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
+ printk(KERN_INFO MODULE_NAME "failed to get memory region\n");
+ return -ENOENT;
+ }
+ wdt_base = (void __iomem *)IO_ADDRESS(wdt_mem->start);
+
+ wdt_clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(wdt_clk)) {
+ ret = PTR_ERR(wdt_clk);
+ release_mem_region(wdt_mem->start, size);
+ wdt_mem = NULL;
+ goto out;
+ }
+
+ ret = clk_enable(wdt_clk);
+ if (ret) {
+ release_mem_region(wdt_mem->start, size);
+ wdt_mem = NULL;
+ clk_put(wdt_clk);
+ goto out;
+ }
+
+ ret = misc_register(&pnx4008_wdt_miscdev);
+ if (ret < 0) {
+ printk(KERN_ERR MODULE_NAME "cannot register misc device\n");
+ release_mem_region(wdt_mem->start, size);
+ wdt_mem = NULL;
+ clk_disable(wdt_clk);
+ clk_put(wdt_clk);
+ } else {
+ boot_status = (__raw_readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
+ WDIOF_CARDRESET : 0;
+ wdt_disable(); /*disable for now */
+ clk_disable(wdt_clk);
+ set_bit(WDT_DEVICE_INITED, &wdt_status);
+ }
+
+out:
+ return ret;
+}
+
+static int __devexit pnx4008_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&pnx4008_wdt_miscdev);
+
+ clk_disable(wdt_clk);
+ clk_put(wdt_clk);
+
+ if (wdt_mem) {
+ release_mem_region(wdt_mem->start, resource_size(wdt_mem));
+ wdt_mem = NULL;
+ }
+ return 0;
+}
+
+static struct platform_driver platform_wdt_driver = {
+ .driver = {
+ .name = "pnx4008-watchdog",
+ .owner = THIS_MODULE,
+ },
+ .probe = pnx4008_wdt_probe,
+ .remove = __devexit_p(pnx4008_wdt_remove),
+};
+
+static int __init pnx4008_wdt_init(void)
+{
+ return platform_driver_register(&platform_wdt_driver);
+}
+
+static void __exit pnx4008_wdt_exit(void)
+{
+ platform_driver_unregister(&platform_wdt_driver);
+}
+
+module_init(pnx4008_wdt_init);
+module_exit(pnx4008_wdt_exit);
+
+MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
+MODULE_DESCRIPTION("PNX4008 Watchdog Driver");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat period in seconds from 1 to "
+ __MODULE_STRING(MAX_HEARTBEAT) ", default "
+ __MODULE_STRING(DEFAULT_HEARTBEAT));
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Set to 1 to keep watchdog running after device release");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:pnx4008-watchdog");
diff --git a/drivers/watchdog/pnx833x_wdt.c b/drivers/watchdog/pnx833x_wdt.c
new file mode 100644
index 00000000..a7b5ad2a
--- /dev/null
+++ b/drivers/watchdog/pnx833x_wdt.c
@@ -0,0 +1,285 @@
+/*
+ * PNX833x Hardware Watchdog Driver
+ * Copyright 2008 NXP Semiconductors
+ * Daniel Laird <daniel.j.laird@nxp.com>
+ * Andre McCurdy <andre.mccurdy@nxp.com>
+ *
+ * Heavily based upon - IndyDog 0.3
+ * A Hardware Watchdog Device for SGI IP22
+ *
+ * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * based on softdog.c by Alan Cox <alan@redhat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <asm/mach-pnx833x/pnx833x.h>
+
+#define PFX "pnx833x: "
+#define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */
+#define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */
+#define PNX_WATCHDOG_TIMEOUT (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY)
+#define PNX_TIMEOUT_VALUE 2040000000U
+
+/** CONFIG block */
+#define PNX833X_CONFIG (0x07000U)
+#define PNX833X_CONFIG_CPU_WATCHDOG (0x54)
+#define PNX833X_CONFIG_CPU_WATCHDOG_COMPARE (0x58)
+#define PNX833X_CONFIG_CPU_COUNTERS_CONTROL (0x1c)
+
+/** RESET block */
+#define PNX833X_RESET (0x08000U)
+#define PNX833X_RESET_CONFIG (0x08)
+
+static int pnx833x_wdt_alive;
+
+/* Set default timeout in MHZ.*/
+static int pnx833x_wdt_timeout = PNX_WATCHDOG_TIMEOUT;
+module_param(pnx833x_wdt_timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default="
+ __MODULE_STRING(PNX_TIMEOUT_VALUE) "(30 seconds).");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#define START_DEFAULT 1
+static int start_enabled = START_DEFAULT;
+module_param(start_enabled, int, 0);
+MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion "
+ "(default=" __MODULE_STRING(START_DEFAULT) ")");
+
+static void pnx833x_wdt_start(void)
+{
+ /* Enable watchdog causing reset. */
+ PNX833X_REG(PNX833X_RESET + PNX833X_RESET_CONFIG) |= 0x1;
+ /* Set timeout.*/
+ PNX833X_REG(PNX833X_CONFIG +
+ PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout;
+ /* Enable watchdog. */
+ PNX833X_REG(PNX833X_CONFIG +
+ PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1;
+
+ printk(KERN_INFO PFX "Started watchdog timer.\n");
+}
+
+static void pnx833x_wdt_stop(void)
+{
+ /* Disable watchdog causing reset. */
+ PNX833X_REG(PNX833X_RESET + PNX833X_CONFIG) &= 0xFFFFFFFE;
+ /* Disable watchdog.*/
+ PNX833X_REG(PNX833X_CONFIG +
+ PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE;
+
+ printk(KERN_INFO PFX "Stopped watchdog timer.\n");
+}
+
+static void pnx833x_wdt_ping(void)
+{
+ PNX833X_REG(PNX833X_CONFIG +
+ PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout;
+}
+
+/*
+ * Allow only one person to hold it open
+ */
+static int pnx833x_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &pnx833x_wdt_alive))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Activate timer */
+ if (!start_enabled)
+ pnx833x_wdt_start();
+
+ pnx833x_wdt_ping();
+
+ printk(KERN_INFO "Started watchdog timer.\n");
+
+ return nonseekable_open(inode, file);
+}
+
+static int pnx833x_wdt_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer.
+ * Lock it in if it's a module and we defined ...NOWAYOUT */
+ if (!nowayout)
+ pnx833x_wdt_stop(); /* Turn the WDT off */
+
+ clear_bit(0, &pnx833x_wdt_alive);
+ return 0;
+}
+
+static ssize_t pnx833x_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+ /* Refresh the timer. */
+ if (len)
+ pnx833x_wdt_ping();
+
+ return len;
+}
+
+static long pnx833x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int options, new_timeout = 0;
+ uint32_t timeout, timeout_left = 0;
+
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
+ .firmware_version = 0,
+ .identity = "Hardware Watchdog for PNX833x",
+ };
+
+ switch (cmd) {
+ default:
+ return -ENOTTY;
+
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg,
+ &ident, sizeof(ident)))
+ return -EFAULT;
+ return 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, (int *)arg);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(options, (int *)arg))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD)
+ pnx833x_wdt_stop();
+
+ if (options & WDIOS_ENABLECARD)
+ pnx833x_wdt_start();
+
+ return 0;
+
+ case WDIOC_KEEPALIVE:
+ pnx833x_wdt_ping();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ {
+ if (get_user(new_timeout, (int *)arg))
+ return -EFAULT;
+
+ pnx833x_wdt_timeout = new_timeout;
+ PNX833X_REG(PNX833X_CONFIG +
+ PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = new_timeout;
+ return put_user(new_timeout, (int *)arg);
+ }
+
+ case WDIOC_GETTIMEOUT:
+ timeout = PNX833X_REG(PNX833X_CONFIG +
+ PNX833X_CONFIG_CPU_WATCHDOG_COMPARE);
+ return put_user(timeout, (int *)arg);
+
+ case WDIOC_GETTIMELEFT:
+ timeout_left = PNX833X_REG(PNX833X_CONFIG +
+ PNX833X_CONFIG_CPU_WATCHDOG);
+ return put_user(timeout_left, (int *)arg);
+
+ }
+}
+
+static int pnx833x_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ pnx833x_wdt_stop(); /* Turn the WDT off */
+
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations pnx833x_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = pnx833x_wdt_write,
+ .unlocked_ioctl = pnx833x_wdt_ioctl,
+ .open = pnx833x_wdt_open,
+ .release = pnx833x_wdt_release,
+};
+
+static struct miscdevice pnx833x_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &pnx833x_wdt_fops,
+};
+
+static struct notifier_block pnx833x_wdt_notifier = {
+ .notifier_call = pnx833x_wdt_notify_sys,
+};
+
+static char banner[] __initdata =
+ KERN_INFO PFX "Hardware Watchdog Timer for PNX833x: Version 0.1\n";
+
+static int __init watchdog_init(void)
+{
+ int ret, cause;
+
+ /* Lets check the reason for the reset.*/
+ cause = PNX833X_REG(PNX833X_RESET);
+ /*If bit 31 is set then watchdog was cause of reset.*/
+ if (cause & 0x80000000) {
+ printk(KERN_INFO PFX "The system was previously reset due to "
+ "the watchdog firing - please investigate...\n");
+ }
+
+ ret = register_reboot_notifier(&pnx833x_wdt_notifier);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ return ret;
+ }
+
+ ret = misc_register(&pnx833x_wdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ unregister_reboot_notifier(&pnx833x_wdt_notifier);
+ return ret;
+ }
+
+ printk(banner);
+ if (start_enabled)
+ pnx833x_wdt_start();
+
+ return 0;
+}
+
+static void __exit watchdog_exit(void)
+{
+ misc_deregister(&pnx833x_wdt_miscdev);
+ unregister_reboot_notifier(&pnx833x_wdt_notifier);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+
+MODULE_AUTHOR("Daniel Laird/Andre McCurdy");
+MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c
new file mode 100644
index 00000000..d4c29b53
--- /dev/null
+++ b/drivers/watchdog/rc32434_wdt.c
@@ -0,0 +1,352 @@
+/*
+ * IDT Interprise 79RC32434 watchdog driver
+ *
+ * Copyright (C) 2006, Ondrej Zajicek <santiago@crfreenet.org>
+ * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
+ *
+ * based on
+ * SoftDog 0.05: A Software Watchdog Device
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h> /* For module specific items */
+#include <linux/moduleparam.h> /* For new moduleparam's */
+#include <linux/types.h> /* For standard types (like size_t) */
+#include <linux/errno.h> /* For the -ENODEV/... values */
+#include <linux/kernel.h> /* For printk/panic/... */
+#include <linux/fs.h> /* For file operations */
+#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
+ (WATCHDOG_MINOR) */
+#include <linux/watchdog.h> /* For the watchdog specific items */
+#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/platform_device.h> /* For platform_driver framework */
+#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
+#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+
+#include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */
+
+#define PFX KBUILD_MODNAME ": "
+
+#define VERSION "1.0"
+
+static struct {
+ unsigned long inuse;
+ spinlock_t io_lock;
+} rc32434_wdt_device;
+
+static struct integ __iomem *wdt_reg;
+
+static int expect_close;
+
+/* Board internal clock speed in Hz,
+ * the watchdog timer ticks at. */
+extern unsigned int idt_cpu_freq;
+
+/* translate wtcompare value to seconds and vice versa */
+#define WTCOMP2SEC(x) (x / idt_cpu_freq)
+#define SEC2WTCOMP(x) (x * idt_cpu_freq)
+
+/* Use a default timeout of 20s. This should be
+ * safe for CPU clock speeds up to 400MHz, as
+ * ((2 ^ 32) - 1) / (400MHz / 2) = 21s. */
+#define WATCHDOG_TIMEOUT 20
+
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout value, in seconds (default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/* apply or and nand masks to data read from addr and write back */
+#define SET_BITS(addr, or, nand) \
+ writel((readl(&addr) | or) & ~nand, &addr)
+
+static int rc32434_wdt_set(int new_timeout)
+{
+ int max_to = WTCOMP2SEC((u32)-1);
+
+ if (new_timeout < 0 || new_timeout > max_to) {
+ printk(KERN_ERR PFX "timeout value must be between 0 and %d",
+ max_to);
+ return -EINVAL;
+ }
+ timeout = new_timeout;
+ spin_lock(&rc32434_wdt_device.io_lock);
+ writel(SEC2WTCOMP(timeout), &wdt_reg->wtcompare);
+ spin_unlock(&rc32434_wdt_device.io_lock);
+
+ return 0;
+}
+
+static void rc32434_wdt_start(void)
+{
+ u32 or, nand;
+
+ spin_lock(&rc32434_wdt_device.io_lock);
+
+ /* zero the counter before enabling */
+ writel(0, &wdt_reg->wtcount);
+
+ /* don't generate a non-maskable interrupt,
+ * do a warm reset instead */
+ nand = 1 << RC32434_ERR_WNE;
+ or = 1 << RC32434_ERR_WRE;
+
+ /* reset the ERRCS timeout bit in case it's set */
+ nand |= 1 << RC32434_ERR_WTO;
+
+ SET_BITS(wdt_reg->errcs, or, nand);
+
+ /* set the timeout (either default or based on module param) */
+ rc32434_wdt_set(timeout);
+
+ /* reset WTC timeout bit and enable WDT */
+ nand = 1 << RC32434_WTC_TO;
+ or = 1 << RC32434_WTC_EN;
+
+ SET_BITS(wdt_reg->wtc, or, nand);
+
+ spin_unlock(&rc32434_wdt_device.io_lock);
+ printk(KERN_INFO PFX "Started watchdog timer.\n");
+}
+
+static void rc32434_wdt_stop(void)
+{
+ spin_lock(&rc32434_wdt_device.io_lock);
+
+ /* Disable WDT */
+ SET_BITS(wdt_reg->wtc, 0, 1 << RC32434_WTC_EN);
+
+ spin_unlock(&rc32434_wdt_device.io_lock);
+ printk(KERN_INFO PFX "Stopped watchdog timer.\n");
+}
+
+static void rc32434_wdt_ping(void)
+{
+ spin_lock(&rc32434_wdt_device.io_lock);
+ writel(0, &wdt_reg->wtcount);
+ spin_unlock(&rc32434_wdt_device.io_lock);
+}
+
+static int rc32434_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &rc32434_wdt_device.inuse))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ rc32434_wdt_start();
+ rc32434_wdt_ping();
+
+ return nonseekable_open(inode, file);
+}
+
+static int rc32434_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ rc32434_wdt_stop();
+ module_put(THIS_MODULE);
+ } else {
+ printk(KERN_CRIT PFX
+ "device closed unexpectedly. WDT will not stop!\n");
+ rc32434_wdt_ping();
+ }
+ clear_bit(0, &rc32434_wdt_device.inuse);
+ return 0;
+}
+
+static ssize_t rc32434_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ rc32434_wdt_ping();
+ return len;
+ }
+ return 0;
+}
+
+static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int new_timeout;
+ unsigned int value;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .identity = "RC32434_WDT Watchdog",
+ };
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ value = 0;
+ if (copy_to_user(argp, &value, sizeof(int)))
+ return -EFAULT;
+ break;
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&value, argp, sizeof(int)))
+ return -EFAULT;
+ switch (value) {
+ case WDIOS_ENABLECARD:
+ rc32434_wdt_start();
+ break;
+ case WDIOS_DISABLECARD:
+ rc32434_wdt_stop();
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case WDIOC_KEEPALIVE:
+ rc32434_wdt_ping();
+ break;
+ case WDIOC_SETTIMEOUT:
+ if (copy_from_user(&new_timeout, argp, sizeof(int)))
+ return -EFAULT;
+ if (rc32434_wdt_set(new_timeout))
+ return -EINVAL;
+ /* Fall through */
+ case WDIOC_GETTIMEOUT:
+ return copy_to_user(argp, &timeout, sizeof(int));
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static const struct file_operations rc32434_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = rc32434_wdt_write,
+ .unlocked_ioctl = rc32434_wdt_ioctl,
+ .open = rc32434_wdt_open,
+ .release = rc32434_wdt_release,
+};
+
+static struct miscdevice rc32434_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &rc32434_wdt_fops,
+};
+
+static char banner[] __devinitdata = KERN_INFO PFX
+ "Watchdog Timer version " VERSION ", timer margin: %d sec\n";
+
+static int __devinit rc32434_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb532_wdt_res");
+ if (!r) {
+ printk(KERN_ERR PFX "failed to retrieve resources\n");
+ return -ENODEV;
+ }
+
+ wdt_reg = ioremap_nocache(r->start, resource_size(r));
+ if (!wdt_reg) {
+ printk(KERN_ERR PFX "failed to remap I/O resources\n");
+ return -ENXIO;
+ }
+
+ spin_lock_init(&rc32434_wdt_device.io_lock);
+
+ /* Make sure the watchdog is not running */
+ rc32434_wdt_stop();
+
+ /* Check that the heartbeat value is within it's range;
+ * if not reset to the default */
+ if (rc32434_wdt_set(timeout)) {
+ rc32434_wdt_set(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be between 0 and %d\n",
+ WTCOMP2SEC((u32)-1));
+ }
+
+ ret = misc_register(&rc32434_wdt_miscdev);
+ if (ret < 0) {
+ printk(KERN_ERR PFX "failed to register watchdog device\n");
+ goto unmap;
+ }
+
+ printk(banner, timeout);
+
+ return 0;
+
+unmap:
+ iounmap(wdt_reg);
+ return ret;
+}
+
+static int __devexit rc32434_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&rc32434_wdt_miscdev);
+ iounmap(wdt_reg);
+ return 0;
+}
+
+static void rc32434_wdt_shutdown(struct platform_device *pdev)
+{
+ rc32434_wdt_stop();
+}
+
+static struct platform_driver rc32434_wdt_driver = {
+ .probe = rc32434_wdt_probe,
+ .remove = __devexit_p(rc32434_wdt_remove),
+ .shutdown = rc32434_wdt_shutdown,
+ .driver = {
+ .name = "rc32434_wdt",
+ }
+};
+
+static int __init rc32434_wdt_init(void)
+{
+ return platform_driver_register(&rc32434_wdt_driver);
+}
+
+static void __exit rc32434_wdt_exit(void)
+{
+ platform_driver_unregister(&rc32434_wdt_driver);
+}
+
+module_init(rc32434_wdt_init);
+module_exit(rc32434_wdt_exit);
+
+MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>,"
+ "Florian Fainelli <florian@openwrt.org>");
+MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c
new file mode 100644
index 00000000..428f8a15
--- /dev/null
+++ b/drivers/watchdog/rdc321x_wdt.c
@@ -0,0 +1,312 @@
+/*
+ * RDC321x watchdog driver
+ *
+ * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
+ *
+ * This driver is highly inspired from the cpu5_wdt driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/completion.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/rdc321x.h>
+
+#define RDC_WDT_MASK 0x80000000 /* Mask */
+#define RDC_WDT_EN 0x00800000 /* Enable bit */
+#define RDC_WDT_WTI 0x00200000 /* Generate CPU reset/NMI/WDT on timeout */
+#define RDC_WDT_RST 0x00100000 /* Reset bit */
+#define RDC_WDT_WIF 0x00040000 /* WDT IRQ Flag */
+#define RDC_WDT_IRT 0x00000100 /* IRQ Routing table */
+#define RDC_WDT_CNT 0x00000001 /* WDT count */
+
+#define RDC_CLS_TMR 0x80003844 /* Clear timer */
+
+#define RDC_WDT_INTERVAL (HZ/10+1)
+
+static int ticks = 1000;
+
+/* some device data */
+
+static struct {
+ struct completion stop;
+ int running;
+ struct timer_list timer;
+ int queue;
+ int default_ticks;
+ unsigned long inuse;
+ spinlock_t lock;
+ struct pci_dev *sb_pdev;
+ int base_reg;
+} rdc321x_wdt_device;
+
+/* generic helper functions */
+
+static void rdc321x_wdt_trigger(unsigned long unused)
+{
+ unsigned long flags;
+ u32 val;
+
+ if (rdc321x_wdt_device.running)
+ ticks--;
+
+ /* keep watchdog alive */
+ spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
+ pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
+ rdc321x_wdt_device.base_reg, &val);
+ val |= RDC_WDT_EN;
+ pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
+ rdc321x_wdt_device.base_reg, val);
+ spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
+
+ /* requeue?? */
+ if (rdc321x_wdt_device.queue && ticks)
+ mod_timer(&rdc321x_wdt_device.timer,
+ jiffies + RDC_WDT_INTERVAL);
+ else {
+ /* ticks doesn't matter anyway */
+ complete(&rdc321x_wdt_device.stop);
+ }
+
+}
+
+static void rdc321x_wdt_reset(void)
+{
+ ticks = rdc321x_wdt_device.default_ticks;
+}
+
+static void rdc321x_wdt_start(void)
+{
+ unsigned long flags;
+
+ if (!rdc321x_wdt_device.queue) {
+ rdc321x_wdt_device.queue = 1;
+
+ /* Clear the timer */
+ spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
+ pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
+ rdc321x_wdt_device.base_reg, RDC_CLS_TMR);
+
+ /* Enable watchdog and set the timeout to 81.92 us */
+ pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
+ rdc321x_wdt_device.base_reg,
+ RDC_WDT_EN | RDC_WDT_CNT);
+ spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
+
+ mod_timer(&rdc321x_wdt_device.timer,
+ jiffies + RDC_WDT_INTERVAL);
+ }
+
+ /* if process dies, counter is not decremented */
+ rdc321x_wdt_device.running++;
+}
+
+static int rdc321x_wdt_stop(void)
+{
+ if (rdc321x_wdt_device.running)
+ rdc321x_wdt_device.running = 0;
+
+ ticks = rdc321x_wdt_device.default_ticks;
+
+ return -EIO;
+}
+
+/* filesystem operations */
+static int rdc321x_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &rdc321x_wdt_device.inuse))
+ return -EBUSY;
+
+ return nonseekable_open(inode, file);
+}
+
+static int rdc321x_wdt_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &rdc321x_wdt_device.inuse);
+ return 0;
+}
+
+static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ u32 value;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET,
+ .identity = "RDC321x WDT",
+ };
+ unsigned long flags;
+
+ switch (cmd) {
+ case WDIOC_KEEPALIVE:
+ rdc321x_wdt_reset();
+ break;
+ case WDIOC_GETSTATUS:
+ /* Read the value from the DATA register */
+ spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
+ pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
+ rdc321x_wdt_device.base_reg, &value);
+ spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
+ if (copy_to_user(argp, &value, sizeof(u32)))
+ return -EFAULT;
+ break;
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&value, argp, sizeof(int)))
+ return -EFAULT;
+ switch (value) {
+ case WDIOS_ENABLECARD:
+ rdc321x_wdt_start();
+ break;
+ case WDIOS_DISABLECARD:
+ return rdc321x_wdt_stop();
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (!count)
+ return -EIO;
+
+ rdc321x_wdt_reset();
+
+ return count;
+}
+
+static const struct file_operations rdc321x_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = rdc321x_wdt_ioctl,
+ .open = rdc321x_wdt_open,
+ .write = rdc321x_wdt_write,
+ .release = rdc321x_wdt_release,
+};
+
+static struct miscdevice rdc321x_wdt_misc = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &rdc321x_wdt_fops,
+};
+
+static int __devinit rdc321x_wdt_probe(struct platform_device *pdev)
+{
+ int err;
+ struct resource *r;
+ struct rdc321x_wdt_pdata *pdata;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data supplied\n");
+ return -ENODEV;
+ }
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg");
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get wdt-reg resource\n");
+ return -ENODEV;
+ }
+
+ rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
+ rdc321x_wdt_device.base_reg = r->start;
+
+ err = misc_register(&rdc321x_wdt_misc);
+ if (err < 0) {
+ dev_err(&pdev->dev, "misc_register failed\n");
+ return err;
+ }
+
+ spin_lock_init(&rdc321x_wdt_device.lock);
+
+ /* Reset the watchdog */
+ pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
+ rdc321x_wdt_device.base_reg, RDC_WDT_RST);
+
+ init_completion(&rdc321x_wdt_device.stop);
+ rdc321x_wdt_device.queue = 0;
+
+ clear_bit(0, &rdc321x_wdt_device.inuse);
+
+ setup_timer(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0);
+
+ rdc321x_wdt_device.default_ticks = ticks;
+
+ dev_info(&pdev->dev, "watchdog init success\n");
+
+ return 0;
+}
+
+static int __devexit rdc321x_wdt_remove(struct platform_device *pdev)
+{
+ if (rdc321x_wdt_device.queue) {
+ rdc321x_wdt_device.queue = 0;
+ wait_for_completion(&rdc321x_wdt_device.stop);
+ }
+
+ misc_deregister(&rdc321x_wdt_misc);
+
+ return 0;
+}
+
+static struct platform_driver rdc321x_wdt_driver = {
+ .probe = rdc321x_wdt_probe,
+ .remove = __devexit_p(rdc321x_wdt_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rdc321x-wdt",
+ },
+};
+
+static int __init rdc321x_wdt_init(void)
+{
+ return platform_driver_register(&rdc321x_wdt_driver);
+}
+
+static void __exit rdc321x_wdt_exit(void)
+{
+ platform_driver_unregister(&rdc321x_wdt_driver);
+}
+
+module_init(rdc321x_wdt_init);
+module_exit(rdc321x_wdt_exit);
+
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_DESCRIPTION("RDC321x watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c
new file mode 100644
index 00000000..109b5338
--- /dev/null
+++ b/drivers/watchdog/riowd.c
@@ -0,0 +1,261 @@
+/* riowd.c - driver for hw watchdog inside Super I/O of RIO
+ *
+ * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+
+/* RIO uses the NatSemi Super I/O power management logical device
+ * as its' watchdog.
+ *
+ * When the watchdog triggers, it asserts a line to the BBC (Boot Bus
+ * Controller) of the machine. The BBC can only be configured to
+ * trigger a power-on reset when the signal is asserted. The BBC
+ * can be configured to ignore the signal entirely as well.
+ *
+ * The only Super I/O device register we care about is at index
+ * 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255).
+ * If set to zero, this disables the watchdog. When set, the system
+ * must periodically (before watchdog expires) clear (set to zero) and
+ * re-set the watchdog else it will trigger.
+ *
+ * There are two other indexed watchdog registers inside this Super I/O
+ * logical device, but they are unused. The first, at index 0x06 is
+ * the watchdog control and can be used to make the watchdog timer re-set
+ * when the PS/2 mouse or serial lines show activity. The second, at
+ * index 0x07 is merely a sampling of the line from the watchdog to the
+ * BBC.
+ *
+ * The watchdog device generates no interrupts.
+ */
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO");
+MODULE_SUPPORTED_DEVICE("watchdog");
+MODULE_LICENSE("GPL");
+
+#define DRIVER_NAME "riowd"
+#define PFX DRIVER_NAME ": "
+
+struct riowd {
+ void __iomem *regs;
+ spinlock_t lock;
+};
+
+static struct riowd *riowd_device;
+
+#define WDTO_INDEX 0x05
+
+static int riowd_timeout = 1; /* in minutes */
+module_param(riowd_timeout, int, 0);
+MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes");
+
+static void riowd_writereg(struct riowd *p, u8 val, int index)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->lock, flags);
+ writeb(index, p->regs + 0);
+ writeb(val, p->regs + 1);
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int riowd_open(struct inode *inode, struct file *filp)
+{
+ nonseekable_open(inode, filp);
+ return 0;
+}
+
+static int riowd_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static long riowd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ static const struct watchdog_info info = {
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 1,
+ .identity = DRIVER_NAME,
+ };
+ void __user *argp = (void __user *)arg;
+ struct riowd *p = riowd_device;
+ unsigned int options;
+ int new_margin;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int __user *)argp))
+ return -EFAULT;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&options, argp, sizeof(options)))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD)
+ riowd_writereg(p, 0, WDTO_INDEX);
+ else if (options & WDIOS_ENABLECARD)
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ else
+ return -EINVAL;
+
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)argp))
+ return -EFAULT;
+ if ((new_margin < 60) || (new_margin > (255 * 60)))
+ return -EINVAL;
+ riowd_timeout = (new_margin + 59) / 60;
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(riowd_timeout * 60, (int __user *)argp);
+
+ default:
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+static ssize_t riowd_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct riowd *p = riowd_device;
+
+ if (count) {
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct file_operations riowd_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = riowd_ioctl,
+ .open = riowd_open,
+ .write = riowd_write,
+ .release = riowd_release,
+};
+
+static struct miscdevice riowd_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &riowd_fops
+};
+
+static int __devinit riowd_probe(struct platform_device *op)
+{
+ struct riowd *p;
+ int err = -EINVAL;
+
+ if (riowd_device)
+ goto out;
+
+ err = -ENOMEM;
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ goto out;
+
+ spin_lock_init(&p->lock);
+
+ p->regs = of_ioremap(&op->resource[0], 0, 2, DRIVER_NAME);
+ if (!p->regs) {
+ printk(KERN_ERR PFX "Cannot map registers.\n");
+ goto out_free;
+ }
+ /* Make miscdev useable right away */
+ riowd_device = p;
+
+ err = misc_register(&riowd_miscdev);
+ if (err) {
+ printk(KERN_ERR PFX "Cannot register watchdog misc device.\n");
+ goto out_iounmap;
+ }
+
+ printk(KERN_INFO PFX "Hardware watchdog [%i minutes], "
+ "regs at %p\n", riowd_timeout, p->regs);
+
+ dev_set_drvdata(&op->dev, p);
+ return 0;
+
+out_iounmap:
+ riowd_device = NULL;
+ of_iounmap(&op->resource[0], p->regs, 2);
+
+out_free:
+ kfree(p);
+
+out:
+ return err;
+}
+
+static int __devexit riowd_remove(struct platform_device *op)
+{
+ struct riowd *p = dev_get_drvdata(&op->dev);
+
+ misc_deregister(&riowd_miscdev);
+ of_iounmap(&op->resource[0], p->regs, 2);
+ kfree(p);
+
+ return 0;
+}
+
+static const struct of_device_id riowd_match[] = {
+ {
+ .name = "pmc",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, riowd_match);
+
+static struct platform_driver riowd_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = riowd_match,
+ },
+ .probe = riowd_probe,
+ .remove = __devexit_p(riowd_remove),
+};
+
+static int __init riowd_init(void)
+{
+ return platform_driver_register(&riowd_driver);
+}
+
+static void __exit riowd_exit(void)
+{
+ platform_driver_unregister(&riowd_driver);
+}
+
+module_init(riowd_init);
+module_exit(riowd_exit);
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
new file mode 100644
index 00000000..f7f5aa00
--- /dev/null
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -0,0 +1,628 @@
+/* linux/drivers/char/watchdog/s3c2410_wdt.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 Watchdog Timer Support
+ *
+ * Based on, softdog.c by Alan Cox,
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+
+#include <mach/map.h>
+
+#undef S3C_VA_WATCHDOG
+#define S3C_VA_WATCHDOG (0)
+
+#include <plat/regs-watchdog.h>
+
+#define PFX "s3c2410-wdt: "
+
+#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
+#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
+static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;
+static int soft_noboot;
+static int debug;
+
+module_param(tmr_margin, int, 0);
+module_param(tmr_atboot, int, 0);
+module_param(nowayout, int, 0);
+module_param(soft_noboot, int, 0);
+module_param(debug, int, 0);
+
+MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="
+ __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")");
+MODULE_PARM_DESC(tmr_atboot,
+ "Watchdog is started at boot time if set to 1, default="
+ __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
+ "0 to reboot (default 0)");
+MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)");
+
+static unsigned long open_lock;
+static struct device *wdt_dev; /* platform device attached to */
+static struct resource *wdt_mem;
+static struct resource *wdt_irq;
+static struct clk *wdt_clock;
+static void __iomem *wdt_base;
+static unsigned int wdt_count;
+static char expect_close;
+static DEFINE_SPINLOCK(wdt_lock);
+
+/* watchdog control routines */
+
+#define DBG(msg...) do { \
+ if (debug) \
+ printk(KERN_INFO msg); \
+ } while (0)
+
+/* functions */
+
+static void s3c2410wdt_keepalive(void)
+{
+ spin_lock(&wdt_lock);
+ writel(wdt_count, wdt_base + S3C2410_WTCNT);
+ spin_unlock(&wdt_lock);
+}
+
+static void __s3c2410wdt_stop(void)
+{
+ unsigned long wtcon;
+
+ wtcon = readl(wdt_base + S3C2410_WTCON);
+ wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
+ writel(wtcon, wdt_base + S3C2410_WTCON);
+}
+
+static void s3c2410wdt_stop(void)
+{
+ spin_lock(&wdt_lock);
+ __s3c2410wdt_stop();
+ spin_unlock(&wdt_lock);
+}
+
+static void s3c2410wdt_start(void)
+{
+ unsigned long wtcon;
+
+ spin_lock(&wdt_lock);
+
+ __s3c2410wdt_stop();
+
+ wtcon = readl(wdt_base + S3C2410_WTCON);
+ wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
+
+ if (soft_noboot) {
+ wtcon |= S3C2410_WTCON_INTEN;
+ wtcon &= ~S3C2410_WTCON_RSTEN;
+ } else {
+ wtcon &= ~S3C2410_WTCON_INTEN;
+ wtcon |= S3C2410_WTCON_RSTEN;
+ }
+
+ DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",
+ __func__, wdt_count, wtcon);
+
+ writel(wdt_count, wdt_base + S3C2410_WTDAT);
+ writel(wdt_count, wdt_base + S3C2410_WTCNT);
+ writel(wtcon, wdt_base + S3C2410_WTCON);
+ spin_unlock(&wdt_lock);
+}
+
+static inline int s3c2410wdt_is_running(void)
+{
+ return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
+}
+
+static int s3c2410wdt_set_heartbeat(int timeout)
+{
+ unsigned long freq = clk_get_rate(wdt_clock);
+ unsigned int count;
+ unsigned int divisor = 1;
+ unsigned long wtcon;
+
+ if (timeout < 1)
+ return -EINVAL;
+
+ freq /= 128;
+ count = timeout * freq;
+
+ DBG("%s: count=%d, timeout=%d, freq=%lu\n",
+ __func__, count, timeout, freq);
+
+ /* if the count is bigger than the watchdog register,
+ then work out what we need to do (and if) we can
+ actually make this value
+ */
+
+ if (count >= 0x10000) {
+ for (divisor = 1; divisor <= 0x100; divisor++) {
+ if ((count / divisor) < 0x10000)
+ break;
+ }
+
+ if ((count / divisor) >= 0x10000) {
+ dev_err(wdt_dev, "timeout %d too big\n", timeout);
+ return -EINVAL;
+ }
+ }
+
+ tmr_margin = timeout;
+
+ DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
+ __func__, timeout, divisor, count, count/divisor);
+
+ count /= divisor;
+ wdt_count = count;
+
+ /* update the pre-scaler */
+ wtcon = readl(wdt_base + S3C2410_WTCON);
+ wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
+ wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
+
+ writel(count, wdt_base + S3C2410_WTDAT);
+ writel(wtcon, wdt_base + S3C2410_WTCON);
+
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int s3c2410wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &open_lock))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ expect_close = 0;
+
+ /* start the timer */
+ s3c2410wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int s3c2410wdt_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ * Lock it in if it's a module and we set nowayout
+ */
+
+ if (expect_close == 42)
+ s3c2410wdt_stop();
+ else {
+ dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
+ s3c2410wdt_keepalive();
+ }
+ expect_close = 0;
+ clear_bit(0, &open_lock);
+ return 0;
+}
+
+static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /*
+ * Refresh the timer.
+ */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ s3c2410wdt_keepalive();
+ }
+ return len;
+}
+
+#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
+
+static const struct watchdog_info s3c2410_wdt_ident = {
+ .options = OPTIONS,
+ .firmware_version = 0,
+ .identity = "S3C2410 Watchdog",
+};
+
+
+static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_margin;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &s3c2410_wdt_ident,
+ sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_KEEPALIVE:
+ s3c2410wdt_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, p))
+ return -EFAULT;
+ if (s3c2410wdt_set_heartbeat(new_margin))
+ return -EINVAL;
+ s3c2410wdt_keepalive();
+ return put_user(tmr_margin, p);
+ case WDIOC_GETTIMEOUT:
+ return put_user(tmr_margin, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/* kernel interface */
+
+static const struct file_operations s3c2410wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = s3c2410wdt_write,
+ .unlocked_ioctl = s3c2410wdt_ioctl,
+ .open = s3c2410wdt_open,
+ .release = s3c2410wdt_release,
+};
+
+static struct miscdevice s3c2410wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &s3c2410wdt_fops,
+};
+
+/* interrupt handler code */
+
+static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
+{
+ dev_info(wdt_dev, "watchdog timer expired (irq)\n");
+
+ s3c2410wdt_keepalive();
+ return IRQ_HANDLED;
+}
+
+
+#ifdef CONFIG_CPU_FREQ
+
+static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ int ret;
+
+ if (!s3c2410wdt_is_running())
+ goto done;
+
+ if (val == CPUFREQ_PRECHANGE) {
+ /* To ensure that over the change we don't cause the
+ * watchdog to trigger, we perform an keep-alive if
+ * the watchdog is running.
+ */
+
+ s3c2410wdt_keepalive();
+ } else if (val == CPUFREQ_POSTCHANGE) {
+ s3c2410wdt_stop();
+
+ ret = s3c2410wdt_set_heartbeat(tmr_margin);
+
+ if (ret >= 0)
+ s3c2410wdt_start();
+ else
+ goto err;
+ }
+
+done:
+ return 0;
+
+ err:
+ dev_err(wdt_dev, "cannot set new value for timeout %d\n", tmr_margin);
+ return ret;
+}
+
+static struct notifier_block s3c2410wdt_cpufreq_transition_nb = {
+ .notifier_call = s3c2410wdt_cpufreq_transition,
+};
+
+static inline int s3c2410wdt_cpufreq_register(void)
+{
+ return cpufreq_register_notifier(&s3c2410wdt_cpufreq_transition_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3c2410wdt_cpufreq_deregister(void)
+{
+ cpufreq_unregister_notifier(&s3c2410wdt_cpufreq_transition_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3c2410wdt_cpufreq_register(void)
+{
+ return 0;
+}
+
+static inline void s3c2410wdt_cpufreq_deregister(void)
+{
+}
+#endif
+
+
+
+/* device interface */
+
+static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev;
+ unsigned int wtcon;
+ int started = 0;
+ int ret;
+ int size;
+
+ DBG("%s: probe=%p\n", __func__, pdev);
+
+ dev = &pdev->dev;
+ wdt_dev = &pdev->dev;
+
+ /* get the memory region for the watchdog timer */
+
+ wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (wdt_mem == NULL) {
+ dev_err(dev, "no memory resource specified\n");
+ return -ENOENT;
+ }
+
+ size = resource_size(wdt_mem);
+ if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
+ dev_err(dev, "failed to get memory region\n");
+ return -EBUSY;
+ }
+
+ wdt_base = ioremap(wdt_mem->start, size);
+ if (wdt_base == NULL) {
+ dev_err(dev, "failed to ioremap() region\n");
+ ret = -EINVAL;
+ goto err_req;
+ }
+
+ DBG("probe: mapped wdt_base=%p\n", wdt_base);
+
+ wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (wdt_irq == NULL) {
+ dev_err(dev, "no irq resource specified\n");
+ ret = -ENOENT;
+ goto err_map;
+ }
+
+ ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);
+ if (ret != 0) {
+ dev_err(dev, "failed to install irq (%d)\n", ret);
+ goto err_map;
+ }
+
+ wdt_clock = clk_get(&pdev->dev, "watchdog");
+ if (IS_ERR(wdt_clock)) {
+ dev_err(dev, "failed to find watchdog clock source\n");
+ ret = PTR_ERR(wdt_clock);
+ goto err_irq;
+ }
+
+ clk_enable(wdt_clock);
+
+ if (s3c2410wdt_cpufreq_register() < 0) {
+ printk(KERN_ERR PFX "failed to register cpufreq\n");
+ goto err_clk;
+ }
+
+ /* see if we can actually set the requested timer margin, and if
+ * not, try the default value */
+
+ if (s3c2410wdt_set_heartbeat(tmr_margin)) {
+ started = s3c2410wdt_set_heartbeat(
+ CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
+
+ if (started == 0)
+ dev_info(dev,
+ "tmr_margin value out of range, default %d used\n",
+ CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
+ else
+ dev_info(dev, "default timer value is out of range, "
+ "cannot start\n");
+ }
+
+ ret = misc_register(&s3c2410wdt_miscdev);
+ if (ret) {
+ dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto err_cpufreq;
+ }
+
+ if (tmr_atboot && started == 0) {
+ dev_info(dev, "starting watchdog timer\n");
+ s3c2410wdt_start();
+ } else if (!tmr_atboot) {
+ /* if we're not enabling the watchdog, then ensure it is
+ * disabled if it has been left running from the bootloader
+ * or other source */
+
+ s3c2410wdt_stop();
+ }
+
+ /* print out a statement of readiness */
+
+ wtcon = readl(wdt_base + S3C2410_WTCON);
+
+ dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
+ (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
+ (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",
+ (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");
+
+ return 0;
+
+ err_cpufreq:
+ s3c2410wdt_cpufreq_deregister();
+
+ err_clk:
+ clk_disable(wdt_clock);
+ clk_put(wdt_clock);
+
+ err_irq:
+ free_irq(wdt_irq->start, pdev);
+
+ err_map:
+ iounmap(wdt_base);
+
+ err_req:
+ release_mem_region(wdt_mem->start, size);
+ wdt_mem = NULL;
+
+ return ret;
+}
+
+static int __devexit s3c2410wdt_remove(struct platform_device *dev)
+{
+ misc_deregister(&s3c2410wdt_miscdev);
+
+ s3c2410wdt_cpufreq_deregister();
+
+ clk_disable(wdt_clock);
+ clk_put(wdt_clock);
+ wdt_clock = NULL;
+
+ free_irq(wdt_irq->start, dev);
+ wdt_irq = NULL;
+
+ iounmap(wdt_base);
+
+ release_mem_region(wdt_mem->start, resource_size(wdt_mem));
+ wdt_mem = NULL;
+ return 0;
+}
+
+static void s3c2410wdt_shutdown(struct platform_device *dev)
+{
+ s3c2410wdt_stop();
+}
+
+#ifdef CONFIG_PM
+
+static unsigned long wtcon_save;
+static unsigned long wtdat_save;
+
+static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
+{
+ /* Save watchdog state, and turn it off. */
+ wtcon_save = readl(wdt_base + S3C2410_WTCON);
+ wtdat_save = readl(wdt_base + S3C2410_WTDAT);
+
+ /* Note that WTCNT doesn't need to be saved. */
+ s3c2410wdt_stop();
+
+ return 0;
+}
+
+static int s3c2410wdt_resume(struct platform_device *dev)
+{
+ /* Restore watchdog state. */
+
+ writel(wtdat_save, wdt_base + S3C2410_WTDAT);
+ writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
+ writel(wtcon_save, wdt_base + S3C2410_WTCON);
+
+ printk(KERN_INFO PFX "watchdog %sabled\n",
+ (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
+
+ return 0;
+}
+
+#else
+#define s3c2410wdt_suspend NULL
+#define s3c2410wdt_resume NULL
+#endif /* CONFIG_PM */
+
+
+static struct platform_driver s3c2410wdt_driver = {
+ .probe = s3c2410wdt_probe,
+ .remove = __devexit_p(s3c2410wdt_remove),
+ .shutdown = s3c2410wdt_shutdown,
+ .suspend = s3c2410wdt_suspend,
+ .resume = s3c2410wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "s3c2410-wdt",
+ },
+};
+
+
+static char banner[] __initdata =
+ KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";
+
+static int __init watchdog_init(void)
+{
+ printk(banner);
+ return platform_driver_register(&s3c2410wdt_driver);
+}
+
+static void __exit watchdog_exit(void)
+{
+ platform_driver_unregister(&s3c2410wdt_driver);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "
+ "Dimitry Andric <dimitry.andric@tomtom.com>");
+MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:s3c2410-wdt");
diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c
new file mode 100644
index 00000000..01624541
--- /dev/null
+++ b/drivers/watchdog/sa1100_wdt.c
@@ -0,0 +1,193 @@
+/*
+ * Watchdog driver for the SA11x0/PXA2xx
+ *
+ * (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
+ * Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Oleg Drokin nor iXcelerator.com admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * 27/11/2000 Initial release
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <linux/timex.h>
+
+#ifdef CONFIG_ARCH_PXA
+#include <mach/regs-ost.h>
+#endif
+
+#include <mach/reset.h>
+#include <mach/hardware.h>
+
+static unsigned long oscr_freq;
+static unsigned long sa1100wdt_users;
+static unsigned int pre_margin;
+static int boot_status;
+
+/*
+ * Allow only one person to hold it open
+ */
+static int sa1100dog_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(1, &sa1100wdt_users))
+ return -EBUSY;
+
+ /* Activate SA1100 Watchdog timer */
+ OSMR3 = OSCR + pre_margin;
+ OSSR = OSSR_M3;
+ OWER = OWER_WME;
+ OIER |= OIER_E3;
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * The watchdog cannot be disabled.
+ *
+ * Previous comments suggested that turning off the interrupt by
+ * clearing OIER[E3] would prevent the watchdog timing out but this
+ * does not appear to be true (at least on the PXA255).
+ */
+static int sa1100dog_release(struct inode *inode, struct file *file)
+{
+ printk(KERN_CRIT "WATCHDOG: Device closed - timer will not stop\n");
+ clear_bit(1, &sa1100wdt_users);
+ return 0;
+}
+
+static ssize_t sa1100dog_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len)
+ /* Refresh OSMR3 timer. */
+ OSMR3 = OSCR + pre_margin;
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT
+ | WDIOF_KEEPALIVEPING,
+ .identity = "SA1100/PXA255 Watchdog",
+ .firmware_version = 1,
+};
+
+static long sa1100dog_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user(argp, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, p);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(boot_status, p);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ OSMR3 = OSCR + pre_margin;
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, p);
+ if (ret)
+ break;
+
+ if (time <= 0 || (oscr_freq * (long long)time >= 0xffffffff)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ pre_margin = oscr_freq * time;
+ OSMR3 = OSCR + pre_margin;
+ /*fall through*/
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(pre_margin / oscr_freq, p);
+ break;
+ }
+ return ret;
+}
+
+static const struct file_operations sa1100dog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sa1100dog_write,
+ .unlocked_ioctl = sa1100dog_ioctl,
+ .open = sa1100dog_open,
+ .release = sa1100dog_release,
+};
+
+static struct miscdevice sa1100dog_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sa1100dog_fops,
+};
+
+static int margin __initdata = 60; /* (secs) Default is 1 minute */
+
+static int __init sa1100dog_init(void)
+{
+ int ret;
+
+ oscr_freq = get_clock_tick_rate();
+
+ /*
+ * Read the reset status, and save it for later. If
+ * we suspend, RCSR will be cleared, and the watchdog
+ * reset reason will be lost.
+ */
+ boot_status = (reset_status & RESET_STATUS_WATCHDOG) ?
+ WDIOF_CARDRESET : 0;
+ pre_margin = oscr_freq * margin;
+
+ ret = misc_register(&sa1100dog_miscdev);
+ if (ret == 0)
+ printk(KERN_INFO
+ "SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n",
+ margin);
+ return ret;
+}
+
+static void __exit sa1100dog_exit(void)
+{
+ misc_deregister(&sa1100dog_miscdev);
+}
+
+module_init(sa1100dog_init);
+module_exit(sa1100dog_exit);
+
+MODULE_AUTHOR("Oleg Drokin <green@crimea.edu>");
+MODULE_DESCRIPTION("SA1100/PXA2xx Watchdog");
+
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c
new file mode 100644
index 00000000..f31493e6
--- /dev/null
+++ b/drivers/watchdog/sb_wdog.c
@@ -0,0 +1,362 @@
+/*
+ * Watchdog driver for SiByte SB1 SoCs
+ *
+ * Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@lsi.com>
+ *
+ * This driver is intended to make the second of two hardware watchdogs
+ * on the Sibyte 12XX and 11XX SoCs available to the user. There are two
+ * such devices available on the SoC, but it seems that there isn't an
+ * enumeration class for watchdogs in Linux like there is for RTCs.
+ * The second is used rather than the first because it uses IRQ 1,
+ * thereby avoiding all that IRQ 0 problematic nonsense.
+ *
+ * I have not tried this driver on a 1480 processor; it might work
+ * just well enough to really screw things up.
+ *
+ * It is a simple timer, and there is an interrupt that is raised the
+ * first time the timer expires. The second time it expires, the chip
+ * is reset and there is no way to redirect that NMI. Which could
+ * be problematic in some cases where this chip is sitting on the HT
+ * bus and has just taken responsibility for providing a cache block.
+ * Since the reset can't be redirected to the external reset pin, it is
+ * possible that other HT connected processors might hang and not reset.
+ * For Linux, a soft reset would probably be even worse than a hard reset.
+ * There you have it.
+ *
+ * The timer takes 23 bits of a 64 bit register (?) as a count value,
+ * and decrements the count every microsecond, for a max value of
+ * 0x7fffff usec or about 8.3ish seconds.
+ *
+ * This watchdog borrows some user semantics from the softdog driver,
+ * in that if you close the fd, it leaves the watchdog running, unless
+ * you previously wrote a 'V' to the fd, in which case it disables
+ * the watchdog when you close the fd like some other drivers.
+ *
+ * Based on various other watchdog drivers, which are probably all
+ * loosely based on something Alan Cox wrote years ago.
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 1 or 2 as published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/interrupt.h>
+
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/sibyte/sb1250_scd.h>
+
+static DEFINE_SPINLOCK(sbwd_lock);
+
+/*
+ * set the initial count value of a timer
+ *
+ * wdog is the iomem address of the cfg register
+ */
+void sbwdog_set(char __iomem *wdog, unsigned long t)
+{
+ spin_lock(&sbwd_lock);
+ __raw_writeb(0, wdog);
+ __raw_writeq(t & 0x7fffffUL, wdog - 0x10);
+ spin_unlock(&sbwd_lock);
+}
+
+/*
+ * cause the timer to [re]load it's initial count and start counting
+ * all over again
+ *
+ * wdog is the iomem address of the cfg register
+ */
+void sbwdog_pet(char __iomem *wdog)
+{
+ spin_lock(&sbwd_lock);
+ __raw_writeb(__raw_readb(wdog) | 1, wdog);
+ spin_unlock(&sbwd_lock);
+}
+
+static unsigned long sbwdog_gate; /* keeps it to one thread only */
+static char __iomem *kern_dog = (char __iomem *)(IO_BASE + (A_SCD_WDOG_CFG_0));
+static char __iomem *user_dog = (char __iomem *)(IO_BASE + (A_SCD_WDOG_CFG_1));
+static unsigned long timeout = 0x7fffffUL; /* useconds: 8.3ish secs. */
+static int expect_close;
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "SiByte Watchdog",
+};
+
+/*
+ * Allow only a single thread to walk the dog
+ */
+static int sbwdog_open(struct inode *inode, struct file *file)
+{
+ nonseekable_open(inode, file);
+ if (test_and_set_bit(0, &sbwdog_gate))
+ return -EBUSY;
+ __module_get(THIS_MODULE);
+
+ /*
+ * Activate the timer
+ */
+ sbwdog_set(user_dog, timeout);
+ __raw_writeb(1, user_dog);
+
+ return 0;
+}
+
+/*
+ * Put the dog back in the kennel.
+ */
+static int sbwdog_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ __raw_writeb(0, user_dog);
+ module_put(THIS_MODULE);
+ } else {
+ printk(KERN_CRIT
+ "%s: Unexpected close, not stopping watchdog!\n",
+ ident.identity);
+ sbwdog_pet(user_dog);
+ }
+ clear_bit(0, &sbwdog_gate);
+ expect_close = 0;
+
+ return 0;
+}
+
+/*
+ * 42 - the answer
+ */
+static ssize_t sbwdog_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ int i;
+
+ if (len) {
+ /*
+ * restart the timer
+ */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ sbwdog_pet(user_dog);
+ }
+
+ return len;
+}
+
+static long sbwdog_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ unsigned long time;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, p);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ sbwdog_pet(user_dog);
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, p);
+ if (ret)
+ break;
+
+ time *= 1000000;
+ if (time > 0x7fffffUL) {
+ ret = -EINVAL;
+ break;
+ }
+ timeout = time;
+ sbwdog_set(user_dog, timeout);
+ sbwdog_pet(user_dog);
+
+ case WDIOC_GETTIMEOUT:
+ /*
+ * get the remaining count from the ... count register
+ * which is 1*8 before the config register
+ */
+ ret = put_user(__raw_readq(user_dog - 8) / 1000000, p);
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Notifier for system down
+ */
+static int sbwdog_notify_sys(struct notifier_block *this, unsigned long code,
+ void *erf)
+{
+ if (code == SYS_DOWN || code == SYS_HALT) {
+ /*
+ * sit and sit
+ */
+ __raw_writeb(0, user_dog);
+ __raw_writeb(0, kern_dog);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations sbwdog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sbwdog_write,
+ .unlocked_ioctl = sbwdog_ioctl,
+ .open = sbwdog_open,
+ .release = sbwdog_release,
+};
+
+static struct miscdevice sbwdog_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sbwdog_fops,
+};
+
+static struct notifier_block sbwdog_notifier = {
+ .notifier_call = sbwdog_notify_sys,
+};
+
+/*
+ * interrupt handler
+ *
+ * doesn't do a whole lot for user, but oh so cleverly written so kernel
+ * code can use it to re-up the watchdog, thereby saving the kernel from
+ * having to create and maintain a timer, just to tickle another timer,
+ * which is just so wrong.
+ */
+irqreturn_t sbwdog_interrupt(int irq, void *addr)
+{
+ unsigned long wd_init;
+ char *wd_cfg_reg = (char *)addr;
+ u8 cfg;
+
+ cfg = __raw_readb(wd_cfg_reg);
+ wd_init = __raw_readq(wd_cfg_reg - 8) & 0x7fffff;
+
+ /*
+ * if it's the second watchdog timer, it's for those users
+ */
+ if (wd_cfg_reg == user_dog)
+ printk(KERN_CRIT "%s in danger of initiating system reset "
+ "in %ld.%01ld seconds\n",
+ ident.identity,
+ wd_init / 1000000, (wd_init / 100000) % 10);
+ else
+ cfg |= 1;
+
+ __raw_writeb(cfg, wd_cfg_reg);
+
+ return IRQ_HANDLED;
+}
+
+static int __init sbwdog_init(void)
+{
+ int ret;
+
+ /*
+ * register a reboot notifier
+ */
+ ret = register_reboot_notifier(&sbwdog_notifier);
+ if (ret) {
+ printk(KERN_ERR
+ "%s: cannot register reboot notifier (err=%d)\n",
+ ident.identity, ret);
+ return ret;
+ }
+
+ /*
+ * get the resources
+ */
+
+ ret = request_irq(1, sbwdog_interrupt, IRQF_DISABLED | IRQF_SHARED,
+ ident.identity, (void *)user_dog);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to request irq 1 - %d\n",
+ ident.identity, ret);
+ goto out;
+ }
+
+ ret = misc_register(&sbwdog_miscdev);
+ if (ret == 0) {
+ printk(KERN_INFO "%s: timeout is %ld.%ld secs\n",
+ ident.identity,
+ timeout / 1000000, (timeout / 100000) % 10);
+ return 0;
+ }
+ free_irq(1, (void *)user_dog);
+out:
+ unregister_reboot_notifier(&sbwdog_notifier);
+
+ return ret;
+}
+
+static void __exit sbwdog_exit(void)
+{
+ misc_deregister(&sbwdog_miscdev);
+ free_irq(1, (void *)user_dog);
+ unregister_reboot_notifier(&sbwdog_notifier);
+}
+
+module_init(sbwdog_init);
+module_exit(sbwdog_exit);
+
+MODULE_AUTHOR("Andrew Sharp <andy.sharp@lsi.com>");
+MODULE_DESCRIPTION("SiByte Watchdog");
+
+module_param(timeout, ulong, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in microseconds (max/default 8388607 or 8.3ish secs)");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+/*
+ * example code that can be put in a platform code area to utilize the
+ * first watchdog timer for the kernels own purpose.
+
+void platform_wd_setup(void)
+{
+ int ret;
+
+ ret = request_irq(1, sbwdog_interrupt, IRQF_DISABLED | IRQF_SHARED,
+ "Kernel Watchdog", IOADDR(A_SCD_WDOG_CFG_0));
+ if (ret) {
+ printk(KERN_CRIT
+ "Watchdog IRQ zero(0) failed to be requested - %d\n", ret);
+ }
+}
+
+
+ */
diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c
new file mode 100644
index 00000000..626d0e8e
--- /dev/null
+++ b/drivers/watchdog/sbc60xxwdt.c
@@ -0,0 +1,398 @@
+/*
+ * 60xx Single Board Computer Watchdog Timer driver for Linux 2.2.x
+ *
+ * Based on acquirewdt.c by Alan Cox.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * The author does NOT admit liability nor provide warranty for
+ * any of this software. This material is provided "AS-IS" in
+ * the hope that it may be useful for others.
+ *
+ * (c) Copyright 2000 Jakob Oestergaard <jakob@unthought.net>
+ *
+ * 12/4 - 2000 [Initial revision]
+ * 25/4 - 2000 Added /dev/watchdog support
+ * 09/5 - 2001 [smj@oro.net] fixed fop_write to "return 1"
+ * on success
+ * 12/4 - 2002 [rob@osinvestor.com] eliminate fop_read
+ * fix possible wdt_is_open race
+ * add CONFIG_WATCHDOG_NOWAYOUT support
+ * remove lock_kernel/unlock_kernel pairs
+ * added KERN_* to printk's
+ * got rid of extraneous comments
+ * changed watchdog_info to correctly reflect what
+ * the driver offers
+ * added WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS,
+ * WDIOC_SETTIMEOUT, WDIOC_GETTIMEOUT, and
+ * WDIOC_SETOPTIONS ioctls
+ * 09/8 - 2003 [wim@iguana.be] cleanup of trailing spaces
+ * use module_param
+ * made timeout (the emulated heartbeat) a
+ * module_param
+ * made the keepalive ping an internal subroutine
+ * made wdt_stop and wdt_start module params
+ * added extra printk's for startup problems
+ * added MODULE_AUTHOR and MODULE_DESCRIPTION info
+ *
+ *
+ * This WDT driver is different from the other Linux WDT
+ * drivers in the following ways:
+ * *) The driver will ping the watchdog by itself, because this
+ * particular WDT has a very short timeout (one second) and it
+ * would be insane to count on any userspace daemon always
+ * getting scheduled within that time frame.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define OUR_NAME "sbc60xxwdt"
+#define PFX OUR_NAME ": "
+
+/*
+ * You must set these - The driver cannot probe for the settings
+ */
+
+static int wdt_stop = 0x45;
+module_param(wdt_stop, int, 0);
+MODULE_PARM_DESC(wdt_stop, "SBC60xx WDT 'stop' io port (default 0x45)");
+
+static int wdt_start = 0x443;
+module_param(wdt_start, int, 0);
+MODULE_PARM_DESC(wdt_start, "SBC60xx WDT 'start' io port (default 0x443)");
+
+/*
+ * The 60xx board can use watchdog timeout values from one second
+ * to several minutes. The default is one second, so if we reset
+ * the watchdog every ~250ms we should be safe.
+ */
+
+#define WDT_INTERVAL (HZ/4+1)
+
+/*
+ * We must not require too good response from the userspace daemon.
+ * Here we require the userspace daemon to send us a heartbeat
+ * char to /dev/watchdog every 30 seconds.
+ * If the daemon pulses us every 25 seconds, we can still afford
+ * a 5 second scheduling delay on the (high priority) daemon. That
+ * should be sufficient for a box under any load.
+ */
+
+#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds, multiplied by HZ to
+ get seconds to wait for a ping */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (1<=timeout<=3600, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static void wdt_timer_ping(unsigned long);
+static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0);
+static unsigned long next_heartbeat;
+static unsigned long wdt_is_open;
+static char wdt_expect_close;
+
+/*
+ * Whack the dog
+ */
+
+static void wdt_timer_ping(unsigned long data)
+{
+ /* If we got a heartbeat pulse within the WDT_US_INTERVAL
+ * we agree to ping the WDT
+ */
+ if (time_before(jiffies, next_heartbeat)) {
+ /* Ping the WDT by reading from wdt_start */
+ inb_p(wdt_start);
+ /* Re-set the timer interval */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+ } else
+ printk(KERN_WARNING PFX
+ "Heartbeat lost! Will not ping the watchdog\n");
+}
+
+/*
+ * Utility routines
+ */
+
+static void wdt_startup(void)
+{
+ next_heartbeat = jiffies + (timeout * HZ);
+
+ /* Start the timer */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+ printk(KERN_INFO PFX "Watchdog timer is now enabled.\n");
+}
+
+static void wdt_turnoff(void)
+{
+ /* Stop the timer */
+ del_timer(&timer);
+ inb_p(wdt_stop);
+ printk(KERN_INFO PFX "Watchdog timer is now disabled...\n");
+}
+
+static void wdt_keepalive(void)
+{
+ /* user land ping */
+ next_heartbeat = jiffies + (timeout * HZ);
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t fop_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (count) {
+ if (!nowayout) {
+ size_t ofs;
+
+ /* note: just in case someone wrote the
+ magic character five months ago... */
+ wdt_expect_close = 0;
+
+ /* scan to see whether or not we got the
+ magic character */
+ for (ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if (c == 'V')
+ wdt_expect_close = 42;
+ }
+ }
+
+ /* Well, anyhow someone wrote to us, we should
+ return that favour */
+ wdt_keepalive();
+ }
+ return count;
+}
+
+static int fop_open(struct inode *inode, struct file *file)
+{
+ /* Just in case we're already talking to someone... */
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Good, fire up the show */
+ wdt_startup();
+ return nonseekable_open(inode, file);
+}
+
+static int fop_close(struct inode *inode, struct file *file)
+{
+ if (wdt_expect_close == 42)
+ wdt_turnoff();
+ else {
+ del_timer(&timer);
+ printk(KERN_CRIT PFX
+ "device file closed unexpectedly. Will not stop the WDT!\n");
+ }
+ clear_bit(0, &wdt_is_open);
+ wdt_expect_close = 0;
+ return 0;
+}
+
+static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "SBC60xx",
+ };
+
+ 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:
+ {
+ int new_options, retval = -EINVAL;
+ if (get_user(new_options, p))
+ return -EFAULT;
+ if (new_options & WDIOS_DISABLECARD) {
+ wdt_turnoff();
+ retval = 0;
+ }
+ if (new_options & WDIOS_ENABLECARD) {
+ wdt_startup();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ {
+ int new_timeout;
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ /* arbitrary upper limit */
+ if (new_timeout < 1 || new_timeout > 3600)
+ return -EINVAL;
+
+ timeout = new_timeout;
+ wdt_keepalive();
+ /* Fall through */
+ }
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = fop_write,
+ .open = fop_open,
+ .release = fop_close,
+ .unlocked_ioctl = fop_ioctl,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_turnoff();
+ return NOTIFY_DONE;
+}
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static void __exit sbc60xxwdt_unload(void)
+{
+ wdt_turnoff();
+
+ /* Deregister */
+ misc_deregister(&wdt_miscdev);
+
+ unregister_reboot_notifier(&wdt_notifier);
+ if ((wdt_stop != 0x45) && (wdt_stop != wdt_start))
+ release_region(wdt_stop, 1);
+ release_region(wdt_start, 1);
+}
+
+static int __init sbc60xxwdt_init(void)
+{
+ int rc = -EBUSY;
+
+ if (timeout < 1 || timeout > 3600) { /* arbitrary upper limit */
+ timeout = WATCHDOG_TIMEOUT;
+ printk(KERN_INFO PFX
+ "timeout value must be 1 <= x <= 3600, using %d\n",
+ timeout);
+ }
+
+ if (!request_region(wdt_start, 1, "SBC 60XX WDT")) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ wdt_start);
+ rc = -EIO;
+ goto err_out;
+ }
+
+ /* We cannot reserve 0x45 - the kernel already has! */
+ if (wdt_stop != 0x45 && wdt_stop != wdt_start) {
+ if (!request_region(wdt_stop, 1, "SBC 60XX WDT")) {
+ printk(KERN_ERR PFX
+ "I/O address 0x%04x already in use\n",
+ wdt_stop);
+ rc = -EIO;
+ goto err_out_region1;
+ }
+ }
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out_region2;
+ }
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
+ goto err_out_reboot;
+ }
+ printk(KERN_INFO PFX
+ "WDT driver for 60XX single board computer initialised. "
+ "timeout=%d sec (nowayout=%d)\n", timeout, nowayout);
+
+ return 0;
+
+err_out_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+err_out_region2:
+ if (wdt_stop != 0x45 && wdt_stop != wdt_start)
+ release_region(wdt_stop, 1);
+err_out_region1:
+ release_region(wdt_start, 1);
+err_out:
+ return rc;
+}
+
+module_init(sbc60xxwdt_init);
+module_exit(sbc60xxwdt_unload);
+
+MODULE_AUTHOR("Jakob Oestergaard <jakob@unthought.net>");
+MODULE_DESCRIPTION("60xx Single Board Computer Watchdog Timer driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c
new file mode 100644
index 00000000..ff11504c
--- /dev/null
+++ b/drivers/watchdog/sbc7240_wdt.c
@@ -0,0 +1,324 @@
+/*
+ * NANO7240 SBC Watchdog device driver
+ *
+ * Based on w83877f.c by Scott Jennings,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * (c) Copyright 2007 Gilles GIGAN <gilles.gigan@jcu.edu.au>
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+#include <asm/system.h>
+
+#define SBC7240_PREFIX "sbc7240_wdt: "
+
+#define SBC7240_ENABLE_PORT 0x443
+#define SBC7240_DISABLE_PORT 0x043
+#define SBC7240_SET_TIMEOUT_PORT SBC7240_ENABLE_PORT
+#define SBC7240_MAGIC_CHAR 'V'
+
+#define SBC7240_TIMEOUT 30
+#define SBC7240_MAX_TIMEOUT 255
+static int timeout = SBC7240_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<="
+ __MODULE_STRING(SBC7240_MAX_TIMEOUT) ", default="
+ __MODULE_STRING(SBC7240_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Disable watchdog when closing device file");
+
+#define SBC7240_OPEN_STATUS_BIT 0
+#define SBC7240_ENABLED_STATUS_BIT 1
+#define SBC7240_EXPECT_CLOSE_STATUS_BIT 2
+static unsigned long wdt_status;
+
+/*
+ * Utility routines
+ */
+
+static void wdt_disable(void)
+{
+ /* disable the watchdog */
+ if (test_and_clear_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) {
+ inb_p(SBC7240_DISABLE_PORT);
+ printk(KERN_INFO SBC7240_PREFIX
+ "Watchdog timer is now disabled.\n");
+ }
+}
+
+static void wdt_enable(void)
+{
+ /* enable the watchdog */
+ if (!test_and_set_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) {
+ inb_p(SBC7240_ENABLE_PORT);
+ printk(KERN_INFO SBC7240_PREFIX
+ "Watchdog timer is now enabled.\n");
+ }
+}
+
+static int wdt_set_timeout(int t)
+{
+ if (t < 1 || t > SBC7240_MAX_TIMEOUT) {
+ printk(KERN_ERR SBC7240_PREFIX
+ "timeout value must be 1<=x<=%d\n",
+ SBC7240_MAX_TIMEOUT);
+ return -1;
+ }
+ /* set the timeout */
+ outb_p((unsigned)t, SBC7240_SET_TIMEOUT_PORT);
+ timeout = t;
+ printk(KERN_INFO SBC7240_PREFIX "timeout set to %d seconds\n", t);
+ return 0;
+}
+
+/* Whack the dog */
+static inline void wdt_keepalive(void)
+{
+ if (test_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status))
+ inb_p(SBC7240_ENABLE_PORT);
+}
+
+/*
+ * /dev/watchdog handling
+ */
+static ssize_t fop_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ size_t i;
+ char c;
+
+ if (count) {
+ if (!nowayout) {
+ clear_bit(SBC7240_EXPECT_CLOSE_STATUS_BIT,
+ &wdt_status);
+
+ /* is there a magic char ? */
+ for (i = 0; i != count; i++) {
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == SBC7240_MAGIC_CHAR) {
+ set_bit(SBC7240_EXPECT_CLOSE_STATUS_BIT,
+ &wdt_status);
+ break;
+ }
+ }
+ }
+
+ wdt_keepalive();
+ }
+
+ return count;
+}
+
+static int fop_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(SBC7240_OPEN_STATUS_BIT, &wdt_status))
+ return -EBUSY;
+
+ wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static int fop_close(struct inode *inode, struct file *file)
+{
+ if (test_and_clear_bit(SBC7240_EXPECT_CLOSE_STATUS_BIT, &wdt_status)
+ || !nowayout) {
+ wdt_disable();
+ } else {
+ printk(KERN_CRIT SBC7240_PREFIX
+ "Unexpected close, not stopping watchdog!\n");
+ wdt_keepalive();
+ }
+
+ clear_bit(SBC7240_OPEN_STATUS_BIT, &wdt_status);
+ return 0;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING|
+ WDIOF_SETTIMEOUT|
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "SBC7240",
+};
+
+
+static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((void __user *)arg, &ident, sizeof(ident))
+ ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, (int __user *)arg);
+ case WDIOC_SETOPTIONS:
+ {
+ int options;
+ int retval = -EINVAL;
+
+ if (get_user(options, (int __user *)arg))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ wdt_disable();
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ wdt_enable();
+ retval = 0;
+ }
+
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ {
+ int new_timeout;
+
+ if (get_user(new_timeout, (int __user *)arg))
+ return -EFAULT;
+
+ if (wdt_set_timeout(new_timeout))
+ return -EINVAL;
+
+ /* Fall through */
+ }
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, (int __user *)arg);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = fop_write,
+ .open = fop_open,
+ .release = fop_close,
+ .unlocked_ioctl = fop_ioctl,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_disable();
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static void __exit sbc7240_wdt_unload(void)
+{
+ printk(KERN_INFO SBC7240_PREFIX "Removing watchdog\n");
+ misc_deregister(&wdt_miscdev);
+
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(SBC7240_ENABLE_PORT, 1);
+}
+
+static int __init sbc7240_wdt_init(void)
+{
+ int rc = -EBUSY;
+
+ if (!request_region(SBC7240_ENABLE_PORT, 1, "SBC7240 WDT")) {
+ printk(KERN_ERR SBC7240_PREFIX
+ "I/O address 0x%04x already in use\n",
+ SBC7240_ENABLE_PORT);
+ rc = -EIO;
+ goto err_out;
+ }
+
+ /* The IO port 0x043 used to disable the watchdog
+ * is already claimed by the system timer, so we
+ * can't request_region() it ...*/
+
+ if (timeout < 1 || timeout > SBC7240_MAX_TIMEOUT) {
+ timeout = SBC7240_TIMEOUT;
+ printk(KERN_INFO SBC7240_PREFIX
+ "timeout value must be 1<=x<=%d, using %d\n",
+ SBC7240_MAX_TIMEOUT, timeout);
+ }
+ wdt_set_timeout(timeout);
+ wdt_disable();
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR SBC7240_PREFIX
+ "cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out_region;
+ }
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR SBC7240_PREFIX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
+ goto err_out_reboot_notifier;
+ }
+
+ printk(KERN_INFO SBC7240_PREFIX
+ "Watchdog driver for SBC7240 initialised (nowayout=%d)\n",
+ nowayout);
+
+ return 0;
+
+err_out_reboot_notifier:
+ unregister_reboot_notifier(&wdt_notifier);
+err_out_region:
+ release_region(SBC7240_ENABLE_PORT, 1);
+err_out:
+ return rc;
+}
+
+module_init(sbc7240_wdt_init);
+module_exit(sbc7240_wdt_unload);
+
+MODULE_AUTHOR("Gilles Gigan");
+MODULE_DESCRIPTION("Watchdog device driver for single board"
+ " computers EPIC Nano 7240 from iEi");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
diff --git a/drivers/watchdog/sbc8360.c b/drivers/watchdog/sbc8360.c
new file mode 100644
index 00000000..514ec230
--- /dev/null
+++ b/drivers/watchdog/sbc8360.c
@@ -0,0 +1,412 @@
+/*
+ * SBC8360 Watchdog driver
+ *
+ * (c) Copyright 2005 Webcon, Inc.
+ *
+ * Based on ib700wdt.c, which is based on advantechwdt.c which is based
+ * on acquirewdt.c which is based on wdt.c.
+ *
+ * (c) Copyright 2001 Charles Howes <chowes@vsol.net>
+ *
+ * Based on advantechwdt.c which is based on acquirewdt.c which
+ * is based on wdt.c.
+ *
+ * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ * Based on acquirewdt.c which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
+ * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ * Added timeout module option to override default
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+static unsigned long sbc8360_is_open;
+static char expect_close;
+
+#define PFX "sbc8360: "
+
+/*
+ *
+ * Watchdog Timer Configuration
+ *
+ * The function of the watchdog timer is to reset the system automatically
+ * and is defined at I/O port 0120H and 0121H. To enable the watchdog timer
+ * and allow the system to reset, write appropriate values from the table
+ * below to I/O port 0120H and 0121H. To disable the timer, write a zero
+ * value to I/O port 0121H for the system to stop the watchdog function.
+ *
+ * The following describes how the timer should be programmed (according to
+ * the vendor documentation)
+ *
+ * Enabling Watchdog:
+ * MOV AX,000AH (enable, phase I)
+ * MOV DX,0120H
+ * OUT DX,AX
+ * MOV AX,000BH (enable, phase II)
+ * MOV DX,0120H
+ * OUT DX,AX
+ * MOV AX,000nH (set multiplier n, from 1-4)
+ * MOV DX,0120H
+ * OUT DX,AX
+ * MOV AX,000mH (set base timer m, from 0-F)
+ * MOV DX,0121H
+ * OUT DX,AX
+ *
+ * Reset timer:
+ * MOV AX,000mH (same as set base timer, above)
+ * MOV DX,0121H
+ * OUT DX,AX
+ *
+ * Disabling Watchdog:
+ * MOV AX,0000H (a zero value)
+ * MOV DX,0120H
+ * OUT DX,AX
+ *
+ * Watchdog timeout configuration values:
+ * N
+ * M | 1 2 3 4
+ * --|----------------------------------
+ * 0 | 0.5s 5s 50s 100s
+ * 1 | 1s 10s 100s 200s
+ * 2 | 1.5s 15s 150s 300s
+ * 3 | 2s 20s 200s 400s
+ * 4 | 2.5s 25s 250s 500s
+ * 5 | 3s 30s 300s 600s
+ * 6 | 3.5s 35s 350s 700s
+ * 7 | 4s 40s 400s 800s
+ * 8 | 4.5s 45s 450s 900s
+ * 9 | 5s 50s 500s 1000s
+ * A | 5.5s 55s 550s 1100s
+ * B | 6s 60s 600s 1200s
+ * C | 6.5s 65s 650s 1300s
+ * D | 7s 70s 700s 1400s
+ * E | 7.5s 75s 750s 1500s
+ * F | 8s 80s 800s 1600s
+ *
+ * Another way to say the same things is:
+ * For N=1, Timeout = (M+1) * 0.5s
+ * For N=2, Timeout = (M+1) * 5s
+ * For N=3, Timeout = (M+1) * 50s
+ * For N=4, Timeout = (M+1) * 100s
+ *
+ */
+
+static int wd_times[64][2] = {
+ {0, 1}, /* 0 = 0.5s */
+ {1, 1}, /* 1 = 1s */
+ {2, 1}, /* 2 = 1.5s */
+ {3, 1}, /* 3 = 2s */
+ {4, 1}, /* 4 = 2.5s */
+ {5, 1}, /* 5 = 3s */
+ {6, 1}, /* 6 = 3.5s */
+ {7, 1}, /* 7 = 4s */
+ {8, 1}, /* 8 = 4.5s */
+ {9, 1}, /* 9 = 5s */
+ {0xA, 1}, /* 10 = 5.5s */
+ {0xB, 1}, /* 11 = 6s */
+ {0xC, 1}, /* 12 = 6.5s */
+ {0xD, 1}, /* 13 = 7s */
+ {0xE, 1}, /* 14 = 7.5s */
+ {0xF, 1}, /* 15 = 8s */
+ {0, 2}, /* 16 = 5s */
+ {1, 2}, /* 17 = 10s */
+ {2, 2}, /* 18 = 15s */
+ {3, 2}, /* 19 = 20s */
+ {4, 2}, /* 20 = 25s */
+ {5, 2}, /* 21 = 30s */
+ {6, 2}, /* 22 = 35s */
+ {7, 2}, /* 23 = 40s */
+ {8, 2}, /* 24 = 45s */
+ {9, 2}, /* 25 = 50s */
+ {0xA, 2}, /* 26 = 55s */
+ {0xB, 2}, /* 27 = 60s */
+ {0xC, 2}, /* 28 = 65s */
+ {0xD, 2}, /* 29 = 70s */
+ {0xE, 2}, /* 30 = 75s */
+ {0xF, 2}, /* 31 = 80s */
+ {0, 3}, /* 32 = 50s */
+ {1, 3}, /* 33 = 100s */
+ {2, 3}, /* 34 = 150s */
+ {3, 3}, /* 35 = 200s */
+ {4, 3}, /* 36 = 250s */
+ {5, 3}, /* 37 = 300s */
+ {6, 3}, /* 38 = 350s */
+ {7, 3}, /* 39 = 400s */
+ {8, 3}, /* 40 = 450s */
+ {9, 3}, /* 41 = 500s */
+ {0xA, 3}, /* 42 = 550s */
+ {0xB, 3}, /* 43 = 600s */
+ {0xC, 3}, /* 44 = 650s */
+ {0xD, 3}, /* 45 = 700s */
+ {0xE, 3}, /* 46 = 750s */
+ {0xF, 3}, /* 47 = 800s */
+ {0, 4}, /* 48 = 100s */
+ {1, 4}, /* 49 = 200s */
+ {2, 4}, /* 50 = 300s */
+ {3, 4}, /* 51 = 400s */
+ {4, 4}, /* 52 = 500s */
+ {5, 4}, /* 53 = 600s */
+ {6, 4}, /* 54 = 700s */
+ {7, 4}, /* 55 = 800s */
+ {8, 4}, /* 56 = 900s */
+ {9, 4}, /* 57 = 1000s */
+ {0xA, 4}, /* 58 = 1100s */
+ {0xB, 4}, /* 59 = 1200s */
+ {0xC, 4}, /* 60 = 1300s */
+ {0xD, 4}, /* 61 = 1400s */
+ {0xE, 4}, /* 62 = 1500s */
+ {0xF, 4} /* 63 = 1600s */
+};
+
+#define SBC8360_ENABLE 0x120
+#define SBC8360_BASETIME 0x121
+
+static int timeout = 27;
+static int wd_margin = 0xB;
+static int wd_multiplier = 2;
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Index into timeout table (0-63) (default=27 (60s))");
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Kernel methods.
+ */
+
+/* Activate and pre-configure watchdog */
+static void sbc8360_activate(void)
+{
+ /* Enable the watchdog */
+ outb(0x0A, SBC8360_ENABLE);
+ msleep_interruptible(100);
+ outb(0x0B, SBC8360_ENABLE);
+ msleep_interruptible(100);
+ /* Set timeout multiplier */
+ outb(wd_multiplier, SBC8360_ENABLE);
+ msleep_interruptible(100);
+ /* Nothing happens until first sbc8360_ping() */
+}
+
+/* Kernel pings watchdog */
+static void sbc8360_ping(void)
+{
+ /* Write the base timer register */
+ outb(wd_margin, SBC8360_BASETIME);
+}
+
+/* stop watchdog */
+static void sbc8360_stop(void)
+{
+ /* De-activate the watchdog */
+ outb(0, SBC8360_ENABLE);
+}
+
+/* Userspace pings kernel driver, or requests clean close */
+static ssize_t sbc8360_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ sbc8360_ping();
+ }
+ return count;
+}
+
+static int sbc8360_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &sbc8360_is_open))
+ return -EBUSY;
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Activate and ping once to start the countdown */
+ sbc8360_activate();
+ sbc8360_ping();
+ return nonseekable_open(inode, file);
+}
+
+static int sbc8360_close(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ sbc8360_stop();
+ else
+ printk(KERN_CRIT PFX "SBC8360 device closed unexpectedly. "
+ "SBC8360 will not stop!\n");
+
+ clear_bit(0, &sbc8360_is_open);
+ expect_close = 0;
+ return 0;
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int sbc8360_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ sbc8360_stop(); /* Disable the SBC8360 Watchdog */
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations sbc8360_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sbc8360_write,
+ .open = sbc8360_open,
+ .release = sbc8360_close,
+};
+
+static struct miscdevice sbc8360_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sbc8360_fops,
+};
+
+/*
+ * The SBC8360 needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block sbc8360_notifier = {
+ .notifier_call = sbc8360_notify_sys,
+};
+
+static int __init sbc8360_init(void)
+{
+ int res;
+ unsigned long int mseconds = 60000;
+
+ if (timeout < 0 || timeout > 63) {
+ printk(KERN_ERR PFX "Invalid timeout index (must be 0-63).\n");
+ res = -EINVAL;
+ goto out;
+ }
+
+ if (!request_region(SBC8360_ENABLE, 1, "SBC8360")) {
+ printk(KERN_ERR PFX "ENABLE method I/O %X is not available.\n",
+ SBC8360_ENABLE);
+ res = -EIO;
+ goto out;
+ }
+ if (!request_region(SBC8360_BASETIME, 1, "SBC8360")) {
+ printk(KERN_ERR PFX
+ "BASETIME method I/O %X is not available.\n",
+ SBC8360_BASETIME);
+ res = -EIO;
+ goto out_nobasetimereg;
+ }
+
+ res = register_reboot_notifier(&sbc8360_notifier);
+ if (res) {
+ printk(KERN_ERR PFX "Failed to register reboot notifier.\n");
+ goto out_noreboot;
+ }
+
+ res = misc_register(&sbc8360_miscdev);
+ if (res) {
+ printk(KERN_ERR PFX "failed to register misc device\n");
+ goto out_nomisc;
+ }
+
+ wd_margin = wd_times[timeout][0];
+ wd_multiplier = wd_times[timeout][1];
+
+ if (wd_multiplier == 1)
+ mseconds = (wd_margin + 1) * 500;
+ else if (wd_multiplier == 2)
+ mseconds = (wd_margin + 1) * 5000;
+ else if (wd_multiplier == 3)
+ mseconds = (wd_margin + 1) * 50000;
+ else if (wd_multiplier == 4)
+ mseconds = (wd_margin + 1) * 100000;
+
+ /* My kingdom for the ability to print "0.5 seconds" in the kernel! */
+ printk(KERN_INFO PFX "Timeout set at %ld ms.\n", mseconds);
+
+ return 0;
+
+out_nomisc:
+ unregister_reboot_notifier(&sbc8360_notifier);
+out_noreboot:
+ release_region(SBC8360_BASETIME, 1);
+out_nobasetimereg:
+ release_region(SBC8360_ENABLE, 1);
+out:
+ return res;
+}
+
+static void __exit sbc8360_exit(void)
+{
+ misc_deregister(&sbc8360_miscdev);
+ unregister_reboot_notifier(&sbc8360_notifier);
+ release_region(SBC8360_ENABLE, 1);
+ release_region(SBC8360_BASETIME, 1);
+}
+
+module_init(sbc8360_init);
+module_exit(sbc8360_exit);
+
+MODULE_AUTHOR("Ian E. Morgan <imorgan@webcon.ca>");
+MODULE_DESCRIPTION("SBC8360 watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.01");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+/* end of sbc8360.c */
diff --git a/drivers/watchdog/sbc_epx_c3.c b/drivers/watchdog/sbc_epx_c3.c
new file mode 100644
index 00000000..3066a512
--- /dev/null
+++ b/drivers/watchdog/sbc_epx_c3.c
@@ -0,0 +1,226 @@
+/*
+ * SBC EPX C3 0.1 A Hardware Watchdog Device for the Winsystems EPX-C3
+ * single board computer
+ *
+ * (c) Copyright 2006 Calin A. Culianu <calin@ajvar.org>, All Rights
+ * Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#define PFX "epx_c3: "
+static int epx_c3_alive;
+
+#define WATCHDOG_TIMEOUT 1 /* 1 sec default timeout */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#define EPXC3_WATCHDOG_CTL_REG 0x1ee /* write 1 to enable, 0 to disable */
+#define EPXC3_WATCHDOG_PET_REG 0x1ef /* write anything to pet once enabled */
+
+static void epx_c3_start(void)
+{
+ outb(1, EPXC3_WATCHDOG_CTL_REG);
+}
+
+static void epx_c3_stop(void)
+{
+
+ outb(0, EPXC3_WATCHDOG_CTL_REG);
+
+ printk(KERN_INFO PFX "Stopped watchdog timer.\n");
+}
+
+static void epx_c3_pet(void)
+{
+ outb(1, EPXC3_WATCHDOG_PET_REG);
+}
+
+/*
+ * Allow only one person to hold it open
+ */
+static int epx_c3_open(struct inode *inode, struct file *file)
+{
+ if (epx_c3_alive)
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Activate timer */
+ epx_c3_start();
+ epx_c3_pet();
+
+ epx_c3_alive = 1;
+ printk(KERN_INFO "Started watchdog timer.\n");
+
+ return nonseekable_open(inode, file);
+}
+
+static int epx_c3_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer.
+ * Lock it in if it's a module and we defined ...NOWAYOUT */
+ if (!nowayout)
+ epx_c3_stop(); /* Turn the WDT off */
+
+ epx_c3_alive = 0;
+
+ return 0;
+}
+
+static ssize_t epx_c3_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* Refresh the timer. */
+ if (len)
+ epx_c3_pet();
+ return len;
+}
+
+static long epx_c3_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int options, retval = -EINVAL;
+ int __user *argp = (void __user *)arg;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING,
+ .firmware_version = 0,
+ .identity = "Winsystems EPX-C3 H/W Watchdog",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ return 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, argp);
+ case WDIOC_SETOPTIONS:
+ if (get_user(options, argp))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ epx_c3_stop();
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ epx_c3_start();
+ retval = 0;
+ }
+
+ return retval;
+ case WDIOC_KEEPALIVE:
+ epx_c3_pet();
+ return 0;
+ case WDIOC_GETTIMEOUT:
+ return put_user(WATCHDOG_TIMEOUT, argp);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int epx_c3_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ epx_c3_stop(); /* Turn the WDT off */
+
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations epx_c3_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = epx_c3_write,
+ .unlocked_ioctl = epx_c3_ioctl,
+ .open = epx_c3_open,
+ .release = epx_c3_release,
+};
+
+static struct miscdevice epx_c3_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &epx_c3_fops,
+};
+
+static struct notifier_block epx_c3_notifier = {
+ .notifier_call = epx_c3_notify_sys,
+};
+
+static const char banner[] __initdata = KERN_INFO PFX
+ "Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n";
+
+static int __init watchdog_init(void)
+{
+ int ret;
+
+ if (!request_region(EPXC3_WATCHDOG_CTL_REG, 2, "epxc3_watchdog"))
+ return -EBUSY;
+
+ ret = register_reboot_notifier(&epx_c3_notifier);
+ if (ret) {
+ printk(KERN_ERR PFX "cannot register reboot notifier "
+ "(err=%d)\n", ret);
+ goto out;
+ }
+
+ ret = misc_register(&epx_c3_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX "cannot register miscdev on minor=%d "
+ "(err=%d)\n", WATCHDOG_MINOR, ret);
+ unregister_reboot_notifier(&epx_c3_notifier);
+ goto out;
+ }
+
+ printk(banner);
+
+ return 0;
+
+out:
+ release_region(EPXC3_WATCHDOG_CTL_REG, 2);
+ return ret;
+}
+
+static void __exit watchdog_exit(void)
+{
+ misc_deregister(&epx_c3_miscdev);
+ unregister_reboot_notifier(&epx_c3_notifier);
+ release_region(EPXC3_WATCHDOG_CTL_REG, 2);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+
+MODULE_AUTHOR("Calin A. Culianu <calin@ajvar.org>");
+MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC. "
+ "Note that there is no way to probe for this device -- "
+ "so only use it if you are *sure* you are running on this specific "
+ "SBC system from Winsystems! It writes to IO ports 0x1ee and 0x1ef!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c
new file mode 100644
index 00000000..d5d39946
--- /dev/null
+++ b/drivers/watchdog/sbc_fitpc2_wdt.c
@@ -0,0 +1,269 @@
+/*
+ * Watchdog driver for SBC-FITPC2 board
+ *
+ * Author: Denis Turischev <denis@compulab.co.il>
+ *
+ * Adapted from the IXP2000 watchdog driver by Deepak Saxena.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME " WATCHDOG: " fmt
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/dmi.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static unsigned int margin = 60; /* (secs) Default is 1 minute */
+static unsigned long wdt_status;
+static DEFINE_MUTEX(wdt_lock);
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+#define COMMAND_PORT 0x4c
+#define DATA_PORT 0x48
+
+#define IFACE_ON_COMMAND 1
+#define REBOOT_COMMAND 2
+
+#define WATCHDOG_NAME "SBC-FITPC2 Watchdog"
+
+static void wdt_send_data(unsigned char command, unsigned char data)
+{
+ outb(data, DATA_PORT);
+ msleep(200);
+ outb(command, COMMAND_PORT);
+ msleep(100);
+}
+
+static void wdt_enable(void)
+{
+ mutex_lock(&wdt_lock);
+ wdt_send_data(IFACE_ON_COMMAND, 1);
+ wdt_send_data(REBOOT_COMMAND, margin);
+ mutex_unlock(&wdt_lock);
+}
+
+static void wdt_disable(void)
+{
+ mutex_lock(&wdt_lock);
+ wdt_send_data(IFACE_ON_COMMAND, 0);
+ wdt_send_data(REBOOT_COMMAND, 0);
+ mutex_unlock(&wdt_lock);
+}
+
+static int fitpc2_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t fitpc2_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ size_t i;
+
+ if (!len)
+ return 0;
+
+ if (nowayout) {
+ len = 0;
+ goto out;
+ }
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+
+out:
+ wdt_enable();
+
+ return len;
+}
+
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = WATCHDOG_NAME,
+};
+
+
+static long fitpc2_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time < 31 || time > 255) {
+ ret = -EINVAL;
+ break;
+ }
+
+ margin = time;
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(margin, (int *)arg);
+ break;
+ }
+
+ return ret;
+}
+
+static int fitpc2_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
+ wdt_disable();
+ pr_info("Device disabled\n");
+ } else {
+ pr_warning("Device closed unexpectedly -"
+ " timer will not stop\n");
+ wdt_enable();
+ }
+
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static const struct file_operations fitpc2_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = fitpc2_wdt_write,
+ .unlocked_ioctl = fitpc2_wdt_ioctl,
+ .open = fitpc2_wdt_open,
+ .release = fitpc2_wdt_release,
+};
+
+static struct miscdevice fitpc2_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &fitpc2_wdt_fops,
+};
+
+static int __init fitpc2_wdt_init(void)
+{
+ int err;
+ const char *brd_name;
+
+ brd_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+ if (!brd_name || !strstr(brd_name, "SBC-FITPC2"))
+ return -ENODEV;
+
+ pr_info("%s found\n", brd_name);
+
+ if (!request_region(COMMAND_PORT, 1, WATCHDOG_NAME)) {
+ pr_err("I/O address 0x%04x already in use\n", COMMAND_PORT);
+ return -EIO;
+ }
+
+ if (!request_region(DATA_PORT, 1, WATCHDOG_NAME)) {
+ pr_err("I/O address 0x%04x already in use\n", DATA_PORT);
+ err = -EIO;
+ goto err_data_port;
+ }
+
+ if (margin < 31 || margin > 255) {
+ pr_err("margin must be in range 31 - 255"
+ " seconds, you tried to set %d\n", margin);
+ err = -EINVAL;
+ goto err_margin;
+ }
+
+ err = misc_register(&fitpc2_wdt_miscdev);
+ if (err) {
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, err);
+ goto err_margin;
+ }
+
+ return 0;
+
+err_margin:
+ release_region(DATA_PORT, 1);
+err_data_port:
+ release_region(COMMAND_PORT, 1);
+
+ return err;
+}
+
+static void __exit fitpc2_wdt_exit(void)
+{
+ misc_deregister(&fitpc2_wdt_miscdev);
+ release_region(DATA_PORT, 1);
+ release_region(COMMAND_PORT, 1);
+}
+
+module_init(fitpc2_wdt_init);
+module_exit(fitpc2_wdt_exit);
+
+MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
+MODULE_DESCRIPTION("SBC-FITPC2 Watchdog");
+
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c
new file mode 100644
index 00000000..c01daca8
--- /dev/null
+++ b/drivers/watchdog/sc1200wdt.c
@@ -0,0 +1,483 @@
+/*
+ * National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
+ * (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>,
+ * All Rights Reserved.
+ * Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * The author(s) of this software shall not be held liable for damages
+ * of any nature resulting due to the use of this software. This
+ * software is provided AS-IS with no warranties.
+ *
+ * Changelog:
+ * 20020220 Zwane Mwaikambo Code based on datasheet, no hardware.
+ * 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik
+ * and Alan Cox.
+ * 20020222 Zwane Mwaikambo Added probing.
+ * 20020225 Zwane Mwaikambo Added ISAPNP support.
+ * 20020412 Rob Radez Broke out start/stop functions
+ * <rob@osinvestor.com> Return proper status instead of
+ * temperature warning
+ * Add WDIOC_GETBOOTSTATUS and
+ * WDIOC_SETOPTIONS ioctls
+ * Fix CONFIG_WATCHDOG_NOWAYOUT
+ * 20020530 Joel Becker Add Matt Domsch's nowayout module
+ * option
+ * 20030116 Adam Belay Updated to the latest pnp code
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/pnp.h>
+#include <linux/fs.h>
+#include <linux/semaphore.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#define SC1200_MODULE_VER "build 20020303"
+#define SC1200_MODULE_NAME "sc1200wdt"
+#define PFX SC1200_MODULE_NAME ": "
+
+#define MAX_TIMEOUT 255 /* 255 minutes */
+#define PMIR (io) /* Power Management Index Register */
+#define PMDR (io+1) /* Power Management Data Register */
+
+/* Data Register indexes */
+#define FER1 0x00 /* Function enable register 1 */
+#define FER2 0x01 /* Function enable register 2 */
+#define PMC1 0x02 /* Power Management Ctrl 1 */
+#define PMC2 0x03 /* Power Management Ctrl 2 */
+#define PMC3 0x04 /* Power Management Ctrl 3 */
+#define WDTO 0x05 /* Watchdog timeout register */
+#define WDCF 0x06 /* Watchdog config register */
+#define WDST 0x07 /* Watchdog status register */
+
+/* WDCF bitfields - which devices assert WDO */
+#define KBC_IRQ 0x01 /* Keyboard Controller */
+#define MSE_IRQ 0x02 /* Mouse */
+#define UART1_IRQ 0x03 /* Serial0 */
+#define UART2_IRQ 0x04 /* Serial1 */
+/* 5 -7 are reserved */
+
+static char banner[] __initdata = PFX SC1200_MODULE_VER;
+static int timeout = 1;
+static int io = -1;
+static int io_len = 2; /* for non plug and play */
+static unsigned long open_flag;
+static char expect_close;
+static DEFINE_SPINLOCK(sc1200wdt_lock); /* io port access serialisation */
+
+#if defined CONFIG_PNP
+static int isapnp = 1;
+static struct pnp_dev *wdt_dev;
+
+module_param(isapnp, int, 0);
+MODULE_PARM_DESC(isapnp,
+ "When set to 0 driver ISA PnP support will be disabled");
+#endif
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "io port");
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+
+
+/* Read from Data Register */
+static inline void __sc1200wdt_read_data(unsigned char index,
+ unsigned char *data)
+{
+ outb_p(index, PMIR);
+ *data = inb(PMDR);
+}
+
+static void sc1200wdt_read_data(unsigned char index, unsigned char *data)
+{
+ spin_lock(&sc1200wdt_lock);
+ __sc1200wdt_read_data(index, data);
+ spin_unlock(&sc1200wdt_lock);
+}
+
+/* Write to Data Register */
+static inline void __sc1200wdt_write_data(unsigned char index,
+ unsigned char data)
+{
+ outb_p(index, PMIR);
+ outb(data, PMDR);
+}
+
+static inline void sc1200wdt_write_data(unsigned char index,
+ unsigned char data)
+{
+ spin_lock(&sc1200wdt_lock);
+ __sc1200wdt_write_data(index, data);
+ spin_unlock(&sc1200wdt_lock);
+}
+
+
+static void sc1200wdt_start(void)
+{
+ unsigned char reg;
+ spin_lock(&sc1200wdt_lock);
+
+ __sc1200wdt_read_data(WDCF, &reg);
+ /* assert WDO when any of the following interrupts are triggered too */
+ reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ);
+ __sc1200wdt_write_data(WDCF, reg);
+ /* set the timeout and get the ball rolling */
+ __sc1200wdt_write_data(WDTO, timeout);
+
+ spin_unlock(&sc1200wdt_lock);
+}
+
+static void sc1200wdt_stop(void)
+{
+ sc1200wdt_write_data(WDTO, 0);
+}
+
+/* This returns the status of the WDO signal, inactive high. */
+static inline int sc1200wdt_status(void)
+{
+ unsigned char ret;
+
+ sc1200wdt_read_data(WDST, &ret);
+ /* If the bit is inactive, the watchdog is enabled, so return
+ * KEEPALIVEPING which is a bit of a kludge because there's nothing
+ * else for enabled/disabled status
+ */
+ return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING;
+}
+
+static int sc1200wdt_open(struct inode *inode, struct file *file)
+{
+ /* allow one at a time */
+ if (test_and_set_bit(0, &open_flag))
+ return -EBUSY;
+
+ if (timeout > MAX_TIMEOUT)
+ timeout = MAX_TIMEOUT;
+
+ sc1200wdt_start();
+ printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout);
+
+ return nonseekable_open(inode, file);
+}
+
+
+static long sc1200wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_timeout;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = "PC87307/PC97307",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ return 0;
+
+ case WDIOC_GETSTATUS:
+ return put_user(sc1200wdt_status(), p);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ sc1200wdt_stop();
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ sc1200wdt_start();
+ retval = 0;
+ }
+
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ sc1200wdt_write_data(WDTO, timeout);
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ /* the API states this is given in secs */
+ new_timeout /= 60;
+ if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
+ return -EINVAL;
+ timeout = new_timeout;
+ sc1200wdt_write_data(WDTO, timeout);
+ /* fall through and return the new timeout */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout * 60, p);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+
+static int sc1200wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ sc1200wdt_stop();
+ printk(KERN_INFO PFX "Watchdog disabled\n");
+ } else {
+ sc1200wdt_write_data(WDTO, timeout);
+ printk(KERN_CRIT PFX
+ "Unexpected close!, timeout = %d min(s)\n", timeout);
+ }
+ clear_bit(0, &open_flag);
+ expect_close = 0;
+
+ return 0;
+}
+
+
+static ssize_t sc1200wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+
+ sc1200wdt_write_data(WDTO, timeout);
+ return len;
+ }
+
+ return 0;
+}
+
+
+static int sc1200wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ sc1200wdt_stop();
+
+ return NOTIFY_DONE;
+}
+
+
+static struct notifier_block sc1200wdt_notifier = {
+ .notifier_call = sc1200wdt_notify_sys,
+};
+
+static const struct file_operations sc1200wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sc1200wdt_write,
+ .unlocked_ioctl = sc1200wdt_ioctl,
+ .open = sc1200wdt_open,
+ .release = sc1200wdt_release,
+};
+
+static struct miscdevice sc1200wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sc1200wdt_fops,
+};
+
+
+static int __init sc1200wdt_probe(void)
+{
+ /* The probe works by reading the PMC3 register's default value of 0x0e
+ * there is one caveat, if the device disables the parallel port or any
+ * of the UARTs we won't be able to detect it.
+ * NB. This could be done with accuracy by reading the SID registers,
+ * but we don't have access to those io regions.
+ */
+
+ unsigned char reg;
+
+ sc1200wdt_read_data(PMC3, &reg);
+ reg &= 0x0f; /* we don't want the UART busy bits */
+ return (reg == 0x0e) ? 0 : -ENODEV;
+}
+
+
+#if defined CONFIG_PNP
+
+static struct pnp_device_id scl200wdt_pnp_devices[] = {
+ /* National Semiconductor PC87307/PC97307 watchdog component */
+ {.id = "NSC0800", .driver_data = 0},
+ {.id = ""},
+};
+
+static int scl200wdt_pnp_probe(struct pnp_dev *dev,
+ const struct pnp_device_id *dev_id)
+{
+ /* this driver only supports one card at a time */
+ if (wdt_dev || !isapnp)
+ return -EBUSY;
+
+ wdt_dev = dev;
+ io = pnp_port_start(wdt_dev, 0);
+ io_len = pnp_port_len(wdt_dev, 0);
+
+ if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
+ printk(KERN_ERR PFX "Unable to register IO port %#x\n", io);
+ return -EBUSY;
+ }
+
+ printk(KERN_INFO "scl200wdt: PnP device found at io port %#x/%d\n",
+ io, io_len);
+ return 0;
+}
+
+static void scl200wdt_pnp_remove(struct pnp_dev *dev)
+{
+ if (wdt_dev) {
+ release_region(io, io_len);
+ wdt_dev = NULL;
+ }
+}
+
+static struct pnp_driver scl200wdt_pnp_driver = {
+ .name = "scl200wdt",
+ .id_table = scl200wdt_pnp_devices,
+ .probe = scl200wdt_pnp_probe,
+ .remove = scl200wdt_pnp_remove,
+};
+
+#endif /* CONFIG_PNP */
+
+
+static int __init sc1200wdt_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "%s\n", banner);
+
+#if defined CONFIG_PNP
+ if (isapnp) {
+ ret = pnp_register_driver(&scl200wdt_pnp_driver);
+ if (ret)
+ goto out_clean;
+ }
+#endif
+
+ if (io == -1) {
+ printk(KERN_ERR PFX "io parameter must be specified\n");
+ ret = -EINVAL;
+ goto out_pnp;
+ }
+
+#if defined CONFIG_PNP
+ /* now that the user has specified an IO port and we haven't detected
+ * any devices, disable pnp support */
+ isapnp = 0;
+ pnp_unregister_driver(&scl200wdt_pnp_driver);
+#endif
+
+ if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
+ printk(KERN_ERR PFX "Unable to register IO port %#x\n", io);
+ ret = -EBUSY;
+ goto out_pnp;
+ }
+
+ ret = sc1200wdt_probe();
+ if (ret)
+ goto out_io;
+
+ ret = register_reboot_notifier(&sc1200wdt_notifier);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "Unable to register reboot notifier err = %d\n", ret);
+ goto out_io;
+ }
+
+ ret = misc_register(&sc1200wdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "Unable to register miscdev on minor %d\n",
+ WATCHDOG_MINOR);
+ goto out_rbt;
+ }
+
+ /* ret = 0 */
+
+out_clean:
+ return ret;
+
+out_rbt:
+ unregister_reboot_notifier(&sc1200wdt_notifier);
+
+out_io:
+ release_region(io, io_len);
+
+out_pnp:
+#if defined CONFIG_PNP
+ if (isapnp)
+ pnp_unregister_driver(&scl200wdt_pnp_driver);
+#endif
+ goto out_clean;
+}
+
+
+static void __exit sc1200wdt_exit(void)
+{
+ misc_deregister(&sc1200wdt_miscdev);
+ unregister_reboot_notifier(&sc1200wdt_notifier);
+
+#if defined CONFIG_PNP
+ if (isapnp)
+ pnp_unregister_driver(&scl200wdt_pnp_driver);
+ else
+#endif
+ release_region(io, io_len);
+}
+
+module_init(sc1200wdt_init);
+module_exit(sc1200wdt_exit);
+
+MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
+MODULE_DESCRIPTION(
+ "Driver for National Semiconductor PC87307/PC97307 watchdog component");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c
new file mode 100644
index 00000000..52b63f2f
--- /dev/null
+++ b/drivers/watchdog/sc520_wdt.c
@@ -0,0 +1,444 @@
+/*
+ * AMD Elan SC520 processor Watchdog Timer driver
+ *
+ * Based on acquirewdt.c by Alan Cox,
+ * and sbc60xxwdt.c by Jakob Oestergaard <jakob@unthought.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * The authors do NOT admit liability nor provide warranty for
+ * any of this software. This material is provided "AS-IS" in
+ * the hope that it may be useful for others.
+ *
+ * (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net>
+ * 9/27 - 2001 [Initial release]
+ *
+ * Additional fixes Alan Cox
+ * - Fixed formatting
+ * - Removed debug printks
+ * - Fixed SMP built kernel deadlock
+ * - Switched to private locks not lock_kernel
+ * - Used ioremap/writew/readw
+ * - Added NOWAYOUT support
+ * 4/12 - 2002 Changes by Rob Radez <rob@osinvestor.com>
+ * - Change comments
+ * - Eliminate fop_llseek
+ * - Change CONFIG_WATCHDOG_NOWAYOUT semantics
+ * - Add KERN_* tags to printks
+ * - fix possible wdt_is_open race
+ * - Report proper capabilities in watchdog_info
+ * - Add WDIOC_{GETSTATUS, GETBOOTSTATUS, SETTIMEOUT,
+ * GETTIMEOUT, SETOPTIONS} ioctls
+ * 09/8 - 2003 Changes by Wim Van Sebroeck <wim@iguana.be>
+ * - cleanup of trailing spaces
+ * - added extra printk's for startup problems
+ * - use module_param
+ * - made timeout (the emulated heartbeat) a module_param
+ * - made the keepalive ping an internal subroutine
+ * 3/27 - 2004 Changes by Sean Young <sean@mess.org>
+ * - set MMCR_BASE to 0xfffef000
+ * - CBAR does not need to be read
+ * - removed debugging printks
+ *
+ * This WDT driver is different from most other Linux WDT
+ * drivers in that the driver will ping the watchdog by itself,
+ * because this particular WDT has a very short timeout (1.6
+ * seconds) and it would be insane to count on any userspace
+ * daemon always getting scheduled within that time frame.
+ *
+ * This driver uses memory mapped IO, and spinlock.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define OUR_NAME "sc520_wdt"
+#define PFX OUR_NAME ": "
+
+/*
+ * The AMD Elan SC520 timeout value is 492us times a power of 2 (0-7)
+ *
+ * 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s
+ * 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s
+ *
+ * We will program the SC520 watchdog for a timeout of 2.01s.
+ * If we reset the watchdog every ~250ms we should be safe.
+ */
+
+#define WDT_INTERVAL (HZ/4+1)
+
+/*
+ * We must not require too good response from the userspace daemon.
+ * Here we require the userspace daemon to send us a heartbeat
+ * char to /dev/watchdog every 30 seconds.
+ */
+
+#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */
+/* in seconds, will be multiplied by HZ to get seconds to wait for a ping */
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (1 <= timeout <= 3600, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * AMD Elan SC520 - Watchdog Timer Registers
+ */
+#define MMCR_BASE 0xfffef000 /* The default base address */
+#define OFFS_WDTMRCTL 0xCB0 /* Watchdog Timer Control Register */
+
+/* WDT Control Register bit definitions */
+#define WDT_EXP_SEL_01 0x0001 /* [01] Time-out = 496 us (with 33 Mhz clk). */
+#define WDT_EXP_SEL_02 0x0002 /* [02] Time-out = 508 ms (with 33 Mhz clk). */
+#define WDT_EXP_SEL_03 0x0004 /* [03] Time-out = 1.02 s (with 33 Mhz clk). */
+#define WDT_EXP_SEL_04 0x0008 /* [04] Time-out = 2.03 s (with 33 Mhz clk). */
+#define WDT_EXP_SEL_05 0x0010 /* [05] Time-out = 4.07 s (with 33 Mhz clk). */
+#define WDT_EXP_SEL_06 0x0020 /* [06] Time-out = 8.13 s (with 33 Mhz clk). */
+#define WDT_EXP_SEL_07 0x0040 /* [07] Time-out = 16.27s (with 33 Mhz clk). */
+#define WDT_EXP_SEL_08 0x0080 /* [08] Time-out = 32.54s (with 33 Mhz clk). */
+#define WDT_IRQ_FLG 0x1000 /* [12] Interrupt Request Flag */
+#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */
+#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */
+
+static __u16 __iomem *wdtmrctl;
+
+static void wdt_timer_ping(unsigned long);
+static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0);
+static unsigned long next_heartbeat;
+static unsigned long wdt_is_open;
+static char wdt_expect_close;
+static DEFINE_SPINLOCK(wdt_spinlock);
+
+/*
+ * Whack the dog
+ */
+
+static void wdt_timer_ping(unsigned long data)
+{
+ /* If we got a heartbeat pulse within the WDT_US_INTERVAL
+ * we agree to ping the WDT
+ */
+ if (time_before(jiffies, next_heartbeat)) {
+ /* Ping the WDT */
+ spin_lock(&wdt_spinlock);
+ writew(0xAAAA, wdtmrctl);
+ writew(0x5555, wdtmrctl);
+ spin_unlock(&wdt_spinlock);
+
+ /* Re-set the timer interval */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+ } else
+ printk(KERN_WARNING PFX
+ "Heartbeat lost! Will not ping the watchdog\n");
+}
+
+/*
+ * Utility routines
+ */
+
+static void wdt_config(int writeval)
+{
+ __u16 dummy;
+ unsigned long flags;
+
+ /* buy some time (ping) */
+ spin_lock_irqsave(&wdt_spinlock, flags);
+ dummy = readw(wdtmrctl); /* ensure write synchronization */
+ writew(0xAAAA, wdtmrctl);
+ writew(0x5555, wdtmrctl);
+ /* unlock WDT = make WDT configuration register writable one time */
+ writew(0x3333, wdtmrctl);
+ writew(0xCCCC, wdtmrctl);
+ /* write WDT configuration register */
+ writew(writeval, wdtmrctl);
+ spin_unlock_irqrestore(&wdt_spinlock, flags);
+}
+
+static int wdt_startup(void)
+{
+ next_heartbeat = jiffies + (timeout * HZ);
+
+ /* Start the timer */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+
+ /* Start the watchdog */
+ wdt_config(WDT_ENB | WDT_WRST_ENB | WDT_EXP_SEL_04);
+
+ printk(KERN_INFO PFX "Watchdog timer is now enabled.\n");
+ return 0;
+}
+
+static int wdt_turnoff(void)
+{
+ /* Stop the timer */
+ del_timer(&timer);
+
+ /* Stop the watchdog */
+ wdt_config(0);
+
+ printk(KERN_INFO PFX "Watchdog timer is now disabled...\n");
+ return 0;
+}
+
+static int wdt_keepalive(void)
+{
+ /* user land ping */
+ next_heartbeat = jiffies + (timeout * HZ);
+ return 0;
+}
+
+static int wdt_set_heartbeat(int t)
+{
+ if ((t < 1) || (t > 3600)) /* arbitrary upper limit */
+ return -EINVAL;
+
+ timeout = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t fop_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (count) {
+ if (!nowayout) {
+ size_t ofs;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ wdt_expect_close = 0;
+
+ /* now scan */
+ for (ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if (c == 'V')
+ wdt_expect_close = 42;
+ }
+ }
+
+ /* Well, anyhow someone wrote to us, we should
+ return that favour */
+ wdt_keepalive();
+ }
+ return count;
+}
+
+static int fop_open(struct inode *inode, struct file *file)
+{
+ /* Just in case we're already talking to someone... */
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Good, fire up the show */
+ wdt_startup();
+ return nonseekable_open(inode, file);
+}
+
+static int fop_close(struct inode *inode, struct file *file)
+{
+ if (wdt_expect_close == 42)
+ wdt_turnoff();
+ else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ wdt_keepalive();
+ }
+ clear_bit(0, &wdt_is_open);
+ wdt_expect_close = 0;
+ return 0;
+}
+
+static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
+ | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "SC520",
+ };
+
+ 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:
+ {
+ int new_options, retval = -EINVAL;
+
+ if (get_user(new_options, p))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD) {
+ wdt_turnoff();
+ retval = 0;
+ }
+
+ if (new_options & WDIOS_ENABLECARD) {
+ wdt_startup();
+ retval = 0;
+ }
+
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ {
+ int new_timeout;
+
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+
+ if (wdt_set_heartbeat(new_timeout))
+ return -EINVAL;
+
+ wdt_keepalive();
+ /* Fall through */
+ }
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = fop_write,
+ .open = fop_open,
+ .release = fop_close,
+ .unlocked_ioctl = fop_ioctl,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_turnoff();
+ return NOTIFY_DONE;
+}
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static void __exit sc520_wdt_unload(void)
+{
+ if (!nowayout)
+ wdt_turnoff();
+
+ /* Deregister */
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ iounmap(wdtmrctl);
+}
+
+static int __init sc520_wdt_init(void)
+{
+ int rc = -EBUSY;
+
+ /* Check that the timeout value is within it's range ;
+ if not reset to the default */
+ if (wdt_set_heartbeat(timeout)) {
+ wdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 1 <= timeout <= 3600, using %d\n",
+ WATCHDOG_TIMEOUT);
+ }
+
+ wdtmrctl = ioremap((unsigned long)(MMCR_BASE + OFFS_WDTMRCTL), 2);
+ if (!wdtmrctl) {
+ printk(KERN_ERR PFX "Unable to remap memory\n");
+ rc = -ENOMEM;
+ goto err_out_region2;
+ }
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out_ioremap;
+ }
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, rc);
+ goto err_out_notifier;
+ }
+
+ printk(KERN_INFO PFX
+ "WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+ return 0;
+
+err_out_notifier:
+ unregister_reboot_notifier(&wdt_notifier);
+err_out_ioremap:
+ iounmap(wdtmrctl);
+err_out_region2:
+ return rc;
+}
+
+module_init(sc520_wdt_init);
+module_exit(sc520_wdt_unload);
+
+MODULE_AUTHOR("Scott and Bill Jennings");
+MODULE_DESCRIPTION(
+ "Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c
new file mode 100644
index 00000000..c7cf4b01
--- /dev/null
+++ b/drivers/watchdog/sch311x_wdt.c
@@ -0,0 +1,578 @@
+/*
+ * sch311x_wdt.c - Driver for the SCH311x Super-I/O chips
+ * integrated watchdog.
+ *
+ * (c) Copyright 2008 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
+ * provide warranty for any of this software. This material is
+ * provided "AS-IS" and at no charge.
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+/* Includes */
+#include <linux/module.h> /* For module specific items */
+#include <linux/moduleparam.h> /* For new moduleparam's */
+#include <linux/types.h> /* For standard types (like size_t) */
+#include <linux/errno.h> /* For the -ENODEV/... values */
+#include <linux/kernel.h> /* For printk/... */
+#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
+ (WATCHDOG_MINOR) */
+#include <linux/watchdog.h> /* For the watchdog specific items */
+#include <linux/init.h> /* For __init/__exit/... */
+#include <linux/fs.h> /* For file operations */
+#include <linux/platform_device.h> /* For platform_driver framework */
+#include <linux/ioport.h> /* For io-port access */
+#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
+#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+#include <linux/io.h> /* For inb/outb/... */
+
+/* Module and version information */
+#define DRV_NAME "sch311x_wdt"
+#define PFX DRV_NAME ": "
+
+/* Runtime registers */
+#define RESGEN 0x1d
+#define GP60 0x47
+#define WDT_TIME_OUT 0x65
+#define WDT_VAL 0x66
+#define WDT_CFG 0x67
+#define WDT_CTRL 0x68
+
+/* internal variables */
+static unsigned long sch311x_wdt_is_open;
+static char sch311x_wdt_expect_close;
+static struct platform_device *sch311x_wdt_pdev;
+
+static int sch311x_ioports[] = { 0x2e, 0x4e, 0x162e, 0x164e, 0x00 };
+
+static struct { /* The devices private data */
+ /* the Runtime Register base address */
+ unsigned short runtime_reg;
+ /* The card's boot status */
+ int boot_status;
+ /* the lock for io operations */
+ spinlock_t io_lock;
+} sch311x_wdt_data;
+
+/* Module load parameters */
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static unsigned short therm_trip;
+module_param(therm_trip, ushort, 0);
+MODULE_PARM_DESC(therm_trip, "Should a ThermTrip trigger the reset generator");
+
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <=15300, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Super-IO functions
+ */
+
+static inline void sch311x_sio_enter(int sio_config_port)
+{
+ outb(0x55, sio_config_port);
+}
+
+static inline void sch311x_sio_exit(int sio_config_port)
+{
+ outb(0xaa, sio_config_port);
+}
+
+static inline int sch311x_sio_inb(int sio_config_port, int reg)
+{
+ outb(reg, sio_config_port);
+ return inb(sio_config_port + 1);
+}
+
+static inline void sch311x_sio_outb(int sio_config_port, int reg, int val)
+{
+ outb(reg, sio_config_port);
+ outb(val, sio_config_port + 1);
+}
+
+/*
+ * Watchdog Operations
+ */
+
+static void sch311x_wdt_set_timeout(int t)
+{
+ unsigned char timeout_unit = 0x80;
+
+ /* When new timeout is bigger then 255 seconds, we will use minutes */
+ if (t > 255) {
+ timeout_unit = 0;
+ t /= 60;
+ }
+
+ /* -- Watchdog Timeout --
+ * Bit 0-6 (Reserved)
+ * Bit 7 WDT Time-out Value Units Select
+ * (0 = Minutes, 1 = Seconds)
+ */
+ outb(timeout_unit, sch311x_wdt_data.runtime_reg + WDT_TIME_OUT);
+
+ /* -- Watchdog Timer Time-out Value --
+ * Bit 0-7 Binary coded units (0=Disabled, 1..255)
+ */
+ outb(t, sch311x_wdt_data.runtime_reg + WDT_VAL);
+}
+
+static void sch311x_wdt_start(void)
+{
+ spin_lock(&sch311x_wdt_data.io_lock);
+
+ /* set watchdog's timeout */
+ sch311x_wdt_set_timeout(timeout);
+ /* enable the watchdog */
+ /* -- General Purpose I/O Bit 6.0 --
+ * Bit 0, In/Out: 0 = Output, 1 = Input
+ * Bit 1, Polarity: 0 = No Invert, 1 = Invert
+ * Bit 2-3, Function select: 00 = GPI/O, 01 = LED1, 11 = WDT,
+ * 10 = Either Edge Triggered Intr.4
+ * Bit 4-6 (Reserved)
+ * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain
+ */
+ outb(0x0e, sch311x_wdt_data.runtime_reg + GP60);
+
+ spin_unlock(&sch311x_wdt_data.io_lock);
+
+}
+
+static void sch311x_wdt_stop(void)
+{
+ spin_lock(&sch311x_wdt_data.io_lock);
+
+ /* stop the watchdog */
+ outb(0x01, sch311x_wdt_data.runtime_reg + GP60);
+ /* disable timeout by setting it to 0 */
+ sch311x_wdt_set_timeout(0);
+
+ spin_unlock(&sch311x_wdt_data.io_lock);
+}
+
+static void sch311x_wdt_keepalive(void)
+{
+ spin_lock(&sch311x_wdt_data.io_lock);
+ sch311x_wdt_set_timeout(timeout);
+ spin_unlock(&sch311x_wdt_data.io_lock);
+}
+
+static int sch311x_wdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > (255*60))
+ return -EINVAL;
+
+ /* When new timeout is bigger then 255 seconds,
+ * we will round up to minutes (with a max of 255) */
+ if (t > 255)
+ t = (((t - 1) / 60) + 1) * 60;
+
+ timeout = t;
+ return 0;
+}
+
+static void sch311x_wdt_get_status(int *status)
+{
+ unsigned char new_status;
+
+ *status = 0;
+
+ spin_lock(&sch311x_wdt_data.io_lock);
+
+ /* -- Watchdog timer control --
+ * Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occurred
+ * Bit 1 Reserved
+ * Bit 2 Force Timeout: 1 = Forces WD timeout event (self-cleaning)
+ * Bit 3 P20 Force Timeout enabled:
+ * 0 = P20 activity does not generate the WD timeout event
+ * 1 = P20 Allows rising edge of P20, from the keyboard
+ * controller, to force the WD timeout event.
+ * Bit 4-7 Reserved
+ */
+ new_status = inb(sch311x_wdt_data.runtime_reg + WDT_CTRL);
+ if (new_status & 0x01)
+ *status |= WDIOF_CARDRESET;
+
+ spin_unlock(&sch311x_wdt_data.io_lock);
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t sch311x_wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ sch311x_wdt_expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ sch311x_wdt_expect_close = 42;
+ }
+ }
+ sch311x_wdt_keepalive();
+ }
+ return count;
+}
+
+static long sch311x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int status;
+ int new_timeout;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = DRV_NAME,
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ {
+ sch311x_wdt_get_status(&status);
+ return put_user(status, p);
+ }
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(sch311x_wdt_data.boot_status, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+ if (options & WDIOS_DISABLECARD) {
+ sch311x_wdt_stop();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ sch311x_wdt_start();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ sch311x_wdt_keepalive();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (sch311x_wdt_set_heartbeat(new_timeout))
+ return -EINVAL;
+ sch311x_wdt_keepalive();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int sch311x_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &sch311x_wdt_is_open))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+ sch311x_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int sch311x_wdt_close(struct inode *inode, struct file *file)
+{
+ if (sch311x_wdt_expect_close == 42) {
+ sch311x_wdt_stop();
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ sch311x_wdt_keepalive();
+ }
+ clear_bit(0, &sch311x_wdt_is_open);
+ sch311x_wdt_expect_close = 0;
+ return 0;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations sch311x_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sch311x_wdt_write,
+ .unlocked_ioctl = sch311x_wdt_ioctl,
+ .open = sch311x_wdt_open,
+ .release = sch311x_wdt_close,
+};
+
+static struct miscdevice sch311x_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sch311x_wdt_fops,
+};
+
+/*
+ * Init & exit routines
+ */
+
+static int __devinit sch311x_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned char val;
+ int err;
+
+ spin_lock_init(&sch311x_wdt_data.io_lock);
+
+ if (!request_region(sch311x_wdt_data.runtime_reg + RESGEN, 1,
+ DRV_NAME)) {
+ dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
+ sch311x_wdt_data.runtime_reg + RESGEN,
+ sch311x_wdt_data.runtime_reg + RESGEN);
+ err = -EBUSY;
+ goto exit;
+ }
+
+ if (!request_region(sch311x_wdt_data.runtime_reg + GP60, 1, DRV_NAME)) {
+ dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
+ sch311x_wdt_data.runtime_reg + GP60,
+ sch311x_wdt_data.runtime_reg + GP60);
+ err = -EBUSY;
+ goto exit_release_region;
+ }
+
+ if (!request_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4,
+ DRV_NAME)) {
+ dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
+ sch311x_wdt_data.runtime_reg + WDT_TIME_OUT,
+ sch311x_wdt_data.runtime_reg + WDT_CTRL);
+ err = -EBUSY;
+ goto exit_release_region2;
+ }
+
+ /* Make sure that the watchdog is not running */
+ sch311x_wdt_stop();
+
+ /* Disable keyboard and mouse interaction and interrupt */
+ /* -- Watchdog timer configuration --
+ * Bit 0 Reserved
+ * Bit 1 Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr.
+ * Bit 2 Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr
+ * Bit 3 Reserved
+ * Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled,
+ * 0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15)
+ */
+ outb(0, sch311x_wdt_data.runtime_reg + WDT_CFG);
+
+ /* Check that the heartbeat value is within it's range ;
+ * if not reset to the default */
+ if (sch311x_wdt_set_heartbeat(timeout)) {
+ sch311x_wdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ dev_info(dev, "timeout value must be 1<=x<=15300, using %d\n",
+ timeout);
+ }
+
+ /* Get status at boot */
+ sch311x_wdt_get_status(&sch311x_wdt_data.boot_status);
+
+ /* enable watchdog */
+ /* -- Reset Generator --
+ * Bit 0 Enable Watchdog Timer Generation: 0* = Enabled, 1 = Disabled
+ * Bit 1 Thermtrip Source Select: O* = No Source, 1 = Source
+ * Bit 2 WDT2_CTL: WDT input bit
+ * Bit 3-7 Reserved
+ */
+ outb(0, sch311x_wdt_data.runtime_reg + RESGEN);
+ val = therm_trip ? 0x06 : 0x04;
+ outb(val, sch311x_wdt_data.runtime_reg + RESGEN);
+
+ sch311x_wdt_miscdev.parent = dev;
+
+ err = misc_register(&sch311x_wdt_miscdev);
+ if (err != 0) {
+ dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, err);
+ goto exit_release_region3;
+ }
+
+ dev_info(dev,
+ "SMSC SCH311x WDT initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+ return 0;
+
+exit_release_region3:
+ release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
+exit_release_region2:
+ release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
+exit_release_region:
+ release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1);
+ sch311x_wdt_data.runtime_reg = 0;
+exit:
+ return err;
+}
+
+static int __devexit sch311x_wdt_remove(struct platform_device *pdev)
+{
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ sch311x_wdt_stop();
+
+ /* Deregister */
+ misc_deregister(&sch311x_wdt_miscdev);
+ release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
+ release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
+ release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1);
+ sch311x_wdt_data.runtime_reg = 0;
+ return 0;
+}
+
+static void sch311x_wdt_shutdown(struct platform_device *dev)
+{
+ /* Turn the WDT off if we have a soft shutdown */
+ sch311x_wdt_stop();
+}
+
+#define sch311x_wdt_suspend NULL
+#define sch311x_wdt_resume NULL
+
+static struct platform_driver sch311x_wdt_driver = {
+ .probe = sch311x_wdt_probe,
+ .remove = __devexit_p(sch311x_wdt_remove),
+ .shutdown = sch311x_wdt_shutdown,
+ .suspend = sch311x_wdt_suspend,
+ .resume = sch311x_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+static int __init sch311x_detect(int sio_config_port, unsigned short *addr)
+{
+ int err = 0, reg;
+ unsigned short base_addr;
+ unsigned char dev_id;
+
+ sch311x_sio_enter(sio_config_port);
+
+ /* Check device ID. We currently know about:
+ * SCH3112 (0x7c), SCH3114 (0x7d), and SCH3116 (0x7f). */
+ reg = force_id ? force_id : sch311x_sio_inb(sio_config_port, 0x20);
+ if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) {
+ err = -ENODEV;
+ goto exit;
+ }
+ dev_id = reg == 0x7c ? 2 : reg == 0x7d ? 4 : 6;
+
+ /* Select logical device A (runtime registers) */
+ sch311x_sio_outb(sio_config_port, 0x07, 0x0a);
+
+ /* Check if Logical Device Register is currently active */
+ if ((sch311x_sio_inb(sio_config_port, 0x30) & 0x01) == 0)
+ printk(KERN_INFO PFX "Seems that LDN 0x0a is not active...\n");
+
+ /* Get the base address of the runtime registers */
+ base_addr = (sch311x_sio_inb(sio_config_port, 0x60) << 8) |
+ sch311x_sio_inb(sio_config_port, 0x61);
+ if (!base_addr) {
+ printk(KERN_ERR PFX "Base address not set.\n");
+ err = -ENODEV;
+ goto exit;
+ }
+ *addr = base_addr;
+
+ printk(KERN_INFO PFX "Found an SMSC SCH311%d chip at 0x%04x\n",
+ dev_id, base_addr);
+
+exit:
+ sch311x_sio_exit(sio_config_port);
+ return err;
+}
+
+static int __init sch311x_wdt_init(void)
+{
+ int err, i, found = 0;
+ unsigned short addr = 0;
+
+ for (i = 0; !found && sch311x_ioports[i]; i++)
+ if (sch311x_detect(sch311x_ioports[i], &addr) == 0)
+ found++;
+
+ if (!found)
+ return -ENODEV;
+
+ sch311x_wdt_data.runtime_reg = addr;
+
+ err = platform_driver_register(&sch311x_wdt_driver);
+ if (err)
+ return err;
+
+ sch311x_wdt_pdev = platform_device_register_simple(DRV_NAME, addr,
+ NULL, 0);
+
+ if (IS_ERR(sch311x_wdt_pdev)) {
+ err = PTR_ERR(sch311x_wdt_pdev);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&sch311x_wdt_driver);
+ return err;
+}
+
+static void __exit sch311x_wdt_exit(void)
+{
+ platform_device_unregister(sch311x_wdt_pdev);
+ platform_driver_unregister(&sch311x_wdt_driver);
+}
+
+module_init(sch311x_wdt_init);
+module_exit(sch311x_wdt_exit);
+
+MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
+MODULE_DESCRIPTION("SMSC SCH311x WatchDog Timer Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
diff --git a/drivers/watchdog/scx200_wdt.c b/drivers/watchdog/scx200_wdt.c
new file mode 100644
index 00000000..e67b76c0
--- /dev/null
+++ b/drivers/watchdog/scx200_wdt.c
@@ -0,0 +1,273 @@
+/* drivers/char/watchdog/scx200_wdt.c
+
+ National Semiconductor SCx200 Watchdog support
+
+ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+ Some code taken from:
+ National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
+ (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The author(s) of this software shall not be held liable for damages
+ of any nature resulting due to the use of this software. This
+ software is provided AS-IS with no warranties. */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/scx200.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#define NAME "scx200_wdt"
+
+MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
+MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+static int margin = 60; /* in seconds */
+module_param(margin, int, 0);
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+static u16 wdto_restart;
+static char expect_close;
+static unsigned long open_lock;
+static DEFINE_SPINLOCK(scx_lock);
+
+/* Bits of the WDCNFG register */
+#define W_ENABLE 0x00fa /* Enable watchdog */
+#define W_DISABLE 0x0000 /* Disable watchdog */
+
+/* The scaling factor for the timer, this depends on the value of W_ENABLE */
+#define W_SCALE (32768/1024)
+
+static void scx200_wdt_ping(void)
+{
+ spin_lock(&scx_lock);
+ outw(wdto_restart, scx200_cb_base + SCx200_WDT_WDTO);
+ spin_unlock(&scx_lock);
+}
+
+static void scx200_wdt_update_margin(void)
+{
+ printk(KERN_INFO NAME ": timer margin %d seconds\n", margin);
+ wdto_restart = margin * W_SCALE;
+}
+
+static void scx200_wdt_enable(void)
+{
+ printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n",
+ wdto_restart);
+
+ spin_lock(&scx_lock);
+ outw(0, scx200_cb_base + SCx200_WDT_WDTO);
+ outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS);
+ outw(W_ENABLE, scx200_cb_base + SCx200_WDT_WDCNFG);
+ spin_unlock(&scx_lock);
+
+ scx200_wdt_ping();
+}
+
+static void scx200_wdt_disable(void)
+{
+ printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
+
+ spin_lock(&scx_lock);
+ outw(0, scx200_cb_base + SCx200_WDT_WDTO);
+ outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS);
+ outw(W_DISABLE, scx200_cb_base + SCx200_WDT_WDCNFG);
+ spin_unlock(&scx_lock);
+}
+
+static int scx200_wdt_open(struct inode *inode, struct file *file)
+{
+ /* only allow one at a time */
+ if (test_and_set_bit(0, &open_lock))
+ return -EBUSY;
+ scx200_wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static int scx200_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close != 42)
+ printk(KERN_WARNING NAME
+ ": watchdog device closed unexpectedly, "
+ "will not disable the watchdog timer\n");
+ else if (!nowayout)
+ scx200_wdt_disable();
+ expect_close = 0;
+ clear_bit(0, &open_lock);
+
+ return 0;
+}
+
+static int scx200_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_HALT || code == SYS_POWER_OFF)
+ if (!nowayout)
+ scx200_wdt_disable();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block scx200_wdt_notifier = {
+ .notifier_call = scx200_wdt_notify_sys,
+};
+
+static ssize_t scx200_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* check for a magic close character */
+ if (len) {
+ size_t i;
+
+ scx200_wdt_ping();
+
+ expect_close = 0;
+ for (i = 0; i < len; ++i) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+
+ return len;
+ }
+
+ return 0;
+}
+
+static long scx200_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .identity = "NatSemi SCx200 Watchdog",
+ .firmware_version = 1,
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ };
+ int new_margin;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ return 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, p))
+ return -EFAULT;
+ return 0;
+ case WDIOC_KEEPALIVE:
+ scx200_wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, p))
+ return -EFAULT;
+ if (new_margin < 1)
+ return -EINVAL;
+ margin = new_margin;
+ scx200_wdt_update_margin();
+ scx200_wdt_ping();
+ case WDIOC_GETTIMEOUT:
+ if (put_user(margin, p))
+ return -EFAULT;
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations scx200_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = scx200_wdt_write,
+ .unlocked_ioctl = scx200_wdt_ioctl,
+ .open = scx200_wdt_open,
+ .release = scx200_wdt_release,
+};
+
+static struct miscdevice scx200_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &scx200_wdt_fops,
+};
+
+static int __init scx200_wdt_init(void)
+{
+ int r;
+
+ printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n");
+
+ /* check that we have found the configuration block */
+ if (!scx200_cb_present())
+ return -ENODEV;
+
+ if (!request_region(scx200_cb_base + SCx200_WDT_OFFSET,
+ SCx200_WDT_SIZE,
+ "NatSemi SCx200 Watchdog")) {
+ printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
+ return -EBUSY;
+ }
+
+ scx200_wdt_update_margin();
+ scx200_wdt_disable();
+
+ r = register_reboot_notifier(&scx200_wdt_notifier);
+ if (r) {
+ printk(KERN_ERR NAME ": unable to register reboot notifier");
+ release_region(scx200_cb_base + SCx200_WDT_OFFSET,
+ SCx200_WDT_SIZE);
+ return r;
+ }
+
+ r = misc_register(&scx200_wdt_miscdev);
+ if (r) {
+ unregister_reboot_notifier(&scx200_wdt_notifier);
+ release_region(scx200_cb_base + SCx200_WDT_OFFSET,
+ SCx200_WDT_SIZE);
+ return r;
+ }
+
+ return 0;
+}
+
+static void __exit scx200_wdt_cleanup(void)
+{
+ misc_deregister(&scx200_wdt_miscdev);
+ unregister_reboot_notifier(&scx200_wdt_notifier);
+ release_region(scx200_cb_base + SCx200_WDT_OFFSET,
+ SCx200_WDT_SIZE);
+}
+
+module_init(scx200_wdt_init);
+module_exit(scx200_wdt_cleanup);
+
+/*
+ Local variables:
+ compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
+ c-basic-offset: 8
+ End:
+*/
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
new file mode 100644
index 00000000..a267dc07
--- /dev/null
+++ b/drivers/watchdog/shwdt.c
@@ -0,0 +1,487 @@
+/*
+ * drivers/watchdog/shwdt.c
+ *
+ * Watchdog driver for integrated watchdog in the SuperH processors.
+ *
+ * Copyright (C) 2001 - 2010 Paul Mundt <lethal@linux-sh.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
+ * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ *
+ * 19-Apr-2002 Rob Radez <rob@osinvestor.com>
+ * Added expect close support, made emulated timeout runtime changeable
+ * general cleanups, add some ioctls
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/notifier.h>
+#include <linux/ioport.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/watchdog.h>
+
+#define DRV_NAME "sh-wdt"
+
+/*
+ * Default clock division ratio is 5.25 msecs. For an additional table of
+ * values, consult the asm-sh/watchdog.h. Overload this at module load
+ * time.
+ *
+ * In order for this to work reliably we need to have HZ set to 1000 or
+ * something quite higher than 100 (or we need a proper high-res timer
+ * implementation that will deal with this properly), otherwise the 10ms
+ * resolution of a jiffy is enough to trigger the overflow. For things like
+ * the SH-4 and SH-5, this isn't necessarily that big of a problem, though
+ * for the SH-2 and SH-3, this isn't recommended unless the WDT is absolutely
+ * necssary.
+ *
+ * As a result of this timing problem, the only modes that are particularly
+ * feasible are the 4096 and the 2048 divisors, which yield 5.25 and 2.62ms
+ * overflow periods respectively.
+ *
+ * Also, since we can't really expect userspace to be responsive enough
+ * before the overflow happens, we maintain two separate timers .. One in
+ * the kernel for clearing out WOVF every 2ms or so (again, this depends on
+ * HZ == 1000), and another for monitoring userspace writes to the WDT device.
+ *
+ * As such, we currently use a configurable heartbeat interval which defaults
+ * to 30s. In this case, the userspace daemon is only responsible for periodic
+ * writes to the device before the next heartbeat is scheduled. If the daemon
+ * misses its deadline, the kernel timer will allow the WDT to overflow.
+ */
+static int clock_division_ratio = WTCSR_CKS_4096;
+#define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4))
+
+static const struct watchdog_info sh_wdt_info;
+static struct platform_device *sh_wdt_dev;
+static DEFINE_SPINLOCK(shwdt_lock);
+
+#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
+static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
+static int nowayout = WATCHDOG_NOWAYOUT;
+static unsigned long next_heartbeat;
+
+struct sh_wdt {
+ void __iomem *base;
+ struct device *dev;
+
+ struct timer_list timer;
+
+ unsigned long enabled;
+ char expect_close;
+};
+
+static void sh_wdt_start(struct sh_wdt *wdt)
+{
+ unsigned long flags;
+ u8 csr;
+
+ spin_lock_irqsave(&shwdt_lock, flags);
+
+ next_heartbeat = jiffies + (heartbeat * HZ);
+ mod_timer(&wdt->timer, next_ping_period(clock_division_ratio));
+
+ csr = sh_wdt_read_csr();
+ csr |= WTCSR_WT | clock_division_ratio;
+ sh_wdt_write_csr(csr);
+
+ sh_wdt_write_cnt(0);
+
+ /*
+ * These processors have a bit of an inconsistent initialization
+ * process.. starting with SH-3, RSTS was moved to WTCSR, and the
+ * RSTCSR register was removed.
+ *
+ * On the SH-2 however, in addition with bits being in different
+ * locations, we must deal with RSTCSR outright..
+ */
+ csr = sh_wdt_read_csr();
+ csr |= WTCSR_TME;
+ csr &= ~WTCSR_RSTS;
+ sh_wdt_write_csr(csr);
+
+#ifdef CONFIG_CPU_SH2
+ csr = sh_wdt_read_rstcsr();
+ csr &= ~RSTCSR_RSTS;
+ sh_wdt_write_rstcsr(csr);
+#endif
+ spin_unlock_irqrestore(&shwdt_lock, flags);
+}
+
+static void sh_wdt_stop(struct sh_wdt *wdt)
+{
+ unsigned long flags;
+ u8 csr;
+
+ spin_lock_irqsave(&shwdt_lock, flags);
+
+ del_timer(&wdt->timer);
+
+ csr = sh_wdt_read_csr();
+ csr &= ~WTCSR_TME;
+ sh_wdt_write_csr(csr);
+
+ spin_unlock_irqrestore(&shwdt_lock, flags);
+}
+
+static inline void sh_wdt_keepalive(struct sh_wdt *wdt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&shwdt_lock, flags);
+ next_heartbeat = jiffies + (heartbeat * HZ);
+ spin_unlock_irqrestore(&shwdt_lock, flags);
+}
+
+static int sh_wdt_set_heartbeat(int t)
+{
+ unsigned long flags;
+
+ if (unlikely(t < 1 || t > 3600)) /* arbitrary upper limit */
+ return -EINVAL;
+
+ spin_lock_irqsave(&shwdt_lock, flags);
+ heartbeat = t;
+ spin_unlock_irqrestore(&shwdt_lock, flags);
+ return 0;
+}
+
+static void sh_wdt_ping(unsigned long data)
+{
+ struct sh_wdt *wdt = (struct sh_wdt *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&shwdt_lock, flags);
+ if (time_before(jiffies, next_heartbeat)) {
+ u8 csr;
+
+ csr = sh_wdt_read_csr();
+ csr &= ~WTCSR_IOVF;
+ sh_wdt_write_csr(csr);
+
+ sh_wdt_write_cnt(0);
+
+ mod_timer(&wdt->timer, next_ping_period(clock_division_ratio));
+ } else
+ dev_warn(wdt->dev, "Heartbeat lost! Will not ping "
+ "the watchdog\n");
+ spin_unlock_irqrestore(&shwdt_lock, flags);
+}
+
+static int sh_wdt_open(struct inode *inode, struct file *file)
+{
+ struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev);
+
+ if (test_and_set_bit(0, &wdt->enabled))
+ return -EBUSY;
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ file->private_data = wdt;
+
+ sh_wdt_start(wdt);
+
+ return nonseekable_open(inode, file);
+}
+
+static int sh_wdt_close(struct inode *inode, struct file *file)
+{
+ struct sh_wdt *wdt = file->private_data;
+
+ if (wdt->expect_close == 42) {
+ sh_wdt_stop(wdt);
+ } else {
+ dev_crit(wdt->dev, "Unexpected close, not "
+ "stopping watchdog!\n");
+ sh_wdt_keepalive(wdt);
+ }
+
+ clear_bit(0, &wdt->enabled);
+ wdt->expect_close = 0;
+
+ return 0;
+}
+
+static ssize_t sh_wdt_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct sh_wdt *wdt = file->private_data;
+
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ wdt->expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ wdt->expect_close = 42;
+ }
+ }
+ sh_wdt_keepalive(wdt);
+ }
+
+ return count;
+}
+
+static long sh_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sh_wdt *wdt = file->private_data;
+ int new_heartbeat;
+ int options, retval = -EINVAL;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info *)arg,
+ &sh_wdt_info, sizeof(sh_wdt_info)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, (int *)arg);
+ case WDIOC_SETOPTIONS:
+ if (get_user(options, (int *)arg))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ sh_wdt_stop(wdt);
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ sh_wdt_start(wdt);
+ retval = 0;
+ }
+
+ return retval;
+ case WDIOC_KEEPALIVE:
+ sh_wdt_keepalive(wdt);
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_heartbeat, (int *)arg))
+ return -EFAULT;
+
+ if (sh_wdt_set_heartbeat(new_heartbeat))
+ return -EINVAL;
+
+ sh_wdt_keepalive(wdt);
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, (int *)arg);
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int sh_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev);
+
+ if (code == SYS_DOWN || code == SYS_HALT)
+ sh_wdt_stop(wdt);
+
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations sh_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sh_wdt_write,
+ .unlocked_ioctl = sh_wdt_ioctl,
+ .open = sh_wdt_open,
+ .release = sh_wdt_close,
+};
+
+static const struct watchdog_info sh_wdt_info = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "SH WDT",
+};
+
+static struct notifier_block sh_wdt_notifier = {
+ .notifier_call = sh_wdt_notify_sys,
+};
+
+static struct miscdevice sh_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sh_wdt_fops,
+};
+
+static int __devinit sh_wdt_probe(struct platform_device *pdev)
+{
+ struct sh_wdt *wdt;
+ struct resource *res;
+ int rc;
+
+ /*
+ * As this driver only covers the global watchdog case, reject
+ * any attempts to register per-CPU watchdogs.
+ */
+ if (pdev->id != -1)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!res))
+ return -EINVAL;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), DRV_NAME))
+ return -EBUSY;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(struct sh_wdt), GFP_KERNEL);
+ if (unlikely(!wdt)) {
+ rc = -ENOMEM;
+ goto out_release;
+ }
+
+ wdt->dev = &pdev->dev;
+
+ wdt->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (unlikely(!wdt->base)) {
+ rc = -ENXIO;
+ goto out_err;
+ }
+
+ rc = register_reboot_notifier(&sh_wdt_notifier);
+ if (unlikely(rc)) {
+ dev_err(&pdev->dev,
+ "Can't register reboot notifier (err=%d)\n", rc);
+ goto out_unmap;
+ }
+
+ sh_wdt_miscdev.parent = wdt->dev;
+
+ rc = misc_register(&sh_wdt_miscdev);
+ if (unlikely(rc)) {
+ dev_err(&pdev->dev,
+ "Can't register miscdev on minor=%d (err=%d)\n",
+ sh_wdt_miscdev.minor, rc);
+ goto out_unreg;
+ }
+
+ init_timer(&wdt->timer);
+ wdt->timer.function = sh_wdt_ping;
+ wdt->timer.data = (unsigned long)wdt;
+ wdt->timer.expires = next_ping_period(clock_division_ratio);
+
+ platform_set_drvdata(pdev, wdt);
+ sh_wdt_dev = pdev;
+
+ dev_info(&pdev->dev, "initialized.\n");
+
+ return 0;
+
+out_unreg:
+ unregister_reboot_notifier(&sh_wdt_notifier);
+out_unmap:
+ devm_iounmap(&pdev->dev, wdt->base);
+out_err:
+ devm_kfree(&pdev->dev, wdt);
+out_release:
+ devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
+
+ return rc;
+}
+
+static int __devexit sh_wdt_remove(struct platform_device *pdev)
+{
+ struct sh_wdt *wdt = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ platform_set_drvdata(pdev, NULL);
+
+ misc_deregister(&sh_wdt_miscdev);
+
+ sh_wdt_dev = NULL;
+
+ unregister_reboot_notifier(&sh_wdt_notifier);
+ devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
+ devm_iounmap(&pdev->dev, wdt->base);
+ devm_kfree(&pdev->dev, wdt);
+
+ return 0;
+}
+
+static struct platform_driver sh_wdt_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = sh_wdt_probe,
+ .remove = __devexit_p(sh_wdt_remove),
+};
+
+static int __init sh_wdt_init(void)
+{
+ int rc;
+
+ if (unlikely(clock_division_ratio < 0x5 ||
+ clock_division_ratio > 0x7)) {
+ clock_division_ratio = WTCSR_CKS_4096;
+
+ pr_info("%s: divisor must be 0x5<=x<=0x7, using %d\n",
+ DRV_NAME, clock_division_ratio);
+ }
+
+ rc = sh_wdt_set_heartbeat(heartbeat);
+ if (unlikely(rc)) {
+ heartbeat = WATCHDOG_HEARTBEAT;
+
+ pr_info("%s: heartbeat value must be 1<=x<=3600, using %d\n",
+ DRV_NAME, heartbeat);
+ }
+
+ pr_info("%s: configured with heartbeat=%d sec (nowayout=%d)\n",
+ DRV_NAME, heartbeat, nowayout);
+
+ return platform_driver_register(&sh_wdt_driver);
+}
+
+static void __exit sh_wdt_exit(void)
+{
+ platform_driver_unregister(&sh_wdt_driver);
+}
+module_init(sh_wdt_init);
+module_exit(sh_wdt_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("SuperH watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+module_param(clock_division_ratio, int, 0);
+MODULE_PARM_DESC(clock_division_ratio,
+ "Clock division ratio. Valid ranges are from 0x5 (1.31ms) "
+ "to 0x7 (5.25ms). (default=" __MODULE_STRING(WTCSR_CKS_4096) ")");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat in seconds. (1 <= heartbeat <= 3600, default="
+ __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c
new file mode 100644
index 00000000..e97b0499
--- /dev/null
+++ b/drivers/watchdog/smsc37b787_wdt.c
@@ -0,0 +1,627 @@
+/*
+ * SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x
+ *
+ * Based on acquirewdt.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
+ * and some other existing drivers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * The authors do NOT admit liability nor provide warranty for
+ * any of this software. This material is provided "AS-IS" in
+ * the hope that it may be useful for others.
+ *
+ * (C) Copyright 2003-2006 Sven Anders <anders@anduras.de>
+ *
+ * History:
+ * 2003 - Created version 1.0 for Linux 2.4.x.
+ * 2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE
+ * features. Released version 1.1
+ *
+ * Theory of operation:
+ *
+ * A Watchdog Timer (WDT) is a hardware circuit that can
+ * reset the computer system in case of a software fault.
+ * You probably knew that already.
+ *
+ * Usually a userspace daemon will notify the kernel WDT driver
+ * via the /dev/watchdog special device file that userspace is
+ * still alive, at regular intervals. When such a notification
+ * occurs, the driver will usually tell the hardware watchdog
+ * that everything is in order, and that the watchdog should wait
+ * for yet another little while to reset the system.
+ * If userspace fails (RAM error, kernel bug, whatever), the
+ * notifications cease to occur, and the hardware watchdog will
+ * reset the system (causing a reboot) after the timeout occurs.
+ *
+ * Create device with:
+ * mknod /dev/watchdog c 10 130
+ *
+ * For an example userspace keep-alive daemon, see:
+ * Documentation/watchdog/watchdog.txt
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+/* enable support for minutes as units? */
+/* (does not always work correctly, so disabled by default!) */
+#define SMSC_SUPPORT_MINUTES
+#undef SMSC_SUPPORT_MINUTES
+
+#define MAX_TIMEOUT 255
+
+#define UNIT_SECOND 0
+#define UNIT_MINUTE 1
+
+#define MODNAME "smsc37b787_wdt: "
+#define VERSION "1.1"
+
+#define IOPORT 0x3F0
+#define IOPORT_SIZE 2
+#define IODEV_NO 8
+
+static int unit = UNIT_SECOND; /* timer's unit */
+static int timeout = 60; /* timeout value: default is 60 "units" */
+static unsigned long timer_enabled; /* is the timer enabled? */
+
+static char expect_close; /* is the close expected? */
+
+static DEFINE_SPINLOCK(io_lock);/* to guard the watchdog from io races */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+/* -- Low level function ----------------------------------------*/
+
+/* unlock the IO chip */
+
+static inline void open_io_config(void)
+{
+ outb(0x55, IOPORT);
+ mdelay(1);
+ outb(0x55, IOPORT);
+}
+
+/* lock the IO chip */
+static inline void close_io_config(void)
+{
+ outb(0xAA, IOPORT);
+}
+
+/* select the IO device */
+static inline void select_io_device(unsigned char devno)
+{
+ outb(0x07, IOPORT);
+ outb(devno, IOPORT+1);
+}
+
+/* write to the control register */
+static inline void write_io_cr(unsigned char reg, unsigned char data)
+{
+ outb(reg, IOPORT);
+ outb(data, IOPORT+1);
+}
+
+/* read from the control register */
+static inline char read_io_cr(unsigned char reg)
+{
+ outb(reg, IOPORT);
+ return inb(IOPORT+1);
+}
+
+/* -- Medium level functions ------------------------------------*/
+
+static inline void gpio_bit12(unsigned char reg)
+{
+ /* -- General Purpose I/O Bit 1.2 --
+ * Bit 0, In/Out: 0 = Output, 1 = Input
+ * Bit 1, Polarity: 0 = No Invert, 1 = Invert
+ * Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable
+ * Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17,
+ * 11 = Either Edge Triggered Intr. 2
+ * Bit 5/6 (Reserved)
+ * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain
+ */
+ write_io_cr(0xE2, reg);
+}
+
+static inline void gpio_bit13(unsigned char reg)
+{
+ /* -- General Purpose I/O Bit 1.3 --
+ * Bit 0, In/Out: 0 = Output, 1 = Input
+ * Bit 1, Polarity: 0 = No Invert, 1 = Invert
+ * Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable
+ * Bit 3, Function select: 0 = GPI/O, 1 = LED
+ * Bit 4-6 (Reserved)
+ * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain
+ */
+ write_io_cr(0xE3, reg);
+}
+
+static inline void wdt_timer_units(unsigned char new_units)
+{
+ /* -- Watchdog timer units --
+ * Bit 0-6 (Reserved)
+ * Bit 7, WDT Time-out Value Units Select
+ * (0 = Minutes, 1 = Seconds)
+ */
+ write_io_cr(0xF1, new_units);
+}
+
+static inline void wdt_timeout_value(unsigned char new_timeout)
+{
+ /* -- Watchdog Timer Time-out Value --
+ * Bit 0-7 Binary coded units (0=Disabled, 1..255)
+ */
+ write_io_cr(0xF2, new_timeout);
+}
+
+static inline void wdt_timer_conf(unsigned char conf)
+{
+ /* -- Watchdog timer configuration --
+ * Bit 0 Joystick enable: 0* = No Reset, 1 = Reset WDT upon
+ * Gameport I/O
+ * Bit 1 Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr.
+ * Bit 2 Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr
+ * Bit 3 Reset the timer
+ * (Wrong in SMsC documentation? Given as: PowerLED Timout
+ * Enabled)
+ * Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled,
+ * 0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15)
+ */
+ write_io_cr(0xF3, conf);
+}
+
+static inline void wdt_timer_ctrl(unsigned char reg)
+{
+ /* -- Watchdog timer control --
+ * Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occurred
+ * Bit 1 Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz
+ * Bit 2 Force Timeout: 1 = Forces WD timeout event (self-cleaning)
+ * Bit 3 P20 Force Timeout enabled:
+ * 0 = P20 activity does not generate the WD timeout event
+ * 1 = P20 Allows rising edge of P20, from the keyboard
+ * controller, to force the WD timeout event.
+ * Bit 4 (Reserved)
+ * -- Soft power management --
+ * Bit 5 Stop Counter: 1 = Stop software power down counter
+ * set via register 0xB8, (self-cleaning)
+ * (Upon read: 0 = Counter running, 1 = Counter stopped)
+ * Bit 6 Restart Counter: 1 = Restart software power down counter
+ * set via register 0xB8, (self-cleaning)
+ * Bit 7 SPOFF: 1 = Force software power down (self-cleaning)
+ */
+ write_io_cr(0xF4, reg);
+}
+
+/* -- Higher level functions ------------------------------------*/
+
+/* initialize watchdog */
+
+static void wb_smsc_wdt_initialize(void)
+{
+ unsigned char old;
+
+ spin_lock(&io_lock);
+ open_io_config();
+ select_io_device(IODEV_NO);
+
+ /* enable the watchdog */
+ gpio_bit13(0x08); /* Select pin 80 = LED not GPIO */
+ gpio_bit12(0x0A); /* Set pin 79 = WDT not
+ GPIO/Output/Polarity=Invert */
+ /* disable the timeout */
+ wdt_timeout_value(0);
+
+ /* reset control register */
+ wdt_timer_ctrl(0x00);
+
+ /* reset configuration register */
+ wdt_timer_conf(0x00);
+
+ /* read old (timer units) register */
+ old = read_io_cr(0xF1) & 0x7F;
+ if (unit == UNIT_SECOND)
+ old |= 0x80; /* set to seconds */
+
+ /* set the watchdog timer units */
+ wdt_timer_units(old);
+
+ close_io_config();
+ spin_unlock(&io_lock);
+}
+
+/* shutdown the watchdog */
+
+static void wb_smsc_wdt_shutdown(void)
+{
+ spin_lock(&io_lock);
+ open_io_config();
+ select_io_device(IODEV_NO);
+
+ /* disable the watchdog */
+ gpio_bit13(0x09);
+ gpio_bit12(0x09);
+
+ /* reset watchdog config register */
+ wdt_timer_conf(0x00);
+
+ /* reset watchdog control register */
+ wdt_timer_ctrl(0x00);
+
+ /* disable timeout */
+ wdt_timeout_value(0x00);
+
+ close_io_config();
+ spin_unlock(&io_lock);
+}
+
+/* set timeout => enable watchdog */
+
+static void wb_smsc_wdt_set_timeout(unsigned char new_timeout)
+{
+ spin_lock(&io_lock);
+ open_io_config();
+ select_io_device(IODEV_NO);
+
+ /* set Power LED to blink, if we enable the timeout */
+ wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02);
+
+ /* set timeout value */
+ wdt_timeout_value(new_timeout);
+
+ close_io_config();
+ spin_unlock(&io_lock);
+}
+
+/* get timeout */
+
+static unsigned char wb_smsc_wdt_get_timeout(void)
+{
+ unsigned char set_timeout;
+
+ spin_lock(&io_lock);
+ open_io_config();
+ select_io_device(IODEV_NO);
+ set_timeout = read_io_cr(0xF2);
+ close_io_config();
+ spin_unlock(&io_lock);
+
+ return set_timeout;
+}
+
+/* disable watchdog */
+
+static void wb_smsc_wdt_disable(void)
+{
+ /* set the timeout to 0 to disable the watchdog */
+ wb_smsc_wdt_set_timeout(0);
+}
+
+/* enable watchdog by setting the current timeout */
+
+static void wb_smsc_wdt_enable(void)
+{
+ /* set the current timeout... */
+ wb_smsc_wdt_set_timeout(timeout);
+}
+
+/* reset the timer */
+
+static void wb_smsc_wdt_reset_timer(void)
+{
+ spin_lock(&io_lock);
+ open_io_config();
+ select_io_device(IODEV_NO);
+
+ /* reset the timer */
+ wdt_timeout_value(timeout);
+ wdt_timer_conf(0x08);
+
+ close_io_config();
+ spin_unlock(&io_lock);
+}
+
+/* return, if the watchdog is enabled (timeout is set...) */
+
+static int wb_smsc_wdt_status(void)
+{
+ return (wb_smsc_wdt_get_timeout() == 0) ? 0 : WDIOF_KEEPALIVEPING;
+}
+
+
+/* -- File operations -------------------------------------------*/
+
+/* open => enable watchdog and set initial timeout */
+
+static int wb_smsc_wdt_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+
+ if (test_and_set_bit(0, &timer_enabled))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ /* Reload and activate timer */
+ wb_smsc_wdt_enable();
+
+ printk(KERN_INFO MODNAME
+ "Watchdog enabled. Timeout set to %d %s.\n",
+ timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
+
+ return nonseekable_open(inode, file);
+}
+
+/* close => shut off the timer */
+
+static int wb_smsc_wdt_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer. */
+
+ if (expect_close == 42) {
+ wb_smsc_wdt_disable();
+ printk(KERN_INFO MODNAME
+ "Watchdog disabled, sleeping again...\n");
+ } else {
+ printk(KERN_CRIT MODNAME
+ "Unexpected close, not stopping watchdog!\n");
+ wb_smsc_wdt_reset_timer();
+ }
+
+ clear_bit(0, &timer_enabled);
+ expect_close = 0;
+ return 0;
+}
+
+/* write => update the timer to keep the machine alive */
+
+static ssize_t wb_smsc_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* reset expect flag */
+ expect_close = 0;
+
+ /* scan to see whether or not we got the
+ magic character */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ wb_smsc_wdt_reset_timer();
+ }
+ return len;
+}
+
+/* ioctl => control interface */
+
+static long wb_smsc_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int new_timeout;
+
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = "SMsC 37B787 Watchdog",
+ };
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident, &ident, sizeof(ident))
+ ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ return put_user(wb_smsc_wdt_status(), uarg.i);
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, uarg.i))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ wb_smsc_wdt_disable();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ wb_smsc_wdt_enable();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ wb_smsc_wdt_reset_timer();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+ /* the API states this is given in secs */
+ if (unit == UNIT_MINUTE)
+ new_timeout /= 60;
+ if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
+ return -EINVAL;
+ timeout = new_timeout;
+ wb_smsc_wdt_set_timeout(timeout);
+ /* fall through and return the new timeout... */
+ case WDIOC_GETTIMEOUT:
+ new_timeout = timeout;
+ if (unit == UNIT_MINUTE)
+ new_timeout *= 60;
+ return put_user(new_timeout, uarg.i);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/* -- Notifier funtions -----------------------------------------*/
+
+static int wb_smsc_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT) {
+ /* set timeout to 0, to avoid possible race-condition */
+ timeout = 0;
+ wb_smsc_wdt_disable();
+ }
+ return NOTIFY_DONE;
+}
+
+/* -- Module's structures ---------------------------------------*/
+
+static const struct file_operations wb_smsc_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wb_smsc_wdt_write,
+ .unlocked_ioctl = wb_smsc_wdt_ioctl,
+ .open = wb_smsc_wdt_open,
+ .release = wb_smsc_wdt_release,
+};
+
+static struct notifier_block wb_smsc_wdt_notifier = {
+ .notifier_call = wb_smsc_wdt_notify_sys,
+};
+
+static struct miscdevice wb_smsc_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wb_smsc_wdt_fops,
+};
+
+/* -- Module init functions -------------------------------------*/
+
+/* module's "constructor" */
+
+static int __init wb_smsc_wdt_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "SMsC 37B787 watchdog component driver "
+ VERSION " initialising...\n");
+
+ if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) {
+ printk(KERN_ERR MODNAME "Unable to register IO port %#x\n",
+ IOPORT);
+ ret = -EBUSY;
+ goto out_pnp;
+ }
+
+ /* set new maximum, if it's too big */
+ if (timeout > MAX_TIMEOUT)
+ timeout = MAX_TIMEOUT;
+
+ /* init the watchdog timer */
+ wb_smsc_wdt_initialize();
+
+ ret = register_reboot_notifier(&wb_smsc_wdt_notifier);
+ if (ret) {
+ printk(KERN_ERR MODNAME
+ "Unable to register reboot notifier err = %d\n", ret);
+ goto out_io;
+ }
+
+ ret = misc_register(&wb_smsc_wdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR MODNAME
+ "Unable to register miscdev on minor %d\n",
+ WATCHDOG_MINOR);
+ goto out_rbt;
+ }
+
+ /* output info */
+ printk(KERN_INFO MODNAME "Timeout set to %d %s.\n",
+ timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
+ printk(KERN_INFO MODNAME
+ "Watchdog initialized and sleeping (nowayout=%d)...\n",
+ nowayout);
+out_clean:
+ return ret;
+
+out_rbt:
+ unregister_reboot_notifier(&wb_smsc_wdt_notifier);
+
+out_io:
+ release_region(IOPORT, IOPORT_SIZE);
+
+out_pnp:
+ goto out_clean;
+}
+
+/* module's "destructor" */
+
+static void __exit wb_smsc_wdt_exit(void)
+{
+ /* Stop the timer before we leave */
+ if (!nowayout) {
+ wb_smsc_wdt_shutdown();
+ printk(KERN_INFO MODNAME "Watchdog disabled.\n");
+ }
+
+ misc_deregister(&wb_smsc_wdt_miscdev);
+ unregister_reboot_notifier(&wb_smsc_wdt_notifier);
+ release_region(IOPORT, IOPORT_SIZE);
+
+ printk(KERN_INFO "SMsC 37B787 watchdog component driver removed.\n");
+}
+
+module_init(wb_smsc_wdt_init);
+module_exit(wb_smsc_wdt_exit);
+
+MODULE_AUTHOR("Sven Anders <anders@anduras.de>");
+MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version "
+ VERSION ")");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+#ifdef SMSC_SUPPORT_MINUTES
+module_param(unit, int, 0);
+MODULE_PARM_DESC(unit,
+ "set unit to use, 0=seconds or 1=minutes, default is 0");
+#endif
+
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c
new file mode 100644
index 00000000..bf16ffb4
--- /dev/null
+++ b/drivers/watchdog/softdog.c
@@ -0,0 +1,328 @@
+/*
+ * SoftDog 0.07: A Software Watchdog Device
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * Software only watchdog driver. Unlike its big brother the WDT501P
+ * driver this won't always recover a failed machine.
+ *
+ * 03/96: Angelo Haritsis <ah@doc.ic.ac.uk> :
+ * Modularised.
+ * Added soft_margin; use upon insmod to change the timer delay.
+ * NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
+ * minors.
+ *
+ * 19980911 Alan Cox
+ * Made SMP safe for 2.3.x
+ *
+ * 20011127 Joel Becker (jlbec@evilplan.org>
+ * Added soft_noboot; Allows testing the softdog trigger without
+ * requiring a recompile.
+ * Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
+ *
+ * 20020530 Joel Becker <joel.becker@oracle.com>
+ * Added Matt Domsch's nowayout module option.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+
+#define PFX "SoftDog: "
+
+#define TIMER_MARGIN 60 /* Default is 60 seconds */
+static int soft_margin = TIMER_MARGIN; /* in seconds */
+module_param(soft_margin, int, 0);
+MODULE_PARM_DESC(soft_margin,
+ "Watchdog soft_margin in seconds. (0 < soft_margin < 65536, default="
+ __MODULE_STRING(TIMER_MARGIN) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#ifdef ONLY_TESTING
+static int soft_noboot = 1;
+#else
+static int soft_noboot = 0;
+#endif /* ONLY_TESTING */
+
+module_param(soft_noboot, int, 0);
+MODULE_PARM_DESC(soft_noboot,
+ "Softdog action, set to 1 to ignore reboots, 0 to reboot "
+ "(default depends on ONLY_TESTING)");
+
+static int soft_panic;
+module_param(soft_panic, int, 0);
+MODULE_PARM_DESC(soft_panic,
+ "Softdog action, set to 1 to panic, 0 to reboot (default=0)");
+
+/*
+ * Our timer
+ */
+
+static void watchdog_fire(unsigned long);
+
+static struct timer_list watchdog_ticktock =
+ TIMER_INITIALIZER(watchdog_fire, 0, 0);
+static unsigned long driver_open, orphan_timer;
+static char expect_close;
+
+
+/*
+ * If the timer expires..
+ */
+
+static void watchdog_fire(unsigned long data)
+{
+ if (test_and_clear_bit(0, &orphan_timer))
+ module_put(THIS_MODULE);
+
+ if (soft_noboot)
+ printk(KERN_CRIT PFX "Triggered - Reboot ignored.\n");
+ else if (soft_panic) {
+ printk(KERN_CRIT PFX "Initiating panic.\n");
+ panic("Software Watchdog Timer expired.");
+ } else {
+ printk(KERN_CRIT PFX "Initiating system reboot.\n");
+ emergency_restart();
+ printk(KERN_CRIT PFX "Reboot didn't ?????\n");
+ }
+}
+
+/*
+ * Softdog operations
+ */
+
+static int softdog_keepalive(void)
+{
+ mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
+ return 0;
+}
+
+static int softdog_stop(void)
+{
+ del_timer(&watchdog_ticktock);
+ return 0;
+}
+
+static int softdog_set_heartbeat(int t)
+{
+ if ((t < 0x0001) || (t > 0xFFFF))
+ return -EINVAL;
+
+ soft_margin = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int softdog_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &driver_open))
+ return -EBUSY;
+ if (!test_and_clear_bit(0, &orphan_timer))
+ __module_get(THIS_MODULE);
+ /*
+ * Activate timer
+ */
+ softdog_keepalive();
+ return nonseekable_open(inode, file);
+}
+
+static int softdog_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ * Lock it in if it's a module and we set nowayout
+ */
+ if (expect_close == 42) {
+ softdog_stop();
+ module_put(THIS_MODULE);
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ set_bit(0, &orphan_timer);
+ softdog_keepalive();
+ }
+ clear_bit(0, &driver_open);
+ expect_close = 0;
+ return 0;
+}
+
+static ssize_t softdog_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /*
+ * Refresh the timer.
+ */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ softdog_keepalive();
+ }
+ return len;
+}
+
+static long softdog_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_margin;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = "Software Watchdog",
+ };
+ 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_KEEPALIVE:
+ softdog_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, p))
+ return -EFAULT;
+ if (softdog_set_heartbeat(new_margin))
+ return -EINVAL;
+ softdog_keepalive();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(soft_margin, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int softdog_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ /* Turn the WDT off */
+ softdog_stop();
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations softdog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = softdog_write,
+ .unlocked_ioctl = softdog_ioctl,
+ .open = softdog_open,
+ .release = softdog_release,
+};
+
+static struct miscdevice softdog_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &softdog_fops,
+};
+
+static struct notifier_block softdog_notifier = {
+ .notifier_call = softdog_notify_sys,
+};
+
+static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07 "
+ "initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d "
+ "(nowayout= %d)\n";
+
+static int __init watchdog_init(void)
+{
+ int ret;
+
+ /* Check that the soft_margin value is within it's range;
+ if not reset to the default */
+ if (softdog_set_heartbeat(soft_margin)) {
+ softdog_set_heartbeat(TIMER_MARGIN);
+ printk(KERN_INFO PFX
+ "soft_margin must be 0 < soft_margin < 65536, using %d\n",
+ TIMER_MARGIN);
+ }
+
+ ret = register_reboot_notifier(&softdog_notifier);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ return ret;
+ }
+
+ ret = misc_register(&softdog_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ unregister_reboot_notifier(&softdog_notifier);
+ return ret;
+ }
+
+ printk(banner, soft_noboot, soft_margin, soft_panic, nowayout);
+
+ return 0;
+}
+
+static void __exit watchdog_exit(void)
+{
+ misc_deregister(&softdog_miscdev);
+ unregister_reboot_notifier(&softdog_notifier);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("Software Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c
new file mode 100644
index 00000000..87e05276
--- /dev/null
+++ b/drivers/watchdog/sp5100_tco.c
@@ -0,0 +1,492 @@
+/*
+ * sp5100_tco : TCO timer driver for sp5100 chipsets
+ *
+ * (c) Copyright 2009 Google Inc., All Rights Reserved.
+ *
+ * Based on i8xx_tco.c:
+ * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
+ * Reserved.
+ * http://www.kernelconcepts.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide"
+ */
+
+/*
+ * Includes, defines, variables, module parameters, ...
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "sp5100_tco.h"
+
+/* Module and version information */
+#define TCO_VERSION "0.01"
+#define TCO_MODULE_NAME "SP5100 TCO timer"
+#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
+#define PFX TCO_MODULE_NAME ": "
+
+/* internal variables */
+static u32 tcobase_phys;
+static void __iomem *tcobase;
+static unsigned int pm_iobase;
+static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */
+static unsigned long timer_alive;
+static char tco_expect_close;
+static struct pci_dev *sp5100_tco_pci;
+
+/* the watchdog platform device */
+static struct platform_device *sp5100_tco_platform_device;
+
+/* module parameters */
+
+#define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat. */
+static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default="
+ __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
+ " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Some TCO specific functions
+ */
+static void tco_timer_start(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = readl(SP5100_WDT_CONTROL(tcobase));
+ val |= SP5100_WDT_START_STOP_BIT;
+ writel(val, SP5100_WDT_CONTROL(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static void tco_timer_stop(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = readl(SP5100_WDT_CONTROL(tcobase));
+ val &= ~SP5100_WDT_START_STOP_BIT;
+ writel(val, SP5100_WDT_CONTROL(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static void tco_timer_keepalive(void)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tco_lock, flags);
+ val = readl(SP5100_WDT_CONTROL(tcobase));
+ val |= SP5100_WDT_TRIGGER_BIT;
+ writel(val, SP5100_WDT_CONTROL(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+}
+
+static int tco_timer_set_heartbeat(int t)
+{
+ unsigned long flags;
+
+ if (t < 0 || t > 0xffff)
+ return -EINVAL;
+
+ /* Write new heartbeat to watchdog */
+ spin_lock_irqsave(&tco_lock, flags);
+ writel(t, SP5100_WDT_COUNT(tcobase));
+ spin_unlock_irqrestore(&tco_lock, flags);
+
+ heartbeat = t;
+ return 0;
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int sp5100_tco_open(struct inode *inode, struct file *file)
+{
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &timer_alive))
+ return -EBUSY;
+
+ /* Reload and activate timer */
+ tco_timer_start();
+ tco_timer_keepalive();
+ return nonseekable_open(inode, file);
+}
+
+static int sp5100_tco_release(struct inode *inode, struct file *file)
+{
+ /* Shut off the timer. */
+ if (tco_expect_close == 42) {
+ tco_timer_stop();
+ } else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ tco_timer_keepalive();
+ }
+ clear_bit(0, &timer_alive);
+ tco_expect_close = 0;
+ return 0;
+}
+
+static ssize_t sp5100_tco_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ tco_expect_close = 0;
+
+ /* scan to see whether or not we got the magic character
+ */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ tco_expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ tco_timer_keepalive();
+ }
+ return len;
+}
+
+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")) {
+ printk(KERN_ERR PFX "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")) {
+ printk(KERN_ERR PFX "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 == 0) {
+ printk(KERN_ERR PFX "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 */
+ printk(KERN_INFO PFX "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) {
+ printk(KERN_ERR PFX "cannot register miscdev on minor="
+ "%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto exit;
+ }
+
+ clear_bit(0, &timer_alive);
+
+ printk(KERN_INFO PFX "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;
+
+ printk(KERN_INFO PFX "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;
+}
+
+static void __exit sp5100_tco_cleanup_module(void)
+{
+ platform_device_unregister(sp5100_tco_platform_device);
+ platform_driver_unregister(&sp5100_tco_driver);
+ printk(KERN_INFO PFX "SP5100 TCO Watchdog Module Unloaded.\n");
+}
+
+module_init(sp5100_tco_init_module);
+module_exit(sp5100_tco_cleanup_module);
+
+MODULE_AUTHOR("Priyanka Gupta");
+MODULE_DESCRIPTION("TCO timer driver for SP5100 chipset");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h
new file mode 100644
index 00000000..a5a16cc9
--- /dev/null
+++ b/drivers/watchdog/sp5100_tco.h
@@ -0,0 +1,41 @@
+/*
+ * sp5100_tco: TCO timer driver for sp5100 chipsets.
+ *
+ * (c) Copyright 2009 Google Inc., All Rights Reserved.
+ *
+ * TCO timer driver for sp5100 chipsets
+ */
+
+/*
+ * Some address definitions for the Watchdog
+ */
+
+#define SP5100_WDT_MEM_MAP_SIZE 0x08
+#define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */
+#define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */
+
+#define SP5100_WDT_START_STOP_BIT 1
+#define SP5100_WDT_TRIGGER_BIT (1 << 7)
+
+#define SP5100_PCI_WATCHDOG_MISC_REG 0x41
+#define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3)
+
+#define SP5100_PM_IOPORTS_SIZE 0x02
+
+/* These two IO registers are hardcoded and there doesn't seem to be a way to
+ * read them from a register.
+ */
+#define SP5100_IO_PM_INDEX_REG 0xCD6
+#define SP5100_IO_PM_DATA_REG 0xCD7
+
+#define SP5100_PM_WATCHDOG_CONTROL 0x69
+#define SP5100_PM_WATCHDOG_BASE0 0x6C
+#define SP5100_PM_WATCHDOG_BASE1 0x6D
+#define SP5100_PM_WATCHDOG_BASE2 0x6E
+#define SP5100_PM_WATCHDOG_BASE3 0x6F
+
+#define SP5100_PM_WATCHDOG_FIRED (1 << 1)
+#define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2)
+
+#define SP5100_PM_WATCHDOG_DISABLE 1
+#define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1)
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
new file mode 100644
index 00000000..0d80e08b
--- /dev/null
+++ b/drivers/watchdog/sp805_wdt.c
@@ -0,0 +1,387 @@
+/*
+ * drivers/char/watchdog/sp805-wdt.c
+ *
+ * Watchdog driver for ARM SP805 watchdog module
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2 or later. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/device.h>
+#include <linux/resource.h>
+#include <linux/amba/bus.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+/* default timeout in seconds */
+#define DEFAULT_TIMEOUT 60
+
+#define MODULE_NAME "sp805-wdt"
+
+/* watchdog register offsets and masks */
+#define WDTLOAD 0x000
+ #define LOAD_MIN 0x00000001
+ #define LOAD_MAX 0xFFFFFFFF
+#define WDTVALUE 0x004
+#define WDTCONTROL 0x008
+ /* control register masks */
+ #define INT_ENABLE (1 << 0)
+ #define RESET_ENABLE (1 << 1)
+#define WDTINTCLR 0x00C
+#define WDTRIS 0x010
+#define WDTMIS 0x014
+ #define INT_MASK (1 << 0)
+#define WDTLOCK 0xC00
+ #define UNLOCK 0x1ACCE551
+ #define LOCK 0x00000001
+
+/**
+ * struct sp805_wdt: sp805 wdt device structure
+ *
+ * lock: spin lock protecting dev structure and io access
+ * base: base address of wdt
+ * clk: clock structure of wdt
+ * dev: amba device structure of wdt
+ * status: current status of wdt
+ * load_val: load value to be set for current timeout
+ * timeout: current programmed timeout
+ */
+struct sp805_wdt {
+ spinlock_t lock;
+ void __iomem *base;
+ struct clk *clk;
+ struct amba_device *adev;
+ unsigned long status;
+ #define WDT_BUSY 0
+ #define WDT_CAN_BE_CLOSED 1
+ unsigned int load_val;
+ unsigned int timeout;
+};
+
+/* local variables */
+static struct sp805_wdt *wdt;
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+/* This routine finds load value that will reset system in required timout */
+static void wdt_setload(unsigned int timeout)
+{
+ u64 load, rate;
+
+ rate = clk_get_rate(wdt->clk);
+
+ /*
+ * sp805 runs counter with given value twice, after the end of first
+ * counter it gives an interrupt and then starts counter again. If
+ * interrupt already occurred then it resets the system. This is why
+ * load is half of what should be required.
+ */
+ load = div_u64(rate, 2) * timeout - 1;
+
+ load = (load > LOAD_MAX) ? LOAD_MAX : load;
+ load = (load < LOAD_MIN) ? LOAD_MIN : load;
+
+ spin_lock(&wdt->lock);
+ wdt->load_val = load;
+ /* roundup timeout to closest positive integer value */
+ wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
+ spin_unlock(&wdt->lock);
+}
+
+/* returns number of seconds left for reset to occur */
+static u32 wdt_timeleft(void)
+{
+ u64 load, rate;
+
+ rate = clk_get_rate(wdt->clk);
+
+ spin_lock(&wdt->lock);
+ load = readl(wdt->base + WDTVALUE);
+
+ /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
+ if (!(readl(wdt->base + WDTRIS) & INT_MASK))
+ load += wdt->load_val + 1;
+ spin_unlock(&wdt->lock);
+
+ return div_u64(load, rate);
+}
+
+/* enables watchdog timers reset */
+static void wdt_enable(void)
+{
+ spin_lock(&wdt->lock);
+
+ writel(UNLOCK, wdt->base + WDTLOCK);
+ writel(wdt->load_val, wdt->base + WDTLOAD);
+ writel(INT_MASK, wdt->base + WDTINTCLR);
+ writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
+ writel(LOCK, wdt->base + WDTLOCK);
+
+ spin_unlock(&wdt->lock);
+}
+
+/* disables watchdog timers reset */
+static void wdt_disable(void)
+{
+ spin_lock(&wdt->lock);
+
+ writel(UNLOCK, wdt->base + WDTLOCK);
+ writel(0, wdt->base + WDTCONTROL);
+ writel(0, wdt->base + WDTLOAD);
+ writel(LOCK, wdt->base + WDTLOCK);
+
+ spin_unlock(&wdt->lock);
+}
+
+static ssize_t sp805_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ /* Check for Magic Close character */
+ if (c == 'V') {
+ set_bit(WDT_CAN_BE_CLOSED,
+ &wdt->status);
+ break;
+ }
+ }
+ }
+ wdt_enable();
+ }
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = MODULE_NAME,
+};
+
+static long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ unsigned int timeout;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(timeout, (unsigned int *)arg);
+ if (ret)
+ break;
+
+ wdt_setload(timeout);
+
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(wdt->timeout, (unsigned int *)arg);
+ break;
+ case WDIOC_GETTIMELEFT:
+ ret = put_user(wdt_timeleft(), (unsigned int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int sp805_wdt_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+
+ if (test_and_set_bit(WDT_BUSY, &wdt->status))
+ return -EBUSY;
+
+ ret = clk_enable(wdt->clk);
+ if (ret) {
+ dev_err(&wdt->adev->dev, "clock enable fail");
+ goto err;
+ }
+
+ wdt_enable();
+
+ /* can not be closed, once enabled */
+ clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
+ return nonseekable_open(inode, file);
+
+err:
+ clear_bit(WDT_BUSY, &wdt->status);
+ return ret;
+}
+
+static int sp805_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) {
+ clear_bit(WDT_BUSY, &wdt->status);
+ dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
+ return 0;
+ }
+
+ wdt_disable();
+ clk_disable(wdt->clk);
+ clear_bit(WDT_BUSY, &wdt->status);
+
+ return 0;
+}
+
+static const struct file_operations sp805_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sp805_wdt_write,
+ .unlocked_ioctl = sp805_wdt_ioctl,
+ .open = sp805_wdt_open,
+ .release = sp805_wdt_release,
+};
+
+static struct miscdevice sp805_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sp805_wdt_fops,
+};
+
+static int __devinit
+sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
+{
+ int ret = 0;
+
+ if (!request_mem_region(adev->res.start, resource_size(&adev->res),
+ "sp805_wdt")) {
+ dev_warn(&adev->dev, "Failed to get memory region resource\n");
+ ret = -ENOENT;
+ goto err;
+ }
+
+ wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
+ if (!wdt) {
+ dev_warn(&adev->dev, "Kzalloc failed\n");
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ wdt->clk = clk_get(&adev->dev, NULL);
+ if (IS_ERR(wdt->clk)) {
+ dev_warn(&adev->dev, "Clock not found\n");
+ ret = PTR_ERR(wdt->clk);
+ goto err_clk_get;
+ }
+
+ wdt->base = ioremap(adev->res.start, resource_size(&adev->res));
+ if (!wdt->base) {
+ ret = -ENOMEM;
+ dev_warn(&adev->dev, "ioremap fail\n");
+ goto err_ioremap;
+ }
+
+ wdt->adev = adev;
+ spin_lock_init(&wdt->lock);
+ wdt_setload(DEFAULT_TIMEOUT);
+
+ ret = misc_register(&sp805_wdt_miscdev);
+ if (ret < 0) {
+ dev_warn(&adev->dev, "cannot register misc device\n");
+ goto err_misc_register;
+ }
+
+ dev_info(&adev->dev, "registration successful\n");
+ return 0;
+
+err_misc_register:
+ iounmap(wdt->base);
+err_ioremap:
+ clk_put(wdt->clk);
+err_clk_get:
+ kfree(wdt);
+ wdt = NULL;
+err_kzalloc:
+ release_mem_region(adev->res.start, resource_size(&adev->res));
+err:
+ dev_err(&adev->dev, "Probe Failed!!!\n");
+ return ret;
+}
+
+static int __devexit sp805_wdt_remove(struct amba_device *adev)
+{
+ misc_deregister(&sp805_wdt_miscdev);
+ iounmap(wdt->base);
+ clk_put(wdt->clk);
+ kfree(wdt);
+ release_mem_region(adev->res.start, resource_size(&adev->res));
+
+ return 0;
+}
+
+static struct amba_id sp805_wdt_ids[] __initdata = {
+ {
+ .id = 0x00141805,
+ .mask = 0x00ffffff,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver sp805_wdt_driver = {
+ .drv = {
+ .name = MODULE_NAME,
+ },
+ .id_table = sp805_wdt_ids,
+ .probe = sp805_wdt_probe,
+ .remove = __devexit_p(sp805_wdt_remove),
+};
+
+static int __init sp805_wdt_init(void)
+{
+ return amba_driver_register(&sp805_wdt_driver);
+}
+module_init(sp805_wdt_init);
+
+static void __exit sp805_wdt_exit(void)
+{
+ amba_driver_unregister(&sp805_wdt_driver);
+}
+module_exit(sp805_wdt_exit);
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Set to 1 to keep watchdog running after device release");
+
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
+MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c
new file mode 100644
index 00000000..b3421fd2
--- /dev/null
+++ b/drivers/watchdog/stmp3xxx_wdt.c
@@ -0,0 +1,296 @@
+/*
+ * Watchdog driver for Freescale STMP37XX/STMP378X
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+#include <mach/platform.h>
+#include <mach/regs-rtc.h>
+
+#define DEFAULT_HEARTBEAT 19
+#define MAX_HEARTBEAT (0x10000000 >> 6)
+
+/* missing bitmask in headers */
+#define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+#define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */
+
+static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock);
+static unsigned long wdt_status;
+static const int nowayout = WATCHDOG_NOWAYOUT;
+static int heartbeat = DEFAULT_HEARTBEAT;
+static unsigned long boot_status;
+
+static void wdt_enable(u32 value)
+{
+ spin_lock(&stmp3xxx_wdt_io_lock);
+ __raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG);
+ stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
+ stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
+ REGS_RTC_BASE + HW_RTC_PERSISTENT1);
+ spin_unlock(&stmp3xxx_wdt_io_lock);
+}
+
+static void wdt_disable(void)
+{
+ spin_lock(&stmp3xxx_wdt_io_lock);
+ stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
+ REGS_RTC_BASE + HW_RTC_PERSISTENT1);
+ stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
+ spin_unlock(&stmp3xxx_wdt_io_lock);
+}
+
+static void wdt_ping(void)
+{
+ wdt_enable(heartbeat * WDOG_COUNTER_RATE);
+}
+
+static int stmp3xxx_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ wdt_ping();
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_ping();
+ }
+
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET |
+ WDIOF_MAGICCLOSE |
+ WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "STMP3XXX Watchdog",
+};
+
+static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_heartbeat, opts;
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, p);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(boot_status, p);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(opts, p)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (opts & WDIOS_DISABLECARD)
+ wdt_disable();
+ else if (opts & WDIOS_ENABLECARD)
+ wdt_ping();
+ else {
+ pr_debug("%s: unknown option 0x%x\n", __func__, opts);
+ ret = -EINVAL;
+ break;
+ }
+ ret = 0;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_ping();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_heartbeat, p)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) {
+ ret = -EINVAL;
+ break;
+ }
+
+ heartbeat = new_heartbeat;
+ wdt_ping();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, p);
+ break;
+ }
+ return ret;
+}
+
+static int stmp3xxx_wdt_release(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+
+ if (!nowayout) {
+ if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
+ wdt_ping();
+ pr_debug("%s: Device closed unexpectdly\n", __func__);
+ ret = -EINVAL;
+ } else {
+ wdt_disable();
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ clear_bit(WDT_IN_USE, &wdt_status);
+
+ return ret;
+}
+
+static const struct file_operations stmp3xxx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = stmp3xxx_wdt_write,
+ .unlocked_ioctl = stmp3xxx_wdt_ioctl,
+ .open = stmp3xxx_wdt_open,
+ .release = stmp3xxx_wdt_release,
+};
+
+static struct miscdevice stmp3xxx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &stmp3xxx_wdt_fops,
+};
+
+static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
+ heartbeat = DEFAULT_HEARTBEAT;
+
+ boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) &
+ BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER;
+ boot_status = !!boot_status;
+ stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
+ REGS_RTC_BASE + HW_RTC_PERSISTENT1);
+ wdt_disable(); /* disable for now */
+
+ ret = misc_register(&stmp3xxx_wdt_miscdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot register misc device\n");
+ return ret;
+ }
+
+ printk(KERN_INFO "stmp3xxx watchdog: initialized, heartbeat %d sec\n",
+ heartbeat);
+
+ return ret;
+}
+
+static int __devexit stmp3xxx_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&stmp3xxx_wdt_miscdev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wdt_suspended;
+static u32 wdt_saved_time;
+
+static int stmp3xxx_wdt_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) &
+ BM_RTC_CTRL_WATCHDOGEN) {
+ wdt_suspended = 1;
+ wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG);
+ wdt_disable();
+ }
+ return 0;
+}
+
+static int stmp3xxx_wdt_resume(struct platform_device *pdev)
+{
+ if (wdt_suspended) {
+ wdt_enable(wdt_saved_time);
+ wdt_suspended = 0;
+ }
+ return 0;
+}
+#else
+#define stmp3xxx_wdt_suspend NULL
+#define stmp3xxx_wdt_resume NULL
+#endif
+
+static struct platform_driver platform_wdt_driver = {
+ .driver = {
+ .name = "stmp3xxx_wdt",
+ },
+ .probe = stmp3xxx_wdt_probe,
+ .remove = __devexit_p(stmp3xxx_wdt_remove),
+ .suspend = stmp3xxx_wdt_suspend,
+ .resume = stmp3xxx_wdt_resume,
+};
+
+static int __init stmp3xxx_wdt_init(void)
+{
+ return platform_driver_register(&platform_wdt_driver);
+}
+
+static void __exit stmp3xxx_wdt_exit(void)
+{
+ return platform_driver_unregister(&platform_wdt_driver);
+}
+
+module_init(stmp3xxx_wdt_init);
+module_exit(stmp3xxx_wdt_exit);
+
+MODULE_DESCRIPTION("STMP3XXX Watchdog Driver");
+MODULE_LICENSE("GPL");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat period in seconds from 1 to "
+ __MODULE_STRING(MAX_HEARTBEAT) ", default "
+ __MODULE_STRING(DEFAULT_HEARTBEAT));
+
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c
new file mode 100644
index 00000000..5a90a4a8
--- /dev/null
+++ b/drivers/watchdog/ts72xx_wdt.c
@@ -0,0 +1,524 @@
+/*
+ * Watchdog driver for Technologic Systems TS-72xx based SBCs
+ * (TS-7200, TS-7250 and TS-7260). These boards have external
+ * glue logic CPLD chip, which includes programmable watchdog
+ * timer.
+ *
+ * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi>
+ *
+ * This driver is based on ep93xx_wdt and wm831x_wdt drivers.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+
+#define TS72XX_WDT_FEED_VAL 0x05
+#define TS72XX_WDT_DEFAULT_TIMEOUT 8
+
+static int timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. "
+ "(1 <= timeout <= 8, default="
+ __MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT)
+ ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+/**
+ * struct ts72xx_wdt - watchdog control structure
+ * @lock: lock that protects this structure
+ * @regval: watchdog timeout value suitable for control register
+ * @flags: flags controlling watchdog device state
+ * @control_reg: watchdog control register
+ * @feed_reg: watchdog feed register
+ * @pdev: back pointer to platform dev
+ */
+struct ts72xx_wdt {
+ struct mutex lock;
+ int regval;
+
+#define TS72XX_WDT_BUSY_FLAG 1
+#define TS72XX_WDT_EXPECT_CLOSE_FLAG 2
+ int flags;
+
+ void __iomem *control_reg;
+ void __iomem *feed_reg;
+
+ struct platform_device *pdev;
+};
+
+struct platform_device *ts72xx_wdt_pdev;
+
+/*
+ * TS-72xx Watchdog supports following timeouts (value written
+ * to control register):
+ * value description
+ * -------------------------
+ * 0x00 watchdog disabled
+ * 0x01 250ms
+ * 0x02 500ms
+ * 0x03 1s
+ * 0x04 reserved
+ * 0x05 2s
+ * 0x06 4s
+ * 0x07 8s
+ *
+ * Timeouts below 1s are not very usable so we don't
+ * allow them at all.
+ *
+ * We provide two functions that convert between these:
+ * timeout_to_regval() and regval_to_timeout().
+ */
+static const struct {
+ int timeout;
+ int regval;
+} ts72xx_wdt_map[] = {
+ { 1, 3 },
+ { 2, 5 },
+ { 4, 6 },
+ { 8, 7 },
+};
+
+/**
+ * timeout_to_regval() - converts given timeout to control register value
+ * @new_timeout: timeout in seconds to be converted
+ *
+ * Function converts given @new_timeout into valid value that can
+ * be programmed into watchdog control register. When conversion is
+ * not possible, function returns %-EINVAL.
+ */
+static int timeout_to_regval(int new_timeout)
+{
+ int i;
+
+ /* first limit it to 1 - 8 seconds */
+ new_timeout = clamp_val(new_timeout, 1, 8);
+
+ for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
+ if (ts72xx_wdt_map[i].timeout >= new_timeout)
+ return ts72xx_wdt_map[i].regval;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * regval_to_timeout() - converts control register value to timeout
+ * @regval: control register value to be converted
+ *
+ * Function converts given @regval to timeout in seconds (1, 2, 4 or 8).
+ * If @regval cannot be converted, function returns %-EINVAL.
+ */
+static int regval_to_timeout(int regval)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
+ if (ts72xx_wdt_map[i].regval == regval)
+ return ts72xx_wdt_map[i].timeout;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * ts72xx_wdt_kick() - kick the watchdog
+ * @wdt: watchdog to be kicked
+ *
+ * Called with @wdt->lock held.
+ */
+static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt)
+{
+ __raw_writeb(TS72XX_WDT_FEED_VAL, wdt->feed_reg);
+}
+
+/**
+ * ts72xx_wdt_start() - starts the watchdog timer
+ * @wdt: watchdog to be started
+ *
+ * This function programs timeout to watchdog timer
+ * and starts it.
+ *
+ * Called with @wdt->lock held.
+ */
+static void ts72xx_wdt_start(struct ts72xx_wdt *wdt)
+{
+ /*
+ * To program the wdt, it first must be "fed" and
+ * only after that (within 30 usecs) the configuration
+ * can be changed.
+ */
+ ts72xx_wdt_kick(wdt);
+ __raw_writeb((u8)wdt->regval, wdt->control_reg);
+}
+
+/**
+ * ts72xx_wdt_stop() - stops the watchdog timer
+ * @wdt: watchdog to be stopped
+ *
+ * Called with @wdt->lock held.
+ */
+static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt)
+{
+ ts72xx_wdt_kick(wdt);
+ __raw_writeb(0, wdt->control_reg);
+}
+
+static int ts72xx_wdt_open(struct inode *inode, struct file *file)
+{
+ struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev);
+ int regval;
+
+ /*
+ * Try to convert default timeout to valid register
+ * value first.
+ */
+ regval = timeout_to_regval(timeout);
+ if (regval < 0) {
+ dev_err(&wdt->pdev->dev,
+ "failed to convert timeout (%d) to register value\n",
+ timeout);
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&wdt->lock))
+ return -ERESTARTSYS;
+
+ if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) {
+ mutex_unlock(&wdt->lock);
+ return -EBUSY;
+ }
+
+ wdt->flags = TS72XX_WDT_BUSY_FLAG;
+ wdt->regval = regval;
+ file->private_data = wdt;
+
+ ts72xx_wdt_start(wdt);
+
+ mutex_unlock(&wdt->lock);
+ return nonseekable_open(inode, file);
+}
+
+static int ts72xx_wdt_release(struct inode *inode, struct file *file)
+{
+ struct ts72xx_wdt *wdt = file->private_data;
+
+ if (mutex_lock_interruptible(&wdt->lock))
+ return -ERESTARTSYS;
+
+ if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) {
+ ts72xx_wdt_stop(wdt);
+ } else {
+ dev_warn(&wdt->pdev->dev,
+ "TS-72XX WDT device closed unexpectly. "
+ "Watchdog timer will not stop!\n");
+ /*
+ * Kick it one more time, to give userland some time
+ * to recover (for example, respawning the kicker
+ * daemon).
+ */
+ ts72xx_wdt_kick(wdt);
+ }
+
+ wdt->flags = 0;
+
+ mutex_unlock(&wdt->lock);
+ return 0;
+}
+
+static ssize_t ts72xx_wdt_write(struct file *file,
+ const char __user *data,
+ size_t len,
+ loff_t *ppos)
+{
+ struct ts72xx_wdt *wdt = file->private_data;
+
+ if (!len)
+ return 0;
+
+ if (mutex_lock_interruptible(&wdt->lock))
+ return -ERESTARTSYS;
+
+ ts72xx_wdt_kick(wdt);
+
+ /*
+ * Support for magic character closing. User process
+ * writes 'V' into the device, just before it is closed.
+ * This means that we know that the wdt timer can be
+ * stopped after user closes the device.
+ */
+ if (!nowayout) {
+ int i;
+
+ for (i = 0; i < len; i++) {
+ char c;
+
+ /* In case it was set long ago */
+ wdt->flags &= ~TS72XX_WDT_EXPECT_CLOSE_FLAG;
+
+ if (get_user(c, data + i)) {
+ mutex_unlock(&wdt->lock);
+ return -EFAULT;
+ }
+ if (c == 'V') {
+ wdt->flags |= TS72XX_WDT_EXPECT_CLOSE_FLAG;
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&wdt->lock);
+ return len;
+}
+
+static const struct watchdog_info winfo = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "TS-72XX WDT",
+};
+
+static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct ts72xx_wdt *wdt = file->private_data;
+ void __user *argp = (void __user *)arg;
+ int __user *p = (int __user *)argp;
+ int error = 0;
+
+ if (mutex_lock_interruptible(&wdt->lock))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ error = copy_to_user(argp, &winfo, sizeof(winfo));
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_KEEPALIVE:
+ ts72xx_wdt_kick(wdt);
+ break;
+
+ case WDIOC_SETOPTIONS: {
+ int options;
+
+ if (get_user(options, p)) {
+ error = -EFAULT;
+ break;
+ }
+
+ error = -EINVAL;
+
+ if ((options & WDIOS_DISABLECARD) != 0) {
+ ts72xx_wdt_stop(wdt);
+ error = 0;
+ }
+ if ((options & WDIOS_ENABLECARD) != 0) {
+ ts72xx_wdt_start(wdt);
+ error = 0;
+ }
+
+ break;
+ }
+
+ case WDIOC_SETTIMEOUT: {
+ int new_timeout;
+
+ if (get_user(new_timeout, p)) {
+ error = -EFAULT;
+ } else {
+ int regval;
+
+ regval = timeout_to_regval(new_timeout);
+ if (regval < 0) {
+ error = -EINVAL;
+ } else {
+ ts72xx_wdt_stop(wdt);
+ wdt->regval = regval;
+ ts72xx_wdt_start(wdt);
+ }
+ }
+ if (error)
+ break;
+
+ /*FALLTHROUGH*/
+ }
+
+ case WDIOC_GETTIMEOUT:
+ if (put_user(regval_to_timeout(wdt->regval), p))
+ error = -EFAULT;
+ break;
+
+ default:
+ error = -ENOTTY;
+ break;
+ }
+
+ mutex_unlock(&wdt->lock);
+ return error;
+}
+
+static const struct file_operations ts72xx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = ts72xx_wdt_open,
+ .release = ts72xx_wdt_release,
+ .write = ts72xx_wdt_write,
+ .unlocked_ioctl = ts72xx_wdt_ioctl,
+};
+
+static struct miscdevice ts72xx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ts72xx_wdt_fops,
+};
+
+static __devinit int ts72xx_wdt_probe(struct platform_device *pdev)
+{
+ struct ts72xx_wdt *wdt;
+ struct resource *r1, *r2;
+ int error = 0;
+
+ wdt = kzalloc(sizeof(struct ts72xx_wdt), GFP_KERNEL);
+ if (!wdt) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r1) {
+ dev_err(&pdev->dev, "failed to get memory resource\n");
+ error = -ENODEV;
+ goto fail;
+ }
+
+ r1 = request_mem_region(r1->start, resource_size(r1), pdev->name);
+ if (!r1) {
+ dev_err(&pdev->dev, "cannot request memory region\n");
+ error = -EBUSY;
+ goto fail;
+ }
+
+ wdt->control_reg = ioremap(r1->start, resource_size(r1));
+ if (!wdt->control_reg) {
+ dev_err(&pdev->dev, "failed to map memory\n");
+ error = -ENODEV;
+ goto fail_free_control;
+ }
+
+ r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r2) {
+ dev_err(&pdev->dev, "failed to get memory resource\n");
+ error = -ENODEV;
+ goto fail_unmap_control;
+ }
+
+ r2 = request_mem_region(r2->start, resource_size(r2), pdev->name);
+ if (!r2) {
+ dev_err(&pdev->dev, "cannot request memory region\n");
+ error = -EBUSY;
+ goto fail_unmap_control;
+ }
+
+ wdt->feed_reg = ioremap(r2->start, resource_size(r2));
+ if (!wdt->feed_reg) {
+ dev_err(&pdev->dev, "failed to map memory\n");
+ error = -ENODEV;
+ goto fail_free_feed;
+ }
+
+ platform_set_drvdata(pdev, wdt);
+ ts72xx_wdt_pdev = pdev;
+ wdt->pdev = pdev;
+ mutex_init(&wdt->lock);
+
+ /* make sure that the watchdog is disabled */
+ ts72xx_wdt_stop(wdt);
+
+ error = misc_register(&ts72xx_wdt_miscdev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register miscdev\n");
+ goto fail_unmap_feed;
+ }
+
+ dev_info(&pdev->dev, "TS-72xx Watchdog driver\n");
+
+ return 0;
+
+fail_unmap_feed:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(wdt->feed_reg);
+fail_free_feed:
+ release_mem_region(r2->start, resource_size(r2));
+fail_unmap_control:
+ iounmap(wdt->control_reg);
+fail_free_control:
+ release_mem_region(r1->start, resource_size(r1));
+fail:
+ kfree(wdt);
+ return error;
+}
+
+static __devexit int ts72xx_wdt_remove(struct platform_device *pdev)
+{
+ struct ts72xx_wdt *wdt = platform_get_drvdata(pdev);
+ struct resource *res;
+ int error;
+
+ error = misc_deregister(&ts72xx_wdt_miscdev);
+ platform_set_drvdata(pdev, NULL);
+
+ iounmap(wdt->feed_reg);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(res->start, resource_size(res));
+
+ iounmap(wdt->control_reg);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(wdt);
+ return error;
+}
+
+static struct platform_driver ts72xx_wdt_driver = {
+ .probe = ts72xx_wdt_probe,
+ .remove = __devexit_p(ts72xx_wdt_remove),
+ .driver = {
+ .name = "ts72xx-wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static __init int ts72xx_wdt_init(void)
+{
+ return platform_driver_register(&ts72xx_wdt_driver);
+}
+module_init(ts72xx_wdt_init);
+
+static __exit void ts72xx_wdt_exit(void)
+{
+ platform_driver_unregister(&ts72xx_wdt_driver);
+}
+module_exit(ts72xx_wdt_exit);
+
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
+MODULE_DESCRIPTION("TS-72xx SBC Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ts72xx-wdt");
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c
new file mode 100644
index 00000000..b5045ca7
--- /dev/null
+++ b/drivers/watchdog/twl4030_wdt.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) Nokia Corporation
+ *
+ * Written by Timo Kokkonen <timo.t.kokkonen at nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/i2c/twl.h>
+
+#define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3
+
+#define TWL4030_WDT_STATE_OPEN 0x1
+#define TWL4030_WDT_STATE_ACTIVE 0x8
+
+static struct platform_device *twl4030_wdt_dev;
+
+struct twl4030_wdt {
+ struct miscdevice miscdev;
+ int timer_margin;
+ unsigned long state;
+};
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int twl4030_wdt_write(unsigned char val)
+{
+ return twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val,
+ TWL4030_WATCHDOG_CFG_REG_OFFS);
+}
+
+static int twl4030_wdt_enable(struct twl4030_wdt *wdt)
+{
+ return twl4030_wdt_write(wdt->timer_margin + 1);
+}
+
+static int twl4030_wdt_disable(struct twl4030_wdt *wdt)
+{
+ return twl4030_wdt_write(0);
+}
+
+static int twl4030_wdt_set_timeout(struct twl4030_wdt *wdt, int timeout)
+{
+ if (timeout < 0 || timeout > 30) {
+ dev_warn(wdt->miscdev.parent,
+ "Timeout can only be in the range [0-30] seconds");
+ return -EINVAL;
+ }
+ wdt->timer_margin = timeout;
+ return twl4030_wdt_enable(wdt);
+}
+
+static ssize_t twl4030_wdt_write_fop(struct file *file,
+ const char __user *data, size_t len, loff_t *ppos)
+{
+ struct twl4030_wdt *wdt = file->private_data;
+
+ if (len)
+ twl4030_wdt_enable(wdt);
+
+ return len;
+}
+
+static long twl4030_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_margin;
+ struct twl4030_wdt *wdt = file->private_data;
+
+ static const struct watchdog_info twl4030_wd_ident = {
+ .identity = "TWL4030 Watchdog",
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 0,
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &twl4030_wd_ident,
+ sizeof(twl4030_wd_ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_KEEPALIVE:
+ twl4030_wdt_enable(wdt);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, p))
+ return -EFAULT;
+ if (twl4030_wdt_set_timeout(wdt, new_margin))
+ return -EINVAL;
+ return put_user(wdt->timer_margin, p);
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdt->timer_margin, p);
+
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static int twl4030_wdt_open(struct inode *inode, struct file *file)
+{
+ struct twl4030_wdt *wdt = platform_get_drvdata(twl4030_wdt_dev);
+
+ /* /dev/watchdog can only be opened once */
+ if (test_and_set_bit(0, &wdt->state))
+ return -EBUSY;
+
+ wdt->state |= TWL4030_WDT_STATE_ACTIVE;
+ file->private_data = (void *) wdt;
+
+ twl4030_wdt_enable(wdt);
+ return nonseekable_open(inode, file);
+}
+
+static int twl4030_wdt_release(struct inode *inode, struct file *file)
+{
+ struct twl4030_wdt *wdt = file->private_data;
+ if (nowayout) {
+ dev_alert(wdt->miscdev.parent,
+ "Unexpected close, watchdog still running!\n");
+ twl4030_wdt_enable(wdt);
+ } else {
+ if (twl4030_wdt_disable(wdt))
+ return -EFAULT;
+ wdt->state &= ~TWL4030_WDT_STATE_ACTIVE;
+ }
+
+ clear_bit(0, &wdt->state);
+ return 0;
+}
+
+static const struct file_operations twl4030_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = twl4030_wdt_open,
+ .release = twl4030_wdt_release,
+ .unlocked_ioctl = twl4030_wdt_ioctl,
+ .write = twl4030_wdt_write_fop,
+};
+
+static int __devinit twl4030_wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct twl4030_wdt *wdt;
+
+ wdt = kzalloc(sizeof(struct twl4030_wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->state = 0;
+ wdt->timer_margin = 30;
+ wdt->miscdev.parent = &pdev->dev;
+ wdt->miscdev.fops = &twl4030_wdt_fops;
+ wdt->miscdev.minor = WATCHDOG_MINOR;
+ wdt->miscdev.name = "watchdog";
+
+ platform_set_drvdata(pdev, wdt);
+
+ twl4030_wdt_dev = pdev;
+
+ twl4030_wdt_disable(wdt);
+
+ ret = misc_register(&wdt->miscdev);
+ if (ret) {
+ dev_err(wdt->miscdev.parent,
+ "Failed to register misc device\n");
+ platform_set_drvdata(pdev, NULL);
+ kfree(wdt);
+ twl4030_wdt_dev = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+static int __devexit twl4030_wdt_remove(struct platform_device *pdev)
+{
+ struct twl4030_wdt *wdt = platform_get_drvdata(pdev);
+
+ if (wdt->state & TWL4030_WDT_STATE_ACTIVE)
+ if (twl4030_wdt_disable(wdt))
+ return -EFAULT;
+
+ wdt->state &= ~TWL4030_WDT_STATE_ACTIVE;
+ misc_deregister(&wdt->miscdev);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(wdt);
+ twl4030_wdt_dev = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct twl4030_wdt *wdt = platform_get_drvdata(pdev);
+ if (wdt->state & TWL4030_WDT_STATE_ACTIVE)
+ return twl4030_wdt_disable(wdt);
+
+ return 0;
+}
+
+static int twl4030_wdt_resume(struct platform_device *pdev)
+{
+ struct twl4030_wdt *wdt = platform_get_drvdata(pdev);
+ if (wdt->state & TWL4030_WDT_STATE_ACTIVE)
+ return twl4030_wdt_enable(wdt);
+
+ return 0;
+}
+#else
+#define twl4030_wdt_suspend NULL
+#define twl4030_wdt_resume NULL
+#endif
+
+static struct platform_driver twl4030_wdt_driver = {
+ .probe = twl4030_wdt_probe,
+ .remove = __devexit_p(twl4030_wdt_remove),
+ .suspend = twl4030_wdt_suspend,
+ .resume = twl4030_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "twl4030_wdt",
+ },
+};
+
+static int __devinit twl4030_wdt_init(void)
+{
+ return platform_driver_register(&twl4030_wdt_driver);
+}
+module_init(twl4030_wdt_init);
+
+static void __devexit twl4030_wdt_exit(void)
+{
+ platform_driver_unregister(&twl4030_wdt_driver);
+}
+module_exit(twl4030_wdt_exit);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:twl4030_wdt");
+
diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c
new file mode 100644
index 00000000..9e9ed7bf
--- /dev/null
+++ b/drivers/watchdog/txx9wdt.c
@@ -0,0 +1,269 @@
+/*
+ * txx9wdt: A Hardware Watchdog Driver for TXx9 SoCs
+ *
+ * Copyright (C) 2007 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <asm/txx9tmr.h>
+
+#define TIMER_MARGIN 60 /* Default is 60 seconds */
+
+static int timeout = TIMER_MARGIN; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. "
+ "(0<timeout<((2^" __MODULE_STRING(TXX9_TIMER_BITS) ")/(IMCLK/256)), "
+ "default=" __MODULE_STRING(TIMER_MARGIN) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#define WD_TIMER_CCD 7 /* 1/256 */
+#define WD_TIMER_CLK (clk_get_rate(txx9_imclk) / (2 << WD_TIMER_CCD))
+#define WD_MAX_TIMEOUT ((0xffffffff >> (32 - TXX9_TIMER_BITS)) / WD_TIMER_CLK)
+
+static unsigned long txx9wdt_alive;
+static int expect_close;
+static struct txx9_tmr_reg __iomem *txx9wdt_reg;
+static struct clk *txx9_imclk;
+static DEFINE_SPINLOCK(txx9_lock);
+
+static void txx9wdt_ping(void)
+{
+ spin_lock(&txx9_lock);
+ __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
+ spin_unlock(&txx9_lock);
+}
+
+static void txx9wdt_start(void)
+{
+ spin_lock(&txx9_lock);
+ __raw_writel(WD_TIMER_CLK * timeout, &txx9wdt_reg->cpra);
+ __raw_writel(WD_TIMER_CCD, &txx9wdt_reg->ccdr);
+ __raw_writel(0, &txx9wdt_reg->tisr); /* clear pending interrupt */
+ __raw_writel(TXx9_TMTCR_TCE | TXx9_TMTCR_CCDE | TXx9_TMTCR_TMODE_WDOG,
+ &txx9wdt_reg->tcr);
+ __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
+ spin_unlock(&txx9_lock);
+}
+
+static void txx9wdt_stop(void)
+{
+ spin_lock(&txx9_lock);
+ __raw_writel(TXx9_TMWTMR_WDIS, &txx9wdt_reg->wtmr);
+ __raw_writel(__raw_readl(&txx9wdt_reg->tcr) & ~TXx9_TMTCR_TCE,
+ &txx9wdt_reg->tcr);
+ spin_unlock(&txx9_lock);
+}
+
+static int txx9wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &txx9wdt_alive))
+ return -EBUSY;
+
+ if (__raw_readl(&txx9wdt_reg->tcr) & TXx9_TMTCR_TCE) {
+ clear_bit(0, &txx9wdt_alive);
+ return -EBUSY;
+ }
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ txx9wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int txx9wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close)
+ txx9wdt_stop();
+ else {
+ printk(KERN_CRIT "txx9wdt: "
+ "Unexpected close, not stopping watchdog!\n");
+ txx9wdt_ping();
+ }
+ clear_bit(0, &txx9wdt_alive);
+ expect_close = 0;
+ return 0;
+}
+
+static ssize_t txx9wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 1;
+ }
+ }
+ txx9wdt_ping();
+ }
+ return len;
+}
+
+static long txx9wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_timeout;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = "Hardware Watchdog for TXx9",
+ };
+
+ 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_KEEPALIVE:
+ txx9wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (new_timeout < 1 || new_timeout > WD_MAX_TIMEOUT)
+ return -EINVAL;
+ timeout = new_timeout;
+ txx9wdt_stop();
+ txx9wdt_start();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations txx9wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = txx9wdt_write,
+ .unlocked_ioctl = txx9wdt_ioctl,
+ .open = txx9wdt_open,
+ .release = txx9wdt_release,
+};
+
+static struct miscdevice txx9wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &txx9wdt_fops,
+};
+
+static int __init txx9wdt_probe(struct platform_device *dev)
+{
+ struct resource *res;
+ int ret;
+
+ txx9_imclk = clk_get(NULL, "imbus_clk");
+ if (IS_ERR(txx9_imclk)) {
+ ret = PTR_ERR(txx9_imclk);
+ txx9_imclk = NULL;
+ goto exit;
+ }
+ ret = clk_enable(txx9_imclk);
+ if (ret) {
+ clk_put(txx9_imclk);
+ txx9_imclk = NULL;
+ goto exit;
+ }
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res)
+ goto exit_busy;
+ if (!devm_request_mem_region(&dev->dev, res->start, resource_size(res),
+ "txx9wdt"))
+ goto exit_busy;
+ txx9wdt_reg = devm_ioremap(&dev->dev, res->start, resource_size(res));
+ if (!txx9wdt_reg)
+ goto exit_busy;
+
+ ret = misc_register(&txx9wdt_miscdev);
+ if (ret) {
+ goto exit;
+ }
+
+ printk(KERN_INFO "Hardware Watchdog Timer for TXx9: "
+ "timeout=%d sec (max %ld) (nowayout= %d)\n",
+ timeout, WD_MAX_TIMEOUT, nowayout);
+
+ return 0;
+exit_busy:
+ ret = -EBUSY;
+exit:
+ if (txx9_imclk) {
+ clk_disable(txx9_imclk);
+ clk_put(txx9_imclk);
+ }
+ return ret;
+}
+
+static int __exit txx9wdt_remove(struct platform_device *dev)
+{
+ misc_deregister(&txx9wdt_miscdev);
+ clk_disable(txx9_imclk);
+ clk_put(txx9_imclk);
+ return 0;
+}
+
+static void txx9wdt_shutdown(struct platform_device *dev)
+{
+ txx9wdt_stop();
+}
+
+static struct platform_driver txx9wdt_driver = {
+ .remove = __exit_p(txx9wdt_remove),
+ .shutdown = txx9wdt_shutdown,
+ .driver = {
+ .name = "txx9wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init watchdog_init(void)
+{
+ return platform_driver_probe(&txx9wdt_driver, txx9wdt_probe);
+}
+
+static void __exit watchdog_exit(void)
+{
+ platform_driver_unregister(&txx9wdt_driver);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+
+MODULE_DESCRIPTION("TXx9 Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:txx9wdt");
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
new file mode 100644
index 00000000..e5c91d44
--- /dev/null
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -0,0 +1,385 @@
+/*
+ * w83627hf/thf WDT driver
+ *
+ * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
+ * added support for W83627THF.
+ *
+ * (c) Copyright 2003,2007 Pádraig Brady <P@draigBrady.com>
+ *
+ * Based on advantechwdt.c which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
+#define PFX WATCHDOG_NAME ": "
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+
+static unsigned long wdt_is_open;
+static char expect_close;
+static DEFINE_SPINLOCK(io_lock);
+
+/* You must set this - there is no sane way to probe for this board. */
+static int wdt_io = 0x2E;
+module_param(wdt_io, int, 0);
+MODULE_PARM_DESC(wdt_io, "w83627hf/thf WDT io port (default 0x2E)");
+
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Kernel methods.
+ */
+
+#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
+#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
+ (same as EFER) */
+#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
+
+static void w83627hf_select_wd_register(void)
+{
+ unsigned char c;
+ outb_p(0x87, WDT_EFER); /* Enter extended function mode */
+ outb_p(0x87, WDT_EFER); /* Again according to manual */
+
+ outb(0x20, WDT_EFER); /* check chip version */
+ c = inb(WDT_EFDR);
+ if (c == 0x82) { /* W83627THF */
+ outb_p(0x2b, WDT_EFER); /* select GPIO3 */
+ c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */
+ outb_p(0x2b, WDT_EFER);
+ outb_p(c, WDT_EFDR); /* set GPIO3 to WDT0 */
+ } else if (c == 0x88 || c == 0xa0) { /* W83627EHF / W83627DHG */
+ outb_p(0x2d, WDT_EFER); /* select GPIO5 */
+ c = inb_p(WDT_EFDR) & ~0x01; /* PIN77 -> WDT0# */
+ outb_p(0x2d, WDT_EFER);
+ outb_p(c, WDT_EFDR); /* set GPIO5 to WDT0 */
+ }
+
+ outb_p(0x07, WDT_EFER); /* point to logical device number reg */
+ outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
+ outb_p(0x30, WDT_EFER); /* select CR30 */
+ outb_p(0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
+}
+
+static void w83627hf_unselect_wd_register(void)
+{
+ outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
+}
+
+/* tyan motherboards seem to set F5 to 0x4C ?
+ * So explicitly init to appropriate value. */
+
+static void w83627hf_init(void)
+{
+ unsigned char t;
+
+ w83627hf_select_wd_register();
+
+ outb_p(0xF6, WDT_EFER); /* Select CRF6 */
+ t = inb_p(WDT_EFDR); /* read CRF6 */
+ if (t != 0) {
+ printk(KERN_INFO PFX
+ "Watchdog already running. Resetting timeout to %d sec\n",
+ timeout);
+ outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
+ }
+
+ outb_p(0xF5, WDT_EFER); /* Select CRF5 */
+ t = inb_p(WDT_EFDR); /* read CRF5 */
+ t &= ~0x0C; /* set second mode & disable keyboard
+ turning off watchdog */
+ t |= 0x02; /* enable the WDTO# output low pulse
+ to the KBRST# pin (PIN60) */
+ outb_p(t, WDT_EFDR); /* Write back to CRF5 */
+
+ outb_p(0xF7, WDT_EFER); /* Select CRF7 */
+ t = inb_p(WDT_EFDR); /* read CRF7 */
+ t &= ~0xC0; /* disable keyboard & mouse turning off
+ watchdog */
+ outb_p(t, WDT_EFDR); /* Write back to CRF7 */
+
+ w83627hf_unselect_wd_register();
+}
+
+static void wdt_ctrl(int timeout)
+{
+ spin_lock(&io_lock);
+
+ w83627hf_select_wd_register();
+
+ outb_p(0xF6, WDT_EFER); /* Select CRF6 */
+ outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */
+
+ w83627hf_unselect_wd_register();
+
+ spin_unlock(&io_lock);
+}
+
+static int wdt_ping(void)
+{
+ wdt_ctrl(timeout);
+ return 0;
+}
+
+static int wdt_disable(void)
+{
+ wdt_ctrl(0);
+ return 0;
+}
+
+static int wdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > 255)
+ return -EINVAL;
+ timeout = t;
+ return 0;
+}
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ wdt_ping();
+ }
+ return count;
+}
+
+static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_timeout;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "W83627HF WDT",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+ if (options & WDIOS_DISABLECARD) {
+ wdt_disable();
+ retval = 0;
+ }
+ if (options & WDIOS_ENABLECARD) {
+ wdt_ping();
+ retval = 0;
+ }
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ wdt_ping();
+ break;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (wdt_set_heartbeat(new_timeout))
+ return -EINVAL;
+ wdt_ping();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+
+ wdt_ping();
+ return nonseekable_open(inode, file);
+}
+
+static int wdt_close(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ wdt_disable();
+ else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ wdt_ping();
+ }
+ expect_close = 0;
+ clear_bit(0, &wdt_is_open);
+ return 0;
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_disable(); /* Turn the WDT off */
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdt_write,
+ .unlocked_ioctl = wdt_ioctl,
+ .open = wdt_open,
+ .release = wdt_close,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static int __init wdt_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising.\n");
+
+ if (wdt_set_heartbeat(timeout)) {
+ wdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 1 <= timeout <= 255, using %d\n",
+ WATCHDOG_TIMEOUT);
+ }
+
+ if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ wdt_io);
+ ret = -EIO;
+ goto out;
+ }
+
+ w83627hf_init();
+
+ ret = register_reboot_notifier(&wdt_notifier);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ goto unreg_regions;
+ }
+
+ ret = misc_register(&wdt_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto unreg_reboot;
+ }
+
+ printk(KERN_INFO PFX
+ "initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+out:
+ return ret;
+unreg_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+unreg_regions:
+ release_region(wdt_io, 1);
+ goto out;
+}
+
+static void __exit wdt_exit(void)
+{
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(wdt_io, 1);
+}
+
+module_init(wdt_init);
+module_exit(wdt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pádraig Brady <P@draigBrady.com>");
+MODULE_DESCRIPTION("w83627hf/thf WDT driver");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/w83697hf_wdt.c b/drivers/watchdog/w83697hf_wdt.c
new file mode 100644
index 00000000..af08972d
--- /dev/null
+++ b/drivers/watchdog/w83697hf_wdt.c
@@ -0,0 +1,469 @@
+/*
+ * w83697hf/hg WDT driver
+ *
+ * (c) Copyright 2006 Samuel Tardieu <sam@rfc1149.net>
+ * (c) Copyright 2006 Marcus Junker <junker@anduras.de>
+ *
+ * Based on w83627hf_wdt.c which is based on advantechwdt.c
+ * which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
+ *
+ * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Marcus Junker nor ANDURAS AG admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define WATCHDOG_NAME "w83697hf/hg WDT"
+#define PFX WATCHDOG_NAME ": "
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+#define WATCHDOG_EARLY_DISABLE 1 /* Disable until userland kicks in */
+
+static unsigned long wdt_is_open;
+static char expect_close;
+static DEFINE_SPINLOCK(io_lock);
+
+/* You must set this - there is no sane way to probe for this board. */
+static int wdt_io = 0x2e;
+module_param(wdt_io, int, 0);
+MODULE_PARM_DESC(wdt_io,
+ "w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)");
+
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <=255 (default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int early_disable = WATCHDOG_EARLY_DISABLE;
+module_param(early_disable, int, 0);
+MODULE_PARM_DESC(early_disable,
+ "Watchdog gets disabled at boot time (default="
+ __MODULE_STRING(WATCHDOG_EARLY_DISABLE) ")");
+
+/*
+ * Kernel methods.
+ */
+
+#define W83697HF_EFER (wdt_io + 0) /* Extended Function Enable Register */
+#define W83697HF_EFIR (wdt_io + 0) /* Extended Function Index Register
+ (same as EFER) */
+#define W83697HF_EFDR (wdt_io + 1) /* Extended Function Data Register */
+
+static inline void w83697hf_unlock(void)
+{
+ outb_p(0x87, W83697HF_EFER); /* Enter extended function mode */
+ outb_p(0x87, W83697HF_EFER); /* Again according to manual */
+}
+
+static inline void w83697hf_lock(void)
+{
+ outb_p(0xAA, W83697HF_EFER); /* Leave extended function mode */
+}
+
+/*
+ * The three functions w83697hf_get_reg(), w83697hf_set_reg() and
+ * w83697hf_write_timeout() must be called with the device unlocked.
+ */
+
+static unsigned char w83697hf_get_reg(unsigned char reg)
+{
+ outb_p(reg, W83697HF_EFIR);
+ return inb_p(W83697HF_EFDR);
+}
+
+static void w83697hf_set_reg(unsigned char reg, unsigned char data)
+{
+ outb_p(reg, W83697HF_EFIR);
+ outb_p(data, W83697HF_EFDR);
+}
+
+static void w83697hf_write_timeout(int timeout)
+{
+ /* Write Timeout counter to CRF4 */
+ w83697hf_set_reg(0xF4, timeout);
+}
+
+static void w83697hf_select_wdt(void)
+{
+ w83697hf_unlock();
+ w83697hf_set_reg(0x07, 0x08); /* Switch to logic device 8 (GPIO2) */
+}
+
+static inline void w83697hf_deselect_wdt(void)
+{
+ w83697hf_lock();
+}
+
+static void w83697hf_init(void)
+{
+ unsigned char bbuf;
+
+ w83697hf_select_wdt();
+
+ bbuf = w83697hf_get_reg(0x29);
+ bbuf &= ~0x60;
+ bbuf |= 0x20;
+
+ /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
+ w83697hf_set_reg(0x29, bbuf);
+
+ bbuf = w83697hf_get_reg(0xF3);
+ bbuf &= ~0x04;
+ w83697hf_set_reg(0xF3, bbuf); /* Count mode is seconds */
+
+ w83697hf_deselect_wdt();
+}
+
+static void wdt_ping(void)
+{
+ spin_lock(&io_lock);
+ w83697hf_select_wdt();
+
+ w83697hf_write_timeout(timeout);
+
+ w83697hf_deselect_wdt();
+ spin_unlock(&io_lock);
+}
+
+static void wdt_enable(void)
+{
+ spin_lock(&io_lock);
+ w83697hf_select_wdt();
+
+ w83697hf_write_timeout(timeout);
+ w83697hf_set_reg(0x30, 1); /* Enable timer */
+
+ w83697hf_deselect_wdt();
+ spin_unlock(&io_lock);
+}
+
+static void wdt_disable(void)
+{
+ spin_lock(&io_lock);
+ w83697hf_select_wdt();
+
+ w83697hf_set_reg(0x30, 0); /* Disable timer */
+ w83697hf_write_timeout(0);
+
+ w83697hf_deselect_wdt();
+ spin_unlock(&io_lock);
+}
+
+static unsigned char wdt_running(void)
+{
+ unsigned char t;
+
+ spin_lock(&io_lock);
+ w83697hf_select_wdt();
+
+ t = w83697hf_get_reg(0xF4); /* Read timer */
+
+ w83697hf_deselect_wdt();
+ spin_unlock(&io_lock);
+
+ return t;
+}
+
+static int wdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > 255)
+ return -EINVAL;
+
+ timeout = t;
+ return 0;
+}
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ wdt_ping();
+ }
+ return count;
+}
+
+static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_timeout;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
+ | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "W83697HF WDT",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ wdt_disable();
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ wdt_enable();
+ retval = 0;
+ }
+
+ return retval;
+ }
+
+ case WDIOC_KEEPALIVE:
+ wdt_ping();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (wdt_set_heartbeat(new_timeout))
+ return -EINVAL;
+ wdt_ping();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+
+ wdt_enable();
+ return nonseekable_open(inode, file);
+}
+
+static int wdt_close(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ wdt_disable();
+ else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ wdt_ping();
+ }
+ expect_close = 0;
+ clear_bit(0, &wdt_is_open);
+ return 0;
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_disable(); /* Turn the WDT off */
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdt_write,
+ .unlocked_ioctl = wdt_ioctl,
+ .open = wdt_open,
+ .release = wdt_close,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static int w83697hf_check_wdt(void)
+{
+ if (!request_region(wdt_io, 2, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX
+ "I/O address 0x%x already in use\n", wdt_io);
+ return -EIO;
+ }
+
+ printk(KERN_DEBUG PFX
+ "Looking for watchdog at address 0x%x\n", wdt_io);
+ w83697hf_unlock();
+ if (w83697hf_get_reg(0x20) == 0x60) {
+ printk(KERN_INFO PFX
+ "watchdog found at address 0x%x\n", wdt_io);
+ w83697hf_lock();
+ return 0;
+ }
+ /* Reprotect in case it was a compatible device */
+ w83697hf_lock();
+
+ printk(KERN_INFO PFX "watchdog not found at address 0x%x\n", wdt_io);
+ release_region(wdt_io, 2);
+ return -EIO;
+}
+
+static int w83697hf_ioports[] = { 0x2e, 0x4e, 0x00 };
+
+static int __init wdt_init(void)
+{
+ int ret, i, found = 0;
+
+ printk(KERN_INFO PFX "WDT driver for W83697HF/HG initializing\n");
+
+ if (wdt_io == 0) {
+ /* we will autodetect the W83697HF/HG watchdog */
+ for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) {
+ wdt_io = w83697hf_ioports[i];
+ if (!w83697hf_check_wdt())
+ found++;
+ }
+ } else {
+ if (!w83697hf_check_wdt())
+ found++;
+ }
+
+ if (!found) {
+ printk(KERN_ERR PFX "No W83697HF/HG could be found\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ w83697hf_init();
+ if (early_disable) {
+ if (wdt_running())
+ printk(KERN_WARNING PFX "Stopping previously enabled "
+ "watchdog until userland kicks in\n");
+ wdt_disable();
+ }
+
+ if (wdt_set_heartbeat(timeout)) {
+ wdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 1 <= timeout <= 255, using %d\n",
+ WATCHDOG_TIMEOUT);
+ }
+
+ ret = register_reboot_notifier(&wdt_notifier);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ goto unreg_regions;
+ }
+
+ ret = misc_register(&wdt_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto unreg_reboot;
+ }
+
+ printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+out:
+ return ret;
+unreg_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+unreg_regions:
+ release_region(wdt_io, 2);
+ goto out;
+}
+
+static void __exit wdt_exit(void)
+{
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(wdt_io, 2);
+}
+
+module_init(wdt_init);
+module_exit(wdt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marcus Junker <junker@anduras.de>, "
+ "Samuel Tardieu <sam@rfc1149.net>");
+MODULE_DESCRIPTION("w83697hf/hg WDT driver");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c
new file mode 100644
index 00000000..be9c4d83
--- /dev/null
+++ b/drivers/watchdog/w83697ug_wdt.c
@@ -0,0 +1,403 @@
+/*
+ * w83697ug/uf WDT driver
+ *
+ * (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net>
+ * reused original code to support w83697ug/uf.
+ *
+ * Based on w83627hf_wdt.c which is based on advantechwdt.c
+ * which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
+ * added support for W83627THF.
+ *
+ * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
+ *
+ * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
+ * http://www.redhat.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@redhat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define WATCHDOG_NAME "w83697ug/uf WDT"
+#define PFX WATCHDOG_NAME ": "
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+
+static unsigned long wdt_is_open;
+static char expect_close;
+static DEFINE_SPINLOCK(io_lock);
+
+static int wdt_io = 0x2e;
+module_param(wdt_io, int, 0);
+MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)");
+
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <=255 (default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Kernel methods.
+ */
+
+#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
+#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
+ (same as EFER) */
+#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
+
+static int w83697ug_select_wd_register(void)
+{
+ unsigned char c;
+ unsigned char version;
+
+ outb_p(0x87, WDT_EFER); /* Enter extended function mode */
+ outb_p(0x87, WDT_EFER); /* Again according to manual */
+
+ outb(0x20, WDT_EFER); /* check chip version */
+ version = inb(WDT_EFDR);
+
+ if (version == 0x68) { /* W83697UG */
+ printk(KERN_INFO PFX "Watchdog chip version 0x%02x = "
+ "W83697UG/UF found at 0x%04x\n", version, wdt_io);
+
+ outb_p(0x2b, WDT_EFER);
+ c = inb_p(WDT_EFDR); /* select WDT0 */
+ c &= ~0x04;
+ outb_p(0x2b, WDT_EFER);
+ outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */
+
+ } else {
+ printk(KERN_ERR PFX "No W83697UG/UF could be found\n");
+ return -ENODEV;
+ }
+
+ outb_p(0x07, WDT_EFER); /* point to logical device number reg */
+ outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
+ outb_p(0x30, WDT_EFER); /* select CR30 */
+ c = inb_p(WDT_EFDR);
+ outb_p(c | 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
+
+ return 0;
+}
+
+static void w83697ug_unselect_wd_register(void)
+{
+ outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
+}
+
+static int w83697ug_init(void)
+{
+ int ret;
+ unsigned char t;
+
+ ret = w83697ug_select_wd_register();
+ if (ret != 0)
+ return ret;
+
+ outb_p(0xF6, WDT_EFER); /* Select CRF6 */
+ t = inb_p(WDT_EFDR); /* read CRF6 */
+ if (t != 0) {
+ printk(KERN_INFO PFX "Watchdog already running."
+ " Resetting timeout to %d sec\n", timeout);
+ outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
+ }
+ outb_p(0xF5, WDT_EFER); /* Select CRF5 */
+ t = inb_p(WDT_EFDR); /* read CRF5 */
+ t &= ~0x0C; /* set second mode &
+ disable keyboard turning off watchdog */
+ outb_p(t, WDT_EFDR); /* Write back to CRF5 */
+
+ w83697ug_unselect_wd_register();
+ return 0;
+}
+
+static void wdt_ctrl(int timeout)
+{
+ spin_lock(&io_lock);
+
+ if (w83697ug_select_wd_register() < 0) {
+ spin_unlock(&io_lock);
+ return;
+ }
+
+ outb_p(0xF4, WDT_EFER); /* Select CRF4 */
+ outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */
+
+ w83697ug_unselect_wd_register();
+
+ spin_unlock(&io_lock);
+}
+
+static int wdt_ping(void)
+{
+ wdt_ctrl(timeout);
+ return 0;
+}
+
+static int wdt_disable(void)
+{
+ wdt_ctrl(0);
+ return 0;
+}
+
+static int wdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > 255)
+ return -EINVAL;
+
+ timeout = t;
+ return 0;
+}
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ wdt_ping();
+ }
+ return count;
+}
+
+static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_timeout;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "W83697UG WDT",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ wdt_disable();
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ wdt_ping();
+ retval = 0;
+ }
+
+ return retval;
+ }
+
+ case WDIOC_KEEPALIVE:
+ wdt_ping();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (wdt_set_heartbeat(new_timeout))
+ return -EINVAL;
+ wdt_ping();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+
+ wdt_ping();
+ return nonseekable_open(inode, file);
+}
+
+static int wdt_close(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ wdt_disable();
+ else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ wdt_ping();
+ }
+ expect_close = 0;
+ clear_bit(0, &wdt_is_open);
+ return 0;
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_disable(); /* Turn the WDT off */
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdt_write,
+ .unlocked_ioctl = wdt_ioctl,
+ .open = wdt_open,
+ .release = wdt_close,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static int __init wdt_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising.\n");
+
+ if (wdt_set_heartbeat(timeout)) {
+ wdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 1<=timeout<=255, using %d\n",
+ WATCHDOG_TIMEOUT);
+ }
+
+ if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ wdt_io);
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = w83697ug_init();
+ if (ret != 0)
+ goto unreg_regions;
+
+ ret = register_reboot_notifier(&wdt_notifier);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ goto unreg_regions;
+ }
+
+ ret = misc_register(&wdt_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto unreg_reboot;
+ }
+
+ printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+out:
+ return ret;
+unreg_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+unreg_regions:
+ release_region(wdt_io, 1);
+ goto out;
+}
+
+static void __exit wdt_exit(void)
+{
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(wdt_io, 1);
+}
+
+module_init(wdt_init);
+module_exit(wdt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>");
+MODULE_DESCRIPTION("w83697ug/uf WDT driver");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c
new file mode 100644
index 00000000..24587d20
--- /dev/null
+++ b/drivers/watchdog/w83877f_wdt.c
@@ -0,0 +1,416 @@
+/*
+ * W83877F Computer Watchdog Timer driver
+ *
+ * Based on acquirewdt.c by Alan Cox,
+ * and sbc60xxwdt.c by Jakob Oestergaard <jakob@unthought.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * The authors do NOT admit liability nor provide warranty for
+ * any of this software. This material is provided "AS-IS" in
+ * the hope that it may be useful for others.
+ *
+ * (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net>
+ *
+ * 4/19 - 2001 [Initial revision]
+ * 9/27 - 2001 Added spinlocking
+ * 4/12 - 2002 [rob@osinvestor.com] Eliminate extra comments
+ * Eliminate fop_read
+ * Eliminate extra spin_unlock
+ * Added KERN_* tags to printks
+ * add CONFIG_WATCHDOG_NOWAYOUT support
+ * fix possible wdt_is_open race
+ * changed watchdog_info to correctly reflect what
+ * the driver offers
+ * added WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS,
+ * WDIOC_SETTIMEOUT,
+ * WDIOC_GETTIMEOUT, and WDIOC_SETOPTIONS ioctls
+ * 09/8 - 2003 [wim@iguana.be] cleanup of trailing spaces
+ * added extra printk's for startup problems
+ * use module_param
+ * made timeout (the emulated heartbeat) a
+ * module_param
+ * made the keepalive ping an internal subroutine
+ *
+ * This WDT driver is different from most other Linux WDT
+ * drivers in that the driver will ping the watchdog by itself,
+ * because this particular WDT has a very short timeout (1.6
+ * seconds) and it would be insane to count on any userspace
+ * daemon always getting scheduled within that time frame.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#define OUR_NAME "w83877f_wdt"
+#define PFX OUR_NAME ": "
+
+#define ENABLE_W83877F_PORT 0x3F0
+#define ENABLE_W83877F 0x87
+#define DISABLE_W83877F 0xAA
+#define WDT_PING 0x443
+#define WDT_REGISTER 0x14
+#define WDT_ENABLE 0x9C
+#define WDT_DISABLE 0x8C
+
+/*
+ * The W83877F seems to be fixed at 1.6s timeout (at least on the
+ * EMACS PC-104 board I'm using). If we reset the watchdog every
+ * ~250ms we should be safe. */
+
+#define WDT_INTERVAL (HZ/4+1)
+
+/*
+ * We must not require too good response from the userspace daemon.
+ * Here we require the userspace daemon to send us a heartbeat
+ * char to /dev/watchdog every 30 seconds.
+ */
+
+#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */
+/* in seconds, will be multiplied by HZ to get seconds to wait for a ping */
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (1<=timeout<=3600, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static void wdt_timer_ping(unsigned long);
+static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0);
+static unsigned long next_heartbeat;
+static unsigned long wdt_is_open;
+static char wdt_expect_close;
+static DEFINE_SPINLOCK(wdt_spinlock);
+
+/*
+ * Whack the dog
+ */
+
+static void wdt_timer_ping(unsigned long data)
+{
+ /* If we got a heartbeat pulse within the WDT_US_INTERVAL
+ * we agree to ping the WDT
+ */
+ if (time_before(jiffies, next_heartbeat)) {
+ /* Ping the WDT */
+ spin_lock(&wdt_spinlock);
+
+ /* Ping the WDT by reading from WDT_PING */
+ inb_p(WDT_PING);
+
+ /* Re-set the timer interval */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+
+ spin_unlock(&wdt_spinlock);
+
+ } else
+ printk(KERN_WARNING PFX
+ "Heartbeat lost! Will not ping the watchdog\n");
+}
+
+/*
+ * Utility routines
+ */
+
+static void wdt_change(int writeval)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&wdt_spinlock, flags);
+
+ /* buy some time */
+ inb_p(WDT_PING);
+
+ /* make W83877F available */
+ outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT);
+ outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT);
+
+ /* enable watchdog */
+ outb_p(WDT_REGISTER, ENABLE_W83877F_PORT);
+ outb_p(writeval, ENABLE_W83877F_PORT+1);
+
+ /* lock the W8387FF away */
+ outb_p(DISABLE_W83877F, ENABLE_W83877F_PORT);
+
+ spin_unlock_irqrestore(&wdt_spinlock, flags);
+}
+
+static void wdt_startup(void)
+{
+ next_heartbeat = jiffies + (timeout * HZ);
+
+ /* Start the timer */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
+
+ wdt_change(WDT_ENABLE);
+
+ printk(KERN_INFO PFX "Watchdog timer is now enabled.\n");
+}
+
+static void wdt_turnoff(void)
+{
+ /* Stop the timer */
+ del_timer(&timer);
+
+ wdt_change(WDT_DISABLE);
+
+ printk(KERN_INFO PFX "Watchdog timer is now disabled...\n");
+}
+
+static void wdt_keepalive(void)
+{
+ /* user land ping */
+ next_heartbeat = jiffies + (timeout * HZ);
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t fop_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (count) {
+ if (!nowayout) {
+ size_t ofs;
+
+ /* note: just in case someone wrote the magic
+ character five months ago... */
+ wdt_expect_close = 0;
+
+ /* scan to see whether or not we got the
+ magic character */
+ for (ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if (c == 'V')
+ wdt_expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should restart timer */
+ wdt_keepalive();
+ }
+ return count;
+}
+
+static int fop_open(struct inode *inode, struct file *file)
+{
+ /* Just in case we're already talking to someone... */
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+
+ /* Good, fire up the show */
+ wdt_startup();
+ return nonseekable_open(inode, file);
+}
+
+static int fop_close(struct inode *inode, struct file *file)
+{
+ if (wdt_expect_close == 42)
+ wdt_turnoff();
+ else {
+ del_timer(&timer);
+ printk(KERN_CRIT PFX
+ "device file closed unexpectedly. Will not stop the WDT!\n");
+ }
+ clear_bit(0, &wdt_is_open);
+ wdt_expect_close = 0;
+ return 0;
+}
+
+static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
+ | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "W83877F",
+ };
+
+ 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:
+ {
+ int new_options, retval = -EINVAL;
+
+ if (get_user(new_options, p))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD) {
+ wdt_turnoff();
+ retval = 0;
+ }
+
+ if (new_options & WDIOS_ENABLECARD) {
+ wdt_startup();
+ retval = 0;
+ }
+
+ return retval;
+ }
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ {
+ int new_timeout;
+
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+
+ /* arbitrary upper limit */
+ if (new_timeout < 1 || new_timeout > 3600)
+ return -EINVAL;
+
+ timeout = new_timeout;
+ wdt_keepalive();
+ /* Fall through */
+ }
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = fop_write,
+ .open = fop_open,
+ .release = fop_close,
+ .unlocked_ioctl = fop_ioctl,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_turnoff();
+ return NOTIFY_DONE;
+}
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static void __exit w83877f_wdt_unload(void)
+{
+ wdt_turnoff();
+
+ /* Deregister */
+ misc_deregister(&wdt_miscdev);
+
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(WDT_PING, 1);
+ release_region(ENABLE_W83877F_PORT, 2);
+}
+
+static int __init w83877f_wdt_init(void)
+{
+ int rc = -EBUSY;
+
+ if (timeout < 1 || timeout > 3600) { /* arbitrary upper limit */
+ timeout = WATCHDOG_TIMEOUT;
+ printk(KERN_INFO PFX
+ "timeout value must be 1 <= x <= 3600, using %d\n",
+ timeout);
+ }
+
+ if (!request_region(ENABLE_W83877F_PORT, 2, "W83877F WDT")) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ ENABLE_W83877F_PORT);
+ rc = -EIO;
+ goto err_out;
+ }
+
+ if (!request_region(WDT_PING, 1, "W8387FF WDT")) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ WDT_PING);
+ rc = -EIO;
+ goto err_out_region1;
+ }
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out_region2;
+ }
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
+ goto err_out_reboot;
+ }
+
+ printk(KERN_INFO PFX
+ "WDT driver for W83877F initialised. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+ return 0;
+
+err_out_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+err_out_region2:
+ release_region(WDT_PING, 1);
+err_out_region1:
+ release_region(ENABLE_W83877F_PORT, 2);
+err_out:
+ return rc;
+}
+
+module_init(w83877f_wdt_init);
+module_exit(w83877f_wdt_unload);
+
+MODULE_AUTHOR("Scott and Bill Jennings");
+MODULE_DESCRIPTION("Driver for watchdog timer in w83877f chip");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c
new file mode 100644
index 00000000..6e6743d1
--- /dev/null
+++ b/drivers/watchdog/w83977f_wdt.c
@@ -0,0 +1,537 @@
+/*
+ * W83977F Watchdog Timer Driver for Winbond W83977F I/O Chip
+ *
+ * (c) Copyright 2005 Jose Goncalves <jose.goncalves@inov.pt>
+ *
+ * Based on w83877f_wdt.c by Scott Jennings,
+ * and wdt977.c by Woody Suwalski
+ *
+ * -----------------------
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+
+#define WATCHDOG_VERSION "1.00"
+#define WATCHDOG_NAME "W83977F WDT"
+#define PFX WATCHDOG_NAME ": "
+#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
+
+#define IO_INDEX_PORT 0x3F0
+#define IO_DATA_PORT (IO_INDEX_PORT+1)
+
+#define UNLOCK_DATA 0x87
+#define LOCK_DATA 0xAA
+#define DEVICE_REGISTER 0x07
+
+#define DEFAULT_TIMEOUT 45 /* default timeout in seconds */
+
+static int timeout = DEFAULT_TIMEOUT;
+static int timeoutW; /* timeout in watchdog counter units */
+static unsigned long timer_alive;
+static int testmode;
+static char expect_close;
+static DEFINE_SPINLOCK(spinlock);
+
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds (15..7635), default="
+ __MODULE_STRING(DEFAULT_TIMEOUT) ")");
+module_param(testmode, int, 0);
+MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Start the watchdog
+ */
+
+static int wdt_start(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+
+ /* Unlock the SuperIO chip */
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+
+ /*
+ * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4.
+ * F2 has the timeout in watchdog counter units.
+ * F3 is set to enable watchdog LED blink at timeout.
+ * F4 is used to just clear the TIMEOUT'ed state (bit 0).
+ */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ outb_p(0xF2, IO_INDEX_PORT);
+ outb_p(timeoutW, IO_DATA_PORT);
+ outb_p(0xF3, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ outb_p(0xF4, IO_INDEX_PORT);
+ outb_p(0x00, IO_DATA_PORT);
+
+ /* Set device Aux2 active */
+ outb_p(0x30, IO_INDEX_PORT);
+ outb_p(0x01, IO_DATA_PORT);
+
+ /*
+ * Select device Aux1 (dev=7) to set GP16 as the watchdog output
+ * (in reg E6) and GP13 as the watchdog LED output (in reg E3).
+ * Map GP16 at pin 119.
+ * In test mode watch the bit 0 on F4 to indicate "triggered" or
+ * check watchdog LED on SBC.
+ */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x07, IO_DATA_PORT);
+ if (!testmode) {
+ unsigned pin_map;
+
+ outb_p(0xE6, IO_INDEX_PORT);
+ outb_p(0x0A, IO_DATA_PORT);
+ outb_p(0x2C, IO_INDEX_PORT);
+ pin_map = inb_p(IO_DATA_PORT);
+ pin_map |= 0x10;
+ pin_map &= ~(0x20);
+ outb_p(0x2C, IO_INDEX_PORT);
+ outb_p(pin_map, IO_DATA_PORT);
+ }
+ outb_p(0xE3, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+
+ /* Set device Aux1 active */
+ outb_p(0x30, IO_INDEX_PORT);
+ outb_p(0x01, IO_DATA_PORT);
+
+ /* Lock the SuperIO chip */
+ outb_p(LOCK_DATA, IO_INDEX_PORT);
+
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ printk(KERN_INFO PFX "activated.\n");
+
+ return 0;
+}
+
+/*
+ * Stop the watchdog
+ */
+
+static int wdt_stop(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+
+ /* Unlock the SuperIO chip */
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+
+ /*
+ * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4.
+ * F2 is reset to its default value (watchdog timer disabled).
+ * F3 is reset to its default state.
+ * F4 clears the TIMEOUT'ed state (bit 0) - back to default.
+ */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ outb_p(0xF2, IO_INDEX_PORT);
+ outb_p(0xFF, IO_DATA_PORT);
+ outb_p(0xF3, IO_INDEX_PORT);
+ outb_p(0x00, IO_DATA_PORT);
+ outb_p(0xF4, IO_INDEX_PORT);
+ outb_p(0x00, IO_DATA_PORT);
+ outb_p(0xF2, IO_INDEX_PORT);
+ outb_p(0x00, IO_DATA_PORT);
+
+ /*
+ * Select device Aux1 (dev=7) to set GP16 (in reg E6) and
+ * Gp13 (in reg E3) as inputs.
+ */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x07, IO_DATA_PORT);
+ if (!testmode) {
+ outb_p(0xE6, IO_INDEX_PORT);
+ outb_p(0x01, IO_DATA_PORT);
+ }
+ outb_p(0xE3, IO_INDEX_PORT);
+ outb_p(0x01, IO_DATA_PORT);
+
+ /* Lock the SuperIO chip */
+ outb_p(LOCK_DATA, IO_INDEX_PORT);
+
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ printk(KERN_INFO PFX "shutdown.\n");
+
+ return 0;
+}
+
+/*
+ * Send a keepalive ping to the watchdog
+ * This is done by simply re-writing the timeout to reg. 0xF2
+ */
+
+static int wdt_keepalive(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+
+ /* Unlock the SuperIO chip */
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+
+ /* Select device Aux2 (device=8) to kick watchdog reg F2 */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ outb_p(0xF2, IO_INDEX_PORT);
+ outb_p(timeoutW, IO_DATA_PORT);
+
+ /* Lock the SuperIO chip */
+ outb_p(LOCK_DATA, IO_INDEX_PORT);
+
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * Set the watchdog timeout value
+ */
+
+static int wdt_set_timeout(int t)
+{
+ int tmrval;
+
+ /*
+ * Convert seconds to watchdog counter time units, rounding up.
+ * On PCM-5335 watchdog units are 30 seconds/step with 15 sec startup
+ * value. This information is supplied in the PCM-5335 manual and was
+ * checked by me on a real board. This is a bit strange because W83977f
+ * datasheet says counter unit is in minutes!
+ */
+ if (t < 15)
+ return -EINVAL;
+
+ tmrval = ((t + 15) + 29) / 30;
+
+ if (tmrval > 255)
+ return -EINVAL;
+
+ /*
+ * timeout is the timeout in seconds,
+ * timeoutW is the timeout in watchdog counter units.
+ */
+ timeoutW = tmrval;
+ timeout = (timeoutW * 30) - 15;
+ return 0;
+}
+
+/*
+ * Get the watchdog status
+ */
+
+static int wdt_get_status(int *status)
+{
+ int new_status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+
+ /* Unlock the SuperIO chip */
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+
+ /* Select device Aux2 (device=8) to read watchdog reg F4 */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ outb_p(0xF4, IO_INDEX_PORT);
+ new_status = inb_p(IO_DATA_PORT);
+
+ /* Lock the SuperIO chip */
+ outb_p(LOCK_DATA, IO_INDEX_PORT);
+
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ *status = 0;
+ if (new_status & 1)
+ *status |= WDIOF_CARDRESET;
+
+ return 0;
+}
+
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ /* If the watchdog is alive we don't need to start it again */
+ if (test_and_set_bit(0, &timer_alive))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int wdt_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ * Lock it in if it's a module and we set nowayout
+ */
+ if (expect_close == 42) {
+ wdt_stop();
+ clear_bit(0, &timer_alive);
+ } else {
+ wdt_keepalive();
+ printk(KERN_CRIT PFX
+ "unexpected close, not stopping watchdog!\n");
+ }
+ expect_close = 0;
+ return 0;
+}
+
+/*
+ * wdt_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write (unused as data does not matter here
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (count) {
+ if (!nowayout) {
+ size_t ofs;
+
+ /* note: just in case someone wrote the
+ magic character long ago */
+ expect_close = 0;
+
+ /* scan to see whether or not we got the
+ magic character */
+ for (ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should restart timer */
+ wdt_keepalive();
+ }
+ return count;
+}
+
+/*
+ * wdt_ioctl:
+ * @inode: inode of the device
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
+ */
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = WATCHDOG_NAME,
+};
+
+static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int status;
+ int new_options, retval = -EINVAL;
+ int new_timeout;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ wdt_get_status(&status);
+ return put_user(status, uarg.i);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, uarg.i))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD) {
+ wdt_stop();
+ retval = 0;
+ }
+
+ if (new_options & WDIOS_ENABLECARD) {
+ wdt_start();
+ retval = 0;
+ }
+
+ return retval;
+
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+
+ if (wdt_set_timeout(new_timeout))
+ return -EINVAL;
+
+ wdt_keepalive();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, uarg.i);
+
+ default:
+ return -ENOTTY;
+
+ }
+}
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_stop();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdt_write,
+ .unlocked_ioctl = wdt_ioctl,
+ .open = wdt_open,
+ .release = wdt_release,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static int __init w83977f_wdt_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO PFX DRIVER_VERSION);
+
+ /*
+ * Check that the timeout value is within it's range;
+ * if not reset to the default
+ */
+ if (wdt_set_timeout(timeout)) {
+ wdt_set_timeout(DEFAULT_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 15 <= timeout <= 7635, using %d\n",
+ DEFAULT_TIMEOUT);
+ }
+
+ if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ IO_INDEX_PORT);
+ rc = -EIO;
+ goto err_out;
+ }
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out_region;
+ }
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
+ goto err_out_reboot;
+ }
+
+ printk(KERN_INFO PFX
+ "initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
+ timeout, nowayout, testmode);
+
+ return 0;
+
+err_out_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+err_out_region:
+ release_region(IO_INDEX_PORT, 2);
+err_out:
+ return rc;
+}
+
+static void __exit w83977f_wdt_exit(void)
+{
+ wdt_stop();
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(IO_INDEX_PORT, 2);
+}
+
+module_init(w83977f_wdt_init);
+module_exit(w83977f_wdt_exit);
+
+MODULE_AUTHOR("Jose Goncalves <jose.goncalves@inov.pt>");
+MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c
new file mode 100644
index 00000000..42e940c2
--- /dev/null
+++ b/drivers/watchdog/wafer5823wdt.c
@@ -0,0 +1,333 @@
+/*
+ * ICP Wafer 5823 Single Board Computer WDT driver
+ * http://www.icpamerica.com/wafer_5823.php
+ * May also work on other similar models
+ *
+ * (c) Copyright 2002 Justin Cormack <justin@street-vision.com>
+ *
+ * Release 0.02
+ *
+ * Based on advantechwdt.c which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#define WATCHDOG_NAME "Wafer 5823 WDT"
+#define PFX WATCHDOG_NAME ": "
+#define WD_TIMO 60 /* 60 sec default timeout */
+
+static unsigned long wafwdt_is_open;
+static char expect_close;
+static DEFINE_SPINLOCK(wafwdt_lock);
+
+/*
+ * You must set these - there is no sane way to probe for this board.
+ *
+ * To enable, write the timeout value in seconds (1 to 255) to I/O
+ * port WDT_START, then read the port to start the watchdog. To pat
+ * the dog, read port WDT_STOP to stop the timer, then read WDT_START
+ * to restart it again.
+ */
+
+static int wdt_stop = 0x843;
+static int wdt_start = 0x443;
+
+static int timeout = WD_TIMO; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
+ __MODULE_STRING(WD_TIMO) ".");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static void wafwdt_ping(void)
+{
+ /* pat watchdog */
+ spin_lock(&wafwdt_lock);
+ inb_p(wdt_stop);
+ inb_p(wdt_start);
+ spin_unlock(&wafwdt_lock);
+}
+
+static void wafwdt_start(void)
+{
+ /* start up watchdog */
+ outb_p(timeout, wdt_start);
+ inb_p(wdt_start);
+}
+
+static void wafwdt_stop(void)
+{
+ /* stop watchdog */
+ inb_p(wdt_stop);
+}
+
+static ssize_t wafwdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ /* scan to see whether or not we got the magic
+ character */
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ /* Well, anyhow someone wrote to us, we should
+ return that favour */
+ wafwdt_ping();
+ }
+ return count;
+}
+
+static long wafwdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_timeout;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "Wafer 5823 WDT",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ wafwdt_start();
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ wafwdt_stop();
+ retval = 0;
+ }
+
+ return retval;
+ }
+
+ case WDIOC_KEEPALIVE:
+ wafwdt_ping();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if ((new_timeout < 1) || (new_timeout > 255))
+ return -EINVAL;
+ timeout = new_timeout;
+ wafwdt_stop();
+ wafwdt_start();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int wafwdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &wafwdt_is_open))
+ return -EBUSY;
+
+ /*
+ * Activate
+ */
+ wafwdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int wafwdt_close(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ wafwdt_stop();
+ else {
+ printk(KERN_CRIT PFX
+ "WDT device closed unexpectedly. WDT will not stop!\n");
+ wafwdt_ping();
+ }
+ clear_bit(0, &wafwdt_is_open);
+ expect_close = 0;
+ return 0;
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wafwdt_stop();
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations wafwdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wafwdt_write,
+ .unlocked_ioctl = wafwdt_ioctl,
+ .open = wafwdt_open,
+ .release = wafwdt_close,
+};
+
+static struct miscdevice wafwdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wafwdt_fops,
+};
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wafwdt_notifier = {
+ .notifier_call = wafwdt_notify_sys,
+};
+
+static int __init wafwdt_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO
+ "WDT driver for Wafer 5823 single board computer initialising.\n");
+
+ if (timeout < 1 || timeout > 255) {
+ timeout = WD_TIMO;
+ printk(KERN_INFO PFX
+ "timeout value must be 1 <= x <= 255, using %d\n",
+ timeout);
+ }
+
+ if (wdt_stop != wdt_start) {
+ if (!request_region(wdt_stop, 1, "Wafer 5823 WDT")) {
+ printk(KERN_ERR PFX
+ "I/O address 0x%04x already in use\n",
+ wdt_stop);
+ ret = -EIO;
+ goto error;
+ }
+ }
+
+ if (!request_region(wdt_start, 1, "Wafer 5823 WDT")) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ wdt_start);
+ ret = -EIO;
+ goto error2;
+ }
+
+ ret = register_reboot_notifier(&wafwdt_notifier);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ goto error3;
+ }
+
+ ret = misc_register(&wafwdt_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto error4;
+ }
+
+ printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+ return ret;
+error4:
+ unregister_reboot_notifier(&wafwdt_notifier);
+error3:
+ release_region(wdt_start, 1);
+error2:
+ if (wdt_stop != wdt_start)
+ release_region(wdt_stop, 1);
+error:
+ return ret;
+}
+
+static void __exit wafwdt_exit(void)
+{
+ misc_deregister(&wafwdt_miscdev);
+ unregister_reboot_notifier(&wafwdt_notifier);
+ if (wdt_stop != wdt_start)
+ release_region(wdt_stop, 1);
+ release_region(wdt_start, 1);
+}
+
+module_init(wafwdt_init);
+module_exit(wafwdt_exit);
+
+MODULE_AUTHOR("Justin Cormack");
+MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+/* end of wafer5823wdt.c */
diff --git a/drivers/watchdog/wd501p.h b/drivers/watchdog/wd501p.h
new file mode 100644
index 00000000..0e3a497d
--- /dev/null
+++ b/drivers/watchdog/wd501p.h
@@ -0,0 +1,51 @@
+/*
+ * Industrial Computer Source WDT500/501 driver
+ *
+ * (c) Copyright 1995 CymruNET Ltd
+ * Innovation Centre
+ * Singleton Park
+ * Swansea
+ * Wales
+ * UK
+ * SA2 8PP
+ *
+ * http://www.cymru.net
+ *
+ * This driver is provided under the GNU General Public License,
+ * incorporated herein by reference. The driver is provided without
+ * warranty or support.
+ *
+ * Release 0.04.
+ *
+ */
+
+
+#define WDT_COUNT0 (io+0)
+#define WDT_COUNT1 (io+1)
+#define WDT_COUNT2 (io+2)
+#define WDT_CR (io+3)
+#define WDT_SR (io+4) /* Start buzzer on PCI write */
+#define WDT_RT (io+5) /* Stop buzzer on PCI write */
+#define WDT_BUZZER (io+6) /* PCI only: rd=disable, wr=enable */
+#define WDT_DC (io+7)
+
+/* The following are only on the PCI card, they're outside of I/O space on
+ * the ISA card: */
+#define WDT_CLOCK (io+12) /* COUNT2: rd=16.67MHz, wr=2.0833MHz */
+/* inverted opto isolated reset output: */
+#define WDT_OPTONOTRST (io+13) /* wr=enable, rd=disable */
+/* opto isolated reset output: */
+#define WDT_OPTORST (io+14) /* wr=enable, rd=disable */
+/* programmable outputs: */
+#define WDT_PROGOUT (io+15) /* wr=enable, rd=disable */
+
+ /* FAN 501 500 */
+#define WDC_SR_WCCR 1 /* Active low */ /* X X X */
+#define WDC_SR_TGOOD 2 /* X X - */
+#define WDC_SR_ISOI0 4 /* X X X */
+#define WDC_SR_ISII1 8 /* X X X */
+#define WDC_SR_FANGOOD 16 /* X - - */
+#define WDC_SR_PSUOVER 32 /* Active low */ /* X X - */
+#define WDC_SR_PSUUNDR 64 /* Active low */ /* X X - */
+#define WDC_SR_IRQ 128 /* Active low */ /* X X X */
+
diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c
new file mode 100644
index 00000000..94ec22b9
--- /dev/null
+++ b/drivers/watchdog/wdrtas.c
@@ -0,0 +1,676 @@
+/*
+ * FIXME: add wdrtas_get_status and wdrtas_get_boot_status as soon as
+ * RTAS calls are available
+ */
+
+/*
+ * RTAS watchdog driver
+ *
+ * (C) Copyright IBM Corp. 2005
+ * device driver to exploit watchdog RTAS functions
+ *
+ * Authors : Utz Bacher <utz.bacher@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+
+#include <asm/rtas.h>
+
+#define WDRTAS_MAGIC_CHAR 42
+#define WDRTAS_SUPPORTED_MASK (WDIOF_SETTIMEOUT | \
+ WDIOF_MAGICCLOSE)
+
+MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
+MODULE_DESCRIPTION("RTAS watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS_MISCDEV(TEMP_MINOR);
+
+static int wdrtas_nowayout = WATCHDOG_NOWAYOUT;
+static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0);
+static char wdrtas_expect_close;
+
+static int wdrtas_interval;
+
+#define WDRTAS_THERMAL_SENSOR 3
+static int wdrtas_token_get_sensor_state;
+#define WDRTAS_SURVEILLANCE_IND 9000
+static int wdrtas_token_set_indicator;
+#define WDRTAS_SP_SPI 28
+static int wdrtas_token_get_sp;
+static int wdrtas_token_event_scan;
+
+#define WDRTAS_DEFAULT_INTERVAL 300
+
+#define WDRTAS_LOGBUFFER_LEN 128
+static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN];
+
+
+/*** watchdog access functions */
+
+/**
+ * wdrtas_set_interval - sets the watchdog interval
+ * @interval: new interval
+ *
+ * returns 0 on success, <0 on failures
+ *
+ * wdrtas_set_interval sets the watchdog keepalive interval by calling the
+ * RTAS function set-indicator (surveillance). The unit of interval is
+ * seconds.
+ */
+
+static int wdrtas_set_interval(int interval)
+{
+ long result;
+ static int print_msg = 10;
+
+ /* rtas uses minutes */
+ interval = (interval + 59) / 60;
+
+ result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL,
+ WDRTAS_SURVEILLANCE_IND, 0, interval);
+ if (result < 0 && print_msg) {
+ printk(KERN_ERR "wdrtas: setting the watchdog to %i "
+ "timeout failed: %li\n", interval, result);
+ print_msg--;
+ }
+
+ return result;
+}
+
+#define WDRTAS_SP_SPI_LEN 4
+
+/**
+ * wdrtas_get_interval - returns the current watchdog interval
+ * @fallback_value: value (in seconds) to use, if the RTAS call fails
+ *
+ * returns the interval
+ *
+ * wdrtas_get_interval returns the current watchdog keepalive interval
+ * as reported by the RTAS function ibm,get-system-parameter. The unit
+ * of the return value is seconds.
+ */
+static int wdrtas_get_interval(int fallback_value)
+{
+ long result;
+ char value[WDRTAS_SP_SPI_LEN];
+
+ spin_lock(&rtas_data_buf_lock);
+ memset(rtas_data_buf, 0, WDRTAS_SP_SPI_LEN);
+ result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL,
+ WDRTAS_SP_SPI, __pa(rtas_data_buf),
+ WDRTAS_SP_SPI_LEN);
+
+ memcpy(value, rtas_data_buf, WDRTAS_SP_SPI_LEN);
+ spin_unlock(&rtas_data_buf_lock);
+
+ if (value[0] != 0 || value[1] != 2 || value[3] != 0 || result < 0) {
+ printk(KERN_WARNING "wdrtas: could not get sp_spi watchdog "
+ "timeout (%li). Continuing\n", result);
+ return fallback_value;
+ }
+
+ /* rtas uses minutes */
+ return ((int)value[2]) * 60;
+}
+
+/**
+ * wdrtas_timer_start - starts watchdog
+ *
+ * wdrtas_timer_start starts the watchdog by calling the RTAS function
+ * set-interval (surveillance)
+ */
+static void wdrtas_timer_start(void)
+{
+ wdrtas_set_interval(wdrtas_interval);
+}
+
+/**
+ * wdrtas_timer_stop - stops watchdog
+ *
+ * wdrtas_timer_stop stops the watchdog timer by calling the RTAS function
+ * set-interval (surveillance)
+ */
+static void wdrtas_timer_stop(void)
+{
+ wdrtas_set_interval(0);
+}
+
+/**
+ * wdrtas_log_scanned_event - logs an event we received during keepalive
+ *
+ * wdrtas_log_scanned_event prints a message to the log buffer dumping
+ * the results of the last event-scan call
+ */
+static void wdrtas_log_scanned_event(void)
+{
+ int i;
+
+ for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16)
+ printk(KERN_INFO "wdrtas: dumping event (line %i/%i), data = "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
+ wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
+ wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
+ wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
+ wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
+ wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
+ wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
+ wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
+ wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
+}
+
+/**
+ * wdrtas_timer_keepalive - resets watchdog timer to keep system alive
+ *
+ * wdrtas_timer_keepalive restarts the watchdog timer by calling the
+ * RTAS function event-scan and repeats these calls as long as there are
+ * events available. All events will be dumped.
+ */
+static void wdrtas_timer_keepalive(void)
+{
+ long result;
+
+ do {
+ result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL,
+ RTAS_EVENT_SCAN_ALL_EVENTS, 0,
+ (void *)__pa(wdrtas_logbuffer),
+ WDRTAS_LOGBUFFER_LEN);
+ if (result < 0)
+ printk(KERN_ERR "wdrtas: event-scan failed: %li\n",
+ result);
+ if (result == 0)
+ wdrtas_log_scanned_event();
+ } while (result == 0);
+}
+
+/**
+ * wdrtas_get_temperature - returns current temperature
+ *
+ * returns temperature or <0 on failures
+ *
+ * wdrtas_get_temperature returns the current temperature in Fahrenheit. It
+ * uses the RTAS call get-sensor-state, token 3 to do so
+ */
+static int wdrtas_get_temperature(void)
+{
+ int result;
+ int temperature = 0;
+
+ result = rtas_get_sensor(WDRTAS_THERMAL_SENSOR, 0, &temperature);
+
+ if (result < 0)
+ printk(KERN_WARNING "wdrtas: reading the thermal sensor "
+ "failed: %i\n", result);
+ else
+ temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */
+
+ return temperature;
+}
+
+/**
+ * wdrtas_get_status - returns the status of the watchdog
+ *
+ * returns a bitmask of defines WDIOF_... as defined in
+ * include/linux/watchdog.h
+ */
+static int wdrtas_get_status(void)
+{
+ return 0; /* TODO */
+}
+
+/**
+ * wdrtas_get_boot_status - returns the reason for the last boot
+ *
+ * returns a bitmask of defines WDIOF_... as defined in
+ * include/linux/watchdog.h, indicating why the watchdog rebooted the system
+ */
+static int wdrtas_get_boot_status(void)
+{
+ return 0; /* TODO */
+}
+
+/*** watchdog API and operations stuff */
+
+/* wdrtas_write - called when watchdog device is written to
+ * @file: file structure
+ * @buf: user buffer with data
+ * @len: amount to data written
+ * @ppos: position in file
+ *
+ * returns the number of successfully processed characters, which is always
+ * the number of bytes passed to this function
+ *
+ * wdrtas_write processes all the data given to it and looks for the magic
+ * character 'V'. This character allows the watchdog device to be closed
+ * properly.
+ */
+static ssize_t wdrtas_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ int i;
+ char c;
+
+ if (!len)
+ goto out;
+
+ if (!wdrtas_nowayout) {
+ wdrtas_expect_close = 0;
+ /* look for 'V' */
+ for (i = 0; i < len; i++) {
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ /* allow to close device */
+ if (c == 'V')
+ wdrtas_expect_close = WDRTAS_MAGIC_CHAR;
+ }
+ }
+
+ wdrtas_timer_keepalive();
+
+out:
+ return len;
+}
+
+/**
+ * wdrtas_ioctl - ioctl function for the watchdog device
+ * @file: file structure
+ * @cmd: command for ioctl
+ * @arg: argument pointer
+ *
+ * returns 0 on success, <0 on failure
+ *
+ * wdrtas_ioctl implements the watchdog API ioctls
+ */
+
+static long wdrtas_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int __user *argp = (void __user *)arg;
+ int i;
+ static const struct watchdog_info wdinfo = {
+ .options = WDRTAS_SUPPORTED_MASK,
+ .firmware_version = 0,
+ .identity = "wdrtas",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &wdinfo, sizeof(wdinfo)))
+ return -EFAULT;
+ return 0;
+
+ case WDIOC_GETSTATUS:
+ i = wdrtas_get_status();
+ return put_user(i, argp);
+
+ case WDIOC_GETBOOTSTATUS:
+ i = wdrtas_get_boot_status();
+ return put_user(i, argp);
+
+ case WDIOC_GETTEMP:
+ if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE)
+ return -EOPNOTSUPP;
+
+ i = wdrtas_get_temperature();
+ return put_user(i, argp);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(i, argp))
+ return -EFAULT;
+ if (i & WDIOS_DISABLECARD)
+ wdrtas_timer_stop();
+ if (i & WDIOS_ENABLECARD) {
+ wdrtas_timer_keepalive();
+ wdrtas_timer_start();
+ }
+ /* not implemented. Done by H8
+ if (i & WDIOS_TEMPPANIC) {
+ } */
+ return 0;
+
+ case WDIOC_KEEPALIVE:
+ wdrtas_timer_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(i, argp))
+ return -EFAULT;
+
+ if (wdrtas_set_interval(i))
+ return -EINVAL;
+
+ wdrtas_timer_keepalive();
+
+ if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
+ wdrtas_interval = i;
+ else
+ wdrtas_interval = wdrtas_get_interval(i);
+ /* fallthrough */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdrtas_interval, argp);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+/**
+ * wdrtas_open - open function of watchdog device
+ * @inode: inode structure
+ * @file: file structure
+ *
+ * returns 0 on success, -EBUSY if the file has been opened already, <0 on
+ * other failures
+ *
+ * function called when watchdog device is opened
+ */
+static int wdrtas_open(struct inode *inode, struct file *file)
+{
+ /* only open once */
+ if (atomic_inc_return(&wdrtas_miscdev_open) > 1) {
+ atomic_dec(&wdrtas_miscdev_open);
+ return -EBUSY;
+ }
+
+ wdrtas_timer_start();
+ wdrtas_timer_keepalive();
+
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * wdrtas_close - close function of watchdog device
+ * @inode: inode structure
+ * @file: file structure
+ *
+ * returns 0 on success
+ *
+ * close function. Always succeeds
+ */
+static int wdrtas_close(struct inode *inode, struct file *file)
+{
+ /* only stop watchdog, if this was announced using 'V' before */
+ if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR)
+ wdrtas_timer_stop();
+ else {
+ printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog "
+ "not stopped.\n");
+ wdrtas_timer_keepalive();
+ }
+
+ wdrtas_expect_close = 0;
+ atomic_dec(&wdrtas_miscdev_open);
+ return 0;
+}
+
+/**
+ * wdrtas_temp_read - gives back the temperature in fahrenheit
+ * @file: file structure
+ * @buf: user buffer
+ * @count: number of bytes to be read
+ * @ppos: position in file
+ *
+ * returns always 1 or -EFAULT in case of user space copy failures, <0 on
+ * other failures
+ *
+ * wdrtas_temp_read gives the temperature to the users by copying this
+ * value as one byte into the user space buffer. The unit is Fahrenheit...
+ */
+static ssize_t wdrtas_temp_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int temperature = 0;
+
+ temperature = wdrtas_get_temperature();
+ if (temperature < 0)
+ return temperature;
+
+ if (copy_to_user(buf, &temperature, 1))
+ return -EFAULT;
+
+ return 1;
+}
+
+/**
+ * wdrtas_temp_open - open function of temperature device
+ * @inode: inode structure
+ * @file: file structure
+ *
+ * returns 0 on success, <0 on failure
+ *
+ * function called when temperature device is opened
+ */
+static int wdrtas_temp_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * wdrtas_temp_close - close function of temperature device
+ * @inode: inode structure
+ * @file: file structure
+ *
+ * returns 0 on success
+ *
+ * close function. Always succeeds
+ */
+static int wdrtas_temp_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/**
+ * wdrtas_reboot - reboot notifier function
+ * @nb: notifier block structure
+ * @code: reboot code
+ * @ptr: unused
+ *
+ * returns NOTIFY_DONE
+ *
+ * wdrtas_reboot stops the watchdog in case of a reboot
+ */
+static int wdrtas_reboot(struct notifier_block *this,
+ unsigned long code, void *ptr)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdrtas_timer_stop();
+
+ return NOTIFY_DONE;
+}
+
+/*** initialization stuff */
+
+static const struct file_operations wdrtas_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdrtas_write,
+ .unlocked_ioctl = wdrtas_ioctl,
+ .open = wdrtas_open,
+ .release = wdrtas_close,
+};
+
+static struct miscdevice wdrtas_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdrtas_fops,
+};
+
+static const struct file_operations wdrtas_temp_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = wdrtas_temp_read,
+ .open = wdrtas_temp_open,
+ .release = wdrtas_temp_close,
+};
+
+static struct miscdevice wdrtas_tempdev = {
+ .minor = TEMP_MINOR,
+ .name = "temperature",
+ .fops = &wdrtas_temp_fops,
+};
+
+static struct notifier_block wdrtas_notifier = {
+ .notifier_call = wdrtas_reboot,
+};
+
+/**
+ * wdrtas_get_tokens - reads in RTAS tokens
+ *
+ * returns 0 on success, <0 on failure
+ *
+ * wdrtas_get_tokens reads in the tokens for the RTAS calls used in
+ * this watchdog driver. It tolerates, if "get-sensor-state" and
+ * "ibm,get-system-parameter" are not available.
+ */
+static int wdrtas_get_tokens(void)
+{
+ wdrtas_token_get_sensor_state = rtas_token("get-sensor-state");
+ if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) {
+ printk(KERN_WARNING "wdrtas: couldn't get token for "
+ "get-sensor-state. Trying to continue without "
+ "temperature support.\n");
+ }
+
+ wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter");
+ if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) {
+ printk(KERN_WARNING "wdrtas: couldn't get token for "
+ "ibm,get-system-parameter. Trying to continue with "
+ "a default timeout value of %i seconds.\n",
+ WDRTAS_DEFAULT_INTERVAL);
+ }
+
+ wdrtas_token_set_indicator = rtas_token("set-indicator");
+ if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) {
+ printk(KERN_ERR "wdrtas: couldn't get token for "
+ "set-indicator. Terminating watchdog code.\n");
+ return -EIO;
+ }
+
+ wdrtas_token_event_scan = rtas_token("event-scan");
+ if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) {
+ printk(KERN_ERR "wdrtas: couldn't get token for event-scan. "
+ "Terminating watchdog code.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * wdrtas_unregister_devs - unregisters the misc dev handlers
+ *
+ * wdrtas_register_devs unregisters the watchdog and temperature watchdog
+ * misc devs
+ */
+static void wdrtas_unregister_devs(void)
+{
+ misc_deregister(&wdrtas_miscdev);
+ if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE)
+ misc_deregister(&wdrtas_tempdev);
+}
+
+/**
+ * wdrtas_register_devs - registers the misc dev handlers
+ *
+ * returns 0 on success, <0 on failure
+ *
+ * wdrtas_register_devs registers the watchdog and temperature watchdog
+ * misc devs
+ */
+static int wdrtas_register_devs(void)
+{
+ int result;
+
+ result = misc_register(&wdrtas_miscdev);
+ if (result) {
+ printk(KERN_ERR "wdrtas: couldn't register watchdog misc "
+ "device. Terminating watchdog code.\n");
+ return result;
+ }
+
+ if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) {
+ result = misc_register(&wdrtas_tempdev);
+ if (result) {
+ printk(KERN_WARNING "wdrtas: couldn't register "
+ "watchdog temperature misc device. Continuing "
+ "without temperature support.\n");
+ wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * wdrtas_init - init function of the watchdog driver
+ *
+ * returns 0 on success, <0 on failure
+ *
+ * registers the file handlers and the reboot notifier
+ */
+static int __init wdrtas_init(void)
+{
+ if (wdrtas_get_tokens())
+ return -ENODEV;
+
+ if (wdrtas_register_devs())
+ return -ENODEV;
+
+ if (register_reboot_notifier(&wdrtas_notifier)) {
+ printk(KERN_ERR "wdrtas: could not register reboot notifier. "
+ "Terminating watchdog code.\n");
+ wdrtas_unregister_devs();
+ return -ENODEV;
+ }
+
+ if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
+ wdrtas_interval = WDRTAS_DEFAULT_INTERVAL;
+ else
+ wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL);
+
+ return 0;
+}
+
+/**
+ * wdrtas_exit - exit function of the watchdog driver
+ *
+ * unregisters the file handlers and the reboot notifier
+ */
+static void __exit wdrtas_exit(void)
+{
+ if (!wdrtas_nowayout)
+ wdrtas_timer_stop();
+
+ wdrtas_unregister_devs();
+
+ unregister_reboot_notifier(&wdrtas_notifier);
+}
+
+module_init(wdrtas_init);
+module_exit(wdrtas_exit);
diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c
new file mode 100644
index 00000000..bb03e151
--- /dev/null
+++ b/drivers/watchdog/wdt.c
@@ -0,0 +1,673 @@
+/*
+ * Industrial Computer Source WDT501 driver
+ *
+ * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * Release 0.10.
+ *
+ * Fixes
+ * Dave Gregorich : Modularisation and minor bugs
+ * Alan Cox : Added the watchdog ioctl() stuff
+ * Alan Cox : Fixed the reboot problem (as noted by
+ * Matt Crocker).
+ * Alan Cox : Added wdt= boot option
+ * Alan Cox : Cleaned up copy/user stuff
+ * Tim Hockin : Added insmod parameters, comment
+ * cleanup, parameterized timeout
+ * Tigran Aivazian : Restructured wdt_init() to handle
+ * failures
+ * Joel Becker : Added WDIOC_GET/SETTIMEOUT
+ * Matt Domsch : Added nowayout module option
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+#include "wd501p.h"
+
+static unsigned long wdt_is_open;
+static char expect_close;
+
+/*
+ * Module parameters
+ */
+
+#define WD_TIMO 60 /* Default heartbeat = 60 seconds */
+
+static int heartbeat = WD_TIMO;
+static int wd_heartbeat;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default="
+ __MODULE_STRING(WD_TIMO) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/* You must set these - there is no sane way to probe for this board. */
+static int io = 0x240;
+static int irq = 11;
+
+static DEFINE_SPINLOCK(wdt_lock);
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "WDT io port (default=0x240)");
+module_param(irq, int, 0);
+MODULE_PARM_DESC(irq, "WDT irq (default=11)");
+
+/* Support for the Fan Tachometer on the WDT501-P */
+static int tachometer;
+module_param(tachometer, int, 0);
+MODULE_PARM_DESC(tachometer,
+ "WDT501-P Fan Tachometer support (0=disable, default=0)");
+
+static int type = 500;
+module_param(type, int, 0);
+MODULE_PARM_DESC(type,
+ "WDT501-P Card type (500 or 501, default=500)");
+
+/*
+ * Programming support
+ */
+
+static void wdt_ctr_mode(int ctr, int mode)
+{
+ ctr <<= 6;
+ ctr |= 0x30;
+ ctr |= (mode << 1);
+ outb_p(ctr, WDT_CR);
+}
+
+static void wdt_ctr_load(int ctr, int val)
+{
+ outb_p(val&0xFF, WDT_COUNT0+ctr);
+ outb_p(val>>8, WDT_COUNT0+ctr);
+}
+
+/**
+ * wdt_start:
+ *
+ * Start the watchdog driver.
+ */
+
+static int wdt_start(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&wdt_lock, flags);
+ inb_p(WDT_DC); /* Disable watchdog */
+ wdt_ctr_mode(0, 3); /* Program CTR0 for Mode 3:
+ Square Wave Generator */
+ wdt_ctr_mode(1, 2); /* Program CTR1 for Mode 2:
+ Rate Generator */
+ wdt_ctr_mode(2, 0); /* Program CTR2 for Mode 0:
+ Pulse on Terminal Count */
+ wdt_ctr_load(0, 8948); /* Count at 100Hz */
+ wdt_ctr_load(1, wd_heartbeat); /* Heartbeat */
+ wdt_ctr_load(2, 65535); /* Length of reset pulse */
+ outb_p(0, WDT_DC); /* Enable watchdog */
+ spin_unlock_irqrestore(&wdt_lock, flags);
+ return 0;
+}
+
+/**
+ * wdt_stop:
+ *
+ * Stop the watchdog driver.
+ */
+
+static int wdt_stop(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&wdt_lock, flags);
+ /* Turn the card off */
+ inb_p(WDT_DC); /* Disable watchdog */
+ wdt_ctr_load(2, 0); /* 0 length reset pulses now */
+ spin_unlock_irqrestore(&wdt_lock, flags);
+ return 0;
+}
+
+/**
+ * wdt_ping:
+ *
+ * Reload counter one with the watchdog heartbeat. We don't bother
+ * reloading the cascade counter.
+ */
+
+static void wdt_ping(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&wdt_lock, flags);
+ /* Write a watchdog value */
+ inb_p(WDT_DC); /* Disable watchdog */
+ wdt_ctr_mode(1, 2); /* Re-Program CTR1 for Mode 2:
+ Rate Generator */
+ wdt_ctr_load(1, wd_heartbeat); /* Heartbeat */
+ outb_p(0, WDT_DC); /* Enable watchdog */
+ spin_unlock_irqrestore(&wdt_lock, flags);
+}
+
+/**
+ * wdt_set_heartbeat:
+ * @t: the new heartbeat value that needs to be set.
+ *
+ * Set a new heartbeat value for the watchdog device. If the heartbeat
+ * value is incorrect we keep the old value and return -EINVAL. If
+ * successful we return 0.
+ */
+
+static int wdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > 65535)
+ return -EINVAL;
+
+ heartbeat = t;
+ wd_heartbeat = t * 100;
+ return 0;
+}
+
+/**
+ * wdt_get_status:
+ *
+ * Extract the status information from a WDT watchdog device. There are
+ * several board variants so we have to know which bits are valid. Some
+ * bits default to one and some to zero in order to be maximally painful.
+ *
+ * we then map the bits onto the status ioctl flags.
+ */
+
+static int wdt_get_status(void)
+{
+ unsigned char new_status;
+ int status = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt_lock, flags);
+ new_status = inb_p(WDT_SR);
+ spin_unlock_irqrestore(&wdt_lock, flags);
+
+ if (new_status & WDC_SR_ISOI0)
+ status |= WDIOF_EXTERN1;
+ if (new_status & WDC_SR_ISII1)
+ status |= WDIOF_EXTERN2;
+ if (type == 501) {
+ if (!(new_status & WDC_SR_TGOOD))
+ status |= WDIOF_OVERHEAT;
+ if (!(new_status & WDC_SR_PSUOVER))
+ status |= WDIOF_POWEROVER;
+ if (!(new_status & WDC_SR_PSUUNDR))
+ status |= WDIOF_POWERUNDER;
+ if (tachometer) {
+ if (!(new_status & WDC_SR_FANGOOD))
+ status |= WDIOF_FANFAULT;
+ }
+ }
+ return status;
+}
+
+/**
+ * wdt_get_temperature:
+ *
+ * Reports the temperature in degrees Fahrenheit. The API is in
+ * farenheit. It was designed by an imperial measurement luddite.
+ */
+
+static int wdt_get_temperature(void)
+{
+ unsigned short c;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt_lock, flags);
+ c = inb_p(WDT_RT);
+ spin_unlock_irqrestore(&wdt_lock, flags);
+ return (c * 11 / 15) + 7;
+}
+
+static void wdt_decode_501(int status)
+{
+ if (!(status & WDC_SR_TGOOD))
+ printk(KERN_CRIT "Overheat alarm.(%d)\n", inb_p(WDT_RT));
+ if (!(status & WDC_SR_PSUOVER))
+ printk(KERN_CRIT "PSU over voltage.\n");
+ if (!(status & WDC_SR_PSUUNDR))
+ printk(KERN_CRIT "PSU under voltage.\n");
+}
+
+/**
+ * wdt_interrupt:
+ * @irq: Interrupt number
+ * @dev_id: Unused as we don't allow multiple devices.
+ *
+ * Handle an interrupt from the board. These are raised when the status
+ * map changes in what the board considers an interesting way. That means
+ * a failure condition occurring.
+ */
+
+static irqreturn_t wdt_interrupt(int irq, void *dev_id)
+{
+ /*
+ * Read the status register see what is up and
+ * then printk it.
+ */
+ unsigned char status;
+
+ spin_lock(&wdt_lock);
+ status = inb_p(WDT_SR);
+
+ printk(KERN_CRIT "WDT status %d\n", status);
+
+ if (type == 501) {
+ wdt_decode_501(status);
+ if (tachometer) {
+ if (!(status & WDC_SR_FANGOOD))
+ printk(KERN_CRIT "Possible fan fault.\n");
+ }
+ }
+ if (!(status & WDC_SR_WCCR)) {
+#ifdef SOFTWARE_REBOOT
+#ifdef ONLY_TESTING
+ printk(KERN_CRIT "Would Reboot.\n");
+#else
+ printk(KERN_CRIT "Initiating system reboot.\n");
+ emergency_restart();
+#endif
+#else
+ printk(KERN_CRIT "Reset in 5ms.\n");
+#endif
+ }
+ spin_unlock(&wdt_lock);
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * wdt_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write (unused as data does not matter here
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ wdt_ping();
+ }
+ return count;
+}
+
+/**
+ * wdt_ioctl:
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features. We only actually usefully support
+ * querying capabilities and current status.
+ */
+
+static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_heartbeat;
+ int status;
+
+ struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT|
+ WDIOF_MAGICCLOSE|
+ WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = "WDT500/501",
+ };
+
+ /* Add options according to the card we have */
+ ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);
+ if (type == 501) {
+ ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|
+ WDIOF_POWEROVER);
+ if (tachometer)
+ ident.options |= WDIOF_FANFAULT;
+ }
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ status = wdt_get_status();
+ return put_user(status, p);
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_KEEPALIVE:
+ wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_heartbeat, p))
+ return -EFAULT;
+ if (wdt_set_heartbeat(new_heartbeat))
+ return -EINVAL;
+ wdt_ping();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/**
+ * wdt_open:
+ * @inode: inode of device
+ * @file: file handle to device
+ *
+ * The watchdog device has been opened. The watchdog device is single
+ * open and on opening we load the counters. Counter zero is a 100Hz
+ * cascade, into counter 1 which downcounts to reboot. When the counter
+ * triggers counter 2 downcounts the length of the reset pulse which
+ * set set to be as long as possible.
+ */
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+ wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * wdt_release:
+ * @inode: inode to board
+ * @file: file handle to board
+ *
+ * The watchdog has a configurable API. There is a religious dispute
+ * between people who want their watchdog to be able to shut down and
+ * those who want to be sure if the watchdog manager dies the machine
+ * reboots. In the former case we disable the counters, in the latter
+ * case you have to open it again very soon.
+ */
+
+static int wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ wdt_stop();
+ clear_bit(0, &wdt_is_open);
+ } else {
+ printk(KERN_CRIT
+ "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
+ wdt_ping();
+ }
+ expect_close = 0;
+ return 0;
+}
+
+/**
+ * wdt_temp_read:
+ * @file: file handle to the watchdog board
+ * @buf: buffer to write 1 byte into
+ * @count: length of buffer
+ * @ptr: offset (no seek allowed)
+ *
+ * Temp_read reports the temperature in degrees Fahrenheit. The API is in
+ * farenheit. It was designed by an imperial measurement luddite.
+ */
+
+static ssize_t wdt_temp_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ptr)
+{
+ int temperature = wdt_get_temperature();
+
+ if (copy_to_user(buf, &temperature, 1))
+ return -EFAULT;
+
+ return 1;
+}
+
+/**
+ * wdt_temp_open:
+ * @inode: inode of device
+ * @file: file handle to device
+ *
+ * The temperature device has been opened.
+ */
+
+static int wdt_temp_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * wdt_temp_release:
+ * @inode: inode to board
+ * @file: file handle to board
+ *
+ * The temperature device has been closed.
+ */
+
+static int wdt_temp_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/**
+ * notify_sys:
+ * @this: our notifier block
+ * @code: the event being reported
+ * @unused: unused
+ *
+ * Our notifier is called on system shutdowns. We want to turn the card
+ * off at reboot otherwise the machine will reboot again during memory
+ * test or worse yet during the following fsck. This would suck, in fact
+ * trust me - if it happens it does suck.
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_stop();
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdt_write,
+ .unlocked_ioctl = wdt_ioctl,
+ .open = wdt_open,
+ .release = wdt_release,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+static const struct file_operations wdt_temp_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = wdt_temp_read,
+ .open = wdt_temp_open,
+ .release = wdt_temp_release,
+};
+
+static struct miscdevice temp_miscdev = {
+ .minor = TEMP_MINOR,
+ .name = "temperature",
+ .fops = &wdt_temp_fops,
+};
+
+/*
+ * The WDT card needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+/**
+ * cleanup_module:
+ *
+ * Unload the watchdog. You cannot do this with any file handles open.
+ * If your watchdog is set to continue ticking on close and you unload
+ * it, well it keeps ticking. We won't get the interrupt but the board
+ * will not touch PC memory so all is fine. You just have to load a new
+ * module in 60 seconds or reboot.
+ */
+
+static void __exit wdt_exit(void)
+{
+ misc_deregister(&wdt_miscdev);
+ if (type == 501)
+ misc_deregister(&temp_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ free_irq(irq, NULL);
+ release_region(io, 8);
+}
+
+/**
+ * wdt_init:
+ *
+ * Set up the WDT watchdog board. All we have to do is grab the
+ * resources we require and bitch if anyone beat us to them.
+ * The open() function will actually kick the board off.
+ */
+
+static int __init wdt_init(void)
+{
+ int ret;
+
+ if (type != 500 && type != 501) {
+ printk(KERN_ERR "wdt: unknown card type '%d'.\n", type);
+ return -ENODEV;
+ }
+
+ /* Check that the heartbeat value is within it's range;
+ if not reset to the default */
+ if (wdt_set_heartbeat(heartbeat)) {
+ wdt_set_heartbeat(WD_TIMO);
+ printk(KERN_INFO "wdt: heartbeat value must be "
+ "0 < heartbeat < 65536, using %d\n", WD_TIMO);
+ }
+
+ if (!request_region(io, 8, "wdt501p")) {
+ printk(KERN_ERR
+ "wdt: I/O address 0x%04x already in use\n", io);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = request_irq(irq, wdt_interrupt, IRQF_DISABLED, "wdt501p", NULL);
+ if (ret) {
+ printk(KERN_ERR "wdt: IRQ %d is not free.\n", irq);
+ goto outreg;
+ }
+
+ ret = register_reboot_notifier(&wdt_notifier);
+ if (ret) {
+ printk(KERN_ERR
+ "wdt: cannot register reboot notifier (err=%d)\n", ret);
+ goto outirq;
+ }
+
+ if (type == 501) {
+ ret = misc_register(&temp_miscdev);
+ if (ret) {
+ printk(KERN_ERR "wdt: cannot register miscdev "
+ "on minor=%d (err=%d)\n", TEMP_MINOR, ret);
+ goto outrbt;
+ }
+ }
+
+ ret = misc_register(&wdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR
+ "wdt: cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto outmisc;
+ }
+
+ printk(KERN_INFO "WDT500/501-P driver 0.10 "
+ "at 0x%04x (Interrupt %d). heartbeat=%d sec (nowayout=%d)\n",
+ io, irq, heartbeat, nowayout);
+ if (type == 501)
+ printk(KERN_INFO "wdt: Fan Tachometer is %s\n",
+ (tachometer ? "Enabled" : "Disabled"));
+ return 0;
+
+outmisc:
+ if (type == 501)
+ misc_deregister(&temp_miscdev);
+outrbt:
+ unregister_reboot_notifier(&wdt_notifier);
+outirq:
+ free_irq(irq, NULL);
+outreg:
+ release_region(io, 8);
+out:
+ return ret;
+}
+
+module_init(wdt_init);
+module_exit(wdt_exit);
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("Driver for ISA ICS watchdog cards (WDT500/501)");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS_MISCDEV(TEMP_MINOR);
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c
new file mode 100644
index 00000000..f5513566
--- /dev/null
+++ b/drivers/watchdog/wdt285.c
@@ -0,0 +1,232 @@
+/*
+ * Intel 21285 watchdog driver
+ * Copyright (c) Phil Blundell <pb@nexus.co.uk>, 1998
+ *
+ * based on
+ *
+ * SoftDog 0.05: A Software Watchdog Device
+ *
+ * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/irq.h>
+#include <mach/hardware.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/dec21285.h>
+
+/*
+ * Define this to stop the watchdog actually rebooting the machine.
+ */
+#undef ONLY_TESTING
+
+static unsigned int soft_margin = 60; /* in seconds */
+static unsigned int reload;
+static unsigned long timer_alive;
+
+#ifdef ONLY_TESTING
+/*
+ * If the timer expires..
+ */
+static void watchdog_fire(int irq, void *dev_id)
+{
+ printk(KERN_CRIT "Watchdog: Would Reboot.\n");
+ *CSR_TIMER4_CNTL = 0;
+ *CSR_TIMER4_CLR = 0;
+}
+#endif
+
+/*
+ * Refresh the timer.
+ */
+static void watchdog_ping(void)
+{
+ *CSR_TIMER4_LOAD = reload;
+}
+
+/*
+ * Allow only one person to hold it open
+ */
+static int watchdog_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ if (*CSR_SA110_CNTL & (1 << 13))
+ return -EBUSY;
+
+ if (test_and_set_bit(1, &timer_alive))
+ return -EBUSY;
+
+ reload = soft_margin * (mem_fclk_21285 / 256);
+
+ *CSR_TIMER4_CLR = 0;
+ watchdog_ping();
+ *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD
+ | TIMER_CNTL_DIV256;
+
+#ifdef ONLY_TESTING
+ ret = request_irq(IRQ_TIMER4, watchdog_fire, 0, "watchdog", NULL);
+ if (ret) {
+ *CSR_TIMER4_CNTL = 0;
+ clear_bit(1, &timer_alive);
+ }
+#else
+ /*
+ * Setting this bit is irreversible; once enabled, there is
+ * no way to disable the watchdog.
+ */
+ *CSR_SA110_CNTL |= 1 << 13;
+
+ ret = 0;
+#endif
+ nonseekable_open(inode, file);
+ return ret;
+}
+
+/*
+ * Shut off the timer.
+ * Note: if we really have enabled the watchdog, there
+ * is no way to turn off.
+ */
+static int watchdog_release(struct inode *inode, struct file *file)
+{
+#ifdef ONLY_TESTING
+ free_irq(IRQ_TIMER4, NULL);
+ clear_bit(1, &timer_alive);
+#endif
+ return 0;
+}
+
+static ssize_t watchdog_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /*
+ * Refresh the timer.
+ */
+ if (len)
+ watchdog_ping();
+
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT,
+ .identity = "Footbridge Watchdog",
+};
+
+static long watchdog_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int new_margin;
+ int __user *int_arg = (int __user *)arg;
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = 0;
+ if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
+ ret = -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, int_arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ watchdog_ping();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(new_margin, int_arg);
+ if (ret)
+ break;
+
+ /* Arbitrary, can't find the card's limits */
+ if (new_margin < 0 || new_margin > 60) {
+ ret = -EINVAL;
+ break;
+ }
+
+ soft_margin = new_margin;
+ reload = soft_margin * (mem_fclk_21285 / 256);
+ watchdog_ping();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(soft_margin, int_arg);
+ break;
+ }
+ return ret;
+}
+
+static const struct file_operations watchdog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = watchdog_write,
+ .unlocked_ioctl = watchdog_ioctl,
+ .open = watchdog_open,
+ .release = watchdog_release,
+};
+
+static struct miscdevice watchdog_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &watchdog_fops,
+};
+
+static int __init footbridge_watchdog_init(void)
+{
+ int retval;
+
+ if (machine_is_netwinder())
+ return -ENODEV;
+
+ retval = misc_register(&watchdog_miscdev);
+ if (retval < 0)
+ return retval;
+
+ printk(KERN_INFO
+ "Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n",
+ soft_margin);
+
+ if (machine_is_cats())
+ printk(KERN_WARNING
+ "Warning: Watchdog reset may not work on this machine.\n");
+ return 0;
+}
+
+static void __exit footbridge_watchdog_exit(void)
+{
+ misc_deregister(&watchdog_miscdev);
+}
+
+MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>");
+MODULE_DESCRIPTION("Footbridge watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+module_param(soft_margin, int, 0);
+MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
+
+module_init(footbridge_watchdog_init);
+module_exit(footbridge_watchdog_exit);
diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c
new file mode 100644
index 00000000..a2f01c9f
--- /dev/null
+++ b/drivers/watchdog/wdt977.c
@@ -0,0 +1,517 @@
+/*
+ * Wdt977 0.04: A Watchdog Device for Netwinder W83977AF chip
+ *
+ * (c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>)
+ *
+ * -----------------------
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * -----------------------
+ * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
+ * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ * 19-Dec-2001 Woody Suwalski: Netwinder fixes, ioctl interface
+ * 06-Jan-2002 Woody Suwalski: For compatibility, convert all timeouts
+ * from minutes to seconds.
+ * 07-Jul-2003 Daniele Bellucci: Audit return code of misc_register in
+ * nwwatchdog_init.
+ * 25-Oct-2005 Woody Suwalski: Convert addresses to #defs, add spinlocks
+ * remove limitiation to be used on
+ * Netwinders only
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#define WATCHDOG_VERSION "0.04"
+#define WATCHDOG_NAME "Wdt977"
+#define PFX WATCHDOG_NAME ": "
+#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
+
+#define IO_INDEX_PORT 0x370 /* on some systems it can be 0x3F0 */
+#define IO_DATA_PORT (IO_INDEX_PORT + 1)
+
+#define UNLOCK_DATA 0x87
+#define LOCK_DATA 0xAA
+#define DEVICE_REGISTER 0x07
+
+
+#define DEFAULT_TIMEOUT 60 /* default timeout in seconds */
+
+static int timeout = DEFAULT_TIMEOUT;
+static int timeoutM; /* timeout in minutes */
+static unsigned long timer_alive;
+static int testmode;
+static char expect_close;
+static DEFINE_SPINLOCK(spinlock);
+
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (60..15300, default="
+ __MODULE_STRING(DEFAULT_TIMEOUT) ")");
+module_param(testmode, int, 0);
+MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Start the watchdog
+ */
+
+static int wdt977_start(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+
+ /* unlock the SuperIO chip */
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+
+ /* select device Aux2 (device=8) and set watchdog regs F2, F3 and F4
+ * F2 has the timeout in minutes
+ * F3 could be set to the POWER LED blink (with GP17 set to PowerLed)
+ * at timeout, and to reset timer on kbd/mouse activity (not impl.)
+ * F4 is used to just clear the TIMEOUT'ed state (bit 0)
+ */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ outb_p(0xF2, IO_INDEX_PORT);
+ outb_p(timeoutM, IO_DATA_PORT);
+ outb_p(0xF3, IO_INDEX_PORT);
+ outb_p(0x00, IO_DATA_PORT); /* another setting is 0E for
+ kbd/mouse/LED */
+ outb_p(0xF4, IO_INDEX_PORT);
+ outb_p(0x00, IO_DATA_PORT);
+
+ /* At last select device Aux1 (dev=7) and set GP16 as a
+ * watchdog output. In test mode watch the bit 1 on F4 to
+ * indicate "triggered"
+ */
+ if (!testmode) {
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x07, IO_DATA_PORT);
+ outb_p(0xE6, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ }
+
+ /* lock the SuperIO chip */
+ outb_p(LOCK_DATA, IO_INDEX_PORT);
+
+ spin_unlock_irqrestore(&spinlock, flags);
+ printk(KERN_INFO PFX "activated.\n");
+
+ return 0;
+}
+
+/*
+ * Stop the watchdog
+ */
+
+static int wdt977_stop(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&spinlock, flags);
+
+ /* unlock the SuperIO chip */
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+
+ /* select device Aux2 (device=8) and set watchdog regs F2,F3 and F4
+ * F3 is reset to its default state
+ * F4 can clear the TIMEOUT'ed state (bit 0) - back to default
+ * We can not use GP17 as a PowerLed, as we use its usage as a RedLed
+ */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ outb_p(0xF2, IO_INDEX_PORT);
+ outb_p(0xFF, IO_DATA_PORT);
+ outb_p(0xF3, IO_INDEX_PORT);
+ outb_p(0x00, IO_DATA_PORT);
+ outb_p(0xF4, IO_INDEX_PORT);
+ outb_p(0x00, IO_DATA_PORT);
+ outb_p(0xF2, IO_INDEX_PORT);
+ outb_p(0x00, IO_DATA_PORT);
+
+ /* at last select device Aux1 (dev=7) and set
+ GP16 as a watchdog output */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x07, IO_DATA_PORT);
+ outb_p(0xE6, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+
+ /* lock the SuperIO chip */
+ outb_p(LOCK_DATA, IO_INDEX_PORT);
+
+ spin_unlock_irqrestore(&spinlock, flags);
+ printk(KERN_INFO PFX "shutdown.\n");
+
+ return 0;
+}
+
+/*
+ * Send a keepalive ping to the watchdog
+ * This is done by simply re-writing the timeout to reg. 0xF2
+ */
+
+static int wdt977_keepalive(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&spinlock, flags);
+
+ /* unlock the SuperIO chip */
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+
+ /* select device Aux2 (device=8) and kicks watchdog reg F2 */
+ /* F2 has the timeout in minutes */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ outb_p(0xF2, IO_INDEX_PORT);
+ outb_p(timeoutM, IO_DATA_PORT);
+
+ /* lock the SuperIO chip */
+ outb_p(LOCK_DATA, IO_INDEX_PORT);
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * Set the watchdog timeout value
+ */
+
+static int wdt977_set_timeout(int t)
+{
+ int tmrval;
+
+ /* convert seconds to minutes, rounding up */
+ tmrval = (t + 59) / 60;
+
+ if (machine_is_netwinder()) {
+ /* we have a hw bug somewhere, so each 977 minute is actually
+ * only 30sec. This limits the max timeout to half of device
+ * max of 255 minutes...
+ */
+ tmrval += tmrval;
+ }
+
+ if (tmrval < 1 || tmrval > 255)
+ return -EINVAL;
+
+ /* timeout is the timeout in seconds, timeoutM is
+ the timeout in minutes) */
+ timeout = t;
+ timeoutM = tmrval;
+ return 0;
+}
+
+/*
+ * Get the watchdog status
+ */
+
+static int wdt977_get_status(int *status)
+{
+ int new_status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+
+ /* unlock the SuperIO chip */
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+ outb_p(UNLOCK_DATA, IO_INDEX_PORT);
+
+ /* select device Aux2 (device=8) and read watchdog reg F4 */
+ outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
+ outb_p(0x08, IO_DATA_PORT);
+ outb_p(0xF4, IO_INDEX_PORT);
+ new_status = inb_p(IO_DATA_PORT);
+
+ /* lock the SuperIO chip */
+ outb_p(LOCK_DATA, IO_INDEX_PORT);
+
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ *status = 0;
+ if (new_status & 1)
+ *status |= WDIOF_CARDRESET;
+
+ return 0;
+}
+
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int wdt977_open(struct inode *inode, struct file *file)
+{
+ /* If the watchdog is alive we don't need to start it again */
+ if (test_and_set_bit(0, &timer_alive))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ wdt977_start();
+ return nonseekable_open(inode, file);
+}
+
+static int wdt977_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ * Lock it in if it's a module and we set nowayout
+ */
+ if (expect_close == 42) {
+ wdt977_stop();
+ clear_bit(0, &timer_alive);
+ } else {
+ wdt977_keepalive();
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ }
+ expect_close = 0;
+ return 0;
+}
+
+
+/*
+ * wdt977_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write (unused as data does not matter here
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t wdt977_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+
+ /* someone wrote to us, we should restart timer */
+ wdt977_keepalive();
+ }
+ return count;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = WATCHDOG_NAME,
+};
+
+/*
+ * wdt977_ioctl:
+ * @inode: inode of the device
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
+ */
+
+static long wdt977_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int status;
+ int new_options, retval = -EINVAL;
+ int new_timeout;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ wdt977_get_status(&status);
+ return put_user(status, uarg.i);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, uarg.i))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD) {
+ wdt977_stop();
+ retval = 0;
+ }
+
+ if (new_options & WDIOS_ENABLECARD) {
+ wdt977_start();
+ retval = 0;
+ }
+
+ return retval;
+
+ case WDIOC_KEEPALIVE:
+ wdt977_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+
+ if (wdt977_set_timeout(new_timeout))
+ return -EINVAL;
+
+ wdt977_keepalive();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, uarg.i);
+
+ default:
+ return -ENOTTY;
+
+ }
+}
+
+static int wdt977_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt977_stop();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations wdt977_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdt977_write,
+ .unlocked_ioctl = wdt977_ioctl,
+ .open = wdt977_open,
+ .release = wdt977_release,
+};
+
+static struct miscdevice wdt977_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt977_fops,
+};
+
+static struct notifier_block wdt977_notifier = {
+ .notifier_call = wdt977_notify_sys,
+};
+
+static int __init wd977_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO PFX DRIVER_VERSION);
+
+ /* Check that the timeout value is within its range;
+ if not reset to the default */
+ if (wdt977_set_timeout(timeout)) {
+ wdt977_set_timeout(DEFAULT_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 60 < timeout < 15300, using %d\n",
+ DEFAULT_TIMEOUT);
+ }
+
+ /* on Netwinder the IOports are already reserved by
+ * arch/arm/mach-footbridge/netwinder-hw.c
+ */
+ if (!machine_is_netwinder()) {
+ if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX
+ "I/O address 0x%04x already in use\n",
+ IO_INDEX_PORT);
+ rc = -EIO;
+ goto err_out;
+ }
+ }
+
+ rc = register_reboot_notifier(&wdt977_notifier);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out_region;
+ }
+
+ rc = misc_register(&wdt977_miscdev);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ wdt977_miscdev.minor, rc);
+ goto err_out_reboot;
+ }
+
+ printk(KERN_INFO PFX
+ "initialized. timeout=%d sec (nowayout=%d, testmode=%i)\n",
+ timeout, nowayout, testmode);
+
+ return 0;
+
+err_out_reboot:
+ unregister_reboot_notifier(&wdt977_notifier);
+err_out_region:
+ if (!machine_is_netwinder())
+ release_region(IO_INDEX_PORT, 2);
+err_out:
+ return rc;
+}
+
+static void __exit wd977_exit(void)
+{
+ wdt977_stop();
+ misc_deregister(&wdt977_miscdev);
+ unregister_reboot_notifier(&wdt977_notifier);
+ release_region(IO_INDEX_PORT, 2);
+}
+
+module_init(wd977_init);
+module_exit(wd977_exit);
+
+MODULE_AUTHOR("Woody Suwalski <woodys@xandros.com>");
+MODULE_DESCRIPTION("W83977AF Watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c
new file mode 100644
index 00000000..172dad6c
--- /dev/null
+++ b/drivers/watchdog/wdt_pci.c
@@ -0,0 +1,787 @@
+/*
+ * Industrial Computer Source PCI-WDT500/501 driver
+ *
+ * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * Release 0.10.
+ *
+ * Fixes
+ * Dave Gregorich : Modularisation and minor bugs
+ * Alan Cox : Added the watchdog ioctl() stuff
+ * Alan Cox : Fixed the reboot problem (as noted by
+ * Matt Crocker).
+ * Alan Cox : Added wdt= boot option
+ * Alan Cox : Cleaned up copy/user stuff
+ * Tim Hockin : Added insmod parameters, comment cleanup
+ * Parameterized timeout
+ * JP Nollmann : Added support for PCI wdt501p
+ * Alan Cox : Split ISA and PCI cards into two drivers
+ * Jeff Garzik : PCI cleanups
+ * Tigran Aivazian : Restructured wdtpci_init_one() to handle
+ * failures
+ * Joel Becker : Added WDIOC_GET/SETTIMEOUT
+ * Zwane Mwaikambo : Magic char closing, locking changes,
+ * cleanups
+ * Matt Domsch : nowayout module option
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define WDT_IS_PCI
+#include "wd501p.h"
+
+#define PFX "wdt_pci: "
+
+/* We can only use 1 card due to the /dev/watchdog restriction */
+static int dev_count;
+
+static unsigned long open_lock;
+static DEFINE_SPINLOCK(wdtpci_lock);
+static char expect_close;
+
+static resource_size_t io;
+static int irq;
+
+/* Default timeout */
+#define WD_TIMO 60 /* Default heartbeat = 60 seconds */
+
+static int heartbeat = WD_TIMO;
+static int wd_heartbeat;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat in seconds. (0<heartbeat<65536, default="
+ __MODULE_STRING(WD_TIMO) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/* Support for the Fan Tachometer on the PCI-WDT501 */
+static int tachometer;
+module_param(tachometer, int, 0);
+MODULE_PARM_DESC(tachometer,
+ "PCI-WDT501 Fan Tachometer support (0=disable, default=0)");
+
+static int type = 500;
+module_param(type, int, 0);
+MODULE_PARM_DESC(type,
+ "PCI-WDT501 Card type (500 or 501 , default=500)");
+
+/*
+ * Programming support
+ */
+
+static void wdtpci_ctr_mode(int ctr, int mode)
+{
+ ctr <<= 6;
+ ctr |= 0x30;
+ ctr |= (mode << 1);
+ outb(ctr, WDT_CR);
+ udelay(8);
+}
+
+static void wdtpci_ctr_load(int ctr, int val)
+{
+ outb(val & 0xFF, WDT_COUNT0 + ctr);
+ udelay(8);
+ outb(val >> 8, WDT_COUNT0 + ctr);
+ udelay(8);
+}
+
+/**
+ * wdtpci_start:
+ *
+ * Start the watchdog driver.
+ */
+
+static int wdtpci_start(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdtpci_lock, flags);
+
+ /*
+ * "pet" the watchdog, as Access says.
+ * This resets the clock outputs.
+ */
+ inb(WDT_DC); /* Disable watchdog */
+ udelay(8);
+ wdtpci_ctr_mode(2, 0); /* Program CTR2 for Mode 0:
+ Pulse on Terminal Count */
+ outb(0, WDT_DC); /* Enable watchdog */
+ udelay(8);
+ inb(WDT_DC); /* Disable watchdog */
+ udelay(8);
+ outb(0, WDT_CLOCK); /* 2.0833MHz clock */
+ udelay(8);
+ inb(WDT_BUZZER); /* disable */
+ udelay(8);
+ inb(WDT_OPTONOTRST); /* disable */
+ udelay(8);
+ inb(WDT_OPTORST); /* disable */
+ udelay(8);
+ inb(WDT_PROGOUT); /* disable */
+ udelay(8);
+ wdtpci_ctr_mode(0, 3); /* Program CTR0 for Mode 3:
+ Square Wave Generator */
+ wdtpci_ctr_mode(1, 2); /* Program CTR1 for Mode 2:
+ Rate Generator */
+ wdtpci_ctr_mode(2, 1); /* Program CTR2 for Mode 1:
+ Retriggerable One-Shot */
+ wdtpci_ctr_load(0, 20833); /* count at 100Hz */
+ wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */
+ /* DO NOT LOAD CTR2 on PCI card! -- JPN */
+ outb(0, WDT_DC); /* Enable watchdog */
+ udelay(8);
+
+ spin_unlock_irqrestore(&wdtpci_lock, flags);
+ return 0;
+}
+
+/**
+ * wdtpci_stop:
+ *
+ * Stop the watchdog driver.
+ */
+
+static int wdtpci_stop(void)
+{
+ unsigned long flags;
+
+ /* Turn the card off */
+ spin_lock_irqsave(&wdtpci_lock, flags);
+ inb(WDT_DC); /* Disable watchdog */
+ udelay(8);
+ wdtpci_ctr_load(2, 0); /* 0 length reset pulses now */
+ spin_unlock_irqrestore(&wdtpci_lock, flags);
+ return 0;
+}
+
+/**
+ * wdtpci_ping:
+ *
+ * Reload counter one with the watchdog heartbeat. We don't bother
+ * reloading the cascade counter.
+ */
+
+static int wdtpci_ping(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdtpci_lock, flags);
+ /* Write a watchdog value */
+ inb(WDT_DC); /* Disable watchdog */
+ udelay(8);
+ wdtpci_ctr_mode(1, 2); /* Re-Program CTR1 for Mode 2:
+ Rate Generator */
+ wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */
+ outb(0, WDT_DC); /* Enable watchdog */
+ udelay(8);
+ spin_unlock_irqrestore(&wdtpci_lock, flags);
+ return 0;
+}
+
+/**
+ * wdtpci_set_heartbeat:
+ * @t: the new heartbeat value that needs to be set.
+ *
+ * Set a new heartbeat value for the watchdog device. If the heartbeat
+ * value is incorrect we keep the old value and return -EINVAL.
+ * If successful we return 0.
+ */
+static int wdtpci_set_heartbeat(int t)
+{
+ /* Arbitrary, can't find the card's limits */
+ if (t < 1 || t > 65535)
+ return -EINVAL;
+
+ heartbeat = t;
+ wd_heartbeat = t * 100;
+ return 0;
+}
+
+/**
+ * wdtpci_get_status:
+ * @status: the new status.
+ *
+ * Extract the status information from a WDT watchdog device. There are
+ * several board variants so we have to know which bits are valid. Some
+ * bits default to one and some to zero in order to be maximally painful.
+ *
+ * we then map the bits onto the status ioctl flags.
+ */
+
+static int wdtpci_get_status(int *status)
+{
+ unsigned char new_status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdtpci_lock, flags);
+ new_status = inb(WDT_SR);
+ spin_unlock_irqrestore(&wdtpci_lock, flags);
+
+ *status = 0;
+ if (new_status & WDC_SR_ISOI0)
+ *status |= WDIOF_EXTERN1;
+ if (new_status & WDC_SR_ISII1)
+ *status |= WDIOF_EXTERN2;
+ if (type == 501) {
+ if (!(new_status & WDC_SR_TGOOD))
+ *status |= WDIOF_OVERHEAT;
+ if (!(new_status & WDC_SR_PSUOVER))
+ *status |= WDIOF_POWEROVER;
+ if (!(new_status & WDC_SR_PSUUNDR))
+ *status |= WDIOF_POWERUNDER;
+ if (tachometer) {
+ if (!(new_status & WDC_SR_FANGOOD))
+ *status |= WDIOF_FANFAULT;
+ }
+ }
+ return 0;
+}
+
+/**
+ * wdtpci_get_temperature:
+ *
+ * Reports the temperature in degrees Fahrenheit. The API is in
+ * farenheit. It was designed by an imperial measurement luddite.
+ */
+
+static int wdtpci_get_temperature(int *temperature)
+{
+ unsigned short c;
+ unsigned long flags;
+ spin_lock_irqsave(&wdtpci_lock, flags);
+ c = inb(WDT_RT);
+ udelay(8);
+ spin_unlock_irqrestore(&wdtpci_lock, flags);
+ *temperature = (c * 11 / 15) + 7;
+ return 0;
+}
+
+/**
+ * wdtpci_interrupt:
+ * @irq: Interrupt number
+ * @dev_id: Unused as we don't allow multiple devices.
+ *
+ * Handle an interrupt from the board. These are raised when the status
+ * map changes in what the board considers an interesting way. That means
+ * a failure condition occurring.
+ */
+
+static irqreturn_t wdtpci_interrupt(int irq, void *dev_id)
+{
+ /*
+ * Read the status register see what is up and
+ * then printk it.
+ */
+ unsigned char status;
+
+ spin_lock(&wdtpci_lock);
+
+ status = inb(WDT_SR);
+ udelay(8);
+
+ printk(KERN_CRIT PFX "status %d\n", status);
+
+ if (type == 501) {
+ if (!(status & WDC_SR_TGOOD)) {
+ printk(KERN_CRIT PFX "Overheat alarm.(%d)\n",
+ inb(WDT_RT));
+ udelay(8);
+ }
+ if (!(status & WDC_SR_PSUOVER))
+ printk(KERN_CRIT PFX "PSU over voltage.\n");
+ if (!(status & WDC_SR_PSUUNDR))
+ printk(KERN_CRIT PFX "PSU under voltage.\n");
+ if (tachometer) {
+ if (!(status & WDC_SR_FANGOOD))
+ printk(KERN_CRIT PFX "Possible fan fault.\n");
+ }
+ }
+ if (!(status & WDC_SR_WCCR)) {
+#ifdef SOFTWARE_REBOOT
+#ifdef ONLY_TESTING
+ printk(KERN_CRIT PFX "Would Reboot.\n");
+#else
+ printk(KERN_CRIT PFX "Initiating system reboot.\n");
+ emergency_restart(NULL);
+#endif
+#else
+ printk(KERN_CRIT PFX "Reset in 5ms.\n");
+#endif
+ }
+ spin_unlock(&wdtpci_lock);
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * wdtpci_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write (unused as data does not matter here
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t wdtpci_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ wdtpci_ping();
+ }
+ return count;
+}
+
+/**
+ * wdtpci_ioctl:
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features. We only actually usefully support
+ * querying capabilities and current status.
+ */
+
+static long wdtpci_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_heartbeat;
+ int status;
+
+ struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT|
+ WDIOF_MAGICCLOSE|
+ WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = "PCI-WDT500/501",
+ };
+
+ /* Add options according to the card we have */
+ ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);
+ if (type == 501) {
+ ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|
+ WDIOF_POWEROVER);
+ if (tachometer)
+ ident.options |= WDIOF_FANFAULT;
+ }
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ wdtpci_get_status(&status);
+ return put_user(status, p);
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_KEEPALIVE:
+ wdtpci_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_heartbeat, p))
+ return -EFAULT;
+ if (wdtpci_set_heartbeat(new_heartbeat))
+ return -EINVAL;
+ wdtpci_ping();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/**
+ * wdtpci_open:
+ * @inode: inode of device
+ * @file: file handle to device
+ *
+ * The watchdog device has been opened. The watchdog device is single
+ * open and on opening we load the counters. Counter zero is a 100Hz
+ * cascade, into counter 1 which downcounts to reboot. When the counter
+ * triggers counter 2 downcounts the length of the reset pulse which
+ * set set to be as long as possible.
+ */
+
+static int wdtpci_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &open_lock))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+ /*
+ * Activate
+ */
+ wdtpci_start();
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * wdtpci_release:
+ * @inode: inode to board
+ * @file: file handle to board
+ *
+ * The watchdog has a configurable API. There is a religious dispute
+ * between people who want their watchdog to be able to shut down and
+ * those who want to be sure if the watchdog manager dies the machine
+ * reboots. In the former case we disable the counters, in the latter
+ * case you have to open it again very soon.
+ */
+
+static int wdtpci_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ wdtpci_stop();
+ } else {
+ printk(KERN_CRIT PFX "Unexpected close, not stopping timer!");
+ wdtpci_ping();
+ }
+ expect_close = 0;
+ clear_bit(0, &open_lock);
+ return 0;
+}
+
+/**
+ * wdtpci_temp_read:
+ * @file: file handle to the watchdog board
+ * @buf: buffer to write 1 byte into
+ * @count: length of buffer
+ * @ptr: offset (no seek allowed)
+ *
+ * Read reports the temperature in degrees Fahrenheit. The API is in
+ * fahrenheit. It was designed by an imperial measurement luddite.
+ */
+
+static ssize_t wdtpci_temp_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ptr)
+{
+ int temperature;
+
+ if (wdtpci_get_temperature(&temperature))
+ return -EFAULT;
+
+ if (copy_to_user(buf, &temperature, 1))
+ return -EFAULT;
+
+ return 1;
+}
+
+/**
+ * wdtpci_temp_open:
+ * @inode: inode of device
+ * @file: file handle to device
+ *
+ * The temperature device has been opened.
+ */
+
+static int wdtpci_temp_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * wdtpci_temp_release:
+ * @inode: inode to board
+ * @file: file handle to board
+ *
+ * The temperature device has been closed.
+ */
+
+static int wdtpci_temp_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/**
+ * notify_sys:
+ * @this: our notifier block
+ * @code: the event being reported
+ * @unused: unused
+ *
+ * Our notifier is called on system shutdowns. We want to turn the card
+ * off at reboot otherwise the machine will reboot again during memory
+ * test or worse yet during the following fsck. This would suck, in fact
+ * trust me - if it happens it does suck.
+ */
+
+static int wdtpci_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdtpci_stop();
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+
+static const struct file_operations wdtpci_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdtpci_write,
+ .unlocked_ioctl = wdtpci_ioctl,
+ .open = wdtpci_open,
+ .release = wdtpci_release,
+};
+
+static struct miscdevice wdtpci_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdtpci_fops,
+};
+
+static const struct file_operations wdtpci_temp_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = wdtpci_temp_read,
+ .open = wdtpci_temp_open,
+ .release = wdtpci_temp_release,
+};
+
+static struct miscdevice temp_miscdev = {
+ .minor = TEMP_MINOR,
+ .name = "temperature",
+ .fops = &wdtpci_temp_fops,
+};
+
+/*
+ * The WDT card needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdtpci_notifier = {
+ .notifier_call = wdtpci_notify_sys,
+};
+
+
+static int __devinit wdtpci_init_one(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ int ret = -EIO;
+
+ dev_count++;
+ if (dev_count > 1) {
+ printk(KERN_ERR PFX "This driver only supports one device\n");
+ return -ENODEV;
+ }
+
+ if (type != 500 && type != 501) {
+ printk(KERN_ERR PFX "unknown card type '%d'.\n", type);
+ return -ENODEV;
+ }
+
+ if (pci_enable_device(dev)) {
+ printk(KERN_ERR PFX "Not possible to enable PCI Device\n");
+ return -ENODEV;
+ }
+
+ if (pci_resource_start(dev, 2) == 0x0000) {
+ printk(KERN_ERR PFX "No I/O-Address for card detected\n");
+ ret = -ENODEV;
+ goto out_pci;
+ }
+
+ if (pci_request_region(dev, 2, "wdt_pci")) {
+ printk(KERN_ERR PFX "I/O address 0x%llx already in use\n",
+ (unsigned long long)pci_resource_start(dev, 2));
+ goto out_pci;
+ }
+
+ irq = dev->irq;
+ io = pci_resource_start(dev, 2);
+
+ if (request_irq(irq, wdtpci_interrupt, IRQF_DISABLED | IRQF_SHARED,
+ "wdt_pci", &wdtpci_miscdev)) {
+ printk(KERN_ERR PFX "IRQ %d is not free\n", irq);
+ goto out_reg;
+ }
+
+ printk(KERN_INFO
+ "PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n",
+ (unsigned long long)io, irq);
+
+ /* Check that the heartbeat value is within its range;
+ if not reset to the default */
+ if (wdtpci_set_heartbeat(heartbeat)) {
+ wdtpci_set_heartbeat(WD_TIMO);
+ printk(KERN_INFO PFX
+ "heartbeat value must be 0 < heartbeat < 65536, using %d\n",
+ WD_TIMO);
+ }
+
+ ret = register_reboot_notifier(&wdtpci_notifier);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ goto out_irq;
+ }
+
+ if (type == 501) {
+ ret = misc_register(&temp_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ TEMP_MINOR, ret);
+ goto out_rbt;
+ }
+ }
+
+ ret = misc_register(&wdtpci_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto out_misc;
+ }
+
+ printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ heartbeat, nowayout);
+ if (type == 501)
+ printk(KERN_INFO "wdt: Fan Tachometer is %s\n",
+ (tachometer ? "Enabled" : "Disabled"));
+
+ ret = 0;
+out:
+ return ret;
+
+out_misc:
+ if (type == 501)
+ misc_deregister(&temp_miscdev);
+out_rbt:
+ unregister_reboot_notifier(&wdtpci_notifier);
+out_irq:
+ free_irq(irq, &wdtpci_miscdev);
+out_reg:
+ pci_release_region(dev, 2);
+out_pci:
+ pci_disable_device(dev);
+ goto out;
+}
+
+
+static void __devexit wdtpci_remove_one(struct pci_dev *pdev)
+{
+ /* here we assume only one device will ever have
+ * been picked up and registered by probe function */
+ misc_deregister(&wdtpci_miscdev);
+ if (type == 501)
+ misc_deregister(&temp_miscdev);
+ unregister_reboot_notifier(&wdtpci_notifier);
+ free_irq(irq, &wdtpci_miscdev);
+ pci_release_region(pdev, 2);
+ pci_disable_device(pdev);
+ dev_count--;
+}
+
+
+static DEFINE_PCI_DEVICE_TABLE(wdtpci_pci_tbl) = {
+ {
+ .vendor = PCI_VENDOR_ID_ACCESSIO,
+ .device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { 0, }, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, wdtpci_pci_tbl);
+
+
+static struct pci_driver wdtpci_driver = {
+ .name = "wdt_pci",
+ .id_table = wdtpci_pci_tbl,
+ .probe = wdtpci_init_one,
+ .remove = __devexit_p(wdtpci_remove_one),
+};
+
+
+/**
+ * wdtpci_cleanup:
+ *
+ * Unload the watchdog. You cannot do this with any file handles open.
+ * If your watchdog is set to continue ticking on close and you unload
+ * it, well it keeps ticking. We won't get the interrupt but the board
+ * will not touch PC memory so all is fine. You just have to load a new
+ * module in xx seconds or reboot.
+ */
+
+static void __exit wdtpci_cleanup(void)
+{
+ pci_unregister_driver(&wdtpci_driver);
+}
+
+
+/**
+ * wdtpci_init:
+ *
+ * Set up the WDT watchdog board. All we have to do is grab the
+ * resources we require and bitch if anyone beat us to them.
+ * The open() function will actually kick the board off.
+ */
+
+static int __init wdtpci_init(void)
+{
+ return pci_register_driver(&wdtpci_driver);
+}
+
+
+module_init(wdtpci_init);
+module_exit(wdtpci_cleanup);
+
+MODULE_AUTHOR("JP Nollmann, Alan Cox");
+MODULE_DESCRIPTION("Driver for the ICS PCI-WDT500/501 watchdog cards");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS_MISCDEV(TEMP_MINOR);
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
new file mode 100644
index 00000000..871caea4
--- /dev/null
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -0,0 +1,446 @@
+/*
+ * Watchdog driver for the wm831x PMICs
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/watchdog.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned long wm831x_wdt_users;
+static struct miscdevice wm831x_wdt_miscdev;
+static int wm831x_wdt_expect_close;
+static DEFINE_MUTEX(wdt_mutex);
+static struct wm831x *wm831x;
+static unsigned int update_gpio;
+static unsigned int update_state;
+
+/* We can't use the sub-second values here but they're included
+ * for completeness. */
+static struct {
+ int time; /* Seconds */
+ u16 val; /* WDOG_TO value */
+} wm831x_wdt_cfgs[] = {
+ { 1, 2 },
+ { 2, 3 },
+ { 4, 4 },
+ { 8, 5 },
+ { 16, 6 },
+ { 32, 7 },
+ { 33, 7 }, /* Actually 32.768s so include both, others round down */
+};
+
+static int wm831x_wdt_set_timeout(struct wm831x *wm831x, u16 value)
+{
+ int ret;
+
+ mutex_lock(&wdt_mutex);
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
+ WM831X_WDOG_TO_MASK, value);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
+ ret);
+ }
+
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm831x_wdt_start(struct wm831x *wm831x)
+{
+ int ret;
+
+ mutex_lock(&wdt_mutex);
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
+ WM831X_WDOG_ENA, WM831X_WDOG_ENA);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
+ ret);
+ }
+
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm831x_wdt_stop(struct wm831x *wm831x)
+{
+ int ret;
+
+ mutex_lock(&wdt_mutex);
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
+ WM831X_WDOG_ENA, 0);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
+ ret);
+ }
+
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm831x_wdt_kick(struct wm831x *wm831x)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+
+ if (update_gpio) {
+ gpio_set_value_cansleep(update_gpio, update_state);
+ update_state = !update_state;
+ ret = 0;
+ goto out;
+ }
+
+
+ reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
+
+ if (!(reg & WM831X_WDOG_RST_SRC)) {
+ dev_err(wm831x->dev, "Hardware watchdog update unsupported\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ reg |= WM831X_WDOG_RESET;
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
+ ret);
+ }
+
+out:
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm831x_wdt_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ if (!wm831x)
+ return -ENODEV;
+
+ if (test_and_set_bit(0, &wm831x_wdt_users))
+ return -EBUSY;
+
+ ret = wm831x_wdt_start(wm831x);
+ if (ret != 0)
+ return ret;
+
+ return nonseekable_open(inode, file);
+}
+
+static int wm831x_wdt_release(struct inode *inode, struct file *file)
+{
+ if (wm831x_wdt_expect_close)
+ wm831x_wdt_stop(wm831x);
+ else {
+ dev_warn(wm831x->dev, "Watchdog device closed uncleanly\n");
+ wm831x_wdt_kick(wm831x);
+ }
+
+ clear_bit(0, &wm831x_wdt_users);
+
+ return 0;
+}
+
+static ssize_t wm831x_wdt_write(struct file *file,
+ const char __user *data, size_t count,
+ loff_t *ppos)
+{
+ size_t i;
+
+ if (count) {
+ wm831x_wdt_kick(wm831x);
+
+ if (!nowayout) {
+ /* In case it was set long ago */
+ wm831x_wdt_expect_close = 0;
+
+ /* scan to see whether or not we got the magic
+ character */
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ wm831x_wdt_expect_close = 42;
+ }
+ }
+ }
+ return count;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "WM831x Watchdog",
+};
+
+static long wm831x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY, time, i;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ u16 reg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, p);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ ret = -EINVAL;
+
+ /* Setting both simultaneously means at least one must fail */
+ if (options == WDIOS_DISABLECARD)
+ ret = wm831x_wdt_start(wm831x);
+
+ if (options == WDIOS_ENABLECARD)
+ ret = wm831x_wdt_stop(wm831x);
+ break;
+ }
+
+ case WDIOC_KEEPALIVE:
+ ret = wm831x_wdt_kick(wm831x);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, p);
+ if (ret)
+ break;
+
+ if (time == 0) {
+ if (nowayout)
+ ret = -EINVAL;
+ else
+ wm831x_wdt_stop(wm831x);
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
+ if (wm831x_wdt_cfgs[i].time == time)
+ break;
+ if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
+ ret = -EINVAL;
+ else
+ ret = wm831x_wdt_set_timeout(wm831x,
+ wm831x_wdt_cfgs[i].val);
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
+ reg &= WM831X_WDOG_TO_MASK;
+ for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
+ if (wm831x_wdt_cfgs[i].val == reg)
+ break;
+ if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) {
+ dev_warn(wm831x->dev,
+ "Unknown watchdog configuration: %x\n", reg);
+ ret = -EINVAL;
+ } else
+ ret = put_user(wm831x_wdt_cfgs[i].time, p);
+
+ }
+
+ return ret;
+}
+
+static const struct file_operations wm831x_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wm831x_wdt_write,
+ .unlocked_ioctl = wm831x_wdt_ioctl,
+ .open = wm831x_wdt_open,
+ .release = wm831x_wdt_release,
+};
+
+static struct miscdevice wm831x_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wm831x_wdt_fops,
+};
+
+static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
+{
+ struct wm831x_pdata *chip_pdata;
+ struct wm831x_watchdog_pdata *pdata;
+ int reg, ret;
+
+ if (wm831x) {
+ dev_err(&pdev->dev, "wm831x watchdog already registered\n");
+ return -EBUSY;
+ }
+
+ wm831x = dev_get_drvdata(pdev->dev.parent);
+
+ ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Failed to read watchdog status: %d\n",
+ ret);
+ goto err;
+ }
+ reg = ret;
+
+ if (reg & WM831X_WDOG_DEBUG)
+ dev_warn(wm831x->dev, "Watchdog is paused\n");
+
+ /* Apply any configuration */
+ if (pdev->dev.parent->platform_data) {
+ chip_pdata = pdev->dev.parent->platform_data;
+ pdata = chip_pdata->watchdog;
+ } else {
+ pdata = NULL;
+ }
+
+ if (pdata) {
+ reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK |
+ WM831X_WDOG_RST_SRC);
+
+ reg |= pdata->primary << WM831X_WDOG_PRIMACT_SHIFT;
+ reg |= pdata->secondary << WM831X_WDOG_SECACT_SHIFT;
+ reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT;
+
+ if (pdata->update_gpio) {
+ ret = gpio_request(pdata->update_gpio,
+ "Watchdog update");
+ if (ret < 0) {
+ dev_err(wm831x->dev,
+ "Failed to request update GPIO: %d\n",
+ ret);
+ goto err;
+ }
+
+ ret = gpio_direction_output(pdata->update_gpio, 0);
+ if (ret != 0) {
+ dev_err(wm831x->dev,
+ "gpio_direction_output returned: %d\n",
+ ret);
+ goto err_gpio;
+ }
+
+ update_gpio = pdata->update_gpio;
+
+ /* Make sure the watchdog takes hardware updates */
+ reg |= WM831X_WDOG_RST_SRC;
+ }
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret == 0) {
+ ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
+ wm831x_reg_lock(wm831x);
+ } else {
+ dev_err(wm831x->dev,
+ "Failed to unlock security key: %d\n", ret);
+ goto err_gpio;
+ }
+ }
+
+ wm831x_wdt_miscdev.parent = &pdev->dev;
+
+ ret = misc_register(&wm831x_wdt_miscdev);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to register miscdev: %d\n", ret);
+ goto err_gpio;
+ }
+
+ return 0;
+
+err_gpio:
+ if (update_gpio) {
+ gpio_free(update_gpio);
+ update_gpio = 0;
+ }
+err:
+ return ret;
+}
+
+static int __devexit wm831x_wdt_remove(struct platform_device *pdev)
+{
+ if (update_gpio) {
+ gpio_free(update_gpio);
+ update_gpio = 0;
+ }
+
+ misc_deregister(&wm831x_wdt_miscdev);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_wdt_driver = {
+ .probe = wm831x_wdt_probe,
+ .remove = __devexit_p(wm831x_wdt_remove),
+ .driver = {
+ .name = "wm831x-watchdog",
+ },
+};
+
+static int __init wm831x_wdt_init(void)
+{
+ return platform_driver_register(&wm831x_wdt_driver);
+}
+module_init(wm831x_wdt_init);
+
+static void __exit wm831x_wdt_exit(void)
+{
+ platform_driver_unregister(&wm831x_wdt_driver);
+}
+module_exit(wm831x_wdt_exit);
+
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("WM831x Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-watchdog");
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
new file mode 100644
index 00000000..b68d928c
--- /dev/null
+++ b/drivers/watchdog/wm8350_wdt.c
@@ -0,0 +1,329 @@
+/*
+ * Watchdog driver for the wm8350
+ *
+ * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/wm8350/core.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned long wm8350_wdt_users;
+static struct miscdevice wm8350_wdt_miscdev;
+static int wm8350_wdt_expect_close;
+static DEFINE_MUTEX(wdt_mutex);
+
+static struct {
+ int time; /* Seconds */
+ u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */
+} wm8350_wdt_cfgs[] = {
+ { 1, 0x02 },
+ { 2, 0x04 },
+ { 4, 0x05 },
+};
+
+static struct wm8350 *get_wm8350(void)
+{
+ return dev_get_drvdata(wm8350_wdt_miscdev.parent);
+}
+
+static int wm8350_wdt_set_timeout(struct wm8350 *wm8350, u16 value)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+ wm8350_reg_unlock(wm8350);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= ~WM8350_WDOG_TO_MASK;
+ reg |= value;
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ wm8350_reg_lock(wm8350);
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_start(struct wm8350 *wm8350)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+ wm8350_reg_unlock(wm8350);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= ~WM8350_WDOG_MODE_MASK;
+ reg |= 0x20;
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ wm8350_reg_lock(wm8350);
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_stop(struct wm8350 *wm8350)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+ wm8350_reg_unlock(wm8350);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= ~WM8350_WDOG_MODE_MASK;
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ wm8350_reg_lock(wm8350);
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_kick(struct wm8350 *wm8350)
+{
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_open(struct inode *inode, struct file *file)
+{
+ struct wm8350 *wm8350 = get_wm8350();
+ int ret;
+
+ if (!wm8350)
+ return -ENODEV;
+
+ if (test_and_set_bit(0, &wm8350_wdt_users))
+ return -EBUSY;
+
+ ret = wm8350_wdt_start(wm8350);
+ if (ret != 0)
+ return ret;
+
+ return nonseekable_open(inode, file);
+}
+
+static int wm8350_wdt_release(struct inode *inode, struct file *file)
+{
+ struct wm8350 *wm8350 = get_wm8350();
+
+ if (wm8350_wdt_expect_close)
+ wm8350_wdt_stop(wm8350);
+ else {
+ dev_warn(wm8350->dev, "Watchdog device closed uncleanly\n");
+ wm8350_wdt_kick(wm8350);
+ }
+
+ clear_bit(0, &wm8350_wdt_users);
+
+ return 0;
+}
+
+static ssize_t wm8350_wdt_write(struct file *file,
+ const char __user *data, size_t count,
+ loff_t *ppos)
+{
+ struct wm8350 *wm8350 = get_wm8350();
+ size_t i;
+
+ if (count) {
+ wm8350_wdt_kick(wm8350);
+
+ if (!nowayout) {
+ /* In case it was set long ago */
+ wm8350_wdt_expect_close = 0;
+
+ /* scan to see whether or not we got the magic
+ character */
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ wm8350_wdt_expect_close = 42;
+ }
+ }
+ }
+ return count;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "WM8350 Watchdog",
+};
+
+static long wm8350_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct wm8350 *wm8350 = get_wm8350();
+ int ret = -ENOTTY, time, i;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ u16 reg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, p);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ ret = -EINVAL;
+
+ /* Setting both simultaneously means at least one must fail */
+ if (options == WDIOS_DISABLECARD)
+ ret = wm8350_wdt_start(wm8350);
+
+ if (options == WDIOS_ENABLECARD)
+ ret = wm8350_wdt_stop(wm8350);
+ break;
+ }
+
+ case WDIOC_KEEPALIVE:
+ ret = wm8350_wdt_kick(wm8350);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, p);
+ if (ret)
+ break;
+
+ if (time == 0) {
+ if (nowayout)
+ ret = -EINVAL;
+ else
+ wm8350_wdt_stop(wm8350);
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
+ if (wm8350_wdt_cfgs[i].time == time)
+ break;
+ if (i == ARRAY_SIZE(wm8350_wdt_cfgs))
+ ret = -EINVAL;
+ else
+ ret = wm8350_wdt_set_timeout(wm8350,
+ wm8350_wdt_cfgs[i].val);
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= WM8350_WDOG_TO_MASK;
+ for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
+ if (wm8350_wdt_cfgs[i].val == reg)
+ break;
+ if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) {
+ dev_warn(wm8350->dev,
+ "Unknown watchdog configuration: %x\n", reg);
+ ret = -EINVAL;
+ } else
+ ret = put_user(wm8350_wdt_cfgs[i].time, p);
+
+ }
+
+ return ret;
+}
+
+static const struct file_operations wm8350_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wm8350_wdt_write,
+ .unlocked_ioctl = wm8350_wdt_ioctl,
+ .open = wm8350_wdt_open,
+ .release = wm8350_wdt_release,
+};
+
+static struct miscdevice wm8350_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wm8350_wdt_fops,
+};
+
+static int __devinit wm8350_wdt_probe(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+
+ if (!wm8350) {
+ pr_err("No driver data supplied\n");
+ return -ENODEV;
+ }
+
+ /* Default to 4s timeout */
+ wm8350_wdt_set_timeout(wm8350, 0x05);
+
+ wm8350_wdt_miscdev.parent = &pdev->dev;
+
+ return misc_register(&wm8350_wdt_miscdev);
+}
+
+static int __devexit wm8350_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&wm8350_wdt_miscdev);
+
+ return 0;
+}
+
+static struct platform_driver wm8350_wdt_driver = {
+ .probe = wm8350_wdt_probe,
+ .remove = __devexit_p(wm8350_wdt_remove),
+ .driver = {
+ .name = "wm8350-wdt",
+ },
+};
+
+static int __init wm8350_wdt_init(void)
+{
+ return platform_driver_register(&wm8350_wdt_driver);
+}
+module_init(wm8350_wdt_init);
+
+static void __exit wm8350_wdt_exit(void)
+{
+ platform_driver_unregister(&wm8350_wdt_driver);
+}
+module_exit(wm8350_wdt_exit);
+
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("WM8350 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-wdt");
diff --git a/drivers/watchdog/xen_wdt.c b/drivers/watchdog/xen_wdt.c
new file mode 100644
index 00000000..49bd9d39
--- /dev/null
+++ b/drivers/watchdog/xen_wdt.c
@@ -0,0 +1,359 @@
+/*
+ * Xen Watchdog Driver
+ *
+ * (c) Copyright 2010 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define DRV_NAME "wdt"
+#define DRV_VERSION "0.01"
+#define PFX DRV_NAME ": "
+
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#include <xen/xen.h>
+#include <asm/xen/hypercall.h>
+#include <xen/interface/sched.h>
+
+static struct platform_device *platform_device;
+static DEFINE_SPINLOCK(wdt_lock);
+static struct sched_watchdog wdt;
+static __kernel_time_t wdt_expires;
+static bool is_active, expect_release;
+
+#define WATCHDOG_TIMEOUT 60 /* in seconds */
+static unsigned int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, uint, S_IRUGO);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
+ "(default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, S_IRUGO);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static inline __kernel_time_t set_timeout(void)
+{
+ wdt.timeout = timeout;
+ return ktime_to_timespec(ktime_get()).tv_sec + timeout;
+}
+
+static int xen_wdt_start(void)
+{
+ __kernel_time_t expires;
+ int err;
+
+ spin_lock(&wdt_lock);
+
+ expires = set_timeout();
+ if (!wdt.id)
+ err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
+ else
+ err = -EBUSY;
+ if (err > 0) {
+ wdt.id = err;
+ wdt_expires = expires;
+ err = 0;
+ } else
+ BUG_ON(!err);
+
+ spin_unlock(&wdt_lock);
+
+ return err;
+}
+
+static int xen_wdt_stop(void)
+{
+ int err = 0;
+
+ spin_lock(&wdt_lock);
+
+ wdt.timeout = 0;
+ if (wdt.id)
+ err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
+ if (!err)
+ wdt.id = 0;
+
+ spin_unlock(&wdt_lock);
+
+ return err;
+}
+
+static int xen_wdt_kick(void)
+{
+ __kernel_time_t expires;
+ int err;
+
+ spin_lock(&wdt_lock);
+
+ expires = set_timeout();
+ if (wdt.id)
+ err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
+ else
+ err = -ENXIO;
+ if (!err)
+ wdt_expires = expires;
+
+ spin_unlock(&wdt_lock);
+
+ return err;
+}
+
+static int xen_wdt_open(struct inode *inode, struct file *file)
+{
+ int err;
+
+ /* /dev/watchdog can only be opened once */
+ if (xchg(&is_active, true))
+ return -EBUSY;
+
+ err = xen_wdt_start();
+ if (err == -EBUSY)
+ err = xen_wdt_kick();
+ return err ?: nonseekable_open(inode, file);
+}
+
+static int xen_wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_release)
+ xen_wdt_stop();
+ else {
+ printk(KERN_CRIT PFX
+ "unexpected close, not stopping watchdog!\n");
+ xen_wdt_kick();
+ }
+ is_active = false;
+ expect_release = false;
+ return 0;
+}
+
+static ssize_t xen_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* See if we got the magic character 'V' and reload the timer */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* in case it was set long ago */
+ expect_release = false;
+
+ /* scan to see whether or not we got the magic
+ character */
+ for (i = 0; i != len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_release = true;
+ }
+ }
+
+ /* someone wrote to us, we should reload the timer */
+ xen_wdt_kick();
+ }
+ return len;
+}
+
+static long xen_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_options, retval = -EINVAL;
+ int new_timeout;
+ int __user *argp = (void __user *)arg;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | 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, argp);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, argp))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD)
+ retval = xen_wdt_stop();
+ if (new_options & WDIOS_ENABLECARD) {
+ retval = xen_wdt_start();
+ if (retval == -EBUSY)
+ retval = xen_wdt_kick();
+ }
+ return retval;
+
+ case WDIOC_KEEPALIVE:
+ xen_wdt_kick();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, argp))
+ return -EFAULT;
+ if (!new_timeout)
+ return -EINVAL;
+ timeout = new_timeout;
+ xen_wdt_kick();
+ /* fall through */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, argp);
+
+ case WDIOC_GETTIMELEFT:
+ retval = wdt_expires - ktime_to_timespec(ktime_get()).tv_sec;
+ return put_user(retval, argp);
+ }
+
+ return -ENOTTY;
+}
+
+static const struct file_operations xen_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = xen_wdt_write,
+ .unlocked_ioctl = xen_wdt_ioctl,
+ .open = xen_wdt_open,
+ .release = xen_wdt_release,
+};
+
+static struct miscdevice xen_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &xen_wdt_fops,
+};
+
+static int __devinit xen_wdt_probe(struct platform_device *dev)
+{
+ struct sched_watchdog wd = { .id = ~0 };
+ int ret = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wd);
+
+ switch (ret) {
+ case -EINVAL:
+ if (!timeout) {
+ timeout = WATCHDOG_TIMEOUT;
+ printk(KERN_INFO PFX
+ "timeout value invalid, using %d\n", timeout);
+ }
+
+ ret = misc_register(&xen_wdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (%d)\n",
+ WATCHDOG_MINOR, ret);
+ break;
+ }
+
+ printk(KERN_INFO PFX
+ "initialized (timeout=%ds, nowayout=%d)\n",
+ timeout, nowayout);
+ break;
+
+ case -ENOSYS:
+ printk(KERN_INFO PFX "not supported\n");
+ ret = -ENODEV;
+ break;
+
+ default:
+ printk(KERN_INFO PFX "bogus return value %d\n", ret);
+ break;
+ }
+
+ return ret;
+}
+
+static int __devexit xen_wdt_remove(struct platform_device *dev)
+{
+ /* Stop the timer before we leave */
+ if (!nowayout)
+ xen_wdt_stop();
+
+ misc_deregister(&xen_wdt_miscdev);
+
+ return 0;
+}
+
+static void xen_wdt_shutdown(struct platform_device *dev)
+{
+ xen_wdt_stop();
+}
+
+static int xen_wdt_suspend(struct platform_device *dev, pm_message_t state)
+{
+ return xen_wdt_stop();
+}
+
+static int xen_wdt_resume(struct platform_device *dev)
+{
+ return xen_wdt_start();
+}
+
+static struct platform_driver xen_wdt_driver = {
+ .probe = xen_wdt_probe,
+ .remove = __devexit_p(xen_wdt_remove),
+ .shutdown = xen_wdt_shutdown,
+ .suspend = xen_wdt_suspend,
+ .resume = xen_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ },
+};
+
+static int __init xen_wdt_init_module(void)
+{
+ int err;
+
+ if (!xen_domain())
+ return -ENODEV;
+
+ printk(KERN_INFO PFX "Xen WatchDog Timer Driver v%s\n", DRV_VERSION);
+
+ err = platform_driver_register(&xen_wdt_driver);
+ if (err)
+ return err;
+
+ platform_device = platform_device_register_simple(DRV_NAME,
+ -1, NULL, 0);
+ if (IS_ERR(platform_device)) {
+ err = PTR_ERR(platform_device);
+ platform_driver_unregister(&xen_wdt_driver);
+ }
+
+ return err;
+}
+
+static void __exit xen_wdt_cleanup_module(void)
+{
+ platform_device_unregister(platform_device);
+ platform_driver_unregister(&xen_wdt_driver);
+ printk(KERN_INFO PFX "module unloaded\n");
+}
+
+module_init(xen_wdt_init_module);
+module_exit(xen_wdt_cleanup_module);
+
+MODULE_AUTHOR("Jan Beulich <jbeulich@novell.com>");
+MODULE_DESCRIPTION("Xen WatchDog Timer Driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);