xref: /onnv-gate/usr/src/uts/common/io/rwn/rt2860.c (revision 9172:e625dee308d1)
1*9172SFei.Feng@Sun.COM /*
2*9172SFei.Feng@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3*9172SFei.Feng@Sun.COM  * Use is subject to license terms.
4*9172SFei.Feng@Sun.COM  */
5*9172SFei.Feng@Sun.COM 
6*9172SFei.Feng@Sun.COM /*
7*9172SFei.Feng@Sun.COM  * Copyright (c) 2007, 2008
8*9172SFei.Feng@Sun.COM  *	Damien Bergamini <damien.bergamini@free.fr>
9*9172SFei.Feng@Sun.COM  *
10*9172SFei.Feng@Sun.COM  * Permission to use, copy, modify, and distribute this software for any
11*9172SFei.Feng@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
12*9172SFei.Feng@Sun.COM  * copyright notice and this permission notice appear in all copies.
13*9172SFei.Feng@Sun.COM  *
14*9172SFei.Feng@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15*9172SFei.Feng@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16*9172SFei.Feng@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17*9172SFei.Feng@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18*9172SFei.Feng@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19*9172SFei.Feng@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20*9172SFei.Feng@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21*9172SFei.Feng@Sun.COM  */
22*9172SFei.Feng@Sun.COM 
23*9172SFei.Feng@Sun.COM /*
24*9172SFei.Feng@Sun.COM  * Ralink Technology RT2860 chipset driver
25*9172SFei.Feng@Sun.COM  * http://www.ralinktech.com/
26*9172SFei.Feng@Sun.COM  */
27*9172SFei.Feng@Sun.COM 
28*9172SFei.Feng@Sun.COM #include <sys/types.h>
29*9172SFei.Feng@Sun.COM #include <sys/byteorder.h>
30*9172SFei.Feng@Sun.COM #include <sys/conf.h>
31*9172SFei.Feng@Sun.COM #include <sys/cmn_err.h>
32*9172SFei.Feng@Sun.COM #include <sys/stat.h>
33*9172SFei.Feng@Sun.COM #include <sys/ddi.h>
34*9172SFei.Feng@Sun.COM #include <sys/sunddi.h>
35*9172SFei.Feng@Sun.COM #include <sys/strsubr.h>
36*9172SFei.Feng@Sun.COM #include <inet/common.h>
37*9172SFei.Feng@Sun.COM #include <sys/note.h>
38*9172SFei.Feng@Sun.COM #include <sys/stream.h>
39*9172SFei.Feng@Sun.COM #include <sys/strsun.h>
40*9172SFei.Feng@Sun.COM #include <sys/modctl.h>
41*9172SFei.Feng@Sun.COM #include <sys/devops.h>
42*9172SFei.Feng@Sun.COM #include <sys/mac_provider.h>
43*9172SFei.Feng@Sun.COM #include <sys/mac_wifi.h>
44*9172SFei.Feng@Sun.COM #include <sys/net80211.h>
45*9172SFei.Feng@Sun.COM #include <sys/net80211_proto.h>
46*9172SFei.Feng@Sun.COM #include <sys/varargs.h>
47*9172SFei.Feng@Sun.COM #include <sys/pci.h>
48*9172SFei.Feng@Sun.COM #include <sys/crypto/common.h>
49*9172SFei.Feng@Sun.COM #include <sys/crypto/api.h>
50*9172SFei.Feng@Sun.COM #include <inet/wifi_ioctl.h>
51*9172SFei.Feng@Sun.COM 
52*9172SFei.Feng@Sun.COM #include "rt2860_reg.h"
53*9172SFei.Feng@Sun.COM #include "rt2860_var.h"
54*9172SFei.Feng@Sun.COM 
55*9172SFei.Feng@Sun.COM #define	RT2860_DBG_80211	(1 << 0)
56*9172SFei.Feng@Sun.COM #define	RT2860_DBG_DMA		(1 << 1)
57*9172SFei.Feng@Sun.COM #define	RT2860_DBG_EEPROM	(1 << 2)
58*9172SFei.Feng@Sun.COM #define	RT2860_DBG_FW		(1 << 3)
59*9172SFei.Feng@Sun.COM #define	RT2860_DBG_HW		(1 << 4)
60*9172SFei.Feng@Sun.COM #define	RT2860_DBG_INTR		(1 << 5)
61*9172SFei.Feng@Sun.COM #define	RT2860_DBG_RX		(1 << 6)
62*9172SFei.Feng@Sun.COM #define	RT2860_DBG_SCAN		(1 << 7)
63*9172SFei.Feng@Sun.COM #define	RT2860_DBG_TX		(1 << 8)
64*9172SFei.Feng@Sun.COM #define	RT2860_DBG_RADIO	(1 << 9)
65*9172SFei.Feng@Sun.COM #define	RT2860_DBG_RESUME	(1 << 10)
66*9172SFei.Feng@Sun.COM #define	RT2860_DBG_MSG		(1 << 11)
67*9172SFei.Feng@Sun.COM 
68*9172SFei.Feng@Sun.COM uint32_t rt2860_dbg_flags = 0x0;
69*9172SFei.Feng@Sun.COM 
70*9172SFei.Feng@Sun.COM #ifdef DEBUG
71*9172SFei.Feng@Sun.COM #define	RWN_DEBUG \
72*9172SFei.Feng@Sun.COM 	rt2860_debug
73*9172SFei.Feng@Sun.COM #else
74*9172SFei.Feng@Sun.COM #define	RWN_DEBUG
75*9172SFei.Feng@Sun.COM #endif
76*9172SFei.Feng@Sun.COM 
77*9172SFei.Feng@Sun.COM static void *rt2860_soft_state_p = NULL;
78*9172SFei.Feng@Sun.COM static uint8_t rt2860_fw_bin [] = {
79*9172SFei.Feng@Sun.COM #include "fw-rt2860/rt2860.ucode"
80*9172SFei.Feng@Sun.COM };
81*9172SFei.Feng@Sun.COM 
82*9172SFei.Feng@Sun.COM static const struct ieee80211_rateset rt2560_rateset_11b =
83*9172SFei.Feng@Sun.COM 	{ 4, { 2, 4, 11, 22 } };
84*9172SFei.Feng@Sun.COM 
85*9172SFei.Feng@Sun.COM static const struct ieee80211_rateset rt2560_rateset_11g =
86*9172SFei.Feng@Sun.COM 	{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
87*9172SFei.Feng@Sun.COM 
88*9172SFei.Feng@Sun.COM static const struct {
89*9172SFei.Feng@Sun.COM 	uint32_t	reg;
90*9172SFei.Feng@Sun.COM 	uint32_t	val;
91*9172SFei.Feng@Sun.COM } rt2860_def_mac[] = {
92*9172SFei.Feng@Sun.COM 	RT2860_DEF_MAC
93*9172SFei.Feng@Sun.COM };
94*9172SFei.Feng@Sun.COM 
95*9172SFei.Feng@Sun.COM static const struct {
96*9172SFei.Feng@Sun.COM 	uint8_t	reg;
97*9172SFei.Feng@Sun.COM 	uint8_t	val;
98*9172SFei.Feng@Sun.COM } rt2860_def_bbp[] = {
99*9172SFei.Feng@Sun.COM 	RT2860_DEF_BBP
100*9172SFei.Feng@Sun.COM };
101*9172SFei.Feng@Sun.COM 
102*9172SFei.Feng@Sun.COM static const struct rfprog {
103*9172SFei.Feng@Sun.COM 	uint8_t		chan;
104*9172SFei.Feng@Sun.COM 	uint32_t	r1, r2, r3, r4;
105*9172SFei.Feng@Sun.COM } rt2860_rf2850[] = {
106*9172SFei.Feng@Sun.COM 	RT2860_RF2850
107*9172SFei.Feng@Sun.COM };
108*9172SFei.Feng@Sun.COM 
109*9172SFei.Feng@Sun.COM /*
110*9172SFei.Feng@Sun.COM  * PIO access attributes for registers
111*9172SFei.Feng@Sun.COM  */
112*9172SFei.Feng@Sun.COM static ddi_device_acc_attr_t rwn_csr_accattr = {
113*9172SFei.Feng@Sun.COM 	DDI_DEVICE_ATTR_V0,
114*9172SFei.Feng@Sun.COM 	DDI_STRUCTURE_LE_ACC,
115*9172SFei.Feng@Sun.COM 	DDI_STRICTORDER_ACC
116*9172SFei.Feng@Sun.COM };
117*9172SFei.Feng@Sun.COM 
118*9172SFei.Feng@Sun.COM /*
119*9172SFei.Feng@Sun.COM  * DMA access attributes for descriptors: NOT to be byte swapped.
120*9172SFei.Feng@Sun.COM  */
121*9172SFei.Feng@Sun.COM static ddi_device_acc_attr_t rt2860_desc_accattr = {
122*9172SFei.Feng@Sun.COM 	DDI_DEVICE_ATTR_V0,
123*9172SFei.Feng@Sun.COM 	DDI_STRUCTURE_LE_ACC,
124*9172SFei.Feng@Sun.COM 	DDI_STRICTORDER_ACC
125*9172SFei.Feng@Sun.COM };
126*9172SFei.Feng@Sun.COM 
127*9172SFei.Feng@Sun.COM static ddi_device_acc_attr_t rt2860_buf_accattr = {
128*9172SFei.Feng@Sun.COM 	DDI_DEVICE_ATTR_V0,
129*9172SFei.Feng@Sun.COM 	DDI_NEVERSWAP_ACC,
130*9172SFei.Feng@Sun.COM 	DDI_STRICTORDER_ACC,
131*9172SFei.Feng@Sun.COM 	DDI_DEFAULT_ACC
132*9172SFei.Feng@Sun.COM };
133*9172SFei.Feng@Sun.COM 
134*9172SFei.Feng@Sun.COM /*
135*9172SFei.Feng@Sun.COM  * Describes the chip's DMA engine
136*9172SFei.Feng@Sun.COM  */
137*9172SFei.Feng@Sun.COM static ddi_dma_attr_t rt2860_dma_attr = {
138*9172SFei.Feng@Sun.COM 	DMA_ATTR_V0,			/* dma_attr version */
139*9172SFei.Feng@Sun.COM 	0x0,				/* dma_attr_addr_lo */
140*9172SFei.Feng@Sun.COM 	0xffffffffU,			/* dma_attr_addr_hi */
141*9172SFei.Feng@Sun.COM 	0xffffffffU,			/* dma_attr_count_max */
142*9172SFei.Feng@Sun.COM 	16,				/* dma_attr_align */
143*9172SFei.Feng@Sun.COM 	0x00000fff,			/* dma_attr_burstsizes */
144*9172SFei.Feng@Sun.COM 	1,				/* dma_attr_minxfer */
145*9172SFei.Feng@Sun.COM 	0xffffffffU,			/* dma_attr_maxxfer */
146*9172SFei.Feng@Sun.COM 	0xffffffffU,			/* dma_attr_seg */
147*9172SFei.Feng@Sun.COM 	1,				/* dma_attr_sgllen */
148*9172SFei.Feng@Sun.COM 	1,				/* dma_attr_granular */
149*9172SFei.Feng@Sun.COM 	0				/* dma_attr_flags */
150*9172SFei.Feng@Sun.COM };
151*9172SFei.Feng@Sun.COM 
152*9172SFei.Feng@Sun.COM static uint16_t	rt2860_eeprom_read(struct rt2860_softc *, uint8_t);
153*9172SFei.Feng@Sun.COM static int 	rt2860_read_eeprom(struct rt2860_softc *);
154*9172SFei.Feng@Sun.COM const char	*rt2860_get_rf(uint8_t);
155*9172SFei.Feng@Sun.COM static int	rt2860_alloc_dma_mem(dev_info_t *, ddi_dma_attr_t *, size_t,
156*9172SFei.Feng@Sun.COM 		    ddi_device_acc_attr_t *, uint_t, uint_t, struct dma_area *);
157*9172SFei.Feng@Sun.COM static void	rt2860_free_dma_mem(struct dma_area *);
158*9172SFei.Feng@Sun.COM static int	rt2860_alloc_tx_ring(struct rt2860_softc *,
159*9172SFei.Feng@Sun.COM 		    struct rt2860_tx_ring *);
160*9172SFei.Feng@Sun.COM static void	rt2860_free_tx_ring(struct rt2860_softc *,
161*9172SFei.Feng@Sun.COM 		    struct rt2860_tx_ring *);
162*9172SFei.Feng@Sun.COM static int	rt2860_alloc_rx_ring(struct rt2860_softc *,
163*9172SFei.Feng@Sun.COM 		    struct rt2860_rx_ring *);
164*9172SFei.Feng@Sun.COM static void	rt2860_free_rx_ring(struct rt2860_softc *,
165*9172SFei.Feng@Sun.COM 		    struct rt2860_rx_ring *);
166*9172SFei.Feng@Sun.COM static int	rt2860_alloc_tx_pool(struct rt2860_softc *);
167*9172SFei.Feng@Sun.COM static void	rt2860_free_tx_pool(struct rt2860_softc *);
168*9172SFei.Feng@Sun.COM static uint16_t	rt2860_txtime(int, int, uint32_t);
169*9172SFei.Feng@Sun.COM static int	rt2860_ack_rate(struct ieee80211com *, int);
170*9172SFei.Feng@Sun.COM static int	rt2860_send(ieee80211com_t *, mblk_t *, uint8_t);
171*9172SFei.Feng@Sun.COM static uint8_t	rt2860_maxrssi_chain(struct rt2860_softc *,
172*9172SFei.Feng@Sun.COM 		    const struct rt2860_rxwi *);
173*9172SFei.Feng@Sun.COM static void	rt2860_drain_stats_fifo(struct rt2860_softc *);
174*9172SFei.Feng@Sun.COM static void	rt2860_tx_intr(struct rt2860_softc *, int);
175*9172SFei.Feng@Sun.COM static void	rt2860_rx_intr(struct rt2860_softc *);
176*9172SFei.Feng@Sun.COM static uint_t	rt2860_softintr(caddr_t);
177*9172SFei.Feng@Sun.COM static uint_t	rt2860_intr(caddr_t);
178*9172SFei.Feng@Sun.COM static void	rt2860_set_region_4(struct rt2860_softc *,
179*9172SFei.Feng@Sun.COM 		    uint32_t, uint32_t, int);
180*9172SFei.Feng@Sun.COM static int	rt2860_load_microcode(struct rt2860_softc *);
181*9172SFei.Feng@Sun.COM static void	rt2860_set_macaddr(struct rt2860_softc *, const uint8_t *);
182*9172SFei.Feng@Sun.COM static int	rt2860_bbp_init(struct rt2860_softc *);
183*9172SFei.Feng@Sun.COM static uint8_t	rt2860_mcu_bbp_read(struct rt2860_softc *, uint8_t);
184*9172SFei.Feng@Sun.COM static void	rt2860_mcu_bbp_write(struct rt2860_softc *, uint8_t, uint8_t);
185*9172SFei.Feng@Sun.COM static int	rt2860_mcu_cmd(struct rt2860_softc *, uint8_t, uint16_t);
186*9172SFei.Feng@Sun.COM static void	rt2860_rf_write(struct rt2860_softc *, uint8_t, uint32_t);
187*9172SFei.Feng@Sun.COM static void	rt2860_select_chan_group(struct rt2860_softc *, int);
188*9172SFei.Feng@Sun.COM static void	rt2860_set_chan(struct rt2860_softc *,
189*9172SFei.Feng@Sun.COM 		    struct ieee80211_channel *);
190*9172SFei.Feng@Sun.COM static void	rt2860_updateprot(struct ieee80211com *);
191*9172SFei.Feng@Sun.COM static void	rt2860_set_leds(struct rt2860_softc *, uint16_t);
192*9172SFei.Feng@Sun.COM static void	rt2860_next_scan(void *);
193*9172SFei.Feng@Sun.COM static void	rt2860_iter_func(void *, struct ieee80211_node *);
194*9172SFei.Feng@Sun.COM static void	rt2860_updateslot(struct rt2860_softc *);
195*9172SFei.Feng@Sun.COM static uint8_t	rt2860_rate2mcs(uint8_t);
196*9172SFei.Feng@Sun.COM static void	rt2860_enable_mrr(struct rt2860_softc *);
197*9172SFei.Feng@Sun.COM static void	rt2860_set_txpreamble(struct rt2860_softc *);
198*9172SFei.Feng@Sun.COM static void	rt2860_set_basicrates(struct rt2860_softc *);
199*9172SFei.Feng@Sun.COM static void	rt2860_set_bssid(struct rt2860_softc *, const uint8_t *);
200*9172SFei.Feng@Sun.COM static void	rt2860_amrr_node_init(const struct rt2860_amrr *,
201*9172SFei.Feng@Sun.COM 		    struct rt2860_amrr_node *);
202*9172SFei.Feng@Sun.COM static void	rt2860_amrr_choose(struct rt2860_amrr *,
203*9172SFei.Feng@Sun.COM 		    struct ieee80211_node *, struct rt2860_amrr_node *);
204*9172SFei.Feng@Sun.COM static void	rt2860_newassoc(struct ieee80211com *, struct ieee80211_node *,
205*9172SFei.Feng@Sun.COM 		    int);
206*9172SFei.Feng@Sun.COM static void	rt2860_enable_tsf_sync(struct rt2860_softc *);
207*9172SFei.Feng@Sun.COM static int	rt2860_newstate(struct ieee80211com *,
208*9172SFei.Feng@Sun.COM 		    enum ieee80211_state, int);
209*9172SFei.Feng@Sun.COM static int	rt2860_init(struct rt2860_softc *);
210*9172SFei.Feng@Sun.COM static void	rt2860_stop(struct rt2860_softc *);
211*9172SFei.Feng@Sun.COM static int	rt2860_quiesce(dev_info_t *t);
212*9172SFei.Feng@Sun.COM 
213*9172SFei.Feng@Sun.COM /*
214*9172SFei.Feng@Sun.COM  * device operations
215*9172SFei.Feng@Sun.COM  */
216*9172SFei.Feng@Sun.COM static int rt2860_attach(dev_info_t *, ddi_attach_cmd_t);
217*9172SFei.Feng@Sun.COM static int rt2860_detach(dev_info_t *, ddi_detach_cmd_t);
218*9172SFei.Feng@Sun.COM 
219*9172SFei.Feng@Sun.COM /*
220*9172SFei.Feng@Sun.COM  * Module Loading Data & Entry Points
221*9172SFei.Feng@Sun.COM  */
222*9172SFei.Feng@Sun.COM DDI_DEFINE_STREAM_OPS(rwn_dev_ops, nulldev, nulldev, rt2860_attach,
223*9172SFei.Feng@Sun.COM     rt2860_detach, nodev, NULL, D_MP, NULL, rt2860_quiesce);
224*9172SFei.Feng@Sun.COM 
225*9172SFei.Feng@Sun.COM static struct modldrv rwn_modldrv = {
226*9172SFei.Feng@Sun.COM 	&mod_driverops,		/* Type of module.  This one is a driver */
227*9172SFei.Feng@Sun.COM 	"Ralink RT2700/2800 driver v1.1",	/* short description */
228*9172SFei.Feng@Sun.COM 	&rwn_dev_ops		/* driver specific ops */
229*9172SFei.Feng@Sun.COM };
230*9172SFei.Feng@Sun.COM 
231*9172SFei.Feng@Sun.COM static struct modlinkage modlinkage = {
232*9172SFei.Feng@Sun.COM 	MODREV_1,
233*9172SFei.Feng@Sun.COM 	(void *)&rwn_modldrv,
234*9172SFei.Feng@Sun.COM 	NULL
235*9172SFei.Feng@Sun.COM };
236*9172SFei.Feng@Sun.COM 
237*9172SFei.Feng@Sun.COM static int	rt2860_m_stat(void *,  uint_t, uint64_t *);
238*9172SFei.Feng@Sun.COM static int	rt2860_m_start(void *);
239*9172SFei.Feng@Sun.COM static void	rt2860_m_stop(void *);
240*9172SFei.Feng@Sun.COM static int	rt2860_m_promisc(void *, boolean_t);
241*9172SFei.Feng@Sun.COM static int	rt2860_m_multicst(void *, boolean_t, const uint8_t *);
242*9172SFei.Feng@Sun.COM static int	rt2860_m_unicst(void *, const uint8_t *);
243*9172SFei.Feng@Sun.COM static mblk_t	*rt2860_m_tx(void *, mblk_t *);
244*9172SFei.Feng@Sun.COM static void	rt2860_m_ioctl(void *, queue_t *, mblk_t *);
245*9172SFei.Feng@Sun.COM static int	rt2860_m_setprop(void *arg, const char *pr_name,
246*9172SFei.Feng@Sun.COM 		    mac_prop_id_t wldp_pr_num,
247*9172SFei.Feng@Sun.COM 		    uint_t wldp_length, const void *wldp_buf);
248*9172SFei.Feng@Sun.COM static int	rt2860_m_getprop(void *arg, const char *pr_name,
249*9172SFei.Feng@Sun.COM 		    mac_prop_id_t wldp_pr_num, uint_t pr_flags,
250*9172SFei.Feng@Sun.COM 		    uint_t wldp_length, void *wldp_buf, uint_t *);
251*9172SFei.Feng@Sun.COM 
252*9172SFei.Feng@Sun.COM static mac_callbacks_t rt2860_m_callbacks = {
253*9172SFei.Feng@Sun.COM 	MC_IOCTL | MC_SETPROP | MC_GETPROP,
254*9172SFei.Feng@Sun.COM 	rt2860_m_stat,
255*9172SFei.Feng@Sun.COM 	rt2860_m_start,
256*9172SFei.Feng@Sun.COM 	rt2860_m_stop,
257*9172SFei.Feng@Sun.COM 	rt2860_m_promisc,
258*9172SFei.Feng@Sun.COM 	rt2860_m_multicst,
259*9172SFei.Feng@Sun.COM 	rt2860_m_unicst,
260*9172SFei.Feng@Sun.COM 	rt2860_m_tx,
261*9172SFei.Feng@Sun.COM 	rt2860_m_ioctl,
262*9172SFei.Feng@Sun.COM 	NULL,
263*9172SFei.Feng@Sun.COM 	NULL,
264*9172SFei.Feng@Sun.COM 	NULL,
265*9172SFei.Feng@Sun.COM 	rt2860_m_setprop,
266*9172SFei.Feng@Sun.COM 	rt2860_m_getprop
267*9172SFei.Feng@Sun.COM };
268*9172SFei.Feng@Sun.COM 
269*9172SFei.Feng@Sun.COM #ifdef DEBUG
270*9172SFei.Feng@Sun.COM void
271*9172SFei.Feng@Sun.COM rt2860_debug(uint32_t dbg_flags, const int8_t *fmt, ...)
272*9172SFei.Feng@Sun.COM {
273*9172SFei.Feng@Sun.COM 	va_list args;
274*9172SFei.Feng@Sun.COM 
275*9172SFei.Feng@Sun.COM 	if (dbg_flags & rt2860_dbg_flags) {
276*9172SFei.Feng@Sun.COM 		va_start(args, fmt);
277*9172SFei.Feng@Sun.COM 		vcmn_err(CE_CONT, fmt, args);
278*9172SFei.Feng@Sun.COM 		va_end(args);
279*9172SFei.Feng@Sun.COM 	}
280*9172SFei.Feng@Sun.COM }
281*9172SFei.Feng@Sun.COM #endif
282*9172SFei.Feng@Sun.COM 
283*9172SFei.Feng@Sun.COM const char *
284*9172SFei.Feng@Sun.COM rt2860_get_rf(uint8_t rev)
285*9172SFei.Feng@Sun.COM {
286*9172SFei.Feng@Sun.COM 	switch (rev) {
287*9172SFei.Feng@Sun.COM 	case RT2860_RF_2820:	return "RT2820";
288*9172SFei.Feng@Sun.COM 	case RT2860_RF_2850:	return "RT2850";
289*9172SFei.Feng@Sun.COM 	case RT2860_RF_2720:	return "RT2720";
290*9172SFei.Feng@Sun.COM 	case RT2860_RF_2750:	return "RT2750";
291*9172SFei.Feng@Sun.COM 	default:		return "unknown";
292*9172SFei.Feng@Sun.COM 	}
293*9172SFei.Feng@Sun.COM }
294*9172SFei.Feng@Sun.COM 
295*9172SFei.Feng@Sun.COM /*
296*9172SFei.Feng@Sun.COM  * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46,
297*9172SFei.Feng@Sun.COM  * 93C66 or 93C86).
298*9172SFei.Feng@Sun.COM  */
299*9172SFei.Feng@Sun.COM static uint16_t
300*9172SFei.Feng@Sun.COM rt2860_eeprom_read(struct rt2860_softc *sc, uint8_t addr)
301*9172SFei.Feng@Sun.COM {
302*9172SFei.Feng@Sun.COM 	int		n;
303*9172SFei.Feng@Sun.COM 	uint16_t	val;
304*9172SFei.Feng@Sun.COM 	uint32_t	tmp;
305*9172SFei.Feng@Sun.COM 
306*9172SFei.Feng@Sun.COM 	/* clock C once before the first command */
307*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, 0);
308*9172SFei.Feng@Sun.COM 
309*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S);
310*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C);
311*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S);
312*9172SFei.Feng@Sun.COM 
313*9172SFei.Feng@Sun.COM 	/* write start bit (1) */
314*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D);
315*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C);
316*9172SFei.Feng@Sun.COM 
317*9172SFei.Feng@Sun.COM 	/* write READ opcode (10) */
318*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D);
319*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C);
320*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S);
321*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C);
322*9172SFei.Feng@Sun.COM 
323*9172SFei.Feng@Sun.COM 	/* write address (A5-A0 or A7-A0) */
324*9172SFei.Feng@Sun.COM 	n = ((RT2860_READ(sc, RT2860_PCI_EECTRL) & 0x30) == 0) ? 5 : 7;
325*9172SFei.Feng@Sun.COM 	for (; n >= 0; n--) {
326*9172SFei.Feng@Sun.COM 		RT2860_EEPROM_CTL(sc, RT2860_S |
327*9172SFei.Feng@Sun.COM 		    (((addr >> n) & 1) << RT2860_SHIFT_D));
328*9172SFei.Feng@Sun.COM 		RT2860_EEPROM_CTL(sc, RT2860_S |
329*9172SFei.Feng@Sun.COM 		    (((addr >> n) & 1) << RT2860_SHIFT_D) | RT2860_C);
330*9172SFei.Feng@Sun.COM 	}
331*9172SFei.Feng@Sun.COM 
332*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S);
333*9172SFei.Feng@Sun.COM 
334*9172SFei.Feng@Sun.COM 	/* read data Q15-Q0 */
335*9172SFei.Feng@Sun.COM 	val = 0;
336*9172SFei.Feng@Sun.COM 	for (n = 15; n >= 0; n--) {
337*9172SFei.Feng@Sun.COM 		RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C);
338*9172SFei.Feng@Sun.COM 		tmp = RT2860_READ(sc, RT2860_PCI_EECTRL);
339*9172SFei.Feng@Sun.COM 		val |= ((tmp & RT2860_Q) >> RT2860_SHIFT_Q) << n;
340*9172SFei.Feng@Sun.COM 		RT2860_EEPROM_CTL(sc, RT2860_S);
341*9172SFei.Feng@Sun.COM 	}
342*9172SFei.Feng@Sun.COM 
343*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, 0);
344*9172SFei.Feng@Sun.COM 
345*9172SFei.Feng@Sun.COM 	/* clear Chip Select and clock C */
346*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_S);
347*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, 0);
348*9172SFei.Feng@Sun.COM 	RT2860_EEPROM_CTL(sc, RT2860_C);
349*9172SFei.Feng@Sun.COM 
350*9172SFei.Feng@Sun.COM 	return (val);
351*9172SFei.Feng@Sun.COM }
352*9172SFei.Feng@Sun.COM 
353*9172SFei.Feng@Sun.COM /*
354*9172SFei.Feng@Sun.COM  * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word.
355*9172SFei.Feng@Sun.COM  * Used to adjust per-rate Tx power registers.
356*9172SFei.Feng@Sun.COM  */
357*9172SFei.Feng@Sun.COM static inline uint32_t
358*9172SFei.Feng@Sun.COM b4inc(uint32_t b32, int8_t delta)
359*9172SFei.Feng@Sun.COM {
360*9172SFei.Feng@Sun.COM 	int8_t i, b4;
361*9172SFei.Feng@Sun.COM 
362*9172SFei.Feng@Sun.COM 	for (i = 0; i < 8; i++) {
363*9172SFei.Feng@Sun.COM 		b4 = b32 & 0xf;
364*9172SFei.Feng@Sun.COM 		b4 += delta;
365*9172SFei.Feng@Sun.COM 		if (b4 < 0)
366*9172SFei.Feng@Sun.COM 			b4 = 0;
367*9172SFei.Feng@Sun.COM 		else if (b4 > 0xf)
368*9172SFei.Feng@Sun.COM 			b4 = 0xf;
369*9172SFei.Feng@Sun.COM 		b32 = b32 >> 4 | b4 << 28;
370*9172SFei.Feng@Sun.COM 	}
371*9172SFei.Feng@Sun.COM 	return (b32);
372*9172SFei.Feng@Sun.COM }
373*9172SFei.Feng@Sun.COM 
374*9172SFei.Feng@Sun.COM static int
375*9172SFei.Feng@Sun.COM rt2860_read_eeprom(struct rt2860_softc *sc)
376*9172SFei.Feng@Sun.COM {
377*9172SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
378*9172SFei.Feng@Sun.COM 	int			ridx, ant, i;
379*9172SFei.Feng@Sun.COM 	int8_t			delta_2ghz, delta_5ghz;
380*9172SFei.Feng@Sun.COM 	uint16_t		val;
381*9172SFei.Feng@Sun.COM 
382*9172SFei.Feng@Sun.COM 	/* read EEPROM version */
383*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_VERSION);
384*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
385*9172SFei.Feng@Sun.COM 	    "EEPROM rev=%d, FAE=%d\n",
386*9172SFei.Feng@Sun.COM 	    val & 0xff, val >> 8);
387*9172SFei.Feng@Sun.COM 
388*9172SFei.Feng@Sun.COM 	/* read MAC address */
389*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC01);
390*9172SFei.Feng@Sun.COM 	ic->ic_macaddr[0] = val & 0xff;
391*9172SFei.Feng@Sun.COM 	ic->ic_macaddr[1] = val >> 8;
392*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC23);
393*9172SFei.Feng@Sun.COM 	ic->ic_macaddr[2] = val & 0xff;
394*9172SFei.Feng@Sun.COM 	ic->ic_macaddr[3] = val >> 8;
395*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC45);
396*9172SFei.Feng@Sun.COM 	ic->ic_macaddr[4] = val & 0xff;
397*9172SFei.Feng@Sun.COM 	ic->ic_macaddr[5] = val >> 8;
398*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
399*9172SFei.Feng@Sun.COM 	    "MAC address is: %x:%x:%x:%x:%x:%x\n",
400*9172SFei.Feng@Sun.COM 	    ic->ic_macaddr[0], ic->ic_macaddr[1],
401*9172SFei.Feng@Sun.COM 	    ic->ic_macaddr[2], ic->ic_macaddr[3],
402*9172SFei.Feng@Sun.COM 	    ic->ic_macaddr[4], ic->ic_macaddr[5]);
403*9172SFei.Feng@Sun.COM 
404*9172SFei.Feng@Sun.COM 	/* read country code */
405*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_COUNTRY);
406*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
407*9172SFei.Feng@Sun.COM 	    "EEPROM region code=0x%04x\n", val);
408*9172SFei.Feng@Sun.COM 
409*9172SFei.Feng@Sun.COM 	/* read default BBP settings */
410*9172SFei.Feng@Sun.COM 	for (i = 0; i < 8; i++) {
411*9172SFei.Feng@Sun.COM 		val = rt2860_eeprom_read(sc, RT2860_EEPROM_BBP_BASE + i);
412*9172SFei.Feng@Sun.COM 		sc->bbp[i].val = val & 0xff;
413*9172SFei.Feng@Sun.COM 		sc->bbp[i].reg = val >> 8;
414*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
415*9172SFei.Feng@Sun.COM 		    "BBP%d=0x%02x\n",
416*9172SFei.Feng@Sun.COM 		    sc->bbp[i].reg, sc->bbp[i].val);
417*9172SFei.Feng@Sun.COM 	}
418*9172SFei.Feng@Sun.COM 
419*9172SFei.Feng@Sun.COM 	/* read RF frequency offset from EEPROM */
420*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_FREQ_LEDS);
421*9172SFei.Feng@Sun.COM 	sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0;
422*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
423*9172SFei.Feng@Sun.COM 	    "EEPROM freq offset %d\n", sc->freq & 0xff);
424*9172SFei.Feng@Sun.COM 
425*9172SFei.Feng@Sun.COM 	if ((sc->leds = val >> 8) != 0xff) {
426*9172SFei.Feng@Sun.COM 		/* read LEDs operating mode */
427*9172SFei.Feng@Sun.COM 		sc->led[0] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED1);
428*9172SFei.Feng@Sun.COM 		sc->led[1] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED2);
429*9172SFei.Feng@Sun.COM 		sc->led[2] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED3);
430*9172SFei.Feng@Sun.COM 	} else {
431*9172SFei.Feng@Sun.COM 		/* broken EEPROM, use default settings */
432*9172SFei.Feng@Sun.COM 		sc->leds = 0x01;
433*9172SFei.Feng@Sun.COM 		sc->led[0] = 0x5555;
434*9172SFei.Feng@Sun.COM 		sc->led[1] = 0x2221;
435*9172SFei.Feng@Sun.COM 		sc->led[2] = 0xa9f8;
436*9172SFei.Feng@Sun.COM 	}
437*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
438*9172SFei.Feng@Sun.COM 	    "EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n",
439*9172SFei.Feng@Sun.COM 	    sc->leds, sc->led[0], sc->led[1], sc->led[2]);
440*9172SFei.Feng@Sun.COM 
441*9172SFei.Feng@Sun.COM 	/* read RF information */
442*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_ANTENNA);
443*9172SFei.Feng@Sun.COM 	if (val == 0xffff) {
444*9172SFei.Feng@Sun.COM 		/* broken EEPROM, default to RF2820 1T2R */
445*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
446*9172SFei.Feng@Sun.COM 		    "invalid EEPROM antenna info, using default\n");
447*9172SFei.Feng@Sun.COM 		sc->rf_rev = RT2860_RF_2820;
448*9172SFei.Feng@Sun.COM 		sc->ntxchains = 1;
449*9172SFei.Feng@Sun.COM 		sc->nrxchains = 2;
450*9172SFei.Feng@Sun.COM 	} else {
451*9172SFei.Feng@Sun.COM 		sc->rf_rev = (val >> 8) & 0xf;
452*9172SFei.Feng@Sun.COM 		sc->ntxchains = (val >> 4) & 0xf;
453*9172SFei.Feng@Sun.COM 		sc->nrxchains = val & 0xf;
454*9172SFei.Feng@Sun.COM 	}
455*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
456*9172SFei.Feng@Sun.COM 	    "EEPROM RF rev=0x%02x chains=%dT%dR\n",
457*9172SFei.Feng@Sun.COM 	    sc->rf_rev, sc->ntxchains, sc->nrxchains);
458*9172SFei.Feng@Sun.COM 
459*9172SFei.Feng@Sun.COM 	/* check if RF supports automatic Tx access gain control */
460*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_CONFIG);
461*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
462*9172SFei.Feng@Sun.COM 	    "EEPROM CFG 0x%04x\n", val);
463*9172SFei.Feng@Sun.COM 	if ((val & 0xff) != 0xff)
464*9172SFei.Feng@Sun.COM 		sc->calib_2ghz = sc->calib_5ghz = 0; /* XXX (val >> 1) & 1 */;
465*9172SFei.Feng@Sun.COM 
466*9172SFei.Feng@Sun.COM 	if (sc->sc_flags & RT2860_ADVANCED_PS) {
467*9172SFei.Feng@Sun.COM 		/* read PCIe power save level */
468*9172SFei.Feng@Sun.COM 		val = rt2860_eeprom_read(sc, RT2860_EEPROM_PCIE_PSLEVEL);
469*9172SFei.Feng@Sun.COM 		if ((val & 0xff) != 0xff) {
470*9172SFei.Feng@Sun.COM 			sc->pslevel = val & 0x3;
471*9172SFei.Feng@Sun.COM 			val = rt2860_eeprom_read(sc, RT2860_EEPROM_REV);
472*9172SFei.Feng@Sun.COM 			if (val >> 8 != 0x92 || !(val & 0x80))
473*9172SFei.Feng@Sun.COM 				sc->pslevel = MIN(sc->pslevel, 1);
474*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_EEPROM,
475*9172SFei.Feng@Sun.COM 			    "rwn: rt2860_read_eeprom(): "
476*9172SFei.Feng@Sun.COM 			    "EEPROM PCIe PS Level=%d\n",
477*9172SFei.Feng@Sun.COM 			    sc->pslevel);
478*9172SFei.Feng@Sun.COM 		}
479*9172SFei.Feng@Sun.COM 	}
480*9172SFei.Feng@Sun.COM 	/* read power settings for 2GHz channels */
481*9172SFei.Feng@Sun.COM 	for (i = 0; i < 14; i += 2) {
482*9172SFei.Feng@Sun.COM 		val = rt2860_eeprom_read(sc,
483*9172SFei.Feng@Sun.COM 		    RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2);
484*9172SFei.Feng@Sun.COM 		sc->txpow1[i + 0] = (int8_t)(val & 0xff);
485*9172SFei.Feng@Sun.COM 		sc->txpow1[i + 1] = (int8_t)(val >> 8);
486*9172SFei.Feng@Sun.COM 
487*9172SFei.Feng@Sun.COM 		val = rt2860_eeprom_read(sc,
488*9172SFei.Feng@Sun.COM 		    RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2);
489*9172SFei.Feng@Sun.COM 		sc->txpow2[i + 0] = (int8_t)(val & 0xff);
490*9172SFei.Feng@Sun.COM 		sc->txpow2[i + 1] = (int8_t)(val >> 8);
491*9172SFei.Feng@Sun.COM 	}
492*9172SFei.Feng@Sun.COM 	/* fix broken Tx power entries */
493*9172SFei.Feng@Sun.COM 	for (i = 0; i < 14; i++) {
494*9172SFei.Feng@Sun.COM 		if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31)
495*9172SFei.Feng@Sun.COM 			sc->txpow1[i] = 5;
496*9172SFei.Feng@Sun.COM 		if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31)
497*9172SFei.Feng@Sun.COM 			sc->txpow2[i] = 5;
498*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
499*9172SFei.Feng@Sun.COM 		    "chan %d: power1=%d, power2=%d\n",
500*9172SFei.Feng@Sun.COM 		    rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i]);
501*9172SFei.Feng@Sun.COM 	}
502*9172SFei.Feng@Sun.COM 	/* read power settings for 5GHz channels */
503*9172SFei.Feng@Sun.COM 	for (i = 0; i < 36; i += 2) {
504*9172SFei.Feng@Sun.COM 		val = rt2860_eeprom_read(sc,
505*9172SFei.Feng@Sun.COM 		    RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2);
506*9172SFei.Feng@Sun.COM 		sc->txpow1[i + 14] = (int8_t)(val & 0xff);
507*9172SFei.Feng@Sun.COM 		sc->txpow1[i + 15] = (int8_t)(val >> 8);
508*9172SFei.Feng@Sun.COM 
509*9172SFei.Feng@Sun.COM 		val = rt2860_eeprom_read(sc,
510*9172SFei.Feng@Sun.COM 		    RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2);
511*9172SFei.Feng@Sun.COM 		sc->txpow2[i + 14] = (int8_t)(val & 0xff);
512*9172SFei.Feng@Sun.COM 		sc->txpow2[i + 15] = (int8_t)(val >> 8);
513*9172SFei.Feng@Sun.COM 	}
514*9172SFei.Feng@Sun.COM 	/* fix broken Tx power entries */
515*9172SFei.Feng@Sun.COM 	for (i = 0; i < 36; i++) {
516*9172SFei.Feng@Sun.COM 		if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15)
517*9172SFei.Feng@Sun.COM 			sc->txpow1[14 + i] = 5;
518*9172SFei.Feng@Sun.COM 		if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15)
519*9172SFei.Feng@Sun.COM 			sc->txpow2[14 + i] = 5;
520*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
521*9172SFei.Feng@Sun.COM 		    "chan %d: power1=%d, power2=%d\n",
522*9172SFei.Feng@Sun.COM 		    rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i],
523*9172SFei.Feng@Sun.COM 		    sc->txpow2[14 + i]);
524*9172SFei.Feng@Sun.COM 	}
525*9172SFei.Feng@Sun.COM 
526*9172SFei.Feng@Sun.COM 	/* read Tx power compensation for each Tx rate */
527*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_DELTAPWR);
528*9172SFei.Feng@Sun.COM 	delta_2ghz = delta_5ghz = 0;
529*9172SFei.Feng@Sun.COM 	if ((val & 0xff) != 0xff && (val & 0x80)) {
530*9172SFei.Feng@Sun.COM 		delta_2ghz = val & 0xf;
531*9172SFei.Feng@Sun.COM 		if (!(val & 0x40))	/* negative number */
532*9172SFei.Feng@Sun.COM 			delta_2ghz = -delta_2ghz;
533*9172SFei.Feng@Sun.COM 	}
534*9172SFei.Feng@Sun.COM 	val >>= 8;
535*9172SFei.Feng@Sun.COM 	if ((val & 0xff) != 0xff && (val & 0x80)) {
536*9172SFei.Feng@Sun.COM 		delta_5ghz = val & 0xf;
537*9172SFei.Feng@Sun.COM 		if (!(val & 0x40))	/* negative number */
538*9172SFei.Feng@Sun.COM 			delta_5ghz = -delta_5ghz;
539*9172SFei.Feng@Sun.COM 	}
540*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
541*9172SFei.Feng@Sun.COM 	    "power compensation=%d (2GHz), %d (5GHz) \n",
542*9172SFei.Feng@Sun.COM 	    delta_2ghz, delta_5ghz);
543*9172SFei.Feng@Sun.COM 
544*9172SFei.Feng@Sun.COM 	for (ridx = 0; ridx < 5; ridx++) {
545*9172SFei.Feng@Sun.COM 		uint32_t	reg;
546*9172SFei.Feng@Sun.COM 
547*9172SFei.Feng@Sun.COM 		val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx);
548*9172SFei.Feng@Sun.COM 		reg = (uint32_t)val << 16;
549*9172SFei.Feng@Sun.COM 		val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx + 1);
550*9172SFei.Feng@Sun.COM 		reg |= val;
551*9172SFei.Feng@Sun.COM 
552*9172SFei.Feng@Sun.COM 		sc->txpow20mhz[ridx] = reg;
553*9172SFei.Feng@Sun.COM 		sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz);
554*9172SFei.Feng@Sun.COM 		sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz);
555*9172SFei.Feng@Sun.COM 
556*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
557*9172SFei.Feng@Sun.COM 		    "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, "
558*9172SFei.Feng@Sun.COM 		    "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx],
559*9172SFei.Feng@Sun.COM 		    sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx]);
560*9172SFei.Feng@Sun.COM 	}
561*9172SFei.Feng@Sun.COM 
562*9172SFei.Feng@Sun.COM 	/* read factory-calibrated samples for temperature compensation */
563*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_2GHZ);
564*9172SFei.Feng@Sun.COM 	sc->tssi_2ghz[0] = val & 0xff;	/* [-4] */
565*9172SFei.Feng@Sun.COM 	sc->tssi_2ghz[1] = val >> 8;	/* [-3] */
566*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_2GHZ);
567*9172SFei.Feng@Sun.COM 	sc->tssi_2ghz[2] = val & 0xff;	/* [-2] */
568*9172SFei.Feng@Sun.COM 	sc->tssi_2ghz[3] = val >> 8;	/* [-1] */
569*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_2GHZ);
570*9172SFei.Feng@Sun.COM 	sc->tssi_2ghz[4] = val & 0xff;	/* [+0] */
571*9172SFei.Feng@Sun.COM 	sc->tssi_2ghz[5] = val >> 8;	/* [+1] */
572*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_2GHZ);
573*9172SFei.Feng@Sun.COM 	sc->tssi_2ghz[6] = val & 0xff;	/* [+2] */
574*9172SFei.Feng@Sun.COM 	sc->tssi_2ghz[7] = val >> 8;	/* [+3] */
575*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_2GHZ);
576*9172SFei.Feng@Sun.COM 	sc->tssi_2ghz[8] = val & 0xff;	/* [+4] */
577*9172SFei.Feng@Sun.COM 	sc->step_2ghz = val >> 8;
578*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
579*9172SFei.Feng@Sun.COM 	    "TSSI 2GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
580*9172SFei.Feng@Sun.COM 	    "0x%02x 0x%02x step=%d\n", sc->tssi_2ghz[0], sc->tssi_2ghz[1],
581*9172SFei.Feng@Sun.COM 	    sc->tssi_2ghz[2], sc->tssi_2ghz[3], sc->tssi_2ghz[4],
582*9172SFei.Feng@Sun.COM 	    sc->tssi_2ghz[5], sc->tssi_2ghz[6], sc->tssi_2ghz[7],
583*9172SFei.Feng@Sun.COM 	    sc->tssi_2ghz[8], sc->step_2ghz);
584*9172SFei.Feng@Sun.COM 	/* check that ref value is correct, otherwise disable calibration */
585*9172SFei.Feng@Sun.COM 	if (sc->tssi_2ghz[4] == 0xff)
586*9172SFei.Feng@Sun.COM 		sc->calib_2ghz = 0;
587*9172SFei.Feng@Sun.COM 
588*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_5GHZ);
589*9172SFei.Feng@Sun.COM 	sc->tssi_5ghz[0] = val & 0xff;	/* [-4] */
590*9172SFei.Feng@Sun.COM 	sc->tssi_5ghz[1] = val >> 8;	/* [-3] */
591*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_5GHZ);
592*9172SFei.Feng@Sun.COM 	sc->tssi_5ghz[2] = val & 0xff;	/* [-2] */
593*9172SFei.Feng@Sun.COM 	sc->tssi_5ghz[3] = val >> 8;	/* [-1] */
594*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_5GHZ);
595*9172SFei.Feng@Sun.COM 	sc->tssi_5ghz[4] = val & 0xff;	/* [+0] */
596*9172SFei.Feng@Sun.COM 	sc->tssi_5ghz[5] = val >> 8;	/* [+1] */
597*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_5GHZ);
598*9172SFei.Feng@Sun.COM 	sc->tssi_5ghz[6] = val & 0xff;	/* [+2] */
599*9172SFei.Feng@Sun.COM 	sc->tssi_5ghz[7] = val >> 8;	/* [+3] */
600*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_5GHZ);
601*9172SFei.Feng@Sun.COM 	sc->tssi_5ghz[8] = val & 0xff;	/* [+4] */
602*9172SFei.Feng@Sun.COM 	sc->step_5ghz = val >> 8;
603*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
604*9172SFei.Feng@Sun.COM 	    "TSSI 5GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
605*9172SFei.Feng@Sun.COM 	    "0x%02x 0x%02x step=%d\n", sc->tssi_5ghz[0], sc->tssi_5ghz[1],
606*9172SFei.Feng@Sun.COM 	    sc->tssi_5ghz[2], sc->tssi_5ghz[3], sc->tssi_5ghz[4],
607*9172SFei.Feng@Sun.COM 	    sc->tssi_5ghz[5], sc->tssi_5ghz[6], sc->tssi_5ghz[7],
608*9172SFei.Feng@Sun.COM 	    sc->tssi_5ghz[8], sc->step_5ghz);
609*9172SFei.Feng@Sun.COM 	/* check that ref value is correct, otherwise disable calibration */
610*9172SFei.Feng@Sun.COM 	if (sc->tssi_5ghz[4] == 0xff)
611*9172SFei.Feng@Sun.COM 		sc->calib_5ghz = 0;
612*9172SFei.Feng@Sun.COM 
613*9172SFei.Feng@Sun.COM 	/* read RSSI offsets and LNA gains from EEPROM */
614*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_2GHZ);
615*9172SFei.Feng@Sun.COM 	sc->rssi_2ghz[0] = val & 0xff;	/* Ant A */
616*9172SFei.Feng@Sun.COM 	sc->rssi_2ghz[1] = val >> 8;	/* Ant B */
617*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_2GHZ);
618*9172SFei.Feng@Sun.COM 	sc->rssi_2ghz[2] = val & 0xff;	/* Ant C */
619*9172SFei.Feng@Sun.COM 	sc->lna[2] = val >> 8;		/* channel group 2 */
620*9172SFei.Feng@Sun.COM 
621*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_5GHZ);
622*9172SFei.Feng@Sun.COM 	sc->rssi_5ghz[0] = val & 0xff;	/* Ant A */
623*9172SFei.Feng@Sun.COM 	sc->rssi_5ghz[1] = val >> 8;	/* Ant B */
624*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_5GHZ);
625*9172SFei.Feng@Sun.COM 	sc->rssi_5ghz[2] = val & 0xff;	/* Ant C */
626*9172SFei.Feng@Sun.COM 	sc->lna[3] = val >> 8;		/* channel group 3 */
627*9172SFei.Feng@Sun.COM 
628*9172SFei.Feng@Sun.COM 	val = rt2860_eeprom_read(sc, RT2860_EEPROM_LNA);
629*9172SFei.Feng@Sun.COM 	sc->lna[0] = val & 0xff;	/* channel group 0 */
630*9172SFei.Feng@Sun.COM 	sc->lna[1] = val >> 8;		/* channel group 1 */
631*9172SFei.Feng@Sun.COM 
632*9172SFei.Feng@Sun.COM 	/* fix broken 5GHz LNA entries */
633*9172SFei.Feng@Sun.COM 	if (sc->lna[2] == 0 || sc->lna[2] == 0xff) {
634*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
635*9172SFei.Feng@Sun.COM 		    "invalid LNA for channel group %d\n", 2);
636*9172SFei.Feng@Sun.COM 		sc->lna[2] = sc->lna[1];
637*9172SFei.Feng@Sun.COM 	}
638*9172SFei.Feng@Sun.COM 	if (sc->lna[3] == 0 || sc->lna[3] == 0xff) {
639*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): "
640*9172SFei.Feng@Sun.COM 		    "invalid LNA for channel group %d\n", 3);
641*9172SFei.Feng@Sun.COM 		sc->lna[3] = sc->lna[1];
642*9172SFei.Feng@Sun.COM 	}
643*9172SFei.Feng@Sun.COM 
644*9172SFei.Feng@Sun.COM 	/* fix broken RSSI offset entries */
645*9172SFei.Feng@Sun.COM 	for (ant = 0; ant < 3; ant++) {
646*9172SFei.Feng@Sun.COM 		if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) {
647*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_EEPROM,
648*9172SFei.Feng@Sun.COM 			    "rwn: rt2860_read_eeprom(): "
649*9172SFei.Feng@Sun.COM 			    "invalid RSSI%d offset: %d (2GHz)\n",
650*9172SFei.Feng@Sun.COM 			    ant + 1, sc->rssi_2ghz[ant]);
651*9172SFei.Feng@Sun.COM 			sc->rssi_2ghz[ant] = 0;
652*9172SFei.Feng@Sun.COM 		}
653*9172SFei.Feng@Sun.COM 		if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) {
654*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_EEPROM,
655*9172SFei.Feng@Sun.COM 			    "rwn: rt2860_read_eeprom(): "
656*9172SFei.Feng@Sun.COM 			    "invalid RSSI%d offset: %d (2GHz)\n",
657*9172SFei.Feng@Sun.COM 			    ant + 1, sc->rssi_5ghz[ant]);
658*9172SFei.Feng@Sun.COM 			sc->rssi_5ghz[ant] = 0;
659*9172SFei.Feng@Sun.COM 		}
660*9172SFei.Feng@Sun.COM 	}
661*9172SFei.Feng@Sun.COM 
662*9172SFei.Feng@Sun.COM 	return (RT2860_SUCCESS);
663*9172SFei.Feng@Sun.COM }
664*9172SFei.Feng@Sun.COM 
665*9172SFei.Feng@Sun.COM /*
666*9172SFei.Feng@Sun.COM  * Allocate an DMA memory and a DMA handle for accessing it
667*9172SFei.Feng@Sun.COM  */
668*9172SFei.Feng@Sun.COM static int
669*9172SFei.Feng@Sun.COM rt2860_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr,
670*9172SFei.Feng@Sun.COM 	size_t memsize, ddi_device_acc_attr_t *attr_p, uint_t alloc_flags,
671*9172SFei.Feng@Sun.COM 	uint_t bind_flags, struct dma_area *dma_p)
672*9172SFei.Feng@Sun.COM {
673*9172SFei.Feng@Sun.COM 	int	err;
674*9172SFei.Feng@Sun.COM 
675*9172SFei.Feng@Sun.COM 	/*
676*9172SFei.Feng@Sun.COM 	 * Allocate handle
677*9172SFei.Feng@Sun.COM 	 */
678*9172SFei.Feng@Sun.COM 	err = ddi_dma_alloc_handle(devinfo, dma_attr,
679*9172SFei.Feng@Sun.COM 	    DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
680*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
681*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_allo_dma_mem(): "
682*9172SFei.Feng@Sun.COM 		    "failed to alloc handle\n");
683*9172SFei.Feng@Sun.COM 		goto fail1;
684*9172SFei.Feng@Sun.COM 	}
685*9172SFei.Feng@Sun.COM 
686*9172SFei.Feng@Sun.COM 	/*
687*9172SFei.Feng@Sun.COM 	 * Allocate memory
688*9172SFei.Feng@Sun.COM 	 */
689*9172SFei.Feng@Sun.COM 	err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
690*9172SFei.Feng@Sun.COM 	    alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
691*9172SFei.Feng@Sun.COM 	    &dma_p->alength, &dma_p->acc_hdl);
692*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
693*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_alloc_dma_mem(): "
694*9172SFei.Feng@Sun.COM 		    "failed to alloc mem\n");
695*9172SFei.Feng@Sun.COM 		goto fail2;
696*9172SFei.Feng@Sun.COM 	}
697*9172SFei.Feng@Sun.COM 
698*9172SFei.Feng@Sun.COM 	/*
699*9172SFei.Feng@Sun.COM 	 * Bind the two together
700*9172SFei.Feng@Sun.COM 	 */
701*9172SFei.Feng@Sun.COM 	err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
702*9172SFei.Feng@Sun.COM 	    dma_p->mem_va, dma_p->alength, bind_flags,
703*9172SFei.Feng@Sun.COM 	    DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
704*9172SFei.Feng@Sun.COM 	if (err != DDI_DMA_MAPPED) {
705*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_alloc_dma_mem(): "
706*9172SFei.Feng@Sun.COM 		    "failed to bind handle\n");
707*9172SFei.Feng@Sun.COM 		goto fail3;
708*9172SFei.Feng@Sun.COM 	}
709*9172SFei.Feng@Sun.COM 
710*9172SFei.Feng@Sun.COM 	if (dma_p->ncookies != 1) {
711*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_alloc_dma_mem(): "
712*9172SFei.Feng@Sun.COM 		    "failed to alloc cookies\n");
713*9172SFei.Feng@Sun.COM 		goto fail4;
714*9172SFei.Feng@Sun.COM 	}
715*9172SFei.Feng@Sun.COM 
716*9172SFei.Feng@Sun.COM 	dma_p->nslots = ~0U;
717*9172SFei.Feng@Sun.COM 	dma_p->size = ~0U;
718*9172SFei.Feng@Sun.COM 	dma_p->token = ~0U;
719*9172SFei.Feng@Sun.COM 	dma_p->offset = 0;
720*9172SFei.Feng@Sun.COM 	return (DDI_SUCCESS);
721*9172SFei.Feng@Sun.COM 
722*9172SFei.Feng@Sun.COM fail4:
723*9172SFei.Feng@Sun.COM 	(void) ddi_dma_unbind_handle(dma_p->dma_hdl);
724*9172SFei.Feng@Sun.COM fail3:
725*9172SFei.Feng@Sun.COM 	ddi_dma_mem_free(&dma_p->acc_hdl);
726*9172SFei.Feng@Sun.COM fail2:
727*9172SFei.Feng@Sun.COM 	ddi_dma_free_handle(&dma_p->dma_hdl);
728*9172SFei.Feng@Sun.COM fail1:
729*9172SFei.Feng@Sun.COM 	return (err);
730*9172SFei.Feng@Sun.COM }
731*9172SFei.Feng@Sun.COM 
732*9172SFei.Feng@Sun.COM static void
733*9172SFei.Feng@Sun.COM rt2860_free_dma_mem(struct dma_area *dma_p)
734*9172SFei.Feng@Sun.COM {
735*9172SFei.Feng@Sun.COM 	if (dma_p->dma_hdl != NULL) {
736*9172SFei.Feng@Sun.COM 		(void) ddi_dma_unbind_handle(dma_p->dma_hdl);
737*9172SFei.Feng@Sun.COM 		if (dma_p->acc_hdl != NULL) {
738*9172SFei.Feng@Sun.COM 			ddi_dma_mem_free(&dma_p->acc_hdl);
739*9172SFei.Feng@Sun.COM 			dma_p->acc_hdl = NULL;
740*9172SFei.Feng@Sun.COM 		}
741*9172SFei.Feng@Sun.COM 		ddi_dma_free_handle(&dma_p->dma_hdl);
742*9172SFei.Feng@Sun.COM 		dma_p->ncookies = 0;
743*9172SFei.Feng@Sun.COM 		dma_p->dma_hdl = NULL;
744*9172SFei.Feng@Sun.COM 	}
745*9172SFei.Feng@Sun.COM }
746*9172SFei.Feng@Sun.COM 
747*9172SFei.Feng@Sun.COM /*ARGSUSED*/
748*9172SFei.Feng@Sun.COM static int
749*9172SFei.Feng@Sun.COM rt2860_alloc_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring)
750*9172SFei.Feng@Sun.COM {
751*9172SFei.Feng@Sun.COM 	int	size, err;
752*9172SFei.Feng@Sun.COM 
753*9172SFei.Feng@Sun.COM 	size = RT2860_TX_RING_COUNT * sizeof (struct rt2860_txd);
754*9172SFei.Feng@Sun.COM 
755*9172SFei.Feng@Sun.COM 	err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, size,
756*9172SFei.Feng@Sun.COM 	    &rt2860_desc_accattr, DDI_DMA_CONSISTENT,
757*9172SFei.Feng@Sun.COM 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
758*9172SFei.Feng@Sun.COM 	    &ring->txdesc_dma);
759*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
760*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_alloc_tx_ring(): "
761*9172SFei.Feng@Sun.COM 		    "failed to alloc dma mem\n");
762*9172SFei.Feng@Sun.COM 		goto fail1;
763*9172SFei.Feng@Sun.COM 	}
764*9172SFei.Feng@Sun.COM 
765*9172SFei.Feng@Sun.COM 	ring->txd = (struct rt2860_txd *)ring->txdesc_dma.mem_va;
766*9172SFei.Feng@Sun.COM 	ring->paddr = ring->txdesc_dma.cookie.dmac_address;
767*9172SFei.Feng@Sun.COM 
768*9172SFei.Feng@Sun.COM 	ring->cur = 0;
769*9172SFei.Feng@Sun.COM 	ring->next = 0;
770*9172SFei.Feng@Sun.COM 	ring->queued = 0;
771*9172SFei.Feng@Sun.COM 
772*9172SFei.Feng@Sun.COM 	(void) bzero(ring->txd, size);
773*9172SFei.Feng@Sun.COM 	RT2860_DMA_SYNC(ring->txdesc_dma, DDI_DMA_SYNC_FORDEV);
774*9172SFei.Feng@Sun.COM 	return (DDI_SUCCESS);
775*9172SFei.Feng@Sun.COM fail1:
776*9172SFei.Feng@Sun.COM 	return (err);
777*9172SFei.Feng@Sun.COM }
778*9172SFei.Feng@Sun.COM 
779*9172SFei.Feng@Sun.COM void
780*9172SFei.Feng@Sun.COM rt2860_reset_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring)
781*9172SFei.Feng@Sun.COM {
782*9172SFei.Feng@Sun.COM 	struct rt2860_tx_data *data;
783*9172SFei.Feng@Sun.COM 	int i;
784*9172SFei.Feng@Sun.COM 
785*9172SFei.Feng@Sun.COM 	for (i = 0; i < RT2860_TX_RING_COUNT; i++) {
786*9172SFei.Feng@Sun.COM 		ring->txd[i].sdl0 &= ~LE_16(RT2860_TX_DDONE);
787*9172SFei.Feng@Sun.COM 
788*9172SFei.Feng@Sun.COM 		if ((data = ring->data[i]) == NULL)
789*9172SFei.Feng@Sun.COM 			continue;	/* nothing mapped in this slot */
790*9172SFei.Feng@Sun.COM 
791*9172SFei.Feng@Sun.COM 		/* by pass if it's quiesced */
792*9172SFei.Feng@Sun.COM 		if (!(sc->sc_flags & RT2860_F_QUIESCE))
793*9172SFei.Feng@Sun.COM 			RT2860_DMA_SYNC(data->txbuf_dma, DDI_DMA_SYNC_FORDEV);
794*9172SFei.Feng@Sun.COM 
795*9172SFei.Feng@Sun.COM 		if (data->ni != NULL) {
796*9172SFei.Feng@Sun.COM 			ieee80211_free_node(data->ni);
797*9172SFei.Feng@Sun.COM 			data->ni = NULL;	/* node already freed */
798*9172SFei.Feng@Sun.COM 		}
799*9172SFei.Feng@Sun.COM 
800*9172SFei.Feng@Sun.COM 		SLIST_INSERT_HEAD(&sc->data_pool, data, next);
801*9172SFei.Feng@Sun.COM 		ring->data[i] = NULL;
802*9172SFei.Feng@Sun.COM 	}
803*9172SFei.Feng@Sun.COM 
804*9172SFei.Feng@Sun.COM 	/* by pass if it's quiesced */
805*9172SFei.Feng@Sun.COM 	if (!(sc->sc_flags & RT2860_F_QUIESCE))
806*9172SFei.Feng@Sun.COM 		RT2860_DMA_SYNC(ring->txdesc_dma, DDI_DMA_SYNC_FORDEV);
807*9172SFei.Feng@Sun.COM 
808*9172SFei.Feng@Sun.COM 	ring->queued = 0;
809*9172SFei.Feng@Sun.COM 	ring->cur = ring->next = 0;
810*9172SFei.Feng@Sun.COM }
811*9172SFei.Feng@Sun.COM 
812*9172SFei.Feng@Sun.COM /*ARGSUSED*/
813*9172SFei.Feng@Sun.COM static void
814*9172SFei.Feng@Sun.COM rt2860_free_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring)
815*9172SFei.Feng@Sun.COM {
816*9172SFei.Feng@Sun.COM 	if (ring->txd != NULL) {
817*9172SFei.Feng@Sun.COM 		rt2860_free_dma_mem(&ring->txdesc_dma);
818*9172SFei.Feng@Sun.COM 	}
819*9172SFei.Feng@Sun.COM }
820*9172SFei.Feng@Sun.COM 
821*9172SFei.Feng@Sun.COM static int
822*9172SFei.Feng@Sun.COM rt2860_alloc_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring)
823*9172SFei.Feng@Sun.COM {
824*9172SFei.Feng@Sun.COM 	struct rt2860_rx_data	*data;
825*9172SFei.Feng@Sun.COM 	struct rt2860_rxd	*rxd;
826*9172SFei.Feng@Sun.COM 	int			i, err, size, datalen;
827*9172SFei.Feng@Sun.COM 
828*9172SFei.Feng@Sun.COM 	size = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rxd);
829*9172SFei.Feng@Sun.COM 
830*9172SFei.Feng@Sun.COM 	err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, size,
831*9172SFei.Feng@Sun.COM 	    &rt2860_desc_accattr, DDI_DMA_CONSISTENT,
832*9172SFei.Feng@Sun.COM 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
833*9172SFei.Feng@Sun.COM 	    &ring->rxdesc_dma);
834*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
835*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_alloc_rx_ring(): "
836*9172SFei.Feng@Sun.COM 		    "failed to alloc dma mem\n");
837*9172SFei.Feng@Sun.COM 		goto fail1;
838*9172SFei.Feng@Sun.COM 	}
839*9172SFei.Feng@Sun.COM 
840*9172SFei.Feng@Sun.COM 	ring->rxd = (struct rt2860_rxd *)ring->rxdesc_dma.mem_va;
841*9172SFei.Feng@Sun.COM 	ring->paddr = ring->rxdesc_dma.cookie.dmac_address;
842*9172SFei.Feng@Sun.COM 	bzero(ring->rxd, size);
843*9172SFei.Feng@Sun.COM 
844*9172SFei.Feng@Sun.COM 	/*
845*9172SFei.Feng@Sun.COM 	 * Pre-allocate Rx buffers and populate Rx ring.
846*9172SFei.Feng@Sun.COM 	 */
847*9172SFei.Feng@Sun.COM 	datalen = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rx_data);
848*9172SFei.Feng@Sun.COM 	bzero(ring->data, datalen);
849*9172SFei.Feng@Sun.COM 	for (i = 0; i < RT2860_RX_RING_COUNT; i++) {
850*9172SFei.Feng@Sun.COM 		rxd = &ring->rxd[i];
851*9172SFei.Feng@Sun.COM 		data = &ring->data[i];
852*9172SFei.Feng@Sun.COM 		/* alloc DMA memory */
853*9172SFei.Feng@Sun.COM 		(void) rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr,
854*9172SFei.Feng@Sun.COM 		    sc->sc_dmabuf_size,
855*9172SFei.Feng@Sun.COM 		    &rt2860_buf_accattr,
856*9172SFei.Feng@Sun.COM 		    DDI_DMA_STREAMING,
857*9172SFei.Feng@Sun.COM 		    DDI_DMA_READ | DDI_DMA_STREAMING,
858*9172SFei.Feng@Sun.COM 		    &data->rxbuf_dma);
859*9172SFei.Feng@Sun.COM 		rxd->sdp0 = LE_32(data->rxbuf_dma.cookie.dmac_address);
860*9172SFei.Feng@Sun.COM 		rxd->sdl0 = LE_16(sc->sc_dmabuf_size);
861*9172SFei.Feng@Sun.COM 	}
862*9172SFei.Feng@Sun.COM 
863*9172SFei.Feng@Sun.COM 	ring->cur = 0;
864*9172SFei.Feng@Sun.COM 
865*9172SFei.Feng@Sun.COM 	RT2860_DMA_SYNC(ring->rxdesc_dma, DDI_DMA_SYNC_FORDEV);
866*9172SFei.Feng@Sun.COM 	return (DDI_SUCCESS);
867*9172SFei.Feng@Sun.COM fail2:
868*9172SFei.Feng@Sun.COM 	rt2860_free_dma_mem(&ring->rxdesc_dma);
869*9172SFei.Feng@Sun.COM fail1:
870*9172SFei.Feng@Sun.COM 	return (err);
871*9172SFei.Feng@Sun.COM }
872*9172SFei.Feng@Sun.COM 
873*9172SFei.Feng@Sun.COM /*ARGSUSED*/
874*9172SFei.Feng@Sun.COM void
875*9172SFei.Feng@Sun.COM rt2860_reset_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring)
876*9172SFei.Feng@Sun.COM {
877*9172SFei.Feng@Sun.COM 	int i;
878*9172SFei.Feng@Sun.COM 
879*9172SFei.Feng@Sun.COM 	for (i = 0; i < RT2860_RX_RING_COUNT; i++)
880*9172SFei.Feng@Sun.COM 		ring->rxd[i].sdl0 &= ~LE_16(RT2860_RX_DDONE);
881*9172SFei.Feng@Sun.COM 
882*9172SFei.Feng@Sun.COM 	RT2860_DMA_SYNC(ring->rxdesc_dma, DDI_DMA_SYNC_FORDEV);
883*9172SFei.Feng@Sun.COM 
884*9172SFei.Feng@Sun.COM 	ring->cur = 0;
885*9172SFei.Feng@Sun.COM }
886*9172SFei.Feng@Sun.COM 
887*9172SFei.Feng@Sun.COM /*ARGSUSED*/
888*9172SFei.Feng@Sun.COM static void
889*9172SFei.Feng@Sun.COM rt2860_free_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring)
890*9172SFei.Feng@Sun.COM {
891*9172SFei.Feng@Sun.COM 	struct rt2860_rx_data	*data;
892*9172SFei.Feng@Sun.COM 	int			i, count;
893*9172SFei.Feng@Sun.COM 
894*9172SFei.Feng@Sun.COM 	if (ring->rxd != NULL)
895*9172SFei.Feng@Sun.COM 		rt2860_free_dma_mem(&ring->rxdesc_dma);
896*9172SFei.Feng@Sun.COM 
897*9172SFei.Feng@Sun.COM 	count = RT2860_RX_RING_COUNT;
898*9172SFei.Feng@Sun.COM 	if (ring->data != NULL) {
899*9172SFei.Feng@Sun.COM 		for (i = 0; i < count; i++) {
900*9172SFei.Feng@Sun.COM 			data = &ring->data[i];
901*9172SFei.Feng@Sun.COM 			rt2860_free_dma_mem(&data->rxbuf_dma);
902*9172SFei.Feng@Sun.COM 		}
903*9172SFei.Feng@Sun.COM 	}
904*9172SFei.Feng@Sun.COM }
905*9172SFei.Feng@Sun.COM 
906*9172SFei.Feng@Sun.COM static int
907*9172SFei.Feng@Sun.COM rt2860_alloc_tx_pool(struct rt2860_softc *sc)
908*9172SFei.Feng@Sun.COM {
909*9172SFei.Feng@Sun.COM 	struct rt2860_tx_data	*data;
910*9172SFei.Feng@Sun.COM 	int			i, err, size;
911*9172SFei.Feng@Sun.COM 
912*9172SFei.Feng@Sun.COM 	size = RT2860_TX_POOL_COUNT * sizeof (struct rt2860_txwi);
913*9172SFei.Feng@Sun.COM 
914*9172SFei.Feng@Sun.COM 	/* init data_pool early in case of failure.. */
915*9172SFei.Feng@Sun.COM 	SLIST_INIT(&sc->data_pool);
916*9172SFei.Feng@Sun.COM 
917*9172SFei.Feng@Sun.COM 	err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, size,
918*9172SFei.Feng@Sun.COM 	    &rt2860_desc_accattr, DDI_DMA_CONSISTENT,
919*9172SFei.Feng@Sun.COM 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
920*9172SFei.Feng@Sun.COM 	    &sc->txpool_dma);
921*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
922*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_alloc_tx_pool(): "
923*9172SFei.Feng@Sun.COM 		    "failed to alloc dma mem\n");
924*9172SFei.Feng@Sun.COM 		goto fail1;
925*9172SFei.Feng@Sun.COM 	}
926*9172SFei.Feng@Sun.COM 
927*9172SFei.Feng@Sun.COM 	sc->txwi = (struct rt2860_txwi *)sc->txpool_dma.mem_va;
928*9172SFei.Feng@Sun.COM 	(void) bzero(sc->txwi, size);
929*9172SFei.Feng@Sun.COM 	RT2860_DMA_SYNC(sc->txpool_dma, DDI_DMA_SYNC_FORDEV);
930*9172SFei.Feng@Sun.COM 
931*9172SFei.Feng@Sun.COM 	for (i = 0; i < RT2860_TX_POOL_COUNT; i++) {
932*9172SFei.Feng@Sun.COM 		data = &sc->data[i];
933*9172SFei.Feng@Sun.COM 
934*9172SFei.Feng@Sun.COM 		err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr,
935*9172SFei.Feng@Sun.COM 		    sc->sc_dmabuf_size,
936*9172SFei.Feng@Sun.COM 		    &rt2860_buf_accattr, DDI_DMA_CONSISTENT,
937*9172SFei.Feng@Sun.COM 		    DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
938*9172SFei.Feng@Sun.COM 		    &data->txbuf_dma);
939*9172SFei.Feng@Sun.COM 		if (err != DDI_SUCCESS) {
940*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_DMA,
941*9172SFei.Feng@Sun.COM 			    "rwn: rt2860_alloc_tx_pool(): "
942*9172SFei.Feng@Sun.COM 			    "failed to alloc dma mem\n");
943*9172SFei.Feng@Sun.COM 			goto fail2;
944*9172SFei.Feng@Sun.COM 		}
945*9172SFei.Feng@Sun.COM 		data->txwi = &sc->txwi[i];
946*9172SFei.Feng@Sun.COM 		data->paddr = sc->txpool_dma.cookie.dmac_address +
947*9172SFei.Feng@Sun.COM 		    i * sizeof (struct rt2860_txwi);
948*9172SFei.Feng@Sun.COM 
949*9172SFei.Feng@Sun.COM 		SLIST_INSERT_HEAD(&sc->data_pool, data, next);
950*9172SFei.Feng@Sun.COM 	}
951*9172SFei.Feng@Sun.COM 	return (DDI_SUCCESS);
952*9172SFei.Feng@Sun.COM fail2:
953*9172SFei.Feng@Sun.COM 	rt2860_free_dma_mem(&sc->txpool_dma);
954*9172SFei.Feng@Sun.COM fail1:
955*9172SFei.Feng@Sun.COM 	return (err);
956*9172SFei.Feng@Sun.COM }
957*9172SFei.Feng@Sun.COM 
958*9172SFei.Feng@Sun.COM static void
959*9172SFei.Feng@Sun.COM rt2860_free_tx_pool(struct rt2860_softc *sc)
960*9172SFei.Feng@Sun.COM {
961*9172SFei.Feng@Sun.COM 	struct rt2860_tx_data	*data;
962*9172SFei.Feng@Sun.COM 	int	i;
963*9172SFei.Feng@Sun.COM 
964*9172SFei.Feng@Sun.COM 	if (sc->txwi != NULL) {
965*9172SFei.Feng@Sun.COM 		rt2860_free_dma_mem(&sc->txpool_dma);
966*9172SFei.Feng@Sun.COM 	}
967*9172SFei.Feng@Sun.COM 
968*9172SFei.Feng@Sun.COM 	for (i = 0; i < RT2860_TX_POOL_COUNT; i++) {
969*9172SFei.Feng@Sun.COM 		data = &sc->data[i];
970*9172SFei.Feng@Sun.COM 		rt2860_free_dma_mem(&data->txbuf_dma);
971*9172SFei.Feng@Sun.COM 	}
972*9172SFei.Feng@Sun.COM }
973*9172SFei.Feng@Sun.COM 
974*9172SFei.Feng@Sun.COM /* quickly determine if a given rate is CCK or OFDM */
975*9172SFei.Feng@Sun.COM #define	RT2860_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
976*9172SFei.Feng@Sun.COM 
977*9172SFei.Feng@Sun.COM #define	RT2860_ACK_SIZE		14	/* 10 + 4(FCS) */
978*9172SFei.Feng@Sun.COM #define	RT2860_SIFS_TIME	10
979*9172SFei.Feng@Sun.COM 
980*9172SFei.Feng@Sun.COM static uint8_t
981*9172SFei.Feng@Sun.COM rt2860_rate2mcs(uint8_t rate)
982*9172SFei.Feng@Sun.COM {
983*9172SFei.Feng@Sun.COM 	switch (rate) {
984*9172SFei.Feng@Sun.COM 	/* CCK rates */
985*9172SFei.Feng@Sun.COM 	case 2:
986*9172SFei.Feng@Sun.COM 		return (0);
987*9172SFei.Feng@Sun.COM 	case 4:
988*9172SFei.Feng@Sun.COM 		return (1);
989*9172SFei.Feng@Sun.COM 	case 11:
990*9172SFei.Feng@Sun.COM 		return (2);
991*9172SFei.Feng@Sun.COM 	case 22:
992*9172SFei.Feng@Sun.COM 		return (3);
993*9172SFei.Feng@Sun.COM 	/* OFDM rates */
994*9172SFei.Feng@Sun.COM 	case 12:
995*9172SFei.Feng@Sun.COM 		return (0);
996*9172SFei.Feng@Sun.COM 	case 18:
997*9172SFei.Feng@Sun.COM 		return (1);
998*9172SFei.Feng@Sun.COM 	case 24:
999*9172SFei.Feng@Sun.COM 		return (2);
1000*9172SFei.Feng@Sun.COM 	case 36:
1001*9172SFei.Feng@Sun.COM 		return (3);
1002*9172SFei.Feng@Sun.COM 	case 48:
1003*9172SFei.Feng@Sun.COM 		return (4);
1004*9172SFei.Feng@Sun.COM 	case 72:
1005*9172SFei.Feng@Sun.COM 		return (5);
1006*9172SFei.Feng@Sun.COM 	case 96:
1007*9172SFei.Feng@Sun.COM 		return (6);
1008*9172SFei.Feng@Sun.COM 	case 108:
1009*9172SFei.Feng@Sun.COM 		return (7);
1010*9172SFei.Feng@Sun.COM 	}
1011*9172SFei.Feng@Sun.COM 
1012*9172SFei.Feng@Sun.COM 	return (0);	/* shouldn't get there */
1013*9172SFei.Feng@Sun.COM }
1014*9172SFei.Feng@Sun.COM 
1015*9172SFei.Feng@Sun.COM /*
1016*9172SFei.Feng@Sun.COM  * Return the expected ack rate for a frame transmitted at rate `rate'.
1017*9172SFei.Feng@Sun.COM  */
1018*9172SFei.Feng@Sun.COM static int
1019*9172SFei.Feng@Sun.COM rt2860_ack_rate(struct ieee80211com *ic, int rate)
1020*9172SFei.Feng@Sun.COM {
1021*9172SFei.Feng@Sun.COM 	switch (rate) {
1022*9172SFei.Feng@Sun.COM 	/* CCK rates */
1023*9172SFei.Feng@Sun.COM 	case 2:
1024*9172SFei.Feng@Sun.COM 		return (2);
1025*9172SFei.Feng@Sun.COM 	case 4:
1026*9172SFei.Feng@Sun.COM 	case 11:
1027*9172SFei.Feng@Sun.COM 	case 22:
1028*9172SFei.Feng@Sun.COM 		return ((ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate);
1029*9172SFei.Feng@Sun.COM 
1030*9172SFei.Feng@Sun.COM 	/* OFDM rates */
1031*9172SFei.Feng@Sun.COM 	case 12:
1032*9172SFei.Feng@Sun.COM 	case 18:
1033*9172SFei.Feng@Sun.COM 		return (12);
1034*9172SFei.Feng@Sun.COM 	case 24:
1035*9172SFei.Feng@Sun.COM 	case 36:
1036*9172SFei.Feng@Sun.COM 		return (24);
1037*9172SFei.Feng@Sun.COM 	case 48:
1038*9172SFei.Feng@Sun.COM 	case 72:
1039*9172SFei.Feng@Sun.COM 	case 96:
1040*9172SFei.Feng@Sun.COM 	case 108:
1041*9172SFei.Feng@Sun.COM 		return (48);
1042*9172SFei.Feng@Sun.COM 	}
1043*9172SFei.Feng@Sun.COM 
1044*9172SFei.Feng@Sun.COM 	/* default to 1Mbps */
1045*9172SFei.Feng@Sun.COM 	return (2);
1046*9172SFei.Feng@Sun.COM }
1047*9172SFei.Feng@Sun.COM 
1048*9172SFei.Feng@Sun.COM 
1049*9172SFei.Feng@Sun.COM /*
1050*9172SFei.Feng@Sun.COM  * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
1051*9172SFei.Feng@Sun.COM  * The function automatically determines the operating mode depending on the
1052*9172SFei.Feng@Sun.COM  * given rate. `flags' indicates whether short preamble is in use or not.
1053*9172SFei.Feng@Sun.COM  */
1054*9172SFei.Feng@Sun.COM static uint16_t
1055*9172SFei.Feng@Sun.COM rt2860_txtime(int len, int rate, uint32_t flags)
1056*9172SFei.Feng@Sun.COM {
1057*9172SFei.Feng@Sun.COM 	uint16_t	txtime;
1058*9172SFei.Feng@Sun.COM 
1059*9172SFei.Feng@Sun.COM 	if (RT2860_RATE_IS_OFDM(rate)) {
1060*9172SFei.Feng@Sun.COM 		/* IEEE Std 802.11g-2003, pp. 44 */
1061*9172SFei.Feng@Sun.COM 		txtime = (8 + 4 * len + 3 + rate - 1) / rate;
1062*9172SFei.Feng@Sun.COM 		txtime = 16 + 4 + 4 * txtime + 6;
1063*9172SFei.Feng@Sun.COM 	} else {
1064*9172SFei.Feng@Sun.COM 		/* IEEE Std 802.11b-1999, pp. 28 */
1065*9172SFei.Feng@Sun.COM 		txtime = (16 * len + rate - 1) / rate;
1066*9172SFei.Feng@Sun.COM 		if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
1067*9172SFei.Feng@Sun.COM 			txtime +=  72 + 24;
1068*9172SFei.Feng@Sun.COM 		else
1069*9172SFei.Feng@Sun.COM 			txtime += 144 + 48;
1070*9172SFei.Feng@Sun.COM 	}
1071*9172SFei.Feng@Sun.COM 	return (txtime);
1072*9172SFei.Feng@Sun.COM }
1073*9172SFei.Feng@Sun.COM 
1074*9172SFei.Feng@Sun.COM static int
1075*9172SFei.Feng@Sun.COM rt2860_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
1076*9172SFei.Feng@Sun.COM {
1077*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc = (struct rt2860_softc *)ic;
1078*9172SFei.Feng@Sun.COM 	struct rt2860_tx_ring	*ring;
1079*9172SFei.Feng@Sun.COM 	struct rt2860_tx_data	*data;
1080*9172SFei.Feng@Sun.COM 	struct rt2860_txd	*txd;
1081*9172SFei.Feng@Sun.COM 	struct rt2860_txwi	*txwi;
1082*9172SFei.Feng@Sun.COM 	struct ieee80211_frame	*wh;
1083*9172SFei.Feng@Sun.COM 	struct ieee80211_node	*ni;
1084*9172SFei.Feng@Sun.COM 	int			qid, off, rate, err;
1085*9172SFei.Feng@Sun.COM 	int			mblen, pktlen;
1086*9172SFei.Feng@Sun.COM 	uint_t			hdrlen;
1087*9172SFei.Feng@Sun.COM 	uint8_t			mcs, pid, qsel;
1088*9172SFei.Feng@Sun.COM 	uint16_t		dur;
1089*9172SFei.Feng@Sun.COM 	mblk_t			*m, *m0;
1090*9172SFei.Feng@Sun.COM 
1091*9172SFei.Feng@Sun.COM 	err = DDI_SUCCESS;
1092*9172SFei.Feng@Sun.COM 
1093*9172SFei.Feng@Sun.COM 	mutex_enter(&sc->sc_txlock);
1094*9172SFei.Feng@Sun.COM 	if (RT2860_IS_SUSPEND(sc)) {
1095*9172SFei.Feng@Sun.COM 		err = ENXIO;
1096*9172SFei.Feng@Sun.COM 		goto fail1;
1097*9172SFei.Feng@Sun.COM 	}
1098*9172SFei.Feng@Sun.COM 
1099*9172SFei.Feng@Sun.COM 	if ((type & IEEE80211_FC0_TYPE_MASK) !=
1100*9172SFei.Feng@Sun.COM 	    IEEE80211_FC0_TYPE_DATA)
1101*9172SFei.Feng@Sun.COM 		qid = sc->mgtqid;
1102*9172SFei.Feng@Sun.COM 	else
1103*9172SFei.Feng@Sun.COM 		qid = EDCA_AC_BE;
1104*9172SFei.Feng@Sun.COM 	ring = &sc->txq[qid];
1105*9172SFei.Feng@Sun.COM 
1106*9172SFei.Feng@Sun.COM 	if (SLIST_EMPTY(&sc->data_pool) || (ring->queued > 15)) {
1107*9172SFei.Feng@Sun.COM 		sc->sc_need_sched = 1;
1108*9172SFei.Feng@Sun.COM 		sc->sc_tx_nobuf++;
1109*9172SFei.Feng@Sun.COM 		err = ENOMEM;
1110*9172SFei.Feng@Sun.COM 		goto fail1;
1111*9172SFei.Feng@Sun.COM 	}
1112*9172SFei.Feng@Sun.COM 
1113*9172SFei.Feng@Sun.COM 	/* the data pool contains at least one element, pick the first */
1114*9172SFei.Feng@Sun.COM 	data = SLIST_FIRST(&sc->data_pool);
1115*9172SFei.Feng@Sun.COM 
1116*9172SFei.Feng@Sun.COM 	m = allocb(msgdsize(mp) + 32, BPRI_MED);
1117*9172SFei.Feng@Sun.COM 	if (m == NULL) {
1118*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_send():"
1119*9172SFei.Feng@Sun.COM 		    "rt2560_mgmt_send: can't alloc mblk.\n");
1120*9172SFei.Feng@Sun.COM 		err = DDI_FAILURE;
1121*9172SFei.Feng@Sun.COM 		goto fail1;
1122*9172SFei.Feng@Sun.COM 	}
1123*9172SFei.Feng@Sun.COM 
1124*9172SFei.Feng@Sun.COM 	for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
1125*9172SFei.Feng@Sun.COM 		mblen = MBLKL(m0);
1126*9172SFei.Feng@Sun.COM 		(void) bcopy(m0->b_rptr, m->b_rptr + off, mblen);
1127*9172SFei.Feng@Sun.COM 		off += mblen;
1128*9172SFei.Feng@Sun.COM 	}
1129*9172SFei.Feng@Sun.COM 	m->b_wptr += off;
1130*9172SFei.Feng@Sun.COM 
1131*9172SFei.Feng@Sun.COM 	wh = (struct ieee80211_frame *)m->b_rptr;
1132*9172SFei.Feng@Sun.COM 	ni = ieee80211_find_txnode(ic, wh->i_addr1);
1133*9172SFei.Feng@Sun.COM 	if (ni == NULL) {
1134*9172SFei.Feng@Sun.COM 		err = DDI_FAILURE;
1135*9172SFei.Feng@Sun.COM 		sc->sc_tx_err++;
1136*9172SFei.Feng@Sun.COM 		goto fail2;
1137*9172SFei.Feng@Sun.COM 	}
1138*9172SFei.Feng@Sun.COM 
1139*9172SFei.Feng@Sun.COM 	if ((type & IEEE80211_FC0_TYPE_MASK) ==
1140*9172SFei.Feng@Sun.COM 	    IEEE80211_FC0_TYPE_DATA)
1141*9172SFei.Feng@Sun.COM 		(void) ieee80211_encap(ic, m, ni);
1142*9172SFei.Feng@Sun.COM 
1143*9172SFei.Feng@Sun.COM 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
1144*9172SFei.Feng@Sun.COM 		struct ieee80211_key *k;
1145*9172SFei.Feng@Sun.COM 		k = ieee80211_crypto_encap(ic, m);
1146*9172SFei.Feng@Sun.COM 		if (k == NULL) {
1147*9172SFei.Feng@Sun.COM 			sc->sc_tx_err++;
1148*9172SFei.Feng@Sun.COM 			err = DDI_FAILURE;
1149*9172SFei.Feng@Sun.COM 			goto fail3;
1150*9172SFei.Feng@Sun.COM 		}
1151*9172SFei.Feng@Sun.COM 		/* packet header may have moved, reset our local pointer */
1152*9172SFei.Feng@Sun.COM 		wh = (struct ieee80211_frame *)m->b_rptr;
1153*9172SFei.Feng@Sun.COM 	}
1154*9172SFei.Feng@Sun.COM 	pktlen = msgdsize(m);
1155*9172SFei.Feng@Sun.COM 	hdrlen = sizeof (*wh);
1156*9172SFei.Feng@Sun.COM 
1157*9172SFei.Feng@Sun.COM 	/* pickup a rate */
1158*9172SFei.Feng@Sun.COM 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
1159*9172SFei.Feng@Sun.COM 	    ((type & IEEE80211_FC0_TYPE_MASK) !=
1160*9172SFei.Feng@Sun.COM 	    IEEE80211_FC0_TYPE_DATA))
1161*9172SFei.Feng@Sun.COM 		rate = ni->in_rates.ir_rates[0];
1162*9172SFei.Feng@Sun.COM 	else {
1163*9172SFei.Feng@Sun.COM 		if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE)
1164*9172SFei.Feng@Sun.COM 			rate = ic->ic_fixed_rate;
1165*9172SFei.Feng@Sun.COM 		else
1166*9172SFei.Feng@Sun.COM 			rate = ni->in_rates.ir_rates[ni->in_txrate];
1167*9172SFei.Feng@Sun.COM 	}
1168*9172SFei.Feng@Sun.COM 	rate &= IEEE80211_RATE_VAL;
1169*9172SFei.Feng@Sun.COM 
1170*9172SFei.Feng@Sun.COM 	/* get MCS code from rate */
1171*9172SFei.Feng@Sun.COM 	mcs = rt2860_rate2mcs(rate);
1172*9172SFei.Feng@Sun.COM 
1173*9172SFei.Feng@Sun.COM 	/* setup TX Wireless Information */
1174*9172SFei.Feng@Sun.COM 	txwi = data->txwi;
1175*9172SFei.Feng@Sun.COM 	(void) bzero(txwi, sizeof (struct rt2860_txwi));
1176*9172SFei.Feng@Sun.COM 	txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ?
1177*9172SFei.Feng@Sun.COM 	    RT2860_AID2WCID(ni->in_associd) : 0xff;
1178*9172SFei.Feng@Sun.COM 	txwi->len = LE_16(pktlen);
1179*9172SFei.Feng@Sun.COM 	if (!RT2860_RATE_IS_OFDM(rate)) {
1180*9172SFei.Feng@Sun.COM 		txwi->phy = LE_16(RT2860_PHY_CCK);
1181*9172SFei.Feng@Sun.COM 		if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
1182*9172SFei.Feng@Sun.COM 			mcs |= RT2860_PHY_SHPRE;
1183*9172SFei.Feng@Sun.COM 	} else
1184*9172SFei.Feng@Sun.COM 		txwi->phy = LE_16(RT2860_PHY_OFDM);
1185*9172SFei.Feng@Sun.COM 	txwi->phy |= LE_16(mcs);
1186*9172SFei.Feng@Sun.COM 
1187*9172SFei.Feng@Sun.COM 	/*
1188*9172SFei.Feng@Sun.COM 	 * We store the MCS code into the driver-private PacketID field.
1189*9172SFei.Feng@Sun.COM 	 * The PacketID is latched into TX_STAT_FIFO when Tx completes so
1190*9172SFei.Feng@Sun.COM 	 * that we know at which initial rate the frame was transmitted.
1191*9172SFei.Feng@Sun.COM 	 * We add 1 to the MCS code because setting the PacketID field to
1192*9172SFei.Feng@Sun.COM 	 * 0 means that we don't want feedback in TX_STAT_FIFO.
1193*9172SFei.Feng@Sun.COM 	 */
1194*9172SFei.Feng@Sun.COM 	pid = (mcs + 1) & 0xf;
1195*9172SFei.Feng@Sun.COM 	txwi->len |= LE_16(pid << RT2860_TX_PID_SHIFT);
1196*9172SFei.Feng@Sun.COM 
1197*9172SFei.Feng@Sun.COM 	/* check if RTS/CTS or CTS-to-self protection is required */
1198*9172SFei.Feng@Sun.COM 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
1199*9172SFei.Feng@Sun.COM 	    (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold ||
1200*9172SFei.Feng@Sun.COM 	    ((ic->ic_flags &
1201*9172SFei.Feng@Sun.COM 	    IEEE80211_F_USEPROT) && RT2860_RATE_IS_OFDM(rate))))
1202*9172SFei.Feng@Sun.COM 		txwi->txop = RT2860_TX_TXOP_HT;
1203*9172SFei.Feng@Sun.COM 	else
1204*9172SFei.Feng@Sun.COM 		txwi->txop = RT2860_TX_TXOP_BACKOFF;
1205*9172SFei.Feng@Sun.COM 
1206*9172SFei.Feng@Sun.COM 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1207*9172SFei.Feng@Sun.COM 		txwi->xflags |= RT2860_TX_ACK;
1208*9172SFei.Feng@Sun.COM 
1209*9172SFei.Feng@Sun.COM 		dur = rt2860_txtime(RT2860_ACK_SIZE, rt2860_ack_rate(ic, rate),
1210*9172SFei.Feng@Sun.COM 		    ic->ic_flags) + sc->sifs;
1211*9172SFei.Feng@Sun.COM 		*(uint16_t *)wh->i_dur = LE_16(dur);
1212*9172SFei.Feng@Sun.COM 	}
1213*9172SFei.Feng@Sun.COM 
1214*9172SFei.Feng@Sun.COM 	/* copy and trim 802.11 header */
1215*9172SFei.Feng@Sun.COM 	bcopy(wh, &txwi->wh, hdrlen);
1216*9172SFei.Feng@Sun.COM 	m->b_rptr += hdrlen;
1217*9172SFei.Feng@Sun.COM 	bcopy(m->b_rptr, data->txbuf_dma.mem_va, pktlen - hdrlen);
1218*9172SFei.Feng@Sun.COM 
1219*9172SFei.Feng@Sun.COM 	qsel = (qid < EDCA_NUM_AC) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_MGMT;
1220*9172SFei.Feng@Sun.COM 
1221*9172SFei.Feng@Sun.COM 	/* first segment is TXWI + 802.11 header */
1222*9172SFei.Feng@Sun.COM 	txd = &ring->txd[ring->cur];
1223*9172SFei.Feng@Sun.COM 	txd->sdp0 = LE_32(data->paddr);
1224*9172SFei.Feng@Sun.COM 	txd->sdl0 = LE_16(16 + hdrlen);
1225*9172SFei.Feng@Sun.COM 	txd->flags = qsel;
1226*9172SFei.Feng@Sun.COM 
1227*9172SFei.Feng@Sun.COM 	/* finalize last segment */
1228*9172SFei.Feng@Sun.COM 	txd->sdp1 = LE_32(data->txbuf_dma.cookie.dmac_address);
1229*9172SFei.Feng@Sun.COM 	txd->sdl1 = LE_16(pktlen - hdrlen | RT2860_TX_LS1);
1230*9172SFei.Feng@Sun.COM 
1231*9172SFei.Feng@Sun.COM 	/* remove from the free pool and link it into the SW Tx slot */
1232*9172SFei.Feng@Sun.COM 	SLIST_REMOVE_HEAD(&sc->data_pool, next);
1233*9172SFei.Feng@Sun.COM 	data->ni = ieee80211_ref_node(ni);
1234*9172SFei.Feng@Sun.COM 	ring->data[ring->cur] = data;
1235*9172SFei.Feng@Sun.COM 
1236*9172SFei.Feng@Sun.COM 	(void) ddi_dma_sync(sc->txpool_dma.dma_hdl,
1237*9172SFei.Feng@Sun.COM 	    _PTRDIFF(txwi, sc->txwi),
1238*9172SFei.Feng@Sun.COM 	    (hdrlen + 16 + 2),
1239*9172SFei.Feng@Sun.COM 	    DDI_DMA_SYNC_FORDEV);
1240*9172SFei.Feng@Sun.COM 	RT2860_DMA_SYNC(data->txbuf_dma, DDI_DMA_SYNC_FORDEV);
1241*9172SFei.Feng@Sun.COM 	RT2860_DMA_SYNC(ring->txdesc_dma, DDI_DMA_SYNC_FORDEV);
1242*9172SFei.Feng@Sun.COM 
1243*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_send():"
1244*9172SFei.Feng@Sun.COM 	    "sending frame qid=%d wcid=%d rate=%d cur = %x\n",
1245*9172SFei.Feng@Sun.COM 	    qid, txwi->wcid, rate, ring->cur);
1246*9172SFei.Feng@Sun.COM 
1247*9172SFei.Feng@Sun.COM 	ring->queued++;
1248*9172SFei.Feng@Sun.COM 	ring->cur = (ring->cur + 1) % RT2860_TX_RING_COUNT;
1249*9172SFei.Feng@Sun.COM 
1250*9172SFei.Feng@Sun.COM 	/* kick Tx */
1251*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_TX_CTX_IDX(qid), ring->cur);
1252*9172SFei.Feng@Sun.COM 
1253*9172SFei.Feng@Sun.COM 	sc->sc_tx_timer = 5;
1254*9172SFei.Feng@Sun.COM 
1255*9172SFei.Feng@Sun.COM 	ic->ic_stats.is_tx_frags++;
1256*9172SFei.Feng@Sun.COM 	ic->ic_stats.is_tx_bytes += pktlen;
1257*9172SFei.Feng@Sun.COM 
1258*9172SFei.Feng@Sun.COM fail3:
1259*9172SFei.Feng@Sun.COM 	ieee80211_free_node(ni);
1260*9172SFei.Feng@Sun.COM fail2:
1261*9172SFei.Feng@Sun.COM 	freemsg(m);
1262*9172SFei.Feng@Sun.COM fail1:
1263*9172SFei.Feng@Sun.COM 	if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA ||
1264*9172SFei.Feng@Sun.COM 	    err == DDI_SUCCESS)
1265*9172SFei.Feng@Sun.COM 		freemsg(mp);
1266*9172SFei.Feng@Sun.COM 	mutex_exit(&sc->sc_txlock);
1267*9172SFei.Feng@Sun.COM 	return (err);
1268*9172SFei.Feng@Sun.COM }
1269*9172SFei.Feng@Sun.COM 
1270*9172SFei.Feng@Sun.COM /*
1271*9172SFei.Feng@Sun.COM  * This function is called periodically (every 200ms) during scanning to
1272*9172SFei.Feng@Sun.COM  * switch from one channel to another.
1273*9172SFei.Feng@Sun.COM  */
1274*9172SFei.Feng@Sun.COM static void
1275*9172SFei.Feng@Sun.COM rt2860_next_scan(void *arg)
1276*9172SFei.Feng@Sun.COM {
1277*9172SFei.Feng@Sun.COM 	struct rt2860_softc *sc = (struct rt2860_softc *)arg;
1278*9172SFei.Feng@Sun.COM 	struct ieee80211com *ic = &sc->sc_ic;
1279*9172SFei.Feng@Sun.COM 
1280*9172SFei.Feng@Sun.COM 	if (ic->ic_state == IEEE80211_S_SCAN)
1281*9172SFei.Feng@Sun.COM 		(void) ieee80211_next_scan(ic);
1282*9172SFei.Feng@Sun.COM }
1283*9172SFei.Feng@Sun.COM 
1284*9172SFei.Feng@Sun.COM static void
1285*9172SFei.Feng@Sun.COM rt2860_updateslot(struct rt2860_softc *sc)
1286*9172SFei.Feng@Sun.COM {
1287*9172SFei.Feng@Sun.COM 	struct ieee80211com *ic = &sc->sc_ic;
1288*9172SFei.Feng@Sun.COM 	uint32_t tmp;
1289*9172SFei.Feng@Sun.COM 
1290*9172SFei.Feng@Sun.COM 	tmp = RT2860_READ(sc, RT2860_BKOFF_SLOT_CFG);
1291*9172SFei.Feng@Sun.COM 	tmp &= ~0xff;
1292*9172SFei.Feng@Sun.COM 	tmp |= (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
1293*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_BKOFF_SLOT_CFG, tmp);
1294*9172SFei.Feng@Sun.COM }
1295*9172SFei.Feng@Sun.COM 
1296*9172SFei.Feng@Sun.COM static void
1297*9172SFei.Feng@Sun.COM rt2860_iter_func(void *arg, struct ieee80211_node *ni)
1298*9172SFei.Feng@Sun.COM {
1299*9172SFei.Feng@Sun.COM 	struct rt2860_softc *sc = (struct rt2860_softc *)arg;
1300*9172SFei.Feng@Sun.COM 	uint8_t wcid;
1301*9172SFei.Feng@Sun.COM 
1302*9172SFei.Feng@Sun.COM 	wcid = RT2860_AID2WCID(ni->in_associd);
1303*9172SFei.Feng@Sun.COM 	rt2860_amrr_choose(&sc->amrr, ni, &sc->amn[wcid]);
1304*9172SFei.Feng@Sun.COM }
1305*9172SFei.Feng@Sun.COM 
1306*9172SFei.Feng@Sun.COM static void
1307*9172SFei.Feng@Sun.COM rt2860_updatestats(void *arg)
1308*9172SFei.Feng@Sun.COM {
1309*9172SFei.Feng@Sun.COM 	struct rt2860_softc *sc = (struct rt2860_softc *)arg;
1310*9172SFei.Feng@Sun.COM 	struct ieee80211com *ic = &sc->sc_ic;
1311*9172SFei.Feng@Sun.COM 
1312*9172SFei.Feng@Sun.COM 	if (ic->ic_opmode == IEEE80211_M_STA)
1313*9172SFei.Feng@Sun.COM 		rt2860_iter_func(sc, ic->ic_bss);
1314*9172SFei.Feng@Sun.COM 	else
1315*9172SFei.Feng@Sun.COM 		ieee80211_iterate_nodes(&ic->ic_sta, rt2860_iter_func, arg);
1316*9172SFei.Feng@Sun.COM 
1317*9172SFei.Feng@Sun.COM 	sc->sc_rssadapt_id = timeout(rt2860_updatestats, (void *)sc,
1318*9172SFei.Feng@Sun.COM 	    drv_usectohz(500 * 1000));
1319*9172SFei.Feng@Sun.COM }
1320*9172SFei.Feng@Sun.COM 
1321*9172SFei.Feng@Sun.COM static void
1322*9172SFei.Feng@Sun.COM rt2860_enable_mrr(struct rt2860_softc *sc)
1323*9172SFei.Feng@Sun.COM {
1324*9172SFei.Feng@Sun.COM #define	CCK(mcs)	(mcs)
1325*9172SFei.Feng@Sun.COM #define	OFDM(mcs)	((uint32_t)1 << 3 | (mcs))
1326*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_LG_FBK_CFG0,
1327*9172SFei.Feng@Sun.COM 	    OFDM(6) << 28 |	/* 54->48 */
1328*9172SFei.Feng@Sun.COM 	    OFDM(5) << 24 |	/* 48->36 */
1329*9172SFei.Feng@Sun.COM 	    OFDM(4) << 20 |	/* 36->24 */
1330*9172SFei.Feng@Sun.COM 	    OFDM(3) << 16 |	/* 24->18 */
1331*9172SFei.Feng@Sun.COM 	    OFDM(2) << 12 |	/* 18->12 */
1332*9172SFei.Feng@Sun.COM 	    OFDM(1) <<  8 |	/* 12-> 9 */
1333*9172SFei.Feng@Sun.COM 	    OFDM(0) <<  4 |	/*  9-> 6 */
1334*9172SFei.Feng@Sun.COM 	    OFDM(0));		/*  6-> 6 */
1335*9172SFei.Feng@Sun.COM 
1336*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_LG_FBK_CFG1,
1337*9172SFei.Feng@Sun.COM 	    CCK(2) << 12 |	/* 11->5.5 */
1338*9172SFei.Feng@Sun.COM 	    CCK(1) <<  8 |	/* 5.5-> 2 */
1339*9172SFei.Feng@Sun.COM 	    CCK(0) <<  4 |	/*   2-> 1 */
1340*9172SFei.Feng@Sun.COM 	    CCK(0));		/*   1-> 1 */
1341*9172SFei.Feng@Sun.COM #undef OFDM
1342*9172SFei.Feng@Sun.COM #undef CCK
1343*9172SFei.Feng@Sun.COM }
1344*9172SFei.Feng@Sun.COM 
1345*9172SFei.Feng@Sun.COM static void
1346*9172SFei.Feng@Sun.COM rt2860_set_txpreamble(struct rt2860_softc *sc)
1347*9172SFei.Feng@Sun.COM {
1348*9172SFei.Feng@Sun.COM 	uint32_t tmp;
1349*9172SFei.Feng@Sun.COM 
1350*9172SFei.Feng@Sun.COM 	tmp = RT2860_READ(sc, RT2860_AUTO_RSP_CFG);
1351*9172SFei.Feng@Sun.COM 	tmp &= ~RT2860_CCK_SHORT_EN;
1352*9172SFei.Feng@Sun.COM 	if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE)
1353*9172SFei.Feng@Sun.COM 		tmp |= RT2860_CCK_SHORT_EN;
1354*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_AUTO_RSP_CFG, tmp);
1355*9172SFei.Feng@Sun.COM }
1356*9172SFei.Feng@Sun.COM 
1357*9172SFei.Feng@Sun.COM static void
1358*9172SFei.Feng@Sun.COM rt2860_set_bssid(struct rt2860_softc *sc, const uint8_t *bssid)
1359*9172SFei.Feng@Sun.COM {
1360*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_BSSID_DW0,
1361*9172SFei.Feng@Sun.COM 	    bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24);
1362*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_BSSID_DW1,
1363*9172SFei.Feng@Sun.COM 	    bssid[4] | bssid[5] << 8);
1364*9172SFei.Feng@Sun.COM }
1365*9172SFei.Feng@Sun.COM 
1366*9172SFei.Feng@Sun.COM static void
1367*9172SFei.Feng@Sun.COM rt2860_set_basicrates(struct rt2860_softc *sc)
1368*9172SFei.Feng@Sun.COM {
1369*9172SFei.Feng@Sun.COM 	struct ieee80211com *ic = &sc->sc_ic;
1370*9172SFei.Feng@Sun.COM 
1371*9172SFei.Feng@Sun.COM 	/* set basic rates mask */
1372*9172SFei.Feng@Sun.COM 	if (ic->ic_curmode == IEEE80211_MODE_11B)
1373*9172SFei.Feng@Sun.COM 		RT2860_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x003);
1374*9172SFei.Feng@Sun.COM 	else if (ic->ic_curmode == IEEE80211_MODE_11A)
1375*9172SFei.Feng@Sun.COM 		RT2860_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x150);
1376*9172SFei.Feng@Sun.COM 	else	/* 11g */
1377*9172SFei.Feng@Sun.COM 		RT2860_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x15f);
1378*9172SFei.Feng@Sun.COM }
1379*9172SFei.Feng@Sun.COM 
1380*9172SFei.Feng@Sun.COM static void
1381*9172SFei.Feng@Sun.COM rt2860_amrr_node_init(const struct rt2860_amrr *amrr,
1382*9172SFei.Feng@Sun.COM     struct rt2860_amrr_node *amn)
1383*9172SFei.Feng@Sun.COM {
1384*9172SFei.Feng@Sun.COM 	amn->amn_success = 0;
1385*9172SFei.Feng@Sun.COM 	amn->amn_recovery = 0;
1386*9172SFei.Feng@Sun.COM 	amn->amn_txcnt = amn->amn_retrycnt = 0;
1387*9172SFei.Feng@Sun.COM 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
1388*9172SFei.Feng@Sun.COM }
1389*9172SFei.Feng@Sun.COM 
1390*9172SFei.Feng@Sun.COM static void
1391*9172SFei.Feng@Sun.COM rt2860_amrr_choose(struct rt2860_amrr *amrr, struct ieee80211_node *ni,
1392*9172SFei.Feng@Sun.COM     struct rt2860_amrr_node *amn)
1393*9172SFei.Feng@Sun.COM {
1394*9172SFei.Feng@Sun.COM #define	RV(rate)	((rate) & IEEE80211_RATE_VAL)
1395*9172SFei.Feng@Sun.COM #define	is_success(amn)	\
1396*9172SFei.Feng@Sun.COM 	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
1397*9172SFei.Feng@Sun.COM #define	is_failure(amn)	\
1398*9172SFei.Feng@Sun.COM 	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
1399*9172SFei.Feng@Sun.COM #define	is_enough(amn)		\
1400*9172SFei.Feng@Sun.COM 	((amn)->amn_txcnt > 10)
1401*9172SFei.Feng@Sun.COM #define	is_min_rate(ni)		\
1402*9172SFei.Feng@Sun.COM 	((ni)->in_txrate == 0)
1403*9172SFei.Feng@Sun.COM #define	is_max_rate(ni)		\
1404*9172SFei.Feng@Sun.COM 	((ni)->in_txrate == (ni)->in_rates.ir_nrates - 1)
1405*9172SFei.Feng@Sun.COM #define	increase_rate(ni)	\
1406*9172SFei.Feng@Sun.COM 	((ni)->in_txrate++)
1407*9172SFei.Feng@Sun.COM #define	decrease_rate(ni)	\
1408*9172SFei.Feng@Sun.COM 	((ni)->in_txrate--)
1409*9172SFei.Feng@Sun.COM #define	reset_cnt(amn)		\
1410*9172SFei.Feng@Sun.COM 	{ (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; }
1411*9172SFei.Feng@Sun.COM 
1412*9172SFei.Feng@Sun.COM 	int need_change = 0;
1413*9172SFei.Feng@Sun.COM 
1414*9172SFei.Feng@Sun.COM 	if (is_success(amn) && is_enough(amn)) {
1415*9172SFei.Feng@Sun.COM 		amn->amn_success++;
1416*9172SFei.Feng@Sun.COM 		if (amn->amn_success >= amn->amn_success_threshold &&
1417*9172SFei.Feng@Sun.COM 		    !is_max_rate(ni)) {
1418*9172SFei.Feng@Sun.COM 			amn->amn_recovery = 1;
1419*9172SFei.Feng@Sun.COM 			amn->amn_success = 0;
1420*9172SFei.Feng@Sun.COM 			increase_rate(ni);
1421*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_amrr_choose(): "
1422*9172SFei.Feng@Sun.COM 			    "increase rate = %d, #tx = %d, #retries = %d\n",
1423*9172SFei.Feng@Sun.COM 			    RV(ni->in_rates.ir_rates[ni->in_txrate]),
1424*9172SFei.Feng@Sun.COM 			    amn->amn_txcnt, amn->amn_retrycnt);
1425*9172SFei.Feng@Sun.COM 			need_change = 1;
1426*9172SFei.Feng@Sun.COM 		} else {
1427*9172SFei.Feng@Sun.COM 			amn->amn_recovery = 0;
1428*9172SFei.Feng@Sun.COM 		}
1429*9172SFei.Feng@Sun.COM 	} else if (is_failure(amn)) {
1430*9172SFei.Feng@Sun.COM 		amn->amn_success = 0;
1431*9172SFei.Feng@Sun.COM 		if (!is_min_rate(ni)) {
1432*9172SFei.Feng@Sun.COM 			if (amn->amn_recovery) {
1433*9172SFei.Feng@Sun.COM 				amn->amn_success_threshold *= 2;
1434*9172SFei.Feng@Sun.COM 				if (amn->amn_success_threshold >
1435*9172SFei.Feng@Sun.COM 				    amrr->amrr_max_success_threshold)
1436*9172SFei.Feng@Sun.COM 					amn->amn_success_threshold =
1437*9172SFei.Feng@Sun.COM 					    amrr->amrr_max_success_threshold;
1438*9172SFei.Feng@Sun.COM 			} else {
1439*9172SFei.Feng@Sun.COM 				amn->amn_success_threshold =
1440*9172SFei.Feng@Sun.COM 				    amrr->amrr_min_success_threshold;
1441*9172SFei.Feng@Sun.COM 			}
1442*9172SFei.Feng@Sun.COM 			decrease_rate(ni);
1443*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_amrr_choose(): "
1444*9172SFei.Feng@Sun.COM 			    "decrease rate = %d, #tx = %d, #retries = %d\n",
1445*9172SFei.Feng@Sun.COM 			    RV(ni->in_rates.ir_rates[ni->in_txrate]),
1446*9172SFei.Feng@Sun.COM 			    amn->amn_txcnt, amn->amn_retrycnt);
1447*9172SFei.Feng@Sun.COM 			need_change = 1;
1448*9172SFei.Feng@Sun.COM 		}
1449*9172SFei.Feng@Sun.COM 		amn->amn_recovery = 0;
1450*9172SFei.Feng@Sun.COM 	}
1451*9172SFei.Feng@Sun.COM 
1452*9172SFei.Feng@Sun.COM 	if (is_enough(amn) || need_change)
1453*9172SFei.Feng@Sun.COM 		reset_cnt(amn);
1454*9172SFei.Feng@Sun.COM #undef RV
1455*9172SFei.Feng@Sun.COM }
1456*9172SFei.Feng@Sun.COM 
1457*9172SFei.Feng@Sun.COM static void
1458*9172SFei.Feng@Sun.COM rt2860_newassoc(struct ieee80211com *ic, struct ieee80211_node *in, int isnew)
1459*9172SFei.Feng@Sun.COM {
1460*9172SFei.Feng@Sun.COM 	struct rt2860_softc *sc = (struct rt2860_softc *)ic;
1461*9172SFei.Feng@Sun.COM 	uint32_t off;
1462*9172SFei.Feng@Sun.COM 	uint8_t *fptr, wcid = 0;
1463*9172SFei.Feng@Sun.COM 	int i;
1464*9172SFei.Feng@Sun.COM 
1465*9172SFei.Feng@Sun.COM 	if (isnew && in->in_associd != 0) {
1466*9172SFei.Feng@Sun.COM 		/* only interested in true associations */
1467*9172SFei.Feng@Sun.COM 		wcid = RT2860_AID2WCID(in->in_associd);
1468*9172SFei.Feng@Sun.COM 
1469*9172SFei.Feng@Sun.COM 		/* init WCID table entry */
1470*9172SFei.Feng@Sun.COM 		off = RT2860_WCID_ENTRY(wcid);
1471*9172SFei.Feng@Sun.COM 		fptr = in->in_macaddr;
1472*9172SFei.Feng@Sun.COM 		for (i = 0; i < IEEE80211_ADDR_LEN; i++)
1473*9172SFei.Feng@Sun.COM 			rt2860_mem_write1(sc, off++, *fptr++);
1474*9172SFei.Feng@Sun.COM 	}
1475*9172SFei.Feng@Sun.COM 	rt2860_amrr_node_init(&sc->amrr, &sc->amn[wcid]);
1476*9172SFei.Feng@Sun.COM 
1477*9172SFei.Feng@Sun.COM 	/* set rate to some reasonable initial value */
1478*9172SFei.Feng@Sun.COM 	i = in->in_rates.ir_nrates - 1;
1479*9172SFei.Feng@Sun.COM 	for (; i > 0 && (in->in_rates.ir_rates[i] & IEEE80211_RATE_VAL) > 72; )
1480*9172SFei.Feng@Sun.COM 		i--;
1481*9172SFei.Feng@Sun.COM 	in->in_txrate = i;
1482*9172SFei.Feng@Sun.COM 
1483*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_80211, "rwn: rt2860_newassoc(): "
1484*9172SFei.Feng@Sun.COM 	    "new assoc isnew=%d WCID=%d, initial rate=%d\n",
1485*9172SFei.Feng@Sun.COM 	    isnew, wcid,
1486*9172SFei.Feng@Sun.COM 	    in->in_rates.ir_rates[i] & IEEE80211_RATE_VAL);
1487*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_80211, "rwn: rt2860_newassoc(): "
1488*9172SFei.Feng@Sun.COM 	    "addr=%x:%x:%x:%x:%x:%x\n",
1489*9172SFei.Feng@Sun.COM 	    in->in_macaddr[0], in->in_macaddr[1], in->in_macaddr[2],
1490*9172SFei.Feng@Sun.COM 	    in->in_macaddr[3], in->in_macaddr[4], in->in_macaddr[5]);
1491*9172SFei.Feng@Sun.COM }
1492*9172SFei.Feng@Sun.COM 
1493*9172SFei.Feng@Sun.COM void
1494*9172SFei.Feng@Sun.COM rt2860_enable_tsf_sync(struct rt2860_softc *sc)
1495*9172SFei.Feng@Sun.COM {
1496*9172SFei.Feng@Sun.COM 	struct ieee80211com *ic = &sc->sc_ic;
1497*9172SFei.Feng@Sun.COM 	uint32_t tmp;
1498*9172SFei.Feng@Sun.COM 
1499*9172SFei.Feng@Sun.COM 	tmp = RT2860_READ(sc, RT2860_BCN_TIME_CFG);
1500*9172SFei.Feng@Sun.COM 
1501*9172SFei.Feng@Sun.COM 	tmp &= ~0x1fffff;
1502*9172SFei.Feng@Sun.COM 	tmp |= ic->ic_bss->in_intval * 16;
1503*9172SFei.Feng@Sun.COM 	tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN;
1504*9172SFei.Feng@Sun.COM 	if (ic->ic_opmode == IEEE80211_M_STA) {
1505*9172SFei.Feng@Sun.COM 		/*
1506*9172SFei.Feng@Sun.COM 		 * Local TSF is always updated with remote TSF on beacon
1507*9172SFei.Feng@Sun.COM 		 * reception.
1508*9172SFei.Feng@Sun.COM 		 */
1509*9172SFei.Feng@Sun.COM 		tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT;
1510*9172SFei.Feng@Sun.COM 	}
1511*9172SFei.Feng@Sun.COM 
1512*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_BCN_TIME_CFG, tmp);
1513*9172SFei.Feng@Sun.COM }
1514*9172SFei.Feng@Sun.COM 
1515*9172SFei.Feng@Sun.COM static int
1516*9172SFei.Feng@Sun.COM rt2860_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
1517*9172SFei.Feng@Sun.COM {
1518*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc = (struct rt2860_softc *)ic;
1519*9172SFei.Feng@Sun.COM 	enum ieee80211_state	ostate;
1520*9172SFei.Feng@Sun.COM 	int			err;
1521*9172SFei.Feng@Sun.COM 	uint32_t		tmp;
1522*9172SFei.Feng@Sun.COM 
1523*9172SFei.Feng@Sun.COM 	ostate = ic->ic_state;
1524*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_80211, "rwn: rt2860_newstate(): "
1525*9172SFei.Feng@Sun.COM 	    "%x -> %x!\n", ostate, nstate);
1526*9172SFei.Feng@Sun.COM 
1527*9172SFei.Feng@Sun.COM 	RT2860_GLOCK(sc);
1528*9172SFei.Feng@Sun.COM 	if (sc->sc_scan_id != 0) {
1529*9172SFei.Feng@Sun.COM 		(void) untimeout(sc->sc_scan_id);
1530*9172SFei.Feng@Sun.COM 		sc->sc_scan_id = 0;
1531*9172SFei.Feng@Sun.COM 	}
1532*9172SFei.Feng@Sun.COM 	if (sc->sc_rssadapt_id != 0) {
1533*9172SFei.Feng@Sun.COM 		(void) untimeout(sc->sc_rssadapt_id);
1534*9172SFei.Feng@Sun.COM 		sc->sc_rssadapt_id = 0;
1535*9172SFei.Feng@Sun.COM 	}
1536*9172SFei.Feng@Sun.COM 	if (ostate == IEEE80211_S_RUN) {
1537*9172SFei.Feng@Sun.COM 		/* turn link LED off */
1538*9172SFei.Feng@Sun.COM 		rt2860_set_leds(sc, RT2860_LED_RADIO);
1539*9172SFei.Feng@Sun.COM 	}
1540*9172SFei.Feng@Sun.COM 
1541*9172SFei.Feng@Sun.COM 	switch (nstate) {
1542*9172SFei.Feng@Sun.COM 	case IEEE80211_S_INIT:
1543*9172SFei.Feng@Sun.COM 		if (ostate == IEEE80211_S_RUN) {
1544*9172SFei.Feng@Sun.COM 			/* abort TSF synchronization */
1545*9172SFei.Feng@Sun.COM 			tmp = RT2860_READ(sc, RT2860_BCN_TIME_CFG);
1546*9172SFei.Feng@Sun.COM 			RT2860_WRITE(sc, RT2860_BCN_TIME_CFG,
1547*9172SFei.Feng@Sun.COM 			    tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN |
1548*9172SFei.Feng@Sun.COM 			    RT2860_TBTT_TIMER_EN));
1549*9172SFei.Feng@Sun.COM 		}
1550*9172SFei.Feng@Sun.COM 		break;
1551*9172SFei.Feng@Sun.COM 
1552*9172SFei.Feng@Sun.COM 	case IEEE80211_S_SCAN:
1553*9172SFei.Feng@Sun.COM 		rt2860_set_chan(sc, ic->ic_curchan);
1554*9172SFei.Feng@Sun.COM 		sc->sc_scan_id = timeout(rt2860_next_scan, (void *)sc,
1555*9172SFei.Feng@Sun.COM 		    drv_usectohz(200000));
1556*9172SFei.Feng@Sun.COM 		break;
1557*9172SFei.Feng@Sun.COM 
1558*9172SFei.Feng@Sun.COM 	case IEEE80211_S_AUTH:
1559*9172SFei.Feng@Sun.COM 	case IEEE80211_S_ASSOC:
1560*9172SFei.Feng@Sun.COM 		rt2860_set_chan(sc, ic->ic_curchan);
1561*9172SFei.Feng@Sun.COM 		break;
1562*9172SFei.Feng@Sun.COM 
1563*9172SFei.Feng@Sun.COM 	case IEEE80211_S_RUN:
1564*9172SFei.Feng@Sun.COM 		rt2860_set_chan(sc, ic->ic_curchan);
1565*9172SFei.Feng@Sun.COM 
1566*9172SFei.Feng@Sun.COM 		if (ic->ic_opmode != IEEE80211_M_MONITOR) {
1567*9172SFei.Feng@Sun.COM 			rt2860_updateslot(sc);
1568*9172SFei.Feng@Sun.COM 			rt2860_enable_mrr(sc);
1569*9172SFei.Feng@Sun.COM 			rt2860_set_txpreamble(sc);
1570*9172SFei.Feng@Sun.COM 			rt2860_set_basicrates(sc);
1571*9172SFei.Feng@Sun.COM 			rt2860_set_bssid(sc, ic->ic_bss->in_bssid);
1572*9172SFei.Feng@Sun.COM 		}
1573*9172SFei.Feng@Sun.COM 		if (ic->ic_opmode == IEEE80211_M_STA) {
1574*9172SFei.Feng@Sun.COM 			/* fake a join to init the tx rate */
1575*9172SFei.Feng@Sun.COM 			rt2860_newassoc(ic, ic->ic_bss, 1);
1576*9172SFei.Feng@Sun.COM 		}
1577*9172SFei.Feng@Sun.COM 
1578*9172SFei.Feng@Sun.COM 		if (ic->ic_opmode != IEEE80211_M_MONITOR) {
1579*9172SFei.Feng@Sun.COM 			rt2860_enable_tsf_sync(sc);
1580*9172SFei.Feng@Sun.COM 			sc->sc_rssadapt_id = timeout(rt2860_updatestats,
1581*9172SFei.Feng@Sun.COM 			    (void *)sc, drv_usectohz(500 * 1000));
1582*9172SFei.Feng@Sun.COM 		}
1583*9172SFei.Feng@Sun.COM 
1584*9172SFei.Feng@Sun.COM 		/* turn link LED on */
1585*9172SFei.Feng@Sun.COM 		rt2860_set_leds(sc, RT2860_LED_RADIO |
1586*9172SFei.Feng@Sun.COM 		    (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ?
1587*9172SFei.Feng@Sun.COM 		    RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ));
1588*9172SFei.Feng@Sun.COM 		break;
1589*9172SFei.Feng@Sun.COM 	}
1590*9172SFei.Feng@Sun.COM 
1591*9172SFei.Feng@Sun.COM 	RT2860_GUNLOCK(sc);
1592*9172SFei.Feng@Sun.COM 
1593*9172SFei.Feng@Sun.COM 	err = sc->sc_newstate(ic, nstate, arg);
1594*9172SFei.Feng@Sun.COM 
1595*9172SFei.Feng@Sun.COM 	return (err);
1596*9172SFei.Feng@Sun.COM }
1597*9172SFei.Feng@Sun.COM 
1598*9172SFei.Feng@Sun.COM /*
1599*9172SFei.Feng@Sun.COM  * Return the Rx chain with the highest RSSI for a given frame.
1600*9172SFei.Feng@Sun.COM  */
1601*9172SFei.Feng@Sun.COM static uint8_t
1602*9172SFei.Feng@Sun.COM rt2860_maxrssi_chain(struct rt2860_softc *sc, const struct rt2860_rxwi *rxwi)
1603*9172SFei.Feng@Sun.COM {
1604*9172SFei.Feng@Sun.COM 	uint8_t rxchain = 0;
1605*9172SFei.Feng@Sun.COM 
1606*9172SFei.Feng@Sun.COM 	if (sc->nrxchains > 1)
1607*9172SFei.Feng@Sun.COM 		if (rxwi->rssi[1] > rxwi->rssi[rxchain])
1608*9172SFei.Feng@Sun.COM 			rxchain = 1;
1609*9172SFei.Feng@Sun.COM 	if (sc->nrxchains > 2)
1610*9172SFei.Feng@Sun.COM 		if (rxwi->rssi[2] > rxwi->rssi[rxchain])
1611*9172SFei.Feng@Sun.COM 			rxchain = 2;
1612*9172SFei.Feng@Sun.COM 
1613*9172SFei.Feng@Sun.COM 	return (rxchain);
1614*9172SFei.Feng@Sun.COM }
1615*9172SFei.Feng@Sun.COM 
1616*9172SFei.Feng@Sun.COM static void
1617*9172SFei.Feng@Sun.COM rt2860_drain_stats_fifo(struct rt2860_softc *sc)
1618*9172SFei.Feng@Sun.COM {
1619*9172SFei.Feng@Sun.COM 	struct rt2860_amrr_node *amn;
1620*9172SFei.Feng@Sun.COM 	uint32_t stat;
1621*9172SFei.Feng@Sun.COM 	uint8_t wcid, mcs, pid;
1622*9172SFei.Feng@Sun.COM 
1623*9172SFei.Feng@Sun.COM 	/* drain Tx status FIFO (maxsize = 16) */
1624*9172SFei.Feng@Sun.COM 	while ((stat = RT2860_READ(sc, RT2860_TX_STAT_FIFO)) & RT2860_TXQ_VLD) {
1625*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_drain_stats_fifo(): "
1626*9172SFei.Feng@Sun.COM 		    "tx stat 0x%08\n", stat);
1627*9172SFei.Feng@Sun.COM 
1628*9172SFei.Feng@Sun.COM 		wcid = (stat >> 8) & 0xff;
1629*9172SFei.Feng@Sun.COM 
1630*9172SFei.Feng@Sun.COM 		/* if no ACK was requested, no feedback is available */
1631*9172SFei.Feng@Sun.COM 		if (!(stat & RT2860_TXQ_ACKREQ) || wcid == 0xff)
1632*9172SFei.Feng@Sun.COM 			continue;
1633*9172SFei.Feng@Sun.COM 		/* update per-STA AMRR stats */
1634*9172SFei.Feng@Sun.COM 		amn = &sc->amn[wcid];
1635*9172SFei.Feng@Sun.COM 		amn->amn_txcnt++;
1636*9172SFei.Feng@Sun.COM 		if (stat & RT2860_TXQ_OK) {
1637*9172SFei.Feng@Sun.COM 			/*
1638*9172SFei.Feng@Sun.COM 			 * Check if there were retries, ie if the Tx success
1639*9172SFei.Feng@Sun.COM 			 * rate is different from the requested rate.  Note
1640*9172SFei.Feng@Sun.COM 			 * that it works only because we do not allow rate
1641*9172SFei.Feng@Sun.COM 			 * fallback from OFDM to CCK.
1642*9172SFei.Feng@Sun.COM 			 */
1643*9172SFei.Feng@Sun.COM 			mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f;
1644*9172SFei.Feng@Sun.COM 			pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf;
1645*9172SFei.Feng@Sun.COM 			if (mcs + 1 != pid)
1646*9172SFei.Feng@Sun.COM 				amn->amn_retrycnt++;
1647*9172SFei.Feng@Sun.COM 		} else
1648*9172SFei.Feng@Sun.COM 			amn->amn_retrycnt++;
1649*9172SFei.Feng@Sun.COM 	}
1650*9172SFei.Feng@Sun.COM }
1651*9172SFei.Feng@Sun.COM 
1652*9172SFei.Feng@Sun.COM /*ARGSUSED*/
1653*9172SFei.Feng@Sun.COM static void
1654*9172SFei.Feng@Sun.COM rt2860_tx_intr(struct rt2860_softc *sc, int qid)
1655*9172SFei.Feng@Sun.COM {
1656*9172SFei.Feng@Sun.COM 	struct rt2860_tx_ring	*ring = &sc->txq[qid];
1657*9172SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
1658*9172SFei.Feng@Sun.COM 	uint32_t hw;
1659*9172SFei.Feng@Sun.COM 
1660*9172SFei.Feng@Sun.COM 	rt2860_drain_stats_fifo(sc);
1661*9172SFei.Feng@Sun.COM 
1662*9172SFei.Feng@Sun.COM 	mutex_enter(&sc->sc_txlock);
1663*9172SFei.Feng@Sun.COM 	hw = RT2860_READ(sc, RT2860_TX_DTX_IDX(qid));
1664*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_TX, "rwn: rwn_tx_intr():"
1665*9172SFei.Feng@Sun.COM 	    "hw = %x, ring->next = %x, queued = %d\n",
1666*9172SFei.Feng@Sun.COM 	    hw, ring->next, ring->queued);
1667*9172SFei.Feng@Sun.COM 	while (ring->next != hw) {
1668*9172SFei.Feng@Sun.COM 		struct rt2860_txd *txd = &ring->txd[ring->next];
1669*9172SFei.Feng@Sun.COM 		struct rt2860_tx_data *data = ring->data[ring->next];
1670*9172SFei.Feng@Sun.COM 
1671*9172SFei.Feng@Sun.COM 		if (data != NULL) {
1672*9172SFei.Feng@Sun.COM 			RT2860_DMA_SYNC(data->txbuf_dma, DDI_DMA_SYNC_FORDEV);
1673*9172SFei.Feng@Sun.COM 			if (data->ni != NULL) {
1674*9172SFei.Feng@Sun.COM 				ieee80211_free_node(data->ni);
1675*9172SFei.Feng@Sun.COM 				data->ni = NULL;
1676*9172SFei.Feng@Sun.COM 			}
1677*9172SFei.Feng@Sun.COM 			SLIST_INSERT_HEAD(&sc->data_pool, data, next);
1678*9172SFei.Feng@Sun.COM 			ring->data[ring->next] = NULL;
1679*9172SFei.Feng@Sun.COM 		}
1680*9172SFei.Feng@Sun.COM 
1681*9172SFei.Feng@Sun.COM 		txd->sdl0 &= ~LE_16(RT2860_TX_DDONE);
1682*9172SFei.Feng@Sun.COM 
1683*9172SFei.Feng@Sun.COM 		(void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
1684*9172SFei.Feng@Sun.COM 		    ring->next * sizeof (struct rt2860_txd),
1685*9172SFei.Feng@Sun.COM 		    sizeof (struct rt2860_txd),
1686*9172SFei.Feng@Sun.COM 		    DDI_DMA_SYNC_FORDEV);
1687*9172SFei.Feng@Sun.COM 
1688*9172SFei.Feng@Sun.COM 		ring->queued--;
1689*9172SFei.Feng@Sun.COM 		ring->next = (ring->next + 1) % RT2860_TX_RING_COUNT;
1690*9172SFei.Feng@Sun.COM 
1691*9172SFei.Feng@Sun.COM 		if (sc->sc_need_sched &&
1692*9172SFei.Feng@Sun.COM 		    (ring->queued < RT2860_TX_RING_COUNT)) {
1693*9172SFei.Feng@Sun.COM 			sc->sc_need_sched = 0;
1694*9172SFei.Feng@Sun.COM 			mac_tx_update(ic->ic_mach);
1695*9172SFei.Feng@Sun.COM 		}
1696*9172SFei.Feng@Sun.COM 	}
1697*9172SFei.Feng@Sun.COM 	sc->sc_tx_timer = 0;
1698*9172SFei.Feng@Sun.COM 	mutex_exit(&sc->sc_txlock);
1699*9172SFei.Feng@Sun.COM }
1700*9172SFei.Feng@Sun.COM static void
1701*9172SFei.Feng@Sun.COM rt2860_rx_intr(struct rt2860_softc *sc)
1702*9172SFei.Feng@Sun.COM {
1703*9172SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
1704*9172SFei.Feng@Sun.COM 	struct ieee80211_node	*ni;
1705*9172SFei.Feng@Sun.COM 	struct ieee80211_frame	*wh;
1706*9172SFei.Feng@Sun.COM 	int	pktlen;
1707*9172SFei.Feng@Sun.COM 	uint8_t ant, rssi, *rxbuf;
1708*9172SFei.Feng@Sun.COM 	mblk_t	*mp0;
1709*9172SFei.Feng@Sun.COM 
1710*9172SFei.Feng@Sun.COM 	mutex_enter(&sc->sc_rxlock);
1711*9172SFei.Feng@Sun.COM 	for (;;) {
1712*9172SFei.Feng@Sun.COM 		struct rt2860_rx_data *data = &sc->rxq.data[sc->rxq.cur];
1713*9172SFei.Feng@Sun.COM 		struct rt2860_rxd *rxd = &sc->rxq.rxd[sc->rxq.cur];
1714*9172SFei.Feng@Sun.COM 		struct rt2860_rxwi *rxwi;
1715*9172SFei.Feng@Sun.COM 
1716*9172SFei.Feng@Sun.COM 		(void) ddi_dma_sync(sc->rxq.rxdesc_dma.dma_hdl,
1717*9172SFei.Feng@Sun.COM 		    sc->rxq.cur * sizeof (struct rt2860_rxd),
1718*9172SFei.Feng@Sun.COM 		    sizeof (struct rt2860_rxd),
1719*9172SFei.Feng@Sun.COM 		    DDI_DMA_SYNC_FORKERNEL);
1720*9172SFei.Feng@Sun.COM 
1721*9172SFei.Feng@Sun.COM 		if (!(rxd->sdl0 & LE_16(RT2860_RX_DDONE))) {
1722*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr(): "
1723*9172SFei.Feng@Sun.COM 			    "rx done!\n");
1724*9172SFei.Feng@Sun.COM 			break;
1725*9172SFei.Feng@Sun.COM 		}
1726*9172SFei.Feng@Sun.COM 
1727*9172SFei.Feng@Sun.COM 		if (rxd->flags &
1728*9172SFei.Feng@Sun.COM 		    LE_32(RT2860_RX_CRCERR | RT2860_RX_ICVERR)) {
1729*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr(): "
1730*9172SFei.Feng@Sun.COM 			    "rx crc error & rx icv error!\n");
1731*9172SFei.Feng@Sun.COM 			sc->sc_rx_err++;
1732*9172SFei.Feng@Sun.COM 			goto skip;
1733*9172SFei.Feng@Sun.COM 		}
1734*9172SFei.Feng@Sun.COM 
1735*9172SFei.Feng@Sun.COM 		if (rxd->flags & LE_32(RT2860_RX_MICERR)) {
1736*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr(): "
1737*9172SFei.Feng@Sun.COM 			    "rx mic error!\n");
1738*9172SFei.Feng@Sun.COM 			sc->sc_rx_err++;
1739*9172SFei.Feng@Sun.COM 			goto skip;
1740*9172SFei.Feng@Sun.COM 		}
1741*9172SFei.Feng@Sun.COM 
1742*9172SFei.Feng@Sun.COM 		(void) ddi_dma_sync(data->rxbuf_dma.dma_hdl,
1743*9172SFei.Feng@Sun.COM 		    data->rxbuf_dma.offset,
1744*9172SFei.Feng@Sun.COM 		    data->rxbuf_dma.alength,
1745*9172SFei.Feng@Sun.COM 		    DDI_DMA_SYNC_FORCPU);
1746*9172SFei.Feng@Sun.COM 
1747*9172SFei.Feng@Sun.COM 		rxbuf = (uint8_t *)data->rxbuf_dma.mem_va;
1748*9172SFei.Feng@Sun.COM 		rxd->sdp0 = LE_32(data->rxbuf_dma.cookie.dmac_address);
1749*9172SFei.Feng@Sun.COM 		rxwi = (struct rt2860_rxwi *)rxbuf;
1750*9172SFei.Feng@Sun.COM 		rxbuf = (uint8_t *)(rxwi + 1);
1751*9172SFei.Feng@Sun.COM 		pktlen = LE_16(rxwi->len) & 0xfff;
1752*9172SFei.Feng@Sun.COM 
1753*9172SFei.Feng@Sun.COM 		mp0 = allocb(sc->sc_dmabuf_size, BPRI_MED);
1754*9172SFei.Feng@Sun.COM 		if (mp0 == NULL) {
1755*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr():"
1756*9172SFei.Feng@Sun.COM 			    "alloc mblk error\n");
1757*9172SFei.Feng@Sun.COM 			sc->sc_rx_nobuf++;
1758*9172SFei.Feng@Sun.COM 			goto skip;
1759*9172SFei.Feng@Sun.COM 		}
1760*9172SFei.Feng@Sun.COM 		bcopy(rxbuf, mp0->b_rptr, pktlen);
1761*9172SFei.Feng@Sun.COM 		mp0->b_wptr += pktlen;
1762*9172SFei.Feng@Sun.COM 
1763*9172SFei.Feng@Sun.COM 		wh = (struct ieee80211_frame *)mp0->b_rptr;
1764*9172SFei.Feng@Sun.COM 
1765*9172SFei.Feng@Sun.COM 		/* HW may insert 2 padding bytes after 802.11 header */
1766*9172SFei.Feng@Sun.COM 		if (rxd->flags & LE_32(RT2860_RX_L2PAD)) {
1767*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr():"
1768*9172SFei.Feng@Sun.COM 			    "2 padding bytes after 80211 header!\n");
1769*9172SFei.Feng@Sun.COM 		}
1770*9172SFei.Feng@Sun.COM 
1771*9172SFei.Feng@Sun.COM 		ant = rt2860_maxrssi_chain(sc, rxwi);
1772*9172SFei.Feng@Sun.COM 		rssi = rxwi->rssi[ant];
1773*9172SFei.Feng@Sun.COM 		/* grab a reference to the source node */
1774*9172SFei.Feng@Sun.COM 		ni = ieee80211_find_rxnode(ic, wh);
1775*9172SFei.Feng@Sun.COM 
1776*9172SFei.Feng@Sun.COM 		ieee80211_input(ic, mp0, ni, rssi, 0);
1777*9172SFei.Feng@Sun.COM 
1778*9172SFei.Feng@Sun.COM 		/* node is no longer needed */
1779*9172SFei.Feng@Sun.COM 		ieee80211_free_node(ni);
1780*9172SFei.Feng@Sun.COM skip:
1781*9172SFei.Feng@Sun.COM 		rxd->sdl0 &= ~LE_16(RT2860_RX_DDONE);
1782*9172SFei.Feng@Sun.COM 
1783*9172SFei.Feng@Sun.COM 		(void) ddi_dma_sync(sc->rxq.rxdesc_dma.dma_hdl,
1784*9172SFei.Feng@Sun.COM 		    sc->rxq.cur * sizeof (struct rt2860_rxd),
1785*9172SFei.Feng@Sun.COM 		    sizeof (struct rt2860_rxd),
1786*9172SFei.Feng@Sun.COM 		    DDI_DMA_SYNC_FORDEV);
1787*9172SFei.Feng@Sun.COM 
1788*9172SFei.Feng@Sun.COM 		sc->rxq.cur = (sc->rxq.cur + 1) % RT2860_RX_RING_COUNT;
1789*9172SFei.Feng@Sun.COM 	}
1790*9172SFei.Feng@Sun.COM 	mutex_exit(&sc->sc_rxlock);
1791*9172SFei.Feng@Sun.COM 
1792*9172SFei.Feng@Sun.COM 	/* tell HW what we have processed */
1793*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_RX_CALC_IDX,
1794*9172SFei.Feng@Sun.COM 	    (sc->rxq.cur - 1) % RT2860_RX_RING_COUNT);
1795*9172SFei.Feng@Sun.COM }
1796*9172SFei.Feng@Sun.COM 
1797*9172SFei.Feng@Sun.COM static uint_t
1798*9172SFei.Feng@Sun.COM rt2860_softintr(caddr_t data)
1799*9172SFei.Feng@Sun.COM {
1800*9172SFei.Feng@Sun.COM 	struct rt2860_softc *sc = (struct rt2860_softc *)data;
1801*9172SFei.Feng@Sun.COM 
1802*9172SFei.Feng@Sun.COM 	/*
1803*9172SFei.Feng@Sun.COM 	 * Check if the soft interrupt is triggered by another
1804*9172SFei.Feng@Sun.COM 	 * driver at the same level.
1805*9172SFei.Feng@Sun.COM 	 */
1806*9172SFei.Feng@Sun.COM 	RT2860_GLOCK(sc);
1807*9172SFei.Feng@Sun.COM 	if (sc->sc_rx_pend) {
1808*9172SFei.Feng@Sun.COM 		sc->sc_rx_pend = 0;
1809*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
1810*9172SFei.Feng@Sun.COM 		rt2860_rx_intr(sc);
1811*9172SFei.Feng@Sun.COM 		return (DDI_INTR_CLAIMED);
1812*9172SFei.Feng@Sun.COM 	}
1813*9172SFei.Feng@Sun.COM 	RT2860_GUNLOCK(sc);
1814*9172SFei.Feng@Sun.COM 
1815*9172SFei.Feng@Sun.COM 	return (DDI_INTR_UNCLAIMED);
1816*9172SFei.Feng@Sun.COM }
1817*9172SFei.Feng@Sun.COM 
1818*9172SFei.Feng@Sun.COM static uint_t
1819*9172SFei.Feng@Sun.COM rt2860_intr(caddr_t arg)
1820*9172SFei.Feng@Sun.COM {
1821*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc = (struct rt2860_softc *)arg;
1822*9172SFei.Feng@Sun.COM 	uint32_t		r;
1823*9172SFei.Feng@Sun.COM 
1824*9172SFei.Feng@Sun.COM 	RT2860_GLOCK(sc);
1825*9172SFei.Feng@Sun.COM 
1826*9172SFei.Feng@Sun.COM 	if ((!RT2860_IS_RUNNING(sc)) || RT2860_IS_SUSPEND(sc)) {
1827*9172SFei.Feng@Sun.COM 		/*
1828*9172SFei.Feng@Sun.COM 		 * The hardware is not ready/present, don't touch anything.
1829*9172SFei.Feng@Sun.COM 		 * Note this can happen early on if the IRQ is shared.
1830*9172SFei.Feng@Sun.COM 		 */
1831*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
1832*9172SFei.Feng@Sun.COM 		return (DDI_INTR_UNCLAIMED);
1833*9172SFei.Feng@Sun.COM 	}
1834*9172SFei.Feng@Sun.COM 
1835*9172SFei.Feng@Sun.COM 	r = RT2860_READ(sc, RT2860_INT_STATUS);
1836*9172SFei.Feng@Sun.COM 	if (r == 0xffffffff) {
1837*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
1838*9172SFei.Feng@Sun.COM 		return (DDI_INTR_UNCLAIMED);
1839*9172SFei.Feng@Sun.COM 	}
1840*9172SFei.Feng@Sun.COM 	if (r == 0) {
1841*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
1842*9172SFei.Feng@Sun.COM 		return (DDI_INTR_UNCLAIMED);
1843*9172SFei.Feng@Sun.COM 	}
1844*9172SFei.Feng@Sun.COM 
1845*9172SFei.Feng@Sun.COM 	/* acknowledge interrupts */
1846*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_INT_STATUS, r);
1847*9172SFei.Feng@Sun.COM 
1848*9172SFei.Feng@Sun.COM 	if (r & RT2860_TX_COHERENT)
1849*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr()"
1850*9172SFei.Feng@Sun.COM 		    "RT2860_TX_COHERENT\n");
1851*9172SFei.Feng@Sun.COM 
1852*9172SFei.Feng@Sun.COM 	if (r & RT2860_RX_COHERENT)
1853*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr()"
1854*9172SFei.Feng@Sun.COM 		    "RT2860_RX_COHERENT\n");
1855*9172SFei.Feng@Sun.COM 
1856*9172SFei.Feng@Sun.COM 	if (r & RT2860_MAC_INT_2) {
1857*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): "
1858*9172SFei.Feng@Sun.COM 		    "RT2860_MAC_INT_2\n");
1859*9172SFei.Feng@Sun.COM 		rt2860_drain_stats_fifo(sc);
1860*9172SFei.Feng@Sun.COM 	}
1861*9172SFei.Feng@Sun.COM 
1862*9172SFei.Feng@Sun.COM 	if (r & RT2860_TX_DONE_INT5) {
1863*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): "
1864*9172SFei.Feng@Sun.COM 		    "RT2860_TX_DONE_INT5\n");
1865*9172SFei.Feng@Sun.COM 		rt2860_tx_intr(sc, 5);
1866*9172SFei.Feng@Sun.COM 	}
1867*9172SFei.Feng@Sun.COM 
1868*9172SFei.Feng@Sun.COM 	if (r & RT2860_RX_DONE_INT) {
1869*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr()"
1870*9172SFei.Feng@Sun.COM 		    "RT2860_RX_INT\n");
1871*9172SFei.Feng@Sun.COM 		sc->sc_rx_pend = 1;
1872*9172SFei.Feng@Sun.COM 		ddi_trigger_softintr(sc->sc_softintr_hdl);
1873*9172SFei.Feng@Sun.COM 	}
1874*9172SFei.Feng@Sun.COM 
1875*9172SFei.Feng@Sun.COM 	if (r & RT2860_TX_DONE_INT4) {
1876*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): "
1877*9172SFei.Feng@Sun.COM 		    "RT2860_TX_DONE_INT4\n");
1878*9172SFei.Feng@Sun.COM 		rt2860_tx_intr(sc, 4);
1879*9172SFei.Feng@Sun.COM 	}
1880*9172SFei.Feng@Sun.COM 
1881*9172SFei.Feng@Sun.COM 	if (r & RT2860_TX_DONE_INT3) {
1882*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): "
1883*9172SFei.Feng@Sun.COM 		    "RT2860_TX_DONE_INT3\n");
1884*9172SFei.Feng@Sun.COM 		rt2860_tx_intr(sc, 3);
1885*9172SFei.Feng@Sun.COM 	}
1886*9172SFei.Feng@Sun.COM 
1887*9172SFei.Feng@Sun.COM 	if (r & RT2860_TX_DONE_INT2) {
1888*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): "
1889*9172SFei.Feng@Sun.COM 		    "RT2860_TX_DONE_INT2\n");
1890*9172SFei.Feng@Sun.COM 		rt2860_tx_intr(sc, 2);
1891*9172SFei.Feng@Sun.COM 	}
1892*9172SFei.Feng@Sun.COM 
1893*9172SFei.Feng@Sun.COM 	if (r & RT2860_TX_DONE_INT1) {
1894*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): "
1895*9172SFei.Feng@Sun.COM 		    "RT2860_TX_DONE_INT1\n");
1896*9172SFei.Feng@Sun.COM 		rt2860_tx_intr(sc, 1);
1897*9172SFei.Feng@Sun.COM 	}
1898*9172SFei.Feng@Sun.COM 
1899*9172SFei.Feng@Sun.COM 	if (r & RT2860_TX_DONE_INT0) {
1900*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): "
1901*9172SFei.Feng@Sun.COM 		    "RT2860_TX_DONE_INT0\n");
1902*9172SFei.Feng@Sun.COM 		rt2860_tx_intr(sc, 0);
1903*9172SFei.Feng@Sun.COM 	}
1904*9172SFei.Feng@Sun.COM 
1905*9172SFei.Feng@Sun.COM 	if (r & RT2860_MAC_INT_0) {
1906*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): "
1907*9172SFei.Feng@Sun.COM 		    "RT2860_MAC_INT_0\n");
1908*9172SFei.Feng@Sun.COM 		struct ieee80211com *ic = &sc->sc_ic;
1909*9172SFei.Feng@Sun.COM 		/* check if protection mode has changed */
1910*9172SFei.Feng@Sun.COM 		if ((sc->sc_ic_flags ^ ic->ic_flags) & IEEE80211_F_USEPROT) {
1911*9172SFei.Feng@Sun.COM 			rt2860_updateprot(ic);
1912*9172SFei.Feng@Sun.COM 			sc->sc_ic_flags = ic->ic_flags;
1913*9172SFei.Feng@Sun.COM 		}
1914*9172SFei.Feng@Sun.COM 	}
1915*9172SFei.Feng@Sun.COM 
1916*9172SFei.Feng@Sun.COM 	if (r & RT2860_MAC_INT_3)
1917*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): "
1918*9172SFei.Feng@Sun.COM 		    "RT2860_MAC_INT_3\n");
1919*9172SFei.Feng@Sun.COM 
1920*9172SFei.Feng@Sun.COM 	RT2860_GUNLOCK(sc);
1921*9172SFei.Feng@Sun.COM 
1922*9172SFei.Feng@Sun.COM 	return (DDI_INTR_CLAIMED);
1923*9172SFei.Feng@Sun.COM }
1924*9172SFei.Feng@Sun.COM 
1925*9172SFei.Feng@Sun.COM static void
1926*9172SFei.Feng@Sun.COM rt2860_set_region_4(struct rt2860_softc *sc,
1927*9172SFei.Feng@Sun.COM     uint32_t addr, uint32_t data, int size)
1928*9172SFei.Feng@Sun.COM {
1929*9172SFei.Feng@Sun.COM 	for (; size > 0; size--, data++, addr += 4)
1930*9172SFei.Feng@Sun.COM 		ddi_put32((sc)->sc_io_handle,
1931*9172SFei.Feng@Sun.COM 		    (uint32_t *)((uintptr_t)(sc)->sc_io_base + addr), data);
1932*9172SFei.Feng@Sun.COM }
1933*9172SFei.Feng@Sun.COM 
1934*9172SFei.Feng@Sun.COM static int
1935*9172SFei.Feng@Sun.COM rt2860_load_microcode(struct rt2860_softc *sc)
1936*9172SFei.Feng@Sun.COM {
1937*9172SFei.Feng@Sun.COM 	int		ntries;
1938*9172SFei.Feng@Sun.COM 	size_t		size;
1939*9172SFei.Feng@Sun.COM 	uint8_t		*ucode, *fptr;
1940*9172SFei.Feng@Sun.COM 	uint32_t	off, i;
1941*9172SFei.Feng@Sun.COM 
1942*9172SFei.Feng@Sun.COM 	ucode = rt2860_fw_bin;
1943*9172SFei.Feng@Sun.COM 	size = sizeof (rt2860_fw_bin);
1944*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_load_microcode(): "
1945*9172SFei.Feng@Sun.COM 	    "The size of ucode is: %x\n", size);
1946*9172SFei.Feng@Sun.COM 
1947*9172SFei.Feng@Sun.COM 	/* set "host program ram write selection" bit */
1948*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_SYS_CTRL, RT2860_HST_PM_SEL);
1949*9172SFei.Feng@Sun.COM 	/* write microcode image */
1950*9172SFei.Feng@Sun.COM 	fptr = ucode;
1951*9172SFei.Feng@Sun.COM 	off = RT2860_FW_BASE;
1952*9172SFei.Feng@Sun.COM 	for (i = 0; i < size; i++) {
1953*9172SFei.Feng@Sun.COM 		rt2860_mem_write1(sc, off++, *fptr++);
1954*9172SFei.Feng@Sun.COM 	}
1955*9172SFei.Feng@Sun.COM 	/* kick microcontroller unit */
1956*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_SYS_CTRL, 0);
1957*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_SYS_CTRL, RT2860_MCU_RESET);
1958*9172SFei.Feng@Sun.COM 
1959*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, 0);
1960*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_H2M_MAILBOX, 0);
1961*9172SFei.Feng@Sun.COM 
1962*9172SFei.Feng@Sun.COM 	/* wait until microcontroller is ready */
1963*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 1000; ntries++) {
1964*9172SFei.Feng@Sun.COM 		if (RT2860_READ(sc, RT2860_SYS_CTRL) & RT2860_MCU_READY)
1965*9172SFei.Feng@Sun.COM 			break;
1966*9172SFei.Feng@Sun.COM 		DELAY(1000);
1967*9172SFei.Feng@Sun.COM 	}
1968*9172SFei.Feng@Sun.COM 	if (ntries == 1000) {
1969*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_load_microcode(): "
1970*9172SFei.Feng@Sun.COM 		    "timeout waiting for MCU to initialie\n");
1971*9172SFei.Feng@Sun.COM 		return (ETIMEDOUT);
1972*9172SFei.Feng@Sun.COM 	}
1973*9172SFei.Feng@Sun.COM 
1974*9172SFei.Feng@Sun.COM 	return (0);
1975*9172SFei.Feng@Sun.COM }
1976*9172SFei.Feng@Sun.COM 
1977*9172SFei.Feng@Sun.COM static void
1978*9172SFei.Feng@Sun.COM rt2860_set_macaddr(struct rt2860_softc *sc, const uint8_t *addr)
1979*9172SFei.Feng@Sun.COM {
1980*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_ADDR_DW0,
1981*9172SFei.Feng@Sun.COM 	    addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
1982*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_ADDR_DW1,
1983*9172SFei.Feng@Sun.COM 	    addr[4] | addr[5] << 8);
1984*9172SFei.Feng@Sun.COM }
1985*9172SFei.Feng@Sun.COM 
1986*9172SFei.Feng@Sun.COM /*
1987*9172SFei.Feng@Sun.COM  * Send a command to the 8051 microcontroller unit.
1988*9172SFei.Feng@Sun.COM  */
1989*9172SFei.Feng@Sun.COM static int
1990*9172SFei.Feng@Sun.COM rt2860_mcu_cmd(struct rt2860_softc *sc, uint8_t cmd, uint16_t arg)
1991*9172SFei.Feng@Sun.COM {
1992*9172SFei.Feng@Sun.COM 	int	ntries;
1993*9172SFei.Feng@Sun.COM 
1994*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 100; ntries++) {
1995*9172SFei.Feng@Sun.COM 		if (!(RT2860_READ(sc, RT2860_H2M_MAILBOX) & RT2860_H2M_BUSY))
1996*9172SFei.Feng@Sun.COM 			break;
1997*9172SFei.Feng@Sun.COM 		DELAY(2);
1998*9172SFei.Feng@Sun.COM 	}
1999*9172SFei.Feng@Sun.COM 	if (ntries == 100)
2000*9172SFei.Feng@Sun.COM 		return (EIO);
2001*9172SFei.Feng@Sun.COM 
2002*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_H2M_MAILBOX,
2003*9172SFei.Feng@Sun.COM 	    RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg);
2004*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_HOST_CMD, cmd);
2005*9172SFei.Feng@Sun.COM 
2006*9172SFei.Feng@Sun.COM 	return (RT2860_SUCCESS);
2007*9172SFei.Feng@Sun.COM }
2008*9172SFei.Feng@Sun.COM 
2009*9172SFei.Feng@Sun.COM /*
2010*9172SFei.Feng@Sun.COM  * Reading and writing from/to the BBP is different from RT2560 and RT2661.
2011*9172SFei.Feng@Sun.COM  * We access the BBP through the 8051 microcontroller unit which means that
2012*9172SFei.Feng@Sun.COM  * the microcode must be loaded first.
2013*9172SFei.Feng@Sun.COM  */
2014*9172SFei.Feng@Sun.COM static uint8_t
2015*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_read(struct rt2860_softc *sc, uint8_t reg)
2016*9172SFei.Feng@Sun.COM {
2017*9172SFei.Feng@Sun.COM 	uint32_t val;
2018*9172SFei.Feng@Sun.COM 	int ntries;
2019*9172SFei.Feng@Sun.COM 
2020*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 100; ntries++) {
2021*9172SFei.Feng@Sun.COM 		if (!(RT2860_READ(sc,
2022*9172SFei.Feng@Sun.COM 		    RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK))
2023*9172SFei.Feng@Sun.COM 			break;
2024*9172SFei.Feng@Sun.COM 		DELAY(1);
2025*9172SFei.Feng@Sun.COM 	}
2026*9172SFei.Feng@Sun.COM 	if (ntries == 100) {
2027*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_mcu_bbp_read():"
2028*9172SFei.Feng@Sun.COM 		    "could not read from BBP through MCU\n");
2029*9172SFei.Feng@Sun.COM 		return (0);
2030*9172SFei.Feng@Sun.COM 	}
2031*9172SFei.Feng@Sun.COM 
2032*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL |
2033*9172SFei.Feng@Sun.COM 	    RT2860_BBP_CSR_KICK | RT2860_BBP_CSR_READ | reg << 8);
2034*9172SFei.Feng@Sun.COM 
2035*9172SFei.Feng@Sun.COM 	(void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0);
2036*9172SFei.Feng@Sun.COM 	DELAY(1000);
2037*9172SFei.Feng@Sun.COM 
2038*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 100; ntries++) {
2039*9172SFei.Feng@Sun.COM 		val = RT2860_READ(sc, RT2860_H2M_BBPAGENT);
2040*9172SFei.Feng@Sun.COM 		if (!(val & RT2860_BBP_CSR_KICK))
2041*9172SFei.Feng@Sun.COM 			return (val & 0xff);
2042*9172SFei.Feng@Sun.COM 		DELAY(1);
2043*9172SFei.Feng@Sun.COM 	}
2044*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_mcu_bbp_read():"
2045*9172SFei.Feng@Sun.COM 	    "could not read from BBP through MCU\n");
2046*9172SFei.Feng@Sun.COM 
2047*9172SFei.Feng@Sun.COM 	return (0);
2048*9172SFei.Feng@Sun.COM }
2049*9172SFei.Feng@Sun.COM 
2050*9172SFei.Feng@Sun.COM static void
2051*9172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val)
2052*9172SFei.Feng@Sun.COM {
2053*9172SFei.Feng@Sun.COM 	int ntries;
2054*9172SFei.Feng@Sun.COM 
2055*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 100; ntries++) {
2056*9172SFei.Feng@Sun.COM 		if (!(RT2860_READ(sc,
2057*9172SFei.Feng@Sun.COM 		    RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK))
2058*9172SFei.Feng@Sun.COM 			break;
2059*9172SFei.Feng@Sun.COM 		DELAY(1);
2060*9172SFei.Feng@Sun.COM 	}
2061*9172SFei.Feng@Sun.COM 	if (ntries == 100) {
2062*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_mcu_bbp_write():"
2063*9172SFei.Feng@Sun.COM 		    "could not write to BBP through MCU\n");
2064*9172SFei.Feng@Sun.COM 		return;
2065*9172SFei.Feng@Sun.COM 	}
2066*9172SFei.Feng@Sun.COM 
2067*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL |
2068*9172SFei.Feng@Sun.COM 	    RT2860_BBP_CSR_KICK | reg << 8 | val);
2069*9172SFei.Feng@Sun.COM 
2070*9172SFei.Feng@Sun.COM 	(void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0);
2071*9172SFei.Feng@Sun.COM 	DELAY(1000);
2072*9172SFei.Feng@Sun.COM }
2073*9172SFei.Feng@Sun.COM 
2074*9172SFei.Feng@Sun.COM static int
2075*9172SFei.Feng@Sun.COM rt2860_bbp_init(struct rt2860_softc *sc)
2076*9172SFei.Feng@Sun.COM {
2077*9172SFei.Feng@Sun.COM 	int i, ntries;
2078*9172SFei.Feng@Sun.COM 
2079*9172SFei.Feng@Sun.COM 	/* wait for BBP to wake up */
2080*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 20; ntries++) {
2081*9172SFei.Feng@Sun.COM 		uint8_t bbp0 = rt2860_mcu_bbp_read(sc, 0);
2082*9172SFei.Feng@Sun.COM 		if (bbp0 != 0 && bbp0 != 0xff)
2083*9172SFei.Feng@Sun.COM 			break;
2084*9172SFei.Feng@Sun.COM 	}
2085*9172SFei.Feng@Sun.COM 	if (ntries == 20) {
2086*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_bbp_init():"
2087*9172SFei.Feng@Sun.COM 		    "timeout waiting for BBP to wake up\n");
2088*9172SFei.Feng@Sun.COM 		return (ETIMEDOUT);
2089*9172SFei.Feng@Sun.COM 	}
2090*9172SFei.Feng@Sun.COM 
2091*9172SFei.Feng@Sun.COM 	/* initialize BBP registers to default values */
2092*9172SFei.Feng@Sun.COM 	for (i = 0; i < 12; i++) {
2093*9172SFei.Feng@Sun.COM 		rt2860_mcu_bbp_write(sc, rt2860_def_bbp[i].reg,
2094*9172SFei.Feng@Sun.COM 		    rt2860_def_bbp[i].val);
2095*9172SFei.Feng@Sun.COM 	}
2096*9172SFei.Feng@Sun.COM 
2097*9172SFei.Feng@Sun.COM 	/* fix BBP69 and BBP73 for RT2860C */
2098*9172SFei.Feng@Sun.COM 	if (sc->mac_rev == 0x28600100) {
2099*9172SFei.Feng@Sun.COM 		rt2860_mcu_bbp_write(sc, 69, 0x16);
2100*9172SFei.Feng@Sun.COM 		rt2860_mcu_bbp_write(sc, 73, 0x12);
2101*9172SFei.Feng@Sun.COM 	}
2102*9172SFei.Feng@Sun.COM 
2103*9172SFei.Feng@Sun.COM 	return (0);
2104*9172SFei.Feng@Sun.COM }
2105*9172SFei.Feng@Sun.COM 
2106*9172SFei.Feng@Sun.COM static void
2107*9172SFei.Feng@Sun.COM rt2860_rf_write(struct rt2860_softc *sc, uint8_t reg, uint32_t val)
2108*9172SFei.Feng@Sun.COM {
2109*9172SFei.Feng@Sun.COM 	uint32_t tmp;
2110*9172SFei.Feng@Sun.COM 	int ntries;
2111*9172SFei.Feng@Sun.COM 
2112*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 100; ntries++) {
2113*9172SFei.Feng@Sun.COM 		if (!(RT2860_READ(sc, RT2860_RF_CSR_CFG0) & RT2860_RF_REG_CTRL))
2114*9172SFei.Feng@Sun.COM 			break;
2115*9172SFei.Feng@Sun.COM 		DELAY(1);
2116*9172SFei.Feng@Sun.COM 	}
2117*9172SFei.Feng@Sun.COM 	if (ntries == 100) {
2118*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_FW, "rwn: rwn_init()"
2119*9172SFei.Feng@Sun.COM 		    "could not write to RF\n");
2120*9172SFei.Feng@Sun.COM 		return;
2121*9172SFei.Feng@Sun.COM 	}
2122*9172SFei.Feng@Sun.COM 
2123*9172SFei.Feng@Sun.COM 	/* RF registers are 24-bit on the RT2860 */
2124*9172SFei.Feng@Sun.COM 	tmp = RT2860_RF_REG_CTRL | 24 << RT2860_RF_REG_WIDTH_SHIFT |
2125*9172SFei.Feng@Sun.COM 	    (val & 0x3fffff) << 2 | (reg & 3);
2126*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_RF_CSR_CFG0, tmp);
2127*9172SFei.Feng@Sun.COM }
2128*9172SFei.Feng@Sun.COM 
2129*9172SFei.Feng@Sun.COM static void
2130*9172SFei.Feng@Sun.COM rt2860_select_chan_group(struct rt2860_softc *sc, int group)
2131*9172SFei.Feng@Sun.COM {
2132*9172SFei.Feng@Sun.COM 	uint32_t tmp;
2133*9172SFei.Feng@Sun.COM 
2134*9172SFei.Feng@Sun.COM 	rt2860_mcu_bbp_write(sc, 62, 0x37 - sc->lna[group]);
2135*9172SFei.Feng@Sun.COM 	rt2860_mcu_bbp_write(sc, 63, 0x37 - sc->lna[group]);
2136*9172SFei.Feng@Sun.COM 	rt2860_mcu_bbp_write(sc, 64, 0x37 - sc->lna[group]);
2137*9172SFei.Feng@Sun.COM 	rt2860_mcu_bbp_write(sc, 82, (group == 0) ? 0x62 : 0xf2);
2138*9172SFei.Feng@Sun.COM 
2139*9172SFei.Feng@Sun.COM 	tmp = RT2860_READ(sc, RT2860_TX_BAND_CFG);
2140*9172SFei.Feng@Sun.COM 	tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P);
2141*9172SFei.Feng@Sun.COM 	tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P;
2142*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_TX_BAND_CFG, tmp);
2143*9172SFei.Feng@Sun.COM 
2144*9172SFei.Feng@Sun.COM 	/* enable appropriate Power Amplifiers and Low Noise Amplifiers */
2145*9172SFei.Feng@Sun.COM 	tmp = RT2860_RFTR_EN | RT2860_TRSW_EN;
2146*9172SFei.Feng@Sun.COM 	if (group == 0) {	/* 2GHz */
2147*9172SFei.Feng@Sun.COM 		tmp |= RT2860_PA_PE_G0_EN | RT2860_LNA_PE_G0_EN;
2148*9172SFei.Feng@Sun.COM 		if (sc->ntxchains > 1)
2149*9172SFei.Feng@Sun.COM 			tmp |= RT2860_PA_PE_G1_EN;
2150*9172SFei.Feng@Sun.COM 		if (sc->nrxchains > 1)
2151*9172SFei.Feng@Sun.COM 			tmp |= RT2860_LNA_PE_G1_EN;
2152*9172SFei.Feng@Sun.COM 	} else {		/* 5GHz */
2153*9172SFei.Feng@Sun.COM 		tmp |= RT2860_PA_PE_A0_EN | RT2860_LNA_PE_A0_EN;
2154*9172SFei.Feng@Sun.COM 		if (sc->ntxchains > 1)
2155*9172SFei.Feng@Sun.COM 			tmp |= RT2860_PA_PE_A1_EN;
2156*9172SFei.Feng@Sun.COM 		if (sc->nrxchains > 1)
2157*9172SFei.Feng@Sun.COM 			tmp |= RT2860_LNA_PE_A1_EN;
2158*9172SFei.Feng@Sun.COM 	}
2159*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_TX_PIN_CFG, tmp);
2160*9172SFei.Feng@Sun.COM 
2161*9172SFei.Feng@Sun.COM 	rt2860_mcu_bbp_write(sc, 66, 0x2e + sc->lna[group]);
2162*9172SFei.Feng@Sun.COM }
2163*9172SFei.Feng@Sun.COM static void
2164*9172SFei.Feng@Sun.COM rt2860_set_chan(struct rt2860_softc *sc, struct ieee80211_channel *c)
2165*9172SFei.Feng@Sun.COM {
2166*9172SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
2167*9172SFei.Feng@Sun.COM 	const struct rfprog	*rfprog = rt2860_rf2850;
2168*9172SFei.Feng@Sun.COM 	uint_t			i, chan, group;
2169*9172SFei.Feng@Sun.COM 	uint8_t			txpow1, txpow2;
2170*9172SFei.Feng@Sun.COM 	uint32_t		r2, r3, r4;
2171*9172SFei.Feng@Sun.COM 
2172*9172SFei.Feng@Sun.COM 	chan = ieee80211_chan2ieee(ic, c);
2173*9172SFei.Feng@Sun.COM 	if (chan == 0 || chan == IEEE80211_CHAN_ANY) {
2174*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_FW, "Unkonwn channel!\n");
2175*9172SFei.Feng@Sun.COM 		return;
2176*9172SFei.Feng@Sun.COM 	}
2177*9172SFei.Feng@Sun.COM 
2178*9172SFei.Feng@Sun.COM 	/* find the settings for this channel (we know it exists) */
2179*9172SFei.Feng@Sun.COM 	for (i = 0; rfprog[i].chan != chan; )
2180*9172SFei.Feng@Sun.COM 		i++;
2181*9172SFei.Feng@Sun.COM 
2182*9172SFei.Feng@Sun.COM 	r2 = rfprog[i].r2;
2183*9172SFei.Feng@Sun.COM 	if (sc->ntxchains == 1)
2184*9172SFei.Feng@Sun.COM 		r2 |= 1 << 12;		/* 1T: disable Tx chain 2 */
2185*9172SFei.Feng@Sun.COM 	if (sc->nrxchains == 1)
2186*9172SFei.Feng@Sun.COM 		r2 |= 1 << 15 | 1 << 4;	/* 1R: disable Rx chains 2 & 3 */
2187*9172SFei.Feng@Sun.COM 	else if (sc->nrxchains == 2)
2188*9172SFei.Feng@Sun.COM 		r2 |= 1 << 4;		/* 2R: disable Rx chain 3 */
2189*9172SFei.Feng@Sun.COM 
2190*9172SFei.Feng@Sun.COM 	/* use Tx power values from EEPROM */
2191*9172SFei.Feng@Sun.COM 	txpow1 = sc->txpow1[i];
2192*9172SFei.Feng@Sun.COM 	txpow2 = sc->txpow2[i];
2193*9172SFei.Feng@Sun.COM 	if (IEEE80211_IS_CHAN_5GHZ(c)) {
2194*9172SFei.Feng@Sun.COM 		txpow1 = txpow1 << 1 | 1;
2195*9172SFei.Feng@Sun.COM 		txpow2 = txpow2 << 1 | 1;
2196*9172SFei.Feng@Sun.COM 	}
2197*9172SFei.Feng@Sun.COM 	r3 = rfprog[i].r3 | txpow1 << 7;
2198*9172SFei.Feng@Sun.COM 	r4 = rfprog[i].r4 | sc->freq << 13 | txpow2 << 4;
2199*9172SFei.Feng@Sun.COM 
2200*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1);
2201*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF2, r2);
2202*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF3, r3);
2203*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF4, r4);
2204*9172SFei.Feng@Sun.COM 
2205*9172SFei.Feng@Sun.COM 	DELAY(200);
2206*9172SFei.Feng@Sun.COM 
2207*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1);
2208*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF2, r2);
2209*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF3, r3 | 1);
2210*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF4, r4);
2211*9172SFei.Feng@Sun.COM 
2212*9172SFei.Feng@Sun.COM 	DELAY(200);
2213*9172SFei.Feng@Sun.COM 
2214*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1);
2215*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF2, r2);
2216*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF3, r3);
2217*9172SFei.Feng@Sun.COM 	rt2860_rf_write(sc, RAL_RF4, r4);
2218*9172SFei.Feng@Sun.COM 
2219*9172SFei.Feng@Sun.COM 	/* 802.11a uses a 16 microseconds short interframe space */
2220*9172SFei.Feng@Sun.COM 	sc->sifs = IEEE80211_IS_CHAN_5GHZ(c) ? 16 : 10;
2221*9172SFei.Feng@Sun.COM 
2222*9172SFei.Feng@Sun.COM 	/* determine channel group */
2223*9172SFei.Feng@Sun.COM 	if (chan <= 14)
2224*9172SFei.Feng@Sun.COM 		group = 0;
2225*9172SFei.Feng@Sun.COM 	else if (chan <= 64)
2226*9172SFei.Feng@Sun.COM 		group = 1;
2227*9172SFei.Feng@Sun.COM 	else if (chan <= 128)
2228*9172SFei.Feng@Sun.COM 		group = 2;
2229*9172SFei.Feng@Sun.COM 	else
2230*9172SFei.Feng@Sun.COM 		group = 3;
2231*9172SFei.Feng@Sun.COM 
2232*9172SFei.Feng@Sun.COM 	/* XXX necessary only when group has changed! */
2233*9172SFei.Feng@Sun.COM 	rt2860_select_chan_group(sc, group);
2234*9172SFei.Feng@Sun.COM 
2235*9172SFei.Feng@Sun.COM 	DELAY(1000);
2236*9172SFei.Feng@Sun.COM }
2237*9172SFei.Feng@Sun.COM 
2238*9172SFei.Feng@Sun.COM static void
2239*9172SFei.Feng@Sun.COM rt2860_updateprot(struct ieee80211com *ic)
2240*9172SFei.Feng@Sun.COM {
2241*9172SFei.Feng@Sun.COM 	struct rt2860_softc *sc = (struct rt2860_softc *)ic;
2242*9172SFei.Feng@Sun.COM 	uint32_t tmp;
2243*9172SFei.Feng@Sun.COM 
2244*9172SFei.Feng@Sun.COM 	tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL;
2245*9172SFei.Feng@Sun.COM 	/* setup protection frame rate (MCS code) */
2246*9172SFei.Feng@Sun.COM 	tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ? 0 : 3;
2247*9172SFei.Feng@Sun.COM 
2248*9172SFei.Feng@Sun.COM 	/* CCK frames don't require protection */
2249*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_CCK_PROT_CFG, tmp);
2250*9172SFei.Feng@Sun.COM 
2251*9172SFei.Feng@Sun.COM 	if (ic->ic_flags & IEEE80211_F_USEPROT) {
2252*9172SFei.Feng@Sun.COM 		if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
2253*9172SFei.Feng@Sun.COM 			tmp |= RT2860_PROT_CTRL_RTS_CTS;
2254*9172SFei.Feng@Sun.COM 		else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
2255*9172SFei.Feng@Sun.COM 			tmp |= RT2860_PROT_CTRL_CTS;
2256*9172SFei.Feng@Sun.COM 	}
2257*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_OFDM_PROT_CFG, tmp);
2258*9172SFei.Feng@Sun.COM }
2259*9172SFei.Feng@Sun.COM 
2260*9172SFei.Feng@Sun.COM static void
2261*9172SFei.Feng@Sun.COM rt2860_set_leds(struct rt2860_softc *sc, uint16_t which)
2262*9172SFei.Feng@Sun.COM {
2263*9172SFei.Feng@Sun.COM 	(void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LEDS,
2264*9172SFei.Feng@Sun.COM 	    which | (sc->leds & 0x7f));
2265*9172SFei.Feng@Sun.COM }
2266*9172SFei.Feng@Sun.COM 
2267*9172SFei.Feng@Sun.COM static int
2268*9172SFei.Feng@Sun.COM rt2860_init(struct rt2860_softc *sc)
2269*9172SFei.Feng@Sun.COM {
2270*9172SFei.Feng@Sun.COM #define	N(a)	(sizeof (a) / sizeof ((a)[0]))
2271*9172SFei.Feng@Sun.COM 	struct ieee80211com	*ic;
2272*9172SFei.Feng@Sun.COM 	int			i, err, qid, ridx, ntries;
2273*9172SFei.Feng@Sun.COM 	uint8_t			bbp1, bbp3;
2274*9172SFei.Feng@Sun.COM 	uint32_t		tmp;
2275*9172SFei.Feng@Sun.COM 
2276*9172SFei.Feng@Sun.COM 	ic = &sc->sc_ic;
2277*9172SFei.Feng@Sun.COM 
2278*9172SFei.Feng@Sun.COM 	rt2860_stop(sc);
2279*9172SFei.Feng@Sun.COM 	tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG);
2280*9172SFei.Feng@Sun.COM 	tmp &= 0xff0;
2281*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE);
2282*9172SFei.Feng@Sun.COM 
2283*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_WPDMA_RST_IDX, 0xffffffff);
2284*9172SFei.Feng@Sun.COM 
2285*9172SFei.Feng@Sun.COM 	/* PBF hardware reset */
2286*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe1f);
2287*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe00);
2288*9172SFei.Feng@Sun.COM 
2289*9172SFei.Feng@Sun.COM 	if (!(sc->sc_flags & RT2860_FWLOADED)) {
2290*9172SFei.Feng@Sun.COM 		if ((err = rt2860_load_microcode(sc)) != 0) {
2291*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_init(): "
2292*9172SFei.Feng@Sun.COM 			    "could not load 8051 microcode\n");
2293*9172SFei.Feng@Sun.COM 			rt2860_stop(sc);
2294*9172SFei.Feng@Sun.COM 			return (err);
2295*9172SFei.Feng@Sun.COM 		}
2296*9172SFei.Feng@Sun.COM 		RT2860_GLOCK(sc);
2297*9172SFei.Feng@Sun.COM 		sc->sc_flags |= RT2860_FWLOADED;
2298*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
2299*9172SFei.Feng@Sun.COM 	}
2300*9172SFei.Feng@Sun.COM 
2301*9172SFei.Feng@Sun.COM 	rt2860_set_macaddr(sc, ic->ic_macaddr);
2302*9172SFei.Feng@Sun.COM 
2303*9172SFei.Feng@Sun.COM 	/* init Tx power for all Tx rates (from EEPROM) */
2304*9172SFei.Feng@Sun.COM 	for (ridx = 0; ridx < 5; ridx++) {
2305*9172SFei.Feng@Sun.COM 		if (sc->txpow20mhz[ridx] == 0xffffffff)
2306*9172SFei.Feng@Sun.COM 			continue;
2307*9172SFei.Feng@Sun.COM 		RT2860_WRITE(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]);
2308*9172SFei.Feng@Sun.COM 	}
2309*9172SFei.Feng@Sun.COM 
2310*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 100; ntries++) {
2311*9172SFei.Feng@Sun.COM 		tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG);
2312*9172SFei.Feng@Sun.COM 		if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0)
2313*9172SFei.Feng@Sun.COM 			break;
2314*9172SFei.Feng@Sun.COM 		DELAY(1000);
2315*9172SFei.Feng@Sun.COM 	}
2316*9172SFei.Feng@Sun.COM 	if (ntries == 100) {
2317*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_init():"
2318*9172SFei.Feng@Sun.COM 		    "timeout waiting for DMA engine\n");
2319*9172SFei.Feng@Sun.COM 		rt2860_stop(sc);
2320*9172SFei.Feng@Sun.COM 		return (ETIMEDOUT);
2321*9172SFei.Feng@Sun.COM 	}
2322*9172SFei.Feng@Sun.COM 	tmp &= 0xff0;
2323*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE);
2324*9172SFei.Feng@Sun.COM 
2325*9172SFei.Feng@Sun.COM 	/* reset Rx ring and all 6 Tx rings */
2326*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_WPDMA_RST_IDX, 0x1003f);
2327*9172SFei.Feng@Sun.COM 
2328*9172SFei.Feng@Sun.COM 	/* PBF hardware reset */
2329*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe1f);
2330*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe00);
2331*9172SFei.Feng@Sun.COM 
2332*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL,
2333*9172SFei.Feng@Sun.COM 	    RT2860_BBP_HRST | RT2860_MAC_SRST);
2334*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 0);
2335*9172SFei.Feng@Sun.COM 
2336*9172SFei.Feng@Sun.COM 	for (i = 0; i < N(rt2860_def_mac); i++)
2337*9172SFei.Feng@Sun.COM 		RT2860_WRITE(sc, rt2860_def_mac[i].reg, rt2860_def_mac[i].val);
2338*9172SFei.Feng@Sun.COM 
2339*9172SFei.Feng@Sun.COM 	/* wait while MAC is busy */
2340*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 100; ntries++) {
2341*9172SFei.Feng@Sun.COM 		if (!(RT2860_READ(sc, RT2860_MAC_STATUS_REG) &
2342*9172SFei.Feng@Sun.COM 		    (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY)))
2343*9172SFei.Feng@Sun.COM 			break;
2344*9172SFei.Feng@Sun.COM 		DELAY(1000);
2345*9172SFei.Feng@Sun.COM 	}
2346*9172SFei.Feng@Sun.COM 	if (ntries == 100) {
2347*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_init():"
2348*9172SFei.Feng@Sun.COM 		    "timeout waiting for MAC\n");
2349*9172SFei.Feng@Sun.COM 		rt2860_stop(sc);
2350*9172SFei.Feng@Sun.COM 		return (ETIMEDOUT);
2351*9172SFei.Feng@Sun.COM 	}
2352*9172SFei.Feng@Sun.COM 
2353*9172SFei.Feng@Sun.COM 	/* clear Host to MCU mailbox */
2354*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, 0);
2355*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_H2M_MAILBOX, 0);
2356*9172SFei.Feng@Sun.COM 
2357*9172SFei.Feng@Sun.COM 	if ((err = rt2860_bbp_init(sc)) != 0) {
2358*9172SFei.Feng@Sun.COM 		rt2860_stop(sc);
2359*9172SFei.Feng@Sun.COM 		return (err);
2360*9172SFei.Feng@Sun.COM 	}
2361*9172SFei.Feng@Sun.COM 
2362*9172SFei.Feng@Sun.COM 	/* init Tx rings (4 EDCAs + HCCA + Mgt) */
2363*9172SFei.Feng@Sun.COM 	for (qid = 0; qid < 6; qid++) {
2364*9172SFei.Feng@Sun.COM 		RT2860_WRITE(sc, RT2860_TX_BASE_PTR(qid), sc->txq[qid].paddr);
2365*9172SFei.Feng@Sun.COM 		RT2860_WRITE(sc, RT2860_TX_MAX_CNT(qid), RT2860_TX_RING_COUNT);
2366*9172SFei.Feng@Sun.COM 		RT2860_WRITE(sc, RT2860_TX_CTX_IDX(qid), 0);
2367*9172SFei.Feng@Sun.COM 	}
2368*9172SFei.Feng@Sun.COM 
2369*9172SFei.Feng@Sun.COM 	/* init Rx ring */
2370*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_RX_BASE_PTR, sc->rxq.paddr);
2371*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_RX_MAX_CNT, RT2860_RX_RING_COUNT);
2372*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_RX_CALC_IDX, RT2860_RX_RING_COUNT - 1);
2373*9172SFei.Feng@Sun.COM 
2374*9172SFei.Feng@Sun.COM 	/* setup maximum buffer sizes */
2375*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAX_LEN_CFG, 1 << 12 |
2376*9172SFei.Feng@Sun.COM 	    (sc->sc_dmabuf_size - sizeof (struct rt2860_rxwi) - 2));
2377*9172SFei.Feng@Sun.COM 
2378*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 100; ntries++) {
2379*9172SFei.Feng@Sun.COM 		tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG);
2380*9172SFei.Feng@Sun.COM 		if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0)
2381*9172SFei.Feng@Sun.COM 			break;
2382*9172SFei.Feng@Sun.COM 		DELAY(1000);
2383*9172SFei.Feng@Sun.COM 	}
2384*9172SFei.Feng@Sun.COM 	if (ntries == 100) {
2385*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_init():"
2386*9172SFei.Feng@Sun.COM 		    "timeout waiting for DMA engine\n");
2387*9172SFei.Feng@Sun.COM 		rt2860_stop(sc);
2388*9172SFei.Feng@Sun.COM 		return (ETIMEDOUT);
2389*9172SFei.Feng@Sun.COM 	}
2390*9172SFei.Feng@Sun.COM 	tmp &= 0xff0;
2391*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE);
2392*9172SFei.Feng@Sun.COM 
2393*9172SFei.Feng@Sun.COM 	/* disable interrupts mitigation */
2394*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_DELAY_INT_CFG, 0);
2395*9172SFei.Feng@Sun.COM 
2396*9172SFei.Feng@Sun.COM 	/* write vendor-specific BBP values (from EEPROM) */
2397*9172SFei.Feng@Sun.COM 	for (i = 0; i < 8; i++) {
2398*9172SFei.Feng@Sun.COM 		if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff)
2399*9172SFei.Feng@Sun.COM 			continue;
2400*9172SFei.Feng@Sun.COM 		rt2860_mcu_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val);
2401*9172SFei.Feng@Sun.COM 	}
2402*9172SFei.Feng@Sun.COM 
2403*9172SFei.Feng@Sun.COM 	/* send LEDs operating mode to microcontroller */
2404*9172SFei.Feng@Sun.COM 	(void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]);
2405*9172SFei.Feng@Sun.COM 	(void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]);
2406*9172SFei.Feng@Sun.COM 	(void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]);
2407*9172SFei.Feng@Sun.COM 
2408*9172SFei.Feng@Sun.COM 	/* disable non-existing Rx chains */
2409*9172SFei.Feng@Sun.COM 	bbp3 = rt2860_mcu_bbp_read(sc, 3);
2410*9172SFei.Feng@Sun.COM 	bbp3 &= ~(1 << 3 | 1 << 4);
2411*9172SFei.Feng@Sun.COM 	if (sc->nrxchains == 2)
2412*9172SFei.Feng@Sun.COM 		bbp3 |= 1 << 3;
2413*9172SFei.Feng@Sun.COM 	else if (sc->nrxchains == 3)
2414*9172SFei.Feng@Sun.COM 		bbp3 |= 1 << 4;
2415*9172SFei.Feng@Sun.COM 	rt2860_mcu_bbp_write(sc, 3, bbp3);
2416*9172SFei.Feng@Sun.COM 
2417*9172SFei.Feng@Sun.COM 	/* disable non-existing Tx chains */
2418*9172SFei.Feng@Sun.COM 	bbp1 = rt2860_mcu_bbp_read(sc, 1);
2419*9172SFei.Feng@Sun.COM 	if (sc->ntxchains == 1)
2420*9172SFei.Feng@Sun.COM 		bbp1 &= ~(1 << 3 | 1 << 4);
2421*9172SFei.Feng@Sun.COM 	rt2860_mcu_bbp_write(sc, 1, bbp1);
2422*9172SFei.Feng@Sun.COM 
2423*9172SFei.Feng@Sun.COM 	/* select default channel */
2424*9172SFei.Feng@Sun.COM 	rt2860_set_chan(sc, ic->ic_curchan);
2425*9172SFei.Feng@Sun.COM 
2426*9172SFei.Feng@Sun.COM 	/* XXX not clear what the following 8051 command does.. */
2427*9172SFei.Feng@Sun.COM 	(void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BOOT, 0);
2428*9172SFei.Feng@Sun.COM 
2429*9172SFei.Feng@Sun.COM 	/* set RTS threshold */
2430*9172SFei.Feng@Sun.COM 	tmp = RT2860_READ(sc, RT2860_TX_RTS_CFG);
2431*9172SFei.Feng@Sun.COM 	tmp &= ~0xffff00;
2432*9172SFei.Feng@Sun.COM 	tmp |= ic->ic_rtsthreshold << 8;
2433*9172SFei.Feng@Sun.COM 
2434*9172SFei.Feng@Sun.COM 	/* setup initial protection mode */
2435*9172SFei.Feng@Sun.COM 	sc->sc_ic_flags = ic->ic_flags;
2436*9172SFei.Feng@Sun.COM 	rt2860_updateprot(ic);
2437*9172SFei.Feng@Sun.COM 
2438*9172SFei.Feng@Sun.COM 	/* enable Tx/Rx DMA engine */
2439*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN);
2440*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 200; ntries++) {
2441*9172SFei.Feng@Sun.COM 		tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG);
2442*9172SFei.Feng@Sun.COM 		if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0)
2443*9172SFei.Feng@Sun.COM 			break;
2444*9172SFei.Feng@Sun.COM 		DELAY(1000);
2445*9172SFei.Feng@Sun.COM 	}
2446*9172SFei.Feng@Sun.COM 	if (ntries == 200) {
2447*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_int():"
2448*9172SFei.Feng@Sun.COM 		    "timeout waiting for DMA engine\n");
2449*9172SFei.Feng@Sun.COM 		rt2860_stop(sc);
2450*9172SFei.Feng@Sun.COM 		return (ETIMEDOUT);
2451*9172SFei.Feng@Sun.COM 	}
2452*9172SFei.Feng@Sun.COM 
2453*9172SFei.Feng@Sun.COM 	DELAY(50);
2454*9172SFei.Feng@Sun.COM 
2455*9172SFei.Feng@Sun.COM 	tmp |= RT2860_TX_WB_DDONE | RT2860_RX_DMA_EN | RT2860_TX_DMA_EN |
2456*9172SFei.Feng@Sun.COM 	    RT2860_WPDMA_BT_SIZE64 << RT2860_WPDMA_BT_SIZE_SHIFT;
2457*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp);
2458*9172SFei.Feng@Sun.COM 
2459*9172SFei.Feng@Sun.COM 	/* turn radio LED on */
2460*9172SFei.Feng@Sun.COM 	rt2860_set_leds(sc, RT2860_LED_RADIO);
2461*9172SFei.Feng@Sun.COM 
2462*9172SFei.Feng@Sun.COM 	/* set Rx filter */
2463*9172SFei.Feng@Sun.COM 	tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR;
2464*9172SFei.Feng@Sun.COM 	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
2465*9172SFei.Feng@Sun.COM 		tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL |
2466*9172SFei.Feng@Sun.COM 		    RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK |
2467*9172SFei.Feng@Sun.COM 		    RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV |
2468*9172SFei.Feng@Sun.COM 		    RT2860_DROP_CFACK | RT2860_DROP_CFEND;
2469*9172SFei.Feng@Sun.COM 		if (ic->ic_opmode == IEEE80211_M_STA)
2470*9172SFei.Feng@Sun.COM 			tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL;
2471*9172SFei.Feng@Sun.COM 	}
2472*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_RX_FILTR_CFG, tmp);
2473*9172SFei.Feng@Sun.COM 
2474*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL,
2475*9172SFei.Feng@Sun.COM 	    RT2860_MAC_RX_EN | RT2860_MAC_TX_EN);
2476*9172SFei.Feng@Sun.COM 
2477*9172SFei.Feng@Sun.COM 	/* clear pending interrupts */
2478*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_INT_STATUS, 0xffffffff);
2479*9172SFei.Feng@Sun.COM 	/* enable interrupts */
2480*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_INT_MASK, 0x3fffc);
2481*9172SFei.Feng@Sun.COM 
2482*9172SFei.Feng@Sun.COM 	if (sc->sc_flags & RT2860_ADVANCED_PS)
2483*9172SFei.Feng@Sun.COM 		(void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_PSLEVEL, sc->pslevel);
2484*9172SFei.Feng@Sun.COM 
2485*9172SFei.Feng@Sun.COM 	return (DDI_SUCCESS);
2486*9172SFei.Feng@Sun.COM }
2487*9172SFei.Feng@Sun.COM 
2488*9172SFei.Feng@Sun.COM static int
2489*9172SFei.Feng@Sun.COM rt2860_quiesce(dev_info_t *dip)
2490*9172SFei.Feng@Sun.COM {
2491*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc;
2492*9172SFei.Feng@Sun.COM 
2493*9172SFei.Feng@Sun.COM 	sc = ddi_get_soft_state(rt2860_soft_state_p, ddi_get_instance(dip));
2494*9172SFei.Feng@Sun.COM 	if (sc == NULL)
2495*9172SFei.Feng@Sun.COM 		return (DDI_FAILURE);
2496*9172SFei.Feng@Sun.COM 
2497*9172SFei.Feng@Sun.COM #ifdef DEBUG
2498*9172SFei.Feng@Sun.COM 	rt2860_dbg_flags = 0;
2499*9172SFei.Feng@Sun.COM #endif
2500*9172SFei.Feng@Sun.COM 
2501*9172SFei.Feng@Sun.COM 	/*
2502*9172SFei.Feng@Sun.COM 	 * No more blocking is allowed while we are in quiesce(9E) entry point
2503*9172SFei.Feng@Sun.COM 	 */
2504*9172SFei.Feng@Sun.COM 	sc->sc_flags |= RT2860_F_QUIESCE;
2505*9172SFei.Feng@Sun.COM 
2506*9172SFei.Feng@Sun.COM 	/*
2507*9172SFei.Feng@Sun.COM 	 * Disable and mask all interrupts
2508*9172SFei.Feng@Sun.COM 	 */
2509*9172SFei.Feng@Sun.COM 	rt2860_stop(sc);
2510*9172SFei.Feng@Sun.COM 	return (DDI_SUCCESS);
2511*9172SFei.Feng@Sun.COM }
2512*9172SFei.Feng@Sun.COM 
2513*9172SFei.Feng@Sun.COM static void
2514*9172SFei.Feng@Sun.COM rt2860_stop(struct rt2860_softc *sc)
2515*9172SFei.Feng@Sun.COM {
2516*9172SFei.Feng@Sun.COM 	int		qid;
2517*9172SFei.Feng@Sun.COM 	uint32_t	tmp;
2518*9172SFei.Feng@Sun.COM 
2519*9172SFei.Feng@Sun.COM 	/* by pass if it's quiesced */
2520*9172SFei.Feng@Sun.COM 	if (!(sc->sc_flags & RT2860_F_QUIESCE))
2521*9172SFei.Feng@Sun.COM 		RT2860_GLOCK(sc);
2522*9172SFei.Feng@Sun.COM 	if (sc->sc_flags == RT2860_F_RUNNING)
2523*9172SFei.Feng@Sun.COM 		rt2860_set_leds(sc, 0);	/* turn all LEDs off */
2524*9172SFei.Feng@Sun.COM 	sc->sc_tx_timer = 0;
2525*9172SFei.Feng@Sun.COM 	/* by pass if it's quiesced */
2526*9172SFei.Feng@Sun.COM 	if (!(sc->sc_flags & RT2860_F_QUIESCE))
2527*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
2528*9172SFei.Feng@Sun.COM 
2529*9172SFei.Feng@Sun.COM 	/* clear RX WCID search table */
2530*9172SFei.Feng@Sun.COM 	rt2860_set_region_4(sc, RT2860_WCID_ENTRY(0), 0, 512);
2531*9172SFei.Feng@Sun.COM 	/* clear pairwise key table */
2532*9172SFei.Feng@Sun.COM 	rt2860_set_region_4(sc, RT2860_PKEY(0), 0, 2048);
2533*9172SFei.Feng@Sun.COM 	/* clear IV/EIV table */
2534*9172SFei.Feng@Sun.COM 	rt2860_set_region_4(sc, RT2860_IVEIV(0), 0, 512);
2535*9172SFei.Feng@Sun.COM 	/* clear WCID attribute table */
2536*9172SFei.Feng@Sun.COM 	rt2860_set_region_4(sc, RT2860_WCID_ATTR(0), 0, 256);
2537*9172SFei.Feng@Sun.COM 	/* clear shared key table */
2538*9172SFei.Feng@Sun.COM 	rt2860_set_region_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32);
2539*9172SFei.Feng@Sun.COM 	/* clear shared key mode */
2540*9172SFei.Feng@Sun.COM 	rt2860_set_region_4(sc, RT2860_SKEY_MODE_0_7, 0, 4);
2541*9172SFei.Feng@Sun.COM 
2542*9172SFei.Feng@Sun.COM 	/* disable interrupts */
2543*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_INT_MASK, 0);
2544*9172SFei.Feng@Sun.COM 
2545*9172SFei.Feng@Sun.COM 	/* disable Rx */
2546*9172SFei.Feng@Sun.COM 	tmp = RT2860_READ(sc, RT2860_MAC_SYS_CTRL);
2547*9172SFei.Feng@Sun.COM 	tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN);
2548*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, tmp);
2549*9172SFei.Feng@Sun.COM 
2550*9172SFei.Feng@Sun.COM 	/* reset adapter */
2551*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL,
2552*9172SFei.Feng@Sun.COM 	    RT2860_BBP_HRST | RT2860_MAC_SRST);
2553*9172SFei.Feng@Sun.COM 	RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 0);
2554*9172SFei.Feng@Sun.COM 
2555*9172SFei.Feng@Sun.COM 	/* reset Tx and Rx rings (and reclaim TXWIs) */
2556*9172SFei.Feng@Sun.COM 	for (qid = 0; qid < 6; qid++)
2557*9172SFei.Feng@Sun.COM 		rt2860_reset_tx_ring(sc, &sc->txq[qid]);
2558*9172SFei.Feng@Sun.COM 	rt2860_reset_rx_ring(sc, &sc->rxq);
2559*9172SFei.Feng@Sun.COM 
2560*9172SFei.Feng@Sun.COM 	/* by pass if it's quiesced */
2561*9172SFei.Feng@Sun.COM 	if (!(sc->sc_flags & RT2860_F_QUIESCE))
2562*9172SFei.Feng@Sun.COM 		RT2860_GLOCK(sc);
2563*9172SFei.Feng@Sun.COM 	sc->sc_flags &= ~RT2860_UPD_BEACON;
2564*9172SFei.Feng@Sun.COM 	/* by pass if it's quiesced */
2565*9172SFei.Feng@Sun.COM 	if (!(sc->sc_flags & RT2860_F_QUIESCE))
2566*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
2567*9172SFei.Feng@Sun.COM }
2568*9172SFei.Feng@Sun.COM 
2569*9172SFei.Feng@Sun.COM static int
2570*9172SFei.Feng@Sun.COM rt2860_m_start(void *arg)
2571*9172SFei.Feng@Sun.COM {
2572*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc = (struct rt2860_softc *)arg;
2573*9172SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
2574*9172SFei.Feng@Sun.COM 	int			err;
2575*9172SFei.Feng@Sun.COM 
2576*9172SFei.Feng@Sun.COM 	err = rt2860_init(sc);
2577*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
2578*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_m_start():"
2579*9172SFei.Feng@Sun.COM 		    "Hardware initialization failed\n");
2580*9172SFei.Feng@Sun.COM 		goto fail1;
2581*9172SFei.Feng@Sun.COM 	}
2582*9172SFei.Feng@Sun.COM 
2583*9172SFei.Feng@Sun.COM 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
2584*9172SFei.Feng@Sun.COM 
2585*9172SFei.Feng@Sun.COM 	RT2860_GLOCK(sc);
2586*9172SFei.Feng@Sun.COM 	sc->sc_flags |= RT2860_F_RUNNING;
2587*9172SFei.Feng@Sun.COM 	RT2860_GUNLOCK(sc);
2588*9172SFei.Feng@Sun.COM 
2589*9172SFei.Feng@Sun.COM 	return (err);
2590*9172SFei.Feng@Sun.COM fail1:
2591*9172SFei.Feng@Sun.COM 	rt2860_stop(sc);
2592*9172SFei.Feng@Sun.COM 	return (err);
2593*9172SFei.Feng@Sun.COM }
2594*9172SFei.Feng@Sun.COM 
2595*9172SFei.Feng@Sun.COM static void
2596*9172SFei.Feng@Sun.COM rt2860_m_stop(void *arg)
2597*9172SFei.Feng@Sun.COM {
2598*9172SFei.Feng@Sun.COM 	struct rt2860_softc *sc = (struct rt2860_softc *)arg;
2599*9172SFei.Feng@Sun.COM 
2600*9172SFei.Feng@Sun.COM 	(void) rt2860_stop(sc);
2601*9172SFei.Feng@Sun.COM 
2602*9172SFei.Feng@Sun.COM 	ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
2603*9172SFei.Feng@Sun.COM 
2604*9172SFei.Feng@Sun.COM 	RT2860_GLOCK(sc);
2605*9172SFei.Feng@Sun.COM 	sc->sc_flags &= ~RT2860_F_RUNNING;
2606*9172SFei.Feng@Sun.COM 	RT2860_GUNLOCK(sc);
2607*9172SFei.Feng@Sun.COM }
2608*9172SFei.Feng@Sun.COM 
2609*9172SFei.Feng@Sun.COM static void
2610*9172SFei.Feng@Sun.COM rt2860_m_ioctl(void* arg, queue_t *wq, mblk_t *mp)
2611*9172SFei.Feng@Sun.COM {
2612*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc = (struct rt2860_softc *)arg;
2613*9172SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
2614*9172SFei.Feng@Sun.COM 	int			err;
2615*9172SFei.Feng@Sun.COM 
2616*9172SFei.Feng@Sun.COM 	err = ieee80211_ioctl(ic, wq, mp);
2617*9172SFei.Feng@Sun.COM 	RT2860_GLOCK(sc);
2618*9172SFei.Feng@Sun.COM 	if (err == ENETRESET) {
2619*9172SFei.Feng@Sun.COM 		if (ic->ic_des_esslen) {
2620*9172SFei.Feng@Sun.COM 			if (RT2860_IS_RUNNING(sc)) {
2621*9172SFei.Feng@Sun.COM 				RT2860_GUNLOCK(sc);
2622*9172SFei.Feng@Sun.COM 				(void) rt2860_init(sc);
2623*9172SFei.Feng@Sun.COM 				(void) ieee80211_new_state(ic,
2624*9172SFei.Feng@Sun.COM 				    IEEE80211_S_SCAN, -1);
2625*9172SFei.Feng@Sun.COM 				RT2860_GLOCK(sc);
2626*9172SFei.Feng@Sun.COM 			}
2627*9172SFei.Feng@Sun.COM 		}
2628*9172SFei.Feng@Sun.COM 	}
2629*9172SFei.Feng@Sun.COM 	RT2860_GUNLOCK(sc);
2630*9172SFei.Feng@Sun.COM }
2631*9172SFei.Feng@Sun.COM 
2632*9172SFei.Feng@Sun.COM /*
2633*9172SFei.Feng@Sun.COM  * Call back function for get/set proporty
2634*9172SFei.Feng@Sun.COM  */
2635*9172SFei.Feng@Sun.COM static int
2636*9172SFei.Feng@Sun.COM rt2860_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2637*9172SFei.Feng@Sun.COM     uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm)
2638*9172SFei.Feng@Sun.COM {
2639*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc = (struct rt2860_softc *)arg;
2640*9172SFei.Feng@Sun.COM 	int			err = 0;
2641*9172SFei.Feng@Sun.COM 
2642*9172SFei.Feng@Sun.COM 	err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num,
2643*9172SFei.Feng@Sun.COM 	    pr_flags, wldp_length, wldp_buf, perm);
2644*9172SFei.Feng@Sun.COM 
2645*9172SFei.Feng@Sun.COM 	return (err);
2646*9172SFei.Feng@Sun.COM }
2647*9172SFei.Feng@Sun.COM 
2648*9172SFei.Feng@Sun.COM static int
2649*9172SFei.Feng@Sun.COM rt2860_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2650*9172SFei.Feng@Sun.COM     uint_t wldp_length, const void *wldp_buf)
2651*9172SFei.Feng@Sun.COM {
2652*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc = (struct rt2860_softc *)arg;
2653*9172SFei.Feng@Sun.COM 	ieee80211com_t		*ic = &sc->sc_ic;
2654*9172SFei.Feng@Sun.COM 	int			err;
2655*9172SFei.Feng@Sun.COM 
2656*9172SFei.Feng@Sun.COM 	err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
2657*9172SFei.Feng@Sun.COM 	    wldp_buf);
2658*9172SFei.Feng@Sun.COM 	RT2860_GLOCK(sc);
2659*9172SFei.Feng@Sun.COM 	if (err == ENETRESET) {
2660*9172SFei.Feng@Sun.COM 		if (ic->ic_des_esslen) {
2661*9172SFei.Feng@Sun.COM 			if (RT2860_IS_RUNNING(sc)) {
2662*9172SFei.Feng@Sun.COM 				RT2860_GUNLOCK(sc);
2663*9172SFei.Feng@Sun.COM 				(void) rt2860_init(sc);
2664*9172SFei.Feng@Sun.COM 				(void) ieee80211_new_state(ic,
2665*9172SFei.Feng@Sun.COM 				    IEEE80211_S_SCAN, -1);
2666*9172SFei.Feng@Sun.COM 				RT2860_GLOCK(sc);
2667*9172SFei.Feng@Sun.COM 			}
2668*9172SFei.Feng@Sun.COM 		}
2669*9172SFei.Feng@Sun.COM 		err = 0;
2670*9172SFei.Feng@Sun.COM 	}
2671*9172SFei.Feng@Sun.COM 	RT2860_GUNLOCK(sc);
2672*9172SFei.Feng@Sun.COM 	return (err);
2673*9172SFei.Feng@Sun.COM }
2674*9172SFei.Feng@Sun.COM 
2675*9172SFei.Feng@Sun.COM static mblk_t *
2676*9172SFei.Feng@Sun.COM rt2860_m_tx(void *arg, mblk_t *mp)
2677*9172SFei.Feng@Sun.COM {
2678*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc = (struct rt2860_softc *)arg;
2679*9172SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
2680*9172SFei.Feng@Sun.COM 	mblk_t			*next;
2681*9172SFei.Feng@Sun.COM 
2682*9172SFei.Feng@Sun.COM 	if (RT2860_IS_SUSPEND(sc)) {
2683*9172SFei.Feng@Sun.COM 		freemsgchain(mp);
2684*9172SFei.Feng@Sun.COM 		return (NULL);
2685*9172SFei.Feng@Sun.COM 	}
2686*9172SFei.Feng@Sun.COM 
2687*9172SFei.Feng@Sun.COM 	/*
2688*9172SFei.Feng@Sun.COM 	 * No data frames go out unless we're associated; this
2689*9172SFei.Feng@Sun.COM 	 * should not happen as the 802.11 layer does not enable
2690*9172SFei.Feng@Sun.COM 	 * the xmit queue until we enter the RUN state.
2691*9172SFei.Feng@Sun.COM 	 */
2692*9172SFei.Feng@Sun.COM 	if (ic->ic_state != IEEE80211_S_RUN) {
2693*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_tx_data(): "
2694*9172SFei.Feng@Sun.COM 		    "discard, state %u\n", ic->ic_state);
2695*9172SFei.Feng@Sun.COM 		freemsgchain(mp);
2696*9172SFei.Feng@Sun.COM 		return (NULL);
2697*9172SFei.Feng@Sun.COM 	}
2698*9172SFei.Feng@Sun.COM 
2699*9172SFei.Feng@Sun.COM 	while (mp != NULL) {
2700*9172SFei.Feng@Sun.COM 		next = mp->b_next;
2701*9172SFei.Feng@Sun.COM 		mp->b_next = NULL;
2702*9172SFei.Feng@Sun.COM 		if (rt2860_send(ic, mp, IEEE80211_FC0_TYPE_DATA) !=
2703*9172SFei.Feng@Sun.COM 		    DDI_SUCCESS) {
2704*9172SFei.Feng@Sun.COM 			mp->b_next = next;
2705*9172SFei.Feng@Sun.COM 			break;
2706*9172SFei.Feng@Sun.COM 		}
2707*9172SFei.Feng@Sun.COM 		mp = next;
2708*9172SFei.Feng@Sun.COM 	}
2709*9172SFei.Feng@Sun.COM 	return (mp);
2710*9172SFei.Feng@Sun.COM }
2711*9172SFei.Feng@Sun.COM 
2712*9172SFei.Feng@Sun.COM /*ARGSUSED*/
2713*9172SFei.Feng@Sun.COM static int
2714*9172SFei.Feng@Sun.COM rt2860_m_unicst(void *arg, const uint8_t *macaddr)
2715*9172SFei.Feng@Sun.COM {
2716*9172SFei.Feng@Sun.COM 	return (ENOTSUP);
2717*9172SFei.Feng@Sun.COM }
2718*9172SFei.Feng@Sun.COM 
2719*9172SFei.Feng@Sun.COM /*ARGSUSED*/
2720*9172SFei.Feng@Sun.COM static int
2721*9172SFei.Feng@Sun.COM rt2860_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
2722*9172SFei.Feng@Sun.COM {
2723*9172SFei.Feng@Sun.COM 	return (ENOTSUP);
2724*9172SFei.Feng@Sun.COM }
2725*9172SFei.Feng@Sun.COM 
2726*9172SFei.Feng@Sun.COM /*ARGSUSED*/
2727*9172SFei.Feng@Sun.COM static int
2728*9172SFei.Feng@Sun.COM rt2860_m_promisc(void *arg, boolean_t on)
2729*9172SFei.Feng@Sun.COM {
2730*9172SFei.Feng@Sun.COM 	return (0);
2731*9172SFei.Feng@Sun.COM }
2732*9172SFei.Feng@Sun.COM 
2733*9172SFei.Feng@Sun.COM static int
2734*9172SFei.Feng@Sun.COM rt2860_m_stat(void *arg, uint_t stat, uint64_t *val)
2735*9172SFei.Feng@Sun.COM {
2736*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc  = (struct rt2860_softc *)arg;
2737*9172SFei.Feng@Sun.COM 	ieee80211com_t		*ic = &sc->sc_ic;
2738*9172SFei.Feng@Sun.COM 	ieee80211_node_t	*ni = ic->ic_bss;
2739*9172SFei.Feng@Sun.COM 	struct ieee80211_rateset *rs = &ni->in_rates;
2740*9172SFei.Feng@Sun.COM 
2741*9172SFei.Feng@Sun.COM 	RT2860_GLOCK(sc);
2742*9172SFei.Feng@Sun.COM 	switch (stat) {
2743*9172SFei.Feng@Sun.COM 	case MAC_STAT_IFSPEED:
2744*9172SFei.Feng@Sun.COM 		*val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
2745*9172SFei.Feng@Sun.COM 		    (rs->ir_rates[ni->in_txrate] & IEEE80211_RATE_VAL)
2746*9172SFei.Feng@Sun.COM 		    : ic->ic_fixed_rate) / 2 * 1000000;
2747*9172SFei.Feng@Sun.COM 		break;
2748*9172SFei.Feng@Sun.COM 	case MAC_STAT_NOXMTBUF:
2749*9172SFei.Feng@Sun.COM 		*val = sc->sc_tx_nobuf;
2750*9172SFei.Feng@Sun.COM 		break;
2751*9172SFei.Feng@Sun.COM 	case MAC_STAT_NORCVBUF:
2752*9172SFei.Feng@Sun.COM 		*val = sc->sc_rx_nobuf;
2753*9172SFei.Feng@Sun.COM 		break;
2754*9172SFei.Feng@Sun.COM 	case MAC_STAT_IERRORS:
2755*9172SFei.Feng@Sun.COM 		*val = sc->sc_rx_err;
2756*9172SFei.Feng@Sun.COM 		break;
2757*9172SFei.Feng@Sun.COM 	case MAC_STAT_RBYTES:
2758*9172SFei.Feng@Sun.COM 		*val = ic->ic_stats.is_rx_bytes;
2759*9172SFei.Feng@Sun.COM 		break;
2760*9172SFei.Feng@Sun.COM 	case MAC_STAT_IPACKETS:
2761*9172SFei.Feng@Sun.COM 		*val = ic->ic_stats.is_rx_frags;
2762*9172SFei.Feng@Sun.COM 		break;
2763*9172SFei.Feng@Sun.COM 	case MAC_STAT_OBYTES:
2764*9172SFei.Feng@Sun.COM 		*val = ic->ic_stats.is_tx_bytes;
2765*9172SFei.Feng@Sun.COM 		break;
2766*9172SFei.Feng@Sun.COM 	case MAC_STAT_OPACKETS:
2767*9172SFei.Feng@Sun.COM 		*val = ic->ic_stats.is_tx_frags;
2768*9172SFei.Feng@Sun.COM 		break;
2769*9172SFei.Feng@Sun.COM 	case MAC_STAT_OERRORS:
2770*9172SFei.Feng@Sun.COM 	case WIFI_STAT_TX_FAILED:
2771*9172SFei.Feng@Sun.COM 		*val = sc->sc_tx_err;
2772*9172SFei.Feng@Sun.COM 		break;
2773*9172SFei.Feng@Sun.COM 	case WIFI_STAT_TX_RETRANS:
2774*9172SFei.Feng@Sun.COM 		*val = sc->sc_tx_retries;
2775*9172SFei.Feng@Sun.COM 		break;
2776*9172SFei.Feng@Sun.COM 	case WIFI_STAT_FCS_ERRORS:
2777*9172SFei.Feng@Sun.COM 	case WIFI_STAT_WEP_ERRORS:
2778*9172SFei.Feng@Sun.COM 	case WIFI_STAT_TX_FRAGS:
2779*9172SFei.Feng@Sun.COM 	case WIFI_STAT_MCAST_TX:
2780*9172SFei.Feng@Sun.COM 	case WIFI_STAT_RTS_SUCCESS:
2781*9172SFei.Feng@Sun.COM 	case WIFI_STAT_RTS_FAILURE:
2782*9172SFei.Feng@Sun.COM 	case WIFI_STAT_ACK_FAILURE:
2783*9172SFei.Feng@Sun.COM 	case WIFI_STAT_RX_FRAGS:
2784*9172SFei.Feng@Sun.COM 	case WIFI_STAT_MCAST_RX:
2785*9172SFei.Feng@Sun.COM 	case WIFI_STAT_RX_DUPS:
2786*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
2787*9172SFei.Feng@Sun.COM 		return (ieee80211_stat(ic, stat, val));
2788*9172SFei.Feng@Sun.COM 	default:
2789*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
2790*9172SFei.Feng@Sun.COM 		return (ENOTSUP);
2791*9172SFei.Feng@Sun.COM 	}
2792*9172SFei.Feng@Sun.COM 	RT2860_GUNLOCK(sc);
2793*9172SFei.Feng@Sun.COM 
2794*9172SFei.Feng@Sun.COM 	return (0);
2795*9172SFei.Feng@Sun.COM }
2796*9172SFei.Feng@Sun.COM 
2797*9172SFei.Feng@Sun.COM static int
2798*9172SFei.Feng@Sun.COM rt2860_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
2799*9172SFei.Feng@Sun.COM {
2800*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc;
2801*9172SFei.Feng@Sun.COM 	struct ieee80211com	*ic;
2802*9172SFei.Feng@Sun.COM 	int			i, err, qid, ntries, instance;
2803*9172SFei.Feng@Sun.COM 	uint8_t			cachelsz;
2804*9172SFei.Feng@Sun.COM 	uint16_t		command, vendor_id, device_id;
2805*9172SFei.Feng@Sun.COM 	char			strbuf[32];
2806*9172SFei.Feng@Sun.COM 	wifi_data_t		wd = { 0 };
2807*9172SFei.Feng@Sun.COM 	mac_register_t		*macp;
2808*9172SFei.Feng@Sun.COM 
2809*9172SFei.Feng@Sun.COM 	switch (cmd) {
2810*9172SFei.Feng@Sun.COM 	case DDI_ATTACH:
2811*9172SFei.Feng@Sun.COM 		break;
2812*9172SFei.Feng@Sun.COM 	case DDI_RESUME:
2813*9172SFei.Feng@Sun.COM 		sc = ddi_get_soft_state(rt2860_soft_state_p,
2814*9172SFei.Feng@Sun.COM 		    ddi_get_instance(devinfo));
2815*9172SFei.Feng@Sun.COM 		ASSERT(sc != NULL);
2816*9172SFei.Feng@Sun.COM 		RT2860_GLOCK(sc);
2817*9172SFei.Feng@Sun.COM 		sc->sc_flags &= ~RT2860_F_SUSPEND;
2818*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
2819*9172SFei.Feng@Sun.COM 		if (RT2860_IS_RUNNING(sc))
2820*9172SFei.Feng@Sun.COM 			(void) rt2860_init(sc);
2821*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_RESUME, "rwn: rt2860_attach(): "
2822*9172SFei.Feng@Sun.COM 		    "resume now\n");
2823*9172SFei.Feng@Sun.COM 		return (DDI_SUCCESS);
2824*9172SFei.Feng@Sun.COM 	default:
2825*9172SFei.Feng@Sun.COM 		return (DDI_FAILURE);
2826*9172SFei.Feng@Sun.COM 	}
2827*9172SFei.Feng@Sun.COM 
2828*9172SFei.Feng@Sun.COM 	instance = ddi_get_instance(devinfo);
2829*9172SFei.Feng@Sun.COM 
2830*9172SFei.Feng@Sun.COM 	err = ddi_soft_state_zalloc(rt2860_soft_state_p, instance);
2831*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
2832*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2833*9172SFei.Feng@Sun.COM 		    "unable to alloc soft_state_p\n");
2834*9172SFei.Feng@Sun.COM 		return (err);
2835*9172SFei.Feng@Sun.COM 	}
2836*9172SFei.Feng@Sun.COM 
2837*9172SFei.Feng@Sun.COM 	sc = ddi_get_soft_state(rt2860_soft_state_p, instance);
2838*9172SFei.Feng@Sun.COM 	ic = (ieee80211com_t *)&sc->sc_ic;
2839*9172SFei.Feng@Sun.COM 	sc->sc_dev = devinfo;
2840*9172SFei.Feng@Sun.COM 
2841*9172SFei.Feng@Sun.COM 	/* pci configuration */
2842*9172SFei.Feng@Sun.COM 	err = ddi_regs_map_setup(devinfo, 0, &sc->sc_cfg_base, 0, 0,
2843*9172SFei.Feng@Sun.COM 	    &rwn_csr_accattr, &sc->sc_cfg_handle);
2844*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
2845*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2846*9172SFei.Feng@Sun.COM 		    "ddi_regs_map_setup() failed");
2847*9172SFei.Feng@Sun.COM 		goto fail1;
2848*9172SFei.Feng@Sun.COM 	}
2849*9172SFei.Feng@Sun.COM 
2850*9172SFei.Feng@Sun.COM 	cachelsz = ddi_get8(sc->sc_cfg_handle,
2851*9172SFei.Feng@Sun.COM 	    (uint8_t *)(sc->sc_cfg_base + PCI_CONF_CACHE_LINESZ));
2852*9172SFei.Feng@Sun.COM 	if (cachelsz == 0)
2853*9172SFei.Feng@Sun.COM 		cachelsz = 0x10;
2854*9172SFei.Feng@Sun.COM 	sc->sc_cachelsz = cachelsz << 2;
2855*9172SFei.Feng@Sun.COM 	sc->sc_dmabuf_size = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz);
2856*9172SFei.Feng@Sun.COM 
2857*9172SFei.Feng@Sun.COM 	vendor_id = ddi_get16(sc->sc_cfg_handle,
2858*9172SFei.Feng@Sun.COM 	    (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_VENID));
2859*9172SFei.Feng@Sun.COM 	device_id = ddi_get16(sc->sc_cfg_handle,
2860*9172SFei.Feng@Sun.COM 	    (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_DEVID));
2861*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2862*9172SFei.Feng@Sun.COM 	    "vendor 0x%x, device id 0x%x, cache size %d\n",
2863*9172SFei.Feng@Sun.COM 	    vendor_id, device_id, cachelsz);
2864*9172SFei.Feng@Sun.COM 
2865*9172SFei.Feng@Sun.COM 	/*
2866*9172SFei.Feng@Sun.COM 	 * Enable response to memory space accesses,
2867*9172SFei.Feng@Sun.COM 	 * and enabe bus master.
2868*9172SFei.Feng@Sun.COM 	 */
2869*9172SFei.Feng@Sun.COM 	command = PCI_COMM_MAE | PCI_COMM_ME;
2870*9172SFei.Feng@Sun.COM 	ddi_put16(sc->sc_cfg_handle,
2871*9172SFei.Feng@Sun.COM 	    (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_COMM),
2872*9172SFei.Feng@Sun.COM 	    command);
2873*9172SFei.Feng@Sun.COM 	ddi_put8(sc->sc_cfg_handle,
2874*9172SFei.Feng@Sun.COM 	    (uint8_t *)(sc->sc_cfg_base + PCI_CONF_LATENCY_TIMER), 0xa8);
2875*9172SFei.Feng@Sun.COM 	ddi_put8(sc->sc_cfg_handle,
2876*9172SFei.Feng@Sun.COM 	    (uint8_t *)(sc->sc_cfg_base + PCI_CONF_ILINE), 0x10);
2877*9172SFei.Feng@Sun.COM 
2878*9172SFei.Feng@Sun.COM 	/* pci i/o space */
2879*9172SFei.Feng@Sun.COM 	err = ddi_regs_map_setup(devinfo, 1,
2880*9172SFei.Feng@Sun.COM 	    &sc->sc_io_base, 0, 0, &rwn_csr_accattr, &sc->sc_io_handle);
2881*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
2882*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2883*9172SFei.Feng@Sun.COM 		    "ddi_regs_map_setup() failed");
2884*9172SFei.Feng@Sun.COM 		goto fail2;
2885*9172SFei.Feng@Sun.COM 	}
2886*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2887*9172SFei.Feng@Sun.COM 	    "PCI configuration is done successfully\n");
2888*9172SFei.Feng@Sun.COM 
2889*9172SFei.Feng@Sun.COM 	/* wait for NIC to initialize */
2890*9172SFei.Feng@Sun.COM 	for (ntries = 0; ntries < 100; ntries++) {
2891*9172SFei.Feng@Sun.COM 		sc->mac_rev = RT2860_READ(sc, RT2860_ASIC_VER_ID);
2892*9172SFei.Feng@Sun.COM 		if (sc->mac_rev != 0 && sc->mac_rev != 0xffffffff)
2893*9172SFei.Feng@Sun.COM 			break;
2894*9172SFei.Feng@Sun.COM 		DELAY(10);
2895*9172SFei.Feng@Sun.COM 	}
2896*9172SFei.Feng@Sun.COM 	if (ntries == 100) {
2897*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2898*9172SFei.Feng@Sun.COM 		    "timeout waiting for NIC initialize\n");
2899*9172SFei.Feng@Sun.COM 		return (DDI_FAILURE);
2900*9172SFei.Feng@Sun.COM 	}
2901*9172SFei.Feng@Sun.COM 
2902*9172SFei.Feng@Sun.COM 	if ((sc->mac_rev >> 16) != 0x2860 &&
2903*9172SFei.Feng@Sun.COM 	    (device_id == PRODUCT_RALINK_RT2890 ||
2904*9172SFei.Feng@Sun.COM 	    device_id == PRODUCT_RALINK_RT2790 ||
2905*9172SFei.Feng@Sun.COM 	    device_id == PRODUCT_AWT_RT2890))
2906*9172SFei.Feng@Sun.COM 		sc->sc_flags |= RT2860_ADVANCED_PS;
2907*9172SFei.Feng@Sun.COM 
2908*9172SFei.Feng@Sun.COM 	/* retrieve RF rev. no and various other things from EEPROM */
2909*9172SFei.Feng@Sun.COM 	(void) rt2860_read_eeprom(sc);
2910*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2911*9172SFei.Feng@Sun.COM 	    "MAC/BBP RT%X (rev 0x%04X), RF %s (%dT%dR)\n",
2912*9172SFei.Feng@Sun.COM 	    sc->mac_rev >> 16, sc->mac_rev & 0xffff,
2913*9172SFei.Feng@Sun.COM 	    rt2860_get_rf(sc->rf_rev), sc->ntxchains, sc->nrxchains);
2914*9172SFei.Feng@Sun.COM 
2915*9172SFei.Feng@Sun.COM 	/*
2916*9172SFei.Feng@Sun.COM 	 * Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings.
2917*9172SFei.Feng@Sun.COM 	 */
2918*9172SFei.Feng@Sun.COM 	for (qid = 0; qid < 6; qid++) {
2919*9172SFei.Feng@Sun.COM 		if ((err = rt2860_alloc_tx_ring(sc, &sc->txq[qid])) != 0) {
2920*9172SFei.Feng@Sun.COM 			RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2921*9172SFei.Feng@Sun.COM 			    "could not allocate Tx ring %d\n", qid);
2922*9172SFei.Feng@Sun.COM 			goto fail3;
2923*9172SFei.Feng@Sun.COM 		}
2924*9172SFei.Feng@Sun.COM 	}
2925*9172SFei.Feng@Sun.COM 
2926*9172SFei.Feng@Sun.COM 	if ((err = rt2860_alloc_rx_ring(sc, &sc->rxq)) != 0) {
2927*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2928*9172SFei.Feng@Sun.COM 		    "could not allocte Rx ring\n");
2929*9172SFei.Feng@Sun.COM 		goto fail4;
2930*9172SFei.Feng@Sun.COM 	}
2931*9172SFei.Feng@Sun.COM 
2932*9172SFei.Feng@Sun.COM 	if ((err = rt2860_alloc_tx_pool(sc)) != 0) {
2933*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2934*9172SFei.Feng@Sun.COM 		    "could not allocte Tx pool\n");
2935*9172SFei.Feng@Sun.COM 		goto fail5;
2936*9172SFei.Feng@Sun.COM 	}
2937*9172SFei.Feng@Sun.COM 
2938*9172SFei.Feng@Sun.COM 	mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL);
2939*9172SFei.Feng@Sun.COM 	mutex_init(&sc->sc_txlock, NULL, MUTEX_DRIVER, NULL);
2940*9172SFei.Feng@Sun.COM 	mutex_init(&sc->sc_rxlock, NULL, MUTEX_DRIVER, NULL);
2941*9172SFei.Feng@Sun.COM 
2942*9172SFei.Feng@Sun.COM 	/* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */
2943*9172SFei.Feng@Sun.COM 	sc->mgtqid = (sc->mac_rev == 0x28600100) ? EDCA_AC_VO : 5;
2944*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach():"
2945*9172SFei.Feng@Sun.COM 	    "mgtqid = %x\n", sc->mgtqid);
2946*9172SFei.Feng@Sun.COM 
2947*9172SFei.Feng@Sun.COM 	ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
2948*9172SFei.Feng@Sun.COM 	ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
2949*9172SFei.Feng@Sun.COM 	ic->ic_state = IEEE80211_S_INIT;
2950*9172SFei.Feng@Sun.COM 
2951*9172SFei.Feng@Sun.COM 	/* set device capabilities */
2952*9172SFei.Feng@Sun.COM 	ic->ic_caps =
2953*9172SFei.Feng@Sun.COM 	    IEEE80211_C_TXPMGT |	/* tx power management */
2954*9172SFei.Feng@Sun.COM 	    IEEE80211_C_SHPREAMBLE |	/* short preamble supported */
2955*9172SFei.Feng@Sun.COM 	    IEEE80211_C_SHSLOT;		/* short slot time supported */
2956*9172SFei.Feng@Sun.COM 
2957*9172SFei.Feng@Sun.COM 	/* WPA/WPA2 support */
2958*9172SFei.Feng@Sun.COM 	ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */
2959*9172SFei.Feng@Sun.COM 
2960*9172SFei.Feng@Sun.COM 	/* set supported .11b and .11g rates */
2961*9172SFei.Feng@Sun.COM 	ic->ic_sup_rates[IEEE80211_MODE_11B] = rt2560_rateset_11b;
2962*9172SFei.Feng@Sun.COM 	ic->ic_sup_rates[IEEE80211_MODE_11G] = rt2560_rateset_11g;
2963*9172SFei.Feng@Sun.COM 
2964*9172SFei.Feng@Sun.COM 	/* set supported .11b and .11g channels (1 through 14) */
2965*9172SFei.Feng@Sun.COM 	for (i = 1; i <= 14; i++) {
2966*9172SFei.Feng@Sun.COM 		ic->ic_sup_channels[i].ich_freq =
2967*9172SFei.Feng@Sun.COM 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
2968*9172SFei.Feng@Sun.COM 		ic->ic_sup_channels[i].ich_flags =
2969*9172SFei.Feng@Sun.COM 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
2970*9172SFei.Feng@Sun.COM 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
2971*9172SFei.Feng@Sun.COM 	}
2972*9172SFei.Feng@Sun.COM 
2973*9172SFei.Feng@Sun.COM 	ic->ic_xmit = rt2860_send;
2974*9172SFei.Feng@Sun.COM 
2975*9172SFei.Feng@Sun.COM 	ieee80211_attach(ic);
2976*9172SFei.Feng@Sun.COM 
2977*9172SFei.Feng@Sun.COM 	/* register WPA door */
2978*9172SFei.Feng@Sun.COM 	ieee80211_register_door(ic, ddi_driver_name(devinfo),
2979*9172SFei.Feng@Sun.COM 	    ddi_get_instance(devinfo));
2980*9172SFei.Feng@Sun.COM 
2981*9172SFei.Feng@Sun.COM 	/* override state transition machine */
2982*9172SFei.Feng@Sun.COM 	sc->sc_newstate = ic->ic_newstate;
2983*9172SFei.Feng@Sun.COM 	ic->ic_newstate = rt2860_newstate;
2984*9172SFei.Feng@Sun.COM 	ieee80211_media_init(ic);
2985*9172SFei.Feng@Sun.COM 	ic->ic_def_txkey = 0;
2986*9172SFei.Feng@Sun.COM 
2987*9172SFei.Feng@Sun.COM 	err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW,
2988*9172SFei.Feng@Sun.COM 	    &sc->sc_softintr_hdl, NULL, 0, rt2860_softintr, (caddr_t)sc);
2989*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
2990*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2991*9172SFei.Feng@Sun.COM 		    "ddi_add_softintr() failed");
2992*9172SFei.Feng@Sun.COM 		goto fail8;
2993*9172SFei.Feng@Sun.COM 	}
2994*9172SFei.Feng@Sun.COM 
2995*9172SFei.Feng@Sun.COM 	err = ddi_get_iblock_cookie(devinfo, 0, &sc->sc_iblock);
2996*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
2997*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
2998*9172SFei.Feng@Sun.COM 		    "Can not get iblock cookie for INT\n");
2999*9172SFei.Feng@Sun.COM 		goto fail7;
3000*9172SFei.Feng@Sun.COM 	}
3001*9172SFei.Feng@Sun.COM 
3002*9172SFei.Feng@Sun.COM 	err = ddi_add_intr(devinfo, 0, NULL, NULL, rt2860_intr, (caddr_t)sc);
3003*9172SFei.Feng@Sun.COM 	if (err != DDI_SUCCESS) {
3004*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
3005*9172SFei.Feng@Sun.COM 		    "unable to add device interrupt handler\n");
3006*9172SFei.Feng@Sun.COM 		goto fail7;
3007*9172SFei.Feng@Sun.COM 	}
3008*9172SFei.Feng@Sun.COM 
3009*9172SFei.Feng@Sun.COM 	/*
3010*9172SFei.Feng@Sun.COM 	 * Provide initial settings for the WiFi plugin; whenever this
3011*9172SFei.Feng@Sun.COM 	 * information changes, we need to call mac_plugindata_update()
3012*9172SFei.Feng@Sun.COM 	 */
3013*9172SFei.Feng@Sun.COM 	wd.wd_opmode = ic->ic_opmode;
3014*9172SFei.Feng@Sun.COM 	wd.wd_secalloc = WIFI_SEC_NONE;
3015*9172SFei.Feng@Sun.COM 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
3016*9172SFei.Feng@Sun.COM 
3017*9172SFei.Feng@Sun.COM 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
3018*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
3019*9172SFei.Feng@Sun.COM 		    "MAC version mismatch\n");
3020*9172SFei.Feng@Sun.COM 		goto fail9;
3021*9172SFei.Feng@Sun.COM 	}
3022*9172SFei.Feng@Sun.COM 
3023*9172SFei.Feng@Sun.COM 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
3024*9172SFei.Feng@Sun.COM 	macp->m_driver		= sc;
3025*9172SFei.Feng@Sun.COM 	macp->m_dip		= devinfo;
3026*9172SFei.Feng@Sun.COM 	macp->m_src_addr	= ic->ic_macaddr;
3027*9172SFei.Feng@Sun.COM 	macp->m_callbacks	= &rt2860_m_callbacks;
3028*9172SFei.Feng@Sun.COM 	macp->m_min_sdu		= 0;
3029*9172SFei.Feng@Sun.COM 	macp->m_max_sdu		= IEEE80211_MTU;
3030*9172SFei.Feng@Sun.COM 	macp->m_pdata		= &wd;
3031*9172SFei.Feng@Sun.COM 	macp->m_pdata_size	= sizeof (wd);
3032*9172SFei.Feng@Sun.COM 
3033*9172SFei.Feng@Sun.COM 	err = mac_register(macp, &ic->ic_mach);
3034*9172SFei.Feng@Sun.COM 	mac_free(macp);
3035*9172SFei.Feng@Sun.COM 	if (err != 0) {
3036*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): "
3037*9172SFei.Feng@Sun.COM 		    "mac_register err %x\n", err);
3038*9172SFei.Feng@Sun.COM 		goto fail9;
3039*9172SFei.Feng@Sun.COM 	}
3040*9172SFei.Feng@Sun.COM 
3041*9172SFei.Feng@Sun.COM 	/*
3042*9172SFei.Feng@Sun.COM 	 * Create minor node of type DDI_NT_NET_WIFI
3043*9172SFei.Feng@Sun.COM 	 */
3044*9172SFei.Feng@Sun.COM 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
3045*9172SFei.Feng@Sun.COM 	    "rwn", instance);
3046*9172SFei.Feng@Sun.COM 	err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
3047*9172SFei.Feng@Sun.COM 	    instance + 1, DDI_NT_NET_WIFI, 0);
3048*9172SFei.Feng@Sun.COM 
3049*9172SFei.Feng@Sun.COM 	/*
3050*9172SFei.Feng@Sun.COM 	 * Notify link is down now
3051*9172SFei.Feng@Sun.COM 	 */
3052*9172SFei.Feng@Sun.COM 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
3053*9172SFei.Feng@Sun.COM 
3054*9172SFei.Feng@Sun.COM 	sc->sc_flags &= ~RT2860_F_RUNNING;
3055*9172SFei.Feng@Sun.COM 
3056*9172SFei.Feng@Sun.COM 	RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach() successfully.\n");
3057*9172SFei.Feng@Sun.COM 	return (DDI_SUCCESS);
3058*9172SFei.Feng@Sun.COM fail9:
3059*9172SFei.Feng@Sun.COM 	ddi_remove_softintr(sc->sc_softintr_hdl);
3060*9172SFei.Feng@Sun.COM fail8:
3061*9172SFei.Feng@Sun.COM 	ddi_remove_intr(devinfo, 0, sc->sc_iblock);
3062*9172SFei.Feng@Sun.COM fail7:
3063*9172SFei.Feng@Sun.COM 	mutex_destroy(&sc->sc_genlock);
3064*9172SFei.Feng@Sun.COM 	mutex_destroy(&sc->sc_txlock);
3065*9172SFei.Feng@Sun.COM 	mutex_destroy(&sc->sc_rxlock);
3066*9172SFei.Feng@Sun.COM fail6:
3067*9172SFei.Feng@Sun.COM 	rt2860_free_tx_pool(sc);
3068*9172SFei.Feng@Sun.COM fail5:
3069*9172SFei.Feng@Sun.COM 	rt2860_free_rx_ring(sc, &sc->rxq);
3070*9172SFei.Feng@Sun.COM fail4:
3071*9172SFei.Feng@Sun.COM 	while (--qid >= 0)
3072*9172SFei.Feng@Sun.COM 		rt2860_free_tx_ring(sc, &sc->txq[qid]);
3073*9172SFei.Feng@Sun.COM 
3074*9172SFei.Feng@Sun.COM fail3:
3075*9172SFei.Feng@Sun.COM 	ddi_regs_map_free(&sc->sc_io_handle);
3076*9172SFei.Feng@Sun.COM fail2:
3077*9172SFei.Feng@Sun.COM 	ddi_regs_map_free(&sc->sc_cfg_handle);
3078*9172SFei.Feng@Sun.COM fail1:
3079*9172SFei.Feng@Sun.COM 	return (err);
3080*9172SFei.Feng@Sun.COM }
3081*9172SFei.Feng@Sun.COM 
3082*9172SFei.Feng@Sun.COM static int
3083*9172SFei.Feng@Sun.COM rt2860_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
3084*9172SFei.Feng@Sun.COM {
3085*9172SFei.Feng@Sun.COM 	struct rt2860_softc	*sc;
3086*9172SFei.Feng@Sun.COM 	int			qid;
3087*9172SFei.Feng@Sun.COM 
3088*9172SFei.Feng@Sun.COM 	sc = ddi_get_soft_state(rt2860_soft_state_p, ddi_get_instance(devinfo));
3089*9172SFei.Feng@Sun.COM 
3090*9172SFei.Feng@Sun.COM 	switch (cmd) {
3091*9172SFei.Feng@Sun.COM 	case DDI_DETACH:
3092*9172SFei.Feng@Sun.COM 		break;
3093*9172SFei.Feng@Sun.COM 	case DDI_SUSPEND:
3094*9172SFei.Feng@Sun.COM 		if (RT2860_IS_RUNNING(sc))
3095*9172SFei.Feng@Sun.COM 			rt2860_stop(sc);
3096*9172SFei.Feng@Sun.COM 		RT2860_GLOCK(sc);
3097*9172SFei.Feng@Sun.COM 		sc->sc_flags &= ~RT2860_FWLOADED;
3098*9172SFei.Feng@Sun.COM 		sc->sc_flags |= RT2860_F_SUSPEND;
3099*9172SFei.Feng@Sun.COM 		RT2860_GUNLOCK(sc);
3100*9172SFei.Feng@Sun.COM 		RWN_DEBUG(RT2860_DBG_RESUME, "rwn: rt2860_detach(): "
3101*9172SFei.Feng@Sun.COM 		    "suspend now\n");
3102*9172SFei.Feng@Sun.COM 		return (DDI_SUCCESS);
3103*9172SFei.Feng@Sun.COM 	default:
3104*9172SFei.Feng@Sun.COM 		return (DDI_FAILURE);
3105*9172SFei.Feng@Sun.COM 	}
3106*9172SFei.Feng@Sun.COM 
3107*9172SFei.Feng@Sun.COM 	if (mac_disable(sc->sc_ic.ic_mach) != 0)
3108*9172SFei.Feng@Sun.COM 		return (DDI_FAILURE);
3109*9172SFei.Feng@Sun.COM 
3110*9172SFei.Feng@Sun.COM 	rt2860_stop(sc);
3111*9172SFei.Feng@Sun.COM 
3112*9172SFei.Feng@Sun.COM 	/*
3113*9172SFei.Feng@Sun.COM 	 * Unregister from the MAC layer subsystem
3114*9172SFei.Feng@Sun.COM 	 */
3115*9172SFei.Feng@Sun.COM 	(void) mac_unregister(sc->sc_ic.ic_mach);
3116*9172SFei.Feng@Sun.COM 
3117*9172SFei.Feng@Sun.COM 	ddi_remove_intr(devinfo, 0, sc->sc_iblock);
3118*9172SFei.Feng@Sun.COM 	ddi_remove_softintr(sc->sc_softintr_hdl);
3119*9172SFei.Feng@Sun.COM 
3120*9172SFei.Feng@Sun.COM 	/*
3121*9172SFei.Feng@Sun.COM 	 * detach ieee80211 layer
3122*9172SFei.Feng@Sun.COM 	 */
3123*9172SFei.Feng@Sun.COM 	ieee80211_detach(&sc->sc_ic);
3124*9172SFei.Feng@Sun.COM 
3125*9172SFei.Feng@Sun.COM 	rt2860_free_tx_pool(sc);
3126*9172SFei.Feng@Sun.COM 	rt2860_free_rx_ring(sc, &sc->rxq);
3127*9172SFei.Feng@Sun.COM 	for (qid = 0; qid < 6; qid++)
3128*9172SFei.Feng@Sun.COM 		rt2860_free_tx_ring(sc, &sc->txq[qid]);
3129*9172SFei.Feng@Sun.COM 
3130*9172SFei.Feng@Sun.COM 	ddi_regs_map_free(&sc->sc_io_handle);
3131*9172SFei.Feng@Sun.COM 
3132*9172SFei.Feng@Sun.COM 	mutex_destroy(&sc->sc_genlock);
3133*9172SFei.Feng@Sun.COM 	mutex_destroy(&sc->sc_txlock);
3134*9172SFei.Feng@Sun.COM 	mutex_destroy(&sc->sc_rxlock);
3135*9172SFei.Feng@Sun.COM 
3136*9172SFei.Feng@Sun.COM 	ddi_remove_minor_node(devinfo, NULL);
3137*9172SFei.Feng@Sun.COM 	ddi_soft_state_free(rt2860_soft_state_p, ddi_get_instance(devinfo));
3138*9172SFei.Feng@Sun.COM 
3139*9172SFei.Feng@Sun.COM 	return (DDI_SUCCESS);
3140*9172SFei.Feng@Sun.COM }
3141*9172SFei.Feng@Sun.COM 
3142*9172SFei.Feng@Sun.COM int
3143*9172SFei.Feng@Sun.COM _info(struct modinfo *modinfop)
3144*9172SFei.Feng@Sun.COM {
3145*9172SFei.Feng@Sun.COM 	return (mod_info(&modlinkage, modinfop));
3146*9172SFei.Feng@Sun.COM }
3147*9172SFei.Feng@Sun.COM 
3148*9172SFei.Feng@Sun.COM int
3149*9172SFei.Feng@Sun.COM _init(void)
3150*9172SFei.Feng@Sun.COM {
3151*9172SFei.Feng@Sun.COM 	int status;
3152*9172SFei.Feng@Sun.COM 
3153*9172SFei.Feng@Sun.COM 	status = ddi_soft_state_init(&rt2860_soft_state_p,
3154*9172SFei.Feng@Sun.COM 	    sizeof (struct rt2860_softc), 1);
3155*9172SFei.Feng@Sun.COM 	if (status != 0)
3156*9172SFei.Feng@Sun.COM 		return (status);
3157*9172SFei.Feng@Sun.COM 
3158*9172SFei.Feng@Sun.COM 	mac_init_ops(&rwn_dev_ops, "rwn");
3159*9172SFei.Feng@Sun.COM 	status = mod_install(&modlinkage);
3160*9172SFei.Feng@Sun.COM 	if (status != 0) {
3161*9172SFei.Feng@Sun.COM 		mac_fini_ops(&rwn_dev_ops);
3162*9172SFei.Feng@Sun.COM 		ddi_soft_state_fini(&rt2860_soft_state_p);
3163*9172SFei.Feng@Sun.COM 	}
3164*9172SFei.Feng@Sun.COM 	return (status);
3165*9172SFei.Feng@Sun.COM }
3166*9172SFei.Feng@Sun.COM 
3167*9172SFei.Feng@Sun.COM int
3168*9172SFei.Feng@Sun.COM _fini(void)
3169*9172SFei.Feng@Sun.COM {
3170*9172SFei.Feng@Sun.COM 	int status;
3171*9172SFei.Feng@Sun.COM 
3172*9172SFei.Feng@Sun.COM 	status = mod_remove(&modlinkage);
3173*9172SFei.Feng@Sun.COM 	if (status == 0) {
3174*9172SFei.Feng@Sun.COM 		mac_fini_ops(&rwn_dev_ops);
3175*9172SFei.Feng@Sun.COM 		ddi_soft_state_fini(&rt2860_soft_state_p);
3176*9172SFei.Feng@Sun.COM 	}
3177*9172SFei.Feng@Sun.COM 	return (status);
3178*9172SFei.Feng@Sun.COM }
3179