RE: [PATCH 2/2] gpio / ACPI: add support for GPIO operation regions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> From: linux-acpi-owner@xxxxxxxxxxxxxxx [mailto:linux-acpi-owner@xxxxxxxxxxxxxxx] On Behalf Of Mika Westerberg
> Sent: Friday, September 13, 2013 11:15 PM
> 
> GPIO operation regions is a new feature introduced in ACPI 5.0
> specification. In practise it means that now ASL code can toggle GPIOs with
> the help of the OS GPIO driver.
> 
> An imaginary example of such ASL code:
> 
> 	Device (\SB.GPIO)
> 	{
> 		// _REG is called when the operation region handler is
> 		// installed.
> 		Method (_REG)
> 		{
> 			...
> 		}
> 
> 		OperationRegion (GPOP, GeneralPurposeIo, 0, 0xc)
> 		Field (GPOP, ByteAcc, NoLock, Preserve)
> 		{
> 			Connection
> 			(
> 	                    GpioIo (Exclusive, PullDefault, 0x0000, 0x0000,
> 				    IoRestrictionOutputOnly, "\\_SB.GPIO",
> 				    0x00, ResourceConsumer,,)
> 				{
> 				    0x0005
> 				}
> 			),
> 			PWR0, 1
> 		}
> 
> 		...
> 	}
> 
> 	Device (\SB.DEV0)
> 	{
> 		...
> 
> 		Method (_PS0, 0, Serialized)
> 		{
> 			// Toggle the GPIO
> 			Store (1, \SB.GPIO.PWR0)
> 		}
> 
> 		Method (_PS3, 0, Serialized)
> 		{
> 			Store (0, \SB.GPIO.PRW0)
> 		}
> 	}
> 
> The device \SB.DEV0 is powered on by asserting \SB.GPIO.PWR0 GPIO line. So
> when the OS calls _PS0 or _PS3, that GPIO line should be set to 1 or 0 by
> the actual GPIO driver.
> 
> In order to support GPIO operation regions we install ACPI address space
> handler for the device (provided that it has an ACPI handle). This handler
> uses the standard GPIO APIs to toggle the pin according to what the ASL
> code does.
> 
> Since there is need to attach more data to the ACPI object, we create a new
> structure acpi_gpio_chip_data that contains data for both GPIO operation
> regions and for ACPI event handling and make the event handling code to use
> this new structure.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx>
> ---
>  drivers/gpio/gpiolib-acpi.c | 131 ++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 126 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
> index e12a08e..f52536a 100644
> --- a/drivers/gpio/gpiolib-acpi.c
> +++ b/drivers/gpio/gpiolib-acpi.c
> @@ -24,6 +24,17 @@ struct acpi_gpio_evt_pin {
>  	unsigned int irq;
>  };
> 
> +struct acpi_gpio_chip_data {
> +	/*
> +	 * acpi_install_address_space_handler() wants to put the connection
> +	 * information at the start of the context structure we pass it, in
> +	 * case we are dealing with GPIO operation regions.
> +	 */
> +	struct acpi_connection_info ci;
> +	struct gpio_chip *chip;
> +	struct list_head *evt_pins;
> +};
> +
>  static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
>  {
>  	if (!gc->dev)
> @@ -86,7 +97,7 @@ static irqreturn_t acpi_gpio_irq_handler_evt(int irq, void *data)
>  	return IRQ_HANDLED;
>  }
> 
> -static void acpi_gpio_evt_dh(acpi_handle handle, void *data)
> +static void acpi_gpio_chip_dh(acpi_handle handle, void *data)
>  {
>  	/* The address of this function is used as a key. */
>  }
> @@ -127,12 +138,16 @@ static void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
>  	if (ACPI_SUCCESS(status)) {
>  		evt_pins = kzalloc(sizeof(*evt_pins), GFP_KERNEL);
>  		if (evt_pins) {
> +			struct acpi_gpio_chip_data *data;
> +
>  			INIT_LIST_HEAD(evt_pins);
> -			status = acpi_attach_data(handle, acpi_gpio_evt_dh,
> -						  evt_pins);
> +			status = acpi_get_data(handle, acpi_gpio_chip_dh,
> +					       (void **)&data);
>  			if (ACPI_FAILURE(status)) {
>  				kfree(evt_pins);
>  				evt_pins = NULL;
> +			} else {
> +				data->evt_pins = evt_pins;
>  			}
>  		}
>  	}
> @@ -293,6 +308,7 @@ static void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
>  	acpi_status status;
>  	struct list_head *evt_pins;
>  	struct acpi_gpio_evt_pin *evt_pin, *ep;
> +	struct acpi_gpio_chip_data *data;
> 
>  	if (!chip->dev || !chip->to_irq)
>  		return;
> @@ -301,28 +317,133 @@ static void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
>  	if (!handle)
>  		return;
> 
> -	status = acpi_get_data(handle, acpi_gpio_evt_dh, (void **)&evt_pins);
> +	status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&data);
>  	if (ACPI_FAILURE(status))
>  		return;
> 
> +	evt_pins = data->evt_pins;
> +	data->evt_pins = NULL;
> +
>  	list_for_each_entry_safe_reverse(evt_pin, ep, evt_pins, node) {
>  		devm_free_irq(chip->dev, evt_pin->irq, evt_pin);
>  		list_del(&evt_pin->node);
>  		kfree(evt_pin);
>  	}
> 
> -	acpi_detach_data(handle, acpi_gpio_evt_dh);
>  	kfree(evt_pins);
>  }
> 
> +static acpi_status
> +acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
> +			    u32 bits, u64 *value, void *handler_context,
> +			    void *reqion_context)
> +{
> +	struct acpi_gpio_chip_data *data = handler_context;
> +	struct gpio_chip *chip = data->chip;
> +	struct acpi_resource *ares;
> +	int ret, gpio = -EINVAL;
> +	unsigned long flags = 0;
> +	acpi_status status;
> +
> +	status = acpi_buffer_to_resource(data->ci.connection, data->ci.length,
> +					 &ares);
> +	if (ACPI_FAILURE(status))
> +		return status;
> +
> +	if (ares->type == ACPI_RESOURCE_TYPE_GPIO) {
> +		const struct acpi_resource_gpio *agpio = &ares->data.gpio;
> +
> +		switch (agpio->io_restriction) {
> +		case ACPI_IO_RESTRICT_NONE:
> +			flags = GPIOF_DIR_IN | GPIOF_DIR_OUT;
> +			break;
> +		case ACPI_IO_RESTRICT_INPUT:
> +			flags = GPIOF_DIR_IN;
> +			break;
> +		case ACPI_IO_RESTRICT_OUTPUT:
> +			flags = GPIOF_DIR_OUT;
> +			break;
> +		}
> +
> +		gpio = acpi_get_gpio(agpio->resource_source.string_ptr,
> +				     agpio->pin_table[0]);
> +	}
> +
> +	ACPI_FREE(ares);
> +
> +	if (!gpio_is_valid(gpio)) {
> +		dev_err(chip->dev, "GpioOpReg: invalid GPIO resource\n");
> +		return AE_ERROR;
> +	}
> +
> +	ret = gpio_request_one(gpio, flags, "acpi_gpio_adr_space");
> +	if (ret) {
> +		dev_err(chip->dev, "GpioOpReg: failed to request GPIO\n");
> +		return AE_ERROR;
> +	}
> +
> +	if (function == ACPI_WRITE)
> +		gpio_set_value(gpio, (int)*value);
> +	else
> +		*value = gpio_get_value(gpio);
> +
> +	gpio_free(gpio);
> +	return AE_OK;
> +}
> +
>  void acpi_gpiochip_add(struct gpio_chip *chip)
>  {
> +	struct acpi_gpio_chip_data *data;
> +	acpi_handle handle;
> +	acpi_status status;
> +
> +	handle = ACPI_HANDLE(chip->dev);
> +	if (!handle)
> +		return;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return;
> +
> +	status = acpi_attach_data(handle, acpi_gpio_chip_dh, data);
> +	if (ACPI_FAILURE(status)) {
> +		kfree(data);
> +		return;
> +	}
> +
> +	data->chip = chip;
> +
> +	status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
> +						    acpi_gpio_adr_space_handler,
> +						    NULL, data);

Is it possible to install the handler for ACPI_ROOT_OBJECT?
Can it be achieved by implementing a setup callback?
Maybe you can also eliminate acpi_attach_data usages by doing so.

> +	if (ACPI_FAILURE(status)) {
> +		acpi_detach_data(handle, acpi_gpio_chip_dh);
> +		kfree(data);
> +		return;
> +	}
> +
>  	acpi_gpiochip_request_interrupts(chip);
>  }
>  EXPORT_SYMBOL_GPL(acpi_gpiochip_add);
> 
>  void acpi_gpiochip_remove(struct gpio_chip *chip)
>  {
> +	struct acpi_gpio_chip_data *data;
> +	acpi_handle handle;
> +	acpi_status status;
> +
> +	handle = ACPI_HANDLE(chip->dev);
> +	if (!handle)
> +		return;
> +
> +	status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&data);
> +	if (ACPI_FAILURE(status))
> +		return;
> +
>  	acpi_gpiochip_free_interrupts(chip);
> +	acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
> +					  acpi_gpio_adr_space_handler);
> +	acpi_detach_data(handle, acpi_gpio_chip_dh);
> +	kfree(data);
>  }
>  EXPORT_SYMBOL_GPL(acpi_gpiochip_remove);
> --
> 1.8.4.rc3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux