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:
parent
a5375ead0c
commit
a3fae2f153
7 changed files with 100 additions and 30 deletions
|
@ -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
|
||||||
|
|
8
dts/bindings/test/vnd,gpio-expander-i2c.yaml
Normal file
8
dts/bindings/test/vnd,gpio-expander-i2c.yaml
Normal 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
|
8
dts/bindings/test/vnd,gpio-expander-spi.yaml
Normal file
8
dts/bindings/test/vnd,gpio-expander-spi.yaml
Normal 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
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue