1a72f7eaql/*
20dc2366Venugopal Iyer * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
3a72f7eaql * Use is subject to license terms.
4a72f7eaql */
5a72f7eaql
6a72f7eaql/*
7a72f7eaql * Copyright (c) 2004 David Young.  All rights reserved.
8a72f7eaql *
9a72f7eaql * This code was written by David Young.
10a72f7eaql *
11a72f7eaql * Redistribution and use in source and binary forms, with or without
12a72f7eaql * modification, are permitted provided that the following conditions
13a72f7eaql * are met:
14a72f7eaql * 1. Redistributions of source code must retain the above copyright
15a72f7eaql *    notice, this list of conditions and the following disclaimer.
16a72f7eaql * 2. Redistributions in binary form must reproduce the above copyright
17a72f7eaql *    notice, this list of conditions and the following disclaimer in the
18a72f7eaql *    documentation and/or other materials provided with the distribution.
19a72f7eaql * 3. Neither the name of the author nor the names of any co-contributors
20a72f7eaql *    may be used to endorse or promote products derived from this software
21a72f7eaql *    without specific prior written permission.
22a72f7eaql *
23a72f7eaql * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
24a72f7eaql * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25a72f7eaql * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26a72f7eaql * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL David
27a72f7eaql * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28a72f7eaql * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29a72f7eaql * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30a72f7eaql * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31a72f7eaql * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32a72f7eaql * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33a72f7eaql * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
34a72f7eaql * OF SUCH DAMAGE.
35a72f7eaql */
369aa73b6Qin Michael Li#include <sys/sysmacros.h>
37a72f7eaql#include <sys/pci.h>
389aa73b6Qin Michael Li#include <sys/stat.h>
39a72f7eaql#include <sys/strsubr.h>
409aa73b6Qin Michael Li#include <sys/strsun.h>
419aa73b6Qin Michael Li#include <sys/mac_provider.h>
42a72f7eaql#include <sys/mac_wifi.h>
439aa73b6Qin Michael Li#include <sys/net80211.h>
440dc2366Venugopal Iyer#include <sys/byteorder.h>
45a72f7eaql#include "rtwreg.h"
46a72f7eaql#include "rtwvar.h"
47a72f7eaql#include "smc93cx6var.h"
48a72f7eaql#include "rtwphy.h"
49a72f7eaql#include "rtwphyio.h"
50a72f7eaql
51a72f7eaql/*
52a72f7eaql * PIO access attributes for registers
53a72f7eaql */
54a72f7eaqlstatic ddi_device_acc_attr_t rtw_reg_accattr = {
55a72f7eaql	DDI_DEVICE_ATTR_V0,
56a72f7eaql	DDI_STRUCTURE_LE_ACC,
57a72f7eaql	DDI_STRICTORDER_ACC,
58a72f7eaql	DDI_DEFAULT_ACC
59a72f7eaql};
60a72f7eaql
61a72f7eaql/*
62a72f7eaql * DMA access attributes for descriptors and bufs: NOT to be byte swapped.
63a72f7eaql */
64a72f7eaqlstatic ddi_device_acc_attr_t rtw_desc_accattr = {
65a72f7eaql	DDI_DEVICE_ATTR_V0,
66a72f7eaql	DDI_NEVERSWAP_ACC,
67a72f7eaql	DDI_STRICTORDER_ACC,
68a72f7eaql	DDI_DEFAULT_ACC
69a72f7eaql};
70a72f7eaqlstatic ddi_device_acc_attr_t rtw_buf_accattr = {
71a72f7eaql	DDI_DEVICE_ATTR_V0,
72a72f7eaql	DDI_NEVERSWAP_ACC,
73a72f7eaql	DDI_STRICTORDER_ACC,
74a72f7eaql	DDI_DEFAULT_ACC
75a72f7eaql};
76a72f7eaql
77a72f7eaql/*
78a72f7eaql * Describes the chip's DMA engine
79a72f7eaql */
80a72f7eaqlstatic ddi_dma_attr_t dma_attr_desc = {
81a72f7eaql	DMA_ATTR_V0,			/* dma_attr version */
82a72f7eaql	0x0000000000000000ull,		/* dma_attr_addr_lo */
83020c477ql	0xFFFFFFFF,			/* dma_attr_addr_hi */
84a72f7eaql	0x00000000FFFFFFFFull,		/* dma_attr_count_max */
85a72f7eaql	0x100,				/* dma_attr_align */
86a72f7eaql	0xFFFFFFFF,			/* dma_attr_burstsizes */
87a72f7eaql	0x00000001,			/* dma_attr_minxfer */
88a72f7eaql	0x00000000FFFFull,		/* dma_attr_maxxfer */
89a72f7eaql	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_seg */
90a72f7eaql	1,				/* dma_attr_sgllen */
91a72f7eaql	1,				/* dma_attr_granular */
92a72f7eaql	0				/* dma_attr_flags */
93a72f7eaql};
94a72f7eaql
95a72f7eaqlstatic ddi_dma_attr_t dma_attr_rxbuf = {
96a72f7eaql	DMA_ATTR_V0,			/* dma_attr version */
97a72f7eaql	0x0000000000000000ull,		/* dma_attr_addr_lo */
98020c477ql	0xFFFFFFFF,			/* dma_attr_addr_hi */
99a72f7eaql	0x00000000FFFFFFFFull,		/* dma_attr_count_max */
100a72f7eaql	(uint32_t)16,			/* dma_attr_align */
101a72f7eaql	0xFFFFFFFF,			/* dma_attr_burstsizes */
102a72f7eaql	0x00000001,			/* dma_attr_minxfer */
103a72f7eaql	0x00000000FFFFull,		/* dma_attr_maxxfer */
104a72f7eaql	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_seg */
105a72f7eaql	1,				/* dma_attr_sgllen */
106a72f7eaql	1,				/* dma_attr_granular */
107a72f7eaql	0				/* dma_attr_flags */
108a72f7eaql};
109a72f7eaql
110a72f7eaqlstatic ddi_dma_attr_t dma_attr_txbuf = {
111a72f7eaql	DMA_ATTR_V0,			/* dma_attr version */
112a72f7eaql	0x0000000000000000ull,		/* dma_attr_addr_lo */
113020c477ql	0xFFFFFFFF,			/* dma_attr_addr_hi */
114a72f7eaql	0x00000000FFFFFFFFull,		/* dma_attr_count_max */
115a72f7eaql	(uint32_t)16,			/* dma_attr_align */
116a72f7eaql	0xFFFFFFFF,			/* dma_attr_burstsizes */
117a72f7eaql	0x00000001,			/* dma_attr_minxfer */
118a72f7eaql	0x00000000FFFFull,		/* dma_attr_maxxfer */
119a72f7eaql	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_seg */
120a72f7eaql	1,				/* dma_attr_sgllen */
121a72f7eaql	1,				/* dma_attr_granular */
122a72f7eaql	0				/* dma_attr_flags */
123a72f7eaql};
124a72f7eaql
125a72f7eaql
126a72f7eaqlstatic void *rtw_soft_state_p = NULL;
127a72f7eaql
1289aa73b6Qin Michael Listatic void	rtw_stop(void *);
1299aa73b6Qin Michael Listatic int	rtw_attach(dev_info_t *, ddi_attach_cmd_t);
1309aa73b6Qin Michael Listatic int	rtw_detach(dev_info_t *, ddi_detach_cmd_t);
1319aa73b6Qin Michael Listatic int	rtw_quiesce(dev_info_t *);
132a72f7eaqlstatic int	rtw_m_stat(void *,  uint_t, uint64_t *);
133a72f7eaqlstatic int	rtw_m_start(void *);
134a72f7eaqlstatic void	rtw_m_stop(void *);
135a72f7eaqlstatic int	rtw_m_promisc(void *, boolean_t);
136a72f7eaqlstatic int	rtw_m_multicst(void *, boolean_t, const uint8_t *);
137a72f7eaqlstatic int	rtw_m_unicst(void *, const uint8_t *);
138a72f7eaqlstatic mblk_t	*rtw_m_tx(void *, mblk_t *);
139a72f7eaqlstatic void	rtw_m_ioctl(void *, queue_t *, mblk_t *);
14094d05f6Qin Michael Listatic int	rtw_m_setprop(void *, const char *, mac_prop_id_t,
14194d05f6Qin Michael Li    uint_t, const void *);
14294d05f6Qin Michael Listatic int	rtw_m_getprop(void *, const char *, mac_prop_id_t,
1430dc2366Venugopal Iyer    uint_t, void *);
1440dc2366Venugopal Iyerstatic void	rtw_m_propinfo(void *, const char *, mac_prop_id_t,
1450dc2366Venugopal Iyer    mac_prop_info_handle_t);
14694d05f6Qin Michael Li
147a72f7eaqlstatic mac_callbacks_t rtw_m_callbacks = {
1480dc2366Venugopal Iyer	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
149a72f7eaql	rtw_m_stat,
150a72f7eaql	rtw_m_start,
151a72f7eaql	rtw_m_stop,
152a72f7eaql	rtw_m_promisc,
153a72f7eaql	rtw_m_multicst,
154a72f7eaql	rtw_m_unicst,
155a72f7eaql	rtw_m_tx,
1560dc2366Venugopal Iyer	NULL,
15794d05f6Qin Michael Li	rtw_m_ioctl,
15894d05f6Qin Michael Li	NULL,		/* mc_getcapab */
15994d05f6Qin Michael Li	NULL,
16094d05f6Qin Michael Li	NULL,
16194d05f6Qin Michael Li	rtw_m_setprop,
1620dc2366Venugopal Iyer	rtw_m_getprop,
1630dc2366Venugopal Iyer	rtw_m_propinfo
164a72f7eaql};
165a72f7eaql
166a72f7eaqlDDI_DEFINE_STREAM_OPS(rtw_dev_ops, nulldev, nulldev, rtw_attach, rtw_detach,
16794d05f6Qin Michael Li    nodev, NULL, D_MP, NULL, rtw_quiesce);
168a72f7eaql
169a72f7eaqlstatic struct modldrv rtw_modldrv = {
170a72f7eaql	&mod_driverops,		/* Type of module.  This one is a driver */
1719aa73b6Qin Michael Li	"realtek 8180L driver 1.7",	/* short description */
172a72f7eaql	&rtw_dev_ops		/* driver specific ops */
173a72f7eaql};
174a72f7eaql
175a72f7eaqlstatic struct modlinkage modlinkage = {
176a72f7eaql	MODREV_1, (void *)&rtw_modldrv, NULL
177a72f7eaql};
178a72f7eaql
179a72f7eaqlstatic uint32_t rtw_qlen[RTW_NTXPRI] = {
180a72f7eaql	RTW_TXQLENLO,
181a72f7eaql	RTW_TXQLENMD,
182a72f7eaql	RTW_TXQLENHI,
183a72f7eaql	RTW_TXQLENBCN
184a72f7eaql};
185a72f7eaql
186020c477qluint32_t rtw_dbg_flags = 0;
187a72f7eaql	/*
188a72f7eaql	 * RTW_DEBUG_ATTACH | RTW_DEBUG_TUNE |
189a72f7eaql	 * RTW_DEBUG_ACCESS | RTW_DEBUG_INIT | RTW_DEBUG_PKTFILT |
190a72f7eaql	 * RTW_DEBUG_RECV | RTW_DEBUG_XMIT | RTW_DEBUG_80211 | RTW_DEBUG_INTR |
191a72f7eaql	 * RTW_DEBUG_PKTDUMP;
192a72f7eaql	 */
193a72f7eaql
19494d05f6Qin Michael Li/*
19594d05f6Qin Michael Li * Supported rates for 802.11b modes (in 500Kbps unit).
19694d05f6Qin Michael Li */
19794d05f6Qin Michael Listatic const struct ieee80211_rateset rtw_rateset_11b =
19894d05f6Qin Michael Li	{ 4, { 2, 4, 11, 22 } };
19994d05f6Qin Michael Li
200a72f7eaqlint
201a72f7eaql_info(struct modinfo *modinfop)
202a72f7eaql{
203a72f7eaql	return (mod_info(&modlinkage, modinfop));
204a72f7eaql}
205a72f7eaql
206a72f7eaqlint
207a72f7eaql_init(void)
208a72f7eaql{
209a72f7eaql	int status;
210a72f7eaql
211a72f7eaql	status = ddi_soft_state_init(&rtw_soft_state_p,
212a72f7eaql	    sizeof (rtw_softc_t), 1);
213a72f7eaql	if (status != 0)
214a72f7eaql		return (status);
215a72f7eaql
216a72f7eaql	mac_init_ops(&rtw_dev_ops, "rtw");
217a72f7eaql	status = mod_install(&modlinkage);
218a72f7eaql	if (status != 0) {
219a72f7eaql		mac_fini_ops(&rtw_dev_ops);
220a72f7eaql		ddi_soft_state_fini(&rtw_soft_state_p);
221a72f7eaql	}
222a72f7eaql	return (status);
223a72f7eaql}
224a72f7eaql
225a72f7eaqlint
226a72f7eaql_fini(void)
227a72f7eaql{
228a72f7eaql	int status;
229a72f7eaql
230a72f7eaql	status = mod_remove(&modlinkage);
231a72f7eaql	if (status == 0) {
232a72f7eaql		mac_fini_ops(&rtw_dev_ops);
233a72f7eaql		ddi_soft_state_fini(&rtw_soft_state_p);
234a72f7eaql	}
235a72f7eaql	return (status);
236a72f7eaql}
237a72f7eaql
238a72f7eaqlvoid
239a72f7eaqlrtw_dbg(uint32_t dbg_flags, const int8_t *fmt, ...)
240a72f7eaql{
241a72f7eaql	va_list args;
242a72f7eaql
243a72f7eaql	if (dbg_flags & rtw_dbg_flags) {
244a72f7eaql		va_start(args, fmt);
245a72f7eaql		vcmn_err(CE_CONT, fmt, args);
246a72f7eaql		va_end(args);
247a72f7eaql	}
248a72f7eaql}
249a72f7eaql
250a72f7eaql#ifdef DEBUG
251a72f7eaqlstatic void
252a72f7eaqlrtw_print_regs(struct rtw_regs *regs, const char *dvname, const char *where)
253a72f7eaql{
254a72f7eaql#define	PRINTREG32(sc, reg)				\
255a72f7eaql	RTW_DPRINTF(RTW_DEBUG_REGDUMP,			\
256a72f7eaql	    "%s: reg[ " #reg " / %03x ] = %08x\n",	\
257a72f7eaql	    dvname, reg, RTW_READ(regs, reg))
258a72f7eaql
259a72f7eaql#define	PRINTREG16(sc, reg)				\
260a72f7eaql	RTW_DPRINTF(RTW_DEBUG_REGDUMP,			\
261a72f7eaql	    "%s: reg[ " #reg " / %03x ] = %04x\n",	\
262a72f7eaql	    dvname, reg, RTW_READ16(regs, reg))
263a72f7eaql
264a72f7eaql#define	PRINTREG8(sc, reg)				\
265a72f7eaql	RTW_DPRINTF(RTW_DEBUG_REGDUMP,			\
266a72f7eaql	    "%s: reg[ " #reg " / %03x ] = %02x\n",	\
267a72f7eaql	    dvname, reg, RTW_READ8(regs, reg))
268a72f7eaql
269a72f7eaql	RTW_DPRINTF(RTW_DEBUG_REGDUMP, "%s: %s\n", dvname, where);
270a72f7eaql
271a72f7eaql	PRINTREG32(regs, RTW_IDR0);
272a72f7eaql	PRINTREG32(regs, RTW_IDR1);
273a72f7eaql	PRINTREG32(regs, RTW_MAR0);
274a72f7eaql	PRINTREG32(regs, RTW_MAR1);
275a72f7eaql	PRINTREG32(regs, RTW_TSFTRL);
276a72f7eaql	PRINTREG32(regs, RTW_TSFTRH);
277a72f7eaql	PRINTREG32(regs, RTW_TLPDA);
278a72f7eaql	PRINTREG32(regs, RTW_TNPDA);
279a72f7eaql	PRINTREG32(regs, RTW_THPDA);
280a72f7eaql	PRINTREG32(regs, RTW_TCR);
281a72f7eaql	PRINTREG32(regs, RTW_RCR);
282a72f7eaql	PRINTREG32(regs, RTW_TINT);
283a72f7eaql	PRINTREG32(regs, RTW_TBDA);
284a72f7eaql	PRINTREG32(regs, RTW_ANAPARM);
285a72f7eaql	PRINTREG32(regs, RTW_BB);
286a72f7eaql	PRINTREG32(regs, RTW_PHYCFG);
287a72f7eaql	PRINTREG32(regs, RTW_WAKEUP0L);
288a72f7eaql	PRINTREG32(regs, RTW_WAKEUP0H);
289a72f7eaql	PRINTREG32(regs, RTW_WAKEUP1L);
290a72f7eaql	PRINTREG32(regs, RTW_WAKEUP1H);
291a72f7eaql	PRINTREG32(regs, RTW_WAKEUP2LL);
292a72f7eaql	PRINTREG32(regs, RTW_WAKEUP2LH);
293a72f7eaql	PRINTREG32(regs, RTW_WAKEUP2HL);
294a72f7eaql	PRINTREG32(regs, RTW_WAKEUP2HH);
295a72f7eaql	PRINTREG32(regs, RTW_WAKEUP3LL);
296a72f7eaql	PRINTREG32(regs, RTW_WAKEUP3LH);
297a72f7eaql	PRINTREG32(regs, RTW_WAKEUP3HL);
298a72f7eaql	PRINTREG32(regs, RTW_WAKEUP3HH);
299a72f7eaql	PRINTREG32(regs, RTW_WAKEUP4LL);
300a72f7eaql	PRINTREG32(regs, RTW_WAKEUP4LH);
301a72f7eaql	PRINTREG32(regs, RTW_WAKEUP4HL);
302a72f7eaql	PRINTREG32(regs, RTW_WAKEUP4HH);
303a72f7eaql	PRINTREG32(regs, RTW_DK0);
304a72f7eaql	PRINTREG32(regs, RTW_DK1);
305a72f7eaql	PRINTREG32(regs, RTW_DK2);
306a72f7eaql	PRINTREG32(regs, RTW_DK3);
307a72f7eaql	PRINTREG32(regs, RTW_RETRYCTR);
308a72f7eaql	PRINTREG32(regs, RTW_RDSAR);
309a72f7eaql	PRINTREG32(regs, RTW_FER);
310a72f7eaql	PRINTREG32(regs, RTW_FEMR);
311a72f7eaql	PRINTREG32(regs, RTW_FPSR);
312a72f7eaql	PRINTREG32(regs, RTW_FFER);
313a72f7eaql
314a72f7eaql	/* 16-bit registers */
315a72f7eaql	PRINTREG16(regs, RTW_BRSR);
316a72f7eaql	PRINTREG16(regs, RTW_IMR);
317a72f7eaql	PRINTREG16(regs, RTW_ISR);
318a72f7eaql	PRINTREG16(regs, RTW_BCNITV);
319a72f7eaql	PRINTREG16(regs, RTW_ATIMWND);
320a72f7eaql	PRINTREG16(regs, RTW_BINTRITV);
321a72f7eaql	PRINTREG16(regs, RTW_ATIMTRITV);
322a72f7eaql	PRINTREG16(regs, RTW_CRC16ERR);
323a72f7eaql	PRINTREG16(regs, RTW_CRC0);
324a72f7eaql	PRINTREG16(regs, RTW_CRC1);
325a72f7eaql	PRINTREG16(regs, RTW_CRC2);
326a72f7eaql	PRINTREG16(regs, RTW_CRC3);
327a72f7eaql	PRINTREG16(regs, RTW_CRC4);
328a72f7eaql	PRINTREG16(regs, RTW_CWR);
329a72f7eaql
330a72f7eaql	/* 8-bit registers */
331a72f7eaql	PRINTREG8(regs, RTW_CR);
332a72f7eaql	PRINTREG8(regs, RTW_9346CR);
333a72f7eaql	PRINTREG8(regs, RTW_CONFIG0);
334a72f7eaql	PRINTREG8(regs, RTW_CONFIG1);
335a72f7eaql	PRINTREG8(regs, RTW_CONFIG2);
336a72f7eaql	PRINTREG8(regs, RTW_MSR);
337a72f7eaql	PRINTREG8(regs, RTW_CONFIG3);
338a72f7eaql	PRINTREG8(regs, RTW_CONFIG4);
339a72f7eaql	PRINTREG8(regs, RTW_TESTR);
340a72f7eaql	PRINTREG8(regs, RTW_PSR);
341a72f7eaql	PRINTREG8(regs, RTW_SCR);
342a72f7eaql	PRINTREG8(regs, RTW_PHYDELAY);
343a72f7eaql	PRINTREG8(regs, RTW_CRCOUNT);
344a72f7eaql	PRINTREG8(regs, RTW_PHYADDR);
345a72f7eaql	PRINTREG8(regs, RTW_PHYDATAW);
346a72f7eaql	PRINTREG8(regs, RTW_PHYDATAR);
347a72f7eaql	PRINTREG8(regs, RTW_CONFIG5);
348a72f7eaql	PRINTREG8(regs, RTW_TPPOLL);
349a72f7eaql
350a72f7eaql	PRINTREG16(regs, RTW_BSSID16);
351a72f7eaql	PRINTREG32(regs, RTW_BSSID32);
352a72f7eaql#undef PRINTREG32
353a72f7eaql#undef PRINTREG16
354a72f7eaql#undef PRINTREG8
355a72f7eaql}
356a72f7eaql
357a72f7eaql#endif /* DEBUG */
358a72f7eaqlstatic const char *
359a72f7eaqlrtw_access_string(enum rtw_access access)
360a72f7eaql{
361a72f7eaql	switch (access) {
362a72f7eaql	case RTW_ACCESS_NONE:
363a72f7eaql		return ("none");
364a72f7eaql	case RTW_ACCESS_CONFIG:
365a72f7eaql		return ("config");
366a72f7eaql	case RTW_ACCESS_ANAPARM:
367a72f7eaql		return ("anaparm");
368a72f7eaql	default:
369a72f7eaql		return ("unknown");
370a72f7eaql	}
371a72f7eaql}
372a72f7eaql
373a72f7eaql/*
374a72f7eaql * Enable registers, switch register banks.
375a72f7eaql */
376a72f7eaqlvoid
377a72f7eaqlrtw_config0123_enable(struct rtw_regs *regs, int enable)
378a72f7eaql{
379a72f7eaql	uint8_t ecr;
380a72f7eaql	ecr = RTW_READ8(regs, RTW_9346CR);
381a72f7eaql	ecr &= ~(RTW_9346CR_EEM_MASK | RTW_9346CR_EECS | RTW_9346CR_EESK);
382a72f7eaql	if (enable)
383a72f7eaql		ecr |= RTW_9346CR_EEM_CONFIG;
384a72f7eaql	else {
385a72f7eaql		RTW_WBW(regs, RTW_9346CR, MAX(RTW_CONFIG0, RTW_CONFIG3));
386a72f7eaql		ecr |= RTW_9346CR_EEM_NORMAL;
387a72f7eaql	}
388a72f7eaql	RTW_WRITE8(regs, RTW_9346CR, ecr);
389a72f7eaql	RTW_SYNC(regs, RTW_9346CR, RTW_9346CR);
390a72f7eaql}
391a72f7eaql
392a72f7eaql/*
393a72f7eaql * requires rtw_config0123_enable(, 1)
394a72f7eaql */
395a72f7eaqlvoid
396a72f7eaqlrtw_anaparm_enable(struct rtw_regs *regs, int enable)
397a72f7eaql{
398a72f7eaql	uint8_t cfg3;
399a72f7eaql
400a72f7eaql	cfg3 = RTW_READ8(regs, RTW_CONFIG3);
401a72f7eaql	cfg3 |= RTW_CONFIG3_CLKRUNEN;
402a72f7eaql	if (enable)
403a72f7eaql		cfg3 |= RTW_CONFIG3_PARMEN;
404a72f7eaql	else
405a72f7eaql		cfg3 &= ~RTW_CONFIG3_PARMEN;
406a72f7eaql	RTW_WRITE8(regs, RTW_CONFIG3, cfg3);
407a72f7eaql	RTW_SYNC(regs, RTW_CONFIG3, RTW_CONFIG3);
408a72f7eaql}
409a72f7eaql
410a72f7eaql/*
411a72f7eaql * requires rtw_anaparm_enable(, 1)
412a72f7eaql */
413a72f7eaqlvoid
414a72f7eaqlrtw_txdac_enable(rtw_softc_t *rsc, int enable)
415a72f7eaql{
416a72f7eaql	uint32_t anaparm;
417a72f7eaql	struct rtw_regs *regs = &rsc->sc_regs;
418a72f7eaql
419a72f7eaql	anaparm = RTW_READ(regs, RTW_ANAPARM);
420a72f7eaql	if (enable)
421a72f7eaql		anaparm &= ~RTW_ANAPARM_TXDACOFF;
422a72f7eaql	else
423a72f7eaql		anaparm |= RTW_ANAPARM_TXDACOFF;
424a72f7eaql	RTW_WRITE(regs, RTW_ANAPARM, anaparm);
425a72f7eaql	RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM);
426a72f7eaql}
427a72f7eaql
428a72f7eaqlstatic void
429a72f7eaqlrtw_set_access1(struct rtw_regs *regs, enum rtw_access naccess)
430a72f7eaql{
431a72f7eaql	ASSERT(naccess >= RTW_ACCESS_NONE && naccess <= RTW_ACCESS_ANAPARM);
432a72f7eaql	ASSERT(regs->r_access >= RTW_ACCESS_NONE &&
433a72f7eaql	    regs->r_access <= RTW_ACCESS_ANAPARM);
434a72f7eaql
435a72f7eaql	if (naccess == regs->r_access)
436a72f7eaql		return;
437a72f7eaql
438a72f7eaql	switch (naccess) {
439a72f7eaql	case RTW_ACCESS_NONE:
440a72f7eaql		switch (regs->r_access) {
441a72f7eaql		case RTW_ACCESS_ANAPARM:
442a72f7eaql			rtw_anaparm_enable(regs, 0);
443a72f7eaql			/*FALLTHROUGH*/
444a72f7eaql		case RTW_ACCESS_CONFIG:
445a72f7eaql			rtw_config0123_enable(regs, 0);
446a72f7eaql			/*FALLTHROUGH*/
447a72f7eaql		case RTW_ACCESS_NONE:
448a72f7eaql			break;
449a72f7eaql		}
450a72f7eaql		break;
451a72f7eaql	case RTW_ACCESS_CONFIG:
452a72f7eaql		switch (regs->r_access) {
453a72f7eaql		case RTW_ACCESS_NONE:
454a72f7eaql			rtw_config0123_enable(regs, 1);
455a72f7eaql			/*FALLTHROUGH*/
456a72f7eaql		case RTW_ACCESS_CONFIG:
457a72f7eaql			break;
458a72f7eaql		case RTW_ACCESS_ANAPARM:
459a72f7eaql			rtw_anaparm_enable(regs, 0);
460a72f7eaql			break;
461a72f7eaql		}
462a72f7eaql		break;
463a72f7eaql	case RTW_ACCESS_ANAPARM:
464a72f7eaql		switch (regs->r_access) {
465a72f7eaql		case RTW_ACCESS_NONE:
466a72f7eaql			rtw_config0123_enable(regs, 1);
467a72f7eaql			/*FALLTHROUGH*/
468a72f7eaql		case RTW_ACCESS_CONFIG:
469a72f7eaql			rtw_anaparm_enable(regs, 1);
470a72f7eaql			/*FALLTHROUGH*/
471a72f7eaql		case RTW_ACCESS_ANAPARM:
472a72f7eaql			break;
473a72f7eaql		}
474a72f7eaql		break;
475a72f7eaql	}
476a72f7eaql}
477a72f7eaql
478a72f7eaqlvoid
479a72f7eaqlrtw_set_access(struct rtw_regs *regs, enum rtw_access access)
480a72f7eaql{
481a72f7eaql	rtw_set_access1(regs, access);
482a72f7eaql	RTW_DPRINTF(RTW_DEBUG_ACCESS,
483a72f7eaql	    "%s: access %s -> %s\n", __func__,
484a72f7eaql	    rtw_access_string(regs->r_access),
485a72f7eaql	    rtw_access_string(access));
486a72f7eaql	regs->r_access = access;
487a72f7eaql}
488a72f7eaql
489a72f7eaql
490a72f7eaqlvoid
491a72f7eaqlrtw_continuous_tx_enable(rtw_softc_t *rsc, int enable)
492a72f7eaql{
493a72f7eaql	struct rtw_regs *regs = &rsc->sc_regs;
494a72f7eaql
495a72f7eaql	uint32_t tcr;
496a72f7eaql	tcr = RTW_READ(regs, RTW_TCR);
497a72f7eaql	tcr &= ~RTW_TCR_LBK_MASK;
498a72f7eaql	if (enable)
499a72f7eaql		tcr |= RTW_TCR_LBK_CONT;
500a72f7eaql	else
501a72f7eaql		tcr |= RTW_TCR_LBK_NORMAL;
502a72f7eaql	RTW_WRITE(regs, RTW_TCR, tcr);
503a72f7eaql	RTW_SYNC(regs, RTW_TCR, RTW_TCR);
504a72f7eaql	rtw_set_access(regs, RTW_ACCESS_ANAPARM);
505a72f7eaql	rtw_txdac_enable(rsc, !enable);
506a72f7eaql	rtw_set_access(regs, RTW_ACCESS_ANAPARM);
507a72f7eaql	rtw_set_access(regs, RTW_ACCESS_NONE);
508a72f7eaql}
509a72f7eaql
510a72f7eaqlstatic int
511a72f7eaqlrtw_chip_reset1(struct rtw_regs *regs, const char *dvname)
512a72f7eaql{
513a72f7eaql	uint8_t cr;
514a72f7eaql	int i;
515a72f7eaql
516a72f7eaql	RTW_WRITE8(regs, RTW_CR, RTW_CR_RST);
517a72f7eaql
518a72f7eaql	RTW_WBR(regs, RTW_CR, RTW_CR);
519a72f7eaql
520a72f7eaql	for (i = 0; i < 1000; i++) {
521a72f7eaql		cr = RTW_READ8(regs, RTW_CR);
522a72f7eaql		if ((cr & RTW_CR_RST) == 0) {
523a72f7eaql			RTW_DPRINTF(RTW_DEBUG_RESET,
524a72f7eaql			    "%s: reset in %dus\n", dvname, i);
525a72f7eaql			return (0);
526a72f7eaql		}
527a72f7eaql		RTW_RBR(regs, RTW_CR, RTW_CR);
528a72f7eaql		DELAY(10); /* 10us */
529a72f7eaql	}
530a72f7eaql
531a72f7eaql	cmn_err(CE_WARN, "%s: reset failed\n", dvname);
532a72f7eaql	return (ETIMEDOUT);
533a72f7eaql}
534a72f7eaql
535a72f7eaqlstatic int
536a72f7eaqlrtw_chip_reset(struct rtw_regs *regs, const char *dvname)
537a72f7eaql{
538a72f7eaql	RTW_WBW(regs, RTW_CR, RTW_TCR);
539a72f7eaql	return (rtw_chip_reset1(regs, dvname));
540a72f7eaql}
541a72f7eaql
542a72f7eaqlstatic void
543a72f7eaqlrtw_disable_interrupts(struct rtw_regs *regs)
544a72f7eaql{
545a72f7eaql	RTW_WRITE16(regs, RTW_IMR, 0);
546a72f7eaql	RTW_WRITE16(regs, RTW_ISR, 0xffff);
547a72f7eaql	(void) RTW_READ16(regs, RTW_IMR);
548a72f7eaql}
549a72f7eaql
550a72f7eaqlstatic void
551a72f7eaqlrtw_enable_interrupts(rtw_softc_t *rsc)
552a72f7eaql{
553a72f7eaql	struct rtw_regs *regs = &rsc->sc_regs;
554a72f7eaql
555a72f7eaql	rsc->sc_inten = RTW_INTR_RX | RTW_INTR_TX | RTW_INTR_IOERROR;
556a72f7eaql
557a72f7eaql	RTW_WRITE16(regs, RTW_IMR, rsc->sc_inten);
558a72f7eaql	RTW_WRITE16(regs, RTW_ISR, 0xffff);
559a72f7eaql
560a72f7eaql	/* XXX necessary? */
561a72f7eaql	if (rsc->sc_intr_ack != NULL)
562a72f7eaql		(*rsc->sc_intr_ack)(regs);
563a72f7eaql}
564a72f7eaql
565a72f7eaqlstatic int
566a72f7eaqlrtw_recall_eeprom(struct rtw_regs *regs, const char *dvname)
567a72f7eaql{
568a72f7eaql	int i;
569a72f7eaql	uint8_t ecr;
570a72f7eaql
571a72f7eaql	ecr = RTW_READ8(regs, RTW_9346CR);
572a72f7eaql	ecr = (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_AUTOLOAD;
573a72f7eaql	RTW_WRITE8(regs, RTW_9346CR, ecr);
574a72f7eaql
575a72f7eaql	RTW_WBR(regs, RTW_9346CR, RTW_9346CR);
576a72f7eaql
577a72f7eaql	/* wait 25ms for completion */
578a72f7eaql	for (i = 0; i < 250; i++) {
579a72f7eaql		ecr = RTW_READ8(regs, RTW_9346CR);
580a72f7eaql		if ((ecr & RTW_9346CR_EEM_MASK) == RTW_9346CR_EEM_NORMAL) {
581a72f7eaql			RTW_DPRINTF(RTW_DEBUG_RESET,
582a72f7eaql			    "%s: recall EEPROM in %dus\n", dvname, i * 100);
583a72f7eaql			return (0);
584a72f7eaql		}
585a72f7eaql		RTW_RBR(regs, RTW_9346CR, RTW_9346CR);
586a72f7eaql		DELAY(100);
587a72f7eaql	}
588a72f7eaql	cmn_err(CE_WARN, "%s: recall EEPROM failed\n", dvname);
589a72f7eaql	return (ETIMEDOUT);
590a72f7eaql}
591a72f7eaql
592a72f7eaqlstatic int
593a72f7eaqlrtw_reset(rtw_softc_t *rsc)
594a72f7eaql{
595a72f7eaql	int rc;
596a72f7eaql
597a72f7eaql	rc = rtw_chip_reset(&rsc->sc_regs, "rtw");
598a72f7eaql	if (rc != 0)
599a72f7eaql		return (rc);
600a72f7eaql
601a72f7eaql	(void) rtw_recall_eeprom(&rsc->sc_regs, "rtw");
602a72f7eaql	return (0);
603a72f7eaql}
604a72f7eaql
605a72f7eaqlvoid
606a72f7eaqlrtw_set_mode(struct rtw_regs *regs, int mode)
607a72f7eaql{
608a72f7eaql	uint8_t command;
609a72f7eaql	command = RTW_READ8(regs, RTW_9346CR);
610a72f7eaql	command = command &~ RTW_EPROM_CMD_OPERATING_MODE_MASK;
611a72f7eaql	command = command | (mode<<RTW_EPROM_CMD_OPERATING_MODE_SHIFT);
612a72f7eaql	command = command &~ (1<<RTW_EPROM_CS_SHIFT);
613a72f7eaql	command = command &~ (1<<RTW_EPROM_CK_SHIFT);
614a72f7eaql	RTW_WRITE8(regs, RTW_9346CR, command);
615a72f7eaql}
616a72f7eaql
617a72f7eaqlvoid
618a72f7eaqlrtw_dma_start(struct rtw_regs *regs, int priority)
619a72f7eaql{
620a72f7eaql	uint8_t check = 0;
621a72f7eaql
622a72f7eaql	check = RTW_READ8(regs, RTW_TPPOLL);
623a72f7eaql	switch (priority) {
624a72f7eaql	case (0):
625a72f7eaql		RTW_WRITE8(regs, RTW_TPPOLL,
626a72f7eaql		    (1<< RTW_TX_DMA_POLLING_LOWPRIORITY_SHIFT) | check);
627a72f7eaql		break;
628a72f7eaql	case (1):
629a72f7eaql		RTW_WRITE8(regs, RTW_TPPOLL,
630a72f7eaql		    (1<< RTW_TX_DMA_POLLING_NORMPRIORITY_SHIFT) | check);
631a72f7eaql		break;
632a72f7eaql	case (2):
633a72f7eaql		RTW_WRITE8(regs, RTW_TPPOLL,
634a72f7eaql		    (1<< RTW_TX_DMA_POLLING_HIPRIORITY_SHIFT) | check);
635a72f7eaql		break;
636a72f7eaql	}
637a72f7eaql	(void) RTW_READ8(regs, RTW_TPPOLL);
638a72f7eaql}
639a72f7eaql
640a72f7eaqlvoid
641a72f7eaqlrtw_beacon_tx_disable(struct rtw_regs *regs)
642a72f7eaql{
643a72f7eaql	uint8_t mask = 0;
644a72f7eaql	mask |= (1 << RTW_TX_DMA_STOP_BEACON_SHIFT);
645a72f7eaql	rtw_set_mode(regs, RTW_EPROM_CMD_CONFIG);
646a72f7eaql	RTW_WRITE8(regs, RTW_TPPOLL, mask);
647a72f7eaql	rtw_set_mode(regs, RTW_EPROM_CMD_NORMAL);
648a72f7eaql}
649a72f7eaql
650a72f7eaqlstatic void
651a72f7eaqlrtw_io_enable(rtw_softc_t *rsc, uint8_t flags, int enable);
652a72f7eaql
653a72f7eaqlvoid
654a72f7eaqlrtw_rtx_disable(rtw_softc_t *rsc)
655a72f7eaql{
656a72f7eaql	struct rtw_regs *regs = &rsc->sc_regs;
657a72f7eaql
658a72f7eaql	rtw_io_enable(rsc, RTW_CR_RE|RTW_CR_TE, 0);
659a72f7eaql	(void) RTW_READ8(regs, RTW_CR);
660a72f7eaql}
661a72f7eaql
662a72f7eaqlstatic void
663a72f7eaqlrtw_srom_free(struct rtw_srom *sr)
664a72f7eaql{
665a72f7eaql	if (sr->sr_content == NULL)
666a72f7eaql		return;
667a72f7eaql	kmem_free(sr->sr_content, sr->sr_size);
668a72f7eaql	sr->sr_size = 0;
669a72f7eaql	sr->sr_content = NULL;
670a72f7eaql}
671a72f7eaql
672a72f7eaql/*ARGSUSED*/
673a72f7eaqlstatic void
674a72f7eaqlrtw_srom_defaults(struct rtw_srom *sr, uint32_t *flags, uint8_t *cs_threshold,
675a72f7eaql    enum rtw_rfchipid *rfchipid, uint32_t *rcr)
676a72f7eaql{
677a72f7eaql	*flags |= (RTW_F_DIGPHY|RTW_F_ANTDIV);
678a72f7eaql	*cs_threshold = RTW_SR_ENERGYDETTHR_DEFAULT;
679a72f7eaql	*rcr |= RTW_RCR_ENCS1;
680a72f7eaql	*rfchipid = RTW_RFCHIPID_PHILIPS;
681a72f7eaql}
682a72f7eaql
683a72f7eaqlstatic int
684a72f7eaqlrtw_srom_parse(struct rtw_srom *sr, uint32_t *flags, uint8_t *cs_threshold,
685a72f7eaql    enum rtw_rfchipid *rfchipid, uint32_t *rcr, enum rtw_locale *locale,
686a72f7eaql    const char *dvname)
687a72f7eaql{
688a72f7eaql	int i;
689a72f7eaql	const char *rfname, *paname;
690a72f7eaql	char scratch[sizeof ("unknown 0xXX")];
691a72f7eaql	uint16_t version;
692a72f7eaql	uint8_t mac[IEEE80211_ADDR_LEN];
693a72f7eaql
694a72f7eaql	*flags &= ~(RTW_F_DIGPHY|RTW_F_DFLANTB|RTW_F_ANTDIV);
695a72f7eaql	*rcr &= ~(RTW_RCR_ENCS1 | RTW_RCR_ENCS2);
696a72f7eaql
697a72f7eaql	version = RTW_SR_GET16(sr, RTW_SR_VERSION);
698a72f7eaql	RTW_DPRINTF(RTW_DEBUG_IOSTATE, "%s: SROM version %d.%d", dvname,
699a72f7eaql	    version >> 8, version & 0xff);
700a72f7eaql
701a72f7eaql	if (version <= 0x0101) {
702a72f7eaql		cmn_err(CE_NOTE, " is not understood, limping along "
703a72f7eaql		    "with defaults\n");
704a72f7eaql		rtw_srom_defaults(sr, flags, cs_threshold, rfchipid, rcr);
705a72f7eaql		return (0);
706a72f7eaql	}
707a72f7eaql
708a72f7eaql	for (i = 0; i < IEEE80211_ADDR_LEN; i++)
709a72f7eaql		mac[i] = RTW_SR_GET(sr, RTW_SR_MAC + i);
710a72f7eaql
711a72f7eaql	RTW_DPRINTF(RTW_DEBUG_ATTACH,
712a72f7eaql	    "%s: EEPROM MAC %s\n", dvname, mac);
713a72f7eaql
714a72f7eaql	*cs_threshold = RTW_SR_GET(sr, RTW_SR_ENERGYDETTHR);
715a72f7eaql
716a72f7eaql	if ((RTW_SR_GET(sr, RTW_SR_CONFIG2) & RTW_CONFIG2_ANT) != 0)
717a72f7eaql		*flags |= RTW_F_ANTDIV;
718a72f7eaql
719a72f7eaql	/*
720a72f7eaql	 * Note well: the sense of the RTW_SR_RFPARM_DIGPHY bit seems
721a72f7eaql	 * to be reversed.
722a72f7eaql	 */
723a72f7eaql	if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DIGPHY) == 0)
724a72f7eaql		*flags |= RTW_F_DIGPHY;
725a72f7eaql	if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DFLANTB) != 0)
726a72f7eaql		*flags |= RTW_F_DFLANTB;
727a72f7eaql
728a72f7eaql	*rcr |= LSHIFT(MASK_AND_RSHIFT(RTW_SR_GET(sr, RTW_SR_RFPARM),
729a72f7eaql	    RTW_SR_RFPARM_CS_MASK), RTW_RCR_ENCS1);
730a72f7eaql
731a72f7eaql	*rfchipid = RTW_SR_GET(sr, RTW_SR_RFCHIPID);
732a72f7eaql	switch (*rfchipid) {
733a72f7eaql	case RTW_RFCHIPID_GCT:		/* this combo seen in the wild */
734a72f7eaql		rfname = "GCT GRF5101";
735a72f7eaql		paname = "Winspring WS9901";
736a72f7eaql		break;
737a72f7eaql	case RTW_RFCHIPID_MAXIM:
738a72f7eaql		rfname = "Maxim MAX2820";	/* guess */
739a72f7eaql		paname = "Maxim MAX2422";	/* guess */
740a72f7eaql		break;
741a72f7eaql	case RTW_RFCHIPID_INTERSIL:
742a72f7eaql		rfname = "Intersil HFA3873";	/* guess */
743a72f7eaql		paname = "Intersil <unknown>";
744a72f7eaql		break;
745a72f7eaql	case RTW_RFCHIPID_PHILIPS:	/* this combo seen in the wild */
746a72f7eaql		rfname = "Philips SA2400A";
747a72f7eaql		paname = "Philips SA2411";
748a72f7eaql		break;
749a72f7eaql	case RTW_RFCHIPID_RFMD:
750a72f7eaql		/*
751a72f7eaql		 * this is the same front-end as an atw(4)!
752a72f7eaql		 */
753a72f7eaql		rfname = "RFMD RF2948B, "	/* mentioned in Realtek docs */
754a72f7eaql		    "LNA: RFMD RF2494, "	/* mentioned in Realtek docs */
755a72f7eaql		    "SYN: Silicon Labs Si4126";
756a72f7eaql		paname = "RFMD RF2189";		/* mentioned in Realtek docs */
757a72f7eaql		break;
758a72f7eaql	case RTW_RFCHIPID_RESERVED:
759a72f7eaql		rfname = paname = "reserved";
760a72f7eaql		break;
761a72f7eaql	default:
762a72f7eaql		(void) snprintf(scratch, sizeof (scratch),
763a72f7eaql		    "unknown 0x%02x", *rfchipid);
764a72f7eaql		rfname = paname = scratch;
765a72f7eaql	}
766a72f7eaql	RTW_DPRINTF(RTW_DEBUG_PHY, "%s: RF: %s, PA: %s\n",
767a72f7eaql	    dvname, rfname, paname);
768a72f7eaql
769a72f7eaql	switch (RTW_SR_GET(sr, RTW_SR_CONFIG0) & RTW_CONFIG0_GL_MASK) {
770a72f7eaql	case RTW_CONFIG0_GL_USA:
771a72f7eaql		*locale = RTW_LOCALE_USA;
772a72f7eaql		break;
773a72f7eaql	case RTW_CONFIG0_GL_EUROPE:
774a72f7eaql		*locale = RTW_LOCALE_EUROPE;
775a72f7eaql		break;
776a72f7eaql	case RTW_CONFIG0_GL_JAPAN:
777a72f7eaql		*locale = RTW_LOCALE_JAPAN;
778a72f7eaql		break;
779a72f7eaql	default:
780a72f7eaql		*locale = RTW_LOCALE_UNKNOWN;
781a72f7eaql		break;
782a72f7eaql	}
783a72f7eaql	return (0);
784a72f7eaql}
785a72f7eaql
786a72f7eaql/*
787a72f7eaql * Returns -1 on failure.
788a72f7eaql */
789a72f7eaqlstatic int
790a72f7eaqlrtw_srom_read(struct rtw_regs *regs, uint32_t flags, struct rtw_srom *sr,
791a72f7eaql    const char *dvname)
792a72f7eaql{
793a72f7eaql	int rc;
794a72f7eaql	struct seeprom_descriptor sd;
795a72f7eaql	uint8_t ecr;
796a72f7eaql
797a72f7eaql	(void) memset(&sd, 0, sizeof (sd));
798a72f7eaql
799a72f7eaql	ecr = RTW_READ8(regs, RTW_9346CR);
800a72f7eaql
801a72f7eaql	if ((flags & RTW_F_9356SROM) != 0) {
802a72f7eaql		RTW_DPRINTF(RTW_DEBUG_ATTACH, "%s: 93c56 SROM\n", dvname);
803a72f7eaql		sr->sr_size = 256;
804a72f7eaql		sd.sd_chip = C56_66;
805a72f7eaql	} else {
806a72f7eaql		RTW_DPRINTF(RTW_DEBUG_ATTACH, "%s: 93c46 SROM\n", dvname);
807a72f7eaql		sr->sr_size = 128;
808a72f7eaql		sd.sd_chip = C46;
809a72f7eaql	}
810a72f7eaql
811a72f7eaql	ecr &= ~(RTW_9346CR_EEDI | RTW_9346CR_EEDO | RTW_9346CR_EESK |
812a72f7eaql	    RTW_9346CR_EEM_MASK | RTW_9346CR_EECS);
813a72f7eaql	ecr |= RTW_9346CR_EEM_PROGRAM;
814a72f7eaql
815a72f7eaql	RTW_WRITE8(regs, RTW_9346CR, ecr);
816a72f7eaql
817a72f7eaql	sr->sr_content = kmem_zalloc(sr->sr_size, KM_SLEEP);
818a72f7eaql
819a72f7eaql	if (sr->sr_content == NULL) {
820a72f7eaql		cmn_err(CE_WARN, "%s: unable to allocate SROM buffer\n",
821a72f7eaql		    dvname);
822a72f7eaql		return (ENOMEM);
823a72f7eaql	}
824a72f7eaql
825a72f7eaql	(void) memset(sr->sr_content, 0, sr->sr_size);
826a72f7eaql
827a72f7eaql	/*
828a72f7eaql	 * RTL8180 has a single 8-bit register for controlling the
829a72f7eaql	 * 93cx6 SROM.  There is no "ready" bit. The RTL8180
830a72f7eaql	 * input/output sense is the reverse of read_seeprom's.
831a72f7eaql	 */
832a72f7eaql	sd.sd_handle = regs->r_handle;
833a72f7eaql	sd.sd_base = regs->r_base;
834a72f7eaql	sd.sd_regsize = 1;
835a72f7eaql	sd.sd_control_offset = RTW_9346CR;
836a72f7eaql	sd.sd_status_offset = RTW_9346CR;
837a72f7eaql	sd.sd_dataout_offset = RTW_9346CR;
838a72f7eaql	sd.sd_CK = RTW_9346CR_EESK;
839a72f7eaql	sd.sd_CS = RTW_9346CR_EECS;
840a72f7eaql	sd.sd_DI = RTW_9346CR_EEDO;
841a72f7eaql	sd.sd_DO = RTW_9346CR_EEDI;
842a72f7eaql	/*
843a72f7eaql	 * make read_seeprom enter EEPROM read/write mode
844a72f7eaql	 */
845a72f7eaql	sd.sd_MS = ecr;
846a72f7eaql	sd.sd_RDY = 0;
847a72f7eaql
848a72f7eaql	/*
849a72f7eaql	 * TBD bus barriers
850a72f7eaql	 */
851a72f7eaql	if (!read_seeprom(&sd, sr->sr_content, 0, sr->sr_size/2)) {
852a72f7eaql		cmn_err(CE_WARN, "%s: could not read SROM\n", dvname);
853a72f7eaql		kmem_free(sr->sr_content, sr->sr_size);
854a72f7eaql		sr->sr_content = NULL;
855a72f7eaql		return (-1);	/* XXX */
856a72f7eaql	}
857a72f7eaql
858a72f7eaql	/*
859a72f7eaql	 * end EEPROM read/write mode
860a72f7eaql	 */
861a72f7eaql	RTW_WRITE8(regs, RTW_9346CR,
862a72f7eaql	    (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_NORMAL);
863a72f7eaql	RTW_WBRW(regs, RTW_9346CR, RTW_9346CR);
864a72f7eaql
865a72f7eaql	if ((rc = rtw_recall_eeprom(regs, dvname)) != 0)
866a72f7eaql		return (rc);
867a72f7eaql
868a72f7eaql#ifdef SROM_DEBUG
869a72f7eaql	{
870a72f7eaql		int i;
871a72f7eaql		RTW_DPRINTF(RTW_DEBUG_ATTACH,
872a72f7eaql		    "\n%s: serial ROM:\n\t", dvname);
873a72f7eaql		for (i = 0; i < sr->sr_size/2; i++) {
874a72f7eaql			RTW_DPRINTF(RTW_DEBUG_ATTACH,
875a72f7eaql			    "offset-0x%x: %04x", 2*i, sr->sr_content[i]);
876a72f7eaql		}
877a72f7eaql	}
878a72f7eaql#endif /* DEBUG */
879a72f7eaql	return (0);
880a72f7eaql}
881a72f7eaql
882a72f7eaqlstatic void
883a72f7eaqlrtw_set_rfprog(struct rtw_regs *regs, enum rtw_rfchipid rfchipid,
884a72f7eaql    const char *dvname)
885a72f7eaql{
886a72f7eaql	uint8_t cfg4;
887a72f7eaql	const char *method;
888a72f7eaql
889a72f7eaql	cfg4 = RTW_READ8(regs, RTW_CONFIG4) & ~RTW_CONFIG4_RFTYPE_MASK;
890a72f7eaql
891a72f7eaql	switch (rfchipid) {
892a72f7eaql	default:
893a72f7eaql		cfg4 |= LSHIFT(0, RTW_CONFIG4_RFTYPE_MASK);
894a72f7eaql		method = "fallback";
895a72f7eaql		break;
896a72f7eaql	case RTW_RFCHIPID_INTERSIL:
897a72f7eaql		cfg4 |= RTW_CONFIG4_RFTYPE_INTERSIL;
898a72f7eaql		method = "Intersil";
899a72f7eaql		break;
900a72f7eaql	case RTW_RFCHIPID_PHILIPS:
901a72f7eaql		cfg4 |= RTW_CONFIG4_RFTYPE_PHILIPS;
902a72f7eaql		method = "Philips";
903a72f7eaql		break;
904a72f7eaql	case RTW_RFCHIPID_GCT:	/* XXX a guess */
905a72f7eaql	case RTW_RFCHIPID_RFMD:
906a72f7eaql		cfg4 |= RTW_CONFIG4_RFTYPE_RFMD;
907a72f7eaql		method = "RFMD";
908a72f7eaql		break;
909a72f7eaql	}
910a72f7eaql
911a72f7eaql	RTW_WRITE8(regs, RTW_CONFIG4, cfg4);
912a72f7eaql
913a72f7eaql	RTW_WBR(regs, RTW_CONFIG4, RTW_CONFIG4);
914a72f7eaql
915a72f7eaql	RTW_DPRINTF(RTW_DEBUG_INIT,
916a72f7eaql	    "%s: %s RF programming method, %02x\n", dvname, method,
917a72f7eaql	    RTW_READ8(regs, RTW_CONFIG4));
918a72f7eaql}
919a72f7eaql
920a72f7eaqlstatic void
921a72f7eaqlrtw_init_channels(enum rtw_locale locale,
922a72f7eaql    struct ieee80211_channel (*chans)[IEEE80211_CHAN_MAX+1],
923a72f7eaql    const char *dvname)
924a72f7eaql{
925a72f7eaql	int i;
926a72f7eaql	const char *name = NULL;
927a72f7eaql#define	ADD_CHANNEL(_chans, _chan) {			\
928a72f7eaql	(*_chans)[_chan].ich_flags = IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK;\
929a72f7eaql	(*_chans)[_chan].ich_freq =				\
930a72f7eaql	    ieee80211_ieee2mhz(_chan, (*_chans)[_chan].ich_flags);\
931a72f7eaql}
932a72f7eaql
933a72f7eaql	switch (locale) {
934a72f7eaql	case RTW_LOCALE_USA:	/* 1-11 */
935a72f7eaql		name = "USA";
936a72f7eaql		for (i = 1; i <= 11; i++)
937a72f7eaql			ADD_CHANNEL(chans, i);
938a72f7eaql		break;
939a72f7eaql	case RTW_LOCALE_JAPAN:	/* 1-14 */
940a72f7eaql		name = "Japan";
941a72f7eaql		ADD_CHANNEL(chans, 14);
942a72f7eaql		for (i = 1; i <= 14; i++)
943a72f7eaql			ADD_CHANNEL(chans, i);
944a72f7eaql		break;
945a72f7eaql	case RTW_LOCALE_EUROPE:	/* 1-13 */
946a72f7eaql		name = "Europe";
947a72f7eaql		for (i = 1; i <= 13; i++)
948a72f7eaql			ADD_CHANNEL(chans, i);
949a72f7eaql		break;
950a72f7eaql	default:			/* 10-11 allowed by most countries */
951a72f7eaql		name = "<unknown>";
952a72f7eaql		for (i = 10; i <= 11; i++)
953a72f7eaql			ADD_CHANNEL(chans, i);
954a72f7eaql		break;
955a72f7eaql	}
956a72f7eaql	RTW_DPRINTF(RTW_DEBUG_ATTACH, "%s: Geographic Location %s\n",
957a72f7eaql	    dvname, name);
958a72f7eaql#undef ADD_CHANNEL
959a72f7eaql}
960a72f7eaql
961a72f7eaqlstatic void
962a72f7eaqlrtw_set80211props(struct ieee80211com *ic)
963a72f7eaql{
964a72f7eaql	ic->ic_phytype = IEEE80211_T_DS;
965a72f7eaql	ic->ic_opmode = IEEE80211_M_STA;
966a72f7eaql	ic->ic_caps = IEEE80211_C_PMGT | IEEE80211_C_IBSS |
96794d05f6Qin Michael Li	    IEEE80211_C_SHPREAMBLE;
96894d05f6Qin Michael Li	/* IEEE80211_C_HOSTAP | IEEE80211_C_MONITOR | IEEE80211_C_WEP */
969a72f7eaql
97094d05f6Qin Michael Li	ic->ic_sup_rates[IEEE80211_MODE_11B] = rtw_rateset_11b;
971a72f7eaql}
972a72f7eaql
973a72f7eaql/*ARGSUSED*/
974a72f7eaqlstatic void
975a72f7eaqlrtw_identify_country(struct rtw_regs *regs, enum rtw_locale *locale,
976a72f7eaql    const char *dvname)
977a72f7eaql{
978a72f7eaql	uint8_t cfg0 = RTW_READ8(regs, RTW_CONFIG0);
979a72f7eaql
980a72f7eaql	switch (cfg0 & RTW_CONFIG0_GL_MASK) {
981a72f7eaql	case RTW_CONFIG0_GL_USA:
982a72f7eaql		*locale = RTW_LOCALE_USA;
983a72f7eaql		break;
984a72f7eaql	case RTW_CONFIG0_GL_JAPAN:
985a72f7eaql		*locale = RTW_LOCALE_JAPAN;
986a72f7eaql		break;
987a72f7eaql	case RTW_CONFIG0_GL_EUROPE:
988a72f7eaql		*locale = RTW_LOCALE_EUROPE;
989a72f7eaql		break;
990a72f7eaql	default:
991a72f7eaql		*locale = RTW_LOCALE_UNKNOWN;
992a72f7eaql		break;
993a72f7eaql	}
994a72f7eaql}
995a72f7eaql
996a72f7eaqlstatic int
997a72f7eaqlrtw_identify_sta(struct rtw_regs *regs, uint8_t *addr,
998a72f7eaql    const char *dvname)
999a72f7eaql{
1000a72f7eaql	uint32_t idr0 = RTW_READ(regs, RTW_IDR0),
1001a72f7eaql	    idr1 = RTW_READ(regs, RTW_IDR1);
1002a72f7eaql
1003a72f7eaql	*addr = MASK_AND_RSHIFT(idr0, BITS(0,  7));
1004a72f7eaql	*(addr + 1) = MASK_AND_RSHIFT(idr0, BITS(8,  15));
1005a72f7eaql	*(addr + 2) = MASK_AND_RSHIFT(idr0, BITS(16, 23));
1006a72f7eaql	*(addr + 3) = MASK_AND_RSHIFT(idr0, BITS(24, 31));
1007a72f7eaql
1008a72f7eaql	*(addr + 4) = MASK_AND_RSHIFT(idr1, BITS(0,  7));
1009a72f7eaql	*(addr + 5) = MASK_AND_RSHIFT(idr1, BITS(8, 15));
1010a72f7eaql
1011a72f7eaql	RTW_DPRINTF(RTW_DEBUG_ATTACH,
1012a72f7eaql	    "%s: 802.11mac address %x:%x:%x:%x:%x:%x\n", dvname,
1013a72f7eaql	    *addr, *(addr+1), *(addr+2), *(addr+3), *(addr+4), *(addr+5));
1014a72f7eaql
1015a72f7eaql	return (0);
1016a72f7eaql}
1017a72f7eaql
1018a72f7eaqlstatic uint8_t
1019a72f7eaqlrtw_chan2txpower(struct rtw_srom *sr, struct ieee80211com *ic,
1020a72f7eaql    struct ieee80211_channel *chan)
1021a72f7eaql{
1022a72f7eaql	uint32_t idx = RTW_SR_TXPOWER1 + ieee80211_chan2ieee(ic, chan) - 1;
1023a72f7eaql	return (RTW_SR_GET(sr, idx));
1024a72f7eaql}
1025a72f7eaql
1026a72f7eaqlstatic void
1027a72f7eaqlrtw_rxdesc_init(rtw_softc_t *rsc, struct rtw_rxbuf *rbf, int idx, int is_last)
1028a72f7eaql{
1029a72f7eaql	uint32_t ctl = 0;
1030a72f7eaql	uint8_t *buf = (uint8_t *)rbf->bf_dma.mem_va;
1031a72f7eaql
1032a72f7eaql	ASSERT(rbf != NULL);
1033a72f7eaql	rbf->rxdesc->rd_buf = (rbf->bf_dma.cookie.dmac_address);
1034a72f7eaql	bzero(buf, rbf->bf_dma.alength);
1035a72f7eaql	RTW_DMA_SYNC(rbf->bf_dma, DDI_DMA_SYNC_FORDEV);
1036a72f7eaql
1037a72f7eaql	ctl = (rbf->bf_dma.alength & 0xfff) | RTW_RXCTL_OWN;
1038a72f7eaql
1039a72f7eaql	if (is_last)
1040a72f7eaql		ctl |= RTW_RXCTL_EOR;
1041a72f7eaql
1042a72f7eaql	rbf->rxdesc->rd_ctl = (ctl);
1043a72f7eaql	/* sync the mbuf */
1044a72f7eaql
1045a72f7eaql	/* sync the descriptor */
1046a72f7eaql	RTW_DMA_SYNC_DESC(rsc->sc_desc_dma,
1047a72f7eaql	    RTW_DESC_OFFSET(hd_rx, idx),
1048a72f7eaql	    sizeof (struct rtw_rxdesc),
1049a72f7eaql	    DDI_DMA_SYNC_FORDEV);
1050a72f7eaql}
1051a72f7eaql
1052a72f7eaqlstatic void
1053a72f7eaqlrtw_idle(struct rtw_regs *regs)
1054a72f7eaql{
1055a72f7eaql	int active;
1056a72f7eaql
1057a72f7eaql	/* request stop DMA; wait for packets to stop transmitting. */
1058a72f7eaql
1059a72f7eaql	RTW_WRITE8(regs, RTW_TPPOLL, RTW_TPPOLL_SALL);
1060a72f7eaql
1061a72f7eaql	for (active = 0; active < 300 &&
106294d05f6Qin Michael Li	    (RTW_READ8(regs, RTW_TPPOLL) & RTW_TPPOLL_ALL) != 0; active++)
1063a72f7eaql		drv_usecwait(10);
1064a72f7eaql}
1065a72f7eaql
1066a72f7eaqlstatic void
1067a72f7eaqlrtw_io_enable(rtw_softc_t *rsc, uint8_t flags, int enable)
1068a72f7eaql{
1069a72f7eaql	uint8_t cr;
1070a72f7eaql	struct rtw_regs *regs = &rsc->sc_regs;
1071a72f7eaql
1072a72f7eaql	RTW_DPRINTF(RTW_DEBUG_IOSTATE, "%s: %s 0x%02x\n", __func__,
1073a72f7eaql	    enable ? "enable" : "disable", flags);
1074a72f7eaql
1075a72f7eaql	cr = RTW_READ8(regs, RTW_CR);
107694d05f6Qin Michael Li
1077a72f7eaql	/* The receive engine will always start at RDSAR.  */
1078a72f7eaql	if (enable && (flags & ~cr & RTW_CR_RE)) {
1079a72f7eaql		RTW_DMA_SYNC_DESC(rsc->sc_desc_dma,
1080a72f7eaql		    RTW_DESC_OFFSET(hd_rx, 0),
1081a72f7eaql		    sizeof (struct rtw_rxdesc),
1082a72f7eaql		    DDI_DMA_SYNC_FORCPU);
1083a72f7eaql		rsc->rx_next = 0;
1084a72f7eaql		rtw_rxdesc_init(rsc, rsc->rxbuf_h, 0, 0);
1085a72f7eaql	}
108694d05f6Qin Michael Li
1087a72f7eaql	if (enable)
1088a72f7eaql		cr |= flags;
1089a72f7eaql	else
1090a72f7eaql		cr &= ~flags;
1091a72f7eaql	RTW_WRITE8(regs, RTW_CR, cr);
1092a72f7eaql	(void) RTW_READ8(regs, RTW_CR);
1093a72f7eaql}
1094a72f7eaql
1095a72f7eaql/*
1096a72f7eaql * Allocate an area of memory and a DMA handle for accessing it
1097a72f7eaql */
1098a72f7eaqlstatic int
1099a72f7eaqlrtw_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr,
1100a72f7eaql	size_t memsize, ddi_device_acc_attr_t *attr_p, uint_t alloc_flags,
1101a72f7eaql	uint_t bind_flags, dma_area_t *dma_p)
1102a72f7eaql{
1103a72f7eaql	int err;
1104a72f7eaql
1105a72f7eaql	/*
1106a72f7eaql	 * Allocate handle
1107a72f7eaql	 */
1108a72f7eaql	err = ddi_dma_alloc_handle(devinfo, dma_attr,
1109a72f7eaql	    DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
1110a72f7eaql	if (err != DDI_SUCCESS)
1111a72f7eaql		return (DDI_FAILURE);
1112a72f7eaql
1113a72f7eaql	/*
1114a72f7eaql	 * Allocate memory
1115a72f7eaql	 */
1116a72f7eaql	err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
1117a72f7eaql	    alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
1118a72f7eaql	    &dma_p->alength, &dma_p->acc_hdl);
1119a72f7eaql	if (err != DDI_SUCCESS)
1120a72f7eaql		return (DDI_FAILURE);
1121a72f7eaql
1122a72f7eaql	/*
1123a72f7eaql	 * Bind the two together
1124a72f7eaql	 */
1125a72f7eaql	err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
1126a72f7eaql	    dma_p->mem_va, dma_p->alength, bind_flags,
1127a72f7eaql	    DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
1128a72f7eaql	if ((dma_p->ncookies != 1) || (err != DDI_DMA_MAPPED))
1129a72f7eaql		return (DDI_FAILURE);
1130a72f7eaql
1131a72f7eaql	dma_p->nslots = ~0U;
1132a72f7eaql	dma_p->size = ~0U;
1133a72f7eaql	dma_p->token = ~0U;
1134a72f7eaql	dma_p->offset = 0;
1135a72f7eaql	return (DDI_SUCCESS);
1136a72f7eaql}
1137a72f7eaql
1138a72f7eaql/*
1139a72f7eaql * Free one allocated area of DMAable memory
1140a72f7eaql */
1141a72f7eaqlstatic void
1142a72f7eaqlrtw_free_dma_mem(dma_area_t *dma_p)
1143a72f7eaql{
1144a72f7eaql	if (dma_p->dma_hdl != NULL) {
1145a72f7eaql		(void) ddi_dma_unbind_handle(dma_p->dma_hdl);
1146a72f7eaql		if (dma_p->acc_hdl != NULL) {
1147a72f7eaql			ddi_dma_mem_free(&dma_p->acc_hdl);
1148a72f7eaql			dma_p->acc_hdl = NULL;
1149a72f7eaql		}
1150a72f7eaql		ddi_dma_free_handle(&dma_p->dma_hdl);
1151a72f7eaql		dma_p->ncookies = 0;
1152a72f7eaql		dma_p->dma_hdl = NULL;
1153a72f7eaql	}
1154a72f7eaql}
1155a72f7eaql
1156a72f7eaqlstatic void
1157a72f7eaqlrtw_dma_free(rtw_softc_t *rsc)
1158a72f7eaql{
1159a72f7eaql	struct rtw_txbuf *txbf;
1160a72f7eaql	struct rtw_rxbuf *rxbf;
1161a72f7eaql	int i, j;
1162a72f7eaql
1163a72f7eaql	/* Free TX DMA buffer */
1164a72f7eaql	for (i = 0; i < RTW_NTXPRI; i++) {
1165a72f7eaql		txbf = list_head(&rsc->sc_txq[i].tx_free_list);
1166a72f7eaql		while (txbf != NULL) {
1167a72f7eaql			rtw_free_dma_mem(&txbf->bf_dma);
1168a72f7eaql			list_remove(&rsc->sc_txq[i].tx_free_list, txbf);
1169a72f7eaql			txbf = list_head(&rsc->sc_txq[i].tx_free_list);
1170a72f7eaql		}
1171a72f7eaql		list_destroy(&rsc->sc_txq[i].tx_free_list);
1172a72f7eaql		txbf = list_head(&rsc->sc_txq[i].tx_dirty_list);
1173a72f7eaql		while (txbf != NULL) {
1174a72f7eaql			rtw_free_dma_mem(&txbf->bf_dma);
1175a72f7eaql			list_remove(&rsc->sc_txq[i].tx_dirty_list, txbf);
1176a72f7eaql			txbf = list_head(&rsc->sc_txq[i].tx_dirty_list);
1177a72f7eaql		}
1178a72f7eaql		list_destroy(&rsc->sc_txq[i].tx_dirty_list);
1179a72f7eaql
1180a72f7eaql		if (rsc->sc_txq[i].txbuf_h != NULL) {
1181a72f7eaql			kmem_free(rsc->sc_txq[i].txbuf_h,
1182a72f7eaql			    sizeof (struct rtw_txbuf) * rtw_qlen[i]);
1183a72f7eaql			rsc->sc_txq[i].txbuf_h = NULL;
1184a72f7eaql		}
1185a72f7eaql	}
1186a72f7eaql
1187a72f7eaql	/* Free RX DMA buffer */
1188a72f7eaql	rxbf = rsc->rxbuf_h;
1189a72f7eaql	for (j = 0; j < RTW_RXQLEN; j++) {
1190a72f7eaql		rtw_free_dma_mem(&rxbf->bf_dma);
1191a72f7eaql		rxbf++;
1192a72f7eaql	}
1193a72f7eaql
1194a72f7eaql	if (rsc->rxbuf_h != NULL) {
1195020c477ql		kmem_free(rsc->rxbuf_h,
1196020c477ql		    sizeof (struct rtw_rxbuf) * RTW_RXQLEN);
1197a72f7eaql		rsc->rxbuf_h = NULL;
1198a72f7eaql	}
1199a72f7eaql
1200a72f7eaql	rtw_free_dma_mem(&rsc->sc_desc_dma);
1201a72f7eaql}
1202a72f7eaql
1203a72f7eaqlstatic int
1204a72f7eaqlrtw_dma_init(dev_info_t *devinfo, rtw_softc_t *rsc)
1205a72f7eaql{
1206a72f7eaql	int i, j, err;
1207a72f7eaql	size_t size;
1208a72f7eaql	uint32_t buflen;
1209a72f7eaql	struct rtw_txdesc *txds;
1210a72f7eaql	struct rtw_rxdesc *rxds;
1211a72f7eaql	struct rtw_txbuf *txbf;
1212a72f7eaql	struct rtw_rxbuf *rxbf;
1213a72f7eaql	uint32_t phybaseaddr, ptx[RTW_NTXPRI], prx;
1214a72f7eaql	caddr_t virbaseaddr, vtx[RTW_NTXPRI], vrx;
1215a72f7eaql
1216a72f7eaql	/* DMA buffer size for each TX/RX packet */
1217a72f7eaql	rsc->sc_dmabuf_size = roundup(sizeof (struct ieee80211_frame) + 0x100 +
1218a72f7eaql	    IEEE80211_MTU + IEEE80211_CRC_LEN + sizeof (struct ieee80211_llc) +
1219a72f7eaql	    (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
1220a72f7eaql	    IEEE80211_WEP_CRCLEN), rsc->sc_cachelsz);
1221a72f7eaql	size = sizeof (struct rtw_descs);
1222