xref: /titanic_50/usr/src/uts/common/io/ipw/ipw2100_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
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/2100 mini-PCI adapter driver
35bb5e3b2fSeh146360  * ipw2100_hw.c is used to handle hardware operation 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 "ipw2100.h"
46bb5e3b2fSeh146360 #include "ipw2100_impl.h"
47bb5e3b2fSeh146360 
48bb5e3b2fSeh146360 /*
49bb5e3b2fSeh146360  * Hardware related operations
50bb5e3b2fSeh146360  */
51bb5e3b2fSeh146360 #define	IPW2100_EEPROM_SHIFT_D	(2)
52bb5e3b2fSeh146360 #define	IPW2100_EEPROM_SHIFT_Q	(4)
53bb5e3b2fSeh146360 
54bb5e3b2fSeh146360 #define	IPW2100_EEPROM_C	(1 << 0)
55bb5e3b2fSeh146360 #define	IPW2100_EEPROM_S	(1 << 1)
56bb5e3b2fSeh146360 #define	IPW2100_EEPROM_D	(1 << IPW2100_EEPROM_SHIFT_D)
57bb5e3b2fSeh146360 #define	IPW2100_EEPROM_Q	(1 << IPW2100_EEPROM_SHIFT_Q)
58bb5e3b2fSeh146360 
59bb5e3b2fSeh146360 uint8_t
ipw2100_csr_get8(struct ipw2100_softc * sc,uint32_t off)60bb5e3b2fSeh146360 ipw2100_csr_get8(struct ipw2100_softc *sc, uint32_t off)
61bb5e3b2fSeh146360 {
62bb5e3b2fSeh146360 	return (ddi_get8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off)));
63bb5e3b2fSeh146360 }
64bb5e3b2fSeh146360 
65bb5e3b2fSeh146360 uint16_t
ipw2100_csr_get16(struct ipw2100_softc * sc,uint32_t off)66bb5e3b2fSeh146360 ipw2100_csr_get16(struct ipw2100_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
ipw2100_csr_get32(struct ipw2100_softc * sc,uint32_t off)73bb5e3b2fSeh146360 ipw2100_csr_get32(struct ipw2100_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
ipw2100_csr_rep_get16(struct ipw2100_softc * sc,uint32_t off,uint16_t * buf,size_t cnt)80bb5e3b2fSeh146360 ipw2100_csr_rep_get16(struct ipw2100_softc *sc,
81bb5e3b2fSeh146360 	uint32_t off, uint16_t *buf, size_t cnt)
82bb5e3b2fSeh146360 {
83922d2c76Seh146360 	ddi_rep_get16(sc->sc_ioh, buf,
84922d2c76Seh146360 	    (uint16_t *)((uintptr_t)sc->sc_regs + off),
85bb5e3b2fSeh146360 	    cnt, DDI_DEV_NO_AUTOINCR);
86bb5e3b2fSeh146360 }
87bb5e3b2fSeh146360 
88bb5e3b2fSeh146360 void
ipw2100_csr_put8(struct ipw2100_softc * sc,uint32_t off,uint8_t val)89bb5e3b2fSeh146360 ipw2100_csr_put8(struct ipw2100_softc *sc, uint32_t off, uint8_t val)
90bb5e3b2fSeh146360 {
91bb5e3b2fSeh146360 	ddi_put8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off), val);
92bb5e3b2fSeh146360 }
93bb5e3b2fSeh146360 
94bb5e3b2fSeh146360 void
ipw2100_csr_put16(struct ipw2100_softc * sc,uint32_t off,uint16_t val)95bb5e3b2fSeh146360 ipw2100_csr_put16(struct ipw2100_softc *sc, uint32_t off, uint16_t val)
96bb5e3b2fSeh146360 {
97922d2c76Seh146360 	ddi_put16(sc->sc_ioh,
98922d2c76Seh146360 	    (uint16_t *)((uintptr_t)sc->sc_regs + off), val);
99bb5e3b2fSeh146360 }
100bb5e3b2fSeh146360 
101bb5e3b2fSeh146360 void
ipw2100_csr_put32(struct ipw2100_softc * sc,uint32_t off,uint32_t val)102bb5e3b2fSeh146360 ipw2100_csr_put32(struct ipw2100_softc *sc, uint32_t off, uint32_t val)
103bb5e3b2fSeh146360 {
104922d2c76Seh146360 	ddi_put32(sc->sc_ioh,
105922d2c76Seh146360 	    (uint32_t *)((uintptr_t)sc->sc_regs + off), val);
106bb5e3b2fSeh146360 }
107bb5e3b2fSeh146360 
108bb5e3b2fSeh146360 void
ipw2100_csr_rep_put8(struct ipw2100_softc * sc,uint32_t off,uint8_t * buf,size_t cnt)109bb5e3b2fSeh146360 ipw2100_csr_rep_put8(struct ipw2100_softc *sc,
110bb5e3b2fSeh146360 	uint32_t off, uint8_t *buf, size_t cnt)
111bb5e3b2fSeh146360 {
112bb5e3b2fSeh146360 	ddi_rep_put8(sc->sc_ioh, buf, (uint8_t *)(sc->sc_regs + off),
113bb5e3b2fSeh146360 	    cnt, DDI_DEV_NO_AUTOINCR);
114bb5e3b2fSeh146360 }
115bb5e3b2fSeh146360 
116bb5e3b2fSeh146360 uint8_t
ipw2100_imem_get8(struct ipw2100_softc * sc,int32_t addr)117bb5e3b2fSeh146360 ipw2100_imem_get8(struct ipw2100_softc *sc, int32_t addr)
118bb5e3b2fSeh146360 {
119bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
120bb5e3b2fSeh146360 
121bb5e3b2fSeh146360 	return (ipw2100_csr_get8(sc, IPW2100_CSR_INDIRECT_DATA));
122bb5e3b2fSeh146360 }
123bb5e3b2fSeh146360 
124bb5e3b2fSeh146360 uint16_t
ipw2100_imem_get16(struct ipw2100_softc * sc,uint32_t addr)125bb5e3b2fSeh146360 ipw2100_imem_get16(struct ipw2100_softc *sc, uint32_t addr)
126bb5e3b2fSeh146360 {
127bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
128bb5e3b2fSeh146360 
129bb5e3b2fSeh146360 	return (ipw2100_csr_get16(sc, IPW2100_CSR_INDIRECT_DATA));
130bb5e3b2fSeh146360 }
131bb5e3b2fSeh146360 
132bb5e3b2fSeh146360 uint32_t
ipw2100_imem_get32(struct ipw2100_softc * sc,uint32_t addr)133bb5e3b2fSeh146360 ipw2100_imem_get32(struct ipw2100_softc *sc, uint32_t addr)
134bb5e3b2fSeh146360 {
135bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
136bb5e3b2fSeh146360 
137bb5e3b2fSeh146360 	return (ipw2100_csr_get32(sc, IPW2100_CSR_INDIRECT_DATA));
138bb5e3b2fSeh146360 }
139bb5e3b2fSeh146360 
140bb5e3b2fSeh146360 void
ipw2100_imem_rep_get16(struct ipw2100_softc * sc,uint32_t addr,uint16_t * buf,size_t cnt)141bb5e3b2fSeh146360 ipw2100_imem_rep_get16(struct ipw2100_softc *sc,
142bb5e3b2fSeh146360 	uint32_t addr, uint16_t *buf, size_t cnt)
143bb5e3b2fSeh146360 {
144bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
145bb5e3b2fSeh146360 	ipw2100_csr_rep_get16(sc, IPW2100_CSR_INDIRECT_DATA, buf, cnt);
146bb5e3b2fSeh146360 }
147bb5e3b2fSeh146360 
148bb5e3b2fSeh146360 void
ipw2100_imem_put8(struct ipw2100_softc * sc,uint32_t addr,uint8_t val)149bb5e3b2fSeh146360 ipw2100_imem_put8(struct ipw2100_softc *sc, uint32_t addr, uint8_t val)
150bb5e3b2fSeh146360 {
151bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
152bb5e3b2fSeh146360 	ipw2100_csr_put8(sc, IPW2100_CSR_INDIRECT_DATA, val);
153bb5e3b2fSeh146360 }
154bb5e3b2fSeh146360 
155bb5e3b2fSeh146360 void
ipw2100_imem_put16(struct ipw2100_softc * sc,uint32_t addr,uint16_t val)156bb5e3b2fSeh146360 ipw2100_imem_put16(struct ipw2100_softc *sc, uint32_t addr, uint16_t val)
157bb5e3b2fSeh146360 {
158bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
159bb5e3b2fSeh146360 	ipw2100_csr_put16(sc, IPW2100_CSR_INDIRECT_DATA, val);
160bb5e3b2fSeh146360 }
161bb5e3b2fSeh146360 
162bb5e3b2fSeh146360 void
ipw2100_imem_put32(struct ipw2100_softc * sc,uint32_t addr,uint32_t val)163bb5e3b2fSeh146360 ipw2100_imem_put32(struct ipw2100_softc *sc, uint32_t addr, uint32_t val)
164bb5e3b2fSeh146360 {
165bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
166bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_DATA, val);
167bb5e3b2fSeh146360 }
168bb5e3b2fSeh146360 
169bb5e3b2fSeh146360 void
ipw2100_imem_rep_put8(struct ipw2100_softc * sc,uint32_t addr,uint8_t * buf,size_t cnt)170bb5e3b2fSeh146360 ipw2100_imem_rep_put8(struct ipw2100_softc *sc,
171bb5e3b2fSeh146360 	uint32_t addr, uint8_t *buf, size_t cnt)
172bb5e3b2fSeh146360 {
173bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
174bb5e3b2fSeh146360 	ipw2100_csr_rep_put8(sc, IPW2100_CSR_INDIRECT_DATA, buf, cnt);
175bb5e3b2fSeh146360 }
176bb5e3b2fSeh146360 
177bb5e3b2fSeh146360 void
ipw2100_imem_getbuf(struct ipw2100_softc * sc,uint32_t addr,uint8_t * buf,size_t cnt)178bb5e3b2fSeh146360 ipw2100_imem_getbuf(struct ipw2100_softc *sc,
179bb5e3b2fSeh146360 	uint32_t addr, uint8_t *buf, size_t cnt)
180bb5e3b2fSeh146360 {
181bb5e3b2fSeh146360 	for (; cnt > 0; addr++, buf++, cnt--) {
182bb5e3b2fSeh146360 		ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr & ~3);
183bb5e3b2fSeh146360 		*buf = ipw2100_csr_get8(sc,
184bb5e3b2fSeh146360 		    IPW2100_CSR_INDIRECT_DATA +(addr & 3));
185bb5e3b2fSeh146360 	}
186bb5e3b2fSeh146360 }
187bb5e3b2fSeh146360 
188bb5e3b2fSeh146360 void
ipw2100_imem_putbuf(struct ipw2100_softc * sc,uint32_t addr,uint8_t * buf,size_t cnt)189bb5e3b2fSeh146360 ipw2100_imem_putbuf(struct ipw2100_softc *sc,
190bb5e3b2fSeh146360 	uint32_t addr, uint8_t *buf, size_t cnt)
191bb5e3b2fSeh146360 {
192bb5e3b2fSeh146360 	for (; cnt > 0; addr++, buf++, cnt--) {
193bb5e3b2fSeh146360 		ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr & ~3);
194bb5e3b2fSeh146360 		ipw2100_csr_put8(sc,
195bb5e3b2fSeh146360 		    IPW2100_CSR_INDIRECT_DATA +(addr & 3), *buf);
196bb5e3b2fSeh146360 	}
197bb5e3b2fSeh146360 }
198bb5e3b2fSeh146360 
199bb5e3b2fSeh146360 void
ipw2100_rom_control(struct ipw2100_softc * sc,uint32_t val)200bb5e3b2fSeh146360 ipw2100_rom_control(struct ipw2100_softc *sc, uint32_t val)
201bb5e3b2fSeh146360 {
202bb5e3b2fSeh146360 	ipw2100_imem_put32(sc, IPW2100_IMEM_EEPROM_CTL, val);
203bb5e3b2fSeh146360 	drv_usecwait(IPW2100_EEPROM_DELAY);
204bb5e3b2fSeh146360 }
205bb5e3b2fSeh146360 
206bb5e3b2fSeh146360 
207bb5e3b2fSeh146360 uint8_t
ipw2100_table1_get8(struct ipw2100_softc * sc,uint32_t off)208bb5e3b2fSeh146360 ipw2100_table1_get8(struct ipw2100_softc *sc, uint32_t off)
209bb5e3b2fSeh146360 {
210bb5e3b2fSeh146360 	uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off);
211bb5e3b2fSeh146360 	return (ipw2100_imem_get8(sc, addr));
212bb5e3b2fSeh146360 }
213bb5e3b2fSeh146360 
214bb5e3b2fSeh146360 uint32_t
ipw2100_table1_get32(struct ipw2100_softc * sc,uint32_t off)215bb5e3b2fSeh146360 ipw2100_table1_get32(struct ipw2100_softc *sc, uint32_t off)
216bb5e3b2fSeh146360 {
217bb5e3b2fSeh146360 	uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off);
218bb5e3b2fSeh146360 	return (ipw2100_imem_get32(sc, addr));
219bb5e3b2fSeh146360 }
220bb5e3b2fSeh146360 
221bb5e3b2fSeh146360 void
ipw2100_table1_put32(struct ipw2100_softc * sc,uint32_t off,uint32_t val)222bb5e3b2fSeh146360 ipw2100_table1_put32(struct ipw2100_softc *sc, uint32_t off, uint32_t val)
223bb5e3b2fSeh146360 {
224bb5e3b2fSeh146360 	uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off);
225bb5e3b2fSeh146360 	ipw2100_imem_put32(sc, addr, val);
226bb5e3b2fSeh146360 }
227bb5e3b2fSeh146360 
228bb5e3b2fSeh146360 int
ipw2100_table2_getbuf(struct ipw2100_softc * sc,uint32_t off,uint8_t * buf,uint32_t * len)229bb5e3b2fSeh146360 ipw2100_table2_getbuf(struct ipw2100_softc *sc,
230bb5e3b2fSeh146360 	uint32_t off, uint8_t *buf, uint32_t *len)
231bb5e3b2fSeh146360 {
232bb5e3b2fSeh146360 	uint32_t	addr, info;
233bb5e3b2fSeh146360 	uint16_t	cnt, size;
234bb5e3b2fSeh146360 	uint32_t	total;
235bb5e3b2fSeh146360 
236bb5e3b2fSeh146360 	addr = ipw2100_imem_get32(sc, sc->sc_table2_base + off);
237bb5e3b2fSeh146360 	info = ipw2100_imem_get32(sc,
238bb5e3b2fSeh146360 	    sc->sc_table2_base + off + sizeof (uint32_t));
239bb5e3b2fSeh146360 
240bb5e3b2fSeh146360 	cnt = info >> 16;
241bb5e3b2fSeh146360 	size = info & 0xffff;
242bb5e3b2fSeh146360 	total = cnt * size;
243bb5e3b2fSeh146360 
244bb5e3b2fSeh146360 	if (total > *len) {
245bb5e3b2fSeh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
246bb5e3b2fSeh146360 		    "ipw2100_table2_getbuf(): invalid table offset = 0x%08x\n",
247bb5e3b2fSeh146360 		    off));
248bb5e3b2fSeh146360 		return (DDI_FAILURE);
249bb5e3b2fSeh146360 	}
250bb5e3b2fSeh146360 
251bb5e3b2fSeh146360 	*len = total;
252bb5e3b2fSeh146360 	ipw2100_imem_getbuf(sc, addr, buf, total);
253bb5e3b2fSeh146360 
254bb5e3b2fSeh146360 	return (DDI_SUCCESS);
255bb5e3b2fSeh146360 }
256bb5e3b2fSeh146360 
257bb5e3b2fSeh146360 uint16_t
ipw2100_rom_get16(struct ipw2100_softc * sc,uint8_t addr)258bb5e3b2fSeh146360 ipw2100_rom_get16(struct ipw2100_softc *sc, uint8_t addr)
259bb5e3b2fSeh146360 {
260bb5e3b2fSeh146360 	uint32_t	tmp;
261bb5e3b2fSeh146360 	uint16_t	val;
262bb5e3b2fSeh146360 	int		n;
263bb5e3b2fSeh146360 
264bb5e3b2fSeh146360 	/*
265bb5e3b2fSeh146360 	 * According to i2c bus protocol to set them.
266bb5e3b2fSeh146360 	 */
267bb5e3b2fSeh146360 	/* clock */
268bb5e3b2fSeh146360 	ipw2100_rom_control(sc, 0);
269bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
270bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_C);
271bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
272bb5e3b2fSeh146360 	/* start bit */
273bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_D);
274bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S
275bb5e3b2fSeh146360 	    | IPW2100_EEPROM_D | IPW2100_EEPROM_C);
276bb5e3b2fSeh146360 	/* read opcode */
277bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_D);
278bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S
279bb5e3b2fSeh146360 	    | IPW2100_EEPROM_D | IPW2100_EEPROM_C);
280bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
281bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_C);
282bb5e3b2fSeh146360 	/*
283bb5e3b2fSeh146360 	 * address, totally 8 bits, defined by hardware, push from MSB to LSB
284bb5e3b2fSeh146360 	 */
285bb5e3b2fSeh146360 	for (n = 7; n >= 0; n--) {
286bb5e3b2fSeh146360 		ipw2100_rom_control(sc, IPW2100_EEPROM_S
287bb5e3b2fSeh146360 		    |(((addr >> n) & 1) << IPW2100_EEPROM_SHIFT_D));
288bb5e3b2fSeh146360 		ipw2100_rom_control(sc, IPW2100_EEPROM_S
289bb5e3b2fSeh146360 		    |(((addr >> n) & 1) << IPW2100_EEPROM_SHIFT_D)
290bb5e3b2fSeh146360 		    | IPW2100_EEPROM_C);
291bb5e3b2fSeh146360 	}
292bb5e3b2fSeh146360 
293bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
294bb5e3b2fSeh146360 
295bb5e3b2fSeh146360 	/*
296bb5e3b2fSeh146360 	 * data, totally 16 bits, defined by hardware, push from MSB to LSB
297bb5e3b2fSeh146360 	 */
298bb5e3b2fSeh146360 	val = 0;
299bb5e3b2fSeh146360 	for (n = 15; n >= 0; n--) {
300bb5e3b2fSeh146360 		ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_C);
301bb5e3b2fSeh146360 		ipw2100_rom_control(sc, IPW2100_EEPROM_S);
302bb5e3b2fSeh146360 		tmp = ipw2100_imem_get32(sc, IPW2100_IMEM_EEPROM_CTL);
303bb5e3b2fSeh146360 		val |= ((tmp & IPW2100_EEPROM_Q)
304bb5e3b2fSeh146360 		    >> IPW2100_EEPROM_SHIFT_Q) << n;
305bb5e3b2fSeh146360 	}
306bb5e3b2fSeh146360 
307bb5e3b2fSeh146360 	ipw2100_rom_control(sc, 0);
308bb5e3b2fSeh146360 
309bb5e3b2fSeh146360 	/* clear chip select and clock */
310bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
311bb5e3b2fSeh146360 	ipw2100_rom_control(sc, 0);
312bb5e3b2fSeh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_C);
313bb5e3b2fSeh146360 
314bb5e3b2fSeh146360 	return (LE_16(val));
315bb5e3b2fSeh146360 }
316bb5e3b2fSeh146360 
317bb5e3b2fSeh146360 
318bb5e3b2fSeh146360 /*
319bb5e3b2fSeh146360  * Firmware related operations
320bb5e3b2fSeh146360  */
321bb5e3b2fSeh146360 #define	IPW2100_FW_MAJOR_VERSION (1)
322bb5e3b2fSeh146360 #define	IPW2100_FW_MINOR_VERSION (3)
323bb5e3b2fSeh146360 
324bb5e3b2fSeh146360 #define	IPW2100_FW_MAJOR(x)((x) & 0xff)
325bb5e3b2fSeh146360 #define	IPW2100_FW_MINOR(x)(((x) & 0xff) >> 8)
326bb5e3b2fSeh146360 
327bb5e3b2fSeh146360 /*
328bb5e3b2fSeh146360  * The firware was issued by Intel as binary which need to be loaded
329bb5e3b2fSeh146360  * to hardware when card is initiated, or when fatal error happened,
330bb5e3b2fSeh146360  * or when the chip need be reset.
331bb5e3b2fSeh146360  */
332bb5e3b2fSeh146360 static uint8_t ipw2100_firmware_bin [] = {
333bb5e3b2fSeh146360 #include "fw-ipw2100/ipw2100-1.3.fw.hex"
334bb5e3b2fSeh146360 };
335bb5e3b2fSeh146360 
336bb5e3b2fSeh146360 int
ipw2100_cache_firmware(struct ipw2100_softc * sc)337bb5e3b2fSeh146360 ipw2100_cache_firmware(struct ipw2100_softc *sc)
338bb5e3b2fSeh146360 {
339bb5e3b2fSeh146360 	uint8_t				*bin = ipw2100_firmware_bin;
340bb5e3b2fSeh146360 	struct ipw2100_firmware_hdr	*h = (struct ipw2100_firmware_hdr *)bin;
341bb5e3b2fSeh146360 
342bb5e3b2fSeh146360 	IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
343bb5e3b2fSeh146360 	    "ipw2100_cache_firmwares(): enter\n"));
344bb5e3b2fSeh146360 
345bb5e3b2fSeh146360 	sc->sc_fw.bin_base  = bin;
346bb5e3b2fSeh146360 	sc->sc_fw.bin_size  = sizeof (ipw2100_firmware_bin);
347bb5e3b2fSeh146360 
348bb5e3b2fSeh146360 	if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
349bb5e3b2fSeh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
350bb5e3b2fSeh146360 		    "ipw2100_cache_firmware(): image not compatible, %u\n",
351bb5e3b2fSeh146360 		    h->version));
352bb5e3b2fSeh146360 		return (DDI_FAILURE);
353bb5e3b2fSeh146360 	}
354bb5e3b2fSeh146360 
355bb5e3b2fSeh146360 	sc->sc_fw.fw_base = bin + sizeof (struct ipw2100_firmware_hdr);
356bb5e3b2fSeh146360 	sc->sc_fw.fw_size = LE_32(h->fw_size);
357bb5e3b2fSeh146360 	sc->sc_fw.uc_base = sc->sc_fw.fw_base + sc->sc_fw.fw_size;
358bb5e3b2fSeh146360 	sc->sc_fw.uc_size = LE_32(h->uc_size);
359bb5e3b2fSeh146360 
360bb5e3b2fSeh146360 	sc->sc_flags |= IPW2100_FLAG_FW_CACHED;
361bb5e3b2fSeh146360 
362bb5e3b2fSeh146360 	IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
363bb5e3b2fSeh146360 	    "ipw2100_cache_firmware(): exit\n"));
364bb5e3b2fSeh146360 
365bb5e3b2fSeh146360 	return (DDI_SUCCESS);
366bb5e3b2fSeh146360 }
367bb5e3b2fSeh146360 
368bb5e3b2fSeh146360 /*
369bb5e3b2fSeh146360  * If user-land firmware loading is supported, this routine
370bb5e3b2fSeh146360  * free kmemory if sc->sc_fw.bin_base & sc->sc_fw.bin_size are
371bb5e3b2fSeh146360  * not empty.
372bb5e3b2fSeh146360  */
373bb5e3b2fSeh146360 int
ipw2100_free_firmware(struct ipw2100_softc * sc)374bb5e3b2fSeh146360 ipw2100_free_firmware(struct ipw2100_softc *sc)
375bb5e3b2fSeh146360 {
376bb5e3b2fSeh146360 	sc->sc_flags &= ~IPW2100_FLAG_FW_CACHED;
377bb5e3b2fSeh146360 
378bb5e3b2fSeh146360 	return (DDI_SUCCESS);
379bb5e3b2fSeh146360 }
380bb5e3b2fSeh146360 
381bb5e3b2fSeh146360 /*
382bb5e3b2fSeh146360  * the following routines load code onto ipw2100 hardware
383bb5e3b2fSeh146360  */
384bb5e3b2fSeh146360 int
ipw2100_load_uc(struct ipw2100_softc * sc)385bb5e3b2fSeh146360 ipw2100_load_uc(struct ipw2100_softc *sc)
386bb5e3b2fSeh146360 {
387bb5e3b2fSeh146360 	int	ntries;
388bb5e3b2fSeh146360 
389bb5e3b2fSeh146360 	ipw2100_imem_put32(sc, 0x3000e0, 0x80000000);
390bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, 0);
391bb5e3b2fSeh146360 
392bb5e3b2fSeh146360 	ipw2100_imem_put16(sc, 0x220000, 0x0703);
393bb5e3b2fSeh146360 	ipw2100_imem_put16(sc, 0x220000, 0x0707);
394bb5e3b2fSeh146360 
395bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210014, 0x72);
396bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210014, 0x72);
397bb5e3b2fSeh146360 
398bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210000, 0x40);
399bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210000, 0x00);
400bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210000, 0x40);
401bb5e3b2fSeh146360 
402bb5e3b2fSeh146360 	ipw2100_imem_rep_put8(sc, 0x210010,
403bb5e3b2fSeh146360 	    sc->sc_fw.uc_base, sc->sc_fw.uc_size);
404bb5e3b2fSeh146360 
405bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210000, 0x00);
406bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210000, 0x00);
407bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210000, 0x80);
408bb5e3b2fSeh146360 
409bb5e3b2fSeh146360 	ipw2100_imem_put16(sc, 0x220000, 0x0703);
410bb5e3b2fSeh146360 	ipw2100_imem_put16(sc, 0x220000, 0x0707);
411bb5e3b2fSeh146360 
412bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210014, 0x72);
413bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210014, 0x72);
414bb5e3b2fSeh146360 
415bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210000, 0x00);
416bb5e3b2fSeh146360 	ipw2100_imem_put8(sc, 0x210000, 0x80);
417bb5e3b2fSeh146360 
418bb5e3b2fSeh146360 	/* try many times */
419bb5e3b2fSeh146360 	for (ntries = 0; ntries < 5000; ntries++) {
420bb5e3b2fSeh146360 		if (ipw2100_imem_get8(sc, 0x210000) & 1)
421bb5e3b2fSeh146360 			break;
422bb5e3b2fSeh146360 		drv_usecwait(1000); /* wait for a while */
423bb5e3b2fSeh146360 	}
424bb5e3b2fSeh146360 	if (ntries == 5000)
425bb5e3b2fSeh146360 		return (DDI_FAILURE);
426bb5e3b2fSeh146360 
427bb5e3b2fSeh146360 	ipw2100_imem_put32(sc, 0x3000e0, 0);
428bb5e3b2fSeh146360 
429bb5e3b2fSeh146360 	return (DDI_SUCCESS);
430bb5e3b2fSeh146360 }
431bb5e3b2fSeh146360 
432bb5e3b2fSeh146360 int
ipw2100_load_fw(struct ipw2100_softc * sc)433bb5e3b2fSeh146360 ipw2100_load_fw(struct ipw2100_softc *sc)
434bb5e3b2fSeh146360 {
435bb5e3b2fSeh146360 	uint8_t		*p, *e;
436bb5e3b2fSeh146360 	uint32_t	dst;
437bb5e3b2fSeh146360 	uint16_t	len;
438bb5e3b2fSeh146360 	clock_t		clk;
439bb5e3b2fSeh146360 
440bb5e3b2fSeh146360 	IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
441bb5e3b2fSeh146360 	    "ipw2100_load_fw(): enter\n"));
442bb5e3b2fSeh146360 
443bb5e3b2fSeh146360 	p = sc->sc_fw.fw_base;
444bb5e3b2fSeh146360 	e = sc->sc_fw.fw_base + sc->sc_fw.fw_size;
445bb5e3b2fSeh146360 	while (p < e) {
446bb5e3b2fSeh146360 		/*
447bb5e3b2fSeh146360 		 * each block is organized as <DST,LEN,DATA>
448bb5e3b2fSeh146360 		 */
449bb5e3b2fSeh146360 		if ((p + sizeof (dst) + sizeof (len)) > e) {
450bb5e3b2fSeh146360 			IPW2100_WARN((sc->sc_dip, CE_CONT,
451bb5e3b2fSeh146360 			    "ipw2100_load_fw(): invalid firmware image\n"));
452bb5e3b2fSeh146360 			return (DDI_FAILURE);
453bb5e3b2fSeh146360 		}
454922d2c76Seh146360 		dst = LE_32(*((uint32_t *)(uintptr_t)p)); p += sizeof (dst);
455922d2c76Seh146360 		len = LE_16(*((uint16_t *)(uintptr_t)p)); p += sizeof (len);
456bb5e3b2fSeh146360 		if ((p + len) > e) {
457bb5e3b2fSeh146360 			IPW2100_WARN((sc->sc_dip, CE_CONT,
458bb5e3b2fSeh146360 			    "ipw2100_load_fw(): invalid firmware image\n"));
459bb5e3b2fSeh146360 			return (DDI_FAILURE);
460bb5e3b2fSeh146360 		}
461bb5e3b2fSeh146360 
462bb5e3b2fSeh146360 		ipw2100_imem_putbuf(sc, dst, p, len);
463bb5e3b2fSeh146360 		p += len;
464bb5e3b2fSeh146360 	}
465bb5e3b2fSeh146360 
466bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_IO,
467bb5e3b2fSeh146360 	    IPW2100_IO_GPIO1_ENABLE | IPW2100_IO_GPIO3_MASK |
468bb5e3b2fSeh146360 	    IPW2100_IO_LED_OFF);
469bb5e3b2fSeh146360 
470bb5e3b2fSeh146360 	mutex_enter(&sc->sc_ilock);
471bb5e3b2fSeh146360 
472bb5e3b2fSeh146360 	/*
473bb5e3b2fSeh146360 	 * enable all interrupts
474bb5e3b2fSeh146360 	 */
475bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL);
476bb5e3b2fSeh146360 
477bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, 0);
478bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_CTL,
479bb5e3b2fSeh146360 	    ipw2100_csr_get32(sc, IPW2100_CSR_CTL) | IPW2100_CTL_ALLOW_STANDBY);
480bb5e3b2fSeh146360 
481bb5e3b2fSeh146360 	/*
482bb5e3b2fSeh146360 	 * wait for interrupt to notify fw initialization is done
483bb5e3b2fSeh146360 	 */
484*d3d50737SRafael Vanoni 	clk = drv_usectohz(5000000);  /* 5 second */
485bb5e3b2fSeh146360 	while (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) {
486bb5e3b2fSeh146360 		/*
487bb5e3b2fSeh146360 		 * wait longer for the fw  initialized
488bb5e3b2fSeh146360 		 */
489*d3d50737SRafael Vanoni 		if (cv_reltimedwait(&sc->sc_fw_cond, &sc->sc_ilock, clk,
490*d3d50737SRafael Vanoni 		    TR_CLOCK_TICK) < 0)
491bb5e3b2fSeh146360 			break;
492bb5e3b2fSeh146360 	}
493bb5e3b2fSeh146360 	mutex_exit(&sc->sc_ilock);
494bb5e3b2fSeh146360 
495bb5e3b2fSeh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_IO,
496bb5e3b2fSeh146360 	    ipw2100_csr_get32(sc, IPW2100_CSR_IO) |
497bb5e3b2fSeh146360 	    IPW2100_IO_GPIO1_MASK | IPW2100_IO_GPIO3_MASK);
498bb5e3b2fSeh146360 
499bb5e3b2fSeh146360 	if (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) {
500bb5e3b2fSeh146360 		IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
501bb5e3b2fSeh146360 		    "ipw2100_load_fw(): exit, init failed\n"));
502bb5e3b2fSeh146360 		return (DDI_FAILURE);
503bb5e3b2fSeh146360 	}
504bb5e3b2fSeh146360 
505bb5e3b2fSeh146360 	IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
506bb5e3b2fSeh146360 	    "ipw2100_load_fw(): exit\n"));
507bb5e3b2fSeh146360 	return (DDI_SUCCESS);
508bb5e3b2fSeh146360 }
509