devicetree: add DT_COMPAT_ON_BUS()

And implement DT_ANY_INST_ON_BUS() in terms of it.

This makes some error messages quite a bit shorter by avoiding
UTIL_LISTIFY(), which has a nasty temper and tends to explode if not
treated gently.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2020-03-25 14:18:27 -07:00 committed by Kumar Gala
commit a3fae2f153
7 changed files with 100 additions and 30 deletions

View file

@ -127,6 +127,12 @@ other-macro = %s"DT_N_" alternate-id
other-macro =/ %s"DT_N_INST_" dt-name %s"_NUM" other-macro =/ %s"DT_N_INST_" dt-name %s"_NUM"
; E.g.: #define DT_CHOSEN_zephyr_flash ; E.g.: #define DT_CHOSEN_zephyr_flash
other-macro =/ %s"DT_CHOSEN_" dt-name other-macro =/ %s"DT_CHOSEN_" dt-name
; Declares that a compatible has at least one node on a bus.
;
; Example:
;
; #define DT_COMPAT_vnd_dev_BUS_spi 1
other-macro =/ %s"DT_COMPAT_" dt-name %s"_BUS_" dt-name
; -------------------------------------------------------------------- ; --------------------------------------------------------------------
; alternate-id: another way to specify a node besides a path-id ; alternate-id: another way to specify a node besides a path-id

View file

@ -0,0 +1,8 @@
# Copyright (c) 2020 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
description: GPIO expander via I2C
compatible: "vnd,gpio-expander"
include: i2c-device.yaml

View file

@ -0,0 +1,8 @@
# Copyright (c) 2020 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
description: GPIO expander via SPI
compatible: "vnd,gpio-expander"
include: spi-device.yaml

View file

@ -1036,6 +1036,31 @@
*/ */
#define DT_ON_BUS(node_id, bus) IS_ENABLED(DT_CAT(node_id, _BUS_##bus)) #define DT_ON_BUS(node_id, bus) IS_ENABLED(DT_CAT(node_id, _BUS_##bus))
/**
* @brief Test if any node of a compatible is on a bus of a given type
*
* Example devicetree overlay:
*
* &i2c0 {
* temp: temperature-sensor@76 {
* compatible = "vnd,some-sensor";
* reg = <0x76>;
* };
* };
*
* Example usage, assuming "i2c0" is an I2C bus controller node, and
* therefore "temp" is on an I2C bus:
*
* DT_COMPAT_ON_BUS(vnd_some_sensor, i2c) // 1
*
* @param compat lowercase-and-underscores version of a compatible
* @param bus a binding's bus type as a C token, lowercased and without quotes
* @return 1 if any enabled node with that compatible is on that bus type,
* 0 otherwise
*/
#define DT_COMPAT_ON_BUS(compat, bus) \
IS_ENABLED(UTIL_CAT(DT_CAT(DT_COMPAT_, compat), _BUS_##bus))
/** /**
* @} * @}
*/ */
@ -1319,17 +1344,10 @@
/** /**
* @brief Test if any node with compatible DT_DRV_COMPAT is on a bus * @brief Test if any node with compatible DT_DRV_COMPAT is on a bus
* *
* This is the same as logically ORing together DT_ON_BUS(node, bus) * This is equivalent to DT_COMPAT_ON_BUS(DT_DRV_COMPAT, bus).
* for every enabled node which matches compatible DT_DRV_COMPAT.
*
* It can be useful, for instance, when writing device drivers for
* hardware that supports multiple possible bus connections to the
* SoC.
*
* @param bus a binding's bus type as a C token, lowercased and without quotes * @param bus a binding's bus type as a C token, lowercased and without quotes
*/ */
#define DT_ANY_INST_ON_BUS(bus) \ #define DT_ANY_INST_ON_BUS(bus) DT_COMPAT_ON_BUS(DT_DRV_COMPAT, bus)
(UTIL_LISTIFY(DT_NUM_INST(DT_DRV_COMPAT), DT_INST_ON_BUS_OR, bus) 0)
/** /**
* @def DT_INST_FOREACH * @def DT_INST_FOREACH
@ -1445,8 +1463,6 @@
#define DT_DASH(...) MACRO_MAP_CAT(DT_DASH_PREFIX, __VA_ARGS__) #define DT_DASH(...) MACRO_MAP_CAT(DT_DASH_PREFIX, __VA_ARGS__)
/** @internal helper for DT_DASH(): prepends _ to a name */ /** @internal helper for DT_DASH(): prepends _ to a name */
#define DT_DASH_PREFIX(name) _##name #define DT_DASH_PREFIX(name) _##name
/** @internal DT_ANY_INST_ON_BUS helper */
#define DT_INST_ON_BUS_OR(inst, bus) DT_ON_BUS(DT_DRV_INST(inst), bus) ||
/** @internal DT_INST_FOREACH helper */ /** @internal DT_INST_FOREACH helper */
#define DT_CALL_WITH_ARG(arg, expr) expr(arg); #define DT_CALL_WITH_ARG(arg, expr) expr(arg);

View file

@ -19,6 +19,7 @@
# edtlib. This will keep this script simple. # edtlib. This will keep this script simple.
import argparse import argparse
from collections import defaultdict
import os import os
import pathlib import pathlib
import re import re
@ -64,7 +65,7 @@ def main():
write_vanilla_props(node) write_vanilla_props(node)
write_chosen(edt) write_chosen(edt)
write_inst_num(edt) write_global_compat_info(edt)
def parse_args(): def parse_args():
@ -510,26 +511,29 @@ def write_chosen(edt):
out_define(macro, value, width=max_len) out_define(macro, value, width=max_len)
def write_inst_num(edt): def write_global_compat_info(edt):
# Tree-wide information such as number of instances is printed here. # Tree-wide information related to each compatible, such as number
# of instances, is printed here.
out_comment("Number of instances\n") compat2numinst = {}
compat_list = [] compat2buses = defaultdict(list)
for compat, enabled in edt.compat2enabled.items():
compat2numinst[compat] = len(enabled)
# Walk the nodes to build which compats we need to generate for for node in enabled:
for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): bus = node.on_bus
if not node.enabled: if bus is not None and bus not in compat2buses[compat]:
continue compat2buses[compat].append(bus)
if not node.matching_compat:
continue
for compat in node.compats:
if compat not in compat_list:
compat_list.append(compat)
for compat in compat_list: out_comment("Number of enabled instances of each compatible\n")
num_inst = len(edt.compat2enabled[compat]) for compat, numinst in compat2numinst.items():
out_define(f"DT_N_INST_{str2ident(compat)}_NUM", num_inst) out_define(f"DT_N_INST_{str2ident(compat)}_NUM", numinst)
out_comment("Bus information for enabled nodes of each compatible\n")
for compat, buses in compat2buses.items():
for bus in buses:
out_define(
f"DT_COMPAT_{str2ident(compat)}_BUS_{str2ident(bus)}", 1)
def str2ident(s): def str2ident(s):
# Converts 's' to a form suitable for (part of) an identifier # Converts 's' to a form suitable for (part of) an identifier

View file

@ -121,6 +121,12 @@
label = "TEST_I2C_DEV_10"; label = "TEST_I2C_DEV_10";
reg = < 0x10 >; reg = < 0x10 >;
}; };
gpio@11 {
compatible = "vnd,gpio-expander";
reg = <0x11>;
label = "TEST_EXPANDER_I2C";
};
}; };
test_spi: spi@33334444 { test_spi: spi@33334444 {
@ -134,7 +140,8 @@
clock-frequency = < 2000000 >; clock-frequency = < 2000000 >;
cs-gpios = <&test_gpio_1 0x10 0x20>, cs-gpios = <&test_gpio_1 0x10 0x20>,
<&test_gpio_2 0x30 0x40>; <&test_gpio_2 0x30 0x40>,
<&test_gpio_2 0x50 0x60>;
/* all vnd,spi-device instances should have CS */ /* all vnd,spi-device instances should have CS */
@ -151,6 +158,13 @@
reg = <1>; reg = <1>;
spi-max-frequency = < 2000000 >; spi-max-frequency = < 2000000 >;
}; };
gpio@2 {
compatible = "vnd,gpio-expander";
reg = <2>;
label = "TEST_EXPANDER_SPI";
spi-max-frequency = <(1 * 1000 * 1000)>;
};
}; };
test_spi_no_cs: spi@55556666 { test_spi_no_cs: spi@55556666 {

View file

@ -292,6 +292,20 @@ static void test_bus(void)
"inst 0 i2c dev label"); "inst 0 i2c dev label");
zassert_true(!strncmp(i2c_bus, DT_INST_BUS_LABEL(0), strlen(i2c_bus)), zassert_true(!strncmp(i2c_bus, DT_INST_BUS_LABEL(0), strlen(i2c_bus)),
"inst 0 i2c bus label"); "inst 0 i2c bus label");
#undef DT_DRV_COMPAT
/*
* Make sure the underlying DT_COMPAT_ON_BUS used by
* DT_ANY_INST_ON_BUS works without DT_DRV_COMPAT defined.
*/
zassert_equal(DT_COMPAT_ON_BUS(vnd_spi_device, spi), 1, NULL);
zassert_equal(DT_COMPAT_ON_BUS(vnd_spi_device, i2c), 0, NULL);
zassert_equal(DT_COMPAT_ON_BUS(vnd_i2c_device, i2c), 1, NULL);
zassert_equal(DT_COMPAT_ON_BUS(vnd_i2c_device, spi), 0, NULL);
zassert_equal(DT_COMPAT_ON_BUS(vnd_gpio_expander, i2c), 1, NULL);
zassert_equal(DT_COMPAT_ON_BUS(vnd_gpio_expander, spi), 1, NULL);
} }
#undef DT_DRV_COMPAT #undef DT_DRV_COMPAT
@ -1032,7 +1046,7 @@ static void test_cs_gpios(void)
zassert_equal(DT_SPI_NUM_CS_GPIOS(TEST_SPI_NO_CS), 0, "wrong no. of cs"); zassert_equal(DT_SPI_NUM_CS_GPIOS(TEST_SPI_NO_CS), 0, "wrong no. of cs");
zassert_equal(DT_SPI_HAS_CS_GPIOS(TEST_SPI), 1, "missing cs"); zassert_equal(DT_SPI_HAS_CS_GPIOS(TEST_SPI), 1, "missing cs");
zassert_equal(DT_SPI_NUM_CS_GPIOS(TEST_SPI), 2, "wrong no. of cs"); zassert_equal(DT_SPI_NUM_CS_GPIOS(TEST_SPI), 3, "wrong no. of cs");
zassert_true(!strcmp(DT_SPI_DEV_CS_GPIOS_LABEL(TEST_SPI_DEV_0), zassert_true(!strcmp(DT_SPI_DEV_CS_GPIOS_LABEL(TEST_SPI_DEV_0),
"TEST_GPIO_1"), "TEST_GPIO_1"),