295 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * PC-Engines APUv2/APUv3 board platform driver5 * for GPIO buttons and LEDs6 *7 * Copyright (C) 2018 metux IT consult8 * Author: Enrico Weigelt <info@metux.net>9 */10 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt12 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 one26 * has completely different register layouts.27 */28 29/* Register mappings */30#define APU2_GPIO_REG_LED1 AMD_FCH_GPIO_REG_GPIO5731#define APU2_GPIO_REG_LED2 AMD_FCH_GPIO_REG_GPIO5832#define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP133#define APU2_GPIO_REG_MODESW AMD_FCH_GPIO_REG_GPIO32_GE134#define APU2_GPIO_REG_SIMSWAP AMD_FCH_GPIO_REG_GPIO33_GE235#define APU2_GPIO_REG_MPCIE2 AMD_FCH_GPIO_REG_GPIO55_DEVSLP036#define APU2_GPIO_REG_MPCIE3 AMD_FCH_GPIO_REG_GPIO5137 38/* Order in which the GPIO lines are defined in the register list */39#define APU2_GPIO_LINE_LED1 040#define APU2_GPIO_LINE_LED2 141#define APU2_GPIO_LINE_LED3 242#define APU2_GPIO_LINE_MODESW 343#define APU2_GPIO_LINE_SIMSWAP 444#define APU2_GPIO_LINE_MPCIE2 545#define APU2_GPIO_LINE_MPCIE3 646 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 {} /* Terminating entry */98 }99};100 101/* GPIO keyboard device */102 103static struct gpio_keys_button apu2_keys_buttons[] = {104 {105 .code = KEY_RESTART,106 .active_low = 1,107 .desc = "front button",108 .type = EV_KEY,109 .debounce_interval = 10,110 .value = 1,111 },112};113 114static const struct gpio_keys_platform_data apu2_keys_pdata = {115 .buttons = apu2_keys_buttons,116 .nbuttons = ARRAY_SIZE(apu2_keys_buttons),117 .poll_interval = 100,118 .rep = 0,119 .name = "apu2-keys",120};121 122static struct gpiod_lookup_table gpios_key_table = {123 .dev_id = "gpio-keys-polled",124 .table = {125 GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW,126 NULL, 0, GPIO_ACTIVE_LOW),127 {} /* Terminating entry */128 }129};130 131/* Board setup */132 133/* Note: matching works on string prefix, so "apu2" must come before "apu" */134static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = {135 136 /* APU2 w/ legacy BIOS < 4.0.8 */137 {138 .ident = "apu2",139 .matches = {140 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),141 DMI_MATCH(DMI_BOARD_NAME, "APU2")142 },143 .driver_data = (void *)&board_apu2,144 },145 /* APU2 w/ legacy BIOS >= 4.0.8 */146 {147 .ident = "apu2",148 .matches = {149 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),150 DMI_MATCH(DMI_BOARD_NAME, "apu2")151 },152 .driver_data = (void *)&board_apu2,153 },154 /* APU2 w/ mainline BIOS */155 {156 .ident = "apu2",157 .matches = {158 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),159 DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")160 },161 .driver_data = (void *)&board_apu2,162 },163 164 /* APU3 w/ legacy BIOS < 4.0.8 */165 {166 .ident = "apu3",167 .matches = {168 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),169 DMI_MATCH(DMI_BOARD_NAME, "APU3")170 },171 .driver_data = (void *)&board_apu2,172 },173 /* APU3 w/ legacy BIOS >= 4.0.8 */174 {175 .ident = "apu3",176 .matches = {177 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),178 DMI_MATCH(DMI_BOARD_NAME, "apu3")179 },180 .driver_data = (void *)&board_apu2,181 },182 /* APU3 w/ mainline BIOS */183 {184 .ident = "apu3",185 .matches = {186 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),187 DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3")188 },189 .driver_data = (void *)&board_apu2,190 },191 /* APU4 w/ legacy BIOS < 4.0.8 */192 {193 .ident = "apu4",194 .matches = {195 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),196 DMI_MATCH(DMI_BOARD_NAME, "APU4")197 },198 .driver_data = (void *)&board_apu2,199 },200 /* APU4 w/ legacy BIOS >= 4.0.8 */201 {202 .ident = "apu4",203 .matches = {204 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),205 DMI_MATCH(DMI_BOARD_NAME, "apu4")206 },207 .driver_data = (void *)&board_apu2,208 },209 /* APU4 w/ mainline BIOS */210 {211 .ident = "apu4",212 .matches = {213 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),214 DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu4")215 },216 .driver_data = (void *)&board_apu2,217 },218 {}219};220 221static struct platform_device *apu_gpio_pdev;222static struct platform_device *apu_leds_pdev;223static struct platform_device *apu_keys_pdev;224 225static struct platform_device * __init apu_create_pdev(226 const char *name,227 const void *pdata,228 size_t sz)229{230 struct platform_device *pdev;231 232 pdev = platform_device_register_resndata(NULL,233 name,234 PLATFORM_DEVID_NONE,235 NULL,236 0,237 pdata,238 sz);239 240 if (IS_ERR(pdev))241 pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));242 243 return pdev;244}245 246static int __init apu_board_init(void)247{248 const struct dmi_system_id *id;249 250 id = dmi_first_match(apu_gpio_dmi_table);251 if (!id) {252 pr_err("failed to detect APU board via DMI\n");253 return -ENODEV;254 }255 256 gpiod_add_lookup_table(&gpios_led_table);257 gpiod_add_lookup_table(&gpios_key_table);258 259 apu_gpio_pdev = apu_create_pdev(260 AMD_FCH_GPIO_DRIVER_NAME,261 id->driver_data,262 sizeof(struct amd_fch_gpio_pdata));263 264 apu_leds_pdev = apu_create_pdev(265 "leds-gpio",266 &apu2_leds_pdata,267 sizeof(apu2_leds_pdata));268 269 apu_keys_pdev = apu_create_pdev(270 "gpio-keys-polled",271 &apu2_keys_pdata,272 sizeof(apu2_keys_pdata));273 274 return 0;275}276 277static void __exit apu_board_exit(void)278{279 gpiod_remove_lookup_table(&gpios_led_table);280 gpiod_remove_lookup_table(&gpios_key_table);281 282 platform_device_unregister(apu_keys_pdev);283 platform_device_unregister(apu_leds_pdev);284 platform_device_unregister(apu_gpio_pdev);285}286 287module_init(apu_board_init);288module_exit(apu_board_exit);289 290MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");291MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver");292MODULE_LICENSE("GPL");293MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);294MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");295