diff options
| -rw-r--r-- | drivers/watchdog/w83627hf_wdt.c | 132 |
1 files changed, 76 insertions, 56 deletions
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index c5d34f46e7ad..e24b21082874 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c | |||
| @@ -70,80 +70,102 @@ MODULE_PARM_DESC(nowayout, | |||
| 70 | (same as EFER) */ | 70 | (same as EFER) */ |
| 71 | #define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ | 71 | #define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ |
| 72 | 72 | ||
| 73 | static void w83627hf_select_wd_register(void) | 73 | #define W83627HF_LD_WDT 0x08 |
| 74 | |||
| 75 | static void superio_outb(int reg, int val) | ||
| 76 | { | ||
| 77 | outb(reg, WDT_EFER); | ||
| 78 | outb(val, WDT_EFDR); | ||
| 79 | } | ||
| 80 | |||
| 81 | static inline int superio_inb(int reg) | ||
| 82 | { | ||
| 83 | outb(reg, WDT_EFER); | ||
| 84 | return inb(WDT_EFDR); | ||
| 85 | } | ||
| 86 | |||
| 87 | static int superio_enter(void) | ||
| 74 | { | 88 | { |
| 89 | if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME)) | ||
| 90 | return -EBUSY; | ||
| 91 | |||
| 75 | outb_p(0x87, WDT_EFER); /* Enter extended function mode */ | 92 | outb_p(0x87, WDT_EFER); /* Enter extended function mode */ |
| 76 | outb_p(0x87, WDT_EFER); /* Again according to manual */ | 93 | outb_p(0x87, WDT_EFER); /* Again according to manual */ |
| 77 | outb_p(0x07, WDT_EFER); /* point to logical device number reg */ | 94 | |
| 78 | outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */ | 95 | return 0; |
| 79 | } | 96 | } |
| 80 | 97 | ||
| 81 | static void w83627hf_unselect_wd_register(void) | 98 | static void superio_select(int ld) |
| 99 | { | ||
| 100 | superio_outb(0x07, ld); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void superio_exit(void) | ||
| 82 | { | 104 | { |
| 83 | outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ | 105 | outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ |
| 106 | release_region(wdt_io, 2); | ||
| 84 | } | 107 | } |
| 85 | 108 | ||
| 86 | /* tyan motherboards seem to set F5 to 0x4C ? | 109 | /* tyan motherboards seem to set F5 to 0x4C ? |
| 87 | * So explicitly init to appropriate value. */ | 110 | * So explicitly init to appropriate value. */ |
| 88 | 111 | ||
| 89 | static void w83627hf_init(struct watchdog_device *wdog) | 112 | static int w83627hf_init(struct watchdog_device *wdog) |
| 90 | { | 113 | { |
| 114 | int ret; | ||
| 91 | unsigned char t; | 115 | unsigned char t; |
| 92 | 116 | ||
| 93 | w83627hf_select_wd_register(); | 117 | ret = superio_enter(); |
| 118 | if (ret) | ||
| 119 | return ret; | ||
| 94 | 120 | ||
| 95 | outb(0x20, WDT_EFER); /* check chip version */ | 121 | superio_select(W83627HF_LD_WDT); |
| 96 | t = inb(WDT_EFDR); | 122 | t = superio_inb(0x20); /* check chip version */ |
| 97 | if (t == 0x82) { /* W83627THF */ | 123 | if (t == 0x82) { /* W83627THF */ |
| 98 | outb_p(0x2b, WDT_EFER); /* select GPIO3 */ | 124 | t = (superio_inb(0x2b) & 0xf7); |
| 99 | t = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */ | 125 | superio_outb(0x2b, t | 0x04); /* set GPIO3 to WDT0 */ |
| 100 | outb_p(0x2b, WDT_EFER); | ||
| 101 | outb_p(t, WDT_EFDR); /* set GPIO3 to WDT0 */ | ||
| 102 | } else if (t == 0x88 || t == 0xa0) { /* W83627EHF / W83627DHG */ | 126 | } else if (t == 0x88 || t == 0xa0) { /* W83627EHF / W83627DHG */ |
| 103 | outb_p(0x2d, WDT_EFER); /* select GPIO5 */ | 127 | t = superio_inb(0x2d); |
| 104 | t = inb_p(WDT_EFDR) & ~0x01; /* PIN77 -> WDT0# */ | 128 | superio_outb(0x2d, t & ~0x01); /* set GPIO5 to WDT0 */ |
| 105 | outb_p(0x2d, WDT_EFER); | ||
| 106 | outb_p(t, WDT_EFDR); /* set GPIO5 to WDT0 */ | ||
| 107 | } | 129 | } |
| 108 | 130 | ||
| 109 | outb_p(0x30, WDT_EFER); /* select CR30 */ | 131 | /* set CR30 bit 0 to activate GPIO2 */ |
| 110 | t = inb(WDT_EFDR); | 132 | t = superio_inb(0x30); |
| 111 | if (!(t & 0x01)) | 133 | if (!(t & 0x01)) |
| 112 | outb_p(t | 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */ | 134 | superio_outb(0x30, t | 0x01); |
| 113 | 135 | ||
| 114 | outb_p(0xF6, WDT_EFER); /* Select CRF6 */ | 136 | t = superio_inb(0xF6); |
| 115 | t = inb_p(WDT_EFDR); /* read CRF6 */ | ||
| 116 | if (t != 0) { | 137 | if (t != 0) { |
| 117 | pr_info("Watchdog already running. Resetting timeout to %d sec\n", | 138 | pr_info("Watchdog already running. Resetting timeout to %d sec\n", |
| 118 | wdog->timeout); | 139 | wdog->timeout); |
| 119 | outb_p(wdog->timeout, WDT_EFDR); /* Write back to CRF6 */ | 140 | superio_outb(0xF6, wdog->timeout); |
| 120 | } | 141 | } |
| 121 | 142 | ||
| 122 | outb_p(0xF5, WDT_EFER); /* Select CRF5 */ | 143 | /* set second mode & disable keyboard turning off watchdog */ |
| 123 | t = inb_p(WDT_EFDR); /* read CRF5 */ | 144 | t = superio_inb(0xF5) & ~0x0C; |
| 124 | t &= ~0x0C; /* set second mode & disable keyboard | 145 | /* enable the WDTO# output low pulse to the KBRST# pin */ |
| 125 | turning off watchdog */ | 146 | t |= 0x02; |
| 126 | t |= 0x02; /* enable the WDTO# output low pulse | 147 | superio_outb(0xF5, t); |
| 127 | to the KBRST# pin (PIN60) */ | 148 | |
| 128 | outb_p(t, WDT_EFDR); /* Write back to CRF5 */ | 149 | /* disable keyboard & mouse turning off watchdog */ |
| 129 | 150 | t = superio_inb(0xF7) & ~0xC0; | |
| 130 | outb_p(0xF7, WDT_EFER); /* Select CRF7 */ | 151 | superio_outb(0xF7, t); |
| 131 | t = inb_p(WDT_EFDR); /* read CRF7 */ | 152 | |
| 132 | t &= ~0xC0; /* disable keyboard & mouse turning off | 153 | superio_exit(); |
| 133 | watchdog */ | 154 | |
| 134 | outb_p(t, WDT_EFDR); /* Write back to CRF7 */ | 155 | return 0; |
| 135 | |||
| 136 | w83627hf_unselect_wd_register(); | ||
| 137 | } | 156 | } |
| 138 | 157 | ||
| 139 | static int wdt_set_time(unsigned int timeout) | 158 | static int wdt_set_time(unsigned int timeout) |
| 140 | { | 159 | { |
| 141 | w83627hf_select_wd_register(); | 160 | int ret; |
| 142 | 161 | ||
| 143 | outb_p(0xF6, WDT_EFER); /* Select CRF6 */ | 162 | ret = superio_enter(); |
| 144 | outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */ | 163 | if (ret) |
| 164 | return ret; | ||
| 145 | 165 | ||
| 146 | w83627hf_unselect_wd_register(); | 166 | superio_select(W83627HF_LD_WDT); |
| 167 | superio_outb(0xF6, timeout); | ||
| 168 | superio_exit(); | ||
| 147 | 169 | ||
| 148 | return 0; | 170 | return 0; |
| 149 | } | 171 | } |
| @@ -168,13 +190,15 @@ static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout) | |||
| 168 | static unsigned int wdt_get_time(struct watchdog_device *wdog) | 190 | static unsigned int wdt_get_time(struct watchdog_device *wdog) |
| 169 | { | 191 | { |
| 170 | unsigned int timeleft; | 192 | unsigned int timeleft; |
| 193 | int ret; | ||
| 171 | 194 | ||
| 172 | w83627hf_select_wd_register(); | 195 | ret = superio_enter(); |
| 173 | 196 | if (ret) | |
| 174 | outb_p(0xF6, WDT_EFER); /* Select CRF6 */ | 197 | return 0; |
| 175 | timeleft = inb_p(WDT_EFDR); /* Read Timeout counter to CRF6 */ | ||
| 176 | 198 | ||
| 177 | w83627hf_unselect_wd_register(); | 199 | superio_select(W83627HF_LD_WDT); |
| 200 | timeleft = superio_inb(0xF6); | ||
| 201 | superio_exit(); | ||
| 178 | 202 | ||
| 179 | return timeleft; | 203 | return timeleft; |
| 180 | } | 204 | } |
| @@ -231,20 +255,19 @@ static int __init wdt_init(void) | |||
| 231 | 255 | ||
| 232 | pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n"); | 256 | pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n"); |
| 233 | 257 | ||
| 234 | if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { | ||
| 235 | pr_err("I/O address 0x%04x already in use\n", wdt_io); | ||
| 236 | return -EIO; | ||
| 237 | } | ||
| 238 | |||
| 239 | watchdog_init_timeout(&wdt_dev, timeout, NULL); | 258 | watchdog_init_timeout(&wdt_dev, timeout, NULL); |
| 240 | watchdog_set_nowayout(&wdt_dev, nowayout); | 259 | watchdog_set_nowayout(&wdt_dev, nowayout); |
| 241 | 260 | ||
| 242 | w83627hf_init(&wdt_dev); | 261 | ret = w83627hf_init(&wdt_dev); |
| 262 | if (ret) { | ||
| 263 | pr_err("failed to initialize watchdog (err=%d)\n", ret); | ||
| 264 | return ret; | ||
| 265 | } | ||
| 243 | 266 | ||
| 244 | ret = register_reboot_notifier(&wdt_notifier); | 267 | ret = register_reboot_notifier(&wdt_notifier); |
| 245 | if (ret != 0) { | 268 | if (ret != 0) { |
| 246 | pr_err("cannot register reboot notifier (err=%d)\n", ret); | 269 | pr_err("cannot register reboot notifier (err=%d)\n", ret); |
| 247 | goto unreg_regions; | 270 | return ret; |
| 248 | } | 271 | } |
| 249 | 272 | ||
| 250 | ret = watchdog_register_device(&wdt_dev); | 273 | ret = watchdog_register_device(&wdt_dev); |
| @@ -258,8 +281,6 @@ static int __init wdt_init(void) | |||
| 258 | 281 | ||
| 259 | unreg_reboot: | 282 | unreg_reboot: |
| 260 | unregister_reboot_notifier(&wdt_notifier); | 283 | unregister_reboot_notifier(&wdt_notifier); |
| 261 | unreg_regions: | ||
| 262 | release_region(wdt_io, 1); | ||
| 263 | return ret; | 284 | return ret; |
| 264 | } | 285 | } |
| 265 | 286 | ||
| @@ -267,7 +288,6 @@ static void __exit wdt_exit(void) | |||
| 267 | { | 288 | { |
| 268 | watchdog_unregister_device(&wdt_dev); | 289 | watchdog_unregister_device(&wdt_dev); |
| 269 | unregister_reboot_notifier(&wdt_notifier); | 290 | unregister_reboot_notifier(&wdt_notifier); |
| 270 | release_region(wdt_io, 1); | ||
| 271 | } | 291 | } |
| 272 | 292 | ||
| 273 | module_init(wdt_init); | 293 | module_init(wdt_init); |
