hpwdt.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * HPE WatchDog Driver
  3. * based on
  4. *
  5. * SoftDog 0.05: A Software Watchdog Device
  6. *
  7. * (c) Copyright 2018 Hewlett Packard Enterprise Development LP
  8. * Thomas Mingarelli <thomas.mingarelli@hpe.com>
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * version 2 as published by the Free Software Foundation
  13. *
  14. */
  15. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  16. #include <linux/device.h>
  17. #include <linux/io.h>
  18. #include <linux/kernel.h>
  19. #include <linux/module.h>
  20. #include <linux/moduleparam.h>
  21. #include <linux/pci.h>
  22. #include <linux/pci_ids.h>
  23. #include <linux/types.h>
  24. #include <linux/watchdog.h>
  25. #include <asm/nmi.h>
  26. #define HPWDT_VERSION "2.0.0"
  27. #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
  28. #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
  29. #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
  30. #define DEFAULT_MARGIN 30
  31. #define PRETIMEOUT_SEC 9
  32. static bool ilo5;
  33. static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
  34. static bool nowayout = WATCHDOG_NOWAYOUT;
  35. static bool pretimeout = IS_ENABLED(CONFIG_HPWDT_NMI_DECODING);
  36. static void __iomem *pci_mem_addr; /* the PCI-memory address */
  37. static unsigned long __iomem *hpwdt_nmistat;
  38. static unsigned long __iomem *hpwdt_timer_reg;
  39. static unsigned long __iomem *hpwdt_timer_con;
  40. static const struct pci_device_id hpwdt_devices[] = {
  41. { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */
  42. { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */
  43. {0}, /* terminate list */
  44. };
  45. MODULE_DEVICE_TABLE(pci, hpwdt_devices);
  46. /*
  47. * Watchdog operations
  48. */
  49. static int hpwdt_start(struct watchdog_device *wdd)
  50. {
  51. int control = 0x81 | (pretimeout ? 0x4 : 0);
  52. int reload = SECS_TO_TICKS(wdd->timeout);
  53. dev_dbg(wdd->parent, "start watchdog 0x%08x:0x%02x\n", reload, control);
  54. iowrite16(reload, hpwdt_timer_reg);
  55. iowrite8(control, hpwdt_timer_con);
  56. return 0;
  57. }
  58. static void hpwdt_stop(void)
  59. {
  60. unsigned long data;
  61. pr_debug("stop watchdog\n");
  62. data = ioread8(hpwdt_timer_con);
  63. data &= 0xFE;
  64. iowrite8(data, hpwdt_timer_con);
  65. }
  66. static int hpwdt_stop_core(struct watchdog_device *wdd)
  67. {
  68. hpwdt_stop();
  69. return 0;
  70. }
  71. static int hpwdt_ping(struct watchdog_device *wdd)
  72. {
  73. int reload = SECS_TO_TICKS(wdd->timeout);
  74. dev_dbg(wdd->parent, "ping watchdog 0x%08x\n", reload);
  75. iowrite16(reload, hpwdt_timer_reg);
  76. return 0;
  77. }
  78. static unsigned int hpwdt_gettimeleft(struct watchdog_device *wdd)
  79. {
  80. return TICKS_TO_SECS(ioread16(hpwdt_timer_reg));
  81. }
  82. static int hpwdt_settimeout(struct watchdog_device *wdd, unsigned int val)
  83. {
  84. dev_dbg(wdd->parent, "set_timeout = %d\n", val);
  85. wdd->timeout = val;
  86. if (val <= wdd->pretimeout) {
  87. dev_dbg(wdd->parent, "pretimeout < timeout. Setting to zero\n");
  88. wdd->pretimeout = 0;
  89. pretimeout = 0;
  90. if (watchdog_active(wdd))
  91. hpwdt_start(wdd);
  92. }
  93. hpwdt_ping(wdd);
  94. return 0;
  95. }
  96. #ifdef CONFIG_HPWDT_NMI_DECODING
  97. static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req)
  98. {
  99. unsigned int val = 0;
  100. dev_dbg(wdd->parent, "set_pretimeout = %d\n", req);
  101. if (req) {
  102. val = PRETIMEOUT_SEC;
  103. if (val >= wdd->timeout)
  104. return -EINVAL;
  105. }
  106. if (val != req)
  107. dev_dbg(wdd->parent, "Rounding pretimeout to: %d\n", val);
  108. wdd->pretimeout = val;
  109. pretimeout = !!val;
  110. if (watchdog_active(wdd))
  111. hpwdt_start(wdd);
  112. return 0;
  113. }
  114. static int hpwdt_my_nmi(void)
  115. {
  116. return ioread8(hpwdt_nmistat) & 0x6;
  117. }
  118. /*
  119. * NMI Handler
  120. */
  121. static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
  122. {
  123. unsigned int mynmi = hpwdt_my_nmi();
  124. static char panic_msg[] =
  125. "00: An NMI occurred. Depending on your system the reason "
  126. "for the NMI is logged in any one of the following resources:\n"
  127. "1. Integrated Management Log (IML)\n"
  128. "2. OA Syslog\n"
  129. "3. OA Forward Progress Log\n"
  130. "4. iLO Event Log";
  131. if (ilo5 && ulReason == NMI_UNKNOWN && !mynmi)
  132. return NMI_DONE;
  133. if (ilo5 && !pretimeout)
  134. return NMI_DONE;
  135. hpwdt_stop();
  136. hex_byte_pack(panic_msg, mynmi);
  137. nmi_panic(regs, panic_msg);
  138. return NMI_HANDLED;
  139. }
  140. #endif /* CONFIG_HPWDT_NMI_DECODING */
  141. static const struct watchdog_info ident = {
  142. .options = WDIOF_PRETIMEOUT |
  143. WDIOF_SETTIMEOUT |
  144. WDIOF_KEEPALIVEPING |
  145. WDIOF_MAGICCLOSE,
  146. .identity = "HPE iLO2+ HW Watchdog Timer",
  147. };
  148. /*
  149. * Kernel interfaces
  150. */
  151. static const struct watchdog_ops hpwdt_ops = {
  152. .owner = THIS_MODULE,
  153. .start = hpwdt_start,
  154. .stop = hpwdt_stop_core,
  155. .ping = hpwdt_ping,
  156. .set_timeout = hpwdt_settimeout,
  157. .get_timeleft = hpwdt_gettimeleft,
  158. #ifdef CONFIG_HPWDT_NMI_DECODING
  159. .set_pretimeout = hpwdt_set_pretimeout,
  160. #endif
  161. };
  162. static struct watchdog_device hpwdt_dev = {
  163. .info = &ident,
  164. .ops = &hpwdt_ops,
  165. .min_timeout = 1,
  166. .max_timeout = HPWDT_MAX_TIMER,
  167. .timeout = DEFAULT_MARGIN,
  168. #ifdef CONFIG_HPWDT_NMI_DECODING
  169. .pretimeout = PRETIMEOUT_SEC,
  170. #endif
  171. };
  172. /*
  173. * Init & Exit
  174. */
  175. static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
  176. {
  177. #ifdef CONFIG_HPWDT_NMI_DECODING
  178. int retval;
  179. /*
  180. * Only one function can register for NMI_UNKNOWN
  181. */
  182. retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt");
  183. if (retval)
  184. goto error;
  185. retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt");
  186. if (retval)
  187. goto error1;
  188. retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt");
  189. if (retval)
  190. goto error2;
  191. dev_info(&dev->dev,
  192. "HPE Watchdog Timer Driver: NMI decoding initialized\n");
  193. return 0;
  194. error2:
  195. unregister_nmi_handler(NMI_SERR, "hpwdt");
  196. error1:
  197. unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
  198. error:
  199. dev_warn(&dev->dev,
  200. "Unable to register a die notifier (err=%d).\n",
  201. retval);
  202. return retval;
  203. #endif /* CONFIG_HPWDT_NMI_DECODING */
  204. return 0;
  205. }
  206. static void hpwdt_exit_nmi_decoding(void)
  207. {
  208. #ifdef CONFIG_HPWDT_NMI_DECODING
  209. unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
  210. unregister_nmi_handler(NMI_SERR, "hpwdt");
  211. unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
  212. #endif
  213. }
  214. static int hpwdt_init_one(struct pci_dev *dev,
  215. const struct pci_device_id *ent)
  216. {
  217. int retval;
  218. /*
  219. * First let's find out if we are on an iLO2+ server. We will
  220. * not run on a legacy ASM box.
  221. * So we only support the G5 ProLiant servers and higher.
  222. */
  223. if (dev->subsystem_vendor != PCI_VENDOR_ID_HP &&
  224. dev->subsystem_vendor != PCI_VENDOR_ID_HP_3PAR) {
  225. dev_warn(&dev->dev,
  226. "This server does not have an iLO2+ ASIC.\n");
  227. return -ENODEV;
  228. }
  229. /*
  230. * Ignore all auxilary iLO devices with the following PCI ID
  231. */
  232. if (dev->subsystem_vendor == PCI_VENDOR_ID_HP &&
  233. dev->subsystem_device == 0x1979)
  234. return -ENODEV;
  235. if (pci_enable_device(dev)) {
  236. dev_warn(&dev->dev,
  237. "Not possible to enable PCI Device: 0x%x:0x%x.\n",
  238. ent->vendor, ent->device);
  239. return -ENODEV;
  240. }
  241. pci_mem_addr = pci_iomap(dev, 1, 0x80);
  242. if (!pci_mem_addr) {
  243. dev_warn(&dev->dev,
  244. "Unable to detect the iLO2+ server memory.\n");
  245. retval = -ENOMEM;
  246. goto error_pci_iomap;
  247. }
  248. hpwdt_nmistat = pci_mem_addr + 0x6e;
  249. hpwdt_timer_reg = pci_mem_addr + 0x70;
  250. hpwdt_timer_con = pci_mem_addr + 0x72;
  251. /* Make sure that timer is disabled until /dev/watchdog is opened */
  252. hpwdt_stop();
  253. /* Initialize NMI Decoding functionality */
  254. retval = hpwdt_init_nmi_decoding(dev);
  255. if (retval != 0)
  256. goto error_init_nmi_decoding;
  257. watchdog_set_nowayout(&hpwdt_dev, nowayout);
  258. if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL))
  259. dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin);
  260. hpwdt_dev.parent = &dev->dev;
  261. retval = watchdog_register_device(&hpwdt_dev);
  262. if (retval < 0) {
  263. dev_err(&dev->dev, "watchdog register failed: %d.\n", retval);
  264. goto error_wd_register;
  265. }
  266. dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s"
  267. ", timer margin: %d seconds (nowayout=%d).\n",
  268. HPWDT_VERSION, hpwdt_dev.timeout, nowayout);
  269. if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR)
  270. ilo5 = true;
  271. return 0;
  272. error_wd_register:
  273. hpwdt_exit_nmi_decoding();
  274. error_init_nmi_decoding:
  275. pci_iounmap(dev, pci_mem_addr);
  276. error_pci_iomap:
  277. pci_disable_device(dev);
  278. return retval;
  279. }
  280. static void hpwdt_exit(struct pci_dev *dev)
  281. {
  282. if (!nowayout)
  283. hpwdt_stop();
  284. watchdog_unregister_device(&hpwdt_dev);
  285. hpwdt_exit_nmi_decoding();
  286. pci_iounmap(dev, pci_mem_addr);
  287. pci_disable_device(dev);
  288. }
  289. static struct pci_driver hpwdt_driver = {
  290. .name = "hpwdt",
  291. .id_table = hpwdt_devices,
  292. .probe = hpwdt_init_one,
  293. .remove = hpwdt_exit,
  294. };
  295. MODULE_AUTHOR("Tom Mingarelli");
  296. MODULE_DESCRIPTION("hpe watchdog driver");
  297. MODULE_LICENSE("GPL");
  298. MODULE_VERSION(HPWDT_VERSION);
  299. module_param(soft_margin, int, 0);
  300. MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
  301. module_param(nowayout, bool, 0);
  302. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  303. __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  304. #ifdef CONFIG_HPWDT_NMI_DECODING
  305. module_param(pretimeout, bool, 0);
  306. MODULE_PARM_DESC(pretimeout, "Watchdog pretimeout enabled");
  307. #endif
  308. module_pci_driver(hpwdt_driver);