]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 |