Skip to content

Commit

Permalink
serdev: Add ACPI devices by ResourceSource field
Browse files Browse the repository at this point in the history
When registering a serdev controller, ACPI needs to be checked for
devices attached to it. Currently, all immediate children of the ACPI
node of the controller are assumed to be UART client devices for this
controller. Furthermore, these devices are not searched elsewhere.

This is incorrect: Similar to SPI and I2C devices, the UART client
device definition (via UARTSerialBusV2) can reside anywhere in the ACPI
namespace as resource definition inside the _CRS method and points to
the controller via its ResourceSource field. This field may either
contain a fully qualified or relative path, indicating the controller
device. To address this, we need to walk over the whole ACPI namespace,
looking at each resource definition, and match the client device to the
controller via this field.

This patch is based on the existing acpi serial bus implementations in
drivers/i2c/i2c-core-acpi.c and drivers/spi/spi.c, specifically commit
4c3c595 ("spi/acpi: enumerate all SPI
slaves in the namespace").

Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  • Loading branch information
qzed committed Sep 28, 2019
1 parent 8d74ef9 commit 6a3fb10
Showing 1 changed file with 99 additions and 12 deletions.
111 changes: 99 additions & 12 deletions drivers/tty/serdev/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,16 +496,97 @@ static int of_serdev_register_devices(struct serdev_controller *ctrl)
}

#ifdef CONFIG_ACPI

#define SERDEV_ACPI_MAX_SCAN_DEPTH 32

struct acpi_serdev_lookup {
acpi_handle device_handle;
acpi_handle controller_handle;
int n;
int index;
};

static int acpi_serdev_parse_resource(struct acpi_resource *ares, void *data)
{
struct acpi_serdev_lookup *lookup = data;
struct acpi_resource_uart_serialbus *sb;
acpi_status status;

if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
return 1;

if (ares->data.common_serial_bus.type != ACPI_RESOURCE_SERIAL_TYPE_UART)
return 1;

if (lookup->index != -1 && lookup->n++ != lookup->index)
return 1;

sb = &ares->data.uart_serial_bus;

status = acpi_get_handle(lookup->device_handle,
sb->resource_source.string_ptr,
&lookup->controller_handle);
if (ACPI_FAILURE(status))
return 1;

/*
* NOTE: Ideally, we would also want to retreive other properties here,
* once setting them before opening the device is supported by serdev.
*/

return 1;
}

static int acpi_serdev_do_lookup(struct acpi_device *adev,
struct acpi_serdev_lookup *lookup)
{
struct list_head resource_list;
int ret;

lookup->device_handle = acpi_device_handle(adev);
lookup->controller_handle = NULL;
lookup->n = 0;

INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
acpi_serdev_parse_resource, lookup);
acpi_dev_free_resource_list(&resource_list);

if (ret < 0)
return -EINVAL;

return 0;
}

static int acpi_serdev_check_resources(struct serdev_controller *ctrl,
struct acpi_device *adev)
{
struct acpi_serdev_lookup lookup;
int ret;

if (acpi_bus_get_status(adev) || !adev->status.present)
return -EINVAL;

/* Look for UARTSerialBusV2 resource */
lookup.index = -1; // we only care for the last device

ret = acpi_serdev_do_lookup(adev, &lookup);
if (ret)
return ret;

/* Make sure controller and ResourceSource handle match */
if (ACPI_HANDLE(ctrl->dev.parent) != lookup.controller_handle)
return -ENODEV;

return 0;
}

static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl,
struct acpi_device *adev)
struct acpi_device *adev)
{
struct serdev_device *serdev = NULL;
struct serdev_device *serdev;
int err;

if (acpi_bus_get_status(adev) || !adev->status.present ||
acpi_device_enumerated(adev))
return AE_OK;

serdev = serdev_device_alloc(ctrl);
if (!serdev) {
dev_err(&ctrl->dev, "failed to allocate serdev device for %s\n",
Expand All @@ -527,30 +608,36 @@ static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl,
}

static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level,
void *data, void **return_value)
void *data, void **return_value)
{
struct serdev_controller *ctrl = data;
struct acpi_device *adev;

if (acpi_bus_get_device(handle, &adev))
return AE_OK;

if (acpi_device_enumerated(adev))
return AE_OK;

if (acpi_serdev_check_resources(ctrl, adev))
return AE_OK;

return acpi_serdev_register_device(ctrl, adev);
}


static int acpi_serdev_register_devices(struct serdev_controller *ctrl)
{
acpi_status status;
acpi_handle handle;

handle = ACPI_HANDLE(ctrl->dev.parent);
if (!handle)
if (!has_acpi_companion(ctrl->dev.parent))
return -ENODEV;

status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
SERDEV_ACPI_MAX_SCAN_DEPTH,
acpi_serdev_add_device, NULL, ctrl, NULL);
if (ACPI_FAILURE(status))
dev_dbg(&ctrl->dev, "failed to enumerate serdev slaves\n");
dev_warn(&ctrl->dev, "failed to enumerate serdev slaves\n");

if (!ctrl->serdev)
return -ENODEV;
Expand Down

0 comments on commit 6a3fb10

Please sign in to comment.