Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 /*
0004  * PC-Engines APUv2/APUv3 board platform driver
0005  * for GPIO buttons and LEDs
0006  *
0007  * Copyright (C) 2018 metux IT consult
0008  * Author: Enrico Weigelt <info@metux.net>
0009  */
0010 
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012 
0013 #include <linux/dmi.h>
0014 #include <linux/err.h>
0015 #include <linux/kernel.h>
0016 #include <linux/leds.h>
0017 #include <linux/module.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/gpio_keys.h>
0020 #include <linux/gpio/machine.h>
0021 #include <linux/input.h>
0022 #include <linux/platform_data/gpio/gpio-amd-fch.h>
0023 
0024 /*
0025  * NOTE: this driver only supports APUv2/3 - not APUv1, as this one
0026  * has completely different register layouts.
0027  */
0028 
0029 /* Register mappings */
0030 #define APU2_GPIO_REG_LED1      AMD_FCH_GPIO_REG_GPIO57
0031 #define APU2_GPIO_REG_LED2      AMD_FCH_GPIO_REG_GPIO58
0032 #define APU2_GPIO_REG_LED3      AMD_FCH_GPIO_REG_GPIO59_DEVSLP1
0033 #define APU2_GPIO_REG_MODESW        AMD_FCH_GPIO_REG_GPIO32_GE1
0034 #define APU2_GPIO_REG_SIMSWAP       AMD_FCH_GPIO_REG_GPIO33_GE2
0035 #define APU2_GPIO_REG_MPCIE2        AMD_FCH_GPIO_REG_GPIO55_DEVSLP0
0036 #define APU2_GPIO_REG_MPCIE3        AMD_FCH_GPIO_REG_GPIO51
0037 
0038 /* Order in which the GPIO lines are defined in the register list */
0039 #define APU2_GPIO_LINE_LED1     0
0040 #define APU2_GPIO_LINE_LED2     1
0041 #define APU2_GPIO_LINE_LED3     2
0042 #define APU2_GPIO_LINE_MODESW       3
0043 #define APU2_GPIO_LINE_SIMSWAP      4
0044 #define APU2_GPIO_LINE_MPCIE2       5
0045 #define APU2_GPIO_LINE_MPCIE3       6
0046 
0047 /* GPIO device */
0048 
0049 static int apu2_gpio_regs[] = {
0050     [APU2_GPIO_LINE_LED1]       = APU2_GPIO_REG_LED1,
0051     [APU2_GPIO_LINE_LED2]       = APU2_GPIO_REG_LED2,
0052     [APU2_GPIO_LINE_LED3]       = APU2_GPIO_REG_LED3,
0053     [APU2_GPIO_LINE_MODESW]     = APU2_GPIO_REG_MODESW,
0054     [APU2_GPIO_LINE_SIMSWAP]    = APU2_GPIO_REG_SIMSWAP,
0055     [APU2_GPIO_LINE_MPCIE2]     = APU2_GPIO_REG_MPCIE2,
0056     [APU2_GPIO_LINE_MPCIE3]     = APU2_GPIO_REG_MPCIE3,
0057 };
0058 
0059 static const char * const apu2_gpio_names[] = {
0060     [APU2_GPIO_LINE_LED1]       = "front-led1",
0061     [APU2_GPIO_LINE_LED2]       = "front-led2",
0062     [APU2_GPIO_LINE_LED3]       = "front-led3",
0063     [APU2_GPIO_LINE_MODESW]     = "front-button",
0064     [APU2_GPIO_LINE_SIMSWAP]    = "simswap",
0065     [APU2_GPIO_LINE_MPCIE2]     = "mpcie2_reset",
0066     [APU2_GPIO_LINE_MPCIE3]     = "mpcie3_reset",
0067 };
0068 
0069 static const struct amd_fch_gpio_pdata board_apu2 = {
0070     .gpio_num   = ARRAY_SIZE(apu2_gpio_regs),
0071     .gpio_reg   = apu2_gpio_regs,
0072     .gpio_names = apu2_gpio_names,
0073 };
0074 
0075 /* GPIO LEDs device */
0076 
0077 static const struct gpio_led apu2_leds[] = {
0078     { .name = "apu:green:1" },
0079     { .name = "apu:green:2" },
0080     { .name = "apu:green:3" },
0081 };
0082 
0083 static const struct gpio_led_platform_data apu2_leds_pdata = {
0084     .num_leds   = ARRAY_SIZE(apu2_leds),
0085     .leds       = apu2_leds,
0086 };
0087 
0088 static struct gpiod_lookup_table gpios_led_table = {
0089     .dev_id = "leds-gpio",
0090     .table = {
0091         GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1,
0092                 NULL, 0, GPIO_ACTIVE_LOW),
0093         GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED2,
0094                 NULL, 1, GPIO_ACTIVE_LOW),
0095         GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3,
0096                 NULL, 2, GPIO_ACTIVE_LOW),
0097         {} /* Terminating entry */
0098     }
0099 };
0100 
0101 /* GPIO keyboard device */
0102 
0103 static struct gpio_keys_button apu2_keys_buttons[] = {
0104     {
0105         .code           = KEY_RESTART,
0106         .active_low     = 1,
0107         .desc           = "front button",
0108         .type           = EV_KEY,
0109         .debounce_interval  = 10,
0110         .value          = 1,
0111     },
0112 };
0113 
0114 static const struct gpio_keys_platform_data apu2_keys_pdata = {
0115     .buttons    = apu2_keys_buttons,
0116     .nbuttons   = ARRAY_SIZE(apu2_keys_buttons),
0117     .poll_interval  = 100,
0118     .rep        = 0,
0119     .name       = "apu2-keys",
0120 };
0121 
0122 static struct gpiod_lookup_table gpios_key_table = {
0123     .dev_id = "gpio-keys-polled",
0124     .table = {
0125         GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW,
0126                 NULL, 0, GPIO_ACTIVE_LOW),
0127         {} /* Terminating entry */
0128     }
0129 };
0130 
0131 /* Board setup */
0132 
0133 /* Note: matching works on string prefix, so "apu2" must come before "apu" */
0134 static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = {
0135 
0136     /* APU2 w/ legacy BIOS < 4.0.8 */
0137     {
0138         .ident      = "apu2",
0139         .matches    = {
0140             DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0141             DMI_MATCH(DMI_BOARD_NAME, "APU2")
0142         },
0143         .driver_data    = (void *)&board_apu2,
0144     },
0145     /* APU2 w/ legacy BIOS >= 4.0.8 */
0146     {
0147         .ident      = "apu2",
0148         .matches    = {
0149             DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0150             DMI_MATCH(DMI_BOARD_NAME, "apu2")
0151         },
0152         .driver_data    = (void *)&board_apu2,
0153     },
0154     /* APU2 w/ mainline BIOS */
0155     {
0156         .ident      = "apu2",
0157         .matches    = {
0158             DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0159             DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")
0160         },
0161         .driver_data    = (void *)&board_apu2,
0162     },
0163 
0164     /* APU3 w/ legacy BIOS < 4.0.8 */
0165     {
0166         .ident      = "apu3",
0167         .matches    = {
0168             DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0169             DMI_MATCH(DMI_BOARD_NAME, "APU3")
0170         },
0171         .driver_data = (void *)&board_apu2,
0172     },
0173     /* APU3 w/ legacy BIOS >= 4.0.8 */
0174     {
0175         .ident       = "apu3",
0176         .matches     = {
0177             DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0178             DMI_MATCH(DMI_BOARD_NAME, "apu3")
0179         },
0180         .driver_data = (void *)&board_apu2,
0181     },
0182     /* APU3 w/ mainline BIOS */
0183     {
0184         .ident       = "apu3",
0185         .matches     = {
0186             DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0187             DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3")
0188         },
0189         .driver_data = (void *)&board_apu2,
0190     },
0191     /* APU4 w/ legacy BIOS < 4.0.8 */
0192     {
0193         .ident        = "apu4",
0194         .matches    = {
0195             DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0196             DMI_MATCH(DMI_BOARD_NAME, "APU4")
0197         },
0198         .driver_data = (void *)&board_apu2,
0199     },
0200     /* APU4 w/ legacy BIOS >= 4.0.8 */
0201     {
0202         .ident       = "apu4",
0203         .matches     = {
0204             DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0205             DMI_MATCH(DMI_BOARD_NAME, "apu4")
0206         },
0207         .driver_data = (void *)&board_apu2,
0208     },
0209     /* APU4 w/ mainline BIOS */
0210     {
0211         .ident       = "apu4",
0212         .matches     = {
0213             DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0214             DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu4")
0215         },
0216         .driver_data = (void *)&board_apu2,
0217     },
0218     {}
0219 };
0220 
0221 static struct platform_device *apu_gpio_pdev;
0222 static struct platform_device *apu_leds_pdev;
0223 static struct platform_device *apu_keys_pdev;
0224 
0225 static struct platform_device * __init apu_create_pdev(
0226     const char *name,
0227     const void *pdata,
0228     size_t sz)
0229 {
0230     struct platform_device *pdev;
0231 
0232     pdev = platform_device_register_resndata(NULL,
0233         name,
0234         PLATFORM_DEVID_NONE,
0235         NULL,
0236         0,
0237         pdata,
0238         sz);
0239 
0240     if (IS_ERR(pdev))
0241         pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
0242 
0243     return pdev;
0244 }
0245 
0246 static int __init apu_board_init(void)
0247 {
0248     const struct dmi_system_id *id;
0249 
0250     id = dmi_first_match(apu_gpio_dmi_table);
0251     if (!id) {
0252         pr_err("failed to detect APU board via DMI\n");
0253         return -ENODEV;
0254     }
0255 
0256     gpiod_add_lookup_table(&gpios_led_table);
0257     gpiod_add_lookup_table(&gpios_key_table);
0258 
0259     apu_gpio_pdev = apu_create_pdev(
0260         AMD_FCH_GPIO_DRIVER_NAME,
0261         id->driver_data,
0262         sizeof(struct amd_fch_gpio_pdata));
0263 
0264     apu_leds_pdev = apu_create_pdev(
0265         "leds-gpio",
0266         &apu2_leds_pdata,
0267         sizeof(apu2_leds_pdata));
0268 
0269     apu_keys_pdev = apu_create_pdev(
0270         "gpio-keys-polled",
0271         &apu2_keys_pdata,
0272         sizeof(apu2_keys_pdata));
0273 
0274     return 0;
0275 }
0276 
0277 static void __exit apu_board_exit(void)
0278 {
0279     gpiod_remove_lookup_table(&gpios_led_table);
0280     gpiod_remove_lookup_table(&gpios_key_table);
0281 
0282     platform_device_unregister(apu_keys_pdev);
0283     platform_device_unregister(apu_leds_pdev);
0284     platform_device_unregister(apu_gpio_pdev);
0285 }
0286 
0287 module_init(apu_board_init);
0288 module_exit(apu_board_exit);
0289 
0290 MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
0291 MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver");
0292 MODULE_LICENSE("GPL");
0293 MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);
0294 MODULE_ALIAS("platform:pcengines-apuv2");
0295 MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");