root/usr/src/uts/common/io/bnxe/577xx/drivers/common/lm/device/lm_nvram.c
 /******************************************************************************
 * 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 */