xref: /freebsd/sys/dev/rtwn/rtl8188e/r88e_fw.c (revision edf8578117e8844e02c0121147f45e4609b30680)
1 /*	$OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
5  * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
6  * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/cdefs.h>
22 #include "opt_wlan.h"
23 
24 #include <sys/param.h>
25 #include <sys/lock.h>
26 #include <sys/mutex.h>
27 #include <sys/mbuf.h>
28 #include <sys/kernel.h>
29 #include <sys/socket.h>
30 #include <sys/systm.h>
31 #include <sys/malloc.h>
32 #include <sys/queue.h>
33 #include <sys/taskqueue.h>
34 #include <sys/bus.h>
35 #include <sys/endian.h>
36 #include <sys/linker.h>
37 
38 #include <net/if.h>
39 #include <net/ethernet.h>
40 #include <net/if_media.h>
41 
42 #include <net80211/ieee80211_var.h>
43 #include <net80211/ieee80211_radiotap.h>
44 
45 #include <dev/rtwn/if_rtwnreg.h>
46 #include <dev/rtwn/if_rtwnvar.h>
47 
48 #include <dev/rtwn/if_rtwn_debug.h>
49 
50 #include <dev/rtwn/rtl8188e/r88e.h>
51 #include <dev/rtwn/rtl8188e/r88e_reg.h>
52 #include <dev/rtwn/rtl8188e/r88e_fw_cmd.h>
53 
54 #ifndef RTWN_WITHOUT_UCODE
55 int
56 r88e_fw_cmd(struct rtwn_softc *sc, uint8_t id, const void *buf, int len)
57 {
58 	struct r88e_fw_cmd cmd;
59 	int ntries, error;
60 
61 	if (!(sc->sc_flags & RTWN_FW_LOADED)) {
62 		RTWN_DPRINTF(sc, RTWN_DEBUG_FIRMWARE, "%s: firmware "
63 		    "was not loaded; command (id %d) will be discarded\n",
64 		    __func__, id);
65 		return (0);
66 	}
67 
68 	/* Wait for current FW box to be empty. */
69 	for (ntries = 0; ntries < 100; ntries++) {
70 		if (!(rtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur)))
71 			break;
72 		rtwn_delay(sc, 2000);
73 	}
74 	if (ntries == 100) {
75 		device_printf(sc->sc_dev,
76 		    "could not send firmware command\n");
77 		return (ETIMEDOUT);
78 	}
79 	memset(&cmd, 0, sizeof(cmd));
80 	cmd.id = id;
81 	KASSERT(len <= sizeof(cmd.msg),
82 	    ("%s: firmware command too long (%d > %zu)\n",
83 	    __func__, len, sizeof(cmd.msg)));
84 	memcpy(cmd.msg, buf, len);
85 
86 	/* Write the first word last since that will trigger the FW. */
87 	if (len > 3) {
88 		error = rtwn_write_4(sc, R88E_HMEBOX_EXT(sc->fwcur),
89 		    *(uint32_t *)((uint8_t *)&cmd + 4));
90 		if (error != 0)
91 			return (error);
92 	}
93 	error = rtwn_write_4(sc, R92C_HMEBOX(sc->fwcur), *(uint32_t *)&cmd);
94 	if (error != 0)
95 		return (error);
96 
97 	sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX;
98 
99 	return (0);
100 }
101 
102 void
103 r88e_fw_reset(struct rtwn_softc *sc, int reason)
104 {
105 	uint16_t reg;
106 
107 	reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN);
108 	rtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN);
109 
110 	if (reason != RTWN_FW_RESET_SHUTDOWN) {
111 		rtwn_write_2(sc, R92C_SYS_FUNC_EN,
112 		    reg | R92C_SYS_FUNC_EN_CPUEN);
113 	}
114 }
115 
116 void
117 r88e_fw_download_enable(struct rtwn_softc *sc, int enable)
118 {
119 	if (enable) {
120 		/* MCU firmware download enable. */
121 		rtwn_setbits_1(sc, R92C_MCUFWDL, 0, R92C_MCUFWDL_EN);
122 		/* 8051 reset. */
123 		rtwn_setbits_1_shift(sc, R92C_MCUFWDL, R92C_MCUFWDL_ROM_DLEN,
124 		    0, 2);
125 	} else {
126 		/* MCU download disable. */
127 		rtwn_setbits_1(sc, R92C_MCUFWDL, R92C_MCUFWDL_EN, 0);
128 		/* Reserved for f/w extension. */
129 		rtwn_write_1(sc, R92C_MCUFWDL + 1, 0);
130 	}
131 }
132 #endif
133 
134 void
135 r88e_macid_enable_link(struct rtwn_softc *sc, int id, int enable)
136 {
137 	uint32_t reg;
138 
139 	reg = R88E_MACID_NO_LINK;
140 	if (id > 32)
141 		reg += 4;
142 
143 	if (enable)
144 		rtwn_setbits_4(sc, reg, 1 << (id % 32), 0);
145 	else
146 		rtwn_setbits_4(sc, reg, 0, 1 << (id % 32));
147 
148 	/* XXX max macid for tx reports */
149 }
150 
151 void
152 r88e_set_media_status(struct rtwn_softc *sc, int macid)
153 {
154 	struct r88e_fw_cmd_msrrpt status;
155 
156 	if (macid & RTWN_MACID_VALID)
157 		status.msrb0 = R88E_MSRRPT_B0_ASSOC;
158 	else
159 		status.msrb0 = R88E_MSRRPT_B0_DISASSOC;
160 	status.macid = (macid & ~RTWN_MACID_VALID);
161 
162 	r88e_macid_enable_link(sc, status.macid,
163 	    (macid & RTWN_MACID_VALID) != 0);
164 
165 #ifndef RTWN_WITHOUT_UCODE
166 	if (r88e_fw_cmd(sc, R88E_CMD_MSR_RPT, &status, sizeof(status)) != 0) {
167 		device_printf(sc->sc_dev, "%s: cannot change media status!\n",
168 		    __func__);
169 	}
170 #endif
171 }
172 
173 #ifndef RTWN_WITHOUT_UCODE
174 int
175 r88e_set_rsvd_page(struct rtwn_softc *sc, int probe_resp, int null,
176     int qos_null)
177 {
178 	struct r88e_fw_cmd_rsvdpage rsvd;
179 
180 	rsvd.probe_resp = probe_resp;
181 	rsvd.ps_poll = 0;
182 	rsvd.null_data = null;
183 	rsvd.null_data_qos = qos_null;
184 	rsvd.null_data_qos_bt = 0;
185 	return (r88e_fw_cmd(sc, R88E_CMD_RSVD_PAGE, &rsvd, sizeof(rsvd)));
186 }
187 
188 int
189 r88e_set_pwrmode(struct rtwn_softc *sc, struct ieee80211vap *vap,
190     int off)
191 {
192 	struct r88e_fw_cmd_pwrmode mode;
193 	int error;
194 
195 	if (off && vap->iv_state == IEEE80211_S_RUN &&
196 	    (vap->iv_flags & IEEE80211_F_PMGTON)) {
197 		mode.mode = R88E_PWRMODE_LEG;
198 		/*
199 		 * TODO: switch to RFOFF state
200 		 * (something is missing here - Rx stops with it).
201 		 */
202 #ifdef RTWN_TODO
203 		mode.pwr_state = R88E_PWRMODE_STATE_RFOFF;
204 #else
205 		mode.pwr_state = R88E_PWRMODE_STATE_RFON;
206 #endif
207 	} else {
208 		mode.mode = R88E_PWRMODE_CAM;
209 		mode.pwr_state = R88E_PWRMODE_STATE_ALLON;
210 	}
211 	mode.pwrb1 =
212 	    SM(R88E_PWRMODE_B1_SMART_PS, R88E_PWRMODE_B1_LEG_NULLDATA) |
213 	    SM(R88E_PWRMODE_B1_RLBM, R88E_PWRMODE_B1_MODE_MIN);
214 	/* XXX ignored */
215 	mode.bcn_pass = 0;
216 	mode.queue_uapsd = 0;
217 	error = r88e_fw_cmd(sc, R88E_CMD_SET_PWRMODE, &mode, sizeof(mode));
218 	if (error != 0) {
219 		device_printf(sc->sc_dev,
220 		    "%s: CMD_SET_PWRMODE was not sent, error %d\n",
221 		    __func__, error);
222 	}
223 
224 	return (error);
225 }
226 #endif
227