/******************************************************************************
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2014 QLogic Corporation
* The contents of this file are subject to the terms of the
* QLogic End User License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License at
* http://www.qlogic.com/Resources/Documents/DriverDownloadHelp/
* QLogic_End_User_Software_License.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
*
* Module Description:
*
*
* History:
* 03/21/03 Hav Khauv Inception.
******************************************************************************/
#include "lm5710.h"
#define NVRAM_TIMEOUT_COUNT 100000
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
static lm_status_t
acquire_nvram_lock(
lm_device_t *pdev)
{
lm_status_t lm_status;
u32_t j, cnt;
u32_t val;
u8_t port_num = PORT_ID(pdev); /* TBD - E1H: nvram lock � DOES NOT scale to 8 functions! (only 4 clients)
* 1. Can we assume no concurrent access by control applications?
* 2. If not, the MISC lock is our backup */
DbgMessage(pdev, VERBOSEnv, "### acquire_nvram_lock\n");
/* Adjust timeout for emulation/FPGA */
cnt = NVRAM_TIMEOUT_COUNT;
if (CHIP_REV_IS_EMUL(pdev)) cnt *= 100;
val = 0;
/* Request access to the flash interface. */
REG_WR(pdev, MCP_REG_MCPR_NVM_SW_ARB, (MCPR_NVM_SW_ARB_ARB_REQ_SET1 << port_num ));
for(j = 0; j < cnt*10; j++)
{
val=REG_RD(pdev, MCP_REG_MCPR_NVM_SW_ARB);
if(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port_num))
{
break;
}
mm_wait(pdev, 5);
}
if(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port_num))
{
lm_status = LM_STATUS_SUCCESS;
}
else
{
DbgMessage(NULL, FATAL, "Value of MCP_REG_MCPR_NVM_SW_ARB is 0x%x\n", val);
DbgBreakMsg("Cannot get access to nvram interface.\n");
lm_status = LM_STATUS_BUSY;
}
return lm_status;
} /* acquire_nvram_lock */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
static void
release_nvram_lock(
lm_device_t *pdev)
{
u32_t j, cnt;
u32_t val;
u8_t port_num = PORT_ID(pdev);
DbgMessage(pdev, VERBOSEnv, "### release_nvram_lock\n");
/* Adjust timeout for emulation/FPGA */
cnt = NVRAM_TIMEOUT_COUNT;
if (CHIP_REV_IS_EMUL(pdev)) cnt *= 100;
/* Relinquish nvram interface. */
REG_WR(pdev, MCP_REG_MCPR_NVM_SW_ARB, (MCPR_NVM_SW_ARB_ARB_REQ_CLR1 << port_num));
val = 0;
for(j = 0; j < cnt; j++)
{
val=REG_RD(pdev, MCP_REG_MCPR_NVM_SW_ARB);
if(!(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port_num)))
{
break;
}
mm_wait(pdev, 5);
}
DbgBreakIf(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port_num));
} /* release_nvram_lock */
#if 0
/*******************************************************************************
* Description:
*
* Return:
*
******************************************************************************/
static lm_status_t
enable_nvram_write(
lm_device_t *pdev)
{
u32_t val, j, cnt;
lm_status_t lm_status;
lm_status = LM_STATUS_SUCCESS;
DbgMessage(pdev, INFORMnv, "### enable_nvram_write\n");
/* Need to clear DONE bit separately. */
REG_WR(pdev, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE);
/* Issue a write enable command. */
REG_WR(pdev, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DOIT | MCPR_NVM_COMMAND_WREN);
/* Adjust timeout for emulation/FPGA */
cnt = NVRAM_TIMEOUT_COUNT;
if (CHIP_REV(pdev) == CHIP_REV_EMUL) cnt *= 100;
lm_status = LM_STATUS_BUSY;
for(j = 0; j < cnt; j++)
{
mm_wait(pdev, 5);
val=REG_RD(pdev, MCP_REG_MCPR_NVM_COMMAND);
if(val & MCPR_NVM_COMMAND_DONE)
{
lm_status = LM_STATUS_SUCCESS;
break;
}
}
return lm_status;
} /* enable_nvram_write */
/*******************************************************************************
* Description:
*
* Return:
*
******************************************************************************/
static lm_status_t
disable_nvram_write(
lm_device_t *pdev)
{
lm_status_t lm_status;
u32_t cnt,j,val;
DbgMessage(pdev, INFORMnv, "### disable_nvram_write\n");
/* Need to clear DONE bit separately. */
REG_WR(pdev, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE);
/* Issue a write disable command. */
REG_WR(pdev, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DOIT | MCPR_NVM_COMMAND_WRDI);
/* Adjust timeout for emulation/FPGA */
cnt = NVRAM_TIMEOUT_COUNT;
if (CHIP_REV(pdev) == CHIP_REV_EMUL) cnt *= 100;
lm_status = LM_STATUS_BUSY;
for(j = 0; j < cnt; j++)
{
mm_wait(pdev, 5);
val=REG_RD(pdev, MCP_REG_MCPR_NVM_COMMAND);
if(val & MCPR_NVM_COMMAND_DONE)
{
lm_status = LM_STATUS_SUCCESS;
break;
}
}
return lm_status;
} /* disable_nvram_write */
#endif /* 0 */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
static lm_status_t
enable_nvram_access(
lm_device_t *pdev)
{
u32_t val;
DbgMessage(pdev, VERBOSEnv, "### enable_nvram_access\n");
val=REG_RD(pdev, MCP_REG_MCPR_NVM_ACCESS_ENABLE);
/* Enable both bits, even on read. */
REG_WR(pdev, MCP_REG_MCPR_NVM_ACCESS_ENABLE, val | MCPR_NVM_ACCESS_ENABLE_EN | MCPR_NVM_ACCESS_ENABLE_WR_EN);
return LM_STATUS_SUCCESS;
} /* enable_nvram_access */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
static lm_status_t
disable_nvram_access(
lm_device_t *pdev)
{
u32_t val;
DbgMessage(pdev, VERBOSEnv, "### disable_nvram_access\n");
val=REG_RD(pdev, MCP_REG_MCPR_NVM_ACCESS_ENABLE);
/* Disable both bits, even after read. */
REG_WR(pdev, MCP_REG_MCPR_NVM_ACCESS_ENABLE, val & ~(MCPR_NVM_ACCESS_ENABLE_EN | MCPR_NVM_ACCESS_ENABLE_WR_EN));
return LM_STATUS_SUCCESS;
} /* disable_nvram_access */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
static lm_status_t
nvram_read_dword(
lm_device_t *pdev,
u32_t offset,
u32_t *ret_val,
u32_t nvram_flags)
{
lm_status_t lm_status;
u32_t cmd_flags;
u32_t val;
u32_t j, cnt;
DbgMessage(pdev, VERBOSEnv, "### nvram_read_dword\n");
DbgMessage(pdev, VERBOSEnv, "offset %d flags %d\n",offset,nvram_flags);
/* Build the command word. */
cmd_flags = nvram_flags | MCPR_NVM_COMMAND_DOIT;
/* Need to clear DONE bit separately. */
REG_WR(pdev, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE);
/* Address of the NVRAM to read from. */
REG_WR(pdev, MCP_REG_MCPR_NVM_ADDR, offset & MCPR_NVM_ADDR_NVM_ADDR_VALUE);
/* Issue a read command. */
REG_WR(pdev, MCP_REG_MCPR_NVM_COMMAND, cmd_flags);
/* Adjust timeout for emulation/FPGA */
cnt = NVRAM_TIMEOUT_COUNT;
if (CHIP_REV_IS_EMUL(pdev)) cnt *= 100;
/* Wait for completion. */
lm_status = LM_STATUS_BUSY;
for(j = 0; j < cnt; j++)
{
mm_wait(pdev, 5);
val=REG_RD(pdev, MCP_REG_MCPR_NVM_COMMAND);
if(val & MCPR_NVM_COMMAND_DONE)
{
val=REG_RD(pdev, MCP_REG_MCPR_NVM_READ);
/* Change to little endian. */
#if defined(LITTLE_ENDIAN)
val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) |
((val & 0xff0000) >> 8) | ((val >> 24) & 0xff);
#endif
*ret_val = val;
lm_status = LM_STATUS_SUCCESS;
break;
}
}
return lm_status;
} /* nvram_read_dword */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
static lm_status_t
nvram_write_dword(
lm_device_t *pdev,
u32_t offset,
u32_t val,
u32_t nvram_flags)
{
lm_status_t lm_status;
u32_t cmd_flags;
u32_t j, cnt;
DbgMessage(pdev, VERBOSEnv, "### nvram_write_dword\n");
/* Build the command word. */
cmd_flags = nvram_flags | MCPR_NVM_COMMAND_DOIT | MCPR_NVM_COMMAND_WR;
/* Change to little endian. */
#if defined(LITTLE_ENDIAN)
val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) |
((val & 0xff0000) >> 8) | ((val >> 24) & 0xff);
#endif
/* Need to clear DONE bit separately. */
REG_WR(pdev, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE);
/* Write the data. */
REG_WR(pdev, MCP_REG_MCPR_NVM_WRITE, val);
/* Address of the NVRAM to write to. */
REG_WR(pdev, MCP_REG_MCPR_NVM_ADDR, offset & MCPR_NVM_ADDR_NVM_ADDR_VALUE);
/* Issue the write command. */
REG_WR(pdev, MCP_REG_MCPR_NVM_COMMAND, cmd_flags);
/* Adjust timeout for emulation/FPGA */
cnt = NVRAM_TIMEOUT_COUNT;
if (CHIP_REV_IS_EMUL(pdev)) cnt *= 100;
/* Wait for completion. */
lm_status = LM_STATUS_BUSY;
for(j = 0; j < cnt; j++)
{
mm_wait(pdev, 5);
val=REG_RD(pdev, MCP_REG_MCPR_NVM_COMMAND);
if(val & MCPR_NVM_COMMAND_DONE)
{
lm_status = LM_STATUS_SUCCESS;
break;
}
}
return lm_status;
} /* nvram_write_dword */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
lm_status_t
lm_nvram_read(
lm_device_t *pdev,
u32_t offset,
u32_t *ret_buf,
u32_t buf_size)
{
lm_status_t lm_status;
u32_t cmd_flags;
DbgMessage(pdev, VERBOSEnv, "### lm_nvram_read\n");
DbgMessage(pdev, VERBOSEnv, "offset %d size %d\n",offset,buf_size);
if((buf_size & 0x03) || (offset & 0x03))
{
DbgBreakMsg("Invalid paramter.\n");
return LM_STATUS_FAILURE;
}
// TODO what is the nvram total size
if(offset + buf_size > pdev->hw_info.flash_spec.total_size)
{
DbgBreakMsg("Invalid paramter.\n");
return LM_STATUS_FAILURE;
}
/* Request access to the flash interface. */
lm_status = acquire_nvram_lock(pdev);
if(lm_status != LM_STATUS_SUCCESS)
{
return lm_status;
}
/* Enable access to flash interface */
lm_status = enable_nvram_access(pdev);
if(lm_status != LM_STATUS_SUCCESS)
{
release_nvram_lock(pdev);
return lm_status;
}
/* Read the first word. */
cmd_flags = MCPR_NVM_COMMAND_FIRST;
while(buf_size > sizeof(u32_t) && lm_status == LM_STATUS_SUCCESS)
{
lm_status = nvram_read_dword(pdev, offset, ret_buf, cmd_flags);
/* Advance to the next dword. */
offset += sizeof(u32_t);
ret_buf++;
buf_size -= sizeof(u32_t);
cmd_flags = 0;
}
if(lm_status == LM_STATUS_SUCCESS)
{
cmd_flags |= MCPR_NVM_COMMAND_LAST;
lm_status = nvram_read_dword(pdev, offset, ret_buf, cmd_flags);
}
/* Disable access to flash interface */
disable_nvram_access(pdev);
release_nvram_lock(pdev);
return lm_status;
} /* lm_nvram_read */
/*******************************************************************************
* Description:
*
* Return:
******************************************************************************/
lm_status_t
lm_nvram_write(
lm_device_t *pdev,
u32_t offset,
u32_t *data_buf,
u32_t buf_size)
{
lm_status_t lm_status;
u32_t written_so_far;
u32_t cmd_flags;
u32_t *ptr32, addr;
DbgMessage(pdev, VERBOSEnv, "### lm_nvram_write\n");
if(offset & 0x03)
{
DbgBreakMsg("Invalid paramter.\n");
return LM_STATUS_FAILURE;
}
// TODO what is the nvram total size
if(offset + buf_size > pdev->hw_info.flash_spec.total_size)
{
DbgMessage(pdev, FATAL, "lm_nvram_write failed ! buf_size %d larger than NVM total_size %d\n", buf_size, pdev->hw_info.flash_spec.total_size);
DbgBreakMsg("Failed to write to NVM! Attemp to write to offset larger than NVM total size !\n");
return LM_STATUS_FAILURE;
}
lm_status = LM_STATUS_SUCCESS;
/* Request access to the flash interface. */
lm_status = acquire_nvram_lock(pdev);
if(lm_status != LM_STATUS_SUCCESS)
return lm_status;
/* Enable access to flash interface */
lm_status = enable_nvram_access(pdev);
if(lm_status != LM_STATUS_SUCCESS)
{
release_nvram_lock(pdev);
return lm_status;
}
written_so_far = 0;
cmd_flags = MCPR_NVM_COMMAND_FIRST;
addr = offset;
ptr32 = data_buf;
while (written_so_far < buf_size)
{
if (written_so_far == (buf_size - 4))
cmd_flags |= MCPR_NVM_COMMAND_LAST;
else if (((addr & 0xff) + 4) == NVRAM_PAGE_SIZE) // else if (((addr + 4) % NVRAM_PAGE_SIZE) == 0)
cmd_flags |= MCPR_NVM_COMMAND_LAST;
else if ((addr & 0xff) == 0) // else if ((addr % NVRAM_PAGE_SIZE) == 0)
cmd_flags |= MCPR_NVM_COMMAND_FIRST;
nvram_write_dword(pdev, addr, *ptr32, cmd_flags);
ptr32++;
addr += 4;
written_so_far += 4;
cmd_flags = 0;
}
/* Disable access to flash interface */
disable_nvram_access(pdev);
release_nvram_lock(pdev);
return lm_status;
} /* lm_nvram_write */