* Sony package version: 52.1.A.0.618 Change-Id: I7717499f3ce8ac85d1c4e966a49fe01d463a8093 Signed-off-by: Sorayukii <sorayukii69@gmail.com>
696 lines
18 KiB
C
696 lines
18 KiB
C
/*
|
|
* 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 <linux/uaccess.h>
|
|
#include <linux/kobject.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/gpio.h>
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/input.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
|
|
#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");
|