]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/rtwn/if_rtwn_fw.c
MFV r356143:
[FreeBSD/FreeBSD.git] / sys / dev / rtwn / if_rtwn_fw.c
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 __FBSDID("$FreeBSD$");
23
24 #include "opt_wlan.h"
25
26 #include <sys/param.h>
27 #include <sys/lock.h>
28 #include <sys/mutex.h>
29 #include <sys/mbuf.h>
30 #include <sys/kernel.h>
31 #include <sys/socket.h>
32 #include <sys/systm.h>
33 #include <sys/malloc.h>
34 #include <sys/queue.h>
35 #include <sys/taskqueue.h>
36 #include <sys/bus.h>
37 #include <sys/endian.h>
38 #include <sys/linker.h>
39 #include <sys/firmware.h>
40
41 #include <net/if.h>
42 #include <net/ethernet.h>
43 #include <net/if_media.h>
44
45 #include <net80211/ieee80211_var.h>
46 #include <net80211/ieee80211_radiotap.h>
47
48 #include <dev/rtwn/if_rtwnreg.h>
49 #include <dev/rtwn/if_rtwnvar.h>
50
51 #include <dev/rtwn/if_rtwn_debug.h>
52 #include <dev/rtwn/if_rtwn_fw.h>
53
54 #include <dev/rtwn/rtl8192c/r92c_reg.h>
55
56
57 #ifndef RTWN_WITHOUT_UCODE
58 static int
59 rtwn_fw_loadpage(struct rtwn_softc *sc, int page, const uint8_t *buf,
60     int len)
61 {
62         uint32_t reg;
63         uint16_t off;
64         int mlen, error;
65
66         reg = rtwn_read_4(sc, R92C_MCUFWDL);
67         reg = RW(reg, R92C_MCUFWDL_PAGE, page);
68         rtwn_write_4(sc, R92C_MCUFWDL, reg);
69
70         error = 0;
71         off = R92C_FW_START_ADDR;
72         while (len > 0) {
73                 if (len > R92C_FW_MAX_BLOCK_SIZE)
74                         mlen = R92C_FW_MAX_BLOCK_SIZE;
75                 else if (len > 4)
76                         mlen = 4;
77                 else
78                         mlen = 1;
79                 error = rtwn_fw_write_block(sc, buf, off, mlen);
80                 if (error != 0)
81                         break;
82                 off += mlen;
83                 buf += mlen;
84                 len -= mlen;
85         }
86
87         if (error != 0) {
88                 RTWN_DPRINTF(sc, RTWN_DEBUG_FIRMWARE,
89                     "%s: could not load firmware page %d (offset %u)\n",
90                     __func__, page, off);
91         }
92
93         return (error);
94 }
95
96 static int
97 rtwn_fw_checksum_report(struct rtwn_softc *sc)
98 {
99         int ntries;
100
101         for (ntries = 0; ntries < 25; ntries++) {
102                 if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_CHKSUM_RPT)
103                         break;
104                 rtwn_delay(sc, 10000);
105         }
106         if (ntries == 25) {
107                 RTWN_DPRINTF(sc, RTWN_DEBUG_FIRMWARE,
108                     "timeout waiting for checksum report\n");
109                 return (ETIMEDOUT);
110         }
111
112         return (0);
113 }
114
115 int
116 rtwn_load_firmware(struct rtwn_softc *sc)
117 {
118         const struct firmware *fw;
119         const struct r92c_fw_hdr *hdr;
120         const u_char *ptr;
121         size_t len;
122         int ntries, error;
123
124         /* Read firmware image from the filesystem. */
125         RTWN_UNLOCK(sc);
126         fw = firmware_get(sc->fwname);
127         RTWN_LOCK(sc);
128         if (fw == NULL) {
129                 device_printf(sc->sc_dev,
130                     "failed loadfirmware of file %s\n", sc->fwname);
131                 return (ENOENT);
132         }
133
134         len = fw->datasize;
135         if (len < sizeof(*hdr) || len > sc->fwsize_limit) {
136                 device_printf(sc->sc_dev, "wrong firmware size (%zu)\n", len);
137                 error = EINVAL;
138                 goto fail;
139         }
140         ptr = fw->data;
141         hdr = (const struct r92c_fw_hdr *)ptr;
142         /* Check if there is a valid FW header and skip it. */
143         if ((le16toh(hdr->signature) >> 4) == sc->fwsig) {
144                 sc->fwver = le16toh(hdr->version);
145
146                 RTWN_DPRINTF(sc, RTWN_DEBUG_FIRMWARE,
147                     "FW V%u.%u %02u-%02u %02u:%02u\n",
148                     le16toh(hdr->version), le16toh(hdr->subversion),
149                     hdr->month, hdr->date, hdr->hour, hdr->minute);
150                 ptr += sizeof(*hdr);
151                 len -= sizeof(*hdr);
152         }
153
154         if (rtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL) {
155                 rtwn_write_1(sc, R92C_MCUFWDL, 0);
156                 rtwn_fw_reset(sc, RTWN_FW_RESET_DOWNLOAD);
157         }
158
159         /* Enable firmware download. */
160         rtwn_fw_download_enable(sc, 1);
161
162         error = 0;      /* compiler warning */
163         for (ntries = 0; ntries < 3; ntries++) {
164                 const u_char *curr_ptr = ptr;
165                 const int maxpages = len / R92C_FW_PAGE_SIZE;
166                 int page;
167
168                 /* Reset the FWDL checksum. */
169                 rtwn_setbits_1(sc, R92C_MCUFWDL, 0, R92C_MCUFWDL_CHKSUM_RPT);
170
171                 for (page = 0; page < maxpages; page++) {
172                         error = rtwn_fw_loadpage(sc, page, curr_ptr,
173                             R92C_FW_PAGE_SIZE);
174                         if (error != 0)
175                                 break;
176                         curr_ptr += R92C_FW_PAGE_SIZE;
177                 }
178                 if (page != maxpages)
179                         continue;
180
181                 if (len % R92C_FW_PAGE_SIZE != 0) {
182                         error = rtwn_fw_loadpage(sc, page, curr_ptr,
183                             len % R92C_FW_PAGE_SIZE);
184                         if (error != 0)
185                                 continue;
186                 }
187
188                 /* Wait for checksum report. */
189                 error = rtwn_fw_checksum_report(sc);
190                 if (error == 0)
191                         break;
192         }
193         if (ntries == 3) {
194                 device_printf(sc->sc_dev,
195                     "%s: failed to upload firmware %s (error %d)\n",
196                     __func__, sc->fwname, error);
197                 goto fail;
198         }
199
200         /* Disable firmware download. */
201         rtwn_fw_download_enable(sc, 0);
202
203         rtwn_setbits_4(sc, R92C_MCUFWDL, R92C_MCUFWDL_WINTINI_RDY,
204             R92C_MCUFWDL_RDY);
205
206         rtwn_fw_reset(sc, RTWN_FW_RESET_CHECKSUM);
207
208         /* Wait for firmware readiness. */
209         for (ntries = 0; ntries < 20; ntries++) {
210                 if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_WINTINI_RDY)
211                         break;
212                 rtwn_delay(sc, 10000);
213         }
214         if (ntries == 20) {
215                 device_printf(sc->sc_dev,
216                     "timeout waiting for firmware readiness\n");
217                 error = ETIMEDOUT;
218                 goto fail;
219         }
220 fail:
221         firmware_put(fw, FIRMWARE_UNLOAD);
222         return (error);
223 }
224 #endif