linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] input: joystick:   Adding Austria Microsystem AS5011 joystick driver (second version)
@ 2010-12-30 10:37 Fabien Marteau
  2010-12-31 10:14 ` Trilok Soni
  2011-01-02  7:58 ` Dmitry Torokhov
  0 siblings, 2 replies; 5+ messages in thread
From: Fabien Marteau @ 2010-12-30 10:37 UTC (permalink / raw)
  To: Dmitry Torokhov, Scott Moreau; +Cc: linux-input, linux-kernel

Driver for EasyPoint AS5011 2 axis joystick chip. This chip is plugged
on an I2C bus. It has been tested on ARM processor (i.MX27).

Interruptions are now managed by threaded irq.

Signed-off-by: Fabien Marteau <fabien.marteau@armadeus.com>
---
 drivers/input/joystick/Kconfig  |    9 +
 drivers/input/joystick/Makefile |    1 +
 drivers/input/joystick/as5011.c |  417 +++++++++++++++++++++++++++++++++++++++
 include/linux/as5011.h          |   76 +++++++
 4 files changed, 503 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/joystick/as5011.c
 create mode 100644 include/linux/as5011.h

diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 5b59616..5fad03e 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -255,6 +255,15 @@ config JOYSTICK_AMIGA
 	  To compile this driver as a module, choose M here: the
 	  module will be called amijoy.

+config JOYSTICK_AS5011
+	tristate "Austria Microsystem AS5011 joystick"
+	depends on I2C
+	help
+	  Say Y here if you have an AS5011 digital joystick.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called as5011.
+
 config JOYSTICK_JOYDUMP
 	tristate "Gameport data dumper"
 	select GAMEPORT
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index f3a8cbe..92dc0de 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -7,6 +7,7 @@
 obj-$(CONFIG_JOYSTICK_A3D)		+= a3d.o
 obj-$(CONFIG_JOYSTICK_ADI)		+= adi.o
 obj-$(CONFIG_JOYSTICK_AMIGA)		+= amijoy.o
+obj-$(CONFIG_JOYSTICK_AS5011)		+= as5011.o
 obj-$(CONFIG_JOYSTICK_ANALOG)		+= analog.o
 obj-$(CONFIG_JOYSTICK_COBRA)		+= cobra.o
 obj-$(CONFIG_JOYSTICK_DB9)		+= db9.o
diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c
new file mode 100644
index 0000000..9f65357
--- /dev/null
+++ b/drivers/input/joystick/as5011.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2010 Fabien Marteau <fabien.marteau@armadeus.com>
+ *
+ * Sponsored by ARMadeus Systems
+ */
+
+/*
+ * Driver for Austria Microsystems joysticks AS5011
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ *
+ * TODO:
+ *	- Manage power mode
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <linux/as5011.h>
+#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick"
+
+MODULE_AUTHOR("Fabien Marteau <fabien.marteau@armadeus.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define SIZE_NAME 30
+#define SIZE_EVENT_PATH 40
+#define AS5011_MAX_AXIS	80
+#define AS5011_MIN_AXIS	(-80)
+#define AS5011_FUZZ	8
+#define AS5011_FLAT	40
+
+static int g_num = 1; /* device counter */
+
+static signed char as5011_i2c_read(struct i2c_client *client,
+			   uint8_t aRegAddr);
+static int as5011_i2c_write(struct i2c_client *client,
+			    uint8_t aRegAddr,
+			    uint8_t aValue);
+
+/*
+ * interrupts operations
+ */
+
+static irqreturn_t button_interrupt(int irq, void *dev_id)
+{
+	struct as5011_platform_data *plat_dat =
+		(struct as5011_platform_data *)dev_id;
+	int ret;
+
+	mutex_lock(&plat_dat->button_lock);
+	ret = gpio_get_value(plat_dat->button_gpio);
+	if (ret)
+		input_report_key(plat_dat->input_dev, BTN_JOYSTICK, 0);
+	else
+		input_report_key(plat_dat->input_dev, BTN_JOYSTICK, 1);
+	input_sync(plat_dat->input_dev);
+	mutex_unlock(&plat_dat->button_lock);
+
+	return IRQ_HANDLED;
+}
+
+static void as5011_update_axes(struct as5011_platform_data *plat_dat)
+{
+	signed char x, y;
+
+	x = as5011_i2c_read(plat_dat->i2c_client, AS5011_X_RES_INT);
+	y = as5011_i2c_read(plat_dat->i2c_client, AS5011_Y_RES_INT);
+	input_report_abs(plat_dat->input_dev, ABS_X, x);
+	input_report_abs(plat_dat->input_dev, ABS_Y, y);
+	input_sync(plat_dat->input_dev);
+}
+
+static irqreturn_t as5011_int_interrupt(int irq, void *dev_id)
+{
+	struct as5011_platform_data *plat_dat =
+		(struct as5011_platform_data *)dev_id;
+
+	mutex_lock(&plat_dat->update_lock);
+	as5011_update_axes(plat_dat);
+	mutex_unlock(&plat_dat->update_lock);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * I2C bus operation
+ */
+
+static int as5011_i2c_write(struct i2c_client *client,
+			    uint8_t aRegAddr,
+			    uint8_t aValue)
+{
+	int ret;
+	uint8_t data[2] = { aRegAddr, aValue };
+
+	struct i2c_msg msg = {	client->addr,
+				I2C_M_IGNORE_NAK,
+				2,
+				(uint8_t *)data };
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+
+	if (ret < 0)
+		return ret;
+	return 1;
+}
+
+static signed char as5011_i2c_read(struct i2c_client *client,
+			   uint8_t aRegAddr)
+{
+	int ret;
+	uint8_t data[2];
+	struct i2c_msg msg_set[2];
+	struct i2c_msg msg1 = {	client->addr,
+				I2C_M_REV_DIR_ADDR,
+				1,
+				(uint8_t *)data};
+	struct i2c_msg msg2 = {	client->addr,
+				I2C_M_RD|I2C_M_NOSTART,
+				1,
+				(uint8_t *)data};
+
+	data[0] = aRegAddr;
+	msg_set[0] = msg1;
+	msg_set[1] = msg2;
+	ret = i2c_transfer(client->adapter, msg_set, 2);
+	if (ret < 0)
+		return ret;
+
+	if (data[0] & 0x80)
+		return -1*(1+(~data[0]));
+	else
+		return data[0];
+}
+
+static int __devinit as5011_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct as5011_platform_data *plat_dat = client->dev.platform_data;
+	int retval = 0;
+
+	plat_dat->num = g_num;
+	g_num++;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_PROTOCOL_MANGLING)) {
+		dev_err(&client->dev,
+		"i2c bus does not support protocol mangling, as5011 can't work\n");
+		retval = -ENODEV;
+		goto err_out;
+	}
+	plat_dat->i2c_client = client;
+
+
+	plat_dat->input_dev = input_allocate_device();
+	if (plat_dat->input_dev == NULL) {
+		dev_err(&client->dev,
+		"not enough memory for input devices structure\n");
+		retval = -ENOMEM;
+		goto err_out;
+	}
+
+	plat_dat->input_dev->name = "Austria Microsystem as5011 joystick";
+	plat_dat->input_dev->uniq = "Austria0";
+	plat_dat->input_dev->id.bustype = BUS_I2C;
+	plat_dat->input_dev->phys = NULL;
+	plat_dat->input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	plat_dat->input_dev->keybit[BIT_WORD(BTN_JOYSTICK)] =
+					BIT_MASK(BTN_JOYSTICK);
+
+	input_set_abs_params(plat_dat->input_dev,
+			     ABS_X,
+			     AS5011_MIN_AXIS,
+			     AS5011_MAX_AXIS,
+			     AS5011_FUZZ,
+			     AS5011_FLAT);
+	input_set_abs_params(plat_dat->input_dev,
+			     ABS_Y,
+			     AS5011_MIN_AXIS,
+			     AS5011_MAX_AXIS,
+			     AS5011_FUZZ,
+			     AS5011_FLAT);
+
+	plat_dat->button_irq_name = kmalloc(SIZE_NAME, GFP_KERNEL);
+	if (plat_dat->button_irq_name == NULL) {
+		dev_err(&client->dev,
+		"not enough memory for input devices irq name\n");
+		retval = -ENOMEM;
+		goto err_input_free_device;
+	}
+
+	scnprintf(plat_dat->button_irq_name,
+		  SIZE_NAME,
+		  "as5011_%d_button",
+		  plat_dat->num);
+
+	retval = request_threaded_irq(plat_dat->button_irq,
+					NULL, button_interrupt,
+					0, plat_dat->button_irq_name,
+					(void *)plat_dat);
+	if (retval < 0) {
+		dev_err(&client->dev, "Can't allocate irq %d\n",
+			plat_dat->button_irq);
+		retval = -EBUSY;
+		goto err_free_button_irq_name;
+	}
+
+	if (plat_dat->init_gpio != NULL) {
+		retval = plat_dat->init_gpio();
+		if (retval < 0) {
+			dev_err(&client->dev, "Failed to init gpios\n");
+			goto err_free_irq_button_interrupt;
+		}
+	}
+
+	/* chip soft reset */
+	retval = as5011_i2c_write(plat_dat->i2c_client,
+				  AS5011_CTRL1,
+				  AS5011_CTRL1_SOFT_RST);
+	if (retval < 0) {
+		dev_err(&plat_dat->i2c_client->dev,
+			"Soft reset failed\n");
+		goto err_exit_gpio;
+	}
+
+	mdelay(10);
+
+	retval = as5011_i2c_write(plat_dat->i2c_client,
+				  AS5011_CTRL1,
+				  AS5011_CTRL1_LP_PULSED |
+				  AS5011_CTRL1_LP_ACTIVE |
+				  AS5011_CTRL1_INT_ACT_EN
+				  );
+	if (retval < 0) {
+		dev_err(&plat_dat->i2c_client->dev,
+			"Power config failed\n");
+		goto err_exit_gpio;
+	}
+
+	retval = as5011_i2c_write(plat_dat->i2c_client,
+				  AS5011_CTRL2,
+				  AS5011_CTRL2_INV_SPINNING);
+	if (retval < 0) {
+		dev_err(&plat_dat->i2c_client->dev,
+			"Can't invert spinning\n");
+		goto err_exit_gpio;
+	}
+
+	/* write threshold */
+	retval = as5011_i2c_write(plat_dat->i2c_client,
+				  AS5011_XP,
+				  plat_dat->Xp);
+	if (retval < 0) {
+		dev_err(&plat_dat->i2c_client->dev,
+			"Can't write threshold\n");
+		goto err_exit_gpio;
+	}
+
+	retval = as5011_i2c_write(plat_dat->i2c_client,
+				  AS5011_XN,
+				  plat_dat->Xn);
+	if (retval < 0) {
+		dev_err(&plat_dat->i2c_client->dev,
+			"Can't write threshold\n");
+		goto err_exit_gpio;
+	}
+
+	retval = as5011_i2c_write(plat_dat->i2c_client,
+				  AS5011_YP,
+				  plat_dat->Yp);
+	if (retval < 0) {
+		dev_err(&plat_dat->i2c_client->dev,
+			"Can't write threshold\n");
+		goto err_exit_gpio;
+	}
+
+	retval = as5011_i2c_write(plat_dat->i2c_client,
+				  AS5011_YN,
+				  plat_dat->Yn);
+	if (retval < 0) {
+		dev_err(&plat_dat->i2c_client->dev,
+			"Can't write threshold\n");
+		goto err_exit_gpio;
+	}
+
+	/* request irq */
+	plat_dat->int_irq_name = kmalloc(SIZE_NAME, GFP_KERNEL);
+	if (plat_dat->int_irq_name == NULL) {
+		dev_err(&client->dev,
+		"not enough memory for input devices int irq name\n");
+		retval = -ENOMEM;
+		goto err_exit_gpio;
+	}
+
+
+	/* to free irq gpio in chip*/
+	as5011_i2c_read(plat_dat->i2c_client, AS5011_X_RES_INT);
+
+	/* initialize mutex */
+	mutex_init(&plat_dat->update_lock);
+	mutex_init(&plat_dat->button_lock);
+
+	scnprintf(plat_dat->int_irq_name,
+		  SIZE_NAME,
+		  "as5011_%d_int",
+		  plat_dat->num);
+
+	if (request_threaded_irq(plat_dat->int_irq, NULL,
+				as5011_int_interrupt,
+				0, plat_dat->int_irq_name, (void *)plat_dat)) {
+		dev_err(&client->dev, "Can't allocate int irq %d\n",
+			plat_dat->int_irq);
+		retval = -EBUSY;
+		goto err_free_int_irq_name;
+	}
+
+	retval = input_register_device(plat_dat->input_dev);
+	if (retval) {
+		dev_err(&client->dev, "Failed to register device\n");
+		goto err_free_irq_int;
+	}
+
+	return 0;
+
+	/* Error management */
+err_free_irq_int:
+	free_irq(plat_dat->int_irq, as5011_int_interrupt);
+err_free_int_irq_name:
+	kfree(plat_dat->int_irq_name);
+err_exit_gpio:
+	if (plat_dat->exit_gpio != NULL)
+		plat_dat->exit_gpio();
+err_free_irq_button_interrupt:
+	free_irq(plat_dat->button_irq, button_interrupt);
+err_free_button_irq_name:
+	kfree(plat_dat->button_irq_name);
+err_input_free_device:
+	input_free_device(plat_dat->input_dev);
+err_out:
+	return retval;
+}
+static int as5011_remove(struct i2c_client *client)
+{
+	struct as5011_platform_data *plat_dat = client->dev.platform_data;
+
+	input_unregister_device(plat_dat->input_dev);
+	free_irq(plat_dat->button_irq, plat_dat);
+	free_irq(plat_dat->int_irq, plat_dat);
+	kfree(plat_dat->button_irq_name);
+	if (plat_dat->exit_gpio != NULL)
+		plat_dat->exit_gpio();
+
+	return 0;
+}
+static const struct i2c_device_id as5011_id[] = {
+	{ "as5011", 0 },
+	{ }
+};
+
+static struct i2c_driver as5011_driver = {
+	.driver = {
+		.name = "as5011",
+	},
+	.probe		= as5011_probe,
+	.remove		= __devexit_p(as5011_remove),
+	.id_table	= as5011_id,
+};
+
+/*
+ * Module initialization
+ */
+
+static int __init as5011_init(void)
+{
+	return i2c_add_driver(&as5011_driver);
+}
+
+static void __exit as5011_exit(void)
+{
+	i2c_del_driver(&as5011_driver);
+}
+
+module_init(as5011_init);
+module_exit(as5011_exit);
diff --git a/include/linux/as5011.h b/include/linux/as5011.h
new file mode 100644
index 0000000..c224752
--- /dev/null
+++ b/include/linux/as5011.h
@@ -0,0 +1,76 @@
+#ifndef _AS5011_H
+#define _AS5011_H
+
+/*
+ * Copyright (c) 2010 Fabien Marteau
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/mutex.h>
+
+#define AS5011_MAX_NAME_LENGTH	64
+#define AS5011_MAX_CNAME_LENGTH	16
+#define AS5011_MAX_PHYS_LENGTH	64
+#define AS5011_MAX_LENGTH	64
+
+/* registers */
+#define AS5011_CTRL1		0x76
+#define AS5011_CTRL2		0x75
+#define AS5011_XP		0x43
+#define AS5011_XN		0x44
+#define AS5011_YP		0x53
+#define AS5011_YN		0x54
+#define AS5011_X_REG		0x41
+#define AS5011_Y_REG		0x42
+#define AS5011_X_RES_INT	0x51
+#define AS5011_Y_RES_INT	0x52
+
+/* CTRL1 bits */
+#define AS5011_CTRL1_LP_PULSED		0x80
+#define AS5011_CTRL1_LP_ACTIVE		0x40
+#define AS5011_CTRL1_LP_CONTINUE	0x20
+#define AS5011_CTRL1_INT_WUP_EN		0x10
+#define AS5011_CTRL1_INT_ACT_EN		0x08
+#define AS5011_CTRL1_EXT_CLK_EN		0x04
+#define AS5011_CTRL1_SOFT_RST		0x02
+#define AS5011_CTRL1_DATA_VALID		0x01
+
+/* CTRL2 bits */
+#define AS5011_CTRL2_EXT_SAMPLE_EN	0x08
+#define AS5011_CTRL2_RC_BIAS_ON		0x04
+#define AS5011_CTRL2_INV_SPINNING	0x02
+
+
+struct as5011_platform_data {
+	/* public */
+	int button_gpio;
+	int button_irq;
+	int int_gpio;
+	int int_irq;
+	char Xp, Xn; /* threshold for x axis */
+	char Yp, Yn; /* threshold for y axis */
+
+	int (*init_gpio)(void); /* init interrupts gpios */
+	void (*exit_gpio)(void);/* exit gpios */
+
+	/* private */
+	int num;
+	struct input_dev *input_dev;
+	struct i2c_client *i2c_client;
+	unsigned char *button_irq_name;
+	unsigned char *int_irq_name;
+	char name[AS5011_MAX_NAME_LENGTH];
+	char cname[AS5011_MAX_CNAME_LENGTH];
+	char phys[AS5011_MAX_PHYS_LENGTH];
+	unsigned char data[AS5011_MAX_LENGTH];
+	char workqueue_name[AS5011_MAX_NAME_LENGTH];
+	struct workqueue_struct *workqueue;
+	struct work_struct update_axes_work;
+	struct mutex update_lock;
+	struct mutex button_lock;
+};
+
+#endif /* _AS5011_H */
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] input: joystick:   Adding Austria Microsystem AS5011 joystick driver (second version)
  2010-12-30 10:37 [PATCH] input: joystick: Adding Austria Microsystem AS5011 joystick driver (second version) Fabien Marteau
@ 2010-12-31 10:14 ` Trilok Soni
  2010-12-31 11:38   ` Fabien Marteau
  2011-01-02  7:58 ` Dmitry Torokhov
  1 sibling, 1 reply; 5+ messages in thread
From: Trilok Soni @ 2010-12-31 10:14 UTC (permalink / raw)
  To: fabien.marteau; +Cc: Dmitry Torokhov, Scott Moreau, linux-input, linux-kernel

Hi Fabien,

On 12/30/2010 4:07 PM, Fabien Marteau wrote:
> diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c
> new file mode 100644
> index 0000000..9f65357
> --- /dev/null
> +++ b/drivers/input/joystick/as5011.c
> @@ -0,0 +1,417 @@
> +/*
> + * Copyright (c) 2010 Fabien Marteau <fabien.marteau@armadeus.com>
> + *
> + * Sponsored by ARMadeus Systems
> + */

Please move this along with GPL text down below.

> +
> +/*
> + * Driver for Austria Microsystems joysticks AS5011
> + */
> +

This too.

> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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
> + *
> + * TODO:
> + *	- Manage power mode
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/string.h>
> +#include <linux/list.h>
> +#include <linux/sysfs.h>
> +#include <linux/ctype.h>
> +#include <linux/hwmon-sysfs.h>
> +
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/mutex.h>
> +
> +#include <linux/input.h>
> +#include <linux/gpio.h>
> +#include <linux/delay.h>
> +
> +#include <linux/as5011.h>
> +#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick"
> +
> +MODULE_AUTHOR("Fabien Marteau <fabien.marteau@armadeus.com>");
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE("GPL");
> +
> +#define SIZE_NAME 30
> +#define SIZE_EVENT_PATH 40
> +#define AS5011_MAX_AXIS	80
> +#define AS5011_MIN_AXIS	(-80)
> +#define AS5011_FUZZ	8
> +#define AS5011_FLAT	40
> +
> +static int g_num = 1; /* device counter */
> +
> +static signed char as5011_i2c_read(struct i2c_client *client,
> +			   uint8_t aRegAddr);
> +static int as5011_i2c_write(struct i2c_client *client,
> +			    uint8_t aRegAddr,
> +			    uint8_t aValue);
> +

Please re-arrange these functions in such a way that you don't need
these forward declarations.

> +/*
> + * interrupts operations
> + */

No need to mention :)

> +
> +static irqreturn_t button_interrupt(int irq, void *dev_id)
> +{
> +	struct as5011_platform_data *plat_dat =
> +		(struct as5011_platform_data *)dev_id;

casting from void * not required.

> +	int ret;
> +
> +	mutex_lock(&plat_dat->button_lock);
> +	ret = gpio_get_value(plat_dat->button_gpio);
> +	if (ret)
> +		input_report_key(plat_dat->input_dev, BTN_JOYSTICK, 0);
> +	else
> +		input_report_key(plat_dat->input_dev, BTN_JOYSTICK, 1);
> +	input_sync(plat_dat->input_dev);
> +	mutex_unlock(&plat_dat->button_lock);

Do you need this lock? Please explain.

> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void as5011_update_axes(struct as5011_platform_data *plat_dat)
> +{
> +	signed char x, y;
> +
> +	x = as5011_i2c_read(plat_dat->i2c_client, AS5011_X_RES_INT);
> +	y = as5011_i2c_read(plat_dat->i2c_client, AS5011_Y_RES_INT);
> +	input_report_abs(plat_dat->input_dev, ABS_X, x);
> +	input_report_abs(plat_dat->input_dev, ABS_Y, y);
> +	input_sync(plat_dat->input_dev);
> +}
> +
> +static irqreturn_t as5011_int_interrupt(int irq, void *dev_id)
> +{
> +	struct as5011_platform_data *plat_dat =
> +		(struct as5011_platform_data *)dev_id;

As mentioned above no casting required.

> +
> +	mutex_lock(&plat_dat->update_lock);
> +	as5011_update_axes(plat_dat);
> +	mutex_unlock(&plat_dat->update_lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * I2C bus operation
> + */
> +

No need to metnion.

> +static int as5011_i2c_write(struct i2c_client *client,
> +			    uint8_t aRegAddr,
> +			    uint8_t aValue)
> +{
> +	int ret;
> +	uint8_t data[2] = { aRegAddr, aValue };

No CamelCases please. Please run scripts/checkpatch.pl on your patch before submission.

> +
> +	struct i2c_msg msg = {	client->addr,
> +				I2C_M_IGNORE_NAK,
> +				2,
> +				(uint8_t *)data };
> +
> +	ret = i2c_transfer(client->adapter, &msg, 1);
> +
> +	if (ret < 0)
> +		return ret;
> +	return 1;
> +}
> +
> +static signed char as5011_i2c_read(struct i2c_client *client,
> +			   uint8_t aRegAddr)
> +{
> +	int ret;
> +	uint8_t data[2];
> +	struct i2c_msg msg_set[2];
> +	struct i2c_msg msg1 = {	client->addr,
> +				I2C_M_REV_DIR_ADDR,
> +				1,
> +				(uint8_t *)data};
> +	struct i2c_msg msg2 = {	client->addr,
> +				I2C_M_RD|I2C_M_NOSTART,
> +				1,
> +				(uint8_t *)data};
> +
> +	data[0] = aRegAddr;
> +	msg_set[0] = msg1;
> +	msg_set[1] = msg2;
> +	ret = i2c_transfer(client->adapter, msg_set, 2);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (data[0] & 0x80)
> +		return -1*(1+(~data[0]));
> +	else
> +		return data[0];
> +}
> +
> +static int __devinit as5011_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct as5011_platform_data *plat_dat = client->dev.platform_data;
> +	int retval = 0;
> +
> +	plat_dat->num = g_num;
> +	g_num++;

What is g_num++ and why it has to be global?

> +
> +	if (!i2c_check_functionality(client->adapter,
> +				     I2C_FUNC_PROTOCOL_MANGLING)) {

Please explain why MANGLING required.

> +		dev_err(&client->dev,
> +		"i2c bus does not support protocol mangling, as5011 can't work\n");
> +		retval = -ENODEV;
> +		goto err_out;
> +	}
> +	plat_dat->i2c_client = client;
> +
> +
> +	plat_dat->input_dev = input_allocate_device();
> +	if (plat_dat->input_dev == NULL) {
> +		dev_err(&client->dev,
> +		"not enough memory for input devices structure\n");
> +		retval = -ENOMEM;
> +		goto err_out;
> +	}
> +
> +	plat_dat->input_dev->name = "Austria Microsystem as5011 joystick";
> +	plat_dat->input_dev->uniq = "Austria0";
> +	plat_dat->input_dev->id.bustype = BUS_I2C;
> +	plat_dat->input_dev->phys = NULL;
> +	plat_dat->input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +	plat_dat->input_dev->keybit[BIT_WORD(BTN_JOYSTICK)] =
> +					BIT_MASK(BTN_JOYSTICK);
> +
> +	input_set_abs_params(plat_dat->input_dev,
> +			     ABS_X,
> +			     AS5011_MIN_AXIS,
> +			     AS5011_MAX_AXIS,
> +			     AS5011_FUZZ,
> +			     AS5011_FLAT);
> +	input_set_abs_params(plat_dat->input_dev,
> +			     ABS_Y,
> +			     AS5011_MIN_AXIS,
> +			     AS5011_MAX_AXIS,
> +			     AS5011_FUZZ,
> +			     AS5011_FLAT);
> +
> +	plat_dat->button_irq_name = kmalloc(SIZE_NAME, GFP_KERNEL);
> +	if (plat_dat->button_irq_name == NULL) {
> +		dev_err(&client->dev,
> +		"not enough memory for input devices irq name\n");
> +		retval = -ENOMEM;
> +		goto err_input_free_device;
> +	}
> +
> +	scnprintf(plat_dat->button_irq_name,
> +		  SIZE_NAME,
> +		  "as5011_%d_button",
> +		  plat_dat->num);
> +
> +	retval = request_threaded_irq(plat_dat->button_irq,
> +					NULL, button_interrupt,
> +					0, plat_dat->button_irq_name,
> +					(void *)plat_dat);

casting not required for last param.

> +	if (retval < 0) {
> +		dev_err(&client->dev, "Can't allocate irq %d\n",
> +			plat_dat->button_irq);
> +		retval = -EBUSY;
> +		goto err_free_button_irq_name;
> +	}
> +
> +	if (plat_dat->init_gpio != NULL) {
> +		retval = plat_dat->init_gpio();
> +		if (retval < 0) {
> +			dev_err(&client->dev, "Failed to init gpios\n");
> +			goto err_free_irq_button_interrupt;
> +		}
> +	}
> +
> +	/* chip soft reset */
> +	retval = as5011_i2c_write(plat_dat->i2c_client,
> +				  AS5011_CTRL1,
> +				  AS5011_CTRL1_SOFT_RST);
> +	if (retval < 0) {
> +		dev_err(&plat_dat->i2c_client->dev,
> +			"Soft reset failed\n");
> +		goto err_exit_gpio;
> +	}
> +
> +	mdelay(10);
> +
> +	retval = as5011_i2c_write(plat_dat->i2c_client,
> +				  AS5011_CTRL1,
> +				  AS5011_CTRL1_LP_PULSED |
> +				  AS5011_CTRL1_LP_ACTIVE |
> +				  AS5011_CTRL1_INT_ACT_EN
> +				  );
> +	if (retval < 0) {
> +		dev_err(&plat_dat->i2c_client->dev,
> +			"Power config failed\n");
> +		goto err_exit_gpio;
> +	}
> +
> +	retval = as5011_i2c_write(plat_dat->i2c_client,
> +				  AS5011_CTRL2,
> +				  AS5011_CTRL2_INV_SPINNING);
> +	if (retval < 0) {
> +		dev_err(&plat_dat->i2c_client->dev,
> +			"Can't invert spinning\n");
> +		goto err_exit_gpio;
> +	}
> +
> +	/* write threshold */
> +	retval = as5011_i2c_write(plat_dat->i2c_client,
> +				  AS5011_XP,
> +				  plat_dat->Xp);
> +	if (retval < 0) {
> +		dev_err(&plat_dat->i2c_client->dev,
> +			"Can't write threshold\n");
> +		goto err_exit_gpio;
> +	}
> +
> +	retval = as5011_i2c_write(plat_dat->i2c_client,
> +				  AS5011_XN,
> +				  plat_dat->Xn);
> +	if (retval < 0) {
> +		dev_err(&plat_dat->i2c_client->dev,
> +			"Can't write threshold\n");
> +		goto err_exit_gpio;
> +	}
> +
> +	retval = as5011_i2c_write(plat_dat->i2c_client,
> +				  AS5011_YP,
> +				  plat_dat->Yp);
> +	if (retval < 0) {
> +		dev_err(&plat_dat->i2c_client->dev,
> +			"Can't write threshold\n");
> +		goto err_exit_gpio;
> +	}
> +
> +	retval = as5011_i2c_write(plat_dat->i2c_client,
> +				  AS5011_YN,
> +				  plat_dat->Yn);
> +	if (retval < 0) {
> +		dev_err(&plat_dat->i2c_client->dev,
> +			"Can't write threshold\n");
> +		goto err_exit_gpio;
> +	}
> +
> +	/* request irq */
> +	plat_dat->int_irq_name = kmalloc(SIZE_NAME, GFP_KERNEL);

So much for name? why not just put it like "as5011_joystick" in the call itself.

> +	if (plat_dat->int_irq_name == NULL) {
> +		dev_err(&client->dev,
> +		"not enough memory for input devices int irq name\n");
> +		retval = -ENOMEM;
> +		goto err_exit_gpio;
> +	}
> +
> +
> +	/* to free irq gpio in chip*/
> +	as5011_i2c_read(plat_dat->i2c_client, AS5011_X_RES_INT);
> +
> +	/* initialize mutex */
> +	mutex_init(&plat_dat->update_lock);
> +	mutex_init(&plat_dat->button_lock);
> +
> +	scnprintf(plat_dat->int_irq_name,
> +		  SIZE_NAME,
> +		  "as5011_%d_int",
> +		  plat_dat->num);
> +
> +	if (request_threaded_irq(plat_dat->int_irq, NULL,
> +				as5011_int_interrupt,
> +				0, plat_dat->int_irq_name, (void *)plat_dat)) {
> +		dev_err(&client->dev, "Can't allocate int irq %d\n",
> +			plat_dat->int_irq);
> +		retval = -EBUSY;
> +		goto err_free_int_irq_name;
> +	}
> +
> +	retval = input_register_device(plat_dat->input_dev);
> +	if (retval) {
> +		dev_err(&client->dev, "Failed to register device\n");
> +		goto err_free_irq_int;
> +	}
> +
> +	return 0;
> +
> +	/* Error management */
> +err_free_irq_int:
> +	free_irq(plat_dat->int_irq, as5011_int_interrupt);
> +err_free_int_irq_name:
> +	kfree(plat_dat->int_irq_name);
> +err_exit_gpio:
> +	if (plat_dat->exit_gpio != NULL)
> +		plat_dat->exit_gpio();
> +err_free_irq_button_interrupt:
> +	free_irq(plat_dat->button_irq, button_interrupt);
> +err_free_button_irq_name:
> +	kfree(plat_dat->button_irq_name);
> +err_input_free_device:
> +	input_free_device(plat_dat->input_dev);
> +err_out:
> +	return retval;
> +}
> +static int as5011_remove(struct i2c_client *client)
> +{
> +	struct as5011_platform_data *plat_dat = client->dev.platform_data;
> +
> +	input_unregister_device(plat_dat->input_dev);
> +	free_irq(plat_dat->button_irq, plat_dat);
> +	free_irq(plat_dat->int_irq, plat_dat);
> +	kfree(plat_dat->button_irq_name);
> +	if (plat_dat->exit_gpio != NULL)
> +		plat_dat->exit_gpio();
> +
> +	return 0;
> +}
> +static const struct i2c_device_id as5011_id[] = {
> +	{ "as5011", 0 },
> +	{ }
> +};

MODULE_DEVICE_ALIAS
> +
> +static struct i2c_driver as5011_driver = {
> +	.driver = {
> +		.name = "as5011",
> +	},
> +	.probe		= as5011_probe,
> +	.remove		= __devexit_p(as5011_remove),
> +	.id_table	= as5011_id,
> +};
> +
> +/*
> + * Module initialization
> + */

no need 

> +
> +static int __init as5011_init(void)
> +{
> +	return i2c_add_driver(&as5011_driver);
> +}
> +
> +static void __exit as5011_exit(void)
> +{
> +	i2c_del_driver(&as5011_driver);
> +}
> +
> +module_init(as5011_init);
> +module_exit(as5011_exit);
> diff --git a/include/linux/as5011.h b/include/linux/as5011.h
> new file mode 100644
> index 0000000..c224752
> --- /dev/null
> +++ b/include/linux/as5011.h
> @@ -0,0 +1,76 @@
> +#ifndef _AS5011_H
> +#define _AS5011_H
> +
> +/*
> + * Copyright (c) 2010 Fabien Marteau
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/mutex.h>
> +
> +#define AS5011_MAX_NAME_LENGTH	64
> +#define AS5011_MAX_CNAME_LENGTH	16
> +#define AS5011_MAX_PHYS_LENGTH	64
> +#define AS5011_MAX_LENGTH	64
> +
> +/* registers */
> +#define AS5011_CTRL1		0x76
> +#define AS5011_CTRL2		0x75
> +#define AS5011_XP		0x43
> +#define AS5011_XN		0x44
> +#define AS5011_YP		0x53
> +#define AS5011_YN		0x54
> +#define AS5011_X_REG		0x41
> +#define AS5011_Y_REG		0x42
> +#define AS5011_X_RES_INT	0x51
> +#define AS5011_Y_RES_INT	0x52
> +
> +/* CTRL1 bits */
> +#define AS5011_CTRL1_LP_PULSED		0x80
> +#define AS5011_CTRL1_LP_ACTIVE		0x40
> +#define AS5011_CTRL1_LP_CONTINUE	0x20
> +#define AS5011_CTRL1_INT_WUP_EN		0x10
> +#define AS5011_CTRL1_INT_ACT_EN		0x08
> +#define AS5011_CTRL1_EXT_CLK_EN		0x04
> +#define AS5011_CTRL1_SOFT_RST		0x02
> +#define AS5011_CTRL1_DATA_VALID		0x01
> +
> +/* CTRL2 bits */
> +#define AS5011_CTRL2_EXT_SAMPLE_EN	0x08
> +#define AS5011_CTRL2_RC_BIAS_ON		0x04
> +#define AS5011_CTRL2_INV_SPINNING	0x02
> +

These should move to .c file.

> +
> +struct as5011_platform_data {
> +	/* public */
> +	int button_gpio;
> +	int button_irq;
> +	int int_gpio;
> +	int int_irq;
> +	char Xp, Xn; /* threshold for x axis */
> +	char Yp, Yn; /* threshold for y axis */

no CamelCases please

> +
> +	int (*init_gpio)(void); /* init interrupts gpios */
> +	void (*exit_gpio)(void);/* exit gpios */

You should better do gpio_request/free etc, in the driver itself,
and anyother thing can be left out in these hooks.

> +
> +	/* private */
> +	int num;
> +	struct input_dev *input_dev;
> +	struct i2c_client *i2c_client;
> +	unsigned char *button_irq_name;
> +	unsigned char *int_irq_name;

no need

> +	char name[AS5011_MAX_NAME_LENGTH];
> +	char cname[AS5011_MAX_CNAME_LENGTH];
> +	char phys[AS5011_MAX_PHYS_LENGTH];
> +	unsigned char data[AS5011_MAX_LENGTH];
> +	char workqueue_name[AS5011_MAX_NAME_LENGTH];

no need

> +	struct workqueue_struct *workqueue;
> +	struct work_struct update_axes_work;
> +	struct mutex update_lock;
> +	struct mutex button_lock;
> +};
> +
> +#endif /* _AS5011_H */

---Trilok Soni


-- 
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] input: joystick:   Adding Austria Microsystem AS5011 joystick driver (second version)
  2010-12-31 10:14 ` Trilok Soni
@ 2010-12-31 11:38   ` Fabien Marteau
  2011-01-02  7:41     ` Dmitry Torokhov
  0 siblings, 1 reply; 5+ messages in thread
From: Fabien Marteau @ 2010-12-31 11:38 UTC (permalink / raw)
  To: Trilok Soni; +Cc: Dmitry Torokhov, Scott Moreau, linux-input, linux-kernel

Hi Trilok,

> 
>> +	int ret;
>> +
>> +	mutex_lock(&plat_dat->button_lock);
>> +	ret = gpio_get_value(plat_dat->button_gpio);
>> +	if (ret)
>> +		input_report_key(plat_dat->input_dev, BTN_JOYSTICK, 0);
>> +	else
>> +		input_report_key(plat_dat->input_dev, BTN_JOYSTICK, 1);
>> +	input_sync(plat_dat->input_dev);
>> +	mutex_unlock(&plat_dat->button_lock);
> 
> Do you need this lock? Please explain.

If gpio_get_value() sleep, I need it to avoid race condition.

>> +static int as5011_i2c_write(struct i2c_client *client,
>> +			    uint8_t aRegAddr,
>> +			    uint8_t aValue)
>> +{
>> +	int ret;
>> +	uint8_t data[2] = { aRegAddr, aValue };
> 
> No CamelCases please. Please run scripts/checkpatch.pl on your patch before submission.

I had no warning for CamelCases name.

>> +static int __devinit as5011_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *id)
>> +{
>> +	struct as5011_platform_data *plat_dat = client->dev.platform_data;
>> +	int retval = 0;
>> +
>> +	plat_dat->num = g_num;
>> +	g_num++;
> 
> What is g_num++ and why it has to be global?

It's for interruptions name, if several as5011 joystick I set a number different for each joystick :

	scnprintf(plat_dat->button_irq_name,
		  SIZE_NAME,
		  "as5011_%d_button",
		  plat_dat->num);
> 
>> +
>> +	if (!i2c_check_functionality(client->adapter,
>> +				     I2C_FUNC_PROTOCOL_MANGLING)) {
> 
> Please explain why MANGLING required.

It's required for i2c register reading. To read register as5011 chip use this i2c frames :
	S Addr Rd [A] Register_addr [A] [Data] NA P
Wich is not conform to i2c protocol, i2c protocol require a repeated start to do that :
	S Addr Wr [A] Register_addr [A] S Addr Rd [Data] NA P

Then protocol mangling capability is required to avoid repeated start.


>> +	int (*init_gpio)(void); /* init interrupts gpios */
>> +	void (*exit_gpio)(void);/* exit gpios */
> 
> You should better do gpio_request/free etc, in the driver itself,
> and anyother thing can be left out in these hooks.

Yes, but on my platform, requesting gpio are specific. And I have to configure irq edge on initialization :
static int as5011_init_gpio(void)
{
	int ret;

	ret = mxc_gpio_setup_multiple_pins(as5011_pins0,
					   ARRAY_SIZE(as5011_pins0),
					   "AS5011_0");
	if (ret < 0) {
		goto as5011_gpio_setup_error;
	}
	set_irq_type(AS5011_INT_IRQ, IRQ_TYPE_EDGE_FALLING);
	set_irq_type(AS5011_BUTTON_IRQ, IRQ_TYPE_EDGE_BOTH);
	return ret;

	mxc_gpio_release_multiple_pins(as5011_pins0, ARRAY_SIZE(as5011_pins0));
as5011_gpio_setup_error:
	return ret;
}


> ---Trilok Soni
Thanks for the review.

--- Fabien Marteau

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] input: joystick:   Adding Austria Microsystem AS5011 joystick driver (second version)
  2010-12-31 11:38   ` Fabien Marteau
@ 2011-01-02  7:41     ` Dmitry Torokhov
  0 siblings, 0 replies; 5+ messages in thread
From: Dmitry Torokhov @ 2011-01-02  7:41 UTC (permalink / raw)
  To: Fabien Marteau; +Cc: Trilok Soni, Scott Moreau, linux-input, linux-kernel

Hi Fabien,

On Fri, Dec 31, 2010 at 12:38:07PM +0100, Fabien Marteau wrote:
> Hi Trilok,
> 
> > 
> >> +	int ret;
> >> +
> >> +	mutex_lock(&plat_dat->button_lock);
> >> +	ret = gpio_get_value(plat_dat->button_gpio);
> >> +	if (ret)
> >> +		input_report_key(plat_dat->input_dev, BTN_JOYSTICK, 0);
> >> +	else
> >> +		input_report_key(plat_dat->input_dev, BTN_JOYSTICK, 1);
> >> +	input_sync(plat_dat->input_dev);
> >> +	mutex_unlock(&plat_dat->button_lock);
> > 
> > Do you need this lock? Please explain.
> 
> If gpio_get_value() sleep, I need it to avoid race condition.

Race condition with what though?

> 
> >> +static int as5011_i2c_write(struct i2c_client *client,
> >> +			    uint8_t aRegAddr,
> >> +			    uint8_t aValue)
> >> +{
> >> +	int ret;
> >> +	uint8_t data[2] = { aRegAddr, aValue };
> > 
> > No CamelCases please. Please run scripts/checkpatch.pl on your patch before submission.
> 
> I had no warning for CamelCases name.

This goes wihtout saying ;)

> 
> >> +static int __devinit as5011_probe(struct i2c_client *client,
> >> +			const struct i2c_device_id *id)
> >> +{
> >> +	struct as5011_platform_data *plat_dat = client->dev.platform_data;
> >> +	int retval = 0;
> >> +
> >> +	plat_dat->num = g_num;
> >> +	g_num++;
> > 
> > What is g_num++ and why it has to be global?
> 
> It's for interruptions name, if several as5011 joystick I set a number different for each joystick :
> 
> 	scnprintf(plat_dat->button_irq_name,
> 		  SIZE_NAME,
> 		  "as5011_%d_button",
> 		  plat_dat->num);

Right, but who cares about having different names? Just mark all
interrupts as5011_button and as5011_joystick or even simply use "as5011"
for everything and call it a day.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] input: joystick:   Adding Austria Microsystem AS5011 joystick driver (second version)
  2010-12-30 10:37 [PATCH] input: joystick: Adding Austria Microsystem AS5011 joystick driver (second version) Fabien Marteau
  2010-12-31 10:14 ` Trilok Soni
@ 2011-01-02  7:58 ` Dmitry Torokhov
  1 sibling, 0 replies; 5+ messages in thread
From: Dmitry Torokhov @ 2011-01-02  7:58 UTC (permalink / raw)
  To: Fabien Marteau; +Cc: Scott Moreau, linux-input, linux-kernel

On Thu, Dec 30, 2010 at 11:37:37AM +0100, Fabien Marteau wrote:
> +
> +	plat_dat->input_dev->name = "Austria Microsystem as5011 joystick";
> +	plat_dat->input_dev->uniq = "Austria0";

This is improper value for 'uniq' which has to be globally unique
identifier (across systems). like a serial number.

> +	plat_dat->input_dev->id.bustype = BUS_I2C;
> +	plat_dat->input_dev->phys = NULL;

No need to set this to NULL.

> +	plat_dat->input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +	plat_dat->input_dev->keybit[BIT_WORD(BTN_JOYSTICK)] =
> +					BIT_MASK(BTN_JOYSTICK);

__set_bit() is shorter and generally safer (no chance of writing to
wrong longword).

-- 
Dmitry

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-01-02  8:46 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-30 10:37 [PATCH] input: joystick: Adding Austria Microsystem AS5011 joystick driver (second version) Fabien Marteau
2010-12-31 10:14 ` Trilok Soni
2010-12-31 11:38   ` Fabien Marteau
2011-01-02  7:41     ` Dmitry Torokhov
2011-01-02  7:58 ` 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).