| From 8f10daa0cc99ce286b9a2f249f1fa0a245d4fc03 Mon Sep 17 00:00:00 2001 |
| From: "Edward A. James" <eajames@us.ibm.com> |
| Date: Wed, 17 May 2017 09:46:19 -0500 |
| Subject: [PATCH] fsi: Match fsi slaves & engines to available device tree |
| nodes |
| |
| This change populates device tree nodes for scanned FSI slaves and |
| engines. If the master populates ->of_node of the FSI master device, |
| we'll look for matching slaves, and under those slaves we'll look for |
| matching engines. |
| |
| This means that FSI drivers will have their ->of_node pointer populated |
| if there's a corresponding DT node, which they can use for further |
| device discover. |
| |
| Presence of device tree nodes is optional, and only required for |
| fsi device drivers that need extra properties, or subordinate devices, |
| to be enumerated. |
| |
| Signed-off-by: Jeremy Kerr <jk@ozlabs.org> |
| Signed-off-by: Edward A. James <eajames@us.ibm.com> |
| --- |
| drivers/fsi/fsi-core.c | 99 +++++++++++++++++++++++++++++++++++++++++++ |
| drivers/fsi/fsi-master-gpio.c | 4 ++ |
| drivers/fsi/fsi-master-hub.c | 4 ++ |
| 3 files changed, 107 insertions(+) |
| |
| diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c |
| index 7ca5b74..71e3af9 100644 |
| --- a/drivers/fsi/fsi-core.c |
| +++ b/drivers/fsi/fsi-core.c |
| @@ -17,6 +17,7 @@ |
| #include <linux/fsi.h> |
| #include <linux/idr.h> |
| #include <linux/module.h> |
| +#include <linux/of.h> |
| #include <linux/slab.h> |
| |
| #include "fsi-master.h" |
| @@ -125,6 +126,7 @@ static void fsi_device_release(struct device *_device) |
| { |
| struct fsi_device *device = to_fsi_dev(_device); |
| |
| + of_node_put(device->dev.of_node); |
| kfree(device); |
| } |
| |
| @@ -337,6 +339,57 @@ extern void fsi_slave_release_range(struct fsi_slave *slave, |
| { |
| } |
| |
| +static bool fsi_device_node_matches(struct device *dev, struct device_node *np, |
| + uint32_t addr, uint32_t size) |
| +{ |
| + unsigned int len, na, ns; |
| + const __be32 *prop; |
| + uint32_t psize; |
| + |
| + na = of_n_addr_cells(np); |
| + ns = of_n_size_cells(np); |
| + |
| + if (na != 1 || ns != 1) |
| + return false; |
| + |
| + prop = of_get_property(np, "reg", &len); |
| + if (!prop || len != 8) |
| + return false; |
| + |
| + if (of_read_number(prop, 1) != addr) |
| + return false; |
| + |
| + psize = of_read_number(prop + 1, 1); |
| + if (psize != size) { |
| + dev_warn(dev, |
| + "node %s matches probed address, but not size (got 0x%x, expected 0x%x)", |
| + of_node_full_name(np), psize, size); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +/* Find a matching node for the slave engine at @address, using @size bytes |
| + * of space. Returns NULL if not found, or a matching node with refcount |
| + * already incremented. |
| + */ |
| +static struct device_node *fsi_device_find_of_node(struct fsi_device *dev) |
| +{ |
| + struct device_node *parent, *np; |
| + |
| + parent = dev_of_node(&dev->slave->dev); |
| + if (!parent) |
| + return NULL; |
| + |
| + for_each_child_of_node(parent, np) { |
| + if (fsi_device_node_matches(&dev->dev, np, |
| + dev->addr, dev->size)) |
| + return np; |
| + } |
| + |
| + return NULL; |
| +} |
| + |
| static int fsi_slave_scan(struct fsi_slave *slave) |
| { |
| uint32_t engine_addr; |
| @@ -405,6 +458,7 @@ static int fsi_slave_scan(struct fsi_slave *slave) |
| dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x", |
| slave->master->idx, slave->link, |
| slave->id, i - 2); |
| + dev->dev.of_node = fsi_device_find_of_node(dev); |
| |
| rc = device_register(&dev->dev); |
| if (rc) { |
| @@ -561,9 +615,53 @@ static void fsi_slave_release(struct device *dev) |
| { |
| struct fsi_slave *slave = to_fsi_slave(dev); |
| |
| + of_node_put(dev->of_node); |
| kfree(slave); |
| } |
| |
| +static bool fsi_slave_node_matches(struct device_node *np, |
| + int link, uint8_t id) |
| +{ |
| + unsigned int len, na, ns; |
| + const __be32 *prop; |
| + |
| + na = of_n_addr_cells(np); |
| + ns = of_n_size_cells(np); |
| + |
| + /* Ensure we have the correct format for addresses and sizes in |
| + * reg properties |
| + */ |
| + if (na != 2 || ns != 0) |
| + return false; |
| + |
| + prop = of_get_property(np, "reg", &len); |
| + if (!prop || len != 8) |
| + return false; |
| + |
| + return (of_read_number(prop, 1) == link) && |
| + (of_read_number(prop + 1, 1) == id); |
| +} |
| + |
| +/* Find a matching node for the slave at (link, id). Returns NULL if none |
| + * found, or a matching node with refcount already incremented. |
| + */ |
| +static struct device_node *fsi_slave_find_of_node(struct fsi_master *master, |
| + int link, uint8_t id) |
| +{ |
| + struct device_node *parent, *np; |
| + |
| + parent = dev_of_node(&master->dev); |
| + if (!parent) |
| + return NULL; |
| + |
| + for_each_child_of_node(parent, np) { |
| + if (fsi_slave_node_matches(np, link, id)) |
| + return np; |
| + } |
| + |
| + return NULL; |
| +} |
| + |
| static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) |
| { |
| uint32_t chip_id, llmode; |
| @@ -626,6 +724,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) |
| |
| slave->master = master; |
| slave->dev.parent = &master->dev; |
| + slave->dev.of_node = fsi_slave_find_of_node(master, link, id); |
| slave->dev.release = fsi_slave_release; |
| slave->link = link; |
| slave->id = id; |
| diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c |
| index ef209ef..2a6a812 100644 |
| --- a/drivers/fsi/fsi-master-gpio.c |
| +++ b/drivers/fsi/fsi-master-gpio.c |
| @@ -5,6 +5,7 @@ |
| #include <linux/platform_device.h> |
| #include <linux/gpio/consumer.h> |
| #include <linux/module.h> |
| +#include <linux/of.h> |
| #include <linux/delay.h> |
| #include <linux/fsi.h> |
| #include <linux/device.h> |
| @@ -529,6 +530,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev) |
| |
| master->dev = &pdev->dev; |
| master->master.dev.parent = master->dev; |
| + master->master.dev.of_node = of_node_get(dev_of_node(master->dev)); |
| master->master.dev.release = fsi_master_gpio_release; |
| |
| gpio = devm_gpiod_get(&pdev->dev, "clock", 0); |
| @@ -598,6 +600,8 @@ static int fsi_master_gpio_remove(struct platform_device *pdev) |
| devm_gpiod_put(&pdev->dev, master->gpio_mux); |
| fsi_master_unregister(&master->master); |
| |
| + of_node_put(master->master.dev.of_node); |
| + |
| return 0; |
| } |
| |
| diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c |
| index 133b9bf..3223a67 100644 |
| --- a/drivers/fsi/fsi-master-hub.c |
| +++ b/drivers/fsi/fsi-master-hub.c |
| @@ -16,6 +16,7 @@ |
| #include <linux/delay.h> |
| #include <linux/fsi.h> |
| #include <linux/module.h> |
| +#include <linux/of.h> |
| #include <linux/slab.h> |
| |
| #include "fsi-master.h" |
| @@ -274,6 +275,7 @@ static int hub_master_probe(struct device *dev) |
| |
| hub->master.dev.parent = dev; |
| hub->master.dev.release = hub_master_release; |
| + hub->master.dev.of_node = of_node_get(dev_of_node(dev)); |
| |
| hub->master.n_links = links; |
| hub->master.read = hub_master_read; |
| @@ -302,6 +304,8 @@ static int hub_master_remove(struct device *dev) |
| |
| fsi_master_unregister(&hub->master); |
| fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size); |
| + of_node_put(hub->master.dev.of_node); |
| + |
| return 0; |
| } |
| |
| -- |
| 1.8.3.1 |
| |