diff options
| author | Xiaolong Chen <xiaolong.chen@gmail.com> | 2010-07-26 04:01:11 -0400 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-07-26 04:17:41 -0400 |
| commit | ba9f507a1bea5ca2fc4a19e227c56b60fd5faca3 (patch) | |
| tree | e3eb50ca1716f9230022eac5497b1ed058e75949 | |
| parent | f1cba532e8c1001a39650379aa7e04ad974d0592 (diff) | |
Input: adp5588-keys - export unused GPIO pins
This patch allows exporting GPIO pins not used by the keypad itself
to be accessible from elsewhere.
Signed-off-by: Xiaolong Chen <xiao-long.chen@motorola.com>
Signed-off-by: Yuanbo Ye <yuan-bo.ye@motorola.com>
Signed-off-by: Tao Hu <taohu@motorola.com>
Acked-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
| -rw-r--r-- | drivers/input/keyboard/adp5588-keys.c | 209 | ||||
| -rw-r--r-- | include/linux/i2c/adp5588.h | 1 |
2 files changed, 208 insertions, 2 deletions
diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 9096db73c3c8..c39ec93c0c58 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
| 20 | #include <linux/input.h> | 20 | #include <linux/input.h> |
| 21 | #include <linux/i2c.h> | 21 | #include <linux/i2c.h> |
| 22 | #include <linux/gpio.h> | ||
| 22 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
| 23 | 24 | ||
| 24 | #include <linux/i2c/adp5588.h> | 25 | #include <linux/i2c/adp5588.h> |
| @@ -54,6 +55,10 @@ | |||
| 54 | 55 | ||
| 55 | #define KEYP_MAX_EVENT 10 | 56 | #define KEYP_MAX_EVENT 10 |
| 56 | 57 | ||
| 58 | #define MAXGPIO 18 | ||
| 59 | #define ADP_BANK(offs) ((offs) >> 3) | ||
| 60 | #define ADP_BIT(offs) (1u << ((offs) & 0x7)) | ||
| 61 | |||
| 57 | /* | 62 | /* |
| 58 | * Early pre 4.0 Silicon required to delay readout by at least 25ms, | 63 | * Early pre 4.0 Silicon required to delay readout by at least 25ms, |
| 59 | * since the Event Counter Register updated 25ms after the interrupt | 64 | * since the Event Counter Register updated 25ms after the interrupt |
| @@ -69,6 +74,14 @@ struct adp5588_kpad { | |||
| 69 | unsigned short keycode[ADP5588_KEYMAPSIZE]; | 74 | unsigned short keycode[ADP5588_KEYMAPSIZE]; |
| 70 | const struct adp5588_gpi_map *gpimap; | 75 | const struct adp5588_gpi_map *gpimap; |
| 71 | unsigned short gpimapsize; | 76 | unsigned short gpimapsize; |
| 77 | #ifdef CONFIG_GPIOLIB | ||
| 78 | unsigned char gpiomap[MAXGPIO]; | ||
| 79 | bool export_gpio; | ||
| 80 | struct gpio_chip gc; | ||
| 81 | struct mutex gpio_lock; /* Protect cached dir, dat_out */ | ||
| 82 | u8 dat_out[3]; | ||
| 83 | u8 dir[3]; | ||
| 84 | #endif | ||
| 72 | }; | 85 | }; |
| 73 | 86 | ||
| 74 | static int adp5588_read(struct i2c_client *client, u8 reg) | 87 | static int adp5588_read(struct i2c_client *client, u8 reg) |
| @@ -86,6 +99,183 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val) | |||
| 86 | return i2c_smbus_write_byte_data(client, reg, val); | 99 | return i2c_smbus_write_byte_data(client, reg, val); |
| 87 | } | 100 | } |
| 88 | 101 | ||
| 102 | #ifdef CONFIG_GPIOLIB | ||
| 103 | static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) | ||
| 104 | { | ||
| 105 | struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); | ||
| 106 | unsigned int bank = ADP_BANK(kpad->gpiomap[off]); | ||
| 107 | unsigned int bit = ADP_BIT(kpad->gpiomap[off]); | ||
| 108 | |||
| 109 | return !!(adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank) & bit); | ||
| 110 | } | ||
| 111 | |||
| 112 | static void adp5588_gpio_set_value(struct gpio_chip *chip, | ||
| 113 | unsigned off, int val) | ||
| 114 | { | ||
| 115 | struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); | ||
| 116 | unsigned int bank = ADP_BANK(kpad->gpiomap[off]); | ||
| 117 | unsigned int bit = ADP_BIT(kpad->gpiomap[off]); | ||
| 118 | |||
| 119 | mutex_lock(&kpad->gpio_lock); | ||
| 120 | |||
| 121 | if (val) | ||
| 122 | kpad->dat_out[bank] |= bit; | ||
| 123 | else | ||
| 124 | kpad->dat_out[bank] &= ~bit; | ||
| 125 | |||
| 126 | adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, | ||
| 127 | kpad->dat_out[bank]); | ||
| 128 | |||
| 129 | mutex_unlock(&kpad->gpio_lock); | ||
| 130 | } | ||
| 131 | |||
| 132 | static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) | ||
| 133 | { | ||
| 134 | struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); | ||
| 135 | unsigned int bank = ADP_BANK(kpad->gpiomap[off]); | ||
| 136 | unsigned int bit = ADP_BIT(kpad->gpiomap[off]); | ||
| 137 | int ret; | ||
| 138 | |||
| 139 | mutex_lock(&kpad->gpio_lock); | ||
| 140 | |||
| 141 | kpad->dir[bank] &= ~bit; | ||
| 142 | ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]); | ||
| 143 | |||
| 144 | mutex_unlock(&kpad->gpio_lock); | ||
| 145 | |||
| 146 | return ret; | ||
| 147 | } | ||
| 148 | |||
| 149 | static int adp5588_gpio_direction_output(struct gpio_chip *chip, | ||
| 150 | unsigned off, int val) | ||
| 151 | { | ||
| 152 | struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); | ||
| 153 | unsigned int bank = ADP_BANK(kpad->gpiomap[off]); | ||
| 154 | unsigned int bit = ADP_BIT(kpad->gpiomap[off]); | ||
| 155 | int ret; | ||
| 156 | |||
| 157 | mutex_lock(&kpad->gpio_lock); | ||
| 158 | |||
| 159 | kpad->dir[bank] |= bit; | ||
| 160 | |||
| 161 | if (val) | ||
| 162 | kpad->dat_out[bank] |= bit; | ||
| 163 | else | ||
| 164 | kpad->dat_out[bank] &= ~bit; | ||
| 165 | |||
| 166 | ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, | ||
| 167 | kpad->dat_out[bank]); | ||
| 168 | ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank, | ||
| 169 | kpad->dir[bank]); | ||
| 170 | |||
| 171 | mutex_unlock(&kpad->gpio_lock); | ||
| 172 | |||
| 173 | return ret; | ||
| 174 | } | ||
| 175 | |||
| 176 | static int __devinit adp5588_gpio_add(struct device *dev) | ||
| 177 | { | ||
| 178 | struct adp5588_kpad *kpad = dev_get_drvdata(dev); | ||
| 179 | const struct adp5588_kpad_platform_data *pdata = dev->platform_data; | ||
| 180 | const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; | ||
| 181 | int i, error; | ||
| 182 | |||
| 183 | if (gpio_data) { | ||
| 184 | int j = 0; | ||
| 185 | bool pin_used[MAXGPIO]; | ||
| 186 | |||
| 187 | for (i = 0; i < pdata->rows; i++) | ||
| 188 | pin_used[i] = true; | ||
| 189 | |||
| 190 | for (i = 0; i < pdata->cols; i++) | ||
| 191 | pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true; | ||
| 192 | |||
| 193 | for (i = 0; i < kpad->gpimapsize; i++) | ||
| 194 | pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true; | ||
| 195 | |||
| 196 | for (i = 0; i < MAXGPIO; i++) { | ||
| 197 | if (!pin_used[i]) | ||
| 198 | kpad->gpiomap[j++] = i; | ||
| 199 | } | ||
| 200 | kpad->gc.ngpio = j; | ||
| 201 | |||
| 202 | if (kpad->gc.ngpio) | ||
| 203 | kpad->export_gpio = true; | ||
| 204 | } | ||
| 205 | |||
| 206 | if (!kpad->export_gpio) { | ||
| 207 | dev_info(dev, "No unused gpios left to export\n"); | ||
| 208 | return 0; | ||
| 209 | } | ||
| 210 | |||
| 211 | kpad->gc.direction_input = adp5588_gpio_direction_input; | ||
| 212 | kpad->gc.direction_output = adp5588_gpio_direction_output; | ||
| 213 | kpad->gc.get = adp5588_gpio_get_value; | ||
| 214 | kpad->gc.set = adp5588_gpio_set_value; | ||
| 215 | kpad->gc.can_sleep = 1; | ||
| 216 | |||
| 217 | kpad->gc.base = gpio_data->gpio_start; | ||
| 218 | kpad->gc.label = kpad->client->name; | ||
| 219 | kpad->gc.owner = THIS_MODULE; | ||
| 220 | |||
| 221 | mutex_init(&kpad->gpio_lock); | ||
| 222 | |||
| 223 | error = gpiochip_add(&kpad->gc); | ||
| 224 | if (error) { | ||
| 225 | dev_err(dev, "gpiochip_add failed, err: %d\n", error); | ||
| 226 | return error; | ||
| 227 | } | ||
| 228 | |||
| 229 | for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { | ||
| 230 | kpad->dat_out[i] = adp5588_read(kpad->client, | ||
| 231 | GPIO_DAT_OUT1 + i); | ||
| 232 | kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i); | ||
| 233 | } | ||
| 234 | |||
| 235 | if (gpio_data->setup) { | ||
| 236 | error = gpio_data->setup(kpad->client, | ||
| 237 | kpad->gc.base, kpad->gc.ngpio, | ||
| 238 | gpio_data->context); | ||
| 239 | if (error) | ||
| 24 | |||
