Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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