xref: /freebsd/sys/dev/rtwn/rtl8188e/r88e_fw.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
17453645fSAndriy Voskoboinyk /*	$OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $	*/
27453645fSAndriy Voskoboinyk 
37453645fSAndriy Voskoboinyk /*-
47453645fSAndriy Voskoboinyk  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
57453645fSAndriy Voskoboinyk  * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
67453645fSAndriy Voskoboinyk  * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org>
77453645fSAndriy Voskoboinyk  *
87453645fSAndriy Voskoboinyk  * Permission to use, copy, modify, and distribute this software for any
97453645fSAndriy Voskoboinyk  * purpose with or without fee is hereby granted, provided that the above
107453645fSAndriy Voskoboinyk  * copyright notice and this permission notice appear in all copies.
117453645fSAndriy Voskoboinyk  *
127453645fSAndriy Voskoboinyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
137453645fSAndriy Voskoboinyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
147453645fSAndriy Voskoboinyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
157453645fSAndriy Voskoboinyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
167453645fSAndriy Voskoboinyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
177453645fSAndriy Voskoboinyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
187453645fSAndriy Voskoboinyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
197453645fSAndriy Voskoboinyk  */
207453645fSAndriy Voskoboinyk 
217453645fSAndriy Voskoboinyk #include <sys/cdefs.h>
227453645fSAndriy Voskoboinyk #include "opt_wlan.h"
237453645fSAndriy Voskoboinyk 
247453645fSAndriy Voskoboinyk #include <sys/param.h>
257453645fSAndriy Voskoboinyk #include <sys/lock.h>
267453645fSAndriy Voskoboinyk #include <sys/mutex.h>
277453645fSAndriy Voskoboinyk #include <sys/mbuf.h>
287453645fSAndriy Voskoboinyk #include <sys/kernel.h>
297453645fSAndriy Voskoboinyk #include <sys/socket.h>
307453645fSAndriy Voskoboinyk #include <sys/systm.h>
317453645fSAndriy Voskoboinyk #include <sys/malloc.h>
327453645fSAndriy Voskoboinyk #include <sys/queue.h>
337453645fSAndriy Voskoboinyk #include <sys/taskqueue.h>
347453645fSAndriy Voskoboinyk #include <sys/bus.h>
357453645fSAndriy Voskoboinyk #include <sys/endian.h>
367453645fSAndriy Voskoboinyk #include <sys/linker.h>
377453645fSAndriy Voskoboinyk 
387453645fSAndriy Voskoboinyk #include <net/if.h>
397453645fSAndriy Voskoboinyk #include <net/ethernet.h>
407453645fSAndriy Voskoboinyk #include <net/if_media.h>
417453645fSAndriy Voskoboinyk 
427453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
437453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h>
447453645fSAndriy Voskoboinyk 
457453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
467453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
477453645fSAndriy Voskoboinyk 
487453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
497453645fSAndriy Voskoboinyk 
507453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8188e/r88e.h>
517453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8188e/r88e_reg.h>
527453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8188e/r88e_fw_cmd.h>
537453645fSAndriy Voskoboinyk 
547453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE
557453645fSAndriy Voskoboinyk int
r88e_fw_cmd(struct rtwn_softc * sc,uint8_t id,const void * buf,int len)567453645fSAndriy Voskoboinyk r88e_fw_cmd(struct rtwn_softc *sc, uint8_t id, const void *buf, int len)
577453645fSAndriy Voskoboinyk {
587453645fSAndriy Voskoboinyk 	struct r88e_fw_cmd cmd;
597453645fSAndriy Voskoboinyk 	int ntries, error;
607453645fSAndriy Voskoboinyk 
617453645fSAndriy Voskoboinyk 	if (!(sc->sc_flags & RTWN_FW_LOADED)) {
627453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_FIRMWARE, "%s: firmware "
637453645fSAndriy Voskoboinyk 		    "was not loaded; command (id %d) will be discarded\n",
647453645fSAndriy Voskoboinyk 		    __func__, id);
657453645fSAndriy Voskoboinyk 		return (0);
667453645fSAndriy Voskoboinyk 	}
677453645fSAndriy Voskoboinyk 
687453645fSAndriy Voskoboinyk 	/* Wait for current FW box to be empty. */
6968c3c025SKevin Lo 	for (ntries = 0; ntries < 100; ntries++) {
707453645fSAndriy Voskoboinyk 		if (!(rtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur)))
717453645fSAndriy Voskoboinyk 			break;
727453645fSAndriy Voskoboinyk 		rtwn_delay(sc, 2000);
737453645fSAndriy Voskoboinyk 	}
747453645fSAndriy Voskoboinyk 	if (ntries == 100) {
757453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev,
767453645fSAndriy Voskoboinyk 		    "could not send firmware command\n");
777453645fSAndriy Voskoboinyk 		return (ETIMEDOUT);
787453645fSAndriy Voskoboinyk 	}
797453645fSAndriy Voskoboinyk 	memset(&cmd, 0, sizeof(cmd));
807453645fSAndriy Voskoboinyk 	cmd.id = id;
817453645fSAndriy Voskoboinyk 	KASSERT(len <= sizeof(cmd.msg),
827453645fSAndriy Voskoboinyk 	    ("%s: firmware command too long (%d > %zu)\n",
837453645fSAndriy Voskoboinyk 	    __func__, len, sizeof(cmd.msg)));
847453645fSAndriy Voskoboinyk 	memcpy(cmd.msg, buf, len);
857453645fSAndriy Voskoboinyk 
867453645fSAndriy Voskoboinyk 	/* Write the first word last since that will trigger the FW. */
877453645fSAndriy Voskoboinyk 	if (len > 3) {
887453645fSAndriy Voskoboinyk 		error = rtwn_write_4(sc, R88E_HMEBOX_EXT(sc->fwcur),
897453645fSAndriy Voskoboinyk 		    *(uint32_t *)((uint8_t *)&cmd + 4));
907453645fSAndriy Voskoboinyk 		if (error != 0)
917453645fSAndriy Voskoboinyk 			return (error);
927453645fSAndriy Voskoboinyk 	}
937453645fSAndriy Voskoboinyk 	error = rtwn_write_4(sc, R92C_HMEBOX(sc->fwcur), *(uint32_t *)&cmd);
947453645fSAndriy Voskoboinyk 	if (error != 0)
957453645fSAndriy Voskoboinyk 		return (error);
967453645fSAndriy Voskoboinyk 
977453645fSAndriy Voskoboinyk 	sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX;
987453645fSAndriy Voskoboinyk 
997453645fSAndriy Voskoboinyk 	return (0);
1007453645fSAndriy Voskoboinyk }
1017453645fSAndriy Voskoboinyk 
1027453645fSAndriy Voskoboinyk void
r88e_fw_reset(struct rtwn_softc * sc,int reason)1037453645fSAndriy Voskoboinyk r88e_fw_reset(struct rtwn_softc *sc, int reason)
1047453645fSAndriy Voskoboinyk {
1057453645fSAndriy Voskoboinyk 	uint16_t reg;
1067453645fSAndriy Voskoboinyk 
1077453645fSAndriy Voskoboinyk 	reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN);
1087453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN);
109*44c68782SAndriy Voskoboinyk 
110*44c68782SAndriy Voskoboinyk 	if (reason != RTWN_FW_RESET_SHUTDOWN) {
111*44c68782SAndriy Voskoboinyk 		rtwn_write_2(sc, R92C_SYS_FUNC_EN,
112*44c68782SAndriy Voskoboinyk 		    reg | R92C_SYS_FUNC_EN_CPUEN);
113*44c68782SAndriy Voskoboinyk 	}
1147453645fSAndriy Voskoboinyk }
1157453645fSAndriy Voskoboinyk 
1167453645fSAndriy Voskoboinyk void
r88e_fw_download_enable(struct rtwn_softc * sc,int enable)1177453645fSAndriy Voskoboinyk r88e_fw_download_enable(struct rtwn_softc *sc, int enable)
1187453645fSAndriy Voskoboinyk {
1197453645fSAndriy Voskoboinyk 	if (enable) {
1207453645fSAndriy Voskoboinyk 		/* MCU firmware download enable. */
1217453645fSAndriy Voskoboinyk 		rtwn_setbits_1(sc, R92C_MCUFWDL, 0, R92C_MCUFWDL_EN);
1227453645fSAndriy Voskoboinyk 		/* 8051 reset. */
1237453645fSAndriy Voskoboinyk 		rtwn_setbits_1_shift(sc, R92C_MCUFWDL, R92C_MCUFWDL_ROM_DLEN,
1247453645fSAndriy Voskoboinyk 		    0, 2);
1257453645fSAndriy Voskoboinyk 	} else {
1267453645fSAndriy Voskoboinyk 		/* MCU download disable. */
1277453645fSAndriy Voskoboinyk 		rtwn_setbits_1(sc, R92C_MCUFWDL, R92C_MCUFWDL_EN, 0);
1287453645fSAndriy Voskoboinyk 		/* Reserved for f/w extension. */
1297453645fSAndriy Voskoboinyk 		rtwn_write_1(sc, R92C_MCUFWDL + 1, 0);
1307453645fSAndriy Voskoboinyk 	}
1317453645fSAndriy Voskoboinyk }
1327453645fSAndriy Voskoboinyk #endif
1337453645fSAndriy Voskoboinyk 
1347453645fSAndriy Voskoboinyk void
r88e_macid_enable_link(struct rtwn_softc * sc,int id,int enable)1357453645fSAndriy Voskoboinyk r88e_macid_enable_link(struct rtwn_softc *sc, int id, int enable)
1367453645fSAndriy Voskoboinyk {
1377453645fSAndriy Voskoboinyk 	uint32_t reg;
1387453645fSAndriy Voskoboinyk 
1397453645fSAndriy Voskoboinyk 	reg = R88E_MACID_NO_LINK;
1407453645fSAndriy Voskoboinyk 	if (id > 32)
1417453645fSAndriy Voskoboinyk 		reg += 4;
1427453645fSAndriy Voskoboinyk 
1437453645fSAndriy Voskoboinyk 	if (enable)
1447453645fSAndriy Voskoboinyk 		rtwn_setbits_4(sc, reg, 1 << (id % 32), 0);
1457453645fSAndriy Voskoboinyk 	else
1467453645fSAndriy Voskoboinyk 		rtwn_setbits_4(sc, reg, 0, 1 << (id % 32));
1477453645fSAndriy Voskoboinyk 
1487453645fSAndriy Voskoboinyk 	/* XXX max macid for tx reports */
1497453645fSAndriy Voskoboinyk }
1507453645fSAndriy Voskoboinyk 
1517453645fSAndriy Voskoboinyk void
r88e_set_media_status(struct rtwn_softc * sc,int macid)1527453645fSAndriy Voskoboinyk r88e_set_media_status(struct rtwn_softc *sc, int macid)
1537453645fSAndriy Voskoboinyk {
1547453645fSAndriy Voskoboinyk 	struct r88e_fw_cmd_msrrpt status;
1557453645fSAndriy Voskoboinyk 
1567453645fSAndriy Voskoboinyk 	if (macid & RTWN_MACID_VALID)
1577453645fSAndriy Voskoboinyk 		status.msrb0 = R88E_MSRRPT_B0_ASSOC;
1587453645fSAndriy Voskoboinyk 	else
1597453645fSAndriy Voskoboinyk 		status.msrb0 = R88E_MSRRPT_B0_DISASSOC;
1607453645fSAndriy Voskoboinyk 	status.macid = (macid & ~RTWN_MACID_VALID);
1617453645fSAndriy Voskoboinyk 
1627453645fSAndriy Voskoboinyk 	r88e_macid_enable_link(sc, status.macid,
1637453645fSAndriy Voskoboinyk 	    (macid & RTWN_MACID_VALID) != 0);
1647453645fSAndriy Voskoboinyk 
1657453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE
1667453645fSAndriy Voskoboinyk 	if (r88e_fw_cmd(sc, R88E_CMD_MSR_RPT, &status, sizeof(status)) != 0) {
1677453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "%s: cannot change media status!\n",
1687453645fSAndriy Voskoboinyk 		    __func__);
1697453645fSAndriy Voskoboinyk 	}
1707453645fSAndriy Voskoboinyk #endif
1717453645fSAndriy Voskoboinyk }
1727453645fSAndriy Voskoboinyk 
1737453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE
1747453645fSAndriy Voskoboinyk int
r88e_set_rsvd_page(struct rtwn_softc * sc,int probe_resp,int null,int qos_null)1757453645fSAndriy Voskoboinyk r88e_set_rsvd_page(struct rtwn_softc *sc, int probe_resp, int null,
1767453645fSAndriy Voskoboinyk     int qos_null)
1777453645fSAndriy Voskoboinyk {
1787453645fSAndriy Voskoboinyk 	struct r88e_fw_cmd_rsvdpage rsvd;
1797453645fSAndriy Voskoboinyk 
1807453645fSAndriy Voskoboinyk 	rsvd.probe_resp = probe_resp;
1817453645fSAndriy Voskoboinyk 	rsvd.ps_poll = 0;
1827453645fSAndriy Voskoboinyk 	rsvd.null_data = null;
1837453645fSAndriy Voskoboinyk 	rsvd.null_data_qos = qos_null;
1847453645fSAndriy Voskoboinyk 	rsvd.null_data_qos_bt = 0;
1857453645fSAndriy Voskoboinyk 	return (r88e_fw_cmd(sc, R88E_CMD_RSVD_PAGE, &rsvd, sizeof(rsvd)));
1867453645fSAndriy Voskoboinyk }
1877453645fSAndriy Voskoboinyk 
1887453645fSAndriy Voskoboinyk int
r88e_set_pwrmode(struct rtwn_softc * sc,struct ieee80211vap * vap,int off)1897453645fSAndriy Voskoboinyk r88e_set_pwrmode(struct rtwn_softc *sc, struct ieee80211vap *vap,
1907453645fSAndriy Voskoboinyk     int off)
1917453645fSAndriy Voskoboinyk {
1927453645fSAndriy Voskoboinyk 	struct r88e_fw_cmd_pwrmode mode;
1937453645fSAndriy Voskoboinyk 	int error;
1947453645fSAndriy Voskoboinyk 
1957453645fSAndriy Voskoboinyk 	if (off && vap->iv_state == IEEE80211_S_RUN &&
1967453645fSAndriy Voskoboinyk 	    (vap->iv_flags & IEEE80211_F_PMGTON)) {
1977453645fSAndriy Voskoboinyk 		mode.mode = R88E_PWRMODE_LEG;
1987453645fSAndriy Voskoboinyk 		/*
1997453645fSAndriy Voskoboinyk 		 * TODO: switch to RFOFF state
2007453645fSAndriy Voskoboinyk 		 * (something is missing here - Rx stops with it).
2017453645fSAndriy Voskoboinyk 		 */
2027453645fSAndriy Voskoboinyk #ifdef RTWN_TODO
2037453645fSAndriy Voskoboinyk 		mode.pwr_state = R88E_PWRMODE_STATE_RFOFF;
2047453645fSAndriy Voskoboinyk #else
2057453645fSAndriy Voskoboinyk 		mode.pwr_state = R88E_PWRMODE_STATE_RFON;
2067453645fSAndriy Voskoboinyk #endif
2077453645fSAndriy Voskoboinyk 	} else {
2087453645fSAndriy Voskoboinyk 		mode.mode = R88E_PWRMODE_CAM;
2097453645fSAndriy Voskoboinyk 		mode.pwr_state = R88E_PWRMODE_STATE_ALLON;
2107453645fSAndriy Voskoboinyk 	}
2117453645fSAndriy Voskoboinyk 	mode.pwrb1 =
2127453645fSAndriy Voskoboinyk 	    SM(R88E_PWRMODE_B1_SMART_PS, R88E_PWRMODE_B1_LEG_NULLDATA) |
2137453645fSAndriy Voskoboinyk 	    SM(R88E_PWRMODE_B1_RLBM, R88E_PWRMODE_B1_MODE_MIN);
2147453645fSAndriy Voskoboinyk 	/* XXX ignored */
2157453645fSAndriy Voskoboinyk 	mode.bcn_pass = 0;
2167453645fSAndriy Voskoboinyk 	mode.queue_uapsd = 0;
2177453645fSAndriy Voskoboinyk 	error = r88e_fw_cmd(sc, R88E_CMD_SET_PWRMODE, &mode, sizeof(mode));
2187453645fSAndriy Voskoboinyk 	if (error != 0) {
2197453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev,
2207453645fSAndriy Voskoboinyk 		    "%s: CMD_SET_PWRMODE was not sent, error %d\n",
2217453645fSAndriy Voskoboinyk 		    __func__, error);
2227453645fSAndriy Voskoboinyk 	}
2237453645fSAndriy Voskoboinyk 
2247453645fSAndriy Voskoboinyk 	return (error);
2257453645fSAndriy Voskoboinyk }
2267453645fSAndriy Voskoboinyk #endif
227