xref: /onnv-gate/usr/src/uts/common/io/iwi/ipw2200_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, 2005
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/2200 mini-PCI adapter driver
353847Seh146360  * ipw2200_hw.c is used t handle hardware operations 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 "ipw2200.h"
463847Seh146360 #include "ipw2200_impl.h"
473847Seh146360 
483847Seh146360 /*
493847Seh146360  * Hardware related operations
503847Seh146360  */
513847Seh146360 #define	IPW2200_EEPROM_SHIFT_D		(2)
523847Seh146360 #define	IPW2200_EEPROM_SHIFT_Q		(4)
533847Seh146360 
543847Seh146360 #define	IPW2200_EEPROM_C		(1 << 0)
553847Seh146360 #define	IPW2200_EEPROM_S		(1 << 1)
563847Seh146360 #define	IPW2200_EEPROM_D		(1 << IPW2200_EEPROM_SHIFT_D)
573847Seh146360 #define	IPW2200_EEPROM_Q		(1 << IPW2200_EEPROM_SHIFT_Q)
583847Seh146360 
593847Seh146360 uint8_t
ipw2200_csr_get8(struct ipw2200_softc * sc,uint32_t off)603847Seh146360 ipw2200_csr_get8(struct ipw2200_softc *sc, uint32_t off)
613847Seh146360 {
623847Seh146360 	return (ddi_get8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off)));
633847Seh146360 }
643847Seh146360 
653847Seh146360 uint16_t
ipw2200_csr_get16(struct ipw2200_softc * sc,uint32_t off)663847Seh146360 ipw2200_csr_get16(struct ipw2200_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
ipw2200_csr_get32(struct ipw2200_softc * sc,uint32_t off)733847Seh146360 ipw2200_csr_get32(struct ipw2200_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
ipw2200_csr_getbuf32(struct ipw2200_softc * sc,uint32_t off,uint32_t * buf,size_t cnt)803847Seh146360 ipw2200_csr_getbuf32(struct ipw2200_softc *sc, uint32_t off,
813847Seh146360 	uint32_t *buf, size_t cnt)
823847Seh146360 {
837194Seh146360 	ddi_rep_get32(sc->sc_ioh, buf,
847194Seh146360 	    (uint32_t *)((uintptr_t)sc->sc_regs + off),
857194Seh146360 	    cnt, DDI_DEV_AUTOINCR);
863847Seh146360 }
873847Seh146360 
883847Seh146360 void
ipw2200_csr_put8(struct ipw2200_softc * sc,uint32_t off,uint8_t val)893847Seh146360 ipw2200_csr_put8(struct ipw2200_softc *sc, uint32_t off,
903847Seh146360 	uint8_t val)
913847Seh146360 {
923847Seh146360 	ddi_put8(sc->sc_ioh, (uint8_t *)(sc->sc_regs + off), val);
933847Seh146360 }
943847Seh146360 
953847Seh146360 void
ipw2200_csr_put16(struct ipw2200_softc * sc,uint32_t off,uint16_t val)963847Seh146360 ipw2200_csr_put16(struct ipw2200_softc *sc, uint32_t off,
973847Seh146360 	uint16_t val)
983847Seh146360 {
997194Seh146360 	ddi_put16(sc->sc_ioh,
1007194Seh146360 	    (uint16_t *)((uintptr_t)sc->sc_regs + off), val);
1013847Seh146360 }
1023847Seh146360 
1033847Seh146360 void
ipw2200_csr_put32(struct ipw2200_softc * sc,uint32_t off,uint32_t val)1043847Seh146360 ipw2200_csr_put32(struct ipw2200_softc *sc, uint32_t off,
1053847Seh146360 	uint32_t val)
1063847Seh146360 {
1077194Seh146360 	ddi_put32(sc->sc_ioh,
1087194Seh146360 	    (uint32_t *)((uintptr_t)sc->sc_regs + off), val);
1093847Seh146360 }
1103847Seh146360 
1113847Seh146360 uint8_t
ipw2200_imem_get8(struct ipw2200_softc * sc,uint32_t addr)1123847Seh146360 ipw2200_imem_get8(struct ipw2200_softc *sc, uint32_t addr)
1133847Seh146360 {
1143847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr);
1153847Seh146360 	return (ipw2200_csr_get8(sc, IPW2200_CSR_INDIRECT_DATA));
1163847Seh146360 }
1173847Seh146360 
1183847Seh146360 uint16_t
ipw2200_imem_get16(struct ipw2200_softc * sc,uint32_t addr)1193847Seh146360 ipw2200_imem_get16(struct ipw2200_softc *sc,
1203847Seh146360 	uint32_t addr)
1213847Seh146360 {
1223847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr);
1233847Seh146360 	return (ipw2200_csr_get16(sc, IPW2200_CSR_INDIRECT_DATA));
1243847Seh146360 }
1253847Seh146360 
1263847Seh146360 uint32_t
ipw2200_imem_get32(struct ipw2200_softc * sc,uint32_t addr)1273847Seh146360 ipw2200_imem_get32(struct ipw2200_softc *sc, uint32_t addr)
1283847Seh146360 {
1293847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr);
1303847Seh146360 	return (ipw2200_csr_get32(sc, IPW2200_CSR_INDIRECT_DATA));
1313847Seh146360 }
1323847Seh146360 
1333847Seh146360 void
ipw2200_imem_put8(struct ipw2200_softc * sc,uint32_t addr,uint8_t val)1343847Seh146360 ipw2200_imem_put8(struct ipw2200_softc *sc, uint32_t addr, uint8_t val)
1353847Seh146360 {
1363847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr);
1373847Seh146360 	ipw2200_csr_put8(sc, IPW2200_CSR_INDIRECT_DATA, val);
1383847Seh146360 }
1393847Seh146360 
1403847Seh146360 void
ipw2200_imem_put16(struct ipw2200_softc * sc,uint32_t addr,uint16_t val)1413847Seh146360 ipw2200_imem_put16(struct ipw2200_softc *sc, uint32_t addr,
1423847Seh146360 	uint16_t val)
1433847Seh146360 {
1443847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr);
1453847Seh146360 	ipw2200_csr_put16(sc, IPW2200_CSR_INDIRECT_DATA, val);
1463847Seh146360 }
1473847Seh146360 
1483847Seh146360 void
ipw2200_imem_put32(struct ipw2200_softc * sc,uint32_t addr,uint32_t val)1493847Seh146360 ipw2200_imem_put32(struct ipw2200_softc *sc, uint32_t addr,
1503847Seh146360 	uint32_t val)
1513847Seh146360 {
1523847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_ADDR, addr);
1533847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INDIRECT_DATA, val);
1543847Seh146360 }
1553847Seh146360 
1563847Seh146360 void
ipw2200_rom_control(struct ipw2200_softc * sc,uint32_t val)1573847Seh146360 ipw2200_rom_control(struct ipw2200_softc *sc, uint32_t val)
1583847Seh146360 {
1593847Seh146360 	ipw2200_imem_put32(sc, IPW2200_IMEM_EEPROM_CTL, val);
1603847Seh146360 	drv_usecwait(IPW2200_EEPROM_DELAY);
1613847Seh146360 }
1623847Seh146360 
1633847Seh146360 uint16_t
ipw2200_rom_get16(struct ipw2200_softc * sc,uint8_t addr)1643847Seh146360 ipw2200_rom_get16(struct ipw2200_softc *sc, uint8_t addr)
1653847Seh146360 {
1663847Seh146360 	uint32_t	tmp;
1673847Seh146360 	uint16_t	val;
1683847Seh146360 	int		n;
1693847Seh146360 
1703847Seh146360 	/*
1713847Seh146360 	 * According to i2c bus protocol
1723847Seh146360 	 */
1733847Seh146360 	/* clock */
1743847Seh146360 	ipw2200_rom_control(sc, 0);
1753847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S);
1763847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_C);
1773847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S);
1783847Seh146360 	/* start bit */
1793847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_D);
1803847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_D |
1817194Seh146360 	    IPW2200_EEPROM_C);
1823847Seh146360 	/* read opcode */
1833847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_D);
1843847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_D |
1857194Seh146360 	    IPW2200_EEPROM_C);
1863847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S);
1873847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_C);
1883847Seh146360 	/*
1893847Seh146360 	 * address, totally 8 bits, defined by hardware, push from MSB to LSB
1903847Seh146360 	 */
1913847Seh146360 	for (n = 7; n >= 0; n--) {
1923847Seh146360 		ipw2200_rom_control(sc, IPW2200_EEPROM_S |
1937194Seh146360 		    (((addr >> n) & 1) << IPW2200_EEPROM_SHIFT_D));
1943847Seh146360 		ipw2200_rom_control(sc, IPW2200_EEPROM_S |
1957194Seh146360 		    (((addr >> n) & 1) << IPW2200_EEPROM_SHIFT_D) |
1967194Seh146360 		    IPW2200_EEPROM_C);
1973847Seh146360 	}
1983847Seh146360 
1993847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S);
2003847Seh146360 
2013847Seh146360 	/*
2023847Seh146360 	 * data, totally 16 bits, defined by hardware, push from MSB to LSB
2033847Seh146360 	 */
2043847Seh146360 	val = 0;
2053847Seh146360 	for (n = 15; n >= 0; n--) {
2063847Seh146360 		ipw2200_rom_control(sc, IPW2200_EEPROM_S | IPW2200_EEPROM_C);
2073847Seh146360 		ipw2200_rom_control(sc, IPW2200_EEPROM_S);
2083847Seh146360 		tmp = ipw2200_imem_get32(sc, IPW2200_IMEM_EEPROM_CTL);
2093847Seh146360 		val |= ((tmp & IPW2200_EEPROM_Q) >> IPW2200_EEPROM_SHIFT_Q)
2103847Seh146360 		    << n;
2113847Seh146360 	}
2123847Seh146360 
2133847Seh146360 	ipw2200_rom_control(sc, 0);
2143847Seh146360 
2153847Seh146360 	/* clear chip select and clock */
2163847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_S);
2173847Seh146360 	ipw2200_rom_control(sc, 0);
2183847Seh146360 	ipw2200_rom_control(sc, IPW2200_EEPROM_C);
2193847Seh146360 
2203847Seh146360 	return (BE_16(val));
2213847Seh146360 }
2223847Seh146360 
2233847Seh146360 /*
2243847Seh146360  * Firmware related operations
2253847Seh146360  */
2263847Seh146360 #define	IPW2200_FW_MAJOR_VERSION	(2)
2273847Seh146360 #define	IPW2200_FW_MINOR_VERSION	(4)
2283847Seh146360 
2293847Seh146360 #define	IPW2200_FW_MAJOR(x)((x) & 0xff)
2303847Seh146360 #define	IPW2200_FW_MINOR(x)(((x) & 0xff) >> 8)
2313847Seh146360 
2323847Seh146360 /*
2333847Seh146360  * These firwares were issued by Intel as binaries which need to be
2343847Seh146360  * loaded to hardware when card is initiated, or when fatal error
2353847Seh146360  * happened, or when the chip need be reset.
2363847Seh146360  */
2373847Seh146360 static uint8_t ipw2200_boot_bin [] = {
2383847Seh146360 #include "fw-ipw2200/ipw-2.4-boot.hex"
2393847Seh146360 };
2403847Seh146360 static uint8_t ipw2200_ucode_bin [] = {
2413847Seh146360 #include "fw-ipw2200/ipw-2.4-bss_ucode.hex"
2423847Seh146360 };
2433847Seh146360 static uint8_t ipw2200_fw_bin [] = {
2443847Seh146360 #include "fw-ipw2200/ipw-2.4-bss.hex"
2453847Seh146360 };
2463847Seh146360 
2473847Seh146360 #pragma pack(1)
2483847Seh146360 struct header {
2493847Seh146360 	uint32_t	version;
2503847Seh146360 	uint32_t	mode;
2513847Seh146360 };
2523847Seh146360 #pragma pack()
2533847Seh146360 
2543847Seh146360 int
ipw2200_cache_firmware(struct ipw2200_softc * sc)2553847Seh146360 ipw2200_cache_firmware(struct ipw2200_softc *sc)
2563847Seh146360 {
2573847Seh146360 	IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT,
2583847Seh146360 	    "ipw2200_cache_firmware(): enter\n"));
2593847Seh146360 
2603847Seh146360 	/* boot code */
2613847Seh146360 	sc->sc_fw.boot_base = ipw2200_boot_bin + sizeof (struct header);
2623847Seh146360 	sc->sc_fw.boot_size =
2637194Seh146360 	    sizeof (ipw2200_boot_bin) - sizeof (struct header);
2643847Seh146360 	/* ucode */
2653847Seh146360 	sc->sc_fw.uc_base = ipw2200_ucode_bin + sizeof (struct header);
2663847Seh146360 	sc->sc_fw.uc_size = sizeof (ipw2200_ucode_bin) - sizeof (struct header);
2673847Seh146360 	/* firmware */
2683847Seh146360 	sc->sc_fw.fw_base = ipw2200_fw_bin + sizeof (struct header);
2693847Seh146360 	sc->sc_fw.fw_size = sizeof (ipw2200_fw_bin) - sizeof (struct header);
2703847Seh146360 
2713847Seh146360 	sc->sc_flags |= IPW2200_FLAG_FW_CACHED;
2723847Seh146360 
2733847Seh146360 	IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT,
2743847Seh146360 	    "ipw2200_cache_firmware(): boot=%u,uc=%u,fw=%u\n",
2753847Seh146360 	    sc->sc_fw.boot_size, sc->sc_fw.uc_size, sc->sc_fw.fw_size));
2763847Seh146360 	IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT,
2773847Seh146360 	    "ipw2200_cache_firmware(): exit\n"));
2783847Seh146360 
2793847Seh146360 	return (DDI_SUCCESS);
2803847Seh146360 }
2813847Seh146360 
2823847Seh146360 /*
2833847Seh146360  * If user-land firmware loading is supported, this routine will
2843847Seh146360  * free kernel memory, when sc->sc_fw.bin_base & sc->sc_fw.bin_size
2853847Seh146360  * are not empty
2863847Seh146360  */
2873847Seh146360 int
ipw2200_free_firmware(struct ipw2200_softc * sc)2883847Seh146360 ipw2200_free_firmware(struct ipw2200_softc *sc)
2893847Seh146360 {
2903847Seh146360 	sc->sc_flags &= ~IPW2200_FLAG_FW_CACHED;
2913847Seh146360 
2923847Seh146360 	return (DDI_SUCCESS);
2933847Seh146360 }
2943847Seh146360 
2953847Seh146360 /*
2963847Seh146360  * the following routines load code onto ipw2200 hardware
2973847Seh146360  */
2983847Seh146360 int
ipw2200_load_uc(struct ipw2200_softc * sc,uint8_t * buf,size_t size)2993847Seh146360 ipw2200_load_uc(struct ipw2200_softc *sc, uint8_t *buf, size_t size)
3003847Seh146360 {
3013847Seh146360 	int		ntries, i;
3023847Seh146360 	uint16_t	*w;
3033847Seh146360 
3043847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST,
3053847Seh146360 	    IPW2200_RST_STOP_MASTER | ipw2200_csr_get32(sc, IPW2200_CSR_RST));
3063847Seh146360 	for (ntries = 0; ntries < 5; ntries++) {
3073847Seh146360 		if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) &
3083847Seh146360 		    IPW2200_RST_MASTER_DISABLED)
3093847Seh146360 			break;
3103847Seh146360 		drv_usecwait(10);
3113847Seh146360 	}
3123847Seh146360 	if (ntries == 5) {
3133847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_CONT,
3143847Seh146360 		    "ipw2200_load_uc(): timeout waiting for master"));
3153847Seh146360 		return (DDI_FAILURE);
3163847Seh146360 	}
3173847Seh146360 
3183847Seh146360 	ipw2200_imem_put32(sc, 0x3000e0, 0x80000000);
3193847Seh146360 	drv_usecwait(5000);
3203847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST,
3213847Seh146360 	    ~IPW2200_RST_PRINCETON_RESET &
3223847Seh146360 	    ipw2200_csr_get32(sc, IPW2200_CSR_RST));
3233847Seh146360 	drv_usecwait(5000);
3243847Seh146360 	ipw2200_imem_put32(sc, 0x3000e0, 0);
3253847Seh146360 	drv_usecwait(1000);
3263847Seh146360 	ipw2200_imem_put32(sc, IPW2200_IMEM_EVENT_CTL, 1);
3273847Seh146360 	drv_usecwait(1000);
3283847Seh146360 	ipw2200_imem_put32(sc, IPW2200_IMEM_EVENT_CTL, 0);
3293847Seh146360 	drv_usecwait(1000);
3303847Seh146360 	ipw2200_imem_put8(sc, 0x200000, 0x00);
3313847Seh146360 	ipw2200_imem_put8(sc, 0x200000, 0x40);
3323847Seh146360 	drv_usecwait(1000);
3333847Seh146360 
3347194Seh146360 	for (w = (uint16_t *)(uintptr_t)buf; size > 0; w++, size -= 2)
3353847Seh146360 		ipw2200_imem_put16(sc, 0x200010, LE_16(*w));
3363847Seh146360 
3373847Seh146360 	ipw2200_imem_put8(sc, 0x200000, 0x00);
3383847Seh146360 	ipw2200_imem_put8(sc, 0x200000, 0x80);
3393847Seh146360 
3403847Seh146360 	/*
3413847Seh146360 	 * try many times to wait the upload is ready, 2000times
3423847Seh146360 	 */
3433847Seh146360 	for (ntries = 0; ntries < 2000; ntries++) {
3443847Seh146360 		uint8_t val;
3453847Seh146360 
3463847Seh146360 		val = ipw2200_imem_get8(sc, 0x200000);
3473847Seh146360 		if (val & 1)
3483847Seh146360 			break;
3493847Seh146360 		drv_usecwait(1000); /* wait for a while */
3503847Seh146360 	}
3513847Seh146360 	if (ntries == 2000) {
3523847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
3533847Seh146360 		    "ipw2200_load_uc(): timeout waiting for ucode init.\n"));
3543847Seh146360 		return (DDI_FAILURE);
3553847Seh146360 	}
3563847Seh146360 
3573847Seh146360 	for (i = 0; i < 7; i++)
3583847Seh146360 		(void) ipw2200_imem_get32(sc, 0x200004);
3593847Seh146360 
3603847Seh146360 	ipw2200_imem_put8(sc, 0x200000, 0x00);
3613847Seh146360 
3623847Seh146360 	return (DDI_SUCCESS);
3633847Seh146360 }
3643847Seh146360 
3653847Seh146360 #define	MAX_DR_NUM	(64)
3663847Seh146360 #define	MAX_DR_SIZE	(4096)
3673847Seh146360 
3683847Seh146360 int
ipw2200_load_fw(struct ipw2200_softc * sc,uint8_t * buf,size_t size)3693847Seh146360 ipw2200_load_fw(struct ipw2200_softc *sc, uint8_t *buf, size_t size)
3703847Seh146360 {
3713847Seh146360 	struct dma_region	dr[MAX_DR_NUM]; /* maximal, 64 * 4KB = 256KB */
3723847Seh146360 	uint8_t			*p, *end, *v;
3733847Seh146360 	uint32_t		mlen;
3743847Seh146360 	uint32_t		src, dst, ctl, len, sum, off;
3753847Seh146360 	uint32_t		sentinel;
3763847Seh146360 	int			ntries, err, cnt, i;
377*11066Srafael.vanoni@sun.com 	clock_t			clk = drv_usectohz(5000000);  /* 5 second */
3783847Seh146360 
3793847Seh146360 	ipw2200_imem_put32(sc, 0x3000a0, 0x27000);
3803847Seh146360 
3813847Seh146360 	p   = buf;
3823847Seh146360 	end = p + size;
3833847Seh146360 
3843847Seh146360 	cnt = 0;
3853847Seh146360 	err = ipw2200_dma_region_alloc(sc, &dr[cnt], MAX_DR_SIZE, DDI_DMA_READ,
3863847Seh146360 	    DDI_DMA_STREAMING);
3873847Seh146360 	if (err != DDI_SUCCESS)
3883847Seh146360 		goto fail0;
3893847Seh146360 	off = 0;
3903847Seh146360 	src = dr[cnt].dr_pbase;
3913847Seh146360 
3923847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0x27000);
3933847Seh146360 
3943847Seh146360 	while (p < end) {
3957194Seh146360 		dst = LE_32(*((uint32_t *)(uintptr_t)p)); p += 4;
3967194Seh146360 		len = LE_32(*((uint32_t *)(uintptr_t)p)); p += 4;
3973847Seh146360 		v = p;
3983847Seh146360 		p += len;
3993847Seh146360 		IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT,
4003847Seh146360 		    "ipw2200_load_fw(): dst=0x%x,len=%u\n", dst, len));
4013847Seh146360 
4023847Seh146360 		while (len > 0) {
4033847Seh146360 			/*
4043847Seh146360 			 * if no DMA region is available, allocate a new one
4053847Seh146360 			 */
4063847Seh146360 			if (off == dr[cnt].dr_size) {
4073847Seh146360 				cnt++;
4083847Seh146360 				if (cnt >= MAX_DR_NUM) {
4093847Seh146360 					IPW2200_WARN((sc->sc_dip, CE_WARN,
4103847Seh146360 					    "ipw2200_load_fw(): "
4113847Seh146360 					    "maximum %d DRs is reached\n",
4123847Seh146360 					    cnt));
4133847Seh146360 					cnt--; /* only free alloced DMA */
4143847Seh146360 					goto fail1;
4153847Seh146360 				}
4163847Seh146360 				err = ipw2200_dma_region_alloc(sc, &dr[cnt],
4173847Seh146360 				    MAX_DR_SIZE, DDI_DMA_WRITE,
4183847Seh146360 				    DDI_DMA_STREAMING);
4193847Seh146360 				if (err != DDI_SUCCESS) {
4203847Seh146360 					cnt--; /* only free alloced DMA */
4213847Seh146360 					goto fail1;
4223847Seh146360 				}
4233847Seh146360 				off = 0;
4243847Seh146360 				src = dr[cnt].dr_pbase;
4253847Seh146360 			}
4263847Seh146360 			mlen = min(IPW2200_CB_MAXDATALEN, len);
4273847Seh146360 			mlen = min(mlen, dr[cnt].dr_size - off);
4283847Seh146360 
4293847Seh146360 			(void) memcpy(dr[cnt].dr_base + off, v, mlen);
4303847Seh146360 			(void) ddi_dma_sync(dr[cnt].dr_hnd, off, mlen,
4313847Seh146360 			    DDI_DMA_SYNC_FORDEV);
4323847Seh146360 
4333847Seh146360 			ctl = IPW2200_CB_DEFAULT_CTL | mlen;
4343847Seh146360 			sum = ctl ^ src ^ dst;
4353847Seh146360 			/*
4363847Seh146360 			 * write a command
4373847Seh146360 			 */
4383847Seh146360 			ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, ctl);
4393847Seh146360 			ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, src);
4403847Seh146360 			ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, dst);
4413847Seh146360 			ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, sum);
4423847Seh146360 
4433847Seh146360 			off += mlen;
4443847Seh146360 			src += mlen;
4453847Seh146360 			dst += mlen;
4463847Seh146360 			v   += mlen;
4473847Seh146360 			len -= mlen;
4483847Seh146360 		}
4493847Seh146360 	}
4503847Seh146360 
4513847Seh146360 	sentinel = ipw2200_csr_get32(sc, IPW2200_CSR_AUTOINC_ADDR);
4523847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0);
4533847Seh146360 
4543847Seh146360 	IPW2200_DBG(IPW2200_DBG_FW, (sc->sc_dip, CE_CONT,
4553847Seh146360 	    "ipw2200_load_fw(): sentinel=%x\n", sentinel));
4563847Seh146360 
4573847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST,
4583847Seh146360 	    ~(IPW2200_RST_MASTER_DISABLED | IPW2200_RST_STOP_MASTER)
4593847Seh146360 	    & ipw2200_csr_get32(sc, IPW2200_CSR_RST));
4603847Seh146360 
4613847Seh146360 	ipw2200_imem_put32(sc, 0x3000a4, 0x540100);
4623847Seh146360 	for (ntries = 0; ntries < 400; ntries++) {
4633847Seh146360 		uint32_t val;
4643847Seh146360 		val = ipw2200_imem_get32(sc, 0x3000d0);
4653847Seh146360 		if (val >= sentinel)
4663847Seh146360 			break;
4673847Seh146360 		drv_usecwait(100);
4683847Seh146360 	}
4693847Seh146360 	if (ntries == 400) {
4703847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
4713847Seh146360 		    "ipw2200_load_fw(): timeout processing command blocks\n"));
4723847Seh146360 		goto fail1;
4733847Seh146360 	}
4743847Seh146360 
4753847Seh146360 	mutex_enter(&sc->sc_ilock);
4763847Seh146360 
4773847Seh146360 	ipw2200_imem_put32(sc, 0x3000a4, 0x540c00);
4783847Seh146360 
4793847Seh146360 	/*
4803847Seh146360 	 * enable all interrupts
4813847Seh146360 	 */
4823847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL);
4833847Seh146360 
4843847Seh146360 	/*
4853847Seh146360 	 * tell the adapter to initialize the firmware,
4863847Seh146360 	 * just simply set it to 0
4873847Seh146360 	 */
4883847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, 0);
4893847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CTL,
4907194Seh146360 	    ipw2200_csr_get32(sc, IPW2200_CSR_CTL) |
4917194Seh146360 	    IPW2200_CTL_ALLOW_STANDBY);
4923847Seh146360 
4933847Seh146360 	/*
4943847Seh146360 	 * wait for interrupt to notify fw initialization is done
4953847Seh146360 	 */
4963847Seh146360 	sc->sc_fw_ok = 0;
4973847Seh146360 	while (!sc->sc_fw_ok) {
4983847Seh146360 		/*
4993847Seh146360 		 * There is an enhancement! we just change from 1s to 5s
5003847Seh146360 		 */
501*11066Srafael.vanoni@sun.com 		if (cv_reltimedwait(&sc->sc_fw_cond, &sc->sc_ilock, clk,
502*11066Srafael.vanoni@sun.com 		    TR_CLOCK_TICK) < 0)
5033847Seh146360 			break;
5043847Seh146360 	}
5053847Seh146360 	mutex_exit(&sc->sc_ilock);
5063847Seh146360 
5073847Seh146360 	if (!sc->sc_fw_ok) {
5083847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
5093847Seh146360 		    "ipw2200_load_fw(): firmware(%u) load failed!", size));
5103847Seh146360 		goto fail1;
5113847Seh146360 	}
5123847Seh146360 
5133847Seh146360 	for (i = 0; i <= cnt; i++)
5143847Seh146360 		ipw2200_dma_region_free(&dr[i]);
5153847Seh146360 
5163847Seh146360 	return (DDI_SUCCESS);
5173847Seh146360 
5183847Seh146360 fail1:
5193847Seh146360 	IPW2200_WARN((sc->sc_dip, CE_WARN,
5203847Seh146360 	    "ipw2200_load_fw(): DMA allocation failed, cnt=%d\n", cnt));
5213847Seh146360 	for (i = 0; i <= cnt; i++)
5223847Seh146360 		ipw2200_dma_region_free(&dr[i]);
5233847Seh146360 fail0:
5243847Seh146360 	return (DDI_FAILURE);
5253847Seh146360 }
526