diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-24 11:58:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-24 11:58:49 -0700 |
commit | 4637f40f200063973553ce3c4c1ac6c247e4535c (patch) | |
tree | ff317a0dfb67cae313a208d120edd5102730044d /drivers/input | |
parent | 5129df03d0c44b2d5a5f9d7d52f3b079706b9a8f (diff) | |
parent | b73077eb03f510a84b102fb97640e595a958403c (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (40 commits)
Input: ADP5589 - new driver for I2C Keypad Decoder and I/O Expander
Input: tsc2007 - add X, Y and Z fuzz factors to platform data
Input: tsc2007 - add poll_period parameter to platform data
Input: tsc2007 - add poll_delay parameter to platform data
Input: tsc2007 - add max_rt parameter to platform data
Input: tsc2007 - debounce pressure measurement
Input: ad714x - fix captouch wheel option algorithm
Input: ad714x - allow platform code to specify irqflags
Input: ad714x - fix threshold and completion interrupt masks
Input: ad714x - fix up input configuration
Input: elantech - remove support for proprietary X driver
Input: elantech - report multitouch with proper ABS_MT messages
Input: elantech - export pressure and width when supported
Input: elantech - describe further the protocol
Input: atmel_tsadcc - correct call to input_free_device
Input: add driver FSL MPR121 capacitive touch sensor
Input: remove useless synchronize_rcu() calls
Input: ads7846 - fix gpio_pendown configuration
Input: ads7846 - add possibility to use external vref on ads7846
Input: rotary-encoder - add support for half-period encoders
...
Diffstat (limited to 'drivers/input')
28 files changed, 1791 insertions, 328 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 88d8e4cb419..be0921ef6b5 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -41,6 +41,7 @@ struct evdev { struct evdev_client { unsigned int head; unsigned int tail; + unsigned int packet_head; /* [future] position of the first element of next packet */ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ struct fasync_struct *fasync; struct evdev *evdev; @@ -72,12 +73,16 @@ static void evdev_pass_event(struct evdev_client *client, client->buffer[client->tail].type = EV_SYN; client->buffer[client->tail].code = SYN_DROPPED; client->buffer[client->tail].value = 0; - } - spin_unlock(&client->buffer_lock); + client->packet_head = client->tail; + } - if (event->type == EV_SYN) + if (event->type == EV_SYN && event->code == SYN_REPORT) { + client->packet_head = client->head; kill_fasync(&client->fasync, SIGIO, POLL_IN); + } + + spin_unlock(&client->buffer_lock); } /* @@ -159,7 +164,6 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client) return error; rcu_assign_pointer(evdev->grab, client); - synchronize_rcu(); return 0; } @@ -182,7 +186,6 @@ static void evdev_attach_client(struct evdev *evdev, spin_lock(&evdev->client_lock); list_add_tail_rcu(&client->node, &evdev->client_list); spin_unlock(&evdev->client_lock); - synchronize_rcu(); } static void evdev_detach_client(struct evdev *evdev, @@ -387,12 +390,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, if (count < input_event_size()) return -EINVAL; - if (client->head == client->tail && evdev->exist && + if (client->packet_head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; retval = wait_event_interruptible(evdev->wait, - client->head != client->tail || !evdev->exist); + client->packet_head != client->tail || !evdev->exist); if (retval) return retval; @@ -421,7 +424,7 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait) poll_wait(file, &evdev->wait, wait); mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; - if (client->head != client->tail) + if (client->packet_head != client->tail) mask |= POLLIN | POLLRDNORM; return mask; diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 3037842a60d..b1aabde8752 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -13,6 +13,7 @@ #include <linux/jiffies.h> #include <linux/slab.h> #include <linux/mutex.h> +#include <linux/workqueue.h> #include <linux/input-polldev.h> MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); @@ -20,44 +21,6 @@ MODULE_DESCRIPTION("Generic implementation of a polled input device"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("0.1"); -static DEFINE_MUTEX(polldev_mutex); -static int polldev_users; -static struct workqueue_struct *polldev_wq; - -static int input_polldev_start_workqueue(void) -{ - int retval; - - retval = mutex_lock_interruptible(&polldev_mutex); - if (retval) - return retval; - - if (!polldev_users) { - polldev_wq = create_singlethread_workqueue("ipolldevd"); - if (!polldev_wq) { - pr_err("failed to create ipolldevd workqueue\n"); - retval = -ENOMEM; - goto out; - } - } - - polldev_users++; - - out: - mutex_unlock(&polldev_mutex); - return retval; -} - -static void input_polldev_stop_workqueue(void) -{ - mutex_lock(&polldev_mutex); - - if (!--polldev_users) - destroy_workqueue(polldev_wq); - - mutex_unlock(&polldev_mutex); -} - static void input_polldev_queue_work(struct input_polled_dev *dev) { unsigned long delay; @@ -66,7 +29,7 @@ static void input_polldev_queue_work(struct input_polled_dev *dev) if (delay >= HZ) delay = round_jiffies_relative(delay); - queue_delayed_work(polldev_wq, &dev->work, delay); + queue_delayed_work(system_freezable_wq, &dev->work, delay); } static void input_polled_device_work(struct work_struct *work) @@ -81,18 +44,13 @@ static void input_polled_device_work(struct work_struct *work) static int input_open_polled_device(struct input_dev *input) { struct input_polled_dev *dev = input_get_drvdata(input); - int error; - - error = input_polldev_start_workqueue(); - if (error) - return error; if (dev->open) dev->open(dev); /* Only start polling if polling is enabled */ if (dev->poll_interval > 0) - queue_delayed_work(polldev_wq, &dev->work, 0); + queue_delayed_work(system_freezable_wq, &dev->work, 0); return 0; } @@ -102,13 +60,6 @@ static void input_close_polled_device(struct input_dev *input) struct input_polled_dev *dev = input_get_drvdata(input); cancel_delayed_work_sync(&dev->work); - /* - * Clean up work struct to remove references to the workqueue. - * It may be destroyed by the next call. This causes problems - * at next device open-close in case of poll_interval == 0. - */ - INIT_DELAYED_WORK(&dev->work, dev->work.work.func); - input_polldev_stop_workqueue(); if (dev->close) dev->close(dev); @@ -295,4 +246,3 @@ void input_unregister_polled_device(struct input_polled_dev *dev) input_unregister_device(dev->input); } EXPORT_SYMBOL(input_unregister_polled_device); - diff --git a/drivers/input/input.c b/drivers/input/input.c index ebbceedc92f..75e11c7b70f 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -451,7 +451,6 @@ int input_grab_device(struct input_handle *handle) } rcu_assign_pointer(dev->grab, handle); - synchronize_rcu(); out: mutex_unlock(&dev->mutex); diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 5688b5c88f2..c24ec2d5f92 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -180,7 +180,6 @@ static void joydev_attach_client(struct joydev *joydev, spin_lock(&joydev->client_lock); list_add_tail_rcu(&client->node, &joydev->client_list); spin_unlock(&joydev->client_lock); - synchronize_rcu(); } static void joydev_detach_client(struct joydev *joydev, diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b16bed038f7..69badb4e06a 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -32,6 +32,16 @@ config KEYBOARD_ADP5588 To compile this driver as a module, choose M here: the module will be called adp5588-keys. +config KEYBOARD_ADP5589 + tristate "ADP5589 I2C QWERTY Keypad and IO Expander" + depends on I2C + help + Say Y here if you want to use a ADP5589 attached to your + system I2C bus. + + To compile this driver as a module, choose M here: the + module will be called adp5589-keys. + config KEYBOARD_AMIGA tristate "Amiga keyboard" depends on AMIGA @@ -325,6 +335,18 @@ config KEYBOARD_MCS To compile this driver as a module, choose M here: the module will be called mcs_touchkey. +config KEYBOARD_MPR121 + tristate "Freescale MPR121 Touchkey" + depends on I2C + help + Say Y here if you have Freescale MPR121 touchkey controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mpr121_touchkey. + config KEYBOARD_IMX tristate "IMX keypad support" depends on ARCH_MXC diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 878e6c20deb..c49cf8e04cd 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o +obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o @@ -27,6 +28,7 @@ obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o +obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c new file mode 100644 index 00000000000..631598663aa --- /dev/null +++ b/drivers/input/keyboard/adp5589-keys.c @@ -0,0 +1,771 @@ +/* + * Description: keypad driver for ADP5589 + * I2C QWERTY Keypad and IO Expander + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2010-2011 Analog Devices Inc. + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/workqueue.h> +#include <linux/errno.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/slab.h> + +#include <linux/input/adp5589.h> + +/* GENERAL_CFG Register */ +#define OSC_EN (1 << 7) +#define CORE_CLK(x) (((x) & 0x3) << 5) +#define LCK_TRK_LOGIC (1 << 4) +#define LCK_TRK_GPI (1 << 3) +#define INT_CFG (1 << 1) +#define RST_CFG (1 << 0) + +/* INT_EN Register */ +#define LOGIC2_IEN (1 << 5) +#define LOGIC1_IEN (1 << 4) +#define LOCK_IEN (1 << 3) +#define OVRFLOW_IEN (1 << 2) +#define GPI_IEN (1 << 1) +#define EVENT_IEN (1 << 0) + +/* Interrupt Status Register */ +#define LOGIC2_INT (1 << 5) +#define LOGIC1_INT (1 << 4) +#define LOCK_INT (1 << 3) +#define OVRFLOW_INT (1 << 2) +#define GPI_INT (1 << 1) +#define EVENT_INT (1 << 0) + +/* STATUS Register */ + +#define LOGIC2_STAT (1 << 7) +#define LOGIC1_STAT (1 << 6) +#define LOCK_STAT (1 << 5) +#define KEC 0xF + +/* PIN_CONFIG_D Register */ +#define C4_EXTEND_CFG (1 << 6) /* RESET2 */ +#define R4_EXTEND_CFG (1 << 5) /* RESET1 */ + +/* LOCK_CFG */ +#define LOCK_EN (1 << 0) + +#define PTIME_MASK 0x3 +#define LTIME_MASK 0x3 + +/* Key Event Register xy */ +#define KEY_EV_PRESSED (1 << 7) +#define KEY_EV_MASK (0x7F) + +#define KEYP_MAX_EVENT 16 + +#define MAXGPIO 19 +#define ADP_BANK(offs) ((offs) >> 3) +#define ADP_BIT(offs) (1u << ((offs) & 0x7)) + +struct adp5589_kpad { + struct i2c_client *client; + struct input_dev *input; + unsigned short keycode[ADP5589_KEYMAPSIZE]; + const struct adp5589_gpi_map *gpimap; + unsigned short gpimapsize; + unsigned extend_cfg; +#ifdef CONFIG_GPIOLIB + unsigned char gpiomap[MAXGPIO]; + bool export_gpio; + struct gpio_chip gc; + struct mutex gpio_lock; /* Protect cached dir, dat_out */ + u8 dat_out[3]; + u8 dir[3]; +#endif +}; + +static int adp5589_read(struct i2c_client *client, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "Read Error\n"); + + return ret; +} + +static int adp5589_write(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, reg, val); +} + +#ifdef CONFIG_GPIOLIB +static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + + return !!(adp5589_read(kpad->client, ADP5589_GPI_STATUS_A + bank) & + bit); +} + +static void adp5589_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + + mutex_lock(&kpad->gpio_lock); + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank, + kpad->dat_out[bank]); + + mutex_unlock(&kpad->gpio_lock); +} + +static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] &= ~bit; + ret = adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int adp5589_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] |= bit; + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + ret = adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank, + kpad->dat_out[bank]); + ret |= adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int __devinit adp5589_build_gpiomap(struct adp5589_kpad *kpad, + const struct adp5589_kpad_platform_data *pdata) +{ + bool pin_used[MAXGPIO]; + int n_unused = 0; + int i; + + memset(pin_used, false, sizeof(pin_used)); + + for (i = 0; i < MAXGPIO; i++) + if (pdata->keypad_en_mask & (1 << i)) + pin_used[i] = true; + + for (i = 0; i < kpad->gpimapsize; i++) + pin_used[kpad->gpimap[i].pin - ADP5589_GPI_PIN_BASE] = true; + + if (kpad->extend_cfg & R4_EXTEND_CFG) + pin_used[4] = true; + + if (kpad->extend_cfg & C4_EXTEND_CFG) + pin_used[12] = true; + + for (i = 0; i < MAXGPIO; i++) + if (!pin_used[i]) + kpad->gpiomap[n_unused++] = i; + + return n_unused; +} + +static int __devinit adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev->platform_data; + const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; + int i, error; + + if (!gpio_data) + return 0; + + kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata); + if (kpad->gc.ngpio == 0) { + dev_info(dev, "No unused gpios left to export\n"); + return 0; + } + + kpad->export_gpio = true; + + kpad->gc.direction_input = adp5589_gpio_direction_input; + kpad->gc.direction_output = adp5589_gpio_direction_output; + kpad->gc.get = adp5589_gpio_get_value; + kpad->gc.set = adp5589_gpio_set_value; + kpad->gc.can_sleep = 1; + + kpad->gc.base = gpio_data->gpio_start; + kpad->gc.label = kpad->client->name; + kpad->gc.owner = THIS_MODULE; + + mutex_init(&kpad->gpio_lock); + + error = gpiochip_add(&kpad->gc); + if (error) { + dev_err(dev, "gpiochip_add failed, err: %d\n", error); + return error; + } + + for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + kpad->dat_out[i] = adp5589_read(kpad->client, + ADP5589_GPO_DATA_OUT_A + i); + kpad->dir[i] = adp5589_read(kpad->client, + ADP5589_GPIO_DIRECTION_A + i); + } + + if (gpio_data->setup) { + error = gpio_data->setup(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "setup failed, %d\n", error); + } + + return 0; +} + +static void __devexit adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev->platform_data; + const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; + int error; + + if (!kpad->export_gpio) + return; + + if (gpio_data->teardown) { + error = gpio_data->teardown(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "teardown failed %d\n", error); + } + + error = gpiochip_remove(&kpad->gc); + if (error) + dev_warn(dev, "gpiochip_remove failed %d\n", error); +} +#else +static inline int adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + return 0; +} + +static inline void adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ +} +#endif + +static void adp5589_report_switches(struct adp5589_kpad *kpad, + int key, int key_val) +{ + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + if (key_val == kpad->gpimap[i].pin) { + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + key & KEY_EV_PRESSED); + break; + } + } +} + +static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt) +{ + int i; + + for (i = 0; i < ev_cnt; i++) { + int key = adp5589_read(kpad->client, ADP5589_FIFO_1 + i); + int key_val = key & KEY_EV_MASK; + + if (key_val >= ADP5589_GPI_PIN_BASE && + key_val <= ADP5589_GPI_PIN_END) { + adp5589_report_switches(kpad, key, key_val); + } else { + input_report_key(kpad->input, + kpad->keycode[key_val - 1], + key & KEY_EV_PRESSED); + } + } +} + +static irqreturn_t adp5589_irq(int irq, void *handle) +{ + struct adp5589_kpad *kpad = handle; + struct i2c_client *client = kpad->client; + int status, ev_cnt; + + status = adp5589_read(client, ADP5589_INT_STATUS); + + if (status & OVRFLOW_INT) /* Unlikely and should never happen */ + dev_err(&client->dev, "Event Overflow Error\n"); + + if (status & EVENT_INT) { + ev_cnt = adp5589_read(client, ADP5589_STATUS) & KEC; + if (ev_cnt) { + adp5589_report_events(kpad, ev_cnt); + input_sync(kpad->input); + } + } + + adp5589_write(client, ADP5589_INT_STATUS, status); /* Status is W1C */ + + return IRQ_HANDLED; +} + +static int __devinit adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key) +{ + int i; + + for (i = 0; i < ADP5589_KEYMAPSIZE; i++) + if (key == kpad->keycode[i]) + return (i + 1) | KEY_EV_PRESSED; + + dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n"); + + return -EINVAL; +} + +static int __devinit adp5589_setup(struct adp5589_kpad *kpad) +{ + struct i2c_client *client = kpad->client; + const struct adp5589_kpad_platform_data *pdata = + client->dev.platform_data; + int i, ret; + unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; + unsigned char pull_mask = 0; + + ret = adp5589_write(client, ADP5589_PIN_CONFIG_A, + pdata->keypad_en_mask & 0xFF); + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_B, + (pdata->keypad_en_mask >> 8) & 0xFF); + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C, + (pdata->keypad_en_mask >> 16) & 0xFF); + + if (pdata->en_keylock) { + ret |= adp5589_write(client, ADP5589_UNLOCK1, + pdata->unlock_key1); + ret |= adp5589_write(client, ADP5589_UNLOCK2, + pdata->unlock_key2); + ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS, + pdata->unlock_timer & LTIME_MASK); + ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN); + } + + for (i = 0; i < KEYP_MAX_EVENT; i++) + ret |= adp5589_read(client, ADP5589_FIFO_1 + i); + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin <= ADP5589_GPI_PIN_ROW_END) { + evt_mode1 |= (1 << (pin - ADP5589_GPI_PIN_ROW_BASE)); + } else { + evt_mode2 |= + ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) & 0xFF); + evt_mode3 |= + ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) >> 8); + } + } + + if (pdata->gpimapsize) { + ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_A, evt_mode1); + ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_B, evt_mode2); + ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_C, evt_mode3); + } + + if (pdata->pull_dis_mask & pdata->pullup_en_100k & + pdata->pullup_en_300k & pdata->pulldown_en_300k) + dev_warn(&client->dev, "Conflicting pull resistor config\n"); + + for (i = 0; i < MAXGPIO; i++) { + unsigned val = 0; + + if (pdata->pullup_en_300k & (1 << i)) + val = 0; + else if (pdata->pulldown_en_300k & (1 << i)) + val = 1; + else if (pdata->pullup_en_100k & (1 << i)) + val = 2; + else if (pdata->pull_dis_mask & (1 << i)) + val = 3; + + pull_mask |= val << (2 * (i & 0x3)); + + if ((i & 0x3) == 0x3 || i == MAXGPIO - 1) { + ret |= adp5589_write(client, + ADP5589_RPULL_CONFIG_A + (i >> 2), + pull_mask); + pull_mask = 0; + } + } + + if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) { + ret |= adp5589_write(client, ADP5589_RESET1_EVENT_A, + adp5589_get_evcode(kpad, + pdata->reset1_key_1)); + ret |= adp5589_write(client, ADP5589_RESET1_EVENT_B, + adp5589_get_evcode(kpad, + pdata->reset1_key_2)); + ret |= adp5589_write(client, ADP5589_RESET1_EVENT_C, + adp5589_get_evcode(kpad, + pdata->reset1_key_3)); + kpad->extend_cfg |= R4_EXTEND_CFG; + } + + if (pdata->reset2_key_1 && pdata->reset2_key_2) { + ret |= adp5589_write(client, ADP5589_RESET2_EVENT_A, + adp5589_get_evcode(kpad, + pdata->reset2_key_1)); + ret |= adp5589_write(client, ADP5589_RESET2_EVENT_B, + adp5589_get_evcode(kpad, + pdata->reset2_key_2)); + kpad->extend_cfg |= C4_EXTEND_CFG; + } + + if (kpad->extend_cfg) { + ret |= adp5589_write(client, ADP5589_RESET_CFG, + pdata->reset_cfg); + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_D, + kpad->extend_cfg); + } + + for (i = 0; i <= ADP_BANK(MAXGPIO); i++) + ret |= adp5589_write(client, ADP5589_DEBOUNCE_DIS_A + i, + pdata->debounce_dis_mask >> (i * 8)); + + ret |= adp5589_write(client, ADP5589_POLL_PTIME_CFG, + pdata->scan_cycle_time & PTIME_MASK); + ret |= adp5589_write(client, ADP5589_INT_STATUS, LOGIC2_INT | + LOGIC1_INT | OVRFLOW_INT | LOCK_INT | + GPI_INT | EVENT_INT); /* Status is W1C */ + + ret |= adp5589_write(client, ADP5589_GENERAL_CFG, + INT_CFG | OSC_EN | CORE_CLK(3)); + ret |= adp5589_write(client, ADP5589_INT_EN, + OVRFLOW_IEN | GPI_IEN | EVENT_IEN); + + if (ret < 0) { + dev_err(&client->dev, "Write Error\n"); + return ret; + } + + return 0; +} + +static void __devinit adp5589_report_switch_state(struct adp5589_kpad *kpad) +{ + int gpi_stat1 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_A); + int gpi_stat2 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_B); + int gpi_stat3 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_C); + int gpi_stat_tmp, pin_loc; + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + unsigned short pin = kpad->gpimap[i].pin; + + if (pin <= ADP5589_GPI_PIN_ROW_END) { + gpi_stat_tmp = gpi_stat1; + pin_loc = pin - ADP5589_GPI_PIN_ROW_BASE; + } else if ((pin - ADP5589_GPI_PIN_COL_BASE) < 8) { + gpi_stat_tmp = gpi_stat2; + pin_loc = pin - ADP5589_GPI_PIN_COL_BASE; + } else { + gpi_stat_tmp = gpi_stat3; + pin_loc = pin - ADP5589_GPI_PIN_COL_BASE - 8; + } + + if (gpi_stat_tmp < 0) { + dev_err(&kpad->client->dev, + "Can't read GPIO_DAT_STAT switch" + " %d default to OFF\n", pin); + gpi_stat_tmp = 0; + } + + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + !(gpi_stat_tmp & (1 << pin_loc))); + } + + input_sync(kpad->input); +} + +static int __devinit adp5589_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5589_kpad *kpad; + const struct adp5589_kpad_platform_data *pdata; + struct input_dev *input; + unsigned int revid; + int ret, i; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + if (!((pdata->keypad_en_mask & 0xFF) && + (pdata->keypad_en_mask >> 8)) || !pdata->keymap) { + dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); + return -EINVAL; + } + + if (pdata->keymapsize != ADP5589_KEYMAPSIZE) { + dev_err(&client->dev, "invalid keymapsize\n"); + return -EINVAL; + } + + if (!pdata->gpimap && pdata->gpimapsize) { + dev_err(&client->dev, "invalid gpimap from pdata\n"); + return -EINVAL; + } + + if (pdata->gpimapsize > ADP5589_GPIMAPSIZE_MAX) { + dev_err(&client->dev, "invalid gpimapsize\n"); + return -EINVAL; + } + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin < ADP5589_GPI_PIN_BASE || pin > ADP5589_GPI_PIN_END) { + dev_err(&client->dev, "invalid gpi pin data\n"); + return -EINVAL; + } + + if ((1 << (pin - ADP5589_GPI_PIN_ROW_BASE)) & + pdata->keypad_en_mask) { + dev_err(&client->dev, "invalid gpi row/col data\n"); + return -EINVAL; + } < |