1 // SPDX-License-Identifier: GPL-2.0+
2
3 /*
4 * PC-Engines APUv2/APUv3 board platform driver
5 * for GPIO buttons and LEDs
6 *
7 * Copyright (C) 2018 metux IT consult
8 * Author: Enrico Weigelt <info@metux.net>
9 */
10
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12
13 #include <linux/dmi.h>
14 #include <linux/err.h>
15 #include <linux/gpio/machine.h>
16 #include <linux/gpio/property.h>
17 #include <linux/input-event-codes.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <linux/property.h>
22 #include <linux/platform_data/gpio/gpio-amd-fch.h>
23
24 /*
25 * NOTE: this driver only supports APUv2/3 - not APUv1, as this one
26 * has completely different register layouts.
27 */
28
29 /* Register mappings */
30 #define APU2_GPIO_REG_LED1 AMD_FCH_GPIO_REG_GPIO57
31 #define APU2_GPIO_REG_LED2 AMD_FCH_GPIO_REG_GPIO58
32 #define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP1
33 #define APU2_GPIO_REG_MODESW AMD_FCH_GPIO_REG_GPIO32_GE1
34 #define APU2_GPIO_REG_SIMSWAP AMD_FCH_GPIO_REG_GPIO33_GE2
35 #define APU2_GPIO_REG_MPCIE2 AMD_FCH_GPIO_REG_GPIO55_DEVSLP0
36 #define APU2_GPIO_REG_MPCIE3 AMD_FCH_GPIO_REG_GPIO51
37
38 /* Order in which the GPIO lines are defined in the register list */
39 #define APU2_GPIO_LINE_LED1 0
40 #define APU2_GPIO_LINE_LED2 1
41 #define APU2_GPIO_LINE_LED3 2
42 #define APU2_GPIO_LINE_MODESW 3
43 #define APU2_GPIO_LINE_SIMSWAP 4
44 #define APU2_GPIO_LINE_MPCIE2 5
45 #define APU2_GPIO_LINE_MPCIE3 6
46
47 /* GPIO device */
48
49 static int apu2_gpio_regs[] = {
50 [APU2_GPIO_LINE_LED1] = APU2_GPIO_REG_LED1,
51 [APU2_GPIO_LINE_LED2] = APU2_GPIO_REG_LED2,
52 [APU2_GPIO_LINE_LED3] = APU2_GPIO_REG_LED3,
53 [APU2_GPIO_LINE_MODESW] = APU2_GPIO_REG_MODESW,
54 [APU2_GPIO_LINE_SIMSWAP] = APU2_GPIO_REG_SIMSWAP,
55 [APU2_GPIO_LINE_MPCIE2] = APU2_GPIO_REG_MPCIE2,
56 [APU2_GPIO_LINE_MPCIE3] = APU2_GPIO_REG_MPCIE3,
57 };
58
59 static const char * const apu2_gpio_names[] = {
60 [APU2_GPIO_LINE_LED1] = "front-led1",
61 [APU2_GPIO_LINE_LED2] = "front-led2",
62 [APU2_GPIO_LINE_LED3] = "front-led3",
63 [APU2_GPIO_LINE_MODESW] = "front-button",
64 [APU2_GPIO_LINE_SIMSWAP] = "simswap",
65 [APU2_GPIO_LINE_MPCIE2] = "mpcie2_reset",
66 [APU2_GPIO_LINE_MPCIE3] = "mpcie3_reset",
67 };
68
69 static const struct amd_fch_gpio_pdata board_apu2 = {
70 .gpio_num = ARRAY_SIZE(apu2_gpio_regs),
71 .gpio_reg = apu2_gpio_regs,
72 .gpio_names = apu2_gpio_names,
73 };
74
75 static const struct software_node apu2_gpiochip_node = {
76 .name = AMD_FCH_GPIO_DRIVER_NAME,
77 };
78
79 /* GPIO LEDs device */
80 static const struct software_node apu2_leds_node = {
81 .name = "apu2-leds",
82 };
83
84 static const struct property_entry apu2_led1_props[] = {
85 PROPERTY_ENTRY_STRING("label", "apu:green:1"),
86 PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
87 APU2_GPIO_LINE_LED1, GPIO_ACTIVE_LOW),
88 { }
89 };
90
91 static const struct software_node apu2_led1_swnode = {
92 .name = "led-1",
93 .parent = &apu2_leds_node,
94 .properties = apu2_led1_props,
95 };
96
97 static const struct property_entry apu2_led2_props[] = {
98 PROPERTY_ENTRY_STRING("label", "apu:green:2"),
99 PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
100 APU2_GPIO_LINE_LED2, GPIO_ACTIVE_LOW),
101 { }
102 };
103
104 static const struct software_node apu2_led2_swnode = {
105 .name = "led-2",
106 .parent = &apu2_leds_node,
107 .properties = apu2_led2_props,
108 };
109
110 static const struct property_entry apu2_led3_props[] = {
111 PROPERTY_ENTRY_STRING("label", "apu:green:3"),
112 PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
113 APU2_GPIO_LINE_LED3, GPIO_ACTIVE_LOW),
114 { }
115 };
116
117 static const struct software_node apu2_led3_swnode = {
118 .name = "led-3",
119 .parent = &apu2_leds_node,
120 .properties = apu2_led3_props,
121 };
122
123 /* GPIO keyboard device */
124 static const struct property_entry apu2_keys_props[] = {
125 PROPERTY_ENTRY_U32("poll-interval", 100),
126 { }
127 };
128
129 static const struct software_node apu2_keys_node = {
130 .name = "apu2-keys",
131 .properties = apu2_keys_props,
132 };
133
134 static const struct property_entry apu2_front_button_props[] = {
135 PROPERTY_ENTRY_STRING("label", "front button"),
136 PROPERTY_ENTRY_U32("linux,code", KEY_RESTART),
137 PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
138 APU2_GPIO_LINE_MODESW, GPIO_ACTIVE_LOW),
139 PROPERTY_ENTRY_U32("debounce-interval", 10),
140 { }
141 };
142
143 static const struct software_node apu2_front_button_swnode = {
144 .name = "front-button",
145 .parent = &apu2_keys_node,
146 .properties = apu2_front_button_props,
147 };
148
149 static const struct software_node *apu2_swnodes[] = {
150 &apu2_gpiochip_node,
151 /* LEDs nodes */
152 &apu2_leds_node,
153 &apu2_led1_swnode,
154 &apu2_led2_swnode,
155 &apu2_led3_swnode,
156 /* Keys nodes */
157 &apu2_keys_node,
158 &apu2_front_button_swnode,
159 NULL
160 };
161
162 /* Board setup */
163
164 /* Note: matching works on string prefix, so "apu2" must come before "apu" */
165 static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = {
166
167 /* APU2 w/ legacy BIOS < 4.0.8 */
168 {
169 .ident = "apu2",
170 .matches = {
171 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
172 DMI_MATCH(DMI_BOARD_NAME, "APU2")
173 },
174 .driver_data = (void *)&board_apu2,
175 },
176 /* APU2 w/ legacy BIOS >= 4.0.8 */
177 {
178 .ident = "apu2",
179 .matches = {
180 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
181 DMI_MATCH(DMI_BOARD_NAME, "apu2")
182 },
183 .driver_data = (void *)&board_apu2,
184 },
185 /* APU2 w/ mainline BIOS */
186 {
187 .ident = "apu2",
188 .matches = {
189 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
190 DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")
191 },
192 .driver_data = (void *)&board_apu2,
193 },
194
195 /* APU3 w/ legacy BIOS < 4.0.8 */
196 {
197 .ident = "apu3",
198 .matches = {
199 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
200 DMI_MATCH(DMI_BOARD_NAME, "APU3")
201 },
202 .driver_data = (void *)&board_apu2,
203 },
204 /* APU3 w/ legacy BIOS >= 4.0.8 */
205 {
206 .ident = "apu3",
207 .matches = {
208 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
209 DMI_MATCH(DMI_BOARD_NAME, "apu3")
210 },
211 .driver_data = (void *)&board_apu2,
212 },
213 /* APU3 w/ mainline BIOS */
214 {
215 .ident = "apu3",
216 .matches = {
217 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
218 DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3")
219 },
220 .driver_data = (void *)&board_apu2,
221 },
222 /* APU4 w/ legacy BIOS < 4.0.8 */
223 {
224 .ident = "apu4",
225 .matches = {
226 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
227 DMI_MATCH(DMI_BOARD_NAME, "APU4")
228 },
229 .driver_data = (void *)&board_apu2,
230 },
231 /* APU4 w/ legacy BIOS >= 4.0.8 */
232 {
233 .ident = "apu4",
234 .matches = {
235 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
236 DMI_MATCH(DMI_BOARD_NAME, "apu4")
237 },
238 .driver_data = (void *)&board_apu2,
239 },
240 /* APU4 w/ mainline BIOS */
241 {
242 .ident = "apu4",
243 .matches = {
244 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
245 DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu4")
246 },
247 .driver_data = (void *)&board_apu2,
248 },
249 {}
250 };
251
252 static struct platform_device *apu_gpio_pdev;
253 static struct platform_device *apu_leds_pdev;
254 static struct platform_device *apu_keys_pdev;
255
apu_create_pdev(const char * name,const void * data,size_t size,const struct software_node * swnode)256 static struct platform_device * __init apu_create_pdev(const char *name,
257 const void *data, size_t size,
258 const struct software_node *swnode)
259 {
260 struct platform_device_info pdev_info = {
261 .name = name,
262 .id = PLATFORM_DEVID_NONE,
263 .data = data,
264 .size_data = size,
265 .fwnode = software_node_fwnode(swnode),
266 };
267 struct platform_device *pdev;
268 int err;
269
270 pdev = platform_device_register_full(&pdev_info);
271
272 err = PTR_ERR_OR_ZERO(pdev);
273 if (err)
274 pr_err("failed registering %s: %d\n", name, err);
275
276 return pdev;
277 }
278
apu_board_init(void)279 static int __init apu_board_init(void)
280 {
281 const struct dmi_system_id *id;
282 int err;
283
284 id = dmi_first_match(apu_gpio_dmi_table);
285 if (!id) {
286 pr_err("failed to detect APU board via DMI\n");
287 return -ENODEV;
288 }
289
290 err = software_node_register_node_group(apu2_swnodes);
291 if (err) {
292 pr_err("failed to register software nodes: %d\n", err);
293 return err;
294 }
295
296 apu_gpio_pdev = apu_create_pdev(AMD_FCH_GPIO_DRIVER_NAME,
297 id->driver_data, sizeof(struct amd_fch_gpio_pdata), NULL);
298 err = PTR_ERR_OR_ZERO(apu_gpio_pdev);
299 if (err)
300 goto err_unregister_swnodes;
301
302 apu_leds_pdev = apu_create_pdev("leds-gpio", NULL, 0, &apu2_leds_node);
303 err = PTR_ERR_OR_ZERO(apu_leds_pdev);
304 if (err)
305 goto err_unregister_gpio;
306
307 apu_keys_pdev = apu_create_pdev("gpio-keys-polled", NULL, 0, &apu2_keys_node);
308 err = PTR_ERR_OR_ZERO(apu_keys_pdev);
309 if (err)
310 goto err_unregister_leds;
311
312 return 0;
313
314 err_unregister_leds:
315 platform_device_unregister(apu_leds_pdev);
316 err_unregister_gpio:
317 platform_device_unregister(apu_gpio_pdev);
318 err_unregister_swnodes:
319 software_node_unregister_node_group(apu2_swnodes);
320 return err;
321 }
322
apu_board_exit(void)323 static void __exit apu_board_exit(void)
324 {
325 platform_device_unregister(apu_keys_pdev);
326 platform_device_unregister(apu_leds_pdev);
327 platform_device_unregister(apu_gpio_pdev);
328 software_node_unregister_node_group(apu2_swnodes);
329 }
330
331 module_init(apu_board_init);
332 module_exit(apu_board_exit);
333
334 MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
335 MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver");
336 MODULE_LICENSE("GPL");
337 MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);
338 MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");
339