Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: watchdog support for rtc-m41t80 driver

Add a watchdog driver interface to rtc-m41t80 driver. This is derived from
works by Alexander Bigga <ab@mycable.de>

Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Alexander Bigga <ab@mycable.de>
Cc: David Brownell <david-b@pacbell.net>
Acked-by: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Atsushi Nemoto and committed by
Linus Torvalds
617780d2 caaff562

+295
+7
drivers/rtc/Kconfig
··· 224 224 This driver can also be built as a module. If so, the module 225 225 will be called rtc-m41t80. 226 226 227 + config RTC_DRV_M41T80_WDT 228 + bool "ST M41T80 series RTC watchdog timer" 229 + depends on RTC_DRV_M41T80 230 + help 231 + If you say Y here you will get support for the 232 + watchdog timer in ST M41T80 RTC chips series. 233 + 227 234 comment "SPI RTC drivers" 228 235 depends on RTC_CLASS && SPI_MASTER 229 236
+288
drivers/rtc/rtc-m41t80.c
··· 20 20 #include <linux/i2c.h> 21 21 #include <linux/rtc.h> 22 22 #include <linux/bcd.h> 23 + #ifdef CONFIG_RTC_DRV_M41T80_WDT 24 + #include <linux/miscdevice.h> 25 + #include <linux/watchdog.h> 26 + #include <linux/reboot.h> 27 + #include <linux/fs.h> 28 + #include <linux/ioctl.h> 29 + #endif 23 30 24 31 #define M41T80_REG_SSEC 0 25 32 #define M41T80_REG_SEC 1 ··· 487 480 } 488 481 #endif 489 482 483 + #ifdef CONFIG_RTC_DRV_M41T80_WDT 484 + /* 485 + ***************************************************************************** 486 + * 487 + * Watchdog Driver 488 + * 489 + ***************************************************************************** 490 + */ 491 + static struct i2c_client *save_client; 492 + 493 + /* Default margin */ 494 + #define WD_TIMO 60 /* 1..31 seconds */ 495 + 496 + static int wdt_margin = WD_TIMO; 497 + module_param(wdt_margin, int, 0); 498 + MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 60s)"); 499 + 500 + static unsigned long wdt_is_open; 501 + static int boot_flag; 502 + 503 + /** 504 + * wdt_ping: 505 + * 506 + * Reload counter one with the watchdog timeout. We don't bother reloading 507 + * the cascade counter. 508 + */ 509 + static void wdt_ping(void) 510 + { 511 + unsigned char i2c_data[2]; 512 + struct i2c_msg msgs1[1] = { 513 + { 514 + .addr = save_client->addr, 515 + .flags = 0, 516 + .len = 2, 517 + .buf = i2c_data, 518 + }, 519 + }; 520 + i2c_data[0] = 0x09; /* watchdog register */ 521 + 522 + if (wdt_margin > 31) 523 + i2c_data[1] = (wdt_margin & 0xFC) | 0x83; /* resolution = 4s */ 524 + else 525 + /* 526 + * WDS = 1 (0x80), mulitplier = WD_TIMO, resolution = 1s (0x02) 527 + */ 528 + i2c_data[1] = wdt_margin<<2 | 0x82; 529 + 530 + i2c_transfer(save_client->adapter, msgs1, 1); 531 + } 532 + 533 + /** 534 + * wdt_disable: 535 + * 536 + * disables watchdog. 537 + */ 538 + static void wdt_disable(void) 539 + { 540 + unsigned char i2c_data[2], i2c_buf[0x10]; 541 + struct i2c_msg msgs0[2] = { 542 + { 543 + .addr = save_client->addr, 544 + .flags = 0, 545 + .len = 1, 546 + .buf = i2c_data, 547 + }, 548 + { 549 + .addr = save_client->addr, 550 + .flags = I2C_M_RD, 551 + .len = 1, 552 + .buf = i2c_buf, 553 + }, 554 + }; 555 + struct i2c_msg msgs1[1] = { 556 + { 557 + .addr = save_client->addr, 558 + .flags = 0, 559 + .len = 2, 560 + .buf = i2c_data, 561 + }, 562 + }; 563 + 564 + i2c_data[0] = 0x09; 565 + i2c_transfer(save_client->adapter, msgs0, 2); 566 + 567 + i2c_data[0] = 0x09; 568 + i2c_data[1] = 0x00; 569 + i2c_transfer(save_client->adapter, msgs1, 1); 570 + } 571 + 572 + /** 573 + * wdt_write: 574 + * @file: file handle to the watchdog 575 + * @buf: buffer to write (unused as data does not matter here 576 + * @count: count of bytes 577 + * @ppos: pointer to the position to write. No seeks allowed 578 + * 579 + * A write to a watchdog device is defined as a keepalive signal. Any 580 + * write of data will do, as we we don't define content meaning. 581 + */ 582 + static ssize_t wdt_write(struct file *file, const char __user *buf, 583 + size_t count, loff_t *ppos) 584 + { 585 + /* Can't seek (pwrite) on this device 586 + if (ppos != &file->f_pos) 587 + return -ESPIPE; 588 + */ 589 + if (count) { 590 + wdt_ping(); 591 + return 1; 592 + } 593 + return 0; 594 + } 595 + 596 + static ssize_t wdt_read(struct file *file, char __user *buf, 597 + size_t count, loff_t *ppos) 598 + { 599 + return 0; 600 + } 601 + 602 + /** 603 + * wdt_ioctl: 604 + * @inode: inode of the device 605 + * @file: file handle to the device 606 + * @cmd: watchdog command 607 + * @arg: argument pointer 608 + * 609 + * The watchdog API defines a common set of functions for all watchdogs 610 + * according to their available features. We only actually usefully support 611 + * querying capabilities and current status. 612 + */ 613 + static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 614 + unsigned long arg) 615 + { 616 + int new_margin, rv; 617 + static struct watchdog_info ident = { 618 + .options = WDIOF_POWERUNDER | WDIOF_KEEPALIVEPING | 619 + WDIOF_SETTIMEOUT, 620 + .firmware_version = 1, 621 + .identity = "M41T80 WTD" 622 + }; 623 + 624 + switch (cmd) { 625 + case WDIOC_GETSUPPORT: 626 + return copy_to_user((struct watchdog_info __user *)arg, &ident, 627 + sizeof(ident)) ? -EFAULT : 0; 628 + 629 + case WDIOC_GETSTATUS: 630 + case WDIOC_GETBOOTSTATUS: 631 + return put_user(boot_flag, (int __user *)arg); 632 + case WDIOC_KEEPALIVE: 633 + wdt_ping(); 634 + return 0; 635 + case WDIOC_SETTIMEOUT: 636 + if (get_user(new_margin, (int __user *)arg)) 637 + return -EFAULT; 638 + /* Arbitrary, can't find the card's limits */ 639 + if (new_margin < 1 || new_margin > 124) 640 + return -EINVAL; 641 + wdt_margin = new_margin; 642 + wdt_ping(); 643 + /* Fall */ 644 + case WDIOC_GETTIMEOUT: 645 + return put_user(wdt_margin, (int __user *)arg); 646 + 647 + case WDIOC_SETOPTIONS: 648 + if (copy_from_user(&rv, (int __user *)arg, sizeof(int))) 649 + return -EFAULT; 650 + 651 + if (rv & WDIOS_DISABLECARD) { 652 + printk(KERN_INFO 653 + "rtc-m41t80: disable watchdog\n"); 654 + wdt_disable(); 655 + } 656 + 657 + if (rv & WDIOS_ENABLECARD) { 658 + printk(KERN_INFO 659 + "rtc-m41t80: enable watchdog\n"); 660 + wdt_ping(); 661 + } 662 + 663 + return -EINVAL; 664 + } 665 + return -ENOTTY; 666 + } 667 + 668 + /** 669 + * wdt_open: 670 + * @inode: inode of device 671 + * @file: file handle to device 672 + * 673 + */ 674 + static int wdt_open(struct inode *inode, struct file *file) 675 + { 676 + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { 677 + if (test_and_set_bit(0, &wdt_is_open)) 678 + return -EBUSY; 679 + /* 680 + * Activate 681 + */ 682 + wdt_is_open = 1; 683 + return 0; 684 + } 685 + return -ENODEV; 686 + } 687 + 688 + /** 689 + * wdt_close: 690 + * @inode: inode to board 691 + * @file: file handle to board 692 + * 693 + */ 694 + static int wdt_release(struct inode *inode, struct file *file) 695 + { 696 + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) 697 + clear_bit(0, &wdt_is_open); 698 + return 0; 699 + } 700 + 701 + /** 702 + * notify_sys: 703 + * @this: our notifier block 704 + * @code: the event being reported 705 + * @unused: unused 706 + * 707 + * Our notifier is called on system shutdowns. We want to turn the card 708 + * off at reboot otherwise the machine will reboot again during memory 709 + * test or worse yet during the following fsck. This would suck, in fact 710 + * trust me - if it happens it does suck. 711 + */ 712 + static int wdt_notify_sys(struct notifier_block *this, unsigned long code, 713 + void *unused) 714 + { 715 + if (code == SYS_DOWN || code == SYS_HALT) 716 + /* Disable Watchdog */ 717 + wdt_disable(); 718 + return NOTIFY_DONE; 719 + } 720 + 721 + static const struct file_operations wdt_fops = { 722 + .owner = THIS_MODULE, 723 + .read = wdt_read, 724 + .ioctl = wdt_ioctl, 725 + .write = wdt_write, 726 + .open = wdt_open, 727 + .release = wdt_release, 728 + }; 729 + 730 + static struct miscdevice wdt_dev = { 731 + .minor = WATCHDOG_MINOR, 732 + .name = "watchdog", 733 + .fops = &wdt_fops, 734 + }; 735 + 736 + /* 737 + * The WDT card needs to learn about soft shutdowns in order to 738 + * turn the timebomb registers off. 739 + */ 740 + static struct notifier_block wdt_notifier = { 741 + .notifier_call = wdt_notify_sys, 742 + }; 743 + #endif /* CONFIG_RTC_DRV_M41T80_WDT */ 744 + 490 745 /* 491 746 ***************************************************************************** 492 747 * ··· 841 572 if (rc) 842 573 goto exit; 843 574 575 + #ifdef CONFIG_RTC_DRV_M41T80_WDT 576 + if (chip->features & M41T80_FEATURE_HT) { 577 + rc = misc_register(&wdt_dev); 578 + if (rc) 579 + goto exit; 580 + rc = register_reboot_notifier(&wdt_notifier); 581 + if (rc) { 582 + misc_deregister(&wdt_dev); 583 + goto exit; 584 + } 585 + save_client = client; 586 + } 587 + #endif 844 588 return 0; 845 589 846 590 st_err: ··· 877 595 struct m41t80_data *clientdata = i2c_get_clientdata(client); 878 596 struct rtc_device *rtc = clientdata->rtc; 879 597 598 + #ifdef CONFIG_RTC_DRV_M41T80_WDT 599 + if (clientdata->chip->features & M41T80_FEATURE_HT) { 600 + misc_deregister(&wdt_dev); 601 + unregister_reboot_notifier(&wdt_notifier); 602 + } 603 + #endif 880 604 if (rtc) 881 605 rtc_device_unregister(rtc); 882 606 kfree(clientdata);