xref: /linux/drivers/platform/x86/pcengines-apuv2.c (revision 6093a688a07da07808f0122f9aa2a3eed250d853)
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 
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 
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 
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