mirror of
https://github.com/AsahiLinux/u-boot
synced 2025-02-17 22:49:02 +00:00
gpio: Add a method to convert a GPIO to ACPI
When generating ACPI tables we need to convert GPIOs in U-Boot to the ACPI structures required by ACPI. This is a SoC-specific conversion and cannot be handled by generic code, so add a new GPIO method to do the conversion. 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>
This commit is contained in:
parent
ff715c6f4f
commit
2912686c08
5 changed files with 278 additions and 0 deletions
|
@ -13,6 +13,7 @@
|
|||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <malloc.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <linux/bug.h>
|
||||
|
@ -855,6 +856,27 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio)
|
||||
{
|
||||
struct dm_gpio_ops *ops;
|
||||
|
||||
memset(gpio, '\0', sizeof(*gpio));
|
||||
if (!dm_gpio_is_valid(desc)) {
|
||||
/* Indicate that the GPIO is not valid */
|
||||
gpio->pin_count = 0;
|
||||
gpio->pins[0] = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ops = gpio_get_ops(desc->dev);
|
||||
if (!ops->get_acpi)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_acpi(desc, gpio);
|
||||
}
|
||||
#endif
|
||||
|
||||
int gpio_claim_vector(const int *gpio_num_array, const char *fmt)
|
||||
{
|
||||
int i, ret;
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
#include <fdtdec.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/of.h>
|
||||
|
@ -197,6 +199,63 @@ static int sb_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
static int sb_gpio_get_acpi(const struct gpio_desc *desc,
|
||||
struct acpi_gpio *gpio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Note that gpio_get_acpi() zeroes *gpio before calling here */
|
||||
gpio->pin_count = 1;
|
||||
gpio->pins[0] = desc->offset;
|
||||
ret = acpi_device_scope(desc->dev, gpio->resource,
|
||||
sizeof(gpio->resource));
|
||||
if (ret)
|
||||
return log_ret(ret);
|
||||
|
||||
/* All of these values are just used for testing */
|
||||
if (desc->flags & GPIOD_ACTIVE_LOW) {
|
||||
gpio->pin0_addr = 0x80012 + desc->offset;
|
||||
gpio->type = ACPI_GPIO_TYPE_INTERRUPT;
|
||||
gpio->pull = ACPI_GPIO_PULL_DOWN;
|
||||
gpio->interrupt_debounce_timeout = 4321;
|
||||
|
||||
/* We use the GpioInt part */
|
||||
gpio->irq.pin = desc->offset;
|
||||
gpio->irq.polarity = ACPI_IRQ_ACTIVE_BOTH;
|
||||
gpio->irq.shared = ACPI_IRQ_SHARED;
|
||||
gpio->irq.wake = ACPI_IRQ_WAKE;
|
||||
|
||||
/* The GpioIo part is only used for testing */
|
||||
gpio->polarity = ACPI_GPIO_ACTIVE_LOW;
|
||||
} else {
|
||||
gpio->pin0_addr = 0xc00dc + desc->offset;
|
||||
gpio->type = ACPI_GPIO_TYPE_IO;
|
||||
gpio->pull = ACPI_GPIO_PULL_UP;
|
||||
gpio->interrupt_debounce_timeout = 0;
|
||||
|
||||
/* The GpioInt part is not used */
|
||||
|
||||
/* We use the GpioIo part */
|
||||
gpio->output_drive_strength = 1234;
|
||||
gpio->io_shared = true;
|
||||
gpio->io_restrict = ACPI_GPIO_IO_RESTRICT_INPUT;
|
||||
gpio->polarity = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sb_gpio_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
return acpi_copy_name(out_name, "GPIO");
|
||||
}
|
||||
|
||||
struct acpi_ops gpio_sandbox_acpi_ops = {
|
||||
.get_name = sb_gpio_get_name,
|
||||
};
|
||||
#endif /* ACPIGEN */
|
||||
|
||||
static const struct dm_gpio_ops gpio_sandbox_ops = {
|
||||
.direction_input = sb_gpio_direction_input,
|
||||
.direction_output = sb_gpio_direction_output,
|
||||
|
@ -206,6 +265,9 @@ static const struct dm_gpio_ops gpio_sandbox_ops = {
|
|||
.xlate = sb_gpio_xlate,
|
||||
.set_dir_flags = sb_gpio_set_dir_flags,
|
||||
.get_dir_flags = sb_gpio_get_dir_flags,
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
.get_acpi = sb_gpio_get_acpi,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int sandbox_gpio_ofdata_to_platdata(struct udevice *dev)
|
||||
|
@ -252,6 +314,7 @@ U_BOOT_DRIVER(sandbox_gpio) = {
|
|||
.probe = gpio_sandbox_probe,
|
||||
.remove = gpio_sandbox_remove,
|
||||
.ops = &gpio_sandbox_ops,
|
||||
ACPI_OPS_PTR(&gpio_sandbox_acpi_ops)
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias)
|
||||
|
@ -421,6 +484,13 @@ static int sb_pinctrl_get_pin_muxing(struct udevice *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
static int sb_pinctrl_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
return acpi_copy_name(out_name, "PINC");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sandbox_pinctrl_probe(struct udevice *dev)
|
||||
{
|
||||
struct sb_pinctrl_priv *priv = dev_get_priv(dev);
|
||||
|
@ -436,6 +506,12 @@ static struct pinctrl_ops sandbox_pinctrl_gpio_ops = {
|
|||
.get_pin_muxing = sb_pinctrl_get_pin_muxing,
|
||||
};
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
struct acpi_ops pinctrl_sandbox_acpi_ops = {
|
||||
.get_name = sb_pinctrl_get_name,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct udevice_id sandbox_pinctrl_gpio_match[] = {
|
||||
{ .compatible = "sandbox,pinctrl-gpio" },
|
||||
{ /* sentinel */ }
|
||||
|
@ -449,4 +525,5 @@ U_BOOT_DRIVER(sandbox_pinctrl_gpio) = {
|
|||
.bind = dm_scan_fdt_dev,
|
||||
.probe = sandbox_pinctrl_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct sb_pinctrl_priv),
|
||||
ACPI_OPS_PTR(&pinctrl_sandbox_acpi_ops)
|
||||
};
|
||||
|
|
|
@ -92,6 +92,96 @@ struct acpi_irq {
|
|||
enum acpi_irq_wake wake;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum acpi_gpio_type - type of the descriptor
|
||||
*
|
||||
* @ACPI_GPIO_TYPE_INTERRUPT: GpioInterrupt
|
||||
* @ACPI_GPIO_TYPE_IO: GpioIo
|
||||
*/
|
||||
enum acpi_gpio_type {
|
||||
ACPI_GPIO_TYPE_INTERRUPT,
|
||||
ACPI_GPIO_TYPE_IO,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum acpi_gpio_pull - pull direction
|
||||
*
|
||||
* @ACPI_GPIO_PULL_DEFAULT: Use default value for pin
|
||||
* @ACPI_GPIO_PULL_UP: Pull up
|
||||
* @ACPI_GPIO_PULL_DOWN: Pull down
|
||||
* @ACPI_GPIO_PULL_NONE: No pullup/pulldown
|
||||
*/
|
||||
enum acpi_gpio_pull {
|
||||
ACPI_GPIO_PULL_DEFAULT,
|
||||
ACPI_GPIO_PULL_UP,
|
||||
ACPI_GPIO_PULL_DOWN,
|
||||
ACPI_GPIO_PULL_NONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum acpi_gpio_io_restrict - controls input/output of pin
|
||||
*
|
||||
* @ACPI_GPIO_IO_RESTRICT_NONE: no restrictions
|
||||
* @ACPI_GPIO_IO_RESTRICT_INPUT: input only (no output)
|
||||
* @ACPI_GPIO_IO_RESTRICT_OUTPUT: output only (no input)
|
||||
* @ACPI_GPIO_IO_RESTRICT_PRESERVE: preserve settings when driver not active
|
||||
*/
|
||||
enum acpi_gpio_io_restrict {
|
||||
ACPI_GPIO_IO_RESTRICT_NONE,
|
||||
ACPI_GPIO_IO_RESTRICT_INPUT,
|
||||
ACPI_GPIO_IO_RESTRICT_OUTPUT,
|
||||
ACPI_GPIO_IO_RESTRICT_PRESERVE,
|
||||
};
|
||||
|
||||
/** enum acpi_gpio_polarity - controls the GPIO polarity */
|
||||
enum acpi_gpio_polarity {
|
||||
ACPI_GPIO_ACTIVE_HIGH = 0,
|
||||
ACPI_GPIO_ACTIVE_LOW = 1,
|
||||
};
|
||||
|
||||
#define ACPI_GPIO_REVISION_ID 1
|
||||
#define ACPI_GPIO_MAX_PINS 2
|
||||
|
||||
/**
|
||||
* struct acpi_gpio - representation of an ACPI GPIO
|
||||
*
|
||||
* @pin_count: Number of pins represented
|
||||
* @pins: List of pins
|
||||
* @pin0_addr: Address in memory of the control registers for pin 0. This is
|
||||
* used when generating ACPI tables
|
||||
* @type: GPIO type
|
||||
* @pull: Pullup/pulldown setting
|
||||
* @resource: Resource name for this GPIO controller
|
||||
* For GpioInt:
|
||||
* @interrupt_debounce_timeout: Debounce timeout in units of 10us
|
||||
* @irq: Interrupt
|
||||
*
|
||||
* For GpioIo:
|
||||
* @output_drive_strength: Drive strength in units of 10uA
|
||||
* @io_shared; true if GPIO is shared
|
||||
* @io_restrict: I/O restriction setting
|
||||
* @polarity: GPIO polarity
|
||||
*/
|
||||
struct acpi_gpio {
|
||||
int pin_count;
|
||||
u16 pins[ACPI_GPIO_MAX_PINS];
|
||||
ulong pin0_addr;
|
||||
|
||||
enum acpi_gpio_type type;
|
||||
enum acpi_gpio_pull pull;
|
||||
char resource[ACPI_PATH_MAX];
|
||||
|
||||
/* GpioInt */
|
||||
u16 interrupt_debounce_timeout;
|
||||
struct acpi_irq irq;
|
||||
|
||||
/* GpioIo */
|
||||
u16 output_drive_strength;
|
||||
bool io_shared;
|
||||
enum acpi_gpio_io_restrict io_restrict;
|
||||
enum acpi_gpio_polarity polarity;
|
||||
};
|
||||
|
||||
/**
|
||||
* acpi_device_path() - Get the full path to an ACPI device
|
||||
*
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <dm/ofnode.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
struct acpi_gpio;
|
||||
struct ofnode_phandle_args;
|
||||
|
||||
/*
|
||||
|
@ -329,6 +330,20 @@ struct dm_gpio_ops {
|
|||
*/
|
||||
int (*get_dir_flags)(struct udevice *dev, unsigned int offset,
|
||||
ulong *flags);
|
||||
|
||||
#if CONFIG_IS_ENABLED(ACPIGEN)
|
||||
/**
|
||||
* get_acpi() - Get the ACPI info for a GPIO
|
||||
*
|
||||
* This converts a GPIO to an ACPI structure for adding to the ACPI
|
||||
* tables.
|
||||
*
|
||||
* @desc: GPIO description to convert
|
||||
* @gpio: Output ACPI GPIO information
|
||||
* @return ACPI pin number or -ve on error
|
||||
*/
|
||||
int (*get_acpi)(const struct gpio_desc *desc, struct acpi_gpio *gpio);
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -674,4 +689,16 @@ int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags);
|
|||
*/
|
||||
int gpio_get_number(const struct gpio_desc *desc);
|
||||
|
||||
/**
|
||||
* gpio_get_acpi() - Get the ACPI pin for a GPIO
|
||||
*
|
||||
* This converts a GPIO to an ACPI pin number for adding to the ACPI
|
||||
* tables. If the GPIO is invalid, the pin_count and pins[0] are set to 0
|
||||
*
|
||||
* @desc: GPIO description to convert
|
||||
* @gpio: Output ACPI GPIO information
|
||||
* @return ACPI pin number or -ve on error
|
||||
*/
|
||||
int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio);
|
||||
|
||||
#endif /* _ASM_GENERIC_GPIO_H_ */
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <dm/root.h>
|
||||
#include <dm/test.h>
|
||||
#include <dm/util.h>
|
||||
|
@ -417,3 +418,64 @@ static int dm_test_gpio_get_dir_flags(struct unit_test_state *uts)
|
|||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_gpio_get_dir_flags, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test of gpio_get_acpi() */
|
||||
static int dm_test_gpio_get_acpi(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_gpio agpio;
|
||||
struct udevice *dev;
|
||||
struct gpio_desc desc;
|
||||
|
||||
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));
|
||||
|
||||
/* See sb_gpio_get_acpi() */
|
||||
ut_assertok(gpio_get_acpi(&desc, &agpio));
|
||||
ut_asserteq(1, agpio.pin_count);
|
||||
ut_asserteq(4, agpio.pins[0]);
|
||||
ut_asserteq(ACPI_GPIO_TYPE_IO, agpio.type);
|
||||
ut_asserteq(ACPI_GPIO_PULL_UP, agpio.pull);
|
||||
ut_asserteq_str("\\_SB.PINC", agpio.resource);
|
||||
ut_asserteq(0, agpio.interrupt_debounce_timeout);
|
||||
ut_asserteq(0, agpio.irq.pin);
|
||||
ut_asserteq(1234, agpio.output_drive_strength);
|
||||
ut_asserteq(true, agpio.io_shared);
|
||||
ut_asserteq(ACPI_GPIO_IO_RESTRICT_INPUT, agpio.io_restrict);
|
||||
ut_asserteq(ACPI_GPIO_ACTIVE_HIGH, agpio.polarity);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_gpio_get_acpi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
||||
/* Test of gpio_get_acpi() with an interrupt GPIO */
|
||||
static int dm_test_gpio_get_acpi_irq(struct unit_test_state *uts)
|
||||
{
|
||||
struct acpi_gpio agpio;
|
||||
struct udevice *dev;
|
||||
struct gpio_desc desc;
|
||||
|
||||
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));
|
||||
|
||||
/* See sb_gpio_get_acpi() */
|
||||
ut_assertok(gpio_get_acpi(&desc, &agpio));
|
||||
ut_asserteq(1, agpio.pin_count);
|
||||
ut_asserteq(6, agpio.pins[0]);
|
||||
ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, agpio.type);
|
||||
ut_asserteq(ACPI_GPIO_PULL_DOWN, agpio.pull);
|
||||
ut_asserteq_str("\\_SB.PINC", agpio.resource);
|
||||
ut_asserteq(4321, agpio.interrupt_debounce_timeout);
|
||||
ut_asserteq(6, agpio.irq.pin);
|
||||
ut_asserteq(ACPI_IRQ_ACTIVE_BOTH, agpio.irq.polarity);
|
||||
ut_asserteq(ACPI_IRQ_SHARED, agpio.irq.shared);
|
||||
ut_asserteq(true, agpio.irq.wake);
|
||||
ut_asserteq(0, agpio.output_drive_strength);
|
||||
ut_asserteq(false, agpio.io_shared);
|
||||
ut_asserteq(0, agpio.io_restrict);
|
||||
ut_asserteq(ACPI_GPIO_ACTIVE_LOW, agpio.polarity);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_gpio_get_acpi_irq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
|
Loading…
Add table
Reference in a new issue