162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright IBM Corp. 2012 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author(s): 662306a36Sopenharmony_ci * Jan Glauber <jang@linux.vnet.ibm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define KMSG_COMPONENT "zpci" 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/stat.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "../../../drivers/pci/pci.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/sclp.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define zpci_attr(name, fmt, member) \ 2162306a36Sopenharmony_cistatic ssize_t name##_show(struct device *dev, \ 2262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 2362306a36Sopenharmony_ci{ \ 2462306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci(to_pci_dev(dev)); \ 2562306a36Sopenharmony_ci \ 2662306a36Sopenharmony_ci return sprintf(buf, fmt, zdev->member); \ 2762306a36Sopenharmony_ci} \ 2862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(name) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cizpci_attr(function_id, "0x%08x\n", fid); 3162306a36Sopenharmony_cizpci_attr(function_handle, "0x%08x\n", fh); 3262306a36Sopenharmony_cizpci_attr(pchid, "0x%04x\n", pchid); 3362306a36Sopenharmony_cizpci_attr(pfgid, "0x%02x\n", pfgid); 3462306a36Sopenharmony_cizpci_attr(vfn, "0x%04x\n", vfn); 3562306a36Sopenharmony_cizpci_attr(pft, "0x%02x\n", pft); 3662306a36Sopenharmony_cizpci_attr(port, "%d\n", port); 3762306a36Sopenharmony_cizpci_attr(uid, "0x%x\n", uid); 3862306a36Sopenharmony_cizpci_attr(segment0, "0x%02x\n", pfip[0]); 3962306a36Sopenharmony_cizpci_attr(segment1, "0x%02x\n", pfip[1]); 4062306a36Sopenharmony_cizpci_attr(segment2, "0x%02x\n", pfip[2]); 4162306a36Sopenharmony_cizpci_attr(segment3, "0x%02x\n", pfip[3]); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic ssize_t mio_enabled_show(struct device *dev, 4462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci(to_pci_dev(dev)); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return sprintf(buf, zpci_use_mio(zdev) ? "1\n" : "0\n"); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(mio_enabled); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic ssize_t recover_store(struct device *dev, struct device_attribute *attr, 5362306a36Sopenharmony_ci const char *buf, size_t count) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct kernfs_node *kn; 5662306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 5762306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci(pdev); 5862306a36Sopenharmony_ci int ret = 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* Can't use device_remove_self() here as that would lead us to lock 6162306a36Sopenharmony_ci * the pci_rescan_remove_lock while holding the device' kernfs lock. 6262306a36Sopenharmony_ci * This would create a possible deadlock with disable_slot() which is 6362306a36Sopenharmony_ci * not directly protected by the device' kernfs lock but takes it 6462306a36Sopenharmony_ci * during the device removal which happens under 6562306a36Sopenharmony_ci * pci_rescan_remove_lock. 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * This is analogous to sdev_store_delete() in 6862306a36Sopenharmony_ci * drivers/scsi/scsi_sysfs.c 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci kn = sysfs_break_active_protection(&dev->kobj, &attr->attr); 7162306a36Sopenharmony_ci WARN_ON_ONCE(!kn); 7262306a36Sopenharmony_ci /* device_remove_file() serializes concurrent calls ignoring all but 7362306a36Sopenharmony_ci * the first 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci device_remove_file(dev, attr); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* A concurrent call to recover_store() may slip between 7862306a36Sopenharmony_ci * sysfs_break_active_protection() and the sysfs file removal. 7962306a36Sopenharmony_ci * Once it unblocks from pci_lock_rescan_remove() the original pdev 8062306a36Sopenharmony_ci * will already be removed. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci pci_lock_rescan_remove(); 8362306a36Sopenharmony_ci if (pci_dev_is_added(pdev)) { 8462306a36Sopenharmony_ci pci_stop_and_remove_bus_device(pdev); 8562306a36Sopenharmony_ci if (zdev->dma_table) { 8662306a36Sopenharmony_ci ret = zpci_dma_exit_device(zdev); 8762306a36Sopenharmony_ci if (ret) 8862306a36Sopenharmony_ci goto out; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (zdev_enabled(zdev)) { 9262306a36Sopenharmony_ci ret = zpci_disable_device(zdev); 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * Due to a z/VM vs LPAR inconsistency in the error 9562306a36Sopenharmony_ci * state the FH may indicate an enabled device but 9662306a36Sopenharmony_ci * disable says the device is already disabled don't 9762306a36Sopenharmony_ci * treat it as an error here. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci if (ret == -EINVAL) 10062306a36Sopenharmony_ci ret = 0; 10162306a36Sopenharmony_ci if (ret) 10262306a36Sopenharmony_ci goto out; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci ret = zpci_enable_device(zdev); 10662306a36Sopenharmony_ci if (ret) 10762306a36Sopenharmony_ci goto out; 10862306a36Sopenharmony_ci ret = zpci_dma_init_device(zdev); 10962306a36Sopenharmony_ci if (ret) { 11062306a36Sopenharmony_ci zpci_disable_device(zdev); 11162306a36Sopenharmony_ci goto out; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci pci_rescan_bus(zdev->zbus->bus); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ciout: 11662306a36Sopenharmony_ci pci_unlock_rescan_remove(); 11762306a36Sopenharmony_ci if (kn) 11862306a36Sopenharmony_ci sysfs_unbreak_active_protection(kn); 11962306a36Sopenharmony_ci return ret ? ret : count; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_cistatic DEVICE_ATTR_WO(recover); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic ssize_t util_string_read(struct file *filp, struct kobject *kobj, 12462306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, 12562306a36Sopenharmony_ci loff_t off, size_t count) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 12862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 12962306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci(pdev); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return memory_read_from_buffer(buf, count, &off, zdev->util_str, 13262306a36Sopenharmony_ci sizeof(zdev->util_str)); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_cistatic BIN_ATTR_RO(util_string, CLP_UTIL_STR_LEN); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic ssize_t report_error_write(struct file *filp, struct kobject *kobj, 13762306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, 13862306a36Sopenharmony_ci loff_t off, size_t count) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct zpci_report_error_header *report = (void *) buf; 14162306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 14262306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 14362306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci(pdev); 14462306a36Sopenharmony_ci int ret; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (off || (count < sizeof(*report))) 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = sclp_pci_report(report, zdev->fh, zdev->fid); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return ret ? ret : count; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_cistatic BIN_ATTR(report_error, S_IWUSR, NULL, report_error_write, PAGE_SIZE); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic ssize_t uid_is_unique_show(struct device *dev, 15662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", zpci_unique_uid ? 1 : 0); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(uid_is_unique); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#ifndef CONFIG_DMI 16362306a36Sopenharmony_ci/* analogous to smbios index */ 16462306a36Sopenharmony_cistatic ssize_t index_show(struct device *dev, 16562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci(to_pci_dev(dev)); 16862306a36Sopenharmony_ci u32 index = ~0; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (zpci_unique_uid) 17162306a36Sopenharmony_ci index = zdev->uid; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", index); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(index); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic umode_t zpci_index_is_visible(struct kobject *kobj, 17862306a36Sopenharmony_ci struct attribute *attr, int n) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci return zpci_unique_uid ? attr->mode : 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic struct attribute *zpci_ident_attrs[] = { 18462306a36Sopenharmony_ci &dev_attr_index.attr, 18562306a36Sopenharmony_ci NULL, 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic struct attribute_group zpci_ident_attr_group = { 18962306a36Sopenharmony_ci .attrs = zpci_ident_attrs, 19062306a36Sopenharmony_ci .is_visible = zpci_index_is_visible, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci#endif 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic struct bin_attribute *zpci_bin_attrs[] = { 19562306a36Sopenharmony_ci &bin_attr_util_string, 19662306a36Sopenharmony_ci &bin_attr_report_error, 19762306a36Sopenharmony_ci NULL, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic struct attribute *zpci_dev_attrs[] = { 20162306a36Sopenharmony_ci &dev_attr_function_id.attr, 20262306a36Sopenharmony_ci &dev_attr_function_handle.attr, 20362306a36Sopenharmony_ci &dev_attr_pchid.attr, 20462306a36Sopenharmony_ci &dev_attr_pfgid.attr, 20562306a36Sopenharmony_ci &dev_attr_pft.attr, 20662306a36Sopenharmony_ci &dev_attr_port.attr, 20762306a36Sopenharmony_ci &dev_attr_vfn.attr, 20862306a36Sopenharmony_ci &dev_attr_uid.attr, 20962306a36Sopenharmony_ci &dev_attr_recover.attr, 21062306a36Sopenharmony_ci &dev_attr_mio_enabled.attr, 21162306a36Sopenharmony_ci &dev_attr_uid_is_unique.attr, 21262306a36Sopenharmony_ci NULL, 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic struct attribute_group zpci_attr_group = { 21662306a36Sopenharmony_ci .attrs = zpci_dev_attrs, 21762306a36Sopenharmony_ci .bin_attrs = zpci_bin_attrs, 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic struct attribute *pfip_attrs[] = { 22162306a36Sopenharmony_ci &dev_attr_segment0.attr, 22262306a36Sopenharmony_ci &dev_attr_segment1.attr, 22362306a36Sopenharmony_ci &dev_attr_segment2.attr, 22462306a36Sopenharmony_ci &dev_attr_segment3.attr, 22562306a36Sopenharmony_ci NULL, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_cistatic struct attribute_group pfip_attr_group = { 22862306a36Sopenharmony_ci .name = "pfip", 22962306a36Sopenharmony_ci .attrs = pfip_attrs, 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ciconst struct attribute_group *zpci_attr_groups[] = { 23362306a36Sopenharmony_ci &zpci_attr_group, 23462306a36Sopenharmony_ci &pfip_attr_group, 23562306a36Sopenharmony_ci#ifndef CONFIG_DMI 23662306a36Sopenharmony_ci &zpci_ident_attr_group, 23762306a36Sopenharmony_ci#endif 23862306a36Sopenharmony_ci NULL, 23962306a36Sopenharmony_ci}; 240