diff options
author | Wong Vee Khee <vee.khee.wong@ni.com> | 2017-06-01 05:43:06 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-06-19 17:54:53 -0400 |
commit | 56c1af4606f04048e3ae9ab2027a708b9684ff37 (patch) | |
tree | 4797daa24a91f388a42b95c1cd5568507ca1c1e7 | |
parent | 99b3c58f7ba7fae801e501b45c5fcf6e08d9247f (diff) |
PCI: Add sysfs max_link_speed/width, current_link_speed/width, etc
Expose PCIe bridges attributes such as secondary bus number, subordinate
bus number, max link speed and link width, current link speed and link
width via sysfs in /sys/bus/pci/devices/...
This information is available via lspci, but that requires root privilege.
Signed-off-by: Wong Vee Khee <vee.khee.wong@ni.com>
Signed-off-by: Hui Chun Ong <hui.chun.ong@ni.com>
[bhelgaas: changelog, return errors early to unindent usual case, return
errors with same style throughout]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r-- | drivers/pci/pci-sysfs.c | 199 | ||||
-rw-r--r-- | include/uapi/linux/pci_regs.h | 1 |
2 files changed, 196 insertions, 4 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 31e99613a12e..a3537cf58a20 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
@@ -154,6 +154,129 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr, | |||
154 | } | 154 | } |
155 | static DEVICE_ATTR_RO(resource); | 155 | static DEVICE_ATTR_RO(resource); |
156 | 156 | ||
157 | static ssize_t max_link_speed_show(struct device *dev, | ||
158 | struct device_attribute *attr, char *buf) | ||
159 | { | ||
160 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
161 | u32 linkcap; | ||
162 | int err; | ||
163 | const char *speed; | ||
164 | |||
165 | err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap); | ||
166 | if (err) | ||
167 | return -EINVAL; | ||
168 | |||
169 | switch (linkcap & PCI_EXP_LNKCAP_SLS) { | ||
170 | case PCI_EXP_LNKCAP_SLS_8_0GB: | ||
171 | speed = "8 GT/s"; | ||
172 | break; | ||
173 | case PCI_EXP_LNKCAP_SLS_5_0GB: | ||
174 | speed = "5 GT/s"; | ||
175 | break; | ||
176 | case PCI_EXP_LNKCAP_SLS_2_5GB: | ||
177 | speed = "2.5 GT/s"; | ||
178 | break; | ||
179 | default: | ||
180 | speed = "Unknown speed"; | ||
181 | } | ||
182 | |||
183 | return sprintf(buf, "%s\n", speed); | ||
184 | } | ||
185 | static DEVICE_ATTR_RO(max_link_speed); | ||
186 | |||
187 | static ssize_t max_link_width_show(struct device *dev, | ||
188 | struct device_attribute *attr, char *buf) | ||
189 | { | ||
190 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
191 | u32 linkcap; | ||
192 | int err; | ||
193 | |||
194 | err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap); | ||
195 | if (err) | ||
196 | return -EINVAL; | ||
197 | |||
198 | return sprintf(buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4); | ||
199 | } | ||
200 | static DEVICE_ATTR_RO(max_link_width); | ||
201 | |||
202 | static ssize_t current_link_speed_show(struct device *dev, | ||
203 | struct device_attribute *attr, char *buf) | ||
204 | { | ||
205 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
206 | u16 linkstat; | ||
207 | int err; | ||
208 | const char *speed; | ||
209 | |||
210 | err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat); | ||
211 | if (err) | ||
212 | return -EINVAL; | ||
213 | |||
214 | switch (linkstat & PCI_EXP_LNKSTA_CLS) { | ||
215 | case PCI_EXP_LNKSTA_CLS_8_0GB: | ||
216 | speed = "8 GT/s"; | ||
217 | break; | ||
218 | case PCI_EXP_LNKSTA_CLS_5_0GB: | ||
219 | speed = "5 GT/s"; | ||
220 | break; | ||
221 | case PCI_EXP_LNKSTA_CLS_2_5GB: | ||
222 | speed = "2.5 GT/s"; | ||
223 | break; | ||
224 | default: | ||
225 | speed = "Unknown speed"; | ||
226 | } | ||
227 | |||
228 | return sprintf(buf, "%s\n", speed); | ||
229 | } | ||
230 | static DEVICE_ATTR_RO(current_link_speed); | ||
231 | |||
232 | static ssize_t current_link_width_show(struct device *dev, | ||
233 | struct device_attribute *attr, char *buf) | ||
234 | { | ||
235 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
236 | u16 linkstat; | ||
237 | int err; | ||
238 | |||
239 | err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat); | ||
240 | if (err) | ||
241 | return -EINVAL; | ||
242 | |||
243 | return sprintf(buf, "%u\n", | ||
244 | (linkstat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT); | ||
245 | } | ||
246 | static DEVICE_ATTR_RO(current_link_width); | ||
247 | |||
248 | static ssize_t secondary_bus_number_show(struct device *dev, | ||
249 | struct device_attribute *attr, | ||
250 | char *buf) | ||
251 | { | ||
252 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
253 | u8 sec_bus; | ||
254 | int err; | ||
255 | |||
256 | err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, &sec_bus); | ||
257 | if (err) | ||
258 | return -EINVAL; | ||
259 | |||
260 | return sprintf(buf, "%u\n", sec_bus); | ||
261 | } | ||
262 | static DEVICE_ATTR_RO(secondary_bus_number); | ||
263 | |||
264 | static ssize_t subordinate_bus_number_show(struct device *dev, | ||
265 | struct device_attribute *attr, | ||
266 | char *buf) | ||
267 | { | ||
268 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
269 | u8 sub_bus; | ||
270 | int err; | ||
271 | |||
272 | err = pci_read_config_byte(pci_dev, PCI_SUBORDINATE_BUS, &sub_bus); | ||
273 | if (err) | ||
274 | return -EINVAL; | ||
275 | |||
276 | return sprintf(buf, "%u\n", sub_bus); | ||
277 | } | ||
278 | static DEVICE_ATTR_RO(subordinate_bus_number); | ||
279 | |||
157 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, | 280 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, |
158 | char *buf) | 281 | char *buf) |
159 | { | 282 | { |
@@ -629,12 +752,17 @@ static struct attribute *pci_dev_attrs[] = { | |||
629 | NULL, | 752 | NULL, |
630 | }; | 753 | }; |
631 | 754 | ||
632 | static const struct attribute_group pci_dev_group = { | 755 | static struct attribute *pci_bridge_attrs[] = { |
633 | .attrs = pci_dev_attrs, | 756 | &dev_attr_subordinate_bus_number.attr, |
757 | &dev_attr_secondary_bus_number.attr, | ||
758 | NULL, | ||
634 | }; | 759 | }; |
635 | 760 | ||
636 | const struct attribute_group *pci_dev_groups[] = { | 761 | static struct attribute *pcie_dev_attrs[] = { |
637 | &pci_dev_group, | 762 | &dev_attr_current_link_speed.attr, |
763 | &dev_attr_current_link_width.attr, | ||
764 | &dev_attr_max_link_width.attr, | ||
765 | &dev_attr_max_link_speed.attr, | ||
638 | NULL, | 766 | NULL, |
639 | }; | 767 | }; |
640 | 768 | ||
@@ -1557,6 +1685,57 @@ static umode_t pci_dev_hp_attrs_are_visible(struct kobject *kobj, | |||
1557 | return a->mode; | 1685 | return a->mode; |
1558 | } | 1686 | } |
1559 | 1687 | ||
1688 | static umode_t pci_bridge_attrs_are_visible(struct kobject *kobj, | ||
1689 | struct attribute *a, int n) | ||
1690 | { | ||
1691 | struct device *dev = kobj_to_dev(kobj); | ||
1692 | struct pci_dev *pdev = to_pci_dev(dev); | ||
1693 | |||
1694 | if (pci_is_bridge(pdev)) | ||
1695 | return a->mode; | ||
1696 | |||
1697 | return 0; | ||
1698 | } | ||
1699 | |||
1700 | static umode_t pcie_dev_attrs_are_visible(struct kobject *kobj, | ||
1701 | struct attribute *a, int n) | ||
1702 | { | ||
1703 | struct device *dev = kobj_to_dev(kobj); | ||
1704 | struct pci_dev *pdev = to_pci_dev(dev); | ||
1705 | |||
1706 | if (pci_is_pcie(pdev)) | ||
1707 | return a->mode; | ||
1708 | |||
1709 | return 0; | ||
1710 | } | ||
1711 | |||
1712 | static const struct attribute_group pci_dev_group = { | ||
1713 | .attrs = pci_dev_attrs, | ||
1714 | }; | ||
1715 | |||
1716 | const struct attribute_group *pci_dev_groups[] = { | ||
1717 | &pci_dev_group, | ||
1718 | NULL, | ||
1719 | }; | ||
1720 | |||
1721 | static const struct attribute_group pci_bridge_group = { | ||
1722 | .attrs = pci_bridge_attrs, | ||
1723 | }; | ||
1724 | |||
1725 | const struct attribute_group *pci_bridge_groups[] = { | ||
1726 | &pci_bridge_group, | ||
1727 | NULL, | ||
1728 | }; | ||
1729 | |||
1730 | static const struct attribute_group pcie_dev_group = { | ||
1731 | .attrs = pcie_dev_attrs, | ||
1732 | }; | ||
1733 | |||
1734 | const struct attribute_group *pcie_dev_groups[] = { | ||
1735 | &pcie_dev_group, | ||
1736 | NULL, | ||
1737 | }; | ||
1738 | |||
1560 | static struct attribute_group pci_dev_hp_attr_group = { | 1739 | static struct attribute_group pci_dev_hp_attr_group = { |
1561 | .attrs = pci_dev_hp_attrs, | 1740 | .attrs = pci_dev_hp_attrs, |
1562 | .is_visible = pci_dev_hp_attrs_are_visible, | 1741 | .is_visible = pci_dev_hp_attrs_are_visible, |
@@ -1592,12 +1771,24 @@ static struct attribute_group pci_dev_attr_group = { | |||
1592 | .is_visible = pci_dev_attrs_are_visible, | 1771 | .is_visible = pci_dev_attrs_are_visible, |
1593 | }; | 1772 | }; |
1594 | 1773 | ||
1774 | static struct attribute_group pci_bridge_attr_group = { | ||
1775 | .attrs = pci_bridge_attrs, | ||
1776 | .is_visible = pci_bridge_attrs_are_visible, | ||
1777 | }; | ||
1778 | |||
1779 | static struct attribute_group pcie_dev_attr_group = { | ||
1780 | .attrs = pcie_dev_attrs, | ||
1781 | .is_visible = pcie_dev_attrs_are_visible, | ||
1782 | }; | ||
1783 | |||
1595 | static const struct attribute_group *pci_dev_attr_groups[] = { | 1784 | static const struct attribute_group *pci_dev_attr_groups[] = { |
1596 | &pci_dev_attr_group, | 1785 | &pci_dev_attr_group, |
1597 | &pci_dev_hp_attr_group, | 1786 | &pci_dev_hp_attr_group, |
1598 | #ifdef CONFIG_PCI_IOV | 1787 | #ifdef CONFIG_PCI_IOV |
1599 | &sriov_dev_attr_group, | 1788 | &sriov_dev_attr_group, |
1600 | #endif | 1789 | #endif |
1790 | &pci_bridge_attr_group, | ||
1791 | &pcie_dev_attr_group, | ||
1601 | NULL, | 1792 | NULL, |
1602 | }; | 1793 | }; |
1603 | 1794 | ||
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index d56bb0051009..c22d3ebaca20 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h | |||
@@ -517,6 +517,7 @@ | |||
517 | #define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ | 517 | #define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ |
518 | #define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ | 518 | #define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ |
519 | #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ | 519 | #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ |
520 | #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ | ||
520 | #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ | 521 | #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ |
521 | #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ | 522 | #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ |
522 | #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ | 523 | #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ |