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 49 static 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 59 static 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 69 static 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 static const struct software_node apu2_gpiochip_node = { 76 .name = AMD_FCH_GPIO_DRIVER_NAME, 77 }; 78 79 /* GPIO LEDs device */ 80 static const struct software_node apu2_leds_node = { 81 .name = "apu2-leds", 82 }; 83 84 static 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 91 static const struct software_node apu2_led1_swnode = { 92 .name = "led-1", 93 .parent = &apu2_leds_node, 94 .properties = apu2_led1_props, 95 }; 96 97 static 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 104 static const struct software_node apu2_led2_swnode = { 105 .name = "led-2", 106 .parent = &apu2_leds_node, 107 .properties = apu2_led2_props, 108 }; 109 110 static 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 117 static 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 */ 124 static const struct property_entry apu2_keys_props[] = { 125 PROPERTY_ENTRY_U32("poll-interval", 100), 126 { } 127 }; 128 129 static const struct software_node apu2_keys_node = { 130 .name = "apu2-keys", 131 .properties = apu2_keys_props, 132 }; 133 134 static 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 143 static 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 149 static 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" */ 165 static 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 252 static struct platform_device *apu_gpio_pdev; 253 static struct platform_device *apu_leds_pdev; 254 static struct platform_device *apu_keys_pdev; 255 256 static 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 279 static 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 314 err_unregister_leds: 315 platform_device_unregister(apu_leds_pdev); 316 err_unregister_gpio: 317 platform_device_unregister(apu_gpio_pdev); 318 err_unregister_swnodes: 319 software_node_unregister_node_group(apu2_swnodes); 320 return err; 321 } 322 323 static 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 331 module_init(apu_board_init); 332 module_exit(apu_board_exit); 333 334 MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>"); 335 MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver"); 336 MODULE_LICENSE("GPL"); 337 MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table); 338 MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled"); 339