|
@@ -0,0 +1,827 @@
|
|
|
+From: Matthias Schiffer <mschiffer@universe-factory.net>
|
|
|
+Date: Sun, 29 May 2016 13:39:10 +0200
|
|
|
+Subject: mac80211: rework gpio chip/button support to build on platforms without CONFIG_GPIOLIB
|
|
|
+
|
|
|
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
|
|
+
|
|
|
+Backport of r48938
|
|
|
+
|
|
|
+diff --git a/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch b/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch
|
|
|
+new file mode 100644
|
|
|
+index 0000000..f91d85c
|
|
|
+--- /dev/null
|
|
|
++++ b/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch
|
|
|
+@@ -0,0 +1,235 @@
|
|
|
++From: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
|
|
|
++Date: Sun, 31 Jan 2016 21:01:31 +0100
|
|
|
++Subject: [PATCH v4 4/8] mac80211: ath9k: enable access to GPIO
|
|
|
++
|
|
|
++Enable access to GPIO chip and its pins for Atheros AR92xx
|
|
|
++wireless devices. For now AR9285 and AR9287 are supported.
|
|
|
++
|
|
|
++Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
|
|
|
++Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
|
|
++---
|
|
|
++--- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
+++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
++@@ -24,6 +24,7 @@
|
|
|
++ #include <linux/completion.h>
|
|
|
++ #include <linux/time.h>
|
|
|
++ #include <linux/hw_random.h>
|
|
|
+++#include <linux/gpio/driver.h>
|
|
|
++
|
|
|
++ #include "common.h"
|
|
|
++ #include "debug.h"
|
|
|
++@@ -963,6 +964,14 @@ struct ath_led {
|
|
|
++ struct led_classdev cdev;
|
|
|
++ };
|
|
|
++
|
|
|
+++#ifdef CONFIG_GPIOLIB
|
|
|
+++struct ath9k_gpio_chip {
|
|
|
+++ struct ath_softc *sc;
|
|
|
+++ char label[32];
|
|
|
+++ struct gpio_chip gchip;
|
|
|
+++};
|
|
|
+++#endif
|
|
|
+++
|
|
|
++ struct ath_softc {
|
|
|
++ struct ieee80211_hw *hw;
|
|
|
++ struct device *dev;
|
|
|
++@@ -1017,6 +1026,9 @@ struct ath_softc {
|
|
|
++ #ifdef CPTCFG_MAC80211_LEDS
|
|
|
++ const char *led_default_trigger;
|
|
|
++ struct list_head leds;
|
|
|
+++#ifdef CONFIG_GPIOLIB
|
|
|
+++ struct ath9k_gpio_chip *gpiochip;
|
|
|
+++#endif
|
|
|
++ #endif
|
|
|
++
|
|
|
++ #ifdef CPTCFG_ATH9K_DEBUGFS
|
|
|
++--- a/drivers/net/wireless/ath/ath9k/gpio.c
|
|
|
+++++ b/drivers/net/wireless/ath/ath9k/gpio.c
|
|
|
++@@ -16,12 +16,138 @@
|
|
|
++
|
|
|
++ #include "ath9k.h"
|
|
|
++ #include <linux/ath9k_platform.h>
|
|
|
+++#include <linux/gpio.h>
|
|
|
+++
|
|
|
+++#ifdef CPTCFG_MAC80211_LEDS
|
|
|
+++
|
|
|
+++#ifdef CONFIG_GPIOLIB
|
|
|
+++
|
|
|
+++/***************/
|
|
|
+++/* GPIO Chip */
|
|
|
+++/***************/
|
|
|
+++
|
|
|
+++/* gpio_chip handler : set GPIO to input */
|
|
|
+++static int ath9k_gpio_pin_cfg_input(struct gpio_chip *chip, unsigned offset)
|
|
|
+++{
|
|
|
+++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+++ gchip);
|
|
|
+++
|
|
|
+++ ath9k_hw_cfg_gpio_input(gc->sc->sc_ah, offset);
|
|
|
+++
|
|
|
+++ return 0;
|
|
|
+++}
|
|
|
+++
|
|
|
+++/* gpio_chip handler : set GPIO to output */
|
|
|
+++static int ath9k_gpio_pin_cfg_output(struct gpio_chip *chip, unsigned offset,
|
|
|
+++ int value)
|
|
|
+++{
|
|
|
+++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+++ gchip);
|
|
|
+++
|
|
|
+++ ath9k_hw_cfg_output(gc->sc->sc_ah, offset,
|
|
|
+++ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
|
|
|
+++ ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
|
|
|
+++
|
|
|
+++ return 0;
|
|
|
+++}
|
|
|
+++
|
|
|
+++/* gpio_chip handler : query GPIO direction (0=out, 1=in) */
|
|
|
+++static int ath9k_gpio_pin_get_dir(struct gpio_chip *chip, unsigned offset)
|
|
|
+++{
|
|
|
+++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+++ gchip);
|
|
|
+++ struct ath_hw *ah = gc->sc->sc_ah;
|
|
|
+++
|
|
|
+++ return !((REG_READ(ah, AR_GPIO_OE_OUT) >> (offset * 2)) & 3);
|
|
|
+++}
|
|
|
+++
|
|
|
+++/* gpio_chip handler : get GPIO pin value */
|
|
|
+++static int ath9k_gpio_pin_get(struct gpio_chip *chip, unsigned offset)
|
|
|
+++{
|
|
|
+++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+++ gchip);
|
|
|
+++
|
|
|
+++ return ath9k_hw_gpio_get(gc->sc->sc_ah, offset);
|
|
|
+++}
|
|
|
+++
|
|
|
+++/* gpio_chip handler : set GPIO pin to value */
|
|
|
+++static void ath9k_gpio_pin_set(struct gpio_chip *chip, unsigned offset,
|
|
|
+++ int value)
|
|
|
+++{
|
|
|
+++ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+++ gchip);
|
|
|
+++
|
|
|
+++ ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
|
|
|
+++}
|
|
|
+++
|
|
|
+++/* register GPIO chip */
|
|
|
+++static void ath9k_register_gpio_chip(struct ath_softc *sc)
|
|
|
+++{
|
|
|
+++ struct ath9k_gpio_chip *gc;
|
|
|
+++ u16 ng;
|
|
|
+++
|
|
|
+++ /* for now only AR9285 and AR9287 are recognized */
|
|
|
+++ if (AR_SREV_9287(sc->sc_ah))
|
|
|
+++ ng = AR9287_NUM_GPIO;
|
|
|
+++ else if (AR_SREV_9285(sc->sc_ah))
|
|
|
+++ ng = AR9285_NUM_GPIO;
|
|
|
+++ else
|
|
|
+++ return;
|
|
|
+++
|
|
|
+++ gc = kzalloc(sizeof(struct ath9k_gpio_chip), GFP_KERNEL);
|
|
|
+++ if (!gc)
|
|
|
+++ return;
|
|
|
+++
|
|
|
+++ snprintf(gc->label, sizeof(gc->label), "ath9k-%s",
|
|
|
+++ wiphy_name(sc->hw->wiphy));
|
|
|
+++ gc->gchip.label = gc->label;
|
|
|
+++ gc->gchip.base = -1; /* determine base automatically */
|
|
|
+++ gc->gchip.ngpio = ng;
|
|
|
+++ gc->gchip.direction_input = ath9k_gpio_pin_cfg_input;
|
|
|
+++ gc->gchip.direction_output = ath9k_gpio_pin_cfg_output;
|
|
|
+++ gc->gchip.get_direction = ath9k_gpio_pin_get_dir;
|
|
|
+++ gc->gchip.get = ath9k_gpio_pin_get;
|
|
|
+++ gc->gchip.set = ath9k_gpio_pin_set;
|
|
|
+++ gc->gchip.owner = THIS_MODULE;
|
|
|
+++
|
|
|
+++ if (gpiochip_add(&gc->gchip)) {
|
|
|
+++ kfree(gc);
|
|
|
+++ return;
|
|
|
+++ }
|
|
|
+++
|
|
|
+++ sc->gpiochip = gc;
|
|
|
+++ gc->sc = sc;
|
|
|
+++}
|
|
|
+++
|
|
|
+++/* remove GPIO chip */
|
|
|
+++static void ath9k_unregister_gpio_chip(struct ath_softc *sc)
|
|
|
+++{
|
|
|
+++ struct ath9k_gpio_chip *gc = sc->gpiochip;
|
|
|
+++
|
|
|
+++ if (!gc)
|
|
|
+++ return;
|
|
|
+++
|
|
|
+++ gpiochip_remove(&gc->gchip);
|
|
|
+++ kfree(gc);
|
|
|
+++ sc->gpiochip = NULL;
|
|
|
+++}
|
|
|
+++
|
|
|
+++#else /* CONFIG_GPIOLIB */
|
|
|
+++
|
|
|
+++static inline void ath9k_register_gpio_chip(struct ath_softc *sc)
|
|
|
+++{
|
|
|
+++}
|
|
|
+++
|
|
|
+++static inline void ath9k_unregister_gpio_chip(struct ath_softc *sc)
|
|
|
+++{
|
|
|
+++}
|
|
|
+++
|
|
|
+++#endif /* CONFIG_GPIOLIB */
|
|
|
++
|
|
|
++ /********************************/
|
|
|
++ /* LED functions */
|
|
|
++ /********************************/
|
|
|
++
|
|
|
++-#ifdef CPTCFG_MAC80211_LEDS
|
|
|
++ static void ath_led_brightness(struct led_classdev *led_cdev,
|
|
|
++ enum led_brightness brightness)
|
|
|
++ {
|
|
|
++@@ -60,6 +186,12 @@ static int ath_add_led(struct ath_softc
|
|
|
++ else
|
|
|
++ ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, gpio->active_low);
|
|
|
++
|
|
|
+++#ifdef CONFIG_GPIOLIB
|
|
|
+++ /* If there is GPIO chip configured, reserve LED pin */
|
|
|
+++ if (sc->gpiochip)
|
|
|
+++ gpio_request(sc->gpiochip->gchip.base + gpio->gpio, gpio->name);
|
|
|
+++#endif
|
|
|
+++
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++
|
|
|
++@@ -116,11 +248,17 @@ void ath_deinit_leds(struct ath_softc *s
|
|
|
++
|
|
|
++ while (!list_empty(&sc->leds)) {
|
|
|
++ led = list_first_entry(&sc->leds, struct ath_led, list);
|
|
|
+++#ifdef CONFIG_GPIOLIB
|
|
|
+++ /* If there is GPIO chip configured, free LED pin */
|
|
|
+++ if (sc->gpiochip)
|
|
|
+++ gpio_free(sc->gpiochip->gchip.base + led->gpio->gpio);
|
|
|
+++#endif
|
|
|
++ list_del(&led->list);
|
|
|
++ ath_led_brightness(&led->cdev, LED_OFF);
|
|
|
++ led_classdev_unregister(&led->cdev);
|
|
|
++ kfree(led);
|
|
|
++ }
|
|
|
+++ ath9k_unregister_gpio_chip(sc);
|
|
|
++ }
|
|
|
++
|
|
|
++ void ath_init_leds(struct ath_softc *sc)
|
|
|
++@@ -135,6 +273,8 @@ void ath_init_leds(struct ath_softc *sc)
|
|
|
++ if (AR_SREV_9100(sc->sc_ah))
|
|
|
++ return;
|
|
|
++
|
|
|
+++ ath9k_register_gpio_chip(sc);
|
|
|
+++
|
|
|
++ if (pdata && pdata->led_name)
|
|
|
++ strncpy(led_name, pdata->led_name, sizeof(led_name));
|
|
|
++ else
|
|
|
++@@ -186,6 +326,7 @@ void ath_fill_led_pin(struct ath_softc *
|
|
|
++ /* LED off, active low */
|
|
|
++ ath9k_hw_set_gpio(ah, ah->led_pin, (ah->config.led_active_high) ? 0 : 1);
|
|
|
++ }
|
|
|
+++
|
|
|
++ #endif
|
|
|
++
|
|
|
++ /*******************/
|
|
|
+diff --git a/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch b/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch
|
|
|
+new file mode 100644
|
|
|
+index 0000000..0527406
|
|
|
+--- /dev/null
|
|
|
++++ b/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch
|
|
|
+@@ -0,0 +1,148 @@
|
|
|
++From: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
|
|
|
++Subject: [PATCH v5 5/8] mac80211: ath9k: enable GPIO buttons
|
|
|
++
|
|
|
++Enable platform-defined GPIO button support for ath9k device.
|
|
|
++Key poller is activated for attached platform buttons.
|
|
|
++Requires ath9k GPIO chip access.
|
|
|
++
|
|
|
++Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
|
|
|
++Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
|
|
++---
|
|
|
++--- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
+++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
++@@ -1028,6 +1028,7 @@ struct ath_softc {
|
|
|
++ struct list_head leds;
|
|
|
++ #ifdef CONFIG_GPIOLIB
|
|
|
++ struct ath9k_gpio_chip *gpiochip;
|
|
|
+++ struct platform_device *btnpdev; /* gpio-keys-polled */
|
|
|
++ #endif
|
|
|
++ #endif
|
|
|
++
|
|
|
++--- a/drivers/net/wireless/ath/ath9k/gpio.c
|
|
|
+++++ b/drivers/net/wireless/ath/ath9k/gpio.c
|
|
|
++@@ -17,6 +17,8 @@
|
|
|
++ #include "ath9k.h"
|
|
|
++ #include <linux/ath9k_platform.h>
|
|
|
++ #include <linux/gpio.h>
|
|
|
+++#include <linux/platform_device.h>
|
|
|
+++#include <linux/gpio_keys.h>
|
|
|
++
|
|
|
++ #ifdef CPTCFG_MAC80211_LEDS
|
|
|
++
|
|
|
++@@ -132,6 +134,63 @@ static void ath9k_unregister_gpio_chip(s
|
|
|
++ sc->gpiochip = NULL;
|
|
|
++ }
|
|
|
++
|
|
|
+++/******************/
|
|
|
+++/* GPIO Buttons */
|
|
|
+++/******************/
|
|
|
+++
|
|
|
+++/* add GPIO buttons */
|
|
|
+++static void ath9k_init_buttons(struct ath_softc *sc)
|
|
|
+++{
|
|
|
+++ struct ath9k_platform_data *pdata = sc->dev->platform_data;
|
|
|
+++ struct platform_device *pdev;
|
|
|
+++ struct gpio_keys_platform_data gkpdata;
|
|
|
+++ struct gpio_keys_button *bt;
|
|
|
+++ int i;
|
|
|
+++
|
|
|
+++ if (!sc->gpiochip)
|
|
|
+++ return;
|
|
|
+++
|
|
|
+++ if (!pdata || !pdata->btns || !pdata->num_btns)
|
|
|
+++ return;
|
|
|
+++
|
|
|
+++ bt = devm_kmemdup(sc->dev, pdata->btns,
|
|
|
+++ pdata->num_btns * sizeof(struct gpio_keys_button),
|
|
|
+++ GFP_KERNEL);
|
|
|
+++ if (!bt)
|
|
|
+++ return;
|
|
|
+++
|
|
|
+++ for (i = 0; i < pdata->num_btns; i++) {
|
|
|
+++ ath9k_hw_cfg_gpio_input(sc->sc_ah, pdata->btns[i].gpio);
|
|
|
+++ bt[i].gpio = sc->gpiochip->gchip.base + pdata->btns[i].gpio;
|
|
|
+++ }
|
|
|
+++
|
|
|
+++ memset(&gkpdata, 0, sizeof(struct gpio_keys_platform_data));
|
|
|
+++ gkpdata.buttons = bt;
|
|
|
+++ gkpdata.nbuttons = pdata->num_btns;
|
|
|
+++ gkpdata.poll_interval = pdata->btn_poll_interval;
|
|
|
+++
|
|
|
+++ pdev = platform_device_register_data(sc->dev, "gpio-keys-polled",
|
|
|
+++ PLATFORM_DEVID_AUTO, &gkpdata,
|
|
|
+++ sizeof(gkpdata));
|
|
|
+++ if (!IS_ERR_OR_NULL(pdev))
|
|
|
+++ sc->btnpdev = pdev;
|
|
|
+++ else {
|
|
|
+++ sc->btnpdev = NULL;
|
|
|
+++ devm_kfree(sc->dev, bt);
|
|
|
+++ }
|
|
|
+++}
|
|
|
+++
|
|
|
+++/* remove GPIO buttons */
|
|
|
+++static void ath9k_deinit_buttons(struct ath_softc *sc)
|
|
|
+++{
|
|
|
+++ if (!sc->gpiochip || !sc->btnpdev)
|
|
|
+++ return;
|
|
|
+++
|
|
|
+++ platform_device_unregister(sc->btnpdev);
|
|
|
+++
|
|
|
+++ sc->btnpdev = NULL;
|
|
|
+++}
|
|
|
+++
|
|
|
++ #else /* CONFIG_GPIOLIB */
|
|
|
++
|
|
|
++ static inline void ath9k_register_gpio_chip(struct ath_softc *sc)
|
|
|
++@@ -142,6 +201,14 @@ static inline void ath9k_unregister_gpio
|
|
|
++ {
|
|
|
++ }
|
|
|
++
|
|
|
+++static inline void ath9k_init_buttons(struct ath_softc *sc)
|
|
|
+++{
|
|
|
+++}
|
|
|
+++
|
|
|
+++static inline void ath9k_deinit_buttons(struct ath_softc *sc)
|
|
|
+++{
|
|
|
+++}
|
|
|
+++
|
|
|
++ #endif /* CONFIG_GPIOLIB */
|
|
|
++
|
|
|
++ /********************************/
|
|
|
++@@ -246,6 +313,7 @@ void ath_deinit_leds(struct ath_softc *s
|
|
|
++ {
|
|
|
++ struct ath_led *led;
|
|
|
++
|
|
|
+++ ath9k_deinit_buttons(sc);
|
|
|
++ while (!list_empty(&sc->leds)) {
|
|
|
++ led = list_first_entry(&sc->leds, struct ath_led, list);
|
|
|
++ #ifdef CONFIG_GPIOLIB
|
|
|
++@@ -274,6 +342,7 @@ void ath_init_leds(struct ath_softc *sc)
|
|
|
++ return;
|
|
|
++
|
|
|
++ ath9k_register_gpio_chip(sc);
|
|
|
+++ ath9k_init_buttons(sc);
|
|
|
++
|
|
|
++ if (pdata && pdata->led_name)
|
|
|
++ strncpy(led_name, pdata->led_name, sizeof(led_name));
|
|
|
++@@ -289,7 +358,7 @@ void ath_init_leds(struct ath_softc *sc)
|
|
|
++ ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger,
|
|
|
++ !sc->sc_ah->config.led_active_high);
|
|
|
++
|
|
|
++- if (!pdata)
|
|
|
+++ if (!pdata || !pdata->leds || !pdata->num_leds)
|
|
|
++ return;
|
|
|
++
|
|
|
++ for (i = 0; i < pdata->num_leds; i++)
|
|
|
++--- a/include/linux/ath9k_platform.h
|
|
|
+++++ b/include/linux/ath9k_platform.h
|
|
|
++@@ -46,6 +46,10 @@ struct ath9k_platform_data {
|
|
|
++ int num_leds;
|
|
|
++ const struct gpio_led *leds;
|
|
|
++ const char *led_name;
|
|
|
+++
|
|
|
+++ unsigned num_btns;
|
|
|
+++ const struct gpio_keys_button *btns;
|
|
|
+++ unsigned btn_poll_interval;
|
|
|
++ };
|
|
|
++
|
|
|
++ #endif /* _LINUX_ATH9K_PLATFORM_H */
|
|
|
+diff --git a/package/kernel/mac80211/patches/549-ath9k_enable_gpio_chip.patch b/package/kernel/mac80211/patches/549-ath9k_enable_gpio_chip.patch
|
|
|
+deleted file mode 100644
|
|
|
+index 55bb991..0000000
|
|
|
+--- a/package/kernel/mac80211/patches/549-ath9k_enable_gpio_chip.patch
|
|
|
++++ /dev/null
|
|
|
+@@ -1,243 +0,0 @@
|
|
|
+-From: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
|
|
|
+-Date: Sun, 31 Jan 2016 21:01:31 +0100
|
|
|
+-Subject: [PATCH v4 4/8] mac80211: ath9k: enable access to GPIO
|
|
|
+-
|
|
|
+-Enable access to GPIO chip and its pins for Atheros AR92xx
|
|
|
+-wireless devices. For now AR9285 and AR9287 are supported.
|
|
|
+-
|
|
|
+-Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
|
|
|
+----
|
|
|
+- ath9k.h | 23 ++++++++++++
|
|
|
+- gpio.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
+- init.c | 2 +
|
|
|
+- 3 files changed, 146 insertions(+)
|
|
|
+-
|
|
|
+---- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
+-+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
+-@@ -24,6 +24,7 @@
|
|
|
+- #include <linux/completion.h>
|
|
|
+- #include <linux/time.h>
|
|
|
+- #include <linux/hw_random.h>
|
|
|
+-+#include <linux/gpio/driver.h>
|
|
|
+-
|
|
|
+- #include "common.h"
|
|
|
+- #include "debug.h"
|
|
|
+-@@ -817,6 +818,13 @@ void ath_fill_led_pin(struct ath_softc *
|
|
|
+- int ath_create_gpio_led(struct ath_softc *sc, int gpio, const char *name,
|
|
|
+- const char *trigger, bool active_low);
|
|
|
+-
|
|
|
+-+/***************/
|
|
|
+-+/* GPIO Chip */
|
|
|
+-+/***************/
|
|
|
+-+
|
|
|
+-+void ath9k_register_gpio_chip(struct ath_softc *sc);
|
|
|
+-+void ath9k_unregister_gpio_chip(struct ath_softc *sc);
|
|
|
+-+
|
|
|
+- #else
|
|
|
+- static inline void ath_init_leds(struct ath_softc *sc)
|
|
|
+- {
|
|
|
+-@@ -828,6 +836,14 @@ static inline void ath_deinit_leds(struc
|
|
|
+- static inline void ath_fill_led_pin(struct ath_softc *sc)
|
|
|
+- {
|
|
|
+- }
|
|
|
+-+
|
|
|
+-+static inline void ath9k_register_gpio_chip(struct ath_softc *sc)
|
|
|
+-+{
|
|
|
+-+}
|
|
|
+-+
|
|
|
+-+static inline void ath9k_unregister_gpio_chip(struct ath_softc *sc)
|
|
|
+-+{
|
|
|
+-+}
|
|
|
+- #endif
|
|
|
+-
|
|
|
+- /************************/
|
|
|
+-@@ -963,6 +979,12 @@ struct ath_led {
|
|
|
+- struct led_classdev cdev;
|
|
|
+- };
|
|
|
+-
|
|
|
+-+struct ath9k_gpio_chip {
|
|
|
+-+ struct ath_softc *sc;
|
|
|
+-+ char label[32];
|
|
|
+-+ struct gpio_chip gchip;
|
|
|
+-+};
|
|
|
+-+
|
|
|
+- struct ath_softc {
|
|
|
+- struct ieee80211_hw *hw;
|
|
|
+- struct device *dev;
|
|
|
+-@@ -1017,6 +1039,7 @@ struct ath_softc {
|
|
|
+- #ifdef CPTCFG_MAC80211_LEDS
|
|
|
+- const char *led_default_trigger;
|
|
|
+- struct list_head leds;
|
|
|
+-+ struct ath9k_gpio_chip *gpiochip;
|
|
|
+- #endif
|
|
|
+-
|
|
|
+- #ifdef CPTCFG_ATH9K_DEBUGFS
|
|
|
+---- a/drivers/net/wireless/ath/ath9k/gpio.c
|
|
|
+-+++ b/drivers/net/wireless/ath/ath9k/gpio.c
|
|
|
+-@@ -22,6 +22,9 @@
|
|
|
+- /********************************/
|
|
|
+-
|
|
|
+- #ifdef CPTCFG_MAC80211_LEDS
|
|
|
+-+
|
|
|
+-+#include <asm-generic/gpio.h>
|
|
|
+-+
|
|
|
+- static void ath_led_brightness(struct led_classdev *led_cdev,
|
|
|
+- enum led_brightness brightness)
|
|
|
+- {
|
|
|
+-@@ -60,6 +63,10 @@ static int ath_add_led(struct ath_softc
|
|
|
+- else
|
|
|
+- ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, gpio->active_low);
|
|
|
+-
|
|
|
+-+ /* If there is GPIO chip configured, reserve LED pin */
|
|
|
+-+ if (sc->gpiochip)
|
|
|
+-+ gpio_request(sc->gpiochip->gchip.base + gpio->gpio, gpio->name);
|
|
|
+-+
|
|
|
+- return 0;
|
|
|
+- }
|
|
|
+-
|
|
|
+-@@ -116,6 +123,9 @@ void ath_deinit_leds(struct ath_softc *s
|
|
|
+-
|
|
|
+- while (!list_empty(&sc->leds)) {
|
|
|
+- led = list_first_entry(&sc->leds, struct ath_led, list);
|
|
|
+-+ /* If there is GPIO chip configured, free LED pin */
|
|
|
+-+ if (sc->gpiochip)
|
|
|
+-+ gpio_free(sc->gpiochip->gchip.base + led->gpio->gpio);
|
|
|
+- list_del(&led->list);
|
|
|
+- ath_led_brightness(&led->cdev, LED_OFF);
|
|
|
+- led_classdev_unregister(&led->cdev);
|
|
|
+-@@ -186,6 +196,117 @@ void ath_fill_led_pin(struct ath_softc *
|
|
|
+- /* LED off, active low */
|
|
|
+- ath9k_hw_set_gpio(ah, ah->led_pin, (ah->config.led_active_high) ? 0 : 1);
|
|
|
+- }
|
|
|
+-+
|
|
|
+-+/***************/
|
|
|
+-+/* GPIO Chip */
|
|
|
+-+/***************/
|
|
|
+-+
|
|
|
+-+/* gpio_chip handler : set GPIO to input */
|
|
|
+-+static int ath9k_gpio_pin_cfg_input(struct gpio_chip *chip, unsigned offset)
|
|
|
+-+{
|
|
|
+-+ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+-+ gchip);
|
|
|
+-+
|
|
|
+-+ ath9k_hw_cfg_gpio_input(gc->sc->sc_ah, offset);
|
|
|
+-+
|
|
|
+-+ return 0;
|
|
|
+-+}
|
|
|
+-+
|
|
|
+-+/* gpio_chip handler : set GPIO to output */
|
|
|
+-+static int ath9k_gpio_pin_cfg_output(struct gpio_chip *chip, unsigned offset,
|
|
|
+-+ int value)
|
|
|
+-+{
|
|
|
+-+ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+-+ gchip);
|
|
|
+-+
|
|
|
+-+ ath9k_hw_cfg_output(gc->sc->sc_ah, offset,
|
|
|
+-+ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
|
|
|
+-+ ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
|
|
|
+-+
|
|
|
+-+ return 0;
|
|
|
+-+}
|
|
|
+-+
|
|
|
+-+/* gpio_chip handler : query GPIO direction (0=out, 1=in) */
|
|
|
+-+static int ath9k_gpio_pin_get_dir(struct gpio_chip *chip, unsigned offset)
|
|
|
+-+{
|
|
|
+-+ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+-+ gchip);
|
|
|
+-+ struct ath_hw *ah = gc->sc->sc_ah;
|
|
|
+-+
|
|
|
+-+ return !((REG_READ(ah, AR_GPIO_OE_OUT) >> (offset * 2)) & 3);
|
|
|
+-+}
|
|
|
+-+
|
|
|
+-+/* gpio_chip handler : get GPIO pin value */
|
|
|
+-+static int ath9k_gpio_pin_get(struct gpio_chip *chip, unsigned offset)
|
|
|
+-+{
|
|
|
+-+ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+-+ gchip);
|
|
|
+-+
|
|
|
+-+ return ath9k_hw_gpio_get(gc->sc->sc_ah, offset);
|
|
|
+-+}
|
|
|
+-+
|
|
|
+-+/* gpio_chip handler : set GPIO pin to value */
|
|
|
+-+static void ath9k_gpio_pin_set(struct gpio_chip *chip, unsigned offset,
|
|
|
+-+ int value)
|
|
|
+-+{
|
|
|
+-+ struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
|
|
|
+-+ gchip);
|
|
|
+-+
|
|
|
+-+ ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
|
|
|
+-+}
|
|
|
+-+
|
|
|
+-+/* register GPIO chip */
|
|
|
+-+void ath9k_register_gpio_chip(struct ath_softc *sc)
|
|
|
+-+{
|
|
|
+-+ struct ath9k_gpio_chip *gc;
|
|
|
+-+ u16 ng;
|
|
|
+-+
|
|
|
+-+ /* for now only AR9285 and AR9287 are recognized */
|
|
|
+-+ if (AR_SREV_9287(sc->sc_ah))
|
|
|
+-+ ng = AR9287_NUM_GPIO;
|
|
|
+-+ else if (AR_SREV_9285(sc->sc_ah))
|
|
|
+-+ ng = AR9285_NUM_GPIO;
|
|
|
+-+ else
|
|
|
+-+ return;
|
|
|
+-+
|
|
|
+-+ gc = kzalloc(sizeof(struct ath9k_gpio_chip), GFP_KERNEL);
|
|
|
+-+ if (!gc)
|
|
|
+-+ return;
|
|
|
+-+
|
|
|
+-+ snprintf(gc->label, sizeof(gc->label), "ath9k-%s",
|
|
|
+-+ wiphy_name(sc->hw->wiphy));
|
|
|
+-+ gc->gchip.label = gc->label;
|
|
|
+-+ gc->gchip.base = -1; /* determine base automatically */
|
|
|
+-+ gc->gchip.ngpio = ng;
|
|
|
+-+ gc->gchip.direction_input = ath9k_gpio_pin_cfg_input;
|
|
|
+-+ gc->gchip.direction_output = ath9k_gpio_pin_cfg_output;
|
|
|
+-+ gc->gchip.get_direction = ath9k_gpio_pin_get_dir;
|
|
|
+-+ gc->gchip.get = ath9k_gpio_pin_get;
|
|
|
+-+ gc->gchip.set = ath9k_gpio_pin_set;
|
|
|
+-+ gc->gchip.owner = THIS_MODULE;
|
|
|
+-+
|
|
|
+-+ if (gpiochip_add(&gc->gchip)) {
|
|
|
+-+ kfree(gc);
|
|
|
+-+ return;
|
|
|
+-+ }
|
|
|
+-+
|
|
|
+-+ sc->gpiochip = gc;
|
|
|
+-+ gc->sc = sc;
|
|
|
+-+}
|
|
|
+-+
|
|
|
+-+/* remove GPIO chip */
|
|
|
+-+void ath9k_unregister_gpio_chip(struct ath_softc *sc)
|
|
|
+-+{
|
|
|
+-+ struct ath9k_gpio_chip *gc = sc->gpiochip;
|
|
|
+-+
|
|
|
+-+ if (!gc)
|
|
|
+-+ return;
|
|
|
+-+
|
|
|
+-+ gpiochip_remove(&gc->gchip);
|
|
|
+-+ kfree(gc);
|
|
|
+-+ sc->gpiochip = NULL;
|
|
|
+-+}
|
|
|
+-+
|
|
|
+- #endif
|
|
|
+-
|
|
|
+- /*******************/
|
|
|
+---- a/drivers/net/wireless/ath/ath9k/init.c
|
|
|
+-+++ b/drivers/net/wireless/ath/ath9k/init.c
|
|
|
+-@@ -975,6 +975,7 @@ int ath9k_init_device(u16 devid, struct
|
|
|
+- goto debug_cleanup;
|
|
|
+- }
|
|
|
+-
|
|
|
+-+ ath9k_register_gpio_chip(sc);
|
|
|
+- ath_init_leds(sc);
|
|
|
+- ath_start_rfkill_poll(sc);
|
|
|
+-
|
|
|
+-@@ -1022,6 +1023,7 @@ void ath9k_deinit_device(struct ath_soft
|
|
|
+-
|
|
|
+- wiphy_rfkill_stop_polling(sc->hw->wiphy);
|
|
|
+- ath_deinit_leds(sc);
|
|
|
+-+ ath9k_unregister_gpio_chip(sc);
|
|
|
+-
|
|
|
+- ath9k_ps_restore(sc);
|
|
|
+-
|
|
|
+diff --git a/package/kernel/mac80211/patches/550-ath9k_enable_gpio_buttons.patch b/package/kernel/mac80211/patches/550-ath9k_enable_gpio_buttons.patch
|
|
|
+deleted file mode 100644
|
|
|
+index e3a8cec..0000000
|
|
|
+--- a/package/kernel/mac80211/patches/550-ath9k_enable_gpio_buttons.patch
|
|
|
++++ /dev/null
|
|
|
+@@ -1,169 +0,0 @@
|
|
|
+-From: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
|
|
|
+-Subject: [PATCH v5 5/8] mac80211: ath9k: enable GPIO buttons
|
|
|
+-
|
|
|
+-Enable platform-defined GPIO button support for ath9k device.
|
|
|
+-Key poller is activated for attached platform buttons.
|
|
|
+-Requires ath9k GPIO chip access.
|
|
|
+-
|
|
|
+-Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
|
|
|
+----
|
|
|
+- drivers/net/wireless/ath/ath9k/ath9k.h | 16 ++++++
|
|
|
+- drivers/net/wireless/ath/ath9k/gpio.c | 77 +++++++++++++++++++++++++++++++++
|
|
|
+- drivers/net/wireless/ath/ath9k/init.c | 2
|
|
|
+- include/linux/ath9k_platform.h | 4 +
|
|
|
+- 4 files changed, 99 insertions(+)
|
|
|
+-
|
|
|
+---- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
+-+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
+-@@ -825,6 +825,13 @@ int ath_create_gpio_led(struct ath_softc
|
|
|
+- void ath9k_register_gpio_chip(struct ath_softc *sc);
|
|
|
+- void ath9k_unregister_gpio_chip(struct ath_softc *sc);
|
|
|
+-
|
|
|
+-+/******************/
|
|
|
+-+/* GPIO Buttons */
|
|
|
+-+/******************/
|
|
|
+-+
|
|
|
+-+void ath9k_init_buttons(struct ath_softc *sc);
|
|
|
+-+void ath9k_deinit_buttons(struct ath_softc *sc);
|
|
|
+-+
|
|
|
+- #else
|
|
|
+- static inline void ath_init_leds(struct ath_softc *sc)
|
|
|
+- {
|
|
|
+-@@ -844,6 +851,14 @@ static inline void ath9k_register_gpio_c
|
|
|
+- static inline void ath9k_unregister_gpio_chip(struct ath_softc *sc)
|
|
|
+- {
|
|
|
+- }
|
|
|
+-+
|
|
|
+-+static inline void ath9k_init_buttons(struct ath_softc *sc)
|
|
|
+-+{
|
|
|
+-+}
|
|
|
+-+
|
|
|
+-+static inline void ath9k_deinit_buttons(struct ath_softc *sc)
|
|
|
+-+{
|
|
|
+-+}
|
|
|
+- #endif
|
|
|
+-
|
|
|
+- /************************/
|
|
|
+-@@ -1040,6 +1055,7 @@ struct ath_softc {
|
|
|
+- const char *led_default_trigger;
|
|
|
+- struct list_head leds;
|
|
|
+- struct ath9k_gpio_chip *gpiochip;
|
|
|
+-+ struct platform_device *btnpdev; /* gpio-keys-polled */
|
|
|
+- #endif
|
|
|
+-
|
|
|
+- #ifdef CPTCFG_ATH9K_DEBUGFS
|
|
|
+---- a/drivers/net/wireless/ath/ath9k/gpio.c
|
|
|
+-+++ b/drivers/net/wireless/ath/ath9k/gpio.c
|
|
|
+-@@ -24,6 +24,8 @@
|
|
|
+- #ifdef CPTCFG_MAC80211_LEDS
|
|
|
+-
|
|
|
+- #include <asm-generic/gpio.h>
|
|
|
+-+#include <linux/platform_device.h>
|
|
|
+-+#include <linux/gpio_keys.h>
|
|
|
+-
|
|
|
+- static void ath_led_brightness(struct led_classdev *led_cdev,
|
|
|
+- enum led_brightness brightness)
|
|
|
+-@@ -159,7 +161,7 @@ void ath_init_leds(struct ath_softc *sc)
|
|
|
+- ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger,
|
|
|
+- !sc->sc_ah->config.led_active_high);
|
|
|
+-
|
|
|
+-- if (!pdata)
|
|
|
+-+ if (!pdata || !pdata->leds || !pdata->num_leds)
|
|
|
+- return;
|
|
|
+-
|
|
|
+- for (i = 0; i < pdata->num_leds; i++)
|
|
|
+-@@ -307,6 +309,63 @@ void ath9k_unregister_gpio_chip(struct a
|
|
|
+- sc->gpiochip = NULL;
|
|
|
+- }
|
|
|
+-
|
|
|
+-+/******************/
|
|
|
+-+/* GPIO Buttons */
|
|
|
+-+/******************/
|
|
|
+-+
|
|
|
+-+/* add GPIO buttons */
|
|
|
+-+void ath9k_init_buttons(struct ath_softc *sc)
|
|
|
+-+{
|
|
|
+-+ struct ath9k_platform_data *pdata = sc->dev->platform_data;
|
|
|
+-+ struct platform_device *pdev;
|
|
|
+-+ struct gpio_keys_platform_data gkpdata;
|
|
|
+-+ struct gpio_keys_button *bt;
|
|
|
+-+ int i;
|
|
|
+-+
|
|
|
+-+ if (!sc->gpiochip)
|
|
|
+-+ return;
|
|
|
+-+
|
|
|
+-+ if (!pdata || !pdata->btns || !pdata->num_btns)
|
|
|
+-+ return;
|
|
|
+-+
|
|
|
+-+ bt = devm_kmemdup(sc->dev, pdata->btns,
|
|
|
+-+ pdata->num_btns * sizeof(struct gpio_keys_button),
|
|
|
+-+ GFP_KERNEL);
|
|
|
+-+ if (!bt)
|
|
|
+-+ return;
|
|
|
+-+
|
|
|
+-+ for (i = 0; i < pdata->num_btns; i++) {
|
|
|
+-+ ath9k_hw_cfg_gpio_input(sc->sc_ah, pdata->btns[i].gpio);
|
|
|
+-+ bt[i].gpio = sc->gpiochip->gchip.base + pdata->btns[i].gpio;
|
|
|
+-+ }
|
|
|
+-+
|
|
|
+-+ memset(&gkpdata, 0, sizeof(struct gpio_keys_platform_data));
|
|
|
+-+ gkpdata.buttons = bt;
|
|
|
+-+ gkpdata.nbuttons = pdata->num_btns;
|
|
|
+-+ gkpdata.poll_interval = pdata->btn_poll_interval;
|
|
|
+-+
|
|
|
+-+ pdev = platform_device_register_data(sc->dev, "gpio-keys-polled",
|
|
|
+-+ PLATFORM_DEVID_AUTO, &gkpdata,
|
|
|
+-+ sizeof(gkpdata));
|
|
|
+-+ if (!IS_ERR_OR_NULL(pdev))
|
|
|
+-+ sc->btnpdev = pdev;
|
|
|
+-+ else {
|
|
|
+-+ sc->btnpdev = NULL;
|
|
|
+-+ devm_kfree(sc->dev, bt);
|
|
|
+-+ }
|
|
|
+-+}
|
|
|
+-+
|
|
|
+-+/* remove GPIO buttons */
|
|
|
+-+void ath9k_deinit_buttons(struct ath_softc *sc)
|
|
|
+-+{
|
|
|
+-+ if (!sc->gpiochip || !sc->btnpdev)
|
|
|
+-+ return;
|
|
|
+-+
|
|
|
+-+ platform_device_unregister(sc->btnpdev);
|
|
|
+-+
|
|
|
+-+ sc->btnpdev = NULL;
|
|
|
+-+}
|
|
|
+-+
|
|
|
+- #endif
|
|
|
+-
|
|
|
+- /*******************/
|
|
|
+---- a/drivers/net/wireless/ath/ath9k/init.c
|
|
|
+-+++ b/drivers/net/wireless/ath/ath9k/init.c
|
|
|
+-@@ -977,6 +977,7 @@ int ath9k_init_device(u16 devid, struct
|
|
|
+-
|
|
|
+- ath9k_register_gpio_chip(sc);
|
|
|
+- ath_init_leds(sc);
|
|
|
+-+ ath9k_init_buttons(sc);
|
|
|
+- ath_start_rfkill_poll(sc);
|
|
|
+-
|
|
|
+- return 0;
|
|
|
+-@@ -1022,6 +1023,7 @@ void ath9k_deinit_device(struct ath_soft
|
|
|
+- ath9k_ps_wakeup(sc);
|
|
|
+-
|
|
|
+- wiphy_rfkill_stop_polling(sc->hw->wiphy);
|
|
|
+-+ ath9k_deinit_buttons(sc);
|
|
|
+- ath_deinit_leds(sc);
|
|
|
+- ath9k_unregister_gpio_chip(sc);
|
|
|
+-
|
|
|
+---- a/include/linux/ath9k_platform.h
|
|
|
+-+++ b/include/linux/ath9k_platform.h
|
|
|
+-@@ -46,6 +46,10 @@ struct ath9k_platform_data {
|
|
|
+- int num_leds;
|
|
|
+- const struct gpio_led *leds;
|
|
|
+- const char *led_name;
|
|
|
+-+
|
|
|
+-+ unsigned num_btns;
|
|
|
+-+ const struct gpio_keys_button *btns;
|
|
|
+-+ unsigned btn_poll_interval;
|
|
|
+- };
|
|
|
+-
|
|
|
+- #endif /* _LINUX_ATH9K_PLATFORM_H */
|