qla_sup.c

来自「linux2.6.16版本」· C语言 代码 · 共 1,661 行 · 第 1/3 页

C
1,661
字号
		/* Specify 64K address range: */		/*  clear out Module Select and Flash Address bits [19:16]. */		bank_select &= ~0xf8;		bank_select |= addr >> 12 & 0xf0;		bank_select |= CSR_FLASH_64K_BANK;		WRT_REG_WORD(&reg->ctrl_status, bank_select);		RD_REG_WORD(&reg->ctrl_status);	/* PCI Posting. */		WRT_REG_WORD(&reg->flash_address, (uint16_t)addr);		RD_REG_WORD(&reg->ctrl_status);		/* PCI Posting. */		WRT_REG_WORD(&reg->flash_data, (uint16_t)data);		RD_REG_WORD(&reg->ctrl_status);		/* PCI Posting. */		return;	}	/* Setup bit 16 of flash address. */	if ((addr & BIT_16) && ((bank_select & CSR_FLASH_64K_BANK) == 0)) {		bank_select |= CSR_FLASH_64K_BANK;		WRT_REG_WORD(&reg->ctrl_status, bank_select);		RD_REG_WORD(&reg->ctrl_status);	/* PCI Posting. */	} else if (((addr & BIT_16) == 0) &&	    (bank_select & CSR_FLASH_64K_BANK)) {		bank_select &= ~(CSR_FLASH_64K_BANK);		WRT_REG_WORD(&reg->ctrl_status, bank_select);		RD_REG_WORD(&reg->ctrl_status);	/* PCI Posting. */	}	/* Always perform IO mapped accesses to the FLASH registers. */	if (ha->pio_address) {		reg = (struct device_reg_2xxx __iomem *)ha->pio_address;		WRT_REG_WORD_PIO(&reg->flash_address, (uint16_t)addr);		WRT_REG_WORD_PIO(&reg->flash_data, (uint16_t)data);	} else {		WRT_REG_WORD(&reg->flash_address, (uint16_t)addr);		RD_REG_WORD(&reg->ctrl_status);		/* PCI Posting. */		WRT_REG_WORD(&reg->flash_data, (uint16_t)data);		RD_REG_WORD(&reg->ctrl_status);		/* PCI Posting. */	}}/** * qla2x00_poll_flash() - Polls flash for completion. * @ha: HA context * @addr: Address in flash to poll * @poll_data: Data to be polled * @man_id: Flash manufacturer ID * @flash_id: Flash ID * * This function polls the device until bit 7 of what is read matches data * bit 7 or until data bit 5 becomes a 1.  If that hapens, the flash ROM timed * out (a fatal error).  The flash book recommeds reading bit 7 again after * reading bit 5 as a 1. * * Returns 0 on success, else non-zero. */static intqla2x00_poll_flash(scsi_qla_host_t *ha, uint32_t addr, uint8_t poll_data,    uint8_t man_id, uint8_t flash_id){	int status;	uint8_t flash_data;	uint32_t cnt;	status = 1;	/* Wait for 30 seconds for command to finish. */	poll_data &= BIT_7;	for (cnt = 3000000; cnt; cnt--) {		flash_data = qla2x00_read_flash_byte(ha, addr);		if ((flash_data & BIT_7) == poll_data) {			status = 0;			break;		}		if (man_id != 0x40 && man_id != 0xda) {			if ((flash_data & BIT_5) && cnt > 2)				cnt = 2;		}		udelay(10);		barrier();	}	return status;}#define IS_OEM_001(ha) \	((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2322 && \	 (ha)->pdev->subsystem_vendor == 0x1028 && \	 (ha)->pdev->subsystem_device == 0x0170)/** * qla2x00_program_flash_address() - Programs a flash address * @ha: HA context * @addr: Address in flash to program * @data: Data to be written in flash * @man_id: Flash manufacturer ID * @flash_id: Flash ID * * Returns 0 on success, else non-zero. */static intqla2x00_program_flash_address(scsi_qla_host_t *ha, uint32_t addr, uint8_t data,    uint8_t man_id, uint8_t flash_id){	/* Write Program Command Sequence. */	if (IS_OEM_001(ha)) {		qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);		qla2x00_write_flash_byte(ha, 0x555, 0x55);		qla2x00_write_flash_byte(ha, 0xaaa, 0xa0);		qla2x00_write_flash_byte(ha, addr, data);	} else {		if (man_id == 0xda && flash_id == 0xc1) {			qla2x00_write_flash_byte(ha, addr, data);			if (addr & 0x7e)				return 0;		} else {			qla2x00_write_flash_byte(ha, 0x5555, 0xaa);			qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);			qla2x00_write_flash_byte(ha, 0x5555, 0xa0);			qla2x00_write_flash_byte(ha, addr, data);		}	}	udelay(150);	/* Wait for write to complete. */	return qla2x00_poll_flash(ha, addr, data, man_id, flash_id);}/** * qla2x00_erase_flash() - Erase the flash. * @ha: HA context * @man_id: Flash manufacturer ID * @flash_id: Flash ID * * Returns 0 on success, else non-zero. */static intqla2x00_erase_flash(scsi_qla_host_t *ha, uint8_t man_id, uint8_t flash_id){	/* Individual Sector Erase Command Sequence */	if (IS_OEM_001(ha)) {		qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);		qla2x00_write_flash_byte(ha, 0x555, 0x55);		qla2x00_write_flash_byte(ha, 0xaaa, 0x80);		qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);		qla2x00_write_flash_byte(ha, 0x555, 0x55);		qla2x00_write_flash_byte(ha, 0xaaa, 0x10);	} else {		qla2x00_write_flash_byte(ha, 0x5555, 0xaa);		qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);		qla2x00_write_flash_byte(ha, 0x5555, 0x80);		qla2x00_write_flash_byte(ha, 0x5555, 0xaa);		qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);		qla2x00_write_flash_byte(ha, 0x5555, 0x10);	}	udelay(150);	/* Wait for erase to complete. */	return qla2x00_poll_flash(ha, 0x00, 0x80, man_id, flash_id);}/** * qla2x00_erase_flash_sector() - Erase a flash sector. * @ha: HA context * @addr: Flash sector to erase * @sec_mask: Sector address mask * @man_id: Flash manufacturer ID * @flash_id: Flash ID * * Returns 0 on success, else non-zero. */static intqla2x00_erase_flash_sector(scsi_qla_host_t *ha, uint32_t addr,    uint32_t sec_mask, uint8_t man_id, uint8_t flash_id){	/* Individual Sector Erase Command Sequence */	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);	qla2x00_write_flash_byte(ha, 0x5555, 0x80);	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);	if (man_id == 0x1f && flash_id == 0x13)		qla2x00_write_flash_byte(ha, addr & sec_mask, 0x10);	else		qla2x00_write_flash_byte(ha, addr & sec_mask, 0x30);	udelay(150);	/* Wait for erase to complete. */	return qla2x00_poll_flash(ha, addr, 0x80, man_id, flash_id);}/** * qla2x00_get_flash_manufacturer() - Read manufacturer ID from flash chip. * @man_id: Flash manufacturer ID * @flash_id: Flash ID */static voidqla2x00_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,    uint8_t *flash_id){	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);	qla2x00_write_flash_byte(ha, 0x5555, 0x90);	*man_id = qla2x00_read_flash_byte(ha, 0x0000);	*flash_id = qla2x00_read_flash_byte(ha, 0x0001);	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);	qla2x00_write_flash_byte(ha, 0x5555, 0xf0);}static inline voidqla2x00_suspend_hba(struct scsi_qla_host *ha){	int cnt;	unsigned long flags;	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	/* Suspend HBA. */	scsi_block_requests(ha->host);	ha->isp_ops.disable_intrs(ha);	set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);	/* Pause RISC. */	spin_lock_irqsave(&ha->hardware_lock, flags);	WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);	RD_REG_WORD(&reg->hccr);	if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {		for (cnt = 0; cnt < 30000; cnt++) {			if ((RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) != 0)				break;			udelay(100);		}	} else {		udelay(10);	}	spin_unlock_irqrestore(&ha->hardware_lock, flags);}static inline voidqla2x00_resume_hba(struct scsi_qla_host *ha){	/* Resume HBA. */	clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);	set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);	up(ha->dpc_wait);	qla2x00_wait_for_hba_online(ha);	scsi_unblock_requests(ha->host);}uint8_t *qla2x00_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,    uint32_t offset, uint32_t length){	unsigned long flags;	uint32_t addr, midpoint;	uint8_t *data;	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	/* Suspend HBA. */	qla2x00_suspend_hba(ha);	/* Go with read. */	spin_lock_irqsave(&ha->hardware_lock, flags);	midpoint = ha->optrom_size / 2;	qla2x00_flash_enable(ha);	WRT_REG_WORD(&reg->nvram, 0);	RD_REG_WORD(&reg->nvram);		/* PCI Posting. */	for (addr = offset, data = buf; addr < length; addr++, data++) {		if (addr == midpoint) {			WRT_REG_WORD(&reg->nvram, NVR_SELECT);			RD_REG_WORD(&reg->nvram);	/* PCI Posting. */		}		*data = qla2x00_read_flash_byte(ha, addr);	}	qla2x00_flash_disable(ha);	spin_unlock_irqrestore(&ha->hardware_lock, flags);	/* Resume HBA. */	qla2x00_resume_hba(ha);	return buf;}intqla2x00_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,    uint32_t offset, uint32_t length){	int rval;	unsigned long flags;	uint8_t man_id, flash_id, sec_number, data;	uint16_t wd;	uint32_t addr, liter, sec_mask, rest_addr;	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	/* Suspend HBA. */	qla2x00_suspend_hba(ha);	rval = QLA_SUCCESS;	sec_number = 0;	/* Reset ISP chip. */	spin_lock_irqsave(&ha->hardware_lock, flags);	WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);	pci_read_config_word(ha->pdev, PCI_COMMAND, &wd);	/* Go with write. */	qla2x00_flash_enable(ha);	do {	/* Loop once to provide quick error exit */		/* Structure of flash memory based on manufacturer */		if (IS_OEM_001(ha)) {			/* OEM variant with special flash part. */			man_id = flash_id = 0;			rest_addr = 0xffff;			sec_mask   = 0x10000;			goto update_flash;		}		qla2x00_get_flash_manufacturer(ha, &man_id, &flash_id);		switch (man_id) {		case 0x20: /* ST flash. */			if (flash_id == 0xd2 || flash_id == 0xe3) {				/*				 * ST m29w008at part - 64kb sector size with				 * 32kb,8kb,8kb,16kb sectors at memory address				 * 0xf0000.				 */				rest_addr = 0xffff;				sec_mask = 0x10000;				break;   			}			/*			 * ST m29w010b part - 16kb sector size			 * Default to 16kb sectors			 */			rest_addr = 0x3fff;			sec_mask = 0x1c000;			break;		case 0x40: /* Mostel flash. */			/* Mostel v29c51001 part - 512 byte sector size. */			rest_addr = 0x1ff;			sec_mask = 0x1fe00;			break;		case 0xbf: /* SST flash. */			/* SST39sf10 part - 4kb sector size. */			rest_addr = 0xfff;			sec_mask = 0x1f000;			break;		case 0xda: /* Winbond flash. */			/* Winbond W29EE011 part - 256 byte sector size. */			rest_addr = 0x7f;			sec_mask = 0x1ff80;			break;		case 0xc2: /* Macronix flash. */			/* 64k sector size. */			if (flash_id == 0x38 || flash_id == 0x4f) {				rest_addr = 0xffff;				sec_mask = 0x10000;				break;			}			/* Fall through... */		case 0x1f: /* Atmel flash. */			/* 512k sector size. */			if (flash_id == 0x13) {				rest_addr = 0x7fffffff;				sec_mask =   0x80000000;				break;			}			/* Fall through... */		case 0x01: /* AMD flash. */			if (flash_id == 0x38 || flash_id == 0x40 ||			    flash_id == 0x4f) {				/* Am29LV081 part - 64kb sector size. */				/* Am29LV002BT part - 64kb sector size. */				rest_addr = 0xffff;				sec_mask = 0x10000;				break;			} else if (flash_id == 0x3e) {				/*				 * Am29LV008b part - 64kb sector size with				 * 32kb,8kb,8kb,16kb sector at memory address				 * h0xf0000.				 */				rest_addr = 0xffff;				sec_mask = 0x10000;				break;			} else if (flash_id == 0x20 || flash_id == 0x6e) {				/*				 * Am29LV010 part or AM29f010 - 16kb sector				 * size.				 */				rest_addr = 0x3fff;				sec_mask = 0x1c000;				break;			} else if (flash_id == 0x6d) {				/* Am29LV001 part - 8kb sector size. */				rest_addr = 0x1fff;				sec_mask = 0x1e000;				break;			}		default:			/* Default to 16 kb sector size. */			rest_addr = 0x3fff;			sec_mask = 0x1c000;			break;		}update_flash:		if (IS_QLA2322(ha) || IS_QLA6322(ha)) {			if (qla2x00_erase_flash(ha, man_id, flash_id)) {				rval = QLA_FUNCTION_FAILED;				break;			}		}		for (addr = offset, liter = 0; liter < length; liter++,		    addr++) {			data = buf[liter];			/* Are we at the beginning of a sector? */			if ((addr & rest_addr) == 0) {				if (IS_QLA2322(ha) || IS_QLA6322(ha)) {					if (addr >= 0x10000UL) {						if (((addr >> 12) & 0xf0) &&						    ((man_id == 0x01 &&							flash_id == 0x3e) ||						     (man_id == 0x20 &&							 flash_id == 0xd2))) {							sec_number++;							if (sec_number == 1) {								rest_addr =								    0x7fff;								sec_mask =								    0x18000;							} else if (							    sec_number == 2 ||							    sec_number == 3) {								rest_addr =								    0x1fff;								sec_mask =								    0x1e000;							} else if (							    sec_number == 4) {								rest_addr =								    0x3fff;								sec_mask =								    0x1c000;							}						}					}				} else if (addr == ha->optrom_size / 2) {					WRT_REG_WORD(&reg->nvram, NVR_SELECT);					RD_REG_WORD(&reg->nvram);				}				if (flash_id == 0xda && man_id == 0xc1) {					qla2x00_write_flash_byte(ha, 0x5555,					    0xaa);					qla2x00_write_flash_byte(ha, 0x2aaa,					    0x55);					qla2x00_write_flash_byte(ha, 0x5555,					    0xa0);				} else if (!IS_QLA2322(ha) && !IS_QLA6322(ha)) {					/* Then erase it */					if (qla2x00_erase_flash_sector(ha,					    addr, sec_mask, man_id,					    flash_id)) {						rval = QLA_FUNCTION_FAILED;						break;					}					if (man_id == 0x01 && flash_id == 0x6d)						sec_number++;				}			}			if (man_id == 0x01 && flash_id == 0x6d) {				if (sec_number == 1 &&				    addr == (rest_addr - 1)) {					rest_addr = 0x0fff;					sec_mask   = 0x1f000;				} else if (sec_number == 3 && (addr & 0x7ffe)) {					rest_addr = 0x3fff;					sec_mask   = 0x1c000;				}			}			if (qla2x00_program_flash_address(ha, addr, data,			    man_id, flash_id)) {				rval = QLA_FUNCTION_FAILED;				break;			}		}	} while (0);	qla2x00_flash_disable(ha);	spin_unlock_irqrestore(&ha->hardware_lock, flags);	/* Resume HBA. */	qla2x00_resume_hba(ha);	return rval;}uint8_t *qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,    uint32_t offset, uint32_t length){	/* 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. */	qla24xx_read_flash_data(ha, (uint32_t *)buf, offset >> 2, length >> 2);	/* 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;}intqla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,    uint32_t offset, uint32_t length){	int rval;	/* 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. */	rval = qla24xx_write_flash_data(ha, (uint32_t *)buf, offset >> 2,	    length >> 2);	/* Resume HBA -- RISC reset needed. */	clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);	set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);	up(ha->dpc_wait);	qla2x00_wait_for_hba_online(ha);	scsi_unblock_requests(ha->host);	return rval;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?