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