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