So far this adds support for one Netgear model only, but it's designed and ready to add many more device. We could hopefully import database from OpenWrt. Signed-off-by: Rafał Miłecki <zajec5@xxxxxxxxx> --- This is another try to implement GPIO buttons support. Instead of re-implementing most of the gpio_keys code, I decided to modify gpio_keys a bit. A proper patch was posted to the linux-input: [PATCH] Input: gpio_keys - add ack_irq callback for HW that needs ACK http://www.mail-archive.com/linux-input@xxxxxxxxxxxxxxx/msg07071.html This is just a RFC, we have to see if the above patch will be accepted by linux-input guys first. --- arch/mips/bcm47xx/Kconfig | 6 ++ arch/mips/bcm47xx/Makefile | 2 +- arch/mips/bcm47xx/bcm47xx_private.h | 3 + arch/mips/bcm47xx/buttons.c | 152 +++++++++++++++++++++++++++++++++++ arch/mips/bcm47xx/setup.c | 1 + 5 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 arch/mips/bcm47xx/buttons.c diff --git a/arch/mips/bcm47xx/Kconfig b/arch/mips/bcm47xx/Kconfig index 09fc922..28b7d4b 100644 --- a/arch/mips/bcm47xx/Kconfig +++ b/arch/mips/bcm47xx/Kconfig @@ -11,6 +11,9 @@ config BCM47XX_SSB select SSB_PCICORE_HOSTMODE if PCI select SSB_DRIVER_GPIO select GPIOLIB + select INPUT + select INPUT_KEYBOARD + select KEYBOARD_GPIO select LEDS_GPIO_REGISTER default y help @@ -28,6 +31,9 @@ config BCM47XX_BCMA select BCMA_DRIVER_PCI_HOSTMODE if PCI select BCMA_DRIVER_GPIO select GPIOLIB + select INPUT + select INPUT_KEYBOARD + select KEYBOARD_GPIO select LEDS_GPIO_REGISTER default y help diff --git a/arch/mips/bcm47xx/Makefile b/arch/mips/bcm47xx/Makefile index 84e9aed..006a05e 100644 --- a/arch/mips/bcm47xx/Makefile +++ b/arch/mips/bcm47xx/Makefile @@ -4,5 +4,5 @@ # obj-y += irq.o nvram.o prom.o serial.o setup.o time.o sprom.o -obj-y += board.o leds.o +obj-y += board.o buttons.o leds.o obj-$(CONFIG_BCM47XX_SSB) += wgt634u.o diff --git a/arch/mips/bcm47xx/bcm47xx_private.h b/arch/mips/bcm47xx/bcm47xx_private.h index 1a1e600..5c94ace 100644 --- a/arch/mips/bcm47xx/bcm47xx_private.h +++ b/arch/mips/bcm47xx/bcm47xx_private.h @@ -3,6 +3,9 @@ #include <linux/kernel.h> +/* buttons.c */ +int __init bcm47xx_buttons_register(void); + /* leds.c */ void __init bcm47xx_leds_register(void); diff --git a/arch/mips/bcm47xx/buttons.c b/arch/mips/bcm47xx/buttons.c new file mode 100644 index 0000000..bec9f61 --- /dev/null +++ b/arch/mips/bcm47xx/buttons.c @@ -0,0 +1,152 @@ +#include "bcm47xx_private.h" + +#include <linux/input.h> +#include <linux/gpio_keys.h> +#include <linux/interrupt.h> +#include <linux/ssb/ssb_embedded.h> +#include <bcm47xx_board.h> +#include <bcm47xx.h> + +struct input_dev *input; + +/************************************************** + * Database + **************************************************/ + +static struct gpio_keys_button +bcm47xx_buttons_netgear_wndr4500_v1[] = { + { + .code = KEY_WPS_BUTTON, + .gpio = 4, + .active_low = 1, + }, + { + .code = KEY_RFKILL, + .gpio = 5, + .active_low = 1, + }, + { + .code = KEY_RESTART, + .gpio = 6, + .active_low = 1, + }, +}; + +/************************************************** + * Handlers + **************************************************/ + +static void bcm47xx_buttons_gpio_polarity(u32 mask, u32 value) +{ + switch (bcm47xx_bus_type) { +#ifdef CONFIG_BCM47XX_SSB + case BCM47XX_BUS_TYPE_SSB: + ssb_gpio_polarity(&bcm47xx_bus.ssb, mask, value); + return; +#endif +#ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: + bcma_chipco_gpio_polarity(&bcm47xx_bus.bcma.bus.drv_cc, mask, + value); + return; +#endif + } + WARN_ON(1); +} + +static void bcm47xx_buttons_irq(const struct gpio_keys_button *button) +{ + int gpio = button->gpio; + u32 val = __gpio_get_value(gpio); + + /* + * As soon as button state changes, adjust interrupt polarity. + * This prevents hardware from keep generating interrupts for + * the current state. + */ + bcm47xx_buttons_gpio_polarity(BIT(gpio), val ? BIT(gpio) : 0); +} + +/************************************************** + * Init + **************************************************/ + +static struct gpio_keys_platform_data bcm47xx_button_pdata; + +static struct platform_device bcm47xx_buttons_gpio_keys = { + .name = "gpio-keys", + .dev = { + .platform_data = &bcm47xx_button_pdata, + } +}; + +#define bcm47xx_set_bdata(dev_buttons) do { \ + bcm47xx_button_pdata.buttons = dev_buttons; \ + bcm47xx_button_pdata.nbuttons = ARRAY_SIZE(dev_buttons); \ +} while (0) + +int __init bcm47xx_buttons_register(void) +{ +#ifdef CONFIG_BCM47XX_SSB + struct ssb_bus *ssb; +#endif +#ifdef CONFIG_BCM47XX_BCMA + struct bcma_drv_cc *bcma_cc; +#endif + enum bcm47xx_board board = bcm47xx_board_get(); + u32 gpio_mask = 0; + int i, err; + + switch (board) { + case BCM47XX_BOARD_NETGEAR_WNDR4500V1: + bcm47xx_set_bdata(bcm47xx_buttons_netgear_wndr4500_v1); + break; + default: + pr_debug("No buttons configuration found for this device\n"); + return -ENOTSUPP; + } + + for (i = 0; i < bcm47xx_button_pdata.nbuttons; i++) + bcm47xx_button_pdata.buttons[i].ack_irq = bcm47xx_buttons_irq; + + err = platform_device_register(&bcm47xx_buttons_gpio_keys); + if (err) { + pr_err("Failed to register platform device: %d\n", err); + return err; + } + + for (i = 0; i < bcm47xx_button_pdata.nbuttons; i++) { + struct gpio_keys_button *button = + &bcm47xx_button_pdata.buttons[i]; + int gpio = button->gpio; + int val = __gpio_get_value(gpio); + + bcm47xx_buttons_gpio_polarity(BIT(gpio), val ? BIT(gpio) : 0); + gpio_mask |= BIT(gpio); + } + + /* + * Set a list of GPIOs that should generate interrupts and enable GPIO + * interrupts in the ChipCommon core. + */ + switch (bcm47xx_bus_type) { +#ifdef CONFIG_BCM47XX_SSB + case BCM47XX_BUS_TYPE_SSB: + ssb = &bcm47xx_bus.ssb; + ssb_gpio_intmask(ssb, gpio_mask, gpio_mask); + if (&ssb->chipco) + chipco_set32(&ssb->chipco, SSB_CHIPCO_IRQMASK, + SSB_CHIPCO_IRQ_GPIO); + break; +#endif +#ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: + bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; + bcma_chipco_gpio_intmask(bcma_cc, gpio_mask, gpio_mask); + bcma_cc_set32(bcma_cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO); + break; +#endif + } + + return 0; +} diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c index 7e61c0b..a791124 100644 --- a/arch/mips/bcm47xx/setup.c +++ b/arch/mips/bcm47xx/setup.c @@ -242,6 +242,7 @@ static int __init bcm47xx_register_bus_complete(void) #endif } + bcm47xx_buttons_register(); bcm47xx_leds_register(); return 0; -- 1.7.10.4