barco-p50-gpio.c (9376B)
1// SPDX-License-Identifier: GPL-2.0+ 2 3/* 4 * Support for EC-connected GPIOs for identify 5 * LED/button on Barco P50 board 6 * 7 * Copyright (C) 2021 Barco NV 8 * Author: Santosh Kumar Yadav <santoshkumar.yadav@barco.com> 9 */ 10 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 13#include <linux/delay.h> 14#include <linux/dmi.h> 15#include <linux/err.h> 16#include <linux/io.h> 17#include <linux/kernel.h> 18#include <linux/leds.h> 19#include <linux/module.h> 20#include <linux/platform_device.h> 21#include <linux/gpio_keys.h> 22#include <linux/gpio/driver.h> 23#include <linux/gpio/machine.h> 24#include <linux/input.h> 25 26 27#define DRIVER_NAME "barco-p50-gpio" 28 29/* GPIO lines */ 30#define P50_GPIO_LINE_LED 0 31#define P50_GPIO_LINE_BTN 1 32 33/* GPIO IO Ports */ 34#define P50_GPIO_IO_PORT_BASE 0x299 35 36#define P50_PORT_DATA 0x00 37#define P50_PORT_CMD 0x01 38 39#define P50_STATUS_OBF 0x01 /* EC output buffer full */ 40#define P50_STATUS_IBF 0x02 /* EC input buffer full */ 41 42#define P50_CMD_READ 0xa0 43#define P50_CMD_WRITE 0x50 44 45/* EC mailbox registers */ 46#define P50_MBOX_REG_CMD 0x00 47#define P50_MBOX_REG_STATUS 0x01 48#define P50_MBOX_REG_PARAM 0x02 49#define P50_MBOX_REG_DATA 0x03 50 51#define P50_MBOX_CMD_READ_GPIO 0x11 52#define P50_MBOX_CMD_WRITE_GPIO 0x12 53#define P50_MBOX_CMD_CLEAR 0xff 54 55#define P50_MBOX_STATUS_SUCCESS 0x01 56 57#define P50_MBOX_PARAM_LED 0x12 58#define P50_MBOX_PARAM_BTN 0x13 59 60 61struct p50_gpio { 62 struct gpio_chip gc; 63 struct mutex lock; 64 unsigned long base; 65 struct platform_device *leds_pdev; 66 struct platform_device *keys_pdev; 67}; 68 69static struct platform_device *gpio_pdev; 70 71static int gpio_params[] = { 72 [P50_GPIO_LINE_LED] = P50_MBOX_PARAM_LED, 73 [P50_GPIO_LINE_BTN] = P50_MBOX_PARAM_BTN, 74}; 75 76static const char * const gpio_names[] = { 77 [P50_GPIO_LINE_LED] = "identify-led", 78 [P50_GPIO_LINE_BTN] = "identify-button", 79}; 80 81 82static struct gpiod_lookup_table p50_gpio_led_table = { 83 .dev_id = "leds-gpio", 84 .table = { 85 GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH), 86 {} 87 } 88}; 89 90/* GPIO LEDs */ 91static struct gpio_led leds[] = { 92 { .name = "identify" } 93}; 94 95static struct gpio_led_platform_data leds_pdata = { 96 .num_leds = ARRAY_SIZE(leds), 97 .leds = leds, 98}; 99 100/* GPIO keyboard */ 101static struct gpio_keys_button buttons[] = { 102 { 103 .code = KEY_VENDOR, 104 .gpio = P50_GPIO_LINE_BTN, 105 .active_low = 1, 106 .type = EV_KEY, 107 .value = 1, 108 }, 109}; 110 111static struct gpio_keys_platform_data keys_pdata = { 112 .buttons = buttons, 113 .nbuttons = ARRAY_SIZE(buttons), 114 .poll_interval = 100, 115 .rep = 0, 116 .name = "identify", 117}; 118 119 120/* low level access routines */ 121 122static int p50_wait_ec(struct p50_gpio *p50, int mask, int expected) 123{ 124 int i, val; 125 126 for (i = 0; i < 100; i++) { 127 val = inb(p50->base + P50_PORT_CMD) & mask; 128 if (val == expected) 129 return 0; 130 usleep_range(500, 2000); 131 } 132 133 dev_err(p50->gc.parent, "Timed out waiting for EC (0x%x)\n", val); 134 return -ETIMEDOUT; 135} 136 137 138static int p50_read_mbox_reg(struct p50_gpio *p50, int reg) 139{ 140 int ret; 141 142 ret = p50_wait_ec(p50, P50_STATUS_IBF, 0); 143 if (ret) 144 return ret; 145 146 /* clear output buffer flag, prevent unfinished commands */ 147 inb(p50->base + P50_PORT_DATA); 148 149 /* cmd/address */ 150 outb(P50_CMD_READ | reg, p50->base + P50_PORT_CMD); 151 152 ret = p50_wait_ec(p50, P50_STATUS_OBF, P50_STATUS_OBF); 153 if (ret) 154 return ret; 155 156 return inb(p50->base + P50_PORT_DATA); 157} 158 159static int p50_write_mbox_reg(struct p50_gpio *p50, int reg, int val) 160{ 161 int ret; 162 163 ret = p50_wait_ec(p50, P50_STATUS_IBF, 0); 164 if (ret) 165 return ret; 166 167 /* cmd/address */ 168 outb(P50_CMD_WRITE | reg, p50->base + P50_PORT_CMD); 169 170 ret = p50_wait_ec(p50, P50_STATUS_IBF, 0); 171 if (ret) 172 return ret; 173 174 /* data */ 175 outb(val, p50->base + P50_PORT_DATA); 176 177 return 0; 178} 179 180 181/* mbox routines */ 182 183static int p50_wait_mbox_idle(struct p50_gpio *p50) 184{ 185 int i, val; 186 187 for (i = 0; i < 1000; i++) { 188 val = p50_read_mbox_reg(p50, P50_MBOX_REG_CMD); 189 /* cmd is 0 when idle */ 190 if (val <= 0) 191 return val; 192 193 usleep_range(500, 2000); 194 } 195 196 dev_err(p50->gc.parent, "Timed out waiting for EC mbox idle (CMD: 0x%x)\n", val); 197 198 return -ETIMEDOUT; 199} 200 201static int p50_send_mbox_cmd(struct p50_gpio *p50, int cmd, int param, int data) 202{ 203 int ret; 204 205 ret = p50_wait_mbox_idle(p50); 206 if (ret) 207 return ret; 208 209 ret = p50_write_mbox_reg(p50, P50_MBOX_REG_DATA, data); 210 if (ret) 211 return ret; 212 213 ret = p50_write_mbox_reg(p50, P50_MBOX_REG_PARAM, param); 214 if (ret) 215 return ret; 216 217 ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, cmd); 218 if (ret) 219 return ret; 220 221 ret = p50_wait_mbox_idle(p50); 222 if (ret) 223 return ret; 224 225 ret = p50_read_mbox_reg(p50, P50_MBOX_REG_STATUS); 226 if (ret < 0) 227 return ret; 228 229 if (ret == P50_MBOX_STATUS_SUCCESS) 230 return 0; 231 232 dev_err(p50->gc.parent, "Mbox command failed (CMD=0x%x STAT=0x%x PARAM=0x%x DATA=0x%x)\n", 233 cmd, ret, param, data); 234 235 return -EIO; 236} 237 238 239/* gpio routines */ 240 241static int p50_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) 242{ 243 switch (offset) { 244 case P50_GPIO_LINE_BTN: 245 return GPIO_LINE_DIRECTION_IN; 246 247 case P50_GPIO_LINE_LED: 248 return GPIO_LINE_DIRECTION_OUT; 249 250 default: 251 return -EINVAL; 252 } 253} 254 255static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset) 256{ 257 struct p50_gpio *p50 = gpiochip_get_data(gc); 258 int ret; 259 260 mutex_lock(&p50->lock); 261 262 ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0); 263 if (ret == 0) 264 ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA); 265 266 mutex_unlock(&p50->lock); 267 268 return ret; 269} 270 271static void p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) 272{ 273 struct p50_gpio *p50 = gpiochip_get_data(gc); 274 275 mutex_lock(&p50->lock); 276 277 p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, gpio_params[offset], value); 278 279 mutex_unlock(&p50->lock); 280} 281 282static int p50_gpio_probe(struct platform_device *pdev) 283{ 284 struct p50_gpio *p50; 285 struct resource *res; 286 int ret; 287 288 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 289 if (!res) { 290 dev_err(&pdev->dev, "Cannot get I/O ports\n"); 291 return -ENODEV; 292 } 293 294 if (!devm_request_region(&pdev->dev, res->start, resource_size(res), pdev->name)) { 295 dev_err(&pdev->dev, "Unable to reserve I/O region\n"); 296 return -EBUSY; 297 } 298 299 p50 = devm_kzalloc(&pdev->dev, sizeof(*p50), GFP_KERNEL); 300 if (!p50) 301 return -ENOMEM; 302 303 platform_set_drvdata(pdev, p50); 304 mutex_init(&p50->lock); 305 p50->base = res->start; 306 p50->gc.owner = THIS_MODULE; 307 p50->gc.parent = &pdev->dev; 308 p50->gc.label = dev_name(&pdev->dev); 309 p50->gc.ngpio = ARRAY_SIZE(gpio_names); 310 p50->gc.names = gpio_names; 311 p50->gc.can_sleep = true; 312 p50->gc.base = -1; 313 p50->gc.get_direction = p50_gpio_get_direction; 314 p50->gc.get = p50_gpio_get; 315 p50->gc.set = p50_gpio_set; 316 317 318 /* reset mbox */ 319 ret = p50_wait_mbox_idle(p50); 320 if (ret) 321 return ret; 322 323 ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, P50_MBOX_CMD_CLEAR); 324 if (ret) 325 return ret; 326 327 ret = p50_wait_mbox_idle(p50); 328 if (ret) 329 return ret; 330 331 332 ret = devm_gpiochip_add_data(&pdev->dev, &p50->gc, p50); 333 if (ret < 0) { 334 dev_err(&pdev->dev, "Could not register gpiochip: %d\n", ret); 335 return ret; 336 } 337 338 gpiod_add_lookup_table(&p50_gpio_led_table); 339 340 p50->leds_pdev = platform_device_register_data(&pdev->dev, 341 "leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata)); 342 343 if (IS_ERR(p50->leds_pdev)) { 344 ret = PTR_ERR(p50->leds_pdev); 345 dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret); 346 goto err_leds; 347 } 348 349 /* gpio-keys-polled uses old-style gpio interface, pass the right identifier */ 350 buttons[0].gpio += p50->gc.base; 351 352 p50->keys_pdev = 353 platform_device_register_data(&pdev->dev, "gpio-keys-polled", 354 PLATFORM_DEVID_NONE, 355 &keys_pdata, sizeof(keys_pdata)); 356 357 if (IS_ERR(p50->keys_pdev)) { 358 ret = PTR_ERR(p50->keys_pdev); 359 dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret); 360 goto err_keys; 361 } 362 363 return 0; 364 365err_keys: 366 platform_device_unregister(p50->leds_pdev); 367err_leds: 368 gpiod_remove_lookup_table(&p50_gpio_led_table); 369 370 return ret; 371} 372 373static int p50_gpio_remove(struct platform_device *pdev) 374{ 375 struct p50_gpio *p50 = platform_get_drvdata(pdev); 376 377 platform_device_unregister(p50->keys_pdev); 378 platform_device_unregister(p50->leds_pdev); 379 380 gpiod_remove_lookup_table(&p50_gpio_led_table); 381 382 return 0; 383} 384 385static struct platform_driver p50_gpio_driver = { 386 .driver = { 387 .name = DRIVER_NAME, 388 }, 389 .probe = p50_gpio_probe, 390 .remove = p50_gpio_remove, 391}; 392 393/* Board setup */ 394static const struct dmi_system_id dmi_ids[] __initconst = { 395 { 396 .matches = { 397 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Barco"), 398 DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50") 399 }, 400 }, 401 {} 402}; 403MODULE_DEVICE_TABLE(dmi, dmi_ids); 404 405static int __init p50_module_init(void) 406{ 407 struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1); 408 int ret; 409 410 if (!dmi_first_match(dmi_ids)) 411 return -ENODEV; 412 413 ret = platform_driver_register(&p50_gpio_driver); 414 if (ret) 415 return ret; 416 417 gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1); 418 if (IS_ERR(gpio_pdev)) { 419 pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev)); 420 platform_driver_unregister(&p50_gpio_driver); 421 return PTR_ERR(gpio_pdev); 422 } 423 424 return 0; 425} 426 427static void __exit p50_module_exit(void) 428{ 429 platform_device_unregister(gpio_pdev); 430 platform_driver_unregister(&p50_gpio_driver); 431} 432 433module_init(p50_module_init); 434module_exit(p50_module_exit); 435 436MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV <santoshkumar.yadav@barco.com>"); 437MODULE_DESCRIPTION("Barco P50 identify GPIOs driver"); 438MODULE_LICENSE("GPL");