2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
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
13 * 1. Redistributions of source code must retain the above copyright
14 * notice unmodified, this list of conditions, and the following
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
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>
40 #include <sys/sunddi.h>
42 #include <sys/stream.h>
43 #include <sys/strsun.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)
60 ipw2100_csr_get8(struct ipw2100_softc
*sc
, uint32_t off
)
62 return (ddi_get8(sc
->sc_ioh
, (uint8_t *)(sc
->sc_regs
+ off
)));
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
)));
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
)));
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
);
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
);
95 ipw2100_csr_put16(struct ipw2100_softc
*sc
, uint32_t off
, uint16_t val
)
98 (uint16_t *)((uintptr_t)sc
->sc_regs
+ off
), val
);
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
);
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
);
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
));
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
));
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
));
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
);
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
);
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
);
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
);
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
);
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));
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);
195 IPW2100_CSR_INDIRECT_DATA
+(addr
& 3), *buf
);
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
);
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
));
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
));
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
)
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));
241 size
= info
& 0xffff;
245 IPW2100_WARN((sc
->sc_dip
, CE_WARN
,
246 "ipw2100_table2_getbuf(): invalid table offset = 0x%08x\n",
248 return (DDI_FAILURE
);
252 ipw2100_imem_getbuf(sc
, addr
, buf
, total
);
254 return (DDI_SUCCESS
);
258 ipw2100_rom_get16(struct ipw2100_softc
*sc
, uint8_t addr
)
265 * According to i2c bus protocol to set them.
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
);
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
);
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
)
293 ipw2100_rom_control(sc
, IPW2100_EEPROM_S
);
296 * data, totally 16 bits, defined by hardware, push from MSB to LSB
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
);
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",
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
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
)
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);
419 for (ntries
= 0; ntries
< 5000; ntries
++) {
420 if (ipw2100_imem_get8(sc
, 0x210000) & 1)
422 drv_usecwait(1000); /* wait for a while */
425 return (DDI_FAILURE
);
427 ipw2100_imem_put32(sc
, 0x3000e0, 0);
429 return (DDI_SUCCESS
);
433 ipw2100_load_fw(struct ipw2100_softc
*sc
)
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
;
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
);
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
);
466 ipw2100_csr_put32(sc
, IPW2100_CSR_IO
,
467 IPW2100_IO_GPIO1_ENABLE
| IPW2100_IO_GPIO3_MASK
|
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
,
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
);