18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2011 Kionix, Inc.
48c2ecf20Sopenharmony_ci * Written by Chris Hudson <chudson@kionix.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/i2c.h>
98c2ecf20Sopenharmony_ci#include <linux/input.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/input/kxtj9.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define NAME			"kxtj9"
168c2ecf20Sopenharmony_ci#define G_MAX			8000
178c2ecf20Sopenharmony_ci/* OUTPUT REGISTERS */
188c2ecf20Sopenharmony_ci#define XOUT_L			0x06
198c2ecf20Sopenharmony_ci#define WHO_AM_I		0x0F
208c2ecf20Sopenharmony_ci/* CONTROL REGISTERS */
218c2ecf20Sopenharmony_ci#define INT_REL			0x1A
228c2ecf20Sopenharmony_ci#define CTRL_REG1		0x1B
238c2ecf20Sopenharmony_ci#define INT_CTRL1		0x1E
248c2ecf20Sopenharmony_ci#define DATA_CTRL		0x21
258c2ecf20Sopenharmony_ci/* CONTROL REGISTER 1 BITS */
268c2ecf20Sopenharmony_ci#define PC1_OFF			0x7F
278c2ecf20Sopenharmony_ci#define PC1_ON			(1 << 7)
288c2ecf20Sopenharmony_ci/* Data ready funtion enable bit: set during probe if using irq mode */
298c2ecf20Sopenharmony_ci#define DRDYE			(1 << 5)
308c2ecf20Sopenharmony_ci/* DATA CONTROL REGISTER BITS */
318c2ecf20Sopenharmony_ci#define ODR12_5F		0
328c2ecf20Sopenharmony_ci#define ODR25F			1
338c2ecf20Sopenharmony_ci#define ODR50F			2
348c2ecf20Sopenharmony_ci#define ODR100F		3
358c2ecf20Sopenharmony_ci#define ODR200F		4
368c2ecf20Sopenharmony_ci#define ODR400F		5
378c2ecf20Sopenharmony_ci#define ODR800F		6
388c2ecf20Sopenharmony_ci/* INTERRUPT CONTROL REGISTER 1 BITS */
398c2ecf20Sopenharmony_ci/* Set these during probe if using irq mode */
408c2ecf20Sopenharmony_ci#define KXTJ9_IEL		(1 << 3)
418c2ecf20Sopenharmony_ci#define KXTJ9_IEA		(1 << 4)
428c2ecf20Sopenharmony_ci#define KXTJ9_IEN		(1 << 5)
438c2ecf20Sopenharmony_ci/* INPUT_ABS CONSTANTS */
448c2ecf20Sopenharmony_ci#define FUZZ			3
458c2ecf20Sopenharmony_ci#define FLAT			3
468c2ecf20Sopenharmony_ci/* RESUME STATE INDICES */
478c2ecf20Sopenharmony_ci#define RES_DATA_CTRL		0
488c2ecf20Sopenharmony_ci#define RES_CTRL_REG1		1
498c2ecf20Sopenharmony_ci#define RES_INT_CTRL1		2
508c2ecf20Sopenharmony_ci#define RESUME_ENTRIES		3
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * The following table lists the maximum appropriate poll interval for each
548c2ecf20Sopenharmony_ci * available output data rate.
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_cistatic const struct {
578c2ecf20Sopenharmony_ci	unsigned int cutoff;
588c2ecf20Sopenharmony_ci	u8 mask;
598c2ecf20Sopenharmony_ci} kxtj9_odr_table[] = {
608c2ecf20Sopenharmony_ci	{ 3,	ODR800F },
618c2ecf20Sopenharmony_ci	{ 5,	ODR400F },
628c2ecf20Sopenharmony_ci	{ 10,	ODR200F },
638c2ecf20Sopenharmony_ci	{ 20,	ODR100F },
648c2ecf20Sopenharmony_ci	{ 40,	ODR50F  },
658c2ecf20Sopenharmony_ci	{ 80,	ODR25F  },
668c2ecf20Sopenharmony_ci	{ 0,	ODR12_5F},
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistruct kxtj9_data {
708c2ecf20Sopenharmony_ci	struct i2c_client *client;
718c2ecf20Sopenharmony_ci	struct kxtj9_platform_data pdata;
728c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
738c2ecf20Sopenharmony_ci	unsigned int last_poll_interval;
748c2ecf20Sopenharmony_ci	u8 shift;
758c2ecf20Sopenharmony_ci	u8 ctrl_reg1;
768c2ecf20Sopenharmony_ci	u8 data_ctrl;
778c2ecf20Sopenharmony_ci	u8 int_ctrl;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct i2c_msg msgs[] = {
838c2ecf20Sopenharmony_ci		{
848c2ecf20Sopenharmony_ci			.addr = tj9->client->addr,
858c2ecf20Sopenharmony_ci			.flags = tj9->client->flags,
868c2ecf20Sopenharmony_ci			.len = 1,
878c2ecf20Sopenharmony_ci			.buf = &addr,
888c2ecf20Sopenharmony_ci		},
898c2ecf20Sopenharmony_ci		{
908c2ecf20Sopenharmony_ci			.addr = tj9->client->addr,
918c2ecf20Sopenharmony_ci			.flags = tj9->client->flags | I2C_M_RD,
928c2ecf20Sopenharmony_ci			.len = len,
938c2ecf20Sopenharmony_ci			.buf = data,
948c2ecf20Sopenharmony_ci		},
958c2ecf20Sopenharmony_ci	};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return i2c_transfer(tj9->client->adapter, msgs, 2);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	s16 acc_data[3]; /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
1038c2ecf20Sopenharmony_ci	s16 x, y, z;
1048c2ecf20Sopenharmony_ci	int err;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	err = kxtj9_i2c_read(tj9, XOUT_L, (u8 *)acc_data, 6);
1078c2ecf20Sopenharmony_ci	if (err < 0)
1088c2ecf20Sopenharmony_ci		dev_err(&tj9->client->dev, "accelerometer data read failed\n");
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]);
1118c2ecf20Sopenharmony_ci	y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]);
1128c2ecf20Sopenharmony_ci	z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	x >>= tj9->shift;
1158c2ecf20Sopenharmony_ci	y >>= tj9->shift;
1168c2ecf20Sopenharmony_ci	z >>= tj9->shift;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
1198c2ecf20Sopenharmony_ci	input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
1208c2ecf20Sopenharmony_ci	input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_z ? -z : z);
1218c2ecf20Sopenharmony_ci	input_sync(tj9->input_dev);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic irqreturn_t kxtj9_isr(int irq, void *dev)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9 = dev;
1278c2ecf20Sopenharmony_ci	int err;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* data ready is the only possible interrupt type */
1308c2ecf20Sopenharmony_ci	kxtj9_report_acceleration_data(tj9);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
1338c2ecf20Sopenharmony_ci	if (err < 0)
1348c2ecf20Sopenharmony_ci		dev_err(&tj9->client->dev,
1358c2ecf20Sopenharmony_ci			"error clearing interrupt status: %d\n", err);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	switch (new_g_range) {
1438c2ecf20Sopenharmony_ci	case KXTJ9_G_2G:
1448c2ecf20Sopenharmony_ci		tj9->shift = 4;
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	case KXTJ9_G_4G:
1478c2ecf20Sopenharmony_ci		tj9->shift = 3;
1488c2ecf20Sopenharmony_ci		break;
1498c2ecf20Sopenharmony_ci	case KXTJ9_G_8G:
1508c2ecf20Sopenharmony_ci		tj9->shift = 2;
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci	default:
1538c2ecf20Sopenharmony_ci		return -EINVAL;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	tj9->ctrl_reg1 &= 0xe7;
1578c2ecf20Sopenharmony_ci	tj9->ctrl_reg1 |= new_g_range;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int kxtj9_update_odr(struct kxtj9_data *tj9, unsigned int poll_interval)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	int err;
1658c2ecf20Sopenharmony_ci	int i;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* Use the lowest ODR that can support the requested poll interval */
1688c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(kxtj9_odr_table); i++) {
1698c2ecf20Sopenharmony_ci		tj9->data_ctrl = kxtj9_odr_table[i].mask;
1708c2ecf20Sopenharmony_ci		if (poll_interval < kxtj9_odr_table[i].cutoff)
1718c2ecf20Sopenharmony_ci			break;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
1758c2ecf20Sopenharmony_ci	if (err < 0)
1768c2ecf20Sopenharmony_ci		return err;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	err = i2c_smbus_write_byte_data(tj9->client, DATA_CTRL, tj9->data_ctrl);
1798c2ecf20Sopenharmony_ci	if (err < 0)
1808c2ecf20Sopenharmony_ci		return err;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
1838c2ecf20Sopenharmony_ci	if (err < 0)
1848c2ecf20Sopenharmony_ci		return err;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int kxtj9_device_power_on(struct kxtj9_data *tj9)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	if (tj9->pdata.power_on)
1928c2ecf20Sopenharmony_ci		return tj9->pdata.power_on();
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic void kxtj9_device_power_off(struct kxtj9_data *tj9)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	int err;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	tj9->ctrl_reg1 &= PC1_OFF;
2028c2ecf20Sopenharmony_ci	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
2038c2ecf20Sopenharmony_ci	if (err < 0)
2048c2ecf20Sopenharmony_ci		dev_err(&tj9->client->dev, "soft power off failed\n");
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (tj9->pdata.power_off)
2078c2ecf20Sopenharmony_ci		tj9->pdata.power_off();
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int kxtj9_enable(struct kxtj9_data *tj9)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	int err;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	err = kxtj9_device_power_on(tj9);
2158c2ecf20Sopenharmony_ci	if (err < 0)
2168c2ecf20Sopenharmony_ci		return err;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* ensure that PC1 is cleared before updating control registers */
2198c2ecf20Sopenharmony_ci	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
2208c2ecf20Sopenharmony_ci	if (err < 0)
2218c2ecf20Sopenharmony_ci		return err;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* only write INT_CTRL_REG1 if in irq mode */
2248c2ecf20Sopenharmony_ci	if (tj9->client->irq) {
2258c2ecf20Sopenharmony_ci		err = i2c_smbus_write_byte_data(tj9->client,
2268c2ecf20Sopenharmony_ci						INT_CTRL1, tj9->int_ctrl);
2278c2ecf20Sopenharmony_ci		if (err < 0)
2288c2ecf20Sopenharmony_ci			return err;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	err = kxtj9_update_g_range(tj9, tj9->pdata.g_range);
2328c2ecf20Sopenharmony_ci	if (err < 0)
2338c2ecf20Sopenharmony_ci		return err;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* turn on outputs */
2368c2ecf20Sopenharmony_ci	tj9->ctrl_reg1 |= PC1_ON;
2378c2ecf20Sopenharmony_ci	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
2388c2ecf20Sopenharmony_ci	if (err < 0)
2398c2ecf20Sopenharmony_ci		return err;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	err = kxtj9_update_odr(tj9, tj9->last_poll_interval);
2428c2ecf20Sopenharmony_ci	if (err < 0)
2438c2ecf20Sopenharmony_ci		return err;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* clear initial interrupt if in irq mode */
2468c2ecf20Sopenharmony_ci	if (tj9->client->irq) {
2478c2ecf20Sopenharmony_ci		err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
2488c2ecf20Sopenharmony_ci		if (err < 0) {
2498c2ecf20Sopenharmony_ci			dev_err(&tj9->client->dev,
2508c2ecf20Sopenharmony_ci				"error clearing interrupt: %d\n", err);
2518c2ecf20Sopenharmony_ci			goto fail;
2528c2ecf20Sopenharmony_ci		}
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return 0;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cifail:
2588c2ecf20Sopenharmony_ci	kxtj9_device_power_off(tj9);
2598c2ecf20Sopenharmony_ci	return err;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic void kxtj9_disable(struct kxtj9_data *tj9)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	kxtj9_device_power_off(tj9);
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int kxtj9_input_open(struct input_dev *input)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9 = input_get_drvdata(input);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return kxtj9_enable(tj9);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void kxtj9_input_close(struct input_dev *dev)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9 = input_get_drvdata(dev);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	kxtj9_disable(tj9);
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci/*
2828c2ecf20Sopenharmony_ci * When IRQ mode is selected, we need to provide an interface to allow the user
2838c2ecf20Sopenharmony_ci * to change the output data rate of the part.  For consistency, we are using
2848c2ecf20Sopenharmony_ci * the set_poll method, which accepts a poll interval in milliseconds, and then
2858c2ecf20Sopenharmony_ci * calls update_odr() while passing this value as an argument.  In IRQ mode, the
2868c2ecf20Sopenharmony_ci * data outputs will not be read AT the requested poll interval, rather, the
2878c2ecf20Sopenharmony_ci * lowest ODR that can support the requested interval.  The client application
2888c2ecf20Sopenharmony_ci * will be responsible for retrieving data from the input node at the desired
2898c2ecf20Sopenharmony_ci * interval.
2908c2ecf20Sopenharmony_ci */
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci/* Returns currently selected poll interval (in ms) */
2938c2ecf20Sopenharmony_cistatic ssize_t kxtj9_get_poll(struct device *dev,
2948c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
2978c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", tj9->last_poll_interval);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/* Allow users to select a new poll interval (in ms) */
3038c2ecf20Sopenharmony_cistatic ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
3048c2ecf20Sopenharmony_ci						const char *buf, size_t count)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
3078c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
3088c2ecf20Sopenharmony_ci	struct input_dev *input_dev = tj9->input_dev;
3098c2ecf20Sopenharmony_ci	unsigned int interval;
3108c2ecf20Sopenharmony_ci	int error;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	error = kstrtouint(buf, 10, &interval);
3138c2ecf20Sopenharmony_ci	if (error < 0)
3148c2ecf20Sopenharmony_ci		return error;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* Lock the device to prevent races with open/close (and itself) */
3178c2ecf20Sopenharmony_ci	mutex_lock(&input_dev->mutex);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	disable_irq(client->irq);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/*
3228c2ecf20Sopenharmony_ci	 * Set current interval to the greater of the minimum interval or
3238c2ecf20Sopenharmony_ci	 * the requested interval
3248c2ecf20Sopenharmony_ci	 */
3258c2ecf20Sopenharmony_ci	tj9->last_poll_interval = max(interval, tj9->pdata.min_interval);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	kxtj9_update_odr(tj9, tj9->last_poll_interval);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	enable_irq(client->irq);
3308c2ecf20Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return count;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic struct attribute *kxtj9_attributes[] = {
3388c2ecf20Sopenharmony_ci	&dev_attr_poll.attr,
3398c2ecf20Sopenharmony_ci	NULL
3408c2ecf20Sopenharmony_ci};
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic struct attribute_group kxtj9_attribute_group = {
3438c2ecf20Sopenharmony_ci	.attrs = kxtj9_attributes
3448c2ecf20Sopenharmony_ci};
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic void kxtj9_poll(struct input_dev *input)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9 = input_get_drvdata(input);
3498c2ecf20Sopenharmony_ci	unsigned int poll_interval = input_get_poll_interval(input);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	kxtj9_report_acceleration_data(tj9);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (poll_interval != tj9->last_poll_interval) {
3548c2ecf20Sopenharmony_ci		kxtj9_update_odr(tj9, poll_interval);
3558c2ecf20Sopenharmony_ci		tj9->last_poll_interval = poll_interval;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic void kxtj9_platform_exit(void *data)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9 = data;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (tj9->pdata.exit)
3648c2ecf20Sopenharmony_ci		tj9->pdata.exit();
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic int kxtj9_verify(struct kxtj9_data *tj9)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	int retval;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	retval = kxtj9_device_power_on(tj9);
3728c2ecf20Sopenharmony_ci	if (retval < 0)
3738c2ecf20Sopenharmony_ci		return retval;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
3768c2ecf20Sopenharmony_ci	if (retval < 0) {
3778c2ecf20Sopenharmony_ci		dev_err(&tj9->client->dev, "read err int source\n");
3788c2ecf20Sopenharmony_ci		goto out;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ciout:
3848c2ecf20Sopenharmony_ci	kxtj9_device_power_off(tj9);
3858c2ecf20Sopenharmony_ci	return retval;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int kxtj9_probe(struct i2c_client *client,
3898c2ecf20Sopenharmony_ci		       const struct i2c_device_id *id)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	const struct kxtj9_platform_data *pdata =
3928c2ecf20Sopenharmony_ci			dev_get_platdata(&client->dev);
3938c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9;
3948c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
3958c2ecf20Sopenharmony_ci	int err;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter,
3988c2ecf20Sopenharmony_ci				I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) {
3998c2ecf20Sopenharmony_ci		dev_err(&client->dev, "client is not i2c capable\n");
4008c2ecf20Sopenharmony_ci		return -ENXIO;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (!pdata) {
4048c2ecf20Sopenharmony_ci		dev_err(&client->dev, "platform data is NULL; exiting\n");
4058c2ecf20Sopenharmony_ci		return -EINVAL;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	tj9 = devm_kzalloc(&client->dev, sizeof(*tj9), GFP_KERNEL);
4098c2ecf20Sopenharmony_ci	if (!tj9) {
4108c2ecf20Sopenharmony_ci		dev_err(&client->dev,
4118c2ecf20Sopenharmony_ci			"failed to allocate memory for module data\n");
4128c2ecf20Sopenharmony_ci		return -ENOMEM;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	tj9->client = client;
4168c2ecf20Sopenharmony_ci	tj9->pdata = *pdata;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (pdata->init) {
4198c2ecf20Sopenharmony_ci		err = pdata->init();
4208c2ecf20Sopenharmony_ci		if (err < 0)
4218c2ecf20Sopenharmony_ci			return err;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	err = devm_add_action_or_reset(&client->dev, kxtj9_platform_exit, tj9);
4258c2ecf20Sopenharmony_ci	if (err)
4268c2ecf20Sopenharmony_ci		return err;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	err = kxtj9_verify(tj9);
4298c2ecf20Sopenharmony_ci	if (err < 0) {
4308c2ecf20Sopenharmony_ci		dev_err(&client->dev, "device not recognized\n");
4318c2ecf20Sopenharmony_ci		return err;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, tj9);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
4378c2ecf20Sopenharmony_ci	tj9->last_poll_interval = tj9->pdata.init_interval;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	input_dev = devm_input_allocate_device(&client->dev);
4408c2ecf20Sopenharmony_ci	if (!input_dev) {
4418c2ecf20Sopenharmony_ci		dev_err(&client->dev, "input device allocate failed\n");
4428c2ecf20Sopenharmony_ci		return -ENOMEM;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	input_set_drvdata(input_dev, tj9);
4468c2ecf20Sopenharmony_ci	tj9->input_dev = input_dev;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	input_dev->name = "kxtj9_accel";
4498c2ecf20Sopenharmony_ci	input_dev->id.bustype = BUS_I2C;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	input_dev->open = kxtj9_input_open;
4528c2ecf20Sopenharmony_ci	input_dev->close = kxtj9_input_close;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
4558c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
4568c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (client->irq <= 0) {
4598c2ecf20Sopenharmony_ci		err = input_setup_polling(input_dev, kxtj9_poll);
4608c2ecf20Sopenharmony_ci		if (err)
4618c2ecf20Sopenharmony_ci			return err;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	err = input_register_device(input_dev);
4658c2ecf20Sopenharmony_ci	if (err) {
4668c2ecf20Sopenharmony_ci		dev_err(&client->dev,
4678c2ecf20Sopenharmony_ci			"unable to register input polled device %s: %d\n",
4688c2ecf20Sopenharmony_ci			input_dev->name, err);
4698c2ecf20Sopenharmony_ci		return err;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (client->irq) {
4738c2ecf20Sopenharmony_ci		/* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
4748c2ecf20Sopenharmony_ci		tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
4758c2ecf20Sopenharmony_ci		tj9->ctrl_reg1 |= DRDYE;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci		err = devm_request_threaded_irq(&client->dev, client->irq,
4788c2ecf20Sopenharmony_ci						NULL, kxtj9_isr,
4798c2ecf20Sopenharmony_ci						IRQF_TRIGGER_RISING |
4808c2ecf20Sopenharmony_ci							IRQF_ONESHOT,
4818c2ecf20Sopenharmony_ci						"kxtj9-irq", tj9);
4828c2ecf20Sopenharmony_ci		if (err) {
4838c2ecf20Sopenharmony_ci			dev_err(&client->dev, "request irq failed: %d\n", err);
4848c2ecf20Sopenharmony_ci			return err;
4858c2ecf20Sopenharmony_ci		}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		err = devm_device_add_group(&client->dev,
4888c2ecf20Sopenharmony_ci					    &kxtj9_attribute_group);
4898c2ecf20Sopenharmony_ci		if (err) {
4908c2ecf20Sopenharmony_ci			dev_err(&client->dev, "sysfs create failed: %d\n", err);
4918c2ecf20Sopenharmony_ci			return err;
4928c2ecf20Sopenharmony_ci		}
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return 0;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int __maybe_unused kxtj9_suspend(struct device *dev)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
5018c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
5028c2ecf20Sopenharmony_ci	struct input_dev *input_dev = tj9->input_dev;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	mutex_lock(&input_dev->mutex);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (input_dev->users)
5078c2ecf20Sopenharmony_ci		kxtj9_disable(tj9);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
5108c2ecf20Sopenharmony_ci	return 0;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic int __maybe_unused kxtj9_resume(struct device *dev)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
5168c2ecf20Sopenharmony_ci	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
5178c2ecf20Sopenharmony_ci	struct input_dev *input_dev = tj9->input_dev;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	mutex_lock(&input_dev->mutex);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (input_dev->users)
5228c2ecf20Sopenharmony_ci		kxtj9_enable(tj9);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic const struct i2c_device_id kxtj9_id[] = {
5318c2ecf20Sopenharmony_ci	{ NAME, 0 },
5328c2ecf20Sopenharmony_ci	{ },
5338c2ecf20Sopenharmony_ci};
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, kxtj9_id);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic struct i2c_driver kxtj9_driver = {
5388c2ecf20Sopenharmony_ci	.driver = {
5398c2ecf20Sopenharmony_ci		.name	= NAME,
5408c2ecf20Sopenharmony_ci		.pm	= &kxtj9_pm_ops,
5418c2ecf20Sopenharmony_ci	},
5428c2ecf20Sopenharmony_ci	.probe		= kxtj9_probe,
5438c2ecf20Sopenharmony_ci	.id_table	= kxtj9_id,
5448c2ecf20Sopenharmony_ci};
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cimodule_i2c_driver(kxtj9_driver);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("KXTJ9 accelerometer driver");
5498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chris Hudson <chudson@kionix.com>");
5508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
551