xref: /illumos-gate/usr/src/uts/common/io/iwi/ipw2200_hw.c (revision df3cd224ef765c29101e4110546062199562f757)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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