1 /*
2  * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
3  * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  */
18 
19 /****************\
20   GPIO Functions
21 \****************/
22 
23 #include "ath5k.h"
24 #include "reg.h"
25 #include "debug.h"
26 
27 
28 /**
29  * DOC: GPIO/LED functions
30  *
31  * Here we control the 6 bidirectional GPIO pins provided by the hw.
32  * We can set a GPIO pin to be an input or an output pin on GPIO control
33  * register and then read or set its status from GPIO data input/output
34  * registers.
35  *
36  * We also control the two LED pins provided by the hw, LED_0 is our
37  * "power" LED and LED_1 is our "network activity" LED but many scenarios
38  * are available from hw. Vendors might also provide LEDs connected to the
39  * GPIO pins, we handle them through the LED subsystem on led.c
40  */
41 
42 
43 /**
44  * ath5k_hw_set_ledstate() - Set led state
45  * @ah: The &struct ath5k_hw
46  * @state: One of AR5K_LED_*
47  *
48  * Used to set the LED blinking state. This only
49  * works for the LED connected to the LED_0, LED_1 pins,
50  * not the GPIO based.
51  */
52 void
ath5k_hw_set_ledstate(struct ath5k_hw * ah,unsigned int state)53 ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state)
54 {
55 	u32 led;
56 	/*5210 has different led mode handling*/
57 	u32 led_5210;
58 
59 	/*Reset led status*/
60 	if (ah->ah_version != AR5K_AR5210)
61 		AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG,
62 			AR5K_PCICFG_LEDMODE |  AR5K_PCICFG_LED);
63 	else
64 		AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_LED);
65 
66 	/*
67 	 * Some blinking values, define at your wish
68 	 */
69 	switch (state) {
70 	case AR5K_LED_SCAN:
71 	case AR5K_LED_AUTH:
72 		led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND;
73 		led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL;
74 		break;
75 
76 	case AR5K_LED_INIT:
77 		led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE;
78 		led_5210 = AR5K_PCICFG_LED_PEND;
79 		break;
80 
81 	case AR5K_LED_ASSOC:
82 	case AR5K_LED_RUN:
83 		led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC;
84 		led_5210 = AR5K_PCICFG_LED_ASSOC;
85 		break;
86 
87 	default:
88 		led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE;
89 		led_5210 = AR5K_PCICFG_LED_PEND;
90 		break;
91 	}
92 
93 	/*Write new status to the register*/
94 	if (ah->ah_version != AR5K_AR5210)
95 		AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led);
96 	else
97 		AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led_5210);
98 }
99 
100 /**
101  * ath5k_hw_set_gpio_input() - Set GPIO inputs
102  * @ah: The &struct ath5k_hw
103  * @gpio: GPIO pin to set as input
104  */
105 int
ath5k_hw_set_gpio_input(struct ath5k_hw * ah,u32 gpio)106 ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio)
107 {
108 	if (gpio >= AR5K_NUM_GPIO)
109 		return -EINVAL;
110 
111 	ath5k_hw_reg_write(ah,
112 		(ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
113 		| AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR);
114 
115 	return 0;
116 }
117 
118 /**
119  * ath5k_hw_set_gpio_output() - Set GPIO outputs
120  * @ah: The &struct ath5k_hw
121  * @gpio: The GPIO pin to set as output
122  */
123 int
ath5k_hw_set_gpio_output(struct ath5k_hw * ah,u32 gpio)124 ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio)
125 {
126 	if (gpio >= AR5K_NUM_GPIO)
127 		return -EINVAL;
128 
129 	ath5k_hw_reg_write(ah,
130 		(ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
131 		| AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR);
132 
133 	return 0;
134 }
135 
136 /**
137  * ath5k_hw_get_gpio() - Get GPIO state
138  * @ah: The &struct ath5k_hw
139  * @gpio: The GPIO pin to read
140  */
141 u32
ath5k_hw_get_gpio(struct ath5k_hw * ah,u32 gpio)142 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio)
143 {
144 	if (gpio >= AR5K_NUM_GPIO)
145 		return 0xffffffff;
146 
147 	/* GPIO input magic */
148 	return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) &
149 		0x1;
150 }
151 
152 /**
153  * ath5k_hw_set_gpio() - Set GPIO state
154  * @ah: The &struct ath5k_hw
155  * @gpio: The GPIO pin to set
156  * @val: Value to set (boolean)
157  */
158 int
ath5k_hw_set_gpio(struct ath5k_hw * ah,u32 gpio,u32 val)159 ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val)
160 {
161 	u32 data;
162 
163 	if (gpio >= AR5K_NUM_GPIO)
164 		return -EINVAL;
165 
166 	/* GPIO output magic */
167 	data = ath5k_hw_reg_read(ah, AR5K_GPIODO);
168 
169 	data &= ~(1 << gpio);
170 	data |= (val & 1) << gpio;
171 
172 	ath5k_hw_reg_write(ah, data, AR5K_GPIODO);
173 
174 	return 0;
175 }
176 
177 /**
178  * ath5k_hw_set_gpio_intr() - Initialize the GPIO interrupt (RFKill switch)
179  * @ah: The &struct ath5k_hw
180  * @gpio: The GPIO pin to use
181  * @interrupt_level: True to generate interrupt on active pin (high)
182  *
183  * This function is used to set up the GPIO interrupt for the hw RFKill switch.
184  * That switch is connected to a GPIO pin and it's number is stored on EEPROM.
185  * It can either open or close the circuit to indicate that we should disable
186  * RF/Wireless to save power (we also get that from EEPROM).
187  */
188 void
ath5k_hw_set_gpio_intr(struct ath5k_hw * ah,unsigned int gpio,u32 interrupt_level)189 ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio,
190 		u32 interrupt_level)
191 {
192 	u32 data;
193 
194 	if (gpio >= AR5K_NUM_GPIO)
195 		return;
196 
197 	/*
198 	 * Set the GPIO interrupt
199 	 */
200 	data = (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &
201 		~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH |
202 		AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) |
203 		(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA);
204 
205 	ath5k_hw_reg_write(ah, interrupt_level ? data :
206 		(data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR);
207 
208 	ah->ah_imr |= AR5K_IMR_GPIO;
209 
210 	/* Enable GPIO interrupts */
211 	AR5K_REG_ENABLE_BITS(ah, AR5K_PIMR, AR5K_IMR_GPIO);
212 }
213 
214