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