Commit | Line | Data |
---|---|---|
2908d778 JB |
1 | /* |
2 | * Aic94xx SAS/SATA driver access to shared data structures and memory | |
3 | * maps. | |
4 | * | |
5 | * Copyright (C) 2005 Adaptec, Inc. All rights reserved. | |
6 | * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> | |
7 | * | |
8 | * This file is licensed under GPLv2. | |
9 | * | |
10 | * This file is part of the aic94xx driver. | |
11 | * | |
12 | * The aic94xx driver is free software; you can redistribute it and/or | |
13 | * modify it under the terms of the GNU General Public License as | |
14 | * published by the Free Software Foundation; version 2 of the | |
15 | * License. | |
16 | * | |
17 | * The aic94xx driver is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
20 | * General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with the aic94xx driver; if not, write to the Free Software | |
24 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
25 | * | |
26 | */ | |
27 | ||
28 | #include <linux/pci.h> | |
5a0e3ad6 | 29 | #include <linux/slab.h> |
2908d778 JB |
30 | #include <linux/delay.h> |
31 | ||
32 | #include "aic94xx.h" | |
33 | #include "aic94xx_reg.h" | |
1237c98d | 34 | #include "aic94xx_sds.h" |
2908d778 JB |
35 | |
36 | /* ---------- OCM stuff ---------- */ | |
37 | ||
38 | struct asd_ocm_dir_ent { | |
39 | u8 type; | |
40 | u8 offs[3]; | |
41 | u8 _r1; | |
42 | u8 size[3]; | |
43 | } __attribute__ ((packed)); | |
44 | ||
45 | struct asd_ocm_dir { | |
46 | char sig[2]; | |
47 | u8 _r1[2]; | |
48 | u8 major; /* 0 */ | |
49 | u8 minor; /* 0 */ | |
50 | u8 _r2; | |
51 | u8 num_de; | |
52 | struct asd_ocm_dir_ent entry[15]; | |
53 | } __attribute__ ((packed)); | |
54 | ||
55 | #define OCM_DE_OCM_DIR 0x00 | |
56 | #define OCM_DE_WIN_DRVR 0x01 | |
57 | #define OCM_DE_BIOS_CHIM 0x02 | |
58 | #define OCM_DE_RAID_ENGN 0x03 | |
59 | #define OCM_DE_BIOS_INTL 0x04 | |
60 | #define OCM_DE_BIOS_CHIM_OSM 0x05 | |
61 | #define OCM_DE_BIOS_CHIM_DYNAMIC 0x06 | |
62 | #define OCM_DE_ADDC2C_RES0 0x07 | |
63 | #define OCM_DE_ADDC2C_RES1 0x08 | |
64 | #define OCM_DE_ADDC2C_RES2 0x09 | |
65 | #define OCM_DE_ADDC2C_RES3 0x0A | |
66 | ||
67 | #define OCM_INIT_DIR_ENTRIES 5 | |
68 | /*************************************************************************** | |
0779bf2d | 69 | * OCM directory default |
2908d778 JB |
70 | ***************************************************************************/ |
71 | static struct asd_ocm_dir OCMDirInit = | |
72 | { | |
73 | .sig = {0x4D, 0x4F}, /* signature */ | |
74 | .num_de = OCM_INIT_DIR_ENTRIES, /* no. of directory entries */ | |
75 | }; | |
76 | ||
77 | /*************************************************************************** | |
0779bf2d | 78 | * OCM directory Entries default |
2908d778 JB |
79 | ***************************************************************************/ |
80 | static struct asd_ocm_dir_ent OCMDirEntriesInit[OCM_INIT_DIR_ENTRIES] = | |
81 | { | |
82 | { | |
83 | .type = (OCM_DE_ADDC2C_RES0), /* Entry type */ | |
84 | .offs = {128}, /* Offset */ | |
85 | .size = {0, 4}, /* size */ | |
86 | }, | |
87 | { | |
88 | .type = (OCM_DE_ADDC2C_RES1), /* Entry type */ | |
89 | .offs = {128, 4}, /* Offset */ | |
90 | .size = {0, 4}, /* size */ | |
91 | }, | |
92 | { | |
93 | .type = (OCM_DE_ADDC2C_RES2), /* Entry type */ | |
94 | .offs = {128, 8}, /* Offset */ | |
95 | .size = {0, 4}, /* size */ | |
96 | }, | |
97 | { | |
98 | .type = (OCM_DE_ADDC2C_RES3), /* Entry type */ | |
99 | .offs = {128, 12}, /* Offset */ | |
100 | .size = {0, 4}, /* size */ | |
101 | }, | |
102 | { | |
103 | .type = (OCM_DE_WIN_DRVR), /* Entry type */ | |
104 | .offs = {128, 16}, /* Offset */ | |
105 | .size = {128, 235, 1}, /* size */ | |
106 | }, | |
107 | }; | |
108 | ||
109 | struct asd_bios_chim_struct { | |
110 | char sig[4]; | |
111 | u8 major; /* 1 */ | |
112 | u8 minor; /* 0 */ | |
113 | u8 bios_major; | |
114 | u8 bios_minor; | |
115 | __le32 bios_build; | |
116 | u8 flags; | |
117 | u8 pci_slot; | |
118 | __le16 ue_num; | |
119 | __le16 ue_size; | |
120 | u8 _r[14]; | |
121 | /* The unit element array is right here. | |
122 | */ | |
123 | } __attribute__ ((packed)); | |
124 | ||
125 | /** | |
126 | * asd_read_ocm_seg - read an on chip memory (OCM) segment | |
127 | * @asd_ha: pointer to the host adapter structure | |
128 | * @buffer: where to write the read data | |
129 | * @offs: offset into OCM where to read from | |
130 | * @size: how many bytes to read | |
131 | * | |
132 | * Return the number of bytes not read. Return 0 on success. | |
133 | */ | |
134 | static int asd_read_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer, | |
135 | u32 offs, int size) | |
136 | { | |
137 | u8 *p = buffer; | |
138 | if (unlikely(asd_ha->iospace)) | |
139 | asd_read_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size); | |
140 | else { | |
141 | for ( ; size > 0; size--, offs++, p++) | |
142 | *p = asd_read_ocm_byte(asd_ha, offs); | |
143 | } | |
144 | return size; | |
145 | } | |
146 | ||
147 | static int asd_read_ocm_dir(struct asd_ha_struct *asd_ha, | |
148 | struct asd_ocm_dir *dir, u32 offs) | |
149 | { | |
150 | int err = asd_read_ocm_seg(asd_ha, dir, offs, sizeof(*dir)); | |
151 | if (err) { | |
152 | ASD_DPRINTK("couldn't read ocm segment\n"); | |
153 | return err; | |
154 | } | |
155 | ||
156 | if (dir->sig[0] != 'M' || dir->sig[1] != 'O') { | |
157 | ASD_DPRINTK("no valid dir signature(%c%c) at start of OCM\n", | |
158 | dir->sig[0], dir->sig[1]); | |
159 | return -ENOENT; | |
160 | } | |
161 | if (dir->major != 0) { | |
162 | asd_printk("unsupported major version of ocm dir:0x%x\n", | |
163 | dir->major); | |
164 | return -ENOENT; | |
165 | } | |
166 | dir->num_de &= 0xf; | |
167 | return 0; | |
168 | } | |
169 | ||
170 | /** | |
171 | * asd_write_ocm_seg - write an on chip memory (OCM) segment | |
172 | * @asd_ha: pointer to the host adapter structure | |
173 | * @buffer: where to read the write data | |
174 | * @offs: offset into OCM to write to | |
175 | * @size: how many bytes to write | |
176 | * | |
177 | * Return the number of bytes not written. Return 0 on success. | |
178 | */ | |
179 | static void asd_write_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer, | |
180 | u32 offs, int size) | |
181 | { | |
182 | u8 *p = buffer; | |
183 | if (unlikely(asd_ha->iospace)) | |
184 | asd_write_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size); | |
185 | else { | |
186 | for ( ; size > 0; size--, offs++, p++) | |
187 | asd_write_ocm_byte(asd_ha, offs, *p); | |
188 | } | |
189 | return; | |
190 | } | |
191 | ||
192 | #define THREE_TO_NUM(X) ((X)[0] | ((X)[1] << 8) | ((X)[2] << 16)) | |
193 | ||
194 | static int asd_find_dir_entry(struct asd_ocm_dir *dir, u8 type, | |
195 | u32 *offs, u32 *size) | |
196 | { | |
197 | int i; | |
198 | struct asd_ocm_dir_ent *ent; | |
199 | ||
200 | for (i = 0; i < dir->num_de; i++) { | |
201 | if (dir->entry[i].type == type) | |
202 | break; | |
203 | } | |
204 | if (i >= dir->num_de) | |
205 | return -ENOENT; | |
206 | ent = &dir->entry[i]; | |
207 | *offs = (u32) THREE_TO_NUM(ent->offs); | |
208 | *size = (u32) THREE_TO_NUM(ent->size); | |
209 | return 0; | |
210 | } | |
211 | ||
212 | #define OCM_BIOS_CHIM_DE 2 | |
213 | #define BC_BIOS_PRESENT 1 | |
214 | ||
215 | static int asd_get_bios_chim(struct asd_ha_struct *asd_ha, | |
216 | struct asd_ocm_dir *dir) | |
217 | { | |
218 | int err; | |
219 | struct asd_bios_chim_struct *bc_struct; | |
220 | u32 offs, size; | |
221 | ||
222 | err = asd_find_dir_entry(dir, OCM_BIOS_CHIM_DE, &offs, &size); | |
223 | if (err) { | |
224 | ASD_DPRINTK("couldn't find BIOS_CHIM dir ent\n"); | |
225 | goto out; | |
226 | } | |
227 | err = -ENOMEM; | |
228 | bc_struct = kmalloc(sizeof(*bc_struct), GFP_KERNEL); | |
229 | if (!bc_struct) { | |
230 | asd_printk("no memory for bios_chim struct\n"); | |
231 | goto out; | |
232 | } | |
233 | err = asd_read_ocm_seg(asd_ha, (void *)bc_struct, offs, | |
234 | sizeof(*bc_struct)); | |
235 | if (err) { | |
236 | ASD_DPRINTK("couldn't read ocm segment\n"); | |
237 | goto out2; | |
238 | } | |
239 | if (strncmp(bc_struct->sig, "SOIB", 4) | |
240 | && strncmp(bc_struct->sig, "IPSA", 4)) { | |
241 | ASD_DPRINTK("BIOS_CHIM entry has no valid sig(%c%c%c%c)\n", | |
242 | bc_struct->sig[0], bc_struct->sig[1], | |
243 | bc_struct->sig[2], bc_struct->sig[3]); | |
244 | err = -ENOENT; | |
245 | goto out2; | |
246 | } | |
247 | if (bc_struct->major != 1) { | |
248 | asd_printk("BIOS_CHIM unsupported major version:0x%x\n", | |
249 | bc_struct->major); | |
250 | err = -ENOENT; | |
251 | goto out2; | |
252 | } | |
253 | if (bc_struct->flags & BC_BIOS_PRESENT) { | |
254 | asd_ha->hw_prof.bios.present = 1; | |
255 | asd_ha->hw_prof.bios.maj = bc_struct->bios_major; | |
256 | asd_ha->hw_prof.bios.min = bc_struct->bios_minor; | |
257 | asd_ha->hw_prof.bios.bld = le32_to_cpu(bc_struct->bios_build); | |
258 | ASD_DPRINTK("BIOS present (%d,%d), %d\n", | |
259 | asd_ha->hw_prof.bios.maj, | |
260 | asd_ha->hw_prof.bios.min, | |
261 | asd_ha->hw_prof.bios.bld); | |
262 | } | |
263 | asd_ha->hw_prof.ue.num = le16_to_cpu(bc_struct->ue_num); | |
264 | asd_ha->hw_prof.ue.size= le16_to_cpu(bc_struct->ue_size); | |
265 | ASD_DPRINTK("ue num:%d, ue size:%d\n", asd_ha->hw_prof.ue.num, | |
266 | asd_ha->hw_prof.ue.size); | |
267 | size = asd_ha->hw_prof.ue.num * asd_ha->hw_prof.ue.size; | |
268 | if (size > 0) { | |
269 | err = -ENOMEM; | |
270 | asd_ha->hw_prof.ue.area = kmalloc(size, GFP_KERNEL); | |
271 | if (!asd_ha->hw_prof.ue.area) | |
272 | goto out2; | |
273 | err = asd_read_ocm_seg(asd_ha, (void *)asd_ha->hw_prof.ue.area, | |
274 | offs + sizeof(*bc_struct), size); | |
275 | if (err) { | |
276 | kfree(asd_ha->hw_prof.ue.area); | |
277 | asd_ha->hw_prof.ue.area = NULL; | |
278 | asd_ha->hw_prof.ue.num = 0; | |
279 | asd_ha->hw_prof.ue.size = 0; | |
280 | ASD_DPRINTK("couldn't read ue entries(%d)\n", err); | |
281 | } | |
282 | } | |
283 | out2: | |
284 | kfree(bc_struct); | |
285 | out: | |
286 | return err; | |
287 | } | |
288 | ||
289 | static void | |
290 | asd_hwi_initialize_ocm_dir (struct asd_ha_struct *asd_ha) | |
291 | { | |
292 | int i; | |
293 | ||
294 | /* Zero OCM */ | |
295 | for (i = 0; i < OCM_MAX_SIZE; i += 4) | |
296 | asd_write_ocm_dword(asd_ha, i, 0); | |
297 | ||
298 | /* Write Dir */ | |
299 | asd_write_ocm_seg(asd_ha, &OCMDirInit, 0, | |
300 | sizeof(struct asd_ocm_dir)); | |
301 | ||
302 | /* Write Dir Entries */ | |
303 | for (i = 0; i < OCM_INIT_DIR_ENTRIES; i++) | |
304 | asd_write_ocm_seg(asd_ha, &OCMDirEntriesInit[i], | |
305 | sizeof(struct asd_ocm_dir) + | |
306 | (i * sizeof(struct asd_ocm_dir_ent)) | |
307 | , sizeof(struct asd_ocm_dir_ent)); | |
308 | ||
309 | } | |
310 | ||
311 | static int | |
312 | asd_hwi_check_ocm_access (struct asd_ha_struct *asd_ha) | |
313 | { | |
314 | struct pci_dev *pcidev = asd_ha->pcidev; | |
315 | u32 reg; | |
316 | int err = 0; | |
317 | u32 v; | |
318 | ||
319 | /* check if OCM has been initialized by BIOS */ | |
320 | reg = asd_read_reg_dword(asd_ha, EXSICNFGR); | |
321 | ||
322 | if (!(reg & OCMINITIALIZED)) { | |
323 | err = pci_read_config_dword(pcidev, PCIC_INTRPT_STAT, &v); | |
324 | if (err) { | |
325 | asd_printk("couldn't access PCIC_INTRPT_STAT of %s\n", | |
326 | pci_name(pcidev)); | |
327 | goto out; | |
328 | } | |
329 | ||
330 | printk(KERN_INFO "OCM is not initialized by BIOS," | |
331 | "reinitialize it and ignore it, current IntrptStatus" | |
332 | "is 0x%x\n", v); | |
333 | ||
334 | if (v) | |
335 | err = pci_write_config_dword(pcidev, | |
336 | PCIC_INTRPT_STAT, v); | |
337 | if (err) { | |
338 | asd_printk("couldn't write PCIC_INTRPT_STAT of %s\n", | |
339 | pci_name(pcidev)); | |
340 | goto out; | |
341 | } | |
342 | ||
343 | asd_hwi_initialize_ocm_dir(asd_ha); | |
344 | ||
345 | } | |
346 | out: | |
347 | return err; | |
348 | } | |
349 | ||
350 | /** | |
351 | * asd_read_ocm - read on chip memory (OCM) | |
352 | * @asd_ha: pointer to the host adapter structure | |
353 | */ | |
354 | int asd_read_ocm(struct asd_ha_struct *asd_ha) | |
355 | { | |
356 | int err; | |
357 | struct asd_ocm_dir *dir; | |
358 | ||
359 | if (asd_hwi_check_ocm_access(asd_ha)) | |
360 | return -1; | |
361 | ||
362 | dir = kmalloc(sizeof(*dir), GFP_KERNEL); | |
363 | if (!dir) { | |
364 | asd_printk("no memory for ocm dir\n"); | |
365 | return -ENOMEM; | |
366 | } | |
367 | ||
368 | err = asd_read_ocm_dir(asd_ha, dir, 0); | |
369 | if (err) | |
370 | goto out; | |
371 | ||
372 | err = asd_get_bios_chim(asd_ha, dir); | |
373 | out: | |
374 | kfree(dir); | |
375 | return err; | |
376 | } | |
377 | ||
378 | /* ---------- FLASH stuff ---------- */ | |
379 | ||
380 | #define FLASH_RESET 0xF0 | |
2908d778 | 381 | |
d297a5d5 | 382 | #define ASD_FLASH_SIZE 0x200000 |
2908d778 JB |
383 | #define FLASH_DIR_COOKIE "*** ADAPTEC FLASH DIRECTORY *** " |
384 | #define FLASH_NEXT_ENTRY_OFFS 0x2000 | |
385 | #define FLASH_MAX_DIR_ENTRIES 32 | |
386 | ||
387 | #define FLASH_DE_TYPE_MASK 0x3FFFFFFF | |
388 | #define FLASH_DE_MS 0x120 | |
389 | #define FLASH_DE_CTRL_A_USER 0xE0 | |
390 | ||
391 | struct asd_flash_de { | |
392 | __le32 type; | |
393 | __le32 offs; | |
394 | __le32 pad_size; | |
395 | __le32 image_size; | |
396 | __le32 chksum; | |
397 | u8 _r[12]; | |
398 | u8 version[32]; | |
399 | } __attribute__ ((packed)); | |
400 | ||
401 | struct asd_flash_dir { | |
402 | u8 cookie[32]; | |
403 | __le32 rev; /* 2 */ | |
404 | __le32 chksum; | |
405 | __le32 chksum_antidote; | |
406 | __le32 bld; | |
407 | u8 bld_id[32]; /* build id data */ | |
408 | u8 ver_data[32]; /* date and time of build */ | |
409 | __le32 ae_mask; | |
410 | __le32 v_mask; | |
411 | __le32 oc_mask; | |
412 | u8 _r[20]; | |
413 | struct asd_flash_de dir_entry[FLASH_MAX_DIR_ENTRIES]; | |
414 | } __attribute__ ((packed)); | |
415 | ||
416 | struct asd_manuf_sec { | |
417 | char sig[2]; /* 'S', 'M' */ | |
418 | u16 offs_next; | |
419 | u8 maj; /* 0 */ | |
420 | u8 min; /* 0 */ | |
421 | u16 chksum; | |
422 | u16 size; | |
423 | u8 _r[6]; | |
424 | u8 sas_addr[SAS_ADDR_SIZE]; | |
425 | u8 pcba_sn[ASD_PCBA_SN_SIZE]; | |
426 | /* Here start the other segments */ | |
427 | u8 linked_list[0]; | |
428 | } __attribute__ ((packed)); | |
429 | ||
430 | struct asd_manuf_phy_desc { | |
431 | u8 state; /* low 4 bits */ | |
86b9c4c1 | 432 | #define MS_PHY_STATE_ENABLED 0 |
2908d778 JB |
433 | #define MS_PHY_STATE_REPORTED 1 |
434 | #define MS_PHY_STATE_HIDDEN 2 | |
435 | u8 phy_id; | |
436 | u16 _r; | |
437 | u8 phy_control_0; /* mode 5 reg 0x160 */ | |
438 | u8 phy_control_1; /* mode 5 reg 0x161 */ | |
439 | u8 phy_control_2; /* mode 5 reg 0x162 */ | |
440 | u8 phy_control_3; /* mode 5 reg 0x163 */ | |
441 | } __attribute__ ((packed)); | |
442 | ||
443 | struct asd_manuf_phy_param { | |
444 | char sig[2]; /* 'P', 'M' */ | |
445 | u16 next; | |
446 | u8 maj; /* 0 */ | |
447 | u8 min; /* 2 */ | |
448 | u8 num_phy_desc; /* 8 */ | |
449 | u8 phy_desc_size; /* 8 */ | |
450 | u8 _r[3]; | |
451 | u8 usage_model_id; | |
452 | u32 _r2; | |
453 | struct asd_manuf_phy_desc phy_desc[ASD_MAX_PHYS]; | |
454 | } __attribute__ ((packed)); | |
455 | ||
456 | #if 0 | |
457 | static const char *asd_sb_type[] = { | |
458 | "unknown", | |
459 | "SGPIO", | |
460 | [2 ... 0x7F] = "unknown", | |
461 | [0x80] = "ADPT_I2C", | |
462 | [0x81 ... 0xFF] = "VENDOR_UNIQUExx" | |
463 | }; | |
464 | #endif | |
465 | ||
466 | struct asd_ms_sb_desc { | |
467 | u8 type; | |
468 | u8 node_desc_index; | |
469 | u8 conn_desc_index; | |
470 | u8 _recvd[0]; | |
471 | } __attribute__ ((packed)); | |
472 | ||
473 | #if 0 | |
474 | static const char *asd_conn_type[] = { | |
475 | [0 ... 7] = "unknown", | |
476 | "SFF8470", | |
477 | "SFF8482", | |
478 | "SFF8484", | |
479 | [0x80] = "PCIX_DAUGHTER0", | |
480 | [0x81] = "SAS_DAUGHTER0", | |
481 | [0x82 ... 0xFF] = "VENDOR_UNIQUExx" | |
482 | }; | |
483 | ||
484 | static const char *asd_conn_location[] = { | |
485 | "unknown", | |
486 | "internal", | |
487 | "external", | |
488 | "board_to_board", | |
489 | }; | |
490 | #endif | |
491 | ||
492 | struct asd_ms_conn_desc { | |
493 | u8 type; | |
494 | u8 location; | |
495 | u8 num_sideband_desc; | |
496 | u8 size_sideband_desc; | |
497 | u32 _resvd; | |
498 | u8 name[16]; | |
499 | struct asd_ms_sb_desc sb_desc[0]; | |
500 | } __attribute__ ((packed)); | |
501 | ||
502 | struct asd_nd_phy_desc { | |
503 | u8 vp_attch_type; | |
504 | u8 attch_specific[0]; | |
505 | } __attribute__ ((packed)); | |
506 | ||
507 | #if 0 | |
508 | static const char *asd_node_type[] = { | |
509 | "IOP", | |
510 | "IO_CONTROLLER", | |
511 | "EXPANDER", | |
512 | "PORT_MULTIPLIER", | |
513 | "PORT_MULTIPLEXER", | |
514 | "MULTI_DROP_I2C_BUS", | |
515 | }; | |
516 | #endif | |
517 | ||
518 | struct asd_ms_node_desc { | |
519 | u8 type; | |
520 | u8 num_phy_desc; | |
521 | u8 size_phy_desc; | |
522 | u8 _resvd; | |
523 | u8 name[16]; | |
524 | struct asd_nd_phy_desc phy_desc[0]; | |
525 | } __attribute__ ((packed)); | |
526 | ||
527 | struct asd_ms_conn_map { | |
528 | char sig[2]; /* 'M', 'C' */ | |
529 | __le16 next; | |
530 | u8 maj; /* 0 */ | |
531 | u8 min; /* 0 */ | |
532 | __le16 cm_size; /* size of this struct */ | |
533 | u8 num_conn; | |
534 | u8 conn_size; | |
535 | u8 num_nodes; | |
536 | u8 usage_model_id; | |
537 | u32 _resvd; | |
538 | struct asd_ms_conn_desc conn_desc[0]; | |
539 | struct asd_ms_node_desc node_desc[0]; | |
540 | } __attribute__ ((packed)); | |
541 | ||
542 | struct asd_ctrla_phy_entry { | |
543 | u8 sas_addr[SAS_ADDR_SIZE]; | |
544 | u8 sas_link_rates; /* max in hi bits, min in low bits */ | |
545 | u8 flags; | |
546 | u8 sata_link_rates; | |
547 | u8 _r[5]; | |
548 | } __attribute__ ((packed)); | |
549 | ||
550 | struct asd_ctrla_phy_settings { | |
551 | u8 id0; /* P'h'y */ | |
552 | u8 _r; | |
553 | u16 next; | |
554 | u8 num_phys; /* number of PHYs in the PCI function */ | |
555 | u8 _r2[3]; | |
556 | struct asd_ctrla_phy_entry phy_ent[ASD_MAX_PHYS]; | |
557 | } __attribute__ ((packed)); | |
558 | ||
559 | struct asd_ll_el { | |
560 | u8 id0; | |
561 | u8 id1; | |
562 | __le16 next; | |
563 | u8 something_here[0]; | |
564 | } __attribute__ ((packed)); | |
565 | ||
566 | static int asd_poll_flash(struct asd_ha_struct *asd_ha) | |
567 | { | |
568 | int c; | |
569 | u8 d; | |
570 | ||
571 | for (c = 5000; c > 0; c--) { | |
572 | d = asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar); | |
573 | d ^= asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar); | |
574 | if (!d) | |
575 | return 0; | |
576 | udelay(5); | |
577 | } | |
578 | return -ENOENT; | |
579 | } | |
580 | ||
581 | static int asd_reset_flash(struct asd_ha_struct *asd_ha) | |
582 | { | |
583 | int err; | |
584 | ||
585 | err = asd_poll_flash(asd_ha); | |
586 | if (err) | |
587 | return err; | |
588 | asd_write_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar, FLASH_RESET); | |
589 | err = asd_poll_flash(asd_ha); | |
590 | ||
591 | return err; | |
592 | } | |
593 | ||
81e56ded AB |
594 | static int asd_read_flash_seg(struct asd_ha_struct *asd_ha, |
595 | void *buffer, u32 offs, int size) | |
2908d778 JB |
596 | { |
597 | asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs, | |
598 | size); | |
599 | return 0; | |
600 | } | |
601 | ||
602 | /** | |
603 | * asd_find_flash_dir - finds and reads the flash directory | |
604 | * @asd_ha: pointer to the host adapter structure | |
605 | * @flash_dir: pointer to flash directory structure | |
606 | * | |
607 | * If found, the flash directory segment will be copied to | |
608 | * @flash_dir. Return 1 if found, 0 if not. | |
609 | */ | |
610 | static int asd_find_flash_dir(struct asd_ha_struct *asd_ha, | |
611 | struct asd_flash_dir *flash_dir) | |
612 | { | |
613 | u32 v; | |
d297a5d5 | 614 | for (v = 0; v < ASD_FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) { |
2908d778 JB |
615 | asd_read_flash_seg(asd_ha, flash_dir, v, |
616 | sizeof(FLASH_DIR_COOKIE)-1); | |
617 | if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE, | |
618 | sizeof(FLASH_DIR_COOKIE)-1) == 0) { | |
619 | asd_ha->hw_prof.flash.dir_offs = v; | |
620 | asd_read_flash_seg(asd_ha, flash_dir, v, | |
621 | sizeof(*flash_dir)); | |
622 | return 1; | |
623 | } | |
624 | } | |
625 | return 0; | |
626 | } | |
627 | ||
628 | static int asd_flash_getid(struct asd_ha_struct *asd_ha) | |
629 | { | |
630 | int err = 0; | |
f2d719c6 | 631 | u32 reg; |
2908d778 JB |
632 | |
633 | reg = asd_read_reg_dword(asd_ha, EXSICNFGR); | |
634 | ||
2908d778 JB |
635 | if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR, |
636 | &asd_ha->hw_prof.flash.bar)) { | |
637 | asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n", | |
638 | pci_name(asd_ha->pcidev)); | |
639 | return -ENOENT; | |
640 | } | |
641 | asd_ha->hw_prof.flash.present = 1; | |
642 | asd_ha->hw_prof.flash.wide = reg & FLASHW ? 1 : 0; | |
643 | err = asd_reset_flash(asd_ha); | |
644 | if (err) { | |
645 | ASD_DPRINTK("couldn't reset flash(%d)\n", err); | |
646 | return err; | |
647 | } | |
f2d719c6 | 648 | return 0; |
2908d778 JB |
649 | } |
650 | ||
651 | static u16 asd_calc_flash_chksum(u16 *p, int size) | |
652 | { | |
653 | u16 chksum = 0; | |
654 | ||
655 | while (size-- > 0) | |
656 | chksum += *p++; | |
657 | ||
658 | return chksum; | |
659 | } | |
660 | ||
661 | ||
662 | static int asd_find_flash_de(struct asd_flash_dir *flash_dir, u32 entry_type, | |
663 | u32 *offs, u32 *size) | |
664 | { | |
665 | int i; | |
666 | struct asd_flash_de *de; | |
667 | ||
668 | for (i = 0; i < FLASH_MAX_DIR_ENTRIES; i++) { | |
669 | u32 type = le32_to_cpu(flash_dir->dir_entry[i].type); | |
670 | ||
671 | type &= FLASH_DE_TYPE_MASK; | |
672 | if (type == entry_type) | |
673 | break; | |
674 | } | |
675 | if (i >= FLASH_MAX_DIR_ENTRIES) | |
676 | return -ENOENT; | |
677 | de = &flash_dir->dir_entry[i]; | |
678 | *offs = le32_to_cpu(de->offs); | |
679 | *size = le32_to_cpu(de->pad_size); | |
680 | return 0; | |
681 | } | |
682 | ||
683 | static int asd_validate_ms(struct asd_manuf_sec *ms) | |
684 | { | |
685 | if (ms->sig[0] != 'S' || ms->sig[1] != 'M') { | |
686 | ASD_DPRINTK("manuf sec: no valid sig(%c%c)\n", | |
687 | ms->sig[0], ms->sig[1]); | |
688 | return -ENOENT; | |
689 | } | |
690 | if (ms->maj != 0) { | |
691 | asd_printk("unsupported manuf. sector. major version:%x\n", | |
692 | ms->maj); | |
693 | return -ENOENT; | |
694 | } | |
695 | ms->offs_next = le16_to_cpu((__force __le16) ms->offs_next); | |
696 | ms->chksum = le16_to_cpu((__force __le16) ms->chksum); | |
697 | ms->size = le16_to_cpu((__force __le16) ms->size); | |
698 | ||
699 | if (asd_calc_flash_chksum((u16 *)ms, ms->size/2)) { | |
700 | asd_printk("failed manuf sector checksum\n"); | |
701 | } | |
702 | ||
703 | return 0; | |
704 | } | |
705 | ||
706 | static int asd_ms_get_sas_addr(struct asd_ha_struct *asd_ha, | |
707 | struct asd_manuf_sec *ms) | |
708 | { | |
709 | memcpy(asd_ha->hw_prof.sas_addr, ms->sas_addr, SAS_ADDR_SIZE); | |
710 | return 0; | |
711 | } | |
712 | ||
713 | static int asd_ms_get_pcba_sn(struct asd_ha_struct *asd_ha, | |
714 | struct asd_manuf_sec *ms) | |
715 | { | |
716 | memcpy(asd_ha->hw_prof.pcba_sn, ms->pcba_sn, ASD_PCBA_SN_SIZE); | |
717 | asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE] = '\0'; | |
718 | return 0; | |
719 | } | |
720 | ||
721 | /** | |
722 | * asd_find_ll_by_id - find a linked list entry by its id | |
723 | * @start: void pointer to the first element in the linked list | |
724 | * @id0: the first byte of the id (offs 0) | |
725 | * @id1: the second byte of the id (offs 1) | |
726 | * | |
727 | * @start has to be the _base_ element start, since the | |
728 | * linked list entries's offset is from this pointer. | |
729 | * Some linked list entries use only the first id, in which case | |
730 | * you can pass 0xFF for the second. | |
731 | */ | |
732 | static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1) | |
733 | { | |
734 | struct asd_ll_el *el = start; | |
735 | ||
736 | do { | |
737 | switch (id1) { | |
738 | default: | |
739 | if (el->id1 == id1) | |
740 | case 0xFF: | |
741 | if (el->id0 == id0) | |
742 | return el; | |
743 | } | |
744 | el = start + le16_to_cpu(el->next); | |
745 | } while (el != start); | |
746 | ||
747 | return NULL; | |
748 | } | |
749 | ||
750 | /** | |
751 | * asd_ms_get_phy_params - get phy parameters from the manufacturing sector | |
752 | * @asd_ha: pointer to the host adapter structure | |
753 | * @manuf_sec: pointer to the manufacturing sector | |
754 | * | |
755 | * The manufacturing sector contans also the linked list of sub-segments, | |
756 | * since when it was read, its size was taken from the flash directory, | |
757 | * not from the structure size. | |
758 | * | |
759 | * HIDDEN phys do not count in the total count. REPORTED phys cannot | |
760 | * be enabled but are reported and counted towards the total. | |
86b9c4c1 | 761 | * ENABLED phys are enabled by default and count towards the total. |
2908d778 JB |
762 | * The absolute total phy number is ASD_MAX_PHYS. hw_prof->num_phys |
763 | * merely specifies the number of phys the host adapter decided to | |
764 | * report. E.g., it is possible for phys 0, 1 and 2 to be HIDDEN, | |
86b9c4c1 | 765 | * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENABLED. |
2908d778 JB |
766 | * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2 |
767 | * are actually enabled (enabled by default, max number of phys | |
768 | * enableable in this case). | |
769 | */ | |
770 | static int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha, | |
771 | struct asd_manuf_sec *manuf_sec) | |
772 | { | |
773 | int i; | |
774 | int en_phys = 0; | |
775 | int rep_phys = 0; | |
776 | struct asd_manuf_phy_param *phy_param; | |
777 | struct asd_manuf_phy_param dflt_phy_param; | |
778 | ||
779 | phy_param = asd_find_ll_by_id(manuf_sec, 'P', 'M'); | |
780 | if (!phy_param) { | |
781 | ASD_DPRINTK("ms: no phy parameters found\n"); | |
782 | ASD_DPRINTK("ms: Creating default phy parameters\n"); | |
783 | dflt_phy_param.sig[0] = 'P'; | |
784 | dflt_phy_param.sig[1] = 'M'; | |
785 | dflt_phy_param.maj = 0; | |
786 | dflt_phy_param.min = 2; | |
787 | dflt_phy_param.num_phy_desc = 8; | |
788 | dflt_phy_param.phy_desc_size = sizeof(struct asd_manuf_phy_desc); | |
789 | for (i =0; i < ASD_MAX_PHYS; i++) { | |
790 | dflt_phy_param.phy_desc[i].state = 0; | |
791 | dflt_phy_param.phy_desc[i].phy_id = i; | |
792 | dflt_phy_param.phy_desc[i].phy_control_0 = 0xf6; | |
793 | dflt_phy_param.phy_desc[i].phy_control_1 = 0x10; | |
794 | dflt_phy_param.phy_desc[i].phy_control_2 = 0x43; | |
795 | dflt_phy_param.phy_desc[i].phy_control_3 = 0xeb; | |
796 | } | |
797 | ||
798 | phy_param = &dflt_phy_param; | |
799 | ||
800 | } | |
801 | ||
802 | if (phy_param->maj != 0) { | |
803 | asd_printk("unsupported manuf. phy param major version:0x%x\n", | |
804 | phy_param->maj); | |
805 | return -ENOENT; | |
806 | } | |
807 | ||
808 | ASD_DPRINTK("ms: num_phy_desc: %d\n", phy_param->num_phy_desc); | |
809 | asd_ha->hw_prof.enabled_phys = 0; | |
810 | for (i = 0; i < phy_param->num_phy_desc; i++) { | |
811 | struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i]; | |
812 | switch (pd->state & 0xF) { | |
813 | case MS_PHY_STATE_HIDDEN: | |
814 | ASD_DPRINTK("ms: phy%d: HIDDEN\n", i); | |
815 | continue; | |
816 | case MS_PHY_STATE_REPORTED: | |
817 | ASD_DPRINTK("ms: phy%d: REPORTED\n", i); | |
818 | asd_ha->hw_prof.enabled_phys &= ~(1 << i); | |
819 | rep_phys++; | |
820 | continue; | |
86b9c4c1 AB |
821 | case MS_PHY_STATE_ENABLED: |
822 | ASD_DPRINTK("ms: phy%d: ENABLED\n", i); | |
2908d778 JB |
823 | asd_ha->hw_prof.enabled_phys |= (1 << i); |
824 | en_phys++; | |
825 | break; | |
826 | } | |
827 | asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0; | |
828 | asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1; | |
829 | asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2; | |
830 | asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3; | |
831 | } | |
832 | asd_ha->hw_prof.max_phys = rep_phys + en_phys; | |
833 | asd_ha->hw_prof.num_phys = en_phys; | |
834 | ASD_DPRINTK("ms: max_phys:0x%x, num_phys:0x%x\n", | |
835 | asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys); | |
836 | ASD_DPRINTK("ms: enabled_phys:0x%x\n", asd_ha->hw_prof.enabled_phys); | |
837 | return 0; | |
838 | } | |
839 | ||
840 | static int asd_ms_get_connector_map(struct asd_ha_struct *asd_ha, | |
841 | struct asd_manuf_sec *manuf_sec) | |
842 | { | |
843 | struct asd_ms_conn_map *cm; | |
844 | ||
845 | cm = asd_find_ll_by_id(manuf_sec, 'M', 'C'); | |
846 | if (!cm) { | |
847 | ASD_DPRINTK("ms: no connector map found\n"); | |
848 | return 0; | |
849 | } | |
850 | ||
851 | if (cm->maj != 0) { | |
852 | ASD_DPRINTK("ms: unsupported: connector map major version 0x%x" | |
853 | "\n", cm->maj); | |
854 | return -ENOENT; | |
855 | } | |
856 | ||
857 | /* XXX */ | |
858 | ||
859 | return 0; | |
860 | } | |
861 | ||
862 | ||
863 | /** | |
864 | * asd_process_ms - find and extract information from the manufacturing sector | |
865 | * @asd_ha: pointer to the host adapter structure | |
866 | * @flash_dir: pointer to the flash directory | |
867 | */ | |
868 | static int asd_process_ms(struct asd_ha_struct *asd_ha, | |
869 | struct asd_flash_dir *flash_dir) | |
870 | { | |
871 | int err; | |
872 | struct asd_manuf_sec *manuf_sec; | |
873 | u32 offs, size; | |
874 | ||
875 | err = asd_find_flash_de(flash_dir, FLASH_DE_MS, &offs, &size); | |
876 | if (err) { | |
877 | ASD_DPRINTK("Couldn't find the manuf. sector\n"); | |
878 | goto out; | |
879 | } | |
880 | ||
881 | if (size == 0) | |
882 | goto out; | |
883 | ||
884 | err = -ENOMEM; | |
885 | manuf_sec = kmalloc(size, GFP_KERNEL); | |
886 | if (!manuf_sec) { | |
887 | ASD_DPRINTK("no mem for manuf sector\n"); | |
888 | goto out; | |
889 | } | |
890 | ||
891 | err = asd_read_flash_seg(asd_ha, (void *)manuf_sec, offs, size); | |
892 | if (err) { | |
893 | ASD_DPRINTK("couldn't read manuf sector at 0x%x, size 0x%x\n", | |
894 | offs, size); | |
895 | goto out2; | |
896 | } | |
897 | ||
898 | err = asd_validate_ms(manuf_sec); | |
899 | if (err) { | |
900 | ASD_DPRINTK("couldn't validate manuf sector\n"); | |
901 | goto out2; | |
902 | } | |
903 | ||
904 | err = asd_ms_get_sas_addr(asd_ha, manuf_sec); | |
905 | if (err) { | |
906 | ASD_DPRINTK("couldn't read the SAS_ADDR\n"); | |
907 | goto out2; | |
908 | } | |
909 | ASD_DPRINTK("manuf sect SAS_ADDR %llx\n", | |
910 | SAS_ADDR(asd_ha->hw_prof.sas_addr)); | |
911 | ||
912 | err = asd_ms_get_pcba_sn(asd_ha, manuf_sec); | |
913 | if (err) { | |
914 | ASD_DPRINTK("couldn't read the PCBA SN\n"); | |
915 | goto out2; | |
916 | } | |
917 | ASD_DPRINTK("manuf sect PCBA SN %s\n", asd_ha->hw_prof.pcba_sn); | |
918 | ||
919 | err = asd_ms_get_phy_params(asd_ha, manuf_sec); | |
920 | if (err) { | |
921 | ASD_DPRINTK("ms: couldn't get phy parameters\n"); | |
922 | goto out2; | |
923 | } | |
924 | ||
925 | err = asd_ms_get_connector_map(asd_ha, manuf_sec); | |
926 | if (err) { | |
927 | ASD_DPRINTK("ms: couldn't get connector map\n"); | |
928 | goto out2; | |
929 | } | |
930 | ||
931 | out2: | |
932 | kfree(manuf_sec); | |
933 | out: | |
934 | return err; | |
935 | } | |
936 | ||
937 | static int asd_process_ctrla_phy_settings(struct asd_ha_struct *asd_ha, | |
938 | struct asd_ctrla_phy_settings *ps) | |
939 | { | |
940 | int i; | |
941 | for (i = 0; i < ps->num_phys; i++) { | |
942 | struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i]; | |
943 | ||
944 | if (!PHY_ENABLED(asd_ha, i)) | |
945 | continue; | |
946 | if (*(u64 *)pe->sas_addr == 0) { | |
947 | asd_ha->hw_prof.enabled_phys &= ~(1 << i); | |
948 | continue; | |
949 | } | |
950 | /* This is the SAS address which should be sent in IDENTIFY. */ | |
951 | memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr, | |
952 | SAS_ADDR_SIZE); | |
953 | asd_ha->hw_prof.phy_desc[i].max_sas_lrate = | |
954 | (pe->sas_link_rates & 0xF0) >> 4; | |
955 | asd_ha->hw_prof.phy_desc[i].min_sas_lrate = | |
956 | (pe->sas_link_rates & 0x0F); | |
957 | asd_ha->hw_prof.phy_desc[i].max_sata_lrate = | |
958 | (pe->sata_link_rates & 0xF0) >> 4; | |
959 | asd_ha->hw_prof.phy_desc[i].min_sata_lrate = | |
960 | (pe->sata_link_rates & 0x0F); | |
961 | asd_ha->hw_prof.phy_desc[i].flags = pe->flags; | |
962 | ASD_DPRINTK("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x," | |
963 | " sata rate:0x%x-0x%x, flags:0x%x\n", | |
964 | i, | |
965 | SAS_ADDR(asd_ha->hw_prof.phy_desc[i].sas_addr), | |
966 | asd_ha->hw_prof.phy_desc[i].max_sas_lrate, | |
967 | asd_ha->hw_prof.phy_desc[i].min_sas_lrate, | |
968 | asd_ha->hw_prof.phy_desc[i].max_sata_lrate, | |
969 | asd_ha->hw_prof.phy_desc[i].min_sata_lrate, | |
970 | asd_ha->hw_prof.phy_desc[i].flags); | |
971 | } | |
972 | ||
973 | return 0; | |
974 | } | |
975 | ||
976 | /** | |
977 | * asd_process_ctrl_a_user - process CTRL-A user settings | |
978 | * @asd_ha: pointer to the host adapter structure | |
979 | * @flash_dir: pointer to the flash directory | |
980 | */ | |
981 | static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, | |
982 | struct asd_flash_dir *flash_dir) | |
983 | { | |
984 | int err, i; | |
985 | u32 offs, size; | |
986 | struct asd_ll_el *el; | |
987 | struct asd_ctrla_phy_settings *ps; | |
988 | struct asd_ctrla_phy_settings dflt_ps; | |
989 | ||
990 | err = asd_find_flash_de(flash_dir, FLASH_DE_CTRL_A_USER, &offs, &size); | |
991 | if (err) { | |
992 | ASD_DPRINTK("couldn't find CTRL-A user settings section\n"); | |
993 | ASD_DPRINTK("Creating default CTRL-A user settings section\n"); | |
994 | ||
995 | dflt_ps.id0 = 'h'; | |
996 | dflt_ps.num_phys = 8; | |
997 | for (i =0; i < ASD_MAX_PHYS; i++) { | |
998 | memcpy(dflt_ps.phy_ent[i].sas_addr, | |
999 | asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE); | |
1000 | dflt_ps.phy_ent[i].sas_link_rates = 0x98; | |
1001 | dflt_ps.phy_ent[i].flags = 0x0; | |
1002 | dflt_ps.phy_ent[i].sata_link_rates = 0x0; | |
1003 | } | |
1004 | ||
1005 | size = sizeof(struct asd_ctrla_phy_settings); | |
1006 | ps = &dflt_ps; | |
1007 | } | |
1008 | ||
1009 | if (size == 0) | |
1010 | goto out; | |
1011 | ||
1012 | err = -ENOMEM; | |
1013 | el = kmalloc(size, GFP_KERNEL); | |
1014 | if (!el) { | |
1015 | ASD_DPRINTK("no mem for ctrla user settings section\n"); | |
1016 | goto out; | |
1017 | } | |
1018 | ||
1019 | err = asd_read_flash_seg(asd_ha, (void *)el, offs, size); | |
1020 | if (err) { | |
1021 | ASD_DPRINTK("couldn't read ctrla phy settings section\n"); | |
1022 | goto out2; | |
1023 | } | |
1024 | ||
1025 | err = -ENOENT; | |
1026 | ps = asd_find_ll_by_id(el, 'h', 0xFF); | |
1027 | if (!ps) { | |
1028 | ASD_DPRINTK("couldn't find ctrla phy settings struct\n"); | |
1029 | goto out2; | |
1030 | } | |
1031 | ||
1032 | err = asd_process_ctrla_phy_settings(asd_ha, ps); | |
1033 | if (err) { | |
1034 | ASD_DPRINTK("couldn't process ctrla phy settings\n"); | |
1035 | goto out2; | |
1036 | } | |
1037 | out2: | |
1038 | kfree(el); | |
1039 | out: | |
1040 | return err; | |
1041 | } | |
1042 | ||
1043 | /** | |
1044 | * asd_read_flash - read flash memory | |
1045 | * @asd_ha: pointer to the host adapter structure | |
1046 | */ | |
1047 | int asd_read_flash(struct asd_ha_struct *asd_ha) | |
1048 | { | |
1049 | int err; | |
1050 | struct asd_flash_dir *flash_dir; | |
1051 | ||
1052 | err = asd_flash_getid(asd_ha); | |
1053 | if (err) | |
1054 | return err; | |
1055 | ||
1056 | flash_dir = kmalloc(sizeof(*flash_dir), GFP_KERNEL); | |
1057 | if (!flash_dir) | |
1058 | return -ENOMEM; | |
1059 | ||
1060 | err = -ENOENT; | |
1061 | if (!asd_find_flash_dir(asd_ha, flash_dir)) { | |
1062 | ASD_DPRINTK("couldn't find flash directory\n"); | |
1063 | goto out; | |
1064 | } | |
1065 | ||
1066 | if (le32_to_cpu(flash_dir->rev) != 2) { | |
1067 | asd_printk("unsupported flash dir version:0x%x\n", | |
1068 | le32_to_cpu(flash_dir->rev)); | |
1069 | goto out; | |
1070 | } | |
1071 | ||
1072 | err = asd_process_ms(asd_ha, flash_dir); | |
1073 | if (err) { | |
1074 | ASD_DPRINTK("couldn't process manuf sector settings\n"); | |
1075 | goto out; | |
1076 | } | |
1077 | ||
1078 | err = asd_process_ctrl_a_user(asd_ha, flash_dir); | |
1079 | if (err) { | |
1080 | ASD_DPRINTK("couldn't process CTRL-A user settings\n"); | |
1081 | goto out; | |
1082 | } | |
1083 | ||
1084 | out: | |
1085 | kfree(flash_dir); | |
1086 | return err; | |
1087 | } | |
1237c98d GW |
1088 | |
1089 | /** | |
1090 | * asd_verify_flash_seg - verify data with flash memory | |
1091 | * @asd_ha: pointer to the host adapter structure | |
1092 | * @src: pointer to the source data to be verified | |
1093 | * @dest_offset: offset from flash memory | |
1094 | * @bytes_to_verify: total bytes to verify | |
1095 | */ | |
1096 | int asd_verify_flash_seg(struct asd_ha_struct *asd_ha, | |
0bc202e0 | 1097 | const void *src, u32 dest_offset, u32 bytes_to_verify) |
1237c98d | 1098 | { |
0bc202e0 | 1099 | const u8 *src_buf; |
1237c98d GW |
1100 | u8 flash_char; |
1101 | int err; | |
1102 | u32 nv_offset, reg, i; | |
1103 | ||
1104 | reg = asd_ha->hw_prof.flash.bar; | |
1105 | src_buf = NULL; | |
1106 | ||
1107 | err = FLASH_OK; | |
1108 | nv_offset = dest_offset; | |
0bc202e0 | 1109 | src_buf = (const u8 *)src; |
1237c98d GW |
1110 | for (i = 0; i < bytes_to_verify; i++) { |
1111 | flash_char = asd_read_reg_byte(asd_ha, reg + nv_offset + i); | |
1112 | if (flash_char != src_buf[i]) { | |
1113 | err = FAIL_VERIFY; | |
1114 | break; | |
1115 | } | |
1116 | } | |
1117 | return err; | |
1118 | } | |
1119 | ||
1120 | /** | |
1121 | * asd_write_flash_seg - write data into flash memory | |
1122 | * @asd_ha: pointer to the host adapter structure | |
1123 | * @src: pointer to the source data to be written | |
1124 | * @dest_offset: offset from flash memory | |
1125 | * @bytes_to_write: total bytes to write | |
1126 | */ | |
1127 | int asd_write_flash_seg(struct asd_ha_struct *asd_ha, | |
0bc202e0 | 1128 | const void *src, u32 dest_offset, u32 bytes_to_write) |
1237c98d | 1129 | { |
0bc202e0 | 1130 | const u8 *src_buf; |
1237c98d GW |
1131 | u32 nv_offset, reg, i; |
1132 | int err; | |
1133 | ||
1134 | reg = asd_ha->hw_prof.flash.bar; | |
1135 | src_buf = NULL; | |
1136 | ||
1137 | err = asd_check_flash_type(asd_ha); | |
1138 | if (err) { | |
1139 | ASD_DPRINTK("couldn't find the type of flash. err=%d\n", err); | |
1140 | return err; | |
1141 | } | |
1142 | ||
1143 | nv_offset = dest_offset; | |
1144 | err = asd_erase_nv_sector(asd_ha, nv_offset, bytes_to_write); | |
1145 | if (err) { | |
1146 | ASD_DPRINTK("Erase failed at offset:0x%x\n", | |
1147 | nv_offset); | |
1148 | return err; | |
1149 | } | |
1150 | ||
1151 | err = asd_reset_flash(asd_ha); | |
1152 | if (err) { | |
1153 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | |
1154 | return err; | |
1155 | } | |
1156 | ||
0bc202e0 | 1157 | src_buf = (const u8 *)src; |
1237c98d GW |
1158 | for (i = 0; i < bytes_to_write; i++) { |
1159 | /* Setup program command sequence */ | |
1160 | switch (asd_ha->hw_prof.flash.method) { | |
1161 | case FLASH_METHOD_A: | |
1162 | { | |
1163 | asd_write_reg_byte(asd_ha, | |
1164 | (reg + 0xAAA), 0xAA); | |
1165 | asd_write_reg_byte(asd_ha, | |
1166 | (reg + 0x555), 0x55); | |
1167 | asd_write_reg_byte(asd_ha, | |
1168 | (reg + 0xAAA), 0xA0); | |
1169 | asd_write_reg_byte(asd_ha, | |
1170 | (reg + nv_offset + i), | |
1171 | (*(src_buf + i))); | |
1172 | break; | |
1173 | } | |
1174 | case FLASH_METHOD_B: | |
1175 | { | |
1176 | asd_write_reg_byte(asd_ha, | |
1177 | (reg + 0x555), 0xAA); | |
1178 | asd_write_reg_byte(asd_ha, | |
1179 | (reg + 0x2AA), 0x55); | |
1180 | asd_write_reg_byte(asd_ha, | |
1181 | (reg + 0x555), 0xA0); | |
1182 | asd_write_reg_byte(asd_ha, | |
1183 | (reg + nv_offset + i), | |
1184 | (*(src_buf + i))); | |
1185 | break; | |
1186 | } | |
1187 | default: | |
1188 | break; | |
1189 | } | |
1190 | if (asd_chk_write_status(asd_ha, | |
1191 | (nv_offset + i), 0) != 0) { | |
1192 | ASD_DPRINTK("aicx: Write failed at offset:0x%x\n", | |
1193 | reg + nv_offset + i); | |
1194 | return FAIL_WRITE_FLASH; | |
1195 | } | |
1196 | } | |
1197 | ||
1198 | err = asd_reset_flash(asd_ha); | |
1199 | if (err) { | |
1200 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | |
1201 | return err; | |
1202 | } | |
1203 | return 0; | |
1204 | } | |
1205 | ||
1206 | int asd_chk_write_status(struct asd_ha_struct *asd_ha, | |
1207 | u32 sector_addr, u8 erase_flag) | |
1208 | { | |
1209 | u32 reg; | |
1210 | u32 loop_cnt; | |
1211 | u8 nv_data1, nv_data2; | |
1212 | u8 toggle_bit1; | |
1213 | ||
1214 | /* | |
1215 | * Read from DQ2 requires sector address | |
1216 | * while it's dont care for DQ6 | |
1217 | */ | |
1218 | reg = asd_ha->hw_prof.flash.bar; | |
1219 | ||
1220 | for (loop_cnt = 0; loop_cnt < 50000; loop_cnt++) { | |
1221 | nv_data1 = asd_read_reg_byte(asd_ha, reg); | |
1222 | nv_data2 = asd_read_reg_byte(asd_ha, reg); | |
1223 | ||
1224 | toggle_bit1 = ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6) | |
1225 | ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6)); | |
1226 | ||
1227 | if (toggle_bit1 == 0) { | |
1228 | return 0; | |
1229 | } else { | |
1230 | if (nv_data2 & FLASH_STATUS_BIT_MASK_DQ5) { | |
1231 | nv_data1 = asd_read_reg_byte(asd_ha, | |
1232 | reg); | |
1233 | nv_data2 = asd_read_reg_byte(asd_ha, | |
1234 | reg); | |
1235 | toggle_bit1 = | |
1236 | ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6) | |
1237 | ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6)); | |
1238 | ||
1239 | if (toggle_bit1 == 0) | |
1240 | return 0; | |
1241 | } | |
1242 | } | |
1243 | ||
1244 | /* | |
1245 | * ERASE is a sector-by-sector operation and requires | |
1246 | * more time to finish while WRITE is byte-byte-byte | |
1247 | * operation and takes lesser time to finish. | |
1248 | * | |
1249 | * For some strange reason a reduced ERASE delay gives different | |
1250 | * behaviour across different spirit boards. Hence we set | |
1251 | * a optimum balance of 50mus for ERASE which works well | |
1252 | * across all boards. | |
1253 | */ | |
1254 | if (erase_flag) { | |
1255 | udelay(FLASH_STATUS_ERASE_DELAY_COUNT); | |
1256 | } else { | |
1257 | udelay(FLASH_STATUS_WRITE_DELAY_COUNT); | |
1258 | } | |
1259 | } | |
1260 | return -1; | |
1261 | } | |
1262 | ||
1263 | /** | |
1264 | * asd_hwi_erase_nv_sector - Erase the flash memory sectors. | |
1265 | * @asd_ha: pointer to the host adapter structure | |
1266 | * @flash_addr: pointer to offset from flash memory | |
1267 | * @size: total bytes to erase. | |
1268 | */ | |
1269 | int asd_erase_nv_sector(struct asd_ha_struct *asd_ha, u32 flash_addr, u32 size) | |
1270 | { | |
1271 | u32 reg; | |
1272 | u32 sector_addr; | |
1273 | ||
1274 | reg = asd_ha->hw_prof.flash.bar; | |
1275 | ||
1276 | /* sector staring address */ | |
1277 | sector_addr = flash_addr & FLASH_SECTOR_SIZE_MASK; | |
1278 | ||
1279 | /* | |
1280 | * Erasing an flash sector needs to be done in six consecutive | |
1281 | * write cyles. | |
1282 | */ | |
1283 | while (sector_addr < flash_addr+size) { | |
1284 | switch (asd_ha->hw_prof.flash.method) { | |
1285 | case FLASH_METHOD_A: | |
1286 | asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA); | |
1287 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55); | |
1288 | asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0x80); | |
1289 | asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA); | |
1290 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55); | |
1291 | asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30); | |
1292 | break; | |
1293 | case FLASH_METHOD_B: | |
1294 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA); | |
1295 | asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55); | |
1296 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0x80); | |
1297 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA); | |
1298 | asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55); | |
1299 | asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30); | |
1300 | break; | |
1301 | default: | |
1302 | break; | |
1303 | } | |
1304 | ||
1305 | if (asd_chk_write_status(asd_ha, sector_addr, 1) != 0) | |
1306 | return FAIL_ERASE_FLASH; | |
1307 | ||
1308 | sector_addr += FLASH_SECTOR_SIZE; | |
1309 | } | |
1310 | ||
1311 | return 0; | |
1312 | } | |
1313 | ||
1314 | int asd_check_flash_type(struct asd_ha_struct *asd_ha) | |
1315 | { | |
1316 | u8 manuf_id; | |
1317 | u8 dev_id; | |
1318 | u8 sec_prot; | |
1319 | u32 inc; | |
1320 | u32 reg; | |
1321 | int err; | |
1322 | ||
1323 | /* get Flash memory base address */ | |
1324 | reg = asd_ha->hw_prof.flash.bar; | |
1325 | ||
1326 | /* Determine flash info */ | |
1327 | err = asd_reset_flash(asd_ha); | |
1328 | if (err) { | |
1329 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | |
1330 | return err; | |
1331 | } | |
1332 | ||
1333 | asd_ha->hw_prof.flash.method = FLASH_METHOD_UNKNOWN; | |
1334 | asd_ha->hw_prof.flash.manuf = FLASH_MANUF_ID_UNKNOWN; | |
1335 | asd_ha->hw_prof.flash.dev_id = FLASH_DEV_ID_UNKNOWN; | |
1336 | ||
1337 | /* Get flash info. This would most likely be AMD Am29LV family flash. | |
1338 | * First try the sequence for word mode. It is the same as for | |
1339 | * 008B (byte mode only), 160B (word mode) and 800D (word mode). | |
1340 | */ | |
1341 | inc = asd_ha->hw_prof.flash.wide ? 2 : 1; | |
1342 | asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA); | |
1343 | asd_write_reg_byte(asd_ha, reg + 0x555, 0x55); | |
1344 | asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90); | |
1345 | manuf_id = asd_read_reg_byte(asd_ha, reg); | |
1346 | dev_id = asd_read_reg_byte(asd_ha, reg + inc); | |
1347 | sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc); | |
1348 | /* Get out of autoselect mode. */ | |
1349 | err = asd_reset_flash(asd_ha); | |
1350 | if (err) { | |
1351 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | |
1352 | return err; | |
1353 | } | |
1354 | ASD_DPRINTK("Flash MethodA manuf_id(0x%x) dev_id(0x%x) " | |
1355 | "sec_prot(0x%x)\n", manuf_id, dev_id, sec_prot); | |
1356 | err = asd_reset_flash(asd_ha); | |
1357 | if (err != 0) | |
1358 | return err; | |
1359 | ||
1360 | switch (manuf_id) { | |
1361 | case FLASH_MANUF_ID_AMD: | |
1362 | switch (sec_prot) { | |
1363 | case FLASH_DEV_ID_AM29LV800DT: | |
1364 | case FLASH_DEV_ID_AM29LV640MT: | |
1365 | case FLASH_DEV_ID_AM29F800B: | |
1366 | asd_ha->hw_prof.flash.method = FLASH_METHOD_A; | |
1367 | break; | |
1368 | default: | |
1369 | break; | |
1370 | } | |
1371 | break; | |
1372 | case FLASH_MANUF_ID_ST: | |
1373 | switch (sec_prot) { | |
1374 | case FLASH_DEV_ID_STM29W800DT: | |
1375 | case FLASH_DEV_ID_STM29LV640: | |
1376 | asd_ha->hw_prof.flash.method = FLASH_METHOD_A; | |
1377 | break; | |
1378 | default: | |
1379 | break; | |
1380 | } | |
1381 | break; | |
1382 | case FLASH_MANUF_ID_FUJITSU: | |
1383 | switch (sec_prot) { | |
1384 | case FLASH_DEV_ID_MBM29LV800TE: | |
1385 | case FLASH_DEV_ID_MBM29DL800TA: | |
1386 | asd_ha->hw_prof.flash.method = FLASH_METHOD_A; | |
1387 | break; | |
1388 | } | |
1389 | break; | |
1390 | case FLASH_MANUF_ID_MACRONIX: | |
1391 | switch (sec_prot) { | |
1392 | case FLASH_DEV_ID_MX29LV800BT: | |
1393 | asd_ha->hw_prof.flash.method = FLASH_METHOD_A; | |
1394 | break; | |
1395 | } | |
1396 | break; | |
1397 | } | |
1398 | ||
1399 | if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) { | |
1400 | err = asd_reset_flash(asd_ha); | |
1401 | if (err) { | |
1402 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | |
1403 | return err; | |
1404 | } | |
1405 | ||
1406 | /* Issue Unlock sequence for AM29LV008BT */ | |
1407 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA); | |
1408 | asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55); | |
1409 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0x90); | |
1410 | manuf_id = asd_read_reg_byte(asd_ha, reg); | |
1411 | dev_id = asd_read_reg_byte(asd_ha, reg + inc); | |
1412 | sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc); | |
1413 | ||
1414 | ASD_DPRINTK("Flash MethodB manuf_id(0x%x) dev_id(0x%x) sec_prot" | |
1415 | "(0x%x)\n", manuf_id, dev_id, sec_prot); | |
1416 | ||
1417 | err = asd_reset_flash(asd_ha); | |
1418 | if (err != 0) { | |
1419 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | |
1420 | return err; | |
1421 | } | |
1422 | ||
1423 | switch (manuf_id) { | |
1424 | case FLASH_MANUF_ID_AMD: | |
1425 | switch (dev_id) { | |
1426 | case FLASH_DEV_ID_AM29LV008BT: | |
1427 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | |
1428 | break; | |
1429 | default: | |
1430 | break; | |
1431 | } | |
1432 | break; | |
1433 | case FLASH_MANUF_ID_ST: | |
1434 | switch (dev_id) { | |
1435 | case FLASH_DEV_ID_STM29008: | |
1436 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | |
1437 | break; | |
1438 | default: | |
1439 | break; | |
1440 | } | |
1441 | break; | |
1442 | case FLASH_MANUF_ID_FUJITSU: | |
1443 | switch (dev_id) { | |
1444 | case FLASH_DEV_ID_MBM29LV008TA: | |
1445 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | |
1446 | break; | |
1447 | } | |
1448 | break; | |
1449 | case FLASH_MANUF_ID_INTEL: | |
1450 | switch (dev_id) { | |
1451 | case FLASH_DEV_ID_I28LV00TAT: | |
1452 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | |
1453 | break; | |
1454 | } | |
1455 | break; | |
1456 | case FLASH_MANUF_ID_MACRONIX: | |
1457 | switch (dev_id) { | |
1458 | case FLASH_DEV_ID_I28LV00TAT: | |
1459 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | |
1460 | break; | |
1461 | } | |
1462 | break; | |
1463 | default: | |
1464 | return FAIL_FIND_FLASH_ID; | |
1465 | } | |
1466 | } | |
1467 | ||
1468 | if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) | |
1469 | return FAIL_FIND_FLASH_ID; | |
1470 | ||
1471 | asd_ha->hw_prof.flash.manuf = manuf_id; | |
1472 | asd_ha->hw_prof.flash.dev_id = dev_id; | |
1473 | asd_ha->hw_prof.flash.sec_prot = sec_prot; | |
1474 | return 0; | |
1475 | } |