diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_sup.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_sup.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c new file mode 100644 index 000000000000..0e75fbb77b6b --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -0,0 +1,296 @@ +/****************************************************************************** + * QLOGIC LINUX SOFTWARE + * + * QLogic ISP2x00 device driver for Linux 2.6.x + * Copyright (C) 2003-2004 QLogic Corporation + * (www.qlogic.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + ******************************************************************************/ + +#include "qla_def.h" + +#include <linux/delay.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 + */ +void +qla2x00_lock_nvram_access(scsi_qla_host_t *ha) +{ + uint16_t data; + device_reg_t __iomem *reg = ha->iobase; + + if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) { + data = RD_REG_WORD(®->nvram); + while (data & NVR_BUSY) { + udelay(100); + data = RD_REG_WORD(®->nvram); + } + + /* Lock resource */ + WRT_REG_WORD(®->u.isp2300.host_semaphore, 0x1); + RD_REG_WORD(®->u.isp2300.host_semaphore); + udelay(5); + data = RD_REG_WORD(®->u.isp2300.host_semaphore); + while ((data & BIT_0) == 0) { + /* Lock failed */ + udelay(100); + WRT_REG_WORD(®->u.isp2300.host_semaphore, 0x1); + RD_REG_WORD(®->u.isp2300.host_semaphore); + udelay(5); + data = RD_REG_WORD(®->u.isp2300.host_semaphore); + } + } +} + +/** + * qla2x00_unlock_nvram_access() - + * @ha: HA context + */ +void +qla2x00_unlock_nvram_access(scsi_qla_host_t *ha) +{ + device_reg_t __iomem *reg = ha->iobase; + + if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) { + WRT_REG_WORD(®->u.isp2300.host_semaphore, 0); + RD_REG_WORD(®->u.isp2300.host_semaphore); + } +} + +/** + * qla2x00_release_nvram_protection() - + * @ha: HA context + */ +void +qla2x00_release_nvram_protection(scsi_qla_host_t *ha) +{ + device_reg_t *reg; + uint32_t word; + + reg = ha->iobase; + + /* Release NVRAM write protection. */ + if (IS_QLA2322(ha) || IS_QLA6322(ha)) { + /* 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(®->nvram, NVR_SELECT); + do { + NVRAM_DELAY(); + word = RD_REG_WORD(®->nvram); + } while ((word & NVR_DATA_IN) == 0); + } +} + +/** + * 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_t +qla2x00_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 + */ +void +qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data) +{ + int count; + uint16_t word; + uint32_t nv_cmd; + device_reg_t __iomem *reg = ha->iobase; + + 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(®->nvram, NVR_SELECT); + do { + NVRAM_DELAY(); + word = RD_REG_WORD(®->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); +} + +/** + * 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_t +qla2x00_nvram_request(scsi_qla_host_t *ha, uint32_t nv_cmd) +{ + uint8_t cnt; + device_reg_t __iomem *reg = ha->iobase; + 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(®->nvram, NVR_SELECT | NVR_CLOCK); + NVRAM_DELAY(); + data <<= 1; + reg_data = RD_REG_WORD(®->nvram); + if (reg_data & NVR_DATA_IN) + data |= BIT_0; + WRT_REG_WORD(®->nvram, NVR_SELECT); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); + } + + /* Deselect chip. */ + WRT_REG_WORD(®->nvram, NVR_DESELECT); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); + + return (data); +} + +/** + * qla2x00_nv_write() - Clean NVRAM operations. + * @ha: HA context + */ +static void +qla2x00_nv_deselect(scsi_qla_host_t *ha) +{ + device_reg_t __iomem *reg = ha->iobase; + + WRT_REG_WORD(®->nvram, NVR_DESELECT); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); +} + +/** + * qla2x00_nv_write() - Prepare for NVRAM read/write operation. + * @ha: HA context + * @data: Serial interface selector + */ +static void +qla2x00_nv_write(scsi_qla_host_t *ha, uint16_t data) +{ + device_reg_t __iomem *reg = ha->iobase; + + WRT_REG_WORD(®->nvram, data | NVR_SELECT | NVR_WRT_ENABLE); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); + WRT_REG_WORD(®->nvram, data | NVR_SELECT| NVR_CLOCK | + NVR_WRT_ENABLE); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); + WRT_REG_WORD(®->nvram, data | NVR_SELECT | NVR_WRT_ENABLE); + RD_REG_WORD(®->nvram); /* PCI Posting. */ + NVRAM_DELAY(); +} + |