[PATCH v2] pci-sysfs: Make PCI bridge attribute visible in sysfs

2017-05-11 Thread Wong Vee Khee
From: vwong 

Export the PCIe link attributes of PCI bridges to sysfs.

Signed-off-by: Wong Vee Khee 
Signed-off-by: Hui Chun Ong 
---
 drivers/pci/pci-sysfs.c   | 197 +-
 include/uapi/linux/pci_regs.h |   1 +
 2 files changed, 194 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 31e9961..a2ada91 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -154,6 +154,127 @@ static ssize_t resource_show(struct device *dev, struct 
device_attribute *attr,
 }
 static DEVICE_ATTR_RO(resource);
 
+static ssize_t max_link_speed_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u32 linkcap;
+   int err;
+   const char *speed;
+
+   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, );
+
+   if (!err) {
+   switch (linkcap & PCI_EXP_LNKCAP_SLS) {
+   case PCI_EXP_LNKCAP_SLS_8_0GB:
+   speed = "8 GT/s";
+   break;
+   case PCI_EXP_LNKCAP_SLS_5_0GB:
+   speed = "5 GT/s";
+   break;
+   case PCI_EXP_LNKCAP_SLS_2_5GB:
+   speed = "2.5 GT/s";
+   break;
+   default:
+   speed = "Unknown speed";
+   }
+
+   return sprintf(buf, "%s\n", speed);
+   }
+
+   return -EINVAL;
+}
+static DEVICE_ATTR_RO(max_link_speed);
+
+static ssize_t max_link_width_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u32 linkcap;
+   int err;
+
+   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, );
+
+   return err ? -EINVAL : sprintf(
+   buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4);
+}
+static DEVICE_ATTR_RO(max_link_width);
+
+static ssize_t current_link_speed_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u16 linkstat;
+   int err;
+   const char *speed;
+
+   err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, );
+
+   if (!err) {
+   switch (linkstat & PCI_EXP_LNKSTA_CLS) {
+   case PCI_EXP_LNKSTA_CLS_8_0GB:
+   speed = "8 GT/s";
+   break;
+   case PCI_EXP_LNKSTA_CLS_5_0GB:
+   speed = "5 GT/s";
+   break;
+   case PCI_EXP_LNKSTA_CLS_2_5GB:
+   speed = "2.5 GT/s";
+   break;
+   default:
+   speed = "Unknown speed";
+   }
+
+   return sprintf(buf, "%s\n", speed);
+   }
+
+   return -EINVAL;
+}
+static DEVICE_ATTR_RO(current_link_speed);
+
+static ssize_t current_link_width_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u16 linkstat;
+   int err;
+
+   err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, );
+
+   return err ? -EINVAL : sprintf(
+   buf, "%u\n",
+   (linkstat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT);
+}
+static DEVICE_ATTR_RO(current_link_width);
+
+static ssize_t secondary_bus_number_show(struct device *dev,
+struct device_attribute *attr,
+char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u8 sec_bus;
+   int err;
+
+   err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, _bus);
+
+   return err ? -EINVAL : sprintf(buf, "%u\n", sec_bus);
+}
+static DEVICE_ATTR_RO(secondary_bus_number);
+
+static ssize_t subordinate_bus_number_show(struct device *dev,
+  struct device_attribute *attr,
+  char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u8 sub_bus;
+   int err;
+
+   err = pci_read_config_byte(pci_dev, PCI_SUBORDINATE_BUS, _bus);
+
+   return err ? -EINVAL : sprintf(buf, "%u\n", sub_bus);
+}
+static DEVICE_ATTR_RO(subordinate_bus_number);
+
 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 char *buf)
 {
@@ -629,12 +750,17 @@ static struct attribute *pci_dev_attrs[] = {
NULL,
 };
 
-static const struct attribute_group pci_dev_group = {
-   .attrs = pci_dev_attrs,
+static struct attribute *pci_bridge_attrs[] = {
+   _attr_subordinate_bus_number.attr,
+   _attr_secondary_bus_number.attr,
+   NULL,
 };

[PATCH v2] pci-sysfs: Make PCI bridge attribute visible in sysfs

2017-05-11 Thread Wong Vee Khee
From: vwong 

Export the PCIe link attributes of PCI bridges to sysfs.

Signed-off-by: Wong Vee Khee 
Signed-off-by: Hui Chun Ong 
---
 drivers/pci/pci-sysfs.c   | 197 +-
 include/uapi/linux/pci_regs.h |   1 +
 2 files changed, 194 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 31e9961..a2ada91 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -154,6 +154,127 @@ static ssize_t resource_show(struct device *dev, struct 
device_attribute *attr,
 }
 static DEVICE_ATTR_RO(resource);
 
+static ssize_t max_link_speed_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u32 linkcap;
+   int err;
+   const char *speed;
+
+   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, );
+
+   if (!err) {
+   switch (linkcap & PCI_EXP_LNKCAP_SLS) {
+   case PCI_EXP_LNKCAP_SLS_8_0GB:
+   speed = "8 GT/s";
+   break;
+   case PCI_EXP_LNKCAP_SLS_5_0GB:
+   speed = "5 GT/s";
+   break;
+   case PCI_EXP_LNKCAP_SLS_2_5GB:
+   speed = "2.5 GT/s";
+   break;
+   default:
+   speed = "Unknown speed";
+   }
+
+   return sprintf(buf, "%s\n", speed);
+   }
+
+   return -EINVAL;
+}
+static DEVICE_ATTR_RO(max_link_speed);
+
+static ssize_t max_link_width_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u32 linkcap;
+   int err;
+
+   err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, );
+
+   return err ? -EINVAL : sprintf(
+   buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4);
+}
+static DEVICE_ATTR_RO(max_link_width);
+
+static ssize_t current_link_speed_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u16 linkstat;
+   int err;
+   const char *speed;
+
+   err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, );
+
+   if (!err) {
+   switch (linkstat & PCI_EXP_LNKSTA_CLS) {
+   case PCI_EXP_LNKSTA_CLS_8_0GB:
+   speed = "8 GT/s";
+   break;
+   case PCI_EXP_LNKSTA_CLS_5_0GB:
+   speed = "5 GT/s";
+   break;
+   case PCI_EXP_LNKSTA_CLS_2_5GB:
+   speed = "2.5 GT/s";
+   break;
+   default:
+   speed = "Unknown speed";
+   }
+
+   return sprintf(buf, "%s\n", speed);
+   }
+
+   return -EINVAL;
+}
+static DEVICE_ATTR_RO(current_link_speed);
+
+static ssize_t current_link_width_show(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u16 linkstat;
+   int err;
+
+   err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, );
+
+   return err ? -EINVAL : sprintf(
+   buf, "%u\n",
+   (linkstat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT);
+}
+static DEVICE_ATTR_RO(current_link_width);
+
+static ssize_t secondary_bus_number_show(struct device *dev,
+struct device_attribute *attr,
+char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u8 sec_bus;
+   int err;
+
+   err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, _bus);
+
+   return err ? -EINVAL : sprintf(buf, "%u\n", sec_bus);
+}
+static DEVICE_ATTR_RO(secondary_bus_number);
+
+static ssize_t subordinate_bus_number_show(struct device *dev,
+  struct device_attribute *attr,
+  char *buf)
+{
+   struct pci_dev *pci_dev = to_pci_dev(dev);
+   u8 sub_bus;
+   int err;
+
+   err = pci_read_config_byte(pci_dev, PCI_SUBORDINATE_BUS, _bus);
+
+   return err ? -EINVAL : sprintf(buf, "%u\n", sub_bus);
+}
+static DEVICE_ATTR_RO(subordinate_bus_number);
+
 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 char *buf)
 {
@@ -629,12 +750,17 @@ static struct attribute *pci_dev_attrs[] = {
NULL,
 };
 
-static const struct attribute_group pci_dev_group = {
-   .attrs = pci_dev_attrs,
+static struct attribute *pci_bridge_attrs[] = {
+   _attr_subordinate_bus_number.attr,
+   _attr_secondary_bus_number.attr,
+   NULL,
 };
 
-const struct attribute_group *pci_dev_groups[] = {
-