1 // SPDX-License-Identifier: GPL-2.0
3 * Siemens SIMATIC IPC platform driver
5 * Copyright (c) Siemens AG, 2018-2023
13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15 #include <linux/dmi.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/platform_data/x86/simatic-ipc.h>
19 #include <linux/platform_device.h>
21 static struct platform_device *ipc_led_platform_device;
22 static struct platform_device *ipc_wdt_platform_device;
23 static struct platform_device *ipc_batt_platform_device;
25 static const struct dmi_system_id simatic_ipc_whitelist[] = {
28 DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
34 static struct simatic_ipc_platform platform_data;
36 #define SIMATIC_IPC_MAX_EXTRA_MODULES 2
43 char *extra_modules[SIMATIC_IPC_MAX_EXTRA_MODULES];
46 SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_127E,
49 SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE,
52 SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E,
55 SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G,
56 { "nct6775", "w83627hf_wdt" }},
58 SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G,
59 { "nct6775", "w83627hf_wdt" }},
61 SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E,
64 SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE,
67 SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE,
70 SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE,
72 {SIMATIC_IPC_IPCBX_39A,
73 SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_39A,
74 { "nct6775", "w83627hf_wdt" }},
75 {SIMATIC_IPC_IPCPX_39A,
76 SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_39A,
77 { "nct6775", "w83627hf_wdt" }},
78 {SIMATIC_IPC_IPCBX_21A,
79 SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A,
81 {SIMATIC_IPC_IPCBX_56A,
82 SIMATIC_IPC_DEVICE_BX_59A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_59A,
83 { "emc1403", "w83627hf_wdt" }},
84 {SIMATIC_IPC_IPCBX_59A,
85 SIMATIC_IPC_DEVICE_BX_59A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_59A,
86 { "emc1403", "w83627hf_wdt" }},
89 static int register_platform_devices(u32 station_id)
91 u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
92 u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
93 u8 battmode = SIMATIC_IPC_DEVICE_NONE;
97 for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
98 if (device_modes[i].station_id == station_id) {
99 ledmode = device_modes[i].led_mode;
100 wdtmode = device_modes[i].wdt_mode;
101 battmode = device_modes[i].batt_mode;
106 if (battmode != SIMATIC_IPC_DEVICE_NONE) {
107 pdevname = KBUILD_MODNAME "_batt";
108 if (battmode == SIMATIC_IPC_DEVICE_127E)
109 pdevname = KBUILD_MODNAME "_batt_apollolake";
110 if (battmode == SIMATIC_IPC_DEVICE_BX_21A)
111 pdevname = KBUILD_MODNAME "_batt_elkhartlake";
112 if (battmode == SIMATIC_IPC_DEVICE_227G ||
113 battmode == SIMATIC_IPC_DEVICE_BX_39A ||
114 battmode == SIMATIC_IPC_DEVICE_BX_59A)
115 pdevname = KBUILD_MODNAME "_batt_f7188x";
116 platform_data.devmode = battmode;
117 ipc_batt_platform_device =
118 platform_device_register_data(NULL, pdevname,
119 PLATFORM_DEVID_NONE, &platform_data,
120 sizeof(struct simatic_ipc_platform));
121 if (IS_ERR(ipc_batt_platform_device))
122 return PTR_ERR(ipc_batt_platform_device);
124 pr_debug("device=%s created\n",
125 ipc_batt_platform_device->name);
128 if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
129 pdevname = KBUILD_MODNAME "_leds";
130 if (ledmode == SIMATIC_IPC_DEVICE_127E)
131 pdevname = KBUILD_MODNAME "_leds_gpio_apollolake";
132 if (ledmode == SIMATIC_IPC_DEVICE_227G || ledmode == SIMATIC_IPC_DEVICE_BX_59A)
133 pdevname = KBUILD_MODNAME "_leds_gpio_f7188x";
134 if (ledmode == SIMATIC_IPC_DEVICE_BX_21A)
135 pdevname = KBUILD_MODNAME "_leds_gpio_elkhartlake";
136 platform_data.devmode = ledmode;
137 ipc_led_platform_device =
138 platform_device_register_data(NULL,
139 pdevname, PLATFORM_DEVID_NONE,
141 sizeof(struct simatic_ipc_platform));
142 if (IS_ERR(ipc_led_platform_device))
143 return PTR_ERR(ipc_led_platform_device);
145 pr_debug("device=%s created\n",
146 ipc_led_platform_device->name);
149 if (wdtmode != SIMATIC_IPC_DEVICE_NONE) {
150 platform_data.devmode = wdtmode;
151 ipc_wdt_platform_device =
152 platform_device_register_data(NULL,
153 KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
155 sizeof(struct simatic_ipc_platform));
156 if (IS_ERR(ipc_wdt_platform_device))
157 return PTR_ERR(ipc_wdt_platform_device);
159 pr_debug("device=%s created\n",
160 ipc_wdt_platform_device->name);
163 if (ledmode == SIMATIC_IPC_DEVICE_NONE &&
164 wdtmode == SIMATIC_IPC_DEVICE_NONE &&
165 battmode == SIMATIC_IPC_DEVICE_NONE) {
166 pr_warn("unsupported IPC detected, station id=%08x\n",
174 static void request_additional_modules(u32 station_id)
176 char **extra_modules = NULL;
179 for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
180 if (device_modes[i].station_id == station_id) {
181 extra_modules = device_modes[i].extra_modules;
189 for (i = 0; i < SIMATIC_IPC_MAX_EXTRA_MODULES; i++) {
190 if (extra_modules[i])
191 request_module(extra_modules[i]);
197 static int __init simatic_ipc_init_module(void)
199 const struct dmi_system_id *match;
203 match = dmi_first_match(simatic_ipc_whitelist);
207 err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id);
209 if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) {
210 pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM);
214 request_additional_modules(station_id);
216 return register_platform_devices(station_id);
219 static void __exit simatic_ipc_exit_module(void)
221 platform_device_unregister(ipc_led_platform_device);
222 ipc_led_platform_device = NULL;
224 platform_device_unregister(ipc_wdt_platform_device);
225 ipc_wdt_platform_device = NULL;
227 platform_device_unregister(ipc_batt_platform_device);
228 ipc_batt_platform_device = NULL;
231 module_init(simatic_ipc_init_module);
232 module_exit(simatic_ipc_exit_module);
234 MODULE_LICENSE("GPL v2");
236 MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");