Thread (1 message) 1 message, 1 author, 2017-02-14

Re: [PATCH v2 4/4] checks: Add bus checks for PCI buses

From: David Gibson <hidden>
Date: 2017-02-14 02:12:55

On Mon, Feb 13, 2017 at 02:49:45PM -0600, Rob Herring wrote:
On Sun, Feb 12, 2017 at 11:03 PM, David Gibson
[off-list ref] wrote:
quoted
On Fri, Feb 10, 2017 at 10:47:17AM -0600, Rob Herring wrote:
quoted
Add PCI bridge and device node checks. We identify PCI bridges with
'device_type = "pci"' as only PCI bridges should set that property. For
bridges, check that ranges is present and #address-cells and

For devices, the primary check is the reg property and the unit address.
Device unit addresses are in the form DD or DD,F where DD is the
device 0-0x1f and F is the function 0-7.

Signed-off-by: Rob Herring <redacted>
---
v2:
- Remove bus_type functions. Combine test for bus_type and bridge check
  into single check.
- Add a check that PCI bridge node name is pci or pcie.

 checks.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dtc.h    |  7 ++++++
 2 files changed, 90 insertions(+)
diff --git a/checks.c b/checks.c
index 16d17d20caec..9ebb148f947a 100644
--- a/checks.c
+++ b/checks.c
@@ -702,6 +702,86 @@ static void check_ranges_format(struct check *c, struct dt_info *dti,
 }
 WARNING(ranges_format, check_ranges_format, NULL, &addr_size_cells);

+static const struct bus_type pci_bus = {
+     .type = PCI_BUS_TYPE,
Since you can use the struct pointer itself as a handle on the bus
type, I don't think there's any value to having the enum-style type
value.  What _would_ be useful is a human readable bus type name.
Okay.
quoted
quoted
+};
+
+static void check_pci_bridge(struct check *c, struct dt_info *dti, struct node *node)
+{
+     struct property *prop;
+
+     prop = get_property(node, "device_type");
+     if (!prop || strcmp(prop->val.val, "pci"))
+             return;
+
+     node->bus = &pci_bus;
+
+     if (strncmp(node->name, "pci", node->basenamelen) &&
+         strncmp(node->name, "pcie", node->basenamelen))
+             FAIL(c, "Node %s node name is not \"pci\" or \"pcie\"",
+                          node->fullpath);
Please use the strneq() macro - I frequently get confused about
whether strcmp()/strncmp() comparisons need an ! or not for equality
testing.  streq() / strneq() help me remember.
quoted
+
+     prop = get_property(node, "ranges");
+     if (!prop)
+             FAIL(c, "Node %s missing ranges for PCI bridge (or not a bridge)",
+                          node->fullpath);
+
+     if (node_addr_cells(node) != 3)
+             FAIL(c, "Node %s incorrect #address-cells for PCI bridge",
+                          node->fullpath);
+     if (node_size_cells(node) != 2)
+             FAIL(c, "Node %s incorrect #size-cells for PCI bridge",
+                          node->fullpath);
+}
+WARNING(pci_bridge, check_pci_bridge, NULL,
+     &device_type_is_string, &addr_size_cells);
+
+static void check_pci_device(struct check *c, struct dt_info *dti, struct node *node)
+{
+     struct property *prop;
+     const char *unitname = get_unitname(node);
+     char unit_addr[5];
+     unsigned int dev, func, reg;
+
+     if (!node->parent || !node->parent->bus ||
+         (node->parent->bus->type != PCI_BUS_TYPE))
You can just use node->parent->bus != &pci_bus here.
quoted
+             return;
+
+     prop = get_property(node, "reg");
+     if (!prop)
+             return;
+
+     reg = fdt32_to_cpu(*((cell_t *)prop->val.val));
+
+     dev = (reg & 0xf800) >> 11;
+     func = (reg & 0x700) >> 8;
+
+     if (reg & 0xff000000)
+             FAIL(c, "Node %s PCI reg address is not configuration space",
+                          node->fullpath);
+
+     if (dev > 0x1f)
+             FAIL(c, "Node %s PCI device number out of range",
+                          node->fullpath);
+     if (func > 7)
+             FAIL(c, "Node %s PCI function number out of range",
+                  node->fullpath);
BTW, I just noticed these 2 checks I can drop. They can never be true
since I'm masking the values.
Ah, good point.  It looks like ther should be more to check though -
dev and func are 8 bits, and you don't allow anything in the top 8
bits, but there's no checking of the remaining 16 bits.  Or the other
2 address cells for that matter.
quoted
quoted
+
+     if (func == 0) {
+             snprintf(unit_addr, sizeof(unit_addr), "%x", dev);
+             if (!strcmp(unitname, unit_addr))
+                     return;
+     }
+
+     snprintf(unit_addr, sizeof(unit_addr), "%x,%x", dev, func);
+     if (!strcmp(unitname, unit_addr))
+             return;
So as mentioned in my comments to 3/4, the test above, I would put
back into unit_address_vs_reg, using a callback in the bus_type which
formats a reg into the correct unit address.
Humm, that doesn't really work. The unit address can be in 2 different
forms when func# is 0. We can have either <dev> or <dev>,0.
Ah, good point.  Alright leave it as is for now.  When we get more bus
types we can see if it makes sense to generalize something.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

Attachments

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help