Commit 64bee4d2 authored by Mika Westerberg's avatar Mika Westerberg Committed by Rafael J. Wysocki

spi / ACPI: add ACPI enumeration support

ACPI 5 introduced SPISerialBus resource that allows us to enumerate and
configure the SPI slave devices behind the SPI controller. This patch adds
support for this to the SPI core.

In addition we bind ACPI nodes to SPI devices. This makes it possible for
the slave drivers to get the ACPI handle for further configuration.
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: default avatarGrant Likely <grant.likely@secretlab.ca>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent e29482e8
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/ioport.h>
#include <linux/acpi.h>
static void spidev_release(struct device *dev) static void spidev_release(struct device *dev)
{ {
...@@ -93,6 +95,10 @@ static int spi_match_device(struct device *dev, struct device_driver *drv) ...@@ -93,6 +95,10 @@ static int spi_match_device(struct device *dev, struct device_driver *drv)
if (of_driver_match_device(dev, drv)) if (of_driver_match_device(dev, drv))
return 1; return 1;
/* Then try ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;
if (sdrv->id_table) if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi); return !!spi_match_id(sdrv->id_table, spi);
...@@ -888,6 +894,100 @@ static void of_register_spi_devices(struct spi_master *master) ...@@ -888,6 +894,100 @@ static void of_register_spi_devices(struct spi_master *master)
static void of_register_spi_devices(struct spi_master *master) { } static void of_register_spi_devices(struct spi_master *master) { }
#endif #endif
#ifdef CONFIG_ACPI
static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
{
struct spi_device *spi = data;
if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
struct acpi_resource_spi_serialbus *sb;
sb = &ares->data.spi_serial_bus;
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) {
spi->chip_select = sb->device_selection;
spi->max_speed_hz = sb->connection_speed;
if (sb->clock_phase == ACPI_SPI_SECOND_PHASE)
spi->mode |= SPI_CPHA;
if (sb->clock_polarity == ACPI_SPI_START_HIGH)
spi->mode |= SPI_CPOL;
if (sb->device_polarity == ACPI_SPI_ACTIVE_HIGH)
spi->mode |= SPI_CS_HIGH;
}
} else if (spi->irq < 0) {
struct resource r;
if (acpi_dev_resource_interrupt(ares, 0, &r))
spi->irq = r.start;
}
/* Always tell the ACPI core to skip this resource */
return 1;
}
static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct spi_master *master = data;
struct list_head resource_list;
struct acpi_device *adev;
struct spi_device *spi;
int ret;
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
if (acpi_bus_get_status(adev) || !adev->status.present)
return AE_OK;
spi = spi_alloc_device(master);
if (!spi) {
dev_err(&master->dev, "failed to allocate SPI device for %s\n",
dev_name(&adev->dev));
return AE_NO_MEMORY;
}
ACPI_HANDLE_SET(&spi->dev, handle);
spi->irq = -1;
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
acpi_spi_add_resource, spi);
acpi_dev_free_resource_list(&resource_list);
if (ret < 0 || !spi->max_speed_hz) {
spi_dev_put(spi);
return AE_OK;
}
strlcpy(spi->modalias, dev_name(&adev->dev), sizeof(spi->modalias));
if (spi_add_device(spi)) {
dev_err(&master->dev, "failed to add SPI device %s from ACPI\n",
dev_name(&adev->dev));
spi_dev_put(spi);
}
return AE_OK;
}
static void acpi_register_spi_devices(struct spi_master *master)
{
acpi_status status;
acpi_handle handle;
handle = ACPI_HANDLE(&master->dev);
if (!handle)
return;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
acpi_spi_add_device, NULL,
master, NULL);
if (ACPI_FAILURE(status))
dev_warn(&master->dev, "failed to enumerate SPI slaves\n");
}
#else
static inline void acpi_register_spi_devices(struct spi_master *master) {}
#endif /* CONFIG_ACPI */
static void spi_master_release(struct device *dev) static void spi_master_release(struct device *dev)
{ {
struct spi_master *master; struct spi_master *master;
...@@ -1023,8 +1123,9 @@ int spi_register_master(struct spi_master *master) ...@@ -1023,8 +1123,9 @@ int spi_register_master(struct spi_master *master)
spi_match_master_to_boardinfo(master, &bi->board_info); spi_match_master_to_boardinfo(master, &bi->board_info);
mutex_unlock(&board_lock); mutex_unlock(&board_lock);
/* Register devices from the device tree */ /* Register devices from the device tree and ACPI */
of_register_spi_devices(master); of_register_spi_devices(master);
acpi_register_spi_devices(master);
done: done:
return status; return status;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment