Commit 338c9161 authored by Andrew Vasquez's avatar Andrew Vasquez Committed by James Bottomley

[SCSI] qla2xxx: Add flash burst-read/write support.

Newer ISPs support a mechanism to read and write flash-memory via
the firmware LOAD/DUMP memory mailbox command routines.  When
supported, utilizing these mechanisms significantly reduces
overall access times.
Signed-off-by: default avatarAndrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent c81d04c9
...@@ -133,6 +133,9 @@ int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t); ...@@ -133,6 +133,9 @@ int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
extern int extern int
qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t); qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
extern int
qla2x00_dump_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
extern int extern int
qla2x00_execute_fw(scsi_qla_host_t *, uint32_t); qla2x00_execute_fw(scsi_qla_host_t *, uint32_t);
...@@ -302,6 +305,8 @@ extern uint8_t *qla24xx_read_optrom_data(struct scsi_qla_host *, uint8_t *, ...@@ -302,6 +305,8 @@ extern uint8_t *qla24xx_read_optrom_data(struct scsi_qla_host *, uint8_t *,
uint32_t, uint32_t); uint32_t, uint32_t);
extern int qla24xx_write_optrom_data(struct scsi_qla_host *, uint8_t *, extern int qla24xx_write_optrom_data(struct scsi_qla_host *, uint8_t *,
uint32_t, uint32_t); uint32_t, uint32_t);
extern uint8_t *qla25xx_read_optrom_data(struct scsi_qla_host *, uint8_t *,
uint32_t, uint32_t);
extern int qla2x00_get_flash_version(scsi_qla_host_t *, void *); extern int qla2x00_get_flash_version(scsi_qla_host_t *, void *);
extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *); extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *);
......
...@@ -2980,3 +2980,51 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format, ...@@ -2980,3 +2980,51 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
return rval; return rval;
} }
int
qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr,
uint32_t size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
if (MSW(addr) || IS_FWI2_CAPABLE(ha)) {
mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED;
mcp->mb[8] = MSW(addr);
mcp->out_mb = MBX_8|MBX_0;
} else {
mcp->mb[0] = MBC_DUMP_RISC_RAM;
mcp->out_mb = MBX_0;
}
mcp->mb[1] = LSW(addr);
mcp->mb[2] = MSW(req_dma);
mcp->mb[3] = LSW(req_dma);
mcp->mb[6] = MSW(MSD(req_dma));
mcp->mb[7] = LSW(MSD(req_dma));
mcp->out_mb |= MBX_7|MBX_6|MBX_3|MBX_2|MBX_1;
if (IS_FWI2_CAPABLE(ha)) {
mcp->mb[4] = MSW(size);
mcp->mb[5] = LSW(size);
mcp->out_mb |= MBX_5|MBX_4;
} else {
mcp->mb[4] = LSW(size);
mcp->out_mb |= MBX_4;
}
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
ha->host_no, rval, mcp->mb[0]));
} else {
DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
}
return rval;
}
...@@ -1384,7 +1384,7 @@ static struct isp_operations qla25xx_isp_ops = { ...@@ -1384,7 +1384,7 @@ static struct isp_operations qla25xx_isp_ops = {
.beacon_on = qla24xx_beacon_on, .beacon_on = qla24xx_beacon_on,
.beacon_off = qla24xx_beacon_off, .beacon_off = qla24xx_beacon_off,
.beacon_blink = qla24xx_beacon_blink, .beacon_blink = qla24xx_beacon_blink,
.read_optrom = qla24xx_read_optrom_data, .read_optrom = qla25xx_read_optrom_data,
.write_optrom = qla24xx_write_optrom_data, .write_optrom = qla24xx_write_optrom_data,
.get_flash_version = qla24xx_get_flash_version, .get_flash_version = qla24xx_get_flash_version,
}; };
......
...@@ -425,6 +425,9 @@ qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat) ...@@ -425,6 +425,9 @@ qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat)
/* Flash Manipulation Routines */ /* Flash Manipulation Routines */
/*****************************************************************************/ /*****************************************************************************/
#define OPTROM_BURST_SIZE 0x1000
#define OPTROM_BURST_DWORDS (OPTROM_BURST_SIZE / 4)
static inline uint32_t static inline uint32_t
flash_conf_to_access_addr(uint32_t faddr) flash_conf_to_access_addr(uint32_t faddr)
{ {
...@@ -544,41 +547,59 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, ...@@ -544,41 +547,59 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
uint32_t dwords) uint32_t dwords)
{ {
int ret; int ret;
uint32_t liter; uint32_t liter, miter;
uint32_t sec_mask, rest_addr, conf_addr, sec_end_mask; uint32_t sec_mask, rest_addr, conf_addr;
uint32_t fdata, findex ; uint32_t fdata, findex ;
uint8_t man_id, flash_id; uint8_t man_id, flash_id;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
dma_addr_t optrom_dma;
void *optrom = NULL;
uint32_t *s, *d;
ret = QLA_SUCCESS; ret = QLA_SUCCESS;
/* Prepare burst-capable write on supported ISPs. */
if (IS_QLA25XX(ha) && !(faddr & ~OPTROM_BURST_SIZE) &&
dwords > OPTROM_BURST_DWORDS) {
optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
&optrom_dma, GFP_KERNEL);
if (!optrom) {
qla_printk(KERN_DEBUG, ha,
"Unable to allocate memory for optrom burst write "
"(%x KB).\n", OPTROM_BURST_SIZE / 1024);
}
}
qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id); qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id);
DEBUG9(printk("%s(%ld): Flash man_id=%d flash_id=%d\n", __func__, DEBUG9(printk("%s(%ld): Flash man_id=%d flash_id=%d\n", __func__,
ha->host_no, man_id, flash_id)); ha->host_no, man_id, flash_id));
sec_end_mask = 0;
conf_addr = flash_conf_to_access_addr(0x03d8); conf_addr = flash_conf_to_access_addr(0x03d8);
switch (man_id) { switch (man_id) {
case 0xbf: /* STT flash. */ case 0xbf: /* STT flash. */
rest_addr = 0x1fff; if (flash_id == 0x8e) {
sec_mask = 0x3e000; rest_addr = 0x3fff;
sec_mask = 0x7c000;
} else {
rest_addr = 0x1fff;
sec_mask = 0x7e000;
}
if (flash_id == 0x80) if (flash_id == 0x80)
conf_addr = flash_conf_to_access_addr(0x0352); conf_addr = flash_conf_to_access_addr(0x0352);
break; break;
case 0x13: /* ST M25P80. */ case 0x13: /* ST M25P80. */
rest_addr = 0x3fff; rest_addr = 0x3fff;
sec_mask = 0x3c000; sec_mask = 0x7c000;
break; break;
case 0x1f: // Atmel 26DF081A case 0x1f: // Atmel 26DF081A
rest_addr = 0x0fff; rest_addr = 0x3fff;
sec_mask = 0xff000; sec_mask = 0x7c000;
sec_end_mask = 0x003ff;
conf_addr = flash_conf_to_access_addr(0x0320); conf_addr = flash_conf_to_access_addr(0x0320);
break; break;
default: default:
/* Default to 64 kb sector size. */ /* Default to 64 kb sector size. */
rest_addr = 0x3fff; rest_addr = 0x3fff;
sec_mask = 0x3c000; sec_mask = 0x7c000;
break; break;
} }
...@@ -592,56 +613,81 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, ...@@ -592,56 +613,81 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
/* Some flash parts need an additional zero-write to clear bits.*/ /* Some flash parts need an additional zero-write to clear bits.*/
qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0); qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0);
do { /* Loop once to provide quick error exit. */ for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) {
for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) { if (man_id == 0x1f) {
if (man_id == 0x1f) { findex = faddr << 2;
findex = faddr << 2; fdata = findex & sec_mask;
fdata = findex & sec_mask; } else {
} else { findex = faddr;
findex = faddr; fdata = (findex & sec_mask) << 2;
fdata = (findex & sec_mask) << 2; }
}
/* Are we at the beginning of a sector? */ /* Are we at the beginning of a sector? */
if ((findex & rest_addr) == 0) { if ((findex & rest_addr) == 0) {
/* /* Do sector unprotect at 4K boundry for Atmel part. */
* Do sector unprotect at 4K boundry for Atmel if (man_id == 0x1f)
* part. qla24xx_write_flash_dword(ha,
*/ flash_conf_to_access_addr(0x0339),
if (man_id == 0x1f) (fdata & 0xff00) | ((fdata << 16) &
qla24xx_write_flash_dword(ha,
flash_conf_to_access_addr(0x0339),
(fdata & 0xff00) | ((fdata << 16) &
0xff0000) | ((fdata >> 16) & 0xff));
ret = qla24xx_write_flash_dword(ha, conf_addr,
(fdata & 0xff00) |((fdata << 16) &
0xff0000) | ((fdata >> 16) & 0xff)); 0xff0000) | ((fdata >> 16) & 0xff));
if (ret != QLA_SUCCESS) { ret = qla24xx_write_flash_dword(ha, conf_addr,
DEBUG9(printk("%s(%ld) Unable to flash " (fdata & 0xff00) |((fdata << 16) &
"sector: address=%x.\n", __func__, 0xff0000) | ((fdata >> 16) & 0xff));
ha->host_no, faddr)); if (ret != QLA_SUCCESS) {
break; DEBUG9(printk("%s(%ld) Unable to flash "
} "sector: address=%x.\n", __func__,
ha->host_no, faddr));
break;
} }
ret = qla24xx_write_flash_dword(ha, }
/* Go with burst-write. */
if (optrom && (liter + OPTROM_BURST_DWORDS) < dwords) {
/* Copy data to DMA'ble buffer. */
for (miter = 0, s = optrom, d = dwptr;
miter < OPTROM_BURST_DWORDS; miter++, s++, d++)
*s = cpu_to_le32(*d);
ret = qla2x00_load_ram(ha, optrom_dma,
flash_data_to_access_addr(faddr), flash_data_to_access_addr(faddr),
cpu_to_le32(*dwptr)); OPTROM_BURST_DWORDS);
if (ret != QLA_SUCCESS) { if (ret != QLA_SUCCESS) {
DEBUG9(printk("%s(%ld) Unable to program flash " qla_printk(KERN_WARNING, ha,
"address=%x data=%x.\n", __func__, "Unable to burst-write optrom segment "
ha->host_no, faddr, *dwptr)); "(%x/%x/%llx).\n", ret,
break; flash_data_to_access_addr(faddr),
optrom_dma);
qla_printk(KERN_WARNING, ha,
"Reverting to slow-write.\n");
dma_free_coherent(&ha->pdev->dev,
OPTROM_BURST_SIZE, optrom, optrom_dma);
optrom = NULL;
} else {
liter += OPTROM_BURST_DWORDS - 1;
faddr += OPTROM_BURST_DWORDS - 1;
dwptr += OPTROM_BURST_DWORDS - 1;
continue;
} }
}
/* Do sector protect at 4K boundry for Atmel part. */ ret = qla24xx_write_flash_dword(ha,
if (man_id == 0x1f && flash_data_to_access_addr(faddr), cpu_to_le32(*dwptr));
((faddr & sec_end_mask) == 0x3ff)) if (ret != QLA_SUCCESS) {
qla24xx_write_flash_dword(ha, DEBUG9(printk("%s(%ld) Unable to program flash "
flash_conf_to_access_addr(0x0336), "address=%x data=%x.\n", __func__,
(fdata & 0xff00) | ((fdata << 16) & ha->host_no, faddr, *dwptr));
0xff0000) | ((fdata >> 16) & 0xff)); break;
} }
} while (0);
/* Do sector protect at 4K boundry for Atmel part. */
if (man_id == 0x1f &&
((faddr & rest_addr) == rest_addr))
qla24xx_write_flash_dword(ha,
flash_conf_to_access_addr(0x0336),
(fdata & 0xff00) | ((fdata << 16) &
0xff0000) | ((fdata >> 16) & 0xff));
}
/* Enable flash write-protection. */ /* Enable flash write-protection. */
qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0x9c); qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0x9c);
...@@ -651,6 +697,10 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, ...@@ -651,6 +697,10 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
RD_REG_DWORD(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE); RD_REG_DWORD(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE);
RD_REG_DWORD(&reg->ctrl_status); /* PCI Posting. */ RD_REG_DWORD(&reg->ctrl_status); /* PCI Posting. */
if (optrom)
dma_free_coherent(&ha->pdev->dev,
OPTROM_BURST_SIZE, optrom, optrom_dma);
return ret; return ret;
} }
...@@ -1728,7 +1778,6 @@ qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, ...@@ -1728,7 +1778,6 @@ qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
{ {
/* Suspend HBA. */ /* Suspend HBA. */
scsi_block_requests(ha->host); scsi_block_requests(ha->host);
ha->isp_ops->disable_intrs(ha);
set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
/* Go with read. */ /* Go with read. */
...@@ -1736,7 +1785,6 @@ qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, ...@@ -1736,7 +1785,6 @@ qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
/* Resume HBA. */ /* Resume HBA. */
clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
ha->isp_ops->enable_intrs(ha);
scsi_unblock_requests(ha->host); scsi_unblock_requests(ha->host);
return buf; return buf;
...@@ -1750,7 +1798,6 @@ qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, ...@@ -1750,7 +1798,6 @@ qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
/* Suspend HBA. */ /* Suspend HBA. */
scsi_block_requests(ha->host); scsi_block_requests(ha->host);
ha->isp_ops->disable_intrs(ha);
set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags); set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
/* Go with write. */ /* Go with write. */
...@@ -1767,6 +1814,70 @@ qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, ...@@ -1767,6 +1814,70 @@ qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
return rval; return rval;
} }
uint8_t *
qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
int rval;
dma_addr_t optrom_dma;
void *optrom;
uint8_t *pbuf;
uint32_t faddr, left, burst;
if (offset & ~OPTROM_BURST_SIZE)
goto slow_read;
if (length < OPTROM_BURST_SIZE)
goto slow_read;
optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
&optrom_dma, GFP_KERNEL);
if (!optrom) {
qla_printk(KERN_DEBUG, ha,
"Unable to allocate memory for optrom burst read "
"(%x KB).\n", OPTROM_BURST_SIZE / 1024);
goto slow_read;
}
pbuf = buf;
faddr = offset >> 2;
left = length >> 2;
burst = OPTROM_BURST_DWORDS;
while (left != 0) {
if (burst > left)
burst = left;
rval = qla2x00_dump_ram(ha, optrom_dma,
flash_data_to_access_addr(faddr), burst);
if (rval) {
qla_printk(KERN_WARNING, ha,
"Unable to burst-read optrom segment "
"(%x/%x/%llx).\n", rval,
flash_data_to_access_addr(faddr), optrom_dma);
qla_printk(KERN_WARNING, ha,
"Reverting to slow-read.\n");
dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
optrom, optrom_dma);
goto slow_read;
}
memcpy(pbuf, optrom, burst * 4);
left -= burst;
faddr += burst;
pbuf += burst * 4;
}
dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
optrom_dma);
return buf;
slow_read:
return qla24xx_read_optrom_data(ha, buf, offset, length);
}
/** /**
* qla2x00_get_fcode_version() - Determine an FCODE image's version. * qla2x00_get_fcode_version() - Determine an FCODE image's version.
* @ha: HA context * @ha: HA context
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment