18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <net/mac80211.h>
38c2ecf20Sopenharmony_ci#include <linux/bcma/bcma_driver_chipcommon.h>
48c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
58c2ecf20Sopenharmony_ci#include <linux/gpio/machine.h>
68c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "mac80211_if.h"
98c2ecf20Sopenharmony_ci#include "pub.h"
108c2ecf20Sopenharmony_ci#include "main.h"
118c2ecf20Sopenharmony_ci#include "led.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci	/* number of leds */
148c2ecf20Sopenharmony_ci#define  BRCMS_LED_NO		4
158c2ecf20Sopenharmony_ci	/* behavior mask */
168c2ecf20Sopenharmony_ci#define  BRCMS_LED_BEH_MASK	0x7f
178c2ecf20Sopenharmony_ci	/* activelow (polarity) bit */
188c2ecf20Sopenharmony_ci#define  BRCMS_LED_AL_MASK	0x80
198c2ecf20Sopenharmony_ci	/* radio enabled */
208c2ecf20Sopenharmony_ci#define  BRCMS_LED_RADIO	3
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic void brcms_radio_led_ctrl(struct brcms_info *wl, bool state)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	if (!wl->radio_led.gpiod)
258c2ecf20Sopenharmony_ci		return;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	if (state)
288c2ecf20Sopenharmony_ci		gpiod_set_value(wl->radio_led.gpiod, 1);
298c2ecf20Sopenharmony_ci	else
308c2ecf20Sopenharmony_ci		gpiod_set_value(wl->radio_led.gpiod, 0);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* Callback from the LED subsystem. */
358c2ecf20Sopenharmony_cistatic void brcms_led_brightness_set(struct led_classdev *led_dev,
368c2ecf20Sopenharmony_ci				   enum led_brightness brightness)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct brcms_info *wl = container_of(led_dev,
398c2ecf20Sopenharmony_ci		struct brcms_info, led_dev);
408c2ecf20Sopenharmony_ci	brcms_radio_led_ctrl(wl, brightness);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_civoid brcms_led_unregister(struct brcms_info *wl)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	if (wl->led_dev.dev)
468c2ecf20Sopenharmony_ci		led_classdev_unregister(&wl->led_dev);
478c2ecf20Sopenharmony_ci	if (wl->radio_led.gpiod)
488c2ecf20Sopenharmony_ci		gpiochip_free_own_desc(wl->radio_led.gpiod);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciint brcms_led_register(struct brcms_info *wl)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	int i, err;
548c2ecf20Sopenharmony_ci	struct brcms_led *radio_led = &wl->radio_led;
558c2ecf20Sopenharmony_ci	/* get CC core */
568c2ecf20Sopenharmony_ci	struct bcma_drv_cc *cc_drv  = &wl->wlc->hw->d11core->bus->drv_cc;
578c2ecf20Sopenharmony_ci	struct gpio_chip *bcma_gpio = &cc_drv->gpio;
588c2ecf20Sopenharmony_ci	struct ssb_sprom *sprom = &wl->wlc->hw->d11core->bus->sprom;
598c2ecf20Sopenharmony_ci	u8 *leds[] = { &sprom->gpio0,
608c2ecf20Sopenharmony_ci		&sprom->gpio1,
618c2ecf20Sopenharmony_ci		&sprom->gpio2,
628c2ecf20Sopenharmony_ci		&sprom->gpio3 };
638c2ecf20Sopenharmony_ci	int hwnum = -1;
648c2ecf20Sopenharmony_ci	enum gpio_lookup_flags lflags = GPIO_ACTIVE_HIGH;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (!bcma_gpio || !gpio_is_valid(bcma_gpio->base))
678c2ecf20Sopenharmony_ci		return -ENODEV;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* find radio enabled LED */
708c2ecf20Sopenharmony_ci	for (i = 0; i < BRCMS_LED_NO; i++) {
718c2ecf20Sopenharmony_ci		u8 led = *leds[i];
728c2ecf20Sopenharmony_ci		if ((led & BRCMS_LED_BEH_MASK) == BRCMS_LED_RADIO) {
738c2ecf20Sopenharmony_ci			hwnum = i;
748c2ecf20Sopenharmony_ci			if (led & BRCMS_LED_AL_MASK)
758c2ecf20Sopenharmony_ci				lflags = GPIO_ACTIVE_LOW;
768c2ecf20Sopenharmony_ci			break;
778c2ecf20Sopenharmony_ci		}
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* No LED, bail out */
818c2ecf20Sopenharmony_ci	if (hwnum == -1)
828c2ecf20Sopenharmony_ci		return -ENODEV;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* Try to obtain this LED GPIO line */
858c2ecf20Sopenharmony_ci	radio_led->gpiod = gpiochip_request_own_desc(bcma_gpio, hwnum,
868c2ecf20Sopenharmony_ci						     "radio on", lflags,
878c2ecf20Sopenharmony_ci						     GPIOD_OUT_LOW);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (IS_ERR(radio_led->gpiod)) {
908c2ecf20Sopenharmony_ci		err = PTR_ERR(radio_led->gpiod);
918c2ecf20Sopenharmony_ci		wiphy_err(wl->wiphy, "requesting led GPIO failed (err: %d)\n",
928c2ecf20Sopenharmony_ci			  err);
938c2ecf20Sopenharmony_ci		return err;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	snprintf(wl->radio_led.name, sizeof(wl->radio_led.name),
978c2ecf20Sopenharmony_ci		 "brcmsmac-%s:radio", wiphy_name(wl->wiphy));
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	wl->led_dev.name = wl->radio_led.name;
1008c2ecf20Sopenharmony_ci	wl->led_dev.default_trigger =
1018c2ecf20Sopenharmony_ci		ieee80211_get_radio_led_name(wl->pub->ieee_hw);
1028c2ecf20Sopenharmony_ci	wl->led_dev.brightness_set = brcms_led_brightness_set;
1038c2ecf20Sopenharmony_ci	err = led_classdev_register(wiphy_dev(wl->wiphy), &wl->led_dev);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (err) {
1068c2ecf20Sopenharmony_ci		wiphy_err(wl->wiphy, "cannot register led device: %s (err: %d)\n",
1078c2ecf20Sopenharmony_ci			  wl->radio_led.name, err);
1088c2ecf20Sopenharmony_ci		return err;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	wiphy_info(wl->wiphy, "registered radio enabled led device: %s\n",
1128c2ecf20Sopenharmony_ci		   wl->radio_led.name);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci}
116