linux/drivers/scsi/qla4xxx/ql4_nvram.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * QLogic iSCSI HBA Driver
   4 * Copyright (c)  2003-2013 QLogic Corporation
   5 */
   6
   7#include "ql4_def.h"
   8#include "ql4_glbl.h"
   9#include "ql4_dbg.h"
  10#include "ql4_inline.h"
  11
  12static inline void eeprom_cmd(uint32_t cmd, struct scsi_qla_host *ha)
  13{
  14        writel(cmd, isp_nvram(ha));
  15        readl(isp_nvram(ha));
  16        udelay(1);
  17}
  18
  19static inline int eeprom_size(struct scsi_qla_host *ha)
  20{
  21        return is_qla4010(ha) ? FM93C66A_SIZE_16 : FM93C86A_SIZE_16;
  22}
  23
  24static inline int eeprom_no_addr_bits(struct scsi_qla_host *ha)
  25{
  26        return is_qla4010(ha) ? FM93C56A_NO_ADDR_BITS_16 :
  27                FM93C86A_NO_ADDR_BITS_16 ;
  28}
  29
  30static inline int eeprom_no_data_bits(struct scsi_qla_host *ha)
  31{
  32        return FM93C56A_DATA_BITS_16;
  33}
  34
  35static int fm93c56a_select(struct scsi_qla_host * ha)
  36{
  37        DEBUG5(printk(KERN_ERR "fm93c56a_select:\n"));
  38
  39        ha->eeprom_cmd_data = AUBURN_EEPROM_CS_1 | 0x000f0000;
  40        eeprom_cmd(ha->eeprom_cmd_data, ha);
  41        return 1;
  42}
  43
  44static int fm93c56a_cmd(struct scsi_qla_host * ha, int cmd, int addr)
  45{
  46        int i;
  47        int mask;
  48        int dataBit;
  49        int previousBit;
  50
  51        /* Clock in a zero, then do the start bit. */
  52        eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1, ha);
  53
  54        eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
  55               AUBURN_EEPROM_CLK_RISE, ha);
  56        eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
  57               AUBURN_EEPROM_CLK_FALL, ha);
  58
  59        mask = 1 << (FM93C56A_CMD_BITS - 1);
  60
  61        /* Force the previous data bit to be different. */
  62        previousBit = 0xffff;
  63        for (i = 0; i < FM93C56A_CMD_BITS; i++) {
  64                dataBit =
  65                        (cmd & mask) ? AUBURN_EEPROM_DO_1 : AUBURN_EEPROM_DO_0;
  66                if (previousBit != dataBit) {
  67
  68                        /*
  69                         * If the bit changed, then change the DO state to
  70                         * match.
  71                         */
  72                        eeprom_cmd(ha->eeprom_cmd_data | dataBit, ha);
  73                        previousBit = dataBit;
  74                }
  75                eeprom_cmd(ha->eeprom_cmd_data | dataBit |
  76                       AUBURN_EEPROM_CLK_RISE, ha);
  77                eeprom_cmd(ha->eeprom_cmd_data | dataBit |
  78                       AUBURN_EEPROM_CLK_FALL, ha);
  79
  80                cmd = cmd << 1;
  81        }
  82        mask = 1 << (eeprom_no_addr_bits(ha) - 1);
  83
  84        /* Force the previous data bit to be different. */
  85        previousBit = 0xffff;
  86        for (i = 0; i < eeprom_no_addr_bits(ha); i++) {
  87                dataBit = addr & mask ? AUBURN_EEPROM_DO_1 :
  88                        AUBURN_EEPROM_DO_0;
  89                if (previousBit != dataBit) {
  90                        /*
  91                         * If the bit changed, then change the DO state to
  92                         * match.
  93                         */
  94                        eeprom_cmd(ha->eeprom_cmd_data | dataBit, ha);
  95
  96                        previousBit = dataBit;
  97                }
  98                eeprom_cmd(ha->eeprom_cmd_data | dataBit |
  99                       AUBURN_EEPROM_CLK_RISE, ha);
 100                eeprom_cmd(ha->eeprom_cmd_data | dataBit |
 101                       AUBURN_EEPROM_CLK_FALL, ha);
 102
 103                addr = addr << 1;
 104        }
 105        return 1;
 106}
 107
 108static int fm93c56a_deselect(struct scsi_qla_host * ha)
 109{
 110        ha->eeprom_cmd_data = AUBURN_EEPROM_CS_0 | 0x000f0000;
 111        eeprom_cmd(ha->eeprom_cmd_data, ha);
 112        return 1;
 113}
 114
 115static int fm93c56a_datain(struct scsi_qla_host * ha, unsigned short *value)
 116{
 117        int i;
 118        int data = 0;
 119        int dataBit;
 120
 121        /* Read the data bits
 122         * The first bit is a dummy.  Clock right over it. */
 123        for (i = 0; i < eeprom_no_data_bits(ha); i++) {
 124                eeprom_cmd(ha->eeprom_cmd_data |
 125                       AUBURN_EEPROM_CLK_RISE, ha);
 126                eeprom_cmd(ha->eeprom_cmd_data |
 127                       AUBURN_EEPROM_CLK_FALL, ha);
 128
 129                dataBit = (readw(isp_nvram(ha)) & AUBURN_EEPROM_DI_1) ? 1 : 0;
 130
 131                data = (data << 1) | dataBit;
 132        }
 133
 134        *value = data;
 135        return 1;
 136}
 137
 138static int eeprom_readword(int eepromAddr, u16 * value,
 139                           struct scsi_qla_host * ha)
 140{
 141        fm93c56a_select(ha);
 142        fm93c56a_cmd(ha, FM93C56A_READ, eepromAddr);
 143        fm93c56a_datain(ha, value);
 144        fm93c56a_deselect(ha);
 145        return 1;
 146}
 147
 148/* Hardware_lock must be set before calling */
 149u16 rd_nvram_word(struct scsi_qla_host * ha, int offset)
 150{
 151        u16 val = 0;
 152
 153        /* NOTE: NVRAM uses half-word addresses */
 154        eeprom_readword(offset, &val, ha);
 155        return val;
 156}
 157
 158u8 rd_nvram_byte(struct scsi_qla_host *ha, int offset)
 159{
 160        u16 val = 0;
 161        u8 rval = 0;
 162        int index = 0;
 163
 164        if (offset & 0x1)
 165                index = (offset - 1) / 2;
 166        else
 167                index = offset / 2;
 168
 169        val = le16_to_cpu(rd_nvram_word(ha, index));
 170
 171        if (offset & 0x1)
 172                rval = (u8)((val & 0xff00) >> 8);
 173        else
 174                rval = (u8)((val & 0x00ff));
 175
 176        return rval;
 177}
 178
 179int qla4xxx_is_nvram_configuration_valid(struct scsi_qla_host * ha)
 180{
 181        int status = QLA_ERROR;
 182        uint16_t checksum = 0;
 183        uint32_t index;
 184        unsigned long flags;
 185
 186        spin_lock_irqsave(&ha->hardware_lock, flags);
 187        for (index = 0; index < eeprom_size(ha); index++)
 188                checksum += rd_nvram_word(ha, index);
 189        spin_unlock_irqrestore(&ha->hardware_lock, flags);
 190
 191        if (checksum == 0)
 192                status = QLA_SUCCESS;
 193
 194        return status;
 195}
 196
 197/*************************************************************************
 198 *
 199 *                      Hardware Semaphore routines
 200 *
 201 *************************************************************************/
 202int ql4xxx_sem_spinlock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits)
 203{
 204        uint32_t value;
 205        unsigned long flags;
 206        unsigned int seconds = 30;
 207
 208        DEBUG2(printk("scsi%ld : Trying to get SEM lock - mask= 0x%x, code = "
 209                      "0x%x\n", ha->host_no, sem_mask, sem_bits));
 210        do {
 211                spin_lock_irqsave(&ha->hardware_lock, flags);
 212                writel((sem_mask | sem_bits), isp_semaphore(ha));
 213                value = readw(isp_semaphore(ha));
 214                spin_unlock_irqrestore(&ha->hardware_lock, flags);
 215                if ((value & (sem_mask >> 16)) == sem_bits) {
 216                        DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, "
 217                                      "code = 0x%x\n", ha->host_no,
 218                                      sem_mask, sem_bits));
 219                        return QLA_SUCCESS;
 220                }
 221                ssleep(1);
 222        } while (--seconds);
 223        return QLA_ERROR;
 224}
 225
 226void ql4xxx_sem_unlock(struct scsi_qla_host * ha, u32 sem_mask)
 227{
 228        unsigned long flags;
 229
 230        spin_lock_irqsave(&ha->hardware_lock, flags);
 231        writel(sem_mask, isp_semaphore(ha));
 232        readl(isp_semaphore(ha));
 233        spin_unlock_irqrestore(&ha->hardware_lock, flags);
 234
 235        DEBUG2(printk("scsi%ld : UNLOCK SEM - mask= 0x%x\n", ha->host_no,
 236                      sem_mask));
 237}
 238
 239int ql4xxx_sem_lock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits)
 240{
 241        uint32_t value;
 242        unsigned long flags;
 243
 244        spin_lock_irqsave(&ha->hardware_lock, flags);
 245        writel((sem_mask | sem_bits), isp_semaphore(ha));
 246        value = readw(isp_semaphore(ha));
 247        spin_unlock_irqrestore(&ha->hardware_lock, flags);
 248        if ((value & (sem_mask >> 16)) == sem_bits) {
 249                DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, code = "
 250                              "0x%x, sema code=0x%x\n", ha->host_no,
 251                              sem_mask, sem_bits, value));
 252                return 1;
 253        }
 254        return 0;
 255}
 256