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/kernel.h>
16 #include <linux/leds.h>
17 #include <linux/module.h>
18 #include <linux/platform_device.h>
19 #include <linux/gpio_keys.h>
20 #include <linux/gpio/machine.h>
21 #include <linux/input.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 
36 /* order in which the gpio lines are defined in the register list */
37 #define APU2_GPIO_LINE_LED1		0
38 #define APU2_GPIO_LINE_LED2		1
39 #define APU2_GPIO_LINE_LED3		2
40 #define APU2_GPIO_LINE_MODESW		3
41 #define APU2_GPIO_LINE_SIMSWAP		4
42 
43 /* gpio device */
44 
45 static int apu2_gpio_regs[] = {
46 	[APU2_GPIO_LINE_LED1]		= APU2_GPIO_REG_LED1,
47 	[APU2_GPIO_LINE_LED2]		= APU2_GPIO_REG_LED2,
48 	[APU2_GPIO_LINE_LED3]		= APU2_GPIO_REG_LED3,
49 	[APU2_GPIO_LINE_MODESW]		= APU2_GPIO_REG_MODESW,
50 	[APU2_GPIO_LINE_SIMSWAP]	= APU2_GPIO_REG_SIMSWAP,
51 };
52 
53 static const char * const apu2_gpio_names[] = {
54 	[APU2_GPIO_LINE_LED1]		= "front-led1",
55 	[APU2_GPIO_LINE_LED2]		= "front-led2",
56 	[APU2_GPIO_LINE_LED3]		= "front-led3",
57 	[APU2_GPIO_LINE_MODESW]		= "front-button",
58 	[APU2_GPIO_LINE_SIMSWAP]	= "simswap",
59 };
60 
61 static const struct amd_fch_gpio_pdata board_apu2 = {
62 	.gpio_num	= ARRAY_SIZE(apu2_gpio_regs),
63 	.gpio_reg	= apu2_gpio_regs,
64 	.gpio_names	= apu2_gpio_names,
65 };
66 
67 /* gpio leds device */
68 
69 static const struct gpio_led apu2_leds[] = {
70 	{ .name = "apu:green:1" },
71 	{ .name = "apu:green:2" },
72 	{ .name = "apu:green:3" }
73 };
74 
75 static const struct gpio_led_platform_data apu2_leds_pdata = {
76 	.num_leds	= ARRAY_SIZE(apu2_leds),
77 	.leds		= apu2_leds,
78 };
79 
80 static struct gpiod_lookup_table gpios_led_table = {
81 	.dev_id = "leds-gpio",
82 	.table = {
83 		GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1,
84 				NULL, 0, GPIO_ACTIVE_LOW),
85 		GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED2,
86 				NULL, 1, GPIO_ACTIVE_LOW),
87 		GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3,
88 				NULL, 2, GPIO_ACTIVE_LOW),
89 	}
90 };
91 
92 /* gpio keyboard device */
93 
94 static struct gpio_keys_button apu2_keys_buttons[] = {
95 	{
96 		.code			= KEY_RESTART,
97 		.active_low		= 1,
98 		.desc			= "front button",
99 		.type			= EV_KEY,
100 		.debounce_interval	= 10,
101 		.value			= 1,
102 	},
103 };
104 
105 static const struct gpio_keys_platform_data apu2_keys_pdata = {
106 	.buttons	= apu2_keys_buttons,
107 	.nbuttons	= ARRAY_SIZE(apu2_keys_buttons),
108 	.poll_interval	= 100,
109 	.rep		= 0,
110 	.name		= "apu2-keys",
111 };
112 
113 static struct gpiod_lookup_table gpios_key_table = {
114 	.dev_id = "gpio-keys-polled",
115 	.table = {
116 		GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW,
117 				NULL, 0, GPIO_ACTIVE_LOW),
118 	}
119 };
120 
121 /* board setup */
122 
123 /* note: matching works on string prefix, so "apu2" must come before "apu" */
124 static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = {
125 
126 	/* APU2 w/ legacy bios < 4.0.8 */
127 	{
128 		.ident		= "apu2",
129 		.matches	= {
130 			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
131 			DMI_MATCH(DMI_BOARD_NAME, "APU2")
132 		},
133 		.driver_data	= (void *)&board_apu2,
134 	},
135 	/* APU2 w/ legacy bios >= 4.0.8 */
136 	{
137 		.ident		= "apu2",
138 		.matches	= {
139 			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
140 			DMI_MATCH(DMI_BOARD_NAME, "apu2")
141 		},
142 		.driver_data	= (void *)&board_apu2,
143 	},
144 	/* APU2 w/ maainline bios */
145 	{
146 		.ident		= "apu2",
147 		.matches	= {
148 			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
149 			DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")
150 		},
151 		.driver_data	= (void *)&board_apu2,
152 	},
153 
154 	/* APU3 w/ legacy bios < 4.0.8 */
155 	{
156 		.ident		= "apu3",
157 		.matches	= {
158 			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
159 			DMI_MATCH(DMI_BOARD_NAME, "APU3")
160 		},
161 		.driver_data = (void *)&board_apu2,
162 	},
163 	/* APU3 w/ legacy bios >= 4.0.8 */
164 	{
165 		.ident       = "apu3",
166 		.matches     = {
167 			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
168 			DMI_MATCH(DMI_BOARD_NAME, "apu3")
169 		},
170 		.driver_data = (void *)&board_apu2,
171 	},
172 	/* APU3 w/ mainline bios */
173 	{
174 		.ident       = "apu3",
175 		.matches     = {
176 			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
177 			DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3")
178 		},
179 		.driver_data = (void *)&board_apu2,
180 	},
181 	{}
182 };
183 
184 static struct platform_device *apu_gpio_pdev;
185 static struct platform_device *apu_leds_pdev;
186 static struct platform_device *apu_keys_pdev;
187 
188 static struct platform_device * __init apu_create_pdev(
189 	const char *name,
190 	const void *pdata,
191 	size_t sz)
192 {
193 	struct platform_device *pdev;
194 
195 	pdev = platform_device_register_resndata(NULL,
196 		name,
197 		PLATFORM_DEVID_NONE,
198 		NULL,
199 		0,
200 		pdata,
201 		sz);
202 
203 	if (IS_ERR(pdev))
204 		pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
205 
206 	return pdev;
207 }
208 
209 static int __init apu_board_init(void)
210 {
211 	const struct dmi_system_id *id;
212 
213 	id = dmi_first_match(apu_gpio_dmi_table);
214 	if (!id) {
215 		pr_err("failed to detect apu board via dmi\n");
216 		return -ENODEV;
217 	}
218 
219 	gpiod_add_lookup_table(&gpios_led_table);
220 	gpiod_add_lookup_table(&gpios_key_table);
221 
222 	apu_gpio_pdev = apu_create_pdev(
223 		AMD_FCH_GPIO_DRIVER_NAME,
224 		id->driver_data,
225 		sizeof(struct amd_fch_gpio_pdata));
226 
227 	apu_leds_pdev = apu_create_pdev(
228 		"leds-gpio",
229 		&apu2_leds_pdata,
230 		sizeof(apu2_leds_pdata));
231 
232 	apu_keys_pdev = apu_create_pdev(
233 		"gpio-keys-polled",
234 		&apu2_keys_pdata,
235 		sizeof(apu2_keys_pdata));
236 
237 	return 0;
238 }
239 
240 static void __exit apu_board_exit(void)
241 {
242 	gpiod_remove_lookup_table(&gpios_led_table);
243 	gpiod_remove_lookup_table(&gpios_key_table);
244 
245 	platform_device_unregister(apu_keys_pdev);
246 	platform_device_unregister(apu_leds_pdev);
247 	platform_device_unregister(apu_gpio_pdev);
248 }
249 
250 module_init(apu_board_init);
251 module_exit(apu_board_exit);
252 
253 MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
254 MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LED/keys driver");
255 MODULE_LICENSE("GPL");
256 MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);
257 MODULE_ALIAS("platform:pcengines-apuv2");
258 MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");
259