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