Merge branches 'acpi-resources' and 'acpi-docs'

* acpi-resources:
  Revert "ACPI / resources: Use AE_CTRL_TERMINATE to terminate resources walks"
  resource: provide meaningful MODULE_LICENSE() in test suite
  ASoC: Intel: catpt: Replace open coded variant of resource_intersection()
  ACPI: watchdog: Replace open coded variant of resource_union()
  PCI/ACPI: Replace open coded variant of resource_union()
  resource: Add test cases for new resource API
  resource: Introduce resource_intersection() for overlapping resources
  resource: Introduce resource_union() for overlapping resources
  resource: Group resource_overlaps() with other inline helpers
  resource: Simplify region_intersects() by reducing conditionals

* acpi-docs:
  Documentation: ACPI: enumeration: add PCI hierarchy representation
  Documentation: ACPI: _DSD: enable hyperlink in final references
  Documentation: ACPI: explain how to use gpio-line-names
This commit is contained in:
Rafael J. Wysocki 2020-12-15 15:30:03 +01:00
commit 5e2cde03da
13 changed files with 414 additions and 39 deletions

View File

@ -90,10 +90,10 @@ where
References
==========
[1] Device tree. <URL:https://www.devicetree.org>, referenced 2019-02-21.
[1] Device tree. https://www.devicetree.org, referenced 2019-02-21.
[2] Advanced Configuration and Power Interface Specification.
<URL:https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf>,
https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf,
referenced 2019-02-21.
[3] Documentation/devicetree/bindings/leds/common.txt
@ -101,11 +101,11 @@ References
[4] Documentation/devicetree/bindings/media/video-interfaces.txt
[5] Device Properties UUID For _DSD.
<URL:https://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf>,
https://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf,
referenced 2019-02-21.
[6] Hierarchical Data Extension UUID For _DSD.
<URL:https://www.uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.1.pdf>,
https://www.uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.1.pdf,
referenced 2019-02-21.
[7] Documentation/firmware-guide/acpi/dsd/data-node-references.rst

View File

@ -461,3 +461,157 @@ Otherwise, the _DSD itself is regarded as invalid and therefore the "compatible"
property returned by it is meaningless.
Refer to :doc:`DSD-properties-rules` for more information.
PCI hierarchy representation
============================
Sometimes could be useful to enumerate a PCI device, knowing its position on the
PCI bus.
For example, some systems use PCI devices soldered directly on the mother board,
in a fixed position (ethernet, Wi-Fi, serial ports, etc.). In this conditions it
is possible to refer to these PCI devices knowing their position on the PCI bus
topology.
To identify a PCI device, a complete hierarchical description is required, from
the chipset root port to the final device, through all the intermediate
bridges/switches of the board.
For example, let us assume to have a system with a PCIe serial port, an
Exar XR17V3521, soldered on the main board. This UART chip also includes
16 GPIOs and we want to add the property ``gpio-line-names`` [1] to these pins.
In this case, the ``lspci`` output for this component is::
07:00.0 Serial controller: Exar Corp. XR17V3521 Dual PCIe UART (rev 03)
The complete ``lspci`` output (manually reduced in length) is::
00:00.0 Host bridge: Intel Corp... Host Bridge (rev 0d)
...
00:13.0 PCI bridge: Intel Corp... PCI Express Port A #1 (rev fd)
00:13.1 PCI bridge: Intel Corp... PCI Express Port A #2 (rev fd)
00:13.2 PCI bridge: Intel Corp... PCI Express Port A #3 (rev fd)
00:14.0 PCI bridge: Intel Corp... PCI Express Port B #1 (rev fd)
00:14.1 PCI bridge: Intel Corp... PCI Express Port B #2 (rev fd)
...
05:00.0 PCI bridge: Pericom Semiconductor Device 2404 (rev 05)
06:01.0 PCI bridge: Pericom Semiconductor Device 2404 (rev 05)
06:02.0 PCI bridge: Pericom Semiconductor Device 2404 (rev 05)
06:03.0 PCI bridge: Pericom Semiconductor Device 2404 (rev 05)
07:00.0 Serial controller: Exar Corp. XR17V3521 Dual PCIe UART (rev 03) <-- Exar
...
The bus topology is::
-[0000:00]-+-00.0
...
+-13.0-[01]----00.0
+-13.1-[02]----00.0
+-13.2-[03]--
+-14.0-[04]----00.0
+-14.1-[05-09]----00.0-[06-09]--+-01.0-[07]----00.0 <-- Exar
| +-02.0-[08]----00.0
| \-03.0-[09]--
...
\-1f.1
To describe this Exar device on the PCI bus, we must start from the ACPI name
of the chipset bridge (also called "root port") with address::
Bus: 0 - Device: 14 - Function: 1
To find this information is necessary disassemble the BIOS ACPI tables, in
particular the DSDT (see also [2])::
mkdir ~/tables/
cd ~/tables/
acpidump > acpidump
acpixtract -a acpidump
iasl -e ssdt?.* -d dsdt.dat
Now, in the dsdt.dsl, we have to search the device whose address is related to
0x14 (device) and 0x01 (function). In this case we can find the following
device::
Scope (_SB.PCI0)
{
... other definitions follow ...
Device (RP02)
{
Method (_ADR, 0, NotSerialized) // _ADR: Address
{
If ((RPA2 != Zero))
{
Return (RPA2) /* \RPA2 */
}
Else
{
Return (0x00140001)
}
}
... other definitions follow ...
and the _ADR method [3] returns exactly the device/function couple that
we are looking for. With this information and analyzing the above ``lspci``
output (both the devices list and the devices tree), we can write the following
ACPI description for the Exar PCIe UART, also adding the list of its GPIO line
names::
Scope (_SB.PCI0.RP02)
{
Device (BRG1) //Bridge
{
Name (_ADR, 0x0000)
Device (BRG2) //Bridge
{
Name (_ADR, 0x00010000)
Device (EXAR)
{
Name (_ADR, 0x0000)
Name (_DSD, Package ()
{
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package ()
{
"gpio-line-names",
Package ()
{
"mode_232",
"mode_422",
"mode_485",
"misc_1",
"misc_2",
"misc_3",
"",
"",
"aux_1",
"aux_2",
"aux_3",
}
}
}
})
}
}
}
}
The location "_SB.PCI0.RP02" is obtained by the above investigation in the
dsdt.dsl table, whereas the device names "BRG1", "BRG2" and "EXAR" are
created analyzing the position of the Exar UART in the PCI bus topology.
References
==========
[1] Documentation/firmware-guide/acpi/gpio-properties.rst
[2] Documentation/admin-guide/acpi/initrd_table_override.rst
[3] ACPI Specifications, Version 6.3 - Paragraph 6.1.1 _ADR Address)
https://uefi.org/sites/default/files/resources/ACPI_6_3_May16.pdf,
referenced 2020-11-18

View File

@ -133,7 +133,61 @@ Example::
- gpio-line-names
Example::
The ``gpio-line-names`` declaration is a list of strings ("names"), which
describes each line/pin of a GPIO controller/expander. This list, contained in
a package, must be inserted inside the GPIO controller declaration of an ACPI
table (typically inside the DSDT). The ``gpio-line-names`` list must respect the
following rules (see also the examples):
- the first name in the list corresponds with the first line/pin of the GPIO
controller/expander
- the names inside the list must be consecutive (no "holes" are permitted)
- the list can be incomplete and can end before the last GPIO line: in
other words, it is not mandatory to fill all the GPIO lines
- empty names are allowed (two quotation marks ``""`` correspond to an empty
name)
Example of a GPIO controller of 16 lines, with an incomplete list with two
empty names::
Package () {
"gpio-line-names",
Package () {
"pin_0",
"pin_1",
"",
"",
"pin_3",
"pin_4_push_button",
}
}
At runtime, the above declaration produces the following result (using the
"libgpiod" tools)::
root@debian:~# gpioinfo gpiochip4
gpiochip4 - 16 lines:
line 0: "pin_0" unused input active-high
line 1: "pin_1" unused input active-high
line 2: unnamed unused input active-high
line 3: unnamed unused input active-high
line 4: "pin_3" unused input active-high
line 5: "pin_4_push_button" unused input active-high
line 6: unnamed unused input active-high
line 7 unnamed unused input active-high
line 8: unnamed unused input active-high
line 9: unnamed unused input active-high
line 10: unnamed unused input active-high
line 11: unnamed unused input active-high
line 12: unnamed unused input active-high
line 13: unnamed unused input active-high
line 14: unnamed unused input active-high
line 15: unnamed unused input active-high
root@debian:~# gpiofind pin_4_push_button
gpiochip4 5
root@debian:~#
Another example::
Package () {
"gpio-line-names",

View File

@ -151,11 +151,7 @@ void __init acpi_watchdog_init(void)
found = false;
resource_list_for_each_entry(rentry, &resource_list) {
if (rentry->res->flags == res.flags &&
resource_overlaps(rentry->res, &res)) {
if (res.start < rentry->res->start)
rentry->res->start = res.start;
if (res.end > rentry->res->end)
rentry->res->end = res.end;
resource_union(rentry->res, &res, rentry->res)) {
found = true;
break;
}

View File

@ -722,9 +722,7 @@ static void acpi_pci_root_validate_resources(struct device *dev,
* our resources no longer match the ACPI _CRS, but
* the kernel resource tree doesn't allow overlaps.
*/
if (resource_overlaps(res1, res2)) {
res2->start = min(res1->start, res2->start);
res2->end = max(res1->end, res2->end);
if (resource_union(res1, res2, res2)) {
dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n",
res2, res1);
free = true;

View File

@ -541,7 +541,7 @@ static acpi_status acpi_dev_process_resource(struct acpi_resource *ares,
ret = c->preproc(ares, c->preproc_data);
if (ret < 0) {
c->error = ret;
return AE_CTRL_TERMINATE;
return AE_ABORT_METHOD;
} else if (ret > 0) {
return AE_OK;
}

View File

@ -10,9 +10,10 @@
#define _LINUX_IOPORT_H
#ifndef __ASSEMBLY__
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/bits.h>
#include <linux/compiler.h>
#include <linux/minmax.h>
#include <linux/types.h>
/*
* Resources are tree-like, allowing
* nesting etc..
@ -229,6 +230,31 @@ static inline bool resource_contains(struct resource *r1, struct resource *r2)
return r1->start <= r2->start && r1->end >= r2->end;
}
/* True if any part of r1 overlaps r2 */
static inline bool resource_overlaps(struct resource *r1, struct resource *r2)
{
return r1->start <= r2->end && r1->end >= r2->start;
}
static inline bool
resource_intersection(struct resource *r1, struct resource *r2, struct resource *r)
{
if (!resource_overlaps(r1, r2))
return false;
r->start = max(r1->start, r2->start);
r->end = min(r1->end, r2->end);
return true;
}
static inline bool
resource_union(struct resource *r1, struct resource *r2, struct resource *r)
{
if (!resource_overlaps(r1, r2))
return false;
r->start = min(r1->start, r2->start);
r->end = max(r1->end, r2->end);
return true;
}
/* Convenience shorthand with allocation */
#define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0)
@ -296,12 +322,6 @@ extern int
walk_iomem_res_desc(unsigned long desc, unsigned long flags, u64 start, u64 end,
void *arg, int (*func)(struct resource *, void *));
/* True if any part of r1 overlaps r2 */
static inline bool resource_overlaps(struct resource *r1, struct resource *r2)
{
return (r1->start <= r2->end && r1->end >= r2->start);
}
struct resource *devm_request_free_mem_region(struct device *dev,
struct resource *base, unsigned long size);
struct resource *request_free_mem_region(struct resource *base,

View File

@ -123,6 +123,7 @@ obj-$(CONFIG_HAS_IOMEM) += iomem.o
obj-$(CONFIG_RSEQ) += rseq.o
obj-$(CONFIG_WATCH_QUEUE) += watch_queue.o
obj-$(CONFIG_RESOURCE_KUNIT_TEST) += resource_kunit.o
obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
CFLAGS_stackleak.o += $(DISABLE_STACKLEAK_PLUGIN)

View File

@ -557,13 +557,13 @@ int region_intersects(resource_size_t start, size_t size, unsigned long flags,
}
read_unlock(&resource_lock);
if (type == 0)
return REGION_DISJOINT;
if (other == 0)
return type ? REGION_INTERSECTS : REGION_DISJOINT;
return REGION_INTERSECTS;
if (type)
return REGION_MIXED;
return REGION_DISJOINT;
return REGION_MIXED;
}
EXPORT_SYMBOL_GPL(region_intersects);

152
kernel/resource_kunit.c Normal file
View File

@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Test cases for API provided by resource.c and ioport.h
*/
#include <kunit/test.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/string.h>
#define R0_START 0x0000
#define R0_END 0xffff
#define R1_START 0x1234
#define R1_END 0x2345
#define R2_START 0x4567
#define R2_END 0x5678
#define R3_START 0x6789
#define R3_END 0x789a
#define R4_START 0x2000
#define R4_END 0x7000
static struct resource r0 = { .start = R0_START, .end = R0_END };
static struct resource r1 = { .start = R1_START, .end = R1_END };
static struct resource r2 = { .start = R2_START, .end = R2_END };
static struct resource r3 = { .start = R3_START, .end = R3_END };
static struct resource r4 = { .start = R4_START, .end = R4_END };
struct result {
struct resource *r1;
struct resource *r2;
struct resource r;
bool ret;
};
static struct result results_for_union[] = {
{
.r1 = &r1, .r2 = &r0, .r.start = R0_START, .r.end = R0_END, .ret = true,
}, {
.r1 = &r2, .r2 = &r0, .r.start = R0_START, .r.end = R0_END, .ret = true,
}, {
.r1 = &r3, .r2 = &r0, .r.start = R0_START, .r.end = R0_END, .ret = true,
}, {
.r1 = &r4, .r2 = &r0, .r.start = R0_START, .r.end = R0_END, .ret = true,
}, {
.r1 = &r2, .r2 = &r1, .ret = false,
}, {
.r1 = &r3, .r2 = &r1, .ret = false,
}, {
.r1 = &r4, .r2 = &r1, .r.start = R1_START, .r.end = R4_END, .ret = true,
}, {
.r1 = &r2, .r2 = &r3, .ret = false,
}, {
.r1 = &r2, .r2 = &r4, .r.start = R4_START, .r.end = R4_END, .ret = true,
}, {
.r1 = &r3, .r2 = &r4, .r.start = R4_START, .r.end = R3_END, .ret = true,
},
};
static struct result results_for_intersection[] = {
{
.r1 = &r1, .r2 = &r0, .r.start = R1_START, .r.end = R1_END, .ret = true,
}, {
.r1 = &r2, .r2 = &r0, .r.start = R2_START, .r.end = R2_END, .ret = true,
}, {
.r1 = &r3, .r2 = &r0, .r.start = R3_START, .r.end = R3_END, .ret = true,
}, {
.r1 = &r4, .r2 = &r0, .r.start = R4_START, .r.end = R4_END, .ret = true,
}, {
.r1 = &r2, .r2 = &r1, .ret = false,
}, {
.r1 = &r3, .r2 = &r1, .ret = false,
}, {
.r1 = &r4, .r2 = &r1, .r.start = R4_START, .r.end = R1_END, .ret = true,
}, {
.r1 = &r2, .r2 = &r3, .ret = false,
}, {
.r1 = &r2, .r2 = &r4, .r.start = R2_START, .r.end = R2_END, .ret = true,
}, {
.r1 = &r3, .r2 = &r4, .r.start = R3_START, .r.end = R4_END, .ret = true,
},
};
static void resource_do_test(struct kunit *test, bool ret, struct resource *r,
bool exp_ret, struct resource *exp_r,
struct resource *r1, struct resource *r2)
{
KUNIT_EXPECT_EQ_MSG(test, ret, exp_ret, "Resources %pR %pR", r1, r2);
KUNIT_EXPECT_EQ_MSG(test, r->start, exp_r->start, "Start elements are not equal");
KUNIT_EXPECT_EQ_MSG(test, r->end, exp_r->end, "End elements are not equal");
}
static void resource_do_union_test(struct kunit *test, struct result *r)
{
struct resource result;
bool ret;
memset(&result, 0, sizeof(result));
ret = resource_union(r->r1, r->r2, &result);
resource_do_test(test, ret, &result, r->ret, &r->r, r->r1, r->r2);
memset(&result, 0, sizeof(result));
ret = resource_union(r->r2, r->r1, &result);
resource_do_test(test, ret, &result, r->ret, &r->r, r->r2, r->r1);
}
static void resource_test_union(struct kunit *test)
{
struct result *r = results_for_union;
unsigned int i = 0;
do {
resource_do_union_test(test, &r[i]);
} while (++i < ARRAY_SIZE(results_for_union));
}
static void resource_do_intersection_test(struct kunit *test, struct result *r)
{
struct resource result;
bool ret;
memset(&result, 0, sizeof(result));
ret = resource_intersection(r->r1, r->r2, &result);
resource_do_test(test, ret, &result, r->ret, &r->r, r->r1, r->r2);
memset(&result, 0, sizeof(result));
ret = resource_intersection(r->r2, r->r1, &result);
resource_do_test(test, ret, &result, r->ret, &r->r, r->r2, r->r1);
}
static void resource_test_intersection(struct kunit *test)
{
struct result *r = results_for_intersection;
unsigned int i = 0;
do {
resource_do_intersection_test(test, &r[i]);
} while (++i < ARRAY_SIZE(results_for_intersection));
}
static struct kunit_case resource_test_cases[] = {
KUNIT_CASE(resource_test_union),
KUNIT_CASE(resource_test_intersection),
{}
};
static struct kunit_suite resource_test_suite = {
.name = "resource",
.test_cases = resource_test_cases,
};
kunit_test_suite(resource_test_suite);
MODULE_LICENSE("GPL");

View File

@ -2226,6 +2226,17 @@ config BITFIELD_KUNIT
If unsure, say N.
config RESOURCE_KUNIT_TEST
tristate "KUnit test for resource API"
depends on KUNIT
help
This builds the resource API unit test.
Tests the logic of API provided by resource.c and ioport.h.
For more information on KUnit and unit tests in general please refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
If unsure, say N.
config SYSCTL_KUNIT_TEST
tristate "KUnit test for sysctl" if !KUNIT_ALL_TESTS
depends on KUNIT

View File

@ -22,17 +22,6 @@ void catpt_sram_free(struct resource *sram);
struct resource *
catpt_request_region(struct resource *root, resource_size_t size);
static inline bool catpt_resource_overlapping(struct resource *r1,
struct resource *r2,
struct resource *ret)
{
if (!resource_overlaps(r1, r2))
return false;
ret->start = max(r1->start, r2->start);
ret->end = min(r1->end, r2->end);
return true;
}
struct catpt_ipc_msg {
union {
u32 header;

View File

@ -267,7 +267,7 @@ static int catpt_restore_fwimage(struct catpt_dev *cdev,
r2.start = off;
r2.end = r2.start + info->size - 1;
if (!catpt_resource_overlapping(&r2, &r1, &common))
if (!resource_intersection(&r2, &r1, &common))
continue;
/* calculate start offset of common data area */
off = common.start - r1.start;