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