acpi: Support generation of GPIO descriptor
Add a function to write a GPIO descriptor to the generated ACPI code. Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Wolfgang Wallner <wolfgang.wallner@br-automation.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com> [bmeng: Drop comment about the type always being ACPI_GPIO_TYPE_IO] Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
This commit is contained in:
parent
7fb8da4ce1
commit
a9e0a077df
@ -12,6 +12,7 @@
|
||||
#include <linux/bitops.h>
|
||||
|
||||
struct acpi_ctx;
|
||||
struct gpio_desc;
|
||||
struct irq;
|
||||
struct udevice;
|
||||
|
||||
@ -233,4 +234,25 @@ enum acpi_dev_status acpi_device_status(const struct udevice *dev);
|
||||
int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx,
|
||||
const struct irq *req_irq);
|
||||
|
||||
/**
|
||||
* acpi_device_write_gpio() - Write GpioIo() or GpioInt() descriptor
|
||||
*
|
||||
* @gpio: GPIO information to write
|
||||
* @return GPIO pin number of first GPIO if OK, -ve on error
|
||||
*/
|
||||
int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio);
|
||||
|
||||
/**
|
||||
* acpi_device_write_gpio_desc() - Write a GPIO to ACPI
|
||||
*
|
||||
* This creates a GPIO descriptor for a GPIO, including information ACPI needs
|
||||
* to use it.
|
||||
*
|
||||
* @ctx: ACPI context pointer
|
||||
* @desc: GPIO to write
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int acpi_device_write_gpio_desc(struct acpi_ctx *ctx,
|
||||
const struct gpio_desc *desc);
|
||||
|
||||
#endif
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <log.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <dm/acpi.h>
|
||||
|
||||
/**
|
||||
@ -203,5 +204,155 @@ int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx,
|
||||
if (ret)
|
||||
return log_msg_ret("write", ret);
|
||||
|
||||
return irq.pin;
|
||||
}
|
||||
|
||||
/* ACPI 6.3 section 6.4.3.8.1 - GPIO Interrupt or I/O */
|
||||
int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio)
|
||||
{
|
||||
void *start, *desc_length;
|
||||
void *pin_table_offset, *vendor_data_offset, *resource_offset;
|
||||
u16 flags = 0;
|
||||
int pin;
|
||||
|
||||
if (gpio->type > ACPI_GPIO_TYPE_IO)
|
||||
return -EINVAL;
|
||||
|
||||
start = acpigen_get_current(ctx);
|
||||
|
||||
/* Byte 0: Descriptor Type */
|
||||
acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_GPIO);
|
||||
|
||||
/* Byte 1-2: Length (fill in later) */
|
||||
desc_length = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 3: Revision ID */
|
||||
acpigen_emit_byte(ctx, ACPI_GPIO_REVISION_ID);
|
||||
|
||||
/* Byte 4: GpioIo or GpioInt */
|
||||
acpigen_emit_byte(ctx, gpio->type);
|
||||
|
||||
/*
|
||||
* Byte 5-6: General Flags
|
||||
* [15:1]: 0 => Reserved
|
||||
* [0]: 1 => ResourceConsumer
|
||||
*/
|
||||
acpigen_emit_word(ctx, 1 << 0);
|
||||
|
||||
switch (gpio->type) {
|
||||
case ACPI_GPIO_TYPE_INTERRUPT:
|
||||
/*
|
||||
* Byte 7-8: GPIO Interrupt Flags
|
||||
* [15:5]: 0 => Reserved
|
||||
* [4]: Wake (0=NO_WAKE 1=WAKE)
|
||||
* [3]: Sharing (0=EXCLUSIVE 1=SHARED)
|
||||
* [2:1]: Polarity (0=HIGH 1=LOW 2=BOTH)
|
||||
* [0]: Mode (0=LEVEL 1=EDGE)
|
||||
*/
|
||||
if (gpio->irq.mode == ACPI_IRQ_EDGE_TRIGGERED)
|
||||
flags |= 1 << 0;
|
||||
if (gpio->irq.shared == ACPI_IRQ_SHARED)
|
||||
flags |= 1 << 3;
|
||||
if (gpio->irq.wake == ACPI_IRQ_WAKE)
|
||||
flags |= 1 << 4;
|
||||
|
||||
switch (gpio->irq.polarity) {
|
||||
case ACPI_IRQ_ACTIVE_HIGH:
|
||||
flags |= 0 << 1;
|
||||
break;
|
||||
case ACPI_IRQ_ACTIVE_LOW:
|
||||
flags |= 1 << 1;
|
||||
break;
|
||||
case ACPI_IRQ_ACTIVE_BOTH:
|
||||
flags |= 2 << 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACPI_GPIO_TYPE_IO:
|
||||
/*
|
||||
* Byte 7-8: GPIO IO Flags
|
||||
* [15:4]: 0 => Reserved
|
||||
* [3]: Sharing (0=EXCLUSIVE 1=SHARED)
|
||||
* [2]: 0 => Reserved
|
||||
* [1:0]: IO Restriction
|
||||
* 0 => IoRestrictionNone
|
||||
* 1 => IoRestrictionInputOnly
|
||||
* 2 => IoRestrictionOutputOnly
|
||||
* 3 => IoRestrictionNoneAndPreserve
|
||||
*/
|
||||
flags |= gpio->io_restrict & 3;
|
||||
if (gpio->io_shared)
|
||||
flags |= 1 << 3;
|
||||
break;
|
||||
}
|
||||
acpigen_emit_word(ctx, flags);
|
||||
|
||||
/*
|
||||
* Byte 9: Pin Configuration
|
||||
* 0x01 => Default (no configuration applied)
|
||||
* 0x02 => Pull-up
|
||||
* 0x03 => Pull-down
|
||||
* 0x04-0x7F => Reserved
|
||||
* 0x80-0xff => Vendor defined
|
||||
*/
|
||||
acpigen_emit_byte(ctx, gpio->pull);
|
||||
|
||||
/* Byte 10-11: Output Drive Strength in 1/100 mA */
|
||||
acpigen_emit_word(ctx, gpio->output_drive_strength);
|
||||
|
||||
/* Byte 12-13: Debounce Timeout in 1/100 ms */
|
||||
acpigen_emit_word(ctx, gpio->interrupt_debounce_timeout);
|
||||
|
||||
/* Byte 14-15: Pin Table Offset, relative to start */
|
||||
pin_table_offset = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 16: Reserved */
|
||||
acpigen_emit_byte(ctx, 0);
|
||||
|
||||
/* Byte 17-18: Resource Source Name Offset, relative to start */
|
||||
resource_offset = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 19-20: Vendor Data Offset, relative to start */
|
||||
vendor_data_offset = largeres_write_len_f(ctx);
|
||||
|
||||
/* Byte 21-22: Vendor Data Length */
|
||||
acpigen_emit_word(ctx, 0);
|
||||
|
||||
/* Fill in Pin Table Offset */
|
||||
largeres_fill_from_len(ctx, pin_table_offset, start);
|
||||
|
||||
/* Pin Table, one word for each pin */
|
||||
for (pin = 0; pin < gpio->pin_count; pin++)
|
||||
acpigen_emit_word(ctx, gpio->pins[pin]);
|
||||
|
||||
/* Fill in Resource Source Name Offset */
|
||||
largeres_fill_from_len(ctx, resource_offset, start);
|
||||
|
||||
/* Resource Source Name String */
|
||||
acpigen_emit_string(ctx, gpio->resource);
|
||||
|
||||
/* Fill in Vendor Data Offset */
|
||||
largeres_fill_from_len(ctx, vendor_data_offset, start);
|
||||
|
||||
/* Fill in GPIO Descriptor Length (account for len word) */
|
||||
largeres_fill_len(ctx, desc_length);
|
||||
|
||||
return gpio->pins[0];
|
||||
}
|
||||
|
||||
int acpi_device_write_gpio_desc(struct acpi_ctx *ctx,
|
||||
const struct gpio_desc *desc)
|
||||
{
|
||||
struct acpi_gpio gpio;
|
||||
int ret;
|
||||
|
||||
ret = gpio_get_acpi(desc, &gpio);
|
||||
if (ret)
|
||||
return log_msg_ret("desc", ret);
|
||||
ret = acpi_device_write_gpio(ctx, &gpio);
|
||||
if (ret < 0)
|
||||
return log_msg_ret("gpio", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <malloc.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/test.h>
|
||||
@ -144,3 +145,93 @@ static int dm_test_acpi_interrupt(struct unit_test_state *uts)
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_interrupt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test emitting a GPIO descriptor */
|
||||
static int dm_test_acpi_gpio(struct unit_test_state *uts)
|
||||
{
|
||||
struct gpio_desc desc;
|
||||
struct acpi_ctx *ctx;
|
||||
struct udevice *dev;
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
|
||||
ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
|
||||
ut_asserteq_str("a-test", dev->name);
|
||||
ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
|
||||
|
||||
/* This should write GPIO pin 4 (see device tree test.dts ) */
|
||||
ut_asserteq(4, acpi_device_write_gpio_desc(ctx, &desc));
|
||||
ut_asserteq(35, acpigen_get_current(ctx) - ptr);
|
||||
ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]);
|
||||
ut_asserteq(32, get_unaligned((u16 *)(ptr + 1)));
|
||||
ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]);
|
||||
ut_asserteq(ACPI_GPIO_TYPE_IO, ptr[4]);
|
||||
ut_asserteq(1, get_unaligned((u16 *)(ptr + 5)));
|
||||
ut_asserteq(9, get_unaligned((u16 *)(ptr + 7)));
|
||||
ut_asserteq(ACPI_GPIO_PULL_UP, ptr[9]);
|
||||
ut_asserteq(1234, get_unaligned((u16 *)(ptr + 10)));
|
||||
ut_asserteq(0, get_unaligned((u16 *)(ptr + 12)));
|
||||
ut_asserteq(23, get_unaligned((u16 *)(ptr + 14)));
|
||||
ut_asserteq(0, ptr[16]);
|
||||
ut_asserteq(25, get_unaligned((u16 *)(ptr + 17)));
|
||||
ut_asserteq(35, get_unaligned((u16 *)(ptr + 19)));
|
||||
ut_asserteq(0, get_unaligned((u16 *)(ptr + 21)));
|
||||
|
||||
/* pin0 */
|
||||
ut_asserteq(4, get_unaligned((u16 *)(ptr + 23)));
|
||||
|
||||
ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25);
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test emitting a GPIO descriptor with an interrupt */
|
||||
static int dm_test_acpi_gpio_irq(struct unit_test_state *uts)
|
||||
{
|
||||
struct gpio_desc desc;
|
||||
struct acpi_ctx *ctx;
|
||||
struct udevice *dev;
|
||||
u8 *ptr;
|
||||
|
||||
ut_assertok(alloc_context(&ctx));
|
||||
|
||||
ptr = acpigen_get_current(ctx);
|
||||
|
||||
ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
|
||||
ut_asserteq_str("a-test", dev->name);
|
||||
ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0));
|
||||
|
||||
/* This should write GPIO pin 6 (see device tree test.dts ) */
|
||||
ut_asserteq(6, acpi_device_write_gpio_desc(ctx, &desc));
|
||||
ut_asserteq(35, acpigen_get_current(ctx) - ptr);
|
||||
ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]);
|
||||
ut_asserteq(32, get_unaligned((u16 *)(ptr + 1)));
|
||||
ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]);
|
||||
ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, ptr[4]);
|
||||
ut_asserteq(1, get_unaligned((u16 *)(ptr + 5)));
|
||||
ut_asserteq(29, get_unaligned((u16 *)(ptr + 7)));
|
||||
ut_asserteq(ACPI_GPIO_PULL_DOWN, ptr[9]);
|
||||
ut_asserteq(0, get_unaligned((u16 *)(ptr + 10)));
|
||||
ut_asserteq(4321, get_unaligned((u16 *)(ptr + 12)));
|
||||
ut_asserteq(23, get_unaligned((u16 *)(ptr + 14)));
|
||||
ut_asserteq(0, ptr[16]);
|
||||
ut_asserteq(25, get_unaligned((u16 *)(ptr + 17)));
|
||||
ut_asserteq(35, get_unaligned((u16 *)(ptr + 19)));
|
||||
ut_asserteq(0, get_unaligned((u16 *)(ptr + 21)));
|
||||
|
||||
/* pin0 */
|
||||
ut_asserteq(6, get_unaligned((u16 *)(ptr + 23)));
|
||||
|
||||
ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25);
|
||||
|
||||
free_context(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_gpio_irq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
Loading…
Reference in New Issue
Block a user