mirror of https://github.com/hak5/openwrt.git
468 lines
14 KiB
C
468 lines
14 KiB
C
|
|
/*
|
|
*
|
|
* Copyright (c) 2004-2007 Atheros Communications Inc.
|
|
* All rights reserved.
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation;
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include "a_config.h"
|
|
#include "athdefs.h"
|
|
#include "a_types.h"
|
|
#include "AR6Khwreg.h"
|
|
#include "targaddrs.h"
|
|
#include "a_osapi.h"
|
|
#include "hif.h"
|
|
#include "htc_api.h"
|
|
#include "bmi.h"
|
|
#include "bmi_msg.h"
|
|
#include "common_drv.h"
|
|
#include "a_debug.h"
|
|
#include "targaddrs.h"
|
|
|
|
#define HOST_INTEREST_ITEM_ADDRESS(target, item) \
|
|
(((TargetType) == TARGET_TYPE_AR6001) ? \
|
|
AR6001_HOST_INTEREST_ITEM_ADDRESS(item) : \
|
|
AR6002_HOST_INTEREST_ITEM_ADDRESS(item))
|
|
|
|
|
|
/* Compile the 4BYTE version of the window register setup routine,
|
|
* This mitigates host interconnect issues with non-4byte aligned bus requests, some
|
|
* interconnects use bus adapters that impose strict limitations.
|
|
* Since diag window access is not intended for performance critical operations, the 4byte mode should
|
|
* be satisfactory even though it generates 4X the bus activity. */
|
|
|
|
#ifdef USE_4BYTE_REGISTER_ACCESS
|
|
|
|
/* set the window address register (using 4-byte register access ). */
|
|
A_STATUS ar6000_SetAddressWindowRegister(HIF_DEVICE *hifDevice, A_UINT32 RegisterAddr, A_UINT32 Address)
|
|
{
|
|
A_STATUS status;
|
|
A_UINT8 addrValue[4];
|
|
int i;
|
|
|
|
/* write bytes 1,2,3 of the register to set the upper address bytes, the LSB is written
|
|
* last to initiate the access cycle */
|
|
|
|
for (i = 1; i <= 3; i++) {
|
|
/* fill the buffer with the address byte value we want to hit 4 times*/
|
|
addrValue[0] = ((A_UINT8 *)&Address)[i];
|
|
addrValue[1] = addrValue[0];
|
|
addrValue[2] = addrValue[0];
|
|
addrValue[3] = addrValue[0];
|
|
|
|
/* hit each byte of the register address with a 4-byte write operation to the same address,
|
|
* this is a harmless operation */
|
|
status = HIFReadWrite(hifDevice,
|
|
RegisterAddr+i,
|
|
addrValue,
|
|
4,
|
|
HIF_WR_SYNC_BYTE_FIX,
|
|
NULL);
|
|
if (status != A_OK) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (status != A_OK) {
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Cannot write initial bytes of 0x%x to window reg: 0x%X \n",
|
|
RegisterAddr, Address));
|
|
return status;
|
|
}
|
|
|
|
/* write the address register again, this time write the whole 4-byte value.
|
|
* The effect here is that the LSB write causes the cycle to start, the extra
|
|
* 3 byte write to bytes 1,2,3 has no effect since we are writing the same values again */
|
|
status = HIFReadWrite(hifDevice,
|
|
RegisterAddr,
|
|
(A_UCHAR *)(&Address),
|
|
4,
|
|
HIF_WR_SYNC_BYTE_INC,
|
|
NULL);
|
|
|
|
if (status != A_OK) {
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Cannot write 0x%x to window reg: 0x%X \n",
|
|
RegisterAddr, Address));
|
|
return status;
|
|
}
|
|
|
|
return A_OK;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
/* set the window address register */
|
|
A_STATUS ar6000_SetAddressWindowRegister(HIF_DEVICE *hifDevice, A_UINT32 RegisterAddr, A_UINT32 Address)
|
|
{
|
|
A_STATUS status;
|
|
|
|
/* write bytes 1,2,3 of the register to set the upper address bytes, the LSB is written
|
|
* last to initiate the access cycle */
|
|
status = HIFReadWrite(hifDevice,
|
|
RegisterAddr+1, /* write upper 3 bytes */
|
|
((A_UCHAR *)(&Address))+1,
|
|
sizeof(A_UINT32)-1,
|
|
HIF_WR_SYNC_BYTE_INC,
|
|
NULL);
|
|
|
|
if (status != A_OK) {
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Cannot write initial bytes of 0x%x to window reg: 0x%X \n",
|
|
RegisterAddr, Address));
|
|
return status;
|
|
}
|
|
|
|
/* write the LSB of the register, this initiates the operation */
|
|
status = HIFReadWrite(hifDevice,
|
|
RegisterAddr,
|
|
(A_UCHAR *)(&Address),
|
|
sizeof(A_UINT8),
|
|
HIF_WR_SYNC_BYTE_INC,
|
|
NULL);
|
|
|
|
if (status != A_OK) {
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Cannot write 0x%x to window reg: 0x%X \n",
|
|
RegisterAddr, Address));
|
|
return status;
|
|
}
|
|
|
|
return A_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Read from the AR6000 through its diagnostic window.
|
|
* No cooperation from the Target is required for this.
|
|
*/
|
|
A_STATUS
|
|
ar6000_ReadRegDiag(HIF_DEVICE *hifDevice, A_UINT32 *address, A_UINT32 *data)
|
|
{
|
|
A_STATUS status;
|
|
|
|
/* set window register to start read cycle */
|
|
status = ar6000_SetAddressWindowRegister(hifDevice,
|
|
WINDOW_READ_ADDR_ADDRESS,
|
|
*address);
|
|
|
|
if (status != A_OK) {
|
|
return status;
|
|
}
|
|
|
|
/* read the data */
|
|
status = HIFReadWrite(hifDevice,
|
|
WINDOW_DATA_ADDRESS,
|
|
(A_UCHAR *)data,
|
|
sizeof(A_UINT32),
|
|
HIF_RD_SYNC_BYTE_INC,
|
|
NULL);
|
|
if (status != A_OK) {
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Cannot read from WINDOW_DATA_ADDRESS\n"));
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write to the AR6000 through its diagnostic window.
|
|
* No cooperation from the Target is required for this.
|
|
*/
|
|
A_STATUS
|
|
ar6000_WriteRegDiag(HIF_DEVICE *hifDevice, A_UINT32 *address, A_UINT32 *data)
|
|
{
|
|
A_STATUS status;
|
|
|
|
/* set write data */
|
|
status = HIFReadWrite(hifDevice,
|
|
WINDOW_DATA_ADDRESS,
|
|
(A_UCHAR *)data,
|
|
sizeof(A_UINT32),
|
|
HIF_WR_SYNC_BYTE_INC,
|
|
NULL);
|
|
if (status != A_OK) {
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Cannot write 0x%x to WINDOW_DATA_ADDRESS\n", *data));
|
|
return status;
|
|
}
|
|
|
|
/* set window register, which starts the write cycle */
|
|
return ar6000_SetAddressWindowRegister(hifDevice,
|
|
WINDOW_WRITE_ADDR_ADDRESS,
|
|
*address);
|
|
}
|
|
|
|
A_STATUS
|
|
ar6000_ReadDataDiag(HIF_DEVICE *hifDevice, A_UINT32 address,
|
|
A_UCHAR *data, A_UINT32 length)
|
|
{
|
|
A_UINT32 count;
|
|
A_STATUS status = A_OK;
|
|
|
|
for (count = 0; count < length; count += 4, address += 4) {
|
|
if ((status = ar6000_ReadRegDiag(hifDevice, &address,
|
|
(A_UINT32 *)&data[count])) != A_OK)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
A_STATUS
|
|
ar6000_WriteDataDiag(HIF_DEVICE *hifDevice, A_UINT32 address,
|
|
A_UCHAR *data, A_UINT32 length)
|
|
{
|
|
A_UINT32 count;
|
|
A_STATUS status = A_OK;
|
|
|
|
for (count = 0; count < length; count += 4, address += 4) {
|
|
if ((status = ar6000_WriteRegDiag(hifDevice, &address,
|
|
(A_UINT32 *)&data[count])) != A_OK)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
A_STATUS
|
|
ar6000_reset_device_skipflash(HIF_DEVICE *hifDevice)
|
|
{
|
|
int i;
|
|
struct forceROM_s {
|
|
A_UINT32 addr;
|
|
A_UINT32 data;
|
|
};
|
|
struct forceROM_s *ForceROM;
|
|
int szForceROM;
|
|
A_UINT32 instruction;
|
|
|
|
static struct forceROM_s ForceROM_REV2[] = {
|
|
/* NB: This works for old REV2 ROM (old). */
|
|
{0x00001ff0, 0x175b0027}, /* jump instruction at 0xa0001ff0 */
|
|
{0x00001ff4, 0x00000000}, /* nop instruction at 0xa0001ff4 */
|
|
|
|
{MC_REMAP_TARGET_ADDRESS, 0x00001ff0}, /* remap to 0xa0001ff0 */
|
|
{MC_REMAP_COMPARE_ADDRESS, 0x01000040},/* ...from 0xbfc00040 */
|
|
{MC_REMAP_SIZE_ADDRESS, 0x00000000}, /* ...1 cache line */
|
|
{MC_REMAP_VALID_ADDRESS, 0x00000001}, /* ...remap is valid */
|
|
|
|
{LOCAL_COUNT_ADDRESS+0x10, 0}, /* clear BMI credit counter */
|
|
|
|
{RESET_CONTROL_ADDRESS, RESET_CONTROL_WARM_RST_MASK},
|
|
};
|
|
|
|
static struct forceROM_s ForceROM_NEW[] = {
|
|
/* NB: This works for AR6000 ROM REV3 and beyond. */
|
|
{LOCAL_SCRATCH_ADDRESS, AR6K_OPTION_IGNORE_FLASH},
|
|
{LOCAL_COUNT_ADDRESS+0x10, 0}, /* clear BMI credit counter */
|
|
{RESET_CONTROL_ADDRESS, RESET_CONTROL_WARM_RST_MASK},
|
|
};
|
|
|
|
/*
|
|
* Examine a semi-arbitrary instruction that's different
|
|
* in REV2 and other revisions.
|
|
* NB: If a Host port does not require simultaneous support
|
|
* for multiple revisions of Target ROM, this code can be elided.
|
|
*/
|
|
(void)ar6000_ReadDataDiag(hifDevice, 0x01000040,
|
|
(A_UCHAR *)&instruction, 4);
|
|
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("instruction=0x%x\n", instruction));
|
|
|
|
if (instruction == 0x3c1aa200) {
|
|
/* It's an old ROM */
|
|
ForceROM = ForceROM_REV2;
|
|
szForceROM = sizeof(ForceROM_REV2)/sizeof(*ForceROM);
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Using OLD method\n"));
|
|
} else {
|
|
ForceROM = ForceROM_NEW;
|
|
szForceROM = sizeof(ForceROM_NEW)/sizeof(*ForceROM);
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Using NEW method\n"));
|
|
}
|
|
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Force Target to execute from ROM....\n"));
|
|
for (i = 0; i < szForceROM; i++)
|
|
{
|
|
if (ar6000_WriteRegDiag(hifDevice,
|
|
&ForceROM[i].addr,
|
|
&ForceROM[i].data) != A_OK)
|
|
{
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Cannot force Target to execute ROM!\n"));
|
|
return A_ERROR;
|
|
}
|
|
}
|
|
|
|
msleep(50); /* delay to allow dragon to come to BMI phase */
|
|
return A_OK;
|
|
}
|
|
|
|
/* reset device */
|
|
A_STATUS ar6000_reset_device(HIF_DEVICE *hifDevice, A_UINT32 TargetType)
|
|
{
|
|
|
|
#if !defined(DWSIM)
|
|
A_STATUS status = A_OK;
|
|
A_UINT32 address;
|
|
A_UINT32 data;
|
|
|
|
do {
|
|
|
|
// address = RESET_CONTROL_ADDRESS;
|
|
data = RESET_CONTROL_COLD_RST_MASK;
|
|
|
|
/* Hardcode the address of RESET_CONTROL_ADDRESS based on the target type */
|
|
if (TargetType == TARGET_TYPE_AR6001) {
|
|
address = 0x0C000000;
|
|
} else {
|
|
if (TargetType == TARGET_TYPE_AR6002) {
|
|
address = 0x00004000;
|
|
} else {
|
|
A_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
status = ar6000_WriteRegDiag(hifDevice, &address, &data);
|
|
|
|
if (A_FAILED(status)) {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Read back the RESET CAUSE register to ensure that the cold reset
|
|
* went through.
|
|
*/
|
|
msleep(2000); /* 2 second delay to allow things to settle down */
|
|
|
|
|
|
// address = RESET_CAUSE_ADDRESS;
|
|
/* Hardcode the address of RESET_CAUSE_ADDRESS based on the target type */
|
|
if (TargetType == TARGET_TYPE_AR6001) {
|
|
address = 0x0C0000CC;
|
|
} else {
|
|
if (TargetType == TARGET_TYPE_AR6002) {
|
|
address = 0x000040C0;
|
|
} else {
|
|
A_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
data = 0;
|
|
status = ar6000_ReadRegDiag(hifDevice, &address, &data);
|
|
|
|
if (A_FAILED(status)) {
|
|
break;
|
|
}
|
|
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Reset Cause readback: 0x%X \n",data));
|
|
data &= RESET_CAUSE_LAST_MASK;
|
|
if (data != 2) {
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Unable to cold reset the target \n"));
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if (A_FAILED(status)) {
|
|
AR_DEBUG_PRINTF(ATH_LOG_ERR, ("Failed to reset target \n"));
|
|
}
|
|
#endif
|
|
return A_OK;
|
|
}
|
|
|
|
#define REG_DUMP_COUNT_AR6001 38 /* WORDs, derived from AR6001_regdump.h */
|
|
#define REG_DUMP_COUNT_AR6002 32 /* WORDs, derived from AR6002_regdump.h */
|
|
|
|
|
|
#if REG_DUMP_COUNT_AR6001 <= REG_DUMP_COUNT_AR6002
|
|
#define REGISTER_DUMP_LEN_MAX REG_DUMP_COUNT_AR6002
|
|
#else
|
|
#define REGISTER_DUMP_LEN_MAX REG_DUMP_COUNT_AR6001
|
|
#endif
|
|
|
|
void ar6000_dump_target_assert_info(HIF_DEVICE *hifDevice, A_UINT32 TargetType)
|
|
{
|
|
A_UINT32 address;
|
|
A_UINT32 regDumpArea = 0;
|
|
A_STATUS status;
|
|
A_UINT32 regDumpValues[REGISTER_DUMP_LEN_MAX];
|
|
A_UINT32 regDumpCount = 0;
|
|
A_UINT32 i;
|
|
|
|
do {
|
|
|
|
/* the reg dump pointer is copied to the host interest area */
|
|
address = HOST_INTEREST_ITEM_ADDRESS(TargetType, hi_failure_state);
|
|
|
|
if (TargetType == TARGET_TYPE_AR6001) {
|
|
/* for AR6001, this is a fixed location because the ptr is actually stuck in cache,
|
|
* this may be fixed in later firmware versions */
|
|
address = 0x18a0;
|
|
regDumpCount = REG_DUMP_COUNT_AR6001;
|
|
|
|
} else if (TargetType == TARGET_TYPE_AR6002) {
|
|
|
|
regDumpCount = REG_DUMP_COUNT_AR6002;
|
|
|
|
} else {
|
|
A_ASSERT(0);
|
|
}
|
|
|
|
/* read RAM location through diagnostic window */
|
|
status = ar6000_ReadRegDiag(hifDevice, &address, ®DumpArea);
|
|
|
|
if (A_FAILED(status)) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR6K: Failed to get ptr to register dump area \n"));
|
|
break;
|
|
}
|
|
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR6K: Location of register dump data: 0x%X \n",regDumpArea));
|
|
|
|
if (regDumpArea == 0) {
|
|
/* no reg dump */
|
|
break;
|
|
}
|
|
|
|
if (TargetType == TARGET_TYPE_AR6001) {
|
|
regDumpArea &= 0x0FFFFFFF; /* convert to physical address in target memory */
|
|
}
|
|
|
|
/* fetch register dump data */
|
|
status = ar6000_ReadDataDiag(hifDevice,
|
|
regDumpArea,
|
|
(A_UCHAR *)®DumpValues[0],
|
|
regDumpCount * (sizeof(A_UINT32)));
|
|
|
|
if (A_FAILED(status)) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR6K: Failed to get register dump \n"));
|
|
break;
|
|
}
|
|
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR6K: Register Dump: \n"));
|
|
|
|
for (i = 0; i < regDumpCount; i++) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" %d : 0x%8.8X \n",i, regDumpValues[i]));
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
}
|
|
|