Linux Audio

Check our new training course

Linux BSP development engineering services

Need help to port Linux and bootloaders to your hardware?
Loading...
Note: File does not exist in v3.1.
  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#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
 49static 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
 59static 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
 69static 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/* GPIO LEDs device */
 76
 77static const struct gpio_led apu2_leds[] = {
 78	{ .name = "apu:green:1" },
 79	{ .name = "apu:green:2" },
 80	{ .name = "apu:green:3" },
 81};
 82
 83static const struct gpio_led_platform_data apu2_leds_pdata = {
 84	.num_leds	= ARRAY_SIZE(apu2_leds),
 85	.leds		= apu2_leds,
 86};
 87
 88static struct gpiod_lookup_table gpios_led_table = {
 89	.dev_id = "leds-gpio",
 90	.table = {
 91		GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1,
 92				NULL, 0, GPIO_ACTIVE_LOW),
 93		GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED2,
 94				NULL, 1, GPIO_ACTIVE_LOW),
 95		GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3,
 96				NULL, 2, GPIO_ACTIVE_LOW),
 97	}
 98};
 99
100/* GPIO keyboard device */
101
102static struct gpio_keys_button apu2_keys_buttons[] = {
103	{
104		.code			= KEY_RESTART,
105		.active_low		= 1,
106		.desc			= "front button",
107		.type			= EV_KEY,
108		.debounce_interval	= 10,
109		.value			= 1,
110	},
111};
112
113static const struct gpio_keys_platform_data apu2_keys_pdata = {
114	.buttons	= apu2_keys_buttons,
115	.nbuttons	= ARRAY_SIZE(apu2_keys_buttons),
116	.poll_interval	= 100,
117	.rep		= 0,
118	.name		= "apu2-keys",
119};
120
121static struct gpiod_lookup_table gpios_key_table = {
122	.dev_id = "gpio-keys-polled",
123	.table = {
124		GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW,
125				NULL, 0, GPIO_ACTIVE_LOW),
126	}
127};
128
129/* Board setup */
130
131/* Note: matching works on string prefix, so "apu2" must come before "apu" */
132static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = {
133
134	/* APU2 w/ legacy BIOS < 4.0.8 */
135	{
136		.ident		= "apu2",
137		.matches	= {
138			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
139			DMI_MATCH(DMI_BOARD_NAME, "APU2")
140		},
141		.driver_data	= (void *)&board_apu2,
142	},
143	/* APU2 w/ legacy BIOS >= 4.0.8 */
144	{
145		.ident		= "apu2",
146		.matches	= {
147			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
148			DMI_MATCH(DMI_BOARD_NAME, "apu2")
149		},
150		.driver_data	= (void *)&board_apu2,
151	},
152	/* APU2 w/ mainline BIOS */
153	{
154		.ident		= "apu2",
155		.matches	= {
156			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
157			DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")
158		},
159		.driver_data	= (void *)&board_apu2,
160	},
161
162	/* APU3 w/ legacy BIOS < 4.0.8 */
163	{
164		.ident		= "apu3",
165		.matches	= {
166			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
167			DMI_MATCH(DMI_BOARD_NAME, "APU3")
168		},
169		.driver_data = (void *)&board_apu2,
170	},
171	/* APU3 w/ legacy BIOS >= 4.0.8 */
172	{
173		.ident       = "apu3",
174		.matches     = {
175			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
176			DMI_MATCH(DMI_BOARD_NAME, "apu3")
177		},
178		.driver_data = (void *)&board_apu2,
179	},
180	/* APU3 w/ mainline BIOS */
181	{
182		.ident       = "apu3",
183		.matches     = {
184			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
185			DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3")
186		},
187		.driver_data = (void *)&board_apu2,
188	},
189	/* APU4 w/ legacy BIOS < 4.0.8 */
190	{
191		.ident        = "apu4",
192		.matches    = {
193			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
194			DMI_MATCH(DMI_BOARD_NAME, "APU4")
195		},
196		.driver_data = (void *)&board_apu2,
197	},
198	/* APU4 w/ legacy BIOS >= 4.0.8 */
199	{
200		.ident       = "apu4",
201		.matches     = {
202			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
203			DMI_MATCH(DMI_BOARD_NAME, "apu4")
204		},
205		.driver_data = (void *)&board_apu2,
206	},
207	/* APU4 w/ mainline BIOS */
208	{
209		.ident       = "apu4",
210		.matches     = {
211			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
212			DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu4")
213		},
214		.driver_data = (void *)&board_apu2,
215	},
216	{}
217};
218
219static struct platform_device *apu_gpio_pdev;
220static struct platform_device *apu_leds_pdev;
221static struct platform_device *apu_keys_pdev;
222
223static struct platform_device * __init apu_create_pdev(
224	const char *name,
225	const void *pdata,
226	size_t sz)
227{
228	struct platform_device *pdev;
229
230	pdev = platform_device_register_resndata(NULL,
231		name,
232		PLATFORM_DEVID_NONE,
233		NULL,
234		0,
235		pdata,
236		sz);
237
238	if (IS_ERR(pdev))
239		pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
240
241	return pdev;
242}
243
244static int __init apu_board_init(void)
245{
246	const struct dmi_system_id *id;
247
248	id = dmi_first_match(apu_gpio_dmi_table);
249	if (!id) {
250		pr_err("failed to detect APU board via DMI\n");
251		return -ENODEV;
252	}
253
254	gpiod_add_lookup_table(&gpios_led_table);
255	gpiod_add_lookup_table(&gpios_key_table);
256
257	apu_gpio_pdev = apu_create_pdev(
258		AMD_FCH_GPIO_DRIVER_NAME,
259		id->driver_data,
260		sizeof(struct amd_fch_gpio_pdata));
261
262	apu_leds_pdev = apu_create_pdev(
263		"leds-gpio",
264		&apu2_leds_pdata,
265		sizeof(apu2_leds_pdata));
266
267	apu_keys_pdev = apu_create_pdev(
268		"gpio-keys-polled",
269		&apu2_keys_pdata,
270		sizeof(apu2_keys_pdata));
271
272	return 0;
273}
274
275static void __exit apu_board_exit(void)
276{
277	gpiod_remove_lookup_table(&gpios_led_table);
278	gpiod_remove_lookup_table(&gpios_key_table);
279
280	platform_device_unregister(apu_keys_pdev);
281	platform_device_unregister(apu_leds_pdev);
282	platform_device_unregister(apu_gpio_pdev);
283}
284
285module_init(apu_board_init);
286module_exit(apu_board_exit);
287
288MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
289MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver");
290MODULE_LICENSE("GPL");
291MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);
292MODULE_ALIAS("platform:pcengines-apuv2");
293MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");