Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -133,6 +133,9 @@ int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
extern int
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
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 *,
    uint32_t, uint32_t);
extern int qla24xx_write_optrom_data(struct scsi_qla_host *, uint8_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 qla24xx_get_flash_version(scsi_qla_host_t *, void *);
+48 −0
Original line number Diff line number Diff line
@@ -2980,3 +2980,51 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,

	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;
}
+1 −1
Original line number Diff line number Diff line
@@ -1384,7 +1384,7 @@ static struct isp_operations qla25xx_isp_ops = {
	.beacon_on		= qla24xx_beacon_on,
	.beacon_off		= qla24xx_beacon_off,
	.beacon_blink		= qla24xx_beacon_blink,
	.read_optrom		= qla24xx_read_optrom_data,
	.read_optrom		= qla25xx_read_optrom_data,
	.write_optrom		= qla24xx_write_optrom_data,
	.get_flash_version	= qla24xx_get_flash_version,
};
+166 −55
Original line number Diff line number Diff line
@@ -425,6 +425,9 @@ qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat)
/* Flash Manipulation Routines                                               */
/*****************************************************************************/

#define OPTROM_BURST_SIZE	0x1000
#define OPTROM_BURST_DWORDS	(OPTROM_BURST_SIZE / 4)

static inline uint32_t
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,
    uint32_t dwords)
{
	int ret;
	uint32_t liter;
	uint32_t sec_mask, rest_addr, conf_addr, sec_end_mask;
	uint32_t liter, miter;
	uint32_t sec_mask, rest_addr, conf_addr;
	uint32_t fdata, findex ;
	uint8_t	man_id, flash_id;
	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
	dma_addr_t optrom_dma;
	void *optrom = NULL;
	uint32_t *s, *d;

	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);
	DEBUG9(printk("%s(%ld): Flash man_id=%d flash_id=%d\n", __func__,
	    ha->host_no, man_id, flash_id));

	sec_end_mask = 0;
	conf_addr = flash_conf_to_access_addr(0x03d8);
	switch (man_id) {
	case 0xbf: /* STT flash. */
		if (flash_id == 0x8e) {
			rest_addr = 0x3fff;
			sec_mask = 0x7c000;
		} else {
			rest_addr = 0x1fff;
		sec_mask = 0x3e000;
			sec_mask = 0x7e000;
		}
		if (flash_id == 0x80)
			conf_addr = flash_conf_to_access_addr(0x0352);
		break;
	case 0x13: /* ST M25P80. */
		rest_addr = 0x3fff;
		sec_mask = 0x3c000;
		sec_mask = 0x7c000;
		break;
	case 0x1f: // Atmel 26DF081A
		rest_addr = 0x0fff;
		sec_mask = 0xff000;
		sec_end_mask = 0x003ff;
		rest_addr = 0x3fff;
		sec_mask = 0x7c000;
		conf_addr = flash_conf_to_access_addr(0x0320);
		break;
	default:
		/* Default to 64 kb sector size. */
		rest_addr = 0x3fff;
		sec_mask = 0x3c000;
		sec_mask = 0x7c000;
		break;
	}

@@ -592,7 +613,6 @@ 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.*/
	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++) {
		if (man_id == 0x1f) {
			findex = faddr << 2;
@@ -604,10 +624,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,

		/* Are we at the beginning of a sector? */
		if ((findex & rest_addr) == 0) {
				/*
				 * Do sector unprotect at 4K boundry for Atmel
				 * part.
				 */
			/* Do sector unprotect at 4K boundry for Atmel part. */
			if (man_id == 0x1f)
				qla24xx_write_flash_dword(ha,
				    flash_conf_to_access_addr(0x0339),
@@ -623,9 +640,39 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t 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),
			    cpu_to_le32(*dwptr));
			    OPTROM_BURST_DWORDS);
			if (ret != QLA_SUCCESS) {
				qla_printk(KERN_WARNING, ha,
				    "Unable to burst-write optrom segment "
				    "(%x/%x/%llx).\n", ret,
				    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;
			}
		}

		ret = qla24xx_write_flash_dword(ha,
		    flash_data_to_access_addr(faddr), cpu_to_le32(*dwptr));
		if (ret != QLA_SUCCESS) {
			DEBUG9(printk("%s(%ld) Unable to program flash "
			    "address=%x data=%x.\n", __func__,
@@ -635,13 +682,12 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,

		/* Do sector protect at 4K boundry for Atmel part. */
		if (man_id == 0x1f &&
			    ((faddr & sec_end_mask) == 0x3ff))
		    ((faddr & rest_addr) == rest_addr))
			qla24xx_write_flash_dword(ha,
			    flash_conf_to_access_addr(0x0336),
			    (fdata & 0xff00) | ((fdata << 16) &
			    0xff0000) | ((fdata >> 16) & 0xff));
	}
	} while (0);

	/* Enable flash write-protection. */
	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,
	    RD_REG_DWORD(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE);
	RD_REG_DWORD(&reg->ctrl_status);	/* PCI Posting. */

	if (optrom)
		dma_free_coherent(&ha->pdev->dev,
		    OPTROM_BURST_SIZE, optrom, optrom_dma);

	return ret;
}

@@ -1728,7 +1778,6 @@ qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
{
	/* Suspend HBA. */
	scsi_block_requests(ha->host);
	ha->isp_ops->disable_intrs(ha);
	set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);

	/* Go with read. */
@@ -1736,7 +1785,6 @@ qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,

	/* Resume HBA. */
	clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
	ha->isp_ops->enable_intrs(ha);
	scsi_unblock_requests(ha->host);

	return buf;
@@ -1750,7 +1798,6 @@ qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,

	/* Suspend HBA. */
	scsi_block_requests(ha->host);
	ha->isp_ops->disable_intrs(ha);
	set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);

	/* Go with write. */
@@ -1767,6 +1814,70 @@ qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
	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.
 * @ha: HA context