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@xxxxxxxxxxxx> > + * > + * 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@xxxxxxxxxxxx>"); > +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. -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html