From febeab9b47096c0dce5eaeaceba4a1b8950e563c Mon Sep 17 00:00:00 2001 From: Marc Dietrich Date: Tue, 11 Mar 2014 13:00:38 +0100 Subject: [PATCH 3/6] NOT_FOR_UPSTREAM: staging: nvec: add support for lid and power button events The support is hardcoded in the board specific driver for now until we get proper DT support. Signed-off-by: Marc Dietrich --- drivers/staging/nvec/nvec_paz00.c | 154 +++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 2 deletions(-) diff --git a/drivers/staging/nvec/nvec_paz00.c b/drivers/staging/nvec/nvec_paz00.c index a10385d..732e9ff 100644 --- a/drivers/staging/nvec/nvec_paz00.c +++ b/drivers/staging/nvec/nvec_paz00.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -19,6 +20,7 @@ #include "nvec.h" #define NVEC_LED_MAX 8 +#define NVEC_SYSTEM_EVENT_VAR_LENGTH (0xC5 & 0x8F) enum nvec_oem0_subcmds { EXEC_EC_CMD = 0x10, @@ -28,12 +30,47 @@ enum nvec_oem0_ec_cmds { SET_DEVICE_STATUS = 0x45, }; +enum nvec_sys_ec_cmds { + CONF_EV_REPORTING = 1, +}; + struct nvec_paz00_struct { struct nvec_chip *nvec; struct led_classdev *led_dev; + struct notifier_block notifier; +}; + +struct nvec_paz00_event { + char name[32]; + struct input_dev *dev; + int input_type; + int key_code; + unsigned long status_mask; +}; + +struct nvec_sys_event { + unsigned char command; + unsigned char length; + unsigned long payload; }; -struct nvec_paz00_struct nvec_paz00; +static struct nvec_paz00_struct nvec_paz00; + +static struct nvec_paz00_event nvec_paz00_events[] = { + { + .name = "lid switch", + .input_type = EV_SW, + .key_code = SW_LID, + .status_mask = BIT(1), + }, { + .name = "power key", + .input_type = EV_KEY, + .key_code = KEY_POWER, + .status_mask = BIT(7), + }, { + /* sentinel */ + }, +}; static void nvec_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) @@ -61,20 +98,133 @@ static int paz00_init_leds(struct device *dev) return led_classdev_register(dev, nvec_paz00.led_dev); } +static int nvec_event_notifier(struct notifier_block *nb, + unsigned long event_type, void *data) +{ + struct nvec_sys_event *event = data; + struct nvec_paz00_event *e = nvec_paz00_events; + + if (event_type != NVEC_SYSTEM_EVENT_VAR_LENGTH || + (event->command & (NVEC_VAR_SIZE << 5)) == 0 || + event->length != 4) + return NOTIFY_DONE; + + print_hex_dump(KERN_DEBUG, "payload: ", DUMP_PREFIX_NONE, 16, 1, + &event->command, event->length + 2, false); + + for (; e->name[0]; e++) { + if (e->status_mask & event->payload) { + if (test_bit(EV_KEY, e->dev->evbit)) { + input_report_key(e->dev, e->key_code, 1); + input_sync(e->dev); + input_report_key(e->dev, e->key_code, 0); + } else if (test_bit(EV_SW, e->dev->evbit)) { + input_report_switch(e->dev, e->key_code, 1); + } else { + pr_err("unknown event type\n"); + return NOTIFY_OK; + } + } else if (event->payload == 0) + input_report_switch(e->dev, e->key_code, 0); + + input_sync(e->dev); + } + + return NOTIFY_STOP; +} + +static void nvec_configure_event(struct nvec_chip *nvec, long mask, int state) +{ + char buf[7] = { NVEC_SYS, CONF_EV_REPORTING, state }; + + buf[3] = (mask >> 16) & 0xff; + buf[4] = (mask >> 24) & 0xff; + buf[5] = (mask >> 0) & 0xff; + buf[6] = (mask >> 8) & 0xff; + + nvec_write_async(nvec, buf, 7); +}; + +static int paz00_init_events(struct device *dev) +{ + struct nvec_paz00_event *event = nvec_paz00_events; + int err; + + for (; event->name[0]; event++) { + + event->dev = input_allocate_device(); + if (event->dev == NULL) { + dev_err(dev, "failed to allocate input device\n"); + break; + } + + event->dev->name = event->name; + event->dev->phys = "NVEC"; + event->dev->evbit[0] = BIT_MASK(event->input_type); + + if (event->input_type == EV_KEY) + set_bit(event->key_code, event->dev->keybit); + else if (event->input_type == EV_SW) + set_bit(event->key_code, event->dev->swbit); + else { + dev_err(dev, "unsupported event type %d\n", + event->input_type); + input_free_device(event->dev); + break; + } + + err = input_register_device(event->dev); + if (err) { + dev_err(dev, "failed to register input device (%d)\n", + err); + input_free_device(event->dev); + break; + } + + nvec_configure_event(nvec_paz00.nvec, event->status_mask, 1); + } + + nvec_paz00.notifier.notifier_call = nvec_event_notifier; + nvec_register_notifier(nvec_paz00.nvec, &nvec_paz00.notifier, 0); + + return err; +} + static int nvec_paz00_probe(struct platform_device *pdev) { struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); + int ret; platform_set_drvdata(pdev, &nvec_paz00); nvec_paz00.nvec = nvec; - return paz00_init_leds(&pdev->dev); + ret = paz00_init_leds(&pdev->dev); + if (!ret) + dev_err(&pdev->dev, "error registrating led device %d\n", + ret); + + ret = paz00_init_events(&pdev->dev); + if (!ret) + dev_err(&pdev->dev, "error registrating input device %d\n", + ret); + + return ret; } static int nvec_paz00_remove(struct platform_device *pdev) { + struct nvec_paz00_event *event = nvec_paz00_events; + led_classdev_unregister(nvec_paz00.led_dev); + nvec_unregister_notifier(nvec_paz00.nvec, &nvec_paz00.notifier); + + for (; event->name[0]; event++) { + nvec_configure_event(nvec_paz00.nvec, event->status_mask, 0); + input_unregister_device(event->dev); + input_free_device(event->dev); + } + return 0; } -- 1.8.3.1