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