1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * QLogic iSCSI HBA Driver |
4 | * Copyright (c) 2003-2013 QLogic Corporation |
5 | */ |
6 | |
7 | #include "ql4_def.h" |
8 | #include "ql4_glbl.h" |
9 | #include "ql4_dbg.h" |
10 | |
11 | static ssize_t |
12 | qla4_8xxx_sysfs_read_fw_dump(struct file *filep, struct kobject *kobj, |
13 | struct bin_attribute *ba, char *buf, loff_t off, |
14 | size_t count) |
15 | { |
16 | struct scsi_qla_host *ha = to_qla_host(shost: dev_to_shost(container_of(kobj, |
17 | struct device, kobj))); |
18 | |
19 | if (is_qla40XX(ha)) |
20 | return -EINVAL; |
21 | |
22 | if (!test_bit(AF_82XX_DUMP_READING, &ha->flags)) |
23 | return 0; |
24 | |
25 | return memory_read_from_buffer(to: buf, count, ppos: &off, from: ha->fw_dump, |
26 | available: ha->fw_dump_size); |
27 | } |
28 | |
29 | static ssize_t |
30 | qla4_8xxx_sysfs_write_fw_dump(struct file *filep, struct kobject *kobj, |
31 | struct bin_attribute *ba, char *buf, loff_t off, |
32 | size_t count) |
33 | { |
34 | struct scsi_qla_host *ha = to_qla_host(shost: dev_to_shost(container_of(kobj, |
35 | struct device, kobj))); |
36 | uint32_t dev_state; |
37 | long reading; |
38 | int ret = 0; |
39 | |
40 | if (is_qla40XX(ha)) |
41 | return -EINVAL; |
42 | |
43 | if (off != 0) |
44 | return ret; |
45 | |
46 | buf[1] = 0; |
47 | ret = kstrtol(s: buf, base: 10, res: &reading); |
48 | if (ret) { |
49 | ql4_printk(KERN_ERR, ha, "%s: Invalid input. Return err %d\n" , |
50 | __func__, ret); |
51 | return ret; |
52 | } |
53 | |
54 | switch (reading) { |
55 | case 0: |
56 | /* clear dump collection flags */ |
57 | if (test_and_clear_bit(AF_82XX_DUMP_READING, addr: &ha->flags)) { |
58 | clear_bit(AF_82XX_FW_DUMPED, addr: &ha->flags); |
59 | /* Reload minidump template */ |
60 | qla4xxx_alloc_fw_dump(ha); |
61 | DEBUG2(ql4_printk(KERN_INFO, ha, |
62 | "Firmware template reloaded\n" )); |
63 | } |
64 | break; |
65 | case 1: |
66 | /* Set flag to read dump */ |
67 | if (test_bit(AF_82XX_FW_DUMPED, &ha->flags) && |
68 | !test_bit(AF_82XX_DUMP_READING, &ha->flags)) { |
69 | set_bit(AF_82XX_DUMP_READING, addr: &ha->flags); |
70 | DEBUG2(ql4_printk(KERN_INFO, ha, |
71 | "Raw firmware dump ready for read on (%ld).\n" , |
72 | ha->host_no)); |
73 | } |
74 | break; |
75 | case 2: |
76 | /* Reset HBA and collect FW dump */ |
77 | ha->isp_ops->idc_lock(ha); |
78 | dev_state = qla4_8xxx_rd_direct(ha, crb_reg: QLA8XXX_CRB_DEV_STATE); |
79 | if (dev_state == QLA8XXX_DEV_READY) { |
80 | ql4_printk(KERN_INFO, ha, "%s: Setting Need reset\n" , |
81 | __func__); |
82 | qla4_8xxx_wr_direct(ha, crb_reg: QLA8XXX_CRB_DEV_STATE, |
83 | QLA8XXX_DEV_NEED_RESET); |
84 | if (is_qla8022(ha) || |
85 | ((is_qla8032(ha) || is_qla8042(ha)) && |
86 | qla4_83xx_can_perform_reset(ha))) { |
87 | set_bit(AF_8XXX_RST_OWNER, addr: &ha->flags); |
88 | set_bit(AF_FW_RECOVERY, addr: &ha->flags); |
89 | ql4_printk(KERN_INFO, ha, "%s: Reset owner is 0x%x\n" , |
90 | __func__, ha->func_num); |
91 | } |
92 | } else |
93 | ql4_printk(KERN_INFO, ha, |
94 | "%s: Reset not performed as device state is 0x%x\n" , |
95 | __func__, dev_state); |
96 | |
97 | ha->isp_ops->idc_unlock(ha); |
98 | break; |
99 | default: |
100 | /* do nothing */ |
101 | break; |
102 | } |
103 | |
104 | return count; |
105 | } |
106 | |
107 | static struct bin_attribute sysfs_fw_dump_attr = { |
108 | .attr = { |
109 | .name = "fw_dump" , |
110 | .mode = S_IRUSR | S_IWUSR, |
111 | }, |
112 | .size = 0, |
113 | .read = qla4_8xxx_sysfs_read_fw_dump, |
114 | .write = qla4_8xxx_sysfs_write_fw_dump, |
115 | }; |
116 | |
117 | static struct sysfs_entry { |
118 | char *name; |
119 | struct bin_attribute *attr; |
120 | } bin_file_entries[] = { |
121 | { "fw_dump" , &sysfs_fw_dump_attr }, |
122 | { NULL }, |
123 | }; |
124 | |
125 | void qla4_8xxx_alloc_sysfs_attr(struct scsi_qla_host *ha) |
126 | { |
127 | struct Scsi_Host *host = ha->host; |
128 | struct sysfs_entry *iter; |
129 | int ret; |
130 | |
131 | for (iter = bin_file_entries; iter->name; iter++) { |
132 | ret = sysfs_create_bin_file(kobj: &host->shost_gendev.kobj, |
133 | attr: iter->attr); |
134 | if (ret) |
135 | ql4_printk(KERN_ERR, ha, |
136 | "Unable to create sysfs %s binary attribute (%d).\n" , |
137 | iter->name, ret); |
138 | } |
139 | } |
140 | |
141 | void qla4_8xxx_free_sysfs_attr(struct scsi_qla_host *ha) |
142 | { |
143 | struct Scsi_Host *host = ha->host; |
144 | struct sysfs_entry *iter; |
145 | |
146 | for (iter = bin_file_entries; iter->name; iter++) |
147 | sysfs_remove_bin_file(kobj: &host->shost_gendev.kobj, |
148 | attr: iter->attr); |
149 | } |
150 | |
151 | /* Scsi_Host attributes. */ |
152 | static ssize_t |
153 | qla4xxx_fw_version_show(struct device *dev, |
154 | struct device_attribute *attr, char *buf) |
155 | { |
156 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
157 | |
158 | if (is_qla80XX(ha)) |
159 | return snprintf(buf, PAGE_SIZE, fmt: "%d.%02d.%02d (%x)\n" , |
160 | ha->fw_info.fw_major, ha->fw_info.fw_minor, |
161 | ha->fw_info.fw_patch, ha->fw_info.fw_build); |
162 | else |
163 | return snprintf(buf, PAGE_SIZE, fmt: "%d.%02d.%02d.%02d\n" , |
164 | ha->fw_info.fw_major, ha->fw_info.fw_minor, |
165 | ha->fw_info.fw_patch, ha->fw_info.fw_build); |
166 | } |
167 | |
168 | static ssize_t |
169 | qla4xxx_serial_num_show(struct device *dev, struct device_attribute *attr, |
170 | char *buf) |
171 | { |
172 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
173 | return snprintf(buf, PAGE_SIZE, fmt: "%s\n" , ha->serial_number); |
174 | } |
175 | |
176 | static ssize_t |
177 | qla4xxx_iscsi_version_show(struct device *dev, struct device_attribute *attr, |
178 | char *buf) |
179 | { |
180 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
181 | return snprintf(buf, PAGE_SIZE, fmt: "%d.%02d\n" , ha->fw_info.iscsi_major, |
182 | ha->fw_info.iscsi_minor); |
183 | } |
184 | |
185 | static ssize_t |
186 | qla4xxx_optrom_version_show(struct device *dev, struct device_attribute *attr, |
187 | char *buf) |
188 | { |
189 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
190 | return snprintf(buf, PAGE_SIZE, fmt: "%d.%02d.%02d.%02d\n" , |
191 | ha->fw_info.bootload_major, ha->fw_info.bootload_minor, |
192 | ha->fw_info.bootload_patch, ha->fw_info.bootload_build); |
193 | } |
194 | |
195 | static ssize_t |
196 | qla4xxx_board_id_show(struct device *dev, struct device_attribute *attr, |
197 | char *buf) |
198 | { |
199 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
200 | return snprintf(buf, PAGE_SIZE, fmt: "0x%08X\n" , ha->board_id); |
201 | } |
202 | |
203 | static ssize_t |
204 | qla4xxx_fw_state_show(struct device *dev, struct device_attribute *attr, |
205 | char *buf) |
206 | { |
207 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
208 | |
209 | qla4xxx_get_firmware_state(ha); |
210 | return snprintf(buf, PAGE_SIZE, fmt: "0x%08X%8X\n" , ha->firmware_state, |
211 | ha->addl_fw_state); |
212 | } |
213 | |
214 | static ssize_t |
215 | qla4xxx_phy_port_cnt_show(struct device *dev, struct device_attribute *attr, |
216 | char *buf) |
217 | { |
218 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
219 | |
220 | if (is_qla40XX(ha)) |
221 | return -ENOSYS; |
222 | |
223 | return snprintf(buf, PAGE_SIZE, fmt: "0x%04X\n" , ha->phy_port_cnt); |
224 | } |
225 | |
226 | static ssize_t |
227 | qla4xxx_phy_port_num_show(struct device *dev, struct device_attribute *attr, |
228 | char *buf) |
229 | { |
230 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
231 | |
232 | if (is_qla40XX(ha)) |
233 | return -ENOSYS; |
234 | |
235 | return snprintf(buf, PAGE_SIZE, fmt: "0x%04X\n" , ha->phy_port_num); |
236 | } |
237 | |
238 | static ssize_t |
239 | qla4xxx_iscsi_func_cnt_show(struct device *dev, struct device_attribute *attr, |
240 | char *buf) |
241 | { |
242 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
243 | |
244 | if (is_qla40XX(ha)) |
245 | return -ENOSYS; |
246 | |
247 | return snprintf(buf, PAGE_SIZE, fmt: "0x%04X\n" , ha->iscsi_pci_func_cnt); |
248 | } |
249 | |
250 | static ssize_t |
251 | qla4xxx_hba_model_show(struct device *dev, struct device_attribute *attr, |
252 | char *buf) |
253 | { |
254 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
255 | |
256 | return snprintf(buf, PAGE_SIZE, fmt: "%s\n" , ha->model_name); |
257 | } |
258 | |
259 | static ssize_t |
260 | qla4xxx_fw_timestamp_show(struct device *dev, struct device_attribute *attr, |
261 | char *buf) |
262 | { |
263 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
264 | return snprintf(buf, PAGE_SIZE, fmt: "%s %s\n" , ha->fw_info.fw_build_date, |
265 | ha->fw_info.fw_build_time); |
266 | } |
267 | |
268 | static ssize_t |
269 | qla4xxx_fw_build_user_show(struct device *dev, struct device_attribute *attr, |
270 | char *buf) |
271 | { |
272 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
273 | return snprintf(buf, PAGE_SIZE, fmt: "%s\n" , ha->fw_info.fw_build_user); |
274 | } |
275 | |
276 | static ssize_t |
277 | qla4xxx_fw_ext_timestamp_show(struct device *dev, struct device_attribute *attr, |
278 | char *buf) |
279 | { |
280 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
281 | return snprintf(buf, PAGE_SIZE, fmt: "%s\n" , ha->fw_info.extended_timestamp); |
282 | } |
283 | |
284 | static ssize_t |
285 | qla4xxx_fw_load_src_show(struct device *dev, struct device_attribute *attr, |
286 | char *buf) |
287 | { |
288 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
289 | char *load_src = NULL; |
290 | |
291 | switch (ha->fw_info.fw_load_source) { |
292 | case 1: |
293 | load_src = "Flash Primary" ; |
294 | break; |
295 | case 2: |
296 | load_src = "Flash Secondary" ; |
297 | break; |
298 | case 3: |
299 | load_src = "Host Download" ; |
300 | break; |
301 | } |
302 | |
303 | return snprintf(buf, PAGE_SIZE, fmt: "%s\n" , load_src); |
304 | } |
305 | |
306 | static ssize_t |
307 | qla4xxx_fw_uptime_show(struct device *dev, struct device_attribute *attr, |
308 | char *buf) |
309 | { |
310 | struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); |
311 | qla4xxx_about_firmware(ha); |
312 | return snprintf(buf, PAGE_SIZE, fmt: "%u.%u secs\n" , ha->fw_uptime_secs, |
313 | ha->fw_uptime_msecs); |
314 | } |
315 | |
316 | static DEVICE_ATTR(fw_version, S_IRUGO, qla4xxx_fw_version_show, NULL); |
317 | static DEVICE_ATTR(serial_num, S_IRUGO, qla4xxx_serial_num_show, NULL); |
318 | static DEVICE_ATTR(iscsi_version, S_IRUGO, qla4xxx_iscsi_version_show, NULL); |
319 | static DEVICE_ATTR(optrom_version, S_IRUGO, qla4xxx_optrom_version_show, NULL); |
320 | static DEVICE_ATTR(board_id, S_IRUGO, qla4xxx_board_id_show, NULL); |
321 | static DEVICE_ATTR(fw_state, S_IRUGO, qla4xxx_fw_state_show, NULL); |
322 | static DEVICE_ATTR(phy_port_cnt, S_IRUGO, qla4xxx_phy_port_cnt_show, NULL); |
323 | static DEVICE_ATTR(phy_port_num, S_IRUGO, qla4xxx_phy_port_num_show, NULL); |
324 | static DEVICE_ATTR(iscsi_func_cnt, S_IRUGO, qla4xxx_iscsi_func_cnt_show, NULL); |
325 | static DEVICE_ATTR(hba_model, S_IRUGO, qla4xxx_hba_model_show, NULL); |
326 | static DEVICE_ATTR(fw_timestamp, S_IRUGO, qla4xxx_fw_timestamp_show, NULL); |
327 | static DEVICE_ATTR(fw_build_user, S_IRUGO, qla4xxx_fw_build_user_show, NULL); |
328 | static DEVICE_ATTR(fw_ext_timestamp, S_IRUGO, qla4xxx_fw_ext_timestamp_show, |
329 | NULL); |
330 | static DEVICE_ATTR(fw_load_src, S_IRUGO, qla4xxx_fw_load_src_show, NULL); |
331 | static DEVICE_ATTR(fw_uptime, S_IRUGO, qla4xxx_fw_uptime_show, NULL); |
332 | |
333 | static struct attribute *qla4xxx_host_attrs[] = { |
334 | &dev_attr_fw_version.attr, |
335 | &dev_attr_serial_num.attr, |
336 | &dev_attr_iscsi_version.attr, |
337 | &dev_attr_optrom_version.attr, |
338 | &dev_attr_board_id.attr, |
339 | &dev_attr_fw_state.attr, |
340 | &dev_attr_phy_port_cnt.attr, |
341 | &dev_attr_phy_port_num.attr, |
342 | &dev_attr_iscsi_func_cnt.attr, |
343 | &dev_attr_hba_model.attr, |
344 | &dev_attr_fw_timestamp.attr, |
345 | &dev_attr_fw_build_user.attr, |
346 | &dev_attr_fw_ext_timestamp.attr, |
347 | &dev_attr_fw_load_src.attr, |
348 | &dev_attr_fw_uptime.attr, |
349 | NULL, |
350 | }; |
351 | |
352 | static const struct attribute_group qla4xxx_host_attr_group = { |
353 | .attrs = qla4xxx_host_attrs |
354 | }; |
355 | |
356 | const struct attribute_group *qla4xxx_host_groups[] = { |
357 | &qla4xxx_host_attr_group, |
358 | NULL |
359 | }; |
360 | |