X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=package%2Fkernel%2Fgpio-button-hotplug%2Fsrc%2Fgpio-button-hotplug.c;h=fcaf7f59de5c0e855ea8567867d8b520560a2ba7;hb=6fe4b7aa2b00b4e93871454e6a6fc41eb192c784;hp=0b3140c0bff45e8e4c1f80c7f371ea9e7721f463;hpb=6988613f7ae628f14d6b25add7054ebb6a0364a5;p=openwrt%2Fopenwrt.git diff --git a/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c b/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c index 0b3140c0bf..fcaf7f59de 100644 --- a/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c +++ b/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c @@ -1,12 +1,12 @@ /* * GPIO Button Hotplug driver * - * Copyright (C) 2012 Felix Fietkau + * Copyright (C) 2012 Felix Fietkau * Copyright (C) 2008-2010 Gabor Juhos * * Based on the diag.c - GPIO interface driver for Broadcom boards * Copyright (C) 2006 Mike Baker , - * Copyright (C) 2006-2007 Felix Fietkau + * Copyright (C) 2006-2007 Felix Fietkau * Copyright (C) 2008 Andy Boyett * * This program is free software; you can redistribute it and/or modify it @@ -23,30 +23,18 @@ #include #include #include +#include #include #include +#include #include - -#define DRV_NAME "gpio-keys-polled" +#include #define BH_SKB_SIZE 2048 +#define DRV_NAME "gpio-keys" #define PFX DRV_NAME ": " -#undef BH_DEBUG - -#ifdef BH_DEBUG -#define BH_DBG(fmt, args...) printk(KERN_DEBUG "%s: " fmt, DRV_NAME, ##args ) -#else -#define BH_DBG(fmt, args...) do {} while (0) -#endif - -#define BH_ERR(fmt, args...) printk(KERN_ERR "%s: " fmt, DRV_NAME, ##args ) - -struct bh_priv { - unsigned long seen; -}; - struct bh_event { const char *name; unsigned int type; @@ -64,11 +52,16 @@ struct bh_map { struct gpio_keys_button_data { struct delayed_work work; - struct bh_priv bh; + unsigned long seen; + int map_entry; int last_state; int count; int threshold; int can_sleep; + int irq; + unsigned int software_debounce; + struct gpio_desc *gpiod; + const struct gpio_keys_button *b; }; extern u64 uevent_next_seqnum(void); @@ -80,25 +73,39 @@ extern u64 uevent_next_seqnum(void); } static struct bh_map button_map[] = { - BH_MAP(BTN_0, "BTN_0"), - BH_MAP(BTN_1, "BTN_1"), - BH_MAP(BTN_2, "BTN_2"), - BH_MAP(BTN_3, "BTN_3"), - BH_MAP(BTN_4, "BTN_4"), - BH_MAP(BTN_5, "BTN_5"), - BH_MAP(BTN_6, "BTN_6"), - BH_MAP(BTN_7, "BTN_7"), - BH_MAP(BTN_8, "BTN_8"), - BH_MAP(BTN_9, "BTN_9"), - BH_MAP(KEY_RESTART, "reset"), - BH_MAP(KEY_RFKILL, "rfkill"), - BH_MAP(KEY_WPS_BUTTON, "wps"), + BH_MAP(BTN_0, "BTN_0"), + BH_MAP(BTN_1, "BTN_1"), + BH_MAP(BTN_2, "BTN_2"), + BH_MAP(BTN_3, "BTN_3"), + BH_MAP(BTN_4, "BTN_4"), + BH_MAP(BTN_5, "BTN_5"), + BH_MAP(BTN_6, "BTN_6"), + BH_MAP(BTN_7, "BTN_7"), + BH_MAP(BTN_8, "BTN_8"), + BH_MAP(BTN_9, "BTN_9"), + BH_MAP(KEY_BRIGHTNESS_ZERO, "brightness_zero"), + BH_MAP(KEY_CONFIG, "config"), + BH_MAP(KEY_COPY, "copy"), + BH_MAP(KEY_EJECTCD, "eject"), + BH_MAP(KEY_HELP, "help"), + BH_MAP(KEY_LIGHTS_TOGGLE, "lights_toggle"), + BH_MAP(KEY_PHONE, "phone"), + BH_MAP(KEY_POWER, "power"), + BH_MAP(KEY_POWER2, "reboot"), + BH_MAP(KEY_RESTART, "reset"), + BH_MAP(KEY_RFKILL, "rfkill"), + BH_MAP(KEY_VIDEO, "video"), + BH_MAP(KEY_VOLUMEDOWN, "volume_down"), + BH_MAP(KEY_VOLUMEUP, "volume_up"), + BH_MAP(KEY_WIMAX, "wwan"), + BH_MAP(KEY_WLAN, "wlan"), + BH_MAP(KEY_WPS_BUTTON, "wps"), }; /* -------------------------------------------------------------------------*/ -static int bh_event_add_var(struct bh_event *event, int argv, - const char *format, ...) +static __printf(3, 4) +int bh_event_add_var(struct bh_event *event, int argv, const char *format, ...) { static char buf[128]; char *s; @@ -113,15 +120,14 @@ static int bh_event_add_var(struct bh_event *event, int argv, va_end(args); if (len >= sizeof(buf)) { - BH_ERR("buffer size too small\n"); - WARN_ON(1); + WARN(1, "buffer size too small"); return -ENOMEM; } s = skb_put(event->skb, len + 1); strcpy(s, buf); - BH_DBG("added variable '%s'\n", s); + pr_debug(PFX "added variable '%s'\n", s); return 0; } @@ -139,20 +145,7 @@ static int button_hotplug_fill_event(struct bh_event *event) if (ret) return ret; - char *s; - switch (event->type) { - case EV_KEY: - s = "button"; - break; - case EV_SW: - s = "switch"; - break; - default: - s = "button"; - break; - } - - ret = bh_event_add_var(event, 0, "SUBSYSTEM=%s", s); + ret = bh_event_add_var(event, 0, "SUBSYSTEM=%s", "button"); if (ret) return ret; @@ -164,6 +157,12 @@ static int button_hotplug_fill_event(struct bh_event *event) if (ret) return ret; + if (event->type == EV_SW) { + ret = bh_event_add_var(event, 0, "TYPE=%s", "switch"); + if (ret) + return ret; + } + ret = bh_event_add_var(event, 0, "SEEN=%ld", event->seen); if (ret) return ret; @@ -195,7 +194,7 @@ static void button_hotplug_work(struct work_struct *work) out_free_skb: if (ret) { - BH_ERR("work error %d\n", ret); + pr_err(PFX "work error %d\n", ret); kfree_skb(event->skb); } out_free_event: @@ -207,8 +206,8 @@ static int button_hotplug_create_event(const char *name, unsigned int type, { struct bh_event *event; - BH_DBG("create event, name=%s, seen=%lu, pressed=%d\n", - name, seen, pressed); + pr_debug(PFX "create event, name=%s, seen=%lu, pressed=%d\n", + name, seen, pressed); event = kzalloc(sizeof(*event), GFP_KERNEL); if (!event) @@ -227,7 +226,6 @@ static int button_hotplug_create_event(const char *name, unsigned int type, /* -------------------------------------------------------------------------*/ -#ifdef CONFIG_HOTPLUG static int button_get_index(unsigned int code) { int i; @@ -239,34 +237,66 @@ static int button_get_index(unsigned int code) return -1; } -static void button_hotplug_event(struct gpio_keys_button_data *data, - unsigned int type, unsigned int code, int value) +static int gpio_button_get_value(struct gpio_keys_button_data *bdata) { - struct bh_priv *priv = &data->bh; - unsigned long seen = jiffies; - int btn; + int val; - BH_DBG("event type=%u, code=%u, value=%d\n", type, code, value); + if (bdata->can_sleep) + val = !!gpiod_get_value_cansleep(bdata->gpiod); + else + val = !!gpiod_get_value(bdata->gpiod); - if ((type != EV_KEY) && (type != EV_SW)) + return val; +} + +static void gpio_keys_handle_button(struct gpio_keys_button_data *bdata) +{ + unsigned int type = bdata->b->type ?: EV_KEY; + int state = gpio_button_get_value(bdata); + unsigned long seen = jiffies; + + pr_debug(PFX "event type=%u, code=%u, pressed=%d\n", + type, bdata->b->code, state); + + /* is this the initialization state? */ + if (bdata->last_state == -1) { + /* + * Don't advertise unpressed buttons on initialization. + * Just save their state and continue otherwise this + * can cause OpenWrt to enter failsafe. + */ + if (type == EV_KEY && state == 0) + goto set_state; + /* + * But we are very interested in pressed buttons and + * initial switch state. These will be reported to + * userland. + */ + } else if (bdata->last_state == state) { + /* reset asserted counter (only relevant for polled keys) */ + bdata->count = 0; return; + } - btn = button_get_index(code); - if (btn < 0) + if (bdata->count < bdata->threshold) { + bdata->count++; return; + } - button_hotplug_create_event(button_map[btn].name, type, - (seen - priv->seen) / HZ, value); - priv->seen = seen; -} -#else -static void button_hotplug_event(struct gpio_keys_button_data *data, - unsigned int type, unsigned int code, int value) -{ + if (bdata->seen == 0) + bdata->seen = seen; + + button_hotplug_create_event(button_map[bdata->map_entry].name, type, + (seen - bdata->seen) / HZ, state); + bdata->seen = seen; + +set_state: + bdata->last_state = state; + bdata->count = 0; } -#endif /* CONFIG_HOTPLUG */ -struct gpio_keys_polled_dev { +struct gpio_keys_button_dev { + int polled; struct delayed_work work; struct device *dev; @@ -274,81 +304,69 @@ struct gpio_keys_polled_dev { struct gpio_keys_button_data data[0]; }; -static void gpio_keys_polled_check_state(struct gpio_keys_button *button, - struct gpio_keys_button_data *bdata) -{ - int state; - - if (bdata->can_sleep) - state = !!gpio_get_value_cansleep(button->gpio); - else - state = !!gpio_get_value(button->gpio); - - state = !!(state ^ button->active_low); - if (state != bdata->last_state) { - unsigned int type = button->type ?: EV_KEY; - - if (bdata->count < bdata->threshold) { - bdata->count++; - return; - } - - button_hotplug_event(bdata, type, button->code, state); - bdata->last_state = state; - } - - bdata->count = 0; -} - -static void gpio_keys_polled_queue_work(struct gpio_keys_polled_dev *bdev) +static void gpio_keys_polled_queue_work(struct gpio_keys_button_dev *bdev) { struct gpio_keys_platform_data *pdata = bdev->pdata; unsigned long delay = msecs_to_jiffies(pdata->poll_interval); if (delay >= HZ) - delay = round_jiffies_relative(delay); + delay = round_jiffies_relative(delay); schedule_delayed_work(&bdev->work, delay); } static void gpio_keys_polled_poll(struct work_struct *work) { - struct gpio_keys_polled_dev *bdev = - container_of(work, struct gpio_keys_polled_dev, work.work); - struct gpio_keys_platform_data *pdata = bdev->pdata; + struct gpio_keys_button_dev *bdev = + container_of(work, struct gpio_keys_button_dev, work.work); int i; for (i = 0; i < bdev->pdata->nbuttons; i++) { struct gpio_keys_button_data *bdata = &bdev->data[i]; - gpio_keys_polled_check_state(&pdata->buttons[i], bdata); + + if (bdata->gpiod) + gpio_keys_handle_button(bdata); } gpio_keys_polled_queue_work(bdev); } -static void gpio_keys_polled_open(struct gpio_keys_polled_dev *bdev) +static void gpio_keys_polled_close(struct gpio_keys_button_dev *bdev) { struct gpio_keys_platform_data *pdata = bdev->pdata; - int i; - if (pdata->enable) - pdata->enable(bdev->dev); + cancel_delayed_work_sync(&bdev->work); - /* report initial state of the buttons */ - for (i = 0; i < pdata->nbuttons; i++) - gpio_keys_polled_check_state(&pdata->buttons[i], &bdev->data[i]); + if (pdata->disable) + pdata->disable(bdev->dev); +} - gpio_keys_polled_queue_work(bdev); +static void gpio_keys_irq_work_func(struct work_struct *work) +{ + struct gpio_keys_button_data *bdata = container_of(work, + struct gpio_keys_button_data, work.work); + + gpio_keys_handle_button(bdata); +} + +static irqreturn_t button_handle_irq(int irq, void *_bdata) +{ + struct gpio_keys_button_data *bdata = + (struct gpio_keys_button_data *) _bdata; + + mod_delayed_work(system_wq, &bdata->work, + msecs_to_jiffies(bdata->software_debounce)); + + return IRQ_HANDLED; } #ifdef CONFIG_OF static struct gpio_keys_platform_data * -gpio_keys_polled_get_devtree_pdata(struct device *dev) +gpio_keys_get_devtree_pdata(struct device *dev) { struct device_node *node, *pp; struct gpio_keys_platform_data *pdata; struct gpio_keys_button *button; - int error; int nbuttons; - int i; + int i = 0; node = dev->of_node; if (!node) @@ -356,14 +374,12 @@ gpio_keys_polled_get_devtree_pdata(struct device *dev) nbuttons = of_get_child_count(node); if (nbuttons == 0) - return NULL; + return ERR_PTR(-EINVAL); - pdata = kzalloc(sizeof(*pdata) + nbuttons * (sizeof *button), + pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * (sizeof *button), GFP_KERNEL); - if (!pdata) { - error = -ENOMEM; - goto err_out; - } + if (!pdata) + return ERR_PTR(-ENOMEM); pdata->buttons = (struct gpio_keys_button *)(pdata + 1); pdata->nbuttons = nbuttons; @@ -371,26 +387,14 @@ gpio_keys_polled_get_devtree_pdata(struct device *dev) pdata->rep = !!of_get_property(node, "autorepeat", NULL); of_property_read_u32(node, "poll-interval", &pdata->poll_interval); - i = 0; for_each_child_of_node(node, pp) { - enum of_gpio_flags flags; - - if (!of_find_property(pp, "gpios", NULL)) { - pdata->nbuttons--; - dev_warn(dev, "Found button without gpios\n"); - continue; - } - - button = &pdata->buttons[i++]; - - button->gpio = of_get_gpio_flags(pp, 0, &flags); - button->active_low = flags & OF_GPIO_ACTIVE_LOW; + button = (struct gpio_keys_button *)(&pdata->buttons[i++]); if (of_property_read_u32(pp, "linux,code", &button->code)) { - dev_err(dev, "Button without keycode: 0x%x\n", - button->gpio); - error = -EINVAL; - goto err_free_pdata; + dev_err(dev, "Button node '%s' without keycode\n", + pp->full_name); + of_node_put(pp); + return ERR_PTR(-EINVAL); } button->desc = of_get_property(pp, "label", NULL); @@ -403,21 +407,20 @@ gpio_keys_polled_get_devtree_pdata(struct device *dev) if (of_property_read_u32(pp, "debounce-interval", &button->debounce_interval)) button->debounce_interval = 5; - } - if (pdata->nbuttons == 0) { - error = -EINVAL; - goto err_free_pdata; + button->irq = irq_of_parse_and_map(pp, 0); + button->gpio = -ENOENT; /* mark this as device-tree */ } return pdata; - -err_free_pdata: - kfree(pdata); -err_out: - return ERR_PTR(error); } +static struct of_device_id gpio_keys_of_match[] = { + { .compatible = "gpio-keys", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_keys_of_match); + static struct of_device_id gpio_keys_polled_of_match[] = { { .compatible = "gpio-keys-polled", }, { }, @@ -427,32 +430,25 @@ MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match); #else static inline struct gpio_keys_platform_data * -gpio_keys_polled_get_devtree_pdata(struct device *dev) +gpio_keys_get_devtree_pdata(struct device *dev) { return NULL; } #endif -static void gpio_keys_polled_close(struct gpio_keys_polled_dev *bdev) -{ - struct gpio_keys_platform_data *pdata = bdev->pdata; - - cancel_delayed_work_sync(&bdev->work); - - if (pdata->disable) - pdata->disable(bdev->dev); -} - -static int gpio_keys_polled_probe(struct platform_device *pdev) +static int gpio_keys_button_probe(struct platform_device *pdev, + struct gpio_keys_button_dev **_bdev, int polled) { - struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; - struct gpio_keys_polled_dev *bdev; - int error; + struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); + struct gpio_keys_button_dev *bdev; + struct gpio_keys_button *buttons; + struct device_node *prev = NULL; + int error = 0; int i; if (!pdata) { - pdata = gpio_keys_polled_get_devtree_pdata(dev); + pdata = gpio_keys_get_devtree_pdata(dev); if (IS_ERR(pdata)) return PTR_ERR(pdata); if (!pdata) { @@ -461,13 +457,20 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) } } - if (!pdata->poll_interval) { + if (polled && !pdata->poll_interval) { dev_err(dev, "missing poll_interval value\n"); - error = -EINVAL; - goto err_free_pdata; + return -EINVAL; + } + + buttons = devm_kzalloc(dev, pdata->nbuttons * sizeof(struct gpio_keys_button), + GFP_KERNEL); + if (!buttons) { + dev_err(dev, "no memory for button data\n"); + return -ENOMEM; } + memcpy(buttons, pdata->buttons, pdata->nbuttons * sizeof(struct gpio_keys_button)); - bdev = kzalloc(sizeof(struct gpio_keys_polled_dev) + + bdev = devm_kzalloc(dev, sizeof(struct gpio_keys_button_dev) + pdata->nbuttons * sizeof(struct gpio_keys_button_data), GFP_KERNEL); if (!bdev) { @@ -475,106 +478,254 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) return -ENOMEM; } + bdev->polled = polled; + for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_keys_button *button = &buttons[i]; struct gpio_keys_button_data *bdata = &bdev->data[i]; - unsigned int gpio = button->gpio; + const char *desc = button->desc ? button->desc : DRV_NAME; if (button->wakeup) { - dev_err(dev, DRV_NAME " does not support wakeup\n"); + dev_err(dev, "does not support wakeup\n"); + error = -EINVAL; + goto out; + } + + bdata->map_entry = button_get_index(button->code); + if (bdata->map_entry < 0) { + dev_err(dev, "does not support key code:%u\n", + button->code); error = -EINVAL; - goto err_free_gpio; + goto out; + } + + if (!(button->type == 0 || button->type == EV_KEY || + button->type == EV_SW)) { + dev_err(dev, "only supports buttons or switches\n"); + error = -EINVAL; + goto out; + } + + if (gpio_is_valid(button->gpio)) { + /* legacy platform data... but is it the lookup table? */ + bdata->gpiod = devm_gpiod_get_index(dev, desc, i, + GPIOD_IN); + if (IS_ERR(bdata->gpiod)) { + /* or the legacy (button->gpio is good) way? */ + error = devm_gpio_request_one(dev, + button->gpio, GPIOF_IN | ( + button->active_low ? GPIOF_ACTIVE_LOW : + 0), desc); + if (error) { + if (error != -EPROBE_DEFER) { + dev_err(dev, "unable to claim gpio %d, err=%d\n", + button->gpio, error); + } + goto out; + } + + bdata->gpiod = gpio_to_desc(button->gpio); + } + } else { + /* Device-tree */ + struct device_node *child = + of_get_next_child(dev->of_node, prev); + + bdata->gpiod = devm_gpiod_get_from_of_node(dev, + child, "gpios", 0, GPIOD_IN, desc); + + prev = child; } - error = gpio_request(gpio, - button->desc ? button->desc : DRV_NAME); - if (error) { - dev_err(dev, "unable to claim gpio %u, err=%d\n", - gpio, error); - goto err_free_gpio; + if (IS_ERR_OR_NULL(bdata->gpiod)) { + error = IS_ERR(bdata->gpiod) ? PTR_ERR(bdata->gpiod) : + -EINVAL; + goto out; } - error = gpio_direction_input(gpio); - if (error) { - dev_err(dev, - "unable to set direction on gpio %u, err=%d\n", - gpio, error); - goto err_free_gpio; + bdata->can_sleep = gpiod_cansleep(bdata->gpiod); + bdata->last_state = -1; /* Unknown state on boot */ + + if (bdev->polled) { + bdata->threshold = DIV_ROUND_UP(button->debounce_interval, + pdata->poll_interval); + } else { + /* bdata->threshold = 0; already initialized */ + + if (button->debounce_interval) { + error = gpiod_set_debounce(bdata->gpiod, + button->debounce_interval * 1000); + /* + * use timer if gpiolib doesn't provide + * debounce. + */ + if (error < 0) { + bdata->software_debounce = + button->debounce_interval; + } + } } - bdata->can_sleep = gpio_cansleep(gpio); - bdata->last_state = 0; - bdata->threshold = DIV_ROUND_UP(button->debounce_interval, - pdata->poll_interval); + bdata->b = &pdata->buttons[i]; } bdev->dev = &pdev->dev; bdev->pdata = pdata; platform_set_drvdata(pdev, bdev); - INIT_DELAYED_WORK(&bdev->work, gpio_keys_polled_poll); + *_bdev = bdev; + error = 0; + +out: + of_node_put(prev); + return error; +} + +static int gpio_keys_probe(struct platform_device *pdev) +{ + struct gpio_keys_platform_data *pdata; + struct gpio_keys_button_dev *bdev; + int ret, i; + + ret = gpio_keys_button_probe(pdev, &bdev, 0); + if (ret) + return ret; - gpio_keys_polled_open(bdev); + pdata = bdev->pdata; + for (i = 0; i < pdata->nbuttons; i++) { + const struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_keys_button_data *bdata = &bdev->data[i]; + unsigned long irqflags = IRQF_ONESHOT; + + INIT_DELAYED_WORK(&bdata->work, gpio_keys_irq_work_func); + + if (!button->irq) { + bdata->irq = gpiod_to_irq(bdata->gpiod); + if (bdata->irq < 0) { + dev_err(&pdev->dev, "failed to get irq for gpio:%d\n", + button->gpio); + continue; + } + + irqflags |= IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + } else { + bdata->irq = button->irq; + } + + schedule_delayed_work(&bdata->work, + msecs_to_jiffies(bdata->software_debounce)); + + ret = devm_request_threaded_irq(&pdev->dev, + bdata->irq, NULL, button_handle_irq, + irqflags, dev_name(&pdev->dev), bdata); + if (ret < 0) { + bdata->irq = 0; + dev_err(&pdev->dev, "failed to request irq:%d for gpio:%d\n", + bdata->irq, button->gpio); + continue; + } else { + dev_dbg(&pdev->dev, "gpio:%d has irq:%d\n", + button->gpio, bdata->irq); + } + } return 0; +} -err_free_gpio: - while (--i >= 0) - gpio_free(pdata->buttons[i].gpio); +static int gpio_keys_polled_probe(struct platform_device *pdev) +{ + struct gpio_keys_platform_data *pdata; + struct gpio_keys_button_dev *bdev; + int ret; - kfree(bdev); - platform_set_drvdata(pdev, NULL); + ret = gpio_keys_button_probe(pdev, &bdev, 1); + if (ret) + return ret; -err_free_pdata: - /* If we have no platform_data, we allocated pdata dynamically. */ - if (!dev_get_platdata(&pdev->dev)) - kfree(pdata); + INIT_DELAYED_WORK(&bdev->work, gpio_keys_polled_poll); - return error; + pdata = bdev->pdata; + if (pdata->enable) + pdata->enable(bdev->dev); + + gpio_keys_polled_queue_work(bdev); + + return ret; } -static int gpio_keys_polled_remove(struct platform_device *pdev) +static void gpio_keys_irq_close(struct gpio_keys_button_dev *bdev) { - struct gpio_keys_polled_dev *bdev = platform_get_drvdata(pdev); struct gpio_keys_platform_data *pdata = bdev->pdata; - int i = pdata->nbuttons; + size_t i; - gpio_keys_polled_close(bdev); + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button_data *bdata = &bdev->data[i]; - while (--i >= 0) - gpio_free(pdata->buttons[i].gpio); + disable_irq(bdata->irq); + cancel_delayed_work_sync(&bdata->work); + } +} + +static int gpio_keys_remove(struct platform_device *pdev) +{ + struct gpio_keys_button_dev *bdev = platform_get_drvdata(pdev); - kfree(bdev); platform_set_drvdata(pdev, NULL); + if (bdev->polled) + gpio_keys_polled_close(bdev); + else + gpio_keys_irq_close(bdev); + return 0; } +static struct platform_driver gpio_keys_driver = { + .probe = gpio_keys_probe, + .remove = gpio_keys_remove, + .driver = { + .name = "gpio-keys", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpio_keys_of_match), + }, +}; + static struct platform_driver gpio_keys_polled_driver = { .probe = gpio_keys_polled_probe, - .remove = gpio_keys_polled_remove, + .remove = gpio_keys_remove, .driver = { - .name = DRV_NAME, + .name = "gpio-keys-polled", .owner = THIS_MODULE, .of_match_table = of_match_ptr(gpio_keys_polled_of_match), }, }; -static int __init gpio_keys_polled_init(void) +static int __init gpio_button_init(void) { - return platform_driver_register(&gpio_keys_polled_driver); + int ret; + + ret = platform_driver_register(&gpio_keys_driver); + if (ret) + return ret; + + ret = platform_driver_register(&gpio_keys_polled_driver); + if (ret) + platform_driver_unregister(&gpio_keys_driver); + + return ret; } -static void __exit gpio_keys_polled_exit(void) +static void __exit gpio_button_exit(void) { + platform_driver_unregister(&gpio_keys_driver); platform_driver_unregister(&gpio_keys_polled_driver); } -module_init(gpio_keys_polled_init); -module_exit(gpio_keys_polled_exit); +module_init(gpio_button_init); +module_exit(gpio_button_exit); MODULE_AUTHOR("Gabor Juhos "); -MODULE_AUTHOR("Felix Fietkau "); +MODULE_AUTHOR("Felix Fietkau "); MODULE_DESCRIPTION("Polled GPIO Buttons hotplug driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME);