diff options
Diffstat (limited to 'drivers/gpio/gpiolib.c')
| -rw-r--r-- | drivers/gpio/gpiolib.c | 107 | 
1 files changed, 85 insertions, 22 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 868128a676ba..f4c26c7826cd 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -986,7 +986,8 @@ static int gpio_chrdev_open(struct inode *inode, struct file *filp)  		return -ENODEV;  	get_device(&gdev->dev);  	filp->private_data = gdev; -	return 0; + +	return nonseekable_open(inode, filp);  }  /** @@ -1011,7 +1012,7 @@ static const struct file_operations gpio_fileops = {  	.release = gpio_chrdev_release,  	.open = gpio_chrdev_open,  	.owner = THIS_MODULE, -	.llseek = noop_llseek, +	.llseek = no_llseek,  	.unlocked_ioctl = gpio_ioctl,  #ifdef CONFIG_COMPAT  	.compat_ioctl = gpio_ioctl_compat, @@ -1512,7 +1513,7 @@ static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,  }  /** - * gpiochip_set_chained_irqchip() - sets a chained irqchip to a gpiochip + * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip   * @gpiochip: the gpiochip to set the irqchip chain to   * @irqchip: the irqchip to chain to the gpiochip   * @parent_irq: the irq number corresponding to the parent IRQ for this @@ -1521,10 +1522,10 @@ static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,   * coming out of the gpiochip. If the interrupt is nested rather than   * cascaded, pass NULL in this handler argument   */ -void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, -				  struct irq_chip *irqchip, -				  int parent_irq, -				  irq_flow_handler_t parent_handler) +static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip, +					  struct irq_chip *irqchip, +					  int parent_irq, +					  irq_flow_handler_t parent_handler)  {  	unsigned int offset; @@ -1548,7 +1549,7 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,  		irq_set_chained_handler_and_data(parent_irq, parent_handler,  						 gpiochip); -		gpiochip->irq_parent = parent_irq; +		gpiochip->irq_chained_parent = parent_irq;  	}  	/* Set the parent IRQ for all affected IRQs */ @@ -1559,9 +1560,48 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,  			       parent_irq);  	}  } + +/** + * gpiochip_set_chained_irqchip() - connects a chained irqchip to a gpiochip + * @gpiochip: the gpiochip to set the irqchip chain to + * @irqchip: the irqchip to chain to the gpiochip + * @parent_irq: the irq number corresponding to the parent IRQ for this + * chained irqchip + * @parent_handler: the parent interrupt handler for the accumulated IRQ + * coming out of the gpiochip. If the interrupt is nested rather than + * cascaded, pass NULL in this handler argument + */ +void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, +				  struct irq_chip *irqchip, +				  int parent_irq, +				  irq_flow_handler_t parent_handler) +{ +	gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq, +				      parent_handler); +}  EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);  /** + * gpiochip_set_nested_irqchip() - connects a nested irqchip to a gpiochip + * @gpiochip: the gpiochip to set the irqchip nested handler to + * @irqchip: the irqchip to nest to the gpiochip + * @parent_irq: the irq number corresponding to the parent IRQ for this + * nested irqchip + */ +void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip, +				 struct irq_chip *irqchip, +				 int parent_irq) +{ +	if (!gpiochip->irq_nested) { +		chip_err(gpiochip, "tried to nest a chained gpiochip\n"); +		return; +	} +	gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq, +				      NULL); +} +EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); + +/**   * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip   * @d: the irqdomain used by this irqchip   * @irq: the global irq number used by this GPIO irqchip irq @@ -1583,8 +1623,8 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,  	 */  	irq_set_lockdep_class(irq, chip->lock_key);  	irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler); -	/* Chips that can sleep need nested thread handlers */ -	if (chip->can_sleep && !chip->irq_not_threaded) +	/* Chips that use nested thread handlers have them marked */ +	if (chip->irq_nested)  		irq_set_nested_thread(irq, 1);  	irq_set_noprobe(irq); @@ -1602,7 +1642,7 @@ static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)  {  	struct gpio_chip *chip = d->host_data; -	if (chip->can_sleep) +	if (chip->irq_nested)  		irq_set_nested_thread(irq, 0);  	irq_set_chip_and_handler(irq, NULL, NULL);  	irq_set_chip_data(irq, NULL); @@ -1657,9 +1697,9 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)  	acpi_gpiochip_free_interrupts(gpiochip); -	if (gpiochip->irq_parent) { -		irq_set_chained_handler(gpiochip->irq_parent, NULL); -		irq_set_handler_data(gpiochip->irq_parent, NULL); +	if (gpiochip->irq_chained_parent) { +		irq_set_chained_handler(gpiochip->irq_chained_parent, NULL); +		irq_set_handler_data(gpiochip->irq_chained_parent, NULL);  	}  	/* Remove all IRQ mappings and delete the domain */ @@ -1683,7 +1723,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)  }  /** - * gpiochip_irqchip_add() - adds an irqchip to a gpiochip + * _gpiochip_irqchip_add() - adds an irqchip to a gpiochip   * @gpiochip: the gpiochip to add the irqchip to   * @irqchip: the irqchip to add to the gpiochip   * @first_irq: if not dynamically assigned, the base (first) IRQ to @@ -1691,6 +1731,8 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)   * @handler: the irq handler to use (often a predefined irq core function)   * @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE   * to have the core avoid setting up any default type in the hardware. + * @nested: whether this is a nested irqchip calling handle_nested_irq() + * in its IRQ handler   * @lock_key: lockdep class   *   * This function closely associates a certain irqchip with a certain @@ -1712,6 +1754,7 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,  			  unsigned int first_irq,  			  irq_flow_handler_t handler,  			  unsigned int type, +			  bool nested,  			  struct lock_class_key *lock_key)  {  	struct device_node *of_node; @@ -1726,6 +1769,7 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,  		pr_err("missing gpiochip .dev parent pointer\n");  		return -EINVAL;  	} +	gpiochip->irq_nested = nested;  	of_node = gpiochip->parent->of_node;  #ifdef CONFIG_OF_GPIO  	/* @@ -2223,6 +2267,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_input);  static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)  {  	struct gpio_chip *gc = desc->gdev->chip; +	int val = !!value;  	int ret;  	/* GPIOs used for IRQs shall not be set as output */ @@ -2242,7 +2287,7 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)  				goto set_output_value;  		}  		/* Emulate open drain by not actively driving the line high */ -		if (value) +		if (val)  			return gpiod_direction_input(desc);  	}  	else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { @@ -2253,7 +2298,7 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)  				goto set_output_value;  		}  		/* Emulate open source by not actively driving the line low */ -		if (!value) +		if (!val)  			return gpiod_direction_input(desc);  	} else {  		/* Make sure to disable open drain/source hardware, if any */ @@ -2271,10 +2316,10 @@ set_output_value:  		return -EIO;  	} -	ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), value); +	ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);  	if (!ret)  		set_bit(FLAG_IS_OUT, &desc->flags); -	trace_gpio_value(desc_to_gpio(desc), 0, value); +	trace_gpio_value(desc_to_gpio(desc), 0, val);  	trace_gpio_direction(desc_to_gpio(desc), 0, ret);  	return ret;  } @@ -2314,6 +2359,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)  	VALIDATE_DESC(desc);  	if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))  		value = !value; +	else +		value = !!value;  	return _gpiod_direction_output_raw(desc, value);  }  EXPORT_SYMBOL_GPL(gpiod_direction_output); @@ -2758,6 +2805,15 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)  	}  	set_bit(FLAG_USED_AS_IRQ, &desc->flags); + +	/* +	 * If the consumer has not set up a label (such as when the +	 * IRQ is referenced from .to_irq()) we set up a label here +	 * so it is clear this is used as an interrupt. +	 */ +	if (!desc->label) +		desc_set_label(desc, "interrupt"); +  	return 0;  }  EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq); @@ -2772,10 +2828,17 @@ EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);   */  void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)  { -	if (offset >= chip->ngpio) +	struct gpio_desc *desc; + +	desc = gpiochip_get_desc(chip, offset); +	if (IS_ERR(desc))  		return; -	clear_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags); +	clear_bit(FLAG_USED_AS_IRQ, &desc->flags); + +	/* If we only had this marking, erase it */ +	if (desc->label && !strcmp(desc->label, "interrupt")) +		desc_set_label(desc, NULL);  }  EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq); @@ -3170,7 +3233,7 @@ static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,  	/* Process flags */  	if (dflags & GPIOD_FLAGS_BIT_DIR_OUT)  		status = gpiod_direction_output(desc, -					      dflags & GPIOD_FLAGS_BIT_DIR_VAL); +				!!(dflags & GPIOD_FLAGS_BIT_DIR_VAL));  	else  		status = gpiod_direction_input(desc);  |