Merge tag 'core_guards_for_6.5_rc1' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / drivers / platform / x86 / simatic-ipc.c
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_IPCBX_39A, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
50         {SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G},
51 };
52
53 static int register_platform_devices(u32 station_id)
54 {
55         u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
56         u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
57         char *pdevname = KBUILD_MODNAME "_leds";
58         int i;
59
60         platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
61
62         for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
63                 if (device_modes[i].station_id == station_id) {
64                         ledmode = device_modes[i].led_mode;
65                         wdtmode = device_modes[i].wdt_mode;
66                         break;
67                 }
68         }
69
70         if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
71                 if (ledmode == SIMATIC_IPC_DEVICE_127E)
72                         pdevname = KBUILD_MODNAME "_leds_gpio_apollolake";
73                 if (ledmode == SIMATIC_IPC_DEVICE_227G)
74                         pdevname = KBUILD_MODNAME "_leds_gpio_f7188x";
75                 platform_data.devmode = ledmode;
76                 ipc_led_platform_device =
77                         platform_device_register_data(NULL,
78                                 pdevname, PLATFORM_DEVID_NONE,
79                                 &platform_data,
80                                 sizeof(struct simatic_ipc_platform));
81                 if (IS_ERR(ipc_led_platform_device))
82                         return PTR_ERR(ipc_led_platform_device);
83
84                 pr_debug("device=%s created\n",
85                          ipc_led_platform_device->name);
86         }
87
88         if (wdtmode == SIMATIC_IPC_DEVICE_227G) {
89                 request_module("w83627hf_wdt");
90                 return 0;
91         }
92
93         if (wdtmode != SIMATIC_IPC_DEVICE_NONE) {
94                 platform_data.devmode = wdtmode;
95                 ipc_wdt_platform_device =
96                         platform_device_register_data(NULL,
97                                 KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
98                                 &platform_data,
99                                 sizeof(struct simatic_ipc_platform));
100                 if (IS_ERR(ipc_wdt_platform_device))
101                         return PTR_ERR(ipc_wdt_platform_device);
102
103                 pr_debug("device=%s created\n",
104                          ipc_wdt_platform_device->name);
105         }
106
107         if (ledmode == SIMATIC_IPC_DEVICE_NONE &&
108             wdtmode == SIMATIC_IPC_DEVICE_NONE) {
109                 pr_warn("unsupported IPC detected, station id=%08x\n",
110                         station_id);
111                 return -EINVAL;
112         }
113
114         return 0;
115 }
116
117 static int __init simatic_ipc_init_module(void)
118 {
119         const struct dmi_system_id *match;
120         u32 station_id;
121         int err;
122
123         match = dmi_first_match(simatic_ipc_whitelist);
124         if (!match)
125                 return 0;
126
127         err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id);
128
129         if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) {
130                 pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM);
131                 return 0;
132         }
133
134         return register_platform_devices(station_id);
135 }
136
137 static void __exit simatic_ipc_exit_module(void)
138 {
139         platform_device_unregister(ipc_led_platform_device);
140         ipc_led_platform_device = NULL;
141
142         platform_device_unregister(ipc_wdt_platform_device);
143         ipc_wdt_platform_device = NULL;
144 }
145
146 module_init(simatic_ipc_init_module);
147 module_exit(simatic_ipc_exit_module);
148
149 MODULE_LICENSE("GPL v2");
150 MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
151 MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");