FILE
pcengines-apuv2.c
File preview with syntax highlighting.
linux-6.19.14/drivers/platform/x86/pcengines-apuv2.c
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/gpio/machine.h> 16#include <linux/gpio/property.h> 17#include <linux/input-event-codes.h> 18#include <linux/kernel.h> 19#include <linux/module.h> 20#include <linux/platform_device.h> 21#include <linux/property.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 75static const struct software_node apu2_gpiochip_node = { 76 .name = AMD_FCH_GPIO_DRIVER_NAME, 77}; 78 79/* GPIO LEDs device */ 80static const struct software_node apu2_leds_node = { 81 .name = "apu2-leds", 82}; 83 84static const struct property_entry apu2_led1_props[] = { 85 PROPERTY_ENTRY_STRING("label", "apu:green:1"), 86 PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node, 87 APU2_GPIO_LINE_LED1, GPIO_ACTIVE_LOW), 88 { } 89}; 90 91static const struct software_node apu2_led1_swnode = { 92 .name = "led-1", 93 .parent = &apu2_leds_node, 94 .properties = apu2_led1_props, 95}; 96 97static const struct property_entry apu2_led2_props[] = { 98 PROPERTY_ENTRY_STRING("label", "apu:green:2"), 99 PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node, 100 APU2_GPIO_LINE_LED2, GPIO_ACTIVE_LOW), 101 { } 102}; 103 104static const struct software_node apu2_led2_swnode = { 105 .name = "led-2", 106 .parent = &apu2_leds_node, 107 .properties = apu2_led2_props, 108}; 109 110static const struct property_entry apu2_led3_props[] = { 111 PROPERTY_ENTRY_STRING("label", "apu:green:3"), 112 PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node, 113 APU2_GPIO_LINE_LED3, GPIO_ACTIVE_LOW), 114 { } 115}; 116 117static const struct software_node apu2_led3_swnode = { 118 .name = "led-3", 119 .parent = &apu2_leds_node, 120 .properties = apu2_led3_props, 121}; 122 123/* GPIO keyboard device */ 124static const struct property_entry apu2_keys_props[] = { 125 PROPERTY_ENTRY_U32("poll-interval", 100), 126 { } 127}; 128 129static const struct software_node apu2_keys_node = { 130 .name = "apu2-keys", 131 .properties = apu2_keys_props, 132}; 133 134static const struct property_entry apu2_front_button_props[] = { 135 PROPERTY_ENTRY_STRING("label", "front button"), 136 PROPERTY_ENTRY_U32("linux,code", KEY_RESTART), 137 PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node, 138 APU2_GPIO_LINE_MODESW, GPIO_ACTIVE_LOW), 139 PROPERTY_ENTRY_U32("debounce-interval", 10), 140 { } 141}; 142 143static const struct software_node apu2_front_button_swnode = { 144 .name = "front-button", 145 .parent = &apu2_keys_node, 146 .properties = apu2_front_button_props, 147}; 148 149static const struct software_node *apu2_swnodes[] = { 150 &apu2_gpiochip_node, 151 /* LEDs nodes */ 152 &apu2_leds_node, 153 &apu2_led1_swnode, 154 &apu2_led2_swnode, 155 &apu2_led3_swnode, 156 /* Keys nodes */ 157 &apu2_keys_node, 158 &apu2_front_button_swnode, 159 NULL 160}; 161 162/* Board setup */ 163 164/* Note: matching works on string prefix, so "apu2" must come before "apu" */ 165static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { 166 167 /* APU2 w/ legacy BIOS < 4.0.8 */ 168 { 169 .ident = "apu2", 170 .matches = { 171 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), 172 DMI_MATCH(DMI_BOARD_NAME, "APU2") 173 }, 174 .driver_data = (void *)&board_apu2, 175 }, 176 /* APU2 w/ legacy BIOS >= 4.0.8 */ 177 { 178 .ident = "apu2", 179 .matches = { 180 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), 181 DMI_MATCH(DMI_BOARD_NAME, "apu2") 182 }, 183 .driver_data = (void *)&board_apu2, 184 }, 185 /* APU2 w/ mainline BIOS */ 186 { 187 .ident = "apu2", 188 .matches = { 189 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), 190 DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2") 191 }, 192 .driver_data = (void *)&board_apu2, 193 }, 194 195 /* APU3 w/ legacy BIOS < 4.0.8 */ 196 { 197 .ident = "apu3", 198 .matches = { 199 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), 200 DMI_MATCH(DMI_BOARD_NAME, "APU3") 201 }, 202 .driver_data = (void *)&board_apu2, 203 }, 204 /* APU3 w/ legacy BIOS >= 4.0.8 */ 205 { 206 .ident = "apu3", 207 .matches = { 208 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), 209 DMI_MATCH(DMI_BOARD_NAME, "apu3") 210 }, 211 .driver_data = (void *)&board_apu2, 212 }, 213 /* APU3 w/ mainline BIOS */ 214 { 215 .ident = "apu3", 216 .matches = { 217 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), 218 DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3") 219 }, 220 .driver_data = (void *)&board_apu2, 221 }, 222 /* APU4 w/ legacy BIOS < 4.0.8 */ 223 { 224 .ident = "apu4", 225 .matches = { 226 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), 227 DMI_MATCH(DMI_BOARD_NAME, "APU4") 228 }, 229 .driver_data = (void *)&board_apu2, 230 }, 231 /* APU4 w/ legacy BIOS >= 4.0.8 */ 232 { 233 .ident = "apu4", 234 .matches = { 235 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), 236 DMI_MATCH(DMI_BOARD_NAME, "apu4") 237 }, 238 .driver_data = (void *)&board_apu2, 239 }, 240 /* APU4 w/ mainline BIOS */ 241 { 242 .ident = "apu4", 243 .matches = { 244 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), 245 DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu4") 246 }, 247 .driver_data = (void *)&board_apu2, 248 }, 249 {} 250}; 251 252static struct platform_device *apu_gpio_pdev; 253static struct platform_device *apu_leds_pdev; 254static struct platform_device *apu_keys_pdev; 255 256static struct platform_device * __init apu_create_pdev(const char *name, 257 const void *data, size_t size, 258 const struct software_node *swnode) 259{ 260 struct platform_device_info pdev_info = { 261 .name = name, 262 .id = PLATFORM_DEVID_NONE, 263 .data = data, 264 .size_data = size, 265 .fwnode = software_node_fwnode(swnode), 266 }; 267 struct platform_device *pdev; 268 int err; 269 270 pdev = platform_device_register_full(&pdev_info); 271 272 err = PTR_ERR_OR_ZERO(pdev); 273 if (err) 274 pr_err("failed registering %s: %d\n", name, err); 275 276 return pdev; 277} 278 279static int __init apu_board_init(void) 280{ 281 const struct dmi_system_id *id; 282 int err; 283 284 id = dmi_first_match(apu_gpio_dmi_table); 285 if (!id) { 286 pr_err("failed to detect APU board via DMI\n"); 287 return -ENODEV; 288 } 289 290 err = software_node_register_node_group(apu2_swnodes); 291 if (err) { 292 pr_err("failed to register software nodes: %d\n", err); 293 return err; 294 } 295 296 apu_gpio_pdev = apu_create_pdev(AMD_FCH_GPIO_DRIVER_NAME, 297 id->driver_data, sizeof(struct amd_fch_gpio_pdata), NULL); 298 err = PTR_ERR_OR_ZERO(apu_gpio_pdev); 299 if (err) 300 goto err_unregister_swnodes; 301 302 apu_leds_pdev = apu_create_pdev("leds-gpio", NULL, 0, &apu2_leds_node); 303 err = PTR_ERR_OR_ZERO(apu_leds_pdev); 304 if (err) 305 goto err_unregister_gpio; 306 307 apu_keys_pdev = apu_create_pdev("gpio-keys-polled", NULL, 0, &apu2_keys_node); 308 err = PTR_ERR_OR_ZERO(apu_keys_pdev); 309 if (err) 310 goto err_unregister_leds; 311 312 return 0; 313 314err_unregister_leds: 315 platform_device_unregister(apu_leds_pdev); 316err_unregister_gpio: 317 platform_device_unregister(apu_gpio_pdev); 318err_unregister_swnodes: 319 software_node_unregister_node_group(apu2_swnodes); 320 return err; 321} 322 323static void __exit apu_board_exit(void) 324{ 325 platform_device_unregister(apu_keys_pdev); 326 platform_device_unregister(apu_leds_pdev); 327 platform_device_unregister(apu_gpio_pdev); 328 software_node_unregister_node_group(apu2_swnodes); 329} 330 331module_init(apu_board_init); 332module_exit(apu_board_exit); 333 334MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>"); 335MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver"); 336MODULE_LICENSE("GPL"); 337MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table); 338MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");
~/
Read-only file preview.