From ec818da1414aa52b649c61b4bba83f2c335e106a Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 20 Sep 2013 02:55:46 +0200 Subject: HSI: add Device Tree support for HSI clients Add new method hsi_add_clients_from_dt, which can be used to initialize HSI clients from a device tree node. The patch also documents the DT binding for trivial HSI clients. Signed-off-by: Sebastian Reichel --- .../devicetree/bindings/hsi/trivial-devices.txt | 36 +++++++++++ drivers/hsi/hsi.c | 70 +++++++++++++++++++++- include/dt-bindings/hsi/hsi.h | 17 ++++++ include/linux/hsi/hsi.h | 2 + 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/hsi/trivial-devices.txt create mode 100644 include/dt-bindings/hsi/hsi.h diff --git a/Documentation/devicetree/bindings/hsi/trivial-devices.txt b/Documentation/devicetree/bindings/hsi/trivial-devices.txt new file mode 100644 index 000000000000..1ace14a57e58 --- /dev/null +++ b/Documentation/devicetree/bindings/hsi/trivial-devices.txt @@ -0,0 +1,36 @@ +This is a list of trivial hsi client devices that have simple +device tree bindings, consisting only of a compatible field +and the optional hsi configuration. + +If a device needs more specific bindings, such as properties to +describe some aspect of it, there needs to be a specific binding +document for it just like any other devices. + +Optional HSI configuration properties: + +- hsi,mode Bit transmission mode (STREAM or FRAME) + The first value is used for RX and the second one for + TX configuration. If only one value is provided it will + be used for RX and TX. + The assignments may be found in header file + . +- hsi,channels Number of channels to use [1..16] + The first value is used for RX and the second one for + TX configuration. If only one value is provided it will + be used for RX and TX. +- hsi,speed Max bit transmission speed (Kbit/s) + The first value is used for RX and the second one for + TX configuration. If only one value is provided it will + be used for RX and TX. +- hsi,flow RX flow type (SYNCHRONIZED or PIPELINE) + The assignments may be found in header file + . +- hsi,arb_mode Arbitration mode for TX frame (Round robin, priority) + The assignments may be found in header file + . + +This is the list of trivial client devices: + +Compatible Description +========== ============= +hsi-char HSI character device 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 #include #include +#include +#include #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); diff --git a/include/dt-bindings/hsi/hsi.h b/include/dt-bindings/hsi/hsi.h new file mode 100644 index 000000000000..2d6b18140e34 --- /dev/null +++ b/include/dt-bindings/hsi/hsi.h @@ -0,0 +1,17 @@ +/* + * This header provides constants for hsi client bindings. + */ + +#ifndef _DT_BINDINGS_HSI_H +#define _DT_BINDINGS_HSI_H + +#define HSI_MODE_STREAM 1 +#define HSI_MODE_FRAME 2 + +#define HSI_FLOW_SYNC 0 +#define HSI_FLOW_PIPE 1 + +#define HSI_ARB_RR 0 +#define HSI_ARB_PRIO 1 + +#endif /* _DT_BINDINGS_HSI_H */ diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index 0dca785288cf..fb07339f4302 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -282,6 +282,8 @@ struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags); void hsi_put_controller(struct hsi_controller *hsi); int hsi_register_controller(struct hsi_controller *hsi); void hsi_unregister_controller(struct hsi_controller *hsi); +void hsi_add_clients_from_dt(struct hsi_port *port, + struct device_node *clients); static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi, void *data) -- cgit v1.2.3