advantech_ec_wdt.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Advantech Embedded Controller Watchdog Driver
  4. *
  5. * This driver supports Advantech products with ITE based Embedded Controller.
  6. * It does not support Advantech products with other ECs or without EC.
  7. *
  8. * Copyright (C) 2022 Advantech Europe B.V.
  9. */
  10. #include <linux/delay.h>
  11. #include <linux/io.h>
  12. #include <linux/isa.h>
  13. #include <linux/kernel.h>
  14. #include <linux/module.h>
  15. #include <linux/moduleparam.h>
  16. #include <linux/watchdog.h>
  17. #define DRIVER_NAME "advantech_ec_wdt"
  18. /* EC IO region */
  19. #define EC_BASE_ADDR 0x299
  20. #define EC_ADDR_EXTENT 2
  21. /* EC minimum IO access delay in ms */
  22. #define EC_MIN_DELAY 10
  23. /* EC interface definitions */
  24. #define EC_ADDR_CMD (EC_BASE_ADDR + 1)
  25. #define EC_ADDR_DATA EC_BASE_ADDR
  26. #define EC_CMD_EC_PROBE 0x30
  27. #define EC_CMD_COMM 0x89
  28. #define EC_CMD_WDT_START 0x28
  29. #define EC_CMD_WDT_STOP 0x29
  30. #define EC_CMD_WDT_RESET 0x2A
  31. #define EC_DAT_EN_DLY_H 0x58
  32. #define EC_DAT_EN_DLY_L 0x59
  33. #define EC_DAT_RST_DLY_H 0x5E
  34. #define EC_DAT_RST_DLY_L 0x5F
  35. #define EC_MAGIC 0x95
  36. /* module parameters */
  37. #define MIN_TIME 1
  38. #define MAX_TIME 6000 /* 100 minutes */
  39. #define DEFAULT_TIME 60
  40. static unsigned int timeout;
  41. static ktime_t ec_timestamp;
  42. module_param(timeout, uint, 0);
  43. MODULE_PARM_DESC(timeout,
  44. "Default Watchdog timer setting (" __MODULE_STRING(DEFAULT_TIME) "s). The range is from " __MODULE_STRING(MIN_TIME) " to " __MODULE_STRING(MAX_TIME) ".");
  45. static void adv_ec_wdt_timing_gate(void)
  46. {
  47. ktime_t time_cur, time_delta;
  48. /* ensure minimum delay between IO accesses*/
  49. time_cur = ktime_get();
  50. time_delta = ktime_to_ms(ktime_sub(time_cur, ec_timestamp));
  51. if (time_delta < EC_MIN_DELAY) {
  52. time_delta = EC_MIN_DELAY - time_delta;
  53. usleep_range(time_delta * 1000, (time_delta + 1) * 1000);
  54. }
  55. ec_timestamp = ktime_get();
  56. }
  57. static void adv_ec_wdt_outb(unsigned char value, unsigned short port)
  58. {
  59. adv_ec_wdt_timing_gate();
  60. outb(value, port);
  61. }
  62. static unsigned char adv_ec_wdt_inb(unsigned short port)
  63. {
  64. adv_ec_wdt_timing_gate();
  65. return inb(port);
  66. }
  67. static int adv_ec_wdt_ping(struct watchdog_device *wdd)
  68. {
  69. adv_ec_wdt_outb(EC_CMD_WDT_RESET, EC_ADDR_CMD);
  70. return 0;
  71. }
  72. static int adv_ec_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
  73. {
  74. unsigned int val;
  75. /* scale time to EC 100 ms base */
  76. val = t * 10;
  77. /* reset enable delay, just in case it was set by BIOS etc. */
  78. adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
  79. adv_ec_wdt_outb(EC_DAT_EN_DLY_H, EC_ADDR_DATA);
  80. adv_ec_wdt_outb(0, EC_ADDR_DATA);
  81. adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
  82. adv_ec_wdt_outb(EC_DAT_EN_DLY_L, EC_ADDR_DATA);
  83. adv_ec_wdt_outb(0, EC_ADDR_DATA);
  84. /* set reset delay */
  85. adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
  86. adv_ec_wdt_outb(EC_DAT_RST_DLY_H, EC_ADDR_DATA);
  87. adv_ec_wdt_outb(val >> 8, EC_ADDR_DATA);
  88. adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
  89. adv_ec_wdt_outb(EC_DAT_RST_DLY_L, EC_ADDR_DATA);
  90. adv_ec_wdt_outb(val & 0xFF, EC_ADDR_DATA);
  91. wdd->timeout = t;
  92. return 0;
  93. }
  94. static int adv_ec_wdt_start(struct watchdog_device *wdd)
  95. {
  96. adv_ec_wdt_set_timeout(wdd, wdd->timeout);
  97. adv_ec_wdt_outb(EC_CMD_WDT_START, EC_ADDR_CMD);
  98. return 0;
  99. }
  100. static int adv_ec_wdt_stop(struct watchdog_device *wdd)
  101. {
  102. adv_ec_wdt_outb(EC_CMD_WDT_STOP, EC_ADDR_CMD);
  103. return 0;
  104. }
  105. static const struct watchdog_info adv_ec_wdt_info = {
  106. .identity = DRIVER_NAME,
  107. .options = WDIOF_SETTIMEOUT |
  108. WDIOF_MAGICCLOSE |
  109. WDIOF_KEEPALIVEPING,
  110. };
  111. static const struct watchdog_ops adv_ec_wdt_ops = {
  112. .owner = THIS_MODULE,
  113. .start = adv_ec_wdt_start,
  114. .stop = adv_ec_wdt_stop,
  115. .ping = adv_ec_wdt_ping,
  116. .set_timeout = adv_ec_wdt_set_timeout,
  117. };
  118. static struct watchdog_device adv_ec_wdt_dev = {
  119. .info = &adv_ec_wdt_info,
  120. .ops = &adv_ec_wdt_ops,
  121. .min_timeout = MIN_TIME,
  122. .max_timeout = MAX_TIME,
  123. .timeout = DEFAULT_TIME,
  124. };
  125. static int adv_ec_wdt_probe(struct device *dev, unsigned int id)
  126. {
  127. if (!devm_request_region(dev, EC_BASE_ADDR, EC_ADDR_EXTENT, dev_name(dev))) {
  128. dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
  129. EC_BASE_ADDR, EC_BASE_ADDR + EC_ADDR_EXTENT);
  130. return -EBUSY;
  131. }
  132. watchdog_init_timeout(&adv_ec_wdt_dev, timeout, dev);
  133. watchdog_stop_on_reboot(&adv_ec_wdt_dev);
  134. watchdog_stop_on_unregister(&adv_ec_wdt_dev);
  135. return devm_watchdog_register_device(dev, &adv_ec_wdt_dev);
  136. }
  137. static struct isa_driver adv_ec_wdt_driver = {
  138. .probe = adv_ec_wdt_probe,
  139. .driver = {
  140. .name = DRIVER_NAME,
  141. },
  142. };
  143. static int __init adv_ec_wdt_init(void)
  144. {
  145. unsigned int val;
  146. /* quick probe for EC */
  147. if (!request_region(EC_BASE_ADDR, EC_ADDR_EXTENT, DRIVER_NAME))
  148. return -EBUSY;
  149. adv_ec_wdt_outb(EC_CMD_EC_PROBE, EC_ADDR_CMD);
  150. val = adv_ec_wdt_inb(EC_ADDR_DATA);
  151. release_region(EC_BASE_ADDR, EC_ADDR_EXTENT);
  152. if (val != EC_MAGIC)
  153. return -ENODEV;
  154. return isa_register_driver(&adv_ec_wdt_driver, 1);
  155. }
  156. static void __exit adv_ec_wdt_exit(void)
  157. {
  158. isa_unregister_driver(&adv_ec_wdt_driver);
  159. }
  160. module_init(adv_ec_wdt_init);
  161. module_exit(adv_ec_wdt_exit);
  162. MODULE_AUTHOR("Thomas Kastner <thomas.kastner@advantech.com>");
  163. MODULE_DESCRIPTION("Advantech Embedded Controller Watchdog Device Driver");
  164. MODULE_LICENSE("GPL");
  165. MODULE_VERSION("20221019");
  166. MODULE_ALIAS("isa:" DRIVER_NAME);