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