xref: /freebsd/sys/dev/rtwn/if_rtwn_efuse.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1*7453645fSAndriy Voskoboinyk /*	$OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $	*/
2*7453645fSAndriy Voskoboinyk 
3*7453645fSAndriy Voskoboinyk /*-
4*7453645fSAndriy Voskoboinyk  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
5*7453645fSAndriy Voskoboinyk  * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
6*7453645fSAndriy Voskoboinyk  * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org>
7*7453645fSAndriy Voskoboinyk  *
8*7453645fSAndriy Voskoboinyk  * Permission to use, copy, modify, and distribute this software for any
9*7453645fSAndriy Voskoboinyk  * purpose with or without fee is hereby granted, provided that the above
10*7453645fSAndriy Voskoboinyk  * copyright notice and this permission notice appear in all copies.
11*7453645fSAndriy Voskoboinyk  *
12*7453645fSAndriy Voskoboinyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13*7453645fSAndriy Voskoboinyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14*7453645fSAndriy Voskoboinyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15*7453645fSAndriy Voskoboinyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16*7453645fSAndriy Voskoboinyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17*7453645fSAndriy Voskoboinyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18*7453645fSAndriy Voskoboinyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19*7453645fSAndriy Voskoboinyk  */
20*7453645fSAndriy Voskoboinyk 
21*7453645fSAndriy Voskoboinyk #include <sys/cdefs.h>
22*7453645fSAndriy Voskoboinyk #include "opt_wlan.h"
23*7453645fSAndriy Voskoboinyk 
24*7453645fSAndriy Voskoboinyk #include <sys/param.h>
25*7453645fSAndriy Voskoboinyk #include <sys/lock.h>
26*7453645fSAndriy Voskoboinyk #include <sys/mutex.h>
27*7453645fSAndriy Voskoboinyk #include <sys/mbuf.h>
28*7453645fSAndriy Voskoboinyk #include <sys/kernel.h>
29*7453645fSAndriy Voskoboinyk #include <sys/socket.h>
30*7453645fSAndriy Voskoboinyk #include <sys/systm.h>
31*7453645fSAndriy Voskoboinyk #include <sys/malloc.h>
32*7453645fSAndriy Voskoboinyk #include <sys/queue.h>
33*7453645fSAndriy Voskoboinyk #include <sys/taskqueue.h>
34*7453645fSAndriy Voskoboinyk #include <sys/bus.h>
35*7453645fSAndriy Voskoboinyk #include <sys/endian.h>
36*7453645fSAndriy Voskoboinyk 
37*7453645fSAndriy Voskoboinyk #include <net/if.h>
38*7453645fSAndriy Voskoboinyk #include <net/ethernet.h>
39*7453645fSAndriy Voskoboinyk #include <net/if_media.h>
40*7453645fSAndriy Voskoboinyk 
41*7453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
42*7453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h>
43*7453645fSAndriy Voskoboinyk 
44*7453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
45*7453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
46*7453645fSAndriy Voskoboinyk 
47*7453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
48*7453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_efuse.h>
49*7453645fSAndriy Voskoboinyk 
50*7453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c_reg.h>
51*7453645fSAndriy Voskoboinyk 
52*7453645fSAndriy Voskoboinyk static int
rtwn_efuse_switch_power(struct rtwn_softc * sc)53*7453645fSAndriy Voskoboinyk rtwn_efuse_switch_power(struct rtwn_softc *sc)
54*7453645fSAndriy Voskoboinyk {
55*7453645fSAndriy Voskoboinyk 	uint32_t reg;
56*7453645fSAndriy Voskoboinyk 	int error;
57*7453645fSAndriy Voskoboinyk 
58*7453645fSAndriy Voskoboinyk 	error = rtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_ON);
59*7453645fSAndriy Voskoboinyk 	if (error != 0)
60*7453645fSAndriy Voskoboinyk 		return (error);
61*7453645fSAndriy Voskoboinyk 
62*7453645fSAndriy Voskoboinyk 	reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN);
63*7453645fSAndriy Voskoboinyk 	if (!(reg & R92C_SYS_FUNC_EN_ELDR)) {
64*7453645fSAndriy Voskoboinyk 		error = rtwn_write_2(sc, R92C_SYS_FUNC_EN,
65*7453645fSAndriy Voskoboinyk 		    reg | R92C_SYS_FUNC_EN_ELDR);
66*7453645fSAndriy Voskoboinyk 		if (error != 0)
67*7453645fSAndriy Voskoboinyk 			return (error);
68*7453645fSAndriy Voskoboinyk 	}
69*7453645fSAndriy Voskoboinyk 	reg = rtwn_read_2(sc, R92C_SYS_CLKR);
70*7453645fSAndriy Voskoboinyk 	if ((reg & (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) !=
71*7453645fSAndriy Voskoboinyk 	    (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) {
72*7453645fSAndriy Voskoboinyk 		error = rtwn_write_2(sc, R92C_SYS_CLKR,
73*7453645fSAndriy Voskoboinyk 		    reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M);
74*7453645fSAndriy Voskoboinyk 		if (error != 0)
75*7453645fSAndriy Voskoboinyk 			return (error);
76*7453645fSAndriy Voskoboinyk 	}
77*7453645fSAndriy Voskoboinyk 
78*7453645fSAndriy Voskoboinyk 	return (0);
79*7453645fSAndriy Voskoboinyk }
80*7453645fSAndriy Voskoboinyk 
81*7453645fSAndriy Voskoboinyk int
rtwn_efuse_read_next(struct rtwn_softc * sc,uint8_t * val)82*7453645fSAndriy Voskoboinyk rtwn_efuse_read_next(struct rtwn_softc *sc, uint8_t *val)
83*7453645fSAndriy Voskoboinyk {
84*7453645fSAndriy Voskoboinyk 	uint32_t reg;
85*7453645fSAndriy Voskoboinyk 	int ntries, error;
86*7453645fSAndriy Voskoboinyk 
87*7453645fSAndriy Voskoboinyk 	if (sc->next_rom_addr >= sc->efuse_maxlen)
88*7453645fSAndriy Voskoboinyk 		return (EFAULT);
89*7453645fSAndriy Voskoboinyk 
90*7453645fSAndriy Voskoboinyk 	reg = rtwn_read_4(sc, R92C_EFUSE_CTRL);
91*7453645fSAndriy Voskoboinyk 	reg = RW(reg, R92C_EFUSE_CTRL_ADDR, sc->next_rom_addr);
92*7453645fSAndriy Voskoboinyk 	reg &= ~R92C_EFUSE_CTRL_VALID;
93*7453645fSAndriy Voskoboinyk 
94*7453645fSAndriy Voskoboinyk 	error = rtwn_write_4(sc, R92C_EFUSE_CTRL, reg);
95*7453645fSAndriy Voskoboinyk 	if (error != 0)
96*7453645fSAndriy Voskoboinyk 		return (error);
97*7453645fSAndriy Voskoboinyk 	/* Wait for read operation to complete. */
98*7453645fSAndriy Voskoboinyk 	for (ntries = 0; ntries < 100; ntries++) {
99*7453645fSAndriy Voskoboinyk 		reg = rtwn_read_4(sc, R92C_EFUSE_CTRL);
100*7453645fSAndriy Voskoboinyk 		if (reg & R92C_EFUSE_CTRL_VALID)
101*7453645fSAndriy Voskoboinyk 			break;
102*7453645fSAndriy Voskoboinyk 		rtwn_delay(sc, 10);
103*7453645fSAndriy Voskoboinyk 	}
104*7453645fSAndriy Voskoboinyk 	if (ntries == 100) {
105*7453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev,
106*7453645fSAndriy Voskoboinyk 		    "could not read efuse byte at address 0x%x\n",
107*7453645fSAndriy Voskoboinyk 		    sc->next_rom_addr);
108*7453645fSAndriy Voskoboinyk 		return (ETIMEDOUT);
109*7453645fSAndriy Voskoboinyk 	}
110*7453645fSAndriy Voskoboinyk 
111*7453645fSAndriy Voskoboinyk 	*val = MS(reg, R92C_EFUSE_CTRL_DATA);
112*7453645fSAndriy Voskoboinyk 	sc->next_rom_addr++;
113*7453645fSAndriy Voskoboinyk 
114*7453645fSAndriy Voskoboinyk 	return (0);
115*7453645fSAndriy Voskoboinyk }
116*7453645fSAndriy Voskoboinyk 
117*7453645fSAndriy Voskoboinyk static int
rtwn_efuse_read_data(struct rtwn_softc * sc,uint8_t * rom,uint8_t off,uint8_t msk)118*7453645fSAndriy Voskoboinyk rtwn_efuse_read_data(struct rtwn_softc *sc, uint8_t *rom, uint8_t off,
119*7453645fSAndriy Voskoboinyk     uint8_t msk)
120*7453645fSAndriy Voskoboinyk {
121*7453645fSAndriy Voskoboinyk 	uint8_t reg;
122*7453645fSAndriy Voskoboinyk 	int addr, i, error;
123*7453645fSAndriy Voskoboinyk 
124*7453645fSAndriy Voskoboinyk 	for (i = 0; i < 4; i++) {
125*7453645fSAndriy Voskoboinyk 		if (msk & (1 << i))
126*7453645fSAndriy Voskoboinyk 			continue;
127*7453645fSAndriy Voskoboinyk 
128*7453645fSAndriy Voskoboinyk 		addr = off * 8 + i * 2;
129*7453645fSAndriy Voskoboinyk 		if (addr + 1 >= sc->efuse_maplen)
130*7453645fSAndriy Voskoboinyk 			return (EFAULT);
131*7453645fSAndriy Voskoboinyk 
132*7453645fSAndriy Voskoboinyk 		error = rtwn_efuse_read_next(sc, &reg);
133*7453645fSAndriy Voskoboinyk 		if (error != 0)
134*7453645fSAndriy Voskoboinyk 			return (error);
135*7453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_ROM, "rom[0x%03X] == 0x%02X\n",
136*7453645fSAndriy Voskoboinyk 		    addr, reg);
137*7453645fSAndriy Voskoboinyk 		rom[addr] = reg;
138*7453645fSAndriy Voskoboinyk 
139*7453645fSAndriy Voskoboinyk 		error = rtwn_efuse_read_next(sc, &reg);
140*7453645fSAndriy Voskoboinyk 		if (error != 0)
141*7453645fSAndriy Voskoboinyk 			return (error);
142*7453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_ROM, "rom[0x%03X] == 0x%02X\n",
143*7453645fSAndriy Voskoboinyk 		    addr + 1, reg);
144*7453645fSAndriy Voskoboinyk 		rom[addr + 1] = reg;
145*7453645fSAndriy Voskoboinyk 	}
146*7453645fSAndriy Voskoboinyk 
147*7453645fSAndriy Voskoboinyk 	return (0);
148*7453645fSAndriy Voskoboinyk }
149*7453645fSAndriy Voskoboinyk 
150*7453645fSAndriy Voskoboinyk #ifdef RTWN_DEBUG
151*7453645fSAndriy Voskoboinyk static void
rtwn_dump_rom_contents(struct rtwn_softc * sc,uint8_t * rom,uint16_t size)152*7453645fSAndriy Voskoboinyk rtwn_dump_rom_contents(struct rtwn_softc *sc, uint8_t *rom, uint16_t size)
153*7453645fSAndriy Voskoboinyk {
154*7453645fSAndriy Voskoboinyk 	int i;
155*7453645fSAndriy Voskoboinyk 
156*7453645fSAndriy Voskoboinyk 	/* Dump ROM contents. */
157*7453645fSAndriy Voskoboinyk 	device_printf(sc->sc_dev, "%s:", __func__);
158*7453645fSAndriy Voskoboinyk 	for (i = 0; i < size; i++) {
159*7453645fSAndriy Voskoboinyk 		if (i % 32 == 0)
160*7453645fSAndriy Voskoboinyk 			printf("\n%03X: ", i);
161*7453645fSAndriy Voskoboinyk 		else if (i % 4 == 0)
162*7453645fSAndriy Voskoboinyk 			printf(" ");
163*7453645fSAndriy Voskoboinyk 
164*7453645fSAndriy Voskoboinyk 		printf("%02X", rom[i]);
165*7453645fSAndriy Voskoboinyk 	}
166*7453645fSAndriy Voskoboinyk 	printf("\n");
167*7453645fSAndriy Voskoboinyk }
168*7453645fSAndriy Voskoboinyk #endif
169*7453645fSAndriy Voskoboinyk 
170*7453645fSAndriy Voskoboinyk static int
rtwn_efuse_read(struct rtwn_softc * sc,uint8_t * rom,uint16_t size)171*7453645fSAndriy Voskoboinyk rtwn_efuse_read(struct rtwn_softc *sc, uint8_t *rom, uint16_t size)
172*7453645fSAndriy Voskoboinyk {
173*7453645fSAndriy Voskoboinyk #define RTWN_CHK(res) do {	\
174*7453645fSAndriy Voskoboinyk 	if ((error = res) != 0)	\
175*7453645fSAndriy Voskoboinyk 		goto end;	\
176*7453645fSAndriy Voskoboinyk } while(0)
177*7453645fSAndriy Voskoboinyk 	uint8_t msk, off, reg;
178*7453645fSAndriy Voskoboinyk 	int error;
179*7453645fSAndriy Voskoboinyk 
180*7453645fSAndriy Voskoboinyk 	/* Read full ROM image. */
181*7453645fSAndriy Voskoboinyk 	sc->next_rom_addr = 0;
182*7453645fSAndriy Voskoboinyk 	memset(rom, 0xff, size);
183*7453645fSAndriy Voskoboinyk 
184*7453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_efuse_read_next(sc, &reg));
185*7453645fSAndriy Voskoboinyk 	while (reg != 0xff) {
186*7453645fSAndriy Voskoboinyk 		/* check for extended header */
187*7453645fSAndriy Voskoboinyk 		if ((sc->sc_flags & RTWN_FLAG_EXT_HDR) &&
188*7453645fSAndriy Voskoboinyk 		    (reg & 0x1f) == 0x0f) {
189*7453645fSAndriy Voskoboinyk 			off = reg >> 5;
190*7453645fSAndriy Voskoboinyk 			RTWN_CHK(rtwn_efuse_read_next(sc, &reg));
191*7453645fSAndriy Voskoboinyk 
192*7453645fSAndriy Voskoboinyk 			if ((reg & 0x0f) != 0x0f)
193*7453645fSAndriy Voskoboinyk 				off = ((reg & 0xf0) >> 1) | off;
194*7453645fSAndriy Voskoboinyk 			else
195*7453645fSAndriy Voskoboinyk 				continue;
196*7453645fSAndriy Voskoboinyk 		} else
197*7453645fSAndriy Voskoboinyk 			off = reg >> 4;
198*7453645fSAndriy Voskoboinyk 		msk = reg & 0xf;
199*7453645fSAndriy Voskoboinyk 
200*7453645fSAndriy Voskoboinyk 		RTWN_CHK(rtwn_efuse_read_data(sc, rom, off, msk));
201*7453645fSAndriy Voskoboinyk 		RTWN_CHK(rtwn_efuse_read_next(sc, &reg));
202*7453645fSAndriy Voskoboinyk 	}
203*7453645fSAndriy Voskoboinyk 
204*7453645fSAndriy Voskoboinyk end:
205*7453645fSAndriy Voskoboinyk 
206*7453645fSAndriy Voskoboinyk #ifdef RTWN_DEBUG
207*7453645fSAndriy Voskoboinyk 	if (sc->sc_debug & RTWN_DEBUG_ROM)
208*7453645fSAndriy Voskoboinyk 		rtwn_dump_rom_contents(sc, rom, size);
209*7453645fSAndriy Voskoboinyk #endif
210*7453645fSAndriy Voskoboinyk 
211*7453645fSAndriy Voskoboinyk 	/* Device-specific. */
212*7453645fSAndriy Voskoboinyk 	rtwn_efuse_postread(sc);
213*7453645fSAndriy Voskoboinyk 
214*7453645fSAndriy Voskoboinyk 	if (error != 0) {
215*7453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "%s: error while reading ROM\n",
216*7453645fSAndriy Voskoboinyk 		    __func__);
217*7453645fSAndriy Voskoboinyk 	}
218*7453645fSAndriy Voskoboinyk 
219*7453645fSAndriy Voskoboinyk 	return (error);
220*7453645fSAndriy Voskoboinyk #undef RTWN_CHK
221*7453645fSAndriy Voskoboinyk }
222*7453645fSAndriy Voskoboinyk 
223*7453645fSAndriy Voskoboinyk static int
rtwn_efuse_read_prepare(struct rtwn_softc * sc,uint8_t * rom,uint16_t size)224*7453645fSAndriy Voskoboinyk rtwn_efuse_read_prepare(struct rtwn_softc *sc, uint8_t *rom, uint16_t size)
225*7453645fSAndriy Voskoboinyk {
226*7453645fSAndriy Voskoboinyk 	int error;
227*7453645fSAndriy Voskoboinyk 
228*7453645fSAndriy Voskoboinyk 	error = rtwn_efuse_switch_power(sc);
229*7453645fSAndriy Voskoboinyk 	if (error != 0)
230*7453645fSAndriy Voskoboinyk 		goto fail;
231*7453645fSAndriy Voskoboinyk 
232*7453645fSAndriy Voskoboinyk 	error = rtwn_efuse_read(sc, rom, size);
233*7453645fSAndriy Voskoboinyk 
234*7453645fSAndriy Voskoboinyk fail:
235*7453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_OFF);
236*7453645fSAndriy Voskoboinyk 
237*7453645fSAndriy Voskoboinyk 	return (error);
238*7453645fSAndriy Voskoboinyk }
239*7453645fSAndriy Voskoboinyk 
240*7453645fSAndriy Voskoboinyk int
rtwn_read_rom(struct rtwn_softc * sc)241*7453645fSAndriy Voskoboinyk rtwn_read_rom(struct rtwn_softc *sc)
242*7453645fSAndriy Voskoboinyk {
243*7453645fSAndriy Voskoboinyk 	uint8_t *rom;
244*7453645fSAndriy Voskoboinyk 	int error;
245*7453645fSAndriy Voskoboinyk 
246*7453645fSAndriy Voskoboinyk 	rom = malloc(sc->efuse_maplen, M_TEMP, M_WAITOK);
247*7453645fSAndriy Voskoboinyk 
248*7453645fSAndriy Voskoboinyk 	/* Read full ROM image. */
249*7453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
250*7453645fSAndriy Voskoboinyk 	error = rtwn_efuse_read_prepare(sc, rom, sc->efuse_maplen);
251*7453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
252*7453645fSAndriy Voskoboinyk 	if (error != 0)
253*7453645fSAndriy Voskoboinyk 		goto fail;
254*7453645fSAndriy Voskoboinyk 
255*7453645fSAndriy Voskoboinyk 	/* Parse & save data in softc. */
256*7453645fSAndriy Voskoboinyk 	rtwn_parse_rom(sc, rom);
257*7453645fSAndriy Voskoboinyk 
258*7453645fSAndriy Voskoboinyk fail:
259*7453645fSAndriy Voskoboinyk 	free(rom, M_TEMP);
260*7453645fSAndriy Voskoboinyk 
261*7453645fSAndriy Voskoboinyk 	return (error);
262*7453645fSAndriy Voskoboinyk }
263