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
55static int
56rtwn_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
84int
85rtwn_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
120static int
121rtwn_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
154static void
155rtwn_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
173static int
174rtwn_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
207end:
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
226static int
227rtwn_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
237fail:
238	rtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_OFF);
239
240	return (error);
241}
242
243int
244rtwn_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
261fail:
262	free(rom, M_TEMP);
263
264	return (error);
265}
266