mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +00:00
dm: acpi: Enhance acpi_get_name()
For many device types it is possible to figure out the name just by looking at its uclass or parent. Add a function to handle this, since it allows us to cover the vast majority of cases automatically. However it is sometimes impossible to figure out an ACPI name for a device just by looking at its uclass. For example a touch device may have a vendor-specific name. Add a new "acpi,name" property to allow a custom name to be created. With this new feature we can drop the get_name() methods in the sandbox I2C and SPI drivers. They were only added for testing purposes. Update the tests to use the new values. 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
20349781a3
commit
fefac0b064
8 changed files with 190 additions and 23 deletions
|
@ -113,6 +113,7 @@
|
|||
int-array = <5678 9123 4567>;
|
||||
str-value = "test string";
|
||||
interrupts-extended = <&irq 3 0>;
|
||||
acpi,name = "GHIJ";
|
||||
};
|
||||
|
||||
junk {
|
||||
|
|
|
@ -17,6 +17,8 @@ the acpi,compatible property.
|
|||
System) Device Name)
|
||||
- acpi,hid : Contains the string to use as the HID (Hardware ID)
|
||||
identifier _HID
|
||||
- acpi,name : Provides the ACPI name for a device, which is a string consisting
|
||||
of four alphanumeric character (upper case)
|
||||
- acpi,uid : _UID value for device
|
||||
- linux,probed : Tells U-Boot to add 'linux,probed' to the ACPI tables so that
|
||||
Linux will only load the driver if the device can be detected (e.g. on I2C
|
||||
|
@ -34,3 +36,14 @@ elan_touchscreen: elan-touchscreen@10 {
|
|||
interrupts-extended = <&acpi_gpe GPIO_21_IRQ IRQ_TYPE_EDGE_FALLING>;
|
||||
linux,probed;
|
||||
};
|
||||
|
||||
pcie-a0@14,0 {
|
||||
reg = <0x0000a000 0 0 0 0>;
|
||||
acpi,name = "RP01";
|
||||
wifi: wifi {
|
||||
compatible = "intel,generic-wifi";
|
||||
acpi,ddn = "Intel WiFi";
|
||||
acpi,name = "WF00";
|
||||
interrupts-extended = <&acpi_gpe 0x3c 0>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
#define LOG_CATEOGRY LOGC_ACPI
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <dm.h>
|
||||
#include <log.h>
|
||||
#include <malloc.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <dm/acpi.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/root.h>
|
||||
|
@ -65,12 +66,20 @@ int acpi_copy_name(char *out_name, const char *name)
|
|||
int acpi_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
struct acpi_ops *aops;
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
aops = device_get_acpi_ops(dev);
|
||||
if (aops && aops->get_name)
|
||||
return aops->get_name(dev, out_name);
|
||||
name = dev_read_string(dev, "acpi,name");
|
||||
if (name)
|
||||
return acpi_copy_name(out_name, name);
|
||||
ret = acpi_device_infer_name(dev, out_name);
|
||||
if (ret)
|
||||
return log_msg_ret("dev", ret);
|
||||
|
||||
return -ENOSYS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -84,15 +84,6 @@ static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
|||
return ops->xfer(emul, msg, nmsgs);
|
||||
}
|
||||
|
||||
static int sandbox_i2c_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
return acpi_copy_name(out_name, "SI2C");
|
||||
}
|
||||
|
||||
struct acpi_ops sandbox_i2c_acpi_ops = {
|
||||
.get_name = sandbox_i2c_get_name,
|
||||
};
|
||||
|
||||
static const struct dm_i2c_ops sandbox_i2c_ops = {
|
||||
.xfer = sandbox_i2c_xfer,
|
||||
};
|
||||
|
@ -108,5 +99,4 @@ U_BOOT_DRIVER(i2c_sandbox) = {
|
|||
.of_match = sandbox_i2c_ids,
|
||||
.ops = &sandbox_i2c_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_i2c_priv),
|
||||
ACPI_OPS_PTR(&sandbox_i2c_acpi_ops)
|
||||
};
|
||||
|
|
|
@ -134,15 +134,6 @@ static int sandbox_spi_get_mmap(struct udevice *dev, ulong *map_basep,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_spi_get_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
return acpi_copy_name(out_name, "SSPI");
|
||||
}
|
||||
|
||||
struct acpi_ops sandbox_spi_acpi_ops = {
|
||||
.get_name = sandbox_spi_get_name,
|
||||
};
|
||||
|
||||
static const struct dm_spi_ops sandbox_spi_ops = {
|
||||
.xfer = sandbox_spi_xfer,
|
||||
.set_speed = sandbox_spi_set_speed,
|
||||
|
@ -161,5 +152,4 @@ U_BOOT_DRIVER(sandbox_spi) = {
|
|||
.id = UCLASS_SPI,
|
||||
.of_match = sandbox_spi_ids,
|
||||
.ops = &sandbox_spi_ops,
|
||||
ACPI_OPS_PTR(&sandbox_spi_acpi_ops)
|
||||
};
|
||||
|
|
|
@ -384,4 +384,22 @@ int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val,
|
|||
const struct gpio_desc *stop_gpio,
|
||||
uint stop_delay_ms, uint stop_off_delay_ms);
|
||||
|
||||
/**
|
||||
* acpi_device_infer_name() - Infer the name from its uclass or parent
|
||||
*
|
||||
* Many ACPI devices have a standard name that can be inferred from the uclass
|
||||
* they are in, or the uclass of their parent. These rules are implemented in
|
||||
* this function. It attempts to produce a name for a device based on these
|
||||
* rules.
|
||||
*
|
||||
* NOTE: This currently supports only x86 devices. Feel free to enhance it for
|
||||
* other architectures as needed.
|
||||
*
|
||||
* @dev: Device to check
|
||||
* @out_name: Place to put the name (must hold ACPI_NAME_MAX bytes)
|
||||
* @return 0 if a name was found, -ENOENT if not found, -ENXIO if the device
|
||||
* sequence number could not be determined
|
||||
*/
|
||||
int acpi_device_infer_name(const struct udevice *dev, char *out_name);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <dm.h>
|
||||
#include <irq.h>
|
||||
#include <log.h>
|
||||
#include <usb.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
|
@ -715,3 +717,107 @@ int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev)
|
|||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SPI */
|
||||
|
||||
static const char *acpi_name_from_id(enum uclass_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case UCLASS_USB_HUB:
|
||||
/* Root Hub */
|
||||
return "RHUB";
|
||||
/* DSDT: acpi/northbridge.asl */
|
||||
case UCLASS_NORTHBRIDGE:
|
||||
return "MCHC";
|
||||
/* DSDT: acpi/lpc.asl */
|
||||
case UCLASS_LPC:
|
||||
return "LPCB";
|
||||
/* DSDT: acpi/xhci.asl */
|
||||
case UCLASS_USB:
|
||||
/* This only supports USB3.0 controllers at present */
|
||||
return "XHCI";
|
||||
case UCLASS_PWM:
|
||||
return "PWM";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int acpi_check_seq(const struct udevice *dev)
|
||||
{
|
||||
if (dev->req_seq == -1) {
|
||||
log_warning("Device '%s' has no seq\n", dev->name);
|
||||
return log_msg_ret("no seq", -ENXIO);
|
||||
}
|
||||
|
||||
return dev->req_seq;
|
||||
}
|
||||
|
||||
/* If you change this function, add test cases to dm_test_acpi_get_name() */
|
||||
int acpi_device_infer_name(const struct udevice *dev, char *out_name)
|
||||
{
|
||||
enum uclass_id parent_id = UCLASS_INVALID;
|
||||
enum uclass_id id;
|
||||
const char *name = NULL;
|
||||
|
||||
id = device_get_uclass_id(dev);
|
||||
if (dev_get_parent(dev))
|
||||
parent_id = device_get_uclass_id(dev_get_parent(dev));
|
||||
|
||||
if (id == UCLASS_SOUND)
|
||||
name = "HDAS";
|
||||
else if (id == UCLASS_PCI)
|
||||
name = "PCI0";
|
||||
else if (device_is_on_pci_bus(dev))
|
||||
name = acpi_name_from_id(id);
|
||||
if (!name) {
|
||||
switch (parent_id) {
|
||||
case UCLASS_USB: {
|
||||
struct usb_device *udev = dev_get_parent_priv(dev);
|
||||
|
||||
sprintf(out_name, udev->speed >= USB_SPEED_SUPER ?
|
||||
"HS%02d" : "FS%02d", udev->portnr);
|
||||
name = out_name;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
int num;
|
||||
|
||||
switch (id) {
|
||||
/* DSDT: acpi/lpss.asl */
|
||||
case UCLASS_SERIAL:
|
||||
num = acpi_check_seq(dev);
|
||||
if (num < 0)
|
||||
return num;
|
||||
sprintf(out_name, "URT%d", num);
|
||||
name = out_name;
|
||||
break;
|
||||
case UCLASS_I2C:
|
||||
num = acpi_check_seq(dev);
|
||||
if (num < 0)
|
||||
return num;
|
||||
sprintf(out_name, "I2C%d", num);
|
||||
name = out_name;
|
||||
break;
|
||||
case UCLASS_SPI:
|
||||
num = acpi_check_seq(dev);
|
||||
if (num < 0)
|
||||
return num;
|
||||
sprintf(out_name, "SPI%d", num);
|
||||
name = out_name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
log_warning("No name for device '%s'\n", dev->name);
|
||||
return -ENOENT;
|
||||
}
|
||||
if (name != out_name)
|
||||
acpi_copy_name(out_name, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -124,12 +124,52 @@ UCLASS_DRIVER(testacpi) = {
|
|||
static int dm_test_acpi_get_name(struct unit_test_state *uts)
|
||||
{
|
||||
char name[ACPI_NAME_MAX];
|
||||
struct udevice *dev;
|
||||
struct udevice *dev, *dev2, *i2c, *spi, *serial, *timer, *sound;
|
||||
struct udevice *pci, *root;
|
||||
|
||||
/* Test getting the name from the driver */
|
||||
ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
|
||||
ut_assertok(acpi_get_name(dev, name));
|
||||
ut_asserteq_str(ACPI_TEST_DEV_NAME, name);
|
||||
|
||||
/* Test getting the name from the device tree */
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
|
||||
&dev2));
|
||||
ut_assertok(acpi_get_name(dev2, name));
|
||||
ut_asserteq_str("GHIJ", name);
|
||||
|
||||
/* Test getting the name from acpi_device_get_name() */
|
||||
ut_assertok(uclass_first_device(UCLASS_I2C, &i2c));
|
||||
ut_assertok(acpi_get_name(i2c, name));
|
||||
ut_asserteq_str("I2C0", name);
|
||||
|
||||
ut_assertok(uclass_first_device(UCLASS_SPI, &spi));
|
||||
ut_assertok(acpi_get_name(spi, name));
|
||||
ut_asserteq_str("SPI0", name);
|
||||
|
||||
/* The uart has no sequence number, so this should fail */
|
||||
ut_assertok(uclass_first_device(UCLASS_SERIAL, &serial));
|
||||
ut_asserteq(-ENXIO, acpi_get_name(serial, name));
|
||||
|
||||
/* ACPI doesn't know about the timer */
|
||||
ut_assertok(uclass_first_device(UCLASS_TIMER, &timer));
|
||||
ut_asserteq(-ENOENT, acpi_get_name(timer, name));
|
||||
|
||||
/* May as well test the rest of the cases */
|
||||
ut_assertok(uclass_first_device(UCLASS_SOUND, &sound));
|
||||
ut_assertok(acpi_get_name(sound, name));
|
||||
ut_asserteq_str("HDAS", name);
|
||||
|
||||
ut_assertok(uclass_first_device(UCLASS_PCI, &pci));
|
||||
ut_assertok(acpi_get_name(pci, name));
|
||||
ut_asserteq_str("PCI0", name);
|
||||
|
||||
ut_assertok(uclass_first_device(UCLASS_ROOT, &root));
|
||||
ut_assertok(acpi_get_name(root, name));
|
||||
ut_asserteq_str("\\_SB", name);
|
||||
|
||||
/* Note that we don't have tests for acpi_name_from_id() */
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_acpi_get_name, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
||||
|
|
Loading…
Reference in a new issue