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

Commit fae1faee authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "input: misc: enable mma8x5x interrupt mode"

parents 6dea3019 89914ec4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ Optional properties:
 - interrupt-parent	: parent of interrupt.
 - interrupts		: sensor signal interrupt to indicate new data ready or
				 a motion is detected.
 - fsl,use-interrupt	: to enable interrupt mode.
 - fsl,irq-gpio	: First irq gpio which is to provide interrupts to host, same
				 as "interrupts" node. It will also contain active low or active
				 high information.
@@ -36,4 +37,5 @@ Example:
		vio-supply = <&pm8110_l14>;
		fsl,sensors-position = <5>;
		fsl,irq-gpio = <&msmgpio 0x81 0x02>;
		fsl,use-interrupt;
	};
 No newline at end of file
+14 −1
Original line number Diff line number Diff line
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -450,6 +450,12 @@ static struct gpiomux_setting cam_settings[] = {
	},
};

static struct gpiomux_setting accel_interrupt_config = {
	.func = GPIOMUX_FUNC_GPIO,
	.drv = GPIOMUX_DRV_6MA,
	.pull = GPIOMUX_PULL_DOWN,
};

static struct msm_gpiomux_config msm_non_qrd_configs[] __initdata = {
	{
		.gpio = 8, /* CAM1_STANDBY_N */
@@ -458,6 +464,13 @@ static struct msm_gpiomux_config msm_non_qrd_configs[] __initdata = {
			[GPIOMUX_SUSPENDED] = &gpio_suspend_config[1],
		},
	},
	{
		.gpio = 81,	/*ACCEL_INT1 */
		.settings = {
			[GPIOMUX_ACTIVE]    = &accel_interrupt_config,
			[GPIOMUX_SUSPENDED] = &accel_interrupt_config,
		},
	},
};

static struct msm_gpiomux_config msm_sensor_configs[] __initdata = {
+312 −57
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <linux/sensors.h>
#include <linux/regulator/consumer.h>
#include <linux/of_gpio.h>
#include <linux/irq.h>

#define ACCEL_INPUT_DEV_NAME		"accelerometer"
#define MMA8451_ID			0x1A
@@ -41,7 +42,7 @@
/* Polling delay in msecs */
#define POLL_INTERVAL_MIN	1
#define POLL_INTERVAL_MAX	10000
#define POLL_INTERVAL		100
#define POLL_INTERVAL		100 /* msecs */

/* if sensor is standby ,set POLL_STOP_TIME to slow down the poll */
#define POLL_STOP_TIME		10000
@@ -74,6 +75,13 @@ static struct sensors_classdev sensors_cdev = {
	.sensors_poll_delay = NULL,
};

#define MMA_WAKE_CFG		0x02
#define MMA_INT_EN_DRDY		0x01
#define MMA_INT_EN_FF_MT	0x04
#define MMA_INT_ROUTING_CFG	0x01

#define MMA_POWER_CFG_MASK	0xFE

struct sensor_regulator {
	struct regulator *vreg;
	const char *name;
@@ -86,6 +94,23 @@ static struct sensor_regulator mma_vreg[] = {
	{NULL, "vio", 1800000, 1800000},
};

struct mma_odr_selection_table {
	u32 odr_cfg;
	u32 delay_bottom;
	u32 delay_top;
};

static struct mma_odr_selection_table mma_odr_table[] = {
	{0x00, 0, 1500},
	{0x08, 1501, 3500},
	{0x10, 3501, 7500},
	{0x18, 7501, 15000},
	{0x20, 15001, 50000},
	{0x28, 50001, 120000},
	{0x30, 120001, 400000},
	{0x38, 400001, 10000000},
};

/* register enum for mma8x5x registers */
enum {
	MMA8X5X_STATUS = 0x00,
@@ -165,6 +190,7 @@ struct mma8x5x_data_axis {
struct mma8x5x_data {
	struct i2c_client *client;
	struct input_polled_dev *poll_dev;
	struct input_dev *idev;
	struct mutex data_lock;
	struct sensors_classdev cdev;
	int active;
@@ -174,6 +200,7 @@ struct mma8x5x_data {
	int int_pin;
	u32 int_flags;
	int poll_delay;
	bool use_int;
};
/* Addresses scanned */
static const unsigned short normal_i2c[] = {0x1c, 0x1d, I2C_CLIENT_END};
@@ -202,7 +229,7 @@ static int mma8x5x_position_setting[8][3][3] = {
	{{ 0,  1,  0}, { 1,  0,	0}, {0, 0,  -1} },
	{{ 1,  0,  0}, { 0, -1,	0}, {0, 0,  -1} },
};

static struct mma8x5x_data *drv_data;
static int mma8x5x_config_regulator(struct i2c_client *client, bool on)
{
	int rc = 0, i;
@@ -348,6 +375,98 @@ err_out:
	return -EIO;
}

static int mma8x5x_delay2odr(u32 delay_ms)
{
	int i;
	u32 delay_us;

	delay_us = delay_ms * 1000;
	for (i = 0; i < sizeof(mma_odr_table) /
		sizeof(struct mma_odr_selection_table); i++) {
		if ((delay_us <= mma_odr_table[i].delay_top) &&
			(delay_us > mma_odr_table[i].delay_bottom))
			break;
	}
	if (i < sizeof(mma_odr_table) /
		sizeof(struct mma_odr_selection_table))
		return mma_odr_table[i].odr_cfg;
	else
		return -EINVAL;
}

static int mma8x5x_device_set_odr(struct i2c_client *client, u32 delay_ms)
{
	int result;
	u8 val;

	result = mma8x5x_delay2odr(delay_ms);
	if (result < 0)
		goto out;
	val = (u8)result;

	result = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
	if (result < 0)
		goto out;

	val = (u8)result | val;
	result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1,
					   (val & MMA_POWER_CFG_MASK));
	if (result < 0)
		goto out;

	result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG4,
					   MMA_INT_EN_DRDY);
	if (result < 0)
		goto out;

	result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG5,
					   MMA_INT_ROUTING_CFG);
	if (result < 0)
		goto out;

	result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1,
					   val);
	if (result < 0)
		goto out;

	return 0;
out:
	dev_err(&client->dev, "error when set ODR mma8x5x:(%d)", result);
	return result;
}
static int mma8x5x_device_int_init(struct i2c_client *client)
{
	struct mma8x5x_data *pdata = i2c_get_clientdata(client);
	int result;
	int val;

	result = mma8x5x_device_set_odr(client, pdata->poll_delay);
	if (result < 0)
		goto out;

	val = MMA_WAKE_CFG;
	result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG3,
					   val);
	if (result < 0)
		goto out;

	val = MMA_INT_EN_DRDY;
	result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG4,
					   val);
	if (result < 0)
		goto out;

	val = MMA_INT_ROUTING_CFG;
	result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG5,
					   val);
	if (result < 0)
		goto out;

	return 0;
out:
	dev_err(&client->dev, "error when int init mma8x5x:(%d)", result);
	return result;
}
static int mma8x5x_read_data(struct i2c_client *client,
		struct mma8x5x_data_axis *data)
{
@@ -396,6 +515,28 @@ static void mma8x5x_dev_poll(struct input_polled_dev *dev)
	mma8x5x_report_data(pdata);
}

static irqreturn_t mma8x5x_interrupt(int vec, void *data)
{
	struct i2c_client *client = (struct i2c_client *)data;
	struct mma8x5x_data *pdata = i2c_get_clientdata(client);
	struct input_dev *idev = pdata->idev;
	struct mma8x5x_data_axis data_axis;

	mutex_lock(&pdata->data_lock);

	if (mma8x5x_read_data(pdata->client, &data_axis) != 0)
		goto out;
	mma8x5x_data_convert(pdata, &data_axis);
	input_report_abs(idev, ABS_X, data_axis.x);
	input_report_abs(idev, ABS_Y, data_axis.y);
	input_report_abs(idev, ABS_Z, data_axis.z);
	input_sync(idev);
out:
	mutex_unlock(&pdata->data_lock);

	return IRQ_HANDLED;
}

static int mma8x5x_enable_set(struct sensors_classdev *sensors_cdev,
		unsigned int enable)
{
@@ -416,6 +557,9 @@ static int mma8x5x_enable_set(struct sensors_classdev *sensors_cdev,
			if (ret)
				goto err_failed;

			ret = mma8x5x_device_set_odr(client, pdata->poll_delay);
			if (ret)
				goto err_failed;
			pdata->active &= ~MMA_SHUTTEDDOWN;
		}
		if (pdata->active == MMA_STANDBY) {
@@ -472,12 +616,17 @@ err_failed:
static ssize_t mma8x5x_enable_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
	struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
	struct i2c_client *client = pdata->client;
	struct mma8x5x_data *pdata = drv_data;
	struct i2c_client *client;
	u8 val;
	int enable;

	if (!pdata) {
		dev_err(dev, "Invalid driver private data!");
		return -EINVAL;
	}
	client = pdata->client;

	mutex_lock(&pdata->data_lock);
	val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
	if ((val & 0x01) && pdata->active == MMA_ACTIVED)
@@ -492,11 +641,16 @@ static ssize_t mma8x5x_enable_store(struct device *dev,
				    struct device_attribute *attr,
				    const char *buf, size_t count)
{
	struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
	struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
	struct mma8x5x_data *pdata = drv_data;
	struct i2c_client *client;
	int ret;
	unsigned long enable;

	if (!pdata) {
		dev_err(dev, "Invalid driver private data!");
		return -EINVAL;
	}
	client = pdata->client;
	ret = kstrtoul(buf, 10, &enable);
	if (ret)
		return ret;
@@ -509,9 +663,13 @@ static ssize_t mma8x5x_enable_store(struct device *dev,
static ssize_t mma8x5x_position_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
	struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
	struct mma8x5x_data *pdata = drv_data;
	int position = 0;

	if (!pdata) {
		dev_err(dev, "Invalid driver private data!");
		return -EINVAL;
	}
	mutex_lock(&pdata->data_lock);
	position = pdata->position ;
	mutex_unlock(&pdata->data_lock);
@@ -522,10 +680,14 @@ static ssize_t mma8x5x_position_store(struct device *dev,
				    struct device_attribute *attr,
				    const char *buf, size_t count)
{
	struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
	struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
	struct mma8x5x_data *pdata = drv_data;
	int position;
	int ret;

	if (!pdata) {
		dev_err(dev, "Invalid driver private data!");
		return -EINVAL;
	}
	ret = kstrtoint(buf, 10, &position);
	if (ret)
		return ret;
@@ -540,11 +702,21 @@ static int mma8x5x_poll_delay_set(struct sensors_classdev *sensors_cdev,
{
	struct mma8x5x_data *pdata = container_of(sensors_cdev,
			struct mma8x5x_data, cdev);
	int ret;

	if (pdata->use_int) {
		mutex_lock(&pdata->data_lock);
		pdata->poll_delay = delay_ms;
		ret = mma8x5x_device_set_odr(pdata->client, delay_ms);
		mutex_unlock(&pdata->data_lock);
		if (ret < 0)
			return ret;
	} else {
		mutex_lock(&pdata->data_lock);
		pdata->poll_delay = delay_ms;
		pdata->poll_dev->poll_interval = pdata->poll_delay;
		mutex_unlock(&pdata->data_lock);
	}

	return 0;
}
@@ -552,8 +724,13 @@ static int mma8x5x_poll_delay_set(struct sensors_classdev *sensors_cdev,
static ssize_t mma8x5x_poll_delay_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
	struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
	struct mma8x5x_data *pdata = drv_data;

	if (!pdata) {
		dev_err(dev, "Invalid driver private data!");
		return -EINVAL;
	}

	return snprintf(buf, PAGE_SIZE, "%d\n", pdata->poll_delay);
}

@@ -561,19 +738,23 @@ static ssize_t mma8x5x_poll_delay_store(struct device *dev,
				    struct device_attribute *attr,
				    const char *buf, size_t count)
{
	struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
	struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
	int interval;
	struct mma8x5x_data *pdata = drv_data;
	int delay;
	int ret;
	ret = kstrtoint(buf, 10, &interval);

	if (!pdata) {
		dev_err(dev, "Invalid driver private data!");
		return -EINVAL;
	}
	ret = kstrtoint(buf, 10, &delay);
	if (ret)
		return ret;
	if (interval <= POLL_INTERVAL_MIN)
		interval = POLL_INTERVAL_MIN;
	if (interval > POLL_INTERVAL_MAX)
		interval = POLL_INTERVAL_MAX;
	if (delay <= POLL_INTERVAL_MIN)
		delay = POLL_INTERVAL_MIN;
	if (delay > POLL_INTERVAL_MAX)
		delay = POLL_INTERVAL_MAX;

	mma8x5x_poll_delay_set(&pdata->cdev, interval);
	mma8x5x_poll_delay_set(&pdata->cdev, delay);

	return count;
}
@@ -632,6 +813,8 @@ static int mma8x5x_parse_dt(struct device *dev, struct mma8x5x_data *data)
		return rc;
	}

	data->use_int = of_property_read_bool(np, "fsl,use-interrupt");

	return 0;
}

@@ -676,7 +859,7 @@ static int mma8x5x_probe(struct i2c_client *client,
	if (client->dev.of_node) {
		result = mma8x5x_parse_dt(&client->dev, pdata);
		if (result)
			return result;
			goto err_parse_dt;
	} else {
		pdata->position = CONFIG_SENSORS_MMA_POSITION;
		pdata->int_pin = -1;
@@ -684,15 +867,69 @@ static int mma8x5x_probe(struct i2c_client *client,
	}

	/* Initialize the MMA8X5X chip */
	drv_data = pdata;
	pdata->client = client;
	pdata->chip_id = chip_id;
	pdata->mode = MODE_2G;
	pdata->poll_delay = POLL_INTERVAL;
	pdata->poll_delay = POLL_STOP_TIME;
	pdata->poll_dev = NULL;

	mutex_init(&pdata->data_lock);
	i2c_set_clientdata(client, pdata);
	/* Initialize the MMA8X5X chip */
	mma8x5x_device_init(client);
	if (pdata->use_int) {
		if (pdata->int_pin >= 0)
			client->irq = gpio_to_irq(pdata->int_pin);

		if (gpio_is_valid(pdata->int_pin)) {
			result = gpio_request(pdata->int_pin,
				"mma8x5x_irq_gpio");
			if (result) {
				dev_err(&client->dev, "irq gpio(%d) request failed",
					pdata->int_pin);
				goto err_request_gpio;
			}
			result = gpio_direction_input(pdata->int_pin);
			if (result) {
				dev_err(&client->dev,
					"set_direction for irq gpio failed\n");
				goto err_set_direction;
			}
		}
		idev = input_allocate_device();
		if (!idev) {
			result = -ENOMEM;
			dev_err(&client->dev, "alloc input device failed!\n");
			goto err_alloc_poll_device;
		}
		input_set_drvdata(idev, pdata);
		idev->name = ACCEL_INPUT_DEV_NAME;
		idev->uniq = mma8x5x_id2name(pdata->chip_id);
		idev->id.bustype = BUS_I2C;
		idev->evbit[0] = BIT_MASK(EV_ABS);
		input_set_abs_params(idev, ABS_X, -0x7fff, 0x7fff, 0, 0);
		input_set_abs_params(idev, ABS_Y, -0x7fff, 0x7fff, 0, 0);
		input_set_abs_params(idev, ABS_Z, -0x7fff, 0x7fff, 0, 0);
		result = input_register_device(idev);
		if (result) {
			dev_err(&client->dev, "register input device failed!\n");
			goto err_register_device;
		}
		pdata->idev = idev;
		device_init_wakeup(&client->dev, true);
		enable_irq_wake(client->irq);
		result = request_threaded_irq(client->irq, NULL,
			mma8x5x_interrupt,
			IRQ_TYPE_EDGE_RISING | IRQF_ONESHOT | IRQF_NO_SUSPEND,
			ACCEL_INPUT_DEV_NAME, (void *)client);
		if (result) {
			dev_err(&client->dev, "Could not allocate irq(%d) !\n",
				client->irq);
			goto err_register_irq;
		}
		mma8x5x_device_int_init(client);
	} else {
		/* create the input poll device */
		poll_dev = input_allocate_polled_device();
		if (!poll_dev) {
@@ -700,6 +937,8 @@ static int mma8x5x_probe(struct i2c_client *client,
			dev_err(&client->dev, "alloc poll device failed!\n");
			goto err_alloc_poll_device;
		}
		pdata->poll_dev = poll_dev;
		pdata->idev = NULL;
		poll_dev->poll = mma8x5x_dev_poll;
		poll_dev->poll_interval = POLL_STOP_TIME;
		poll_dev->poll_interval_min = POLL_INTERVAL_MIN;
@@ -713,11 +952,11 @@ static int mma8x5x_probe(struct i2c_client *client,
		input_set_abs_params(idev, ABS_X, -0x7fff, 0x7fff, 0, 0);
		input_set_abs_params(idev, ABS_Y, -0x7fff, 0x7fff, 0, 0);
		input_set_abs_params(idev, ABS_Z, -0x7fff, 0x7fff, 0, 0);
	pdata->poll_dev = poll_dev;
		result = input_register_polled_device(pdata->poll_dev);
		if (result) {
			dev_err(&client->dev, "register poll device failed!\n");
		goto err_register_polled_device;
			goto err_register_device;
		}
	}
	result = sysfs_create_group(&idev->dev.kobj, &mma8x5x_attr_group);
	if (result) {
@@ -727,7 +966,7 @@ static int mma8x5x_probe(struct i2c_client *client,
	}
	pdata->cdev = sensors_cdev;
	pdata->cdev.min_delay = POLL_INTERVAL_MIN * 1000;
	pdata->cdev.delay_msec = poll_dev->poll_interval;
	pdata->cdev.delay_msec = pdata->poll_delay;
	pdata->cdev.sensors_enable = mma8x5x_enable_set;
	pdata->cdev.sensors_poll_delay = mma8x5x_poll_delay_set;
	result = sensors_classdev_register(&client->dev, &pdata->cdev);
@@ -745,9 +984,20 @@ err_create_class_sysfs:
	sysfs_remove_group(&idev->dev.kobj, &mma8x5x_attr_group);
err_create_sysfs:
	input_unregister_polled_device(pdata->poll_dev);
err_register_polled_device:
	input_free_polled_device(poll_dev);
err_register_irq:
	if (pdata->use_int)
		device_init_wakeup(&client->dev, false);
err_register_device:
	if (pdata->use_int)
		input_free_device(idev);
	else
		input_free_polled_device(pdata->poll_dev);
err_alloc_poll_device:
err_set_direction:
	if (gpio_is_valid(pdata->int_pin) && pdata->use_int)
		gpio_free(pdata->int_pin);
err_request_gpio:
err_parse_dt:
	kfree(pdata);
err_out:
	mma8x5x_config_regulator(client, 0);
@@ -773,6 +1023,9 @@ static int mma8x5x_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct mma8x5x_data *pdata = i2c_get_clientdata(client);

	if (pdata->use_int && pdata->active == MMA_ACTIVED)
		return 0;
	if (pdata->active == MMA_ACTIVED)
		mma8x5x_device_stop(client);
	if (pdata->active & MMA_SHUTTEDDOWN)
@@ -789,6 +1042,8 @@ static int mma8x5x_resume(struct device *dev)
	struct i2c_client *client = to_i2c_client(dev);
	struct mma8x5x_data *pdata = i2c_get_clientdata(client);

	if (pdata->use_int && pdata->active == MMA_ACTIVED)
		return 0;
	/* No need to power on while device is shutdowned from standby state */
	if (pdata->active == (MMA_SHUTTEDDOWN | MMA_STANDBY))
		return 0;