xref: /openbmc/linux/drivers/platform/x86/meraki-mx100.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1*636a1e69SChris Blake // SPDX-License-Identifier: GPL-2.0+
2*636a1e69SChris Blake 
3*636a1e69SChris Blake /*
4*636a1e69SChris Blake  * Cisco Meraki MX100 (Tinkerbell) board platform driver
5*636a1e69SChris Blake  *
6*636a1e69SChris Blake  * Based off of arch/x86/platform/meraki/tink.c from the
7*636a1e69SChris Blake  * Meraki GPL release meraki-firmware-sources-r23-20150601
8*636a1e69SChris Blake  *
9*636a1e69SChris Blake  * Format inspired by platform/x86/pcengines-apuv2.c
10*636a1e69SChris Blake  *
11*636a1e69SChris Blake  * Copyright (C) 2021 Chris Blake <chrisrblake93@gmail.com>
12*636a1e69SChris Blake  */
13*636a1e69SChris Blake 
14*636a1e69SChris Blake #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
15*636a1e69SChris Blake 
16*636a1e69SChris Blake #include <linux/dmi.h>
17*636a1e69SChris Blake #include <linux/err.h>
18*636a1e69SChris Blake #include <linux/gpio_keys.h>
19*636a1e69SChris Blake #include <linux/gpio/machine.h>
20*636a1e69SChris Blake #include <linux/input.h>
21*636a1e69SChris Blake #include <linux/io.h>
22*636a1e69SChris Blake #include <linux/kernel.h>
23*636a1e69SChris Blake #include <linux/leds.h>
24*636a1e69SChris Blake #include <linux/module.h>
25*636a1e69SChris Blake #include <linux/platform_device.h>
26*636a1e69SChris Blake 
27*636a1e69SChris Blake #define TINK_GPIO_DRIVER_NAME "gpio_ich"
28*636a1e69SChris Blake 
29*636a1e69SChris Blake /* LEDs */
30*636a1e69SChris Blake static const struct gpio_led tink_leds[] = {
31*636a1e69SChris Blake 	{
32*636a1e69SChris Blake 		.name = "mx100:green:internet",
33*636a1e69SChris Blake 		.default_trigger = "default-on",
34*636a1e69SChris Blake 	},
35*636a1e69SChris Blake 	{
36*636a1e69SChris Blake 		.name = "mx100:green:lan2",
37*636a1e69SChris Blake 	},
38*636a1e69SChris Blake 	{
39*636a1e69SChris Blake 		.name = "mx100:green:lan3",
40*636a1e69SChris Blake 	},
41*636a1e69SChris Blake 	{
42*636a1e69SChris Blake 		.name = "mx100:green:lan4",
43*636a1e69SChris Blake 	},
44*636a1e69SChris Blake 	{
45*636a1e69SChris Blake 		.name = "mx100:green:lan5",
46*636a1e69SChris Blake 	},
47*636a1e69SChris Blake 	{
48*636a1e69SChris Blake 		.name = "mx100:green:lan6",
49*636a1e69SChris Blake 	},
50*636a1e69SChris Blake 	{
51*636a1e69SChris Blake 		.name = "mx100:green:lan7",
52*636a1e69SChris Blake 	},
53*636a1e69SChris Blake 	{
54*636a1e69SChris Blake 		.name = "mx100:green:lan8",
55*636a1e69SChris Blake 	},
56*636a1e69SChris Blake 	{
57*636a1e69SChris Blake 		.name = "mx100:green:lan9",
58*636a1e69SChris Blake 	},
59*636a1e69SChris Blake 	{
60*636a1e69SChris Blake 		.name = "mx100:green:lan10",
61*636a1e69SChris Blake 	},
62*636a1e69SChris Blake 	{
63*636a1e69SChris Blake 		.name = "mx100:green:lan11",
64*636a1e69SChris Blake 	},
65*636a1e69SChris Blake 	{
66*636a1e69SChris Blake 		.name = "mx100:green:ha",
67*636a1e69SChris Blake 	},
68*636a1e69SChris Blake 	{
69*636a1e69SChris Blake 		.name = "mx100:orange:ha",
70*636a1e69SChris Blake 	},
71*636a1e69SChris Blake 	{
72*636a1e69SChris Blake 		.name = "mx100:green:usb",
73*636a1e69SChris Blake 	},
74*636a1e69SChris Blake 	{
75*636a1e69SChris Blake 		.name = "mx100:orange:usb",
76*636a1e69SChris Blake 	},
77*636a1e69SChris Blake };
78*636a1e69SChris Blake 
79*636a1e69SChris Blake static const struct gpio_led_platform_data tink_leds_pdata = {
80*636a1e69SChris Blake 	.num_leds	= ARRAY_SIZE(tink_leds),
81*636a1e69SChris Blake 	.leds		= tink_leds,
82*636a1e69SChris Blake };
83*636a1e69SChris Blake 
84*636a1e69SChris Blake static struct gpiod_lookup_table tink_leds_table = {
85*636a1e69SChris Blake 	.dev_id = "leds-gpio",
86*636a1e69SChris Blake 	.table = {
87*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 11,
88*636a1e69SChris Blake 				NULL, 0, GPIO_ACTIVE_LOW),
89*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 18,
90*636a1e69SChris Blake 				NULL, 1, GPIO_ACTIVE_HIGH),
91*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 20,
92*636a1e69SChris Blake 				NULL, 2, GPIO_ACTIVE_HIGH),
93*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 22,
94*636a1e69SChris Blake 				NULL, 3, GPIO_ACTIVE_HIGH),
95*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 23,
96*636a1e69SChris Blake 				NULL, 4, GPIO_ACTIVE_HIGH),
97*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 32,
98*636a1e69SChris Blake 				NULL, 5, GPIO_ACTIVE_HIGH),
99*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 34,
100*636a1e69SChris Blake 				NULL, 6, GPIO_ACTIVE_HIGH),
101*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 35,
102*636a1e69SChris Blake 				NULL, 7, GPIO_ACTIVE_HIGH),
103*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 36,
104*636a1e69SChris Blake 				NULL, 8, GPIO_ACTIVE_HIGH),
105*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 37,
106*636a1e69SChris Blake 				NULL, 9, GPIO_ACTIVE_HIGH),
107*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 48,
108*636a1e69SChris Blake 				NULL, 10, GPIO_ACTIVE_HIGH),
109*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 16,
110*636a1e69SChris Blake 				NULL, 11, GPIO_ACTIVE_LOW),
111*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 7,
112*636a1e69SChris Blake 				NULL, 12, GPIO_ACTIVE_LOW),
113*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 21,
114*636a1e69SChris Blake 				NULL, 13, GPIO_ACTIVE_LOW),
115*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 19,
116*636a1e69SChris Blake 				NULL, 14, GPIO_ACTIVE_LOW),
117*636a1e69SChris Blake 		{} /* Terminating entry */
118*636a1e69SChris Blake 	}
119*636a1e69SChris Blake };
120*636a1e69SChris Blake 
121*636a1e69SChris Blake /* Reset Button */
122*636a1e69SChris Blake static struct gpio_keys_button tink_buttons[] = {
123*636a1e69SChris Blake 	{
124*636a1e69SChris Blake 		.desc			= "Reset",
125*636a1e69SChris Blake 		.type			= EV_KEY,
126*636a1e69SChris Blake 		.code			= KEY_RESTART,
127*636a1e69SChris Blake 		.active_low             = 1,
128*636a1e69SChris Blake 		.debounce_interval      = 100,
129*636a1e69SChris Blake 	},
130*636a1e69SChris Blake };
131*636a1e69SChris Blake 
132*636a1e69SChris Blake static const struct gpio_keys_platform_data tink_buttons_pdata = {
133*636a1e69SChris Blake 	.buttons	= tink_buttons,
134*636a1e69SChris Blake 	.nbuttons	= ARRAY_SIZE(tink_buttons),
135*636a1e69SChris Blake 	.poll_interval  = 20,
136*636a1e69SChris Blake 	.rep		= 0,
137*636a1e69SChris Blake 	.name		= "mx100-keys",
138*636a1e69SChris Blake };
139*636a1e69SChris Blake 
140*636a1e69SChris Blake static struct gpiod_lookup_table tink_keys_table = {
141*636a1e69SChris Blake 	.dev_id = "gpio-keys-polled",
142*636a1e69SChris Blake 	.table = {
143*636a1e69SChris Blake 		GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 60,
144*636a1e69SChris Blake 				NULL, 0, GPIO_ACTIVE_LOW),
145*636a1e69SChris Blake 		{} /* Terminating entry */
146*636a1e69SChris Blake 	}
147*636a1e69SChris Blake };
148*636a1e69SChris Blake 
149*636a1e69SChris Blake /* Board setup */
150*636a1e69SChris Blake static const struct dmi_system_id tink_systems[] __initconst = {
151*636a1e69SChris Blake 	{
152*636a1e69SChris Blake 		.matches = {
153*636a1e69SChris Blake 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Cisco"),
154*636a1e69SChris Blake 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MX100-HW"),
155*636a1e69SChris Blake 		},
156*636a1e69SChris Blake 	},
157*636a1e69SChris Blake 	{} /* Terminating entry */
158*636a1e69SChris Blake };
159*636a1e69SChris Blake MODULE_DEVICE_TABLE(dmi, tink_systems);
160*636a1e69SChris Blake 
161*636a1e69SChris Blake static struct platform_device *tink_leds_pdev;
162*636a1e69SChris Blake static struct platform_device *tink_keys_pdev;
163*636a1e69SChris Blake 
tink_create_dev(const char * name,const void * pdata,size_t sz)164*636a1e69SChris Blake static struct platform_device * __init tink_create_dev(
165*636a1e69SChris Blake 	const char *name, const void *pdata, size_t sz)
166*636a1e69SChris Blake {
167*636a1e69SChris Blake 	struct platform_device *pdev;
168*636a1e69SChris Blake 
169*636a1e69SChris Blake 	pdev = platform_device_register_data(NULL,
170*636a1e69SChris Blake 		name, PLATFORM_DEVID_NONE, pdata, sz);
171*636a1e69SChris Blake 	if (IS_ERR(pdev))
172*636a1e69SChris Blake 		pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
173*636a1e69SChris Blake 
174*636a1e69SChris Blake 	return pdev;
175*636a1e69SChris Blake }
176*636a1e69SChris Blake 
tink_board_init(void)177*636a1e69SChris Blake static int __init tink_board_init(void)
178*636a1e69SChris Blake {
179*636a1e69SChris Blake 	int ret;
180*636a1e69SChris Blake 
181*636a1e69SChris Blake 	if (!dmi_first_match(tink_systems))
182*636a1e69SChris Blake 		return -ENODEV;
183*636a1e69SChris Blake 
184*636a1e69SChris Blake 	/*
185*636a1e69SChris Blake 	 * We need to make sure that GPIO60 isn't set to native mode as is default since it's our
186*636a1e69SChris Blake 	 * Reset Button. To do this, write to GPIO_USE_SEL2 to have GPIO60 set to GPIO mode.
187*636a1e69SChris Blake 	 * This is documented on page 1609 of the PCH datasheet, order number 327879-005US
188*636a1e69SChris Blake 	 */
189*636a1e69SChris Blake 	outl(inl(0x530) | BIT(28), 0x530);
190*636a1e69SChris Blake 
191*636a1e69SChris Blake 	gpiod_add_lookup_table(&tink_leds_table);
192*636a1e69SChris Blake 	gpiod_add_lookup_table(&tink_keys_table);
193*636a1e69SChris Blake 
194*636a1e69SChris Blake 	tink_leds_pdev = tink_create_dev("leds-gpio",
195*636a1e69SChris Blake 		&tink_leds_pdata, sizeof(tink_leds_pdata));
196*636a1e69SChris Blake 	if (IS_ERR(tink_leds_pdev)) {
197*636a1e69SChris Blake 		ret = PTR_ERR(tink_leds_pdev);
198*636a1e69SChris Blake 		goto err;
199*636a1e69SChris Blake 	}
200*636a1e69SChris Blake 
201*636a1e69SChris Blake 	tink_keys_pdev = tink_create_dev("gpio-keys-polled",
202*636a1e69SChris Blake 		&tink_buttons_pdata, sizeof(tink_buttons_pdata));
203*636a1e69SChris Blake 	if (IS_ERR(tink_keys_pdev)) {
204*636a1e69SChris Blake 		ret = PTR_ERR(tink_keys_pdev);
205*636a1e69SChris Blake 		platform_device_unregister(tink_leds_pdev);
206*636a1e69SChris Blake 		goto err;
207*636a1e69SChris Blake 	}
208*636a1e69SChris Blake 
209*636a1e69SChris Blake 	return 0;
210*636a1e69SChris Blake 
211*636a1e69SChris Blake err:
212*636a1e69SChris Blake 	gpiod_remove_lookup_table(&tink_keys_table);
213*636a1e69SChris Blake 	gpiod_remove_lookup_table(&tink_leds_table);
214*636a1e69SChris Blake 	return ret;
215*636a1e69SChris Blake }
216*636a1e69SChris Blake module_init(tink_board_init);
217*636a1e69SChris Blake 
tink_board_exit(void)218*636a1e69SChris Blake static void __exit tink_board_exit(void)
219*636a1e69SChris Blake {
220*636a1e69SChris Blake 	platform_device_unregister(tink_keys_pdev);
221*636a1e69SChris Blake 	platform_device_unregister(tink_leds_pdev);
222*636a1e69SChris Blake 	gpiod_remove_lookup_table(&tink_keys_table);
223*636a1e69SChris Blake 	gpiod_remove_lookup_table(&tink_leds_table);
224*636a1e69SChris Blake }
225*636a1e69SChris Blake module_exit(tink_board_exit);
226*636a1e69SChris Blake 
227*636a1e69SChris Blake MODULE_AUTHOR("Chris Blake <chrisrblake93@gmail.com>");
228*636a1e69SChris Blake MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver");
229*636a1e69SChris Blake MODULE_LICENSE("GPL");
230*636a1e69SChris Blake MODULE_ALIAS("platform:meraki-mx100");
231