123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836 |
- /* drivers/gpio/gpio-msm-smp2p.c
- *
- * Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/bitmap.h>
- #include <linux/of.h>
- #include <linux/gpio.h>
- #include <linux/interrupt.h>
- #include <linux/irq.h>
- #include <linux/irqdomain.h>
- #include <linux/slab.h>
- #include <linux/list.h>
- #include <linux/ipc_logging.h>
- #include "../soc/qcom/smp2p_private_api.h"
- #include "../soc/qcom/smp2p_private.h"
- /* GPIO device - one per SMP2P entry. */
- struct smp2p_chip_dev {
- struct list_head entry_list;
- char name[SMP2P_MAX_ENTRY_NAME];
- int remote_pid;
- bool is_inbound;
- bool is_open;
- bool in_shadow;
- uint32_t shadow_value;
- struct work_struct shadow_work;
- spinlock_t shadow_lock;
- struct notifier_block out_notifier;
- struct notifier_block in_notifier;
- struct msm_smp2p_out *out_handle;
- struct gpio_chip gpio;
- struct irq_domain *irq_domain;
- int irq_base;
- spinlock_t irq_lock;
- DECLARE_BITMAP(irq_enabled, SMP2P_BITS_PER_ENTRY);
- DECLARE_BITMAP(irq_rising_edge, SMP2P_BITS_PER_ENTRY);
- DECLARE_BITMAP(irq_falling_edge, SMP2P_BITS_PER_ENTRY);
- };
- static struct platform_driver smp2p_gpio_driver;
- static struct lock_class_key smp2p_gpio_lock_class;
- static struct irq_chip smp2p_gpio_irq_chip;
- static DEFINE_SPINLOCK(smp2p_entry_lock_lha1);
- static LIST_HEAD(smp2p_entry_list);
- /* Used for mapping edge to name for logging. */
- static const char * const edge_names[] = {
- "-",
- "0->1",
- "1->0",
- "-",
- };
- /* Used for mapping edge to value for logging. */
- static const char * const edge_name_rising[] = {
- "-",
- "0->1",
- };
- /* Used for mapping edge to value for logging. */
- static const char * const edge_name_falling[] = {
- "-",
- "1->0",
- };
- static int smp2p_gpio_to_irq(struct gpio_chip *cp,
- unsigned int offset);
- /**
- * smp2p_get_value - Retrieves GPIO value.
- *
- * @cp: GPIO chip pointer
- * @offset: Pin offset
- * @returns: >=0: value of GPIO Pin; < 0 for error
- *
- * Error codes:
- * -ENODEV - chip/entry invalid
- * -ENETDOWN - valid entry, but entry not yet created
- */
- static int smp2p_get_value(struct gpio_chip *cp,
- unsigned int offset)
- {
- struct smp2p_chip_dev *chip;
- int ret = 0;
- uint32_t data;
- if (!cp)
- return -ENODEV;
- chip = container_of(cp, struct smp2p_chip_dev, gpio);
- if (!chip->is_open)
- return -ENETDOWN;
- if (chip->is_inbound)
- ret = msm_smp2p_in_read(chip->remote_pid, chip->name, &data);
- else
- ret = msm_smp2p_out_read(chip->out_handle, &data);
- if (!ret)
- ret = (data & (1 << offset)) ? 1 : 0;
- return ret;
- }
- /**
- * smp2p_set_value - Sets GPIO value.
- *
- * @cp: GPIO chip pointer
- * @offset: Pin offset
- * @value: New value
- */
- static void smp2p_set_value(struct gpio_chip *cp, unsigned int offset,
- int value)
- {
- struct smp2p_chip_dev *chip;
- uint32_t data_set;
- uint32_t data_clear;
- bool send_irq;
- int ret;
- unsigned long flags;
- if (!cp)
- return;
- chip = container_of(cp, struct smp2p_chip_dev, gpio);
- if (chip->is_inbound) {
- SMP2P_INFO("%s: '%s':%d virq %d invalid operation\n",
- __func__, chip->name, chip->remote_pid,
- chip->irq_base + offset);
- return;
- }
- if (value & SMP2P_GPIO_NO_INT) {
- value &= ~SMP2P_GPIO_NO_INT;
- send_irq = false;
- } else {
- send_irq = true;
- }
- if (value) {
- data_set = 1 << offset;
- data_clear = 0;
- } else {
- data_set = 0;
- data_clear = 1 << offset;
- }
- spin_lock_irqsave(&chip->shadow_lock, flags);
- if (!chip->is_open) {
- chip->in_shadow = true;
- chip->shadow_value &= ~data_clear;
- chip->shadow_value |= data_set;
- spin_unlock_irqrestore(&chip->shadow_lock, flags);
- return;
- }
- if (chip->in_shadow) {
- chip->in_shadow = false;
- chip->shadow_value &= ~data_clear;
- chip->shadow_value |= data_set;
- ret = msm_smp2p_out_modify(chip->out_handle,
- chip->shadow_value, 0x0, send_irq);
- chip->shadow_value = 0x0;
- } else {
- ret = msm_smp2p_out_modify(chip->out_handle,
- data_set, data_clear, send_irq);
- }
- spin_unlock_irqrestore(&chip->shadow_lock, flags);
- if (ret)
- SMP2P_GPIO("'%s':%d gpio %d set to %d failed (%d)\n",
- chip->name, chip->remote_pid,
- chip->gpio.base + offset, value, ret);
- else
- SMP2P_GPIO("'%s':%d gpio %d set to %d\n",
- chip->name, chip->remote_pid,
- chip->gpio.base + offset, value);
- }
- /**
- * smp2p_direction_input - Sets GPIO direction to input.
- *
- * @cp: GPIO chip pointer
- * @offset: Pin offset
- * @returns: 0 for success; < 0 for failure
- */
- static int smp2p_direction_input(struct gpio_chip *cp, unsigned int offset)
- {
- struct smp2p_chip_dev *chip;
- if (!cp)
- return -ENODEV;
- chip = container_of(cp, struct smp2p_chip_dev, gpio);
- if (!chip->is_inbound)
- return -EPERM;
- return 0;
- }
- /**
- * smp2p_direction_output - Sets GPIO direction to output.
- *
- * @cp: GPIO chip pointer
- * @offset: Pin offset
- * @value: Direction
- * @returns: 0 for success; < 0 for failure
- */
- static int smp2p_direction_output(struct gpio_chip *cp,
- unsigned int offset, int value)
- {
- struct smp2p_chip_dev *chip;
- if (!cp)
- return -ENODEV;
- chip = container_of(cp, struct smp2p_chip_dev, gpio);
- if (chip->is_inbound)
- return -EPERM;
- return 0;
- }
- /**
- * smp2p_gpio_to_irq - Convert GPIO pin to virtual IRQ pin.
- *
- * @cp: GPIO chip pointer
- * @offset: Pin offset
- * @returns: >0 for virtual irq value; < 0 for failure
- */
- static int smp2p_gpio_to_irq(struct gpio_chip *cp, unsigned int offset)
- {
- struct smp2p_chip_dev *chip;
- chip = container_of(cp, struct smp2p_chip_dev, gpio);
- if (!cp || chip->irq_base <= 0)
- return -ENODEV;
- return chip->irq_base + offset;
- }
- /**
- * smp2p_gpio_irq_mask_helper - Mask/Unmask interrupt.
- *
- * @d: IRQ data
- * @mask: true to mask (disable), false to unmask (enable)
- */
- static void smp2p_gpio_irq_mask_helper(struct irq_data *d, bool mask)
- {
- struct smp2p_chip_dev *chip;
- int offset;
- unsigned long flags;
- chip = (struct smp2p_chip_dev *)irq_get_chip_data(d->irq);
- if (!chip || chip->irq_base <= 0)
- return;
- offset = d->irq - chip->irq_base;
- spin_lock_irqsave(&chip->irq_lock, flags);
- if (mask)
- clear_bit(offset, chip->irq_enabled);
- else
- set_bit(offset, chip->irq_enabled);
- spin_unlock_irqrestore(&chip->irq_lock, flags);
- }
- /**
- * smp2p_gpio_irq_mask - Mask interrupt.
- *
- * @d: IRQ data
- */
- static void smp2p_gpio_irq_mask(struct irq_data *d)
- {
- smp2p_gpio_irq_mask_helper(d, true);
- }
- /**
- * smp2p_gpio_irq_unmask - Unmask interrupt.
- *
- * @d: IRQ data
- */
- static void smp2p_gpio_irq_unmask(struct irq_data *d)
- {
- smp2p_gpio_irq_mask_helper(d, false);
- }
- /**
- * smp2p_gpio_irq_set_type - Set interrupt edge type.
- *
- * @d: IRQ data
- * @type: Edge type for interrupt
- * @returns 0 for success; < 0 for failure
- */
- static int smp2p_gpio_irq_set_type(struct irq_data *d, unsigned int type)
- {
- struct smp2p_chip_dev *chip;
- int offset;
- unsigned long flags;
- int ret = 0;
- chip = (struct smp2p_chip_dev *)irq_get_chip_data(d->irq);
- if (!chip)
- return -ENODEV;
- if (chip->irq_base <= 0) {
- SMP2P_ERR("%s: '%s':%d virqbase %d invalid\n",
- __func__, chip->name, chip->remote_pid,
- chip->irq_base);
- return -ENODEV;
- }
- offset = d->irq - chip->irq_base;
- spin_lock_irqsave(&chip->irq_lock, flags);
- clear_bit(offset, chip->irq_rising_edge);
- clear_bit(offset, chip->irq_falling_edge);
- switch (type) {
- case IRQ_TYPE_EDGE_RISING:
- set_bit(offset, chip->irq_rising_edge);
- break;
- case IRQ_TYPE_EDGE_FALLING:
- set_bit(offset, chip->irq_falling_edge);
- break;
- case IRQ_TYPE_NONE:
- case IRQ_TYPE_DEFAULT:
- case IRQ_TYPE_EDGE_BOTH:
- set_bit(offset, chip->irq_rising_edge);
- set_bit(offset, chip->irq_falling_edge);
- break;
- default:
- SMP2P_ERR("%s: unsupported interrupt type 0x%x\n",
- __func__, type);
- ret = -EINVAL;
- break;
- }
- spin_unlock_irqrestore(&chip->irq_lock, flags);
- return ret;
- }
- /**
- * smp2p_irq_map - Creates or updates binding of virtual IRQ
- *
- * @domain_ptr: Interrupt domain pointer
- * @virq: Virtual IRQ
- * @hw: Hardware IRQ (same as virq for nomap)
- * @returns: 0 for success
- */
- static int smp2p_irq_map(struct irq_domain *domain_ptr, unsigned int virq,
- irq_hw_number_t hw)
- {
- struct smp2p_chip_dev *chip;
- chip = domain_ptr->host_data;
- if (!chip) {
- SMP2P_ERR("%s: invalid domain ptr\n", __func__);
- return -ENODEV;
- }
- /* map chip structures to device */
- irq_set_lockdep_class(virq, &smp2p_gpio_lock_class);
- irq_set_chip_and_handler(virq, &smp2p_gpio_irq_chip,
- handle_level_irq);
- irq_set_chip_data(virq, chip);
- return 0;
- }
- static struct irq_chip smp2p_gpio_irq_chip = {
- .name = "smp2p_gpio",
- .irq_mask = smp2p_gpio_irq_mask,
- .irq_unmask = smp2p_gpio_irq_unmask,
- .irq_set_type = smp2p_gpio_irq_set_type,
- };
- /* No-map interrupt Domain */
- static const struct irq_domain_ops smp2p_irq_domain_ops = {
- .map = smp2p_irq_map,
- };
- /**
- * msm_summary_irq_handler - Handles inbound entry change notification.
- *
- * @chip: GPIO chip pointer
- * @entry: Change notification data
- *
- * Whenever an entry changes, this callback is triggered to determine
- * which bits changed and if the corresponding interrupts need to be
- * triggered.
- */
- static void msm_summary_irq_handler(struct smp2p_chip_dev *chip,
- struct msm_smp2p_update_notif *entry)
- {
- int i;
- uint32_t cur_val;
- uint32_t prev_val;
- uint32_t edge;
- unsigned long flags;
- bool trigger_interrupt;
- bool irq_rising;
- bool irq_falling;
- cur_val = entry->current_value;
- prev_val = entry->previous_value;
- if (chip->irq_base <= 0)
- return;
- SMP2P_GPIO("'%s':%d GPIO Summary IRQ Change %08x->%08x\n",
- chip->name, chip->remote_pid, prev_val, cur_val);
- for (i = 0; i < SMP2P_BITS_PER_ENTRY; ++i) {
- spin_lock_irqsave(&chip->irq_lock, flags);
- trigger_interrupt = false;
- edge = (prev_val & 0x1) << 1 | (cur_val & 0x1);
- irq_rising = test_bit(i, chip->irq_rising_edge);
- irq_falling = test_bit(i, chip->irq_falling_edge);
- if (test_bit(i, chip->irq_enabled)) {
- if (edge == 0x1 && irq_rising)
- /* 0->1 transition */
- trigger_interrupt = true;
- else if (edge == 0x2 && irq_falling)
- /* 1->0 transition */
- trigger_interrupt = true;
- } else {
- SMP2P_GPIO(
- "'%s':%d GPIO bit %d virq %d (%s,%s) - edge %s disabled\n",
- chip->name, chip->remote_pid, i,
- chip->irq_base + i,
- edge_name_rising[irq_rising],
- edge_name_falling[irq_falling],
- edge_names[edge]);
- }
- spin_unlock_irqrestore(&chip->irq_lock, flags);
- if (trigger_interrupt) {
- SMP2P_INFO(
- "'%s':%d GPIO bit %d virq %d (%s,%s) - edge %s triggering\n",
- chip->name, chip->remote_pid, i,
- chip->irq_base + i,
- edge_name_rising[irq_rising],
- edge_name_falling[irq_falling],
- edge_names[edge]);
- (void)generic_handle_irq(chip->irq_base + i);
- }
- cur_val >>= 1;
- prev_val >>= 1;
- }
- }
- /**
- * Adds an interrupt domain based upon the DT node.
- *
- * @chip: pointer to GPIO chip
- * @node: pointer to Device Tree node
- */
- static void smp2p_add_irq_domain(struct smp2p_chip_dev *chip,
- struct device_node *node)
- {
- int irq_base;
- /* map GPIO pins to interrupts */
- chip->irq_domain = irq_domain_add_linear(node, SMP2P_BITS_PER_ENTRY,
- &smp2p_irq_domain_ops, chip);
- if (!chip->irq_domain) {
- SMP2P_ERR("%s: unable to create interrupt domain '%s':%d\n",
- __func__, chip->name, chip->remote_pid);
- goto domain_fail;
- }
- /* alloc a contiguous set of virt irqs from anywhere in the irq space */
- irq_base = irq_alloc_descs_from(0, SMP2P_BITS_PER_ENTRY, of_node_to_nid(
- irq_domain_get_of_node(chip->irq_domain)));
- if (irq_base < 0) {
- SMP2P_ERR("alloc virt irqs failed:%d name:%s pid%d\n", irq_base,
- chip->name, chip->remote_pid);
- goto irq_alloc_fail;
- }
- /* map the allocated irqs to gpios */
- irq_domain_associate_many(chip->irq_domain, irq_base, 0,
- SMP2P_BITS_PER_ENTRY);
- chip->irq_base = irq_base;
- SMP2P_DBG("create mapping:%d naem:%s pid:%d\n", chip->irq_base,
- chip->name, chip->remote_pid);
- return;
- irq_alloc_fail:
- irq_domain_remove(chip->irq_domain);
- domain_fail:
- return;
- }
- /**
- * Notifier function passed into smp2p API for out bound entries.
- *
- * @self: Pointer to calling notifier block
- * @event: Event
- * @data: Event-specific data
- * @returns: 0
- */
- static int smp2p_gpio_out_notify(struct notifier_block *self,
- unsigned long event, void *data)
- {
- struct smp2p_chip_dev *chip;
- chip = container_of(self, struct smp2p_chip_dev, out_notifier);
- switch (event) {
- case SMP2P_OPEN:
- chip->is_open = 1;
- SMP2P_GPIO("%s: Opened out '%s':%d in_shadow[%d]\n", __func__,
- chip->name, chip->remote_pid, chip->in_shadow);
- if (chip->in_shadow)
- schedule_work(&chip->shadow_work);
- break;
- case SMP2P_ENTRY_UPDATE:
- break;
- default:
- SMP2P_ERR("%s: Unknown event\n", __func__);
- break;
- }
- return 0;
- }
- /**
- * Notifier function passed into smp2p API for in bound entries.
- *
- * @self: Pointer to calling notifier block
- * @event: Event
- * @data: Event-specific data
- * @returns: 0
- */
- static int smp2p_gpio_in_notify(struct notifier_block *self,
- unsigned long event, void *data)
- {
- struct smp2p_chip_dev *chip;
- chip = container_of(self, struct smp2p_chip_dev, in_notifier);
- switch (event) {
- case SMP2P_OPEN:
- chip->is_open = 1;
- SMP2P_GPIO("%s: Opened in '%s':%d\n", __func__,
- chip->name, chip->remote_pid);
- break;
- case SMP2P_ENTRY_UPDATE:
- msm_summary_irq_handler(chip, data);
- break;
- default:
- SMP2P_ERR("%s: Unknown event\n", __func__);
- break;
- }
- return 0;
- }
- /**
- * smp2p_gpio_shadow_worker - Handles shadow updates of an entry.
- *
- * @work: Work Item scheduled to handle the shadow updates.
- */
- static void smp2p_gpio_shadow_worker(struct work_struct *work)
- {
- struct smp2p_chip_dev *chip;
- int ret;
- unsigned long flags;
- chip = container_of(work, struct smp2p_chip_dev, shadow_work);
- spin_lock_irqsave(&chip->shadow_lock, flags);
- if (chip->in_shadow) {
- ret = msm_smp2p_out_modify(chip->out_handle,
- chip->shadow_value, 0x0, true);
- if (ret)
- SMP2P_GPIO("'%s':%d shadow val[0x%x] failed(%d)\n",
- chip->name, chip->remote_pid,
- chip->shadow_value, ret);
- else
- SMP2P_GPIO("'%s':%d shadow val[0x%x]\n",
- chip->name, chip->remote_pid,
- chip->shadow_value);
- chip->shadow_value = 0;
- chip->in_shadow = false;
- }
- spin_unlock_irqrestore(&chip->shadow_lock, flags);
- }
- /**
- * Device tree probe function.
- *
- * @pdev: Pointer to device tree data.
- * @returns: 0 on success; -ENODEV otherwise
- *
- * Called for each smp2pgpio entry in the device tree.
- */
- static int smp2p_gpio_probe(struct platform_device *pdev)
- {
- struct device_node *node;
- char *key;
- struct smp2p_chip_dev *chip;
- const char *name_tmp;
- unsigned long flags;
- bool is_test_entry = false;
- int ret;
- chip = kzalloc(sizeof(struct smp2p_chip_dev), GFP_KERNEL);
- if (!chip) {
- SMP2P_ERR("%s: out of memory\n", __func__);
- ret = -ENOMEM;
- goto fail;
- }
- spin_lock_init(&chip->irq_lock);
- spin_lock_init(&chip->shadow_lock);
- INIT_WORK(&chip->shadow_work, smp2p_gpio_shadow_worker);
- /* parse device tree */
- node = pdev->dev.of_node;
- key = "qcom,entry-name";
- ret = of_property_read_string(node, key, &name_tmp);
- if (ret) {
- SMP2P_ERR("%s: missing DT key '%s'\n", __func__, key);
- goto fail;
- }
- strlcpy(chip->name, name_tmp, sizeof(chip->name));
- key = "qcom,remote-pid";
- ret = of_property_read_u32(node, key, &chip->remote_pid);
- if (ret) {
- SMP2P_ERR("%s: missing DT key '%s'\n", __func__, key);
- goto fail;
- }
- key = "qcom,is-inbound";
- chip->is_inbound = of_property_read_bool(node, key);
- /* create virtual GPIO controller */
- chip->gpio.label = chip->name;
- chip->gpio.parent = &pdev->dev;
- chip->gpio.owner = THIS_MODULE;
- chip->gpio.direction_input = smp2p_direction_input,
- chip->gpio.get = smp2p_get_value;
- chip->gpio.direction_output = smp2p_direction_output,
- chip->gpio.set = smp2p_set_value;
- chip->gpio.to_irq = smp2p_gpio_to_irq,
- chip->gpio.base = -1; /* use dynamic GPIO pin allocation */
- chip->gpio.ngpio = SMP2P_BITS_PER_ENTRY;
- ret = gpiochip_add(&chip->gpio);
- if (ret) {
- SMP2P_ERR("%s: unable to register GPIO '%s' ret %d\n",
- __func__, chip->name, ret);
- goto fail;
- }
- /*
- * Test entries opened by GPIO Test conflict with loopback
- * support, so the test entries must be explicitly opened
- * in the unit test framework.
- */
- if (strcmp("smp2p", chip->name) == 0)
- is_test_entry = true;
- if (!chip->is_inbound) {
- chip->out_notifier.notifier_call = smp2p_gpio_out_notify;
- if (!is_test_entry) {
- ret = msm_smp2p_out_open(chip->remote_pid, chip->name,
- &chip->out_notifier,
- &chip->out_handle);
- if (ret < 0)
- goto error;
- }
- } else {
- chip->in_notifier.notifier_call = smp2p_gpio_in_notify;
- if (!is_test_entry) {
- ret = msm_smp2p_in_register(chip->remote_pid,
- chip->name,
- &chip->in_notifier);
- if (ret < 0)
- goto error;
- }
- }
- spin_lock_irqsave(&smp2p_entry_lock_lha1, flags);
- list_add(&chip->entry_list, &smp2p_entry_list);
- spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
- /*
- * Create interrupt domain - note that chip can't be removed from the
- * interrupt domain, so chip cannot be deleted after this point.
- */
- if (chip->is_inbound)
- smp2p_add_irq_domain(chip, node);
- else
- chip->irq_base = -1;
- SMP2P_GPIO("%s: added %s%s entry '%s':%d gpio %d irq %d",
- __func__,
- is_test_entry ? "test " : "",
- chip->is_inbound ? "in" : "out",
- chip->name, chip->remote_pid,
- chip->gpio.base, chip->irq_base);
- return 0;
- error:
- gpiochip_remove(&chip->gpio);
- fail:
- kfree(chip);
- return ret;
- }
- /**
- * smp2p_gpio_open_close - Opens or closes entry.
- *
- * @entry: Entry to open or close
- * @do_open: true = open port; false = close
- */
- static void smp2p_gpio_open_close(struct smp2p_chip_dev *entry,
- bool do_open)
- {
- int ret;
- if (do_open) {
- /* open entry */
- if (entry->is_inbound)
- ret = msm_smp2p_in_register(entry->remote_pid,
- entry->name, &entry->in_notifier);
- else
- ret = msm_smp2p_out_open(entry->remote_pid,
- entry->name, &entry->out_notifier,
- &entry->out_handle);
- SMP2P_GPIO("%s: opened %s '%s':%d ret %d\n",
- __func__,
- entry->is_inbound ? "in" : "out",
- entry->name, entry->remote_pid,
- ret);
- } else {
- /* close entry */
- if (entry->is_inbound)
- ret = msm_smp2p_in_unregister(entry->remote_pid,
- entry->name, &entry->in_notifier);
- else
- ret = msm_smp2p_out_close(&entry->out_handle);
- entry->is_open = false;
- SMP2P_GPIO("%s: closed %s '%s':%d ret %d\n",
- __func__,
- entry->is_inbound ? "in" : "out",
- entry->name, entry->remote_pid, ret);
- }
- }
- /**
- * smp2p_gpio_open_test_entry - Opens or closes test entries for unit testing.
- *
- * @name: Name of the entry
- * @remote_pid: Remote processor ID
- * @do_open: true = open port; false = close
- */
- void smp2p_gpio_open_test_entry(const char *name, int remote_pid, bool do_open)
- {
- struct smp2p_chip_dev *entry;
- struct smp2p_chip_dev *start_entry;
- unsigned long flags;
- spin_lock_irqsave(&smp2p_entry_lock_lha1, flags);
- if (list_empty(&smp2p_entry_list)) {
- spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
- return;
- }
- start_entry = list_first_entry(&smp2p_entry_list,
- struct smp2p_chip_dev,
- entry_list);
- entry = start_entry;
- do {
- if (!strcmp(entry->name, name)
- && entry->remote_pid == remote_pid) {
- /* found entry to change */
- spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
- smp2p_gpio_open_close(entry, do_open);
- spin_lock_irqsave(&smp2p_entry_lock_lha1, flags);
- }
- list_rotate_left(&smp2p_entry_list);
- entry = list_first_entry(&smp2p_entry_list,
- struct smp2p_chip_dev,
- entry_list);
- } while (entry != start_entry);
- spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
- }
- static const struct of_device_id msm_smp2p_match_table[] = {
- {.compatible = "qcom,smp2pgpio", },
- {},
- };
- static struct platform_driver smp2p_gpio_driver = {
- .probe = smp2p_gpio_probe,
- .driver = {
- .name = "smp2pgpio",
- .owner = THIS_MODULE,
- .of_match_table = msm_smp2p_match_table,
- },
- };
- static int smp2p_init(void)
- {
- INIT_LIST_HEAD(&smp2p_entry_list);
- return platform_driver_register(&smp2p_gpio_driver);
- }
- module_init(smp2p_init);
- static void __exit smp2p_exit(void)
- {
- platform_driver_unregister(&smp2p_gpio_driver);
- }
- module_exit(smp2p_exit);
- MODULE_DESCRIPTION("SMP2P GPIO");
- MODULE_LICENSE("GPL v2");
|