1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Siemens SIMATIC IPC platform driver
4  *
5  * Copyright (c) Siemens AG, 2018-2021
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/pci.h>
19 #include <linux/platform_data/x86/simatic-ipc.h>
20 #include <linux/platform_device.h>
21 
22 static struct platform_device *ipc_led_platform_device;
23 static struct platform_device *ipc_wdt_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 static struct {
37 	u32 station_id;
38 	u8 led_mode;
39 	u8 wdt_mode;
40 } device_modes[] = {
41 	{SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE},
42 	{SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE},
43 	{SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E},
44 	{SIMATIC_IPC_IPC227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
45 	{SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E},
46 	{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
47 	{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
48 	{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
49 	{SIMATIC_IPC_IPC427G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
50 };
51 
register_platform_devices(u32 station_id)52 static int register_platform_devices(u32 station_id)
53 {
54 	u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
55 	u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
56 	char *pdevname = KBUILD_MODNAME "_leds";
57 	int i;
58 
59 	platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
60 
61 	for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
62 		if (device_modes[i].station_id == station_id) {
63 			ledmode = device_modes[i].led_mode;
64 			wdtmode = device_modes[i].wdt_mode;
65 			break;
66 		}
67 	}
68 
69 	if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
70 		if (ledmode == SIMATIC_IPC_DEVICE_127E ||
71 		    ledmode == SIMATIC_IPC_DEVICE_227G)
72 			pdevname = KBUILD_MODNAME "_leds_gpio";
73 		platform_data.devmode = ledmode;
74 		ipc_led_platform_device =
75 			platform_device_register_data(NULL,
76 				pdevname, PLATFORM_DEVID_NONE,
77 				&platform_data,
78 				sizeof(struct simatic_ipc_platform));
79 		if (IS_ERR(ipc_led_platform_device))
80 			return PTR_ERR(ipc_led_platform_device);
81 
82 		pr_debug("device=%s created\n",
83 			 ipc_led_platform_device->name);
84 	}
85 
86 	if (wdtmode == SIMATIC_IPC_DEVICE_227G) {
87 		request_module("w83627hf_wdt");
88 		return 0;
89 	}
90 
91 	if (wdtmode != SIMATIC_IPC_DEVICE_NONE) {
92 		platform_data.devmode = wdtmode;
93 		ipc_wdt_platform_device =
94 			platform_device_register_data(NULL,
95 				KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
96 				&platform_data,
97 				sizeof(struct simatic_ipc_platform));
98 		if (IS_ERR(ipc_wdt_platform_device))
99 			return PTR_ERR(ipc_wdt_platform_device);
100 
101 		pr_debug("device=%s created\n",
102 			 ipc_wdt_platform_device->name);
103 	}
104 
105 	if (ledmode == SIMATIC_IPC_DEVICE_NONE &&
106 	    wdtmode == SIMATIC_IPC_DEVICE_NONE) {
107 		pr_warn("unsupported IPC detected, station id=%08x\n",
108 			station_id);
109 		return -EINVAL;
110 	}
111 
112 	return 0;
113 }
114 
simatic_ipc_init_module(void)115 static int __init simatic_ipc_init_module(void)
116 {
117 	const struct dmi_system_id *match;
118 	u32 station_id;
119 	int err;
120 
121 	match = dmi_first_match(simatic_ipc_whitelist);
122 	if (!match)
123 		return 0;
124 
125 	err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id);
126 
127 	if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) {
128 		pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM);
129 		return 0;
130 	}
131 
132 	return register_platform_devices(station_id);
133 }
134 
simatic_ipc_exit_module(void)135 static void __exit simatic_ipc_exit_module(void)
136 {
137 	platform_device_unregister(ipc_led_platform_device);
138 	ipc_led_platform_device = NULL;
139 
140 	platform_device_unregister(ipc_wdt_platform_device);
141 	ipc_wdt_platform_device = NULL;
142 }
143 
144 module_init(simatic_ipc_init_module);
145 module_exit(simatic_ipc_exit_module);
146 
147 MODULE_LICENSE("GPL v2");
148 MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
149 MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");
150