diff options
Diffstat (limited to 'drivers/watchdog')
48 files changed, 5037 insertions, 394 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 8828d8ffd353..faa9127aecaa 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -73,6 +73,13 @@ config WM8350_WATCHDOG | |||
73 | 73 | ||
74 | # ARM Architecture | 74 | # ARM Architecture |
75 | 75 | ||
76 | config ARM_SP805_WATCHDOG | ||
77 | tristate "ARM SP805 Watchdog" | ||
78 | depends on ARM_AMBA | ||
79 | help | ||
80 | ARM Primecell SP805 Watchdog timer. This will reboot your system when | ||
81 | the timeout is reached. | ||
82 | |||
76 | config AT91RM9200_WATCHDOG | 83 | config AT91RM9200_WATCHDOG |
77 | tristate "AT91RM9200 watchdog" | 84 | tristate "AT91RM9200 watchdog" |
78 | depends on ARCH_AT91RM9200 | 85 | depends on ARCH_AT91RM9200 |
@@ -145,13 +152,19 @@ config KS8695_WATCHDOG | |||
145 | Watchdog timer embedded into KS8695 processor. This will reboot your | 152 | Watchdog timer embedded into KS8695 processor. This will reboot your |
146 | system when the timeout is reached. | 153 | system when the timeout is reached. |
147 | 154 | ||
155 | config HAVE_S3C2410_WATCHDOG | ||
156 | bool | ||
157 | help | ||
158 | This will include watchdog timer support for Samsung SoCs. If | ||
159 | you want to include watchdog support for any machine, kindly | ||
160 | select this in the respective mach-XXXX/Kconfig file. | ||
161 | |||
148 | config S3C2410_WATCHDOG | 162 | config S3C2410_WATCHDOG |
149 | tristate "S3C2410 Watchdog" | 163 | tristate "S3C2410 Watchdog" |
150 | depends on ARCH_S3C2410 | 164 | depends on ARCH_S3C2410 || HAVE_S3C2410_WATCHDOG |
151 | help | 165 | help |
152 | Watchdog timer block in the Samsung S3C2410 chips. This will | 166 | Watchdog timer block in the Samsung SoCs. This will reboot |
153 | reboot the system when the timer expires with the watchdog | 167 | the system when the timer expires with the watchdog enabled. |
154 | enabled. | ||
155 | 168 | ||
156 | The driver is limited by the speed of the system's PCLK | 169 | The driver is limited by the speed of the system's PCLK |
157 | signal, so with reasonably fast systems (PCLK around 50-66MHz) | 170 | signal, so with reasonably fast systems (PCLK around 50-66MHz) |
@@ -200,11 +213,11 @@ config OMAP_WATCHDOG | |||
200 | here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer. | 213 | here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer. |
201 | 214 | ||
202 | config PNX4008_WATCHDOG | 215 | config PNX4008_WATCHDOG |
203 | tristate "PNX4008 Watchdog" | 216 | tristate "PNX4008 and LPC32XX Watchdog" |
204 | depends on ARCH_PNX4008 | 217 | depends on ARCH_PNX4008 || ARCH_LPC32XX |
205 | help | 218 | help |
206 | Say Y here if to include support for the watchdog timer | 219 | Say Y here if to include support for the watchdog timer |
207 | in the PNX4008 processor. | 220 | in the PNX4008 or LPC32XX processor. |
208 | This driver can be built as a module by choosing M. The module | 221 | This driver can be built as a module by choosing M. The module |
209 | will be called pnx4008_wdt. | 222 | will be called pnx4008_wdt. |
210 | 223 | ||
@@ -306,6 +319,18 @@ config MAX63XX_WATCHDOG | |||
306 | help | 319 | help |
307 | Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. | 320 | Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. |
308 | 321 | ||
322 | config IMX2_WDT | ||
323 | tristate "IMX2+ Watchdog" | ||
324 | depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX5 | ||
325 | help | ||
326 | This is the driver for the hardware watchdog | ||
327 | on the Freescale IMX2 and later processors. | ||
328 | If you have one of these processors and wish to have | ||
329 | watchdog support enabled, say Y, otherwise say N. | ||
330 | |||
331 | To compile this driver as a module, choose M here: the | ||
332 | module will be called imx2_wdt. | ||
333 | |||
309 | # AVR32 Architecture | 334 | # AVR32 Architecture |
310 | 335 | ||
311 | config AT32AP700X_WDT | 336 | config AT32AP700X_WDT |
@@ -383,6 +408,28 @@ config ALIM7101_WDT | |||
383 | 408 | ||
384 | Most people will say N. | 409 | Most people will say N. |
385 | 410 | ||
411 | config F71808E_WDT | ||
412 | tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" | ||
413 | depends on X86 && EXPERIMENTAL | ||
414 | help | ||
415 | This is the driver for the hardware watchdog on the Fintek | ||
416 | F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers. | ||
417 | |||
418 | You can compile this driver directly into the kernel, or use | ||
419 | it as a module. The module will be called f71808e_wdt. | ||
420 | |||
421 | config SP5100_TCO | ||
422 | tristate "AMD/ATI SP5100 TCO Timer/Watchdog" | ||
423 | depends on X86 && PCI | ||
424 | ---help--- | ||
425 | Hardware watchdog driver for the AMD/ATI SP5100 chipset. The TCO | ||
426 | (Total Cost of Ownership) timer is a watchdog timer that will reboot | ||
427 | the machine after its expiration. The expiration time can be | ||
428 | configured with the "heartbeat" parameter. | ||
429 | |||
430 | To compile this driver as a module, choose M here: the | ||
431 | module will be called sp5100_tco. | ||
432 | |||
386 | config GEODE_WDT | 433 | config GEODE_WDT |
387 | tristate "AMD Geode CS5535/CS5536 Watchdog" | 434 | tristate "AMD Geode CS5535/CS5536 Watchdog" |
388 | depends on CS5535_MFGPT | 435 | depends on CS5535_MFGPT |
@@ -522,6 +569,9 @@ config IT8712F_WDT | |||
522 | This is the driver for the built-in watchdog timer on the IT8712F | 569 | This is the driver for the built-in watchdog timer on the IT8712F |
523 | Super I/0 chipset used on many motherboards. | 570 | Super I/0 chipset used on many motherboards. |
524 | 571 | ||
572 | If the driver does not work, then make sure that the game port in | ||
573 | the BIOS is enabled. | ||
574 | |||
525 | To compile this driver as a module, choose M here: the | 575 | To compile this driver as a module, choose M here: the |
526 | module will be called it8712f_wdt. | 576 | module will be called it8712f_wdt. |
527 | 577 | ||
@@ -529,25 +579,31 @@ config IT87_WDT | |||
529 | tristate "IT87 Watchdog Timer" | 579 | tristate "IT87 Watchdog Timer" |
530 | depends on X86 && EXPERIMENTAL | 580 | depends on X86 && EXPERIMENTAL |
531 | ---help--- | 581 | ---help--- |
532 | This is the driver for the hardware watchdog on the ITE IT8716, | 582 | This is the driver for the hardware watchdog on the ITE IT8702, |
533 | IT8718, IT8726, IT8712(Version J,K) Super I/O chips. This watchdog | 583 | IT8712, IT8716, IT8718, IT8720, IT8726, IT8712 Super I/O chips. |
534 | simply watches your kernel to make sure it doesn't freeze, and if | 584 | This watchdog simply watches your kernel to make sure it doesn't |
535 | it does, it reboots your computer after a certain amount of time. | 585 | freeze, and if it does, it reboots your computer after a certain |
586 | amount of time. | ||
536 | 587 | ||
537 | To compile this driver as a module, choose M here: the module will | 588 | To compile this driver as a module, choose M here: the module will |
538 | be called it87_wdt. | 589 | be called it87_wdt. |
539 | 590 | ||
540 | config HP_WATCHDOG | 591 | config HP_WATCHDOG |
541 | tristate "HP Proliant iLO 2 Hardware Watchdog Timer" | 592 | tristate "HP Proliant iLO2+ Hardware Watchdog Timer" |
542 | depends on X86 | 593 | depends on X86 |
543 | help | 594 | help |
544 | A software monitoring watchdog and NMI sourcing driver. This driver | 595 | A software monitoring watchdog and NMI sourcing driver. This driver |
545 | will detect lockups and provide stack trace. Also, when an NMI | 596 | will detect lockups and provide a stack trace. This is a driver that |
546 | occurs this driver will make the necessary BIOS calls to log | 597 | will only load on a HP ProLiant system with a minimum of iLO2 support. |
547 | the cause of the NMI. This is a driver that will only load on a | 598 | To compile this driver as a module, choose M here: the module will be |
548 | HP ProLiant system with a minimum of iLO2 support. | 599 | called hpwdt. |
549 | To compile this driver as a module, choose M here: the | 600 | |
550 | module will be called hpwdt. | 601 | config HPWDT_NMI_DECODING |
602 | bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer" | ||
603 | depends on HP_WATCHDOG | ||
604 | help | ||
605 | When an NMI occurs this feature will make the necessary BIOS calls to | ||
606 | log the cause of the NMI. | ||
551 | 607 | ||
552 | config SC1200_WDT | 608 | config SC1200_WDT |
553 | tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" | 609 | tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" |
@@ -586,6 +642,24 @@ config PC87413_WDT | |||
586 | 642 | ||
587 | Most people will say N. | 643 | Most people will say N. |
588 | 644 | ||
645 | config NV_TCO | ||
646 | tristate "nVidia TCO Timer/Watchdog" | ||
647 | depends on X86 && PCI | ||
648 | ---help--- | ||
649 | Hardware driver for the TCO timer built into the nVidia Hub family | ||
650 | (such as the MCP51). The TCO (Total Cost of Ownership) timer is a | ||
651 | watchdog timer that will reboot the machine after its second | ||
652 | expiration. The expiration time can be configured with the | ||
653 | "heartbeat" parameter. | ||
654 | |||
655 | On some motherboards the driver may fail to reset the chipset's | ||
656 | NO_REBOOT flag which prevents the watchdog from rebooting the | ||
657 | machine. If this is the case you will get a kernel message like | ||
658 | "failed to reset NO_REBOOT flag, reboot disabled by hardware". | ||
659 | |||
660 | To compile this driver as a module, choose M here: the | ||
661 | module will be called nv_tco. | ||
662 | |||
589 | config RDC321X_WDT | 663 | config RDC321X_WDT |
590 | tristate "RDC R-321x SoC watchdog" | 664 | tristate "RDC R-321x SoC watchdog" |
591 | depends on X86_RDC321X | 665 | depends on X86_RDC321X |
@@ -677,14 +751,15 @@ config SMSC37B787_WDT | |||
677 | Most people will say N. | 751 | Most people will say N. |
678 | 752 | ||
679 | config W83627HF_WDT | 753 | config W83627HF_WDT |
680 | tristate "W83627HF Watchdog Timer" | 754 | tristate "W83627HF/W83627DHG Watchdog Timer" |
681 | depends on X86 | 755 | depends on X86 |
682 | ---help--- | 756 | ---help--- |
683 | This is the driver for the hardware watchdog on the W83627HF chipset | 757 | This is the driver for the hardware watchdog on the W83627HF chipset |
684 | as used in Advantech PC-9578 and Tyan S2721-533 motherboards | 758 | as used in Advantech PC-9578 and Tyan S2721-533 motherboards |
685 | (and likely others). This watchdog simply watches your kernel to | 759 | (and likely others). The driver also supports the W83627DHG chip. |
686 | make sure it doesn't freeze, and if it does, it reboots your computer | 760 | This watchdog simply watches your kernel to make sure it doesn't |
687 | after a certain amount of time. | 761 | freeze, and if it does, it reboots your computer after a certain |
762 | amount of time. | ||
688 | 763 | ||
689 | To compile this driver as a module, choose M here: the | 764 | To compile this driver as a module, choose M here: the |
690 | module will be called w83627hf_wdt. | 765 | module will be called w83627hf_wdt. |
@@ -787,10 +862,22 @@ config SBC_EPX_C3_WATCHDOG | |||
787 | 862 | ||
788 | # M68K Architecture | 863 | # M68K Architecture |
789 | 864 | ||
790 | # M68KNOMMU Architecture | 865 | config M548x_WATCHDOG |
866 | tristate "MCF548x watchdog support" | ||
867 | depends on M548x | ||
868 | help | ||
869 | To compile this driver as a module, choose M here: the | ||
870 | module will be called m548x_wdt. | ||
791 | 871 | ||
792 | # MIPS Architecture | 872 | # MIPS Architecture |
793 | 873 | ||
874 | config ATH79_WDT | ||
875 | tristate "Atheros AR71XX/AR724X/AR913X hardware watchdog" | ||
876 | depends on ATH79 | ||
877 | help | ||
878 | Hardware driver for the built-in watchdog timer on the Atheros | ||
879 | AR71XX/AR724X/AR913X SoCs. | ||
880 | |||
794 | config BCM47XX_WDT | 881 | config BCM47XX_WDT |
795 | tristate "Broadcom BCM47xx Watchdog Timer" | 882 | tristate "Broadcom BCM47xx Watchdog Timer" |
796 | depends on BCM47XX | 883 | depends on BCM47XX |
@@ -857,6 +944,34 @@ config TXX9_WDT | |||
857 | help | 944 | help |
858 | Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs. | 945 | Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs. |
859 | 946 | ||
947 | config OCTEON_WDT | ||
948 | tristate "Cavium OCTEON SOC family Watchdog Timer" | ||
949 | depends on CPU_CAVIUM_OCTEON | ||
950 | default y | ||
951 | select EXPORT_UASM if OCTEON_WDT = m | ||
952 | help | ||
953 | Hardware driver for OCTEON's on chip watchdog timer. | ||
954 | Enables the watchdog for all cores running Linux. It | ||
955 | installs a NMI handler and pokes the watchdog based on an | ||
956 | interrupt. On first expiration of the watchdog, the | ||
957 | interrupt handler pokes it. The second expiration causes an | ||
958 | NMI that prints a message. The third expiration causes a | ||
959 | global soft reset. | ||
960 | |||
961 | When userspace has /dev/watchdog open, no poking is done | ||
962 | from the first interrupt, it is then only poked when the | ||
963 | device is written. | ||
964 | |||
965 | config BCM63XX_WDT | ||
966 | tristate "Broadcom BCM63xx hardware watchdog" | ||
967 | depends on BCM63XX | ||
968 | help | ||
969 | Watchdog driver for the built in watchdog hardware in Broadcom | ||
970 | BCM63xx SoC. | ||
971 | |||
972 | To compile this driver as a loadable module, choose M here. | ||
973 | The module will be called bcm63xx_wdt. | ||
974 | |||
860 | # PARISC Architecture | 975 | # PARISC Architecture |
861 | 976 | ||
862 | # POWERPC Architecture | 977 | # POWERPC Architecture |
@@ -898,12 +1013,32 @@ config PIKA_WDT | |||
898 | the Warp platform. | 1013 | the Warp platform. |
899 | 1014 | ||
900 | config BOOKE_WDT | 1015 | config BOOKE_WDT |
901 | bool "PowerPC Book-E Watchdog Timer" | 1016 | tristate "PowerPC Book-E Watchdog Timer" |
902 | depends on BOOKE || 4xx | 1017 | depends on BOOKE || 4xx |
903 | ---help--- | 1018 | ---help--- |
1019 | Watchdog driver for PowerPC Book-E chips, such as the Freescale | ||
1020 | MPC85xx SOCs and the IBM PowerPC 440. | ||
1021 | |||
904 | Please see Documentation/watchdog/watchdog-api.txt for | 1022 | Please see Documentation/watchdog/watchdog-api.txt for |
905 | more information. | 1023 | more information. |
906 | 1024 | ||
1025 | config BOOKE_WDT_DEFAULT_TIMEOUT | ||
1026 | int "PowerPC Book-E Watchdog Timer Default Timeout" | ||
1027 | depends on BOOKE_WDT | ||
1028 | default 38 if FSL_BOOKE | ||
1029 | range 0 63 if FSL_BOOKE | ||
1030 | default 3 if !FSL_BOOKE | ||
1031 | range 0 3 if !FSL_BOOKE | ||
1032 | help | ||
1033 | Select the default watchdog timer period to be used by the PowerPC | ||
1034 | Book-E watchdog driver. A watchdog "event" occurs when the bit | ||
1035 | position represented by this number transitions from zero to one. | ||
1036 | |||
1037 | For Freescale Book-E processors, this is a number between 0 and 63. | ||
1038 | For other Book-E processors, this is a number between 0 and 3. | ||
1039 | |||
1040 | The value can be overidden by the wdt_period command-line parameter. | ||
1041 | |||
907 | # PPC64 Architecture | 1042 | # PPC64 Architecture |
908 | 1043 | ||
909 | config WATCHDOG_RTAS | 1044 | config WATCHDOG_RTAS |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5e3cb95bb0e9..dd776651917c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -25,6 +25,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o | |||
25 | # ALPHA Architecture | 25 | # ALPHA Architecture |
26 | 26 | ||
27 | # ARM Architecture | 27 | # ARM Architecture |
28 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o | ||
28 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o | 29 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o |
29 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o | 30 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o |
30 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o | 31 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o |
@@ -47,6 +48,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o | |||
47 | obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o | 48 | obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o |
48 | obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o | 49 | obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o |
49 | obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o | 50 | obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o |
51 | obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o | ||
50 | 52 | ||
51 | # AVR32 Architecture | 53 | # AVR32 Architecture |
52 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o | 54 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o |
@@ -65,6 +67,8 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o | |||
65 | obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o | 67 | obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o |
66 | obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o | 68 | obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o |
67 | obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o | 69 | obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o |
70 | obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o | ||
71 | obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o | ||
68 | obj-$(CONFIG_GEODE_WDT) += geodewdt.o | 72 | obj-$(CONFIG_GEODE_WDT) += geodewdt.o |
69 | obj-$(CONFIG_SC520_WDT) += sc520_wdt.o | 73 | obj-$(CONFIG_SC520_WDT) += sc520_wdt.o |
70 | obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o | 74 | obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o |
@@ -83,6 +87,7 @@ obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o | |||
83 | obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o | 87 | obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o |
84 | obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o | 88 | obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o |
85 | obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o | 89 | obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o |
90 | obj-$(CONFIG_NV_TCO) += nv_tco.o | ||
86 | obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o | 91 | obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o |
87 | obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o | 92 | obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o |
88 | obj-$(CONFIG_SBC8360_WDT) += sbc8360.o | 93 | obj-$(CONFIG_SBC8360_WDT) += sbc8360.o |
@@ -101,11 +106,12 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o | |||
101 | # M32R Architecture | 106 | # M32R Architecture |
102 | 107 | ||
103 | # M68K Architecture | 108 | # M68K Architecture |
104 | 109 | obj-$(CONFIG_M548x_WATCHDOG) += m548x_wdt.o | |
105 | # M68KNOMMU Architecture | ||
106 | 110 | ||
107 | # MIPS Architecture | 111 | # MIPS Architecture |
112 | obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o | ||
108 | obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o | 113 | obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o |
114 | obj-$(CONFIG_BCM63XX_WDT) += bcm63xx_wdt.o | ||
109 | obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o | 115 | obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o |
110 | obj-$(CONFIG_INDYDOG) += indydog.o | 116 | obj-$(CONFIG_INDYDOG) += indydog.o |
111 | obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o | 117 | obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o |
@@ -113,6 +119,8 @@ obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o | |||
113 | obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o | 119 | obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o |
114 | obj-$(CONFIG_AR7_WDT) += ar7_wdt.o | 120 | obj-$(CONFIG_AR7_WDT) += ar7_wdt.o |
115 | obj-$(CONFIG_TXX9_WDT) += txx9wdt.o | 121 | obj-$(CONFIG_TXX9_WDT) += txx9wdt.o |
122 | obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o | ||
123 | octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o | ||
116 | 124 | ||
117 | # PARISC Architecture | 125 | # PARISC Architecture |
118 | 126 | ||
diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c index 1e9caea8ff8a..fa4d36033552 100644 --- a/drivers/watchdog/alim1535_wdt.c +++ b/drivers/watchdog/alim1535_wdt.c | |||
@@ -301,7 +301,7 @@ static int ali_notify_sys(struct notifier_block *this, | |||
301 | * want to register another driver on the same PCI id. | 301 | * want to register another driver on the same PCI id. |
302 | */ | 302 | */ |
303 | 303 | ||
304 | static struct pci_device_id ali_pci_tbl[] = { | 304 | static struct pci_device_id ali_pci_tbl[] __used = { |
305 | { PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,}, | 305 | { PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,}, |
306 | { PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,}, | 306 | { PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,}, |
307 | { 0, }, | 307 | { 0, }, |
diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c index d8d4da9a483d..4b7a2b4138ed 100644 --- a/drivers/watchdog/alim7101_wdt.c +++ b/drivers/watchdog/alim7101_wdt.c | |||
@@ -430,7 +430,7 @@ err_out: | |||
430 | module_init(alim7101_wdt_init); | 430 | module_init(alim7101_wdt_init); |
431 | module_exit(alim7101_wdt_unload); | 431 | module_exit(alim7101_wdt_unload); |
432 | 432 | ||
433 | static struct pci_device_id alim7101_pci_tbl[] __devinitdata = { | 433 | static struct pci_device_id alim7101_pci_tbl[] __devinitdata __used = { |
434 | { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) }, | 434 | { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) }, |
435 | { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, | 435 | { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, |
436 | { } | 436 | { } |
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c index c764c52412e4..b29221783598 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c | |||
@@ -267,6 +267,7 @@ static const struct file_operations ar7_wdt_fops = { | |||
267 | .unlocked_ioctl = ar7_wdt_ioctl, | 267 | .unlocked_ioctl = ar7_wdt_ioctl, |
268 | .open = ar7_wdt_open, | 268 | .open = ar7_wdt_open, |
269 | .release = ar7_wdt_release, | 269 | .release = ar7_wdt_release, |
270 | .llseek = no_llseek, | ||
270 | }; | 271 | }; |
271 | 272 | ||
272 | static struct miscdevice ar7_wdt_miscdev = { | 273 | static struct miscdevice ar7_wdt_miscdev = { |
diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c index 1cddf92cb9a6..750bc5281d79 100644 --- a/drivers/watchdog/at32ap700x_wdt.c +++ b/drivers/watchdog/at32ap700x_wdt.c | |||
@@ -346,9 +346,13 @@ static int __init at32_wdt_probe(struct platform_device *pdev) | |||
346 | } else { | 346 | } else { |
347 | wdt->users = 0; | 347 | wdt->users = 0; |
348 | } | 348 | } |
349 | wdt->miscdev.minor = WATCHDOG_MINOR; | 349 | |
350 | wdt->miscdev.name = "watchdog"; | 350 | wdt->miscdev.minor = WATCHDOG_MINOR; |
351 | wdt->miscdev.fops = &at32_wdt_fops; | 351 | wdt->miscdev.name = "watchdog"; |
352 | wdt->miscdev.fops = &at32_wdt_fops; | ||
353 | wdt->miscdev.parent = &pdev->dev; | ||
354 | |||
355 | platform_set_drvdata(pdev, wdt); | ||
352 | 356 | ||
353 | if (at32_wdt_settimeout(timeout)) { | 357 | if (at32_wdt_settimeout(timeout)) { |
354 | at32_wdt_settimeout(TIMEOUT_DEFAULT); | 358 | at32_wdt_settimeout(TIMEOUT_DEFAULT); |
@@ -360,17 +364,17 @@ static int __init at32_wdt_probe(struct platform_device *pdev) | |||
360 | ret = misc_register(&wdt->miscdev); | 364 | ret = misc_register(&wdt->miscdev); |
361 | if (ret) { | 365 | if (ret) { |
362 | dev_dbg(&pdev->dev, "failed to register wdt miscdev\n"); | 366 | dev_dbg(&pdev->dev, "failed to register wdt miscdev\n"); |
363 | goto err_iounmap; | 367 | goto err_register; |
364 | } | 368 | } |
365 | 369 | ||
366 | platform_set_drvdata(pdev, wdt); | ||
367 | wdt->miscdev.parent = &pdev->dev; | ||
368 | dev_info(&pdev->dev, | 370 | dev_info(&pdev->dev, |
369 | "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n", | 371 | "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n", |
370 | wdt->regs, wdt->timeout, nowayout); | 372 | wdt->regs, wdt->timeout, nowayout); |
371 | 373 | ||
372 | return 0; | 374 | return 0; |
373 | 375 | ||
376 | err_register: | ||
377 | platform_set_drvdata(pdev, NULL); | ||
374 | err_iounmap: | 378 | err_iounmap: |
375 | iounmap(wdt->regs); | 379 | iounmap(wdt->regs); |
376 | err_free: | 380 | err_free: |
diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c new file mode 100644 index 000000000000..725c84bfdd76 --- /dev/null +++ b/drivers/watchdog/ath79_wdt.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * Atheros AR71XX/AR724X/AR913X built-in hardware watchdog timer. | ||
3 | * | ||
4 | * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> | ||
5 | * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> | ||
6 | * | ||
7 | * This driver was based on: drivers/watchdog/ixp4xx_wdt.c | ||
8 | * Author: Deepak Saxena <dsaxena@plexity.net> | ||
9 | * Copyright 2004 (c) MontaVista, Software, Inc. | ||
10 | * | ||
11 | * which again was based on sa1100 driver, | ||
12 | * Copyright (C) 2000 Oleg Drokin <green@crimea.edu> | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify it | ||
15 | * under the terms of the GNU General Public License version 2 as published | ||
16 | * by the Free Software Foundation. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/bitops.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/fs.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/watchdog.h> | ||
31 | #include <linux/clk.h> | ||
32 | #include <linux/err.h> | ||
33 | |||
34 | #include <asm/mach-ath79/ath79.h> | ||
35 | #include <asm/mach-ath79/ar71xx_regs.h> | ||
36 | |||
37 | #define DRIVER_NAME "ath79-wdt" | ||
38 | |||
39 | #define WDT_TIMEOUT 15 /* seconds */ | ||
40 | |||
41 | #define WDOG_CTRL_LAST_RESET BIT(31) | ||
42 | #define WDOG_CTRL_ACTION_MASK 3 | ||
43 | #define WDOG_CTRL_ACTION_NONE 0 /* no action */ | ||
44 | #define WDOG_CTRL_ACTION_GPI 1 /* general purpose interrupt */ | ||
45 | #define WDOG_CTRL_ACTION_NMI 2 /* NMI */ | ||
46 | #define WDOG_CTRL_ACTION_FCR 3 /* full chip reset */ | ||
47 | |||
48 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
49 | module_param(nowayout, int, 0); | ||
50 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | ||
51 | "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
52 | |||
53 | static int timeout = WDT_TIMEOUT; | ||
54 | module_param(timeout, int, 0); | ||
55 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds " | ||
56 | "(default=" __MODULE_STRING(WDT_TIMEOUT) "s)"); | ||
57 | |||
58 | static unsigned long wdt_flags; | ||
59 | |||
60 | #define WDT_FLAGS_BUSY 0 | ||
61 | #define WDT_FLAGS_EXPECT_CLOSE 1 | ||
62 | |||
63 | static struct clk *wdt_clk; | ||
64 | static unsigned long wdt_freq; | ||
65 | static int boot_status; | ||
66 | static int max_timeout; | ||
67 | |||
68 | static inline void ath79_wdt_keepalive(void) | ||
69 | { | ||
70 | ath79_reset_wr(AR71XX_RESET_REG_WDOG, wdt_freq * timeout); | ||
71 | } | ||
72 | |||
73 | static inline void ath79_wdt_enable(void) | ||
74 | { | ||
75 | ath79_wdt_keepalive(); | ||
76 | ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_FCR); | ||
77 | } | ||
78 | |||
79 | static inline void ath79_wdt_disable(void) | ||
80 | { | ||
81 | ath79_reset_wr(AR71XX_RESET_REG_WDOG_CTRL, WDOG_CTRL_ACTION_NONE); | ||
82 | } | ||
83 | |||
84 | static int ath79_wdt_set_timeout(int val) | ||
85 | { | ||
86 | if (val < 1 || val > max_timeout) | ||
87 | return -EINVAL; | ||
88 | |||
89 | timeout = val; | ||
90 | ath79_wdt_keepalive(); | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int ath79_wdt_open(struct inode *inode, struct file *file) | ||
96 | { | ||
97 | if (test_and_set_bit(WDT_FLAGS_BUSY, &wdt_flags)) | ||
98 | return -EBUSY; | ||
99 | |||
100 | clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags); | ||
101 | ath79_wdt_enable(); | ||
102 | |||
103 | return nonseekable_open(inode, file); | ||
104 | } | ||
105 | |||
106 | static int ath79_wdt_release(struct inode *inode, struct file *file) | ||
107 | { | ||
108 | if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags)) | ||
109 | ath79_wdt_disable(); | ||
110 | else { | ||
111 | pr_crit(DRIVER_NAME ": device closed unexpectedly, " | ||
112 | "watchdog timer will not stop!\n"); | ||
113 | ath79_wdt_keepalive(); | ||
114 | } | ||
115 | |||
116 | clear_bit(WDT_FLAGS_BUSY, &wdt_flags); | ||
117 | clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags); | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static ssize_t ath79_wdt_write(struct file *file, const char *data, | ||
123 | size_t len, loff_t *ppos) | ||
124 | { | ||
125 | if (len) { | ||
126 | if (!nowayout) { | ||
127 | size_t i; | ||
128 | |||
129 | clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags); | ||
130 | |||
131 | for (i = 0; i != len; i++) { | ||
132 | char c; | ||
133 | |||
134 | if (get_user(c, data + i)) | ||
135 | return -EFAULT; | ||
136 | |||
137 | if (c == 'V') | ||
138 | set_bit(WDT_FLAGS_EXPECT_CLOSE, | ||
139 | &wdt_flags); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | ath79_wdt_keepalive(); | ||
144 | } | ||
145 | |||
146 | return len; | ||
147 | } | ||
148 | |||
149 | static const struct watchdog_info ath79_wdt_info = { | ||
150 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | | ||
151 | WDIOF_MAGICCLOSE | WDIOF_CARDRESET, | ||
152 | .firmware_version = 0, | ||
153 | .identity = "ATH79 watchdog", | ||
154 | }; | ||
155 | |||
156 | static long ath79_wdt_ioctl(struct file *file, unsigned int cmd, | ||
157 | unsigned long arg) | ||
158 | { | ||
159 | void __user *argp = (void __user *)arg; | ||
160 | int __user *p = argp; | ||
161 | int err; | ||
162 | int t; | ||
163 | |||
164 | switch (cmd) { | ||
165 | case WDIOC_GETSUPPORT: | ||
166 | err = copy_to_user(argp, &ath79_wdt_info, | ||
167 | sizeof(ath79_wdt_info)) ? -EFAULT : 0; | ||
168 | break; | ||
169 | |||
170 | case WDIOC_GETSTATUS: | ||
171 | err = put_user(0, p); | ||
172 | break; | ||
173 | |||
174 | case WDIOC_GETBOOTSTATUS: | ||
175 | err = put_user(boot_status, p); | ||
176 | break; | ||
177 | |||
178 | case WDIOC_KEEPALIVE: | ||
179 | ath79_wdt_keepalive(); | ||
180 | err = 0; | ||
181 | break; | ||
182 | |||
183 | case WDIOC_SETTIMEOUT: | ||
184 | err = get_user(t, p); | ||
185 | if (err) | ||
186 | break; | ||
187 | |||
188 | err = ath79_wdt_set_timeout(t); | ||
189 | if (err) | ||
190 | break; | ||
191 | |||
192 | /* fallthrough */ | ||
193 | case WDIOC_GETTIMEOUT: | ||
194 | err = put_user(timeout, p); | ||
195 | break; | ||
196 | |||
197 | default: | ||
198 | err = -ENOTTY; | ||
199 | break; | ||
200 | } | ||
201 | |||
202 | return err; | ||
203 | } | ||
204 | |||
205 | static const struct file_operations ath79_wdt_fops = { | ||
206 | .owner = THIS_MODULE, | ||
207 | .llseek = no_llseek, | ||
208 | .write = ath79_wdt_write, | ||
209 | .unlocked_ioctl = ath79_wdt_ioctl, | ||
210 | .open = ath79_wdt_open, | ||
211 | .release = ath79_wdt_release, | ||
212 | }; | ||
213 | |||
214 | static struct miscdevice ath79_wdt_miscdev = { | ||
215 | .minor = WATCHDOG_MINOR, | ||
216 | .name = "watchdog", | ||
217 | .fops = &ath79_wdt_fops, | ||
218 | }; | ||
219 | |||
220 | static int __devinit ath79_wdt_probe(struct platform_device *pdev) | ||
221 | { | ||
222 | u32 ctrl; | ||
223 | int err; | ||
224 | |||
225 | wdt_clk = clk_get(&pdev->dev, "wdt"); | ||
226 | if (IS_ERR(wdt_clk)) | ||
227 | return PTR_ERR(wdt_clk); | ||
228 | |||
229 | err = clk_enable(wdt_clk); | ||
230 | if (err) | ||
231 | goto err_clk_put; | ||
232 | |||
233 | wdt_freq = clk_get_rate(wdt_clk); | ||
234 | if (!wdt_freq) { | ||
235 | err = -EINVAL; | ||
236 | goto err_clk_disable; | ||
237 | } | ||
238 | |||
239 | max_timeout = (0xfffffffful / wdt_freq); | ||
240 | if (timeout < 1 || timeout > max_timeout) { | ||
241 | timeout = max_timeout; | ||
242 | dev_info(&pdev->dev, | ||
243 | "timeout value must be 0 < timeout < %d, using %d\n", | ||
244 | max_timeout, timeout); | ||
245 | } | ||
246 | |||
247 | ctrl = ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL); | ||
248 | boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0; | ||
249 | |||
250 | err = misc_register(&ath79_wdt_miscdev); | ||
251 | if (err) { | ||
252 | dev_err(&pdev->dev, | ||
253 | "unable to register misc device, err=%d\n", err); | ||
254 | goto err_clk_disable; | ||
255 | } | ||
256 | |||
257 | return 0; | ||
258 | |||
259 | err_clk_disable: | ||
260 | clk_disable(wdt_clk); | ||
261 | err_clk_put: | ||
262 | clk_put(wdt_clk); | ||
263 | return err; | ||
264 | } | ||
265 | |||
266 | static int __devexit ath79_wdt_remove(struct platform_device *pdev) | ||
267 | { | ||
268 | misc_deregister(&ath79_wdt_miscdev); | ||
269 | clk_disable(wdt_clk); | ||
270 | clk_put(wdt_clk); | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static void ath97_wdt_shutdown(struct platform_device *pdev) | ||
275 | { | ||
276 | ath79_wdt_disable(); | ||
277 | } | ||
278 | |||
279 | static struct platform_driver ath79_wdt_driver = { | ||
280 | .remove = __devexit_p(ath79_wdt_remove), | ||
281 | .shutdown = ath97_wdt_shutdown, | ||
282 | .driver = { | ||
283 | .name = DRIVER_NAME, | ||
284 | .owner = THIS_MODULE, | ||
285 | }, | ||
286 | }; | ||
287 | |||
288 | static int __init ath79_wdt_init(void) | ||
289 | { | ||
290 | return platform_driver_probe(&ath79_wdt_driver, ath79_wdt_probe); | ||
291 | } | ||
292 | module_init(ath79_wdt_init); | ||
293 | |||
294 | static void __exit ath79_wdt_exit(void) | ||
295 | { | ||
296 | platform_driver_unregister(&ath79_wdt_driver); | ||
297 | } | ||
298 | module_exit(ath79_wdt_exit); | ||
299 | |||
300 | MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver"); | ||
301 | MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org"); | ||
302 | MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org"); | ||
303 | MODULE_LICENSE("GPL v2"); | ||
304 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||
305 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c new file mode 100644 index 000000000000..3c5045a206dd --- /dev/null +++ b/drivers/watchdog/bcm63xx_wdt.c | |||
@@ -0,0 +1,332 @@ | |||
1 | /* | ||
2 | * Broadcom BCM63xx SoC watchdog driver | ||
3 | * | ||
4 | * Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com> | ||
5 | * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the License, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/bitops.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/miscdevice.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/uaccess.h> | ||
23 | #include <linux/watchdog.h> | ||
24 | #include <linux/timer.h> | ||
25 | #include <linux/jiffies.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/ptrace.h> | ||
28 | #include <linux/resource.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | |||
31 | #include <bcm63xx_cpu.h> | ||
32 | #include <bcm63xx_io.h> | ||
33 | #include <bcm63xx_regs.h> | ||
34 | #include <bcm63xx_timer.h> | ||
35 | |||
36 | #define PFX KBUILD_MODNAME | ||
37 | |||
38 | #define WDT_HZ 50000000 /* Fclk */ | ||
39 | #define WDT_DEFAULT_TIME 30 /* seconds */ | ||
40 | #define WDT_MAX_TIME 256 /* seconds */ | ||
41 | |||
42 | static struct { | ||
43 | void __iomem *regs; | ||
44 | struct timer_list timer; | ||
45 | int default_ticks; | ||
46 | unsigned long inuse; | ||
47 | atomic_t ticks; | ||
48 | } bcm63xx_wdt_device; | ||
49 | |||
50 | static int expect_close; | ||
51 | |||
52 | static int wdt_time = WDT_DEFAULT_TIME; | ||
53 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
54 | module_param(nowayout, int, 0); | ||
55 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
56 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
57 | |||
58 | /* HW functions */ | ||
59 | static void bcm63xx_wdt_hw_start(void) | ||
60 | { | ||
61 | bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG); | ||
62 | bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG); | ||
63 | bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG); | ||
64 | } | ||
65 | |||
66 | static void bcm63xx_wdt_hw_stop(void) | ||
67 | { | ||
68 | bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG); | ||
69 | bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG); | ||
70 | } | ||
71 | |||
72 | static void bcm63xx_wdt_isr(void *data) | ||
73 | { | ||
74 | struct pt_regs *regs = get_irq_regs(); | ||
75 | |||
76 | die(PFX " fire", regs); | ||
77 | } | ||
78 | |||
79 | static void bcm63xx_timer_tick(unsigned long unused) | ||
80 | { | ||
81 | if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) { | ||
82 | bcm63xx_wdt_hw_start(); | ||
83 | mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ); | ||
84 | } else | ||
85 | printk(KERN_CRIT PFX ": watchdog will restart system\n"); | ||
86 | } | ||
87 | |||
88 | static void bcm63xx_wdt_pet(void) | ||
89 | { | ||
90 | atomic_set(&bcm63xx_wdt_device.ticks, wdt_time); | ||
91 | } | ||
92 | |||
93 | static void bcm63xx_wdt_start(void) | ||
94 | { | ||
95 | bcm63xx_wdt_pet(); | ||
96 | bcm63xx_timer_tick(0); | ||
97 | } | ||
98 | |||
99 | static void bcm63xx_wdt_pause(void) | ||
100 | { | ||
101 | del_timer_sync(&bcm63xx_wdt_device.timer); | ||
102 | bcm63xx_wdt_hw_stop(); | ||
103 | } | ||
104 | |||
105 | static int bcm63xx_wdt_settimeout(int new_time) | ||
106 | { | ||
107 | if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) | ||
108 | return -EINVAL; | ||
109 | |||
110 | wdt_time = new_time; | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int bcm63xx_wdt_open(struct inode *inode, struct file *file) | ||
116 | { | ||
117 | if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse)) | ||
118 | return -EBUSY; | ||
119 | |||
120 | bcm63xx_wdt_start(); | ||
121 | return nonseekable_open(inode, file); | ||
122 | } | ||
123 | |||
124 | static int bcm63xx_wdt_release(struct inode *inode, struct file *file) | ||
125 | { | ||
126 | if (expect_close == 42) | ||
127 | bcm63xx_wdt_pause(); | ||
128 | else { | ||
129 | printk(KERN_CRIT PFX | ||
130 | ": Unexpected close, not stopping watchdog!\n"); | ||
131 | bcm63xx_wdt_start(); | ||
132 | } | ||
133 | clear_bit(0, &bcm63xx_wdt_device.inuse); | ||
134 | expect_close = 0; | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static ssize_t bcm63xx_wdt_write(struct file *file, const char *data, | ||
139 | size_t len, loff_t *ppos) | ||
140 | { | ||
141 | if (len) { | ||
142 | if (!nowayout) { | ||
143 | size_t i; | ||
144 | |||
145 | /* In case it was set long ago */ | ||
146 | expect_close = 0; | ||
147 | |||
148 | for (i = 0; i != len; i++) { | ||
149 | char c; | ||
150 | if (get_user(c, data + i)) | ||
151 | return -EFAULT; | ||
152 | if (c == 'V') | ||
153 | expect_close = 42; | ||
154 | } | ||
155 | } | ||
156 | bcm63xx_wdt_pet(); | ||
157 | } | ||
158 | return len; | ||
159 | } | ||
160 | |||
161 | static struct watchdog_info bcm63xx_wdt_info = { | ||
162 | .identity = PFX, | ||
163 | .options = WDIOF_SETTIMEOUT | | ||
164 | WDIOF_KEEPALIVEPING | | ||
165 | WDIOF_MAGICCLOSE, | ||
166 | }; | ||
167 | |||
168 | |||
169 | static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd, | ||
170 | unsigned long arg) | ||
171 | { | ||
172 | void __user *argp = (void __user *)arg; | ||
173 | int __user *p = argp; | ||
174 | int new_value, retval = -EINVAL; | ||
175 | |||
176 | switch (cmd) { | ||
177 | case WDIOC_GETSUPPORT: | ||
178 | return copy_to_user(argp, &bcm63xx_wdt_info, | ||
179 | sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0; | ||
180 | |||
181 | case WDIOC_GETSTATUS: | ||
182 | case WDIOC_GETBOOTSTATUS: | ||
183 | return put_user(0, p); | ||
184 | |||
185 | case WDIOC_SETOPTIONS: | ||
186 | if (get_user(new_value, p)) | ||
187 | return -EFAULT; | ||
188 | |||
189 | if (new_value & WDIOS_DISABLECARD) { | ||
190 | bcm63xx_wdt_pause(); | ||
191 | retval = 0; | ||
192 | } | ||
193 | if (new_value & WDIOS_ENABLECARD) { | ||
194 | bcm63xx_wdt_start(); | ||
195 | retval = 0; | ||
196 | } | ||
197 | |||
198 | return retval; | ||
199 | |||
200 | case WDIOC_KEEPALIVE: | ||
201 | bcm63xx_wdt_pet(); | ||
202 | return 0; | ||
203 | |||
204 | case WDIOC_SETTIMEOUT: | ||
205 | if (get_user(new_value, p)) | ||
206 | return -EFAULT; | ||
207 | |||
208 | if (bcm63xx_wdt_settimeout(new_value)) | ||
209 | return -EINVAL; | ||
210 | |||
211 | bcm63xx_wdt_pet(); | ||
212 | |||
213 | case WDIOC_GETTIMEOUT: | ||
214 | return put_user(wdt_time, p); | ||
215 | |||
216 | default: | ||
217 | return -ENOTTY; | ||
218 | |||
219 | } | ||
220 | } | ||
221 | |||
222 | static const struct file_operations bcm63xx_wdt_fops = { | ||
223 | .owner = THIS_MODULE, | ||
224 | .llseek = no_llseek, | ||
225 | .write = bcm63xx_wdt_write, | ||
226 | .unlocked_ioctl = bcm63xx_wdt_ioctl, | ||
227 | .open = bcm63xx_wdt_open, | ||
228 | .release = bcm63xx_wdt_release, | ||
229 | }; | ||
230 | |||
231 | static struct miscdevice bcm63xx_wdt_miscdev = { | ||
232 | .minor = WATCHDOG_MINOR, | ||
233 | .name = "watchdog", | ||
234 | .fops = &bcm63xx_wdt_fops, | ||
235 | }; | ||
236 | |||
237 | |||
238 | static int __devinit bcm63xx_wdt_probe(struct platform_device *pdev) | ||
239 | { | ||
240 | int ret; | ||
241 | struct resource *r; | ||
242 | |||
243 | setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L); | ||
244 | |||
245 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
246 | if (!r) { | ||
247 | dev_err(&pdev->dev, "failed to get resources\n"); | ||
248 | return -ENODEV; | ||
249 | } | ||
250 | |||
251 | bcm63xx_wdt_device.regs = ioremap_nocache(r->start, r->end - r->start); | ||
252 | if (!bcm63xx_wdt_device.regs) { | ||
253 | dev_err(&pdev->dev, "failed to remap I/O resources\n"); | ||
254 | return -ENXIO; | ||
255 | } | ||
256 | |||
257 | ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL); | ||
258 | if (ret < 0) { | ||
259 | dev_err(&pdev->dev, "failed to register wdt timer isr\n"); | ||
260 | goto unmap; | ||
261 | } | ||
262 | |||
263 | if (bcm63xx_wdt_settimeout(wdt_time)) { | ||
264 | bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME); | ||
265 | dev_info(&pdev->dev, | ||
266 | ": wdt_time value must be 1 <= wdt_time <= 256, using %d\n", | ||
267 | wdt_time); | ||
268 | } | ||
269 | |||
270 | ret = misc_register(&bcm63xx_wdt_miscdev); | ||
271 | if (ret < 0) { | ||
272 | dev_err(&pdev->dev, "failed to register watchdog device\n"); | ||
273 | goto unregister_timer; | ||
274 | } | ||
275 | |||
276 | dev_info(&pdev->dev, " started, timer margin: %d sec\n", | ||
277 | WDT_DEFAULT_TIME); | ||
278 | |||
279 | return 0; | ||
280 | |||
281 | unregister_timer: | ||
282 | bcm63xx_timer_unregister(TIMER_WDT_ID); | ||
283 | unmap: | ||
284 | iounmap(bcm63xx_wdt_device.regs); | ||
285 | return ret; | ||
286 | } | ||
287 | |||
288 | static int __devexit bcm63xx_wdt_remove(struct platform_device *pdev) | ||
289 | { | ||
290 | if (!nowayout) | ||
291 | bcm63xx_wdt_pause(); | ||
292 | |||
293 | misc_deregister(&bcm63xx_wdt_miscdev); | ||
294 | bcm63xx_timer_unregister(TIMER_WDT_ID); | ||
295 | iounmap(bcm63xx_wdt_device.regs); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static void bcm63xx_wdt_shutdown(struct platform_device *pdev) | ||
300 | { | ||
301 | bcm63xx_wdt_pause(); | ||
302 | } | ||
303 | |||
304 | static struct platform_driver bcm63xx_wdt = { | ||
305 | .probe = bcm63xx_wdt_probe, | ||
306 | .remove = __devexit_p(bcm63xx_wdt_remove), | ||
307 | .shutdown = bcm63xx_wdt_shutdown, | ||
308 | .driver = { | ||
309 | .owner = THIS_MODULE, | ||
310 | .name = "bcm63xx-wdt", | ||
311 | } | ||
312 | }; | ||
313 | |||
314 | static int __init bcm63xx_wdt_init(void) | ||
315 | { | ||
316 | return platform_driver_register(&bcm63xx_wdt); | ||
317 | } | ||
318 | |||
319 | static void __exit bcm63xx_wdt_exit(void) | ||
320 | { | ||
321 | platform_driver_unregister(&bcm63xx_wdt); | ||
322 | } | ||
323 | |||
324 | module_init(bcm63xx_wdt_init); | ||
325 | module_exit(bcm63xx_wdt_exit); | ||
326 | |||
327 | MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>"); | ||
328 | MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); | ||
329 | MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog"); | ||
330 | MODULE_LICENSE("GPL"); | ||
331 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
332 | MODULE_ALIAS("platform:bcm63xx-wdt"); | ||
diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c index 9c7ccd1e9088..9042a95fc98c 100644 --- a/drivers/watchdog/bfin_wdt.c +++ b/drivers/watchdog/bfin_wdt.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
24 | #include <linux/uaccess.h> | 24 | #include <linux/uaccess.h> |
25 | #include <asm/blackfin.h> | 25 | #include <asm/blackfin.h> |
26 | #include <asm/bfin_watchdog.h> | ||
26 | 27 | ||
27 | #define stamp(fmt, args...) \ | 28 | #define stamp(fmt, args...) \ |
28 | pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) | 29 | pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) |
@@ -49,24 +50,6 @@ | |||
49 | # define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x) | 50 | # define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x) |
50 | #endif | 51 | #endif |
51 | 52 | ||
52 | /* Bit in SWRST that indicates boot caused by watchdog */ | ||
53 | #define SWRST_RESET_WDOG 0x4000 | ||
54 | |||
55 | /* Bit in WDOG_CTL that indicates watchdog has expired (WDR0) */ | ||
56 | #define WDOG_EXPIRED 0x8000 | ||
57 | |||
58 | /* Masks for WDEV field in WDOG_CTL register */ | ||
59 | #define ICTL_RESET 0x0 | ||
60 | #define ICTL_NMI 0x2 | ||
61 | #define ICTL_GPI 0x4 | ||
62 | #define ICTL_NONE 0x6 | ||
63 | #define ICTL_MASK 0x6 | ||
64 | |||
65 | /* Masks for WDEN field in WDOG_CTL register */ | ||
66 | #define WDEN_MASK 0x0FF0 | ||
67 | #define WDEN_ENABLE 0x0000 | ||
68 | #define WDEN_DISABLE 0x0AD0 | ||
69 | |||
70 | /* some defaults */ | 53 | /* some defaults */ |
71 | #define WATCHDOG_TIMEOUT 20 | 54 | #define WATCHDOG_TIMEOUT 20 |
72 | 55 | ||
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 801ead191499..7e7ec9c35b6a 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Author: Matthew McClintock | 4 | * Author: Matthew McClintock |
5 | * Maintainer: Kumar Gala <galak@kernel.crashing.org> | 5 | * Maintainer: Kumar Gala <galak@kernel.crashing.org> |
6 | * | 6 | * |
7 | * Copyright 2005, 2008 Freescale Semiconductor Inc. | 7 | * Copyright 2005, 2008, 2010 Freescale Semiconductor Inc. |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify it | 9 | * This program is free software; you can redistribute it and/or modify it |
10 | * under the terms of the GNU General Public License as published by the | 10 | * under the terms of the GNU General Public License as published by the |
@@ -33,14 +33,8 @@ | |||
33 | * occur, and the final time the board will reset. | 33 | * occur, and the final time the board will reset. |
34 | */ | 34 | */ |
35 | 35 | ||
36 | #ifdef CONFIG_FSL_BOOKE | ||
37 | #define WDT_PERIOD_DEFAULT 38 /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */ | ||
38 | #else | ||
39 | #define WDT_PERIOD_DEFAULT 3 /* Refer to the PPC40x and PPC4xx manuals */ | ||
40 | #endif /* for timing information */ | ||
41 | |||
42 | u32 booke_wdt_enabled; | 36 | u32 booke_wdt_enabled; |
43 | u32 booke_wdt_period = WDT_PERIOD_DEFAULT; | 37 | u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; |
44 | 38 | ||
45 | #ifdef CONFIG_FSL_BOOKE | 39 | #ifdef CONFIG_FSL_BOOKE |
46 | #define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15)) | 40 | #define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15)) |
@@ -91,6 +85,22 @@ static unsigned int sec_to_period(unsigned int secs) | |||
91 | return 0; | 85 | return 0; |
92 | } | 86 | } |
93 | 87 | ||
88 | static void __booke_wdt_set(void *data) | ||
89 | { | ||
90 | u32 val; | ||
91 | |||
92 | val = mfspr(SPRN_TCR); | ||
93 | val &= ~WDTP_MASK; | ||
94 | val |= WDTP(booke_wdt_period); | ||
95 | |||
96 | mtspr(SPRN_TCR, val); | ||
97 | } | ||
98 | |||
99 | static void booke_wdt_set(void) | ||
100 | { | ||
101 | on_each_cpu(__booke_wdt_set, NULL, 0); | ||
102 | } | ||
103 | |||
94 | static void __booke_wdt_ping(void *data) | 104 | static void __booke_wdt_ping(void *data) |
95 | { | 105 | { |
96 | mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); | 106 | mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); |
@@ -114,6 +124,27 @@ static void __booke_wdt_enable(void *data) | |||
114 | mtspr(SPRN_TCR, val); | 124 | mtspr(SPRN_TCR, val); |
115 | } | 125 | } |
116 | 126 | ||
127 | /** | ||
128 | * booke_wdt_disable - disable the watchdog on the given CPU | ||
129 | * | ||
130 | * This function is called on each CPU. It disables the watchdog on that CPU. | ||
131 | * | ||
132 | * TCR[WRC] cannot be changed once it has been set to non-zero, but we can | ||
133 | * effectively disable the watchdog by setting its period to the maximum value. | ||
134 | */ | ||
135 | static void __booke_wdt_disable(void *data) | ||
136 | { | ||
137 | u32 val; | ||
138 | |||
139 | val = mfspr(SPRN_TCR); | ||
140 | val &= ~(TCR_WIE | WDTP_MASK); | ||
141 | mtspr(SPRN_TCR, val); | ||
142 | |||
143 | /* clear status to make sure nothing is pending */ | ||
144 | __booke_wdt_ping(NULL); | ||
145 | |||
146 | } | ||
147 | |||
117 | static ssize_t booke_wdt_write(struct file *file, const char __user *buf, | 148 | static ssize_t booke_wdt_write(struct file *file, const char __user *buf, |
118 | size_t count, loff_t *ppos) | 149 | size_t count, loff_t *ppos) |
119 | { | 150 | { |
@@ -137,12 +168,12 @@ static long booke_wdt_ioctl(struct file *file, | |||
137 | if (copy_to_user((void *)arg, &ident, sizeof(ident))) | 168 | if (copy_to_user((void *)arg, &ident, sizeof(ident))) |
138 | return -EFAULT; | 169 | return -EFAULT; |
139 | case WDIOC_GETSTATUS: | 170 | case WDIOC_GETSTATUS: |
140 | return put_user(ident.options, p); | 171 | return put_user(0, p); |
141 | case WDIOC_GETBOOTSTATUS: | 172 | case WDIOC_GETBOOTSTATUS: |
142 | /* XXX: something is clearing TSR */ | 173 | /* XXX: something is clearing TSR */ |
143 | tmp = mfspr(SPRN_TSR) & TSR_WRS(3); | 174 | tmp = mfspr(SPRN_TSR) & TSR_WRS(3); |
144 | /* returns 1 if last reset was caused by the WDT */ | 175 | /* returns CARDRESET if last reset was caused by the WDT */ |
145 | return (tmp ? 1 : 0); | 176 | return (tmp ? WDIOF_CARDRESET : 0); |
146 | case WDIOC_SETOPTIONS: | 177 | case WDIOC_SETOPTIONS: |
147 | if (get_user(tmp, p)) | 178 | if (get_user(tmp, p)) |
148 | return -EINVAL; | 179 | return -EINVAL; |
@@ -166,8 +197,7 @@ static long booke_wdt_ioctl(struct file *file, | |||
166 | #else | 197 | #else |
167 | booke_wdt_period = tmp; | 198 | booke_wdt_period = tmp; |
168 | #endif | 199 | #endif |
169 | mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) | | 200 | booke_wdt_set(); |
170 | WDTP(booke_wdt_period)); | ||
171 | return 0; | 201 | return 0; |
172 | case WDIOC_GETTIMEOUT: | 202 | case WDIOC_GETTIMEOUT: |
173 | return put_user(booke_wdt_period, p); | 203 | return put_user(booke_wdt_period, p); |
@@ -178,8 +208,15 @@ static long booke_wdt_ioctl(struct file *file, | |||
178 | return 0; | 208 | return 0; |
179 | } | 209 | } |
180 | 210 | ||
211 | /* wdt_is_active stores wether or not the /dev/watchdog device is opened */ | ||
212 | static unsigned long wdt_is_active; | ||
213 | |||
181 | static int booke_wdt_open(struct inode *inode, struct file *file) | 214 | static int booke_wdt_open(struct inode *inode, struct file *file) |
182 | { | 215 | { |
216 | /* /dev/watchdog can only be opened once */ | ||
217 | if (test_and_set_bit(0, &wdt_is_active)) | ||
218 | return -EBUSY; | ||
219 | |||
183 | spin_lock(&booke_wdt_lock); | 220 | spin_lock(&booke_wdt_lock); |
184 | if (booke_wdt_enabled == 0) { | 221 | if (booke_wdt_enabled == 0) { |
185 | booke_wdt_enabled = 1; | 222 | booke_wdt_enabled = 1; |
@@ -193,12 +230,30 @@ static int booke_wdt_open(struct inode *inode, struct file *file) | |||
193 | return nonseekable_open(inode, file); | 230 | return nonseekable_open(inode, file); |
194 | } | 231 | } |
195 | 232 | ||
233 | static int booke_wdt_release(struct inode *inode, struct file *file) | ||
234 | { | ||
235 | #ifndef CONFIG_WATCHDOG_NOWAYOUT | ||
236 | /* Normally, the watchdog is disabled when /dev/watchdog is closed, but | ||
237 | * if CONFIG_WATCHDOG_NOWAYOUT is defined, then it means that the | ||
238 | * watchdog should remain enabled. So we disable it only if | ||
239 | * CONFIG_WATCHDOG_NOWAYOUT is not defined. | ||
240 | */ | ||
241 | on_each_cpu(__booke_wdt_disable, NULL, 0); | ||
242 | booke_wdt_enabled = 0; | ||
243 | #endif | ||
244 | |||
245 | clear_bit(0, &wdt_is_active); | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
196 | static const struct file_operations booke_wdt_fops = { | 250 | static const struct file_operations booke_wdt_fops = { |
197 | .owner = THIS_MODULE, | 251 | .owner = THIS_MODULE, |
198 | .llseek = no_llseek, | 252 | .llseek = no_llseek, |
199 | .write = booke_wdt_write, | 253 | .write = booke_wdt_write, |
200 | .unlocked_ioctl = booke_wdt_ioctl, | 254 | .unlocked_ioctl = booke_wdt_ioctl, |
201 | .open = booke_wdt_open, | 255 | .open = booke_wdt_open, |
256 | .release = booke_wdt_release, | ||
202 | }; | 257 | }; |
203 | 258 | ||
204 | static struct miscdevice booke_wdt_miscdev = { | 259 | static struct miscdevice booke_wdt_miscdev = { |
@@ -237,4 +292,9 @@ static int __init booke_wdt_init(void) | |||
237 | 292 | ||
238 | return ret; | 293 | return ret; |
239 | } | 294 | } |
240 | device_initcall(booke_wdt_init); | 295 | |
296 | module_init(booke_wdt_init); | ||
297 | module_exit(booke_wdt_exit); | ||
298 | |||
299 | MODULE_DESCRIPTION("PowerPC Book-E watchdog driver"); | ||
300 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index d62b9ce8f773..eca855a55c0d 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <linux/ioport.h> | 25 | #include <linux/ioport.h> |
26 | #include <linux/timer.h> | 26 | #include <linux/timer.h> |
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/smp_lock.h> | 28 | #include <linux/mutex.h> |
29 | #include <linux/io.h> | 29 | #include <linux/io.h> |
30 | #include <linux/of.h> | 30 | #include <linux/of.h> |
31 | #include <linux/of_device.h> | 31 | #include <linux/of_device.h> |
@@ -89,6 +89,7 @@ struct cpwd { | |||
89 | } devs[WD_NUMDEVS]; | 89 | } devs[WD_NUMDEVS]; |
90 | }; | 90 | }; |
91 | 91 | ||
92 | static DEFINE_MUTEX(cpwd_mutex); | ||
92 | static struct cpwd *cpwd_device; | 93 | static struct cpwd *cpwd_device; |
93 | 94 | ||
94 | /* Sun uses Altera PLD EPF8820ATC144-4 | 95 | /* Sun uses Altera PLD EPF8820ATC144-4 |
@@ -368,7 +369,7 @@ static int cpwd_open(struct inode *inode, struct file *f) | |||
368 | { | 369 | { |
369 | struct cpwd *p = cpwd_device; | 370 | struct cpwd *p = cpwd_device; |
370 | 371 | ||
371 | lock_kernel(); | 372 | mutex_lock(&cpwd_mutex); |
372 | switch (iminor(inode)) { | 373 | switch (iminor(inode)) { |
373 | case WD0_MINOR: | 374 | case WD0_MINOR: |
374 | case WD1_MINOR: | 375 | case WD1_MINOR: |
@@ -376,7 +377,7 @@ static int cpwd_open(struct inode *inode, struct file *f) | |||
376 | break; | 377 | break; |
377 | 378 | ||
378 | default: | 379 | default: |
379 | unlock_kernel(); | 380 | mutex_unlock(&cpwd_mutex); |
380 | return -ENODEV; | 381 | return -ENODEV; |
381 | } | 382 | } |
382 | 383 | ||
@@ -386,13 +387,13 @@ static int cpwd_open(struct inode *inode, struct file *f) | |||
386 | IRQF_SHARED, DRIVER_NAME, p)) { | 387 | IRQF_SHARED, DRIVER_NAME, p)) { |
387 | printk(KERN_ERR PFX "Cannot register IRQ %d\n", | 388 | printk(KERN_ERR PFX "Cannot register IRQ %d\n", |
388 | p->irq); | 389 | p->irq); |
389 | unlock_kernel(); | 390 | mutex_unlock(&cpwd_mutex); |
390 | return -EBUSY; | 391 | return -EBUSY; |
391 | } | 392 | } |
392 | p->initialized = true; | 393 | p->initialized = true; |
393 | } | 394 | } |
394 | 395 | ||
395 | unlock_kernel(); | 396 | mutex_unlock(&cpwd_mutex); |
396 | 397 | ||
397 | return nonseekable_open(inode, f); | 398 | return nonseekable_open(inode, f); |
398 | } | 399 | } |
@@ -482,9 +483,9 @@ static long cpwd_compat_ioctl(struct file *file, unsigned int cmd, | |||
482 | case WIOCSTART: | 483 | case WIOCSTART: |
483 | case WIOCSTOP: | 484 | case WIOCSTOP: |
484 | case WIOCGSTAT: | 485 | case WIOCGSTAT: |
485 | lock_kernel(); | 486 | mutex_lock(&cpwd_mutex); |
486 | rval = cpwd_ioctl(file, cmd, arg); | 487 | rval = cpwd_ioctl(file, cmd, arg); |
487 | unlock_kernel(); | 488 | mutex_unlock(&cpwd_mutex); |
488 | break; | 489 | break; |
489 | 490 | ||
490 | /* everything else is handled by the generic compat layer */ | 491 | /* everything else is handled by the generic compat layer */ |
@@ -524,9 +525,10 @@ static const struct file_operations cpwd_fops = { | |||
524 | .write = cpwd_write, | 525 | .write = cpwd_write, |
525 | .read = cpwd_read, | 526 | .read = cpwd_read, |
526 | .release = cpwd_release, | 527 | .release = cpwd_release, |
528 | .llseek = no_llseek, | ||
527 | }; | 529 | }; |
528 | 530 | ||
529 | static int __devinit cpwd_probe(struct of_device *op, | 531 | static int __devinit cpwd_probe(struct platform_device *op, |
530 | const struct of_device_id *match) | 532 | const struct of_device_id *match) |
531 | { | 533 | { |
532 | struct device_node *options; | 534 | struct device_node *options; |
@@ -545,7 +547,7 @@ static int __devinit cpwd_probe(struct of_device *op, | |||
545 | goto out; | 547 | goto out; |
546 | } | 548 | } |
547 | 549 | ||
548 | p->irq = op->irqs[0]; | 550 | p->irq = op->archdata.irqs[0]; |
549 | 551 | ||
550 | spin_lock_init(&p->lock); | 552 | spin_lock_init(&p->lock); |
551 | 553 | ||
@@ -639,7 +641,7 @@ out_free: | |||
639 | goto out; | 641 | goto out; |
640 | } | 642 | } |
641 | 643 | ||
642 | static int __devexit cpwd_remove(struct of_device *op) | 644 | static int __devexit cpwd_remove(struct platform_device *op) |
643 | { | 645 | { |
644 | struct cpwd *p = dev_get_drvdata(&op->dev); | 646 | struct cpwd *p = dev_get_drvdata(&op->dev); |
645 | int i; | 647 | int i; |
@@ -688,12 +690,12 @@ static struct of_platform_driver cpwd_driver = { | |||
688 | 690 | ||
689 | static int __init cpwd_init(void) | 691 | static int __init cpwd_init(void) |
690 | { | 692 | { |
691 | return of_register_driver(&cpwd_driver, &of_bus_type); | 693 | return of_register_platform_driver(&cpwd_driver); |
692 | } | 694 | } |
693 | 695 | ||
694 | static void __exit cpwd_exit(void) | 696 | static void __exit cpwd_exit(void) |
695 | { | 697 | { |
696 | of_unregister_driver(&cpwd_driver); | 698 | of_unregister_platform_driver(&cpwd_driver); |
697 | } | 699 | } |
698 | 700 | ||
699 | module_init(cpwd_init); | 701 | module_init(cpwd_init); |
diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index 59359c9a5e01..726b7df61fd0 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c | |||
@@ -188,6 +188,7 @@ static const struct file_operations ep93xx_wdt_fops = { | |||
188 | .unlocked_ioctl = ep93xx_wdt_ioctl, | 188 | .unlocked_ioctl = ep93xx_wdt_ioctl, |
189 | .open = ep93xx_wdt_open, | 189 | .open = ep93xx_wdt_open, |
190 | .release = ep93xx_wdt_release, | 190 | .release = ep93xx_wdt_release, |
191 | .llseek = no_llseek, | ||
191 | }; | 192 | }; |
192 | 193 | ||
193 | static struct miscdevice ep93xx_wdt_miscdev = { | 194 | static struct miscdevice ep93xx_wdt_miscdev = { |
diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c index d1c4e55b1db0..3f3dc093ad68 100644 --- a/drivers/watchdog/eurotechwdt.c +++ b/drivers/watchdog/eurotechwdt.c | |||
@@ -68,7 +68,6 @@ static spinlock_t eurwdt_lock; | |||
68 | 68 | ||
69 | /* | 69 | /* |
70 | * You must set these - there is no sane way to probe for this board. | 70 | * You must set these - there is no sane way to probe for this board. |
71 | * You can use eurwdt=x,y to set these now. | ||
72 | */ | 71 | */ |
73 | 72 | ||
74 | static int io = 0x3f0; | 73 | static int io = 0x3f0; |
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c new file mode 100644 index 000000000000..d4d8d1fdccc4 --- /dev/null +++ b/drivers/watchdog/f71808e_wdt.c | |||
@@ -0,0 +1,824 @@ | |||
1 | /*************************************************************************** | ||
2 | * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> * | ||
3 | * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> * | ||
4 | * Copyright (C) 2010 Giel van Schijndel <me@mortis.eu> * | ||
5 | * * | ||
6 | * This program is free software; you can redistribute it and/or modify * | ||
7 | * it under the terms of the GNU General Public License as published by * | ||
8 | * the Free Software Foundation; either version 2 of the License, or * | ||
9 | * (at your option) any later version. * | ||
10 | * * | ||
11 | * This program is distributed in the hope that it will be useful, * | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | ||
14 | * GNU General Public License for more details. * | ||
15 | * * | ||
16 | * You should have received a copy of the GNU General Public License * | ||
17 | * along with this program; if not, write to the * | ||
18 | * Free Software Foundation, Inc., * | ||
19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | ||
20 | ***************************************************************************/ | ||
21 | |||
22 | #include <linux/err.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <linux/miscdevice.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/mutex.h> | ||
30 | #include <linux/notifier.h> | ||
31 | #include <linux/reboot.h> | ||
32 | #include <linux/uaccess.h> | ||
33 | #include <linux/watchdog.h> | ||
34 | |||
35 | #define DRVNAME "f71808e_wdt" | ||
36 | |||
37 | #define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical device */ | ||
38 | #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ | ||
39 | #define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */ | ||
40 | |||
41 | #define SIO_REG_LDSEL 0x07 /* Logical device select */ | ||
42 | #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ | ||
43 | #define SIO_REG_DEVREV 0x22 /* Device revision */ | ||
44 | #define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ | ||
45 | #define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */ | ||
46 | #define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */ | ||
47 | #define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */ | ||
48 | #define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */ | ||
49 | #define SIO_REG_ENABLE 0x30 /* Logical device enable */ | ||
50 | #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ | ||
51 | |||
52 | #define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ | ||
53 | #define SIO_F71808_ID 0x0901 /* Chipset ID */ | ||
54 | #define SIO_F71858_ID 0x0507 /* Chipset ID */ | ||
55 | #define SIO_F71862_ID 0x0601 /* Chipset ID */ | ||
56 | #define SIO_F71869_ID 0x0814 /* Chipset ID */ | ||
57 | #define SIO_F71882_ID 0x0541 /* Chipset ID */ | ||
58 | #define SIO_F71889_ID 0x0723 /* Chipset ID */ | ||
59 | |||
60 | #define F71808FG_REG_WDO_CONF 0xf0 | ||
61 | #define F71808FG_REG_WDT_CONF 0xf5 | ||
62 | #define F71808FG_REG_WD_TIME 0xf6 | ||
63 | |||
64 | #define F71808FG_FLAG_WDOUT_EN 7 | ||
65 | |||
66 | #define F71808FG_FLAG_WDTMOUT_STS 5 | ||
67 | #define F71808FG_FLAG_WD_EN 5 | ||
68 | #define F71808FG_FLAG_WD_PULSE 4 | ||
69 | #define F71808FG_FLAG_WD_UNIT 3 | ||
70 | |||
71 | /* Default values */ | ||
72 | #define WATCHDOG_TIMEOUT 60 /* 1 minute default timeout */ | ||
73 | #define WATCHDOG_MAX_TIMEOUT (60 * 255) | ||
74 | #define WATCHDOG_PULSE_WIDTH 125 /* 125 ms, default pulse width for | ||
75 | watchdog signal */ | ||
76 | #define WATCHDOG_F71862FG_PIN 63 /* default watchdog reset output | ||
77 | pin number 63 */ | ||
78 | |||
79 | static unsigned short force_id; | ||
80 | module_param(force_id, ushort, 0); | ||
81 | MODULE_PARM_DESC(force_id, "Override the detected device ID"); | ||
82 | |||
83 | static const int max_timeout = WATCHDOG_MAX_TIMEOUT; | ||
84 | static int timeout = WATCHDOG_TIMEOUT; /* default timeout in seconds */ | ||
85 | module_param(timeout, int, 0); | ||
86 | MODULE_PARM_DESC(timeout, | ||
87 | "Watchdog timeout in seconds. 1<= timeout <=" | ||
88 | __MODULE_STRING(WATCHDOG_MAX_TIMEOUT) " (default=" | ||
89 | __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); | ||
90 | |||
91 | static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH; | ||
92 | module_param(pulse_width, uint, 0); | ||
93 | MODULE_PARM_DESC(pulse_width, | ||
94 | "Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms" | ||
95 | " (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")"); | ||
96 | |||
97 | static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN; | ||
98 | module_param(f71862fg_pin, uint, 0); | ||
99 | MODULE_PARM_DESC(f71862fg_pin, | ||
100 | "Watchdog f71862fg reset output pin configuration. Choose pin 56 or 63" | ||
101 | " (default=" __MODULE_STRING(WATCHDOG_F71862FG_PIN)")"); | ||
102 | |||
103 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
104 | module_param(nowayout, bool, 0444); | ||
105 | MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); | ||
106 | |||
107 | static unsigned int start_withtimeout; | ||
108 | module_param(start_withtimeout, uint, 0); | ||
109 | MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with" | ||
110 | " given initial timeout. Zero (default) disables this feature."); | ||
111 | |||
112 | enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg }; | ||
113 | |||
114 | static const char *f71808e_names[] = { | ||
115 | "f71808fg", | ||
116 | "f71858fg", | ||
117 | "f71862fg", | ||
118 | "f71869", | ||
119 | "f71882fg", | ||
120 | "f71889fg", | ||
121 | }; | ||
122 | |||
123 | /* Super-I/O Function prototypes */ | ||
124 | static inline int superio_inb(int base, int reg); | ||
125 | static inline int superio_inw(int base, int reg); | ||
126 | static inline void superio_outb(int base, int reg, u8 val); | ||
127 | static inline void superio_set_bit(int base, int reg, int bit); | ||
128 | static inline void superio_clear_bit(int base, int reg, int bit); | ||
129 | static inline int superio_enter(int base); | ||
130 | static inline void superio_select(int base, int ld); | ||
131 | static inline void superio_exit(int base); | ||
132 | |||
133 | struct watchdog_data { | ||
134 | unsigned short sioaddr; | ||
135 | enum chips type; | ||
136 | unsigned long opened; | ||
137 | struct mutex lock; | ||
138 | char expect_close; | ||
139 | struct watchdog_info ident; | ||
140 | |||
141 | unsigned short timeout; | ||
142 | u8 timer_val; /* content for the wd_time register */ | ||
143 | char minutes_mode; | ||
144 | u8 pulse_val; /* pulse width flag */ | ||
145 | char pulse_mode; /* enable pulse output mode? */ | ||
146 | char caused_reboot; /* last reboot was by the watchdog */ | ||
147 | }; | ||
148 | |||
149 | static struct watchdog_data watchdog = { | ||
150 | .lock = __MUTEX_INITIALIZER(watchdog.lock), | ||
151 | }; | ||
152 | |||
153 | /* Super I/O functions */ | ||
154 | static inline int superio_inb(int base, int reg) | ||
155 | { | ||
156 | outb(reg, base); | ||
157 | return inb(base + 1); | ||
158 | } | ||
159 | |||
160 | static int superio_inw(int base, int reg) | ||
161 | { | ||
162 | int val; | ||
163 | val = superio_inb(base, reg) << 8; | ||
164 | val |= superio_inb(base, reg + 1); | ||
165 | return val; | ||
166 | } | ||
167 | |||
168 | static inline void superio_outb(int base, int reg, u8 val) | ||
169 | { | ||
170 | outb(reg, base); | ||
171 | outb(val, base + 1); | ||
172 | } | ||
173 | |||
174 | static inline void superio_set_bit(int base, int reg, int bit) | ||
175 | { | ||
176 | unsigned long val = superio_inb(base, reg); | ||
177 | __set_bit(bit, &val); | ||
178 | superio_outb(base, reg, val); | ||
179 | } | ||
180 | |||
181 | static inline void superio_clear_bit(int base, int reg, int bit) | ||
182 | { | ||
183 | unsigned long val = superio_inb(base, reg); | ||
184 | __clear_bit(bit, &val); | ||
185 | superio_outb(base, reg, val); | ||
186 | } | ||
187 | |||
188 | static inline int superio_enter(int base) | ||
189 | { | ||
190 | /* Don't step on other drivers' I/O space by accident */ | ||
191 | if (!request_muxed_region(base, 2, DRVNAME)) { | ||
192 | printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n", | ||
193 | (int)base); | ||
194 | return -EBUSY; | ||
195 | } | ||
196 | |||
197 | /* according to the datasheet the key must be send twice! */ | ||
198 | outb(SIO_UNLOCK_KEY, base); | ||
199 | outb(SIO_UNLOCK_KEY, base); | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static inline void superio_select(int base, int ld) | ||
205 | { | ||
206 | outb(SIO_REG_LDSEL, base); | ||
207 | outb(ld, base + 1); | ||
208 | } | ||
209 | |||
210 | static inline void superio_exit(int base) | ||
211 | { | ||
212 | outb(SIO_LOCK_KEY, base); | ||
213 | release_region(base, 2); | ||
214 | } | ||
215 | |||
216 | static int watchdog_set_timeout(int timeout) | ||
217 | { | ||
218 | if (timeout <= 0 | ||
219 | || timeout > max_timeout) { | ||
220 | printk(KERN_ERR DRVNAME ": watchdog timeout out of range\n"); | ||
221 | return -EINVAL; | ||
222 | } | ||
223 | |||
224 | mutex_lock(&watchdog.lock); | ||
225 | |||
226 | watchdog.timeout = timeout; | ||
227 | if (timeout > 0xff) { | ||
228 | watchdog.timer_val = DIV_ROUND_UP(timeout, 60); | ||
229 | watchdog.minutes_mode = true; | ||
230 | } else { | ||
231 | watchdog.timer_val = timeout; | ||
232 | watchdog.minutes_mode = false; | ||
233 | } | ||
234 | |||
235 | mutex_unlock(&watchdog.lock); | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | static int watchdog_set_pulse_width(unsigned int pw) | ||
241 | { | ||
242 | int err = 0; | ||
243 | |||
244 | mutex_lock(&watchdog.lock); | ||
245 | |||
246 | if (pw <= 1) { | ||
247 | watchdog.pulse_val = 0; | ||
248 | } else if (pw <= 25) { | ||
249 | watchdog.pulse_val = 1; | ||
250 | } else if (pw <= 125) { | ||
251 | watchdog.pulse_val = 2; | ||
252 | } else if (pw <= 5000) { | ||
253 | watchdog.pulse_val = 3; | ||
254 | } else { | ||
255 | printk(KERN_ERR DRVNAME ": pulse width out of range\n"); | ||
256 | err = -EINVAL; | ||
257 | goto exit_unlock; | ||
258 | } | ||
259 | |||
260 | watchdog.pulse_mode = pw; | ||
261 | |||
262 | exit_unlock: | ||
263 | mutex_unlock(&watchdog.lock); | ||
264 | return err; | ||
265 | } | ||
266 | |||
267 | static int watchdog_keepalive(void) | ||
268 | { | ||
269 | int err = 0; | ||
270 | |||
271 | mutex_lock(&watchdog.lock); | ||
272 | err = superio_enter(watchdog.sioaddr); | ||
273 | if (err) | ||
274 | goto exit_unlock; | ||
275 | superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); | ||
276 | |||
277 | if (watchdog.minutes_mode) | ||
278 | /* select minutes for timer units */ | ||
279 | superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, | ||
280 | F71808FG_FLAG_WD_UNIT); | ||
281 | else | ||
282 | /* select seconds for timer units */ | ||
283 | superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, | ||
284 | F71808FG_FLAG_WD_UNIT); | ||
285 | |||
286 | /* Set timer value */ | ||
287 | superio_outb(watchdog.sioaddr, F71808FG_REG_WD_TIME, | ||
288 | watchdog.timer_val); | ||
289 | |||
290 | superio_exit(watchdog.sioaddr); | ||
291 | |||
292 | exit_unlock: | ||
293 | mutex_unlock(&watchdog.lock); | ||
294 | return err; | ||
295 | } | ||
296 | |||
297 | static int f71862fg_pin_configure(unsigned short ioaddr) | ||
298 | { | ||
299 | /* When ioaddr is non-zero the calling function has to take care of | ||
300 | mutex handling and superio preparation! */ | ||
301 | |||
302 | if (f71862fg_pin == 63) { | ||
303 | if (ioaddr) { | ||
304 | /* SPI must be disabled first to use this pin! */ | ||
305 | superio_clear_bit(ioaddr, SIO_REG_ROM_ADDR_SEL, 6); | ||
306 | superio_set_bit(ioaddr, SIO_REG_MFUNCT3, 4); | ||
307 | } | ||
308 | } else if (f71862fg_pin == 56) { | ||
309 | if (ioaddr) | ||
310 | superio_set_bit(ioaddr, SIO_REG_MFUNCT1, 1); | ||
311 | } else { | ||
312 | printk(KERN_ERR DRVNAME ": Invalid argument f71862fg_pin=%d\n", | ||
313 | f71862fg_pin); | ||
314 | return -EINVAL; | ||
315 | } | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static int watchdog_start(void) | ||
320 | { | ||
321 | /* Make sure we don't die as soon as the watchdog is enabled below */ | ||
322 | int err = watchdog_keepalive(); | ||
323 | if (err) | ||
324 | return err; | ||
325 | |||
326 | mutex_lock(&watchdog.lock); | ||
327 | err = superio_enter(watchdog.sioaddr); | ||
328 | if (err) | ||
329 | goto exit_unlock; | ||
330 | superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); | ||
331 | |||
332 | /* Watchdog pin configuration */ | ||
333 | switch (watchdog.type) { | ||
334 | case f71808fg: | ||
335 | /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */ | ||
336 | superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT2, 3); | ||
337 | superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 3); | ||
338 | break; | ||
339 | |||
340 | case f71862fg: | ||
341 | err = f71862fg_pin_configure(watchdog.sioaddr); | ||
342 | if (err) | ||
343 | goto exit_superio; | ||
344 | break; | ||
345 | |||
346 | case f71869: | ||
347 | /* GPIO14 --> WDTRST# */ | ||
348 | superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4); | ||
349 | break; | ||
350 | |||
351 | case f71882fg: | ||
352 | /* Set pin 56 to WDTRST# */ | ||
353 | superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1); | ||
354 | break; | ||
355 | |||
356 | case f71889fg: | ||
357 | /* set pin 40 to WDTRST# */ | ||
358 | superio_outb(watchdog.sioaddr, SIO_REG_MFUNCT3, | ||
359 | superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf); | ||
360 | break; | ||
361 | |||
362 | default: | ||
363 | /* | ||
364 | * 'default' label to shut up the compiler and catch | ||
365 | * programmer errors | ||
366 | */ | ||
367 | err = -ENODEV; | ||
368 | goto exit_superio; | ||
369 | } | ||
370 | |||
371 | superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); | ||
372 | superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0); | ||
373 | superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF, | ||
374 | F71808FG_FLAG_WDOUT_EN); | ||
375 | |||
376 | superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, | ||
377 | F71808FG_FLAG_WD_EN); | ||
378 | |||
379 | if (watchdog.pulse_mode) { | ||
380 | /* Select "pulse" output mode with given duration */ | ||
381 | u8 wdt_conf = superio_inb(watchdog.sioaddr, | ||
382 | F71808FG_REG_WDT_CONF); | ||
383 | |||
384 | /* Set WD_PSWIDTH bits (1:0) */ | ||
385 | wdt_conf = (wdt_conf & 0xfc) | (watchdog.pulse_val & 0x03); | ||
386 | /* Set WD_PULSE to "pulse" mode */ | ||
387 | wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE); | ||
388 | |||
389 | superio_outb(watchdog.sioaddr, F71808FG_REG_WDT_CONF, | ||
390 | wdt_conf); | ||
391 | } else { | ||
392 | /* Select "level" output mode */ | ||
393 | superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, | ||
394 | F71808FG_FLAG_WD_PULSE); | ||
395 | } | ||
396 | |||
397 | exit_superio: | ||
398 | superio_exit(watchdog.sioaddr); | ||
399 | exit_unlock: | ||
400 | mutex_unlock(&watchdog.lock); | ||
401 | |||
402 | return err; | ||
403 | } | ||
404 | |||
405 | static int watchdog_stop(void) | ||
406 | { | ||
407 | int err = 0; | ||
408 | |||
409 | mutex_lock(&watchdog.lock); | ||
410 | err = superio_enter(watchdog.sioaddr); | ||
411 | if (err) | ||
412 | goto exit_unlock; | ||
413 | superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); | ||
414 | |||
415 | superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, | ||
416 | F71808FG_FLAG_WD_EN); | ||
417 | |||
418 | superio_exit(watchdog.sioaddr); | ||
419 | |||
420 | exit_unlock: | ||
421 | mutex_unlock(&watchdog.lock); | ||
422 | |||
423 | return err; | ||
424 | } | ||
425 | |||
426 | static int watchdog_get_status(void) | ||
427 | { | ||
428 | int status = 0; | ||
429 | |||
430 | mutex_lock(&watchdog.lock); | ||
431 | status = (watchdog.caused_reboot) ? WDIOF_CARDRESET : 0; | ||
432 | mutex_unlock(&watchdog.lock); | ||
433 | |||
434 | return status; | ||
435 | } | ||
436 | |||
437 | static bool watchdog_is_running(void) | ||
438 | { | ||
439 | /* | ||
440 | * if we fail to determine the watchdog's status assume it to be | ||
441 | * running to be on the safe side | ||
442 | */ | ||
443 | bool is_running = true; | ||
444 | |||
445 | mutex_lock(&watchdog.lock); | ||
446 | if (superio_enter(watchdog.sioaddr)) | ||
447 | goto exit_unlock; | ||
448 | superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); | ||
449 | |||
450 | is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0)) | ||
451 | && (superio_inb(watchdog.sioaddr, F71808FG_REG_WDT_CONF) | ||
452 | & F71808FG_FLAG_WD_EN); | ||
453 | |||
454 | superio_exit(watchdog.sioaddr); | ||
455 | |||
456 | exit_unlock: | ||
457 | mutex_unlock(&watchdog.lock); | ||
458 | return is_running; | ||
459 | } | ||
460 | |||
461 | /* /dev/watchdog api */ | ||
462 | |||
463 | static int watchdog_open(struct inode *inode, struct file *file) | ||
464 | { | ||
465 | int err; | ||
466 | |||
467 | /* If the watchdog is alive we don't need to start it again */ | ||
468 | if (test_and_set_bit(0, &watchdog.opened)) | ||
469 | return -EBUSY; | ||
470 | |||
471 | err = watchdog_start(); | ||
472 | if (err) { | ||
473 | clear_bit(0, &watchdog.opened); | ||
474 | return err; | ||
475 | } | ||
476 | |||
477 | if (nowayout) | ||
478 | __module_get(THIS_MODULE); | ||
479 | |||
480 | watchdog.expect_close = 0; | ||
481 | return nonseekable_open(inode, file); | ||
482 | } | ||
483 | |||
484 | static int watchdog_release(struct inode *inode, struct file *file) | ||
485 | { | ||
486 | clear_bit(0, &watchdog.opened); | ||
487 | |||
488 | if (!watchdog.expect_close) { | ||
489 | watchdog_keepalive(); | ||
490 | printk(KERN_CRIT DRVNAME | ||
491 | ": Unexpected close, not stopping watchdog!\n"); | ||
492 | } else if (!nowayout) { | ||
493 | watchdog_stop(); | ||
494 | } | ||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | /* | ||
499 | * watchdog_write: | ||
500 | * @file: file handle to the watchdog | ||
501 | * @buf: buffer to write | ||
502 | * @count: count of bytes | ||
503 | * @ppos: pointer to the position to write. No seeks allowed | ||
504 | * | ||
505 | * A write to a watchdog device is defined as a keepalive signal. Any | ||
506 | * write of data will do, as we we don't define content meaning. | ||
507 | */ | ||
508 | |||
509 | static ssize_t watchdog_write(struct file *file, const char __user *buf, | ||
510 | size_t count, loff_t *ppos) | ||
511 | { | ||
512 | if (count) { | ||
513 | if (!nowayout) { | ||
514 | size_t i; | ||
515 | |||
516 | /* In case it was set long ago */ | ||
517 | bool expect_close = false; | ||
518 | |||
519 | for (i = 0; i != count; i++) { | ||
520 | char c; | ||
521 | if (get_user(c, buf + i)) | ||
522 | return -EFAULT; | ||
523 | expect_close = (c == 'V'); | ||
524 | } | ||
525 | |||
526 | /* Properly order writes across fork()ed processes */ | ||
527 | mutex_lock(&watchdog.lock); | ||
528 | watchdog.expect_close = expect_close; | ||
529 | mutex_unlock(&watchdog.lock); | ||
530 | } | ||
531 | |||
532 | /* someone wrote to us, we should restart timer */ | ||
533 | watchdog_keepalive(); | ||
534 | } | ||
535 | return count; | ||
536 | } | ||
537 | |||
538 | /* | ||
539 | * watchdog_ioctl: | ||
540 | * @inode: inode of the device | ||
541 | * @file: file handle to the device | ||
542 | * @cmd: watchdog command | ||
543 | * @arg: argument pointer | ||
544 | * | ||
545 | * The watchdog API defines a common set of functions for all watchdogs | ||
546 | * according to their available features. | ||
547 | */ | ||
548 | static long watchdog_ioctl(struct file *file, unsigned int cmd, | ||
549 | unsigned long arg) | ||
550 | { | ||
551 | int status; | ||
552 | int new_options; | ||
553 | int new_timeout; | ||
554 | union { | ||
555 | struct watchdog_info __user *ident; | ||
556 | int __user *i; | ||
557 | } uarg; | ||
558 | |||
559 | uarg.i = (int __user *)arg; | ||
560 | |||
561 | switch (cmd) { | ||
562 | case WDIOC_GETSUPPORT: | ||
563 | return copy_to_user(uarg.ident, &watchdog.ident, | ||
564 | sizeof(watchdog.ident)) ? -EFAULT : 0; | ||
565 | |||
566 | case WDIOC_GETSTATUS: | ||
567 | status = watchdog_get_status(); | ||
568 | if (status < 0) | ||
569 | return status; | ||
570 | return put_user(status, uarg.i); | ||
571 | |||
572 | case WDIOC_GETBOOTSTATUS: | ||
573 | return put_user(0, uarg.i); | ||
574 | |||
575 | case WDIOC_SETOPTIONS: | ||
576 | if (get_user(new_options, uarg.i)) | ||
577 | return -EFAULT; | ||
578 | |||
579 | if (new_options & WDIOS_DISABLECARD) | ||
580 | watchdog_stop(); | ||
581 | |||
582 | if (new_options & WDIOS_ENABLECARD) | ||
583 | return watchdog_start(); | ||
584 | |||
585 | |||
586 | case WDIOC_KEEPALIVE: | ||
587 | watchdog_keepalive(); | ||
588 | return 0; | ||
589 | |||
590 | case WDIOC_SETTIMEOUT: | ||
591 | if (get_user(new_timeout, uarg.i)) | ||
592 | return -EFAULT; | ||
593 | |||
594 | if (watchdog_set_timeout(new_timeout)) | ||
595 | return -EINVAL; | ||
596 | |||
597 | watchdog_keepalive(); | ||
598 | /* Fall */ | ||
599 | |||
600 | case WDIOC_GETTIMEOUT: | ||
601 | return put_user(watchdog.timeout, uarg.i); | ||
602 | |||
603 | default: | ||
604 | return -ENOTTY; | ||
605 | |||
606 | } | ||
607 | } | ||
608 | |||
609 | static int watchdog_notify_sys(struct notifier_block *this, unsigned long code, | ||
610 | void *unused) | ||
611 | { | ||
612 | if (code == SYS_DOWN || code == SYS_HALT) | ||
613 | watchdog_stop(); | ||
614 | return NOTIFY_DONE; | ||
615 | } | ||
616 | |||
617 | static const struct file_operations watchdog_fops = { | ||
618 | .owner = THIS_MODULE, | ||
619 | .llseek = no_llseek, | ||
620 | .open = watchdog_open, | ||
621 | .release = watchdog_release, | ||
622 | .write = watchdog_write, | ||
623 | .unlocked_ioctl = watchdog_ioctl, | ||
624 | }; | ||
625 | |||
626 | static struct miscdevice watchdog_miscdev = { | ||
627 | .minor = WATCHDOG_MINOR, | ||
628 | .name = "watchdog", | ||
629 | .fops = &watchdog_fops, | ||
630 | }; | ||
631 | |||
632 | static struct notifier_block watchdog_notifier = { | ||
633 | .notifier_call = watchdog_notify_sys, | ||
634 | }; | ||
635 | |||
636 | static int __init watchdog_init(int sioaddr) | ||
637 | { | ||
638 | int wdt_conf, err = 0; | ||
639 | |||
640 | /* No need to lock watchdog.lock here because no entry points | ||
641 | * into the module have been registered yet. | ||
642 | */ | ||
643 | watchdog.sioaddr = sioaddr; | ||
644 | watchdog.ident.options = WDIOC_SETTIMEOUT | ||
645 | | WDIOF_MAGICCLOSE | ||
646 | | WDIOF_KEEPALIVEPING; | ||
647 | |||
648 | snprintf(watchdog.ident.identity, | ||
649 | sizeof(watchdog.ident.identity), "%s watchdog", | ||
650 | f71808e_names[watchdog.type]); | ||
651 | |||
652 | err = superio_enter(sioaddr); | ||
653 | if (err) | ||
654 | return err; | ||
655 | superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); | ||
656 | |||
657 | wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF); | ||
658 | watchdog.caused_reboot = wdt_conf & F71808FG_FLAG_WDTMOUT_STS; | ||
659 | |||
660 | superio_exit(sioaddr); | ||
661 | |||
662 | err = watchdog_set_timeout(timeout); | ||
663 | if (err) | ||
664 | return err; | ||
665 | err = watchdog_set_pulse_width(pulse_width); | ||
666 | if (err) | ||
667 | return err; | ||
668 | |||
669 | err = register_reboot_notifier(&watchdog_notifier); | ||
670 | if (err) | ||
671 | return err; | ||
672 | |||
673 | err = misc_register(&watchdog_miscdev); | ||
674 | if (err) { | ||
675 | printk(KERN_ERR DRVNAME | ||
676 | ": cannot register miscdev on minor=%d\n", | ||
677 | watchdog_miscdev.minor); | ||
678 | goto exit_reboot; | ||
679 | } | ||
680 | |||
681 | if (start_withtimeout) { | ||
682 | if (start_withtimeout <= 0 | ||
683 | || start_withtimeout > max_timeout) { | ||
684 | printk(KERN_ERR DRVNAME | ||
685 | ": starting timeout out of range\n"); | ||
686 | err = -EINVAL; | ||
687 | goto exit_miscdev; | ||
688 | } | ||
689 | |||
690 | err = watchdog_start(); | ||
691 | if (err) { | ||
692 | printk(KERN_ERR DRVNAME | ||
693 | ": cannot start watchdog timer\n"); | ||
694 | goto exit_miscdev; | ||
695 | } | ||
696 | |||
697 | mutex_lock(&watchdog.lock); | ||
698 | err = superio_enter(sioaddr); | ||
699 | if (err) | ||
700 | goto exit_unlock; | ||
701 | superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); | ||
702 | |||
703 | if (start_withtimeout > 0xff) { | ||
704 | /* select minutes for timer units */ | ||
705 | superio_set_bit(sioaddr, F71808FG_REG_WDT_CONF, | ||
706 | F71808FG_FLAG_WD_UNIT); | ||
707 | superio_outb(sioaddr, F71808FG_REG_WD_TIME, | ||
708 | DIV_ROUND_UP(start_withtimeout, 60)); | ||
709 | } else { | ||
710 | /* select seconds for timer units */ | ||
711 | superio_clear_bit(sioaddr, F71808FG_REG_WDT_CONF, | ||
712 | F71808FG_FLAG_WD_UNIT); | ||
713 | superio_outb(sioaddr, F71808FG_REG_WD_TIME, | ||
714 | start_withtimeout); | ||
715 | } | ||
716 | |||
717 | superio_exit(sioaddr); | ||
718 | mutex_unlock(&watchdog.lock); | ||
719 | |||
720 | if (nowayout) | ||
721 | __module_get(THIS_MODULE); | ||
722 | |||
723 | printk(KERN_INFO DRVNAME | ||
724 | ": watchdog started with initial timeout of %u sec\n", | ||
725 | start_withtimeout); | ||
726 | } | ||
727 | |||
728 | return 0; | ||
729 | |||
730 | exit_unlock: | ||
731 | mutex_unlock(&watchdog.lock); | ||
732 | exit_miscdev: | ||
733 | misc_deregister(&watchdog_miscdev); | ||
734 | exit_reboot: | ||
735 | unregister_reboot_notifier(&watchdog_notifier); | ||
736 | |||
737 | return err; | ||
738 | } | ||
739 | |||
740 | static int __init f71808e_find(int sioaddr) | ||
741 | { | ||
742 | u16 devid; | ||
743 | int err = superio_enter(sioaddr); | ||
744 | if (err) | ||
745 | return err; | ||
746 | |||
747 | devid = superio_inw(sioaddr, SIO_REG_MANID); | ||
748 | if (devid != SIO_FINTEK_ID) { | ||
749 | pr_debug(DRVNAME ": Not a Fintek device\n"); | ||
750 | err = -ENODEV; | ||
751 | goto exit; | ||
752 | } | ||
753 | |||
754 | devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID); | ||
755 | switch (devid) { | ||
756 | case SIO_F71808_ID: | ||
757 | watchdog.type = f71808fg; | ||
758 | break; | ||
759 | case SIO_F71862_ID: | ||
760 | watchdog.type = f71862fg; | ||
761 | err = f71862fg_pin_configure(0); /* validate module parameter */ | ||
762 | break; | ||
763 | case SIO_F71869_ID: | ||
764 | watchdog.type = f71869; | ||
765 | break; | ||
766 | case SIO_F71882_ID: | ||
767 | watchdog.type = f71882fg; | ||
768 | break; | ||
769 | case SIO_F71889_ID: | ||
770 | watchdog.type = f71889fg; | ||
771 | break; | ||
772 | case SIO_F71858_ID: | ||
773 | /* Confirmed (by datasheet) not to have a watchdog. */ | ||
774 | err = -ENODEV; | ||
775 | goto exit; | ||
776 | default: | ||
777 | printk(KERN_INFO DRVNAME ": Unrecognized Fintek device: %04x\n", | ||
778 | (unsigned int)devid); | ||
779 | err = -ENODEV; | ||
780 | goto exit; | ||
781 | } | ||
782 | |||
783 | printk(KERN_INFO DRVNAME ": Found %s watchdog chip, revision %d\n", | ||
784 | f71808e_names[watchdog.type], | ||
785 | (int)superio_inb(sioaddr, SIO_REG_DEVREV)); | ||
786 | exit: | ||
787 | superio_exit(sioaddr); | ||
788 | return err; | ||
789 | } | ||
790 | |||
791 | static int __init f71808e_init(void) | ||
792 | { | ||
793 | static const unsigned short addrs[] = { 0x2e, 0x4e }; | ||
794 | int err = -ENODEV; | ||
795 | int i; | ||
796 | |||
797 | for (i = 0; i < ARRAY_SIZE(addrs); i++) { | ||
798 | err = f71808e_find(addrs[i]); | ||
799 | if (err == 0) | ||
800 | break; | ||
801 | } | ||
802 | if (i == ARRAY_SIZE(addrs)) | ||
803 | return err; | ||
804 | |||
805 | return watchdog_init(addrs[i]); | ||
806 | } | ||
807 | |||
808 | static void __exit f71808e_exit(void) | ||
809 | { | ||
810 | if (watchdog_is_running()) { | ||
811 | printk(KERN_WARNING DRVNAME | ||
812 | ": Watchdog timer still running, stopping it\n"); | ||
813 | watchdog_stop(); | ||
814 | } | ||
815 | misc_deregister(&watchdog_miscdev); | ||
816 | unregister_reboot_notifier(&watchdog_notifier); | ||
817 | } | ||
818 | |||
819 | MODULE_DESCRIPTION("F71808E Watchdog Driver"); | ||
820 | MODULE_AUTHOR("Giel van Schijndel <me@mortis.eu>"); | ||
821 | MODULE_LICENSE("GPL"); | ||
822 | |||
823 | module_init(f71808e_init); | ||
824 | module_exit(f71808e_exit); | ||
diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index ca0f4c6cf5ab..f6bd6f10fcec 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/miscdevice.h> | 31 | #include <linux/miscdevice.h> |
32 | #include <linux/watchdog.h> | 32 | #include <linux/watchdog.h> |
33 | #include <linux/fs.h> | ||
33 | #include <linux/of.h> | 34 | #include <linux/of.h> |
34 | #include <linux/of_platform.h> | 35 | #include <linux/of_platform.h> |
35 | #include <linux/io.h> | 36 | #include <linux/io.h> |
@@ -260,7 +261,7 @@ static struct miscdevice gef_wdt_miscdev = { | |||
260 | }; | 261 | }; |
261 | 262 | ||
262 | 263 | ||
263 | static int __devinit gef_wdt_probe(struct of_device *dev, | 264 | static int __devinit gef_wdt_probe(struct platform_device *dev, |
264 | const struct of_device_id *match) | 265 | const struct of_device_id *match) |
265 | { | 266 | { |
266 | int timeout = 10; | 267 | int timeout = 10; |
@@ -273,7 +274,7 @@ static int __devinit gef_wdt_probe(struct of_device *dev, | |||
273 | bus_clk = freq; | 274 | bus_clk = freq; |
274 | 275 | ||
275 | /* Map devices registers into memory */ | 276 | /* Map devices registers into memory */ |
276 | gef_wdt_regs = of_iomap(dev->node, 0); | 277 | gef_wdt_regs = of_iomap(dev->dev.of_node, 0); |
277 | if (gef_wdt_regs == NULL) | 278 | if (gef_wdt_regs == NULL) |
278 | return -ENOMEM; | 279 | return -ENOMEM; |
279 | 280 | ||
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 809e7167a624..24b966d5061a 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
@@ -16,38 +16,55 @@ | |||
16 | #include <linux/device.h> | 16 | #include <linux/device.h> |
17 | #include <linux/fs.h> | 17 | #include <linux/fs.h> |
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/io.h> | 19 | #include <linux/io.h> |
21 | #include <linux/irq.h> | 20 | #include <linux/bitops.h> |
22 | #include <linux/nmi.h> | ||
23 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
24 | #include <linux/miscdevice.h> | 22 | #include <linux/miscdevice.h> |
25 | #include <linux/mm.h> | ||
26 | #include <linux/module.h> | 23 | #include <linux/module.h> |
27 | #include <linux/kdebug.h> | ||
28 | #include <linux/moduleparam.h> | 24 | #include <linux/moduleparam.h> |
29 | #include <linux/notifier.h> | ||
30 | #include <linux/pci.h> | 25 | #include <linux/pci.h> |
31 | #include <linux/pci_ids.h> | 26 | #include <linux/pci_ids.h> |
32 | #include <linux/reboot.h> | ||
33 | #include <linux/sched.h> | ||
34 | #include <linux/timer.h> | ||
35 | #include <linux/types.h> | 27 | #include <linux/types.h> |
36 | #include <linux/uaccess.h> | 28 | #include <linux/uaccess.h> |
37 | #include <linux/watchdog.h> | 29 | #include <linux/watchdog.h> |
30 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
38 | #include <linux/dmi.h> | 31 | #include <linux/dmi.h> |
39 | #include <linux/efi.h> | 32 | #include <linux/spinlock.h> |
40 | #include <linux/string.h> | 33 | #include <linux/nmi.h> |
41 | #include <linux/bootmem.h> | 34 | #include <linux/kdebug.h> |
42 | #include <asm/desc.h> | 35 | #include <linux/notifier.h> |
43 | #include <asm/cacheflush.h> | 36 | #include <asm/cacheflush.h> |
37 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | ||
38 | |||
39 | #define HPWDT_VERSION "1.2.0" | ||
40 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) | ||
41 | #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) | ||
42 | #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) | ||
43 | #define DEFAULT_MARGIN 30 | ||
44 | |||
45 | static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ | ||
46 | static unsigned int reload; /* the computed soft_margin */ | ||
47 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
48 | static char expect_release; | ||
49 | static unsigned long hpwdt_is_open; | ||
50 | |||
51 | static void __iomem *pci_mem_addr; /* the PCI-memory address */ | ||
52 | static unsigned long __iomem *hpwdt_timer_reg; | ||
53 | static unsigned long __iomem *hpwdt_timer_con; | ||
54 | |||
55 | static struct pci_device_id hpwdt_devices[] = { | ||
56 | { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */ | ||
57 | { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */ | ||
58 | {0}, /* terminate list */ | ||
59 | }; | ||
60 | MODULE_DEVICE_TABLE(pci, hpwdt_devices); | ||
44 | 61 | ||
62 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
45 | #define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */ | 63 | #define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */ |
46 | #define CRU_BIOS_SIGNATURE_VALUE 0x55524324 | 64 | #define CRU_BIOS_SIGNATURE_VALUE 0x55524324 |
47 | #define PCI_BIOS32_PARAGRAPH_LEN 16 | 65 | #define PCI_BIOS32_PARAGRAPH_LEN 16 |
48 | #define PCI_ROM_BASE1 0x000F0000 | 66 | #define PCI_ROM_BASE1 0x000F0000 |
49 | #define ROM_SIZE 0x10000 | 67 | #define ROM_SIZE 0x10000 |
50 | #define HPWDT_VERSION "1.1.1" | ||
51 | 68 | ||
52 | struct bios32_service_dir { | 69 | struct bios32_service_dir { |
53 | u32 signature; | 70 | u32 signature; |
@@ -112,37 +129,17 @@ struct cmn_registers { | |||
112 | u32 reflags; | 129 | u32 reflags; |
113 | } __attribute__((packed)); | 130 | } __attribute__((packed)); |
114 | 131 | ||
115 | #define DEFAULT_MARGIN 30 | 132 | static unsigned int hpwdt_nmi_decoding; |
116 | static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ | ||
117 | static unsigned int reload; /* the computed soft_margin */ | ||
118 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
119 | static char expect_release; | ||
120 | static unsigned long hpwdt_is_open; | ||
121 | static unsigned int allow_kdump; | 133 | static unsigned int allow_kdump; |
122 | static unsigned int hpwdt_nmi_sourcing; | ||
123 | static unsigned int priority; /* hpwdt at end of die_notify list */ | 134 | static unsigned int priority; /* hpwdt at end of die_notify list */ |
124 | |||
125 | static void __iomem *pci_mem_addr; /* the PCI-memory address */ | ||
126 | static unsigned long __iomem *hpwdt_timer_reg; | ||
127 | static unsigned long __iomem *hpwdt_timer_con; | ||
128 | |||
129 | static DEFINE_SPINLOCK(rom_lock); | 135 | static DEFINE_SPINLOCK(rom_lock); |
130 | |||
131 | static void *cru_rom_addr; | 136 | static void *cru_rom_addr; |
132 | |||
133 | static struct cmn_registers cmn_regs; | 137 | static struct cmn_registers cmn_regs; |
134 | 138 | ||
135 | static struct pci_device_id hpwdt_devices[] = { | ||
136 | { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, | ||
137 | { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, | ||
138 | {0}, /* terminate list */ | ||
139 | }; | ||
140 | MODULE_DEVICE_TABLE(pci, hpwdt_devices); | ||
141 | |||
142 | extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs, | 139 | extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs, |
143 | unsigned long *pRomEntry); | 140 | unsigned long *pRomEntry); |
144 | 141 | ||
145 | #ifndef CONFIG_X86_64 | 142 | #ifdef CONFIG_X86_32 |
146 | /* --32 Bit Bios------------------------------------------------------------ */ | 143 | /* --32 Bit Bios------------------------------------------------------------ */ |
147 | 144 | ||
148 | #define HPWDT_ARCH 32 | 145 | #define HPWDT_ARCH 32 |
@@ -246,8 +243,8 @@ static int __devinit cru_detect(unsigned long map_entry, | |||
246 | physical_bios_offset); | 243 | physical_bios_offset); |
247 | printk(KERN_DEBUG "hpwdt: CRU Length: 0x%lx\n", | 244 | printk(KERN_DEBUG "hpwdt: CRU Length: 0x%lx\n", |
248 | cru_length); | 245 | cru_length); |
249 | printk(KERN_DEBUG "hpwdt: CRU Mapped Address: 0x%x\n", | 246 | printk(KERN_DEBUG "hpwdt: CRU Mapped Address: %p\n", |
250 | (unsigned int)&cru_rom_addr); | 247 | &cru_rom_addr); |
251 | } | 248 | } |
252 | iounmap(bios32_map); | 249 | iounmap(bios32_map); |
253 | return retval; | 250 | return retval; |
@@ -331,8 +328,9 @@ static int __devinit detect_cru_service(void) | |||
331 | iounmap(p); | 328 | iounmap(p); |
332 | return rc; | 329 | return rc; |
333 | } | 330 | } |
334 | 331 | /* ------------------------------------------------------------------------- */ | |
335 | #else | 332 | #endif /* CONFIG_X86_32 */ |
333 | #ifdef CONFIG_X86_64 | ||
336 | /* --64 Bit Bios------------------------------------------------------------ */ | 334 | /* --64 Bit Bios------------------------------------------------------------ */ |
337 | 335 | ||
338 | #define HPWDT_ARCH 64 | 336 | #define HPWDT_ARCH 64 |
@@ -410,17 +408,16 @@ static int __devinit detect_cru_service(void) | |||
410 | /* if cru_rom_addr has been set then we found a CRU service */ | 408 | /* if cru_rom_addr has been set then we found a CRU service */ |
411 | return ((cru_rom_addr != NULL) ? 0 : -ENODEV); | 409 | return ((cru_rom_addr != NULL) ? 0 : -ENODEV); |
412 | } | 410 | } |
413 | |||
414 | /* ------------------------------------------------------------------------- */ | 411 | /* ------------------------------------------------------------------------- */ |
415 | 412 | #endif /* CONFIG_X86_64 */ | |
416 | #endif | 413 | #endif /* CONFIG_HPWDT_NMI_DECODING */ |
417 | 414 | ||
418 | /* | 415 | /* |
419 | * Watchdog operations | 416 | * Watchdog operations |
420 | */ | 417 | */ |
421 | static void hpwdt_start(void) | 418 | static void hpwdt_start(void) |
422 | { | 419 | { |
423 | reload = (soft_margin * 1000) / 128; | 420 | reload = SECS_TO_TICKS(soft_margin); |
424 | iowrite16(reload, hpwdt_timer_reg); | 421 | iowrite16(reload, hpwdt_timer_reg); |
425 | iowrite16(0x85, hpwdt_timer_con); | 422 | iowrite16(0x85, hpwdt_timer_con); |
426 | } | 423 | } |
@@ -441,8 +438,7 @@ static void hpwdt_ping(void) | |||
441 | 438 | ||
442 | static int hpwdt_change_timer(int new_margin) | 439 | static int hpwdt_change_timer(int new_margin) |
443 | { | 440 | { |
444 | /* Arbitrary, can't find the card's limits */ | 441 | if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) { |
445 | if (new_margin < 5 || new_margin > 600) { | ||
446 | printk(KERN_WARNING | 442 | printk(KERN_WARNING |
447 | "hpwdt: New value passed in is invalid: %d seconds.\n", | 443 | "hpwdt: New value passed in is invalid: %d seconds.\n", |
448 | new_margin); | 444 | new_margin); |
@@ -453,11 +449,17 @@ static int hpwdt_change_timer(int new_margin) | |||
453 | printk(KERN_DEBUG | 449 | printk(KERN_DEBUG |
454 | "hpwdt: New timer passed in is %d seconds.\n", | 450 | "hpwdt: New timer passed in is %d seconds.\n", |
455 | new_margin); | 451 | new_margin); |
456 | reload = (soft_margin * 1000) / 128; | 452 | reload = SECS_TO_TICKS(soft_margin); |
457 | 453 | ||
458 | return 0; | 454 | return 0; |
459 | } | 455 | } |
460 | 456 | ||
457 | static int hpwdt_time_left(void) | ||
458 | { | ||
459 | return TICKS_TO_SECS(ioread16(hpwdt_timer_reg)); | ||
460 | } | ||
461 | |||
462 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
461 | /* | 463 | /* |
462 | * NMI Handler | 464 | * NMI Handler |
463 | */ | 465 | */ |
@@ -467,27 +469,30 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, | |||
467 | unsigned long rom_pl; | 469 | unsigned long rom_pl; |
468 | static int die_nmi_called; | 470 | static int die_nmi_called; |
469 | 471 | ||
470 | if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI) | 472 | if (ulReason != DIE_NMIUNKNOWN) |
471 | return NOTIFY_OK; | 473 | goto out; |
472 | 474 | ||
473 | if (hpwdt_nmi_sourcing) { | 475 | if (!hpwdt_nmi_decoding) |
474 | spin_lock_irqsave(&rom_lock, rom_pl); | 476 | goto out; |
475 | if (!die_nmi_called) | 477 | |
476 | asminline_call(&cmn_regs, cru_rom_addr); | 478 | spin_lock_irqsave(&rom_lock, rom_pl); |
477 | die_nmi_called = 1; | 479 | if (!die_nmi_called) |
478 | spin_unlock_irqrestore(&rom_lock, rom_pl); | 480 | asminline_call(&cmn_regs, cru_rom_addr); |
479 | if (cmn_regs.u1.ral == 0) { | 481 | die_nmi_called = 1; |
480 | printk(KERN_WARNING "hpwdt: An NMI occurred, " | 482 | spin_unlock_irqrestore(&rom_lock, rom_pl); |
481 | "but unable to determine source.\n"); | 483 | if (cmn_regs.u1.ral == 0) { |
482 | } else { | 484 | printk(KERN_WARNING "hpwdt: An NMI occurred, " |
483 | if (allow_kdump) | 485 | "but unable to determine source.\n"); |
484 | hpwdt_stop(); | 486 | } else { |
485 | panic("An NMI occurred, please see the Integrated " | 487 | if (allow_kdump) |
486 | "Management Log for details.\n"); | 488 | hpwdt_stop(); |
487 | } | 489 | panic("An NMI occurred, please see the Integrated " |
490 | "Management Log for details.\n"); | ||
488 | } | 491 | } |
492 | out: | ||
489 | return NOTIFY_OK; | 493 | return NOTIFY_OK; |
490 | } | 494 | } |
495 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | ||
491 | 496 | ||
492 | /* | 497 | /* |
493 | * /dev/watchdog handling | 498 | * /dev/watchdog handling |
@@ -557,7 +562,7 @@ static const struct watchdog_info ident = { | |||
557 | .options = WDIOF_SETTIMEOUT | | 562 | .options = WDIOF_SETTIMEOUT | |
558 | WDIOF_KEEPALIVEPING | | 563 | WDIOF_KEEPALIVEPING | |
559 | WDIOF_MAGICCLOSE, | 564 | WDIOF_MAGICCLOSE, |
560 | .identity = "HP iLO2 HW Watchdog Timer", | 565 | .identity = "HP iLO2+ HW Watchdog Timer", |
561 | }; | 566 | }; |
562 | 567 | ||
563 | static long hpwdt_ioctl(struct file *file, unsigned int cmd, | 568 | static long hpwdt_ioctl(struct file *file, unsigned int cmd, |
@@ -599,6 +604,10 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd, | |||
599 | case WDIOC_GETTIMEOUT: | 604 | case WDIOC_GETTIMEOUT: |
600 | ret = put_user(soft_margin, p); | 605 | ret = put_user(soft_margin, p); |
601 | break; | 606 | break; |
607 | |||
608 | case WDIOC_GETTIMELEFT: | ||
609 | ret = put_user(hpwdt_time_left(), p); | ||
610 | break; | ||
602 | } | 611 | } |
603 | return ret; | 612 | return ret; |
604 | } | 613 | } |
@@ -621,80 +630,40 @@ static struct miscdevice hpwdt_miscdev = { | |||
621 | .fops = &hpwdt_fops, | 630 | .fops = &hpwdt_fops, |
622 | }; | 631 | }; |
623 | 632 | ||
633 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
624 | static struct notifier_block die_notifier = { | 634 | static struct notifier_block die_notifier = { |
625 | .notifier_call = hpwdt_pretimeout, | 635 | .notifier_call = hpwdt_pretimeout, |
626 | .priority = 0, | 636 | .priority = 0, |
627 | }; | 637 | }; |
638 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | ||
628 | 639 | ||
629 | /* | 640 | /* |
630 | * Init & Exit | 641 | * Init & Exit |
631 | */ | 642 | */ |
632 | 643 | ||
633 | #ifdef ARCH_HAS_NMI_WATCHDOG | 644 | #ifdef CONFIG_HPWDT_NMI_DECODING |
634 | static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) | 645 | #ifdef CONFIG_X86_LOCAL_APIC |
646 | static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) | ||
635 | { | 647 | { |
636 | /* | 648 | /* |
637 | * If nmi_watchdog is turned off then we can turn on | 649 | * If nmi_watchdog is turned off then we can turn on |
638 | * our nmi sourcing capability. | 650 | * our nmi decoding capability. |
639 | */ | 651 | */ |
640 | if (!nmi_watchdog_active()) | 652 | hpwdt_nmi_decoding = 1; |
641 | hpwdt_nmi_sourcing = 1; | ||
642 | else | ||
643 | dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this " | ||
644 | "functionality you must reboot with nmi_watchdog=0 " | ||
645 | "and load the hpwdt driver with priority=1.\n"); | ||
646 | } | 653 | } |
647 | #else | 654 | #else |
648 | static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) | 655 | static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) |
649 | { | 656 | { |
650 | dev_warn(&dev->dev, "NMI sourcing is disabled. " | 657 | dev_warn(&dev->dev, "NMI decoding is disabled. " |
651 | "Your kernel does not support a NMI Watchdog.\n"); | 658 | "Your kernel does not support a NMI Watchdog.\n"); |
652 | } | 659 | } |
653 | #endif | 660 | #endif /* CONFIG_X86_LOCAL_APIC */ |
654 | 661 | ||
655 | static int __devinit hpwdt_init_one(struct pci_dev *dev, | 662 | static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) |
656 | const struct pci_device_id *ent) | ||
657 | { | 663 | { |
658 | int retval; | 664 | int retval; |
659 | 665 | ||
660 | /* | 666 | /* |
661 | * Check if we can do NMI sourcing or not | ||
662 | */ | ||
663 | hpwdt_check_nmi_sourcing(dev); | ||
664 | |||
665 | /* | ||
666 | * First let's find out if we are on an iLO2 server. We will | ||
667 | * not run on a legacy ASM box. | ||
668 | * So we only support the G5 ProLiant servers and higher. | ||
669 | */ | ||
670 | if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { | ||
671 | dev_warn(&dev->dev, | ||
672 | "This server does not have an iLO2 ASIC.\n"); | ||
673 | return -ENODEV; | ||
674 | } | ||
675 | |||
676 | if (pci_enable_device(dev)) { | ||
677 | dev_warn(&dev->dev, | ||
678 | "Not possible to enable PCI Device: 0x%x:0x%x.\n", | ||
679 | ent->vendor, ent->device); | ||
680 | return -ENODEV; | ||
681 | } | ||
682 | |||
683 | pci_mem_addr = pci_iomap(dev, 1, 0x80); | ||
684 | if (!pci_mem_addr) { | ||
685 | dev_warn(&dev->dev, | ||
686 | "Unable to detect the iLO2 server memory.\n"); | ||
687 | retval = -ENOMEM; | ||
688 | goto error_pci_iomap; | ||
689 | } | ||
690 | hpwdt_timer_reg = pci_mem_addr + 0x70; | ||
691 | hpwdt_timer_con = pci_mem_addr + 0x72; | ||
692 | |||
693 | /* Make sure that we have a valid soft_margin */ | ||
694 | if (hpwdt_change_timer(soft_margin)) | ||
695 | hpwdt_change_timer(DEFAULT_MARGIN); | ||
696 | |||
697 | /* | ||
698 | * We need to map the ROM to get the CRU service. | 667 | * We need to map the ROM to get the CRU service. |
699 | * For 32 bit Operating Systems we need to go through the 32 Bit | 668 | * For 32 bit Operating Systems we need to go through the 32 Bit |
700 | * BIOS Service Directory | 669 | * BIOS Service Directory |
@@ -705,7 +674,7 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, | |||
705 | dev_warn(&dev->dev, | 674 | dev_warn(&dev->dev, |
706 | "Unable to detect the %d Bit CRU Service.\n", | 675 | "Unable to detect the %d Bit CRU Service.\n", |
707 | HPWDT_ARCH); | 676 | HPWDT_ARCH); |
708 | goto error_get_cru; | 677 | return retval; |
709 | } | 678 | } |
710 | 679 | ||
711 | /* | 680 | /* |
@@ -728,8 +697,86 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, | |||
728 | dev_warn(&dev->dev, | 697 | dev_warn(&dev->dev, |
729 | "Unable to register a die notifier (err=%d).\n", | 698 | "Unable to register a die notifier (err=%d).\n", |
730 | retval); | 699 | retval); |
731 | goto error_die_notifier; | 700 | if (cru_rom_addr) |
701 | iounmap(cru_rom_addr); | ||
702 | } | ||
703 | |||
704 | dev_info(&dev->dev, | ||
705 | "HP Watchdog Timer Driver: NMI decoding initialized" | ||
706 | ", allow kernel dump: %s (default = 0/OFF)" | ||
707 | ", priority: %s (default = 0/LAST).\n", | ||
708 | (allow_kdump == 0) ? "OFF" : "ON", | ||
709 | (priority == 0) ? "LAST" : "FIRST"); | ||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | static void __devexit hpwdt_exit_nmi_decoding(void) | ||
714 | { | ||
715 | unregister_die_notifier(&die_notifier); | ||
716 | if (cru_rom_addr) | ||
717 | iounmap(cru_rom_addr); | ||
718 | } | ||
719 | #else /* !CONFIG_HPWDT_NMI_DECODING */ | ||
720 | static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) | ||
721 | { | ||
722 | } | ||
723 | |||
724 | static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) | ||
725 | { | ||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | static void __devexit hpwdt_exit_nmi_decoding(void) | ||
730 | { | ||
731 | } | ||
732 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | ||
733 | |||
734 | static int __devinit hpwdt_init_one(struct pci_dev *dev, | ||
735 | const struct pci_device_id *ent) | ||
736 | { | ||
737 | int retval; | ||
738 | |||
739 | /* | ||
740 | * Check if we can do NMI decoding or not | ||
741 | */ | ||
742 | hpwdt_check_nmi_decoding(dev); | ||
743 | |||
744 | /* | ||
745 | * First let's find out if we are on an iLO2+ server. We will | ||
746 | * not run on a legacy ASM box. | ||
747 | * So we only support the G5 ProLiant servers and higher. | ||
748 | */ | ||
749 | if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { | ||
750 | dev_warn(&dev->dev, | ||
751 | "This server does not have an iLO2+ ASIC.\n"); | ||
752 | return -ENODEV; | ||
753 | } | ||
754 | |||
755 | if (pci_enable_device(dev)) { | ||
756 | dev_warn(&dev->dev, | ||
757 | "Not possible to enable PCI Device: 0x%x:0x%x.\n", | ||
758 | ent->vendor, ent->device); | ||
759 | return -ENODEV; | ||
760 | } | ||
761 | |||
762 | pci_mem_addr = pci_iomap(dev, 1, 0x80); | ||
763 | if (!pci_mem_addr) { | ||
764 | dev_warn(&dev->dev, | ||
765 | "Unable to detect the iLO2+ server memory.\n"); | ||
766 | retval = -ENOMEM; | ||
767 | goto error_pci_iomap; | ||
732 | } | 768 | } |
769 | hpwdt_timer_reg = pci_mem_addr + 0x70; | ||
770 | hpwdt_timer_con = pci_mem_addr + 0x72; | ||
771 | |||
772 | /* Make sure that we have a valid soft_margin */ | ||
773 | if (hpwdt_change_timer(soft_margin)) | ||
774 | hpwdt_change_timer(DEFAULT_MARGIN); | ||
775 | |||
776 | /* Initialize NMI Decoding functionality */ | ||
777 | retval = hpwdt_init_nmi_decoding(dev); | ||
778 | if (retval != 0) | ||
779 | goto error_init_nmi_decoding; | ||
733 | 780 | ||
734 | retval = misc_register(&hpwdt_miscdev); | 781 | retval = misc_register(&hpwdt_miscdev); |
735 | if (retval < 0) { | 782 | if (retval < 0) { |
@@ -739,23 +786,14 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, | |||
739 | goto error_misc_register; | 786 | goto error_misc_register; |
740 | } | 787 | } |
741 | 788 | ||
742 | printk(KERN_INFO | 789 | dev_info(&dev->dev, "HP Watchdog Timer Driver: %s" |
743 | "hp Watchdog Timer Driver: %s" | 790 | ", timer margin: %d seconds (nowayout=%d).\n", |
744 | ", timer margin: %d seconds (nowayout=%d)" | 791 | HPWDT_VERSION, soft_margin, nowayout); |
745 | ", allow kernel dump: %s (default = 0/OFF)" | ||
746 | ", priority: %s (default = 0/LAST).\n", | ||
747 | HPWDT_VERSION, soft_margin, nowayout, | ||
748 | (allow_kdump == 0) ? "OFF" : "ON", | ||
749 | (priority == 0) ? "LAST" : "FIRST"); | ||
750 | |||
751 | return 0; | 792 | return 0; |
752 | 793 | ||
753 | error_misc_register: | 794 | error_misc_register: |
754 | unregister_die_notifier(&die_notifier); | 795 | hpwdt_exit_nmi_decoding(); |
755 | error_die_notifier: | 796 | error_init_nmi_decoding: |
756 | if (cru_rom_addr) | ||
757 | iounmap(cru_rom_addr); | ||
758 | error_get_cru: | ||
759 | pci_iounmap(dev, pci_mem_addr); | 797 | pci_iounmap(dev, pci_mem_addr); |
760 | error_pci_iomap: | 798 | error_pci_iomap: |
761 | pci_disable_device(dev); | 799 | pci_disable_device(dev); |
@@ -768,10 +806,7 @@ static void __devexit hpwdt_exit(struct pci_dev *dev) | |||
768 | hpwdt_stop(); | 806 | hpwdt_stop(); |
769 | 807 | ||
770 | misc_deregister(&hpwdt_miscdev); | 808 | misc_deregister(&hpwdt_miscdev); |
771 | unregister_die_notifier(&die_notifier); | 809 | hpwdt_exit_nmi_decoding(); |
772 | |||
773 | if (cru_rom_addr) | ||
774 | iounmap(cru_rom_addr); | ||
775 | pci_iounmap(dev, pci_mem_addr); | 810 | pci_iounmap(dev, pci_mem_addr); |
776 | pci_disable_device(dev); | 811 | pci_disable_device(dev); |
777 | } | 812 | } |
@@ -802,16 +837,18 @@ MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | |||
802 | module_param(soft_margin, int, 0); | 837 | module_param(soft_margin, int, 0); |
803 | MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); | 838 | MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); |
804 | 839 | ||
805 | module_param(allow_kdump, int, 0); | ||
806 | MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); | ||
807 | |||
808 | module_param(nowayout, int, 0); | 840 | module_param(nowayout, int, 0); |
809 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | 841 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" |
810 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 842 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
811 | 843 | ||
844 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
845 | module_param(allow_kdump, int, 0); | ||
846 | MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); | ||
847 | |||
812 | module_param(priority, int, 0); | 848 | module_param(priority, int, 0); |
813 | MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" | 849 | MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" |
814 | " (default = 0/Last)\n"); | 850 | " (default = 0/Last)\n"); |
851 | #endif /* !CONFIG_HPWDT_NMI_DECODING */ | ||
815 | 852 | ||
816 | module_init(hpwdt_init); | 853 | module_init(hpwdt_init); |
817 | module_exit(hpwdt_cleanup); | 854 | module_exit(hpwdt_cleanup); |
diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c index 5133bca5ccbe..481d1ad43464 100644 --- a/drivers/watchdog/iTCO_vendor_support.c +++ b/drivers/watchdog/iTCO_vendor_support.c | |||
@@ -101,13 +101,6 @@ static void supermicro_old_pre_stop(unsigned long acpibase) | |||
101 | outl(val32, SMI_EN); /* Needed to deactivate watchdog */ | 101 | outl(val32, SMI_EN); /* Needed to deactivate watchdog */ |
102 | } | 102 | } |
103 | 103 | ||
104 | static void supermicro_old_pre_keepalive(unsigned long acpibase) | ||
105 | { | ||
106 | /* Reload TCO Timer (done in iTCO_wdt_keepalive) + */ | ||
107 | /* Clear "Expire Flag" (Bit 3 of TC01_STS register) */ | ||
108 | outb(0x08, TCO1_STS); | ||
109 | } | ||
110 | |||
111 | /* | 104 | /* |
112 | * Vendor Support: 2 | 105 | * Vendor Support: 2 |
113 | * Board: Super Micro Computer Inc. P4SBx, P4DPx | 106 | * Board: Super Micro Computer Inc. P4SBx, P4DPx |
@@ -337,9 +330,7 @@ EXPORT_SYMBOL(iTCO_vendor_pre_stop); | |||
337 | 330 | ||
338 | void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat) | 331 | void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat) |
339 | { | 332 | { |
340 | if (vendorsupport == SUPERMICRO_OLD_BOARD) | 333 | if (vendorsupport == SUPERMICRO_NEW_BOARD) |
341 | supermicro_old_pre_keepalive(acpibase); | ||
342 | else if (vendorsupport == SUPERMICRO_NEW_BOARD) | ||
343 | supermicro_new_pre_set_heartbeat(heartbeat); | 334 | supermicro_new_pre_set_heartbeat(heartbeat); |
344 | } | 335 | } |
345 | EXPORT_SYMBOL(iTCO_vendor_pre_keepalive); | 336 | EXPORT_SYMBOL(iTCO_vendor_pre_keepalive); |
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 8da886035374..2c6c2b4ad8bf 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * intel TCO Watchdog Driver | 2 | * intel TCO Watchdog Driver |
3 | * | 3 | * |
4 | * (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>. | 4 | * (c) Copyright 2006-2010 Wim Van Sebroeck <wim@iguana.be>. |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License | 7 | * modify it under the terms of the GNU General Public License |
@@ -26,12 +26,15 @@ | |||
26 | * document number 301473-002, 301474-026: 82801F (ICH6) | 26 | * document number 301473-002, 301474-026: 82801F (ICH6) |
27 | * document number 313082-001, 313075-006: 631xESB, 632xESB | 27 | * document number 313082-001, 313075-006: 631xESB, 632xESB |
28 | * document number 307013-003, 307014-024: 82801G (ICH7) | 28 | * document number 307013-003, 307014-024: 82801G (ICH7) |
29 | * document number 322896-001, 322897-001: NM10 | ||
29 | * document number 313056-003, 313057-017: 82801H (ICH8) | 30 | * document number 313056-003, 313057-017: 82801H (ICH8) |
30 | * document number 316972-004, 316973-012: 82801I (ICH9) | 31 | * document number 316972-004, 316973-012: 82801I (ICH9) |
31 | * document number 319973-002, 319974-002: 82801J (ICH10) | 32 | * document number 319973-002, 319974-002: 82801J (ICH10) |
32 | * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH) | 33 | * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH) |
33 | * document number 320066-003, 320257-008: EP80597 (IICH) | 34 | * document number 320066-003, 320257-008: EP80597 (IICH) |
34 | * document number TBD : Cougar Point (CPT) | 35 | * document number 324645-001, 324646-001: Cougar Point (CPT) |
36 | * document number TBD : Patsburg (PBG) | ||
37 | * document number TBD : DH89xxCC | ||
35 | */ | 38 | */ |
36 | 39 | ||
37 | /* | 40 | /* |
@@ -40,7 +43,7 @@ | |||
40 | 43 | ||
41 | /* Module and version information */ | 44 | /* Module and version information */ |
42 | #define DRV_NAME "iTCO_wdt" | 45 | #define DRV_NAME "iTCO_wdt" |
43 | #define DRV_VERSION "1.05" | 46 | #define DRV_VERSION "1.06" |
44 | #define PFX DRV_NAME ": " | 47 | #define PFX DRV_NAME ": " |
45 | 48 | ||
46 | /* Includes */ | 49 | /* Includes */ |
@@ -84,6 +87,7 @@ enum iTCO_chipsets { | |||
84 | TCO_ICH7DH, /* ICH7DH */ | 87 | TCO_ICH7DH, /* ICH7DH */ |
85 | TCO_ICH7M, /* ICH7-M & ICH7-U */ | 88 | TCO_ICH7M, /* ICH7-M & ICH7-U */ |
86 | TCO_ICH7MDH, /* ICH7-M DH */ | 89 | TCO_ICH7MDH, /* ICH7-M DH */ |
90 | TCO_NM10, /* NM10 */ | ||
87 | TCO_ICH8, /* ICH8 & ICH8R */ | 91 | TCO_ICH8, /* ICH8 & ICH8R */ |
88 | TCO_ICH8DH, /* ICH8DH */ | 92 | TCO_ICH8DH, /* ICH8DH */ |
89 | TCO_ICH8DO, /* ICH8DO */ | 93 | TCO_ICH8DO, /* ICH8DO */ |
@@ -146,6 +150,9 @@ enum iTCO_chipsets { | |||
146 | TCO_CPT29, /* Cougar Point */ | 150 | TCO_CPT29, /* Cougar Point */ |
147 | TCO_CPT30, /* Cougar Point */ | 151 | TCO_CPT30, /* Cougar Point */ |
148 | TCO_CPT31, /* Cougar Point */ | 152 | TCO_CPT31, /* Cougar Point */ |
153 | TCO_PBG1, /* Patsburg */ | ||
154 | TCO_PBG2, /* Patsburg */ | ||
155 | TCO_DH89XXCC, /* DH89xxCC */ | ||
149 | }; | 156 | }; |
150 | 157 | ||
151 | static struct { | 158 | static struct { |
@@ -171,6 +178,7 @@ static struct { | |||
171 | {"ICH7DH", 2}, | 178 | {"ICH7DH", 2}, |
172 | {"ICH7-M or ICH7-U", 2}, | 179 | {"ICH7-M or ICH7-U", 2}, |
173 | {"ICH7-M DH", 2}, | 180 | {"ICH7-M DH", 2}, |
181 | {"NM10", 2}, | ||
174 | {"ICH8 or ICH8R", 2}, | 182 | {"ICH8 or ICH8R", 2}, |
175 | {"ICH8DH", 2}, | 183 | {"ICH8DH", 2}, |
176 | {"ICH8DO", 2}, | 184 | {"ICH8DO", 2}, |
@@ -233,6 +241,9 @@ static struct { | |||
233 | {"Cougar Point", 2}, | 241 | {"Cougar Point", 2}, |
234 | {"Cougar Point", 2}, | 242 | {"Cougar Point", 2}, |
235 | {"Cougar Point", 2}, | 243 | {"Cougar Point", 2}, |
244 | {"Patsburg", 2}, | ||
245 | {"Patsburg", 2}, | ||
246 | {"DH89xxCC", 2}, | ||
236 | {NULL, 0} | 247 | {NULL, 0} |
237 | }; | 248 | }; |
238 | 249 | ||
@@ -286,6 +297,7 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = { | |||
286 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)}, | 297 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)}, |
287 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)}, | 298 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)}, |
288 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)}, | 299 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)}, |
300 | { ITCO_PCI_DEVICE(0x27bc, TCO_NM10)}, | ||
289 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)}, | 301 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)}, |
290 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)}, | 302 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)}, |
291 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)}, | 303 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)}, |
@@ -348,6 +360,9 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = { | |||
348 | { ITCO_PCI_DEVICE(0x1c5d, TCO_CPT29)}, | 360 | { ITCO_PCI_DEVICE(0x1c5d, TCO_CPT29)}, |
349 | { ITCO_PCI_DEVICE(0x1c5e, TCO_CPT30)}, | 361 | { ITCO_PCI_DEVICE(0x1c5e, TCO_CPT30)}, |
350 | { ITCO_PCI_DEVICE(0x1c5f, TCO_CPT31)}, | 362 | { ITCO_PCI_DEVICE(0x1c5f, TCO_CPT31)}, |
363 | { ITCO_PCI_DEVICE(0x1d40, TCO_PBG1)}, | ||
364 | { ITCO_PCI_DEVICE(0x1d41, TCO_PBG2)}, | ||
365 | { ITCO_PCI_DEVICE(0x2310, TCO_DH89XXCC)}, | ||
351 | { 0, }, /* End of list */ | 366 | { 0, }, /* End of list */ |
352 | }; | 367 | }; |
353 | MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); | 368 | MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); |
@@ -374,7 +389,7 @@ static char expect_release; | |||
374 | static struct { /* this is private data for the iTCO_wdt device */ | 389 | static struct { /* this is private data for the iTCO_wdt device */ |
375 | /* TCO version/generation */ | 390 | /* TCO version/generation */ |
376 | unsigned int iTCO_version; | 391 | unsigned int iTCO_version; |
377 | /* The cards ACPIBASE address (TCOBASE = ACPIBASE+0x60) */ | 392 | /* The device's ACPIBASE address (TCOBASE = ACPIBASE+0x60) */ |
378 | unsigned long ACPIBASE; | 393 | unsigned long ACPIBASE; |
379 | /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/ | 394 | /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/ |
380 | unsigned long __iomem *gcs; | 395 | unsigned long __iomem *gcs; |
@@ -391,8 +406,8 @@ static struct platform_device *iTCO_wdt_platform_device; | |||
391 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ | 406 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ |
392 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ | 407 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ |
393 | module_param(heartbeat, int, 0); | 408 | module_param(heartbeat, int, 0); |
394 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. " | 409 | MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. " |
395 | "(2<heartbeat<39 (TCO v1) or 613 (TCO v2), default=" | 410 | "5..76 (TCO v1) or 3..614 (TCO v2), default=" |
396 | __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); | 411 | __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); |
397 | 412 | ||
398 | static int nowayout = WATCHDOG_NOWAYOUT; | 413 | static int nowayout = WATCHDOG_NOWAYOUT; |
@@ -467,7 +482,7 @@ static int iTCO_wdt_start(void) | |||
467 | if (iTCO_wdt_unset_NO_REBOOT_bit()) { | 482 | if (iTCO_wdt_unset_NO_REBOOT_bit()) { |
468 | spin_unlock(&iTCO_wdt_private.io_lock); | 483 | spin_unlock(&iTCO_wdt_private.io_lock); |
469 | printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, " | 484 | printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, " |
470 | "reboot disabled by hardware\n"); | 485 | "reboot disabled by hardware/BIOS\n"); |
471 | return -EIO; | 486 | return -EIO; |
472 | } | 487 | } |
473 | 488 | ||
@@ -523,8 +538,13 @@ static int iTCO_wdt_keepalive(void) | |||
523 | /* Reload the timer by writing to the TCO Timer Counter register */ | 538 | /* Reload the timer by writing to the TCO Timer Counter register */ |
524 | if (iTCO_wdt_private.iTCO_version == 2) | 539 | if (iTCO_wdt_private.iTCO_version == 2) |
525 | outw(0x01, TCO_RLD); | 540 | outw(0x01, TCO_RLD); |
526 | else if (iTCO_wdt_private.iTCO_version == 1) | 541 | else if (iTCO_wdt_private.iTCO_version == 1) { |
542 | /* Reset the timeout status bit so that the timer | ||
543 | * needs to count down twice again before rebooting */ | ||
544 | outw(0x0008, TCO1_STS); /* write 1 to clear bit */ | ||
545 | |||
527 | outb(0x01, TCO_RLD); | 546 | outb(0x01, TCO_RLD); |
547 | } | ||
528 | 548 | ||
529 | spin_unlock(&iTCO_wdt_private.io_lock); | 549 | spin_unlock(&iTCO_wdt_private.io_lock); |
530 | return 0; | 550 | return 0; |
@@ -537,6 +557,11 @@ static int iTCO_wdt_set_heartbeat(int t) | |||
537 | unsigned int tmrval; | 557 | unsigned int tmrval; |
538 | 558 | ||
539 | tmrval = seconds_to_ticks(t); | 559 | tmrval = seconds_to_ticks(t); |
560 | |||
561 | /* For TCO v1 the timer counts down twice before rebooting */ | ||
562 | if (iTCO_wdt_private.iTCO_version == 1) | ||
563 | tmrval /= 2; | ||
564 | |||
540 | /* from the specs: */ | 565 | /* from the specs: */ |
541 | /* "Values of 0h-3h are ignored and should not be attempted" */ | 566 | /* "Values of 0h-3h are ignored and should not be attempted" */ |
542 | if (tmrval < 0x04) | 567 | if (tmrval < 0x04) |
@@ -593,6 +618,8 @@ static int iTCO_wdt_get_timeleft(int *time_left) | |||
593 | spin_lock(&iTCO_wdt_private.io_lock); | 618 | spin_lock(&iTCO_wdt_private.io_lock); |
594 | val8 = inb(TCO_RLD); | 619 | val8 = inb(TCO_RLD); |
595 | val8 &= 0x3f; | 620 | val8 &= 0x3f; |
621 | if (!(inw(TCO1_STS) & 0x0008)) | ||
622 | val8 += (inb(TCOv1_TMR) & 0x3f); | ||
596 | spin_unlock(&iTCO_wdt_private.io_lock); | 623 | spin_unlock(&iTCO_wdt_private.io_lock); |
597 | 624 | ||
598 | *time_left = (val8 * 6) / 10; | 625 | *time_left = (val8 * 6) / 10; |
@@ -769,8 +796,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
769 | base_address &= 0x0000ff80; | 796 | base_address &= 0x0000ff80; |
770 | if (base_address == 0x00000000) { | 797 | if (base_address == 0x00000000) { |
771 | /* Something's wrong here, ACPIBASE has to be set */ | 798 | /* Something's wrong here, ACPIBASE has to be set */ |
772 | printk(KERN_ERR PFX "failed to get TCOBASE address\n"); | 799 | printk(KERN_ERR PFX "failed to get TCOBASE address, " |
773 | pci_dev_put(pdev); | 800 | "device disabled by hardware/BIOS\n"); |
774 | return -ENODEV; | 801 | return -ENODEV; |
775 | } | 802 | } |
776 | iTCO_wdt_private.iTCO_version = | 803 | iTCO_wdt_private.iTCO_version = |
@@ -785,7 +812,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
785 | if (iTCO_wdt_private.iTCO_version == 2) { | 812 | if (iTCO_wdt_private.iTCO_version == 2) { |
786 | pci_read_config_dword(pdev, 0xf0, &base_address); | 813 | pci_read_config_dword(pdev, 0xf0, &base_address); |
787 | if ((base_address & 1) == 0) { | 814 | if ((base_address & 1) == 0) { |
788 | printk(KERN_ERR PFX "RCBA is disabled by hardware\n"); | 815 | printk(KERN_ERR PFX "RCBA is disabled by hardware" |
816 | "/BIOS, device disabled\n"); | ||
789 | ret = -ENODEV; | 817 | ret = -ENODEV; |
790 | goto out; | 818 | goto out; |
791 | } | 819 | } |
@@ -796,7 +824,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
796 | /* Check chipset's NO_REBOOT bit */ | 824 | /* Check chipset's NO_REBOOT bit */ |
797 | if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { | 825 | if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { |
798 | printk(KERN_INFO PFX "unable to reset NO_REBOOT flag, " | 826 | printk(KERN_INFO PFX "unable to reset NO_REBOOT flag, " |
799 | "platform may have disabled it\n"); | 827 | "device disabled by hardware/BIOS\n"); |
800 | ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ | 828 | ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ |
801 | goto out_unmap; | 829 | goto out_unmap; |
802 | } | 830 | } |
@@ -807,7 +835,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
807 | /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ | 835 | /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ |
808 | if (!request_region(SMI_EN, 4, "iTCO_wdt")) { | 836 | if (!request_region(SMI_EN, 4, "iTCO_wdt")) { |
809 | printk(KERN_ERR PFX | 837 | printk(KERN_ERR PFX |
810 | "I/O address 0x%04lx already in use\n", SMI_EN); | 838 | "I/O address 0x%04lx already in use, " |
839 | "device disabled\n", SMI_EN); | ||
811 | ret = -EIO; | 840 | ret = -EIO; |
812 | goto out_unmap; | 841 | goto out_unmap; |
813 | } | 842 | } |
@@ -819,8 +848,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
819 | /* The TCO I/O registers reside in a 32-byte range pointed to | 848 | /* The TCO I/O registers reside in a 32-byte range pointed to |
820 | by the TCOBASE value */ | 849 | by the TCOBASE value */ |
821 | if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) { | 850 | if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) { |
822 | printk(KERN_ERR PFX "I/O address 0x%04lx already in use\n", | 851 | printk(KERN_ERR PFX "I/O address 0x%04lx already in use " |
823 | TCOBASE); | 852 | "device disabled\n", TCOBASE); |
824 | ret = -EIO; | 853 | ret = -EIO; |
825 | goto unreg_smi_en; | 854 | goto unreg_smi_en; |
826 | } | 855 | } |
@@ -832,9 +861,9 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
832 | TCOBASE); | 861 | TCOBASE); |
833 | 862 | ||
834 | /* Clear out the (probably old) status */ | 863 | /* Clear out the (probably old) status */ |
835 | outb(8, TCO1_STS); /* Clear the Time Out Status bit */ | 864 | outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ |
836 | outb(2, TCO2_STS); /* Clear SECOND_TO_STS bit */ | 865 | outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ |
837 | outb(4, TCO2_STS); /* Clear BOOT_STS bit */ | 866 | outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ |
838 | 867 | ||
839 | /* Make sure the watchdog is not running */ | 868 | /* Make sure the watchdog is not running */ |
840 | iTCO_wdt_stop(); | 869 | iTCO_wdt_stop(); |
@@ -844,8 +873,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
844 | if (iTCO_wdt_set_heartbeat(heartbeat)) { | 873 | if (iTCO_wdt_set_heartbeat(heartbeat)) { |
845 | iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT); | 874 | iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT); |
846 | printk(KERN_INFO PFX | 875 | printk(KERN_INFO PFX |
847 | "heartbeat value must be 2 < heartbeat < 39 (TCO v1) " | 876 | "timeout value out of range, using %d\n", heartbeat); |
848 | "or 613 (TCO v2), using %d\n", heartbeat); | ||
849 | } | 877 | } |
850 | 878 | ||
851 | ret = misc_register(&iTCO_wdt_miscdev); | 879 | ret = misc_register(&iTCO_wdt_miscdev); |
@@ -869,7 +897,6 @@ out_unmap: | |||
869 | if (iTCO_wdt_private.iTCO_version == 2) | 897 | if (iTCO_wdt_private.iTCO_version == 2) |
870 | iounmap(iTCO_wdt_private.gcs); | 898 | iounmap(iTCO_wdt_private.gcs); |
871 | out: | 899 | out: |
872 | pci_dev_put(iTCO_wdt_private.pdev); | ||
873 | iTCO_wdt_private.ACPIBASE = 0; | 900 | iTCO_wdt_private.ACPIBASE = 0; |
874 | return ret; | 901 | return ret; |
875 | } | 902 | } |
@@ -910,7 +937,7 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev) | |||
910 | } | 937 | } |
911 | 938 | ||
912 | if (!found) | 939 | if (!found) |
913 | printk(KERN_INFO PFX "No card detected\n"); | 940 | printk(KERN_INFO PFX "No device detected.\n"); |
914 | 941 | ||
915 | return ret; | 942 | return ret; |
916 | } | 943 | } |
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c new file mode 100644 index 000000000000..86f7cac1026c --- /dev/null +++ b/drivers/watchdog/imx2_wdt.c | |||
@@ -0,0 +1,357 @@ | |||
1 | /* | ||
2 | * Watchdog driver for IMX2 and later processors | ||
3 | * | ||
4 | * Copyright (C) 2010 Wolfram Sang, Pengutronix e.K. <w.sang@pengutronix.de> | ||
5 | * | ||
6 | * some parts adapted by similar drivers from Darius Augulis and Vladimir | ||
7 | * Zapolskiy, additional improvements by Wim Van Sebroeck. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License version 2 as published by | ||
11 | * the Free Software Foundation. | ||
12 | * | ||
13 | * NOTE: MX1 has a slightly different Watchdog than MX2 and later: | ||
14 | * | ||
15 | * MX1: MX2+: | ||
16 | * ---- ----- | ||
17 | * Registers: 32-bit 16-bit | ||
18 | * Stopable timer: Yes No | ||
19 | * Need to enable clk: No Yes | ||
20 | * Halt on suspend: Manual Can be automatic | ||
21 | */ | ||
22 | |||
23 | #include <linux/init.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/watchdog.h> | ||
30 | #include <linux/clk.h> | ||
31 | #include <linux/fs.h> | ||
32 | #include <linux/io.h> | ||
33 | #include <linux/uaccess.h> | ||
34 | #include <linux/timer.h> | ||
35 | #include <linux/jiffies.h> | ||
36 | #include <mach/hardware.h> | ||
37 | |||
38 | #define DRIVER_NAME "imx2-wdt" | ||
39 | |||
40 | #define IMX2_WDT_WCR 0x00 /* Control Register */ | ||
41 | #define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ | ||
42 | #define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */ | ||
43 | #define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */ | ||
44 | |||
45 | #define IMX2_WDT_WSR 0x02 /* Service Register */ | ||
46 | #define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */ | ||
47 | #define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */ | ||
48 | |||
49 | #define IMX2_WDT_MAX_TIME 128 | ||
50 | #define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */ | ||
51 | |||
52 | #define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8) | ||
53 | |||
54 | #define IMX2_WDT_STATUS_OPEN 0 | ||
55 | #define IMX2_WDT_STATUS_STARTED 1 | ||
56 | #define IMX2_WDT_EXPECT_CLOSE 2 | ||
57 | |||
58 | static struct { | ||
59 | struct clk *clk; | ||
60 | void __iomem *base; | ||
61 | unsigned timeout; | ||
62 | unsigned long status; | ||
63 | struct timer_list timer; /* Pings the watchdog when closed */ | ||
64 | } imx2_wdt; | ||
65 | |||
66 | static struct miscdevice imx2_wdt_miscdev; | ||
67 | |||
68 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
69 | module_param(nowayout, int, 0); | ||
70 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
71 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
72 | |||
73 | |||
74 | static unsigned timeout = IMX2_WDT_DEFAULT_TIME; | ||
75 | module_param(timeout, uint, 0); | ||
76 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" | ||
77 | __MODULE_STRING(IMX2_WDT_DEFAULT_TIME) ")"); | ||
78 | |||
79 | static const struct watchdog_info imx2_wdt_info = { | ||
80 | .identity = "imx2+ watchdog", | ||
81 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, | ||
82 | }; | ||
83 | |||
84 | static inline void imx2_wdt_setup(void) | ||
85 | { | ||
86 | u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); | ||
87 | |||
88 | /* Strip the old watchdog Time-Out value */ | ||
89 | val &= ~IMX2_WDT_WCR_WT; | ||
90 | /* Generate reset if WDOG times out */ | ||
91 | val &= ~IMX2_WDT_WCR_WRE; | ||
92 | /* Keep Watchdog Disabled */ | ||
93 | val &= ~IMX2_WDT_WCR_WDE; | ||
94 | /* Set the watchdog's Time-Out value */ | ||
95 | val |= WDOG_SEC_TO_COUNT(imx2_wdt.timeout); | ||
96 | |||
97 | __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); | ||
98 | |||
99 | /* enable the watchdog */ | ||
100 | val |= IMX2_WDT_WCR_WDE; | ||
101 | __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); | ||
102 | } | ||
103 | |||
104 | static inline void imx2_wdt_ping(void) | ||
105 | { | ||
106 | __raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR); | ||
107 | __raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR); | ||
108 | } | ||
109 | |||
110 | static void imx2_wdt_timer_ping(unsigned long arg) | ||
111 | { | ||
112 | /* ping it every imx2_wdt.timeout / 2 seconds to prevent reboot */ | ||
113 | imx2_wdt_ping(); | ||
114 | mod_timer(&imx2_wdt.timer, jiffies + imx2_wdt.timeout * HZ / 2); | ||
115 | } | ||
116 | |||
117 | static void imx2_wdt_start(void) | ||
118 | { | ||
119 | if (!test_and_set_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) { | ||
120 | /* at our first start we enable clock and do initialisations */ | ||
121 | clk_enable(imx2_wdt.clk); | ||
122 | |||
123 | imx2_wdt_setup(); | ||
124 | } else /* delete the timer that pings the watchdog after close */ | ||
125 | del_timer_sync(&imx2_wdt.timer); | ||
126 | |||
127 | /* Watchdog is enabled - time to reload the timeout value */ | ||
128 | imx2_wdt_ping(); | ||
129 | } | ||
130 | |||
131 | static void imx2_wdt_stop(void) | ||
132 | { | ||
133 | /* we don't need a clk_disable, it cannot be disabled once started. | ||
134 | * We use a timer to ping the watchdog while /dev/watchdog is closed */ | ||
135 | imx2_wdt_timer_ping(0); | ||
136 | } | ||
137 | |||
138 | static void imx2_wdt_set_timeout(int new_timeout) | ||
139 | { | ||
140 | u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); | ||
141 | |||
142 | /* set the new timeout value in the WSR */ | ||
143 | val &= ~IMX2_WDT_WCR_WT; | ||
144 | val |= WDOG_SEC_TO_COUNT(new_timeout); | ||
145 | __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); | ||
146 | } | ||
147 | |||
148 | static int imx2_wdt_open(struct inode *inode, struct file *file) | ||
149 | { | ||
150 | if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status)) | ||
151 | return -EBUSY; | ||
152 | |||
153 | imx2_wdt_start(); | ||
154 | return nonseekable_open(inode, file); | ||
155 | } | ||
156 | |||
157 | static int imx2_wdt_close(struct inode *inode, struct file *file) | ||
158 | { | ||
159 | if (test_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status) && !nowayout) | ||
160 | imx2_wdt_stop(); | ||
161 | else { | ||
162 | dev_crit(imx2_wdt_miscdev.parent, | ||
163 | "Unexpected close: Expect reboot!\n"); | ||
164 | imx2_wdt_ping(); | ||
165 | } | ||
166 | |||
167 | clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status); | ||
168 | clear_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status); | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static long imx2_wdt_ioctl(struct file *file, unsigned int cmd, | ||
173 | unsigned long arg) | ||
174 | { | ||
175 | void __user *argp = (void __user *)arg; | ||
176 | int __user *p = argp; | ||
177 | int new_value; | ||
178 | |||
179 | switch (cmd) { | ||
180 | case WDIOC_GETSUPPORT: | ||
181 | return copy_to_user(argp, &imx2_wdt_info, | ||
182 | sizeof(struct watchdog_info)) ? -EFAULT : 0; | ||
183 | |||
184 | case WDIOC_GETSTATUS: | ||
185 | case WDIOC_GETBOOTSTATUS: | ||
186 | return put_user(0, p); | ||
187 | |||
188 | case WDIOC_KEEPALIVE: | ||
189 | imx2_wdt_ping(); | ||
190 | return 0; | ||
191 | |||
192 | case WDIOC_SETTIMEOUT: | ||
193 | if (get_user(new_value, p)) | ||
194 | return -EFAULT; | ||
195 | if ((new_value < 1) || (new_value > IMX2_WDT_MAX_TIME)) | ||
196 | return -EINVAL; | ||
197 | imx2_wdt_set_timeout(new_value); | ||
198 | imx2_wdt.timeout = new_value; | ||
199 | imx2_wdt_ping(); | ||
200 | |||
201 | /* Fallthrough to return current value */ | ||
202 | case WDIOC_GETTIMEOUT: | ||
203 | return put_user(imx2_wdt.timeout, p); | ||
204 | |||
205 | default: | ||
206 | return -ENOTTY; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | static ssize_t imx2_wdt_write(struct file *file, const char __user *data, | ||
211 | size_t len, loff_t *ppos) | ||
212 | { | ||
213 | size_t i; | ||
214 | char c; | ||
215 | |||
216 | if (len == 0) /* Can we see this even ? */ | ||
217 | return 0; | ||
218 | |||
219 | clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status); | ||
220 | /* scan to see whether or not we got the magic character */ | ||
221 | for (i = 0; i != len; i++) { | ||
222 | if (get_user(c, data + i)) | ||
223 | return -EFAULT; | ||
224 | if (c == 'V') | ||
225 | set_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status); | ||
226 | } | ||
227 | |||
228 | imx2_wdt_ping(); | ||
229 | return len; | ||
230 | } | ||
231 | |||
232 | static const struct file_operations imx2_wdt_fops = { | ||
233 | .owner = THIS_MODULE, | ||
234 | .llseek = no_llseek, | ||
235 | .unlocked_ioctl = imx2_wdt_ioctl, | ||
236 | .open = imx2_wdt_open, | ||
237 | .release = imx2_wdt_close, | ||
238 | .write = imx2_wdt_write, | ||
239 | }; | ||
240 | |||
241 | static struct miscdevice imx2_wdt_miscdev = { | ||
242 | .minor = WATCHDOG_MINOR, | ||
243 | .name = "watchdog", | ||
244 | .fops = &imx2_wdt_fops, | ||
245 | }; | ||
246 | |||
247 | static int __init imx2_wdt_probe(struct platform_device *pdev) | ||
248 | { | ||
249 | int ret; | ||
250 | int res_size; | ||
251 | struct resource *res; | ||
252 | |||
253 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
254 | if (!res) { | ||
255 | dev_err(&pdev->dev, "can't get device resources\n"); | ||
256 | return -ENODEV; | ||
257 | } | ||
258 | |||
259 | res_size = resource_size(res); | ||
260 | if (!devm_request_mem_region(&pdev->dev, res->start, res_size, | ||
261 | res->name)) { | ||
262 | dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", | ||
263 | res_size, res->start); | ||
264 | return -ENOMEM; | ||
265 | } | ||
266 | |||
267 | imx2_wdt.base = devm_ioremap_nocache(&pdev->dev, res->start, res_size); | ||
268 | if (!imx2_wdt.base) { | ||
269 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
270 | return -ENOMEM; | ||
271 | } | ||
272 | |||
273 | imx2_wdt.clk = clk_get(&pdev->dev, NULL); | ||
274 | if (IS_ERR(imx2_wdt.clk)) { | ||
275 | dev_err(&pdev->dev, "can't get Watchdog clock\n"); | ||
276 | return PTR_ERR(imx2_wdt.clk); | ||
277 | } | ||
278 | |||
279 | imx2_wdt.timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME); | ||
280 | if (imx2_wdt.timeout != timeout) | ||
281 | dev_warn(&pdev->dev, "Initial timeout out of range! " | ||
282 | "Clamped from %u to %u\n", timeout, imx2_wdt.timeout); | ||
283 | |||
284 | setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0); | ||
285 | |||
286 | imx2_wdt_miscdev.parent = &pdev->dev; | ||
287 | ret = misc_register(&imx2_wdt_miscdev); | ||
288 | if (ret) | ||
289 | goto fail; | ||
290 | |||
291 | dev_info(&pdev->dev, | ||
292 | "IMX2+ Watchdog Timer enabled. timeout=%ds (nowayout=%d)\n", | ||
293 | imx2_wdt.timeout, nowayout); | ||
294 | return 0; | ||
295 | |||
296 | fail: | ||
297 | imx2_wdt_miscdev.parent = NULL; | ||
298 | clk_put(imx2_wdt.clk); | ||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | static int __exit imx2_wdt_remove(struct platform_device *pdev) | ||
303 | { | ||
304 | misc_deregister(&imx2_wdt_miscdev); | ||
305 | |||
306 | if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) { | ||
307 | del_timer_sync(&imx2_wdt.timer); | ||
308 | |||
309 | dev_crit(imx2_wdt_miscdev.parent, | ||
310 | "Device removed: Expect reboot!\n"); | ||
311 | } else | ||
312 | clk_put(imx2_wdt.clk); | ||
313 | |||
314 | imx2_wdt_miscdev.parent = NULL; | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static void imx2_wdt_shutdown(struct platform_device *pdev) | ||
319 | { | ||
320 | if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) { | ||
321 | /* we are running, we need to delete the timer but will give | ||
322 | * max timeout before reboot will take place */ | ||
323 | del_timer_sync(&imx2_wdt.timer); | ||
324 | imx2_wdt_set_timeout(IMX2_WDT_MAX_TIME); | ||
325 | imx2_wdt_ping(); | ||
326 | |||
327 | dev_crit(imx2_wdt_miscdev.parent, | ||
328 | "Device shutdown: Expect reboot!\n"); | ||
329 | } | ||
330 | } | ||
331 | |||
332 | static struct platform_driver imx2_wdt_driver = { | ||
333 | .remove = __exit_p(imx2_wdt_remove), | ||
334 | .shutdown = imx2_wdt_shutdown, | ||
335 | .driver = { | ||
336 | .name = DRIVER_NAME, | ||
337 | .owner = THIS_MODULE, | ||
338 | }, | ||
339 | }; | ||
340 | |||
341 | static int __init imx2_wdt_init(void) | ||
342 | { | ||
343 | return platform_driver_probe(&imx2_wdt_driver, imx2_wdt_probe); | ||
344 | } | ||
345 | module_init(imx2_wdt_init); | ||
346 | |||
347 | static void __exit imx2_wdt_exit(void) | ||
348 | { | ||
349 | platform_driver_unregister(&imx2_wdt_driver); | ||
350 | } | ||
351 | module_exit(imx2_wdt_exit); | ||
352 | |||
353 | MODULE_AUTHOR("Wolfram Sang"); | ||
354 | MODULE_DESCRIPTION("Watchdog driver for IMX2 and later"); | ||
355 | MODULE_LICENSE("GPL v2"); | ||
356 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
357 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||
diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index f52c162b1bea..b32c6c045b1a 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c | |||
@@ -75,15 +75,23 @@ static unsigned short address; | |||
75 | #define WDT_CONFIG 0x72 /* WDT Register: Configuration */ | 75 | #define WDT_CONFIG 0x72 /* WDT Register: Configuration */ |
76 | #define WDT_TIMEOUT 0x73 /* WDT Register: Timeout Value */ | 76 | #define WDT_TIMEOUT 0x73 /* WDT Register: Timeout Value */ |
77 | 77 | ||
78 | #define WDT_RESET_GAME 0x10 | 78 | #define WDT_RESET_GAME 0x10 /* Reset timer on read or write to game port */ |
79 | #define WDT_RESET_KBD 0x20 | 79 | #define WDT_RESET_KBD 0x20 /* Reset timer on keyboard interrupt */ |
80 | #define WDT_RESET_MOUSE 0x40 | 80 | #define WDT_RESET_MOUSE 0x40 /* Reset timer on mouse interrupt */ |
81 | #define WDT_RESET_CIR 0x80 | 81 | #define WDT_RESET_CIR 0x80 /* Reset timer on consumer IR interrupt */ |
82 | 82 | ||
83 | #define WDT_UNIT_SEC 0x80 /* If 0 in MINUTES */ | 83 | #define WDT_UNIT_SEC 0x80 /* If 0 in MINUTES */ |
84 | 84 | ||
85 | #define WDT_OUT_PWROK 0x10 | 85 | #define WDT_OUT_PWROK 0x10 /* Pulse PWROK on timeout */ |
86 | #define WDT_OUT_KRST 0x40 | 86 | #define WDT_OUT_KRST 0x40 /* Pulse reset on timeout */ |
87 | |||
88 | static int wdt_control_reg = WDT_RESET_GAME; | ||
89 | module_param(wdt_control_reg, int, 0); | ||
90 | MODULE_PARM_DESC(wdt_control_reg, "Value to write to watchdog control " | ||
91 | "register. The default WDT_RESET_GAME resets the timer on " | ||
92 | "game port reads that this driver generates. You can also " | ||
93 | "use KBD, MOUSE or CIR if you have some external way to " | ||
94 | "generate those interrupts."); | ||
87 | 95 | ||
88 | static int superio_inb(int reg) | 96 | static int superio_inb(int reg) |
89 | { | 97 | { |
@@ -131,7 +139,8 @@ static inline void superio_exit(void) | |||
131 | 139 | ||
132 | static inline void it8712f_wdt_ping(void) | 140 | static inline void it8712f_wdt_ping(void) |
133 | { | 141 | { |
134 | inb(address); | 142 | if (wdt_control_reg & WDT_RESET_GAME) |
143 | inb(address); | ||
135 | } | 144 | } |
136 | 145 | ||
137 | static void it8712f_wdt_update_margin(void) | 146 | static void it8712f_wdt_update_margin(void) |
@@ -170,7 +179,7 @@ static void it8712f_wdt_enable(void) | |||
170 | superio_enter(); | 179 | superio_enter(); |
171 | superio_select(LDN_GPIO); | 180 | superio_select(LDN_GPIO); |
172 | 181 | ||
173 | superio_outb(WDT_RESET_GAME, WDT_CONTROL); | 182 | superio_outb(wdt_control_reg, WDT_CONTROL); |
174 | 183 | ||
175 | it8712f_wdt_update_margin(); | 184 | it8712f_wdt_update_margin(); |
176 | 185 | ||
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index b709b3b2d1ef..dad29245a6a7 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c | |||
@@ -12,7 +12,7 @@ | |||
12 | * http://www.ite.com.tw/ | 12 | * http://www.ite.com.tw/ |
13 | * | 13 | * |
14 | * Support of the watchdog timers, which are available on | 14 | * Support of the watchdog timers, which are available on |
15 | * IT8716, IT8718, IT8726 and IT8712 (J,K version). | 15 | * IT8702, IT8712, IT8716, IT8718, IT8720 and IT8726. |
16 | * | 16 | * |
17 | * This program is free software; you can redistribute it and/or | 17 | * This program is free software; you can redistribute it and/or |
18 | * modify it under the terms of the GNU General Public License | 18 | * modify it under the terms of the GNU General Public License |
@@ -45,7 +45,7 @@ | |||
45 | 45 | ||
46 | #include <asm/system.h> | 46 | #include <asm/system.h> |
47 | 47 | ||
48 | #define WATCHDOG_VERSION "1.12" | 48 | #define WATCHDOG_VERSION "1.13" |
49 | #define WATCHDOG_NAME "IT87 WDT" | 49 | #define WATCHDOG_NAME "IT87 WDT" |
50 | #define PFX WATCHDOG_NAME ": " | 50 | #define PFX WATCHDOG_NAME ": " |
51 | #define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" | 51 | #define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" |
@@ -76,10 +76,12 @@ | |||
76 | 76 | ||
77 | /* Chip Id numbers */ | 77 | /* Chip Id numbers */ |
78 | #define NO_DEV_ID 0xffff | 78 | #define NO_DEV_ID 0xffff |
79 | #define IT8702_ID 0x8702 | ||
79 | #define IT8705_ID 0x8705 | 80 | #define IT8705_ID 0x8705 |
80 | #define IT8712_ID 0x8712 | 81 | #define IT8712_ID 0x8712 |
81 | #define IT8716_ID 0x8716 | 82 | #define IT8716_ID 0x8716 |
82 | #define IT8718_ID 0x8718 | 83 | #define IT8718_ID 0x8718 |
84 | #define IT8720_ID 0x8720 | ||
83 | #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ | 85 | #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ |
84 | 86 | ||
85 | /* GPIO Configuration Registers LDN=0x07 */ | 87 | /* GPIO Configuration Registers LDN=0x07 */ |
@@ -92,7 +94,7 @@ | |||
92 | #define WDT_CIRINT 0x80 | 94 | #define WDT_CIRINT 0x80 |
93 | #define WDT_MOUSEINT 0x40 | 95 | #define WDT_MOUSEINT 0x40 |
94 | #define WDT_KYBINT 0x20 | 96 | #define WDT_KYBINT 0x20 |
95 | #define WDT_GAMEPORT 0x10 /* not it8718 */ | 97 | #define WDT_GAMEPORT 0x10 /* not in it8718, it8720 */ |
96 | #define WDT_FORCE 0x02 | 98 | #define WDT_FORCE 0x02 |
97 | #define WDT_ZERO 0x01 | 99 | #define WDT_ZERO 0x01 |
98 | 100 | ||
@@ -132,7 +134,7 @@ | |||
132 | #define WDTS_USE_GP 4 | 134 | #define WDTS_USE_GP 4 |
133 | #define WDTS_EXPECTED 5 | 135 | #define WDTS_EXPECTED 5 |
134 | 136 | ||
135 | static unsigned int base, gpact, ciract; | 137 | static unsigned int base, gpact, ciract, max_units; |
136 | static unsigned long wdt_status; | 138 | static unsigned long wdt_status; |
137 | static DEFINE_SPINLOCK(spinlock); | 139 | static DEFINE_SPINLOCK(spinlock); |
138 | 140 | ||
@@ -210,6 +212,33 @@ static inline void superio_outw(int val, int reg) | |||
210 | outb(val, VAL); | 212 | outb(val, VAL); |
211 | } | 213 | } |
212 | 214 | ||
215 | /* Internal function, should be called after superio_select(GPIO) */ | ||
216 | static void wdt_update_timeout(void) | ||
217 | { | ||
218 | unsigned char cfg = WDT_KRST | WDT_PWROK; | ||
219 | int tm = timeout; | ||
220 | |||
221 | if (testmode) | ||
222 | cfg = 0; | ||
223 | |||
224 | if (tm <= max_units) | ||
225 | cfg |= WDT_TOV1; | ||
226 | else | ||
227 | tm /= 60; | ||
228 | |||
229 | superio_outb(cfg, WDTCFG); | ||
230 | superio_outb(tm, WDTVALLSB); | ||
231 | if (max_units > 255) | ||
232 | superio_outb(tm>>8, WDTVALMSB); | ||
233 | } | ||
234 | |||
235 | static int wdt_round_time(int t) | ||
236 | { | ||
237 | t += 59; | ||
238 | t -= t % 60; | ||
239 | return t; | ||
240 | } | ||
241 | |||
213 | /* watchdog timer handling */ | 242 | /* watchdog timer handling */ |
214 | 243 | ||
215 | static void wdt_keepalive(void) | 244 | static void wdt_keepalive(void) |
@@ -234,12 +263,7 @@ static void wdt_start(void) | |||
234 | superio_outb(WDT_GAMEPORT, WDTCTRL); | 263 | superio_outb(WDT_GAMEPORT, WDTCTRL); |
235 | else | 264 | else |
236 | superio_outb(WDT_CIRINT, WDTCTRL); | 265 | superio_outb(WDT_CIRINT, WDTCTRL); |
237 | if (!testmode) | 266 | wdt_update_timeout(); |
238 | superio_outb(WDT_TOV1 | WDT_KRST | WDT_PWROK, WDTCFG); | ||
239 | else | ||
240 | superio_outb(WDT_TOV1, WDTCFG); | ||
241 | superio_outb(timeout>>8, WDTVALMSB); | ||
242 | superio_outb(timeout, WDTVALLSB); | ||
243 | 267 | ||
244 | superio_exit(); | 268 | superio_exit(); |
245 | spin_unlock_irqrestore(&spinlock, flags); | 269 | spin_unlock_irqrestore(&spinlock, flags); |
@@ -255,8 +279,9 @@ static void wdt_stop(void) | |||
255 | superio_select(GPIO); | 279 | superio_select(GPIO); |
256 | superio_outb(0x00, WDTCTRL); | 280 | superio_outb(0x00, WDTCTRL); |
257 | superio_outb(WDT_TOV1, WDTCFG); | 281 | superio_outb(WDT_TOV1, WDTCFG); |
258 | superio_outb(0x00, WDTVALMSB); | ||
259 | superio_outb(0x00, WDTVALLSB); | 282 | superio_outb(0x00, WDTVALLSB); |
283 | if (max_units > 255) | ||
284 | superio_outb(0x00, WDTVALMSB); | ||
260 | 285 | ||
261 | superio_exit(); | 286 | superio_exit(); |
262 | spin_unlock_irqrestore(&spinlock, flags); | 287 | spin_unlock_irqrestore(&spinlock, flags); |
@@ -266,8 +291,8 @@ static void wdt_stop(void) | |||
266 | * wdt_set_timeout - set a new timeout value with watchdog ioctl | 291 | * wdt_set_timeout - set a new timeout value with watchdog ioctl |
267 | * @t: timeout value in seconds | 292 | * @t: timeout value in seconds |
268 | * | 293 | * |
269 | * The hardware device has a 16 bit watchdog timer, thus the | 294 | * The hardware device has a 8 or 16 bit watchdog timer (depends on |
270 | * timeout time ranges between 1 and 65535 seconds. | 295 | * chip version) that can be configured to count seconds or minutes. |
271 | * | 296 | * |
272 | * Used within WDIOC_SETTIMEOUT watchdog device ioctl. | 297 | * Used within WDIOC_SETTIMEOUT watchdog device ioctl. |
273 | */ | 298 | */ |
@@ -276,19 +301,19 @@ static int wdt_set_timeout(int t) | |||
276 | { | 301 | { |
277 | unsigned long flags; | 302 | unsigned long flags; |
278 | 303 | ||
279 | if (t < 1 || t > 65535) | 304 | if (t < 1 || t > max_units * 60) |
280 | return -EINVAL; | 305 | return -EINVAL; |
281 | 306 | ||
282 | timeout = t; | 307 | if (t > max_units) |
308 | timeout = wdt_round_time(t); | ||
309 | else | ||
310 | timeout = t; | ||
283 | 311 | ||
284 | spin_lock_irqsave(&spinlock, flags); | 312 | spin_lock_irqsave(&spinlock, flags); |
285 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { | 313 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { |
286 | superio_enter(); | 314 | superio_enter(); |
287 | |||
288 | superio_select(GPIO); | 315 | superio_select(GPIO); |
289 | superio_outb(t>>8, WDTVALMSB); | 316 | wdt_update_timeout(); |
290 | superio_outb(t, WDTVALLSB); | ||
291 | |||
292 | superio_exit(); | 317 | superio_exit(); |
293 | } | 318 | } |
294 | spin_unlock_irqrestore(&spinlock, flags); | 319 | spin_unlock_irqrestore(&spinlock, flags); |
@@ -529,10 +554,13 @@ static struct notifier_block wdt_notifier = { | |||
529 | static int __init it87_wdt_init(void) | 554 | static int __init it87_wdt_init(void) |
530 | { | 555 | { |
531 | int rc = 0; | 556 | int rc = 0; |
557 | int try_gameport = !nogameport; | ||
532 | u16 chip_type; | 558 | u16 chip_type; |
533 | u8 chip_rev; | 559 | u8 chip_rev; |
534 | unsigned long flags; | 560 | unsigned long flags; |
535 | 561 | ||
562 | wdt_status = 0; | ||
563 | |||
536 | spin_lock_irqsave(&spinlock, flags); | 564 | spin_lock_irqsave(&spinlock, flags); |
537 | superio_enter(); | 565 | superio_enter(); |
538 | chip_type = superio_inw(CHIPID); | 566 | chip_type = superio_inw(CHIPID); |
@@ -541,13 +569,21 @@ static int __init it87_wdt_init(void) | |||
541 | spin_unlock_irqrestore(&spinlock, flags); | 569 | spin_unlock_irqrestore(&spinlock, flags); |
542 | 570 | ||
543 | switch (chip_type) { | 571 | switch (chip_type) { |
572 | case IT8702_ID: | ||
573 | max_units = 255; | ||
574 | break; | ||
575 | case IT8712_ID: | ||
576 | max_units = (chip_rev < 8) ? 255 : 65535; | ||
577 | break; | ||
544 | case IT8716_ID: | 578 | case IT8716_ID: |
545 | case IT8718_ID: | ||
546 | case IT8726_ID: | 579 | case IT8726_ID: |
580 | max_units = 65535; | ||
581 | break; | ||
582 | case IT8718_ID: | ||
583 | case IT8720_ID: | ||
584 | max_units = 65535; | ||
585 | try_gameport = 0; | ||
547 | break; | 586 | break; |
548 | case IT8712_ID: | ||
549 | if (chip_rev > 7) | ||
550 | break; | ||
551 | case IT8705_ID: | 587 | case IT8705_ID: |
552 | printk(KERN_ERR PFX | 588 | printk(KERN_ERR PFX |
553 | "Unsupported Chip found, Chip %04x Revision %02x\n", | 589 | "Unsupported Chip found, Chip %04x Revision %02x\n", |
@@ -571,7 +607,7 @@ static int __init it87_wdt_init(void) | |||
571 | superio_outb(0x00, WDTCTRL); | 607 | superio_outb(0x00, WDTCTRL); |
572 | 608 | ||
573 | /* First try to get Gameport support */ | 609 | /* First try to get Gameport support */ |
574 | if (chip_type != IT8718_ID && !nogameport) { | 610 | if (try_gameport) { |
575 | superio_select(GAMEPORT); | 611 | superio_select(GAMEPORT); |
576 | base = superio_inw(BASEREG); | 612 | base = superio_inw(BASEREG); |
577 | if (!base) { | 613 | if (!base) { |
@@ -623,13 +659,16 @@ static int __init it87_wdt_init(void) | |||
623 | spin_unlock_irqrestore(&spinlock, flags); | 659 | spin_unlock_irqrestore(&spinlock, flags); |
624 | } | 660 | } |
625 | 661 | ||
626 | if (timeout < 1 || timeout > 65535) { | 662 | if (timeout < 1 || timeout > max_units * 60) { |
627 | timeout = DEFAULT_TIMEOUT; | 663 | timeout = DEFAULT_TIMEOUT; |
628 | printk(KERN_WARNING PFX | 664 | printk(KERN_WARNING PFX |
629 | "Timeout value out of range, use default %d sec\n", | 665 | "Timeout value out of range, use default %d sec\n", |
630 | DEFAULT_TIMEOUT); | 666 | DEFAULT_TIMEOUT); |
631 | } | 667 | } |
632 | 668 | ||
669 | if (timeout > max_units) | ||
670 | timeout = wdt_round_time(timeout); | ||
671 | |||
633 | rc = register_reboot_notifier(&wdt_notifier); | 672 | rc = register_reboot_notifier(&wdt_notifier); |
634 | if (rc) { | 673 | if (rc) { |
635 | printk(KERN_ERR PFX | 674 | printk(KERN_ERR PFX |
@@ -656,7 +695,7 @@ static int __init it87_wdt_init(void) | |||
656 | outb(0x09, CIR_IER(base)); | 695 | outb(0x09, CIR_IER(base)); |
657 | } | 696 | } |
658 | 697 | ||
659 | printk(KERN_INFO PFX "Chip it%04x revision %d initialized. " | 698 | printk(KERN_INFO PFX "Chip IT%04x revision %d initialized. " |
660 | "timeout=%d sec (nowayout=%d testmode=%d exclusive=%d " | 699 | "timeout=%d sec (nowayout=%d testmode=%d exclusive=%d " |
661 | "nogameport=%d)\n", chip_type, chip_rev, timeout, | 700 | "nogameport=%d)\n", chip_type, chip_rev, timeout, |
662 | nowayout, testmode, exclusive, nogameport); | 701 | nowayout, testmode, exclusive, nogameport); |
@@ -676,7 +715,7 @@ err_out_region: | |||
676 | spin_unlock_irqrestore(&spinlock, flags); | 715 | spin_unlock_irqrestore(&spinlock, flags); |
677 | } | 716 | } |
678 | err_out: | 717 | err_out: |
679 | if (chip_type != IT8718_ID && !nogameport) { | 718 | if (try_gameport) { |
680 | spin_lock_irqsave(&spinlock, flags); | 719 | spin_lock_irqsave(&spinlock, flags); |
681 | superio_enter(); | 720 | superio_enter(); |
682 | superio_select(GAMEPORT); | 721 | superio_select(GAMEPORT); |
@@ -698,8 +737,9 @@ static void __exit it87_wdt_exit(void) | |||
698 | superio_select(GPIO); | 737 | superio_select(GPIO); |
699 | superio_outb(0x00, WDTCTRL); | 738 | superio_outb(0x00, WDTCTRL); |
700 | superio_outb(0x00, WDTCFG); | 739 | superio_outb(0x00, WDTCFG); |
701 | superio_outb(0x00, WDTVALMSB); | ||
702 | superio_outb(0x00, WDTVALLSB); | 740 | superio_outb(0x00, WDTVALLSB); |
741 | if (max_units > 255) | ||
742 | superio_outb(0x00, WDTVALMSB); | ||
703 | if (test_bit(WDTS_USE_GP, &wdt_status)) { | 743 | if (test_bit(WDTS_USE_GP, &wdt_status)) { |
704 | superio_select(GAMEPORT); | 744 | superio_select(GAMEPORT); |
705 | superio_outb(gpact, ACTREG); | 745 | superio_outb(gpact, ACTREG); |
diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c index 2852bb2e3fd9..811471903e8a 100644 --- a/drivers/watchdog/ks8695_wdt.c +++ b/drivers/watchdog/ks8695_wdt.c | |||
@@ -21,7 +21,7 @@ | |||
21 | #include <linux/watchdog.h> | 21 | #include <linux/watchdog.h> |
22 | #include <linux/io.h> | 22 | #include <linux/io.h> |
23 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
24 | #include <mach/timex.h> | 24 | #include <mach/hardware.h> |
25 | #include <mach/regs-timer.h> | 25 | #include <mach/regs-timer.h> |
26 | 26 | ||
27 | #define WDT_DEFAULT_TIME 5 /* seconds */ | 27 | #define WDT_DEFAULT_TIME 5 /* seconds */ |
diff --git a/drivers/watchdog/m548x_wdt.c b/drivers/watchdog/m548x_wdt.c new file mode 100644 index 000000000000..cabbcfe1c847 --- /dev/null +++ b/drivers/watchdog/m548x_wdt.c | |||
@@ -0,0 +1,227 @@ | |||
1 | /* | ||
2 | * drivers/watchdog/m548x_wdt.c | ||
3 | * | ||
4 | * Watchdog driver for ColdFire MCF548x processors | ||
5 | * Copyright 2010 (c) Philippe De Muyter <phdm@macqel.be> | ||
6 | * | ||
7 | * Adapted from the IXP4xx watchdog driver, which carries these notices: | ||
8 | * | ||
9 | * Author: Deepak Saxena <dsaxena@plexity.net> | ||
10 | * | ||
11 | * Copyright 2004 (c) MontaVista, Software, Inc. | ||
12 | * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu> | ||
13 | * | ||
14 | * This file is licensed under the terms of the GNU General Public | ||
15 | * License version 2. This program is licensed "as is" without any | ||
16 | * warranty of any kind, whether express or implied. | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/miscdevice.h> | ||
25 | #include <linux/watchdog.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/bitops.h> | ||
28 | #include <linux/ioport.h> | ||
29 | #include <linux/uaccess.h> | ||
30 | |||
31 | #include <asm/coldfire.h> | ||
32 | #include <asm/m548xsim.h> | ||
33 | #include <asm/m548xgpt.h> | ||
34 | |||
35 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
36 | static unsigned int heartbeat = 30; /* (secs) Default is 0.5 minute */ | ||
37 | static unsigned long wdt_status; | ||
38 | |||
39 | #define WDT_IN_USE 0 | ||
40 | #define WDT_OK_TO_CLOSE 1 | ||
41 | |||
42 | static void wdt_enable(void) | ||
43 | { | ||
44 | unsigned int gms0; | ||
45 | |||
46 | /* preserve GPIO usage, if any */ | ||
47 | gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0); | ||
48 | if (gms0 & MCF_GPT_GMS_TMS_GPIO) | ||
49 | gms0 &= (MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_GPIO_MASK | ||
50 | | MCF_GPT_GMS_OD); | ||
51 | else | ||
52 | gms0 = MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_OD; | ||
53 | __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); | ||
54 | __raw_writel(MCF_GPT_GCIR_PRE(heartbeat*(MCF_BUSCLK/0xffff)) | | ||
55 | MCF_GPT_GCIR_CNT(0xffff), MCF_MBAR + MCF_GPT_GCIR0); | ||
56 | gms0 |= MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE; | ||
57 | __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); | ||
58 | } | ||
59 | |||
60 | static void wdt_disable(void) | ||
61 | { | ||
62 | unsigned int gms0; | ||
63 | |||
64 | /* disable watchdog */ | ||
65 | gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0); | ||
66 | gms0 &= ~(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE); | ||
67 | __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); | ||
68 | } | ||
69 | |||
70 | static void wdt_keepalive(void) | ||
71 | { | ||
72 | unsigned int gms0; | ||
73 | |||
74 | gms0 = __raw_readl(MCF_MBAR + MCF_GPT_GMS0); | ||
75 | gms0 |= MCF_GPT_GMS_OCPW(0xA5); | ||
76 | __raw_writel(gms0, MCF_MBAR + MCF_GPT_GMS0); | ||
77 | } | ||
78 | |||
79 | static int m548x_wdt_open(struct inode *inode, struct file *file) | ||
80 | { | ||
81 | if (test_and_set_bit(WDT_IN_USE, &wdt_status)) | ||
82 | return -EBUSY; | ||
83 | |||
84 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
85 | wdt_enable(); | ||
86 | return nonseekable_open(inode, file); | ||
87 | } | ||
88 | |||
89 | static ssize_t m548x_wdt_write(struct file *file, const char *data, | ||
90 | size_t len, loff_t *ppos) | ||
91 | { | ||
92 | if (len) { | ||
93 | if (!nowayout) { | ||
94 | size_t i; | ||
95 | |||
96 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
97 | |||
98 | for (i = 0; i != len; i++) { | ||
99 | char c; | ||
100 | |||
101 | if (get_user(c, data + i)) | ||
102 | return -EFAULT; | ||
103 | if (c == 'V') | ||
104 | set_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
105 | } | ||
106 | } | ||
107 | wdt_keepalive(); | ||
108 | } | ||
109 | return len; | ||
110 | } | ||
111 | |||
112 | static const struct watchdog_info ident = { | ||
113 | .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | | ||
114 | WDIOF_KEEPALIVEPING, | ||
115 | .identity = "Coldfire M548x Watchdog", | ||
116 | }; | ||
117 | |||
118 | static long m548x_wdt_ioctl(struct file *file, unsigned int cmd, | ||
119 | unsigned long arg) | ||
120 | { | ||
121 | int ret = -ENOTTY; | ||
122 | int time; | ||
123 | |||
124 | switch (cmd) { | ||
125 | case WDIOC_GETSUPPORT: | ||
126 | ret = copy_to_user((struct watchdog_info *)arg, &ident, | ||
127 | sizeof(ident)) ? -EFAULT : 0; | ||
128 | break; | ||
129 | |||
130 | case WDIOC_GETSTATUS: | ||
131 | ret = put_user(0, (int *)arg); | ||
132 | break; | ||
133 | |||
134 | case WDIOC_GETBOOTSTATUS: | ||
135 | ret = put_user(0, (int *)arg); | ||
136 | break; | ||
137 | |||
138 | case WDIOC_KEEPALIVE: | ||
139 | wdt_keepalive(); | ||
140 | ret = 0; | ||
141 | break; | ||
142 | |||
143 | case WDIOC_SETTIMEOUT: | ||
144 | ret = get_user(time, (int *)arg); | ||
145 | if (ret) | ||
146 | break; | ||
147 | |||
148 | if (time <= 0 || time > 30) { | ||
149 | ret = -EINVAL; | ||
150 | break; | ||
151 | } | ||
152 | |||
153 | heartbeat = time; | ||
154 | wdt_enable(); | ||
155 | /* Fall through */ | ||
156 | |||
157 | case WDIOC_GETTIMEOUT: | ||
158 | ret = put_user(heartbeat, (int *)arg); | ||
159 | break; | ||
160 | } | ||
161 | return ret; | ||
162 | } | ||
163 | |||
164 | static int m548x_wdt_release(struct inode *inode, struct file *file) | ||
165 | { | ||
166 | if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) | ||
167 | wdt_disable(); | ||
168 | else { | ||
169 | printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " | ||
170 | "timer will not stop\n"); | ||
171 | wdt_keepalive(); | ||
172 | } | ||
173 | clear_bit(WDT_IN_USE, &wdt_status); | ||
174 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | |||
180 | static const struct file_operations m548x_wdt_fops = { | ||
181 | .owner = THIS_MODULE, | ||
182 | .llseek = no_llseek, | ||
183 | .write = m548x_wdt_write, | ||
184 | .unlocked_ioctl = m548x_wdt_ioctl, | ||
185 | .open = m548x_wdt_open, | ||
186 | .release = m548x_wdt_release, | ||
187 | }; | ||
188 | |||
189 | static struct miscdevice m548x_wdt_miscdev = { | ||
190 | .minor = WATCHDOG_MINOR, | ||
191 | .name = "watchdog", | ||
192 | .fops = &m548x_wdt_fops, | ||
193 | }; | ||
194 | |||
195 | static int __init m548x_wdt_init(void) | ||
196 | { | ||
197 | if (!request_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4, | ||
198 | "Coldfire M548x Watchdog")) { | ||
199 | printk(KERN_WARNING | ||
200 | "Coldfire M548x Watchdog : I/O region busy\n"); | ||
201 | return -EBUSY; | ||
202 | } | ||
203 | printk(KERN_INFO "ColdFire watchdog driver is loaded.\n"); | ||
204 | |||
205 | return misc_register(&m548x_wdt_miscdev); | ||
206 | } | ||
207 | |||
208 | static void __exit m548x_wdt_exit(void) | ||
209 | { | ||
210 | misc_deregister(&m548x_wdt_miscdev); | ||
211 | release_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4); | ||
212 | } | ||
213 | |||
214 | module_init(m548x_wdt_init); | ||
215 | module_exit(m548x_wdt_exit); | ||
216 | |||
217 | MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>"); | ||
218 | MODULE_DESCRIPTION("Coldfire M548x Watchdog"); | ||
219 | |||
220 | module_param(heartbeat, int, 0); | ||
221 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 30s)"); | ||
222 | |||
223 | module_param(nowayout, int, 0); | ||
224 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); | ||
225 | |||
226 | MODULE_LICENSE("GPL"); | ||
227 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c index 2d118cf022fc..928035069396 100644 --- a/drivers/watchdog/machzwd.c +++ b/drivers/watchdog/machzwd.c | |||
@@ -143,7 +143,7 @@ static unsigned long next_heartbeat; | |||
143 | #ifndef ZF_DEBUG | 143 | #ifndef ZF_DEBUG |
144 | # define dprintk(format, args...) | 144 | # define dprintk(format, args...) |
145 | #else | 145 | #else |
146 | # define dprintk(format, args...) printk(KERN_DEBUG PFX | 146 | # define dprintk(format, args...) printk(KERN_DEBUG PFX \ |
147 | ":%s:%d: " format, __func__, __LINE__ , ## args) | 147 | ":%s:%d: " format, __func__, __LINE__ , ## args) |
148 | #endif | 148 | #endif |
149 | 149 | ||
@@ -388,7 +388,7 @@ static struct notifier_block zf_notifier = { | |||
388 | 388 | ||
389 | static void __init zf_show_action(int act) | 389 | static void __init zf_show_action(int act) |
390 | { | 390 | { |
391 | char *str[] = { "RESET", "SMI", "NMI", "SCI" }; | 391 | static const char * const str[] = { "RESET", "SMI", "NMI", "SCI" }; |
392 | 392 | ||
393 | printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]); | 393 | printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]); |
394 | } | 394 | } |
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index 7b55974191dd..8fa213cdb499 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c | |||
@@ -53,7 +53,7 @@ static int mpc8xxx_wdt_init_late(void); | |||
53 | static u16 timeout = 0xffff; | 53 | static u16 timeout = 0xffff; |
54 | module_param(timeout, ushort, 0); | 54 | module_param(timeout, ushort, 0); |
55 | MODULE_PARM_DESC(timeout, | 55 | MODULE_PARM_DESC(timeout, |
56 | "Watchdog timeout in ticks. (0<timeout<65536, default=65535"); | 56 | "Watchdog timeout in ticks. (0<timeout<65536, default=65535)"); |
57 | 57 | ||
58 | static int reset = 1; | 58 | static int reset = 1; |
59 | module_param(reset, bool, 0); | 59 | module_param(reset, bool, 0); |
@@ -185,11 +185,11 @@ static struct miscdevice mpc8xxx_wdt_miscdev = { | |||
185 | .fops = &mpc8xxx_wdt_fops, | 185 | .fops = &mpc8xxx_wdt_fops, |
186 | }; | 186 | }; |
187 | 187 | ||
188 | static int __devinit mpc8xxx_wdt_probe(struct of_device *ofdev, | 188 | static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev, |
189 | const struct of_device_id *match) | 189 | const struct of_device_id *match) |
190 | { | 190 | { |
191 | int ret; | 191 | int ret; |
192 | struct device_node *np = ofdev->node; | 192 | struct device_node *np = ofdev->dev.of_node; |
193 | struct mpc8xxx_wdt_type *wdt_type = match->data; | 193 | struct mpc8xxx_wdt_type *wdt_type = match->data; |
194 | u32 freq = fsl_get_sys_freq(); | 194 | u32 freq = fsl_get_sys_freq(); |
195 | bool enabled; | 195 | bool enabled; |
@@ -238,7 +238,7 @@ err_unmap: | |||
238 | return ret; | 238 | return ret; |
239 | } | 239 | } |
240 | 240 | ||
241 | static int __devexit mpc8xxx_wdt_remove(struct of_device *ofdev) | 241 | static int __devexit mpc8xxx_wdt_remove(struct platform_device *ofdev) |
242 | { | 242 | { |
243 | mpc8xxx_wdt_pr_warn("watchdog removed"); | 243 | mpc8xxx_wdt_pr_warn("watchdog removed"); |
244 | del_timer_sync(&wdt_timer); | 244 | del_timer_sync(&wdt_timer); |
diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c new file mode 100644 index 000000000000..1a50aa7079bf --- /dev/null +++ b/drivers/watchdog/nv_tco.c | |||
@@ -0,0 +1,512 @@ | |||
1 | /* | ||
2 | * nv_tco 0.01: TCO timer driver for NV chipsets | ||
3 | * | ||
4 | * (c) Copyright 2005 Google Inc., All Rights Reserved. | ||
5 | * | ||
6 | * Based off i8xx_tco.c: | ||
7 | * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights | ||
8 | * Reserved. | ||
9 | * http://www.kernelconcepts.de | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version | ||
14 | * 2 of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * TCO timer driver for NV chipsets | ||
17 | * based on softdog.c by Alan Cox <alan@redhat.com> | ||
18 | */ | ||
19 | |||
20 | /* | ||
21 | * Includes, defines, variables, module parameters, ... | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/moduleparam.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <linux/miscdevice.h> | ||
28 | #include <linux/watchdog.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/fs.h> | ||
31 | #include <linux/pci.h> | ||
32 | #include <linux/ioport.h> | ||
33 | #include <linux/jiffies.h> | ||
34 | #include <linux/platform_device.h> | ||
35 | #include <linux/uaccess.h> | ||
36 | #include <linux/io.h> | ||
37 | |||
38 | #include "nv_tco.h" | ||
39 | |||
40 | /* Module and version information */ | ||
41 | #define TCO_VERSION "0.01" | ||
42 | #define TCO_MODULE_NAME "NV_TCO" | ||
43 | #define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION | ||
44 | #define PFX TCO_MODULE_NAME ": " | ||
45 | |||
46 | /* internal variables */ | ||
47 | static unsigned int tcobase; | ||
48 | static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ | ||
49 | static unsigned long timer_alive; | ||
50 | static char tco_expect_close; | ||
51 | static struct pci_dev *tco_pci; | ||
52 | |||
53 | /* the watchdog platform device */ | ||
54 | static struct platform_device *nv_tco_platform_device; | ||
55 | |||
56 | /* module parameters */ | ||
57 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (2<heartbeat<39) */ | ||
58 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ | ||
59 | module_param(heartbeat, int, 0); | ||
60 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, " | ||
61 | "default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); | ||
62 | |||
63 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
64 | module_param(nowayout, int, 0); | ||
65 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started" | ||
66 | " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
67 | |||
68 | /* | ||
69 | * Some TCO specific functions | ||
70 | */ | ||
71 | static inline unsigned char seconds_to_ticks(int seconds) | ||
72 | { | ||
73 | /* the internal timer is stored as ticks which decrement | ||
74 | * every 0.6 seconds */ | ||
75 | return (seconds * 10) / 6; | ||
76 | } | ||
77 | |||
78 | static void tco_timer_start(void) | ||
79 | { | ||
80 | u32 val; | ||
81 | unsigned long flags; | ||
82 | |||
83 | spin_lock_irqsave(&tco_lock, flags); | ||
84 | val = inl(TCO_CNT(tcobase)); | ||
85 | val &= ~TCO_CNT_TCOHALT; | ||
86 | outl(val, TCO_CNT(tcobase)); | ||
87 | spin_unlock_irqrestore(&tco_lock, flags); | ||
88 | } | ||
89 | |||
90 | static void tco_timer_stop(void) | ||
91 | { | ||
92 | u32 val; | ||
93 | unsigned long flags; | ||
94 | |||
95 | spin_lock_irqsave(&tco_lock, flags); | ||
96 | val = inl(TCO_CNT(tcobase)); | ||
97 | val |= TCO_CNT_TCOHALT; | ||
98 | outl(val, TCO_CNT(tcobase)); | ||
99 | spin_unlock_irqrestore(&tco_lock, flags); | ||
100 | } | ||
101 | |||
102 | static void tco_timer_keepalive(void) | ||
103 | { | ||
104 | unsigned long flags; | ||
105 | |||
106 | spin_lock_irqsave(&tco_lock, flags); | ||
107 | outb(0x01, TCO_RLD(tcobase)); | ||
108 | spin_unlock_irqrestore(&tco_lock, flags); | ||
109 | } | ||
110 | |||
111 | static int tco_timer_set_heartbeat(int t) | ||
112 | { | ||
113 | int ret = 0; | ||
114 | unsigned char tmrval; | ||
115 | unsigned long flags; | ||
116 | u8 val; | ||
117 | |||
118 | /* | ||
119 | * note seconds_to_ticks(t) > t, so if t > 0x3f, so is | ||
120 | * tmrval=seconds_to_ticks(t). Check that the count in seconds isn't | ||
121 | * out of range on it's own (to avoid overflow in tmrval). | ||
122 | */ | ||
123 | if (t < 0 || t > 0x3f) | ||
124 | return -EINVAL; | ||
125 | tmrval = seconds_to_ticks(t); | ||
126 | |||
127 | /* "Values of 0h-3h are ignored and should not be attempted" */ | ||
128 | if (tmrval > 0x3f || tmrval < 0x04) | ||
129 | return -EINVAL; | ||
130 | |||
131 | /* Write new heartbeat to watchdog */ | ||
132 | spin_lock_irqsave(&tco_lock, flags); | ||
133 | val = inb(TCO_TMR(tcobase)); | ||
134 | val &= 0xc0; | ||
135 | val |= tmrval; | ||
136 | outb(val, TCO_TMR(tcobase)); | ||
137 | val = inb(TCO_TMR(tcobase)); | ||
138 | |||
139 | if ((val & 0x3f) != tmrval) | ||
140 | ret = -EINVAL; | ||
141 | spin_unlock_irqrestore(&tco_lock, flags); | ||
142 | |||
143 | if (ret) | ||
144 | return ret; | ||
145 | |||
146 | heartbeat = t; | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * /dev/watchdog handling | ||
152 | */ | ||
153 | |||
154 | static int nv_tco_open(struct inode *inode, struct file *file) | ||
155 | { | ||
156 | /* /dev/watchdog can only be opened once */ | ||
157 | if (test_and_set_bit(0, &timer_alive)) | ||
158 | return -EBUSY; | ||
159 | |||
160 | /* Reload and activate timer */ | ||
161 | tco_timer_keepalive(); | ||
162 | tco_timer_start(); | ||
163 | return nonseekable_open(inode, file); | ||
164 | } | ||
165 | |||
166 | static int nv_tco_release(struct inode *inode, struct file *file) | ||
167 | { | ||
168 | /* Shut off the timer */ | ||
169 | if (tco_expect_close == 42) { | ||
170 | tco_timer_stop(); | ||
171 | } else { | ||
172 | printk(KERN_CRIT PFX "Unexpected close, not stopping " | ||
173 | "watchdog!\n"); | ||
174 | tco_timer_keepalive(); | ||
175 | } | ||
176 | clear_bit(0, &timer_alive); | ||
177 | tco_expect_close = 0; | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static ssize_t nv_tco_write(struct file *file, const char __user *data, | ||
182 | size_t len, loff_t *ppos) | ||
183 | { | ||
184 | /* See if we got the magic character 'V' and reload the timer */ | ||
185 | if (len) { | ||
186 | if (!nowayout) { | ||
187 | size_t i; | ||
188 | |||
189 | /* | ||
190 | * note: just in case someone wrote the magic character | ||
191 | * five months ago... | ||
192 | */ | ||
193 | tco_expect_close = 0; | ||
194 | |||
195 | /* | ||
196 | * scan to see whether or not we got the magic | ||
197 | * character | ||
198 | */ | ||
199 | for (i = 0; i != len; i++) { | ||
200 | char c; | ||
201 | if (get_user(c, data + i)) | ||
202 | return -EFAULT; | ||
203 | if (c == 'V') | ||
204 | tco_expect_close = 42; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | /* someone wrote to us, we should reload the timer */ | ||
209 | tco_timer_keepalive(); | ||
210 | } | ||
211 | return len; | ||
212 | } | ||
213 | |||
214 | static long nv_tco_ioctl(struct file *file, unsigned int cmd, | ||
215 | unsigned long arg) | ||
216 | { | ||
217 | int new_options, retval = -EINVAL; | ||
218 | int new_heartbeat; | ||
219 | void __user *argp = (void __user *)arg; | ||
220 | int __user *p = argp; | ||
221 | static const struct watchdog_info ident = { | ||
222 | .options = WDIOF_SETTIMEOUT | | ||
223 | WDIOF_KEEPALIVEPING | | ||
224 | WDIOF_MAGICCLOSE, | ||
225 | .firmware_version = 0, | ||
226 | .identity = TCO_MODULE_NAME, | ||
227 | }; | ||
228 | |||
229 | switch (cmd) { | ||
230 | case WDIOC_GETSUPPORT: | ||
231 | return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; | ||
232 | case WDIOC_GETSTATUS: | ||
233 | case WDIOC_GETBOOTSTATUS: | ||
234 | return put_user(0, p); | ||
235 | case WDIOC_SETOPTIONS: | ||
236 | if (get_user(new_options, p)) | ||
237 | return -EFAULT; | ||
238 | if (new_options & WDIOS_DISABLECARD) { | ||
239 | tco_timer_stop(); | ||
240 | retval = 0; | ||
241 | } | ||
242 | if (new_options & WDIOS_ENABLECARD) { | ||
243 | tco_timer_keepalive(); | ||
244 | tco_timer_start(); | ||
245 | retval = 0; | ||
246 | } | ||
247 | return retval; | ||
248 | case WDIOC_KEEPALIVE: | ||
249 | tco_timer_keepalive(); | ||
250 | return 0; | ||
251 | case WDIOC_SETTIMEOUT: | ||
252 | if (get_user(new_heartbeat, p)) | ||
253 | return -EFAULT; | ||
254 | if (tco_timer_set_heartbeat(new_heartbeat)) | ||
255 | return -EINVAL; | ||
256 | tco_timer_keepalive(); | ||
257 | /* Fall through */ | ||
258 | case WDIOC_GETTIMEOUT: | ||
259 | return put_user(heartbeat, p); | ||
260 | default: | ||
261 | return -ENOTTY; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Kernel Interfaces | ||
267 | */ | ||
268 | |||
269 | static const struct file_operations nv_tco_fops = { | ||
270 | .owner = THIS_MODULE, | ||
271 | .llseek = no_llseek, | ||
272 | .write = nv_tco_write, | ||
273 | .unlocked_ioctl = nv_tco_ioctl, | ||
274 | .open = nv_tco_open, | ||
275 | .release = nv_tco_release, | ||
276 | }; | ||
277 | |||
278 | static struct miscdevice nv_tco_miscdev = { | ||
279 | .minor = WATCHDOG_MINOR, | ||
280 | .name = "watchdog", | ||
281 | .fops = &nv_tco_fops, | ||
282 | }; | ||
283 | |||
284 | /* | ||
285 | * Data for PCI driver interface | ||
286 | * | ||
287 | * This data only exists for exporting the supported | ||
288 | * PCI ids via MODULE_DEVICE_TABLE. We do not actually | ||
289 | * register a pci_driver, because someone else might one day | ||
290 | * want to register another driver on the same PCI id. | ||
291 | */ | ||
292 | static struct pci_device_id tco_pci_tbl[] = { | ||
293 | { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS, | ||
294 | PCI_ANY_ID, PCI_ANY_ID, }, | ||
295 | { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS, | ||
296 | PCI_ANY_ID, PCI_ANY_ID, }, | ||
297 | { 0, }, /* End of list */ | ||
298 | }; | ||
299 | MODULE_DEVICE_TABLE(pci, tco_pci_tbl); | ||
300 | |||
301 | /* | ||
302 | * Init & exit routines | ||
303 | */ | ||
304 | |||
305 | static unsigned char __init nv_tco_getdevice(void) | ||
306 | { | ||
307 | struct pci_dev *dev = NULL; | ||
308 | u32 val; | ||
309 | |||
310 | /* Find the PCI device */ | ||
311 | for_each_pci_dev(dev) { | ||
312 | if (pci_match_id(tco_pci_tbl, dev) != NULL) { | ||
313 | tco_pci = dev; | ||
314 | break; | ||
315 | } | ||
316 | } | ||
317 | |||
318 | if (!tco_pci) | ||
319 | return 0; | ||
320 | |||
321 | /* Find the base io port */ | ||
322 | pci_read_config_dword(tco_pci, 0x64, &val); | ||
323 | val &= 0xffff; | ||
324 | if (val == 0x0001 || val == 0x0000) { | ||
325 | /* Something is wrong here, bar isn't setup */ | ||
326 | printk(KERN_ERR PFX "failed to get tcobase address\n"); | ||
327 | return 0; | ||
328 | } | ||
329 | val &= 0xff00; | ||
330 | tcobase = val + 0x40; | ||
331 | |||
332 | if (!request_region(tcobase, 0x10, "NV TCO")) { | ||
333 | printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", | ||
334 | tcobase); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | /* Set a reasonable heartbeat before we stop the timer */ | ||
339 | tco_timer_set_heartbeat(30); | ||
340 | |||
341 | /* | ||
342 | * Stop the TCO before we change anything so we don't race with | ||
343 | * a zeroed timer. | ||
344 | */ | ||
345 | tco_timer_keepalive(); | ||
346 | tco_timer_stop(); | ||
347 | |||
348 | /* Disable SMI caused by TCO */ | ||
349 | if (!request_region(MCP51_SMI_EN(tcobase), 4, "NV TCO")) { | ||
350 | printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", | ||
351 | MCP51_SMI_EN(tcobase)); | ||
352 | goto out; | ||
353 | } | ||
354 | val = inl(MCP51_SMI_EN(tcobase)); | ||
355 | val &= ~MCP51_SMI_EN_TCO; | ||
356 | outl(val, MCP51_SMI_EN(tcobase)); | ||
357 | val = inl(MCP51_SMI_EN(tcobase)); | ||
358 | release_region(MCP51_SMI_EN(tcobase), 4); | ||
359 | if (val & MCP51_SMI_EN_TCO) { | ||
360 | printk(KERN_ERR PFX "Could not disable SMI caused by TCO\n"); | ||
361 | goto out; | ||
362 | } | ||
363 | |||
364 | /* Check chipset's NO_REBOOT bit */ | ||
365 | pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val); | ||
366 | val |= MCP51_SMBUS_SETUP_B_TCO_REBOOT; | ||
367 | pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val); | ||
368 | pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val); | ||
369 | if (!(val & MCP51_SMBUS_SETUP_B_TCO_REBOOT)) { | ||
370 | printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot " | ||
371 | "disabled by hardware\n"); | ||
372 | goto out; | ||
373 | } | ||
374 | |||
375 | return 1; | ||
376 | out: | ||
377 | release_region(tcobase, 0x10); | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int __devinit nv_tco_init(struct platform_device *dev) | ||
382 | { | ||
383 | int ret; | ||
384 | |||
385 | /* Check whether or not the hardware watchdog is there */ | ||
386 | if (!nv_tco_getdevice()) | ||
387 | return -ENODEV; | ||
388 | |||
389 | /* Check to see if last reboot was due to watchdog timeout */ | ||
390 | printk(KERN_INFO PFX "Watchdog reboot %sdetected.\n", | ||
391 | inl(TCO_STS(tcobase)) & TCO_STS_TCO2TO_STS ? "" : "not "); | ||
392 | |||
393 | /* Clear out the old status */ | ||
394 | outl(TCO_STS_RESET, TCO_STS(tcobase)); | ||
395 | |||
396 | /* | ||
397 | * Check that the heartbeat value is within it's range. | ||
398 | * If not, reset to the default. | ||
399 | */ | ||
400 | if (tco_timer_set_heartbeat(heartbeat)) { | ||
401 | heartbeat = WATCHDOG_HEARTBEAT; | ||
402 | tco_timer_set_heartbeat(heartbeat); | ||
403 | printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39, " | ||
404 | "using %d\n", heartbeat); | ||
405 | } | ||
406 | |||
407 | ret = misc_register(&nv_tco_miscdev); | ||
408 | if (ret != 0) { | ||
409 | printk(KERN_ERR PFX "cannot register miscdev on minor=%d " | ||
410 | "(err=%d)\n", WATCHDOG_MINOR, ret); | ||
411 | goto unreg_region; | ||
412 | } | ||
413 | |||
414 | clear_bit(0, &timer_alive); | ||
415 | |||
416 | tco_timer_stop(); | ||
417 | |||
418 | printk(KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec " | ||
419 | "(nowayout=%d)\n", tcobase, heartbeat, nowayout); | ||
420 | |||
421 | return 0; | ||
422 | |||
423 | unreg_region: | ||
424 | release_region(tcobase, 0x10); | ||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | static void __devexit nv_tco_cleanup(void) | ||
429 | { | ||
430 | u32 val; | ||
431 | |||
432 | /* Stop the timer before we leave */ | ||
433 | if (!nowayout) | ||
434 | tco_timer_stop(); | ||
435 | |||
436 | /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ | ||
437 | pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val); | ||
438 | val &= ~MCP51_SMBUS_SETUP_B_TCO_REBOOT; | ||
439 | pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val); | ||
440 | pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val); | ||
441 | if (val & MCP51_SMBUS_SETUP_B_TCO_REBOOT) { | ||
442 | printk(KERN_CRIT PFX "Couldn't unset REBOOT bit. Machine may " | ||
443 | "soon reset\n"); | ||
444 | } | ||
445 | |||
446 | /* Deregister */ | ||
447 | misc_deregister(&nv_tco_miscdev); | ||
448 | release_region(tcobase, 0x10); | ||
449 | } | ||
450 | |||
451 | static int __devexit nv_tco_remove(struct platform_device *dev) | ||
452 | { | ||
453 | if (tcobase) | ||
454 | nv_tco_cleanup(); | ||
455 | |||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | static void nv_tco_shutdown(struct platform_device *dev) | ||
460 | { | ||
461 | tco_timer_stop(); | ||
462 | } | ||
463 | |||
464 | static struct platform_driver nv_tco_driver = { | ||
465 | .probe = nv_tco_init, | ||
466 | .remove = __devexit_p(nv_tco_remove), | ||
467 | .shutdown = nv_tco_shutdown, | ||
468 | .driver = { | ||
469 | .owner = THIS_MODULE, | ||
470 | .name = TCO_MODULE_NAME, | ||
471 | }, | ||
472 | }; | ||
473 | |||
474 | static int __init nv_tco_init_module(void) | ||
475 | { | ||
476 | int err; | ||
477 | |||
478 | printk(KERN_INFO PFX "NV TCO WatchDog Timer Driver v%s\n", | ||
479 | TCO_VERSION); | ||
480 | |||
481 | err = platform_driver_register(&nv_tco_driver); | ||
482 | if (err) | ||
483 | return err; | ||
484 | |||
485 | nv_tco_platform_device = platform_device_register_simple( | ||
486 | TCO_MODULE_NAME, -1, NULL, 0); | ||
487 | if (IS_ERR(nv_tco_platform_device)) { | ||
488 | err = PTR_ERR(nv_tco_platform_device); | ||
489 | goto unreg_platform_driver; | ||
490 | } | ||
491 | |||
492 | return 0; | ||
493 | |||
494 | unreg_platform_driver: | ||
495 | platform_driver_unregister(&nv_tco_driver); | ||
496 | return err; | ||
497 | } | ||
498 | |||
499 | static void __exit nv_tco_cleanup_module(void) | ||
500 | { | ||
501 | platform_device_unregister(nv_tco_platform_device); | ||
502 | platform_driver_unregister(&nv_tco_driver); | ||
503 | printk(KERN_INFO PFX "NV TCO Watchdog Module Unloaded.\n"); | ||
504 | } | ||
505 | |||
506 | module_init(nv_tco_init_module); | ||
507 | module_exit(nv_tco_cleanup_module); | ||
508 | |||
509 | MODULE_AUTHOR("Mike Waychison"); | ||
510 | MODULE_DESCRIPTION("TCO timer driver for NV chipsets"); | ||
511 | MODULE_LICENSE("GPL"); | ||
512 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/nv_tco.h b/drivers/watchdog/nv_tco.h new file mode 100644 index 000000000000..c2d1d04e055b --- /dev/null +++ b/drivers/watchdog/nv_tco.h | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * nv_tco: TCO timer driver for nVidia chipsets. | ||
3 | * | ||
4 | * (c) Copyright 2005 Google Inc., All Rights Reserved. | ||
5 | * | ||
6 | * Supported Chipsets: | ||
7 | * - MCP51/MCP55 | ||
8 | * | ||
9 | * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights | ||
10 | * Reserved. | ||
11 | * http://www.kernelconcepts.de | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version | ||
16 | * 2 of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * Neither kernel concepts nor Nils Faerber admit liability nor provide | ||
19 | * warranty for any of this software. This material is provided | ||
20 | * "AS-IS" and at no charge. | ||
21 | * | ||
22 | * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de> | ||
23 | * developed for | ||
24 | * Jentro AG, Haar/Munich (Germany) | ||
25 | * | ||
26 | * TCO timer driver for NV chipsets | ||
27 | * based on softdog.c by Alan Cox <alan@redhat.com> | ||
28 | */ | ||
29 | |||
30 | /* | ||
31 | * Some address definitions for the TCO | ||
32 | */ | ||
33 | |||
34 | #define TCO_RLD(base) ((base) + 0x00) /* TCO Timer Reload and Current Value */ | ||
35 | #define TCO_TMR(base) ((base) + 0x01) /* TCO Timer Initial Value */ | ||
36 | |||
37 | #define TCO_STS(base) ((base) + 0x04) /* TCO Status Register */ | ||
38 | /* | ||
39 | * TCO Boot Status bit: set on TCO reset, reset by software or standby | ||
40 | * power-good (survives reboots), unfortunately this bit is never | ||
41 | * set. | ||
42 | */ | ||
43 | # define TCO_STS_BOOT_STS (1 << 9) | ||
44 | /* | ||
45 | * first and 2nd timeout status bits, these also survive a warm boot, | ||
46 | * and they work, so we use them. | ||
47 | */ | ||
48 | # define TCO_STS_TCO_INT_STS (1 << 1) | ||
49 | # define TCO_STS_TCO2TO_STS (1 << 10) | ||
50 | # define TCO_STS_RESET (TCO_STS_BOOT_STS | TCO_STS_TCO2TO_STS | \ | ||
51 | TCO_STS_TCO_INT_STS) | ||
52 | |||
53 | #define TCO_CNT(base) ((base) + 0x08) /* TCO Control Register */ | ||
54 | # define TCO_CNT_TCOHALT (1 << 12) | ||
55 | |||
56 | #define MCP51_SMBUS_SETUP_B 0xe8 | ||
57 | # define MCP51_SMBUS_SETUP_B_TCO_REBOOT (1 << 25) | ||
58 | |||
59 | /* | ||
60 | * The SMI_EN register is at the base io address + 0x04, | ||
61 | * while TCOBASE is + 0x40. | ||
62 | */ | ||
63 | #define MCP51_SMI_EN(base) ((base) - 0x40 + 0x04) | ||
64 | # define MCP51_SMI_EN_TCO ((1 << 4) | (1 << 5)) | ||
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c new file mode 100644 index 000000000000..945ee8300306 --- /dev/null +++ b/drivers/watchdog/octeon-wdt-main.c | |||
@@ -0,0 +1,746 @@ | |||
1 | /* | ||
2 | * Octeon Watchdog driver | ||
3 | * | ||
4 | * Copyright (C) 2007, 2008, 2009, 2010 Cavium Networks | ||
5 | * | ||
6 | * Some parts derived from wdt.c | ||
7 | * | ||
8 | * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>, | ||
9 | * All Rights Reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version | ||
14 | * 2 of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide | ||
17 | * warranty for any of this software. This material is provided | ||
18 | * "AS-IS" and at no charge. | ||
19 | * | ||
20 | * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> | ||
21 | * | ||
22 | * This file is subject to the terms and conditions of the GNU General Public | ||
23 | * License. See the file "COPYING" in the main directory of this archive | ||
24 | * for more details. | ||
25 | * | ||
26 | * | ||
27 | * The OCTEON watchdog has a maximum timeout of 2^32 * io_clock. | ||
28 | * For most systems this is less than 10 seconds, so to allow for | ||
29 | * software to request longer watchdog heartbeats, we maintain software | ||
30 | * counters to count multiples of the base rate. If the system locks | ||
31 | * up in such a manner that we can not run the software counters, the | ||
32 | * only result is a watchdog reset sooner than was requested. But | ||
33 | * that is OK, because in this case userspace would likely not be able | ||
34 | * to do anything anyhow. | ||
35 | * | ||
36 | * The hardware watchdog interval we call the period. The OCTEON | ||
37 | * watchdog goes through several stages, after the first period an | ||
38 | * irq is asserted, then if it is not reset, after the next period NMI | ||
39 | * is asserted, then after an additional period a chip wide soft reset. | ||
40 | * So for the software counters, we reset watchdog after each period | ||
41 | * and decrement the counter. But for the last two periods we need to | ||
42 | * let the watchdog progress to the NMI stage so we disable the irq | ||
43 | * and let it proceed. Once in the NMI, we print the register state | ||
44 | * to the serial port and then wait for the reset. | ||
45 | * | ||
46 | * A watchdog is maintained for each CPU in the system, that way if | ||
47 | * one CPU suffers a lockup, we also get a register dump and reset. | ||
48 | * The userspace ping resets the watchdog on all CPUs. | ||
49 | * | ||
50 | * Before userspace opens the watchdog device, we still run the | ||
51 | * watchdogs to catch any lockups that may be kernel related. | ||
52 | * | ||
53 | */ | ||
54 | |||
55 | #include <linux/miscdevice.h> | ||
56 | #include <linux/interrupt.h> | ||
57 | #include <linux/watchdog.h> | ||
58 | #include <linux/cpumask.h> | ||
59 | #include <linux/bitops.h> | ||
60 | #include <linux/kernel.h> | ||
61 | #include <linux/module.h> | ||
62 | #include <linux/string.h> | ||
63 | #include <linux/delay.h> | ||
64 | #include <linux/cpu.h> | ||
65 | #include <linux/smp.h> | ||
66 | #include <linux/fs.h> | ||
67 | #include <linux/irq.h> | ||
68 | |||
69 | #include <asm/mipsregs.h> | ||
70 | #include <asm/uasm.h> | ||
71 | |||
72 | #include <asm/octeon/octeon.h> | ||
73 | |||
74 | /* The count needed to achieve timeout_sec. */ | ||
75 | static unsigned int timeout_cnt; | ||
76 | |||
77 | /* The maximum period supported. */ | ||
78 | static unsigned int max_timeout_sec; | ||
79 | |||
80 | /* The current period. */ | ||
81 | static unsigned int timeout_sec; | ||
82 | |||
83 | /* Set to non-zero when userspace countdown mode active */ | ||
84 | static int do_coundown; | ||
85 | static unsigned int countdown_reset; | ||
86 | static unsigned int per_cpu_countdown[NR_CPUS]; | ||
87 | |||
88 | static cpumask_t irq_enabled_cpus; | ||
89 | |||
90 | #define WD_TIMO 60 /* Default heartbeat = 60 seconds */ | ||
91 | |||
92 | static int heartbeat = WD_TIMO; | ||
93 | module_param(heartbeat, int, S_IRUGO); | ||
94 | MODULE_PARM_DESC(heartbeat, | ||
95 | "Watchdog heartbeat in seconds. (0 < heartbeat, default=" | ||
96 | __MODULE_STRING(WD_TIMO) ")"); | ||
97 | |||
98 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
99 | module_param(nowayout, int, S_IRUGO); | ||
100 | MODULE_PARM_DESC(nowayout, | ||
101 | "Watchdog cannot be stopped once started (default=" | ||
102 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
103 | |||
104 | static unsigned long octeon_wdt_is_open; | ||
105 | static char expect_close; | ||
106 | |||
107 | static u32 __initdata nmi_stage1_insns[64]; | ||
108 | /* We need one branch and therefore one relocation per target label. */ | ||
109 | static struct uasm_label __initdata labels[5]; | ||
110 | static struct uasm_reloc __initdata relocs[5]; | ||
111 | |||
112 | enum lable_id { | ||
113 | label_enter_bootloader = 1 | ||
114 | }; | ||
115 | |||
116 | /* Some CP0 registers */ | ||
117 | #define K0 26 | ||
118 | #define C0_CVMMEMCTL 11, 7 | ||
119 | #define C0_STATUS 12, 0 | ||
120 | #define C0_EBASE 15, 1 | ||
121 | #define C0_DESAVE 31, 0 | ||
122 | |||
123 | void octeon_wdt_nmi_stage2(void); | ||
124 | |||
125 | static void __init octeon_wdt_build_stage1(void) | ||
126 | { | ||
127 | int i; | ||
128 | int len; | ||
129 | u32 *p = nmi_stage1_insns; | ||
130 | #ifdef CONFIG_HOTPLUG_CPU | ||
131 | struct uasm_label *l = labels; | ||
132 | struct uasm_reloc *r = relocs; | ||
133 | #endif | ||
134 | |||
135 | /* | ||
136 | * For the next few instructions running the debugger may | ||
137 | * cause corruption of k0 in the saved registers. Since we're | ||
138 | * about to crash, nobody probably cares. | ||
139 | * | ||
140 | * Save K0 into the debug scratch register | ||
141 | */ | ||
142 | uasm_i_dmtc0(&p, K0, C0_DESAVE); | ||
143 | |||
144 | uasm_i_mfc0(&p, K0, C0_STATUS); | ||
145 | #ifdef CONFIG_HOTPLUG_CPU | ||
146 | uasm_il_bbit0(&p, &r, K0, ilog2(ST0_NMI), label_enter_bootloader); | ||
147 | #endif | ||
148 | /* Force 64-bit addressing enabled */ | ||
149 | uasm_i_ori(&p, K0, K0, ST0_UX | ST0_SX | ST0_KX); | ||
150 | uasm_i_mtc0(&p, K0, C0_STATUS); | ||
151 | |||
152 | #ifdef CONFIG_HOTPLUG_CPU | ||
153 | uasm_i_mfc0(&p, K0, C0_EBASE); | ||
154 | /* Coreid number in K0 */ | ||
155 | uasm_i_andi(&p, K0, K0, 0xf); | ||
156 | /* 8 * coreid in bits 16-31 */ | ||
157 | uasm_i_dsll_safe(&p, K0, K0, 3 + 16); | ||
158 | uasm_i_ori(&p, K0, K0, 0x8001); | ||
159 | uasm_i_dsll_safe(&p, K0, K0, 16); | ||
160 | uasm_i_ori(&p, K0, K0, 0x0700); | ||
161 | uasm_i_drotr_safe(&p, K0, K0, 32); | ||
162 | /* | ||
163 | * Should result in: 0x8001,0700,0000,8*coreid which is | ||
164 | * CVMX_CIU_WDOGX(coreid) - 0x0500 | ||
165 | * | ||
166 | * Now ld K0, CVMX_CIU_WDOGX(coreid) | ||
167 | */ | ||
168 | uasm_i_ld(&p, K0, 0x500, K0); | ||
169 | /* | ||
170 | * If bit one set handle the NMI as a watchdog event. | ||
171 | * otherwise transfer control to bootloader. | ||
172 | */ | ||
173 | uasm_il_bbit0(&p, &r, K0, 1, label_enter_bootloader); | ||
174 | uasm_i_nop(&p); | ||
175 | #endif | ||
176 | |||
177 | /* Clear Dcache so cvmseg works right. */ | ||
178 | uasm_i_cache(&p, 1, 0, 0); | ||
179 | |||
180 | /* Use K0 to do a read/modify/write of CVMMEMCTL */ | ||
181 | uasm_i_dmfc0(&p, K0, C0_CVMMEMCTL); | ||
182 | /* Clear out the size of CVMSEG */ | ||
183 | uasm_i_dins(&p, K0, 0, 0, 6); | ||
184 | /* Set CVMSEG to its largest value */ | ||
185 | uasm_i_ori(&p, K0, K0, 0x1c0 | 54); | ||
186 | /* Store the CVMMEMCTL value */ | ||
187 | uasm_i_dmtc0(&p, K0, C0_CVMMEMCTL); | ||
188 | |||
189 | /* Load the address of the second stage handler */ | ||
190 | UASM_i_LA(&p, K0, (long)octeon_wdt_nmi_stage2); | ||
191 | uasm_i_jr(&p, K0); | ||
192 | uasm_i_dmfc0(&p, K0, C0_DESAVE); | ||
193 | |||
194 | #ifdef CONFIG_HOTPLUG_CPU | ||
195 | uasm_build_label(&l, p, label_enter_bootloader); | ||
196 | /* Jump to the bootloader and restore K0 */ | ||
197 | UASM_i_LA(&p, K0, (long)octeon_bootloader_entry_addr); | ||
198 | uasm_i_jr(&p, K0); | ||
199 | uasm_i_dmfc0(&p, K0, C0_DESAVE); | ||
200 | #endif | ||
201 | uasm_resolve_relocs(relocs, labels); | ||
202 | |||
203 | len = (int)(p - nmi_stage1_insns); | ||
204 | pr_debug("Synthesized NMI stage 1 handler (%d instructions).\n", len); | ||
205 | |||
206 | pr_debug("\t.set push\n"); | ||
207 | pr_debug("\t.set noreorder\n"); | ||
208 | for (i = 0; i < len; i++) | ||
209 | pr_debug("\t.word 0x%08x\n", nmi_stage1_insns[i]); | ||
210 | pr_debug("\t.set pop\n"); | ||
211 | |||
212 | if (len > 32) | ||
213 | panic("NMI stage 1 handler exceeds 32 instructions, was %d\n", len); | ||
214 | } | ||
215 | |||
216 | static int cpu2core(int cpu) | ||
217 | { | ||
218 | #ifdef CONFIG_SMP | ||
219 | return cpu_logical_map(cpu); | ||
220 | #else | ||
221 | return cvmx_get_core_num(); | ||
222 | #endif | ||
223 | } | ||
224 | |||
225 | static int core2cpu(int coreid) | ||
226 | { | ||
227 | #ifdef CONFIG_SMP | ||
228 | return cpu_number_map(coreid); | ||
229 | #else | ||
230 | return 0; | ||
231 | #endif | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * Poke the watchdog when an interrupt is received | ||
236 | * | ||
237 | * @cpl: | ||
238 | * @dev_id: | ||
239 | * | ||
240 | * Returns | ||
241 | */ | ||
242 | static irqreturn_t octeon_wdt_poke_irq(int cpl, void *dev_id) | ||
243 | { | ||
244 | unsigned int core = cvmx_get_core_num(); | ||
245 | int cpu = core2cpu(core); | ||
246 | |||
247 | if (do_coundown) { | ||
248 | if (per_cpu_countdown[cpu] > 0) { | ||
249 | /* We're alive, poke the watchdog */ | ||
250 | cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1); | ||
251 | per_cpu_countdown[cpu]--; | ||
252 | } else { | ||
253 | /* Bad news, you are about to reboot. */ | ||
254 | disable_irq_nosync(cpl); | ||
255 | cpumask_clear_cpu(cpu, &irq_enabled_cpus); | ||
256 | } | ||
257 | } else { | ||
258 | /* Not open, just ping away... */ | ||
259 | cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1); | ||
260 | } | ||
261 | return IRQ_HANDLED; | ||
262 | } | ||
263 | |||
264 | /* From setup.c */ | ||
265 | extern int prom_putchar(char c); | ||
266 | |||
267 | /** | ||
268 | * Write a string to the uart | ||
269 | * | ||
270 | * @str: String to write | ||
271 | */ | ||
272 | static void octeon_wdt_write_string(const char *str) | ||
273 | { | ||
274 | /* Just loop writing one byte at a time */ | ||
275 | while (*str) | ||
276 | prom_putchar(*str++); | ||
277 | } | ||
278 | |||
279 | /** | ||
280 | * Write a hex number out of the uart | ||
281 | * | ||
282 | * @value: Number to display | ||
283 | * @digits: Number of digits to print (1 to 16) | ||
284 | */ | ||
285 | static void octeon_wdt_write_hex(u64 value, int digits) | ||
286 | { | ||
287 | int d; | ||
288 | int v; | ||
289 | for (d = 0; d < digits; d++) { | ||
290 | v = (value >> ((digits - d - 1) * 4)) & 0xf; | ||
291 | if (v >= 10) | ||
292 | prom_putchar('a' + v - 10); | ||
293 | else | ||
294 | prom_putchar('0' + v); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | const char *reg_name[] = { | ||
299 | "$0", "at", "v0", "v1", "a0", "a1", "a2", "a3", | ||
300 | "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", | ||
301 | "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", | ||
302 | "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" | ||
303 | }; | ||
304 | |||
305 | /** | ||
306 | * NMI stage 3 handler. NMIs are handled in the following manner: | ||
307 | * 1) The first NMI handler enables CVMSEG and transfers from | ||
308 | * the bootbus region into normal memory. It is careful to not | ||
309 | * destroy any registers. | ||
310 | * 2) The second stage handler uses CVMSEG to save the registers | ||
311 | * and create a stack for C code. It then calls the third level | ||
312 | * handler with one argument, a pointer to the register values. | ||
313 | * 3) The third, and final, level handler is the following C | ||
314 | * function that prints out some useful infomration. | ||
315 | * | ||
316 | * @reg: Pointer to register state before the NMI | ||
317 | */ | ||
318 | void octeon_wdt_nmi_stage3(u64 reg[32]) | ||
319 | { | ||
320 | u64 i; | ||
321 | |||
322 | unsigned int coreid = cvmx_get_core_num(); | ||
323 | /* | ||
324 | * Save status and cause early to get them before any changes | ||
325 | * might happen. | ||
326 | */ | ||
327 | u64 cp0_cause = read_c0_cause(); | ||
328 | u64 cp0_status = read_c0_status(); | ||
329 | u64 cp0_error_epc = read_c0_errorepc(); | ||
330 | u64 cp0_epc = read_c0_epc(); | ||
331 | |||
332 | /* Delay so output from all cores output is not jumbled together. */ | ||
333 | __delay(100000000ull * coreid); | ||
334 | |||
335 | octeon_wdt_write_string("\r\n*** NMI Watchdog interrupt on Core 0x"); | ||
336 | octeon_wdt_write_hex(coreid, 1); | ||
337 | octeon_wdt_write_string(" ***\r\n"); | ||
338 | for (i = 0; i < 32; i++) { | ||
339 | octeon_wdt_write_string("\t"); | ||
340 | octeon_wdt_write_string(reg_name[i]); | ||
341 | octeon_wdt_write_string("\t0x"); | ||
342 | octeon_wdt_write_hex(reg[i], 16); | ||
343 | if (i & 1) | ||
344 | octeon_wdt_write_string("\r\n"); | ||
345 | } | ||
346 | octeon_wdt_write_string("\terr_epc\t0x"); | ||
347 | octeon_wdt_write_hex(cp0_error_epc, 16); | ||
348 | |||
349 | octeon_wdt_write_string("\tepc\t0x"); | ||
350 | octeon_wdt_write_hex(cp0_epc, 16); | ||
351 | octeon_wdt_write_string("\r\n"); | ||
352 | |||
353 | octeon_wdt_write_string("\tstatus\t0x"); | ||
354 | octeon_wdt_write_hex(cp0_status, 16); | ||
355 | octeon_wdt_write_string("\tcause\t0x"); | ||
356 | octeon_wdt_write_hex(cp0_cause, 16); | ||
357 | octeon_wdt_write_string("\r\n"); | ||
358 | |||
359 | octeon_wdt_write_string("\tsum0\t0x"); | ||
360 | octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_SUM0(coreid * 2)), 16); | ||
361 | octeon_wdt_write_string("\ten0\t0x"); | ||
362 | octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)), 16); | ||
363 | octeon_wdt_write_string("\r\n"); | ||
364 | |||
365 | octeon_wdt_write_string("*** Chip soft reset soon ***\r\n"); | ||
366 | } | ||
367 | |||
368 | static void octeon_wdt_disable_interrupt(int cpu) | ||
369 | { | ||
370 | unsigned int core; | ||
371 | unsigned int irq; | ||
372 | union cvmx_ciu_wdogx ciu_wdog; | ||
373 | |||
374 | core = cpu2core(cpu); | ||
375 | |||
376 | irq = OCTEON_IRQ_WDOG0 + core; | ||
377 | |||
378 | /* Poke the watchdog to clear out its state */ | ||
379 | cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1); | ||
380 | |||
381 | /* Disable the hardware. */ | ||
382 | ciu_wdog.u64 = 0; | ||
383 | cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64); | ||
384 | |||
385 | free_irq(irq, octeon_wdt_poke_irq); | ||
386 | } | ||
387 | |||
388 | static void octeon_wdt_setup_interrupt(int cpu) | ||
389 | { | ||
390 | unsigned int core; | ||
391 | unsigned int irq; | ||
392 | union cvmx_ciu_wdogx ciu_wdog; | ||
393 | |||
394 | core = cpu2core(cpu); | ||
395 | |||
396 | /* Disable it before doing anything with the interrupts. */ | ||
397 | ciu_wdog.u64 = 0; | ||
398 | cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64); | ||
399 | |||
400 | per_cpu_countdown[cpu] = countdown_reset; | ||
401 | |||
402 | irq = OCTEON_IRQ_WDOG0 + core; | ||
403 | |||
404 | if (request_irq(irq, octeon_wdt_poke_irq, | ||
405 | IRQF_DISABLED, "octeon_wdt", octeon_wdt_poke_irq)) | ||
406 | panic("octeon_wdt: Couldn't obtain irq %d", irq); | ||
407 | |||
408 | cpumask_set_cpu(cpu, &irq_enabled_cpus); | ||
409 | |||
410 | /* Poke the watchdog to clear out its state */ | ||
411 | cvmx_write_csr(CVMX_CIU_PP_POKEX(core), 1); | ||
412 | |||
413 | /* Finally enable the watchdog now that all handlers are installed */ | ||
414 | ciu_wdog.u64 = 0; | ||
415 | ciu_wdog.s.len = timeout_cnt; | ||
416 | ciu_wdog.s.mode = 3; /* 3 = Interrupt + NMI + Soft-Reset */ | ||
417 | cvmx_write_csr(CVMX_CIU_WDOGX(core), ciu_wdog.u64); | ||
418 | } | ||
419 | |||
420 | static int octeon_wdt_cpu_callback(struct notifier_block *nfb, | ||
421 | unsigned long action, void *hcpu) | ||
422 | { | ||
423 | unsigned int cpu = (unsigned long)hcpu; | ||
424 | |||
425 | switch (action) { | ||
426 | case CPU_DOWN_PREPARE: | ||
427 | octeon_wdt_disable_interrupt(cpu); | ||
428 | break; | ||
429 | case CPU_ONLINE: | ||
430 | case CPU_DOWN_FAILED: | ||
431 | octeon_wdt_setup_interrupt(cpu); | ||
432 | break; | ||
433 | default: | ||
434 | break; | ||
435 | } | ||
436 | return NOTIFY_OK; | ||
437 | } | ||
438 | |||
439 | static void octeon_wdt_ping(void) | ||
440 | { | ||
441 | int cpu; | ||
442 | int coreid; | ||
443 | |||
444 | for_each_online_cpu(cpu) { | ||
445 | coreid = cpu2core(cpu); | ||
446 | cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1); | ||
447 | per_cpu_countdown[cpu] = countdown_reset; | ||
448 | if ((countdown_reset || !do_coundown) && | ||
449 | !cpumask_test_cpu(cpu, &irq_enabled_cpus)) { | ||
450 | /* We have to enable the irq */ | ||
451 | int irq = OCTEON_IRQ_WDOG0 + coreid; | ||
452 | enable_irq(irq); | ||
453 | cpumask_set_cpu(cpu, &irq_enabled_cpus); | ||
454 | } | ||
455 | } | ||
456 | } | ||
457 | |||
458 | static void octeon_wdt_calc_parameters(int t) | ||
459 | { | ||
460 | unsigned int periods; | ||
461 | |||
462 | timeout_sec = max_timeout_sec; | ||
463 | |||
464 | |||
465 | /* | ||
466 | * Find the largest interrupt period, that can evenly divide | ||
467 | * the requested heartbeat time. | ||
468 | */ | ||
469 | while ((t % timeout_sec) != 0) | ||
470 | timeout_sec--; | ||
471 | |||
472 | periods = t / timeout_sec; | ||
473 | |||
474 | /* | ||
475 | * The last two periods are after the irq is disabled, and | ||
476 | * then to the nmi, so we subtract them off. | ||
477 | */ | ||
478 | |||
479 | countdown_reset = periods > 2 ? periods - 2 : 0; | ||
480 | heartbeat = t; | ||
481 | timeout_cnt = ((octeon_get_io_clock_rate() >> 8) * timeout_sec) >> 8; | ||
482 | } | ||
483 | |||
484 | static int octeon_wdt_set_heartbeat(int t) | ||
485 | { | ||
486 | int cpu; | ||
487 | int coreid; | ||
488 | union cvmx_ciu_wdogx ciu_wdog; | ||
489 | |||
490 | if (t <= 0) | ||
491 | return -1; | ||
492 | |||
493 | octeon_wdt_calc_parameters(t); | ||
494 | |||
495 | for_each_online_cpu(cpu) { | ||
496 | coreid = cpu2core(cpu); | ||
497 | cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1); | ||
498 | ciu_wdog.u64 = 0; | ||
499 | ciu_wdog.s.len = timeout_cnt; | ||
500 | ciu_wdog.s.mode = 3; /* 3 = Interrupt + NMI + Soft-Reset */ | ||
501 | cvmx_write_csr(CVMX_CIU_WDOGX(coreid), ciu_wdog.u64); | ||
502 | cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1); | ||
503 | } | ||
504 | octeon_wdt_ping(); /* Get the irqs back on. */ | ||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | /** | ||
509 | * octeon_wdt_write: | ||
510 | * @file: file handle to the watchdog | ||
511 | * @buf: buffer to write (unused as data does not matter here | ||
512 | * @count: count of bytes | ||
513 | * @ppos: pointer to the position to write. No seeks allowed | ||
514 | * | ||
515 | * A write to a watchdog device is defined as a keepalive signal. Any | ||
516 | * write of data will do, as we we don't define content meaning. | ||
517 | */ | ||
518 | |||
519 | static ssize_t octeon_wdt_write(struct file *file, const char __user *buf, | ||
520 | size_t count, loff_t *ppos) | ||
521 | { | ||
522 | if (count) { | ||
523 | if (!nowayout) { | ||
524 | size_t i; | ||
525 | |||
526 | /* In case it was set long ago */ | ||
527 | expect_close = 0; | ||
528 | |||
529 | for (i = 0; i != count; i++) { | ||
530 | char c; | ||
531 | if (get_user(c, buf + i)) | ||
532 | return -EFAULT; | ||
533 | if (c == 'V') | ||
534 | expect_close = 1; | ||
535 | } | ||
536 | } | ||
537 | octeon_wdt_ping(); | ||
538 | } | ||
539 | return count; | ||
540 | } | ||
541 | |||
542 | /** | ||
543 | * octeon_wdt_ioctl: | ||
544 | * @file: file handle to the device | ||
545 | * @cmd: watchdog command | ||
546 | * @arg: argument pointer | ||
547 | * | ||
548 | * The watchdog API defines a common set of functions for all | ||
549 | * watchdogs according to their available features. We only | ||
550 | * actually usefully support querying capabilities and setting | ||
551 | * the timeout. | ||
552 | */ | ||
553 | |||
554 | static long octeon_wdt_ioctl(struct file *file, unsigned int cmd, | ||
555 | unsigned long arg) | ||
556 | { | ||
557 | void __user *argp = (void __user *)arg; | ||
558 | int __user *p = argp; | ||
559 | int new_heartbeat; | ||
560 | |||
561 | static struct watchdog_info ident = { | ||
562 | .options = WDIOF_SETTIMEOUT| | ||
563 | WDIOF_MAGICCLOSE| | ||
564 | WDIOF_KEEPALIVEPING, | ||
565 | .firmware_version = 1, | ||
566 | .identity = "OCTEON", | ||
567 | }; | ||
568 | |||
569 | switch (cmd) { | ||
570 | case WDIOC_GETSUPPORT: | ||
571 | return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; | ||
572 | case WDIOC_GETSTATUS: | ||
573 | case WDIOC_GETBOOTSTATUS: | ||
574 | return put_user(0, p); | ||
575 | case WDIOC_KEEPALIVE: | ||
576 | octeon_wdt_ping(); | ||
577 | return 0; | ||
578 | case WDIOC_SETTIMEOUT: | ||
579 | if (get_user(new_heartbeat, p)) | ||
580 | return -EFAULT; | ||
581 | if (octeon_wdt_set_heartbeat(new_heartbeat)) | ||
582 | return -EINVAL; | ||
583 | /* Fall through. */ | ||
584 | case WDIOC_GETTIMEOUT: | ||
585 | return put_user(heartbeat, p); | ||
586 | default: | ||
587 | return -ENOTTY; | ||
588 | } | ||
589 | } | ||
590 | |||
591 | /** | ||
592 | * octeon_wdt_open: | ||
593 | * @inode: inode of device | ||
594 | * @file: file handle to device | ||
595 | * | ||
596 | * The watchdog device has been opened. The watchdog device is single | ||
597 | * open and on opening we do a ping to reset the counters. | ||
598 | */ | ||
599 | |||
600 | static int octeon_wdt_open(struct inode *inode, struct file *file) | ||
601 | { | ||
602 | if (test_and_set_bit(0, &octeon_wdt_is_open)) | ||
603 | return -EBUSY; | ||
604 | /* | ||
605 | * Activate | ||
606 | */ | ||
607 | octeon_wdt_ping(); | ||
608 | do_coundown = 1; | ||
609 | return nonseekable_open(inode, file); | ||
610 | } | ||
611 | |||
612 | /** | ||
613 | * octeon_wdt_release: | ||
614 | * @inode: inode to board | ||
615 | * @file: file handle to board | ||
616 | * | ||
617 | * The watchdog has a configurable API. There is a religious dispute | ||
618 | * between people who want their watchdog to be able to shut down and | ||
619 | * those who want to be sure if the watchdog manager dies the machine | ||
620 | * reboots. In the former case we disable the counters, in the latter | ||
621 | * case you have to open it again very soon. | ||
622 | */ | ||
623 | |||
624 | static int octeon_wdt_release(struct inode *inode, struct file *file) | ||
625 | { | ||
626 | if (expect_close) { | ||
627 | do_coundown = 0; | ||
628 | octeon_wdt_ping(); | ||
629 | } else { | ||
630 | pr_crit("octeon_wdt: WDT device closed unexpectedly. WDT will not stop!\n"); | ||
631 | } | ||
632 | clear_bit(0, &octeon_wdt_is_open); | ||
633 | expect_close = 0; | ||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | static const struct file_operations octeon_wdt_fops = { | ||
638 | .owner = THIS_MODULE, | ||
639 | .llseek = no_llseek, | ||
640 | .write = octeon_wdt_write, | ||
641 | .unlocked_ioctl = octeon_wdt_ioctl, | ||
642 | .open = octeon_wdt_open, | ||
643 | .release = octeon_wdt_release, | ||
644 | }; | ||
645 | |||
646 | static struct miscdevice octeon_wdt_miscdev = { | ||
647 | .minor = WATCHDOG_MINOR, | ||
648 | .name = "watchdog", | ||
649 | .fops = &octeon_wdt_fops, | ||
650 | }; | ||
651 | |||
652 | static struct notifier_block octeon_wdt_cpu_notifier = { | ||
653 | .notifier_call = octeon_wdt_cpu_callback, | ||
654 | }; | ||
655 | |||
656 | |||
657 | /** | ||
658 | * Module/ driver initialization. | ||
659 | * | ||
660 | * Returns Zero on success | ||
661 | */ | ||
662 | static int __init octeon_wdt_init(void) | ||
663 | { | ||
664 | int i; | ||
665 | int ret; | ||
666 | int cpu; | ||
667 | u64 *ptr; | ||
668 | |||
669 | /* | ||
670 | * Watchdog time expiration length = The 16 bits of LEN | ||
671 | * represent the most significant bits of a 24 bit decrementer | ||
672 | * that decrements every 256 cycles. | ||
673 | * | ||
674 | * Try for a timeout of 5 sec, if that fails a smaller number | ||
675 | * of even seconds, | ||
676 | */ | ||
677 | max_timeout_sec = 6; | ||
678 | do { | ||
679 | max_timeout_sec--; | ||
680 | timeout_cnt = ((octeon_get_io_clock_rate() >> 8) * max_timeout_sec) >> 8; | ||
681 | } while (timeout_cnt > 65535); | ||
682 | |||
683 | BUG_ON(timeout_cnt == 0); | ||
684 | |||
685 | octeon_wdt_calc_parameters(heartbeat); | ||
686 | |||
687 | pr_info("octeon_wdt: Initial granularity %d Sec.\n", timeout_sec); | ||
688 | |||
689 | ret = misc_register(&octeon_wdt_miscdev); | ||
690 | if (ret) { | ||
691 | pr_err("octeon_wdt: cannot register miscdev on minor=%d (err=%d)\n", | ||
692 | WATCHDOG_MINOR, ret); | ||
693 | goto out; | ||
694 | } | ||
695 | |||
696 | /* Build the NMI handler ... */ | ||
697 | octeon_wdt_build_stage1(); | ||
698 | |||
699 | /* ... and install it. */ | ||
700 | ptr = (u64 *) nmi_stage1_insns; | ||
701 | for (i = 0; i < 16; i++) { | ||
702 | cvmx_write_csr(CVMX_MIO_BOOT_LOC_ADR, i * 8); | ||
703 | cvmx_write_csr(CVMX_MIO_BOOT_LOC_DAT, ptr[i]); | ||
704 | } | ||
705 | cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0x81fc0000); | ||
706 | |||
707 | cpumask_clear(&irq_enabled_cpus); | ||
708 | |||
709 | for_each_online_cpu(cpu) | ||
710 | octeon_wdt_setup_interrupt(cpu); | ||
711 | |||
712 | register_hotcpu_notifier(&octeon_wdt_cpu_notifier); | ||
713 | out: | ||
714 | return ret; | ||
715 | } | ||
716 | |||
717 | /** | ||
718 | * Module / driver shutdown | ||
719 | */ | ||
720 | static void __exit octeon_wdt_cleanup(void) | ||
721 | { | ||
722 | int cpu; | ||
723 | |||
724 | misc_deregister(&octeon_wdt_miscdev); | ||
725 | |||
726 | unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier); | ||
727 | |||
728 | for_each_online_cpu(cpu) { | ||
729 | int core = cpu2core(cpu); | ||
730 | /* Disable the watchdog */ | ||
731 | cvmx_write_csr(CVMX_CIU_WDOGX(core), 0); | ||
732 | /* Free the interrupt handler */ | ||
733 | free_irq(OCTEON_IRQ_WDOG0 + core, octeon_wdt_poke_irq); | ||
734 | } | ||
735 | /* | ||
736 | * Disable the boot-bus memory, the code it points to is soon | ||
737 | * to go missing. | ||
738 | */ | ||
739 | cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0); | ||
740 | } | ||
741 | |||
742 | MODULE_LICENSE("GPL"); | ||
743 | MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>"); | ||
744 | MODULE_DESCRIPTION("Cavium Networks Octeon Watchdog driver."); | ||
745 | module_init(octeon_wdt_init); | ||
746 | module_exit(octeon_wdt_cleanup); | ||
diff --git a/drivers/watchdog/octeon-wdt-nmi.S b/drivers/watchdog/octeon-wdt-nmi.S new file mode 100644 index 000000000000..8a900a5e3233 --- /dev/null +++ b/drivers/watchdog/octeon-wdt-nmi.S | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2007 Cavium Networks | ||
7 | */ | ||
8 | #include <asm/asm.h> | ||
9 | #include <asm/regdef.h> | ||
10 | |||
11 | #define SAVE_REG(r) sd $r, -32768+6912-(32-r)*8($0) | ||
12 | |||
13 | NESTED(octeon_wdt_nmi_stage2, 0, sp) | ||
14 | .set push | ||
15 | .set noreorder | ||
16 | .set noat | ||
17 | /* Save all registers to the top CVMSEG. This shouldn't | ||
18 | * corrupt any state used by the kernel. Also all registers | ||
19 | * should have the value right before the NMI. */ | ||
20 | SAVE_REG(0) | ||
21 | SAVE_REG(1) | ||
22 | SAVE_REG(2) | ||
23 | SAVE_REG(3) | ||
24 | SAVE_REG(4) | ||
25 | SAVE_REG(5) | ||
26 | SAVE_REG(6) | ||
27 | SAVE_REG(7) | ||
28 | SAVE_REG(8) | ||
29 | SAVE_REG(9) | ||
30 | SAVE_REG(10) | ||
31 | SAVE_REG(11) | ||
32 | SAVE_REG(12) | ||
33 | SAVE_REG(13) | ||
34 | SAVE_REG(14) | ||
35 | SAVE_REG(15) | ||
36 | SAVE_REG(16) | ||
37 | SAVE_REG(17) | ||
38 | SAVE_REG(18) | ||
39 | SAVE_REG(19) | ||
40 | SAVE_REG(20) | ||
41 | SAVE_REG(21) | ||
42 | SAVE_REG(22) | ||
43 | SAVE_REG(23) | ||
44 | SAVE_REG(24) | ||
45 | SAVE_REG(25) | ||
46 | SAVE_REG(26) | ||
47 | SAVE_REG(27) | ||
48 | SAVE_REG(28) | ||
49 | SAVE_REG(29) | ||
50 | SAVE_REG(30) | ||
51 | SAVE_REG(31) | ||
52 | /* Set the stack to begin right below the registers */ | ||
53 | li sp, -32768+6912-32*8 | ||
54 | /* Load the address of the third stage handler */ | ||
55 | dla a0, octeon_wdt_nmi_stage3 | ||
56 | /* Call the third stage handler */ | ||
57 | jal a0 | ||
58 | /* a0 is the address of the saved registers */ | ||
59 | move a0, sp | ||
60 | /* Loop forvever if we get here. */ | ||
61 | 1: b 1b | ||
62 | nop | ||
63 | .set pop | ||
64 | END(octeon_wdt_nmi_stage2) | ||
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 76b58abf4451..3dd4971160ef 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c | |||
@@ -38,11 +38,11 @@ | |||
38 | #include <linux/err.h> | 38 | #include <linux/err.h> |
39 | #include <linux/platform_device.h> | 39 | #include <linux/platform_device.h> |
40 | #include <linux/moduleparam.h> | 40 | #include <linux/moduleparam.h> |
41 | #include <linux/clk.h> | ||
42 | #include <linux/bitops.h> | 41 | #include <linux/bitops.h> |
43 | #include <linux/io.h> | 42 | #include <linux/io.h> |
44 | #include <linux/uaccess.h> | 43 | #include <linux/uaccess.h> |
45 | #include <linux/slab.h> | 44 | #include <linux/slab.h> |
45 | #include <linux/pm_runtime.h> | ||
46 | #include <mach/hardware.h> | 46 | #include <mach/hardware.h> |
47 | #include <plat/prcm.h> | 47 | #include <plat/prcm.h> |
48 | 48 | ||
@@ -61,8 +61,6 @@ struct omap_wdt_dev { | |||
61 | void __iomem *base; /* physical */ | 61 | void __iomem *base; /* physical */ |
62 | struct device *dev; | 62 | struct device *dev; |
63 | int omap_wdt_users; | 63 | int omap_wdt_users; |
64 | struct clk *ick; | ||
65 | struct clk *fck; | ||
66 | struct resource *mem; | 64 | struct resource *mem; |
67 | struct miscdevice omap_wdt_miscdev; | 65 | struct miscdevice omap_wdt_miscdev; |
68 | }; | 66 | }; |
@@ -146,8 +144,7 @@ static int omap_wdt_open(struct inode *inode, struct file *file) | |||
146 | if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users))) | 144 | if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users))) |
147 | return -EBUSY; | 145 | return -EBUSY; |
148 | 146 | ||
149 | clk_enable(wdev->ick); /* Enable the interface clock */ | 147 | pm_runtime_get_sync(wdev->dev); |
150 | clk_enable(wdev->fck); /* Enable the functional clock */ | ||
151 | 148 | ||
152 | /* initialize prescaler */ | 149 | /* initialize prescaler */ |
153 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) | 150 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) |
@@ -177,8 +174,7 @@ static int omap_wdt_release(struct inode *inode, struct file *file) | |||
177 | 174 | ||
178 | omap_wdt_disable(wdev); | 175 | omap_wdt_disable(wdev); |
179 | 176 | ||
180 | clk_disable(wdev->ick); | 177 | pm_runtime_put_sync(wdev->dev); |
181 | clk_disable(wdev->fck); | ||
182 | #else | 178 | #else |
183 | printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); | 179 | printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); |
184 | #endif | 180 | #endif |
@@ -258,6 +254,7 @@ static const struct file_operations omap_wdt_fops = { | |||
258 | .unlocked_ioctl = omap_wdt_ioctl, | 254 | .unlocked_ioctl = omap_wdt_ioctl, |
259 | .open = omap_wdt_open, | 255 | .open = omap_wdt_open, |
260 | .release = omap_wdt_release, | 256 | .release = omap_wdt_release, |
257 | .llseek = no_llseek, | ||
261 | }; | 258 | }; |
262 | 259 | ||
263 | static int __devinit omap_wdt_probe(struct platform_device *pdev) | 260 | static int __devinit omap_wdt_probe(struct platform_device *pdev) |
@@ -292,19 +289,7 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) | |||
292 | 289 | ||
293 | wdev->omap_wdt_users = 0; | 290 | wdev->omap_wdt_users = 0; |
294 | wdev->mem = mem; | 291 | wdev->mem = mem; |
295 | 292 | wdev->dev = &pdev->dev; | |
296 | wdev->ick = clk_get(&pdev->dev, "ick"); | ||
297 | if (IS_ERR(wdev->ick)) { | ||
298 | ret = PTR_ERR(wdev->ick); | ||
299 | wdev->ick = NULL; | ||
300 | goto err_clk; | ||
301 | } | ||
302 | wdev->fck = clk_get(&pdev->dev, "fck"); | ||
303 | if (IS_ERR(wdev->fck)) { | ||
304 | ret = PTR_ERR(wdev->fck); | ||
305 | wdev->fck = NULL; | ||
306 | goto err_clk; | ||
307 | } | ||
308 | 293 | ||
309 | wdev->base = ioremap(res->start, resource_size(res)); | 294 | wdev->base = ioremap(res->start, resource_size(res)); |
310 | if (!wdev->base) { | 295 | if (!wdev->base) { |
@@ -314,8 +299,8 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) | |||
314 | 299 | ||
315 | platform_set_drvdata(pdev, wdev); | 300 | platform_set_drvdata(pdev, wdev); |
316 | 301 | ||
317 | clk_enable(wdev->ick); | 302 | pm_runtime_enable(wdev->dev); |
318 | clk_enable(wdev->fck); | 303 | pm_runtime_get_sync(wdev->dev); |
319 | 304 | ||
320 | omap_wdt_disable(wdev); | 305 | omap_wdt_disable(wdev); |
321 | omap_wdt_adjust_timeout(timer_margin); | 306 | omap_wdt_adjust_timeout(timer_margin); |
@@ -333,11 +318,7 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) | |||
333 | __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, | 318 | __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, |
334 | timer_margin); | 319 | timer_margin); |
335 | 320 | ||
336 | /* autogate OCP interface clock */ | 321 | pm_runtime_put_sync(wdev->dev); |
337 | __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG); | ||
338 | |||
339 | clk_disable(wdev->ick); | ||
340 | clk_disable(wdev->fck); | ||
341 | 322 | ||
342 | omap_wdt_dev = pdev; | 323 | omap_wdt_dev = pdev; |
343 | 324 | ||
@@ -349,12 +330,6 @@ err_misc: | |||
349 | 330 | ||
350 | err_ioremap: | 331 | err_ioremap: |
351 | wdev->base = NULL; | 332 | wdev->base = NULL; |
352 | |||
353 | err_clk: | ||
354 | if (wdev->ick) | ||
355 | clk_put(wdev->ick); | ||
356 | if (wdev->fck) | ||
357 | clk_put(wdev->fck); | ||
358 | kfree(wdev); | 333 | kfree(wdev); |
359 | 334 | ||
360 | err_kzalloc: | 335 | err_kzalloc: |
@@ -386,8 +361,6 @@ static int __devexit omap_wdt_remove(struct platform_device *pdev) | |||
386 | release_mem_region(res->start, resource_size(res)); | 361 | release_mem_region(res->start, resource_size(res)); |
387 | platform_set_drvdata(pdev, NULL); | 362 | platform_set_drvdata(pdev, NULL); |
388 | 363 | ||
389 | clk_put(wdev->ick); | ||
390 | clk_put(wdev->fck); | ||
391 | iounmap(wdev->base); | 364 | iounmap(wdev->base); |
392 | 365 | ||
393 | kfree(wdev); | 366 | kfree(wdev); |
diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index d3aa2f1fe61d..3a56bc360924 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c | |||
@@ -53,7 +53,9 @@ | |||
53 | #define WDTO 0x11 /* Watchdog timeout register */ | 53 | #define WDTO 0x11 /* Watchdog timeout register */ |
54 | #define WDCFG 0x12 /* Watchdog config register */ | 54 | #define WDCFG 0x12 /* Watchdog config register */ |
55 | 55 | ||
56 | static int io = 0x2E; /* Address used on Portwell Boards */ | 56 | #define IO_DEFAULT 0x2E /* Address used on Portwell Boards */ |
57 | |||
58 | static int io = IO_DEFAULT; | ||
57 | 59 | ||
58 | static int timeout = DEFAULT_TIMEOUT; /* timeout value */ | 60 | static int timeout = DEFAULT_TIMEOUT; /* timeout value */ |
59 | static unsigned long timer_enabled; /* is the timer enabled? */ | 61 | static unsigned long timer_enabled; /* is the timer enabled? */ |
@@ -583,12 +585,13 @@ MODULE_LICENSE("GPL"); | |||
583 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 585 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
584 | 586 | ||
585 | module_param(io, int, 0); | 587 | module_param(io, int, 0); |
586 | MODULE_PARM_DESC(io, MODNAME " I/O port (default: " __MODULE_STRING(io) ")."); | 588 | MODULE_PARM_DESC(io, MODNAME " I/O port (default: " |
589 | __MODULE_STRING(IO_DEFAULT) ")."); | ||
587 | 590 | ||
588 | module_param(timeout, int, 0); | 591 | module_param(timeout, int, 0); |
589 | MODULE_PARM_DESC(timeout, | 592 | MODULE_PARM_DESC(timeout, |
590 | "Watchdog timeout in minutes (default=" | 593 | "Watchdog timeout in minutes (default=" |
591 | __MODULE_STRING(timeout) ")."); | 594 | __MODULE_STRING(DEFAULT_TIMEOUT) ")."); |
592 | 595 | ||
593 | module_param(nowayout, int, 0); | 596 | module_param(nowayout, int, 0); |
594 | MODULE_PARM_DESC(nowayout, | 597 | MODULE_PARM_DESC(nowayout, |
diff --git a/drivers/watchdog/pnx833x_wdt.c b/drivers/watchdog/pnx833x_wdt.c index 09102f09e681..a7b5ad2a98bd 100644 --- a/drivers/watchdog/pnx833x_wdt.c +++ b/drivers/watchdog/pnx833x_wdt.c | |||
@@ -33,6 +33,8 @@ | |||
33 | #define PFX "pnx833x: " | 33 | #define PFX "pnx833x: " |
34 | #define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */ | 34 | #define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */ |
35 | #define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */ | 35 | #define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */ |
36 | #define PNX_WATCHDOG_TIMEOUT (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY) | ||
37 | #define PNX_TIMEOUT_VALUE 2040000000U | ||
36 | 38 | ||
37 | /** CONFIG block */ | 39 | /** CONFIG block */ |
38 | #define PNX833X_CONFIG (0x07000U) | 40 | #define PNX833X_CONFIG (0x07000U) |
@@ -47,20 +49,21 @@ | |||
47 | static int pnx833x_wdt_alive; | 49 | static int pnx833x_wdt_alive; |
48 | 50 | ||
49 | /* Set default timeout in MHZ.*/ | 51 | /* Set default timeout in MHZ.*/ |
50 | static int pnx833x_wdt_timeout = (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY); | 52 | static int pnx833x_wdt_timeout = PNX_WATCHDOG_TIMEOUT; |
51 | module_param(pnx833x_wdt_timeout, int, 0); | 53 | module_param(pnx833x_wdt_timeout, int, 0); |
52 | MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default=" | 54 | MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default=" |
53 | __MODULE_STRING(pnx833x_wdt_timeout) "(30 seconds)."); | 55 | __MODULE_STRING(PNX_TIMEOUT_VALUE) "(30 seconds)."); |
54 | 56 | ||
55 | static int nowayout = WATCHDOG_NOWAYOUT; | 57 | static int nowayout = WATCHDOG_NOWAYOUT; |
56 | module_param(nowayout, int, 0); | 58 | module_param(nowayout, int, 0); |
57 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | 59 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" |
58 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 60 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
59 | 61 | ||
60 | static int start_enabled = 1; | 62 | #define START_DEFAULT 1 |
63 | static int start_enabled = START_DEFAULT; | ||
61 | module_param(start_enabled, int, 0); | 64 | module_param(start_enabled, int, 0); |
62 | MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion " | 65 | MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion " |
63 | "(default=" __MODULE_STRING(start_enabled) ")"); | 66 | "(default=" __MODULE_STRING(START_DEFAULT) ")"); |
64 | 67 | ||
65 | static void pnx833x_wdt_start(void) | 68 | static void pnx833x_wdt_start(void) |
66 | { | 69 | { |
diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c index 69c6adbd8205..3939e53f5f98 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * RDC321x watchdog driver | 2 | * RDC321x watchdog driver |
3 | * | 3 | * |
4 | * Copyright (C) 2007 Florian Fainelli <florian@openwrt.org> | 4 | * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> |
5 | * | 5 | * |
6 | * This driver is highly inspired from the cpu5_wdt driver | 6 | * This driver is highly inspired from the cpu5_wdt driver |
7 | * | 7 | * |
@@ -36,8 +36,7 @@ | |||
36 | #include <linux/watchdog.h> | 36 | #include <linux/watchdog.h> |
37 | #include <linux/io.h> | 37 | #include <linux/io.h> |
38 | #include <linux/uaccess.h> | 38 | #include <linux/uaccess.h> |
39 | 39 | #include <linux/mfd/rdc321x.h> | |
40 | #include <asm/rdc321x_defs.h> | ||
41 | 40 | ||
42 | #define RDC_WDT_MASK 0x80000000 /* Mask */ | 41 | #define RDC_WDT_MASK 0x80000000 /* Mask */ |
43 | #define RDC_WDT_EN 0x00800000 /* Enable bit */ | 42 | #define RDC_WDT_EN 0x00800000 /* Enable bit */ |
@@ -63,6 +62,8 @@ static struct { | |||
63 | int default_ticks; | 62 | int default_ticks; |
64 | unsigned long inuse; | 63 | unsigned long inuse; |
65 | spinlock_t lock; | 64 | spinlock_t lock; |
65 | struct pci_dev *sb_pdev; | ||
66 | int base_reg; | ||
66 | } rdc321x_wdt_device; | 67 | } rdc321x_wdt_device; |
67 | 68 | ||
68 | /* generic helper functions */ | 69 | /* generic helper functions */ |
@@ -70,14 +71,18 @@ static struct { | |||
70 | static void rdc321x_wdt_trigger(unsigned long unused) | 71 | static void rdc321x_wdt_trigger(unsigned long unused) |
71 | { | 72 | { |
72 | unsigned long flags; | 73 | unsigned long flags; |
74 | u32 val; | ||
73 | 75 | ||
74 | if (rdc321x_wdt_device.running) | 76 | if (rdc321x_wdt_device.running) |
75 | ticks--; | 77 | ticks--; |
76 | 78 | ||
77 | /* keep watchdog alive */ | 79 | /* keep watchdog alive */ |
78 | spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); | 80 | spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); |
79 | outl(RDC_WDT_EN | inl(RDC3210_CFGREG_DATA), | 81 | pci_read_config_dword(rdc321x_wdt_device.sb_pdev, |
80 | RDC3210_CFGREG_DATA); | 82 | rdc321x_wdt_device.base_reg, &val); |
83 | val |= RDC_WDT_EN; | ||
84 | pci_write_config_dword(rdc321x_wdt_device.sb_pdev, | ||
85 | rdc321x_wdt_device.base_reg, val); | ||
81 | spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); | 86 | spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); |
82 | 87 | ||
83 | /* requeue?? */ | 88 | /* requeue?? */ |
@@ -105,10 +110,13 @@ static void rdc321x_wdt_start(void) | |||
105 | 110 | ||
106 | /* Clear the timer */ | 111 | /* Clear the timer */ |
107 | spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); | 112 | spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); |
108 | outl(RDC_CLS_TMR, RDC3210_CFGREG_ADDR); | 113 | pci_write_config_dword(rdc321x_wdt_device.sb_pdev, |
114 | rdc321x_wdt_device.base_reg, RDC_CLS_TMR); | ||
109 | 115 | ||
110 | /* Enable watchdog and set the timeout to 81.92 us */ | 116 | /* Enable watchdog and set the timeout to 81.92 us */ |
111 | outl(RDC_WDT_EN | RDC_WDT_CNT, RDC3210_CFGREG_DATA); | 117 | pci_write_config_dword(rdc321x_wdt_device.sb_pdev, |
118 | rdc321x_wdt_device.base_reg, | ||
119 | RDC_WDT_EN | RDC_WDT_CNT); | ||
112 | spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); | 120 | spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); |
113 | 121 | ||
114 | mod_timer(&rdc321x_wdt_device.timer, | 122 | mod_timer(&rdc321x_wdt_device.timer, |
@@ -148,7 +156,7 @@ static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd, | |||
148 | unsigned long arg) | 156 | unsigned long arg) |
149 | { | 157 | { |
150 | void __user *argp = (void __user *)arg; | 158 | void __user *argp = (void __user *)arg; |
151 | unsigned int value; | 159 | u32 value; |
152 | static const struct watchdog_info ident = { | 160 | static const struct watchdog_info ident = { |
153 | .options = WDIOF_CARDRESET, | 161 | .options = WDIOF_CARDRESET, |
154 | .identity = "RDC321x WDT", | 162 | .identity = "RDC321x WDT", |
@@ -162,9 +170,10 @@ static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd, | |||
162 | case WDIOC_GETSTATUS: | 170 | case WDIOC_GETSTATUS: |
163 | /* Read the value from the DATA register */ | 171 | /* Read the value from the DATA register */ |
164 | spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); | 172 | spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); |
165 | value = inl(RDC3210_CFGREG_DATA); | 173 | pci_read_config_dword(rdc321x_wdt_device.sb_pdev, |
174 | rdc321x_wdt_device.base_reg, &value); | ||
166 | spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); | 175 | spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); |
167 | if (copy_to_user(argp, &value, sizeof(int))) | 176 | if (copy_to_user(argp, &value, sizeof(u32))) |
168 | return -EFAULT; | 177 | return -EFAULT; |
169 | break; | 178 | break; |
170 | case WDIOC_GETSUPPORT: | 179 | case WDIOC_GETSUPPORT: |
@@ -219,17 +228,35 @@ static struct miscdevice rdc321x_wdt_misc = { | |||
219 | static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) | 228 | static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) |
220 | { | 229 | { |
221 | int err; | 230 | int err; |
231 | struct resource *r; | ||
232 | struct rdc321x_wdt_pdata *pdata; | ||
233 | |||
234 | pdata = platform_get_drvdata(pdev); | ||
235 | if (!pdata) { | ||
236 | dev_err(&pdev->dev, "no platform data supplied\n"); | ||
237 | return -ENODEV; | ||
238 | } | ||
239 | |||
240 | r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg"); | ||
241 | if (!r) { | ||
242 | dev_err(&pdev->dev, "failed to get wdt-reg resource\n"); | ||
243 | return -ENODEV; | ||
244 | } | ||
245 | |||
246 | rdc321x_wdt_device.sb_pdev = pdata->sb_pdev; | ||
247 | rdc321x_wdt_device.base_reg = r->start; | ||
222 | 248 | ||
223 | err = misc_register(&rdc321x_wdt_misc); | 249 | err = misc_register(&rdc321x_wdt_misc); |
224 | if (err < 0) { | 250 | if (err < 0) { |
225 | printk(KERN_ERR PFX "watchdog misc_register failed\n"); | 251 | dev_err(&pdev->dev, "misc_register failed\n"); |
226 | return err; | 252 | return err; |
227 | } | 253 | } |
228 | 254 | ||
229 | spin_lock_init(&rdc321x_wdt_device.lock); | 255 | spin_lock_init(&rdc321x_wdt_device.lock); |
230 | 256 | ||
231 | /* Reset the watchdog */ | 257 | /* Reset the watchdog */ |
232 | outl(RDC_WDT_RST, RDC3210_CFGREG_DATA); | 258 | pci_write_config_dword(rdc321x_wdt_device.sb_pdev, |
259 | rdc321x_wdt_device.base_reg, RDC_WDT_RST); | ||
233 | 260 | ||
234 | init_completion(&rdc321x_wdt_device.stop); | 261 | init_completion(&rdc321x_wdt_device.stop); |
235 | rdc321x_wdt_device.queue = 0; | 262 | rdc321x_wdt_device.queue = 0; |
@@ -240,7 +267,7 @@ static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) | |||
240 | 267 | ||
241 | rdc321x_wdt_device.default_ticks = ticks; | 268 | rdc321x_wdt_device.default_ticks = ticks; |
242 | 269 | ||
243 | printk(KERN_INFO PFX "watchdog init success\n"); | 270 | dev_info(&pdev->dev, "watchdog init success\n"); |
244 | 271 | ||
245 | return 0; | 272 | return 0; |
246 | } | 273 | } |
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index 5dceeddc8859..3faee1ae64bd 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c | |||
@@ -172,7 +172,7 @@ static struct miscdevice riowd_miscdev = { | |||
172 | .fops = &riowd_fops | 172 | .fops = &riowd_fops |
173 | }; | 173 | }; |
174 | 174 | ||
175 | static int __devinit riowd_probe(struct of_device *op, | 175 | static int __devinit riowd_probe(struct platform_device *op, |
176 | const struct of_device_id *match) | 176 | const struct of_device_id *match) |
177 | { | 177 | { |
178 | struct riowd *p; | 178 | struct riowd *p; |
@@ -219,7 +219,7 @@ out: | |||
219 | return err; | 219 | return err; |
220 | } | 220 | } |
221 | 221 | ||
222 | static int __devexit riowd_remove(struct of_device *op) | 222 | static int __devexit riowd_remove(struct platform_device *op) |
223 | { | 223 | { |
224 | struct riowd *p = dev_get_drvdata(&op->dev); | 224 | struct riowd *p = dev_get_drvdata(&op->dev); |
225 | 225 | ||
@@ -250,12 +250,12 @@ static struct of_platform_driver riowd_driver = { | |||
250 | 250 | ||
251 | static int __init riowd_init(void) | 251 | static int __init riowd_init(void) |
252 | { | 252 | { |
253 | return of_register_driver(&riowd_driver, &of_bus_type); | 253 | return of_register_platform_driver(&riowd_driver); |
254 | } | 254 | } |
255 | 255 | ||
256 | static void __exit riowd_exit(void) | 256 | static void __exit riowd_exit(void) |
257 | { | 257 | { |
258 | of_unregister_driver(&riowd_driver); | 258 | of_unregister_platform_driver(&riowd_driver); |
259 | } | 259 | } |
260 | 260 | ||
261 | module_init(riowd_init); | 261 | module_init(riowd_init); |
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index e4cebef55177..ae53662c29bc 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c | |||
@@ -63,7 +63,7 @@ module_param(nowayout, int, 0); | |||
63 | module_param(soft_noboot, int, 0); | 63 | module_param(soft_noboot, int, 0); |
64 | module_param(debug, int, 0); | 64 | module_param(debug, int, 0); |
65 | 65 | ||
66 | MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default=" | 66 | MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default=" |
67 | __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")"); | 67 | __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")"); |
68 | MODULE_PARM_DESC(tmr_atboot, | 68 | MODULE_PARM_DESC(tmr_atboot, |
69 | "Watchdog is started at boot time if set to 1, default=" | 69 | "Watchdog is started at boot time if set to 1, default=" |
@@ -71,8 +71,8 @@ MODULE_PARM_DESC(tmr_atboot, | |||
71 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | 71 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" |
72 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 72 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
73 | MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, " | 73 | MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, " |
74 | "0 to reboot (default depends on ONLY_TESTING)"); | 74 | "0 to reboot (default 0)"); |
75 | MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)"); | 75 | MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)"); |
76 | 76 | ||
77 | static unsigned long open_lock; | 77 | static unsigned long open_lock; |
78 | static struct device *wdt_dev; /* platform device attached to */ | 78 | static struct device *wdt_dev; /* platform device attached to */ |
@@ -426,8 +426,7 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) | |||
426 | wdt_mem = request_mem_region(res->start, size, pdev->name); | 426 | wdt_mem = request_mem_region(res->start, size, pdev->name); |
427 | if (wdt_mem == NULL) { | 427 | if (wdt_mem == NULL) { |
428 | dev_err(dev, "failed to get memory region\n"); | 428 | dev_err(dev, "failed to get memory region\n"); |
429 | ret = -ENOENT; | 429 | return -EBUSY; |
430 | goto err_req; | ||
431 | } | 430 | } |
432 | 431 | ||
433 | wdt_base = ioremap(res->start, size); | 432 | wdt_base = ioremap(res->start, size); |
@@ -533,21 +532,22 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) | |||
533 | 532 | ||
534 | static int __devexit s3c2410wdt_remove(struct platform_device *dev) | 533 | static int __devexit s3c2410wdt_remove(struct platform_device *dev) |
535 | { | 534 | { |
536 | s3c2410wdt_cpufreq_deregister(); | 535 | misc_deregister(&s3c2410wdt_miscdev); |
537 | |||
538 | release_resource(wdt_mem); | ||
539 | kfree(wdt_mem); | ||
540 | wdt_mem = NULL; | ||
541 | 536 | ||
542 | free_irq(wdt_irq->start, dev); | 537 | s3c2410wdt_cpufreq_deregister(); |
543 | wdt_irq = NULL; | ||
544 | 538 | ||
545 | clk_disable(wdt_clock); | 539 | clk_disable(wdt_clock); |
546 | clk_put(wdt_clock); | 540 | clk_put(wdt_clock); |
547 | wdt_clock = NULL; | 541 | wdt_clock = NULL; |
548 | 542 | ||
543 | free_irq(wdt_irq->start, dev); | ||
544 | wdt_irq = NULL; | ||
545 | |||
549 | iounmap(wdt_base); | 546 | iounmap(wdt_base); |
550 | misc_deregister(&s3c2410wdt_miscdev); | 547 | |
548 | release_resource(wdt_mem); | ||
549 | kfree(wdt_mem); | ||
550 | wdt_mem = NULL; | ||
551 | return 0; | 551 | return 0; |
552 | } | 552 | } |
553 | 553 | ||
diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c index 88c83aa57303..f31493e65b38 100644 --- a/drivers/watchdog/sb_wdog.c +++ b/drivers/watchdog/sb_wdog.c | |||
@@ -305,7 +305,7 @@ static int __init sbwdog_init(void) | |||
305 | if (ret) { | 305 | if (ret) { |
306 | printk(KERN_ERR "%s: failed to request irq 1 - %d\n", | 306 | printk(KERN_ERR "%s: failed to request irq 1 - %d\n", |
307 | ident.identity, ret); | 307 | ident.identity, ret); |
308 | return ret; | 308 | goto out; |
309 | } | 309 | } |
310 | 310 | ||
311 | ret = misc_register(&sbwdog_miscdev); | 311 | ret = misc_register(&sbwdog_miscdev); |
@@ -313,14 +313,20 @@ static int __init sbwdog_init(void) | |||
313 | printk(KERN_INFO "%s: timeout is %ld.%ld secs\n", | 313 | printk(KERN_INFO "%s: timeout is %ld.%ld secs\n", |
314 | ident.identity, | 314 | ident.identity, |
315 | timeout / 1000000, (timeout / 100000) % 10); | 315 | timeout / 1000000, (timeout / 100000) % 10); |
316 | } else | 316 | return 0; |
317 | free_irq(1, (void *)user_dog); | 317 | } |
318 | free_irq(1, (void *)user_dog); | ||
319 | out: | ||
320 | unregister_reboot_notifier(&sbwdog_notifier); | ||
321 | |||
318 | return ret; | 322 | return ret; |
319 | } | 323 | } |
320 | 324 | ||
321 | static void __exit sbwdog_exit(void) | 325 | static void __exit sbwdog_exit(void) |
322 | { | 326 | { |
323 | misc_deregister(&sbwdog_miscdev); | 327 | misc_deregister(&sbwdog_miscdev); |
328 | free_irq(1, (void *)user_dog); | ||
329 | unregister_reboot_notifier(&sbwdog_notifier); | ||
324 | } | 330 | } |
325 | 331 | ||
326 | module_init(sbwdog_init); | 332 | module_init(sbwdog_init); |
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c index 9c40f48804f5..0461858e07d0 100644 --- a/drivers/watchdog/sch311x_wdt.c +++ b/drivers/watchdog/sch311x_wdt.c | |||
@@ -425,6 +425,8 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev) | |||
425 | val = therm_trip ? 0x06 : 0x04; | 425 | val = therm_trip ? 0x06 : 0x04; |
426 | outb(val, sch311x_wdt_data.runtime_reg + RESGEN); | 426 | outb(val, sch311x_wdt_data.runtime_reg + RESGEN); |
427 | 427 | ||
428 | sch311x_wdt_miscdev.parent = dev; | ||
429 | |||
428 | err = misc_register(&sch311x_wdt_miscdev); | 430 | err = misc_register(&sch311x_wdt_miscdev); |
429 | if (err != 0) { | 431 | if (err != 0) { |
430 | dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n", | 432 | dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n", |
@@ -432,8 +434,6 @@ static int __devinit sch311x_wdt_probe(struct platform_device *pdev) | |||
432 | goto exit_release_region3; | 434 | goto exit_release_region3; |
433 | } | 435 | } |
434 | 436 | ||
435 | sch311x_wdt_miscdev.parent = dev; | ||
436 | |||
437 | dev_info(dev, | 437 | dev_info(dev, |
438 | "SMSC SCH311x WDT initialized. timeout=%d sec (nowayout=%d)\n", | 438 | "SMSC SCH311x WDT initialized. timeout=%d sec (nowayout=%d)\n", |
439 | timeout, nowayout); | 439 | timeout, nowayout); |
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index b7d2f8a0422b..4e3e7eb5919c 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c | |||
@@ -474,7 +474,7 @@ MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | |||
474 | module_param(clock_division_ratio, int, 0); | 474 | module_param(clock_division_ratio, int, 0); |
475 | MODULE_PARM_DESC(clock_division_ratio, | 475 | MODULE_PARM_DESC(clock_division_ratio, |
476 | "Clock division ratio. Valid ranges are from 0x5 (1.31ms) " | 476 | "Clock division ratio. Valid ranges are from 0x5 (1.31ms) " |
477 | "to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")"); | 477 | "to 0x7 (5.25ms). (default=" __MODULE_STRING(WTCSR_CKS_4096) ")"); |
478 | 478 | ||
479 | module_param(heartbeat, int, 0); | 479 | module_param(heartbeat, int, 0); |
480 | MODULE_PARM_DESC(heartbeat, | 480 | MODULE_PARM_DESC(heartbeat, |
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c new file mode 100644 index 000000000000..808372883e88 --- /dev/null +++ b/drivers/watchdog/sp5100_tco.c | |||
@@ -0,0 +1,480 @@ | |||
1 | /* | ||
2 | * sp5100_tco : TCO timer driver for sp5100 chipsets | ||
3 | * | ||
4 | * (c) Copyright 2009 Google Inc., All Rights Reserved. | ||
5 | * | ||
6 | * Based on i8xx_tco.c: | ||
7 | * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights | ||
8 | * Reserved. | ||
9 | * http://www.kernelconcepts.de | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version | ||
14 | * 2 of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide" | ||
17 | */ | ||
18 | |||
19 | /* | ||
20 | * Includes, defines, variables, module parameters, ... | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | #include <linux/types.h> | ||
26 | #include <linux/miscdevice.h> | ||
27 | #include <linux/watchdog.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/fs.h> | ||
30 | #include <linux/pci.h> | ||
31 | #include <linux/ioport.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/uaccess.h> | ||
34 | #include <linux/io.h> | ||
35 | |||
36 | #include "sp5100_tco.h" | ||
37 | |||
38 | /* Module and version information */ | ||
39 | #define TCO_VERSION "0.01" | ||
40 | #define TCO_MODULE_NAME "SP5100 TCO timer" | ||
41 | #define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION | ||
42 | #define PFX TCO_MODULE_NAME ": " | ||
43 | |||
44 | /* internal variables */ | ||
45 | static void __iomem *tcobase; | ||
46 | static unsigned int pm_iobase; | ||
47 | static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ | ||
48 | static unsigned long timer_alive; | ||
49 | static char tco_expect_close; | ||
50 | static struct pci_dev *sp5100_tco_pci; | ||
51 | |||
52 | /* the watchdog platform device */ | ||
53 | static struct platform_device *sp5100_tco_platform_device; | ||
54 | |||
55 | /* module parameters */ | ||
56 | |||
57 | #define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat. */ | ||
58 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ | ||
59 | module_param(heartbeat, int, 0); | ||
60 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default=" | ||
61 | __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); | ||
62 | |||
63 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
64 | module_param(nowayout, int, 0); | ||
65 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started" | ||
66 | " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
67 | |||
68 | /* | ||
69 | * Some TCO specific functions | ||
70 | */ | ||
71 | static void tco_timer_start(void) | ||
72 | { | ||
73 | u32 val; | ||
74 | unsigned long flags; | ||
75 | |||
76 | spin_lock_irqsave(&tco_lock, flags); | ||
77 | val = readl(SP5100_WDT_CONTROL(tcobase)); | ||
78 | val |= SP5100_WDT_START_STOP_BIT; | ||
79 | writel(val, SP5100_WDT_CONTROL(tcobase)); | ||
80 | spin_unlock_irqrestore(&tco_lock, flags); | ||
81 | } | ||
82 | |||
83 | static void tco_timer_stop(void) | ||
84 | { | ||
85 | u32 val; | ||
86 | unsigned long flags; | ||
87 | |||
88 | spin_lock_irqsave(&tco_lock, flags); | ||
89 | val = readl(SP5100_WDT_CONTROL(tcobase)); | ||
90 | val &= ~SP5100_WDT_START_STOP_BIT; | ||
91 | writel(val, SP5100_WDT_CONTROL(tcobase)); | ||
92 | spin_unlock_irqrestore(&tco_lock, flags); | ||
93 | } | ||
94 | |||
95 | static void tco_timer_keepalive(void) | ||
96 | { | ||
97 | u32 val; | ||
98 | unsigned long flags; | ||
99 | |||
100 | spin_lock_irqsave(&tco_lock, flags); | ||
101 | val = readl(SP5100_WDT_CONTROL(tcobase)); | ||
102 | val |= SP5100_WDT_TRIGGER_BIT; | ||
103 | writel(val, SP5100_WDT_CONTROL(tcobase)); | ||
104 | spin_unlock_irqrestore(&tco_lock, flags); | ||
105 | } | ||
106 | |||
107 | static int tco_timer_set_heartbeat(int t) | ||
108 | { | ||
109 | unsigned long flags; | ||
110 | |||
111 | if (t < 0 || t > 0xffff) | ||
112 | return -EINVAL; | ||
113 | |||
114 | /* Write new heartbeat to watchdog */ | ||
115 | spin_lock_irqsave(&tco_lock, flags); | ||
116 | writel(t, SP5100_WDT_COUNT(tcobase)); | ||
117 | spin_unlock_irqrestore(&tco_lock, flags); | ||
118 | |||
119 | heartbeat = t; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * /dev/watchdog handling | ||
125 | */ | ||
126 | |||
127 | static int sp5100_tco_open(struct inode *inode, struct file *file) | ||
128 | { | ||
129 | /* /dev/watchdog can only be opened once */ | ||
130 | if (test_and_set_bit(0, &timer_alive)) | ||
131 | return -EBUSY; | ||
132 | |||
133 | /* Reload and activate timer */ | ||
134 | tco_timer_start(); | ||
135 | tco_timer_keepalive(); | ||
136 | return nonseekable_open(inode, file); | ||
137 | } | ||
138 | |||
139 | static int sp5100_tco_release(struct inode *inode, struct file *file) | ||
140 | { | ||
141 | /* Shut off the timer. */ | ||
142 | if (tco_expect_close == 42) { | ||
143 | tco_timer_stop(); | ||
144 | } else { | ||
145 | printk(KERN_CRIT PFX | ||
146 | "Unexpected close, not stopping watchdog!\n"); | ||
147 | tco_timer_keepalive(); | ||
148 | } | ||
149 | clear_bit(0, &timer_alive); | ||
150 | tco_expect_close = 0; | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static ssize_t sp5100_tco_write(struct file *file, const char __user *data, | ||
155 | size_t len, loff_t *ppos) | ||
156 | { | ||
157 | /* See if we got the magic character 'V' and reload the timer */ | ||
158 | if (len) { | ||
159 | if (!nowayout) { | ||
160 | size_t i; | ||
161 | |||
162 | /* note: just in case someone wrote the magic character | ||
163 | * five months ago... */ | ||
164 | tco_expect_close = 0; | ||
165 | |||
166 | /* scan to see whether or not we got the magic character | ||
167 | */ | ||
168 | for (i = 0; i != len; i++) { | ||
169 | char c; | ||
170 | if (get_user(c, data + i)) | ||
171 | return -EFAULT; | ||
172 | if (c == 'V') | ||
173 | tco_expect_close = 42; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | /* someone wrote to us, we should reload the timer */ | ||
178 | tco_timer_keepalive(); | ||
179 | } | ||
180 | return len; | ||
181 | } | ||
182 | |||
183 | static long sp5100_tco_ioctl(struct file *file, unsigned int cmd, | ||
184 | unsigned long arg) | ||
185 | { | ||
186 | int new_options, retval = -EINVAL; | ||
187 | int new_heartbeat; | ||
188 | void __user *argp = (void __user *)arg; | ||
189 | int __user *p = argp; | ||
190 | static const struct watchdog_info ident = { | ||
191 | .options = WDIOF_SETTIMEOUT | | ||
192 | WDIOF_KEEPALIVEPING | | ||
193 | WDIOF_MAGICCLOSE, | ||
194 | .firmware_version = 0, | ||
195 | .identity = TCO_MODULE_NAME, | ||
196 | }; | ||
197 | |||
198 | switch (cmd) { | ||
199 | case WDIOC_GETSUPPORT: | ||
200 | return copy_to_user(argp, &ident, | ||
201 | sizeof(ident)) ? -EFAULT : 0; | ||
202 | case WDIOC_GETSTATUS: | ||
203 | case WDIOC_GETBOOTSTATUS: | ||
204 | return put_user(0, p); | ||
205 | case WDIOC_SETOPTIONS: | ||
206 | if (get_user(new_options, p)) | ||
207 | return -EFAULT; | ||
208 | if (new_options & WDIOS_DISABLECARD) { | ||
209 | tco_timer_stop(); | ||
210 | retval = 0; | ||
211 | } | ||
212 | if (new_options & WDIOS_ENABLECARD) { | ||
213 | tco_timer_start(); | ||
214 | tco_timer_keepalive(); | ||
215 | retval = 0; | ||
216 | } | ||
217 | return retval; | ||
218 | case WDIOC_KEEPALIVE: | ||
219 | tco_timer_keepalive(); | ||
220 | return 0; | ||
221 | case WDIOC_SETTIMEOUT: | ||
222 | if (get_user(new_heartbeat, p)) | ||
223 | return -EFAULT; | ||
224 | if (tco_timer_set_heartbeat(new_heartbeat)) | ||
225 | return -EINVAL; | ||
226 | tco_timer_keepalive(); | ||
227 | /* Fall through */ | ||
228 | case WDIOC_GETTIMEOUT: | ||
229 | return put_user(heartbeat, p); | ||
230 | default: | ||
231 | return -ENOTTY; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * Kernel Interfaces | ||
237 | */ | ||
238 | |||
239 | static const struct file_operations sp5100_tco_fops = { | ||
240 | .owner = THIS_MODULE, | ||
241 | .llseek = no_llseek, | ||
242 | .write = sp5100_tco_write, | ||
243 | .unlocked_ioctl = sp5100_tco_ioctl, | ||
244 | .open = sp5100_tco_open, | ||
245 | .release = sp5100_tco_release, | ||
246 | }; | ||
247 | |||
248 | static struct miscdevice sp5100_tco_miscdev = { | ||
249 | .minor = WATCHDOG_MINOR, | ||
250 | .name = "watchdog", | ||
251 | .fops = &sp5100_tco_fops, | ||
252 | }; | ||
253 | |||
254 | /* | ||
255 | * Data for PCI driver interface | ||
256 | * | ||
257 | * This data only exists for exporting the supported | ||
258 | * PCI ids via MODULE_DEVICE_TABLE. We do not actually | ||
259 | * register a pci_driver, because someone else might | ||
260 | * want to register another driver on the same PCI id. | ||
261 | */ | ||
262 | static struct pci_device_id sp5100_tco_pci_tbl[] = { | ||
263 | { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, | ||
264 | PCI_ANY_ID, }, | ||
265 | { 0, }, /* End of list */ | ||
266 | }; | ||
267 | MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); | ||
268 | |||
269 | /* | ||
270 | * Init & exit routines | ||
271 | */ | ||
272 | |||
273 | static unsigned char __devinit sp5100_tco_setupdevice(void) | ||
274 | { | ||
275 | struct pci_dev *dev = NULL; | ||
276 | u32 val; | ||
277 | |||
278 | /* Match the PCI device */ | ||
279 | for_each_pci_dev(dev) { | ||
280 | if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) { | ||
281 | sp5100_tco_pci = dev; | ||
282 | break; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | if (!sp5100_tco_pci) | ||
287 | return 0; | ||
288 | |||
289 | /* Request the IO ports used by this driver */ | ||
290 | pm_iobase = SP5100_IO_PM_INDEX_REG; | ||
291 | if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, "SP5100 TCO")) { | ||
292 | printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", | ||
293 | pm_iobase); | ||
294 | goto exit; | ||
295 | } | ||
296 | |||
297 | /* Find the watchdog base address. */ | ||
298 | outb(SP5100_PM_WATCHDOG_BASE3, SP5100_IO_PM_INDEX_REG); | ||
299 | val = inb(SP5100_IO_PM_DATA_REG); | ||
300 | outb(SP5100_PM_WATCHDOG_BASE2, SP5100_IO_PM_INDEX_REG); | ||
301 | val = val << 8 | inb(SP5100_IO_PM_DATA_REG); | ||
302 | outb(SP5100_PM_WATCHDOG_BASE1, SP5100_IO_PM_INDEX_REG); | ||
303 | val = val << 8 | inb(SP5100_IO_PM_DATA_REG); | ||
304 | outb(SP5100_PM_WATCHDOG_BASE0, SP5100_IO_PM_INDEX_REG); | ||
305 | /* Low three bits of BASE0 are reserved. */ | ||
306 | val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8); | ||
307 | |||
308 | tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); | ||
309 | if (tcobase == 0) { | ||
310 | printk(KERN_ERR PFX "failed to get tcobase address\n"); | ||
311 | goto unreg_region; | ||
312 | } | ||
313 | |||
314 | /* Enable watchdog decode bit */ | ||
315 | pci_read_config_dword(sp5100_tco_pci, | ||
316 | SP5100_PCI_WATCHDOG_MISC_REG, | ||
317 | &val); | ||
318 | |||
319 | val |= SP5100_PCI_WATCHDOG_DECODE_EN; | ||
320 | |||
321 | pci_write_config_dword(sp5100_tco_pci, | ||
322 | SP5100_PCI_WATCHDOG_MISC_REG, | ||
323 | val); | ||
324 | |||
325 | /* Enable Watchdog timer and set the resolution to 1 sec. */ | ||
326 | outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG); | ||
327 | val = inb(SP5100_IO_PM_DATA_REG); | ||
328 | val |= SP5100_PM_WATCHDOG_SECOND_RES; | ||
329 | val &= ~SP5100_PM_WATCHDOG_DISABLE; | ||
330 | outb(val, SP5100_IO_PM_DATA_REG); | ||
331 | |||
332 | /* Check that the watchdog action is set to reset the system. */ | ||
333 | val = readl(SP5100_WDT_CONTROL(tcobase)); | ||
334 | val &= ~SP5100_PM_WATCHDOG_ACTION_RESET; | ||
335 | writel(val, SP5100_WDT_CONTROL(tcobase)); | ||
336 | |||
337 | /* Set a reasonable heartbeat before we stop the timer */ | ||
338 | tco_timer_set_heartbeat(heartbeat); | ||
339 | |||
340 | /* | ||
341 | * Stop the TCO before we change anything so we don't race with | ||
342 | * a zeroed timer. | ||
343 | */ | ||
344 | tco_timer_stop(); | ||
345 | |||
346 | /* Done */ | ||
347 | return 1; | ||
348 | |||
349 | iounmap(tcobase); | ||
350 | unreg_region: | ||
351 | release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); | ||
352 | exit: | ||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static int __devinit sp5100_tco_init(struct platform_device *dev) | ||
357 | { | ||
358 | int ret; | ||
359 | u32 val; | ||
360 | |||
361 | /* Check whether or not the hardware watchdog is there. If found, then | ||
362 | * set it up. | ||
363 | */ | ||
364 | if (!sp5100_tco_setupdevice()) | ||
365 | return -ENODEV; | ||
366 | |||
367 | /* Check to see if last reboot was due to watchdog timeout */ | ||
368 | printk(KERN_INFO PFX "Watchdog reboot %sdetected.\n", | ||
369 | readl(SP5100_WDT_CONTROL(tcobase)) & SP5100_PM_WATCHDOG_FIRED ? | ||
370 | "" : "not "); | ||
371 | |||
372 | /* Clear out the old status */ | ||
373 | val = readl(SP5100_WDT_CONTROL(tcobase)); | ||
374 | val &= ~SP5100_PM_WATCHDOG_FIRED; | ||
375 | writel(val, SP5100_WDT_CONTROL(tcobase)); | ||
376 | |||
377 | /* | ||
378 | * Check that the heartbeat value is within it's range. | ||
379 | * If not, reset to the default. | ||
380 | */ | ||
381 | if (tco_timer_set_heartbeat(heartbeat)) { | ||
382 | heartbeat = WATCHDOG_HEARTBEAT; | ||
383 | tco_timer_set_heartbeat(heartbeat); | ||
384 | } | ||
385 | |||
386 | ret = misc_register(&sp5100_tco_miscdev); | ||
387 | if (ret != 0) { | ||
388 | printk(KERN_ERR PFX "cannot register miscdev on minor=" | ||
389 | "%d (err=%d)\n", | ||
390 | WATCHDOG_MINOR, ret); | ||
391 | goto exit; | ||
392 | } | ||
393 | |||
394 | clear_bit(0, &timer_alive); | ||
395 | |||
396 | printk(KERN_INFO PFX "initialized (0x%p). heartbeat=%d sec" | ||
397 | " (nowayout=%d)\n", | ||
398 | tcobase, heartbeat, nowayout); | ||
399 | |||
400 | return 0; | ||
401 | |||
402 | exit: | ||
403 | iounmap(tcobase); | ||
404 | release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); | ||
405 | return ret; | ||
406 | } | ||
407 | |||
408 | static void __devexit sp5100_tco_cleanup(void) | ||
409 | { | ||
410 | /* Stop the timer before we leave */ | ||
411 | if (!nowayout) | ||
412 | tco_timer_stop(); | ||
413 | |||
414 | /* Deregister */ | ||
415 | misc_deregister(&sp5100_tco_miscdev); | ||
416 | iounmap(tcobase); | ||
417 | release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); | ||
418 | } | ||
419 | |||
420 | static int __devexit sp5100_tco_remove(struct platform_device *dev) | ||
421 | { | ||
422 | if (tcobase) | ||
423 | sp5100_tco_cleanup(); | ||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static void sp5100_tco_shutdown(struct platform_device *dev) | ||
428 | { | ||
429 | tco_timer_stop(); | ||
430 | } | ||
431 | |||
432 | static struct platform_driver sp5100_tco_driver = { | ||
433 | .probe = sp5100_tco_init, | ||
434 | .remove = __devexit_p(sp5100_tco_remove), | ||
435 | .shutdown = sp5100_tco_shutdown, | ||
436 | .driver = { | ||
437 | .owner = THIS_MODULE, | ||
438 | .name = TCO_MODULE_NAME, | ||
439 | }, | ||
440 | }; | ||
441 | |||
442 | static int __init sp5100_tco_init_module(void) | ||
443 | { | ||
444 | int err; | ||
445 | |||
446 | printk(KERN_INFO PFX "SP5100 TCO WatchDog Timer Driver v%s\n", | ||
447 | TCO_VERSION); | ||
448 | |||
449 | err = platform_driver_register(&sp5100_tco_driver); | ||
450 | if (err) | ||
451 | return err; | ||
452 | |||
453 | sp5100_tco_platform_device = platform_device_register_simple( | ||
454 | TCO_MODULE_NAME, -1, NULL, 0); | ||
455 | if (IS_ERR(sp5100_tco_platform_device)) { | ||
456 | err = PTR_ERR(sp5100_tco_platform_device); | ||
457 | goto unreg_platform_driver; | ||
458 | } | ||
459 | |||
460 | return 0; | ||
461 | |||
462 | unreg_platform_driver: | ||
463 | platform_driver_unregister(&sp5100_tco_driver); | ||
464 | return err; | ||
465 | } | ||
466 | |||
467 | static void __exit sp5100_tco_cleanup_module(void) | ||
468 | { | ||
469 | platform_device_unregister(sp5100_tco_platform_device); | ||
470 | platform_driver_unregister(&sp5100_tco_driver); | ||
471 | printk(KERN_INFO PFX "SP5100 TCO Watchdog Module Unloaded.\n"); | ||
472 | } | ||
473 | |||
474 | module_init(sp5100_tco_init_module); | ||
475 | module_exit(sp5100_tco_cleanup_module); | ||
476 | |||
477 | MODULE_AUTHOR("Priyanka Gupta"); | ||
478 | MODULE_DESCRIPTION("TCO timer driver for SP5100 chipset"); | ||
479 | MODULE_LICENSE("GPL"); | ||
480 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h new file mode 100644 index 000000000000..a5a16cc90a34 --- /dev/null +++ b/drivers/watchdog/sp5100_tco.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * sp5100_tco: TCO timer driver for sp5100 chipsets. | ||
3 | * | ||
4 | * (c) Copyright 2009 Google Inc., All Rights Reserved. | ||
5 | * | ||
6 | * TCO timer driver for sp5100 chipsets | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * Some address definitions for the Watchdog | ||
11 | */ | ||
12 | |||
13 | #define SP5100_WDT_MEM_MAP_SIZE 0x08 | ||
14 | #define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */ | ||
15 | #define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */ | ||
16 | |||
17 | #define SP5100_WDT_START_STOP_BIT 1 | ||
18 | #define SP5100_WDT_TRIGGER_BIT (1 << 7) | ||
19 | |||
20 | #define SP5100_PCI_WATCHDOG_MISC_REG 0x41 | ||
21 | #define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3) | ||
22 | |||
23 | #define SP5100_PM_IOPORTS_SIZE 0x02 | ||
24 | |||
25 | /* These two IO registers are hardcoded and there doesn't seem to be a way to | ||
26 | * read them from a register. | ||
27 | */ | ||
28 | #define SP5100_IO_PM_INDEX_REG 0xCD6 | ||
29 | #define SP5100_IO_PM_DATA_REG 0xCD7 | ||
30 | |||
31 | #define SP5100_PM_WATCHDOG_CONTROL 0x69 | ||
32 | #define SP5100_PM_WATCHDOG_BASE0 0x6C | ||
33 | #define SP5100_PM_WATCHDOG_BASE1 0x6D | ||
34 | #define SP5100_PM_WATCHDOG_BASE2 0x6E | ||
35 | #define SP5100_PM_WATCHDOG_BASE3 0x6F | ||
36 | |||
37 | #define SP5100_PM_WATCHDOG_FIRED (1 << 1) | ||
38 | #define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2) | ||
39 | |||
40 | #define SP5100_PM_WATCHDOG_DISABLE 1 | ||
41 | #define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1) | ||
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c new file mode 100644 index 000000000000..9127eda2145b --- /dev/null +++ b/drivers/watchdog/sp805_wdt.c | |||
@@ -0,0 +1,387 @@ | |||
1 | /* | ||
2 | * drivers/char/watchdog/sp805-wdt.c | ||
3 | * | ||
4 | * Watchdog driver for ARM SP805 watchdog module | ||
5 | * | ||
6 | * Copyright (C) 2010 ST Microelectronics | ||
7 | * Viresh Kumar<viresh.kumar@st.com> | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public | ||
10 | * License version 2 or later. This program is licensed "as is" without any | ||
11 | * warranty of any kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/device.h> | ||
15 | #include <linux/resource.h> | ||
16 | #include <linux/amba/bus.h> | ||
17 | #include <linux/bitops.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/ioport.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/math64.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/spinlock.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/uaccess.h> | ||
32 | #include <linux/watchdog.h> | ||
33 | |||
34 | /* default timeout in seconds */ | ||
35 | #define DEFAULT_TIMEOUT 60 | ||
36 | |||
37 | #define MODULE_NAME "sp805-wdt" | ||
38 | |||
39 | /* watchdog register offsets and masks */ | ||
40 | #define WDTLOAD 0x000 | ||
41 | #define LOAD_MIN 0x00000001 | ||
42 | #define LOAD_MAX 0xFFFFFFFF | ||
43 | #define WDTVALUE 0x004 | ||
44 | #define WDTCONTROL 0x008 | ||
45 | /* control register masks */ | ||
46 | #define INT_ENABLE (1 << 0) | ||
47 | #define RESET_ENABLE (1 << 1) | ||
48 | #define WDTINTCLR 0x00C | ||
49 | #define WDTRIS 0x010 | ||
50 | #define WDTMIS 0x014 | ||
51 | #define INT_MASK (1 << 0) | ||
52 | #define WDTLOCK 0xC00 | ||
53 | #define UNLOCK 0x1ACCE551 | ||
54 | #define LOCK 0x00000001 | ||
55 | |||
56 | /** | ||
57 | * struct sp805_wdt: sp805 wdt device structure | ||
58 | * | ||
59 | * lock: spin lock protecting dev structure and io access | ||
60 | * base: base address of wdt | ||
61 | * clk: clock structure of wdt | ||
62 | * dev: amba device structure of wdt | ||
63 | * status: current status of wdt | ||
64 | * load_val: load value to be set for current timeout | ||
65 | * timeout: current programmed timeout | ||
66 | */ | ||
67 | struct sp805_wdt { | ||
68 | spinlock_t lock; | ||
69 | void __iomem *base; | ||
70 | struct clk *clk; | ||
71 | struct amba_device *adev; | ||
72 | unsigned long status; | ||
73 | #define WDT_BUSY 0 | ||
74 | #define WDT_CAN_BE_CLOSED 1 | ||
75 | unsigned int load_val; | ||
76 | unsigned int timeout; | ||
77 | }; | ||
78 | |||
79 | /* local variables */ | ||
80 | static struct sp805_wdt *wdt; | ||
81 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
82 | |||
83 | /* This routine finds load value that will reset system in required timout */ | ||
84 | static void wdt_setload(unsigned int timeout) | ||
85 | { | ||
86 | u64 load, rate; | ||
87 | |||
88 | rate = clk_get_rate(wdt->clk); | ||
89 | |||
90 | /* | ||
91 | * sp805 runs counter with given value twice, after the end of first | ||
92 | * counter it gives an interrupt and then starts counter again. If | ||
93 | * interrupt already occured then it resets the system. This is why | ||
94 | * load is half of what should be required. | ||
95 | */ | ||
96 | load = div_u64(rate, 2) * timeout - 1; | ||
97 | |||
98 | load = (load > LOAD_MAX) ? LOAD_MAX : load; | ||
99 | load = (load < LOAD_MIN) ? LOAD_MIN : load; | ||
100 | |||
101 | spin_lock(&wdt->lock); | ||
102 | wdt->load_val = load; | ||
103 | /* roundup timeout to closest positive integer value */ | ||
104 | wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate); | ||
105 | spin_unlock(&wdt->lock); | ||
106 | } | ||
107 | |||
108 | /* returns number of seconds left for reset to occur */ | ||
109 | static u32 wdt_timeleft(void) | ||
110 | { | ||
111 | u64 load, rate; | ||
112 | |||
113 | rate = clk_get_rate(wdt->clk); | ||
114 | |||
115 | spin_lock(&wdt->lock); | ||
116 | load = readl(wdt->base + WDTVALUE); | ||
117 | |||
118 | /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */ | ||
119 | if (!(readl(wdt->base + WDTRIS) & INT_MASK)) | ||
120 | load += wdt->load_val + 1; | ||
121 | spin_unlock(&wdt->lock); | ||
122 | |||
123 | return div_u64(load, rate); | ||
124 | } | ||
125 | |||
126 | /* enables watchdog timers reset */ | ||
127 | static void wdt_enable(void) | ||
128 | { | ||
129 | spin_lock(&wdt->lock); | ||
130 | |||
131 | writel(UNLOCK, wdt->base + WDTLOCK); | ||
132 | writel(wdt->load_val, wdt->base + WDTLOAD); | ||
133 | writel(INT_MASK, wdt->base + WDTINTCLR); | ||
134 | writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); | ||
135 | writel(LOCK, wdt->base + WDTLOCK); | ||
136 | |||
137 | spin_unlock(&wdt->lock); | ||
138 | } | ||
139 | |||
140 | /* disables watchdog timers reset */ | ||
141 | static void wdt_disable(void) | ||
142 | { | ||
143 | spin_lock(&wdt->lock); | ||
144 | |||
145 | writel(UNLOCK, wdt->base + WDTLOCK); | ||
146 | writel(0, wdt->base + WDTCONTROL); | ||
147 | writel(0, wdt->base + WDTLOAD); | ||
148 | writel(LOCK, wdt->base + WDTLOCK); | ||
149 | |||
150 | spin_unlock(&wdt->lock); | ||
151 | } | ||
152 | |||
153 | static ssize_t sp805_wdt_write(struct file *file, const char *data, | ||
154 | size_t len, loff_t *ppos) | ||
155 | { | ||
156 | if (len) { | ||
157 | if (!nowayout) { | ||
158 | size_t i; | ||
159 | |||
160 | clear_bit(WDT_CAN_BE_CLOSED, &wdt->status); | ||
161 | |||
162 | for (i = 0; i != len; i++) { | ||
163 | char c; | ||
164 | |||
165 | if (get_user(c, data + i)) | ||
166 | return -EFAULT; | ||
167 | /* Check for Magic Close character */ | ||
168 | if (c == 'V') { | ||
169 | set_bit(WDT_CAN_BE_CLOSED, | ||
170 | &wdt->status); | ||
171 | break; | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | wdt_enable(); | ||
176 | } | ||
177 | return len; | ||
178 | } | ||
179 | |||
180 | static const struct watchdog_info ident = { | ||
181 | .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
182 | .identity = MODULE_NAME, | ||
183 | }; | ||
184 | |||
185 | static long sp805_wdt_ioctl(struct file *file, unsigned int cmd, | ||
186 | unsigned long arg) | ||
187 | { | ||
188 | int ret = -ENOTTY; | ||
189 | unsigned int timeout; | ||
190 | |||
191 | switch (cmd) { | ||
192 | case WDIOC_GETSUPPORT: | ||
193 | ret = copy_to_user((struct watchdog_info *)arg, &ident, | ||
194 | sizeof(ident)) ? -EFAULT : 0; | ||
195 | break; | ||
196 | |||
197 | case WDIOC_GETSTATUS: | ||
198 | ret = put_user(0, (int *)arg); | ||
199 | break; | ||
200 | |||
201 | case WDIOC_KEEPALIVE: | ||
202 | wdt_enable(); | ||
203 | ret = 0; | ||
204 | break; | ||
205 | |||
206 | case WDIOC_SETTIMEOUT: | ||
207 | ret = get_user(timeout, (unsigned int *)arg); | ||
208 | if (ret) | ||
209 | break; | ||
210 | |||
211 | wdt_setload(timeout); | ||
212 | |||
213 | wdt_enable(); | ||
214 | /* Fall through */ | ||
215 | |||
216 | case WDIOC_GETTIMEOUT: | ||
217 | ret = put_user(wdt->timeout, (unsigned int *)arg); | ||
218 | break; | ||
219 | case WDIOC_GETTIMELEFT: | ||
220 | ret = put_user(wdt_timeleft(), (unsigned int *)arg); | ||
221 | break; | ||
222 | } | ||
223 | return ret; | ||
224 | } | ||
225 | |||
226 | static int sp805_wdt_open(struct inode *inode, struct file *file) | ||
227 | { | ||
228 | int ret = 0; | ||
229 | |||
230 | if (test_and_set_bit(WDT_BUSY, &wdt->status)) | ||
231 | return -EBUSY; | ||
232 | |||
233 | ret = clk_enable(wdt->clk); | ||
234 | if (ret) { | ||
235 | dev_err(&wdt->adev->dev, "clock enable fail"); | ||
236 | goto err; | ||
237 | } | ||
238 | |||
239 | wdt_enable(); | ||
240 | |||
241 | /* can not be closed, once enabled */ | ||
242 | clear_bit(WDT_CAN_BE_CLOSED, &wdt->status); | ||
243 | return nonseekable_open(inode, file); | ||
244 | |||
245 | err: | ||
246 | clear_bit(WDT_BUSY, &wdt->status); | ||
247 | return ret; | ||
248 | } | ||
249 | |||
250 | static int sp805_wdt_release(struct inode *inode, struct file *file) | ||
251 | { | ||
252 | if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) { | ||
253 | clear_bit(WDT_BUSY, &wdt->status); | ||
254 | dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n"); | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | wdt_disable(); | ||
259 | clk_disable(wdt->clk); | ||
260 | clear_bit(WDT_BUSY, &wdt->status); | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | static const struct file_operations sp805_wdt_fops = { | ||
266 | .owner = THIS_MODULE, | ||
267 | .llseek = no_llseek, | ||
268 | .write = sp805_wdt_write, | ||
269 | .unlocked_ioctl = sp805_wdt_ioctl, | ||
270 | .open = sp805_wdt_open, | ||
271 | .release = sp805_wdt_release, | ||
272 | }; | ||
273 | |||
274 | static struct miscdevice sp805_wdt_miscdev = { | ||
275 | .minor = WATCHDOG_MINOR, | ||
276 | .name = "watchdog", | ||
277 | .fops = &sp805_wdt_fops, | ||
278 | }; | ||
279 | |||
280 | static int __devinit | ||
281 | sp805_wdt_probe(struct amba_device *adev, struct amba_id *id) | ||
282 | { | ||
283 | int ret = 0; | ||
284 | |||
285 | if (!request_mem_region(adev->res.start, resource_size(&adev->res), | ||
286 | "sp805_wdt")) { | ||
287 | dev_warn(&adev->dev, "Failed to get memory region resource\n"); | ||
288 | ret = -ENOENT; | ||
289 | goto err; | ||
290 | } | ||
291 | |||
292 | wdt = kzalloc(sizeof(*wdt), GFP_KERNEL); | ||
293 | if (!wdt) { | ||
294 | dev_warn(&adev->dev, "Kzalloc failed\n"); | ||
295 | ret = -ENOMEM; | ||
296 | goto err_kzalloc; | ||
297 | } | ||
298 | |||
299 | wdt->clk = clk_get(&adev->dev, NULL); | ||
300 | if (IS_ERR(wdt->clk)) { | ||
301 | dev_warn(&adev->dev, "Clock not found\n"); | ||
302 | ret = PTR_ERR(wdt->clk); | ||
303 | goto err_clk_get; | ||
304 | } | ||
305 | |||
306 | wdt->base = ioremap(adev->res.start, resource_size(&adev->res)); | ||
307 | if (!wdt->base) { | ||
308 | ret = -ENOMEM; | ||
309 | dev_warn(&adev->dev, "ioremap fail\n"); | ||
310 | goto err_ioremap; | ||
311 | } | ||
312 | |||
313 | wdt->adev = adev; | ||
314 | spin_lock_init(&wdt->lock); | ||
315 | wdt_setload(DEFAULT_TIMEOUT); | ||
316 | |||
317 | ret = misc_register(&sp805_wdt_miscdev); | ||
318 | if (ret < 0) { | ||
319 | dev_warn(&adev->dev, "cannot register misc device\n"); | ||
320 | goto err_misc_register; | ||
321 | } | ||
322 | |||
323 | dev_info(&adev->dev, "registration successful\n"); | ||
324 | return 0; | ||
325 | |||
326 | err_misc_register: | ||
327 | iounmap(wdt->base); | ||
328 | err_ioremap: | ||
329 | clk_put(wdt->clk); | ||
330 | err_clk_get: | ||
331 | kfree(wdt); | ||
332 | wdt = NULL; | ||
333 | err_kzalloc: | ||
334 | release_mem_region(adev->res.start, resource_size(&adev->res)); | ||
335 | err: | ||
336 | dev_err(&adev->dev, "Probe Failed!!!\n"); | ||
337 | return ret; | ||
338 | } | ||
339 | |||
340 | static int __devexit sp805_wdt_remove(struct amba_device *adev) | ||
341 | { | ||
342 | misc_deregister(&sp805_wdt_miscdev); | ||
343 | iounmap(wdt->base); | ||
344 | clk_put(wdt->clk); | ||
345 | kfree(wdt); | ||
346 | release_mem_region(adev->res.start, resource_size(&adev->res)); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static struct amba_id sp805_wdt_ids[] __initdata = { | ||
352 | { | ||
353 | .id = 0x00141805, | ||
354 | .mask = 0x00ffffff, | ||
355 | }, | ||
356 | { 0, 0 }, | ||
357 | }; | ||
358 | |||
359 | static struct amba_driver sp805_wdt_driver = { | ||
360 | .drv = { | ||
361 | .name = MODULE_NAME, | ||
362 | }, | ||
363 | .id_table = sp805_wdt_ids, | ||
364 | .probe = sp805_wdt_probe, | ||
365 | .remove = __devexit_p(sp805_wdt_remove), | ||
366 | }; | ||
367 | |||
368 | static int __init sp805_wdt_init(void) | ||
369 | { | ||
370 | return amba_driver_register(&sp805_wdt_driver); | ||
371 | } | ||
372 | module_init(sp805_wdt_init); | ||
373 | |||
374 | static void __exit sp805_wdt_exit(void) | ||
375 | { | ||
376 | amba_driver_unregister(&sp805_wdt_driver); | ||
377 | } | ||
378 | module_exit(sp805_wdt_exit); | ||
379 | |||
380 | module_param(nowayout, int, 0); | ||
381 | MODULE_PARM_DESC(nowayout, | ||
382 | "Set to 1 to keep watchdog running after device release"); | ||
383 | |||
384 | MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>"); | ||
385 | MODULE_DESCRIPTION("ARM SP805 Watchdog Driver"); | ||
386 | MODULE_LICENSE("GPL"); | ||
387 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index 458c499c1223..18cdeb4c4258 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c | |||
@@ -449,6 +449,9 @@ static __devinit int ts72xx_wdt_probe(struct platform_device *pdev) | |||
449 | wdt->pdev = pdev; | 449 | wdt->pdev = pdev; |
450 | mutex_init(&wdt->lock); | 450 | mutex_init(&wdt->lock); |
451 | 451 | ||
452 | /* make sure that the watchdog is disabled */ | ||
453 | ts72xx_wdt_stop(wdt); | ||
454 | |||
452 | error = misc_register(&ts72xx_wdt_miscdev); | 455 | error = misc_register(&ts72xx_wdt_miscdev); |
453 | if (error) { | 456 | if (error) { |
454 | dev_err(&pdev->dev, "failed to register miscdev\n"); | 457 | dev_err(&pdev->dev, "failed to register miscdev\n"); |
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c index dcabe77ad141..b5045ca7e61c 100644 --- a/drivers/watchdog/twl4030_wdt.c +++ b/drivers/watchdog/twl4030_wdt.c | |||
@@ -190,6 +190,8 @@ static int __devinit twl4030_wdt_probe(struct platform_device *pdev) | |||
190 | 190 | ||
191 | twl4030_wdt_dev = pdev; | 191 | twl4030_wdt_dev = pdev; |
192 | 192 | ||
193 | twl4030_wdt_disable(wdt); | ||
194 | |||
193 | ret = misc_register(&wdt->miscdev); | 195 | ret = misc_register(&wdt->miscdev); |
194 | if (ret) { | 196 | if (ret) { |
195 | dev_err(wdt->miscdev.parent, | 197 | dev_err(wdt->miscdev.parent, |
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 0f5288df0091..e5c91d4404ed 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c | |||
@@ -42,7 +42,7 @@ | |||
42 | 42 | ||
43 | #include <asm/system.h> | 43 | #include <asm/system.h> |
44 | 44 | ||
45 | #define WATCHDOG_NAME "w83627hf/thf/hg WDT" | 45 | #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT" |
46 | #define PFX WATCHDOG_NAME ": " | 46 | #define PFX WATCHDOG_NAME ": " |
47 | #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ | 47 | #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ |
48 | 48 | ||
@@ -89,7 +89,7 @@ static void w83627hf_select_wd_register(void) | |||
89 | c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */ | 89 | c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */ |
90 | outb_p(0x2b, WDT_EFER); | 90 | outb_p(0x2b, WDT_EFER); |
91 | outb_p(c, WDT_EFDR); /* set GPIO3 to WDT0 */ | 91 | outb_p(c, WDT_EFDR); /* set GPIO3 to WDT0 */ |
92 | } else if (c == 0x88) { /* W83627EHF */ | 92 | } else if (c == 0x88 || c == 0xa0) { /* W83627EHF / W83627DHG */ |
93 | outb_p(0x2d, WDT_EFER); /* select GPIO5 */ | 93 | outb_p(0x2d, WDT_EFER); /* select GPIO5 */ |
94 | c = inb_p(WDT_EFDR) & ~0x01; /* PIN77 -> WDT0# */ | 94 | c = inb_p(WDT_EFDR) & ~0x01; /* PIN77 -> WDT0# */ |
95 | outb_p(0x2d, WDT_EFER); | 95 | outb_p(0x2d, WDT_EFER); |
@@ -129,6 +129,8 @@ static void w83627hf_init(void) | |||
129 | t = inb_p(WDT_EFDR); /* read CRF5 */ | 129 | t = inb_p(WDT_EFDR); /* read CRF5 */ |
130 | t &= ~0x0C; /* set second mode & disable keyboard | 130 | t &= ~0x0C; /* set second mode & disable keyboard |
131 | turning off watchdog */ | 131 | turning off watchdog */ |
132 | t |= 0x02; /* enable the WDTO# output low pulse | ||
133 | to the KBRST# pin (PIN60) */ | ||
132 | outb_p(t, WDT_EFDR); /* Write back to CRF5 */ | 134 | outb_p(t, WDT_EFDR); /* Write back to CRF5 */ |
133 | 135 | ||
134 | outb_p(0xF7, WDT_EFER); /* Select CRF7 */ | 136 | outb_p(0xF7, WDT_EFER); /* Select CRF7 */ |
@@ -321,7 +323,7 @@ static int __init wdt_init(void) | |||
321 | { | 323 | { |
322 | int ret; | 324 | int ret; |
323 | 325 | ||
324 | printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG Super I/O chip initialising.\n"); | 326 | printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising.\n"); |
325 | 327 | ||
326 | if (wdt_set_heartbeat(timeout)) { | 328 | if (wdt_set_heartbeat(timeout)) { |
327 | wdt_set_heartbeat(WATCHDOG_TIMEOUT); | 329 | wdt_set_heartbeat(WATCHDOG_TIMEOUT); |
diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c index bfda2e99dd89..552a4381e78f 100644 --- a/drivers/watchdog/wdt.c +++ b/drivers/watchdog/wdt.c | |||
@@ -91,7 +91,7 @@ MODULE_PARM_DESC(tachometer, | |||
91 | static int type = 500; | 91 | static int type = 500; |
92 | module_param(type, int, 0); | 92 | module_param(type, int, 0); |
93 | MODULE_PARM_DESC(type, | 93 | MODULE_PARM_DESC(type, |
94 | "WDT501-P Card type (500 or 501 , default=500)"); | 94 | "WDT501-P Card type (500 or 501, default=500)"); |
95 | 95 | ||
96 | /* | 96 | /* |
97 | * Programming support | 97 | * Programming support |
diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c index 90ef70eb47d7..5c2521fc836c 100644 --- a/drivers/watchdog/wdt977.c +++ b/drivers/watchdog/wdt977.c | |||
@@ -63,7 +63,7 @@ static char expect_close; | |||
63 | static DEFINE_SPINLOCK(spinlock); | 63 | static DEFINE_SPINLOCK(spinlock); |
64 | 64 | ||
65 | module_param(timeout, int, 0); | 65 | module_param(timeout, int, 0); |
66 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (60..15300), default=" | 66 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (60..15300, default=" |
67 | __MODULE_STRING(DEFAULT_TIMEOUT) ")"); | 67 | __MODULE_STRING(DEFAULT_TIMEOUT) ")"); |
68 | module_param(testmode, int, 0); | 68 | module_param(testmode, int, 0); |
69 | MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0"); | 69 | MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0"); |
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index 7b22e3cdbc81..6130c88fa5ac 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c | |||
@@ -60,19 +60,6 @@ | |||
60 | 60 | ||
61 | #define PFX "wdt_pci: " | 61 | #define PFX "wdt_pci: " |
62 | 62 | ||
63 | /* | ||
64 | * Until Access I/O gets their application for a PCI vendor ID approved, | ||
65 | * I don't think that it's appropriate to move these constants into the | ||
66 | * regular pci_ids.h file. -- JPN 2000/01/18 | ||
67 | */ | ||
68 | |||
69 | #ifndef PCI_VENDOR_ID_ACCESSIO | ||
70 | #define PCI_VENDOR_ID_ACCESSIO 0x494f | ||
71 | #endif | ||
72 | #ifndef PCI_DEVICE_ID_WDG_CSM | ||
73 | #define PCI_DEVICE_ID_WDG_CSM 0x22c0 | ||
74 | #endif | ||
75 | |||
76 | /* We can only use 1 card due to the /dev/watchdog restriction */ | 63 | /* We can only use 1 card due to the /dev/watchdog restriction */ |
77 | static int dev_count; | 64 | static int dev_count; |
78 | 65 | ||
@@ -743,7 +730,7 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev) | |||
743 | static struct pci_device_id wdtpci_pci_tbl[] = { | 730 | static struct pci_device_id wdtpci_pci_tbl[] = { |
744 | { | 731 | { |
745 | .vendor = PCI_VENDOR_ID_ACCESSIO, | 732 | .vendor = PCI_VENDOR_ID_ACCESSIO, |
746 | .device = PCI_DEVICE_ID_WDG_CSM, | 733 | .device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM, |
747 | .subvendor = PCI_ANY_ID, | 734 | .subvendor = PCI_ANY_ID, |
748 | .subdevice = PCI_ANY_ID, | 735 | .subdevice = PCI_ANY_ID, |
749 | }, | 736 | }, |
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c index 89dd7b035295..b68d928c8f90 100644 --- a/drivers/watchdog/wm8350_wdt.c +++ b/drivers/watchdog/wm8350_wdt.c | |||
@@ -284,7 +284,7 @@ static int __devinit wm8350_wdt_probe(struct platform_device *pdev) | |||
284 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); | 284 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); |
285 | 285 | ||
286 | if (!wm8350) { | 286 | if (!wm8350) { |
287 | dev_err(wm8350->dev, "No driver data supplied\n"); | 287 | pr_err("No driver data supplied\n"); |
288 | return -ENODEV; | 288 | return -ENODEV; |
289 | } | 289 | } |
290 | 290 | ||