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 /drivers/input/keyboard/adp5588-keys.c | |
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>
Diffstat (limited to 'drivers/input/keyboard/adp5588-keys.c')
-rw-r--r-- | drivers/input/keyboard/adp5588-keys.c | 209 |
1 files changed, 207 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; |