xref: /freebsd/sys/dev/rtwn/if_rtwn_efuse.c (revision 63d1fd5970ec814904aa0f4580b10a0d302d08b2)
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 
39 #include <net/if.h>
40 #include <net/ethernet.h>
41 #include <net/if_media.h>
42 
43 #include <net80211/ieee80211_var.h>
44 #include <net80211/ieee80211_radiotap.h>
45 
46 #include <dev/rtwn/if_rtwnreg.h>
47 #include <dev/rtwn/if_rtwnvar.h>
48 
49 #include <dev/rtwn/if_rtwn_debug.h>
50 #include <dev/rtwn/if_rtwn_efuse.h>
51 
52 #include <dev/rtwn/rtl8192c/r92c_reg.h>
53 
54 
55 static int
56 rtwn_efuse_switch_power(struct rtwn_softc *sc)
57 {
58 	uint32_t reg;
59 	int error;
60 
61 	error = rtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_ON);
62 	if (error != 0)
63 		return (error);
64 
65 	reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN);
66 	if (!(reg & R92C_SYS_FUNC_EN_ELDR)) {
67 		error = rtwn_write_2(sc, R92C_SYS_FUNC_EN,
68 		    reg | R92C_SYS_FUNC_EN_ELDR);
69 		if (error != 0)
70 			return (error);
71 	}
72 	reg = rtwn_read_2(sc, R92C_SYS_CLKR);
73 	if ((reg & (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) !=
74 	    (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) {
75 		error = rtwn_write_2(sc, R92C_SYS_CLKR,
76 		    reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M);
77 		if (error != 0)
78 			return (error);
79 	}
80 
81 	return (0);
82 }
83 
84 int
85 rtwn_efuse_read_next(struct rtwn_softc *sc, uint8_t *val)
86 {
87 	uint32_t reg;
88 	int ntries, error;
89 
90 	if (sc->next_rom_addr >= sc->efuse_maxlen)
91 		return (EFAULT);
92 
93 	reg = rtwn_read_4(sc, R92C_EFUSE_CTRL);
94 	reg = RW(reg, R92C_EFUSE_CTRL_ADDR, sc->next_rom_addr);
95 	reg &= ~R92C_EFUSE_CTRL_VALID;
96 
97 	error = rtwn_write_4(sc, R92C_EFUSE_CTRL, reg);
98 	if (error != 0)
99 		return (error);
100 	/* Wait for read operation to complete. */
101 	for (ntries = 0; ntries < 100; ntries++) {
102 		reg = rtwn_read_4(sc, R92C_EFUSE_CTRL);
103 		if (reg & R92C_EFUSE_CTRL_VALID)
104 			break;
105 		rtwn_delay(sc, 10);
106 	}
107 	if (ntries == 100) {
108 		device_printf(sc->sc_dev,
109 		    "could not read efuse byte at address 0x%x\n",
110 		    sc->next_rom_addr);
111 		return (ETIMEDOUT);
112 	}
113 
114 	*val = MS(reg, R92C_EFUSE_CTRL_DATA);
115 	sc->next_rom_addr++;
116 
117 	return (0);
118 }
119 
120 static int
121 rtwn_efuse_read_data(struct rtwn_softc *sc, uint8_t *rom, uint8_t off,
122     uint8_t msk)
123 {
124 	uint8_t reg;
125 	int addr, i, error;
126 
127 	for (i = 0; i < 4; i++) {
128 		if (msk & (1 << i))
129 			continue;
130 
131 		addr = off * 8 + i * 2;
132 		if (addr + 1 >= sc->efuse_maplen)
133 			return (EFAULT);
134 
135 		error = rtwn_efuse_read_next(sc, &reg);
136 		if (error != 0)
137 			return (error);
138 		RTWN_DPRINTF(sc, RTWN_DEBUG_ROM, "rom[0x%03X] == 0x%02X\n",
139 		    addr, reg);
140 		rom[addr] = reg;
141 
142 		error = rtwn_efuse_read_next(sc, &reg);
143 		if (error != 0)
144 			return (error);
145 		RTWN_DPRINTF(sc, RTWN_DEBUG_ROM, "rom[0x%03X] == 0x%02X\n",
146 		    addr + 1, reg);
147 		rom[addr + 1] = reg;
148 	}
149 
150 	return (0);
151 }
152 
153 #ifdef RTWN_DEBUG
154 static void
155 rtwn_dump_rom_contents(struct rtwn_softc *sc, uint8_t *rom, uint16_t size)
156 {
157 	int i;
158 
159 	/* Dump ROM contents. */
160 	device_printf(sc->sc_dev, "%s:", __func__);
161 	for (i = 0; i < size; i++) {
162 		if (i % 32 == 0)
163 			printf("\n%03X: ", i);
164 		else if (i % 4 == 0)
165 			printf(" ");
166 
167 		printf("%02X", rom[i]);
168 	}
169 	printf("\n");
170 }
171 #endif
172 
173 static int
174 rtwn_efuse_read(struct rtwn_softc *sc, uint8_t *rom, uint16_t size)
175 {
176 #define RTWN_CHK(res) do {	\
177 	if ((error = res) != 0)	\
178 		goto end;	\
179 } while(0)
180 	uint8_t msk, off, reg;
181 	int error;
182 
183 	/* Read full ROM image. */
184 	sc->next_rom_addr = 0;
185 	memset(rom, 0xff, size);
186 
187 	RTWN_CHK(rtwn_efuse_read_next(sc, &reg));
188 	while (reg != 0xff) {
189 		/* check for extended header */
190 		if ((sc->sc_flags & RTWN_FLAG_EXT_HDR) &&
191 		    (reg & 0x1f) == 0x0f) {
192 			off = reg >> 5;
193 			RTWN_CHK(rtwn_efuse_read_next(sc, &reg));
194 
195 			if ((reg & 0x0f) != 0x0f)
196 				off = ((reg & 0xf0) >> 1) | off;
197 			else
198 				continue;
199 		} else
200 			off = reg >> 4;
201 		msk = reg & 0xf;
202 
203 		RTWN_CHK(rtwn_efuse_read_data(sc, rom, off, msk));
204 		RTWN_CHK(rtwn_efuse_read_next(sc, &reg));
205 	}
206 
207 end:
208 
209 #ifdef RTWN_DEBUG
210 	if (sc->sc_debug & RTWN_DEBUG_ROM)
211 		rtwn_dump_rom_contents(sc, rom, size);
212 #endif
213 
214 	/* Device-specific. */
215 	rtwn_efuse_postread(sc);
216 
217 	if (error != 0) {
218 		device_printf(sc->sc_dev, "%s: error while reading ROM\n",
219 		    __func__);
220 	}
221 
222 	return (error);
223 #undef RTWN_CHK
224 }
225 
226 static int
227 rtwn_efuse_read_prepare(struct rtwn_softc *sc, uint8_t *rom, uint16_t size)
228 {
229 	int error;
230 
231 	error = rtwn_efuse_switch_power(sc);
232 	if (error != 0)
233 		goto fail;
234 
235 	error = rtwn_efuse_read(sc, rom, size);
236 
237 fail:
238 	rtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_OFF);
239 
240 	return (error);
241 }
242 
243 int
244 rtwn_read_rom(struct rtwn_softc *sc)
245 {
246 	uint8_t *rom;
247 	int error;
248 
249 	rom = malloc(sc->efuse_maplen, M_TEMP, M_WAITOK);
250 
251 	/* Read full ROM image. */
252 	RTWN_LOCK(sc);
253 	error = rtwn_efuse_read_prepare(sc, rom, sc->efuse_maplen);
254 	RTWN_UNLOCK(sc);
255 	if (error != 0)
256 		goto fail;
257 
258 	/* Parse & save data in softc. */
259 	rtwn_parse_rom(sc, rom);
260 
261 fail:
262 	free(rom, M_TEMP);
263 
264 	return (error);
265 }
266