1bb5e3b2fSeh146360 /* 2*d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3bb5e3b2fSeh146360 * Use is subject to license terms. 4bb5e3b2fSeh146360 */ 5bb5e3b2fSeh146360 6bb5e3b2fSeh146360 /* 7bb5e3b2fSeh146360 * Copyright (c) 2004 8bb5e3b2fSeh146360 * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. 9bb5e3b2fSeh146360 * 10bb5e3b2fSeh146360 * Redistribution and use in source and binary forms, with or without 11bb5e3b2fSeh146360 * modification, are permitted provided that the following conditions 12bb5e3b2fSeh146360 * are met: 13bb5e3b2fSeh146360 * 1. Redistributions of source code must retain the above copyright 14bb5e3b2fSeh146360 * notice unmodified, this list of conditions, and the following 15bb5e3b2fSeh146360 * disclaimer. 16bb5e3b2fSeh146360 * 2. Redistributions in binary form must reproduce the above copyright 17bb5e3b2fSeh146360 * notice, this list of conditions and the following disclaimer in the 18bb5e3b2fSeh146360 * documentation and/or other materials provided with the distribution. 19bb5e3b2fSeh146360 * 20bb5e3b2fSeh146360 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21bb5e3b2fSeh146360 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22bb5e3b2fSeh146360 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23bb5e3b2fSeh146360 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24bb5e3b2fSeh146360 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25bb5e3b2fSeh146360 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26bb5e3b2fSeh146360 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27bb5e3b2fSeh146360 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28bb5e3b2fSeh146360 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29bb5e3b2fSeh146360 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30bb5e3b2fSeh146360 * SUCH DAMAGE. 31bb5e3b2fSeh146360 */ 32bb5e3b2fSeh146360 33bb5e3b2fSeh146360 /* 34bb5e3b2fSeh146360 * Intel Wireless PRO/2100 mini-PCI adapter driver 35bb5e3b2fSeh146360 * ipw2100_hw.c is used to handle hardware operation and firmware operations. 36bb5e3b2fSeh146360 */ 37bb5e3b2fSeh146360 #include <sys/types.h> 38bb5e3b2fSeh146360 #include <sys/byteorder.h> 39bb5e3b2fSeh146360 #include <sys/ddi.h> 40bb5e3b2fSeh146360 #include <sys/sunddi.h> 41bb5e3b2fSeh146360 #include <sys/note.h> 42bb5e3b2fSeh146360 #include <sys/stream.h> 43bb5e3b2fSeh146360 #include <sys/strsun.h> 44bb5e3b2fSeh146360 45bb5e3b2fSeh146360 #include "ipw2100.h" 46bb5e3b2fSeh146360 #include "ipw2100_impl.h" 47bb5e3b2fSeh146360 48bb5e3b2fSeh146360 /* 49bb5e3b2fSeh146360 * Hardware related operations 50bb5e3b2fSeh146360 */ 51bb5e3b2fSeh146360 #define IPW2100_EEPROM_SHIFT_D (2) 52bb5e3b2fSeh146360 #define IPW2100_EEPROM_SHIFT_Q (4) 53bb5e3b2fSeh146360 54bb5e3b2fSeh146360 #define IPW2100_EEPROM_C (1 << 0) 55bb5e3b2fSeh146360 #define IPW2100_EEPROM_S (1 << 1) 56bb5e3b2fSeh146360 #define IPW2100_EEPROM_D (1 << IPW2100_EEPROM_SHIFT_D) 57bb5e3b2fSeh146360 #define IPW2100_EEPROM_Q (1 << IPW2100_EEPROM_SHIFT_Q) 58bb5e3b2fSeh146360 59bb5e3b2fSeh146360 uint8_t 60bb5e3b2fSeh146360 ipw2100_csr_get8(struct ipw2100_softc *sc, uint32_t off) 61bb5e3b2fSeh146360 { 62bb5e3b2fSeh146360 return (ddi_get8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off))); 63bb5e3b2fSeh146360 } 64bb5e3b2fSeh146360 65bb5e3b2fSeh146360 uint16_t 66bb5e3b2fSeh146360 ipw2100_csr_get16(struct ipw2100_softc *sc, uint32_t off) 67bb5e3b2fSeh146360 { 68922d2c76Seh146360 return (ddi_get16(sc->sc_ioh, 69922d2c76Seh146360 (uint16_t *)((uintptr_t)sc->sc_regs + off))); 70bb5e3b2fSeh146360 } 71bb5e3b2fSeh146360 72bb5e3b2fSeh146360 uint32_t 73bb5e3b2fSeh146360 ipw2100_csr_get32(struct ipw2100_softc *sc, uint32_t off) 74bb5e3b2fSeh146360 { 75922d2c76Seh146360 return (ddi_get32(sc->sc_ioh, 76922d2c76Seh146360 (uint32_t *)((uintptr_t)sc->sc_regs + off))); 77bb5e3b2fSeh146360 } 78bb5e3b2fSeh146360 79bb5e3b2fSeh146360 void 80bb5e3b2fSeh146360 ipw2100_csr_rep_get16(struct ipw2100_softc *sc, 81bb5e3b2fSeh146360 uint32_t off, uint16_t *buf, size_t cnt) 82bb5e3b2fSeh146360 { 83922d2c76Seh146360 ddi_rep_get16(sc->sc_ioh, buf, 84922d2c76Seh146360 (uint16_t *)((uintptr_t)sc->sc_regs + off), 85bb5e3b2fSeh146360 cnt, DDI_DEV_NO_AUTOINCR); 86bb5e3b2fSeh146360 } 87bb5e3b2fSeh146360 88bb5e3b2fSeh146360 void 89bb5e3b2fSeh146360 ipw2100_csr_put8(struct ipw2100_softc *sc, uint32_t off, uint8_t val) 90bb5e3b2fSeh146360 { 91bb5e3b2fSeh146360 ddi_put8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off), val); 92bb5e3b2fSeh146360 } 93bb5e3b2fSeh146360 94bb5e3b2fSeh146360 void 95bb5e3b2fSeh146360 ipw2100_csr_put16(struct ipw2100_softc *sc, uint32_t off, uint16_t val) 96bb5e3b2fSeh146360 { 97922d2c76Seh146360 ddi_put16(sc->sc_ioh, 98922d2c76Seh146360 (uint16_t *)((uintptr_t)sc->sc_regs + off), val); 99bb5e3b2fSeh146360 } 100bb5e3b2fSeh146360 101bb5e3b2fSeh146360 void 102bb5e3b2fSeh146360 ipw2100_csr_put32(struct ipw2100_softc *sc, uint32_t off, uint32_t val) 103bb5e3b2fSeh146360 { 104922d2c76Seh146360 ddi_put32(sc->sc_ioh, 105922d2c76Seh146360 (uint32_t *)((uintptr_t)sc->sc_regs + off), val); 106bb5e3b2fSeh146360 } 107bb5e3b2fSeh146360 108bb5e3b2fSeh146360 void 109bb5e3b2fSeh146360 ipw2100_csr_rep_put8(struct ipw2100_softc *sc, 110bb5e3b2fSeh146360 uint32_t off, uint8_t *buf, size_t cnt) 111bb5e3b2fSeh146360 { 112bb5e3b2fSeh146360 ddi_rep_put8(sc->sc_ioh, buf, (uint8_t *)(sc->sc_regs + off), 113bb5e3b2fSeh146360 cnt, DDI_DEV_NO_AUTOINCR); 114bb5e3b2fSeh146360 } 115bb5e3b2fSeh146360 116bb5e3b2fSeh146360 uint8_t 117bb5e3b2fSeh146360 ipw2100_imem_get8(struct ipw2100_softc *sc, int32_t addr) 118bb5e3b2fSeh146360 { 119bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr); 120bb5e3b2fSeh146360 121bb5e3b2fSeh146360 return (ipw2100_csr_get8(sc, IPW2100_CSR_INDIRECT_DATA)); 122bb5e3b2fSeh146360 } 123bb5e3b2fSeh146360 124bb5e3b2fSeh146360 uint16_t 125bb5e3b2fSeh146360 ipw2100_imem_get16(struct ipw2100_softc *sc, uint32_t addr) 126bb5e3b2fSeh146360 { 127bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr); 128bb5e3b2fSeh146360 129bb5e3b2fSeh146360 return (ipw2100_csr_get16(sc, IPW2100_CSR_INDIRECT_DATA)); 130bb5e3b2fSeh146360 } 131bb5e3b2fSeh146360 132bb5e3b2fSeh146360 uint32_t 133bb5e3b2fSeh146360 ipw2100_imem_get32(struct ipw2100_softc *sc, uint32_t addr) 134bb5e3b2fSeh146360 { 135bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr); 136bb5e3b2fSeh146360 137bb5e3b2fSeh146360 return (ipw2100_csr_get32(sc, IPW2100_CSR_INDIRECT_DATA)); 138bb5e3b2fSeh146360 } 139bb5e3b2fSeh146360 140bb5e3b2fSeh146360 void 141bb5e3b2fSeh146360 ipw2100_imem_rep_get16(struct ipw2100_softc *sc, 142bb5e3b2fSeh146360 uint32_t addr, uint16_t *buf, size_t cnt) 143bb5e3b2fSeh146360 { 144bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr); 145bb5e3b2fSeh146360 ipw2100_csr_rep_get16(sc, IPW2100_CSR_INDIRECT_DATA, buf, cnt); 146bb5e3b2fSeh146360 } 147bb5e3b2fSeh146360 148bb5e3b2fSeh146360 void 149bb5e3b2fSeh146360 ipw2100_imem_put8(struct ipw2100_softc *sc, uint32_t addr, uint8_t val) 150bb5e3b2fSeh146360 { 151bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr); 152bb5e3b2fSeh146360 ipw2100_csr_put8(sc, IPW2100_CSR_INDIRECT_DATA, val); 153bb5e3b2fSeh146360 } 154bb5e3b2fSeh146360 155bb5e3b2fSeh146360 void 156bb5e3b2fSeh146360 ipw2100_imem_put16(struct ipw2100_softc *sc, uint32_t addr, uint16_t val) 157bb5e3b2fSeh146360 { 158bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr); 159bb5e3b2fSeh146360 ipw2100_csr_put16(sc, IPW2100_CSR_INDIRECT_DATA, val); 160bb5e3b2fSeh146360 } 161bb5e3b2fSeh146360 162bb5e3b2fSeh146360 void 163bb5e3b2fSeh146360 ipw2100_imem_put32(struct ipw2100_softc *sc, uint32_t addr, uint32_t val) 164bb5e3b2fSeh146360 { 165bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr); 166bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_DATA, val); 167bb5e3b2fSeh146360 } 168bb5e3b2fSeh146360 169bb5e3b2fSeh146360 void 170bb5e3b2fSeh146360 ipw2100_imem_rep_put8(struct ipw2100_softc *sc, 171bb5e3b2fSeh146360 uint32_t addr, uint8_t *buf, size_t cnt) 172bb5e3b2fSeh146360 { 173bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr); 174bb5e3b2fSeh146360 ipw2100_csr_rep_put8(sc, IPW2100_CSR_INDIRECT_DATA, buf, cnt); 175bb5e3b2fSeh146360 } 176bb5e3b2fSeh146360 177bb5e3b2fSeh146360 void 178bb5e3b2fSeh146360 ipw2100_imem_getbuf(struct ipw2100_softc *sc, 179bb5e3b2fSeh146360 uint32_t addr, uint8_t *buf, size_t cnt) 180bb5e3b2fSeh146360 { 181bb5e3b2fSeh146360 for (; cnt > 0; addr++, buf++, cnt--) { 182bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr & ~3); 183bb5e3b2fSeh146360 *buf = ipw2100_csr_get8(sc, 184bb5e3b2fSeh146360 IPW2100_CSR_INDIRECT_DATA +(addr & 3)); 185bb5e3b2fSeh146360 } 186bb5e3b2fSeh146360 } 187bb5e3b2fSeh146360 188bb5e3b2fSeh146360 void 189bb5e3b2fSeh146360 ipw2100_imem_putbuf(struct ipw2100_softc *sc, 190bb5e3b2fSeh146360 uint32_t addr, uint8_t *buf, size_t cnt) 191bb5e3b2fSeh146360 { 192bb5e3b2fSeh146360 for (; cnt > 0; addr++, buf++, cnt--) { 193bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr & ~3); 194bb5e3b2fSeh146360 ipw2100_csr_put8(sc, 195bb5e3b2fSeh146360 IPW2100_CSR_INDIRECT_DATA +(addr & 3), *buf); 196bb5e3b2fSeh146360 } 197bb5e3b2fSeh146360 } 198bb5e3b2fSeh146360 199bb5e3b2fSeh146360 void 200bb5e3b2fSeh146360 ipw2100_rom_control(struct ipw2100_softc *sc, uint32_t val) 201bb5e3b2fSeh146360 { 202bb5e3b2fSeh146360 ipw2100_imem_put32(sc, IPW2100_IMEM_EEPROM_CTL, val); 203bb5e3b2fSeh146360 drv_usecwait(IPW2100_EEPROM_DELAY); 204bb5e3b2fSeh146360 } 205bb5e3b2fSeh146360 206bb5e3b2fSeh146360 207bb5e3b2fSeh146360 uint8_t 208bb5e3b2fSeh146360 ipw2100_table1_get8(struct ipw2100_softc *sc, uint32_t off) 209bb5e3b2fSeh146360 { 210bb5e3b2fSeh146360 uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off); 211bb5e3b2fSeh146360 return (ipw2100_imem_get8(sc, addr)); 212bb5e3b2fSeh146360 } 213bb5e3b2fSeh146360 214bb5e3b2fSeh146360 uint32_t 215bb5e3b2fSeh146360 ipw2100_table1_get32(struct ipw2100_softc *sc, uint32_t off) 216bb5e3b2fSeh146360 { 217bb5e3b2fSeh146360 uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off); 218bb5e3b2fSeh146360 return (ipw2100_imem_get32(sc, addr)); 219bb5e3b2fSeh146360 } 220bb5e3b2fSeh146360 221bb5e3b2fSeh146360 void 222bb5e3b2fSeh146360 ipw2100_table1_put32(struct ipw2100_softc *sc, uint32_t off, uint32_t val) 223bb5e3b2fSeh146360 { 224bb5e3b2fSeh146360 uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off); 225bb5e3b2fSeh146360 ipw2100_imem_put32(sc, addr, val); 226bb5e3b2fSeh146360 } 227bb5e3b2fSeh146360 228bb5e3b2fSeh146360 int 229bb5e3b2fSeh146360 ipw2100_table2_getbuf(struct ipw2100_softc *sc, 230bb5e3b2fSeh146360 uint32_t off, uint8_t *buf, uint32_t *len) 231bb5e3b2fSeh146360 { 232bb5e3b2fSeh146360 uint32_t addr, info; 233bb5e3b2fSeh146360 uint16_t cnt, size; 234bb5e3b2fSeh146360 uint32_t total; 235bb5e3b2fSeh146360 236bb5e3b2fSeh146360 addr = ipw2100_imem_get32(sc, sc->sc_table2_base + off); 237bb5e3b2fSeh146360 info = ipw2100_imem_get32(sc, 238bb5e3b2fSeh146360 sc->sc_table2_base + off + sizeof (uint32_t)); 239bb5e3b2fSeh146360 240bb5e3b2fSeh146360 cnt = info >> 16; 241bb5e3b2fSeh146360 size = info & 0xffff; 242bb5e3b2fSeh146360 total = cnt * size; 243bb5e3b2fSeh146360 244bb5e3b2fSeh146360 if (total > *len) { 245bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 246bb5e3b2fSeh146360 "ipw2100_table2_getbuf(): invalid table offset = 0x%08x\n", 247bb5e3b2fSeh146360 off)); 248bb5e3b2fSeh146360 return (DDI_FAILURE); 249bb5e3b2fSeh146360 } 250bb5e3b2fSeh146360 251bb5e3b2fSeh146360 *len = total; 252bb5e3b2fSeh146360 ipw2100_imem_getbuf(sc, addr, buf, total); 253bb5e3b2fSeh146360 254bb5e3b2fSeh146360 return (DDI_SUCCESS); 255bb5e3b2fSeh146360 } 256bb5e3b2fSeh146360 257bb5e3b2fSeh146360 uint16_t 258bb5e3b2fSeh146360 ipw2100_rom_get16(struct ipw2100_softc *sc, uint8_t addr) 259bb5e3b2fSeh146360 { 260bb5e3b2fSeh146360 uint32_t tmp; 261bb5e3b2fSeh146360 uint16_t val; 262bb5e3b2fSeh146360 int n; 263bb5e3b2fSeh146360 264bb5e3b2fSeh146360 /* 265bb5e3b2fSeh146360 * According to i2c bus protocol to set them. 266bb5e3b2fSeh146360 */ 267bb5e3b2fSeh146360 /* clock */ 268bb5e3b2fSeh146360 ipw2100_rom_control(sc, 0); 269bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S); 270bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_C); 271bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S); 272bb5e3b2fSeh146360 /* start bit */ 273bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_D); 274bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S 275bb5e3b2fSeh146360 | IPW2100_EEPROM_D | IPW2100_EEPROM_C); 276bb5e3b2fSeh146360 /* read opcode */ 277bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_D); 278bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S 279bb5e3b2fSeh146360 | IPW2100_EEPROM_D | IPW2100_EEPROM_C); 280bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S); 281bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_C); 282bb5e3b2fSeh146360 /* 283bb5e3b2fSeh146360 * address, totally 8 bits, defined by hardware, push from MSB to LSB 284bb5e3b2fSeh146360 */ 285bb5e3b2fSeh146360 for (n = 7; n >= 0; n--) { 286bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S 287bb5e3b2fSeh146360 |(((addr >> n) & 1) << IPW2100_EEPROM_SHIFT_D)); 288bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S 289bb5e3b2fSeh146360 |(((addr >> n) & 1) << IPW2100_EEPROM_SHIFT_D) 290bb5e3b2fSeh146360 | IPW2100_EEPROM_C); 291bb5e3b2fSeh146360 } 292bb5e3b2fSeh146360 293bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S); 294bb5e3b2fSeh146360 295bb5e3b2fSeh146360 /* 296bb5e3b2fSeh146360 * data, totally 16 bits, defined by hardware, push from MSB to LSB 297bb5e3b2fSeh146360 */ 298bb5e3b2fSeh146360 val = 0; 299bb5e3b2fSeh146360 for (n = 15; n >= 0; n--) { 300bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_C); 301bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S); 302bb5e3b2fSeh146360 tmp = ipw2100_imem_get32(sc, IPW2100_IMEM_EEPROM_CTL); 303bb5e3b2fSeh146360 val |= ((tmp & IPW2100_EEPROM_Q) 304bb5e3b2fSeh146360 >> IPW2100_EEPROM_SHIFT_Q) << n; 305bb5e3b2fSeh146360 } 306bb5e3b2fSeh146360 307bb5e3b2fSeh146360 ipw2100_rom_control(sc, 0); 308bb5e3b2fSeh146360 309bb5e3b2fSeh146360 /* clear chip select and clock */ 310bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_S); 311bb5e3b2fSeh146360 ipw2100_rom_control(sc, 0); 312bb5e3b2fSeh146360 ipw2100_rom_control(sc, IPW2100_EEPROM_C); 313bb5e3b2fSeh146360 314bb5e3b2fSeh146360 return (LE_16(val)); 315bb5e3b2fSeh146360 } 316bb5e3b2fSeh146360 317bb5e3b2fSeh146360 318bb5e3b2fSeh146360 /* 319bb5e3b2fSeh146360 * Firmware related operations 320bb5e3b2fSeh146360 */ 321bb5e3b2fSeh146360 #define IPW2100_FW_MAJOR_VERSION (1) 322bb5e3b2fSeh146360 #define IPW2100_FW_MINOR_VERSION (3) 323bb5e3b2fSeh146360 324bb5e3b2fSeh146360 #define IPW2100_FW_MAJOR(x)((x) & 0xff) 325bb5e3b2fSeh146360 #define IPW2100_FW_MINOR(x)(((x) & 0xff) >> 8) 326bb5e3b2fSeh146360 327bb5e3b2fSeh146360 /* 328bb5e3b2fSeh146360 * The firware was issued by Intel as binary which need to be loaded 329bb5e3b2fSeh146360 * to hardware when card is initiated, or when fatal error happened, 330bb5e3b2fSeh146360 * or when the chip need be reset. 331bb5e3b2fSeh146360 */ 332bb5e3b2fSeh146360 static uint8_t ipw2100_firmware_bin [] = { 333bb5e3b2fSeh146360 #include "fw-ipw2100/ipw2100-1.3.fw.hex" 334bb5e3b2fSeh146360 }; 335bb5e3b2fSeh146360 336bb5e3b2fSeh146360 int 337bb5e3b2fSeh146360 ipw2100_cache_firmware(struct ipw2100_softc *sc) 338bb5e3b2fSeh146360 { 339bb5e3b2fSeh146360 uint8_t *bin = ipw2100_firmware_bin; 340bb5e3b2fSeh146360 struct ipw2100_firmware_hdr *h = (struct ipw2100_firmware_hdr *)bin; 341bb5e3b2fSeh146360 342bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT, 343bb5e3b2fSeh146360 "ipw2100_cache_firmwares(): enter\n")); 344bb5e3b2fSeh146360 345bb5e3b2fSeh146360 sc->sc_fw.bin_base = bin; 346bb5e3b2fSeh146360 sc->sc_fw.bin_size = sizeof (ipw2100_firmware_bin); 347bb5e3b2fSeh146360 348bb5e3b2fSeh146360 if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) { 349bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 350bb5e3b2fSeh146360 "ipw2100_cache_firmware(): image not compatible, %u\n", 351bb5e3b2fSeh146360 h->version)); 352bb5e3b2fSeh146360 return (DDI_FAILURE); 353bb5e3b2fSeh146360 } 354bb5e3b2fSeh146360 355bb5e3b2fSeh146360 sc->sc_fw.fw_base = bin + sizeof (struct ipw2100_firmware_hdr); 356bb5e3b2fSeh146360 sc->sc_fw.fw_size = LE_32(h->fw_size); 357bb5e3b2fSeh146360 sc->sc_fw.uc_base = sc->sc_fw.fw_base + sc->sc_fw.fw_size; 358bb5e3b2fSeh146360 sc->sc_fw.uc_size = LE_32(h->uc_size); 359bb5e3b2fSeh146360 360bb5e3b2fSeh146360 sc->sc_flags |= IPW2100_FLAG_FW_CACHED; 361bb5e3b2fSeh146360 362bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT, 363bb5e3b2fSeh146360 "ipw2100_cache_firmware(): exit\n")); 364bb5e3b2fSeh146360 365bb5e3b2fSeh146360 return (DDI_SUCCESS); 366bb5e3b2fSeh146360 } 367bb5e3b2fSeh146360 368bb5e3b2fSeh146360 /* 369bb5e3b2fSeh146360 * If user-land firmware loading is supported, this routine 370bb5e3b2fSeh146360 * free kmemory if sc->sc_fw.bin_base & sc->sc_fw.bin_size are 371bb5e3b2fSeh146360 * not empty. 372bb5e3b2fSeh146360 */ 373bb5e3b2fSeh146360 int 374bb5e3b2fSeh146360 ipw2100_free_firmware(struct ipw2100_softc *sc) 375bb5e3b2fSeh146360 { 376bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2100_FLAG_FW_CACHED; 377bb5e3b2fSeh146360 378bb5e3b2fSeh146360 return (DDI_SUCCESS); 379bb5e3b2fSeh146360 } 380bb5e3b2fSeh146360 381bb5e3b2fSeh146360 /* 382bb5e3b2fSeh146360 * the following routines load code onto ipw2100 hardware 383bb5e3b2fSeh146360 */ 384bb5e3b2fSeh146360 int 385bb5e3b2fSeh146360 ipw2100_load_uc(struct ipw2100_softc *sc) 386bb5e3b2fSeh146360 { 387bb5e3b2fSeh146360 int ntries; 388bb5e3b2fSeh146360 389bb5e3b2fSeh146360 ipw2100_imem_put32(sc, 0x3000e0, 0x80000000); 390bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, 0); 391bb5e3b2fSeh146360 392bb5e3b2fSeh146360 ipw2100_imem_put16(sc, 0x220000, 0x0703); 393bb5e3b2fSeh146360 ipw2100_imem_put16(sc, 0x220000, 0x0707); 394bb5e3b2fSeh146360 395bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210014, 0x72); 396bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210014, 0x72); 397bb5e3b2fSeh146360 398bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210000, 0x40); 399bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210000, 0x00); 400bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210000, 0x40); 401bb5e3b2fSeh146360 402bb5e3b2fSeh146360 ipw2100_imem_rep_put8(sc, 0x210010, 403bb5e3b2fSeh146360 sc->sc_fw.uc_base, sc->sc_fw.uc_size); 404bb5e3b2fSeh146360 405bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210000, 0x00); 406bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210000, 0x00); 407bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210000, 0x80); 408bb5e3b2fSeh146360 409bb5e3b2fSeh146360 ipw2100_imem_put16(sc, 0x220000, 0x0703); 410bb5e3b2fSeh146360 ipw2100_imem_put16(sc, 0x220000, 0x0707); 411bb5e3b2fSeh146360 412bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210014, 0x72); 413bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210014, 0x72); 414bb5e3b2fSeh146360 415bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210000, 0x00); 416bb5e3b2fSeh146360 ipw2100_imem_put8(sc, 0x210000, 0x80); 417bb5e3b2fSeh146360 418bb5e3b2fSeh146360 /* try many times */ 419bb5e3b2fSeh146360 for (ntries = 0; ntries < 5000; ntries++) { 420bb5e3b2fSeh146360 if (ipw2100_imem_get8(sc, 0x210000) & 1) 421bb5e3b2fSeh146360 break; 422bb5e3b2fSeh146360 drv_usecwait(1000); /* wait for a while */ 423bb5e3b2fSeh146360 } 424bb5e3b2fSeh146360 if (ntries == 5000) 425bb5e3b2fSeh146360 return (DDI_FAILURE); 426bb5e3b2fSeh146360 427bb5e3b2fSeh146360 ipw2100_imem_put32(sc, 0x3000e0, 0); 428bb5e3b2fSeh146360 429bb5e3b2fSeh146360 return (DDI_SUCCESS); 430bb5e3b2fSeh146360 } 431bb5e3b2fSeh146360 432bb5e3b2fSeh146360 int 433bb5e3b2fSeh146360 ipw2100_load_fw(struct ipw2100_softc *sc) 434bb5e3b2fSeh146360 { 435bb5e3b2fSeh146360 uint8_t *p, *e; 436bb5e3b2fSeh146360 uint32_t dst; 437bb5e3b2fSeh146360 uint16_t len; 438bb5e3b2fSeh146360 clock_t clk; 439bb5e3b2fSeh146360 440bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT, 441bb5e3b2fSeh146360 "ipw2100_load_fw(): enter\n")); 442bb5e3b2fSeh146360 443bb5e3b2fSeh146360 p = sc->sc_fw.fw_base; 444bb5e3b2fSeh146360 e = sc->sc_fw.fw_base + sc->sc_fw.fw_size; 445bb5e3b2fSeh146360 while (p < e) { 446bb5e3b2fSeh146360 /* 447bb5e3b2fSeh146360 * each block is organized as <DST,LEN,DATA> 448bb5e3b2fSeh146360 */ 449bb5e3b2fSeh146360 if ((p + sizeof (dst) + sizeof (len)) > e) { 450bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_CONT, 451bb5e3b2fSeh146360 "ipw2100_load_fw(): invalid firmware image\n")); 452bb5e3b2fSeh146360 return (DDI_FAILURE); 453bb5e3b2fSeh146360 } 454922d2c76Seh146360 dst = LE_32(*((uint32_t *)(uintptr_t)p)); p += sizeof (dst); 455922d2c76Seh146360 len = LE_16(*((uint16_t *)(uintptr_t)p)); p += sizeof (len); 456bb5e3b2fSeh146360 if ((p + len) > e) { 457bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_CONT, 458bb5e3b2fSeh146360 "ipw2100_load_fw(): invalid firmware image\n")); 459bb5e3b2fSeh146360 return (DDI_FAILURE); 460bb5e3b2fSeh146360 } 461bb5e3b2fSeh146360 462bb5e3b2fSeh146360 ipw2100_imem_putbuf(sc, dst, p, len); 463bb5e3b2fSeh146360 p += len; 464bb5e3b2fSeh146360 } 465bb5e3b2fSeh146360 466bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_IO, 467bb5e3b2fSeh146360 IPW2100_IO_GPIO1_ENABLE | IPW2100_IO_GPIO3_MASK | 468bb5e3b2fSeh146360 IPW2100_IO_LED_OFF); 469bb5e3b2fSeh146360 470bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 471bb5e3b2fSeh146360 472bb5e3b2fSeh146360 /* 473bb5e3b2fSeh146360 * enable all interrupts 474bb5e3b2fSeh146360 */ 475bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL); 476bb5e3b2fSeh146360 477bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, 0); 478bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_CTL, 479bb5e3b2fSeh146360 ipw2100_csr_get32(sc, IPW2100_CSR_CTL) | IPW2100_CTL_ALLOW_STANDBY); 480bb5e3b2fSeh146360 481bb5e3b2fSeh146360 /* 482bb5e3b2fSeh146360 * wait for interrupt to notify fw initialization is done 483bb5e3b2fSeh146360 */ 484*d3d50737SRafael Vanoni clk = drv_usectohz(5000000); /* 5 second */ 485bb5e3b2fSeh146360 while (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) { 486bb5e3b2fSeh146360 /* 487bb5e3b2fSeh146360 * wait longer for the fw initialized 488bb5e3b2fSeh146360 */ 489*d3d50737SRafael Vanoni if (cv_reltimedwait(&sc->sc_fw_cond, &sc->sc_ilock, clk, 490*d3d50737SRafael Vanoni TR_CLOCK_TICK) < 0) 491bb5e3b2fSeh146360 break; 492bb5e3b2fSeh146360 } 493bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 494bb5e3b2fSeh146360 495bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_IO, 496bb5e3b2fSeh146360 ipw2100_csr_get32(sc, IPW2100_CSR_IO) | 497bb5e3b2fSeh146360 IPW2100_IO_GPIO1_MASK | IPW2100_IO_GPIO3_MASK); 498bb5e3b2fSeh146360 499bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) { 500bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT, 501bb5e3b2fSeh146360 "ipw2100_load_fw(): exit, init failed\n")); 502bb5e3b2fSeh146360 return (DDI_FAILURE); 503bb5e3b2fSeh146360 } 504bb5e3b2fSeh146360 505bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT, 506bb5e3b2fSeh146360 "ipw2100_load_fw(): exit\n")); 507bb5e3b2fSeh146360 return (DDI_SUCCESS); 508bb5e3b2fSeh146360 } 509