495 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Intel La Jolla Cove Adapter USB-GPIO driver4 *5 * Copyright (c) 2023, Intel Corporation.6 */7 8#include <linux/acpi.h>9#include <linux/auxiliary_bus.h>10#include <linux/bitfield.h>11#include <linux/bitops.h>12#include <linux/dev_printk.h>13#include <linux/gpio/driver.h>14#include <linux/irq.h>15#include <linux/kernel.h>16#include <linux/kref.h>17#include <linux/module.h>18#include <linux/slab.h>19#include <linux/types.h>20#include <linux/usb/ljca.h>21 22/* GPIO commands */23#define LJCA_GPIO_CONFIG 124#define LJCA_GPIO_READ 225#define LJCA_GPIO_WRITE 326#define LJCA_GPIO_INT_EVENT 427#define LJCA_GPIO_INT_MASK 528#define LJCA_GPIO_INT_UNMASK 629 30#define LJCA_GPIO_CONF_DISABLE BIT(0)31#define LJCA_GPIO_CONF_INPUT BIT(1)32#define LJCA_GPIO_CONF_OUTPUT BIT(2)33#define LJCA_GPIO_CONF_PULLUP BIT(3)34#define LJCA_GPIO_CONF_PULLDOWN BIT(4)35#define LJCA_GPIO_CONF_DEFAULT BIT(5)36#define LJCA_GPIO_CONF_INTERRUPT BIT(6)37#define LJCA_GPIO_INT_TYPE BIT(7)38 39#define LJCA_GPIO_CONF_EDGE FIELD_PREP(LJCA_GPIO_INT_TYPE, 1)40#define LJCA_GPIO_CONF_LEVEL FIELD_PREP(LJCA_GPIO_INT_TYPE, 0)41 42/* Intentional overlap with PULLUP / PULLDOWN */43#define LJCA_GPIO_CONF_SET BIT(3)44#define LJCA_GPIO_CONF_CLR BIT(4)45 46#define LJCA_GPIO_BUF_SIZE 60u47 48struct ljca_gpio_op {49 u8 index;50 u8 value;51} __packed;52 53struct ljca_gpio_packet {54 u8 num;55 struct ljca_gpio_op item[] __counted_by(num);56} __packed;57 58struct ljca_gpio_dev {59 struct ljca_client *ljca;60 struct gpio_chip gc;61 struct ljca_gpio_info *gpio_info;62 DECLARE_BITMAP(unmasked_irqs, LJCA_MAX_GPIO_NUM);63 DECLARE_BITMAP(enabled_irqs, LJCA_MAX_GPIO_NUM);64 DECLARE_BITMAP(reenable_irqs, LJCA_MAX_GPIO_NUM);65 DECLARE_BITMAP(output_enabled, LJCA_MAX_GPIO_NUM);66 u8 *connect_mode;67 /* protect irq bus */68 struct mutex irq_lock;69 struct work_struct work;70 /* protect package transfer to hardware */71 struct mutex trans_lock;72 73 u8 obuf[LJCA_GPIO_BUF_SIZE];74 u8 ibuf[LJCA_GPIO_BUF_SIZE];75};76 77static int ljca_gpio_config(struct ljca_gpio_dev *ljca_gpio, u8 gpio_id,78 u8 config)79{80 struct ljca_gpio_packet *packet =81 (struct ljca_gpio_packet *)ljca_gpio->obuf;82 int ret;83 84 mutex_lock(&ljca_gpio->trans_lock);85 packet->item[0].index = gpio_id;86 packet->item[0].value = config | ljca_gpio->connect_mode[gpio_id];87 packet->num = 1;88 89 ret = ljca_transfer(ljca_gpio->ljca, LJCA_GPIO_CONFIG, (u8 *)packet,90 struct_size(packet, item, packet->num), NULL, 0);91 mutex_unlock(&ljca_gpio->trans_lock);92 93 return ret < 0 ? ret : 0;94}95 96static int ljca_gpio_read(struct ljca_gpio_dev *ljca_gpio, u8 gpio_id)97{98 struct ljca_gpio_packet *ack_packet =99 (struct ljca_gpio_packet *)ljca_gpio->ibuf;100 struct ljca_gpio_packet *packet =101 (struct ljca_gpio_packet *)ljca_gpio->obuf;102 int ret;103 104 mutex_lock(&ljca_gpio->trans_lock);105 packet->num = 1;106 packet->item[0].index = gpio_id;107 ret = ljca_transfer(ljca_gpio->ljca, LJCA_GPIO_READ, (u8 *)packet,108 struct_size(packet, item, packet->num),109 ljca_gpio->ibuf, LJCA_GPIO_BUF_SIZE);110 111 if (ret <= 0 || ack_packet->num != packet->num) {112 dev_err(&ljca_gpio->ljca->auxdev.dev,113 "read package error, gpio_id: %u num: %u ret: %d\n",114 gpio_id, ack_packet->num, ret);115 ret = ret < 0 ? ret : -EIO;116 }117 mutex_unlock(&ljca_gpio->trans_lock);118 119 return ret < 0 ? ret : ack_packet->item[0].value > 0;120}121 122static int ljca_gpio_write(struct ljca_gpio_dev *ljca_gpio, u8 gpio_id, int value)123{124 struct ljca_gpio_packet *packet =125 (struct ljca_gpio_packet *)ljca_gpio->obuf;126 int ret;127 128 mutex_lock(&ljca_gpio->trans_lock);129 packet->num = 1;130 packet->item[0].index = gpio_id;131 packet->item[0].value = value & 1;132 133 ret = ljca_transfer(ljca_gpio->ljca, LJCA_GPIO_WRITE, (u8 *)packet,134 struct_size(packet, item, packet->num), NULL, 0);135 mutex_unlock(&ljca_gpio->trans_lock);136 137 return ret < 0 ? ret : 0;138}139 140static int ljca_gpio_get_value(struct gpio_chip *chip, unsigned int offset)141{142 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(chip);143 144 return ljca_gpio_read(ljca_gpio, offset);145}146 147static void ljca_gpio_set_value(struct gpio_chip *chip, unsigned int offset,148 int val)149{150 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(chip);151 int ret;152 153 ret = ljca_gpio_write(ljca_gpio, offset, val);154 if (ret)155 dev_err(chip->parent,156 "set value failed offset: %u val: %d ret: %d\n",157 offset, val, ret);158}159 160static int ljca_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)161{162 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(chip);163 u8 config = LJCA_GPIO_CONF_INPUT | LJCA_GPIO_CONF_CLR;164 int ret;165 166 ret = ljca_gpio_config(ljca_gpio, offset, config);167 if (ret)168 return ret;169 170 clear_bit(offset, ljca_gpio->output_enabled);171 172 return 0;173}174 175static int ljca_gpio_direction_output(struct gpio_chip *chip,176 unsigned int offset, int val)177{178 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(chip);179 u8 config = LJCA_GPIO_CONF_OUTPUT | LJCA_GPIO_CONF_CLR;180 int ret;181 182 ret = ljca_gpio_config(ljca_gpio, offset, config);183 if (ret)184 return ret;185 186 ljca_gpio_set_value(chip, offset, val);187 set_bit(offset, ljca_gpio->output_enabled);188 189 return 0;190}191 192static int ljca_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)193{194 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(chip);195 196 if (test_bit(offset, ljca_gpio->output_enabled))197 return GPIO_LINE_DIRECTION_OUT;198 199 return GPIO_LINE_DIRECTION_IN;200}201 202static int ljca_gpio_set_config(struct gpio_chip *chip, unsigned int offset,203 unsigned long config)204{205 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(chip);206 207 ljca_gpio->connect_mode[offset] = 0;208 switch (pinconf_to_config_param(config)) {209 case PIN_CONFIG_BIAS_PULL_UP:210 ljca_gpio->connect_mode[offset] |= LJCA_GPIO_CONF_PULLUP;211 break;212 case PIN_CONFIG_BIAS_PULL_DOWN:213 ljca_gpio->connect_mode[offset] |= LJCA_GPIO_CONF_PULLDOWN;214 break;215 case PIN_CONFIG_DRIVE_PUSH_PULL:216 case PIN_CONFIG_PERSIST_STATE:217 break;218 default:219 return -ENOTSUPP;220 }221 222 return 0;223}224 225static int ljca_gpio_init_valid_mask(struct gpio_chip *chip,226 unsigned long *valid_mask,227 unsigned int ngpios)228{229 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(chip);230 231 WARN_ON_ONCE(ngpios != ljca_gpio->gpio_info->num);232 bitmap_copy(valid_mask, ljca_gpio->gpio_info->valid_pin_map, ngpios);233 234 return 0;235}236 237static void ljca_gpio_irq_init_valid_mask(struct gpio_chip *chip,238 unsigned long *valid_mask,239 unsigned int ngpios)240{241 ljca_gpio_init_valid_mask(chip, valid_mask, ngpios);242}243 244static int ljca_enable_irq(struct ljca_gpio_dev *ljca_gpio, int gpio_id,245 bool enable)246{247 struct ljca_gpio_packet *packet =248 (struct ljca_gpio_packet *)ljca_gpio->obuf;249 int ret;250 251 mutex_lock(&ljca_gpio->trans_lock);252 packet->num = 1;253 packet->item[0].index = gpio_id;254 packet->item[0].value = 0;255 256 ret = ljca_transfer(ljca_gpio->ljca,257 enable ? LJCA_GPIO_INT_UNMASK : LJCA_GPIO_INT_MASK,258 (u8 *)packet, struct_size(packet, item, packet->num),259 NULL, 0);260 mutex_unlock(&ljca_gpio->trans_lock);261 262 return ret < 0 ? ret : 0;263}264 265static void ljca_gpio_async(struct work_struct *work)266{267 struct ljca_gpio_dev *ljca_gpio =268 container_of(work, struct ljca_gpio_dev, work);269 int gpio_id, unmasked;270 271 for_each_set_bit(gpio_id, ljca_gpio->reenable_irqs, ljca_gpio->gc.ngpio) {272 clear_bit(gpio_id, ljca_gpio->reenable_irqs);273 unmasked = test_bit(gpio_id, ljca_gpio->unmasked_irqs);274 if (unmasked)275 ljca_enable_irq(ljca_gpio, gpio_id, true);276 }277}278 279static void ljca_gpio_event_cb(void *context, u8 cmd, const void *evt_data,280 int len)281{282 const struct ljca_gpio_packet *packet = evt_data;283 struct ljca_gpio_dev *ljca_gpio = context;284 int i, irq;285 286 if (cmd != LJCA_GPIO_INT_EVENT)287 return;288 289 for (i = 0; i < packet->num; i++) {290 irq = irq_find_mapping(ljca_gpio->gc.irq.domain,291 packet->item[i].index);292 if (!irq) {293 dev_err(ljca_gpio->gc.parent,294 "gpio_id %u does not mapped to IRQ yet\n",295 packet->item[i].index);296 return;297 }298 299 generic_handle_domain_irq(ljca_gpio->gc.irq.domain, irq);300 set_bit(packet->item[i].index, ljca_gpio->reenable_irqs);301 }302 303 schedule_work(&ljca_gpio->work);304}305 306static void ljca_irq_unmask(struct irq_data *irqd)307{308 struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);309 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(gc);310 int gpio_id = irqd_to_hwirq(irqd);311 312 gpiochip_enable_irq(gc, gpio_id);313 set_bit(gpio_id, ljca_gpio->unmasked_irqs);314}315 316static void ljca_irq_mask(struct irq_data *irqd)317{318 struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);319 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(gc);320 int gpio_id = irqd_to_hwirq(irqd);321 322 clear_bit(gpio_id, ljca_gpio->unmasked_irqs);323 gpiochip_disable_irq(gc, gpio_id);324}325 326static int ljca_irq_set_type(struct irq_data *irqd, unsigned int type)327{328 struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);329 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(gc);330 int gpio_id = irqd_to_hwirq(irqd);331 332 ljca_gpio->connect_mode[gpio_id] = LJCA_GPIO_CONF_INTERRUPT;333 switch (type) {334 case IRQ_TYPE_LEVEL_HIGH:335 ljca_gpio->connect_mode[gpio_id] |=336 (LJCA_GPIO_CONF_LEVEL | LJCA_GPIO_CONF_PULLUP);337 break;338 case IRQ_TYPE_LEVEL_LOW:339 ljca_gpio->connect_mode[gpio_id] |=340 (LJCA_GPIO_CONF_LEVEL | LJCA_GPIO_CONF_PULLDOWN);341 break;342 case IRQ_TYPE_EDGE_BOTH:343 break;344 case IRQ_TYPE_EDGE_RISING:345 ljca_gpio->connect_mode[gpio_id] |=346 (LJCA_GPIO_CONF_EDGE | LJCA_GPIO_CONF_PULLUP);347 break;348 case IRQ_TYPE_EDGE_FALLING:349 ljca_gpio->connect_mode[gpio_id] |=350 (LJCA_GPIO_CONF_EDGE | LJCA_GPIO_CONF_PULLDOWN);351 break;352 default:353 return -EINVAL;354 }355 356 return 0;357}358 359static void ljca_irq_bus_lock(struct irq_data *irqd)360{361 struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);362 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(gc);363 364 mutex_lock(&ljca_gpio->irq_lock);365}366 367static void ljca_irq_bus_unlock(struct irq_data *irqd)368{369 struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);370 struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(gc);371 int gpio_id = irqd_to_hwirq(irqd);372 int enabled, unmasked;373 374 enabled = test_bit(gpio_id, ljca_gpio->enabled_irqs);375 unmasked = test_bit(gpio_id, ljca_gpio->unmasked_irqs);376 377 if (enabled != unmasked) {378 if (unmasked) {379 ljca_gpio_config(ljca_gpio, gpio_id, 0);380 ljca_enable_irq(ljca_gpio, gpio_id, true);381 set_bit(gpio_id, ljca_gpio->enabled_irqs);382 } else {383 ljca_enable_irq(ljca_gpio, gpio_id, false);384 clear_bit(gpio_id, ljca_gpio->enabled_irqs);385 }386 }387 388 mutex_unlock(&ljca_gpio->irq_lock);389}390 391static const struct irq_chip ljca_gpio_irqchip = {392 .name = "ljca-irq",393 .irq_mask = ljca_irq_mask,394 .irq_unmask = ljca_irq_unmask,395 .irq_set_type = ljca_irq_set_type,396 .irq_bus_lock = ljca_irq_bus_lock,397 .irq_bus_sync_unlock = ljca_irq_bus_unlock,398 .flags = IRQCHIP_IMMUTABLE,399 GPIOCHIP_IRQ_RESOURCE_HELPERS,400};401 402static int ljca_gpio_probe(struct auxiliary_device *auxdev,403 const struct auxiliary_device_id *aux_dev_id)404{405 struct ljca_client *ljca = auxiliary_dev_to_ljca_client(auxdev);406 struct ljca_gpio_dev *ljca_gpio;407 struct gpio_irq_chip *girq;408 int ret;409 410 ljca_gpio = devm_kzalloc(&auxdev->dev, sizeof(*ljca_gpio), GFP_KERNEL);411 if (!ljca_gpio)412 return -ENOMEM;413 414 ljca_gpio->ljca = ljca;415 ljca_gpio->gpio_info = dev_get_platdata(&auxdev->dev);416 ljca_gpio->connect_mode = devm_kcalloc(&auxdev->dev,417 ljca_gpio->gpio_info->num,418 sizeof(*ljca_gpio->connect_mode),419 GFP_KERNEL);420 if (!ljca_gpio->connect_mode)421 return -ENOMEM;422 423 mutex_init(&ljca_gpio->irq_lock);424 mutex_init(&ljca_gpio->trans_lock);425 ljca_gpio->gc.direction_input = ljca_gpio_direction_input;426 ljca_gpio->gc.direction_output = ljca_gpio_direction_output;427 ljca_gpio->gc.get_direction = ljca_gpio_get_direction;428 ljca_gpio->gc.get = ljca_gpio_get_value;429 ljca_gpio->gc.set = ljca_gpio_set_value;430 ljca_gpio->gc.set_config = ljca_gpio_set_config;431 ljca_gpio->gc.init_valid_mask = ljca_gpio_init_valid_mask;432 ljca_gpio->gc.can_sleep = true;433 ljca_gpio->gc.parent = &auxdev->dev;434 435 ljca_gpio->gc.base = -1;436 ljca_gpio->gc.ngpio = ljca_gpio->gpio_info->num;437 ljca_gpio->gc.label = ACPI_COMPANION(&auxdev->dev) ?438 acpi_dev_name(ACPI_COMPANION(&auxdev->dev)) :439 dev_name(&auxdev->dev);440 ljca_gpio->gc.owner = THIS_MODULE;441 442 auxiliary_set_drvdata(auxdev, ljca_gpio);443 ljca_register_event_cb(ljca, ljca_gpio_event_cb, ljca_gpio);444 445 girq = &ljca_gpio->gc.irq;446 gpio_irq_chip_set_chip(girq, &ljca_gpio_irqchip);447 girq->parent_handler = NULL;448 girq->num_parents = 0;449 girq->parents = NULL;450 girq->default_type = IRQ_TYPE_NONE;451 girq->handler = handle_simple_irq;452 girq->init_valid_mask = ljca_gpio_irq_init_valid_mask;453 454 INIT_WORK(&ljca_gpio->work, ljca_gpio_async);455 ret = gpiochip_add_data(&ljca_gpio->gc, ljca_gpio);456 if (ret) {457 ljca_unregister_event_cb(ljca);458 mutex_destroy(&ljca_gpio->irq_lock);459 mutex_destroy(&ljca_gpio->trans_lock);460 }461 462 return ret;463}464 465static void ljca_gpio_remove(struct auxiliary_device *auxdev)466{467 struct ljca_gpio_dev *ljca_gpio = auxiliary_get_drvdata(auxdev);468 469 gpiochip_remove(&ljca_gpio->gc);470 ljca_unregister_event_cb(ljca_gpio->ljca);471 cancel_work_sync(&ljca_gpio->work);472 mutex_destroy(&ljca_gpio->irq_lock);473 mutex_destroy(&ljca_gpio->trans_lock);474}475 476static const struct auxiliary_device_id ljca_gpio_id_table[] = {477 { "usb_ljca.ljca-gpio", 0 },478 { /* sentinel */ },479};480MODULE_DEVICE_TABLE(auxiliary, ljca_gpio_id_table);481 482static struct auxiliary_driver ljca_gpio_driver = {483 .probe = ljca_gpio_probe,484 .remove = ljca_gpio_remove,485 .id_table = ljca_gpio_id_table,486};487module_auxiliary_driver(ljca_gpio_driver);488 489MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");490MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");491MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>");492MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-GPIO driver");493MODULE_LICENSE("GPL");494MODULE_IMPORT_NS(LJCA);495