Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / ipw / ipw2100_hw.c
blobf8c7d105838c3c11f59077437ebef0ddf8b21aa1
1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * Copyright (c) 2004
8 * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
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.
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.
34 * Intel Wireless PRO/2100 mini-PCI adapter driver
35 * ipw2100_hw.c is used to handle hardware operation and firmware operations.
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>
45 #include "ipw2100.h"
46 #include "ipw2100_impl.h"
49 * Hardware related operations
51 #define IPW2100_EEPROM_SHIFT_D (2)
52 #define IPW2100_EEPROM_SHIFT_Q (4)
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)
59 uint8_t
60 ipw2100_csr_get8(struct ipw2100_softc *sc, uint32_t off)
62 return (ddi_get8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off)));
65 uint16_t
66 ipw2100_csr_get16(struct ipw2100_softc *sc, uint32_t off)
68 return (ddi_get16(sc->sc_ioh,
69 (uint16_t *)((uintptr_t)sc->sc_regs + off)));
72 uint32_t
73 ipw2100_csr_get32(struct ipw2100_softc *sc, uint32_t off)
75 return (ddi_get32(sc->sc_ioh,
76 (uint32_t *)((uintptr_t)sc->sc_regs + off)));
79 void
80 ipw2100_csr_rep_get16(struct ipw2100_softc *sc,
81 uint32_t off, uint16_t *buf, size_t cnt)
83 ddi_rep_get16(sc->sc_ioh, buf,
84 (uint16_t *)((uintptr_t)sc->sc_regs + off),
85 cnt, DDI_DEV_NO_AUTOINCR);
88 void
89 ipw2100_csr_put8(struct ipw2100_softc *sc, uint32_t off, uint8_t val)
91 ddi_put8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off), val);
94 void
95 ipw2100_csr_put16(struct ipw2100_softc *sc, uint32_t off, uint16_t val)
97 ddi_put16(sc->sc_ioh,
98 (uint16_t *)((uintptr_t)sc->sc_regs + off), val);
101 void
102 ipw2100_csr_put32(struct ipw2100_softc *sc, uint32_t off, uint32_t val)
104 ddi_put32(sc->sc_ioh,
105 (uint32_t *)((uintptr_t)sc->sc_regs + off), val);
108 void
109 ipw2100_csr_rep_put8(struct ipw2100_softc *sc,
110 uint32_t off, uint8_t *buf, size_t cnt)
112 ddi_rep_put8(sc->sc_ioh, buf, (uint8_t *)(sc->sc_regs + off),
113 cnt, DDI_DEV_NO_AUTOINCR);
116 uint8_t
117 ipw2100_imem_get8(struct ipw2100_softc *sc, int32_t addr)
119 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
121 return (ipw2100_csr_get8(sc, IPW2100_CSR_INDIRECT_DATA));
124 uint16_t
125 ipw2100_imem_get16(struct ipw2100_softc *sc, uint32_t addr)
127 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
129 return (ipw2100_csr_get16(sc, IPW2100_CSR_INDIRECT_DATA));
132 uint32_t
133 ipw2100_imem_get32(struct ipw2100_softc *sc, uint32_t addr)
135 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
137 return (ipw2100_csr_get32(sc, IPW2100_CSR_INDIRECT_DATA));
140 void
141 ipw2100_imem_rep_get16(struct ipw2100_softc *sc,
142 uint32_t addr, uint16_t *buf, size_t cnt)
144 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
145 ipw2100_csr_rep_get16(sc, IPW2100_CSR_INDIRECT_DATA, buf, cnt);
148 void
149 ipw2100_imem_put8(struct ipw2100_softc *sc, uint32_t addr, uint8_t val)
151 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
152 ipw2100_csr_put8(sc, IPW2100_CSR_INDIRECT_DATA, val);
155 void
156 ipw2100_imem_put16(struct ipw2100_softc *sc, uint32_t addr, uint16_t val)
158 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
159 ipw2100_csr_put16(sc, IPW2100_CSR_INDIRECT_DATA, val);
162 void
163 ipw2100_imem_put32(struct ipw2100_softc *sc, uint32_t addr, uint32_t val)
165 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
166 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_DATA, val);
169 void
170 ipw2100_imem_rep_put8(struct ipw2100_softc *sc,
171 uint32_t addr, uint8_t *buf, size_t cnt)
173 ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
174 ipw2100_csr_rep_put8(sc, IPW2100_CSR_INDIRECT_DATA, buf, cnt);
177 void
178 ipw2100_imem_getbuf(struct ipw2100_softc *sc,
179 uint32_t addr, uint8_t *buf, size_t cnt)
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));
188 void
189 ipw2100_imem_putbuf(struct ipw2100_softc *sc,
190 uint32_t addr, uint8_t *buf, size_t cnt)
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);
199 void
200 ipw2100_rom_control(struct ipw2100_softc *sc, uint32_t val)
202 ipw2100_imem_put32(sc, IPW2100_IMEM_EEPROM_CTL, val);
203 drv_usecwait(IPW2100_EEPROM_DELAY);
207 uint8_t
208 ipw2100_table1_get8(struct ipw2100_softc *sc, uint32_t off)
210 uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off);
211 return (ipw2100_imem_get8(sc, addr));
214 uint32_t
215 ipw2100_table1_get32(struct ipw2100_softc *sc, uint32_t off)
217 uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off);
218 return (ipw2100_imem_get32(sc, addr));
221 void
222 ipw2100_table1_put32(struct ipw2100_softc *sc, uint32_t off, uint32_t val)
224 uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off);
225 ipw2100_imem_put32(sc, addr, val);
229 ipw2100_table2_getbuf(struct ipw2100_softc *sc,
230 uint32_t off, uint8_t *buf, uint32_t *len)
232 uint32_t addr, info;
233 uint16_t cnt, size;
234 uint32_t total;
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));
240 cnt = info >> 16;
241 size = info & 0xffff;
242 total = cnt * size;
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);
251 *len = total;
252 ipw2100_imem_getbuf(sc, addr, buf, total);
254 return (DDI_SUCCESS);
257 uint16_t
258 ipw2100_rom_get16(struct ipw2100_softc *sc, uint8_t addr)
260 uint32_t tmp;
261 uint16_t val;
262 int n;
265 * According to i2c bus protocol to set them.
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);
283 * address, totally 8 bits, defined by hardware, push from MSB to LSB
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);
293 ipw2100_rom_control(sc, IPW2100_EEPROM_S);
296 * data, totally 16 bits, defined by hardware, push from MSB to LSB
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;
307 ipw2100_rom_control(sc, 0);
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);
314 return (LE_16(val));
319 * Firmware related operations
321 #define IPW2100_FW_MAJOR_VERSION (1)
322 #define IPW2100_FW_MINOR_VERSION (3)
324 #define IPW2100_FW_MAJOR(x)((x) & 0xff)
325 #define IPW2100_FW_MINOR(x)(((x) & 0xff) >> 8)
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.
332 static uint8_t ipw2100_firmware_bin [] = {
333 #include "fw-ipw2100/ipw2100-1.3.fw.hex"
337 ipw2100_cache_firmware(struct ipw2100_softc *sc)
339 uint8_t *bin = ipw2100_firmware_bin;
340 struct ipw2100_firmware_hdr *h = (struct ipw2100_firmware_hdr *)bin;
342 IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
343 "ipw2100_cache_firmwares(): enter\n"));
345 sc->sc_fw.bin_base = bin;
346 sc->sc_fw.bin_size = sizeof (ipw2100_firmware_bin);
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);
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);
360 sc->sc_flags |= IPW2100_FLAG_FW_CACHED;
362 IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
363 "ipw2100_cache_firmware(): exit\n"));
365 return (DDI_SUCCESS);
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.
374 ipw2100_free_firmware(struct ipw2100_softc *sc)
376 sc->sc_flags &= ~IPW2100_FLAG_FW_CACHED;
378 return (DDI_SUCCESS);
382 * the following routines load code onto ipw2100 hardware
385 ipw2100_load_uc(struct ipw2100_softc *sc)
387 int ntries;
389 ipw2100_imem_put32(sc, 0x3000e0, 0x80000000);
390 ipw2100_csr_put32(sc, IPW2100_CSR_RST, 0);
392 ipw2100_imem_put16(sc, 0x220000, 0x0703);
393 ipw2100_imem_put16(sc, 0x220000, 0x0707);
395 ipw2100_imem_put8(sc, 0x210014, 0x72);
396 ipw2100_imem_put8(sc, 0x210014, 0x72);
398 ipw2100_imem_put8(sc, 0x210000, 0x40);
399 ipw2100_imem_put8(sc, 0x210000, 0x00);
400 ipw2100_imem_put8(sc, 0x210000, 0x40);
402 ipw2100_imem_rep_put8(sc, 0x210010,
403 sc->sc_fw.uc_base, sc->sc_fw.uc_size);
405 ipw2100_imem_put8(sc, 0x210000, 0x00);
406 ipw2100_imem_put8(sc, 0x210000, 0x00);
407 ipw2100_imem_put8(sc, 0x210000, 0x80);
409 ipw2100_imem_put16(sc, 0x220000, 0x0703);
410 ipw2100_imem_put16(sc, 0x220000, 0x0707);
412 ipw2100_imem_put8(sc, 0x210014, 0x72);
413 ipw2100_imem_put8(sc, 0x210014, 0x72);
415 ipw2100_imem_put8(sc, 0x210000, 0x00);
416 ipw2100_imem_put8(sc, 0x210000, 0x80);
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 */
424 if (ntries == 5000)
425 return (DDI_FAILURE);
427 ipw2100_imem_put32(sc, 0x3000e0, 0);
429 return (DDI_SUCCESS);
433 ipw2100_load_fw(struct ipw2100_softc *sc)
435 uint8_t *p, *e;
436 uint32_t dst;
437 uint16_t len;
438 clock_t clk;
440 IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
441 "ipw2100_load_fw(): enter\n"));
443 p = sc->sc_fw.fw_base;
444 e = sc->sc_fw.fw_base + sc->sc_fw.fw_size;
445 while (p < e) {
447 * each block is organized as <DST,LEN,DATA>
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);
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);
462 ipw2100_imem_putbuf(sc, dst, p, len);
463 p += len;
466 ipw2100_csr_put32(sc, IPW2100_CSR_IO,
467 IPW2100_IO_GPIO1_ENABLE | IPW2100_IO_GPIO3_MASK |
468 IPW2100_IO_LED_OFF);
470 mutex_enter(&sc->sc_ilock);
473 * enable all interrupts
475 ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL);
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);
482 * wait for interrupt to notify fw initialization is done
484 clk = drv_usectohz(5000000); /* 5 second */
485 while (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) {
487 * wait longer for the fw initialized
489 if (cv_reltimedwait(&sc->sc_fw_cond, &sc->sc_ilock, clk,
490 TR_CLOCK_TICK) < 0)
491 break;
493 mutex_exit(&sc->sc_ilock);
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);
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);
505 IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
506 "ipw2100_load_fw(): exit\n"));
507 return (DDI_SUCCESS);