0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0024
0025 #include <linux/module.h>
0026 #include <linux/moduleparam.h>
0027 #include <linux/types.h>
0028 #include <linux/timer.h>
0029 #include <linux/miscdevice.h>
0030 #include <linux/watchdog.h>
0031 #include <linux/ioport.h>
0032 #include <linux/notifier.h>
0033 #include <linux/reboot.h>
0034 #include <linux/init.h>
0035 #include <linux/fs.h>
0036 #include <linux/pci.h>
0037 #include <linux/io.h>
0038 #include <linux/uaccess.h>
0039
0040
0041 #define WDT_ENABLE 0x9C
0042 #define WDT_DISABLE 0x8C
0043
0044 #define ALI_7101_WDT 0x92
0045 #define ALI_7101_GPIO 0x7D
0046 #define ALI_7101_GPIO_O 0x7E
0047 #define ALI_WDT_ARM 0x01
0048
0049
0050
0051
0052
0053 #define WDT_INTERVAL (HZ/4+1)
0054
0055
0056
0057
0058
0059
0060
0061 #define WATCHDOG_TIMEOUT 30
0062
0063 static int timeout = WATCHDOG_TIMEOUT;
0064 module_param(timeout, int, 0);
0065 MODULE_PARM_DESC(timeout,
0066 "Watchdog timeout in seconds. (1<=timeout<=3600, default="
0067 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
0068
0069 static int use_gpio;
0070 module_param(use_gpio, int, 0);
0071 MODULE_PARM_DESC(use_gpio,
0072 "Use the gpio watchdog (required by old cobalt boards).");
0073
0074 static void wdt_timer_ping(struct timer_list *);
0075 static DEFINE_TIMER(timer, wdt_timer_ping);
0076 static unsigned long next_heartbeat;
0077 static unsigned long wdt_is_open;
0078 static char wdt_expect_close;
0079 static struct pci_dev *alim7101_pmu;
0080
0081 static bool nowayout = WATCHDOG_NOWAYOUT;
0082 module_param(nowayout, bool, 0);
0083 MODULE_PARM_DESC(nowayout,
0084 "Watchdog cannot be stopped once started (default="
0085 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0086
0087
0088
0089
0090
0091 static void wdt_timer_ping(struct timer_list *unused)
0092 {
0093
0094
0095
0096 char tmp;
0097
0098 if (time_before(jiffies, next_heartbeat)) {
0099
0100 pci_read_config_byte(alim7101_pmu, 0x92, &tmp);
0101 pci_write_config_byte(alim7101_pmu,
0102 ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
0103 pci_write_config_byte(alim7101_pmu,
0104 ALI_7101_WDT, (tmp | ALI_WDT_ARM));
0105 if (use_gpio) {
0106 pci_read_config_byte(alim7101_pmu,
0107 ALI_7101_GPIO_O, &tmp);
0108 pci_write_config_byte(alim7101_pmu,
0109 ALI_7101_GPIO_O, tmp | 0x20);
0110 pci_write_config_byte(alim7101_pmu,
0111 ALI_7101_GPIO_O, tmp & ~0x20);
0112 }
0113 } else {
0114 pr_warn("Heartbeat lost! Will not ping the watchdog\n");
0115 }
0116
0117 mod_timer(&timer, jiffies + WDT_INTERVAL);
0118 }
0119
0120
0121
0122
0123
0124 static void wdt_change(int writeval)
0125 {
0126 char tmp;
0127
0128 pci_read_config_byte(alim7101_pmu, ALI_7101_WDT, &tmp);
0129 if (writeval == WDT_ENABLE) {
0130 pci_write_config_byte(alim7101_pmu,
0131 ALI_7101_WDT, (tmp | ALI_WDT_ARM));
0132 if (use_gpio) {
0133 pci_read_config_byte(alim7101_pmu,
0134 ALI_7101_GPIO_O, &tmp);
0135 pci_write_config_byte(alim7101_pmu,
0136 ALI_7101_GPIO_O, tmp & ~0x20);
0137 }
0138
0139 } else {
0140 pci_write_config_byte(alim7101_pmu,
0141 ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
0142 if (use_gpio) {
0143 pci_read_config_byte(alim7101_pmu,
0144 ALI_7101_GPIO_O, &tmp);
0145 pci_write_config_byte(alim7101_pmu,
0146 ALI_7101_GPIO_O, tmp | 0x20);
0147 }
0148 }
0149 }
0150
0151 static void wdt_startup(void)
0152 {
0153 next_heartbeat = jiffies + (timeout * HZ);
0154
0155
0156
0157
0158 wdt_change(WDT_ENABLE);
0159
0160
0161 mod_timer(&timer, jiffies + WDT_INTERVAL);
0162
0163 pr_info("Watchdog timer is now enabled\n");
0164 }
0165
0166 static void wdt_turnoff(void)
0167 {
0168
0169 del_timer_sync(&timer);
0170 wdt_change(WDT_DISABLE);
0171 pr_info("Watchdog timer is now disabled...\n");
0172 }
0173
0174 static void wdt_keepalive(void)
0175 {
0176
0177 next_heartbeat = jiffies + (timeout * HZ);
0178 }
0179
0180
0181
0182
0183
0184 static ssize_t fop_write(struct file *file, const char __user *buf,
0185 size_t count, loff_t *ppos)
0186 {
0187
0188 if (count) {
0189 if (!nowayout) {
0190 size_t ofs;
0191
0192
0193
0194 wdt_expect_close = 0;
0195
0196
0197 for (ofs = 0; ofs != count; ofs++) {
0198 char c;
0199 if (get_user(c, buf + ofs))
0200 return -EFAULT;
0201 if (c == 'V')
0202 wdt_expect_close = 42;
0203 }
0204 }
0205
0206 wdt_keepalive();
0207 }
0208 return count;
0209 }
0210
0211 static int fop_open(struct inode *inode, struct file *file)
0212 {
0213
0214 if (test_and_set_bit(0, &wdt_is_open))
0215 return -EBUSY;
0216
0217 wdt_startup();
0218 return stream_open(inode, file);
0219 }
0220
0221 static int fop_close(struct inode *inode, struct file *file)
0222 {
0223 if (wdt_expect_close == 42)
0224 wdt_turnoff();
0225 else {
0226
0227 pr_crit("device file closed unexpectedly. Will not stop the WDT!\n");
0228 }
0229 clear_bit(0, &wdt_is_open);
0230 wdt_expect_close = 0;
0231 return 0;
0232 }
0233
0234 static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0235 {
0236 void __user *argp = (void __user *)arg;
0237 int __user *p = argp;
0238 static const struct watchdog_info ident = {
0239 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
0240 | WDIOF_MAGICCLOSE,
0241 .firmware_version = 1,
0242 .identity = "ALiM7101",
0243 };
0244
0245 switch (cmd) {
0246 case WDIOC_GETSUPPORT:
0247 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
0248 case WDIOC_GETSTATUS:
0249 case WDIOC_GETBOOTSTATUS:
0250 return put_user(0, p);
0251 case WDIOC_SETOPTIONS:
0252 {
0253 int new_options, retval = -EINVAL;
0254
0255 if (get_user(new_options, p))
0256 return -EFAULT;
0257 if (new_options & WDIOS_DISABLECARD) {
0258 wdt_turnoff();
0259 retval = 0;
0260 }
0261 if (new_options & WDIOS_ENABLECARD) {
0262 wdt_startup();
0263 retval = 0;
0264 }
0265 return retval;
0266 }
0267 case WDIOC_KEEPALIVE:
0268 wdt_keepalive();
0269 return 0;
0270 case WDIOC_SETTIMEOUT:
0271 {
0272 int new_timeout;
0273
0274 if (get_user(new_timeout, p))
0275 return -EFAULT;
0276
0277 if (new_timeout < 1 || new_timeout > 3600)
0278 return -EINVAL;
0279 timeout = new_timeout;
0280 wdt_keepalive();
0281 }
0282 fallthrough;
0283 case WDIOC_GETTIMEOUT:
0284 return put_user(timeout, p);
0285 default:
0286 return -ENOTTY;
0287 }
0288 }
0289
0290 static const struct file_operations wdt_fops = {
0291 .owner = THIS_MODULE,
0292 .llseek = no_llseek,
0293 .write = fop_write,
0294 .open = fop_open,
0295 .release = fop_close,
0296 .unlocked_ioctl = fop_ioctl,
0297 .compat_ioctl = compat_ptr_ioctl,
0298 };
0299
0300 static struct miscdevice wdt_miscdev = {
0301 .minor = WATCHDOG_MINOR,
0302 .name = "watchdog",
0303 .fops = &wdt_fops,
0304 };
0305
0306 static int wdt_restart_handle(struct notifier_block *this, unsigned long mode,
0307 void *cmd)
0308 {
0309
0310
0311
0312
0313
0314 wdt_change(WDT_ENABLE);
0315
0316
0317 while (true)
0318 ;
0319
0320 return NOTIFY_DONE;
0321 }
0322
0323 static struct notifier_block wdt_restart_handler = {
0324 .notifier_call = wdt_restart_handle,
0325 .priority = 128,
0326 };
0327
0328
0329
0330
0331
0332 static int wdt_notify_sys(struct notifier_block *this,
0333 unsigned long code, void *unused)
0334 {
0335 if (code == SYS_DOWN || code == SYS_HALT)
0336 wdt_turnoff();
0337
0338 return NOTIFY_DONE;
0339 }
0340
0341
0342
0343
0344
0345
0346 static struct notifier_block wdt_notifier = {
0347 .notifier_call = wdt_notify_sys,
0348 };
0349
0350 static void __exit alim7101_wdt_unload(void)
0351 {
0352 wdt_turnoff();
0353
0354 misc_deregister(&wdt_miscdev);
0355 unregister_reboot_notifier(&wdt_notifier);
0356 unregister_restart_handler(&wdt_restart_handler);
0357 pci_dev_put(alim7101_pmu);
0358 }
0359
0360 static int __init alim7101_wdt_init(void)
0361 {
0362 int rc = -EBUSY;
0363 struct pci_dev *ali1543_south;
0364 char tmp;
0365
0366 pr_info("Steve Hill <steve@navaho.co.uk>\n");
0367 alim7101_pmu = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,
0368 NULL);
0369 if (!alim7101_pmu) {
0370 pr_info("ALi M7101 PMU not present - WDT not set\n");
0371 return -EBUSY;
0372 }
0373
0374
0375 pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02);
0376
0377 ali1543_south = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533,
0378 NULL);
0379 if (!ali1543_south) {
0380 pr_info("ALi 1543 South-Bridge not present - WDT not set\n");
0381 goto err_out;
0382 }
0383 pci_read_config_byte(ali1543_south, 0x5e, &tmp);
0384 pci_dev_put(ali1543_south);
0385 if ((tmp & 0x1e) == 0x00) {
0386 if (!use_gpio) {
0387 pr_info("Detected old alim7101 revision 'a1d'. If this is a cobalt board, set the 'use_gpio' module parameter.\n");
0388 goto err_out;
0389 }
0390 nowayout = 1;
0391 } else if ((tmp & 0x1e) != 0x12 && (tmp & 0x1e) != 0x00) {
0392 pr_info("ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set\n");
0393 goto err_out;
0394 }
0395
0396 if (timeout < 1 || timeout > 3600) {
0397
0398 timeout = WATCHDOG_TIMEOUT;
0399 pr_info("timeout value must be 1 <= x <= 3600, using %d\n",
0400 timeout);
0401 }
0402
0403 rc = register_reboot_notifier(&wdt_notifier);
0404 if (rc) {
0405 pr_err("cannot register reboot notifier (err=%d)\n", rc);
0406 goto err_out;
0407 }
0408
0409 rc = register_restart_handler(&wdt_restart_handler);
0410 if (rc) {
0411 pr_err("cannot register restart handler (err=%d)\n", rc);
0412 goto err_out_reboot;
0413 }
0414
0415 rc = misc_register(&wdt_miscdev);
0416 if (rc) {
0417 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0418 wdt_miscdev.minor, rc);
0419 goto err_out_restart;
0420 }
0421
0422 if (nowayout)
0423 __module_get(THIS_MODULE);
0424
0425 pr_info("WDT driver for ALi M7101 initialised. timeout=%d sec (nowayout=%d)\n",
0426 timeout, nowayout);
0427 return 0;
0428
0429 err_out_restart:
0430 unregister_restart_handler(&wdt_restart_handler);
0431 err_out_reboot:
0432 unregister_reboot_notifier(&wdt_notifier);
0433 err_out:
0434 pci_dev_put(alim7101_pmu);
0435 return rc;
0436 }
0437
0438 module_init(alim7101_wdt_init);
0439 module_exit(alim7101_wdt_unload);
0440
0441 static const struct pci_device_id alim7101_pci_tbl[] __used = {
0442 { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) },
0443 { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
0444 { }
0445 };
0446
0447 MODULE_DEVICE_TABLE(pci, alim7101_pci_tbl);
0448
0449 MODULE_AUTHOR("Steve Hill");
0450 MODULE_DESCRIPTION("ALi M7101 PMU Computer Watchdog Timer driver");
0451 MODULE_LICENSE("GPL");