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_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E},
45 	{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
46 	{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
47 	{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
48 };
49 
register_platform_devices(u32 station_id)50 static int register_platform_devices(u32 station_id)
51 {
52 	u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
53 	u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
54 	int i;
55 
56 	platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
57 
58 	for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
59 		if (device_modes[i].station_id == station_id) {
60 			ledmode = device_modes[i].led_mode;
61 			wdtmode = device_modes[i].wdt_mode;
62 			break;
63 		}
64 	}
65 
66 	if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
67 		platform_data.devmode = ledmode;
68 		ipc_led_platform_device =
69 			platform_device_register_data(NULL,
70 				KBUILD_MODNAME "_leds", PLATFORM_DEVID_NONE,
71 				&platform_data,
72 				sizeof(struct simatic_ipc_platform));
73 		if (IS_ERR(ipc_led_platform_device))
74 			return PTR_ERR(ipc_led_platform_device);
75 
76 		pr_debug("device=%s created\n",
77 			 ipc_led_platform_device->name);
78 	}
79 
80 	if (wdtmode != SIMATIC_IPC_DEVICE_NONE) {
81 		platform_data.devmode = wdtmode;
82 		ipc_wdt_platform_device =
83 			platform_device_register_data(NULL,
84 				KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
85 				&platform_data,
86 				sizeof(struct simatic_ipc_platform));
87 		if (IS_ERR(ipc_wdt_platform_device))
88 			return PTR_ERR(ipc_wdt_platform_device);
89 
90 		pr_debug("device=%s created\n",
91 			 ipc_wdt_platform_device->name);
92 	}
93 
94 	if (ledmode == SIMATIC_IPC_DEVICE_NONE &&
95 	    wdtmode == SIMATIC_IPC_DEVICE_NONE) {
96 		pr_warn("unsupported IPC detected, station id=%08x\n",
97 			station_id);
98 		return -EINVAL;
99 	}
100 
101 	return 0;
102 }
103 
104 /* FIXME: this should eventually be done with generic P2SB discovery code
105  * the individual drivers for watchdogs and LEDs access memory that implements
106  * GPIO, but pinctrl will not come up because of missing ACPI entries
107  *
108  * While there is no conflict a cleaner solution would be to somehow bring up
109  * pinctrl even with these ACPI entries missing, and base the drivers on pinctrl.
110  * After which the following function could be dropped, together with the code
111  * poking the memory.
112  */
113 /*
114  * Get membase address from PCI, used in leds and wdt module. Here we read
115  * the bar0. The final address calculation is done in the appropriate modules
116  */
simatic_ipc_get_membase0(unsigned int p2sb)117 u32 simatic_ipc_get_membase0(unsigned int p2sb)
118 {
119 	struct pci_bus *bus;
120 	u32 bar0 = 0;
121 	/*
122 	 * The GPIO memory is in bar0 of the hidden P2SB device.
123 	 * Unhide the device to have a quick look at it, before we hide it
124 	 * again.
125 	 * Also grab the pci rescan lock so that device does not get discovered
126 	 * and remapped while it is visible.
127 	 * This code is inspired by drivers/mfd/lpc_ich.c
128 	 */
129 	bus = pci_find_bus(0, 0);
130 	pci_lock_rescan_remove();
131 	pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x0);
132 	pci_bus_read_config_dword(bus, p2sb, PCI_BASE_ADDRESS_0, &bar0);
133 
134 	bar0 &= ~0xf;
135 	pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x1);
136 	pci_unlock_rescan_remove();
137 
138 	return bar0;
139 }
140 EXPORT_SYMBOL(simatic_ipc_get_membase0);
141 
simatic_ipc_init_module(void)142 static int __init simatic_ipc_init_module(void)
143 {
144 	const struct dmi_system_id *match;
145 	u32 station_id;
146 	int err;
147 
148 	match = dmi_first_match(simatic_ipc_whitelist);
149 	if (!match)
150 		return 0;
151 
152 	err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id);
153 
154 	if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) {
155 		pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM);
156 		return 0;
157 	}
158 
159 	return register_platform_devices(station_id);
160 }
161 
simatic_ipc_exit_module(void)162 static void __exit simatic_ipc_exit_module(void)
163 {
164 	platform_device_unregister(ipc_led_platform_device);
165 	ipc_led_platform_device = NULL;
166 
167 	platform_device_unregister(ipc_wdt_platform_device);
168 	ipc_wdt_platform_device = NULL;
169 }
170 
171 module_init(simatic_ipc_init_module);
172 module_exit(simatic_ipc_exit_module);
173 
174 MODULE_LICENSE("GPL v2");
175 MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
176 MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");
177