From patchwork Tue Jan 4 09:47:34 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Marteau X-Patchwork-Id: 450331 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p049l4XS015194 for ; Tue, 4 Jan 2011 09:48:00 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1750963Ab1ADJsA (ORCPT ); Tue, 4 Jan 2011 04:48:00 -0500 Received: from 27.mail-out.ovh.net ([91.121.30.210]:53817 "HELO 27.mail-out.ovh.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with SMTP id S1750897Ab1ADJr6 (ORCPT ); Tue, 4 Jan 2011 04:47:58 -0500 Received: (qmail 2535 invoked by uid 503); 4 Jan 2011 09:50:06 -0000 Received: from b9.ovh.net (HELO mail625.ha.ovh.net) (213.186.33.59) by 27.mail-out.ovh.net with SMTP; 4 Jan 2011 09:49:51 -0000 Received: from b0.ovh.net (HELO queueout) (213.186.33.50) by b0.ovh.net with SMTP; 4 Jan 2011 11:47:36 +0200 Received: from arc68-6-82-232-255-99.fbx.proxad.net (HELO ?192.168.0.214?) (82.232.255.99) by ns0.ovh.net with SMTP; 4 Jan 2011 11:47:35 +0200 Message-ID: <4D22ECB6.6090800@armadeus.com> Date: Tue, 04 Jan 2011 10:47:34 +0100 From: Fabien Marteau Reply-To: fabien.marteau@armadeus.com Organization: ARMadeus Systems User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.13) Gecko/20101208 Lightning/1.0b2 Thunderbird/3.1.7 MIME-Version: 1.0 To: Dmitry Torokhov , Scott Moreau CC: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] input: joystick: Adding Austria Microsystem AS5011 joystick driver (third version) X-Enigmail-Version: 1.1.2 X-Ovh-Tracer-Id: 17056820639600435611 X-Ovh-Remote: 82.232.255.99 (arc68-6-82-232-255-99.fbx.proxad.net) X-Ovh-Local: 213.186.33.20 (ns0.ovh.net) X-Spam-Check: DONE|U 0.5/N Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Tue, 04 Jan 2011 09:48:01 +0000 (UTC) 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..066a125 --- /dev/null +++ b/drivers/input/joystick/as5011.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2010, 2011 Fabien Marteau + * Sponsored by ARMadeus Systems + * + * 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 + * + * Driver for Austria Microsystems joysticks AS5011 + * + * TODO: + * - Manage power mode + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick" +#define MODULE_DEVICE_ALIAS "as5011" + +MODULE_AUTHOR("Fabien Marteau "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* 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 + +#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 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 irqreturn_t button_interrupt(int irq, void *dev_id) +{ + struct as5011_platform_data *plat_dat = dev_id; + int ret; + + 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); + + 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 = dev_id; + + mutex_lock(&plat_dat->update_lock); + as5011_update_axes(plat_dat); + mutex_unlock(&plat_dat->update_lock); + + return IRQ_HANDLED; +} + +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; + + 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->id.bustype = BUS_I2C; + __set_bit(EV_KEY, plat_dat->input_dev->evbit); + __set_bit(EV_ABS, plat_dat->input_dev->evbit); + __set_bit(BTN_JOYSTICK, plat_dat->input_dev->keybit); + + 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); + + retval = request_threaded_irq(plat_dat->button_irq, + NULL, button_interrupt, + 0, "as5011_button", + plat_dat); + if (retval < 0) { + dev_err(&client->dev, "Can't allocate irq %d\n", + plat_dat->button_irq); + retval = -EBUSY; + goto err_input_free_device; + } + + 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; + } + + /* 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); + + if (request_threaded_irq(plat_dat->int_irq, NULL, + as5011_int_interrupt, + 0, "as5011_joystick", plat_dat)) { + dev_err(&client->dev, "Can't allocate int irq %d\n", + plat_dat->int_irq); + retval = -EBUSY; + goto err_exit_gpio; + } + + 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_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_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); + if (plat_dat->exit_gpio != NULL) + plat_dat->exit_gpio(); + + return 0; +} +static const struct i2c_device_id as5011_id[] = { + { MODULE_DEVICE_ALIAS, 0 }, + { } +}; + +static struct i2c_driver as5011_driver = { + .driver = { + .name = "as5011", + }, + .probe = as5011_probe, + .remove = __devexit_p(as5011_remove), + .id_table = as5011_id, +}; + +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..48dfcb6 --- /dev/null +++ b/include/linux/as5011.h @@ -0,0 +1,35 @@ +#ifndef _AS5011_H +#define _AS5011_H + +/* + * Copyright (c) 2010, 2011 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 + +#define AS5011_MAX_NAME_LENGTH 64 + +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 */ + struct input_dev *input_dev; + struct i2c_client *i2c_client; + char name[AS5011_MAX_NAME_LENGTH]; + struct mutex update_lock; +}; + +#endif /* _AS5011_H */