/** * i2c_acpi_get_i2c_resource - Gets I2cSerialBus resource if type matches * @ares: ACPI resource * @i2c: Pointer to I2cSerialBus resource will be returned here * * Checks if the given ACPI resource is of type I2cSerialBus. * In this case, returns a pointer to it to the caller. * * Returns true if resource type is of I2cSerialBus, otherwise false.
*/ bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, struct acpi_resource_i2c_serialbus **i2c)
{ struct acpi_resource_i2c_serialbus *sb;
if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) returnfalse;
sb = &ares->data.i2c_serial_bus; if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) returnfalse;
if (i2c_acpi_get_i2c_resource(ares, &sb))
*count = *count + 1;
return 1;
}
/** * i2c_acpi_client_count - Count the number of I2cSerialBus resources * @adev: ACPI device * * Returns the number of I2cSerialBus resources in the ACPI-device's * resource-list; or a negative error code.
*/ int i2c_acpi_client_count(struct acpi_device *adev)
{ int ret, count = 0;
LIST_HEAD(r);
ret = acpi_dev_get_resources(adev, &r, i2c_acpi_resource_count, &count); if (ret < 0) return ret;
staticconststruct acpi_device_id i2c_acpi_ignored_device_ids[] = { /* * ACPI video acpi_devices, which are handled by the acpi-video driver * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these.
*/
{ ACPI_VIDEO_HID, 0 },
{}
};
struct i2c_acpi_irq_context { int irq; bool wake_capable;
};
/* Look up for I2cSerialBus resource */
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
i2c_acpi_fill_info, lookup);
acpi_dev_free_resource_list(&resource_list);
return 1; /* No need to add resource to the list */
}
/** * i2c_acpi_get_irq - get device IRQ number from ACPI * @client: Pointer to the I2C client device * @wake_capable: Set to true if the IRQ is wake capable * * Find the IRQ number used by a specific client device. * * Return: The IRQ number or an error code.
*/ int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable)
{ struct acpi_device *adev = ACPI_COMPANION(&client->dev); struct list_head resource_list; struct i2c_acpi_irq_context irq_ctx = {
.irq = -ENOENT,
}; int ret;
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
i2c_acpi_add_irq_resource, &irq_ctx); if (ret < 0) return ret;
acpi_dev_free_resource_list(&resource_list);
if (irq_ctx.irq == -ENOENT)
irq_ctx.irq = acpi_dev_gpio_irq_wake_get(adev, 0, &irq_ctx.wake_capable);
if (irq_ctx.irq < 0) return irq_ctx.irq;
if (wake_capable)
*wake_capable = irq_ctx.wake_capable;
ret = i2c_acpi_do_lookup(adev, &lookup); if (ret) return ret;
if (adapter) { /* The adapter must match the one in I2cSerialBus() connector */ if (!device_match_acpi_handle(&adapter->dev, lookup.adapter_handle)) return -ENODEV;
} else { struct acpi_device *adapter_adev;
/* The adapter must be present */
adapter_adev = acpi_fetch_acpi_dev(lookup.adapter_handle); if (!adapter_adev) return -ENODEV; if (acpi_bus_get_status(adapter_adev) ||
!adapter_adev->status.present) return -ENODEV;
}
info->fwnode = acpi_fwnode_handle(adev); if (adapter_handle)
*adapter_handle = lookup.adapter_handle;
staticvoid i2c_acpi_register_device(struct i2c_adapter *adapter, struct acpi_device *adev, struct i2c_board_info *info)
{ /* * Skip registration on boards where the ACPI tables are * known to contain bogus I2C devices.
*/ if (acpi_quirk_skip_i2c_client_enumeration(adev)) return;
if (!adev || i2c_acpi_get_info(adev, &info, adapter, NULL)) return AE_OK;
i2c_acpi_register_device(adapter, adev, &info);
return AE_OK;
}
#define I2C_ACPI_MAX_SCAN_DEPTH 32
/** * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter * @adap: 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 i2c_acpi_register_devices(struct i2c_adapter *adap)
{ struct acpi_device *adev;
acpi_status status;
if (!has_acpi_companion(&adap->dev)) return;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
I2C_ACPI_MAX_SCAN_DEPTH,
i2c_acpi_add_device, NULL,
adap, NULL); if (ACPI_FAILURE(status))
dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
if (!adap->dev.parent) return;
adev = ACPI_COMPANION(adap->dev.parent); if (!adev) return;
acpi_dev_clear_dependencies(adev);
}
staticconststruct acpi_device_id i2c_acpi_force_400khz_device_ids[] = { /* * These Silead touchscreen controllers only work at 400KHz, for * some reason they do not work at 100KHz. On some devices the ACPI * tables list another device at their bus as only being capable * of 100KHz, testing has shown that these other devices work fine * at 400KHz (as can be expected of any recent i2c hw) so we force * the speed of the bus to 400 KHz if a Silead device is present.
*/
{ "MSSL1680", 0 },
{}
};
staticconststruct acpi_device_id i2c_acpi_force_100khz_device_ids[] = { /* * When a 400KHz freq is used on this model of ELAN touchpad in Linux, * excessive smoothing (similar to when the touchpad's firmware detects * a noisy signal) is sometimes applied. As some devices' (e.g, Lenovo * V15 G4) ACPI tables specify a 400KHz frequency for this device and * some I2C busses (e.g, Designware I2C) default to a 400KHz freq, * force the speed to 100KHz as a workaround. * * For future investigation: This problem may be related to the default * HCNT/LCNT values given by some busses' drivers, because they are not * specified in the aforementioned devices' ACPI tables, and because * the device works without issues on Windows at what is expected to be * a 400KHz frequency. The root cause of the issue is not known.
*/
{ "DLL0945", 0 },
{ "ELAN06FA", 0 },
{}
};
if (!adev || i2c_acpi_do_lookup(adev, lookup)) return AE_OK;
if (lookup->search_handle != lookup->adapter_handle) return AE_OK;
if (lookup->speed <= lookup->min_speed)
lookup->min_speed = lookup->speed;
if (acpi_match_device_ids(adev, i2c_acpi_force_400khz_device_ids) == 0)
lookup->force_speed = I2C_MAX_FAST_MODE_FREQ;
if (acpi_match_device_ids(adev, i2c_acpi_force_100khz_device_ids) == 0)
lookup->force_speed = I2C_MAX_STANDARD_MODE_FREQ;
return AE_OK;
}
/** * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI * @dev: The device owning the bus * * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves * devices connected to this bus and use the speed of slowest device. * * Returns the speed in Hz or zero
*/
u32 i2c_acpi_find_bus_speed(struct device *dev)
{ struct i2c_acpi_lookup lookup; struct i2c_board_info dummy;
acpi_status status;
switch (value) { case ACPI_RECONFIG_DEVICE_ADD: if (i2c_acpi_get_info(adev, &info, NULL, &adapter_handle)) break;
adapter = i2c_acpi_find_adapter_by_handle(adapter_handle); if (!adapter) break;
i2c_acpi_register_device(adapter, adev, &info);
put_device(&adapter->dev); break; case ACPI_RECONFIG_DEVICE_REMOVE: if (!acpi_device_enumerated(adev)) break;
client = i2c_acpi_find_client_by_adev(adev); if (client) {
i2c_unregister_device(client);
put_device(&client->dev);
}
adapter = i2c_acpi_find_adapter_by_adev(adev); if (adapter) {
acpi_unbind_one(&adapter->dev);
put_device(&adapter->dev);
}
/** * i2c_acpi_new_device_by_fwnode - Create i2c-client for the Nth I2cSerialBus resource * @fwnode: fwnode with the ACPI resources to get the client from * @index: Index of ACPI resource to get * @info: describes the I2C device; note this is modified (addr gets set) * Context: can sleep * * By default the i2c subsys creates an i2c-client for the first I2cSerialBus * resource of an acpi_device, but some acpi_devices have multiple I2cSerialBus * resources, in that case this function can be used to create an i2c-client * for other I2cSerialBus resources in the Current Resource Settings table. * * Also see i2c_new_client_device, which this function calls to create the * i2c-client. * * Returns a pointer to the new i2c-client, or error pointer in case of failure. * Specifically, -EPROBE_DEFER is returned if the adapter is not found.
*/ struct i2c_client *i2c_acpi_new_device_by_fwnode(struct fwnode_handle *fwnode, int index, struct i2c_board_info *info)
{ struct i2c_acpi_lookup lookup; struct i2c_adapter *adapter; struct acpi_device *adev;
LIST_HEAD(resource_list); int ret;
adev = to_acpi_device_node(fwnode); if (!adev) return ERR_PTR(-ENODEV);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.