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