| /* |
| * ACPI I2C enumeration support |
| * |
| * Copyright (C) 2012, Intel Corporation |
| * Author: Mika Westerberg <mika.westerberg@linux.intel.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <linux/acpi.h> |
| #include <linux/device.h> |
| #include <linux/export.h> |
| #include <linux/i2c.h> |
| #include <linux/ioport.h> |
| |
| ACPI_MODULE_NAME("i2c"); |
| |
| static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data) |
| { |
| struct i2c_board_info *info = data; |
| |
| if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { |
| struct acpi_resource_i2c_serialbus *sb; |
| |
| sb = &ares->data.i2c_serial_bus; |
| if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { |
| info->addr = sb->slave_address; |
| if (sb->access_mode == ACPI_I2C_10BIT_MODE) |
| info->flags |= I2C_CLIENT_TEN; |
| } |
| } else if (info->irq < 0) { |
| struct resource r; |
| |
| if (acpi_dev_resource_interrupt(ares, 0, &r)) |
| info->irq = r.start; |
| } |
| |
| /* Tell the ACPI core to skip this resource */ |
| return 1; |
| } |
| |
| static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, |
| void *data, void **return_value) |
| { |
| struct i2c_adapter *adapter = data; |
| struct list_head resource_list; |
| struct i2c_board_info info; |
| struct acpi_device *adev; |
| int ret; |
| |
| if (acpi_bus_get_device(handle, &adev)) |
| return AE_OK; |
| if (acpi_bus_get_status(adev) || !adev->status.present) |
| return AE_OK; |
| |
| memset(&info, 0, sizeof(info)); |
| info.acpi_node.handle = handle; |
| info.irq = -1; |
| |
| INIT_LIST_HEAD(&resource_list); |
| ret = acpi_dev_get_resources(adev, &resource_list, |
| acpi_i2c_add_resource, &info); |
| acpi_dev_free_resource_list(&resource_list); |
| |
| if (ret < 0 || !info.addr) |
| return AE_OK; |
| |
| strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); |
| if (!i2c_new_device(adapter, &info)) { |
| dev_err(&adapter->dev, |
| "failed to add I2C device %s from ACPI\n", |
| dev_name(&adev->dev)); |
| } |
| |
| return AE_OK; |
| } |
| |
| /** |
| * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter |
| * @adapter: pointer to adapter |
| * |
| * Enumerate all I2C slave devices behind this adapter by walking the ACPI |
| * namespace. When a device is found it will be added to the Linux device |
| * model and bound to the corresponding ACPI handle. |
| */ |
| void acpi_i2c_register_devices(struct i2c_adapter *adapter) |
| { |
| acpi_handle handle; |
| acpi_status status; |
| |
| handle = ACPI_HANDLE(adapter->dev.parent); |
| if (!handle) |
| return; |
| |
| status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, |
| acpi_i2c_add_device, NULL, |
| adapter, NULL); |
| if (ACPI_FAILURE(status)) |
| dev_warn(&adapter->dev, "failed to enumerate I2C slaves\n"); |
| } |
| EXPORT_SYMBOL_GPL(acpi_i2c_register_devices); |