blob: 844d4b987b78eef1c0b3df837522fbea55c195e6 [file] [log] [blame]
/*
*************************************************************************
* 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:
rtmp_mcu.c
Abstract:
Miniport generic portion header file
Revision History:
Who When What
-------- ---------- ----------------------------------------------
*/
#include "../rt_config.h"
#include <linux/crc-ccitt.h>
#include <linux/firmware.h>
#ifdef RTMP_MAC_USB
#define FIRMWAREIMAGE_LENGTH 0x1000
#define FIRMWARE_2870_MIN_VERSION 12
#define FIRMWARE_2870_FILENAME "rt2870.bin"
MODULE_FIRMWARE(FIRMWARE_2870_FILENAME);
#define FIRMWARE_3070_MIN_VERSION 17
#define FIRMWARE_3070_FILENAME "rt3070.bin"
MODULE_FIRMWARE(FIRMWARE_3070_FILENAME);
#define FIRMWARE_3071_MIN_VERSION 17
#define FIRMWARE_3071_FILENAME "rt3071.bin" /* for RT3071/RT3072 */
MODULE_FIRMWARE(FIRMWARE_3071_FILENAME);
#else /* RTMP_MAC_PCI */
#define FIRMWAREIMAGE_LENGTH 0x2000
#define FIRMWARE_2860_MIN_VERSION 11
#define FIRMWARE_2860_FILENAME "rt2860.bin"
MODULE_FIRMWARE(FIRMWARE_2860_FILENAME);
#define FIRMWARE_3090_MIN_VERSION 19
#define FIRMWARE_3090_FILENAME "rt3090.bin" /* for RT3090/RT3390 */
MODULE_FIRMWARE(FIRMWARE_3090_FILENAME);
#endif
/*
========================================================================
Routine Description:
erase 8051 firmware image in MAC ASIC
Arguments:
Adapter Pointer to our adapter
IRQL = PASSIVE_LEVEL
========================================================================
*/
int RtmpAsicEraseFirmware(struct rt_rtmp_adapter *pAd)
{
unsigned long i;
for (i = 0; i < MAX_FIRMWARE_IMAGE_SIZE; i += 4)
RTMP_IO_WRITE32(pAd, FIRMWARE_IMAGE_BASE + i, 0);
return 0;
}
static const struct firmware *rtmp_get_firmware(struct rt_rtmp_adapter *adapter)
{
const char *name;
const struct firmware *fw = NULL;
u8 min_version;
struct device *dev;
int err;
if (adapter->firmware)
return adapter->firmware;
#ifdef RTMP_MAC_USB
if (IS_RT3071(adapter)) {
name = FIRMWARE_3071_FILENAME;
min_version = FIRMWARE_3071_MIN_VERSION;
} else if (IS_RT3070(adapter)) {
name = FIRMWARE_3070_FILENAME;
min_version = FIRMWARE_3070_MIN_VERSION;
} else {
name = FIRMWARE_2870_FILENAME;
min_version = FIRMWARE_2870_MIN_VERSION;
}
dev = &((struct os_cookie *)adapter->OS_Cookie)->pUsb_Dev->dev;
#else /* RTMP_MAC_PCI */
if (IS_RT3090(adapter) || IS_RT3390(adapter)) {
name = FIRMWARE_3090_FILENAME;
min_version = FIRMWARE_3090_MIN_VERSION;
} else {
name = FIRMWARE_2860_FILENAME;
min_version = FIRMWARE_2860_MIN_VERSION;
}
dev = &((struct os_cookie *)adapter->OS_Cookie)->pci_dev->dev;
#endif
err = request_firmware(&fw, name, dev);
if (err) {
dev_err(dev, "firmware file %s request failed (%d)\n",
name, err);
return NULL;
}
if (fw->size < FIRMWAREIMAGE_LENGTH) {
dev_err(dev, "firmware file %s size is invalid\n", name);
goto invalid;
}
/* is it new enough? */
adapter->FirmwareVersion = fw->data[FIRMWAREIMAGE_LENGTH - 3];
if (adapter->FirmwareVersion < min_version) {
dev_err(dev,
"firmware file %s is too old;"
" driver requires v%d or later\n",
name, min_version);
goto invalid;
}
/* is the internal CRC correct? */
if (crc_ccitt(0xffff, fw->data, FIRMWAREIMAGE_LENGTH - 2) !=
(fw->data[FIRMWAREIMAGE_LENGTH - 2] |
(fw->data[FIRMWAREIMAGE_LENGTH - 1] << 8))) {
dev_err(dev, "firmware file %s failed internal CRC\n", name);
goto invalid;
}
adapter->firmware = fw;
return fw;
invalid:
release_firmware(fw);
return NULL;
}
/*
========================================================================
Routine Description:
Load 8051 firmware file into MAC ASIC
Arguments:
Adapter Pointer to our adapter
Return Value:
NDIS_STATUS_SUCCESS firmware image load ok
NDIS_STATUS_FAILURE image not found
IRQL = PASSIVE_LEVEL
========================================================================
*/
int RtmpAsicLoadFirmware(struct rt_rtmp_adapter *pAd)
{
const struct firmware *fw;
int Status = NDIS_STATUS_SUCCESS;
unsigned long Index;
u32 MacReg = 0;
fw = rtmp_get_firmware(pAd);
if (!fw)
return NDIS_STATUS_FAILURE;
RTMP_WRITE_FIRMWARE(pAd, fw->data, FIRMWAREIMAGE_LENGTH);
/* check if MCU is ready */
Index = 0;
do {
RTMP_IO_READ32(pAd, PBF_SYS_CTRL, &MacReg);
if (MacReg & 0x80)
break;
RTMPusecDelay(1000);
} while (Index++ < 1000);
if (Index > 1000) {
DBGPRINT(RT_DEBUG_ERROR,
("NICLoadFirmware: MCU is not ready\n"));
Status = NDIS_STATUS_FAILURE;
}
DBGPRINT(RT_DEBUG_TRACE, ("<=== %s (status=%d)\n", __func__, Status));
return Status;
}
int RtmpAsicSendCommandToMcu(struct rt_rtmp_adapter *pAd,
u8 Command,
u8 Token, u8 Arg0, u8 Arg1)
{
HOST_CMD_CSR_STRUC H2MCmd;
H2M_MAILBOX_STRUC H2MMailbox;
unsigned long i = 0;
#ifdef PCIE_PS_SUPPORT
/* 3090F power solution 3 has hw limitation that needs to ban all mcu command */
/* when firmware is in radio state. For other chip doesn't have this limitation. */
if (((IS_RT3090(pAd) || IS_RT3572(pAd) || IS_RT3390(pAd))
&& IS_VERSION_AFTER_F(pAd)) && IS_VERSION_AFTER_F(pAd)
&& (pAd->StaCfg.PSControl.field.rt30xxPowerMode == 3)
&& (pAd->StaCfg.PSControl.field.EnableNewPS == TRUE)) {
RTMP_SEM_LOCK(&pAd->McuCmdLock);
if ((pAd->brt30xxBanMcuCmd == TRUE)
&& (Command != WAKE_MCU_CMD) && (Command != RFOFF_MCU_CMD)) {
RTMP_SEM_UNLOCK(&pAd->McuCmdLock);
DBGPRINT(RT_DEBUG_TRACE,
(" Ban Mcu Cmd %x in sleep mode\n", Command));
return FALSE;
} else if ((Command == SLEEP_MCU_CMD)
|| (Command == RFOFF_MCU_CMD)) {
pAd->brt30xxBanMcuCmd = TRUE;
} else if (Command != WAKE_MCU_CMD) {
pAd->brt30xxBanMcuCmd = FALSE;
}
RTMP_SEM_UNLOCK(&pAd->McuCmdLock);
}
if (((IS_RT3090(pAd) || IS_RT3572(pAd) || IS_RT3390(pAd))
&& IS_VERSION_AFTER_F(pAd)) && IS_VERSION_AFTER_F(pAd)
&& (pAd->StaCfg.PSControl.field.rt30xxPowerMode == 3)
&& (pAd->StaCfg.PSControl.field.EnableNewPS == TRUE)
&& (Command == WAKE_MCU_CMD)) {
do {
RTMP_IO_FORCE_READ32(pAd, H2M_MAILBOX_CSR,
&H2MMailbox.word);
if (H2MMailbox.field.Owner == 0)
break;
RTMPusecDelay(2);
DBGPRINT(RT_DEBUG_INFO,
("AsicSendCommanToMcu::Mail box is busy\n"));
} while (i++ < 100);
if (i > 100) {
DBGPRINT_ERR(("H2M_MAILBOX still hold by MCU. command fail\n"));
return FALSE;
}
H2MMailbox.field.Owner = 1; /* pass ownership to MCU */
H2MMailbox.field.CmdToken = Token;
H2MMailbox.field.HighByte = Arg1;
H2MMailbox.field.LowByte = Arg0;
RTMP_IO_FORCE_WRITE32(pAd, H2M_MAILBOX_CSR, H2MMailbox.word);
H2MCmd.word = 0;
H2MCmd.field.HostCommand = Command;
RTMP_IO_FORCE_WRITE32(pAd, HOST_CMD_CSR, H2MCmd.word);
} else
#endif /* PCIE_PS_SUPPORT // */
{
do {
RTMP_IO_READ32(pAd, H2M_MAILBOX_CSR, &H2MMailbox.word);
if (H2MMailbox.field.Owner == 0)
break;
RTMPusecDelay(2);
} while (i++ < 100);
if (i > 100) {
#ifdef RTMP_MAC_PCI
#endif /* RTMP_MAC_PCI // */
{
DBGPRINT_ERR(("H2M_MAILBOX still hold by MCU. command fail\n"));
}
return FALSE;
}
#ifdef RTMP_MAC_PCI
#endif /* RTMP_MAC_PCI // */
H2MMailbox.field.Owner = 1; /* pass ownership to MCU */
H2MMailbox.field.CmdToken = Token;
H2MMailbox.field.HighByte = Arg1;
H2MMailbox.field.LowByte = Arg0;
RTMP_IO_WRITE32(pAd, H2M_MAILBOX_CSR, H2MMailbox.word);
H2MCmd.word = 0;
H2MCmd.field.HostCommand = Command;
RTMP_IO_WRITE32(pAd, HOST_CMD_CSR, H2MCmd.word);
if (Command != 0x80) {
}
}
#ifdef PCIE_PS_SUPPORT
/* 3090 MCU Wakeup command needs more time to be stable. */
/* Before stable, don't issue other MCU command to prevent from firmware error. */
if (((IS_RT3090(pAd) || IS_RT3572(pAd) || IS_RT3390(pAd))
&& IS_VERSION_AFTER_F(pAd)) && IS_VERSION_AFTER_F(pAd)
&& (pAd->StaCfg.PSControl.field.rt30xxPowerMode == 3)
&& (pAd->StaCfg.PSControl.field.EnableNewPS == TRUE)
&& (Command == WAKE_MCU_CMD)) {
RTMPusecDelay(2000);
/*Put this is after RF programming. */
/*NdisAcquireSpinLock(&pAd->McuCmdLock); */
/*pAd->brt30xxBanMcuCmd = FALSE; */
/*NdisReleaseSpinLock(&pAd->McuCmdLock); */
}
#endif /* PCIE_PS_SUPPORT // */
return TRUE;
}