/* * Driver for keys on GPIO lines capable of generating interrupts. * * Copyright 2005 Phil Blundell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern struct class *sec_class; struct gpio_button_data { struct gpio_keys_button *button; struct input_dev *input; struct timer_list timer; struct work_struct work; int timer_debounce; /* in msecs */ bool disabled; bool key_state; bool wakeup; }; struct gpio_keys_drvdata { struct input_dev *input; struct device *sec_key; struct mutex disable_lock; unsigned int n_buttons; int (*enable)(struct device *dev); void (*disable)(struct device *dev); #ifdef CONFIG_SENSORS_HALL int gpio_flip_cover; bool flip_cover; struct delayed_work flip_cover_dwork; #endif struct gpio_button_data data[0]; /* WARNING: this area can be expanded. Do NOT add any member! */ }; #ifdef CONFIG_SENSORS_HALL int flip_cover_open; extern ts_powered_on; #endif /* * SYSFS interface for enabling/disabling keys and switches: * * There are 4 attributes under /sys/devices/platform/gpio-keys/ * keys [ro] - bitmap of keys (EV_KEY) which can be * disabled * switches [ro] - bitmap of switches (EV_SW) which can be * disabled * disabled_keys [rw] - bitmap of keys currently disabled * disabled_switches [rw] - bitmap of switches currently disabled * * Userland can change these values and hence disable event generation * for each key (or switch). Disabling a key means its interrupt line * is disabled. * * For example, if we have following switches set up as gpio-keys: * SW_DOCK = 5 * SW_CAMERA_LENS_COVER = 9 * SW_KEYPAD_SLIDE = 10 * SW_FRONT_PROXIMITY = 11 * This is read from switches: * 11-9,5 * Next we want to disable proximity (11) and dock (5), we write: * 11,5 * to file disabled_switches. Now proximity and dock IRQs are disabled. * This can be verified by reading the file disabled_switches: * 11,5 * If we now want to enable proximity (11) switch we write: * 5 * to disabled_switches. * * We can disable only those keys which don't allow sharing the irq. */ /** * get_n_events_by_type() - returns maximum number of events per @type * @type: type of button (%EV_KEY, %EV_SW) * * Return value of this function can be used to allocate bitmap * large enough to hold all bits for given type. */ static inline int get_n_events_by_type(int type) { BUG_ON(type != EV_SW && type != EV_KEY); return (type == EV_KEY) ? KEY_CNT : SW_CNT; } /** * gpio_keys_disable_button() - disables given GPIO button * @bdata: button data for button to be disabled * * Disables button pointed by @bdata. This is done by masking * IRQ line. After this function is called, button won't generate * input events anymore. Note that one can only disable buttons * that don't share IRQs. * * Make sure that @bdata->disable_lock is locked when entering * this function to avoid races when concurrent threads are * disabling buttons at the same time. */ static void gpio_keys_disable_button(struct gpio_button_data *bdata) { if (!bdata->disabled) { /* * Disable IRQ and possible debouncing timer. */ disable_irq(gpio_to_irq(bdata->button->gpio)); if (bdata->timer_debounce) del_timer_sync(&bdata->timer); bdata->disabled = true; } } /** * gpio_keys_enable_button() - enables given GPIO button * @bdata: button data for button to be disabled * * Enables given button pointed by @bdata. * * Make sure that @bdata->disable_lock is locked when entering * this function to avoid races with concurrent threads trying * to enable the same button at the same time. */ static void gpio_keys_enable_button(struct gpio_button_data *bdata) { if (bdata->disabled) { enable_irq(gpio_to_irq(bdata->button->gpio)); bdata->disabled = false; } } /** * gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons * @ddata: pointer to drvdata * @buf: buffer where stringified bitmap is written * @type: button type (%EV_KEY, %EV_SW) * @only_disabled: does caller want only those buttons that are * currently disabled or all buttons that can be * disabled * * This function writes buttons that can be disabled to @buf. If * @only_disabled is true, then @buf contains only those buttons * that are currently disabled. Returns 0 on success or negative * errno on failure. */ static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata, char *buf, unsigned int type, bool only_disabled) { int n_events = get_n_events_by_type(type); unsigned long *bits; ssize_t ret; int i; bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); if (!bits) return -ENOMEM; for (i = 0; i < ddata->n_buttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->button->type != type) continue; if (only_disabled && !bdata->disabled) continue; __set_bit(bdata->button->code, bits); } ret = bitmap_scnlistprintf(buf, PAGE_SIZE - 2, bits, n_events); buf[ret++] = '\n'; buf[ret] = '\0'; kfree(bits); return ret; } /** * gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap * @ddata: pointer to drvdata * @buf: buffer from userspace that contains stringified bitmap * @type: button type (%EV_KEY, %EV_SW) * * This function parses stringified bitmap from @buf and disables/enables * GPIO buttons accordinly. Returns 0 on success and negative error * on failure. */ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, const char *buf, unsigned int type) { int n_events = get_n_events_by_type(type); unsigned long *bits; ssize_t error; int i; bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); if (!bits) return -ENOMEM; error = bitmap_parselist(buf, bits, n_events); if (error) goto out; /* First validate */ for (i = 0; i < ddata->n_buttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->button->type != type) continue; if (test_bit(bdata->button->code, bits) && !bdata->button->can_disable) { error = -EINVAL; goto out; } } mutex_lock(&ddata->disable_lock); for (i = 0; i < ddata->n_buttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; if (bdata->button->type != type) continue; if (test_bit(bdata->button->code, bits)) gpio_keys_disable_button(bdata); else gpio_keys_enable_button(bdata); } mutex_unlock(&ddata->disable_lock); out: kfree(bits); return error; } #define ATTR_SHOW_FN(name, type, only_disabled) \ static ssize_t gpio_keys_show_##name(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct platform_device *pdev = to_platform_device(dev); \ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ \ return gpio_keys_attr_show_helper(ddata, buf, \ type, only_disabled); \ } ATTR_SHOW_FN(keys, EV_KEY, false); ATTR_SHOW_FN(switches, EV_SW, false); ATTR_SHOW_FN(disabled_keys, EV_KEY, true); ATTR_SHOW_FN(disabled_switches, EV_SW, true); /* * ATTRIBUTES: * * /sys/devices/platform/gpio-keys/keys [ro] * /sys/devices/platform/gpio-keys/switches [ro] */ static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL); static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL); #define ATTR_STORE_FN(name, type) \ static ssize_t gpio_keys_store_##name(struct device *dev, \ struct device_attribute *attr, \ const char *buf, \ size_t count) \ { \ struct platform_device *pdev = to_platform_device(dev); \ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); \ ssize_t error; \ \ error = gpio_keys_attr_store_helper(ddata, buf, type); \ if (error) \ return error; \ \ return count; \ } ATTR_STORE_FN(disabled_keys, EV_KEY); ATTR_STORE_FN(disabled_switches, EV_SW); /* * ATTRIBUTES: * * /sys/devices/platform/gpio-keys/disabled_keys [rw] * /sys/devices/platform/gpio-keys/disables_switches [rw] */ static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO, gpio_keys_show_disabled_keys, gpio_keys_store_disabled_keys); static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO, gpio_keys_show_disabled_switches, gpio_keys_store_disabled_switches); static struct attribute *gpio_keys_attrs[] = { &dev_attr_keys.attr, &dev_attr_switches.attr, &dev_attr_disabled_keys.attr, &dev_attr_disabled_switches.attr, NULL, }; static struct attribute_group gpio_keys_attr_group = { .attrs = gpio_keys_attrs, }; static ssize_t key_pressed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); int i; int keystate = 0; for (i = 0; i < ddata->n_buttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; keystate |= bdata->key_state; } if (keystate) sprintf(buf, "PRESS"); else sprintf(buf, "RELEASE"); return strlen(buf); } /* the volume keys can be the wakeup keys in special case */ static ssize_t wakeup_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); int n_events = get_n_events_by_type(EV_KEY); unsigned long *bits; ssize_t error; int i; bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL); if (!bits) return -ENOMEM; error = bitmap_parselist(buf, bits, n_events); if (error) goto out; for (i = 0; i < ddata->n_buttons; i++) { struct gpio_button_data *button = &ddata->data[i]; if (test_bit(button->button->code, bits)) button->button->wakeup = 1; else button->button->wakeup = 0; } out: kfree(bits); return count; } #ifdef CONFIG_SENSORS_HALL static ssize_t hall_detect_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); if (ddata->flip_cover){ printk("%s: OPEN",__func__); sprintf(buf, "OPEN"); }else{ printk("%s: CLOSE",__func__); sprintf(buf, "CLOSE"); } return strlen(buf); } #endif static DEVICE_ATTR(sec_key_pressed, 0664, key_pressed_show, NULL); static DEVICE_ATTR(wakeup_keys, 0664, NULL, wakeup_enable); #ifdef CONFIG_SENSORS_HALL static DEVICE_ATTR(hall_detect, 0664, hall_detect_show, NULL); #endif static struct attribute *sec_key_attrs[] = { &dev_attr_sec_key_pressed.attr, &dev_attr_wakeup_keys.attr, #ifdef CONFIG_SENSORS_HALL &dev_attr_hall_detect.attr, #endif NULL, }; static struct attribute_group sec_key_attr_group = { .attrs = sec_key_attrs, }; #ifdef CONFIG_MACH_GC1 void gpio_keys_check_zoom_exception(unsigned int code, bool *zoomkey, unsigned int *hotkey, unsigned int *index) { switch (code) { case KEY_CAMERA_ZOOMIN: *hotkey = 0x221; *index = 5; break; case KEY_CAMERA_ZOOMOUT: *hotkey = 0x222; *index = 6; break; case 0x221: *hotkey = KEY_CAMERA_ZOOMIN; *index = 3; break; case 0x222: *hotkey = KEY_CAMERA_ZOOMOUT; *index = 4; break; default: *zoomkey = false; return; } *zoomkey = true; } #endif #ifdef CONFIG_FAST_BOOT extern bool fake_shut_down; struct timer_list fake_timer; bool fake_pressed; static void gpio_keys_fake_off_check(unsigned long _data) { struct input_dev *input = (struct input_dev *)_data; unsigned int type = EV_KEY; if (fake_pressed == false) return ; printk(KERN_DEBUG"[Keys] make event\n"); input_event(input, type, KEY_FAKE_PWR, 1); input_sync(input); input_event(input, type, KEY_FAKE_PWR, 0); input_sync(input); } #endif static void gpio_keys_report_event(struct gpio_button_data *bdata) { struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; #ifdef CONFIG_MACH_GC1 struct gpio_keys_drvdata *ddata = input_get_drvdata(input); struct gpio_button_data *tmp_bdata; static bool overlapped; static unsigned int hotkey; unsigned int index_hotkey = 0; bool zoomkey = false; #ifdef CONFIG_FAST_BOOT /*Fake pwr off control*/ if (fake_shut_down) { if (button->code == KEY_POWER) { if (!!state) { printk(KERN_DEBUG"[Keys] start fake check\n"); fake_pressed = true; mod_timer(&fake_timer, jiffies + msecs_to_jiffies(1000)); } else { printk(KERN_DEBUG"[Keys] end fake checkPwr 0\n"); fake_pressed = false; } } return ; } #endif if (system_rev < 6 && system_rev >= 2) { if (overlapped) { if (hotkey == button->code && !state) { bdata->key_state = !!state; bdata->wakeup = false; overlapped = false; #ifdef CONFIG_SAMSUNG_PRODUCT_SHIP printk(KERN_DEBUG"[KEYS] Ignored\n"); #else printk(KERN_DEBUG"[KEYS] Ignore %d %d\n", hotkey, state); #endif return; } } gpio_keys_check_zoom_exception(button->code, &zoomkey, &hotkey, &index_hotkey); } #endif if (type == EV_ABS) { if (state) { input_event(input, type, button->code, button->value); input_sync(input); } } else { if (bdata->wakeup && !state) { input_event(input, type, button->code, !state); input_sync(input); if (button->code == KEY_POWER) printk(KERN_DEBUG"[keys] f PWR %d\n", !state); } bdata->key_state = !!state; bdata->wakeup = false; #ifdef CONFIG_MACH_GC1 if (system_rev < 6 && system_rev >= 2 && zoomkey && state) { tmp_bdata = &ddata->data[index_hotkey]; if (tmp_bdata->key_state) { #ifdef CONFIG_SAMSUNG_PRODUCT_SHIP printk(KERN_DEBUG"[KEYS] overlapped\n"); #else printk(KERN_DEBUG"[KEYS] overlapped. Forced release c %d h %d\n", tmp_bdata->button->code, hotkey); #endif input_event(input, type, hotkey, 0); input_sync(input); overlapped = true; } } #endif #ifdef CONFIG_SENSORS_HALL if(!flip_cover_open && button->code == KEY_POWER){ printk(KERN_DEBUG" cover closed...ignoring PWR button"); }else{ #endif input_event(input, type, button->code, !!state); input_sync(input); #ifdef CONFIG_SENSORS_HALL } #endif if (button->code == KEY_POWER) printk(KERN_DEBUG"[keys]PWR %d\n", !!state); } } static void gpio_keys_work_func(struct work_struct *work) { struct gpio_button_data *bdata = container_of(work, struct gpio_button_data, work); gpio_keys_report_event(bdata); } static void gpio_keys_timer(unsigned long _data) { struct gpio_button_data *data = (struct gpio_button_data *)_data; schedule_work(&data->work); } static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; struct gpio_keys_button *button = bdata->button; struct irq_desc *desc = irq_to_desc(irq); int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; BUG_ON(irq != gpio_to_irq(button->gpio)); if (desc) { if (0 < desc->wake_depth) { bdata->wakeup = true; printk(KERN_DEBUG"[keys] in the sleep\n"); } } if (bdata->timer_debounce) mod_timer(&bdata->timer, jiffies + msecs_to_jiffies(bdata->timer_debounce)); else schedule_work(&bdata->work); if (button->isr_hook) button->isr_hook(button->code, state); return IRQ_HANDLED; } static int __devinit gpio_keys_setup_key(struct platform_device *pdev, struct gpio_button_data *bdata, struct gpio_keys_button *button) { const char *desc = button->desc ? button->desc : "gpio_keys"; struct device *dev = &pdev->dev; unsigned long irqflags; int irq, error; setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata); INIT_WORK(&bdata->work, gpio_keys_work_func); error = gpio_request(button->gpio, desc); if (error < 0) { dev_err(dev, "failed to request GPIO %d, error %d\n", button->gpio, error); goto fail2; } error = gpio_direction_input(button->gpio); if (error < 0) { dev_err(dev, "failed to configure" " direction for GPIO %d, error %d\n", button->gpio, error); goto fail3; } if (button->debounce_interval) { error = gpio_set_debounce(button->gpio, button->debounce_interval * 1000); /* use timer if gpiolib doesn't provide debounce */ if (error < 0) bdata->timer_debounce = button->debounce_interval; } irq = gpio_to_irq(button->gpio); if (irq < 0) { error = irq; dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n", button->gpio, error); goto fail3; } irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; /* * If platform has specified that the button can be disabled, * we don't want it to share the interrupt line. */ if (!button->can_disable) irqflags |= IRQF_SHARED; if (button->wakeup) irqflags |= IRQF_NO_SUSPEND; error = request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata); if (error < 0) { dev_err(dev, "Unable to claim irq %d; error %d\n", irq, error); goto fail3; } return 0; fail3: gpio_free(button->gpio); fail2: return error; } #ifdef CONFIG_SENSORS_HALL static void flip_cover_work(struct work_struct *work) { struct gpio_keys_drvdata *ddata = container_of(work, struct gpio_keys_drvdata, flip_cover_dwork.work); ddata->flip_cover = gpio_get_value(ddata->gpio_flip_cover); printk(KERN_DEBUG "[keys] %s : %d\n", __func__, ddata->flip_cover); /* input_report_switch(ddata->input, SW_FLIP, ddata->flip_cover); input_sync(ddata->input);*/ flip_cover_open = ddata->flip_cover; if(!ts_powered_on && !ddata->flip_cover){ printk("screen already off\n"); }else{ input_report_key(ddata->input, KEY_POWER, 1); input_sync(ddata->input); input_report_key(ddata->input, KEY_POWER, 0); input_sync(ddata->input); } } static irqreturn_t flip_cover_detect(int irq, void *dev_id) { struct gpio_keys_drvdata *ddata = dev_id; cancel_delayed_work_sync(&ddata->flip_cover_dwork); schedule_delayed_work(&ddata->flip_cover_dwork, HZ / 20); return IRQ_HANDLED; } #endif static int gpio_keys_open(struct input_dev *input) { struct gpio_keys_drvdata *ddata = input_get_drvdata(input); #ifdef CONFIG_SENSORS_HALL int ret = 0; int irq = gpio_to_irq(ddata->gpio_flip_cover); INIT_DELAYED_WORK(&ddata->flip_cover_dwork, flip_cover_work); ret = request_threaded_irq( irq, NULL, flip_cover_detect, IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "flip_cover", ddata); if (ret) { printk(KERN_ERR "keys: failed to request flip cover irq %d gpio %d\n", irq, ddata->gpio_flip_cover); goto hall_sensor_error; } ret = enable_irq_wake(irq); if (ret < 0) { printk(KERN_ERR "keys: Failed to Enable Wakeup Source(%d) \n", ret); goto hall_sensor_error; } /* update the current status */ schedule_delayed_work(&ddata->flip_cover_dwork, HZ / 2); hall_sensor_error: #endif return ddata->enable ? ddata->enable(input->dev.parent) : 0; } static void gpio_keys_close(struct input_dev *input) { struct gpio_keys_drvdata *ddata = input_get_drvdata(input); if (ddata->disable) ddata->disable(input->dev.parent); } static int __devinit gpio_keys_probe(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_drvdata *ddata; struct device *dev = &pdev->dev; struct input_dev *input; int i, error; int wakeup = 0; ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + pdata->nbuttons * sizeof(struct gpio_button_data), GFP_KERNEL); input = input_allocate_device(); if (!ddata || !input) { dev_err(dev, "failed to allocate state\n"); error = -ENOMEM; goto fail1; } ddata->input = input; ddata->n_buttons = pdata->nbuttons; ddata->enable = pdata->enable; ddata->disable = pdata->disable; #ifdef CONFIG_SENSORS_HALL ddata->gpio_flip_cover = pdata->gpio_flip_cover; #endif mutex_init(&ddata->disable_lock); platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); input->name = pdata->name ? : pdev->name; input->phys = "gpio-keys/input0"; input->dev.parent = &pdev->dev; /*#ifdef CONFIG_SENSORS_HALL input->evbit[0] |= BIT_MASK(EV_SW); input_set_capability(input, EV_SW, SW_FLIP); #endif*/ input->open = gpio_keys_open; input->close = gpio_keys_close; input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; /* Enable auto repeat feature of Linux input subsystem */ if (pdata->rep) __set_bit(EV_REP, input->evbit); for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_button_data *bdata = &ddata->data[i]; unsigned int type = button->type ?: EV_KEY; bdata->input = input; bdata->button = button; error = gpio_keys_setup_key(pdev, bdata, button); if (error) goto fail2; if (button->wakeup) wakeup = 1; input_set_capability(input, type, button->code); } error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); if (error) { dev_err(dev, "Unable to export keys/switches, error: %d\n", error); goto fail2; } ddata->sec_key = device_create(sec_class, NULL, 0, ddata, "sec_key"); if (IS_ERR(ddata->sec_key)) dev_err(dev, "Failed to create sec_key device\n"); error = sysfs_create_group(&ddata->sec_key->kobj, &sec_key_attr_group); if (error) { dev_err(dev, "Failed to create the test sysfs: %d\n", error); goto fail2; } error = input_register_device(input); if (error) { dev_err(dev, "Unable to register input device, error: %d\n", error); goto fail3; } /* get current state of buttons */ for (i = 0; i < pdata->nbuttons; i++) gpio_keys_report_event(&ddata->data[i]); input_sync(input); #ifdef CONFIG_FAST_BOOT /*Fake power off*/ input_set_capability(input, EV_KEY, KEY_FAKE_PWR); setup_timer(&fake_timer, gpio_keys_fake_off_check, (unsigned long)input); #endif device_init_wakeup(&pdev->dev, wakeup); return 0; fail3: sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); sysfs_remove_group(&ddata->sec_key->kobj, &sec_key_attr_group); fail2: while (--i >= 0) { free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); gpio_free(pdata->buttons[i].gpio); } platform_set_drvdata(pdev, NULL); fail1: input_free_device(input); kfree(ddata); return error; } static int __devexit gpio_keys_remove(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); struct input_dev *input = ddata->input; int i; sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); device_init_wakeup(&pdev->dev, 0); for (i = 0; i < pdata->nbuttons; i++) { int irq = gpio_to_irq(pdata->buttons[i].gpio); free_irq(irq, &ddata->data[i]); if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); gpio_free(pdata->buttons[i].gpio); } input_unregister_device(input); return 0; } #ifdef CONFIG_PM static int gpio_keys_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; int i; if (device_may_wakeup(&pdev->dev)) { for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; if (button->wakeup) { int irq = gpio_to_irq(button->gpio); enable_irq_wake(irq); } } } return 0; } static int gpio_keys_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; int i; for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; if (button->wakeup && device_may_wakeup(&pdev->dev)) { int irq = gpio_to_irq(button->gpio); disable_irq_wake(irq); } gpio_keys_report_event(&ddata->data[i]); } input_sync(ddata->input); return 0; } static const struct dev_pm_ops gpio_keys_pm_ops = { .suspend = gpio_keys_suspend, .resume = gpio_keys_resume, }; #endif static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, .remove = __devexit_p(gpio_keys_remove), .driver = { .name = "gpio-keys", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &gpio_keys_pm_ops, #endif } }; static int __init gpio_keys_init(void) { return platform_driver_register(&gpio_keys_device_driver); } static void __exit gpio_keys_exit(void) { platform_driver_unregister(&gpio_keys_device_driver); } module_init(gpio_keys_init); module_exit(gpio_keys_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Phil Blundell "); MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs"); MODULE_ALIAS("platform:gpio-keys");