diff options
Diffstat (limited to 'drivers/scsi/aic94xx/aic94xx_sds.c')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_sds.c | 1136 |
1 files changed, 1136 insertions, 0 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c new file mode 100644 index 000000000000..eec1e0db0e0f --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_sds.c | |||
@@ -0,0 +1,1136 @@ | |||
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" | ||
33 | |||
34 | /* ---------- OCM stuff ---------- */ | ||
35 | |||
36 | struct asd_ocm_dir_ent { | ||
37 | u8 type; | ||
38 | u8 offs[3]; | ||
39 | u8 _r1; | ||
40 | u8 size[3]; | ||
41 | } __attribute__ ((packed)); | ||
42 | |||
43 | struct asd_ocm_dir { | ||
44 | char sig[2]; | ||
45 | u8 _r1[2]; | ||
46 | u8 major; /* 0 */ | ||
47 | u8 minor; /* 0 */ | ||
48 | u8 _r2; | ||
49 | u8 num_de; | ||
50 | struct asd_ocm_dir_ent entry[15]; | ||
51 | } __attribute__ ((packed)); | ||
52 | |||
53 | #define OCM_DE_OCM_DIR 0x00 | ||
54 | #define OCM_DE_WIN_DRVR 0x01 | ||
55 | #define OCM_DE_BIOS_CHIM 0x02 | ||
56 | #define OCM_DE_RAID_ENGN 0x03 | ||
57 | #define OCM_DE_BIOS_INTL 0x04 | ||
58 | #define OCM_DE_BIOS_CHIM_OSM 0x05 | ||
59 | #define OCM_DE_BIOS_CHIM_DYNAMIC 0x06 | ||
60 | #define OCM_DE_ADDC2C_RES0 0x07 | ||
61 | #define OCM_DE_ADDC2C_RES1 0x08 | ||
62 | #define OCM_DE_ADDC2C_RES2 0x09 | ||
63 | #define OCM_DE_ADDC2C_RES3 0x0A | ||
64 | |||
65 | #define OCM_INIT_DIR_ENTRIES 5 | ||
66 | /*************************************************************************** | ||
67 | * OCM dircetory default | ||
68 | ***************************************************************************/ | ||
69 | static struct asd_ocm_dir OCMDirInit = | ||
70 | { | ||
71 | .sig = {0x4D, 0x4F}, /* signature */ | ||
72 | .num_de = OCM_INIT_DIR_ENTRIES, /* no. of directory entries */ | ||
73 | }; | ||
74 | |||
75 | /*************************************************************************** | ||
76 | * OCM dircetory Entries default | ||
77 | ***************************************************************************/ | ||
78 | static struct asd_ocm_dir_ent OCMDirEntriesInit[OCM_INIT_DIR_ENTRIES] = | ||
79 | { | ||
80 | { | ||
81 | .type = (OCM_DE_ADDC2C_RES0), /* Entry type */ | ||
82 | .offs = {128}, /* Offset */ | ||
83 | .size = {0, 4}, /* size */ | ||
84 | }, | ||
85 | { | ||
86 | .type = (OCM_DE_ADDC2C_RES1), /* Entry type */ | ||
87 | .offs = {128, 4}, /* Offset */ | ||
88 | .size = {0, 4}, /* size */ | ||
89 | }, | ||
90 | { | ||
91 | .type = (OCM_DE_ADDC2C_RES2), /* Entry type */ | ||
92 | .offs = {128, 8}, /* Offset */ | ||
93 | .size = {0, 4}, /* size */ | ||
94 | }, | ||
95 | { | ||
96 | .type = (OCM_DE_ADDC2C_RES3), /* Entry type */ | ||
97 | .offs = {128, 12}, /* Offset */ | ||
98 | .size = {0, 4}, /* size */ | ||
99 | }, | ||
100 | { | ||
101 | .type = (OCM_DE_WIN_DRVR), /* Entry type */ | ||
102 | .offs = {128, 16}, /* Offset */ | ||
103 | .size = {128, 235, 1}, /* size */ | ||
104 | }, | ||
105 | }; | ||
106 | |||
107 | struct asd_bios_chim_struct { | ||
108 | char sig[4]; | ||
109 | u8 major; /* 1 */ | ||
110 | u8 minor; /* 0 */ | ||
111 | u8 bios_major; | ||
112 | u8 bios_minor; | ||
113 | __le32 bios_build; | ||
114 | u8 flags; | ||
115 | u8 pci_slot; | ||
116 | __le16 ue_num; | ||
117 | __le16 ue_size; | ||
118 | u8 _r[14]; | ||
119 | /* The unit element array is right here. | ||
120 | */ | ||
121 | } __attribute__ ((packed)); | ||
122 | |||
123 | /** | ||
124 | * asd_read_ocm_seg - read an on chip memory (OCM) segment | ||
125 | * @asd_ha: pointer to the host adapter structure | ||
126 | * @buffer: where to write the read data | ||
127 | * @offs: offset into OCM where to read from | ||
128 | * @size: how many bytes to read | ||
129 | * | ||
130 | * Return the number of bytes not read. Return 0 on success. | ||
131 | */ | ||
132 | static int asd_read_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer, | ||
133 | u32 offs, int size) | ||
134 | { | ||
135 | u8 *p = buffer; | ||
136 | if (unlikely(asd_ha->iospace)) | ||
137 | asd_read_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size); | ||
138 | else { | ||
139 | for ( ; size > 0; size--, offs++, p++) | ||
140 | *p = asd_read_ocm_byte(asd_ha, offs); | ||
141 | } | ||
142 | return size; | ||
143 | } | ||
144 | |||
145 | static int asd_read_ocm_dir(struct asd_ha_struct *asd_ha, | ||
146 | struct asd_ocm_dir *dir, u32 offs) | ||
147 | { | ||
148 | int err = asd_read_ocm_seg(asd_ha, dir, offs, sizeof(*dir)); | ||
149 | if (err) { | ||
150 | ASD_DPRINTK("couldn't read ocm segment\n"); | ||
151 | return err; | ||
152 | } | ||
153 | |||
154 | if (dir->sig[0] != 'M' || dir->sig[1] != 'O') { | ||
155 | ASD_DPRINTK("no valid dir signature(%c%c) at start of OCM\n", | ||
156 | dir->sig[0], dir->sig[1]); | ||
157 | return -ENOENT; | ||
158 | } | ||
159 | if (dir->major != 0) { | ||
160 | asd_printk("unsupported major version of ocm dir:0x%x\n", | ||
161 | dir->major); | ||
162 | return -ENOENT; | ||
163 | } | ||
164 | dir->num_de &= 0xf; | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * asd_write_ocm_seg - write an on chip memory (OCM) segment | ||
170 | * @asd_ha: pointer to the host adapter structure | ||
171 | * @buffer: where to read the write data | ||
172 | * @offs: offset into OCM to write to | ||
173 | * @size: how many bytes to write | ||
174 | * | ||
175 | * Return the number of bytes not written. Return 0 on success. | ||
176 | */ | ||
177 | static void asd_write_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer, | ||
178 | u32 offs, int size) | ||
179 | { | ||
180 | u8 *p = buffer; | ||
181 | if (unlikely(asd_ha->iospace)) | ||
182 | asd_write_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size); | ||
183 | else { | ||
184 | for ( ; size > 0; size--, offs++, p++) | ||
185 | asd_write_ocm_byte(asd_ha, offs, *p); | ||
186 | } | ||
187 | return; | ||
188 | } | ||
189 | |||
190 | #define THREE_TO_NUM(X) ((X)[0] | ((X)[1] << 8) | ((X)[2] << 16)) | ||
191 | |||
192 | static int asd_find_dir_entry(struct asd_ocm_dir *dir, u8 type, | ||
193 | u32 *offs, u32 *size) | ||
194 | { | ||
195 | int i; | ||
196 | struct asd_ocm_dir_ent *ent; | ||
197 | |||
198 | for (i = 0; i < dir->num_de; i++) { | ||
199 | if (dir->entry[i].type == type) | ||
200 | break; | ||
201 | } | ||
202 | if (i >= dir->num_de) | ||
203 | return -ENOENT; | ||
204 | ent = &dir->entry[i]; | ||
205 | *offs = (u32) THREE_TO_NUM(ent->offs); | ||
206 | *size = (u32) THREE_TO_NUM(ent->size); | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | #define OCM_BIOS_CHIM_DE 2 | ||
211 | #define BC_BIOS_PRESENT 1 | ||
212 | |||
213 | static int asd_get_bios_chim(struct asd_ha_struct *asd_ha, | ||
214 | struct asd_ocm_dir *dir) | ||
215 | { | ||
216 | int err; | ||
217 | struct asd_bios_chim_struct *bc_struct; | ||
218 | u32 offs, size; | ||
219 | |||
220 | err = asd_find_dir_entry(dir, OCM_BIOS_CHIM_DE, &offs, &size); | ||
221 | if (err) { | ||
222 | ASD_DPRINTK("couldn't find BIOS_CHIM dir ent\n"); | ||
223 | goto out; | ||
224 | } | ||
225 | err = -ENOMEM; | ||
226 | bc_struct = kmalloc(sizeof(*bc_struct), GFP_KERNEL); | ||
227 | if (!bc_struct) { | ||
228 | asd_printk("no memory for bios_chim struct\n"); | ||
229 | goto out; | ||
230 | } | ||
231 | err = asd_read_ocm_seg(asd_ha, (void *)bc_struct, offs, | ||
232 | sizeof(*bc_struct)); | ||
233 | if (err) { | ||
234 | ASD_DPRINTK("couldn't read ocm segment\n"); | ||
235 | goto out2; | ||
236 | } | ||
237 | if (strncmp(bc_struct->sig, "SOIB", 4) | ||
238 | && strncmp(bc_struct->sig, "IPSA", 4)) { | ||
239 | ASD_DPRINTK("BIOS_CHIM entry has no valid sig(%c%c%c%c)\n", | ||
240 | bc_struct->sig[0], bc_struct->sig[1], | ||
241 | bc_struct->sig[2], bc_struct->sig[3]); | ||
242 | err = -ENOENT; | ||
243 | goto out2; | ||
244 | } | ||
245 | if (bc_struct->major != 1) { | ||
246 | asd_printk("BIOS_CHIM unsupported major version:0x%x\n", | ||
247 | bc_struct->major); | ||
248 | err = -ENOENT; | ||
249 | goto out2; | ||
250 | } | ||
251 | if (bc_struct->flags & BC_BIOS_PRESENT) { | ||
252 | asd_ha->hw_prof.bios.present = 1; | ||
253 | asd_ha->hw_prof.bios.maj = bc_struct->bios_major; | ||
254 | asd_ha->hw_prof.bios.min = bc_struct->bios_minor; | ||
255 | asd_ha->hw_prof.bios.bld = le32_to_cpu(bc_struct->bios_build); | ||
256 | ASD_DPRINTK("BIOS present (%d,%d), %d\n", | ||
257 | asd_ha->hw_prof.bios.maj, | ||
258 | asd_ha->hw_prof.bios.min, | ||
259 | asd_ha->hw_prof.bios.bld); | ||
260 | } | ||
261 | asd_ha->hw_prof.ue.num = le16_to_cpu(bc_struct->ue_num); | ||
262 | asd_ha->hw_prof.ue.size= le16_to_cpu(bc_struct->ue_size); | ||
263 | ASD_DPRINTK("ue num:%d, ue size:%d\n", asd_ha->hw_prof.ue.num, | ||
264 | asd_ha->hw_prof.ue.size); | ||
265 | size = asd_ha->hw_prof.ue.num * asd_ha->hw_prof.ue.size; | ||
266 | if (size > 0) { | ||
267 | err = -ENOMEM; | ||
268 | asd_ha->hw_prof.ue.area = kmalloc(size, GFP_KERNEL); | ||
269 | if (!asd_ha->hw_prof.ue.area) | ||
270 | goto out2; | ||
271 | err = asd_read_ocm_seg(asd_ha, (void *)asd_ha->hw_prof.ue.area, | ||
272 | offs + sizeof(*bc_struct), size); | ||
273 | if (err) { | ||
274 | kfree(asd_ha->hw_prof.ue.area); | ||
275 | asd_ha->hw_prof.ue.area = NULL; | ||
276 | asd_ha->hw_prof.ue.num = 0; | ||
277 | asd_ha->hw_prof.ue.size = 0; | ||
278 | ASD_DPRINTK("couldn't read ue entries(%d)\n", err); | ||
279 | } | ||
280 | } | ||
281 | out2: | ||
282 | kfree(bc_struct); | ||
283 | out: | ||
284 | return err; | ||
285 | } | ||
286 | |||
287 | static void | ||
288 | asd_hwi_initialize_ocm_dir (struct asd_ha_struct *asd_ha) | ||
289 | { | ||
290 | int i; | ||
291 | |||
292 | /* Zero OCM */ | ||
293 | for (i = 0; i < OCM_MAX_SIZE; i += 4) | ||
294 | asd_write_ocm_dword(asd_ha, i, 0); | ||
295 | |||
296 | /* Write Dir */ | ||
297 | asd_write_ocm_seg(asd_ha, &OCMDirInit, 0, | ||
298 | sizeof(struct asd_ocm_dir)); | ||
299 | |||
300 | /* Write Dir Entries */ | ||
301 | for (i = 0; i < OCM_INIT_DIR_ENTRIES; i++) | ||
302 | asd_write_ocm_seg(asd_ha, &OCMDirEntriesInit[i], | ||
303 | sizeof(struct asd_ocm_dir) + | ||
304 | (i * sizeof(struct asd_ocm_dir_ent)) | ||
305 | , sizeof(struct asd_ocm_dir_ent)); | ||
306 | |||
307 | } | ||
308 | |||
309 | static int | ||
310 | asd_hwi_check_ocm_access (struct asd_ha_struct *asd_ha) | ||
311 | { | ||
312 | struct pci_dev *pcidev = asd_ha->pcidev; | ||
313 | u32 reg; | ||
314 | int err = 0; | ||
315 | u32 v; | ||
316 | |||
317 | /* check if OCM has been initialized by BIOS */ | ||
318 | reg = asd_read_reg_dword(asd_ha, EXSICNFGR); | ||
319 | |||
320 | if (!(reg & OCMINITIALIZED)) { | ||
321 | err = pci_read_config_dword(pcidev, PCIC_INTRPT_STAT, &v); | ||
322 | if (err) { | ||
323 | asd_printk("couldn't access PCIC_INTRPT_STAT of %s\n", | ||
324 | pci_name(pcidev)); | ||
325 | goto out; | ||
326 | } | ||
327 | |||
328 | printk(KERN_INFO "OCM is not initialized by BIOS," | ||
329 | "reinitialize it and ignore it, current IntrptStatus" | ||
330 | "is 0x%x\n", v); | ||
331 | |||
332 | if (v) | ||
333 | err = pci_write_config_dword(pcidev, | ||
334 | PCIC_INTRPT_STAT, v); | ||
335 | if (err) { | ||
336 | asd_printk("couldn't write PCIC_INTRPT_STAT of %s\n", | ||
337 | pci_name(pcidev)); | ||
338 | goto out; | ||
339 | } | ||
340 | |||
341 | asd_hwi_initialize_ocm_dir(asd_ha); | ||
342 | |||
343 | } | ||
344 | out: | ||
345 | return err; | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * asd_read_ocm - read on chip memory (OCM) | ||
350 | * @asd_ha: pointer to the host adapter structure | ||
351 | */ | ||
352 | int asd_read_ocm(struct asd_ha_struct *asd_ha) | ||
353 | { | ||
354 | int err; | ||
355 | struct asd_ocm_dir *dir; | ||
356 | |||
357 | if (asd_hwi_check_ocm_access(asd_ha)) | ||
358 | return -1; | ||
359 | |||
360 | dir = kmalloc(sizeof(*dir), GFP_KERNEL); | ||
361 | if (!dir) { | ||
362 | asd_printk("no memory for ocm dir\n"); | ||
363 | return -ENOMEM; | ||
364 | } | ||
365 | |||
366 | err = asd_read_ocm_dir(asd_ha, dir, 0); | ||
367 | if (err) | ||
368 | goto out; | ||
369 | |||
370 | err = asd_get_bios_chim(asd_ha, dir); | ||
371 | out: | ||
372 | kfree(dir); | ||
373 | return err; | ||
374 | } | ||
375 | |||
376 | /* ---------- FLASH stuff ---------- */ | ||
377 | |||
378 | #define FLASH_RESET 0xF0 | ||
379 | #define FLASH_MANUF_AMD 1 | ||
380 | |||
381 | #define FLASH_SIZE 0x200000 | ||
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 */ | ||
431 | #define MS_PHY_STATE_ENABLEABLE 0 | ||
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 | |||
593 | static inline int asd_read_flash_seg(struct asd_ha_struct *asd_ha, | ||
594 | void *buffer, u32 offs, int size) | ||
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; | ||
613 | for (v = 0; v < FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) { | ||
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; | ||
630 | u32 reg, inc; | ||
631 | |||
632 | reg = asd_read_reg_dword(asd_ha, EXSICNFGR); | ||
633 | |||
634 | if (!(reg & FLASHEX)) { | ||
635 | ASD_DPRINTK("flash doesn't exist\n"); | ||
636 | return -ENOENT; | ||
637 | } | ||
638 | if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR, | ||
639 | &asd_ha->hw_prof.flash.bar)) { | ||
640 | asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n", | ||
641 | pci_name(asd_ha->pcidev)); | ||
642 | return -ENOENT; | ||
643 | } | ||
644 | asd_ha->hw_prof.flash.present = 1; | ||
645 | asd_ha->hw_prof.flash.wide = reg & FLASHW ? 1 : 0; | ||
646 | err = asd_reset_flash(asd_ha); | ||
647 | if (err) { | ||
648 | ASD_DPRINTK("couldn't reset flash(%d)\n", err); | ||
649 | return err; | ||
650 | } | ||
651 | /* Get flash info. This would most likely be AMD Am29LV family flash. | ||
652 | * First try the sequence for word mode. It is the same as for | ||
653 | * 008B (byte mode only), 160B (word mode) and 800D (word mode). | ||
654 | */ | ||
655 | reg = asd_ha->hw_prof.flash.bar; | ||
656 | inc = asd_ha->hw_prof.flash.wide ? 2 : 1; | ||
657 | asd_write_reg_byte(asd_ha, reg + 0x555, 0xAA); | ||
658 | asd_write_reg_byte(asd_ha, reg + 0x2AA, 0x55); | ||
659 | asd_write_reg_byte(asd_ha, reg + 0x555, 0x90); | ||
660 | asd_ha->hw_prof.flash.manuf = asd_read_reg_byte(asd_ha, reg); | ||
661 | asd_ha->hw_prof.flash.dev_id= asd_read_reg_byte(asd_ha,reg+inc); | ||
662 | asd_ha->hw_prof.flash.sec_prot = asd_read_reg_byte(asd_ha,reg+inc+inc); | ||
663 | /* Get out of autoselect mode. */ | ||
664 | err = asd_reset_flash(asd_ha); | ||
665 | |||
666 | if (asd_ha->hw_prof.flash.manuf == FLASH_MANUF_AMD) { | ||
667 | ASD_DPRINTK("0Found FLASH(%d) manuf:%d, dev_id:0x%x, " | ||
668 | "sec_prot:%d\n", | ||
669 | asd_ha->hw_prof.flash.wide ? 16 : 8, | ||
670 | asd_ha->hw_prof.flash.manuf, | ||
671 | asd_ha->hw_prof.flash.dev_id, | ||
672 | asd_ha->hw_prof.flash.sec_prot); | ||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | /* Ok, try the sequence for byte mode of 160B and 800D. | ||
677 | * We may actually never need this. | ||
678 | */ | ||
679 | asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA); | ||
680 | asd_write_reg_byte(asd_ha, reg + 0x555, 0x55); | ||
681 | asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90); | ||
682 | asd_ha->hw_prof.flash.manuf = asd_read_reg_byte(asd_ha, reg); | ||
683 | asd_ha->hw_prof.flash.dev_id = asd_read_reg_byte(asd_ha, reg + 2); | ||
684 | asd_ha->hw_prof.flash.sec_prot = asd_read_reg_byte(asd_ha, reg + 4); | ||
685 | err = asd_reset_flash(asd_ha); | ||
686 | |||
687 | if (asd_ha->hw_prof.flash.manuf == FLASH_MANUF_AMD) { | ||
688 | ASD_DPRINTK("1Found FLASH(%d) manuf:%d, dev_id:0x%x, " | ||
689 | "sec_prot:%d\n", | ||
690 | asd_ha->hw_prof.flash.wide ? 16 : 8, | ||
691 | asd_ha->hw_prof.flash.manuf, | ||
692 | asd_ha->hw_prof.flash.dev_id, | ||
693 | asd_ha->hw_prof.flash.sec_prot); | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | return -ENOENT; | ||
698 | } | ||
699 | |||
700 | static u16 asd_calc_flash_chksum(u16 *p, int size) | ||
701 | { | ||
702 | u16 chksum = 0; | ||
703 | |||
704 | while (size-- > 0) | ||
705 | chksum += *p++; | ||
706 | |||
707 | return chksum; | ||
708 | } | ||
709 | |||
710 | |||
711 | static int asd_find_flash_de(struct asd_flash_dir *flash_dir, u32 entry_type, | ||
712 | u32 *offs, u32 *size) | ||
713 | { | ||
714 | int i; | ||
715 | struct asd_flash_de *de; | ||
716 | |||
717 | for (i = 0; i < FLASH_MAX_DIR_ENTRIES; i++) { | ||
718 | u32 type = le32_to_cpu(flash_dir->dir_entry[i].type); | ||
719 | |||
720 | type &= FLASH_DE_TYPE_MASK; | ||
721 | if (type == entry_type) | ||
722 | break; | ||
723 | } | ||
724 | if (i >= FLASH_MAX_DIR_ENTRIES) | ||
725 | return -ENOENT; | ||
726 | de = &flash_dir->dir_entry[i]; | ||
727 | *offs = le32_to_cpu(de->offs); | ||
728 | *size = le32_to_cpu(de->pad_size); | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | static int asd_validate_ms(struct asd_manuf_sec *ms) | ||
733 | { | ||
734 | if (ms->sig[0] != 'S' || ms->sig[1] != 'M') { | ||
735 | ASD_DPRINTK("manuf sec: no valid sig(%c%c)\n", | ||
736 | ms->sig[0], ms->sig[1]); | ||
737 | return -ENOENT; | ||
738 | } | ||
739 | if (ms->maj != 0) { | ||
740 | asd_printk("unsupported manuf. sector. major version:%x\n", | ||
741 | ms->maj); | ||
742 | return -ENOENT; | ||
743 | } | ||
744 | ms->offs_next = le16_to_cpu((__force __le16) ms->offs_next); | ||
745 | ms->chksum = le16_to_cpu((__force __le16) ms->chksum); | ||
746 | ms->size = le16_to_cpu((__force __le16) ms->size); | ||
747 | |||
748 | if (asd_calc_flash_chksum((u16 *)ms, ms->size/2)) { | ||
749 | asd_printk("failed manuf sector checksum\n"); | ||
750 | } | ||
751 | |||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static int asd_ms_get_sas_addr(struct asd_ha_struct *asd_ha, | ||
756 | struct asd_manuf_sec *ms) | ||
757 | { | ||
758 | memcpy(asd_ha->hw_prof.sas_addr, ms->sas_addr, SAS_ADDR_SIZE); | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static int asd_ms_get_pcba_sn(struct asd_ha_struct *asd_ha, | ||
763 | struct asd_manuf_sec *ms) | ||
764 | { | ||
765 | memcpy(asd_ha->hw_prof.pcba_sn, ms->pcba_sn, ASD_PCBA_SN_SIZE); | ||
766 | asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE] = '\0'; | ||
767 | return 0; | ||
768 | } | ||
769 | |||
770 | /** | ||
771 | * asd_find_ll_by_id - find a linked list entry by its id | ||
772 | * @start: void pointer to the first element in the linked list | ||
773 | * @id0: the first byte of the id (offs 0) | ||
774 | * @id1: the second byte of the id (offs 1) | ||
775 | * | ||
776 | * @start has to be the _base_ element start, since the | ||
777 | * linked list entries's offset is from this pointer. | ||
778 | * Some linked list entries use only the first id, in which case | ||
779 | * you can pass 0xFF for the second. | ||
780 | */ | ||
781 | static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1) | ||
782 | { | ||
783 | struct asd_ll_el *el = start; | ||
784 | |||
785 | do { | ||
786 | switch (id1) { | ||
787 | default: | ||
788 | if (el->id1 == id1) | ||
789 | case 0xFF: | ||
790 | if (el->id0 == id0) | ||
791 | return el; | ||
792 | } | ||
793 | el = start + le16_to_cpu(el->next); | ||
794 | } while (el != start); | ||
795 | |||
796 | return NULL; | ||
797 | } | ||
798 | |||
799 | /** | ||
800 | * asd_ms_get_phy_params - get phy parameters from the manufacturing sector | ||
801 | * @asd_ha: pointer to the host adapter structure | ||
802 | * @manuf_sec: pointer to the manufacturing sector | ||
803 | * | ||
804 | * The manufacturing sector contans also the linked list of sub-segments, | ||
805 | * since when it was read, its size was taken from the flash directory, | ||
806 | * not from the structure size. | ||
807 | * | ||
808 | * HIDDEN phys do not count in the total count. REPORTED phys cannot | ||
809 | * be enabled but are reported and counted towards the total. | ||
810 | * ENEBLEABLE phys are enabled by default and count towards the total. | ||
811 | * The absolute total phy number is ASD_MAX_PHYS. hw_prof->num_phys | ||
812 | * merely specifies the number of phys the host adapter decided to | ||
813 | * report. E.g., it is possible for phys 0, 1 and 2 to be HIDDEN, | ||
814 | * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENEBLEABLE. | ||
815 | * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2 | ||
816 | * are actually enabled (enabled by default, max number of phys | ||
817 | * enableable in this case). | ||
818 | */ | ||
819 | static int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha, | ||
820 | struct asd_manuf_sec *manuf_sec) | ||
821 | { | ||
822 | int i; | ||
823 | int en_phys = 0; | ||
824 | int rep_phys = 0; | ||
825 | struct asd_manuf_phy_param *phy_param; | ||
826 | struct asd_manuf_phy_param dflt_phy_param; | ||
827 | |||
828 | phy_param = asd_find_ll_by_id(manuf_sec, 'P', 'M'); | ||
829 | if (!phy_param) { | ||
830 | ASD_DPRINTK("ms: no phy parameters found\n"); | ||
831 | ASD_DPRINTK("ms: Creating default phy parameters\n"); | ||
832 | dflt_phy_param.sig[0] = 'P'; | ||
833 | dflt_phy_param.sig[1] = 'M'; | ||
834 | dflt_phy_param.maj = 0; | ||
835 | dflt_phy_param.min = 2; | ||
836 | dflt_phy_param.num_phy_desc = 8; | ||
837 | dflt_phy_param.phy_desc_size = sizeof(struct asd_manuf_phy_desc); | ||
838 | for (i =0; i < ASD_MAX_PHYS; i++) { | ||
839 | dflt_phy_param.phy_desc[i].state = 0; | ||
840 | dflt_phy_param.phy_desc[i].phy_id = i; | ||
841 | dflt_phy_param.phy_desc[i].phy_control_0 = 0xf6; | ||
842 | dflt_phy_param.phy_desc[i].phy_control_1 = 0x10; | ||
843 | dflt_phy_param.phy_desc[i].phy_control_2 = 0x43; | ||
844 | dflt_phy_param.phy_desc[i].phy_control_3 = 0xeb; | ||
845 | } | ||
846 | |||
847 | phy_param = &dflt_phy_param; | ||
848 | |||
849 | } | ||
850 | |||
851 | if (phy_param->maj != 0) { | ||
852 | asd_printk("unsupported manuf. phy param major version:0x%x\n", | ||
853 | phy_param->maj); | ||
854 | return -ENOENT; | ||
855 | } | ||
856 | |||
857 | ASD_DPRINTK("ms: num_phy_desc: %d\n", phy_param->num_phy_desc); | ||
858 | asd_ha->hw_prof.enabled_phys = 0; | ||
859 | for (i = 0; i < phy_param->num_phy_desc; i++) { | ||
860 | struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i]; | ||
861 | switch (pd->state & 0xF) { | ||
862 | case MS_PHY_STATE_HIDDEN: | ||
863 | ASD_DPRINTK("ms: phy%d: HIDDEN\n", i); | ||
864 | continue; | ||
865 | case MS_PHY_STATE_REPORTED: | ||
866 | ASD_DPRINTK("ms: phy%d: REPORTED\n", i); | ||
867 | asd_ha->hw_prof.enabled_phys &= ~(1 << i); | ||
868 | rep_phys++; | ||
869 | continue; | ||
870 | case MS_PHY_STATE_ENABLEABLE: | ||
871 | ASD_DPRINTK("ms: phy%d: ENEBLEABLE\n", i); | ||
872 | asd_ha->hw_prof.enabled_phys |= (1 << i); | ||
873 | en_phys++; | ||
874 | break; | ||
875 | } | ||
876 | asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0; | ||
877 | asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1; | ||
878 | asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2; | ||
879 | asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3; | ||
880 | } | ||
881 | asd_ha->hw_prof.max_phys = rep_phys + en_phys; | ||
882 | asd_ha->hw_prof.num_phys = en_phys; | ||
883 | ASD_DPRINTK("ms: max_phys:0x%x, num_phys:0x%x\n", | ||
884 | asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys); | ||
885 | ASD_DPRINTK("ms: enabled_phys:0x%x\n", asd_ha->hw_prof.enabled_phys); | ||
886 | return 0; | ||
887 | } | ||
888 | |||
889 | static int asd_ms_get_connector_map(struct asd_ha_struct *asd_ha, | ||
890 | struct asd_manuf_sec *manuf_sec) | ||
891 | { | ||
892 | struct asd_ms_conn_map *cm; | ||
893 | |||
894 | cm = asd_find_ll_by_id(manuf_sec, 'M', 'C'); | ||
895 | if (!cm) { | ||
896 | ASD_DPRINTK("ms: no connector map found\n"); | ||
897 | return 0; | ||
898 | } | ||
899 | |||
900 | if (cm->maj != 0) { | ||
901 | ASD_DPRINTK("ms: unsupported: connector map major version 0x%x" | ||
902 | "\n", cm->maj); | ||
903 | return -ENOENT; | ||
904 | } | ||
905 | |||
906 | /* XXX */ | ||
907 | |||
908 | return 0; | ||
909 | } | ||
910 | |||
911 | |||
912 | /** | ||
913 | * asd_process_ms - find and extract information from the manufacturing sector | ||
914 | * @asd_ha: pointer to the host adapter structure | ||
915 | * @flash_dir: pointer to the flash directory | ||
916 | */ | ||
917 | static int asd_process_ms(struct asd_ha_struct *asd_ha, | ||
918 | struct asd_flash_dir *flash_dir) | ||
919 | { | ||
920 | int err; | ||
921 | struct asd_manuf_sec *manuf_sec; | ||
922 | u32 offs, size; | ||
923 | |||
924 | err = asd_find_flash_de(flash_dir, FLASH_DE_MS, &offs, &size); | ||
925 | if (err) { | ||
926 | ASD_DPRINTK("Couldn't find the manuf. sector\n"); | ||
927 | goto out; | ||
928 | } | ||
929 | |||
930 | if (size == 0) | ||
931 | goto out; | ||
932 | |||
933 | err = -ENOMEM; | ||
934 | manuf_sec = kmalloc(size, GFP_KERNEL); | ||
935 | if (!manuf_sec) { | ||
936 | ASD_DPRINTK("no mem for manuf sector\n"); | ||
937 | goto out; | ||
938 | } | ||
939 | |||
940 | err = asd_read_flash_seg(asd_ha, (void *)manuf_sec, offs, size); | ||
941 | if (err) { | ||
942 | ASD_DPRINTK("couldn't read manuf sector at 0x%x, size 0x%x\n", | ||
943 | offs, size); | ||
944 | goto out2; | ||
945 | } | ||
946 | |||
947 | err = asd_validate_ms(manuf_sec); | ||
948 | if (err) { | ||
949 | ASD_DPRINTK("couldn't validate manuf sector\n"); | ||
950 | goto out2; | ||
951 | } | ||
952 | |||
953 | err = asd_ms_get_sas_addr(asd_ha, manuf_sec); | ||
954 | if (err) { | ||
955 | ASD_DPRINTK("couldn't read the SAS_ADDR\n"); | ||
956 | goto out2; | ||
957 | } | ||
958 | ASD_DPRINTK("manuf sect SAS_ADDR %llx\n", | ||
959 | SAS_ADDR(asd_ha->hw_prof.sas_addr)); | ||
960 | |||
961 | err = asd_ms_get_pcba_sn(asd_ha, manuf_sec); | ||
962 | if (err) { | ||
963 | ASD_DPRINTK("couldn't read the PCBA SN\n"); | ||
964 | goto out2; | ||
965 | } | ||
966 | ASD_DPRINTK("manuf sect PCBA SN %s\n", asd_ha->hw_prof.pcba_sn); | ||
967 | |||
968 | err = asd_ms_get_phy_params(asd_ha, manuf_sec); | ||
969 | if (err) { | ||
970 | ASD_DPRINTK("ms: couldn't get phy parameters\n"); | ||
971 | goto out2; | ||
972 | } | ||
973 | |||
974 | err = asd_ms_get_connector_map(asd_ha, manuf_sec); | ||
975 | if (err) { | ||
976 | ASD_DPRINTK("ms: couldn't get connector map\n"); | ||
977 | goto out2; | ||
978 | } | ||
979 | |||
980 | out2: | ||
981 | kfree(manuf_sec); | ||
982 | out: | ||
983 | return err; | ||
984 | } | ||
985 | |||
986 | static int asd_process_ctrla_phy_settings(struct asd_ha_struct *asd_ha, | ||
987 | struct asd_ctrla_phy_settings *ps) | ||
988 | { | ||
989 | int i; | ||
990 | for (i = 0; i < ps->num_phys; i++) { | ||
991 | struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i]; | ||
992 | |||
993 | if (!PHY_ENABLED(asd_ha, i)) | ||
994 | continue; | ||
995 | if (*(u64 *)pe->sas_addr == 0) { | ||
996 | asd_ha->hw_prof.enabled_phys &= ~(1 << i); | ||
997 | continue; | ||
998 | } | ||
999 | /* This is the SAS address which should be sent in IDENTIFY. */ | ||
1000 | memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr, | ||
1001 | SAS_ADDR_SIZE); | ||
1002 | asd_ha->hw_prof.phy_desc[i].max_sas_lrate = | ||
1003 | (pe->sas_link_rates & 0xF0) >> 4; | ||
1004 | asd_ha->hw_prof.phy_desc[i].min_sas_lrate = | ||
1005 | (pe->sas_link_rates & 0x0F); | ||
1006 | asd_ha->hw_prof.phy_desc[i].max_sata_lrate = | ||
1007 | (pe->sata_link_rates & 0xF0) >> 4; | ||
1008 | asd_ha->hw_prof.phy_desc[i].min_sata_lrate = | ||
1009 | (pe->sata_link_rates & 0x0F); | ||
1010 | asd_ha->hw_prof.phy_desc[i].flags = pe->flags; | ||
1011 | ASD_DPRINTK("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x," | ||
1012 | " sata rate:0x%x-0x%x, flags:0x%x\n", | ||
1013 | i, | ||
1014 | SAS_ADDR(asd_ha->hw_prof.phy_desc[i].sas_addr), | ||
1015 | asd_ha->hw_prof.phy_desc[i].max_sas_lrate, | ||
1016 | asd_ha->hw_prof.phy_desc[i].min_sas_lrate, | ||
1017 | asd_ha->hw_prof.phy_desc[i].max_sata_lrate, | ||
1018 | asd_ha->hw_prof.phy_desc[i].min_sata_lrate, | ||
1019 | asd_ha->hw_prof.phy_desc[i].flags); | ||
1020 | } | ||
1021 | |||
1022 | return 0; | ||
1023 | } | ||
1024 | |||
1025 | /** | ||
1026 | * asd_process_ctrl_a_user - process CTRL-A user settings | ||
1027 | * @asd_ha: pointer to the host adapter structure | ||
1028 | * @flash_dir: pointer to the flash directory | ||
1029 | */ | ||
1030 | static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, | ||
1031 | struct asd_flash_dir *flash_dir) | ||
1032 | { | ||
1033 | int err, i; | ||
1034 | u32 offs, size; | ||
1035 | struct asd_ll_el *el; | ||
1036 | struct asd_ctrla_phy_settings *ps; | ||
1037 | struct asd_ctrla_phy_settings dflt_ps; | ||
1038 | |||
1039 | err = asd_find_flash_de(flash_dir, FLASH_DE_CTRL_A_USER, &offs, &size); | ||
1040 | if (err) { | ||
1041 | ASD_DPRINTK("couldn't find CTRL-A user settings section\n"); | ||
1042 | ASD_DPRINTK("Creating default CTRL-A user settings section\n"); | ||
1043 | |||
1044 | dflt_ps.id0 = 'h'; | ||
1045 | dflt_ps.num_phys = 8; | ||
1046 | for (i =0; i < ASD_MAX_PHYS; i++) { | ||
1047 | memcpy(dflt_ps.phy_ent[i].sas_addr, | ||
1048 | asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE); | ||
1049 | dflt_ps.phy_ent[i].sas_link_rates = 0x98; | ||
1050 | dflt_ps.phy_ent[i].flags = 0x0; | ||
1051 | dflt_ps.phy_ent[i].sata_link_rates = 0x0; | ||
1052 | } | ||
1053 | |||
1054 | size = sizeof(struct asd_ctrla_phy_settings); | ||
1055 | ps = &dflt_ps; | ||
1056 | } | ||
1057 | |||
1058 | if (size == 0) | ||
1059 | goto out; | ||
1060 | |||
1061 | err = -ENOMEM; | ||
1062 | el = kmalloc(size, GFP_KERNEL); | ||
1063 | if (!el) { | ||
1064 | ASD_DPRINTK("no mem for ctrla user settings section\n"); | ||
1065 | goto out; | ||
1066 | } | ||
1067 | |||
1068 | err = asd_read_flash_seg(asd_ha, (void *)el, offs, size); | ||
1069 | if (err) { | ||
1070 | ASD_DPRINTK("couldn't read ctrla phy settings section\n"); | ||
1071 | goto out2; | ||
1072 | } | ||
1073 | |||
1074 | err = -ENOENT; | ||
1075 | ps = asd_find_ll_by_id(el, 'h', 0xFF); | ||
1076 | if (!ps) { | ||
1077 | ASD_DPRINTK("couldn't find ctrla phy settings struct\n"); | ||
1078 | goto out2; | ||
1079 | } | ||
1080 | |||
1081 | err = asd_process_ctrla_phy_settings(asd_ha, ps); | ||
1082 | if (err) { | ||
1083 | ASD_DPRINTK("couldn't process ctrla phy settings\n"); | ||
1084 | goto out2; | ||
1085 | } | ||
1086 | out2: | ||
1087 | kfree(el); | ||
1088 | out: | ||
1089 | return err; | ||
1090 | } | ||
1091 | |||
1092 | /** | ||
1093 | * asd_read_flash - read flash memory | ||
1094 | * @asd_ha: pointer to the host adapter structure | ||
1095 | */ | ||
1096 | int asd_read_flash(struct asd_ha_struct *asd_ha) | ||
1097 | { | ||
1098 | int err; | ||
1099 | struct asd_flash_dir *flash_dir; | ||
1100 | |||
1101 | err = asd_flash_getid(asd_ha); | ||
1102 | if (err) | ||
1103 | return err; | ||
1104 | |||
1105 | flash_dir = kmalloc(sizeof(*flash_dir), GFP_KERNEL); | ||
1106 | if (!flash_dir) | ||
1107 | return -ENOMEM; | ||
1108 | |||
1109 | err = -ENOENT; | ||
1110 | if (!asd_find_flash_dir(asd_ha, flash_dir)) { | ||
1111 | ASD_DPRINTK("couldn't find flash directory\n"); | ||
1112 | goto out; | ||
1113 | } | ||
1114 | |||
1115 | if (le32_to_cpu(flash_dir->rev) != 2) { | ||
1116 | asd_printk("unsupported flash dir version:0x%x\n", | ||
1117 | le32_to_cpu(flash_dir->rev)); | ||
1118 | goto out; | ||
1119 | } | ||
1120 | |||
1121 | err = asd_process_ms(asd_ha, flash_dir); | ||
1122 | if (err) { | ||
1123 | ASD_DPRINTK("couldn't process manuf sector settings\n"); | ||
1124 | goto out; | ||
1125 | } | ||
1126 | |||
1127 | err = asd_process_ctrl_a_user(asd_ha, flash_dir); | ||
1128 | if (err) { | ||
1129 | ASD_DPRINTK("couldn't process CTRL-A user settings\n"); | ||
1130 | goto out; | ||
1131 | } | ||
1132 | |||
1133 | out: | ||
1134 | kfree(flash_dir); | ||
1135 | return err; | ||
1136 | } | ||