blob: cf2a6bc444d2d217412076aba9ba504ebc21eea7 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/******************************************************************************
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
25static uint16_t qla2x00_nvram_request(scsi_qla_host_t *, uint32_t);
26static void qla2x00_nv_deselect(scsi_qla_host_t *);
27static 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 */
37void
38qla2x00_lock_nvram_access(scsi_qla_host_t *ha)
39{
40 uint16_t data;
Andrew Vasquez3d716442005-07-06 10:30:26 -070041 struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
43 if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
44 data = RD_REG_WORD(&reg->nvram);
45 while (data & NVR_BUSY) {
46 udelay(100);
47 data = RD_REG_WORD(&reg->nvram);
48 }
49
50 /* Lock resource */
51 WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);
52 RD_REG_WORD(&reg->u.isp2300.host_semaphore);
53 udelay(5);
54 data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);
55 while ((data & BIT_0) == 0) {
56 /* Lock failed */
57 udelay(100);
58 WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);
59 RD_REG_WORD(&reg->u.isp2300.host_semaphore);
60 udelay(5);
61 data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);
62 }
63 }
64}
65
66/**
67 * qla2x00_unlock_nvram_access() -
68 * @ha: HA context
69 */
70void
71qla2x00_unlock_nvram_access(scsi_qla_host_t *ha)
72{
Andrew Vasquez3d716442005-07-06 10:30:26 -070073 struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
75 if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
76 WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0);
77 RD_REG_WORD(&reg->u.isp2300.host_semaphore);
78 }
79}
80
81/**
82 * qla2x00_release_nvram_protection() -
83 * @ha: HA context
84 */
85void
86qla2x00_release_nvram_protection(scsi_qla_host_t *ha)
87{
Andrew Vasquez3d716442005-07-06 10:30:26 -070088 struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 uint32_t word;
90
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 /* Release NVRAM write protection. */
92 if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
93 /* Write enable. */
94 qla2x00_nv_write(ha, NVR_DATA_OUT);
95 qla2x00_nv_write(ha, 0);
96 qla2x00_nv_write(ha, 0);
97 for (word = 0; word < 8; word++)
98 qla2x00_nv_write(ha, NVR_DATA_OUT);
99
100 qla2x00_nv_deselect(ha);
101
102 /* Enable protection register. */
103 qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
104 qla2x00_nv_write(ha, NVR_PR_ENABLE);
105 qla2x00_nv_write(ha, NVR_PR_ENABLE);
106 for (word = 0; word < 8; word++)
107 qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
108
109 qla2x00_nv_deselect(ha);
110
111 /* Clear protection register (ffff is cleared). */
112 qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
113 qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
114 qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
115 for (word = 0; word < 8; word++)
116 qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
117
118 qla2x00_nv_deselect(ha);
119
120 /* Wait for NVRAM to become ready. */
121 WRT_REG_WORD(&reg->nvram, NVR_SELECT);
122 do {
123 NVRAM_DELAY();
124 word = RD_REG_WORD(&reg->nvram);
125 } while ((word & NVR_DATA_IN) == 0);
126 }
127}
128
129/**
130 * qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the
131 * request routine to get the word from NVRAM.
132 * @ha: HA context
133 * @addr: Address in NVRAM to read
134 *
135 * Returns the word read from nvram @addr.
136 */
137uint16_t
138qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr)
139{
140 uint16_t data;
141 uint32_t nv_cmd;
142
143 nv_cmd = addr << 16;
144 nv_cmd |= NV_READ_OP;
145 data = qla2x00_nvram_request(ha, nv_cmd);
146
147 return (data);
148}
149
150/**
151 * qla2x00_write_nvram_word() - Write NVRAM data.
152 * @ha: HA context
153 * @addr: Address in NVRAM to write
154 * @data: word to program
155 */
156void
157qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
158{
159 int count;
160 uint16_t word;
161 uint32_t nv_cmd;
Andrew Vasquez3d716442005-07-06 10:30:26 -0700162 struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
164 qla2x00_nv_write(ha, NVR_DATA_OUT);
165 qla2x00_nv_write(ha, 0);
166 qla2x00_nv_write(ha, 0);
167
168 for (word = 0; word < 8; word++)
169 qla2x00_nv_write(ha, NVR_DATA_OUT);
170
171 qla2x00_nv_deselect(ha);
172
173 /* Write data */
174 nv_cmd = (addr << 16) | NV_WRITE_OP;
175 nv_cmd |= data;
176 nv_cmd <<= 5;
177 for (count = 0; count < 27; count++) {
178 if (nv_cmd & BIT_31)
179 qla2x00_nv_write(ha, NVR_DATA_OUT);
180 else
181 qla2x00_nv_write(ha, 0);
182
183 nv_cmd <<= 1;
184 }
185
186 qla2x00_nv_deselect(ha);
187
188 /* Wait for NVRAM to become ready */
189 WRT_REG_WORD(&reg->nvram, NVR_SELECT);
190 do {
191 NVRAM_DELAY();
192 word = RD_REG_WORD(&reg->nvram);
193 } while ((word & NVR_DATA_IN) == 0);
194
195 qla2x00_nv_deselect(ha);
196
197 /* Disable writes */
198 qla2x00_nv_write(ha, NVR_DATA_OUT);
199 for (count = 0; count < 10; count++)
200 qla2x00_nv_write(ha, 0);
201
202 qla2x00_nv_deselect(ha);
203}
204
205/**
206 * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
207 * NVRAM.
208 * @ha: HA context
209 * @nv_cmd: NVRAM command
210 *
211 * Bit definitions for NVRAM command:
212 *
213 * Bit 26 = start bit
214 * Bit 25, 24 = opcode
215 * Bit 23-16 = address
216 * Bit 15-0 = write data
217 *
218 * Returns the word read from nvram @addr.
219 */
220static uint16_t
221qla2x00_nvram_request(scsi_qla_host_t *ha, uint32_t nv_cmd)
222{
223 uint8_t cnt;
Andrew Vasquez3d716442005-07-06 10:30:26 -0700224 struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 uint16_t data = 0;
226 uint16_t reg_data;
227
228 /* Send command to NVRAM. */
229 nv_cmd <<= 5;
230 for (cnt = 0; cnt < 11; cnt++) {
231 if (nv_cmd & BIT_31)
232 qla2x00_nv_write(ha, NVR_DATA_OUT);
233 else
234 qla2x00_nv_write(ha, 0);
235 nv_cmd <<= 1;
236 }
237
238 /* Read data from NVRAM. */
239 for (cnt = 0; cnt < 16; cnt++) {
240 WRT_REG_WORD(&reg->nvram, NVR_SELECT | NVR_CLOCK);
241 NVRAM_DELAY();
242 data <<= 1;
243 reg_data = RD_REG_WORD(&reg->nvram);
244 if (reg_data & NVR_DATA_IN)
245 data |= BIT_0;
246 WRT_REG_WORD(&reg->nvram, NVR_SELECT);
247 RD_REG_WORD(&reg->nvram); /* PCI Posting. */
248 NVRAM_DELAY();
249 }
250
251 /* Deselect chip. */
252 WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
253 RD_REG_WORD(&reg->nvram); /* PCI Posting. */
254 NVRAM_DELAY();
255
256 return (data);
257}
258
259/**
260 * qla2x00_nv_write() - Clean NVRAM operations.
261 * @ha: HA context
262 */
263static void
264qla2x00_nv_deselect(scsi_qla_host_t *ha)
265{
Andrew Vasquez3d716442005-07-06 10:30:26 -0700266 struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
268 WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
269 RD_REG_WORD(&reg->nvram); /* PCI Posting. */
270 NVRAM_DELAY();
271}
272
273/**
274 * qla2x00_nv_write() - Prepare for NVRAM read/write operation.
275 * @ha: HA context
276 * @data: Serial interface selector
277 */
278static void
279qla2x00_nv_write(scsi_qla_host_t *ha, uint16_t data)
280{
Andrew Vasquez3d716442005-07-06 10:30:26 -0700281 struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
283 WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
284 RD_REG_WORD(&reg->nvram); /* PCI Posting. */
285 NVRAM_DELAY();
286 WRT_REG_WORD(&reg->nvram, data | NVR_SELECT| NVR_CLOCK |
287 NVR_WRT_ENABLE);
288 RD_REG_WORD(&reg->nvram); /* PCI Posting. */
289 NVRAM_DELAY();
290 WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
291 RD_REG_WORD(&reg->nvram); /* PCI Posting. */
292 NVRAM_DELAY();
293}
294