Re: [PATCH] ACPI: EC: add support for hardware-reduced systems

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

 



Sorry for the delay.

On Wednesday, September 4, 2019 8:34:43 AM CEST Daniel Drake wrote:
> As defined in the ACPI spec section 12.11, ACPI hardware-reduced
> platforms define the EC SCI interrupt as a GpioInt in the _CRS object.
> 
> This replaces the previous way of using a GPE for this interrupt;
> GPE blocks are not available on reduced hardware platforms.
> 
> Add support for handling this interrupt as an EC event source, and
> avoid GPE usage on reduced hardware platforms.
> 
> This enables the use of several media keys (e.g. screen brightness
> up/down) on Asus UX434DA.
> 
> Signed-off-by: Daniel Drake <drake@xxxxxxxxxxxx>

Just as a matter of clarity, I would like the changes to be split into the
rename of EC_FLAGS_GPE_MASKED (without functional impact) and the rest.

> ---
>  drivers/acpi/ec.c       | 81 +++++++++++++++++++++++++++++++----------
>  drivers/acpi/internal.h |  1 +
>  2 files changed, 63 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
> index c33756ed3304..37bbec89be82 100644
> --- a/drivers/acpi/ec.c
> +++ b/drivers/acpi/ec.c
> @@ -99,7 +99,7 @@ enum {
>  	EC_FLAGS_EVT_HANDLER_INSTALLED, /* _Qxx handlers installed */
>  	EC_FLAGS_STARTED,		/* Driver is started */
>  	EC_FLAGS_STOPPED,		/* Driver is stopped */
> -	EC_FLAGS_GPE_MASKED,		/* GPE masked */
> +	EC_FLAGS_EVENTS_MASKED,		/* GPE or IRQ masked */
>  };
>  
>  #define ACPI_EC_COMMAND_POLL		0x01 /* Available for command byte */
> @@ -414,20 +414,26 @@ static void acpi_ec_complete_request(struct acpi_ec *ec)
>  		wake_up(&ec->wait);
>  }
>  
> -static void acpi_ec_mask_gpe(struct acpi_ec *ec)
> +static void acpi_ec_mask_events(struct acpi_ec *ec)
>  {
> -	if (!test_bit(EC_FLAGS_GPE_MASKED, &ec->flags)) {
> -		acpi_ec_disable_gpe(ec, false);
> +	if (!test_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags)) {
> +		if (ec->irq >= 0)
> +			disable_irq_nosync(ec->irq);
> +		else
> +			acpi_ec_disable_gpe(ec, false);

So IMO it would be better to change the gpe data type to int and initialize it
to -1.

Then, you can do

if (ec->gpe >= 0)
        acpi_ec_disable_gpe(ec, false);
else
        disable_irq_nosync(ec->irq);

and also check gpe in some other places like acpi_ec_suspend/resume_noirq().

And fail the initialization when you cannot get neither the GPE number nor
the interrupt.

>  		ec_dbg_drv("Polling enabled");
> -		set_bit(EC_FLAGS_GPE_MASKED, &ec->flags);
> +		set_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags);
>  	}
>  }
>  
> -static void acpi_ec_unmask_gpe(struct acpi_ec *ec)
> +static void acpi_ec_unmask_events(struct acpi_ec *ec)
>  {
> -	if (test_bit(EC_FLAGS_GPE_MASKED, &ec->flags)) {
> -		clear_bit(EC_FLAGS_GPE_MASKED, &ec->flags);
> -		acpi_ec_enable_gpe(ec, false);
> +	if (test_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags)) {
> +		clear_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags);
> +		if (ec->irq >= 0)
> +			enable_irq(ec->irq);
> +		else
> +			acpi_ec_enable_gpe(ec, false);
>  		ec_dbg_drv("Polling disabled");
>  	}
>  }
> @@ -453,7 +459,7 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)
>  
>  static void acpi_ec_submit_query(struct acpi_ec *ec)
>  {
> -	acpi_ec_mask_gpe(ec);
> +	acpi_ec_mask_events(ec);
>  	if (!acpi_ec_event_enabled(ec))
>  		return;
>  	if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
> @@ -469,7 +475,7 @@ static void acpi_ec_complete_query(struct acpi_ec *ec)
>  	if (test_and_clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
>  		ec_dbg_evt("Command(%s) unblocked",
>  			   acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
> -	acpi_ec_unmask_gpe(ec);
> +	acpi_ec_unmask_events(ec);
>  }
>  
>  static inline void __acpi_ec_enable_event(struct acpi_ec *ec)
> @@ -647,7 +653,8 @@ static void advance_transaction(struct acpi_ec *ec)
>  	 * ensure a hardware STS 0->1 change after this clearing can always
>  	 * trigger a GPE interrupt.
>  	 */
> -	acpi_ec_clear_gpe(ec);
> +	if (ec->irq < 0)
> +		acpi_ec_clear_gpe(ec);
>  	status = acpi_ec_read_status(ec);
>  	t = ec->curr;
>  	/*
> @@ -716,7 +723,7 @@ static void advance_transaction(struct acpi_ec *ec)
>  				++t->irq_count;
>  			/* Allow triggering on 0 threshold */
>  			if (t->irq_count == ec_storm_threshold)
> -				acpi_ec_mask_gpe(ec);
> +				acpi_ec_mask_events(ec);
>  		}
>  	}
>  out:
> @@ -814,7 +821,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
>  
>  	spin_lock_irqsave(&ec->lock, tmp);
>  	if (t->irq_count == ec_storm_threshold)
> -		acpi_ec_unmask_gpe(ec);
> +		acpi_ec_unmask_events(ec);
>  	ec_dbg_req("Command(%s) stopped", acpi_ec_cmd_string(t->command));
>  	ec->curr = NULL;
>  	/* Disable GPE for command processing (IBF=0/OBF=1) */
> @@ -1292,18 +1299,32 @@ static void acpi_ec_event_handler(struct work_struct *work)
>  	acpi_ec_check_event(ec);
>  }
>  
> -static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
> -	u32 gpe_number, void *data)
> +static void acpi_ec_handle_interrupt(struct acpi_ec *ec)
>  {
>  	unsigned long flags;
> -	struct acpi_ec *ec = data;
>  
>  	spin_lock_irqsave(&ec->lock, flags);
>  	advance_transaction(ec);
>  	spin_unlock_irqrestore(&ec->lock, flags);
> +}
> +
> +static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
> +	u32 gpe_number, void *data)
> +{
> +	struct acpi_ec *ec = data;
> +
> +	acpi_ec_handle_interrupt(ec);

You can pass data here without using the extra local var.

>  	return ACPI_INTERRUPT_HANDLED;
>  }
>  
> +static irqreturn_t acpi_ec_irq_handler(int irq, void *data)
> +{
> +	struct acpi_ec *ec = data;
> +
> +	acpi_ec_handle_interrupt(ec);
> +	return IRQ_HANDLED;
> +}
> +
>  /* --------------------------------------------------------------------------
>   *                           Address Space Management
>   * -------------------------------------------------------------------------- */
> @@ -1376,6 +1397,7 @@ static struct acpi_ec *acpi_ec_alloc(void)
>  	ec->timestamp = jiffies;
>  	ec->busy_polling = true;
>  	ec->polling_guard = 0;
> +	ec->irq = -1;
>  	return ec;
>  }
>  
> @@ -1419,7 +1441,7 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
>  		 * EC.
>  		 */
>  		ec->gpe = boot_ec->gpe;
> -	} else {
> +	} else if (!acpi_gbl_reduced_hardware) {

I'm not sure if this check is necessary.

if _GPE is not present, you can assume hardware-reduced and look for the GPIO
IRQ.

>  		/* Get GPE bit assignment (EC events). */
>  		/* TODO: Add support for _GPE returning a package */
>  		status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
> @@ -1480,7 +1502,8 @@ static int ec_install_handlers(struct acpi_ec *ec, bool handle_events)
>  				    NULL, ec, NULL);
>  		set_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags);

I would rename this to EC_FLAGS_QUERY_METHODS_INSTALLED ->

>  	}
> -	if (!test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) {

and this to EC_FLAGS_EVTENT_HANDLER_INSTALLED ->>

> +	if (!acpi_gbl_reduced_hardware &&
> +	    !test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) {

and I would use it to check if there is an "event" (either GPE or IRQ) handler
registered already.

>  		status = acpi_install_gpe_raw_handler(NULL, ec->gpe,
>  					  ACPI_GPE_EDGE_TRIGGERED,
>  					  &acpi_ec_gpe_handler, ec);

And there's some code below which I believe you kind of need for the IRQ case
too.

> @@ -1493,6 +1516,12 @@ static int ec_install_handlers(struct acpi_ec *ec, bool handle_events)
>  				acpi_ec_enable_gpe(ec, true);
>  		}
>  	}
> +	if (ec->irq >= 0) {
> +		int ret = request_irq(ec->irq, acpi_ec_irq_handler,
> +				      IRQF_SHARED, "ACPI EC", ec);
> +		if (ret)
> +			return ret;
> +	}

I would move this to the "_HANDLER_INSTALLED" block above.

>  	/* EC is fully operational, allow queries */
>  	acpi_ec_enable_event(ec);
>  
> @@ -1521,6 +1550,8 @@ static void ec_remove_handlers(struct acpi_ec *ec)
>  	 */
>  	acpi_ec_stop(ec, false);
>  
> +	if (ec->irq >= 0)
> +		free_irq(ec->irq, ec);
>  	if (test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) {
>  		if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe,
>  					&acpi_ec_gpe_handler)))
> @@ -1611,6 +1642,18 @@ static int acpi_ec_add(struct acpi_device *device)
>  			acpi_ec_free(ec);
>  			ec = boot_ec;
>  		}
> +
> +		/*
> +		 * Reduced hartware platforms use a GpioInt specified in
> +		 * _CRS.
> +		 */
> +		if (acpi_gbl_reduced_hardware) {
> +			ret = acpi_dev_gpio_irq_get(device, 0);
> +			if (ret == -EPROBE_DEFER)
> +				goto err_alloc;
> +
> +			ec->irq = ret;
> +		}

And this should go to where you request the IRQ.  There's no reason for it
to be here AFAICS.

>  	}
>  
>  	ret = acpi_ec_setup(ec, true);
> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index f4c2fe6be4f2..34c2c85e667e 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -166,6 +166,7 @@ static inline void acpi_early_processor_osc(void) {}
>  struct acpi_ec {
>  	acpi_handle handle;
>  	u32 gpe;
> +	int irq;
>  	unsigned long command_addr;
>  	unsigned long data_addr;
>  	bool global_lock;
> 







[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