diff options
author | Anand Kumar Santhanam <AnandKumar.Santhanam@pmcs.com> | 2013-09-04 03:27:00 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-10-25 04:58:16 -0400 |
commit | d078b5117f18dce57b895df640d9bf2614864829 (patch) | |
tree | 6d6141a83ba64f24848a0348804e3522866ae815 /drivers/scsi/pm8001/pm80xx_hwi.c | |
parent | 279094079a442c19ff7e7c0fd9511d9404cb2518 (diff) |
[SCSI] pm80xx: Firmware logging support.
Supports below logging facilities,
Inbound outbound queues dump.
Non fatal dump in case of IO failures.
Fatal dump in case of firmware failure.
[jejb: checkpatch spacing fixes]
Signed-off-by: Anandkumar.Santhanam@pmcs.com
Reviewed-by: Jack Wang <jinpu.wang@profitbricks.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/pm8001/pm80xx_hwi.c')
-rw-r--r-- | drivers/scsi/pm8001/pm80xx_hwi.c | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 91cf4242a03d..8987b1706216 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c | |||
@@ -45,6 +45,228 @@ | |||
45 | 45 | ||
46 | #define SMP_DIRECT 1 | 46 | #define SMP_DIRECT 1 |
47 | #define SMP_INDIRECT 2 | 47 | #define SMP_INDIRECT 2 |
48 | |||
49 | |||
50 | int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shift_value) | ||
51 | { | ||
52 | u32 reg_val; | ||
53 | unsigned long start; | ||
54 | pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, shift_value); | ||
55 | /* confirm the setting is written */ | ||
56 | start = jiffies + HZ; /* 1 sec */ | ||
57 | do { | ||
58 | reg_val = pm8001_cr32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER); | ||
59 | } while ((reg_val != shift_value) && time_before(jiffies, start)); | ||
60 | if (reg_val != shift_value) { | ||
61 | PM8001_FAIL_DBG(pm8001_ha, | ||
62 | pm8001_printk("TIMEOUT:MEMBASE_II_SHIFT_REGISTER" | ||
63 | " = 0x%x\n", reg_val)); | ||
64 | return -1; | ||
65 | } | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | void pm80xx_pci_mem_copy(struct pm8001_hba_info *pm8001_ha, u32 soffset, | ||
70 | const void *destination, | ||
71 | u32 dw_count, u32 bus_base_number) | ||
72 | { | ||
73 | u32 index, value, offset; | ||
74 | u32 *destination1; | ||
75 | destination1 = (u32 *)destination; | ||
76 | |||
77 | for (index = 0; index < dw_count; index += 4, destination1++) { | ||
78 | offset = (soffset + index / 4); | ||
79 | if (offset < (64 * 1024)) { | ||
80 | value = pm8001_cr32(pm8001_ha, bus_base_number, offset); | ||
81 | *destination1 = cpu_to_le32(value); | ||
82 | } | ||
83 | } | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | ssize_t pm80xx_get_fatal_dump(struct device *cdev, | ||
88 | struct device_attribute *attr, char *buf) | ||
89 | { | ||
90 | struct Scsi_Host *shost = class_to_shost(cdev); | ||
91 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); | ||
92 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; | ||
93 | void __iomem *fatal_table_address = pm8001_ha->fatal_tbl_addr; | ||
94 | u32 status = 1; | ||
95 | u32 accum_len , reg_val, index, *temp; | ||
96 | unsigned long start; | ||
97 | u8 *direct_data; | ||
98 | char *fatal_error_data = buf; | ||
99 | |||
100 | pm8001_ha->forensic_info.data_buf.direct_data = buf; | ||
101 | if (pm8001_ha->chip_id == chip_8001) { | ||
102 | pm8001_ha->forensic_info.data_buf.direct_data += | ||
103 | sprintf(pm8001_ha->forensic_info.data_buf.direct_data, | ||
104 | "Not supported for SPC controller"); | ||
105 | return (char *)pm8001_ha->forensic_info.data_buf.direct_data - | ||
106 | (char *)buf; | ||
107 | } | ||
108 | if (pm8001_ha->forensic_info.data_buf.direct_offset == 0) { | ||
109 | PM8001_IO_DBG(pm8001_ha, | ||
110 | pm8001_printk("forensic_info TYPE_NON_FATAL..............\n")); | ||
111 | direct_data = (u8 *)fatal_error_data; | ||
112 | pm8001_ha->forensic_info.data_type = TYPE_NON_FATAL; | ||
113 | pm8001_ha->forensic_info.data_buf.direct_len = SYSFS_OFFSET; | ||
114 | pm8001_ha->forensic_info.data_buf.direct_offset = 0; | ||
115 | pm8001_ha->forensic_info.data_buf.read_len = 0; | ||
116 | |||
117 | pm8001_ha->forensic_info.data_buf.direct_data = direct_data; | ||
118 | } | ||
119 | |||
120 | if (pm8001_ha->forensic_info.data_buf.direct_offset == 0) { | ||
121 | /* start to get data */ | ||
122 | /* Program the MEMBASE II Shifting Register with 0x00.*/ | ||
123 | pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, | ||
124 | pm8001_ha->fatal_forensic_shift_offset); | ||
125 | pm8001_ha->forensic_last_offset = 0; | ||
126 | pm8001_ha->forensic_fatal_step = 0; | ||
127 | pm8001_ha->fatal_bar_loc = 0; | ||
128 | } | ||
129 | /* Read until accum_len is retrived */ | ||
130 | accum_len = pm8001_mr32(fatal_table_address, | ||
131 | MPI_FATAL_EDUMP_TABLE_ACCUM_LEN); | ||
132 | PM8001_IO_DBG(pm8001_ha, pm8001_printk("accum_len 0x%x\n", | ||
133 | accum_len)); | ||
134 | if (accum_len == 0xFFFFFFFF) { | ||
135 | PM8001_IO_DBG(pm8001_ha, | ||
136 | pm8001_printk("Possible PCI issue 0x%x not expected\n", | ||
137 | accum_len)); | ||
138 | return status; | ||
139 | } | ||
140 | if (accum_len == 0 || accum_len >= 0x100000) { | ||
141 | pm8001_ha->forensic_info.data_buf.direct_data += | ||
142 | sprintf(pm8001_ha->forensic_info.data_buf.direct_data, | ||
143 | "%08x ", 0xFFFFFFFF); | ||
144 | return (char *)pm8001_ha->forensic_info.data_buf.direct_data - | ||
145 | (char *)buf; | ||
146 | } | ||
147 | temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr; | ||
148 | if (pm8001_ha->forensic_fatal_step == 0) { | ||
149 | moreData: | ||
150 | if (pm8001_ha->forensic_info.data_buf.direct_data) { | ||
151 | /* Data is in bar, copy to host memory */ | ||
152 | pm80xx_pci_mem_copy(pm8001_ha, pm8001_ha->fatal_bar_loc, | ||
153 | pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr, | ||
154 | pm8001_ha->forensic_info.data_buf.direct_len , | ||
155 | 1); | ||
156 | } | ||
157 | pm8001_ha->fatal_bar_loc += | ||
158 | pm8001_ha->forensic_info.data_buf.direct_len; | ||
159 | pm8001_ha->forensic_info.data_buf.direct_offset += | ||
160 | pm8001_ha->forensic_info.data_buf.direct_len; | ||
161 | pm8001_ha->forensic_last_offset += | ||
162 | pm8001_ha->forensic_info.data_buf.direct_len; | ||
163 | pm8001_ha->forensic_info.data_buf.read_len = | ||
164 | pm8001_ha->forensic_info.data_buf.direct_len; | ||
165 | |||
166 | if (pm8001_ha->forensic_last_offset >= accum_len) { | ||
167 | pm8001_ha->forensic_info.data_buf.direct_data += | ||
168 | sprintf(pm8001_ha->forensic_info.data_buf.direct_data, | ||
169 | "%08x ", 3); | ||
170 | for (index = 0; index < (SYSFS_OFFSET / 4); index++) { | ||
171 | pm8001_ha->forensic_info.data_buf.direct_data += | ||
172 | sprintf(pm8001_ha-> | ||
173 | forensic_info.data_buf.direct_data, | ||
174 | "%08x ", *(temp + index)); | ||
175 | } | ||
176 | |||
177 | pm8001_ha->fatal_bar_loc = 0; | ||
178 | pm8001_ha->forensic_fatal_step = 1; | ||
179 | pm8001_ha->fatal_forensic_shift_offset = 0; | ||
180 | pm8001_ha->forensic_last_offset = 0; | ||
181 | status = 0; | ||
182 | return (char *)pm8001_ha-> | ||
183 | forensic_info.data_buf.direct_data - | ||
184 | (char *)buf; | ||
185 | } | ||
186 | if (pm8001_ha->fatal_bar_loc < (64 * 1024)) { | ||
187 | pm8001_ha->forensic_info.data_buf.direct_data += | ||
188 | sprintf(pm8001_ha-> | ||
189 | forensic_info.data_buf.direct_data, | ||
190 | "%08x ", 2); | ||
191 | for (index = 0; index < (SYSFS_OFFSET / 4); index++) { | ||
192 | pm8001_ha->forensic_info.data_buf.direct_data += | ||
193 | sprintf(pm8001_ha-> | ||
194 | forensic_info.data_buf.direct_data, | ||
195 | "%08x ", *(temp + index)); | ||
196 | } | ||
197 | status = 0; | ||
198 | return (char *)pm8001_ha-> | ||
199 | forensic_info.data_buf.direct_data - | ||
200 | (char *)buf; | ||
201 | } | ||
202 | |||
203 | /* Increment the MEMBASE II Shifting Register value by 0x100.*/ | ||
204 | pm8001_ha->forensic_info.data_buf.direct_data += | ||
205 | sprintf(pm8001_ha->forensic_info.data_buf.direct_data, | ||
206 | "%08x ", 2); | ||
207 | for (index = 0; index < 256; index++) { | ||
208 | pm8001_ha->forensic_info.data_buf.direct_data += | ||
209 | sprintf(pm8001_ha-> | ||
210 | forensic_info.data_buf.direct_data, | ||
211 | "%08x ", *(temp + index)); | ||
212 | } | ||
213 | pm8001_ha->fatal_forensic_shift_offset += 0x100; | ||
214 | pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, | ||
215 | pm8001_ha->fatal_forensic_shift_offset); | ||
216 | pm8001_ha->fatal_bar_loc = 0; | ||
217 | status = 0; | ||
218 | return (char *)pm8001_ha->forensic_info.data_buf.direct_data - | ||
219 | (char *)buf; | ||
220 | } | ||
221 | if (pm8001_ha->forensic_fatal_step == 1) { | ||
222 | pm8001_ha->fatal_forensic_shift_offset = 0; | ||
223 | /* Read 64K of the debug data. */ | ||
224 | pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, | ||
225 | pm8001_ha->fatal_forensic_shift_offset); | ||
226 | pm8001_mw32(fatal_table_address, | ||
227 | MPI_FATAL_EDUMP_TABLE_HANDSHAKE, | ||
228 | MPI_FATAL_EDUMP_HANDSHAKE_RDY); | ||
229 | |||
230 | /* Poll FDDHSHK until clear */ | ||
231 | start = jiffies + (2 * HZ); /* 2 sec */ | ||
232 | |||
233 | do { | ||
234 | reg_val = pm8001_mr32(fatal_table_address, | ||
235 | MPI_FATAL_EDUMP_TABLE_HANDSHAKE); | ||
236 | } while ((reg_val) && time_before(jiffies, start)); | ||
237 | |||
238 | if (reg_val != 0) { | ||
239 | PM8001_FAIL_DBG(pm8001_ha, | ||
240 | pm8001_printk("TIMEOUT:MEMBASE_II_SHIFT_REGISTER" | ||
241 | " = 0x%x\n", reg_val)); | ||
242 | return -1; | ||
243 | } | ||
244 | |||
245 | /* Read the next 64K of the debug data. */ | ||
246 | pm8001_ha->forensic_fatal_step = 0; | ||
247 | if (pm8001_mr32(fatal_table_address, | ||
248 | MPI_FATAL_EDUMP_TABLE_STATUS) != | ||
249 | MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE) { | ||
250 | pm8001_mw32(fatal_table_address, | ||
251 | MPI_FATAL_EDUMP_TABLE_HANDSHAKE, 0); | ||
252 | goto moreData; | ||
253 | } else { | ||
254 | pm8001_ha->forensic_info.data_buf.direct_data += | ||
255 | sprintf(pm8001_ha-> | ||
256 | forensic_info.data_buf.direct_data, | ||
257 | "%08x ", 4); | ||
258 | pm8001_ha->forensic_info.data_buf.read_len = 0xFFFFFFFF; | ||
259 | pm8001_ha->forensic_info.data_buf.direct_len = 0; | ||
260 | pm8001_ha->forensic_info.data_buf.direct_offset = 0; | ||
261 | pm8001_ha->forensic_info.data_buf.read_len = 0; | ||
262 | status = 0; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | return (char *)pm8001_ha->forensic_info.data_buf.direct_data - | ||
267 | (char *)buf; | ||
268 | } | ||
269 | |||
48 | /** | 270 | /** |
49 | * read_main_config_table - read the configure table and save it. | 271 | * read_main_config_table - read the configure table and save it. |
50 | * @pm8001_ha: our hba card information | 272 | * @pm8001_ha: our hba card information |
@@ -583,6 +805,9 @@ static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha) | |||
583 | pm8001_ha->pspa_q_tbl_addr = | 805 | pm8001_ha->pspa_q_tbl_addr = |
584 | base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0x90) & | 806 | base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0x90) & |
585 | 0xFFFFFF); | 807 | 0xFFFFFF); |
808 | pm8001_ha->fatal_tbl_addr = | ||
809 | base_addr + (pm8001_cr32(pm8001_ha, pcibar, offset + 0xA0) & | ||
810 | 0xFFFFFF); | ||
586 | 811 | ||
587 | PM8001_INIT_DBG(pm8001_ha, | 812 | PM8001_INIT_DBG(pm8001_ha, |
588 | pm8001_printk("GST OFFSET 0x%x\n", | 813 | pm8001_printk("GST OFFSET 0x%x\n", |