diff options
Diffstat (limited to 'drivers/watchdog')
126 files changed, 10288 insertions, 8916 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index df9e8f0e327..76dd54122f7 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -55,6 +55,7 @@ comment "Watchdog Device Drivers" config SOFT_WATCHDOG tristate "Software watchdog" + select WATCHDOG_CORE help A software monitoring watchdog. This will fail to reboot your system from some situations that the hardware watchdog will recover @@ -63,6 +64,37 @@ config SOFT_WATCHDOG To compile this driver as a module, choose M here: the module will be called softdog. +config DA9052_WATCHDOG + tristate "Dialog DA9052 Watchdog" + depends on PMIC_DA9052 + select WATCHDOG_CORE + help + Support for the watchdog in the DA9052 PMIC. Watchdog trigger + cause system reset. + + Say Y here to include support for the DA9052 watchdog. + Alternatively say M to compile the driver as a module, + which will be called da9052_wdt. + +config DA9055_WATCHDOG + tristate "Dialog Semiconductor DA9055 Watchdog" + depends on MFD_DA9055 + select WATCHDOG_CORE + help + If you say yes here you get support for watchdog on the Dialog + Semiconductor DA9055 PMIC. + + This driver can also be built as a module. If so, the module + will be called da9055_wdt. + +config GPIO_WATCHDOG + tristate "Watchdog device controlled through GPIO-line" + depends on OF_GPIO + select WATCHDOG_CORE + help + If you say yes here you get support for watchdog device + controlled through GPIO-line. + config WM831X_WATCHDOG tristate "WM831x watchdog" depends on MFD_WM831X @@ -74,17 +106,28 @@ config WM831X_WATCHDOG config WM8350_WATCHDOG tristate "WM8350 watchdog" depends on MFD_WM8350 + select WATCHDOG_CORE help Support for the watchdog in the WM8350 AudioPlus PMIC. When the watchdog triggers the system will be reset. +config XILINX_WATCHDOG + tristate "Xilinx Watchdog timer" + select WATCHDOG_CORE + help + Watchdog driver for the xps_timebase_wdt ip core. + + To compile this driver as a module, choose M here: the + module will be called of_xilinx_wdt. + # ALPHA Architecture # ARM Architecture config ARM_SP805_WATCHDOG tristate "ARM SP805 Watchdog" - depends on ARM_AMBA + depends on (ARM || ARM64) && ARM_AMBA + select WATCHDOG_CORE help ARM Primecell SP805 Watchdog timer. This will reboot your system when the timeout is reached. @@ -99,6 +142,7 @@ config AT91RM9200_WATCHDOG config AT91SAM9X_WATCHDOG tristate "AT91SAM9X / AT91CAP9 watchdog" depends on ARCH_AT91 && !ARCH_AT91RM9200 + select WATCHDOG_CORE help Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will reboot your system when the timeout is reached. @@ -127,17 +171,6 @@ config 977_WATCHDOG 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 @@ -170,8 +203,9 @@ config HAVE_S3C2410_WATCHDOG config S3C2410_WATCHDOG tristate "S3C2410 Watchdog" - depends on ARCH_S3C2410 || HAVE_S3C2410_WATCHDOG + depends on HAVE_S3C2410_WATCHDOG select WATCHDOG_CORE + select MFD_SYSCON if ARCH_EXYNOS5 help Watchdog timer block in the Samsung SoCs. This will reboot the system when the timer expires with the watchdog enabled. @@ -198,25 +232,17 @@ config SA1100_WATCHDOG config DW_WATCHDOG tristate "Synopsys DesignWare watchdog" - depends on ARM && HAVE_CLK + depends on HAS_IOMEM help Say Y here if to include support for the Synopsys DesignWare - watchdog timer found in many ARM chips. + watchdog timer found in many chips. To compile this driver as a module, choose M here: the module will be called dw_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 + select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer embedded in the Cirrus Logic EP93xx family of devices. @@ -227,16 +253,18 @@ config EP93XX_WATCHDOG config OMAP_WATCHDOG tristate "OMAP Watchdog" depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS + select WATCHDOG_CORE 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 + tristate "LPC32XX Watchdog" + depends on ARCH_LPC32XX + select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer - in the PNX4008 or LPC32XX processor. + in the LPC32XX processor. This driver can be built as a module by choosing M. The module will be called pnx4008_wdt. @@ -244,7 +272,7 @@ config PNX4008_WATCHDOG config IOP_WATCHDOG tristate "IOP Watchdog" - depends on PLAT_IOP + depends on ARCH_IOP13XX select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X) help Say Y here if to include support for the watchdog timer @@ -260,10 +288,11 @@ config IOP_WATCHDOG config DAVINCI_WATCHDOG tristate "DaVinci watchdog" - depends on ARCH_DAVINCI + depends on ARCH_DAVINCI || ARCH_KEYSTONE + select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer - in the DaVinci DM644x/DM646x processors. + in the DaVinci DM644x/DM646x or Keystone processors. To compile this driver as a module, choose M here: the module will be called davinci_wdt. @@ -272,17 +301,29 @@ config DAVINCI_WATCHDOG config ORION_WATCHDOG tristate "Orion watchdog" - depends on ARCH_ORION5X || ARCH_KIRKWOOD + depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU + select WATCHDOG_CORE 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 SUNXI_WATCHDOG + tristate "Allwinner SoCs watchdog support" + depends on ARCH_SUNXI + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + in Allwinner SoCs. + To compile this driver as a module, choose M here: the + module will be called sunxi_wdt. + config COH901327_WATCHDOG bool "ST-Ericsson COH 901 327 watchdog" depends on ARCH_U300 default y if MACH_U300 + select WATCHDOG_CORE help Say Y here to include Watchdog timer support for the watchdog embedded into the ST-Ericsson U300 series platforms. @@ -292,18 +333,20 @@ config COH901327_WATCHDOG config TWL4030_WATCHDOG tristate "TWL4030 Watchdog" depends on TWL4030_CORE + select WATCHDOG_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 +config STMP3XXX_RTC_WATCHDOG + tristate "Freescale STMP3XXX & i.MX23/28 watchdog" + depends on RTC_DRV_STMP + select WATCHDOG_CORE help - Say Y here if to include support for the watchdog timer - for the Sigmatel STMP37XX/378X SoC. + Say Y here to include support for the watchdog timer inside + the RTC for the STMP37XX/378X or i.MX23/28 SoC. To compile this driver as a module, choose M here: the - module will be called stmp3xxx_wdt. + module will be called stmp3xxx_rtc_wdt. config NUC900_WATCHDOG tristate "Nuvoton NUC900 watchdog" @@ -328,12 +371,15 @@ config TS72XX_WATCHDOG config MAX63XX_WATCHDOG tristate "Max63xx watchdog" depends on ARM && HAS_IOMEM + select WATCHDOG_CORE help Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. config IMX2_WDT tristate "IMX2+ Watchdog" - depends on IMX_HAVE_PLATFORM_IMX2_WDT + depends on ARCH_MXC + select REGMAP_MMIO + select WATCHDOG_CORE help This is the driver for the hardware watchdog on the Freescale IMX2 and later processors. @@ -343,6 +389,60 @@ config IMX2_WDT To compile this driver as a module, choose M here: the module will be called imx2_wdt. +config UX500_WATCHDOG + tristate "ST-Ericsson Ux500 watchdog" + depends on MFD_DB8500_PRCMU + select WATCHDOG_CORE + default y + help + Say Y here to include Watchdog timer support for the watchdog + existing in the prcmu of ST-Ericsson Ux500 series platforms. + + To compile this driver as a module, choose M here: the + module will be called ux500_wdt. + +config RETU_WATCHDOG + tristate "Retu watchdog" + depends on MFD_RETU + select WATCHDOG_CORE + help + Retu watchdog driver for Nokia Internet Tablets (770, N800, + N810). At least on N800 the watchdog cannot be disabled, so + this driver is essential and you should enable it. + + To compile this driver as a module, choose M here: the + module will be called retu_wdt. + +config MOXART_WDT + tristate "MOXART watchdog" + depends on ARCH_MOXART + help + Say Y here to include Watchdog timer support for the watchdog + existing on the MOXA ART SoC series platforms. + + To compile this driver as a module, choose M here: the + module will be called moxart_wdt. + +config SIRFSOC_WATCHDOG + tristate "SiRFSOC watchdog" + depends on ARCH_SIRF + select WATCHDOG_CORE + default y + help + Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When + the watchdog triggers the system will be reset. + +config TEGRA_WATCHDOG + tristate "Tegra watchdog" + depends on ARCH_TEGRA || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + embedded in NVIDIA Tegra SoCs. + + To compile this driver as a module, choose M here: the + module will be called tegra_wdt. + # AVR32 Architecture config AT32AP700X_WDT @@ -369,8 +469,6 @@ config BFIN_WDT # FRV Architecture -# H8300 Architecture - # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT @@ -422,7 +520,7 @@ config ALIM7101_WDT config F71808E_WDT tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" - depends on X86 && EXPERIMENTAL + depends on X86 help This is the driver for the hardware watchdog on the Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers. @@ -457,7 +555,7 @@ config GEODE_WDT config SC520_WDT tristate "AMD Elan SC520 processor Watchdog" - depends on X86 + depends on MELAN help This is the driver for the hardware watchdog built in to the AMD "Elan" SC520 microcomputer commonly used in embedded systems. @@ -537,7 +635,7 @@ config WAFER_WDT config I6300ESB_WDT tristate "Intel 6300ESB Timer/Watchdog" - depends on X86 && PCI + depends on PCI ---help--- Hardware driver for the watchdog timer built into the Intel 6300ESB controller hub. @@ -545,18 +643,46 @@ config I6300ESB_WDT To compile this driver as a module, choose M here: the module will be called i6300esb. +config IE6XX_WDT + tristate "Intel Atom E6xx Watchdog" + depends on X86 && PCI + select WATCHDOG_CORE + select MFD_CORE + select LPC_SCH + ---help--- + Hardware driver for the watchdog timer built into the Intel + Atom E6XX (TunnelCreek) processor. + + To compile this driver as a module, choose M here: the + module will be called ie6xx_wdt. + config INTEL_SCU_WATCHDOG bool "Intel SCU Watchdog for Mobile Platforms" - depends on X86_MRST + depends on X86_INTEL_MID ---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 INTEL_MID_WATCHDOG + tristate "Intel MID Watchdog Timer" + depends on X86_INTEL_MID + select WATCHDOG_CORE + ---help--- + Watchdog timer driver built into the Intel SCU for Intel MID + Platforms. + + This driver currently supports only the watchdog evolution + implementation in SCU, available for Merrifield generation. + + To compile this driver as a module, choose M here. + config ITCO_WDT tristate "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCI + select WATCHDOG_CORE + select LPC_ICH ---help--- Hardware driver for the intel TCO timer based watchdog devices. These drivers are included in the Intel 82801 I/O Controller @@ -598,10 +724,15 @@ config IT8712F_WDT config IT87_WDT tristate "IT87 Watchdog Timer" - depends on X86 && EXPERIMENTAL + depends on X86 ---help--- This is the driver for the hardware watchdog on the ITE IT8702, - IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 Super I/O chips. + IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728 + Super I/O chips. + + If the driver does not work, then make sure that the game port in + the BIOS is enabled. + 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. @@ -619,6 +750,17 @@ config HP_WATCHDOG To compile this driver as a module, choose M here: the module will be called hpwdt. +config KEMPLD_WDT + tristate "Kontron COM Watchdog Timer" + depends on MFD_KEMPLD + select WATCHDOG_CORE + help + Support for the PLD watchdog on some Kontron ETX and COMexpress + (ETXexpress) modules + + This driver can also be built as a module. If so, the module will be + called kempld_wdt. + config HPWDT_NMI_DECODING bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer" depends on HP_WATCHDOG @@ -708,7 +850,7 @@ config 60XX_WDT config SBC8360_WDT tristate "SBC8360 Watchdog Timer" - depends on X86 + depends on X86_32 ---help--- This is the driver for the hardware watchdog on the SBC8360 Single @@ -774,7 +916,7 @@ config SMSC37B787_WDT config VIA_WDT tristate "VIA Watchdog Timer" - depends on X86 + depends on X86 && PCI select WATCHDOG_CORE ---help--- This is the driver for the hardware watchdog timer on VIA @@ -786,48 +928,28 @@ config VIA_WDT Most people will say N. config W83627HF_WDT - tristate "W83627HF/W83627DHG Watchdog Timer" + tristate "Watchdog timer for W83627HF/W83627DHG and compatibles" depends on X86 + select WATCHDOG_CORE ---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. + This is the driver for the hardware watchdog on the following + Super I/O chips. + W83627DHG/DHG-P/EHF/EHG/F/G/HF/S/SF/THF/UHG/UG + W83637HF + W83667HG/HG-B + W83687THF + W83697HF + W83697UG + NCT6775 + NCT6776 + NCT6779 -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. + module will be called w83627hf_wdt. Most people will say N. @@ -906,18 +1028,6 @@ config M54xx_WATCHDOG # MicroBlaze Architecture -config XILINX_WATCHDOG - tristate "Xilinx Watchdog timer" - depends on MICROBLAZE - ---help--- - Watchdog driver for the xps_timebase_wdt ip core. - - IMPORTANT: The xps_timebase_wdt parent must have the property - "clock-frequency" at device tree. - - To compile this driver as a module, choose M here: the - module will be called of_xilinx_wdt. - # MIPS Architecture config ATH79_WDT @@ -930,8 +1040,9 @@ config ATH79_WDT config BCM47XX_WDT tristate "Broadcom BCM47xx Watchdog Timer" depends on BCM47XX + select WATCHDOG_CORE help - Hardware driver for the Broadcom BCM47xx Watchog Timer. + Hardware driver for the Broadcom BCM47xx Watchdog Timer. config RC32434_WDT tristate "IDT RC32434 SoC Watchdog Timer" @@ -955,6 +1066,7 @@ config INDYDOG config JZ4740_WDT tristate "Ingenic jz4740 SoC hardware watchdog" depends on MACH_JZ4740 + select WATCHDOG_CORE help Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs. @@ -996,12 +1108,13 @@ config AR7_WDT config TXX9_WDT tristate "Toshiba TXx9 Watchdog Timer" depends on CPU_TX39XX || CPU_TX49XX + select WATCHDOG_CORE 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 + depends on CAVIUM_OCTEON_SOC default y select EXPORT_UASM if OCTEON_WDT = m help @@ -1027,19 +1140,59 @@ config BCM63XX_WDT To compile this driver as a loadable module, choose M here. The module will be called bcm63xx_wdt. +config BCM2835_WDT + tristate "Broadcom BCM2835 hardware watchdog" + depends on ARCH_BCM2835 + select WATCHDOG_CORE + help + Watchdog driver for the built in watchdog hardware in Broadcom + BCM2835 SoC. + + To compile this driver as a loadable module, choose M here. + The module will be called bcm2835_wdt. + +config BCM_KONA_WDT + tristate "BCM Kona Watchdog" + depends on ARCH_BCM_MOBILE + select WATCHDOG_CORE + help + Support for the watchdog timer on the following Broadcom BCM281xx + family, which includes BCM11130, BCM11140, BCM11351, BCM28145 and + BCM28155 variants. + + Say 'Y' or 'M' here to enable the driver. The module will be called + bcm_kona_wdt. + +config BCM_KONA_WDT_DEBUG + bool "DEBUGFS support for BCM Kona Watchdog" + depends on BCM_KONA_WDT + help + If enabled, adds /sys/kernel/debug/bcm_kona_wdt/info which provides + access to the driver's internal data structures as well as watchdog + timer hardware registres. + + If in doubt, say 'N'. + config LANTIQ_WDT tristate "Lantiq SoC watchdog" depends on LANTIQ help Hardware driver for the Lantiq SoC Watchdog Timer. +config RALINK_WDT + tristate "Ralink SoC watchdog" + select WATCHDOG_CORE + depends on RALINK + help + Hardware driver for the Ralink SoC Watchdog Timer. + # PARISC Architecture # POWERPC Architecture config GEF_WDT tristate "GE Watchdog Timer" - depends on GEF_SBC610 || GEF_SBC310 || GEF_PPC9A + depends on GE_FPGA ---help--- Watchdog timer found in a number of GE single board computers. @@ -1052,6 +1205,7 @@ config MPC5200_WDT config 8xxx_WDT tristate "MPC8xxx Platform Watchdog Timer" depends on PPC_8xx || PPC_83xx || PPC_86xx + select WATCHDOG_CORE help This driver is for a SoC level watchdog that exists on some Freescale PowerPC processors. So far this driver supports: @@ -1076,6 +1230,7 @@ config PIKA_WDT config BOOKE_WDT tristate "PowerPC Book-E Watchdog Timer" depends on BOOKE || 4xx + select WATCHDOG_CORE ---help--- Watchdog driver for PowerPC Book-E chips, such as the Freescale MPC85xx SOCs and the IBM PowerPC 440. @@ -1086,10 +1241,10 @@ config BOOKE_WDT 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 + default 38 if PPC_FSL_BOOK3E + range 0 63 if PPC_FSL_BOOK3E + default 3 if !PPC_FSL_BOOK3E + range 0 3 if !PPC_FSL_BOOK3E help Select the default watchdog timer period to be used by the PowerPC Book-E watchdog driver. A watchdog "event" occurs when the bit @@ -1100,6 +1255,18 @@ config BOOKE_WDT_DEFAULT_TIMEOUT The value can be overridden by the wdt_period command-line parameter. +config MEN_A21_WDT + tristate "MEN A21 VME CPU Carrier Board Watchdog Timer" + select WATCHDOG_CORE + depends on GPIOLIB + help + Watchdog driver for MEN A21 VMEbus CPU Carrier Boards. + + The driver can also be built as a module. If so, the module will be + called mena21_wdt. + + If unsure select N here. + # PPC64 Architecture config WATCHDOG_RTAS @@ -1113,14 +1280,17 @@ config WATCHDOG_RTAS # S390 Architecture -config ZVM_WATCHDOG - tristate "z/VM Watchdog Timer" +config DIAG288_WATCHDOG + tristate "System z diag288 Watchdog" depends on S390 + select WATCHDOG_CORE 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. + LPAR provides a very similar interface. This driver handles + both. To compile this driver as a module, choose M here. The module will be called vmwatchdog. @@ -1130,6 +1300,7 @@ config ZVM_WATCHDOG config SH_WDT tristate "SuperH Watchdog" depends on SUPERH && (CPU_SH3 || CPU_SH4) + select WATCHDOG_CORE help This driver adds watchdog support for the integrated watchdog in the SuperH processors. If you have one of these processors and wish diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index e8f479a1640..468c3204c3b 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -36,23 +36,29 @@ 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_DW_WATCHDOG) += dw_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_SUNXI_WATCHDOG) += sunxi_wdt.o obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o -obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o +obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o +obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o +obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o +obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o +obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o +obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o +obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o +obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o @@ -64,8 +70,6 @@ obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o # FRV Architecture -# H8300 Architecture - # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o @@ -81,6 +85,7 @@ 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_IE6XX_WDT) += ie6xx_wdt.o obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y) obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o @@ -88,6 +93,7 @@ endif obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o obj-$(CONFIG_IT87_WDT) += it87_wdt.o obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o +obj-$(CONFIG_KEMPLD_WDT) += kempld_wdt.o obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o @@ -101,13 +107,12 @@ obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o obj-$(CONFIG_VIA_WDT) += via_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 +obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o # M32R Architecture @@ -132,6 +137,7 @@ 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 +obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o # PARISC Architecture @@ -141,11 +147,13 @@ 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 +obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o # PPC64 Architecture obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o # S390 Architecture +obj-$(CONFIG_DIAG288_WATCHDOG) += diag288_wdt.o # SUPERH (sh + sh64) Architecture obj-$(CONFIG_SH_WDT) += shwdt.o @@ -163,6 +171,9 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o obj-$(CONFIG_XEN_WDT) += xen_wdt.o # Architecture Independent +obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o +obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o +obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o diff --git a/drivers/watchdog/acquirewdt.c b/drivers/watchdog/acquirewdt.c index b6a2b58cbe6..5614416f103 100644 --- a/drivers/watchdog/acquirewdt.c +++ b/drivers/watchdog/acquirewdt.c @@ -52,14 +52,15 @@ * Includes, defines, variables, module parameters, ... */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + /* 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/miscdevice.h> /* For struct miscdevice */ #include <linux/watchdog.h> /* For the watchdog specific items */ #include <linux/fs.h> /* For file operations */ #include <linux/ioport.h> /* For io-port access */ @@ -70,7 +71,6 @@ /* 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 @@ -92,8 +92,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -208,8 +208,7 @@ 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"); + pr_crit("Unexpected close, not stopping watchdog!\n"); acq_keepalive(); } clear_bit(0, &acq_is_open); @@ -240,33 +239,30 @@ static struct miscdevice acq_miscdev = { * Init & exit routines */ -static int __devinit acq_probe(struct platform_device *dev) +static int __init 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); + pr_err("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); + pr_err("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); + pr_err("cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); goto unreg_regions; } - printk(KERN_INFO PFX "initialized. (nowayout=%d)\n", nowayout); + pr_info("initialized. (nowayout=%d)\n", nowayout); return 0; unreg_regions: @@ -278,7 +274,7 @@ out: return ret; } -static int __devexit acq_remove(struct platform_device *dev) +static int acq_remove(struct platform_device *dev) { misc_deregister(&acq_miscdev); release_region(wdt_start, 1); @@ -295,8 +291,7 @@ static void acq_shutdown(struct platform_device *dev) } static struct platform_driver acquirewdt_driver = { - .probe = acq_probe, - .remove = __devexit_p(acq_remove), + .remove = acq_remove, .shutdown = acq_shutdown, .driver = { .owner = THIS_MODULE, @@ -308,23 +303,20 @@ 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; + pr_info("WDT driver for Acquire single board computer initialising\n"); 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; - } + if (IS_ERR(acq_platform_device)) + return PTR_ERR(acq_platform_device); + + err = platform_driver_probe(&acquirewdt_driver, acq_probe); + if (err) + goto unreg_platform_device; return 0; -unreg_platform_driver: - platform_driver_unregister(&acquirewdt_driver); +unreg_platform_device: + platform_device_unregister(acq_platform_device); return err; } @@ -332,7 +324,7 @@ 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"); + pr_info("Watchdog Module Unloaded\n"); } module_init(acq_init); @@ -341,4 +333,3 @@ 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 index 4d40965d2c9..7796db7fa6e 100644 --- a/drivers/watchdog/advantechwdt.c +++ b/drivers/watchdog/advantechwdt.c @@ -28,6 +28,8 @@ * add wdt_start and wdt_stop as parameters. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -40,10 +42,8 @@ #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 */ @@ -77,8 +77,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -207,8 +207,7 @@ 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"); + pr_crit("Unexpected close, not stopping watchdog!\n"); advwdt_ping(); } clear_bit(0, &advwdt_is_open); @@ -239,24 +238,21 @@ static struct miscdevice advwdt_miscdev = { * Init & exit routines */ -static int __devinit advwdt_probe(struct platform_device *dev) +static int __init 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); + pr_err("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); + pr_err("I/O address 0x%04x already in use\n", wdt_start); ret = -EIO; goto unreg_stop; } @@ -265,18 +261,16 @@ static int __devinit advwdt_probe(struct platform_device *dev) * 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); + pr_info("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); + pr_err("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", + pr_info("initialized. timeout=%d sec (nowayout=%d)\n", timeout, nowayout); out: return ret; @@ -288,7 +282,7 @@ unreg_stop: goto out; } -static int __devexit advwdt_remove(struct platform_device *dev) +static int advwdt_remove(struct platform_device *dev) { misc_deregister(&advwdt_miscdev); release_region(wdt_start, 1); @@ -305,8 +299,7 @@ static void advwdt_shutdown(struct platform_device *dev) } static struct platform_driver advwdt_driver = { - .probe = advwdt_probe, - .remove = __devexit_p(advwdt_remove), + .remove = advwdt_remove, .shutdown = advwdt_shutdown, .driver = { .owner = THIS_MODULE, @@ -318,24 +311,21 @@ 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; + pr_info("WDT driver for Advantech single board computer initialising\n"); 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; - } + if (IS_ERR(advwdt_platform_device)) + return PTR_ERR(advwdt_platform_device); + + err = platform_driver_probe(&advwdt_driver, advwdt_probe); + if (err) + goto unreg_platform_device; return 0; -unreg_platform_driver: - platform_driver_unregister(&advwdt_driver); +unreg_platform_device: + platform_device_unregister(advwdt_platform_device); return err; } @@ -343,7 +333,7 @@ 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"); + pr_info("Watchdog Module Unloaded\n"); } module_init(advwdt_init); @@ -352,4 +342,3 @@ 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/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c index f16dcbd475f..3a17fbd39f8 100644 --- a/drivers/watchdog/alim1535_wdt.c +++ b/drivers/watchdog/alim1535_wdt.c @@ -7,6 +7,8 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -22,7 +24,6 @@ #include <linux/io.h> #define WATCHDOG_NAME "ALi_M1535" -#define PFX WATCHDOG_NAME ": " #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ /* internal variables */ @@ -39,8 +40,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -268,8 +269,7 @@ static int ali_release(struct inode *inode, struct file *file) if (ali_expect_release == 42) ali_stop(); else { - printk(KERN_CRIT PFX - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); ali_keepalive(); } clear_bit(0, &ali_is_open); @@ -301,7 +301,7 @@ static int ali_notify_sys(struct notifier_block *this, * want to register another driver on the same PCI id. */ -static DEFINE_PCI_DEVICE_TABLE(ali_pci_tbl) __used = { +static const struct pci_device_id 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, }, @@ -399,9 +399,8 @@ static int __init watchdog_init(void) 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); + pr_info("timeout value must be 0 < timeout < 18000, using %d\n", + timeout); } /* Calculate the watchdog's timeout */ @@ -409,20 +408,18 @@ static int __init watchdog_init(void) ret = register_reboot_notifier(&ali_notifier); if (ret != 0) { - printk(KERN_ERR PFX - "cannot register reboot notifier (err=%d)\n", ret); + pr_err("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); + pr_err("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", + pr_info("initialized. timeout=%d sec (nowayout=%d)\n", timeout, nowayout); out: @@ -455,4 +452,3 @@ 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 index 46f4b85b46d..996b2f7d330 100644 --- a/drivers/watchdog/alim7101_wdt.c +++ b/drivers/watchdog/alim7101_wdt.c @@ -19,6 +19,8 @@ * -- Mike Waychison <michael.waychison@sun.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -34,10 +36,6 @@ #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 @@ -79,8 +77,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -112,8 +110,7 @@ static void wdt_timer_ping(unsigned long data) ALI_7101_GPIO_O, tmp & ~0x20); } } else { - printk(KERN_WARNING PFX - "Heartbeat lost! Will not ping the watchdog\n"); + pr_warn("Heartbeat lost! Will not ping the watchdog\n"); } /* Re-set the timer interval */ mod_timer(&timer, jiffies + WDT_INTERVAL); @@ -162,7 +159,7 @@ static void wdt_startup(void) /* Start the timer */ mod_timer(&timer, jiffies + WDT_INTERVAL); - printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); + pr_info("Watchdog timer is now enabled\n"); } static void wdt_turnoff(void) @@ -170,7 +167,7 @@ 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"); + pr_info("Watchdog timer is now disabled...\n"); } static void wdt_keepalive(void) @@ -226,8 +223,7 @@ static int fop_close(struct inode *inode, struct file *file) 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"); + pr_crit("device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &wdt_is_open); wdt_expect_close = 0; @@ -322,8 +318,7 @@ static int wdt_notify_sys(struct notifier_block *this, * 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"); + pr_info("Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second\n"); } return NOTIFY_DONE; } @@ -352,12 +347,11 @@ static int __init alim7101_wdt_init(void) struct pci_dev *ali1543_south; char tmp; - printk(KERN_INFO PFX "Steve Hill <steve@navaho.co.uk>.\n"); + pr_info("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"); + pr_info("ALi M7101 PMU not present - WDT not set\n"); return -EBUSY; } @@ -367,56 +361,46 @@ static int __init alim7101_wdt_init(void) 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"); + pr_info("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"); + pr_info("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"); + pr_info("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); + pr_info("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); + pr_err("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); + pr_err("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", + pr_info("WDT driver for ALi M7101 initialised. timeout=%d sec (nowayout=%d)\n", timeout, nowayout); return 0; @@ -430,7 +414,7 @@ err_out: module_init(alim7101_wdt_init); module_exit(alim7101_wdt_unload); -static DEFINE_PCI_DEVICE_TABLE(alim7101_pci_tbl) __used = { +static const struct pci_device_id 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) }, { } @@ -441,4 +425,3 @@ 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 index 502773ad5ac..ae6c287a49c 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c @@ -23,10 +23,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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> @@ -39,20 +40,18 @@ #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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); #define READ_REG(x) readl((void __iomem *)&(x)) @@ -93,7 +92,7 @@ static void ar7_wdt_kick(u32 value) return; } } - printk(KERN_ERR DRVNAME ": failed to unlock WDT kick reg\n"); + pr_err("failed to unlock WDT kick reg\n"); } static void ar7_wdt_prescale(u32 value) @@ -106,7 +105,7 @@ static void ar7_wdt_prescale(u32 value) return; } } - printk(KERN_ERR DRVNAME ": failed to unlock WDT prescale reg\n"); + pr_err("failed to unlock WDT prescale reg\n"); } static void ar7_wdt_change(u32 value) @@ -119,7 +118,7 @@ static void ar7_wdt_change(u32 value) return; } } - printk(KERN_ERR DRVNAME ": failed to unlock WDT change reg\n"); + pr_err("failed to unlock WDT change reg\n"); } static void ar7_wdt_disable(u32 value) @@ -135,7 +134,7 @@ static void ar7_wdt_disable(u32 value) } } } - printk(KERN_ERR DRVNAME ": failed to unlock WDT disable reg\n"); + pr_err("failed to unlock WDT disable reg\n"); } static void ar7_wdt_update_margin(int new_margin) @@ -151,21 +150,20 @@ static void ar7_wdt_update_margin(int new_margin) 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); + pr_info("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"); + pr_debug("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"); + pr_debug("disabling watchdog timer\n"); ar7_wdt_disable(0); } @@ -183,9 +181,7 @@ static int ar7_wdt_open(struct inode *inode, struct file *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"); + pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n"); else if (!nowayout) ar7_wdt_disable_wdt(); clear_bit(0, &wdt_is_open); @@ -276,37 +272,20 @@ static struct miscdevice ar7_wdt_miscdev = { .fops = &ar7_wdt_fops, }; -static int __devinit ar7_wdt_probe(struct platform_device *pdev) +static int ar7_wdt_probe(struct platform_device *pdev) { int rc; 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; - } + ar7_wdt = devm_ioremap_resource(&pdev->dev, ar7_regs_wdt); + if (IS_ERR(ar7_wdt)) + return PTR_ERR(ar7_wdt); 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; + pr_err("could not get vbus clock\n"); + return PTR_ERR(vbus_clk); } ar7_wdt_disable_wdt(); @@ -315,25 +294,22 @@ static int __devinit ar7_wdt_probe(struct platform_device *pdev) rc = misc_register(&ar7_wdt_miscdev); if (rc) { - printk(KERN_ERR DRVNAME ": unable to register misc device\n"); - goto out_alloc; + pr_err("unable to register misc device\n"); + goto out; } - goto out; + return 0; -out_alloc: - iounmap(ar7_wdt); -out_mem_region: - release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt)); out: + clk_put(vbus_clk); + vbus_clk = NULL; return rc; } -static int __devexit ar7_wdt_remove(struct platform_device *pdev) +static int 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)); - + clk_put(vbus_clk); + vbus_clk = NULL; return 0; } @@ -345,7 +321,7 @@ static void ar7_wdt_shutdown(struct platform_device *pdev) static struct platform_driver ar7_wdt_driver = { .probe = ar7_wdt_probe, - .remove = __devexit_p(ar7_wdt_remove), + .remove = ar7_wdt_remove, .shutdown = ar7_wdt_shutdown, .driver = { .owner = THIS_MODULE, diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c index 4ca5d40304b..25b5c67d3af 100644 --- a/drivers/watchdog/at32ap700x_wdt.c +++ b/drivers/watchdog/at32ap700x_wdt.c @@ -45,8 +45,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -321,13 +321,12 @@ static int __init at32_wdt_probe(struct platform_device *pdev) return -ENXIO; } - wdt = kzalloc(sizeof(struct wdt_at32ap700x), GFP_KERNEL); - if (!wdt) { - dev_dbg(&pdev->dev, "no memory for wdt structure\n"); + wdt = devm_kzalloc(&pdev->dev, sizeof(struct wdt_at32ap700x), + GFP_KERNEL); + if (!wdt) return -ENOMEM; - } - wdt->regs = ioremap(regs->start, resource_size(regs)); + wdt->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); if (!wdt->regs) { ret = -ENOMEM; dev_dbg(&pdev->dev, "could not map I/O memory\n"); @@ -342,7 +341,7 @@ static int __init at32_wdt_probe(struct platform_device *pdev) dev_info(&pdev->dev, "CPU must be reset with external " "reset or POR due to silicon errata.\n"); ret = -EIO; - goto err_iounmap; + goto err_free; } else { wdt->users = 0; } @@ -364,7 +363,7 @@ static int __init at32_wdt_probe(struct platform_device *pdev) ret = misc_register(&wdt->miscdev); if (ret) { dev_dbg(&pdev->dev, "failed to register wdt miscdev\n"); - goto err_register; + goto err_free; } dev_info(&pdev->dev, @@ -373,12 +372,7 @@ static int __init at32_wdt_probe(struct platform_device *pdev) return 0; -err_register: - platform_set_drvdata(pdev, NULL); -err_iounmap: - iounmap(wdt->regs); err_free: - kfree(wdt); wdt = NULL; return ret; } @@ -391,10 +385,7 @@ static int __exit at32_wdt_remove(struct platform_device *pdev) at32_wdt_stop(); misc_deregister(&wdt->miscdev); - iounmap(wdt->regs); - kfree(wdt); wdt = NULL; - platform_set_drvdata(pdev, NULL); } return 0; } @@ -436,19 +427,8 @@ static struct platform_driver at32_wdt_driver = { .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_platform_driver_probe(at32_wdt_driver, at32_wdt_probe); 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 index b3046dc4b56..dee6cc21d27 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -9,6 +9,8 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/bitops.h> #include <linux/errno.h> #include <linux/fs.h> @@ -22,20 +24,22 @@ #include <linux/types.h> #include <linux/watchdog.h> #include <linux/uaccess.h> +#include <linux/of.h> +#include <linux/of_device.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; +static bool 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_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -51,7 +55,7 @@ static unsigned long at91wdt_busy; */ static inline void at91_wdt_stop(void) { - at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN); + at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN); } /* @@ -59,9 +63,9 @@ static inline void at91_wdt_stop(void) */ static inline void at91_wdt_start(void) { - at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | + at91_st_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); + at91_st_write(AT91_ST_CR, AT91_ST_WDRST); } /* @@ -69,7 +73,7 @@ static inline void at91_wdt_start(void) */ static inline void at91_wdt_reload(void) { - at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); + at91_st_write(AT91_ST_CR, AT91_ST_WDRST); } /* ......................................................................... */ @@ -197,7 +201,7 @@ static struct miscdevice at91wdt_miscdev = { .fops = &at91wdt_fops, }; -static int __devinit at91wdt_probe(struct platform_device *pdev) +static int at91wdt_probe(struct platform_device *pdev) { int res; @@ -209,12 +213,12 @@ static int __devinit at91wdt_probe(struct platform_device *pdev) if (res) return res; - printk(KERN_INFO "AT91 Watchdog Timer enabled (%d seconds%s)\n", - wdt_time, nowayout ? ", nowayout" : ""); + pr_info("AT91 Watchdog Timer enabled (%d seconds%s)\n", + wdt_time, nowayout ? ", nowayout" : ""); return 0; } -static int __devexit at91wdt_remove(struct platform_device *pdev) +static int at91wdt_remove(struct platform_device *pdev) { int res; @@ -250,15 +254,22 @@ static int at91wdt_resume(struct platform_device *pdev) #define at91wdt_resume NULL #endif +static const struct of_device_id at91_wdt_dt_ids[] = { + { .compatible = "atmel,at91rm9200-wdt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids); + static struct platform_driver at91wdt_driver = { .probe = at91wdt_probe, - .remove = __devexit_p(at91wdt_remove), + .remove = at91wdt_remove, .shutdown = at91wdt_shutdown, .suspend = at91wdt_suspend, .resume = at91wdt_resume, .driver = { .name = "at91_wdt", .owner = THIS_MODULE, + .of_match_table = at91_wdt_dt_ids, }, }; @@ -268,8 +279,8 @@ static int __init at91_wdt_init(void) 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); + pr_info("wdt_time value must be 1 <= wdt_time <= 256, using %d\n", + wdt_time); } return platform_driver_register(&at91wdt_driver); @@ -286,5 +297,4 @@ 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 index 00562566ef5..489729b2629 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -15,37 +15,51 @@ * bootloader doesn't write to this register. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/errno.h> -#include <linux/fs.h> #include <linux/init.h> +#include <linux/interrupt.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/reboot.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 <linux/of.h> +#include <linux/of_irq.h> #include "at91sam9_wdt.h" #define DRV_NAME "AT91SAM9 Watchdog" -#define wdt_read(field) \ - __raw_readl(at91wdt_private.base + field) -#define wdt_write(field, val) \ - __raw_writel((val), at91wdt_private.base + field) +#define wdt_read(wdt, field) \ + __raw_readl((wdt)->base + (field)) +#define wdt_write(wtd, field, val) \ + __raw_writel((val), (wdt)->base + (field)) /* 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) +#define ticks_to_hz_rounddown(t) ((((t) + 1) * HZ) >> 8) +#define ticks_to_hz_roundup(t) (((((t) + 1) * HZ) + 255) >> 8) +#define ticks_to_secs(t) (((t) + 1) >> 8) +#define secs_to_ticks(s) ((s) ? (((s) << 8) - 1) : 0) + +#define WDT_MR_RESET 0x3FFF2FFF + +/* Watchdog max counter value in ticks */ +#define WDT_COUNTER_MAX_TICKS 0xFFF + +/* Watchdog max delta/value in secs */ +#define WDT_COUNTER_MAX_SECS ticks_to_secs(WDT_COUNTER_MAX_TICKS) /* Hardware timeout in seconds */ #define WDT_HW_TIMEOUT 2 @@ -55,35 +69,50 @@ /* User land timeout */ #define WDT_HEARTBEAT 15 -static int heartbeat = WDT_HEARTBEAT; +static int 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 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 { +#define to_wdt(wdd) container_of(wdd, struct at91wdt, wdd) +struct at91wdt { + struct watchdog_device wdd; void __iomem *base; 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; + u32 mr; + u32 mr_mask; + unsigned long heartbeat; /* WDT heartbeat in jiffies */ + bool nowayout; + unsigned int irq; +}; /* ......................................................................... */ +static irqreturn_t wdt_interrupt(int irq, void *dev_id) +{ + struct at91wdt *wdt = (struct at91wdt *)dev_id; + + if (wdt_read(wdt, AT91_WDT_SR)) { + pr_crit("at91sam9 WDT software reset\n"); + emergency_restart(); + pr_crit("Reboot didn't ?????\n"); + } + + return IRQ_HANDLED; +} /* * Reload the watchdog timer. (ie, pat the watchdog) */ -static inline void at91_wdt_reset(void) +static inline void at91_wdt_reset(struct at91wdt *wdt) { - wdt_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); + wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); } /* @@ -91,237 +120,286 @@ static inline void at91_wdt_reset(void) */ 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"); + struct at91wdt *wdt = (struct at91wdt *)data; + if (time_before(jiffies, wdt->next_heartbeat) || + !watchdog_active(&wdt->wdd)) { + at91_wdt_reset(wdt); + mod_timer(&wdt->timer, jiffies + wdt->heartbeat); + } else { + pr_crit("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) +static int at91_wdt_start(struct watchdog_device *wdd) { - if (test_and_set_bit(0, &at91wdt_private.open)) - return -EBUSY; + struct at91wdt *wdt = to_wdt(wdd); + /* calculate when the next userspace timeout will be */ + wdt->next_heartbeat = jiffies + wdd->timeout * HZ; + return 0; +} - at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; - mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); +static int at91_wdt_stop(struct watchdog_device *wdd) +{ + /* The watchdog timer hardware can not be stopped... */ + return 0; +} - return nonseekable_open(inode, file); +static int at91_wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout) +{ + wdd->timeout = new_timeout; + return at91_wdt_start(wdd); } -/* - * Close the watchdog device. - */ -static int at91_wdt_close(struct inode *inode, struct file *file) +static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt) { - clear_bit(0, &at91wdt_private.open); + u32 tmp; + u32 delta; + u32 value; + int err; + u32 mask = wdt->mr_mask; + unsigned long min_heartbeat = 1; + unsigned long max_heartbeat; + struct device *dev = &pdev->dev; + + tmp = wdt_read(wdt, AT91_WDT_MR); + if ((tmp & mask) != (wdt->mr & mask)) { + if (tmp == WDT_MR_RESET) { + wdt_write(wdt, AT91_WDT_MR, wdt->mr); + tmp = wdt_read(wdt, AT91_WDT_MR); + } + } - /* stop internal ping */ - if (!at91wdt_private.expect_close) - del_timer(&at91wdt_private.timer); + if (tmp & AT91_WDT_WDDIS) { + if (wdt->mr & AT91_WDT_WDDIS) + return 0; + dev_err(dev, "watchdog is disabled\n"); + return -EINVAL; + } - at91wdt_private.expect_close = 0; - return 0; -} + value = tmp & AT91_WDT_WDV; + delta = (tmp & AT91_WDT_WDD) >> 16; -/* - * 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 = wdt_read(AT91_WDT_MR); - if (mr & AT91_WDT_WDDIS) { - printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n"); - return -EIO; + if (delta < value) + min_heartbeat = ticks_to_hz_roundup(value - delta); + + max_heartbeat = ticks_to_hz_rounddown(value); + if (!max_heartbeat) { + dev_err(dev, + "heartbeat is too small for the system to handle it correctly\n"); + return -EINVAL; + } + + /* + * Try to reset the watchdog counter 4 or 2 times more often than + * actually requested, to avoid spurious watchdog reset. + * If this is not possible because of the min_heartbeat value, reset + * it at the min_heartbeat period. + */ + if ((max_heartbeat / 4) >= min_heartbeat) + wdt->heartbeat = max_heartbeat / 4; + else if ((max_heartbeat / 2) >= min_heartbeat) + wdt->heartbeat = max_heartbeat / 2; + else + wdt->heartbeat = min_heartbeat; + + if (max_heartbeat < min_heartbeat + 4) + dev_warn(dev, + "min heartbeat and max heartbeat might be too close for the system to handle it correctly\n"); + + if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) { + err = request_irq(wdt->irq, wdt_interrupt, + IRQF_SHARED | IRQF_IRQPOLL, + pdev->name, wdt); + if (err) + return err; } + if ((tmp & wdt->mr_mask) != (wdt->mr & wdt->mr_mask)) + dev_warn(dev, + "watchdog already configured differently (mr = %x expecting %x)\n", + tmp & wdt->mr_mask, wdt->mr & wdt->mr_mask); + + setup_timer(&wdt->timer, at91_ping, (unsigned long)wdt); + /* - * All counting occurs at SLOW_CLOCK / 128 = 256 Hz - * - * Since WDV is a 12-bit counter, the maximum period is - * 4096 / 256 = 16 seconds. + * Use min_heartbeat the first time to avoid spurious watchdog reset: + * we don't know for how long the watchdog counter is running, and + * - resetting it right now might trigger a watchdog fault reset + * - waiting for heartbeat time might lead to a watchdog timeout + * reset */ - 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 */ - wdt_write(AT91_WDT_MR, reg); + mod_timer(&wdt->timer, jiffies + min_heartbeat); + + /* Try to set timeout from device tree first */ + if (watchdog_init_timeout(&wdt->wdd, 0, dev)) + watchdog_init_timeout(&wdt->wdd, heartbeat, dev); + watchdog_set_nowayout(&wdt->wdd, wdt->nowayout); + err = watchdog_register_device(&wdt->wdd); + if (err) + goto out_stop_timer; + + wdt->next_heartbeat = jiffies + wdt->wdd.timeout * HZ; return 0; + +out_stop_timer: + del_timer(&wdt->timer); + return err; } +/* ......................................................................... */ + 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) +static const struct watchdog_ops at91_wdt_ops = { + .owner = THIS_MODULE, + .start = at91_wdt_start, + .stop = at91_wdt_stop, + .set_timeout = at91_wdt_set_timeout, +}; + +#if defined(CONFIG_OF) +static int of_at91wdt_init(struct device_node *np, struct at91wdt *wdt) { - void __user *argp = (void __user *)arg; - int __user *p = argp; - int new_value; + u32 min = 0; + u32 max = WDT_COUNTER_MAX_SECS; + const char *tmp; + + /* Get the interrupts property */ + wdt->irq = irq_of_parse_and_map(np, 0); + if (!wdt->irq) + dev_warn(wdt->wdd.parent, "failed to get IRQ from DT\n"); + + if (!of_property_read_u32_index(np, "atmel,max-heartbeat-sec", 0, + &max)) { + if (!max || max > WDT_COUNTER_MAX_SECS) + max = WDT_COUNTER_MAX_SECS; + + if (!of_property_read_u32_index(np, "atmel,min-heartbeat-sec", + 0, &min)) { + if (min >= max) + min = max - 1; + } + } - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &at91_wdt_info, - sizeof(at91_wdt_info)) ? -EFAULT : 0; + min = secs_to_ticks(min); + max = secs_to_ticks(max); + + wdt->mr_mask = 0x3FFFFFFF; + wdt->mr = 0; + if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && + !strcmp(tmp, "software")) { + wdt->mr |= AT91_WDT_WDFIEN; + wdt->mr_mask &= ~AT91_WDT_WDRPROC; + } else { + wdt->mr |= AT91_WDT_WDRSTEN; + } - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); + if (!of_property_read_string(np, "atmel,reset-type", &tmp) && + !strcmp(tmp, "proc")) + wdt->mr |= AT91_WDT_WDRPROC; - case WDIOC_KEEPALIVE: - at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; - return 0; + if (of_property_read_bool(np, "atmel,disable")) { + wdt->mr |= AT91_WDT_WDDIS; + wdt->mr_mask &= AT91_WDT_WDDIS; + } - case WDIOC_SETTIMEOUT: - if (get_user(new_value, p)) - return -EFAULT; + if (of_property_read_bool(np, "atmel,idle-halt")) + wdt->mr |= AT91_WDT_WDIDLEHLT; - heartbeat = new_value; - at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; + if (of_property_read_bool(np, "atmel,dbg-halt")) + wdt->mr |= AT91_WDT_WDDBGHLT; - return put_user(new_value, p); /* return current value */ + wdt->mr |= max | ((max - min) << 16); - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - } - return -ENOTTY; + return 0; } - -/* - * 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) +#else +static inline int of_at91wdt_init(struct device_node *np, struct at91wdt *wdt) { - 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; + return 0; } - -/* ......................................................................... */ - -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, -}; +#endif static int __init at91wdt_probe(struct platform_device *pdev) { struct resource *r; - int res; + int err; + struct at91wdt *wdt; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; - if (at91wdt_miscdev.parent) - return -EBUSY; - at91wdt_miscdev.parent = &pdev->dev; + wdt->mr = (WDT_HW_TIMEOUT * 256) | AT91_WDT_WDRSTEN | AT91_WDT_WDD | + AT91_WDT_WDDBGHLT | AT91_WDT_WDIDLEHLT; + wdt->mr_mask = 0x3FFFFFFF; + wdt->nowayout = nowayout; + wdt->wdd.parent = &pdev->dev; + wdt->wdd.info = &at91_wdt_info; + wdt->wdd.ops = &at91_wdt_ops; + wdt->wdd.timeout = WDT_HEARTBEAT; + wdt->wdd.min_timeout = 1; + wdt->wdd.max_timeout = 0xFFFF; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - return -ENODEV; - at91wdt_private.base = ioremap(r->start, resource_size(r)); - if (!at91wdt_private.base) { - dev_err(&pdev->dev, "failed to map registers, aborting.\n"); - return -ENOMEM; + wdt->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); + + if (pdev->dev.of_node) { + err = of_at91wdt_init(pdev->dev.of_node, wdt); + if (err) + return err; } - /* 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; + err = at91_wdt_init(pdev, wdt); + if (err) + return err; - at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; - setup_timer(&at91wdt_private.timer, at91_ping, 0); - mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); + platform_set_drvdata(pdev, wdt); - printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n", - heartbeat, nowayout); + pr_info("enabled (heartbeat=%d sec, nowayout=%d)\n", + wdt->wdd.timeout, wdt->nowayout); return 0; } static int __exit at91wdt_remove(struct platform_device *pdev) { - int res; + struct at91wdt *wdt = platform_get_drvdata(pdev); + watchdog_unregister_device(&wdt->wdd); - res = misc_deregister(&at91wdt_miscdev); - if (!res) - at91wdt_miscdev.parent = NULL; + pr_warn("I quit now, hardware will probably reboot!\n"); + del_timer(&wdt->timer); - return res; + return 0; } +#if defined(CONFIG_OF) +static const struct of_device_id at91_wdt_dt_ids[] = { + { .compatible = "atmel,at91sam9260-wdt" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids); +#endif + static struct platform_driver at91wdt_driver = { .remove = __exit_p(at91wdt_remove), .driver = { .name = "at91_wdt", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_wdt_dt_ids), }, }; -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_platform_driver_probe(at91wdt_driver, at91wdt_probe); 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 index 9db808349f8..41ac4660fb8 100644 --- a/drivers/watchdog/ath79_wdt.c +++ b/drivers/watchdog/ath79_wdt.c @@ -17,10 +17,13 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/bitops.h> +#include <linux/delay.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> @@ -30,14 +33,16 @@ #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> +#include <linux/of.h> +#include <linux/of_platform.h> #define DRIVER_NAME "ath79-wdt" #define WDT_TIMEOUT 15 /* seconds */ +#define WDOG_REG_CTRL 0x00 +#define WDOG_REG_TIMER 0x04 + #define WDOG_CTRL_LAST_RESET BIT(31) #define WDOG_CTRL_ACTION_MASK 3 #define WDOG_CTRL_ACTION_NONE 0 /* no action */ @@ -45,8 +50,8 @@ #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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -64,27 +69,47 @@ static struct clk *wdt_clk; static unsigned long wdt_freq; static int boot_status; static int max_timeout; +static void __iomem *wdt_base; + +static inline void ath79_wdt_wr(unsigned reg, u32 val) +{ + iowrite32(val, wdt_base + reg); +} + +static inline u32 ath79_wdt_rr(unsigned reg) +{ + return ioread32(wdt_base + reg); +} static inline void ath79_wdt_keepalive(void) { - ath79_reset_wr(AR71XX_RESET_REG_WDOG, wdt_freq * timeout); + ath79_wdt_wr(WDOG_REG_TIMER, wdt_freq * timeout); /* flush write */ - ath79_reset_rr(AR71XX_RESET_REG_WDOG); + ath79_wdt_rr(WDOG_REG_TIMER); } static inline void ath79_wdt_enable(void) { ath79_wdt_keepalive(); - ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_FCR); + + /* + * Updating the TIMER register requires a few microseconds + * on the AR934x SoCs at least. Use a small delay to ensure + * that the TIMER register is updated within the hardware + * before enabling the watchdog. + */ + udelay(2); + + ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_FCR); /* flush write */ - ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL); + ath79_wdt_rr(WDOG_REG_CTRL); } static inline void ath79_wdt_disable(void) { - ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_NONE); + ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_NONE); /* flush write */ - ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL); + ath79_wdt_rr(WDOG_REG_CTRL); } static int ath79_wdt_set_timeout(int val) @@ -114,8 +139,7 @@ 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"); + pr_crit("device closed unexpectedly, watchdog timer will not stop!\n"); ath79_wdt_keepalive(); } @@ -223,18 +247,27 @@ static struct miscdevice ath79_wdt_miscdev = { .fops = &ath79_wdt_fops, }; -static int __devinit ath79_wdt_probe(struct platform_device *pdev) +static int ath79_wdt_probe(struct platform_device *pdev) { + struct resource *res; u32 ctrl; int err; - wdt_clk = clk_get(&pdev->dev, "wdt"); + if (wdt_base) + return -EBUSY; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdt_base)) + return PTR_ERR(wdt_base); + + wdt_clk = devm_clk_get(&pdev->dev, "wdt"); if (IS_ERR(wdt_clk)) return PTR_ERR(wdt_clk); - err = clk_enable(wdt_clk); + err = clk_prepare_enable(wdt_clk); if (err) - goto err_clk_put; + return err; wdt_freq = clk_get_rate(wdt_clk); if (!wdt_freq) { @@ -250,7 +283,7 @@ static int __devinit ath79_wdt_probe(struct platform_device *pdev) max_timeout, timeout); } - ctrl = ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL); + ctrl = ath79_wdt_rr(WDOG_REG_CTRL); boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0; err = misc_register(&ath79_wdt_miscdev); @@ -263,17 +296,14 @@ static int __devinit ath79_wdt_probe(struct platform_device *pdev) return 0; err_clk_disable: - clk_disable(wdt_clk); -err_clk_put: - clk_put(wdt_clk); + clk_disable_unprepare(wdt_clk); return err; } -static int __devexit ath79_wdt_remove(struct platform_device *pdev) +static int ath79_wdt_remove(struct platform_device *pdev) { misc_deregister(&ath79_wdt_miscdev); - clk_disable(wdt_clk); - clk_put(wdt_clk); + clk_disable_unprepare(wdt_clk); return 0; } @@ -282,30 +312,29 @@ static void ath97_wdt_shutdown(struct platform_device *pdev) ath79_wdt_disable(); } +#ifdef CONFIG_OF +static const struct of_device_id ath79_wdt_match[] = { + { .compatible = "qca,ar7130-wdt" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ath79_wdt_match); +#endif + static struct platform_driver ath79_wdt_driver = { - .remove = __devexit_p(ath79_wdt_remove), + .probe = ath79_wdt_probe, + .remove = ath79_wdt_remove, .shutdown = ath97_wdt_shutdown, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ath79_wdt_match), }, }; -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_platform_driver(ath79_wdt_driver); 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/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c new file mode 100644 index 00000000000..8df450c090a --- /dev/null +++ b/drivers/watchdog/bcm2835_wdt.c @@ -0,0 +1,185 @@ +/* + * Watchdog driver for Broadcom BCM2835 + * + * "bcm2708_wdog" driver written by Luke Diamand that was obtained from + * branch "rpi-3.6.y" of git://github.com/raspberrypi/linux.git was used + * as a hardware reference for the Broadcom BCM2835 watchdog timer. + * + * Copyright (C) 2013 Lubomir Rintel <lkundrak@v3.sk> + * + * 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/types.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> + +#define PM_RSTC 0x1c +#define PM_WDOG 0x24 + +#define PM_PASSWORD 0x5a000000 + +#define PM_WDOG_TIME_SET 0x000fffff +#define PM_RSTC_WRCFG_CLR 0xffffffcf +#define PM_RSTC_WRCFG_SET 0x00000030 +#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 +#define PM_RSTC_RESET 0x00000102 + +#define SECS_TO_WDOG_TICKS(x) ((x) << 16) +#define WDOG_TICKS_TO_SECS(x) ((x) >> 16) + +struct bcm2835_wdt { + void __iomem *base; + spinlock_t lock; +}; + +static unsigned int heartbeat; +static bool nowayout = WATCHDOG_NOWAYOUT; + +static int bcm2835_wdt_start(struct watchdog_device *wdog) +{ + struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); + uint32_t cur; + unsigned long flags; + + spin_lock_irqsave(&wdt->lock, flags); + + writel_relaxed(PM_PASSWORD | (SECS_TO_WDOG_TICKS(wdog->timeout) & + PM_WDOG_TIME_SET), wdt->base + PM_WDOG); + cur = readl_relaxed(wdt->base + PM_RSTC); + writel_relaxed(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) | + PM_RSTC_WRCFG_FULL_RESET, wdt->base + PM_RSTC); + + spin_unlock_irqrestore(&wdt->lock, flags); + + return 0; +} + +static int bcm2835_wdt_stop(struct watchdog_device *wdog) +{ + struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); + + writel_relaxed(PM_PASSWORD | PM_RSTC_RESET, wdt->base + PM_RSTC); + dev_info(wdog->dev, "Watchdog timer stopped"); + return 0; +} + +static int bcm2835_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) +{ + wdog->timeout = t; + return 0; +} + +static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); + + uint32_t ret = readl_relaxed(wdt->base + PM_WDOG); + return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET); +} + +static struct watchdog_ops bcm2835_wdt_ops = { + .owner = THIS_MODULE, + .start = bcm2835_wdt_start, + .stop = bcm2835_wdt_stop, + .set_timeout = bcm2835_wdt_set_timeout, + .get_timeleft = bcm2835_wdt_get_timeleft, +}; + +static struct watchdog_info bcm2835_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .identity = "Broadcom BCM2835 Watchdog timer", +}; + +static struct watchdog_device bcm2835_wdt_wdd = { + .info = &bcm2835_wdt_info, + .ops = &bcm2835_wdt_ops, + .min_timeout = 1, + .max_timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET), + .timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET), +}; + +static int bcm2835_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct bcm2835_wdt *wdt; + int err; + + wdt = devm_kzalloc(dev, sizeof(struct bcm2835_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + platform_set_drvdata(pdev, wdt); + + spin_lock_init(&wdt->lock); + + wdt->base = of_iomap(np, 0); + if (!wdt->base) { + dev_err(dev, "Failed to remap watchdog regs"); + return -ENODEV; + } + + watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt); + watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev); + watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout); + err = watchdog_register_device(&bcm2835_wdt_wdd); + if (err) { + dev_err(dev, "Failed to register watchdog device"); + iounmap(wdt->base); + return err; + } + + dev_info(dev, "Broadcom BCM2835 watchdog timer"); + return 0; +} + +static int bcm2835_wdt_remove(struct platform_device *pdev) +{ + struct bcm2835_wdt *wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&bcm2835_wdt_wdd); + iounmap(wdt->base); + + return 0; +} + +static void bcm2835_wdt_shutdown(struct platform_device *pdev) +{ + bcm2835_wdt_stop(&bcm2835_wdt_wdd); +} + +static const struct of_device_id bcm2835_wdt_of_match[] = { + { .compatible = "brcm,bcm2835-pm-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_wdt_of_match); + +static struct platform_driver bcm2835_wdt_driver = { + .probe = bcm2835_wdt_probe, + .remove = bcm2835_wdt_remove, + .shutdown = bcm2835_wdt_shutdown, + .driver = { + .name = "bcm2835-wdt", + .owner = THIS_MODULE, + .of_match_table = bcm2835_wdt_of_match, + }, +}; +module_platform_driver(bcm2835_wdt_driver); + +module_param(heartbeat, uint, 0); +MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); +MODULE_DESCRIPTION("Driver for Broadcom BCM2835 watchdog timer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index 5c5f4b14fd0..b61fcc53597 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -3,6 +3,7 @@ * * Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs> * Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr> + * Copyright (C) 2012-2013 Hauke Mehrtens <hauke@hauke-m.de> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -10,166 +11,144 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/bcm47xx_wdt.h> #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/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 */ +#define WDT_SOFTTIMER_MAX 255 /* seconds */ +#define WDT_SOFTTIMER_THRESHOLD 60 /* seconds */ -static int wdt_time = WDT_DEFAULT_TIME; -static int nowayout = WATCHDOG_NOWAYOUT; +static int timeout = WDT_DEFAULT_TIME; +static bool nowayout = WATCHDOG_NOWAYOUT; -module_param(wdt_time, int, 0); -MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog time in seconds. (default=" __MODULE_STRING(WDT_DEFAULT_TIME) ")"); -#ifdef CONFIG_WATCHDOG_NOWAYOUT -module_param(nowayout, int, 0); +module_param(nowayout, bool, 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) +static inline struct bcm47xx_wdt *bcm47xx_wdt_get(struct watchdog_device *wdd) { - /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */ - switch (bcm47xx_bus_type) { -#ifdef CONFIG_BCM47XX_SSB - case BCM47XX_BUS_TYPE_SSB: - ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0xfffffff); - break; -#endif -#ifdef CONFIG_BCM47XX_BCMA - case BCM47XX_BUS_TYPE_BCMA: - bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, - 0xfffffff); - break; -#endif - } + return container_of(wdd, struct bcm47xx_wdt, wdd); } -static inline int bcm47xx_wdt_hw_stop(void) +static int bcm47xx_wdt_hard_keepalive(struct watchdog_device *wdd) { - switch (bcm47xx_bus_type) { -#ifdef CONFIG_BCM47XX_SSB - case BCM47XX_BUS_TYPE_SSB: - return ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0); -#endif -#ifdef CONFIG_BCM47XX_BCMA - case BCM47XX_BUS_TYPE_BCMA: - bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 0); - return 0; -#endif - } - return -EINVAL; -} + struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); -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"); - } + wdt->timer_set_ms(wdt, wdd->timeout * 1000); + + return 0; } -static inline void bcm47xx_wdt_pet(void) +static int bcm47xx_wdt_hard_start(struct watchdog_device *wdd) { - atomic_set(&ticks, wdt_time); + return 0; } -static void bcm47xx_wdt_start(void) +static int bcm47xx_wdt_hard_stop(struct watchdog_device *wdd) { - bcm47xx_wdt_pet(); - bcm47xx_timer_tick(0); + struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); + + wdt->timer_set(wdt, 0); + + return 0; } -static void bcm47xx_wdt_pause(void) +static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd, + unsigned int new_time) { - del_timer_sync(&wdt_timer); - bcm47xx_wdt_hw_stop(); + struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); + u32 max_timer = wdt->max_timer_ms; + + if (new_time < 1 || new_time > max_timer / 1000) { + pr_warn("timeout value must be 1<=x<=%d, using %d\n", + max_timer / 1000, new_time); + return -EINVAL; + } + + wdd->timeout = new_time; + return 0; } -static void bcm47xx_wdt_stop(void) +static struct watchdog_ops bcm47xx_wdt_hard_ops = { + .owner = THIS_MODULE, + .start = bcm47xx_wdt_hard_start, + .stop = bcm47xx_wdt_hard_stop, + .ping = bcm47xx_wdt_hard_keepalive, + .set_timeout = bcm47xx_wdt_hard_set_timeout, +}; + +static void bcm47xx_wdt_soft_timer_tick(unsigned long data) { - bcm47xx_wdt_pause(); + struct bcm47xx_wdt *wdt = (struct bcm47xx_wdt *)data; + u32 next_tick = min(wdt->wdd.timeout * 1000, wdt->max_timer_ms); + + if (!atomic_dec_and_test(&wdt->soft_ticks)) { + wdt->timer_set_ms(wdt, next_tick); + mod_timer(&wdt->soft_timer, jiffies + HZ); + } else { + pr_crit("Watchdog will fire soon!!!\n"); + } } -static int bcm47xx_wdt_settimeout(int new_time) +static int bcm47xx_wdt_soft_keepalive(struct watchdog_device *wdd) { - if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) - return -EINVAL; + struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); + + atomic_set(&wdt->soft_ticks, wdd->timeout); - wdt_time = new_time; return 0; } -static int bcm47xx_wdt_open(struct inode *inode, struct file *file) +static int bcm47xx_wdt_soft_start(struct watchdog_device *wdd) { - if (test_and_set_bit(0, &bcm47xx_wdt_busy)) - return -EBUSY; + struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); + + bcm47xx_wdt_soft_keepalive(wdd); + bcm47xx_wdt_soft_timer_tick((unsigned long)wdt); - bcm47xx_wdt_start(); - return nonseekable_open(inode, file); + return 0; } -static int bcm47xx_wdt_release(struct inode *inode, struct file *file) +static int bcm47xx_wdt_soft_stop(struct watchdog_device *wdd) { - if (expect_release == 42) { - bcm47xx_wdt_stop(); - } else { - printk(KERN_CRIT DRV_NAME - ": Unexpected close, not stopping watchdog!\n"); - bcm47xx_wdt_start(); - } + struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); + + del_timer_sync(&wdt->soft_timer); + wdt->timer_set(wdt, 0); - 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) +static int bcm47xx_wdt_soft_set_timeout(struct watchdog_device *wdd, + unsigned int new_time) { - 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(); + if (new_time < 1 || new_time > WDT_SOFTTIMER_MAX) { + pr_warn("timeout value must be 1<=x<=%d, using %d\n", + WDT_SOFTTIMER_MAX, new_time); + return -EINVAL; } - return len; + + wdd->timeout = new_time; + return 0; } static const struct watchdog_info bcm47xx_wdt_info = { @@ -179,131 +158,100 @@ static const struct watchdog_info bcm47xx_wdt_info = { 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) + unsigned long code, void *unused) { + struct bcm47xx_wdt *wdt; + + wdt = container_of(this, struct bcm47xx_wdt, notifier); if (code == SYS_DOWN || code == SYS_HALT) - bcm47xx_wdt_stop(); + wdt->wdd.ops->stop(&wdt->wdd); return NOTIFY_DONE; } -static const struct file_operations bcm47xx_wdt_fops = { +static struct watchdog_ops bcm47xx_wdt_soft_ops = { .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, + .start = bcm47xx_wdt_soft_start, + .stop = bcm47xx_wdt_soft_stop, + .ping = bcm47xx_wdt_soft_keepalive, + .set_timeout = bcm47xx_wdt_soft_set_timeout, }; -static int __init bcm47xx_wdt_init(void) +static int bcm47xx_wdt_probe(struct platform_device *pdev) { int ret; + bool soft; + struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev); - if (bcm47xx_wdt_hw_stop() < 0) - return -ENODEV; + if (!wdt) + return -ENXIO; - setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L); + soft = wdt->max_timer_ms < WDT_SOFTTIMER_THRESHOLD * 1000; - 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); + if (soft) { + wdt->wdd.ops = &bcm47xx_wdt_soft_ops; + setup_timer(&wdt->soft_timer, bcm47xx_wdt_soft_timer_tick, + (long unsigned int)wdt); + } else { + wdt->wdd.ops = &bcm47xx_wdt_hard_ops; } - ret = register_reboot_notifier(&bcm47xx_wdt_notifier); + wdt->wdd.info = &bcm47xx_wdt_info; + wdt->wdd.timeout = WDT_DEFAULT_TIME; + ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout); if (ret) - return ret; + goto err_timer; + watchdog_set_nowayout(&wdt->wdd, nowayout); - ret = misc_register(&bcm47xx_wdt_miscdev); - if (ret) { - unregister_reboot_notifier(&bcm47xx_wdt_notifier); - return ret; - } + wdt->notifier.notifier_call = &bcm47xx_wdt_notify_sys; + + ret = register_reboot_notifier(&wdt->notifier); + if (ret) + goto err_timer; - printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n", - wdt_time, nowayout ? ", nowayout" : ""); + ret = watchdog_register_device(&wdt->wdd); + if (ret) + goto err_notifier; + + dev_info(&pdev->dev, "BCM47xx Watchdog Timer enabled (%d seconds%s%s)\n", + timeout, nowayout ? ", nowayout" : "", + soft ? ", Software Timer" : ""); return 0; + +err_notifier: + unregister_reboot_notifier(&wdt->notifier); +err_timer: + if (soft) + del_timer_sync(&wdt->soft_timer); + + return ret; } -static void __exit bcm47xx_wdt_exit(void) +static int bcm47xx_wdt_remove(struct platform_device *pdev) { - if (!nowayout) - bcm47xx_wdt_stop(); + struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev); - misc_deregister(&bcm47xx_wdt_miscdev); + if (!wdt) + return -ENXIO; - unregister_reboot_notifier(&bcm47xx_wdt_notifier); + watchdog_unregister_device(&wdt->wdd); + unregister_reboot_notifier(&wdt->notifier); + + return 0; } -module_init(bcm47xx_wdt_init); -module_exit(bcm47xx_wdt_exit); +static struct platform_driver bcm47xx_wdt_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "bcm47xx-wdt", + }, + .probe = bcm47xx_wdt_probe, + .remove = bcm47xx_wdt_remove, +}; + +module_platform_driver(bcm47xx_wdt_driver); MODULE_AUTHOR("Aleksandar Radovanovic"); +MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>"); 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 index 8dc7de641e2..5a8e879a430 100644 --- a/drivers/watchdog/bcm63xx_wdt.c +++ b/drivers/watchdog/bcm63xx_wdt.c @@ -10,10 +10,12 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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> @@ -42,7 +44,6 @@ static struct { void __iomem *regs; struct timer_list timer; - int default_ticks; unsigned long inuse; atomic_t ticks; } bcm63xx_wdt_device; @@ -50,8 +51,8 @@ static struct { static int expect_close; static int wdt_time = WDT_DEFAULT_TIME; -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -82,7 +83,7 @@ static void bcm63xx_timer_tick(unsigned long unused) bcm63xx_wdt_hw_start(); mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ); } else - printk(KERN_CRIT PFX ": watchdog will restart system\n"); + pr_crit("watchdog will restart system\n"); } static void bcm63xx_wdt_pet(void) @@ -126,8 +127,7 @@ 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"); + pr_crit("Unexpected close, not stopping watchdog!\n"); bcm63xx_wdt_start(); } clear_bit(0, &bcm63xx_wdt_device.inuse); @@ -235,7 +235,7 @@ static struct miscdevice bcm63xx_wdt_miscdev = { }; -static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev) +static int bcm63xx_wdt_probe(struct platform_device *pdev) { int ret; struct resource *r; @@ -248,7 +248,8 @@ static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev) return -ENODEV; } - bcm63xx_wdt_device.regs = ioremap_nocache(r->start, resource_size(r)); + bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start, + resource_size(r)); if (!bcm63xx_wdt_device.regs) { dev_err(&pdev->dev, "failed to remap I/O resources\n"); return -ENXIO; @@ -257,7 +258,7 @@ static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev) 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; + return ret; } if (bcm63xx_wdt_settimeout(wdt_time)) { @@ -280,19 +281,16 @@ static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev) 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) +static int 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; } @@ -301,9 +299,9 @@ static void bcm63xx_wdt_shutdown(struct platform_device *pdev) bcm63xx_wdt_pause(); } -static struct platform_driver bcm63xx_wdt = { +static struct platform_driver bcm63xx_wdt_driver = { .probe = bcm63xx_wdt_probe, - .remove = __devexit_p(bcm63xx_wdt_remove), + .remove = bcm63xx_wdt_remove, .shutdown = bcm63xx_wdt_shutdown, .driver = { .owner = THIS_MODULE, @@ -311,11 +309,10 @@ static struct platform_driver bcm63xx_wdt = { } }; -module_platform_driver(bcm63xx_wdt); +module_platform_driver(bcm63xx_wdt_driver); 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/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c new file mode 100644 index 00000000000..9c248099f4a --- /dev/null +++ b/drivers/watchdog/bcm_kona_wdt.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2013 Broadcom Corporation + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +#define SECWDOG_CTRL_REG 0x00000000 +#define SECWDOG_COUNT_REG 0x00000004 + +#define SECWDOG_RESERVED_MASK 0x1dffffff +#define SECWDOG_WD_LOAD_FLAG 0x10000000 +#define SECWDOG_EN_MASK 0x08000000 +#define SECWDOG_SRSTEN_MASK 0x04000000 +#define SECWDOG_RES_MASK 0x00f00000 +#define SECWDOG_COUNT_MASK 0x000fffff + +#define SECWDOG_MAX_COUNT SECWDOG_COUNT_MASK +#define SECWDOG_CLKS_SHIFT 20 +#define SECWDOG_MAX_RES 15 +#define SECWDOG_DEFAULT_RESOLUTION 4 +#define SECWDOG_MAX_TRY 1000 + +#define SECS_TO_TICKS(x, w) ((x) << (w)->resolution) +#define TICKS_TO_SECS(x, w) ((x) >> (w)->resolution) + +#define BCM_KONA_WDT_NAME "bcm_kona_wdt" + +struct bcm_kona_wdt { + void __iomem *base; + /* + * One watchdog tick is 1/(2^resolution) seconds. Resolution can take + * the values 0-15, meaning one tick can be 1s to 30.52us. Our default + * resolution of 4 means one tick is 62.5ms. + * + * The watchdog counter is 20 bits. Depending on resolution, the maximum + * counter value of 0xfffff expires after about 12 days (resolution 0) + * down to only 32s (resolution 15). The default resolution of 4 gives + * us a maximum of about 18 hours and 12 minutes before the watchdog + * times out. + */ + int resolution; + spinlock_t lock; +#ifdef CONFIG_BCM_KONA_WDT_DEBUG + unsigned long busy_count; + struct dentry *debugfs; +#endif +}; + +static int secure_register_read(struct bcm_kona_wdt *wdt, uint32_t offset) +{ + uint32_t val; + unsigned count = 0; + + /* + * If the WD_LOAD_FLAG is set, the watchdog counter field is being + * updated in hardware. Once the WD timer is updated in hardware, it + * gets cleared. + */ + do { + if (unlikely(count > 1)) + udelay(5); + val = readl_relaxed(wdt->base + offset); + count++; + } while ((val & SECWDOG_WD_LOAD_FLAG) && count < SECWDOG_MAX_TRY); + +#ifdef CONFIG_BCM_KONA_WDT_DEBUG + /* Remember the maximum number iterations due to WD_LOAD_FLAG */ + if (count > wdt->busy_count) + wdt->busy_count = count; +#endif + + /* This is the only place we return a negative value. */ + if (val & SECWDOG_WD_LOAD_FLAG) + return -ETIMEDOUT; + + /* We always mask out reserved bits. */ + val &= SECWDOG_RESERVED_MASK; + + return val; +} + +#ifdef CONFIG_BCM_KONA_WDT_DEBUG + +static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data) +{ + int ctl_val, cur_val, ret; + unsigned long flags; + struct bcm_kona_wdt *wdt = s->private; + + if (!wdt) + return seq_puts(s, "No device pointer\n"); + + spin_lock_irqsave(&wdt->lock, flags); + ctl_val = secure_register_read(wdt, SECWDOG_CTRL_REG); + cur_val = secure_register_read(wdt, SECWDOG_COUNT_REG); + spin_unlock_irqrestore(&wdt->lock, flags); + + if (ctl_val < 0 || cur_val < 0) { + ret = seq_puts(s, "Error accessing hardware\n"); + } else { + int ctl, cur, ctl_sec, cur_sec, res; + + ctl = ctl_val & SECWDOG_COUNT_MASK; + res = (ctl_val & SECWDOG_RES_MASK) >> SECWDOG_CLKS_SHIFT; + cur = cur_val & SECWDOG_COUNT_MASK; + ctl_sec = TICKS_TO_SECS(ctl, wdt); + cur_sec = TICKS_TO_SECS(cur, wdt); + ret = seq_printf(s, "Resolution: %d / %d\n" + "Control: %d s / %d (%#x) ticks\n" + "Current: %d s / %d (%#x) ticks\n" + "Busy count: %lu\n", res, + wdt->resolution, ctl_sec, ctl, ctl, cur_sec, + cur, cur, wdt->busy_count); + } + + return ret; +} + +static int bcm_kona_dbg_open(struct inode *inode, struct file *file) +{ + return single_open(file, bcm_kona_wdt_dbg_show, inode->i_private); +} + +static const struct file_operations bcm_kona_dbg_operations = { + .open = bcm_kona_dbg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void bcm_kona_wdt_debug_init(struct platform_device *pdev) +{ + struct dentry *dir; + struct bcm_kona_wdt *wdt = platform_get_drvdata(pdev); + + if (!wdt) + return; + + wdt->debugfs = NULL; + + dir = debugfs_create_dir(BCM_KONA_WDT_NAME, NULL); + if (IS_ERR_OR_NULL(dir)) + return; + + if (debugfs_create_file("info", S_IFREG | S_IRUGO, dir, wdt, + &bcm_kona_dbg_operations)) + wdt->debugfs = dir; + else + debugfs_remove_recursive(dir); +} + +static void bcm_kona_wdt_debug_exit(struct platform_device *pdev) +{ + struct bcm_kona_wdt *wdt = platform_get_drvdata(pdev); + + if (wdt && wdt->debugfs) { + debugfs_remove_recursive(wdt->debugfs); + wdt->debugfs = NULL; + } +} + +#else + +static void bcm_kona_wdt_debug_init(struct platform_device *pdev) {} +static void bcm_kona_wdt_debug_exit(struct platform_device *pdev) {} + +#endif /* CONFIG_BCM_KONA_WDT_DEBUG */ + +static int bcm_kona_wdt_ctrl_reg_modify(struct bcm_kona_wdt *wdt, + unsigned mask, unsigned newval) +{ + int val; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&wdt->lock, flags); + + val = secure_register_read(wdt, SECWDOG_CTRL_REG); + if (val < 0) { + ret = val; + } else { + val &= ~mask; + val |= newval; + writel_relaxed(val, wdt->base + SECWDOG_CTRL_REG); + } + + spin_unlock_irqrestore(&wdt->lock, flags); + + return ret; +} + +static int bcm_kona_wdt_set_resolution_reg(struct bcm_kona_wdt *wdt) +{ + if (wdt->resolution > SECWDOG_MAX_RES) + return -EINVAL; + + return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_RES_MASK, + wdt->resolution << SECWDOG_CLKS_SHIFT); +} + +static int bcm_kona_wdt_set_timeout_reg(struct watchdog_device *wdog, + unsigned watchdog_flags) +{ + struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog); + + return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_COUNT_MASK, + SECS_TO_TICKS(wdog->timeout, wdt) | + watchdog_flags); +} + +static int bcm_kona_wdt_set_timeout(struct watchdog_device *wdog, + unsigned int t) +{ + wdog->timeout = t; + return 0; +} + +static unsigned int bcm_kona_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog); + int val; + unsigned long flags; + + spin_lock_irqsave(&wdt->lock, flags); + val = secure_register_read(wdt, SECWDOG_COUNT_REG); + spin_unlock_irqrestore(&wdt->lock, flags); + + if (val < 0) + return val; + + return TICKS_TO_SECS(val & SECWDOG_COUNT_MASK, wdt); +} + +static int bcm_kona_wdt_start(struct watchdog_device *wdog) +{ + return bcm_kona_wdt_set_timeout_reg(wdog, + SECWDOG_EN_MASK | SECWDOG_SRSTEN_MASK); +} + +static int bcm_kona_wdt_stop(struct watchdog_device *wdog) +{ + struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog); + + return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_EN_MASK | + SECWDOG_SRSTEN_MASK, 0); +} + +static struct watchdog_ops bcm_kona_wdt_ops = { + .owner = THIS_MODULE, + .start = bcm_kona_wdt_start, + .stop = bcm_kona_wdt_stop, + .set_timeout = bcm_kona_wdt_set_timeout, + .get_timeleft = bcm_kona_wdt_get_timeleft, +}; + +static struct watchdog_info bcm_kona_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .identity = "Broadcom Kona Watchdog Timer", +}; + +static struct watchdog_device bcm_kona_wdt_wdd = { + .info = &bcm_kona_wdt_info, + .ops = &bcm_kona_wdt_ops, + .min_timeout = 1, + .max_timeout = SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION, + .timeout = SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION, +}; + +static void bcm_kona_wdt_shutdown(struct platform_device *pdev) +{ + bcm_kona_wdt_stop(&bcm_kona_wdt_wdd); +} + +static int bcm_kona_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm_kona_wdt *wdt; + struct resource *res; + int ret; + + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->base = devm_ioremap_resource(dev, res); + if (IS_ERR(wdt->base)) + return -ENODEV; + + wdt->resolution = SECWDOG_DEFAULT_RESOLUTION; + ret = bcm_kona_wdt_set_resolution_reg(wdt); + if (ret) { + dev_err(dev, "Failed to set resolution (error: %d)", ret); + return ret; + } + + spin_lock_init(&wdt->lock); + platform_set_drvdata(pdev, wdt); + watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt); + + ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0); + if (ret) { + dev_err(dev, "Failed set watchdog timeout"); + return ret; + } + + ret = watchdog_register_device(&bcm_kona_wdt_wdd); + if (ret) { + dev_err(dev, "Failed to register watchdog device"); + return ret; + } + + bcm_kona_wdt_debug_init(pdev); + dev_dbg(dev, "Broadcom Kona Watchdog Timer"); + + return 0; +} + +static int bcm_kona_wdt_remove(struct platform_device *pdev) +{ + bcm_kona_wdt_debug_exit(pdev); + bcm_kona_wdt_shutdown(pdev); + watchdog_unregister_device(&bcm_kona_wdt_wdd); + dev_dbg(&pdev->dev, "Watchdog driver disabled"); + + return 0; +} + +static const struct of_device_id bcm_kona_wdt_of_match[] = { + { .compatible = "brcm,kona-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_kona_wdt_of_match); + +static struct platform_driver bcm_kona_wdt_driver = { + .driver = { + .name = BCM_KONA_WDT_NAME, + .owner = THIS_MODULE, + .of_match_table = bcm_kona_wdt_of_match, + }, + .probe = bcm_kona_wdt_probe, + .remove = bcm_kona_wdt_remove, + .shutdown = bcm_kona_wdt_shutdown, +}; + +module_platform_driver(bcm_kona_wdt_driver); + +MODULE_ALIAS("platform:" BCM_KONA_WDT_NAME); +MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom Kona Watchdog Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c index b9fa9b71583..a3b6a5b30f9 100644 --- a/drivers/watchdog/bfin_wdt.c +++ b/drivers/watchdog/bfin_wdt.c @@ -11,6 +11,8 @@ * Licensed under the GPL-2 or later. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/platform_device.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -28,15 +30,8 @@ #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. @@ -54,7 +49,7 @@ #define WATCHDOG_TIMEOUT 20 static unsigned int timeout = WATCHDOG_TIMEOUT; -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; static const struct watchdog_info bfin_wdt_info; static unsigned long open_check; static char expect_close; @@ -126,7 +121,7 @@ static int bfin_wdt_set_timeout(unsigned long t) 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"); + pr_warn("timeout value is too large\n"); return -EINVAL; } @@ -182,8 +177,7 @@ static int bfin_wdt_release(struct inode *inode, struct file *file) if (expect_close == 42) bfin_wdt_stop(); else { - printk(KERN_CRIT PFX - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); bfin_wdt_keepalive(); } expect_close = 0; @@ -362,20 +356,19 @@ static const struct watchdog_info bfin_wdt_info = { * Registers the misc device. Actual device * initialization is handled by bfin_wdt_open(). */ -static int __devinit bfin_wdt_probe(struct platform_device *pdev) +static int 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); + pr_err("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); + pr_info("initialized: timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); return 0; } @@ -386,7 +379,7 @@ static int __devinit bfin_wdt_probe(struct platform_device *pdev) * Unregisters the misc device. Actual device * deinitialization is handled by bfin_wdt_close(). */ -static int __devexit bfin_wdt_remove(struct platform_device *pdev) +static int bfin_wdt_remove(struct platform_device *pdev) { misc_deregister(&bfin_wdt_miscdev); return 0; @@ -408,7 +401,7 @@ static struct platform_device *bfin_wdt_device; static struct platform_driver bfin_wdt_driver = { .probe = bfin_wdt_probe, - .remove = __devexit_p(bfin_wdt_remove), + .remove = bfin_wdt_remove, .shutdown = bfin_wdt_shutdown, .suspend = bfin_wdt_suspend, .resume = bfin_wdt_resume, @@ -439,14 +432,14 @@ static int __init bfin_wdt_init(void) */ ret = platform_driver_register(&bfin_wdt_driver); if (ret) { - pr_init(KERN_ERR PFX "unable to register driver\n"); + pr_err("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"); + pr_err("unable to register device\n"); platform_driver_unregister(&bfin_wdt_driver); return PTR_ERR(bfin_wdt_device); } @@ -472,14 +465,13 @@ 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_param(nowayout, bool, 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 index 7c0fdfca264..08a785398ea 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -12,16 +12,13 @@ * option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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> @@ -36,7 +33,7 @@ u32 booke_wdt_enabled; u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; -#ifdef CONFIG_FSL_BOOKE +#ifdef CONFIG_PPC_FSL_BOOK3E #define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15)) #define WDTP_MASK (WDTP(0x3f)) #else @@ -44,7 +41,29 @@ u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; #define WDTP_MASK (TCR_WP_MASK) #endif -static DEFINE_SPINLOCK(booke_wdt_lock); +/* Checks wdt=x and wdt_period=xx command-line option */ +notrace int __init early_parse_wdt(char *p) +{ + if (p && strncmp(p, "0", 1) != 0) + booke_wdt_enabled = 1; + + return 0; +} +early_param("wdt", early_parse_wdt); + +int __init early_parse_wdt_period(char *p) +{ + unsigned long ret; + if (p) { + if (!kstrtol(p, 0, &ret)) + booke_wdt_period = ret; + } + + return 0; +} +early_param("wdt_period", early_parse_wdt_period); + +#ifdef CONFIG_PPC_FSL_BOOK3E /* For the specified period, determine the number of seconds * corresponding to the reset time. There will be a watchdog @@ -85,20 +104,39 @@ static unsigned int sec_to_period(unsigned int secs) return 0; } +#define MAX_WDT_TIMEOUT period_to_sec(1) + +#else /* CONFIG_PPC_FSL_BOOK3E */ + +static unsigned long long period_to_sec(unsigned int period) +{ + return period; +} + +static unsigned int sec_to_period(unsigned int secs) +{ + return secs; +} + +#define MAX_WDT_TIMEOUT 3 /* from Kconfig */ + +#endif /* !CONFIG_PPC_FSL_BOOK3E */ + static void __booke_wdt_set(void *data) { u32 val; + struct watchdog_device *wdog = data; val = mfspr(SPRN_TCR); val &= ~WDTP_MASK; - val |= WDTP(booke_wdt_period); + val |= WDTP(sec_to_period(wdog->timeout)); mtspr(SPRN_TCR, val); } -static void booke_wdt_set(void) +static void booke_wdt_set(void *data) { - on_each_cpu(__booke_wdt_set, NULL, 0); + on_each_cpu(__booke_wdt_set, data, 0); } static void __booke_wdt_ping(void *data) @@ -106,20 +144,23 @@ static void __booke_wdt_ping(void *data) mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); } -static void booke_wdt_ping(void) +static int booke_wdt_ping(struct watchdog_device *wdog) { on_each_cpu(__booke_wdt_ping, NULL, 0); + + return 0; } static void __booke_wdt_enable(void *data) { u32 val; + struct watchdog_device *wdog = data; /* 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)); + val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(sec_to_period(wdog->timeout))); mtspr(SPRN_TCR, val); } @@ -145,153 +186,72 @@ static void __booke_wdt_disable(void *data) } -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) +static int booke_wdt_start(struct watchdog_device *wdog) { - 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(); - /* Fall */ - case WDIOC_GETTIMEOUT: -#ifdef CONFIG_FSL_BOOKE - return put_user(period_to_sec(booke_wdt_period), p); -#else - return put_user(booke_wdt_period, p); -#endif - default: - return -ENOTTY; - } + on_each_cpu(__booke_wdt_enable, wdog, 0); + pr_debug("watchdog enabled (timeout = %u sec)\n", wdog->timeout); 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) +static int booke_wdt_stop(struct watchdog_device *wdog) { - /* /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); + on_each_cpu(__booke_wdt_disable, NULL, 0); + pr_debug("watchdog disabled\n"); - return nonseekable_open(inode, file); + return 0; } -static int booke_wdt_release(struct inode *inode, struct file *file) +static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) { -#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); + if (timeout > MAX_WDT_TIMEOUT) + return -EINVAL; + wdt_dev->timeout = timeout; + booke_wdt_set(wdt_dev); return 0; } -static const struct file_operations booke_wdt_fops = { +static struct watchdog_info booke_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "PowerPC Book-E Watchdog", +}; + +static struct watchdog_ops booke_wdt_ops = { .owner = THIS_MODULE, - .llseek = no_llseek, - .write = booke_wdt_write, - .unlocked_ioctl = booke_wdt_ioctl, - .open = booke_wdt_open, - .release = booke_wdt_release, + .start = booke_wdt_start, + .stop = booke_wdt_stop, + .ping = booke_wdt_ping, + .set_timeout = booke_wdt_set_timeout, }; -static struct miscdevice booke_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &booke_wdt_fops, +static struct watchdog_device booke_wdt_dev = { + .info = &booke_wdt_info, + .ops = &booke_wdt_ops, + .min_timeout = 1, + .max_timeout = 0xFFFF }; static void __exit booke_wdt_exit(void) { - misc_deregister(&booke_wdt_miscdev); + watchdog_unregister_device(&booke_wdt_dev); } static int __init booke_wdt_init(void) { int ret = 0; + bool nowayout = WATCHDOG_NOWAYOUT; - pr_info("booke_wdt: powerpc book-e watchdog driver loaded\n"); - ident.firmware_version = cur_cpu_spec->pvr_value; + pr_info("powerpc book-e watchdog driver loaded\n"); + booke_wdt_info.firmware_version = cur_cpu_spec->pvr_value; + booke_wdt_set_timeout(&booke_wdt_dev, + period_to_sec(booke_wdt_period)); + watchdog_set_nowayout(&booke_wdt_dev, nowayout); + if (booke_wdt_enabled) + booke_wdt_start(&booke_wdt_dev); - 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); + ret = watchdog_register_device(&booke_wdt_dev); return ret; } diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c index 5b89f7d6cd0..4bd070f524e 100644 --- a/drivers/watchdog/coh901327_wdt.c +++ b/drivers/watchdog/coh901327_wdt.c @@ -8,17 +8,15 @@ */ #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> +#include <linux/err.h> #define DRV_NAME "WDOG COH 901 327" @@ -69,13 +67,11 @@ #define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U /* Default timeout in seconds = 1 minute */ -static int margin = 60; +static unsigned 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 struct device *parent; /* @@ -155,34 +151,35 @@ static void coh901327_disable(void) __func__, val); } -static void coh901327_start(void) +static int coh901327_start(struct watchdog_device *wdt_dev) { - coh901327_enable(margin * 100); + coh901327_enable(wdt_dev->timeout * 100); + return 0; } -static void coh901327_keepalive(void) +static int coh901327_stop(struct watchdog_device *wdt_dev) +{ + coh901327_disable(); + return 0; +} + +static int coh901327_ping(struct watchdog_device *wdd) { clk_enable(clk); /* Feed the watchdog */ writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); clk_disable(clk); + return 0; } -static int coh901327_settimeout(int time) +static int coh901327_settimeout(struct watchdog_device *wdt_dev, + unsigned 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; + wdt_dev->timeout = time; clk_enable(clk); /* Set new timeout value */ - writew(margin * 100, virtbase + U300_WDOG_TR); + writew(time * 100, virtbase + U300_WDOG_TR); /* Feed the dog */ writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); @@ -190,6 +187,23 @@ static int coh901327_settimeout(int time) return 0; } +static unsigned int coh901327_gettimeleft(struct watchdog_device *wdt_dev) +{ + u16 val; + + 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; + + return val; +} + /* * This interrupt occurs 10 ms before the watchdog WILL bark. */ @@ -218,139 +232,44 @@ static irqreturn_t coh901327_interrupt(int irq, void *data) 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 watchdog_info coh901327_ident = { + .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = DRV_NAME, +}; -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 watchdog_ops coh901327_ops = { + .owner = THIS_MODULE, + .start = coh901327_start, + .stop = coh901327_stop, + .ping = coh901327_ping, + .set_timeout = coh901327_settimeout, + .get_timeleft = coh901327_gettimeleft, }; -static struct miscdevice coh901327_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &coh901327_fops, +static struct watchdog_device coh901327_wdt = { + .info = &coh901327_ident, + .ops = &coh901327_ops, + /* + * Max timeout is 327 since the 10ms + * timeout register is max + * 0x7FFF = 327670ms ~= 327s. + */ + .min_timeout = 0, + .max_timeout = 327, }; static int __exit coh901327_remove(struct platform_device *pdev) { - misc_deregister(&coh901327_miscdev); + watchdog_unregister_device(&coh901327_wdt); coh901327_disable(); free_irq(irq, pdev); + clk_unprepare(clk); clk_put(clk); iounmap(virtbase); release_mem_region(phybase, physize); return 0; } - static int __init coh901327_probe(struct platform_device *pdev) { int ret; @@ -382,9 +301,9 @@ static int __init coh901327_probe(struct platform_device *pdev) dev_err(&pdev->dev, "could not get clock\n"); goto out_no_clk; } - ret = clk_enable(clk); + ret = clk_prepare_enable(clk); if (ret) { - dev_err(&pdev->dev, "could not enable clock\n"); + dev_err(&pdev->dev, "could not prepare and enable clock\n"); goto out_no_clk_enable; } @@ -393,7 +312,7 @@ static int __init coh901327_probe(struct platform_device *pdev) case U300_WDOG_SR_STATUS_TIMED_OUT: dev_info(&pdev->dev, "watchdog timed out since last chip reset!\n"); - boot_status = WDIOF_CARDRESET; + coh901327_wdt.bootstatus |= WDIOF_CARDRESET; /* Status will be cleared below */ break; case U300_WDOG_SR_STATUS_NORMAL: @@ -435,7 +354,11 @@ static int __init coh901327_probe(struct platform_device *pdev) clk_disable(clk); - ret = misc_register(&coh901327_miscdev); + ret = watchdog_init_timeout(&coh901327_wdt, margin, &pdev->dev); + if (ret < 0) + coh901327_wdt.timeout = 60; + + ret = watchdog_register_device(&coh901327_wdt); if (ret == 0) dev_info(&pdev->dev, "initialized. timer margin=%d sec\n", margin); @@ -447,7 +370,7 @@ static int __init coh901327_probe(struct platform_device *pdev) out_no_wdog: free_irq(irq, pdev); out_no_irq: - clk_disable(clk); + clk_disable_unprepare(clk); out_no_clk_enable: clk_put(clk); out_no_clk: @@ -518,33 +441,29 @@ void coh901327_watchdog_reset(void) /* Return and await doom */ } +static const struct of_device_id coh901327_dt_match[] = { + { .compatible = "stericsson,coh901327" }, + {}, +}; + static struct platform_driver coh901327_driver = { .driver = { .owner = THIS_MODULE, .name = "coh901327_wdog", + .of_match_table = coh901327_dt_match, }, .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_platform_driver_probe(coh901327_driver, coh901327_probe); MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>"); MODULE_DESCRIPTION("COH 901 327 Watchdog"); -module_param(margin, int, 0); +module_param(margin, uint, 0); MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS("platform:coh901327-watchdog"); diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c index 251c863d71d..6d03e8e30f8 100644 --- a/drivers/watchdog/cpu5wdt.c +++ b/drivers/watchdog/cpu5wdt.c @@ -19,13 +19,14 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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> @@ -71,7 +72,7 @@ static struct { static void cpu5wdt_trigger(unsigned long unused) { if (verbose > 2) - printk(KERN_DEBUG PFX "trigger at %i ticks\n", ticks); + pr_debug("trigger at %i ticks\n", ticks); if (cpu5wdt_device.running) ticks--; @@ -96,7 +97,7 @@ static void cpu5wdt_reset(void) ticks = cpu5wdt_device.default_ticks; if (verbose) - printk(KERN_DEBUG PFX "reset (%i ticks)\n", (int) ticks); + pr_debug("reset (%i ticks)\n", (int) ticks); } @@ -129,7 +130,7 @@ static int cpu5wdt_stop(void) ticks = cpu5wdt_device.default_ticks; spin_unlock_irqrestore(&cpu5wdt_lock, flags); if (verbose) - printk(KERN_CRIT PFX "stop not possible\n"); + pr_crit("stop not possible\n"); return -EIO; } @@ -213,14 +214,13 @@ static struct miscdevice cpu5wdt_misc = { /* init/exit function */ -static int __devinit cpu5wdt_init(void) +static int cpu5wdt_init(void) { unsigned int val; int err; if (verbose) - printk(KERN_DEBUG PFX - "port=0x%x, verbose=%i\n", port, verbose); + pr_debug("port=0x%x, verbose=%i\n", port, verbose); init_completion(&cpu5wdt_device.stop); cpu5wdt_device.queue = 0; @@ -228,7 +228,7 @@ static int __devinit cpu5wdt_init(void) cpu5wdt_device.default_ticks = ticks; if (!request_region(port, CPU5WDT_EXTENT, PFX)) { - printk(KERN_ERR PFX "request_region failed\n"); + pr_err("request_region failed\n"); err = -EBUSY; goto no_port; } @@ -237,16 +237,16 @@ static int __devinit cpu5wdt_init(void) val = inb(port + CPU5WDT_STATUS_REG); val = (val >> 2) & 1; if (!val) - printk(KERN_INFO PFX "sorry, was my fault\n"); + pr_info("sorry, was my fault\n"); err = misc_register(&cpu5wdt_misc); if (err < 0) { - printk(KERN_ERR PFX "misc_register failed\n"); + pr_err("misc_register failed\n"); goto no_misc; } - printk(KERN_INFO PFX "init success\n"); + pr_info("init success\n"); return 0; no_misc: @@ -255,16 +255,17 @@ no_port: return err; } -static int __devinit cpu5wdt_init_module(void) +static int cpu5wdt_init_module(void) { return cpu5wdt_init(); } -static void __devexit cpu5wdt_exit(void) +static void cpu5wdt_exit(void) { if (cpu5wdt_device.queue) { cpu5wdt_device.queue = 0; wait_for_completion(&cpu5wdt_device.stop); + del_timer(&cpu5wdt_device.timer); } misc_deregister(&cpu5wdt_misc); @@ -273,7 +274,7 @@ static void __devexit cpu5wdt_exit(void) } -static void __devexit cpu5wdt_exit_module(void) +static void cpu5wdt_exit_module(void) { cpu5wdt_exit(); } @@ -287,7 +288,6 @@ 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"); diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index 1b793dfd868..e55ed702209 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c @@ -14,12 +14,13 @@ * Copyright (C) 2008 David S. Miller <davem@davemloft.net> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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> @@ -35,7 +36,6 @@ #include <asm/watchdog.h> #define DRIVER_NAME "cpwd" -#define PFX DRIVER_NAME ": " #define WD_OBPNAME "watchdog" #define WD_BADMODEL "SUNW,501-5336" @@ -385,8 +385,7 @@ static int cpwd_open(struct inode *inode, struct file *f) 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); + pr_err("Cannot register IRQ %d\n", p->irq); mutex_unlock(&cpwd_mutex); return -EBUSY; } @@ -411,7 +410,7 @@ static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) .identity = DRIVER_NAME, }; void __user *argp = (void __user *)arg; - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); int index = iminor(inode) - WD0_MINOR; struct cpwd *p = cpwd_device; int setopt = 0; @@ -499,7 +498,7 @@ static long cpwd_compat_ioctl(struct file *file, unsigned int cmd, 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 inode *inode = file_inode(file); struct cpwd *p = cpwd_device; int index = iminor(inode); @@ -528,7 +527,7 @@ static const struct file_operations cpwd_fops = { .llseek = no_llseek, }; -static int __devinit cpwd_probe(struct platform_device *op) +static int cpwd_probe(struct platform_device *op) { struct device_node *options; const char *str_prop; @@ -542,7 +541,7 @@ static int __devinit cpwd_probe(struct platform_device *op) p = kzalloc(sizeof(*p), GFP_KERNEL); err = -ENOMEM; if (!p) { - printk(KERN_ERR PFX "Unable to allocate struct cpwd.\n"); + pr_err("Unable to allocate struct cpwd\n"); goto out; } @@ -553,14 +552,14 @@ static int __devinit cpwd_probe(struct platform_device *op) 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"); + pr_err("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"); + pr_err("Unable to find /options node\n"); goto out_iounmap; } @@ -605,8 +604,8 @@ static int __devinit cpwd_probe(struct platform_device *op) err = misc_register(&p->devs[i].misc); if (err) { - printk(KERN_ERR "Could not register misc device for " - "dev %d\n", i); + pr_err("Could not register misc device for dev %d\n", + i); goto out_unregister; } } @@ -617,11 +616,11 @@ static int __devinit cpwd_probe(struct platform_device *op) cpwd_timer.data = (unsigned long) p; cpwd_timer.expires = WD_BTIMEOUT; - printk(KERN_INFO PFX "PLD defect workaround enabled for " - "model " WD_BADMODEL ".\n"); + pr_info("PLD defect workaround enabled for model %s\n", + WD_BADMODEL); } - dev_set_drvdata(&op->dev, p); + platform_set_drvdata(op, p); cpwd_device = p; err = 0; @@ -640,9 +639,9 @@ out_free: goto out; } -static int __devexit cpwd_remove(struct platform_device *op) +static int cpwd_remove(struct platform_device *op) { - struct cpwd *p = dev_get_drvdata(&op->dev); + struct cpwd *p = platform_get_drvdata(op); int i; for (i = 0; i < WD_NUMDEVS; i++) { @@ -684,7 +683,7 @@ static struct platform_driver cpwd_driver = { .of_match_table = cpwd_match, }, .probe = cpwd_probe, - .remove = __devexit_p(cpwd_remove), + .remove = cpwd_remove, }; module_platform_driver(cpwd_driver); diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c new file mode 100644 index 00000000000..2e9589652e1 --- /dev/null +++ b/drivers/watchdog/da9052_wdt.c @@ -0,0 +1,245 @@ +/* + * System monitoring driver for DA9052 PMICs. + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: Anthony Olech <Anthony.Olech@diasemi.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. + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/time.h> +#include <linux/watchdog.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/jiffies.h> + +#include <linux/mfd/da9052/reg.h> +#include <linux/mfd/da9052/da9052.h> + +#define DA9052_DEF_TIMEOUT 4 +#define DA9052_TWDMIN 256 + +struct da9052_wdt_data { + struct watchdog_device wdt; + struct da9052 *da9052; + struct kref kref; + unsigned long jpast; +}; + +static const struct { + u8 reg_val; + int time; /* Seconds */ +} da9052_wdt_maps[] = { + { 1, 2 }, + { 2, 4 }, + { 3, 8 }, + { 4, 16 }, + { 5, 32 }, + { 5, 33 }, /* Actual time 32.768s so included both 32s and 33s */ + { 6, 65 }, + { 6, 66 }, /* Actual time 65.536s so include both, 65s and 66s */ + { 7, 131 }, +}; + + +static void da9052_wdt_release_resources(struct kref *r) +{ +} + +static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); + struct da9052 *da9052 = driver_data->da9052; + int ret, i; + + /* + * Disable the Watchdog timer before setting + * new time out. + */ + ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, + DA9052_CONTROLD_TWDSCALE, 0); + if (ret < 0) { + dev_err(da9052->dev, "Failed to disable watchdog bit, %d\n", + ret); + return ret; + } + if (timeout) { + /* + * To change the timeout, da9052 needs to + * be disabled for at least 150 us. + */ + udelay(150); + + /* Set the desired timeout */ + for (i = 0; i < ARRAY_SIZE(da9052_wdt_maps); i++) + if (da9052_wdt_maps[i].time == timeout) + break; + + if (i == ARRAY_SIZE(da9052_wdt_maps)) + ret = -EINVAL; + else + ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, + DA9052_CONTROLD_TWDSCALE, + da9052_wdt_maps[i].reg_val); + if (ret < 0) { + dev_err(da9052->dev, + "Failed to update timescale bit, %d\n", ret); + return ret; + } + + wdt_dev->timeout = timeout; + driver_data->jpast = jiffies; + } + + return 0; +} + +static void da9052_wdt_ref(struct watchdog_device *wdt_dev) +{ + struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); + + kref_get(&driver_data->kref); +} + +static void da9052_wdt_unref(struct watchdog_device *wdt_dev) +{ + struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); + + kref_put(&driver_data->kref, da9052_wdt_release_resources); +} + +static int da9052_wdt_start(struct watchdog_device *wdt_dev) +{ + return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout); +} + +static int da9052_wdt_stop(struct watchdog_device *wdt_dev) +{ + return da9052_wdt_set_timeout(wdt_dev, 0); +} + +static int da9052_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); + struct da9052 *da9052 = driver_data->da9052; + unsigned long msec, jnow = jiffies; + int ret; + + /* + * We have a minimum time for watchdog window called TWDMIN. A write + * to the watchdog before this elapsed time should cause an error. + */ + msec = (jnow - driver_data->jpast) * 1000/HZ; + if (msec < DA9052_TWDMIN) + mdelay(msec); + + /* Reset the watchdog timer */ + ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, + DA9052_CONTROLD_WATCHDOG, 1 << 7); + if (ret < 0) + goto err_strobe; + + /* + * FIXME: Reset the watchdog core, in general PMIC + * is supposed to do this + */ + ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, + DA9052_CONTROLD_WATCHDOG, 0 << 7); +err_strobe: + return ret; +} + +static struct watchdog_info da9052_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "DA9052 Watchdog", +}; + +static const struct watchdog_ops da9052_wdt_ops = { + .owner = THIS_MODULE, + .start = da9052_wdt_start, + .stop = da9052_wdt_stop, + .ping = da9052_wdt_ping, + .set_timeout = da9052_wdt_set_timeout, + .ref = da9052_wdt_ref, + .unref = da9052_wdt_unref, +}; + + +static int da9052_wdt_probe(struct platform_device *pdev) +{ + struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); + struct da9052_wdt_data *driver_data; + struct watchdog_device *da9052_wdt; + int ret; + + driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), + GFP_KERNEL); + if (!driver_data) { + ret = -ENOMEM; + goto err; + } + driver_data->da9052 = da9052; + + da9052_wdt = &driver_data->wdt; + + da9052_wdt->timeout = DA9052_DEF_TIMEOUT; + da9052_wdt->info = &da9052_wdt_info; + da9052_wdt->ops = &da9052_wdt_ops; + watchdog_set_drvdata(da9052_wdt, driver_data); + + kref_init(&driver_data->kref); + + ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, + DA9052_CONTROLD_TWDSCALE, 0); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to disable watchdog bits, %d\n", + ret); + goto err; + } + + ret = watchdog_register_device(&driver_data->wdt); + if (ret != 0) { + dev_err(da9052->dev, "watchdog_register_device() failed: %d\n", + ret); + goto err; + } + + platform_set_drvdata(pdev, driver_data); +err: + return ret; +} + +static int da9052_wdt_remove(struct platform_device *pdev) +{ + struct da9052_wdt_data *driver_data = platform_get_drvdata(pdev); + + watchdog_unregister_device(&driver_data->wdt); + kref_put(&driver_data->kref, da9052_wdt_release_resources); + + return 0; +} + +static struct platform_driver da9052_wdt_driver = { + .probe = da9052_wdt_probe, + .remove = da9052_wdt_remove, + .driver = { + .name = "da9052-watchdog", + }, +}; + +module_platform_driver(da9052_wdt_driver); + +MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>"); +MODULE_DESCRIPTION("DA9052 SM Device Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-watchdog"); diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c new file mode 100644 index 00000000000..495089d8dbf --- /dev/null +++ b/drivers/watchdog/da9055_wdt.c @@ -0,0 +1,209 @@ +/* + * System monitoring driver for DA9055 PMICs. + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen <dchen@diasemi.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. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> +#include <linux/delay.h> + +#include <linux/mfd/da9055/core.h> +#include <linux/mfd/da9055/reg.h> + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +#define DA9055_DEF_TIMEOUT 4 +#define DA9055_TWDMIN 256 + +struct da9055_wdt_data { + struct watchdog_device wdt; + struct da9055 *da9055; + struct kref kref; +}; + +static const struct { + u8 reg_val; + int user_time; /* In seconds */ +} da9055_wdt_maps[] = { + { 0, 0 }, + { 1, 2 }, + { 2, 4 }, + { 3, 8 }, + { 4, 16 }, + { 5, 32 }, + { 5, 33 }, /* Actual time 32.768s so included both 32s and 33s */ + { 6, 65 }, + { 6, 66 }, /* Actual time 65.536s so include both, 65s and 66s */ + { 7, 131 }, +}; + +static int da9055_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); + struct da9055 *da9055 = driver_data->da9055; + int ret, i; + + for (i = 0; i < ARRAY_SIZE(da9055_wdt_maps); i++) + if (da9055_wdt_maps[i].user_time == timeout) + break; + + if (i == ARRAY_SIZE(da9055_wdt_maps)) + ret = -EINVAL; + else + ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B, + DA9055_TWDSCALE_MASK, + da9055_wdt_maps[i].reg_val << + DA9055_TWDSCALE_SHIFT); + if (ret < 0) { + dev_err(da9055->dev, + "Failed to update timescale bit, %d\n", ret); + return ret; + } + + wdt_dev->timeout = timeout; + + return 0; +} + +static int da9055_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); + struct da9055 *da9055 = driver_data->da9055; + + /* + * We have a minimum time for watchdog window called TWDMIN. A write + * to the watchdog before this elapsed time will cause an error. + */ + mdelay(DA9055_TWDMIN); + + /* Reset the watchdog timer */ + return da9055_reg_update(da9055, DA9055_REG_CONTROL_E, + DA9055_WATCHDOG_MASK, 1); +} + +static void da9055_wdt_release_resources(struct kref *r) +{ +} + +static void da9055_wdt_ref(struct watchdog_device *wdt_dev) +{ + struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); + + kref_get(&driver_data->kref); +} + +static void da9055_wdt_unref(struct watchdog_device *wdt_dev) +{ + struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); + + kref_put(&driver_data->kref, da9055_wdt_release_resources); +} + +static int da9055_wdt_start(struct watchdog_device *wdt_dev) +{ + return da9055_wdt_set_timeout(wdt_dev, wdt_dev->timeout); +} + +static int da9055_wdt_stop(struct watchdog_device *wdt_dev) +{ + return da9055_wdt_set_timeout(wdt_dev, 0); +} + +static struct watchdog_info da9055_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "DA9055 Watchdog", +}; + +static const struct watchdog_ops da9055_wdt_ops = { + .owner = THIS_MODULE, + .start = da9055_wdt_start, + .stop = da9055_wdt_stop, + .ping = da9055_wdt_ping, + .set_timeout = da9055_wdt_set_timeout, + .ref = da9055_wdt_ref, + .unref = da9055_wdt_unref, +}; + +static int da9055_wdt_probe(struct platform_device *pdev) +{ + struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent); + struct da9055_wdt_data *driver_data; + struct watchdog_device *da9055_wdt; + int ret; + + driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), + GFP_KERNEL); + if (!driver_data) + return -ENOMEM; + + driver_data->da9055 = da9055; + + da9055_wdt = &driver_data->wdt; + + da9055_wdt->timeout = DA9055_DEF_TIMEOUT; + da9055_wdt->info = &da9055_wdt_info; + da9055_wdt->ops = &da9055_wdt_ops; + watchdog_set_nowayout(da9055_wdt, nowayout); + watchdog_set_drvdata(da9055_wdt, driver_data); + + kref_init(&driver_data->kref); + + ret = da9055_wdt_stop(da9055_wdt); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret); + goto err; + } + + platform_set_drvdata(pdev, driver_data); + + ret = watchdog_register_device(&driver_data->wdt); + if (ret != 0) + dev_err(da9055->dev, "watchdog_register_device() failed: %d\n", + ret); + +err: + return ret; +} + +static int da9055_wdt_remove(struct platform_device *pdev) +{ + struct da9055_wdt_data *driver_data = platform_get_drvdata(pdev); + + watchdog_unregister_device(&driver_data->wdt); + kref_put(&driver_data->kref, da9055_wdt_release_resources); + + return 0; +} + +static struct platform_driver da9055_wdt_driver = { + .probe = da9055_wdt_probe, + .remove = da9055_wdt_remove, + .driver = { + .name = "da9055-watchdog", + }, +}; + +module_platform_driver(da9055_wdt_driver); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_DESCRIPTION("DA9055 watchdog"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9055-watchdog"); diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index c8c5c8032bc..d09ad2254b5 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -3,7 +3,7 @@ * * Watchdog driver for DaVinci DM644x/DM646x processors * - * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2006-2013 Texas Instruments. * * 2007 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program @@ -15,18 +15,12 @@ #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 <linux/err.h> #define MODULE_NAME "DAVINCI-WDT: " @@ -60,215 +54,175 @@ #define WDKEY_SEQ0 (0xa5c6 << 16) #define WDKEY_SEQ1 (0xda7e << 16) -static int heartbeat = DEFAULT_HEARTBEAT; +static int 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); -} +/* + * struct to hold data for each WDT device + * @base - base io address of WD device + * @clk - source clock of WDT + * @wdd - hold watchdog device as is in WDT core + */ +struct davinci_wdt_device { + void __iomem *base; + struct clk *clk; + struct watchdog_device wdd; +}; -static void wdt_enable(void) +static int davinci_wdt_start(struct watchdog_device *wdd) { u32 tgcr; u32 timer_margin; unsigned long wdt_freq; + struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd); - wdt_freq = clk_get_rate(wdt_clk); - - spin_lock(&io_lock); + wdt_freq = clk_get_rate(davinci_wdt->clk); /* disable, internal clock source */ - iowrite32(0, wdt_base + TCR); + iowrite32(0, davinci_wdt->base + TCR); /* reset timer, set mode to 64-bit watchdog, and unreset */ - iowrite32(0, wdt_base + TGCR); + iowrite32(0, davinci_wdt->base + TGCR); tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET; - iowrite32(tgcr, wdt_base + TGCR); + iowrite32(tgcr, davinci_wdt->base + TGCR); /* clear counter regs */ - iowrite32(0, wdt_base + TIM12); - iowrite32(0, wdt_base + TIM34); + iowrite32(0, davinci_wdt->base + TIM12); + iowrite32(0, davinci_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); + timer_margin = (((u64)wdd->timeout * wdt_freq) & 0xffffffff); + iowrite32(timer_margin, davinci_wdt->base + PRD12); + timer_margin = (((u64)wdd->timeout * wdt_freq) >> 32); + iowrite32(timer_margin, davinci_wdt->base + PRD34); /* enable run continuously */ - iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR); + iowrite32(ENAMODE12_PERIODIC, davinci_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); + iowrite32(WDKEY_SEQ0 | WDEN, davinci_wdt->base + WDTCR); /* put watchdog in active state */ - iowrite32(WDKEY_SEQ1 | WDEN, wdt_base + WDTCR); - - spin_unlock(&io_lock); + iowrite32(WDKEY_SEQ1 | WDEN, davinci_wdt->base + WDTCR); + return 0; } -static int davinci_wdt_open(struct inode *inode, struct file *file) +static int davinci_wdt_ping(struct watchdog_device *wdd) { - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - wdt_enable(); + struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd); - return nonseekable_open(inode, file); + /* put watchdog in service state */ + iowrite32(WDKEY_SEQ0, davinci_wdt->base + WDTCR); + /* put watchdog in active state */ + iowrite32(WDKEY_SEQ1, davinci_wdt->base + WDTCR); + return 0; } -static ssize_t -davinci_wdt_write(struct file *file, const char *data, size_t len, - loff_t *ppos) +static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd) { - if (len) - wdt_service(); + u64 timer_counter; + unsigned long freq; + u32 val; + struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd); - return len; -} + /* if timeout has occured then return 0 */ + val = ioread32(davinci_wdt->base + WDTCR); + if (val & WDFLAG) + return 0; -static const struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING, - .identity = "DaVinci Watchdog", -}; + freq = clk_get_rate(davinci_wdt->clk); -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; -} + if (!freq) + return 0; -static int davinci_wdt_release(struct inode *inode, struct file *file) -{ - wdt_service(); - clear_bit(WDT_IN_USE, &wdt_status); + timer_counter = ioread32(davinci_wdt->base + TIM12); + timer_counter |= ((u64)ioread32(davinci_wdt->base + TIM34) << 32); - return 0; + do_div(timer_counter, freq); + + return wdd->timeout - timer_counter; } -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 const struct watchdog_info davinci_wdt_info = { + .options = WDIOF_KEEPALIVEPING, + .identity = "DaVinci/Keystone Watchdog", }; -static struct miscdevice davinci_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &davinci_wdt_fops, +static const struct watchdog_ops davinci_wdt_ops = { + .owner = THIS_MODULE, + .start = davinci_wdt_start, + .stop = davinci_wdt_ping, + .ping = davinci_wdt_ping, + .get_timeleft = davinci_wdt_get_timeleft, }; -static int __devinit davinci_wdt_probe(struct platform_device *pdev) +static int davinci_wdt_probe(struct platform_device *pdev) { - int ret = 0, size; + int ret = 0; struct device *dev = &pdev->dev; + struct resource *wdt_mem; + struct watchdog_device *wdd; + struct davinci_wdt_device *davinci_wdt; - wdt_clk = clk_get(dev, NULL); - if (WARN_ON(IS_ERR(wdt_clk))) - return PTR_ERR(wdt_clk); + davinci_wdt = devm_kzalloc(dev, sizeof(*davinci_wdt), GFP_KERNEL); + if (!davinci_wdt) + return -ENOMEM; - clk_enable(wdt_clk); + davinci_wdt->clk = devm_clk_get(dev, NULL); + if (WARN_ON(IS_ERR(davinci_wdt->clk))) + return PTR_ERR(davinci_wdt->clk); - if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) - heartbeat = DEFAULT_HEARTBEAT; + clk_prepare_enable(davinci_wdt->clk); - dev_info(dev, "heartbeat %d sec\n", heartbeat); + platform_set_drvdata(pdev, davinci_wdt); + + wdd = &davinci_wdt->wdd; + wdd->info = &davinci_wdt_info; + wdd->ops = &davinci_wdt_ops; + wdd->min_timeout = 1; + wdd->max_timeout = MAX_HEARTBEAT; + wdd->timeout = DEFAULT_HEARTBEAT; + + watchdog_init_timeout(wdd, heartbeat, dev); + + dev_info(dev, "heartbeat %d sec\n", wdd->timeout); + + watchdog_set_drvdata(wdd, davinci_wdt); + watchdog_set_nowayout(wdd, 1); 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); + davinci_wdt->base = devm_ioremap_resource(dev, wdt_mem); + if (IS_ERR(davinci_wdt->base)) + return PTR_ERR(davinci_wdt->base); + + ret = watchdog_register_device(wdd); + if (ret < 0) + dev_err(dev, "cannot register watchdog device\n"); + return ret; } -static int __devexit davinci_wdt_remove(struct platform_device *pdev) +static int 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; - } + struct davinci_wdt_device *davinci_wdt = platform_get_drvdata(pdev); - clk_disable(wdt_clk); - clk_put(wdt_clk); + watchdog_unregister_device(&davinci_wdt->wdd); + clk_disable_unprepare(davinci_wdt->clk); return 0; } +static const struct of_device_id davinci_wdt_of_match[] = { + { .compatible = "ti,davinci-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, davinci_wdt_of_match); + static struct platform_driver platform_wdt_driver = { .driver = { - .name = "watchdog", + .name = "davinci-wdt", .owner = THIS_MODULE, + .of_match_table = davinci_wdt_of_match, }, .probe = davinci_wdt_probe, - .remove = __devexit_p(davinci_wdt_remove), + .remove = davinci_wdt_remove, }; module_platform_driver(platform_wdt_driver); @@ -283,5 +237,4 @@ MODULE_PARM_DESC(heartbeat, __MODULE_STRING(DEFAULT_HEARTBEAT)); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -MODULE_ALIAS("platform:watchdog"); +MODULE_ALIAS("platform:davinci-wdt"); diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c new file mode 100644 index 00000000000..429494b6c82 --- /dev/null +++ b/drivers/watchdog/diag288_wdt.c @@ -0,0 +1,316 @@ +/* + * Watchdog driver for z/VM and LPAR using the diag 288 interface. + * + * Under z/VM, expiration of the watchdog will send a "system restart" command + * to CP. + * + * The command can be altered using the module parameter "cmd". This is + * not recommended because it's only supported on z/VM but not whith LPAR. + * + * On LPAR, the watchdog will always trigger a system restart. the module + * paramter cmd is meaningless here. + * + * + * Copyright IBM Corp. 2004, 2013 + * Author(s): Arnd Bergmann (arndb@de.ibm.com) + * Philipp Hachtmann (phacht@de.ibm.com) + * + */ + +#define KMSG_COMPONENT "diag288_wdt" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/suspend.h> +#include <asm/ebcdic.h> +#include <linux/io.h> +#include <linux/uaccess.h> + +#define MAX_CMDLEN 240 +#define DEFAULT_CMD "SYSTEM RESTART" + +#define MIN_INTERVAL 15 /* Minimal time supported by diag88 */ +#define MAX_INTERVAL 3600 /* One hour should be enough - pure estimation */ + +#define WDT_DEFAULT_TIMEOUT 30 + +/* Function codes - init, change, cancel */ +#define WDT_FUNC_INIT 0 +#define WDT_FUNC_CHANGE 1 +#define WDT_FUNC_CANCEL 2 +#define WDT_FUNC_CONCEAL 0x80000000 + +/* Action codes for LPAR watchdog */ +#define LPARWDT_RESTART 0 + +static char wdt_cmd[MAX_CMDLEN] = DEFAULT_CMD; +static bool conceal_on; +static bool nowayout_info = WATCHDOG_NOWAYOUT; + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); +MODULE_AUTHOR("Philipp Hachtmann <phacht@de.ibm.com>"); + +MODULE_DESCRIPTION("System z diag288 Watchdog Timer"); + +module_param_string(cmd, wdt_cmd, MAX_CMDLEN, 0644); +MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers (z/VM only)"); + +module_param_named(conceal, conceal_on, bool, 0644); +MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is active (z/VM only)"); + +module_param_named(nowayout, nowayout_info, bool, 0444); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)"); + +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS("vmwatchdog"); + +static int __diag288(unsigned int func, unsigned int timeout, + unsigned long action, unsigned int len) +{ + register unsigned long __func asm("2") = func; + register unsigned long __timeout asm("3") = timeout; + register unsigned long __action asm("4") = action; + register unsigned long __len asm("5") = len; + int err; + + err = -EINVAL; + asm volatile( + " diag %1, %3, 0x288\n" + "0: la %0, 0\n" + "1:\n" + EX_TABLE(0b, 1b) + : "+d" (err) : "d"(__func), "d"(__timeout), + "d"(__action), "d"(__len) : "1", "cc"); + return err; +} + +static int __diag288_vm(unsigned int func, unsigned int timeout, + char *cmd, size_t len) +{ + return __diag288(func, timeout, virt_to_phys(cmd), len); +} + +static int __diag288_lpar(unsigned int func, unsigned int timeout, + unsigned long action) +{ + return __diag288(func, timeout, action, 0); +} + +static int wdt_start(struct watchdog_device *dev) +{ + char *ebc_cmd; + size_t len; + int ret; + unsigned int func; + + ret = -ENODEV; + + if (MACHINE_IS_VM) { + ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); + if (!ebc_cmd) + return -ENOMEM; + len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN); + ASCEBC(ebc_cmd, MAX_CMDLEN); + EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); + + func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) + : WDT_FUNC_INIT; + ret = __diag288_vm(func, dev->timeout, ebc_cmd, len); + WARN_ON(ret != 0); + kfree(ebc_cmd); + } + + if (MACHINE_IS_LPAR) { + ret = __diag288_lpar(WDT_FUNC_INIT, + dev->timeout, LPARWDT_RESTART); + } + + if (ret) { + pr_err("The watchdog cannot be activated\n"); + return ret; + } + pr_info("The watchdog was activated\n"); + return 0; +} + +static int wdt_stop(struct watchdog_device *dev) +{ + int ret; + + ret = __diag288(WDT_FUNC_CANCEL, 0, 0, 0); + pr_info("The watchdog was deactivated\n"); + return ret; +} + +static int wdt_ping(struct watchdog_device *dev) +{ + char *ebc_cmd; + size_t len; + int ret; + unsigned int func; + + ret = -ENODEV; + + if (MACHINE_IS_VM) { + ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); + if (!ebc_cmd) + return -ENOMEM; + len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN); + ASCEBC(ebc_cmd, MAX_CMDLEN); + EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); + + /* + * It seems to be ok to z/VM to use the init function to + * retrigger the watchdog. On LPAR WDT_FUNC_CHANGE must + * be used when the watchdog is running. + */ + func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) + : WDT_FUNC_INIT; + + ret = __diag288_vm(func, dev->timeout, ebc_cmd, len); + WARN_ON(ret != 0); + kfree(ebc_cmd); + } + + if (MACHINE_IS_LPAR) + ret = __diag288_lpar(WDT_FUNC_CHANGE, dev->timeout, 0); + + if (ret) + pr_err("The watchdog timer cannot be started or reset\n"); + return ret; +} + +static int wdt_set_timeout(struct watchdog_device * dev, unsigned int new_to) +{ + dev->timeout = new_to; + return wdt_ping(dev); +} + +static struct watchdog_ops wdt_ops = { + .owner = THIS_MODULE, + .start = wdt_start, + .stop = wdt_stop, + .ping = wdt_ping, + .set_timeout = wdt_set_timeout, +}; + +static struct watchdog_info wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "z Watchdog", +}; + +static struct watchdog_device wdt_dev = { + .parent = NULL, + .info = &wdt_info, + .ops = &wdt_ops, + .bootstatus = 0, + .timeout = WDT_DEFAULT_TIMEOUT, + .min_timeout = MIN_INTERVAL, + .max_timeout = MAX_INTERVAL, +}; + +/* + * It makes no sense to go into suspend while the watchdog is running. + * Depending on the memory size, the watchdog might trigger, while we + * are still saving the memory. + * We reuse the open flag to ensure that suspend and watchdog open are + * exclusive operations + */ +static int wdt_suspend(void) +{ + if (test_and_set_bit(WDOG_DEV_OPEN, &wdt_dev.status)) { + pr_err("Linux cannot be suspended while the watchdog is in use\n"); + return notifier_from_errno(-EBUSY); + } + if (test_bit(WDOG_ACTIVE, &wdt_dev.status)) { + clear_bit(WDOG_DEV_OPEN, &wdt_dev.status); + pr_err("Linux cannot be suspended while the watchdog is in use\n"); + return notifier_from_errno(-EBUSY); + } + return NOTIFY_DONE; +} + +static int wdt_resume(void) +{ + clear_bit(WDOG_DEV_OPEN, &wdt_dev.status); + return NOTIFY_DONE; +} + +static int wdt_power_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + switch (event) { + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + return wdt_resume(); + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + return wdt_suspend(); + default: + return NOTIFY_DONE; + } +} + +static struct notifier_block wdt_power_notifier = { + .notifier_call = wdt_power_event, +}; + +static int __init diag288_init(void) +{ + int ret; + char ebc_begin[] = { + 194, 197, 199, 201, 213 + }; + + watchdog_set_nowayout(&wdt_dev, nowayout_info); + + if (MACHINE_IS_VM) { + pr_info("The watchdog device driver detected a z/VM environment\n"); + if (__diag288_vm(WDT_FUNC_INIT, 15, + ebc_begin, sizeof(ebc_begin)) != 0) { + pr_err("The watchdog cannot be initialized\n"); + return -EINVAL; + } + } else if (MACHINE_IS_LPAR) { + pr_info("The watchdog device driver detected an LPAR environment\n"); + if (__diag288_lpar(WDT_FUNC_INIT, 30, LPARWDT_RESTART)) { + pr_err("The watchdog cannot be initialized\n"); + return -EINVAL; + } + } else { + pr_err("Linux runs in an environment that does not support the diag288 watchdog\n"); + return -ENODEV; + } + + if (__diag288_lpar(WDT_FUNC_CANCEL, 0, 0)) { + pr_err("The watchdog cannot be deactivated\n"); + return -EINVAL; + } + + ret = register_pm_notifier(&wdt_power_notifier); + if (ret) + return ret; + + ret = watchdog_register_device(&wdt_dev); + if (ret) + unregister_pm_notifier(&wdt_power_notifier); + + return ret; +} + +static void __exit diag288_exit(void) +{ + watchdog_unregister_device(&wdt_dev); + unregister_pm_notifier(&wdt_power_notifier); +} + +module_init(diag288_init); +module_exit(diag288_exit); diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 63d7b58f1c7..ee4f86ba83e 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -8,7 +8,7 @@ * 2 of the License, or (at your option) any later version. * * This file implements a driver for the Synopsys DesignWare watchdog device - * in the many ARM subsystems. The watchdog has 16 different timeout periods + * in the many subsystems. The watchdog has 16 different timeout periods * and these are a function of the input clock frequency. * * The DesignWare watchdog cannot be stopped once it has been started so we @@ -16,7 +16,8 @@ * If we receive an expected close for the watchdog then we keep the timer * running, otherwise the timer is stopped and the watchdog will expire. */ -#define pr_fmt(fmt) "dw_wdt: " fmt + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/bitops.h> #include <linux/clk.h> @@ -28,6 +29,7 @@ #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/of.h> #include <linux/pm.h> #include <linux/platform_device.h> #include <linux/spinlock.h> @@ -45,8 +47,8 @@ /* The maximum TOP (timeout period) value that can be set in the watchdog. */ #define DW_WDT_MAX_TOP 15 -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -153,8 +155,8 @@ static int dw_wdt_open(struct inode *inode, struct file *filp) return nonseekable_open(inode, filp); } -ssize_t dw_wdt_write(struct file *filp, const char __user *buf, size_t len, - loff_t *offset) +static ssize_t dw_wdt_write(struct file *filp, const char __user *buf, + size_t len, loff_t *offset) { if (!len) return 0; @@ -202,12 +204,12 @@ static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case WDIOC_GETSUPPORT: - return copy_to_user((struct watchdog_info *)arg, &dw_wdt_ident, + return copy_to_user((void __user *)arg, &dw_wdt_ident, sizeof(dw_wdt_ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: - return put_user(0, (int *)arg); + return put_user(0, (int __user *)arg); case WDIOC_KEEPALIVE: dw_wdt_set_next_heartbeat(); @@ -251,17 +253,17 @@ static int dw_wdt_release(struct inode *inode, struct file *filp) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int dw_wdt_suspend(struct device *dev) { - clk_disable(dw_wdt.clk); + clk_disable_unprepare(dw_wdt.clk); return 0; } static int dw_wdt_resume(struct device *dev) { - int err = clk_enable(dw_wdt.clk); + int err = clk_prepare_enable(dw_wdt.clk); if (err) return err; @@ -270,12 +272,9 @@ static int dw_wdt_resume(struct device *dev) return 0; } +#endif /* CONFIG_PM_SLEEP */ -static const struct dev_pm_ops dw_wdt_pm_ops = { - .suspend = dw_wdt_suspend, - .resume = dw_wdt_resume, -}; -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); static const struct file_operations wdt_fops = { .owner = THIS_MODULE, @@ -292,7 +291,7 @@ static struct miscdevice dw_wdt_miscdev = { .minor = WATCHDOG_MINOR, }; -static int __devinit dw_wdt_drv_probe(struct platform_device *pdev) +static int dw_wdt_drv_probe(struct platform_device *pdev) { int ret; struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -300,17 +299,17 @@ static int __devinit dw_wdt_drv_probe(struct platform_device *pdev) if (!mem) return -EINVAL; - dw_wdt.regs = devm_request_and_ioremap(&pdev->dev, mem); - if (!dw_wdt.regs) - return -ENOMEM; + dw_wdt.regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(dw_wdt.regs)) + return PTR_ERR(dw_wdt.regs); - dw_wdt.clk = clk_get(&pdev->dev, NULL); + dw_wdt.clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dw_wdt.clk)) return PTR_ERR(dw_wdt.clk); - ret = clk_enable(dw_wdt.clk); + ret = clk_prepare_enable(dw_wdt.clk); if (ret) - goto out_put_clk; + return ret; spin_lock_init(&dw_wdt.lock); @@ -325,32 +324,36 @@ static int __devinit dw_wdt_drv_probe(struct platform_device *pdev) return 0; out_disable_clk: - clk_disable(dw_wdt.clk); -out_put_clk: - clk_put(dw_wdt.clk); + clk_disable_unprepare(dw_wdt.clk); return ret; } -static int __devexit dw_wdt_drv_remove(struct platform_device *pdev) +static int dw_wdt_drv_remove(struct platform_device *pdev) { misc_deregister(&dw_wdt_miscdev); - clk_disable(dw_wdt.clk); - clk_put(dw_wdt.clk); + clk_disable_unprepare(dw_wdt.clk); return 0; } +#ifdef CONFIG_OF +static const struct of_device_id dw_wdt_of_match[] = { + { .compatible = "snps,dw-wdt", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dw_wdt_of_match); +#endif + static struct platform_driver dw_wdt_driver = { .probe = dw_wdt_drv_probe, - .remove = __devexit_p(dw_wdt_drv_remove), + .remove = dw_wdt_drv_remove, .driver = { .name = "dw_wdt", .owner = THIS_MODULE, -#ifdef CONFIG_PM + .of_match_table = of_match_ptr(dw_wdt_of_match), .pm = &dw_wdt_pm_ops, -#endif /* CONFIG_PM */ }, }; @@ -359,4 +362,3 @@ module_platform_driver(dw_wdt_driver); MODULE_AUTHOR("Jamie Iles"); MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index 726b7df61fd..5f54e1e5819 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c @@ -8,6 +8,9 @@ * Authors: Ray Lehtiniemi <rayl@mail.com>, * Alessandro Zummo <a.zummo@towertech.it> * + * Copyright (c) 2012 H Hartley Sweeten <hsweeten@visionengravers.com> + * Convert to a platform device and use the watchdog framework API + * * 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. @@ -23,233 +26,148 @@ * - Add a few missing ioctls */ +#include <linux/platform_device.h> #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: " +#define WDT_VERSION "0.4" /* default timeout (secs) */ #define WDT_TIMEOUT 30 -static int nowayout = WATCHDOG_NOWAYOUT; -static int timeout = WDT_TIMEOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); +static unsigned int timeout = WDT_TIMEOUT; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (1<=timeout<=3600, default=" + __MODULE_STRING(WDT_TIMEOUT) ")"); + +static void __iomem *mmio_base; 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) +#define EP93XX_WATCHDOG 0x00 +#define EP93XX_WDSTATUS 0x04 -/* reset the wdt every ~200ms */ +/* reset the wdt every ~200ms - the heartbeat of the device is 0.250 seconds*/ #define WDT_INTERVAL (HZ/5) -static void wdt_enable(void) +static void ep93xx_wdt_timer_ping(unsigned long data) { - __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG); -} - -static void wdt_disable(void) -{ - __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG); -} + if (time_before(jiffies, next_heartbeat)) + writel(0x5555, mmio_base + EP93XX_WATCHDOG); -static inline void wdt_ping(void) -{ - __raw_writew(0x5555, EP93XX_WDT_WATCHDOG); + /* Re-set the timer interval */ + mod_timer(&timer, jiffies + WDT_INTERVAL); } -static void wdt_startup(void) +static int ep93xx_wdt_start(struct watchdog_device *wdd) { next_heartbeat = jiffies + (timeout * HZ); - wdt_enable(); + writel(0xaaaa, mmio_base + EP93XX_WATCHDOG); mod_timer(&timer, jiffies + WDT_INTERVAL); + + return 0; } -static void wdt_shutdown(void) +static int ep93xx_wdt_stop(struct watchdog_device *wdd) { del_timer_sync(&timer); - wdt_disable(); + writel(0xaa55, mmio_base + EP93XX_WATCHDOG); + + return 0; } -static void wdt_keepalive(void) +static int ep93xx_wdt_keepalive(struct watchdog_device *wdd) { /* 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; + return 0; } -static const struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE, - .identity = "EP93xx Watchdog", +static const struct watchdog_info ep93xx_wdt_ident = { + .options = WDIOF_CARDRESET | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .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 = { +static struct watchdog_ops ep93xx_wdt_ops = { .owner = THIS_MODULE, - .write = ep93xx_wdt_write, - .unlocked_ioctl = ep93xx_wdt_ioctl, - .open = ep93xx_wdt_open, - .release = ep93xx_wdt_release, - .llseek = no_llseek, + .start = ep93xx_wdt_start, + .stop = ep93xx_wdt_stop, + .ping = ep93xx_wdt_keepalive, }; -static struct miscdevice ep93xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ep93xx_wdt_fops, +static struct watchdog_device ep93xx_wdt_wdd = { + .info = &ep93xx_wdt_ident, + .ops = &ep93xx_wdt_ops, }; -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) +static int ep93xx_wdt_probe(struct platform_device *pdev) { + struct resource *res; + unsigned long val; 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)" : ""); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mmio_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mmio_base)) + return PTR_ERR(mmio_base); if (timeout < 1 || timeout > 3600) { timeout = WDT_TIMEOUT; - printk(KERN_INFO PFX + dev_warn(&pdev->dev, "timeout value must be 1<=x<=3600, using %d\n", timeout); } - setup_timer(&timer, ep93xx_timer_ping, 1); - return err; + val = readl(mmio_base + EP93XX_WATCHDOG); + ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0; + ep93xx_wdt_wdd.timeout = timeout; + + watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout); + + setup_timer(&timer, ep93xx_wdt_timer_ping, 1); + + err = watchdog_register_device(&ep93xx_wdt_wdd); + if (err) + return err; + + dev_info(&pdev->dev, + "EP93XX watchdog, driver version " WDT_VERSION "%s\n", + (val & 0x08) ? " (nCS1 disable detected)" : ""); + + return 0; } -static void __exit ep93xx_wdt_exit(void) +static int ep93xx_wdt_remove(struct platform_device *pdev) { - wdt_shutdown(); - misc_deregister(&ep93xx_wdt_miscdev); + watchdog_unregister_device(&ep93xx_wdt_wdd); + return 0; } -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"); +static struct platform_driver ep93xx_wdt_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ep93xx-wdt", + }, + .probe = ep93xx_wdt_probe, + .remove = ep93xx_wdt_remove, +}; -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, - "Watchdog timeout in seconds. (1<=timeout<=3600, default=" - __MODULE_STRING(WDT_TIMEOUT) ")"); +module_platform_driver(ep93xx_wdt_driver); -MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>," - "Alessandro Zummo <a.zummo@towertech.it>"); +MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>"); +MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); +MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 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 index 3946c51099c..23ee53240c4 100644 --- a/drivers/watchdog/eurotechwdt.c +++ b/drivers/watchdog/eurotechwdt.c @@ -45,6 +45,8 @@ * of the on-board SUPER I/O device SMSC FDC 37B782. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/interrupt.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -59,7 +61,6 @@ #include <linux/io.h> #include <linux/uaccess.h> -#include <asm/system.h> static unsigned long eurwdt_is_open; static int eurwdt_timeout; @@ -76,8 +77,8 @@ static char *ev = "int"; #define WDT_TIMEOUT 60 /* 1 minute */ -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -144,11 +145,11 @@ static void eurwdt_activate_timer(void) /* Setting interrupt line */ if (irq == 2 || irq > 15 || irq < 0) { - printk(KERN_ERR ": invalid irq number\n"); + pr_err("invalid irq number\n"); irq = 0; /* if invalid we disable interrupt */ } if (irq == 0) - printk(KERN_INFO ": interrupt disabled\n"); + pr_info("interrupt disabled\n"); eurwdt_write_reg(WDT_TIMER_CFG, irq << 4); @@ -163,12 +164,12 @@ static void eurwdt_activate_timer(void) static irqreturn_t eurwdt_interrupt(int irq, void *dev_id) { - printk(KERN_CRIT "timeout WDT timeout\n"); + pr_crit("timeout WDT timeout\n"); #ifdef ONLY_TESTING - printk(KERN_CRIT "Would Reboot.\n"); + pr_crit("Would Reboot\n"); #else - printk(KERN_CRIT "Initiating system reboot.\n"); + pr_crit("Initiating system reboot\n"); emergency_restart(); #endif return IRQ_HANDLED; @@ -335,8 +336,7 @@ 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"); + pr_crit("Unexpected close, not stopping watchdog!\n"); eurwdt_ping(); } clear_bit(0, &eurwdt_is_open); @@ -429,35 +429,32 @@ static int __init eurwdt_init(void) ret = request_irq(irq, eurwdt_interrupt, 0, "eurwdt", NULL); if (ret) { - printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq); + pr_err("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); + pr_err("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); + pr_err("can't register reboot notifier (err=%d)\n", ret); goto outreg; } ret = misc_register(&eurwdt_miscdev); if (ret) { - printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n", - WATCHDOG_MINOR); + pr_err("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", + pr_info("Eurotech WDT driver 0.01 at %X (Interrupt %d) - timeout event: %s\n", io, irq, (!strcmp("int", ev) ? "int" : "reboot")); out: @@ -480,4 +477,3 @@ 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 index e45ca2b4bfb..016bd935519 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -19,6 +19,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/err.h> #include <linux/fs.h> #include <linux/init.h> @@ -54,6 +56,7 @@ #define SIO_F71858_ID 0x0507 /* Chipset ID */ #define SIO_F71862_ID 0x0601 /* Chipset ID */ #define SIO_F71869_ID 0x0814 /* Chipset ID */ +#define SIO_F71869A_ID 0x1007 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ #define SIO_F71889_ID 0x0723 /* Chipset ID */ @@ -189,12 +192,11 @@ 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); + pr_err("I/O address 0x%04x already in use\n", (int)base); return -EBUSY; } - /* according to the datasheet the key must be send twice! */ + /* according to the datasheet the key must be sent twice! */ outb(SIO_UNLOCK_KEY, base); outb(SIO_UNLOCK_KEY, base); @@ -217,7 +219,7 @@ static int watchdog_set_timeout(int timeout) { if (timeout <= 0 || timeout > max_timeout) { - printk(KERN_ERR DRVNAME ": watchdog timeout out of range\n"); + pr_err("watchdog timeout out of range\n"); return -EINVAL; } @@ -252,7 +254,7 @@ static int watchdog_set_pulse_width(unsigned int pw) } else if (pw <= 5000) { watchdog.pulse_val = 3; } else { - printk(KERN_ERR DRVNAME ": pulse width out of range\n"); + pr_err("pulse width out of range\n"); err = -EINVAL; goto exit_unlock; } @@ -309,8 +311,7 @@ static int f71862fg_pin_configure(unsigned short ioaddr) if (ioaddr) superio_set_bit(ioaddr, SIO_REG_MFUNCT1, 1); } else { - printk(KERN_ERR DRVNAME ": Invalid argument f71862fg_pin=%d\n", - f71862fg_pin); + pr_err("Invalid argument f71862fg_pin=%d\n", f71862fg_pin); return -EINVAL; } return 0; @@ -487,8 +488,7 @@ static int watchdog_release(struct inode *inode, struct file *file) if (!watchdog.expect_close) { watchdog_keepalive(); - printk(KERN_CRIT DRVNAME - ": Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); } else if (!nowayout) { watchdog_stop(); } @@ -672,25 +672,22 @@ static int __init watchdog_init(int sioaddr) err = misc_register(&watchdog_miscdev); if (err) { - printk(KERN_ERR DRVNAME - ": cannot register miscdev on minor=%d\n", - watchdog_miscdev.minor); + pr_err("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"); + pr_err("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"); + pr_err("cannot start watchdog timer\n"); goto exit_miscdev; } @@ -720,8 +717,7 @@ static int __init watchdog_init(int sioaddr) if (nowayout) __module_get(THIS_MODULE); - printk(KERN_INFO DRVNAME - ": watchdog started with initial timeout of %u sec\n", + pr_info("watchdog started with initial timeout of %u sec\n", start_withtimeout); } @@ -746,7 +742,7 @@ static int __init f71808e_find(int sioaddr) devid = superio_inw(sioaddr, SIO_REG_MANID); if (devid != SIO_FINTEK_ID) { - pr_debug(DRVNAME ": Not a Fintek device\n"); + pr_debug("Not a Fintek device\n"); err = -ENODEV; goto exit; } @@ -761,6 +757,7 @@ static int __init f71808e_find(int sioaddr) err = f71862fg_pin_configure(0); /* validate module parameter */ break; case SIO_F71869_ID: + case SIO_F71869A_ID: watchdog.type = f71869; break; case SIO_F71882_ID: @@ -774,13 +771,13 @@ static int __init f71808e_find(int sioaddr) err = -ENODEV; goto exit; default: - printk(KERN_INFO DRVNAME ": Unrecognized Fintek device: %04x\n", - (unsigned int)devid); + pr_info("Unrecognized Fintek device: %04x\n", + (unsigned int)devid); err = -ENODEV; goto exit; } - printk(KERN_INFO DRVNAME ": Found %s watchdog chip, revision %d\n", + pr_info("Found %s watchdog chip, revision %d\n", f71808e_names[watchdog.type], (int)superio_inb(sioaddr, SIO_REG_DEVREV)); exit: @@ -808,8 +805,7 @@ static int __init f71808e_init(void) static void __exit f71808e_exit(void) { if (watchdog_is_running()) { - printk(KERN_WARNING DRVNAME - ": Watchdog timer still running, stopping it\n"); + pr_warn("Watchdog timer still running, stopping it\n"); watchdog_stop(); } misc_deregister(&watchdog_miscdev); diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index b146082bd85..25beb30878d 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -24,6 +24,8 @@ * capabilities) a kernel-based watchdog. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/compiler.h> #include <linux/init.h> @@ -32,6 +34,7 @@ #include <linux/watchdog.h> #include <linux/fs.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/io.h> #include <linux/uaccess.h> @@ -68,8 +71,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -110,7 +113,7 @@ 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"); + pr_notice("watchdog activated\n"); } } @@ -118,7 +121,7 @@ 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"); + pr_notice("watchdog deactivated\n"); } static void gef_wdt_set_timeout(unsigned int timeout) @@ -234,8 +237,7 @@ 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"); + pr_crit("unexpected close, not stopping timer!\n"); gef_wdt_service(); } expect_close = 0; @@ -261,7 +263,7 @@ static struct miscdevice gef_wdt_miscdev = { }; -static int __devinit gef_wdt_probe(struct platform_device *dev) +static int gef_wdt_probe(struct platform_device *dev) { int timeout = 10; u32 freq; @@ -284,7 +286,7 @@ static int __devinit gef_wdt_probe(struct platform_device *dev) return misc_register(&gef_wdt_miscdev); } -static int __devexit gef_wdt_remove(struct platform_device *dev) +static int gef_wdt_remove(struct platform_device *dev) { misc_deregister(&gef_wdt_miscdev); @@ -309,11 +311,12 @@ static struct platform_driver gef_wdt_driver = { .of_match_table = gef_wdt_ids, }, .probe = gef_wdt_probe, + .remove = gef_wdt_remove, }; static int __init gef_wdt_init(void) { - printk(KERN_INFO "GE watchdog driver\n"); + pr_info("GE watchdog driver\n"); return platform_driver_register(&gef_wdt_driver); } @@ -328,5 +331,4 @@ 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 index 9b49b125ad5..4c43e3fa8bd 100644 --- a/drivers/watchdog/geodewdt.c +++ b/drivers/watchdog/geodewdt.c @@ -9,6 +9,7 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/moduleparam.h> @@ -39,8 +40,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -100,7 +101,7 @@ static int geodewdt_release(struct inode *inode, struct file *file) geodewdt_disable(); module_put(THIS_MODULE); } else { - printk(KERN_CRIT "Unexpected close - watchdog is not stopping.\n"); + pr_crit("Unexpected close - watchdog is not stopping\n"); geodewdt_ping(); set_bit(WDT_FLAGS_ORPHAN, &wdt_flags); @@ -214,13 +215,13 @@ static struct miscdevice geodewdt_miscdev = { .fops = &geodewdt_fops, }; -static int __devinit geodewdt_probe(struct platform_device *dev) +static int __init 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"); + pr_err("No timers were available\n"); return -ENODEV; } @@ -242,7 +243,7 @@ static int __devinit geodewdt_probe(struct platform_device *dev) return ret; } -static int __devexit geodewdt_remove(struct platform_device *dev) +static int geodewdt_remove(struct platform_device *dev) { misc_deregister(&geodewdt_miscdev); return 0; @@ -254,8 +255,7 @@ static void geodewdt_shutdown(struct platform_device *dev) } static struct platform_driver geodewdt_driver = { - .probe = geodewdt_probe, - .remove = __devexit_p(geodewdt_remove), + .remove = geodewdt_remove, .shutdown = geodewdt_shutdown, .driver = { .owner = THIS_MODULE, @@ -267,20 +267,18 @@ 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); + if (IS_ERR(geodewdt_platform_device)) + return PTR_ERR(geodewdt_platform_device); + + ret = platform_driver_probe(&geodewdt_driver, geodewdt_probe); + if (ret) goto err; - } return 0; err: - platform_driver_unregister(&geodewdt_driver); + platform_device_unregister(geodewdt_platform_device); return ret; } @@ -296,4 +294,3 @@ 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/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c new file mode 100644 index 00000000000..220a9e07cfd --- /dev/null +++ b/drivers/watchdog/gpio_wdt.c @@ -0,0 +1,254 @@ +/* + * Driver for watchdog device controlled through GPIO-line + * + * Author: 2013, Alexander Shiyan <shc_work@mail.ru> + * + * 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/err.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> + +#define SOFT_TIMEOUT_MIN 1 +#define SOFT_TIMEOUT_DEF 60 +#define SOFT_TIMEOUT_MAX 0xffff + +enum { + HW_ALGO_TOGGLE, + HW_ALGO_LEVEL, +}; + +struct gpio_wdt_priv { + int gpio; + bool active_low; + bool state; + unsigned int hw_algo; + unsigned int hw_margin; + unsigned long last_jiffies; + struct notifier_block notifier; + struct timer_list timer; + struct watchdog_device wdd; +}; + +static void gpio_wdt_disable(struct gpio_wdt_priv *priv) +{ + gpio_set_value_cansleep(priv->gpio, !priv->active_low); + + /* Put GPIO back to tristate */ + if (priv->hw_algo == HW_ALGO_TOGGLE) + gpio_direction_input(priv->gpio); +} + +static int gpio_wdt_start(struct watchdog_device *wdd) +{ + struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); + + priv->state = priv->active_low; + gpio_direction_output(priv->gpio, priv->state); + priv->last_jiffies = jiffies; + mod_timer(&priv->timer, priv->last_jiffies + priv->hw_margin); + + return 0; +} + +static int gpio_wdt_stop(struct watchdog_device *wdd) +{ + struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); + + mod_timer(&priv->timer, 0); + gpio_wdt_disable(priv); + + return 0; +} + +static int gpio_wdt_ping(struct watchdog_device *wdd) +{ + struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); + + priv->last_jiffies = jiffies; + + return 0; +} + +static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) +{ + wdd->timeout = t; + + return gpio_wdt_ping(wdd); +} + +static void gpio_wdt_hwping(unsigned long data) +{ + struct watchdog_device *wdd = (struct watchdog_device *)data; + struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); + + if (time_after(jiffies, priv->last_jiffies + + msecs_to_jiffies(wdd->timeout * 1000))) { + dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n"); + return; + } + + /* Restart timer */ + mod_timer(&priv->timer, jiffies + priv->hw_margin); + + switch (priv->hw_algo) { + case HW_ALGO_TOGGLE: + /* Toggle output pin */ + priv->state = !priv->state; + gpio_set_value_cansleep(priv->gpio, priv->state); + break; + case HW_ALGO_LEVEL: + /* Pulse */ + gpio_set_value_cansleep(priv->gpio, !priv->active_low); + udelay(1); + gpio_set_value_cansleep(priv->gpio, priv->active_low); + break; + } +} + +static int gpio_wdt_notify_sys(struct notifier_block *nb, unsigned long code, + void *unused) +{ + struct gpio_wdt_priv *priv = container_of(nb, struct gpio_wdt_priv, + notifier); + + mod_timer(&priv->timer, 0); + + switch (code) { + case SYS_HALT: + case SYS_POWER_OFF: + gpio_wdt_disable(priv); + break; + default: + break; + } + + return NOTIFY_DONE; +} + +static const struct watchdog_info gpio_wdt_ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT, + .identity = "GPIO Watchdog", +}; + +static const struct watchdog_ops gpio_wdt_ops = { + .owner = THIS_MODULE, + .start = gpio_wdt_start, + .stop = gpio_wdt_stop, + .ping = gpio_wdt_ping, + .set_timeout = gpio_wdt_set_timeout, +}; + +static int gpio_wdt_probe(struct platform_device *pdev) +{ + struct gpio_wdt_priv *priv; + enum of_gpio_flags flags; + unsigned int hw_margin; + unsigned long f = 0; + const char *algo; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); + if (!gpio_is_valid(priv->gpio)) + return priv->gpio; + + priv->active_low = flags & OF_GPIO_ACTIVE_LOW; + + ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo); + if (ret) + return ret; + if (!strncmp(algo, "toggle", 6)) { + priv->hw_algo = HW_ALGO_TOGGLE; + f = GPIOF_IN; + } else if (!strncmp(algo, "level", 5)) { + priv->hw_algo = HW_ALGO_LEVEL; + f = priv->active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + } else { + return -EINVAL; + } + + ret = devm_gpio_request_one(&pdev->dev, priv->gpio, f, + dev_name(&pdev->dev)); + if (ret) + return ret; + + ret = of_property_read_u32(pdev->dev.of_node, + "hw_margin_ms", &hw_margin); + if (ret) + return ret; + /* Disallow values lower than 2 and higher than 65535 ms */ + if (hw_margin < 2 || hw_margin > 65535) + return -EINVAL; + + /* Use safe value (1/2 of real timeout) */ + priv->hw_margin = msecs_to_jiffies(hw_margin / 2); + + watchdog_set_drvdata(&priv->wdd, priv); + + priv->wdd.info = &gpio_wdt_ident; + priv->wdd.ops = &gpio_wdt_ops; + priv->wdd.min_timeout = SOFT_TIMEOUT_MIN; + priv->wdd.max_timeout = SOFT_TIMEOUT_MAX; + + if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0) + priv->wdd.timeout = SOFT_TIMEOUT_DEF; + + setup_timer(&priv->timer, gpio_wdt_hwping, (unsigned long)&priv->wdd); + + ret = watchdog_register_device(&priv->wdd); + if (ret) + return ret; + + priv->notifier.notifier_call = gpio_wdt_notify_sys; + ret = register_reboot_notifier(&priv->notifier); + if (ret) + watchdog_unregister_device(&priv->wdd); + + return ret; +} + +static int gpio_wdt_remove(struct platform_device *pdev) +{ + struct gpio_wdt_priv *priv = platform_get_drvdata(pdev); + + del_timer_sync(&priv->timer); + unregister_reboot_notifier(&priv->notifier); + watchdog_unregister_device(&priv->wdd); + + return 0; +} + +static const struct of_device_id gpio_wdt_dt_ids[] = { + { .compatible = "linux,wdt-gpio", }, + { } +}; +MODULE_DEVICE_TABLE(of, gpio_wdt_dt_ids); + +static struct platform_driver gpio_wdt_driver = { + .driver = { + .name = "gpio-wdt", + .owner = THIS_MODULE, + .of_match_table = gpio_wdt_dt_ids, + }, + .probe = gpio_wdt_probe, + .remove = gpio_wdt_remove, +}; +module_platform_driver(gpio_wdt_driver); + +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); +MODULE_DESCRIPTION("GPIO Watchdog"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 3c166d3f4e5..75d2243b94f 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -13,9 +13,10 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/device.h> #include <linux/fs.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/bitops.h> #include <linux/kernel.h> @@ -37,7 +38,7 @@ #endif /* CONFIG_HPWDT_NMI_DECODING */ #include <asm/nmi.h> -#define HPWDT_VERSION "1.3.0" +#define HPWDT_VERSION "1.3.3" #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) @@ -45,7 +46,7 @@ static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ static unsigned int reload; /* the computed soft_margin */ -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; static char expect_release; static unsigned long hpwdt_is_open; @@ -53,7 +54,7 @@ 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) = { +static const struct pci_device_id hpwdt_devices[] = { { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */ { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */ {0}, /* terminate list */ @@ -144,9 +145,9 @@ struct cmn_registers { } __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 unsigned int allow_kdump = 1; static unsigned int is_icru; +static unsigned int is_uefi; static DEFINE_SPINLOCK(rom_lock); static void *cru_rom_addr; static struct cmn_registers cmn_regs; @@ -160,7 +161,8 @@ extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs, #define HPWDT_ARCH 32 asm(".text \n\t" - ".align 4 \n" + ".align 4 \n\t" + ".globl asminline_call \n" "asminline_call: \n\t" "pushl %ebp \n\t" "movl %esp, %ebp \n\t" @@ -211,7 +213,7 @@ asm(".text \n\t" * 0 : SUCCESS * <0 : FAILURE */ -static int __devinit cru_detect(unsigned long map_entry, +static int cru_detect(unsigned long map_entry, unsigned long map_offset) { void *bios32_map; @@ -235,8 +237,7 @@ static int __devinit cru_detect(unsigned long map_entry, 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", + pr_warn("Call succeeded but with an error: 0x%x\n", cmn_regs.u1.ral); } else { physical_bios_base = cmn_regs.u2.rebx; @@ -256,14 +257,10 @@ static int __devinit cru_detect(unsigned long map_entry, } } - 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); + pr_debug("CRU Base Address: 0x%lx\n", physical_bios_base); + pr_debug("CRU Offset Address: 0x%lx\n", physical_bios_offset); + pr_debug("CRU Length: 0x%lx\n", cru_length); + pr_debug("CRU Mapped Address: %p\n", &cru_rom_addr); } iounmap(bios32_map); return retval; @@ -272,7 +269,7 @@ static int __devinit cru_detect(unsigned long map_entry, /* * bios_checksum */ -static int __devinit bios_checksum(const char __iomem *ptr, int len) +static int bios_checksum(const char __iomem *ptr, int len) { char sum = 0; int i; @@ -297,7 +294,7 @@ static int __devinit bios_checksum(const char __iomem *ptr, int len) * 0 : SUCCESS * <0 : FAILURE */ -static int __devinit bios32_present(const char __iomem *p) +static int bios32_present(const char __iomem *p) { struct bios32_service_dir *bios_32_ptr; int length; @@ -327,7 +324,7 @@ static int __devinit bios32_present(const char __iomem *p) return -ENODEV; } -static int __devinit detect_cru_service(void) +static int detect_cru_service(void) { char __iomem *p, *q; int rc = -1; @@ -355,7 +352,8 @@ static int __devinit detect_cru_service(void) #define HPWDT_ARCH 64 asm(".text \n\t" - ".align 4 \n" + ".align 4 \n\t" + ".globl asminline_call \n" "asminline_call: \n\t" "pushq %rbp \n\t" "movq %rsp, %rbp \n\t" @@ -399,7 +397,7 @@ asm(".text \n\t" * 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) +static void dmi_find_cru(const struct dmi_header *dm, void *dummy) { struct smbios_cru64_info *smbios_cru64_ptr; unsigned long cru_physical_address; @@ -418,7 +416,7 @@ static void __devinit dmi_find_cru(const struct dmi_header *dm, void *dummy) } } -static int __devinit detect_cru_service(void) +static int detect_cru_service(void) { cru_rom_addr = NULL; @@ -438,16 +436,16 @@ static void hpwdt_start(void) { reload = SECS_TO_TICKS(soft_margin); iowrite16(reload, hpwdt_timer_reg); - iowrite16(0x85, hpwdt_timer_con); + iowrite8(0x85, hpwdt_timer_con); } static void hpwdt_stop(void) { unsigned long data; - data = ioread16(hpwdt_timer_con); + data = ioread8(hpwdt_timer_con); data &= 0xFE; - iowrite16(data, hpwdt_timer_con); + iowrite8(data, hpwdt_timer_con); } static void hpwdt_ping(void) @@ -458,16 +456,13 @@ static void hpwdt_ping(void) 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", + pr_warn("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); + pr_debug("New timer passed in is %d seconds\n", new_margin); reload = SECS_TO_TICKS(soft_margin); return 0; @@ -491,7 +486,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) goto out; spin_lock_irqsave(&rom_lock, rom_pl); - if (!die_nmi_called && !is_icru) + if (!die_nmi_called && !is_icru && !is_uefi) asminline_call(&cmn_regs, cru_rom_addr); die_nmi_called = 1; spin_unlock_irqrestore(&rom_lock, rom_pl); @@ -499,14 +494,19 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) if (allow_kdump) hpwdt_stop(); - if (!is_icru) { + if (!is_icru && !is_uefi) { if (cmn_regs.u1.ral == 0) { panic("An NMI occurred, " "but unable to determine source.\n"); } } - panic("An NMI occurred, please see the Integrated " - "Management Log for details.\n"); + panic("An NMI occurred. Depending on your system the reason " + "for the NMI is logged in any one of the following " + "resources:\n" + "1. Integrated Management Log (IML)\n" + "2. OA Syslog\n" + "3. OA Forward Progress Log\n" + "4. iLO Event Log"); out: return NMI_DONE; @@ -535,8 +535,7 @@ static int hpwdt_release(struct inode *inode, struct file *file) if (expect_release == 42) { hpwdt_stop(); } else { - printk(KERN_CRIT - "hpwdt: Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); hpwdt_ping(); } @@ -655,7 +654,7 @@ static struct miscdevice hpwdt_miscdev = { #ifdef CONFIG_HPWDT_NMI_DECODING #ifdef CONFIG_X86_LOCAL_APIC -static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) +static void hpwdt_check_nmi_decoding(struct pci_dev *dev) { /* * If nmi_watchdog is turned off then we can turn on @@ -664,7 +663,7 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) hpwdt_nmi_decoding = 1; } #else -static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) +static void 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"); @@ -679,7 +678,7 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) * This check is independent of architecture and needs to be made for * any ProLiant system. */ -static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy) +static void dmi_find_icru(const struct dmi_header *dm, void *dummy) { struct smbios_proliant_info *smbios_proliant_ptr; @@ -687,10 +686,12 @@ static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy) smbios_proliant_ptr = (struct smbios_proliant_info *) dm; if (smbios_proliant_ptr->misc_features & 0x01) is_icru = 1; + if (smbios_proliant_ptr->misc_features & 0x408) + is_uefi = 1; } } -static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) +static int hpwdt_init_nmi_decoding(struct pci_dev *dev) { int retval; @@ -705,7 +706,7 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) * the old cru detect code. */ dmi_walk(dmi_find_icru, NULL); - if (!is_icru) { + if (!is_icru && !is_uefi) { /* * We need to map the ROM to get the CRU service. @@ -730,42 +731,51 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) } /* - * 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. + * Only one function can register for NMI_UNKNOWN */ - retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, - (priority) ? NMI_FLAG_FIRST : 0, - "hpwdt"); - 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); - } + retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt"); + if (retval) + goto error; + retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt"); + if (retval) + goto error1; + retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt"); + if (retval) + goto error2; 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"); + ", allow kernel dump: %s (default = 0/OFF)\n", + (allow_kdump == 0) ? "OFF" : "ON"); return 0; + +error2: + unregister_nmi_handler(NMI_SERR, "hpwdt"); +error1: + unregister_nmi_handler(NMI_UNKNOWN, "hpwdt"); +error: + dev_warn(&dev->dev, + "Unable to register a die notifier (err=%d).\n", + retval); + if (cru_rom_addr) + iounmap(cru_rom_addr); + return retval; } static void hpwdt_exit_nmi_decoding(void) { unregister_nmi_handler(NMI_UNKNOWN, "hpwdt"); + unregister_nmi_handler(NMI_SERR, "hpwdt"); + unregister_nmi_handler(NMI_IO_CHECK, "hpwdt"); 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 void hpwdt_check_nmi_decoding(struct pci_dev *dev) { } -static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) +static int hpwdt_init_nmi_decoding(struct pci_dev *dev) { return 0; } @@ -775,7 +785,7 @@ static void hpwdt_exit_nmi_decoding(void) } #endif /* CONFIG_HPWDT_NMI_DECODING */ -static int __devinit hpwdt_init_one(struct pci_dev *dev, +static int hpwdt_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { int retval; @@ -796,6 +806,12 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, return -ENODEV; } + /* + * Ignore all auxilary iLO devices with the following PCI ID + */ + if (dev->subsystem_device == 0x1979) + return -ENODEV; + if (pci_enable_device(dev)) { dev_warn(&dev->dev, "Not possible to enable PCI Device: 0x%x:0x%x.\n", @@ -813,6 +829,9 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, hpwdt_timer_reg = pci_mem_addr + 0x70; hpwdt_timer_con = pci_mem_addr + 0x72; + /* Make sure that timer is disabled until /dev/watchdog is opened */ + hpwdt_stop(); + /* Make sure that we have a valid soft_margin */ if (hpwdt_change_timer(soft_margin)) hpwdt_change_timer(DEFAULT_MARGIN); @@ -844,7 +863,7 @@ error_pci_iomap: return retval; } -static void __devexit hpwdt_exit(struct pci_dev *dev) +static void hpwdt_exit(struct pci_dev *dev) { if (!nowayout) hpwdt_stop(); @@ -859,40 +878,24 @@ static struct pci_driver hpwdt_driver = { .name = "hpwdt", .id_table = hpwdt_devices, .probe = hpwdt_init_one, - .remove = __devexit_p(hpwdt_exit), + .remove = 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_param(nowayout, bool, 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); +module_pci_driver(hpwdt_driver); diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c index db45091ef43..d7befd58b39 100644 --- a/drivers/watchdog/i6300esb.c +++ b/drivers/watchdog/i6300esb.c @@ -27,6 +27,8 @@ * Includes, defines, variables, module parameters, ... */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> @@ -34,7 +36,6 @@ #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> @@ -44,7 +45,6 @@ #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 */ @@ -94,8 +94,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -213,8 +213,7 @@ static int esb_release(struct inode *inode, struct file *file) if (esb_expect_close == 42) esb_timer_stop(); else { - printk(KERN_CRIT PFX - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); esb_timer_keepalive(); } clear_bit(0, &timer_alive); @@ -334,7 +333,7 @@ static struct miscdevice esb_miscdev = { /* * Data for PCI driver interface */ -static DEFINE_PCI_DEVICE_TABLE(esb_pci_tbl) = { +static const struct pci_device_id esb_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), }, { 0, }, /* End of list */ }; @@ -344,22 +343,22 @@ MODULE_DEVICE_TABLE(pci, esb_pci_tbl); * Init & exit routines */ -static unsigned char __devinit esb_getdevice(struct pci_dev *pdev) +static unsigned char esb_getdevice(struct pci_dev *pdev) { if (pci_enable_device(pdev)) { - printk(KERN_ERR PFX "failed to enable device\n"); + pr_err("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"); + pr_err("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"); + pr_err("failed to get BASEADDR\n"); goto err_release; } @@ -375,7 +374,7 @@ err_devput: return 0; } -static void __devinit esb_initdevice(void) +static void esb_initdevice(void) { u8 val1; u16 val2; @@ -397,7 +396,7 @@ static void __devinit esb_initdevice(void) /* 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"); + pr_warn("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); @@ -416,18 +415,18 @@ static void __devinit esb_initdevice(void) esb_timer_set_heartbeat(heartbeat); } -static int __devinit esb_probe(struct pci_dev *pdev, +static int 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", + pr_info("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"); + pr_err("This driver only supports 1 device\n"); return -ENODEV; } @@ -439,9 +438,8 @@ static int __devinit esb_probe(struct pci_dev *pdev, 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); + pr_info("heartbeat value must be 1<heartbeat<2046, using %d\n", + heartbeat); } /* Initialize the watchdog and make sure it does not run */ @@ -450,14 +448,12 @@ static int __devinit esb_probe(struct pci_dev *pdev, /* 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); + pr_err("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); + pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", + BASEADDR, heartbeat, nowayout); return 0; err_unmap: @@ -468,7 +464,7 @@ err_unmap: return ret; } -static void __devexit esb_remove(struct pci_dev *pdev) +static void esb_remove(struct pci_dev *pdev) { /* Stop the timer before we leave */ if (!nowayout) @@ -491,25 +487,12 @@ static struct pci_driver esb_driver = { .name = ESB_MODULE_NAME, .id_table = esb_pci_tbl, .probe = esb_probe, - .remove = __devexit_p(esb_remove), + .remove = 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_pci_driver(esb_driver); 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 index 9e27e6422f6..3c57b45537a 100644 --- a/drivers/watchdog/iTCO_vendor.h +++ b/drivers/watchdog/iTCO_vendor.h @@ -1,8 +1,8 @@ /* 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_start(struct resource *, unsigned int); +extern void iTCO_vendor_pre_stop(struct resource *); +extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int); extern void iTCO_vendor_pre_set_heartbeat(unsigned int); extern int iTCO_vendor_check_noreboot_on(void); #else diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c index 481d1ad4346..b6b2f90b5d4 100644 --- a/drivers/watchdog/iTCO_vendor_support.c +++ b/drivers/watchdog/iTCO_vendor_support.c @@ -17,10 +17,11 @@ * Includes, defines, variables, module parameters, ... */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + /* 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 */ @@ -34,11 +35,6 @@ #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 @@ -81,24 +77,24 @@ MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=" * 20.6 seconds. */ -static void supermicro_old_pre_start(unsigned long acpibase) +static void supermicro_old_pre_start(struct resource *smires) { unsigned long val32; /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ - val32 = inl(SMI_EN); + val32 = inl(smires->start); val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ - outl(val32, SMI_EN); /* Needed to activate watchdog */ + outl(val32, smires->start); /* Needed to activate watchdog */ } -static void supermicro_old_pre_stop(unsigned long acpibase) +static void supermicro_old_pre_stop(struct resource *smires) { unsigned long val32; /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */ - val32 = inl(SMI_EN); + val32 = inl(smires->start); val32 |= 0x00002000; /* Turn on SMI clearing watchdog */ - outl(val32, SMI_EN); /* Needed to deactivate watchdog */ + outl(val32, smires->start); /* Needed to deactivate watchdog */ } /* @@ -269,66 +265,66 @@ static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat) * Don't use this fix if you don't need to!!! */ -static void broken_bios_start(unsigned long acpibase) +static void broken_bios_start(struct resource *smires) { unsigned long val32; - val32 = inl(SMI_EN); + val32 = inl(smires->start); /* 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); + outl(val32, smires->start); } -static void broken_bios_stop(unsigned long acpibase) +static void broken_bios_stop(struct resource *smires) { unsigned long val32; - val32 = inl(SMI_EN); + val32 = inl(smires->start); /* 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); + outl(val32, smires->start); } /* * Generic Support Functions */ -void iTCO_vendor_pre_start(unsigned long acpibase, +void iTCO_vendor_pre_start(struct resource *smires, unsigned int heartbeat) { switch (vendorsupport) { case SUPERMICRO_OLD_BOARD: - supermicro_old_pre_start(acpibase); + supermicro_old_pre_start(smires); break; case SUPERMICRO_NEW_BOARD: supermicro_new_pre_start(heartbeat); break; case BROKEN_BIOS: - broken_bios_start(acpibase); + broken_bios_start(smires); break; } } EXPORT_SYMBOL(iTCO_vendor_pre_start); -void iTCO_vendor_pre_stop(unsigned long acpibase) +void iTCO_vendor_pre_stop(struct resource *smires) { switch (vendorsupport) { case SUPERMICRO_OLD_BOARD: - supermicro_old_pre_stop(acpibase); + supermicro_old_pre_stop(smires); break; case SUPERMICRO_NEW_BOARD: supermicro_new_pre_stop(); break; case BROKEN_BIOS: - broken_bios_stop(acpibase); + broken_bios_stop(smires); break; } } EXPORT_SYMBOL(iTCO_vendor_pre_stop); -void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat) +void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat) { if (vendorsupport == SUPERMICRO_NEW_BOARD) supermicro_new_pre_set_heartbeat(heartbeat); @@ -355,13 +351,13 @@ EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on); static int __init iTCO_vendor_init_module(void) { - printk(KERN_INFO PFX "vendor-support=%d\n", vendorsupport); + pr_info("vendor-support=%d\n", vendorsupport); return 0; } static void __exit iTCO_vendor_exit_module(void) { - printk(KERN_INFO PFX "Module Unloaded\n"); + pr_info("Module Unloaded\n"); } module_init(iTCO_vendor_init_module); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index bdf401b240b..0ba1b7c9976 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -37,16 +37,18 @@ * document number TBD : DH89xxCC * document number TBD : Panther Point * document number TBD : Lynx Point + * document number TBD : Lynx Point-LP */ /* * Includes, defines, variables, module parameters, ... */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + /* Module and version information */ #define DRV_NAME "iTCO_wdt" -#define DRV_VERSION "1.07" -#define PFX DRV_NAME ": " +#define DRV_VERSION "1.11" /* Includes */ #include <linux/module.h> /* For module specific items */ @@ -54,8 +56,6 @@ #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 */ @@ -65,316 +65,16 @@ #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 <linux/mfd/core.h> +#include <linux/mfd/lpc_ich.h> #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_CPT, /* Cougar Point */ - TCO_CPTD, /* Cougar Point Desktop */ - TCO_CPTM, /* Cougar Point Mobile */ - TCO_PBG, /* Patsburg */ - TCO_DH89XXCC, /* DH89xxCC */ - TCO_PPT, /* Panther Point */ - TCO_LPT, /* Lynx 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 Desktop", 2}, - {"Cougar Point Mobile", 2}, - {"Patsburg", 2}, - {"DH89xxCC", 2}, - {"Panther Point", 2}, - {"Lynx Point", 2}, - {NULL, 0} -}; - -/* - * 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) = { - { PCI_VDEVICE(INTEL, 0x2410), TCO_ICH}, - { PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0}, - { PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2}, - { PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M}, - { PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3}, - { PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M}, - { PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4}, - { PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M}, - { PCI_VDEVICE(INTEL, 0x2450), TCO_CICH}, - { PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5}, - { PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB}, - { PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6}, - { PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M}, - { PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W}, - { PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB}, - { PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7}, - { PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH}, - { PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M}, - { PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH}, - { PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10}, - { PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8}, - { PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH}, - { PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO}, - { PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M}, - { PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME}, - { PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9}, - { PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R}, - { PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH}, - { PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO}, - { PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M}, - { PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME}, - { PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10}, - { PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R}, - { PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D}, - { PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO}, - { PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH}, - { PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM}, - { PCI_VDEVICE(INTEL, 0x3b02), TCO_P55}, - { PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55}, - { PCI_VDEVICE(INTEL, 0x3b06), TCO_H55}, - { PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57}, - { PCI_VDEVICE(INTEL, 0x3b08), TCO_H57}, - { PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55}, - { PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57}, - { PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57}, - { PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF}, - { PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57}, - { PCI_VDEVICE(INTEL, 0x3b12), TCO_3400}, - { PCI_VDEVICE(INTEL, 0x3b14), TCO_3420}, - { PCI_VDEVICE(INTEL, 0x3b16), TCO_3450}, - { PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579}, - { PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD}, - { PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM}, - { PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT}, - { PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG}, - { PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG}, - { PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC}, - { PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT}, - { PCI_VDEVICE(INTEL, 0x8c40), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c41), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c42), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c43), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c44), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c45), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c46), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c47), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c48), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c49), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4a), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4b), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4c), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4d), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4e), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4f), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c50), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c51), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c52), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c53), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c54), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c55), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c56), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c57), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c58), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c59), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5a), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5b), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5c), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5d), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5e), TCO_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5f), TCO_LPT}, - { 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) +#define TCOBASE (iTCO_wdt_private.tco_res->start) /* SMI Control and Enable Register */ -#define SMI_EN (iTCO_wdt_private.ACPIBASE + 0x30) +#define SMI_EN (iTCO_wdt_private.smi_res->start) #define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */ #define TCOv1_TMR (TCOBASE + 0x01) /* TCOv1 Timer Initial Value */ @@ -387,34 +87,34 @@ MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); #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; + struct resource *tco_res; + struct resource *smi_res; + /* + * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2), + * or memory-mapped PMC register bit 4 (TCO version 3). + */ + struct resource *gcs_pmc_res; + unsigned long __iomem *gcs_pmc; /* the lock for io operations */ spinlock_t io_lock; + struct platform_device *dev; /* 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 */ +#define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */ +static int heartbeat = WATCHDOG_TIMEOUT; /* 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) ")"); + __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -428,11 +128,19 @@ MODULE_PARM_DESC(turn_SMI_watchdog_clear_off, * Some TCO specific functions */ -static inline unsigned int seconds_to_ticks(int seconds) +/* + * The iTCO v1 and v2's internal timer is stored as ticks which decrement + * every 0.6 seconds. v3's internal timer is stored as seconds (some + * datasheets incorrectly state 0.6 seconds). + */ +static inline unsigned int seconds_to_ticks(int secs) { - /* the internal timer is stored as ticks which decrement - * every 0.6 seconds */ - return (seconds * 10) / 6; + return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6; +} + +static inline unsigned int ticks_to_seconds(int ticks) +{ + return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10; } static void iTCO_wdt_set_NO_REBOOT_bit(void) @@ -440,10 +148,14 @@ 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); + if (iTCO_wdt_private.iTCO_version == 3) { + val32 = readl(iTCO_wdt_private.gcs_pmc); + val32 |= 0x00000010; + writel(val32, iTCO_wdt_private.gcs_pmc); + } else if (iTCO_wdt_private.iTCO_version == 2) { + val32 = readl(iTCO_wdt_private.gcs_pmc); val32 |= 0x00000020; - writel(val32, iTCO_wdt_private.gcs); + writel(val32, iTCO_wdt_private.gcs_pmc); } else if (iTCO_wdt_private.iTCO_version == 1) { pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); val32 |= 0x00000002; @@ -457,12 +169,20 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void) u32 val32; /* Unset the NO_REBOOT bit: this enables reboots */ - if (iTCO_wdt_private.iTCO_version == 2) { - val32 = readl(iTCO_wdt_private.gcs); + if (iTCO_wdt_private.iTCO_version == 3) { + val32 = readl(iTCO_wdt_private.gcs_pmc); + val32 &= 0xffffffef; + writel(val32, iTCO_wdt_private.gcs_pmc); + + val32 = readl(iTCO_wdt_private.gcs_pmc); + if (val32 & 0x00000010) + ret = -EIO; + } else if (iTCO_wdt_private.iTCO_version == 2) { + val32 = readl(iTCO_wdt_private.gcs_pmc); val32 &= 0xffffffdf; - writel(val32, iTCO_wdt_private.gcs); + writel(val32, iTCO_wdt_private.gcs_pmc); - val32 = readl(iTCO_wdt_private.gcs); + val32 = readl(iTCO_wdt_private.gcs_pmc); if (val32 & 0x00000020) ret = -EIO; } else if (iTCO_wdt_private.iTCO_version == 1) { @@ -478,25 +198,24 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void) return ret; /* returns: 0 = OK, -EIO = Error */ } -static int iTCO_wdt_start(void) +static int iTCO_wdt_start(struct watchdog_device *wd_dev) { unsigned int val; spin_lock(&iTCO_wdt_private.io_lock); - iTCO_vendor_pre_start(iTCO_wdt_private.ACPIBASE, heartbeat); + iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, wd_dev->timeout); /* 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"); + pr_err("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) + if (iTCO_wdt_private.iTCO_version >= 2) outw(0x01, TCO_RLD); else if (iTCO_wdt_private.iTCO_version == 1) outb(0x01, TCO_RLD); @@ -513,13 +232,13 @@ static int iTCO_wdt_start(void) return 0; } -static int iTCO_wdt_stop(void) +static int iTCO_wdt_stop(struct watchdog_device *wd_dev) { unsigned int val; spin_lock(&iTCO_wdt_private.io_lock); - iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE); + iTCO_vendor_pre_stop(iTCO_wdt_private.smi_res); /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ val = inw(TCO1_CNT); @@ -537,16 +256,16 @@ static int iTCO_wdt_stop(void) return 0; } -static int iTCO_wdt_keepalive(void) +static int iTCO_wdt_ping(struct watchdog_device *wd_dev) { spin_lock(&iTCO_wdt_private.io_lock); - iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat); + iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout); /* Reload the timer by writing to the TCO Timer Counter register */ - if (iTCO_wdt_private.iTCO_version == 2) + if (iTCO_wdt_private.iTCO_version >= 2) { outw(0x01, TCO_RLD); - else if (iTCO_wdt_private.iTCO_version == 1) { + } 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 */ @@ -558,7 +277,7 @@ static int iTCO_wdt_keepalive(void) return 0; } -static int iTCO_wdt_set_heartbeat(int t) +static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) { unsigned int val16; unsigned char val8; @@ -574,14 +293,14 @@ static int iTCO_wdt_set_heartbeat(int t) /* "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)) || + 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) { + if (iTCO_wdt_private.iTCO_version >= 2) { spin_lock(&iTCO_wdt_private.io_lock); val16 = inw(TCOv2_TMR); val16 &= 0xfc00; @@ -605,23 +324,24 @@ static int iTCO_wdt_set_heartbeat(int t) return -EINVAL; } - heartbeat = t; + wd_dev->timeout = t; return 0; } -static int iTCO_wdt_get_timeleft(int *time_left) +static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) { unsigned int val16; unsigned char val8; + unsigned int time_left = 0; /* read the TCO Timer */ - if (iTCO_wdt_private.iTCO_version == 2) { + 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; + time_left = ticks_to_seconds(val16); } else if (iTCO_wdt_private.iTCO_version == 1) { spin_lock(&iTCO_wdt_private.io_lock); val8 = inb(TCO_RLD); @@ -630,331 +350,217 @@ static int iTCO_wdt_get_timeleft(int *time_left) val8 += (inb(TCOv1_TMR) & 0x3f); spin_unlock(&iTCO_wdt_private.io_lock); - *time_left = (val8 * 6) / 10; - } else - return -EINVAL; - return 0; + time_left = ticks_to_seconds(val8); + } + return time_left; } /* - * /dev/watchdog handling + * Kernel Interfaces */ -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; +static const struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = DRV_NAME, +}; - /* - * Reload and activate timer - */ - iTCO_wdt_start(); - return nonseekable_open(inode, file); -} +static const struct watchdog_ops iTCO_wdt_ops = { + .owner = THIS_MODULE, + .start = iTCO_wdt_start, + .stop = iTCO_wdt_stop, + .ping = iTCO_wdt_ping, + .set_timeout = iTCO_wdt_set_timeout, + .get_timeleft = iTCO_wdt_get_timeleft, +}; -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 struct watchdog_device iTCO_wdt_watchdog_dev = { + .info = &ident, + .ops = &iTCO_wdt_ops, +}; -static ssize_t iTCO_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) +/* + * Init & exit routines + */ + +static void iTCO_wdt_cleanup(void) { - /* 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; - } - } + /* Stop the timer before we leave */ + if (!nowayout) + iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); - /* someone wrote to us, we should reload the timer */ - iTCO_wdt_keepalive(); + /* Deregister */ + watchdog_unregister_device(&iTCO_wdt_watchdog_dev); + + /* release resources */ + release_region(iTCO_wdt_private.tco_res->start, + resource_size(iTCO_wdt_private.tco_res)); + release_region(iTCO_wdt_private.smi_res->start, + resource_size(iTCO_wdt_private.smi_res)); + if (iTCO_wdt_private.iTCO_version >= 2) { + iounmap(iTCO_wdt_private.gcs_pmc); + release_mem_region(iTCO_wdt_private.gcs_pmc_res->start, + resource_size(iTCO_wdt_private.gcs_pmc_res)); } - return len; + + iTCO_wdt_private.tco_res = NULL; + iTCO_wdt_private.smi_res = NULL; + iTCO_wdt_private.gcs_pmc_res = NULL; + iTCO_wdt_private.gcs_pmc = NULL; } -static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static int iTCO_wdt_probe(struct platform_device *dev) { - 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; - } -} + int ret = -ENODEV; + unsigned long val32; + struct lpc_ich_info *ich_info = dev_get_platdata(&dev->dev); -/* - * Kernel Interfaces - */ + if (!ich_info) + goto out; -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, -}; + spin_lock_init(&iTCO_wdt_private.io_lock); -static struct miscdevice iTCO_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &iTCO_wdt_fops, -}; + iTCO_wdt_private.tco_res = + platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO); + if (!iTCO_wdt_private.tco_res) + goto out; -/* - * Init & exit routines - */ + iTCO_wdt_private.smi_res = + platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI); + if (!iTCO_wdt_private.smi_res) + goto out; -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; + iTCO_wdt_private.iTCO_version = ich_info->iTCO_version; + iTCO_wdt_private.dev = dev; + iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent); /* - * Find the ACPI/PM base I/O address which is the base - * for the TCO registers (TCOBASE=ACPIBASE + 0x60) - * ACPIBASE is bits [15:7] from 0x40-0x43 + * Get the Memory-Mapped GCS or PMC register, we need it for the + * NO_REBOOT flag (TCO v2 and v3). */ - 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; + if (iTCO_wdt_private.iTCO_version >= 2) { + iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev, + IORESOURCE_MEM, + ICH_RES_MEM_GCS_PMC); + + if (!iTCO_wdt_private.gcs_pmc_res) + goto out; + + if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start, + resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) { + ret = -EBUSY; goto out; } - RCBA = base_address & 0xffffc000; - iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4); + iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start, + resource_size(iTCO_wdt_private.gcs_pmc_res)); + if (!iTCO_wdt_private.gcs_pmc) { + ret = -EIO; + goto unreg_gcs_pmc; + } } /* 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"); + pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n"); ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ - goto out_unmap; + goto unmap_gcs_pmc; } /* 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; + if (!request_region(iTCO_wdt_private.smi_res->start, + resource_size(iTCO_wdt_private.smi_res), dev->name)) { + pr_err("I/O address 0x%04llx already in use, device disabled\n", + (u64)SMI_EN); + ret = -EBUSY; + goto unmap_gcs_pmc; } if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) { - /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */ + /* + * 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; + if (!request_region(iTCO_wdt_private.tco_res->start, + resource_size(iTCO_wdt_private.tco_res), dev->name)) { + pr_err("I/O address 0x%04llx already in use, device disabled\n", + (u64)TCOBASE); + ret = -EBUSY; + goto unreg_smi; } - 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); + pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n", + ich_info->name, ich_info->iTCO_version, (u64)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 */ + if (iTCO_wdt_private.iTCO_version == 3) { + outl(0x20008, TCO1_STS); + } else { + 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 */ + } + + iTCO_wdt_watchdog_dev.bootstatus = 0; + iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT; + watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout); + iTCO_wdt_watchdog_dev.parent = &dev->dev; /* Make sure the watchdog is not running */ - iTCO_wdt_stop(); + iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); /* 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); + if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) { + iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT); + pr_info("timeout value out of range, using %d\n", + WATCHDOG_TIMEOUT); } - ret = misc_register(&iTCO_wdt_miscdev); + ret = watchdog_register_device(&iTCO_wdt_watchdog_dev); if (ret != 0) { - printk(KERN_ERR PFX - "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - goto unreg_region; + pr_err("cannot register watchdog device (err=%d)\n", ret); + goto unreg_tco; } - printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", - heartbeat, nowayout); + pr_info("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); +unreg_tco: + release_region(iTCO_wdt_private.tco_res->start, + resource_size(iTCO_wdt_private.tco_res)); +unreg_smi: + release_region(iTCO_wdt_private.smi_res->start, + resource_size(iTCO_wdt_private.smi_res)); +unmap_gcs_pmc: + if (iTCO_wdt_private.iTCO_version >= 2) + iounmap(iTCO_wdt_private.gcs_pmc); +unreg_gcs_pmc: + if (iTCO_wdt_private.iTCO_version >= 2) + release_mem_region(iTCO_wdt_private.gcs_pmc_res->start, + resource_size(iTCO_wdt_private.gcs_pmc_res)); 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"); + iTCO_wdt_private.tco_res = NULL; + iTCO_wdt_private.smi_res = NULL; + iTCO_wdt_private.gcs_pmc_res = NULL; + iTCO_wdt_private.gcs_pmc = NULL; return ret; } -static int __devexit iTCO_wdt_remove(struct platform_device *dev) +static int iTCO_wdt_remove(struct platform_device *dev) { - if (iTCO_wdt_private.ACPIBASE) + if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res) iTCO_wdt_cleanup(); return 0; @@ -962,12 +568,12 @@ static int __devexit iTCO_wdt_remove(struct platform_device *dev) static void iTCO_wdt_shutdown(struct platform_device *dev) { - iTCO_wdt_stop(); + iTCO_wdt_stop(NULL); } static struct platform_driver iTCO_wdt_driver = { .probe = iTCO_wdt_probe, - .remove = __devexit_p(iTCO_wdt_remove), + .remove = iTCO_wdt_remove, .shutdown = iTCO_wdt_shutdown, .driver = { .owner = THIS_MODULE, @@ -979,32 +585,19 @@ static int __init iTCO_wdt_init_module(void) { int err; - printk(KERN_INFO PFX "Intel TCO WatchDog Timer Driver v%s\n", - DRV_VERSION); + pr_info("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"); + pr_info("Watchdog Module Unloaded\n"); } module_init(iTCO_wdt_init_module); @@ -1014,4 +607,4 @@ 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); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c index 0149d8dfc81..4247c498ee7 100644 --- a/drivers/watchdog/ib700wdt.c +++ b/drivers/watchdog/ib700wdt.c @@ -31,6 +31,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/types.h> #include <linux/miscdevice.h> @@ -44,7 +46,6 @@ #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; @@ -53,7 +54,6 @@ static char expect_close; /* Module information */ #define DRV_NAME "ib700wdt" -#define PFX DRV_NAME ": " /* * @@ -102,8 +102,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -246,8 +246,7 @@ 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"); + pr_crit("WDT device closed unexpectedly. WDT will not stop!\n"); ibwdt_ping(); } clear_bit(0, &ibwdt_is_open); @@ -278,22 +277,20 @@ static struct miscdevice ibwdt_miscdev = { * Init & exit routines */ -static int __devinit ibwdt_probe(struct platform_device *dev) +static int __init 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); + pr_err("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); + pr_err("START method I/O %X is not available\n", WDT_START); res = -EIO; goto out_nostartreg; } @@ -302,13 +299,12 @@ static int __devinit ibwdt_probe(struct platform_device *dev) * 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); + pr_info("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"); + pr_err("failed to register misc device\n"); goto out_nomisc; } return 0; @@ -323,7 +319,7 @@ out_nostopreg: return res; } -static int __devexit ibwdt_remove(struct platform_device *dev) +static int ibwdt_remove(struct platform_device *dev) { misc_deregister(&ibwdt_miscdev); release_region(WDT_START, 1); @@ -340,8 +336,7 @@ static void ibwdt_shutdown(struct platform_device *dev) } static struct platform_driver ibwdt_driver = { - .probe = ibwdt_probe, - .remove = __devexit_p(ibwdt_remove), + .remove = ibwdt_remove, .shutdown = ibwdt_shutdown, .driver = { .owner = THIS_MODULE, @@ -353,24 +348,21 @@ 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; + pr_info("WDT driver for IB700 single board computer initialising\n"); 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; - } + if (IS_ERR(ibwdt_platform_device)) + return PTR_ERR(ibwdt_platform_device); + + err = platform_driver_probe(&ibwdt_driver, ibwdt_probe); + if (err) + goto unreg_platform_device; return 0; -unreg_platform_driver: - platform_driver_unregister(&ibwdt_driver); +unreg_platform_device: + platform_device_unregister(ibwdt_platform_device); return err; } @@ -378,7 +370,7 @@ 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"); + pr_info("Watchdog Module Unloaded\n"); } module_init(ibwdt_init); @@ -387,6 +379,5 @@ 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 index c7481ad5162..366b0474f27 100644 --- a/drivers/watchdog/ibmasr.c +++ b/drivers/watchdog/ibmasr.c @@ -10,6 +10,8 @@ * of the GNU Public License, incorporated herein by reference. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/fs.h> #include <linux/kernel.h> #include <linux/module.h> @@ -31,8 +33,6 @@ enum { ASMTYPE_SPRUCE, }; -#define PFX "ibmasr: " - #define TOPAZ_ASR_REG_OFFSET 4 #define TOPAZ_ASR_TOGGLE 0x40 #define TOPAZ_ASR_DISABLE 0x80 @@ -60,7 +60,7 @@ enum { #define SPRUCE_ASR_TOGGLE_MASK 0x02 /* bit 0: 0, then 1, then 0 */ -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; static unsigned long asr_is_open; static char asr_expect_close; @@ -234,12 +234,11 @@ static int __init asr_get_base_address(void) } if (!request_region(asr_base, asr_length, "ibmasr")) { - printk(KERN_ERR PFX "address %#x already in use\n", - asr_base); + pr_err("address %#x already in use\n", asr_base); return -EBUSY; } - printk(KERN_INFO PFX "found %sASR @ addr %#x\n", type, asr_base); + pr_info("found %sASR @ addr %#x\n", type, asr_base); return 0; } @@ -332,8 +331,7 @@ 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"); + pr_crit("unexpected close, not stopping watchdog!\n"); asr_toggle(); } clear_bit(0, &asr_is_open); @@ -362,7 +360,7 @@ struct ibmasr_id { int type; }; -static struct ibmasr_id __initdata ibmasr_id_table[] = { +static struct ibmasr_id ibmasr_id_table[] __initdata = { { "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 }, @@ -393,7 +391,7 @@ static int __init ibmasr_init(void) rc = misc_register(&asr_miscdev); if (rc < 0) { release_region(asr_base, asr_length); - printk(KERN_ERR PFX "failed to register misc device\n"); + pr_err("failed to register misc device\n"); return rc; } @@ -413,7 +411,7 @@ static void __exit ibmasr_exit(void) module_init(ibmasr_init); module_exit(ibmasr_exit); -module_param(nowayout, int, 0); +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -421,4 +419,3 @@ MODULE_PARM_DESC(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/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c new file mode 100644 index 00000000000..07f88f54e5c --- /dev/null +++ b/drivers/watchdog/ie6xx_wdt.c @@ -0,0 +1,346 @@ +/* + * Intel Atom E6xx Watchdog driver + * + * Copyright (C) 2011 Alexander Stein + * <alexander.stein@systec-electronic.com> + * + * 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/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/watchdog.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/uaccess.h> +#include <linux/spinlock.h> + +#define DRIVER_NAME "ie6xx_wdt" + +#define PV1 0x00 +#define PV2 0x04 + +#define RR0 0x0c +#define RR1 0x0d +#define WDT_RELOAD 0x01 +#define WDT_TOUT 0x02 + +#define WDTCR 0x10 +#define WDT_PRE_SEL 0x04 +#define WDT_RESET_SEL 0x08 +#define WDT_RESET_EN 0x10 +#define WDT_TOUT_EN 0x20 + +#define DCR 0x14 + +#define WDTLR 0x18 +#define WDT_LOCK 0x01 +#define WDT_ENABLE 0x02 +#define WDT_TOUT_CNF 0x03 + +#define MIN_TIME 1 +#define MAX_TIME (10 * 60) /* 10 minutes */ +#define DEFAULT_TIME 60 + +static unsigned int timeout = DEFAULT_TIME; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Default Watchdog timer setting (" + __MODULE_STRING(DEFAULT_TIME) "s)." + "The range is from 1 to 600"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static u8 resetmode = 0x10; +module_param(resetmode, byte, 0); +MODULE_PARM_DESC(resetmode, + "Resetmode bits: 0x08 warm reset (cold reset otherwise), " + "0x10 reset enable, 0x20 disable toggle GPIO[4] (default=0x10)"); + +static struct { + unsigned short sch_wdtba; + struct spinlock unlock_sequence; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; +#endif +} ie6xx_wdt_data; + +/* + * This is needed to write to preload and reload registers + * struct ie6xx_wdt_data.unlock_sequence must be used + * to prevent sequence interrupts + */ +static void ie6xx_wdt_unlock_registers(void) +{ + outb(0x80, ie6xx_wdt_data.sch_wdtba + RR0); + outb(0x86, ie6xx_wdt_data.sch_wdtba + RR0); +} + +static int ie6xx_wdt_ping(struct watchdog_device *wdd) +{ + spin_lock(&ie6xx_wdt_data.unlock_sequence); + ie6xx_wdt_unlock_registers(); + outb(WDT_RELOAD, ie6xx_wdt_data.sch_wdtba + RR1); + spin_unlock(&ie6xx_wdt_data.unlock_sequence); + return 0; +} + +static int ie6xx_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) +{ + u32 preload; + u64 clock; + u8 wdtcr; + + /* Watchdog clock is PCI Clock (33MHz) */ + clock = 33000000; + /* and the preload value is loaded into [34:15] of the down counter */ + preload = (t * clock) >> 15; + /* + * Manual states preload must be one less. + * Does not wrap as t is at least 1 + */ + preload -= 1; + + spin_lock(&ie6xx_wdt_data.unlock_sequence); + + /* Set ResetMode & Enable prescaler for range 10ms to 10 min */ + wdtcr = resetmode & 0x38; + outb(wdtcr, ie6xx_wdt_data.sch_wdtba + WDTCR); + + ie6xx_wdt_unlock_registers(); + outl(0, ie6xx_wdt_data.sch_wdtba + PV1); + + ie6xx_wdt_unlock_registers(); + outl(preload, ie6xx_wdt_data.sch_wdtba + PV2); + + ie6xx_wdt_unlock_registers(); + outb(WDT_RELOAD | WDT_TOUT, ie6xx_wdt_data.sch_wdtba + RR1); + + spin_unlock(&ie6xx_wdt_data.unlock_sequence); + + wdd->timeout = t; + return 0; +} + +static int ie6xx_wdt_start(struct watchdog_device *wdd) +{ + ie6xx_wdt_set_timeout(wdd, wdd->timeout); + + /* Enable the watchdog timer */ + spin_lock(&ie6xx_wdt_data.unlock_sequence); + outb(WDT_ENABLE, ie6xx_wdt_data.sch_wdtba + WDTLR); + spin_unlock(&ie6xx_wdt_data.unlock_sequence); + + return 0; +} + +static int ie6xx_wdt_stop(struct watchdog_device *wdd) +{ + if (inb(ie6xx_wdt_data.sch_wdtba + WDTLR) & WDT_LOCK) + return -1; + + /* Disable the watchdog timer */ + spin_lock(&ie6xx_wdt_data.unlock_sequence); + outb(0, ie6xx_wdt_data.sch_wdtba + WDTLR); + spin_unlock(&ie6xx_wdt_data.unlock_sequence); + + return 0; +} + +static const struct watchdog_info ie6xx_wdt_info = { + .identity = "Intel Atom E6xx Watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, +}; + +static const struct watchdog_ops ie6xx_wdt_ops = { + .owner = THIS_MODULE, + .start = ie6xx_wdt_start, + .stop = ie6xx_wdt_stop, + .ping = ie6xx_wdt_ping, + .set_timeout = ie6xx_wdt_set_timeout, +}; + +static struct watchdog_device ie6xx_wdt_dev = { + .info = &ie6xx_wdt_info, + .ops = &ie6xx_wdt_ops, + .min_timeout = MIN_TIME, + .max_timeout = MAX_TIME, +}; + +#ifdef CONFIG_DEBUG_FS + +static int ie6xx_wdt_dbg_show(struct seq_file *s, void *unused) +{ + seq_printf(s, "PV1 = 0x%08x\n", + inl(ie6xx_wdt_data.sch_wdtba + PV1)); + seq_printf(s, "PV2 = 0x%08x\n", + inl(ie6xx_wdt_data.sch_wdtba + PV2)); + seq_printf(s, "RR = 0x%08x\n", + inw(ie6xx_wdt_data.sch_wdtba + RR0)); + seq_printf(s, "WDTCR = 0x%08x\n", + inw(ie6xx_wdt_data.sch_wdtba + WDTCR)); + seq_printf(s, "DCR = 0x%08x\n", + inl(ie6xx_wdt_data.sch_wdtba + DCR)); + seq_printf(s, "WDTLR = 0x%08x\n", + inw(ie6xx_wdt_data.sch_wdtba + WDTLR)); + + seq_printf(s, "\n"); + return 0; +} + +static int ie6xx_wdt_dbg_open(struct inode *inode, struct file *file) +{ + return single_open(file, ie6xx_wdt_dbg_show, NULL); +} + +static const struct file_operations ie6xx_wdt_dbg_operations = { + .open = ie6xx_wdt_dbg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void ie6xx_wdt_debugfs_init(void) +{ + /* /sys/kernel/debug/ie6xx_wdt */ + ie6xx_wdt_data.debugfs = debugfs_create_file("ie6xx_wdt", + S_IFREG | S_IRUGO, NULL, NULL, &ie6xx_wdt_dbg_operations); +} + +static void ie6xx_wdt_debugfs_exit(void) +{ + debugfs_remove(ie6xx_wdt_data.debugfs); +} + +#else +static void ie6xx_wdt_debugfs_init(void) +{ +} + +static void ie6xx_wdt_debugfs_exit(void) +{ +} +#endif + +static int ie6xx_wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + u8 wdtlr; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -ENODEV; + + if (!request_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Watchdog region 0x%llx already in use!\n", + (u64)res->start); + return -EBUSY; + } + + ie6xx_wdt_data.sch_wdtba = res->start; + dev_dbg(&pdev->dev, "WDT = 0x%X\n", ie6xx_wdt_data.sch_wdtba); + + ie6xx_wdt_dev.timeout = timeout; + watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout); + + spin_lock_init(&ie6xx_wdt_data.unlock_sequence); + + wdtlr = inb(ie6xx_wdt_data.sch_wdtba + WDTLR); + if (wdtlr & WDT_LOCK) + dev_warn(&pdev->dev, + "Watchdog Timer is Locked (Reg=0x%x)\n", wdtlr); + + ie6xx_wdt_debugfs_init(); + + ret = watchdog_register_device(&ie6xx_wdt_dev); + if (ret) { + dev_err(&pdev->dev, + "Watchdog timer: cannot register device (err =%d)\n", + ret); + goto misc_register_error; + } + + return 0; + +misc_register_error: + ie6xx_wdt_debugfs_exit(); + release_region(res->start, resource_size(res)); + ie6xx_wdt_data.sch_wdtba = 0; + return ret; +} + +static int ie6xx_wdt_remove(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + ie6xx_wdt_stop(NULL); + watchdog_unregister_device(&ie6xx_wdt_dev); + ie6xx_wdt_debugfs_exit(); + release_region(res->start, resource_size(res)); + ie6xx_wdt_data.sch_wdtba = 0; + + return 0; +} + +static struct platform_driver ie6xx_wdt_driver = { + .probe = ie6xx_wdt_probe, + .remove = ie6xx_wdt_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init ie6xx_wdt_init(void) +{ + /* Check boot parameters to verify that their initial values */ + /* are in range. */ + if ((timeout < MIN_TIME) || + (timeout > MAX_TIME)) { + pr_err("Watchdog timer: value of timeout %d (dec) " + "is out of range from %d to %d (dec)\n", + timeout, MIN_TIME, MAX_TIME); + return -EINVAL; + } + + return platform_driver_register(&ie6xx_wdt_driver); +} + +static void __exit ie6xx_wdt_exit(void) +{ + platform_driver_unregister(&ie6xx_wdt_driver); +} + +late_initcall(ie6xx_wdt_init); +module_exit(ie6xx_wdt_exit); + +MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>"); +MODULE_DESCRIPTION("Intel Atom E6xx Watchdog Device Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index c44c3334003..9d4874f0994 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -2,6 +2,7 @@ * Watchdog driver for IMX2 and later processors * * Copyright (C) 2010 Wolfram Sang, Pengutronix e.K. <w.sang@pengutronix.de> + * Copyright (C) 2014 Freescale Semiconductor, Inc. * * some parts adapted by similar drivers from Darius Augulis and Vladimir * Zapolskiy, additional improvements by Wim Van Sebroeck. @@ -20,20 +21,17 @@ * Halt on suspend: Manual Can be automatic */ +#include <linux/clk.h> #include <linux/init.h> +#include <linux/io.h> +#include <linux/jiffies.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/regmap.h> #include <linux/timer.h> -#include <linux/jiffies.h> -#include <mach/hardware.h> +#include <linux/watchdog.h> #define DRIVER_NAME "imx2-wdt" @@ -41,32 +39,29 @@ #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_WRSR 0x04 /* Reset Status Register */ +#define IMX2_WDT_WRSR_TOUT (1 << 1) /* -> Reset due to Timeout */ + #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 IMX2_WDT_STATUS_OPEN 0 -#define IMX2_WDT_STATUS_STARTED 1 -#define IMX2_WDT_EXPECT_CLOSE 2 - -static struct { +struct imx2_wdt_device { struct clk *clk; - void __iomem *base; - unsigned timeout; - unsigned long status; + struct regmap *regmap; struct timer_list timer; /* Pings the watchdog when closed */ -} imx2_wdt; - -static struct miscdevice imx2_wdt_miscdev; + struct watchdog_device wdog; +}; -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -81,10 +76,15 @@ static const struct watchdog_info imx2_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, }; -static inline void imx2_wdt_setup(void) +static inline void imx2_wdt_setup(struct watchdog_device *wdog) { - u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + u32 val; + + regmap_read(wdev->regmap, IMX2_WDT_WCR, &val); + /* Suspend 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 */ @@ -92,231 +92,199 @@ static inline void imx2_wdt_setup(void) /* Keep Watchdog Disabled */ val &= ~IMX2_WDT_WCR_WDE; /* Set the watchdog's Time-Out value */ - val |= WDOG_SEC_TO_COUNT(imx2_wdt.timeout); + val |= WDOG_SEC_TO_COUNT(wdog->timeout); - __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); + regmap_write(wdev->regmap, IMX2_WDT_WCR, val); /* enable the watchdog */ val |= IMX2_WDT_WCR_WDE; - __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); + regmap_write(wdev->regmap, IMX2_WDT_WCR, val); } -static inline void imx2_wdt_ping(void) +static inline bool imx2_wdt_is_running(struct imx2_wdt_device *wdev) { - __raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR); - __raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR); -} + u32 val; -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); + regmap_read(wdev->regmap, IMX2_WDT_WCR, &val); + + return val & IMX2_WDT_WCR_WDE; } -static void imx2_wdt_start(void) +static int imx2_wdt_ping(struct watchdog_device *wdog) { - 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); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); - 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(); + regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1); + regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ2); + return 0; } -static void imx2_wdt_stop(void) +static void imx2_wdt_timer_ping(unsigned long arg) { - /* 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); + struct watchdog_device *wdog = (struct watchdog_device *)arg; + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + /* ping it every wdog->timeout / 2 seconds to prevent reboot */ + imx2_wdt_ping(wdog); + mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2); } -static void imx2_wdt_set_timeout(int new_timeout) +static int imx2_wdt_set_timeout(struct watchdog_device *wdog, + unsigned int new_timeout) { - u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); - /* 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); + regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT, + WDOG_SEC_TO_COUNT(new_timeout)); + return 0; } -static int imx2_wdt_open(struct inode *inode, struct file *file) +static int imx2_wdt_start(struct watchdog_device *wdog) { - if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status)) - return -EBUSY; + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); - imx2_wdt_start(); - return nonseekable_open(inode, file); + if (imx2_wdt_is_running(wdev)) { + /* delete the timer that pings the watchdog after close */ + del_timer_sync(&wdev->timer); + imx2_wdt_set_timeout(wdog, wdog->timeout); + } else + imx2_wdt_setup(wdog); + + return imx2_wdt_ping(wdog); } -static int imx2_wdt_close(struct inode *inode, struct file *file) +static int imx2_wdt_stop(struct watchdog_device *wdog) { - 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); + /* + * 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((unsigned long)wdog); return 0; } -static long imx2_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog) { - 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); - - default: - return -ENOTTY; - } -} + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); -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); + if (imx2_wdt_is_running(wdev)) { + imx2_wdt_set_timeout(wdog, wdog->timeout); + imx2_wdt_timer_ping((unsigned long)wdog); } - - imx2_wdt_ping(); - return len; } -static const struct file_operations imx2_wdt_fops = { +static struct watchdog_ops imx2_wdt_ops = { .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = imx2_wdt_ioctl, - .open = imx2_wdt_open, - .release = imx2_wdt_close, - .write = imx2_wdt_write, + .start = imx2_wdt_start, + .stop = imx2_wdt_stop, + .ping = imx2_wdt_ping, + .set_timeout = imx2_wdt_set_timeout, }; -static struct miscdevice imx2_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &imx2_wdt_fops, +static struct regmap_config imx2_wdt_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x8, }; static int __init imx2_wdt_probe(struct platform_device *pdev) { - int ret; + struct imx2_wdt_device *wdev; + struct watchdog_device *wdog; struct resource *res; + void __iomem *base; + int ret; + u32 val; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "can't get device resources\n"); - return -ENODEV; - } - - imx2_wdt.base = devm_request_and_ioremap(&pdev->dev, res); - if (!imx2_wdt.base) { - dev_err(&pdev->dev, "ioremap failed\n"); + wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + &imx2_wdt_regmap_config); + if (IS_ERR(wdev->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(wdev->regmap); } - imx2_wdt.clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(imx2_wdt.clk)) { + wdev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdev->clk)) { dev_err(&pdev->dev, "can't get Watchdog clock\n"); - return PTR_ERR(imx2_wdt.clk); + return PTR_ERR(wdev->clk); } - 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); + wdog = &wdev->wdog; + wdog->info = &imx2_wdt_info; + wdog->ops = &imx2_wdt_ops; + wdog->min_timeout = 1; + wdog->max_timeout = IMX2_WDT_MAX_TIME; - setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0); + clk_prepare_enable(wdev->clk); - imx2_wdt_miscdev.parent = &pdev->dev; - ret = misc_register(&imx2_wdt_miscdev); - if (ret) - goto fail; + regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); + wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; - dev_info(&pdev->dev, - "IMX2+ Watchdog Timer enabled. timeout=%ds (nowayout=%d)\n", - imx2_wdt.timeout, nowayout); - return 0; + wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME); + if (wdog->timeout != timeout) + dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n", + timeout, wdog->timeout); + + platform_set_drvdata(pdev, wdog); + watchdog_set_drvdata(wdog, wdev); + watchdog_set_nowayout(wdog, nowayout); + watchdog_init_timeout(wdog, timeout, &pdev->dev); -fail: - imx2_wdt_miscdev.parent = NULL; - clk_put(imx2_wdt.clk); - return ret; + setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog); + + imx2_wdt_ping_if_active(wdog); + + ret = watchdog_register_device(wdog); + if (ret) { + dev_err(&pdev->dev, "cannot register watchdog device\n"); + return ret; + } + + dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", + wdog->timeout, nowayout); + + return 0; } 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); + struct watchdog_device *wdog = platform_get_drvdata(pdev); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); - dev_crit(imx2_wdt_miscdev.parent, - "Device removed: Expect reboot!\n"); - } else - clk_put(imx2_wdt.clk); + watchdog_unregister_device(wdog); - imx2_wdt_miscdev.parent = NULL; + if (imx2_wdt_is_running(wdev)) { + del_timer_sync(&wdev->timer); + imx2_wdt_ping(wdog); + dev_crit(&pdev->dev, "Device removed: Expect reboot!\n"); + } 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"); + struct watchdog_device *wdog = platform_get_drvdata(pdev); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + if (imx2_wdt_is_running(wdev)) { + /* + * We are running, we need to delete the timer but will + * give max timeout before reboot will take place + */ + del_timer_sync(&wdev->timer); + imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); + imx2_wdt_ping(wdog); + dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n"); } } @@ -324,6 +292,7 @@ static const struct of_device_id imx2_wdt_dt_ids[] = { { .compatible = "fsl,imx21-wdt", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids); static struct platform_driver imx2_wdt_driver = { .remove = __exit_p(imx2_wdt_remove), @@ -335,20 +304,9 @@ static struct platform_driver imx2_wdt_driver = { }, }; -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_platform_driver_probe(imx2_wdt_driver, imx2_wdt_probe); 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 index 1475e09f9af..5d20cdd30ef 100644 --- a/drivers/watchdog/indydog.c +++ b/drivers/watchdog/indydog.c @@ -12,6 +12,8 @@ * based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -26,41 +28,31 @@ #include <linux/uaccess.h> #include <asm/sgi/mc.h> -#define PFX "indydog: " static unsigned long indydog_alive; static DEFINE_SPINLOCK(indydog_lock); #define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 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; + sgimc->cpuctrl0 |= SGIMC_CCTRL0_WDOG; 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; + sgimc->cpuctrl0 &= ~SGIMC_CCTRL0_WDOG; spin_unlock(&indydog_lock); - printk(KERN_INFO PFX "Stopped watchdog timer.\n"); + pr_info("Stopped watchdog timer\n"); } static void indydog_ping(void) @@ -83,7 +75,7 @@ static int indydog_open(struct inode *inode, struct file *file) indydog_start(); indydog_ping(); - printk(KERN_INFO "Started watchdog timer.\n"); + pr_info("Started watchdog timer\n"); return nonseekable_open(inode, file); } @@ -178,30 +170,25 @@ 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; ret = register_reboot_notifier(&indydog_notifier); if (ret) { - printk(KERN_ERR PFX - "cannot register reboot notifier (err=%d)\n", ret); + pr_err("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); + pr_err("cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); unregister_reboot_notifier(&indydog_notifier); return ret; } - printk(banner); + pr_info("Hardware Watchdog Timer for SGI IP22: 0.3\n"); return 0; } @@ -218,4 +205,3 @@ 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-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c new file mode 100644 index 00000000000..ca66e8e7463 --- /dev/null +++ b/drivers/watchdog/intel-mid_wdt.c @@ -0,0 +1,184 @@ +/* + * intel-mid_wdt: generic Intel MID SCU watchdog driver + * + * Platforms supported so far: + * - Merrifield only + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * Contact: David Cohen <david.a.cohen@linux.intel.com> + * + * 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. + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/nmi.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> +#include <linux/platform_data/intel-mid_wdt.h> + +#include <asm/intel_scu_ipc.h> +#include <asm/intel-mid.h> + +#define IPC_WATCHDOG 0xf8 + +#define MID_WDT_PRETIMEOUT 15 +#define MID_WDT_TIMEOUT_MIN (1 + MID_WDT_PRETIMEOUT) +#define MID_WDT_TIMEOUT_MAX 170 +#define MID_WDT_DEFAULT_TIMEOUT 90 + +/* SCU watchdog messages */ +enum { + SCU_WATCHDOG_START = 0, + SCU_WATCHDOG_STOP, + SCU_WATCHDOG_KEEPALIVE, +}; + +static inline int wdt_command(int sub, u32 *in, int inlen) +{ + return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0); +} + +static int wdt_start(struct watchdog_device *wd) +{ + int ret, in_size; + int timeout = wd->timeout; + struct ipc_wd_start { + u32 pretimeout; + u32 timeout; + } ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout }; + + /* + * SCU expects the input size for watchdog IPC to + * be based on 4 bytes + */ + in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4); + + ret = wdt_command(SCU_WATCHDOG_START, (u32 *)&ipc_wd_start, in_size); + if (ret) { + struct device *dev = watchdog_get_drvdata(wd); + dev_crit(dev, "error starting watchdog: %d\n", ret); + } + + return ret; +} + +static int wdt_ping(struct watchdog_device *wd) +{ + int ret; + + ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0); + if (ret) { + struct device *dev = watchdog_get_drvdata(wd); + dev_crit(dev, "Error executing keepalive: 0x%x\n", ret); + } + + return ret; +} + +static int wdt_stop(struct watchdog_device *wd) +{ + int ret; + + ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0); + if (ret) { + struct device *dev = watchdog_get_drvdata(wd); + dev_crit(dev, "Error stopping watchdog: 0x%x\n", ret); + } + + return ret; +} + +static irqreturn_t mid_wdt_irq(int irq, void *dev_id) +{ + panic("Kernel Watchdog"); + + /* This code should not be reached */ + return IRQ_HANDLED; +} + +static const struct watchdog_info mid_wdt_info = { + .identity = "Intel MID SCU watchdog", + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, +}; + +static const struct watchdog_ops mid_wdt_ops = { + .owner = THIS_MODULE, + .start = wdt_start, + .stop = wdt_stop, + .ping = wdt_ping, +}; + +static int mid_wdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdt_dev; + struct intel_mid_wdt_pdata *pdata = pdev->dev.platform_data; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); + return -EINVAL; + } + + if (pdata->probe) { + ret = pdata->probe(pdev); + if (ret) + return ret; + } + + wdt_dev = devm_kzalloc(&pdev->dev, sizeof(*wdt_dev), GFP_KERNEL); + if (!wdt_dev) + return -ENOMEM; + + wdt_dev->info = &mid_wdt_info; + wdt_dev->ops = &mid_wdt_ops; + wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN; + wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX; + wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT; + + watchdog_set_drvdata(wdt_dev, &pdev->dev); + platform_set_drvdata(pdev, wdt_dev); + + ret = devm_request_irq(&pdev->dev, pdata->irq, mid_wdt_irq, + IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog", + wdt_dev); + if (ret) { + dev_err(&pdev->dev, "error requesting warning irq %d\n", + pdata->irq); + return ret; + } + + ret = watchdog_register_device(wdt_dev); + if (ret) { + dev_err(&pdev->dev, "error registering watchdog device\n"); + return ret; + } + + dev_info(&pdev->dev, "Intel MID watchdog device probed\n"); + + return 0; +} + +static int mid_wdt_remove(struct platform_device *pdev) +{ + struct watchdog_device *wd = platform_get_drvdata(pdev); + watchdog_unregister_device(wd); + return 0; +} + +static struct platform_driver mid_wdt_driver = { + .probe = mid_wdt_probe, + .remove = mid_wdt_remove, + .driver = { + .owner = THIS_MODULE, + .name = "intel_mid_wdt", + }, +}; + +module_platform_driver(mid_wdt_driver); + +MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>"); +MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c index 1abdc0454c5..0caab6241eb 100644 --- a/drivers/watchdog/intel_scu_watchdog.c +++ b/drivers/watchdog/intel_scu_watchdog.c @@ -22,6 +22,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/compiler.h> #include <linux/module.h> #include <linux/kernel.h> @@ -46,7 +48,7 @@ #include <linux/atomic.h> #include <asm/intel_scu_ipc.h> #include <asm/apb_timer.h> -#include <asm/mrst.h> +#include <asm/intel-mid.h> #include "intel_scu_watchdog.h" @@ -96,15 +98,14 @@ static struct intel_scu_watchdog_dev watchdog_device; static void watchdog_fire(void) { if (force_boot) { - printk(KERN_CRIT PFX "Initiating system reboot.\n"); + pr_crit("Initiating system reboot\n"); emergency_restart(); - printk(KERN_CRIT PFX "Reboot didn't ?????\n"); + pr_crit("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"); + pr_crit("Immediate Reboot Disabled\n"); + pr_crit("System will reset when watchdog timer times out!\n"); } } @@ -112,8 +113,8 @@ 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); + pr_debug("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; @@ -156,14 +157,14 @@ 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); + pr_debug("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"); + pr_debug("spurious interrupt received\n"); return IRQ_HANDLED; } @@ -210,7 +211,6 @@ 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; @@ -220,16 +220,15 @@ static int intel_scu_set_heartbeat(u32 t) (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); + pr_debug("set_heartbeat: timer freq is %d\n", + watchdog_device.timer_tbl_ptr->freq_hz); + pr_debug("set_heartbeat: timer_set is %x (hex)\n", + watchdog_device.timer_set); + pr_debug("set_hearbeat: timer_margin is %x (hex)\n", timer_margin); + pr_debug("set_heartbeat: threshold is %x (hex)\n", + watchdog_device.threshold); + pr_debug("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. */ @@ -264,7 +263,7 @@ static int intel_scu_set_heartbeat(u32 t) if (MAX_RETRY < retry_count++) { /* Unable to set timer value */ - pr_err("Watchdog timer: Unable to set timer\n"); + pr_err("Unable to set timer\n"); return -ENODEV; } @@ -273,8 +272,7 @@ static int intel_scu_set_heartbeat(u32 t) 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; + ioread32(watchdog_device.timer_load_count_addr); /* Start the timer */ iowrite32(0x00000003, watchdog_device.timer_control_addr); @@ -321,18 +319,17 @@ static int intel_scu_release(struct inode *inode, struct file *file) */ if (!test_and_clear_bit(0, &watchdog_device.driver_open)) { - pr_debug("Watchdog timer: intel_scu_release, without open\n"); + pr_debug("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"); + pr_debug("closed, without starting timer\n"); return 0; } - printk(KERN_CRIT PFX - "Unexpected close of /dev/watchdog!\n"); + pr_crit("Unexpected close of /dev/watchdog!\n"); /* Since the timer was started, prevent future reopens */ watchdog_device.driver_closed = 1; @@ -446,7 +443,7 @@ static int __init intel_scu_watchdog_init(void) * * If it isn't an intel MID device then it doesn't have this watchdog */ - if (!mrst_identify_cpu()) + if (!intel_mid_identify_cpu()) return -ENODEV; /* Check boot parameters to verify that their initial values */ @@ -454,9 +451,8 @@ static int __init intel_scu_watchdog_init(void) /* 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); + pr_err("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; } @@ -467,19 +463,18 @@ static int __init intel_scu_watchdog_init(void) 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"); + pr_debug("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); + pr_debug("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); + pr_debug("timer %d invalid irq\n", sfi_mtimer_num); return -ENODEV; } @@ -487,7 +482,7 @@ static int __init intel_scu_watchdog_init(void) 20); if (tmp_addr == NULL) { - pr_debug("Watchdog timer: timer unable to ioremap\n"); + pr_debug("timer unable to ioremap\n"); return -ENOMEM; } @@ -512,7 +507,7 @@ static int __init intel_scu_watchdog_init(void) ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier); if (ret) { - pr_err("Watchdog timer: cannot register notifier %d)\n", ret); + pr_err("cannot register notifier %d)\n", ret); goto register_reboot_error; } @@ -522,8 +517,8 @@ static int __init intel_scu_watchdog_init(void) ret = misc_register(&watchdog_device.miscdev); if (ret) { - pr_err("Watchdog timer: cannot register miscdev %d err =%d\n", - WATCHDOG_MINOR, ret); + pr_err("cannot register miscdev %d err =%d\n", + WATCHDOG_MINOR, ret); goto misc_register_error; } @@ -532,7 +527,7 @@ static int __init intel_scu_watchdog_init(void) IRQF_SHARED, "watchdog", &watchdog_device.timer_load_count_addr); if (ret) { - pr_err("Watchdog timer: error requesting irq %d\n", ret); + pr_err("error requesting irq %d\n", ret); goto request_irq_error; } /* Make sure timer is disabled before returning */ @@ -567,5 +562,4 @@ 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 index d2b074a82db..f3ac608deb6 100644 --- a/drivers/watchdog/intel_scu_watchdog.h +++ b/drivers/watchdog/intel_scu_watchdog.h @@ -25,7 +25,6 @@ #ifndef __INTEL_SCU_WATCHDOG_H #define __INTEL_SCU_WATCHDOG_H -#define PFX "Intel_SCU: " #define WDT_VER "0.3" /* minimum time between interrupts */ diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c index 82fa7a92a8d..b16013ffacc 100644 --- a/drivers/watchdog/iop_wdt.c +++ b/drivers/watchdog/iop_wdt.c @@ -24,6 +24,8 @@ * Dan Williams <dan.j.williams@intel.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> @@ -34,7 +36,7 @@ #include <linux/uaccess.h> #include <mach/hardware.h> -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; static unsigned long wdt_status; static unsigned long boot_status; static DEFINE_SPINLOCK(wdt_lock); @@ -85,7 +87,7 @@ static int wdt_disable(void) write_wdtcr(IOP_WDTCR_DIS); clear_bit(WDT_ENABLED, &wdt_status); spin_unlock(&wdt_lock); - printk(KERN_INFO "WATCHDOG: Disabled\n"); + pr_info("Disabled\n"); return 0; } else return 1; @@ -197,8 +199,8 @@ static int iop_wdt_release(struct inode *inode, struct file *file) */ if (state != 0) { wdt_enable(); - printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " - "reset in %lu seconds\n", iop_watchdog_timeout()); + pr_crit("Device closed unexpectedly - reset in %lu seconds\n", + iop_watchdog_timeout()); } clear_bit(WDT_IN_USE, &wdt_status); @@ -238,8 +240,7 @@ static int __init iop_wdt_init(void) 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()); + pr_info("timeout %lu sec\n", iop_watchdog_timeout()); return ret; } @@ -252,10 +253,9 @@ static void __exit iop_wdt_exit(void) module_init(iop_wdt_init); module_exit(iop_wdt_exit); -module_param(nowayout, int, 0); +module_param(nowayout, bool, 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 index 8d2d8502d3e..41b3979a9d8 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c @@ -20,6 +20,8 @@ * software is provided AS-IS with no warranties. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -33,20 +35,20 @@ #include <linux/io.h> #include <linux/ioport.h> +#define DEBUG #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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); static unsigned long wdt_open; @@ -158,10 +160,10 @@ static void it8712f_wdt_update_margin(void) */ if (units <= max_units) { config |= WDT_UNIT_SEC; /* else UNIT is MINUTES */ - printk(KERN_INFO NAME ": timer margin %d seconds\n", units); + pr_info("timer margin %d seconds\n", units); } else { units /= 60; - printk(KERN_INFO NAME ": timer margin %d minutes\n", units); + pr_info("timer margin %d minutes\n", units); } superio_outb(config, WDT_CONFIG); @@ -184,7 +186,7 @@ static int it8712f_wdt_enable(void) if (ret) return ret; - printk(KERN_DEBUG NAME ": enabling watchdog timer\n"); + pr_debug("enabling watchdog timer\n"); superio_select(LDN_GPIO); superio_outb(wdt_control_reg, WDT_CONTROL); @@ -204,7 +206,7 @@ static int it8712f_wdt_disable(void) if (ret) return ret; - printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); + pr_debug("disabling watchdog timer\n"); superio_select(LDN_GPIO); superio_outb(0, WDT_CONFIG); @@ -331,12 +333,10 @@ static int it8712f_wdt_open(struct inode *inode, struct file *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"); + pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n"); } else if (!nowayout) { if (it8712f_wdt_disable()) - printk(KERN_WARNING NAME "Watchdog disable failed\n"); + pr_warn("Watchdog disable failed\n"); } expect_close = 0; clear_bit(0, &wdt_open); @@ -374,13 +374,13 @@ static int __init it8712f_wdt_find(unsigned short *address) superio_select(LDN_GAME); superio_outb(1, ACT_REG); if (!(superio_inb(ACT_REG) & 0x01)) { - printk(KERN_ERR NAME ": Device not activated, skipping\n"); + pr_err("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"); + pr_err("Base address not set, skipping\n"); goto exit; } @@ -394,8 +394,7 @@ static int __init it8712f_wdt_find(unsigned short *address) 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", + pr_info("Found IT%04xF chip revision %d - using DogFood address 0x%x\n", chip_type, revision, *address); exit: @@ -411,27 +410,26 @@ static int __init it8712f_wdt_init(void) return -ENODEV; if (!request_region(address, 1, "IT8712F Watchdog")) { - printk(KERN_WARNING NAME ": watchdog I/O region busy\n"); + pr_warn("watchdog I/O region busy\n"); return -EBUSY; } err = it8712f_wdt_disable(); if (err) { - printk(KERN_ERR NAME ": unable to disable watchdog timer.\n"); + pr_err("unable to disable watchdog timer\n"); goto out; } err = register_reboot_notifier(&it8712f_wdt_notifier); if (err) { - printk(KERN_ERR NAME ": unable to register reboot notifier\n"); + pr_err("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); + pr_err("cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, err); goto reboot_out; } diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index a2d9a1266a2..0b93739c010 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -12,7 +12,8 @@ * http://www.ite.com.tw/ * * Support of the watchdog timers, which are available on - * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721 and IT8726. + * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 + * and IT8728. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,6 +30,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -43,16 +46,15 @@ #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_NOCIR 0 #define DEFAULT_EXCLUSIVE 1 #define DEFAULT_TIMEOUT 60 #define DEFAULT_TESTMODE 0 @@ -84,6 +86,7 @@ #define IT8720_ID 0x8720 #define IT8721_ID 0x8721 #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ +#define IT8728_ID 0x8728 /* GPIO Configuration Registers LDN=0x07 */ #define WDTCTRL 0x71 @@ -95,7 +98,7 @@ #define WDT_CIRINT 0x80 #define WDT_MOUSEINT 0x40 #define WDT_KYBINT 0x20 -#define WDT_GAMEPORT 0x10 /* not in it8718, it8720, it8721 */ +#define WDT_GAMEPORT 0x10 /* not in it8718, it8720, it8721, it8728 */ #define WDT_FORCE 0x02 #define WDT_ZERO 0x01 @@ -134,19 +137,24 @@ #define WDTS_LOCKED 3 #define WDTS_USE_GP 4 #define WDTS_EXPECTED 5 +#define WDTS_USE_CIR 6 static unsigned int base, gpact, ciract, max_units, chip_type; static unsigned long wdt_status; static int nogameport = DEFAULT_NOGAMEPORT; +static int nocir = DEFAULT_NOCIR; static int exclusive = DEFAULT_EXCLUSIVE; static int timeout = DEFAULT_TIMEOUT; static int testmode = DEFAULT_TESTMODE; -static int nowayout = DEFAULT_NOWAYOUT; +static bool 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(nocir, int, 0); +MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default=" + __MODULE_STRING(DEFAULT_NOCIR)); module_param(exclusive, int, 0); MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default=" __MODULE_STRING(DEFAULT_EXCLUSIVE)); @@ -156,7 +164,7 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default=" 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_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default=" __MODULE_STRING(WATCHDOG_NOWAYOUT)); @@ -256,9 +264,17 @@ static void wdt_keepalive(void) { if (test_bit(WDTS_USE_GP, &wdt_status)) inb(base); - else + else if (test_bit(WDTS_USE_CIR, &wdt_status)) /* The timer reloads with around 5 msec delay */ outb(0x55, CIR_DR(base)); + else { + if (superio_enter()) + return; + + superio_select(GPIO); + wdt_update_timeout(); + superio_exit(); + } set_bit(WDTS_KEEPALIVE, &wdt_status); } @@ -271,7 +287,7 @@ static int wdt_start(void) superio_select(GPIO); if (test_bit(WDTS_USE_GP, &wdt_status)) superio_outb(WDT_GAMEPORT, WDTCTRL); - else + else if (test_bit(WDTS_USE_CIR, &wdt_status)) superio_outb(WDT_CIRINT, WDTCTRL); wdt_update_timeout(); @@ -428,8 +444,7 @@ static int wdt_release(struct inode *inode, struct file *file) clear_bit(WDTS_TIMER_RUN, &wdt_status); } else { wdt_keepalive(); - printk(KERN_CRIT PFX - "unexpected close, not stopping watchdog!\n"); + pr_crit("unexpected close, not stopping watchdog!\n"); } } clear_bit(WDTS_DEV_OPEN, &wdt_status); @@ -617,20 +632,19 @@ static int __init it87_wdt_init(void) case IT8718_ID: case IT8720_ID: case IT8721_ID: + case IT8728_ID: max_units = 65535; try_gameport = 0; break; case IT8705_ID: - printk(KERN_ERR PFX - "Unsupported Chip found, Chip %04x Revision %02x\n", + pr_err("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"); + pr_err("no device\n"); return -ENODEV; default: - printk(KERN_ERR PFX - "Unknown Chip found, Chip %04x Revision %04x\n", + pr_err("Unknown Chip found, Chip %04x Revision %04x\n", chip_type, chip_rev); return -ENODEV; } @@ -660,16 +674,14 @@ static int __init it87_wdt_init(void) } /* If we haven't Gameport support, try to get CIR support */ - if (!test_bit(WDTS_USE_GP, &wdt_status)) { + if (!nocir && !test_bit(WDTS_USE_GP, &wdt_status)) { if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { if (gp_rreq_fail) - printk(KERN_ERR PFX - "I/O Address 0x%04x and 0x%04x" - " already in use\n", base, CIR_BASE); + pr_err("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); + pr_err("I/O Address 0x%04x already in use\n", + CIR_BASE); rc = -EIO; goto err_out; } @@ -684,13 +696,13 @@ static int __init it87_wdt_init(void) superio_select(GAMEPORT); superio_outb(gpact, ACTREG); } + set_bit(WDTS_USE_CIR, &wdt_status); } 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); + pr_warn("Timeout value out of range, use default %d sec\n", + DEFAULT_TIMEOUT); } if (timeout > max_units) @@ -698,21 +710,19 @@ static int __init it87_wdt_init(void) rc = register_reboot_notifier(&wdt_notifier); if (rc) { - printk(KERN_ERR PFX - "Cannot register reboot notifier (err=%d)\n", rc); + pr_err("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); + pr_err("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)) { + if (test_bit(WDTS_USE_CIR, &wdt_status)) { outb(0x00, CIR_RCR(base)); outb(0xc0, CIR_TCR1(base)); outb(0x5c, CIR_TCR2(base)); @@ -722,10 +732,9 @@ static int __init it87_wdt_init(void) 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); + pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d nocir=%d)\n", + chip_type, chip_rev, timeout, + nowayout, testmode, exclusive, nogameport, nocir); superio_exit(); return 0; @@ -733,8 +742,10 @@ static int __init it87_wdt_init(void) 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)) { + if (test_bit(WDTS_USE_GP, &wdt_status)) + release_region(base, 1); + else if (test_bit(WDTS_USE_CIR, &wdt_status)) { + release_region(base, 8); superio_select(CIR); superio_outb(ciract, ACTREG); } @@ -760,7 +771,7 @@ static void __exit it87_wdt_exit(void) if (test_bit(WDTS_USE_GP, &wdt_status)) { superio_select(GAMEPORT); superio_outb(gpact, ACTREG); - } else { + } else if (test_bit(WDTS_USE_CIR, &wdt_status)) { superio_select(CIR); superio_outb(ciract, ACTREG); } @@ -769,7 +780,11 @@ static void __exit it87_wdt_exit(void) misc_deregister(&wdt_miscdev); unregister_reboot_notifier(&wdt_notifier); - release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); + + if (test_bit(WDTS_USE_GP, &wdt_status)) + release_region(base, 1); + else if (test_bit(WDTS_USE_CIR, &wdt_status)) + release_region(base, 8); } module_init(it87_wdt_init); @@ -778,4 +793,3 @@ 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 deleted file mode 100644 index 084f71aa855..00000000000 --- a/drivers/watchdog/ixp2000_wdt.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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 DEFINE_SPINLOCK(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; - 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 index 4fc2e9ac26f..f20cc53ff71 100644 --- a/drivers/watchdog/ixp4xx_wdt.c +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -13,6 +13,8 @@ * warranty of any kind, whether express or implied. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -25,7 +27,7 @@ #include <linux/uaccess.h> #include <mach/hardware.h> -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; static int heartbeat = 60; /* (secs) Default is 1 minute */ static unsigned long wdt_status; static unsigned long boot_status; @@ -147,8 +149,7 @@ 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"); + pr_crit("Device closed unexpectedly - timer will not stop\n"); clear_bit(WDT_IN_USE, &wdt_status); clear_bit(WDT_OK_TO_CLOSE, &wdt_status); @@ -176,8 +177,7 @@ 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"); + pr_err("Rev. A0 IXP42x CPU detected - watchdog disabled\n"); return -ENODEV; } @@ -185,8 +185,7 @@ static int __init ixp4xx_wdt_init(void) WDIOF_CARDRESET : 0; ret = misc_register(&ixp4xx_wdt_miscdev); if (ret == 0) - printk(KERN_INFO "IXP4xx Watchdog Timer: heartbeat %d sec\n", - heartbeat); + pr_info("timer heartbeat %d sec\n", heartbeat); return ret; } @@ -205,9 +204,7 @@ 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_param(nowayout, bool, 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 index 17ef300bccc..91e45ca589e 100644 --- a/drivers/watchdog/jz4740_wdt.c +++ b/drivers/watchdog/jz4740_wdt.c @@ -17,18 +17,13 @@ #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 <linux/err.h> #include <asm/mach-jz4740/timer.h> @@ -41,9 +36,6 @@ #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) @@ -56,32 +48,44 @@ #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 bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -static int heartbeat = DEFAULT_HEARTBEAT; +static unsigned int heartbeat = DEFAULT_HEARTBEAT; +module_param(heartbeat, uint, 0); +MODULE_PARM_DESC(heartbeat, + "Watchdog heartbeat period in seconds from 1 to " + __MODULE_STRING(MAX_HEARTBEAT) ", default " + __MODULE_STRING(DEFAULT_HEARTBEAT)); +struct jz4740_wdt_drvdata { + struct watchdog_device wdt; + void __iomem *base; + struct clk *rtc_clk; +}; -static void jz4740_wdt_service(void) +static int jz4740_wdt_ping(struct watchdog_device *wdt_dev) { - writew(0x0, jz4740_wdt.base + JZ_REG_WDT_TIMER_COUNTER); + struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); + + writew(0x0, drvdata->base + JZ_REG_WDT_TIMER_COUNTER); + return 0; } -static void jz4740_wdt_set_heartbeat(int new_heartbeat) +static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int new_timeout) { + struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); 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); + rtc_clk_rate = clk_get_rate(drvdata->rtc_clk); - timeout_value = rtc_clk_rate * heartbeat; + timeout_value = rtc_clk_rate * new_timeout; while (timeout_value > 0xffff) { if (clock_div == JZ_WDT_CLOCK_DIV_1024) { /* Requested timeout too high; @@ -93,202 +97,118 @@ static void jz4740_wdt_set_heartbeat(int new_heartbeat) 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); + writeb(0x0, drvdata->base + JZ_REG_WDT_COUNTER_ENABLE); + writew(clock_div, drvdata->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((u16)timeout_value, drvdata->base + JZ_REG_WDT_TIMER_DATA); + writew(0x0, drvdata->base + JZ_REG_WDT_TIMER_COUNTER); writew(clock_div | JZ_WDT_CLOCK_RTC, - jz4740_wdt.base + JZ_REG_WDT_TIMER_CONTROL); + drvdata->base + JZ_REG_WDT_TIMER_CONTROL); - writeb(0x1, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE); -} + writeb(0x1, drvdata->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); + wdt_dev->timeout = new_timeout; + return 0; } -static int jz4740_wdt_open(struct inode *inode, struct file *file) +static int jz4740_wdt_start(struct watchdog_device *wdt_dev) { - if (test_and_set_bit(WDT_IN_USE, &jz4740_wdt.status)) - return -EBUSY; - - jz4740_wdt_enable(); + jz4740_timer_enable_watchdog(); + jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout); - return nonseekable_open(inode, file); + return 0; } -static ssize_t jz4740_wdt_write(struct file *file, const char *data, - size_t len, loff_t *ppos) +static int jz4740_wdt_stop(struct watchdog_device *wdt_dev) { - if (len) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status); - for (i = 0; i != len; i++) { - char c; + struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); - if (get_user(c, data + i)) - return -EFAULT; - - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status); - } - jz4740_wdt_service(); - } + jz4740_timer_disable_watchdog(); + writeb(0x0, drvdata->base + JZ_REG_WDT_COUNTER_ENABLE); - return len; + return 0; } -static const struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING, +static const struct watchdog_info jz4740_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .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 = { +static const struct watchdog_ops jz4740_wdt_ops = { .owner = THIS_MODULE, - .llseek = no_llseek, - .write = jz4740_wdt_write, - .unlocked_ioctl = jz4740_wdt_ioctl, - .open = jz4740_wdt_open, - .release = jz4740_wdt_release, + .start = jz4740_wdt_start, + .stop = jz4740_wdt_stop, + .ping = jz4740_wdt_ping, + .set_timeout = jz4740_wdt_set_timeout, }; -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) +static int 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; + struct jz4740_wdt_drvdata *drvdata; + struct watchdog_device *jz4740_wdt; + struct resource *res; + int ret; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct jz4740_wdt_drvdata), + GFP_KERNEL); + if (!drvdata) { + dev_err(&pdev->dev, "Unable to alloacate watchdog device\n"); + return -ENOMEM; } - 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; - } + if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) + heartbeat = DEFAULT_HEARTBEAT; - 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 = &drvdata->wdt; + jz4740_wdt->info = &jz4740_wdt_info; + jz4740_wdt->ops = &jz4740_wdt_ops; + jz4740_wdt->timeout = heartbeat; + jz4740_wdt->min_timeout = 1; + jz4740_wdt->max_timeout = MAX_HEARTBEAT; + watchdog_set_nowayout(jz4740_wdt, nowayout); + watchdog_set_drvdata(jz4740_wdt, drvdata); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drvdata->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(drvdata->base)) { + ret = PTR_ERR(drvdata->base); + goto err_out; } - 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; + drvdata->rtc_clk = clk_get(&pdev->dev, "rtc"); + if (IS_ERR(drvdata->rtc_clk)) { + dev_err(&pdev->dev, "cannot find RTC clock\n"); + ret = PTR_ERR(drvdata->rtc_clk); + goto err_out; } - ret = misc_register(&jz4740_wdt_miscdev); - if (ret < 0) { - dev_err(dev, "cannot register misc device\n"); + ret = watchdog_register_device(&drvdata->wdt); + if (ret < 0) goto err_disable_clk; - } + platform_set_drvdata(pdev, drvdata); 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)); + clk_put(drvdata->rtc_clk); +err_out: return ret; } - -static int __devexit jz4740_wdt_remove(struct platform_device *pdev) +static int jz4740_wdt_remove(struct platform_device *pdev) { - jz4740_wdt_disable(); - misc_deregister(&jz4740_wdt_miscdev); - clk_put(jz4740_wdt.rtc_clk); + struct jz4740_wdt_drvdata *drvdata = platform_get_drvdata(pdev); - iounmap(jz4740_wdt.base); - jz4740_wdt.base = NULL; - - release_mem_region(jz4740_wdt.mem->start, - resource_size(jz4740_wdt.mem)); - jz4740_wdt.mem = NULL; + jz4740_wdt_stop(&drvdata->wdt); + watchdog_unregister_device(&drvdata->wdt); + clk_put(drvdata->rtc_clk); return 0; } - static struct platform_driver jz4740_wdt_driver = { .probe = jz4740_wdt_probe, - .remove = __devexit_p(jz4740_wdt_remove), + .remove = jz4740_wdt_remove, .driver = { .name = "jz4740-wdt", .owner = THIS_MODULE, @@ -299,13 +219,5 @@ module_platform_driver(jz4740_wdt_driver); 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/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c new file mode 100644 index 00000000000..d9c1a160192 --- /dev/null +++ b/drivers/watchdog/kempld_wdt.c @@ -0,0 +1,579 @@ +/* + * Kontron PLD watchdog driver + * + * Copyright (c) 2010-2013 Kontron Europe GmbH + * Author: Michael Brunner <michael.brunner@kontron.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 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. + * + * Note: From the PLD watchdog point of view timeout and pretimeout are + * defined differently than in the kernel. + * First the pretimeout stage runs out before the timeout stage gets + * active. + * + * Kernel/API: P-----| pretimeout + * |-----------------------T timeout + * Watchdog: |-----------------P pretimeout_stage + * |-----T timeout_stage + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/uaccess.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/mfd/kempld.h> + +#define KEMPLD_WDT_STAGE_TIMEOUT(x) (0x1b + (x) * 4) +#define KEMPLD_WDT_STAGE_CFG(x) (0x18 + (x)) +#define STAGE_CFG_GET_PRESCALER(x) (((x) & 0x30) >> 4) +#define STAGE_CFG_SET_PRESCALER(x) (((x) & 0x3) << 4) +#define STAGE_CFG_PRESCALER_MASK 0x30 +#define STAGE_CFG_ACTION_MASK 0x7 +#define STAGE_CFG_ASSERT (1 << 3) + +#define KEMPLD_WDT_MAX_STAGES 2 +#define KEMPLD_WDT_KICK 0x16 +#define KEMPLD_WDT_CFG 0x17 +#define KEMPLD_WDT_CFG_ENABLE 0x10 +#define KEMPLD_WDT_CFG_ENABLE_LOCK 0x8 +#define KEMPLD_WDT_CFG_GLOBAL_LOCK 0x80 + +enum { + ACTION_NONE = 0, + ACTION_RESET, + ACTION_NMI, + ACTION_SMI, + ACTION_SCI, + ACTION_DELAY, +}; + +enum { + STAGE_TIMEOUT = 0, + STAGE_PRETIMEOUT, +}; + +enum { + PRESCALER_21 = 0, + PRESCALER_17, + PRESCALER_12, +}; + +static const u32 kempld_prescaler[] = { + [PRESCALER_21] = (1 << 21) - 1, + [PRESCALER_17] = (1 << 17) - 1, + [PRESCALER_12] = (1 << 12) - 1, + 0, +}; + +struct kempld_wdt_stage { + unsigned int id; + u32 mask; +}; + +struct kempld_wdt_data { + struct kempld_device_data *pld; + struct watchdog_device wdd; + unsigned int pretimeout; + struct kempld_wdt_stage stage[KEMPLD_WDT_MAX_STAGES]; +#ifdef CONFIG_PM + u8 pm_status_store; +#endif +}; + +#define DEFAULT_TIMEOUT 30 /* seconds */ +#define DEFAULT_PRETIMEOUT 0 + +static unsigned int timeout = DEFAULT_TIMEOUT; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (>=0, default=" + __MODULE_STRING(DEFAULT_TIMEOUT) ")"); + +static unsigned int pretimeout = DEFAULT_PRETIMEOUT; +module_param(pretimeout, uint, 0); +MODULE_PARM_DESC(pretimeout, + "Watchdog pretimeout in seconds. (>=0, default=" + __MODULE_STRING(DEFAULT_PRETIMEOUT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int kempld_wdt_set_stage_action(struct kempld_wdt_data *wdt_data, + struct kempld_wdt_stage *stage, + u8 action) +{ + struct kempld_device_data *pld = wdt_data->pld; + u8 stage_cfg; + + if (!stage || !stage->mask) + return -EINVAL; + + kempld_get_mutex(pld); + stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); + stage_cfg &= ~STAGE_CFG_ACTION_MASK; + stage_cfg |= (action & STAGE_CFG_ACTION_MASK); + + if (action == ACTION_RESET) + stage_cfg |= STAGE_CFG_ASSERT; + else + stage_cfg &= ~STAGE_CFG_ASSERT; + + kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg); + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data, + struct kempld_wdt_stage *stage, + unsigned int timeout) +{ + struct kempld_device_data *pld = wdt_data->pld; + u32 prescaler = kempld_prescaler[PRESCALER_21]; + u64 stage_timeout64; + u32 stage_timeout; + u32 remainder; + u8 stage_cfg; + + if (!stage) + return -EINVAL; + + stage_timeout64 = (u64)timeout * pld->pld_clock; + remainder = do_div(stage_timeout64, prescaler); + if (remainder) + stage_timeout64++; + + if (stage_timeout64 > stage->mask) + return -EINVAL; + + stage_timeout = stage_timeout64 & stage->mask; + + kempld_get_mutex(pld); + stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); + stage_cfg &= ~STAGE_CFG_PRESCALER_MASK; + stage_cfg |= STAGE_CFG_SET_PRESCALER(PRESCALER_21); + kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg); + kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id), + stage_timeout); + kempld_release_mutex(pld); + + return 0; +} + +/* + * kempld_get_mutex must be called prior to calling this function. + */ +static unsigned int kempld_wdt_get_timeout(struct kempld_wdt_data *wdt_data, + struct kempld_wdt_stage *stage) +{ + struct kempld_device_data *pld = wdt_data->pld; + unsigned int timeout; + u64 stage_timeout; + u32 prescaler; + u32 remainder; + u8 stage_cfg; + + if (!stage->mask) + return 0; + + stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); + stage_timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id)); + prescaler = kempld_prescaler[STAGE_CFG_GET_PRESCALER(stage_cfg)]; + + stage_timeout = (stage_timeout & stage->mask) * prescaler; + remainder = do_div(stage_timeout, pld->pld_clock); + if (remainder) + stage_timeout++; + + timeout = stage_timeout; + WARN_ON_ONCE(timeout != stage_timeout); + + return timeout; +} + +static int kempld_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_wdt_stage *pretimeout_stage; + struct kempld_wdt_stage *timeout_stage; + int ret; + + timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; + pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; + + if (pretimeout_stage->mask && wdt_data->pretimeout > 0) + timeout = wdt_data->pretimeout; + + ret = kempld_wdt_set_stage_action(wdt_data, timeout_stage, + ACTION_RESET); + if (ret) + return ret; + ret = kempld_wdt_set_stage_timeout(wdt_data, timeout_stage, + timeout); + if (ret) + return ret; + + wdd->timeout = timeout; + return 0; +} + +static int kempld_wdt_set_pretimeout(struct watchdog_device *wdd, + unsigned int pretimeout) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_wdt_stage *pretimeout_stage; + u8 action = ACTION_NONE; + int ret; + + pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; + + if (!pretimeout_stage->mask) + return -ENXIO; + + if (pretimeout > wdd->timeout) + return -EINVAL; + + if (pretimeout > 0) + action = ACTION_NMI; + + ret = kempld_wdt_set_stage_action(wdt_data, pretimeout_stage, + action); + if (ret) + return ret; + ret = kempld_wdt_set_stage_timeout(wdt_data, pretimeout_stage, + wdd->timeout - pretimeout); + if (ret) + return ret; + + wdt_data->pretimeout = pretimeout; + return 0; +} + +static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data) +{ + struct kempld_device_data *pld = wdt_data->pld; + struct kempld_wdt_stage *pretimeout_stage; + struct kempld_wdt_stage *timeout_stage; + unsigned int pretimeout, timeout; + + pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; + timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; + + kempld_get_mutex(pld); + pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage); + timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage); + kempld_release_mutex(pld); + + if (pretimeout) + wdt_data->pretimeout = timeout; + else + wdt_data->pretimeout = 0; + + wdt_data->wdd.timeout = pretimeout + timeout; +} + +static int kempld_wdt_start(struct watchdog_device *wdd) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_device_data *pld = wdt_data->pld; + u8 status; + int ret; + + ret = kempld_wdt_set_timeout(wdd, wdd->timeout); + if (ret) + return ret; + + kempld_get_mutex(pld); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + status |= KEMPLD_WDT_CFG_ENABLE; + kempld_write8(pld, KEMPLD_WDT_CFG, status); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + kempld_release_mutex(pld); + + /* Check if the watchdog was enabled */ + if (!(status & KEMPLD_WDT_CFG_ENABLE)) + return -EACCES; + + return 0; +} + +static int kempld_wdt_stop(struct watchdog_device *wdd) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_device_data *pld = wdt_data->pld; + u8 status; + + kempld_get_mutex(pld); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + status &= ~KEMPLD_WDT_CFG_ENABLE; + kempld_write8(pld, KEMPLD_WDT_CFG, status); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + kempld_release_mutex(pld); + + /* Check if the watchdog was disabled */ + if (status & KEMPLD_WDT_CFG_ENABLE) + return -EACCES; + + return 0; +} + +static int kempld_wdt_keepalive(struct watchdog_device *wdd) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_device_data *pld = wdt_data->pld; + + kempld_get_mutex(pld); + kempld_write8(pld, KEMPLD_WDT_KICK, 'K'); + kempld_release_mutex(pld); + + return 0; +} + +static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd, + unsigned long arg) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + void __user *argp = (void __user *)arg; + int ret = -ENOIOCTLCMD; + int __user *p = argp; + int new_value; + + switch (cmd) { + case WDIOC_SETPRETIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + ret = kempld_wdt_set_pretimeout(wdd, new_value); + if (ret) + return ret; + ret = kempld_wdt_keepalive(wdd); + break; + case WDIOC_GETPRETIMEOUT: + ret = put_user(wdt_data->pretimeout, (int __user *)arg); + break; + } + + return ret; +} + +static int kempld_wdt_probe_stages(struct watchdog_device *wdd) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_device_data *pld = wdt_data->pld; + struct kempld_wdt_stage *pretimeout_stage; + struct kempld_wdt_stage *timeout_stage; + u8 index, data, data_orig; + u32 mask; + int i, j; + + pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; + timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; + + pretimeout_stage->mask = 0; + timeout_stage->mask = 0; + + for (i = 0; i < 3; i++) { + index = KEMPLD_WDT_STAGE_TIMEOUT(i); + mask = 0; + + kempld_get_mutex(pld); + /* Probe each byte individually. */ + for (j = 0; j < 4; j++) { + data_orig = kempld_read8(pld, index + j); + kempld_write8(pld, index + j, 0x00); + data = kempld_read8(pld, index + j); + /* A failed write means this byte is reserved */ + if (data != 0x00) + break; + kempld_write8(pld, index + j, data_orig); + mask |= 0xff << (j * 8); + } + kempld_release_mutex(pld); + + /* Assign available stages to timeout and pretimeout */ + if (!timeout_stage->mask) { + timeout_stage->mask = mask; + timeout_stage->id = i; + } else { + if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) { + pretimeout_stage->mask = timeout_stage->mask; + timeout_stage->mask = mask; + pretimeout_stage->id = timeout_stage->id; + timeout_stage->id = i; + } + break; + } + } + + if (!timeout_stage->mask) + return -ENODEV; + + return 0; +} + +static struct watchdog_info kempld_wdt_info = { + .identity = "KEMPLD Watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE | + WDIOF_PRETIMEOUT +}; + +static struct watchdog_ops kempld_wdt_ops = { + .owner = THIS_MODULE, + .start = kempld_wdt_start, + .stop = kempld_wdt_stop, + .ping = kempld_wdt_keepalive, + .set_timeout = kempld_wdt_set_timeout, + .ioctl = kempld_wdt_ioctl, +}; + +static int kempld_wdt_probe(struct platform_device *pdev) +{ + struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent); + struct kempld_wdt_data *wdt_data; + struct device *dev = &pdev->dev; + struct watchdog_device *wdd; + u8 status; + int ret = 0; + + wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL); + if (!wdt_data) + return -ENOMEM; + + wdt_data->pld = pld; + wdd = &wdt_data->wdd; + wdd->parent = dev; + + kempld_get_mutex(pld); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + kempld_release_mutex(pld); + + /* Enable nowayout if watchdog is already locked */ + if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK | + KEMPLD_WDT_CFG_GLOBAL_LOCK)) { + if (!nowayout) + dev_warn(dev, + "Forcing nowayout - watchdog lock enabled!\n"); + nowayout = true; + } + + wdd->info = &kempld_wdt_info; + wdd->ops = &kempld_wdt_ops; + + watchdog_set_drvdata(wdd, wdt_data); + watchdog_set_nowayout(wdd, nowayout); + + ret = kempld_wdt_probe_stages(wdd); + if (ret) + return ret; + + kempld_wdt_set_timeout(wdd, timeout); + kempld_wdt_set_pretimeout(wdd, pretimeout); + + /* Check if watchdog is already enabled */ + if (status & KEMPLD_WDT_CFG_ENABLE) { + /* Get current watchdog settings */ + kempld_wdt_update_timeouts(wdt_data); + dev_info(dev, "Watchdog was already enabled\n"); + } + + platform_set_drvdata(pdev, wdt_data); + ret = watchdog_register_device(wdd); + if (ret) + return ret; + + dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout); + + return 0; +} + +static void kempld_wdt_shutdown(struct platform_device *pdev) +{ + struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); + + kempld_wdt_stop(&wdt_data->wdd); +} + +static int kempld_wdt_remove(struct platform_device *pdev) +{ + struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); + struct watchdog_device *wdd = &wdt_data->wdd; + int ret = 0; + + if (!nowayout) + ret = kempld_wdt_stop(wdd); + watchdog_unregister_device(wdd); + + return ret; +} + +#ifdef CONFIG_PM +/* Disable watchdog if it is active during suspend */ +static int kempld_wdt_suspend(struct platform_device *pdev, + pm_message_t message) +{ + struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); + struct kempld_device_data *pld = wdt_data->pld; + struct watchdog_device *wdd = &wdt_data->wdd; + + kempld_get_mutex(pld); + wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG); + kempld_release_mutex(pld); + + kempld_wdt_update_timeouts(wdt_data); + + if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE) + return kempld_wdt_stop(wdd); + + return 0; +} + +/* Enable watchdog and configure it if necessary */ +static int kempld_wdt_resume(struct platform_device *pdev) +{ + struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); + struct watchdog_device *wdd = &wdt_data->wdd; + + /* + * If watchdog was stopped before suspend be sure it gets disabled + * again, for the case BIOS has enabled it during resume + */ + if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE) + return kempld_wdt_start(wdd); + else + return kempld_wdt_stop(wdd); +} +#else +#define kempld_wdt_suspend NULL +#define kempld_wdt_resume NULL +#endif + +static struct platform_driver kempld_wdt_driver = { + .driver = { + .name = "kempld-wdt", + .owner = THIS_MODULE, + }, + .probe = kempld_wdt_probe, + .remove = kempld_wdt_remove, + .shutdown = kempld_wdt_shutdown, + .suspend = kempld_wdt_suspend, + .resume = kempld_wdt_resume, +}; + +module_platform_driver(kempld_wdt_driver); + +MODULE_DESCRIPTION("KEM PLD Watchdog Driver"); +MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c index 51757a520e8..40ca5594a33 100644 --- a/drivers/watchdog/ks8695_wdt.c +++ b/drivers/watchdog/ks8695_wdt.c @@ -8,6 +8,8 @@ * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/bitops.h> #include <linux/errno.h> #include <linux/fs.h> @@ -22,20 +24,32 @@ #include <linux/io.h> #include <linux/uaccess.h> #include <mach/hardware.h> -#include <mach/regs-timer.h> + +#define KS8695_TMR_OFFSET (0xF0000 + 0xE400) +#define KS8695_TMR_VA (KS8695_IO_VA + KS8695_TMR_OFFSET) + +/* + * Timer registers + */ +#define KS8695_TMCON (0x00) /* Timer Control Register */ +#define KS8695_T0TC (0x08) /* Timer 0 Timeout Count Register */ +#define TMCON_T0EN (1 << 0) /* Timer 0 Enable */ + +/* Timer0 Timeout Counter Register */ +#define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */ #define WDT_DEFAULT_TIME 5 /* seconds */ #define WDT_MAX_TIME 171 /* seconds */ static int wdt_time = WDT_DEFAULT_TIME; -static int nowayout = WATCHDOG_NOWAYOUT; +static bool 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_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); #endif @@ -221,7 +235,7 @@ static struct miscdevice ks8695wdt_miscdev = { .fops = &ks8695wdt_fops, }; -static int __devinit ks8695wdt_probe(struct platform_device *pdev) +static int ks8695wdt_probe(struct platform_device *pdev) { int res; @@ -233,12 +247,12 @@ static int __devinit ks8695wdt_probe(struct platform_device *pdev) if (res) return res; - printk(KERN_INFO "KS8695 Watchdog Timer enabled (%d seconds%s)\n", - wdt_time, nowayout ? ", nowayout" : ""); + pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n", + wdt_time, nowayout ? ", nowayout" : ""); return 0; } -static int __devexit ks8695wdt_remove(struct platform_device *pdev) +static int ks8695wdt_remove(struct platform_device *pdev) { int res; @@ -276,7 +290,7 @@ static int ks8695wdt_resume(struct platform_device *pdev) static struct platform_driver ks8695wdt_driver = { .probe = ks8695wdt_probe, - .remove = __devexit_p(ks8695wdt_remove), + .remove = ks8695wdt_remove, .shutdown = ks8695wdt_shutdown, .suspend = ks8695wdt_suspend, .resume = ks8695wdt_resume, @@ -309,5 +323,4 @@ 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 index d3a63be2e28..3b3148c764a 100644 --- a/drivers/watchdog/lantiq_wdt.c +++ b/drivers/watchdog/lantiq_wdt.c @@ -7,18 +7,21 @@ * Based on EP93xx wdt driver */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> -#include <linux/platform_device.h> +#include <linux/of_platform.h> #include <linux/uaccess.h> #include <linux/clk.h> #include <linux/io.h> -#include <lantiq.h> +#include <lantiq_soc.h> -/* Section 3.4 of the datasheet +/* + * 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. * @@ -38,7 +41,7 @@ #define LTQ_WDT_DIVIDER 0x40000 #define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */ -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; static void __iomem *ltq_wdt_membase; static unsigned long ltq_io_region_clk_rate; @@ -68,7 +71,8 @@ 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 + /* + * write the second password magic with no config * this turns the watchdog off */ ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR); @@ -160,7 +164,7 @@ 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"); + pr_err("watchdog closed without warning\n"); ltq_wdt_ok_to_close = 0; clear_bit(0, <q_wdt_in_use); @@ -182,7 +186,7 @@ static struct miscdevice ltq_wdt_miscdev = { .fops = <q_wdt_fops, }; -static int __init +static int ltq_wdt_probe(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -192,32 +196,29 @@ ltq_wdt_probe(struct platform_device *pdev) 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; - } + + ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ltq_wdt_membase)) + return PTR_ERR(ltq_wdt_membase); /* we do not need to enable the clock as it is always running */ - clk = clk_get(&pdev->dev, "io"); - WARN_ON(!clk); + clk = clk_get_io(); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Failed to get clock\n"); + return -ENOENT; + } ltq_io_region_clk_rate = clk_get_rate(clk); clk_put(clk); + /* find out if the watchdog caused the last reboot */ if (ltq_reset_cause() == LTQ_RST_CAUSE_WDTRST) ltq_wdt_bootstatus = WDIOF_CARDRESET; + dev_info(&pdev->dev, "Init done\n"); return misc_register(<q_wdt_miscdev); } -static int __devexit +static int ltq_wdt_remove(struct platform_device *pdev) { misc_deregister(<q_wdt_miscdev); @@ -225,34 +226,26 @@ ltq_wdt_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ltq_wdt_match[] = { + { .compatible = "lantiq,wdt" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ltq_wdt_match); static struct platform_driver ltq_wdt_driver = { - .remove = __devexit_p(ltq_wdt_remove), + .probe = ltq_wdt_probe, + .remove = ltq_wdt_remove, .driver = { - .name = "ltq_wdt", + .name = "wdt", .owner = THIS_MODULE, + .of_match_table = ltq_wdt_match, }, }; -static int __init -init_ltq_wdt(void) -{ - return platform_driver_probe(<q_wdt_driver, ltq_wdt_probe); -} +module_platform_driver(ltq_wdt_driver); -static void __exit -exit_ltq_wdt(void) -{ - return platform_driver_unregister(<q_wdt_driver); -} - -module_init(init_ltq_wdt); -module_exit(exit_ltq_wdt); - -module_param(nowayout, int, 0); +module_param(nowayout, bool, 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 index 4d43286074a..da6fa2b6807 100644 --- a/drivers/watchdog/m54xx_wdt.c +++ b/drivers/watchdog/m54xx_wdt.c @@ -16,6 +16,8 @@ * warranty of any kind, whether express or implied. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -32,7 +34,7 @@ #include <asm/m54xxsim.h> #include <asm/m54xxgpt.h> -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; static unsigned int heartbeat = 30; /* (secs) Default is 0.5 minute */ static unsigned long wdt_status; @@ -44,17 +46,17 @@ static void wdt_enable(void) unsigned int gms0; /* preserve GPIO usage, if any */ - gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0); + gms0 = __raw_readl(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(gms0, MCF_GPT_GMS0); __raw_writel(MCF_GPT_GCIR_PRE(heartbeat*(MCF_BUSCLK/0xffff)) | - MCF_GPT_GCIR_CNT(0xffff), MCF_MBAR + MCF_GPT_GCIR0); + MCF_GPT_GCIR_CNT(0xffff), 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); + __raw_writel(gms0, MCF_GPT_GMS0); } static void wdt_disable(void) @@ -62,18 +64,18 @@ static void wdt_disable(void) unsigned int gms0; /* disable watchdog */ - gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0); + gms0 = __raw_readl(MCF_GPT_GMS0); gms0 &= ~(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE); - __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); + __raw_writel(gms0, MCF_GPT_GMS0); } static void wdt_keepalive(void) { unsigned int gms0; - gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0); + gms0 = __raw_readl(MCF_GPT_GMS0); gms0 |= MCF_GPT_GMS_OCPW(0xA5); - __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); + __raw_writel(gms0, MCF_GPT_GMS0); } static int m54xx_wdt_open(struct inode *inode, struct file *file) @@ -166,8 +168,7 @@ 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"); + pr_crit("Device closed unexpectedly - timer will not stop\n"); wdt_keepalive(); } clear_bit(WDT_IN_USE, &wdt_status); @@ -194,13 +195,11 @@ static struct miscdevice m54xx_wdt_miscdev = { 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"); + if (!request_mem_region(MCF_GPT_GCIR0, 4, "Coldfire M54xx Watchdog")) { + pr_warn("I/O region busy\n"); return -EBUSY; } - printk(KERN_INFO "ColdFire watchdog driver is loaded.\n"); + pr_info("driver is loaded\n"); return misc_register(&m54xx_wdt_miscdev); } @@ -208,7 +207,7 @@ static int __init m54xx_wdt_init(void) static void __exit m54xx_wdt_exit(void) { misc_deregister(&m54xx_wdt_miscdev); - release_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4); + release_mem_region(MCF_GPT_GCIR0, 4); } module_init(m54xx_wdt_init); @@ -220,8 +219,7 @@ 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_param(nowayout, bool, 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 index 1332b838cc5..9826b59ef73 100644 --- a/drivers/watchdog/machzwd.c +++ b/drivers/watchdog/machzwd.c @@ -28,6 +28,8 @@ * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -43,7 +45,6 @@ #include <linux/io.h> #include <linux/uaccess.h> -#include <asm/system.h> /* ports */ #define ZF_IOBASE 0x218 @@ -91,10 +92,9 @@ static unsigned short zf_readw(unsigned char port) 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -141,10 +141,10 @@ static unsigned long next_heartbeat; #define ZF_CTIMEOUT 0xffff #ifndef ZF_DEBUG -# define dprintk(format, args...) +#define dprintk(format, args...) #else -# define dprintk(format, args...) printk(KERN_DEBUG PFX \ - ":%s:%d: " format, __func__, __LINE__ , ## args) +#define dprintk(format, args...) \ + pr_debug(":%s:%d: " format, __func__, __LINE__ , ## args) #endif @@ -203,7 +203,7 @@ static void zf_timer_off(void) zf_set_control(ctrl_reg); spin_unlock_irqrestore(&zf_port_lock, flags); - printk(KERN_INFO PFX ": Watchdog timer is now disabled\n"); + pr_info("Watchdog timer is now disabled\n"); } @@ -233,7 +233,7 @@ static void zf_timer_on(void) zf_set_control(ctrl_reg); spin_unlock_irqrestore(&zf_port_lock, flags); - printk(KERN_INFO PFX ": Watchdog timer is now enabled\n"); + pr_info("Watchdog timer is now enabled\n"); } @@ -263,7 +263,7 @@ static void zf_ping(unsigned long data) mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO); } else - printk(KERN_CRIT PFX ": I will reset your machine\n"); + pr_crit("I will reset your machine\n"); } static ssize_t zf_write(struct file *file, const char __user *buf, size_t count, @@ -342,8 +342,7 @@ static int zf_close(struct inode *inode, struct file *file) zf_timer_off(); else { del_timer(&zf_timer); - printk(KERN_ERR PFX ": device file closed unexpectedly. " - "Will not stop the WDT!\n"); + pr_err("device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &zf_is_open); zf_expect_close = 0; @@ -390,19 +389,18 @@ 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]); + pr_info("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"); + pr_info("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"); + pr_warn("no ZF-Logic found\n"); return -ENODEV; } @@ -414,23 +412,20 @@ static int __init zf_init(void) 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); + pr_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); + pr_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); + pr_err("can't misc_register on minor=%d\n", WATCHDOG_MINOR); goto no_misc; } diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index af63ecfbfa6..0e9cc6f5a91 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c @@ -14,27 +14,23 @@ * another interface, some abstraction will have to be introduced. */ +#include <linux/err.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/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; +static unsigned int heartbeat = DEFAULT_HEARTBEAT; +static bool nowayout = WATCHDOG_NOWAYOUT; /* * Memory mapping: a single byte, 3 first lower bits to select bit 3 @@ -45,15 +41,8 @@ static int nowayout = WATCHDOG_NOWAYOUT; 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 @@ -117,7 +106,7 @@ max63xx_select_timeout(struct max63xx_timeout *table, int value) return NULL; } -static void max63xx_wdt_ping(void) +static int max63xx_wdt_ping(struct watchdog_device *wdd) { u8 val; @@ -129,15 +118,14 @@ static void max63xx_wdt_ping(void) __raw_writeb(val & ~MAX6369_WDI, wdt_base); spin_unlock(&io_lock); + return 0; } -static void max63xx_wdt_enable(struct max63xx_timeout *entry) +static int max63xx_wdt_start(struct watchdog_device *wdd) { + struct max63xx_timeout *entry = watchdog_get_drvdata(wdd); u8 val; - if (test_and_set_bit(WDT_RUNNING, &wdt_status)) - return; - spin_lock(&io_lock); val = __raw_readb(wdt_base); @@ -149,10 +137,11 @@ static void max63xx_wdt_enable(struct max63xx_timeout *entry) /* check for a edge triggered startup */ if (entry->tdelay == 0) - max63xx_wdt_ping(); + max63xx_wdt_ping(wdd); + return 0; } -static void max63xx_wdt_disable(void) +static int max63xx_wdt_stop(struct watchdog_device *wdd) { u8 val; @@ -164,113 +153,29 @@ static void max63xx_wdt_disable(void) __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; + return 0; } -static const struct watchdog_info ident = { - .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, +static const struct watchdog_info max63xx_wdt_info = { + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .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 const struct watchdog_ops max63xx_wdt_ops = { + .owner = THIS_MODULE, + .start = max63xx_wdt_start, + .stop = max63xx_wdt_stop, + .ping = max63xx_wdt_ping, }; -static struct miscdevice max63xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &max63xx_wdt_fops, +static struct watchdog_device max63xx_wdt_dev = { + .info = &max63xx_wdt_info, + .ops = &max63xx_wdt_ops, }; -static int __devinit max63xx_wdt_probe(struct platform_device *pdev) +static int max63xx_wdt_probe(struct platform_device *pdev) { - int ret = 0; - int size; - struct device *dev = &pdev->dev; + struct resource *wdt_mem; struct max63xx_timeout *table; table = (struct max63xx_timeout *)pdev->id_entry->driver_data; @@ -278,68 +183,34 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) heartbeat = DEFAULT_HEARTBEAT; - dev_info(dev, "requesting %ds heartbeat\n", heartbeat); + dev_info(&pdev->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"); + dev_err(&pdev->dev, "unable to satisfy heartbeat request\n"); return -EINVAL; } - dev_info(dev, "using %ds heartbeat with %ds initial delay\n", + dev_info(&pdev->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; - } + wdt_base = devm_ioremap_resource(&pdev->dev, wdt_mem); + if (IS_ERR(wdt_base)) + return PTR_ERR(wdt_base); - 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; - } + max63xx_wdt_dev.timeout = heartbeat; + watchdog_set_nowayout(&max63xx_wdt_dev, nowayout); + watchdog_set_drvdata(&max63xx_wdt_dev, current_timeout); - 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; + return watchdog_register_device(&max63xx_wdt_dev); } -static int __devexit max63xx_wdt_remove(struct platform_device *pdev) +static int 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); - + watchdog_unregister_device(&max63xx_wdt_dev); return 0; } @@ -356,7 +227,7 @@ MODULE_DEVICE_TABLE(platform, max63xx_id_table); static struct platform_driver max63xx_wdt_driver = { .probe = max63xx_wdt_probe, - .remove = __devexit_p(max63xx_wdt_remove), + .remove = max63xx_wdt_remove, .id_table = max63xx_id_table, .driver = { .name = "max63xx_wdt", @@ -375,7 +246,7 @@ MODULE_PARM_DESC(heartbeat, __MODULE_STRING(MAX_HEARTBEAT) ", default " __MODULE_STRING(DEFAULT_HEARTBEAT)); -module_param(nowayout, int, 0); +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -385,4 +256,3 @@ MODULE_PARM_DESC(nodelay, "(max6373/74 only, default=0)"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c new file mode 100644 index 00000000000..96dbba98057 --- /dev/null +++ b/drivers/watchdog/mena21_wdt.c @@ -0,0 +1,270 @@ +/* + * Watchdog driver for the A21 VME CPU Boards + * + * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH + * + * 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/slab.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> +#include <linux/uaccess.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/delay.h> +#include <linux/bitops.h> + +#define NUM_GPIOS 6 + +enum a21_wdt_gpios { + GPIO_WD_ENAB, + GPIO_WD_FAST, + GPIO_WD_TRIG, + GPIO_WD_RST0, + GPIO_WD_RST1, + GPIO_WD_RST2, +}; + +struct a21_wdt_drv { + struct watchdog_device wdt; + struct mutex lock; + unsigned gpios[NUM_GPIOS]; +}; + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv) +{ + int reset = 0; + + reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0; + reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0; + reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0; + + return reset; +} + +static int a21_wdt_start(struct watchdog_device *wdt) +{ + struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt); + + mutex_lock(&drv->lock); + + gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1); + + mutex_unlock(&drv->lock); + + return 0; +} + +static int a21_wdt_stop(struct watchdog_device *wdt) +{ + struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt); + + mutex_lock(&drv->lock); + + gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0); + + mutex_unlock(&drv->lock); + + return 0; +} + +static int a21_wdt_ping(struct watchdog_device *wdt) +{ + struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt); + + mutex_lock(&drv->lock); + + gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0); + ndelay(10); + gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1); + + mutex_unlock(&drv->lock); + + return 0; +} + +static int a21_wdt_set_timeout(struct watchdog_device *wdt, + unsigned int timeout) +{ + struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt); + + if (timeout != 1 && timeout != 30) { + dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n"); + return -EINVAL; + } + + if (timeout == 30 && wdt->timeout == 1) { + dev_err(wdt->dev, + "Transition from fast to slow mode not allowed\n"); + return -EINVAL; + } + + mutex_lock(&drv->lock); + + if (timeout == 1) + gpio_set_value(drv->gpios[GPIO_WD_FAST], 1); + else + gpio_set_value(drv->gpios[GPIO_WD_FAST], 0); + + wdt->timeout = timeout; + + mutex_unlock(&drv->lock); + + return 0; +} + +static const struct watchdog_info a21_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "MEN A21 Watchdog", +}; + +static const struct watchdog_ops a21_wdt_ops = { + .owner = THIS_MODULE, + .start = a21_wdt_start, + .stop = a21_wdt_stop, + .ping = a21_wdt_ping, + .set_timeout = a21_wdt_set_timeout, +}; + +static struct watchdog_device a21_wdt = { + .info = &a21_wdt_info, + .ops = &a21_wdt_ops, + .min_timeout = 1, + .max_timeout = 30, +}; + +static int a21_wdt_probe(struct platform_device *pdev) +{ + struct device_node *node; + struct a21_wdt_drv *drv; + unsigned int reset = 0; + int num_gpios; + int ret; + int i; + + drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + /* Fill GPIO pin array */ + node = pdev->dev.of_node; + + num_gpios = of_gpio_count(node); + if (num_gpios != NUM_GPIOS) { + dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d", + num_gpios, NUM_GPIOS); + return -ENODEV; + } + + for (i = 0; i < num_gpios; i++) { + int val; + + val = of_get_gpio(node, i); + if (val < 0) + return val; + + drv->gpios[i] = val; + } + + /* Request the used GPIOs */ + for (i = 0; i < num_gpios; i++) { + ret = devm_gpio_request(&pdev->dev, drv->gpios[i], + "MEN A21 Watchdog"); + if (ret) + return ret; + + if (i < GPIO_WD_RST0) + ret = gpio_direction_output(drv->gpios[i], + gpio_get_value(drv->gpios[i])); + else /* GPIO_WD_RST[0..2] are inputs */ + ret = gpio_direction_input(drv->gpios[i]); + if (ret) + return ret; + } + + mutex_init(&drv->lock); + watchdog_init_timeout(&a21_wdt, 30, &pdev->dev); + watchdog_set_nowayout(&a21_wdt, nowayout); + watchdog_set_drvdata(&a21_wdt, drv); + + reset = a21_wdt_get_bootstatus(drv); + if (reset == 2) + a21_wdt.bootstatus |= WDIOF_EXTERN1; + else if (reset == 4) + a21_wdt.bootstatus |= WDIOF_CARDRESET; + else if (reset == 5) + a21_wdt.bootstatus |= WDIOF_POWERUNDER; + else if (reset == 7) + a21_wdt.bootstatus |= WDIOF_EXTERN2; + + ret = watchdog_register_device(&a21_wdt); + if (ret) { + dev_err(&pdev->dev, "Cannot register watchdog device\n"); + goto err_register_wd; + } + + dev_set_drvdata(&pdev->dev, drv); + + dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n"); + + return 0; + +err_register_wd: + mutex_destroy(&drv->lock); + + return ret; +} + +static int a21_wdt_remove(struct platform_device *pdev) +{ + struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev); + + dev_warn(&pdev->dev, + "Unregistering A21 watchdog driver, board may reboot\n"); + + watchdog_unregister_device(&drv->wdt); + + mutex_destroy(&drv->lock); + + return 0; +} + +static void a21_wdt_shutdown(struct platform_device *pdev) +{ + struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev); + + gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0); +} + +static const struct of_device_id a21_wdt_ids[] = { + { .compatible = "men,a021-wdt" }, + { }, +}; + +static struct platform_driver a21_wdt_driver = { + .probe = a21_wdt_probe, + .remove = a21_wdt_remove, + .shutdown = a21_wdt_shutdown, + .driver = { + .name = "a21-watchdog", + .of_match_table = a21_wdt_ids, + }, +}; + +module_platform_driver(a21_wdt_driver); + +MODULE_AUTHOR("MEN Mikro Elektronik"); +MODULE_DESCRIPTION("MEN A21 Watchdog"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:a21-watchdog"); diff --git a/drivers/watchdog/mixcomwd.c b/drivers/watchdog/mixcomwd.c index bc820d16699..be86ea359ee 100644 --- a/drivers/watchdog/mixcomwd.c +++ b/drivers/watchdog/mixcomwd.c @@ -39,9 +39,10 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define VERSION "0.6" #define WATCHDOG_NAME "mixcomwd" -#define PFX WATCHDOG_NAME ": " #include <linux/module.h> #include <linux/moduleparam.h> @@ -72,7 +73,7 @@ static struct { int ioport; int id; -} mixcomwd_io_info[] __devinitdata = { +} mixcomwd_io_info[] = { /* The Mixcom cards */ {0x0d90, MIXCOM_ID}, {0x0e90, MIXCOM_ID}, @@ -107,8 +108,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -156,15 +157,13 @@ 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"); + pr_err("release called while internal timer alive\n"); 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"); + pr_crit("WDT device closed unexpectedly. WDT will not stop!\n"); clear_bit(0, &mixcomwd_opened); expect_close = 0; @@ -274,22 +273,19 @@ static int __init mixcomwd_init(void) } if (!found) { - printk(KERN_ERR PFX - "No card detected, or port not available.\n"); + pr_err("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); + pr_err("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); + pr_info("MixCOM watchdog driver v%s, watchdog port at 0x%3x\n", + VERSION, watchdog_port); return 0; @@ -303,8 +299,7 @@ static void __exit mixcomwd_exit(void) { if (!nowayout) { if (mixcomwd_timer_alive) { - printk(KERN_WARNING PFX "I quit now, hardware will" - " probably reboot!\n"); + pr_warn("I quit now, hardware will probably reboot!\n"); del_timer_sync(&mixcomwd_timer); mixcomwd_timer_alive = 0; } @@ -320,4 +315,3 @@ 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/moxart_wdt.c b/drivers/watchdog/moxart_wdt.c new file mode 100644 index 00000000000..4aa3a8a876f --- /dev/null +++ b/drivers/watchdog/moxart_wdt.c @@ -0,0 +1,180 @@ +/* + * MOXA ART SoCs watchdog driver. + * + * Copyright (C) 2013 Jonas Jensen + * + * Jonas Jensen <jonas.jensen@gmail.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/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> +#include <linux/moduleparam.h> + +#include <asm/system_misc.h> + +#define REG_COUNT 0x4 +#define REG_MODE 0x8 +#define REG_ENABLE 0xC + +struct moxart_wdt_dev { + struct watchdog_device dev; + void __iomem *base; + unsigned int clock_frequency; +}; + +static struct moxart_wdt_dev *moxart_restart_ctx; + +static int heartbeat; + +static void moxart_wdt_restart(enum reboot_mode reboot_mode, const char *cmd) +{ + writel(1, moxart_restart_ctx->base + REG_COUNT); + writel(0x5ab9, moxart_restart_ctx->base + REG_MODE); + writel(0x03, moxart_restart_ctx->base + REG_ENABLE); +} + +static int moxart_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev); + + writel(0, moxart_wdt->base + REG_ENABLE); + + return 0; +} + +static int moxart_wdt_start(struct watchdog_device *wdt_dev) +{ + struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev); + + writel(moxart_wdt->clock_frequency * wdt_dev->timeout, + moxart_wdt->base + REG_COUNT); + writel(0x5ab9, moxart_wdt->base + REG_MODE); + writel(0x03, moxart_wdt->base + REG_ENABLE); + + return 0; +} + +static int moxart_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + wdt_dev->timeout = timeout; + + return 0; +} + +static const struct watchdog_info moxart_wdt_info = { + .identity = "moxart-wdt", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops moxart_wdt_ops = { + .owner = THIS_MODULE, + .start = moxart_wdt_start, + .stop = moxart_wdt_stop, + .set_timeout = moxart_wdt_set_timeout, +}; + +static int moxart_wdt_probe(struct platform_device *pdev) +{ + struct moxart_wdt_dev *moxart_wdt; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct resource *res; + struct clk *clk; + int err; + unsigned int max_timeout; + bool nowayout = WATCHDOG_NOWAYOUT; + + moxart_wdt = devm_kzalloc(dev, sizeof(*moxart_wdt), GFP_KERNEL); + if (!moxart_wdt) + return -ENOMEM; + + platform_set_drvdata(pdev, moxart_wdt); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + moxart_wdt->base = devm_ioremap_resource(dev, res); + if (IS_ERR(moxart_wdt->base)) + return PTR_ERR(moxart_wdt->base); + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) { + pr_err("%s: of_clk_get failed\n", __func__); + return PTR_ERR(clk); + } + + moxart_wdt->clock_frequency = clk_get_rate(clk); + if (moxart_wdt->clock_frequency == 0) { + pr_err("%s: incorrect clock frequency\n", __func__); + return -EINVAL; + } + + max_timeout = UINT_MAX / moxart_wdt->clock_frequency; + + moxart_wdt->dev.info = &moxart_wdt_info; + moxart_wdt->dev.ops = &moxart_wdt_ops; + moxart_wdt->dev.timeout = max_timeout; + moxart_wdt->dev.min_timeout = 1; + moxart_wdt->dev.max_timeout = max_timeout; + moxart_wdt->dev.parent = dev; + + watchdog_init_timeout(&moxart_wdt->dev, heartbeat, dev); + watchdog_set_nowayout(&moxart_wdt->dev, nowayout); + + watchdog_set_drvdata(&moxart_wdt->dev, moxart_wdt); + + err = watchdog_register_device(&moxart_wdt->dev); + if (err) + return err; + + moxart_restart_ctx = moxart_wdt; + arm_pm_restart = moxart_wdt_restart; + + dev_dbg(dev, "Watchdog enabled (heartbeat=%d sec, nowayout=%d)\n", + moxart_wdt->dev.timeout, nowayout); + + return 0; +} + +static int moxart_wdt_remove(struct platform_device *pdev) +{ + struct moxart_wdt_dev *moxart_wdt = platform_get_drvdata(pdev); + + arm_pm_restart = NULL; + moxart_wdt_stop(&moxart_wdt->dev); + watchdog_unregister_device(&moxart_wdt->dev); + + return 0; +} + +static const struct of_device_id moxart_watchdog_match[] = { + { .compatible = "moxa,moxart-watchdog" }, + { }, +}; + +static struct platform_driver moxart_wdt_driver = { + .probe = moxart_wdt_probe, + .remove = moxart_wdt_remove, + .driver = { + .name = "moxart-watchdog", + .owner = THIS_MODULE, + .of_match_table = moxart_watchdog_match, + }, +}; +module_platform_driver(moxart_wdt_driver); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds"); + +MODULE_DESCRIPTION("MOXART watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>"); diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index 20feb4d3d79..7831955cd9e 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -17,11 +17,14 @@ * option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/timer.h> #include <linux/miscdevice.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/module.h> #include <linux/watchdog.h> @@ -60,8 +63,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -70,9 +73,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " * 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) @@ -84,39 +85,23 @@ static void mpc8xxx_wdt_keepalive(void) spin_unlock(&wdt_spinlock); } +static struct watchdog_device mpc8xxx_wdt_dev; static void mpc8xxx_wdt_timer_ping(unsigned long arg); -static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0, 0); +static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0, + (unsigned long)&mpc8xxx_wdt_dev); static void mpc8xxx_wdt_timer_ping(unsigned long arg) { + struct watchdog_device *w = (struct watchdog_device *)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"); + mod_timer(&wdt_timer, jiffies + HZ * w->timeout / 2); } -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) +static int mpc8xxx_wdt_start(struct watchdog_device *w) { 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) @@ -130,70 +115,49 @@ static int mpc8xxx_wdt_open(struct inode *inode, struct file *file) del_timer_sync(&wdt_timer); - return nonseekable_open(inode, file); + return 0; } -static int mpc8xxx_wdt_release(struct inode *inode, struct file *file) +static int mpc8xxx_wdt_ping(struct watchdog_device *w) { - if (!nowayout) - mpc8xxx_wdt_timer_ping(0); - else - mpc8xxx_wdt_pr_warn("watchdog closed"); - clear_bit(0, &wdt_is_open); + mpc8xxx_wdt_keepalive(); return 0; } -static long mpc8xxx_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static int mpc8xxx_wdt_stop(struct watchdog_device *w) { - 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; - } + mod_timer(&wdt_timer, jiffies); + return 0; } -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 watchdog_info mpc8xxx_wdt_info = { + .options = WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = "MPC8xxx", }; -static struct miscdevice mpc8xxx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mpc8xxx_wdt_fops, +static struct watchdog_ops mpc8xxx_wdt_ops = { + .owner = THIS_MODULE, + .start = mpc8xxx_wdt_start, + .ping = mpc8xxx_wdt_ping, + .stop = mpc8xxx_wdt_stop, +}; + +static struct watchdog_device mpc8xxx_wdt_dev = { + .info = &mpc8xxx_wdt_info, + .ops = &mpc8xxx_wdt_ops, }; static const struct of_device_id mpc8xxx_wdt_match[]; -static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev) +static int 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; + const struct mpc8xxx_wdt_type *wdt_type; u32 freq = fsl_get_sys_freq(); bool enabled; + unsigned int timeout_sec; match = of_match_device(mpc8xxx_wdt_match, &ofdev->dev); if (!match) @@ -209,7 +173,7 @@ static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev) 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"); + pr_info("could not be enabled in software\n"); ret = -ENOSYS; goto err_unmap; } @@ -220,15 +184,15 @@ static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev) else timeout_sec = timeout / freq; + mpc8xxx_wdt_dev.timeout = timeout_sec; #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); + 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 @@ -236,7 +200,7 @@ static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev) * userspace handles it. */ if (enabled) - mpc8xxx_wdt_timer_ping(0); + mod_timer(&wdt_timer, jiffies); return 0; err_unmap: iounmap(wd_base); @@ -244,11 +208,12 @@ err_unmap: return ret; } -static int __devexit mpc8xxx_wdt_remove(struct platform_device *ofdev) +static int mpc8xxx_wdt_remove(struct platform_device *ofdev) { - mpc8xxx_wdt_pr_warn("watchdog removed"); + pr_crit("Watchdog removed, expect the %s soon!\n", + reset ? "reset" : "machine check exception"); del_timer_sync(&wdt_timer); - misc_deregister(&mpc8xxx_wdt_miscdev); + watchdog_unregister_device(&mpc8xxx_wdt_dev); iounmap(wd_base); return 0; @@ -272,6 +237,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { .compatible = "fsl,mpc823-wdt", .data = &(struct mpc8xxx_wdt_type) { .prescaler = 0x800, + .hw_enabled = true, }, }, {}, @@ -280,7 +246,7 @@ MODULE_DEVICE_TABLE(of, mpc8xxx_wdt_match); static struct platform_driver mpc8xxx_wdt_driver = { .probe = mpc8xxx_wdt_probe, - .remove = __devexit_p(mpc8xxx_wdt_remove), + .remove = mpc8xxx_wdt_remove, .driver = { .name = "mpc8xxx_wdt", .owner = THIS_MODULE, @@ -300,10 +266,11 @@ static int mpc8xxx_wdt_init_late(void) if (!wd_base) return -ENODEV; - ret = misc_register(&mpc8xxx_wdt_miscdev); + watchdog_set_nowayout(&mpc8xxx_wdt_dev, nowayout); + + ret = watchdog_register_device(&mpc8xxx_wdt_dev); if (ret) { - pr_err("cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); + pr_err("cannot register watchdog device (err=%d)\n", ret); return ret; } return 0; @@ -328,4 +295,3 @@ 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 deleted file mode 100644 index 82ccd36e2c9..00000000000 --- a/drivers/watchdog/mpcore_wdt.c +++ /dev/null @@ -1,476 +0,0 @@ -/* - * 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, 0, "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; -} - -#ifdef CONFIG_PM -static int mpcore_wdt_suspend(struct platform_device *dev, pm_message_t msg) -{ - struct mpcore_wdt *wdt = platform_get_drvdata(dev); - mpcore_wdt_stop(wdt); /* Turn the WDT off */ - return 0; -} - -static int mpcore_wdt_resume(struct platform_device *dev) -{ - struct mpcore_wdt *wdt = platform_get_drvdata(dev); - /* re-activate timer */ - if (test_bit(0, &wdt->timer_alive)) - mpcore_wdt_start(wdt); - return 0; -} -#else -#define mpcore_wdt_suspend NULL -#define mpcore_wdt_resume NULL -#endif - -/* 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), - .suspend = mpcore_wdt_suspend, - .resume = mpcore_wdt_resume, - .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 index c29e31d99fe..ff27c4ac96e 100644 --- a/drivers/watchdog/mtx-1_wdt.c +++ b/drivers/watchdog/mtx-1_wdt.c @@ -40,7 +40,6 @@ #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> @@ -204,12 +203,12 @@ static struct miscdevice mtx1_wdt_misc = { }; -static int __devinit mtx1_wdt_probe(struct platform_device *pdev) +static int 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, + ret = devm_gpio_request_one(&pdev->dev, mtx1_wdt_device.gpio, GPIOF_OUT_INIT_HIGH, "mtx1-wdt"); if (ret < 0) { dev_err(&pdev->dev, "failed to request gpio"); @@ -233,7 +232,7 @@ static int __devinit mtx1_wdt_probe(struct platform_device *pdev) return 0; } -static int __devexit mtx1_wdt_remove(struct platform_device *pdev) +static int mtx1_wdt_remove(struct platform_device *pdev) { /* FIXME: do we need to lock this test ? */ if (mtx1_wdt_device.queue) { @@ -241,14 +240,13 @@ static int __devexit mtx1_wdt_remove(struct platform_device *pdev) 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), + .remove = mtx1_wdt_remove, .driver.name = "mtx1-wdt", .driver.owner = THIS_MODULE, }; @@ -258,5 +256,4 @@ module_platform_driver(mtx1_wdt_driver); 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 index 97f8a48d8b7..f9fa5840939 100644 --- a/drivers/watchdog/mv64x60_wdt.c +++ b/drivers/watchdog/mv64x60_wdt.c @@ -15,6 +15,8 @@ * or implied. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> @@ -58,8 +60,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -100,7 +102,7 @@ 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"); + pr_notice("watchdog activated\n"); } } @@ -108,7 +110,7 @@ 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"); + pr_notice("watchdog deactivated\n"); } static void mv64x60_wdt_set_timeout(unsigned int timeout) @@ -139,8 +141,7 @@ 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"); + pr_crit("unexpected close, not stopping timer!\n"); mv64x60_wdt_service(); } expect_close = 0; @@ -252,9 +253,9 @@ static struct miscdevice mv64x60_wdt_miscdev = { .fops = &mv64x60_wdt_fops, }; -static int __devinit mv64x60_wdt_probe(struct platform_device *dev) +static int mv64x60_wdt_probe(struct platform_device *dev) { - struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data; + struct mv64x60_wdt_pdata *pdata = dev_get_platdata(&dev->dev); struct resource *r; int timeout = 10; @@ -275,7 +276,7 @@ static int __devinit mv64x60_wdt_probe(struct platform_device *dev) if (!r) return -ENODEV; - mv64x60_wdt_regs = ioremap(r->start, resource_size(r)); + mv64x60_wdt_regs = devm_ioremap(&dev->dev, r->start, resource_size(r)); if (mv64x60_wdt_regs == NULL) return -ENOMEM; @@ -286,20 +287,18 @@ static int __devinit mv64x60_wdt_probe(struct platform_device *dev) return misc_register(&mv64x60_wdt_miscdev); } -static int __devexit mv64x60_wdt_remove(struct platform_device *dev) +static int 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), + .remove = mv64x60_wdt_remove, .driver = { .owner = THIS_MODULE, .name = MV64x60_WDT_NAME, @@ -308,7 +307,7 @@ static struct platform_driver mv64x60_wdt_driver = { static int __init mv64x60_wdt_init(void) { - printk(KERN_INFO "MV64x60 watchdog driver\n"); + pr_info("MV64x60 watchdog driver\n"); return platform_driver_register(&mv64x60_wdt_driver); } @@ -324,5 +323,4 @@ 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 index 529085b8b8f..7135803ca1a 100644 --- a/drivers/watchdog/nuc900_wdt.c +++ b/drivers/watchdog/nuc900_wdt.c @@ -12,7 +12,6 @@ #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> @@ -55,13 +54,12 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 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; @@ -242,11 +240,13 @@ static struct miscdevice nuc900wdt_miscdev = { .fops = &nuc900wdt_fops, }; -static int __devinit nuc900wdt_probe(struct platform_device *pdev) +static int nuc900wdt_probe(struct platform_device *pdev) { + struct resource *res; int ret = 0; - nuc900_wdt = kzalloc(sizeof(struct nuc900_wdt), GFP_KERNEL); + nuc900_wdt = devm_kzalloc(&pdev->dev, sizeof(*nuc900_wdt), + GFP_KERNEL); if (!nuc900_wdt) return -ENOMEM; @@ -254,33 +254,15 @@ static int __devinit nuc900wdt_probe(struct platform_device *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; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nuc900_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nuc900_wdt->wdt_base)) + return PTR_ERR(nuc900_wdt->wdt_base); - nuc900_wdt->wdt_clock = clk_get(&pdev->dev, NULL); + nuc900_wdt->wdt_clock = devm_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; + return PTR_ERR(nuc900_wdt->wdt_clock); } clk_enable(nuc900_wdt->wdt_clock); @@ -298,37 +280,21 @@ static int __devinit nuc900wdt_probe(struct platform_device *pdev) 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) +static int 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), + .remove = nuc900wdt_remove, .driver = { .name = "nuc900-wdt", .owner = THIS_MODULE, @@ -340,5 +306,4 @@ module_platform_driver(nuc900wdt_driver); 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 index 809f41c30c4..0b9ec61e131 100644 --- a/drivers/watchdog/nv_tco.c +++ b/drivers/watchdog/nv_tco.c @@ -21,6 +21,8 @@ * Includes, defines, variables, module parameters, ... */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -41,7 +43,6 @@ #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; @@ -60,8 +61,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started" " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -169,8 +170,7 @@ static int nv_tco_release(struct inode *inode, struct file *file) if (tco_expect_close == 42) { tco_timer_stop(); } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping " - "watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); tco_timer_keepalive(); } clear_bit(0, &timer_alive); @@ -289,7 +289,7 @@ static struct miscdevice nv_tco_miscdev = { * 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) = { +static const struct pci_device_id tco_pci_tbl[] = { { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS, @@ -302,7 +302,7 @@ MODULE_DEVICE_TABLE(pci, tco_pci_tbl); * Init & exit routines */ -static unsigned char __devinit nv_tco_getdevice(void) +static unsigned char nv_tco_getdevice(void) { struct pci_dev *dev = NULL; u32 val; @@ -323,15 +323,14 @@ static unsigned char __devinit nv_tco_getdevice(void) 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"); + pr_err("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); + pr_err("I/O address 0x%04x already in use\n", tcobase); return 0; } @@ -347,7 +346,7 @@ static unsigned char __devinit nv_tco_getdevice(void) /* 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", + pr_err("I/O address 0x%04x already in use\n", MCP51_SMI_EN(tcobase)); goto out; } @@ -357,7 +356,7 @@ static unsigned char __devinit nv_tco_getdevice(void) 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"); + pr_err("Could not disable SMI caused by TCO\n"); goto out; } @@ -367,8 +366,7 @@ static unsigned char __devinit nv_tco_getdevice(void) 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"); + pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware\n"); goto out; } @@ -378,7 +376,7 @@ out: return 0; } -static int __devinit nv_tco_init(struct platform_device *dev) +static int nv_tco_init(struct platform_device *dev) { int ret; @@ -387,8 +385,8 @@ static int __devinit nv_tco_init(struct platform_device *dev) 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 "); + pr_info("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)); @@ -400,14 +398,14 @@ static int __devinit nv_tco_init(struct platform_device *dev) 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); + pr_info("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); + pr_err("cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); goto unreg_region; } @@ -415,8 +413,8 @@ static int __devinit nv_tco_init(struct platform_device *dev) tco_timer_stop(); - printk(KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec " - "(nowayout=%d)\n", tcobase, heartbeat, nowayout); + pr_info("initialized (0x%04x). heartbeat=%d sec (nowayout=%d)\n", + tcobase, heartbeat, nowayout); return 0; @@ -425,7 +423,7 @@ unreg_region: return ret; } -static void __devexit nv_tco_cleanup(void) +static void nv_tco_cleanup(void) { u32 val; @@ -439,8 +437,7 @@ static void __devexit nv_tco_cleanup(void) 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"); + pr_crit("Couldn't unset REBOOT bit. Machine may soon reset\n"); } /* Deregister */ @@ -448,7 +445,7 @@ static void __devexit nv_tco_cleanup(void) release_region(tcobase, 0x10); } -static int __devexit nv_tco_remove(struct platform_device *dev) +static int nv_tco_remove(struct platform_device *dev) { if (tcobase) nv_tco_cleanup(); @@ -471,7 +468,7 @@ static void nv_tco_shutdown(struct platform_device *dev) static struct platform_driver nv_tco_driver = { .probe = nv_tco_init, - .remove = __devexit_p(nv_tco_remove), + .remove = nv_tco_remove, .shutdown = nv_tco_shutdown, .driver = { .owner = THIS_MODULE, @@ -483,8 +480,7 @@ static int __init nv_tco_init_module(void) { int err; - printk(KERN_INFO PFX "NV TCO WatchDog Timer Driver v%s\n", - TCO_VERSION); + pr_info("NV TCO WatchDog Timer Driver v%s\n", TCO_VERSION); err = platform_driver_register(&nv_tco_driver); if (err) @@ -508,7 +504,7 @@ 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"); + pr_info("NV TCO Watchdog Module Unloaded\n"); } module_init(nv_tco_init_module); @@ -517,4 +513,3 @@ 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/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c index 7c0d8630e64..4baf2d78892 100644 --- a/drivers/watchdog/octeon-wdt-main.c +++ b/drivers/watchdog/octeon-wdt-main.c @@ -52,6 +52,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/watchdog.h> @@ -95,8 +97,8 @@ 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); +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) ")"); @@ -201,7 +203,7 @@ static void __init octeon_wdt_build_stage1(void) uasm_resolve_relocs(relocs, labels); len = (int)(p - nmi_stage1_insns); - pr_debug("Synthesized NMI stage 1 handler (%d instructions).\n", len); + pr_debug("Synthesized NMI stage 1 handler (%d instructions)\n", len); pr_debug("\t.set push\n"); pr_debug("\t.set noreorder\n"); @@ -627,7 +629,7 @@ static int octeon_wdt_release(struct inode *inode, struct file *file) do_coundown = 0; octeon_wdt_ping(); } else { - pr_crit("octeon_wdt: WDT device closed unexpectedly. WDT will not stop!\n"); + pr_crit("WDT device closed unexpectedly. WDT will not stop!\n"); } clear_bit(0, &octeon_wdt_is_open); expect_close = 0; @@ -684,12 +686,12 @@ static int __init octeon_wdt_init(void) octeon_wdt_calc_parameters(heartbeat); - pr_info("octeon_wdt: Initial granularity %d Sec.\n", timeout_sec); + pr_info("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); + pr_err("cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); goto out; } @@ -706,10 +708,13 @@ static int __init octeon_wdt_init(void) cpumask_clear(&irq_enabled_cpus); + cpu_notifier_register_begin(); for_each_online_cpu(cpu) octeon_wdt_setup_interrupt(cpu); - register_hotcpu_notifier(&octeon_wdt_cpu_notifier); + __register_hotcpu_notifier(&octeon_wdt_cpu_notifier); + cpu_notifier_register_done(); + out: return ret; } @@ -723,7 +728,8 @@ static void __exit octeon_wdt_cleanup(void) misc_deregister(&octeon_wdt_miscdev); - unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier); + cpu_notifier_register_begin(); + __unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier); for_each_online_cpu(cpu) { int core = cpu2core(cpu); @@ -732,6 +738,9 @@ static void __exit octeon_wdt_cleanup(void) /* Free the interrupt handler */ free_irq(OCTEON_IRQ_WDOG0 + core, octeon_wdt_poke_irq); } + + cpu_notifier_register_done(); + /* * Disable the boot-bus memory, the code it points to is soon * to go missing. diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index f359ab85c3b..1e6e28df5d7 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c @@ -1,34 +1,22 @@ /* -* of_xilinx_wdt.c 1.01 A Watchdog Device Driver for Xilinx xps_timebase_wdt -* -* (C) Copyright 2011 (Alejandro Cabrera <aldaya@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; either version -* 2 of the License, or (at your option) any later version. -* -* ----------------------- -* 30-May-2011 Alejandro Cabrera <aldaya@gmail.com> -* - If "xlnx,wdt-enable-once" wasn't found on device tree the -* module will use CONFIG_WATCHDOG_NOWAYOUT -* - If the device tree parameters ("clock-frequency" and -* "xlnx,wdt-interval") wasn't found the driver won't -* know the wdt reset interval -*/ + * Watchdog Device Driver for Xilinx axi/xps_timebase_wdt + * + * (C) Copyright 2013 - 2014 Xilinx, Inc. + * (C) Copyright 2011 (Alejandro Cabrera <aldaya@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; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/err.h> #include <linux/module.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/io.h> -#include <linux/uaccess.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_address.h> @@ -51,102 +39,103 @@ #define XWT_TIMER_FAILED 0xFFFFFFFF #define WATCHDOG_NAME "Xilinx Watchdog" -#define PFX WATCHDOG_NAME ": " struct xwdt_device { - struct resource res; void __iomem *base; - u32 nowayout; u32 wdt_interval; - u32 boot_status; + spinlock_t spinlock; + struct watchdog_device xilinx_wdt_wdd; }; -static struct xwdt_device xdev; - -static u32 timeout; -static u32 control_status_reg; -static u8 expect_close; -static u8 no_timeout; -static unsigned long driver_open; - -static DEFINE_SPINLOCK(spinlock); - -static void xwdt_start(void) +static int xilinx_wdt_start(struct watchdog_device *wdd) { - spin_lock(&spinlock); + u32 control_status_reg; + struct xwdt_device *xdev = watchdog_get_drvdata(wdd); + + spin_lock(&xdev->spinlock); /* Clean previous status and enable the watchdog timer */ - control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); + control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET); control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK), - xdev.base + XWT_TWCSR0_OFFSET); + xdev->base + XWT_TWCSR0_OFFSET); + + iowrite32(XWT_CSRX_EWDT2_MASK, xdev->base + XWT_TWCSR1_OFFSET); - iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET); + spin_unlock(&xdev->spinlock); - spin_unlock(&spinlock); + return 0; } -static void xwdt_stop(void) +static int xilinx_wdt_stop(struct watchdog_device *wdd) { - spin_lock(&spinlock); + u32 control_status_reg; + struct xwdt_device *xdev = watchdog_get_drvdata(wdd); - control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); + spin_lock(&xdev->spinlock); + + control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET); iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK), - xdev.base + XWT_TWCSR0_OFFSET); + xdev->base + XWT_TWCSR0_OFFSET); + + iowrite32(0, xdev->base + XWT_TWCSR1_OFFSET); - iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET); + spin_unlock(&xdev->spinlock); + pr_info("Stopped!\n"); - spin_unlock(&spinlock); - printk(KERN_INFO PFX "Stopped!\n"); + return 0; } -static void xwdt_keepalive(void) +static int xilinx_wdt_keepalive(struct watchdog_device *wdd) { - spin_lock(&spinlock); + u32 control_status_reg; + struct xwdt_device *xdev = watchdog_get_drvdata(wdd); - control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); - control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); - iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET); + spin_lock(&xdev->spinlock); - spin_unlock(&spinlock); -} + control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET); + control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); + iowrite32(control_status_reg, xdev->base + XWT_TWCSR0_OFFSET); -static void xwdt_get_status(int *status) -{ - int new_status; + spin_unlock(&xdev->spinlock); - spin_lock(&spinlock); + return 0; +} - control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); - new_status = ((control_status_reg & - (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0); - spin_unlock(&spinlock); +static const struct watchdog_info xilinx_wdt_ident = { + .options = WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = WATCHDOG_NAME, +}; - *status = 0; - if (new_status & 1) - *status |= WDIOF_CARDRESET; -} +static const struct watchdog_ops xilinx_wdt_ops = { + .owner = THIS_MODULE, + .start = xilinx_wdt_start, + .stop = xilinx_wdt_stop, + .ping = xilinx_wdt_keepalive, +}; -static u32 xwdt_selftest(void) +static u32 xwdt_selftest(struct xwdt_device *xdev) { int i; u32 timer_value1; u32 timer_value2; - spin_lock(&spinlock); + spin_lock(&xdev->spinlock); - timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET); - timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); + timer_value1 = ioread32(xdev->base + XWT_TBR_OFFSET); + timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET); for (i = 0; ((i <= XWT_MAX_SELFTEST_LOOP_COUNT) && (timer_value2 == timer_value1)); i++) { - timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); + timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET); } - spin_unlock(&spinlock); + spin_unlock(&xdev->spinlock); if (timer_value2 != timer_value1) return ~XWT_TIMER_FAILED; @@ -154,251 +143,90 @@ static u32 xwdt_selftest(void) return XWT_TIMER_FAILED; } -static int xwdt_open(struct inode *inode, struct file *file) -{ - /* Only one process can handle the wdt at a time */ - if (test_and_set_bit(0, &driver_open)) - return -EBUSY; - - /* Make sure that the module are always loaded...*/ - if (xdev.nowayout) - __module_get(THIS_MODULE); - - xwdt_start(); - printk(KERN_INFO PFX "Started...\n"); - - return nonseekable_open(inode, file); -} - -static int xwdt_release(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - xwdt_stop(); - } else { - printk(KERN_CRIT PFX - "Unexpected close, not stopping watchdog!\n"); - xwdt_keepalive(); - } - - clear_bit(0, &driver_open); - expect_close = 0; - return 0; -} - -/* - * xwdt_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 don't define content meaning. - */ -static ssize_t xwdt_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) -{ - if (len) { - if (!xdev.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; - } - } - xwdt_keepalive(); - } - return len; -} - -static const struct watchdog_info ident = { - .options = WDIOF_MAGICCLOSE | - WDIOF_KEEPALIVEPING, - .firmware_version = 1, - .identity = WATCHDOG_NAME, -}; - -/* - * xwdt_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 xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int status; - - 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_GETBOOTSTATUS: - return put_user(xdev.boot_status, uarg.i); - - case WDIOC_GETSTATUS: - xwdt_get_status(&status); - return put_user(status, uarg.i); - - case WDIOC_KEEPALIVE: - xwdt_keepalive(); - return 0; - - case WDIOC_GETTIMEOUT: - if (no_timeout) - return -ENOTTY; - else - return put_user(timeout, uarg.i); - - default: - return -ENOTTY; - } -} - -static const struct file_operations xwdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = xwdt_write, - .open = xwdt_open, - .release = xwdt_release, - .unlocked_ioctl = xwdt_ioctl, -}; - -static struct miscdevice xwdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &xwdt_fops, -}; - -static int __devinit xwdt_probe(struct platform_device *pdev) +static int xwdt_probe(struct platform_device *pdev) { int rc; - u32 *tmptr; - u32 *pfreq; - - no_timeout = 0; - - pfreq = (u32 *)of_get_property(pdev->dev.of_node->parent, - "clock-frequency", NULL); - - if (pfreq == NULL) { - printk(KERN_WARNING PFX - "The watchdog clock frequency cannot be obtained!\n"); - no_timeout = 1; - } - - rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res); - if (rc) { - printk(KERN_WARNING PFX "invalid address!\n"); - return rc; - } - - tmptr = (u32 *)of_get_property(pdev->dev.of_node, - "xlnx,wdt-interval", NULL); - if (tmptr == NULL) { - printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-interval\"" - " not found in device tree!\n"); - no_timeout = 1; - } else { - xdev.wdt_interval = *tmptr; - } - - tmptr = (u32 *)of_get_property(pdev->dev.of_node, - "xlnx,wdt-enable-once", NULL); - if (tmptr == NULL) { - printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-enable-once\"" - " not found in device tree!\n"); - xdev.nowayout = WATCHDOG_NOWAYOUT; - } - -/* - * Twice of the 2^wdt_interval / freq because the first wdt overflow is - * ignored (interrupt), reset is only generated at second wdt overflow - */ - if (!no_timeout) - timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq); - - if (!request_mem_region(xdev.res.start, - xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) { - rc = -ENXIO; - printk(KERN_ERR PFX "memory request failure!\n"); - goto err_out; - } - - xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1); - if (xdev.base == NULL) { - rc = -ENOMEM; - printk(KERN_ERR PFX "ioremap failure!\n"); - goto release_mem; - } - - rc = xwdt_selftest(); + u32 pfreq = 0, enable_once = 0; + struct resource *res; + struct xwdt_device *xdev; + struct watchdog_device *xilinx_wdt_wdd; + + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return -ENOMEM; + + xilinx_wdt_wdd = &xdev->xilinx_wdt_wdd; + xilinx_wdt_wdd->info = &xilinx_wdt_ident; + xilinx_wdt_wdd->ops = &xilinx_wdt_ops; + xilinx_wdt_wdd->parent = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xdev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xdev->base)) + return PTR_ERR(xdev->base); + + rc = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &pfreq); + if (rc) + dev_warn(&pdev->dev, + "The watchdog clock frequency cannot be obtained\n"); + + rc = of_property_read_u32(pdev->dev.of_node, "xlnx,wdt-interval", + &xdev->wdt_interval); + if (rc) + dev_warn(&pdev->dev, + "Parameter \"xlnx,wdt-interval\" not found\n"); + + rc = of_property_read_u32(pdev->dev.of_node, "xlnx,wdt-enable-once", + &enable_once); + if (rc) + dev_warn(&pdev->dev, + "Parameter \"xlnx,wdt-enable-once\" not found\n"); + + watchdog_set_nowayout(xilinx_wdt_wdd, enable_once); + + /* + * Twice of the 2^wdt_interval / freq because the first wdt overflow is + * ignored (interrupt), reset is only generated at second wdt overflow + */ + if (pfreq && xdev->wdt_interval) + xilinx_wdt_wdd->timeout = 2 * ((1 << xdev->wdt_interval) / + pfreq); + + spin_lock_init(&xdev->spinlock); + watchdog_set_drvdata(xilinx_wdt_wdd, xdev); + + rc = xwdt_selftest(xdev); if (rc == XWT_TIMER_FAILED) { - printk(KERN_ERR PFX "SelfTest routine error!\n"); - goto unmap_io; + dev_err(&pdev->dev, "SelfTest routine error\n"); + return rc; } - xwdt_get_status(&xdev.boot_status); - - rc = misc_register(&xwdt_miscdev); + rc = watchdog_register_device(xilinx_wdt_wdd); if (rc) { - printk(KERN_ERR PFX - "cannot register miscdev on minor=%d (err=%d)\n", - xwdt_miscdev.minor, rc); - goto unmap_io; + dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc); + return rc; } - if (no_timeout) - printk(KERN_INFO PFX - "driver loaded (timeout=? sec, nowayout=%d)\n", - xdev.nowayout); - else - printk(KERN_INFO PFX - "driver loaded (timeout=%d sec, nowayout=%d)\n", - timeout, xdev.nowayout); + dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n", + xdev->base, xilinx_wdt_wdd->timeout); - expect_close = 0; - clear_bit(0, &driver_open); + platform_set_drvdata(pdev, xdev); return 0; - -unmap_io: - iounmap(xdev.base); -release_mem: - release_mem_region(xdev.res.start, resource_size(&xdev.res)); -err_out: - return rc; } -static int __devexit xwdt_remove(struct platform_device *dev) +static int xwdt_remove(struct platform_device *pdev) { - misc_deregister(&xwdt_miscdev); - iounmap(xdev.base); - release_mem_region(xdev.res.start, resource_size(&xdev.res)); + struct xwdt_device *xdev = platform_get_drvdata(pdev); + + watchdog_unregister_device(&xdev->xilinx_wdt_wdd); return 0; } /* Match table for of_platform binding */ -static struct of_device_id __devinitdata xwdt_of_match[] = { +static const struct of_device_id xwdt_of_match[] = { + { .compatible = "xlnx,xps-timebase-wdt-1.00.a", }, { .compatible = "xlnx,xps-timebase-wdt-1.01.a", }, {}, }; @@ -406,7 +234,7 @@ MODULE_DEVICE_TABLE(of, xwdt_of_match); static struct platform_driver xwdt_driver = { .probe = xwdt_probe, - .remove = __devexit_p(xwdt_remove), + .remove = xwdt_remove, .driver = { .owner = THIS_MODULE, .name = WATCHDOG_NAME, @@ -418,5 +246,4 @@ module_platform_driver(xwdt_driver); MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>"); MODULE_DESCRIPTION("Xilinx Watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index d19ff5145e8..3691b157516 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -26,58 +26,54 @@ * Use the driver model and standard identifiers; handle bigger timeouts. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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 <linux/platform_data/omap-wd-timer.h> #include "omap_wdt.h" -static struct platform_device *omap_wdt_dev; +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " + "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 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 DEFINE_SPINLOCK(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; + bool omap_wdt_users; + int wdt_trgr_pattern; + struct mutex lock; /* to avoid races with PM */ }; -static void omap_wdt_ping(struct omap_wdt_dev *wdev) +static void omap_wdt_reload(struct omap_wdt_dev *wdev) { void __iomem *base = wdev->base; /* wait for posted write to complete */ - while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) + while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08) cpu_relax(); - wdt_trgr_pattern = ~wdt_trgr_pattern; - __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR)); + wdev->wdt_trgr_pattern = ~wdev->wdt_trgr_pattern; + writel_relaxed(wdev->wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR)); /* wait for posted write to complete */ - while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) + while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08) cpu_relax(); /* reloaded WCRR from WLDR */ } @@ -87,12 +83,12 @@ 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) + writel_relaxed(0xBBBB, base + OMAP_WATCHDOG_SPR); + while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10) cpu_relax(); - __raw_writel(0x4444, base + OMAP_WATCHDOG_SPR); - while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10) + writel_relaxed(0x4444, base + OMAP_WATCHDOG_SPR); + while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10) cpu_relax(); } @@ -101,289 +97,201 @@ 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) + writel_relaxed(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x10) cpu_relax(); - __raw_writel(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */ - while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10) + writel_relaxed(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (readl_relaxed(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) +static void omap_wdt_set_timer(struct omap_wdt_dev *wdev, + unsigned int timeout) { - u32 pre_margin = GET_WLDR_VAL(timer_margin); + u32 pre_margin = GET_WLDR_VAL(timeout); 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) + while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x04) cpu_relax(); - __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR); - while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04) + writel_relaxed(pre_margin, base + OMAP_WATCHDOG_LDR); + while (readl_relaxed(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) +static int omap_wdt_start(struct watchdog_device *wdog) { - struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev); + struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); void __iomem *base = wdev->base; - if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users))) - return -EBUSY; + mutex_lock(&wdev->lock); + + wdev->omap_wdt_users = true; pm_runtime_get_sync(wdev->dev); /* initialize prescaler */ - while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) + while (readl_relaxed(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) + writel_relaxed((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL); + while (readl_relaxed(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_set_timer(wdev, wdog->timeout); + omap_wdt_reload(wdev); /* trigger loading of new timeout value */ omap_wdt_enable(wdev); - pm_runtime_put_sync(wdev->dev); + mutex_unlock(&wdev->lock); - return nonseekable_open(inode, file); + return 0; } -static int omap_wdt_release(struct inode *inode, struct file *file) +static int omap_wdt_stop(struct watchdog_device *wdog) { - 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); + struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + mutex_lock(&wdev->lock); 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; - + wdev->omap_wdt_users = false; + mutex_unlock(&wdev->lock); return 0; } -static ssize_t omap_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) +static int omap_wdt_ping(struct watchdog_device *wdog) { - struct omap_wdt_dev *wdev = file->private_data; + struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); - /* 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; + mutex_lock(&wdev->lock); + omap_wdt_reload(wdev); + mutex_unlock(&wdev->lock); + + return 0; } -static long omap_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static int omap_wdt_set_timeout(struct watchdog_device *wdog, + unsigned int timeout) { - 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); - return put_user(0, (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); + struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); - pm_runtime_get_sync(wdev->dev); - spin_lock(&wdt_lock); - omap_wdt_disable(wdev); - omap_wdt_set_timeout(wdev); - omap_wdt_enable(wdev); + mutex_lock(&wdev->lock); + omap_wdt_disable(wdev); + omap_wdt_set_timer(wdev, timeout); + omap_wdt_enable(wdev); + omap_wdt_reload(wdev); + wdog->timeout = timeout; + mutex_unlock(&wdev->lock); - 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; - } + return 0; } -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 const struct watchdog_info omap_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "OMAP Watchdog", +}; + +static const struct watchdog_ops omap_wdt_ops = { + .owner = THIS_MODULE, + .start = omap_wdt_start, + .stop = omap_wdt_stop, + .ping = omap_wdt_ping, + .set_timeout = omap_wdt_set_timeout, }; -static int __devinit omap_wdt_probe(struct platform_device *pdev) +static int omap_wdt_probe(struct platform_device *pdev) { - struct resource *res, *mem; + struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct watchdog_device *omap_wdt; + struct resource *res; struct omap_wdt_dev *wdev; + u32 rs; int ret; - /* reserve static register mappings */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENOENT; - goto err_get_resource; - } + omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL); + if (!omap_wdt) + return -ENOMEM; - if (omap_wdt_dev) { - ret = -EBUSY; - goto err_busy; - } + wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) + return -ENOMEM; - mem = request_mem_region(res->start, resource_size(res), pdev->name); - if (!mem) { - ret = -EBUSY; - goto err_busy; - } + wdev->omap_wdt_users = false; + wdev->dev = &pdev->dev; + wdev->wdt_trgr_pattern = 0x1234; + mutex_init(&wdev->lock); - wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL); - if (!wdev) { - ret = -ENOMEM; - goto err_kzalloc; - } + /* reserve static register mappings */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdev->base)) + return PTR_ERR(wdev->base); - wdev->omap_wdt_users = 0; - wdev->mem = mem; - wdev->dev = &pdev->dev; + omap_wdt->info = &omap_wdt_info; + omap_wdt->ops = &omap_wdt_ops; + omap_wdt->min_timeout = TIMER_MARGIN_MIN; + omap_wdt->max_timeout = TIMER_MARGIN_MAX; - wdev->base = ioremap(res->start, resource_size(res)); - if (!wdev->base) { - ret = -ENOMEM; - goto err_ioremap; - } + if (timer_margin >= TIMER_MARGIN_MIN && + timer_margin <= TIMER_MARGIN_MAX) + omap_wdt->timeout = timer_margin; + else + omap_wdt->timeout = TIMER_MARGIN_DEFAULT; + + watchdog_set_drvdata(omap_wdt, wdev); + watchdog_set_nowayout(omap_wdt, nowayout); - platform_set_drvdata(pdev, wdev); + platform_set_drvdata(pdev, omap_wdt); pm_runtime_enable(wdev->dev); pm_runtime_get_sync(wdev->dev); - omap_wdt_disable(wdev); - omap_wdt_adjust_timeout(timer_margin); + if (pdata && pdata->read_reset_sources) + rs = pdata->read_reset_sources(); + else + rs = 0; + omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ? + WDIOF_CARDRESET : 0; - 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; + omap_wdt_disable(wdev); - ret = misc_register(&(wdev->omap_wdt_miscdev)); - if (ret) - goto err_misc; + ret = watchdog_register_device(omap_wdt); + if (ret) { + pm_runtime_disable(wdev->dev); + return ret; + } pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", - __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, - timer_margin); + readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, + omap_wdt->timeout); pm_runtime_put_sync(wdev->dev); - omap_wdt_dev = pdev; - return 0; - -err_misc: - pm_runtime_disable(wdev->dev); - 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); + struct watchdog_device *wdog = platform_get_drvdata(pdev); + struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + mutex_lock(&wdev->lock); if (wdev->omap_wdt_users) { - pm_runtime_get_sync(wdev->dev); omap_wdt_disable(wdev); pm_runtime_put_sync(wdev->dev); } + mutex_unlock(&wdev->lock); } -static int __devexit omap_wdt_remove(struct platform_device *pdev) +static int 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); + struct watchdog_device *wdog = platform_get_drvdata(pdev); + struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); pm_runtime_disable(wdev->dev); - 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; + watchdog_unregister_device(wdog); return 0; } @@ -398,27 +306,31 @@ static int __devexit omap_wdt_remove(struct platform_device *pdev) static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) { - struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); + struct watchdog_device *wdog = platform_get_drvdata(pdev); + struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + mutex_lock(&wdev->lock); if (wdev->omap_wdt_users) { - pm_runtime_get_sync(wdev->dev); omap_wdt_disable(wdev); pm_runtime_put_sync(wdev->dev); } + mutex_unlock(&wdev->lock); return 0; } static int omap_wdt_resume(struct platform_device *pdev) { - struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); + struct watchdog_device *wdog = platform_get_drvdata(pdev); + struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); + mutex_lock(&wdev->lock); 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); + omap_wdt_reload(wdev); } + mutex_unlock(&wdev->lock); return 0; } @@ -428,15 +340,22 @@ static int omap_wdt_resume(struct platform_device *pdev) #define omap_wdt_resume NULL #endif +static const struct of_device_id omap_wdt_of_match[] = { + { .compatible = "ti,omap3-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_wdt_of_match); + static struct platform_driver omap_wdt_driver = { .probe = omap_wdt_probe, - .remove = __devexit_p(omap_wdt_remove), + .remove = omap_wdt_remove, .shutdown = omap_wdt_shutdown, .suspend = omap_wdt_suspend, .resume = omap_wdt_resume, .driver = { .owner = THIS_MODULE, .name = "omap_wdt", + .of_match_table = omap_wdt_of_match, }, }; @@ -444,5 +363,4 @@ module_platform_driver(omap_wdt_driver); MODULE_AUTHOR("George G. Davis"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); MODULE_ALIAS("platform:omap_wdt"); diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 4ad78f86851..00d0741228f 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -10,287 +10,592 @@ * warranty of any kind, whether express or implied. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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/interrupt.h> #include <linux/io.h> -#include <linux/spinlock.h> -#include <mach/bridge-regs.h> -#include <plat/orion_wdt.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_device.h> + +/* RSTOUT mask register physical address for Orion5x, Kirkwood and Dove */ +#define ORION_RSTOUT_MASK_OFFSET 0x20108 + +/* Internal registers can be configured at any 1 MiB aligned address */ +#define INTERNAL_REGS_MASK ~(SZ_1M - 1) /* * Watchdog timer block registers. */ -#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000) -#define WDT_EN 0x0010 -#define WDT_VAL (TIMER_VIRT_BASE + 0x0024) +#define TIMER_CTRL 0x0000 +#define TIMER_A370_STATUS 0x04 #define WDT_MAX_CYCLE_COUNT 0xffffffff -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 -static int nowayout = WATCHDOG_NOWAYOUT; +#define WDT_A370_RATIO_MASK(v) ((v) << 16) +#define WDT_A370_RATIO_SHIFT 5 +#define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT) + +#define WDT_AXP_FIXED_ENABLE_BIT BIT(10) +#define WDT_A370_EXPIRED BIT(31) + +static bool 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 DEFINE_SPINLOCK(wdt_lock); -static void orion_wdt_ping(void) +struct orion_watchdog; + +struct orion_watchdog_data { + int wdt_counter_offset; + int wdt_enable_bit; + int rstout_enable_bit; + int rstout_mask_bit; + int (*clock_init)(struct platform_device *, + struct orion_watchdog *); + int (*enabled)(struct orion_watchdog *); + int (*start)(struct watchdog_device *); + int (*stop)(struct watchdog_device *); +}; + +struct orion_watchdog { + struct watchdog_device wdt; + void __iomem *reg; + void __iomem *rstout; + void __iomem *rstout_mask; + unsigned long clk_rate; + struct clk *clk; + const struct orion_watchdog_data *data; +}; + +static int orion_wdt_clock_init(struct platform_device *pdev, + struct orion_watchdog *dev) { - spin_lock(&wdt_lock); + int ret; - /* Reload watchdog duration */ - writel(wdt_tclk * heartbeat, WDT_VAL); + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + ret = clk_prepare_enable(dev->clk); + if (ret) { + clk_put(dev->clk); + return ret; + } - spin_unlock(&wdt_lock); + dev->clk_rate = clk_get_rate(dev->clk); + return 0; } -static void orion_wdt_enable(void) +static int armada370_wdt_clock_init(struct platform_device *pdev, + struct orion_watchdog *dev) { - u32 reg; + int ret; + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + ret = clk_prepare_enable(dev->clk); + if (ret) { + clk_put(dev->clk); + return ret; + } + + /* Setup watchdog input clock */ + atomic_io_modify(dev->reg + TIMER_CTRL, + WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT), + WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT)); + + dev->clk_rate = clk_get_rate(dev->clk) / WDT_A370_RATIO; + return 0; +} + +static int armadaxp_wdt_clock_init(struct platform_device *pdev, + struct orion_watchdog *dev) +{ + int ret; + + dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed"); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + ret = clk_prepare_enable(dev->clk); + if (ret) { + clk_put(dev->clk); + return ret; + } + + /* Enable the fixed watchdog clock input */ + atomic_io_modify(dev->reg + TIMER_CTRL, + WDT_AXP_FIXED_ENABLE_BIT, + WDT_AXP_FIXED_ENABLE_BIT); + + dev->clk_rate = clk_get_rate(dev->clk); + return 0; +} + +static int orion_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + /* Reload watchdog duration */ + writel(dev->clk_rate * wdt_dev->timeout, + dev->reg + dev->data->wdt_counter_offset); + return 0; +} - spin_lock(&wdt_lock); +static int armada375_start(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + u32 reg; /* Set watchdog duration */ - writel(wdt_tclk * heartbeat, WDT_VAL); + writel(dev->clk_rate * wdt_dev->timeout, + dev->reg + dev->data->wdt_counter_offset); - /* Clear watchdog timer interrupt */ - reg = readl(BRIDGE_CAUSE); - reg &= ~WDT_INT_REQ; - writel(reg, BRIDGE_CAUSE); + /* Clear the watchdog expiration bit */ + atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0); /* Enable watchdog timer */ - reg = readl(TIMER_CTRL); - reg |= WDT_EN; - writel(reg, TIMER_CTRL); + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, + dev->data->wdt_enable_bit); /* Enable reset on watchdog */ - reg = readl(RSTOUTn_MASK); - reg |= WDT_RESET_OUT_EN; - writel(reg, RSTOUTn_MASK); + reg = readl(dev->rstout); + reg |= dev->data->rstout_enable_bit; + writel(reg, dev->rstout); - spin_unlock(&wdt_lock); + atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, 0); + return 0; } -static void orion_wdt_disable(void) +static int armada370_start(struct watchdog_device *wdt_dev) { + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); u32 reg; - spin_lock(&wdt_lock); + /* Set watchdog duration */ + writel(dev->clk_rate * wdt_dev->timeout, + dev->reg + dev->data->wdt_counter_offset); - /* Disable reset on watchdog */ - reg = readl(RSTOUTn_MASK); - reg &= ~WDT_RESET_OUT_EN; - writel(reg, RSTOUTn_MASK); + /* Clear the watchdog expiration bit */ + atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0); - /* Disable watchdog timer */ - reg = readl(TIMER_CTRL); - reg &= ~WDT_EN; - writel(reg, TIMER_CTRL); + /* Enable watchdog timer */ + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, + dev->data->wdt_enable_bit); - spin_unlock(&wdt_lock); + /* Enable reset on watchdog */ + reg = readl(dev->rstout); + reg |= dev->data->rstout_enable_bit; + writel(reg, dev->rstout); + return 0; } -static int orion_wdt_get_timeleft(int *time_left) +static int orion_start(struct watchdog_device *wdt_dev) { - spin_lock(&wdt_lock); - *time_left = readl(WDT_VAL) / wdt_tclk; - spin_unlock(&wdt_lock); + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + /* Set watchdog duration */ + writel(dev->clk_rate * wdt_dev->timeout, + dev->reg + dev->data->wdt_counter_offset); + + /* Enable watchdog timer */ + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, + dev->data->wdt_enable_bit); + + /* Enable reset on watchdog */ + atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, + dev->data->rstout_enable_bit); + return 0; } -static int orion_wdt_open(struct inode *inode, struct file *file) +static int orion_wdt_start(struct watchdog_device *wdt_dev) { - 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); + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + /* There are some per-SoC quirks to handle */ + return dev->data->start(wdt_dev); } -static ssize_t orion_wdt_write(struct file *file, const char *data, - size_t len, loff_t *ppos) +static int orion_stop(struct watchdog_device *wdt_dev) { - 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; + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + /* Disable reset on watchdog */ + atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0); + + /* Disable watchdog timer */ + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); + + return 0; } -static int orion_wdt_settimeout(int new_time) +static int armada375_stop(struct watchdog_device *wdt_dev) { - if ((new_time <= 0) || (new_time > wdt_max_duration)) - return -EINVAL; + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + u32 reg; + + /* Disable reset on watchdog */ + atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, + dev->data->rstout_mask_bit); + reg = readl(dev->rstout); + reg &= ~dev->data->rstout_enable_bit; + writel(reg, dev->rstout); + + /* Disable watchdog timer */ + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); - /* 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 int armada370_stop(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + u32 reg; + + /* Disable reset on watchdog */ + reg = readl(dev->rstout); + reg &= ~dev->data->rstout_enable_bit; + writel(reg, dev->rstout); -static long orion_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) + /* Disable watchdog timer */ + atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); + + return 0; +} + +static int orion_wdt_stop(struct watchdog_device *wdt_dev) { - 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 */ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); - case WDIOC_GETTIMEOUT: - ret = put_user(heartbeat, (int *)arg); - break; + return dev->data->stop(wdt_dev); +} - case WDIOC_GETTIMELEFT: - if (orion_wdt_get_timeleft(&time)) { - ret = -EINVAL; - break; - } - ret = put_user(time, (int *)arg); - break; - } - return ret; +static int orion_enabled(struct orion_watchdog *dev) +{ + bool enabled, running; + + enabled = readl(dev->rstout) & dev->data->rstout_enable_bit; + running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit; + + return enabled && running; } -static int orion_wdt_release(struct inode *inode, struct file *file) +static int armada375_enabled(struct orion_watchdog *dev) { - 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); + bool masked, enabled, running; + + masked = readl(dev->rstout_mask) & dev->data->rstout_mask_bit; + enabled = readl(dev->rstout) & dev->data->rstout_enable_bit; + running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit; + + return !masked && enabled && running; +} + +static int orion_wdt_enabled(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + return dev->data->enabled(dev); +} +static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) +{ + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate; +} + +static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + wdt_dev->timeout = timeout; return 0; } +static const struct watchdog_info orion_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Orion Watchdog", +}; + +static const struct watchdog_ops orion_wdt_ops = { + .owner = THIS_MODULE, + .start = orion_wdt_start, + .stop = orion_wdt_stop, + .ping = orion_wdt_ping, + .set_timeout = orion_wdt_set_timeout, + .get_timeleft = orion_wdt_get_timeleft, +}; + +static irqreturn_t orion_wdt_irq(int irq, void *devid) +{ + panic("Watchdog Timeout"); + return IRQ_HANDLED; +} + +/* + * The original devicetree binding for this driver specified only + * one memory resource, so in order to keep DT backwards compatibility + * we try to fallback to a hardcoded register address, if the resource + * is missing from the devicetree. + */ +static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev, + phys_addr_t internal_regs) +{ + struct resource *res; + phys_addr_t rstout; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) + return devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET; + + WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout); + return devm_ioremap(&pdev->dev, rstout, 0x4); +} -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 const struct orion_watchdog_data orion_data = { + .rstout_enable_bit = BIT(1), + .wdt_enable_bit = BIT(4), + .wdt_counter_offset = 0x24, + .clock_init = orion_wdt_clock_init, + .enabled = orion_enabled, + .start = orion_start, + .stop = orion_stop, }; -static struct miscdevice orion_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &orion_wdt_fops, +static const struct orion_watchdog_data armada370_data = { + .rstout_enable_bit = BIT(8), + .wdt_enable_bit = BIT(8), + .wdt_counter_offset = 0x34, + .clock_init = armada370_wdt_clock_init, + .enabled = orion_enabled, + .start = armada370_start, + .stop = armada370_stop, }; -static int __devinit orion_wdt_probe(struct platform_device *pdev) +static const struct orion_watchdog_data armadaxp_data = { + .rstout_enable_bit = BIT(8), + .wdt_enable_bit = BIT(8), + .wdt_counter_offset = 0x34, + .clock_init = armadaxp_wdt_clock_init, + .enabled = orion_enabled, + .start = armada370_start, + .stop = armada370_stop, +}; + +static const struct orion_watchdog_data armada375_data = { + .rstout_enable_bit = BIT(8), + .rstout_mask_bit = BIT(10), + .wdt_enable_bit = BIT(8), + .wdt_counter_offset = 0x34, + .clock_init = armada370_wdt_clock_init, + .enabled = armada375_enabled, + .start = armada375_start, + .stop = armada375_stop, +}; + +static const struct orion_watchdog_data armada380_data = { + .rstout_enable_bit = BIT(8), + .rstout_mask_bit = BIT(10), + .wdt_enable_bit = BIT(8), + .wdt_counter_offset = 0x34, + .clock_init = armadaxp_wdt_clock_init, + .enabled = armada375_enabled, + .start = armada375_start, + .stop = armada375_stop, +}; + +static const struct of_device_id orion_wdt_of_match_table[] = { + { + .compatible = "marvell,orion-wdt", + .data = &orion_data, + }, + { + .compatible = "marvell,armada-370-wdt", + .data = &armada370_data, + }, + { + .compatible = "marvell,armada-xp-wdt", + .data = &armadaxp_data, + }, + { + .compatible = "marvell,armada-375-wdt", + .data = &armada375_data, + }, + { + .compatible = "marvell,armada-380-wdt", + .data = &armada380_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table); + +static int orion_wdt_get_regs(struct platform_device *pdev, + struct orion_watchdog *dev) { - struct orion_wdt_platform_data *pdata = pdev->dev.platform_data; - int ret; + struct device_node *node = pdev->dev.of_node; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + dev->reg = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!dev->reg) + return -ENOMEM; + + /* Each supported compatible has some RSTOUT register quirk */ + if (of_device_is_compatible(node, "marvell,orion-wdt")) { + + dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start & + INTERNAL_REGS_MASK); + if (!dev->rstout) + return -ENODEV; + + } else if (of_device_is_compatible(node, "marvell,armada-370-wdt") || + of_device_is_compatible(node, "marvell,armada-xp-wdt")) { + + /* Dedicated RSTOUT register, can be requested. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dev->rstout = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->rstout)) + return PTR_ERR(dev->rstout); + + } else if (of_device_is_compatible(node, "marvell,armada-375-wdt") || + of_device_is_compatible(node, "marvell,armada-380-wdt")) { + + /* Dedicated RSTOUT register, can be requested. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dev->rstout = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->rstout)) + return PTR_ERR(dev->rstout); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) + return -ENODEV; + dev->rstout_mask = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!dev->rstout_mask) + return -ENOMEM; - 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) +static int orion_wdt_probe(struct platform_device *pdev) { - int ret; + struct orion_watchdog *dev; + const struct of_device_id *match; + unsigned int wdt_max_duration; /* (seconds) */ + int ret, irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog), + GFP_KERNEL); + if (!dev) + return -ENOMEM; + + match = of_match_device(orion_wdt_of_match_table, &pdev->dev); + if (!match) + /* Default legacy match */ + match = &orion_wdt_of_match_table[0]; + + dev->wdt.info = &orion_wdt_info; + dev->wdt.ops = &orion_wdt_ops; + dev->wdt.min_timeout = 1; + dev->data = match->data; + + ret = orion_wdt_get_regs(pdev, dev); + if (ret) + return ret; + + ret = dev->data->clock_init(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "cannot initialize clock\n"); + return ret; + } - if (test_bit(WDT_IN_USE, &wdt_status)) { - orion_wdt_disable(); - clear_bit(WDT_IN_USE, &wdt_status); + wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate; + + dev->wdt.timeout = wdt_max_duration; + dev->wdt.max_timeout = wdt_max_duration; + watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev); + + platform_set_drvdata(pdev, &dev->wdt); + watchdog_set_drvdata(&dev->wdt, dev); + + /* + * Let's make sure the watchdog is fully stopped, unless it's + * explicitly enabled. This may be the case if the module was + * removed and re-insterted, or if the bootloader explicitly + * set a running watchdog before booting the kernel. + */ + if (!orion_wdt_enabled(&dev->wdt)) + orion_wdt_stop(&dev->wdt); + + /* Request the IRQ only after the watchdog is disabled */ + irq = platform_get_irq(pdev, 0); + if (irq > 0) { + /* + * Not all supported platforms specify an interrupt for the + * watchdog, so let's make it optional. + */ + ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0, + pdev->name, dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto disable_clk; + } } - ret = misc_deregister(&orion_wdt_miscdev); - if (!ret) - orion_wdt_miscdev.parent = NULL; + watchdog_set_nowayout(&dev->wdt, nowayout); + ret = watchdog_register_device(&dev->wdt); + if (ret) + goto disable_clk; + pr_info("Initial timeout %d sec%s\n", + dev->wdt.timeout, nowayout ? ", nowayout" : ""); + return 0; + +disable_clk: + clk_disable_unprepare(dev->clk); + clk_put(dev->clk); return ret; } +static int orion_wdt_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdt_dev = platform_get_drvdata(pdev); + struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); + + watchdog_unregister_device(wdt_dev); + clk_disable_unprepare(dev->clk); + clk_put(dev->clk); + return 0; +} + static void orion_wdt_shutdown(struct platform_device *pdev) { - if (test_bit(WDT_IN_USE, &wdt_status)) - orion_wdt_disable(); + struct watchdog_device *wdt_dev = platform_get_drvdata(pdev); + orion_wdt_stop(wdt_dev); } static struct platform_driver orion_wdt_driver = { .probe = orion_wdt_probe, - .remove = __devexit_p(orion_wdt_remove), + .remove = orion_wdt_remove, .shutdown = orion_wdt_shutdown, .driver = { .owner = THIS_MODULE, .name = "orion_wdt", + .of_match_table = orion_wdt_of_match_table, }, }; @@ -302,9 +607,9 @@ 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_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS("platform:orion_wdt"); diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index e78d8998676..9f15dd9435d 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -18,6 +18,8 @@ * Release 1.1 */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/types.h> #include <linux/miscdevice.h> @@ -33,7 +35,6 @@ #include <linux/io.h> #include <linux/uaccess.h> -#include <asm/system.h> /* #define DEBUG 1 */ @@ -42,7 +43,6 @@ #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) */ @@ -65,7 +65,7 @@ static char expect_close; /* is the close expected? */ static DEFINE_SPINLOCK(io_lock); /* to guard us from io races */ -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; /* -- Low level function ----------------------------------------*/ @@ -87,7 +87,7 @@ static inline void pc87413_select_wdt_out(void) outb_p(cr_data, WDT_DATA_IO_PORT); #ifdef DEBUG - printk(KERN_INFO DPFX + pr_info(DPFX "Select multiple pin,pin55,as WDT output: Bit7 to 1: %d\n", cr_data); #endif @@ -111,7 +111,7 @@ static inline void pc87413_enable_swc(void) outb_p(cr_data, WDT_DATA_IO_PORT); /* Index0x30_bit0P1 */ #ifdef DEBUG - printk(KERN_INFO DPFX "pc87413 - Enable SWC functions\n"); + pr_info(DPFX "pc87413 - Enable SWC functions\n"); #endif } @@ -132,7 +132,7 @@ static void pc87413_get_swc_base_addr(void) swc_base_addr = (addr_h << 8) + addr_l; #ifdef DEBUG - printk(KERN_INFO DPFX + pr_info(DPFX "Read SWC I/O Base Address: low %d, high %d, res %d\n", addr_l, addr_h, swc_base_addr); #endif @@ -145,7 +145,7 @@ static inline void pc87413_swc_bank3(void) /* 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"); + pr_info(DPFX "Select Bank3 of SWC\n"); #endif } @@ -156,7 +156,7 @@ static inline void pc87413_programm_wdto(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); + pr_info(DPFX "Set WDTO to %d minutes\n", pc87413_time); #endif } @@ -167,7 +167,7 @@ static inline void pc87413_enable_wden(void) /* 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"); + pr_info(DPFX "Enable WDEN\n"); #endif } @@ -177,7 +177,7 @@ static inline void pc87413_enable_sw_wd_tren(void) /* 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"); + pr_info(DPFX "Enable SW_WD_TREN\n"); #endif } @@ -188,7 +188,7 @@ static inline void pc87413_disable_sw_wd_tren(void) /* 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"); + pr_info(DPFX "pc87413 - Disable SW_WD_TREN\n"); #endif } @@ -199,7 +199,7 @@ static inline void pc87413_enable_sw_wd_trg(void) /* 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"); + pr_info(DPFX "pc87413 - Enable SW_WD_TRG\n"); #endif } @@ -210,7 +210,7 @@ static inline void pc87413_disable_sw_wd_trg(void) /* 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"); + pr_info(DPFX "Disable SW_WD_TRG\n"); #endif } @@ -284,8 +284,7 @@ static int pc87413_open(struct inode *inode, struct file *file) /* Reload and activate timer */ pc87413_refresh(); - printk(KERN_INFO MODNAME - "Watchdog enabled. Timeout set to %d minute(s).\n", timeout); + pr_info("Watchdog enabled. Timeout set to %d minute(s).\n", timeout); return nonseekable_open(inode, file); } @@ -308,11 +307,9 @@ static int pc87413_release(struct inode *inode, struct file *file) if (expect_close == 42) { pc87413_disable(); - printk(KERN_INFO MODNAME - "Watchdog disabled, sleeping again...\n"); + pr_info("Watchdog disabled, sleeping again...\n"); } else { - printk(KERN_CRIT MODNAME - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); pc87413_refresh(); } clear_bit(0, &timer_enabled); @@ -428,7 +425,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd, case WDIOC_KEEPALIVE: pc87413_refresh(); #ifdef DEBUG - printk(KERN_INFO DPFX "keepalive\n"); + pr_info(DPFX "keepalive\n"); #endif return 0; case WDIOC_SETTIMEOUT: @@ -508,34 +505,30 @@ static int __init pc87413_init(void) { int ret; - printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n", + pr_info("Version " VERSION " at io 0x%X\n", WDT_INDEX_IO_PORT); if (!request_muxed_region(io, 2, MODNAME)) return -EBUSY; ret = register_reboot_notifier(&pc87413_notifier); - if (ret != 0) { - printk(KERN_ERR PFX - "cannot register reboot notifier (err=%d)\n", ret); - } + if (ret != 0) + pr_err("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); + pr_err("cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); goto reboot_unreg; } - printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout); + pr_info("initialized. timeout=%d min\n", timeout); pc87413_select_wdt_out(); pc87413_enable_swc(); pc87413_get_swc_base_addr(); if (!request_region(swc_base_addr, 0x20, MODNAME)) { - printk(KERN_ERR PFX - "cannot request SWC region at 0x%x\n", swc_base_addr); + pr_err("cannot request SWC region at 0x%x\n", swc_base_addr); ret = -EBUSY; goto misc_unreg; } @@ -568,26 +561,24 @@ static void __exit pc87413_exit(void) /* Stop the timer before we leave */ if (!nowayout) { pc87413_disable(); - printk(KERN_INFO MODNAME "Watchdog disabled.\n"); + pr_info("Watchdog disabled\n"); } misc_deregister(&pc87413_miscdev); unregister_reboot_notifier(&pc87413_notifier); release_region(swc_base_addr, 0x20); - printk(KERN_INFO MODNAME " watchdog component driver removed.\n"); + pr_info("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_AUTHOR("Sven Anders <anders@anduras.de>"); +MODULE_AUTHOR("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) ")."); @@ -597,7 +588,7 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in minutes (default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")."); -module_param(nowayout, int, 0); +module_param(nowayout, bool, 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 index 06f7922606c..e936f15dc7c 100644 --- a/drivers/watchdog/pcwd.c +++ b/drivers/watchdog/pcwd.c @@ -51,6 +51,8 @@ * http://www.pcwatchdog.com/ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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) */ @@ -59,7 +61,7 @@ #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/miscdevice.h> /* For struct miscdevice */ #include <linux/watchdog.h> /* For the watchdog specific items */ #include <linux/reboot.h> /* For kernel_power_off() */ #include <linux/init.h> /* For __init/__exit/... */ @@ -75,7 +77,6 @@ #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" /* @@ -203,8 +204,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -220,8 +221,7 @@ static int send_isa_command(int cmd) int port0, last_port0; /* Double read for stabilising */ if (debug >= DEBUG) - printk(KERN_DEBUG PFX "sending following data cmd=0x%02x\n", - cmd); + pr_debug("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; @@ -240,9 +240,8 @@ static int send_isa_command(int cmd) } 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); + pr_debug("received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n", + cmd, port0, last_port0); return port0; } @@ -271,8 +270,7 @@ static int set_command_mode(void) pcwd_private.command_mode = found; if (debug >= DEBUG) - printk(KERN_DEBUG PFX "command_mode=%d\n", - pcwd_private.command_mode); + pr_debug("command_mode=%d\n", pcwd_private.command_mode); return found; } @@ -288,8 +286,7 @@ static void unset_command_mode(void) pcwd_private.command_mode = 0; if (debug >= DEBUG) - printk(KERN_DEBUG PFX "command_mode=%d\n", - pcwd_private.command_mode); + pr_debug("command_mode=%d\n", pcwd_private.command_mode); } static inline void pcwd_check_temperature_support(void) @@ -336,17 +333,14 @@ static void pcwd_show_card_info(void) /* 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); + pr_info("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", + pr_info("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", + pr_info("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")); @@ -359,22 +353,18 @@ static void pcwd_show_card_info(void) } if (pcwd_private.supports_temp) - printk(KERN_INFO PFX "Temperature Option Detected\n"); + pr_info("Temperature Option Detected\n"); if (pcwd_private.boot_status & WDIOF_CARDRESET) - printk(KERN_INFO PFX - "Previous reboot was caused by the card\n"); + pr_info("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"); + pr_emerg("Card senses a CPU Overheat. Panicking!\n"); + pr_emerg("CPU Overheat\n"); } if (pcwd_private.boot_status == 0) - printk(KERN_INFO PFX - "No previous trip detected - Cold boot or reset\n"); + pr_info("No previous trip detected - Cold boot or reset\n"); } static void pcwd_timer_ping(unsigned long data) @@ -404,8 +394,7 @@ static void pcwd_timer_ping(unsigned long data) spin_unlock(&pcwd_private.io_lock); } else { - printk(KERN_WARNING PFX - "Heartbeat lost! Will not ping the watchdog\n"); + pr_warn("Heartbeat lost! Will not ping the watchdog\n"); } } @@ -426,13 +415,13 @@ static int pcwd_start(void) 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"); + pr_info("Could not start watchdog\n"); return -EIO; } } if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Watchdog started\n"); + pr_debug("Watchdog started\n"); return 0; } @@ -454,13 +443,13 @@ static int pcwd_stop(void) 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"); + pr_info("Could not stop watchdog\n"); return -EIO; } } if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Watchdog stopped\n"); + pr_debug("Watchdog stopped\n"); return 0; } @@ -471,7 +460,7 @@ static int pcwd_keepalive(void) pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); if (debug >= DEBUG) - printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n"); + pr_debug("Watchdog keepalive signal send\n"); return 0; } @@ -484,8 +473,7 @@ static int pcwd_set_heartbeat(int t) heartbeat = t; if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "New heartbeat: %d\n", - heartbeat); + pr_debug("New heartbeat: %d\n", heartbeat); return 0; } @@ -518,8 +506,7 @@ static int pcwd_get_status(int *status) if (control_status & WD_T110) { *status |= WDIOF_OVERHEAT; if (temp_panic) { - printk(KERN_INFO PFX - "Temperature overheat trip!\n"); + pr_info("Temperature overheat trip!\n"); kernel_power_off(); } } @@ -530,8 +517,7 @@ static int pcwd_get_status(int *status) if (control_status & WD_REVC_TTRP) { *status |= WDIOF_OVERHEAT; if (temp_panic) { - printk(KERN_INFO PFX - "Temperature overheat trip!\n"); + pr_info("Temperature overheat trip!\n"); kernel_power_off(); } } @@ -548,16 +534,14 @@ static int pcwd_clear_status(void) spin_lock(&pcwd_private.io_lock); if (debug >= VERBOSE) - printk(KERN_INFO PFX - "clearing watchdog trip status\n"); + pr_info("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)); + pr_debug("status was: 0x%02x\n", control_status); + pr_debug("sending: 0x%02x\n", + (control_status & WD_REVC_R2DS)); } /* clear reset status & Keep Relay 2 disable state as it is */ @@ -588,8 +572,7 @@ static int pcwd_get_temperature(int *temperature) spin_unlock(&pcwd_private.io_lock); if (debug >= DEBUG) { - printk(KERN_DEBUG PFX "temperature is: %d F\n", - *temperature); + pr_debug("temperature is: %d F\n", *temperature); } return 0; @@ -720,8 +703,7 @@ 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"); + pr_crit("Unexpected close, not stopping watchdog!\n"); pcwd_keepalive(); } expect_close = 0; @@ -819,7 +801,7 @@ static inline int get_revision(void) * 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) +static int 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 */ @@ -828,11 +810,10 @@ static int __devinit pcwd_isa_match(struct device *dev, unsigned int id) int retval; if (debug >= DEBUG) - printk(KERN_DEBUG PFX "pcwd_isa_match id=%d\n", - id); + pr_debug("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); + pr_info("Port 0x%04x unavailable\n", base_addr); return 0; } @@ -865,26 +846,25 @@ static int __devinit pcwd_isa_match(struct device *dev, unsigned int id) return retval; } -static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id) +static int 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); + pr_debug("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", + pr_info("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"); + pr_err("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"); + pr_err("No I/O-Address for card detected\n"); return -ENODEV; } pcwd_private.io_addr = pcwd_ioports[id]; @@ -896,8 +876,8 @@ static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id) 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); + pr_err("I/O address 0x%04x already in use\n", + pcwd_private.io_addr); ret = -EIO; goto error_request_region; } @@ -932,30 +912,27 @@ static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id) 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); + pr_info("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); + pr_err("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); + pr_err("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", + pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); return 0; @@ -972,11 +949,10 @@ error_request_region: return ret; } -static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id) +static int pcwd_isa_remove(struct device *dev, unsigned int id) { if (debug >= DEBUG) - printk(KERN_DEBUG PFX "pcwd_isa_remove id=%d\n", - id); + pr_debug("pcwd_isa_remove id=%d\n", id); if (!pcwd_private.io_addr) return 1; @@ -1000,8 +976,7 @@ static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id) 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); + pr_debug("pcwd_isa_shutdown id=%d\n", id); pcwd_stop(); } @@ -1009,7 +984,7 @@ static void pcwd_isa_shutdown(struct device *dev, unsigned int id) static struct isa_driver pcwd_isa_driver = { .match = pcwd_isa_match, .probe = pcwd_isa_probe, - .remove = __devexit_p(pcwd_isa_remove), + .remove = pcwd_isa_remove, .shutdown = pcwd_isa_shutdown, .driver = { .owner = THIS_MODULE, @@ -1025,7 +1000,7 @@ static int __init pcwd_init_module(void) static void __exit pcwd_cleanup_module(void) { isa_unregister_driver(&pcwd_isa_driver); - printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); + pr_info("Watchdog Module Unloaded\n"); } module_init(pcwd_init_module); @@ -1036,5 +1011,3 @@ MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, " 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 index b8d14f88f0b..c0d07eef264 100644 --- a/drivers/watchdog/pcwd_pci.c +++ b/drivers/watchdog/pcwd_pci.c @@ -32,13 +32,15 @@ * Includes, defines, variables, module parameters, ... */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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/miscdevice.h> /* For struct miscdevice */ #include <linux/watchdog.h> /* For the watchdog specific items */ #include <linux/notifier.h> /* For notifier support */ #include <linux/reboot.h> /* For reboot_notifier stuff */ @@ -54,8 +56,7 @@ #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" +#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION /* Stuff for the PCI ID's */ #ifndef PCI_VENDOR_ID_QUICKLOGIC @@ -145,8 +146,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -159,8 +160,8 @@ 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); + pr_debug("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. @@ -185,12 +186,10 @@ static int send_command(int cmd, int *msb, int *lsb) if (debug >= DEBUG) { if (got_response) { - printk(KERN_DEBUG PFX - "time to process command was: %d ms\n", - count); + pr_debug("time to process command was: %d ms\n", + count); } else { - printk(KERN_DEBUG PFX - "card did not respond on command!\n"); + pr_debug("card did not respond on command!\n"); } } @@ -203,9 +202,8 @@ static int send_command(int cmd, int *msb, int *lsb) 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); + pr_debug("received following data for cmd=0x%02x: msb=0x%02x lsb=0x%02x\n", + cmd, *msb, *lsb); } spin_unlock(&pcipcwd_private.io_lock); @@ -243,27 +241,23 @@ static void pcipcwd_show_card_info(void) /* 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", + pr_info("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", + pr_info("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"); + pr_info("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"); + pr_info("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"); + pr_info("No previous trip detected - Cold boot or reset\n"); } static int pcipcwd_start(void) @@ -278,12 +272,12 @@ static int pcipcwd_start(void) spin_unlock(&pcipcwd_private.io_lock); if (stat_reg & WD_PCI_WDIS) { - printk(KERN_ERR PFX "Card timer not enabled\n"); + pr_err("Card timer not enabled\n"); return -1; } if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Watchdog started\n"); + pr_debug("Watchdog started\n"); return 0; } @@ -303,13 +297,12 @@ static int pcipcwd_stop(void) spin_unlock(&pcipcwd_private.io_lock); if (!(stat_reg & WD_PCI_WDIS)) { - printk(KERN_ERR PFX - "Card did not acknowledge disable attempt\n"); + pr_err("Card did not acknowledge disable attempt\n"); return -1; } if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Watchdog stopped\n"); + pr_debug("Watchdog stopped\n"); return 0; } @@ -322,7 +315,7 @@ static int pcipcwd_keepalive(void) spin_unlock(&pcipcwd_private.io_lock); if (debug >= DEBUG) - printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n"); + pr_debug("Watchdog keepalive signal send\n"); return 0; } @@ -340,8 +333,7 @@ static int pcipcwd_set_heartbeat(int t) heartbeat = t; if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "New heartbeat: %d\n", - heartbeat); + pr_debug("New heartbeat: %d\n", heartbeat); return 0; } @@ -357,12 +349,11 @@ static int pcipcwd_get_status(int *status) if (control_status & WD_PCI_TTRP) { *status |= WDIOF_OVERHEAT; if (temp_panic) - panic(PFX "Temperature overheat trip!\n"); + panic(KBUILD_MODNAME ": Temperature overheat trip!\n"); } if (debug >= DEBUG) - printk(KERN_DEBUG PFX "Control Status #1: 0x%02x\n", - control_status); + pr_debug("Control Status #1: 0x%02x\n", control_status); return 0; } @@ -374,14 +365,14 @@ static int pcipcwd_clear_status(void) int reset_counter; if (debug >= VERBOSE) - printk(KERN_INFO PFX "clearing watchdog trip status & LED\n"); + pr_info("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); + pr_debug("status was: 0x%02x\n", control_status); + pr_debug("sending: 0x%02x\n", + (control_status & WD_PCI_R2DS) | WD_PCI_WTRP); } /* clear trip status & LED and keep mode of relay 2 */ @@ -394,8 +385,7 @@ static int pcipcwd_clear_status(void) 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); + pr_debug("reset count was: 0x%02x\n", reset_counter); } return 0; @@ -418,8 +408,7 @@ static int pcipcwd_get_temperature(int *temperature) *temperature = (*temperature * 9 / 5) + 32; if (debug >= DEBUG) { - printk(KERN_DEBUG PFX "temperature is: %d F\n", - *temperature); + pr_debug("temperature is: %d F\n", *temperature); } return 0; @@ -437,8 +426,7 @@ static int pcipcwd_get_timeleft(int *time_left) *time_left = (msb << 8) + lsb; if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Time left before next reboot: %d\n", - *time_left); + pr_debug("Time left before next reboot: %d\n", *time_left); return 0; } @@ -583,8 +571,7 @@ 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"); + pr_err("Attempt to open already opened device\n"); return -EBUSY; } @@ -602,8 +589,7 @@ static int pcipcwd_release(struct inode *inode, struct file *file) if (expect_release == 42) { pcipcwd_stop(); } else { - printk(KERN_CRIT PFX - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); pcipcwd_keepalive(); } expect_release = 0; @@ -696,37 +682,38 @@ static struct notifier_block pcipcwd_notifier = { * Init & exit routines */ -static int __devinit pcipcwd_card_init(struct pci_dev *pdev, +static int 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); + pr_info("%s\n", DRIVER_VERSION); if (cards_found > 1) { - printk(KERN_ERR PFX "This driver only supports 1 device\n"); + pr_err("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"); + pr_err("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"); + pr_err("No I/O-Address for card detected\n"); ret = -ENODEV; goto err_out_disable_device; } + spin_lock_init(&pcipcwd_private.io_lock); 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); + pr_err("I/O address 0x%04x already in use\n", + (int) pcipcwd_private.io_addr); ret = -EIO; goto err_out_disable_device; } @@ -755,36 +742,33 @@ static int __devinit pcipcwd_card_init(struct pci_dev *pdev, * 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", + pr_info("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); + pr_err("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); + pr_err("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); + pr_err("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", + pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); return 0; @@ -801,7 +785,7 @@ err_out_disable_device: return ret; } -static void __devexit pcipcwd_card_exit(struct pci_dev *pdev) +static void pcipcwd_card_exit(struct pci_dev *pdev) { /* Stop the timer before we leave */ if (!nowayout) @@ -817,7 +801,7 @@ static void __devexit pcipcwd_card_exit(struct pci_dev *pdev) cards_found--; } -static DEFINE_PCI_DEVICE_TABLE(pcipcwd_pci_tbl) = { +static const struct pci_device_id pcipcwd_pci_tbl[] = { { PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_WATCHDOG_PCIPCWD, PCI_ANY_ID, PCI_ANY_ID, }, { 0 }, /* End of list */ @@ -828,28 +812,11 @@ static struct pci_driver pcipcwd_driver = { .name = WATCHDOG_NAME, .id_table = pcipcwd_pci_tbl, .probe = pcipcwd_card_init, - .remove = __devexit_p(pcipcwd_card_exit), + .remove = 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_pci_driver(pcipcwd_driver); 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 index d8de1ddd176..1a11aedc4fe 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -24,13 +24,15 @@ * http://www.berkprod.com/ or http://www.pcwatchdog.com/ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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/miscdevice.h> /* For struct miscdevice */ #include <linux/watchdog.h> /* For the watchdog specific items */ #include <linux/notifier.h> /* For notifier support */ #include <linux/reboot.h> /* For reboot_notifier stuff */ @@ -43,34 +45,16 @@ #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 */ @@ -80,8 +64,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -188,6 +172,7 @@ 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; + struct device *dev = &usb_pcwd->interface->dev; int retval; switch (urb->status) { @@ -197,17 +182,17 @@ static void usb_pcwd_intr_done(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, - urb->status); + dev_dbg(dev, "%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); + dev_dbg(dev, "%s - nonzero urb status received: %d", + __func__, urb->status); goto resubmit; } - dbg("received following data cmd=0x%02x msb=0x%02x lsb=0x%02x", + dev_dbg(dev, "received following data cmd=0x%02x msb=0x%02x lsb=0x%02x", data[0], data[1], data[2]); usb_pcwd->cmd_command = data[0]; @@ -220,21 +205,25 @@ static void usb_pcwd_intr_done(struct urb *urb) 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); + pr_err("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]; + unsigned char *buf; /* We will not send any commands if the USB PCWD device does * not exist */ if ((!usb_pcwd) || (!usb_pcwd->exists)) return -1; + buf = kmalloc(6, GFP_KERNEL); + if (buf == NULL) + return 0; + /* 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 */ @@ -242,17 +231,19 @@ static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd, 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", + dev_dbg(&usb_pcwd->interface->dev, + "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); + 0x0200, usb_pcwd->interface_number, buf, 6, + USB_COMMAND_TIMEOUT) != 6) { + dev_dbg(&usb_pcwd->interface->dev, + "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 */ @@ -270,6 +261,8 @@ static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd, *lsb = usb_pcwd->cmd_data_lsb; } + kfree(buf); + return got_response; } @@ -284,8 +277,7 @@ static int usb_pcwd_start(struct usb_pcwd_private *usb_pcwd) &msb, &lsb); if ((retval == 0) || (lsb == 0)) { - printk(KERN_ERR PFX - "Card did not acknowledge enable attempt\n"); + pr_err("Card did not acknowledge enable attempt\n"); return -1; } @@ -303,8 +295,7 @@ static int usb_pcwd_stop(struct usb_pcwd_private *usb_pcwd) &msb, &lsb); if ((retval == 0) || (lsb != 0)) { - printk(KERN_ERR PFX - "Card did not acknowledge disable attempt\n"); + pr_err("Card did not acknowledge disable attempt\n"); return -1; } @@ -506,8 +497,7 @@ static int usb_pcwd_release(struct inode *inode, struct file *file) if (expect_release == 42) { usb_pcwd_stop(usb_pcwd_device); } else { - printk(KERN_CRIT PFX - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); usb_pcwd_keepalive(usb_pcwd_device); } expect_release = 0; @@ -627,7 +617,7 @@ static int usb_pcwd_probe(struct usb_interface *interface, cards_found++; if (cards_found > 1) { - printk(KERN_ERR PFX "This driver only supports 1 device\n"); + pr_err("This driver only supports 1 device\n"); return -ENODEV; } @@ -636,8 +626,7 @@ static int usb_pcwd_probe(struct usb_interface *interface, /* 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"); + pr_err("The device isn't a Human Interface Device\n"); return -ENODEV; } @@ -646,7 +635,7 @@ static int usb_pcwd_probe(struct usb_interface *interface, 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"); + pr_err("Couldn't find an INTR & IN endpoint\n"); return -ENODEV; } @@ -656,10 +645,8 @@ static int usb_pcwd_probe(struct usb_interface *interface, /* 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"); + if (usb_pcwd == NULL) goto error; - } usb_pcwd_device = usb_pcwd; @@ -674,14 +661,14 @@ static int usb_pcwd_probe(struct usb_interface *interface, 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"); + pr_err("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"); + pr_err("Out of memory\n"); goto error; } @@ -694,7 +681,7 @@ static int usb_pcwd_probe(struct usb_interface *interface, /* 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"); + pr_err("Problem registering interrupt URB\n"); retval = -EIO; /* failure */ goto error; } @@ -713,15 +700,13 @@ static int usb_pcwd_probe(struct usb_interface *interface, else sprintf(fw_ver_str, "<card no answer>"); - printk(KERN_INFO PFX "Found card (Firmware: %s) with temp option\n", - fw_ver_str); + pr_info("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", + pr_info("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")); @@ -734,39 +719,34 @@ static int usb_pcwd_probe(struct usb_interface *interface, * 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", + pr_info("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); + pr_err("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); + pr_err("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); + pr_err("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", + pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); return 0; @@ -824,7 +804,7 @@ static void usb_pcwd_disconnect(struct usb_interface *interface) mutex_unlock(&disconnect_mutex); - printk(KERN_INFO PFX "USB PC Watchdog disconnected\n"); + pr_info("USB PC Watchdog disconnected\n"); } module_usb_driver(usb_pcwd_driver); diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c index 2d22e996e99..0cdfee26669 100644 --- a/drivers/watchdog/pika_wdt.c +++ b/drivers/watchdog/pika_wdt.c @@ -5,6 +5,8 @@ * Sean MacLennan <smaclennan@pikatech.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/errno.h> #include <linux/module.h> @@ -20,10 +22,10 @@ #include <linux/bitops.h> #include <linux/uaccess.h> #include <linux/io.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #define DRV_NAME "PIKA-WDT" -#define PFX DRV_NAME ": " /* Hardware timeout in seconds */ #define WDT_HW_TIMEOUT 2 @@ -38,8 +40,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -90,7 +92,7 @@ static void pikawdt_ping(unsigned long data) pikawdt_reset(); mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT); } else - printk(KERN_CRIT PFX "I will reset your machine !\n"); + pr_crit("I will reset your machine !\n"); } @@ -228,14 +230,14 @@ static int __init pikawdt_init(void) np = of_find_compatible_node(NULL, NULL, "pika,fpga"); if (np == NULL) { - printk(KERN_ERR PFX "Unable to find fpga.\n"); + pr_err("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"); + pr_err("Unable to map fpga\n"); return -ENOMEM; } @@ -244,7 +246,7 @@ static int __init pikawdt_init(void) /* 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"); + pr_err("Unable to find fpga-sd\n"); ret = -ENOENT; goto out; } @@ -252,7 +254,7 @@ static int __init pikawdt_init(void) fpga = of_iomap(np, 0); of_node_put(np); if (fpga == NULL) { - printk(KERN_ERR PFX "Unable to map fpga-sd.\n"); + pr_err("Unable to map fpga-sd\n"); ret = -ENOMEM; goto out; } @@ -271,12 +273,12 @@ static int __init pikawdt_init(void) ret = misc_register(&pikawdt_miscdev); if (ret) { - printk(KERN_ERR PFX "Unable to register miscdev.\n"); + pr_err("Unable to register miscdev\n"); goto out; } - printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", - heartbeat, nowayout); + pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", + heartbeat, nowayout); return 0; out: @@ -297,5 +299,3 @@ 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 index dfae030a7ef..15fb316e943 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -8,33 +8,31 @@ * 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. + * 2005-2006 (c) MontaVista Software, Inc. + * + * (C) 2012 Wolfram Sang, Pengutronix + * + * 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 ": " fmt + #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 <linux/err.h> +#include <linux/of.h> #include <mach/hardware.h> -#define MODULE_NAME "PNX4008-WDT: " - /* WatchDog Timer - Chapter 23 Page 207 */ #define DEFAULT_HEARTBEAT 19 @@ -77,278 +75,160 @@ #define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */ -static int nowayout = WATCHDOG_NOWAYOUT; -static int heartbeat = DEFAULT_HEARTBEAT; +static bool nowayout = WATCHDOG_NOWAYOUT; +static unsigned 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) +static int pnx4008_wdt_start(struct watchdog_device *wdd) { spin_lock(&io_lock); /* stop counter, initiate counter reset */ - __raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base)); + writel(RESET_COUNT, WDTIM_CTRL(wdt_base)); /*wait for reset to complete. 100% guarantee event */ - while (__raw_readl(WDTIM_COUNTER(wdt_base))) + while (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)); + writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0, WDTIM_MCTRL(wdt_base)); /* configure match output */ - __raw_writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base)); + writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base)); /* clear interrupt, just in case */ - __raw_writel(MATCH_INT, WDTIM_INT(wdt_base)); + 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)); + writel(0xFFFF, WDTIM_PULSE(wdt_base)); + writel(wdd->timeout * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base)); /*enable counter, stop when debugger active */ - __raw_writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base)); + writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base)); spin_unlock(&io_lock); + return 0; } -static void wdt_disable(void) +static int pnx4008_wdt_stop(struct watchdog_device *wdd) { spin_lock(&io_lock); - __raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */ + writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */ spin_unlock(&io_lock); + return 0; } -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) +static int pnx4008_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int new_timeout) { - 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; + wdd->timeout = new_timeout; + return 0; } -static const struct watchdog_info ident = { +static const struct watchdog_info pnx4008_wdt_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 unexpectedly\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 = { +static const struct watchdog_ops pnx4008_wdt_ops = { .owner = THIS_MODULE, - .llseek = no_llseek, - .write = pnx4008_wdt_write, - .unlocked_ioctl = pnx4008_wdt_ioctl, - .open = pnx4008_wdt_open, - .release = pnx4008_wdt_release, + .start = pnx4008_wdt_start, + .stop = pnx4008_wdt_stop, + .set_timeout = pnx4008_wdt_set_timeout, }; -static struct miscdevice pnx4008_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &pnx4008_wdt_fops, +static struct watchdog_device pnx4008_wdd = { + .info = &pnx4008_wdt_ident, + .ops = &pnx4008_wdt_ops, + .timeout = DEFAULT_HEARTBEAT, + .min_timeout = 1, + .max_timeout = MAX_HEARTBEAT, }; -static int __devinit pnx4008_wdt_probe(struct platform_device *pdev) +static int pnx4008_wdt_probe(struct platform_device *pdev) { - int ret = 0, size; + struct resource *r; + int ret = 0; - if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) - heartbeat = DEFAULT_HEARTBEAT; + watchdog_init_timeout(&pnx4008_wdd, heartbeat, &pdev->dev); - printk(KERN_INFO MODULE_NAME - "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(wdt_base)) + return PTR_ERR(wdt_base); - wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (wdt_mem == NULL) { - printk(KERN_INFO MODULE_NAME - "failed to get memory region resource\n"); - return -ENOENT; - } + wdt_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt_clk)) + return PTR_ERR(wdt_clk); - size = resource_size(wdt_mem); + ret = clk_enable(wdt_clk); + if (ret) + return ret; - 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; - } + pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ? + WDIOF_CARDRESET : 0; + watchdog_set_nowayout(&pnx4008_wdd, nowayout); - ret = clk_enable(wdt_clk); - if (ret) { - release_mem_region(wdt_mem->start, size); - wdt_mem = NULL; - clk_put(wdt_clk); - goto out; - } + pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */ - ret = misc_register(&pnx4008_wdt_miscdev); + ret = watchdog_register_device(&pnx4008_wdd); 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); + dev_err(&pdev->dev, "cannot register watchdog device\n"); + goto disable_clk; } -out: + dev_info(&pdev->dev, "PNX4008 Watchdog Timer: heartbeat %d sec\n", + pnx4008_wdd.timeout); + + return 0; + +disable_clk: + clk_disable(wdt_clk); return ret; } -static int __devexit pnx4008_wdt_remove(struct platform_device *pdev) +static int pnx4008_wdt_remove(struct platform_device *pdev) { - misc_deregister(&pnx4008_wdt_miscdev); + watchdog_unregister_device(&pnx4008_wdd); 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; } +#ifdef CONFIG_OF +static const struct of_device_id pnx4008_wdt_match[] = { + { .compatible = "nxp,pnx4008-wdt" }, + { } +}; +MODULE_DEVICE_TABLE(of, pnx4008_wdt_match); +#endif + static struct platform_driver platform_wdt_driver = { .driver = { .name = "pnx4008-watchdog", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pnx4008_wdt_match), }, .probe = pnx4008_wdt_probe, - .remove = __devexit_p(pnx4008_wdt_remove), + .remove = pnx4008_wdt_remove, }; module_platform_driver(platform_wdt_driver); MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); +MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>"); MODULE_DESCRIPTION("PNX4008 Watchdog Driver"); -module_param(heartbeat, int, 0); +module_param(heartbeat, uint, 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_param(nowayout, bool, 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 index a7b5ad2a98b..882fdcb46ad 100644 --- a/drivers/watchdog/pnx833x_wdt.c +++ b/drivers/watchdog/pnx833x_wdt.c @@ -17,6 +17,8 @@ * based on softdog.c by Alan Cox <alan@redhat.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -30,7 +32,6 @@ #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) @@ -54,8 +55,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -76,7 +77,7 @@ static void pnx833x_wdt_start(void) PNX833X_REG(PNX833X_CONFIG + PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1; - printk(KERN_INFO PFX "Started watchdog timer.\n"); + pr_info("Started watchdog timer\n"); } static void pnx833x_wdt_stop(void) @@ -87,7 +88,7 @@ static void pnx833x_wdt_stop(void) PNX833X_REG(PNX833X_CONFIG + PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE; - printk(KERN_INFO PFX "Stopped watchdog timer.\n"); + pr_info("Stopped watchdog timer\n"); } static void pnx833x_wdt_ping(void) @@ -113,7 +114,7 @@ static int pnx833x_wdt_open(struct inode *inode, struct file *file) pnx833x_wdt_ping(); - printk(KERN_INFO "Started watchdog timer.\n"); + pr_info("Started watchdog timer\n"); return nonseekable_open(inode, file); } @@ -232,9 +233,6 @@ 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; @@ -243,27 +241,25 @@ static int __init watchdog_init(void) 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"); + pr_info("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); + pr_err("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); + pr_err("cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); unregister_reboot_notifier(&pnx833x_wdt_notifier); return ret; } - printk(banner); + pr_info("Hardware Watchdog Timer for PNX833x: Version 0.1\n"); + if (start_enabled) pnx833x_wdt_start(); @@ -282,4 +278,3 @@ 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 index bf7bc8aa1c0..71e78ef4b73 100644 --- a/drivers/watchdog/rc32434_wdt.c +++ b/drivers/watchdog/rc32434_wdt.c @@ -17,24 +17,24 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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/miscdevice.h> /* For struct miscdevice */ #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 <linux/io.h> /* For devm_ioremap_nocache */ #include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */ -#define PFX KBUILD_MODNAME ": " - #define VERSION "1.0" static struct { @@ -64,8 +64,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -78,8 +78,7 @@ 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); + pr_err("timeout value must be between 0 and %d\n", max_to); return -EINVAL; } timeout = new_timeout; @@ -119,7 +118,7 @@ static void rc32434_wdt_start(void) SET_BITS(wdt_reg->wtc, or, nand); spin_unlock(&rc32434_wdt_device.io_lock); - printk(KERN_INFO PFX "Started watchdog timer.\n"); + pr_info("Started watchdog timer\n"); } static void rc32434_wdt_stop(void) @@ -130,7 +129,7 @@ static void rc32434_wdt_stop(void) 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"); + pr_info("Stopped watchdog timer\n"); } static void rc32434_wdt_ping(void) @@ -160,8 +159,7 @@ static int rc32434_wdt_release(struct inode *inode, struct file *file) rc32434_wdt_stop(); module_put(THIS_MODULE); } else { - printk(KERN_CRIT PFX - "device closed unexpectedly. WDT will not stop!\n"); + pr_crit("device closed unexpectedly. WDT will not stop!\n"); rc32434_wdt_ping(); } clear_bit(0, &rc32434_wdt_device.inuse); @@ -262,23 +260,20 @@ static struct miscdevice rc32434_wdt_miscdev = { .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) +static int 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"); + pr_err("failed to retrieve resources\n"); return -ENODEV; } - wdt_reg = ioremap_nocache(r->start, resource_size(r)); + wdt_reg = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r)); if (!wdt_reg) { - printk(KERN_ERR PFX "failed to remap I/O resources\n"); + pr_err("failed to remap I/O resources\n"); return -ENXIO; } @@ -291,30 +286,25 @@ static int __devinit rc32434_wdt_probe(struct platform_device *pdev) * 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", + pr_info("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; + pr_err("failed to register watchdog device\n"); + return ret; } - printk(banner, timeout); + pr_info("Watchdog Timer version " VERSION ", timer margin: %d sec\n", + timeout); return 0; - -unmap: - iounmap(wdt_reg); - return ret; } -static int __devexit rc32434_wdt_remove(struct platform_device *pdev) +static int rc32434_wdt_remove(struct platform_device *pdev) { misc_deregister(&rc32434_wdt_miscdev); - iounmap(wdt_reg); return 0; } @@ -325,7 +315,7 @@ static void rc32434_wdt_shutdown(struct platform_device *pdev) static struct platform_driver rc32434_wdt_driver = { .probe = rc32434_wdt_probe, - .remove = __devexit_p(rc32434_wdt_remove), + .remove = rc32434_wdt_remove, .shutdown = rc32434_wdt_shutdown, .driver = { .name = "rc32434_wdt", @@ -338,4 +328,3 @@ 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 index 042ccc56ae2..29cf4dcbc59 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c @@ -27,7 +27,6 @@ #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> @@ -225,13 +224,13 @@ static struct miscdevice rdc321x_wdt_misc = { .fops = &rdc321x_wdt_fops, }; -static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) +static int rdc321x_wdt_probe(struct platform_device *pdev) { int err; struct resource *r; struct rdc321x_wdt_pdata *pdata; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no platform data supplied\n"); return -ENODEV; @@ -272,7 +271,7 @@ static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) return 0; } -static int __devexit rdc321x_wdt_remove(struct platform_device *pdev) +static int rdc321x_wdt_remove(struct platform_device *pdev) { if (rdc321x_wdt_device.queue) { rdc321x_wdt_device.queue = 0; @@ -286,7 +285,7 @@ static int __devexit rdc321x_wdt_remove(struct platform_device *pdev) static struct platform_driver rdc321x_wdt_driver = { .probe = rdc321x_wdt_probe, - .remove = __devexit_p(rdc321x_wdt_remove), + .remove = rdc321x_wdt_remove, .driver = { .owner = THIS_MODULE, .name = "rdc321x-wdt", @@ -298,4 +297,3 @@ module_platform_driver(rdc321x_wdt_driver); 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/retu_wdt.c b/drivers/watchdog/retu_wdt.c new file mode 100644 index 00000000000..a7a0695971e --- /dev/null +++ b/drivers/watchdog/retu_wdt.c @@ -0,0 +1,177 @@ +/* + * Retu watchdog driver + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Based on code written by Amit Kucheria and Michael Buesch. + * Rewritten by Aaro Koskinen. + * + * 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. + * + * 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. + */ + +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mfd/retu.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> + +/* Watchdog timer values in seconds */ +#define RETU_WDT_MAX_TIMER 63 + +struct retu_wdt_dev { + struct retu_dev *rdev; + struct device *dev; + struct delayed_work ping_work; +}; + +/* + * Since Retu watchdog cannot be disabled in hardware, we must kick it + * with a timer until userspace watchdog software takes over. If + * CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding. + */ +static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev) +{ + retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER); + schedule_delayed_work(&wdev->ping_work, + round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2)); +} + +static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev) +{ + retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER); + cancel_delayed_work_sync(&wdev->ping_work); +} + +static void retu_wdt_ping_work(struct work_struct *work) +{ + struct retu_wdt_dev *wdev = container_of(to_delayed_work(work), + struct retu_wdt_dev, ping_work); + retu_wdt_ping_enable(wdev); +} + +static int retu_wdt_start(struct watchdog_device *wdog) +{ + struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); + + retu_wdt_ping_disable(wdev); + + return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout); +} + +static int retu_wdt_stop(struct watchdog_device *wdog) +{ + struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); + + retu_wdt_ping_enable(wdev); + + return 0; +} + +static int retu_wdt_ping(struct watchdog_device *wdog) +{ + struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); + + return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout); +} + +static int retu_wdt_set_timeout(struct watchdog_device *wdog, + unsigned int timeout) +{ + struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); + + wdog->timeout = timeout; + return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout); +} + +static const struct watchdog_info retu_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "Retu watchdog", +}; + +static const struct watchdog_ops retu_wdt_ops = { + .owner = THIS_MODULE, + .start = retu_wdt_start, + .stop = retu_wdt_stop, + .ping = retu_wdt_ping, + .set_timeout = retu_wdt_set_timeout, +}; + +static int retu_wdt_probe(struct platform_device *pdev) +{ + struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); + bool nowayout = WATCHDOG_NOWAYOUT; + struct watchdog_device *retu_wdt; + struct retu_wdt_dev *wdev; + int ret; + + retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL); + if (!retu_wdt) + return -ENOMEM; + + wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) + return -ENOMEM; + + retu_wdt->info = &retu_wdt_info; + retu_wdt->ops = &retu_wdt_ops; + retu_wdt->timeout = RETU_WDT_MAX_TIMER; + retu_wdt->min_timeout = 0; + retu_wdt->max_timeout = RETU_WDT_MAX_TIMER; + + watchdog_set_drvdata(retu_wdt, wdev); + watchdog_set_nowayout(retu_wdt, nowayout); + + wdev->rdev = rdev; + wdev->dev = &pdev->dev; + + INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work); + + ret = watchdog_register_device(retu_wdt); + if (ret < 0) + return ret; + + if (nowayout) + retu_wdt_ping(retu_wdt); + else + retu_wdt_ping_enable(wdev); + + platform_set_drvdata(pdev, retu_wdt); + + return 0; +} + +static int retu_wdt_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdog = platform_get_drvdata(pdev); + struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); + + watchdog_unregister_device(wdog); + cancel_delayed_work_sync(&wdev->ping_work); + + return 0; +} + +static struct platform_driver retu_wdt_driver = { + .probe = retu_wdt_probe, + .remove = retu_wdt_remove, + .driver = { + .name = "retu-wdt", + }, +}; +module_platform_driver(retu_wdt_driver); + +MODULE_ALIAS("platform:retu-wdt"); +MODULE_DESCRIPTION("Retu watchdog"); +MODULE_AUTHOR("Amit Kucheria"); +MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index c7e17ceafa6..cfed0fe264d 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -3,12 +3,13 @@ * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net) */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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> @@ -172,7 +173,7 @@ static struct miscdevice riowd_miscdev = { .fops = &riowd_fops }; -static int __devinit riowd_probe(struct platform_device *op) +static int riowd_probe(struct platform_device *op) { struct riowd *p; int err = -EINVAL; @@ -181,7 +182,7 @@ static int __devinit riowd_probe(struct platform_device *op) goto out; err = -ENOMEM; - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL); if (!p) goto out; @@ -189,42 +190,38 @@ static int __devinit riowd_probe(struct platform_device *op) 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; + pr_err("Cannot map registers\n"); + goto out; } /* 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"); + pr_err("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); + pr_info("Hardware watchdog [%i minutes], regs at %p\n", + riowd_timeout, p->regs); - dev_set_drvdata(&op->dev, p); + platform_set_drvdata(op, 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) +static int riowd_remove(struct platform_device *op) { - struct riowd *p = dev_get_drvdata(&op->dev); + struct riowd *p = platform_get_drvdata(op); misc_deregister(&riowd_miscdev); of_iounmap(&op->resource[0], p->regs, 2); - kfree(p); return 0; } @@ -244,7 +241,7 @@ static struct platform_driver riowd_driver = { .of_match_table = riowd_match, }, .probe = riowd_probe, - .remove = __devexit_p(riowd_remove), + .remove = riowd_remove, }; module_platform_driver(riowd_driver); diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c new file mode 100644 index 00000000000..d92c2d5859c --- /dev/null +++ b/drivers/watchdog/rt2880_wdt.c @@ -0,0 +1,206 @@ +/* + * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer + * + * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2013 John Crispin <blogic@openwrt.org> + * + * This driver was based on: drivers/watchdog/softdog.c + * + * 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/clk.h> +#include <linux/reset.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/watchdog.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> + +#include <asm/mach-ralink/ralink_regs.h> + +#define SYSC_RSTSTAT 0x38 +#define WDT_RST_CAUSE BIT(1) + +#define RALINK_WDT_TIMEOUT 30 +#define RALINK_WDT_PRESCALE 65536 + +#define TIMER_REG_TMR1LOAD 0x00 +#define TIMER_REG_TMR1CTL 0x08 + +#define TMRSTAT_TMR1RST BIT(5) + +#define TMR1CTL_ENABLE BIT(7) +#define TMR1CTL_MODE_SHIFT 4 +#define TMR1CTL_MODE_MASK 0x3 +#define TMR1CTL_MODE_FREE_RUNNING 0x0 +#define TMR1CTL_MODE_PERIODIC 0x1 +#define TMR1CTL_MODE_TIMEOUT 0x2 +#define TMR1CTL_MODE_WDT 0x3 +#define TMR1CTL_PRESCALE_MASK 0xf +#define TMR1CTL_PRESCALE_65536 0xf + +static struct clk *rt288x_wdt_clk; +static unsigned long rt288x_wdt_freq; +static void __iomem *rt288x_wdt_base; + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static inline void rt_wdt_w32(unsigned reg, u32 val) +{ + iowrite32(val, rt288x_wdt_base + reg); +} + +static inline u32 rt_wdt_r32(unsigned reg) +{ + return ioread32(rt288x_wdt_base + reg); +} + +static int rt288x_wdt_ping(struct watchdog_device *w) +{ + rt_wdt_w32(TIMER_REG_TMR1LOAD, w->timeout * rt288x_wdt_freq); + + return 0; +} + +static int rt288x_wdt_start(struct watchdog_device *w) +{ + u32 t; + + t = rt_wdt_r32(TIMER_REG_TMR1CTL); + t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT | + TMR1CTL_PRESCALE_MASK); + t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT | + TMR1CTL_PRESCALE_65536); + rt_wdt_w32(TIMER_REG_TMR1CTL, t); + + rt288x_wdt_ping(w); + + t = rt_wdt_r32(TIMER_REG_TMR1CTL); + t |= TMR1CTL_ENABLE; + rt_wdt_w32(TIMER_REG_TMR1CTL, t); + + return 0; +} + +static int rt288x_wdt_stop(struct watchdog_device *w) +{ + u32 t; + + rt288x_wdt_ping(w); + + t = rt_wdt_r32(TIMER_REG_TMR1CTL); + t &= ~TMR1CTL_ENABLE; + rt_wdt_w32(TIMER_REG_TMR1CTL, t); + + return 0; +} + +static int rt288x_wdt_set_timeout(struct watchdog_device *w, unsigned int t) +{ + w->timeout = t; + rt288x_wdt_ping(w); + + return 0; +} + +static int rt288x_wdt_bootcause(void) +{ + if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE) + return WDIOF_CARDRESET; + + return 0; +} + +static struct watchdog_info rt288x_wdt_info = { + .identity = "Ralink Watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, +}; + +static struct watchdog_ops rt288x_wdt_ops = { + .owner = THIS_MODULE, + .start = rt288x_wdt_start, + .stop = rt288x_wdt_stop, + .ping = rt288x_wdt_ping, + .set_timeout = rt288x_wdt_set_timeout, +}; + +static struct watchdog_device rt288x_wdt_dev = { + .info = &rt288x_wdt_info, + .ops = &rt288x_wdt_ops, + .min_timeout = 1, +}; + +static int rt288x_wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rt288x_wdt_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rt288x_wdt_base)) + return PTR_ERR(rt288x_wdt_base); + + rt288x_wdt_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(rt288x_wdt_clk)) + return PTR_ERR(rt288x_wdt_clk); + + device_reset(&pdev->dev); + + rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE; + + rt288x_wdt_dev.dev = &pdev->dev; + rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); + + rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); + rt288x_wdt_dev.timeout = rt288x_wdt_dev.max_timeout; + + watchdog_set_nowayout(&rt288x_wdt_dev, nowayout); + + ret = watchdog_register_device(&rt288x_wdt_dev); + if (!ret) + dev_info(&pdev->dev, "Initialized\n"); + + return 0; +} + +static int rt288x_wdt_remove(struct platform_device *pdev) +{ + watchdog_unregister_device(&rt288x_wdt_dev); + + return 0; +} + +static void rt288x_wdt_shutdown(struct platform_device *pdev) +{ + rt288x_wdt_stop(&rt288x_wdt_dev); +} + +static const struct of_device_id rt288x_wdt_match[] = { + { .compatible = "ralink,rt2880-wdt" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt288x_wdt_match); + +static struct platform_driver rt288x_wdt_driver = { + .probe = rt288x_wdt_probe, + .remove = rt288x_wdt_remove, + .shutdown = rt288x_wdt_shutdown, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .of_match_table = rt288x_wdt_match, + }, +}; + +module_platform_driver(rt288x_wdt_driver); + +MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver"); +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 404172f02c9..7c6ccd071ba 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -23,13 +23,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> #include <linux/timer.h> -#include <linux/miscdevice.h> /* for MODULE_ALIAS_MISCDEV */ #include <linux/watchdog.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/clk.h> @@ -38,28 +38,48 @@ #include <linux/cpufreq.h> #include <linux/slab.h> #include <linux/err.h> +#include <linux/of.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> -#include <mach/map.h> +#define S3C2410_WTCON 0x00 +#define S3C2410_WTDAT 0x04 +#define S3C2410_WTCNT 0x08 -#undef S3C_VA_WATCHDOG -#define S3C_VA_WATCHDOG (0) +#define S3C2410_WTCON_RSTEN (1 << 0) +#define S3C2410_WTCON_INTEN (1 << 2) +#define S3C2410_WTCON_ENABLE (1 << 5) -#include <plat/regs-watchdog.h> +#define S3C2410_WTCON_DIV16 (0 << 3) +#define S3C2410_WTCON_DIV32 (1 << 3) +#define S3C2410_WTCON_DIV64 (2 << 3) +#define S3C2410_WTCON_DIV128 (3 << 3) -#define PFX "s3c2410-wdt: " +#define S3C2410_WTCON_PRESCALE(x) ((x) << 8) +#define S3C2410_WTCON_PRESCALE_MASK (0xff << 8) #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; +#define EXYNOS5_RST_STAT_REG_OFFSET 0x0404 +#define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408 +#define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c +#define QUIRK_HAS_PMU_CONFIG (1 << 0) +#define QUIRK_HAS_RST_STAT (1 << 1) + +/* These quirks require that we have a PMU register map */ +#define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \ + QUIRK_HAS_RST_STAT) + +static bool nowayout = WATCHDOG_NOWAYOUT; +static int tmr_margin; 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(nowayout, bool, 0); module_param(soft_noboot, int, 0); module_param(debug, int, 0); @@ -74,46 +94,159 @@ 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 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 DEFINE_SPINLOCK(wdt_lock); +/** + * struct s3c2410_wdt_variant - Per-variant config data + * + * @disable_reg: Offset in pmureg for the register that disables the watchdog + * timer reset functionality. + * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog + * timer reset functionality. + * @mask_bit: Bit number for the watchdog timer in the disable register and the + * mask reset register. + * @rst_stat_reg: Offset in pmureg for the register that has the reset status. + * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog + * reset. + * @quirks: A bitfield of quirks. + */ + +struct s3c2410_wdt_variant { + int disable_reg; + int mask_reset_reg; + int mask_bit; + int rst_stat_reg; + int rst_stat_bit; + u32 quirks; +}; + +struct s3c2410_wdt { + struct device *dev; + struct clk *clock; + void __iomem *reg_base; + unsigned int count; + spinlock_t lock; + unsigned long wtcon_save; + unsigned long wtdat_save; + struct watchdog_device wdt_device; + struct notifier_block freq_transition; + struct s3c2410_wdt_variant *drv_data; + struct regmap *pmureg; +}; + +static const struct s3c2410_wdt_variant drv_data_s3c2410 = { + .quirks = 0 +}; + +#ifdef CONFIG_OF +static const struct s3c2410_wdt_variant drv_data_exynos5250 = { + .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, + .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, + .mask_bit = 20, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = 20, + .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, +}; + +static const struct s3c2410_wdt_variant drv_data_exynos5420 = { + .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, + .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, + .mask_bit = 0, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = 9, + .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, +}; + +static const struct of_device_id s3c2410_wdt_match[] = { + { .compatible = "samsung,s3c2410-wdt", + .data = &drv_data_s3c2410 }, + { .compatible = "samsung,exynos5250-wdt", + .data = &drv_data_exynos5250 }, + { .compatible = "samsung,exynos5420-wdt", + .data = &drv_data_exynos5420 }, + {}, +}; +MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); +#endif + +static const struct platform_device_id s3c2410_wdt_ids[] = { + { + .name = "s3c2410-wdt", + .driver_data = (unsigned long)&drv_data_s3c2410, + }, + {} +}; +MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids); /* watchdog control routines */ -#define DBG(msg...) do { \ - if (debug) \ - printk(KERN_INFO msg); \ - } while (0) +#define DBG(fmt, ...) \ +do { \ + if (debug) \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) /* functions */ +static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb) +{ + return container_of(nb, struct s3c2410_wdt, freq_transition); +} + +static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask) +{ + int ret; + u32 mask_val = 1 << wdt->drv_data->mask_bit; + u32 val = 0; + + /* No need to do anything if no PMU CONFIG needed */ + if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG)) + return 0; + + if (mask) + val = mask_val; + + ret = regmap_update_bits(wdt->pmureg, + wdt->drv_data->disable_reg, + mask_val, val); + if (ret < 0) + goto error; + + ret = regmap_update_bits(wdt->pmureg, + wdt->drv_data->mask_reset_reg, + mask_val, val); + error: + if (ret < 0) + dev_err(wdt->dev, "failed to update reg(%d)\n", ret); + + return ret; +} + static int s3c2410wdt_keepalive(struct watchdog_device *wdd) { - spin_lock(&wdt_lock); - writel(wdt_count, wdt_base + S3C2410_WTCNT); - spin_unlock(&wdt_lock); + struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->lock); + writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); + spin_unlock(&wdt->lock); return 0; } -static void __s3c2410wdt_stop(void) +static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt) { unsigned long wtcon; - wtcon = readl(wdt_base + S3C2410_WTCON); + wtcon = readl(wdt->reg_base + S3C2410_WTCON); wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN); - writel(wtcon, wdt_base + S3C2410_WTCON); + writel(wtcon, wdt->reg_base + S3C2410_WTCON); } static int s3c2410wdt_stop(struct watchdog_device *wdd) { - spin_lock(&wdt_lock); - __s3c2410wdt_stop(); - spin_unlock(&wdt_lock); + struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + + spin_lock(&wdt->lock); + __s3c2410wdt_stop(wdt); + spin_unlock(&wdt->lock); return 0; } @@ -121,12 +254,13 @@ static int s3c2410wdt_stop(struct watchdog_device *wdd) static int s3c2410wdt_start(struct watchdog_device *wdd) { unsigned long wtcon; + struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); - spin_lock(&wdt_lock); + spin_lock(&wdt->lock); - __s3c2410wdt_stop(); + __s3c2410wdt_stop(wdt); - wtcon = readl(wdt_base + S3C2410_WTCON); + wtcon = readl(wdt->reg_base + S3C2410_WTCON); wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128; if (soft_noboot) { @@ -137,25 +271,26 @@ static int s3c2410wdt_start(struct watchdog_device *wdd) wtcon |= S3C2410_WTCON_RSTEN; } - DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n", - __func__, wdt_count, wtcon); + DBG("%s: 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); + writel(wdt->count, wdt->reg_base + S3C2410_WTDAT); + writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); + writel(wtcon, wdt->reg_base + S3C2410_WTCON); + spin_unlock(&wdt->lock); return 0; } -static inline int s3c2410wdt_is_running(void) +static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt) { - return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; + return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; } static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout) { - unsigned long freq = clk_get_rate(wdt_clock); + struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned long freq = clk_get_rate(wdt->clock); unsigned int count; unsigned int divisor = 1; unsigned long wtcon; @@ -163,7 +298,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou if (timeout < 1) return -EINVAL; - freq /= 128; + freq = DIV_ROUND_UP(freq, 128); count = timeout * freq; DBG("%s: count=%d, timeout=%d, freq=%lu\n", @@ -175,30 +310,29 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou */ if (count >= 0x10000) { - for (divisor = 1; divisor <= 0x100; divisor++) { - if ((count / divisor) < 0x10000) - break; - } + divisor = DIV_ROUND_UP(count, 0xffff); - if ((count / divisor) >= 0x10000) { - dev_err(wdt_dev, "timeout %d too big\n", timeout); + if (divisor > 0x100) { + dev_err(wdt->dev, "timeout %d too big\n", timeout); return -EINVAL; } } DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n", - __func__, timeout, divisor, count, count/divisor); + __func__, timeout, divisor, count, DIV_ROUND_UP(count, divisor)); - count /= divisor; - wdt_count = count; + count = DIV_ROUND_UP(count, divisor); + wdt->count = count; /* update the pre-scaler */ - wtcon = readl(wdt_base + S3C2410_WTCON); + wtcon = readl(wdt->reg_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); + writel(count, wdt->reg_base + S3C2410_WTDAT); + writel(wtcon, wdt->reg_base + S3C2410_WTCON); + + wdd->timeout = (count * divisor) / freq; return 0; } @@ -222,27 +356,30 @@ static struct watchdog_ops s3c2410wdt_ops = { static struct watchdog_device s3c2410_wdd = { .info = &s3c2410_wdt_ident, .ops = &s3c2410wdt_ops, + .timeout = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME, }; /* interrupt handler code */ static irqreturn_t s3c2410wdt_irq(int irqno, void *param) { - dev_info(wdt_dev, "watchdog timer expired (irq)\n"); + struct s3c2410_wdt *wdt = platform_get_drvdata(param); + + dev_info(wdt->dev, "watchdog timer expired (irq)\n"); - s3c2410wdt_keepalive(&s3c2410_wdd); + s3c2410wdt_keepalive(&wdt->wdt_device); return IRQ_HANDLED; } - -#ifdef CONFIG_CPU_FREQ +#ifdef CONFIG_ARM_S3C24XX_CPUFREQ static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data) { int ret; + struct s3c2410_wdt *wdt = freq_to_wdt(nb); - if (!s3c2410wdt_is_running()) + if (!s3c2410wdt_is_running(wdt)) goto done; if (val == CPUFREQ_PRECHANGE) { @@ -251,14 +388,15 @@ static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb, * the watchdog is running. */ - s3c2410wdt_keepalive(&s3c2410_wdd); + s3c2410wdt_keepalive(&wdt->wdt_device); } else if (val == CPUFREQ_POSTCHANGE) { - s3c2410wdt_stop(&s3c2410_wdd); + s3c2410wdt_stop(&wdt->wdt_device); - ret = s3c2410wdt_set_heartbeat(&s3c2410_wdd, s3c2410_wdd.timeout); + ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, + wdt->wdt_device.timeout); if (ret >= 0) - s3c2410wdt_start(&s3c2410_wdd); + s3c2410wdt_start(&wdt->wdt_device); else goto err; } @@ -267,55 +405,100 @@ done: return 0; err: - dev_err(wdt_dev, "cannot set new value for timeout %d\n", - s3c2410_wdd.timeout); + dev_err(wdt->dev, "cannot set new value for timeout %d\n", + wdt->wdt_device.timeout); return ret; } -static struct notifier_block s3c2410wdt_cpufreq_transition_nb = { - .notifier_call = s3c2410wdt_cpufreq_transition, -}; - -static inline int s3c2410wdt_cpufreq_register(void) +static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt) { - return cpufreq_register_notifier(&s3c2410wdt_cpufreq_transition_nb, + wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition; + + return cpufreq_register_notifier(&wdt->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); } -static inline void s3c2410wdt_cpufreq_deregister(void) +static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) { - cpufreq_unregister_notifier(&s3c2410wdt_cpufreq_transition_nb, + wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition; + + cpufreq_unregister_notifier(&wdt->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); } #else -static inline int s3c2410wdt_cpufreq_register(void) + +static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt) { return 0; } -static inline void s3c2410wdt_cpufreq_deregister(void) +static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) { } #endif -static int __devinit s3c2410wdt_probe(struct platform_device *pdev) +static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) +{ + unsigned int rst_stat; + int ret; + + if (!(wdt->drv_data->quirks & QUIRK_HAS_RST_STAT)) + return 0; + + ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat); + if (ret) + dev_warn(wdt->dev, "Couldn't get RST_STAT register\n"); + else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit)) + return WDIOF_CARDRESET; + + return 0; +} + +/* s3c2410_get_wdt_driver_data */ +static inline struct s3c2410_wdt_variant * +get_wdt_drv_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(s3c2410_wdt_match, pdev->dev.of_node); + return (struct s3c2410_wdt_variant *)match->data; + } else { + return (struct s3c2410_wdt_variant *) + platform_get_device_id(pdev)->driver_data; + } +} + +static int s3c2410wdt_probe(struct platform_device *pdev) { struct device *dev; + struct s3c2410_wdt *wdt; + struct resource *wdt_mem; + struct resource *wdt_irq; unsigned int wtcon; int started = 0; int ret; - int size; DBG("%s: probe=%p\n", __func__, pdev); dev = &pdev->dev; - wdt_dev = &pdev->dev; - wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (wdt_mem == NULL) { - dev_err(dev, "no memory resource specified\n"); - return -ENOENT; + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->dev = &pdev->dev; + spin_lock_init(&wdt->lock); + wdt->wdt_device = s3c2410_wdd; + + wdt->drv_data = get_wdt_drv_data(pdev); + if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) { + wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,syscon-phandle"); + if (IS_ERR(wdt->pmureg)) { + dev_err(dev, "syscon regmap lookup failed.\n"); + return PTR_ERR(wdt->pmureg); + } } wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -326,43 +509,44 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) } /* get the memory region for the watchdog timer */ - - size = resource_size(wdt_mem); - if (!request_mem_region(wdt_mem->start, size, pdev->name)) { - dev_err(dev, "failed to get memory region\n"); - ret = -EBUSY; + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->reg_base = devm_ioremap_resource(dev, wdt_mem); + if (IS_ERR(wdt->reg_base)) { + ret = PTR_ERR(wdt->reg_base); goto err; } - 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); + DBG("probe: mapped reg_base=%p\n", wdt->reg_base); - wdt_clock = clk_get(&pdev->dev, "watchdog"); - if (IS_ERR(wdt_clock)) { + wdt->clock = devm_clk_get(dev, "watchdog"); + if (IS_ERR(wdt->clock)) { dev_err(dev, "failed to find watchdog clock source\n"); - ret = PTR_ERR(wdt_clock); - goto err_map; + ret = PTR_ERR(wdt->clock); + goto err; } - clk_enable(wdt_clock); + ret = clk_prepare_enable(wdt->clock); + if (ret < 0) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } - ret = s3c2410wdt_cpufreq_register(); + ret = s3c2410wdt_cpufreq_register(wdt); if (ret < 0) { - printk(KERN_ERR PFX "failed to register cpufreq\n"); + dev_err(dev, "failed to register cpufreq\n"); goto err_clk; } + watchdog_set_drvdata(&wdt->wdt_device, wdt); + /* see if we can actually set the requested timer margin, and if * not, try the default value */ - if (s3c2410wdt_set_heartbeat(&s3c2410_wdd, tmr_margin)) { - started = s3c2410wdt_set_heartbeat(&s3c2410_wdd, + watchdog_init_timeout(&wdt->wdt_device, tmr_margin, &pdev->dev); + ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, + wdt->wdt_device.timeout); + if (ret) { + started = s3c2410wdt_set_heartbeat(&wdt->wdt_device, CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); if (started == 0) @@ -374,34 +558,43 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) "cannot start\n"); } - ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev); + ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0, + pdev->name, pdev); if (ret != 0) { dev_err(dev, "failed to install irq (%d)\n", ret); goto err_cpufreq; } - watchdog_set_nowayout(&s3c2410_wdd, nowayout); + watchdog_set_nowayout(&wdt->wdt_device, nowayout); + + wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); - ret = watchdog_register_device(&s3c2410_wdd); + ret = watchdog_register_device(&wdt->wdt_device); if (ret) { dev_err(dev, "cannot register watchdog (%d)\n", ret); - goto err_irq; + goto err_cpufreq; } + ret = s3c2410wdt_mask_and_disable_reset(wdt, false); + if (ret < 0) + goto err_unregister; + if (tmr_atboot && started == 0) { dev_info(dev, "starting watchdog timer\n"); - s3c2410wdt_start(&s3c2410_wdd); + s3c2410wdt_start(&wdt->wdt_device); } 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(&s3c2410_wdd); + s3c2410wdt_stop(&wdt->wdt_device); } + platform_set_drvdata(pdev, wdt); + /* print out a statement of readiness */ - wtcon = readl(wdt_base + S3C2410_WTCON); + wtcon = readl(wdt->reg_base + S3C2410_WTCON); dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n", (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in", @@ -410,134 +603,107 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) return 0; - err_irq: - free_irq(wdt_irq->start, pdev); + err_unregister: + watchdog_unregister_device(&wdt->wdt_device); err_cpufreq: - s3c2410wdt_cpufreq_deregister(); + s3c2410wdt_cpufreq_deregister(wdt); err_clk: - clk_disable(wdt_clock); - clk_put(wdt_clock); - wdt_clock = NULL; - - err_map: - iounmap(wdt_base); - - err_req: - release_mem_region(wdt_mem->start, size); + clk_disable_unprepare(wdt->clock); err: - wdt_irq = NULL; - wdt_mem = NULL; return ret; } -static int __devexit s3c2410wdt_remove(struct platform_device *dev) +static int s3c2410wdt_remove(struct platform_device *dev) { - watchdog_unregister_device(&s3c2410_wdd); + int ret; + struct s3c2410_wdt *wdt = platform_get_drvdata(dev); - free_irq(wdt_irq->start, dev); + ret = s3c2410wdt_mask_and_disable_reset(wdt, true); + if (ret < 0) + return ret; - s3c2410wdt_cpufreq_deregister(); + watchdog_unregister_device(&wdt->wdt_device); - clk_disable(wdt_clock); - clk_put(wdt_clock); - wdt_clock = NULL; + s3c2410wdt_cpufreq_deregister(wdt); - iounmap(wdt_base); + clk_disable_unprepare(wdt->clock); - release_mem_region(wdt_mem->start, resource_size(wdt_mem)); - wdt_irq = NULL; - wdt_mem = NULL; return 0; } static void s3c2410wdt_shutdown(struct platform_device *dev) { - s3c2410wdt_stop(&s3c2410_wdd); -} + struct s3c2410_wdt *wdt = platform_get_drvdata(dev); + + s3c2410wdt_mask_and_disable_reset(wdt, true); -#ifdef CONFIG_PM + s3c2410wdt_stop(&wdt->wdt_device); +} -static unsigned long wtcon_save; -static unsigned long wtdat_save; +#ifdef CONFIG_PM_SLEEP -static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state) +static int s3c2410wdt_suspend(struct device *dev) { + int ret; + struct s3c2410_wdt *wdt = dev_get_drvdata(dev); + /* Save watchdog state, and turn it off. */ - wtcon_save = readl(wdt_base + S3C2410_WTCON); - wtdat_save = readl(wdt_base + S3C2410_WTDAT); + wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON); + wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT); + + ret = s3c2410wdt_mask_and_disable_reset(wdt, true); + if (ret < 0) + return ret; /* Note that WTCNT doesn't need to be saved. */ - s3c2410wdt_stop(&s3c2410_wdd); + s3c2410wdt_stop(&wdt->wdt_device); return 0; } -static int s3c2410wdt_resume(struct platform_device *dev) +static int s3c2410wdt_resume(struct device *dev) { + int ret; + struct s3c2410_wdt *wdt = dev_get_drvdata(dev); + /* Restore watchdog state. */ + writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT); + writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */ + writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON); - writel(wtdat_save, wdt_base + S3C2410_WTDAT); - writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */ - writel(wtcon_save, wdt_base + S3C2410_WTCON); + ret = s3c2410wdt_mask_and_disable_reset(wdt, false); + if (ret < 0) + return ret; - printk(KERN_INFO PFX "watchdog %sabled\n", - (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); + dev_info(dev, "watchdog %sabled\n", + (wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); return 0; } - -#else -#define s3c2410wdt_suspend NULL -#define s3c2410wdt_resume NULL -#endif /* CONFIG_PM */ - -#ifdef CONFIG_OF -static const struct of_device_id s3c2410_wdt_match[] = { - { .compatible = "samsung,s3c2410-wdt" }, - {}, -}; -MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); -#else -#define s3c2410_wdt_match NULL #endif +static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend, + s3c2410wdt_resume); + static struct platform_driver s3c2410wdt_driver = { .probe = s3c2410wdt_probe, - .remove = __devexit_p(s3c2410wdt_remove), + .remove = s3c2410wdt_remove, .shutdown = s3c2410wdt_shutdown, - .suspend = s3c2410wdt_suspend, - .resume = s3c2410wdt_resume, + .id_table = s3c2410_wdt_ids, .driver = { .owner = THIS_MODULE, .name = "s3c2410-wdt", - .of_match_table = s3c2410_wdt_match, + .pm = &s3c2410wdt_pm_ops, + .of_match_table = of_match_ptr(s3c2410_wdt_match), }, }; - -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_platform_driver(s3c2410wdt_driver); 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 index 016245419fa..e1d39a1e962 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -17,6 +17,9 @@ * * 27/11/2000 Initial release */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -25,6 +28,7 @@ #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/init.h> +#include <linux/io.h> #include <linux/bitops.h> #include <linux/uaccess.h> #include <linux/timex.h> @@ -50,10 +54,10 @@ static int sa1100dog_open(struct inode *inode, struct file *file) return -EBUSY; /* Activate SA1100 Watchdog timer */ - OSMR3 = OSCR + pre_margin; - OSSR = OSSR_M3; - OWER = OWER_WME; - OIER |= OIER_E3; + writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); + writel_relaxed(OSSR_M3, OSSR); + writel_relaxed(OWER_WME, OWER); + writel_relaxed(readl_relaxed(OIER) | OIER_E3, OIER); return nonseekable_open(inode, file); } @@ -66,7 +70,7 @@ static int sa1100dog_open(struct inode *inode, struct file *file) */ static int sa1100dog_release(struct inode *inode, struct file *file) { - printk(KERN_CRIT "WATCHDOG: Device closed - timer will not stop\n"); + pr_crit("Device closed - timer will not stop\n"); clear_bit(1, &sa1100wdt_users); return 0; } @@ -76,7 +80,7 @@ static ssize_t sa1100dog_write(struct file *file, const char __user *data, { if (len) /* Refresh OSMR3 timer. */ - OSMR3 = OSCR + pre_margin; + writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); return len; } @@ -110,7 +114,7 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd, break; case WDIOC_KEEPALIVE: - OSMR3 = OSCR + pre_margin; + writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); ret = 0; break; @@ -125,7 +129,7 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd, } pre_margin = oscr_freq * time; - OSMR3 = OSCR + pre_margin; + writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); /*fall through*/ case WDIOC_GETTIMEOUT: @@ -169,9 +173,8 @@ static int __init sa1100dog_init(void) ret = misc_register(&sa1100dog_miscdev); if (ret == 0) - printk(KERN_INFO - "SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n", - margin); + pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n", + margin); return ret; } @@ -190,4 +193,3 @@ 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 index b01a30e5a66..3abae50773b 100644 --- a/drivers/watchdog/sb_wdog.c +++ b/drivers/watchdog/sb_wdog.c @@ -43,6 +43,9 @@ * version 1 or 2 as published by the Free Software Foundation. * */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/io.h> #include <linux/uaccess.h> @@ -125,9 +128,8 @@ static int sbwdog_release(struct inode *inode, struct file *file) __raw_writeb(0, user_dog); module_put(THIS_MODULE); } else { - printk(KERN_CRIT - "%s: Unexpected close, not stopping watchdog!\n", - ident.identity); + pr_crit("%s: Unexpected close, not stopping watchdog!\n", + ident.identity); sbwdog_pet(user_dog); } clear_bit(0, &sbwdog_gate); @@ -206,7 +208,7 @@ static long sbwdog_ioctl(struct file *file, unsigned int cmd, * 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); + ret = put_user((u32)__raw_readq(user_dog - 8) / 1000000, p); break; } return ret; @@ -269,7 +271,7 @@ irqreturn_t sbwdog_interrupt(int irq, void *addr) * 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 " + pr_crit("%s in danger of initiating system reset " "in %ld.%01ld seconds\n", ident.identity, wd_init / 1000000, (wd_init / 100000) % 10); @@ -290,9 +292,8 @@ static int __init sbwdog_init(void) */ ret = register_reboot_notifier(&sbwdog_notifier); if (ret) { - printk(KERN_ERR - "%s: cannot register reboot notifier (err=%d)\n", - ident.identity, ret); + pr_err("%s: cannot register reboot notifier (err=%d)\n", + ident.identity, ret); return ret; } @@ -303,16 +304,16 @@ static int __init sbwdog_init(void) ret = request_irq(1, sbwdog_interrupt, IRQF_SHARED, ident.identity, (void *)user_dog); if (ret) { - printk(KERN_ERR "%s: failed to request irq 1 - %d\n", - ident.identity, ret); + pr_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); + pr_info("%s: timeout is %ld.%ld secs\n", + ident.identity, + timeout / 1000000, (timeout / 100000) % 10); return 0; } free_irq(1, (void *)user_dog); @@ -340,7 +341,6 @@ 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 @@ -353,8 +353,7 @@ void platform_wd_setup(void) ret = request_irq(1, sbwdog_interrupt, 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); + pr_crit("Watchdog IRQ zero(0) failed to be requested - %d\n", ret); } } diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c index 626d0e8e56c..2eef58a0cf0 100644 --- a/drivers/watchdog/sbc60xxwdt.c +++ b/drivers/watchdog/sbc60xxwdt.c @@ -48,6 +48,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -63,7 +65,6 @@ #include <linux/io.h> #include <linux/uaccess.h> -#include <asm/system.h> #define OUR_NAME "sbc60xxwdt" #define PFX OUR_NAME ": " @@ -105,8 +106,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -132,8 +133,7 @@ static void wdt_timer_ping(unsigned long data) /* Re-set the timer interval */ mod_timer(&timer, jiffies + WDT_INTERVAL); } else - printk(KERN_WARNING PFX - "Heartbeat lost! Will not ping the watchdog\n"); + pr_warn("Heartbeat lost! Will not ping the watchdog\n"); } /* @@ -146,7 +146,7 @@ static void wdt_startup(void) /* Start the timer */ mod_timer(&timer, jiffies + WDT_INTERVAL); - printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); + pr_info("Watchdog timer is now enabled\n"); } static void wdt_turnoff(void) @@ -154,7 +154,7 @@ 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"); + pr_info("Watchdog timer is now disabled...\n"); } static void wdt_keepalive(void) @@ -217,8 +217,7 @@ static int fop_close(struct inode *inode, struct file *file) wdt_turnoff(); else { del_timer(&timer); - printk(KERN_CRIT PFX - "device file closed unexpectedly. Will not stop the WDT!\n"); + pr_crit("device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &wdt_is_open); wdt_expect_close = 0; @@ -335,14 +334,12 @@ static int __init sbc60xxwdt_init(void) 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); + pr_info("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); + pr_err("I/O address 0x%04x already in use\n", wdt_start); rc = -EIO; goto err_out; } @@ -350,9 +347,7 @@ static int __init sbc60xxwdt_init(void) /* 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); + pr_err("I/O address 0x%04x already in use\n", wdt_stop); rc = -EIO; goto err_out_region1; } @@ -360,21 +355,18 @@ static int __init sbc60xxwdt_init(void) rc = register_reboot_notifier(&wdt_notifier); if (rc) { - printk(KERN_ERR PFX - "cannot register reboot notifier (err=%d)\n", rc); + pr_err("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); + pr_err("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); + pr_info("WDT driver for 60XX single board computer initialised. timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); return 0; @@ -395,4 +387,3 @@ 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 index 93ac5895312..5f268add17c 100644 --- a/drivers/watchdog/sbc7240_wdt.c +++ b/drivers/watchdog/sbc7240_wdt.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/fs.h> #include <linux/init.h> #include <linux/ioport.h> @@ -30,9 +32,6 @@ #include <linux/io.h> #include <linux/uaccess.h> #include <linux/atomic.h> -#include <asm/system.h> - -#define SBC7240_PREFIX "sbc7240_wdt: " #define SBC7240_ENABLE_PORT 0x443 #define SBC7240_DISABLE_PORT 0x043 @@ -47,8 +46,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Disable watchdog when closing device file"); #define SBC7240_OPEN_STATUS_BIT 0 @@ -65,8 +64,7 @@ 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"); + pr_info("Watchdog timer is now disabled\n"); } } @@ -75,23 +73,20 @@ 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"); + pr_info("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); + pr_err("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); + pr_info("timeout set to %d seconds\n", t); return 0; } @@ -150,8 +145,7 @@ static int fop_close(struct inode *inode, struct file *file) || !nowayout) { wdt_disable(); } else { - printk(KERN_CRIT SBC7240_PREFIX - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); wdt_keepalive(); } @@ -252,7 +246,7 @@ static struct notifier_block wdt_notifier = { static void __exit sbc7240_wdt_unload(void) { - printk(KERN_INFO SBC7240_PREFIX "Removing watchdog\n"); + pr_info("Removing watchdog\n"); misc_deregister(&wdt_miscdev); unregister_reboot_notifier(&wdt_notifier); @@ -264,8 +258,7 @@ 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", + pr_err("I/O address 0x%04x already in use\n", SBC7240_ENABLE_PORT); rc = -EIO; goto err_out; @@ -277,31 +270,27 @@ static int __init sbc7240_wdt_init(void) 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); + pr_info("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); + pr_err("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", + pr_err("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); + pr_info("Watchdog driver for SBC7240 initialised (nowayout=%d)\n", + nowayout); return 0; @@ -320,5 +309,3 @@ 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 index 514ec23050f..da60560ca44 100644 --- a/drivers/watchdog/sbc8360.c +++ b/drivers/watchdog/sbc8360.c @@ -36,6 +36,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/types.h> #include <linux/miscdevice.h> @@ -51,13 +53,10 @@ #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 @@ -197,11 +196,11 @@ static int wd_times[64][2] = { static int timeout = 27; static int wd_margin = 0xB; static int wd_multiplier = 2; -static int nowayout = WATCHDOG_NOWAYOUT; +static bool 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_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -280,8 +279,7 @@ 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"); + pr_crit("SBC8360 device closed unexpectedly. SBC8360 will not stop!\n"); clear_bit(0, &sbc8360_is_open); expect_close = 0; @@ -334,20 +332,19 @@ static int __init sbc8360_init(void) unsigned long int mseconds = 60000; if (timeout < 0 || timeout > 63) { - printk(KERN_ERR PFX "Invalid timeout index (must be 0-63).\n"); + pr_err("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", + pr_err("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", + pr_err("BASETIME method I/O %X is not available\n", SBC8360_BASETIME); res = -EIO; goto out_nobasetimereg; @@ -355,13 +352,13 @@ static int __init sbc8360_init(void) res = register_reboot_notifier(&sbc8360_notifier); if (res) { - printk(KERN_ERR PFX "Failed to register reboot notifier.\n"); + pr_err("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"); + pr_err("failed to register misc device\n"); goto out_nomisc; } @@ -378,7 +375,7 @@ static int __init sbc8360_init(void) 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); + pr_info("Timeout set at %ld ms\n", mseconds); return 0; @@ -407,6 +404,5 @@ 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 index eaca366b723..a1c502e0d8e 100644 --- a/drivers/watchdog/sbc_epx_c3.c +++ b/drivers/watchdog/sbc_epx_c3.c @@ -13,6 +13,8 @@ * based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -28,13 +30,12 @@ #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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -51,7 +52,7 @@ static void epx_c3_stop(void) outb(0, EPXC3_WATCHDOG_CTL_REG); - printk(KERN_INFO PFX "Stopped watchdog timer.\n"); + pr_info("Stopped watchdog timer\n"); } static void epx_c3_pet(void) @@ -75,7 +76,7 @@ static int epx_c3_open(struct inode *inode, struct file *file) epx_c3_pet(); epx_c3_alive = 1; - printk(KERN_INFO "Started watchdog timer.\n"); + pr_info("Started watchdog timer\n"); return nonseekable_open(inode, file); } @@ -173,9 +174,6 @@ static struct notifier_block epx_c3_notifier = { .notifier_call = epx_c3_notify_sys, }; -static const char banner[] __initconst = KERN_INFO PFX - "Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n"; - static int __init watchdog_init(void) { int ret; @@ -185,20 +183,19 @@ static int __init watchdog_init(void) ret = register_reboot_notifier(&epx_c3_notifier); if (ret) { - printk(KERN_ERR PFX "cannot register reboot notifier " - "(err=%d)\n", ret); + pr_err("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); + pr_err("cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); unregister_reboot_notifier(&epx_c3_notifier); goto out; } - printk(banner); + pr_info("Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n"); return 0; @@ -223,4 +220,3 @@ MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC. " "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 index d5d39946459..a517d8bae75 100644 --- a/drivers/watchdog/sbc_fitpc2_wdt.c +++ b/drivers/watchdog/sbc_fitpc2_wdt.c @@ -25,9 +25,8 @@ #include <linux/io.h> #include <linux/uaccess.h> -#include <asm/system.h> -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; static unsigned int margin = 60; /* (secs) Default is 1 minute */ static unsigned long wdt_status; static DEFINE_MUTEX(wdt_lock); @@ -171,8 +170,7 @@ static int fitpc2_wdt_release(struct inode *inode, struct file *file) wdt_disable(); pr_info("Device disabled\n"); } else { - pr_warning("Device closed unexpectedly -" - " timer will not stop\n"); + pr_warn("Device closed unexpectedly - timer will not stop\n"); wdt_enable(); } @@ -222,8 +220,8 @@ static int __init fitpc2_wdt_init(void) } if (margin < 31 || margin > 255) { - pr_err("margin must be in range 31 - 255" - " seconds, you tried to set %d\n", margin); + pr_err("margin must be in range 31 - 255 seconds, you tried to set %d\n", + margin); err = -EINVAL; goto err_margin; } @@ -231,7 +229,7 @@ static int __init fitpc2_wdt_init(void) err = misc_register(&fitpc2_wdt_miscdev); if (err) { pr_err("cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, err); + WATCHDOG_MINOR, err); goto err_margin; } @@ -261,9 +259,7 @@ 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_param(nowayout, bool, 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 index c01daca8405..131193a7acd 100644 --- a/drivers/watchdog/sc1200wdt.c +++ b/drivers/watchdog/sc1200wdt.c @@ -31,6 +31,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/miscdevice.h> @@ -48,7 +50,6 @@ #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 */ @@ -71,7 +72,6 @@ #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 */ @@ -93,8 +93,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -176,7 +176,7 @@ static int sc1200wdt_open(struct inode *inode, struct file *file) timeout = MAX_TIMEOUT; sc1200wdt_start(); - printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout); + pr_info("Watchdog enabled, timeout = %d min(s)", timeout); return nonseekable_open(inode, file); } @@ -254,11 +254,10 @@ static int sc1200wdt_release(struct inode *inode, struct file *file) { if (expect_close == 42) { sc1200wdt_stop(); - printk(KERN_INFO PFX "Watchdog disabled\n"); + pr_info("Watchdog disabled\n"); } else { sc1200wdt_write_data(WDTO, timeout); - printk(KERN_CRIT PFX - "Unexpected close!, timeout = %d min(s)\n", timeout); + pr_crit("Unexpected close!, timeout = %d min(s)\n", timeout); } clear_bit(0, &open_flag); expect_close = 0; @@ -361,12 +360,11 @@ static int scl200wdt_pnp_probe(struct pnp_dev *dev, 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); + pr_err("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); + pr_info("PnP device found at io port %#x/%d\n", io, io_len); return 0; } @@ -392,7 +390,7 @@ static int __init sc1200wdt_init(void) { int ret; - printk(KERN_INFO "%s\n", banner); + pr_info("%s\n", SC1200_MODULE_VER); #if defined CONFIG_PNP if (isapnp) { @@ -403,7 +401,7 @@ static int __init sc1200wdt_init(void) #endif if (io == -1) { - printk(KERN_ERR PFX "io parameter must be specified\n"); + pr_err("io parameter must be specified\n"); ret = -EINVAL; goto out_pnp; } @@ -411,12 +409,13 @@ static int __init sc1200wdt_init(void) #if defined CONFIG_PNP /* now that the user has specified an IO port and we haven't detected * any devices, disable pnp support */ + if (isapnp) + pnp_unregister_driver(&scl200wdt_pnp_driver); 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); + pr_err("Unable to register IO port %#x\n", io); ret = -EBUSY; goto out_pnp; } @@ -427,16 +426,14 @@ static int __init sc1200wdt_init(void) ret = register_reboot_notifier(&sc1200wdt_notifier); if (ret) { - printk(KERN_ERR PFX - "Unable to register reboot notifier err = %d\n", ret); + pr_err("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); + pr_err("Unable to register miscdev on minor %d\n", + WATCHDOG_MINOR); goto out_rbt; } @@ -480,4 +477,3 @@ 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 index b2840409ebc..1cfd3f6a13d 100644 --- a/drivers/watchdog/sc520_wdt.c +++ b/drivers/watchdog/sc520_wdt.c @@ -52,6 +52,8 @@ * This driver uses memory mapped IO, and spinlock. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -67,10 +69,6 @@ #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) @@ -98,8 +96,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -151,8 +149,7 @@ static void wdt_timer_ping(unsigned long data) /* Re-set the timer interval */ mod_timer(&timer, jiffies + WDT_INTERVAL); } else - printk(KERN_WARNING PFX - "Heartbeat lost! Will not ping the watchdog\n"); + pr_warn("Heartbeat lost! Will not ping the watchdog\n"); } /* @@ -161,12 +158,11 @@ static void wdt_timer_ping(unsigned long data) 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 */ + readw(wdtmrctl); /* ensure write synchronization */ writew(0xAAAA, wdtmrctl); writew(0x5555, wdtmrctl); /* unlock WDT = make WDT configuration register writable one time */ @@ -187,7 +183,7 @@ static int wdt_startup(void) /* Start the watchdog */ wdt_config(WDT_ENB | WDT_WRST_ENB | WDT_EXP_SEL_04); - printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); + pr_info("Watchdog timer is now enabled\n"); return 0; } @@ -199,7 +195,7 @@ static int wdt_turnoff(void) /* Stop the watchdog */ wdt_config(0); - printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); + pr_info("Watchdog timer is now disabled...\n"); return 0; } @@ -270,8 +266,7 @@ 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"); + pr_crit("Unexpected close, not stopping watchdog!\n"); wdt_keepalive(); } clear_bit(0, &wdt_is_open); @@ -393,36 +388,32 @@ static int __init sc520_wdt_init(void) 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); + pr_info("timeout value must be 1 <= timeout <= 3600, using %d\n", + WATCHDOG_TIMEOUT); } wdtmrctl = ioremap(MMCR_BASE + OFFS_WDTMRCTL, 2); if (!wdtmrctl) { - printk(KERN_ERR PFX "Unable to remap memory\n"); + pr_err("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); + pr_err("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); + pr_err("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); + pr_info("WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); return 0; @@ -441,4 +432,3 @@ 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 index 029467e3463..b96127ea3de 100644 --- a/drivers/watchdog/sch311x_wdt.c +++ b/drivers/watchdog/sch311x_wdt.c @@ -18,14 +18,15 @@ * Includes, defines, variables, module parameters, ... */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + /* 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/miscdevice.h> /* For struct miscdevice */ #include <linux/watchdog.h> /* For the watchdog specific items */ #include <linux/init.h> /* For __init/__exit/... */ #include <linux/fs.h> /* For file operations */ @@ -37,10 +38,8 @@ /* 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 @@ -68,10 +67,6 @@ 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); @@ -79,8 +74,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -140,6 +135,8 @@ static void sch311x_wdt_set_timeout(int t) static void sch311x_wdt_start(void) { + unsigned char t; + spin_lock(&sch311x_wdt_data.io_lock); /* set watchdog's timeout */ @@ -153,7 +150,8 @@ static void sch311x_wdt_start(void) * Bit 4-6 (Reserved) * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain */ - outb(0x0e, sch311x_wdt_data.runtime_reg + GP60); + t = inb(sch311x_wdt_data.runtime_reg + GP60); + outb((t & ~0x0d) | 0x0c, sch311x_wdt_data.runtime_reg + GP60); spin_unlock(&sch311x_wdt_data.io_lock); @@ -161,10 +159,13 @@ static void sch311x_wdt_start(void) static void sch311x_wdt_stop(void) { + unsigned char t; + spin_lock(&sch311x_wdt_data.io_lock); /* stop the watchdog */ - outb(0x01, sch311x_wdt_data.runtime_reg + GP60); + t = inb(sch311x_wdt_data.runtime_reg + GP60); + outb((t & ~0x0d) | 0x01, sch311x_wdt_data.runtime_reg + GP60); /* disable timeout by setting it to 0 */ sch311x_wdt_set_timeout(0); @@ -323,8 +324,7 @@ 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"); + pr_crit("Unexpected close, not stopping watchdog!\n"); sch311x_wdt_keepalive(); } clear_bit(0, &sch311x_wdt_is_open); @@ -355,29 +355,19 @@ static struct miscdevice sch311x_wdt_miscdev = { * Init & exit routines */ -static int __devinit sch311x_wdt_probe(struct platform_device *pdev) +static int 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; + goto exit; } if (!request_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4, @@ -386,7 +376,7 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev) sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, sch311x_wdt_data.runtime_reg + WDT_CTRL); err = -EBUSY; - goto exit_release_region2; + goto exit_release_region; } /* Make sure that the watchdog is not running */ @@ -414,24 +404,13 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev) /* 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; + goto exit_release_region2; } dev_info(dev, @@ -440,18 +419,16 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev) 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); + release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4); exit_release_region: - release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1); + release_region(sch311x_wdt_data.runtime_reg + GP60, 1); sch311x_wdt_data.runtime_reg = 0; exit: return err; } -static int __devexit sch311x_wdt_remove(struct platform_device *pdev) +static int sch311x_wdt_remove(struct platform_device *pdev) { /* Stop the timer before we leave */ if (!nowayout) @@ -461,7 +438,6 @@ static int __devexit sch311x_wdt_remove(struct platform_device *pdev) 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; } @@ -474,7 +450,7 @@ static void sch311x_wdt_shutdown(struct platform_device *dev) static struct platform_driver sch311x_wdt_driver = { .probe = sch311x_wdt_probe, - .remove = __devexit_p(sch311x_wdt_remove), + .remove = sch311x_wdt_remove, .shutdown = sch311x_wdt_shutdown, .driver = { .owner = THIS_MODULE, @@ -504,20 +480,19 @@ static int __init sch311x_detect(int sio_config_port, unsigned short *addr) /* 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"); + pr_info("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"); + pr_err("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); + pr_info("Found an SMSC SCH311%d chip at 0x%04x\n", dev_id, base_addr); exit: sch311x_sio_exit(sio_config_port); @@ -569,5 +544,3 @@ 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 index e67b76c0526..836377cf927 100644 --- a/drivers/watchdog/scx200_wdt.c +++ b/drivers/watchdog/scx200_wdt.c @@ -17,6 +17,8 @@ of any nature resulting due to the use of this software. This software is provided AS-IS with no warranties. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -30,19 +32,18 @@ #include <linux/uaccess.h> #include <linux/io.h> -#define NAME "scx200_wdt" +#define DEBUG 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); static u16 wdto_restart; @@ -66,14 +67,13 @@ static void scx200_wdt_ping(void) static void scx200_wdt_update_margin(void) { - printk(KERN_INFO NAME ": timer margin %d seconds\n", margin); + pr_info("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); + pr_debug("enabling watchdog timer, wdto_restart = %d\n", wdto_restart); spin_lock(&scx_lock); outw(0, scx200_cb_base + SCx200_WDT_WDTO); @@ -86,7 +86,7 @@ static void scx200_wdt_enable(void) static void scx200_wdt_disable(void) { - printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); + pr_debug("disabling watchdog timer\n"); spin_lock(&scx_lock); outw(0, scx200_cb_base + SCx200_WDT_WDTO); @@ -108,9 +108,7 @@ static int scx200_wdt_open(struct inode *inode, struct file *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"); + pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n"); else if (!nowayout) scx200_wdt_disable(); expect_close = 0; @@ -219,7 +217,7 @@ static int __init scx200_wdt_init(void) { int r; - printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n"); + pr_debug("NatSemi SCx200 Watchdog Driver\n"); /* check that we have found the configuration block */ if (!scx200_cb_present()) @@ -228,7 +226,7 @@ static int __init scx200_wdt_init(void) 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"); + pr_warn("watchdog I/O region busy\n"); return -EBUSY; } @@ -237,7 +235,7 @@ static int __init scx200_wdt_init(void) r = register_reboot_notifier(&scx200_wdt_notifier); if (r) { - printk(KERN_ERR NAME ": unable to register reboot notifier"); + pr_err("unable to register reboot notifier\n"); release_region(scx200_cb_base + SCx200_WDT_OFFSET, SCx200_WDT_SIZE); return r; diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index a267dc078da..061756e36cf 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c @@ -3,7 +3,7 @@ * * Watchdog driver for integrated watchdog in the SuperH processors. * - * Copyright (C) 2001 - 2010 Paul Mundt <lethal@linux-sh.org> + * Copyright (C) 2001 - 2012 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 @@ -17,21 +17,23 @@ * Added expect close support, made emulated timeout runtime changeable * general cleanups, add some ioctls */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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/spinlock.h> #include <linux/watchdog.h> -#include <linux/reboot.h> -#include <linux/notifier.h> -#include <linux/ioport.h> +#include <linux/pm_runtime.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/io.h> -#include <linux/uaccess.h> +#include <linux/clk.h> +#include <linux/err.h> #include <asm/watchdog.h> #define DRV_NAME "sh-wdt" @@ -66,31 +68,30 @@ 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 bool nowayout = WATCHDOG_NOWAYOUT; static unsigned long next_heartbeat; struct sh_wdt { void __iomem *base; struct device *dev; + struct clk *clk; + spinlock_t lock; struct timer_list timer; - - unsigned long enabled; - char expect_close; }; -static void sh_wdt_start(struct sh_wdt *wdt) +static int sh_wdt_start(struct watchdog_device *wdt_dev) { + struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev); unsigned long flags; u8 csr; - spin_lock_irqsave(&shwdt_lock, flags); + pm_runtime_get_sync(wdt->dev); + clk_enable(wdt->clk); + + spin_lock_irqsave(&wdt->lock, flags); next_heartbeat = jiffies + (heartbeat * HZ); mod_timer(&wdt->timer, next_ping_period(clock_division_ratio)); @@ -119,15 +120,18 @@ static void sh_wdt_start(struct sh_wdt *wdt) csr &= ~RSTCSR_RSTS; sh_wdt_write_rstcsr(csr); #endif - spin_unlock_irqrestore(&shwdt_lock, flags); + spin_unlock_irqrestore(&wdt->lock, flags); + + return 0; } -static void sh_wdt_stop(struct sh_wdt *wdt) +static int sh_wdt_stop(struct watchdog_device *wdt_dev) { + struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev); unsigned long flags; u8 csr; - spin_lock_irqsave(&shwdt_lock, flags); + spin_lock_irqsave(&wdt->lock, flags); del_timer(&wdt->timer); @@ -135,28 +139,39 @@ static void sh_wdt_stop(struct sh_wdt *wdt) csr &= ~WTCSR_TME; sh_wdt_write_csr(csr); - spin_unlock_irqrestore(&shwdt_lock, flags); + spin_unlock_irqrestore(&wdt->lock, flags); + + clk_disable(wdt->clk); + pm_runtime_put_sync(wdt->dev); + + return 0; } -static inline void sh_wdt_keepalive(struct sh_wdt *wdt) +static int sh_wdt_keepalive(struct watchdog_device *wdt_dev) { + struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev); unsigned long flags; - spin_lock_irqsave(&shwdt_lock, flags); + spin_lock_irqsave(&wdt->lock, flags); next_heartbeat = jiffies + (heartbeat * HZ); - spin_unlock_irqrestore(&shwdt_lock, flags); + spin_unlock_irqrestore(&wdt->lock, flags); + + return 0; } -static int sh_wdt_set_heartbeat(int t) +static int sh_wdt_set_heartbeat(struct watchdog_device *wdt_dev, unsigned t) { + struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev); unsigned long flags; if (unlikely(t < 1 || t > 3600)) /* arbitrary upper limit */ return -EINVAL; - spin_lock_irqsave(&shwdt_lock, flags); + spin_lock_irqsave(&wdt->lock, flags); heartbeat = t; - spin_unlock_irqrestore(&shwdt_lock, flags); + wdt_dev->timeout = t; + spin_unlock_irqrestore(&wdt->lock, flags); + return 0; } @@ -165,7 +180,7 @@ 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); + spin_lock_irqsave(&wdt->lock, flags); if (time_before(jiffies, next_heartbeat)) { u8 csr; @@ -179,137 +194,9 @@ static void sh_wdt_ping(unsigned long data) } else dev_warn(wdt->dev, "Heartbeat lost! Will not ping " "the watchdog\n"); - spin_unlock_irqrestore(&shwdt_lock, flags); + spin_unlock_irqrestore(&wdt->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, @@ -317,17 +204,20 @@ static const struct watchdog_info sh_wdt_info = { .identity = "SH WDT", }; -static struct notifier_block sh_wdt_notifier = { - .notifier_call = sh_wdt_notify_sys, +static const struct watchdog_ops sh_wdt_ops = { + .owner = THIS_MODULE, + .start = sh_wdt_start, + .stop = sh_wdt_stop, + .ping = sh_wdt_keepalive, + .set_timeout = sh_wdt_set_heartbeat, }; -static struct miscdevice sh_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &sh_wdt_fops, +static struct watchdog_device sh_wdt_dev = { + .info = &sh_wdt_info, + .ops = &sh_wdt_ops, }; -static int __devinit sh_wdt_probe(struct platform_device *pdev) +static int sh_wdt_probe(struct platform_device *pdev) { struct sh_wdt *wdt; struct resource *res; @@ -344,39 +234,47 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev) 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; - } + if (unlikely(!wdt)) + return -ENOMEM; wdt->dev = &pdev->dev; - wdt->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (unlikely(!wdt->base)) { - rc = -ENXIO; - goto out_err; + wdt->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt->clk)) { + /* + * Clock framework support is optional, continue on + * anyways if we don't find a matching clock. + */ + wdt->clk = NULL; } - rc = register_reboot_notifier(&sh_wdt_notifier); + wdt->base = devm_ioremap_resource(wdt->dev, res); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); + + watchdog_set_nowayout(&sh_wdt_dev, nowayout); + watchdog_set_drvdata(&sh_wdt_dev, wdt); + + spin_lock_init(&wdt->lock); + + rc = sh_wdt_set_heartbeat(&sh_wdt_dev, heartbeat); if (unlikely(rc)) { - dev_err(&pdev->dev, - "Can't register reboot notifier (err=%d)\n", rc); - goto out_unmap; + /* Default timeout if invalid */ + sh_wdt_set_heartbeat(&sh_wdt_dev, WATCHDOG_HEARTBEAT); + + dev_warn(&pdev->dev, + "heartbeat value must be 1<=x<=3600, using %d\n", + sh_wdt_dev.timeout); } - sh_wdt_miscdev.parent = wdt->dev; + dev_info(&pdev->dev, "configured with heartbeat=%d sec (nowayout=%d)\n", + sh_wdt_dev.timeout, nowayout); - rc = misc_register(&sh_wdt_miscdev); + rc = watchdog_register_device(&sh_wdt_dev); 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; + dev_err(&pdev->dev, "Can't register watchdog (err=%d)\n", rc); + return rc; } init_timer(&wdt->timer); @@ -384,77 +282,48 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev) 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)); + pm_runtime_enable(&pdev->dev); - return rc; + return 0; } -static int __devexit sh_wdt_remove(struct platform_device *pdev) +static int 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); + watchdog_unregister_device(&sh_wdt_dev); - 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); + pm_runtime_disable(&pdev->dev); return 0; } +static void sh_wdt_shutdown(struct platform_device *pdev) +{ + sh_wdt_stop(&sh_wdt_dev); +} + static struct platform_driver sh_wdt_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, - .probe = sh_wdt_probe, - .remove = __devexit_p(sh_wdt_remove), + .probe = sh_wdt_probe, + .remove = sh_wdt_remove, + .shutdown = sh_wdt_shutdown, }; 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("divisor must be 0x5<=x<=0x7, using %d\n", + clock_division_ratio); } - pr_info("%s: configured with heartbeat=%d sec (nowayout=%d)\n", - DRV_NAME, heartbeat, nowayout); - return platform_driver_register(&sh_wdt_driver); } @@ -469,7 +338,6 @@ 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, @@ -481,7 +349,7 @@ MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1 <= heartbeat <= 3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); -module_param(nowayout, int, 0); +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c new file mode 100644 index 00000000000..702d0787080 --- /dev/null +++ b/drivers/watchdog/sirfsoc_wdt.c @@ -0,0 +1,226 @@ +/* + * Watchdog driver for CSR SiRFprimaII and SiRFatlasVI + * + * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/module.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/io.h> +#include <linux/uaccess.h> + +#define CLOCK_FREQ 1000000 + +#define SIRFSOC_TIMER_COUNTER_LO 0x0000 +#define SIRFSOC_TIMER_MATCH_0 0x0008 +#define SIRFSOC_TIMER_INT_EN 0x0024 +#define SIRFSOC_TIMER_WATCHDOG_EN 0x0028 +#define SIRFSOC_TIMER_LATCH 0x0030 +#define SIRFSOC_TIMER_LATCHED_LO 0x0034 + +#define SIRFSOC_TIMER_WDT_INDEX 5 + +#define SIRFSOC_WDT_MIN_TIMEOUT 30 /* 30 secs */ +#define SIRFSOC_WDT_MAX_TIMEOUT (10 * 60) /* 10 mins */ +#define SIRFSOC_WDT_DEFAULT_TIMEOUT 30 /* 30 secs */ + +static unsigned int timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; + +module_param(timeout, uint, 0); +module_param(nowayout, bool, 0); + +MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd) +{ + u32 counter, match; + void __iomem *wdt_base; + int time_left; + + wdt_base = watchdog_get_drvdata(wdd); + counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO); + match = readl(wdt_base + + SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2)); + + time_left = match - counter; + + return time_left / CLOCK_FREQ; +} + +static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd) +{ + u32 counter, timeout_ticks; + void __iomem *wdt_base; + + timeout_ticks = wdd->timeout * CLOCK_FREQ; + wdt_base = watchdog_get_drvdata(wdd); + + /* Enable the latch before reading the LATCH_LO register */ + writel(1, wdt_base + SIRFSOC_TIMER_LATCH); + + /* Set the TO value */ + counter = readl(wdt_base + SIRFSOC_TIMER_LATCHED_LO); + + counter += timeout_ticks; + + writel(counter, wdt_base + + SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2)); + + return 0; +} + +static int sirfsoc_wdt_enable(struct watchdog_device *wdd) +{ + void __iomem *wdt_base = watchdog_get_drvdata(wdd); + sirfsoc_wdt_updatetimeout(wdd); + + /* + * NOTE: If interrupt is not enabled + * then WD-Reset doesn't get generated at all. + */ + writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN) + | (1 << SIRFSOC_TIMER_WDT_INDEX), + wdt_base + SIRFSOC_TIMER_INT_EN); + writel(1, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN); + + return 0; +} + +static int sirfsoc_wdt_disable(struct watchdog_device *wdd) +{ + void __iomem *wdt_base = watchdog_get_drvdata(wdd); + + writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN); + writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN) + & (~(1 << SIRFSOC_TIMER_WDT_INDEX)), + wdt_base + SIRFSOC_TIMER_INT_EN); + + return 0; +} + +static int sirfsoc_wdt_settimeout(struct watchdog_device *wdd, unsigned int to) +{ + wdd->timeout = to; + sirfsoc_wdt_updatetimeout(wdd); + + return 0; +} + +#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) + +static const struct watchdog_info sirfsoc_wdt_ident = { + .options = OPTIONS, + .firmware_version = 0, + .identity = "SiRFSOC Watchdog", +}; + +static struct watchdog_ops sirfsoc_wdt_ops = { + .owner = THIS_MODULE, + .start = sirfsoc_wdt_enable, + .stop = sirfsoc_wdt_disable, + .get_timeleft = sirfsoc_wdt_gettimeleft, + .ping = sirfsoc_wdt_updatetimeout, + .set_timeout = sirfsoc_wdt_settimeout, +}; + +static struct watchdog_device sirfsoc_wdd = { + .info = &sirfsoc_wdt_ident, + .ops = &sirfsoc_wdt_ops, + .timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT, + .min_timeout = SIRFSOC_WDT_MIN_TIMEOUT, + .max_timeout = SIRFSOC_WDT_MAX_TIMEOUT, +}; + +static int sirfsoc_wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret; + void __iomem *base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + watchdog_set_drvdata(&sirfsoc_wdd, base); + + watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev); + watchdog_set_nowayout(&sirfsoc_wdd, nowayout); + + ret = watchdog_register_device(&sirfsoc_wdd); + if (ret) + return ret; + + platform_set_drvdata(pdev, &sirfsoc_wdd); + + return 0; +} + +static void sirfsoc_wdt_shutdown(struct platform_device *pdev) +{ + struct watchdog_device *wdd = platform_get_drvdata(pdev); + + sirfsoc_wdt_disable(wdd); +} + +static int sirfsoc_wdt_remove(struct platform_device *pdev) +{ + sirfsoc_wdt_shutdown(pdev); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sirfsoc_wdt_suspend(struct device *dev) +{ + return 0; +} + +static int sirfsoc_wdt_resume(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + /* + * NOTE: Since timer controller registers settings are saved + * and restored back by the timer-prima2.c, so we need not + * update WD settings except refreshing timeout. + */ + sirfsoc_wdt_updatetimeout(wdd); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops, + sirfsoc_wdt_suspend, sirfsoc_wdt_resume); + +static const struct of_device_id sirfsoc_wdt_of_match[] = { + { .compatible = "sirf,prima2-tick"}, + {}, +}; +MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match); + +static struct platform_driver sirfsoc_wdt_driver = { + .driver = { + .name = "sirfsoc-wdt", + .owner = THIS_MODULE, + .pm = &sirfsoc_wdt_pm_ops, + .of_match_table = sirfsoc_wdt_of_match, + }, + .probe = sirfsoc_wdt_probe, + .remove = sirfsoc_wdt_remove, + .shutdown = sirfsoc_wdt_shutdown, +}; +module_platform_driver(sirfsoc_wdt_driver); + +MODULE_DESCRIPTION("SiRF SoC watchdog driver"); +MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sirfsoc-wdt"); diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index 97b8184614a..445ea1ad1fa 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -43,6 +43,8 @@ * Documentation/watchdog/wdt.txt */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -58,7 +60,6 @@ #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!) */ @@ -70,7 +71,6 @@ #define UNIT_SECOND 0 #define UNIT_MINUTE 1 -#define MODNAME "smsc37b787_wdt: " #define VERSION "1.1" #define IOPORT 0x3F0 @@ -85,7 +85,7 @@ 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; +static bool nowayout = WATCHDOG_NOWAYOUT; /* -- Low level function ----------------------------------------*/ @@ -363,8 +363,7 @@ static int wb_smsc_wdt_open(struct inode *inode, struct file *file) /* Reload and activate timer */ wb_smsc_wdt_enable(); - printk(KERN_INFO MODNAME - "Watchdog enabled. Timeout set to %d %s.\n", + pr_info("Watchdog enabled. Timeout set to %d %s\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); return nonseekable_open(inode, file); @@ -378,11 +377,9 @@ static int wb_smsc_wdt_release(struct inode *inode, struct file *file) if (expect_close == 42) { wb_smsc_wdt_disable(); - printk(KERN_INFO MODNAME - "Watchdog disabled, sleeping again...\n"); + pr_info("Watchdog disabled, sleeping again...\n"); } else { - printk(KERN_CRIT MODNAME - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); wb_smsc_wdt_reset_timer(); } @@ -534,12 +531,11 @@ static int __init wb_smsc_wdt_init(void) { int ret; - printk(KERN_INFO "SMsC 37B787 watchdog component driver " - VERSION " initialising...\n"); + pr_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); + pr_err("Unable to register IO port %#x\n", IOPORT); ret = -EBUSY; goto out_pnp; } @@ -553,25 +549,22 @@ static int __init wb_smsc_wdt_init(void) ret = register_reboot_notifier(&wb_smsc_wdt_notifier); if (ret) { - printk(KERN_ERR MODNAME - "Unable to register reboot notifier err = %d\n", ret); + pr_err("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); + pr_err("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", + pr_info("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); + pr_info("Watchdog initialized and sleeping (nowayout=%d)...\n", + nowayout); out_clean: return ret; @@ -592,14 +585,14 @@ 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"); + pr_info("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"); + pr_info("SMsC 37B787 watchdog component driver removed\n"); } module_init(wb_smsc_wdt_init); @@ -610,8 +603,6 @@ 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, @@ -621,7 +612,7 @@ MODULE_PARM_DESC(unit, module_param(timeout, int, 0); MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60"); -module_param(nowayout, int, 0); +module_param(nowayout, bool, 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 index bf16ffb4d21..0dc5e323d59 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -1,5 +1,5 @@ /* - * SoftDog 0.07: A Software Watchdog Device + * SoftDog: A Software Watchdog Device * * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, * All Rights Reserved. @@ -36,45 +36,36 @@ * Added Matt Domsch's nowayout module option. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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); +static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */ +module_param(soft_margin, uint, 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 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 */ - +static int soft_noboot; 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)"); + "Softdog action, set to 1 to ignore reboots, 0 to reboot (default=0)"); static int soft_panic; module_param(soft_panic, int, 0); @@ -89,9 +80,6 @@ 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.. @@ -99,18 +87,15 @@ static char expect_close; 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"); + pr_crit("Triggered - Reboot ignored\n"); else if (soft_panic) { - printk(KERN_CRIT PFX "Initiating panic.\n"); - panic("Software Watchdog Timer expired."); + pr_crit("Initiating panic\n"); + panic("Software Watchdog Timer expired"); } else { - printk(KERN_CRIT PFX "Initiating system reboot.\n"); + pr_crit("Initiating system reboot\n"); emergency_restart(); - printk(KERN_CRIT PFX "Reboot didn't ?????\n"); + pr_crit("Reboot didn't ?????\n"); } } @@ -118,127 +103,24 @@ static void watchdog_fire(unsigned long data) * Softdog operations */ -static int softdog_keepalive(void) +static int softdog_ping(struct watchdog_device *w) { - mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); + mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ)); return 0; } -static int softdog_stop(void) +static int softdog_stop(struct watchdog_device *w) { 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) +static int softdog_set_timeout(struct watchdog_device *w, unsigned int t) { - /* - * 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; + w->timeout = t; 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 */ @@ -248,7 +130,7 @@ static int softdog_notify_sys(struct notifier_block *this, unsigned long code, { if (code == SYS_DOWN || code == SYS_HALT) /* Turn the WDT off */ - softdog_stop(); + softdog_stop(NULL); return NOTIFY_DONE; } @@ -256,28 +138,28 @@ static int softdog_notify_sys(struct notifier_block *this, unsigned long code, * 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 notifier_block softdog_notifier = { + .notifier_call = softdog_notify_sys, }; -static struct miscdevice softdog_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &softdog_fops, +static struct watchdog_info softdog_info = { + .identity = "Software Watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, }; -static struct notifier_block softdog_notifier = { - .notifier_call = softdog_notify_sys, +static struct watchdog_ops softdog_ops = { + .owner = THIS_MODULE, + .start = softdog_ping, + .stop = softdog_stop, + .set_timeout = softdog_set_timeout, }; -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 struct watchdog_device softdog_dev = { + .info = &softdog_info, + .ops = &softdog_ops, + .min_timeout = 1, + .max_timeout = 0xFFFF +}; static int __init watchdog_init(void) { @@ -285,37 +167,36 @@ static int __init watchdog_init(void) /* 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", + if (soft_margin < 1 || soft_margin > 65535) { + pr_info("soft_margin must be 0 < soft_margin < 65536, using %d\n", TIMER_MARGIN); + return -EINVAL; } + softdog_dev.timeout = soft_margin; + + watchdog_set_nowayout(&softdog_dev, nowayout); ret = register_reboot_notifier(&softdog_notifier); if (ret) { - printk(KERN_ERR PFX - "cannot register reboot notifier (err=%d)\n", ret); + pr_err("cannot register reboot notifier (err=%d)\n", ret); return ret; } - ret = misc_register(&softdog_miscdev); + ret = watchdog_register_device(&softdog_dev); 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); + pr_info("Software Watchdog Timer: 0.08 initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n", + soft_noboot, soft_margin, soft_panic, nowayout); return 0; } static void __exit watchdog_exit(void) { - misc_deregister(&softdog_miscdev); + watchdog_unregister_device(&softdog_dev); unregister_reboot_notifier(&softdog_notifier); } @@ -325,4 +206,3 @@ 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 index 87e0527669d..5cca9cddb87 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -13,13 +13,17 @@ * 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" + * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide", + * AMD Publication 45482 "AMD SB800-Series Southbridges Register + * Reference Guide" */ /* * Includes, defines, variables, module parameters, ... */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -36,13 +40,13 @@ #include "sp5100_tco.h" /* Module and version information */ -#define TCO_VERSION "0.01" +#define TCO_VERSION "0.05" #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 u32 tco_wdt_fired; static void __iomem *tcobase; static unsigned int pm_iobase; static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ @@ -61,9 +65,9 @@ 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" +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started." " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); /* @@ -121,6 +125,46 @@ static int tco_timer_set_heartbeat(int t) return 0; } +static void tco_timer_enable(void) +{ + int val; + + if (sp5100_tco_pci->revision >= 0x40) { + /* For SB800 or later */ + /* Set the Watchdog timer resolution to 1 sec */ + outb(SB800_PM_WATCHDOG_CONFIG, SB800_IO_PM_INDEX_REG); + val = inb(SB800_IO_PM_DATA_REG); + val |= SB800_PM_WATCHDOG_SECOND_RES; + outb(val, SB800_IO_PM_DATA_REG); + + /* Enable watchdog decode bit and watchdog timer */ + outb(SB800_PM_WATCHDOG_CONTROL, SB800_IO_PM_INDEX_REG); + val = inb(SB800_IO_PM_DATA_REG); + val |= SB800_PCI_WATCHDOG_DECODE_EN; + val &= ~SB800_PM_WATCHDOG_DISABLE; + outb(val, SB800_IO_PM_DATA_REG); + } else { + /* For SP5100 or SB7x0 */ + /* 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); + } +} + /* * /dev/watchdog handling */ @@ -143,8 +187,7 @@ static int sp5100_tco_release(struct inode *inode, struct file *file) if (tco_expect_close == 42) { tco_timer_stop(); } else { - printk(KERN_CRIT PFX - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); tco_timer_keepalive(); } clear_bit(0, &timer_alive); @@ -260,7 +303,7 @@ static struct miscdevice sp5100_tco_miscdev = { * 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) = { +static const struct pci_device_id sp5100_tco_pci_tbl[] = { { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, PCI_ANY_ID, }, { 0, }, /* End of list */ @@ -270,11 +313,12 @@ MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); /* * Init & exit routines */ - -static unsigned char __devinit sp5100_tco_setupdevice(void) +static unsigned char sp5100_tco_setupdevice(void) { struct pci_dev *dev = NULL; + const char *dev_name = NULL; u32 val; + u32 index_reg, data_reg, base_addr; /* Match the PCI device */ for_each_pci_dev(dev) { @@ -287,59 +331,114 @@ static unsigned char __devinit sp5100_tco_setupdevice(void) if (!sp5100_tco_pci) return 0; + pr_info("PCI Revision ID: 0x%x\n", sp5100_tco_pci->revision); + + /* + * Determine type of southbridge chipset. + */ + if (sp5100_tco_pci->revision >= 0x40) { + dev_name = SB800_DEVNAME; + index_reg = SB800_IO_PM_INDEX_REG; + data_reg = SB800_IO_PM_DATA_REG; + base_addr = SB800_PM_WATCHDOG_BASE; + } else { + dev_name = SP5100_DEVNAME; + index_reg = SP5100_IO_PM_INDEX_REG; + data_reg = SP5100_IO_PM_DATA_REG; + base_addr = SP5100_PM_WATCHDOG_BASE; + } + /* Request the IO ports used by this driver */ pm_iobase = SP5100_IO_PM_INDEX_REG; - if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, "SP5100 TCO")) { - printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", - pm_iobase); + if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, dev_name)) { + pr_err("I/O address 0x%04x already in use\n", pm_iobase); goto exit; } - /* 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; + /* + * First, Find the watchdog timer MMIO address from indirect I/O. + */ + outb(base_addr+3, index_reg); + val = inb(data_reg); + outb(base_addr+2, index_reg); + val = val << 8 | inb(data_reg); + outb(base_addr+1, index_reg); + val = val << 8 | inb(data_reg); + outb(base_addr+0, index_reg); + /* Low three bits of BASE are reserved */ + val = val << 8 | (inb(data_reg) & 0xf8); + + pr_debug("Got 0x%04x from indirect I/O\n", val); + + /* Check MMIO address conflict */ + if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, + dev_name)) + goto setup_wdt; + else + pr_debug("MMIO address 0x%04x already in use\n", val); + + /* + * Secondly, Find the watchdog timer MMIO address + * from SBResource_MMIO register. + */ + if (sp5100_tco_pci->revision >= 0x40) { + /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ + outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG); + val = inb(SB800_IO_PM_DATA_REG); + outb(SB800_PM_ACPI_MMIO_EN+2, SB800_IO_PM_INDEX_REG); + val = val << 8 | inb(SB800_IO_PM_DATA_REG); + outb(SB800_PM_ACPI_MMIO_EN+1, SB800_IO_PM_INDEX_REG); + val = val << 8 | inb(SB800_IO_PM_DATA_REG); + outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG); + val = val << 8 | inb(SB800_IO_PM_DATA_REG); + } else { + /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ + pci_read_config_dword(sp5100_tco_pci, + SP5100_SB_RESOURCE_MMIO_BASE, &val); } + + /* The SBResource_MMIO is enabled and mapped memory space? */ + if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) == + SB800_ACPI_MMIO_DECODE_EN) { + /* Clear unnecessary the low twelve bits */ + val &= ~0xFFF; + /* Add the Watchdog Timer offset to base address. */ + val += SB800_PM_WDT_MMIO_OFFSET; + /* Check MMIO address conflict */ + if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, + dev_name)) { + pr_debug("Got 0x%04x from SBResource_MMIO register\n", + val); + goto setup_wdt; + } else + pr_debug("MMIO address 0x%04x already in use\n", val); + } else + pr_debug("SBResource_MMIO is disabled(0x%04x)\n", val); + + pr_notice("failed to find MMIO address, giving up.\n"); + goto unreg_region; + +setup_wdt: tcobase_phys = val; tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); - if (tcobase == 0) { - printk(KERN_ERR PFX "failed to get tcobase address\n"); + if (!tcobase) { + pr_err("failed to get tcobase address\n"); goto unreg_mem_region; } - /* Enable watchdog decode bit */ - pci_read_config_dword(sp5100_tco_pci, - SP5100_PCI_WATCHDOG_MISC_REG, - &val); - - val |= SP5100_PCI_WATCHDOG_DECODE_EN; - - pci_write_config_dword(sp5100_tco_pci, - SP5100_PCI_WATCHDOG_MISC_REG, - val); + pr_info("Using 0x%04x for watchdog MMIO address\n", 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); + /* Setup the watchdog timer */ + tco_timer_enable(); - /* Check that the watchdog action is set to reset the system. */ + /* Check that the watchdog action is set to reset the system */ val = readl(SP5100_WDT_CONTROL(tcobase)); + /* + * Save WatchDogFired status, because WatchDogFired flag is + * cleared here. + */ + tco_wdt_fired = val & SP5100_PM_WATCHDOG_FIRED; val &= ~SP5100_PM_WATCHDOG_ACTION_RESET; writel(val, SP5100_WDT_CONTROL(tcobase)); @@ -363,26 +462,20 @@ exit: return 0; } -static int __devinit sp5100_tco_init(struct platform_device *dev) +static int sp5100_tco_init(struct platform_device *dev) { int ret; - u32 val; - /* Check whether or not the hardware watchdog is there. If found, then + /* + * 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)); + pr_info("Last reboot was %striggered by watchdog.\n", + tco_wdt_fired ? "" : "not "); /* * Check that the heartbeat value is within it's range. @@ -395,16 +488,15 @@ static int __devinit sp5100_tco_init(struct platform_device *dev) ret = misc_register(&sp5100_tco_miscdev); if (ret != 0) { - printk(KERN_ERR PFX "cannot register miscdev on minor=" - "%d (err=%d)\n", + pr_err("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", + /* Show module parameters */ + pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", tcobase, heartbeat, nowayout); return 0; @@ -416,7 +508,7 @@ exit: return ret; } -static void __devexit sp5100_tco_cleanup(void) +static void sp5100_tco_cleanup(void) { /* Stop the timer before we leave */ if (!nowayout) @@ -429,7 +521,7 @@ static void __devexit sp5100_tco_cleanup(void) release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); } -static int __devexit sp5100_tco_remove(struct platform_device *dev) +static int sp5100_tco_remove(struct platform_device *dev) { if (tcobase) sp5100_tco_cleanup(); @@ -443,7 +535,7 @@ static void sp5100_tco_shutdown(struct platform_device *dev) static struct platform_driver sp5100_tco_driver = { .probe = sp5100_tco_init, - .remove = __devexit_p(sp5100_tco_remove), + .remove = sp5100_tco_remove, .shutdown = sp5100_tco_shutdown, .driver = { .owner = THIS_MODULE, @@ -455,8 +547,7 @@ static int __init sp5100_tco_init_module(void) { int err; - printk(KERN_INFO PFX "SP5100 TCO WatchDog Timer Driver v%s\n", - TCO_VERSION); + pr_info("SP5100/SB800 TCO WatchDog Timer Driver v%s\n", TCO_VERSION); err = platform_driver_register(&sp5100_tco_driver); if (err) @@ -480,13 +571,12 @@ 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"); + pr_info("SP5100/SB800 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_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index a5a16cc90a3..2b28c00da0d 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -9,33 +9,57 @@ /* * 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_START_STOP_BIT (1 << 0) #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 +/* + * These two IO registers are hardcoded and there doesn't seem to be a way to * read them from a register. */ + +/* For SP5100/SB7x0 chipset */ #define SP5100_IO_PM_INDEX_REG 0xCD6 #define SP5100_IO_PM_DATA_REG 0xCD7 +#define SP5100_SB_RESOURCE_MMIO_BASE 0x9C + #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_BASE 0x6C #define SP5100_PM_WATCHDOG_FIRED (1 << 1) #define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2) -#define SP5100_PM_WATCHDOG_DISABLE 1 +#define SP5100_PCI_WATCHDOG_MISC_REG 0x41 +#define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3) + +#define SP5100_PM_WATCHDOG_DISABLE (1 << 0) #define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1) + +#define SP5100_DEVNAME "SP5100 TCO" + + +/* For SB8x0(or later) chipset */ +#define SB800_IO_PM_INDEX_REG 0xCD6 +#define SB800_IO_PM_DATA_REG 0xCD7 + +#define SB800_PM_ACPI_MMIO_EN 0x24 +#define SB800_PM_WATCHDOG_CONTROL 0x48 +#define SB800_PM_WATCHDOG_BASE 0x48 +#define SB800_PM_WATCHDOG_CONFIG 0x4C + +#define SB800_PCI_WATCHDOG_DECODE_EN (1 << 0) +#define SB800_PM_WATCHDOG_DISABLE (1 << 2) +#define SB800_PM_WATCHDOG_SECOND_RES (3 << 0) +#define SB800_ACPI_MMIO_DECODE_EN (1 << 0) +#define SB800_ACPI_MMIO_SEL (1 << 1) + + +#define SB800_PM_WDT_MMIO_OFFSET 0xB00 + +#define SB800_DEVNAME "SB800 TCO" diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index eef1524ae52..c1b03f4235b 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -4,7 +4,7 @@ * Watchdog driver for ARM SP805 watchdog module * * Copyright (C) 2010 ST Microelectronics - * Viresh Kumar<viresh.kumar@st.com> + * Viresh Kumar <viresh.linux@gmail.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 @@ -16,19 +16,16 @@ #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/pm.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 */ @@ -55,34 +52,32 @@ /** * 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 + * @wdd: instance of struct watchdog_device + * @lock: spin lock protecting dev structure and io access + * @base: base address of wdt + * @clk: clock structure of wdt + * @adev: amba device structure of wdt + * @status: current status of wdt + * @load_val: load value to be set for current timeout */ struct sp805_wdt { + struct watchdog_device wdd; 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; +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Set to 1 to keep watchdog running after device release"); /* This routine finds load value that will reset system in required timout */ -static void wdt_setload(unsigned int timeout) +static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout) { + struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); u64 load, rate; rate = clk_get_rate(wdt->clk); @@ -101,256 +96,191 @@ static void wdt_setload(unsigned int timeout) 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); + wdd->timeout = div_u64((load + 1) * 2 + (rate / 2), rate); spin_unlock(&wdt->lock); + + return 0; } /* returns number of seconds left for reset to occur */ -static u32 wdt_timeleft(void) +static unsigned int wdt_timeleft(struct watchdog_device *wdd) { + struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); u64 load, rate; rate = clk_get_rate(wdt->clk); spin_lock(&wdt->lock); - load = readl(wdt->base + WDTVALUE); + load = readl_relaxed(wdt->base + WDTVALUE); /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */ - if (!(readl(wdt->base + WDTRIS) & INT_MASK)) + if (!(readl_relaxed(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) +static int wdt_config(struct watchdog_device *wdd, bool ping) { - spin_lock(&wdt->lock); + struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); + int ret; - 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); + if (!ping) { - /* Flush posted writes. */ - readl(wdt->base + WDTLOCK); - spin_unlock(&wdt->lock); -} + ret = clk_prepare_enable(wdt->clk); + if (ret) { + dev_err(&wdt->adev->dev, "clock enable fail"); + return ret; + } + } -/* disables watchdog timers reset */ -static void wdt_disable(void) -{ spin_lock(&wdt->lock); - writel(UNLOCK, wdt->base + WDTLOCK); - writel(0, wdt->base + WDTCONTROL); - writel(LOCK, wdt->base + WDTLOCK); + writel_relaxed(UNLOCK, wdt->base + WDTLOCK); + writel_relaxed(wdt->load_val, wdt->base + WDTLOAD); + + if (!ping) { + writel_relaxed(INT_MASK, wdt->base + WDTINTCLR); + writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + + WDTCONTROL); + } + + writel_relaxed(LOCK, wdt->base + WDTLOCK); /* Flush posted writes. */ - readl(wdt->base + WDTLOCK); + readl_relaxed(wdt->base + WDTLOCK); spin_unlock(&wdt->lock); + + return 0; } -static ssize_t sp805_wdt_write(struct file *file, const char *data, - size_t len, loff_t *ppos) +static int wdt_ping(struct watchdog_device *wdd) { - 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; + return wdt_config(wdd, true); } -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) +/* enables watchdog timers reset */ +static int wdt_enable(struct watchdog_device *wdd) { - 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; + return wdt_config(wdd, false); } -static int sp805_wdt_open(struct inode *inode, struct file *file) +/* disables watchdog timers reset */ +static int wdt_disable(struct watchdog_device *wdd) { - int ret = 0; + struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); - 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); + spin_lock(&wdt->lock); -err: - clear_bit(WDT_BUSY, &wdt->status); - return ret; -} + writel_relaxed(UNLOCK, wdt->base + WDTLOCK); + writel_relaxed(0, wdt->base + WDTCONTROL); + writel_relaxed(LOCK, wdt->base + WDTLOCK); -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; - } + /* Flush posted writes. */ + readl_relaxed(wdt->base + WDTLOCK); + spin_unlock(&wdt->lock); - wdt_disable(); - clk_disable(wdt->clk); - clear_bit(WDT_BUSY, &wdt->status); + clk_disable_unprepare(wdt->clk); 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 const struct watchdog_info wdt_info = { + .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = MODULE_NAME, }; -static struct miscdevice sp805_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &sp805_wdt_fops, +static const struct watchdog_ops wdt_ops = { + .owner = THIS_MODULE, + .start = wdt_enable, + .stop = wdt_disable, + .ping = wdt_ping, + .set_timeout = wdt_setload, + .get_timeleft = wdt_timeleft, }; -static int __devinit +static int sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) { + struct sp805_wdt *wdt; 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); + wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) { - dev_warn(&adev->dev, "Kzalloc failed\n"); ret = -ENOMEM; - goto err_kzalloc; + goto err; } - wdt->clk = clk_get(&adev->dev, NULL); + wdt->base = devm_ioremap_resource(&adev->dev, &adev->res); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); + + wdt->clk = devm_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; + goto err; } wdt->adev = adev; + wdt->wdd.info = &wdt_info; + wdt->wdd.ops = &wdt_ops; + spin_lock_init(&wdt->lock); - wdt_setload(DEFAULT_TIMEOUT); + watchdog_set_nowayout(&wdt->wdd, nowayout); + watchdog_set_drvdata(&wdt->wdd, wdt); + wdt_setload(&wdt->wdd, DEFAULT_TIMEOUT); - ret = misc_register(&sp805_wdt_miscdev); - if (ret < 0) { - dev_warn(&adev->dev, "cannot register misc device\n"); - goto err_misc_register; + ret = watchdog_register_device(&wdt->wdd); + if (ret) { + dev_err(&adev->dev, "watchdog_register_device() failed: %d\n", + ret); + goto err; } + amba_set_drvdata(adev, wdt); 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) +static int sp805_wdt_remove(struct amba_device *adev) +{ + struct sp805_wdt *wdt = amba_get_drvdata(adev); + + watchdog_unregister_device(&wdt->wdd); + watchdog_set_drvdata(&wdt->wdd, NULL); + + return 0; +} + +static int __maybe_unused sp805_wdt_suspend(struct device *dev) +{ + struct sp805_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + return wdt_disable(&wdt->wdd); + + return 0; +} + +static int __maybe_unused sp805_wdt_resume(struct device *dev) { - misc_deregister(&sp805_wdt_miscdev); - iounmap(wdt->base); - clk_put(wdt->clk); - kfree(wdt); - release_mem_region(adev->res.start, resource_size(&adev->res)); + struct sp805_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + return wdt_enable(&wdt->wdd); return 0; } +static SIMPLE_DEV_PM_OPS(sp805_wdt_dev_pm_ops, sp805_wdt_suspend, + sp805_wdt_resume); + static struct amba_id sp805_wdt_ids[] = { { .id = 0x00141805, @@ -364,29 +294,15 @@ MODULE_DEVICE_TABLE(amba, sp805_wdt_ids); static struct amba_driver sp805_wdt_driver = { .drv = { .name = MODULE_NAME, + .pm = &sp805_wdt_dev_pm_ops, }, .id_table = sp805_wdt_ids, .probe = sp805_wdt_probe, - .remove = __devexit_p(sp805_wdt_remove), + .remove = 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_amba_driver(sp805_wdt_driver); -MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>"); +MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>"); MODULE_DESCRIPTION("ARM SP805 Watchdog Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c new file mode 100644 index 00000000000..3804d5e9bae --- /dev/null +++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c @@ -0,0 +1,108 @@ +/* + * Watchdog driver for the RTC based watchdog in STMP3xxx and i.MX23/28 + * + * Author: Wolfram Sang <w.sang@pengutronix.de> + * + * Copyright (C) 2011-12 Wolfram Sang, Pengutronix + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/stmp3xxx_rtc_wdt.h> + +#define WDOG_TICK_RATE 1000 /* 1 kHz clock */ +#define STMP3XXX_DEFAULT_TIMEOUT 19 +#define STMP3XXX_MAX_TIMEOUT (UINT_MAX / WDOG_TICK_RATE) + +static int heartbeat = STMP3XXX_DEFAULT_TIMEOUT; +module_param(heartbeat, uint, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat period in seconds from 1 to " + __MODULE_STRING(STMP3XXX_MAX_TIMEOUT) ", default " + __MODULE_STRING(STMP3XXX_DEFAULT_TIMEOUT)); + +static int wdt_start(struct watchdog_device *wdd) +{ + struct device *dev = watchdog_get_drvdata(wdd); + struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev); + + pdata->wdt_set_timeout(dev->parent, wdd->timeout * WDOG_TICK_RATE); + return 0; +} + +static int wdt_stop(struct watchdog_device *wdd) +{ + struct device *dev = watchdog_get_drvdata(wdd); + struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev); + + pdata->wdt_set_timeout(dev->parent, 0); + return 0; +} + +static int wdt_set_timeout(struct watchdog_device *wdd, unsigned new_timeout) +{ + wdd->timeout = new_timeout; + return wdt_start(wdd); +} + +static const struct watchdog_info stmp3xxx_wdt_ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "STMP3XXX RTC Watchdog", +}; + +static const struct watchdog_ops stmp3xxx_wdt_ops = { + .owner = THIS_MODULE, + .start = wdt_start, + .stop = wdt_stop, + .set_timeout = wdt_set_timeout, +}; + +static struct watchdog_device stmp3xxx_wdd = { + .info = &stmp3xxx_wdt_ident, + .ops = &stmp3xxx_wdt_ops, + .min_timeout = 1, + .max_timeout = STMP3XXX_MAX_TIMEOUT, + .status = WATCHDOG_NOWAYOUT_INIT_STATUS, +}; + +static int stmp3xxx_wdt_probe(struct platform_device *pdev) +{ + int ret; + + watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev); + + stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT); + + ret = watchdog_register_device(&stmp3xxx_wdd); + if (ret < 0) { + dev_err(&pdev->dev, "cannot register watchdog device\n"); + return ret; + } + + dev_info(&pdev->dev, "initialized watchdog with heartbeat %ds\n", + stmp3xxx_wdd.timeout); + return 0; +} + +static int stmp3xxx_wdt_remove(struct platform_device *pdev) +{ + watchdog_unregister_device(&stmp3xxx_wdd); + return 0; +} + +static struct platform_driver stmp3xxx_wdt_driver = { + .driver = { + .name = "stmp3xxx_rtc_wdt", + }, + .probe = stmp3xxx_wdt_probe, + .remove = stmp3xxx_wdt_remove, +}; +module_platform_driver(stmp3xxx_wdt_driver); + +MODULE_DESCRIPTION("STMP3XXX RTC Watchdog Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>"); diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c deleted file mode 100644 index e37d81178b9..00000000000 --- a/drivers/watchdog/stmp3xxx_wdt.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * 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 <linux/module.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 unexpectedly\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, -}; - -module_platform_driver(platform_wdt_driver); - -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/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c new file mode 100644 index 00000000000..693b9d2c6e3 --- /dev/null +++ b/drivers/watchdog/sunxi_wdt.c @@ -0,0 +1,237 @@ +/* + * sunxi Watchdog Driver + * + * Copyright (c) 2013 Carlo Caione + * 2012 Henrik Nordstrom + * + * 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 xen_wdt.c + * (c) Copyright 2010 Novell, Inc. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <linux/watchdog.h> + +#define WDT_MAX_TIMEOUT 16 +#define WDT_MIN_TIMEOUT 1 +#define WDT_MODE_TIMEOUT(n) ((n) << 3) +#define WDT_TIMEOUT_MASK WDT_MODE_TIMEOUT(0x0F) + +#define WDT_CTRL 0x00 +#define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) + +#define WDT_MODE 0x04 +#define WDT_MODE_EN (1 << 0) +#define WDT_MODE_RST_EN (1 << 1) + +#define DRV_NAME "sunxi-wdt" +#define DRV_VERSION "1.0" + +static bool nowayout = WATCHDOG_NOWAYOUT; +static unsigned int timeout = WDT_MAX_TIMEOUT; + +struct sunxi_wdt_dev { + struct watchdog_device wdt_dev; + void __iomem *wdt_base; +}; + +/* + * wdt_timeout_map maps the watchdog timer interval value in seconds to + * the value of the register WDT_MODE bit 3:6 + * + * [timeout seconds] = register value + * + */ + +static const int wdt_timeout_map[] = { + [1] = 0x1, /* 1s */ + [2] = 0x2, /* 2s */ + [3] = 0x3, /* 3s */ + [4] = 0x4, /* 4s */ + [5] = 0x5, /* 5s */ + [6] = 0x6, /* 6s */ + [8] = 0x7, /* 8s */ + [10] = 0x8, /* 10s */ + [12] = 0x9, /* 12s */ + [14] = 0xA, /* 14s */ + [16] = 0xB, /* 16s */ +}; + +static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base = sunxi_wdt->wdt_base; + + iowrite32(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); + + return 0; +} + +static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base = sunxi_wdt->wdt_base; + u32 reg; + + if (wdt_timeout_map[timeout] == 0) + timeout++; + + sunxi_wdt->wdt_dev.timeout = timeout; + + reg = ioread32(wdt_base + WDT_MODE); + reg &= ~WDT_TIMEOUT_MASK; + reg |= WDT_MODE_TIMEOUT(wdt_timeout_map[timeout]); + iowrite32(reg, wdt_base + WDT_MODE); + + sunxi_wdt_ping(wdt_dev); + + return 0; +} + +static int sunxi_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base = sunxi_wdt->wdt_base; + + iowrite32(0, wdt_base + WDT_MODE); + + return 0; +} + +static int sunxi_wdt_start(struct watchdog_device *wdt_dev) +{ + u32 reg; + struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base = sunxi_wdt->wdt_base; + int ret; + + ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev, + sunxi_wdt->wdt_dev.timeout); + if (ret < 0) + return ret; + + reg = ioread32(wdt_base + WDT_MODE); + reg |= (WDT_MODE_RST_EN | WDT_MODE_EN); + iowrite32(reg, wdt_base + WDT_MODE); + + return 0; +} + +static const struct watchdog_info sunxi_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops sunxi_wdt_ops = { + .owner = THIS_MODULE, + .start = sunxi_wdt_start, + .stop = sunxi_wdt_stop, + .ping = sunxi_wdt_ping, + .set_timeout = sunxi_wdt_set_timeout, +}; + +static int sunxi_wdt_probe(struct platform_device *pdev) +{ + struct sunxi_wdt_dev *sunxi_wdt; + struct resource *res; + int err; + + sunxi_wdt = devm_kzalloc(&pdev->dev, sizeof(*sunxi_wdt), GFP_KERNEL); + if (!sunxi_wdt) + return -EINVAL; + + platform_set_drvdata(pdev, sunxi_wdt); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sunxi_wdt->wdt_base)) + return PTR_ERR(sunxi_wdt->wdt_base); + + sunxi_wdt->wdt_dev.info = &sunxi_wdt_info; + sunxi_wdt->wdt_dev.ops = &sunxi_wdt_ops; + sunxi_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT; + sunxi_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT; + sunxi_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT; + sunxi_wdt->wdt_dev.parent = &pdev->dev; + + watchdog_init_timeout(&sunxi_wdt->wdt_dev, timeout, &pdev->dev); + watchdog_set_nowayout(&sunxi_wdt->wdt_dev, nowayout); + + watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt); + + sunxi_wdt_stop(&sunxi_wdt->wdt_dev); + + err = watchdog_register_device(&sunxi_wdt->wdt_dev); + if (unlikely(err)) + return err; + + dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", + sunxi_wdt->wdt_dev.timeout, nowayout); + + return 0; +} + +static int sunxi_wdt_remove(struct platform_device *pdev) +{ + struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&sunxi_wdt->wdt_dev); + watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL); + + return 0; +} + +static void sunxi_wdt_shutdown(struct platform_device *pdev) +{ + struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); + + sunxi_wdt_stop(&sunxi_wdt->wdt_dev); +} + +static const struct of_device_id sunxi_wdt_dt_ids[] = { + { .compatible = "allwinner,sun4i-a10-wdt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); + +static struct platform_driver sunxi_wdt_driver = { + .probe = sunxi_wdt_probe, + .remove = sunxi_wdt_remove, + .shutdown = sunxi_wdt_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = sunxi_wdt_dt_ids, + }, +}; + +module_platform_driver(sunxi_wdt_driver); + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " + "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Carlo Caione <carlo.caione@gmail.com>"); +MODULE_AUTHOR("Henrik Nordstrom <henrik@henriknordstrom.net>"); +MODULE_DESCRIPTION("sunxi WatchDog Timer Driver"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c new file mode 100644 index 00000000000..750e2a26cb1 --- /dev/null +++ b/drivers/watchdog/tegra_wdt.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +/* minimum and maximum watchdog trigger timeout, in seconds */ +#define MIN_WDT_TIMEOUT 1 +#define MAX_WDT_TIMEOUT 255 + +/* + * Base of the WDT registers, from the timer base address. There are + * actually 5 watchdogs that can be configured (by pairing with an available + * timer), at bases 0x100 + (WDT ID) * 0x20, where WDT ID is 0 through 4. + * This driver only configures the first watchdog (WDT ID 0). + */ +#define WDT_BASE 0x100 +#define WDT_ID 0 + +/* + * Register base of the timer that's selected for pairing with the watchdog. + * This driver arbitrarily uses timer 5, which is currently unused by + * other drivers (in particular, the Tegra clocksource driver). If this + * needs to change, take care that the new timer is not used by the + * clocksource driver. + */ +#define WDT_TIMER_BASE 0x60 +#define WDT_TIMER_ID 5 + +/* WDT registers */ +#define WDT_CFG 0x0 +#define WDT_CFG_PERIOD_SHIFT 4 +#define WDT_CFG_PERIOD_MASK 0xff +#define WDT_CFG_INT_EN (1 << 12) +#define WDT_CFG_PMC2CAR_RST_EN (1 << 15) +#define WDT_STS 0x4 +#define WDT_STS_COUNT_SHIFT 4 +#define WDT_STS_COUNT_MASK 0xff +#define WDT_STS_EXP_SHIFT 12 +#define WDT_STS_EXP_MASK 0x3 +#define WDT_CMD 0x8 +#define WDT_CMD_START_COUNTER (1 << 0) +#define WDT_CMD_DISABLE_COUNTER (1 << 1) +#define WDT_UNLOCK (0xc) +#define WDT_UNLOCK_PATTERN (0xc45a << 0) + +/* Timer registers */ +#define TIMER_PTV 0x0 +#define TIMER_EN (1 << 31) +#define TIMER_PERIODIC (1 << 30) + +struct tegra_wdt { + struct watchdog_device wdd; + void __iomem *wdt_regs; + void __iomem *tmr_regs; +}; + +#define WDT_HEARTBEAT 120 +static int heartbeat = WDT_HEARTBEAT; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, + "Watchdog heartbeats in seconds. (default = " + __MODULE_STRING(WDT_HEARTBEAT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int tegra_wdt_start(struct watchdog_device *wdd) +{ + struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); + u32 val; + + /* + * This thing has a fixed 1MHz clock. Normally, we would set the + * period to 1 second by writing 1000000ul, but the watchdog system + * reset actually occurs on the 4th expiration of this counter, + * so we set the period to 1/4 of this amount. + */ + val = 1000000ul / 4; + val |= (TIMER_EN | TIMER_PERIODIC); + writel(val, wdt->tmr_regs + TIMER_PTV); + + /* + * Set number of periods and start counter. + * + * Interrupt handler is not required for user space + * WDT accesses, since the caller is responsible to ping the + * WDT to reset the counter before expiration, through ioctls. + */ + val = WDT_TIMER_ID | + (wdd->timeout << WDT_CFG_PERIOD_SHIFT) | + WDT_CFG_PMC2CAR_RST_EN; + writel(val, wdt->wdt_regs + WDT_CFG); + + writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD); + + return 0; +} + +static int tegra_wdt_stop(struct watchdog_device *wdd) +{ + struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); + + writel(WDT_UNLOCK_PATTERN, wdt->wdt_regs + WDT_UNLOCK); + writel(WDT_CMD_DISABLE_COUNTER, wdt->wdt_regs + WDT_CMD); + writel(0, wdt->tmr_regs + TIMER_PTV); + + return 0; +} + +static int tegra_wdt_ping(struct watchdog_device *wdd) +{ + struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); + + writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD); + + return 0; +} + +static int tegra_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->timeout = timeout; + + if (watchdog_active(wdd)) + return tegra_wdt_start(wdd); + + return 0; +} + +static unsigned int tegra_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); + u32 val; + int count; + int exp; + + val = readl(wdt->wdt_regs + WDT_STS); + + /* Current countdown (from timeout) */ + count = (val >> WDT_STS_COUNT_SHIFT) & WDT_STS_COUNT_MASK; + + /* Number of expirations (we are waiting for the 4th expiration) */ + exp = (val >> WDT_STS_EXP_SHIFT) & WDT_STS_EXP_MASK; + + /* + * The entire thing is divided by 4 because we are ticking down 4 times + * faster due to needing to wait for the 4th expiration. + */ + return (((3 - exp) * wdd->timeout) + count) / 4; +} + +static const struct watchdog_info tegra_wdt_info = { + .options = WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .firmware_version = 0, + .identity = "Tegra Watchdog", +}; + +static struct watchdog_ops tegra_wdt_ops = { + .owner = THIS_MODULE, + .start = tegra_wdt_start, + .stop = tegra_wdt_stop, + .ping = tegra_wdt_ping, + .set_timeout = tegra_wdt_set_timeout, + .get_timeleft = tegra_wdt_get_timeleft, +}; + +static int tegra_wdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdd; + struct tegra_wdt *wdt; + struct resource *res; + void __iomem *regs; + int ret; + + /* This is the timer base. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + /* + * Allocate our watchdog driver data, which has the + * struct watchdog_device nested within it. + */ + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + /* Initialize struct tegra_wdt. */ + wdt->wdt_regs = regs + WDT_BASE; + wdt->tmr_regs = regs + WDT_TIMER_BASE; + + /* Initialize struct watchdog_device. */ + wdd = &wdt->wdd; + wdd->timeout = heartbeat; + wdd->info = &tegra_wdt_info; + wdd->ops = &tegra_wdt_ops; + wdd->min_timeout = MIN_WDT_TIMEOUT; + wdd->max_timeout = MAX_WDT_TIMEOUT; + + watchdog_set_drvdata(wdd, wdt); + + watchdog_set_nowayout(wdd, nowayout); + + ret = watchdog_register_device(wdd); + if (ret) { + dev_err(&pdev->dev, + "failed to register watchdog device\n"); + return ret; + } + + platform_set_drvdata(pdev, wdt); + + dev_info(&pdev->dev, + "initialized (heartbeat = %d sec, nowayout = %d)\n", + heartbeat, nowayout); + + return 0; +} + +static int tegra_wdt_remove(struct platform_device *pdev) +{ + struct tegra_wdt *wdt = platform_get_drvdata(pdev); + + tegra_wdt_stop(&wdt->wdd); + + watchdog_unregister_device(&wdt->wdd); + + dev_info(&pdev->dev, "removed wdt\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_wdt_runtime_suspend(struct device *dev) +{ + struct tegra_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + tegra_wdt_stop(&wdt->wdd); + + return 0; +} + +static int tegra_wdt_runtime_resume(struct device *dev) +{ + struct tegra_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + tegra_wdt_start(&wdt->wdd); + + return 0; +} +#endif + +static const struct of_device_id tegra_wdt_of_match[] = { + { .compatible = "nvidia,tegra30-timer", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_wdt_of_match); + +static const struct dev_pm_ops tegra_wdt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_wdt_runtime_suspend, + tegra_wdt_runtime_resume) +}; + +static struct platform_driver tegra_wdt_driver = { + .probe = tegra_wdt_probe, + .remove = tegra_wdt_remove, + .driver = { + .owner = THIS_MODULE, + .name = "tegra-wdt", + .pm = &tegra_wdt_pm_ops, + .of_match_table = tegra_wdt_of_match, + }, +}; +module_platform_driver(tegra_wdt_driver); + +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_DESCRIPTION("Tegra Watchdog Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index 1490293dc7d..afa9d6ef353 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c @@ -34,8 +34,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. " __MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT) ")"); -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); /** @@ -61,7 +61,7 @@ struct ts72xx_wdt { struct platform_device *pdev; }; -struct platform_device *ts72xx_wdt_pdev; +static struct platform_device *ts72xx_wdt_pdev; /* * TS-72xx Watchdog supports following timeouts (value written @@ -192,7 +192,7 @@ static int ts72xx_wdt_open(struct inode *inode, struct file *file) dev_err(&wdt->pdev->dev, "failed to convert timeout (%d) to register value\n", timeout); - return -EINVAL; + return regval; } if (mutex_lock_interruptible(&wdt->lock)) @@ -305,12 +305,14 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case WDIOC_GETSUPPORT: - error = copy_to_user(argp, &winfo, sizeof(winfo)); + if (copy_to_user(argp, &winfo, sizeof(winfo))) + error = -EFAULT; break; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: - return put_user(0, p); + error = put_user(0, p); + break; case WDIOC_KEEPALIVE: ts72xx_wdt_kick(wdt); @@ -319,10 +321,9 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd, case WDIOC_SETOPTIONS: { int options; - if (get_user(options, p)) { - error = -EFAULT; + error = get_user(options, p); + if (error) break; - } error = -EINVAL; @@ -340,30 +341,26 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd, case WDIOC_SETTIMEOUT: { int new_timeout; + int regval; - 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); - } - } + error = get_user(new_timeout, p); if (error) break; + regval = timeout_to_regval(new_timeout); + if (regval < 0) { + error = regval; + break; + } + ts72xx_wdt_stop(wdt); + wdt->regval = regval; + ts72xx_wdt_start(wdt); + /*FALLTHROUGH*/ } case WDIOC_GETTIMEOUT: - if (put_user(regval_to_timeout(wdt->regval), p)) - error = -EFAULT; + error = put_user(regval_to_timeout(wdt->regval), p); break; default: @@ -390,59 +387,25 @@ static struct miscdevice ts72xx_wdt_miscdev = { .fops = &ts72xx_wdt_fops, }; -static __devinit int ts72xx_wdt_probe(struct platform_device *pdev) +static 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"); + wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL); + if (!wdt) 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; - } + wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1); + if (IS_ERR(wdt->control_reg)) + return PTR_ERR(wdt->control_reg); 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; - } + wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2); + if (IS_ERR(wdt->feed_reg)) + return PTR_ERR(wdt->feed_reg); platform_set_drvdata(pdev, wdt); ts72xx_wdt_pdev = pdev; @@ -455,51 +418,26 @@ static __devinit int ts72xx_wdt_probe(struct platform_device *pdev) error = misc_register(&ts72xx_wdt_miscdev); if (error) { dev_err(&pdev->dev, "failed to register miscdev\n"); - goto fail_unmap_feed; + return error; } 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) +static 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), + .remove = ts72xx_wdt_remove, .driver = { .name = "ts72xx-wdt", .owner = THIS_MODULE, diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c index 0764c6239b9..2d4535dc267 100644 --- a/drivers/watchdog/twl4030_wdt.c +++ b/drivers/watchdog/twl4030_wdt.c @@ -22,202 +22,85 @@ #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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 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, + return twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, val, TWL4030_WATCHDOG_CFG_REG_OFFS); } -static int twl4030_wdt_enable(struct twl4030_wdt *wdt) +static int twl4030_wdt_start(struct watchdog_device *wdt) { - return twl4030_wdt_write(wdt->timer_margin + 1); + return twl4030_wdt_write(wdt->timeout + 1); } -static int twl4030_wdt_disable(struct twl4030_wdt *wdt) +static int twl4030_wdt_stop(struct watchdog_device *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) +static int twl4030_wdt_set_timeout(struct watchdog_device *wdt, + unsigned int timeout) { - 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; - } - + wdt->timeout = timeout; 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 watchdog_info twl4030_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "TWL4030 Watchdog", +}; -static const struct file_operations twl4030_wdt_fops = { +static const struct watchdog_ops twl4030_wdt_ops = { .owner = THIS_MODULE, - .llseek = no_llseek, - .open = twl4030_wdt_open, - .release = twl4030_wdt_release, - .unlocked_ioctl = twl4030_wdt_ioctl, - .write = twl4030_wdt_write_fop, + .start = twl4030_wdt_start, + .stop = twl4030_wdt_stop, + .set_timeout = twl4030_wdt_set_timeout, }; -static int __devinit twl4030_wdt_probe(struct platform_device *pdev) +static int twl4030_wdt_probe(struct platform_device *pdev) { int ret = 0; - struct twl4030_wdt *wdt; + struct watchdog_device *wdt; - wdt = kzalloc(sizeof(struct twl4030_wdt), GFP_KERNEL); + wdt = devm_kzalloc(&pdev->dev, sizeof(*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"; + wdt->info = &twl4030_wdt_info; + wdt->ops = &twl4030_wdt_ops; + wdt->status = 0; + wdt->timeout = 30; + wdt->min_timeout = 1; + wdt->max_timeout = 30; + watchdog_set_nowayout(wdt, nowayout); platform_set_drvdata(pdev, wdt); - twl4030_wdt_dev = pdev; + twl4030_wdt_stop(wdt); - 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; + ret = watchdog_register_device(wdt); + if (ret) return ret; - } + return 0; } -static int __devexit twl4030_wdt_remove(struct platform_device *pdev) +static int twl4030_wdt_remove(struct platform_device *pdev) { - struct twl4030_wdt *wdt = platform_get_drvdata(pdev); + struct watchdog_device *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; + watchdog_unregister_device(wdt); return 0; } @@ -225,18 +108,18 @@ static int __devexit twl4030_wdt_remove(struct platform_device *pdev) #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); + struct watchdog_device *wdt = platform_get_drvdata(pdev); + if (watchdog_active(wdt)) + return twl4030_wdt_stop(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); + struct watchdog_device *wdt = platform_get_drvdata(pdev); + if (watchdog_active(wdt)) + return twl4030_wdt_start(wdt); return 0; } @@ -245,14 +128,21 @@ static int twl4030_wdt_resume(struct platform_device *pdev) #define twl4030_wdt_resume NULL #endif +static const struct of_device_id twl_wdt_of_match[] = { + { .compatible = "ti,twl4030-wdt", }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_wdt_of_match); + static struct platform_driver twl4030_wdt_driver = { .probe = twl4030_wdt_probe, - .remove = __devexit_p(twl4030_wdt_remove), + .remove = twl4030_wdt_remove, .suspend = twl4030_wdt_suspend, .resume = twl4030_wdt_resume, .driver = { - .owner = THIS_MODULE, - .name = "twl4030_wdt", + .owner = THIS_MODULE, + .name = "twl4030_wdt", + .of_match_table = twl_wdt_of_match, }, }; @@ -260,6 +150,5 @@ module_platform_driver(twl4030_wdt_driver); 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 index 9e9ed7bfabc..6a447e321dd 100644 --- a/drivers/watchdog/txx9wdt.c +++ b/drivers/watchdog/txx9wdt.c @@ -7,177 +7,98 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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 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) #define TIMER_MARGIN 60 /* Default is 60 seconds */ -static int timeout = TIMER_MARGIN; /* in seconds */ -module_param(timeout, int, 0); +static unsigned int timeout = TIMER_MARGIN; /* in seconds */ +module_param(timeout, uint, 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 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) +static int txx9wdt_ping(struct watchdog_device *wdt_dev) { spin_lock(&txx9_lock); __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr); spin_unlock(&txx9_lock); + return 0; } -static void txx9wdt_start(void) +static int txx9wdt_start(struct watchdog_device *wdt_dev) { spin_lock(&txx9_lock); - __raw_writel(WD_TIMER_CLK * timeout, &txx9wdt_reg->cpra); + __raw_writel(WD_TIMER_CLK * wdt_dev->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); + return 0; } -static void txx9wdt_stop(void) +static int txx9wdt_stop(struct watchdog_device *wdt_dev) { 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) +static int txx9wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int new_timeout) { - 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; + wdt_dev->timeout = new_timeout; + txx9wdt_stop(wdt_dev); + txx9wdt_start(wdt_dev); + return 0; } -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 watchdog_info txx9wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Hardware Watchdog for TXx9", +}; -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 const struct watchdog_ops txx9wdt_ops = { + .owner = THIS_MODULE, + .start = txx9wdt_start, + .stop = txx9wdt_stop, + .ping = txx9wdt_ping, + .set_timeout = txx9wdt_set_timeout, }; -static struct miscdevice txx9wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &txx9wdt_fops, +static struct watchdog_device txx9wdt = { + .info = &txx9wdt_info, + .ops = &txx9wdt_ops, }; static int __init txx9wdt_probe(struct platform_device *dev) @@ -199,27 +120,27 @@ static int __init txx9wdt_probe(struct platform_device *dev) } 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) { + txx9wdt_reg = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(txx9wdt_reg)) { + ret = PTR_ERR(txx9wdt_reg); goto exit; } - printk(KERN_INFO "Hardware Watchdog Timer for TXx9: " - "timeout=%d sec (max %ld) (nowayout= %d)\n", - timeout, WD_MAX_TIMEOUT, nowayout); + if (timeout < 1 || timeout > WD_MAX_TIMEOUT) + timeout = TIMER_MARGIN; + txx9wdt.timeout = timeout; + txx9wdt.min_timeout = 1; + txx9wdt.max_timeout = WD_MAX_TIMEOUT; + watchdog_set_nowayout(&txx9wdt, nowayout); + + ret = watchdog_register_device(&txx9wdt); + if (ret) + goto exit; + + pr_info("Hardware Watchdog Timer: 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); @@ -230,7 +151,7 @@ exit: static int __exit txx9wdt_remove(struct platform_device *dev) { - misc_deregister(&txx9wdt_miscdev); + watchdog_unregister_device(&txx9wdt); clk_disable(txx9_imclk); clk_put(txx9_imclk); return 0; @@ -238,7 +159,7 @@ static int __exit txx9wdt_remove(struct platform_device *dev) static void txx9wdt_shutdown(struct platform_device *dev) { - txx9wdt_stop(); + txx9wdt_stop(&txx9wdt); } static struct platform_driver txx9wdt_driver = { @@ -250,20 +171,8 @@ static struct platform_driver txx9wdt_driver = { }, }; -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_platform_driver_probe(txx9wdt_driver, txx9wdt_probe); MODULE_DESCRIPTION("TXx9 Watchdog Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); MODULE_ALIAS("platform:txx9wdt"); diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c new file mode 100644 index 00000000000..5aed9d7ad47 --- /dev/null +++ b/drivers/watchdog/ux500_wdt.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) ST-Ericsson SA 2011-2013 + * + * License Terms: GNU General Public License v2 + * + * Author: Mathieu Poirier <mathieu.poirier@linaro.org> for ST-Ericsson + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/uaccess.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/platform_data/ux500_wdt.h> + +#include <linux/mfd/dbx500-prcmu.h> + +#define WATCHDOG_TIMEOUT 600 /* 10 minutes */ + +#define WATCHDOG_MIN 0 +#define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */ +#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */ + +static unsigned int timeout = WATCHDOG_TIMEOUT; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) "."); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int ux500_wdt_start(struct watchdog_device *wdd) +{ + return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_stop(struct watchdog_device *wdd) +{ + return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_keepalive(struct watchdog_device *wdd) +{ + return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + ux500_wdt_stop(wdd); + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(wdd); + + return 0; +} + +static const struct watchdog_info ux500_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Ux500 WDT", + .firmware_version = 1, +}; + +static const struct watchdog_ops ux500_wdt_ops = { + .owner = THIS_MODULE, + .start = ux500_wdt_start, + .stop = ux500_wdt_stop, + .ping = ux500_wdt_keepalive, + .set_timeout = ux500_wdt_set_timeout, +}; + +static struct watchdog_device ux500_wdt = { + .info = &ux500_wdt_info, + .ops = &ux500_wdt_ops, + .min_timeout = WATCHDOG_MIN, + .max_timeout = WATCHDOG_MAX32, +}; + +static int ux500_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct ux500_wdt_data *pdata = dev_get_platdata(&pdev->dev); + + if (pdata) { + if (pdata->timeout > 0) + timeout = pdata->timeout; + if (pdata->has_28_bits_resolution) + ux500_wdt.max_timeout = WATCHDOG_MAX28; + } + + watchdog_set_nowayout(&ux500_wdt, nowayout); + + /* disable auto off on sleep */ + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + + /* set HW initial value */ + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + + ret = watchdog_register_device(&ux500_wdt); + if (ret) + return ret; + + dev_info(&pdev->dev, "initialized\n"); + + return 0; +} + +static int ux500_wdt_remove(struct platform_device *dev) +{ + watchdog_unregister_device(&ux500_wdt); + + return 0; +} + +#ifdef CONFIG_PM +static int ux500_wdt_suspend(struct platform_device *pdev, + pm_message_t state) +{ + if (watchdog_active(&ux500_wdt)) { + ux500_wdt_stop(&ux500_wdt); + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); + + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(&ux500_wdt); + } + return 0; +} + +static int ux500_wdt_resume(struct platform_device *pdev) +{ + if (watchdog_active(&ux500_wdt)) { + ux500_wdt_stop(&ux500_wdt); + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(&ux500_wdt); + } + return 0; +} +#else +#define ux500_wdt_suspend NULL +#define ux500_wdt_resume NULL +#endif + +static struct platform_driver ux500_wdt_driver = { + .probe = ux500_wdt_probe, + .remove = ux500_wdt_remove, + .suspend = ux500_wdt_suspend, + .resume = ux500_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "ux500_wdt", + }, +}; + +module_platform_driver(ux500_wdt_driver); + +MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>"); +MODULE_DESCRIPTION("Ux500 Watchdog Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ux500_wdt"); diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c index 8f07dd4bd94..56369c4f196 100644 --- a/drivers/watchdog/via_wdt.c +++ b/drivers/watchdog/via_wdt.c @@ -10,6 +10,9 @@ * Caveat: PnP must be enabled in BIOS to allow full access to watchdog * control registers. If not, the watchdog must be configured in BIOS manually. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/device.h> #include <linux/io.h> #include <linux/jiffies.h> @@ -55,8 +58,8 @@ module_param(timeout, int, 0); MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, between 1 and 1023 " "(default = " __MODULE_STRING(WDT_TIMEOUT) ")"); -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default = " __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -88,7 +91,7 @@ static inline void wdt_reset(void) static void wdt_timer_tick(unsigned long data) { if (time_before(jiffies, next_heartbeat) || - (!test_bit(WDOG_ACTIVE, &wdt_dev.status))) { + (!watchdog_active(&wdt_dev))) { wdt_reset(); mod_timer(&timer, jiffies + WDT_HEARTBEAT); } else @@ -98,7 +101,7 @@ static void wdt_timer_tick(unsigned long data) static int wdt_ping(struct watchdog_device *wdd) { /* calculate when the next userspace timeout will be */ - next_heartbeat = jiffies + timeout * HZ; + next_heartbeat = jiffies + wdd->timeout * HZ; return 0; } @@ -106,7 +109,7 @@ static int wdt_start(struct watchdog_device *wdd) { unsigned int ctl = readl(wdt_mem); - writel(timeout, wdt_mem + VIA_WDT_COUNT); + writel(wdd->timeout, wdt_mem + VIA_WDT_COUNT); writel(ctl | VIA_WDT_RUNNING | VIA_WDT_TRIGGER, wdt_mem); wdt_ping(wdd); mod_timer(&timer, jiffies + WDT_HEARTBEAT); @@ -125,7 +128,7 @@ static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout) { writel(new_timeout, wdt_mem + VIA_WDT_COUNT); - timeout = new_timeout; + wdd->timeout = new_timeout; return 0; } @@ -152,7 +155,7 @@ static struct watchdog_device wdt_dev = { .max_timeout = WDT_TIMEOUT_MAX, }; -static int __devinit wdt_probe(struct pci_dev *pdev, +static int wdt_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { unsigned char conf; @@ -199,6 +202,9 @@ static int __devinit wdt_probe(struct pci_dev *pdev, goto err_out_release; } + if (timeout < 1 || timeout > WDT_TIMEOUT_MAX) + timeout = WDT_TIMEOUT; + wdt_dev.timeout = timeout; watchdog_set_nowayout(&wdt_dev, nowayout); if (readl(wdt_mem) & VIA_WDT_FIRED) @@ -223,17 +229,17 @@ err_out_disable_device: return ret; } -static void __devexit wdt_remove(struct pci_dev *pdev) +static void wdt_remove(struct pci_dev *pdev) { watchdog_unregister_device(&wdt_dev); - del_timer(&timer); + del_timer_sync(&timer); iounmap(wdt_mem); release_mem_region(mmio, VIA_WDT_MMIO_LEN); release_resource(&wdt_res); pci_disable_device(pdev); } -static DEFINE_PCI_DEVICE_TABLE(wdt_pci_table) = { +static const struct pci_device_id wdt_pci_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) }, @@ -244,23 +250,10 @@ static struct pci_driver wdt_driver = { .name = "via_wdt", .id_table = wdt_pci_table, .probe = wdt_probe, - .remove = __devexit_p(wdt_remove), + .remove = wdt_remove, }; -static int __init wdt_init(void) -{ - if (timeout < 1 || timeout > WDT_TIMEOUT_MAX) - timeout = WDT_TIMEOUT; - return pci_register_driver(&wdt_driver); -} - -static void __exit wdt_exit(void) -{ - pci_unregister_driver(&wdt_driver); -} - -module_init(wdt_init); -module_exit(wdt_exit); +module_pci_driver(wdt_driver); MODULE_AUTHOR("Marc Vertes"); MODULE_DESCRIPTION("Driver for watchdog timer on VIA chipset"); diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 576a388a116..7165704a3e3 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -1,6 +1,9 @@ /* * w83627hf/thf WDT driver * + * (c) Copyright 2013 Guenter Roeck + * converted to watchdog infrastructure + * * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com> * added support for W83627THF. * @@ -26,47 +29,45 @@ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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); +static int wdt_io; +static int cr_wdt_timeout; /* WDT timeout register */ +static int cr_wdt_control; /* WDT control register */ -/* 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)"); +enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, + w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, + w83667hg_b, nct6775, nct6776, nct6779 }; -static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ +static int 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +static int early_disable; +module_param(early_disable, int, 0); +MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); + /* * Kernel methods. */ @@ -76,238 +77,220 @@ MODULE_PARM_DESC(nowayout, (same as EFER) */ #define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ -static void w83627hf_select_wd_register(void) +#define W83627HF_LD_WDT 0x08 + +#define W83627HF_ID 0x52 +#define W83627S_ID 0x59 +#define W83697HF_ID 0x60 +#define W83697UG_ID 0x68 +#define W83637HF_ID 0x70 +#define W83627THF_ID 0x82 +#define W83687THF_ID 0x85 +#define W83627EHF_ID 0x88 +#define W83627DHG_ID 0xa0 +#define W83627UHG_ID 0xa2 +#define W83667HG_ID 0xa5 +#define W83627DHG_P_ID 0xb0 +#define W83667HG_B_ID 0xb3 +#define NCT6775_ID 0xb4 +#define NCT6776_ID 0xc3 +#define NCT6779_ID 0xc5 + +#define W83627HF_WDT_TIMEOUT 0xf6 +#define W83697HF_WDT_TIMEOUT 0xf4 + +#define W83627HF_WDT_CONTROL 0xf5 +#define W83697HF_WDT_CONTROL 0xf3 + +static void superio_outb(int reg, int val) +{ + outb(reg, WDT_EFER); + outb(val, WDT_EFDR); +} + +static inline int superio_inb(int reg) { - unsigned char c; + outb(reg, WDT_EFER); + return inb(WDT_EFDR); +} + +static int superio_enter(void) +{ + if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME)) + return -EBUSY; + 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 */ - } + return 0; +} - 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 superio_select(int ld) +{ + superio_outb(0x07, ld); } -static void w83627hf_unselect_wd_register(void) +static void superio_exit(void) { outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ + release_region(wdt_io, 2); } -/* tyan motherboards seem to set F5 to 0x4C ? - * So explicitly init to appropriate value. */ - -static void w83627hf_init(void) +static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) { + int ret; unsigned char t; - w83627hf_select_wd_register(); + ret = superio_enter(); + if (ret) + 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 */ - } + superio_select(W83627HF_LD_WDT); - 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(); -} + /* set CR30 bit 0 to activate GPIO2 */ + t = superio_inb(0x30); + if (!(t & 0x01)) + superio_outb(0x30, t | 0x01); -static void wdt_set_time(int timeout) -{ - spin_lock(&io_lock); + switch (chip) { + case w83627hf: + case w83627s: + t = superio_inb(0x2B) & ~0x10; + superio_outb(0x2B, t); /* set GPIO24 to WDT0 */ + break; + case w83697hf: + /* Set pin 119 to WDTO# mode (= CR29, WDT0) */ + t = superio_inb(0x29) & ~0x60; + t |= 0x20; + superio_outb(0x29, t); + break; + case w83697ug: + /* Set pin 118 to WDTO# mode */ + t = superio_inb(0x2b) & ~0x04; + superio_outb(0x2b, t); + break; + case w83627thf: + t = (superio_inb(0x2B) & ~0x08) | 0x04; + superio_outb(0x2B, t); /* set GPIO3 to WDT0 */ + break; + case w83627dhg: + case w83627dhg_p: + t = superio_inb(0x2D) & ~0x01; /* PIN77 -> WDT0# */ + superio_outb(0x2D, t); /* set GPIO5 to WDT0 */ + t = superio_inb(cr_wdt_control); + t |= 0x02; /* enable the WDTO# output low pulse + * to the KBRST# pin */ + superio_outb(cr_wdt_control, t); + break; + case w83637hf: + break; + case w83687thf: + t = superio_inb(0x2C) & ~0x80; /* PIN47 -> WDT0# */ + superio_outb(0x2C, t); + break; + case w83627ehf: + case w83627uhg: + case w83667hg: + case w83667hg_b: + case nct6775: + case nct6776: + case nct6779: + /* + * These chips have a fixed WDTO# output pin (W83627UHG), + * or support more than one WDTO# output pin. + * Don't touch its configuration, and hope the BIOS + * does the right thing. + */ + t = superio_inb(cr_wdt_control); + t |= 0x02; /* enable the WDTO# output low pulse + * to the KBRST# pin */ + superio_outb(cr_wdt_control, t); + break; + default: + break; + } - w83627hf_select_wd_register(); + t = superio_inb(cr_wdt_timeout); + if (t != 0) { + if (early_disable) { + pr_warn("Stopping previously enabled watchdog until userland kicks in\n"); + superio_outb(cr_wdt_timeout, 0); + } else { + pr_info("Watchdog already running. Resetting timeout to %d sec\n", + wdog->timeout); + superio_outb(cr_wdt_timeout, wdog->timeout); + } + } - outb_p(0xF6, WDT_EFER); /* Select CRF6 */ - outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */ + /* set second mode & disable keyboard turning off watchdog */ + t = superio_inb(cr_wdt_control) & ~0x0C; + superio_outb(cr_wdt_control, t); - w83627hf_unselect_wd_register(); + /* reset trigger, disable keyboard & mouse turning off watchdog */ + t = superio_inb(0xF7) & ~0xD0; + superio_outb(0xF7, t); - spin_unlock(&io_lock); -} + superio_exit(); -static int wdt_ping(void) -{ - wdt_set_time(timeout); return 0; } -static int wdt_disable(void) +static int wdt_set_time(unsigned int timeout) { - wdt_set_time(0); - return 0; -} + int ret; + + ret = superio_enter(); + if (ret) + return ret; + + superio_select(W83627HF_LD_WDT); + superio_outb(cr_wdt_timeout, timeout); + superio_exit(); -static int wdt_set_heartbeat(int t) -{ - if (t < 1 || t > 255) - return -EINVAL; - timeout = t; return 0; } -static int wdt_get_time(void) +static int wdt_start(struct watchdog_device *wdog) { - int timeleft; - - spin_lock(&io_lock); - - w83627hf_select_wd_register(); - - outb_p(0xF6, WDT_EFER); /* Select CRF6 */ - timeleft = inb_p(WDT_EFDR); /* Read Timeout counter to CRF6 */ - - w83627hf_unselect_wd_register(); - - spin_unlock(&io_lock); - - return timeleft; + return wdt_set_time(wdog->timeout); } -static ssize_t wdt_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static int wdt_stop(struct watchdog_device *wdog) { - 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; + return wdt_set_time(0); } -static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout) { - void __user *argp = (void __user *)arg; - int __user *p = argp; - int timeval; - static const struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | - WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "W83627HF WDT", - }; + wdog->timeout = timeout; - 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(timeval, p)) - return -EFAULT; - if (wdt_set_heartbeat(timeval)) - return -EINVAL; - wdt_ping(); - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(timeout, p); - case WDIOC_GETTIMELEFT: - timeval = wdt_get_time(); - return put_user(timeval, p); - default: - return -ENOTTY; - } return 0; } -static int wdt_open(struct inode *inode, struct file *file) +static unsigned int wdt_get_time(struct watchdog_device *wdog) { - if (test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - /* - * Activate - */ + unsigned int timeleft; + int ret; - wdt_ping(); - return nonseekable_open(inode, file); -} + ret = superio_enter(); + if (ret) + return 0; -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; + superio_select(W83627HF_LD_WDT); + timeleft = superio_inb(cr_wdt_timeout); + superio_exit(); + + return timeleft; } /* * 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 */ + wdt_set_time(0); /* Turn the WDT off */ return NOTIFY_DONE; } @@ -316,19 +299,25 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, * 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 watchdog_info wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "W83627HF Watchdog", +}; + +static struct watchdog_ops wdt_ops = { + .owner = THIS_MODULE, + .start = wdt_start, + .stop = wdt_stop, + .set_timeout = wdt_set_timeout, + .get_timeleft = wdt_get_time, }; -static struct miscdevice wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt_fops, +static struct watchdog_device wdt_dev = { + .info = &wdt_info, + .ops = &wdt_ops, + .timeout = WATCHDOG_TIMEOUT, + .min_timeout = 1, + .max_timeout = 255, }; /* @@ -340,61 +329,152 @@ static struct notifier_block wdt_notifier = { .notifier_call = wdt_notify_sys, }; -static int __init wdt_init(void) +static int wdt_find(int addr) { + u8 val; 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); + cr_wdt_timeout = W83627HF_WDT_TIMEOUT; + cr_wdt_control = W83627HF_WDT_CONTROL; + + ret = superio_enter(); + if (ret) + return ret; + superio_select(W83627HF_LD_WDT); + val = superio_inb(0x20); + switch (val) { + case W83627HF_ID: + ret = w83627hf; + break; + case W83627S_ID: + ret = w83627s; + break; + case W83697HF_ID: + ret = w83697hf; + cr_wdt_timeout = W83697HF_WDT_TIMEOUT; + cr_wdt_control = W83697HF_WDT_CONTROL; + break; + case W83697UG_ID: + ret = w83697ug; + cr_wdt_timeout = W83697HF_WDT_TIMEOUT; + cr_wdt_control = W83697HF_WDT_CONTROL; + break; + case W83637HF_ID: + ret = w83637hf; + break; + case W83627THF_ID: + ret = w83627thf; + break; + case W83687THF_ID: + ret = w83687thf; + break; + case W83627EHF_ID: + ret = w83627ehf; + break; + case W83627DHG_ID: + ret = w83627dhg; + break; + case W83627DHG_P_ID: + ret = w83627dhg_p; + break; + case W83627UHG_ID: + ret = w83627uhg; + break; + case W83667HG_ID: + ret = w83667hg; + break; + case W83667HG_B_ID: + ret = w83667hg_b; + break; + case NCT6775_ID: + ret = nct6775; + break; + case NCT6776_ID: + ret = nct6776; + break; + case NCT6779_ID: + ret = nct6779; + break; + case 0xff: + ret = -ENODEV; + break; + default: + ret = -ENODEV; + pr_err("Unsupported chip ID: 0x%02x\n", val); + break; } + superio_exit(); + return ret; +} - 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; +static int __init wdt_init(void) +{ + int ret; + int chip; + const char * const chip_name[] = { + "W83627HF", + "W83627S", + "W83697HF", + "W83697UG", + "W83637HF", + "W83627THF", + "W83687THF", + "W83627EHF", + "W83627DHG", + "W83627UHG", + "W83667HG", + "W83667DHG-P", + "W83667HG-B", + "NCT6775", + "NCT6776", + "NCT6779", + }; + + wdt_io = 0x2e; + chip = wdt_find(0x2e); + if (chip < 0) { + wdt_io = 0x4e; + chip = wdt_find(0x4e); + if (chip < 0) + return chip; } - w83627hf_init(); + pr_info("WDT driver for %s Super I/O chip initialising\n", + chip_name[chip]); + + watchdog_init_timeout(&wdt_dev, timeout, NULL); + watchdog_set_nowayout(&wdt_dev, nowayout); + + ret = w83627hf_init(&wdt_dev, chip); + if (ret) { + pr_err("failed to initialize watchdog (err=%d)\n", ret); + return ret; + } ret = register_reboot_notifier(&wdt_notifier); if (ret != 0) { - printk(KERN_ERR PFX - "cannot register reboot notifier (err=%d)\n", ret); - goto unreg_regions; + pr_err("cannot register reboot notifier (err=%d)\n", ret); + return ret; } - ret = misc_register(&wdt_miscdev); - if (ret != 0) { - printk(KERN_ERR PFX - "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); + ret = watchdog_register_device(&wdt_dev); + if (ret) goto unreg_reboot; - } - printk(KERN_INFO PFX - "initialized. timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); + pr_info("initialized. timeout=%d sec (nowayout=%d)\n", + wdt_dev.timeout, nowayout); -out: return ret; + unreg_reboot: unregister_reboot_notifier(&wdt_notifier); -unreg_regions: - release_region(wdt_io, 1); - goto out; + return ret; } static void __exit wdt_exit(void) { - misc_deregister(&wdt_miscdev); + watchdog_unregister_device(&wdt_dev); unregister_reboot_notifier(&wdt_notifier); - release_region(wdt_io, 1); } module_init(wdt_init); @@ -403,4 +483,3 @@ 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 deleted file mode 100644 index af08972de50..00000000000 --- a/drivers/watchdog/w83697hf_wdt.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * 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 deleted file mode 100644 index be9c4d839e1..00000000000 --- a/drivers/watchdog/w83697ug_wdt.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - * 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 index 24587d2060c..f0483c75ed3 100644 --- a/drivers/watchdog/w83877f_wdt.c +++ b/drivers/watchdog/w83877f_wdt.c @@ -42,6 +42,8 @@ * daemon always getting scheduled within that time frame. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -56,10 +58,8 @@ #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 @@ -91,8 +91,8 @@ MODULE_PARM_DESC(timeout, __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -126,8 +126,7 @@ static void wdt_timer_ping(unsigned long data) spin_unlock(&wdt_spinlock); } else - printk(KERN_WARNING PFX - "Heartbeat lost! Will not ping the watchdog\n"); + pr_warn("Heartbeat lost! Will not ping the watchdog\n"); } /* @@ -165,7 +164,7 @@ static void wdt_startup(void) wdt_change(WDT_ENABLE); - printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); + pr_info("Watchdog timer is now enabled\n"); } static void wdt_turnoff(void) @@ -175,7 +174,7 @@ static void wdt_turnoff(void) wdt_change(WDT_DISABLE); - printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); + pr_info("Watchdog timer is now disabled...\n"); } static void wdt_keepalive(void) @@ -234,8 +233,7 @@ static int fop_close(struct inode *inode, struct file *file) wdt_turnoff(); else { del_timer(&timer); - printk(KERN_CRIT PFX - "device file closed unexpectedly. Will not stop the WDT!\n"); + pr_crit("device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &wdt_is_open); wdt_expect_close = 0; @@ -357,42 +355,37 @@ static int __init w83877f_wdt_init(void) 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); + pr_info("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); + pr_err("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); + pr_err("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); + pr_err("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); + pr_err("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", + pr_info("WDT driver for W83877F initialised. timeout=%d sec (nowayout=%d)\n", timeout, nowayout); return 0; @@ -413,4 +406,3 @@ 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 index 6e6743d1066..91bf55a2002 100644 --- a/drivers/watchdog/w83977f_wdt.c +++ b/drivers/watchdog/w83977f_wdt.c @@ -15,6 +15,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -29,12 +31,9 @@ #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) @@ -59,8 +58,8 @@ MODULE_PARM_DESC(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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -131,7 +130,7 @@ static int wdt_start(void) spin_unlock_irqrestore(&spinlock, flags); - printk(KERN_INFO PFX "activated.\n"); + pr_info("activated\n"); return 0; } @@ -185,7 +184,7 @@ static int wdt_stop(void) spin_unlock_irqrestore(&spinlock, flags); - printk(KERN_INFO PFX "shutdown.\n"); + pr_info("shutdown\n"); return 0; } @@ -313,8 +312,7 @@ static int wdt_release(struct inode *inode, struct file *file) clear_bit(0, &timer_alive); } else { wdt_keepalive(); - printk(KERN_CRIT PFX - "unexpected close, not stopping watchdog!\n"); + pr_crit("unexpected close, not stopping watchdog!\n"); } expect_close = 0; return 0; @@ -471,7 +469,7 @@ static int __init w83977f_wdt_init(void) { int rc; - printk(KERN_INFO PFX DRIVER_VERSION); + pr_info("driver v%s\n", WATCHDOG_VERSION); /* * Check that the timeout value is within it's range; @@ -479,36 +477,31 @@ static int __init w83977f_wdt_init(void) */ 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); + pr_info("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); + pr_err("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); + pr_err("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); + pr_err("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); + pr_info("initialized. timeout=%d sec (nowayout=%d testmode=%d)\n", + timeout, nowayout, testmode); return 0; @@ -534,4 +527,3 @@ 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 index c3c3188c34d..db0da7ea4fd 100644 --- a/drivers/watchdog/wafer5823wdt.c +++ b/drivers/watchdog/wafer5823wdt.c @@ -26,6 +26,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/miscdevice.h> @@ -65,8 +67,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -203,8 +205,7 @@ 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"); + pr_crit("WDT device closed unexpectedly. WDT will not stop!\n"); wafwdt_ping(); } clear_bit(0, &wafwdt_is_open); @@ -256,49 +257,42 @@ static int __init wafwdt_init(void) { int ret; - printk(KERN_INFO - "WDT driver for Wafer 5823 single board computer initialising.\n"); + pr_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); + pr_info("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); + pr_err("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); + pr_err("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); + pr_err("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); + pr_err("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", + pr_info("initialized. timeout=%d sec (nowayout=%d)\n", timeout, nowayout); return ret; @@ -328,6 +322,5 @@ 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/watchdog_core.c b/drivers/watchdog/watchdog_core.c index cfa1a1518aa..cec9b559647 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -34,8 +34,69 @@ #include <linux/kernel.h> /* For printk/panic/... */ #include <linux/watchdog.h> /* For watchdog specific items */ #include <linux/init.h> /* For __init/__exit/... */ +#include <linux/idr.h> /* For ida_* macros */ +#include <linux/err.h> /* For IS_ERR macros */ +#include <linux/of.h> /* For of_get_timeout_sec */ -#include "watchdog_dev.h" /* For watchdog_dev_register/... */ +#include "watchdog_core.h" /* For watchdog_dev_register/... */ + +static DEFINE_IDA(watchdog_ida); +static struct class *watchdog_class; + +static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) +{ + /* + * Check that we have valid min and max timeout values, if + * not reset them both to 0 (=not used or unknown) + */ + if (wdd->min_timeout > wdd->max_timeout) { + pr_info("Invalid min and max timeout values, resetting to 0!\n"); + wdd->min_timeout = 0; + wdd->max_timeout = 0; + } +} + +/** + * watchdog_init_timeout() - initialize the timeout field + * @timeout_parm: timeout module parameter + * @dev: Device that stores the timeout-sec property + * + * Initialize the timeout field of the watchdog_device struct with either the + * timeout module parameter (if it is valid value) or the timeout-sec property + * (only if it is a valid value and the timeout_parm is out of bounds). + * If none of them are valid then we keep the old value (which should normally + * be the default timeout value. + * + * A zero is returned on success and -EINVAL for failure. + */ +int watchdog_init_timeout(struct watchdog_device *wdd, + unsigned int timeout_parm, struct device *dev) +{ + unsigned int t = 0; + int ret = 0; + + watchdog_check_min_max_timeout(wdd); + + /* try to get the timeout module parameter first */ + if (!watchdog_timeout_invalid(wdd, timeout_parm) && timeout_parm) { + wdd->timeout = timeout_parm; + return ret; + } + if (timeout_parm) + ret = -EINVAL; + + /* try to get the timeout_sec property */ + if (dev == NULL || dev->of_node == NULL) + return ret; + of_property_read_u32(dev->of_node, "timeout-sec", &t); + if (!watchdog_timeout_invalid(wdd, t) && t) + wdd->timeout = t; + else + ret = -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(watchdog_init_timeout); /** * watchdog_register_device() - register a watchdog device @@ -49,7 +110,7 @@ */ int watchdog_register_device(struct watchdog_device *wdd) { - int ret; + int ret, id, devno; if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) return -EINVAL; @@ -58,15 +119,7 @@ int watchdog_register_device(struct watchdog_device *wdd) if (wdd->ops->start == NULL || wdd->ops->stop == NULL) return -EINVAL; - /* - * Check that we have valid min and max timeout values, if - * not reset them both to 0 (=not used or unknown) - */ - if (wdd->min_timeout > wdd->max_timeout) { - pr_info("Invalid min and max timeout values, resetting to 0!\n"); - wdd->min_timeout = 0; - wdd->max_timeout = 0; - } + watchdog_check_min_max_timeout(wdd); /* * Note: now that all watchdog_device data has been verified, we @@ -74,10 +127,38 @@ int watchdog_register_device(struct watchdog_device *wdd) * corrupted in a later stage then we expect a kernel panic! */ - /* We only support 1 watchdog device via the /dev/watchdog interface */ + mutex_init(&wdd->lock); + id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL); + if (id < 0) + return id; + wdd->id = id; + ret = watchdog_dev_register(wdd); if (ret) { - pr_err("error registering /dev/watchdog (err=%d).\n", ret); + ida_simple_remove(&watchdog_ida, id); + if (!(id == 0 && ret == -EBUSY)) + return ret; + + /* Retry in case a legacy watchdog module exists */ + id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL); + if (id < 0) + return id; + wdd->id = id; + + ret = watchdog_dev_register(wdd); + if (ret) { + ida_simple_remove(&watchdog_ida, id); + return ret; + } + } + + devno = wdd->cdev.dev; + wdd->dev = device_create(watchdog_class, wdd->parent, devno, + NULL, "watchdog%d", wdd->id); + if (IS_ERR(wdd->dev)) { + watchdog_dev_unregister(wdd); + ida_simple_remove(&watchdog_ida, id); + ret = PTR_ERR(wdd->dev); return ret; } @@ -95,16 +176,50 @@ EXPORT_SYMBOL_GPL(watchdog_register_device); void watchdog_unregister_device(struct watchdog_device *wdd) { int ret; + int devno; if (wdd == NULL) return; + devno = wdd->cdev.dev; ret = watchdog_dev_unregister(wdd); if (ret) - pr_err("error unregistering /dev/watchdog (err=%d).\n", ret); + pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); + device_destroy(watchdog_class, devno); + ida_simple_remove(&watchdog_ida, wdd->id); + wdd->dev = NULL; } EXPORT_SYMBOL_GPL(watchdog_unregister_device); +static int __init watchdog_init(void) +{ + int err; + + watchdog_class = class_create(THIS_MODULE, "watchdog"); + if (IS_ERR(watchdog_class)) { + pr_err("couldn't create class\n"); + return PTR_ERR(watchdog_class); + } + + err = watchdog_dev_init(); + if (err < 0) { + class_destroy(watchdog_class); + return err; + } + + return 0; +} + +static void __exit watchdog_exit(void) +{ + watchdog_dev_exit(); + class_destroy(watchdog_class); + ida_destroy(&watchdog_ida); +} + +subsys_initcall(watchdog_init); +module_exit(watchdog_exit); + MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); MODULE_DESCRIPTION("WatchDog Timer Driver Core"); diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_core.h index bc7612be25c..6c951418fca 100644 --- a/drivers/watchdog/watchdog_dev.h +++ b/drivers/watchdog/watchdog_core.h @@ -26,8 +26,12 @@ * This material is provided "AS-IS" and at no charge. */ +#define MAX_DOGS 32 /* Maximum number of watchdog devices */ + /* * Functions/procedures to be called by the core */ -int watchdog_dev_register(struct watchdog_device *); -int watchdog_dev_unregister(struct watchdog_device *); +extern int watchdog_dev_register(struct watchdog_device *); +extern int watchdog_dev_unregister(struct watchdog_device *); +extern int __init watchdog_dev_init(void); +extern void __exit watchdog_dev_exit(void); diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 1199da0f98c..6aaefbad303 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -42,10 +42,12 @@ #include <linux/init.h> /* For __init/__exit/... */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ -/* make sure we only register one /dev/watchdog device */ -static unsigned long watchdog_dev_busy; +#include "watchdog_core.h" + +/* the dev_t structure to store the dynamically allocated watchdog devices */ +static dev_t watchdog_devt; /* the watchdog device behind /dev/watchdog */ -static struct watchdog_device *wdd; +static struct watchdog_device *old_wdd; /* * watchdog_ping: ping the watchdog. @@ -59,13 +61,26 @@ static struct watchdog_device *wdd; static int watchdog_ping(struct watchdog_device *wddev) { - if (test_bit(WDOG_ACTIVE, &wddev->status)) { - if (wddev->ops->ping) - return wddev->ops->ping(wddev); /* ping the watchdog */ - else - return wddev->ops->start(wddev); /* restart watchdog */ + int err = 0; + + mutex_lock(&wddev->lock); + + if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + err = -ENODEV; + goto out_ping; } - return 0; + + if (!watchdog_active(wddev)) + goto out_ping; + + if (wddev->ops->ping) + err = wddev->ops->ping(wddev); /* ping the watchdog */ + else + err = wddev->ops->start(wddev); /* restart watchdog */ + +out_ping: + mutex_unlock(&wddev->lock); + return err; } /* @@ -79,16 +94,25 @@ static int watchdog_ping(struct watchdog_device *wddev) static int watchdog_start(struct watchdog_device *wddev) { - int err; + int err = 0; - if (!test_bit(WDOG_ACTIVE, &wddev->status)) { - err = wddev->ops->start(wddev); - if (err < 0) - return err; + mutex_lock(&wddev->lock); - set_bit(WDOG_ACTIVE, &wddev->status); + if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + err = -ENODEV; + goto out_start; } - return 0; + + if (watchdog_active(wddev)) + goto out_start; + + err = wddev->ops->start(wddev); + if (err == 0) + set_bit(WDOG_ACTIVE, &wddev->status); + +out_start: + mutex_unlock(&wddev->lock); + return err; } /* @@ -103,22 +127,154 @@ static int watchdog_start(struct watchdog_device *wddev) static int watchdog_stop(struct watchdog_device *wddev) { - int err = -EBUSY; + int err = 0; - if (test_bit(WDOG_NO_WAY_OUT, &wddev->status)) { - pr_info("%s: nowayout prevents watchdog to be stopped!\n", - wddev->info->identity); - return err; + mutex_lock(&wddev->lock); + + if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + err = -ENODEV; + goto out_stop; } - if (test_bit(WDOG_ACTIVE, &wddev->status)) { - err = wddev->ops->stop(wddev); - if (err < 0) - return err; + if (!watchdog_active(wddev)) + goto out_stop; + + if (test_bit(WDOG_NO_WAY_OUT, &wddev->status)) { + dev_info(wddev->dev, "nowayout prevents watchdog being stopped!\n"); + err = -EBUSY; + goto out_stop; + } + err = wddev->ops->stop(wddev); + if (err == 0) clear_bit(WDOG_ACTIVE, &wddev->status); + +out_stop: + mutex_unlock(&wddev->lock); + return err; +} + +/* + * watchdog_get_status: wrapper to get the watchdog status + * @wddev: the watchdog device to get the status from + * @status: the status of the watchdog device + * + * Get the watchdog's status flags. + */ + +static int watchdog_get_status(struct watchdog_device *wddev, + unsigned int *status) +{ + int err = 0; + + *status = 0; + if (!wddev->ops->status) + return -EOPNOTSUPP; + + mutex_lock(&wddev->lock); + + if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + err = -ENODEV; + goto out_status; } - return 0; + + *status = wddev->ops->status(wddev); + +out_status: + mutex_unlock(&wddev->lock); + return err; +} + +/* + * watchdog_set_timeout: set the watchdog timer timeout + * @wddev: the watchdog device to set the timeout for + * @timeout: timeout to set in seconds + */ + +static int watchdog_set_timeout(struct watchdog_device *wddev, + unsigned int timeout) +{ + int err; + + if ((wddev->ops->set_timeout == NULL) || + !(wddev->info->options & WDIOF_SETTIMEOUT)) + return -EOPNOTSUPP; + + if (watchdog_timeout_invalid(wddev, timeout)) + return -EINVAL; + + mutex_lock(&wddev->lock); + + if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + err = -ENODEV; + goto out_timeout; + } + + err = wddev->ops->set_timeout(wddev, timeout); + +out_timeout: + mutex_unlock(&wddev->lock); + return err; +} + +/* + * watchdog_get_timeleft: wrapper to get the time left before a reboot + * @wddev: the watchdog device to get the remaining time from + * @timeleft: the time that's left + * + * Get the time before a watchdog will reboot (if not pinged). + */ + +static int watchdog_get_timeleft(struct watchdog_device *wddev, + unsigned int *timeleft) +{ + int err = 0; + + *timeleft = 0; + if (!wddev->ops->get_timeleft) + return -EOPNOTSUPP; + + mutex_lock(&wddev->lock); + + if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + err = -ENODEV; + goto out_timeleft; + } + + *timeleft = wddev->ops->get_timeleft(wddev); + +out_timeleft: + mutex_unlock(&wddev->lock); + return err; +} + +/* + * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined + * @wddev: the watchdog device to do the ioctl on + * @cmd: watchdog command + * @arg: argument pointer + */ + +static int watchdog_ioctl_op(struct watchdog_device *wddev, unsigned int cmd, + unsigned long arg) +{ + int err; + + if (!wddev->ops->ioctl) + return -ENOIOCTLCMD; + + mutex_lock(&wddev->lock); + + if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { + err = -ENODEV; + goto out_ioctl; + } + + err = wddev->ops->ioctl(wddev, cmd, arg); + +out_ioctl: + mutex_unlock(&wddev->lock); + return err; } /* @@ -136,6 +292,7 @@ static int watchdog_stop(struct watchdog_device *wddev) static ssize_t watchdog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { + struct watchdog_device *wdd = file->private_data; size_t i; char c; @@ -175,23 +332,24 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, static long watchdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct watchdog_device *wdd = file->private_data; void __user *argp = (void __user *)arg; int __user *p = argp; unsigned int val; int err; - if (wdd->ops->ioctl) { - err = wdd->ops->ioctl(wdd, cmd, arg); - if (err != -ENOIOCTLCMD) - return err; - } + err = watchdog_ioctl_op(wdd, cmd, arg); + if (err != -ENOIOCTLCMD) + return err; switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user(argp, wdd->info, sizeof(struct watchdog_info)) ? -EFAULT : 0; case WDIOC_GETSTATUS: - val = wdd->ops->status ? wdd->ops->status(wdd) : 0; + err = watchdog_get_status(wdd, &val); + if (err == -ENODEV) + return err; return put_user(val, p); case WDIOC_GETBOOTSTATUS: return put_user(wdd->bootstatus, p); @@ -215,18 +373,11 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, watchdog_ping(wdd); return 0; case WDIOC_SETTIMEOUT: - if ((wdd->ops->set_timeout == NULL) || - !(wdd->info->options & WDIOF_SETTIMEOUT)) - return -EOPNOTSUPP; if (get_user(val, p)) return -EFAULT; - if ((wdd->max_timeout != 0) && - (val < wdd->min_timeout || val > wdd->max_timeout)) - return -EINVAL; - err = wdd->ops->set_timeout(wdd, val); + err = watchdog_set_timeout(wdd, val); if (err < 0) return err; - wdd->timeout = val; /* If the watchdog is active then we send a keepalive ping * to make sure that the watchdog keep's running (and if * possible that it takes the new timeout) */ @@ -237,17 +388,22 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, if (wdd->timeout == 0) return -EOPNOTSUPP; return put_user(wdd->timeout, p); + case WDIOC_GETTIMELEFT: + err = watchdog_get_timeleft(wdd, &val); + if (err) + return err; + return put_user(val, p); default: return -ENOTTY; } } /* - * watchdog_open: open the /dev/watchdog device. + * watchdog_open: open the /dev/watchdog* devices. * @inode: inode of device * @file: file handle to device * - * When the /dev/watchdog device gets opened, we start the watchdog. + * When the /dev/watchdog* device gets opened, we start the watchdog. * Watch out: the /dev/watchdog device is single open, so we make sure * it can only be opened once. */ @@ -255,6 +411,13 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, static int watchdog_open(struct inode *inode, struct file *file) { int err = -EBUSY; + struct watchdog_device *wdd; + + /* Get the corresponding watchdog device */ + if (imajor(inode) == MISC_MAJOR) + wdd = old_wdd; + else + wdd = container_of(inode->i_cdev, struct watchdog_device, cdev); /* the watchdog is single open! */ if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) @@ -271,6 +434,11 @@ static int watchdog_open(struct inode *inode, struct file *file) if (err < 0) goto out_mod; + file->private_data = wdd; + + if (wdd->ops->ref) + wdd->ops->ref(wdd); + /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ return nonseekable_open(inode, file); @@ -282,9 +450,9 @@ out: } /* - * watchdog_release: release the /dev/watchdog device. - * @inode: inode of device - * @file: file handle to device + * watchdog_release: release the watchdog device. + * @inode: inode of device + * @file: file handle to device * * This is the code for when /dev/watchdog gets closed. We will only * stop the watchdog when we have received the magic char (and nowayout @@ -293,6 +461,7 @@ out: static int watchdog_release(struct inode *inode, struct file *file) { + struct watchdog_device *wdd = file->private_data; int err = -EBUSY; /* @@ -300,13 +469,18 @@ static int watchdog_release(struct inode *inode, struct file *file) * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then * watchdog_stop will fail. */ - if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || - !(wdd->info->options & WDIOF_MAGICCLOSE)) + if (!test_bit(WDOG_ACTIVE, &wdd->status)) + err = 0; + else if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || + !(wdd->info->options & WDIOF_MAGICCLOSE)) err = watchdog_stop(wdd); /* If the watchdog was not stopped, send a keepalive ping */ if (err < 0) { - pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); + mutex_lock(&wdd->lock); + if (!test_bit(WDOG_UNREGISTERED, &wdd->status)) + dev_crit(wdd->dev, "watchdog did not stop!\n"); + mutex_unlock(&wdd->lock); watchdog_ping(wdd); } @@ -316,6 +490,10 @@ static int watchdog_release(struct inode *inode, struct file *file) /* make sure that /dev/watchdog can be re-opened */ clear_bit(WDOG_DEV_OPEN, &wdd->status); + /* Note wdd may be gone after this, do not use after this! */ + if (wdd->ops->unref) + wdd->ops->unref(wdd); + return 0; } @@ -334,62 +512,93 @@ static struct miscdevice watchdog_miscdev = { }; /* - * watchdog_dev_register: + * watchdog_dev_register: register a watchdog device * @watchdog: watchdog device * - * Register a watchdog device as /dev/watchdog. /dev/watchdog - * is actually a miscdevice and thus we set it up like that. + * Register a watchdog device including handling the legacy + * /dev/watchdog node. /dev/watchdog is actually a miscdevice and + * thus we set it up like that. */ int watchdog_dev_register(struct watchdog_device *watchdog) { - int err; - - /* Only one device can register for /dev/watchdog */ - if (test_and_set_bit(0, &watchdog_dev_busy)) { - pr_err("only one watchdog can use /dev/watchdog.\n"); - return -EBUSY; + int err, devno; + + if (watchdog->id == 0) { + old_wdd = watchdog; + watchdog_miscdev.parent = watchdog->parent; + err = misc_register(&watchdog_miscdev); + if (err != 0) { + pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", + watchdog->info->identity, WATCHDOG_MINOR, err); + if (err == -EBUSY) + pr_err("%s: a legacy watchdog module is probably present.\n", + watchdog->info->identity); + old_wdd = NULL; + return err; + } } - wdd = watchdog; - - err = misc_register(&watchdog_miscdev); - if (err != 0) { - pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", - watchdog->info->identity, WATCHDOG_MINOR, err); - goto out; + /* Fill in the data structures */ + devno = MKDEV(MAJOR(watchdog_devt), watchdog->id); + cdev_init(&watchdog->cdev, &watchdog_fops); + watchdog->cdev.owner = watchdog->ops->owner; + + /* Add the device */ + err = cdev_add(&watchdog->cdev, devno, 1); + if (err) { + pr_err("watchdog%d unable to add device %d:%d\n", + watchdog->id, MAJOR(watchdog_devt), watchdog->id); + if (watchdog->id == 0) { + misc_deregister(&watchdog_miscdev); + old_wdd = NULL; + } } - - return 0; - -out: - wdd = NULL; - clear_bit(0, &watchdog_dev_busy); return err; } /* - * watchdog_dev_unregister: + * watchdog_dev_unregister: unregister a watchdog device * @watchdog: watchdog device * - * Deregister the /dev/watchdog device. + * Unregister the watchdog and if needed the legacy /dev/watchdog device. */ int watchdog_dev_unregister(struct watchdog_device *watchdog) { - /* Check that a watchdog device was registered in the past */ - if (!test_bit(0, &watchdog_dev_busy) || !wdd) - return -ENODEV; - - /* We can only unregister the watchdog device that was registered */ - if (watchdog != wdd) { - pr_err("%s: watchdog was not registered as /dev/watchdog.\n", - watchdog->info->identity); - return -ENODEV; + mutex_lock(&watchdog->lock); + set_bit(WDOG_UNREGISTERED, &watchdog->status); + mutex_unlock(&watchdog->lock); + + cdev_del(&watchdog->cdev); + if (watchdog->id == 0) { + misc_deregister(&watchdog_miscdev); + old_wdd = NULL; } - - misc_deregister(&watchdog_miscdev); - wdd = NULL; - clear_bit(0, &watchdog_dev_busy); return 0; } + +/* + * watchdog_dev_init: init dev part of watchdog core + * + * Allocate a range of chardev nodes to use for watchdog devices + */ + +int __init watchdog_dev_init(void) +{ + int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); + if (err < 0) + pr_err("watchdog: unable to allocate char dev region\n"); + return err; +} + +/* + * watchdog_dev_exit: exit dev part of watchdog core + * + * Release the range of chardev nodes used for watchdog devices + */ + +void __exit watchdog_dev_exit(void) +{ + unregister_chrdev_region(watchdog_devt, MAX_DOGS); +} diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c index 94ec22b9e66..0240c60d14e 100644 --- a/drivers/watchdog/wdrtas.c +++ b/drivers/watchdog/wdrtas.c @@ -26,6 +26,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> @@ -46,10 +48,8 @@ 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 bool wdrtas_nowayout = WATCHDOG_NOWAYOUT; static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0); static char wdrtas_expect_close; @@ -93,8 +93,8 @@ static int wdrtas_set_interval(int interval) 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); + pr_err("setting the watchdog to %i timeout failed: %li\n", + interval, result); print_msg--; } @@ -128,8 +128,8 @@ static int wdrtas_get_interval(int fallback_value) 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); + pr_warn("could not get sp_spi watchdog timeout (%li). Continuing\n", + result); return fallback_value; } @@ -160,31 +160,6 @@ static void wdrtas_timer_stop(void) } /** - * 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 @@ -201,10 +176,11 @@ static void wdrtas_timer_keepalive(void) (void *)__pa(wdrtas_logbuffer), WDRTAS_LOGBUFFER_LEN); if (result < 0) - printk(KERN_ERR "wdrtas: event-scan failed: %li\n", - result); + pr_err("event-scan failed: %li\n", result); if (result == 0) - wdrtas_log_scanned_event(); + print_hex_dump(KERN_INFO, "dumping event, data: ", + DUMP_PREFIX_OFFSET, 16, 1, + wdrtas_logbuffer, WDRTAS_LOGBUFFER_LEN, false); } while (result == 0); } @@ -224,8 +200,7 @@ static int wdrtas_get_temperature(void) result = rtas_get_sensor(WDRTAS_THERMAL_SENSOR, 0, &temperature); if (result < 0) - printk(KERN_WARNING "wdrtas: reading the thermal sensor " - "failed: %i\n", result); + pr_warn("reading the thermal sensor failed: %i\n", result); else temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */ @@ -419,8 +394,7 @@ static int wdrtas_close(struct inode *inode, struct file *file) if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR) wdrtas_timer_stop(); else { - printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog " - "not stopped.\n"); + pr_warn("got unexpected close. Watchdog not stopped.\n"); wdrtas_timer_keepalive(); } @@ -552,30 +526,24 @@ 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"); + pr_warn("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); + pr_warn("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"); + pr_err("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"); + pr_err("couldn't get token for event-scan. Terminating watchdog code.\n"); return -EIO; } @@ -609,17 +577,14 @@ static int wdrtas_register_devs(void) result = misc_register(&wdrtas_miscdev); if (result) { - printk(KERN_ERR "wdrtas: couldn't register watchdog misc " - "device. Terminating watchdog code.\n"); + pr_err("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"); + pr_warn("couldn't register watchdog temperature misc device. Continuing without temperature support.\n"); wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE; } } @@ -643,8 +608,7 @@ static int __init wdrtas_init(void) return -ENODEV; if (register_reboot_notifier(&wdrtas_notifier)) { - printk(KERN_ERR "wdrtas: could not register reboot notifier. " - "Terminating watchdog code.\n"); + pr_err("could not register reboot notifier. Terminating watchdog code.\n"); wdrtas_unregister_devs(); return -ENODEV; } diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c index d2ef002be96..e0206b5b7d8 100644 --- a/drivers/watchdog/wdt.c +++ b/drivers/watchdog/wdt.c @@ -32,6 +32,8 @@ * Matt Domsch : Added nowayout module option */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/interrupt.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -46,7 +48,6 @@ #include <linux/io.h> #include <linux/uaccess.h> -#include <asm/system.h> #include "wd501p.h" static unsigned long wdt_is_open; @@ -65,8 +66,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -252,11 +253,11 @@ static int wdt_get_temperature(void) static void wdt_decode_501(int status) { if (!(status & WDC_SR_TGOOD)) - printk(KERN_CRIT "Overheat alarm.(%d)\n", inb_p(WDT_RT)); + pr_crit("Overheat alarm (%d)\n", inb_p(WDT_RT)); if (!(status & WDC_SR_PSUOVER)) - printk(KERN_CRIT "PSU over voltage.\n"); + pr_crit("PSU over voltage\n"); if (!(status & WDC_SR_PSUUNDR)) - printk(KERN_CRIT "PSU under voltage.\n"); + pr_crit("PSU under voltage\n"); } /** @@ -280,25 +281,25 @@ static irqreturn_t wdt_interrupt(int irq, void *dev_id) spin_lock(&wdt_lock); status = inb_p(WDT_SR); - printk(KERN_CRIT "WDT status %d\n", status); + pr_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"); + pr_crit("Possible fan fault\n"); } } if (!(status & WDC_SR_WCCR)) { #ifdef SOFTWARE_REBOOT #ifdef ONLY_TESTING - printk(KERN_CRIT "Would Reboot.\n"); + pr_crit("Would Reboot\n"); #else - printk(KERN_CRIT "Initiating system reboot.\n"); + pr_crit("Initiating system reboot\n"); emergency_restart(); #endif #else - printk(KERN_CRIT "Reset in 5ms.\n"); + pr_crit("Reset in 5ms\n"); #endif } spin_unlock(&wdt_lock); @@ -441,8 +442,7 @@ static int wdt_release(struct inode *inode, struct file *file) wdt_stop(); clear_bit(0, &wdt_is_open); } else { - printk(KERN_CRIT - "wdt: WDT device closed unexpectedly. WDT will not stop!\n"); + pr_crit("WDT device closed unexpectedly. WDT will not stop!\n"); wdt_ping(); } expect_close = 0; @@ -593,7 +593,7 @@ static int __init wdt_init(void) int ret; if (type != 500 && type != 501) { - printk(KERN_ERR "wdt: unknown card type '%d'.\n", type); + pr_err("unknown card type '%d'\n", type); return -ENODEV; } @@ -601,53 +601,49 @@ static int __init wdt_init(void) 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); + pr_info("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); + pr_err("I/O address 0x%04x already in use\n", io); ret = -EBUSY; goto out; } ret = request_irq(irq, wdt_interrupt, 0, "wdt501p", NULL); if (ret) { - printk(KERN_ERR "wdt: IRQ %d is not free.\n", irq); + pr_err("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); + pr_err("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); + pr_err("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); + pr_err("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", + pr_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")); + pr_info("Fan Tachometer is %s\n", + tachometer ? "Enabled" : "Disabled"); return 0; outmisc: @@ -668,6 +664,4 @@ 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 index f55135662d7..ebbb183be61 100644 --- a/drivers/watchdog/wdt285.c +++ b/drivers/watchdog/wdt285.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -32,6 +34,7 @@ #include <mach/hardware.h> #include <asm/mach-types.h> +#include <asm/system_info.h> #include <asm/hardware/dec21285.h> /* @@ -49,7 +52,7 @@ static unsigned long timer_alive; */ static void watchdog_fire(int irq, void *dev_id) { - printk(KERN_CRIT "Watchdog: Would Reboot.\n"); + pr_crit("Would Reboot\n"); *CSR_TIMER4_CNTL = 0; *CSR_TIMER4_CLR = 0; } @@ -136,9 +139,8 @@ static const struct watchdog_info ident = { 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; + int new_margin, ret = -ENOTTY; switch (cmd) { case WDIOC_GETSUPPORT: @@ -205,13 +207,11 @@ static int __init footbridge_watchdog_init(void) if (retval < 0) return retval; - printk(KERN_INFO - "Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n", - soft_margin); + pr_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"); + pr_warn("Warning: Watchdog reset may not work on this machine\n"); return 0; } @@ -223,7 +223,6 @@ static void __exit footbridge_watchdog_exit(void) 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"); diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c index a2f01c9f5c3..a8e6f87f60c 100644 --- a/drivers/watchdog/wdt977.c +++ b/drivers/watchdog/wdt977.c @@ -23,6 +23,8 @@ * Netwinders only */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -37,13 +39,10 @@ #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) @@ -68,8 +67,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (60..15300, default=" 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -119,7 +118,7 @@ static int wdt977_start(void) outb_p(LOCK_DATA, IO_INDEX_PORT); spin_unlock_irqrestore(&spinlock, flags); - printk(KERN_INFO PFX "activated.\n"); + pr_info("activated\n"); return 0; } @@ -164,7 +163,7 @@ static int wdt977_stop(void) outb_p(LOCK_DATA, IO_INDEX_PORT); spin_unlock_irqrestore(&spinlock, flags); - printk(KERN_INFO PFX "shutdown.\n"); + pr_info("shutdown\n"); return 0; } @@ -288,8 +287,7 @@ static int wdt977_release(struct inode *inode, struct file *file) clear_bit(0, &timer_alive); } else { wdt977_keepalive(); - printk(KERN_CRIT PFX - "Unexpected close, not stopping watchdog!\n"); + pr_crit("Unexpected close, not stopping watchdog!\n"); } expect_close = 0; return 0; @@ -446,15 +444,14 @@ static int __init wd977_init(void) { int rc; - printk(KERN_INFO PFX DRIVER_VERSION); + pr_info("driver v%s\n", WATCHDOG_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); + pr_info("timeout value must be 60 < timeout < 15300, using %d\n", + DEFAULT_TIMEOUT); } /* on Netwinder the IOports are already reserved by @@ -462,9 +459,8 @@ static int __init wd977_init(void) */ 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); + pr_err("I/O address 0x%04x already in use\n", + IO_INDEX_PORT); rc = -EIO; goto err_out; } @@ -472,22 +468,19 @@ static int __init wd977_init(void) rc = register_reboot_notifier(&wdt977_notifier); if (rc) { - printk(KERN_ERR PFX - "cannot register reboot notifier (err=%d)\n", rc); + pr_err("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); + pr_err("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); + pr_info("initialized. timeout=%d sec (nowayout=%d, testmode=%i)\n", + timeout, nowayout, testmode); return 0; @@ -514,4 +507,3 @@ 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 index e0fc3baa919..48b2c058b00 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c @@ -37,6 +37,8 @@ * Matt Domsch : nowayout module option */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/interrupt.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -47,19 +49,15 @@ #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; @@ -80,8 +78,8 @@ 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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -312,33 +310,32 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) status = inb(WDT_SR); udelay(8); - printk(KERN_CRIT PFX "status %d\n", status); + pr_crit("status %d\n", status); if (type == 501) { if (!(status & WDC_SR_TGOOD)) { - printk(KERN_CRIT PFX "Overheat alarm.(%d)\n", - inb(WDT_RT)); + pr_crit("Overheat alarm (%d)\n", inb(WDT_RT)); udelay(8); } if (!(status & WDC_SR_PSUOVER)) - printk(KERN_CRIT PFX "PSU over voltage.\n"); + pr_crit("PSU over voltage\n"); if (!(status & WDC_SR_PSUUNDR)) - printk(KERN_CRIT PFX "PSU under voltage.\n"); + pr_crit("PSU under voltage\n"); if (tachometer) { if (!(status & WDC_SR_FANGOOD)) - printk(KERN_CRIT PFX "Possible fan fault.\n"); + pr_crit("Possible fan fault\n"); } } if (!(status & WDC_SR_WCCR)) { #ifdef SOFTWARE_REBOOT #ifdef ONLY_TESTING - printk(KERN_CRIT PFX "Would Reboot.\n"); + pr_crit("Would Reboot\n"); #else - printk(KERN_CRIT PFX "Initiating system reboot.\n"); + pr_crit("Initiating system reboot\n"); emergency_restart(NULL); #endif #else - printk(KERN_CRIT PFX "Reset in 5ms.\n"); + pr_crit("Reset in 5ms\n"); #endif } spin_unlock(&wdtpci_lock); @@ -484,7 +481,7 @@ 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!"); + pr_crit("Unexpected close, not stopping timer!\n"); wdtpci_ping(); } expect_close = 0; @@ -607,36 +604,36 @@ static struct notifier_block wdtpci_notifier = { }; -static int __devinit wdtpci_init_one(struct pci_dev *dev, +static int 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"); + pr_err("This driver only supports one device\n"); return -ENODEV; } if (type != 500 && type != 501) { - printk(KERN_ERR PFX "unknown card type '%d'.\n", type); + pr_err("unknown card type '%d'\n", type); return -ENODEV; } if (pci_enable_device(dev)) { - printk(KERN_ERR PFX "Not possible to enable PCI Device\n"); + pr_err("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"); + pr_err("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)); + pr_err("I/O address 0x%llx already in use\n", + (unsigned long long)pci_resource_start(dev, 2)); goto out_pci; } @@ -645,53 +642,48 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, if (request_irq(irq, wdtpci_interrupt, IRQF_SHARED, "wdt_pci", &wdtpci_miscdev)) { - printk(KERN_ERR PFX "IRQ %d is not free\n", irq); + pr_err("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); + pr_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); + pr_info("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); + pr_err("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); + pr_err("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); + pr_err("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", + pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); if (type == 501) - printk(KERN_INFO "wdt: Fan Tachometer is %s\n", - (tachometer ? "Enabled" : "Disabled")); + pr_info("Fan Tachometer is %s\n", + tachometer ? "Enabled" : "Disabled"); ret = 0; out: @@ -712,7 +704,7 @@ out_pci: } -static void __devexit wdtpci_remove_one(struct pci_dev *pdev) +static void wdtpci_remove_one(struct pci_dev *pdev) { /* here we assume only one device will ever have * been picked up and registered by probe function */ @@ -727,7 +719,7 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev) } -static DEFINE_PCI_DEVICE_TABLE(wdtpci_pci_tbl) = { +static const struct pci_device_id wdtpci_pci_tbl[] = { { .vendor = PCI_VENDOR_ID_ACCESSIO, .device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM, @@ -743,45 +735,11 @@ static struct pci_driver wdtpci_driver = { .name = "wdt_pci", .id_table = wdtpci_pci_tbl, .probe = wdtpci_init_one, - .remove = __devexit_p(wdtpci_remove_one), + .remove = 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_pci_driver(wdtpci_driver); 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 index 263c883f080..2fa17e746ff 100644 --- a/drivers/watchdog/wm831x_wdt.c +++ b/drivers/watchdog/wm831x_wdt.c @@ -22,8 +22,8 @@ #include <linux/mfd/wm831x/pdata.h> #include <linux/mfd/wm831x/watchdog.h> -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -163,6 +163,8 @@ static int wm831x_wdt_set_timeout(struct watchdog_device *wdt_dev, ret); } + wdt_dev->timeout = timeout; + return ret; } @@ -179,10 +181,10 @@ static const struct watchdog_ops wm831x_wdt_ops = { .set_timeout = wm831x_wdt_set_timeout, }; -static int __devinit wm831x_wdt_probe(struct platform_device *pdev) +static int wm831x_wdt_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *chip_pdata; + struct wm831x_pdata *chip_pdata = dev_get_platdata(pdev->dev.parent); struct wm831x_watchdog_pdata *pdata; struct wm831x_wdt_drvdata *driver_data; struct watchdog_device *wm831x_wdt; @@ -202,7 +204,6 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), GFP_KERNEL); if (!driver_data) { - dev_err(wm831x->dev, "Unable to alloacate watchdog device\n"); ret = -ENOMEM; goto err; } @@ -229,12 +230,10 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) wm831x_wdt->timeout = wm831x_wdt_cfgs[i].time; /* Apply any configuration */ - if (pdev->dev.parent->platform_data) { - chip_pdata = pdev->dev.parent->platform_data; + if (chip_pdata) pdata = chip_pdata->watchdog; - } else { + else pdata = NULL; - } if (pdata) { reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK | @@ -245,8 +244,10 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT; if (pdata->update_gpio) { - ret = gpio_request(pdata->update_gpio, - "Watchdog update"); + ret = devm_gpio_request_one(&pdev->dev, + pdata->update_gpio, + GPIOF_OUT_INIT_LOW, + "Watchdog update"); if (ret < 0) { dev_err(wm831x->dev, "Failed to request update GPIO: %d\n", @@ -254,14 +255,6 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) 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; - } - driver_data->update_gpio = pdata->update_gpio; /* Make sure the watchdog takes hardware updates */ @@ -275,7 +268,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) } else { dev_err(wm831x->dev, "Failed to unlock security key: %d\n", ret); - goto err_gpio; + goto err; } } @@ -283,35 +276,29 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) if (ret != 0) { dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n", ret); - goto err_gpio; + goto err; } - dev_set_drvdata(&pdev->dev, driver_data); + platform_set_drvdata(pdev, driver_data); return 0; -err_gpio: - if (driver_data->update_gpio) - gpio_free(driver_data->update_gpio); err: return ret; } -static int __devexit wm831x_wdt_remove(struct platform_device *pdev) +static int wm831x_wdt_remove(struct platform_device *pdev) { - struct wm831x_wdt_drvdata *driver_data = dev_get_drvdata(&pdev->dev); + struct wm831x_wdt_drvdata *driver_data = platform_get_drvdata(pdev); watchdog_unregister_device(&driver_data->wdt); - if (driver_data->update_gpio) - gpio_free(driver_data->update_gpio); - return 0; } static struct platform_driver wm831x_wdt_driver = { .probe = wm831x_wdt_probe, - .remove = __devexit_p(wm831x_wdt_remove), + .remove = wm831x_wdt_remove, .driver = { .name = "wm831x-watchdog", }, diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c index 5d7113c7e50..34d272ada23 100644 --- a/drivers/watchdog/wm8350_wdt.c +++ b/drivers/watchdog/wm8350_wdt.c @@ -8,63 +8,65 @@ * as published by the Free Software Foundation */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 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 */ + unsigned 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) +static int wm8350_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) { - int ret; + struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); + int ret, i; u16 reg; + for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++) + if (wm8350_wdt_cfgs[i].time == timeout) + break; + if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) + return -EINVAL; + mutex_lock(&wdt_mutex); wm8350_reg_unlock(wm8350); reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); reg &= ~WM8350_WDOG_TO_MASK; - reg |= value; + reg |= wm8350_wdt_cfgs[i].val; ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); wm8350_reg_lock(wm8350); mutex_unlock(&wdt_mutex); + wdt_dev->timeout = timeout; return ret; } -static int wm8350_wdt_start(struct wm8350 *wm8350) +static int wm8350_wdt_start(struct watchdog_device *wdt_dev) { + struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); int ret; u16 reg; @@ -82,8 +84,9 @@ static int wm8350_wdt_start(struct wm8350 *wm8350) return ret; } -static int wm8350_wdt_stop(struct wm8350 *wm8350) +static int wm8350_wdt_stop(struct watchdog_device *wdt_dev) { + struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); int ret; u16 reg; @@ -100,8 +103,9 @@ static int wm8350_wdt_stop(struct wm8350 *wm8350) return ret; } -static int wm8350_wdt_kick(struct wm8350 *wm8350) +static int wm8350_wdt_ping(struct watchdog_device *wdt_dev) { + struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); int ret; u16 reg; @@ -115,171 +119,28 @@ static int wm8350_wdt_kick(struct wm8350 *wm8350) 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 = { +static const struct watchdog_info wm8350_wdt_info = { .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_stop(wm8350); - - if (options == WDIOS_ENABLECARD) - ret = wm8350_wdt_start(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 = { +static const struct watchdog_ops wm8350_wdt_ops = { .owner = THIS_MODULE, - .llseek = no_llseek, - .write = wm8350_wdt_write, - .unlocked_ioctl = wm8350_wdt_ioctl, - .open = wm8350_wdt_open, - .release = wm8350_wdt_release, + .start = wm8350_wdt_start, + .stop = wm8350_wdt_stop, + .ping = wm8350_wdt_ping, + .set_timeout = wm8350_wdt_set_timeout, }; -static struct miscdevice wm8350_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wm8350_wdt_fops, +static struct watchdog_device wm8350_wdt = { + .info = &wm8350_wdt_info, + .ops = &wm8350_wdt_ops, + .timeout = 4, + .min_timeout = 1, + .max_timeout = 4, }; -static int __devinit wm8350_wdt_probe(struct platform_device *pdev) +static int wm8350_wdt_probe(struct platform_device *pdev) { struct wm8350 *wm8350 = platform_get_drvdata(pdev); @@ -288,24 +149,24 @@ static int __devinit wm8350_wdt_probe(struct platform_device *pdev) return -ENODEV; } - /* Default to 4s timeout */ - wm8350_wdt_set_timeout(wm8350, 0x05); + watchdog_set_nowayout(&wm8350_wdt, nowayout); + watchdog_set_drvdata(&wm8350_wdt, wm8350); - wm8350_wdt_miscdev.parent = &pdev->dev; + /* Default to 4s timeout */ + wm8350_wdt_set_timeout(&wm8350_wdt, 4); - return misc_register(&wm8350_wdt_miscdev); + return watchdog_register_device(&wm8350_wdt); } -static int __devexit wm8350_wdt_remove(struct platform_device *pdev) +static int wm8350_wdt_remove(struct platform_device *pdev) { - misc_deregister(&wm8350_wdt_miscdev); - + watchdog_unregister_device(&wm8350_wdt); return 0; } static struct platform_driver wm8350_wdt_driver = { .probe = wm8350_wdt_probe, - .remove = __devexit_p(wm8350_wdt_remove), + .remove = wm8350_wdt_remove, .driver = { .name = "wm8350-wdt", }, diff --git a/drivers/watchdog/xen_wdt.c b/drivers/watchdog/xen_wdt.c index 49bd9d39556..7a42dffd39e 100644 --- a/drivers/watchdog/xen_wdt.c +++ b/drivers/watchdog/xen_wdt.c @@ -9,9 +9,10 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define DRV_NAME "wdt" #define DRV_VERSION "0.01" -#define PFX DRV_NAME ": " #include <linux/bug.h> #include <linux/errno.h> @@ -131,16 +132,17 @@ static int xen_wdt_open(struct inode *inode, struct file *file) static int xen_wdt_release(struct inode *inode, struct file *file) { + int err = 0; + if (expect_release) - xen_wdt_stop(); + err = xen_wdt_stop(); else { - printk(KERN_CRIT PFX - "unexpected close, not stopping watchdog!\n"); + pr_crit("unexpected close, not stopping watchdog!\n"); xen_wdt_kick(); } - is_active = false; + is_active = err; expect_release = false; - return 0; + return err; } static ssize_t xen_wdt_write(struct file *file, const char __user *data, @@ -242,7 +244,7 @@ static struct miscdevice xen_wdt_miscdev = { .fops = &xen_wdt_fops, }; -static int __devinit xen_wdt_probe(struct platform_device *dev) +static int xen_wdt_probe(struct platform_device *dev) { struct sched_watchdog wd = { .id = ~0 }; int ret = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wd); @@ -251,37 +253,34 @@ static int __devinit xen_wdt_probe(struct platform_device *dev) case -EINVAL: if (!timeout) { timeout = WATCHDOG_TIMEOUT; - printk(KERN_INFO PFX - "timeout value invalid, using %d\n", timeout); + pr_info("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", + pr_err("cannot register miscdev on minor=%d (%d)\n", WATCHDOG_MINOR, ret); break; } - printk(KERN_INFO PFX - "initialized (timeout=%ds, nowayout=%d)\n", - timeout, nowayout); + pr_info("initialized (timeout=%ds, nowayout=%d)\n", + timeout, nowayout); break; case -ENOSYS: - printk(KERN_INFO PFX "not supported\n"); + pr_info("not supported\n"); ret = -ENODEV; break; default: - printk(KERN_INFO PFX "bogus return value %d\n", ret); + pr_info("bogus return value %d\n", ret); break; } return ret; } -static int __devexit xen_wdt_remove(struct platform_device *dev) +static int xen_wdt_remove(struct platform_device *dev) { /* Stop the timer before we leave */ if (!nowayout) @@ -299,17 +298,24 @@ static void xen_wdt_shutdown(struct platform_device *dev) static int xen_wdt_suspend(struct platform_device *dev, pm_message_t state) { - return xen_wdt_stop(); + typeof(wdt.id) id = wdt.id; + int rc = xen_wdt_stop(); + + wdt.id = id; + return rc; } static int xen_wdt_resume(struct platform_device *dev) { + if (!wdt.id) + return 0; + wdt.id = 0; return xen_wdt_start(); } static struct platform_driver xen_wdt_driver = { .probe = xen_wdt_probe, - .remove = __devexit_p(xen_wdt_remove), + .remove = xen_wdt_remove, .shutdown = xen_wdt_shutdown, .suspend = xen_wdt_suspend, .resume = xen_wdt_resume, @@ -326,7 +332,7 @@ static int __init xen_wdt_init_module(void) if (!xen_domain()) return -ENODEV; - printk(KERN_INFO PFX "Xen WatchDog Timer Driver v%s\n", DRV_VERSION); + pr_info("Xen WatchDog Timer Driver v%s\n", DRV_VERSION); err = platform_driver_register(&xen_wdt_driver); if (err) @@ -346,7 +352,7 @@ 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"); + pr_info("module unloaded\n"); } module_init(xen_wdt_init_module); @@ -356,4 +362,3 @@ 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); |
