xref: /freebsd/sys/dev/bwi/bwimac.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1*7282444bSPedro F. Giffuni /*-
2*7282444bSPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
3*7282444bSPedro F. Giffuni  *
412e36acbSWarner Losh  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
512e36acbSWarner Losh  *
612e36acbSWarner Losh  * This code is derived from software contributed to The DragonFly Project
712e36acbSWarner Losh  * by Sepherosa Ziehau <sepherosa@gmail.com>
812e36acbSWarner Losh  *
912e36acbSWarner Losh  * Redistribution and use in source and binary forms, with or without
1012e36acbSWarner Losh  * modification, are permitted provided that the following conditions
1112e36acbSWarner Losh  * are met:
1212e36acbSWarner Losh  *
1312e36acbSWarner Losh  * 1. Redistributions of source code must retain the above copyright
1412e36acbSWarner Losh  *    notice, this list of conditions and the following disclaimer.
1512e36acbSWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
1612e36acbSWarner Losh  *    notice, this list of conditions and the following disclaimer in
1712e36acbSWarner Losh  *    the documentation and/or other materials provided with the
1812e36acbSWarner Losh  *    distribution.
1912e36acbSWarner Losh  * 3. Neither the name of The DragonFly Project nor the names of its
2012e36acbSWarner Losh  *    contributors may be used to endorse or promote products derived
2112e36acbSWarner Losh  *    from this software without specific, prior written permission.
2212e36acbSWarner Losh  *
2312e36acbSWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2412e36acbSWarner Losh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2512e36acbSWarner Losh  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2612e36acbSWarner Losh  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2712e36acbSWarner Losh  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2812e36acbSWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2912e36acbSWarner Losh  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3012e36acbSWarner Losh  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
3112e36acbSWarner Losh  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3212e36acbSWarner Losh  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3312e36acbSWarner Losh  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3412e36acbSWarner Losh  * SUCH DAMAGE.
3512e36acbSWarner Losh  *
3612e36acbSWarner Losh  * $DragonFly: src/sys/dev/netif/bwi/bwimac.c,v 1.13 2008/02/15 11:15:38 sephe Exp $
3712e36acbSWarner Losh  */
3812e36acbSWarner Losh 
3912e36acbSWarner Losh #include <sys/cdefs.h>
4012e36acbSWarner Losh #include "opt_inet.h"
4112e36acbSWarner Losh #include "opt_bwi.h"
4260807f5bSAdrian Chadd #include "opt_wlan.h"
4312e36acbSWarner Losh 
4412e36acbSWarner Losh #include <sys/param.h>
4512e36acbSWarner Losh #include <sys/endian.h>
4612e36acbSWarner Losh #include <sys/kernel.h>
4712e36acbSWarner Losh #include <sys/bus.h>
4812e36acbSWarner Losh #include <sys/malloc.h>
4912e36acbSWarner Losh #include <sys/proc.h>
5012e36acbSWarner Losh #include <sys/rman.h>
5112e36acbSWarner Losh #include <sys/socket.h>
5212e36acbSWarner Losh #include <sys/sockio.h>
5312e36acbSWarner Losh #include <sys/sysctl.h>
5412e36acbSWarner Losh #include <sys/systm.h>
5512e36acbSWarner Losh 
5612e36acbSWarner Losh #include <sys/linker.h>
5712e36acbSWarner Losh #include <sys/firmware.h>
5812e36acbSWarner Losh 
5912e36acbSWarner Losh #include <net/if.h>
6076039bc8SGleb Smirnoff #include <net/if_var.h>
6112e36acbSWarner Losh #include <net/if_dl.h>
6212e36acbSWarner Losh #include <net/if_media.h>
6312e36acbSWarner Losh #include <net/if_types.h>
6412e36acbSWarner Losh #include <net/if_arp.h>
6512e36acbSWarner Losh #include <net/ethernet.h>
6612e36acbSWarner Losh #include <net/if_llc.h>
6712e36acbSWarner Losh 
6812e36acbSWarner Losh #include <net80211/ieee80211_var.h>
6912e36acbSWarner Losh #include <net80211/ieee80211_radiotap.h>
7012e36acbSWarner Losh #include <net80211/ieee80211_amrr.h>
7112e36acbSWarner Losh #include <net80211/ieee80211_phy.h>
7212e36acbSWarner Losh 
7312e36acbSWarner Losh #include <machine/bus.h>
7412e36acbSWarner Losh 
7512e36acbSWarner Losh #include <dev/bwi/bitops.h>
7612e36acbSWarner Losh #include <dev/bwi/if_bwireg.h>
7712e36acbSWarner Losh #include <dev/bwi/if_bwivar.h>
7812e36acbSWarner Losh #include <dev/bwi/bwimac.h>
7912e36acbSWarner Losh #include <dev/bwi/bwirf.h>
8012e36acbSWarner Losh #include <dev/bwi/bwiphy.h>
8112e36acbSWarner Losh 
8212e36acbSWarner Losh struct bwi_retry_lim {
8312e36acbSWarner Losh 	uint16_t	shretry;
8412e36acbSWarner Losh 	uint16_t	shretry_fb;
8512e36acbSWarner Losh 	uint16_t	lgretry;
8612e36acbSWarner Losh 	uint16_t	lgretry_fb;
8712e36acbSWarner Losh };
8812e36acbSWarner Losh 
8912e36acbSWarner Losh static int	bwi_mac_test(struct bwi_mac *);
9012e36acbSWarner Losh static int	bwi_mac_get_property(struct bwi_mac *);
9112e36acbSWarner Losh 
9212e36acbSWarner Losh static void	bwi_mac_set_retry_lim(struct bwi_mac *,
9312e36acbSWarner Losh 			const struct bwi_retry_lim *);
9412e36acbSWarner Losh static void	bwi_mac_set_ackrates(struct bwi_mac *,
9512e36acbSWarner Losh 			const struct ieee80211_rate_table *rt,
9612e36acbSWarner Losh 			const struct ieee80211_rateset *);
9712e36acbSWarner Losh 
9812e36acbSWarner Losh static int	bwi_mac_gpio_init(struct bwi_mac *);
9912e36acbSWarner Losh static int	bwi_mac_gpio_fini(struct bwi_mac *);
10012e36acbSWarner Losh static void	bwi_mac_opmode_init(struct bwi_mac *);
10112e36acbSWarner Losh static void	bwi_mac_hostflags_init(struct bwi_mac *);
10212e36acbSWarner Losh static void	bwi_mac_bss_param_init(struct bwi_mac *);
10312e36acbSWarner Losh 
10412e36acbSWarner Losh static void	bwi_mac_fw_free(struct bwi_mac *);
10512e36acbSWarner Losh static int	bwi_mac_fw_load(struct bwi_mac *);
10612e36acbSWarner Losh static int	bwi_mac_fw_init(struct bwi_mac *);
10712e36acbSWarner Losh static int	bwi_mac_fw_load_iv(struct bwi_mac *, const struct firmware *);
10812e36acbSWarner Losh 
10912e36acbSWarner Losh static void	bwi_mac_setup_tpctl(struct bwi_mac *);
11012e36acbSWarner Losh static void	bwi_mac_adjust_tpctl(struct bwi_mac *, int, int);
11112e36acbSWarner Losh 
11212e36acbSWarner Losh static void	bwi_mac_lock(struct bwi_mac *);
11312e36acbSWarner Losh static void	bwi_mac_unlock(struct bwi_mac *);
11412e36acbSWarner Losh 
11512e36acbSWarner Losh static const uint8_t bwi_sup_macrev[] = { 2, 4, 5, 6, 7, 9, 10 };
11612e36acbSWarner Losh 
11712e36acbSWarner Losh void
bwi_tmplt_write_4(struct bwi_mac * mac,uint32_t ofs,uint32_t val)11812e36acbSWarner Losh bwi_tmplt_write_4(struct bwi_mac *mac, uint32_t ofs, uint32_t val)
11912e36acbSWarner Losh {
12012e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
12112e36acbSWarner Losh 
12212e36acbSWarner Losh 	if (mac->mac_flags & BWI_MAC_F_BSWAP)
12312e36acbSWarner Losh 		val = bswap32(val);
12412e36acbSWarner Losh 
12512e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_TMPLT_CTRL, ofs);
12612e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_TMPLT_DATA, val);
12712e36acbSWarner Losh }
12812e36acbSWarner Losh 
12912e36acbSWarner Losh void
bwi_hostflags_write(struct bwi_mac * mac,uint64_t flags)13012e36acbSWarner Losh bwi_hostflags_write(struct bwi_mac *mac, uint64_t flags)
13112e36acbSWarner Losh {
13212e36acbSWarner Losh 	uint64_t val;
13312e36acbSWarner Losh 
13412e36acbSWarner Losh 	val = flags & 0xffff;
13512e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_HFLAGS_LO, val);
13612e36acbSWarner Losh 
13712e36acbSWarner Losh 	val = (flags >> 16) & 0xffff;
13812e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_HFLAGS_MI, val);
13912e36acbSWarner Losh 
14012e36acbSWarner Losh 	/* HI has unclear meaning, so leave it as it is */
14112e36acbSWarner Losh }
14212e36acbSWarner Losh 
14312e36acbSWarner Losh uint64_t
bwi_hostflags_read(struct bwi_mac * mac)14412e36acbSWarner Losh bwi_hostflags_read(struct bwi_mac *mac)
14512e36acbSWarner Losh {
14612e36acbSWarner Losh 	uint64_t flags, val;
14712e36acbSWarner Losh 
14812e36acbSWarner Losh 	/* HI has unclear meaning, so don't touch it */
14912e36acbSWarner Losh 	flags = 0;
15012e36acbSWarner Losh 
15112e36acbSWarner Losh 	val = MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_HFLAGS_MI);
15212e36acbSWarner Losh 	flags |= val << 16;
15312e36acbSWarner Losh 
15412e36acbSWarner Losh 	val = MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_HFLAGS_LO);
15512e36acbSWarner Losh 	flags |= val;
15612e36acbSWarner Losh 
15712e36acbSWarner Losh 	return flags;
15812e36acbSWarner Losh }
15912e36acbSWarner Losh 
16012e36acbSWarner Losh uint16_t
bwi_memobj_read_2(struct bwi_mac * mac,uint16_t obj_id,uint16_t ofs0)16112e36acbSWarner Losh bwi_memobj_read_2(struct bwi_mac *mac, uint16_t obj_id, uint16_t ofs0)
16212e36acbSWarner Losh {
16312e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
16412e36acbSWarner Losh 	uint32_t data_reg;
16512e36acbSWarner Losh 	int ofs;
16612e36acbSWarner Losh 
16712e36acbSWarner Losh 	data_reg = BWI_MOBJ_DATA;
16812e36acbSWarner Losh 	ofs = ofs0 / 4;
16912e36acbSWarner Losh 
17012e36acbSWarner Losh 	if (ofs0 % 4 != 0)
17112e36acbSWarner Losh 		data_reg = BWI_MOBJ_DATA_UNALIGN;
17212e36acbSWarner Losh 
17312e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
17412e36acbSWarner Losh 	return CSR_READ_2(sc, data_reg);
17512e36acbSWarner Losh }
17612e36acbSWarner Losh 
17712e36acbSWarner Losh uint32_t
bwi_memobj_read_4(struct bwi_mac * mac,uint16_t obj_id,uint16_t ofs0)17812e36acbSWarner Losh bwi_memobj_read_4(struct bwi_mac *mac, uint16_t obj_id, uint16_t ofs0)
17912e36acbSWarner Losh {
18012e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
18112e36acbSWarner Losh 	int ofs;
18212e36acbSWarner Losh 
18312e36acbSWarner Losh 	ofs = ofs0 / 4;
18412e36acbSWarner Losh 	if (ofs0 % 4 != 0) {
18512e36acbSWarner Losh 		uint32_t ret;
18612e36acbSWarner Losh 
18712e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
18812e36acbSWarner Losh 		ret = CSR_READ_2(sc, BWI_MOBJ_DATA_UNALIGN);
18912e36acbSWarner Losh 		ret <<= 16;
19012e36acbSWarner Losh 
19112e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
19212e36acbSWarner Losh 			    BWI_MOBJ_CTRL_VAL(obj_id, ofs + 1));
19312e36acbSWarner Losh 		ret |= CSR_READ_2(sc, BWI_MOBJ_DATA);
19412e36acbSWarner Losh 
19512e36acbSWarner Losh 		return ret;
19612e36acbSWarner Losh 	} else {
19712e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
19812e36acbSWarner Losh 		return CSR_READ_4(sc, BWI_MOBJ_DATA);
19912e36acbSWarner Losh 	}
20012e36acbSWarner Losh }
20112e36acbSWarner Losh 
20212e36acbSWarner Losh void
bwi_memobj_write_2(struct bwi_mac * mac,uint16_t obj_id,uint16_t ofs0,uint16_t v)20312e36acbSWarner Losh bwi_memobj_write_2(struct bwi_mac *mac, uint16_t obj_id, uint16_t ofs0,
20412e36acbSWarner Losh 		   uint16_t v)
20512e36acbSWarner Losh {
20612e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
20712e36acbSWarner Losh 	uint32_t data_reg;
20812e36acbSWarner Losh 	int ofs;
20912e36acbSWarner Losh 
21012e36acbSWarner Losh 	data_reg = BWI_MOBJ_DATA;
21112e36acbSWarner Losh 	ofs = ofs0 / 4;
21212e36acbSWarner Losh 
21312e36acbSWarner Losh 	if (ofs0 % 4 != 0)
21412e36acbSWarner Losh 		data_reg = BWI_MOBJ_DATA_UNALIGN;
21512e36acbSWarner Losh 
21612e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
21712e36acbSWarner Losh 	CSR_WRITE_2(sc, data_reg, v);
21812e36acbSWarner Losh }
21912e36acbSWarner Losh 
22012e36acbSWarner Losh void
bwi_memobj_write_4(struct bwi_mac * mac,uint16_t obj_id,uint16_t ofs0,uint32_t v)22112e36acbSWarner Losh bwi_memobj_write_4(struct bwi_mac *mac, uint16_t obj_id, uint16_t ofs0,
22212e36acbSWarner Losh 		   uint32_t v)
22312e36acbSWarner Losh {
22412e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
22512e36acbSWarner Losh 	int ofs;
22612e36acbSWarner Losh 
22712e36acbSWarner Losh 	ofs = ofs0 / 4;
22812e36acbSWarner Losh 	if (ofs0 % 4 != 0) {
22912e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
23012e36acbSWarner Losh 		CSR_WRITE_2(sc, BWI_MOBJ_DATA_UNALIGN, v >> 16);
23112e36acbSWarner Losh 
23212e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
23312e36acbSWarner Losh 			    BWI_MOBJ_CTRL_VAL(obj_id, ofs + 1));
23412e36acbSWarner Losh 		CSR_WRITE_2(sc, BWI_MOBJ_DATA, v & 0xffff);
23512e36acbSWarner Losh 	} else {
23612e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_MOBJ_CTRL, BWI_MOBJ_CTRL_VAL(obj_id, ofs));
23712e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_MOBJ_DATA, v);
23812e36acbSWarner Losh 	}
23912e36acbSWarner Losh }
24012e36acbSWarner Losh 
24112e36acbSWarner Losh int
bwi_mac_lateattach(struct bwi_mac * mac)24212e36acbSWarner Losh bwi_mac_lateattach(struct bwi_mac *mac)
24312e36acbSWarner Losh {
24412e36acbSWarner Losh 	int error;
24512e36acbSWarner Losh 
24612e36acbSWarner Losh 	if (mac->mac_rev >= 5)
24712e36acbSWarner Losh 		CSR_READ_4(mac->mac_sc, BWI_STATE_HI); /* dummy read */
24812e36acbSWarner Losh 
24912e36acbSWarner Losh 	bwi_mac_reset(mac, 1);
25012e36acbSWarner Losh 
25112e36acbSWarner Losh 	error = bwi_phy_attach(mac);
25212e36acbSWarner Losh 	if (error)
25312e36acbSWarner Losh 		return error;
25412e36acbSWarner Losh 
25512e36acbSWarner Losh 	error = bwi_rf_attach(mac);
25612e36acbSWarner Losh 	if (error)
25712e36acbSWarner Losh 		return error;
25812e36acbSWarner Losh 
25912e36acbSWarner Losh 	/* Link 11B/G PHY, unlink 11A PHY */
26012e36acbSWarner Losh 	if (mac->mac_phy.phy_mode == IEEE80211_MODE_11A)
26112e36acbSWarner Losh 		bwi_mac_reset(mac, 0);
26212e36acbSWarner Losh 	else
26312e36acbSWarner Losh 		bwi_mac_reset(mac, 1);
26412e36acbSWarner Losh 
26512e36acbSWarner Losh 	error = bwi_mac_test(mac);
26612e36acbSWarner Losh 	if (error)
26712e36acbSWarner Losh 		return error;
26812e36acbSWarner Losh 
26912e36acbSWarner Losh 	error = bwi_mac_get_property(mac);
27012e36acbSWarner Losh 	if (error)
27112e36acbSWarner Losh 		return error;
27212e36acbSWarner Losh 
27312e36acbSWarner Losh 	error = bwi_rf_map_txpower(mac);
27412e36acbSWarner Losh 	if (error)
27512e36acbSWarner Losh 		return error;
27612e36acbSWarner Losh 
27712e36acbSWarner Losh 	bwi_rf_off(mac);
27812e36acbSWarner Losh 	CSR_WRITE_2(mac->mac_sc, BWI_BBP_ATTEN, BWI_BBP_ATTEN_MAGIC);
27912e36acbSWarner Losh 	bwi_regwin_disable(mac->mac_sc, &mac->mac_regwin, 0);
28012e36acbSWarner Losh 
28112e36acbSWarner Losh 	return 0;
28212e36acbSWarner Losh }
28312e36acbSWarner Losh 
28412e36acbSWarner Losh int
bwi_mac_init(struct bwi_mac * mac)28512e36acbSWarner Losh bwi_mac_init(struct bwi_mac *mac)
28612e36acbSWarner Losh {
28712e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
28812e36acbSWarner Losh 	int error, i;
28912e36acbSWarner Losh 
29012e36acbSWarner Losh 	/* Clear MAC/PHY/RF states */
29112e36acbSWarner Losh 	bwi_mac_setup_tpctl(mac);
29212e36acbSWarner Losh 	bwi_rf_clear_state(&mac->mac_rf);
29312e36acbSWarner Losh 	bwi_phy_clear_state(&mac->mac_phy);
29412e36acbSWarner Losh 
29512e36acbSWarner Losh 	/* Enable MAC and linked it to PHY */
29612e36acbSWarner Losh 	if (!bwi_regwin_is_enabled(sc, &mac->mac_regwin))
29712e36acbSWarner Losh 		bwi_mac_reset(mac, 1);
29812e36acbSWarner Losh 
29912e36acbSWarner Losh 	/* Initialize backplane */
30012e36acbSWarner Losh 	error = bwi_bus_init(sc, mac);
30112e36acbSWarner Losh 	if (error)
30212e36acbSWarner Losh 		return error;
30312e36acbSWarner Losh 
304d83d76dfSWarner Losh 	/* do timeout fixup */
30512e36acbSWarner Losh 	if (sc->sc_bus_regwin.rw_rev <= 5 &&
30612e36acbSWarner Losh 	    sc->sc_bus_regwin.rw_type != BWI_REGWIN_T_BUSPCIE) {
30712e36acbSWarner Losh 		CSR_SETBITS_4(sc, BWI_CONF_LO,
30812e36acbSWarner Losh 		__SHIFTIN(BWI_CONF_LO_SERVTO, BWI_CONF_LO_SERVTO_MASK) |
30912e36acbSWarner Losh 		__SHIFTIN(BWI_CONF_LO_REQTO, BWI_CONF_LO_REQTO_MASK));
31012e36acbSWarner Losh 	}
31112e36acbSWarner Losh 
31212e36acbSWarner Losh 	/* Calibrate PHY */
31312e36acbSWarner Losh 	error = bwi_phy_calibrate(mac);
31412e36acbSWarner Losh 	if (error) {
31512e36acbSWarner Losh 		device_printf(sc->sc_dev, "PHY calibrate failed\n");
31612e36acbSWarner Losh 		return error;
31712e36acbSWarner Losh 	}
31812e36acbSWarner Losh 
31912e36acbSWarner Losh 	/* Prepare to initialize firmware */
32012e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_STATUS,
32112e36acbSWarner Losh 		    BWI_MAC_STATUS_UCODE_JUMP0 |
32212e36acbSWarner Losh 		    BWI_MAC_STATUS_IHREN);
32312e36acbSWarner Losh 
32412e36acbSWarner Losh 	/*
32512e36acbSWarner Losh 	 * Load and initialize firmwares
32612e36acbSWarner Losh 	 */
32712e36acbSWarner Losh 	error = bwi_mac_fw_load(mac);
32812e36acbSWarner Losh 	if (error)
32912e36acbSWarner Losh 		return error;
33012e36acbSWarner Losh 
33112e36acbSWarner Losh 	error = bwi_mac_gpio_init(mac);
33212e36acbSWarner Losh 	if (error)
33312e36acbSWarner Losh 		return error;
33412e36acbSWarner Losh 
33512e36acbSWarner Losh 	error = bwi_mac_fw_init(mac);
33612e36acbSWarner Losh 	if (error)
33712e36acbSWarner Losh 		return error;
33812e36acbSWarner Losh 
33912e36acbSWarner Losh 	/*
34012e36acbSWarner Losh 	 * Turn on RF
34112e36acbSWarner Losh 	 */
34212e36acbSWarner Losh 	bwi_rf_on(mac);
34312e36acbSWarner Losh 
34412e36acbSWarner Losh 	/* TODO: LED, hardware rf enabled is only related to LED setting */
34512e36acbSWarner Losh 
34612e36acbSWarner Losh 	/*
34712e36acbSWarner Losh 	 * Initialize PHY
34812e36acbSWarner Losh 	 */
34912e36acbSWarner Losh 	CSR_WRITE_2(sc, BWI_BBP_ATTEN, 0);
35012e36acbSWarner Losh 	bwi_phy_init(mac);
35112e36acbSWarner Losh 
35212e36acbSWarner Losh 	/* TODO: interference mitigation */
35312e36acbSWarner Losh 
35412e36acbSWarner Losh 	/*
35512e36acbSWarner Losh 	 * Setup antenna mode
35612e36acbSWarner Losh 	 */
35712e36acbSWarner Losh 	bwi_rf_set_ant_mode(mac, mac->mac_rf.rf_ant_mode);
35812e36acbSWarner Losh 
35912e36acbSWarner Losh 	/*
36012e36acbSWarner Losh 	 * Initialize operation mode (RX configuration)
36112e36acbSWarner Losh 	 */
36212e36acbSWarner Losh 	bwi_mac_opmode_init(mac);
36312e36acbSWarner Losh 
364d83d76dfSWarner Losh 	/* set up Beacon interval */
36512e36acbSWarner Losh 	if (mac->mac_rev < 3) {
36612e36acbSWarner Losh 		CSR_WRITE_2(sc, 0x60e, 0);
36712e36acbSWarner Losh 		CSR_WRITE_2(sc, 0x610, 0x8000);
36812e36acbSWarner Losh 		CSR_WRITE_2(sc, 0x604, 0);
36912e36acbSWarner Losh 		CSR_WRITE_2(sc, 0x606, 0x200);
37012e36acbSWarner Losh 	} else {
37112e36acbSWarner Losh 		CSR_WRITE_4(sc, 0x188, 0x80000000);
37212e36acbSWarner Losh 		CSR_WRITE_4(sc, 0x18c, 0x2000000);
37312e36acbSWarner Losh 	}
37412e36acbSWarner Losh 
37512e36acbSWarner Losh 	/*
37612e36acbSWarner Losh 	 * Initialize TX/RX interrupts' mask
37712e36acbSWarner Losh 	 */
37812e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_INTR_STATUS, BWI_INTR_TIMER1);
37912e36acbSWarner Losh 	for (i = 0; i < BWI_TXRX_NRING; ++i) {
38012e36acbSWarner Losh 		uint32_t intrs;
38112e36acbSWarner Losh 
38212e36acbSWarner Losh 		if (BWI_TXRX_IS_RX(i))
38312e36acbSWarner Losh 			intrs = BWI_TXRX_RX_INTRS;
38412e36acbSWarner Losh 		else
38512e36acbSWarner Losh 			intrs = BWI_TXRX_TX_INTRS;
38612e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_TXRX_INTR_MASK(i), intrs);
38712e36acbSWarner Losh 	}
38812e36acbSWarner Losh 
389d83d76dfSWarner Losh 	/* allow the MAC to control the PHY clock (dynamic on/off) */
39012e36acbSWarner Losh 	CSR_SETBITS_4(sc, BWI_STATE_LO, 0x100000);
39112e36acbSWarner Losh 
39212e36acbSWarner Losh 	/* Setup MAC power up delay */
39312e36acbSWarner Losh 	CSR_WRITE_2(sc, BWI_MAC_POWERUP_DELAY, sc->sc_pwron_delay);
39412e36acbSWarner Losh 
39512e36acbSWarner Losh 	/* Set MAC regwin revision */
39612e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_MACREV, mac->mac_rev);
39712e36acbSWarner Losh 
39812e36acbSWarner Losh 	/*
39912e36acbSWarner Losh 	 * Initialize host flags
40012e36acbSWarner Losh 	 */
40112e36acbSWarner Losh 	bwi_mac_hostflags_init(mac);
40212e36acbSWarner Losh 
40312e36acbSWarner Losh 	/*
40412e36acbSWarner Losh 	 * Initialize BSS parameters
40512e36acbSWarner Losh 	 */
40612e36acbSWarner Losh 	bwi_mac_bss_param_init(mac);
40712e36acbSWarner Losh 
40812e36acbSWarner Losh 	/*
40912e36acbSWarner Losh 	 * Initialize TX rings
41012e36acbSWarner Losh 	 */
41112e36acbSWarner Losh 	for (i = 0; i < BWI_TX_NRING; ++i) {
41212e36acbSWarner Losh 		error = sc->sc_init_tx_ring(sc, i);
41312e36acbSWarner Losh 		if (error) {
41412e36acbSWarner Losh 			device_printf(sc->sc_dev,
41512e36acbSWarner Losh 				  "can't initialize %dth TX ring\n", i);
41612e36acbSWarner Losh 			return error;
41712e36acbSWarner Losh 		}
41812e36acbSWarner Losh 	}
41912e36acbSWarner Losh 
42012e36acbSWarner Losh 	/*
42112e36acbSWarner Losh 	 * Initialize RX ring
42212e36acbSWarner Losh 	 */
42312e36acbSWarner Losh 	error = sc->sc_init_rx_ring(sc);
42412e36acbSWarner Losh 	if (error) {
42512e36acbSWarner Losh 		device_printf(sc->sc_dev, "can't initialize RX ring\n");
42612e36acbSWarner Losh 		return error;
42712e36acbSWarner Losh 	}
42812e36acbSWarner Losh 
42912e36acbSWarner Losh 	/*
43012e36acbSWarner Losh 	 * Initialize TX stats if the current MAC uses that
43112e36acbSWarner Losh 	 */
43212e36acbSWarner Losh 	if (mac->mac_flags & BWI_MAC_F_HAS_TXSTATS) {
43312e36acbSWarner Losh 		error = sc->sc_init_txstats(sc);
43412e36acbSWarner Losh 		if (error) {
43512e36acbSWarner Losh 			device_printf(sc->sc_dev,
43612e36acbSWarner Losh 				  "can't initialize TX stats ring\n");
43712e36acbSWarner Losh 			return error;
43812e36acbSWarner Losh 		}
43912e36acbSWarner Losh 	}
44012e36acbSWarner Losh 
441d83d76dfSWarner Losh 	/* update PRETBTT */
44212e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x612, 0x50);	/* Force Pre-TBTT to 80? */
44312e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, 0x416, 0x50);
44412e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, 0x414, 0x1f4);
44512e36acbSWarner Losh 
44612e36acbSWarner Losh 	mac->mac_flags |= BWI_MAC_F_INITED;
44712e36acbSWarner Losh 	return 0;
44812e36acbSWarner Losh }
44912e36acbSWarner Losh 
45012e36acbSWarner Losh void
bwi_mac_reset(struct bwi_mac * mac,int link_phy)45112e36acbSWarner Losh bwi_mac_reset(struct bwi_mac *mac, int link_phy)
45212e36acbSWarner Losh {
45312e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
45412e36acbSWarner Losh 	uint32_t flags, state_lo, status;
45512e36acbSWarner Losh 
45612e36acbSWarner Losh 	flags = BWI_STATE_LO_FLAG_PHYRST | BWI_STATE_LO_FLAG_PHYCLKEN;
45712e36acbSWarner Losh 	if (link_phy)
45812e36acbSWarner Losh 		flags |= BWI_STATE_LO_FLAG_PHYLNK;
45912e36acbSWarner Losh 	bwi_regwin_enable(sc, &mac->mac_regwin, flags);
46012e36acbSWarner Losh 	DELAY(2000);
46112e36acbSWarner Losh 
46212e36acbSWarner Losh 	state_lo = CSR_READ_4(sc, BWI_STATE_LO);
46312e36acbSWarner Losh 	state_lo |= BWI_STATE_LO_GATED_CLOCK;
46412e36acbSWarner Losh 	state_lo &= ~__SHIFTIN(BWI_STATE_LO_FLAG_PHYRST,
46512e36acbSWarner Losh 			       BWI_STATE_LO_FLAGS_MASK);
46612e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_STATE_LO, state_lo);
46712e36acbSWarner Losh 	/* Flush pending bus write */
46812e36acbSWarner Losh 	CSR_READ_4(sc, BWI_STATE_LO);
46912e36acbSWarner Losh 	DELAY(1000);
47012e36acbSWarner Losh 
47112e36acbSWarner Losh 	state_lo &= ~BWI_STATE_LO_GATED_CLOCK;
47212e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_STATE_LO, state_lo);
47312e36acbSWarner Losh 	/* Flush pending bus write */
47412e36acbSWarner Losh 	CSR_READ_4(sc, BWI_STATE_LO);
47512e36acbSWarner Losh 	DELAY(1000);
47612e36acbSWarner Losh 
47712e36acbSWarner Losh 	CSR_WRITE_2(sc, BWI_BBP_ATTEN, 0);
47812e36acbSWarner Losh 
47912e36acbSWarner Losh 	status = CSR_READ_4(sc, BWI_MAC_STATUS);
48012e36acbSWarner Losh 	status |= BWI_MAC_STATUS_IHREN;
48112e36acbSWarner Losh 	if (link_phy)
48212e36acbSWarner Losh 		status |= BWI_MAC_STATUS_PHYLNK;
48312e36acbSWarner Losh 	else
48412e36acbSWarner Losh 		status &= ~BWI_MAC_STATUS_PHYLNK;
48512e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_STATUS, status);
48612e36acbSWarner Losh 
48712e36acbSWarner Losh 	if (link_phy) {
48812e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH | BWI_DBG_INIT,
48912e36acbSWarner Losh 			"%s\n", "PHY is linked");
49012e36acbSWarner Losh 		mac->mac_phy.phy_flags |= BWI_PHY_F_LINKED;
49112e36acbSWarner Losh 	} else {
49212e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH | BWI_DBG_INIT,
49312e36acbSWarner Losh 			"%s\n", "PHY is unlinked");
49412e36acbSWarner Losh 		mac->mac_phy.phy_flags &= ~BWI_PHY_F_LINKED;
49512e36acbSWarner Losh 	}
49612e36acbSWarner Losh }
49712e36acbSWarner Losh 
49812e36acbSWarner Losh void
bwi_mac_set_tpctl_11bg(struct bwi_mac * mac,const struct bwi_tpctl * new_tpctl)49912e36acbSWarner Losh bwi_mac_set_tpctl_11bg(struct bwi_mac *mac, const struct bwi_tpctl *new_tpctl)
50012e36acbSWarner Losh {
50112e36acbSWarner Losh 	struct bwi_rf *rf = &mac->mac_rf;
50212e36acbSWarner Losh 	struct bwi_tpctl *tpctl = &mac->mac_tpctl;
50312e36acbSWarner Losh 
50412e36acbSWarner Losh 	if (new_tpctl != NULL) {
50512e36acbSWarner Losh 		KASSERT(new_tpctl->bbp_atten <= BWI_BBP_ATTEN_MAX,
50612e36acbSWarner Losh 		    ("bbp_atten %d", new_tpctl->bbp_atten));
50712e36acbSWarner Losh 		KASSERT(new_tpctl->rf_atten <=
50812e36acbSWarner Losh 			 (rf->rf_rev < 6 ? BWI_RF_ATTEN_MAX0
50912e36acbSWarner Losh 			 		 : BWI_RF_ATTEN_MAX1),
51012e36acbSWarner Losh 		    ("rf_atten %d", new_tpctl->rf_atten));
51112e36acbSWarner Losh 		KASSERT(new_tpctl->tp_ctrl1 <= BWI_TPCTL1_MAX,
51212e36acbSWarner Losh 		    ("tp_ctrl1 %d", new_tpctl->tp_ctrl1));
51312e36acbSWarner Losh 
51412e36acbSWarner Losh 		tpctl->bbp_atten = new_tpctl->bbp_atten;
51512e36acbSWarner Losh 		tpctl->rf_atten = new_tpctl->rf_atten;
51612e36acbSWarner Losh 		tpctl->tp_ctrl1 = new_tpctl->tp_ctrl1;
51712e36acbSWarner Losh 	}
51812e36acbSWarner Losh 
51912e36acbSWarner Losh 	/* Set BBP attenuation */
52012e36acbSWarner Losh 	bwi_phy_set_bbp_atten(mac, tpctl->bbp_atten);
52112e36acbSWarner Losh 
52212e36acbSWarner Losh 	/* Set RF attenuation */
52312e36acbSWarner Losh 	RF_WRITE(mac, BWI_RFR_ATTEN, tpctl->rf_atten);
52412e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_RF_ATTEN,
52512e36acbSWarner Losh 		     tpctl->rf_atten);
52612e36acbSWarner Losh 
52712e36acbSWarner Losh 	/* Set TX power */
52812e36acbSWarner Losh 	if (rf->rf_type == BWI_RF_T_BCM2050) {
52912e36acbSWarner Losh 		RF_FILT_SETBITS(mac, BWI_RFR_TXPWR, ~BWI_RFR_TXPWR1_MASK,
53012e36acbSWarner Losh 			__SHIFTIN(tpctl->tp_ctrl1, BWI_RFR_TXPWR1_MASK));
53112e36acbSWarner Losh 	}
53212e36acbSWarner Losh 
53312e36acbSWarner Losh 	/* Adjust RF Local Oscillator */
53412e36acbSWarner Losh 	if (mac->mac_phy.phy_mode == IEEE80211_MODE_11G)
53512e36acbSWarner Losh 		bwi_rf_lo_adjust(mac, tpctl);
53612e36acbSWarner Losh }
53712e36acbSWarner Losh 
53812e36acbSWarner Losh static int
bwi_mac_test(struct bwi_mac * mac)53912e36acbSWarner Losh bwi_mac_test(struct bwi_mac *mac)
54012e36acbSWarner Losh {
54112e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
54212e36acbSWarner Losh 	uint32_t orig_val, val;
54312e36acbSWarner Losh 
54412e36acbSWarner Losh #define TEST_VAL1	0xaa5555aa
54512e36acbSWarner Losh #define TEST_VAL2	0x55aaaa55
54612e36acbSWarner Losh 
54712e36acbSWarner Losh 	/* Save it for later restoring */
54812e36acbSWarner Losh 	orig_val = MOBJ_READ_4(mac, BWI_COMM_MOBJ, 0);
54912e36acbSWarner Losh 
55012e36acbSWarner Losh 	/* Test 1 */
55112e36acbSWarner Losh 	MOBJ_WRITE_4(mac, BWI_COMM_MOBJ, 0, TEST_VAL1);
55212e36acbSWarner Losh 	val = MOBJ_READ_4(mac, BWI_COMM_MOBJ, 0);
55312e36acbSWarner Losh 	if (val != TEST_VAL1) {
55412e36acbSWarner Losh 		device_printf(sc->sc_dev, "TEST1 failed\n");
55512e36acbSWarner Losh 		return ENXIO;
55612e36acbSWarner Losh 	}
55712e36acbSWarner Losh 
55812e36acbSWarner Losh 	/* Test 2 */
55912e36acbSWarner Losh 	MOBJ_WRITE_4(mac, BWI_COMM_MOBJ, 0, TEST_VAL2);
56012e36acbSWarner Losh 	val = MOBJ_READ_4(mac, BWI_COMM_MOBJ, 0);
56112e36acbSWarner Losh 	if (val != TEST_VAL2) {
56212e36acbSWarner Losh 		device_printf(sc->sc_dev, "TEST2 failed\n");
56312e36acbSWarner Losh 		return ENXIO;
56412e36acbSWarner Losh 	}
56512e36acbSWarner Losh 
56612e36acbSWarner Losh 	/* Restore to the original value */
56712e36acbSWarner Losh 	MOBJ_WRITE_4(mac, BWI_COMM_MOBJ, 0, orig_val);
56812e36acbSWarner Losh 
56912e36acbSWarner Losh 	val = CSR_READ_4(sc, BWI_MAC_STATUS);
57012e36acbSWarner Losh 	if ((val & ~BWI_MAC_STATUS_PHYLNK) != BWI_MAC_STATUS_IHREN) {
57112e36acbSWarner Losh 		device_printf(sc->sc_dev, "%s failed, MAC status 0x%08x\n",
57212e36acbSWarner Losh 			      __func__, val);
57312e36acbSWarner Losh 		return ENXIO;
57412e36acbSWarner Losh 	}
57512e36acbSWarner Losh 
57612e36acbSWarner Losh 	val = CSR_READ_4(sc, BWI_MAC_INTR_STATUS);
57712e36acbSWarner Losh 	if (val != 0) {
57812e36acbSWarner Losh 		device_printf(sc->sc_dev, "%s failed, intr status %08x\n",
57912e36acbSWarner Losh 			      __func__, val);
58012e36acbSWarner Losh 		return ENXIO;
58112e36acbSWarner Losh 	}
58212e36acbSWarner Losh 
58312e36acbSWarner Losh #undef TEST_VAL2
58412e36acbSWarner Losh #undef TEST_VAL1
58512e36acbSWarner Losh 
58612e36acbSWarner Losh 	return 0;
58712e36acbSWarner Losh }
58812e36acbSWarner Losh 
58912e36acbSWarner Losh static void
bwi_mac_setup_tpctl(struct bwi_mac * mac)59012e36acbSWarner Losh bwi_mac_setup_tpctl(struct bwi_mac *mac)
59112e36acbSWarner Losh {
59212e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
59312e36acbSWarner Losh 	struct bwi_rf *rf = &mac->mac_rf;
59412e36acbSWarner Losh 	struct bwi_phy *phy = &mac->mac_phy;
59512e36acbSWarner Losh 	struct bwi_tpctl *tpctl = &mac->mac_tpctl;
59612e36acbSWarner Losh 
59712e36acbSWarner Losh 	/* Calc BBP attenuation */
59812e36acbSWarner Losh 	if (rf->rf_type == BWI_RF_T_BCM2050 && rf->rf_rev < 6)
59912e36acbSWarner Losh 		tpctl->bbp_atten = 0;
60012e36acbSWarner Losh 	else
60112e36acbSWarner Losh 		tpctl->bbp_atten = 2;
60212e36acbSWarner Losh 
60312e36acbSWarner Losh 	/* Calc TX power CTRL1?? */
60412e36acbSWarner Losh 	tpctl->tp_ctrl1 = 0;
60512e36acbSWarner Losh 	if (rf->rf_type == BWI_RF_T_BCM2050) {
60612e36acbSWarner Losh 		if (rf->rf_rev == 1)
60712e36acbSWarner Losh 			tpctl->tp_ctrl1 = 3;
60812e36acbSWarner Losh 		else if (rf->rf_rev < 6)
60912e36acbSWarner Losh 			tpctl->tp_ctrl1 = 2;
61012e36acbSWarner Losh 		else if (rf->rf_rev == 8)
61112e36acbSWarner Losh 			tpctl->tp_ctrl1 = 1;
61212e36acbSWarner Losh 	}
61312e36acbSWarner Losh 
61412e36acbSWarner Losh 	/* Empty TX power CTRL2?? */
61512e36acbSWarner Losh 	tpctl->tp_ctrl2 = 0xffff;
61612e36acbSWarner Losh 
61712e36acbSWarner Losh 	/*
61812e36acbSWarner Losh 	 * Calc RF attenuation
61912e36acbSWarner Losh 	 */
62012e36acbSWarner Losh 	if (phy->phy_mode == IEEE80211_MODE_11A) {
62112e36acbSWarner Losh 		tpctl->rf_atten = 0x60;
62212e36acbSWarner Losh 		goto back;
62312e36acbSWarner Losh 	}
62412e36acbSWarner Losh 
62512e36acbSWarner Losh 	if (BWI_IS_BRCM_BCM4309G(sc) && sc->sc_pci_revid < 0x51) {
62612e36acbSWarner Losh 		tpctl->rf_atten = sc->sc_pci_revid < 0x43 ? 2 : 3;
62712e36acbSWarner Losh 		goto back;
62812e36acbSWarner Losh 	}
62912e36acbSWarner Losh 
63012e36acbSWarner Losh 	tpctl->rf_atten = 5;
63112e36acbSWarner Losh 
63212e36acbSWarner Losh 	if (rf->rf_type != BWI_RF_T_BCM2050) {
63312e36acbSWarner Losh 		if (rf->rf_type == BWI_RF_T_BCM2053 && rf->rf_rev == 1)
63412e36acbSWarner Losh 			tpctl->rf_atten = 6;
63512e36acbSWarner Losh 		goto back;
63612e36acbSWarner Losh 	}
63712e36acbSWarner Losh 
63812e36acbSWarner Losh 	/*
63912e36acbSWarner Losh 	 * NB: If we reaches here and the card is BRCM_BCM4309G,
64012e36acbSWarner Losh 	 *     then the card's PCI revision must >= 0x51
64112e36acbSWarner Losh 	 */
64212e36acbSWarner Losh 
64312e36acbSWarner Losh 	/* BCM2050 RF */
64412e36acbSWarner Losh 	switch (rf->rf_rev) {
64512e36acbSWarner Losh 	case 1:
64612e36acbSWarner Losh 		if (phy->phy_mode == IEEE80211_MODE_11G) {
64712e36acbSWarner Losh 			if (BWI_IS_BRCM_BCM4309G(sc) || BWI_IS_BRCM_BU4306(sc))
64812e36acbSWarner Losh 				tpctl->rf_atten = 3;
64912e36acbSWarner Losh 			else
65012e36acbSWarner Losh 				tpctl->rf_atten = 1;
65112e36acbSWarner Losh 		} else {
65212e36acbSWarner Losh 			if (BWI_IS_BRCM_BCM4309G(sc))
65312e36acbSWarner Losh 				tpctl->rf_atten = 7;
65412e36acbSWarner Losh 			else
65512e36acbSWarner Losh 				tpctl->rf_atten = 6;
65612e36acbSWarner Losh 		}
65712e36acbSWarner Losh 		break;
65812e36acbSWarner Losh 	case 2:
65912e36acbSWarner Losh 		if (phy->phy_mode == IEEE80211_MODE_11G) {
66012e36acbSWarner Losh 			/*
66112e36acbSWarner Losh 			 * NOTE: Order of following conditions is critical
66212e36acbSWarner Losh 			 */
66312e36acbSWarner Losh 			if (BWI_IS_BRCM_BCM4309G(sc))
66412e36acbSWarner Losh 				tpctl->rf_atten = 3;
66512e36acbSWarner Losh 			else if (BWI_IS_BRCM_BU4306(sc))
66612e36acbSWarner Losh 				tpctl->rf_atten = 5;
66712e36acbSWarner Losh 			else if (sc->sc_bbp_id == BWI_BBPID_BCM4320)
66812e36acbSWarner Losh 				tpctl->rf_atten = 4;
66912e36acbSWarner Losh 			else
67012e36acbSWarner Losh 				tpctl->rf_atten = 3;
67112e36acbSWarner Losh 		} else {
67212e36acbSWarner Losh 			tpctl->rf_atten = 6;
67312e36acbSWarner Losh 		}
67412e36acbSWarner Losh 		break;
67512e36acbSWarner Losh 	case 4:
67612e36acbSWarner Losh 	case 5:
67712e36acbSWarner Losh 		tpctl->rf_atten = 1;
67812e36acbSWarner Losh 		break;
67912e36acbSWarner Losh 	case 8:
68012e36acbSWarner Losh 		tpctl->rf_atten = 0x1a;
68112e36acbSWarner Losh 		break;
68212e36acbSWarner Losh 	}
68312e36acbSWarner Losh back:
68412e36acbSWarner Losh 	DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_INIT | BWI_DBG_TXPOWER,
68512e36acbSWarner Losh 		"bbp atten: %u, rf atten: %u, ctrl1: %u, ctrl2: %u\n",
68612e36acbSWarner Losh 		tpctl->bbp_atten, tpctl->rf_atten,
68712e36acbSWarner Losh 		tpctl->tp_ctrl1, tpctl->tp_ctrl2);
68812e36acbSWarner Losh }
68912e36acbSWarner Losh 
69012e36acbSWarner Losh void
bwi_mac_dummy_xmit(struct bwi_mac * mac)69112e36acbSWarner Losh bwi_mac_dummy_xmit(struct bwi_mac *mac)
69212e36acbSWarner Losh {
69312e36acbSWarner Losh #define PACKET_LEN	5
69412e36acbSWarner Losh 	static const uint32_t	packet_11a[PACKET_LEN] =
69512e36acbSWarner Losh 	{ 0x000201cc, 0x00d40000, 0x00000000, 0x01000000, 0x00000000 };
69612e36acbSWarner Losh 	static const uint32_t	packet_11bg[PACKET_LEN] =
69712e36acbSWarner Losh 	{ 0x000b846e, 0x00d40000, 0x00000000, 0x01000000, 0x00000000 };
69812e36acbSWarner Losh 
69912e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
70012e36acbSWarner Losh 	struct bwi_rf *rf = &mac->mac_rf;
70112e36acbSWarner Losh 	const uint32_t *packet;
70212e36acbSWarner Losh 	uint16_t val_50c;
70312e36acbSWarner Losh 	int wait_max, i;
70412e36acbSWarner Losh 
70512e36acbSWarner Losh 	if (mac->mac_phy.phy_mode == IEEE80211_MODE_11A) {
70612e36acbSWarner Losh 		wait_max = 30;
70712e36acbSWarner Losh 		packet = packet_11a;
70812e36acbSWarner Losh 		val_50c = 1;
70912e36acbSWarner Losh 	} else {
71012e36acbSWarner Losh 		wait_max = 250;
71112e36acbSWarner Losh 		packet = packet_11bg;
71212e36acbSWarner Losh 		val_50c = 0;
71312e36acbSWarner Losh 	}
71412e36acbSWarner Losh 
71512e36acbSWarner Losh 	for (i = 0; i < PACKET_LEN; ++i)
71612e36acbSWarner Losh 		TMPLT_WRITE_4(mac, i * 4, packet[i]);
71712e36acbSWarner Losh 
71812e36acbSWarner Losh 	CSR_READ_4(sc, BWI_MAC_STATUS);	/* dummy read */
71912e36acbSWarner Losh 
72012e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x568, 0);
72112e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x7c0, 0);
72212e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x50c, val_50c);
72312e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x508, 0);
72412e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x50a, 0);
72512e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x54c, 0);
72612e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x56a, 0x14);
72712e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x568, 0x826);
72812e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x500, 0);
72912e36acbSWarner Losh 	CSR_WRITE_2(sc, 0x502, 0x30);
73012e36acbSWarner Losh 
73112e36acbSWarner Losh 	if (rf->rf_type == BWI_RF_T_BCM2050 && rf->rf_rev <= 5)
73212e36acbSWarner Losh 		RF_WRITE(mac, 0x51, 0x17);
73312e36acbSWarner Losh 
73412e36acbSWarner Losh 	for (i = 0; i < wait_max; ++i) {
73512e36acbSWarner Losh 		if (CSR_READ_2(sc, 0x50e) & 0x80)
73612e36acbSWarner Losh 			break;
73712e36acbSWarner Losh 		DELAY(10);
73812e36acbSWarner Losh 	}
73912e36acbSWarner Losh 	for (i = 0; i < 10; ++i) {
74012e36acbSWarner Losh 		if (CSR_READ_2(sc, 0x50e) & 0x400)
74112e36acbSWarner Losh 			break;
74212e36acbSWarner Losh 		DELAY(10);
74312e36acbSWarner Losh 	}
74412e36acbSWarner Losh 	for (i = 0; i < 10; ++i) {
74512e36acbSWarner Losh 		if ((CSR_READ_2(sc, 0x690) & 0x100) == 0)
74612e36acbSWarner Losh 			break;
74712e36acbSWarner Losh 		DELAY(10);
74812e36acbSWarner Losh 	}
74912e36acbSWarner Losh 
75012e36acbSWarner Losh 	if (rf->rf_type == BWI_RF_T_BCM2050 && rf->rf_rev <= 5)
75112e36acbSWarner Losh 		RF_WRITE(mac, 0x51, 0x37);
75212e36acbSWarner Losh #undef PACKET_LEN
75312e36acbSWarner Losh }
75412e36acbSWarner Losh 
75512e36acbSWarner Losh void
bwi_mac_init_tpctl_11bg(struct bwi_mac * mac)75612e36acbSWarner Losh bwi_mac_init_tpctl_11bg(struct bwi_mac *mac)
75712e36acbSWarner Losh {
75812e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
75912e36acbSWarner Losh 	struct bwi_phy *phy = &mac->mac_phy;
76012e36acbSWarner Losh 	struct bwi_rf *rf = &mac->mac_rf;
76112e36acbSWarner Losh 	struct bwi_tpctl tpctl_orig;
76212e36acbSWarner Losh 	int restore_tpctl = 0;
76312e36acbSWarner Losh 
76412e36acbSWarner Losh 	KASSERT(phy->phy_mode != IEEE80211_MODE_11A,
76512e36acbSWarner Losh 	    ("phy_mode %d", phy->phy_mode));
76612e36acbSWarner Losh 
76712e36acbSWarner Losh 	if (BWI_IS_BRCM_BU4306(sc))
76812e36acbSWarner Losh 		return;
76912e36acbSWarner Losh 
77012e36acbSWarner Losh 	PHY_WRITE(mac, 0x28, 0x8018);
77112e36acbSWarner Losh 	CSR_CLRBITS_2(sc, BWI_BBP_ATTEN, 0x20);
77212e36acbSWarner Losh 
77312e36acbSWarner Losh 	if (phy->phy_mode == IEEE80211_MODE_11G) {
77412e36acbSWarner Losh 		if ((phy->phy_flags & BWI_PHY_F_LINKED) == 0)
77512e36acbSWarner Losh 			return;
77612e36acbSWarner Losh 		PHY_WRITE(mac, 0x47a, 0xc111);
77712e36acbSWarner Losh 	}
77812e36acbSWarner Losh 	if (mac->mac_flags & BWI_MAC_F_TPCTL_INITED)
77912e36acbSWarner Losh 		return;
78012e36acbSWarner Losh 
78112e36acbSWarner Losh 	if (phy->phy_mode == IEEE80211_MODE_11B && phy->phy_rev >= 2 &&
78212e36acbSWarner Losh 	    rf->rf_type == BWI_RF_T_BCM2050) {
78312e36acbSWarner Losh 		RF_SETBITS(mac, 0x76, 0x84);
78412e36acbSWarner Losh 	} else {
78512e36acbSWarner Losh 		struct bwi_tpctl tpctl;
78612e36acbSWarner Losh 
78712e36acbSWarner Losh 		/* Backup original TX power control variables */
78812e36acbSWarner Losh 		bcopy(&mac->mac_tpctl, &tpctl_orig, sizeof(tpctl_orig));
78912e36acbSWarner Losh 		restore_tpctl = 1;
79012e36acbSWarner Losh 
79112e36acbSWarner Losh 		bcopy(&mac->mac_tpctl, &tpctl, sizeof(tpctl));
79212e36acbSWarner Losh 		tpctl.bbp_atten = 11;
79312e36acbSWarner Losh 		tpctl.tp_ctrl1 = 0;
79412e36acbSWarner Losh #ifdef notyet
79512e36acbSWarner Losh 		if (rf->rf_rev >= 6 && rf->rf_rev <= 8)
79612e36acbSWarner Losh 			tpctl.rf_atten = 31;
79712e36acbSWarner Losh 		else
79812e36acbSWarner Losh #endif
79912e36acbSWarner Losh 			tpctl.rf_atten = 9;
80012e36acbSWarner Losh 
80112e36acbSWarner Losh 		bwi_mac_set_tpctl_11bg(mac, &tpctl);
80212e36acbSWarner Losh 	}
80312e36acbSWarner Losh 
80412e36acbSWarner Losh 	bwi_mac_dummy_xmit(mac);
80512e36acbSWarner Losh 
80612e36acbSWarner Losh 	mac->mac_flags |= BWI_MAC_F_TPCTL_INITED;
80712e36acbSWarner Losh 	rf->rf_base_tssi = PHY_READ(mac, 0x29);
80812e36acbSWarner Losh 	DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_INIT | BWI_DBG_TXPOWER,
80912e36acbSWarner Losh 		"base tssi %d\n", rf->rf_base_tssi);
81012e36acbSWarner Losh 
81112e36acbSWarner Losh 	if (abs(rf->rf_base_tssi - rf->rf_idle_tssi) >= 20) {
81212e36acbSWarner Losh 		device_printf(sc->sc_dev, "base tssi measure failed\n");
81312e36acbSWarner Losh 		mac->mac_flags |= BWI_MAC_F_TPCTL_ERROR;
81412e36acbSWarner Losh 	}
81512e36acbSWarner Losh 
81612e36acbSWarner Losh 	if (restore_tpctl)
81712e36acbSWarner Losh 		bwi_mac_set_tpctl_11bg(mac, &tpctl_orig);
81812e36acbSWarner Losh 	else
81912e36acbSWarner Losh 		RF_CLRBITS(mac, 0x76, 0x84);
82012e36acbSWarner Losh 
82112e36acbSWarner Losh 	bwi_rf_clear_tssi(mac);
82212e36acbSWarner Losh }
82312e36acbSWarner Losh 
82412e36acbSWarner Losh void
bwi_mac_detach(struct bwi_mac * mac)82512e36acbSWarner Losh bwi_mac_detach(struct bwi_mac *mac)
82612e36acbSWarner Losh {
82712e36acbSWarner Losh 	bwi_mac_fw_free(mac);
82812e36acbSWarner Losh }
82912e36acbSWarner Losh 
83012e36acbSWarner Losh static __inline int
bwi_fwimage_is_valid(struct bwi_softc * sc,const struct firmware * fw,uint8_t fw_type)83112e36acbSWarner Losh bwi_fwimage_is_valid(struct bwi_softc *sc, const struct firmware *fw,
83212e36acbSWarner Losh 		     uint8_t fw_type)
83312e36acbSWarner Losh {
83412e36acbSWarner Losh 	const struct bwi_fwhdr *hdr;
83512e36acbSWarner Losh 
83612e36acbSWarner Losh 	if (fw->datasize < sizeof(*hdr)) {
8377a79cebfSGleb Smirnoff 		device_printf(sc->sc_dev,
8387a79cebfSGleb Smirnoff 		    "invalid firmware (%s): invalid size %zu\n",
83912e36acbSWarner Losh 		    fw->name, fw->datasize);
84012e36acbSWarner Losh 		return 0;
84112e36acbSWarner Losh 	}
84212e36acbSWarner Losh 
84312e36acbSWarner Losh 	hdr = (const struct bwi_fwhdr *)fw->data;
84412e36acbSWarner Losh 
84512e36acbSWarner Losh 	if (fw_type != BWI_FW_T_IV) {
84612e36acbSWarner Losh 		/*
84712e36acbSWarner Losh 		 * Don't verify IV's size, it has different meaning
84812e36acbSWarner Losh 		 */
84912e36acbSWarner Losh 		if (be32toh(hdr->fw_size) != fw->datasize - sizeof(*hdr)) {
8507a79cebfSGleb Smirnoff 			device_printf(sc->sc_dev,
8517a79cebfSGleb Smirnoff 			    "invalid firmware (%s): size mismatch, "
85212e36acbSWarner Losh 			    "fw %u, real %zu\n", fw->name,
8537a79cebfSGleb Smirnoff 			    be32toh(hdr->fw_size), fw->datasize - sizeof(*hdr));
85412e36acbSWarner Losh 			return 0;
85512e36acbSWarner Losh 		}
85612e36acbSWarner Losh 	}
85712e36acbSWarner Losh 
85812e36acbSWarner Losh 	if (hdr->fw_type != fw_type) {
8597a79cebfSGleb Smirnoff 		device_printf(sc->sc_dev,
8607a79cebfSGleb Smirnoff 		    "invalid firmware (%s): type mismatch, "
86112e36acbSWarner Losh 		    "fw \'%c\', target \'%c\'\n", fw->name,
86212e36acbSWarner Losh 		    hdr->fw_type, fw_type);
86312e36acbSWarner Losh 		return 0;
86412e36acbSWarner Losh 	}
86512e36acbSWarner Losh 
86612e36acbSWarner Losh 	if (hdr->fw_gen != BWI_FW_GEN_1) {
8677a79cebfSGleb Smirnoff 		device_printf(sc->sc_dev,
8687a79cebfSGleb Smirnoff 		    "invalid firmware (%s): wrong generation, "
8697a79cebfSGleb Smirnoff 		    "fw %d, target %d\n", fw->name, hdr->fw_gen, BWI_FW_GEN_1);
87012e36acbSWarner Losh 		return 0;
87112e36acbSWarner Losh 	}
87212e36acbSWarner Losh 	return 1;
87312e36acbSWarner Losh }
87412e36acbSWarner Losh 
87512e36acbSWarner Losh /*
87612e36acbSWarner Losh  * XXX Error cleanup
87712e36acbSWarner Losh  */
878c803f24bSGleb Smirnoff int
bwi_mac_fw_alloc(struct bwi_mac * mac)87912e36acbSWarner Losh bwi_mac_fw_alloc(struct bwi_mac *mac)
88012e36acbSWarner Losh {
88112e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
88212e36acbSWarner Losh 	char fwname[64];
88312e36acbSWarner Losh 	int idx;
88412e36acbSWarner Losh 
88512e36acbSWarner Losh 	/*
88612e36acbSWarner Losh 	 * Try getting the firmware stub so firmware
88712e36acbSWarner Losh 	 * module would be loaded automatically
88812e36acbSWarner Losh 	 */
88912e36acbSWarner Losh 	if (mac->mac_stub == NULL) {
89012e36acbSWarner Losh 		snprintf(fwname, sizeof(fwname), BWI_FW_STUB_PATH,
89112e36acbSWarner Losh 			 sc->sc_fw_version);
89212e36acbSWarner Losh 		mac->mac_stub = firmware_get(fwname);
89315692978SGleb Smirnoff 		if (mac->mac_stub == NULL)
89415692978SGleb Smirnoff 			goto no_firmware;
89512e36acbSWarner Losh 	}
89612e36acbSWarner Losh 
89712e36acbSWarner Losh 	if (mac->mac_ucode == NULL) {
89812e36acbSWarner Losh 		snprintf(fwname, sizeof(fwname), BWI_FW_UCODE_PATH,
89912e36acbSWarner Losh 			  sc->sc_fw_version,
90012e36acbSWarner Losh 			  mac->mac_rev >= 5 ? 5 : mac->mac_rev);
90112e36acbSWarner Losh 
90212e36acbSWarner Losh 		mac->mac_ucode = firmware_get(fwname);
90315692978SGleb Smirnoff 		if (mac->mac_ucode == NULL)
90415692978SGleb Smirnoff 			goto no_firmware;
90512e36acbSWarner Losh 		if (!bwi_fwimage_is_valid(sc, mac->mac_ucode, BWI_FW_T_UCODE))
90612e36acbSWarner Losh 			return EINVAL;
90712e36acbSWarner Losh 	}
90812e36acbSWarner Losh 
90912e36acbSWarner Losh 	if (mac->mac_pcm == NULL) {
91012e36acbSWarner Losh 		snprintf(fwname, sizeof(fwname), BWI_FW_PCM_PATH,
91112e36acbSWarner Losh 			  sc->sc_fw_version,
91212e36acbSWarner Losh 			  mac->mac_rev < 5 ? 4 : 5);
91312e36acbSWarner Losh 
91412e36acbSWarner Losh 		mac->mac_pcm = firmware_get(fwname);
91515692978SGleb Smirnoff 		if (mac->mac_pcm == NULL)
91615692978SGleb Smirnoff 			goto no_firmware;
91712e36acbSWarner Losh 		if (!bwi_fwimage_is_valid(sc, mac->mac_pcm, BWI_FW_T_PCM))
91812e36acbSWarner Losh 			return EINVAL;
91912e36acbSWarner Losh 	}
92012e36acbSWarner Losh 
92112e36acbSWarner Losh 	if (mac->mac_iv == NULL) {
92212e36acbSWarner Losh 		/* TODO: 11A */
92312e36acbSWarner Losh 		if (mac->mac_rev == 2 || mac->mac_rev == 4) {
92412e36acbSWarner Losh 			idx = 2;
92512e36acbSWarner Losh 		} else if (mac->mac_rev >= 5 && mac->mac_rev <= 10) {
92612e36acbSWarner Losh 			idx = 5;
92712e36acbSWarner Losh 		} else {
92815692978SGleb Smirnoff 			device_printf(sc->sc_dev,
92915692978SGleb Smirnoff 			    "no suitible IV for MAC rev %d\n", mac->mac_rev);
93012e36acbSWarner Losh 			return ENODEV;
93112e36acbSWarner Losh 		}
93212e36acbSWarner Losh 
93312e36acbSWarner Losh 		snprintf(fwname, sizeof(fwname), BWI_FW_IV_PATH,
93412e36acbSWarner Losh 			  sc->sc_fw_version, idx);
93512e36acbSWarner Losh 
93612e36acbSWarner Losh 		mac->mac_iv = firmware_get(fwname);
93715692978SGleb Smirnoff 		if (mac->mac_iv == NULL)
93815692978SGleb Smirnoff 			goto no_firmware;
93912e36acbSWarner Losh 		if (!bwi_fwimage_is_valid(sc, mac->mac_iv, BWI_FW_T_IV))
94012e36acbSWarner Losh 			return EINVAL;
94112e36acbSWarner Losh 	}
94212e36acbSWarner Losh 
94312e36acbSWarner Losh 	if (mac->mac_iv_ext == NULL) {
94412e36acbSWarner Losh 		/* TODO: 11A */
94512e36acbSWarner Losh 		if (mac->mac_rev == 2 || mac->mac_rev == 4 ||
94612e36acbSWarner Losh 		    mac->mac_rev >= 11) {
94712e36acbSWarner Losh 			/* No extended IV */
94815692978SGleb Smirnoff 			return (0);
94912e36acbSWarner Losh 		} else if (mac->mac_rev >= 5 && mac->mac_rev <= 10) {
95012e36acbSWarner Losh 			idx = 5;
95112e36acbSWarner Losh 		} else {
95215692978SGleb Smirnoff 			device_printf(sc->sc_dev,
95315692978SGleb Smirnoff 			    "no suitible ExtIV for MAC rev %d\n", mac->mac_rev);
95412e36acbSWarner Losh 			return ENODEV;
95512e36acbSWarner Losh 		}
95612e36acbSWarner Losh 
95712e36acbSWarner Losh 		snprintf(fwname, sizeof(fwname), BWI_FW_IV_EXT_PATH,
95812e36acbSWarner Losh 			  sc->sc_fw_version, idx);
95912e36acbSWarner Losh 
96012e36acbSWarner Losh 		mac->mac_iv_ext = firmware_get(fwname);
96115692978SGleb Smirnoff 		if (mac->mac_iv_ext == NULL)
96215692978SGleb Smirnoff 			goto no_firmware;
96312e36acbSWarner Losh 		if (!bwi_fwimage_is_valid(sc, mac->mac_iv_ext, BWI_FW_T_IV))
96412e36acbSWarner Losh 			return EINVAL;
96512e36acbSWarner Losh 	}
96615692978SGleb Smirnoff 	return (0);
96715692978SGleb Smirnoff 
96815692978SGleb Smirnoff no_firmware:
96915692978SGleb Smirnoff 	device_printf(sc->sc_dev, "request firmware %s failed\n", fwname);
97015692978SGleb Smirnoff 	return (ENOENT);
97112e36acbSWarner Losh }
97212e36acbSWarner Losh 
97312e36acbSWarner Losh static void
bwi_mac_fw_free(struct bwi_mac * mac)97412e36acbSWarner Losh bwi_mac_fw_free(struct bwi_mac *mac)
97512e36acbSWarner Losh {
97612e36acbSWarner Losh 	if (mac->mac_ucode != NULL) {
97712e36acbSWarner Losh 		firmware_put(mac->mac_ucode, FIRMWARE_UNLOAD);
97812e36acbSWarner Losh 		mac->mac_ucode = NULL;
97912e36acbSWarner Losh 	}
98012e36acbSWarner Losh 
98112e36acbSWarner Losh 	if (mac->mac_pcm != NULL) {
98212e36acbSWarner Losh 		firmware_put(mac->mac_pcm, FIRMWARE_UNLOAD);
98312e36acbSWarner Losh 		mac->mac_pcm = NULL;
98412e36acbSWarner Losh 	}
98512e36acbSWarner Losh 
98612e36acbSWarner Losh 	if (mac->mac_iv != NULL) {
98712e36acbSWarner Losh 		firmware_put(mac->mac_iv, FIRMWARE_UNLOAD);
98812e36acbSWarner Losh 		mac->mac_iv = NULL;
98912e36acbSWarner Losh 	}
99012e36acbSWarner Losh 
99112e36acbSWarner Losh 	if (mac->mac_iv_ext != NULL) {
99212e36acbSWarner Losh 		firmware_put(mac->mac_iv_ext, FIRMWARE_UNLOAD);
99312e36acbSWarner Losh 		mac->mac_iv_ext = NULL;
99412e36acbSWarner Losh 	}
99512e36acbSWarner Losh 
99612e36acbSWarner Losh 	if (mac->mac_stub != NULL) {
99712e36acbSWarner Losh 		firmware_put(mac->mac_stub, FIRMWARE_UNLOAD);
99812e36acbSWarner Losh 		mac->mac_stub = NULL;
99912e36acbSWarner Losh 	}
100012e36acbSWarner Losh }
100112e36acbSWarner Losh 
100212e36acbSWarner Losh static int
bwi_mac_fw_load(struct bwi_mac * mac)100312e36acbSWarner Losh bwi_mac_fw_load(struct bwi_mac *mac)
100412e36acbSWarner Losh {
100512e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
100612e36acbSWarner Losh 	const uint32_t *fw;
100712e36acbSWarner Losh 	uint16_t fw_rev;
100812e36acbSWarner Losh 	int fw_len, i;
100912e36acbSWarner Losh 
101012e36acbSWarner Losh 	/*
101112e36acbSWarner Losh 	 * Load ucode image
101212e36acbSWarner Losh 	 */
101312e36acbSWarner Losh 	fw = (const uint32_t *)
101412e36acbSWarner Losh 	     ((const uint8_t *)mac->mac_ucode->data + BWI_FWHDR_SZ);
101512e36acbSWarner Losh 	fw_len = (mac->mac_ucode->datasize - BWI_FWHDR_SZ) / sizeof(uint32_t);
101612e36acbSWarner Losh 
101712e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
101812e36acbSWarner Losh 		    BWI_MOBJ_CTRL_VAL(
101912e36acbSWarner Losh 		    BWI_FW_UCODE_MOBJ | BWI_WR_MOBJ_AUTOINC, 0));
102012e36acbSWarner Losh 	for (i = 0; i < fw_len; ++i) {
102112e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_MOBJ_DATA, be32toh(fw[i]));
102212e36acbSWarner Losh 		DELAY(10);
102312e36acbSWarner Losh 	}
102412e36acbSWarner Losh 
102512e36acbSWarner Losh 	/*
102612e36acbSWarner Losh 	 * Load PCM image
102712e36acbSWarner Losh 	 */
102812e36acbSWarner Losh 	fw = (const uint32_t *)
102912e36acbSWarner Losh 	     ((const uint8_t *)mac->mac_pcm->data + BWI_FWHDR_SZ);
103012e36acbSWarner Losh 	fw_len = (mac->mac_pcm->datasize - BWI_FWHDR_SZ) / sizeof(uint32_t);
103112e36acbSWarner Losh 
103212e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
103312e36acbSWarner Losh 		    BWI_MOBJ_CTRL_VAL(BWI_FW_PCM_MOBJ, 0x01ea));
103412e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MOBJ_DATA, 0x4000);
103512e36acbSWarner Losh 
103612e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MOBJ_CTRL,
103712e36acbSWarner Losh 		    BWI_MOBJ_CTRL_VAL(BWI_FW_PCM_MOBJ, 0x01eb));
103812e36acbSWarner Losh 	for (i = 0; i < fw_len; ++i) {
103912e36acbSWarner Losh 		CSR_WRITE_4(sc, BWI_MOBJ_DATA, be32toh(fw[i]));
104012e36acbSWarner Losh 		DELAY(10);
104112e36acbSWarner Losh 	}
104212e36acbSWarner Losh 
104312e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_INTR_STATUS, BWI_ALL_INTRS);
104412e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_STATUS,
104512e36acbSWarner Losh 		    BWI_MAC_STATUS_UCODE_START |
104612e36acbSWarner Losh 		    BWI_MAC_STATUS_IHREN |
104712e36acbSWarner Losh 		    BWI_MAC_STATUS_INFRA);
104812e36acbSWarner Losh 
104912e36acbSWarner Losh #define NRETRY	200
105012e36acbSWarner Losh 
105112e36acbSWarner Losh 	for (i = 0; i < NRETRY; ++i) {
105212e36acbSWarner Losh 		uint32_t intr_status;
105312e36acbSWarner Losh 
105412e36acbSWarner Losh 		intr_status = CSR_READ_4(sc, BWI_MAC_INTR_STATUS);
105512e36acbSWarner Losh 		if (intr_status == BWI_INTR_READY)
105612e36acbSWarner Losh 			break;
105712e36acbSWarner Losh 		DELAY(10);
105812e36acbSWarner Losh 	}
105912e36acbSWarner Losh 	if (i == NRETRY) {
10607a79cebfSGleb Smirnoff 		device_printf(sc->sc_dev,
10617a79cebfSGleb Smirnoff 		    "firmware (ucode&pcm) loading timed out\n");
106212e36acbSWarner Losh 		return ETIMEDOUT;
106312e36acbSWarner Losh 	}
106412e36acbSWarner Losh 
106512e36acbSWarner Losh #undef NRETRY
106612e36acbSWarner Losh 
106712e36acbSWarner Losh 	CSR_READ_4(sc, BWI_MAC_INTR_STATUS);	/* dummy read */
106812e36acbSWarner Losh 
106912e36acbSWarner Losh 	fw_rev = MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_FWREV);
107012e36acbSWarner Losh 	if (fw_rev > BWI_FW_VERSION3_REVMAX) {
10717a79cebfSGleb Smirnoff 		device_printf(sc->sc_dev,
10727a79cebfSGleb Smirnoff 		    "firmware version 4 is not supported yet\n");
107312e36acbSWarner Losh 		return ENODEV;
107412e36acbSWarner Losh 	}
107512e36acbSWarner Losh 
10767a79cebfSGleb Smirnoff 	device_printf(sc->sc_dev,
10777a79cebfSGleb Smirnoff 	    "firmware rev 0x%04x, patch level 0x%04x\n", fw_rev,
107812e36acbSWarner Losh 	    MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_FWPATCHLV));
107912e36acbSWarner Losh 	return 0;
108012e36acbSWarner Losh }
108112e36acbSWarner Losh 
108212e36acbSWarner Losh static int
bwi_mac_gpio_init(struct bwi_mac * mac)108312e36acbSWarner Losh bwi_mac_gpio_init(struct bwi_mac *mac)
108412e36acbSWarner Losh {
108512e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
108612e36acbSWarner Losh 	struct bwi_regwin *old, *gpio_rw;
108712e36acbSWarner Losh 	uint32_t filt, bits;
108812e36acbSWarner Losh 	int error;
108912e36acbSWarner Losh 
109012e36acbSWarner Losh 	CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_GPOSEL_MASK);
109112e36acbSWarner Losh 	/* TODO:LED */
109212e36acbSWarner Losh 
109312e36acbSWarner Losh 	CSR_SETBITS_2(sc, BWI_MAC_GPIO_MASK, 0xf);
109412e36acbSWarner Losh 
109512e36acbSWarner Losh 	filt = 0x1f;
109612e36acbSWarner Losh 	bits = 0xf;
109712e36acbSWarner Losh 	if (sc->sc_bbp_id == BWI_BBPID_BCM4301) {
109812e36acbSWarner Losh 		filt |= 0x60;
109912e36acbSWarner Losh 		bits |= 0x60;
110012e36acbSWarner Losh 	}
110112e36acbSWarner Losh 	if (sc->sc_card_flags & BWI_CARD_F_PA_GPIO9) {
110212e36acbSWarner Losh 		CSR_SETBITS_2(sc, BWI_MAC_GPIO_MASK, 0x200);
110312e36acbSWarner Losh 		filt |= 0x200;
110412e36acbSWarner Losh 		bits |= 0x200;
110512e36acbSWarner Losh 	}
110612e36acbSWarner Losh 
110712e36acbSWarner Losh 	gpio_rw = BWI_GPIO_REGWIN(sc);
110812e36acbSWarner Losh 	error = bwi_regwin_switch(sc, gpio_rw, &old);
110912e36acbSWarner Losh 	if (error)
111012e36acbSWarner Losh 		return error;
111112e36acbSWarner Losh 
111212e36acbSWarner Losh 	CSR_FILT_SETBITS_4(sc, BWI_GPIO_CTRL, filt, bits);
111312e36acbSWarner Losh 
111412e36acbSWarner Losh 	return bwi_regwin_switch(sc, old, NULL);
111512e36acbSWarner Losh }
111612e36acbSWarner Losh 
111712e36acbSWarner Losh static int
bwi_mac_gpio_fini(struct bwi_mac * mac)111812e36acbSWarner Losh bwi_mac_gpio_fini(struct bwi_mac *mac)
111912e36acbSWarner Losh {
112012e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
112112e36acbSWarner Losh 	struct bwi_regwin *old, *gpio_rw;
112212e36acbSWarner Losh 	int error;
112312e36acbSWarner Losh 
112412e36acbSWarner Losh 	gpio_rw = BWI_GPIO_REGWIN(sc);
112512e36acbSWarner Losh 	error = bwi_regwin_switch(sc, gpio_rw, &old);
112612e36acbSWarner Losh 	if (error)
112712e36acbSWarner Losh 		return error;
112812e36acbSWarner Losh 
112912e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_GPIO_CTRL, 0);
113012e36acbSWarner Losh 
113112e36acbSWarner Losh 	return bwi_regwin_switch(sc, old, NULL);
113212e36acbSWarner Losh }
113312e36acbSWarner Losh 
113412e36acbSWarner Losh static int
bwi_mac_fw_load_iv(struct bwi_mac * mac,const struct firmware * fw)113512e36acbSWarner Losh bwi_mac_fw_load_iv(struct bwi_mac *mac, const struct firmware *fw)
113612e36acbSWarner Losh {
113712e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
113812e36acbSWarner Losh 	const struct bwi_fwhdr *hdr;
113912e36acbSWarner Losh 	const struct bwi_fw_iv *iv;
114012e36acbSWarner Losh 	int n, i, iv_img_size;
114112e36acbSWarner Losh 
114212e36acbSWarner Losh 	/* Get the number of IVs in the IV image */
114312e36acbSWarner Losh 	hdr = (const struct bwi_fwhdr *)fw->data;
114412e36acbSWarner Losh 	n = be32toh(hdr->fw_iv_cnt);
114512e36acbSWarner Losh 	DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_INIT | BWI_DBG_FIRMWARE,
114612e36acbSWarner Losh 		"IV count %d\n", n);
114712e36acbSWarner Losh 
114812e36acbSWarner Losh 	/* Calculate the IV image size, for later sanity check */
114912e36acbSWarner Losh 	iv_img_size = fw->datasize - sizeof(*hdr);
115012e36acbSWarner Losh 
115112e36acbSWarner Losh 	/* Locate the first IV */
115212e36acbSWarner Losh 	iv = (const struct bwi_fw_iv *)
115312e36acbSWarner Losh 	     ((const uint8_t *)fw->data + sizeof(*hdr));
115412e36acbSWarner Losh 
115512e36acbSWarner Losh 	for (i = 0; i < n; ++i) {
115612e36acbSWarner Losh 		uint16_t iv_ofs, ofs;
115712e36acbSWarner Losh 		int sz = 0;
115812e36acbSWarner Losh 
115912e36acbSWarner Losh 		if (iv_img_size < sizeof(iv->iv_ofs)) {
11607a79cebfSGleb Smirnoff 			device_printf(sc->sc_dev, "invalid IV image, ofs\n");
116112e36acbSWarner Losh 			return EINVAL;
116212e36acbSWarner Losh 		}
116312e36acbSWarner Losh 		iv_img_size -= sizeof(iv->iv_ofs);
116412e36acbSWarner Losh 		sz += sizeof(iv->iv_ofs);
116512e36acbSWarner Losh 
116612e36acbSWarner Losh 		iv_ofs = be16toh(iv->iv_ofs);
116712e36acbSWarner Losh 
116812e36acbSWarner Losh 		ofs = __SHIFTOUT(iv_ofs, BWI_FW_IV_OFS_MASK);
116912e36acbSWarner Losh 		if (ofs >= 0x1000) {
11707a79cebfSGleb Smirnoff 			device_printf(sc->sc_dev, "invalid ofs (0x%04x) "
117112e36acbSWarner Losh 				  "for %dth iv\n", ofs, i);
117212e36acbSWarner Losh 			return EINVAL;
117312e36acbSWarner Losh 		}
117412e36acbSWarner Losh 
117512e36acbSWarner Losh 		if (iv_ofs & BWI_FW_IV_IS_32BIT) {
117612e36acbSWarner Losh 			uint32_t val32;
117712e36acbSWarner Losh 
117812e36acbSWarner Losh 			if (iv_img_size < sizeof(iv->iv_val.val32)) {
11797a79cebfSGleb Smirnoff 				device_printf(sc->sc_dev,
11807a79cebfSGleb Smirnoff 				    "invalid IV image, val32\n");
118112e36acbSWarner Losh 				return EINVAL;
118212e36acbSWarner Losh 			}
118312e36acbSWarner Losh 			iv_img_size -= sizeof(iv->iv_val.val32);
118412e36acbSWarner Losh 			sz += sizeof(iv->iv_val.val32);
118512e36acbSWarner Losh 
118612e36acbSWarner Losh 			val32 = be32toh(iv->iv_val.val32);
118712e36acbSWarner Losh 			CSR_WRITE_4(sc, ofs, val32);
118812e36acbSWarner Losh 		} else {
118912e36acbSWarner Losh 			uint16_t val16;
119012e36acbSWarner Losh 
119112e36acbSWarner Losh 			if (iv_img_size < sizeof(iv->iv_val.val16)) {
11927a79cebfSGleb Smirnoff 				device_printf(sc->sc_dev,
11937a79cebfSGleb Smirnoff 				    "invalid IV image, val16\n");
119412e36acbSWarner Losh 				return EINVAL;
119512e36acbSWarner Losh 			}
119612e36acbSWarner Losh 			iv_img_size -= sizeof(iv->iv_val.val16);
119712e36acbSWarner Losh 			sz += sizeof(iv->iv_val.val16);
119812e36acbSWarner Losh 
119912e36acbSWarner Losh 			val16 = be16toh(iv->iv_val.val16);
120012e36acbSWarner Losh 			CSR_WRITE_2(sc, ofs, val16);
120112e36acbSWarner Losh 		}
120212e36acbSWarner Losh 
120312e36acbSWarner Losh 		iv = (const struct bwi_fw_iv *)((const uint8_t *)iv + sz);
120412e36acbSWarner Losh 	}
120512e36acbSWarner Losh 
120612e36acbSWarner Losh 	if (iv_img_size != 0) {
12077a79cebfSGleb Smirnoff 		device_printf(sc->sc_dev, "invalid IV image, size left %d\n",
12087a79cebfSGleb Smirnoff 		    iv_img_size);
120912e36acbSWarner Losh 		return EINVAL;
121012e36acbSWarner Losh 	}
121112e36acbSWarner Losh 	return 0;
121212e36acbSWarner Losh }
121312e36acbSWarner Losh 
121412e36acbSWarner Losh static int
bwi_mac_fw_init(struct bwi_mac * mac)121512e36acbSWarner Losh bwi_mac_fw_init(struct bwi_mac *mac)
121612e36acbSWarner Losh {
12177a79cebfSGleb Smirnoff 	device_t dev = mac->mac_sc->sc_dev;
121812e36acbSWarner Losh 	int error;
121912e36acbSWarner Losh 
122012e36acbSWarner Losh 	error = bwi_mac_fw_load_iv(mac, mac->mac_iv);
122112e36acbSWarner Losh 	if (error) {
12227a79cebfSGleb Smirnoff 		device_printf(dev, "load IV failed\n");
122312e36acbSWarner Losh 		return error;
122412e36acbSWarner Losh 	}
122512e36acbSWarner Losh 
122612e36acbSWarner Losh 	if (mac->mac_iv_ext != NULL) {
122712e36acbSWarner Losh 		error = bwi_mac_fw_load_iv(mac, mac->mac_iv_ext);
122812e36acbSWarner Losh 		if (error)
12297a79cebfSGleb Smirnoff 			device_printf(dev, "load ExtIV failed\n");
123012e36acbSWarner Losh 	}
123112e36acbSWarner Losh 	return error;
123212e36acbSWarner Losh }
123312e36acbSWarner Losh 
123412e36acbSWarner Losh static void
bwi_mac_opmode_init(struct bwi_mac * mac)123512e36acbSWarner Losh bwi_mac_opmode_init(struct bwi_mac *mac)
123612e36acbSWarner Losh {
123712e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
12387a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
123912e36acbSWarner Losh 	uint32_t mac_status;
124012e36acbSWarner Losh 	uint16_t pre_tbtt;
124112e36acbSWarner Losh 
124212e36acbSWarner Losh 	CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_INFRA);
124312e36acbSWarner Losh 	CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_INFRA);
124412e36acbSWarner Losh 
124512e36acbSWarner Losh 	/* Set probe resp timeout to infinite */
124612e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_PROBE_RESP_TO, 0);
124712e36acbSWarner Losh 
124812e36acbSWarner Losh 	/*
124912e36acbSWarner Losh 	 * TODO: factor out following part
125012e36acbSWarner Losh 	 */
125112e36acbSWarner Losh 
125212e36acbSWarner Losh 	mac_status = CSR_READ_4(sc, BWI_MAC_STATUS);
125312e36acbSWarner Losh 	mac_status &= ~(BWI_MAC_STATUS_OPMODE_HOSTAP |
125412e36acbSWarner Losh 			BWI_MAC_STATUS_PASS_CTL |
125512e36acbSWarner Losh 			BWI_MAC_STATUS_PASS_BCN |
125612e36acbSWarner Losh 			BWI_MAC_STATUS_PASS_BADPLCP |
125712e36acbSWarner Losh 			BWI_MAC_STATUS_PASS_BADFCS |
125812e36acbSWarner Losh 			BWI_MAC_STATUS_PROMISC);
125912e36acbSWarner Losh 	mac_status |= BWI_MAC_STATUS_INFRA;
126012e36acbSWarner Losh 
126112e36acbSWarner Losh 	/* Always turn on PROMISC on old hardware */
126212e36acbSWarner Losh 	if (mac->mac_rev < 5)
126312e36acbSWarner Losh 		mac_status |= BWI_MAC_STATUS_PROMISC;
126412e36acbSWarner Losh 
126512e36acbSWarner Losh 	switch (ic->ic_opmode) {
126612e36acbSWarner Losh 	case IEEE80211_M_IBSS:
126712e36acbSWarner Losh 		mac_status &= ~BWI_MAC_STATUS_INFRA;
126812e36acbSWarner Losh 		break;
126912e36acbSWarner Losh 	case IEEE80211_M_HOSTAP:
127012e36acbSWarner Losh 		mac_status |= BWI_MAC_STATUS_OPMODE_HOSTAP;
127112e36acbSWarner Losh 		break;
127212e36acbSWarner Losh 	case IEEE80211_M_MONITOR:
127312e36acbSWarner Losh #if 0
127412e36acbSWarner Losh 		/* Do you want data from your microwave oven? */
127512e36acbSWarner Losh 		mac_status |= BWI_MAC_STATUS_PASS_CTL |
127612e36acbSWarner Losh 			      BWI_MAC_STATUS_PASS_BADPLCP |
127712e36acbSWarner Losh 			      BWI_MAC_STATUS_PASS_BADFCS;
127812e36acbSWarner Losh #else
127912e36acbSWarner Losh 		mac_status |= BWI_MAC_STATUS_PASS_CTL;
128012e36acbSWarner Losh #endif
128112e36acbSWarner Losh 		/* Promisc? */
128212e36acbSWarner Losh 		break;
128312e36acbSWarner Losh 	default:
128412e36acbSWarner Losh 		break;
128512e36acbSWarner Losh 	}
128612e36acbSWarner Losh 
12877a79cebfSGleb Smirnoff 	if (ic->ic_promisc > 0)
128812e36acbSWarner Losh 		mac_status |= BWI_MAC_STATUS_PROMISC;
128912e36acbSWarner Losh 
129012e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_STATUS, mac_status);
129112e36acbSWarner Losh 
129212e36acbSWarner Losh 	if (ic->ic_opmode != IEEE80211_M_IBSS &&
129312e36acbSWarner Losh 	    ic->ic_opmode != IEEE80211_M_HOSTAP) {
129412e36acbSWarner Losh 		if (sc->sc_bbp_id == BWI_BBPID_BCM4306 && sc->sc_bbp_rev == 3)
129512e36acbSWarner Losh 			pre_tbtt = 100;
129612e36acbSWarner Losh 		else
129712e36acbSWarner Losh 			pre_tbtt = 50;
129812e36acbSWarner Losh 	} else {
129912e36acbSWarner Losh 		pre_tbtt = 2;
130012e36acbSWarner Losh 	}
130112e36acbSWarner Losh 	CSR_WRITE_2(sc, BWI_MAC_PRE_TBTT, pre_tbtt);
130212e36acbSWarner Losh }
130312e36acbSWarner Losh 
130412e36acbSWarner Losh static void
bwi_mac_hostflags_init(struct bwi_mac * mac)130512e36acbSWarner Losh bwi_mac_hostflags_init(struct bwi_mac *mac)
130612e36acbSWarner Losh {
130712e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
130812e36acbSWarner Losh 	struct bwi_phy *phy = &mac->mac_phy;
130912e36acbSWarner Losh 	struct bwi_rf *rf = &mac->mac_rf;
131012e36acbSWarner Losh 	uint64_t host_flags;
131112e36acbSWarner Losh 
131212e36acbSWarner Losh 	if (phy->phy_mode == IEEE80211_MODE_11A)
131312e36acbSWarner Losh 		return;
131412e36acbSWarner Losh 
131512e36acbSWarner Losh 	host_flags = HFLAGS_READ(mac);
131612e36acbSWarner Losh 	host_flags |= BWI_HFLAG_SYM_WA;
131712e36acbSWarner Losh 
131812e36acbSWarner Losh 	if (phy->phy_mode == IEEE80211_MODE_11G) {
131912e36acbSWarner Losh 		if (phy->phy_rev == 1)
132012e36acbSWarner Losh 			host_flags |= BWI_HFLAG_GDC_WA;
132112e36acbSWarner Losh 		if (sc->sc_card_flags & BWI_CARD_F_PA_GPIO9)
132212e36acbSWarner Losh 			host_flags |= BWI_HFLAG_OFDM_PA;
132312e36acbSWarner Losh 	} else if (phy->phy_mode == IEEE80211_MODE_11B) {
132412e36acbSWarner Losh 		if (phy->phy_rev >= 2 && rf->rf_type == BWI_RF_T_BCM2050)
132512e36acbSWarner Losh 			host_flags &= ~BWI_HFLAG_GDC_WA;
132612e36acbSWarner Losh 	} else {
132712e36acbSWarner Losh 		panic("unknown PHY mode %u\n", phy->phy_mode);
132812e36acbSWarner Losh 	}
132912e36acbSWarner Losh 
133012e36acbSWarner Losh 	HFLAGS_WRITE(mac, host_flags);
133112e36acbSWarner Losh }
133212e36acbSWarner Losh 
133312e36acbSWarner Losh static void
bwi_mac_bss_param_init(struct bwi_mac * mac)133412e36acbSWarner Losh bwi_mac_bss_param_init(struct bwi_mac *mac)
133512e36acbSWarner Losh {
133612e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
133712e36acbSWarner Losh 	struct bwi_phy *phy = &mac->mac_phy;
13387a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
133912e36acbSWarner Losh 	const struct ieee80211_rate_table *rt;
134012e36acbSWarner Losh 	struct bwi_retry_lim lim;
134112e36acbSWarner Losh 	uint16_t cw_min;
134212e36acbSWarner Losh 
134312e36acbSWarner Losh 	/*
134412e36acbSWarner Losh 	 * Set short/long retry limits
134512e36acbSWarner Losh 	 */
134612e36acbSWarner Losh 	bzero(&lim, sizeof(lim));
134712e36acbSWarner Losh 	lim.shretry = BWI_SHRETRY;
134812e36acbSWarner Losh 	lim.shretry_fb = BWI_SHRETRY_FB;
134912e36acbSWarner Losh 	lim.lgretry = BWI_LGRETRY;
135012e36acbSWarner Losh 	lim.lgretry_fb = BWI_LGRETRY_FB;
135112e36acbSWarner Losh 	bwi_mac_set_retry_lim(mac, &lim);
135212e36acbSWarner Losh 
135312e36acbSWarner Losh 	/*
135412e36acbSWarner Losh 	 * Implicitly prevent firmware from sending probe response
135512e36acbSWarner Losh 	 * by setting its "probe response timeout" to 1us.
135612e36acbSWarner Losh 	 */
135712e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_PROBE_RESP_TO, 1);
135812e36acbSWarner Losh 
135912e36acbSWarner Losh 	/*
136012e36acbSWarner Losh 	 * XXX MAC level acknowledge and CW min/max should depend
136112e36acbSWarner Losh 	 * on the char rateset of the IBSS/BSS to join.
136212e36acbSWarner Losh 	 * XXX this is all wrong; should be done on channel change
136312e36acbSWarner Losh 	 */
136412e36acbSWarner Losh 	if (phy->phy_mode == IEEE80211_MODE_11B) {
136512e36acbSWarner Losh 		rt = ieee80211_get_ratetable(
136612e36acbSWarner Losh 		    ieee80211_find_channel(ic, 2412, IEEE80211_CHAN_B));
136712e36acbSWarner Losh 		bwi_mac_set_ackrates(mac, rt,
136812e36acbSWarner Losh 		    &ic->ic_sup_rates[IEEE80211_MODE_11B]);
136912e36acbSWarner Losh 	} else {
137012e36acbSWarner Losh 		rt = ieee80211_get_ratetable(
137112e36acbSWarner Losh 		    ieee80211_find_channel(ic, 2412, IEEE80211_CHAN_G));
137212e36acbSWarner Losh 		bwi_mac_set_ackrates(mac, rt,
137312e36acbSWarner Losh 		    &ic->ic_sup_rates[IEEE80211_MODE_11G]);
137412e36acbSWarner Losh 	}
137512e36acbSWarner Losh 
137612e36acbSWarner Losh 	/*
137712e36acbSWarner Losh 	 * Set CW min
137812e36acbSWarner Losh 	 */
137912e36acbSWarner Losh 	if (phy->phy_mode == IEEE80211_MODE_11B)
138012e36acbSWarner Losh 		cw_min = IEEE80211_CW_MIN_0;
138112e36acbSWarner Losh 	else
138212e36acbSWarner Losh 		cw_min = IEEE80211_CW_MIN_1;
138312e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_80211_MOBJ, BWI_80211_MOBJ_CWMIN, cw_min);
138412e36acbSWarner Losh 
138512e36acbSWarner Losh 	/*
138612e36acbSWarner Losh 	 * Set CW max
138712e36acbSWarner Losh 	 */
138812e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_80211_MOBJ, BWI_80211_MOBJ_CWMAX,
138912e36acbSWarner Losh 		     IEEE80211_CW_MAX);
139012e36acbSWarner Losh }
139112e36acbSWarner Losh 
139212e36acbSWarner Losh static void
bwi_mac_set_retry_lim(struct bwi_mac * mac,const struct bwi_retry_lim * lim)139312e36acbSWarner Losh bwi_mac_set_retry_lim(struct bwi_mac *mac, const struct bwi_retry_lim *lim)
139412e36acbSWarner Losh {
139512e36acbSWarner Losh 	/* Short/Long retry limit */
139612e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_80211_MOBJ, BWI_80211_MOBJ_SHRETRY,
139712e36acbSWarner Losh 		     lim->shretry);
139812e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_80211_MOBJ, BWI_80211_MOBJ_LGRETRY,
139912e36acbSWarner Losh 		     lim->lgretry);
140012e36acbSWarner Losh 
140112e36acbSWarner Losh 	/* Short/Long retry fallback limit */
140212e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_SHRETRY_FB,
140312e36acbSWarner Losh 		     lim->shretry_fb);
140412e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_LGRETEY_FB,
140512e36acbSWarner Losh 		     lim->lgretry_fb);
140612e36acbSWarner Losh }
140712e36acbSWarner Losh 
140812e36acbSWarner Losh static void
bwi_mac_set_ackrates(struct bwi_mac * mac,const struct ieee80211_rate_table * rt,const struct ieee80211_rateset * rs)140912e36acbSWarner Losh bwi_mac_set_ackrates(struct bwi_mac *mac, const struct ieee80211_rate_table *rt,
141012e36acbSWarner Losh 	const struct ieee80211_rateset *rs)
141112e36acbSWarner Losh {
141212e36acbSWarner Losh 	int i;
141312e36acbSWarner Losh 
141412e36acbSWarner Losh 	/* XXX not standard conforming */
141512e36acbSWarner Losh 	for (i = 0; i < rs->rs_nrates; ++i) {
141612e36acbSWarner Losh 		enum ieee80211_phytype modtype;
141712e36acbSWarner Losh 		uint16_t ofs;
141812e36acbSWarner Losh 
1419a1df5ac1SAdrian Chadd 		modtype = ieee80211_rate2phytype(rt,
1420a1df5ac1SAdrian Chadd 		    rs->rs_rates[i] & IEEE80211_RATE_VAL);
142112e36acbSWarner Losh 		switch (modtype) {
142212e36acbSWarner Losh 		case IEEE80211_T_DS:
142312e36acbSWarner Losh 			ofs = 0x4c0;
142412e36acbSWarner Losh 			break;
142512e36acbSWarner Losh 		case IEEE80211_T_OFDM:
142612e36acbSWarner Losh 			ofs = 0x480;
142712e36acbSWarner Losh 			break;
142812e36acbSWarner Losh 		default:
142912e36acbSWarner Losh 			panic("unsupported modtype %u\n", modtype);
143012e36acbSWarner Losh 		}
1431a1df5ac1SAdrian Chadd 		ofs += 2*(ieee80211_rate2plcp(
1432a1df5ac1SAdrian Chadd 		    rs->rs_rates[i] & IEEE80211_RATE_VAL,
1433a1df5ac1SAdrian Chadd 		    modtype) & 0xf);
143412e36acbSWarner Losh 
143512e36acbSWarner Losh 		MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, ofs + 0x20,
143612e36acbSWarner Losh 			     MOBJ_READ_2(mac, BWI_COMM_MOBJ, ofs));
143712e36acbSWarner Losh 	}
143812e36acbSWarner Losh }
143912e36acbSWarner Losh 
144012e36acbSWarner Losh int
bwi_mac_start(struct bwi_mac * mac)144112e36acbSWarner Losh bwi_mac_start(struct bwi_mac *mac)
144212e36acbSWarner Losh {
144312e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
144412e36acbSWarner Losh 
144512e36acbSWarner Losh 	CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_ENABLE);
144612e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_INTR_STATUS, BWI_INTR_READY);
144712e36acbSWarner Losh 
144812e36acbSWarner Losh 	/* Flush pending bus writes */
144912e36acbSWarner Losh 	CSR_READ_4(sc, BWI_MAC_STATUS);
145012e36acbSWarner Losh 	CSR_READ_4(sc, BWI_MAC_INTR_STATUS);
145112e36acbSWarner Losh 
145212e36acbSWarner Losh 	return bwi_mac_config_ps(mac);
145312e36acbSWarner Losh }
145412e36acbSWarner Losh 
145512e36acbSWarner Losh int
bwi_mac_stop(struct bwi_mac * mac)145612e36acbSWarner Losh bwi_mac_stop(struct bwi_mac *mac)
145712e36acbSWarner Losh {
145812e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
145912e36acbSWarner Losh 	int error, i;
146012e36acbSWarner Losh 
146112e36acbSWarner Losh 	error = bwi_mac_config_ps(mac);
146212e36acbSWarner Losh 	if (error)
146312e36acbSWarner Losh 		return error;
146412e36acbSWarner Losh 
146512e36acbSWarner Losh 	CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_ENABLE);
146612e36acbSWarner Losh 
146712e36acbSWarner Losh 	/* Flush pending bus write */
146812e36acbSWarner Losh 	CSR_READ_4(sc, BWI_MAC_STATUS);
146912e36acbSWarner Losh 
147012e36acbSWarner Losh #define NRETRY	10000
147112e36acbSWarner Losh 	for (i = 0; i < NRETRY; ++i) {
147212e36acbSWarner Losh 		if (CSR_READ_4(sc, BWI_MAC_INTR_STATUS) & BWI_INTR_READY)
147312e36acbSWarner Losh 			break;
147412e36acbSWarner Losh 		DELAY(1);
147512e36acbSWarner Losh 	}
147612e36acbSWarner Losh 	if (i == NRETRY) {
147712e36acbSWarner Losh 		device_printf(sc->sc_dev, "can't stop MAC\n");
147812e36acbSWarner Losh 		return ETIMEDOUT;
147912e36acbSWarner Losh 	}
148012e36acbSWarner Losh #undef NRETRY
148112e36acbSWarner Losh 
148212e36acbSWarner Losh 	return 0;
148312e36acbSWarner Losh }
148412e36acbSWarner Losh 
148512e36acbSWarner Losh int
bwi_mac_config_ps(struct bwi_mac * mac)148612e36acbSWarner Losh bwi_mac_config_ps(struct bwi_mac *mac)
148712e36acbSWarner Losh {
148812e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
148912e36acbSWarner Losh 	uint32_t status;
149012e36acbSWarner Losh 
149112e36acbSWarner Losh 	status = CSR_READ_4(sc, BWI_MAC_STATUS);
149212e36acbSWarner Losh 
149312e36acbSWarner Losh 	status &= ~BWI_MAC_STATUS_HW_PS;
149412e36acbSWarner Losh 	status |= BWI_MAC_STATUS_WAKEUP;
149512e36acbSWarner Losh 	CSR_WRITE_4(sc, BWI_MAC_STATUS, status);
149612e36acbSWarner Losh 
149712e36acbSWarner Losh 	/* Flush pending bus write */
149812e36acbSWarner Losh 	CSR_READ_4(sc, BWI_MAC_STATUS);
149912e36acbSWarner Losh 
150012e36acbSWarner Losh 	if (mac->mac_rev >= 5) {
150112e36acbSWarner Losh 		int i;
150212e36acbSWarner Losh 
150312e36acbSWarner Losh #define NRETRY	100
150412e36acbSWarner Losh 		for (i = 0; i < NRETRY; ++i) {
150512e36acbSWarner Losh 			if (MOBJ_READ_2(mac, BWI_COMM_MOBJ,
150612e36acbSWarner Losh 			    BWI_COMM_MOBJ_UCODE_STATE) != BWI_UCODE_STATE_PS)
150712e36acbSWarner Losh 				break;
150812e36acbSWarner Losh 			DELAY(10);
150912e36acbSWarner Losh 		}
151012e36acbSWarner Losh 		if (i == NRETRY) {
151112e36acbSWarner Losh 			device_printf(sc->sc_dev, "config PS failed\n");
151212e36acbSWarner Losh 			return ETIMEDOUT;
151312e36acbSWarner Losh 		}
151412e36acbSWarner Losh #undef NRETRY
151512e36acbSWarner Losh 	}
151612e36acbSWarner Losh 	return 0;
151712e36acbSWarner Losh }
151812e36acbSWarner Losh 
151912e36acbSWarner Losh void
bwi_mac_reset_hwkeys(struct bwi_mac * mac)152012e36acbSWarner Losh bwi_mac_reset_hwkeys(struct bwi_mac *mac)
152112e36acbSWarner Losh {
152212e36acbSWarner Losh 	/* TODO: firmware crypto */
152312e36acbSWarner Losh 	MOBJ_READ_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_KEYTABLE_OFS);
152412e36acbSWarner Losh }
152512e36acbSWarner Losh 
152612e36acbSWarner Losh void
bwi_mac_shutdown(struct bwi_mac * mac)152712e36acbSWarner Losh bwi_mac_shutdown(struct bwi_mac *mac)
152812e36acbSWarner Losh {
152912e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
153012e36acbSWarner Losh 	int i;
153112e36acbSWarner Losh 
153212e36acbSWarner Losh 	if (mac->mac_flags & BWI_MAC_F_HAS_TXSTATS)
153312e36acbSWarner Losh 		sc->sc_free_txstats(sc);
153412e36acbSWarner Losh 
153512e36acbSWarner Losh 	sc->sc_free_rx_ring(sc);
153612e36acbSWarner Losh 
153712e36acbSWarner Losh 	for (i = 0; i < BWI_TX_NRING; ++i)
153812e36acbSWarner Losh 		sc->sc_free_tx_ring(sc, i);
153912e36acbSWarner Losh 
154012e36acbSWarner Losh 	bwi_rf_off(mac);
154112e36acbSWarner Losh 
154212e36acbSWarner Losh 	/* TODO:LED */
154312e36acbSWarner Losh 
154412e36acbSWarner Losh 	bwi_mac_gpio_fini(mac);
154512e36acbSWarner Losh 
154612e36acbSWarner Losh 	bwi_rf_off(mac); /* XXX again */
154712e36acbSWarner Losh 	CSR_WRITE_2(sc, BWI_BBP_ATTEN, BWI_BBP_ATTEN_MAGIC);
154812e36acbSWarner Losh 	bwi_regwin_disable(sc, &mac->mac_regwin, 0);
154912e36acbSWarner Losh 
155012e36acbSWarner Losh 	mac->mac_flags &= ~BWI_MAC_F_INITED;
155112e36acbSWarner Losh }
155212e36acbSWarner Losh 
155312e36acbSWarner Losh static int
bwi_mac_get_property(struct bwi_mac * mac)155412e36acbSWarner Losh bwi_mac_get_property(struct bwi_mac *mac)
155512e36acbSWarner Losh {
155612e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
155712e36acbSWarner Losh 	enum bwi_bus_space old_bus_space;
155812e36acbSWarner Losh 	uint32_t val;
155912e36acbSWarner Losh 
156012e36acbSWarner Losh 	/*
156112e36acbSWarner Losh 	 * Byte swap
156212e36acbSWarner Losh 	 */
156312e36acbSWarner Losh 	val = CSR_READ_4(sc, BWI_MAC_STATUS);
156412e36acbSWarner Losh 	if (val & BWI_MAC_STATUS_BSWAP) {
156512e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
156612e36acbSWarner Losh 			"need byte swap");
156712e36acbSWarner Losh 		mac->mac_flags |= BWI_MAC_F_BSWAP;
156812e36acbSWarner Losh 	}
156912e36acbSWarner Losh 
157012e36acbSWarner Losh 	/*
157112e36acbSWarner Losh 	 * DMA address space
157212e36acbSWarner Losh 	 */
157312e36acbSWarner Losh 	old_bus_space = sc->sc_bus_space;
157412e36acbSWarner Losh 
157512e36acbSWarner Losh 	val = CSR_READ_4(sc, BWI_STATE_HI);
157612e36acbSWarner Losh 	if (__SHIFTOUT(val, BWI_STATE_HI_FLAGS_MASK) &
157712e36acbSWarner Losh 	    BWI_STATE_HI_FLAG_64BIT) {
157812e36acbSWarner Losh 		/* 64bit address */
157912e36acbSWarner Losh 		sc->sc_bus_space = BWI_BUS_SPACE_64BIT;
158012e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
158112e36acbSWarner Losh 			"64bit bus space");
158212e36acbSWarner Losh 	} else {
158312e36acbSWarner Losh 		uint32_t txrx_reg = BWI_TXRX_CTRL_BASE + BWI_TX32_CTRL;
158412e36acbSWarner Losh 
158512e36acbSWarner Losh 		CSR_WRITE_4(sc, txrx_reg, BWI_TXRX32_CTRL_ADDRHI_MASK);
158612e36acbSWarner Losh 		if (CSR_READ_4(sc, txrx_reg) & BWI_TXRX32_CTRL_ADDRHI_MASK) {
158712e36acbSWarner Losh 			/* 32bit address */
158812e36acbSWarner Losh 			sc->sc_bus_space = BWI_BUS_SPACE_32BIT;
158912e36acbSWarner Losh 			DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
159012e36acbSWarner Losh 				"32bit bus space");
159112e36acbSWarner Losh 		} else {
159212e36acbSWarner Losh 			/* 30bit address */
159312e36acbSWarner Losh 			sc->sc_bus_space = BWI_BUS_SPACE_30BIT;
159412e36acbSWarner Losh 			DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
159512e36acbSWarner Losh 				"30bit bus space");
159612e36acbSWarner Losh 		}
159712e36acbSWarner Losh 	}
159812e36acbSWarner Losh 
159912e36acbSWarner Losh 	if (old_bus_space != 0 && old_bus_space != sc->sc_bus_space) {
160012e36acbSWarner Losh 		device_printf(sc->sc_dev, "MACs bus space mismatch!\n");
160112e36acbSWarner Losh 		return ENXIO;
160212e36acbSWarner Losh 	}
160312e36acbSWarner Losh 	return 0;
160412e36acbSWarner Losh }
160512e36acbSWarner Losh 
160612e36acbSWarner Losh void
bwi_mac_updateslot(struct bwi_mac * mac,int shslot)160712e36acbSWarner Losh bwi_mac_updateslot(struct bwi_mac *mac, int shslot)
160812e36acbSWarner Losh {
160912e36acbSWarner Losh 	uint16_t slot_time;
161012e36acbSWarner Losh 
161112e36acbSWarner Losh 	if (mac->mac_phy.phy_mode == IEEE80211_MODE_11B)
161212e36acbSWarner Losh 		return;
161312e36acbSWarner Losh 
161412e36acbSWarner Losh 	if (shslot)
161512e36acbSWarner Losh 		slot_time = IEEE80211_DUR_SHSLOT;
161612e36acbSWarner Losh 	else
161712e36acbSWarner Losh 		slot_time = IEEE80211_DUR_SLOT;
161812e36acbSWarner Losh 
161912e36acbSWarner Losh 	CSR_WRITE_2(mac->mac_sc, BWI_MAC_SLOTTIME,
162012e36acbSWarner Losh 		    slot_time + BWI_MAC_SLOTTIME_ADJUST);
162112e36acbSWarner Losh 	MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_SLOTTIME, slot_time);
162212e36acbSWarner Losh }
162312e36acbSWarner Losh 
162412e36acbSWarner Losh int
bwi_mac_attach(struct bwi_softc * sc,int id,uint8_t rev)162512e36acbSWarner Losh bwi_mac_attach(struct bwi_softc *sc, int id, uint8_t rev)
162612e36acbSWarner Losh {
162712e36acbSWarner Losh 	struct bwi_mac *mac;
162812e36acbSWarner Losh 	int i;
162912e36acbSWarner Losh 
163012e36acbSWarner Losh 	KASSERT(sc->sc_nmac <= BWI_MAC_MAX && sc->sc_nmac >= 0,
163112e36acbSWarner Losh 	    ("sc_nmac %d", sc->sc_nmac));
163212e36acbSWarner Losh 
163312e36acbSWarner Losh 	if (sc->sc_nmac == BWI_MAC_MAX) {
163412e36acbSWarner Losh 		device_printf(sc->sc_dev, "too many MACs\n");
163512e36acbSWarner Losh 		return 0;
163612e36acbSWarner Losh 	}
163712e36acbSWarner Losh 
163812e36acbSWarner Losh 	/*
163912e36acbSWarner Losh 	 * More than one MAC is only supported by BCM4309
164012e36acbSWarner Losh 	 */
164112e36acbSWarner Losh 	if (sc->sc_nmac != 0 &&
164212e36acbSWarner Losh 	    sc->sc_pci_did != PCI_PRODUCT_BROADCOM_BCM4309) {
164312e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
164412e36acbSWarner Losh 			"ignore second MAC");
164512e36acbSWarner Losh 		return 0;
164612e36acbSWarner Losh 	}
164712e36acbSWarner Losh 
164812e36acbSWarner Losh 	mac = &sc->sc_mac[sc->sc_nmac];
164912e36acbSWarner Losh 
165012e36acbSWarner Losh 	/* XXX will this happen? */
165112e36acbSWarner Losh 	if (BWI_REGWIN_EXIST(&mac->mac_regwin)) {
165212e36acbSWarner Losh 		device_printf(sc->sc_dev, "%dth MAC already attached\n",
165312e36acbSWarner Losh 			      sc->sc_nmac);
165412e36acbSWarner Losh 		return 0;
165512e36acbSWarner Losh 	}
165612e36acbSWarner Losh 
165712e36acbSWarner Losh 	/*
165812e36acbSWarner Losh 	 * Test whether the revision of this MAC is supported
165912e36acbSWarner Losh 	 */
1660d6166defSAdrian Chadd 	for (i = 0; i < nitems(bwi_sup_macrev); ++i) {
166112e36acbSWarner Losh 		if (bwi_sup_macrev[i] == rev)
166212e36acbSWarner Losh 			break;
166312e36acbSWarner Losh 	}
1664d6166defSAdrian Chadd 	if (i == nitems(bwi_sup_macrev)) {
166512e36acbSWarner Losh 		device_printf(sc->sc_dev, "MAC rev %u is "
166612e36acbSWarner Losh 			      "not supported\n", rev);
166712e36acbSWarner Losh 		return ENXIO;
166812e36acbSWarner Losh 	}
166912e36acbSWarner Losh 
167012e36acbSWarner Losh 	BWI_CREATE_MAC(mac, sc, id, rev);
167112e36acbSWarner Losh 	sc->sc_nmac++;
167212e36acbSWarner Losh 
167312e36acbSWarner Losh 	if (mac->mac_rev < 5) {
167412e36acbSWarner Losh 		mac->mac_flags |= BWI_MAC_F_HAS_TXSTATS;
167512e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "%s\n",
167612e36acbSWarner Losh 			"has TX stats");
167712e36acbSWarner Losh 	} else {
167812e36acbSWarner Losh 		mac->mac_flags |= BWI_MAC_F_PHYE_RESET;
167912e36acbSWarner Losh 	}
168012e36acbSWarner Losh 
168112e36acbSWarner Losh 	device_printf(sc->sc_dev, "MAC: rev %u\n", rev);
168212e36acbSWarner Losh 	return 0;
168312e36acbSWarner Losh }
168412e36acbSWarner Losh 
168512e36acbSWarner Losh static __inline void
bwi_mac_balance_atten(int * bbp_atten0,int * rf_atten0)168612e36acbSWarner Losh bwi_mac_balance_atten(int *bbp_atten0, int *rf_atten0)
168712e36acbSWarner Losh {
168812e36acbSWarner Losh 	int bbp_atten, rf_atten, rf_atten_lim = -1;
168912e36acbSWarner Losh 
169012e36acbSWarner Losh 	bbp_atten = *bbp_atten0;
169112e36acbSWarner Losh 	rf_atten = *rf_atten0;
169212e36acbSWarner Losh 
169312e36acbSWarner Losh 	/*
169412e36acbSWarner Losh 	 * RF attenuation affects TX power BWI_RF_ATTEN_FACTOR times
169512e36acbSWarner Losh 	 * as much as BBP attenuation, so we try our best to keep RF
169612e36acbSWarner Losh 	 * attenuation within range.  BBP attenuation will be clamped
169712e36acbSWarner Losh 	 * later if it is out of range during balancing.
169812e36acbSWarner Losh 	 *
169912e36acbSWarner Losh 	 * BWI_RF_ATTEN_MAX0 is used as RF attenuation upper limit.
170012e36acbSWarner Losh 	 */
170112e36acbSWarner Losh 
170212e36acbSWarner Losh 	/*
170312e36acbSWarner Losh 	 * Use BBP attenuation to balance RF attenuation
170412e36acbSWarner Losh 	 */
170512e36acbSWarner Losh 	if (rf_atten < 0)
170612e36acbSWarner Losh 		rf_atten_lim = 0;
170712e36acbSWarner Losh 	else if (rf_atten > BWI_RF_ATTEN_MAX0)
170812e36acbSWarner Losh 		rf_atten_lim = BWI_RF_ATTEN_MAX0;
170912e36acbSWarner Losh 
171012e36acbSWarner Losh 	if (rf_atten_lim >= 0) {
171112e36acbSWarner Losh 		bbp_atten += (BWI_RF_ATTEN_FACTOR * (rf_atten - rf_atten_lim));
171212e36acbSWarner Losh 		rf_atten = rf_atten_lim;
171312e36acbSWarner Losh 	}
171412e36acbSWarner Losh 
171512e36acbSWarner Losh 	/*
171612e36acbSWarner Losh 	 * If possible, use RF attenuation to balance BBP attenuation
171712e36acbSWarner Losh 	 * NOTE: RF attenuation is still kept within range.
171812e36acbSWarner Losh 	 */
171912e36acbSWarner Losh 	while (rf_atten < BWI_RF_ATTEN_MAX0 && bbp_atten > BWI_BBP_ATTEN_MAX) {
172012e36acbSWarner Losh 		bbp_atten -= BWI_RF_ATTEN_FACTOR;
172112e36acbSWarner Losh 		++rf_atten;
172212e36acbSWarner Losh 	}
172312e36acbSWarner Losh 	while (rf_atten > 0 && bbp_atten < 0) {
172412e36acbSWarner Losh 		bbp_atten += BWI_RF_ATTEN_FACTOR;
172512e36acbSWarner Losh 		--rf_atten;
172612e36acbSWarner Losh 	}
172712e36acbSWarner Losh 
172812e36acbSWarner Losh 	/* RF attenuation MUST be within range */
172912e36acbSWarner Losh 	KASSERT(rf_atten >= 0 && rf_atten <= BWI_RF_ATTEN_MAX0,
173012e36acbSWarner Losh 	    ("rf_atten %d", rf_atten));
173112e36acbSWarner Losh 
173212e36acbSWarner Losh 	/*
173312e36acbSWarner Losh 	 * Clamp BBP attenuation
173412e36acbSWarner Losh 	 */
173512e36acbSWarner Losh 	if (bbp_atten < 0)
173612e36acbSWarner Losh 		bbp_atten = 0;
173712e36acbSWarner Losh 	else if (bbp_atten > BWI_BBP_ATTEN_MAX)
173812e36acbSWarner Losh 		bbp_atten = BWI_BBP_ATTEN_MAX;
173912e36acbSWarner Losh 
174012e36acbSWarner Losh 	*rf_atten0 = rf_atten;
174112e36acbSWarner Losh 	*bbp_atten0 = bbp_atten;
174212e36acbSWarner Losh }
174312e36acbSWarner Losh 
174412e36acbSWarner Losh static void
bwi_mac_adjust_tpctl(struct bwi_mac * mac,int rf_atten_adj,int bbp_atten_adj)174512e36acbSWarner Losh bwi_mac_adjust_tpctl(struct bwi_mac *mac, int rf_atten_adj, int bbp_atten_adj)
174612e36acbSWarner Losh {
174712e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
174812e36acbSWarner Losh 	struct bwi_rf *rf = &mac->mac_rf;
174912e36acbSWarner Losh 	struct bwi_tpctl tpctl;
175012e36acbSWarner Losh 	int bbp_atten, rf_atten, tp_ctrl1;
175112e36acbSWarner Losh 
175212e36acbSWarner Losh 	bcopy(&mac->mac_tpctl, &tpctl, sizeof(tpctl));
175312e36acbSWarner Losh 
175412e36acbSWarner Losh 	/* NOTE: Use signed value to do calulation */
175512e36acbSWarner Losh 	bbp_atten = tpctl.bbp_atten;
175612e36acbSWarner Losh 	rf_atten = tpctl.rf_atten;
175712e36acbSWarner Losh 	tp_ctrl1 = tpctl.tp_ctrl1;
175812e36acbSWarner Losh 
175912e36acbSWarner Losh 	bbp_atten += bbp_atten_adj;
176012e36acbSWarner Losh 	rf_atten += rf_atten_adj;
176112e36acbSWarner Losh 
176212e36acbSWarner Losh 	bwi_mac_balance_atten(&bbp_atten, &rf_atten);
176312e36acbSWarner Losh 
176412e36acbSWarner Losh 	if (rf->rf_type == BWI_RF_T_BCM2050 && rf->rf_rev == 2) {
176512e36acbSWarner Losh 		if (rf_atten <= 1) {
176612e36acbSWarner Losh 			if (tp_ctrl1 == 0) {
176712e36acbSWarner Losh 				tp_ctrl1 = 3;
176812e36acbSWarner Losh 				bbp_atten += 2;
176912e36acbSWarner Losh 				rf_atten += 2;
177012e36acbSWarner Losh 			} else if (sc->sc_card_flags & BWI_CARD_F_PA_GPIO9) {
177112e36acbSWarner Losh 				bbp_atten +=
177212e36acbSWarner Losh 				(BWI_RF_ATTEN_FACTOR * (rf_atten - 2));
177312e36acbSWarner Losh 				rf_atten = 2;
177412e36acbSWarner Losh 			}
177512e36acbSWarner Losh 		} else if (rf_atten > 4 && tp_ctrl1 != 0) {
177612e36acbSWarner Losh 			tp_ctrl1 = 0;
177712e36acbSWarner Losh 			if (bbp_atten < 3) {
177812e36acbSWarner Losh 				bbp_atten += 2;
177912e36acbSWarner Losh 				rf_atten -= 3;
178012e36acbSWarner Losh 			} else {
178112e36acbSWarner Losh 				bbp_atten -= 2;
178212e36acbSWarner Losh 				rf_atten -= 2;
178312e36acbSWarner Losh 			}
178412e36acbSWarner Losh 		}
178512e36acbSWarner Losh 		bwi_mac_balance_atten(&bbp_atten, &rf_atten);
178612e36acbSWarner Losh 	}
178712e36acbSWarner Losh 
178812e36acbSWarner Losh 	tpctl.bbp_atten = bbp_atten;
178912e36acbSWarner Losh 	tpctl.rf_atten = rf_atten;
179012e36acbSWarner Losh 	tpctl.tp_ctrl1 = tp_ctrl1;
179112e36acbSWarner Losh 
179212e36acbSWarner Losh 	bwi_mac_lock(mac);
179312e36acbSWarner Losh 	bwi_mac_set_tpctl_11bg(mac, &tpctl);
179412e36acbSWarner Losh 	bwi_mac_unlock(mac);
179512e36acbSWarner Losh }
179612e36acbSWarner Losh 
179712e36acbSWarner Losh /*
179812e36acbSWarner Losh  * http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower
179912e36acbSWarner Losh  */
180012e36acbSWarner Losh void
bwi_mac_calibrate_txpower(struct bwi_mac * mac,enum bwi_txpwrcb_type type)180112e36acbSWarner Losh bwi_mac_calibrate_txpower(struct bwi_mac *mac, enum bwi_txpwrcb_type type)
180212e36acbSWarner Losh {
180312e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
180412e36acbSWarner Losh 	struct bwi_rf *rf = &mac->mac_rf;
180512e36acbSWarner Losh 	int8_t tssi[4], tssi_avg, cur_txpwr;
180612e36acbSWarner Losh 	int error, i, ofdm_tssi;
180712e36acbSWarner Losh 	int txpwr_diff, rf_atten_adj, bbp_atten_adj;
180812e36acbSWarner Losh 
180912e36acbSWarner Losh 	if (!sc->sc_txpwr_calib)
181012e36acbSWarner Losh 		return;
181112e36acbSWarner Losh 
181212e36acbSWarner Losh 	if (mac->mac_flags & BWI_MAC_F_TPCTL_ERROR) {
181312e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
181412e36acbSWarner Losh 			"tpctl error happened, can't set txpower");
181512e36acbSWarner Losh 		return;
181612e36acbSWarner Losh 	}
181712e36acbSWarner Losh 
181812e36acbSWarner Losh 	if (BWI_IS_BRCM_BU4306(sc)) {
181912e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
182012e36acbSWarner Losh 			"BU4306, can't set txpower");
182112e36acbSWarner Losh 		return;
182212e36acbSWarner Losh 	}
182312e36acbSWarner Losh 
182412e36acbSWarner Losh 	/*
182512e36acbSWarner Losh 	 * Save latest TSSI and reset the related memory objects
182612e36acbSWarner Losh 	 */
182712e36acbSWarner Losh 	ofdm_tssi = 0;
182812e36acbSWarner Losh 	error = bwi_rf_get_latest_tssi(mac, tssi, BWI_COMM_MOBJ_TSSI_DS);
182912e36acbSWarner Losh 	if (error) {
183012e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
183112e36acbSWarner Losh 			"no DS tssi");
183212e36acbSWarner Losh 
183312e36acbSWarner Losh 		if (mac->mac_phy.phy_mode == IEEE80211_MODE_11B) {
183412e36acbSWarner Losh 			if (type == BWI_TXPWR_FORCE) {
183512e36acbSWarner Losh 				rf_atten_adj = 0;
183612e36acbSWarner Losh 				bbp_atten_adj = 1;
183712e36acbSWarner Losh 				goto calib;
183812e36acbSWarner Losh 			} else {
183912e36acbSWarner Losh 				return;
184012e36acbSWarner Losh 			}
184112e36acbSWarner Losh 		}
184212e36acbSWarner Losh 
184312e36acbSWarner Losh 		error = bwi_rf_get_latest_tssi(mac, tssi,
184412e36acbSWarner Losh 				BWI_COMM_MOBJ_TSSI_OFDM);
184512e36acbSWarner Losh 		if (error) {
184612e36acbSWarner Losh 			DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
184712e36acbSWarner Losh 				"no OFDM tssi");
184812e36acbSWarner Losh 			if (type == BWI_TXPWR_FORCE) {
184912e36acbSWarner Losh 				rf_atten_adj = 0;
185012e36acbSWarner Losh 				bbp_atten_adj = 1;
185112e36acbSWarner Losh 				goto calib;
185212e36acbSWarner Losh 			} else {
185312e36acbSWarner Losh 				return;
185412e36acbSWarner Losh 			}
185512e36acbSWarner Losh 		}
185612e36acbSWarner Losh 
185712e36acbSWarner Losh 		for (i = 0; i < 4; ++i) {
185812e36acbSWarner Losh 			tssi[i] += 0x20;
185912e36acbSWarner Losh 			tssi[i] &= 0x3f;
186012e36acbSWarner Losh 		}
186112e36acbSWarner Losh 		ofdm_tssi = 1;
186212e36acbSWarner Losh 	}
186312e36acbSWarner Losh 	bwi_rf_clear_tssi(mac);
186412e36acbSWarner Losh 
186512e36acbSWarner Losh 	DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER,
186612e36acbSWarner Losh 		"tssi0 %d, tssi1 %d, tssi2 %d, tssi3 %d\n",
186712e36acbSWarner Losh 		tssi[0], tssi[1], tssi[2], tssi[3]);
186812e36acbSWarner Losh 
186912e36acbSWarner Losh 	/*
187012e36acbSWarner Losh 	 * Calculate RF/BBP attenuation adjustment based on
187112e36acbSWarner Losh 	 * the difference between desired TX power and sampled
187212e36acbSWarner Losh 	 * TX power.
187312e36acbSWarner Losh 	 */
187412e36acbSWarner Losh 	/* +8 == "each incremented by 1/2" */
187512e36acbSWarner Losh 	tssi_avg = (tssi[0] + tssi[1] + tssi[2] + tssi[3] + 8) / 4;
187612e36acbSWarner Losh 	if (ofdm_tssi && (HFLAGS_READ(mac) & BWI_HFLAG_PWR_BOOST_DS))
187712e36acbSWarner Losh 		tssi_avg -= 13;
187812e36acbSWarner Losh 
187912e36acbSWarner Losh 	DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "tssi avg %d\n", tssi_avg);
188012e36acbSWarner Losh 
188112e36acbSWarner Losh 	error = bwi_rf_tssi2dbm(mac, tssi_avg, &cur_txpwr);
188212e36acbSWarner Losh 	if (error)
188312e36acbSWarner Losh 		return;
188412e36acbSWarner Losh 	DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "current txpower %d\n",
188512e36acbSWarner Losh 		cur_txpwr);
188612e36acbSWarner Losh 
188712e36acbSWarner Losh 	txpwr_diff = rf->rf_txpower_max - cur_txpwr; /* XXX ni_txpower */
188812e36acbSWarner Losh 
188912e36acbSWarner Losh 	rf_atten_adj = -howmany(txpwr_diff, 8);
189012e36acbSWarner Losh 	if (type == BWI_TXPWR_INIT) {
189112e36acbSWarner Losh 		/*
189212e36acbSWarner Losh 		 * Move toward EEPROM max TX power as fast as we can
189312e36acbSWarner Losh 		 */
189412e36acbSWarner Losh 		bbp_atten_adj = -txpwr_diff;
189512e36acbSWarner Losh 	} else {
189612e36acbSWarner Losh 		bbp_atten_adj = -(txpwr_diff / 2);
189712e36acbSWarner Losh 	}
189812e36acbSWarner Losh 	bbp_atten_adj -= (BWI_RF_ATTEN_FACTOR * rf_atten_adj);
189912e36acbSWarner Losh 
190012e36acbSWarner Losh 	if (rf_atten_adj == 0 && bbp_atten_adj == 0) {
190112e36acbSWarner Losh 		DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER, "%s\n",
190212e36acbSWarner Losh 			"no need to adjust RF/BBP attenuation");
190312e36acbSWarner Losh 		/* TODO: LO */
190412e36acbSWarner Losh 		return;
190512e36acbSWarner Losh 	}
190612e36acbSWarner Losh 
190712e36acbSWarner Losh calib:
190812e36acbSWarner Losh 	DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_TXPOWER,
190912e36acbSWarner Losh 		"rf atten adjust %d, bbp atten adjust %d\n",
191012e36acbSWarner Losh 		rf_atten_adj, bbp_atten_adj);
191112e36acbSWarner Losh 	bwi_mac_adjust_tpctl(mac, rf_atten_adj, bbp_atten_adj);
191212e36acbSWarner Losh 	/* TODO: LO */
191312e36acbSWarner Losh }
191412e36acbSWarner Losh 
191512e36acbSWarner Losh static void
bwi_mac_lock(struct bwi_mac * mac)191612e36acbSWarner Losh bwi_mac_lock(struct bwi_mac *mac)
191712e36acbSWarner Losh {
191812e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
19197a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
192012e36acbSWarner Losh 
192112e36acbSWarner Losh 	KASSERT((mac->mac_flags & BWI_MAC_F_LOCKED) == 0,
192212e36acbSWarner Losh 	    ("mac_flags 0x%x", mac->mac_flags));
192312e36acbSWarner Losh 
192412e36acbSWarner Losh 	if (mac->mac_rev < 3)
192512e36acbSWarner Losh 		bwi_mac_stop(mac);
192612e36acbSWarner Losh 	else if (ic->ic_opmode != IEEE80211_M_HOSTAP)
192712e36acbSWarner Losh 		bwi_mac_config_ps(mac);
192812e36acbSWarner Losh 
192912e36acbSWarner Losh 	CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_RFLOCK);
193012e36acbSWarner Losh 
193112e36acbSWarner Losh 	/* Flush pending bus write */
193212e36acbSWarner Losh 	CSR_READ_4(sc, BWI_MAC_STATUS);
193312e36acbSWarner Losh 	DELAY(10);
193412e36acbSWarner Losh 
193512e36acbSWarner Losh 	mac->mac_flags |= BWI_MAC_F_LOCKED;
193612e36acbSWarner Losh }
193712e36acbSWarner Losh 
193812e36acbSWarner Losh static void
bwi_mac_unlock(struct bwi_mac * mac)193912e36acbSWarner Losh bwi_mac_unlock(struct bwi_mac *mac)
194012e36acbSWarner Losh {
194112e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
19427a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
194312e36acbSWarner Losh 
194412e36acbSWarner Losh 	KASSERT(mac->mac_flags & BWI_MAC_F_LOCKED,
194512e36acbSWarner Losh 	    ("mac_flags 0x%x", mac->mac_flags));
194612e36acbSWarner Losh 
194712e36acbSWarner Losh 	CSR_READ_2(sc, BWI_PHYINFO); /* dummy read */
194812e36acbSWarner Losh 
194912e36acbSWarner Losh 	CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_RFLOCK);
195012e36acbSWarner Losh 
195112e36acbSWarner Losh 	if (mac->mac_rev < 3)
195212e36acbSWarner Losh 		bwi_mac_start(mac);
195312e36acbSWarner Losh 	else if (ic->ic_opmode != IEEE80211_M_HOSTAP)
195412e36acbSWarner Losh 		bwi_mac_config_ps(mac);
195512e36acbSWarner Losh 
195612e36acbSWarner Losh 	mac->mac_flags &= ~BWI_MAC_F_LOCKED;
195712e36acbSWarner Losh }
195812e36acbSWarner Losh 
195912e36acbSWarner Losh void
bwi_mac_set_promisc(struct bwi_mac * mac,int promisc)196012e36acbSWarner Losh bwi_mac_set_promisc(struct bwi_mac *mac, int promisc)
196112e36acbSWarner Losh {
196212e36acbSWarner Losh 	struct bwi_softc *sc = mac->mac_sc;
196312e36acbSWarner Losh 
196412e36acbSWarner Losh 	if (mac->mac_rev < 5) /* Promisc is always on */
196512e36acbSWarner Losh 		return;
196612e36acbSWarner Losh 
196712e36acbSWarner Losh 	if (promisc)
196812e36acbSWarner Losh 		CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PROMISC);
196912e36acbSWarner Losh 	else
197012e36acbSWarner Losh 		CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PROMISC);
197112e36acbSWarner Losh }
1972