in mouse/elan_i2c_core.c [1225:1378]
static int elan_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
const struct elan_transport_ops *transport_ops;
struct device *dev = &client->dev;
struct elan_tp_data *data;
unsigned long irqflags;
int error;
if (IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_I2C) &&
i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
transport_ops = &elan_i2c_ops;
} else if (IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) &&
i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK)) {
transport_ops = &elan_smbus_ops;
} else {
dev_err(dev, "not a supported I2C/SMBus adapter\n");
return -EIO;
}
data = devm_kzalloc(dev, sizeof(struct elan_tp_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
data->ops = transport_ops;
data->client = client;
init_completion(&data->fw_completion);
mutex_init(&data->sysfs_mutex);
data->vcc = devm_regulator_get(dev, "vcc");
if (IS_ERR(data->vcc)) {
error = PTR_ERR(data->vcc);
if (error != -EPROBE_DEFER)
dev_err(dev, "Failed to get 'vcc' regulator: %d\n",
error);
return error;
}
error = regulator_enable(data->vcc);
if (error) {
dev_err(dev, "Failed to enable regulator: %d\n", error);
return error;
}
error = devm_add_action_or_reset(dev, elan_disable_regulator, data);
if (error) {
dev_err(dev, "Failed to add disable regulator action: %d\n",
error);
return error;
}
/* Make sure there is something at this address */
error = i2c_smbus_read_byte(client);
if (error < 0) {
dev_dbg(&client->dev, "nothing at this address: %d\n", error);
return -ENXIO;
}
/* Initialize the touchpad. */
error = elan_initialize(data, false);
if (error)
return error;
error = elan_query_device_info(data);
if (error)
return error;
error = elan_query_device_parameters(data);
if (error)
return error;
dev_info(dev,
"Elan Touchpad: Module ID: 0x%04x, Firmware: 0x%04x, Sample: 0x%04x, IAP: 0x%04x\n",
data->product_id,
data->fw_version,
data->sm_version,
data->iap_version);
dev_dbg(dev,
"Elan Touchpad Extra Information:\n"
" Max ABS X,Y: %d,%d\n"
" Width X,Y: %d,%d\n"
" Resolution X,Y: %d,%d (dots/mm)\n"
" ic type: 0x%x\n"
" info pattern: 0x%x\n",
data->max_x, data->max_y,
data->width_x, data->width_y,
data->x_res, data->y_res,
data->ic_type, data->pattern);
/* Set up input device properties based on queried parameters. */
error = elan_setup_input_device(data);
if (error)
return error;
if (device_property_read_bool(&client->dev, "elan,trackpoint")) {
error = elan_setup_trackpoint_input_device(data);
if (error)
return error;
}
/*
* Platform code (ACPI, DTS) should normally set up interrupt
* for us, but in case it did not let's fall back to using falling
* edge to be compatible with older Chromebooks.
*/
irqflags = irq_get_trigger_type(client->irq);
if (!irqflags)
irqflags = IRQF_TRIGGER_FALLING;
error = devm_request_threaded_irq(dev, client->irq, NULL, elan_isr,
irqflags | IRQF_ONESHOT,
client->name, data);
if (error) {
dev_err(dev, "cannot register irq=%d\n", client->irq);
return error;
}
error = devm_device_add_groups(dev, elan_sysfs_groups);
if (error) {
dev_err(dev, "failed to create sysfs attributes: %d\n", error);
return error;
}
error = input_register_device(data->input);
if (error) {
dev_err(dev, "failed to register input device: %d\n", error);
return error;
}
if (data->tp_input) {
error = input_register_device(data->tp_input);
if (error) {
dev_err(&client->dev,
"failed to register TrackPoint input device: %d\n",
error);
return error;
}
}
/*
* Systems using device tree should set up wakeup via DTS,
* the rest will configure device as wakeup source by default.
*/
if (!dev->of_node)
device_init_wakeup(dev, true);
return 0;
}