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