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