259 lines
8.6 KiB
Diff
259 lines
8.6 KiB
Diff
From 3fb5bcdbcd9ceaf3d8d69f8ba5a20c692c3ba105 Mon Sep 17 00:00:00 2001
|
|
From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Thu, 30 May 2019 13:16:34 +0200
|
|
Subject: [PATCH 22/39] spi/acpi: enumerate all SPI slaves in the namespace
|
|
|
|
mainline inclusion
|
|
from mainline-v5.3-rc1
|
|
commit 4c3c59544f33e97cf8557f27e05a9904ead16363
|
|
category: bugfix
|
|
bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP
|
|
CVE: NA
|
|
|
|
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4c3c59544f33e97cf8557f27e05a9904ead16363
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
Currently, the ACPI enumeration that takes place when registering a
|
|
SPI master only considers immediate child devices in the ACPI namespace,
|
|
rather than checking the ResourceSource field in the SpiSerialBus()
|
|
resource descriptor.
|
|
|
|
This is incorrect: SPI slaves could reside anywhere in the ACPI
|
|
namespace, and so we should enumerate the entire namespace and look for
|
|
any device that refers to the newly registered SPI master in its
|
|
resource descriptor.
|
|
|
|
So refactor the existing code and use a lookup structure so that
|
|
allocating the SPI device structure is deferred until we have identified
|
|
the device as an actual child of the controller. This approach is
|
|
loosely based on the way the I2C subsystem handles ACPI enumeration.
|
|
|
|
Note that Apple x86 hardware does not rely on SpiSerialBus() resources
|
|
in _CRS but uses nested devices below the controller's device node in
|
|
the ACPI namespace, with a special set of device properties. This means
|
|
we have to take care to only parse those properties for device nodes
|
|
that are direct children of the controller node.
|
|
|
|
Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Cc: linux-spi@vger.kernel.org
|
|
Cc: broonie@kernel.org
|
|
Cc: andy.shevchenko@gmail.com
|
|
Cc: masahisa.kojima@linaro.org
|
|
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
|
|
Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
|
|
Cc: linux-acpi@vger.kernel.org
|
|
Cc: Lukas Wunner <lukas@wunner.de>
|
|
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Signed-off-by: Mark Brown <broonie@kernel.org>
|
|
Signed-off-by: YunYi Yang <yangyunyi2@huawei.com>
|
|
---
|
|
drivers/spi/spi.c | 104 ++++++++++++++++++++++++++++++++--------------
|
|
1 file changed, 73 insertions(+), 31 deletions(-)
|
|
|
|
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
|
|
index 08c7c8ee3271..c6e272edd806 100644
|
|
--- a/drivers/spi/spi.c
|
|
+++ b/drivers/spi/spi.c
|
|
@@ -1732,9 +1732,18 @@ static void of_register_spi_devices(struct spi_controller *ctlr) { }
|
|
#endif
|
|
|
|
#ifdef CONFIG_ACPI
|
|
-static void acpi_spi_parse_apple_properties(struct spi_device *spi)
|
|
+struct acpi_spi_lookup {
|
|
+ struct spi_controller *ctlr;
|
|
+ u32 max_speed_hz;
|
|
+ u32 mode;
|
|
+ int irq;
|
|
+ u8 bits_per_word;
|
|
+ u8 chip_select;
|
|
+};
|
|
+
|
|
+static void acpi_spi_parse_apple_properties(struct acpi_device *dev,
|
|
+ struct acpi_spi_lookup *lookup)
|
|
{
|
|
- struct acpi_device *dev = ACPI_COMPANION(&spi->dev);
|
|
const union acpi_object *obj;
|
|
|
|
if (!x86_apple_machine)
|
|
@@ -1742,35 +1751,47 @@ static void acpi_spi_parse_apple_properties(struct spi_device *spi)
|
|
|
|
if (!acpi_dev_get_property(dev, "spiSclkPeriod", ACPI_TYPE_BUFFER, &obj)
|
|
&& obj->buffer.length >= 4)
|
|
- spi->max_speed_hz = NSEC_PER_SEC / *(u32 *)obj->buffer.pointer;
|
|
+ lookup->max_speed_hz = NSEC_PER_SEC /
|
|
+ *(u32 *)obj->buffer.pointer;
|
|
|
|
if (!acpi_dev_get_property(dev, "spiWordSize", ACPI_TYPE_BUFFER, &obj)
|
|
&& obj->buffer.length == 8)
|
|
- spi->bits_per_word = *(u64 *)obj->buffer.pointer;
|
|
+ lookup->bits_per_word = *(u64 *)obj->buffer.pointer;
|
|
|
|
if (!acpi_dev_get_property(dev, "spiBitOrder", ACPI_TYPE_BUFFER, &obj)
|
|
&& obj->buffer.length == 8 && !*(u64 *)obj->buffer.pointer)
|
|
- spi->mode |= SPI_LSB_FIRST;
|
|
+ lookup->mode |= SPI_LSB_FIRST;
|
|
|
|
if (!acpi_dev_get_property(dev, "spiSPO", ACPI_TYPE_BUFFER, &obj)
|
|
&& obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer)
|
|
- spi->mode |= SPI_CPOL;
|
|
+ lookup->mode |= SPI_CPOL;
|
|
|
|
if (!acpi_dev_get_property(dev, "spiSPH", ACPI_TYPE_BUFFER, &obj)
|
|
&& obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer)
|
|
- spi->mode |= SPI_CPHA;
|
|
+ lookup->mode |= SPI_CPHA;
|
|
}
|
|
|
|
static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
|
|
{
|
|
- struct spi_device *spi = data;
|
|
- struct spi_controller *ctlr = spi->controller;
|
|
+ struct acpi_spi_lookup *lookup = data;
|
|
+ struct spi_controller *ctlr = lookup->ctlr;
|
|
|
|
if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
|
struct acpi_resource_spi_serialbus *sb;
|
|
+ acpi_handle parent_handle;
|
|
+ acpi_status status;
|
|
|
|
sb = &ares->data.spi_serial_bus;
|
|
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) {
|
|
+
|
|
+ status = acpi_get_handle(NULL,
|
|
+ sb->resource_source.string_ptr,
|
|
+ &parent_handle);
|
|
+
|
|
+ if (!status ||
|
|
+ ACPI_HANDLE(ctlr->dev.parent) != parent_handle)
|
|
+ return -ENODEV;
|
|
+
|
|
/*
|
|
* ACPI DeviceSelection numbering is handled by the
|
|
* host controller driver in Windows and can vary
|
|
@@ -1783,25 +1804,25 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
|
|
sb->device_selection);
|
|
if (cs < 0)
|
|
return cs;
|
|
- spi->chip_select = cs;
|
|
+ lookup->chip_select = cs;
|
|
} else {
|
|
- spi->chip_select = sb->device_selection;
|
|
+ lookup->chip_select = sb->device_selection;
|
|
}
|
|
|
|
- spi->max_speed_hz = sb->connection_speed;
|
|
+ lookup->max_speed_hz = sb->connection_speed;
|
|
|
|
if (sb->clock_phase == ACPI_SPI_SECOND_PHASE)
|
|
- spi->mode |= SPI_CPHA;
|
|
+ lookup->mode |= SPI_CPHA;
|
|
if (sb->clock_polarity == ACPI_SPI_START_HIGH)
|
|
- spi->mode |= SPI_CPOL;
|
|
+ lookup->mode |= SPI_CPOL;
|
|
if (sb->device_polarity == ACPI_SPI_ACTIVE_HIGH)
|
|
- spi->mode |= SPI_CS_HIGH;
|
|
+ lookup->mode |= SPI_CS_HIGH;
|
|
}
|
|
- } else if (spi->irq < 0) {
|
|
+ } else if (lookup->irq < 0) {
|
|
struct resource r;
|
|
|
|
if (acpi_dev_resource_interrupt(ares, 0, &r))
|
|
- spi->irq = r.start;
|
|
+ lookup->irq = r.start;
|
|
}
|
|
|
|
/* Always tell the ACPI core to skip this resource */
|
|
@@ -1811,7 +1832,9 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
|
|
static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
|
|
struct acpi_device *adev)
|
|
{
|
|
+ acpi_handle parent_handle = NULL;
|
|
struct list_head resource_list;
|
|
+ struct acpi_spi_lookup lookup;
|
|
struct spi_device *spi;
|
|
int ret;
|
|
|
|
@@ -1819,28 +1842,44 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
|
|
acpi_device_enumerated(adev))
|
|
return AE_OK;
|
|
|
|
- spi = spi_alloc_device(ctlr);
|
|
- if (!spi) {
|
|
- dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n",
|
|
- dev_name(&adev->dev));
|
|
- return AE_NO_MEMORY;
|
|
- }
|
|
-
|
|
- ACPI_COMPANION_SET(&spi->dev, adev);
|
|
- spi->irq = -1;
|
|
+ lookup.ctlr = ctlr;
|
|
+ lookup.mode = 0;
|
|
+ lookup.bits_per_word = 0;
|
|
+ lookup.irq = -1;
|
|
|
|
INIT_LIST_HEAD(&resource_list);
|
|
ret = acpi_dev_get_resources(adev, &resource_list,
|
|
- acpi_spi_add_resource, spi);
|
|
+ acpi_spi_add_resource, &lookup);
|
|
acpi_dev_free_resource_list(&resource_list);
|
|
|
|
- acpi_spi_parse_apple_properties(spi);
|
|
+ if (ret < 0)
|
|
+ /* found SPI in _CRS but it points to another controller */
|
|
+ return AE_OK;
|
|
|
|
- if (ret < 0 || !spi->max_speed_hz) {
|
|
- spi_dev_put(spi);
|
|
+ if (!lookup.max_speed_hz &&
|
|
+ !ACPI_FAILURE(acpi_get_parent(adev->handle, &parent_handle)) &&
|
|
+ ACPI_HANDLE(ctlr->dev.parent) == parent_handle) {
|
|
+ /* Apple does not use _CRS but nested devices for SPI slaves */
|
|
+ acpi_spi_parse_apple_properties(adev, &lookup);
|
|
+ }
|
|
+
|
|
+ if (!lookup.max_speed_hz)
|
|
return AE_OK;
|
|
+
|
|
+ spi = spi_alloc_device(ctlr);
|
|
+ if (!spi) {
|
|
+ dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n",
|
|
+ dev_name(&adev->dev));
|
|
+ return AE_NO_MEMORY;
|
|
}
|
|
|
|
+ ACPI_COMPANION_SET(&spi->dev, adev);
|
|
+ spi->max_speed_hz = lookup.max_speed_hz;
|
|
+ spi->mode = lookup.mode;
|
|
+ spi->irq = lookup.irq;
|
|
+ spi->bits_per_word = lookup.bits_per_word;
|
|
+ spi->chip_select = lookup.chip_select;
|
|
+
|
|
acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias,
|
|
sizeof(spi->modalias));
|
|
|
|
@@ -1872,6 +1911,8 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
|
|
return acpi_register_spi_device(ctlr, adev);
|
|
}
|
|
|
|
+#define SPI_ACPI_ENUMERATE_MAX_DEPTH 32
|
|
+
|
|
static void acpi_register_spi_devices(struct spi_controller *ctlr)
|
|
{
|
|
acpi_status status;
|
|
@@ -1881,7 +1922,8 @@ static void acpi_register_spi_devices(struct spi_controller *ctlr)
|
|
if (!handle)
|
|
return;
|
|
|
|
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
|
|
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
|
+ SPI_ACPI_ENUMERATE_MAX_DEPTH,
|
|
acpi_spi_add_device, NULL, ctlr, NULL);
|
|
if (ACPI_FAILURE(status))
|
|
dev_warn(&ctlr->dev, "failed to enumerate SPI slaves\n");
|
|
--
|
|
2.27.0
|
|
|