18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux I2C core ACPI support code 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Intel Corp, Author: Lan Tianyu <tianyu.lan@intel.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/acpi.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/list.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "i2c-core.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct i2c_acpi_handler_data { 198c2ecf20Sopenharmony_ci struct acpi_connection_info info; 208c2ecf20Sopenharmony_ci struct i2c_adapter *adapter; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct gsb_buffer { 248c2ecf20Sopenharmony_ci u8 status; 258c2ecf20Sopenharmony_ci u8 len; 268c2ecf20Sopenharmony_ci union { 278c2ecf20Sopenharmony_ci u16 wdata; 288c2ecf20Sopenharmony_ci u8 bdata; 298c2ecf20Sopenharmony_ci u8 data[0]; 308c2ecf20Sopenharmony_ci }; 318c2ecf20Sopenharmony_ci} __packed; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct i2c_acpi_lookup { 348c2ecf20Sopenharmony_ci struct i2c_board_info *info; 358c2ecf20Sopenharmony_ci acpi_handle adapter_handle; 368c2ecf20Sopenharmony_ci acpi_handle device_handle; 378c2ecf20Sopenharmony_ci acpi_handle search_handle; 388c2ecf20Sopenharmony_ci int n; 398c2ecf20Sopenharmony_ci int index; 408c2ecf20Sopenharmony_ci u32 speed; 418c2ecf20Sopenharmony_ci u32 min_speed; 428c2ecf20Sopenharmony_ci u32 force_speed; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/** 468c2ecf20Sopenharmony_ci * i2c_acpi_get_i2c_resource - Gets I2cSerialBus resource if type matches 478c2ecf20Sopenharmony_ci * @ares: ACPI resource 488c2ecf20Sopenharmony_ci * @i2c: Pointer to I2cSerialBus resource will be returned here 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * Checks if the given ACPI resource is of type I2cSerialBus. 518c2ecf20Sopenharmony_ci * In this case, returns a pointer to it to the caller. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Returns true if resource type is of I2cSerialBus, otherwise false. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_cibool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, 568c2ecf20Sopenharmony_ci struct acpi_resource_i2c_serialbus **i2c) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct acpi_resource_i2c_serialbus *sb; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) 618c2ecf20Sopenharmony_ci return false; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci sb = &ares->data.i2c_serial_bus; 648c2ecf20Sopenharmony_ci if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) 658c2ecf20Sopenharmony_ci return false; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci *i2c = sb; 688c2ecf20Sopenharmony_ci return true; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_acpi_get_i2c_resource); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int i2c_acpi_fill_info(struct acpi_resource *ares, void *data) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct i2c_acpi_lookup *lookup = data; 758c2ecf20Sopenharmony_ci struct i2c_board_info *info = lookup->info; 768c2ecf20Sopenharmony_ci struct acpi_resource_i2c_serialbus *sb; 778c2ecf20Sopenharmony_ci acpi_status status; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (info->addr || !i2c_acpi_get_i2c_resource(ares, &sb)) 808c2ecf20Sopenharmony_ci return 1; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (lookup->index != -1 && lookup->n++ != lookup->index) 838c2ecf20Sopenharmony_ci return 1; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci status = acpi_get_handle(lookup->device_handle, 868c2ecf20Sopenharmony_ci sb->resource_source.string_ptr, 878c2ecf20Sopenharmony_ci &lookup->adapter_handle); 888c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 898c2ecf20Sopenharmony_ci return 1; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci info->addr = sb->slave_address; 928c2ecf20Sopenharmony_ci lookup->speed = sb->connection_speed; 938c2ecf20Sopenharmony_ci if (sb->access_mode == ACPI_I2C_10BIT_MODE) 948c2ecf20Sopenharmony_ci info->flags |= I2C_CLIENT_TEN; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 1; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct acpi_device_id i2c_acpi_ignored_device_ids[] = { 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * ACPI video acpi_devices, which are handled by the acpi-video driver 1028c2ecf20Sopenharmony_ci * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci { ACPI_VIDEO_HID, 0 }, 1058c2ecf20Sopenharmony_ci {} 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int i2c_acpi_do_lookup(struct acpi_device *adev, 1098c2ecf20Sopenharmony_ci struct i2c_acpi_lookup *lookup) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct i2c_board_info *info = lookup->info; 1128c2ecf20Sopenharmony_ci struct list_head resource_list; 1138c2ecf20Sopenharmony_ci int ret; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (acpi_bus_get_status(adev) || !adev->status.present) 1168c2ecf20Sopenharmony_ci return -EINVAL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0) 1198c2ecf20Sopenharmony_ci return -ENODEV; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci memset(info, 0, sizeof(*info)); 1228c2ecf20Sopenharmony_ci lookup->device_handle = acpi_device_handle(adev); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Look up for I2cSerialBus resource */ 1258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&resource_list); 1268c2ecf20Sopenharmony_ci ret = acpi_dev_get_resources(adev, &resource_list, 1278c2ecf20Sopenharmony_ci i2c_acpi_fill_info, lookup); 1288c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (ret < 0 || !info->addr) 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int i2c_acpi_add_resource(struct acpi_resource *ares, void *data) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci int *irq = data; 1398c2ecf20Sopenharmony_ci struct resource r; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (*irq <= 0 && acpi_dev_resource_interrupt(ares, 0, &r)) 1428c2ecf20Sopenharmony_ci *irq = i2c_dev_irq_from_resources(&r, 1); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 1; /* No need to add resource to the list */ 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/** 1488c2ecf20Sopenharmony_ci * i2c_acpi_get_irq - get device IRQ number from ACPI 1498c2ecf20Sopenharmony_ci * @client: Pointer to the I2C client device 1508c2ecf20Sopenharmony_ci * 1518c2ecf20Sopenharmony_ci * Find the IRQ number used by a specific client device. 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci * Return: The IRQ number or an error code. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ciint i2c_acpi_get_irq(struct i2c_client *client) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(&client->dev); 1588c2ecf20Sopenharmony_ci struct list_head resource_list; 1598c2ecf20Sopenharmony_ci int irq = -ENOENT; 1608c2ecf20Sopenharmony_ci int ret; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&resource_list); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = acpi_dev_get_resources(adev, &resource_list, 1658c2ecf20Sopenharmony_ci i2c_acpi_add_resource, &irq); 1668c2ecf20Sopenharmony_ci if (ret < 0) 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (irq == -ENOENT) 1728c2ecf20Sopenharmony_ci irq = acpi_dev_gpio_irq_get(adev, 0); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return irq; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int i2c_acpi_get_info(struct acpi_device *adev, 1788c2ecf20Sopenharmony_ci struct i2c_board_info *info, 1798c2ecf20Sopenharmony_ci struct i2c_adapter *adapter, 1808c2ecf20Sopenharmony_ci acpi_handle *adapter_handle) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct i2c_acpi_lookup lookup; 1838c2ecf20Sopenharmony_ci int ret; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci memset(&lookup, 0, sizeof(lookup)); 1868c2ecf20Sopenharmony_ci lookup.info = info; 1878c2ecf20Sopenharmony_ci lookup.index = -1; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (acpi_device_enumerated(adev)) 1908c2ecf20Sopenharmony_ci return -EINVAL; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ret = i2c_acpi_do_lookup(adev, &lookup); 1938c2ecf20Sopenharmony_ci if (ret) 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (adapter) { 1978c2ecf20Sopenharmony_ci /* The adapter must match the one in I2cSerialBus() connector */ 1988c2ecf20Sopenharmony_ci if (ACPI_HANDLE(&adapter->dev) != lookup.adapter_handle) 1998c2ecf20Sopenharmony_ci return -ENODEV; 2008c2ecf20Sopenharmony_ci } else { 2018c2ecf20Sopenharmony_ci struct acpi_device *adapter_adev; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* The adapter must be present */ 2048c2ecf20Sopenharmony_ci if (acpi_bus_get_device(lookup.adapter_handle, &adapter_adev)) 2058c2ecf20Sopenharmony_ci return -ENODEV; 2068c2ecf20Sopenharmony_ci if (acpi_bus_get_status(adapter_adev) || 2078c2ecf20Sopenharmony_ci !adapter_adev->status.present) 2088c2ecf20Sopenharmony_ci return -ENODEV; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci info->fwnode = acpi_fwnode_handle(adev); 2128c2ecf20Sopenharmony_ci if (adapter_handle) 2138c2ecf20Sopenharmony_ci *adapter_handle = lookup.adapter_handle; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci acpi_set_modalias(adev, dev_name(&adev->dev), info->type, 2168c2ecf20Sopenharmony_ci sizeof(info->type)); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void i2c_acpi_register_device(struct i2c_adapter *adapter, 2228c2ecf20Sopenharmony_ci struct acpi_device *adev, 2238c2ecf20Sopenharmony_ci struct i2c_board_info *info) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci adev->power.flags.ignore_parent = true; 2268c2ecf20Sopenharmony_ci acpi_device_set_enumerated(adev); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (IS_ERR(i2c_new_client_device(adapter, info))) { 2298c2ecf20Sopenharmony_ci adev->power.flags.ignore_parent = false; 2308c2ecf20Sopenharmony_ci dev_err(&adapter->dev, 2318c2ecf20Sopenharmony_ci "failed to add I2C device %s from ACPI\n", 2328c2ecf20Sopenharmony_ci dev_name(&adev->dev)); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, 2378c2ecf20Sopenharmony_ci void *data, void **return_value) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = data; 2408c2ecf20Sopenharmony_ci struct acpi_device *adev; 2418c2ecf20Sopenharmony_ci struct i2c_board_info info; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (acpi_bus_get_device(handle, &adev)) 2448c2ecf20Sopenharmony_ci return AE_OK; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (i2c_acpi_get_info(adev, &info, adapter, NULL)) 2478c2ecf20Sopenharmony_ci return AE_OK; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci i2c_acpi_register_device(adapter, adev, &info); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return AE_OK; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#define I2C_ACPI_MAX_SCAN_DEPTH 32 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/** 2578c2ecf20Sopenharmony_ci * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter 2588c2ecf20Sopenharmony_ci * @adap: pointer to adapter 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * Enumerate all I2C slave devices behind this adapter by walking the ACPI 2618c2ecf20Sopenharmony_ci * namespace. When a device is found it will be added to the Linux device 2628c2ecf20Sopenharmony_ci * model and bound to the corresponding ACPI handle. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_civoid i2c_acpi_register_devices(struct i2c_adapter *adap) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci acpi_status status; 2678c2ecf20Sopenharmony_ci acpi_handle handle; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (!has_acpi_companion(&adap->dev)) 2708c2ecf20Sopenharmony_ci return; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 2738c2ecf20Sopenharmony_ci I2C_ACPI_MAX_SCAN_DEPTH, 2748c2ecf20Sopenharmony_ci i2c_acpi_add_device, NULL, 2758c2ecf20Sopenharmony_ci adap, NULL); 2768c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 2778c2ecf20Sopenharmony_ci dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (!adap->dev.parent) 2808c2ecf20Sopenharmony_ci return; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(adap->dev.parent); 2838c2ecf20Sopenharmony_ci if (!handle) 2848c2ecf20Sopenharmony_ci return; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci acpi_walk_dep_device_list(handle); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = { 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * These Silead touchscreen controllers only work at 400KHz, for 2928c2ecf20Sopenharmony_ci * some reason they do not work at 100KHz. On some devices the ACPI 2938c2ecf20Sopenharmony_ci * tables list another device at their bus as only being capable 2948c2ecf20Sopenharmony_ci * of 100KHz, testing has shown that these other devices work fine 2958c2ecf20Sopenharmony_ci * at 400KHz (as can be expected of any recent i2c hw) so we force 2968c2ecf20Sopenharmony_ci * the speed of the bus to 400 KHz if a Silead device is present. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci { "MSSL1680", 0 }, 2998c2ecf20Sopenharmony_ci {} 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level, 3038c2ecf20Sopenharmony_ci void *data, void **return_value) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct i2c_acpi_lookup *lookup = data; 3068c2ecf20Sopenharmony_ci struct acpi_device *adev; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (acpi_bus_get_device(handle, &adev)) 3098c2ecf20Sopenharmony_ci return AE_OK; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (i2c_acpi_do_lookup(adev, lookup)) 3128c2ecf20Sopenharmony_ci return AE_OK; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (lookup->search_handle != lookup->adapter_handle) 3158c2ecf20Sopenharmony_ci return AE_OK; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (lookup->speed <= lookup->min_speed) 3188c2ecf20Sopenharmony_ci lookup->min_speed = lookup->speed; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (acpi_match_device_ids(adev, i2c_acpi_force_400khz_device_ids) == 0) 3218c2ecf20Sopenharmony_ci lookup->force_speed = I2C_MAX_FAST_MODE_FREQ; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return AE_OK; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/** 3278c2ecf20Sopenharmony_ci * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI 3288c2ecf20Sopenharmony_ci * @dev: The device owning the bus 3298c2ecf20Sopenharmony_ci * 3308c2ecf20Sopenharmony_ci * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves 3318c2ecf20Sopenharmony_ci * devices connected to this bus and use the speed of slowest device. 3328c2ecf20Sopenharmony_ci * 3338c2ecf20Sopenharmony_ci * Returns the speed in Hz or zero 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ciu32 i2c_acpi_find_bus_speed(struct device *dev) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct i2c_acpi_lookup lookup; 3388c2ecf20Sopenharmony_ci struct i2c_board_info dummy; 3398c2ecf20Sopenharmony_ci acpi_status status; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (!has_acpi_companion(dev)) 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci memset(&lookup, 0, sizeof(lookup)); 3458c2ecf20Sopenharmony_ci lookup.search_handle = ACPI_HANDLE(dev); 3468c2ecf20Sopenharmony_ci lookup.min_speed = UINT_MAX; 3478c2ecf20Sopenharmony_ci lookup.info = &dummy; 3488c2ecf20Sopenharmony_ci lookup.index = -1; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 3518c2ecf20Sopenharmony_ci I2C_ACPI_MAX_SCAN_DEPTH, 3528c2ecf20Sopenharmony_ci i2c_acpi_lookup_speed, NULL, 3538c2ecf20Sopenharmony_ci &lookup, NULL); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 3568c2ecf20Sopenharmony_ci dev_warn(dev, "unable to find I2C bus speed from ACPI\n"); 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (lookup.force_speed) { 3618c2ecf20Sopenharmony_ci if (lookup.force_speed != lookup.min_speed) 3628c2ecf20Sopenharmony_ci dev_warn(dev, FW_BUG "DSDT uses known not-working I2C bus speed %d, forcing it to %d\n", 3638c2ecf20Sopenharmony_ci lookup.min_speed, lookup.force_speed); 3648c2ecf20Sopenharmony_ci return lookup.force_speed; 3658c2ecf20Sopenharmony_ci } else if (lookup.min_speed != UINT_MAX) { 3668c2ecf20Sopenharmony_ci return lookup.min_speed; 3678c2ecf20Sopenharmony_ci } else { 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int i2c_acpi_find_match_adapter(struct device *dev, const void *data) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = i2c_verify_adapter(dev); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (!adapter) 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return ACPI_HANDLE(dev) == (acpi_handle)data; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistruct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct device *dev; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci dev = bus_find_device(&i2c_bus_type, NULL, handle, 3888c2ecf20Sopenharmony_ci i2c_acpi_find_match_adapter); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return dev ? i2c_verify_adapter(dev) : NULL; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_acpi_find_adapter_by_handle); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct device *dev; 3978c2ecf20Sopenharmony_ci struct i2c_client *client; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); 4008c2ecf20Sopenharmony_ci if (!dev) 4018c2ecf20Sopenharmony_ci return NULL; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci client = i2c_verify_client(dev); 4048c2ecf20Sopenharmony_ci if (!client) 4058c2ecf20Sopenharmony_ci put_device(dev); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return client; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int i2c_acpi_notify(struct notifier_block *nb, unsigned long value, 4118c2ecf20Sopenharmony_ci void *arg) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct acpi_device *adev = arg; 4148c2ecf20Sopenharmony_ci struct i2c_board_info info; 4158c2ecf20Sopenharmony_ci acpi_handle adapter_handle; 4168c2ecf20Sopenharmony_ci struct i2c_adapter *adapter; 4178c2ecf20Sopenharmony_ci struct i2c_client *client; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci switch (value) { 4208c2ecf20Sopenharmony_ci case ACPI_RECONFIG_DEVICE_ADD: 4218c2ecf20Sopenharmony_ci if (i2c_acpi_get_info(adev, &info, NULL, &adapter_handle)) 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci adapter = i2c_acpi_find_adapter_by_handle(adapter_handle); 4258c2ecf20Sopenharmony_ci if (!adapter) 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci i2c_acpi_register_device(adapter, adev, &info); 4298c2ecf20Sopenharmony_ci put_device(&adapter->dev); 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case ACPI_RECONFIG_DEVICE_REMOVE: 4328c2ecf20Sopenharmony_ci if (!acpi_device_enumerated(adev)) 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci client = i2c_acpi_find_client_by_adev(adev); 4368c2ecf20Sopenharmony_ci if (!client) 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci i2c_unregister_device(client); 4408c2ecf20Sopenharmony_ci put_device(&client->dev); 4418c2ecf20Sopenharmony_ci break; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return NOTIFY_OK; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistruct notifier_block i2c_acpi_notifier = { 4488c2ecf20Sopenharmony_ci .notifier_call = i2c_acpi_notify, 4498c2ecf20Sopenharmony_ci}; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci/** 4528c2ecf20Sopenharmony_ci * i2c_acpi_new_device - Create i2c-client for the Nth I2cSerialBus resource 4538c2ecf20Sopenharmony_ci * @dev: Device owning the ACPI resources to get the client from 4548c2ecf20Sopenharmony_ci * @index: Index of ACPI resource to get 4558c2ecf20Sopenharmony_ci * @info: describes the I2C device; note this is modified (addr gets set) 4568c2ecf20Sopenharmony_ci * Context: can sleep 4578c2ecf20Sopenharmony_ci * 4588c2ecf20Sopenharmony_ci * By default the i2c subsys creates an i2c-client for the first I2cSerialBus 4598c2ecf20Sopenharmony_ci * resource of an acpi_device, but some acpi_devices have multiple I2cSerialBus 4608c2ecf20Sopenharmony_ci * resources, in that case this function can be used to create an i2c-client 4618c2ecf20Sopenharmony_ci * for other I2cSerialBus resources in the Current Resource Settings table. 4628c2ecf20Sopenharmony_ci * 4638c2ecf20Sopenharmony_ci * Also see i2c_new_client_device, which this function calls to create the 4648c2ecf20Sopenharmony_ci * i2c-client. 4658c2ecf20Sopenharmony_ci * 4668c2ecf20Sopenharmony_ci * Returns a pointer to the new i2c-client, or error pointer in case of failure. 4678c2ecf20Sopenharmony_ci * Specifically, -EPROBE_DEFER is returned if the adapter is not found. 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_cistruct i2c_client *i2c_acpi_new_device(struct device *dev, int index, 4708c2ecf20Sopenharmony_ci struct i2c_board_info *info) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(dev); 4738c2ecf20Sopenharmony_ci struct i2c_acpi_lookup lookup; 4748c2ecf20Sopenharmony_ci struct i2c_adapter *adapter; 4758c2ecf20Sopenharmony_ci LIST_HEAD(resource_list); 4768c2ecf20Sopenharmony_ci int ret; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci memset(&lookup, 0, sizeof(lookup)); 4798c2ecf20Sopenharmony_ci lookup.info = info; 4808c2ecf20Sopenharmony_ci lookup.device_handle = acpi_device_handle(adev); 4818c2ecf20Sopenharmony_ci lookup.index = index; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci ret = acpi_dev_get_resources(adev, &resource_list, 4848c2ecf20Sopenharmony_ci i2c_acpi_fill_info, &lookup); 4858c2ecf20Sopenharmony_ci if (ret < 0) 4868c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (!info->addr) 4918c2ecf20Sopenharmony_ci return ERR_PTR(-EADDRNOTAVAIL); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci adapter = i2c_acpi_find_adapter_by_handle(lookup.adapter_handle); 4948c2ecf20Sopenharmony_ci if (!adapter) 4958c2ecf20Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return i2c_new_client_device(adapter, info); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_acpi_new_device); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI_I2C_OPREGION 5028c2ecf20Sopenharmony_cistatic int acpi_gsb_i2c_read_bytes(struct i2c_client *client, 5038c2ecf20Sopenharmony_ci u8 cmd, u8 *data, u8 data_len) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci struct i2c_msg msgs[2]; 5078c2ecf20Sopenharmony_ci int ret; 5088c2ecf20Sopenharmony_ci u8 *buffer; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci buffer = kzalloc(data_len, GFP_KERNEL); 5118c2ecf20Sopenharmony_ci if (!buffer) 5128c2ecf20Sopenharmony_ci return AE_NO_MEMORY; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci msgs[0].addr = client->addr; 5158c2ecf20Sopenharmony_ci msgs[0].flags = client->flags; 5168c2ecf20Sopenharmony_ci msgs[0].len = 1; 5178c2ecf20Sopenharmony_ci msgs[0].buf = &cmd; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci msgs[1].addr = client->addr; 5208c2ecf20Sopenharmony_ci msgs[1].flags = client->flags | I2C_M_RD; 5218c2ecf20Sopenharmony_ci msgs[1].len = data_len; 5228c2ecf20Sopenharmony_ci msgs[1].buf = buffer; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 5258c2ecf20Sopenharmony_ci if (ret < 0) { 5268c2ecf20Sopenharmony_ci /* Getting a NACK is unfortunately normal with some DSTDs */ 5278c2ecf20Sopenharmony_ci if (ret == -EREMOTEIO) 5288c2ecf20Sopenharmony_ci dev_dbg(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n", 5298c2ecf20Sopenharmony_ci data_len, client->addr, cmd, ret); 5308c2ecf20Sopenharmony_ci else 5318c2ecf20Sopenharmony_ci dev_err(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n", 5328c2ecf20Sopenharmony_ci data_len, client->addr, cmd, ret); 5338c2ecf20Sopenharmony_ci /* 2 transfers must have completed successfully */ 5348c2ecf20Sopenharmony_ci } else if (ret == 2) { 5358c2ecf20Sopenharmony_ci memcpy(data, buffer, data_len); 5368c2ecf20Sopenharmony_ci ret = 0; 5378c2ecf20Sopenharmony_ci } else { 5388c2ecf20Sopenharmony_ci ret = -EIO; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci kfree(buffer); 5428c2ecf20Sopenharmony_ci return ret; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int acpi_gsb_i2c_write_bytes(struct i2c_client *client, 5468c2ecf20Sopenharmony_ci u8 cmd, u8 *data, u8 data_len) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci struct i2c_msg msgs[1]; 5508c2ecf20Sopenharmony_ci u8 *buffer; 5518c2ecf20Sopenharmony_ci int ret = AE_OK; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci buffer = kzalloc(data_len + 1, GFP_KERNEL); 5548c2ecf20Sopenharmony_ci if (!buffer) 5558c2ecf20Sopenharmony_ci return AE_NO_MEMORY; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci buffer[0] = cmd; 5588c2ecf20Sopenharmony_ci memcpy(buffer + 1, data, data_len); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci msgs[0].addr = client->addr; 5618c2ecf20Sopenharmony_ci msgs[0].flags = client->flags; 5628c2ecf20Sopenharmony_ci msgs[0].len = data_len + 1; 5638c2ecf20Sopenharmony_ci msgs[0].buf = buffer; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci kfree(buffer); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (ret < 0) { 5708c2ecf20Sopenharmony_ci dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret); 5718c2ecf20Sopenharmony_ci return ret; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* 1 transfer must have completed successfully */ 5758c2ecf20Sopenharmony_ci return (ret == 1) ? 0 : -EIO; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic acpi_status 5798c2ecf20Sopenharmony_cii2c_acpi_space_handler(u32 function, acpi_physical_address command, 5808c2ecf20Sopenharmony_ci u32 bits, u64 *value64, 5818c2ecf20Sopenharmony_ci void *handler_context, void *region_context) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct gsb_buffer *gsb = (struct gsb_buffer *)value64; 5848c2ecf20Sopenharmony_ci struct i2c_acpi_handler_data *data = handler_context; 5858c2ecf20Sopenharmony_ci struct acpi_connection_info *info = &data->info; 5868c2ecf20Sopenharmony_ci struct acpi_resource_i2c_serialbus *sb; 5878c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = data->adapter; 5888c2ecf20Sopenharmony_ci struct i2c_client *client; 5898c2ecf20Sopenharmony_ci struct acpi_resource *ares; 5908c2ecf20Sopenharmony_ci u32 accessor_type = function >> 16; 5918c2ecf20Sopenharmony_ci u8 action = function & ACPI_IO_MASK; 5928c2ecf20Sopenharmony_ci acpi_status ret; 5938c2ecf20Sopenharmony_ci int status; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci ret = acpi_buffer_to_resource(info->connection, info->length, &ares); 5968c2ecf20Sopenharmony_ci if (ACPI_FAILURE(ret)) 5978c2ecf20Sopenharmony_ci return ret; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci client = kzalloc(sizeof(*client), GFP_KERNEL); 6008c2ecf20Sopenharmony_ci if (!client) { 6018c2ecf20Sopenharmony_ci ret = AE_NO_MEMORY; 6028c2ecf20Sopenharmony_ci goto err; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (!value64 || !i2c_acpi_get_i2c_resource(ares, &sb)) { 6068c2ecf20Sopenharmony_ci ret = AE_BAD_PARAMETER; 6078c2ecf20Sopenharmony_ci goto err; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci client->adapter = adapter; 6118c2ecf20Sopenharmony_ci client->addr = sb->slave_address; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (sb->access_mode == ACPI_I2C_10BIT_MODE) 6148c2ecf20Sopenharmony_ci client->flags |= I2C_CLIENT_TEN; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci switch (accessor_type) { 6178c2ecf20Sopenharmony_ci case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV: 6188c2ecf20Sopenharmony_ci if (action == ACPI_READ) { 6198c2ecf20Sopenharmony_ci status = i2c_smbus_read_byte(client); 6208c2ecf20Sopenharmony_ci if (status >= 0) { 6218c2ecf20Sopenharmony_ci gsb->bdata = status; 6228c2ecf20Sopenharmony_ci status = 0; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci } else { 6258c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte(client, gsb->bdata); 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci case ACPI_GSB_ACCESS_ATTRIB_BYTE: 6308c2ecf20Sopenharmony_ci if (action == ACPI_READ) { 6318c2ecf20Sopenharmony_ci status = i2c_smbus_read_byte_data(client, command); 6328c2ecf20Sopenharmony_ci if (status >= 0) { 6338c2ecf20Sopenharmony_ci gsb->bdata = status; 6348c2ecf20Sopenharmony_ci status = 0; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci } else { 6378c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(client, command, 6388c2ecf20Sopenharmony_ci gsb->bdata); 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci case ACPI_GSB_ACCESS_ATTRIB_WORD: 6438c2ecf20Sopenharmony_ci if (action == ACPI_READ) { 6448c2ecf20Sopenharmony_ci status = i2c_smbus_read_word_data(client, command); 6458c2ecf20Sopenharmony_ci if (status >= 0) { 6468c2ecf20Sopenharmony_ci gsb->wdata = status; 6478c2ecf20Sopenharmony_ci status = 0; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci } else { 6508c2ecf20Sopenharmony_ci status = i2c_smbus_write_word_data(client, command, 6518c2ecf20Sopenharmony_ci gsb->wdata); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci case ACPI_GSB_ACCESS_ATTRIB_BLOCK: 6568c2ecf20Sopenharmony_ci if (action == ACPI_READ) { 6578c2ecf20Sopenharmony_ci status = i2c_smbus_read_block_data(client, command, 6588c2ecf20Sopenharmony_ci gsb->data); 6598c2ecf20Sopenharmony_ci if (status >= 0) { 6608c2ecf20Sopenharmony_ci gsb->len = status; 6618c2ecf20Sopenharmony_ci status = 0; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci } else { 6648c2ecf20Sopenharmony_ci status = i2c_smbus_write_block_data(client, command, 6658c2ecf20Sopenharmony_ci gsb->len, gsb->data); 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci break; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE: 6708c2ecf20Sopenharmony_ci if (action == ACPI_READ) { 6718c2ecf20Sopenharmony_ci status = acpi_gsb_i2c_read_bytes(client, command, 6728c2ecf20Sopenharmony_ci gsb->data, info->access_length); 6738c2ecf20Sopenharmony_ci } else { 6748c2ecf20Sopenharmony_ci status = acpi_gsb_i2c_write_bytes(client, command, 6758c2ecf20Sopenharmony_ci gsb->data, info->access_length); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci default: 6808c2ecf20Sopenharmony_ci dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", 6818c2ecf20Sopenharmony_ci accessor_type, client->addr); 6828c2ecf20Sopenharmony_ci ret = AE_BAD_PARAMETER; 6838c2ecf20Sopenharmony_ci goto err; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci gsb->status = status; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci err: 6898c2ecf20Sopenharmony_ci kfree(client); 6908c2ecf20Sopenharmony_ci ACPI_FREE(ares); 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ciint i2c_acpi_install_space_handler(struct i2c_adapter *adapter) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci acpi_handle handle; 6988c2ecf20Sopenharmony_ci struct i2c_acpi_handler_data *data; 6998c2ecf20Sopenharmony_ci acpi_status status; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (!adapter->dev.parent) 7028c2ecf20Sopenharmony_ci return -ENODEV; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(adapter->dev.parent); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (!handle) 7078c2ecf20Sopenharmony_ci return -ENODEV; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci data = kzalloc(sizeof(struct i2c_acpi_handler_data), 7108c2ecf20Sopenharmony_ci GFP_KERNEL); 7118c2ecf20Sopenharmony_ci if (!data) 7128c2ecf20Sopenharmony_ci return -ENOMEM; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci data->adapter = adapter; 7158c2ecf20Sopenharmony_ci status = acpi_bus_attach_private_data(handle, (void *)data); 7168c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 7178c2ecf20Sopenharmony_ci kfree(data); 7188c2ecf20Sopenharmony_ci return -ENOMEM; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci status = acpi_install_address_space_handler(handle, 7228c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_GSBUS, 7238c2ecf20Sopenharmony_ci &i2c_acpi_space_handler, 7248c2ecf20Sopenharmony_ci NULL, 7258c2ecf20Sopenharmony_ci data); 7268c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 7278c2ecf20Sopenharmony_ci dev_err(&adapter->dev, "Error installing i2c space handler\n"); 7288c2ecf20Sopenharmony_ci acpi_bus_detach_private_data(handle); 7298c2ecf20Sopenharmony_ci kfree(data); 7308c2ecf20Sopenharmony_ci return -ENOMEM; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_civoid i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci acpi_handle handle; 7398c2ecf20Sopenharmony_ci struct i2c_acpi_handler_data *data; 7408c2ecf20Sopenharmony_ci acpi_status status; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (!adapter->dev.parent) 7438c2ecf20Sopenharmony_ci return; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(adapter->dev.parent); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!handle) 7488c2ecf20Sopenharmony_ci return; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci acpi_remove_address_space_handler(handle, 7518c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_GSBUS, 7528c2ecf20Sopenharmony_ci &i2c_acpi_space_handler); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci status = acpi_bus_get_private_data(handle, (void **)&data); 7558c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) 7568c2ecf20Sopenharmony_ci kfree(data); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci acpi_bus_detach_private_data(handle); 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci#endif /* CONFIG_ACPI_I2C_OPREGION */ 761