diff mbox series

[v3,34/35] dm: acpi: Enhance acpi_get_name()

Message ID 20200613205459.v3.34.I1a77e678d7c6973796cc651ac6d858e1122bb7ae@changeid
State Superseded
Headers show
Series dm: Add programmatic generation of ACPI tables (part B) | expand

Commit Message

Simon Glass June 14, 2020, 2:55 a.m. UTC
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 at chromium.org>
Reviewed-by: Wolfgang Wallner <wolfgang.wallner at br-automation.com>
---

Changes in v3:
- Fix 'of' typo

Changes in v1:
- Use acpi,ddn instead of acpi,desc
- Rename to acpi_device_infer_name()
- Update newly created sandbox tests

 arch/sandbox/dts/test.dts           |   1 +
 doc/device-tree-bindings/device.txt |  13 ++++
 drivers/core/acpi.c                 |  13 +++-
 drivers/i2c/sandbox_i2c.c           |  10 ---
 drivers/spi/sandbox_spi.c           |  10 ---
 include/acpi/acpi_device.h          |  15 ++++
 lib/acpi/acpi_device.c              | 106 ++++++++++++++++++++++++++++
 test/dm/acpi.c                      |  42 ++++++++++-
 test/dm/acpigen.c                   |   4 +-
 9 files changed, 189 insertions(+), 25 deletions(-)

Comments

Bin Meng June 29, 2020, 5:39 a.m. UTC | #1
Hi Simon,

On Sun, Jun 14, 2020 at 10:55 AM Simon Glass <sjg at chromium.org> wrote:
>
> 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 at chromium.org>
> Reviewed-by: Wolfgang Wallner <wolfgang.wallner at br-automation.com>
> ---
>
> Changes in v3:
> - Fix 'of' typo
>
> Changes in v1:
> - Use acpi,ddn instead of acpi,desc
> - Rename to acpi_device_infer_name()
> - Update newly created sandbox tests
>
>  arch/sandbox/dts/test.dts           |   1 +
>  doc/device-tree-bindings/device.txt |  13 ++++
>  drivers/core/acpi.c                 |  13 +++-
>  drivers/i2c/sandbox_i2c.c           |  10 ---
>  drivers/spi/sandbox_spi.c           |  10 ---
>  include/acpi/acpi_device.h          |  15 ++++
>  lib/acpi/acpi_device.c              | 106 ++++++++++++++++++++++++++++
>  test/dm/acpi.c                      |  42 ++++++++++-
>  test/dm/acpigen.c                   |   4 +-
>  9 files changed, 189 insertions(+), 25 deletions(-)
>
> diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
> index caf935cfdf..5d4d3daa28 100644
> --- a/arch/sandbox/dts/test.dts
> +++ b/arch/sandbox/dts/test.dts
> @@ -113,6 +113,7 @@
>                 int-array = <5678 9123 4567>;
>                 str-value = "test string";
>                 interrupts-extended = <&irq 3 0>;
> +               acpi,name = "GHIJ";
>         };
>
>         junk {
> diff --git a/doc/device-tree-bindings/device.txt b/doc/device-tree-bindings/device.txt
> index 27bd3978d9..7140339623 100644
> --- a/doc/device-tree-bindings/device.txt
> +++ b/doc/device-tree-bindings/device.txt
> @@ -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 at 10 {
>         interrupts-extended = <&acpi_gpe GPIO_21_IRQ IRQ_TYPE_EDGE_FALLING>;
>         linux,probed;
>  };
> +
> +pcie-a0 at 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>;
> +       };
> +};
> diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c
> index c1fc364550..acc2e3f7e9 100644
> --- a/drivers/core/acpi.c
> +++ b/drivers/core/acpi.c
> @@ -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;
>  }
>
>  /**
> diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c
> index 125026da90..57b1c60fde 100644
> --- a/drivers/i2c/sandbox_i2c.c
> +++ b/drivers/i2c/sandbox_i2c.c
> @@ -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)
>  };
> diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c
> index 4264acc953..5aeb6bdf43 100644
> --- a/drivers/spi/sandbox_spi.c
> +++ b/drivers/spi/sandbox_spi.c
> @@ -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(spi_sandbox) = {
>         .id     = UCLASS_SPI,
>         .of_match = sandbox_spi_ids,
>         .ops    = &sandbox_spi_ops,
> -       ACPI_OPS_PTR(&sandbox_spi_acpi_ops)
>  };
> diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h
> index 5019a79528..d076b452b5 100644
> --- a/include/acpi/acpi_device.h
> +++ b/include/acpi/acpi_device.h
> @@ -383,4 +383,19 @@ 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.
> + *
> + * @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
> diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c
> index 912369498a..c6560e37bc 100644
> --- a/lib/acpi/acpi_device.c
> +++ b/lib/acpi/acpi_device.c
> @@ -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>
> @@ -711,3 +713,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:
> +               return "XHCI";

What about USB 2.0 controllers?

> +       case UCLASS_PWM:
> +               return "PWM";
> +       default:
> +               return NULL;
> +       }
> +}

The above mapping seems to only work with x86 PCI devices? What about
other architectures? The API name is too generic if this is only for
86,

> +
> +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;
> +}
> diff --git a/test/dm/acpi.c b/test/dm/acpi.c
> index 1abde65c8c..69ca0902aa 100644
> --- a/test/dm/acpi.c
> +++ b/test/dm/acpi.c
> @@ -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);
> diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c
> index 3999825953..ea2033a27c 100644
> --- a/test/dm/acpigen.c
> +++ b/test/dm/acpigen.c
> @@ -307,7 +307,7 @@ static int dm_test_acpi_i2c(struct unit_test_state *uts)
>         ut_asserteq(6, get_unaligned((u16 *)(ptr + 10)));
>         ut_asserteq(100000, get_unaligned((u32 *)(ptr + 12)));
>         ut_asserteq(0x43, get_unaligned((u16 *)(ptr + 16)));
> -       ut_asserteq_str("\\_SB.SI2C", (char *)ptr + 18);
> +       ut_asserteq_str("\\_SB.I2C0", (char *)ptr + 18);
>
>         free_context(&ctx);
>
> @@ -343,7 +343,7 @@ static int dm_test_acpi_spi(struct unit_test_state *uts)
>         ut_asserteq(0, ptr[17]);
>         ut_asserteq(0, ptr[18]);
>         ut_asserteq(0, get_unaligned((u16 *)(ptr + 19)));
> -       ut_asserteq_str("\\_SB.SSPI", (char *)ptr + 21);
> +       ut_asserteq_str("\\_SB.SPI0", (char *)ptr + 21);
>
>         free_context(&ctx);
>
> --

Regards,
Bin
diff mbox series

Patch

diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index caf935cfdf..5d4d3daa28 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -113,6 +113,7 @@ 
 		int-array = <5678 9123 4567>;
 		str-value = "test string";
 		interrupts-extended = <&irq 3 0>;
+		acpi,name = "GHIJ";
 	};
 
 	junk {
diff --git a/doc/device-tree-bindings/device.txt b/doc/device-tree-bindings/device.txt
index 27bd3978d9..7140339623 100644
--- a/doc/device-tree-bindings/device.txt
+++ b/doc/device-tree-bindings/device.txt
@@ -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 at 10 {
 	interrupts-extended = <&acpi_gpe GPIO_21_IRQ IRQ_TYPE_EDGE_FALLING>;
 	linux,probed;
 };
+
+pcie-a0 at 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>;
+	};
+};
diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c
index c1fc364550..acc2e3f7e9 100644
--- a/drivers/core/acpi.c
+++ b/drivers/core/acpi.c
@@ -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;
 }
 
 /**
diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c
index 125026da90..57b1c60fde 100644
--- a/drivers/i2c/sandbox_i2c.c
+++ b/drivers/i2c/sandbox_i2c.c
@@ -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)
 };
diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c
index 4264acc953..5aeb6bdf43 100644
--- a/drivers/spi/sandbox_spi.c
+++ b/drivers/spi/sandbox_spi.c
@@ -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(spi_sandbox) = {
 	.id	= UCLASS_SPI,
 	.of_match = sandbox_spi_ids,
 	.ops	= &sandbox_spi_ops,
-	ACPI_OPS_PTR(&sandbox_spi_acpi_ops)
 };
diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h
index 5019a79528..d076b452b5 100644
--- a/include/acpi/acpi_device.h
+++ b/include/acpi/acpi_device.h
@@ -383,4 +383,19 @@  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.
+ *
+ * @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
diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c
index 912369498a..c6560e37bc 100644
--- a/lib/acpi/acpi_device.c
+++ b/lib/acpi/acpi_device.c
@@ -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>
@@ -711,3 +713,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:
+		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;
+}
diff --git a/test/dm/acpi.c b/test/dm/acpi.c
index 1abde65c8c..69ca0902aa 100644
--- a/test/dm/acpi.c
+++ b/test/dm/acpi.c
@@ -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);
diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c
index 3999825953..ea2033a27c 100644
--- a/test/dm/acpigen.c
+++ b/test/dm/acpigen.c
@@ -307,7 +307,7 @@  static int dm_test_acpi_i2c(struct unit_test_state *uts)
 	ut_asserteq(6, get_unaligned((u16 *)(ptr + 10)));
 	ut_asserteq(100000, get_unaligned((u32 *)(ptr + 12)));
 	ut_asserteq(0x43, get_unaligned((u16 *)(ptr + 16)));
-	ut_asserteq_str("\\_SB.SI2C", (char *)ptr + 18);
+	ut_asserteq_str("\\_SB.I2C0", (char *)ptr + 18);
 
 	free_context(&ctx);
 
@@ -343,7 +343,7 @@  static int dm_test_acpi_spi(struct unit_test_state *uts)
 	ut_asserteq(0, ptr[17]);
 	ut_asserteq(0, ptr[18]);
 	ut_asserteq(0, get_unaligned((u16 *)(ptr + 19)));
-	ut_asserteq_str("\\_SB.SSPI", (char *)ptr + 21);
+	ut_asserteq_str("\\_SB.SPI0", (char *)ptr + 21);
 
 	free_context(&ctx);