On 03/24/2011 09:08 AM, Dmitry Torokhov wrote: > Hi Mike, Michael, > > On Wed, Mar 23, 2011 at 12:05:16PM -0400, Mike Frysinger wrote: > >> From: Michael Hennerich <michael.hennerich@xxxxxxxxxx> >> >> From http://www.analog.com/ADP5589: >> The ADP5589 is an I/O port expander and keypad matrix decoder designed >> for QWERTY type phones that require a large keypad matrix and expanded >> I/O lines. >> >> > Looks mostly good, just a few comments below. > > However the biggest question is if it could be folded into > adp5588-keys.c as they look somewhat similar. > Hi Dmitry, These parts have a totally different register map, and also different bit-fields. I agree there is some common function set, but overall the two parts are too different. > >> Signed-off-by: Michael Hennerich <michael.hennerich@xxxxxxxxxx> >> Signed-off-by: Mike Frysinger <vapier@xxxxxxxxxx> >> --- >> drivers/input/keyboard/Kconfig | 10 + >> drivers/input/keyboard/Makefile | 1 + >> drivers/input/keyboard/adp5589-keys.c | 769 +++++++++++++++++++++++++++++++++ >> include/linux/input/adp5589.h | 213 +++++++++ >> 4 files changed, 993 insertions(+), 0 deletions(-) >> create mode 100644 drivers/input/keyboard/adp5589-keys.c >> create mode 100644 include/linux/input/adp5589.h >> >> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig >> index b16bed0..688a55c 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 >> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile >> index 878e6c2..f57425c 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 >> diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c >> new file mode 100644 >> index 0000000..ab7a880 >> --- /dev/null >> +++ b/drivers/input/keyboard/adp5589-keys.c >> @@ -0,0 +1,769 @@ >> +/* >> + * 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_events(struct adp5589_kpad *kpad, int ev_cnt) >> +{ >> + int i, j; >> + >> + 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) { >> + for (j = 0; j < kpad->gpimapsize; j++) { >> + if (key_val == kpad->gpimap[j].pin) { >> + input_report_switch(kpad->input, >> + kpad->gpimap[j]. >> + sw_evt, >> + key & >> + KEY_EV_PRESSED); >> + break; >> + } >> + } >> + } 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 = >> + client->dev.platform_data; >> + 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; >> + } >> + >> + 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; >> + } >> + } >> + >> + if (!client->irq) { >> + dev_err(&client->dev, "no IRQ?\n"); >> + return -EINVAL; >> + } >> + >> + kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); >> + input = input_allocate_device(); >> + if (!kpad || !input) { >> + error = -ENOMEM; >> + goto err_free_mem; >> + } >> + >> + kpad->client = client; >> + kpad->input = input; >> + >> + ret = adp5589_read(client, ADP5589_ID); >> + if (ret < 0) { >> + error = ret; >> + goto err_free_mem; >> + } >> + >> + revid = (u8) ret & ADP5589_DEVICE_ID_MASK; >> + >> + input->name = client->name; >> + input->phys = "adp5589-keys/input0"; >> + input->dev.parent = &client->dev; >> + >> + input_set_drvdata(input, kpad); >> + >> + input->id.bustype = BUS_I2C; >> + input->id.vendor = 0x0001; >> + input->id.product = 0x0001; >> + input->id.version = revid; >> + >> + input->keycodesize = sizeof(kpad->keycode[0]); >> + input->keycodemax = pdata->keymapsize; >> + input->keycode = kpad->keycode; >> + >> + memcpy(kpad->keycode, pdata->keymap, >> + pdata->keymapsize * input->keycodesize); >> + >> + kpad->gpimap = pdata->gpimap; >> + kpad->gpimapsize = pdata->gpimapsize; >> + >> + /* setup input device */ >> + __set_bit(EV_KEY, input->evbit); >> + >> + if (pdata->repeat) >> + __set_bit(EV_REP, input->evbit); >> + >> + for (i = 0; i < input->keycodemax; i++) >> + __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit); >> + __clear_bit(KEY_RESERVED, input->keybit); >> + >> + if (kpad->gpimapsize) >> + __set_bit(EV_SW, input->evbit); >> + for (i = 0; i < kpad->gpimapsize; i++) >> + __set_bit(kpad->gpimap[i].sw_evt, input->swbit); >> + >> + error = input_register_device(input); >> + if (error) { >> + dev_err(&client->dev, "unable to register input device\n"); >> + goto err_free_mem; >> + } >> + >> + error = request_threaded_irq(client->irq, NULL, adp5589_irq, >> + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, >> + client->dev.driver->name, kpad); >> + if (error) { >> + dev_err(&client->dev, "irq %d busy?\n", client->irq); >> + goto err_unreg_dev; >> + } >> + >> + error = adp5589_setup(kpad); >> + if (error) >> + goto err_free_irq; >> + >> + if (kpad->gpimapsize) >> + adp5589_report_switch_state(kpad); >> + >> + error = adp5589_gpio_add(kpad); >> + if (error) >> + goto err_free_irq; >> + >> + device_init_wakeup(&client->dev, 1); >> + i2c_set_clientdata(client, kpad); >> + >> + dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); >> + return 0; >> + >> +err_free_irq: >> + free_irq(client->irq, kpad); >> +err_unreg_dev: >> + input_unregister_device(input); >> + input = NULL; >> +err_free_mem: >> + input_free_device(input); >> + kfree(kpad); >> + >> + return error; >> +} >> + >> +static int __devexit adp5589_remove(struct i2c_client *client) >> +{ >> + struct adp5589_kpad *kpad = i2c_get_clientdata(client); >> + >> + adp5589_write(client, ADP5589_GENERAL_CFG, 0); >> + free_irq(client->irq, kpad); >> + input_unregister_device(kpad->input); >> + adp5589_gpio_remove(kpad); >> + kfree(kpad); >> + >> + return 0; >> +} >> + >> +#ifdef CONFIG_PM >> > CONFIG_PM_SLEEP > ok > >> +static int adp5589_suspend(struct device *dev) >> +{ >> + struct adp5589_kpad *kpad = dev_get_drvdata(dev); >> + struct i2c_client *client = kpad->client; >> + >> + disable_irq(client->irq); >> + >> + if (device_may_wakeup(&client->dev)) >> + enable_irq_wake(client->irq); >> + >> + return 0; >> +} >> + >> +static int adp5589_resume(struct device *dev) >> +{ >> + struct adp5589_kpad *kpad = dev_get_drvdata(dev); >> + struct i2c_client *client = kpad->client; >> + >> + if (device_may_wakeup(&client->dev)) >> + disable_irq_wake(client->irq); >> + >> + enable_irq(client->irq); >> + >> + return 0; >> +} >> + >> +static const struct dev_pm_ops adp5589_dev_pm_ops = { >> + .suspend = adp5589_suspend, >> + .resume = adp5589_resume, >> +}; >> +#endif >> > Just use unguarded > > static SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume); > ok > >> + >> +static const struct i2c_device_id adp5589_id[] = { >> + {"adp5589-keys", 0}, >> + {} >> +}; >> + >> +MODULE_DEVICE_TABLE(i2c, adp5589_id); >> + >> +static struct i2c_driver adp5589_driver = { >> + .driver = { >> + .name = KBUILD_MODNAME, >> +#ifdef CONFIG_PM >> + .pm = &adp5589_dev_pm_ops, >> +#endif >> > .owner ? > >> + }, >> > Identation seems wrong. > > >> + .probe = adp5589_probe, >> + .remove = __devexit_p(adp5589_remove), >> + .id_table = adp5589_id, >> +}; >> + >> +static int __init adp5589_init(void) >> +{ >> + return i2c_add_driver(&adp5589_driver); >> +} >> + >> +module_init(adp5589_init); >> + >> +static void __exit adp5589_exit(void) >> +{ >> + i2c_del_driver(&adp5589_driver); >> +} >> + >> +module_exit(adp5589_exit); >> + >> +MODULE_LICENSE("GPL"); >> +MODULE_AUTHOR("Michael Hennerich <hennerich@xxxxxxxxxxxxxxxxxxxx>"); >> +MODULE_DESCRIPTION("ADP5589 Keypad driver"); >> +MODULE_ALIAS("i2c:adp5589-keys"); >> > This is not needed since you define MODULE_DEVICE_TABLE() > I see - thanks - I remove it > >> diff --git a/include/linux/input/adp5589.h b/include/linux/input/adp5589.h >> new file mode 100644 >> index 0000000..998d148 >> --- /dev/null >> +++ b/include/linux/input/adp5589.h >> @@ -0,0 +1,213 @@ >> +/* >> + * Analog Devices ADP5589 I/O Expander and QWERTY Keypad Controller >> + * >> + * Copyright 2010-2011 Analog Devices Inc. >> + * >> + * Licensed under the GPL-2. >> + */ >> + >> +#ifndef _ADP5589_H >> +#define _ADP5589_H >> + >> +#define ADP5589_ID 0x00 >> +#define ADP5589_INT_STATUS 0x01 >> +#define ADP5589_STATUS 0x02 >> +#define ADP5589_FIFO_1 0x03 >> +#define ADP5589_FIFO_2 0x04 >> +#define ADP5589_FIFO_3 0x05 >> +#define ADP5589_FIFO_4 0x06 >> +#define ADP5589_FIFO_5 0x07 >> +#define ADP5589_FIFO_6 0x08 >> +#define ADP5589_FIFO_7 0x09 >> +#define ADP5589_FIFO_8 0x0A >> +#define ADP5589_FIFO_9 0x0B >> +#define ADP5589_FIFO_10 0x0C >> +#define ADP5589_FIFO_11 0x0D >> +#define ADP5589_FIFO_12 0x0E >> +#define ADP5589_FIFO_13 0x0F >> +#define ADP5589_FIFO_14 0x10 >> +#define ADP5589_FIFO_15 0x11 >> +#define ADP5589_FIFO_16 0x12 >> +#define ADP5589_GPI_INT_STAT_A 0x13 >> +#define ADP5589_GPI_INT_STAT_B 0x14 >> +#define ADP5589_GPI_INT_STAT_C 0x15 >> +#define ADP5589_GPI_STATUS_A 0x16 >> +#define ADP5589_GPI_STATUS_B 0x17 >> +#define ADP5589_GPI_STATUS_C 0x18 >> +#define ADP5589_RPULL_CONFIG_A 0x19 >> +#define ADP5589_RPULL_CONFIG_B 0x1A >> +#define ADP5589_RPULL_CONFIG_C 0x1B >> +#define ADP5589_RPULL_CONFIG_D 0x1C >> +#define ADP5589_RPULL_CONFIG_E 0x1D >> +#define ADP5589_GPI_INT_LEVEL_A 0x1E >> +#define ADP5589_GPI_INT_LEVEL_B 0x1F >> +#define ADP5589_GPI_INT_LEVEL_C 0x20 >> +#define ADP5589_GPI_EVENT_EN_A 0x21 >> +#define ADP5589_GPI_EVENT_EN_B 0x22 >> +#define ADP5589_GPI_EVENT_EN_C 0x23 >> +#define ADP5589_GPI_INTERRUPT_EN_A 0x24 >> +#define ADP5589_GPI_INTERRUPT_EN_B 0x25 >> +#define ADP5589_GPI_INTERRUPT_EN_C 0x26 >> +#define ADP5589_DEBOUNCE_DIS_A 0x27 >> +#define ADP5589_DEBOUNCE_DIS_B 0x28 >> +#define ADP5589_DEBOUNCE_DIS_C 0x29 >> +#define ADP5589_GPO_DATA_OUT_A 0x2A >> +#define ADP5589_GPO_DATA_OUT_B 0x2B >> +#define ADP5589_GPO_DATA_OUT_C 0x2C >> +#define ADP5589_GPO_OUT_MODE_A 0x2D >> +#define ADP5589_GPO_OUT_MODE_B 0x2E >> +#define ADP5589_GPO_OUT_MODE_C 0x2F >> +#define ADP5589_GPIO_DIRECTION_A 0x30 >> +#define ADP5589_GPIO_DIRECTION_B 0x31 >> +#define ADP5589_GPIO_DIRECTION_C 0x32 >> +#define ADP5589_UNLOCK1 0x33 >> +#define ADP5589_UNLOCK2 0x34 >> +#define ADP5589_EXT_LOCK_EVENT 0x35 >> +#define ADP5589_UNLOCK_TIMERS 0x36 >> +#define ADP5589_LOCK_CFG 0x37 >> +#define ADP5589_RESET1_EVENT_A 0x38 >> +#define ADP5589_RESET1_EVENT_B 0x39 >> +#define ADP5589_RESET1_EVENT_C 0x3A >> +#define ADP5589_RESET2_EVENT_A 0x3B >> +#define ADP5589_RESET2_EVENT_B 0x3C >> +#define ADP5589_RESET_CFG 0x3D >> +#define ADP5589_PWM_OFFT_LOW 0x3E >> +#define ADP5589_PWM_OFFT_HIGH 0x3F >> +#define ADP5589_PWM_ONT_LOW 0x40 >> +#define ADP5589_PWM_ONT_HIGH 0x41 >> +#define ADP5589_PWM_CFG 0x42 >> +#define ADP5589_CLOCK_DIV_CFG 0x43 >> +#define ADP5589_LOGIC_1_CFG 0x44 >> +#define ADP5589_LOGIC_2_CFG 0x45 >> +#define ADP5589_LOGIC_FF_CFG 0x46 >> +#define ADP5589_LOGIC_INT_EVENT_EN 0x47 >> +#define ADP5589_POLL_PTIME_CFG 0x48 >> +#define ADP5589_PIN_CONFIG_A 0x49 >> +#define ADP5589_PIN_CONFIG_B 0x4A >> +#define ADP5589_PIN_CONFIG_C 0x4B >> +#define ADP5589_PIN_CONFIG_D 0x4C >> +#define ADP5589_GENERAL_CFG 0x4D >> +#define ADP5589_INT_EN 0x4E >> + >> +#define ADP5589_DEVICE_ID_MASK 0xF >> + >> +/* Put one of these structures in i2c_board_info platform_data */ >> + >> +#define ADP5589_KEYMAPSIZE 88 >> + >> +#define ADP5589_GPI_PIN_ROW0 97 >> +#define ADP5589_GPI_PIN_ROW1 98 >> +#define ADP5589_GPI_PIN_ROW2 99 >> +#define ADP5589_GPI_PIN_ROW3 100 >> +#define ADP5589_GPI_PIN_ROW4 101 >> +#define ADP5589_GPI_PIN_ROW5 102 >> +#define ADP5589_GPI_PIN_ROW6 103 >> +#define ADP5589_GPI_PIN_ROW7 104 >> +#define ADP5589_GPI_PIN_COL0 105 >> +#define ADP5589_GPI_PIN_COL1 106 >> +#define ADP5589_GPI_PIN_COL2 107 >> +#define ADP5589_GPI_PIN_COL3 108 >> +#define ADP5589_GPI_PIN_COL4 109 >> +#define ADP5589_GPI_PIN_COL5 110 >> +#define ADP5589_GPI_PIN_COL6 111 >> +#define ADP5589_GPI_PIN_COL7 112 >> +#define ADP5589_GPI_PIN_COL8 113 >> +#define ADP5589_GPI_PIN_COL9 114 >> +#define ADP5589_GPI_PIN_COL10 115 >> +#define GPI_LOGIC1 116 >> +#define GPI_LOGIC2 117 >> + >> +#define ADP5589_GPI_PIN_ROW_BASE ADP5589_GPI_PIN_ROW0 >> +#define ADP5589_GPI_PIN_ROW_END ADP5589_GPI_PIN_ROW7 >> +#define ADP5589_GPI_PIN_COL_BASE ADP5589_GPI_PIN_COL0 >> +#define ADP5589_GPI_PIN_COL_END ADP5589_GPI_PIN_COL10 >> + >> +#define ADP5589_GPI_PIN_BASE ADP5589_GPI_PIN_ROW_BASE >> +#define ADP5589_GPI_PIN_END ADP5589_GPI_PIN_COL_END >> + >> +#define ADP5589_GPIMAPSIZE_MAX (ADP5589_GPI_PIN_END - ADP5589_GPI_PIN_BASE + 1) >> + >> +struct adp5589_gpi_map { >> + unsigned short pin; >> + unsigned short sw_evt; >> +}; >> + >> +/* scan_cycle_time */ >> +#define ADP5589_SCAN_CYCLE_10ms 0 >> +#define ADP5589_SCAN_CYCLE_20ms 1 >> +#define ADP5589_SCAN_CYCLE_30ms 2 >> +#define ADP5589_SCAN_CYCLE_40ms 3 >> + >> +/* RESET_CFG */ >> +#define RESET_PULSE_WIDTH_500us 0 >> +#define RESET_PULSE_WIDTH_1ms 1 >> +#define RESET_PULSE_WIDTH_2ms 2 >> +#define RESET_PULSE_WIDTH_10ms 3 >> + >> +#define RESET_TRIG_TIME_0ms (0 << 2) >> +#define RESET_TRIG_TIME_1000ms (1 << 2) >> +#define RESET_TRIG_TIME_1500ms (2 << 2) >> +#define RESET_TRIG_TIME_2000ms (3 << 2) >> +#define RESET_TRIG_TIME_2500ms (4 << 2) >> +#define RESET_TRIG_TIME_3000ms (5 << 2) >> +#define RESET_TRIG_TIME_3500ms (6 << 2) >> +#define RESET_TRIG_TIME_4000ms (7 << 2) >> + >> +#define RESET_PASSTHRU_EN (1 << 5) >> +#define RESET1_POL_HIGH (1 << 6) >> +#define RESET1_POL_LOW (0 << 6) >> +#define RESET2_POL_HIGH (1 << 7) >> +#define RESET2_POL_LOW (0 << 7) >> + >> +/* Mask Bits: >> + * C C C C C C C C C C C | R R R R R R R R >> + * 1 9 8 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 >> + * 0 >> + * ---------------- BIT ------------------ >> + * 1 1 1 1 1 1 1 1 1 0 0 | 0 0 0 0 0 0 0 0 >> + * 8 7 6 5 4 3 2 1 0 9 8 | 7 6 5 4 3 2 1 0 >> + */ >> + >> +#define ADP_ROW(x) (1 << (x)) >> +#define ADP_COL(x) (1 << (x + 8)) >> > Is this used anywhere? > > not in the driver file itself. It's there as convenience macro intended to be used in the platform file, where platform data is supplied. >> + >> +struct adp5589_kpad_platform_data { >> + unsigned keypad_en_mask; /* Keypad (Rows/Columns) enable mask */ >> + const unsigned short *keymap; /* Pointer to keymap */ >> + unsigned short keymapsize; /* Keymap size */ >> + unsigned repeat:1; /* Enable key repeat */ >> + unsigned en_keylock:1; /* Enable key lock feature */ >> > Maybe use bools? You are not saving any space with bitfields as far as > I can see... > > ok >> + unsigned char unlock_key1; /* Unlock Key 1 */ >> + unsigned char unlock_key2; /* Unlock Key 2 */ >> + unsigned char unlock_timer; /* Time in seconds [0..7] between the two unlock keys 0=disable */ >> + unsigned char scan_cycle_time; /* Time between consecutive scan cycles */ >> + unsigned char reset_cfg; /* Reset config */ >> + unsigned short reset1_key_1; /* Reset Key 1 */ >> + unsigned short reset1_key_2; /* Reset Key 2 */ >> + unsigned short reset1_key_3; /* Reset Key 3 */ >> + unsigned short reset2_key_1; /* Reset Key 1 */ >> + unsigned short reset2_key_2; /* Reset Key 2 */ >> + unsigned debounce_dis_mask; /* Disable debounce mask */ >> + unsigned pull_dis_mask; /* Disable all pull resistors mask */ >> + unsigned pullup_en_100k; /* Pull-Up 100k Enable Mask */ >> + unsigned pullup_en_300k; /* Pull-Up 300k Enable Mask */ >> + unsigned pulldown_en_300k; /* Pull-Down 300k Enable Mask */ >> + const struct adp5589_gpi_map *gpimap; >> + unsigned short gpimapsize; >> + const struct adp5589_gpio_platform_data *gpio_data; >> +}; >> + >> +struct i2c_client; /* forward declaration */ >> + >> +struct adp5589_gpio_platform_data { >> + int gpio_start; /* GPIO Chip base # */ >> + int (*setup)(struct i2c_client *client, >> + int gpio, unsigned ngpio, >> + void *context); >> + int (*teardown)(struct i2c_client *client, >> + int gpio, unsigned ngpio, >> + void *context); >> + void *context; >> +}; >> + >> +#endif >> -- >> 1.7.4.1 >> >> > Thanks. > > -- > Dmitry > Thanks for your feedback. -- Greetings, Michael -- Analog Devices GmbH Wilhelm-Wagenfeld-Str. 6 80807 Muenchen Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368; Geschaeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin, Margaret Seif -- 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