Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 42a63f65 authored by Puneet Yatnal's avatar Puneet Yatnal Committed by Gerrit - the friendly Code Review server
Browse files

driver: input: sensors: SMI130 early buffer implemented



This patch allows to buffer SMI130 accel and gyro samples from driver
up to time till user space services are up and running
(or upto nth second). Buffered samples can be retrived at any time via
input subsystem by writing to below sysfs entry.
echo 1 > /sys/class/input/input*/read_acc_boot_sample
echo 1 > /sys/class/input/input*/read_gyro_boot_sample
Use config option to enable buffering feature.

Change-Id: I25109fa17bb0bea900b07c70adcb46fad34cd5c6
Signed-off-by: default avatarPuneet Yatnal <puneet@codeaurora.org>
parent 0d571478
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -127,4 +127,11 @@ depends on SENSORS_SMI_ACC4XY
	 If you say yes here, you get support for Bosch Sensortec's
	 sensor driver of SMI_ACC4XY with mag sensor support.

config ENABLE_SMI_ACC_GYRO_BUFFERING
	bool "Enable accel & gyro  boot time sensor sample buffering"
	depends on SMI130
	help
	 Say Y here if you want to buffer boot time sensor
	 samples for ASM330 accelerometer and gyroscope

endif
+305 −67
Original line number Diff line number Diff line
@@ -154,7 +154,7 @@
#include "bs_log.h"
#define DRIVER_VERSION "0.0.53.0"
#define ACC_NAME  "ACC"
#define SMI_ACC2X2_ENABLE_INT1 1
#define SMI_ACC2X2_ENABLE_INT2 1
#define CONFIG_SMI_ACC_ENABLE_NEWDATA_INT 1

#define SENSOR_NAME                 "smi130_acc"
@@ -1325,7 +1325,7 @@
#define SMI_ACC2X2_SET_BITSLICE(regvar, bitname, val)\
	((regvar & ~bitname##__MSK) | ((val<<bitname##__POS)&bitname##__MSK))

#define CHECK_CHIP_ID_TIME_MAX 5
#define CHECK_CHIP_ID_TIME_MAX 1
#define SMI_ACC255_CHIP_ID 0XFA
#define SMI_ACC250E_CHIP_ID 0XF9
#define SMI_ACC222E_CHIP_ID 0XF8
@@ -1339,7 +1339,7 @@

#define MAX_FIFO_F_LEVEL 32
#define MAX_FIFO_F_BYTES 6
#define SMI_ACC_MAX_RETRY_I2C_XFER (100)
#define SMI_ACC_MAX_RETRY_I2C_XFER (2)

#ifdef CONFIG_DOUBLE_TAP
#define DEFAULT_TAP_JUDGE_PERIOD 1000    /* default judge in 1 second */
@@ -1528,6 +1528,16 @@ struct bosch_sensor_data {
	};
};

#ifdef CONFIG_ENABLE_SMI_ACC_GYRO_BUFFERING
#define SMI_ACC_MAXSAMPLE        4000
#define G_MAX                    23920640
struct smi_acc_sample {
	int xyz[3];
	unsigned int tsec;
	unsigned long long tnsec;
};
#endif

struct smi130_accacc {
	s16 x;
	s16 y;
@@ -1590,6 +1600,17 @@ struct smi130_acc_data {
	struct timer_list	tap_timer;
	int tap_time_period;
#endif
#ifdef CONFIG_ENABLE_SMI_ACC_GYRO_BUFFERING
	bool read_acc_boot_sample;
	int acc_bufsample_cnt;
	bool acc_buffer_smi130_samples;
	bool acc_enable;
	struct kmem_cache *smi_acc_cachepool;
	struct smi_acc_sample *smi130_acc_samplist[SMI_ACC_MAXSAMPLE];
	int max_buffer_time;
	struct input_dev *accbuf_dev;
	int report_evt_cnt;
#endif
};

#ifdef CONFIG_HAS_EARLYSUSPEND
@@ -1898,8 +1919,9 @@ static int smi130_acc_check_chip_id(struct i2c_client *client,
	while (read_count++ < CHECK_CHIP_ID_TIME_MAX) {
		if (smi130_acc_smbus_read_byte(client, SMI_ACC2X2_CHIP_ID_REG,
							&chip_id) < 0) {
			PERR("Bosch Sensortec Device not found\n\n"
			PERR("Bosch Sensortec Device not found\n"
			"i2c bus read error, read chip_id:%d\n", chip_id);
			err = -ENODEV;
			continue;
		} else {
		for (i = 0; i < smi130_acc_sensor_type_count; i++) {
@@ -1907,7 +1929,7 @@ static int smi130_acc_check_chip_id(struct i2c_client *client,
				data->sensor_type =
					sensor_type_map[i].sensor_type;
				data->chip_id = chip_id;
				PINFO("Bosch Sensortec Device detected,\n\n"
				PINFO("Bosch Sensortec Device detected\n"
					" HW IC name: %s\n",
						sensor_type_map[i].sensor_name);
					return err;
@@ -1917,7 +1939,7 @@ static int smi130_acc_check_chip_id(struct i2c_client *client,
			return err;
		else {
			if (read_count == CHECK_CHIP_ID_TIME_MAX) {
				PERR("Failed! Bosch Sensortec Device\n\n"
				PERR("Failed! Bosch Sensortec Device\n"
					" not found, mismatch chip_id:%d\n",
								chip_id);
					err = -ENODEV;
@@ -4043,6 +4065,36 @@ static int smi130_acc_read_temperature(struct i2c_client *client,
	return comres;
}

#ifdef CONFIG_ENABLE_SMI_ACC_GYRO_BUFFERING
static inline int smi130_check_acc_early_buff_enable_flag(
		struct smi130_acc_data *client_data)
{
	if (client_data->acc_buffer_smi130_samples == true)
		return 1;
	else
		return 0;
}
static void smi130_check_acc_enable_flag(struct smi130_acc_data *client_data,
		unsigned long data)
{
	if (data == SMI_ACC2X2_MODE_NORMAL)
		client_data->acc_enable = true;
	else
		client_data->acc_enable = false;
}
#else
static inline int smi130_check_acc_early_buff_enable_flag(
		struct smi130_acc_data *client_data)
{
	return 0;
}
static void smi130_check_acc_enable_flag(struct smi130_acc_data *client_data,
		unsigned long data)
{

}
#endif

static ssize_t smi130_acc_enable_int_store(struct device *dev,
		struct device_attribute *attr,
		const char *buf, size_t count)
@@ -5364,6 +5416,10 @@ static ssize_t smi130_acc_range_store(struct device *dev,
	struct i2c_client *client = to_i2c_client(dev);
	struct smi130_acc_data *smi130_acc = i2c_get_clientdata(client);

	error = smi130_check_acc_early_buff_enable_flag(smi130_acc);
	if (error)
		return count;

	error = kstrtoul(buf, 10, &data);
	if (error)
		return error;
@@ -5396,6 +5452,10 @@ static ssize_t smi130_acc_bandwidth_store(struct device *dev,
	struct i2c_client *client = to_i2c_client(dev);
	struct smi130_acc_data *smi130_acc = i2c_get_clientdata(client);

	error = smi130_check_acc_early_buff_enable_flag(smi130_acc);
	if (error)
		return count;

	error = kstrtoul(buf, 10, &data);
	if (error)
		return error;
@@ -5435,9 +5495,17 @@ static ssize_t smi130_acc_mode_store(struct device *dev,
	struct i2c_client *client = to_i2c_client(dev);
	struct smi130_acc_data *smi130_acc = i2c_get_clientdata(client);


	error = kstrtoul(buf, 10, &data);
	if (error)
		return error;

	smi130_check_acc_enable_flag(smi130_acc, data);

	error = smi130_check_acc_early_buff_enable_flag(smi130_acc);
	if (error)
		return count;

	if (smi130_acc_set_mode(smi130_acc->smi130_acc_client,
		(unsigned char) data, SMI_ACC_ENABLED_BSX) < 0)
			return -EINVAL;
@@ -6431,10 +6499,97 @@ static void smi130_acc_tap_timeout_handle(unsigned long data)
}
#endif

#ifdef CONFIG_ENABLE_SMI_ACC_GYRO_BUFFERING
static int smi_acc_read_bootsampl(struct smi130_acc_data *client_data,
		unsigned long enable_read)
{
	int i = 0;

	if (enable_read) {
		client_data->acc_buffer_smi130_samples = false;
		for (i = 0; i < client_data->acc_bufsample_cnt; i++) {
			if (client_data->debug_level & 0x08)
				PINFO("acc=%d,x=%d,y=%d,z=%d,sec=%d,ns=%lld\n",
				i, client_data->smi130_acc_samplist[i]->xyz[0],
				client_data->smi130_acc_samplist[i]->xyz[1],
				client_data->smi130_acc_samplist[i]->xyz[2],
				client_data->smi130_acc_samplist[i]->tsec,
				client_data->smi130_acc_samplist[i]->tnsec);
			input_report_abs(client_data->accbuf_dev, ABS_X,
				client_data->smi130_acc_samplist[i]->xyz[0]);
			input_report_abs(client_data->accbuf_dev, ABS_Y,
				client_data->smi130_acc_samplist[i]->xyz[1]);
			input_report_abs(client_data->accbuf_dev, ABS_Z,
				client_data->smi130_acc_samplist[i]->xyz[2]);
			input_report_abs(client_data->accbuf_dev, ABS_RX,
				client_data->smi130_acc_samplist[i]->tsec);
			input_report_abs(client_data->accbuf_dev, ABS_RY,
				client_data->smi130_acc_samplist[i]->tnsec);
			input_sync(client_data->accbuf_dev);
		}
	} else {
		/* clean up */
		if (client_data->acc_bufsample_cnt != 0) {
			for (i = 0; i < SMI_ACC_MAXSAMPLE; i++)
				kmem_cache_free(client_data->smi_acc_cachepool,
					client_data->smi130_acc_samplist[i]);
			kmem_cache_destroy(client_data->smi_acc_cachepool);
			client_data->acc_bufsample_cnt = 0;
		}

	}
	/*SYN_CONFIG indicates end of data*/
	input_event(client_data->accbuf_dev, EV_SYN, SYN_CONFIG, 0xFFFFFFFF);
	input_sync(client_data->accbuf_dev);
	if (client_data->debug_level & 0x08)
		PINFO("End of acc samples bufsample_cnt=%d\n",
				client_data->acc_bufsample_cnt);
	return 0;
}
static ssize_t read_acc_boot_sample_show(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct smi130_acc_data *smi130_acc = i2c_get_clientdata(client);

	return snprintf(buf, 16, "%u\n",
			smi130_acc->read_acc_boot_sample);
}

static ssize_t read_acc_boot_sample_store(struct device *dev,
		struct device_attribute *attr,
		const char *buf, size_t count)
{
	int err;
	struct i2c_client *client = to_i2c_client(dev);
	struct smi130_acc_data *smi130_acc = i2c_get_clientdata(client);
	unsigned long enable = 0;

	err = kstrtoul(buf, 10, &enable);
	if (err)
		return err;
	if (enable > 1) {
		PERR("Invalid value of input, input=%ld\n", enable);
		return -EINVAL;
	}
	err = smi_acc_read_bootsampl(smi130_acc, enable);
	if (err)
		return err;

	smi130_acc->read_acc_boot_sample = enable;
	return count;
}
#endif

static DEVICE_ATTR(range, S_IRUGO | S_IWUSR,
		smi130_acc_range_show, smi130_acc_range_store);
static DEVICE_ATTR(bandwidth, S_IRUGO | S_IWUSR,
		smi130_acc_bandwidth_show, smi130_acc_bandwidth_store);
#ifdef CONFIG_ENABLE_SMI_ACC_GYRO_BUFFERING
static DEVICE_ATTR(read_acc_boot_sample, 0644,
		read_acc_boot_sample_show, read_acc_boot_sample_store);
#endif
static DEVICE_ATTR(op_mode, S_IRUGO | S_IWUSR,
		smi130_acc_mode_show, smi130_acc_mode_store);
static DEVICE_ATTR(value, S_IRUSR,
@@ -6554,6 +6709,9 @@ static DEVICE_ATTR(en_double_tap, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
static struct attribute *smi130_acc_attributes[] = {
	&dev_attr_range.attr,
	&dev_attr_bandwidth.attr,
#ifdef CONFIG_ENABLE_SMI_ACC_GYRO_BUFFERING
	&dev_attr_read_acc_boot_sample.attr,
#endif
	&dev_attr_op_mode.attr,
	&dev_attr_value.attr,
	&dev_attr_value_cache.attr,
@@ -6751,6 +6909,140 @@ static void smi130_acc_slope_interrupt_handle(struct smi130_acc_data *smi130_acc
	}
}
#endif
#ifdef CONFIG_ENABLE_SMI_ACC_GYRO_BUFFERING
static void store_acc_boot_sample(struct smi130_acc_data *client_data,
				int x, int y, int z, struct timespec ts)
{
	if (false == client_data->acc_buffer_smi130_samples)
		return;
	if (ts.tv_sec <  client_data->max_buffer_time) {
		if (client_data->acc_bufsample_cnt < SMI_ACC_MAXSAMPLE) {
			client_data->smi130_acc_samplist[client_data
				->acc_bufsample_cnt]->xyz[0] = x;
			client_data->smi130_acc_samplist[client_data
				->acc_bufsample_cnt]->xyz[1] = y;
			client_data->smi130_acc_samplist[client_data
				->acc_bufsample_cnt]->xyz[2] = z;
			client_data->smi130_acc_samplist[client_data
				->acc_bufsample_cnt]->tsec = ts.tv_sec;
			client_data->smi130_acc_samplist[client_data
				->acc_bufsample_cnt]->tnsec = ts.tv_nsec;
			client_data->acc_bufsample_cnt++;
		}
	} else {
		PINFO("End of ACC buffering %d\n",
				client_data->acc_bufsample_cnt);
		client_data->acc_buffer_smi130_samples = false;
		if (client_data->acc_enable == false)
			smi130_acc_set_mode(client_data->smi130_acc_client,
					SMI_ACC2X2_MODE_SUSPEND, 1);
	}
}
#else
static void store_acc_boot_sample(struct smi130_acc_data *client_data,
		int x, int y, int z, struct timespec ts)
{
}
#endif

#ifdef CONFIG_ENABLE_SMI_ACC_GYRO_BUFFERING
static int smi130_acc_early_buff_init(struct i2c_client *client,
			struct smi130_acc_data *client_data)
{
	int i = 0, err = 0;

	client_data->acc_bufsample_cnt = 0;
	client_data->report_evt_cnt = 5;
	client_data->max_buffer_time = 40;

	client_data->smi_acc_cachepool = kmem_cache_create("acc_sensor_sample",
			sizeof(struct smi_acc_sample),
			0,
			SLAB_HWCACHE_ALIGN, NULL);
	if (!client_data->smi_acc_cachepool) {
		PERR("smi_acc_cachepool cache create failed\n");
		err = -ENOMEM;
		goto clean_exit1;
	}
	for (i = 0; i < SMI_ACC_MAXSAMPLE; i++) {
		client_data->smi130_acc_samplist[i] =
			kmem_cache_alloc(client_data->smi_acc_cachepool,
					GFP_KERNEL);
		if (!client_data->smi130_acc_samplist[i]) {
			err = -ENOMEM;
			goto clean_exit2;
		}
	}

	client_data->accbuf_dev = input_allocate_device();
	if (!client_data->accbuf_dev) {
		err = -ENOMEM;
		PERR("input device allocation failed\n");
		goto clean_exit3;
	}
	client_data->accbuf_dev->name = "smi130_accbuf";
	client_data->accbuf_dev->id.bustype = BUS_I2C;
	input_set_events_per_packet(client_data->accbuf_dev,
			client_data->report_evt_cnt * SMI_ACC_MAXSAMPLE);
	set_bit(EV_ABS, client_data->accbuf_dev->evbit);
	input_set_abs_params(client_data->accbuf_dev, ABS_X,
			-G_MAX, G_MAX, 0, 0);
	input_set_abs_params(client_data->accbuf_dev, ABS_Y,
			-G_MAX, G_MAX, 0, 0);
	input_set_abs_params(client_data->accbuf_dev, ABS_Z,
			-G_MAX, G_MAX, 0, 0);
	input_set_abs_params(client_data->accbuf_dev, ABS_RX,
			-G_MAX, G_MAX, 0, 0);
	input_set_abs_params(client_data->accbuf_dev, ABS_RY,
			-G_MAX, G_MAX, 0, 0);
	err = input_register_device(client_data->accbuf_dev);
	if (err) {
		PERR("unable to register input device %s\n",
				client_data->accbuf_dev->name);
		goto clean_exit3;
	}

	client_data->acc_buffer_smi130_samples = true;
	client_data->acc_enable = false;

	smi130_acc_set_mode(client, SMI_ACC2X2_MODE_NORMAL, 1);
	smi130_acc_set_bandwidth(client, SMI_ACC2X2_BW_62_50HZ);
	smi130_acc_set_range(client, SMI_ACC2X2_RANGE_2G);

	return 1;

clean_exit3:
	input_free_device(client_data->accbuf_dev);
clean_exit2:
	for (i = 0; i < SMI_ACC_MAXSAMPLE; i++)
		kmem_cache_free(client_data->smi_acc_cachepool,
				client_data->smi130_acc_samplist[i]);
clean_exit1:
	kmem_cache_destroy(client_data->smi_acc_cachepool);
	return 0;
}

static void smi130_acc_input_cleanup(struct smi130_acc_data *client_data)
{
	int i = 0;

	input_unregister_device(client_data->accbuf_dev);
	input_free_device(client_data->accbuf_dev);
	for (i = 0; i < SMI_ACC_MAXSAMPLE; i++)
		kmem_cache_free(client_data->smi_acc_cachepool,
				client_data->smi130_acc_samplist[i]);
	kmem_cache_destroy(client_data->smi_acc_cachepool);
}
#else
static int smi130_acc_early_buff_init(struct i2c_client *client,
			struct smi130_acc_data *client_data)
{
	return 1;
}
static void smi130_acc_input_cleanup(struct smi130_acc_data *client_data)
{
}
#endif

static void smi130_acc_irq_work_func(struct work_struct *work)
{
@@ -6799,10 +7091,10 @@ static void smi130_acc_irq_work_func(struct work_struct *work)
		smi130_acc->value = acc;
		mutex_unlock(&smi130_acc->value_mutex);
	}
	store_acc_boot_sample(smi130_acc, acc.x, acc.y, acc.z, ts);
#endif

	smi130_acc_get_interruptstatus1(smi130_acc->smi130_acc_client, &status);
	PINFO("smi130_acc_irq_work_func, status = 0x%x\n", status);

#ifdef CONFIG_SIG_MOTION
	if (status & 0x04)	{
@@ -7267,6 +7559,11 @@ static int smi130_acc_probe(struct i2c_client *client,
	PDEBUG("data->IRQ = %d", data->IRQ);
	err = request_irq(data->IRQ, smi130_acc_irq_handler, IRQF_TRIGGER_RISING,
			"smi130_acc", data);

	err = smi130_acc_early_buff_init(client, data);
	if (!err)
		goto exit;

	PINFO("SMI130_ACC driver probe successfully");

	return 0;
@@ -7378,6 +7675,7 @@ static int smi130_acc_remove(struct i2c_client *client)
	if (NULL == data)
		return 0;

	smi130_acc_input_cleanup(data);
	smi130_acc_set_enable(&client->dev, 0);
#ifdef CONFIG_HAS_EARLYSUSPEND
	unregister_early_suspend(&data->early_suspend);
@@ -7404,64 +7702,6 @@ void smi130_acc_shutdown(struct i2c_client *client)
	mutex_unlock(&data->enable_mutex);
}

#ifdef CONFIG_PM
static int smi130_acc_suspend(struct i2c_client *client, pm_message_t mesg)
{
	struct smi130_acc_data *data = i2c_get_clientdata(client);

	mutex_lock(&data->enable_mutex);
	if (atomic_read(&data->enable) == 1) {
		smi130_acc_set_mode(data->smi130_acc_client,
			SMI_ACC2X2_MODE_SUSPEND, SMI_ACC_ENABLED_INPUT);
#ifndef CONFIG_SMI_ACC_ENABLE_NEWDATA_INT
		cancel_delayed_work_sync(&data->work);
#endif
	}
	if (data->is_timer_running) {
		hrtimer_cancel(&data->timer);
		data->base_time = 0;
		data->timestamp = 0;
		data->fifo_time = 0;
		data->acc_count = 0;
	}
	mutex_unlock(&data->enable_mutex);

	return 0;
}

static int smi130_acc_resume(struct i2c_client *client)
{
	struct smi130_acc_data *data = i2c_get_clientdata(client);

	mutex_lock(&data->enable_mutex);
	if (atomic_read(&data->enable) == 1) {
		smi130_acc_set_mode(data->smi130_acc_client,
			SMI_ACC2X2_MODE_NORMAL, SMI_ACC_ENABLED_INPUT);
#ifndef CONFIG_SMI_ACC_ENABLE_NEWDATA_INT
		schedule_delayed_work(&data->work,
				msecs_to_jiffies(atomic_read(&data->delay)));
#endif
	}
	if (data->is_timer_running) {
		hrtimer_start(&data->timer,
					ns_to_ktime(data->time_odr),
			HRTIMER_MODE_REL);
		data->base_time = 0;
		data->timestamp = 0;
		data->is_timer_running = 1;
	}
	mutex_unlock(&data->enable_mutex);

	return 0;
}

#else

#define smi130_acc_suspend      NULL
#define smi130_acc_resume       NULL

#endif /* CONFIG_PM */

static const struct i2c_device_id smi130_acc_id[] = {
	{ SENSOR_NAME, 0 },
	{ }
@@ -7480,8 +7720,6 @@ static struct i2c_driver smi130_acc_driver = {
		.name   = SENSOR_NAME,
		.of_match_table = smi130_acc_of_match,
	},
	//.suspend    = smi130_acc_suspend,
	//.resume     = smi130_acc_resume,
	.id_table   = smi130_acc_id,
	.probe      = smi130_acc_probe,
	.remove     = smi130_acc_remove,
+333 −52

File changed.

Preview size limit exceeded, changes collapsed.