Modified: trunk/drivers/input/joystick/ad7142.c (4130 => 4131)
--- trunk/drivers/input/joystick/ad7142.c 2008-01-22 04:08:50 UTC (rev 4130)
+++ trunk/drivers/input/joystick/ad7142.c 2008-01-22 04:11:06 UTC (rev 4131)
@@ -1,6 +1,5 @@
/*
* File: drivers/input/joystick/ad7142.c
- * Based on: drivers/input/joystick/amijoy.c
* Original Author: Aubrey Li
* Maintained by: Bryan Wu <[EMAIL PROTECTED]>
*
@@ -38,25 +37,16 @@
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/delay.h>
-#include <linux/kthread.h>
#include <linux/uaccess.h>
#include <linux/irq.h>
+#include <linux/workqueue.h>
#include <asm/blackfin.h>
MODULE_AUTHOR("Aubrey Li, Bryan Wu <[EMAIL PROTECTED]>");
-MODULE_DESCRIPTION("Driver for AD7142 joysticks");
+MODULE_DESCRIPTION("Driver for AD7142 Joysticks");
MODULE_LICENSE("GPL");
-/*
- * Feeding the output queue to the device is handled by way of a
- * workqueue.
- */
-static struct task_struct *ad7142_task;
-static DECLARE_WAIT_QUEUE_HEAD(ad7142_wait);
-
-static struct input_dev *ad7142_dev;
-
#define AD7142_DRV_NAME "ad7142_js"
#define AD7142_I2C_ID 0xE622
#define AD7142_I2C_ADDR 0x2C
@@ -134,80 +124,54 @@
{0xFFFF, 0x3FFF, 0x0000, 0x0606, 0x01F4, 0x01F4, 0x0320, 0x0320},
};
-static struct i2c_driver ad7142_driver;
-static struct i2c_client *ad7142_client;
+struct ad7142_data {
+ struct input_dev *input_dev;
+ struct i2c_driver i2c_drv;
+ struct i2c_client client;
+
+ struct work_struct work;
+
+ unsigned short old_status_low;
+ unsigned short old_status_high;
+
+ int irq;
+ int is_open;
+};
+
static unsigned short ignore[] = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { AD7142_I2C_ADDR, I2C_CLIENT_END };
-static int intr_flag;
-
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr,
.probe = ignore,
.ignore = ignore,
};
-static irqreturn_t ad7142_interrupt(int irq, void *dummy)
-{
- disable_irq(CONFIG_BFIN_JOYSTICK_IRQ_PFX);
- intr_flag = 1;
- wake_up_interruptible(&ad7142_wait);
- return IRQ_HANDLED;
-}
+static int ad7142_attach(struct i2c_adapter *adap);
+static int ad7142_detach(struct i2c_client *client);
+static int ad7142_i2c_read(struct i2c_client *client, unsigned short offset,
+ unsigned short *data, unsigned int len);
-static int
-ad7142_probe(struct i2c_adapter *adap, int addr, int kind)
+static struct i2c_driver ad7142_driver = {
+ .driver = {
+ .name = AD7142_DRV_NAME,
+ },
+ .id = AD7142_I2C_ID,
+ .attach_adapter = ad7142_attach,
+ .detach_client = ad7142_detach,
+};
+
+static irqreturn_t ad7142_interrupt(int irq, void *_data)
{
- struct i2c_client *client;
- int rc;
+ struct ad7142_data *data = ""
- client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!client)
- return -ENOMEM;
- memset(client, 0, sizeof(struct i2c_client));
- strncpy(client->name, AD7142_DRV_NAME, I2C_NAME_SIZE);
- client->addr = addr;
- client->adapter = adap;
- client->driver = &ad7142_driver;
+ disable_irq(data->irq);
- rc = i2c_attach_client(client);
- if (rc) {
- printk(KERN_ERR "i2c_attach_client fail: %d\n", rc);
- goto fail_attach;
- }
+ if (data->is_open)
+ schedule_work(&data->work);
- /*
- * The ADV7142 has an autoincrement function,
- * use it if the adapter understands raw I2C
- */
- rc = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
- if (!rc) {
- printk(KERN_ERR
- "AD7142: i2c bus doesn't support raw I2C operation\n");
- rc = -EINVAL;
- goto fail_check;
- }
-
- rc = request_irq(CONFIG_BFIN_JOYSTICK_IRQ_PFX, ad7142_interrupt,
- IRQF_TRIGGER_LOW, "ad7142_joy", ad7142_interrupt);
- if (rc) {
- printk(KERN_ERR "AD7142: Can't allocate irq %d\n",
- CONFIG_BFIN_JOYSTICK_IRQ_PFX);
- rc = -EBUSY;
- goto fail_check;
- }
-
- ad7142_client = client;
- printk(KERN_INFO "%s_attach: at 0x%02x\n",
- client->name, client->addr << 1);
- return 0;
-
-fail_check:
- i2c_detach_client(client);
-fail_attach:
- kfree(client);
- return rc;
+ return IRQ_HANDLED;
}
static int ad7142_i2c_write(struct i2c_client *client, unsigned short offset,
@@ -247,7 +211,7 @@
int i;
u8 block_data[32];
- if (len < 1 && len > 16) {
+ if (len < 1 || len > 16) {
printk(KERN_ERR "AD7142: read data length error\n");
return ret;
}
@@ -278,208 +242,260 @@
return ret;
}
-static int
-ad7142_attach(struct i2c_adapter *adap)
+static void ad7142_work(struct work_struct *work)
{
- return i2c_probe(adap, &addr_data, &ad7142_probe);
-}
-
-static int
-ad7142_detach_client(struct i2c_client *client)
-{
- int rc;
-
- free_irq(CONFIG_BFIN_JOYSTICK_IRQ_PFX, ad7142_interrupt);
-
- rc = i2c_detach_client(client);
- if (!rc)
- kfree(i2c_get_clientdata(client));
- return rc;
-}
-
-static struct i2c_driver ad7142_driver = {
- .driver = {
- .name = AD7142_DRV_NAME,
- },
- .id = AD7142_I2C_ID,
- .attach_adapter = ad7142_attach,
- .detach_client = ad7142_detach_client,
-};
-
-unsigned short old_status_low, old_status_high;
-
-static void ad7142_decode(void)
-{
+ struct ad7142_data *data = "" struct ad7142_data, work);
+ struct i2c_client *client = &data->client;
+ struct input_dev *input_dev = data->input_dev;
unsigned short irqno_low, irqno_high;
unsigned short temp;
- ad7142_i2c_read(ad7142_client, INTSTAT_REG0, &irqno_low, 1);
- temp = irqno_low ^ old_status_low;
+ ad7142_i2c_read(client, INTSTAT_REG0, &irqno_low, 1);
+ temp = irqno_low ^ data->old_status_low;
switch (temp) {
case 0x0001:
- input_report_key(ad7142_dev, BTN_BASE, (irqno_low & 0x0001));
+ input_report_key(input_dev, BTN_BASE, (irqno_low & 0x0001));
break;
case 0x0002:
- input_report_key(ad7142_dev, BTN_BASE4,
+ input_report_key(input_dev, BTN_BASE4,
((irqno_low & 0x0002) >> 1));
break;
case 0x0004:
- input_report_key(ad7142_dev, KEY_UP,
+ input_report_key(input_dev, KEY_UP,
((irqno_low & 0x0004) >> 2));
break;
case 0x0008:
- input_report_key(ad7142_dev, KEY_RIGHT,
+ input_report_key(input_dev, KEY_RIGHT,
((irqno_low & 0x0008) >> 3));
break;
}
- old_status_low = irqno_low;
+ data->old_status_low = irqno_low;
- ad7142_i2c_read(ad7142_client, INTSTAT_REG1, &irqno_high, 1);
- temp = irqno_high ^ old_status_high;
+ ad7142_i2c_read(client, INTSTAT_REG1, &irqno_high, 1);
+ temp = irqno_high ^ data->old_status_high;
switch (temp) {
case 0x0001:
- input_report_key(ad7142_dev, BTN_BASE2, irqno_high & 0x0001);
+ input_report_key(input_dev, BTN_BASE2, irqno_high & 0x0001);
break;
case 0x0002:
- input_report_key(ad7142_dev, BTN_BASE3,
+ input_report_key(input_dev, BTN_BASE3,
((irqno_high & 0x0002) >> 1));
break;
case 0x0004:
- input_report_key(ad7142_dev, KEY_DOWN,
+ input_report_key(input_dev, KEY_DOWN,
((irqno_high & 0x0004) >> 2));
break;
case 0x0008:
- input_report_key(ad7142_dev, KEY_LEFT,
+ input_report_key(input_dev, KEY_LEFT,
((irqno_high & 0x0008) >> 3));
break;
}
- old_status_high = irqno_high;
+ data->old_status_high = irqno_high;
- input_sync(ad7142_dev);
-}
+ input_sync(input_dev);
-static int ad7142_thread(void *nothing)
-{
- do {
- wait_event_interruptible(ad7142_wait,
- kthread_should_stop() || (intr_flag != 0));
- ad7142_decode();
- intr_flag = 0;
- enable_irq(CONFIG_BFIN_JOYSTICK_IRQ_PFX);
- } while (!kthread_should_stop());
-
- pr_debug("ad7142: kthread exiting\n");
-
- return 0;
+ enable_irq(data->irq);
}
static int ad7142_open(struct input_dev *dev)
{
+ struct ad7142_data *data = ""
+ struct i2c_client *client = &data->client;
unsigned short id, value;
- ad7142_i2c_read(ad7142_client, DEVID, &id, 1);
+ ad7142_i2c_read(client, DEVID, &id, 1);
if (id != AD7142_I2C_ID) {
printk(KERN_ERR "Open AD7142 error\n");
return -ENODEV;
}
- ad7142_i2c_write(ad7142_client, STAGE0_CONNECTION, stage[0], 8);
- ad7142_i2c_write(ad7142_client, STAGE1_CONNECTION, stage[1], 8);
- ad7142_i2c_write(ad7142_client, STAGE2_CONNECTION, stage[2], 8);
- ad7142_i2c_write(ad7142_client, STAGE3_CONNECTION, stage[3], 8);
- ad7142_i2c_write(ad7142_client, STAGE4_CONNECTION, stage[4], 8);
- ad7142_i2c_write(ad7142_client, STAGE5_CONNECTION, stage[4], 8);
- ad7142_i2c_write(ad7142_client, STAGE6_CONNECTION, stage[4], 8);
- ad7142_i2c_write(ad7142_client, STAGE7_CONNECTION, stage[4], 8);
- ad7142_i2c_write(ad7142_client, STAGE8_CONNECTION, stage[4], 8);
- ad7142_i2c_write(ad7142_client, STAGE9_CONNECTION, stage[4], 8);
- ad7142_i2c_write(ad7142_client, STAGE10_CONNECTION, stage[4], 8);
- ad7142_i2c_write(ad7142_client, STAGE11_CONNECTION, stage[4], 8);
+ ad7142_i2c_write(client, STAGE0_CONNECTION, stage[0], 8);
+ ad7142_i2c_write(client, STAGE1_CONNECTION, stage[1], 8);
+ ad7142_i2c_write(client, STAGE2_CONNECTION, stage[2], 8);
+ ad7142_i2c_write(client, STAGE3_CONNECTION, stage[3], 8);
+ ad7142_i2c_write(client, STAGE4_CONNECTION, stage[4], 8);
+ ad7142_i2c_write(client, STAGE5_CONNECTION, stage[4], 8);
+ ad7142_i2c_write(client, STAGE6_CONNECTION, stage[4], 8);
+ ad7142_i2c_write(client, STAGE7_CONNECTION, stage[4], 8);
+ ad7142_i2c_write(client, STAGE8_CONNECTION, stage[4], 8);
+ ad7142_i2c_write(client, STAGE9_CONNECTION, stage[4], 8);
+ ad7142_i2c_write(client, STAGE10_CONNECTION, stage[4], 8);
+ ad7142_i2c_write(client, STAGE11_CONNECTION, stage[4], 8);
+ /* In full power mode */
value = 0x00B0;
- ad7142_i2c_write(ad7142_client, PWRCONVCTL, &value, 1);
+ ad7142_i2c_write(client, PWRCONVCTL, &value, 1);
value = 0x0690;
- ad7142_i2c_write(ad7142_client, AMBCOMPCTL_REG1, &value, 1);
+ ad7142_i2c_write(client, AMBCOMPCTL_REG1, &value, 1);
value = 0x0664;
- ad7142_i2c_write(ad7142_client, AMBCOMPCTL_REG2, &value, 1);
+ ad7142_i2c_write(client, AMBCOMPCTL_REG2, &value, 1);
value = 0x290F;
- ad7142_i2c_write(ad7142_client, AMBCOMPCTL_REG3, &value, 1);
+ ad7142_i2c_write(client, AMBCOMPCTL_REG3, &value, 1);
value = 0x000F;
- ad7142_i2c_write(ad7142_client, INTEN_REG0, &value, 1);
- ad7142_i2c_write(ad7142_client, INTEN_REG1, &value, 1);
+ ad7142_i2c_write(client, INTEN_REG0, &value, 1);
+ ad7142_i2c_write(client, INTEN_REG1, &value, 1);
value = 0x0000;
- ad7142_i2c_write(ad7142_client, INTEN_REG2, &value, 1);
+ ad7142_i2c_write(client, INTEN_REG2, &value, 1);
- ad7142_i2c_read(ad7142_client, AMBCOMPCTL_REG1, &value, 1);
+ ad7142_i2c_read(client, AMBCOMPCTL_REG1, &value, 1);
value = 0x000F;
- ad7142_i2c_write(ad7142_client, AMBCOMPCTL_REG0, &value, 1);
+ ad7142_i2c_write(client, AMBCOMPCTL_REG0, &value, 1);
- ad7142_task = kthread_run(ad7142_thread, NULL, "ad7142_task");
- if (IS_ERR(ad7142_task)) {
- printk(KERN_ERR "serio: Failed to start kseriod\n");
- return PTR_ERR(ad7142_task);
- }
+ data->is_open = 1;
+ enable_irq(data->irq);
return 0;
}
static void ad7142_close(struct input_dev *dev)
{
- kthread_stop(ad7142_task);
+ struct ad7142_data *data = ""
+ struct i2c_client *client = &data->client;
+ unsigned short value;
+
+ disable_irq(data->irq);
+ data->is_open = 0;
+
+ flush_scheduled_work();
+
+ /*
+ * Turn AD7142 to full shutdown mode
+ * No CDC conversions
+ */
+ value = 0x0001;
+ ad7142_i2c_write(client, PWRCONVCTL, &value, 1);
}
-static int __init ad7142_init(void)
+static int ad7142_probe(struct i2c_adapter *adap, int addr, int kind)
{
- int ret;
+ struct ad7142_data *data;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ int rc;
- ad7142_dev = input_allocate_device();
- if (!ad7142_dev)
+ data = "" ad7142_data), GFP_KERNEL);
+ if (!data)
return -ENOMEM;
- ad7142_dev->open = ad7142_open;
- ad7142_dev->close = ad7142_close;
- ad7142_dev->evbit[0] = BIT(EV_KEY);
- ad7142_dev->keybit[LONG(BTN_BASE)] = BIT(BTN_BASE) | BIT(BTN_BASE2) |
+ client = &data->client;
+ i2c_set_clientdata(client, data);
+
+ strlcpy(client->name, AD7142_DRV_NAME, I2C_NAME_SIZE);
+ client->addr = addr;
+ client->adapter = adap;
+ client->driver = &ad7142_driver;
+
+ rc = i2c_attach_client(client);
+ if (rc) {
+ printk(KERN_ERR "i2c_attach_client fail: %d\n", rc);
+ goto fail_attach;
+ }
+
+ /*
+ * The ADV7142 has an autoincrement function,
+ * use it if the adapter understands raw I2C
+ */
+ rc = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+ if (!rc) {
+ printk(KERN_ERR
+ "AD7142: i2c bus doesn't support raw I2C operation\n");
+ rc = -ENOSYS;
+ goto fail_check;
+ }
+
+ /* Start workqueue for defer message transfer */
+ INIT_WORK(&data->work, ad7142_work);
+
+ data->irq = CONFIG_BFIN_JOYSTICK_IRQ_PFX;
+ rc = request_irq(data->irq, ad7142_interrupt,
+ IRQF_TRIGGER_LOW, "ad7142_joy", data);
+ if (rc) {
+ printk(KERN_ERR "AD7142: Can't allocate irq %d\n", data->irq);
+ goto fail_check;
+ }
+
+ printk(KERN_INFO "%s_attach: at 0x%02x\n",
+ client->name, client->addr << 1);
+
+ /* Allocate and register AD7142 input device */
+ data->input_dev = input_allocate_device();
+ if (!data->input_dev) {
+ printk(KERN_ERR "AD7142: Can't allocate input device\n");
+ rc = -ENOMEM;
+ goto fail_allocate;
+ }
+
+ input_dev = data->input_dev;
+ input_dev->open = ad7142_open;
+ input_dev->close = ad7142_close;
+ input_dev->evbit[0] = BIT(EV_KEY);
+ input_dev->keybit[LONG(BTN_BASE)] = BIT(BTN_BASE) | BIT(BTN_BASE2) |
BIT(BTN_BASE3) | BIT(BTN_BASE4);
- ad7142_dev->keybit[LONG(KEY_UP)] |= BIT(KEY_UP) | BIT(KEY_DOWN) |
+ input_dev->keybit[LONG(KEY_UP)] |= BIT(KEY_UP) | BIT(KEY_DOWN) |
BIT(KEY_LEFT) | BIT(KEY_RIGHT);
- ad7142_dev->name = "ad7142 joystick";
- ad7142_dev->phys = "ad7142/input0";
- ad7142_dev->id.bustype = BUS_I2C;
- ad7142_dev->id.vendor = 0x0001;
- ad7142_dev->id.product = 0x0001;
- ad7142_dev->id.version = 0x0100;
+ input_dev->name = "ad7142 joystick";
+ input_dev->phys = "ad7142/input0";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
- ret = input_register_device(ad7142_dev);
- if (ret) {
+ input_set_drvdata(input_dev, data);
+
+ rc = input_register_device(input_dev);
+ if (rc) {
printk(KERN_ERR "Failed to register AD7142 input device!\n");
goto fail_register;
}
- ret = i2c_add_driver(&ad7142_driver);
- if (ret) {
- printk(KERN_ERR "Failed to add AD7142 I2C driver!\n");
- goto fail_add;
- }
return 0;
-fail_add:
- input_unregister_device(ad7142_dev);
fail_register:
- input_free_device(ad7142_dev);
- return ret;
+ input_free_device(input_dev);
+fail_allocate:
+ free_irq(data->irq, ad7142_interrupt);
+fail_check:
+ i2c_detach_client(client);
+fail_attach:
+ kfree(client);
+ return rc;
}
+static int ad7142_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, &ad7142_probe);
+}
+
+static int ad7142_detach(struct i2c_client *client)
+{
+ struct ad7142_data *data = ""
+
+ free_irq(data->irq, ad7142_interrupt);
+
+ flush_scheduled_work();
+
+ input_unregister_device(data->input_dev);
+
+ kfree(data);
+
+ return i2c_detach_client(client);
+}
+
+
+static int __init ad7142_init(void)
+{
+ return i2c_add_driver(&ad7142_driver);
+}
+
static void __exit ad7142_exit(void)
{
i2c_del_driver(&ad7142_driver);
- input_unregister_device(ad7142_dev);
}
module_init(ad7142_init);