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, 2005 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/2200 mini-PCI adapter driver 35bb5e3b2fSeh146360 * ipw2200_hw.c is used t handle hardware operations 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 "ipw2200.h" 46bb5e3b2fSeh146360 #include "ipw2200_impl.h" 47bb5e3b2fSeh146360 48bb5e3b2fSeh146360 /* 49bb5e3b2fSeh146360 * Hardware related operations 50bb5e3b2fSeh146360 */ 51bb5e3b2fSeh146360 #define IPW2200_EEPROM_SHIFT_D (2) 52bb5e3b2fSeh146360 #define IPW2200_EEPROM_SHIFT_Q (4) 53bb5e3b2fSeh146360 54bb5e3b2fSeh146360 #define IPW2200_EEPROM_C (1 << 0) 55bb5e3b2fSeh146360 #define IPW2200_EEPROM_S (1 << 1) 56bb5e3b2fSeh146360 #define IPW2200_EEPROM_D (1 << IPW2200_EEPROM_SHIFT_D) 57bb5e3b2fSeh146360 #define IPW2200_EEPROM_Q (1 << IPW2200_EEPROM_SHIFT_Q) 58bb5e3b2fSeh146360 59bb5e3b2fSeh146360 uint8_t 60bb5e3b2fSeh146360 ipw2200_csr_get8(struct ipw2200_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 ipw2200_csr_get16(struct ipw2200_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 ipw2200_csr_get32(struct ipw2200_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 ipw2200_csr_getbuf32(struct ipw2200_softc *sc, uint32_t off, 81bb5e3b2fSeh146360 uint32_t *buf, size_t cnt) 82bb5e3b2fSeh146360 { 83922d2c76Seh146360 ddi_rep_get32(sc->sc_ioh, buf, 84922d2c76Seh146360 (uint32_t *)((uintptr_t)sc->sc_regs + off), 85bb5e3b2fSeh146360 cnt, DDI_DEV_AUTOINCR); 86bb5e3b2fSeh146360 } 87bb5e3b2fSeh146360 88bb5e3b2fSeh146360 void 89bb5e3b2fSeh146360 ipw2200_csr_put8(struct ipw2200_softc *sc, uint32_t off, 90bb5e3b2fSeh146360 uint8_t val) 91bb5e3b2fSeh146360 { 92bb5e3b2fSeh146360 ddi_put8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off), val); 93bb5e3b2fSeh146360 } 94bb5e3b2fSeh146360 95bb5e3b2fSeh146360 void 96bb5e3b2fSeh146360 ipw2200_csr_put16(struct ipw2200_softc *sc, uint32_t off, 97bb5e3b2fSeh146360 uint16_t val) 98bb5e3b2fSeh146360 { 99922d2c76Seh146360 ddi_put16(sc->sc_ioh, 100922d2c76Seh146360 (uint16_t *)((uintptr_t)sc->sc_regs + off), val); 101bb5e3b2fSeh146360 } 102bb5e3b2fSeh146360 103bb5e3b2fSeh146360 void 104bb5e3b2fSeh146360 ipw2200_csr_put32(struct ipw2200_softc *sc, uint32_t off, 105bb5e3b2fSeh146360 uint32_t val) 106bb5e3b2fSeh146360 { 107922d2c76Seh146360 ddi_put32(sc->sc_ioh, 108922d2c76Seh146360 (uint32_t *)((uintptr_t)sc->sc_regs + off), val); 109bb5e3b2fSeh146360 } 110bb5e3b2fSeh146360 111bb5e3b2fSeh146360 uint8_t 112bb5e3b2fSeh146360 ipw2200_imem_get8(struct ipw2200_softc *sc, uint32_t addr) 113bb5e3b2fSeh146360 { 114bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr); 115bb5e3b2fSeh146360 return (ipw2200_csr_get8(sc, IPW2200_CSR_INDIRECT_DATA)); 116bb5e3b2fSeh146360 } 117bb5e3b2fSeh146360 118bb5e3b2fSeh146360 uint16_t 119bb5e3b2fSeh146360 ipw2200_imem_get16(struct ipw2200_softc *sc, 120bb5e3b2fSeh146360 uint32_t addr) 121bb5e3b2fSeh146360 { 122bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr); 123bb5e3b2fSeh146360 return (ipw2200_csr_get16(sc, IPW2200_CSR_INDIRECT_DATA)); 124bb5e3b2fSeh146360 } 125bb5e3b2fSeh146360 126bb5e3b2fSeh146360 uint32_t 127bb5e3b2fSeh146360 ipw2200_imem_get32(struct ipw2200_softc *sc, uint32_t addr) 128bb5e3b2fSeh146360 { 129bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr); 130bb5e3b2fSeh146360 return (ipw2200_csr_get32(sc, IPW2200_CSR_INDIRECT_DATA)); 131bb5e3b2fSeh146360 } 132bb5e3b2fSeh146360 133bb5e3b2fSeh146360 void 134bb5e3b2fSeh146360 ipw2200_imem_put8(struct ipw2200_softc *sc, uint32_t addr, uint8_t val) 135bb5e3b2fSeh146360 { 136bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr); 137bb5e3b2fSeh146360 ipw2200_csr_put8(sc, IPW2200_CSR_INDIRECT_DATA, val); 138bb5e3b2fSeh146360 } 139bb5e3b2fSeh146360 140bb5e3b2fSeh146360 void 141bb5e3b2fSeh146360 ipw2200_imem_put16(struct ipw2200_softc *sc, uint32_t addr, 142bb5e3b2fSeh146360 uint16_t val) 143bb5e3b2fSeh146360 { 144bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr); 145bb5e3b2fSeh146360 ipw2200_csr_put16(sc, IPW2200_CSR_INDIRECT_DATA, val); 146bb5e3b2fSeh146360 } 147bb5e3b2fSeh146360 148bb5e3b2fSeh146360 void 149bb5e3b2fSeh146360 ipw2200_imem_put32(struct ipw2200_softc *sc, uint32_t addr, 150bb5e3b2fSeh146360 uint32_t val) 151bb5e3b2fSeh146360 { 152bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr); 153bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_DATA, val); 154bb5e3b2fSeh146360 } 155bb5e3b2fSeh146360 156bb5e3b2fSeh146360 void 157bb5e3b2fSeh146360 ipw2200_rom_control(struct ipw2200_softc *sc, uint32_t val) 158bb5e3b2fSeh146360 { 159bb5e3b2fSeh146360 ipw2200_imem_put32(sc, IPW2200_IMEM_EEPROM_CTL, val); 160bb5e3b2fSeh146360 drv_usecwait(IPW2200_EEPROM_DELAY); 161bb5e3b2fSeh146360 } 162bb5e3b2fSeh146360 163bb5e3b2fSeh146360 uint16_t 164bb5e3b2fSeh146360 ipw2200_rom_get16(struct ipw2200_softc *sc, uint8_t addr) 165bb5e3b2fSeh146360 { 166bb5e3b2fSeh146360 uint32_t tmp; 167bb5e3b2fSeh146360 uint16_t val; 168bb5e3b2fSeh146360 int n; 169bb5e3b2fSeh146360 170bb5e3b2fSeh146360 /* 171bb5e3b2fSeh146360 * According to i2c bus protocol 172bb5e3b2fSeh146360 */ 173bb5e3b2fSeh146360 /* clock */ 174bb5e3b2fSeh146360 ipw2200_rom_control(sc, 0); 175bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S); 176bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_C); 177bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S); 178bb5e3b2fSeh146360 /* start bit */ 179bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_D); 180bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_D | 181bb5e3b2fSeh146360 IPW2200_EEPROM_C); 182bb5e3b2fSeh146360 /* read opcode */ 183bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_D); 184bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_D | 185bb5e3b2fSeh146360 IPW2200_EEPROM_C); 186bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S); 187bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_C); 188bb5e3b2fSeh146360 /* 189bb5e3b2fSeh146360 * address, totally 8 bits, defined by hardware, push from MSB to LSB 190bb5e3b2fSeh146360 */ 191bb5e3b2fSeh146360 for (n = 7; n >= 0; n--) { 192bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S | 193bb5e3b2fSeh146360 (((addr >> n) & 1) << IPW2200_EEPROM_SHIFT_D)); 194bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S | 195bb5e3b2fSeh146360 (((addr >> n) & 1) << IPW2200_EEPROM_SHIFT_D) | 196bb5e3b2fSeh146360 IPW2200_EEPROM_C); 197bb5e3b2fSeh146360 } 198bb5e3b2fSeh146360 199bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S); 200bb5e3b2fSeh146360 201bb5e3b2fSeh146360 /* 202bb5e3b2fSeh146360 * data, totally 16 bits, defined by hardware, push from MSB to LSB 203bb5e3b2fSeh146360 */ 204bb5e3b2fSeh146360 val = 0; 205bb5e3b2fSeh146360 for (n = 15; n >= 0; n--) { 206bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_C); 207bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S); 208bb5e3b2fSeh146360 tmp = ipw2200_imem_get32(sc, IPW2200_IMEM_EEPROM_CTL); 209bb5e3b2fSeh146360 val |= ((tmp & IPW2200_EEPROM_Q) >> IPW2200_EEPROM_SHIFT_Q) 210bb5e3b2fSeh146360 << n; 211bb5e3b2fSeh146360 } 212bb5e3b2fSeh146360 213bb5e3b2fSeh146360 ipw2200_rom_control(sc, 0); 214bb5e3b2fSeh146360 215bb5e3b2fSeh146360 /* clear chip select and clock */ 216bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_S); 217bb5e3b2fSeh146360 ipw2200_rom_control(sc, 0); 218bb5e3b2fSeh146360 ipw2200_rom_control(sc, IPW2200_EEPROM_C); 219bb5e3b2fSeh146360 220bb5e3b2fSeh146360 return (BE_16(val)); 221bb5e3b2fSeh146360 } 222bb5e3b2fSeh146360 223bb5e3b2fSeh146360 /* 224bb5e3b2fSeh146360 * Firmware related operations 225bb5e3b2fSeh146360 */ 226bb5e3b2fSeh146360 #define IPW2200_FW_MAJOR_VERSION (2) 227bb5e3b2fSeh146360 #define IPW2200_FW_MINOR_VERSION (4) 228bb5e3b2fSeh146360 229bb5e3b2fSeh146360 #define IPW2200_FW_MAJOR(x)((x) & 0xff) 230bb5e3b2fSeh146360 #define IPW2200_FW_MINOR(x)(((x) & 0xff) >> 8) 231bb5e3b2fSeh146360 232bb5e3b2fSeh146360 /* 233bb5e3b2fSeh146360 * These firwares were issued by Intel as binaries which need to be 234bb5e3b2fSeh146360 * loaded to hardware when card is initiated, or when fatal error 235bb5e3b2fSeh146360 * happened, or when the chip need be reset. 236bb5e3b2fSeh146360 */ 237bb5e3b2fSeh146360 static uint8_t ipw2200_boot_bin [] = { 238bb5e3b2fSeh146360 #include "fw-ipw2200/ipw-2.4-boot.hex" 239bb5e3b2fSeh146360 }; 240bb5e3b2fSeh146360 static uint8_t ipw2200_ucode_bin [] = { 241bb5e3b2fSeh146360 #include "fw-ipw2200/ipw-2.4-bss_ucode.hex" 242bb5e3b2fSeh146360 }; 243bb5e3b2fSeh146360 static uint8_t ipw2200_fw_bin [] = { 244bb5e3b2fSeh146360 #include "fw-ipw2200/ipw-2.4-bss.hex" 245bb5e3b2fSeh146360 }; 246bb5e3b2fSeh146360 247bb5e3b2fSeh146360 #pragma pack(1) 248bb5e3b2fSeh146360 struct header { 249bb5e3b2fSeh146360 uint32_t version; 250bb5e3b2fSeh146360 uint32_t mode; 251bb5e3b2fSeh146360 }; 252bb5e3b2fSeh146360 #pragma pack() 253bb5e3b2fSeh146360 254bb5e3b2fSeh146360 int 255bb5e3b2fSeh146360 ipw2200_cache_firmware(struct ipw2200_softc *sc) 256bb5e3b2fSeh146360 { 257bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT, 258bb5e3b2fSeh146360 "ipw2200_cache_firmware(): enter\n")); 259bb5e3b2fSeh146360 260bb5e3b2fSeh146360 /* boot code */ 261bb5e3b2fSeh146360 sc->sc_fw.boot_base = ipw2200_boot_bin + sizeof (struct header); 262bb5e3b2fSeh146360 sc->sc_fw.boot_size = 263bb5e3b2fSeh146360 sizeof (ipw2200_boot_bin) - sizeof (struct header); 264bb5e3b2fSeh146360 /* ucode */ 265bb5e3b2fSeh146360 sc->sc_fw.uc_base = ipw2200_ucode_bin + sizeof (struct header); 266bb5e3b2fSeh146360 sc->sc_fw.uc_size = sizeof (ipw2200_ucode_bin) - sizeof (struct header); 267bb5e3b2fSeh146360 /* firmware */ 268bb5e3b2fSeh146360 sc->sc_fw.fw_base = ipw2200_fw_bin + sizeof (struct header); 269bb5e3b2fSeh146360 sc->sc_fw.fw_size = sizeof (ipw2200_fw_bin) - sizeof (struct header); 270bb5e3b2fSeh146360 271bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_FW_CACHED; 272bb5e3b2fSeh146360 273bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT, 274bb5e3b2fSeh146360 "ipw2200_cache_firmware(): boot=%u,uc=%u,fw=%u\n", 275bb5e3b2fSeh146360 sc->sc_fw.boot_size, sc->sc_fw.uc_size, sc->sc_fw.fw_size)); 276bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT, 277bb5e3b2fSeh146360 "ipw2200_cache_firmware(): exit\n")); 278bb5e3b2fSeh146360 279bb5e3b2fSeh146360 return (DDI_SUCCESS); 280bb5e3b2fSeh146360 } 281bb5e3b2fSeh146360 282bb5e3b2fSeh146360 /* 283bb5e3b2fSeh146360 * If user-land firmware loading is supported, this routine will 284bb5e3b2fSeh146360 * free kernel memory, when sc->sc_fw.bin_base & sc->sc_fw.bin_size 285bb5e3b2fSeh146360 * are not empty 286bb5e3b2fSeh146360 */ 287bb5e3b2fSeh146360 int 288bb5e3b2fSeh146360 ipw2200_free_firmware(struct ipw2200_softc *sc) 289bb5e3b2fSeh146360 { 290bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_FW_CACHED; 291bb5e3b2fSeh146360 292bb5e3b2fSeh146360 return (DDI_SUCCESS); 293bb5e3b2fSeh146360 } 294bb5e3b2fSeh146360 295bb5e3b2fSeh146360 /* 296bb5e3b2fSeh146360 * the following routines load code onto ipw2200 hardware 297bb5e3b2fSeh146360 */ 298bb5e3b2fSeh146360 int 299bb5e3b2fSeh146360 ipw2200_load_uc(struct ipw2200_softc *sc, uint8_t *buf, size_t size) 300bb5e3b2fSeh146360 { 301bb5e3b2fSeh146360 int ntries, i; 302bb5e3b2fSeh146360 uint16_t *w; 303bb5e3b2fSeh146360 304bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, 305bb5e3b2fSeh146360 IPW2200_RST_STOP_MASTER | ipw2200_csr_get32(sc, IPW2200_CSR_RST)); 306bb5e3b2fSeh146360 for (ntries = 0; ntries < 5; ntries++) { 307bb5e3b2fSeh146360 if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) & 308bb5e3b2fSeh146360 IPW2200_RST_MASTER_DISABLED) 309bb5e3b2fSeh146360 break; 310bb5e3b2fSeh146360 drv_usecwait(10); 311bb5e3b2fSeh146360 } 312bb5e3b2fSeh146360 if (ntries == 5) { 313bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_CONT, 314bb5e3b2fSeh146360 "ipw2200_load_uc(): timeout waiting for master")); 315bb5e3b2fSeh146360 return (DDI_FAILURE); 316bb5e3b2fSeh146360 } 317bb5e3b2fSeh146360 318bb5e3b2fSeh146360 ipw2200_imem_put32(sc, 0x3000e0, 0x80000000); 319bb5e3b2fSeh146360 drv_usecwait(5000); 320bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, 321bb5e3b2fSeh146360 ~IPW2200_RST_PRINCETON_RESET & 322bb5e3b2fSeh146360 ipw2200_csr_get32(sc, IPW2200_CSR_RST)); 323bb5e3b2fSeh146360 drv_usecwait(5000); 324bb5e3b2fSeh146360 ipw2200_imem_put32(sc, 0x3000e0, 0); 325bb5e3b2fSeh146360 drv_usecwait(1000); 326bb5e3b2fSeh146360 ipw2200_imem_put32(sc, IPW2200_IMEM_EVENT_CTL, 1); 327bb5e3b2fSeh146360 drv_usecwait(1000); 328bb5e3b2fSeh146360 ipw2200_imem_put32(sc, IPW2200_IMEM_EVENT_CTL, 0); 329bb5e3b2fSeh146360 drv_usecwait(1000); 330bb5e3b2fSeh146360 ipw2200_imem_put8(sc, 0x200000, 0x00); 331bb5e3b2fSeh146360 ipw2200_imem_put8(sc, 0x200000, 0x40); 332bb5e3b2fSeh146360 drv_usecwait(1000); 333bb5e3b2fSeh146360 334922d2c76Seh146360 for (w = (uint16_t *)(uintptr_t)buf; size > 0; w++, size -= 2) 335bb5e3b2fSeh146360 ipw2200_imem_put16(sc, 0x200010, LE_16(*w)); 336bb5e3b2fSeh146360 337bb5e3b2fSeh146360 ipw2200_imem_put8(sc, 0x200000, 0x00); 338bb5e3b2fSeh146360 ipw2200_imem_put8(sc, 0x200000, 0x80); 339bb5e3b2fSeh146360 340bb5e3b2fSeh146360 /* 341bb5e3b2fSeh146360 * try many times to wait the upload is ready, 2000times 342bb5e3b2fSeh146360 */ 343bb5e3b2fSeh146360 for (ntries = 0; ntries < 2000; ntries++) { 344bb5e3b2fSeh146360 uint8_t val; 345bb5e3b2fSeh146360 346bb5e3b2fSeh146360 val = ipw2200_imem_get8(sc, 0x200000); 347bb5e3b2fSeh146360 if (val & 1) 348bb5e3b2fSeh146360 break; 349bb5e3b2fSeh146360 drv_usecwait(1000); /* wait for a while */ 350bb5e3b2fSeh146360 } 351bb5e3b2fSeh146360 if (ntries == 2000) { 352bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 353bb5e3b2fSeh146360 "ipw2200_load_uc(): timeout waiting for ucode init.\n")); 354bb5e3b2fSeh146360 return (DDI_FAILURE); 355bb5e3b2fSeh146360 } 356bb5e3b2fSeh146360 357bb5e3b2fSeh146360 for (i = 0; i < 7; i++) 358bb5e3b2fSeh146360 (void) ipw2200_imem_get32(sc, 0x200004); 359bb5e3b2fSeh146360 360bb5e3b2fSeh146360 ipw2200_imem_put8(sc, 0x200000, 0x00); 361bb5e3b2fSeh146360 362bb5e3b2fSeh146360 return (DDI_SUCCESS); 363bb5e3b2fSeh146360 } 364bb5e3b2fSeh146360 365bb5e3b2fSeh146360 #define MAX_DR_NUM (64) 366bb5e3b2fSeh146360 #define MAX_DR_SIZE (4096) 367bb5e3b2fSeh146360 368bb5e3b2fSeh146360 int 369bb5e3b2fSeh146360 ipw2200_load_fw(struct ipw2200_softc *sc, uint8_t *buf, size_t size) 370bb5e3b2fSeh146360 { 371bb5e3b2fSeh146360 struct dma_region dr[MAX_DR_NUM]; /* maximal, 64 * 4KB = 256KB */ 372bb5e3b2fSeh146360 uint8_t *p, *end, *v; 373bb5e3b2fSeh146360 uint32_t mlen; 374bb5e3b2fSeh146360 uint32_t src, dst, ctl, len, sum, off; 375bb5e3b2fSeh146360 uint32_t sentinel; 376bb5e3b2fSeh146360 int ntries, err, cnt, i; 377*d3d50737SRafael Vanoni clock_t clk = drv_usectohz(5000000); /* 5 second */ 378bb5e3b2fSeh146360 379bb5e3b2fSeh146360 ipw2200_imem_put32(sc, 0x3000a0, 0x27000); 380bb5e3b2fSeh146360 381bb5e3b2fSeh146360 p = buf; 382bb5e3b2fSeh146360 end = p + size; 383bb5e3b2fSeh146360 384bb5e3b2fSeh146360 cnt = 0; 385bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &dr[cnt], MAX_DR_SIZE, DDI_DMA_READ, 386bb5e3b2fSeh146360 DDI_DMA_STREAMING); 387bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 388bb5e3b2fSeh146360 goto fail0; 389bb5e3b2fSeh146360 off = 0; 390bb5e3b2fSeh146360 src = dr[cnt].dr_pbase; 391bb5e3b2fSeh146360 392bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0x27000); 393bb5e3b2fSeh146360 394bb5e3b2fSeh146360 while (p < end) { 395922d2c76Seh146360 dst = LE_32(*((uint32_t *)(uintptr_t)p)); p += 4; 396922d2c76Seh146360 len = LE_32(*((uint32_t *)(uintptr_t)p)); p += 4; 397bb5e3b2fSeh146360 v = p; 398bb5e3b2fSeh146360 p += len; 399bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT, 400bb5e3b2fSeh146360 "ipw2200_load_fw(): dst=0x%x,len=%u\n", dst, len)); 401bb5e3b2fSeh146360 402bb5e3b2fSeh146360 while (len > 0) { 403bb5e3b2fSeh146360 /* 404bb5e3b2fSeh146360 * if no DMA region is available, allocate a new one 405bb5e3b2fSeh146360 */ 406bb5e3b2fSeh146360 if (off == dr[cnt].dr_size) { 407bb5e3b2fSeh146360 cnt++; 408bb5e3b2fSeh146360 if (cnt >= MAX_DR_NUM) { 409bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 410bb5e3b2fSeh146360 "ipw2200_load_fw(): " 411bb5e3b2fSeh146360 "maximum %d DRs is reached\n", 412bb5e3b2fSeh146360 cnt)); 413bb5e3b2fSeh146360 cnt--; /* only free alloced DMA */ 414bb5e3b2fSeh146360 goto fail1; 415bb5e3b2fSeh146360 } 416bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &dr[cnt], 417bb5e3b2fSeh146360 MAX_DR_SIZE, DDI_DMA_WRITE, 418bb5e3b2fSeh146360 DDI_DMA_STREAMING); 419bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 420bb5e3b2fSeh146360 cnt--; /* only free alloced DMA */ 421bb5e3b2fSeh146360 goto fail1; 422bb5e3b2fSeh146360 } 423bb5e3b2fSeh146360 off = 0; 424bb5e3b2fSeh146360 src = dr[cnt].dr_pbase; 425bb5e3b2fSeh146360 } 426bb5e3b2fSeh146360 mlen = min(IPW2200_CB_MAXDATALEN, len); 427bb5e3b2fSeh146360 mlen = min(mlen, dr[cnt].dr_size - off); 428bb5e3b2fSeh146360 429bb5e3b2fSeh146360 (void) memcpy(dr[cnt].dr_base + off, v, mlen); 430bb5e3b2fSeh146360 (void) ddi_dma_sync(dr[cnt].dr_hnd, off, mlen, 431bb5e3b2fSeh146360 DDI_DMA_SYNC_FORDEV); 432bb5e3b2fSeh146360 433bb5e3b2fSeh146360 ctl = IPW2200_CB_DEFAULT_CTL | mlen; 434bb5e3b2fSeh146360 sum = ctl ^ src ^ dst; 435bb5e3b2fSeh146360 /* 436bb5e3b2fSeh146360 * write a command 437bb5e3b2fSeh146360 */ 438bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, ctl); 439bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, src); 440bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, dst); 441bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, sum); 442bb5e3b2fSeh146360 443bb5e3b2fSeh146360 off += mlen; 444bb5e3b2fSeh146360 src += mlen; 445bb5e3b2fSeh146360 dst += mlen; 446bb5e3b2fSeh146360 v += mlen; 447bb5e3b2fSeh146360 len -= mlen; 448bb5e3b2fSeh146360 } 449bb5e3b2fSeh146360 } 450bb5e3b2fSeh146360 451bb5e3b2fSeh146360 sentinel = ipw2200_csr_get32(sc, IPW2200_CSR_AUTOINC_ADDR); 452bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0); 453bb5e3b2fSeh146360 454bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT, 455bb5e3b2fSeh146360 "ipw2200_load_fw(): sentinel=%x\n", sentinel)); 456bb5e3b2fSeh146360 457bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, 458bb5e3b2fSeh146360 ~(IPW2200_RST_MASTER_DISABLED | IPW2200_RST_STOP_MASTER) 459bb5e3b2fSeh146360 & ipw2200_csr_get32(sc, IPW2200_CSR_RST)); 460bb5e3b2fSeh146360 461bb5e3b2fSeh146360 ipw2200_imem_put32(sc, 0x3000a4, 0x540100); 462bb5e3b2fSeh146360 for (ntries = 0; ntries < 400; ntries++) { 463bb5e3b2fSeh146360 uint32_t val; 464bb5e3b2fSeh146360 val = ipw2200_imem_get32(sc, 0x3000d0); 465bb5e3b2fSeh146360 if (val >= sentinel) 466bb5e3b2fSeh146360 break; 467bb5e3b2fSeh146360 drv_usecwait(100); 468bb5e3b2fSeh146360 } 469bb5e3b2fSeh146360 if (ntries == 400) { 470bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 471bb5e3b2fSeh146360 "ipw2200_load_fw(): timeout processing command blocks\n")); 472bb5e3b2fSeh146360 goto fail1; 473bb5e3b2fSeh146360 } 474bb5e3b2fSeh146360 475bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 476bb5e3b2fSeh146360 477bb5e3b2fSeh146360 ipw2200_imem_put32(sc, 0x3000a4, 0x540c00); 478bb5e3b2fSeh146360 479bb5e3b2fSeh146360 /* 480bb5e3b2fSeh146360 * enable all interrupts 481bb5e3b2fSeh146360 */ 482bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL); 483bb5e3b2fSeh146360 484bb5e3b2fSeh146360 /* 485bb5e3b2fSeh146360 * tell the adapter to initialize the firmware, 486bb5e3b2fSeh146360 * just simply set it to 0 487bb5e3b2fSeh146360 */ 488bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, 0); 489bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CTL, 490bb5e3b2fSeh146360 ipw2200_csr_get32(sc, IPW2200_CSR_CTL) | 491bb5e3b2fSeh146360 IPW2200_CTL_ALLOW_STANDBY); 492bb5e3b2fSeh146360 493bb5e3b2fSeh146360 /* 494bb5e3b2fSeh146360 * wait for interrupt to notify fw initialization is done 495bb5e3b2fSeh146360 */ 496bb5e3b2fSeh146360 sc->sc_fw_ok = 0; 497bb5e3b2fSeh146360 while (!sc->sc_fw_ok) { 498bb5e3b2fSeh146360 /* 499bb5e3b2fSeh146360 * There is an enhancement! we just change from 1s to 5s 500bb5e3b2fSeh146360 */ 501*d3d50737SRafael Vanoni if (cv_reltimedwait(&sc->sc_fw_cond, &sc->sc_ilock, clk, 502*d3d50737SRafael Vanoni TR_CLOCK_TICK) < 0) 503bb5e3b2fSeh146360 break; 504bb5e3b2fSeh146360 } 505bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 506bb5e3b2fSeh146360 507bb5e3b2fSeh146360 if (!sc->sc_fw_ok) { 508bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 509bb5e3b2fSeh146360 "ipw2200_load_fw(): firmware(%u) load failed!", size)); 510bb5e3b2fSeh146360 goto fail1; 511bb5e3b2fSeh146360 } 512bb5e3b2fSeh146360 513bb5e3b2fSeh146360 for (i = 0; i <= cnt; i++) 514bb5e3b2fSeh146360 ipw2200_dma_region_free(&dr[i]); 515bb5e3b2fSeh146360 516bb5e3b2fSeh146360 return (DDI_SUCCESS); 517bb5e3b2fSeh146360 518bb5e3b2fSeh146360 fail1: 519bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 520bb5e3b2fSeh146360 "ipw2200_load_fw(): DMA allocation failed, cnt=%d\n", cnt)); 521bb5e3b2fSeh146360 for (i = 0; i <= cnt; i++) 522bb5e3b2fSeh146360 ipw2200_dma_region_free(&dr[i]); 523bb5e3b2fSeh146360 fail0: 524bb5e3b2fSeh146360 return (DDI_FAILURE); 525bb5e3b2fSeh146360 } 526