/* * Copyright (C) 2017 Sony Mobile Communications Inc. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #define ENABLE_LOGE #ifdef ENABLE_LOGE #define LOGE(dev, f, a...) dev_err(dev, "%s: " f, __func__, ##a) #else #define LOGE(dev, f, a...) #endif #ifdef ENABLE_LOGI #define LOGI(dev, f, a...) dev_info(dev, "%s: " f, __func__, ##a) #else #define LOGI(dev, f, a...) #endif #ifdef ENABLE_LOGD #define LOGD(dev, f, a...) dev_dbg(dev, "%s: " f, __func__, ##a) #else #define LOGD(dev, f, a...) #endif #define TOF_SENSOR_SYSFS_LINK_NAME "tof_sensor" #define TOF_SENSOR_PINCTRL_IRQ_ACTIVE "tof_irq_active" #define TOF_SENSOR_PINCTRL_IRQ_SUSPEND "tof_irq_suspend" #define TOF_SENSOR_REGULATOR_NOTIFY_PRIORITY (1000) /* * tof sensor power control. */ enum tof_sensor_power_control { TOF_SENSOR_POWER_CONTROL_OFF, TOF_SENSOR_POWER_CONTROL_ON, }; struct tof_sensor_info { char name[8]; uint32_t i2c_client_id; uint32_t need_camera_on; uint32_t facing; uint32_t vdd_supply_enable; uint32_t gpio_avdd_enable; uint32_t vio_supply_enable; }; struct tof_sensor_data { struct i2c_client *client; struct regulator *avdd; struct regulator *vio; struct regulator *gpio_avdd; uint32_t min_voltage; uint32_t max_voltage; uint8_t power_up; bool is_register_regulator_cam_vio_notify; struct input_dev *input; const char *dev_name; struct pinctrl *pinctrl; struct pinctrl_state *gpio_state_active; struct pinctrl_state *gpio_state_suspend; struct tof_sensor_info info; struct notifier_block cam_io_regulator_cb; int tof_reset_gpio; uint32_t ref_cnt; }; static int tof_sensor_cam_io_notifier_call(struct notifier_block *self, unsigned long event, void *data) { struct tof_sensor_data *tof_data; uint32_t rc; if (event & REGULATOR_EVENT_DISABLE) { tof_data = container_of(self, struct tof_sensor_data, cam_io_regulator_cb); LOGD(&tof_data->client->dev, "%s REGULATOR_EVENT_DISABLE occurred event \n", __func__); if (tof_data->info.vdd_supply_enable && tof_data->avdd) { rc = regulator_disable(tof_data->avdd); if (!rc) LOGD(&tof_data->client->dev, "%s: Regulator avdd disable OK", __func__); else LOGE(&tof_data->client->dev, "%s: Regulator avdd disable failed ", __func__); } if (tof_data->info.gpio_avdd_enable && tof_data->gpio_avdd) { rc = regulator_disable(tof_data->gpio_avdd); if (!rc) LOGD(&tof_data->client->dev, "%s: Regulator gpio_avdd disable OK", __func__); else LOGE(&tof_data->client->dev, "%s: Regulator gpio_avdd disable failed ", __func__); } } return NOTIFY_OK; } static int tof_sensor_power_init(struct tof_sensor_data *data) { int rc = 0; if ((data->avdd == NULL) && data->info.vdd_supply_enable) { data->avdd = regulator_get(&data->client->dev, "tof_avdd"); if (IS_ERR(data->avdd)) { rc = PTR_ERR(data->avdd); LOGE(&data->client->dev, "Regulator get failed, avdd, rc = %d\n", rc); return rc; } } if ((data->gpio_avdd == NULL) && data->info.gpio_avdd_enable) { data->gpio_avdd = regulator_get(&data->client->dev, "tof_gpio_avdd"); if (IS_ERR(data->gpio_avdd)) { rc = PTR_ERR(data->gpio_avdd); LOGE(&data->client->dev, "Regulator get failed, avdd, rc = %d\n", rc); return rc; } } if ((data->vio == NULL) && data->info.vio_supply_enable) { data->vio = regulator_get(&data->client->dev, "tof_vio"); if (IS_ERR(data->vio)) { rc = PTR_ERR(data->vio); LOGE(&data->client->dev, "Regulator get failed, vio, rc = %d\n", rc); return rc; } } data->is_register_regulator_cam_vio_notify = false; LOGI(&data->client->dev, "%s: power init, regulator get OK", __func__); return rc; } static int tof_sensor_power_deinit(struct tof_sensor_data *data) { int rc = 0; if (!data) return -EINVAL; if (data->info.vdd_supply_enable && data->avdd) regulator_put(data->avdd); if (data->info.vio_supply_enable && data->vio) regulator_put(data->vio); if (data->is_register_regulator_cam_vio_notify) { regulator_unregister_notifier(data->gpio_avdd, &(data->cam_io_regulator_cb)); data->is_register_regulator_cam_vio_notify = false; } if (data->info.gpio_avdd_enable && data->gpio_avdd) regulator_put(data->gpio_avdd); return rc; } static int tof_sensor_pinctrl_init(struct tof_sensor_data *data) { data->pinctrl = devm_pinctrl_get(&data->client->dev); if (IS_ERR_OR_NULL(data->pinctrl)) { LOGE(&data->client->dev, "%s:%d Getting pinctrl handle failed\n", __func__, __LINE__); return -EINVAL; } data->gpio_state_active = pinctrl_lookup_state(data->pinctrl, TOF_SENSOR_PINCTRL_IRQ_ACTIVE); if (IS_ERR_OR_NULL(data->gpio_state_active)) { LOGE(&data->client->dev, "%s:Failed to get the active state pinctrl handle\n", __func__); return -EINVAL; } data->gpio_state_suspend = pinctrl_lookup_state(data->pinctrl, TOF_SENSOR_PINCTRL_IRQ_SUSPEND); if (IS_ERR_OR_NULL(data->gpio_state_suspend)) { LOGE(&data->client->dev, "%s:Failed to get the suspend state pinctrl handle\n", __func__); return -EINVAL; } return 0; } static int tof_sensor_gpio_init(struct tof_sensor_data *data) { int rc = 0; int flag = 0; rc = gpio_request_one(data->tof_reset_gpio, flag, "tof-reset-gpio"); if (rc) { LOGE(&data->client->dev, "%s:%d Getting reset gpio failed\n", __func__, __LINE__); return -EINVAL; } else { return 0; } } static int tof_sensor_parse_dt(struct tof_sensor_data *data) { int rc = 0; const char *name = NULL; uint32_t val_u32 = 0; rc = of_property_read_string_index(data->client->dev.of_node, "sony,tof-sensor-name", 0, (const char **)(&name)); if (rc < 0) { LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); goto fail; } memcpy(data->info.name, name, 8); rc = of_property_read_u32(data->client->dev.of_node, "sony,tof-need-cam-on", &val_u32); if (rc < 0) { LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); goto fail; } data->info.need_camera_on = val_u32; rc = of_property_read_u32(data->client->dev.of_node, "sony,tof-sensor-facing", &val_u32); if (rc < 0) { LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); goto fail; } data->info.facing = val_u32; data->tof_reset_gpio = of_get_named_gpio(data->client->dev.of_node, "tof-reset-gpio", 0); rc = of_property_read_u32(data->client->dev.of_node, "sony,tof-avdd-supply", &val_u32); if (rc < 0) { LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); goto fail; } data->info.vdd_supply_enable = val_u32; rc = of_property_read_u32(data->client->dev.of_node, "sony,tof-vio-supply", &val_u32); if (rc < 0) { LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); goto fail; } data->info.vio_supply_enable = val_u32; rc = of_property_read_u32(data->client->dev.of_node, "sony,tof-gpio-avdd", &val_u32); if (rc < 0) { LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); goto fail; } data->info.gpio_avdd_enable = val_u32; LOGD(&data->client->dev, "%s name %s, need_camera_on %d, facing %d\n", __func__, data->info.name, data->info.need_camera_on, data->info.facing); fail: return rc; } static irqreturn_t tof_sensor_irq(int irq, void *handle) { struct tof_sensor_data *data = handle; LOGI(&data->client->dev, "Tof sensor irq"); sysfs_notify(&data->input->dev.kobj, NULL, "tof_ranging_notify"); return IRQ_HANDLED; } static ssize_t tof_sensor_power_ctl(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct tof_sensor_data *data = dev_get_drvdata(dev); int rc = 0; unsigned long value = 0; LOGD(&data->client->dev, "%s: Power control data", __func__); rc = kstrtoul(buf, sizeof(value), &value); if (rc) { LOGE(&data->client->dev, "%s: covert error. rc = %d", __func__, rc); return count; } if (value == TOF_SENSOR_POWER_CONTROL_ON) { if (!data->ref_cnt) { /* power on */ if (!rc && data->pinctrl && data->gpio_state_active) { rc = pinctrl_select_state(data->pinctrl, data->gpio_state_active); if (rc) LOGE(&data->client->dev, "%s: cannot set pin to active state", __func__); } if (!rc) { rc = request_threaded_irq(data->client->irq, NULL, &tof_sensor_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, dev_name(&data->client->dev), data); if (rc) { LOGE(&data->client->dev, "Failed to request irq %d\n", data->client->irq); (void)pinctrl_select_state(data->pinctrl, data->gpio_state_suspend); } } if (!rc && data->is_register_regulator_cam_vio_notify) { regulator_unregister_notifier(data->gpio_avdd, &(data->cam_io_regulator_cb)); data->is_register_regulator_cam_vio_notify = false; } if (!rc && data->info.vdd_supply_enable) { rc = regulator_enable(data->avdd); if (rc) { LOGE(&data->client->dev, "Regulator avdd enable failed rc=%d", rc); pinctrl_select_state(data->pinctrl, data->gpio_state_suspend); free_irq(data->client->irq, data); return count; } usleep_range(3000, 4000); } if (!rc && data->info.vio_supply_enable) { rc = regulator_enable(data->vio); if (rc) { LOGE(&data->client->dev, "Regulator vio enable failed rc=%d", rc); pinctrl_select_state(data->pinctrl, data->gpio_state_suspend); free_irq(data->client->irq, data); return count; } } if (!rc && data->info.gpio_avdd_enable) { rc = regulator_enable(data->gpio_avdd); if (rc) { LOGE(&data->client->dev, "Regulator gpio_avdd enable failed rc=%d", rc); pinctrl_select_state(data->pinctrl, data->gpio_state_suspend); free_irq(data->client->irq, data); return count; } } if (!rc && data->info.need_camera_on) { rc = gpio_direction_output(data->tof_reset_gpio, 1); if (rc) { LOGE(&data->client->dev, "%s: reset enable failed", __func__); regulator_disable(data->avdd); free_irq(data->client->irq, data); pinctrl_select_state(data->pinctrl, data->gpio_state_suspend); return count; } LOGD(&data->client->dev, "%s: power up regulator enable OK", __func__); usleep_range(3000, 4000); data->power_up = 1; } } data->ref_cnt++; } else if (value == TOF_SENSOR_POWER_CONTROL_OFF) { /* power off */ if (data->ref_cnt > 0) { data->ref_cnt--; if (!data->ref_cnt) { if (data->info.need_camera_on) { rc = gpio_direction_output(data->tof_reset_gpio, 0); if (rc) LOGE(&data->client->dev, "%s: reset disable failed", __func__); else { data->power_up = 0; LOGD(&data->client->dev, "%s: reset disable ok", __func__); } } if (data->info.gpio_avdd_enable && data->info.vdd_supply_enable && !data->is_register_regulator_cam_vio_notify) { data->cam_io_regulator_cb.notifier_call = tof_sensor_cam_io_notifier_call; data->cam_io_regulator_cb.priority = TOF_SENSOR_REGULATOR_NOTIFY_PRIORITY; if (regulator_register_notifier(data->gpio_avdd, &(data->cam_io_regulator_cb))) LOGE(&data->client->dev, "%s Regulator notification registration failed!\n", __func__); else data->is_register_regulator_cam_vio_notify = true; } if (data->info.vio_supply_enable) { rc = regulator_disable(data->vio); if (rc) { LOGE(&data->client->dev, "%s: Regulator vio disable failed ", __func__); } else { LOGE(&data->client->dev, "%s: power up vio disable OK", __func__); } } if (data->info.gpio_avdd_enable && !data->is_register_regulator_cam_vio_notify) { rc = regulator_disable(data->gpio_avdd); if (rc) { LOGE(&data->client->dev, "%s: Regulator gpio_avdd disable failed ", __func__); } else { LOGE(&data->client->dev, "%s: power up gpio_regulator disable OK", __func__); } } if (data->info.vdd_supply_enable && !data->is_register_regulator_cam_vio_notify) { rc = regulator_disable(data->avdd); if (!rc) LOGD(&data->client->dev, "%s: Regulator avdd disable OK", __func__); else LOGE(&data->client->dev, "%s: Regulator avdd disable failed ", __func__); } free_irq(data->client->irq, data); if (data->pinctrl && data->gpio_state_suspend) { rc = pinctrl_select_state(data->pinctrl, data->gpio_state_suspend); if (rc) LOGE(&data->client->dev, "%s: cannot set pin to suspend state", __func__); } } } } else LOGE(&data->client->dev, "%s: Power control data error [%ld]", __func__, value); return count; } static ssize_t tof_sensor_show_power_status(struct device *dev, struct device_attribute *attr, char *buf) { struct tof_sensor_data *data = dev_get_drvdata(dev); LOGI(dev, "Get power stat is called"); return snprintf(buf, PAGE_SIZE, "%d\n", data->power_up); } static ssize_t tof_sensor_ranging_notify(struct device *dev, struct device_attribute *attr, char *buf) { LOGI(dev, "Ranging notify is called"); return snprintf(buf, PAGE_SIZE, "%d", 1); } static ssize_t tof_sensor_get_info(struct device *dev, struct device_attribute *attr, char *buf) { struct tof_sensor_data *data = dev_get_drvdata(dev); LOGI(dev, "Get information is called"); return snprintf(buf, PAGE_SIZE, "%s,%u,%u,%u", data->info.name, data->info.i2c_client_id, data->info.need_camera_on, data->info.facing); } static DEVICE_ATTR(tof_get_info, S_IRUGO, tof_sensor_get_info, NULL); static DEVICE_ATTR(tof_ranging_notify, S_IRUGO, tof_sensor_ranging_notify, NULL); static DEVICE_ATTR(tof_power_ctl, S_IRUGO | S_IWUSR | S_IWGRP, tof_sensor_show_power_status, tof_sensor_power_ctl); static struct attribute *tof_sensor_attributes[] = { &dev_attr_tof_get_info.attr, &dev_attr_tof_ranging_notify.attr, &dev_attr_tof_power_ctl.attr, NULL }; static const struct attribute_group tof_sensor_attr_group = { .attrs = tof_sensor_attributes, }; static int tof_sensor_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tof_sensor_data *data; int rc = 0; LOGI(&client->dev, "start probing stmdata\n"); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { LOGE(&client->dev, "%s: check_functionality failed.", __func__); return -EIO; } data = kzalloc(sizeof(struct tof_sensor_data), GFP_KERNEL); if (!data) { LOGE(&client->dev, "failed: no memory to alloc data %p", data); return -ENOMEM; } i2c_set_clientdata(client, data); data->client = client; if (data->client->dev.of_node) { rc = tof_sensor_pinctrl_init(data); if (rc) { LOGE(&client->dev, "%s: failed to pinctrl init", __func__); goto exit_free_i2c; } rc = tof_sensor_parse_dt(data); if (rc) { LOGE(&client->dev, "%s: failed to parse dt", __func__); goto exit_free_i2c; } } rc = tof_sensor_power_init(data); if (rc) { LOGE(&client->dev, "%s: failed to power init", __func__); goto exit_free_i2c; } tof_sensor_gpio_init(data); data->ref_cnt = 0; data->dev_name = dev_name(&client->dev); data->input = input_allocate_device(); if (!data->input) { rc = -ENOMEM; LOGE(&client->dev, "%s: failed to allocate input device", __func__); goto exit_free_i2c; } data->input->name = "ToF Sensor"; rc = input_register_device(data->input); if (rc) { LOGE(&client->dev, "Failed to register input device"); goto exit_free_dev; } input_set_drvdata(data->input, data); LOGI(&client->dev, "tof sensor: i2c nr%d\n", client->adapter->nr); data->info.i2c_client_id = client->adapter->nr; data->power_up = 0; /* sysfs */ rc = sysfs_create_group(&data->input->dev.kobj, &tof_sensor_attr_group); if (rc) { rc = -ENOMEM; LOGE(&client->dev, "%s: failed to create sysfs group", __func__); goto exit_unregister_sysfs; } rc = sysfs_create_link(data->input->dev.kobj.parent, &data->input->dev.kobj, TOF_SENSOR_SYSFS_LINK_NAME); if (rc) { rc = -ENOMEM; LOGE(&client->dev, "%s: failed to create sysfs link", __func__); goto exit_unregister_sysfs; } return rc; exit_unregister_sysfs: input_unregister_device(data->input); exit_free_dev: input_free_device(data->input); exit_free_i2c: tof_sensor_power_deinit(data); i2c_set_clientdata(data->client, NULL); kfree(data); return rc; } static int tof_sensor_remove(struct i2c_client *client) { struct tof_sensor_data *data = i2c_get_clientdata(client); gpio_free(data->tof_reset_gpio); tof_sensor_power_deinit(data); sysfs_remove_link(&data->input->dev.kobj, TOF_SENSOR_SYSFS_LINK_NAME); input_unregister_device(data->input); input_free_device(data->input); LOGI(&data->client->dev, "Removed."); i2c_set_clientdata(data->client, NULL); kfree(data); return 0; } static struct i2c_device_id tof_sensor_id[] = { { "tof_sensor", 0 }, { }, }; MODULE_DEVICE_TABLE(i2c, tof_sensor_id); static struct of_device_id st_tof_sensor_dt_match[] = { { .compatible = "tof_sensor", }, { }, }; static struct i2c_driver tof_sensor_i2c_driver = { .driver = { .name = "tof_sensor", .owner = THIS_MODULE, .of_match_table = st_tof_sensor_dt_match, }, .probe = tof_sensor_probe, .remove = tof_sensor_remove, .id_table = tof_sensor_id, }; static int __init tof_sensor_init(void) { int rc = 0; pr_info("%s: Initialize i2c driver", __func__); rc = i2c_add_driver(&tof_sensor_i2c_driver); pr_info("%s: Added i2c driver rc = %d", __func__, rc); return rc; } static void __exit tof_sensor_exit(void) { pr_info("Delete i2c driver"); i2c_del_driver(&tof_sensor_i2c_driver); } module_init(tof_sensor_init); module_exit(tof_sensor_exit); MODULE_DESCRIPTION("Tof sensor driver"); MODULE_LICENSE("GPL v2");