simatic-ipc.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Siemens SIMATIC IPC platform driver
  4. *
  5. * Copyright (c) Siemens AG, 2018-2021
  6. *
  7. * Authors:
  8. * Henning Schild <[email protected]>
  9. * Jan Kiszka <[email protected]>
  10. * Gerd Haeussler <[email protected]>
  11. */
  12. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13. #include <linux/dmi.h>
  14. #include <linux/kernel.h>
  15. #include <linux/module.h>
  16. #include <linux/pci.h>
  17. #include <linux/platform_data/x86/simatic-ipc.h>
  18. #include <linux/platform_device.h>
  19. static struct platform_device *ipc_led_platform_device;
  20. static struct platform_device *ipc_wdt_platform_device;
  21. static const struct dmi_system_id simatic_ipc_whitelist[] = {
  22. {
  23. .matches = {
  24. DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
  25. },
  26. },
  27. {}
  28. };
  29. static struct simatic_ipc_platform platform_data;
  30. static struct {
  31. u32 station_id;
  32. u8 led_mode;
  33. u8 wdt_mode;
  34. } device_modes[] = {
  35. {SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE},
  36. {SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE},
  37. {SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E},
  38. {SIMATIC_IPC_IPC227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
  39. {SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E},
  40. {SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
  41. {SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
  42. {SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
  43. {SIMATIC_IPC_IPCBX_39A, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
  44. {SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G},
  45. };
  46. static int register_platform_devices(u32 station_id)
  47. {
  48. u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
  49. u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
  50. char *pdevname = KBUILD_MODNAME "_leds";
  51. int i;
  52. platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
  53. for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
  54. if (device_modes[i].station_id == station_id) {
  55. ledmode = device_modes[i].led_mode;
  56. wdtmode = device_modes[i].wdt_mode;
  57. break;
  58. }
  59. }
  60. if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
  61. if (ledmode == SIMATIC_IPC_DEVICE_127E ||
  62. ledmode == SIMATIC_IPC_DEVICE_227G)
  63. pdevname = KBUILD_MODNAME "_leds_gpio";
  64. platform_data.devmode = ledmode;
  65. ipc_led_platform_device =
  66. platform_device_register_data(NULL,
  67. pdevname, PLATFORM_DEVID_NONE,
  68. &platform_data,
  69. sizeof(struct simatic_ipc_platform));
  70. if (IS_ERR(ipc_led_platform_device))
  71. return PTR_ERR(ipc_led_platform_device);
  72. pr_debug("device=%s created\n",
  73. ipc_led_platform_device->name);
  74. }
  75. if (wdtmode == SIMATIC_IPC_DEVICE_227G) {
  76. request_module("w83627hf_wdt");
  77. return 0;
  78. }
  79. if (wdtmode != SIMATIC_IPC_DEVICE_NONE) {
  80. platform_data.devmode = wdtmode;
  81. ipc_wdt_platform_device =
  82. platform_device_register_data(NULL,
  83. KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
  84. &platform_data,
  85. sizeof(struct simatic_ipc_platform));
  86. if (IS_ERR(ipc_wdt_platform_device))
  87. return PTR_ERR(ipc_wdt_platform_device);
  88. pr_debug("device=%s created\n",
  89. ipc_wdt_platform_device->name);
  90. }
  91. if (ledmode == SIMATIC_IPC_DEVICE_NONE &&
  92. wdtmode == SIMATIC_IPC_DEVICE_NONE) {
  93. pr_warn("unsupported IPC detected, station id=%08x\n",
  94. station_id);
  95. return -EINVAL;
  96. }
  97. return 0;
  98. }
  99. static int __init simatic_ipc_init_module(void)
  100. {
  101. const struct dmi_system_id *match;
  102. u32 station_id;
  103. int err;
  104. match = dmi_first_match(simatic_ipc_whitelist);
  105. if (!match)
  106. return 0;
  107. err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id);
  108. if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) {
  109. pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM);
  110. return 0;
  111. }
  112. return register_platform_devices(station_id);
  113. }
  114. static void __exit simatic_ipc_exit_module(void)
  115. {
  116. platform_device_unregister(ipc_led_platform_device);
  117. ipc_led_platform_device = NULL;
  118. platform_device_unregister(ipc_wdt_platform_device);
  119. ipc_wdt_platform_device = NULL;
  120. }
  121. module_init(simatic_ipc_init_module);
  122. module_exit(simatic_ipc_exit_module);
  123. MODULE_LICENSE("GPL v2");
  124. MODULE_AUTHOR("Gerd Haeussler <[email protected]>");
  125. MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");