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 | |
12 | static inline void eeprom_cmd(uint32_t cmd, struct scsi_qla_host *ha) |
13 | { |
14 | writel(val: cmd, addr: isp_nvram(ha)); |
15 | readl(addr: isp_nvram(ha)); |
16 | udelay(1); |
17 | } |
18 | |
19 | static inline int eeprom_size(struct scsi_qla_host *ha) |
20 | { |
21 | return is_qla4010(ha) ? FM93C66A_SIZE_16 : FM93C86A_SIZE_16; |
22 | } |
23 | |
24 | static 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 | |
30 | static inline int eeprom_no_data_bits(struct scsi_qla_host *ha) |
31 | { |
32 | return FM93C56A_DATA_BITS_16; |
33 | } |
34 | |
35 | static 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(cmd: ha->eeprom_cmd_data, ha); |
41 | return 1; |
42 | } |
43 | |
44 | static 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(cmd: ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1, ha); |
53 | |
54 | eeprom_cmd(cmd: ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 | |
55 | AUBURN_EEPROM_CLK_RISE, ha); |
56 | eeprom_cmd(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(cmd: ha->eeprom_cmd_data | dataBit, ha); |
73 | previousBit = dataBit; |
74 | } |
75 | eeprom_cmd(cmd: ha->eeprom_cmd_data | dataBit | |
76 | AUBURN_EEPROM_CLK_RISE, ha); |
77 | eeprom_cmd(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(cmd: ha->eeprom_cmd_data | dataBit, ha); |
95 | |
96 | previousBit = dataBit; |
97 | } |
98 | eeprom_cmd(cmd: ha->eeprom_cmd_data | dataBit | |
99 | AUBURN_EEPROM_CLK_RISE, ha); |
100 | eeprom_cmd(cmd: ha->eeprom_cmd_data | dataBit | |
101 | AUBURN_EEPROM_CLK_FALL, ha); |
102 | |
103 | addr = addr << 1; |
104 | } |
105 | return 1; |
106 | } |
107 | |
108 | static int fm93c56a_deselect(struct scsi_qla_host * ha) |
109 | { |
110 | ha->eeprom_cmd_data = AUBURN_EEPROM_CS_0 | 0x000f0000; |
111 | eeprom_cmd(cmd: ha->eeprom_cmd_data, ha); |
112 | return 1; |
113 | } |
114 | |
115 | static 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(cmd: ha->eeprom_cmd_data | |
125 | AUBURN_EEPROM_CLK_RISE, ha); |
126 | eeprom_cmd(cmd: ha->eeprom_cmd_data | |
127 | AUBURN_EEPROM_CLK_FALL, ha); |
128 | |
129 | dataBit = (readw(addr: 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 | |
138 | static int eeprom_readword(int eepromAddr, u16 * value, |
139 | struct scsi_qla_host * ha) |
140 | { |
141 | fm93c56a_select(ha); |
142 | fm93c56a_cmd(ha, FM93C56A_READ, addr: eepromAddr); |
143 | fm93c56a_datain(ha, value); |
144 | fm93c56a_deselect(ha); |
145 | return 1; |
146 | } |
147 | |
148 | /* Hardware_lock must be set before calling */ |
149 | u16 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(eepromAddr: offset, value: &val, ha); |
155 | return val; |
156 | } |
157 | |
158 | u8 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 | |
179 | int 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, offset: index); |
189 | spin_unlock_irqrestore(lock: &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 | *************************************************************************/ |
202 | int 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(val: (sem_mask | sem_bits), addr: isp_semaphore(ha)); |
213 | value = readw(addr: isp_semaphore(ha)); |
214 | spin_unlock_irqrestore(lock: &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(seconds: 1); |
222 | } while (--seconds); |
223 | return QLA_ERROR; |
224 | } |
225 | |
226 | void 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(val: sem_mask, addr: isp_semaphore(ha)); |
232 | readl(addr: isp_semaphore(ha)); |
233 | spin_unlock_irqrestore(lock: &ha->hardware_lock, flags); |
234 | |
235 | DEBUG2(printk("scsi%ld : UNLOCK SEM - mask= 0x%x\n" , ha->host_no, |
236 | sem_mask)); |
237 | } |
238 | |
239 | int 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(val: (sem_mask | sem_bits), addr: isp_semaphore(ha)); |
246 | value = readw(addr: isp_semaphore(ha)); |
247 | spin_unlock_irqrestore(lock: &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 | |