1/*
2 * Atheros CARL9170 driver
3 *
4 * LED handling
5 *
6 * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
7 * Copyright 2009, 2010, Christian Lamparer <chunkeey@googlemail.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; see the file COPYING.  If not, see
21 * http://www.gnu.org/licenses/.
22 *
23 * This file incorporates work covered by the following copyright and
24 * permission notice:
25 *    Copyright (c) 2007-2008 Atheros Communications, Inc.
26 *
27 *    Permission to use, copy, modify, and/or distribute this software for any
28 *    purpose with or without fee is hereby granted, provided that the above
29 *    copyright notice and this permission notice appear in all copies.
30 *
31 *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
32 *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
33 *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
34 *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
35 *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
36 *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
37 *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
38 */
39
40#include "carl9170.h"
41#include "cmd.h"
42
43int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state)
44{
45	return carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_DATA, led_state);
46}
47
48int carl9170_led_init(struct ar9170 *ar)
49{
50	int err;
51
52	/* disable LEDs */
53	/* GPIO [0/1 mode: output, 2/3: input] */
54	err = carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3);
55	if (err)
56		goto out;
57
58	/* GPIO 0/1 value: off */
59	err = carl9170_led_set_state(ar, 0);
60
61out:
62	return err;
63}
64
65#ifdef CONFIG_CARL9170_LEDS
66static void carl9170_led_update(struct work_struct *work)
67{
68	struct ar9170 *ar = container_of(work, struct ar9170, led_work.work);
69	int i, tmp = 300, blink_delay = 1000;
70	u32 led_val = 0;
71	bool rerun = false;
72
73	if (!IS_ACCEPTING_CMD(ar))
74		return;
75
76	mutex_lock(&ar->mutex);
77	for (i = 0; i < AR9170_NUM_LEDS; i++) {
78		if (ar->leds[i].registered) {
79			if (ar->leds[i].last_state ||
80			    ar->leds[i].toggled) {
81
82				if (ar->leds[i].toggled)
83					tmp = 70 + 200 / (ar->leds[i].toggled);
84
85				if (tmp < blink_delay)
86					blink_delay = tmp;
87
88				led_val |= 1 << i;
89				ar->leds[i].toggled = 0;
90				rerun = true;
91			}
92		}
93	}
94
95	carl9170_led_set_state(ar, led_val);
96	mutex_unlock(&ar->mutex);
97
98	if (!rerun)
99		return;
100
101	ieee80211_queue_delayed_work(ar->hw,
102				     &ar->led_work,
103				     msecs_to_jiffies(blink_delay));
104}
105
106static void carl9170_led_set_brightness(struct led_classdev *led,
107					enum led_brightness brightness)
108{
109	struct carl9170_led *arl = container_of(led, struct carl9170_led, l);
110	struct ar9170 *ar = arl->ar;
111
112	if (!arl->registered)
113		return;
114
115	if (arl->last_state != !!brightness) {
116		arl->toggled++;
117		arl->last_state = !!brightness;
118	}
119
120	if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled))
121		ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ / 10);
122}
123
124static int carl9170_led_register_led(struct ar9170 *ar, int i, char *name,
125				     const char *trigger)
126{
127	int err;
128
129	snprintf(ar->leds[i].name, sizeof(ar->leds[i].name),
130		 "carl9170-%s::%s", wiphy_name(ar->hw->wiphy), name);
131
132	ar->leds[i].ar = ar;
133	ar->leds[i].l.name = ar->leds[i].name;
134	ar->leds[i].l.brightness_set = carl9170_led_set_brightness;
135	ar->leds[i].l.brightness = 0;
136	ar->leds[i].l.default_trigger = trigger;
137
138	err = led_classdev_register(wiphy_dev(ar->hw->wiphy),
139				    &ar->leds[i].l);
140	if (err) {
141		wiphy_err(ar->hw->wiphy, "failed to register %s LED (%d).\n",
142			ar->leds[i].name, err);
143	} else {
144		ar->leds[i].registered = true;
145	}
146
147	return err;
148}
149
150void carl9170_led_unregister(struct ar9170 *ar)
151{
152	int i;
153
154	for (i = 0; i < AR9170_NUM_LEDS; i++)
155		if (ar->leds[i].registered) {
156			led_classdev_unregister(&ar->leds[i].l);
157			ar->leds[i].registered = false;
158			ar->leds[i].toggled = 0;
159		}
160
161	cancel_delayed_work_sync(&ar->led_work);
162}
163
164int carl9170_led_register(struct ar9170 *ar)
165{
166	int err;
167
168	INIT_DELAYED_WORK(&ar->led_work, carl9170_led_update);
169
170	err = carl9170_led_register_led(ar, 0, "tx",
171					ieee80211_get_tx_led_name(ar->hw));
172	if (err)
173		goto fail;
174
175	if (ar->features & CARL9170_ONE_LED)
176		return 0;
177
178	err = carl9170_led_register_led(ar, 1, "assoc",
179					ieee80211_get_assoc_led_name(ar->hw));
180	if (err)
181		goto fail;
182
183	return 0;
184
185fail:
186	carl9170_led_unregister(ar);
187	return err;
188}
189
190#endif /* CONFIG_CARL9170_LEDS */
191