xref: /onnv-gate/usr/src/uts/common/io/ipw/ipw2100_hw.c (revision 11066:cebb50cbe4f9)
13847Seh146360 /*
2*11066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
33847Seh146360  * Use is subject to license terms.
43847Seh146360  */
53847Seh146360 
63847Seh146360 /*
73847Seh146360  * Copyright (c) 2004
83847Seh146360  *	Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
93847Seh146360  *
103847Seh146360  * Redistribution and use in source and binary forms, with or without
113847Seh146360  * modification, are permitted provided that the following conditions
123847Seh146360  * are met:
133847Seh146360  * 1. Redistributions of source code must retain the above copyright
143847Seh146360  *    notice unmodified, this list of conditions, and the following
153847Seh146360  *    disclaimer.
163847Seh146360  * 2. Redistributions in binary form must reproduce the above copyright
173847Seh146360  *    notice, this list of conditions and the following disclaimer in the
183847Seh146360  *    documentation and/or other materials provided with the distribution.
193847Seh146360  *
203847Seh146360  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
213847Seh146360  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
223847Seh146360  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
233847Seh146360  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
243847Seh146360  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
253847Seh146360  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
263847Seh146360  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
273847Seh146360  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
283847Seh146360  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
293847Seh146360  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
303847Seh146360  * SUCH DAMAGE.
313847Seh146360  */
323847Seh146360 
333847Seh146360 /*
343847Seh146360  * Intel Wireless PRO/2100 mini-PCI adapter driver
353847Seh146360  * ipw2100_hw.c is used to handle hardware operation and firmware operations.
363847Seh146360  */
373847Seh146360 #include <sys/types.h>
383847Seh146360 #include <sys/byteorder.h>
393847Seh146360 #include <sys/ddi.h>
403847Seh146360 #include <sys/sunddi.h>
413847Seh146360 #include <sys/note.h>
423847Seh146360 #include <sys/stream.h>
433847Seh146360 #include <sys/strsun.h>
443847Seh146360 
453847Seh146360 #include "ipw2100.h"
463847Seh146360 #include "ipw2100_impl.h"
473847Seh146360 
483847Seh146360 /*
493847Seh146360  * Hardware related operations
503847Seh146360  */
513847Seh146360 #define	IPW2100_EEPROM_SHIFT_D	(2)
523847Seh146360 #define	IPW2100_EEPROM_SHIFT_Q	(4)
533847Seh146360 
543847Seh146360 #define	IPW2100_EEPROM_C	(1 << 0)
553847Seh146360 #define	IPW2100_EEPROM_S	(1 << 1)
563847Seh146360 #define	IPW2100_EEPROM_D	(1 << IPW2100_EEPROM_SHIFT_D)
573847Seh146360 #define	IPW2100_EEPROM_Q	(1 << IPW2100_EEPROM_SHIFT_Q)
583847Seh146360 
593847Seh146360 uint8_t
ipw2100_csr_get8(struct ipw2100_softc * sc,uint32_t off)603847Seh146360 ipw2100_csr_get8(struct ipw2100_softc *sc, uint32_t off)
613847Seh146360 {
623847Seh146360 	return (ddi_get8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off)));
633847Seh146360 }
643847Seh146360 
653847Seh146360 uint16_t
ipw2100_csr_get16(struct ipw2100_softc * sc,uint32_t off)663847Seh146360 ipw2100_csr_get16(struct ipw2100_softc *sc, uint32_t off)
673847Seh146360 {
687194Seh146360 	return (ddi_get16(sc->sc_ioh,
697194Seh146360 	    (uint16_t *)((uintptr_t)sc->sc_regs + off)));
703847Seh146360 }
713847Seh146360 
723847Seh146360 uint32_t
ipw2100_csr_get32(struct ipw2100_softc * sc,uint32_t off)733847Seh146360 ipw2100_csr_get32(struct ipw2100_softc *sc, uint32_t off)
743847Seh146360 {
757194Seh146360 	return (ddi_get32(sc->sc_ioh,
767194Seh146360 	    (uint32_t *)((uintptr_t)sc->sc_regs + off)));
773847Seh146360 }
783847Seh146360 
793847Seh146360 void
ipw2100_csr_rep_get16(struct ipw2100_softc * sc,uint32_t off,uint16_t * buf,size_t cnt)803847Seh146360 ipw2100_csr_rep_get16(struct ipw2100_softc *sc,
813847Seh146360 	uint32_t off, uint16_t *buf, size_t cnt)
823847Seh146360 {
837194Seh146360 	ddi_rep_get16(sc->sc_ioh, buf,
847194Seh146360 	    (uint16_t *)((uintptr_t)sc->sc_regs + off),
857194Seh146360 	    cnt, DDI_DEV_NO_AUTOINCR);
863847Seh146360 }
873847Seh146360 
883847Seh146360 void
ipw2100_csr_put8(struct ipw2100_softc * sc,uint32_t off,uint8_t val)893847Seh146360 ipw2100_csr_put8(struct ipw2100_softc *sc, uint32_t off, uint8_t val)
903847Seh146360 {
913847Seh146360 	ddi_put8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off), val);
923847Seh146360 }
933847Seh146360 
943847Seh146360 void
ipw2100_csr_put16(struct ipw2100_softc * sc,uint32_t off,uint16_t val)953847Seh146360 ipw2100_csr_put16(struct ipw2100_softc *sc, uint32_t off, uint16_t val)
963847Seh146360 {
977194Seh146360 	ddi_put16(sc->sc_ioh,
987194Seh146360 	    (uint16_t *)((uintptr_t)sc->sc_regs + off), val);
993847Seh146360 }
1003847Seh146360 
1013847Seh146360 void
ipw2100_csr_put32(struct ipw2100_softc * sc,uint32_t off,uint32_t val)1023847Seh146360 ipw2100_csr_put32(struct ipw2100_softc *sc, uint32_t off, uint32_t val)
1033847Seh146360 {
1047194Seh146360 	ddi_put32(sc->sc_ioh,
1057194Seh146360 	    (uint32_t *)((uintptr_t)sc->sc_regs + off), val);
1063847Seh146360 }
1073847Seh146360 
1083847Seh146360 void
ipw2100_csr_rep_put8(struct ipw2100_softc * sc,uint32_t off,uint8_t * buf,size_t cnt)1093847Seh146360 ipw2100_csr_rep_put8(struct ipw2100_softc *sc,
1103847Seh146360 	uint32_t off, uint8_t *buf, size_t cnt)
1113847Seh146360 {
1123847Seh146360 	ddi_rep_put8(sc->sc_ioh, buf, (uint8_t *)(sc->sc_regs + off),
1137194Seh146360 	    cnt, DDI_DEV_NO_AUTOINCR);
1143847Seh146360 }
1153847Seh146360 
1163847Seh146360 uint8_t
ipw2100_imem_get8(struct ipw2100_softc * sc,int32_t addr)1173847Seh146360 ipw2100_imem_get8(struct ipw2100_softc *sc, int32_t addr)
1183847Seh146360 {
1193847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
1203847Seh146360 
1213847Seh146360 	return (ipw2100_csr_get8(sc, IPW2100_CSR_INDIRECT_DATA));
1223847Seh146360 }
1233847Seh146360 
1243847Seh146360 uint16_t
ipw2100_imem_get16(struct ipw2100_softc * sc,uint32_t addr)1253847Seh146360 ipw2100_imem_get16(struct ipw2100_softc *sc, uint32_t addr)
1263847Seh146360 {
1273847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
1283847Seh146360 
1293847Seh146360 	return (ipw2100_csr_get16(sc, IPW2100_CSR_INDIRECT_DATA));
1303847Seh146360 }
1313847Seh146360 
1323847Seh146360 uint32_t
ipw2100_imem_get32(struct ipw2100_softc * sc,uint32_t addr)1333847Seh146360 ipw2100_imem_get32(struct ipw2100_softc *sc, uint32_t addr)
1343847Seh146360 {
1353847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
1363847Seh146360 
1373847Seh146360 	return (ipw2100_csr_get32(sc, IPW2100_CSR_INDIRECT_DATA));
1383847Seh146360 }
1393847Seh146360 
1403847Seh146360 void
ipw2100_imem_rep_get16(struct ipw2100_softc * sc,uint32_t addr,uint16_t * buf,size_t cnt)1413847Seh146360 ipw2100_imem_rep_get16(struct ipw2100_softc *sc,
1423847Seh146360 	uint32_t addr, uint16_t *buf, size_t cnt)
1433847Seh146360 {
1443847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
1453847Seh146360 	ipw2100_csr_rep_get16(sc, IPW2100_CSR_INDIRECT_DATA, buf, cnt);
1463847Seh146360 }
1473847Seh146360 
1483847Seh146360 void
ipw2100_imem_put8(struct ipw2100_softc * sc,uint32_t addr,uint8_t val)1493847Seh146360 ipw2100_imem_put8(struct ipw2100_softc *sc, uint32_t addr, uint8_t val)
1503847Seh146360 {
1513847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
1523847Seh146360 	ipw2100_csr_put8(sc, IPW2100_CSR_INDIRECT_DATA, val);
1533847Seh146360 }
1543847Seh146360 
1553847Seh146360 void
ipw2100_imem_put16(struct ipw2100_softc * sc,uint32_t addr,uint16_t val)1563847Seh146360 ipw2100_imem_put16(struct ipw2100_softc *sc, uint32_t addr, uint16_t val)
1573847Seh146360 {
1583847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
1593847Seh146360 	ipw2100_csr_put16(sc, IPW2100_CSR_INDIRECT_DATA, val);
1603847Seh146360 }
1613847Seh146360 
1623847Seh146360 void
ipw2100_imem_put32(struct ipw2100_softc * sc,uint32_t addr,uint32_t val)1633847Seh146360 ipw2100_imem_put32(struct ipw2100_softc *sc, uint32_t addr, uint32_t val)
1643847Seh146360 {
1653847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
1663847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_DATA, val);
1673847Seh146360 }
1683847Seh146360 
1693847Seh146360 void
ipw2100_imem_rep_put8(struct ipw2100_softc * sc,uint32_t addr,uint8_t * buf,size_t cnt)1703847Seh146360 ipw2100_imem_rep_put8(struct ipw2100_softc *sc,
1713847Seh146360 	uint32_t addr, uint8_t *buf, size_t cnt)
1723847Seh146360 {
1733847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr);
1743847Seh146360 	ipw2100_csr_rep_put8(sc, IPW2100_CSR_INDIRECT_DATA, buf, cnt);
1753847Seh146360 }
1763847Seh146360 
1773847Seh146360 void
ipw2100_imem_getbuf(struct ipw2100_softc * sc,uint32_t addr,uint8_t * buf,size_t cnt)1783847Seh146360 ipw2100_imem_getbuf(struct ipw2100_softc *sc,
1793847Seh146360 	uint32_t addr, uint8_t *buf, size_t cnt)
1803847Seh146360 {
1813847Seh146360 	for (; cnt > 0; addr++, buf++, cnt--) {
1823847Seh146360 		ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr & ~3);
1833847Seh146360 		*buf = ipw2100_csr_get8(sc,
1847194Seh146360 		    IPW2100_CSR_INDIRECT_DATA +(addr & 3));
1853847Seh146360 	}
1863847Seh146360 }
1873847Seh146360 
1883847Seh146360 void
ipw2100_imem_putbuf(struct ipw2100_softc * sc,uint32_t addr,uint8_t * buf,size_t cnt)1893847Seh146360 ipw2100_imem_putbuf(struct ipw2100_softc *sc,
1903847Seh146360 	uint32_t addr, uint8_t *buf, size_t cnt)
1913847Seh146360 {
1923847Seh146360 	for (; cnt > 0; addr++, buf++, cnt--) {
1933847Seh146360 		ipw2100_csr_put32(sc, IPW2100_CSR_INDIRECT_ADDR, addr & ~3);
1943847Seh146360 		ipw2100_csr_put8(sc,
1957194Seh146360 		    IPW2100_CSR_INDIRECT_DATA +(addr & 3), *buf);
1963847Seh146360 	}
1973847Seh146360 }
1983847Seh146360 
1993847Seh146360 void
ipw2100_rom_control(struct ipw2100_softc * sc,uint32_t val)2003847Seh146360 ipw2100_rom_control(struct ipw2100_softc *sc, uint32_t val)
2013847Seh146360 {
2023847Seh146360 	ipw2100_imem_put32(sc, IPW2100_IMEM_EEPROM_CTL, val);
2033847Seh146360 	drv_usecwait(IPW2100_EEPROM_DELAY);
2043847Seh146360 }
2053847Seh146360 
2063847Seh146360 
2073847Seh146360 uint8_t
ipw2100_table1_get8(struct ipw2100_softc * sc,uint32_t off)2083847Seh146360 ipw2100_table1_get8(struct ipw2100_softc *sc, uint32_t off)
2093847Seh146360 {
2103847Seh146360 	uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off);
2113847Seh146360 	return (ipw2100_imem_get8(sc, addr));
2123847Seh146360 }
2133847Seh146360 
2143847Seh146360 uint32_t
ipw2100_table1_get32(struct ipw2100_softc * sc,uint32_t off)2153847Seh146360 ipw2100_table1_get32(struct ipw2100_softc *sc, uint32_t off)
2163847Seh146360 {
2173847Seh146360 	uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off);
2183847Seh146360 	return (ipw2100_imem_get32(sc, addr));
2193847Seh146360 }
2203847Seh146360 
2213847Seh146360 void
ipw2100_table1_put32(struct ipw2100_softc * sc,uint32_t off,uint32_t val)2223847Seh146360 ipw2100_table1_put32(struct ipw2100_softc *sc, uint32_t off, uint32_t val)
2233847Seh146360 {
2243847Seh146360 	uint32_t addr = ipw2100_imem_get32(sc, sc->sc_table1_base + off);
2253847Seh146360 	ipw2100_imem_put32(sc, addr, val);
2263847Seh146360 }
2273847Seh146360 
2283847Seh146360 int
ipw2100_table2_getbuf(struct ipw2100_softc * sc,uint32_t off,uint8_t * buf,uint32_t * len)2293847Seh146360 ipw2100_table2_getbuf(struct ipw2100_softc *sc,
2303847Seh146360 	uint32_t off, uint8_t *buf, uint32_t *len)
2313847Seh146360 {
2323847Seh146360 	uint32_t	addr, info;
2333847Seh146360 	uint16_t	cnt, size;
2343847Seh146360 	uint32_t	total;
2353847Seh146360 
2363847Seh146360 	addr = ipw2100_imem_get32(sc, sc->sc_table2_base + off);
2373847Seh146360 	info = ipw2100_imem_get32(sc,
2383847Seh146360 	    sc->sc_table2_base + off + sizeof (uint32_t));
2393847Seh146360 
2403847Seh146360 	cnt = info >> 16;
2413847Seh146360 	size = info & 0xffff;
2423847Seh146360 	total = cnt * size;
2433847Seh146360 
2443847Seh146360 	if (total > *len) {
2453847Seh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
2463847Seh146360 		    "ipw2100_table2_getbuf(): invalid table offset = 0x%08x\n",
2473847Seh146360 		    off));
2483847Seh146360 		return (DDI_FAILURE);
2493847Seh146360 	}
2503847Seh146360 
2513847Seh146360 	*len = total;
2523847Seh146360 	ipw2100_imem_getbuf(sc, addr, buf, total);
2533847Seh146360 
2543847Seh146360 	return (DDI_SUCCESS);
2553847Seh146360 }
2563847Seh146360 
2573847Seh146360 uint16_t
ipw2100_rom_get16(struct ipw2100_softc * sc,uint8_t addr)2583847Seh146360 ipw2100_rom_get16(struct ipw2100_softc *sc, uint8_t addr)
2593847Seh146360 {
2603847Seh146360 	uint32_t	tmp;
2613847Seh146360 	uint16_t	val;
2623847Seh146360 	int		n;
2633847Seh146360 
2643847Seh146360 	/*
2653847Seh146360 	 * According to i2c bus protocol to set them.
2663847Seh146360 	 */
2673847Seh146360 	/* clock */
2683847Seh146360 	ipw2100_rom_control(sc, 0);
2693847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
2703847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_C);
2713847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
2723847Seh146360 	/* start bit */
2733847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_D);
2743847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S
2753847Seh146360 	    | IPW2100_EEPROM_D | IPW2100_EEPROM_C);
2763847Seh146360 	/* read opcode */
2773847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_D);
2783847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S
2793847Seh146360 	    | IPW2100_EEPROM_D | IPW2100_EEPROM_C);
2803847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
2813847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_C);
2823847Seh146360 	/*
2833847Seh146360 	 * address, totally 8 bits, defined by hardware, push from MSB to LSB
2843847Seh146360 	 */
2853847Seh146360 	for (n = 7; n >= 0; n--) {
2863847Seh146360 		ipw2100_rom_control(sc, IPW2100_EEPROM_S
2873847Seh146360 		    |(((addr >> n) & 1) << IPW2100_EEPROM_SHIFT_D));
2883847Seh146360 		ipw2100_rom_control(sc, IPW2100_EEPROM_S
2893847Seh146360 		    |(((addr >> n) & 1) << IPW2100_EEPROM_SHIFT_D)
2903847Seh146360 		    | IPW2100_EEPROM_C);
2913847Seh146360 	}
2923847Seh146360 
2933847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
2943847Seh146360 
2953847Seh146360 	/*
2963847Seh146360 	 * data, totally 16 bits, defined by hardware, push from MSB to LSB
2973847Seh146360 	 */
2983847Seh146360 	val = 0;
2993847Seh146360 	for (n = 15; n >= 0; n--) {
3003847Seh146360 		ipw2100_rom_control(sc, IPW2100_EEPROM_S | IPW2100_EEPROM_C);
3013847Seh146360 		ipw2100_rom_control(sc, IPW2100_EEPROM_S);
3023847Seh146360 		tmp = ipw2100_imem_get32(sc, IPW2100_IMEM_EEPROM_CTL);
3033847Seh146360 		val |= ((tmp & IPW2100_EEPROM_Q)
3043847Seh146360 		    >> IPW2100_EEPROM_SHIFT_Q) << n;
3053847Seh146360 	}
3063847Seh146360 
3073847Seh146360 	ipw2100_rom_control(sc, 0);
3083847Seh146360 
3093847Seh146360 	/* clear chip select and clock */
3103847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_S);
3113847Seh146360 	ipw2100_rom_control(sc, 0);
3123847Seh146360 	ipw2100_rom_control(sc, IPW2100_EEPROM_C);
3133847Seh146360 
3143847Seh146360 	return (LE_16(val));
3153847Seh146360 }
3163847Seh146360 
3173847Seh146360 
3183847Seh146360 /*
3193847Seh146360  * Firmware related operations
3203847Seh146360  */
3213847Seh146360 #define	IPW2100_FW_MAJOR_VERSION (1)
3223847Seh146360 #define	IPW2100_FW_MINOR_VERSION (3)
3233847Seh146360 
3243847Seh146360 #define	IPW2100_FW_MAJOR(x)((x) & 0xff)
3253847Seh146360 #define	IPW2100_FW_MINOR(x)(((x) & 0xff) >> 8)
3263847Seh146360 
3273847Seh146360 /*
3283847Seh146360  * The firware was issued by Intel as binary which need to be loaded
3293847Seh146360  * to hardware when card is initiated, or when fatal error happened,
3303847Seh146360  * or when the chip need be reset.
3313847Seh146360  */
3323847Seh146360 static uint8_t ipw2100_firmware_bin [] = {
3333847Seh146360 #include "fw-ipw2100/ipw2100-1.3.fw.hex"
3343847Seh146360 };
3353847Seh146360 
3363847Seh146360 int
ipw2100_cache_firmware(struct ipw2100_softc * sc)3373847Seh146360 ipw2100_cache_firmware(struct ipw2100_softc *sc)
3383847Seh146360 {
3393847Seh146360 	uint8_t				*bin = ipw2100_firmware_bin;
3403847Seh146360 	struct ipw2100_firmware_hdr	*h = (struct ipw2100_firmware_hdr *)bin;
3413847Seh146360 
3423847Seh146360 	IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
3433847Seh146360 	    "ipw2100_cache_firmwares(): enter\n"));
3443847Seh146360 
3453847Seh146360 	sc->sc_fw.bin_base  = bin;
3463847Seh146360 	sc->sc_fw.bin_size  = sizeof (ipw2100_firmware_bin);
3473847Seh146360 
3483847Seh146360 	if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
3493847Seh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
3503847Seh146360 		    "ipw2100_cache_firmware(): image not compatible, %u\n",
3513847Seh146360 		    h->version));
3523847Seh146360 		return (DDI_FAILURE);
3533847Seh146360 	}
3543847Seh146360 
3553847Seh146360 	sc->sc_fw.fw_base = bin + sizeof (struct ipw2100_firmware_hdr);
3563847Seh146360 	sc->sc_fw.fw_size = LE_32(h->fw_size);
3573847Seh146360 	sc->sc_fw.uc_base = sc->sc_fw.fw_base + sc->sc_fw.fw_size;
3583847Seh146360 	sc->sc_fw.uc_size = LE_32(h->uc_size);
3593847Seh146360 
3603847Seh146360 	sc->sc_flags |= IPW2100_FLAG_FW_CACHED;
3613847Seh146360 
3623847Seh146360 	IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
3633847Seh146360 	    "ipw2100_cache_firmware(): exit\n"));
3643847Seh146360 
3653847Seh146360 	return (DDI_SUCCESS);
3663847Seh146360 }
3673847Seh146360 
3683847Seh146360 /*
3693847Seh146360  * If user-land firmware loading is supported, this routine
3703847Seh146360  * free kmemory if sc->sc_fw.bin_base & sc->sc_fw.bin_size are
3713847Seh146360  * not empty.
3723847Seh146360  */
3733847Seh146360 int
ipw2100_free_firmware(struct ipw2100_softc * sc)3743847Seh146360 ipw2100_free_firmware(struct ipw2100_softc *sc)
3753847Seh146360 {
3763847Seh146360 	sc->sc_flags &= ~IPW2100_FLAG_FW_CACHED;
3773847Seh146360 
3783847Seh146360 	return (DDI_SUCCESS);
3793847Seh146360 }
3803847Seh146360 
3813847Seh146360 /*
3823847Seh146360  * the following routines load code onto ipw2100 hardware
3833847Seh146360  */
3843847Seh146360 int
ipw2100_load_uc(struct ipw2100_softc * sc)3853847Seh146360 ipw2100_load_uc(struct ipw2100_softc *sc)
3863847Seh146360 {
3873847Seh146360 	int	ntries;
3883847Seh146360 
3893847Seh146360 	ipw2100_imem_put32(sc, 0x3000e0, 0x80000000);
3903847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, 0);
3913847Seh146360 
3923847Seh146360 	ipw2100_imem_put16(sc, 0x220000, 0x0703);
3933847Seh146360 	ipw2100_imem_put16(sc, 0x220000, 0x0707);
3943847Seh146360 
3953847Seh146360 	ipw2100_imem_put8(sc, 0x210014, 0x72);
3963847Seh146360 	ipw2100_imem_put8(sc, 0x210014, 0x72);
3973847Seh146360 
3983847Seh146360 	ipw2100_imem_put8(sc, 0x210000, 0x40);
3993847Seh146360 	ipw2100_imem_put8(sc, 0x210000, 0x00);
4003847Seh146360 	ipw2100_imem_put8(sc, 0x210000, 0x40);
4013847Seh146360 
4023847Seh146360 	ipw2100_imem_rep_put8(sc, 0x210010,
4033847Seh146360 	    sc->sc_fw.uc_base, sc->sc_fw.uc_size);
4043847Seh146360 
4053847Seh146360 	ipw2100_imem_put8(sc, 0x210000, 0x00);
4063847Seh146360 	ipw2100_imem_put8(sc, 0x210000, 0x00);
4073847Seh146360 	ipw2100_imem_put8(sc, 0x210000, 0x80);
4083847Seh146360 
4093847Seh146360 	ipw2100_imem_put16(sc, 0x220000, 0x0703);
4103847Seh146360 	ipw2100_imem_put16(sc, 0x220000, 0x0707);
4113847Seh146360 
4123847Seh146360 	ipw2100_imem_put8(sc, 0x210014, 0x72);
4133847Seh146360 	ipw2100_imem_put8(sc, 0x210014, 0x72);
4143847Seh146360 
4153847Seh146360 	ipw2100_imem_put8(sc, 0x210000, 0x00);
4163847Seh146360 	ipw2100_imem_put8(sc, 0x210000, 0x80);
4173847Seh146360 
4183847Seh146360 	/* try many times */
4193847Seh146360 	for (ntries = 0; ntries < 5000; ntries++) {
4203847Seh146360 		if (ipw2100_imem_get8(sc, 0x210000) & 1)
4213847Seh146360 			break;
4223847Seh146360 		drv_usecwait(1000); /* wait for a while */
4233847Seh146360 	}
4243847Seh146360 	if (ntries == 5000)
4253847Seh146360 		return (DDI_FAILURE);
4263847Seh146360 
4273847Seh146360 	ipw2100_imem_put32(sc, 0x3000e0, 0);
4283847Seh146360 
4293847Seh146360 	return (DDI_SUCCESS);
4303847Seh146360 }
4313847Seh146360 
4323847Seh146360 int
ipw2100_load_fw(struct ipw2100_softc * sc)4333847Seh146360 ipw2100_load_fw(struct ipw2100_softc *sc)
4343847Seh146360 {
4353847Seh146360 	uint8_t		*p, *e;
4363847Seh146360 	uint32_t	dst;
4373847Seh146360 	uint16_t	len;
4383847Seh146360 	clock_t		clk;
4393847Seh146360 
4403847Seh146360 	IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
4413847Seh146360 	    "ipw2100_load_fw(): enter\n"));
4423847Seh146360 
4433847Seh146360 	p = sc->sc_fw.fw_base;
4443847Seh146360 	e = sc->sc_fw.fw_base + sc->sc_fw.fw_size;
4453847Seh146360 	while (p < e) {
4463847Seh146360 		/*
4473847Seh146360 		 * each block is organized as <DST,LEN,DATA>
4483847Seh146360 		 */
4493847Seh146360 		if ((p + sizeof (dst) + sizeof (len)) > e) {
4503847Seh146360 			IPW2100_WARN((sc->sc_dip, CE_CONT,
4513847Seh146360 			    "ipw2100_load_fw(): invalid firmware image\n"));
4523847Seh146360 			return (DDI_FAILURE);
4533847Seh146360 		}
4547194Seh146360 		dst = LE_32(*((uint32_t *)(uintptr_t)p)); p += sizeof (dst);
4557194Seh146360 		len = LE_16(*((uint16_t *)(uintptr_t)p)); p += sizeof (len);
4563847Seh146360 		if ((p + len) > e) {
4573847Seh146360 			IPW2100_WARN((sc->sc_dip, CE_CONT,
4583847Seh146360 			    "ipw2100_load_fw(): invalid firmware image\n"));
4593847Seh146360 			return (DDI_FAILURE);
4603847Seh146360 		}
4613847Seh146360 
4623847Seh146360 		ipw2100_imem_putbuf(sc, dst, p, len);
4633847Seh146360 		p += len;
4643847Seh146360 	}
4653847Seh146360 
4663847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_IO,
4673847Seh146360 	    IPW2100_IO_GPIO1_ENABLE | IPW2100_IO_GPIO3_MASK |
4683847Seh146360 	    IPW2100_IO_LED_OFF);
4693847Seh146360 
4703847Seh146360 	mutex_enter(&sc->sc_ilock);
4713847Seh146360 
4723847Seh146360 	/*
4733847Seh146360 	 * enable all interrupts
4743847Seh146360 	 */
4753847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL);
4763847Seh146360 
4773847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, 0);
4783847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_CTL,
4793847Seh146360 	    ipw2100_csr_get32(sc, IPW2100_CSR_CTL) | IPW2100_CTL_ALLOW_STANDBY);
4803847Seh146360 
4813847Seh146360 	/*
4823847Seh146360 	 * wait for interrupt to notify fw initialization is done
4833847Seh146360 	 */
484*11066Srafael.vanoni@sun.com 	clk = drv_usectohz(5000000);  /* 5 second */
4853847Seh146360 	while (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) {
4863847Seh146360 		/*
4873847Seh146360 		 * wait longer for the fw  initialized
4883847Seh146360 		 */
489*11066Srafael.vanoni@sun.com 		if (cv_reltimedwait(&sc->sc_fw_cond, &sc->sc_ilock, clk,
490*11066Srafael.vanoni@sun.com 		    TR_CLOCK_TICK) < 0)
4913847Seh146360 			break;
4923847Seh146360 	}
4933847Seh146360 	mutex_exit(&sc->sc_ilock);
4943847Seh146360 
4953847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_IO,
4963847Seh146360 	    ipw2100_csr_get32(sc, IPW2100_CSR_IO) |
4973847Seh146360 	    IPW2100_IO_GPIO1_MASK | IPW2100_IO_GPIO3_MASK);
4983847Seh146360 
4993847Seh146360 	if (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) {
5003847Seh146360 		IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
5013847Seh146360 		    "ipw2100_load_fw(): exit, init failed\n"));
5023847Seh146360 		return (DDI_FAILURE);
5033847Seh146360 	}
5043847Seh146360 
5053847Seh146360 	IPW2100_DBG(IPW2100_DBG_FW, (sc->sc_dip, CE_CONT,
5063847Seh146360 	    "ipw2100_load_fw(): exit\n"));
5073847Seh146360 	return (DDI_SUCCESS);
5083847Seh146360 }
509