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