diff options
| -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) | ||
| 240 | dev_warn(dev, "setup failed, %d\n", error); | ||
| 241 | } | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | static void __devexit adp5588_gpio_remove(struct device *dev) | ||
| 247 | { | ||
| 248 | struct adp5588_kpad *kpad = dev_get_drvdata(dev); | ||
| 249 | const struct adp5588_kpad_platform_data *pdata = dev->platform_data; | ||
| 250 | const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; | ||
| 251 | int error; | ||
| 252 | |||
| 253 | if (!kpad->export_gpio) | ||
| 254 | return; | ||
| 255 | |||
| 256 | if (gpio_data->teardown) { | ||
| 257 | error = gpio_data->teardown(kpad->client, | ||
| 258 | kpad->gc.base, kpad->gc.ngpio, | ||
| 259 | gpio_data->context); | ||
| 260 | if (error) | ||
| 261 | dev_warn(dev, "teardown failed %d\n", error); | ||
| 262 | } | ||
| 263 | |||
| 264 | error = gpiochip_remove(&kpad->gc); | ||
| 265 | if (error) | ||
| 266 | dev_warn(dev, "gpiochip_remove failed %d\n", error); | ||
| 267 | } | ||
| 268 | #else | ||
| 269 | static inline int adp5588_gpio_add(struct device *dev) | ||
| 270 | { | ||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static inline void adp5588_gpio_remove(struct device *dev) | ||
| 275 | { | ||
| 276 | } | ||
| 277 | #endif | ||
| 278 | |||
| 89 | static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) | 279 | static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) |
| 90 | { | 280 | { |
| 91 | int i, j; | 281 | int i, j; |
| @@ -150,7 +340,8 @@ static irqreturn_t adp5588_irq(int irq, void *handle) | |||
| 150 | 340 | ||
| 151 | static int __devinit adp5588_setup(struct i2c_client *client) | 341 | static int __devinit adp5588_setup(struct i2c_client *client) |
| 152 | { | 342 | { |
| 153 | struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; | 343 | const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; |
| 344 | const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; | ||
| 154 | int i, ret; | 345 | int i, ret; |
| 155 | unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; | 346 | unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; |
| 156 | 347 | ||
| @@ -184,6 +375,15 @@ static int __devinit adp5588_setup(struct i2c_client *client) | |||
| 184 | ret |= adp5588_write(client, GPI_EM3, evt_mode3); | 375 | ret |= adp5588_write(client, GPI_EM3, evt_mode3); |
| 185 | } | 376 | } |
| 186 | 377 | ||
| 378 | if (gpio_data) { | ||
| 379 | for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { | ||
| 380 | int pull_mask = gpio_data->pullup_dis_mask; | ||
| 381 | |||
| 382 | ret |= adp5588_write(client, GPIO_PULL1 + i, | ||
| 383 | (pull_mask >> (8 * i)) & 0xFF); | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 187 | ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT | | 387 | ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT | |
| 188 | OVR_FLOW_INT | K_LCK_INT | | 388 | OVR_FLOW_INT | K_LCK_INT | |
| 189 | GPI_INT | KE_INT); /* Status is W1C */ | 389 | GPI_INT | KE_INT); /* Status is W1C */ |
| @@ -240,7 +440,7 @@ static int __devinit adp5588_probe(struct i2c_client *client, | |||
| 240 | const struct i2c_device_id *id) | 440 | const struct i2c_device_id *id) |
| 241 | { | 441 | { |
| 242 | struct adp5588_kpad *kpad; | 442 | struct adp5588_kpad *kpad; |
| 243 | struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; | 443 | const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; |
| 244 | struct input_dev *input; | 444 | struct input_dev *input; |
| 245 | unsigned int revid; | 445 | unsigned int revid; |
| 246 | int ret, i; | 446 | int ret, i; |
| @@ -381,6 +581,10 @@ static int __devinit adp5588_probe(struct i2c_client *client, | |||
| 381 | if (kpad->gpimapsize) | 581 | if (kpad->gpimapsize) |
| 382 | adp5588_report_switch_state(kpad); | 582 | adp5588_report_switch_state(kpad); |
| 383 | 583 | ||
| 584 | error = adp5588_gpio_add(&client->dev); | ||
| 585 | if (error) | ||
| 586 | goto err_free_irq; | ||
| 587 | |||
| 384 | device_init_wakeup(&client->dev, 1); | 588 | device_init_wakeup(&client->dev, 1); |
| 385 | i2c_set_clientdata(client, kpad); | 589 | i2c_set_clientdata(client, kpad); |
| 386 | 590 | ||
| @@ -407,6 +611,7 @@ static int __devexit adp5588_remove(struct i2c_client *client) | |||
| 407 | free_irq(client->irq, kpad); | 611 | free_irq(client->irq, kpad); |
| 408 | cancel_delayed_work_sync(&kpad->work); | 612 | cancel_delayed_work_sync(&kpad->work); |
| 409 | input_unregister_device(kpad->input); | 613 | input_unregister_device(kpad->input); |
| 614 | adp5588_gpio_remove(&client->dev); | ||
| 410 | kfree(kpad); | 615 | kfree(kpad); |
| 411 | 616 | ||
| 412 | return 0; | 617 | return 0; |
diff --git a/include/linux/i2c/adp5588.h b/include/linux/i2c/adp5588.h index b5f57c498e24..269181b8f623 100644 --- a/include/linux/i2c/adp5588.h +++ b/include/linux/i2c/adp5588.h | |||
| @@ -123,6 +123,7 @@ struct adp5588_kpad_platform_data { | |||
| 123 | unsigned short unlock_key2; /* Unlock Key 2 */ | 123 | unsigned short unlock_key2; /* Unlock Key 2 */ |
| 124 | const struct adp5588_gpi_map *gpimap; | 124 | const struct adp5588_gpi_map *gpimap; |
| 125 | unsigned short gpimapsize; | 125 | unsigned short gpimapsize; |
| 126 | const struct adp5588_gpio_platform_data *gpio_data; | ||
| 126 | }; | 127 | }; |
| 127 | 128 | ||
| 128 | struct adp5588_gpio_platform_data { | 129 | struct adp5588_gpio_platform_data { |
