Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  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
 21static struct platform_device *ipc_led_platform_device;
 22static struct platform_device *ipc_wdt_platform_device;
 23static struct platform_device *ipc_batt_platform_device;
 24
 25static const struct dmi_system_id simatic_ipc_whitelist[] = {
 26	{
 27		.matches = {
 28			DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
 29		},
 30	},
 31	{}
 32};
 33
 34static struct simatic_ipc_platform platform_data;
 35
 36#define SIMATIC_IPC_MAX_EXTRA_MODULES 2
 37
 38static 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
 89static 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
174static 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
197static 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
219static 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
231module_init(simatic_ipc_init_module);
232module_exit(simatic_ipc_exit_module);
233
234MODULE_LICENSE("GPL v2");
235MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
236MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");