File Index
Files and directories
Sign in
FILE

meraki-mx100.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/meraki-mx100.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2
  3/*
  4 * Cisco Meraki MX100 (Tinkerbell) board platform driver
  5 *
  6 * Based off of arch/x86/platform/meraki/tink.c from the
  7 * Meraki GPL release meraki-firmware-sources-r23-20150601
  8 *
  9 * Format inspired by platform/x86/pcengines-apuv2.c
 10 *
 11 * Copyright (C) 2021 Chris Blake <chrisrblake93@gmail.com>
 12 */
 13
 14#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
 15
 16#include <linux/dmi.h>
 17#include <linux/err.h>
 18#include <linux/gpio/machine.h>
 19#include <linux/gpio/property.h>
 20#include <linux/input-event-codes.h>
 21#include <linux/io.h>
 22#include <linux/kernel.h>
 23#include <linux/module.h>
 24#include <linux/platform_device.h>
 25#include <linux/property.h>
 26
 27#define TINK_GPIO_DRIVER_NAME "gpio_ich"
 28
 29static const struct software_node gpio_ich_node = {
 30	.name = TINK_GPIO_DRIVER_NAME,
 31};
 32
 33/* LEDs */
 34static const struct software_node tink_gpio_leds_node = {
 35	.name = "meraki-mx100-leds",
 36};
 37
 38static const struct property_entry tink_internet_led_props[] = {
 39	PROPERTY_ENTRY_STRING("label", "mx100:green:internet"),
 40	PROPERTY_ENTRY_STRING("linux,default-trigger", "default-on"),
 41	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 11, GPIO_ACTIVE_LOW),
 42	{ }
 43};
 44
 45static const struct software_node tink_internet_led_node = {
 46	.name = "internet-led",
 47	.parent = &tink_gpio_leds_node,
 48	.properties = tink_internet_led_props,
 49};
 50
 51static const struct property_entry tink_lan2_led_props[] = {
 52	PROPERTY_ENTRY_STRING("label", "mx100:green:lan2"),
 53	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 18, GPIO_ACTIVE_HIGH),
 54	{ }
 55};
 56
 57static const struct software_node tink_lan2_led_node = {
 58	.name = "lan2-led",
 59	.parent = &tink_gpio_leds_node,
 60	.properties = tink_lan2_led_props,
 61};
 62
 63static const struct property_entry tink_lan3_led_props[] = {
 64	PROPERTY_ENTRY_STRING("label", "mx100:green:lan3"),
 65	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 20, GPIO_ACTIVE_HIGH),
 66	{ }
 67};
 68
 69static const struct software_node tink_lan3_led_node = {
 70	.name = "lan3-led",
 71	.parent = &tink_gpio_leds_node,
 72	.properties = tink_lan3_led_props,
 73};
 74
 75static const struct property_entry tink_lan4_led_props[] = {
 76	PROPERTY_ENTRY_STRING("label", "mx100:green:lan4"),
 77	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 22, GPIO_ACTIVE_HIGH),
 78	{ }
 79};
 80
 81static const struct software_node tink_lan4_led_node = {
 82	.name = "lan4-led",
 83	.parent = &tink_gpio_leds_node,
 84	.properties = tink_lan4_led_props,
 85};
 86
 87static const struct property_entry tink_lan5_led_props[] = {
 88	PROPERTY_ENTRY_STRING("label", "mx100:green:lan5"),
 89	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 23, GPIO_ACTIVE_HIGH),
 90	{ }
 91};
 92
 93static const struct software_node tink_lan5_led_node = {
 94	.name = "lan5-led",
 95	.parent = &tink_gpio_leds_node,
 96	.properties = tink_lan5_led_props,
 97};
 98
 99static const struct property_entry tink_lan6_led_props[] = {
100	PROPERTY_ENTRY_STRING("label", "mx100:green:lan6"),
101	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 32, GPIO_ACTIVE_HIGH),
102	{ }
103};
104
105static const struct software_node tink_lan6_led_node = {
106	.name = "lan6-led",
107	.parent = &tink_gpio_leds_node,
108	.properties = tink_lan6_led_props,
109};
110
111static const struct property_entry tink_lan7_led_props[] = {
112	PROPERTY_ENTRY_STRING("label", "mx100:green:lan7"),
113	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 34, GPIO_ACTIVE_HIGH),
114	{ }
115};
116
117static const struct software_node tink_lan7_led_node = {
118	.name = "lan7-led",
119	.parent = &tink_gpio_leds_node,
120	.properties = tink_lan7_led_props,
121};
122
123static const struct property_entry tink_lan8_led_props[] = {
124	PROPERTY_ENTRY_STRING("label", "mx100:green:lan8"),
125	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 35, GPIO_ACTIVE_HIGH),
126	{ }
127};
128
129static const struct software_node tink_lan8_led_node = {
130	.name = "lan8-led",
131	.parent = &tink_gpio_leds_node,
132	.properties = tink_lan8_led_props,
133};
134
135static const struct property_entry tink_lan9_led_props[] = {
136	PROPERTY_ENTRY_STRING("label", "mx100:green:lan9"),
137	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 36, GPIO_ACTIVE_HIGH),
138	{ }
139};
140
141static const struct software_node tink_lan9_led_node = {
142	.name = "lan9-led",
143	.parent = &tink_gpio_leds_node,
144	.properties = tink_lan9_led_props,
145};
146
147static const struct property_entry tink_lan10_led_props[] = {
148	PROPERTY_ENTRY_STRING("label", "mx100:green:lan10"),
149	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 37, GPIO_ACTIVE_HIGH),
150	{ }
151};
152
153static const struct software_node tink_lan10_led_node = {
154	.name = "lan10-led",
155	.parent = &tink_gpio_leds_node,
156	.properties = tink_lan10_led_props,
157};
158
159static const struct property_entry tink_lan11_led_props[] = {
160	PROPERTY_ENTRY_STRING("label", "mx100:green:lan11"),
161	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 48, GPIO_ACTIVE_HIGH),
162	{ }
163};
164
165static const struct software_node tink_lan11_led_node = {
166	.name = "lan11-led",
167	.parent = &tink_gpio_leds_node,
168	.properties = tink_lan11_led_props,
169};
170
171static const struct property_entry tink_ha_green_led_props[] = {
172	PROPERTY_ENTRY_STRING("label", "mx100:green:ha"),
173	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 16, GPIO_ACTIVE_LOW),
174	{ }
175};
176
177static const struct software_node tink_ha_green_led_node = {
178	.name = "ha-green-led",
179	.parent = &tink_gpio_leds_node,
180	.properties = tink_ha_green_led_props,
181};
182
183static const struct property_entry tink_ha_orange_led_props[] = {
184	PROPERTY_ENTRY_STRING("label", "mx100:orange:ha"),
185	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 7, GPIO_ACTIVE_LOW),
186	{ }
187};
188
189static const struct software_node tink_ha_orange_led_node = {
190	.name = "ha-orange-led",
191	.parent = &tink_gpio_leds_node,
192	.properties = tink_ha_orange_led_props,
193};
194
195static const struct property_entry tink_usb_green_led_props[] = {
196	PROPERTY_ENTRY_STRING("label", "mx100:green:usb"),
197	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 21, GPIO_ACTIVE_LOW),
198	{ }
199};
200
201static const struct software_node tink_usb_green_led_node = {
202	.name = "usb-green-led",
203	.parent = &tink_gpio_leds_node,
204	.properties = tink_usb_green_led_props,
205};
206
207static const struct property_entry tink_usb_orange_led_props[] = {
208	PROPERTY_ENTRY_STRING("label", "mx100:orange:usb"),
209	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 19, GPIO_ACTIVE_LOW),
210	{ }
211};
212
213static const struct software_node tink_usb_orange_led_node = {
214	.name = "usb-orange-led",
215	.parent = &tink_gpio_leds_node,
216	.properties = tink_usb_orange_led_props,
217};
218
219/* Reset Button */
220static const struct property_entry tink_gpio_keys_props[] = {
221	PROPERTY_ENTRY_U32("poll-interval", 20),
222	{ }
223};
224
225static const struct software_node tink_gpio_keys_node = {
226	.name = "mx100-keys",
227	.properties = tink_gpio_keys_props,
228};
229
230static const struct property_entry tink_reset_key_props[] = {
231	PROPERTY_ENTRY_U32("linux,code", KEY_RESTART),
232	PROPERTY_ENTRY_STRING("label", "Reset"),
233	PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 60, GPIO_ACTIVE_LOW),
234	PROPERTY_ENTRY_U32("linux,input-type", EV_KEY),
235	PROPERTY_ENTRY_U32("debounce-interval", 100),
236	{ }
237};
238
239static const struct software_node tink_reset_key_node = {
240	.name = "reset",
241	.parent = &tink_gpio_keys_node,
242	.properties = tink_reset_key_props,
243};
244
245static const struct software_node *tink_swnodes[] = {
246	&gpio_ich_node,
247	/* LEDs nodes */
248	&tink_gpio_leds_node,
249	&tink_internet_led_node,
250	&tink_lan2_led_node,
251	&tink_lan3_led_node,
252	&tink_lan4_led_node,
253	&tink_lan5_led_node,
254	&tink_lan6_led_node,
255	&tink_lan7_led_node,
256	&tink_lan8_led_node,
257	&tink_lan9_led_node,
258	&tink_lan10_led_node,
259	&tink_lan11_led_node,
260	&tink_ha_green_led_node,
261	&tink_ha_orange_led_node,
262	&tink_usb_green_led_node,
263	&tink_usb_orange_led_node,
264	/* Keys nodes */
265	&tink_gpio_keys_node,
266	&tink_reset_key_node,
267	NULL
268};
269
270/* Board setup */
271static const struct dmi_system_id tink_systems[] __initconst = {
272	{
273		.matches = {
274			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Cisco"),
275			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MX100-HW"),
276		},
277	},
278	{} /* Terminating entry */
279};
280MODULE_DEVICE_TABLE(dmi, tink_systems);
281
282static struct platform_device *tink_leds_pdev;
283static struct platform_device *tink_keys_pdev;
284
285static int __init tink_board_init(void)
286{
287	struct platform_device_info keys_info = {
288		.name = "gpio-keys-polled",
289		.id = PLATFORM_DEVID_NONE,
290	};
291	struct platform_device_info leds_info = {
292		.name = "leds-gpio",
293		.id = PLATFORM_DEVID_NONE,
294	};
295	int err;
296
297	if (!dmi_first_match(tink_systems))
298		return -ENODEV;
299
300	/*
301	 * We need to make sure that GPIO60 isn't set to native mode as is default since it's our
302	 * Reset Button. To do this, write to GPIO_USE_SEL2 to have GPIO60 set to GPIO mode.
303	 * This is documented on page 1609 of the PCH datasheet, order number 327879-005US
304	 */
305	outl(inl(0x530) | BIT(28), 0x530);
306
307	err = software_node_register_node_group(tink_swnodes);
308	if (err) {
309		pr_err("failed to register software nodes: %d\n", err);
310		return err;
311	}
312
313	leds_info.fwnode = software_node_fwnode(&tink_gpio_leds_node);
314	tink_leds_pdev = platform_device_register_full(&leds_info);
315	if (IS_ERR(tink_leds_pdev)) {
316		err = PTR_ERR(tink_leds_pdev);
317		pr_err("failed to create LED device: %d\n", err);
318		goto err_unregister_swnodes;
319	}
320
321	keys_info.fwnode = software_node_fwnode(&tink_gpio_keys_node);
322	tink_keys_pdev = platform_device_register_full(&keys_info);
323	if (IS_ERR(tink_keys_pdev)) {
324		err = PTR_ERR(tink_keys_pdev);
325		pr_err("failed to create key device: %d\n", err);
326		goto err_unregister_leds;
327	}
328
329	return 0;
330
331err_unregister_leds:
332	platform_device_unregister(tink_leds_pdev);
333err_unregister_swnodes:
334	software_node_unregister_node_group(tink_swnodes);
335	return err;
336}
337module_init(tink_board_init);
338
339static void __exit tink_board_exit(void)
340{
341	platform_device_unregister(tink_keys_pdev);
342	platform_device_unregister(tink_leds_pdev);
343	software_node_unregister_node_group(tink_swnodes);
344}
345module_exit(tink_board_exit);
346
347MODULE_AUTHOR("Chris Blake <chrisrblake93@gmail.com>");
348MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver");
349MODULE_LICENSE("GPL");
350MODULE_ALIAS("platform:meraki-mx100");
~/ Read-only file preview.