Hi, Michael, Thanks for your comments, I agree with you, it's a better way to export only the spare pins as GPIO. I update this patch, any comments and suggestion, feel free let me know. Patch: >From 027a344b0fc49f87edd325d5896c3d0985ec5869 Mon Sep 17 00:00:00 2001 From: Xiaolong Chen <a21785@xxxxxxxxxxxx> Date: Sat, 17 Jul 2010 03:18:28 +0800 Subject: [PATCH] Input: ADP5588 - Support gpio function on unused pin This implements an optional gpio function on unused pin by adp5588 kpad. adp5588_kpad_platform_data has been extended with support_gpio, support gpio function if it equals 1, and gpio_start, the gpio chip base for this module. Signed-off-by: Xiaolong Chen <xiao-long.chen@xxxxxxxxxxxx> Signed-off-by: Yuanbo Ye <yuan-bo.ye@xxxxxxxxxxxx> Signed-off-by: Tao Hu <taohu@xxxxxxxxxxxx> Acked-by: Michael Hennerich <michael.hennerich@xxxxxxxxxx> --- drivers/input/keyboard/adp5588-keys.c | 144 +++++++++++++++++++++++++++++++++ include/linux/i2c/adp5588.h | 2 + 2 files changed, 146 insertions(+), 0 deletions(-) diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 3c8876a..ed18c93 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h> #include <linux/input.h> #include <linux/i2c.h> +#include <linux/gpio.h> #include <linux/i2c/adp5588.h> @@ -52,6 +53,10 @@ #define KEYP_MAX_EVENT 10 +#define MAXGPIO 18 +#define ADP_BANK(offs) ((offs) >> 3) +#define ADP_BIT(offs) (1u << ((offs) & 0x7)) + /* * Early pre 4.0 Silicon required to delay readout by at least 25ms, * since the Event Counter Register updated 25ms after the interrupt @@ -67,6 +72,12 @@ struct adp5588_kpad { unsigned short keycode[ADP5588_KEYMAPSIZE]; const struct adp5588_gpi_map *gpimap; unsigned short gpimapsize; + unsigned char gpiomap[MAXGPIO]; + unsigned support_gpio; + struct gpio_chip gpio_chip; + struct mutex gpio_lock; /* Protect cached dir, dat_out */ + uint8_t dat_out[3]; + uint8_t dir[3]; }; static int adp5588_read(struct i2c_client *client, u8 reg) @@ -84,6 +95,85 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val) return i2c_smbus_write_byte_data(client, reg, val); } +static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + unsigned bank, bit; + struct adp5588_kpad *dev = + container_of(chip, struct adp5588_kpad, gpio_chip); + + bank = ADP_BANK(dev->gpiomap[off]); + bit = ADP_BIT(dev->gpiomap[off]); + + return !!(adp5588_read(dev->client, GPIO_DAT_STAT1 + bank) & bit); +} + +static void adp5588_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + unsigned bank, bit; + struct adp5588_kpad *dev = + container_of(chip, struct adp5588_kpad, gpio_chip); + + bank = ADP_BANK(dev->gpiomap[off]); + bit = ADP_BIT(dev->gpiomap[off]); + + mutex_lock(&dev->gpio_lock); + if (val) + dev->dat_out[bank] |= bit; + else + dev->dat_out[bank] &= ~bit; + + adp5588_write(dev->client, GPIO_DAT_OUT1 + bank, + dev->dat_out[bank]); + mutex_unlock(&dev->gpio_lock); +} + +static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + int ret; + unsigned bank, bit; + struct adp5588_kpad *dev = + container_of(chip, struct adp5588_kpad, gpio_chip); + + bank = ADP_BANK(dev->gpiomap[off]); + bit = ADP_BIT(dev->gpiomap[off]); + + mutex_lock(&dev->gpio_lock); + dev->dir[bank] &= ~bit; + ret = adp5588_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]); + mutex_unlock(&dev->gpio_lock); + + return ret; +} + +static int adp5588_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + int ret; + unsigned bank, bit; + struct adp5588_kpad *dev = + container_of(chip, struct adp5588_kpad, gpio_chip); + + bank = ADP_BANK(dev->gpiomap[off]); + bit = ADP_BIT(dev->gpiomap[off]); + + mutex_lock(&dev->gpio_lock); + dev->dir[bank] |= bit; + + if (val) + dev->dat_out[bank] |= bit; + else + dev->dat_out[bank] &= ~bit; + + ret = adp5588_write(dev->client, GPIO_DAT_OUT1 + bank, + dev->dat_out[bank]); + ret |= adp5588_write(dev->client, GPIO_DIR1 + bank, + dev->dir[bank]); + mutex_unlock(&dev->gpio_lock); + + return ret; +} + static void adp5588_work(struct work_struct *work) { struct adp5588_kpad *kpad = container_of(work, @@ -204,6 +294,7 @@ static int __devinit adp5588_probe(struct i2c_client *client, int ret, i; int error; int gpi_stat1 = 0, gpi_stat2 = 0, gpi_stat3 = 0; + struct gpio_chip *gc; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -273,6 +364,24 @@ static int __devinit adp5588_probe(struct i2c_client *client, kpad->input = input; INIT_DELAYED_WORK(&kpad->work, adp5588_work); + kpad->support_gpio = pdata->support_gpio; + + if (kpad->support_gpio) { + gc = &kpad->gpio_chip; + gc->direction_input = adp5588_gpio_direction_input; + gc->direction_output = adp5588_gpio_direction_output; + gc->get = adp5588_gpio_get_value; + gc->set = adp5588_gpio_set_value; + gc->can_sleep = 1; + + gc->base = pdata->gpio_start; + gc->ngpio = MAXGPIO; + gc->label = client->name; + gc->owner = THIS_MODULE; + + mutex_init(&kpad->gpio_lock); + } + ret = adp5588_read(client, DEV_ID); if (ret < 0) { error = ret; @@ -340,6 +449,38 @@ static int __devinit adp5588_probe(struct i2c_client *client, device_init_wakeup(&client->dev, 1); i2c_set_clientdata(client, kpad); + if (kpad->support_gpio) { + int j = 0; + unsigned char pin_used[MAXGPIO]; + + for (i = 0; i < pdata->rows; i++) + pin_used[i] = 1; + + for (i = 0; i < pdata->cols; i++) + pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = 1; + + for (i = 0; i < kpad->gpimapsize; i++) + pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = 1; + + for (i = 0; i < MAXGPIO; i++) { + if (!pin_used[i]) + kpad->gpiomap[j++] = i; + } + kpad->gpio_chip.ngpio = j; + + ret = gpiochip_add(&kpad->gpio_chip); + if (ret) { + dev_err(&client->dev, "gpiochip_add err: %d\n", ret); + goto err_free_irq; + } + + for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + kpad->dat_out[i] = adp5588_read(client, + GPIO_DAT_OUT1 + i); + kpad->dir[i] = adp5588_read(client, GPIO_DIR1 + i); + } + } + if (kpad->gpimapsize) { gpi_stat1 = adp5588_read(client, GPIO_DAT_STAT1); gpi_stat2 = adp5588_read(client, GPIO_DAT_STAT2); @@ -389,12 +530,15 @@ static int __devinit adp5588_probe(struct i2c_client *client, static int __devexit adp5588_remove(struct i2c_client *client) { + int ret; struct adp5588_kpad *kpad = i2c_get_clientdata(client); adp5588_write(client, CFG, 0); free_irq(client->irq, kpad); cancel_delayed_work_sync(&kpad->work); input_unregister_device(kpad->input); + if (kpad->support_gpio) + ret = gpiochip_remove(&kpad->gpio_chip); i2c_set_clientdata(client, NULL); kfree(kpad); diff --git a/include/linux/i2c/adp5588.h b/include/linux/i2c/adp5588.h index a00686c..35078c3 100644 --- a/include/linux/i2c/adp5588.h +++ b/include/linux/i2c/adp5588.h @@ -123,6 +123,8 @@ struct adp5588_kpad_platform_data { unsigned short unlock_key2; /* Unlock Key 2 */ const struct adp5588_gpi_map *gpimap; unsigned short gpimapsize; + unsigned support_gpio:1; /* Support gpio on unused pin */ + unsigned gpio_start; }; #endif -- 1.7.0.4 Thanks, Xiaolong On Fri, Jul 16, 2010 at 4:48 PM, Hennerich, Michael <Michael.Hennerich@xxxxxxxxxx> wrote: > Xiaolong CHEN wrote on 2010-07-16: >> Hi, Michael and Dmitry >> >> Do you have any comments and suggestion? > > Hi Xiaolong, > > First of all - great idea to export unused pins as GPIOs. > This closed the gap between adp5588-gpio and adp5588-keys. > > But I wonder why you gpio_request() and free inside the driver. > I would export only the spare pins as GPIO. I see that this will > complicate things a little bit by adding some mapping. > But first exporting as GPIO and then reserving them by > gpio_request doesn't look like a straight approach. > > If no one else complains (adding David) I'm fine with the way it is now. > > Acked-by: Michael Hennerich <michael.hennerich@xxxxxxxxxx> > > Greetings, > Michael > > Analog Devices GmbH Wilhelm-Wagenfeld-Str. 6 80807 Muenchen > Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 4036 Geschaeftsfuehrer 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