1 | /* |
2 | * PMC-Sierra 8001/8081/8088/8089 SAS/SATA based host adapters driver |
3 | * |
4 | * Copyright (c) 2008-2009 USI Co., Ltd. |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions, and the following disclaimer, |
12 | * without modification. |
13 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
14 | * substantially similar to the "NO WARRANTY" disclaimer below |
15 | * ("Disclaimer") and any redistribution must be conditioned upon |
16 | * including a substantially similar Disclaimer requirement for further |
17 | * binary redistribution. |
18 | * 3. Neither the names of the above-listed copyright holders nor the names |
19 | * of any contributors may be used to endorse or promote products derived |
20 | * from this software without specific prior written permission. |
21 | * |
22 | * Alternatively, this software may be distributed under the terms of the |
23 | * GNU General Public License ("GPL") version 2 as published by the Free |
24 | * Software Foundation. |
25 | * |
26 | * NO WARRANTY |
27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
28 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
29 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR |
30 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
31 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
32 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
33 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
35 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
36 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
37 | * POSSIBILITY OF SUCH DAMAGES. |
38 | * |
39 | */ |
40 | #include <linux/firmware.h> |
41 | #include <linux/slab.h> |
42 | #include "pm8001_sas.h" |
43 | #include "pm8001_ctl.h" |
44 | #include "pm8001_chips.h" |
45 | |
46 | /* scsi host attributes */ |
47 | |
48 | /** |
49 | * pm8001_ctl_mpi_interface_rev_show - MPI interface revision number |
50 | * @cdev: pointer to embedded class device |
51 | * @attr: device attribute (unused) |
52 | * @buf: the buffer returned |
53 | * |
54 | * A sysfs 'read-only' shost attribute. |
55 | */ |
56 | static ssize_t pm8001_ctl_mpi_interface_rev_show(struct device *cdev, |
57 | struct device_attribute *attr, char *buf) |
58 | { |
59 | struct Scsi_Host *shost = class_to_shost(cdev); |
60 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
61 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
62 | |
63 | if (pm8001_ha->chip_id == chip_8001) { |
64 | return sysfs_emit(buf, fmt: "%d\n" , |
65 | pm8001_ha->main_cfg_tbl.pm8001_tbl.interface_rev); |
66 | } else { |
67 | return sysfs_emit(buf, fmt: "%d\n" , |
68 | pm8001_ha->main_cfg_tbl.pm80xx_tbl.interface_rev); |
69 | } |
70 | } |
71 | static |
72 | DEVICE_ATTR(interface_rev, S_IRUGO, pm8001_ctl_mpi_interface_rev_show, NULL); |
73 | |
74 | /** |
75 | * controller_fatal_error_show - check controller is under fatal err |
76 | * @cdev: pointer to embedded class device |
77 | * @attr: device attribute (unused) |
78 | * @buf: the buffer returned |
79 | * |
80 | * A sysfs 'read-only' shost attribute. |
81 | */ |
82 | static ssize_t controller_fatal_error_show(struct device *cdev, |
83 | struct device_attribute *attr, char *buf) |
84 | { |
85 | struct Scsi_Host *shost = class_to_shost(cdev); |
86 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
87 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
88 | |
89 | return sysfs_emit(buf, fmt: "%d\n" , |
90 | pm8001_ha->controller_fatal_error); |
91 | } |
92 | static DEVICE_ATTR_RO(controller_fatal_error); |
93 | |
94 | /** |
95 | * pm8001_ctl_fw_version_show - firmware version |
96 | * @cdev: pointer to embedded class device |
97 | * @attr: device attribute (unused) |
98 | * @buf: the buffer returned |
99 | * |
100 | * A sysfs 'read-only' shost attribute. |
101 | */ |
102 | static ssize_t pm8001_ctl_fw_version_show(struct device *cdev, |
103 | struct device_attribute *attr, char *buf) |
104 | { |
105 | struct Scsi_Host *shost = class_to_shost(cdev); |
106 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
107 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
108 | |
109 | if (pm8001_ha->chip_id == chip_8001) { |
110 | return sysfs_emit(buf, fmt: "%02x.%02x.%02x.%02x\n" , |
111 | (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev >> 24), |
112 | (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev >> 16), |
113 | (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev >> 8), |
114 | (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev)); |
115 | } else { |
116 | return sysfs_emit(buf, fmt: "%02x.%02x.%02x.%02x\n" , |
117 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev >> 24), |
118 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev >> 16), |
119 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev >> 8), |
120 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev)); |
121 | } |
122 | } |
123 | static DEVICE_ATTR(fw_version, S_IRUGO, pm8001_ctl_fw_version_show, NULL); |
124 | |
125 | /** |
126 | * pm8001_ctl_ila_version_show - ila version |
127 | * @cdev: pointer to embedded class device |
128 | * @attr: device attribute (unused) |
129 | * @buf: the buffer returned |
130 | * |
131 | * A sysfs 'read-only' shost attribute. |
132 | */ |
133 | static ssize_t pm8001_ctl_ila_version_show(struct device *cdev, |
134 | struct device_attribute *attr, char *buf) |
135 | { |
136 | struct Scsi_Host *shost = class_to_shost(cdev); |
137 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
138 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
139 | |
140 | if (pm8001_ha->chip_id != chip_8001) { |
141 | return sysfs_emit(buf, fmt: "%02x.%02x.%02x.%02x\n" , |
142 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version >> 24), |
143 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version >> 16), |
144 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version >> 8), |
145 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version)); |
146 | } |
147 | return 0; |
148 | } |
149 | static DEVICE_ATTR(ila_version, 0444, pm8001_ctl_ila_version_show, NULL); |
150 | |
151 | /** |
152 | * pm8001_ctl_inactive_fw_version_show - Inactive firmware version number |
153 | * @cdev: pointer to embedded class device |
154 | * @attr: device attribute (unused) |
155 | * @buf: the buffer returned |
156 | * |
157 | * A sysfs 'read-only' shost attribute. |
158 | */ |
159 | static ssize_t pm8001_ctl_inactive_fw_version_show(struct device *cdev, |
160 | struct device_attribute *attr, char *buf) |
161 | { |
162 | struct Scsi_Host *shost = class_to_shost(cdev); |
163 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
164 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
165 | |
166 | if (pm8001_ha->chip_id != chip_8001) { |
167 | return sysfs_emit(buf, fmt: "%02x.%02x.%02x.%02x\n" , |
168 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version >> 24), |
169 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version >> 16), |
170 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version >> 8), |
171 | (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version)); |
172 | } |
173 | return 0; |
174 | } |
175 | static |
176 | DEVICE_ATTR(inc_fw_ver, 0444, pm8001_ctl_inactive_fw_version_show, NULL); |
177 | |
178 | /** |
179 | * pm8001_ctl_max_out_io_show - max outstanding io supported |
180 | * @cdev: pointer to embedded class device |
181 | * @attr: device attribute (unused) |
182 | * @buf: the buffer returned |
183 | * |
184 | * A sysfs 'read-only' shost attribute. |
185 | */ |
186 | static ssize_t pm8001_ctl_max_out_io_show(struct device *cdev, |
187 | struct device_attribute *attr, char *buf) |
188 | { |
189 | struct Scsi_Host *shost = class_to_shost(cdev); |
190 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
191 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
192 | |
193 | if (pm8001_ha->chip_id == chip_8001) { |
194 | return sysfs_emit(buf, fmt: "%d\n" , |
195 | pm8001_ha->main_cfg_tbl.pm8001_tbl.max_out_io); |
196 | } else { |
197 | return sysfs_emit(buf, fmt: "%d\n" , |
198 | pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_out_io); |
199 | } |
200 | } |
201 | static DEVICE_ATTR(max_out_io, S_IRUGO, pm8001_ctl_max_out_io_show, NULL); |
202 | /** |
203 | * pm8001_ctl_max_devices_show - max devices support |
204 | * @cdev: pointer to embedded class device |
205 | * @attr: device attribute (unused) |
206 | * @buf: the buffer returned |
207 | * |
208 | * A sysfs 'read-only' shost attribute. |
209 | */ |
210 | static ssize_t pm8001_ctl_max_devices_show(struct device *cdev, |
211 | struct device_attribute *attr, char *buf) |
212 | { |
213 | struct Scsi_Host *shost = class_to_shost(cdev); |
214 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
215 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
216 | |
217 | if (pm8001_ha->chip_id == chip_8001) { |
218 | return sysfs_emit(buf, fmt: "%04d\n" , |
219 | (u16)(pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl >> 16)); |
220 | } else { |
221 | return sysfs_emit(buf, fmt: "%04d\n" , |
222 | (u16)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_sgl >> 16)); |
223 | } |
224 | } |
225 | static DEVICE_ATTR(max_devices, S_IRUGO, pm8001_ctl_max_devices_show, NULL); |
226 | /** |
227 | * pm8001_ctl_max_sg_list_show - max sg list supported iff not 0.0 for no |
228 | * hardware limitation |
229 | * @cdev: pointer to embedded class device |
230 | * @attr: device attribute (unused) |
231 | * @buf: the buffer returned |
232 | * |
233 | * A sysfs 'read-only' shost attribute. |
234 | */ |
235 | static ssize_t pm8001_ctl_max_sg_list_show(struct device *cdev, |
236 | struct device_attribute *attr, char *buf) |
237 | { |
238 | struct Scsi_Host *shost = class_to_shost(cdev); |
239 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
240 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
241 | |
242 | if (pm8001_ha->chip_id == chip_8001) { |
243 | return sysfs_emit(buf, fmt: "%04d\n" , |
244 | pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl & 0x0000FFFF); |
245 | } else { |
246 | return sysfs_emit(buf, fmt: "%04d\n" , |
247 | pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_sgl & 0x0000FFFF); |
248 | } |
249 | } |
250 | static DEVICE_ATTR(max_sg_list, S_IRUGO, pm8001_ctl_max_sg_list_show, NULL); |
251 | |
252 | #define SAS_1_0 0x1 |
253 | #define SAS_1_1 0x2 |
254 | #define SAS_2_0 0x4 |
255 | |
256 | static ssize_t |
257 | show_sas_spec_support_status(unsigned int mode, char *buf) |
258 | { |
259 | ssize_t len = 0; |
260 | |
261 | if (mode & SAS_1_1) |
262 | len = sprintf(buf, fmt: "%s" , "SAS1.1" ); |
263 | if (mode & SAS_2_0) |
264 | len += sprintf(buf: buf + len, fmt: "%s%s" , len ? ", " : "" , "SAS2.0" ); |
265 | len += sprintf(buf: buf + len, fmt: "\n" ); |
266 | |
267 | return len; |
268 | } |
269 | |
270 | /** |
271 | * pm8001_ctl_sas_spec_support_show - sas spec supported |
272 | * @cdev: pointer to embedded class device |
273 | * @attr: device attribute (unused) |
274 | * @buf: the buffer returned |
275 | * |
276 | * A sysfs 'read-only' shost attribute. |
277 | */ |
278 | static ssize_t pm8001_ctl_sas_spec_support_show(struct device *cdev, |
279 | struct device_attribute *attr, char *buf) |
280 | { |
281 | unsigned int mode; |
282 | struct Scsi_Host *shost = class_to_shost(cdev); |
283 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
284 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
285 | /* fe000000 means supports SAS2.1 */ |
286 | if (pm8001_ha->chip_id == chip_8001) |
287 | mode = (pm8001_ha->main_cfg_tbl.pm8001_tbl.ctrl_cap_flag & |
288 | 0xfe000000)>>25; |
289 | else |
290 | /* fe000000 means supports SAS2.1 */ |
291 | mode = (pm8001_ha->main_cfg_tbl.pm80xx_tbl.ctrl_cap_flag & |
292 | 0xfe000000)>>25; |
293 | return show_sas_spec_support_status(mode, buf); |
294 | } |
295 | static DEVICE_ATTR(sas_spec_support, S_IRUGO, |
296 | pm8001_ctl_sas_spec_support_show, NULL); |
297 | |
298 | /** |
299 | * pm8001_ctl_host_sas_address_show - sas address |
300 | * @cdev: pointer to embedded class device |
301 | * @attr: device attribute (unused) |
302 | * @buf: the buffer returned |
303 | * |
304 | * This is the controller sas address |
305 | * |
306 | * A sysfs 'read-only' shost attribute. |
307 | */ |
308 | static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev, |
309 | struct device_attribute *attr, char *buf) |
310 | { |
311 | struct Scsi_Host *shost = class_to_shost(cdev); |
312 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
313 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
314 | return sysfs_emit(buf, fmt: "0x%016llx\n" , |
315 | be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr)); |
316 | } |
317 | static DEVICE_ATTR(host_sas_address, S_IRUGO, |
318 | pm8001_ctl_host_sas_address_show, NULL); |
319 | |
320 | /** |
321 | * pm8001_ctl_logging_level_show - logging level |
322 | * @cdev: pointer to embedded class device |
323 | * @attr: device attribute (unused) |
324 | * @buf: the buffer returned |
325 | * |
326 | * A sysfs 'read/write' shost attribute. |
327 | */ |
328 | static ssize_t pm8001_ctl_logging_level_show(struct device *cdev, |
329 | struct device_attribute *attr, char *buf) |
330 | { |
331 | struct Scsi_Host *shost = class_to_shost(cdev); |
332 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
333 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
334 | |
335 | return sysfs_emit(buf, fmt: "%08xh\n" , pm8001_ha->logging_level); |
336 | } |
337 | |
338 | static ssize_t pm8001_ctl_logging_level_store(struct device *cdev, |
339 | struct device_attribute *attr, const char *buf, size_t count) |
340 | { |
341 | struct Scsi_Host *shost = class_to_shost(cdev); |
342 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
343 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
344 | int val = 0; |
345 | |
346 | if (sscanf(buf, "%x" , &val) != 1) |
347 | return -EINVAL; |
348 | |
349 | pm8001_ha->logging_level = val; |
350 | return strlen(buf); |
351 | } |
352 | |
353 | static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, |
354 | pm8001_ctl_logging_level_show, pm8001_ctl_logging_level_store); |
355 | /** |
356 | * pm8001_ctl_aap_log_show - aap1 event log |
357 | * @cdev: pointer to embedded class device |
358 | * @attr: device attribute (unused) |
359 | * @buf: the buffer returned |
360 | * |
361 | * A sysfs 'read-only' shost attribute. |
362 | */ |
363 | static ssize_t pm8001_ctl_aap_log_show(struct device *cdev, |
364 | struct device_attribute *attr, char *buf) |
365 | { |
366 | struct Scsi_Host *shost = class_to_shost(cdev); |
367 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
368 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
369 | u8 *ptr = (u8 *)pm8001_ha->memoryMap.region[AAP1].virt_ptr; |
370 | int i; |
371 | |
372 | char *str = buf; |
373 | int max = 2; |
374 | for (i = 0; i < max; i++) { |
375 | str += sprintf(buf: str, fmt: "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" |
376 | "0x%08x 0x%08x\n" , |
377 | pm8001_ctl_aap1_memmap(ptr, idx: i, off: 0), |
378 | pm8001_ctl_aap1_memmap(ptr, idx: i, off: 4), |
379 | pm8001_ctl_aap1_memmap(ptr, idx: i, off: 8), |
380 | pm8001_ctl_aap1_memmap(ptr, idx: i, off: 12), |
381 | pm8001_ctl_aap1_memmap(ptr, idx: i, off: 16), |
382 | pm8001_ctl_aap1_memmap(ptr, idx: i, off: 20), |
383 | pm8001_ctl_aap1_memmap(ptr, idx: i, off: 24), |
384 | pm8001_ctl_aap1_memmap(ptr, idx: i, off: 28)); |
385 | } |
386 | |
387 | return str - buf; |
388 | } |
389 | static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL); |
390 | /** |
391 | * pm8001_ctl_ib_queue_log_show - Out bound Queue log |
392 | * @cdev:pointer to embedded class device |
393 | * @attr: device attribute (unused) |
394 | * @buf: the buffer returned |
395 | * |
396 | * A sysfs 'read-only' shost attribute. |
397 | */ |
398 | static ssize_t pm8001_ctl_ib_queue_log_show(struct device *cdev, |
399 | struct device_attribute *attr, char *buf) |
400 | { |
401 | struct Scsi_Host *shost = class_to_shost(cdev); |
402 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
403 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
404 | int offset; |
405 | char *str = buf; |
406 | int start = 0; |
407 | u32 ib_offset = pm8001_ha->ib_offset; |
408 | u32 queue_size = pm8001_ha->max_q_num * PM8001_MPI_QUEUE * 128; |
409 | #define IB_MEMMAP(c) \ |
410 | (*(u32 *)((u8 *)pm8001_ha-> \ |
411 | memoryMap.region[ib_offset].virt_ptr + \ |
412 | pm8001_ha->evtlog_ib_offset + (c))) |
413 | |
414 | for (offset = 0; offset < IB_OB_READ_TIMES; offset++) { |
415 | str += sprintf(buf: str, fmt: "0x%08x\n" , IB_MEMMAP(start)); |
416 | start = start + 4; |
417 | } |
418 | pm8001_ha->evtlog_ib_offset += SYSFS_OFFSET; |
419 | if (((pm8001_ha->evtlog_ib_offset) % queue_size) == 0) |
420 | pm8001_ha->evtlog_ib_offset = 0; |
421 | |
422 | return str - buf; |
423 | } |
424 | |
425 | static DEVICE_ATTR(ib_log, S_IRUGO, pm8001_ctl_ib_queue_log_show, NULL); |
426 | /** |
427 | * pm8001_ctl_ob_queue_log_show - Out bound Queue log |
428 | * @cdev:pointer to embedded class device |
429 | * @attr: device attribute (unused) |
430 | * @buf: the buffer returned |
431 | * |
432 | * A sysfs 'read-only' shost attribute. |
433 | */ |
434 | |
435 | static ssize_t pm8001_ctl_ob_queue_log_show(struct device *cdev, |
436 | struct device_attribute *attr, char *buf) |
437 | { |
438 | struct Scsi_Host *shost = class_to_shost(cdev); |
439 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
440 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
441 | int offset; |
442 | char *str = buf; |
443 | int start = 0; |
444 | u32 ob_offset = pm8001_ha->ob_offset; |
445 | u32 queue_size = pm8001_ha->max_q_num * PM8001_MPI_QUEUE * 128; |
446 | #define OB_MEMMAP(c) \ |
447 | (*(u32 *)((u8 *)pm8001_ha-> \ |
448 | memoryMap.region[ob_offset].virt_ptr + \ |
449 | pm8001_ha->evtlog_ob_offset + (c))) |
450 | |
451 | for (offset = 0; offset < IB_OB_READ_TIMES; offset++) { |
452 | str += sprintf(buf: str, fmt: "0x%08x\n" , OB_MEMMAP(start)); |
453 | start = start + 4; |
454 | } |
455 | pm8001_ha->evtlog_ob_offset += SYSFS_OFFSET; |
456 | if (((pm8001_ha->evtlog_ob_offset) % queue_size) == 0) |
457 | pm8001_ha->evtlog_ob_offset = 0; |
458 | |
459 | return str - buf; |
460 | } |
461 | static DEVICE_ATTR(ob_log, S_IRUGO, pm8001_ctl_ob_queue_log_show, NULL); |
462 | /** |
463 | * pm8001_ctl_bios_version_show - Bios version Display |
464 | * @cdev:pointer to embedded class device |
465 | * @attr: device attribute (unused) |
466 | * @buf:the buffer returned |
467 | * |
468 | * A sysfs 'read-only' shost attribute. |
469 | */ |
470 | static ssize_t pm8001_ctl_bios_version_show(struct device *cdev, |
471 | struct device_attribute *attr, char *buf) |
472 | { |
473 | struct Scsi_Host *shost = class_to_shost(cdev); |
474 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
475 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
476 | char *str = buf; |
477 | int bios_index; |
478 | DECLARE_COMPLETION_ONSTACK(completion); |
479 | struct pm8001_ioctl_payload payload; |
480 | |
481 | pm8001_ha->nvmd_completion = &completion; |
482 | payload.minor_function = 7; |
483 | payload.offset = 0; |
484 | payload.rd_length = 4096; |
485 | payload.func_specific = kzalloc(size: 4096, GFP_KERNEL); |
486 | if (!payload.func_specific) |
487 | return -ENOMEM; |
488 | if (PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload)) { |
489 | kfree(objp: payload.func_specific); |
490 | return -ENOMEM; |
491 | } |
492 | wait_for_completion(&completion); |
493 | for (bios_index = BIOSOFFSET; bios_index < BIOS_OFFSET_LIMIT; |
494 | bios_index++) |
495 | str += sprintf(buf: str, fmt: "%c" , |
496 | *(payload.func_specific+bios_index)); |
497 | kfree(objp: payload.func_specific); |
498 | return str - buf; |
499 | } |
500 | static DEVICE_ATTR(bios_version, S_IRUGO, pm8001_ctl_bios_version_show, NULL); |
501 | /** |
502 | * event_log_size_show - event log size |
503 | * @cdev: pointer to embedded class device |
504 | * @attr: device attribute (unused) |
505 | * @buf: the buffer returned |
506 | * |
507 | * A sysfs read shost attribute. |
508 | */ |
509 | static ssize_t event_log_size_show(struct device *cdev, |
510 | struct device_attribute *attr, char *buf) |
511 | { |
512 | struct Scsi_Host *shost = class_to_shost(cdev); |
513 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
514 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
515 | |
516 | return sysfs_emit(buf, fmt: "%d\n" , |
517 | pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_size); |
518 | } |
519 | static DEVICE_ATTR_RO(event_log_size); |
520 | /** |
521 | * pm8001_ctl_iop_log_show - IOP event log |
522 | * @cdev: pointer to embedded class device |
523 | * @attr: device attribute (unused) |
524 | * @buf: the buffer returned |
525 | * |
526 | * A sysfs 'read-only' shost attribute. |
527 | */ |
528 | static ssize_t pm8001_ctl_iop_log_show(struct device *cdev, |
529 | struct device_attribute *attr, char *buf) |
530 | { |
531 | struct Scsi_Host *shost = class_to_shost(cdev); |
532 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
533 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
534 | char *str = buf; |
535 | u32 read_size = |
536 | pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_size / 1024; |
537 | static u32 start, end, count; |
538 | u32 max_read_times = 32; |
539 | u32 max_count = (read_size * 1024) / (max_read_times * 4); |
540 | u32 *temp = (u32 *)pm8001_ha->memoryMap.region[IOP].virt_ptr; |
541 | |
542 | if ((count % max_count) == 0) { |
543 | start = 0; |
544 | end = max_read_times; |
545 | count = 0; |
546 | } else { |
547 | start = end; |
548 | end = end + max_read_times; |
549 | } |
550 | |
551 | for (; start < end; start++) |
552 | str += sprintf(buf: str, fmt: "%08x " , *(temp+start)); |
553 | count++; |
554 | return str - buf; |
555 | } |
556 | static DEVICE_ATTR(iop_log, S_IRUGO, pm8001_ctl_iop_log_show, NULL); |
557 | |
558 | /** |
559 | * pm8001_ctl_fatal_log_show - fatal error logging |
560 | * @cdev:pointer to embedded class device |
561 | * @attr: device attribute |
562 | * @buf: the buffer returned |
563 | * |
564 | * A sysfs 'read-only' shost attribute. |
565 | */ |
566 | |
567 | static ssize_t pm8001_ctl_fatal_log_show(struct device *cdev, |
568 | struct device_attribute *attr, char *buf) |
569 | { |
570 | ssize_t count; |
571 | |
572 | count = pm80xx_get_fatal_dump(cdev, attr, buf); |
573 | return count; |
574 | } |
575 | |
576 | static DEVICE_ATTR(fatal_log, S_IRUGO, pm8001_ctl_fatal_log_show, NULL); |
577 | |
578 | /** |
579 | * non_fatal_log_show - non fatal error logging |
580 | * @cdev:pointer to embedded class device |
581 | * @attr: device attribute |
582 | * @buf: the buffer returned |
583 | * |
584 | * A sysfs 'read-only' shost attribute. |
585 | */ |
586 | static ssize_t non_fatal_log_show(struct device *cdev, |
587 | struct device_attribute *attr, char *buf) |
588 | { |
589 | u32 count; |
590 | |
591 | count = pm80xx_get_non_fatal_dump(cdev, attr, buf); |
592 | return count; |
593 | } |
594 | static DEVICE_ATTR_RO(non_fatal_log); |
595 | |
596 | static ssize_t non_fatal_count_show(struct device *cdev, |
597 | struct device_attribute *attr, char *buf) |
598 | { |
599 | struct Scsi_Host *shost = class_to_shost(cdev); |
600 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
601 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
602 | |
603 | return sysfs_emit(buf, fmt: "%08x\n" , |
604 | pm8001_ha->non_fatal_count); |
605 | } |
606 | |
607 | static ssize_t non_fatal_count_store(struct device *cdev, |
608 | struct device_attribute *attr, const char *buf, size_t count) |
609 | { |
610 | struct Scsi_Host *shost = class_to_shost(cdev); |
611 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
612 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
613 | int val = 0; |
614 | |
615 | if (kstrtoint(s: buf, base: 16, res: &val) != 0) |
616 | return -EINVAL; |
617 | |
618 | pm8001_ha->non_fatal_count = val; |
619 | return strlen(buf); |
620 | } |
621 | static DEVICE_ATTR_RW(non_fatal_count); |
622 | |
623 | /** |
624 | * pm8001_ctl_gsm_log_show - gsm dump collection |
625 | * @cdev:pointer to embedded class device |
626 | * @attr: device attribute (unused) |
627 | * @buf: the buffer returned |
628 | * |
629 | * A sysfs 'read-only' shost attribute. |
630 | */ |
631 | static ssize_t pm8001_ctl_gsm_log_show(struct device *cdev, |
632 | struct device_attribute *attr, char *buf) |
633 | { |
634 | ssize_t count; |
635 | |
636 | count = pm8001_get_gsm_dump(cdev, SYSFS_OFFSET, buf); |
637 | return count; |
638 | } |
639 | |
640 | static DEVICE_ATTR(gsm_log, S_IRUGO, pm8001_ctl_gsm_log_show, NULL); |
641 | |
642 | #define FLASH_CMD_NONE 0x00 |
643 | #define FLASH_CMD_UPDATE 0x01 |
644 | #define FLASH_CMD_SET_NVMD 0x02 |
645 | |
646 | struct flash_command { |
647 | u8 command[8]; |
648 | int code; |
649 | }; |
650 | |
651 | static const struct flash_command flash_command_table[] = { |
652 | {"set_nvmd" , FLASH_CMD_SET_NVMD}, |
653 | {"update" , FLASH_CMD_UPDATE}, |
654 | {"" , FLASH_CMD_NONE} /* Last entry should be NULL. */ |
655 | }; |
656 | |
657 | struct error_fw { |
658 | char *reason; |
659 | int err_code; |
660 | }; |
661 | |
662 | static const struct error_fw flash_error_table[] = { |
663 | {"Failed to open fw image file" , FAIL_OPEN_BIOS_FILE}, |
664 | {"image header mismatch" , FLASH_UPDATE_HDR_ERR}, |
665 | {"image offset mismatch" , FLASH_UPDATE_OFFSET_ERR}, |
666 | {"image CRC Error" , FLASH_UPDATE_CRC_ERR}, |
667 | {"image length Error." , FLASH_UPDATE_LENGTH_ERR}, |
668 | {"Failed to program flash chip" , FLASH_UPDATE_HW_ERR}, |
669 | {"Flash chip not supported." , FLASH_UPDATE_DNLD_NOT_SUPPORTED}, |
670 | {"Flash update disabled." , FLASH_UPDATE_DISABLED}, |
671 | {"Flash in progress" , FLASH_IN_PROGRESS}, |
672 | {"Image file size Error" , FAIL_FILE_SIZE}, |
673 | {"Input parameter error" , FAIL_PARAMETERS}, |
674 | {"Out of memory" , FAIL_OUT_MEMORY}, |
675 | {"OK" , 0} /* Last entry err_code = 0. */ |
676 | }; |
677 | |
678 | static int pm8001_set_nvmd(struct pm8001_hba_info *pm8001_ha) |
679 | { |
680 | struct pm8001_ioctl_payload *payload; |
681 | DECLARE_COMPLETION_ONSTACK(completion); |
682 | u8 *ioctlbuffer; |
683 | u32 ret; |
684 | u32 length = 1024 * 5 + sizeof(*payload) - 1; |
685 | |
686 | if (pm8001_ha->fw_image->size > 4096) { |
687 | pm8001_ha->fw_status = FAIL_FILE_SIZE; |
688 | return -EFAULT; |
689 | } |
690 | |
691 | ioctlbuffer = kzalloc(size: length, GFP_KERNEL); |
692 | if (!ioctlbuffer) { |
693 | pm8001_ha->fw_status = FAIL_OUT_MEMORY; |
694 | return -ENOMEM; |
695 | } |
696 | payload = (struct pm8001_ioctl_payload *)ioctlbuffer; |
697 | memcpy((u8 *)&payload->func_specific, (u8 *)pm8001_ha->fw_image->data, |
698 | pm8001_ha->fw_image->size); |
699 | payload->wr_length = pm8001_ha->fw_image->size; |
700 | payload->id = 0; |
701 | payload->minor_function = 0x1; |
702 | pm8001_ha->nvmd_completion = &completion; |
703 | ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload); |
704 | if (ret) { |
705 | pm8001_ha->fw_status = FAIL_OUT_MEMORY; |
706 | goto out; |
707 | } |
708 | wait_for_completion(&completion); |
709 | out: |
710 | kfree(objp: ioctlbuffer); |
711 | return ret; |
712 | } |
713 | |
714 | static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha) |
715 | { |
716 | struct pm8001_ioctl_payload *payload; |
717 | DECLARE_COMPLETION_ONSTACK(completion); |
718 | u8 *ioctlbuffer; |
719 | struct fw_control_info *fwControl; |
720 | __be32 partitionSizeTmp; |
721 | u32 partitionSize; |
722 | u32 loopNumber, loopcount; |
723 | struct pm8001_fw_image_header *image_hdr; |
724 | u32 sizeRead = 0; |
725 | u32 ret = 0; |
726 | u32 length = 1024 * 16 + sizeof(*payload) - 1; |
727 | u32 fc_len; |
728 | u8 *read_buf; |
729 | |
730 | if (pm8001_ha->fw_image->size < 28) { |
731 | pm8001_ha->fw_status = FAIL_FILE_SIZE; |
732 | return -EFAULT; |
733 | } |
734 | ioctlbuffer = kzalloc(size: length, GFP_KERNEL); |
735 | if (!ioctlbuffer) { |
736 | pm8001_ha->fw_status = FAIL_OUT_MEMORY; |
737 | return -ENOMEM; |
738 | } |
739 | image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data; |
740 | while (sizeRead < pm8001_ha->fw_image->size) { |
741 | partitionSizeTmp = |
742 | *(__be32 *)((u8 *)&image_hdr->image_length + sizeRead); |
743 | partitionSize = be32_to_cpu(partitionSizeTmp); |
744 | loopcount = DIV_ROUND_UP(partitionSize + HEADER_LEN, |
745 | IOCTL_BUF_SIZE); |
746 | for (loopNumber = 0; loopNumber < loopcount; loopNumber++) { |
747 | payload = (struct pm8001_ioctl_payload *)ioctlbuffer; |
748 | payload->wr_length = 1024*16; |
749 | payload->id = 0; |
750 | fwControl = |
751 | (struct fw_control_info *)&payload->func_specific; |
752 | fwControl->len = IOCTL_BUF_SIZE; /* IN */ |
753 | fwControl->size = partitionSize + HEADER_LEN;/* IN */ |
754 | fwControl->retcode = 0;/* OUT */ |
755 | fwControl->offset = loopNumber * IOCTL_BUF_SIZE;/*OUT */ |
756 | |
757 | /* |
758 | * for the last chunk of data in case file size is |
759 | * not even with 4k, load only the rest |
760 | */ |
761 | |
762 | read_buf = (u8 *)pm8001_ha->fw_image->data + sizeRead; |
763 | fc_len = (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE; |
764 | |
765 | if (loopcount - loopNumber == 1 && fc_len) { |
766 | fwControl->len = fc_len; |
767 | memcpy((u8 *)fwControl->buffer, read_buf, fc_len); |
768 | sizeRead += fc_len; |
769 | } else { |
770 | memcpy((u8 *)fwControl->buffer, read_buf, IOCTL_BUF_SIZE); |
771 | sizeRead += IOCTL_BUF_SIZE; |
772 | } |
773 | |
774 | pm8001_ha->nvmd_completion = &completion; |
775 | ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload); |
776 | if (ret) { |
777 | pm8001_ha->fw_status = FAIL_OUT_MEMORY; |
778 | goto out; |
779 | } |
780 | wait_for_completion(&completion); |
781 | if (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS) { |
782 | pm8001_ha->fw_status = fwControl->retcode; |
783 | ret = -EFAULT; |
784 | goto out; |
785 | } |
786 | } |
787 | } |
788 | out: |
789 | kfree(objp: ioctlbuffer); |
790 | return ret; |
791 | } |
792 | static ssize_t pm8001_store_update_fw(struct device *cdev, |
793 | struct device_attribute *attr, |
794 | const char *buf, size_t count) |
795 | { |
796 | struct Scsi_Host *shost = class_to_shost(cdev); |
797 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
798 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
799 | char *cmd_ptr, *filename_ptr; |
800 | int res, i; |
801 | int flash_command = FLASH_CMD_NONE; |
802 | int ret; |
803 | |
804 | if (!capable(CAP_SYS_ADMIN)) |
805 | return -EACCES; |
806 | |
807 | /* this test protects us from running two flash processes at once, |
808 | * so we should start with this test */ |
809 | if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) |
810 | return -EINPROGRESS; |
811 | pm8001_ha->fw_status = FLASH_IN_PROGRESS; |
812 | |
813 | cmd_ptr = kcalloc(n: count, size: 2, GFP_KERNEL); |
814 | if (!cmd_ptr) { |
815 | pm8001_ha->fw_status = FAIL_OUT_MEMORY; |
816 | return -ENOMEM; |
817 | } |
818 | |
819 | filename_ptr = cmd_ptr + count; |
820 | res = sscanf(buf, "%s %s" , cmd_ptr, filename_ptr); |
821 | if (res != 2) { |
822 | pm8001_ha->fw_status = FAIL_PARAMETERS; |
823 | ret = -EINVAL; |
824 | goto out; |
825 | } |
826 | |
827 | for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) { |
828 | if (!memcmp(p: flash_command_table[i].command, |
829 | q: cmd_ptr, strlen(cmd_ptr))) { |
830 | flash_command = flash_command_table[i].code; |
831 | break; |
832 | } |
833 | } |
834 | if (flash_command == FLASH_CMD_NONE) { |
835 | pm8001_ha->fw_status = FAIL_PARAMETERS; |
836 | ret = -EINVAL; |
837 | goto out; |
838 | } |
839 | |
840 | ret = request_firmware(fw: &pm8001_ha->fw_image, |
841 | name: filename_ptr, |
842 | device: pm8001_ha->dev); |
843 | |
844 | if (ret) { |
845 | pm8001_dbg(pm8001_ha, FAIL, |
846 | "Failed to load firmware image file %s, error %d\n" , |
847 | filename_ptr, ret); |
848 | pm8001_ha->fw_status = FAIL_OPEN_BIOS_FILE; |
849 | goto out; |
850 | } |
851 | |
852 | if (FLASH_CMD_UPDATE == flash_command) |
853 | ret = pm8001_update_flash(pm8001_ha); |
854 | else |
855 | ret = pm8001_set_nvmd(pm8001_ha); |
856 | |
857 | release_firmware(fw: pm8001_ha->fw_image); |
858 | out: |
859 | kfree(objp: cmd_ptr); |
860 | |
861 | if (ret) |
862 | return ret; |
863 | |
864 | pm8001_ha->fw_status = FLASH_OK; |
865 | return count; |
866 | } |
867 | |
868 | static ssize_t pm8001_show_update_fw(struct device *cdev, |
869 | struct device_attribute *attr, char *buf) |
870 | { |
871 | int i; |
872 | struct Scsi_Host *shost = class_to_shost(cdev); |
873 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
874 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
875 | |
876 | for (i = 0; flash_error_table[i].err_code != 0; i++) { |
877 | if (flash_error_table[i].err_code == pm8001_ha->fw_status) |
878 | break; |
879 | } |
880 | if (pm8001_ha->fw_status != FLASH_IN_PROGRESS) |
881 | pm8001_ha->fw_status = FLASH_OK; |
882 | |
883 | return snprintf(buf, PAGE_SIZE, fmt: "status=%x %s\n" , |
884 | flash_error_table[i].err_code, |
885 | flash_error_table[i].reason); |
886 | } |
887 | static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUSR|S_IWGRP, |
888 | pm8001_show_update_fw, pm8001_store_update_fw); |
889 | |
890 | static const char *const mpiStateText[] = { |
891 | "MPI is not initialized" , |
892 | "MPI is successfully initialized" , |
893 | "MPI termination is in progress" , |
894 | "MPI initialization failed with error in [31:16]" |
895 | }; |
896 | |
897 | /** |
898 | * ctl_mpi_state_show - controller MPI state check |
899 | * @cdev: pointer to embedded class device |
900 | * @attr: device attribute (unused) |
901 | * @buf: the buffer returned |
902 | * |
903 | * A sysfs 'read-only' shost attribute. |
904 | */ |
905 | static ssize_t ctl_mpi_state_show(struct device *cdev, |
906 | struct device_attribute *attr, char *buf) |
907 | { |
908 | struct Scsi_Host *shost = class_to_shost(cdev); |
909 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
910 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
911 | unsigned int mpidw0; |
912 | |
913 | mpidw0 = pm8001_mr32(addr: pm8001_ha->general_stat_tbl_addr, offset: 0); |
914 | return sysfs_emit(buf, fmt: "%s\n" , mpiStateText[mpidw0 & 0x0003]); |
915 | } |
916 | static DEVICE_ATTR_RO(ctl_mpi_state); |
917 | |
918 | /** |
919 | * ctl_hmi_error_show - controller MPI initialization fails |
920 | * @cdev: pointer to embedded class device |
921 | * @attr: device attribute (unused) |
922 | * @buf: the buffer returned |
923 | * |
924 | * A sysfs 'read-only' shost attribute. |
925 | */ |
926 | static ssize_t ctl_hmi_error_show(struct device *cdev, |
927 | struct device_attribute *attr, char *buf) |
928 | { |
929 | struct Scsi_Host *shost = class_to_shost(cdev); |
930 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
931 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
932 | unsigned int mpidw0; |
933 | |
934 | mpidw0 = pm8001_mr32(addr: pm8001_ha->general_stat_tbl_addr, offset: 0); |
935 | return sysfs_emit(buf, fmt: "0x%08x\n" , (mpidw0 >> 16)); |
936 | } |
937 | static DEVICE_ATTR_RO(ctl_hmi_error); |
938 | |
939 | /** |
940 | * ctl_raae_count_show - controller raae count check |
941 | * @cdev: pointer to embedded class device |
942 | * @attr: device attribute (unused) |
943 | * @buf: the buffer returned |
944 | * |
945 | * A sysfs 'read-only' shost attribute. |
946 | */ |
947 | static ssize_t ctl_raae_count_show(struct device *cdev, |
948 | struct device_attribute *attr, char *buf) |
949 | { |
950 | struct Scsi_Host *shost = class_to_shost(cdev); |
951 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
952 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
953 | unsigned int raaecnt; |
954 | |
955 | raaecnt = pm8001_mr32(addr: pm8001_ha->general_stat_tbl_addr, offset: 12); |
956 | return sysfs_emit(buf, fmt: "0x%08x\n" , raaecnt); |
957 | } |
958 | static DEVICE_ATTR_RO(ctl_raae_count); |
959 | |
960 | /** |
961 | * ctl_iop0_count_show - controller iop0 count check |
962 | * @cdev: pointer to embedded class device |
963 | * @attr: device attribute (unused) |
964 | * @buf: the buffer returned |
965 | * |
966 | * A sysfs 'read-only' shost attribute. |
967 | */ |
968 | static ssize_t ctl_iop0_count_show(struct device *cdev, |
969 | struct device_attribute *attr, char *buf) |
970 | { |
971 | struct Scsi_Host *shost = class_to_shost(cdev); |
972 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
973 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
974 | unsigned int iop0cnt; |
975 | |
976 | iop0cnt = pm8001_mr32(addr: pm8001_ha->general_stat_tbl_addr, offset: 16); |
977 | return sysfs_emit(buf, fmt: "0x%08x\n" , iop0cnt); |
978 | } |
979 | static DEVICE_ATTR_RO(ctl_iop0_count); |
980 | |
981 | /** |
982 | * ctl_iop1_count_show - controller iop1 count check |
983 | * @cdev: pointer to embedded class device |
984 | * @attr: device attribute (unused) |
985 | * @buf: the buffer returned |
986 | * |
987 | * A sysfs 'read-only' shost attribute. |
988 | */ |
989 | static ssize_t ctl_iop1_count_show(struct device *cdev, |
990 | struct device_attribute *attr, char *buf) |
991 | { |
992 | struct Scsi_Host *shost = class_to_shost(cdev); |
993 | struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); |
994 | struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; |
995 | unsigned int iop1cnt; |
996 | |
997 | iop1cnt = pm8001_mr32(addr: pm8001_ha->general_stat_tbl_addr, offset: 20); |
998 | return sysfs_emit(buf, fmt: "0x%08x\n" , iop1cnt); |
999 | |
1000 | } |
1001 | static DEVICE_ATTR_RO(ctl_iop1_count); |
1002 | |
1003 | static struct attribute *pm8001_host_attrs[] = { |
1004 | &dev_attr_interface_rev.attr, |
1005 | &dev_attr_controller_fatal_error.attr, |
1006 | &dev_attr_fw_version.attr, |
1007 | &dev_attr_update_fw.attr, |
1008 | &dev_attr_aap_log.attr, |
1009 | &dev_attr_iop_log.attr, |
1010 | &dev_attr_fatal_log.attr, |
1011 | &dev_attr_non_fatal_log.attr, |
1012 | &dev_attr_non_fatal_count.attr, |
1013 | &dev_attr_gsm_log.attr, |
1014 | &dev_attr_max_out_io.attr, |
1015 | &dev_attr_max_devices.attr, |
1016 | &dev_attr_max_sg_list.attr, |
1017 | &dev_attr_sas_spec_support.attr, |
1018 | &dev_attr_logging_level.attr, |
1019 | &dev_attr_event_log_size.attr, |
1020 | &dev_attr_host_sas_address.attr, |
1021 | &dev_attr_bios_version.attr, |
1022 | &dev_attr_ib_log.attr, |
1023 | &dev_attr_ob_log.attr, |
1024 | &dev_attr_ila_version.attr, |
1025 | &dev_attr_inc_fw_ver.attr, |
1026 | &dev_attr_ctl_mpi_state.attr, |
1027 | &dev_attr_ctl_hmi_error.attr, |
1028 | &dev_attr_ctl_raae_count.attr, |
1029 | &dev_attr_ctl_iop0_count.attr, |
1030 | &dev_attr_ctl_iop1_count.attr, |
1031 | NULL, |
1032 | }; |
1033 | |
1034 | static const struct attribute_group pm8001_host_attr_group = { |
1035 | .attrs = pm8001_host_attrs |
1036 | }; |
1037 | |
1038 | const struct attribute_group *pm8001_host_groups[] = { |
1039 | &pm8001_host_attr_group, |
1040 | NULL |
1041 | }; |
1042 | |