/*
 *************************************************************************
 * Ralink Tech Inc.
 * 5F., No.36, Taiyuan St., Jhubei City,
 * Hsinchu County 302,
 * Taiwan, R.O.C.
 *
 * (c) Copyright 2002-2007, Ralink Technology, Inc.
 *
 * This program is free software; you can redistribute it and/or modify  *
 * it under the terms of the GNU General Public License as published by  *
 * the Free Software Foundation; either version 2 of the License, or     *
 * (at your option) any later version.                                   *
 *                                                                       *
 * This program is distributed in the hope that it will be useful,       *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 * GNU General Public License for more details.                          *
 *                                                                       *
 * You should have received a copy of the GNU General Public License     *
 * along with this program; if not, write to the                         *
 * Free Software Foundation, Inc.,                                       *
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 *                                                                       *
 *************************************************************************

	Module Name:
	ee_prom.c

	Abstract:
	Miniport generic portion header file

	Revision History:
	Who         When          What
	--------    ----------    ----------------------------------------------
*/

#include "../rt_config.h"


// IRQL = PASSIVE_LEVEL
static inline VOID RaiseClock(
    IN	PRTMP_ADAPTER	pAd,
    IN  UINT32 *x)
{
	*x = *x | EESK;
	RTMP_IO_WRITE32(pAd, E2PROM_CSR, *x);
	RTMPusecDelay(1);				// Max frequency = 1MHz in Spec. definition
}

// IRQL = PASSIVE_LEVEL
static inline VOID LowerClock(
    IN	PRTMP_ADAPTER	pAd,
    IN  UINT32 *x)
{
	*x = *x & ~EESK;
	RTMP_IO_WRITE32(pAd, E2PROM_CSR, *x);
	RTMPusecDelay(1);
}

// IRQL = PASSIVE_LEVEL
static inline USHORT ShiftInBits(
	IN PRTMP_ADAPTER	pAd)
{
	UINT32		x,i;
	USHORT      data=0;

	RTMP_IO_READ32(pAd, E2PROM_CSR, &x);

	x &= ~( EEDO | EEDI);

	for(i=0; i<16; i++)
	{
		data = data << 1;
		RaiseClock(pAd, &x);

		RTMP_IO_READ32(pAd, E2PROM_CSR, &x);
		LowerClock(pAd, &x); //prevent read failed

		x &= ~(EEDI);
		if(x & EEDO)
		    data |= 1;
	}

	return data;
}


// IRQL = PASSIVE_LEVEL
static inline VOID ShiftOutBits(
	IN PRTMP_ADAPTER	pAd,
	IN USHORT			data,
	IN USHORT			count)
{
	UINT32       x,mask;

	mask = 0x01 << (count - 1);
	RTMP_IO_READ32(pAd, E2PROM_CSR, &x);

	x &= ~(EEDO | EEDI);

	do
	{
	    x &= ~EEDI;
	    if(data & mask)		x |= EEDI;

	    RTMP_IO_WRITE32(pAd, E2PROM_CSR, x);

	    RaiseClock(pAd, &x);
	    LowerClock(pAd, &x);

	    mask = mask >> 1;
	} while(mask);

	x &= ~EEDI;
	RTMP_IO_WRITE32(pAd, E2PROM_CSR, x);
}


// IRQL = PASSIVE_LEVEL
static inline VOID EEpromCleanup(
	IN PRTMP_ADAPTER	pAd)
{
	UINT32 x;

	RTMP_IO_READ32(pAd, E2PROM_CSR, &x);

	x &= ~(EECS | EEDI);
	RTMP_IO_WRITE32(pAd, E2PROM_CSR, x);

	RaiseClock(pAd, &x);
	LowerClock(pAd, &x);
}


static inline VOID EWEN(
	IN PRTMP_ADAPTER	pAd)
{
	UINT32	x;

	// reset bits and set EECS
	RTMP_IO_READ32(pAd, E2PROM_CSR, &x);
	x &= ~(EEDI | EEDO | EESK);
	x |= EECS;
	RTMP_IO_WRITE32(pAd, E2PROM_CSR, x);

	// kick a pulse
	RaiseClock(pAd, &x);
	LowerClock(pAd, &x);

	// output the read_opcode and six pulse in that order
	ShiftOutBits(pAd, EEPROM_EWEN_OPCODE, 5);
	ShiftOutBits(pAd, 0, 6);

	EEpromCleanup(pAd);
}


static inline VOID EWDS(
	IN PRTMP_ADAPTER	pAd)
{
	UINT32	x;

	// reset bits and set EECS
	RTMP_IO_READ32(pAd, E2PROM_CSR, &x);
	x &= ~(EEDI | EEDO | EESK);
	x |= EECS;
	RTMP_IO_WRITE32(pAd, E2PROM_CSR, x);

	// kick a pulse
	RaiseClock(pAd, &x);
	LowerClock(pAd, &x);

	// output the read_opcode and six pulse in that order
	ShiftOutBits(pAd, EEPROM_EWDS_OPCODE, 5);
	ShiftOutBits(pAd, 0, 6);

	EEpromCleanup(pAd);
}


// IRQL = PASSIVE_LEVEL
int rtmp_ee_prom_read16(
	IN PRTMP_ADAPTER	pAd,
	IN USHORT			Offset,
	OUT USHORT			*pValue)
{
	UINT32		x;
	USHORT		data;

#ifdef RT30xx
#ifdef ANT_DIVERSITY_SUPPORT
	if (pAd->NicConfig2.field.AntDiversity)
	{
		pAd->EepromAccess = TRUE;
	}
#endif // ANT_DIVERSITY_SUPPORT //
#endif // RT30xx //

	Offset /= 2;
	// reset bits and set EECS
	RTMP_IO_READ32(pAd, E2PROM_CSR, &x);
	x &= ~(EEDI | EEDO | EESK);
	x |= EECS;
	RTMP_IO_WRITE32(pAd, E2PROM_CSR, x);

	// patch can not access e-Fuse issue
	if (!(IS_RT3090(pAd) || IS_RT3572(pAd) || IS_RT3390(pAd)))
	{
		// kick a pulse
		RaiseClock(pAd, &x);
		LowerClock(pAd, &x);
	}

	// output the read_opcode and register number in that order
	ShiftOutBits(pAd, EEPROM_READ_OPCODE, 3);
	ShiftOutBits(pAd, Offset, pAd->EEPROMAddressNum);

	// Now read the data (16 bits) in from the selected EEPROM word
	data = ShiftInBits(pAd);

	EEpromCleanup(pAd);

#ifdef RT30xx
#ifdef ANT_DIVERSITY_SUPPORT
	// Antenna and EEPROM access are both using EESK pin,
	// Therefor we should avoid accessing EESK at the same time
	// Then restore antenna after EEPROM access
	if ((pAd->NicConfig2.field.AntDiversity)/* || (pAd->RfIcType == RFIC_3020)*/)
	{
		pAd->EepromAccess = FALSE;
		AsicSetRxAnt(pAd, pAd->RxAnt.Pair1PrimaryRxAnt);
	}
#endif // ANT_DIVERSITY_SUPPORT //
#endif // RT30xx //

	*pValue = data;

	return NDIS_STATUS_SUCCESS;
}


int rtmp_ee_prom_write16(
    IN  PRTMP_ADAPTER	pAd,
    IN  USHORT Offset,
    IN  USHORT Data)
{
	UINT32 x;

#ifdef RT30xx
#ifdef ANT_DIVERSITY_SUPPORT
	if (pAd->NicConfig2.field.AntDiversity)
	{
		pAd->EepromAccess = TRUE;
	}
#endif // ANT_DIVERSITY_SUPPORT //
#endif // RT30xx //

	Offset /= 2;

	EWEN(pAd);

	// reset bits and set EECS
	RTMP_IO_READ32(pAd, E2PROM_CSR, &x);
	x &= ~(EEDI | EEDO | EESK);
	x |= EECS;
	RTMP_IO_WRITE32(pAd, E2PROM_CSR, x);

	// patch can not access e-Fuse issue
	if (!(IS_RT3090(pAd) || IS_RT3572(pAd) || IS_RT3390(pAd)))
	{
		// kick a pulse
		RaiseClock(pAd, &x);
		LowerClock(pAd, &x);
	}

	// output the read_opcode ,register number and data in that order
	ShiftOutBits(pAd, EEPROM_WRITE_OPCODE, 3);
	ShiftOutBits(pAd, Offset, pAd->EEPROMAddressNum);
	ShiftOutBits(pAd, Data, 16);		// 16-bit access

	// read DO status
	RTMP_IO_READ32(pAd, E2PROM_CSR, &x);

	EEpromCleanup(pAd);

	RTMPusecDelay(10000);	//delay for twp(MAX)=10ms

	EWDS(pAd);

	EEpromCleanup(pAd);

#ifdef RT30xx
#ifdef ANT_DIVERSITY_SUPPORT
	// Antenna and EEPROM access are both using EESK pin,
	// Therefor we should avoid accessing EESK at the same time
	// Then restore antenna after EEPROM access
	if ((pAd->NicConfig2.field.AntDiversity) /*|| (pAd->RfIcType == RFIC_3020)*/)
	{
		pAd->EepromAccess = FALSE;
		AsicSetRxAnt(pAd, pAd->RxAnt.Pair1PrimaryRxAnt);
	}
#endif // ANT_DIVERSITY_SUPPORT //
#endif // RT30xx //

	return NDIS_STATUS_SUCCESS;

}
