19bc289b8SHenning Schild // SPDX-License-Identifier: GPL-2.0
29bc289b8SHenning Schild /*
39bc289b8SHenning Schild * Siemens SIMATIC IPC platform driver
49bc289b8SHenning Schild *
59bc289b8SHenning Schild * Copyright (c) Siemens AG, 2018-2023
69bc289b8SHenning Schild *
79bc289b8SHenning Schild * Authors:
89bc289b8SHenning Schild * Henning Schild <henning.schild@siemens.com>
99bc289b8SHenning Schild * Jan Kiszka <jan.kiszka@siemens.com>
109bc289b8SHenning Schild * Gerd Haeussler <gerd.haeussler.ext@siemens.com>
119bc289b8SHenning Schild */
129bc289b8SHenning Schild
139bc289b8SHenning Schild #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
149bc289b8SHenning Schild
159bc289b8SHenning Schild #include <linux/dmi.h>
169bc289b8SHenning Schild #include <linux/kernel.h>
179bc289b8SHenning Schild #include <linux/module.h>
189bc289b8SHenning Schild #include <linux/platform_data/x86/simatic-ipc.h>
199bc289b8SHenning Schild #include <linux/platform_device.h>
209bc289b8SHenning Schild
219bc289b8SHenning Schild static struct platform_device *ipc_led_platform_device;
229bc289b8SHenning Schild static struct platform_device *ipc_wdt_platform_device;
239bc289b8SHenning Schild static struct platform_device *ipc_batt_platform_device;
249bc289b8SHenning Schild
259bc289b8SHenning Schild static const struct dmi_system_id simatic_ipc_whitelist[] = {
269bc289b8SHenning Schild {
279bc289b8SHenning Schild .matches = {
289bc289b8SHenning Schild DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
299bc289b8SHenning Schild },
309bc289b8SHenning Schild },
319bc289b8SHenning Schild {}
329bc289b8SHenning Schild };
339bc289b8SHenning Schild
349bc289b8SHenning Schild static struct simatic_ipc_platform platform_data;
359bc289b8SHenning Schild
369bc289b8SHenning Schild #define SIMATIC_IPC_MAX_EXTRA_MODULES 2
379bc289b8SHenning Schild
389bc289b8SHenning Schild static struct {
399bc289b8SHenning Schild u32 station_id;
409bc289b8SHenning Schild u8 led_mode;
419bc289b8SHenning Schild u8 wdt_mode;
429bc289b8SHenning Schild u8 batt_mode;
439bc289b8SHenning Schild char *extra_modules[SIMATIC_IPC_MAX_EXTRA_MODULES];
449bc289b8SHenning Schild } device_modes[] = {
459bc289b8SHenning Schild {SIMATIC_IPC_IPC127E,
469bc289b8SHenning Schild SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_127E,
479bc289b8SHenning Schild { "emc1403", NULL }},
489bc289b8SHenning Schild {SIMATIC_IPC_IPC227D,
499bc289b8SHenning Schild SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE,
509bc289b8SHenning Schild { "emc1403", NULL }},
519bc289b8SHenning Schild {SIMATIC_IPC_IPC227E,
529bc289b8SHenning Schild SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E,
539bc289b8SHenning Schild { "emc1403", NULL }},
549bc289b8SHenning Schild {SIMATIC_IPC_IPC227G,
559bc289b8SHenning Schild SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G,
569bc289b8SHenning Schild { "nct6775", "w83627hf_wdt" }},
579bc289b8SHenning Schild {SIMATIC_IPC_IPC277G,
589bc289b8SHenning Schild SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G,
599bc289b8SHenning Schild { "nct6775", "w83627hf_wdt" }},
609bc289b8SHenning Schild {SIMATIC_IPC_IPC277E,
619bc289b8SHenning Schild SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E,
629bc289b8SHenning Schild { "emc1403", NULL }},
639bc289b8SHenning Schild {SIMATIC_IPC_IPC427D,
649bc289b8SHenning Schild SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE,
659bc289b8SHenning Schild { "emc1403", NULL }},
669bc289b8SHenning Schild {SIMATIC_IPC_IPC427E,
679bc289b8SHenning Schild SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE,
689bc289b8SHenning Schild { "emc1403", NULL }},
699bc289b8SHenning Schild {SIMATIC_IPC_IPC477E,
709bc289b8SHenning Schild SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE,
719bc289b8SHenning Schild { "emc1403", NULL }},
729bc289b8SHenning Schild {SIMATIC_IPC_IPCBX_39A,
739bc289b8SHenning Schild SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_39A,
749bc289b8SHenning Schild { "nct6775", "w83627hf_wdt" }},
759bc289b8SHenning Schild {SIMATIC_IPC_IPCPX_39A,
769bc289b8SHenning Schild SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_39A,
779bc289b8SHenning Schild { "nct6775", "w83627hf_wdt" }},
789bc289b8SHenning Schild {SIMATIC_IPC_IPCBX_21A,
799bc289b8SHenning Schild SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A,
809bc289b8SHenning Schild { "emc1403", NULL }},
81b8af7795Sxingtong.wu {SIMATIC_IPC_IPCBX_56A,
82b8af7795Sxingtong.wu SIMATIC_IPC_DEVICE_BX_59A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_59A,
83b8af7795Sxingtong.wu { "emc1403", "w83627hf_wdt" }},
84b8af7795Sxingtong.wu {SIMATIC_IPC_IPCBX_59A,
85b8af7795Sxingtong.wu SIMATIC_IPC_DEVICE_BX_59A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_59A,
86b8af7795Sxingtong.wu { "emc1403", "w83627hf_wdt" }},
879bc289b8SHenning Schild };
889bc289b8SHenning Schild
register_platform_devices(u32 station_id)899bc289b8SHenning Schild static int register_platform_devices(u32 station_id)
909bc289b8SHenning Schild {
919bc289b8SHenning Schild u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
929bc289b8SHenning Schild u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
939bc289b8SHenning Schild u8 battmode = SIMATIC_IPC_DEVICE_NONE;
949bc289b8SHenning Schild char *pdevname;
959bc289b8SHenning Schild int i;
969bc289b8SHenning Schild
979bc289b8SHenning Schild for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
989bc289b8SHenning Schild if (device_modes[i].station_id == station_id) {
999bc289b8SHenning Schild ledmode = device_modes[i].led_mode;
1009bc289b8SHenning Schild wdtmode = device_modes[i].wdt_mode;
1019bc289b8SHenning Schild battmode = device_modes[i].batt_mode;
1029bc289b8SHenning Schild break;
1039bc289b8SHenning Schild }
1049bc289b8SHenning Schild }
1059bc289b8SHenning Schild
1069bc289b8SHenning Schild if (battmode != SIMATIC_IPC_DEVICE_NONE) {
1079bc289b8SHenning Schild pdevname = KBUILD_MODNAME "_batt";
1089bc289b8SHenning Schild if (battmode == SIMATIC_IPC_DEVICE_127E)
1099bc289b8SHenning Schild pdevname = KBUILD_MODNAME "_batt_apollolake";
1109bc289b8SHenning Schild if (battmode == SIMATIC_IPC_DEVICE_BX_21A)
1119bc289b8SHenning Schild pdevname = KBUILD_MODNAME "_batt_elkhartlake";
112b8af7795Sxingtong.wu if (battmode == SIMATIC_IPC_DEVICE_227G ||
113b8af7795Sxingtong.wu battmode == SIMATIC_IPC_DEVICE_BX_39A ||
114b8af7795Sxingtong.wu battmode == SIMATIC_IPC_DEVICE_BX_59A)
1159bc289b8SHenning Schild pdevname = KBUILD_MODNAME "_batt_f7188x";
1169bc289b8SHenning Schild platform_data.devmode = battmode;
1179bc289b8SHenning Schild ipc_batt_platform_device =
1189bc289b8SHenning Schild platform_device_register_data(NULL, pdevname,
1199bc289b8SHenning Schild PLATFORM_DEVID_NONE, &platform_data,
1209bc289b8SHenning Schild sizeof(struct simatic_ipc_platform));
1219bc289b8SHenning Schild if (IS_ERR(ipc_batt_platform_device))
1229bc289b8SHenning Schild return PTR_ERR(ipc_batt_platform_device);
1239bc289b8SHenning Schild
1249bc289b8SHenning Schild pr_debug("device=%s created\n",
1259bc289b8SHenning Schild ipc_batt_platform_device->name);
1269bc289b8SHenning Schild }
1279bc289b8SHenning Schild
1289bc289b8SHenning Schild if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
1299bc289b8SHenning Schild pdevname = KBUILD_MODNAME "_leds";
1309bc289b8SHenning Schild if (ledmode == SIMATIC_IPC_DEVICE_127E)
1319bc289b8SHenning Schild pdevname = KBUILD_MODNAME "_leds_gpio_apollolake";
132*b01c1e02Sxingtong.wu if (ledmode == SIMATIC_IPC_DEVICE_227G || ledmode == SIMATIC_IPC_DEVICE_BX_59A)
1339bc289b8SHenning Schild pdevname = KBUILD_MODNAME "_leds_gpio_f7188x";
1349bc289b8SHenning Schild if (ledmode == SIMATIC_IPC_DEVICE_BX_21A)
1359bc289b8SHenning Schild pdevname = KBUILD_MODNAME "_leds_gpio_elkhartlake";
1369bc289b8SHenning Schild platform_data.devmode = ledmode;
1379bc289b8SHenning Schild ipc_led_platform_device =
1389bc289b8SHenning Schild platform_device_register_data(NULL,
1399bc289b8SHenning Schild pdevname, PLATFORM_DEVID_NONE,
1409bc289b8SHenning Schild &platform_data,
1419bc289b8SHenning Schild sizeof(struct simatic_ipc_platform));
1429bc289b8SHenning Schild if (IS_ERR(ipc_led_platform_device))
1439bc289b8SHenning Schild return PTR_ERR(ipc_led_platform_device);
1449bc289b8SHenning Schild
1459bc289b8SHenning Schild pr_debug("device=%s created\n",
1469bc289b8SHenning Schild ipc_led_platform_device->name);
1479bc289b8SHenning Schild }
1489bc289b8SHenning Schild
1499bc289b8SHenning Schild if (wdtmode != SIMATIC_IPC_DEVICE_NONE) {
1509bc289b8SHenning Schild platform_data.devmode = wdtmode;
1519bc289b8SHenning Schild ipc_wdt_platform_device =
1529bc289b8SHenning Schild platform_device_register_data(NULL,
1539bc289b8SHenning Schild KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
1549bc289b8SHenning Schild &platform_data,
1559bc289b8SHenning Schild sizeof(struct simatic_ipc_platform));
1569bc289b8SHenning Schild if (IS_ERR(ipc_wdt_platform_device))
1579bc289b8SHenning Schild return PTR_ERR(ipc_wdt_platform_device);
1589bc289b8SHenning Schild
1599bc289b8SHenning Schild pr_debug("device=%s created\n",
1609bc289b8SHenning Schild ipc_wdt_platform_device->name);
1619bc289b8SHenning Schild }
1629bc289b8SHenning Schild
1639bc289b8SHenning Schild if (ledmode == SIMATIC_IPC_DEVICE_NONE &&
1649bc289b8SHenning Schild wdtmode == SIMATIC_IPC_DEVICE_NONE &&
1659bc289b8SHenning Schild battmode == SIMATIC_IPC_DEVICE_NONE) {
1669bc289b8SHenning Schild pr_warn("unsupported IPC detected, station id=%08x\n",
1679bc289b8SHenning Schild station_id);
1689bc289b8SHenning Schild return -EINVAL;
1699bc289b8SHenning Schild }
1709bc289b8SHenning Schild
1719bc289b8SHenning Schild return 0;
1729bc289b8SHenning Schild }
1739bc289b8SHenning Schild
request_additional_modules(u32 station_id)1749bc289b8SHenning Schild static void request_additional_modules(u32 station_id)
1759bc289b8SHenning Schild {
1769bc289b8SHenning Schild char **extra_modules = NULL;
1779bc289b8SHenning Schild int i;
1789bc289b8SHenning Schild
1799bc289b8SHenning Schild for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
1809bc289b8SHenning Schild if (device_modes[i].station_id == station_id) {
1819bc289b8SHenning Schild extra_modules = device_modes[i].extra_modules;
1829bc289b8SHenning Schild break;
1839bc289b8SHenning Schild }
1849bc289b8SHenning Schild }
1859bc289b8SHenning Schild
1869bc289b8SHenning Schild if (!extra_modules)
1879bc289b8SHenning Schild return;
1889bc289b8SHenning Schild
1899bc289b8SHenning Schild for (i = 0; i < SIMATIC_IPC_MAX_EXTRA_MODULES; i++) {
1909bc289b8SHenning Schild if (extra_modules[i])
1919bc289b8SHenning Schild request_module(extra_modules[i]);
1929bc289b8SHenning Schild else
1939bc289b8SHenning Schild break;
1949bc289b8SHenning Schild }
1959bc289b8SHenning Schild }
1969bc289b8SHenning Schild
simatic_ipc_init_module(void)1979bc289b8SHenning Schild static int __init simatic_ipc_init_module(void)
1989bc289b8SHenning Schild {
1999bc289b8SHenning Schild const struct dmi_system_id *match;
2009bc289b8SHenning Schild u32 station_id;
2019bc289b8SHenning Schild int err;
2029bc289b8SHenning Schild
2039bc289b8SHenning Schild match = dmi_first_match(simatic_ipc_whitelist);
2049bc289b8SHenning Schild if (!match)
2059bc289b8SHenning Schild return 0;
2069bc289b8SHenning Schild
2079bc289b8SHenning Schild err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id);
2089bc289b8SHenning Schild
2099bc289b8SHenning Schild if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) {
2109bc289b8SHenning Schild pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM);
2119bc289b8SHenning Schild return 0;
2129bc289b8SHenning Schild }
2139bc289b8SHenning Schild
2149bc289b8SHenning Schild request_additional_modules(station_id);
2159bc289b8SHenning Schild
2169bc289b8SHenning Schild return register_platform_devices(station_id);
2179bc289b8SHenning Schild }
2189bc289b8SHenning Schild
simatic_ipc_exit_module(void)2199bc289b8SHenning Schild static void __exit simatic_ipc_exit_module(void)
2209bc289b8SHenning Schild {
2219bc289b8SHenning Schild platform_device_unregister(ipc_led_platform_device);
2229bc289b8SHenning Schild ipc_led_platform_device = NULL;
2239bc289b8SHenning Schild
2249bc289b8SHenning Schild platform_device_unregister(ipc_wdt_platform_device);
2259bc289b8SHenning Schild ipc_wdt_platform_device = NULL;
2269bc289b8SHenning Schild
2279bc289b8SHenning Schild platform_device_unregister(ipc_batt_platform_device);
2289bc289b8SHenning Schild ipc_batt_platform_device = NULL;
2299bc289b8SHenning Schild }
2309bc289b8SHenning Schild
2319bc289b8SHenning Schild module_init(simatic_ipc_init_module);
2329bc289b8SHenning Schild module_exit(simatic_ipc_exit_module);
2339bc289b8SHenning Schild
2349bc289b8SHenning Schild MODULE_LICENSE("GPL v2");
2359bc289b8SHenning Schild MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
2369bc289b8SHenning Schild MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");
237