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