Re: [PATCH v2] gpiolib-acpi: make sure we trigger edge events at least once on boot

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

 



Hi,

On 12-07-18 18:23, Andy Shevchenko wrote:
On Thu, 2018-07-12 at 17:25 +0200, Hans de Goede wrote:
From: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx>

On some systems using edge triggered ACPI Event Interrupts, the
initial
state at boot is not setup by the firmware, instead relying on the
edge
irq event handler running at least once to setup the initial state.

2 known examples of this are:

1) The Surface 3 has its _LID state controlled by an ACPI operation
region
  triggered by a GPIO event:

  OperationRegion (GPOR, GeneralPurposeIo, Zero, One)
  Field (GPOR, ByteAcc, NoLock, Preserve)
  {
      Connection (
          GpioIo (Shared, PullNone, 0x0000, 0x0000, IoRestrictionNone,
              "\\_SB.GPO0", 0x00, ResourceConsumer, ,
              )
              {   // Pin list
                  0x004C
              }
      ),
      HELD,   1
  }

  Method (_E4C, 0, Serialized)  // _Exx: Edge-Triggered GPE
  {
      If ((HELD == One))
      {
          ^^LID.LIDB = One
      }
      Else
      {
          ^^LID.LIDB = Zero
          Notify (LID, 0x80) // Status Change
      }

      Notify (^^PCI0.SPI1.NTRG, One) // Device Check
  }

  Currently, the state of LIDB is wrong until the user actually closes
or
  open the cover. We need to trigger the GPIO event once to update the
  internal ACPI state.

  Coincidentally, this also enables the Surface 2 integrated HID sensor
hub
  which also requires an ACPI gpio operation region to start
initialization.

2) Various Bay Trail based tablets come with an external USB mux and
  TI T1210B USB phy to enable USB gadget mode. The mux is controlled by
a
  GPIO which is controlled by an edge triggered ACPI Event Interrupt
which
  monitors the micro-USB ID pin.

  When the tablet is connected to a PC (or no cable is plugged in), the
ID
  pin is high and the tablet should be in gadget mode. But the GPIO
  controlling the mux is initialized by the firmware so that the USB
data
  lines are muxed to the host controller.

  This means that if the user wants to use gadget mode, the user needs
to
  first plug in a host-cable to force the ID pin low and then unplug it
  and connect the tablet to a PC, to get the ACPI event handler to run
and
  switch the mux to device mode,

This commit fixes both by running the event-handler once on boot.

Note that the running of the event-handler is done from a
late_initcall,
this is done because the handler AML code may rely on OperationRegions
registered by other builtin drivers. This avoids errors like these:

[    0.133026] ACPI Error: No handler for Region [XSCG]
((____ptrval____)) [GenericSerialBus] (20180531/evregion-132)
[    0.133036] ACPI Error: Region GenericSerialBus (ID=9) has no
handler (20180531/exfldio-265)
[    0.133046] ACPI Error: Method parse/execution failed
\_SB.GPO2._E12, AE_NOT_EXIST (20180531/psparse-516)


Yep, this version I like more.

Benjamin, for sake of testing is it possible to revert (temporary) the
change you mentioned for _LID and test if this helps as well as stated?

Reviewed-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx>

Thanks.

One nitpick below, though I'm fine if you ignore it.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx>
[hdegoede: Document BYT USB mux reliance on initial trigger]
[hdegoede: Run event handler from a late_initcall, rather then
immediately]
Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx>
---
Changes in v2:
-Use late_initcall_sync to delay running the handler till other
drivers
  have been probed, rather then using a delayed_workqueue
---
  drivers/gpio/gpiolib-acpi.c | 56
++++++++++++++++++++++++++++++++++++-
  1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index e2232cbcec8b..addd9fecc198 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -25,6 +25,7 @@
struct acpi_gpio_event {
  	struct list_head node;
+	struct list_head initial_sync_list;
  	acpi_handle handle;
  	unsigned int pin;
  	unsigned int irq;
@@ -50,6 +51,9 @@ struct acpi_gpio_chip {
  	struct list_head events;
  };
+static LIST_HEAD(acpi_gpio_initial_sync_list);
+static DEFINE_MUTEX(acpi_gpio_initial_sync_list_lock);
+
  static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
  {
  	if (!gc->parent)
@@ -85,6 +89,21 @@ static struct gpio_desc *acpi_get_gpiod(char *path,
int pin)
  	return gpiochip_get_desc(chip, pin);
  }
+static void acpi_gpio_add_to_initial_sync_list(struct acpi_gpio_event
*event)
+{
+	mutex_lock(&acpi_gpio_initial_sync_list_lock);
+	list_add(&event->initial_sync_list,
&acpi_gpio_initial_sync_list);
+	mutex_unlock(&acpi_gpio_initial_sync_list_lock);
+}
+
+static void acpi_gpio_del_from_initial_sync_list(struct
acpi_gpio_event *event)
+{
+	mutex_lock(&acpi_gpio_initial_sync_list_lock);
+	if (!list_empty(&event->initial_sync_list))
+		list_del_init(&event->initial_sync_list);
+	mutex_unlock(&acpi_gpio_initial_sync_list_lock);
+}
+
  static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
  {
  	struct acpi_gpio_event *event = data;
@@ -136,7 +155,7 @@ static acpi_status
acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
  	irq_handler_t handler = NULL;
  	struct gpio_desc *desc;
  	unsigned long irqflags;
-	int ret, pin, irq;
+	int ret, pin, irq, value;
if (!acpi_gpio_get_irq_resource(ares, &agpio))
  		return AE_OK;
@@ -167,6 +186,8 @@ static acpi_status
acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
gpiod_direction_input(desc); + value = gpiod_get_value(desc);
+
  	ret = gpiochip_lock_as_irq(chip, pin);
  	if (ret) {
  		dev_err(chip->parent, "Failed to lock GPIO as
interrupt\n");
@@ -208,6 +229,7 @@ static acpi_status
acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
  	event->irq = irq;
  	event->pin = pin;
  	event->desc = desc;
+	INIT_LIST_HEAD(&event->initial_sync_list);
ret = request_threaded_irq(event->irq, NULL, handler,
irqflags,
  				   "ACPI:Event", event);
@@ -222,6 +244,18 @@ static acpi_status
acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
  		enable_irq_wake(irq);
list_add_tail(&event->node, &acpi_gpio->events);
+
+	/*
+	 * Make sure we trigger the initial state of the IRQ when
using RISING
+	 * or FALLING.  Note we run the handlers on late_init, the
AML code
+	 * may refer to OperationRegions from other (builtin) drivers
which
+	 * may be probed after us.
+	 */
+	if (handler == acpi_gpio_irq_handler &&
+	    (((irqflags & IRQF_TRIGGER_RISING) && value == 1) ||
+	     ((irqflags & IRQF_TRIGGER_FALLING) && value == 0)))
+		acpi_gpio_add_to_initial_sync_list(event);
+
  	return AE_OK;
fail_free_event:
@@ -294,6 +328,8 @@ void acpi_gpiochip_free_interrupts(struct
gpio_chip *chip)
  	list_for_each_entry_safe_reverse(event, ep, &acpi_gpio-
events, node) {
  		struct gpio_desc *desc;
+ acpi_gpio_del_from_initial_sync_list(event);
+
  		if (irqd_is_wakeup_set(irq_get_irq_data(event->irq)))
  			disable_irq_wake(event->irq);
@@ -1158,3 +1194,21 @@ bool acpi_can_fallback_to_crs(struct
acpi_device *adev, const char *con_id)
return con_id == NULL;
  }
+
+/* Sync the initial state of handlers after all builtin drivers have
probed */
+static int acpi_gpio_initial_sync(void)
+{
+	struct acpi_gpio_event *event, *ep;
+
+	mutex_lock(&acpi_gpio_initial_sync_list_lock);
+	list_for_each_entry_safe(event, ep,
&acpi_gpio_initial_sync_list,
+				 initial_sync_list) {
+		acpi_evaluate_object(event->handle, NULL, NULL,
NULL);
+		list_del_init(&event->initial_sync_list);
+	}
+	mutex_unlock(&acpi_gpio_initial_sync_list_lock);
+
+	return 0;
+}

+/* We must use _sync so that this runs after the first deferred_probe
run */

I think you may remove _ from above comment.

I actually like it with _ better, as that makes clear it is about the
_sync postfix to late_initcall where as just sync makes that less clear
IMHO.

+late_initcall_sync(acpi_gpio_initial_sync);

Regards,

Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux