1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Siemens SIMATIC IPC platform driver 4 * 5 * Copyright (c) Siemens AG, 2018-2023 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/platform_data/x86/simatic-ipc.h> 19 #include <linux/platform_device.h> 20 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; 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 #define SIMATIC_IPC_MAX_EXTRA_MODULES 2 37 38 static struct { 39 u32 station_id; 40 u8 led_mode; 41 u8 wdt_mode; 42 u8 batt_mode; 43 char *extra_modules[SIMATIC_IPC_MAX_EXTRA_MODULES]; 44 } device_modes[] = { 45 {SIMATIC_IPC_IPC127E, 46 SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_127E, 47 { "emc1403", NULL }}, 48 {SIMATIC_IPC_IPC227D, 49 SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, 50 { "emc1403", NULL }}, 51 {SIMATIC_IPC_IPC227E, 52 SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E, 53 { "emc1403", NULL }}, 54 {SIMATIC_IPC_IPC227G, 55 SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G, 56 { "nct6775", "w83627hf_wdt" }}, 57 {SIMATIC_IPC_IPC277G, 58 SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G, 59 { "nct6775", "w83627hf_wdt" }}, 60 {SIMATIC_IPC_IPC277E, 61 SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E, 62 { "emc1403", NULL }}, 63 {SIMATIC_IPC_IPC427D, 64 SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, 65 { "emc1403", NULL }}, 66 {SIMATIC_IPC_IPC427E, 67 SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, 68 { "emc1403", NULL }}, 69 {SIMATIC_IPC_IPC477E, 70 SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, 71 { "emc1403", NULL }}, 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, 80 { "emc1403", NULL }}, 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" }}, 87 }; 88 89 static int register_platform_devices(u32 station_id) 90 { 91 u8 ledmode = SIMATIC_IPC_DEVICE_NONE; 92 u8 wdtmode = SIMATIC_IPC_DEVICE_NONE; 93 u8 battmode = SIMATIC_IPC_DEVICE_NONE; 94 char *pdevname; 95 int i; 96 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; 102 break; 103 } 104 } 105 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); 123 124 pr_debug("device=%s created\n", 125 ipc_batt_platform_device->name); 126 } 127 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, 140 &platform_data, 141 sizeof(struct simatic_ipc_platform)); 142 if (IS_ERR(ipc_led_platform_device)) 143 return PTR_ERR(ipc_led_platform_device); 144 145 pr_debug("device=%s created\n", 146 ipc_led_platform_device->name); 147 } 148 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, 154 &platform_data, 155 sizeof(struct simatic_ipc_platform)); 156 if (IS_ERR(ipc_wdt_platform_device)) 157 return PTR_ERR(ipc_wdt_platform_device); 158 159 pr_debug("device=%s created\n", 160 ipc_wdt_platform_device->name); 161 } 162 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", 167 station_id); 168 return -EINVAL; 169 } 170 171 return 0; 172 } 173 174 static void request_additional_modules(u32 station_id) 175 { 176 char **extra_modules = NULL; 177 int i; 178 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; 182 break; 183 } 184 } 185 186 if (!extra_modules) 187 return; 188 189 for (i = 0; i < SIMATIC_IPC_MAX_EXTRA_MODULES; i++) { 190 if (extra_modules[i]) 191 request_module(extra_modules[i]); 192 else 193 break; 194 } 195 } 196 197 static int __init simatic_ipc_init_module(void) 198 { 199 const struct dmi_system_id *match; 200 u32 station_id; 201 int err; 202 203 match = dmi_first_match(simatic_ipc_whitelist); 204 if (!match) 205 return 0; 206 207 err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id); 208 209 if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) { 210 pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM); 211 return 0; 212 } 213 214 request_additional_modules(station_id); 215 216 return register_platform_devices(station_id); 217 } 218 219 static void __exit simatic_ipc_exit_module(void) 220 { 221 platform_device_unregister(ipc_led_platform_device); 222 ipc_led_platform_device = NULL; 223 224 platform_device_unregister(ipc_wdt_platform_device); 225 ipc_wdt_platform_device = NULL; 226 227 platform_device_unregister(ipc_batt_platform_device); 228 ipc_batt_platform_device = NULL; 229 } 230 231 module_init(simatic_ipc_init_module); 232 module_exit(simatic_ipc_exit_module); 233 234 MODULE_LICENSE("GPL v2"); 235 MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>"); 236 MODULE_ALIAS("dmi:*:svnSIEMENSAG:*"); 237