diff options
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_nvram.c')
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_nvram.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_nvram.c b/drivers/scsi/qla4xxx/ql4_nvram.c new file mode 100644 index 000000000000..e3957ca5b645 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_nvram.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
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 | |||
10 | static inline int eeprom_size(struct scsi_qla_host *ha) | ||
11 | { | ||
12 | return is_qla4022(ha) ? FM93C86A_SIZE_16 : FM93C66A_SIZE_16; | ||
13 | } | ||
14 | |||
15 | static 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 | |||
21 | static inline int eeprom_no_data_bits(struct scsi_qla_host *ha) | ||
22 | { | ||
23 | return FM93C56A_DATA_BITS_16; | ||
24 | } | ||
25 | |||
26 | static 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 | |||
36 | static 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 | |||
98 | static 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 | |||
106 | static 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 | |||
128 | static 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 */ | ||
139 | u16 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 | |||
148 | int 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 | *************************************************************************/ | ||
171 | int 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 | |||
195 | void 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 | |||
208 | int 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 | } | ||