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

source code of linux/drivers/platform/x86/meraki-mx100.c