0001
0002
0003
0004
0005
0006 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0007
0008 #include <linux/module.h>
0009 #include <linux/moduleparam.h>
0010 #include <linux/types.h>
0011 #include <linux/miscdevice.h>
0012 #include <linux/watchdog.h>
0013 #include <linux/ioport.h>
0014 #include <linux/notifier.h>
0015 #include <linux/reboot.h>
0016 #include <linux/init.h>
0017 #include <linux/fs.h>
0018 #include <linux/pci.h>
0019 #include <linux/uaccess.h>
0020 #include <linux/io.h>
0021
0022 #define WATCHDOG_NAME "ALi_M1535"
0023 #define WATCHDOG_TIMEOUT 60
0024
0025
0026 static unsigned long ali_is_open;
0027 static char ali_expect_release;
0028 static struct pci_dev *ali_pci;
0029 static u32 ali_timeout_bits;
0030 static DEFINE_SPINLOCK(ali_lock);
0031
0032
0033 static int timeout = WATCHDOG_TIMEOUT;
0034 module_param(timeout, int, 0);
0035 MODULE_PARM_DESC(timeout,
0036 "Watchdog timeout in seconds. (0 < timeout < 18000, default="
0037 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
0038
0039 static bool nowayout = WATCHDOG_NOWAYOUT;
0040 module_param(nowayout, bool, 0);
0041 MODULE_PARM_DESC(nowayout,
0042 "Watchdog cannot be stopped once started (default="
0043 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0044
0045
0046
0047
0048
0049
0050
0051
0052 static void ali_start(void)
0053 {
0054 u32 val;
0055
0056 spin_lock(&ali_lock);
0057
0058 pci_read_config_dword(ali_pci, 0xCC, &val);
0059 val &= ~0x3F;
0060 val |= (1 << 25) | ali_timeout_bits;
0061 pci_write_config_dword(ali_pci, 0xCC, val);
0062
0063 spin_unlock(&ali_lock);
0064 }
0065
0066
0067
0068
0069
0070
0071
0072 static void ali_stop(void)
0073 {
0074 u32 val;
0075
0076 spin_lock(&ali_lock);
0077
0078 pci_read_config_dword(ali_pci, 0xCC, &val);
0079 val &= ~0x3F;
0080 val &= ~(1 << 25);
0081 pci_write_config_dword(ali_pci, 0xCC, val);
0082
0083 spin_unlock(&ali_lock);
0084 }
0085
0086
0087
0088
0089
0090
0091
0092 static void ali_keepalive(void)
0093 {
0094 ali_start();
0095 }
0096
0097
0098
0099
0100
0101
0102
0103
0104 static int ali_settimer(int t)
0105 {
0106 if (t < 0)
0107 return -EINVAL;
0108 else if (t < 60)
0109 ali_timeout_bits = t|(1 << 6);
0110 else if (t < 3600)
0111 ali_timeout_bits = (t / 60)|(1 << 7);
0112 else if (t < 18000)
0113 ali_timeout_bits = (t / 300)|(1 << 6)|(1 << 7);
0114 else
0115 return -EINVAL;
0116
0117 timeout = t;
0118 return 0;
0119 }
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137 static ssize_t ali_write(struct file *file, const char __user *data,
0138 size_t len, loff_t *ppos)
0139 {
0140
0141 if (len) {
0142 if (!nowayout) {
0143 size_t i;
0144
0145
0146
0147 ali_expect_release = 0;
0148
0149
0150
0151 for (i = 0; i != len; i++) {
0152 char c;
0153 if (get_user(c, data + i))
0154 return -EFAULT;
0155 if (c == 'V')
0156 ali_expect_release = 42;
0157 }
0158 }
0159
0160
0161 ali_start();
0162 }
0163 return len;
0164 }
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176 static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0177 {
0178 void __user *argp = (void __user *)arg;
0179 int __user *p = argp;
0180 static const struct watchdog_info ident = {
0181 .options = WDIOF_KEEPALIVEPING |
0182 WDIOF_SETTIMEOUT |
0183 WDIOF_MAGICCLOSE,
0184 .firmware_version = 0,
0185 .identity = "ALi M1535 WatchDog Timer",
0186 };
0187
0188 switch (cmd) {
0189 case WDIOC_GETSUPPORT:
0190 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
0191
0192 case WDIOC_GETSTATUS:
0193 case WDIOC_GETBOOTSTATUS:
0194 return put_user(0, p);
0195 case WDIOC_SETOPTIONS:
0196 {
0197 int new_options, retval = -EINVAL;
0198
0199 if (get_user(new_options, p))
0200 return -EFAULT;
0201 if (new_options & WDIOS_DISABLECARD) {
0202 ali_stop();
0203 retval = 0;
0204 }
0205 if (new_options & WDIOS_ENABLECARD) {
0206 ali_start();
0207 retval = 0;
0208 }
0209 return retval;
0210 }
0211 case WDIOC_KEEPALIVE:
0212 ali_keepalive();
0213 return 0;
0214 case WDIOC_SETTIMEOUT:
0215 {
0216 int new_timeout;
0217 if (get_user(new_timeout, p))
0218 return -EFAULT;
0219 if (ali_settimer(new_timeout))
0220 return -EINVAL;
0221 ali_keepalive();
0222 }
0223 fallthrough;
0224 case WDIOC_GETTIMEOUT:
0225 return put_user(timeout, p);
0226 default:
0227 return -ENOTTY;
0228 }
0229 }
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240 static int ali_open(struct inode *inode, struct file *file)
0241 {
0242
0243 if (test_and_set_bit(0, &ali_is_open))
0244 return -EBUSY;
0245
0246
0247 ali_start();
0248 return stream_open(inode, file);
0249 }
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260 static int ali_release(struct inode *inode, struct file *file)
0261 {
0262
0263
0264
0265 if (ali_expect_release == 42)
0266 ali_stop();
0267 else {
0268 pr_crit("Unexpected close, not stopping watchdog!\n");
0269 ali_keepalive();
0270 }
0271 clear_bit(0, &ali_is_open);
0272 ali_expect_release = 0;
0273 return 0;
0274 }
0275
0276
0277
0278
0279
0280
0281
0282
0283 static int ali_notify_sys(struct notifier_block *this,
0284 unsigned long code, void *unused)
0285 {
0286 if (code == SYS_DOWN || code == SYS_HALT)
0287 ali_stop();
0288 return NOTIFY_DONE;
0289 }
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300 static const struct pci_device_id ali_pci_tbl[] __used = {
0301 { PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,},
0302 { PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,},
0303 { 0, },
0304 };
0305 MODULE_DEVICE_TABLE(pci, ali_pci_tbl);
0306
0307
0308
0309
0310
0311
0312
0313
0314 static int __init ali_find_watchdog(void)
0315 {
0316 struct pci_dev *pdev;
0317 u32 wdog;
0318
0319
0320 pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1535, NULL);
0321 if (pdev == NULL)
0322 pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1533, NULL);
0323 if (pdev == NULL)
0324 return -ENODEV;
0325 pci_dev_put(pdev);
0326
0327
0328 pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x7101, NULL);
0329 if (pdev == NULL)
0330 return -ENODEV;
0331
0332 if (pci_enable_device(pdev)) {
0333 pci_dev_put(pdev);
0334 return -EIO;
0335 }
0336
0337 ali_pci = pdev;
0338
0339
0340
0341
0342 pci_read_config_dword(pdev, 0xCC, &wdog);
0343
0344
0345 wdog &= ~0x3F;
0346
0347 wdog &= ~((1 << 27)|(1 << 26)|(1 << 25)|(1 << 24));
0348
0349 wdog &= ~((1 << 16)|(1 << 13)|(1 << 12)|(1 << 11)|(1 << 10)|(1 << 9));
0350
0351 pci_write_config_dword(pdev, 0xCC, wdog);
0352
0353 return 0;
0354 }
0355
0356
0357
0358
0359
0360 static const struct file_operations ali_fops = {
0361 .owner = THIS_MODULE,
0362 .llseek = no_llseek,
0363 .write = ali_write,
0364 .unlocked_ioctl = ali_ioctl,
0365 .compat_ioctl = compat_ptr_ioctl,
0366 .open = ali_open,
0367 .release = ali_release,
0368 };
0369
0370 static struct miscdevice ali_miscdev = {
0371 .minor = WATCHDOG_MINOR,
0372 .name = "watchdog",
0373 .fops = &ali_fops,
0374 };
0375
0376 static struct notifier_block ali_notifier = {
0377 .notifier_call = ali_notify_sys,
0378 };
0379
0380
0381
0382
0383
0384
0385
0386
0387 static int __init watchdog_init(void)
0388 {
0389 int ret;
0390
0391
0392 if (ali_find_watchdog() != 0)
0393 return -ENODEV;
0394
0395
0396
0397 if (timeout < 1 || timeout >= 18000) {
0398 timeout = WATCHDOG_TIMEOUT;
0399 pr_info("timeout value must be 0 < timeout < 18000, using %d\n",
0400 timeout);
0401 }
0402
0403
0404 ali_settimer(timeout);
0405
0406 ret = register_reboot_notifier(&ali_notifier);
0407 if (ret != 0) {
0408 pr_err("cannot register reboot notifier (err=%d)\n", ret);
0409 goto out;
0410 }
0411
0412 ret = misc_register(&ali_miscdev);
0413 if (ret != 0) {
0414 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0415 WATCHDOG_MINOR, ret);
0416 goto unreg_reboot;
0417 }
0418
0419 pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
0420 timeout, nowayout);
0421
0422 out:
0423 return ret;
0424 unreg_reboot:
0425 unregister_reboot_notifier(&ali_notifier);
0426 goto out;
0427 }
0428
0429
0430
0431
0432
0433
0434
0435 static void __exit watchdog_exit(void)
0436 {
0437
0438 ali_stop();
0439
0440
0441 misc_deregister(&ali_miscdev);
0442 unregister_reboot_notifier(&ali_notifier);
0443 pci_dev_put(ali_pci);
0444 }
0445
0446 module_init(watchdog_init);
0447 module_exit(watchdog_exit);
0448
0449 MODULE_AUTHOR("Alan Cox");
0450 MODULE_DESCRIPTION("ALi M1535 PMU Watchdog Timer driver");
0451 MODULE_LICENSE("GPL");