mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-24 07:46:48 +00:00
f506de2cda
Registering a GPIO chip with the ath9k device as parent prevents unload, because the gpiochip core increases the module use count. Unfortunately, the only way to avoid this at the moment seems to be to register the GPIO chip without a parent device Signed-off-by: Felix Fietkau <nbd@nbd.name>
243 lines
6.1 KiB
Diff
243 lines
6.1 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,130 @@
|
|
|
|
#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));
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ 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 +197,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 +259,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 +286,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 +319,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
|
|
|
|
/*******************/
|