blob: e3957ca5b645ba6d57cffaa93cab0ce0f0675a78 [file] [log] [blame]
David Somayajuluafaf5a22006-09-19 10:28:00 -07001/*
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
10static inline int eeprom_size(struct scsi_qla_host *ha)
11{
12 return is_qla4022(ha) ? FM93C86A_SIZE_16 : FM93C66A_SIZE_16;
13}
14
15static inline int eeprom_no_addr_bits(struct scsi_qla_host *ha)
16{
17 return is_qla4022(ha) ? FM93C86A_NO_ADDR_BITS_16 :
18 FM93C56A_NO_ADDR_BITS_16;
19}
20
21static inline int eeprom_no_data_bits(struct scsi_qla_host *ha)
22{
23 return FM93C56A_DATA_BITS_16;
24}
25
26static 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
36static 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
98static 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
106static 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
128static 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 */
139u16 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
148int 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 *************************************************************************/
171int 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
195void 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
208int 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}