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