forked from mirrors/linux
		
	gpiolib: Support 'gpio-reserved-ranges' property
Some qcom platforms make some GPIOs or pins unavailable for use by non-secure operating systems, and thus reading or writing the registers for those pins will cause access control issues. Add support for a DT property to describe the set of GPIOs that are available for use so that higher level OSes are able to know what pins to avoid reading/writing. Non-DT platforms can add support by directly updating the chip->valid_mask. Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Stephen Boyd <swboyd@chromium.org> Tested-by: Timur Tabi <timur@codeaurora.org> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
		
							parent
							
								
									ace56935ff
								
							
						
					
					
						commit
						726cb3ba49
					
				
					 3 changed files with 86 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -511,6 +511,28 @@ void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL(of_mm_gpiochip_remove);
 | 
			
		||||
 | 
			
		||||
static void of_gpiochip_init_valid_mask(struct gpio_chip *chip)
 | 
			
		||||
{
 | 
			
		||||
	int len, i;
 | 
			
		||||
	u32 start, count;
 | 
			
		||||
	struct device_node *np = chip->of_node;
 | 
			
		||||
 | 
			
		||||
	len = of_property_count_u32_elems(np,  "gpio-reserved-ranges");
 | 
			
		||||
	if (len < 0 || len % 2 != 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < len; i += 2) {
 | 
			
		||||
		of_property_read_u32_index(np, "gpio-reserved-ranges",
 | 
			
		||||
					   i, &start);
 | 
			
		||||
		of_property_read_u32_index(np, "gpio-reserved-ranges",
 | 
			
		||||
					   i + 1, &count);
 | 
			
		||||
		if (start >= chip->ngpio || start + count >= chip->ngpio)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		bitmap_clear(chip->valid_mask, start, count);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PINCTRL
 | 
			
		||||
static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -615,6 +637,8 @@ int of_gpiochip_add(struct gpio_chip *chip)
 | 
			
		|||
	if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	of_gpiochip_init_valid_mask(chip);
 | 
			
		||||
 | 
			
		||||
	status = of_gpiochip_add_pin_range(chip);
 | 
			
		||||
	if (status)
 | 
			
		||||
		return status;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -351,6 +351,43 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *chip)
 | 
			
		|||
	return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_OF_GPIO
 | 
			
		||||
	int size;
 | 
			
		||||
	struct device_node *np = gpiochip->of_node;
 | 
			
		||||
 | 
			
		||||
	size = of_property_count_u32_elems(np,  "gpio-reserved-ranges");
 | 
			
		||||
	if (size > 0 && size % 2 == 0)
 | 
			
		||||
		gpiochip->need_valid_mask = true;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (!gpiochip->need_valid_mask)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	gpiochip->valid_mask = gpiochip_allocate_mask(gpiochip);
 | 
			
		||||
	if (!gpiochip->valid_mask)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gpiochip_free_valid_mask(struct gpio_chip *gpiochip)
 | 
			
		||||
{
 | 
			
		||||
	kfree(gpiochip->valid_mask);
 | 
			
		||||
	gpiochip->valid_mask = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool gpiochip_line_is_valid(const struct gpio_chip *gpiochip,
 | 
			
		||||
				unsigned int offset)
 | 
			
		||||
{
 | 
			
		||||
	/* No mask means all valid */
 | 
			
		||||
	if (likely(!gpiochip->valid_mask))
 | 
			
		||||
		return true;
 | 
			
		||||
	return test_bit(offset, gpiochip->valid_mask);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * GPIO line handle management
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -1275,6 +1312,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
 | 
			
		|||
	if (status)
 | 
			
		||||
		goto err_remove_from_list;
 | 
			
		||||
 | 
			
		||||
	status = gpiochip_init_valid_mask(chip);
 | 
			
		||||
	if (status)
 | 
			
		||||
		goto err_remove_irqchip_mask;
 | 
			
		||||
 | 
			
		||||
	status = gpiochip_add_irqchip(chip, lock_key, request_key);
 | 
			
		||||
	if (status)
 | 
			
		||||
		goto err_remove_chip;
 | 
			
		||||
| 
						 | 
				
			
			@ -1304,6 +1345,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
 | 
			
		|||
	acpi_gpiochip_remove(chip);
 | 
			
		||||
	gpiochip_free_hogs(chip);
 | 
			
		||||
	of_gpiochip_remove(chip);
 | 
			
		||||
	gpiochip_free_valid_mask(chip);
 | 
			
		||||
err_remove_irqchip_mask:
 | 
			
		||||
	gpiochip_irqchip_free_valid_mask(chip);
 | 
			
		||||
err_remove_from_list:
 | 
			
		||||
	spin_lock_irqsave(&gpio_lock, flags);
 | 
			
		||||
| 
						 | 
				
			
			@ -1360,6 +1403,7 @@ void gpiochip_remove(struct gpio_chip *chip)
 | 
			
		|||
	acpi_gpiochip_remove(chip);
 | 
			
		||||
	gpiochip_remove_pin_ranges(chip);
 | 
			
		||||
	of_gpiochip_remove(chip);
 | 
			
		||||
	gpiochip_free_valid_mask(chip);
 | 
			
		||||
	/*
 | 
			
		||||
	 * We accept no more calls into the driver from this point, so
 | 
			
		||||
	 * NULL the driver data pointer
 | 
			
		||||
| 
						 | 
				
			
			@ -1536,6 +1580,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
 | 
			
		|||
bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
 | 
			
		||||
				unsigned int offset)
 | 
			
		||||
{
 | 
			
		||||
	if (!gpiochip_line_is_valid(gpiochip, offset))
 | 
			
		||||
		return false;
 | 
			
		||||
	/* No mask means all valid */
 | 
			
		||||
	if (likely(!gpiochip->irq.valid_mask))
 | 
			
		||||
		return true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -288,6 +288,21 @@ struct gpio_chip {
 | 
			
		|||
	struct gpio_irq_chip irq;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @need_valid_mask:
 | 
			
		||||
	 *
 | 
			
		||||
	 * If set core allocates @valid_mask with all bits set to one.
 | 
			
		||||
	 */
 | 
			
		||||
	bool need_valid_mask;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @valid_mask:
 | 
			
		||||
	 *
 | 
			
		||||
	 * If not %NULL holds bitmask of GPIOs which are valid to be used
 | 
			
		||||
	 * from the chip.
 | 
			
		||||
	 */
 | 
			
		||||
	unsigned long *valid_mask;
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_OF_GPIO)
 | 
			
		||||
	/*
 | 
			
		||||
	 * If CONFIG_OF is enabled, then all GPIO controllers described in the
 | 
			
		||||
| 
						 | 
				
			
			@ -384,6 +399,7 @@ bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset);
 | 
			
		|||
 | 
			
		||||
/* Sleep persistence inquiry for drivers */
 | 
			
		||||
bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset);
 | 
			
		||||
bool gpiochip_line_is_valid(const struct gpio_chip *chip, unsigned int offset);
 | 
			
		||||
 | 
			
		||||
/* get driver data */
 | 
			
		||||
void *gpiochip_get_data(struct gpio_chip *chip);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue