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