mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 15:32:33 +00:00
d997712c71
This partitialy reverts commitf506de2cda
. Registering the GPIO chip without a parent device completely breaks the ath9k GPIOs for device tree targets. As long as boards using the devicetree don't have the gpio-controller property set for the ath9k node, the unloading of the driver works as expected. Register the GPIO chip with the ath9k device as parent only for OF targets to find a trade-off between the needs of driver developers and the broken LEDs and buttons seen by users. Fixes: FS#2098 Signed-off-by: Mathias Kresin <dev@kresin.me> (cherry picked from commitd35f2a5565
)
252 lines
6.3 KiB
Diff
252 lines
6.3 KiB
Diff
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@nbd.name>
|
|
---
|
|
--- 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"
|
|
@@ -1001,6 +1002,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;
|
|
@@ -1058,6 +1067,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,13 +16,139 @@
|
|
|
|
#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_gpio_request_in(gc->sc->sc_ah, offset, "ath9k-gpio");
|
|
+
|
|
+ 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_gpio_request_out(gc->sc->sc_ah, offset, "ath9k-gpio",
|
|
+ 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;
|
|
+ struct ath_hw *ah = sc->sc_ah;
|
|
+
|
|
+ gc = kzalloc(sizeof(struct ath9k_gpio_chip), GFP_KERNEL);
|
|
+ if (!gc)
|
|
+ return;
|
|
+
|
|
+ gc->sc = sc;
|
|
+ snprintf(gc->label, sizeof(gc->label), "ath9k-%s",
|
|
+ wiphy_name(sc->hw->wiphy));
|
|
+#ifdef CONFIG_OF
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)
|
|
+ gc->gchip.parent = sc->dev;
|
|
+#else
|
|
+ gc->gchip.dev = sc->dev;
|
|
+#endif
|
|
+#endif
|
|
+ gc->gchip.label = gc->label;
|
|
+ gc->gchip.base = -1; /* determine base automatically */
|
|
+ gc->gchip.ngpio = ah->caps.num_gpio_pins;
|
|
+ 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;
|
|
+
|
|
+ if (gpiochip_add(&gc->gchip)) {
|
|
+ kfree(gc);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_OF
|
|
+ gc->gchip.owner = NULL;
|
|
+#endif
|
|
+ sc->gpiochip = gc;
|
|
+}
|
|
+
|
|
+/* 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_fill_led_pin(struct ath_softc *sc)
|
|
{
|
|
struct ath_hw *ah = sc->sc_ah;
|
|
@@ -80,6 +206,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;
|
|
}
|
|
|
|
@@ -136,17 +268,24 @@ 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);
|
|
ath9k_hw_gpio_free(sc->sc_ah, led->gpio->gpio);
|
|
kfree(led);
|
|
}
|
|
+ ath9k_unregister_gpio_chip(sc);
|
|
}
|
|
|
|
void ath_init_leds(struct ath_softc *sc)
|
|
{
|
|
struct ath9k_platform_data *pdata = sc->dev->platform_data;
|
|
+ struct device_node *np = sc->dev->of_node;
|
|
char led_name[32];
|
|
const char *trigger;
|
|
int i;
|
|
@@ -156,6 +295,15 @@ void ath_init_leds(struct ath_softc *sc)
|
|
if (AR_SREV_9100(sc->sc_ah))
|
|
return;
|
|
|
|
+ if (!np)
|
|
+ ath9k_register_gpio_chip(sc);
|
|
+
|
|
+ /* setup gpio controller only if requested and skip the led_pin setup */
|
|
+ if (of_property_read_bool(np, "gpio-controller")) {
|
|
+ ath9k_register_gpio_chip(sc);
|
|
+ return;
|
|
+ }
|
|
+
|
|
ath_fill_led_pin(sc);
|
|
|
|
if (pdata && pdata->leds && pdata->num_leds)
|
|
@@ -180,6 +328,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);
|
|
}
|
|
+
|
|
#endif
|
|
|
|
/*******************/
|