| /****************************************************************************** |
| * 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 __iomem *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(); |
| } |
| |