18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Common code for mac80211 Prism54 drivers
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
68c2ecf20Sopenharmony_ci * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
78c2ecf20Sopenharmony_ci * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Based on:
108c2ecf20Sopenharmony_ci * - the islsm (softmac prism54) driver, which is:
118c2ecf20Sopenharmony_ci *   Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
128c2ecf20Sopenharmony_ci * - stlc45xx driver
138c2ecf20Sopenharmony_ci *   Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/firmware.h>
178c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <net/mac80211.h>
208c2ecf20Sopenharmony_ci#ifdef CONFIG_P54_LEDS
218c2ecf20Sopenharmony_ci#include <linux/leds.h>
228c2ecf20Sopenharmony_ci#endif /* CONFIG_P54_LEDS */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "p54.h"
258c2ecf20Sopenharmony_ci#include "lmac.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic void p54_update_leds(struct work_struct *work)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct p54_common *priv = container_of(work, struct p54_common,
308c2ecf20Sopenharmony_ci					       led_work.work);
318c2ecf20Sopenharmony_ci	int err, i, tmp, blink_delay = 400;
328c2ecf20Sopenharmony_ci	bool rerun = false;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* Don't toggle the LED, when the device is down. */
358c2ecf20Sopenharmony_ci	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
368c2ecf20Sopenharmony_ci		return ;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(priv->leds); i++)
398c2ecf20Sopenharmony_ci		if (priv->leds[i].toggled) {
408c2ecf20Sopenharmony_ci			priv->softled_state |= BIT(i);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci			tmp = 70 + 200 / (priv->leds[i].toggled);
438c2ecf20Sopenharmony_ci			if (tmp < blink_delay)
448c2ecf20Sopenharmony_ci				blink_delay = tmp;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci			if (priv->leds[i].led_dev.brightness == LED_OFF)
478c2ecf20Sopenharmony_ci				rerun = true;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci			priv->leds[i].toggled =
508c2ecf20Sopenharmony_ci				!!priv->leds[i].led_dev.brightness;
518c2ecf20Sopenharmony_ci		} else
528c2ecf20Sopenharmony_ci			priv->softled_state &= ~BIT(i);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	err = p54_set_leds(priv);
558c2ecf20Sopenharmony_ci	if (err && net_ratelimit())
568c2ecf20Sopenharmony_ci		wiphy_err(priv->hw->wiphy,
578c2ecf20Sopenharmony_ci			  "failed to update LEDs (%d).\n", err);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (rerun)
608c2ecf20Sopenharmony_ci		ieee80211_queue_delayed_work(priv->hw, &priv->led_work,
618c2ecf20Sopenharmony_ci			msecs_to_jiffies(blink_delay));
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void p54_led_brightness_set(struct led_classdev *led_dev,
658c2ecf20Sopenharmony_ci				   enum led_brightness brightness)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev,
688c2ecf20Sopenharmony_ci					       led_dev);
698c2ecf20Sopenharmony_ci	struct ieee80211_hw *dev = led->hw_dev;
708c2ecf20Sopenharmony_ci	struct p54_common *priv = dev->priv;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
738c2ecf20Sopenharmony_ci		return ;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if ((brightness) && (led->registered)) {
768c2ecf20Sopenharmony_ci		led->toggled++;
778c2ecf20Sopenharmony_ci		ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int p54_register_led(struct p54_common *priv,
828c2ecf20Sopenharmony_ci			    unsigned int led_index,
838c2ecf20Sopenharmony_ci			    char *name, const char *trigger)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct p54_led_dev *led = &priv->leds[led_index];
868c2ecf20Sopenharmony_ci	int err;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (led->registered)
898c2ecf20Sopenharmony_ci		return -EEXIST;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	snprintf(led->name, sizeof(led->name), "p54-%s::%s",
928c2ecf20Sopenharmony_ci		 wiphy_name(priv->hw->wiphy), name);
938c2ecf20Sopenharmony_ci	led->hw_dev = priv->hw;
948c2ecf20Sopenharmony_ci	led->index = led_index;
958c2ecf20Sopenharmony_ci	led->led_dev.name = led->name;
968c2ecf20Sopenharmony_ci	led->led_dev.default_trigger = trigger;
978c2ecf20Sopenharmony_ci	led->led_dev.brightness_set = p54_led_brightness_set;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	err = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_dev);
1008c2ecf20Sopenharmony_ci	if (err)
1018c2ecf20Sopenharmony_ci		wiphy_err(priv->hw->wiphy,
1028c2ecf20Sopenharmony_ci			  "Failed to register %s LED.\n", name);
1038c2ecf20Sopenharmony_ci	else
1048c2ecf20Sopenharmony_ci		led->registered = 1;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return err;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ciint p54_init_leds(struct p54_common *priv)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	int err;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * TODO:
1158c2ecf20Sopenharmony_ci	 * Figure out if the EEPROM contains some hints about the number
1168c2ecf20Sopenharmony_ci	 * of available/programmable LEDs of the device.
1178c2ecf20Sopenharmony_ci	 */
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&priv->led_work, p54_update_leds);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	err = p54_register_led(priv, 0, "assoc",
1228c2ecf20Sopenharmony_ci			       ieee80211_get_assoc_led_name(priv->hw));
1238c2ecf20Sopenharmony_ci	if (err)
1248c2ecf20Sopenharmony_ci		return err;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	err = p54_register_led(priv, 1, "tx",
1278c2ecf20Sopenharmony_ci			       ieee80211_get_tx_led_name(priv->hw));
1288c2ecf20Sopenharmony_ci	if (err)
1298c2ecf20Sopenharmony_ci		return err;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	err = p54_register_led(priv, 2, "rx",
1328c2ecf20Sopenharmony_ci			       ieee80211_get_rx_led_name(priv->hw));
1338c2ecf20Sopenharmony_ci	if (err)
1348c2ecf20Sopenharmony_ci		return err;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	err = p54_register_led(priv, 3, "radio",
1378c2ecf20Sopenharmony_ci			       ieee80211_get_radio_led_name(priv->hw));
1388c2ecf20Sopenharmony_ci	if (err)
1398c2ecf20Sopenharmony_ci		return err;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	err = p54_set_leds(priv);
1428c2ecf20Sopenharmony_ci	return err;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_civoid p54_unregister_leds(struct p54_common *priv)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int i;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(priv->leds); i++) {
1508c2ecf20Sopenharmony_ci		if (priv->leds[i].registered) {
1518c2ecf20Sopenharmony_ci			priv->leds[i].registered = false;
1528c2ecf20Sopenharmony_ci			priv->leds[i].toggled = 0;
1538c2ecf20Sopenharmony_ci			led_classdev_unregister(&priv->leds[i].led_dev);
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&priv->led_work);
1588c2ecf20Sopenharmony_ci}
159