Re: [RFC] [PATCH] Input: ADP5588 - Support gpio function on unused pin

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Dmitry,

Thanks for your comments, update the patch, using
adp5588_gpio_platform_data instead of support_gpio and gpio_start. If
there is any comments and suggestion, please feel free let me know.

Why do we do this conditionally? Is there a reason why exporting unused
gpio pins would be unwelcome?
At the beginning, I think anyone who don't want the gpio function,
could disable it by support_gpio, if haven't this var, the gpio_start
have to be set correctly, otherwise, the keypad driver might be failed
when probe.
I update the patch, now using the gpio_data instead of it, if the
gpio_data = NULL, the gpio function will be disabled.

Throughout the driver we normally name struct adp5588_kpad variables as
kpad, not dev. In fact, when I see dev I think "driver model
abstraction" and not "this particular hardware". Care to rename to kpad
here (and below) as well?
Done.

Looks like your e-mail miight be line-wrapped...
Yes, it should be the e-mail system issue.

Make it bool (now that we have it)  and use boolean values below.
Done.

What happens if we end up with ngpio == 0?
Yes, it might be a issue, I correct it in the new patch.

Bool here as well. In theory bools might cause to waste a byte or 2 but
it's ok (and in this case you are not saving anything...).
Done.

You need to actually handle this (add an error message) since we can't
simply ignore errors.
Handle this issue in the new patch, porting the setup() and teardown()
from adp5588_gpio.c.

Patch:

>From bfd7f4e6abd73b00a5f1ad667c6119312784f9b0 Mon Sep 17 00:00:00 2001
From: Xiaolong Chen <a21785@xxxxxxxxxxxx>
Date: Tue, 20 Jul 2010 03:58:52 +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 gpio_data,
support gpio function if gpio_data is not NULL.

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>
Acked-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx>
---
 drivers/input/keyboard/adp5588-keys.c |  184 +++++++++++++++++++++++++++++++++
 include/linux/i2c/adp5588.h           |    1 +
 2 files changed, 185 insertions(+), 0 deletions(-)

diff --git a/drivers/input/keyboard/adp5588-keys.c
b/drivers/input/keyboard/adp5588-keys.c
index 3c8876a..66d1e4a 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];
+	bool 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 *kpad =
+	    container_of(chip, struct adp5588_kpad, gpio_chip);
+
+	bank = ADP_BANK(kpad->gpiomap[off]);
+	bit = ADP_BIT(kpad->gpiomap[off]);
+
+	return !!(adp5588_read(kpad->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 *kpad =
+	    container_of(chip, struct adp5588_kpad, gpio_chip);
+
+	bank = ADP_BANK(kpad->gpiomap[off]);
+	bit = ADP_BIT(kpad->gpiomap[off]);
+
+	mutex_lock(&kpad->gpio_lock);
+	if (val)
+		kpad->dat_out[bank] |= bit;
+	else
+		kpad->dat_out[bank] &= ~bit;
+
+	adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
+			   kpad->dat_out[bank]);
+	mutex_unlock(&kpad->gpio_lock);
+}
+
+static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
+{
+	int ret;
+	unsigned bank, bit;
+	struct adp5588_kpad *kpad =
+	    container_of(chip, struct adp5588_kpad, gpio_chip);
+
+	bank = ADP_BANK(kpad->gpiomap[off]);
+	bit = ADP_BIT(kpad->gpiomap[off]);
+
+	mutex_lock(&kpad->gpio_lock);
+	kpad->dir[bank] &= ~bit;
+	ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
+	mutex_unlock(&kpad->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 *kpad =
+	    container_of(chip, struct adp5588_kpad, gpio_chip);
+
+	bank = ADP_BANK(kpad->gpiomap[off]);
+	bit = ADP_BIT(kpad->gpiomap[off]);
+
+	mutex_lock(&kpad->gpio_lock);
+	kpad->dir[bank] |= bit;
+
+	if (val)
+		kpad->dat_out[bank] |= bit;
+	else
+		kpad->dat_out[bank] &= ~bit;
+
+	ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
+				 kpad->dat_out[bank]);
+	ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank,
+				 kpad->dir[bank]);
+	mutex_unlock(&kpad->gpio_lock);
+
+	return ret;
+}
+
 static void adp5588_work(struct work_struct *work)
 {
 	struct adp5588_kpad *kpad = container_of(work,
@@ -180,6 +270,15 @@ static int __devinit adp5588_setup(struct
i2c_client *client)
 		ret |= adp5588_write(client, GPI_EM3, evt_mode3);
 	}

+	if (pdata->gpio_data) {
+		for (i = 0; i <= ADP_BANK(MAXGPIO); i++) {
+			int pull_mask = pdata->gpio_data->pullup_dis_mask;
+
+			ret |= adp5588_write(client, GPIO_PULL1 + i,
+				(pull_mask >> (8 * i)) & 0xFF);
+		}
+	}
+
 	ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT |
 					OVR_FLOW_INT | K_LCK_INT |
 					GPI_INT | KE_INT); /* Status is W1C */
@@ -204,6 +303,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 +373,44 @@ static int __devinit adp5588_probe(struct
i2c_client *client,
 	kpad->input = input;
 	INIT_DELAYED_WORK(&kpad->work, adp5588_work);

+	if (pdata->gpio_data) {
+		int j = 0;
+		bool pin_used[MAXGPIO];
+
+		for (i = 0; i < pdata->rows; i++)
+			pin_used[i] = true;
+
+		for (i = 0; i < pdata->cols; i++)
+			pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true;
+
+		for (i = 0; i < kpad->gpimapsize; i++)
+			pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true;
+
+		for (i = 0; i < MAXGPIO; i++) {
+			if (!pin_used[i])
+				kpad->gpiomap[j++] = i;
+		}
+		kpad->gpio_chip.ngpio = j;
+
+		if (kpad->gpio_chip.ngpio)
+			kpad->support_gpio = true;
+	}
+
+	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_data->gpio_start;
+		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 +478,31 @@ static int __devinit adp5588_probe(struct
i2c_client *client,
 	device_init_wakeup(&client->dev, 1);
 	i2c_set_clientdata(client, kpad);

+	if (kpad->support_gpio) {
+		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 (pdata->gpio_data->setup) {
+			ret = pdata->gpio_data->setup(client,
+				kpad->gpio_chip.base, kpad->gpio_chip.ngpio,
+				pdata->gpio_data->context);
+			if (ret < 0)
+				dev_warn(&client->dev,
+						"setup failed, %d\n", ret);
+		}
+	}
+
 	if (kpad->gpimapsize) {
 		gpi_stat1 = adp5588_read(client, GPIO_DAT_STAT1);
 		gpi_stat2 = adp5588_read(client, GPIO_DAT_STAT2);
@@ -389,12 +552,33 @@ static int __devinit adp5588_probe(struct
i2c_client *client,

 static int __devexit adp5588_remove(struct i2c_client *client)
 {
+	int ret;
+	struct adp5588_kpad_platform_data *pdata = client->dev.platform_data;
 	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) {
+		if (pdata->gpio_data->teardown) {
+			ret = pdata->gpio_data->teardown(client,
+				kpad->gpio_chip.base, kpad->gpio_chip.ngpio,
+				pdata->gpio_data->context);
+			if (ret < 0) {
+				dev_err(&client->dev,
+						"teardown failed %d\n", ret);
+				return ret;
+			}
+		}
+
+		ret = gpiochip_remove(&kpad->gpio_chip);
+		if (ret) {
+			dev_err(&client->dev,
+					"gpiochip_remove failed %d\n", ret);
+			return ret;
+		}
+	}
 	i2c_set_clientdata(client, NULL);
 	kfree(kpad);

diff --git a/include/linux/i2c/adp5588.h b/include/linux/i2c/adp5588.h
index b6e8807..ede3cd2 100644
--- a/include/linux/i2c/adp5588.h
+++ b/include/linux/i2c/adp5588.h
@@ -123,6 +123,7 @@ struct adp5588_kpad_platform_data {
 	unsigned short unlock_key2;	/* Unlock Key 2 */
 	const struct adp5588_gpi_map *gpimap;
 	unsigned short gpimapsize;
+	struct adp5588_gpio_platform_data *gpio_data;
 };

 struct adp5588_gpio_platform_data {
-- 
1.7.0.4

Thanks,
Xiaolong



On Sat, Jul 17, 2010 at 12:42 AM, Dmitry Torokhov
<dmitry.torokhov@xxxxxxxxx> wrote:
> One more thing...
>
> On Fri, Jul 16, 2010 at 09:20:18AM -0700, Dmitry Torokhov wrote:
>> On Fri, Jul 16, 2010 at 07:48:07PM +0800, Xiaolong CHEN wrote:
>> >  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);
>
> You need to actually handle this (add an error message) since we can't
> simply ignore errors.
>
> However I think that having gpiochip_remove() __must_check while
> gpiochip_add is quite silly.
>
> I'd argue that gpiochip_remove() should wither WARN or BUG if we trying
> to remove a chip that still has GPIOs requested and return void since
> callers normally can't do anything about the failure. Many simply abort
> device teardown leaving device a zombie and report failure up the stack,
> to the driver core, where it is ignored. Not very helpful.
>
> --
> Dmitry
>
--
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


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux