162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <net/mac80211.h>
362306a36Sopenharmony_ci#include <linux/bcma/bcma_driver_chipcommon.h>
462306a36Sopenharmony_ci#include <linux/gpio.h>
562306a36Sopenharmony_ci#include <linux/gpio/driver.h>
662306a36Sopenharmony_ci#include <linux/gpio/machine.h>
762306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "mac80211_if.h"
1062306a36Sopenharmony_ci#include "pub.h"
1162306a36Sopenharmony_ci#include "main.h"
1262306a36Sopenharmony_ci#include "led.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci	/* number of leds */
1562306a36Sopenharmony_ci#define  BRCMS_LED_NO		4
1662306a36Sopenharmony_ci	/* behavior mask */
1762306a36Sopenharmony_ci#define  BRCMS_LED_BEH_MASK	0x7f
1862306a36Sopenharmony_ci	/* activelow (polarity) bit */
1962306a36Sopenharmony_ci#define  BRCMS_LED_AL_MASK	0x80
2062306a36Sopenharmony_ci	/* radio enabled */
2162306a36Sopenharmony_ci#define  BRCMS_LED_RADIO	3
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void brcms_radio_led_ctrl(struct brcms_info *wl, bool state)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	if (!wl->radio_led.gpiod)
2662306a36Sopenharmony_ci		return;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (state)
2962306a36Sopenharmony_ci		gpiod_set_value(wl->radio_led.gpiod, 1);
3062306a36Sopenharmony_ci	else
3162306a36Sopenharmony_ci		gpiod_set_value(wl->radio_led.gpiod, 0);
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* Callback from the LED subsystem. */
3662306a36Sopenharmony_cistatic void brcms_led_brightness_set(struct led_classdev *led_dev,
3762306a36Sopenharmony_ci				   enum led_brightness brightness)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct brcms_info *wl = container_of(led_dev,
4062306a36Sopenharmony_ci		struct brcms_info, led_dev);
4162306a36Sopenharmony_ci	brcms_radio_led_ctrl(wl, brightness);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_civoid brcms_led_unregister(struct brcms_info *wl)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	if (wl->led_dev.dev)
4762306a36Sopenharmony_ci		led_classdev_unregister(&wl->led_dev);
4862306a36Sopenharmony_ci	if (wl->radio_led.gpiod)
4962306a36Sopenharmony_ci		gpiochip_free_own_desc(wl->radio_led.gpiod);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciint brcms_led_register(struct brcms_info *wl)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	int i, err;
5562306a36Sopenharmony_ci	struct brcms_led *radio_led = &wl->radio_led;
5662306a36Sopenharmony_ci	/* get CC core */
5762306a36Sopenharmony_ci	struct bcma_drv_cc *cc_drv  = &wl->wlc->hw->d11core->bus->drv_cc;
5862306a36Sopenharmony_ci	struct gpio_chip *bcma_gpio = &cc_drv->gpio;
5962306a36Sopenharmony_ci	struct ssb_sprom *sprom = &wl->wlc->hw->d11core->bus->sprom;
6062306a36Sopenharmony_ci	u8 *leds[] = { &sprom->gpio0,
6162306a36Sopenharmony_ci		&sprom->gpio1,
6262306a36Sopenharmony_ci		&sprom->gpio2,
6362306a36Sopenharmony_ci		&sprom->gpio3 };
6462306a36Sopenharmony_ci	int hwnum = -1;
6562306a36Sopenharmony_ci	enum gpio_lookup_flags lflags = GPIO_ACTIVE_HIGH;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	/* find radio enabled LED */
6862306a36Sopenharmony_ci	for (i = 0; i < BRCMS_LED_NO; i++) {
6962306a36Sopenharmony_ci		u8 led = *leds[i];
7062306a36Sopenharmony_ci		if ((led & BRCMS_LED_BEH_MASK) == BRCMS_LED_RADIO) {
7162306a36Sopenharmony_ci			hwnum = i;
7262306a36Sopenharmony_ci			if (led & BRCMS_LED_AL_MASK)
7362306a36Sopenharmony_ci				lflags = GPIO_ACTIVE_LOW;
7462306a36Sopenharmony_ci			break;
7562306a36Sopenharmony_ci		}
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* No LED, bail out */
7962306a36Sopenharmony_ci	if (hwnum == -1)
8062306a36Sopenharmony_ci		return -ENODEV;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Try to obtain this LED GPIO line */
8362306a36Sopenharmony_ci	radio_led->gpiod = gpiochip_request_own_desc(bcma_gpio, hwnum,
8462306a36Sopenharmony_ci						     "radio on", lflags,
8562306a36Sopenharmony_ci						     GPIOD_OUT_LOW);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (IS_ERR(radio_led->gpiod)) {
8862306a36Sopenharmony_ci		err = PTR_ERR(radio_led->gpiod);
8962306a36Sopenharmony_ci		wiphy_err(wl->wiphy, "requesting led GPIO failed (err: %d)\n",
9062306a36Sopenharmony_ci			  err);
9162306a36Sopenharmony_ci		return err;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	snprintf(wl->radio_led.name, sizeof(wl->radio_led.name),
9562306a36Sopenharmony_ci		 "brcmsmac-%s:radio", wiphy_name(wl->wiphy));
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	wl->led_dev.name = wl->radio_led.name;
9862306a36Sopenharmony_ci	wl->led_dev.default_trigger =
9962306a36Sopenharmony_ci		ieee80211_get_radio_led_name(wl->pub->ieee_hw);
10062306a36Sopenharmony_ci	wl->led_dev.brightness_set = brcms_led_brightness_set;
10162306a36Sopenharmony_ci	err = led_classdev_register(wiphy_dev(wl->wiphy), &wl->led_dev);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (err) {
10462306a36Sopenharmony_ci		wiphy_err(wl->wiphy, "cannot register led device: %s (err: %d)\n",
10562306a36Sopenharmony_ci			  wl->radio_led.name, err);
10662306a36Sopenharmony_ci		return err;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	wiphy_info(wl->wiphy, "registered radio enabled led device: %s\n",
11062306a36Sopenharmony_ci		   wl->radio_led.name);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return 0;
11362306a36Sopenharmony_ci}
114