diff options
Diffstat (limited to 'drivers/hsi/hsi.c')
-rw-r--r-- | drivers/hsi/hsi.c | 70 |
1 files changed, 69 insertions, 1 deletions
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index 66d44581e1b1..4384d7e2bc81 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c @@ -26,6 +26,8 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/notifier.h> +#include <linux/of.h> +#include <linux/of_device.h> #include "hsi_core.h" static ssize_t modalias_show(struct device *dev, @@ -48,7 +50,10 @@ static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env) static int hsi_bus_match(struct device *dev, struct device_driver *driver) { - return strcmp(dev_name(dev), driver->name) == 0; + if (dev->of_node != NULL) + return of_driver_match_device(dev, driver); + else + return strcmp(dev_name(dev), driver->name) == 0; } static struct bus_type hsi_bus_type = { @@ -73,6 +78,7 @@ static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) cl->tx_cfg = info->tx_cfg; cl->rx_cfg = info->rx_cfg; cl->device.bus = &hsi_bus_type; + cl->device.parent = &port->device; cl->device.release = hsi_client_release; dev_set_name(&cl->device, "%s", info->name); @@ -99,6 +105,68 @@ static void hsi_scan_board_info(struct hsi_controller *hsi) } } +static void hsi_of_get_client_cfg_property(struct device_node *client, + char *name, unsigned int *rx, unsigned int *tx) +{ + int err; + + err = of_property_read_u32_index(client, name, 0, rx); + if (!err) + *rx = 0; + + err = of_property_read_u32_index(client, name, 1, tx); + if (!err) + *tx = *rx; +} + +static void hsi_add_client_from_dt(struct hsi_port *port, + struct device_node *client) +{ + struct hsi_client *cl; + const char *name; + int err; + + cl = kzalloc(sizeof(*cl), GFP_KERNEL); + if (!cl) + return; + + err = of_property_read_string(client, "compatible", &name); + if (!err) { + dev_set_name(&cl->device, "%s", name); + } else { + kfree(cl); + return; + } + + hsi_of_get_client_cfg_property(client, "hsi,mode", &cl->rx_cfg.mode, + &cl->tx_cfg.mode); + hsi_of_get_client_cfg_property(client, "hsi,speed", &cl->rx_cfg.speed, + &cl->tx_cfg.speed); + hsi_of_get_client_cfg_property(client, "hsi,channels", + &cl->rx_cfg.channels, &cl->tx_cfg.channels); + of_property_read_u32(client, "hsi,flow", &cl->rx_cfg.flow); + of_property_read_u32(client, "hsi,arb_mode", &cl->tx_cfg.arb_mode); + + cl->device.bus = &hsi_bus_type; + cl->device.parent = &port->device; + cl->device.release = hsi_client_release; + cl->device.of_node = client; + + if (device_register(&cl->device) < 0) { + pr_err("hsi: failed to register client: %s\n", name); + put_device(&cl->device); + } +} + +void hsi_add_clients_from_dt(struct hsi_port *port, struct device_node *clients) +{ + struct device_node *child; + + for_each_available_child_of_node(clients, child) + hsi_add_client_from_dt(port, child); +} +EXPORT_SYMBOL_GPL(hsi_add_clients_from_dt); + static int hsi_remove_client(struct device *dev, void *data __maybe_unused) { device_unregister(dev); |