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