* [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer
@ 2011-06-28 19:12 chris.hudson.comp.eng
2011-06-29 12:39 ` Jonathan Cameron
2011-07-04 16:26 ` Dmitry Torokhov
0 siblings, 2 replies; 8+ messages in thread
From: chris.hudson.comp.eng @ 2011-06-28 19:12 UTC (permalink / raw)
To: linux-input; +Cc: dmitry.torokhov, jic23, Chris Hudson
From: Chris Hudson <chudson@kionix.com>
- Added Dmitry's changes
- Now using input_polled_dev interface when in polling mode
- IRQ mode provides a separate sysfs node to change the hardware data rate
Signed-off-by: Chris Hudson <chudson@kionix.com>
---
drivers/input/misc/Kconfig | 17 ++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/kxtj9.c | 646 +++++++++++++++++++++++++++++++++++++++++++
include/linux/input/kxtj9.h | 76 +++++
4 files changed, 740 insertions(+), 0 deletions(-)
create mode 100755 drivers/input/misc/kxtj9.c
create mode 100755 include/linux/input/kxtj9.h
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 45dc6aa..cd6a806 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -478,4 +478,21 @@ config INPUT_XEN_KBDDEV_FRONTEND
To compile this driver as a module, choose M here: the
module will be called xen-kbdfront.
+config INPUT_KXTJ9
+ tristate "Kionix KXTJ9 tri-axis digital accelerometer"
+ depends on I2C
+ help
+ If you say yes here you get support for the Kionix KXTJ9 digital
+ tri-axis accelerometer.
+
+ This driver can also be built as a module. If so, the module
+ will be called kxtj9.
+
+config KXTJ9_POLLED_MODE
+ bool "Enable polling mode support"
+ depends on INPUT_KXTJ9
+ select INPUT_POLLDEV
+ help
+ Say Y here if you need accelerometer to work in polling mode.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 38efb2c..bfe1057 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
+obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index 0000000..7faf155
--- /dev/null
+++ b/drivers/input/misc/kxtj9.c
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2011 Kionix, Inc.
+ * Written by Chris Hudson <chudson@kionix.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input/kxtj9.h>
+#include <linux/input-polldev.h>
+
+#define NAME "kxtj9"
+#define G_MAX 8000
+/* OUTPUT REGISTERS */
+#define XOUT_L 0x06
+#define WHO_AM_I 0x0F
+/* CONTROL REGISTERS */
+#define INT_REL 0x1A
+#define CTRL_REG1 0x1B
+#define INT_CTRL1 0x1E
+#define DATA_CTRL 0x21
+/* CONTROL REGISTER 1 BITS */
+#define PC1_OFF 0x7F
+#define PC1_ON (1 << 7)
+/* Data ready funtion enable bit: set during probe if using irq mode */
+#define DRDYE (1 << 5)
+/* INTERRUPT CONTROL REGISTER 1 BITS */
+/* Set these during probe if using irq mode */
+#define KXTJ9_IEL (1 << 3)
+#define KXTJ9_IEA (1 << 4)
+#define KXTJ9_IEN (1 << 5)
+/* INPUT_ABS CONSTANTS */
+#define FUZZ 3
+#define FLAT 3
+/* RESUME STATE INDICES */
+#define RES_DATA_CTRL 0
+#define RES_CTRL_REG1 1
+#define RES_INT_CTRL1 2
+#define RESUME_ENTRIES 3
+
+/*
+ * The following table lists the maximum appropriate poll interval for each
+ * available output data rate.
+ */
+static const struct {
+ unsigned int cutoff;
+ u8 mask;
+} kxtj9_odr_table[] = {
+ { 3, ODR800F },
+ { 5, ODR400F },
+ { 10, ODR200F },
+ { 20, ODR100F },
+ { 40, ODR50F },
+ { 80, ODR25F },
+ { 0, ODR12_5F},
+};
+
+struct kxtj9_data {
+ struct i2c_client *client;
+ struct kxtj9_platform_data pdata;
+ struct input_dev *input_dev;
+#ifdef CONFIG_KXTJ9_POLLED_MODE
+ struct input_polled_dev *poll_dev;
+#else
+ struct mutex lock;
+#endif
+ unsigned int last_poll_interval;
+ u8 shift;
+ u8 ctrl_reg1;
+ u8 data_ctrl;
+ u8 int_ctrl;
+};
+
+static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = tj9->client->addr,
+ .flags = tj9->client->flags,
+ .len = 1,
+ .buf = &addr,
+ },
+ {
+ .addr = tj9->client->addr,
+ .flags = tj9->client->flags | I2C_M_RD,
+ .len = len,
+ .buf = data,
+ },
+ };
+
+ return i2c_transfer(tj9->client->adapter, msgs, 2);
+}
+
+static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
+{
+ s16 acc_data[3]; /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
+ s16 x, y, z;
+ int err;
+
+ err = kxtj9_i2c_read(tj9, XOUT_L, (u8 *)acc_data, 6);
+ if (err < 0)
+ dev_err(&tj9->client->dev, "accelerometer data read failed\n");
+
+ x = le16_to_cpu(acc_data[0]) >> tj9->shift;
+ y = le16_to_cpu(acc_data[1]) >> tj9->shift;
+ z = le16_to_cpu(acc_data[2]) >> tj9->shift;
+
+ input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
+ input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
+ input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_x ? -y : y);
+ input_sync(tj9->input_dev);
+}
+
+static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
+{
+ switch (new_g_range) {
+ case KXTJ9_G_2G:
+ tj9->shift = SHIFT_ADJ_2G;
+ break;
+
+ case KXTJ9_G_4G:
+ tj9->shift = SHIFT_ADJ_4G;
+ break;
+
+ case KXTJ9_G_8G:
+ tj9->shift = SHIFT_ADJ_8G;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ tj9->ctrl_reg1 &= 0xe7;
+ tj9->ctrl_reg1 |= new_g_range;
+
+ return 0;
+}
+
+static int kxtj9_update_odr(struct kxtj9_data *tj9, int poll_interval)
+{
+ int err;
+ int i;
+
+ /* Use the lowest ODR that can support the requested poll interval */
+ for (i = 0; i < ARRAY_SIZE(kxtj9_odr_table); i++) {
+ tj9->data_ctrl = kxtj9_odr_table[i].mask;
+ if (poll_interval < kxtj9_odr_table[i].cutoff)
+ break;
+ }
+
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+ if (err < 0)
+ return err;
+
+ err = i2c_smbus_write_byte_data(tj9->client, DATA_CTRL, tj9->data_ctrl);
+ if (err < 0)
+ return err;
+
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int kxtj9_device_power_on(struct kxtj9_data *tj9)
+{
+ int err;
+
+ if (tj9->pdata.power_on) {
+ err = tj9->pdata.power_on();
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void kxtj9_device_power_off(struct kxtj9_data *tj9)
+{
+ int err;
+
+ tj9->ctrl_reg1 &= PC1_OFF;
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+ if (err < 0)
+ dev_err(&tj9->client->dev, "soft power off failed\n");
+
+ if (tj9->pdata.power_off)
+ tj9->pdata.power_off();
+}
+
+static int kxtj9_enable(struct kxtj9_data *tj9)
+{
+ int err;
+
+ err = kxtj9_device_power_on(tj9);
+ if (err < 0)
+ return err;
+
+ /* ensure that PC1 is cleared before updating control registers */
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+ if (err < 0)
+ return err;
+
+ /* only write INT_CTRL_REG1 if in irq mode */
+ if (tj9->client->irq) {
+ err = i2c_smbus_write_byte_data(tj9->client,
+ INT_CTRL1, tj9->int_ctrl);
+ if (err < 0)
+ return err;
+ }
+
+ err = kxtj9_update_g_range(tj9, tj9->pdata.g_range);
+ if (err < 0)
+ return err;
+
+ /* turn on outputs */
+ tj9->ctrl_reg1 |= PC1_ON;
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+ if (err < 0)
+ return err;
+
+ err = kxtj9_update_odr(tj9, tj9->last_poll_interval);
+ if (err < 0)
+ return err;
+
+ /* clear initial interrupt if in irq mode */
+ if (tj9->client->irq) {
+ err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+ if (err < 0) {
+ dev_err(&tj9->client->dev,
+ "error clearing interrupt: %d\n", err);
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ kxtj9_device_power_off(tj9);
+ return err;
+}
+
+static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
+ struct input_dev *input_dev)
+{
+ __set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
+
+ input_dev->name = "kxtj9_accel";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &tj9->client->dev;
+}
+
+#ifdef CONFIG_KXTJ9_POLLED_MODE
+static void kxtj9_poll(struct input_polled_dev *dev)
+{
+ struct kxtj9_data *tj9 = dev->private;
+ unsigned int poll_interval = dev->poll_interval;
+
+ kxtj9_report_acceleration_data(tj9);
+
+ if (poll_interval != tj9->last_poll_interval) {
+ kxtj9_update_odr(tj9, poll_interval);
+ tj9->last_poll_interval = poll_interval;
+ }
+}
+
+static void kxtj9_polled_input_open(struct input_polled_dev *dev)
+{
+ struct kxtj9_data *tj9 = dev->private;
+
+ kxtj9_enable(tj9);
+}
+
+static void kxtj9_polled_input_close(struct input_polled_dev *dev)
+{
+ struct kxtj9_data *tj9 = dev->private;
+
+ kxtj9_device_power_off(tj9);
+}
+
+static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+ int err;
+ struct input_polled_dev *poll_dev;
+ poll_dev = input_allocate_polled_device();
+
+ if (!poll_dev) {
+ dev_err(&tj9->client->dev,
+ "Failed to allocate polled device\n");
+ return -ENOMEM;
+ }
+
+ tj9->poll_dev = poll_dev;
+ tj9->input_dev = poll_dev->input;
+
+ poll_dev->private = tj9;
+ poll_dev->poll = kxtj9_poll;
+ poll_dev->open = kxtj9_polled_input_open;
+ poll_dev->close = kxtj9_polled_input_close;
+
+ kxtj9_init_input_device(tj9, poll_dev->input);
+
+ err = input_register_polled_device(poll_dev);
+ if (err) {
+ dev_err(&tj9->client->dev,
+ "Unable to register polled device, err=%d\n", err);
+ input_free_polled_device(poll_dev);
+ return 0;
+ }
+
+ return 0;
+}
+
+static void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+ input_unregister_polled_device(tj9->poll_dev);
+ input_free_polled_device(tj9->poll_dev);
+}
+
+#else /* IRQ Mode is enabled */
+static int kxtj9_input_open(struct input_dev *input)
+{
+ struct kxtj9_data *tj9 = input_get_drvdata(input);
+
+ return kxtj9_enable(tj9);
+}
+
+static void kxtj9_input_close(struct input_dev *dev)
+{
+ struct kxtj9_data *tj9 = input_get_drvdata(dev);
+
+ kxtj9_device_power_off(tj9);
+}
+
+static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!tj9->input_dev) {
+ dev_err(&tj9->client->dev, "input device allocate failed\n");
+ return -ENOMEM;
+ }
+
+ tj9->input_dev = input_dev;
+
+ input_dev->open = kxtj9_input_open;
+ input_dev->close = kxtj9_input_close;
+ input_set_drvdata(input_dev, tj9);
+
+ kxtj9_init_input_device(tj9, input_dev);
+
+ err = input_register_device(tj9->input_dev);
+ if (err) {
+ dev_err(&tj9->client->dev,
+ "unable to register input polled device %s: %d\n",
+ tj9->input_dev->name, err);
+ input_free_device(tj9->input_dev);
+ }
+
+ return 0;
+}
+
+static irqreturn_t kxtj9_isr(int irq, void *dev)
+{
+ struct kxtj9_data *tj9 = dev;
+ int err;
+
+ /* data ready is the only possible interrupt type */
+ kxtj9_report_acceleration_data(tj9);
+
+ err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+ if (err < 0)
+ dev_err(&tj9->client->dev,
+ "error clearing interrupt status: %d\n", err);
+
+ return IRQ_HANDLED;
+}
+
+static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+ return -ENOSYS;
+}
+
+static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+}
+
+/* kxtj9_get_poll: returns the currently selected poll interval (in ms) */
+static ssize_t kxtj9_get_poll(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
+ return sprintf(buf, "%d\n", tj9->last_poll_interval);
+}
+
+/* kxtj9_set_poll: allows the user to select a new poll interval (in ms) */
+static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
+
+ unsigned long interval;
+ int ret = kstrtoul(buf, 10, &interval);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&tj9->lock);
+ /* set current interval to the greater of the minimum interval or
+ the requested interval */
+ tj9->last_poll_interval = max((int)interval, tj9->pdata.min_interval);
+ mutex_unlock(&tj9->lock);
+
+ kxtj9_update_odr(tj9, tj9->last_poll_interval);
+
+ return count;
+}
+/* When IRQ mode is selected, we need to provide an interface to allow the user
+ * to change the output data rate of the part. For consistency, we are using
+ * the set_poll method, which accepts a poll interval in milliseconds, and then
+ * calls update_odr() while passing this value as an argument. In IRQ mode, the
+ * data outputs will not be read AT the requested poll interval, rather, the
+ * lowest ODR that can support the requested interval. The client application
+ * will be responsible for retrieving data from the input node at the desired
+ * interval.
+ */
+static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
+
+static struct attribute *kxtj9_attributes[] = {
+ &dev_attr_poll.attr,
+ NULL
+};
+
+static struct attribute_group kxtj9_attribute_group = {
+ .attrs = kxtj9_attributes
+};
+#endif
+
+static int __devinit kxtj9_verify(struct kxtj9_data *tj9)
+{
+ int retval;
+ retval = kxtj9_device_power_on(tj9);
+ if (retval < 0)
+ return retval;
+ retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
+ if (retval < 0) {
+ dev_err(&tj9->client->dev, "read err int source\n");
+ goto out;
+ }
+ retval = retval != 0x06 ? -EIO : 0;
+
+out:
+ kxtj9_device_power_off(tj9);
+ return retval;
+}
+
+static int __devinit kxtj9_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct kxtj9_platform_data *pdata = client->dev.platform_data;
+ struct kxtj9_data *tj9;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "client is not i2c capable\n");
+ return -ENXIO;
+ }
+ if (!pdata) {
+ dev_err(&client->dev, "platform data is NULL; exiting\n");
+ return -EINVAL;
+ }
+ tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL);
+ if (!tj9) {
+ dev_err(&client->dev,
+ "failed to allocate memory for module data\n");
+ return -ENOMEM;
+ }
+
+ tj9->client = client;
+ tj9->pdata = *pdata;
+
+ if (pdata->init) {
+ err = pdata->init();
+ if (err < 0)
+ goto err_free_mem;
+ }
+
+ err = kxtj9_verify(tj9);
+ if (err < 0) {
+ dev_err(&client->dev, "device not recognized\n");
+ goto err_pdata_exit;
+ }
+
+ tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
+ tj9->data_ctrl = tj9->pdata.data_odr_init;
+
+#ifdef CONFIG_KXTJ9_POLLED_MODE
+ err = kxtj9_setup_polled_device(tj9);
+ if (err)
+ goto err_pdata_exit;
+#else
+ /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
+ tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
+ tj9->ctrl_reg1 |= DRDYE;
+
+ err = kxtj9_setup_input_device(tj9);
+ if (err)
+ goto err_pdata_exit;
+
+ err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "kxtj9-irq", tj9);
+ if (err) {
+ dev_err(&client->dev, "request irq failed: %d\n", err);
+ goto err_destroy_input;
+ }
+ err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
+ if (err) {
+ dev_err(&client->dev, "sysfs create failed: %d\n", err);
+ goto err_destroy_input;
+ }
+#endif
+ i2c_set_clientdata(client, tj9);
+ return 0;
+
+#ifndef CONFIG_KXTJ9_POLLED_MODE
+err_destroy_input:
+ input_unregister_device(tj9->input_dev);
+#endif
+err_pdata_exit:
+ if (tj9->pdata.exit)
+ tj9->pdata.exit();
+err_free_mem:
+ kfree(tj9);
+ return err;
+}
+
+static int __devexit kxtj9_remove(struct i2c_client *client)
+{
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+#ifdef CONFIG_KXTJ9_POLLED_MODE
+ kxtj9_teardown_polled_device(tj9);
+#else
+ sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
+ free_irq(client->irq, tj9);
+ input_unregister_device(tj9->input_dev);
+#endif
+ if (tj9->pdata.exit)
+ tj9->pdata.exit();
+
+ kfree(tj9);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int kxtj9_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+ struct input_dev *input_dev = tj9->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ kxtj9_device_power_off(tj9);
+
+ mutex_unlock(&input_dev->mutex);
+ return 0;
+}
+
+static int kxtj9_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+ struct input_dev *input_dev = tj9->input_dev;
+ int retval = 0;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ kxtj9_enable(tj9);
+
+ mutex_unlock(&input_dev->mutex);
+ return retval;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
+
+static const struct i2c_device_id kxtj9_id[] = {
+ { NAME, 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, kxtj9_id);
+
+static struct i2c_driver kxtj9_driver = {
+ .driver = {
+ .name = NAME,
+ .owner = THIS_MODULE,
+ .pm = &kxtj9_pm_ops,
+ },
+ .probe = kxtj9_probe,
+ .remove = __devexit_p(kxtj9_remove),
+ .id_table = kxtj9_id,
+};
+
+static int __init kxtj9_init(void)
+{
+ return i2c_add_driver(&kxtj9_driver);
+}
+module_init(kxtj9_init);
+
+static void __exit kxtj9_exit(void)
+{
+ i2c_del_driver(&kxtj9_driver);
+}
+module_exit(kxtj9_exit);
+
+MODULE_DESCRIPTION("KXTJ9 accelerometer driver");
+MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/input/kxtj9.h b/include/linux/input/kxtj9.h
index 0000000..cc5928b
--- /dev/null
+++ b/include/linux/input/kxtj9.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 Kionix, Inc.
+ * Written by Chris Hudson <chudson@kionix.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef __KXTJ9_H__
+#define __KXTJ9_H__
+
+#define KXTJ9_I2C_ADDR 0x0F
+
+/* These shift values are used to provide consistent output data */
+#define SHIFT_ADJ_2G 4
+#define SHIFT_ADJ_4G 3
+#define SHIFT_ADJ_8G 2
+
+#ifdef __KERNEL__
+struct kxtj9_platform_data {
+ int poll_interval; /* current poll interval (in milli-seconds) */
+ int min_interval; /* minimum poll interval (in milli-seconds) */
+
+ /* by default, x is axis 0, y is axis 1, z is axis 2; these can be
+ changed to account for sensor orientation within the host device */
+ u8 axis_map_x;
+ u8 axis_map_y;
+ u8 axis_map_z;
+
+ /* each axis can be negated to account for sensor orientation within
+ the host device; 1 = negate this axis; 0 = do not negate this axis */
+ bool negate_x;
+ bool negate_y;
+ bool negate_z;
+
+ /* CTRL_REG1: set resolution, g-range, data ready enable */
+ /* Output resolution: 8-bit valid or 12-bit valid */
+ #define RES_8BIT 0
+ #define RES_12BIT (1 << 6)
+ u8 res_12bit;
+ /* Output g-range: +/-2g, 4g, or 8g */
+ #define KXTJ9_G_2G 0
+ #define KXTJ9_G_4G (1 << 3)
+ #define KXTJ9_G_8G (1 << 4)
+ u8 g_range;
+
+ /* DATA_CTRL_REG: controls the output data rate of the part */
+ #define ODR12_5F 0
+ #define ODR25F 1
+ #define ODR50F 2
+ #define ODR100F 3
+ #define ODR200F 4
+ #define ODR400F 5
+ #define ODR800F 6
+ u8 data_odr_init;
+
+ int (*init)(void);
+ void (*exit)(void);
+ int (*power_on)(void);
+ int (*power_off)(void);
+};
+#endif /* __KERNEL__ */
+
+#endif /* __KXTJ9_H__ */
+
--
1.5.4.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer
2011-06-28 19:12 [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer chris.hudson.comp.eng
@ 2011-06-29 12:39 ` Jonathan Cameron
2011-07-04 14:25 ` Dmitry Torokhov
2011-07-04 16:26 ` Dmitry Torokhov
1 sibling, 1 reply; 8+ messages in thread
From: Jonathan Cameron @ 2011-06-29 12:39 UTC (permalink / raw)
To: chris.hudson.comp.eng; +Cc: linux-input, dmitry.torokhov, Chris Hudson
On 06/28/11 20:12, chris.hudson.comp.eng@gmail.com wrote:
> From: Chris Hudson <chudson@kionix.com>
>
> - Added Dmitry's changes
> - Now using input_polled_dev interface when in polling mode
> - IRQ mode provides a separate sysfs node to change the hardware data rate
Hi Chris,
Few minor comments inline.
>
> Signed-off-by: Chris Hudson <chudson@kionix.com>
> ---
> drivers/input/misc/Kconfig | 17 ++
> drivers/input/misc/Makefile | 1 +
> drivers/input/misc/kxtj9.c | 646 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/input/kxtj9.h | 76 +++++
> 4 files changed, 740 insertions(+), 0 deletions(-)
> create mode 100755 drivers/input/misc/kxtj9.c
> create mode 100755 include/linux/input/kxtj9.h
>
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index 45dc6aa..cd6a806 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -478,4 +478,21 @@ config INPUT_XEN_KBDDEV_FRONTEND
> To compile this driver as a module, choose M here: the
> module will be called xen-kbdfront.
>
> +config INPUT_KXTJ9
> + tristate "Kionix KXTJ9 tri-axis digital accelerometer"
> + depends on I2C
> + help
> + If you say yes here you get support for the Kionix KXTJ9 digital
> + tri-axis accelerometer.
> +
> + This driver can also be built as a module. If so, the module
> + will be called kxtj9.
> +
> +config KXTJ9_POLLED_MODE
> + bool "Enable polling mode support"
> + depends on INPUT_KXTJ9
> + select INPUT_POLLDEV
> + help
> + Say Y here if you need accelerometer to work in polling mode.
> +
> endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 38efb2c..bfe1057 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
> obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
> obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
> obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
> +obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
> obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
> obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
> obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
> diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
> index 0000000..7faf155
> --- /dev/null
> +++ b/drivers/input/misc/kxtj9.c
> @@ -0,0 +1,646 @@
> +/*
> + * Copyright (C) 2011 Kionix, Inc.
> + * Written by Chris Hudson <chudson@kionix.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
> + * 02111-1307, USA
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/input/kxtj9.h>
> +#include <linux/input-polldev.h>
> +
> +#define NAME "kxtj9"
> +#define G_MAX 8000
> +/* OUTPUT REGISTERS */
> +#define XOUT_L 0x06
> +#define WHO_AM_I 0x0F
> +/* CONTROL REGISTERS */
> +#define INT_REL 0x1A
> +#define CTRL_REG1 0x1B
> +#define INT_CTRL1 0x1E
> +#define DATA_CTRL 0x21
> +/* CONTROL REGISTER 1 BITS */
> +#define PC1_OFF 0x7F
> +#define PC1_ON (1 << 7)
> +/* Data ready funtion enable bit: set during probe if using irq mode */
> +#define DRDYE (1 << 5)
> +/* INTERRUPT CONTROL REGISTER 1 BITS */
> +/* Set these during probe if using irq mode */
> +#define KXTJ9_IEL (1 << 3)
> +#define KXTJ9_IEA (1 << 4)
> +#define KXTJ9_IEN (1 << 5)
> +/* INPUT_ABS CONSTANTS */
> +#define FUZZ 3
> +#define FLAT 3
> +/* RESUME STATE INDICES */
> +#define RES_DATA_CTRL 0
> +#define RES_CTRL_REG1 1
> +#define RES_INT_CTRL1 2
> +#define RESUME_ENTRIES 3
> +
> +/*
> + * The following table lists the maximum appropriate poll interval for each
> + * available output data rate.
> + */
> +static const struct {
> + unsigned int cutoff;
> + u8 mask;
> +} kxtj9_odr_table[] = {
> + { 3, ODR800F },
> + { 5, ODR400F },
> + { 10, ODR200F },
> + { 20, ODR100F },
> + { 40, ODR50F },
> + { 80, ODR25F },
> + { 0, ODR12_5F},
> +};
> +
> +struct kxtj9_data {
> + struct i2c_client *client;
> + struct kxtj9_platform_data pdata;
> + struct input_dev *input_dev;
> +#ifdef CONFIG_KXTJ9_POLLED_MODE
> + struct input_polled_dev *poll_dev;
> +#else
> + struct mutex lock;
> +#endif
> + unsigned int last_poll_interval;
> + u8 shift;
> + u8 ctrl_reg1;
> + u8 data_ctrl;
> + u8 int_ctrl;
> +};
> +
> +static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len)
> +{
> + struct i2c_msg msgs[] = {
> + {
> + .addr = tj9->client->addr,
> + .flags = tj9->client->flags,
> + .len = 1,
> + .buf = &addr,
> + },
> + {
> + .addr = tj9->client->addr,
> + .flags = tj9->client->flags | I2C_M_RD,
> + .len = len,
> + .buf = data,
> + },
> + };
> +
> + return i2c_transfer(tj9->client->adapter, msgs, 2);
Could just use i2c_smbus_read_i2c_block_data(tj9->client, addr, len, data);
> +}
> +
> +static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
> +{
> + s16 acc_data[3]; /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
> + s16 x, y, z;
> + int err;
> +
> + err = kxtj9_i2c_read(tj9, XOUT_L, (u8 *)acc_data, 6);
> + if (err < 0)
> + dev_err(&tj9->client->dev, "accelerometer data read failed\n");
> +
> + x = le16_to_cpu(acc_data[0]) >> tj9->shift;
> + y = le16_to_cpu(acc_data[1]) >> tj9->shift;
> + z = le16_to_cpu(acc_data[2]) >> tj9->shift;
> +
> + input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
> + input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
> + input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_x ? -y : y);
> + input_sync(tj9->input_dev);
> +}
> +
This next function does look like it should take an enum instead
of new_g_range then use a single table to look up both the shift and the
value for new_g_range. Something like.
enum {
range_2g, range_4g, range_8g
} kxtj9_range;
/* could use a struct for this to make what is what more explicit */
static const u8 rangevals[3][2] = { {KXTJ9_G_2G, SHIFT_ADJ_2G},
{KXTJ9_G_4G, SHIFT_ADJ_4G},
{KXTJ9_G_8G, SHIFT_ADJ_8G}};
static int kxtj9_update_g_range(struct kxtj9_data *tj9, enum_kxtj9_range r)
{
tj9->shift = rangevals[r][1];
tj9->ctrl_reg1 &= 0xe7;
tj9->ctrl_reg1 |= rangevals[r][0];
}
Actually you could just encode values in the array rather than using defines
at all. Also, could make the pdata element the enum value.
> +static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
> +{
> + switch (new_g_range) {
> + case KXTJ9_G_2G:
> + tj9->shift = SHIFT_ADJ_2G;
> + break;
> +
> + case KXTJ9_G_4G:
> + tj9->shift = SHIFT_ADJ_4G;
> + break;
> +
> + case KXTJ9_G_8G:
> + tj9->shift = SHIFT_ADJ_8G;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + tj9->ctrl_reg1 &= 0xe7;
> + tj9->ctrl_reg1 |= new_g_range;
> +
> + return 0;
> +}
> +
> +static int kxtj9_update_odr(struct kxtj9_data *tj9, int poll_interval)
> +{
> + int err;
> + int i;
> +
> + /* Use the lowest ODR that can support the requested poll interval */
> + for (i = 0; i < ARRAY_SIZE(kxtj9_odr_table); i++) {
> + tj9->data_ctrl = kxtj9_odr_table[i].mask;
> + if (poll_interval < kxtj9_odr_table[i].cutoff)
> + break;
> + }
> +
> + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
> + if (err < 0)
> + return err;
> +
> + err = i2c_smbus_write_byte_data(tj9->client, DATA_CTRL, tj9->data_ctrl);
> + if (err < 0)
> + return err;
> +
> + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
> + if (err < 0)
> + return err;
> +
> + return 0;
> +}
> +
> +static int kxtj9_device_power_on(struct kxtj9_data *tj9)
> +{
> + int err;
> +
> + if (tj9->pdata.power_on) {
> + err = tj9->pdata.power_on();
> + if (err < 0)
> + return err;
> + }
> +
> + return 0;
> +}
Slight semantics change to pdata.power_on() returning 0 for success and you'll
have cleaner:
static int kxtj9_device_power_on(struct kxtj9_data *tj9)
{
if (tj9->pdata.power_on)
return tj9->pdata.power_on();
return 0;
}
> +
> +static void kxtj9_device_power_off(struct kxtj9_data *tj9)
> +{
> + int err;
> +
> + tj9->ctrl_reg1 &= PC1_OFF;
> + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
> + if (err < 0)
> + dev_err(&tj9->client->dev, "soft power off failed\n");
> +
> + if (tj9->pdata.power_off)
> + tj9->pdata.power_off();
> +}
> +
> +static int kxtj9_enable(struct kxtj9_data *tj9)
> +{
> + int err;
> +
> + err = kxtj9_device_power_on(tj9);
> + if (err < 0)
> + return err;
> +
> + /* ensure that PC1 is cleared before updating control registers */
> + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
> + if (err < 0)
> + return err;
> +
> + /* only write INT_CTRL_REG1 if in irq mode */
> + if (tj9->client->irq) {
> + err = i2c_smbus_write_byte_data(tj9->client,
> + INT_CTRL1, tj9->int_ctrl);
> + if (err < 0)
> + return err;
> + }
> +
> + err = kxtj9_update_g_range(tj9, tj9->pdata.g_range);
> + if (err < 0)
> + return err;
> +
> + /* turn on outputs */
> + tj9->ctrl_reg1 |= PC1_ON;
> + err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
> + if (err < 0)
> + return err;
> +
> + err = kxtj9_update_odr(tj9, tj9->last_poll_interval);
> + if (err < 0)
> + return err;
> +
> + /* clear initial interrupt if in irq mode */
> + if (tj9->client->irq) {
> + err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
> + if (err < 0) {
> + dev_err(&tj9->client->dev,
> + "error clearing interrupt: %d\n", err);
> + goto fail;
> + }
> + }
> +
> + return 0;
> +
> +fail:
> + kxtj9_device_power_off(tj9);
> + return err;
> +}
> +
> +static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
> + struct input_dev *input_dev)
> +{
> + __set_bit(EV_ABS, input_dev->evbit);
> + input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
> + input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
> + input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
> +
> + input_dev->name = "kxtj9_accel";
Is the _accel bit a convention?
> + input_dev->id.bustype = BUS_I2C;
> + input_dev->dev.parent = &tj9->client->dev;
> +}
> +
> +#ifdef CONFIG_KXTJ9_POLLED_MODE
> +static void kxtj9_poll(struct input_polled_dev *dev)
> +{
> + struct kxtj9_data *tj9 = dev->private;
> + unsigned int poll_interval = dev->poll_interval;
> +
> + kxtj9_report_acceleration_data(tj9);
> +
> + if (poll_interval != tj9->last_poll_interval) {
> + kxtj9_update_odr(tj9, poll_interval);
> + tj9->last_poll_interval = poll_interval;
> + }
> +}
> +
> +static void kxtj9_polled_input_open(struct input_polled_dev *dev)
> +{
> + struct kxtj9_data *tj9 = dev->private;
> +
> + kxtj9_enable(tj9);
Given ktxj9_enable knows what it's getting, squash into
kxtj9_enable(dev->private);
> +}
> +
> +static void kxtj9_polled_input_close(struct input_polled_dev *dev)
> +{
> + struct kxtj9_data *tj9 = dev->private;
> +
> + kxtj9_device_power_off(tj9);
same as previous.
> +}
> +
> +static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9)
> +{
> + int err;
> + struct input_polled_dev *poll_dev;
> + poll_dev = input_allocate_polled_device();
> +
> + if (!poll_dev) {
> + dev_err(&tj9->client->dev,
> + "Failed to allocate polled device\n");
> + return -ENOMEM;
> + }
> +
> + tj9->poll_dev = poll_dev;
> + tj9->input_dev = poll_dev->input;
> +
> + poll_dev->private = tj9;
> + poll_dev->poll = kxtj9_poll;
> + poll_dev->open = kxtj9_polled_input_open;
> + poll_dev->close = kxtj9_polled_input_close;
> +
> + kxtj9_init_input_device(tj9, poll_dev->input);
> +
> + err = input_register_polled_device(poll_dev);
> + if (err) {
> + dev_err(&tj9->client->dev,
> + "Unable to register polled device, err=%d\n", err);
> + input_free_polled_device(poll_dev);
> + return 0;
> + }
> +
> + return 0;
> +}
> +
> +static void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
> +{
> + input_unregister_polled_device(tj9->poll_dev);
> + input_free_polled_device(tj9->poll_dev);
> +}
> +
> +#else /* IRQ Mode is enabled */
> +static int kxtj9_input_open(struct input_dev *input)
> +{
> + struct kxtj9_data *tj9 = input_get_drvdata(input);
> +
> + return kxtj9_enable(tj9);
return kxtj9_enable(input_get_drvdata(input));
> +}
> +
> +static void kxtj9_input_close(struct input_dev *dev)
> +{
> + struct kxtj9_data *tj9 = input_get_drvdata(dev);
> +
> + kxtj9_device_power_off(tj9);
snap
> +}
> +
> +static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9)
> +{
> + struct input_dev *input_dev;
> + int err;
> +
> + input_dev = input_allocate_device();
> + if (!tj9->input_dev) {
> + dev_err(&tj9->client->dev, "input device allocate failed\n");
> + return -ENOMEM;
> + }
> +
> + tj9->input_dev = input_dev;
> +
> + input_dev->open = kxtj9_input_open;
> + input_dev->close = kxtj9_input_close;
> + input_set_drvdata(input_dev, tj9);
> +
> + kxtj9_init_input_device(tj9, input_dev);
> +
> + err = input_register_device(tj9->input_dev);
> + if (err) {
> + dev_err(&tj9->client->dev,
> + "unable to register input polled device %s: %d\n",
> + tj9->input_dev->name, err);
> + input_free_device(tj9->input_dev);
> + }
> +
> + return 0;
> +}
> +
> +static irqreturn_t kxtj9_isr(int irq, void *dev)
> +{
> + struct kxtj9_data *tj9 = dev;
> + int err;
> +
> + /* data ready is the only possible interrupt type */
> + kxtj9_report_acceleration_data(tj9);
> +
> + err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
> + if (err < 0)
> + dev_err(&tj9->client->dev,
> + "error clearing interrupt status: %d\n", err);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
> +{
> +}
> +
> +/* kxtj9_get_poll: returns the currently selected poll interval (in ms) */
> +static ssize_t kxtj9_get_poll(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
> + return sprintf(buf, "%d\n", tj9->last_poll_interval);
> +}
> +
> +/* kxtj9_set_poll: allows the user to select a new poll interval (in ms) */
> +static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
> +
> + unsigned long interval;
> + int ret = kstrtoul(buf, 10, &interval);
> + if (ret < 0)
> + return ret;
> +
> + mutex_lock(&tj9->lock);
> + /* set current interval to the greater of the minimum interval or
> + the requested interval */
> + tj9->last_poll_interval = max((int)interval, tj9->pdata.min_interval);
> + mutex_unlock(&tj9->lock);
> +
> + kxtj9_update_odr(tj9, tj9->last_poll_interval);
> +
> + return count;
> +}
New line here please.
> +/* When IRQ mode is selected, we need to provide an interface to allow the user
> + * to change the output data rate of the part. For consistency, we are using
> + * the set_poll method, which accepts a poll interval in milliseconds, and then
> + * calls update_odr() while passing this value as an argument. In IRQ mode, the
> + * data outputs will not be read AT the requested poll interval, rather, the
> + * lowest ODR that can support the requested interval. The client application
> + * will be responsible for retrieving data from the input node at the desired
> + * interval.
> + */
> +static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
> +
> +static struct attribute *kxtj9_attributes[] = {
> + &dev_attr_poll.attr,
> + NULL
> +};
> +
> +static struct attribute_group kxtj9_attribute_group = {
> + .attrs = kxtj9_attributes
> +};
> +#endif
> +
> +static int __devinit kxtj9_verify(struct kxtj9_data *tj9)
> +{
> + int retval;
> + retval = kxtj9_device_power_on(tj9);
> + if (retval < 0)
> + return retval;
> + retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
> + if (retval < 0) {
> + dev_err(&tj9->client->dev, "read err int source\n");
> + goto out;
> + }
> + retval = retval != 0x06 ? -EIO : 0;
> +
> +out:
> + kxtj9_device_power_off(tj9);
> + return retval;
> +}
> +
> +static int __devinit kxtj9_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + const struct kxtj9_platform_data *pdata = client->dev.platform_data;
> + struct kxtj9_data *tj9;
> + int err;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) {
> + dev_err(&client->dev, "client is not i2c capable\n");
> + return -ENXIO;
> + }
> + if (!pdata) {
> + dev_err(&client->dev, "platform data is NULL; exiting\n");
> + return -EINVAL;
> + }
> + tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL);
> + if (!tj9) {
> + dev_err(&client->dev,
> + "failed to allocate memory for module data\n");
> + return -ENOMEM;
> + }
> +
> + tj9->client = client;
> + tj9->pdata = *pdata;
> +
> + if (pdata->init) {
> + err = pdata->init();
> + if (err < 0)
> + goto err_free_mem;
> + }
> +
> + err = kxtj9_verify(tj9);
> + if (err < 0) {
> + dev_err(&client->dev, "device not recognized\n");
> + goto err_pdata_exit;
> + }
> +
> + tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
> + tj9->data_ctrl = tj9->pdata.data_odr_init;
> +
> +#ifdef CONFIG_KXTJ9_POLLED_MODE
> + err = kxtj9_setup_polled_device(tj9);
> + if (err)
> + goto err_pdata_exit;
> +#else
> + /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
> + tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
> + tj9->ctrl_reg1 |= DRDYE;
> +
> + err = kxtj9_setup_input_device(tj9);
> + if (err)
> + goto err_pdata_exit;
> +
> + err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
> + IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "kxtj9-irq", tj9);
> + if (err) {
> + dev_err(&client->dev, "request irq failed: %d\n", err);
> + goto err_destroy_input;
> + }
> + err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
> + if (err) {
> + dev_err(&client->dev, "sysfs create failed: %d\n", err);
> + goto err_destroy_input;
> + }
> +#endif
> + i2c_set_clientdata(client, tj9);
> + return 0;
> +
> +#ifndef CONFIG_KXTJ9_POLLED_MODE
> +err_destroy_input:
> + input_unregister_device(tj9->input_dev);
> +#endif
> +err_pdata_exit:
> + if (tj9->pdata.exit)
> + tj9->pdata.exit();
> +err_free_mem:
> + kfree(tj9);
> + return err;
> +}
> +
> +static int __devexit kxtj9_remove(struct i2c_client *client)
> +{
> + struct kxtj9_data *tj9 = i2c_get_clientdata(client);
> +
> +#ifdef CONFIG_KXTJ9_POLLED_MODE
> + kxtj9_teardown_polled_device(tj9);
> +#else
> + sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
> + free_irq(client->irq, tj9);
> + input_unregister_device(tj9->input_dev);
> +#endif
> + if (tj9->pdata.exit)
> + tj9->pdata.exit();
> +
> + kfree(tj9);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int kxtj9_suspend(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct kxtj9_data *tj9 = i2c_get_clientdata(client);
> + struct input_dev *input_dev = tj9->input_dev;
> +
> + mutex_lock(&input_dev->mutex);
> +
> + if (input_dev->users)
> + kxtj9_device_power_off(tj9);
> +
> + mutex_unlock(&input_dev->mutex);
> + return 0;
> +}
> +
> +static int kxtj9_resume(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct kxtj9_data *tj9 = i2c_get_clientdata(client);
> + struct input_dev *input_dev = tj9->input_dev;
> + int retval = 0;
> +
> + mutex_lock(&input_dev->mutex);
> +
> + if (input_dev->users)
> + kxtj9_enable(tj9);
> +
> + mutex_unlock(&input_dev->mutex);
> + return retval;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
> +
> +static const struct i2c_device_id kxtj9_id[] = {
> + { NAME, 0 },
> + { },
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, kxtj9_id);
> +
> +static struct i2c_driver kxtj9_driver = {
> + .driver = {
> + .name = NAME,
> + .owner = THIS_MODULE,
> + .pm = &kxtj9_pm_ops,
> + },
> + .probe = kxtj9_probe,
> + .remove = __devexit_p(kxtj9_remove),
> + .id_table = kxtj9_id,
> +};
> +
> +static int __init kxtj9_init(void)
> +{
> + return i2c_add_driver(&kxtj9_driver);
> +}
> +module_init(kxtj9_init);
> +
> +static void __exit kxtj9_exit(void)
> +{
> + i2c_del_driver(&kxtj9_driver);
> +}
> +module_exit(kxtj9_exit);
> +
> +MODULE_DESCRIPTION("KXTJ9 accelerometer driver");
> +MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/input/kxtj9.h b/include/linux/input/kxtj9.h
> index 0000000..cc5928b
> --- /dev/null
> +++ b/include/linux/input/kxtj9.h
> @@ -0,0 +1,76 @@
> +/*
> + * Copyright (C) 2011 Kionix, Inc.
> + * Written by Chris Hudson <chudson@kionix.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
> + * 02111-1307, USA
> + */
> +
> +#ifndef __KXTJ9_H__
> +#define __KXTJ9_H__
> +
> +#define KXTJ9_I2C_ADDR 0x0F
> +
> +/* These shift values are used to provide consistent output data */
> +#define SHIFT_ADJ_2G 4
> +#define SHIFT_ADJ_4G 3
> +#define SHIFT_ADJ_8G 2
> +
> +#ifdef __KERNEL__
> +struct kxtj9_platform_data {
> + int poll_interval; /* current poll interval (in milli-seconds) */
> + int min_interval; /* minimum poll interval (in milli-seconds) */
> +
> + /* by default, x is axis 0, y is axis 1, z is axis 2; these can be
> + changed to account for sensor orientation within the host device */
> + u8 axis_map_x;
> + u8 axis_map_y;
> + u8 axis_map_z;
> +
> + /* each axis can be negated to account for sensor orientation within
> + the host device; 1 = negate this axis; 0 = do not negate this axis */
> + bool negate_x;
> + bool negate_y;
> + bool negate_z;
> +
> + /* CTRL_REG1: set resolution, g-range, data ready enable */
> + /* Output resolution: 8-bit valid or 12-bit valid */
> + #define RES_8BIT 0
> + #define RES_12BIT (1 << 6)
> + u8 res_12bit;
> + /* Output g-range: +/-2g, 4g, or 8g */
> + #define KXTJ9_G_2G 0
> + #define KXTJ9_G_4G (1 << 3)
> + #define KXTJ9_G_8G (1 << 4)
> + u8 g_range;
> +
> + /* DATA_CTRL_REG: controls the output data rate of the part */
> + #define ODR12_5F 0
> + #define ODR25F 1
> + #define ODR50F 2
> + #define ODR100F 3
> + #define ODR200F 4
> + #define ODR400F 5
> + #define ODR800F 6
> + u8 data_odr_init;
> +
> + int (*init)(void);
> + void (*exit)(void);
> + int (*power_on)(void);
> + int (*power_off)(void);
> +};
> +#endif /* __KERNEL__ */
> +
> +#endif /* __KXTJ9_H__ */
> +
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer
2011-06-29 12:39 ` Jonathan Cameron
@ 2011-07-04 14:25 ` Dmitry Torokhov
0 siblings, 0 replies; 8+ messages in thread
From: Dmitry Torokhov @ 2011-07-04 14:25 UTC (permalink / raw)
To: Jonathan Cameron; +Cc: chris.hudson.comp.eng, linux-input, Chris Hudson
On Wed, Jun 29, 2011 at 01:39:41PM +0100, Jonathan Cameron wrote:
> On 06/28/11 20:12, chris.hudson.comp.eng@gmail.com wrote:
> > +static int kxtj9_input_open(struct input_dev *input)
> > +{
> > + struct kxtj9_data *tj9 = input_get_drvdata(input);
> > +
> > + return kxtj9_enable(tj9);
> return kxtj9_enable(input_get_drvdata(input));
Actually I prefer limiting use of void pointers. If kxtj9_enable()
changes to take something other than "struct kxtj9_data *" then original
will issue a warning while the proposed form will happily accept it.
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer
2011-06-28 19:12 [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer chris.hudson.comp.eng
2011-06-29 12:39 ` Jonathan Cameron
@ 2011-07-04 16:26 ` Dmitry Torokhov
2011-07-05 18:08 ` Christopher Hudson
1 sibling, 1 reply; 8+ messages in thread
From: Dmitry Torokhov @ 2011-07-04 16:26 UTC (permalink / raw)
To: chris.hudson.comp.eng; +Cc: linux-input, jic23, Chris Hudson
Hi Chris,
On Tue, Jun 28, 2011 at 03:12:28PM -0400, chris.hudson.comp.eng@gmail.com wrote:
> From: Chris Hudson <chudson@kionix.com>
>
> - Added Dmitry's changes
> - Now using input_polled_dev interface when in polling mode
> - IRQ mode provides a separate sysfs node to change the hardware data rate
>
I am not happy with the fact that this implementation forces users to
select polled or interrupt-driven mode at compile time. The patch I
proposed had polled mode support optional and would automatically select
IRQ mode for clients that have interrupt assigned and try to activate
polled mode if interrupt is not available.
> +
> +/* kxtj9_set_poll: allows the user to select a new poll interval (in ms) */
> +static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
> +
> + unsigned long interval;
> + int ret = kstrtoul(buf, 10, &interval);
> + if (ret < 0)
> + return ret;
> +
> + mutex_lock(&tj9->lock);
> + /* set current interval to the greater of the minimum interval or
> + the requested interval */
> + tj9->last_poll_interval = max((int)interval, tj9->pdata.min_interval);
> + mutex_unlock(&tj9->lock);
This lock does not make sense. You are protecting update of last_poll_interval
field and this update can not be partial (i.e. only LSB or MSB is
written) on all our arches, but you do not protect kxtj9_update_odr()
which alters chip state and does need protection.
I tried bringing forward my changes from the older patch into yours,
please let me know if the patch below works on top of yours.
Thanks.
--
Dmitry
Input: ktxj9 - misc fixes
From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
drivers/input/misc/Kconfig | 34 ++--
drivers/input/misc/kxtj9.c | 355 +++++++++++++++++++++++--------------------
include/linux/input/kxtj9.h | 18 +-
3 files changed, 220 insertions(+), 187 deletions(-)
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 41628d2..0134428 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -230,6 +230,23 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_KXTJ9
+ tristate "Kionix KXTJ9 tri-axis digital accelerometer"
+ depends on I2C
+ help
+ Say Y here to enable support for the Kionix KXTJ9 digital tri-axis
+ accelerometer.
+
+ To compile this driver as a module, choose M here: the module will
+ be called kxtj9.
+
+config INPUT_KXTJ9_POLLED_MODE
+ bool "Enable polling mode support"
+ depends on INPUT_KXTJ9
+ select INPUT_POLLDEV
+ help
+ Say Y here if you need accelerometer to work in polling mode.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
@@ -499,21 +516,4 @@ config INPUT_XEN_KBDDEV_FRONTEND
To compile this driver as a module, choose M here: the
module will be called xen-kbdfront.
-config INPUT_KXTJ9
- tristate "Kionix KXTJ9 tri-axis digital accelerometer"
- depends on I2C
- help
- If you say yes here you get support for the Kionix KXTJ9 digital
- tri-axis accelerometer.
-
- This driver can also be built as a module. If so, the module
- will be called kxtj9.
-
-config KXTJ9_POLLED_MODE
- bool "Enable polling mode support"
- depends on INPUT_KXTJ9
- select INPUT_POLLDEV
- help
- Say Y here if you need accelerometer to work in polling mode.
-
endif
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index 7faf155..31eae6a 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -75,10 +75,8 @@ struct kxtj9_data {
struct i2c_client *client;
struct kxtj9_platform_data pdata;
struct input_dev *input_dev;
-#ifdef CONFIG_KXTJ9_POLLED_MODE
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
struct input_polled_dev *poll_dev;
-#else
- struct mutex lock;
#endif
unsigned int last_poll_interval;
u8 shift;
@@ -117,16 +115,32 @@ static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
if (err < 0)
dev_err(&tj9->client->dev, "accelerometer data read failed\n");
- x = le16_to_cpu(acc_data[0]) >> tj9->shift;
- y = le16_to_cpu(acc_data[1]) >> tj9->shift;
- z = le16_to_cpu(acc_data[2]) >> tj9->shift;
+ x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift;
+ y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift;
+ z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift;
input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
- input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_x ? -y : y);
+ input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_z ? -z : z);
input_sync(tj9->input_dev);
}
+static irqreturn_t kxtj9_isr(int irq, void *dev)
+{
+ struct kxtj9_data *tj9 = dev;
+ int err;
+
+ /* data ready is the only possible interrupt type */
+ kxtj9_report_acceleration_data(tj9);
+
+ err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+ if (err < 0)
+ dev_err(&tj9->client->dev,
+ "error clearing interrupt status: %d\n", err);
+
+ return IRQ_HANDLED;
+}
+
static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
{
switch (new_g_range) {
@@ -152,7 +166,7 @@ static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
return 0;
}
-static int kxtj9_update_odr(struct kxtj9_data *tj9, int poll_interval)
+static int kxtj9_update_odr(struct kxtj9_data *tj9, unsigned int poll_interval)
{
int err;
int i;
@@ -245,7 +259,7 @@ static int kxtj9_enable(struct kxtj9_data *tj9)
err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
if (err < 0) {
dev_err(&tj9->client->dev,
- "error clearing interrupt: %d\n", err);
+ "error clearing interrupt: %d\n", err);
goto fail;
}
}
@@ -257,8 +271,27 @@ fail:
return err;
}
+static void kxtj9_disable(struct kxtj9_data *tj9)
+{
+ kxtj9_device_power_off(tj9);
+}
+
+static int kxtj9_input_open(struct input_dev *input)
+{
+ struct kxtj9_data *tj9 = input_get_drvdata(input);
+
+ return kxtj9_enable(tj9);
+}
+
+static void kxtj9_input_close(struct input_dev *dev)
+{
+ struct kxtj9_data *tj9 = input_get_drvdata(dev);
+
+ kxtj9_disable(tj9);
+}
+
static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
- struct input_dev *input_dev)
+ struct input_dev *input_dev)
{
__set_bit(EV_ABS, input_dev->evbit);
input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
@@ -270,7 +303,104 @@ static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
input_dev->dev.parent = &tj9->client->dev;
}
-#ifdef CONFIG_KXTJ9_POLLED_MODE
+static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!tj9->input_dev) {
+ dev_err(&tj9->client->dev, "input device allocate failed\n");
+ return -ENOMEM;
+ }
+
+ tj9->input_dev = input_dev;
+
+ input_dev->open = kxtj9_input_open;
+ input_dev->close = kxtj9_input_close;
+ input_set_drvdata(input_dev, tj9);
+
+ kxtj9_init_input_device(tj9, input_dev);
+
+ err = input_register_device(tj9->input_dev);
+ if (err) {
+ dev_err(&tj9->client->dev,
+ "unable to register input polled device %s: %d\n",
+ tj9->input_dev->name, err);
+ input_free_device(tj9->input_dev);
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * When IRQ mode is selected, we need to provide an interface to allow the user
+ * to change the output data rate of the part. For consistency, we are using
+ * the set_poll method, which accepts a poll interval in milliseconds, and then
+ * calls update_odr() while passing this value as an argument. In IRQ mode, the
+ * data outputs will not be read AT the requested poll interval, rather, the
+ * lowest ODR that can support the requested interval. The client application
+ * will be responsible for retrieving data from the input node at the desired
+ * interval.
+ */
+
+/* Returns currently selected poll interval (in ms) */
+static ssize_t kxtj9_get_poll(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", tj9->last_poll_interval);
+}
+
+/* Allow users to select a new poll interval (in ms) */
+static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+ struct input_dev *input_dev = tj9->input_dev;
+ unsigned int interval;
+ int error;
+
+ error = kstrtouint(buf, 10, &interval);
+ if (error < 0)
+ return error;
+
+ /* Lock the device to prevent races with open/close (and itself) */
+ mutex_unlock(&input_dev->mutex);
+
+ disable_irq(client->irq);
+
+ /*
+ * Set current interval to the greater of the minimum interval or
+ * the requested interval
+ */
+ tj9->last_poll_interval = max(interval, tj9->pdata.min_interval);
+
+ kxtj9_update_odr(tj9, tj9->last_poll_interval);
+
+ enable_irq(client->irq);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
+
+static struct attribute *kxtj9_attributes[] = {
+ &dev_attr_poll.attr,
+ NULL
+};
+
+static struct attribute_group kxtj9_attribute_group = {
+ .attrs = kxtj9_attributes
+};
+
+
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
static void kxtj9_poll(struct input_polled_dev *dev)
{
struct kxtj9_data *tj9 = dev->private;
@@ -295,7 +425,7 @@ static void kxtj9_polled_input_close(struct input_polled_dev *dev)
{
struct kxtj9_data *tj9 = dev->private;
- kxtj9_device_power_off(tj9);
+ kxtj9_disable(tj9);
}
static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9)
@@ -325,7 +455,7 @@ static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9)
dev_err(&tj9->client->dev,
"Unable to register polled device, err=%d\n", err);
input_free_polled_device(poll_dev);
- return 0;
+ return err;
}
return 0;
@@ -337,66 +467,7 @@ static void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
input_free_polled_device(tj9->poll_dev);
}
-#else /* IRQ Mode is enabled */
-static int kxtj9_input_open(struct input_dev *input)
-{
- struct kxtj9_data *tj9 = input_get_drvdata(input);
-
- return kxtj9_enable(tj9);
-}
-
-static void kxtj9_input_close(struct input_dev *dev)
-{
- struct kxtj9_data *tj9 = input_get_drvdata(dev);
-
- kxtj9_device_power_off(tj9);
-}
-
-static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9)
-{
- struct input_dev *input_dev;
- int err;
-
- input_dev = input_allocate_device();
- if (!tj9->input_dev) {
- dev_err(&tj9->client->dev, "input device allocate failed\n");
- return -ENOMEM;
- }
-
- tj9->input_dev = input_dev;
-
- input_dev->open = kxtj9_input_open;
- input_dev->close = kxtj9_input_close;
- input_set_drvdata(input_dev, tj9);
-
- kxtj9_init_input_device(tj9, input_dev);
-
- err = input_register_device(tj9->input_dev);
- if (err) {
- dev_err(&tj9->client->dev,
- "unable to register input polled device %s: %d\n",
- tj9->input_dev->name, err);
- input_free_device(tj9->input_dev);
- }
-
- return 0;
-}
-
-static irqreturn_t kxtj9_isr(int irq, void *dev)
-{
- struct kxtj9_data *tj9 = dev;
- int err;
-
- /* data ready is the only possible interrupt type */
- kxtj9_report_acceleration_data(tj9);
-
- err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
- if (err < 0)
- dev_err(&tj9->client->dev,
- "error clearing interrupt status: %d\n", err);
-
- return IRQ_HANDLED;
-}
+#else
static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
{
@@ -407,67 +478,22 @@ static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
{
}
-/* kxtj9_get_poll: returns the currently selected poll interval (in ms) */
-static ssize_t kxtj9_get_poll(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
- return sprintf(buf, "%d\n", tj9->last_poll_interval);
-}
-
-/* kxtj9_set_poll: allows the user to select a new poll interval (in ms) */
-static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
-
- unsigned long interval;
- int ret = kstrtoul(buf, 10, &interval);
- if (ret < 0)
- return ret;
-
- mutex_lock(&tj9->lock);
- /* set current interval to the greater of the minimum interval or
- the requested interval */
- tj9->last_poll_interval = max((int)interval, tj9->pdata.min_interval);
- mutex_unlock(&tj9->lock);
-
- kxtj9_update_odr(tj9, tj9->last_poll_interval);
-
- return count;
-}
-/* When IRQ mode is selected, we need to provide an interface to allow the user
- * to change the output data rate of the part. For consistency, we are using
- * the set_poll method, which accepts a poll interval in milliseconds, and then
- * calls update_odr() while passing this value as an argument. In IRQ mode, the
- * data outputs will not be read AT the requested poll interval, rather, the
- * lowest ODR that can support the requested interval. The client application
- * will be responsible for retrieving data from the input node at the desired
- * interval.
- */
-static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
-
-static struct attribute *kxtj9_attributes[] = {
- &dev_attr_poll.attr,
- NULL
-};
-
-static struct attribute_group kxtj9_attribute_group = {
- .attrs = kxtj9_attributes
-};
#endif
static int __devinit kxtj9_verify(struct kxtj9_data *tj9)
{
int retval;
+
retval = kxtj9_device_power_on(tj9);
if (retval < 0)
return retval;
+
retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
if (retval < 0) {
dev_err(&tj9->client->dev, "read err int source\n");
goto out;
}
+
retval = retval != 0x06 ? -EIO : 0;
out:
@@ -476,7 +502,7 @@ out:
}
static int __devinit kxtj9_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
const struct kxtj9_platform_data *pdata = client->dev.platform_data;
struct kxtj9_data *tj9;
@@ -487,10 +513,12 @@ static int __devinit kxtj9_probe(struct i2c_client *client,
dev_err(&client->dev, "client is not i2c capable\n");
return -ENXIO;
}
+
if (!pdata) {
dev_err(&client->dev, "platform data is NULL; exiting\n");
return -EINVAL;
}
+
tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL);
if (!tj9) {
dev_err(&client->dev,
@@ -513,42 +541,46 @@ static int __devinit kxtj9_probe(struct i2c_client *client,
goto err_pdata_exit;
}
+ i2c_set_clientdata(client, tj9);
+
tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
tj9->data_ctrl = tj9->pdata.data_odr_init;
-#ifdef CONFIG_KXTJ9_POLLED_MODE
- err = kxtj9_setup_polled_device(tj9);
- if (err)
- goto err_pdata_exit;
-#else
- /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
- tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
- tj9->ctrl_reg1 |= DRDYE;
+ if (client->irq) {
+ /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
+ tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
+ tj9->ctrl_reg1 |= DRDYE;
+
+ err = kxtj9_setup_input_device(tj9);
+ if (err)
+ goto err_pdata_exit;
+
+ err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "kxtj9-irq", tj9);
+ if (err) {
+ dev_err(&client->dev, "request irq failed: %d\n", err);
+ goto err_destroy_input;
+ }
- err = kxtj9_setup_input_device(tj9);
- if (err)
- goto err_pdata_exit;
+ err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
+ if (err) {
+ dev_err(&client->dev, "sysfs create failed: %d\n", err);
+ goto err_free_irq;
+ }
- err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- "kxtj9-irq", tj9);
- if (err) {
- dev_err(&client->dev, "request irq failed: %d\n", err);
- goto err_destroy_input;
- }
- err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
- if (err) {
- dev_err(&client->dev, "sysfs create failed: %d\n", err);
- goto err_destroy_input;
+ } else {
+ err = kxtj9_setup_polled_device(tj9);
+ if (err)
+ goto err_pdata_exit;
}
-#endif
- i2c_set_clientdata(client, tj9);
+
return 0;
-#ifndef CONFIG_KXTJ9_POLLED_MODE
+err_free_irq:
+ free_irq(client->irq, tj9);
err_destroy_input:
input_unregister_device(tj9->input_dev);
-#endif
err_pdata_exit:
if (tj9->pdata.exit)
tj9->pdata.exit();
@@ -561,13 +593,14 @@ static int __devexit kxtj9_remove(struct i2c_client *client)
{
struct kxtj9_data *tj9 = i2c_get_clientdata(client);
-#ifdef CONFIG_KXTJ9_POLLED_MODE
- kxtj9_teardown_polled_device(tj9);
-#else
- sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
- free_irq(client->irq, tj9);
- input_unregister_device(tj9->input_dev);
-#endif
+ if (client->irq) {
+ sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
+ free_irq(client->irq, tj9);
+ input_unregister_device(tj9->input_dev);
+ } else {
+ kxtj9_teardown_polled_device(tj9);
+ }
+
if (tj9->pdata.exit)
tj9->pdata.exit();
@@ -620,13 +653,13 @@ MODULE_DEVICE_TABLE(i2c, kxtj9_id);
static struct i2c_driver kxtj9_driver = {
.driver = {
- .name = NAME,
- .owner = THIS_MODULE,
- .pm = &kxtj9_pm_ops,
+ .name = NAME,
+ .owner = THIS_MODULE,
+ .pm = &kxtj9_pm_ops,
},
- .probe = kxtj9_probe,
- .remove = __devexit_p(kxtj9_remove),
- .id_table = kxtj9_id,
+ .probe = kxtj9_probe,
+ .remove = __devexit_p(kxtj9_remove),
+ .id_table = kxtj9_id,
};
static int __init kxtj9_init(void)
diff --git a/include/linux/input/kxtj9.h b/include/linux/input/kxtj9.h
index cc5928b..0b546bd 100644
--- a/include/linux/input/kxtj9.h
+++ b/include/linux/input/kxtj9.h
@@ -27,19 +27,21 @@
#define SHIFT_ADJ_4G 3
#define SHIFT_ADJ_8G 2
-#ifdef __KERNEL__
struct kxtj9_platform_data {
- int poll_interval; /* current poll interval (in milli-seconds) */
- int min_interval; /* minimum poll interval (in milli-seconds) */
+ unsigned int min_interval; /* minimum poll interval (in milli-seconds) */
- /* by default, x is axis 0, y is axis 1, z is axis 2; these can be
- changed to account for sensor orientation within the host device */
+ /*
+ * By default, x is axis 0, y is axis 1, z is axis 2; these can be
+ * changed to account for sensor orientation within the host device.
+ */
u8 axis_map_x;
u8 axis_map_y;
u8 axis_map_z;
- /* each axis can be negated to account for sensor orientation within
- the host device; 1 = negate this axis; 0 = do not negate this axis */
+ /*
+ * Each axis can be negated to account for sensor orientation within
+ * the host device.
+ */
bool negate_x;
bool negate_y;
bool negate_z;
@@ -70,7 +72,5 @@ struct kxtj9_platform_data {
int (*power_on)(void);
int (*power_off)(void);
};
-#endif /* __KERNEL__ */
-
#endif /* __KXTJ9_H__ */
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer
2011-07-04 16:26 ` Dmitry Torokhov
@ 2011-07-05 18:08 ` Christopher Hudson
2011-07-05 18:39 ` Dmitry Torokhov
0 siblings, 1 reply; 8+ messages in thread
From: Christopher Hudson @ 2011-07-05 18:08 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, jic23, Chris Hudson
On Mon, Jul 4, 2011 at 12:26 PM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> Hi Chris,
>
> On Tue, Jun 28, 2011 at 03:12:28PM -0400, chris.hudson.comp.eng@gmail.com wrote:
>> From: Chris Hudson <chudson@kionix.com>
>>
>> - Added Dmitry's changes
>> - Now using input_polled_dev interface when in polling mode
>> - IRQ mode provides a separate sysfs node to change the hardware data rate
>>
>
> I am not happy with the fact that this implementation forces users to
> select polled or interrupt-driven mode at compile time. The patch I
> proposed had polled mode support optional and would automatically select
> IRQ mode for clients that have interrupt assigned and try to activate
> polled mode if interrupt is not available.
>
>> +
>> +/* kxtj9_set_poll: allows the user to select a new poll interval (in ms) */
>> +static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
>> + const char *buf, size_t count)
>> +{
>> + struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
>> +
>> + unsigned long interval;
>> + int ret = kstrtoul(buf, 10, &interval);
>> + if (ret < 0)
>> + return ret;
>> +
>> + mutex_lock(&tj9->lock);
>> + /* set current interval to the greater of the minimum interval or
>> + the requested interval */
>> + tj9->last_poll_interval = max((int)interval, tj9->pdata.min_interval);
>> + mutex_unlock(&tj9->lock);
>
> This lock does not make sense. You are protecting update of last_poll_interval
> field and this update can not be partial (i.e. only LSB or MSB is
> written) on all our arches, but you do not protect kxtj9_update_odr()
> which alters chip state and does need protection.
>
> I tried bringing forward my changes from the older patch into yours,
> please let me know if the patch below works on top of yours.
Hi Dmitry,
Thanks for all your hard work, and yes it works fine. There were a
couple of changes proposed by Jonathan that I had already merged into
my version though; should I submit these in a separate patch or resend
the merged version?
Thank you,
Chris
>
> Thanks.
>
> --
> Dmitry
>
>
> Input: ktxj9 - misc fixes
>
> From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
>
> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
> ---
>
> drivers/input/misc/Kconfig | 34 ++--
> drivers/input/misc/kxtj9.c | 355 +++++++++++++++++++++++--------------------
> include/linux/input/kxtj9.h | 18 +-
> 3 files changed, 220 insertions(+), 187 deletions(-)
>
>
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index 41628d2..0134428 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -230,6 +230,23 @@ config INPUT_KEYSPAN_REMOTE
> To compile this driver as a module, choose M here: the module will
> be called keyspan_remote.
>
> +config INPUT_KXTJ9
> + tristate "Kionix KXTJ9 tri-axis digital accelerometer"
> + depends on I2C
> + help
> + Say Y here to enable support for the Kionix KXTJ9 digital tri-axis
> + accelerometer.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called kxtj9.
> +
> +config INPUT_KXTJ9_POLLED_MODE
> + bool "Enable polling mode support"
> + depends on INPUT_KXTJ9
> + select INPUT_POLLDEV
> + help
> + Say Y here if you need accelerometer to work in polling mode.
> +
> config INPUT_POWERMATE
> tristate "Griffin PowerMate and Contour Jog support"
> depends on USB_ARCH_HAS_HCD
> @@ -499,21 +516,4 @@ config INPUT_XEN_KBDDEV_FRONTEND
> To compile this driver as a module, choose M here: the
> module will be called xen-kbdfront.
>
> -config INPUT_KXTJ9
> - tristate "Kionix KXTJ9 tri-axis digital accelerometer"
> - depends on I2C
> - help
> - If you say yes here you get support for the Kionix KXTJ9 digital
> - tri-axis accelerometer.
> -
> - This driver can also be built as a module. If so, the module
> - will be called kxtj9.
> -
> -config KXTJ9_POLLED_MODE
> - bool "Enable polling mode support"
> - depends on INPUT_KXTJ9
> - select INPUT_POLLDEV
> - help
> - Say Y here if you need accelerometer to work in polling mode.
> -
> endif
> diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
> index 7faf155..31eae6a 100644
> --- a/drivers/input/misc/kxtj9.c
> +++ b/drivers/input/misc/kxtj9.c
> @@ -75,10 +75,8 @@ struct kxtj9_data {
> struct i2c_client *client;
> struct kxtj9_platform_data pdata;
> struct input_dev *input_dev;
> -#ifdef CONFIG_KXTJ9_POLLED_MODE
> +#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
> struct input_polled_dev *poll_dev;
> -#else
> - struct mutex lock;
> #endif
> unsigned int last_poll_interval;
> u8 shift;
> @@ -117,16 +115,32 @@ static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
> if (err < 0)
> dev_err(&tj9->client->dev, "accelerometer data read failed\n");
>
> - x = le16_to_cpu(acc_data[0]) >> tj9->shift;
> - y = le16_to_cpu(acc_data[1]) >> tj9->shift;
> - z = le16_to_cpu(acc_data[2]) >> tj9->shift;
> + x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift;
> + y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift;
> + z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift;
>
> input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
> input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
> - input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_x ? -y : y);
> + input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_z ? -z : z);
> input_sync(tj9->input_dev);
> }
>
> +static irqreturn_t kxtj9_isr(int irq, void *dev)
> +{
> + struct kxtj9_data *tj9 = dev;
> + int err;
> +
> + /* data ready is the only possible interrupt type */
> + kxtj9_report_acceleration_data(tj9);
> +
> + err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
> + if (err < 0)
> + dev_err(&tj9->client->dev,
> + "error clearing interrupt status: %d\n", err);
> +
> + return IRQ_HANDLED;
> +}
> +
> static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
> {
> switch (new_g_range) {
> @@ -152,7 +166,7 @@ static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
> return 0;
> }
>
> -static int kxtj9_update_odr(struct kxtj9_data *tj9, int poll_interval)
> +static int kxtj9_update_odr(struct kxtj9_data *tj9, unsigned int poll_interval)
> {
> int err;
> int i;
> @@ -245,7 +259,7 @@ static int kxtj9_enable(struct kxtj9_data *tj9)
> err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
> if (err < 0) {
> dev_err(&tj9->client->dev,
> - "error clearing interrupt: %d\n", err);
> + "error clearing interrupt: %d\n", err);
> goto fail;
> }
> }
> @@ -257,8 +271,27 @@ fail:
> return err;
> }
>
> +static void kxtj9_disable(struct kxtj9_data *tj9)
> +{
> + kxtj9_device_power_off(tj9);
> +}
> +
> +static int kxtj9_input_open(struct input_dev *input)
> +{
> + struct kxtj9_data *tj9 = input_get_drvdata(input);
> +
> + return kxtj9_enable(tj9);
> +}
> +
> +static void kxtj9_input_close(struct input_dev *dev)
> +{
> + struct kxtj9_data *tj9 = input_get_drvdata(dev);
> +
> + kxtj9_disable(tj9);
> +}
> +
> static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
> - struct input_dev *input_dev)
> + struct input_dev *input_dev)
> {
> __set_bit(EV_ABS, input_dev->evbit);
> input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
> @@ -270,7 +303,104 @@ static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
> input_dev->dev.parent = &tj9->client->dev;
> }
>
> -#ifdef CONFIG_KXTJ9_POLLED_MODE
> +static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9)
> +{
> + struct input_dev *input_dev;
> + int err;
> +
> + input_dev = input_allocate_device();
> + if (!tj9->input_dev) {
> + dev_err(&tj9->client->dev, "input device allocate failed\n");
> + return -ENOMEM;
> + }
> +
> + tj9->input_dev = input_dev;
> +
> + input_dev->open = kxtj9_input_open;
> + input_dev->close = kxtj9_input_close;
> + input_set_drvdata(input_dev, tj9);
> +
> + kxtj9_init_input_device(tj9, input_dev);
> +
> + err = input_register_device(tj9->input_dev);
> + if (err) {
> + dev_err(&tj9->client->dev,
> + "unable to register input polled device %s: %d\n",
> + tj9->input_dev->name, err);
> + input_free_device(tj9->input_dev);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * When IRQ mode is selected, we need to provide an interface to allow the user
> + * to change the output data rate of the part. For consistency, we are using
> + * the set_poll method, which accepts a poll interval in milliseconds, and then
> + * calls update_odr() while passing this value as an argument. In IRQ mode, the
> + * data outputs will not be read AT the requested poll interval, rather, the
> + * lowest ODR that can support the requested interval. The client application
> + * will be responsible for retrieving data from the input node at the desired
> + * interval.
> + */
> +
> +/* Returns currently selected poll interval (in ms) */
> +static ssize_t kxtj9_get_poll(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct kxtj9_data *tj9 = i2c_get_clientdata(client);
> +
> + return sprintf(buf, "%d\n", tj9->last_poll_interval);
> +}
> +
> +/* Allow users to select a new poll interval (in ms) */
> +static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct kxtj9_data *tj9 = i2c_get_clientdata(client);
> + struct input_dev *input_dev = tj9->input_dev;
> + unsigned int interval;
> + int error;
> +
> + error = kstrtouint(buf, 10, &interval);
> + if (error < 0)
> + return error;
> +
> + /* Lock the device to prevent races with open/close (and itself) */
> + mutex_unlock(&input_dev->mutex);
> +
> + disable_irq(client->irq);
> +
> + /*
> + * Set current interval to the greater of the minimum interval or
> + * the requested interval
> + */
> + tj9->last_poll_interval = max(interval, tj9->pdata.min_interval);
> +
> + kxtj9_update_odr(tj9, tj9->last_poll_interval);
> +
> + enable_irq(client->irq);
> + mutex_unlock(&input_dev->mutex);
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
> +
> +static struct attribute *kxtj9_attributes[] = {
> + &dev_attr_poll.attr,
> + NULL
> +};
> +
> +static struct attribute_group kxtj9_attribute_group = {
> + .attrs = kxtj9_attributes
> +};
> +
> +
> +#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
> static void kxtj9_poll(struct input_polled_dev *dev)
> {
> struct kxtj9_data *tj9 = dev->private;
> @@ -295,7 +425,7 @@ static void kxtj9_polled_input_close(struct input_polled_dev *dev)
> {
> struct kxtj9_data *tj9 = dev->private;
>
> - kxtj9_device_power_off(tj9);
> + kxtj9_disable(tj9);
> }
>
> static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9)
> @@ -325,7 +455,7 @@ static int __devinit kxtj9_setup_polled_device(struct kxtj9_data *tj9)
> dev_err(&tj9->client->dev,
> "Unable to register polled device, err=%d\n", err);
> input_free_polled_device(poll_dev);
> - return 0;
> + return err;
> }
>
> return 0;
> @@ -337,66 +467,7 @@ static void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
> input_free_polled_device(tj9->poll_dev);
> }
>
> -#else /* IRQ Mode is enabled */
> -static int kxtj9_input_open(struct input_dev *input)
> -{
> - struct kxtj9_data *tj9 = input_get_drvdata(input);
> -
> - return kxtj9_enable(tj9);
> -}
> -
> -static void kxtj9_input_close(struct input_dev *dev)
> -{
> - struct kxtj9_data *tj9 = input_get_drvdata(dev);
> -
> - kxtj9_device_power_off(tj9);
> -}
> -
> -static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9)
> -{
> - struct input_dev *input_dev;
> - int err;
> -
> - input_dev = input_allocate_device();
> - if (!tj9->input_dev) {
> - dev_err(&tj9->client->dev, "input device allocate failed\n");
> - return -ENOMEM;
> - }
> -
> - tj9->input_dev = input_dev;
> -
> - input_dev->open = kxtj9_input_open;
> - input_dev->close = kxtj9_input_close;
> - input_set_drvdata(input_dev, tj9);
> -
> - kxtj9_init_input_device(tj9, input_dev);
> -
> - err = input_register_device(tj9->input_dev);
> - if (err) {
> - dev_err(&tj9->client->dev,
> - "unable to register input polled device %s: %d\n",
> - tj9->input_dev->name, err);
> - input_free_device(tj9->input_dev);
> - }
> -
> - return 0;
> -}
> -
> -static irqreturn_t kxtj9_isr(int irq, void *dev)
> -{
> - struct kxtj9_data *tj9 = dev;
> - int err;
> -
> - /* data ready is the only possible interrupt type */
> - kxtj9_report_acceleration_data(tj9);
> -
> - err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
> - if (err < 0)
> - dev_err(&tj9->client->dev,
> - "error clearing interrupt status: %d\n", err);
> -
> - return IRQ_HANDLED;
> -}
> +#else
>
> static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
> {
> @@ -407,67 +478,22 @@ static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
> {
> }
>
> -/* kxtj9_get_poll: returns the currently selected poll interval (in ms) */
> -static ssize_t kxtj9_get_poll(struct device *dev,
> - struct device_attribute *attr, char *buf)
> -{
> - struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
> - return sprintf(buf, "%d\n", tj9->last_poll_interval);
> -}
> -
> -/* kxtj9_set_poll: allows the user to select a new poll interval (in ms) */
> -static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
> - const char *buf, size_t count)
> -{
> - struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
> -
> - unsigned long interval;
> - int ret = kstrtoul(buf, 10, &interval);
> - if (ret < 0)
> - return ret;
> -
> - mutex_lock(&tj9->lock);
> - /* set current interval to the greater of the minimum interval or
> - the requested interval */
> - tj9->last_poll_interval = max((int)interval, tj9->pdata.min_interval);
> - mutex_unlock(&tj9->lock);
> -
> - kxtj9_update_odr(tj9, tj9->last_poll_interval);
> -
> - return count;
> -}
> -/* When IRQ mode is selected, we need to provide an interface to allow the user
> - * to change the output data rate of the part. For consistency, we are using
> - * the set_poll method, which accepts a poll interval in milliseconds, and then
> - * calls update_odr() while passing this value as an argument. In IRQ mode, the
> - * data outputs will not be read AT the requested poll interval, rather, the
> - * lowest ODR that can support the requested interval. The client application
> - * will be responsible for retrieving data from the input node at the desired
> - * interval.
> - */
> -static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
> -
> -static struct attribute *kxtj9_attributes[] = {
> - &dev_attr_poll.attr,
> - NULL
> -};
> -
> -static struct attribute_group kxtj9_attribute_group = {
> - .attrs = kxtj9_attributes
> -};
> #endif
>
> static int __devinit kxtj9_verify(struct kxtj9_data *tj9)
> {
> int retval;
> +
> retval = kxtj9_device_power_on(tj9);
> if (retval < 0)
> return retval;
> +
> retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
> if (retval < 0) {
> dev_err(&tj9->client->dev, "read err int source\n");
> goto out;
> }
> +
> retval = retval != 0x06 ? -EIO : 0;
>
> out:
> @@ -476,7 +502,7 @@ out:
> }
>
> static int __devinit kxtj9_probe(struct i2c_client *client,
> - const struct i2c_device_id *id)
> + const struct i2c_device_id *id)
> {
> const struct kxtj9_platform_data *pdata = client->dev.platform_data;
> struct kxtj9_data *tj9;
> @@ -487,10 +513,12 @@ static int __devinit kxtj9_probe(struct i2c_client *client,
> dev_err(&client->dev, "client is not i2c capable\n");
> return -ENXIO;
> }
> +
> if (!pdata) {
> dev_err(&client->dev, "platform data is NULL; exiting\n");
> return -EINVAL;
> }
> +
> tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL);
> if (!tj9) {
> dev_err(&client->dev,
> @@ -513,42 +541,46 @@ static int __devinit kxtj9_probe(struct i2c_client *client,
> goto err_pdata_exit;
> }
>
> + i2c_set_clientdata(client, tj9);
> +
> tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
> tj9->data_ctrl = tj9->pdata.data_odr_init;
>
> -#ifdef CONFIG_KXTJ9_POLLED_MODE
> - err = kxtj9_setup_polled_device(tj9);
> - if (err)
> - goto err_pdata_exit;
> -#else
> - /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
> - tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
> - tj9->ctrl_reg1 |= DRDYE;
> + if (client->irq) {
> + /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
> + tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
> + tj9->ctrl_reg1 |= DRDYE;
> +
> + err = kxtj9_setup_input_device(tj9);
> + if (err)
> + goto err_pdata_exit;
> +
> + err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
> + IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "kxtj9-irq", tj9);
> + if (err) {
> + dev_err(&client->dev, "request irq failed: %d\n", err);
> + goto err_destroy_input;
> + }
>
> - err = kxtj9_setup_input_device(tj9);
> - if (err)
> - goto err_pdata_exit;
> + err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
> + if (err) {
> + dev_err(&client->dev, "sysfs create failed: %d\n", err);
> + goto err_free_irq;
> + }
>
> - err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
> - IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> - "kxtj9-irq", tj9);
> - if (err) {
> - dev_err(&client->dev, "request irq failed: %d\n", err);
> - goto err_destroy_input;
> - }
> - err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
> - if (err) {
> - dev_err(&client->dev, "sysfs create failed: %d\n", err);
> - goto err_destroy_input;
> + } else {
> + err = kxtj9_setup_polled_device(tj9);
> + if (err)
> + goto err_pdata_exit;
> }
> -#endif
> - i2c_set_clientdata(client, tj9);
> +
> return 0;
>
> -#ifndef CONFIG_KXTJ9_POLLED_MODE
> +err_free_irq:
> + free_irq(client->irq, tj9);
> err_destroy_input:
> input_unregister_device(tj9->input_dev);
> -#endif
> err_pdata_exit:
> if (tj9->pdata.exit)
> tj9->pdata.exit();
> @@ -561,13 +593,14 @@ static int __devexit kxtj9_remove(struct i2c_client *client)
> {
> struct kxtj9_data *tj9 = i2c_get_clientdata(client);
>
> -#ifdef CONFIG_KXTJ9_POLLED_MODE
> - kxtj9_teardown_polled_device(tj9);
> -#else
> - sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
> - free_irq(client->irq, tj9);
> - input_unregister_device(tj9->input_dev);
> -#endif
> + if (client->irq) {
> + sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
> + free_irq(client->irq, tj9);
> + input_unregister_device(tj9->input_dev);
> + } else {
> + kxtj9_teardown_polled_device(tj9);
> + }
> +
> if (tj9->pdata.exit)
> tj9->pdata.exit();
>
> @@ -620,13 +653,13 @@ MODULE_DEVICE_TABLE(i2c, kxtj9_id);
>
> static struct i2c_driver kxtj9_driver = {
> .driver = {
> - .name = NAME,
> - .owner = THIS_MODULE,
> - .pm = &kxtj9_pm_ops,
> + .name = NAME,
> + .owner = THIS_MODULE,
> + .pm = &kxtj9_pm_ops,
> },
> - .probe = kxtj9_probe,
> - .remove = __devexit_p(kxtj9_remove),
> - .id_table = kxtj9_id,
> + .probe = kxtj9_probe,
> + .remove = __devexit_p(kxtj9_remove),
> + .id_table = kxtj9_id,
> };
>
> static int __init kxtj9_init(void)
> diff --git a/include/linux/input/kxtj9.h b/include/linux/input/kxtj9.h
> index cc5928b..0b546bd 100644
> --- a/include/linux/input/kxtj9.h
> +++ b/include/linux/input/kxtj9.h
> @@ -27,19 +27,21 @@
> #define SHIFT_ADJ_4G 3
> #define SHIFT_ADJ_8G 2
>
> -#ifdef __KERNEL__
> struct kxtj9_platform_data {
> - int poll_interval; /* current poll interval (in milli-seconds) */
> - int min_interval; /* minimum poll interval (in milli-seconds) */
> + unsigned int min_interval; /* minimum poll interval (in milli-seconds) */
>
> - /* by default, x is axis 0, y is axis 1, z is axis 2; these can be
> - changed to account for sensor orientation within the host device */
> + /*
> + * By default, x is axis 0, y is axis 1, z is axis 2; these can be
> + * changed to account for sensor orientation within the host device.
> + */
> u8 axis_map_x;
> u8 axis_map_y;
> u8 axis_map_z;
>
> - /* each axis can be negated to account for sensor orientation within
> - the host device; 1 = negate this axis; 0 = do not negate this axis */
> + /*
> + * Each axis can be negated to account for sensor orientation within
> + * the host device.
> + */
> bool negate_x;
> bool negate_y;
> bool negate_z;
> @@ -70,7 +72,5 @@ struct kxtj9_platform_data {
> int (*power_on)(void);
> int (*power_off)(void);
> };
> -#endif /* __KERNEL__ */
> -
> #endif /* __KXTJ9_H__ */
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer
2011-07-05 18:08 ` Christopher Hudson
@ 2011-07-05 18:39 ` Dmitry Torokhov
2011-07-12 21:52 ` Christopher Hudson
0 siblings, 1 reply; 8+ messages in thread
From: Dmitry Torokhov @ 2011-07-05 18:39 UTC (permalink / raw)
To: Christopher Hudson; +Cc: linux-input, jic23, Chris Hudson
On Tue, Jul 05, 2011 at 02:08:01PM -0400, Christopher Hudson wrote:
> On Mon, Jul 4, 2011 at 12:26 PM, Dmitry Torokhov
> <dmitry.torokhov@gmail.com> wrote:
> > Hi Chris,
> >
> > On Tue, Jun 28, 2011 at 03:12:28PM -0400, chris.hudson.comp.eng@gmail.com wrote:
> >> From: Chris Hudson <chudson@kionix.com>
> >>
> >> - Added Dmitry's changes
> >> - Now using input_polled_dev interface when in polling mode
> >> - IRQ mode provides a separate sysfs node to change the hardware data rate
> >>
> >
> > I am not happy with the fact that this implementation forces users to
> > select polled or interrupt-driven mode at compile time. The patch I
> > proposed had polled mode support optional and would automatically select
> > IRQ mode for clients that have interrupt assigned and try to activate
> > polled mode if interrupt is not available.
> >
> >> +
> >> +/* kxtj9_set_poll: allows the user to select a new poll interval (in ms) */
> >> +static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
> >> + const char *buf, size_t count)
> >> +{
> >> + struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
> >> +
> >> + unsigned long interval;
> >> + int ret = kstrtoul(buf, 10, &interval);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + mutex_lock(&tj9->lock);
> >> + /* set current interval to the greater of the minimum interval or
> >> + the requested interval */
> >> + tj9->last_poll_interval = max((int)interval, tj9->pdata.min_interval);
> >> + mutex_unlock(&tj9->lock);
> >
> > This lock does not make sense. You are protecting update of last_poll_interval
> > field and this update can not be partial (i.e. only LSB or MSB is
> > written) on all our arches, but you do not protect kxtj9_update_odr()
> > which alters chip state and does need protection.
> >
> > I tried bringing forward my changes from the older patch into yours,
> > please let me know if the patch below works on top of yours.
>
> Hi Dmitry,
> Thanks for all your hard work, and yes it works fine. There were a
> couple of changes proposed by Jonathan that I had already merged into
> my version though; should I submit these in a separate patch or resend
> the merged version?
Cris,
Could you please send the changes proposed by Jonathan as a separate
patch relative to the v5 you posted earlier? Then I can fold v5, mine
and your new patch together and queue the driver for 3.1.
Thanks.
--
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer
2011-07-05 18:39 ` Dmitry Torokhov
@ 2011-07-12 21:52 ` Christopher Hudson
2011-07-12 22:08 ` Dmitry Torokhov
0 siblings, 1 reply; 8+ messages in thread
From: Christopher Hudson @ 2011-07-12 21:52 UTC (permalink / raw)
To: Dmitry Torokhov, Jonathan Cameron, linux-input
Hi Dmitry,
I found a small bug in this driver when using the code as the basis
for testing another piece of hardware...I'm not sure how this got
through before:
+static int __devinit kxtj9_setup_input_device(struct kxtj9_data *tj9)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!tj9->input_dev) {
Should be:
if (!input_dev) {
if (!tj9->input_dev) always returns true in this context, thereby
always following the error path; you can see that nothing is assigned
to tj9->input_dev until after this check.
+ dev_err(&tj9->client->dev, "input device allocate failed\n");
+ return -ENOMEM;
+ }
+
+ tj9->input_dev = input_dev;
Should I submit a small patch against your queued version to get this fixed?
On Tue, Jul 5, 2011 at 2:39 PM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> On Tue, Jul 05, 2011 at 02:08:01PM -0400, Christopher Hudson wrote:
>> On Mon, Jul 4, 2011 at 12:26 PM, Dmitry Torokhov
>> <dmitry.torokhov@gmail.com> wrote:
>> > Hi Chris,
>> >
>> > On Tue, Jun 28, 2011 at 03:12:28PM -0400, chris.hudson.comp.eng@gmail.com wrote:
>> >> From: Chris Hudson <chudson@kionix.com>
>> >>
>> >> - Added Dmitry's changes
>> >> - Now using input_polled_dev interface when in polling mode
>> >> - IRQ mode provides a separate sysfs node to change the hardware data rate
>> >>
>> >
>> > I am not happy with the fact that this implementation forces users to
>> > select polled or interrupt-driven mode at compile time. The patch I
>> > proposed had polled mode support optional and would automatically select
>> > IRQ mode for clients that have interrupt assigned and try to activate
>> > polled mode if interrupt is not available.
>> >
>> >> +
>> >> +/* kxtj9_set_poll: allows the user to select a new poll interval (in ms) */
>> >> +static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
>> >> + const char *buf, size_t count)
>> >> +{
>> >> + struct kxtj9_data *tj9 = i2c_get_clientdata(to_i2c_client(dev));
>> >> +
>> >> + unsigned long interval;
>> >> + int ret = kstrtoul(buf, 10, &interval);
>> >> + if (ret < 0)
>> >> + return ret;
>> >> +
>> >> + mutex_lock(&tj9->lock);
>> >> + /* set current interval to the greater of the minimum interval or
>> >> + the requested interval */
>> >> + tj9->last_poll_interval = max((int)interval, tj9->pdata.min_interval);
>> >> + mutex_unlock(&tj9->lock);
>> >
>> > This lock does not make sense. You are protecting update of last_poll_interval
>> > field and this update can not be partial (i.e. only LSB or MSB is
>> > written) on all our arches, but you do not protect kxtj9_update_odr()
>> > which alters chip state and does need protection.
>> >
>> > I tried bringing forward my changes from the older patch into yours,
>> > please let me know if the patch below works on top of yours.
>>
>> Hi Dmitry,
>> Thanks for all your hard work, and yes it works fine. There were a
>> couple of changes proposed by Jonathan that I had already merged into
>> my version though; should I submit these in a separate patch or resend
>> the merged version?
>
> Cris,
>
> Could you please send the changes proposed by Jonathan as a separate
> patch relative to the v5 you posted earlier? Then I can fold v5, mine
> and your new patch together and queue the driver for 3.1.
>
> Thanks.
>
> --
> Dmitry
>
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer
2011-07-12 21:52 ` Christopher Hudson
@ 2011-07-12 22:08 ` Dmitry Torokhov
0 siblings, 0 replies; 8+ messages in thread
From: Dmitry Torokhov @ 2011-07-12 22:08 UTC (permalink / raw)
To: Christopher Hudson; +Cc: Jonathan Cameron, linux-input
On Tuesday, July 12, 2011 02:52:20 PM Christopher Hudson wrote:
> Should I submit a small patch against your queued version to get this
> fixed?
That'd be great since you could verify that it actually runs
on the hardware.
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2011-07-12 22:08 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-06-28 19:12 [PATCH v5] input: Initial support for Kionix KXTJ9 accelerometer chris.hudson.comp.eng
2011-06-29 12:39 ` Jonathan Cameron
2011-07-04 14:25 ` Dmitry Torokhov
2011-07-04 16:26 ` Dmitry Torokhov
2011-07-05 18:08 ` Christopher Hudson
2011-07-05 18:39 ` Dmitry Torokhov
2011-07-12 21:52 ` Christopher Hudson
2011-07-12 22:08 ` Dmitry Torokhov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).