mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	HSI: Add common DT binding for HSI client devices
Implement and document generic DT bindings for HSI clients. Signed-off-by: Sebastian Reichel <sre@kernel.org> Reviewed-by: Pavel Machek <pavel@ucw.cz> Tested-By: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
This commit is contained in:
		
							parent
							
								
									8491451024
								
							
						
					
					
						commit
						a2aa24734d
					
				
					 3 changed files with 261 additions and 2 deletions
				
			
		
							
								
								
									
										44
									
								
								Documentation/devicetree/bindings/hsi/client-devices.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								Documentation/devicetree/bindings/hsi/client-devices.txt
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					Each HSI port is supposed to have one child node, which
 | 
				
			||||||
 | 
					symbols the remote device connected to the HSI port. The
 | 
				
			||||||
 | 
					following properties are standardized for HSI clients:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Required HSI configuration properties:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- hsi-channel-ids:	A list of channel ids
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- hsi-rx-mode:		Receiver Bit transmission mode ("stream" or "frame")
 | 
				
			||||||
 | 
					- hsi-tx-mode:		Transmitter Bit transmission mode ("stream" or "frame")
 | 
				
			||||||
 | 
					- hsi-mode:		May be used instead hsi-rx-mode and hsi-tx-mode if
 | 
				
			||||||
 | 
								the transmission mode is the same for receiver and
 | 
				
			||||||
 | 
								transmitter
 | 
				
			||||||
 | 
					- hsi-speed-kbps:	Max bit transmission speed in kbit/s
 | 
				
			||||||
 | 
					- hsi-flow:		RX flow type ("synchronized" or "pipeline")
 | 
				
			||||||
 | 
					- hsi-arb-mode:		Arbitration mode for TX frame ("round-robin", "priority")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Optional HSI configuration properties:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- hsi-channel-names:	A list with one name per channel specified in the
 | 
				
			||||||
 | 
								hsi-channel-ids property
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Device Tree node example for an HSI client:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hsi-controller {
 | 
				
			||||||
 | 
						hsi-port {
 | 
				
			||||||
 | 
							modem: hsi-client {
 | 
				
			||||||
 | 
								compatible = "nokia,n900-modem";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								hsi-channel-ids = <0>, <1>, <2>, <3>;
 | 
				
			||||||
 | 
								hsi-channel-names = "mcsaab-control",
 | 
				
			||||||
 | 
										    "speech-control",
 | 
				
			||||||
 | 
										    "speech-data",
 | 
				
			||||||
 | 
										    "mcsaab-data";
 | 
				
			||||||
 | 
								hsi-speed-kbps = <55000>;
 | 
				
			||||||
 | 
								hsi-mode = "frame";
 | 
				
			||||||
 | 
								hsi-flow = "synchronized";
 | 
				
			||||||
 | 
								hsi-arb-mode = "round-robin";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* more client specific properties */
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,8 @@
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/string.h>
 | 
					#include <linux/string.h>
 | 
				
			||||||
#include <linux/notifier.h>
 | 
					#include <linux/notifier.h>
 | 
				
			||||||
 | 
					#include <linux/of.h>
 | 
				
			||||||
 | 
					#include <linux/of_device.h>
 | 
				
			||||||
#include "hsi_core.h"
 | 
					#include "hsi_core.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static ssize_t modalias_show(struct device *dev,
 | 
					static ssize_t modalias_show(struct device *dev,
 | 
				
			||||||
| 
						 | 
					@ -50,7 +52,13 @@ 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)
 | 
					static int hsi_bus_match(struct device *dev, struct device_driver *driver)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return strcmp(dev_name(dev), driver->name) == 0;
 | 
						if (of_driver_match_device(dev, driver))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strcmp(dev_name(dev), driver->name) == 0)
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct bus_type hsi_bus_type = {
 | 
					static struct bus_type hsi_bus_type = {
 | 
				
			||||||
| 
						 | 
					@ -123,6 +131,202 @@ static void hsi_scan_board_info(struct hsi_controller *hsi)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_OF
 | 
				
			||||||
 | 
					static struct hsi_board_info hsi_char_dev_info = {
 | 
				
			||||||
 | 
						.name = "hsi_char",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int hsi_of_property_parse_mode(struct device_node *client, char *name,
 | 
				
			||||||
 | 
									      unsigned int *result)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *mode;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = of_property_read_string(client, name, &mode);
 | 
				
			||||||
 | 
						if (err < 0)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strcmp(mode, "stream") == 0)
 | 
				
			||||||
 | 
							*result = HSI_MODE_STREAM;
 | 
				
			||||||
 | 
						else if (strcmp(mode, "frame") == 0)
 | 
				
			||||||
 | 
							*result = HSI_MODE_FRAME;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int hsi_of_property_parse_flow(struct device_node *client, char *name,
 | 
				
			||||||
 | 
									      unsigned int *result)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *flow;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = of_property_read_string(client, name, &flow);
 | 
				
			||||||
 | 
						if (err < 0)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strcmp(flow, "synchronized") == 0)
 | 
				
			||||||
 | 
							*result = HSI_FLOW_SYNC;
 | 
				
			||||||
 | 
						else if (strcmp(flow, "pipeline") == 0)
 | 
				
			||||||
 | 
							*result = HSI_FLOW_PIPE;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int hsi_of_property_parse_arb_mode(struct device_node *client,
 | 
				
			||||||
 | 
										  char *name, unsigned int *result)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *arb_mode;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = of_property_read_string(client, name, &arb_mode);
 | 
				
			||||||
 | 
						if (err < 0)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strcmp(arb_mode, "round-robin") == 0)
 | 
				
			||||||
 | 
							*result = HSI_ARB_RR;
 | 
				
			||||||
 | 
						else if (strcmp(arb_mode, "priority") == 0)
 | 
				
			||||||
 | 
							*result = HSI_ARB_PRIO;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void hsi_add_client_from_dt(struct hsi_port *port,
 | 
				
			||||||
 | 
											struct device_node *client)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct hsi_client *cl;
 | 
				
			||||||
 | 
						struct hsi_channel channel;
 | 
				
			||||||
 | 
						struct property *prop;
 | 
				
			||||||
 | 
						char name[32];
 | 
				
			||||||
 | 
						int length, cells, err, i, max_chan, mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cl = kzalloc(sizeof(*cl), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!cl)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = of_modalias_node(client, name, sizeof(name));
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev_set_name(&cl->device, "%s", name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = hsi_of_property_parse_mode(client, "hsi-mode", &mode);
 | 
				
			||||||
 | 
						if (err) {
 | 
				
			||||||
 | 
							err = hsi_of_property_parse_mode(client, "hsi-rx-mode",
 | 
				
			||||||
 | 
											 &cl->rx_cfg.mode);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = hsi_of_property_parse_mode(client, "hsi-tx-mode",
 | 
				
			||||||
 | 
											 &cl->tx_cfg.mode);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto err;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							cl->rx_cfg.mode = mode;
 | 
				
			||||||
 | 
							cl->tx_cfg.mode = mode;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = of_property_read_u32(client, "hsi-speed-kbps",
 | 
				
			||||||
 | 
									   &cl->tx_cfg.speed);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
						cl->rx_cfg.speed = cl->tx_cfg.speed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = hsi_of_property_parse_flow(client, "hsi-flow",
 | 
				
			||||||
 | 
										 &cl->rx_cfg.flow);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = hsi_of_property_parse_arb_mode(client, "hsi-arb-mode",
 | 
				
			||||||
 | 
										     &cl->rx_cfg.arb_mode);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prop = of_find_property(client, "hsi-channel-ids", &length);
 | 
				
			||||||
 | 
						if (!prop) {
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cells = length / sizeof(u32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cl->rx_cfg.num_channels = cells;
 | 
				
			||||||
 | 
						cl->tx_cfg.num_channels = cells;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cl->rx_cfg.channels = kzalloc(cells * sizeof(channel), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!cl->rx_cfg.channels) {
 | 
				
			||||||
 | 
							err = -ENOMEM;
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cl->tx_cfg.channels = kzalloc(cells * sizeof(channel), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!cl->tx_cfg.channels) {
 | 
				
			||||||
 | 
							err = -ENOMEM;
 | 
				
			||||||
 | 
							goto err2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						max_chan = 0;
 | 
				
			||||||
 | 
						for (i = 0; i < cells; i++) {
 | 
				
			||||||
 | 
							err = of_property_read_u32_index(client, "hsi-channel-ids", i,
 | 
				
			||||||
 | 
											 &channel.id);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto err3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = of_property_read_string_index(client, "hsi-channel-names",
 | 
				
			||||||
 | 
											    i, &channel.name);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								channel.name = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (channel.id > max_chan)
 | 
				
			||||||
 | 
								max_chan = channel.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cl->rx_cfg.channels[i] = channel;
 | 
				
			||||||
 | 
							cl->tx_cfg.channels[i] = channel;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cl->rx_cfg.num_hw_channels = max_chan + 1;
 | 
				
			||||||
 | 
						cl->tx_cfg.num_hw_channels = max_chan + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
							goto err3;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err3:
 | 
				
			||||||
 | 
						kfree(cl->tx_cfg.channels);
 | 
				
			||||||
 | 
					err2:
 | 
				
			||||||
 | 
						kfree(cl->rx_cfg.channels);
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
						kfree(cl);
 | 
				
			||||||
 | 
						pr_err("hsi client: missing or incorrect of property: err=%d\n", err);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void hsi_add_clients_from_dt(struct hsi_port *port, struct device_node *clients)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct device_node *child;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* register hsi-char device */
 | 
				
			||||||
 | 
						hsi_new_client(port, &hsi_char_dev_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for_each_available_child_of_node(clients, child)
 | 
				
			||||||
 | 
							hsi_add_client_from_dt(port, child);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(hsi_add_clients_from_dt);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int hsi_remove_client(struct device *dev, void *data __maybe_unused)
 | 
					int hsi_remove_client(struct device *dev, void *data __maybe_unused)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	device_unregister(dev);
 | 
						device_unregister(dev);
 | 
				
			||||||
| 
						 | 
					@ -505,7 +709,7 @@ int hsi_unregister_port_event(struct hsi_client *cl)
 | 
				
			||||||
EXPORT_SYMBOL_GPL(hsi_unregister_port_event);
 | 
					EXPORT_SYMBOL_GPL(hsi_unregister_port_event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * hsi_event -Notifies clients about port events
 | 
					 * hsi_event - Notifies clients about port events
 | 
				
			||||||
 * @port: Port where the event occurred
 | 
					 * @port: Port where the event occurred
 | 
				
			||||||
 * @event: The event type
 | 
					 * @event: The event type
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -301,6 +301,17 @@ struct hsi_client *hsi_new_client(struct hsi_port *port,
 | 
				
			||||||
int hsi_remove_client(struct device *dev, void *data);
 | 
					int hsi_remove_client(struct device *dev, void *data);
 | 
				
			||||||
void hsi_port_unregister_clients(struct hsi_port *port);
 | 
					void hsi_port_unregister_clients(struct hsi_port *port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_OF
 | 
				
			||||||
 | 
					void hsi_add_clients_from_dt(struct hsi_port *port,
 | 
				
			||||||
 | 
								     struct device_node *clients);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					static inline void hsi_add_clients_from_dt(struct hsi_port *port,
 | 
				
			||||||
 | 
										   struct device_node *clients)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi,
 | 
					static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi,
 | 
				
			||||||
								void *data)
 | 
													void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue