qla_sup.c

来自「linux 内核源代码」· C语言 代码 · 共 2,224 行 · 第 1/4 页

C
2,224
字号
/* * QLogic Fibre Channel HBA Driver * Copyright (c)  2003-2005 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */#include "qla_def.h"#include <linux/delay.h>#include <linux/vmalloc.h>#include <asm/uaccess.h>static uint16_t qla2x00_nvram_request(scsi_qla_host_t *, uint32_t);static void qla2x00_nv_deselect(scsi_qla_host_t *);static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t);/* * NVRAM support routines *//** * qla2x00_lock_nvram_access() - * @ha: HA context */voidqla2x00_lock_nvram_access(scsi_qla_host_t *ha){	uint16_t data;	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {		data = RD_REG_WORD(&reg->nvram);		while (data & NVR_BUSY) {			udelay(100);			data = RD_REG_WORD(&reg->nvram);		}		/* Lock resource */		WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);		RD_REG_WORD(&reg->u.isp2300.host_semaphore);		udelay(5);		data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);		while ((data & BIT_0) == 0) {			/* Lock failed */			udelay(100);			WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);			RD_REG_WORD(&reg->u.isp2300.host_semaphore);			udelay(5);			data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);		}	}}/** * qla2x00_unlock_nvram_access() - * @ha: HA context */voidqla2x00_unlock_nvram_access(scsi_qla_host_t *ha){	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {		WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0);		RD_REG_WORD(&reg->u.isp2300.host_semaphore);	}}/** * qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the *	request routine to get the word from NVRAM. * @ha: HA context * @addr: Address in NVRAM to read * * Returns the word read from nvram @addr. */uint16_tqla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr){	uint16_t	data;	uint32_t	nv_cmd;	nv_cmd = addr << 16;	nv_cmd |= NV_READ_OP;	data = qla2x00_nvram_request(ha, nv_cmd);	return (data);}/** * qla2x00_write_nvram_word() - Write NVRAM data. * @ha: HA context * @addr: Address in NVRAM to write * @data: word to program */voidqla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data){	int count;	uint16_t word;	uint32_t nv_cmd, wait_cnt;	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	qla2x00_nv_write(ha, NVR_DATA_OUT);	qla2x00_nv_write(ha, 0);	qla2x00_nv_write(ha, 0);	for (word = 0; word < 8; word++)		qla2x00_nv_write(ha, NVR_DATA_OUT);	qla2x00_nv_deselect(ha);	/* Write data */	nv_cmd = (addr << 16) | NV_WRITE_OP;	nv_cmd |= data;	nv_cmd <<= 5;	for (count = 0; count < 27; count++) {		if (nv_cmd & BIT_31)			qla2x00_nv_write(ha, NVR_DATA_OUT);		else			qla2x00_nv_write(ha, 0);		nv_cmd <<= 1;	}	qla2x00_nv_deselect(ha);	/* Wait for NVRAM to become ready */	WRT_REG_WORD(&reg->nvram, NVR_SELECT);	RD_REG_WORD(&reg->nvram);		/* PCI Posting. */	wait_cnt = NVR_WAIT_CNT;	do {		if (!--wait_cnt) {			DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n",			    __func__, ha->host_no));			break;		}		NVRAM_DELAY();		word = RD_REG_WORD(&reg->nvram);	} while ((word & NVR_DATA_IN) == 0);	qla2x00_nv_deselect(ha);	/* Disable writes */	qla2x00_nv_write(ha, NVR_DATA_OUT);	for (count = 0; count < 10; count++)		qla2x00_nv_write(ha, 0);	qla2x00_nv_deselect(ha);}static intqla2x00_write_nvram_word_tmo(scsi_qla_host_t *ha, uint32_t addr, uint16_t data,    uint32_t tmo){	int ret, count;	uint16_t word;	uint32_t nv_cmd;	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	ret = QLA_SUCCESS;	qla2x00_nv_write(ha, NVR_DATA_OUT);	qla2x00_nv_write(ha, 0);	qla2x00_nv_write(ha, 0);	for (word = 0; word < 8; word++)		qla2x00_nv_write(ha, NVR_DATA_OUT);	qla2x00_nv_deselect(ha);	/* Write data */	nv_cmd = (addr << 16) | NV_WRITE_OP;	nv_cmd |= data;	nv_cmd <<= 5;	for (count = 0; count < 27; count++) {		if (nv_cmd & BIT_31)			qla2x00_nv_write(ha, NVR_DATA_OUT);		else			qla2x00_nv_write(ha, 0);		nv_cmd <<= 1;	}	qla2x00_nv_deselect(ha);	/* Wait for NVRAM to become ready */	WRT_REG_WORD(&reg->nvram, NVR_SELECT);	RD_REG_WORD(&reg->nvram);		/* PCI Posting. */	do {		NVRAM_DELAY();		word = RD_REG_WORD(&reg->nvram);		if (!--tmo) {			ret = QLA_FUNCTION_FAILED;			break;		}	} while ((word & NVR_DATA_IN) == 0);	qla2x00_nv_deselect(ha);	/* Disable writes */	qla2x00_nv_write(ha, NVR_DATA_OUT);	for (count = 0; count < 10; count++)		qla2x00_nv_write(ha, 0);	qla2x00_nv_deselect(ha);	return ret;}/** * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from *	NVRAM. * @ha: HA context * @nv_cmd: NVRAM command * * Bit definitions for NVRAM command: * *	Bit 26     = start bit *	Bit 25, 24 = opcode *	Bit 23-16  = address *	Bit 15-0   = write data * * Returns the word read from nvram @addr. */static uint16_tqla2x00_nvram_request(scsi_qla_host_t *ha, uint32_t nv_cmd){	uint8_t		cnt;	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	uint16_t	data = 0;	uint16_t	reg_data;	/* Send command to NVRAM. */	nv_cmd <<= 5;	for (cnt = 0; cnt < 11; cnt++) {		if (nv_cmd & BIT_31)			qla2x00_nv_write(ha, NVR_DATA_OUT);		else			qla2x00_nv_write(ha, 0);		nv_cmd <<= 1;	}	/* Read data from NVRAM. */	for (cnt = 0; cnt < 16; cnt++) {		WRT_REG_WORD(&reg->nvram, NVR_SELECT | NVR_CLOCK);		RD_REG_WORD(&reg->nvram);	/* PCI Posting. */		NVRAM_DELAY();		data <<= 1;		reg_data = RD_REG_WORD(&reg->nvram);		if (reg_data & NVR_DATA_IN)			data |= BIT_0;		WRT_REG_WORD(&reg->nvram, NVR_SELECT);		RD_REG_WORD(&reg->nvram);	/* PCI Posting. */		NVRAM_DELAY();	}	/* Deselect chip. */	WRT_REG_WORD(&reg->nvram, NVR_DESELECT);	RD_REG_WORD(&reg->nvram);		/* PCI Posting. */	NVRAM_DELAY();	return (data);}/** * qla2x00_nv_write() - Clean NVRAM operations. * @ha: HA context */static voidqla2x00_nv_deselect(scsi_qla_host_t *ha){	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	WRT_REG_WORD(&reg->nvram, NVR_DESELECT);	RD_REG_WORD(&reg->nvram);		/* PCI Posting. */	NVRAM_DELAY();}/** * qla2x00_nv_write() - Prepare for NVRAM read/write operation. * @ha: HA context * @data: Serial interface selector */static voidqla2x00_nv_write(scsi_qla_host_t *ha, uint16_t data){	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);	RD_REG_WORD(&reg->nvram);		/* PCI Posting. */	NVRAM_DELAY();	WRT_REG_WORD(&reg->nvram, data | NVR_SELECT| NVR_CLOCK |	    NVR_WRT_ENABLE);	RD_REG_WORD(&reg->nvram);		/* PCI Posting. */	NVRAM_DELAY();	WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);	RD_REG_WORD(&reg->nvram);		/* PCI Posting. */	NVRAM_DELAY();}/** * qla2x00_clear_nvram_protection() - * @ha: HA context */static intqla2x00_clear_nvram_protection(scsi_qla_host_t *ha){	int ret, stat;	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	uint32_t word, wait_cnt;	uint16_t wprot, wprot_old;	/* Clear NVRAM write protection. */	ret = QLA_FUNCTION_FAILED;	wprot_old = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base));	stat = qla2x00_write_nvram_word_tmo(ha, ha->nvram_base,	    __constant_cpu_to_le16(0x1234), 100000);	wprot = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base));	if (stat != QLA_SUCCESS || wprot != 0x1234) {		/* Write enable. */		qla2x00_nv_write(ha, NVR_DATA_OUT);		qla2x00_nv_write(ha, 0);		qla2x00_nv_write(ha, 0);		for (word = 0; word < 8; word++)			qla2x00_nv_write(ha, NVR_DATA_OUT);		qla2x00_nv_deselect(ha);		/* Enable protection register. */		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);		qla2x00_nv_write(ha, NVR_PR_ENABLE);		qla2x00_nv_write(ha, NVR_PR_ENABLE);		for (word = 0; word < 8; word++)			qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);		qla2x00_nv_deselect(ha);		/* Clear protection register (ffff is cleared). */		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);		for (word = 0; word < 8; word++)			qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);		qla2x00_nv_deselect(ha);		/* Wait for NVRAM to become ready. */		WRT_REG_WORD(&reg->nvram, NVR_SELECT);		RD_REG_WORD(&reg->nvram);	/* PCI Posting. */		wait_cnt = NVR_WAIT_CNT;		do {			if (!--wait_cnt) {				DEBUG9_10(printk("%s(%ld): NVRAM didn't go "				    "ready...\n", __func__,				    ha->host_no));				break;			}			NVRAM_DELAY();			word = RD_REG_WORD(&reg->nvram);		} while ((word & NVR_DATA_IN) == 0);		if (wait_cnt)			ret = QLA_SUCCESS;	} else		qla2x00_write_nvram_word(ha, ha->nvram_base, wprot_old);	return ret;}static voidqla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat){	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;	uint32_t word, wait_cnt;	if (stat != QLA_SUCCESS)		return;	/* Set NVRAM write protection. */	/* Write enable. */	qla2x00_nv_write(ha, NVR_DATA_OUT);	qla2x00_nv_write(ha, 0);	qla2x00_nv_write(ha, 0);	for (word = 0; word < 8; word++)		qla2x00_nv_write(ha, NVR_DATA_OUT);	qla2x00_nv_deselect(ha);	/* Enable protection register. */	qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);	qla2x00_nv_write(ha, NVR_PR_ENABLE);	qla2x00_nv_write(ha, NVR_PR_ENABLE);	for (word = 0; word < 8; word++)		qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);	qla2x00_nv_deselect(ha);	/* Enable protection register. */	qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);	qla2x00_nv_write(ha, NVR_PR_ENABLE);	qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);	for (word = 0; word < 8; word++)		qla2x00_nv_write(ha, NVR_PR_ENABLE);	qla2x00_nv_deselect(ha);	/* Wait for NVRAM to become ready. */	WRT_REG_WORD(&reg->nvram, NVR_SELECT);	RD_REG_WORD(&reg->nvram);		/* PCI Posting. */	wait_cnt = NVR_WAIT_CNT;	do {		if (!--wait_cnt) {			DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n",			    __func__, ha->host_no));			break;		}		NVRAM_DELAY();		word = RD_REG_WORD(&reg->nvram);	} while ((word & NVR_DATA_IN) == 0);}/*****************************************************************************//* Flash Manipulation Routines                                               *//*****************************************************************************/#define OPTROM_BURST_SIZE	0x1000#define OPTROM_BURST_DWORDS	(OPTROM_BURST_SIZE / 4)static inline uint32_tflash_conf_to_access_addr(uint32_t faddr){	return FARX_ACCESS_FLASH_CONF | faddr;}static inline uint32_tflash_data_to_access_addr(uint32_t faddr){	return FARX_ACCESS_FLASH_DATA | faddr;}static inline uint32_tnvram_conf_to_access_addr(uint32_t naddr){	return FARX_ACCESS_NVRAM_CONF | naddr;}static inline uint32_tnvram_data_to_access_addr(uint32_t naddr){	return FARX_ACCESS_NVRAM_DATA | naddr;}static uint32_tqla24xx_read_flash_dword(scsi_qla_host_t *ha, uint32_t addr){	int rval;	uint32_t cnt, data;	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;	WRT_REG_DWORD(&reg->flash_addr, addr & ~FARX_DATA_FLAG);	/* Wait for READ cycle to complete. */	rval = QLA_SUCCESS;	for (cnt = 3000;	    (RD_REG_DWORD(&reg->flash_addr) & FARX_DATA_FLAG) == 0 &&	    rval == QLA_SUCCESS; cnt--) {		if (cnt)			udelay(10);		else			rval = QLA_FUNCTION_TIMEOUT;		cond_resched();	}	/* TODO: What happens if we time out? */	data = 0xDEADDEAD;	if (rval == QLA_SUCCESS)		data = RD_REG_DWORD(&reg->flash_data);	return data;}uint32_t *qla24xx_read_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,    uint32_t dwords){	uint32_t i;	/* Dword reads to flash. */	for (i = 0; i < dwords; i++, faddr++)		dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha,		    flash_data_to_access_addr(faddr)));	return dwptr;}static intqla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data){	int rval;	uint32_t cnt;	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;	WRT_REG_DWORD(&reg->flash_data, data);	RD_REG_DWORD(&reg->flash_data);		/* PCI Posting. */	WRT_REG_DWORD(&reg->flash_addr, addr | FARX_DATA_FLAG);	/* Wait for Write cycle to complete. */	rval = QLA_SUCCESS;	for (cnt = 500000; (RD_REG_DWORD(&reg->flash_addr) & FARX_DATA_FLAG) &&	    rval == QLA_SUCCESS; cnt--) {		if (cnt)			udelay(10);		else			rval = QLA_FUNCTION_TIMEOUT;		cond_resched();	}	return rval;}static voidqla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,    uint8_t *flash_id){	uint32_t ids;	ids = qla24xx_read_flash_dword(ha, flash_data_to_access_addr(0xd03ab));	*man_id = LSB(ids);	*flash_id = MSB(ids);	/* Check if man_id and flash_id are valid. */	if (ids != 0xDEADDEAD && (*man_id == 0 || *flash_id == 0)) {		/* Read information using 0x9f opcode		 * Device ID, Mfg ID would be read in the format:		 *   <Ext Dev Info><Device ID Part2><Device ID Part 1><Mfg ID>		 * Example: ATMEL 0x00 01 45 1F		 * Extract MFG and Dev ID from last two bytes.		 */		ids = qla24xx_read_flash_dword(ha,		    flash_data_to_access_addr(0xd009f));		*man_id = LSB(ids);		*flash_id = MSB(ids);	}}static intqla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,    uint32_t dwords){	int ret;	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;

⌨️ 快捷键说明

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