Skip to content

Commit bd6f2fd

Browse files
bebarinorobherring
authored andcommitted
of: Support parsing phandle argument lists through a nexus node
Platforms like 96boards have a standardized connector/expansion slot that exposes signals like GPIOs to expansion boards in an SoC agnostic way. We'd like the DT overlays for the expansion boards to be written once without knowledge of the SoC on the other side of the connector. This avoids the unscalable combinatorial explosion of a different DT overlay for each expansion board and SoC pair. We need a way to describe the GPIOs routed through the connector in an SoC agnostic way. Let's introduce nexus property parsing into the OF core to do this. This is largely based on the interrupt nexus support we already have. This allows us to remap a phandle list in a consumer node (e.g. reset-gpios) through a connector in a generic way (e.g. via gpio-map). Do this in a generic routine so that we can remap any sort of variable length phandle list. Taking GPIOs as an example, the connector would be a GPIO nexus, supporting the remapping of a GPIO specifier space to multiple GPIO providers on the SoC. DT would look as shown below, where 'soc_gpio1' and 'soc_gpio2' are inside the SoC, 'connector' is an expansion port where boards can be plugged in, and 'expansion_device' is a device on the expansion board. soc { soc_gpio1: gpio-controller1 { #gpio-cells = <2>; }; soc_gpio2: gpio-controller2 { #gpio-cells = <2>; }; }; connector: connector { #gpio-cells = <2>; gpio-map = <0 0 &soc_gpio1 1 0>, <1 0 &soc_gpio2 4 0>, <2 0 &soc_gpio1 3 0>, <3 0 &soc_gpio2 2 0>; gpio-map-mask = <0xf 0x0>; gpio-map-pass-thru = <0x0 0x1> }; expansion_device { reset-gpios = <&connector 2 GPIO_ACTIVE_LOW>; }; The GPIO core would use of_parse_phandle_with_args_map() instead of of_parse_phandle_with_args() and arrive at the same type of result, a phandle and argument list. The difference is that the phandle and arguments will be remapped through the nexus node to the underlying SoC GPIO controller node. In the example above, we would remap 'reset-gpios' from <&connector 2 GPIO_ACTIVE_LOW> to <&soc_gpio1 3 GPIO_ACTIVE_LOW>. Cc: Pantelis Antoniou <pantelis.antoniou@konsulko.com> Cc: Linus Walleij <linus.walleij@linaro.org> Cc: Mark Brown <broonie@kernel.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Signed-off-by: Rob Herring <robh@kernel.org>
1 parent 7928b2c commit bd6f2fd

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed

drivers/of/base.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,190 @@ int of_parse_phandle_with_args(const struct device_node *np, const char *list_na
12831283
}
12841284
EXPORT_SYMBOL(of_parse_phandle_with_args);
12851285

1286+
/**
1287+
* of_parse_phandle_with_args_map() - Find a node pointed by phandle in a list and remap it
1288+
* @np: pointer to a device tree node containing a list
1289+
* @list_name: property name that contains a list
1290+
* @stem_name: stem of property names that specify phandles' arguments count
1291+
* @index: index of a phandle to parse out
1292+
* @out_args: optional pointer to output arguments structure (will be filled)
1293+
*
1294+
* This function is useful to parse lists of phandles and their arguments.
1295+
* Returns 0 on success and fills out_args, on error returns appropriate errno
1296+
* value. The difference between this function and of_parse_phandle_with_args()
1297+
* is that this API remaps a phandle if the node the phandle points to has
1298+
* a <@stem_name>-map property.
1299+
*
1300+
* Caller is responsible to call of_node_put() on the returned out_args->np
1301+
* pointer.
1302+
*
1303+
* Example:
1304+
*
1305+
* phandle1: node1 {
1306+
* #list-cells = <2>;
1307+
* }
1308+
*
1309+
* phandle2: node2 {
1310+
* #list-cells = <1>;
1311+
* }
1312+
*
1313+
* phandle3: node3 {
1314+
* #list-cells = <1>;
1315+
* list-map = <0 &phandle2 3>,
1316+
* <1 &phandle2 2>,
1317+
* <2 &phandle1 5 1>;
1318+
* list-map-mask = <0x3>;
1319+
* };
1320+
*
1321+
* node4 {
1322+
* list = <&phandle1 1 2 &phandle3 0>;
1323+
* }
1324+
*
1325+
* To get a device_node of the `node2' node you may call this:
1326+
* of_parse_phandle_with_args(node4, "list", "list", 1, &args);
1327+
*/
1328+
int of_parse_phandle_with_args_map(const struct device_node *np,
1329+
const char *list_name,
1330+
const char *stem_name,
1331+
int index, struct of_phandle_args *out_args)
1332+
{
1333+
char *cells_name, *map_name = NULL, *mask_name = NULL;
1334+
char *pass_name = NULL;
1335+
struct device_node *cur, *new = NULL;
1336+
const __be32 *map, *mask, *pass;
1337+
static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 };
1338+
static const __be32 dummy_pass[] = { [0 ... MAX_PHANDLE_ARGS] = 0 };
1339+
__be32 initial_match_array[MAX_PHANDLE_ARGS];
1340+
const __be32 *match_array = initial_match_array;
1341+
int i, ret, map_len, match;
1342+
u32 list_size, new_size;
1343+
1344+
if (index < 0)
1345+
return -EINVAL;
1346+
1347+
cells_name = kasprintf(GFP_KERNEL, "#%s-cells", stem_name);
1348+
if (!cells_name)
1349+
return -ENOMEM;
1350+
1351+
ret = -ENOMEM;
1352+
map_name = kasprintf(GFP_KERNEL, "%s-map", stem_name);
1353+
if (!map_name)
1354+
goto free;
1355+
1356+
mask_name = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name);
1357+
if (!mask_name)
1358+
goto free;
1359+
1360+
pass_name = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name);
1361+
if (!pass_name)
1362+
goto free;
1363+
1364+
ret = __of_parse_phandle_with_args(np, list_name, cells_name, 0, index,
1365+
out_args);
1366+
if (ret)
1367+
goto free;
1368+
1369+
/* Get the #<list>-cells property */
1370+
cur = out_args->np;
1371+
ret = of_property_read_u32(cur, cells_name, &list_size);
1372+
if (ret < 0)
1373+
goto put;
1374+
1375+
/* Precalculate the match array - this simplifies match loop */
1376+
for (i = 0; i < list_size; i++)
1377+
initial_match_array[i] = cpu_to_be32(out_args->args[i]);
1378+
1379+
ret = -EINVAL;
1380+
while (cur) {
1381+
/* Get the <list>-map property */
1382+
map = of_get_property(cur, map_name, &map_len);
1383+
if (!map) {
1384+
ret = 0;
1385+
goto free;
1386+
}
1387+
map_len /= sizeof(u32);
1388+
1389+
/* Get the <list>-map-mask property (optional) */
1390+
mask = of_get_property(cur, mask_name, NULL);
1391+
if (!mask)
1392+
mask = dummy_mask;
1393+
/* Iterate through <list>-map property */
1394+
match = 0;
1395+
while (map_len > (list_size + 1) && !match) {
1396+
/* Compare specifiers */
1397+
match = 1;
1398+
for (i = 0; i < list_size; i++, map_len--)
1399+
match &= !((match_array[i] ^ *map++) & mask[i]);
1400+
1401+
of_node_put(new);
1402+
new = of_find_node_by_phandle(be32_to_cpup(map));
1403+
map++;
1404+
map_len--;
1405+
1406+
/* Check if not found */
1407+
if (!new)
1408+
goto put;
1409+
1410+
if (!of_device_is_available(new))
1411+
match = 0;
1412+
1413+
ret = of_property_read_u32(new, cells_name, &new_size);
1414+
if (ret)
1415+
goto put;
1416+
1417+
/* Check for malformed properties */
1418+
if (WARN_ON(new_size > MAX_PHANDLE_ARGS))
1419+
goto put;
1420+
if (map_len < new_size)
1421+
goto put;
1422+
1423+
/* Move forward by new node's #<list>-cells amount */
1424+
map += new_size;
1425+
map_len -= new_size;
1426+
}
1427+
if (!match)
1428+
goto put;
1429+
1430+
/* Get the <list>-map-pass-thru property (optional) */
1431+
pass = of_get_property(cur, pass_name, NULL);
1432+
if (!pass)
1433+
pass = dummy_pass;
1434+
1435+
/*
1436+
* Successfully parsed a <list>-map translation; copy new
1437+
* specifier into the out_args structure, keeping the
1438+
* bits specified in <list>-map-pass-thru.
1439+
*/
1440+
match_array = map - new_size;
1441+
for (i = 0; i < new_size; i++) {
1442+
__be32 val = *(map - new_size + i);
1443+
1444+
if (i < list_size) {
1445+
val &= ~pass[i];
1446+
val |= cpu_to_be32(out_args->args[i]) & pass[i];
1447+
}
1448+
1449+
out_args->args[i] = be32_to_cpu(val);
1450+
}
1451+
out_args->args_count = list_size = new_size;
1452+
/* Iterate again with new provider */
1453+
out_args->np = new;
1454+
of_node_put(cur);
1455+
cur = new;
1456+
}
1457+
put:
1458+
of_node_put(cur);
1459+
of_node_put(new);
1460+
free:
1461+
kfree(mask_name);
1462+
kfree(map_name);
1463+
kfree(cells_name);
1464+
kfree(pass_name);
1465+
1466+
return ret;
1467+
}
1468+
EXPORT_SYMBOL(of_parse_phandle_with_args_map);
1469+
12861470
/**
12871471
* of_parse_phandle_with_fixed_args() - Find a node pointed by phandle in a list
12881472
* @np: pointer to a device tree node containing a list

include/linux/of.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,9 @@ extern struct device_node *of_parse_phandle(const struct device_node *np,
363363
extern int of_parse_phandle_with_args(const struct device_node *np,
364364
const char *list_name, const char *cells_name, int index,
365365
struct of_phandle_args *out_args);
366+
extern int of_parse_phandle_with_args_map(const struct device_node *np,
367+
const char *list_name, const char *stem_name, int index,
368+
struct of_phandle_args *out_args);
366369
extern int of_parse_phandle_with_fixed_args(const struct device_node *np,
367370
const char *list_name, int cells_count, int index,
368371
struct of_phandle_args *out_args);
@@ -815,6 +818,15 @@ static inline int of_parse_phandle_with_args(const struct device_node *np,
815818
return -ENOSYS;
816819
}
817820

821+
static inline int of_parse_phandle_with_args_map(const struct device_node *np,
822+
const char *list_name,
823+
const char *stem_name,
824+
int index,
825+
struct of_phandle_args *out_args)
826+
{
827+
return -ENOSYS;
828+
}
829+
818830
static inline int of_parse_phandle_with_fixed_args(const struct device_node *np,
819831
const char *list_name, int cells_count, int index,
820832
struct of_phandle_args *out_args)

0 commit comments

Comments
 (0)