18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Atheros CARL9170 driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * LED handling
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
78c2ecf20Sopenharmony_ci * Copyright 2009, 2010, Christian Lamparer <chunkeey@googlemail.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
108c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
118c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
128c2ecf20Sopenharmony_ci * (at your option) any later version.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful,
158c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
168c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
178c2ecf20Sopenharmony_ci * GNU General Public License for more details.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
208c2ecf20Sopenharmony_ci * along with this program; see the file COPYING.  If not, see
218c2ecf20Sopenharmony_ci * http://www.gnu.org/licenses/.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * This file incorporates work covered by the following copyright and
248c2ecf20Sopenharmony_ci * permission notice:
258c2ecf20Sopenharmony_ci *    Copyright (c) 2007-2008 Atheros Communications, Inc.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *    Permission to use, copy, modify, and/or distribute this software for any
288c2ecf20Sopenharmony_ci *    purpose with or without fee is hereby granted, provided that the above
298c2ecf20Sopenharmony_ci *    copyright notice and this permission notice appear in all copies.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
328c2ecf20Sopenharmony_ci *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
338c2ecf20Sopenharmony_ci *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
348c2ecf20Sopenharmony_ci *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
358c2ecf20Sopenharmony_ci *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
368c2ecf20Sopenharmony_ci *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
378c2ecf20Sopenharmony_ci *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include "carl9170.h"
418c2ecf20Sopenharmony_ci#include "cmd.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ciint carl9170_led_set_state(struct ar9170 *ar, const u32 led_state)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	return carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_DATA, led_state);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciint carl9170_led_init(struct ar9170 *ar)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	int err;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* disable LEDs */
538c2ecf20Sopenharmony_ci	/* GPIO [0/1 mode: output, 2/3: input] */
548c2ecf20Sopenharmony_ci	err = carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3);
558c2ecf20Sopenharmony_ci	if (err)
568c2ecf20Sopenharmony_ci		goto out;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* GPIO 0/1 value: off */
598c2ecf20Sopenharmony_ci	err = carl9170_led_set_state(ar, 0);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciout:
628c2ecf20Sopenharmony_ci	return err;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#ifdef CONFIG_CARL9170_LEDS
668c2ecf20Sopenharmony_cistatic void carl9170_led_update(struct work_struct *work)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct ar9170 *ar = container_of(work, struct ar9170, led_work.work);
698c2ecf20Sopenharmony_ci	int i, tmp = 300, blink_delay = 1000;
708c2ecf20Sopenharmony_ci	u32 led_val = 0;
718c2ecf20Sopenharmony_ci	bool rerun = false;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (!IS_ACCEPTING_CMD(ar))
748c2ecf20Sopenharmony_ci		return;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	mutex_lock(&ar->mutex);
778c2ecf20Sopenharmony_ci	for (i = 0; i < AR9170_NUM_LEDS; i++) {
788c2ecf20Sopenharmony_ci		if (ar->leds[i].registered) {
798c2ecf20Sopenharmony_ci			if (ar->leds[i].last_state ||
808c2ecf20Sopenharmony_ci			    ar->leds[i].toggled) {
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci				if (ar->leds[i].toggled)
838c2ecf20Sopenharmony_ci					tmp = 70 + 200 / (ar->leds[i].toggled);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci				if (tmp < blink_delay)
868c2ecf20Sopenharmony_ci					blink_delay = tmp;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci				led_val |= 1 << i;
898c2ecf20Sopenharmony_ci				ar->leds[i].toggled = 0;
908c2ecf20Sopenharmony_ci				rerun = true;
918c2ecf20Sopenharmony_ci			}
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	carl9170_led_set_state(ar, led_val);
968c2ecf20Sopenharmony_ci	mutex_unlock(&ar->mutex);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (!rerun)
998c2ecf20Sopenharmony_ci		return;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ieee80211_queue_delayed_work(ar->hw,
1028c2ecf20Sopenharmony_ci				     &ar->led_work,
1038c2ecf20Sopenharmony_ci				     msecs_to_jiffies(blink_delay));
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void carl9170_led_set_brightness(struct led_classdev *led,
1078c2ecf20Sopenharmony_ci					enum led_brightness brightness)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct carl9170_led *arl = container_of(led, struct carl9170_led, l);
1108c2ecf20Sopenharmony_ci	struct ar9170 *ar = arl->ar;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (!arl->registered)
1138c2ecf20Sopenharmony_ci		return;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (arl->last_state != !!brightness) {
1168c2ecf20Sopenharmony_ci		arl->toggled++;
1178c2ecf20Sopenharmony_ci		arl->last_state = !!brightness;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled))
1218c2ecf20Sopenharmony_ci		ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ / 10);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int carl9170_led_register_led(struct ar9170 *ar, int i, char *name,
1258c2ecf20Sopenharmony_ci				     const char *trigger)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	int err;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	snprintf(ar->leds[i].name, sizeof(ar->leds[i].name),
1308c2ecf20Sopenharmony_ci		 "carl9170-%s::%s", wiphy_name(ar->hw->wiphy), name);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ar->leds[i].ar = ar;
1338c2ecf20Sopenharmony_ci	ar->leds[i].l.name = ar->leds[i].name;
1348c2ecf20Sopenharmony_ci	ar->leds[i].l.brightness_set = carl9170_led_set_brightness;
1358c2ecf20Sopenharmony_ci	ar->leds[i].l.brightness = 0;
1368c2ecf20Sopenharmony_ci	ar->leds[i].l.default_trigger = trigger;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	err = led_classdev_register(wiphy_dev(ar->hw->wiphy),
1398c2ecf20Sopenharmony_ci				    &ar->leds[i].l);
1408c2ecf20Sopenharmony_ci	if (err) {
1418c2ecf20Sopenharmony_ci		wiphy_err(ar->hw->wiphy, "failed to register %s LED (%d).\n",
1428c2ecf20Sopenharmony_ci			ar->leds[i].name, err);
1438c2ecf20Sopenharmony_ci	} else {
1448c2ecf20Sopenharmony_ci		ar->leds[i].registered = true;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return err;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_civoid carl9170_led_unregister(struct ar9170 *ar)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	int i;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	for (i = 0; i < AR9170_NUM_LEDS; i++)
1558c2ecf20Sopenharmony_ci		if (ar->leds[i].registered) {
1568c2ecf20Sopenharmony_ci			led_classdev_unregister(&ar->leds[i].l);
1578c2ecf20Sopenharmony_ci			ar->leds[i].registered = false;
1588c2ecf20Sopenharmony_ci			ar->leds[i].toggled = 0;
1598c2ecf20Sopenharmony_ci		}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&ar->led_work);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ciint carl9170_led_register(struct ar9170 *ar)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	int err;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&ar->led_work, carl9170_led_update);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	err = carl9170_led_register_led(ar, 0, "tx",
1718c2ecf20Sopenharmony_ci					ieee80211_get_tx_led_name(ar->hw));
1728c2ecf20Sopenharmony_ci	if (err)
1738c2ecf20Sopenharmony_ci		goto fail;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (ar->features & CARL9170_ONE_LED)
1768c2ecf20Sopenharmony_ci		return 0;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	err = carl9170_led_register_led(ar, 1, "assoc",
1798c2ecf20Sopenharmony_ci					ieee80211_get_assoc_led_name(ar->hw));
1808c2ecf20Sopenharmony_ci	if (err)
1818c2ecf20Sopenharmony_ci		goto fail;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cifail:
1868c2ecf20Sopenharmony_ci	carl9170_led_unregister(ar);
1878c2ecf20Sopenharmony_ci	return err;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci#endif /* CONFIG_CARL9170_LEDS */
191