@@ -10,16 +10,17 @@
* published by the Free Software Foundation.
*/
+#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/tca6416_keypad.h>
-#include <linux/workqueue.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
#define TCA6416_INPUT 0
#define TCA6416_OUTPUT 1
@@ -43,79 +44,63 @@ struct tca6416_keypad_chip {
uint16_t reg_input;
struct i2c_client *client;
- struct tca6416_drv_data *drv_data;
+ struct input_dev *input;
struct delayed_work dwork;
- uint16_t pinmask;
+ u16 pinmask;
int irqnum;
- int use_polling;
+ bool use_polling;
+ struct tca6416_button buttons[0];
};
-static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg,
- uint16_t val)
+static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val)
{
- int ret;
-
- ret = i2c_smbus_write_word_data(chip->client, reg << 1, val);
-
- if (ret < 0) {
- dev_err(&chip->client->dev, "failed writing register\n");
- return ret;
+ int error;
+
+ error = i2c_smbus_write_word_data(chip->client, reg << 1, val);
+ if (error < 0) {
+ dev_err(&chip->client->dev,
+ "%s failed, reg: %d, val: %d, error: %d\n",
+ __func__, reg, val, error);
+ return error;
}
return 0;
}
-static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg,
- uint16_t *val)
+static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val)
{
- int ret;
-
- ret = i2c_smbus_read_word_data(chip->client, reg << 1);
+ int retval;
- if (ret < 0) {
- dev_err(&chip->client->dev, "failed reading register\n");
- return ret;
+ retval = i2c_smbus_read_word_data(chip->client, reg << 1);
+ if (retval < 0) {
+ dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n",
+ __func__, reg, retval);
+ return retval;
}
- *val = (uint16_t)ret;
+ *val = (u16)retval;
return 0;
}
-static irqreturn_t tca6416_keys_isr(int irq, void *dev_id)
-{
- struct tca6416_keypad_chip *chip =
- (struct tca6416_keypad_chip *) dev_id;
-
- disable_irq(irq);
- schedule_delayed_work(&chip->dwork, 0);
- return IRQ_HANDLED;
-
-}
-
-static void tca6416_keys_work_func(struct work_struct *workstruct)
+static void tca6416_keys_scan(struct tca6416_keypad_chip *chip)
{
- struct delayed_work *delay_work =
- container_of(workstruct, struct delayed_work, work);
- struct tca6416_keypad_chip *chip =
- container_of(delay_work, struct tca6416_keypad_chip, dwork);
- struct tca6416_drv_data *ddata = chip->drv_data;
- uint16_t reg_val, val;
- int ret, i, pin_index;
+ struct input_dev *input = chip->input;
+ u16 reg_val, val;
+ int error, i, pin_index;
- ret = tca6416_read_reg(chip, TCA6416_INPUT, ®_val);
- if (ret)
+ error = tca6416_read_reg(chip, TCA6416_INPUT, ®_val);
+ if (error)
return;
reg_val &= chip->pinmask;
/* Figure out which lines have changed */
- val = reg_val ^ (chip->reg_input);
+ val = reg_val ^ chip->reg_input;
chip->reg_input = reg_val;
for (i = 0, pin_index = 0; i < 16; i++) {
if (val & (1 << i)) {
- struct tca6416_button *button = &ddata->data[pin_index];
- struct input_dev *input = ddata->input;
+ struct tca6416_button *button = &chip->buttons[pin_index];
unsigned int type = button->type ?: EV_KEY;
int state = ((reg_val & (1 << i)) ? 1 : 0)
^ button->active_low;
@@ -127,97 +112,128 @@ static void tca6416_keys_work_func(struct work_struct *workstruct)
if (chip->pinmask & (1 << i))
pin_index++;
}
+}
+
+/*
+ * This is threaded IRQ handler and this can (and will) sleep.
+ */
+static irqreturn_t tca6416_keys_isr(int irq, void *dev_id)
+{
+ struct tca6416_keypad_chip *chip = dev_id;
+
+ tca6416_keys_scan(chip);
+
+ return IRQ_HANDLED;
+}
+
+static void tca6416_keys_work_func(struct work_struct *work)
+{
+ struct tca6416_keypad_chip *chip =
+ container_of(work, struct tca6416_keypad_chip, dwork.work);
+
+ tca6416_keys_scan(chip);
+ schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
+}
+
+static int tca6416_keys_open(struct input_dev *dev)
+{
+ struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
+
+ /* Get initial device state in case it has switches */
+ tca6416_keys_scan(chip);
if (chip->use_polling)
schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
else
enable_irq(chip->irqnum);
+ return 0;
+}
+
+static void tca6416_keys_close(struct input_dev *dev)
+{
+ struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
+
+ if (chip->use_polling)
+ cancel_delayed_work_sync(&chip->dwork);
+ else
+ free_irq(chip->irqnum, chip);
}
+static int __devinit tca6416_setup_registers(struct tca6416_keypad_chip *chip)
+{
+ int error;
+
+ error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output);
+ if (error)
+ return error;
+
+ error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
+ if (error)
+ return error;
+
+ /* ensure that keypad pins are set to input */
+ error = tca6416_write_reg(chip, TCA6416_DIRECTION,
+ chip->reg_direction | chip->pinmask);
+ if (error)
+ return error;
+
+ error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
+ if (error)
+ return error;
+
+ error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input);
+ if (error)
+ return error;
+
+ return 0;
+}
static int __devinit tca6416_keypad_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tca6416_keys_platform_data *pdata;
struct tca6416_keypad_chip *chip;
- struct tca6416_drv_data *ddata;
struct input_dev *input;
- int i, ret, pin_index, err;
- uint16_t reg_val;
+ int error;
+ int i;
/* Check functionality */
- err = i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE);
- if (!err) {
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
dev_err(&client->dev, "%s adapter not supported\n",
dev_driver_string(&client->adapter->dev));
return -ENODEV;
}
-
- chip = kzalloc(sizeof(struct tca6416_keypad_chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
-
pdata = client->dev.platform_data;
- if (pdata == NULL) {
+ if (!pdata) {
dev_dbg(&client->dev, "no platform data\n");
- ret = -EINVAL;
+ return -EINVAL;
+ }
+
+ chip = kzalloc(sizeof(struct tca6416_keypad_chip) +
+ pdata->nbuttons * sizeof(struct tca6416_button),
+ GFP_KERNEL);
+ input = input_allocate_device();
+ if (!chip || !input) {
+ error = -ENOMEM;
goto fail1;
}
chip->client = client;
+ chip->input = input;
chip->pinmask = pdata->pinmask;
+ chip->use_polling = pdata->use_polling;
- /* initialize cached registers from their original values.
- * we can't share this chip with another i2c master.
- */
- ret = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output);
- if (ret)
- goto fail1;
-
- ret = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
- if (ret)
- goto fail1;
-
- /* ensure that keypad pins are set to input */
- reg_val = chip->reg_direction | chip->pinmask;
- ret = tca6416_write_reg(chip, TCA6416_DIRECTION, reg_val);
- if (ret)
- goto fail1;
-
- ret = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
- if (ret)
- goto fail1;
-
- ret = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input);
- if (ret)
- goto fail1;
-
- i2c_set_clientdata(client, chip);
-
-
- ddata = kzalloc(sizeof(struct tca6416_drv_data) +
- pdata->nbuttons * sizeof(struct tca6416_button),
- GFP_KERNEL);
- if (!ddata) {
- ret = -ENOMEM;
- goto fail1;
- }
-
- input = input_allocate_device();
- if (!input) {
- dev_dbg(&client->dev, "failed to allocate state\n");
- ret = -ENOMEM;
- kfree(ddata);
- goto fail2;
- }
+ INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func);
input->phys = "tca6416-keys/input0";
input->name = client->name;
input->dev.parent = &client->dev;
+ input->open = tca6416_keys_open;
+ input->close = tca6416_keys_close;
+
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
@@ -227,20 +243,23 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client,
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
- ddata->input = input;
-
for (i = 0; i < pdata->nbuttons; i++) {
unsigned int type;
- ddata->data[i] = pdata->buttons[i];
+ chip->buttons[i] = pdata->buttons[i];
type = (pdata->buttons[i].type) ?: EV_KEY;
- input_set_capability(input, type, (pdata->buttons[i].code));
+ input_set_capability(input, type, pdata->buttons[i].code);
}
- chip->drv_data = ddata;
- chip->use_polling = pdata->use_polling;
+ input_set_drvdata(input, chip);
- INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func);
+ /*
+ * Initialize cached registers from their original values.
+ * we can't share this chip with another i2c master.
+ */
+ error = tca6416_setup_registers(chip);
+ if (error)
+ goto fail1;
if (!chip->use_polling) {
if (pdata->irq_is_gpio)
@@ -248,81 +267,55 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client,
else
chip->irqnum = client->irq;
- ret = request_irq(chip->irqnum, tca6416_keys_isr,
- IRQF_SHARED | IRQF_TRIGGER_FALLING ,
- "tca6416-keypad", chip);
- if (ret) {
+ error = request_threaded_irq(chip->irqnum, NULL,
+ tca6416_keys_isr,
+ IRQF_TRIGGER_FALLING,
+ "tca6416-keypad", chip);
+ if (error) {
dev_dbg(&client->dev,
"Unable to claim irq %d; error %d\n",
- chip->irqnum, ret);
- goto fail3;
+ chip->irqnum, error);
+ goto fail1;
}
disable_irq(chip->irqnum);
}
- ret = input_register_device(input);
- if (ret) {
- dev_dbg(&client->dev, "Unable to register input device, "
- "error: %d\n", ret);
- goto fail4;
- }
-
- /* get current state of buttons */
-
- ret = tca6416_read_reg(chip, TCA6416_INPUT, ®_val);
- if (ret)
- goto fail5;
-
- chip->reg_input = reg_val & chip->pinmask;
-
- for (i = 0, pin_index = 0; i < 16; i++) {
- if (chip->pinmask & (1 << i)) {
- struct tca6416_button *button = &ddata->data[pin_index];
- unsigned int type = button->type ?: EV_KEY;
- int state = ((reg_val & (1 << i)) ? 1 : 0)
- ^ button->active_low;
-
- input_event(input, type, button->code, !!state);
- input_sync(input);
- pin_index++;
- }
+ error = input_register_device(input);
+ if (error) {
+ dev_dbg(&client->dev,
+ "Unable to register input device, error: %d\n", error);
+ goto fail2;
}
- input_sync(input);
- if (chip->use_polling)
- schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
- else
- enable_irq(chip->irqnum);
+ i2c_set_clientdata(client, chip);
return 0;
-fail5:
- input_unregister_device(input);
-fail4:
- if (!chip->use_polling)
- free_irq(chip->irqnum, chip);
-fail3:
- input_free_device(input);
fail2:
- kfree(ddata);
+ if (!chip->use_polling) {
+ free_irq(chip->irqnum, chip);
+ enable_irq(chip->irqnum);
+ }
fail1:
+ input_free_device(input);
kfree(chip);
- return ret;
+ return error;
}
-static int tca6416_keypad_remove(struct i2c_client *client)
+static int __devexit tca6416_keypad_remove(struct i2c_client *client)
{
struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
- struct tca6416_drv_data *ddata = chip->drv_data;
- struct input_dev *input = ddata->input;
- if (!chip->use_polling)
+ if (!chip->use_polling) {
free_irq(chip->irqnum, chip);
- cancel_delayed_work_sync(&chip->dwork);
- input_unregister_device(input);
- input_free_device(input);
- kfree(ddata);
+ enable_irq(chip->irqnum);
+ }
+
+ input_unregister_device(chip->input);
kfree(chip);
+
+ i2c_set_clientdata(client, NULL);
+
return 0;
}
@@ -332,7 +325,7 @@ static struct i2c_driver tca6416_keypad_driver = {
.name = "tca6416-keypad",
},
.probe = tca6416_keypad_probe,
- .remove = tca6416_keypad_remove,
+ .remove = __devexit_p(tca6416_keypad_remove),
.id_table = tca6416_id,
};