aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/i2c-core.c
diff options
context:
space:
mode:
authorJarkko Nikula <jarkko.nikula@linux.intel.com>2016-08-12 10:02:53 -0400
committerWolfram Sang <wsa@the-dreams.de>2016-08-25 18:42:10 -0400
commit5853b22d96fa786365ff11fe9f008a68a533f043 (patch)
treeb50801019fadf47b6b9efee685e3cc713a15320c /drivers/i2c/i2c-core.c
parentaec809fc63f61a85e4300bb70081fbe6b492171c (diff)
i2c: core: Add function for finding the bus speed from ACPI, take 2
ACPI 5 specification doesn't have property for the I2C bus speed but I2cSerialBus resource descriptor which define each controller-slave connection define the maximum speed supported by that connection. Thus finding the maximum safe speed for the bus is to walk through all I2cSerialBus resources that are associated to I2C controller and use the speed of slowest connection. Add function i2c_acpi_find_bus_speed() to the i2c-core that adapter drivers can call prior registering itself to core. This implies two-step walk through the I2cSerialBus resources: call to i2c_acpi_find_bus_speed() does the first scan and finds the safe bus speed that adapter drivers can set up. Adapter driver registration does the second scan when i2c-core creates the I2C slaves by calling the i2c_acpi_register_devices(). In that way the bus speed is set in case slave device probe gets called during registration and does communication. Previous version commit 55d38d060e99 ("i2c: core: Add function for finding the bus speed from ACPI") got reverted due merge conflicts from commit 525e6fabeae2 ("i2c / ACPI: add support for ACPI reconfigure notifications"). This version is a bit bigger than previous version but is still sharing the lowest and complicated part of I2cSerialBus lookup routines with the existing code. Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Diffstat (limited to 'drivers/i2c/i2c-core.c')
-rw-r--r--drivers/i2c/i2c-core.c99
1 files changed, 88 insertions, 11 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index d4b2f66d8994..05794423e01f 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -107,6 +107,9 @@ struct i2c_acpi_lookup {
107 struct i2c_board_info *info; 107 struct i2c_board_info *info;
108 acpi_handle adapter_handle; 108 acpi_handle adapter_handle;
109 acpi_handle device_handle; 109 acpi_handle device_handle;
110 acpi_handle search_handle;
111 u32 speed;
112 u32 min_speed;
110}; 113};
111 114
112static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data) 115static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data)
@@ -130,19 +133,18 @@ static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data)
130 return 1; 133 return 1;
131 134
132 info->addr = sb->slave_address; 135 info->addr = sb->slave_address;
136 lookup->speed = sb->connection_speed;
133 if (sb->access_mode == ACPI_I2C_10BIT_MODE) 137 if (sb->access_mode == ACPI_I2C_10BIT_MODE)
134 info->flags |= I2C_CLIENT_TEN; 138 info->flags |= I2C_CLIENT_TEN;
135 139
136 return 1; 140 return 1;
137} 141}
138 142
139static int i2c_acpi_get_info(struct acpi_device *adev, 143static int i2c_acpi_do_lookup(struct acpi_device *adev,
140 struct i2c_board_info *info, 144 struct i2c_acpi_lookup *lookup)
141 acpi_handle *adapter_handle)
142{ 145{
146 struct i2c_board_info *info = lookup->info;
143 struct list_head resource_list; 147 struct list_head resource_list;
144 struct resource_entry *entry;
145 struct i2c_acpi_lookup lookup;
146 int ret; 148 int ret;
147 149
148 if (acpi_bus_get_status(adev) || !adev->status.present || 150 if (acpi_bus_get_status(adev) || !adev->status.present ||
@@ -150,24 +152,41 @@ static int i2c_acpi_get_info(struct acpi_device *adev,
150 return -EINVAL; 152 return -EINVAL;
151 153
152 memset(info, 0, sizeof(*info)); 154 memset(info, 0, sizeof(*info));
153 info->fwnode = acpi_fwnode_handle(adev); 155 lookup->device_handle = acpi_device_handle(adev);
154
155 memset(&lookup, 0, sizeof(lookup));
156 lookup.device_handle = acpi_device_handle(adev);
157 lookup.info = info;
158 156
159 /* Look up for I2cSerialBus resource */ 157 /* Look up for I2cSerialBus resource */
160 INIT_LIST_HEAD(&resource_list); 158 INIT_LIST_HEAD(&resource_list);
161 ret = acpi_dev_get_resources(adev, &resource_list, 159 ret = acpi_dev_get_resources(adev, &resource_list,
162 i2c_acpi_fill_info, &lookup); 160 i2c_acpi_fill_info, lookup);
163 acpi_dev_free_resource_list(&resource_list); 161 acpi_dev_free_resource_list(&resource_list);
164 162
165 if (ret < 0 || !info->addr) 163 if (ret < 0 || !info->addr)
166 return -EINVAL; 164 return -EINVAL;
167 165
166 return 0;
167}
168
169static int i2c_acpi_get_info(struct acpi_device *adev,
170 struct i2c_board_info *info,
171 acpi_handle *adapter_handle)
172{
173 struct list_head resource_list;
174 struct resource_entry *entry;
175 struct i2c_acpi_lookup lookup;
176 int ret;
177
178 memset(&lookup, 0, sizeof(lookup));
179 lookup.info = info;
180
181 ret = i2c_acpi_do_lookup(adev, &lookup);
182 if (ret)
183 return ret;
184
185 info->fwnode = acpi_fwnode_handle(adev);
168 *adapter_handle = lookup.adapter_handle; 186 *adapter_handle = lookup.adapter_handle;
169 187
170 /* Then fill IRQ number if any */ 188 /* Then fill IRQ number if any */
189 INIT_LIST_HEAD(&resource_list);
171 ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 190 ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
172 if (ret < 0) 191 if (ret < 0)
173 return -EINVAL; 192 return -EINVAL;
@@ -248,6 +267,64 @@ static void i2c_acpi_register_devices(struct i2c_adapter *adap)
248 dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); 267 dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
249} 268}
250 269
270static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
271 void *data, void **return_value)
272{
273 struct i2c_acpi_lookup *lookup = data;
274 struct acpi_device *adev;
275
276 if (acpi_bus_get_device(handle, &adev))
277 return AE_OK;
278
279 if (i2c_acpi_do_lookup(adev, lookup))
280 return AE_OK;
281
282 if (lookup->search_handle != lookup->adapter_handle)
283 return AE_OK;
284
285 if (lookup->speed <= lookup->min_speed)
286 lookup->min_speed = lookup->speed;
287
288 return AE_OK;
289}
290
291/**
292 * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI
293 * @dev: The device owning the bus
294 *
295 * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves
296 * devices connected to this bus and use the speed of slowest device.
297 *
298 * Returns the speed in Hz or zero
299 */
300u32 i2c_acpi_find_bus_speed(struct device *dev)
301{
302 struct i2c_acpi_lookup lookup;
303 struct i2c_board_info dummy;
304 acpi_status status;
305
306 if (!has_acpi_companion(dev))
307 return 0;
308
309 memset(&lookup, 0, sizeof(lookup));
310 lookup.search_handle = ACPI_HANDLE(dev);
311 lookup.min_speed = UINT_MAX;
312 lookup.info = &dummy;
313
314 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
315 I2C_ACPI_MAX_SCAN_DEPTH,
316 i2c_acpi_lookup_speed, NULL,
317 &lookup, NULL);
318
319 if (ACPI_FAILURE(status)) {
320 dev_warn(dev, "unable to find I2C bus speed from ACPI\n");
321 return 0;
322 }
323
324 return lookup.min_speed != UINT_MAX ? lookup.min_speed : 0;
325}
326EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed);
327
251static int i2c_acpi_match_adapter(struct device *dev, void *data) 328static int i2c_acpi_match_adapter(struct device *dev, void *data)
252{ 329{
253 struct i2c_adapter *adapter = i2c_verify_adapter(dev); 330 struct i2c_adapter *adapter = i2c_verify_adapter(dev);