Devicetree
 help / color / mirror / Atom feed
* [PATCH v3 1/1] of: Move OF property and graph API from base.c to property.c
From: Sakari Ailus @ 2017-05-03 10:21 UTC (permalink / raw)
  To: devicetree, robh, frowand.list
  Cc: linux-acpi, sudeep.holla, lorenzo.pieralisi, mika.westerberg,
	rafael, mark.rutland, broonie, ahs3

base.c contains both core OF functions and increasingly other
functionality such as accessing properties and graphs, including
convenience functions. In the near future this would also include OF
specific implementation of the fwnode property and graph APIs.

Create driver/of/property.c to contain procedures for accessing and
interpreting device tree properties. The procedures are moved from
drivers/of/base.c, with no changes other than copying only the includes
required by the moved procedures.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
Hi Rob and Frank,

This is a pre-dependency for further fwnode property cleanup. The patch
appears to be slightly more conflict-prone than the rest of the patchset:

<URL:http://www.spinics.net/lists/linux-acpi/msg72647.html>

Would it be possible to merge it separately from the rest?

since v2:

- Also move of_property_read_u64_index() added by patch
  "drivers/of/base.c: Add of_property_read_u64_index" found in linux-next

 drivers/of/Makefile   |   2 +-
 drivers/of/base.c     | 733 ------------------------------------------------
 drivers/of/property.c | 763 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 764 insertions(+), 734 deletions(-)
 create mode 100644 drivers/of/property.c

Regards,
Sakari

diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index d7efd9d..97dc01c 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,4 +1,4 @@
-obj-y = base.o device.o platform.o
+obj-y = base.o device.o platform.o property.o
 obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
 obj-$(CONFIG_OF_FLATTREE) += fdt.o
 obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 0ea16bd..d57188d 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1113,458 +1113,6 @@ struct device_node *of_find_node_by_phandle(phandle handle)
 }
 EXPORT_SYMBOL(of_find_node_by_phandle);
 
-/**
- * of_property_count_elems_of_size - Count the number of elements in a property
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @elem_size:	size of the individual element
- *
- * Search for a property in a device node and count the number of elements of
- * size elem_size in it. Returns number of elements on sucess, -EINVAL if the
- * property does not exist or its length does not match a multiple of elem_size
- * and -ENODATA if the property does not have a value.
- */
-int of_property_count_elems_of_size(const struct device_node *np,
-				const char *propname, int elem_size)
-{
-	struct property *prop = of_find_property(np, propname, NULL);
-
-	if (!prop)
-		return -EINVAL;
-	if (!prop->value)
-		return -ENODATA;
-
-	if (prop->length % elem_size != 0) {
-		pr_err("size of %s in node %s is not a multiple of %d\n",
-		       propname, np->full_name, elem_size);
-		return -EINVAL;
-	}
-
-	return prop->length / elem_size;
-}
-EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
-
-/**
- * of_find_property_value_of_size
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @min:	minimum allowed length of property value
- * @max:	maximum allowed length of property value (0 means unlimited)
- * @len:	if !=NULL, actual length is written to here
- *
- * Search for a property in a device node and valid the requested size.
- * Returns the property value on success, -EINVAL if the property does not
- *  exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data is too small or too large.
- *
- */
-static void *of_find_property_value_of_size(const struct device_node *np,
-			const char *propname, u32 min, u32 max, size_t *len)
-{
-	struct property *prop = of_find_property(np, propname, NULL);
-
-	if (!prop)
-		return ERR_PTR(-EINVAL);
-	if (!prop->value)
-		return ERR_PTR(-ENODATA);
-	if (prop->length < min)
-		return ERR_PTR(-EOVERFLOW);
-	if (max && prop->length > max)
-		return ERR_PTR(-EOVERFLOW);
-
-	if (len)
-		*len = prop->length;
-
-	return prop->value;
-}
-
-/**
- * of_property_read_u32_index - Find and read a u32 from a multi-value property.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @index:	index of the u32 in the list of values
- * @out_value:	pointer to return value, modified only if no error.
- *
- * Search for a property in a device node and read nth 32-bit value from
- * it. Returns 0 on success, -EINVAL if the property does not exist,
- * -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
- *
- * The out_value is modified only if a valid u32 value can be decoded.
- */
-int of_property_read_u32_index(const struct device_node *np,
-				       const char *propname,
-				       u32 index, u32 *out_value)
-{
-	const u32 *val = of_find_property_value_of_size(np, propname,
-					((index + 1) * sizeof(*out_value)),
-					0,
-					NULL);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	*out_value = be32_to_cpup(((__be32 *)val) + index);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_u32_index);
-
-/**
- * of_property_read_u64_index - Find and read a u64 from a multi-value property.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @index:	index of the u64 in the list of values
- * @out_value:	pointer to return value, modified only if no error.
- *
- * Search for a property in a device node and read nth 64-bit value from
- * it. Returns 0 on success, -EINVAL if the property does not exist,
- * -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
- *
- * The out_value is modified only if a valid u64 value can be decoded.
- */
-int of_property_read_u64_index(const struct device_node *np,
-				       const char *propname,
-				       u32 index, u64 *out_value)
-{
-	const u64 *val = of_find_property_value_of_size(np, propname,
-					((index + 1) * sizeof(*out_value)),
-					0, NULL);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	*out_value = be64_to_cpup(((__be64 *)val) + index);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_u64_index);
-
-/**
- * of_property_read_variable_u8_array - Find and read an array of u8 from a
- * property, with bounds on the minimum and maximum array size.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_values:	pointer to return value, modified only if return value is 0.
- * @sz_min:	minimum number of array elements to read
- * @sz_max:	maximum number of array elements to read, if zero there is no
- *		upper limit on the number of elements in the dts entry but only
- *		sz_min will be read.
- *
- * Search for a property in a device node and read 8-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * dts entry of array should be like:
- *	property = /bits/ 8 <0x50 0x60 0x70>;
- *
- * The out_values is modified only if a valid u8 value can be decoded.
- */
-int of_property_read_variable_u8_array(const struct device_node *np,
-					const char *propname, u8 *out_values,
-					size_t sz_min, size_t sz_max)
-{
-	size_t sz, count;
-	const u8 *val = of_find_property_value_of_size(np, propname,
-						(sz_min * sizeof(*out_values)),
-						(sz_max * sizeof(*out_values)),
-						&sz);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	if (!sz_max)
-		sz = sz_min;
-	else
-		sz /= sizeof(*out_values);
-
-	count = sz;
-	while (count--)
-		*out_values++ = *val++;
-
-	return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u8_array);
-
-/**
- * of_property_read_variable_u16_array - Find and read an array of u16 from a
- * property, with bounds on the minimum and maximum array size.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_values:	pointer to return value, modified only if return value is 0.
- * @sz_min:	minimum number of array elements to read
- * @sz_max:	maximum number of array elements to read, if zero there is no
- *		upper limit on the number of elements in the dts entry but only
- *		sz_min will be read.
- *
- * Search for a property in a device node and read 16-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * dts entry of array should be like:
- *	property = /bits/ 16 <0x5000 0x6000 0x7000>;
- *
- * The out_values is modified only if a valid u16 value can be decoded.
- */
-int of_property_read_variable_u16_array(const struct device_node *np,
-					const char *propname, u16 *out_values,
-					size_t sz_min, size_t sz_max)
-{
-	size_t sz, count;
-	const __be16 *val = of_find_property_value_of_size(np, propname,
-						(sz_min * sizeof(*out_values)),
-						(sz_max * sizeof(*out_values)),
-						&sz);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	if (!sz_max)
-		sz = sz_min;
-	else
-		sz /= sizeof(*out_values);
-
-	count = sz;
-	while (count--)
-		*out_values++ = be16_to_cpup(val++);
-
-	return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u16_array);
-
-/**
- * of_property_read_variable_u32_array - Find and read an array of 32 bit
- * integers from a property, with bounds on the minimum and maximum array size.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_values:	pointer to return value, modified only if return value is 0.
- * @sz_min:	minimum number of array elements to read
- * @sz_max:	maximum number of array elements to read, if zero there is no
- *		upper limit on the number of elements in the dts entry but only
- *		sz_min will be read.
- *
- * Search for a property in a device node and read 32-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * The out_values is modified only if a valid u32 value can be decoded.
- */
-int of_property_read_variable_u32_array(const struct device_node *np,
-			       const char *propname, u32 *out_values,
-			       size_t sz_min, size_t sz_max)
-{
-	size_t sz, count;
-	const __be32 *val = of_find_property_value_of_size(np, propname,
-						(sz_min * sizeof(*out_values)),
-						(sz_max * sizeof(*out_values)),
-						&sz);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	if (!sz_max)
-		sz = sz_min;
-	else
-		sz /= sizeof(*out_values);
-
-	count = sz;
-	while (count--)
-		*out_values++ = be32_to_cpup(val++);
-
-	return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u32_array);
-
-/**
- * of_property_read_u64 - Find and read a 64 bit integer from a property
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_value:	pointer to return value, modified only if return value is 0.
- *
- * Search for a property in a device node and read a 64-bit value from
- * it. Returns 0 on success, -EINVAL if the property does not exist,
- * -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
- *
- * The out_value is modified only if a valid u64 value can be decoded.
- */
-int of_property_read_u64(const struct device_node *np, const char *propname,
-			 u64 *out_value)
-{
-	const __be32 *val = of_find_property_value_of_size(np, propname,
-						sizeof(*out_value),
-						0,
-						NULL);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	*out_value = of_read_number(val, 2);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_u64);
-
-/**
- * of_property_read_variable_u64_array - Find and read an array of 64 bit
- * integers from a property, with bounds on the minimum and maximum array size.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_values:	pointer to return value, modified only if return value is 0.
- * @sz_min:	minimum number of array elements to read
- * @sz_max:	maximum number of array elements to read, if zero there is no
- *		upper limit on the number of elements in the dts entry but only
- *		sz_min will be read.
- *
- * Search for a property in a device node and read 64-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * The out_values is modified only if a valid u64 value can be decoded.
- */
-int of_property_read_variable_u64_array(const struct device_node *np,
-			       const char *propname, u64 *out_values,
-			       size_t sz_min, size_t sz_max)
-{
-	size_t sz, count;
-	const __be32 *val = of_find_property_value_of_size(np, propname,
-						(sz_min * sizeof(*out_values)),
-						(sz_max * sizeof(*out_values)),
-						&sz);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	if (!sz_max)
-		sz = sz_min;
-	else
-		sz /= sizeof(*out_values);
-
-	count = sz;
-	while (count--) {
-		*out_values++ = of_read_number(val, 2);
-		val += 2;
-	}
-
-	return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u64_array);
-
-/**
- * of_property_read_string - Find and read a string from a property
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_string:	pointer to null terminated return string, modified only if
- *		return value is 0.
- *
- * Search for a property in a device tree node and retrieve a null
- * terminated string value (pointer to data, not a copy). Returns 0 on
- * success, -EINVAL if the property does not exist, -ENODATA if property
- * does not have a value, and -EILSEQ if the string is not null-terminated
- * within the length of the property data.
- *
- * The out_string pointer is modified only if a valid string can be decoded.
- */
-int of_property_read_string(const struct device_node *np, const char *propname,
-				const char **out_string)
-{
-	const struct property *prop = of_find_property(np, propname, NULL);
-	if (!prop)
-		return -EINVAL;
-	if (!prop->value)
-		return -ENODATA;
-	if (strnlen(prop->value, prop->length) >= prop->length)
-		return -EILSEQ;
-	*out_string = prop->value;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_string);
-
-/**
- * of_property_match_string() - Find string in a list and return index
- * @np: pointer to node containing string list property
- * @propname: string list property name
- * @string: pointer to string to search for in string list
- *
- * This function searches a string list property and returns the index
- * of a specific string value.
- */
-int of_property_match_string(const struct device_node *np, const char *propname,
-			     const char *string)
-{
-	const struct property *prop = of_find_property(np, propname, NULL);
-	size_t l;
-	int i;
-	const char *p, *end;
-
-	if (!prop)
-		return -EINVAL;
-	if (!prop->value)
-		return -ENODATA;
-
-	p = prop->value;
-	end = p + prop->length;
-
-	for (i = 0; p < end; i++, p += l) {
-		l = strnlen(p, end - p) + 1;
-		if (p + l > end)
-			return -EILSEQ;
-		pr_debug("comparing %s with %s\n", string, p);
-		if (strcmp(string, p) == 0)
-			return i; /* Found it; return index */
-	}
-	return -ENODATA;
-}
-EXPORT_SYMBOL_GPL(of_property_match_string);
-
-/**
- * of_property_read_string_helper() - Utility helper for parsing string properties
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_strs:	output array of string pointers.
- * @sz:		number of array elements to read.
- * @skip:	Number of strings to skip over at beginning of list.
- *
- * Don't call this function directly. It is a utility helper for the
- * of_property_read_string*() family of functions.
- */
-int of_property_read_string_helper(const struct device_node *np,
-				   const char *propname, const char **out_strs,
-				   size_t sz, int skip)
-{
-	const struct property *prop = of_find_property(np, propname, NULL);
-	int l = 0, i = 0;
-	const char *p, *end;
-
-	if (!prop)
-		return -EINVAL;
-	if (!prop->value)
-		return -ENODATA;
-	p = prop->value;
-	end = p + prop->length;
-
-	for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
-		l = strnlen(p, end - p) + 1;
-		if (p + l > end)
-			return -EILSEQ;
-		if (out_strs && i >= skip)
-			*out_strs++ = p;
-	}
-	i -= skip;
-	return i <= 0 ? -ENODATA : i;
-}
-EXPORT_SYMBOL_GPL(of_property_read_string_helper);
-
 void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
 {
 	int i;
@@ -2211,47 +1759,6 @@ int of_alias_get_highest_id(const char *stem)
 }
 EXPORT_SYMBOL_GPL(of_alias_get_highest_id);
 
-const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
-			       u32 *pu)
-{
-	const void *curv = cur;
-
-	if (!prop)
-		return NULL;
-
-	if (!cur) {
-		curv = prop->value;
-		goto out_val;
-	}
-
-	curv += sizeof(*cur);
-	if (curv >= prop->value + prop->length)
-		return NULL;
-
-out_val:
-	*pu = be32_to_cpup(curv);
-	return curv;
-}
-EXPORT_SYMBOL_GPL(of_prop_next_u32);
-
-const char *of_prop_next_string(struct property *prop, const char *cur)
-{
-	const void *curv = cur;
-
-	if (!prop)
-		return NULL;
-
-	if (!cur)
-		return prop->value;
-
-	curv += strlen(cur) + 1;
-	if (curv >= prop->value + prop->length)
-		return NULL;
-
-	return curv;
-}
-EXPORT_SYMBOL_GPL(of_prop_next_string);
-
 /**
  * of_console_check() - Test and setup console for DT setup
  * @dn - Pointer to device node
@@ -2326,243 +1833,3 @@ int of_find_last_cache_level(unsigned int cpu)
 
 	return cache_level;
 }
-
-/**
- * of_graph_parse_endpoint() - parse common endpoint node properties
- * @node: pointer to endpoint device_node
- * @endpoint: pointer to the OF endpoint data structure
- *
- * The caller should hold a reference to @node.
- */
-int of_graph_parse_endpoint(const struct device_node *node,
-			    struct of_endpoint *endpoint)
-{
-	struct device_node *port_node = of_get_parent(node);
-
-	WARN_ONCE(!port_node, "%s(): endpoint %s has no parent node\n",
-		  __func__, node->full_name);
-
-	memset(endpoint, 0, sizeof(*endpoint));
-
-	endpoint->local_node = node;
-	/*
-	 * It doesn't matter whether the two calls below succeed.
-	 * If they don't then the default value 0 is used.
-	 */
-	of_property_read_u32(port_node, "reg", &endpoint->port);
-	of_property_read_u32(node, "reg", &endpoint->id);
-
-	of_node_put(port_node);
-
-	return 0;
-}
-EXPORT_SYMBOL(of_graph_parse_endpoint);
-
-/**
- * of_graph_get_port_by_id() - get the port matching a given id
- * @parent: pointer to the parent device node
- * @id: id of the port
- *
- * Return: A 'port' node pointer with refcount incremented. The caller
- * has to use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
-{
-	struct device_node *node, *port;
-
-	node = of_get_child_by_name(parent, "ports");
-	if (node)
-		parent = node;
-
-	for_each_child_of_node(parent, port) {
-		u32 port_id = 0;
-
-		if (of_node_cmp(port->name, "port") != 0)
-			continue;
-		of_property_read_u32(port, "reg", &port_id);
-		if (id == port_id)
-			break;
-	}
-
-	of_node_put(node);
-
-	return port;
-}
-EXPORT_SYMBOL(of_graph_get_port_by_id);
-
-/**
- * of_graph_get_next_endpoint() - get next endpoint node
- * @parent: pointer to the parent device node
- * @prev: previous endpoint node, or NULL to get first
- *
- * Return: An 'endpoint' node pointer with refcount incremented. Refcount
- * of the passed @prev node is decremented.
- */
-struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
-					struct device_node *prev)
-{
-	struct device_node *endpoint;
-	struct device_node *port;
-
-	if (!parent)
-		return NULL;
-
-	/*
-	 * Start by locating the port node. If no previous endpoint is specified
-	 * search for the first port node, otherwise get the previous endpoint
-	 * parent port node.
-	 */
-	if (!prev) {
-		struct device_node *node;
-
-		node = of_get_child_by_name(parent, "ports");
-		if (node)
-			parent = node;
-
-		port = of_get_child_by_name(parent, "port");
-		of_node_put(node);
-
-		if (!port) {
-			pr_err("graph: no port node found in %s\n",
-			       parent->full_name);
-			return NULL;
-		}
-	} else {
-		port = of_get_parent(prev);
-		if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
-			      __func__, prev->full_name))
-			return NULL;
-	}
-
-	while (1) {
-		/*
-		 * Now that we have a port node, get the next endpoint by
-		 * getting the next child. If the previous endpoint is NULL this
-		 * will return the first child.
-		 */
-		endpoint = of_get_next_child(port, prev);
-		if (endpoint) {
-			of_node_put(port);
-			return endpoint;
-		}
-
-		/* No more endpoints under this port, try the next one. */
-		prev = NULL;
-
-		do {
-			port = of_get_next_child(parent, port);
-			if (!port)
-				return NULL;
-		} while (of_node_cmp(port->name, "port"));
-	}
-}
-EXPORT_SYMBOL(of_graph_get_next_endpoint);
-
-/**
- * of_graph_get_endpoint_by_regs() - get endpoint node of specific identifiers
- * @parent: pointer to the parent device node
- * @port_reg: identifier (value of reg property) of the parent port node
- * @reg: identifier (value of reg property) of the endpoint node
- *
- * Return: An 'endpoint' node pointer which is identified by reg and at the same
- * is the child of a port node identified by port_reg. reg and port_reg are
- * ignored when they are -1.
- */
-struct device_node *of_graph_get_endpoint_by_regs(
-	const struct device_node *parent, int port_reg, int reg)
-{
-	struct of_endpoint endpoint;
-	struct device_node *node = NULL;
-
-	for_each_endpoint_of_node(parent, node) {
-		of_graph_parse_endpoint(node, &endpoint);
-		if (((port_reg == -1) || (endpoint.port == port_reg)) &&
-			((reg == -1) || (endpoint.id == reg)))
-			return node;
-	}
-
-	return NULL;
-}
-EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
-
-/**
- * of_graph_get_remote_port_parent() - get remote port's parent node
- * @node: pointer to a local endpoint device_node
- *
- * Return: Remote device node associated with remote endpoint node linked
- *	   to @node. Use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_remote_port_parent(
-			       const struct device_node *node)
-{
-	struct device_node *np;
-	unsigned int depth;
-
-	/* Get remote endpoint node. */
-	np = of_parse_phandle(node, "remote-endpoint", 0);
-
-	/* Walk 3 levels up only if there is 'ports' node. */
-	for (depth = 3; depth && np; depth--) {
-		np = of_get_next_parent(np);
-		if (depth == 2 && of_node_cmp(np->name, "ports"))
-			break;
-	}
-	return np;
-}
-EXPORT_SYMBOL(of_graph_get_remote_port_parent);
-
-/**
- * of_graph_get_remote_port() - get remote port node
- * @node: pointer to a local endpoint device_node
- *
- * Return: Remote port node associated with remote endpoint node linked
- *	   to @node. Use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_remote_port(const struct device_node *node)
-{
-	struct device_node *np;
-
-	/* Get remote endpoint node. */
-	np = of_parse_phandle(node, "remote-endpoint", 0);
-	if (!np)
-		return NULL;
-	return of_get_next_parent(np);
-}
-EXPORT_SYMBOL(of_graph_get_remote_port);
-
-/**
- * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
- * @node: pointer to parent device_node containing graph port/endpoint
- * @port: identifier (value of reg property) of the parent port node
- * @endpoint: identifier (value of reg property) of the endpoint node
- *
- * Return: Remote device node associated with remote endpoint node linked
- *	   to @node. Use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_remote_node(const struct device_node *node,
-					     u32 port, u32 endpoint)
-{
-	struct device_node *endpoint_node, *remote;
-
-	endpoint_node = of_graph_get_endpoint_by_regs(node, port, endpoint);
-	if (!endpoint_node) {
-		pr_debug("no valid endpoint (%d, %d) for node %s\n",
-			 port, endpoint, node->full_name);
-		return NULL;
-	}
-
-	remote = of_graph_get_remote_port_parent(endpoint_node);
-	of_node_put(endpoint_node);
-	if (!remote) {
-		pr_debug("no valid remote node\n");
-		return NULL;
-	}
-
-	if (!of_device_is_available(remote)) {
-		pr_debug("not available for remote node\n");
-		return NULL;
-	}
-
-	return remote;
-}
-EXPORT_SYMBOL(of_graph_get_remote_node);
diff --git a/drivers/of/property.c b/drivers/of/property.c
new file mode 100644
index 0000000..e5d3e1f
--- /dev/null
+++ b/drivers/of/property.c
@@ -0,0 +1,763 @@
+/*
+ * drivers/of/property.c - Procedures for accessing and interpreting
+ *			   Devicetree properties and graphs.
+ *
+ * Initially created by copying procedures from drivers/of/base.c. This
+ * file contains the OF property as well as the OF graph interface
+ * functions.
+ *
+ * Paul Mackerras	August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ *  Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
+ *    {engebret|bergner}@us.ibm.com
+ *
+ *  Adapted for sparc and sparc64 by David S. Miller davem@davemloft.net
+ *
+ *  Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell and
+ *  Grant Likely.
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/string.h>
+
+#include "of_private.h"
+/**
+ * of_property_count_elems_of_size - Count the number of elements in a property
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @elem_size:	size of the individual element
+ *
+ * Search for a property in a device node and count the number of elements of
+ * size elem_size in it. Returns number of elements on sucess, -EINVAL if the
+ * property does not exist or its length does not match a multiple of elem_size
+ * and -ENODATA if the property does not have a value.
+ */
+int of_property_count_elems_of_size(const struct device_node *np,
+				const char *propname, int elem_size)
+{
+	struct property *prop = of_find_property(np, propname, NULL);
+
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+
+	if (prop->length % elem_size != 0) {
+		pr_err("size of %s in node %s is not a multiple of %d\n",
+		       propname, np->full_name, elem_size);
+		return -EINVAL;
+	}
+
+	return prop->length / elem_size;
+}
+EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
+
+/**
+ * of_find_property_value_of_size
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @min:	minimum allowed length of property value
+ * @max:	maximum allowed length of property value (0 means unlimited)
+ * @len:	if !=NULL, actual length is written to here
+ *
+ * Search for a property in a device node and valid the requested size.
+ * Returns the property value on success, -EINVAL if the property does not
+ *  exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data is too small or too large.
+ *
+ */
+static void *of_find_property_value_of_size(const struct device_node *np,
+			const char *propname, u32 min, u32 max, size_t *len)
+{
+	struct property *prop = of_find_property(np, propname, NULL);
+
+	if (!prop)
+		return ERR_PTR(-EINVAL);
+	if (!prop->value)
+		return ERR_PTR(-ENODATA);
+	if (prop->length < min)
+		return ERR_PTR(-EOVERFLOW);
+	if (max && prop->length > max)
+		return ERR_PTR(-EOVERFLOW);
+
+	if (len)
+		*len = prop->length;
+
+	return prop->value;
+}
+
+/**
+ * of_property_read_u32_index - Find and read a u32 from a multi-value property.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @index:	index of the u32 in the list of values
+ * @out_value:	pointer to return value, modified only if no error.
+ *
+ * Search for a property in a device node and read nth 32-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_u32_index(const struct device_node *np,
+				       const char *propname,
+				       u32 index, u32 *out_value)
+{
+	const u32 *val = of_find_property_value_of_size(np, propname,
+					((index + 1) * sizeof(*out_value)),
+					0,
+					NULL);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	*out_value = be32_to_cpup(((__be32 *)val) + index);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u32_index);
+
+/**
+ * of_property_read_u64_index - Find and read a u64 from a multi-value property.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @index:	index of the u64 in the list of values
+ * @out_value:	pointer to return value, modified only if no error.
+ *
+ * Search for a property in a device node and read nth 64-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_u64_index(const struct device_node *np,
+				       const char *propname,
+				       u32 index, u64 *out_value)
+{
+	const u64 *val = of_find_property_value_of_size(np, propname,
+					((index + 1) * sizeof(*out_value)),
+					0, NULL);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	*out_value = be64_to_cpup(((__be64 *)val) + index);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u64_index);
+
+/**
+ * of_property_read_variable_u8_array - Find and read an array of u8 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_values:	pointer to return value, modified only if return value is 0.
+ * @sz_min:	minimum number of array elements to read
+ * @sz_max:	maximum number of array elements to read, if zero there is no
+ *		upper limit on the number of elements in the dts entry but only
+ *		sz_min will be read.
+ *
+ * Search for a property in a device node and read 8-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * dts entry of array should be like:
+ *	property = /bits/ 8 <0x50 0x60 0x70>;
+ *
+ * The out_values is modified only if a valid u8 value can be decoded.
+ */
+int of_property_read_variable_u8_array(const struct device_node *np,
+					const char *propname, u8 *out_values,
+					size_t sz_min, size_t sz_max)
+{
+	size_t sz, count;
+	const u8 *val = of_find_property_value_of_size(np, propname,
+						(sz_min * sizeof(*out_values)),
+						(sz_max * sizeof(*out_values)),
+						&sz);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	if (!sz_max)
+		sz = sz_min;
+	else
+		sz /= sizeof(*out_values);
+
+	count = sz;
+	while (count--)
+		*out_values++ = *val++;
+
+	return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u8_array);
+
+/**
+ * of_property_read_variable_u16_array - Find and read an array of u16 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_values:	pointer to return value, modified only if return value is 0.
+ * @sz_min:	minimum number of array elements to read
+ * @sz_max:	maximum number of array elements to read, if zero there is no
+ *		upper limit on the number of elements in the dts entry but only
+ *		sz_min will be read.
+ *
+ * Search for a property in a device node and read 16-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * dts entry of array should be like:
+ *	property = /bits/ 16 <0x5000 0x6000 0x7000>;
+ *
+ * The out_values is modified only if a valid u16 value can be decoded.
+ */
+int of_property_read_variable_u16_array(const struct device_node *np,
+					const char *propname, u16 *out_values,
+					size_t sz_min, size_t sz_max)
+{
+	size_t sz, count;
+	const __be16 *val = of_find_property_value_of_size(np, propname,
+						(sz_min * sizeof(*out_values)),
+						(sz_max * sizeof(*out_values)),
+						&sz);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	if (!sz_max)
+		sz = sz_min;
+	else
+		sz /= sizeof(*out_values);
+
+	count = sz;
+	while (count--)
+		*out_values++ = be16_to_cpup(val++);
+
+	return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u16_array);
+
+/**
+ * of_property_read_variable_u32_array - Find and read an array of 32 bit
+ * integers from a property, with bounds on the minimum and maximum array size.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_values:	pointer to return value, modified only if return value is 0.
+ * @sz_min:	minimum number of array elements to read
+ * @sz_max:	maximum number of array elements to read, if zero there is no
+ *		upper limit on the number of elements in the dts entry but only
+ *		sz_min will be read.
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_variable_u32_array(const struct device_node *np,
+			       const char *propname, u32 *out_values,
+			       size_t sz_min, size_t sz_max)
+{
+	size_t sz, count;
+	const __be32 *val = of_find_property_value_of_size(np, propname,
+						(sz_min * sizeof(*out_values)),
+						(sz_max * sizeof(*out_values)),
+						&sz);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	if (!sz_max)
+		sz = sz_min;
+	else
+		sz /= sizeof(*out_values);
+
+	count = sz;
+	while (count--)
+		*out_values++ = be32_to_cpup(val++);
+
+	return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u32_array);
+
+/**
+ * of_property_read_u64 - Find and read a 64 bit integer from a property
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_value:	pointer to return value, modified only if return value is 0.
+ *
+ * Search for a property in a device node and read a 64-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_u64(const struct device_node *np, const char *propname,
+			 u64 *out_value)
+{
+	const __be32 *val = of_find_property_value_of_size(np, propname,
+						sizeof(*out_value),
+						0,
+						NULL);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	*out_value = of_read_number(val, 2);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u64);
+
+/**
+ * of_property_read_variable_u64_array - Find and read an array of 64 bit
+ * integers from a property, with bounds on the minimum and maximum array size.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_values:	pointer to return value, modified only if return value is 0.
+ * @sz_min:	minimum number of array elements to read
+ * @sz_max:	maximum number of array elements to read, if zero there is no
+ *		upper limit on the number of elements in the dts entry but only
+ *		sz_min will be read.
+ *
+ * Search for a property in a device node and read 64-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_variable_u64_array(const struct device_node *np,
+			       const char *propname, u64 *out_values,
+			       size_t sz_min, size_t sz_max)
+{
+	size_t sz, count;
+	const __be32 *val = of_find_property_value_of_size(np, propname,
+						(sz_min * sizeof(*out_values)),
+						(sz_max * sizeof(*out_values)),
+						&sz);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	if (!sz_max)
+		sz = sz_min;
+	else
+		sz /= sizeof(*out_values);
+
+	count = sz;
+	while (count--) {
+		*out_values++ = of_read_number(val, 2);
+		val += 2;
+	}
+
+	return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u64_array);
+
+/**
+ * of_property_read_string - Find and read a string from a property
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_string:	pointer to null terminated return string, modified only if
+ *		return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy). Returns 0 on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+int of_property_read_string(const struct device_node *np, const char *propname,
+				const char **out_string)
+{
+	const struct property *prop = of_find_property(np, propname, NULL);
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+	if (strnlen(prop->value, prop->length) >= prop->length)
+		return -EILSEQ;
+	*out_string = prop->value;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_string);
+
+/**
+ * of_property_match_string() - Find string in a list and return index
+ * @np: pointer to node containing string list property
+ * @propname: string list property name
+ * @string: pointer to string to search for in string list
+ *
+ * This function searches a string list property and returns the index
+ * of a specific string value.
+ */
+int of_property_match_string(const struct device_node *np, const char *propname,
+			     const char *string)
+{
+	const struct property *prop = of_find_property(np, propname, NULL);
+	size_t l;
+	int i;
+	const char *p, *end;
+
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+
+	p = prop->value;
+	end = p + prop->length;
+
+	for (i = 0; p < end; i++, p += l) {
+		l = strnlen(p, end - p) + 1;
+		if (p + l > end)
+			return -EILSEQ;
+		pr_debug("comparing %s with %s\n", string, p);
+		if (strcmp(string, p) == 0)
+			return i; /* Found it; return index */
+	}
+	return -ENODATA;
+}
+EXPORT_SYMBOL_GPL(of_property_match_string);
+
+/**
+ * of_property_read_string_helper() - Utility helper for parsing string properties
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_strs:	output array of string pointers.
+ * @sz:		number of array elements to read.
+ * @skip:	Number of strings to skip over at beginning of list.
+ *
+ * Don't call this function directly. It is a utility helper for the
+ * of_property_read_string*() family of functions.
+ */
+int of_property_read_string_helper(const struct device_node *np,
+				   const char *propname, const char **out_strs,
+				   size_t sz, int skip)
+{
+	const struct property *prop = of_find_property(np, propname, NULL);
+	int l = 0, i = 0;
+	const char *p, *end;
+
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+	p = prop->value;
+	end = p + prop->length;
+
+	for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
+		l = strnlen(p, end - p) + 1;
+		if (p + l > end)
+			return -EILSEQ;
+		if (out_strs && i >= skip)
+			*out_strs++ = p;
+	}
+	i -= skip;
+	return i <= 0 ? -ENODATA : i;
+}
+EXPORT_SYMBOL_GPL(of_property_read_string_helper);
+
+const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
+			       u32 *pu)
+{
+	const void *curv = cur;
+
+	if (!prop)
+		return NULL;
+
+	if (!cur) {
+		curv = prop->value;
+		goto out_val;
+	}
+
+	curv += sizeof(*cur);
+	if (curv >= prop->value + prop->length)
+		return NULL;
+
+out_val:
+	*pu = be32_to_cpup(curv);
+	return curv;
+}
+EXPORT_SYMBOL_GPL(of_prop_next_u32);
+
+const char *of_prop_next_string(struct property *prop, const char *cur)
+{
+	const void *curv = cur;
+
+	if (!prop)
+		return NULL;
+
+	if (!cur)
+		return prop->value;
+
+	curv += strlen(cur) + 1;
+	if (curv >= prop->value + prop->length)
+		return NULL;
+
+	return curv;
+}
+EXPORT_SYMBOL_GPL(of_prop_next_string);
+
+/**
+ * of_graph_parse_endpoint() - parse common endpoint node properties
+ * @node: pointer to endpoint device_node
+ * @endpoint: pointer to the OF endpoint data structure
+ *
+ * The caller should hold a reference to @node.
+ */
+int of_graph_parse_endpoint(const struct device_node *node,
+			    struct of_endpoint *endpoint)
+{
+	struct device_node *port_node = of_get_parent(node);
+
+	WARN_ONCE(!port_node, "%s(): endpoint %s has no parent node\n",
+		  __func__, node->full_name);
+
+	memset(endpoint, 0, sizeof(*endpoint));
+
+	endpoint->local_node = node;
+	/*
+	 * It doesn't matter whether the two calls below succeed.
+	 * If they don't then the default value 0 is used.
+	 */
+	of_property_read_u32(port_node, "reg", &endpoint->port);
+	of_property_read_u32(node, "reg", &endpoint->id);
+
+	of_node_put(port_node);
+
+	return 0;
+}
+EXPORT_SYMBOL(of_graph_parse_endpoint);
+
+/**
+ * of_graph_get_port_by_id() - get the port matching a given id
+ * @parent: pointer to the parent device node
+ * @id: id of the port
+ *
+ * Return: A 'port' node pointer with refcount incremented. The caller
+ * has to use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
+{
+	struct device_node *node, *port;
+
+	node = of_get_child_by_name(parent, "ports");
+	if (node)
+		parent = node;
+
+	for_each_child_of_node(parent, port) {
+		u32 port_id = 0;
+
+		if (of_node_cmp(port->name, "port") != 0)
+			continue;
+		of_property_read_u32(port, "reg", &port_id);
+		if (id == port_id)
+			break;
+	}
+
+	of_node_put(node);
+
+	return port;
+}
+EXPORT_SYMBOL(of_graph_get_port_by_id);
+
+/**
+ * of_graph_get_next_endpoint() - get next endpoint node
+ * @parent: pointer to the parent device node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
+					struct device_node *prev)
+{
+	struct device_node *endpoint;
+	struct device_node *port;
+
+	if (!parent)
+		return NULL;
+
+	/*
+	 * Start by locating the port node. If no previous endpoint is specified
+	 * search for the first port node, otherwise get the previous endpoint
+	 * parent port node.
+	 */
+	if (!prev) {
+		struct device_node *node;
+
+		node = of_get_child_by_name(parent, "ports");
+		if (node)
+			parent = node;
+
+		port = of_get_child_by_name(parent, "port");
+		of_node_put(node);
+
+		if (!port) {
+			pr_err("graph: no port node found in %s\n",
+			       parent->full_name);
+			return NULL;
+		}
+	} else {
+		port = of_get_parent(prev);
+		if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
+			      __func__, prev->full_name))
+			return NULL;
+	}
+
+	while (1) {
+		/*
+		 * Now that we have a port node, get the next endpoint by
+		 * getting the next child. If the previous endpoint is NULL this
+		 * will return the first child.
+		 */
+		endpoint = of_get_next_child(port, prev);
+		if (endpoint) {
+			of_node_put(port);
+			return endpoint;
+		}
+
+		/* No more endpoints under this port, try the next one. */
+		prev = NULL;
+
+		do {
+			port = of_get_next_child(parent, port);
+			if (!port)
+				return NULL;
+		} while (of_node_cmp(port->name, "port"));
+	}
+}
+EXPORT_SYMBOL(of_graph_get_next_endpoint);
+
+/**
+ * of_graph_get_endpoint_by_regs() - get endpoint node of specific identifiers
+ * @parent: pointer to the parent device node
+ * @port_reg: identifier (value of reg property) of the parent port node
+ * @reg: identifier (value of reg property) of the endpoint node
+ *
+ * Return: An 'endpoint' node pointer which is identified by reg and at the same
+ * is the child of a port node identified by port_reg. reg and port_reg are
+ * ignored when they are -1.
+ */
+struct device_node *of_graph_get_endpoint_by_regs(
+	const struct device_node *parent, int port_reg, int reg)
+{
+	struct of_endpoint endpoint;
+	struct device_node *node = NULL;
+
+	for_each_endpoint_of_node(parent, node) {
+		of_graph_parse_endpoint(node, &endpoint);
+		if (((port_reg == -1) || (endpoint.port == port_reg)) &&
+			((reg == -1) || (endpoint.id == reg)))
+			return node;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
+
+/**
+ * of_graph_get_remote_port_parent() - get remote port's parent node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote device node associated with remote endpoint node linked
+ *	   to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_port_parent(
+			       const struct device_node *node)
+{
+	struct device_node *np;
+	unsigned int depth;
+
+	/* Get remote endpoint node. */
+	np = of_parse_phandle(node, "remote-endpoint", 0);
+
+	/* Walk 3 levels up only if there is 'ports' node. */
+	for (depth = 3; depth && np; depth--) {
+		np = of_get_next_parent(np);
+		if (depth == 2 && of_node_cmp(np->name, "ports"))
+			break;
+	}
+	return np;
+}
+EXPORT_SYMBOL(of_graph_get_remote_port_parent);
+
+/**
+ * of_graph_get_remote_port() - get remote port node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote port node associated with remote endpoint node linked
+ *	   to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_port(const struct device_node *node)
+{
+	struct device_node *np;
+
+	/* Get remote endpoint node. */
+	np = of_parse_phandle(node, "remote-endpoint", 0);
+	if (!np)
+		return NULL;
+	return of_get_next_parent(np);
+}
+EXPORT_SYMBOL(of_graph_get_remote_port);
+
+/**
+ * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
+ * @node: pointer to parent device_node containing graph port/endpoint
+ * @port: identifier (value of reg property) of the parent port node
+ * @endpoint: identifier (value of reg property) of the endpoint node
+ *
+ * Return: Remote device node associated with remote endpoint node linked
+ *	   to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_node(const struct device_node *node,
+					     u32 port, u32 endpoint)
+{
+	struct device_node *endpoint_node, *remote;
+
+	endpoint_node = of_graph_get_endpoint_by_regs(node, port, endpoint);
+	if (!endpoint_node) {
+		pr_debug("no valid endpoint (%d, %d) for node %s\n",
+			 port, endpoint, node->full_name);
+		return NULL;
+	}
+
+	remote = of_graph_get_remote_port_parent(endpoint_node);
+	of_node_put(endpoint_node);
+	if (!remote) {
+		pr_debug("no valid remote node\n");
+		return NULL;
+	}
+
+	if (!of_device_is_available(remote)) {
+		pr_debug("not available for remote node\n");
+		return NULL;
+	}
+
+	return remote;
+}
+EXPORT_SYMBOL(of_graph_get_remote_node);
-- 
2.7.4


^ permalink raw reply related

* [PATCH v5 5/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-veyron
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
	devicetree, linux-rockchip, linux-arm-kernel, linux-kernel,
	Guillaume Tucker
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

From: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Add reference to the Mali GPU device tree node on rk3288-veyron.
Tested on Minnie and Jerry boards.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 arch/arm/boot/dts/rk3288-veyron.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-veyron.dtsi b/arch/arm/boot/dts/rk3288-veyron.dtsi
index 5d1eb0a25827..9847d5c6db3b 100644
--- a/arch/arm/boot/dts/rk3288-veyron.dtsi
+++ b/arch/arm/boot/dts/rk3288-veyron.dtsi
@@ -447,6 +447,11 @@
 	status = "okay";
 };
 
+&gpu {
+	mali-supply = <&vdd_gpu>;
+	status = "okay";
+};
+
 &wdt {
 	status = "okay";
 };
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 4/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-firefly
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
	linux-rockchip, John Reitan, Enric Balletbo i Serra,
	linux-arm-kernel
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

Add reference to the Mali GPU device tree node on rk3288-firefly.
Tested on Firefly board.

Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 arch/arm/boot/dts/rk3288-firefly.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 10793ac18599..f520589493b4 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -594,3 +594,8 @@
 &wdt {
 	status = "okay";
 };
+
+&gpu {
+	mali-supply = <&vdd_gpu>;
+	status = "okay";
+};
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 3/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-rock2-som
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
	linux-rockchip, John Reitan, Enric Balletbo i Serra,
	linux-arm-kernel
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

Add reference to the Mali GPU device tree node on the
rk3288-rock2-som platform.  Tested on a Radxa Rock2 Square board.

Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 arch/arm/boot/dts/rk3288-rock2-som.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-rock2-som.dtsi b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
index 1c0bbc9b928b..f694867fa46a 100644
--- a/arch/arm/boot/dts/rk3288-rock2-som.dtsi
+++ b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
@@ -301,3 +301,8 @@
 &wdt {
 	status = "okay";
 };
+
+&gpu {
+	mali-supply = <&vdd_gpu>;
+	status = "okay";
+};
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 2/5] ARM: dts: rockchip: add ARM Mali GPU node for rk3288
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
	linux-rockchip, John Reitan, Enric Balletbo i Serra,
	linux-arm-kernel
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

Add Mali GPU device tree node for the rk3288 SoC, with devfreq
opp table.

Tested-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 arch/arm/boot/dts/rk3288.dtsi | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index df8a0dbe9d91..35969041eae2 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -43,6 +43,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/clock/rk3288-cru.h>
+#include <dt-bindings/power/rk3288-power.h>
 #include <dt-bindings/thermal/thermal.h>
 #include <dt-bindings/power/rk3288-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
@@ -1117,6 +1118,48 @@
 		};
 	};
 
+	gpu: mali@ffa30000 {
+		compatible = "rockchip,rk3288-mali", "arm,mali-t760", "arm,mali-midgard";
+		reg = <0xffa30000 0x10000>;
+		interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "job", "mmu", "gpu";
+		clocks = <&cru ACLK_GPU>;
+		operating-points-v2 = <&gpu_opp_table>;
+		power-domains = <&power RK3288_PD_GPU>;
+		status = "disabled";
+	};
+
+	gpu_opp_table: opp_table0 {
+		compatible = "operating-points-v2";
+
+		opp@100000000 {
+			opp-hz = /bits/ 64 <100000000>;
+			opp-microvolt = <950000>;
+		};
+		opp@200000000 {
+			opp-hz = /bits/ 64 <200000000>;
+			opp-microvolt = <950000>;
+		};
+		opp@300000000 {
+			opp-hz = /bits/ 64 <300000000>;
+			opp-microvolt = <1000000>;
+		};
+		opp@400000000 {
+			opp-hz = /bits/ 64 <400000000>;
+			opp-microvolt = <1100000>;
+		};
+		opp@500000000 {
+			opp-hz = /bits/ 64 <500000000>;
+			opp-microvolt = <1200000>;
+		};
+		opp@600000000 {
+			opp-hz = /bits/ 64 <600000000>;
+			opp-microvolt = <1250000>;
+		};
+	};
+
 	qos_gpu_r: qos@ffaa0000 {
 		compatible = "syscon";
 		reg = <0xffaa0000 0x20>;
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 1/5] dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
	devicetree, linux-rockchip, linux-arm-kernel, linux-kernel,
	Guillaume Tucker
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

The ARM Mali Midgard GPU family is present in a number of SoCs
from many different vendors such as Samsung Exynos and Rockchip.

Import the device tree bindings documentation from the r16p0
release of the Mali Midgard GPU kernel driver:

  https://developer.arm.com/-/media/Files/downloads/mali-drivers/kernel/mali-midgard-gpu/TX011-SW-99002-r16p0-00rel0.tgz

Remove the copyright and GPL licence header as deemed not necessary.

Redesign the "compatible" property strings to list all the Mali
Midgard GPU types and add vendor specific ones.

Drop the "clock-names" property as the Mali Midgard GPU uses only one
clock (the driver now needs to call clk_get with NULL).

Convert the "interrupt-names" property values to lower-case: "job",
"mmu" and "gpu".

Replace the deprecated "operating-points" optional property with
"operating-points-v2".

Omit the following optional properties in this initial version as they
are only used in very specific cases:

  * snoop_enable_smc
  * snoop_disable_smc
  * jm_config
  * power_model
  * system-coherency
  * ipa-model

Update the example accordingly to reflect all these changes, based on
rk3288 mali-t760.

CC: John Reitan <john.reitan@arm.com>
Tested-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 .../devicetree/bindings/gpu/arm,mali-midgard.txt   | 86 ++++++++++++++++++++++
 1 file changed, 86 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt

diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
new file mode 100644
index 000000000000..d3b6e1a4713a
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
@@ -0,0 +1,86 @@
+ARM Mali Midgard GPU
+====================
+
+Required properties:
+
+- compatible :
+  * Must contain one of the following:
+    + "arm,mali-t604"
+    + "arm,mali-t624"
+    + "arm,mali-t628"
+    + "arm,mali-t720"
+    + "arm,mali-t760"
+    + "arm,mali-t820"
+    + "arm,mali-t830"
+    + "arm,mali-t860"
+    + "arm,mali-t880"
+  * which must be preceded by one of the following vendor specifics:
+    + "amlogic,meson-gxm-mali"
+    + "rockchip,rk3288-mali"
+
+- reg : Physical base address of the device and length of the register area.
+
+- interrupts : Contains the three IRQ lines required by Mali Midgard devices.
+
+- interrupt-names : Contains the names of IRQ resources in the order they were
+  provided in the interrupts property. Must contain: "job", "mmu", "gpu".
+
+
+Optional properties:
+
+- clocks : Phandle to clock for the Mali Midgard device.
+
+- mali-supply : Phandle to regulator for the Mali device. Refer to
+  Documentation/devicetree/bindings/regulator/regulator.txt for details.
+
+- operating-points-v2 : Refer to Documentation/devicetree/bindings/power/opp.txt
+  for details.
+
+
+Example for a Mali-T760:
+
+gpu@ffa30000 {
+	compatible = "rockchip,rk3288-mali", "arm,mali-t760", "arm,mali-midgard";
+	reg = <0xffa30000 0x10000>;
+	interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+		     <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+		     <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+	interrupt-names = "job", "mmu", "gpu";
+	clocks = <&cru ACLK_GPU>;
+	mali-supply = <&vdd_gpu>;
+	operating-points-v2 = <&gpu_opp_table>;
+	power-domains = <&power RK3288_PD_GPU>;
+};
+
+gpu_opp_table: opp_table0 {
+	compatible = "operating-points-v2";
+
+	opp@533000000 {
+		opp-hz = /bits/ 64 <533000000>;
+		opp-microvolt = <1250000>;
+	};
+	opp@450000000 {
+		opp-hz = /bits/ 64 <450000000>;
+		opp-microvolt = <1150000>;
+	};
+	opp@400000000 {
+		opp-hz = /bits/ 64 <400000000>;
+		opp-microvolt = <1125000>;
+	};
+	opp@350000000 {
+		opp-hz = /bits/ 64 <350000000>;
+		opp-microvolt = <1075000>;
+	};
+	opp@266000000 {
+		opp-hz = /bits/ 64 <266000000>;
+		opp-microvolt = <1025000>;
+	};
+	opp@160000000 {
+		opp-hz = /bits/ 64 <160000000>;
+		opp-microvolt = <925000>;
+	};
+	opp@100000000 {
+		opp-hz = /bits/ 64 <100000000>;
+		opp-microvolt = <912500>;
+	};
+};
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 0/5] Add ARM Mali Midgard device tree bindings and gpu node for rk3288
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Guillaume Tucker

The ARM Mali Midgard GPU kernel driver is only available
out-of-tree and is not going to be merged in its current form.
However, it would be useful to have its device tree bindings
merged.  In particular, this would enable distributions to create
working driver packages (dkms...) without having to patch the
kernel.

The bindings for the earlier Mali Utgard GPU family have already
been merged, so this is essentially the same scenario but for
newer GPUs (Mali-T604 ~ Mali-T880).

This series of patches first imports the bindings from the latest
driver release with some clean-up then adds a gpu node for the
rk3288 SoC.  This was successfully tested on Radxa Rock2 Square,
Firefly, Veyron Minnie and Jerry boards using Mali kernel driver
r16p0 and r12p0 user-space binary.


Changes since v1:
- enabled gpu on rk3288-veyron boards

Changes since v2:
- removed "clk-names" property and "clk_mali" name
- converted values of "interrupt-names" property to
  lower-case: "job", "mmu" and "gpu"
- replaced dt compatible strings with list of all Midgard GPU variants and
  optional vendors
- cleaned up gpu node example

Changes since v3:
- add "rockchip,rk3288-mali" vendor compatible string
- move gpu node at the right location in rk3288.dtsi
- use operating-points-v2 in documentation and rk3288.dtsi

Changes since v4:
- removed wildcards (mali-t60x, -t62x)
- vendor compatible strings are not optional any more
- example updated accordingly, based on rk3288


Enric Balletbo i Serra (1):
  ARM: dts: rockchip: enable ARM Mali GPU on rk3288-veyron

Guillaume Tucker (4):
  dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
  ARM: dts: rockchip: add ARM Mali GPU node for rk3288
  ARM: dts: rockchip: enable ARM Mali GPU on rk3288-rock2-som
  ARM: dts: rockchip: enable ARM Mali GPU on rk3288-firefly

 .../devicetree/bindings/gpu/arm,mali-midgard.txt   | 86 ++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi              |  5 ++
 arch/arm/boot/dts/rk3288-rock2-som.dtsi            |  5 ++
 arch/arm/boot/dts/rk3288-veyron.dtsi               |  5 ++
 arch/arm/boot/dts/rk3288.dtsi                      | 43 +++++++++++
 5 files changed, 144 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt

--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
From: Jassi Brar @ 2017-05-03  9:55 UTC (permalink / raw)
  To: Loic PALLARDY
  Cc: Bjorn Andersson, Andy Gross, Rob Herring, Mark Rutland,
	Ohad Ben-Cohen,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <c9b86ecfa6b84ae994da6c30d2840bbf-GGltqRL9kSEdIAuSVraBWEEOCMrvLtNR@public.gmane.org>

Loic, thanks for adding me.

On Wed, May 3, 2017 at 2:58 PM, Loic PALLARDY <loic.pallardy-qxv4g6HH51o@public.gmane.org> wrote:
>
>
>> -----Original Message-----
>> From: linux-remoteproc-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-remoteproc-
>> owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Bjorn Andersson
>> Sent: Wednesday, May 03, 2017 7:29 AM
>> To: Andy Gross <andy.gross-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>; Rob Herring
>> <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>; Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>; Ohad Ben-
>> Cohen <ohad-Ix1uc/W3ht7QT0dZR+AlfA@public.gmane.org>
>> Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
>> devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-
>> remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> Subject: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
>>
>> This implements a driver that exposes the IPC bits found in the APCS Global
>> block in various Qualcomm platforms. The bits are used to signal inter-
>> processor communication signals from the application CPU to other masters.
>>
>> The driver implements the "doorbell" binding and could be used as basis for a
>> new Linux framework, if found useful outside Qualcomm.
>>
> Hi Bjorn,
>
> Even if Qualcom APCS IPC is limited, why don't you rely on existing mailbox framework.
> It is there to gather all IPC management under the same interface.
> No need to create a new one from my pov.
> If you don't provide message data, mailbox framework behaves as doorbell.
>
QCOM RPM reinvented the wheel for what mailbox framework already did,
despite my pointing it out =>
http://lkml.iu.edu/hypermail//linux/kernel/1406.2/03918.html

The driver bypassed mailbox framework and was pushed via another tree.
Same is being attempted now, only now it is more expensive to switch
to generic mailbox framework having spent so much time on QCOM
specific implementation of controller and protocol drivers inside
drivers/soc/qcom/
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 3/4] net: macb: Add hardware PTP support
From: Richard Cochran @ 2017-05-03  9:43 UTC (permalink / raw)
  To: Rafal Ozieblo
  Cc: David Miller,
	nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org,
	netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	harinikatakamlinux-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	harini.katakam-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org,
	Andrei.Pistirica-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org
In-Reply-To: <BN3PR07MB2516757CB4EA7367F6623C06C9170-EldUQEzkDQfxGZiqM5fOI+FPX92sqiQdvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>

On Tue, May 02, 2017 at 01:57:15PM +0000, Rafal Ozieblo wrote:
> > What is the point of this wrapper function anyhow?  Please remove it.
> gem_ptp_gettime() is assigned in ptp_clock_info and it has to have 
> ptp_clock_info pointer as first parameter. gem_tsu_get_time() is used in
> the source code but with macb pointer.
> Do you want me to do something like:
> gem_ptp_gettime(macb->ptp, ts);
> and first would be getting macb pointer from ptp ?
> struct macb *bp = container_of(ptp, struct macb, ptp_clock_info);

Yes.  Unless your sub-function is used in more than one place, then it
is wasteful and confusing to wrap the functionality for no apparent
reason.

> > > +	switch (rq->type) {
> > > +	case PTP_CLK_REQ_EXTTS:	/* Toggle TSU match interrupt */
> > > +		if (on)
> > > +			macb_writel(bp, IER, MACB_BIT(TCI));
> > 
> > No locking to protect IER and IDE?
> There is no need.

But what happens when the PTP_CLK_REQ_EXTTS and PTP_CLK_REQ_PPS ioctls
are called at the same time?

You need to ensure that IDR is consistent.  If the bits are write
only, then you should comment this fact.

> > > +		else
> > > +			macb_writel(bp, IDR, MACB_BIT(TCI));
> > > +		break;
> > > +	case PTP_CLK_REQ_PEROUT: /* Toggle Periodic output */
> > > +		return -EOPNOTSUPP;
> > > +		/* break; */
> > > +	case PTP_CLK_REQ_PPS:	/* Toggle TSU periodic (second)
> > interrupt */
> > > +		if (on)
> > > +			macb_writel(bp, IER, MACB_BIT(SRI));
> > > +		else
> > > +			macb_writel(bp, IDR, MACB_BIT(SRI));
> > > +		break;
> > > +	default:
> > > +		break;
> > > +	}
> > > +	return 0;
> > > +}

Thanks,
Richard
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* RE: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
From: Loic PALLARDY @ 2017-05-03  9:28 UTC (permalink / raw)
  To: Bjorn Andersson, Andy Gross, Rob Herring, Mark Rutland,
	Ohad Ben-Cohen,
	jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
  Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20170503052929.17422-2-bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>



> -----Original Message-----
> From: linux-remoteproc-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-remoteproc-
> owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Bjorn Andersson
> Sent: Wednesday, May 03, 2017 7:29 AM
> To: Andy Gross <andy.gross-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>; Rob Herring
> <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>; Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>; Ohad Ben-
> Cohen <ohad-Ix1uc/W3ht7QT0dZR+AlfA@public.gmane.org>
> Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
> devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-
> remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Subject: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
> 
> This implements a driver that exposes the IPC bits found in the APCS Global
> block in various Qualcomm platforms. The bits are used to signal inter-
> processor communication signals from the application CPU to other masters.
> 
> The driver implements the "doorbell" binding and could be used as basis for a
> new Linux framework, if found useful outside Qualcomm.
> 
Hi Bjorn,

Even if Qualcom APCS IPC is limited, why don't you rely on existing mailbox framework.
It is there to gather all IPC management under the same interface.
No need to create a new one from my pov.
If you don't provide message data, mailbox framework behaves as doorbell.

Regards,
Loic

> Signed-off-by: Bjorn Andersson <bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
> 
> Changes since v2:
> - New driver
> 
>  drivers/soc/qcom/Kconfig          |   8 ++
>  drivers/soc/qcom/Makefile         |   1 +
>  drivers/soc/qcom/apcs-ipc.c       | 182
> ++++++++++++++++++++++++++++++++++++++
>  include/linux/soc/qcom/apcs_ipc.h |  26 ++++++
>  4 files changed, 217 insertions(+)
>  create mode 100644 drivers/soc/qcom/apcs-ipc.c  create mode 100644
> include/linux/soc/qcom/apcs_ipc.h
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index
> 78b1bb7bcf20..4113da81d18b 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -1,6 +1,14 @@
>  #
>  # QCOM Soc drivers
>  #
> +config QCOM_APCS_IPC
> +	tristate "Qualcomm APCS IPC driver"
> +	depends on ARCH_QCOM
> +	help
> +	  Say y here to enable support for the APCS IPC doorbell driver,
> +	  providing an interface for invoking the inter-process communication
> +	  signals from the application processor to other masters.
> +
>  config QCOM_GSBI
>          tristate "QCOM General Serial Bus Interface"
>          depends on ARCH_QCOM
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index
> 1f30260b06b8..e15b33e5a630 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,3 +1,4 @@
> +obj-$(CONFIG_QCOM_APCS_IPC) +=	apcs-ipc.o
>  obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>  obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
>  obj-$(CONFIG_QCOM_PM)	+=	spm.o
> diff --git a/drivers/soc/qcom/apcs-ipc.c b/drivers/soc/qcom/apcs-ipc.c new
> file mode 100644 index 000000000000..ea835cb08657
> --- /dev/null
> +++ b/drivers/soc/qcom/apcs-ipc.c
> @@ -0,0 +1,182 @@
> +/*
> + * Copyright (c) 2017, Linaro Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +static struct platform_driver qcom_apcs_ipc_driver;
> +
> +struct qcom_apcs_ipc {
> +	struct device *dev;
> +
> +	void __iomem *base;
> +	unsigned long offset;
> +};
> +
> +struct qcom_apcs_ipc_bell {
> +	struct qcom_apcs_ipc *apcs;
> +	unsigned int bit;
> +};
> +
> +static void qcom_apcs_ipc_release(struct device *dev, void *res) {
> +	struct qcom_apcs_ipc_bell *bell = res;
> +	struct qcom_apcs_ipc *apcs = bell->apcs;
> +
> +	put_device(apcs->dev);
> +}
> +
> +/**
> + * qcom_apcs_ipc_get() - acquire a handle to a doorbell
> + * @dev:	client device handle
> + * @id:		identifier of the doorbell
> + *
> + * Returns a doorbell reference, or negative errno on failure.
> + */
> +struct qcom_apcs_ipc_bell *devm_qcom_apcs_ipc_get(struct device *dev,
> +						  const char *id)
> +{
> +	struct qcom_apcs_ipc_bell *bell;
> +	struct platform_device *pdev;
> +	struct of_phandle_args args;
> +	int index = 0;
> +	int ret;
> +
> +	if (id) {
> +		index = of_property_match_string(dev->of_node,
> +						 "doorbell-names", id);
> +		if (index < 0)
> +			return ERR_PTR(index);
> +	}
> +
> +	ret = of_parse_phandle_with_args(dev->of_node, "doorbells",
> +					 "#doorbell-cells", index, &args);
> +	if (ret) {
> +		dev_err(dev, "unable to resolve doorbell\n");
> +		return ERR_PTR(-ENODEV);
> +	}
> +
> +	pdev = of_find_device_by_node(args.np);
> +	of_node_put(args.np);
> +
> +	if (!pdev)
> +		return ERR_PTR(-EPROBE_DEFER);
> +
> +	if (args.args[0] >= 32) {
> +		dev_err(dev, "invalid doorbell requested\n");
> +		ret = -EINVAL;
> +		goto release_device;
> +	}
> +
> +	if (pdev->dev.driver != &qcom_apcs_ipc_driver.driver) {
> +		dev_err(dev, "failed to acquire apcs ipc driver\n");
> +		ret = -EINVAL;
> +		goto release_device;
> +	}
> +
> +	bell = devres_alloc(qcom_apcs_ipc_release, sizeof(*bell),
> GFP_KERNEL);
> +	if (!bell) {
> +		ret = -ENOMEM;
> +		goto release_device;
> +	}
> +
> +	bell->apcs = platform_get_drvdata(pdev);
> +	bell->bit = args.args[0];
> +
> +	devres_add(dev, bell);
> +
> +	return bell;
> +
> +release_device:
> +	put_device(&pdev->dev);
> +
> +	return ERR_PTR(ret);
> +
> +}
> +EXPORT_SYMBOL_GPL(devm_qcom_apcs_ipc_get);
> +
> +/**
> + * qcom_apcs_ipc_ring() - ring the doorbell
> + * @bell:	doorbell to ring
> + */
> +void qcom_apcs_ipc_ring(struct qcom_apcs_ipc_bell *bell) {
> +	struct qcom_apcs_ipc *apcs = bell->apcs;
> +
> +	writel(BIT(bell->bit), apcs->base + apcs->offset); }
> +EXPORT_SYMBOL_GPL(qcom_apcs_ipc_ring);
> +
> +static int qcom_apcs_ipc_probe(struct platform_device *pdev) {
> +	struct qcom_apcs_ipc *apcs;
> +	struct resource *res;
> +
> +	apcs = devm_kzalloc(&pdev->dev, sizeof(*apcs), GFP_KERNEL);
> +	if (!apcs)
> +		return -ENOMEM;
> +
> +	apcs->dev = &pdev->dev;
> +	apcs->offset = (unsigned long)of_device_get_match_data(&pdev-
> >dev);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	apcs->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(apcs->base))
> +		return PTR_ERR(apcs->base);
> +
> +	platform_set_drvdata(pdev, apcs);
> +
> +	return 0;
> +}
> +
> +static int qcom_apcs_ipc_remove(struct platform_device *pdev) {
> +	return 0;
> +}
> +
> +/* .data is the offset of the ipc register within the global block */
> +static const struct of_device_id qcom_apcs_ipc_of_match[] = {
> +	{ .compatible = "qcom,msm8916-apcs-kpss-global", .data = (void *)8
> },
> +	{ .compatible = "qcom,msm8996-apcs-hmss-global", .data = (void
> *)16 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match);
> +
> +static struct platform_driver qcom_apcs_ipc_driver = {
> +	.probe = qcom_apcs_ipc_probe,
> +	.remove = qcom_apcs_ipc_remove,
> +	.driver = {
> +		.name = "qcom_apcs_ipc",
> +		.of_match_table = qcom_apcs_ipc_of_match,
> +	},
> +};
> +
> +static int __init qcom_apcs_ipc_init(void) {
> +	return platform_driver_register(&qcom_apcs_ipc_driver);
> +}
> +postcore_initcall(qcom_apcs_ipc_init);
> +
> +static void __exit qcom_apcs_ipc_exit(void) {
> +	platform_driver_unregister(&qcom_apcs_ipc_driver);
> +}
> +module_exit(qcom_apcs_ipc_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Qualcomm APCS IPC driver");
> diff --git a/include/linux/soc/qcom/apcs_ipc.h
> b/include/linux/soc/qcom/apcs_ipc.h
> new file mode 100644
> index 000000000000..72be77555261
> --- /dev/null
> +++ b/include/linux/soc/qcom/apcs_ipc.h
> @@ -0,0 +1,26 @@
> +#ifndef __QCOM_APCS_IPC_H__
> +#define __QCOM_APCS_IPC_H__
> +
> +#include <linux/err.h>
> +
> +struct device;
> +struct qcom_apcs_ipc_bell;
> +
> +#if IS_ENABLED(CONFIG_QCOM_APCS_IPC)
> +
> +struct qcom_apcs_ipc_bell *devm_qcom_apcs_ipc_get(struct device *dev,
> +						  const char *id);
> +void qcom_apcs_ipc_ring(struct qcom_apcs_ipc_bell *bell);
> +
> +#else
> +
> +static inline struct qcom_apcs_ipc_bell *devm_qcom_apcs_ipc_get(struct
> device *dev,
> +								const char
> *id)
> +{
> +	return ERR_PTR(-EINVAL);
> +}
> +
> +static inline void qcom_apcs_ipc_ring(struct qcom_apcs_ipc_bell *bell)
> +{}
> +
> +#endif
> +#endif
> --
> 2.12.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-remoteproc" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo
> info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCHv2 2/2] iio: adc: add driver for the ti-adc084s021 chip
From: Mårten Lindahl @ 2017-05-03  9:13 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mårten Lindahl, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <88d6c17d-8eae-6a41-b304-5224a7a2194a-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

On Sun, 2017-04-30 at 16:51 +0100, Jonathan Cameron wrote:
> On 30/04/17 13:53, Mårten Lindahl wrote:
> > From: Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>
> > 
> > This adds support for the Texas Instruments ADC084S021 ADC chip.
> > 
> > Signed-off-by: Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>
> A few more bits inline.  Mostly stuff that has come up
> in the V2 changes (and the inevitable bits I missed the
> first time!)
> 
> Jonathan

Hi Jonathan! Please see my comments. I will send v3 today.
Thanks,
Mårten

> > ---
> > Changes in v2:
> > - Removed most #defines in favor of inlines
> > - Corrected channel macro
> > - Removed configuration array with only one item
> > - Updated func adc084s021_adc_conversion to use be16_to_cpu
> > - Added IIO_CHAN_INFO_SCALE to func adc084s021_read_raw
> > - Use iio_device_claim_direct_mode in func adc084s021_read_raw
> > - Removed documentation for standard driver functions
> > - Changed retval to ret everywhere
> > - Removed dynamic alloc for data buffer in trigger handler
> > - Keeping mutex for all iterations in trigger handler
> > - Removed usage of events in this driver
> > - Removed info log in probe
> > - Use spi_message_init_with_transfers for spi message structs
> > - Use preenable and postdisable functions for regulator
> > - Inserted blank line before last return in all functions
> > 
> >  drivers/iio/adc/Kconfig         |  12 ++
> >  drivers/iio/adc/Makefile        |   1 +
> >  drivers/iio/adc/ti-adc084s021.c | 294 ++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 307 insertions(+)
> >  create mode 100644 drivers/iio/adc/ti-adc084s021.c
> > 
> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > index dedae7a..13141e5 100644
> > --- a/drivers/iio/adc/Kconfig
> > +++ b/drivers/iio/adc/Kconfig
> > @@ -560,6 +560,18 @@ config TI_ADC0832
> >  	  This driver can also be built as a module. If so, the module will be
> >  	  called ti-adc0832.
> >  
> > +config TI_ADC084S021
> > +	tristate "Texas Instruments ADC084S021"
> > +	depends on SPI
> > +	select IIO_BUFFER
> > +	select IIO_TRIGGERED_BUFFER
> > +	help
> > +	  If you say yes here you get support for Texas Instruments ADC084S021
> > +	  chips.
> > +
> > +	  This driver can also be built as a module. If so, the module will be
> > +	  called ti-adc084s021.
> > +
> >  config TI_ADC12138
> >  	tristate "Texas Instruments ADC12130/ADC12132/ADC12138"
> >  	depends on SPI
> > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> > index d001262..b1a6158 100644
> > --- a/drivers/iio/adc/Makefile
> > +++ b/drivers/iio/adc/Makefile
> > @@ -51,6 +51,7 @@ obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
> >  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> >  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> >  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> > +obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
> >  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> >  obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
> >  obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
> > diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
> > new file mode 100644
> > index 0000000..2dce257
> > --- /dev/null
> > +++ b/drivers/iio/adc/ti-adc084s021.c
> > @@ -0,0 +1,294 @@
> > +/**
> > + * Copyright (C) 2017 Axis Communications AB
> > + *
> > + * Driver for Texas Instruments' ADC084S021 ADC chip.
> > + * Datasheets can be found here:
> > + * http://www.ti.com/lit/ds/symlink/adc084s021.pdf
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/spi/spi.h>
> > +#include <linux/module.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +#include <linux/iio/trigger_consumer.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +#define ADC084S021_DRIVER_NAME "adc084s021"
> > +
> > +struct adc084s021 {
> > +	struct spi_device *spi;
> > +	struct spi_message message;
> > +	struct spi_transfer spi_trans[2];
> > +	struct regulator *reg;
> > +	struct mutex lock;
> > +	/*
> > +	 * DMA (thus cache coherency maintenance) requires the
> > +	 * transfer buffers to live in their own cache lines.
> > +	 */
> > +	union {
> > +		u16 tx_buf;
> > +		__be16 rx_buf;
> > +	} ____cacheline_aligned;
> > +};
> > +
> > +/**
> > + * Channel specification
> > + */
> Comment doesn't add much so I'd drop it.
Fixed in v3.
> > +#define ADC084S021_VOLTAGE_CHANNEL(num)                  \
> > +	{                                                      \
> > +		.type = IIO_VOLTAGE,                                 \
> > +		.channel = (num),                                    \
> > +		.address = (num) << 3,                               \
> This does feel a little pointless as you can just do the shift inline and
> use the channel value instead.  Doesn't really matter though.
Ok, fixed in v3.
> > +		.indexed = 1,                                        \
> > +		.scan_index = (num),                                 \
> > +		.scan_type = {                                       \
> > +			.sign = 'u',                                       \
> > +			.realbits = 8,                                     \
> > +			.storagebits = 16,                                 \
> > +			.shift = 4,                                        \
> > +			.endianness = IIO_BE,                              \
> > +		},                                                   \
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),        \
> > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
> > +	}
> > +
> > +static const struct iio_chan_spec adc084s021_channels[] = {
> > +	ADC084S021_VOLTAGE_CHANNEL(0),
> > +	ADC084S021_VOLTAGE_CHANNEL(1),
> > +	ADC084S021_VOLTAGE_CHANNEL(2),
> > +	ADC084S021_VOLTAGE_CHANNEL(3),
> > +	IIO_CHAN_SOFT_TIMESTAMP(4),
> > +};
> > +
> > +/**
> > + * Read an ADC channel and return its value.
> > + *
> > + * @adc: The ADC SPI data.
> > + * @channel: The IIO channel data structure.
> > + */
> > +static int adc084s021_adc_conversion(struct adc084s021 *adc,
> > +			   struct iio_chan_spec const *channel)
> > +{
> > +	u8 value;
> > +	int ret;
> > +
> > +	adc->tx_buf = channel->address;
> > +
> > +	/* Do the transfer */
> > +	ret = spi_sync(adc->spi, &adc->message);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	value = (be16_to_cpu(adc->rx_buf) >> channel->scan_type.shift) & 0xff;You want to do this for the read_raw sysfs calls, but not the buffered ones
> (where this shift and mask is made userspace's problem).
Fixed in v3.
> > +
> > +	dev_dbg(&adc->spi->dev, "value 0x%02X on channel %d\n",
> > +			     value, channel->channel);
> > +
> > +	return value;
> > +}
> > +
> > +static int adc084s021_read_raw(struct iio_dev *indio_dev,
> > +			   struct iio_chan_spec const *channel, int *val,
> > +			   int *val2, long mask)
> > +{
> > +	struct adc084s021 *adc = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_RAW:
> > +		ret = iio_device_claim_direct_mode(indio_dev);
> > +		if (ret < 0)
> > +			return ret;
> > +
> Unless I'm dozing off this morning, you don't have any power..
> So you'll need to enable the regulator first in this path.
Fixed in v3.
> 
> Note that the runtime power management autosuspend stuff can
> be good for this as it stops the power cycling if a short burst
> of readings is taken.
I don't have much experience of the pm_runtime system, so I looked
around to the other drivers but I see very few users of it. I can
absolutely look into it if you think it should be applied to this
driver.
> > +		ret = adc084s021_adc_conversion(adc, channel);
> > +		iio_device_release_direct_mode(indio_dev);
> > +		if (ret < 0)
> > +			return ret;
> > +
> > +		*val = ret;
> > +
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_SCALE:
> > +		ret = regulator_get_voltage(adc->reg);
> Given the regulator is currently off as far as this device is concerned
> it's possible the answer will be 0...
Fixed in v3.
> > +		if (ret < 0)
> > +			return ret;
> > +
> > +		*val = ret / 1000;
> > +
> > +		return IIO_VAL_INT;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +/**
> > + * Read enabled ADC channels and push data to the buffer.
> > + *
> > + * @irq: The interrupt number (not used).
> > + * @pollfunc: Pointer to the poll func.
> > + */
> > +static irqreturn_t adc084s021_buffer_trigger_handler(int irq, void *pollfunc)
> > +{
> > +	struct iio_poll_func *pf = pollfunc;
> > +	struct iio_dev *indio_dev = pf->indio_dev;
> > +	struct adc084s021 *adc = iio_priv(indio_dev);
> > +	__be16 data[8] = {0}; /* 4 * 16-bit words of data + 8 bytes timestamp */
> > +	int scan_index;
> > +	int i = 0;
> > +
> > +	mutex_lock(&adc->lock);
> > +
> > +	for_each_set_bit(scan_index, indio_dev->active_scan_mask,
> > +			 indio_dev->masklength) {
> > +		const struct iio_chan_spec *channel =
> > +			&indio_dev->channels[scan_index];
> > +		data[i++] = adc084s021_adc_conversion(adc, channel);
> This is clearly a rather simplistic way of handling the read outs.
> Perfectly good for an initial version (which may never get improved
> on!) but you could do the spi transfers for a set of channels much
> more efficiently.
> 
> Figure 1 on the datasheet shows how the control register for the next
> read can be written in parallel with the previous read. That would
> mean each scan took N + 1 2 byte transfers rather than 2N as currently.
> 
> I'm not particularly suggesting you do this, but thought I'd just
> comment on the possibility as it is a common situation with spi
> ADCs (sometimes you get a greater lag between setup and read out
> making this even fiddlier!)
> 
> If you do want to do this, the trick is to do your transfer setup
> and creation stuff in preenable so that it can be customised for
> whatever channels are enabled.
Ok, Fixed in v3.
I have looked into this and I think I improved the design according to
your suggestion. Please note: According to the datasheet (section 8.3)
after each power up the ADC will read out last scanned/first channel,
which will result in a very first read of 16 bits that is ignored. Also,
the readings are setup by only one transfer segment now.
> > +	}
> > +
> > +	iio_push_to_buffers_with_timestamp(indio_dev, data,
> > +					   iio_get_time_ns(indio_dev));
> > +	mutex_unlock(&adc->lock);
> > +	iio_trigger_notify_done(indio_dev->trig);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static int adc084s021_buffer_preenable(struct iio_dev *indio_dev)
> > +{
> > +	struct adc084s021 *adc = iio_priv(indio_dev);
> > +
> > +	return regulator_enable(adc->reg);
> > +}
> > +
> > +static int adc084s021_buffer_postdisable(struct iio_dev *indio_dev)
> > +{
> > +	struct adc084s021 *adc = iio_priv(indio_dev);
> > +
> > +	return regulator_disable(adc->reg);
> > +}
> > +
> > +static const struct iio_info adc084s021_info = {
> > +	.read_raw = adc084s021_read_raw,
> > +	.driver_module = THIS_MODULE,
> > +};
> > +
> > +static const struct iio_buffer_setup_ops adc084s021_buffer_setup_ops = {
> > +	.preenable = adc084s021_buffer_preenable,
> > +	.postenable = iio_triggered_buffer_postenable,
> > +	.predisable = iio_triggered_buffer_predisable,
> > +	.postdisable = adc084s021_buffer_postdisable,
> > +};
> > +
> > +static int adc084s021_probe(struct spi_device *spi)
> > +{
> > +	struct iio_dev *indio_dev;
> > +	struct adc084s021 *adc;
> > +	int ret;
> > +
> > +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
> > +	if (!indio_dev) {
> > +		dev_err(&spi->dev, "Failed to allocate IIO device\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	adc = iio_priv(indio_dev);
> > +	adc->spi = spi;
> > +	spi->bits_per_word = 8;
> > +
> > +	/* Update the SPI device with config and connect the iio dev */
> > +	ret = spi_setup(spi);
> > +	if (ret) {
> > +		dev_err(&spi->dev, "Failed to update SPI device\n");
> > +		return ret;
> > +	}
> > +	spi_set_drvdata(spi, indio_dev);
> > +
> > +	/* Initiate the Industrial I/O device */
> > +	indio_dev->dev.parent = &spi->dev;
> > +	indio_dev->dev.of_node = spi->dev.of_node;
> > +	indio_dev->name = spi_get_device_id(spi)->name;
> > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > +	indio_dev->info = &adc084s021_info;
> > +	indio_dev->channels = adc084s021_channels;
> > +	indio_dev->num_channels = ARRAY_SIZE(adc084s021_channels);
> > +
> > +	/* Create SPI transfer for channel reads */
> > +	adc->spi_trans[0].tx_buf = &adc->tx_buf;
> > +	adc->spi_trans[0].len = 2;
> > +	adc->spi_trans[0].speed_hz = spi->max_speed_hz;
> You don't need to set these for individual transfers unless they
> are different from how the spi bus has been set up...
> 
> It's handled in __spi_validate in drivers/spi/spi.c
> 
> 	list_for_each_entry(xfer, &message->transfers, transfer_list) {
> 		message->frame_length += xfer->len;
> 		if (!xfer->bits_per_word)
> 			xfer->bits_per_word = spi->bits_per_word;
> 
> 		if (!xfer->speed_hz)
> 			xfer->speed_hz = spi->max_speed_hz;
> ...
> 
> So it will set them spi->... in the core if you haven't overridden.
Fixed in v3.
> 
> > +	adc->spi_trans[0].bits_per_word = spi->bits_per_word;
> > +	adc->spi_trans[1].rx_buf = &adc->rx_buf;
> > +	adc->spi_trans[1].len = 2;
> > +	adc->spi_trans[1].speed_hz = spi->max_speed_hz;
> > +	adc->spi_trans[1].bits_per_word = spi->bits_per_word;
> > +
> > +	spi_message_init_with_transfers(&adc->message, adc->spi_trans, 2);
> > +
> > +	adc->reg = devm_regulator_get(&spi->dev, "vref");
> > +	if (IS_ERR(adc->reg))
> > +		return PTR_ERR(adc->reg);
> > +
> > +	mutex_init(&adc->lock);
> > +
> > +	/* Setup triggered buffer with pollfunction */
> > +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> > +					    adc084s021_buffer_trigger_handler,
> > +					    &adc084s021_buffer_setup_ops);
> > +	if (ret) {
> > +		dev_err(&spi->dev, "Failed to setup triggered buffer\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = iio_device_register(indio_dev);
> > +	if (ret) {
> > +		dev_err(&spi->dev, "Failed to register IIO device\n");
> > +		iio_triggered_buffer_cleanup(indio_dev);
> With devm usage this won't be needed any more.
Fixed in v3.
> 
> Hmm. We should improve the error message from the core as it'll allow us
> to remove a lot of boiler plate in drivers. Ah well, a project for
> another day.
I removed this message in v3.
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static int adc084s021_remove(struct spi_device *spi)
> > +{
> > +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
> > +
> > +	iio_device_unregister(indio_dev);
> > +	iio_triggered_buffer_cleanup(indio_dev);
> Now you have moved to dynamically enabling the regulator, it makes
> sense to use the devm_ versions of iio_device_register and 
> iio_triggered_buffer setup.
> 
> With those, you'll be able to get rid of the remove callback entirely as
> there will be nothing to do.
Fixed in v3.
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id adc084s021_of_match[] = {
> > +	{ .compatible = "ti,adc084s021", },
> > +	{},
> > +};
> > +MODULE_DEVICE_TABLE(of, adc084s021_of_match);
> > +
> > +static const struct spi_device_id adc084s021_id[] = {
> > +	{ ADC084S021_DRIVER_NAME, 0},
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(spi, adc084s021_id);
> > +
> > +static struct spi_driver adc084s021_driver = {
> > +	.driver = {
> > +		.name = ADC084S021_DRIVER_NAME,
> > +		.of_match_table = of_match_ptr(adc084s021_of_match),
> > +	},
> > +	.probe = adc084s021_probe,
> > +	.remove = adc084s021_remove,
> > +	.id_table = adc084s021_id,
> > +};
> > +module_spi_driver(adc084s021_driver);
> > +
> > +MODULE_AUTHOR("Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>");
> > +MODULE_DESCRIPTION("Texas Instruments ADC084S021");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_VERSION("1.0");
> > 
> 

^ permalink raw reply

* Re: [PATCH v5 03/14] pinctrl: add a pinctrl driver for the Ingenic jz47xx SoCs
From: Paul Cercueil @ 2017-05-03  9:12 UTC (permalink / raw)
  To: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Ralf Baechle
  Cc: Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
	Maarten ter Huurne, Lars-Peter Clausen, Paul Burton, james.hogan,
	linux-gpio, devicetree, linux-kernel, linux-mips, linux-mmc,
	linux-mtd, linux-pwm, linux-fbdev
In-Reply-To: <20170428200824.10906-4-paul@crapouillou.net>

The dependency on MFD is gone but now I notice I forgot to remove the 
'select MFD_CORE'
in the Kconfig. It'd be great if you can make a quick edit when merging 
this,
otherwise I'll send a v6.

- Paul

Le 2017-04-28 22:08, Paul Cercueil a écrit :
> This driver handles pin configuration and pin muxing for the
> JZ4740 and JZ4780 SoCs from Ingenic.
> 
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> ---
>  drivers/pinctrl/Kconfig           |  10 +
>  drivers/pinctrl/Makefile          |   1 +
>  drivers/pinctrl/pinctrl-ingenic.c | 852 
> ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 863 insertions(+)
>  create mode 100644 drivers/pinctrl/pinctrl-ingenic.c
> 
>  v2: Consider it's a new patch. Completely rewritten from v1.
>  v3: 'unsigned' -> 'unsigned int'
>  v4: Completely rewritten from v3.
>  v5: Probe child devices directly instead of using MFD framework
> 
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 8f8c2af45781..82ce72fcb8e0 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -285,6 +285,16 @@ config PINCTRL_ZYNQ
>  	help
>  	  This selects the pinctrl driver for Xilinx Zynq.
> 
> +config PINCTRL_INGENIC
> +	bool "Pinctrl driver for the Ingenic JZ47xx SoCs"
> +	default y
> +	depends on MACH_INGENIC || COMPILE_TEST
> +	select GENERIC_PINCONF
> +	select GENERIC_PINCTRL_GROUPS
> +	select GENERIC_PINMUX_FUNCTIONS
> +	select REGMAP_MMIO
> +	select MFD_CORE
> +
>  source "drivers/pinctrl/aspeed/Kconfig"
>  source "drivers/pinctrl/bcm/Kconfig"
>  source "drivers/pinctrl/berlin/Kconfig"
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index a251f439626f..80f327239d4b 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_LPC18XX)	+= pinctrl-lpc18xx.o
>  obj-$(CONFIG_PINCTRL_TB10X)	+= pinctrl-tb10x.o
>  obj-$(CONFIG_PINCTRL_ST) 	+= pinctrl-st.o
>  obj-$(CONFIG_PINCTRL_ZYNQ)	+= pinctrl-zynq.o
> +obj-$(CONFIG_PINCTRL_INGENIC)	+= pinctrl-ingenic.o
> 
>  obj-$(CONFIG_ARCH_ASPEED)	+= aspeed/
>  obj-y				+= bcm/
> diff --git a/drivers/pinctrl/pinctrl-ingenic.c
> b/drivers/pinctrl/pinctrl-ingenic.c
> new file mode 100644
> index 000000000000..d8473d929cb1
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-ingenic.c
> @@ -0,0 +1,852 @@
> +/*
> + * Ingenic SoCs pinctrl driver
> + *
> + * Copyright (c) 2017 Paul Cercueil <paul@crapouillou.net>
> + *
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#include <linux/compiler.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include "core.h"
> +#include "pinconf.h"
> +#include "pinmux.h"
> +
> +#define JZ4740_GPIO_DATA	0x10
> +#define JZ4740_GPIO_PULL_DIS	0x30
> +#define JZ4740_GPIO_FUNC	0x40
> +#define JZ4740_GPIO_SELECT	0x50
> +#define JZ4740_GPIO_DIR		0x60
> +#define JZ4740_GPIO_TRIG	0x70
> +#define JZ4740_GPIO_FLAG	0x80
> +
> +#define JZ4770_GPIO_INT		0x10
> +#define JZ4770_GPIO_MSK		0x20
> +#define JZ4770_GPIO_PAT1	0x30
> +#define JZ4770_GPIO_PAT0	0x40
> +#define JZ4770_GPIO_FLAG	0x50
> +#define JZ4770_GPIO_PEN		0x70
> +
> +#define REG_SET(x) ((x) + 0x4)
> +#define REG_CLEAR(x) ((x) + 0x8)
> +
> +#define PINS_PER_GPIO_CHIP 32
> +
> +enum jz_version {
> +	ID_JZ4740,
> +	ID_JZ4770,
> +	ID_JZ4780,
> +};
> +
> +struct ingenic_chip_info {
> +	unsigned int num_chips;
> +
> +	const struct group_desc *groups;
> +	unsigned int num_groups;
> +
> +	const struct function_desc *functions;
> +	unsigned int num_functions;
> +
> +	const u32 *pull_ups, *pull_downs;
> +};
> +
> +struct ingenic_pinctrl {
> +	struct device *dev;
> +	struct regmap *map;
> +	struct pinctrl_dev *pctl;
> +	struct pinctrl_pin_desc *pdesc;
> +	enum jz_version version;
> +
> +	const struct ingenic_chip_info *info;
> +};
> +
> +static const u32 jz4740_pull_ups[4] = {
> +	0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
> +};
> +
> +static const u32 jz4740_pull_downs[4] = {
> +	0x00000000, 0x00000000, 0x00000000, 0x00000000,
> +};
> +
> +static int jz4740_mmc_1bit_pins[] = { 0x69, 0x68, 0x6a, };
> +static int jz4740_mmc_4bit_pins[] = { 0x6b, 0x6c, 0x6d, };
> +static int jz4740_uart0_data_pins[] = { 0x7a, 0x79, };
> +static int jz4740_uart0_hwflow_pins[] = { 0x7e, 0x7f, };
> +static int jz4740_uart1_data_pins[] = { 0x7e, 0x7f, };
> +static int jz4740_lcd_8bit_pins[] = {
> +	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x52, 0x53, 0x54,
> +};
> +static int jz4740_lcd_16bit_pins[] = {
> +	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x55,
> +};
> +static int jz4740_lcd_18bit_pins[] = { 0x50, 0x51, };
> +static int jz4740_lcd_18bit_tft_pins[] = { 0x56, 0x57, 0x31, 0x32, };
> +static int jz4740_nand_cs1_pins[] = { 0x39, };
> +static int jz4740_nand_cs2_pins[] = { 0x3a, };
> +static int jz4740_nand_cs3_pins[] = { 0x3b, };
> +static int jz4740_nand_cs4_pins[] = { 0x3c, };
> +static int jz4740_pwm_pwm0_pins[] = { 0x77, };
> +static int jz4740_pwm_pwm1_pins[] = { 0x78, };
> +static int jz4740_pwm_pwm2_pins[] = { 0x79, };
> +static int jz4740_pwm_pwm3_pins[] = { 0x7a, };
> +static int jz4740_pwm_pwm4_pins[] = { 0x7b, };
> +static int jz4740_pwm_pwm5_pins[] = { 0x7c, };
> +static int jz4740_pwm_pwm6_pins[] = { 0x7e, };
> +static int jz4740_pwm_pwm7_pins[] = { 0x7f, };
> +
> +static int jz4740_mmc_1bit_funcs[] = { 0, 0, 0, };
> +static int jz4740_mmc_4bit_funcs[] = { 0, 0, 0, };
> +static int jz4740_uart0_data_funcs[] = { 1, 1, };
> +static int jz4740_uart0_hwflow_funcs[] = { 1, 1, };
> +static int jz4740_uart1_data_funcs[] = { 2, 2, };
> +static int jz4740_lcd_8bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
> 0, };
> +static int jz4740_lcd_16bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, };
> +static int jz4740_lcd_18bit_funcs[] = { 0, 0, };
> +static int jz4740_lcd_18bit_tft_funcs[] = { 0, 0, 0, 0, };
> +static int jz4740_nand_cs1_funcs[] = { 0, };
> +static int jz4740_nand_cs2_funcs[] = { 0, };
> +static int jz4740_nand_cs3_funcs[] = { 0, };
> +static int jz4740_nand_cs4_funcs[] = { 0, };
> +static int jz4740_pwm_pwm0_funcs[] = { 0, };
> +static int jz4740_pwm_pwm1_funcs[] = { 0, };
> +static int jz4740_pwm_pwm2_funcs[] = { 0, };
> +static int jz4740_pwm_pwm3_funcs[] = { 0, };
> +static int jz4740_pwm_pwm4_funcs[] = { 0, };
> +static int jz4740_pwm_pwm5_funcs[] = { 0, };
> +static int jz4740_pwm_pwm6_funcs[] = { 0, };
> +static int jz4740_pwm_pwm7_funcs[] = { 0, };
> +
> +#define INGENIC_PIN_GROUP(name, id)			\
> +	{						\
> +		name,					\
> +		id##_pins,				\
> +		ARRAY_SIZE(id##_pins),			\
> +		id##_funcs,				\
> +	}
> +
> +static const struct group_desc jz4740_groups[] = {
> +	INGENIC_PIN_GROUP("mmc-1bit", jz4740_mmc_1bit),
> +	INGENIC_PIN_GROUP("mmc-4bit", jz4740_mmc_4bit),
> +	INGENIC_PIN_GROUP("uart0-data", jz4740_uart0_data),
> +	INGENIC_PIN_GROUP("uart0-hwflow", jz4740_uart0_hwflow),
> +	INGENIC_PIN_GROUP("uart1-data", jz4740_uart1_data),
> +	INGENIC_PIN_GROUP("lcd-8bit", jz4740_lcd_8bit),
> +	INGENIC_PIN_GROUP("lcd-16bit", jz4740_lcd_16bit),
> +	INGENIC_PIN_GROUP("lcd-18bit", jz4740_lcd_18bit),
> +	INGENIC_PIN_GROUP("lcd-18bit-tft", jz4740_lcd_18bit_tft),
> +	{ "lcd-no-pins", },
> +	INGENIC_PIN_GROUP("nand-cs1", jz4740_nand_cs1),
> +	INGENIC_PIN_GROUP("nand-cs2", jz4740_nand_cs2),
> +	INGENIC_PIN_GROUP("nand-cs3", jz4740_nand_cs3),
> +	INGENIC_PIN_GROUP("nand-cs4", jz4740_nand_cs4),
> +	INGENIC_PIN_GROUP("pwm0", jz4740_pwm_pwm0),
> +	INGENIC_PIN_GROUP("pwm1", jz4740_pwm_pwm1),
> +	INGENIC_PIN_GROUP("pwm2", jz4740_pwm_pwm2),
> +	INGENIC_PIN_GROUP("pwm3", jz4740_pwm_pwm3),
> +	INGENIC_PIN_GROUP("pwm4", jz4740_pwm_pwm4),
> +	INGENIC_PIN_GROUP("pwm5", jz4740_pwm_pwm5),
> +	INGENIC_PIN_GROUP("pwm6", jz4740_pwm_pwm6),
> +	INGENIC_PIN_GROUP("pwm7", jz4740_pwm_pwm7),
> +};
> +
> +static const char *jz4740_mmc_groups[] = { "mmc-1bit", "mmc-4bit", };
> +static const char *jz4740_uart0_groups[] = { "uart0-data", 
> "uart0-hwflow", };
> +static const char *jz4740_uart1_groups[] = { "uart1-data", };
> +static const char *jz4740_lcd_groups[] = {
> +	"lcd-8bit", "lcd-16bit", "lcd-18bit", "lcd-18bit-tft", "lcd-no-pins",
> +};
> +static const char *jz4740_nand_groups[] = {
> +	"nand-cs1", "nand-cs2", "nand-cs3", "nand-cs4",
> +};
> +static const char *jz4740_pwm0_groups[] = { "pwm0", };
> +static const char *jz4740_pwm1_groups[] = { "pwm1", };
> +static const char *jz4740_pwm2_groups[] = { "pwm2", };
> +static const char *jz4740_pwm3_groups[] = { "pwm3", };
> +static const char *jz4740_pwm4_groups[] = { "pwm4", };
> +static const char *jz4740_pwm5_groups[] = { "pwm5", };
> +static const char *jz4740_pwm6_groups[] = { "pwm6", };
> +static const char *jz4740_pwm7_groups[] = { "pwm7", };
> +
> +static const struct function_desc jz4740_functions[] = {
> +	{ "mmc", jz4740_mmc_groups, ARRAY_SIZE(jz4740_mmc_groups), },
> +	{ "uart0", jz4740_uart0_groups, ARRAY_SIZE(jz4740_uart0_groups), },
> +	{ "uart1", jz4740_uart1_groups, ARRAY_SIZE(jz4740_uart1_groups), },
> +	{ "lcd", jz4740_lcd_groups, ARRAY_SIZE(jz4740_lcd_groups), },
> +	{ "nand", jz4740_nand_groups, ARRAY_SIZE(jz4740_nand_groups), },
> +	{ "pwm0", jz4740_pwm0_groups, ARRAY_SIZE(jz4740_pwm0_groups), },
> +	{ "pwm1", jz4740_pwm1_groups, ARRAY_SIZE(jz4740_pwm1_groups), },
> +	{ "pwm2", jz4740_pwm2_groups, ARRAY_SIZE(jz4740_pwm2_groups), },
> +	{ "pwm3", jz4740_pwm3_groups, ARRAY_SIZE(jz4740_pwm3_groups), },
> +	{ "pwm4", jz4740_pwm4_groups, ARRAY_SIZE(jz4740_pwm4_groups), },
> +	{ "pwm5", jz4740_pwm5_groups, ARRAY_SIZE(jz4740_pwm5_groups), },
> +	{ "pwm6", jz4740_pwm6_groups, ARRAY_SIZE(jz4740_pwm6_groups), },
> +	{ "pwm7", jz4740_pwm7_groups, ARRAY_SIZE(jz4740_pwm7_groups), },
> +};
> +
> +static const struct ingenic_chip_info jz4740_chip_info = {
> +	.num_chips = 4,
> +	.groups = jz4740_groups,
> +	.num_groups = ARRAY_SIZE(jz4740_groups),
> +	.functions = jz4740_functions,
> +	.num_functions = ARRAY_SIZE(jz4740_functions),
> +	.pull_ups = jz4740_pull_ups,
> +	.pull_downs = jz4740_pull_downs,
> +};
> +
> +static const u32 jz4770_pull_ups[6] = {
> +	0x3fffffff, 0xfff0030c, 0xffffffff, 0xffff4fff, 0xfffffb7c, 
> 0xffa7f00f,
> +};
> +
> +static const u32 jz4770_pull_downs[6] = {
> +	0x00000000, 0x000f0c03, 0x00000000, 0x0000b000, 0x00000483, 
> 0x00580ff0,
> +};
> +
> +static int jz4770_uart0_data_pins[] = { 0xa0, 0xa3, };
> +static int jz4770_uart0_hwflow_pins[] = { 0xa1, 0xa2, };
> +static int jz4770_uart1_data_pins[] = { 0x7a, 0x7c, };
> +static int jz4770_uart1_hwflow_pins[] = { 0x7b, 0x7d, };
> +static int jz4770_uart2_data_pins[] = { 0x66, 0x67, };
> +static int jz4770_uart2_hwflow_pins[] = { 0x65, 0x64, };
> +static int jz4770_uart3_data_pins[] = { 0x6c, 0x85, };
> +static int jz4770_uart3_hwflow_pins[] = { 0x88, 0x89, };
> +static int jz4770_uart4_data_pins[] = { 0x54, 0x4a, };
> +static int jz4770_mmc0_8bit_a_pins[] = { 0x04, 0x05, 0x06, 0x07, 0x18, 
> };
> +static int jz4770_mmc0_4bit_a_pins[] = { 0x15, 0x16, 0x17, };
> +static int jz4770_mmc0_1bit_a_pins[] = { 0x12, 0x13, 0x14, };
> +static int jz4770_mmc0_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
> +static int jz4770_mmc0_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, };
> +static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, };
> +static int jz4770_mmc1_1bit_d_pins[] = { 0x78, 0x79, 0x74, };
> +static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
> +static int jz4770_mmc1_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, };
> +static int jz4770_nemc_data_pins[] = {
> +	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
> +};
> +static int jz4770_nemc_cle_ale_pins[] = { 0x20, 0x21, };
> +static int jz4770_nemc_addr_pins[] = { 0x22, 0x23, 0x24, 0x25, };
> +static int jz4770_nemc_rd_we_pins[] = { 0x10, 0x11, };
> +static int jz4770_nemc_frd_fwe_pins[] = { 0x12, 0x13, };
> +static int jz4770_nemc_cs1_pins[] = { 0x15, };
> +static int jz4770_nemc_cs2_pins[] = { 0x16, };
> +static int jz4770_nemc_cs3_pins[] = { 0x17, };
> +static int jz4770_nemc_cs4_pins[] = { 0x18, };
> +static int jz4770_nemc_cs5_pins[] = { 0x19, };
> +static int jz4770_nemc_cs6_pins[] = { 0x1a, };
> +static int jz4770_i2c0_pins[] = { 0x6e, 0x6f, };
> +static int jz4770_i2c1_pins[] = { 0x8e, 0x8f, };
> +static int jz4770_i2c2_pins[] = { 0xb0, 0xb1, };
> +static int jz4770_i2c3_pins[] = { 0x6a, 0x6b, };
> +static int jz4770_i2c4_e_pins[] = { 0x8c, 0x8d, };
> +static int jz4770_i2c4_f_pins[] = { 0xb9, 0xb8, };
> +static int jz4770_cim_pins[] = {
> +	0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 
> 0x31,
> +};
> +static int jz4770_lcd_32bit_pins[] = {
> +	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
> +	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
> +	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
> +	0x58, 0x59, 0x51,
> +};
> +static int jz4770_pwm_pwm0_pins[] = { 0x80, };
> +static int jz4770_pwm_pwm1_pins[] = { 0x81, };
> +static int jz4770_pwm_pwm2_pins[] = { 0x82, };
> +static int jz4770_pwm_pwm3_pins[] = { 0x83, };
> +static int jz4770_pwm_pwm4_pins[] = { 0x84, };
> +static int jz4770_pwm_pwm5_pins[] = { 0x85, };
> +static int jz4770_pwm_pwm6_pins[] = { 0x6a, };
> +static int jz4770_pwm_pwm7_pins[] = { 0x6b, };
> +
> +static int jz4770_uart0_data_funcs[] = { 0, 0, };
> +static int jz4770_uart0_hwflow_funcs[] = { 0, 0, };
> +static int jz4770_uart1_data_funcs[] = { 0, 0, };
> +static int jz4770_uart1_hwflow_funcs[] = { 0, 0, };
> +static int jz4770_uart2_data_funcs[] = { 1, 1, };
> +static int jz4770_uart2_hwflow_funcs[] = { 1, 1, };
> +static int jz4770_uart3_data_funcs[] = { 0, 1, };
> +static int jz4770_uart3_hwflow_funcs[] = { 0, 0, };
> +static int jz4770_uart4_data_funcs[] = { 2, 2, };
> +static int jz4770_mmc0_8bit_a_funcs[] = { 1, 1, 1, 1, 1, };
> +static int jz4770_mmc0_4bit_a_funcs[] = { 1, 1, 1, };
> +static int jz4770_mmc0_1bit_a_funcs[] = { 1, 1, 0, };
> +static int jz4770_mmc0_4bit_e_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc0_1bit_e_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc1_1bit_d_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, };
> +static int jz4770_mmc1_1bit_e_funcs[] = { 1, 1, 1, };
> +static int jz4770_nemc_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
> +static int jz4770_nemc_cle_ale_funcs[] = { 0, 0, };
> +static int jz4770_nemc_addr_funcs[] = { 0, 0, 0, 0, };
> +static int jz4770_nemc_rd_we_funcs[] = { 0, 0, };
> +static int jz4770_nemc_frd_fwe_funcs[] = { 0, 0, };
> +static int jz4770_nemc_cs1_funcs[] = { 0, };
> +static int jz4770_nemc_cs2_funcs[] = { 0, };
> +static int jz4770_nemc_cs3_funcs[] = { 0, };
> +static int jz4770_nemc_cs4_funcs[] = { 0, };
> +static int jz4770_nemc_cs5_funcs[] = { 0, };
> +static int jz4770_nemc_cs6_funcs[] = { 0, };
> +static int jz4770_i2c0_funcs[] = { 0, 0, };
> +static int jz4770_i2c1_funcs[] = { 0, 0, };
> +static int jz4770_i2c2_funcs[] = { 2, 2, };
> +static int jz4770_i2c3_funcs[] = { 1, 1, };
> +static int jz4770_i2c4_e_funcs[] = { 1, 1, };
> +static int jz4770_i2c4_f_funcs[] = { 1, 1, };
> +static int jz4770_cim_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
> };
> +static int jz4770_lcd_32bit_funcs[] = {
> +	0, 0, 0, 0, 0, 0, 0, 0,
> +	0, 0, 0, 0, 0, 0, 0, 0,
> +	0, 0, 0,
> +};
> +static int jz4770_pwm_pwm0_funcs[] = { 0, };
> +static int jz4770_pwm_pwm1_funcs[] = { 0, };
> +static int jz4770_pwm_pwm2_funcs[] = { 0, };
> +static int jz4770_pwm_pwm3_funcs[] = { 0, };
> +static int jz4770_pwm_pwm4_funcs[] = { 0, };
> +static int jz4770_pwm_pwm5_funcs[] = { 0, };
> +static int jz4770_pwm_pwm6_funcs[] = { 0, };
> +static int jz4770_pwm_pwm7_funcs[] = { 0, };
> +
> +static const struct group_desc jz4770_groups[] = {
> +	INGENIC_PIN_GROUP("uart0-data", jz4770_uart0_data),
> +	INGENIC_PIN_GROUP("uart0-hwflow", jz4770_uart0_hwflow),
> +	INGENIC_PIN_GROUP("uart1-data", jz4770_uart1_data),
> +	INGENIC_PIN_GROUP("uart1-hwflow", jz4770_uart1_hwflow),
> +	INGENIC_PIN_GROUP("uart2-data", jz4770_uart2_data),
> +	INGENIC_PIN_GROUP("uart2-hwflow", jz4770_uart2_hwflow),
> +	INGENIC_PIN_GROUP("uart3-data", jz4770_uart3_data),
> +	INGENIC_PIN_GROUP("uart3-hwflow", jz4770_uart3_hwflow),
> +	INGENIC_PIN_GROUP("uart4-data", jz4770_uart4_data),
> +	INGENIC_PIN_GROUP("mmc0-8bit-a", jz4770_mmc0_8bit_a),
> +	INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a),
> +	INGENIC_PIN_GROUP("mmc0-1bit-a", jz4770_mmc0_1bit_a),
> +	INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e),
> +	INGENIC_PIN_GROUP("mmc0-1bit-e", jz4770_mmc0_1bit_e),
> +	INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d),
> +	INGENIC_PIN_GROUP("mmc1-1bit-d", jz4770_mmc1_1bit_d),
> +	INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e),
> +	INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e),
> +	INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_data),
> +	INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale),
> +	INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr),
> +	INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we),
> +	INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe),
> +	INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1),
> +	INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2),
> +	INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3),
> +	INGENIC_PIN_GROUP("nemc-cs4", jz4770_nemc_cs4),
> +	INGENIC_PIN_GROUP("nemc-cs5", jz4770_nemc_cs5),
> +	INGENIC_PIN_GROUP("nemc-cs6", jz4770_nemc_cs6),
> +	INGENIC_PIN_GROUP("i2c0-data", jz4770_i2c0),
> +	INGENIC_PIN_GROUP("i2c1-data", jz4770_i2c1),
> +	INGENIC_PIN_GROUP("i2c2-data", jz4770_i2c2),
> +	INGENIC_PIN_GROUP("i2c3-data", jz4770_i2c3),
> +	INGENIC_PIN_GROUP("i2c4-data-e", jz4770_i2c4_e),
> +	INGENIC_PIN_GROUP("i2c4-data-f", jz4770_i2c4_f),
> +	INGENIC_PIN_GROUP("cim-data", jz4770_cim),
> +	INGENIC_PIN_GROUP("lcd-32bit", jz4770_lcd_32bit),
> +	{ "lcd-no-pins", },
> +	INGENIC_PIN_GROUP("pwm0", jz4770_pwm_pwm0),
> +	INGENIC_PIN_GROUP("pwm1", jz4770_pwm_pwm1),
> +	INGENIC_PIN_GROUP("pwm2", jz4770_pwm_pwm2),
> +	INGENIC_PIN_GROUP("pwm3", jz4770_pwm_pwm3),
> +	INGENIC_PIN_GROUP("pwm4", jz4770_pwm_pwm4),
> +	INGENIC_PIN_GROUP("pwm5", jz4770_pwm_pwm5),
> +	INGENIC_PIN_GROUP("pwm6", jz4770_pwm_pwm6),
> +	INGENIC_PIN_GROUP("pwm7", jz4770_pwm_pwm7),
> +};
> +
> +static const char *jz4770_uart0_groups[] = { "uart0-data", 
> "uart0-hwflow", };
> +static const char *jz4770_uart1_groups[] = { "uart1-data", 
> "uart1-hwflow", };
> +static const char *jz4770_uart2_groups[] = { "uart2-data", 
> "uart2-hwflow", };
> +static const char *jz4770_uart3_groups[] = { "uart3-data", 
> "uart3-hwflow", };
> +static const char *jz4770_uart4_groups[] = { "uart4-data", };
> +static const char *jz4770_mmc0_groups[] = {
> +	"mmc0-8bit-a", "mmc0-4bit-a", "mmc0-1bit-a",
> +	"mmc0-1bit-e", "mmc0-4bit-e",
> +};
> +static const char *jz4770_mmc1_groups[] = {
> +	"mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e",
> +};
> +static const char *jz4770_nemc_groups[] = {
> +	"nemc-data", "nemc-cle-ale", "nemc-addr", "nemc-rd-we", 
> "nemc-frd-fwe",
> +};
> +static const char *jz4770_cs1_groups[] = { "nemc-cs1", };
> +static const char *jz4770_cs6_groups[] = { "nemc-cs6", };
> +static const char *jz4770_i2c0_groups[] = { "i2c0-data", };
> +static const char *jz4770_i2c1_groups[] = { "i2c1-data", };
> +static const char *jz4770_i2c2_groups[] = { "i2c2-data", };
> +static const char *jz4770_i2c3_groups[] = { "i2c3-data", };
> +static const char *jz4770_i2c4_groups[] = { "i2c4-data-e", 
> "i2c4-data-f", };
> +static const char *jz4770_cim_groups[] = { "cim-data", };
> +static const char *jz4770_lcd_groups[] = { "lcd-32bit", "lcd-no-pins", 
> };
> +static const char *jz4770_pwm0_groups[] = { "pwm0", };
> +static const char *jz4770_pwm1_groups[] = { "pwm1", };
> +static const char *jz4770_pwm2_groups[] = { "pwm2", };
> +static const char *jz4770_pwm3_groups[] = { "pwm3", };
> +static const char *jz4770_pwm4_groups[] = { "pwm4", };
> +static const char *jz4770_pwm5_groups[] = { "pwm5", };
> +static const char *jz4770_pwm6_groups[] = { "pwm6", };
> +static const char *jz4770_pwm7_groups[] = { "pwm7", };
> +
> +static const struct function_desc jz4770_functions[] = {
> +	{ "uart0", jz4770_uart0_groups, ARRAY_SIZE(jz4770_uart0_groups), },
> +	{ "uart1", jz4770_uart1_groups, ARRAY_SIZE(jz4770_uart1_groups), },
> +	{ "uart2", jz4770_uart2_groups, ARRAY_SIZE(jz4770_uart2_groups), },
> +	{ "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), },
> +	{ "uart4", jz4770_uart4_groups, ARRAY_SIZE(jz4770_uart4_groups), },
> +	{ "mmc0", jz4770_mmc0_groups, ARRAY_SIZE(jz4770_mmc0_groups), },
> +	{ "mmc1", jz4770_mmc1_groups, ARRAY_SIZE(jz4770_mmc1_groups), },
> +	{ "nemc", jz4770_nemc_groups, ARRAY_SIZE(jz4770_nemc_groups), },
> +	{ "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), },
> +	{ "nemc-cs6", jz4770_cs6_groups, ARRAY_SIZE(jz4770_cs6_groups), },
> +	{ "i2c0", jz4770_i2c0_groups, ARRAY_SIZE(jz4770_i2c0_groups), },
> +	{ "i2c1", jz4770_i2c1_groups, ARRAY_SIZE(jz4770_i2c1_groups), },
> +	{ "i2c2", jz4770_i2c2_groups, ARRAY_SIZE(jz4770_i2c2_groups), },
> +	{ "i2c3", jz4770_i2c3_groups, ARRAY_SIZE(jz4770_i2c3_groups), },
> +	{ "i2c4", jz4770_i2c4_groups, ARRAY_SIZE(jz4770_i2c4_groups), },
> +	{ "cim", jz4770_cim_groups, ARRAY_SIZE(jz4770_cim_groups), },
> +	{ "lcd", jz4770_lcd_groups, ARRAY_SIZE(jz4770_lcd_groups), },
> +	{ "pwm0", jz4770_pwm0_groups, ARRAY_SIZE(jz4770_pwm0_groups), },
> +	{ "pwm1", jz4770_pwm1_groups, ARRAY_SIZE(jz4770_pwm1_groups), },
> +	{ "pwm2", jz4770_pwm2_groups, ARRAY_SIZE(jz4770_pwm2_groups), },
> +	{ "pwm3", jz4770_pwm3_groups, ARRAY_SIZE(jz4770_pwm3_groups), },
> +	{ "pwm4", jz4770_pwm4_groups, ARRAY_SIZE(jz4770_pwm4_groups), },
> +	{ "pwm5", jz4770_pwm5_groups, ARRAY_SIZE(jz4770_pwm5_groups), },
> +	{ "pwm6", jz4770_pwm6_groups, ARRAY_SIZE(jz4770_pwm6_groups), },
> +	{ "pwm7", jz4770_pwm7_groups, ARRAY_SIZE(jz4770_pwm7_groups), },
> +};
> +
> +static const struct ingenic_chip_info jz4770_chip_info = {
> +	.num_chips = 6,
> +	.groups = jz4770_groups,
> +	.num_groups = ARRAY_SIZE(jz4770_groups),
> +	.functions = jz4770_functions,
> +	.num_functions = ARRAY_SIZE(jz4770_functions),
> +	.pull_ups = jz4770_pull_ups,
> +	.pull_downs = jz4770_pull_downs,
> +};
> +
> +static inline void ingenic_config_pin(struct ingenic_pinctrl *jzpc,
> +		unsigned int pin, u8 reg, bool set)
> +{
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +
> +	regmap_write(jzpc->map, offt * 0x100 +
> +			(set ? REG_SET(reg) : REG_CLEAR(reg)), BIT(idx));
> +}
> +
> +static inline bool ingenic_get_pin_config(struct ingenic_pinctrl 
> *jzpc,
> +		unsigned int pin, u8 reg)
> +{
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +	unsigned int val;
> +
> +	regmap_read(jzpc->map, offt * 0x100 + reg, &val);
> +
> +	return val & BIT(idx);
> +}
> +
> +static struct pinctrl_ops ingenic_pctlops = {
> +	.get_groups_count = pinctrl_generic_get_group_count,
> +	.get_group_name = pinctrl_generic_get_group_name,
> +	.get_group_pins = pinctrl_generic_get_group_pins,
> +	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
> +	.dt_free_map = pinconf_generic_dt_free_map,
> +};
> +
> +static int ingenic_pinmux_set_pin_fn(struct ingenic_pinctrl *jzpc,
> +		int pin, int func)
> +{
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +
> +	dev_dbg(jzpc->dev, "set pin P%c%u to function %u\n",
> +			'A' + offt, idx, func);
> +
> +	if (jzpc->version >= ID_JZ4770) {
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, false);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, func & 0x2);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT0, func & 0x1);
> +	} else {
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_FUNC, true);
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_TRIG, func & 0x2);
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, func > 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int ingenic_pinmux_set_mux(struct pinctrl_dev *pctldev,
> +		unsigned int selector, unsigned int group)
> +{
> +	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> +	struct function_desc *func;
> +	struct group_desc *grp;
> +	unsigned int i;
> +
> +	func = pinmux_generic_get_function(pctldev, selector);
> +	if (!func)
> +		return -EINVAL;
> +
> +	grp = pinctrl_generic_get_group(pctldev, group);
> +	if (!grp)
> +		return -EINVAL;
> +
> +	dev_dbg(pctldev->dev, "enable function %s group %s\n",
> +		func->name, grp->name);
> +
> +	for (i = 0; i < grp->num_pins; i++) {
> +		int *pin_modes = grp->data;
> +
> +		ingenic_pinmux_set_pin_fn(jzpc, grp->pins[i], pin_modes[i]);
> +	}
> +
> +	return 0;
> +}
> +
> +static int ingenic_pinmux_gpio_set_direction(struct pinctrl_dev 
> *pctldev,
> +		struct pinctrl_gpio_range *range,
> +		unsigned int pin, bool input)
> +{
> +	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +
> +	dev_dbg(pctldev->dev, "set pin P%c%u to %sput\n",
> +			'A' + offt, idx, input ? "in" : "out");
> +
> +	if (jzpc->version >= ID_JZ4770) {
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, true);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, input);
> +	} else {
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, false);
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_DIR, input);
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_FUNC, false);
> +	}
> +
> +	return 0;
> +}
> +
> +static struct pinmux_ops ingenic_pmxops = {
> +	.get_functions_count = pinmux_generic_get_function_count,
> +	.get_function_name = pinmux_generic_get_function_name,
> +	.get_function_groups = pinmux_generic_get_function_groups,
> +	.set_mux = ingenic_pinmux_set_mux,
> +	.gpio_set_direction = ingenic_pinmux_gpio_set_direction,
> +};
> +
> +static int ingenic_pinconf_get(struct pinctrl_dev *pctldev,
> +		unsigned int pin, unsigned long *config)
> +{
> +	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> +	enum pin_config_param param = pinconf_to_config_param(*config);
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +	bool pull;
> +
> +	if (jzpc->version >= ID_JZ4770)
> +		pull = !ingenic_get_pin_config(jzpc, pin, JZ4770_GPIO_PEN);
> +	else
> +		pull = !ingenic_get_pin_config(jzpc, pin, JZ4740_GPIO_PULL_DIS);
> +
> +	switch (param) {
> +	case PIN_CONFIG_BIAS_DISABLE:
> +		if (pull)
> +			return -EINVAL;
> +		break;
> +
> +	case PIN_CONFIG_BIAS_PULL_UP:
> +		if (!pull || !(jzpc->info->pull_ups[offt] & BIT(idx)))
> +			return -EINVAL;
> +		break;
> +
> +	case PIN_CONFIG_BIAS_PULL_DOWN:
> +		if (!pull || !(jzpc->info->pull_downs[offt] & BIT(idx)))
> +			return -EINVAL;
> +		break;
> +
> +	default:
> +		return -ENOTSUPP;
> +	}
> +
> +	*config = pinconf_to_config_packed(param, 1);
> +	return 0;
> +}
> +
> +static void ingenic_set_bias(struct ingenic_pinctrl *jzpc,
> +		unsigned int pin, bool enabled)
> +{
> +	if (jzpc->version >= ID_JZ4770)
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PEN, !enabled);
> +	else
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_PULL_DIS, !enabled);
> +}
> +
> +static int ingenic_pinconf_set(struct pinctrl_dev *pctldev, unsigned 
> int pin,
> +		unsigned long *configs, unsigned int num_configs)
> +{
> +	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +	unsigned int cfg;
> +
> +	for (cfg = 0; cfg < num_configs; cfg++) {
> +		switch (pinconf_to_config_param(configs[cfg])) {
> +		case PIN_CONFIG_BIAS_DISABLE:
> +		case PIN_CONFIG_BIAS_PULL_UP:
> +		case PIN_CONFIG_BIAS_PULL_DOWN:
> +			continue;
> +		default:
> +			return -ENOTSUPP;
> +		}
> +	}
> +
> +	for (cfg = 0; cfg < num_configs; cfg++) {
> +		switch (pinconf_to_config_param(configs[cfg])) {
> +		case PIN_CONFIG_BIAS_DISABLE:
> +			dev_dbg(jzpc->dev, "disable pull-over for pin P%c%u\n",
> +					'A' + offt, idx);
> +			ingenic_set_bias(jzpc, pin, false);
> +			break;
> +
> +		case PIN_CONFIG_BIAS_PULL_UP:
> +			if (!(jzpc->info->pull_ups[offt] & BIT(idx)))
> +				return -EINVAL;
> +			dev_dbg(jzpc->dev, "set pull-up for pin P%c%u\n",
> +					'A' + offt, idx);
> +			ingenic_set_bias(jzpc, pin, true);
> +			break;
> +
> +		case PIN_CONFIG_BIAS_PULL_DOWN:
> +			if (!(jzpc->info->pull_downs[offt] & BIT(idx)))
> +				return -EINVAL;
> +			dev_dbg(jzpc->dev, "set pull-down for pin P%c%u\n",
> +					'A' + offt, idx);
> +			ingenic_set_bias(jzpc, pin, true);
> +			break;
> +
> +		default:
> +			unreachable();
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int ingenic_pinconf_group_get(struct pinctrl_dev *pctldev,
> +		unsigned int group, unsigned long *config)
> +{
> +	const unsigned int *pins;
> +	unsigned int i, npins, old = 0;
> +	int ret;
> +
> +	ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < npins; i++) {
> +		if (ingenic_pinconf_get(pctldev, pins[i], config))
> +			return -ENOTSUPP;
> +
> +		/* configs do not match between two pins */
> +		if (i && (old != *config))
> +			return -ENOTSUPP;
> +
> +		old = *config;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ingenic_pinconf_group_set(struct pinctrl_dev *pctldev,
> +		unsigned int group, unsigned long *configs,
> +		unsigned int num_configs)
> +{
> +	const unsigned int *pins;
> +	unsigned int i, npins;
> +	int ret;
> +
> +	ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < npins; i++) {
> +		ret = ingenic_pinconf_set(pctldev,
> +				pins[i], configs, num_configs);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct pinconf_ops ingenic_confops = {
> +	.is_generic = true,
> +	.pin_config_get = ingenic_pinconf_get,
> +	.pin_config_set = ingenic_pinconf_set,
> +	.pin_config_group_get = ingenic_pinconf_group_get,
> +	.pin_config_group_set = ingenic_pinconf_group_set,
> +};
> +
> +static const struct regmap_config ingenic_pinctrl_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static const struct of_device_id ingenic_pinctrl_of_match[] = {
> +	{ .compatible = "ingenic,jz4740-pinctrl", .data = (void *) ID_JZ4740 
> },
> +	{ .compatible = "ingenic,jz4770-pinctrl", .data = (void *) ID_JZ4770 
> },
> +	{ .compatible = "ingenic,jz4780-pinctrl", .data = (void *) ID_JZ4780 
> },
> +	{},
> +};
> +
> +int ingenic_pinctrl_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct ingenic_pinctrl *jzpc;
> +	struct pinctrl_desc *pctl_desc;
> +	void __iomem *base;
> +	const struct platform_device_id *id = platform_get_device_id(pdev);
> +	const struct of_device_id *of_id = of_match_device(
> +			ingenic_pinctrl_of_match, dev);
> +	const struct ingenic_chip_info *chip_info;
> +	unsigned int i;
> +	int err;
> +
> +	jzpc = devm_kzalloc(dev, sizeof(*jzpc), GFP_KERNEL);
> +	if (!jzpc)
> +		return -ENOMEM;
> +
> +	base = devm_ioremap_resource(dev,
> +			platform_get_resource(pdev, IORESOURCE_MEM, 0));
> +	if (IS_ERR(base)) {
> +		dev_err(dev, "Failed to ioremap registers\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	jzpc->map = devm_regmap_init_mmio(dev, base,
> +			&ingenic_pinctrl_regmap_config);
> +	if (IS_ERR(jzpc->map)) {
> +		dev_err(dev, "Failed to create regmap\n");
> +		return PTR_ERR(jzpc->map);
> +	}
> +
> +	jzpc->dev = dev;
> +
> +	if (of_id)
> +		jzpc->version = (enum jz_version)of_id->data;
> +	else
> +		jzpc->version = (enum jz_version)id->driver_data;
> +
> +	if (jzpc->version >= ID_JZ4770)
> +		chip_info = &jz4770_chip_info;
> +	else
> +		chip_info = &jz4740_chip_info;
> +	jzpc->info = chip_info;
> +
> +	pctl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctl_desc), GFP_KERNEL);
> +	if (!pctl_desc)
> +		return -ENOMEM;
> +
> +	/* fill in pinctrl_desc structure */
> +	pctl_desc->name = dev_name(dev);
> +	pctl_desc->owner = THIS_MODULE;
> +	pctl_desc->pctlops = &ingenic_pctlops;
> +	pctl_desc->pmxops = &ingenic_pmxops;
> +	pctl_desc->confops = &ingenic_confops;
> +	pctl_desc->npins = chip_info->num_chips * PINS_PER_GPIO_CHIP;
> +	pctl_desc->pins = jzpc->pdesc = devm_kzalloc(&pdev->dev,
> +			sizeof(*jzpc->pdesc) * pctl_desc->npins, GFP_KERNEL);
> +	if (!jzpc->pdesc)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < pctl_desc->npins; i++) {
> +		jzpc->pdesc[i].number = i;
> +		jzpc->pdesc[i].name = kasprintf(GFP_KERNEL, "P%c%d",
> +						'A' + (i / PINS_PER_GPIO_CHIP),
> +						i % PINS_PER_GPIO_CHIP);
> +	}
> +
> +	jzpc->pctl = devm_pinctrl_register(dev, pctl_desc, jzpc);
> +	if (!jzpc->pctl) {
> +		dev_err(dev, "Failed to register pinctrl\n");
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < chip_info->num_groups; i++) {
> +		const struct group_desc *group = &chip_info->groups[i];
> +
> +		err = pinctrl_generic_add_group(jzpc->pctl, group->name,
> +				group->pins, group->num_pins, group->data);
> +		if (err) {
> +			dev_err(dev, "Failed to register group %s\n",
> +					group->name);
> +			return err;
> +		}
> +	}
> +
> +	for (i = 0; i < chip_info->num_functions; i++) {
> +		const struct function_desc *func = &chip_info->functions[i];
> +
> +		err = pinmux_generic_add_function(jzpc->pctl, func->name,
> +				func->group_names, func->num_group_names,
> +				func->data);
> +		if (err) {
> +			dev_err(dev, "Failed to register function %s\n",
> +					func->name);
> +			return err;
> +		}
> +	}
> +
> +	dev_set_drvdata(dev, jzpc->map);
> +
> +	if (dev->of_node) {
> +		err = of_platform_populate(dev->of_node, NULL, NULL, dev);
> +		if (err) {
> +			dev_err(dev, "Failed to probe GPIO devices\n");
> +			return err;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id ingenic_pinctrl_ids[] = {
> +	{ "jz4740-pinctrl", ID_JZ4740 },
> +	{ "jz4770-pinctrl", ID_JZ4770 },
> +	{ "jz4780-pinctrl", ID_JZ4780 },
> +	{},
> +};
> +
> +static struct platform_driver ingenic_pinctrl_driver = {
> +	.driver = {
> +		.name = "pinctrl-ingenic",
> +		.of_match_table = of_match_ptr(ingenic_pinctrl_of_match),
> +		.suppress_bind_attrs = true,
> +	},
> +	.probe = ingenic_pinctrl_probe,
> +	.id_table = ingenic_pinctrl_ids,
> +};
> +
> +static int __init ingenic_pinctrl_drv_register(void)
> +{
> +	return platform_driver_register(&ingenic_pinctrl_driver);
> +}
> +postcore_initcall(ingenic_pinctrl_drv_register);


^ permalink raw reply

* Re: [PATCH v5] media: platform: Renesas IMR driver
From: Laurent Pinchart @ 2017-05-03  9:04 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Sergei Shtylyov, Rob Herring, Mark Rutland, Mauro Carvalho Chehab,
	devicetree@vger.kernel.org, Linux Media Mailing List,
	Linux-Renesas, Konstantin Kozhevnikov, Kuninori Morimoto
In-Reply-To: <CAMuHMdWE+o3gsFnxqBcvrD=PfHHb0i9uK3tsfWaNxfuhK3SNKg@mail.gmail.com>

Hi Geert,

On Wednesday 03 May 2017 09:22:00 Geert Uytterhoeven wrote:
> On Tue, May 2, 2017 at 11:17 PM, Laurent Pinchart wrote:
> > On Wednesday 22 Mar 2017 10:34:16 Geert Uytterhoeven wrote:
> >> On Thu, Mar 9, 2017 at 9:08 PM, Sergei Shtylyov wrote:
> >>> --- /dev/null
> >>> +++ media_tree/Documentation/devicetree/bindings/media/rcar_imr.txt
> >>> @@ -0,0 +1,27 @@
> >>> +Renesas R-Car Image Renderer (Distortion Correction Engine)
> >>> +-----------------------------------------------------------
> >>> +
> >>> +The image renderer, or the distortion correction engine, is a drawing
> >>> processor
> >>> +with a simple instruction system capable of referencing video capture
> >>> data or
> >>> +data in an external memory as 2D texture data and performing texture
> >>> mapping
> >>> +and drawing with respect to any shape that is split into triangular
> >>> objects.
> >>> +
> >>> +Required properties:
> >>> +
> >>> +- compatible: "renesas,<soctype>-imr-lx4", "renesas,imr-lx4" as a
> >>> fallback for
> >>> +  the image renderer light extended 4 (IMR-LX4) found in the R-Car
> >>> gen3 SoCs,
> >>> +  where the examples with <soctype> are:
> >>> +  - "renesas,r8a7795-imr-lx4" for R-Car H3,
> >>> +  - "renesas,r8a7796-imr-lx4" for R-Car M3-W.
> >> 
> >> Laurent: what do you think about the need for SoC-specific compatible
> >> values for the various IM* blocks?
> > 
> > There's no documented IP core version register, but when dumping all
> > configuration registers on H3 and M3-W I noticed that register 0x002c, not
> > documented in the datasheet, reads 0x14060514 on all four IMR instances in
> > H3, and 0x20150505 on both instances in M3-W.
> > 
> > This looks like a version register to me. If my assumption is correct, we
> > could do without any SoC-specific compatible string.
> 
> I read this assumed version registers on all R-Car SoCs, after writing
> zero to 0xe6150990 (SMSTPCR8).
> 
> IMR-X2 on R-Car H2:     0x12072009
> IMR-LSX2 on R-Car H2:   0x12072009
> IMR-LSX3 on R-Car V2H:  0x13052617
> IMR-LX2 on R-Car M2-W:  0x12072009
> IMR-LX2 on R-Car M2-N:  0x12072009
> IMR-LX2 on R-Car E2:    0x13091909
> IMR-LX3 on R-Car V2H:   0x13052617
> 
> Note that several IDs are the same, but you know the type from the
> compatible value.
> 
> It would be good to get confirmation from the hardware team that this is
> indeed a version register.

Thank you for checking.

Morimoto-san, do you think there are still people alive in the Gen2 hardware 
team who could provide the information ? :-) If not, information restricted to 
Gen3 would still be useful.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* Re: [PATCH 13/15] drm/sun4i: Add HDMI support
From: Maxime Ripard @ 2017-05-03  8:41 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Mike Turquette, Stephen Boyd, dri-devel, Daniel Vetter,
	David Airlie, Mark Rutland, Rob Herring, devicetree, linux-clk,
	linux-arm-kernel, linux-kernel, linux-sunxi
In-Reply-To: <CAGb2v67Szt9Xj+hwJD6i9X0XgXOtZBS1Jy4wWjbfSGJSDFM1NQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 3950 bytes --]

On Wed, Apr 26, 2017 at 03:59:28PM +0800, Chen-Yu Tsai wrote:
> >> > +       writel(SUN4I_HDMI_VID_TIMING_X(mode->hdisplay) |
> >> > +              SUN4I_HDMI_VID_TIMING_Y(mode->vdisplay),
> >> > +              hdmi->base + SUN4I_HDMI_VID_TIMING_ACT_REG);
> >> > +
> >> > +       x = mode->htotal - mode->hsync_start;
> >> > +       y = mode->vtotal - mode->vsync_start;
> >>
> >> I'm a bit skeptical about this one. All the other parameters are not
> >> inclusive of other, why would this one be different? Shouldn't it
> >> be "Xtotal - Xsync_end" instead?
> >
> > By the usual meaning of backporch, you're right. However, Allwinner's
> > seems to have it's own, which is actually the backporch + sync length.
> >
> > We also have that on all the other connectors (and TCON), and this was
> > confirmed at the time using a scope on an RGB signal.
> 
> Yes. On the later SoCs such as the A31, the user manual actually has
> timing diagrams showing this.
> 
> Unlike the TCON, the HDMI controller's timings lists the front porch
> separately, instead of an all inclusive Xtotal. This is what made me
> look twice. This should be easy to confirm though. Since the HDMI modes
> are well known and can be exactly reproduced on our hardware, we can
> just check for any distortions or refresh rate errors.

This isn't as trivial as that. Screens usually have some tolerancies
on the timings, which will probably make it go unnoticed, even though
they are wrong.

> >>
> >> > +       writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
> >> > +              hdmi->base + SUN4I_HDMI_VID_TIMING_BP_REG);
> >> > +
> >> > +       x = mode->hsync_start - mode->hdisplay;
> >> > +       y = mode->vsync_start - mode->vdisplay;
> >> > +       writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
> >> > +              hdmi->base + SUN4I_HDMI_VID_TIMING_FP_REG);
> >> > +
> >> > +       x = mode->hsync_end - mode->hsync_start;
> >> > +       y = mode->vsync_end - mode->vsync_start;
> >> > +       writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
> >> > +              hdmi->base + SUN4I_HDMI_VID_TIMING_SPW_REG);
> >> > +
> >> > +       val = SUN4I_HDMI_VID_TIMING_POL_TX_CLK;
> >> > +       if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> >> > +               val |= SUN4I_HDMI_VID_TIMING_POL_HSYNC;
> >> > +
> >> > +       if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> >> > +               val |= SUN4I_HDMI_VID_TIMING_POL_VSYNC;
> >> > +
> >> > +       writel(val, hdmi->base + SUN4I_HDMI_VID_TIMING_POL_REG);
> >>
> >> You don't handle the interlaced video here, even though you set
> >>
> >>     hdmi->connector.interlace_allowed = true
> >>
> >> later.
> >
> > I'll fix that.
> >
> >> The double clock and double scan flags aren't handled either, though
> >> I don't understand which one is supposed to represent the need for the
> >> HDMI pixel repeater. AFAIK this is required for resolutions with pixel
> >> clocks lower than 25 MHz, the lower limit of HDMI's TMDS link.
> >
> > I'm not sure about this one though. I'd like to keep things quite
> > simple for now and build up on that once the basis is working. Is it
> > common in the wild?
> 
> If you drive the display at SDTV resolutions, then yes. Mode lines from
> my HDMI monitor:
> 
>   720x576i 50 720 732 795 864 576 580 586 625 flags: nhsync, nvsync,
> interlace, dblclk; type: driver
>   720x480i 60 720 739 801 858 480 488 494 525 flags: nhsync, nvsync,
> interlace, dblclk; type: driver
>   720x480i 60 720 739 801 858 480 488 494 525 flags: nhsync, nvsync,
> interlace, dblclk; type: driver
> 
> AFAIK these are standard modes that all devices should support. Whether
> they are used daily is another thing. Maybe block modes with dblclk
> in .mode_fixup for now?

That would rather be atomic_check and / or mode_valid, but yeah, I can
do that.

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH v6 5/8] i2c: mux: pca954x: Call request irq after adding mux segments
From: Phil Reid @ 2017-05-03  8:36 UTC (permalink / raw)
  To: Peter Rosin, wsa, robh+dt, mark.rutland, sre, linux-i2c,
	devicetree, linux-pm, benjamin.tissoires
In-Reply-To: <28f3ce1d-ad4b-3082-7499-b30245a256c1@axentia.se>

On 2/05/2017 17:57, Peter Rosin wrote:
> On 2017-05-02 11:12, Phil Reid wrote:
>> The pca954x device do not have the ability to mask interrupts. For
>> i2c slave devices that also don't have masking ability (eg ltc1760
>> smbalert output) delay registering the irq until after the mux
>> segments have been configured. During the mux add_adaptor call the
>> core i2c system can register an smbalert handler which would then
>> be called immediately when the irq is registered. This smbalert
>> handler will then clear the pending irq.
>>
>> Signed-off-by: Phil Reid <preid@electromag.com.au>
>> ---
>>   drivers/i2c/muxes/i2c-mux-pca954x.c | 52 +++++++++++++++++--------------------
>>   1 file changed, 24 insertions(+), 28 deletions(-)
>>
>> diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
>> index ad31d21..4299738 100644
>> --- a/drivers/i2c/muxes/i2c-mux-pca954x.c
>> +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
>> @@ -294,7 +294,7 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
>>   {
>>   	struct pca954x *data = i2c_mux_priv(muxc);
>>   	struct i2c_client *client = data->client;
>> -	int c, err, irq;
>> +	int c, irq;
>>   
>>   	if (!data->chip->has_irq || client->irq <= 0)
>>   		return 0;
>> @@ -314,24 +314,22 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
>>   			handle_simple_irq);
>>   	}
>>   
>> -	err = devm_request_threaded_irq(&client->dev, data->client->irq, NULL,
>> -					pca954x_irq_handler,
>> -					IRQF_ONESHOT | IRQF_SHARED,
>> -					"pca954x", data);
>> -	if (err)
>> -		goto err_req_irq;
>> +	return 0;
>> +}
>>   
>> -	disable_irq(data->client->irq);
>> +static void pca954x_cleanup(struct i2c_mux_core *muxc)
>> +{
>> +	struct pca954x *data = i2c_mux_priv(muxc);
>> +	int c, irq;
>>   
>> -	return 0;
>> -err_req_irq:
>> -	for (c = 0; c < data->chip->nchans; c++) {
>> -		irq = irq_find_mapping(data->irq, c);
>> -		irq_dispose_mapping(irq);
>> +	if (data->irq) {
>> +		for (c = 0; c < data->chip->nchans; c++) {
>> +			irq = irq_find_mapping(data->irq, c);
>> +			irq_dispose_mapping(irq);
>> +		}
>> +		irq_domain_remove(data->irq);
>>   	}
>> -	irq_domain_remove(data->irq);
>> -
>> -	return err;
>> +	i2c_mux_del_adapters(muxc);
>>   }
>>   
>>   /*
>> @@ -422,6 +420,14 @@ static int pca954x_probe(struct i2c_client *client,
>>   		}
>>   	}
>>   
>> +	if (data->chip->has_irq || client->irq > 0) {
> 
> This should be && instead of ||. However, I think it's better to not
> try to replicate the inverse of the test in pca954x_irq_setup and
> instead just check if the irq domain is there with "if (data->irq)".
> Assuming that is the intent...
Yeah, that's better.

> 
>> +		ret = devm_request_threaded_irq(&client->dev, data->client->irq,
>> +		NULL, pca954x_irq_handler, IRQF_ONESHOT | IRQF_SHARED,
>> +		"pca954x", data);
> 
> The indentation is horrific. Please fix.
oops

> 
> Acked with those two fixed.
> 
> I also noticed that there is no check for failure of the call to
> irq_create_mapping in pca954x_irq_setup. For bonus points, can you
> fix that error path too, please? Or should failure to create those
> irq mappings not be fatal for some reason?
Seems reasonable to fail. I'll fix that as well.

> 
> Cheers,
> peda
> 
>> +		if (ret)
>> +			goto fail_del_adapters;
>> +	}
>> +
>>   	dev_info(&client->dev,
>>   		 "registered %d multiplexed busses for I2C %s %s\n",
>>   		 num, data->chip->muxtype == pca954x_ismux
>> @@ -430,25 +436,15 @@ static int pca954x_probe(struct i2c_client *client,
>>   	return 0;
>>   
>>   fail_del_adapters:
>> -	i2c_mux_del_adapters(muxc);
>> +	pca954x_cleanup(muxc);
>>   	return ret;
>>   }
>>   
>>   static int pca954x_remove(struct i2c_client *client)
>>   {
>>   	struct i2c_mux_core *muxc = i2c_get_clientdata(client);
>> -	struct pca954x *data = i2c_mux_priv(muxc);
>> -	int c, irq;
>>   
>> -	if (data->irq) {
>> -		for (c = 0; c < data->chip->nchans; c++) {
>> -			irq = irq_find_mapping(data->irq, c);
>> -			irq_dispose_mapping(irq);
>> -		}
>> -		irq_domain_remove(data->irq);
>> -	}
>> -
>> -	i2c_mux_del_adapters(muxc);
>> +	pca954x_cleanup(muxc);
>>   	return 0;
>>   }
>>   
>>
> 
> 
> 


-- 
Regards
Phil Reid

ElectroMagnetic Imaging Technology Pty Ltd
Development of Geophysical Instrumentation & Software
www.electromag.com.au

3 The Avenue, Midland WA 6056, AUSTRALIA
Ph: +61 8 9250 8100
Fax: +61 8 9250 7100
Email: preid@electromag.com.au

^ permalink raw reply

* Re: [PATCH 2/2] [media] platform: add video-multiplexer subdevice driver
From: Philipp Zabel @ 2017-05-03  8:35 UTC (permalink / raw)
  To: Peter Rosin
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Steve Longerbeam,
	kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Sascha Hauer, Rob Herring,
	Sakari Ailus, Pavel Machek, Steve Longerbeam, Vladimir Zapolskiy
In-Reply-To: <74bfa70b-3407-9484-9717-bb2d07356f70-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On Tue, 2017-05-02 at 19:42 +0200, Peter Rosin wrote:
> On 2017-05-02 17:21, Philipp Zabel wrote:
> > On Sat, 2017-04-29 at 23:42 +0200, Peter Rosin wrote:
> >> On 2017-04-29 23:29, Peter Rosin wrote:
> >>> On 2017-04-28 16:13, Philipp Zabel wrote:
> >>>> This driver can handle SoC internal and external video bus multiplexers,
> >>>> controlled by mux controllers provided by the mux controller framework,
> >>>> such as MMIO register bitfields or GPIOs. The subdevice passes through
> >>>> the mbus configuration of the active input to the output side.
> >>>>
> >>>> Signed-off-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >>>> Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >>>> Signed-off-by: Steve Longerbeam <steve_longerbeam-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
> >>>> ---
> >>>> This has been last sent as part of the i.MX media series.
> >>>>
> >>>> Changes since https://patchwork.kernel.org/patch/9647869/:
> >>>>  - Split out the actual mux operation to be provided by the mux controller
> >>>>    framework [1]. GPIO and MMIO control can be provided by individual mux
> >>>>    controller drivers [2][3].
> >>>>    [1] https://patchwork.kernel.org/patch/9695837/
> >>>>    [2] https://patchwork.kernel.org/patch/9695839/
> >>>>    [3] https://patchwork.kernel.org/patch/9704509/
> >>>>  - Shortened 'video-multiplexer' to 'video-mux', replaced all instances of
> >>>>    vidsw with video_mux.
> >>>>  - Made the mux inactive by default, only activated by user interaction.
> >>>>  - Added CONFIG_OF and CONFIG_MULTIPLEXER dependencies.
> >>>>  - Reuse subdev.entity.num_pads instead of keeping our own count.
> >>>>  - Removed implicit link disabling. Instead, trying to enable a second
> >>>>    sink pad link yields -EBUSY.
> >>>>  - Merged _async_init into _probe.
> >>>>  - Removed superfluous pad index check from _set_format.
> >>>>  - Added is_source_pad helper to tell source and sink pads apart.
> >>>>  - Removed test for status property in endpoint nodes. Disable the remote
> >>>>    device or sever the endpoint link to disable a sink pad.
> >>>> ---
> >>>>  drivers/media/platform/Kconfig     |   6 +
> >>>>  drivers/media/platform/Makefile    |   2 +
> >>>>  drivers/media/platform/video-mux.c | 341 +++++++++++++++++++++++++++++++++++++
> >>>>  3 files changed, 349 insertions(+)
> >>>>  create mode 100644 drivers/media/platform/video-mux.c
> >>>>
> >>>> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> >>>> index c9106e105baba..b046a6d39fee5 100644
> >>>> --- a/drivers/media/platform/Kconfig
> >>>> +++ b/drivers/media/platform/Kconfig
> >>>> @@ -74,6 +74,12 @@ config VIDEO_M32R_AR_M64278
> >>>>  	  To compile this driver as a module, choose M here: the
> >>>>  	  module will be called arv.
> >>>>  
> >>>> +config VIDEO_MUX
> >>>> +	tristate "Video Multiplexer"
> >>>> +	depends on OF && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER && MULTIPLEXER
> >>>> +	help
> >>>> +	  This driver provides support for N:1 video bus multiplexers.
> >>>> +
> >>>>  config VIDEO_OMAP3
> >>>>  	tristate "OMAP 3 Camera support"
> >>>>  	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
> >>>> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> >>>> index 349ddf6a69da2..fd2735ca3ff75 100644
> >>>> --- a/drivers/media/platform/Makefile
> >>>> +++ b/drivers/media/platform/Makefile
> >>>> @@ -27,6 +27,8 @@ obj-$(CONFIG_VIDEO_SH_VEU)		+= sh_veu.o
> >>>>  
> >>>>  obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE)	+= m2m-deinterlace.o
> >>>>  
> >>>> +obj-$(CONFIG_VIDEO_MUX)			+= video-mux.o
> >>>> +
> >>>>  obj-$(CONFIG_VIDEO_S3C_CAMIF) 		+= s3c-camif/
> >>>>  obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) 	+= exynos4-is/
> >>>>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG)	+= s5p-jpeg/
> >>>> diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
> >>>> new file mode 100644
> >>>> index 0000000000000..419541729f67e
> >>>> --- /dev/null
> >>>> +++ b/drivers/media/platform/video-mux.c
> >>>> @@ -0,0 +1,341 @@
> >>>> +/*
> >>>> + * video stream multiplexer controlled via mux control
> >>>> + *
> >>>> + * Copyright (C) 2013 Pengutronix, Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >>>> + * Copyright (C) 2016 Pengutronix, Philipp Zabel <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >>>
> >>> 2017?
> >>>
> >>>> + *
> >>>> + * This program is free software; you can redistribute it and/or
> >>>> + * modify it under the terms of the GNU General Public License
> >>>> + * as published by the Free Software Foundation; either version 2
> >>>> + * of the License, or (at your option) any later version.
> >>>> + * This program is distributed in the hope that it will be useful,
> >>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>>> + * GNU General Public License for more details.
> >>>> + */
> >>>> +
> >>>> +#include <linux/err.h>
> >>>> +#include <linux/module.h>
> >>>> +#include <linux/mux/consumer.h>
> >>>> +#include <linux/of.h>
> >>>> +#include <linux/of_graph.h>
> >>>> +#include <linux/platform_device.h>
> >>>> +#include <media/v4l2-async.h>
> >>>> +#include <media/v4l2-device.h>
> >>>> +#include <media/v4l2-subdev.h>
> >>>> +#include <media/v4l2-of.h>
> >>>> +
> >>>> +struct video_mux {
> >>>> +	struct v4l2_subdev subdev;
> >>>> +	struct media_pad *pads;
> >>>> +	struct v4l2_mbus_framefmt *format_mbus;
> >>>> +	struct v4l2_of_endpoint *endpoint;
> >>>> +	struct mux_control *mux;
> >>>> +	int active;
> >>>> +};
> >>>> +
> >>>> +static inline struct video_mux *v4l2_subdev_to_video_mux(struct v4l2_subdev *sd)
> >>>> +{
> >>>> +	return container_of(sd, struct video_mux, subdev);
> >>>> +}
> >>>> +
> >>>> +static inline bool is_source_pad(struct video_mux *vmux, unsigned int pad)
> >>>> +{
> >>>> +	return pad == vmux->subdev.entity.num_pads - 1;
> >>>> +}
> >>>> +
> >>>> +static int video_mux_link_setup(struct media_entity *entity,
> >>>> +				const struct media_pad *local,
> >>>> +				const struct media_pad *remote, u32 flags)
> >>>> +{
> >>>> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> >>>> +	struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
> >>>> +	int ret;
> >>>> +
> >>>> +	/*
> >>>> +	 * The mux state is determined by the enabled sink pad link.
> >>>> +	 * Enabling or disabling the source pad link has no effect.
> >>>> +	 */
> >>>> +	if (is_source_pad(vmux, local->index))
> >>>> +		return 0;
> >>>> +
> >>>> +	dev_dbg(sd->dev, "link setup '%s':%d->'%s':%d[%d]",
> >>>> +		remote->entity->name, remote->index, local->entity->name,
> >>>> +		local->index, flags & MEDIA_LNK_FL_ENABLED);
> >>>> +
> >>>> +	if (flags & MEDIA_LNK_FL_ENABLED) {
> >>>> +		if (vmux->active == local->index)
> >>>
> >>> Here, you shortcut the mux_control_select_trylock test and return "OK"
> >>> based on a driver-local variable that is intended to keep track of mux
> >>> ownership.
> >>>
> >>>> +			return 0;
> >>>> +
> >>>> +		if (vmux->active >= 0)
> >>>
> >>> Here too (and this check is not needed, the situation will be covered by
> >>> the mux_control_try_select call).
> >>>
> >>>> +			return -EBUSY;
> >>>> +
> >>>> +		dev_dbg(sd->dev, "setting %d active\n", local->index);
> >>>> +		ret = mux_control_try_select(vmux->mux, local->index);
> >>>> +		if (ret < 0)
> >>>> +			return ret;
> >>>> +		vmux->active = local->index;
> >>>> +	} else {
> >>>> +		if (vmux->active != local->index)
> >>>> +			return 0;
> >>>> +
> >>>> +		dev_dbg(sd->dev, "going inactive\n");
> >>>> +		mux_control_deselect(vmux->mux);
> >>>
> >>> But here you let go of the mux *before* you clear the driver-local
> >>> ownership indicator. That looks suspicious. My guess is that this is
> >>> "safe" because the upper layers has some serialization, but I don't
> >>> know. Anyway, even if there is something saving you in the upper
> >>> layers, it looks out of order and unneeded. I would have moved the
> >>> below vmux->active = -1; statement up to before the above deselect.
> >>>
> >>> With that fixed, mux usage looks good to me, so you can add an Acked-
> >>> by from me if you wish (goes for the bindings patch as well).
> >>
> >> Ouch, that was a bit too soon. If there is *no* serialization in the
> >> upper layers, this is *not* ok, even with my reordering. There must be
> >> only one call to mux_control_deselect, and w/o serialization there
> >> is a race where you might get multiple deselect calls when several
> >> callers makes it through the active != index check before any of them
> >> manages to set active = -1. That race must be taken care of!
> > 
> > Thank you, I've resent a version with a mutex lock around vmux->active.
> 
> I had a bunch of ifs in the above message, so I'm not sure it's needed.
> I would expect there to be a lock outside somewhere in the media layer.
> A cursory look gets me to media-entity.c and media_entity_setup_link()
> which does have a mutex. But I'm no media expert, so maybe there are other
> ways of getting to video_mux_link_setup that I'm not aware of?

link_setup is always called under the graph mutex of the /dev/media
device. That is why I didn't think about locking too hard. In fact, I
initially wrote this expecting mux_control_get_exclusive to exist and
the mux select/deselect not to be locked at all.

But set_format is called from an unlocked ioctl on a /dev/v4l-subdev
device. Until your comments I didn't notice that it would be possible to
let link_setup set active = -1 in the middle of the set_format call,
causing it to return garbage.

> If you do end up relying on external locking, a comment saying so would
> be nice. Or even better, some __must_hold markup if possible?
> 
> Cheers,
> peda

regards
Philipp

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v6 3/8] i2c: i2c-smbus: add of_i2c_setup_smbus_alert
From: Phil Reid @ 2017-05-03  8:31 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: wsa, robh+dt, mark.rutland, sre, peda, linux-i2c, devicetree,
	linux-pm
In-Reply-To: <20170502102013.GB2382@mail.corp.redhat.com>

G'day Benjamin,

On 2/05/2017 18:20, Benjamin Tissoires wrote:
> Hi Phil,
> 
> On May 02 2017 or thereabouts, Phil Reid wrote:
>> This commit adds of_i2c_setup_smbus_alert which allows the smbalert
>> driver to be attached to an i2c adapter via the device tree.
>>
>> Signed-off-by: Phil Reid <preid@electromag.com.au>
>> ---
>>   Documentation/devicetree/bindings/i2c/i2c.txt |  4 +--
>>   drivers/i2c/i2c-smbus.c                       | 35 +++++++++++++++++++++++++++
>>   include/linux/i2c-smbus.h                     |  9 +++++++
>>   3 files changed, 46 insertions(+), 2 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/i2c/i2c.txt b/Documentation/devicetree/bindings/i2c/i2c.txt
>> index cee9d50..1126398 100644
>> --- a/Documentation/devicetree/bindings/i2c/i2c.txt
>> +++ b/Documentation/devicetree/bindings/i2c/i2c.txt
>> @@ -59,8 +59,8 @@ wants to support one of the below features, it should adapt the bindings below.
>>   	interrupts used by the device.
>>   
>>   - interrupt-names
>> -	"irq" and "wakeup" names are recognized by I2C core, other names are
>> -	left to individual drivers.
>> +	"irq", "wakeup" and "smbus_alert" names are recognized by I2C core,
>> +	other names are	left to individual drivers.
>>   
>>   - host-notify
>>   	device uses SMBus host notify protocol instead of interrupt line.
>> diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
>> index df0e2fa..a8f8439 100644
>> --- a/drivers/i2c/i2c-smbus.c
>> +++ b/drivers/i2c/i2c-smbus.c
>> @@ -21,6 +21,7 @@
>>   #include <linux/interrupt.h>
>>   #include <linux/kernel.h>
>>   #include <linux/module.h>
>> +#include <linux/of_irq.h>
>>   #include <linux/slab.h>
>>   #include <linux/workqueue.h>
>>   
>> @@ -238,6 +239,40 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
>>   }
>>   EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert);
>>   
>> +int of_i2c_setup_smbus_alert(struct i2c_adapter *adap)
>> +{
>> +	struct i2c_client *client;
>> +	struct i2c_smbus_alert_setup *setup;
>> +	struct i2c_board_info info = {
>> +		I2C_BOARD_INFO("smbus_alert", 0x0c),
>> +	};
> 
> Shouldn't you use i2c_setup_smbus_alert() instead of manually recreating
> the board_info?
Yes looks like that could be cleaned up I think.
Look at i2c_new_device also it looks like client needs to be kept around and
i2c_unregister_device. I was assuming the apdater would clean it up.
But I'm not sure that's the case when looking again. That makes the change
more invasive into the adpater. would need to add an i2c_client ptr to the
i2c_adapter struct. Thoughts?

> Also i2c_setup_smbus_alert() mentions that the level trigger has to be
> explicitely mentioned, and I can't find if this is the case in your
> patch.
I'm not too sure how this wrks with the device tree / OF stuff.
But initally I had the device tree irq type set incorrectly and things
didn't work. Fixing that up and everything flow thru to the request irq
and was set for the correct mode. So I guessed there was some "magic" in
the core irq code somewhere.
I actually think the thread irq handler should be able to handle everything
and the work queue should disappear. Pretty sure the irq core handles disabling
level interrupts will the threaded handler services things now. But I don't
quite understand how the original code was handle edge vs level trigger irq's.
Not having access to test that I was reluctant to change the hard irq.
Any suggestions helpful...

> 
>> +	int irq;
>> +
>> +	if (!adap->dev.of_node)
>> +		return 0;
>> +
>> +	irq = of_irq_get_byname(adap->dev.of_node, "smbus_alert");
>> +	if (irq == -EINVAL || irq == -ENODATA)
>> +		return 0;
>> +	else if (irq < 0)
>> +		return irq;
>> +
>> +	setup = devm_kzalloc(&adap->dev, sizeof(struct i2c_smbus_alert_setup),
>> +		GFP_KERNEL);
> 
> Problem is i2c-core doesn't use devres at all for now. So the code is
> correct here as it won't segfault but mixing both devres and non-devres
> is error prone.
Ok I look at how to avoid the devm alloc.

> 
>> +	if (!setup)
>> +		return -ENOMEM;
>> +
>> +	setup->irq = irq;
>> +	info.platform_data = setup;
>> +
>> +	client = i2c_new_device(adap, &info);
>> +	if (!client)
>> +		return -ENODEV;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert);
>> +
>>   /**
>>    * i2c_handle_smbus_alert - Handle an SMBus alert
>>    * @ara: the ARA client on the relevant adapter
>> diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
>> index a138502..4732d09 100644
>> --- a/include/linux/i2c-smbus.h
>> +++ b/include/linux/i2c-smbus.h
>> @@ -50,4 +50,13 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
>>   					 struct i2c_smbus_alert_setup *setup);
>>   int i2c_handle_smbus_alert(struct i2c_client *ara);
>>   
>> +#if IS_ENABLED(CONFIG_I2C_SMBUS)
>> +int of_i2c_setup_smbus_alert(struct i2c_adapter *adap);
>> +#else
>> +static inline int of_i2c_setup_smbus_alert(struct i2c_adapter *adap)
>> +{
>> +	return 0;
>> +}
>> +#endif
>> +
>>   #endif /* _LINUX_I2C_SMBUS_H */
>> -- 
>> 1.8.3.1
> 
> Cheers,
> Benjamin
> 
> 


-- 
Regards
Phil Reid

^ permalink raw reply

* Re: [PATCH v2 7/7] ARM: dts: imx7d-sdb: Enable PCIe peripheral
From: Shawn Guo @ 2017-05-03  8:30 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w, Dong Aisheng, Sascha Hauer,
	Fabio Estevam, Rob Herring, Mark Rutland, Russell King,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20170418150133.31679-8-andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Tue, Apr 18, 2017 at 08:01:33AM -0700, Andrey Smirnov wrote:
> Enable PCIe peripheral on this board.
> 
> Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
> Cc: Dong Aisheng <aisheng.dong-3arQi8VN3Tc@public.gmane.org>
> Cc: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> Cc: Russell King <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
>  arch/arm/boot/dts/imx7d-sdb.dts | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts
> index d6f2dda..65dda66 100644
> --- a/arch/arm/boot/dts/imx7d-sdb.dts
> +++ b/arch/arm/boot/dts/imx7d-sdb.dts
> @@ -349,6 +349,12 @@
>  	};
>  };
>  
> +&pcie {
> +	pinctrl-names = "default";

No corresponding pinctrl-0 entry?  In that case, there is no point to
have pinctrl-names.

Shawn

> +	reset-gpio = <&extended_io 1 GPIO_ACTIVE_LOW>;
> +	status = "okay";
> +};
> +
>  &pwm1 {
>  	pinctrl-names = "default";
>  	pinctrl-0 = <&pinctrl_pwm1>;
> -- 
> 2.9.3
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v2 6/7] ARM: dts: imx7d: Add node for PCIe controller
From: Shawn Guo @ 2017-05-03  8:28 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: yurovsky, Dong Aisheng, Sascha Hauer, Fabio Estevam, Rob Herring,
	Mark Rutland, Russell King, devicetree, linux-kernel,
	linux-arm-kernel
In-Reply-To: <20170418150133.31679-7-andrew.smirnov@gmail.com>

On Tue, Apr 18, 2017 at 08:01:32AM -0700, Andrey Smirnov wrote:
> Cc: yurovsky@gmail.com
> Cc: Dong Aisheng <aisheng.dong@nxp.com>
> Cc: Shawn Guo <shawnguo@kernel.org>
> Cc: Sascha Hauer <kernel@pengutronix.de>
> Cc: Fabio Estevam <fabio.estevam@nxp.com>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Russell King <linux@armlinux.org.uk>
> Cc: devicetree@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  arch/arm/boot/dts/imx7d.dtsi | 37 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 37 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/imx7d.dtsi b/arch/arm/boot/dts/imx7d.dtsi
> index f6dee41..f46814a 100644
> --- a/arch/arm/boot/dts/imx7d.dtsi
> +++ b/arch/arm/boot/dts/imx7d.dtsi
> @@ -42,6 +42,7 @@
>   */
>  
>  #include "imx7s.dtsi"
> +#include <dt-bindings/reset/imx7-reset.h>

It has dependency on imx7 reset driver, and I need to wait for
v4.12-rc1 to apply the patch.

Shawn

^ permalink raw reply

* Re: [PATCH v2 3/7] ARM: dts: imx7s: Add node for GPC
From: Shawn Guo @ 2017-05-03  8:26 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w, Dong Aisheng, Sascha Hauer,
	Fabio Estevam, Rob Herring, Mark Rutland, Russell King,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20170418150133.31679-4-andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Tue, Apr 18, 2017 at 08:01:29AM -0700, Andrey Smirnov wrote:
> Add node for GPC and specify as a parent interrupt controller for SoC bus.
> 
> Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
> Cc: Dong Aisheng <aisheng.dong-3arQi8VN3Tc@public.gmane.org>
> Cc: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> Cc: Russell King <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
>  arch/arm/boot/dts/imx7s.dtsi | 26 +++++++++++++++++++++++++-
>  1 file changed, 25 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
> index 7148eac..5ba1289 100644
> --- a/arch/arm/boot/dts/imx7s.dtsi
> +++ b/arch/arm/boot/dts/imx7s.dtsi
> @@ -42,6 +42,7 @@
>   */
>  
>  #include <dt-bindings/clock/imx7d-clock.h>
> +#include <dt-bindings/power/imx7-power.h>

It has dependency on imx7 power domain driver, and I need to wait for
v4.12-rc1 to apply the patch.

Shawn

>  #include <dt-bindings/gpio/gpio.h>
>  #include <dt-bindings/input/input.h>
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v2 2/7] ARM: imx: Select GPCv2 for i.MX7
From: Shawn Guo @ 2017-05-03  8:24 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w, Dong Aisheng, Sascha Hauer,
	Fabio Estevam, Rob Herring, Mark Rutland, Russell King,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20170418150133.31679-3-andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Tue, Apr 18, 2017 at 08:01:28AM -0700, Andrey Smirnov wrote:
> GPCv2 IP block is a part of i.MX7 SoC. Select it to make corresponding
> driver availible to support DT changes following this patch.
> 
> Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
> Cc: Dong Aisheng <aisheng.dong-3arQi8VN3Tc@public.gmane.org>
> Cc: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> Cc: Russell King <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
>  arch/arm/mach-imx/Kconfig | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
> index 936c59d..1a4ea3a 100644
> --- a/arch/arm/mach-imx/Kconfig
> +++ b/arch/arm/mach-imx/Kconfig
> @@ -532,6 +532,7 @@ config SOC_IMX7D
>  	bool "i.MX7 Dual support"
>  	select PINCTRL_IMX7D
>  	select ARM_GIC
> +	select IMX_GPCV2

Yes, PINCTRL_IMX7D is already out of order, but still please keep new
added one sorted in alphabetic order.

Shawn

>  	select HAVE_ARM_ARCH_TIMER
>  	select HAVE_IMX_ANATOP
>  	select HAVE_IMX_MMDC
> -- 
> 2.9.3
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v2 1/7] ARM: dts: i.MX: Reintroduce 'anatop-enable-bit' where appropriate
From: Shawn Guo @ 2017-05-03  8:22 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w, Dong Aisheng, Sascha Hauer,
	Fabio Estevam, Rob Herring, Mark Rutland, Russell King,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20170418150133.31679-2-andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Tue, Apr 18, 2017 at 08:01:27AM -0700, Andrey Smirnov wrote:
> Now that support for 'anatop-enable-bit' has been added to ANADIG
> driver, reintroduce 'anatop-enable-bit' for all applicable LDOs.
> 
> Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
> Cc: Dong Aisheng <aisheng.dong-3arQi8VN3Tc@public.gmane.org>
> Cc: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> Cc: Russell King <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Please use 'imx' instead of 'i.MX' in subject prefix to make it more
consistent with other patches in the series.

Shawn
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 4/5] arm64: dts: Add ipq8074 SoC and MTP board support
From: Varadarajan Narayanan @ 2017-05-03  7:30 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: robh+dt, mark.rutland, mturquette, linus.walleij, andy.gross,
	david.brown, catalin.marinas, will.deacon, devicetree,
	linux-kernel, linux-clk, linux-gpio, linux-arm-msm, linux-soc,
	linux-arm-kernel, sricharan, absahu, sjaganat,
	Manoharan Vijaya Raghavan
In-Reply-To: <20170428185349.GD7065@codeaurora.org>



On 4/29/2017 12:23 AM, Stephen Boyd wrote:
> On 04/28, Varadarajan Narayanan wrote:
>> diff --git a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts
>> new file mode 100644
>> index 0000000..c150bea
>> --- /dev/null
>> +++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts
>> @@ -0,0 +1,48 @@
>> +/dts-v1/;
>> +/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +#include "ipq8074.dtsi"
>> +
>> +/ {
>> +	#address-cells = <0x2>;
>> +	#size-cells = <0x2>;
>> +	model = "Qualcomm Technologies, Inc. IPQ8074-HK01";
>> +	compatible = "qcom,ipq8074-hk01", "qcom,ipq8074";
>> +	interrupt-parent = <&intc>;
>> +
>> +	chosen {
>> +		bootargs = "console=ttyMSM0,115200,n8 root=/dev/ram0 rw init=/init";
> 
> Add an aliases node for serial0 and use a chosen node with stdout-path = "serial0" instead please.

Ok

> 
>> +	};
>> +
>> +	memory {
>> +		device_type = "memory";
>> +		reg = <0x0 0x40000000 0x0 0x20000000>;
>> +	};
>> +
>> +	soc: soc {
> 
> Do you need the soc label here? Please remove.

Ok

> 
>> +		pinctrl@1000000 {
>> +			serial_4_pins: serial4_pinmux {
>> +				mux {
>> +					pins = "gpio23", "gpio24";
>> +					function = "blsp4_uart1";
>> +					bias-disable;
>> +				};
>> +			};
>> +		};
>> +
>> +		serial@78b3000 {
>> +			pinctrl-0 = <&serial_4_pins>;
>> +			pinctrl-names = "default";
>> +			status = "ok";
>> +		};
>> +	};
>> +};
>> diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi
>> new file mode 100644
>> index 0000000..f910cc0
>> --- /dev/null
>> +++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi
>> @@ -0,0 +1,153 @@
>> +/*
>> + * Copyright (c) 2017, The Linux Foundation. All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <dt-bindings/interrupt-controller/arm-gic.h>
>> +#include <dt-bindings/clock/qcom,gcc-ipq8074.h>
>> +
>> +/ {
>> +	model = "Qualcomm Technologies, Inc. IPQ8074";
>> +	compatible = "qcom,ipq8074";
>> +
>> +	soc: soc {
>> +		#address-cells = <0x1>;
>> +		#size-cells = <0x1>;
>> +		ranges = <0 0 0 0xffffffff>;
>> +		compatible = "simple-bus";
>> +
>> +		pinctrl@1000000 {
>> +			compatible = "qcom,ipq8074-pinctrl";
>> +			reg = <0x1000000 0x300000>;
>> +			interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>;
>> +			gpio-controller;
>> +			#gpio-cells = <0x2>;
>> +			interrupt-controller;
>> +			#interrupt-cells = <0x2>;
>> +		};
>> +
>> +		intc: interrupt-controller@b000000 {
>> +			compatible = "qcom,msm-qgic2";
>> +			interrupt-controller;
>> +			#interrupt-cells = <0x3>;
>> +			reg = <0xb000000 0x1000>,
>> +			<0xb002000 0x1000>;
> 
> Please align this up with previous reg property.

Ok

> 
>> +		};
>> +
>> +		timer {
>> +			compatible = "arm,armv8-timer";
>> +			interrupts = <GIC_PPI 2 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>> +				     <GIC_PPI 3 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>> +				     <GIC_PPI 4 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>> +				     <GIC_PPI 1 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
>> +		};
> 
> Is there an mmio timer as well? We should add it too.

Ok

> 
>> +
>> +		gcc: gcc@1800000 {
>> +			compatible = "qcom,gcc-ipq8074";
>> +			reg = <0x1800000 0x80000>;
> 
> Wow that is a huge area! Is it really that large?

Yes, per the memory map this region is 512K.

> 
>> +			#clock-cells = <0x1>;
>> +			#reset-cells = <0x1>;
>> +		};
>> +
>> +		serial@78b3000 {
>> +			compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
>> +			reg = <0x78b3000 0x200>;
>> +			interrupts = <GIC_SPI 308 IRQ_TYPE_LEVEL_HIGH>;
>> +			clocks = <&gcc GCC_BLSP1_UART5_APPS_CLK>,
>> +				 <&gcc GCC_BLSP1_AHB_CLK>;
>> +			clock-names = "core", "iface";
>> +			status = "disabled";
>> +		};
>> +	};
>> +
>> +	cpus {
>> +		#address-cells = <0x1>;
>> +		#size-cells = <0x0>;
>> +
>> +		cpu-map {
>> +
>> +			cluster0 {
>> +
>> +				core0 {
>> +					cpu = <&CPU0>;
>> +				};
>> +
>> +				core1 {
>> +					cpu = <&CPU1>;
>> +				};
>> +
>> +				core2 {
>> +					cpu = <&CPU2>;
>> +				};
>> +
>> +				core3 {
>> +					cpu = <&CPU3>;
>> +				};
>> +			};
>> +		};
> 
> Is this needed? Looks ok, but just curious if we need to do it
> for other arm64 platforms we support.

Don't see the need for topology at this time, will add it later if needed.

> 
>> +
>> +		CPU0: cpu@0 {
>> +			device_type = "cpu";
>> +			compatible = "arm,cortex-a53", "arm,armv8";
>> +			reg = <0x0>;
>> +			next-level-cache = <&L2_0>;
>> +			enable-method = "psci";
>> +		};
>> +
>> +		CPU1: cpu@1 {
>> +			device_type = "cpu";
>> +			compatible = "arm,cortex-a53", "arm,armv8";
>> +			enable-method = "psci";
>> +			reg = <0x1>;
>> +			next-level-cache = <&L2_0>;
>> +		};
>> +
>> +		CPU2: cpu@2 {
>> +			device_type = "cpu";
>> +			compatible = "arm,cortex-a53", "arm,armv8";
>> +			enable-method = "psci";
>> +			reg = <0x2>;
>> +			next-level-cache = <&L2_0>;
>> +		};
>> +
>> +		CPU3: cpu@3 {
>> +			device_type = "cpu";
>> +			compatible = "arm,cortex-a53", "arm,armv8";
>> +			enable-method = "psci";
>> +			reg = <0x3>;
>> +			next-level-cache = <&L2_0>;
>> +		};
>> +
>> +		L2_0: l2-cache {
>> +			compatible = "cache";
>> +			cache-level = <0x2>;
>> +		};
> 
> This should be inside some CPU? CPU0?

There is only one L2. We followed the same convention as in msm8916.dtsi.

> 
>> +	};
> 
> We should be able to add the performance monitor node too?
> 
Ok

>> +
>> +	psci {
>> +		compatible = "arm,psci-1.0";
>> +		method = "smc";
>> +	};
>> +
>> +	clocks {
>> +		sleep_clk: sleep_clk {
>> +			compatible = "fixed-clock";
>> +			clock-frequency = <32000>;
> 
> Not 32765 or 32768?

It is 32000.

> 
>> +			#clock-cells = <0>;
>> +		};
>> +
>> +		xo: xo {
>> +			compatible = "fixed-clock";
>> +			clock-frequency = <19200000>;
>> +			#clock-cells = <0>;
>> +		};
>> +	};
> 

-Varada
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora 
Forum, a Linux Foundation Collaborative Project

^ permalink raw reply

* Re: [PATCH v5] media: platform: Renesas IMR driver
From: Geert Uytterhoeven @ 2017-05-03  7:22 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Sergei Shtylyov, Rob Herring, Mark Rutland, Mauro Carvalho Chehab,
	devicetree@vger.kernel.org, Linux Media Mailing List,
	Linux-Renesas, Konstantin Kozhevnikov, Kuninori Morimoto
In-Reply-To: <2382097.9ZIG3XAO0j@avalon>

Hi Laurent,

On Tue, May 2, 2017 at 11:17 PM, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> On Wednesday 22 Mar 2017 10:34:16 Geert Uytterhoeven wrote:
>> On Thu, Mar 9, 2017 at 9:08 PM, Sergei Shtylyov wrote:
>> > --- /dev/null
>> > +++ media_tree/Documentation/devicetree/bindings/media/rcar_imr.txt
>> > @@ -0,0 +1,27 @@
>> > +Renesas R-Car Image Renderer (Distortion Correction Engine)
>> > +-----------------------------------------------------------
>> > +
>> > +The image renderer, or the distortion correction engine, is a drawing
>> > processor
>> > +with a simple instruction system capable of referencing video capture
>> > data or
>> > +data in an external memory as 2D texture data and performing texture
>> > mapping
>> > +and drawing with respect to any shape that is split into triangular
>> > objects.
>> > +
>> > +Required properties:
>> > +
>> > +- compatible: "renesas,<soctype>-imr-lx4", "renesas,imr-lx4" as a
>> > fallback for
>> > +  the image renderer light extended 4 (IMR-LX4) found in the R-Car gen3
>> > SoCs,
>> > +  where the examples with <soctype> are:
>> > +  - "renesas,r8a7795-imr-lx4" for R-Car H3,
>> > +  - "renesas,r8a7796-imr-lx4" for R-Car M3-W.
>>
>> Laurent: what do you think about the need for SoC-specific compatible
>> values for the various IM* blocks?
>
> There's no documented IP core version register, but when dumping all
> configuration registers on H3 and M3-W I noticed that register 0x002c, not
> documented in the datasheet, reads 0x14060514 on all four IMR instances in H3,
> and 0x20150505 on both instances in M3-W.
>
> This looks like a version register to me. If my assumption is correct, we
> could do without any SoC-specific compatible string.

I read this assumed version registers on all R-Car SoCs, after writing
zero to 0xe6150990 (SMSTPCR8).

IMR-X2 on R-Car H2:     0x12072009
IMR-LSX2 on R-Car H2:   0x12072009
IMR-LSX3 on R-Car V2H:  0x13052617
IMR-LX2 on R-Car M2-W:  0x12072009
IMR-LX2 on R-Car M2-N:  0x12072009
IMR-LX2 on R-Car E2:    0x13091909
IMR-LX3 on R-Car V2H:   0x13052617

Note that several IDs are the same, but you know the type from the
compatible value.

It would be good to get confirmation from the hardware team that this is
indeed a version register.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* [PATCH v2 resend] arm: dts: sun7i-a20-bananapi: name the GPIO lines
From: Oleksij Rempel @ 2017-05-03  7:09 UTC (permalink / raw)
  To: ore
  Cc: devicetree, Chen-Yu Tsai, Maxime Ripard, linux-arm-kernel,
	Oleksij Rempel
In-Reply-To: <CACRpkdZcnJ1eCNW4ZF_A4zbeCt3JcAuw-1meB_Pn7ctMy99B_g@mail.gmail.com>

This names the GPIO lines on the Banana Pi board in accordance with
the A20_Banana_Pi v1.4 Specification.

This will make these line names reflect through to userspace
so that they can easily be identified and used with the new
character device ABI.

Some care has been taken to name all lines, not just those used
by the external connectors, also lines that are muxed into some
other function than GPIO: these are named "[FOO]" so that users
can see with lsgpio what all lines are used for.

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/boot/dts/sun7i-a20-bananapi.dts | 60 ++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi.dts b/arch/arm/boot/dts/sun7i-a20-bananapi.dts
index 91f2e5f..5b3e0ee 100644
--- a/arch/arm/boot/dts/sun7i-a20-bananapi.dts
+++ b/arch/arm/boot/dts/sun7i-a20-bananapi.dts
@@ -178,6 +178,66 @@
 };
 
 &pio {
+	/*
+	 * Legend: proper name = the GPIO line is used as GPIO
+	 *         NC = not connected (not routed from the SoC)
+	 *         "[PER]" = pin is muxed for peripheral (not GPIO)
+	 *         "" = no idea, schematic doesn't say, could be
+	 *              unrouted (not connected to any external pin)
+	 *         LSEC = Low Speed External Connector
+	 *         HSEC = High Speed External Connector
+	 */
+	gpio-line-names =
+		/* PA */
+		"[ERXD3]", "[ERXD2]", "[ERXD1]", "[ERXD0]", "[ETXD3]",
+			"[ETXD2]", "[ETXD1]", "[ETXD0]",
+		"[ERXCK]", "[ERXERR]", "[ERXDV]", "[EMDC]", "[EMDIO]",
+			"[ETXEN]", "[ETXCK]", "[ECRS]",
+		"[ECOL]", "[ETXERR]", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		/* PB */
+		"[PMU-SCK]", "[PMU-SDA]", "", "", "", "NC", "NC", "NC",
+		"NC", "[USB0-DRV]", "NC", "NC", "NC", "NC", "", "",
+		"", "", "", "", "SCL", "SDA", "", "",
+		"", "", "", "", "", "", "", "",
+		/* PC */
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		/* PD */
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		/* PE */
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		/* PF */
+		"[SD0-D1]", "[SD0-D0]", "[SD0-CLK]", "[SD0-CMD]", "[SD0-D3]",
+			"[SD0-D2]", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		/* PG */
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "",
+		/* PH */
+		"TXD0", "RXD0", "IO-1", "PH3", "[USB0-IDDET]", "PH5", "", "",
+		"", "", "[SD0-DET]", "", "", "", "", "",
+		"NC", "", "", "", "IO-4", "IO-5", "NC", "[EMAC-PWR-EN]",
+		"[LED1]", "NC", "NC", "NC", "", "", "", "",
+		/* PI */
+		"", "", "", "IO-GCLK", "NC", "NC", "NC", "NC",
+		"NC", "NC", "[SPI-CE0]", "[SPI-CLK]", "[SPI-MOSI]",
+			"[SPI-MISO]", "[SPI-CE1]", "NC",
+		"IO-6", "IO-3", "IO-2", "IO-0", "", "", "", "",
+		"", "", "", "", "", "", "", "";
+
 	usb0_id_detect_pin: usb0_id_detect_pin@0 {
 		pins = "PH4";
 		function = "gpio_in";
-- 
2.7.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox