Commit | Line | Data |
---|---|---|
bbbd7f11 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
e1d04c97 LV |
2 | /* |
3 | * Sysfs entries for PCI Error Recovery for PAPR-compliant platform. | |
4 | * Copyright IBM Corporation 2007 | |
5 | * Copyright Linas Vepstas <linas@austin.ibm.com> 2007 | |
6 | * | |
e1d04c97 LV |
7 | * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com> |
8 | */ | |
e6f6390a | 9 | #include <linux/of.h> |
e1d04c97 | 10 | #include <linux/pci.h> |
b56eade5 | 11 | #include <linux/stat.h> |
e1d04c97 LV |
12 | #include <asm/ppc-pci.h> |
13 | #include <asm/pci-bridge.h> | |
e1d04c97 LV |
14 | |
15 | /** | |
29f8bf1b | 16 | * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic |
e1d04c97 | 17 | * @_name: name of file in sysfs directory |
89f51839 | 18 | * @_memb: name of member in struct eeh_dev to access |
e1d04c97 LV |
19 | * @_format: printf format for display |
20 | * | |
21 | * All of the attributes look very similar, so just | |
22 | * auto-gen a cut-n-paste routine to display them. | |
23 | */ | |
24 | #define EEH_SHOW_ATTR(_name,_memb,_format) \ | |
25 | static ssize_t eeh_show_##_name(struct device *dev, \ | |
26 | struct device_attribute *attr, char *buf) \ | |
27 | { \ | |
28 | struct pci_dev *pdev = to_pci_dev(dev); \ | |
44da8edc | 29 | struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); \ |
e1d04c97 | 30 | \ |
44da8edc GS |
31 | if (!edev) \ |
32 | return 0; \ | |
e1d04c97 | 33 | \ |
44da8edc | 34 | return sprintf(buf, _format "\n", edev->_memb); \ |
e1d04c97 | 35 | } \ |
57ad583f | 36 | static DEVICE_ATTR(_name, 0444, eeh_show_##_name, NULL); |
e1d04c97 | 37 | |
44da8edc | 38 | EEH_SHOW_ATTR(eeh_mode, mode, "0x%x"); |
44da8edc | 39 | EEH_SHOW_ATTR(eeh_pe_config_addr, pe_config_addr, "0x%x"); |
e1d04c97 | 40 | |
940376b3 GS |
41 | static ssize_t eeh_pe_state_show(struct device *dev, |
42 | struct device_attribute *attr, char *buf) | |
43 | { | |
44 | struct pci_dev *pdev = to_pci_dev(dev); | |
45 | struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); | |
46 | int state; | |
47 | ||
48 | if (!edev || !edev->pe) | |
49 | return -ENODEV; | |
50 | ||
51 | state = eeh_ops->get_state(edev->pe, NULL); | |
7531473c | 52 | return sprintf(buf, "0x%08x 0x%08x\n", |
940376b3 GS |
53 | state, edev->pe->state); |
54 | } | |
55 | ||
56 | static ssize_t eeh_pe_state_store(struct device *dev, | |
57 | struct device_attribute *attr, | |
58 | const char *buf, size_t count) | |
59 | { | |
60 | struct pci_dev *pdev = to_pci_dev(dev); | |
61 | struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); | |
940376b3 GS |
62 | |
63 | if (!edev || !edev->pe) | |
64 | return -ENODEV; | |
65 | ||
66 | /* Nothing to do if it's not frozen */ | |
67 | if (!(edev->pe->state & EEH_PE_ISOLATED)) | |
68 | return count; | |
69 | ||
188fdea6 | 70 | if (eeh_unfreeze_pe(edev->pe)) |
940376b3 | 71 | return -EIO; |
9ed5ca66 | 72 | eeh_pe_state_clear(edev->pe, EEH_PE_ISOLATED, true); |
940376b3 GS |
73 | |
74 | return count; | |
75 | } | |
76 | ||
77 | static DEVICE_ATTR_RW(eeh_pe_state); | |
78 | ||
4107248c | 79 | #if defined(CONFIG_PCI_IOV) && defined(CONFIG_PPC_PSERIES) |
6ea3df69 BL |
80 | static ssize_t eeh_notify_resume_show(struct device *dev, |
81 | struct device_attribute *attr, char *buf) | |
82 | { | |
83 | struct pci_dev *pdev = to_pci_dev(dev); | |
84 | struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); | |
85 | struct pci_dn *pdn = pci_get_pdn(pdev); | |
86 | ||
87 | if (!edev || !edev->pe) | |
88 | return -ENODEV; | |
89 | ||
6ea3df69 BL |
90 | return sprintf(buf, "%d\n", pdn->last_allow_rc); |
91 | } | |
92 | ||
93 | static ssize_t eeh_notify_resume_store(struct device *dev, | |
94 | struct device_attribute *attr, | |
95 | const char *buf, size_t count) | |
96 | { | |
97 | struct pci_dev *pdev = to_pci_dev(dev); | |
98 | struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); | |
99 | ||
100 | if (!edev || !edev->pe || !eeh_ops->notify_resume) | |
101 | return -ENODEV; | |
102 | ||
8225d543 | 103 | if (eeh_ops->notify_resume(edev)) |
6ea3df69 BL |
104 | return -EIO; |
105 | ||
106 | return count; | |
107 | } | |
108 | static DEVICE_ATTR_RW(eeh_notify_resume); | |
109 | ||
110 | static int eeh_notify_resume_add(struct pci_dev *pdev) | |
111 | { | |
112 | struct device_node *np; | |
113 | int rc = 0; | |
114 | ||
115 | np = pci_device_to_OF_node(pdev->is_physfn ? pdev : pdev->physfn); | |
116 | ||
117 | if (of_property_read_bool(np, "ibm,is-open-sriov-pf")) | |
118 | rc = device_create_file(&pdev->dev, &dev_attr_eeh_notify_resume); | |
119 | ||
120 | return rc; | |
121 | } | |
122 | ||
123 | static void eeh_notify_resume_remove(struct pci_dev *pdev) | |
124 | { | |
125 | struct device_node *np; | |
126 | ||
127 | np = pci_device_to_OF_node(pdev->is_physfn ? pdev : pdev->physfn); | |
128 | ||
129 | if (of_property_read_bool(np, "ibm,is-open-sriov-pf")) | |
130 | device_remove_file(&pdev->dev, &dev_attr_eeh_notify_resume); | |
131 | } | |
132 | #else | |
133 | static inline int eeh_notify_resume_add(struct pci_dev *pdev) { return 0; } | |
134 | static inline void eeh_notify_resume_remove(struct pci_dev *pdev) { } | |
4107248c | 135 | #endif /* CONFIG_PCI_IOV && CONFIG PPC_PSERIES*/ |
6ea3df69 | 136 | |
e1d04c97 LV |
137 | void eeh_sysfs_add_device(struct pci_dev *pdev) |
138 | { | |
ab55d218 | 139 | struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); |
e1d04c97 LV |
140 | int rc=0; |
141 | ||
2213fb14 WY |
142 | if (!eeh_enabled()) |
143 | return; | |
144 | ||
ab55d218 GS |
145 | if (edev && (edev->mode & EEH_DEV_SYSFS)) |
146 | return; | |
147 | ||
e1d04c97 | 148 | rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode); |
e1d04c97 | 149 | rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); |
940376b3 | 150 | rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_state); |
6ea3df69 | 151 | rc += eeh_notify_resume_add(pdev); |
e1d04c97 LV |
152 | |
153 | if (rc) | |
940376b3 | 154 | pr_warn("EEH: Unable to create sysfs entries\n"); |
ab55d218 GS |
155 | else if (edev) |
156 | edev->mode |= EEH_DEV_SYSFS; | |
e1d04c97 LV |
157 | } |
158 | ||
159 | void eeh_sysfs_remove_device(struct pci_dev *pdev) | |
160 | { | |
ab55d218 GS |
161 | struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); |
162 | ||
3489cdc4 OH |
163 | if (!edev) { |
164 | WARN_ON(eeh_enabled()); | |
165 | return; | |
166 | } | |
167 | ||
168 | edev->mode &= ~EEH_DEV_SYSFS; | |
169 | ||
f5c57710 GS |
170 | /* |
171 | * The parent directory might have been removed. We needn't | |
172 | * continue for that case. | |
173 | */ | |
3489cdc4 | 174 | if (!pdev->dev.kobj.sd) |
f5c57710 GS |
175 | return; |
176 | ||
e1d04c97 | 177 | device_remove_file(&pdev->dev, &dev_attr_eeh_mode); |
e1d04c97 | 178 | device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); |
940376b3 | 179 | device_remove_file(&pdev->dev, &dev_attr_eeh_pe_state); |
ab55d218 | 180 | |
6ea3df69 | 181 | eeh_notify_resume_remove(pdev); |
e1d04c97 | 182 | } |