GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / platform / x86 / meraki-mx100.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_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 */
30 static 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
79 static const struct gpio_led_platform_data tink_leds_pdata = {
80         .num_leds       = ARRAY_SIZE(tink_leds),
81         .leds           = tink_leds,
82 };
83
84 static 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 */
122 static 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
132 static 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
140 static 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 */
150 static 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 };
159 MODULE_DEVICE_TABLE(dmi, tink_systems);
160
161 static struct platform_device *tink_leds_pdev;
162 static struct platform_device *tink_keys_pdev;
163
164 static 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, pdata, sz);
171         if (IS_ERR(pdev))
172                 pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
173
174         return pdev;
175 }
176
177 static int __init tink_board_init(void)
178 {
179         int ret;
180
181         if (!dmi_first_match(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(0x530) | BIT(28), 0x530);
190
191         gpiod_add_lookup_table(&tink_leds_table);
192         gpiod_add_lookup_table(&tink_keys_table);
193
194         tink_leds_pdev = tink_create_dev("leds-gpio",
195                 &tink_leds_pdata, sizeof(tink_leds_pdata));
196         if (IS_ERR(tink_leds_pdev)) {
197                 ret = PTR_ERR(tink_leds_pdev);
198                 goto err;
199         }
200
201         tink_keys_pdev = tink_create_dev("gpio-keys-polled",
202                 &tink_buttons_pdata, sizeof(tink_buttons_pdata));
203         if (IS_ERR(tink_keys_pdev)) {
204                 ret = PTR_ERR(tink_keys_pdev);
205                 platform_device_unregister(tink_leds_pdev);
206                 goto err;
207         }
208
209         return 0;
210
211 err:
212         gpiod_remove_lookup_table(&tink_keys_table);
213         gpiod_remove_lookup_table(&tink_leds_table);
214         return ret;
215 }
216 module_init(tink_board_init);
217
218 static 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(&tink_keys_table);
223         gpiod_remove_lookup_table(&tink_leds_table);
224 }
225 module_exit(tink_board_exit);
226
227 MODULE_AUTHOR("Chris Blake <chrisrblake93@gmail.com>");
228 MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver");
229 MODULE_LICENSE("GPL");
230 MODULE_ALIAS("platform:meraki-mx100");