diff options
author | Jeff Garzik <jeff@garzik.org> | 2006-09-24 01:52:47 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-09-24 01:52:47 -0400 |
commit | 23930fa1cebfea6f79881c588ccd1b0781e49e3f (patch) | |
tree | 36d29e3f83661c4f5f45b6f74ac0d5f9886867a8 /drivers/scsi/aic94xx/aic94xx_sds.c | |
parent | 36b35a5be0e4b406acd816e2122d153e875105be (diff) | |
parent | 4f5537de7c1531398e84e18a24f667e49cc94208 (diff) |
Merge branch 'master' into upstream
Diffstat (limited to 'drivers/scsi/aic94xx/aic94xx_sds.c')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_sds.c | 1089 |
1 files changed, 1089 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..83574b5b4e69 --- /dev/null +++ b/drivers/scsi/aic94xx/aic94xx_sds.c | |||
@@ -0,0 +1,1089 @@ | |||
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 | |||
380 | #define FLASH_SIZE 0x200000 | ||
381 | #define FLASH_DIR_COOKIE "*** ADAPTEC FLASH DIRECTORY *** " | ||
382 | #define FLASH_NEXT_ENTRY_OFFS 0x2000 | ||
383 | #define FLASH_MAX_DIR_ENTRIES 32 | ||
384 | |||
385 | #define FLASH_DE_TYPE_MASK 0x3FFFFFFF | ||
386 | #define FLASH_DE_MS 0x120 | ||
387 | #define FLASH_DE_CTRL_A_USER 0xE0 | ||
388 | |||
389 | struct asd_flash_de { | ||
390 | __le32 type; | ||
391 | __le32 offs; | ||
392 | __le32 pad_size; | ||
393 | __le32 image_size; | ||
394 | __le32 chksum; | ||
395 | u8 _r[12]; | ||
396 | u8 version[32]; | ||
397 | } __attribute__ ((packed)); | ||
398 | |||
399 | struct asd_flash_dir { | ||
400 | u8 cookie[32]; | ||
401 | __le32 rev; /* 2 */ | ||
402 | __le32 chksum; | ||
403 | __le32 chksum_antidote; | ||
404 | __le32 bld; | ||
405 | u8 bld_id[32]; /* build id data */ | ||
406 | u8 ver_data[32]; /* date and time of build */ | ||
407 | __le32 ae_mask; | ||
408 | __le32 v_mask; | ||
409 | __le32 oc_mask; | ||
410 | u8 _r[20]; | ||
411 | struct asd_flash_de dir_entry[FLASH_MAX_DIR_ENTRIES]; | ||
412 | } __attribute__ ((packed)); | ||
413 | |||
414 | struct asd_manuf_sec { | ||
415 | char sig[2]; /* 'S', 'M' */ | ||
416 | u16 offs_next; | ||
417 | u8 maj; /* 0 */ | ||
418 | u8 min; /* 0 */ | ||
419 | u16 chksum; | ||
420 | u16 size; | ||
421 | u8 _r[6]; | ||
422 | u8 sas_addr[SAS_ADDR_SIZE]; | ||
423 | u8 pcba_sn[ASD_PCBA_SN_SIZE]; | ||
424 | /* Here start the other segments */ | ||
425 | u8 linked_list[0]; | ||
426 | } __attribute__ ((packed)); | ||
427 | |||
428 | struct asd_manuf_phy_desc { | ||
429 | u8 state; /* low 4 bits */ | ||
430 | #define MS_PHY_STATE_ENABLEABLE 0 | ||
431 | #define MS_PHY_STATE_REPORTED 1 | ||
432 | #define MS_PHY_STATE_HIDDEN 2 | ||
433 | u8 phy_id; | ||
434 | u16 _r; | ||
435 | u8 phy_control_0; /* mode 5 reg 0x160 */ | ||
436 | u8 phy_control_1; /* mode 5 reg 0x161 */ | ||
437 | u8 phy_control_2; /* mode 5 reg 0x162 */ | ||
438 | u8 phy_control_3; /* mode 5 reg 0x163 */ | ||
439 | } __attribute__ ((packed)); | ||
440 | |||
441 | struct asd_manuf_phy_param { | ||
442 | char sig[2]; /* 'P', 'M' */ | ||
443 | u16 next; | ||
444 | u8 maj; /* 0 */ | ||
445 | u8 min; /* 2 */ | ||
446 | u8 num_phy_desc; /* 8 */ | ||
447 | u8 phy_desc_size; /* 8 */ | ||
448 | u8 _r[3]; | ||
449 | u8 usage_model_id; | ||
450 | u32 _r2; | ||
451 | struct asd_manuf_phy_desc phy_desc[ASD_MAX_PHYS]; | ||
452 | } __attribute__ ((packed)); | ||
453 | |||
454 | #if 0 | ||
455 | static const char *asd_sb_type[] = { | ||
456 | "unknown", | ||
457 | "SGPIO", | ||
458 | [2 ... 0x7F] = "unknown", | ||
459 | [0x80] = "ADPT_I2C", | ||
460 | [0x81 ... 0xFF] = "VENDOR_UNIQUExx" | ||
461 | }; | ||
462 | #endif | ||
463 | |||
464 | struct asd_ms_sb_desc { | ||
465 | u8 type; | ||
466 | u8 node_desc_index; | ||
467 | u8 conn_desc_index; | ||
468 | u8 _recvd[0]; | ||
469 | } __attribute__ ((packed)); | ||
470 | |||
471 | #if 0 | ||
472 | static const char *asd_conn_type[] = { | ||
473 | [0 ... 7] = "unknown", | ||
474 | "SFF8470", | ||
475 | "SFF8482", | ||
476 | "SFF8484", | ||
477 | [0x80] = "PCIX_DAUGHTER0", | ||
478 | [0x81] = "SAS_DAUGHTER0", | ||
479 | [0x82 ... 0xFF] = "VENDOR_UNIQUExx" | ||
480 | }; | ||
481 | |||
482 | static const char *asd_conn_location[] = { | ||
483 | "unknown", | ||
484 | "internal", | ||
485 | "external", | ||
486 | "board_to_board", | ||
487 | }; | ||
488 | #endif | ||
489 | |||
490 | struct asd_ms_conn_desc { | ||
491 | u8 type; | ||
492 | u8 location; | ||
493 | u8 num_sideband_desc; | ||
494 | u8 size_sideband_desc; | ||
495 | u32 _resvd; | ||
496 | u8 name[16]; | ||
497 | struct asd_ms_sb_desc sb_desc[0]; | ||
498 | } __attribute__ ((packed)); | ||
499 | |||
500 | struct asd_nd_phy_desc { | ||
501 | u8 vp_attch_type; | ||
502 | u8 attch_specific[0]; | ||
503 | } __attribute__ ((packed)); | ||
504 | |||
505 | #if 0 | ||
506 | static const char *asd_node_type[] = { | ||
507 | "IOP", | ||
508 | "IO_CONTROLLER", | ||
509 | "EXPANDER", | ||
510 | "PORT_MULTIPLIER", | ||
511 | "PORT_MULTIPLEXER", | ||
512 | "MULTI_DROP_I2C_BUS", | ||
513 | }; | ||
514 | #endif | ||
515 | |||
516 | struct asd_ms_node_desc { | ||
517 | u8 type; | ||
518 | u8 num_phy_desc; | ||
519 | u8 size_phy_desc; | ||
520 | u8 _resvd; | ||
521 | u8 name[16]; | ||
522 | struct asd_nd_phy_desc phy_desc[0]; | ||
523 | } __attribute__ ((packed)); | ||
524 | |||
525 | struct asd_ms_conn_map { | ||
526 | char sig[2]; /* 'M', 'C' */ | ||
527 | __le16 next; | ||
528 | u8 maj; /* 0 */ | ||
529 | u8 min; /* 0 */ | ||
530 | __le16 cm_size; /* size of this struct */ | ||
531 | u8 num_conn; | ||
532 | u8 conn_size; | ||
533 | u8 num_nodes; | ||
534 | u8 usage_model_id; | ||
535 | u32 _resvd; | ||
536 | struct asd_ms_conn_desc conn_desc[0]; | ||
537 | struct asd_ms_node_desc node_desc[0]; | ||
538 | } __attribute__ ((packed)); | ||
539 | |||
540 | struct asd_ctrla_phy_entry { | ||
541 | u8 sas_addr[SAS_ADDR_SIZE]; | ||
542 | u8 sas_link_rates; /* max in hi bits, min in low bits */ | ||
543 | u8 flags; | ||
544 | u8 sata_link_rates; | ||
545 | u8 _r[5]; | ||
546 | } __attribute__ ((packed)); | ||
547 | |||
548 | struct asd_ctrla_phy_settings { | ||
549 | u8 id0; /* P'h'y */ | ||
550 | u8 _r; | ||
551 | u16 next; | ||
552 | u8 num_phys; /* number of PHYs in the PCI function */ | ||
553 | u8 _r2[3]; | ||
554 | struct asd_ctrla_phy_entry phy_ent[ASD_MAX_PHYS]; | ||
555 | } __attribute__ ((packed)); | ||
556 | |||
557 | struct asd_ll_el { | ||
558 | u8 id0; | ||
559 | u8 id1; | ||
560 | __le16 next; | ||
561 | u8 something_here[0]; | ||
562 | } __attribute__ ((packed)); | ||
563 | |||
564 | static int asd_poll_flash(struct asd_ha_struct *asd_ha) | ||
565 | { | ||
566 | int c; | ||
567 | u8 d; | ||
568 | |||
569 | for (c = 5000; c > 0; c--) { | ||
570 | d = asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar); | ||
571 | d ^= asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar); | ||
572 | if (!d) | ||
573 | return 0; | ||
574 | udelay(5); | ||
575 | } | ||
576 | return -ENOENT; | ||
577 | } | ||
578 | |||
579 | static int asd_reset_flash(struct asd_ha_struct *asd_ha) | ||
580 | { | ||
581 | int err; | ||
582 | |||
583 | err = asd_poll_flash(asd_ha); | ||
584 | if (err) | ||
585 | return err; | ||
586 | asd_write_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar, FLASH_RESET); | ||
587 | err = asd_poll_flash(asd_ha); | ||
588 | |||
589 | return err; | ||
590 | } | ||
591 | |||
592 | static inline int asd_read_flash_seg(struct asd_ha_struct *asd_ha, | ||
593 | void *buffer, u32 offs, int size) | ||
594 | { | ||
595 | asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs, | ||
596 | size); | ||
597 | return 0; | ||
598 | } | ||
599 | |||
600 | /** | ||
601 | * asd_find_flash_dir - finds and reads the flash directory | ||
602 | * @asd_ha: pointer to the host adapter structure | ||
603 | * @flash_dir: pointer to flash directory structure | ||
604 | * | ||
605 | * If found, the flash directory segment will be copied to | ||
606 | * @flash_dir. Return 1 if found, 0 if not. | ||
607 | */ | ||
608 | static int asd_find_flash_dir(struct asd_ha_struct *asd_ha, | ||
609 | struct asd_flash_dir *flash_dir) | ||
610 | { | ||
611 | u32 v; | ||
612 | for (v = 0; v < FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) { | ||
613 | asd_read_flash_seg(asd_ha, flash_dir, v, | ||
614 | sizeof(FLASH_DIR_COOKIE)-1); | ||
615 | if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE, | ||
616 | sizeof(FLASH_DIR_COOKIE)-1) == 0) { | ||
617 | asd_ha->hw_prof.flash.dir_offs = v; | ||
618 | asd_read_flash_seg(asd_ha, flash_dir, v, | ||
619 | sizeof(*flash_dir)); | ||
620 | return 1; | ||
621 | } | ||
622 | } | ||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static int asd_flash_getid(struct asd_ha_struct *asd_ha) | ||
627 | { | ||
628 | int err = 0; | ||
629 | u32 reg; | ||
630 | |||
631 | reg = asd_read_reg_dword(asd_ha, EXSICNFGR); | ||
632 | |||
633 | if (!(reg & FLASHEX)) { | ||
634 | ASD_DPRINTK("flash doesn't exist\n"); | ||
635 | return -ENOENT; | ||
636 | } | ||
637 | if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR, | ||
638 | &asd_ha->hw_prof.flash.bar)) { | ||
639 | asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n", | ||
640 | pci_name(asd_ha->pcidev)); | ||
641 | return -ENOENT; | ||
642 | } | ||
643 | asd_ha->hw_prof.flash.present = 1; | ||
644 | asd_ha->hw_prof.flash.wide = reg & FLASHW ? 1 : 0; | ||
645 | err = asd_reset_flash(asd_ha); | ||
646 | if (err) { | ||
647 | ASD_DPRINTK("couldn't reset flash(%d)\n", err); | ||
648 | return err; | ||
649 | } | ||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | static u16 asd_calc_flash_chksum(u16 *p, int size) | ||
654 | { | ||
655 | u16 chksum = 0; | ||
656 | |||
657 | while (size-- > 0) | ||
658 | chksum += *p++; | ||
659 | |||
660 | return chksum; | ||
661 | } | ||
662 | |||
663 | |||
664 | static int asd_find_flash_de(struct asd_flash_dir *flash_dir, u32 entry_type, | ||
665 | u32 *offs, u32 *size) | ||
666 | { | ||
667 | int i; | ||
668 | struct asd_flash_de *de; | ||
669 | |||
670 | for (i = 0; i < FLASH_MAX_DIR_ENTRIES; i++) { | ||
671 | u32 type = le32_to_cpu(flash_dir->dir_entry[i].type); | ||
672 | |||
673 | type &= FLASH_DE_TYPE_MASK; | ||
674 | if (type == entry_type) | ||
675 | break; | ||
676 | } | ||
677 | if (i >= FLASH_MAX_DIR_ENTRIES) | ||
678 | return -ENOENT; | ||
679 | de = &flash_dir->dir_entry[i]; | ||
680 | *offs = le32_to_cpu(de->offs); | ||
681 | *size = le32_to_cpu(de->pad_size); | ||
682 | return 0; | ||
683 | } | ||
684 | |||
685 | static int asd_validate_ms(struct asd_manuf_sec *ms) | ||
686 | { | ||
687 | if (ms->sig[0] != 'S' || ms->sig[1] != 'M') { | ||
688 | ASD_DPRINTK("manuf sec: no valid sig(%c%c)\n", | ||
689 | ms->sig[0], ms->sig[1]); | ||
690 | return -ENOENT; | ||
691 | } | ||
692 | if (ms->maj != 0) { | ||
693 | asd_printk("unsupported manuf. sector. major version:%x\n", | ||
694 | ms->maj); | ||
695 | return -ENOENT; | ||
696 | } | ||
697 | ms->offs_next = le16_to_cpu((__force __le16) ms->offs_next); | ||
698 | ms->chksum = le16_to_cpu((__force __le16) ms->chksum); | ||
699 | ms->size = le16_to_cpu((__force __le16) ms->size); | ||
700 | |||
701 | if (asd_calc_flash_chksum((u16 *)ms, ms->size/2)) { | ||
702 | asd_printk("failed manuf sector checksum\n"); | ||
703 | } | ||
704 | |||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static int asd_ms_get_sas_addr(struct asd_ha_struct *asd_ha, | ||
709 | struct asd_manuf_sec *ms) | ||
710 | { | ||
711 | memcpy(asd_ha->hw_prof.sas_addr, ms->sas_addr, SAS_ADDR_SIZE); | ||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | static int asd_ms_get_pcba_sn(struct asd_ha_struct *asd_ha, | ||
716 | struct asd_manuf_sec *ms) | ||
717 | { | ||
718 | memcpy(asd_ha->hw_prof.pcba_sn, ms->pcba_sn, ASD_PCBA_SN_SIZE); | ||
719 | asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE] = '\0'; | ||
720 | return 0; | ||
721 | } | ||
722 | |||
723 | /** | ||
724 | * asd_find_ll_by_id - find a linked list entry by its id | ||
725 | * @start: void pointer to the first element in the linked list | ||
726 | * @id0: the first byte of the id (offs 0) | ||
727 | * @id1: the second byte of the id (offs 1) | ||
728 | * | ||
729 | * @start has to be the _base_ element start, since the | ||
730 | * linked list entries's offset is from this pointer. | ||
731 | * Some linked list entries use only the first id, in which case | ||
732 | * you can pass 0xFF for the second. | ||
733 | */ | ||
734 | static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1) | ||
735 | { | ||
736 | struct asd_ll_el *el = start; | ||
737 | |||
738 | do { | ||
739 | switch (id1) { | ||
740 | default: | ||
741 | if (el->id1 == id1) | ||
742 | case 0xFF: | ||
743 | if (el->id0 == id0) | ||
744 | return el; | ||
745 | } | ||
746 | el = start + le16_to_cpu(el->next); | ||
747 | } while (el != start); | ||
748 | |||
749 | return NULL; | ||
750 | } | ||
751 | |||
752 | /** | ||
753 | * asd_ms_get_phy_params - get phy parameters from the manufacturing sector | ||
754 | * @asd_ha: pointer to the host adapter structure | ||
755 | * @manuf_sec: pointer to the manufacturing sector | ||
756 | * | ||
757 | * The manufacturing sector contans also the linked list of sub-segments, | ||
758 | * since when it was read, its size was taken from the flash directory, | ||
759 | * not from the structure size. | ||
760 | * | ||
761 | * HIDDEN phys do not count in the total count. REPORTED phys cannot | ||
762 | * be enabled but are reported and counted towards the total. | ||
763 | * ENEBLEABLE phys are enabled by default and count towards the total. | ||
764 | * The absolute total phy number is ASD_MAX_PHYS. hw_prof->num_phys | ||
765 | * merely specifies the number of phys the host adapter decided to | ||
766 | * report. E.g., it is possible for phys 0, 1 and 2 to be HIDDEN, | ||
767 | * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENEBLEABLE. | ||
768 | * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2 | ||
769 | * are actually enabled (enabled by default, max number of phys | ||
770 | * enableable in this case). | ||
771 | */ | ||
772 | static int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha, | ||
773 | struct asd_manuf_sec *manuf_sec) | ||
774 | { | ||
775 | int i; | ||
776 | int en_phys = 0; | ||
777 | int rep_phys = 0; | ||
778 | struct asd_manuf_phy_param *phy_param; | ||
779 | struct asd_manuf_phy_param dflt_phy_param; | ||
780 | |||
781 | phy_param = asd_find_ll_by_id(manuf_sec, 'P', 'M'); | ||
782 | if (!phy_param) { | ||
783 | ASD_DPRINTK("ms: no phy parameters found\n"); | ||
784 | ASD_DPRINTK("ms: Creating default phy parameters\n"); | ||
785 | dflt_phy_param.sig[0] = 'P'; | ||
786 | dflt_phy_param.sig[1] = 'M'; | ||
787 | dflt_phy_param.maj = 0; | ||
788 | dflt_phy_param.min = 2; | ||
789 | dflt_phy_param.num_phy_desc = 8; | ||
790 | dflt_phy_param.phy_desc_size = sizeof(struct asd_manuf_phy_desc); | ||
791 | for (i =0; i < ASD_MAX_PHYS; i++) { | ||
792 | dflt_phy_param.phy_desc[i].state = 0; | ||
793 | dflt_phy_param.phy_desc[i].phy_id = i; | ||
794 | dflt_phy_param.phy_desc[i].phy_control_0 = 0xf6; | ||
795 | dflt_phy_param.phy_desc[i].phy_control_1 = 0x10; | ||
796 | dflt_phy_param.phy_desc[i].phy_control_2 = 0x43; | ||
797 | dflt_phy_param.phy_desc[i].phy_control_3 = 0xeb; | ||
798 | } | ||
799 | |||
800 | phy_param = &dflt_phy_param; | ||
801 | |||
802 | } | ||
803 | |||
804 | if (phy_param->maj != 0) { | ||
805 | asd_printk("unsupported manuf. phy param major version:0x%x\n", | ||
806 | phy_param->maj); | ||
807 | return -ENOENT; | ||
808 | } | ||
809 | |||
810 | ASD_DPRINTK("ms: num_phy_desc: %d\n", phy_param->num_phy_desc); | ||
811 | asd_ha->hw_prof.enabled_phys = 0; | ||
812 | for (i = 0; i < phy_param->num_phy_desc; i++) { | ||
813 | struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i]; | ||
814 | switch (pd->state & 0xF) { | ||
815 | case MS_PHY_STATE_HIDDEN: | ||
816 | ASD_DPRINTK("ms: phy%d: HIDDEN\n", i); | ||
817 | continue; | ||
818 | case MS_PHY_STATE_REPORTED: | ||
819 | ASD_DPRINTK("ms: phy%d: REPORTED\n", i); | ||
820 | asd_ha->hw_prof.enabled_phys &= ~(1 << i); | ||
821 | rep_phys++; | ||
822 | continue; | ||
823 | case MS_PHY_STATE_ENABLEABLE: | ||
824 | ASD_DPRINTK("ms: phy%d: ENEBLEABLE\n", i); | ||
825 | asd_ha->hw_prof.enabled_phys |= (1 << i); | ||
826 | en_phys++; | ||
827 | break; | ||
828 | } | ||
829 | asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0; | ||
830 | asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1; | ||
831 | asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2; | ||
832 | asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3; | ||
833 | } | ||
834 | asd_ha->hw_prof.max_phys = rep_phys + en_phys; | ||
835 | asd_ha->hw_prof.num_phys = en_phys; | ||
836 | ASD_DPRINTK("ms: max_phys:0x%x, num_phys:0x%x\n", | ||
837 | asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys); | ||
838 | ASD_DPRINTK("ms: enabled_phys:0x%x\n", asd_ha->hw_prof.enabled_phys); | ||
839 | return 0; | ||
840 | } | ||
841 | |||
842 | static int asd_ms_get_connector_map(struct asd_ha_struct *asd_ha, | ||
843 | struct asd_manuf_sec *manuf_sec) | ||
844 | { | ||
845 | struct asd_ms_conn_map *cm; | ||
846 | |||
847 | cm = asd_find_ll_by_id(manuf_sec, 'M', 'C'); | ||
848 | if (!cm) { | ||
849 | ASD_DPRINTK("ms: no connector map found\n"); | ||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | if (cm->maj != 0) { | ||
854 | ASD_DPRINTK("ms: unsupported: connector map major version 0x%x" | ||
855 | "\n", cm->maj); | ||
856 | return -ENOENT; | ||
857 | } | ||
858 | |||
859 | /* XXX */ | ||
860 | |||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | |||
865 | /** | ||
866 | * asd_process_ms - find and extract information from the manufacturing sector | ||
867 | * @asd_ha: pointer to the host adapter structure | ||
868 | * @flash_dir: pointer to the flash directory | ||
869 | */ | ||
870 | static int asd_process_ms(struct asd_ha_struct *asd_ha, | ||
871 | struct asd_flash_dir *flash_dir) | ||
872 | { | ||
873 | int err; | ||
874 | struct asd_manuf_sec *manuf_sec; | ||
875 | u32 offs, size; | ||
876 | |||
877 | err = asd_find_flash_de(flash_dir, FLASH_DE_MS, &offs, &size); | ||
878 | if (err) { | ||
879 | ASD_DPRINTK("Couldn't find the manuf. sector\n"); | ||
880 | goto out; | ||
881 | } | ||
882 | |||
883 | if (size == 0) | ||
884 | goto out; | ||
885 | |||
886 | err = -ENOMEM; | ||
887 | manuf_sec = kmalloc(size, GFP_KERNEL); | ||
888 | if (!manuf_sec) { | ||
889 | ASD_DPRINTK("no mem for manuf sector\n"); | ||
890 | goto out; | ||
891 | } | ||
892 | |||
893 | err = asd_read_flash_seg(asd_ha, (void *)manuf_sec, offs, size); | ||
894 | if (err) { | ||
895 | ASD_DPRINTK("couldn't read manuf sector at 0x%x, size 0x%x\n", | ||
896 | offs, size); | ||
897 | goto out2; | ||
898 | } | ||
899 | |||
900 | err = asd_validate_ms(manuf_sec); | ||
901 | if (err) { | ||
902 | ASD_DPRINTK("couldn't validate manuf sector\n"); | ||
903 | goto out2; | ||
904 | } | ||
905 | |||
906 | err = asd_ms_get_sas_addr(asd_ha, manuf_sec); | ||
907 | if (err) { | ||
908 | ASD_DPRINTK("couldn't read the SAS_ADDR\n"); | ||
909 | goto out2; | ||
910 | } | ||
911 | ASD_DPRINTK("manuf sect SAS_ADDR %llx\n", | ||
912 | SAS_ADDR(asd_ha->hw_prof.sas_addr)); | ||
913 | |||
914 | err = asd_ms_get_pcba_sn(asd_ha, manuf_sec); | ||
915 | if (err) { | ||
916 | ASD_DPRINTK("couldn't read the PCBA SN\n"); | ||
917 | goto out2; | ||
918 | } | ||
919 | ASD_DPRINTK("manuf sect PCBA SN %s\n", asd_ha->hw_prof.pcba_sn); | ||
920 | |||
921 | err = asd_ms_get_phy_params(asd_ha, manuf_sec); | ||
922 | if (err) { | ||
923 | ASD_DPRINTK("ms: couldn't get phy parameters\n"); | ||
924 | goto out2; | ||
925 | } | ||
926 | |||
927 | err = asd_ms_get_connector_map(asd_ha, manuf_sec); | ||
928 | if (err) { | ||
929 | ASD_DPRINTK("ms: couldn't get connector map\n"); | ||
930 | goto out2; | ||
931 | } | ||
932 | |||
933 | out2: | ||
934 | kfree(manuf_sec); | ||
935 | out: | ||
936 | return err; | ||
937 | } | ||
938 | |||
939 | static int asd_process_ctrla_phy_settings(struct asd_ha_struct *asd_ha, | ||
940 | struct asd_ctrla_phy_settings *ps) | ||
941 | { | ||
942 | int i; | ||
943 | for (i = 0; i < ps->num_phys; i++) { | ||
944 | struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i]; | ||
945 | |||
946 | if (!PHY_ENABLED(asd_ha, i)) | ||
947 | continue; | ||
948 | if (*(u64 *)pe->sas_addr == 0) { | ||
949 | asd_ha->hw_prof.enabled_phys &= ~(1 << i); | ||
950 | continue; | ||
951 | } | ||
952 | /* This is the SAS address which should be sent in IDENTIFY. */ | ||
953 | memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr, | ||
954 | SAS_ADDR_SIZE); | ||
955 | asd_ha->hw_prof.phy_desc[i].max_sas_lrate = | ||
956 | (pe->sas_link_rates & 0xF0) >> 4; | ||
957 | asd_ha->hw_prof.phy_desc[i].min_sas_lrate = | ||
958 | (pe->sas_link_rates & 0x0F); | ||
959 | asd_ha->hw_prof.phy_desc[i].max_sata_lrate = | ||
960 | (pe->sata_link_rates & 0xF0) >> 4; | ||
961 | asd_ha->hw_prof.phy_desc[i].min_sata_lrate = | ||
962 | (pe->sata_link_rates & 0x0F); | ||
963 | asd_ha->hw_prof.phy_desc[i].flags = pe->flags; | ||
964 | ASD_DPRINTK("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x," | ||
965 | " sata rate:0x%x-0x%x, flags:0x%x\n", | ||
966 | i, | ||
967 | SAS_ADDR(asd_ha->hw_prof.phy_desc[i].sas_addr), | ||
968 | asd_ha->hw_prof.phy_desc[i].max_sas_lrate, | ||
969 | asd_ha->hw_prof.phy_desc[i].min_sas_lrate, | ||
970 | asd_ha->hw_prof.phy_desc[i].max_sata_lrate, | ||
971 | asd_ha->hw_prof.phy_desc[i].min_sata_lrate, | ||
972 | asd_ha->hw_prof.phy_desc[i].flags); | ||
973 | } | ||
974 | |||
975 | return 0; | ||
976 | } | ||
977 | |||
978 | /** | ||
979 | * asd_process_ctrl_a_user - process CTRL-A user settings | ||
980 | * @asd_ha: pointer to the host adapter structure | ||
981 | * @flash_dir: pointer to the flash directory | ||
982 | */ | ||
983 | static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, | ||
984 | struct asd_flash_dir *flash_dir) | ||
985 | { | ||
986 | int err, i; | ||
987 | u32 offs, size; | ||
988 | struct asd_ll_el *el; | ||
989 | struct asd_ctrla_phy_settings *ps; | ||
990 | struct asd_ctrla_phy_settings dflt_ps; | ||
991 | |||
992 | err = asd_find_flash_de(flash_dir, FLASH_DE_CTRL_A_USER, &offs, &size); | ||
993 | if (err) { | ||
994 | ASD_DPRINTK("couldn't find CTRL-A user settings section\n"); | ||
995 | ASD_DPRINTK("Creating default CTRL-A user settings section\n"); | ||
996 | |||
997 | dflt_ps.id0 = 'h'; | ||
998 | dflt_ps.num_phys = 8; | ||
999 | for (i =0; i < ASD_MAX_PHYS; i++) { | ||
1000 | memcpy(dflt_ps.phy_ent[i].sas_addr, | ||
1001 | asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE); | ||
1002 | dflt_ps.phy_ent[i].sas_link_rates = 0x98; | ||
1003 | dflt_ps.phy_ent[i].flags = 0x0; | ||
1004 | dflt_ps.phy_ent[i].sata_link_rates = 0x0; | ||
1005 | } | ||
1006 | |||
1007 | size = sizeof(struct asd_ctrla_phy_settings); | ||
1008 | ps = &dflt_ps; | ||
1009 | } | ||
1010 | |||
1011 | if (size == 0) | ||
1012 | goto out; | ||
1013 | |||
1014 | err = -ENOMEM; | ||
1015 | el = kmalloc(size, GFP_KERNEL); | ||
1016 | if (!el) { | ||
1017 | ASD_DPRINTK("no mem for ctrla user settings section\n"); | ||
1018 | goto out; | ||
1019 | } | ||
1020 | |||
1021 | err = asd_read_flash_seg(asd_ha, (void *)el, offs, size); | ||
1022 | if (err) { | ||
1023 | ASD_DPRINTK("couldn't read ctrla phy settings section\n"); | ||
1024 | goto out2; | ||
1025 | } | ||
1026 | |||
1027 | err = -ENOENT; | ||
1028 | ps = asd_find_ll_by_id(el, 'h', 0xFF); | ||
1029 | if (!ps) { | ||
1030 | ASD_DPRINTK("couldn't find ctrla phy settings struct\n"); | ||
1031 | goto out2; | ||
1032 | } | ||
1033 | |||
1034 | err = asd_process_ctrla_phy_settings(asd_ha, ps); | ||
1035 | if (err) { | ||
1036 | ASD_DPRINTK("couldn't process ctrla phy settings\n"); | ||
1037 | goto out2; | ||
1038 | } | ||
1039 | out2: | ||
1040 | kfree(el); | ||
1041 | out: | ||
1042 | return err; | ||
1043 | } | ||
1044 | |||
1045 | /** | ||
1046 | * asd_read_flash - read flash memory | ||
1047 | * @asd_ha: pointer to the host adapter structure | ||
1048 | */ | ||
1049 | int asd_read_flash(struct asd_ha_struct *asd_ha) | ||
1050 | { | ||
1051 | int err; | ||
1052 | struct asd_flash_dir *flash_dir; | ||
1053 | |||
1054 | err = asd_flash_getid(asd_ha); | ||
1055 | if (err) | ||
1056 | return err; | ||
1057 | |||
1058 | flash_dir = kmalloc(sizeof(*flash_dir), GFP_KERNEL); | ||
1059 | if (!flash_dir) | ||
1060 | return -ENOMEM; | ||
1061 | |||
1062 | err = -ENOENT; | ||
1063 | if (!asd_find_flash_dir(asd_ha, flash_dir)) { | ||
1064 | ASD_DPRINTK("couldn't find flash directory\n"); | ||
1065 | goto out; | ||
1066 | } | ||
1067 | |||
1068 | if (le32_to_cpu(flash_dir->rev) != 2) { | ||
1069 | asd_printk("unsupported flash dir version:0x%x\n", | ||
1070 | le32_to_cpu(flash_dir->rev)); | ||
1071 | goto out; | ||
1072 | } | ||
1073 | |||
1074 | err = asd_process_ms(asd_ha, flash_dir); | ||
1075 | if (err) { | ||
1076 | ASD_DPRINTK("couldn't process manuf sector settings\n"); | ||
1077 | goto out; | ||
1078 | } | ||
1079 | |||
1080 | err = asd_process_ctrl_a_user(asd_ha, flash_dir); | ||
1081 | if (err) { | ||
1082 | ASD_DPRINTK("couldn't process CTRL-A user settings\n"); | ||
1083 | goto out; | ||
1084 | } | ||
1085 | |||
1086 | out: | ||
1087 | kfree(flash_dir); | ||
1088 | return err; | ||
1089 | } | ||