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