From a3fae2f153587c552623ad4940eb6e058aeec78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Bol=C3=ADvar?= Date: Wed, 25 Mar 2020 14:18:27 -0700 Subject: [PATCH] devicetree: add DT_COMPAT_ON_BUS() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- doc/guides/dts/macros.bnf | 6 ++++ dts/bindings/test/vnd,gpio-expander-i2c.yaml | 8 +++++ dts/bindings/test/vnd,gpio-expander-spi.yaml | 8 +++++ include/devicetree.h | 38 ++++++++++++++------ scripts/dts/gen_defines.py | 38 +++++++++++--------- tests/lib/devicetree/app.overlay | 16 ++++++++- tests/lib/devicetree/src/main.c | 16 ++++++++- 7 files changed, 100 insertions(+), 30 deletions(-) create mode 100644 dts/bindings/test/vnd,gpio-expander-i2c.yaml create mode 100644 dts/bindings/test/vnd,gpio-expander-spi.yaml diff --git a/doc/guides/dts/macros.bnf b/doc/guides/dts/macros.bnf index 52da1e9ccec..23b51d49717 100644 --- a/doc/guides/dts/macros.bnf +++ b/doc/guides/dts/macros.bnf @@ -127,6 +127,12 @@ other-macro = %s"DT_N_" alternate-id other-macro =/ %s"DT_N_INST_" dt-name %s"_NUM" ; E.g.: #define DT_CHOSEN_zephyr_flash 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 diff --git a/dts/bindings/test/vnd,gpio-expander-i2c.yaml b/dts/bindings/test/vnd,gpio-expander-i2c.yaml new file mode 100644 index 00000000000..3e763720782 --- /dev/null +++ b/dts/bindings/test/vnd,gpio-expander-i2c.yaml @@ -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 diff --git a/dts/bindings/test/vnd,gpio-expander-spi.yaml b/dts/bindings/test/vnd,gpio-expander-spi.yaml new file mode 100644 index 00000000000..83f8b5368dd --- /dev/null +++ b/dts/bindings/test/vnd,gpio-expander-spi.yaml @@ -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 diff --git a/include/devicetree.h b/include/devicetree.h index 9a039666a21..cfc1abe9b68 100644 --- a/include/devicetree.h +++ b/include/devicetree.h @@ -1036,6 +1036,31 @@ */ #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 * - * This is the same as logically ORing together DT_ON_BUS(node, 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. - * + * This is equivalent to DT_COMPAT_ON_BUS(DT_DRV_COMPAT, bus). * @param bus a binding's bus type as a C token, lowercased and without quotes */ -#define DT_ANY_INST_ON_BUS(bus) \ - (UTIL_LISTIFY(DT_NUM_INST(DT_DRV_COMPAT), DT_INST_ON_BUS_OR, bus) 0) +#define DT_ANY_INST_ON_BUS(bus) DT_COMPAT_ON_BUS(DT_DRV_COMPAT, bus) /** * @def DT_INST_FOREACH @@ -1445,8 +1463,6 @@ #define DT_DASH(...) MACRO_MAP_CAT(DT_DASH_PREFIX, __VA_ARGS__) /** @internal helper for DT_DASH(): prepends _ to a 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 */ #define DT_CALL_WITH_ARG(arg, expr) expr(arg); diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index 6b86ec5b6e6..cf112f77e90 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -19,6 +19,7 @@ # edtlib. This will keep this script simple. import argparse +from collections import defaultdict import os import pathlib import re @@ -64,7 +65,7 @@ def main(): write_vanilla_props(node) write_chosen(edt) - write_inst_num(edt) + write_global_compat_info(edt) def parse_args(): @@ -510,26 +511,29 @@ def write_chosen(edt): out_define(macro, value, width=max_len) -def write_inst_num(edt): - # Tree-wide information such as number of instances is printed here. +def write_global_compat_info(edt): + # Tree-wide information related to each compatible, such as number + # of instances, is printed here. - out_comment("Number of instances\n") - compat_list = [] + compat2numinst = {} + 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 sorted(edt.nodes, key=lambda node: node.dep_ordinal): - if not node.enabled: - continue - if not node.matching_compat: - continue - for compat in node.compats: - if compat not in compat_list: - compat_list.append(compat) + for node in enabled: + bus = node.on_bus + if bus is not None and bus not in compat2buses[compat]: + compat2buses[compat].append(bus) - for compat in compat_list: - num_inst = len(edt.compat2enabled[compat]) - out_define(f"DT_N_INST_{str2ident(compat)}_NUM", num_inst) + out_comment("Number of enabled instances of each compatible\n") + for compat, numinst in compat2numinst.items(): + 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): # Converts 's' to a form suitable for (part of) an identifier diff --git a/tests/lib/devicetree/app.overlay b/tests/lib/devicetree/app.overlay index 568ff9a7e6a..56904ed9318 100644 --- a/tests/lib/devicetree/app.overlay +++ b/tests/lib/devicetree/app.overlay @@ -121,6 +121,12 @@ label = "TEST_I2C_DEV_10"; reg = < 0x10 >; }; + + gpio@11 { + compatible = "vnd,gpio-expander"; + reg = <0x11>; + label = "TEST_EXPANDER_I2C"; + }; }; test_spi: spi@33334444 { @@ -134,7 +140,8 @@ clock-frequency = < 2000000 >; 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 */ @@ -151,6 +158,13 @@ reg = <1>; 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 { diff --git a/tests/lib/devicetree/src/main.c b/tests/lib/devicetree/src/main.c index ecb7d05e725..5873d161168 100644 --- a/tests/lib/devicetree/src/main.c +++ b/tests/lib/devicetree/src/main.c @@ -292,6 +292,20 @@ static void test_bus(void) "inst 0 i2c dev label"); zassert_true(!strncmp(i2c_bus, DT_INST_BUS_LABEL(0), strlen(i2c_bus)), "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 @@ -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_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), "TEST_GPIO_1"),