diff options
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/keyboard/cros_ec_keyb.c | 70 |
1 files changed, 43 insertions, 27 deletions
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 408379669d3c..791781ade4e7 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c | |||
@@ -24,8 +24,8 @@ | |||
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/i2c.h> | 25 | #include <linux/i2c.h> |
26 | #include <linux/input.h> | 26 | #include <linux/input.h> |
27 | #include <linux/interrupt.h> | ||
27 | #include <linux/kernel.h> | 28 | #include <linux/kernel.h> |
28 | #include <linux/notifier.h> | ||
29 | #include <linux/platform_device.h> | 29 | #include <linux/platform_device.h> |
30 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
31 | #include <linux/input/matrix_keypad.h> | 31 | #include <linux/input/matrix_keypad.h> |
@@ -42,7 +42,6 @@ | |||
42 | * @dev: Device pointer | 42 | * @dev: Device pointer |
43 | * @idev: Input device | 43 | * @idev: Input device |
44 | * @ec: Top level ChromeOS device to use to talk to EC | 44 | * @ec: Top level ChromeOS device to use to talk to EC |
45 | * @event_notifier: interrupt event notifier for transport devices | ||
46 | */ | 45 | */ |
47 | struct cros_ec_keyb { | 46 | struct cros_ec_keyb { |
48 | unsigned int rows; | 47 | unsigned int rows; |
@@ -55,7 +54,6 @@ struct cros_ec_keyb { | |||
55 | struct device *dev; | 54 | struct device *dev; |
56 | struct input_dev *idev; | 55 | struct input_dev *idev; |
57 | struct cros_ec_device *ec; | 56 | struct cros_ec_device *ec; |
58 | struct notifier_block notifier; | ||
59 | }; | 57 | }; |
60 | 58 | ||
61 | 59 | ||
@@ -173,41 +171,55 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, | |||
173 | input_sync(ckdev->idev); | 171 | input_sync(ckdev->idev); |
174 | } | 172 | } |
175 | 173 | ||
176 | static int cros_ec_keyb_open(struct input_dev *dev) | ||
177 | { | ||
178 | struct cros_ec_keyb *ckdev = input_get_drvdata(dev); | ||
179 | |||
180 | return blocking_notifier_chain_register(&ckdev->ec->event_notifier, | ||
181 | &ckdev->notifier); | ||
182 | } | ||
183 | |||
184 | static void cros_ec_keyb_close(struct input_dev *dev) | ||
185 | { | ||
186 | struct cros_ec_keyb *ckdev = input_get_drvdata(dev); | ||
187 | |||
188 | blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, | ||
189 | &ckdev->notifier); | ||
190 | } | ||
191 | |||
192 | static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) | 174 | static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) |
193 | { | 175 | { |
194 | return ckdev->ec->command_recv(ckdev->ec, EC_CMD_MKBP_STATE, | 176 | struct cros_ec_command msg = { |
195 | kb_state, ckdev->cols); | 177 | .version = 0, |
178 | .command = EC_CMD_MKBP_STATE, | ||
179 | .outdata = NULL, | ||
180 | .outsize = 0, | ||
181 | .indata = kb_state, | ||
182 | .insize = ckdev->cols, | ||
183 | }; | ||
184 | |||
185 | return ckdev->ec->cmd_xfer(ckdev->ec, &msg); | ||
196 | } | 186 | } |
197 | 187 | ||
198 | static int cros_ec_keyb_work(struct notifier_block *nb, | 188 | static irqreturn_t cros_ec_keyb_irq(int irq, void *data) |
199 | unsigned long state, void *_notify) | ||
200 | { | 189 | { |
190 | struct cros_ec_keyb *ckdev = data; | ||
191 | struct cros_ec_device *ec = ckdev->ec; | ||
201 | int ret; | 192 | int ret; |
202 | struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, | ||
203 | notifier); | ||
204 | uint8_t kb_state[ckdev->cols]; | 193 | uint8_t kb_state[ckdev->cols]; |
205 | 194 | ||
195 | if (device_may_wakeup(ec->dev)) | ||
196 | pm_wakeup_event(ec->dev, 0); | ||
197 | |||
206 | ret = cros_ec_keyb_get_state(ckdev, kb_state); | 198 | ret = cros_ec_keyb_get_state(ckdev, kb_state); |
207 | if (ret >= 0) | 199 | if (ret >= 0) |
208 | cros_ec_keyb_process(ckdev, kb_state, ret); | 200 | cros_ec_keyb_process(ckdev, kb_state, ret); |
201 | else | ||
202 | dev_err(ec->dev, "failed to get keyboard state: %d\n", ret); | ||
209 | 203 | ||
210 | return NOTIFY_DONE; | 204 | return IRQ_HANDLED; |
205 | } | ||
206 | |||
207 | static int cros_ec_keyb_open(struct input_dev *dev) | ||
208 | { | ||
209 | struct cros_ec_keyb *ckdev = input_get_drvdata(dev); | ||
210 | struct cros_ec_device *ec = ckdev->ec; | ||
211 | |||
212 | return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq, | ||
213 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | ||
214 | "cros_ec_keyb", ckdev); | ||
215 | } | ||
216 | |||
217 | static void cros_ec_keyb_close(struct input_dev *dev) | ||
218 | { | ||
219 | struct cros_ec_keyb *ckdev = input_get_drvdata(dev); | ||
220 | struct cros_ec_device *ec = ckdev->ec; | ||
221 | |||
222 | free_irq(ec->irq, ckdev); | ||
211 | } | 223 | } |
212 | 224 | ||
213 | static int cros_ec_keyb_probe(struct platform_device *pdev) | 225 | static int cros_ec_keyb_probe(struct platform_device *pdev) |
@@ -238,8 +250,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) | |||
238 | if (!idev) | 250 | if (!idev) |
239 | return -ENOMEM; | 251 | return -ENOMEM; |
240 | 252 | ||
253 | if (!ec->irq) { | ||
254 | dev_err(dev, "no EC IRQ specified\n"); | ||
255 | return -EINVAL; | ||
256 | } | ||
257 | |||
241 | ckdev->ec = ec; | 258 | ckdev->ec = ec; |
242 | ckdev->notifier.notifier_call = cros_ec_keyb_work; | ||
243 | ckdev->dev = dev; | 259 | ckdev->dev = dev; |
244 | dev_set_drvdata(&pdev->dev, ckdev); | 260 | dev_set_drvdata(&pdev->dev, ckdev); |
245 | 261 | ||