diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_sup.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_sup.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c new file mode 100644 index 000000000000..0e75fbb77b6b --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_sup.c | |||
@@ -0,0 +1,296 @@ | |||
1 | /****************************************************************************** | ||
2 | * QLOGIC LINUX SOFTWARE | ||
3 | * | ||
4 | * QLogic ISP2x00 device driver for Linux 2.6.x | ||
5 | * Copyright (C) 2003-2004 QLogic Corporation | ||
6 | * (www.qlogic.com) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2, or (at your option) any | ||
11 | * later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | ******************************************************************************/ | ||
19 | |||
20 | #include "qla_def.h" | ||
21 | |||
22 | #include <linux/delay.h> | ||
23 | #include <asm/uaccess.h> | ||
24 | |||
25 | static uint16_t qla2x00_nvram_request(scsi_qla_host_t *, uint32_t); | ||
26 | static void qla2x00_nv_deselect(scsi_qla_host_t *); | ||
27 | static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t); | ||
28 | |||
29 | /* | ||
30 | * NVRAM support routines | ||
31 | */ | ||
32 | |||
33 | /** | ||
34 | * qla2x00_lock_nvram_access() - | ||
35 | * @ha: HA context | ||
36 | */ | ||
37 | void | ||
38 | qla2x00_lock_nvram_access(scsi_qla_host_t *ha) | ||
39 | { | ||
40 | uint16_t data; | ||
41 | device_reg_t __iomem *reg = ha->iobase; | ||
42 | |||
43 | if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) { | ||
44 | data = RD_REG_WORD(®->nvram); | ||
45 | while (data & NVR_BUSY) { | ||
46 | udelay(100); | ||
47 | data = RD_REG_WORD(®->nvram); | ||
48 | } | ||
49 | |||
50 | /* Lock resource */ | ||
51 | WRT_REG_WORD(®->u.isp2300.host_semaphore, 0x1); | ||
52 | RD_REG_WORD(®->u.isp2300.host_semaphore); | ||
53 | udelay(5); | ||
54 | data = RD_REG_WORD(®->u.isp2300.host_semaphore); | ||
55 | while ((data & BIT_0) == 0) { | ||
56 | /* Lock failed */ | ||
57 | udelay(100); | ||
58 | WRT_REG_WORD(®->u.isp2300.host_semaphore, 0x1); | ||
59 | RD_REG_WORD(®->u.isp2300.host_semaphore); | ||
60 | udelay(5); | ||
61 | data = RD_REG_WORD(®->u.isp2300.host_semaphore); | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * qla2x00_unlock_nvram_access() - | ||
68 | * @ha: HA context | ||
69 | */ | ||
70 | void | ||
71 | qla2x00_unlock_nvram_access(scsi_qla_host_t *ha) | ||
72 | { | ||
73 | device_reg_t __iomem *reg = ha->iobase; | ||
74 | |||
75 | if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) { | ||
76 | WRT_REG_WORD(®->u.isp2300.host_semaphore, 0); | ||
77 | RD_REG_WORD(®->u.isp2300.host_semaphore); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * qla2x00_release_nvram_protection() - | ||
83 | * @ha: HA context | ||
84 | */ | ||
85 | void | ||
86 | qla2x00_release_nvram_protection(scsi_qla_host_t *ha) | ||
87 | { | ||
88 | device_reg_t *reg; | ||
89 | uint32_t word; | ||
90 | |||
91 | reg = ha->iobase; | ||
92 | |||
93 | /* Release NVRAM write protection. */ | ||
94 | if (IS_QLA2322(ha) || IS_QLA6322(ha)) { | ||
95 | /* Write enable. */ | ||
96 | qla2x00_nv_write(ha, NVR_DATA_OUT); | ||
97 | qla2x00_nv_write(ha, 0); | ||
98 | qla2x00_nv_write(ha, 0); | ||
99 | for (word = 0; word < 8; word++) | ||
100 | qla2x00_nv_write(ha, NVR_DATA_OUT); | ||
101 | |||
102 | qla2x00_nv_deselect(ha); | ||
103 | |||
104 | /* Enable protection register. */ | ||
105 | qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT); | ||
106 | qla2x00_nv_write(ha, NVR_PR_ENABLE); | ||
107 | qla2x00_nv_write(ha, NVR_PR_ENABLE); | ||
108 | for (word = 0; word < 8; word++) | ||
109 | qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE); | ||
110 | |||
111 | qla2x00_nv_deselect(ha); | ||
112 | |||
113 | /* Clear protection register (ffff is cleared). */ | ||
114 | qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT); | ||
115 | qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT); | ||
116 | qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT); | ||
117 | for (word = 0; word < 8; word++) | ||
118 | qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE); | ||
119 | |||
120 | qla2x00_nv_deselect(ha); | ||
121 | |||
122 | /* Wait for NVRAM to become ready. */ | ||
123 | WRT_REG_WORD(®->nvram, NVR_SELECT); | ||
124 | do { | ||
125 | NVRAM_DELAY(); | ||
126 | word = RD_REG_WORD(®->nvram); | ||
127 | } while ((word & NVR_DATA_IN) == 0); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the | ||
133 | * request routine to get the word from NVRAM. | ||
134 | * @ha: HA context | ||
135 | * @addr: Address in NVRAM to read | ||
136 | * | ||
137 | * Returns the word read from nvram @addr. | ||
138 | */ | ||
139 | uint16_t | ||
140 | qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr) | ||
141 | { | ||
142 | uint16_t data; | ||
143 | uint32_t nv_cmd; | ||
144 | |||
145 | nv_cmd = addr << 16; | ||
146 | nv_cmd |= NV_READ_OP; | ||
147 | data = qla2x00_nvram_request(ha, nv_cmd); | ||
148 | |||
149 | return (data); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * qla2x00_write_nvram_word() - Write NVRAM data. | ||
154 | * @ha: HA context | ||
155 | * @addr: Address in NVRAM to write | ||
156 | * @data: word to program | ||
157 | */ | ||
158 | void | ||
159 | qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data) | ||
160 | { | ||
161 | int count; | ||
162 | uint16_t word; | ||
163 | uint32_t nv_cmd; | ||
164 | device_reg_t __iomem *reg = ha->iobase; | ||
165 | |||
166 | qla2x00_nv_write(ha, NVR_DATA_OUT); | ||
167 | qla2x00_nv_write(ha, 0); | ||
168 | qla2x00_nv_write(ha, 0); | ||
169 | |||
170 | for (word = 0; word < 8; word++) | ||
171 | qla2x00_nv_write(ha, NVR_DATA_OUT); | ||
172 | |||
173 | qla2x00_nv_deselect(ha); | ||
174 | |||
175 | /* Write data */ | ||
176 | nv_cmd = (addr << 16) | NV_WRITE_OP; | ||
177 | nv_cmd |= data; | ||
178 | nv_cmd <<= 5; | ||
179 | for (count = 0; count < 27; count++) { | ||
180 | if (nv_cmd & BIT_31) | ||
181 | qla2x00_nv_write(ha, NVR_DATA_OUT); | ||
182 | else | ||
183 | qla2x00_nv_write(ha, 0); | ||
184 | |||
185 | nv_cmd <<= 1; | ||
186 | } | ||
187 | |||
188 | qla2x00_nv_deselect(ha); | ||
189 | |||
190 | /* Wait for NVRAM to become ready */ | ||
191 | WRT_REG_WORD(®->nvram, NVR_SELECT); | ||
192 | do { | ||
193 | NVRAM_DELAY(); | ||
194 | word = RD_REG_WORD(®->nvram); | ||
195 | } while ((word & NVR_DATA_IN) == 0); | ||
196 | |||
197 | qla2x00_nv_deselect(ha); | ||
198 | |||
199 | /* Disable writes */ | ||
200 | qla2x00_nv_write(ha, NVR_DATA_OUT); | ||
201 | for (count = 0; count < 10; count++) | ||
202 | qla2x00_nv_write(ha, 0); | ||
203 | |||
204 | qla2x00_nv_deselect(ha); | ||
205 | } | ||
206 | |||
207 | /** | ||
208 | * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from | ||
209 | * NVRAM. | ||
210 | * @ha: HA context | ||
211 | * @nv_cmd: NVRAM command | ||
212 | * | ||
213 | * Bit definitions for NVRAM command: | ||
214 | * | ||
215 | * Bit 26 = start bit | ||
216 | * Bit 25, 24 = opcode | ||
217 | * Bit 23-16 = address | ||
218 | * Bit 15-0 = write data | ||
219 | * | ||
220 | * Returns the word read from nvram @addr. | ||
221 | */ | ||
222 | static uint16_t | ||
223 | qla2x00_nvram_request(scsi_qla_host_t *ha, uint32_t nv_cmd) | ||
224 | { | ||
225 | uint8_t cnt; | ||
226 | device_reg_t __iomem *reg = ha->iobase; | ||
227 | uint16_t data = 0; | ||
228 | uint16_t reg_data; | ||
229 | |||
230 | /* Send command to NVRAM. */ | ||
231 | nv_cmd <<= 5; | ||
232 | for (cnt = 0; cnt < 11; cnt++) { | ||
233 | if (nv_cmd & BIT_31) | ||
234 | qla2x00_nv_write(ha, NVR_DATA_OUT); | ||
235 | else | ||
236 | qla2x00_nv_write(ha, 0); | ||
237 | nv_cmd <<= 1; | ||
238 | } | ||
239 | |||
240 | /* Read data from NVRAM. */ | ||
241 | for (cnt = 0; cnt < 16; cnt++) { | ||
242 | WRT_REG_WORD(®->nvram, NVR_SELECT | NVR_CLOCK); | ||
243 | NVRAM_DELAY(); | ||
244 | data <<= 1; | ||
245 | reg_data = RD_REG_WORD(®->nvram); | ||
246 | if (reg_data & NVR_DATA_IN) | ||
247 | data |= BIT_0; | ||
248 | WRT_REG_WORD(®->nvram, NVR_SELECT); | ||
249 | RD_REG_WORD(®->nvram); /* PCI Posting. */ | ||
250 | NVRAM_DELAY(); | ||
251 | } | ||
252 | |||
253 | /* Deselect chip. */ | ||
254 | WRT_REG_WORD(®->nvram, NVR_DESELECT); | ||
255 | RD_REG_WORD(®->nvram); /* PCI Posting. */ | ||
256 | NVRAM_DELAY(); | ||
257 | |||
258 | return (data); | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * qla2x00_nv_write() - Clean NVRAM operations. | ||
263 | * @ha: HA context | ||
264 | */ | ||
265 | static void | ||
266 | qla2x00_nv_deselect(scsi_qla_host_t *ha) | ||
267 | { | ||
268 | device_reg_t __iomem *reg = ha->iobase; | ||
269 | |||
270 | WRT_REG_WORD(®->nvram, NVR_DESELECT); | ||
271 | RD_REG_WORD(®->nvram); /* PCI Posting. */ | ||
272 | NVRAM_DELAY(); | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * qla2x00_nv_write() - Prepare for NVRAM read/write operation. | ||
277 | * @ha: HA context | ||
278 | * @data: Serial interface selector | ||
279 | */ | ||
280 | static void | ||
281 | qla2x00_nv_write(scsi_qla_host_t *ha, uint16_t data) | ||
282 | { | ||
283 | device_reg_t __iomem *reg = ha->iobase; | ||
284 | |||
285 | WRT_REG_WORD(®->nvram, data | NVR_SELECT | NVR_WRT_ENABLE); | ||
286 | RD_REG_WORD(®->nvram); /* PCI Posting. */ | ||
287 | NVRAM_DELAY(); | ||
288 | WRT_REG_WORD(®->nvram, data | NVR_SELECT| NVR_CLOCK | | ||
289 | NVR_WRT_ENABLE); | ||
290 | RD_REG_WORD(®->nvram); /* PCI Posting. */ | ||
291 | NVRAM_DELAY(); | ||
292 | WRT_REG_WORD(®->nvram, data | NVR_SELECT | NVR_WRT_ENABLE); | ||
293 | RD_REG_WORD(®->nvram); /* PCI Posting. */ | ||
294 | NVRAM_DELAY(); | ||
295 | } | ||
296 | |||