// SPDX-License-Identifier: GPL-2.0-only
/*
* QLogic iSCSI HBA Driver
* Copyright (c) 2003-2013 QLogic Corporation
*/
#include <linux/ratelimit.h>
#include "ql4_def.h"
#include "ql4_version.h"
#include "ql4_glbl.h"
#include "ql4_dbg.h"
#include "ql4_inline.h"
uint32_t qla4_83xx_rd_reg(struct scsi_qla_host *ha, ulong addr)
{
return readl((void __iomem *)(ha->nx_pcibase + addr));
}
void qla4_83xx_wr_reg(struct scsi_qla_host *ha, ulong addr, uint32_t val)
{
writel(val, (void __iomem *)(ha->nx_pcibase + addr));
}
static int qla4_83xx_set_win_base(struct scsi_qla_host *ha, uint32_t addr)
{
uint32_t val;
int ret_val = QLA_SUCCESS;
qla4_83xx_wr_reg(ha, QLA83XX_CRB_WIN_FUNC(ha->func_num), addr);
val = qla4_83xx_rd_reg(ha, QLA83XX_CRB_WIN_FUNC(ha->func_num));
if (val != addr) {
ql4_printk(KERN_ERR, ha, "%s: Failed to set register window : addr written 0x%x, read 0x%x!\n",
__func__, addr, val);
ret_val = QLA_ERROR;
}
return ret_val;
}
int qla4_83xx_rd_reg_indirect(struct scsi_qla_host *ha, uint32_t addr,
uint32_t *data)
{
int ret_val;
ret_val = qla4_83xx_set_win_base(ha, addr);
if (ret_val == QLA_SUCCESS) {
*data = qla4_83xx_rd_reg(ha, QLA83XX_WILDCARD);
} else {
*data = 0xffffffff;
ql4_printk(KERN_ERR, ha, "%s: failed read of addr 0x%x!\n",
__func__, addr);
}
return ret_val;
}
int qla4_83xx_wr_reg_indirect(struct scsi_qla_host *ha, uint32_t addr,
uint32_t data)
{
int ret_val;
ret_val = qla4_83xx_set_win_base(ha, addr);
if (ret_val == QLA_SUCCESS)
qla4_83xx_wr_reg(ha, QLA83XX_WILDCARD, data);
else
ql4_printk(KERN_ERR, ha, "%s: failed wrt to addr 0x%x, data 0x%x\n",
__func__, addr, data);
return ret_val;
}
static int qla4_83xx_flash_lock(struct scsi_qla_host *ha)
{
int lock_owner;
int timeout = 0;
uint32_t lock_status = 0;
int ret_val = QLA_SUCCESS;
while (lock_status == 0) {
lock_status = qla4_83xx_rd_reg(ha, QLA83XX_FLASH_LOCK);
if (lock_status)
break;
if (++timeout >= QLA83XX_FLASH_LOCK_TIMEOUT / 20) {
lock_owner = qla4_83xx_rd_reg(ha,
QLA83XX_FLASH_LOCK_ID);
ql4_printk(KERN_ERR, ha, "%s: flash lock by func %d failed, held by func %d\n",
__func__, ha->func_num, lock_owner);
ret_val = QLA_ERROR;
break;
}
msleep(20);
}
qla4_83xx_wr_reg(ha, QLA83XX_FLASH_LOCK_ID, ha->func_num);
return ret_val;
}
static void qla4_83xx_flash_unlock(struct scsi_qla_host *ha)
{
/* Reading FLASH_UNLOCK register unlocks the Flash */
qla4_83xx_wr_reg(ha, QLA83XX_FLASH_LOCK_ID, 0xFF);
qla4_83xx_rd_reg(ha, QLA83XX_FLASH_UNLOCK);
}
int qla4_83xx_flash_read_u32(struct scsi_qla_host *ha, uint32_t flash_addr,
uint8_t *p_data, int u32_word_count)
{
int i;
uint32_t u32_word;
uint32_t addr = flash_addr;
int ret_val = QLA_SUCCESS;
ret_val = qla4_83xx_flash_lock(ha);
if (ret_val == QLA_ERROR)
goto exit_lock_error;
if (addr & 0x03) {
ql4_printk(KERN_ERR, ha, "%s: Illegal addr = 0x%x\n",
__func__, addr);
ret_val = QLA_ERROR;
goto exit_flash_read;
}
for (i = 0; i < u32_word_count; i++) {
ret_val = qla4_83xx_wr_reg_indirect(ha,
QLA83XX_FLASH_DIRECT_WINDOW,
(addr & 0xFFFF0000));
if (ret_val == QLA_ERROR) {
ql4_printk(KERN_ERR, ha, "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW\n!",
__func__, addr);
goto exit_flash_read;
}
ret_val = qla4_83xx_rd_reg_indirect(ha,
QLA83XX_FLASH_DIRECT_DATA(addr),
&u32_word);
if (ret_val == QLA_ERROR) {
ql4_printk(KERN_ERR, ha, "%s: failed to read addr 0x%x!\n",
__func__, addr);
goto exit_flash_read;
}
*(__le32 *)p_data = le32_to_cpu(u32_word);
p_data = p_data + 4;
addr = addr + 4;
}
exit_flash_read:
qla4_83xx_flash_unlock(ha);
exit_lock_error:
return ret_val;
}
int qla4_83xx_lockless_flash_read_u32(struct scsi_qla_host *ha,
uint32_t flash_addr, uint8_t *p_data,
int u32_word_count)
{
uint32_t i;
uint32_t u32_word;
uint32_t flash_offset;
uint32_t addr = flash_addr;
int ret_val = QLA_SUCCESS;
flash_offset = addr & (QLA83XX_FLASH_SECTOR_SIZE - 1);
if (addr & 0x3) {
ql4_printk(KERN_ERR, ha, "%s: Illegal addr = 0x%x\n",
__func__, addr);
ret_val = QLA_ERROR;
goto exit_lockless_read;
}
ret_val = qla4_83xx_wr_reg_indirect(ha, QLA83XX_FLASH_DIRECT_WINDOW,
addr);
if (ret_val == QLA_ERROR) {
ql4_printk(KERN_ERR, ha, "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW!\n",
__func__, addr);
goto exit_lockless_read;
}
/* Check if data is spread across multiple sectors */
if ((flash_offset + (u32_word_count * sizeof(uint32_t))) >
(QLA83XX_FLASH_SECTOR_SIZE - 1)) {
/* Multi sector read */
for (i = 0; i < u32_word_count; i++) {
ret_val = qla4_83xx_rd_reg_indirect(ha,
QLA83XX_FLASH_DIRECT_DATA(addr),
&u32_word);
if (ret_val == QLA_ERROR) {
ql4_printk(KERN_ERR, ha, "%s: failed to read addr 0x%x!\n",
__func__, addr);
goto exit_lockless_read;
}
*(__le32 *)p_data = le32_to_cpu(u32_word);
p_data = p_data + 4;
addr = addr + 4;
flash_offset = flash_offset + 4;
if (flash_offset > (QLA83XX_FLASH_SECTOR_SIZE - 1)) {
/* This write is needed once for each sector */
ret_val = qla4_83xx_wr_reg_indirect(ha,
QLA83XX_FLASH_DIRECT_WINDOW,
add
|