2019-02-15 01:04:57 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* PC - Engines APUv2 / APUv3 board platform driver
* for gpio buttons and LEDs
*
* Copyright ( C ) 2018 metux IT consult
* Author : Enrico Weigelt < info @ metux . net >
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/dmi.h>
# include <linux/err.h>
# include <linux/kernel.h>
# include <linux/leds.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/gpio_keys.h>
# include <linux/gpio/machine.h>
# include <linux/input.h>
# include <linux/platform_data/gpio/gpio-amd-fch.h>
/*
* NOTE : this driver only supports APUv2 / 3 - not APUv1 , as this one
* has completely different register layouts
*/
/* register mappings */
# define APU2_GPIO_REG_LED1 AMD_FCH_GPIO_REG_GPIO57
# define APU2_GPIO_REG_LED2 AMD_FCH_GPIO_REG_GPIO58
# define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP1
# define APU2_GPIO_REG_MODESW AMD_FCH_GPIO_REG_GPIO32_GE1
# define APU2_GPIO_REG_SIMSWAP AMD_FCH_GPIO_REG_GPIO33_GE2
2019-07-26 09:21:15 +03:00
# define APU2_GPIO_REG_MPCIE2 AMD_FCH_GPIO_REG_GPIO59_DEVSLP0
# define APU2_GPIO_REG_MPCIE3 AMD_FCH_GPIO_REG_GPIO51
2019-02-15 01:04:57 +03:00
/* order in which the gpio lines are defined in the register list */
# define APU2_GPIO_LINE_LED1 0
# define APU2_GPIO_LINE_LED2 1
# define APU2_GPIO_LINE_LED3 2
# define APU2_GPIO_LINE_MODESW 3
# define APU2_GPIO_LINE_SIMSWAP 4
2019-07-26 09:21:15 +03:00
# define APU2_GPIO_LINE_MPCIE2 5
# define APU2_GPIO_LINE_MPCIE3 6
2019-02-15 01:04:57 +03:00
/* gpio device */
static int apu2_gpio_regs [ ] = {
[ APU2_GPIO_LINE_LED1 ] = APU2_GPIO_REG_LED1 ,
[ APU2_GPIO_LINE_LED2 ] = APU2_GPIO_REG_LED2 ,
[ APU2_GPIO_LINE_LED3 ] = APU2_GPIO_REG_LED3 ,
[ APU2_GPIO_LINE_MODESW ] = APU2_GPIO_REG_MODESW ,
[ APU2_GPIO_LINE_SIMSWAP ] = APU2_GPIO_REG_SIMSWAP ,
2019-07-26 09:21:15 +03:00
[ APU2_GPIO_LINE_MPCIE2 ] = APU2_GPIO_REG_MPCIE2 ,
[ APU2_GPIO_LINE_MPCIE3 ] = APU2_GPIO_REG_MPCIE3 ,
2019-02-15 01:04:57 +03:00
} ;
static const char * const apu2_gpio_names [ ] = {
[ APU2_GPIO_LINE_LED1 ] = " front-led1 " ,
[ APU2_GPIO_LINE_LED2 ] = " front-led2 " ,
[ APU2_GPIO_LINE_LED3 ] = " front-led3 " ,
[ APU2_GPIO_LINE_MODESW ] = " front-button " ,
[ APU2_GPIO_LINE_SIMSWAP ] = " simswap " ,
2019-07-26 09:21:15 +03:00
[ APU2_GPIO_LINE_MPCIE2 ] = " mpcie2_reset " ,
[ APU2_GPIO_LINE_MPCIE3 ] = " mpcie3_reset " ,
2019-02-15 01:04:57 +03:00
} ;
static const struct amd_fch_gpio_pdata board_apu2 = {
. gpio_num = ARRAY_SIZE ( apu2_gpio_regs ) ,
. gpio_reg = apu2_gpio_regs ,
. gpio_names = apu2_gpio_names ,
} ;
/* gpio leds device */
static const struct gpio_led apu2_leds [ ] = {
{ . name = " apu:green:1 " } ,
{ . name = " apu:green:2 " } ,
2019-07-29 17:39:26 +03:00
{ . name = " apu:green:3 " } ,
{ . name = " apu:simswap " } ,
2019-02-15 01:04:57 +03:00
} ;
static const struct gpio_led_platform_data apu2_leds_pdata = {
. num_leds = ARRAY_SIZE ( apu2_leds ) ,
. leds = apu2_leds ,
} ;
2019-05-29 17:38:44 +03:00
static struct gpiod_lookup_table gpios_led_table = {
2019-02-15 01:04:57 +03:00
. dev_id = " leds-gpio " ,
. table = {
GPIO_LOOKUP_IDX ( AMD_FCH_GPIO_DRIVER_NAME , APU2_GPIO_LINE_LED1 ,
NULL , 0 , GPIO_ACTIVE_LOW ) ,
GPIO_LOOKUP_IDX ( AMD_FCH_GPIO_DRIVER_NAME , APU2_GPIO_LINE_LED2 ,
NULL , 1 , GPIO_ACTIVE_LOW ) ,
GPIO_LOOKUP_IDX ( AMD_FCH_GPIO_DRIVER_NAME , APU2_GPIO_LINE_LED3 ,
NULL , 2 , GPIO_ACTIVE_LOW ) ,
2019-07-29 17:39:26 +03:00
GPIO_LOOKUP_IDX ( AMD_FCH_GPIO_DRIVER_NAME , APU2_GPIO_REG_SIMSWAP ,
NULL , 3 , GPIO_ACTIVE_LOW ) ,
2019-02-15 01:04:57 +03:00
}
} ;
/* gpio keyboard device */
static struct gpio_keys_button apu2_keys_buttons [ ] = {
{
2019-07-25 22:06:03 +03:00
. code = KEY_RESTART ,
2019-02-15 01:04:57 +03:00
. active_low = 1 ,
. desc = " front button " ,
. type = EV_KEY ,
. debounce_interval = 10 ,
. value = 1 ,
} ,
} ;
static const struct gpio_keys_platform_data apu2_keys_pdata = {
. buttons = apu2_keys_buttons ,
. nbuttons = ARRAY_SIZE ( apu2_keys_buttons ) ,
. poll_interval = 100 ,
. rep = 0 ,
. name = " apu2-keys " ,
} ;
2019-05-29 17:38:44 +03:00
static struct gpiod_lookup_table gpios_key_table = {
2019-02-15 01:04:57 +03:00
. dev_id = " gpio-keys-polled " ,
. table = {
GPIO_LOOKUP_IDX ( AMD_FCH_GPIO_DRIVER_NAME , APU2_GPIO_LINE_MODESW ,
NULL , 0 , GPIO_ACTIVE_LOW ) ,
}
} ;
/* board setup */
/* note: matching works on string prefix, so "apu2" must come before "apu" */
static const struct dmi_system_id apu_gpio_dmi_table [ ] __initconst = {
/* APU2 w/ legacy bios < 4.0.8 */
{
. ident = " apu2 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " PC Engines " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " APU2 " )
} ,
. driver_data = ( void * ) & board_apu2 ,
} ,
/* APU2 w/ legacy bios >= 4.0.8 */
{
. ident = " apu2 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " PC Engines " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " apu2 " )
} ,
. driver_data = ( void * ) & board_apu2 ,
} ,
/* APU2 w/ maainline bios */
{
. ident = " apu2 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " PC Engines " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " PC Engines apu2 " )
} ,
. driver_data = ( void * ) & board_apu2 ,
} ,
/* APU3 w/ legacy bios < 4.0.8 */
{
. ident = " apu3 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " PC Engines " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " APU3 " )
} ,
. driver_data = ( void * ) & board_apu2 ,
} ,
/* APU3 w/ legacy bios >= 4.0.8 */
{
. ident = " apu3 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " PC Engines " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " apu3 " )
} ,
. driver_data = ( void * ) & board_apu2 ,
} ,
/* APU3 w/ mainline bios */
{
. ident = " apu3 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " PC Engines " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " PC Engines apu3 " )
} ,
. driver_data = ( void * ) & board_apu2 ,
} ,
{ }
} ;
static struct platform_device * apu_gpio_pdev ;
static struct platform_device * apu_leds_pdev ;
static struct platform_device * apu_keys_pdev ;
static struct platform_device * __init apu_create_pdev (
const char * name ,
const void * pdata ,
size_t sz )
{
struct platform_device * pdev ;
pdev = platform_device_register_resndata ( NULL ,
name ,
PLATFORM_DEVID_NONE ,
NULL ,
0 ,
pdata ,
sz ) ;
if ( IS_ERR ( pdev ) )
pr_err ( " failed registering %s: %ld \n " , name , PTR_ERR ( pdev ) ) ;
return pdev ;
}
static int __init apu_board_init ( void )
{
const struct dmi_system_id * id ;
id = dmi_first_match ( apu_gpio_dmi_table ) ;
if ( ! id ) {
pr_err ( " failed to detect apu board via dmi \n " ) ;
return - ENODEV ;
}
gpiod_add_lookup_table ( & gpios_led_table ) ;
gpiod_add_lookup_table ( & gpios_key_table ) ;
apu_gpio_pdev = apu_create_pdev (
AMD_FCH_GPIO_DRIVER_NAME ,
id - > driver_data ,
sizeof ( struct amd_fch_gpio_pdata ) ) ;
apu_leds_pdev = apu_create_pdev (
" leds-gpio " ,
& apu2_leds_pdata ,
sizeof ( apu2_leds_pdata ) ) ;
apu_keys_pdev = apu_create_pdev (
" gpio-keys-polled " ,
& apu2_keys_pdata ,
sizeof ( apu2_keys_pdata ) ) ;
return 0 ;
}
static void __exit apu_board_exit ( void )
{
gpiod_remove_lookup_table ( & gpios_led_table ) ;
gpiod_remove_lookup_table ( & gpios_key_table ) ;
platform_device_unregister ( apu_keys_pdev ) ;
platform_device_unregister ( apu_leds_pdev ) ;
platform_device_unregister ( apu_gpio_pdev ) ;
}
module_init ( apu_board_init ) ;
module_exit ( apu_board_exit ) ;
MODULE_AUTHOR ( " Enrico Weigelt, metux IT consult <info@metux.net> " ) ;
MODULE_DESCRIPTION ( " PC Engines APUv2/APUv3 board GPIO/LED/keys driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( dmi , apu_gpio_dmi_table ) ;
MODULE_ALIAS ( " platform:pcengines-apuv2 " ) ;
2019-06-29 12:41:36 +03:00
MODULE_SOFTDEP ( " pre: platform: " AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled " ) ;