[RFC][PATCH another try] MIPS: BCM47XX: Prepare support for GPIO buttons

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux