device_mmio: Introduce DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME

Currently the device MMIO APIs is only able to map single DT-defined
regions and also the _NAMED variant is assuming that each DT-defined
device has only one single region to map.

This is a limitation and a problem when in the DT are defined devices
with multiple regions that need to be mapped.

This patch is trying to overcome this limitation by introducing the
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME macro that leveraged the 'reg-names'
DT property to map multiple regions defined by a single device.

So for example in the DT we can have a device like:

  driver@c4000000 {
    reg = <0xc4000000 0x1000>, <0xc4001000 0x1000>;
    reg-names = "region0", "region1";
  };

and then we can use DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME doing:

  struct driver_config config = {
    DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(region0, DT_DRV_INST(0)),
    DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(region1, DT_DRV_INST(0)),
  };

Signed-off-by: Carlo Caione <ccaione@baylibre.com>
This commit is contained in:
Carlo Caione 2022-06-10 12:06:46 +02:00 committed by Carles Cufí
commit b6a3d598f3
5 changed files with 194 additions and 1 deletions

View file

@ -529,6 +529,37 @@ For example:
...
}
Device Model Drivers with multiple MMIO regions in the same DT node
===================================================================
Some drivers may have multiple MMIO regions defined into the same DT device
node using the ``reg-names`` property to differentiate them, for example:
.. code-block:: devicetree
/dts-v1/;
/ {
a-driver@40000000 {
reg = <0x40000000 0x1000>,
<0x40001000 0x1000>;
reg-names = "corge", "grault";
};
};
This can be managed as seen in the previous section but this time using the
``DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME`` macro instead. So the only difference
would be in the driver config struct:
.. code-block:: C
const static struct my_driver_config my_driver_config_0 = {
...
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(corge, DT_DRV_INST(...)),
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(grault, DT_DRV_INST(...)),
...
}
Drivers that do not use Zephyr Device Model
===========================================

View file

@ -62,6 +62,12 @@ struct z_device_mmio_rom {
.size = DT_REG_SIZE(node_id) \
}
#define Z_DEVICE_MMIO_NAMED_ROM_INITIALIZER(name, node_id) \
{ \
.phys_addr = DT_REG_ADDR_BY_NAME(node_id, name), \
.size = DT_REG_SIZE_BY_NAME(node_id, name) \
}
/**
* Set linear address for device MMIO access
*
@ -112,6 +118,12 @@ struct z_device_mmio_rom {
{ \
.addr = DT_REG_ADDR(node_id) \
}
#define Z_DEVICE_MMIO_NAMED_ROM_INITIALIZER(name, node_id) \
{ \
.addr = DT_REG_ADDR_BY_NAME(node_id, name) \
}
#endif /* DEVICE_MMIO_IS_IN_RAM */
#endif /* !_ASMLANGUAGE */
/** @} */
@ -435,6 +447,49 @@ struct z_device_mmio_rom {
#define DEVICE_MMIO_NAMED_ROM_INIT(name, node_id) \
.name = Z_DEVICE_MMIO_ROM_INITIALIZER(node_id)
/**
* @def DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(name, node_id)
*
* @brief Initialize a named DEVICE_MMIO_NAMED_ROM member using a named DT
* reg property.
*
* Same as @ref DEVICE_MMIO_NAMED_ROM_INIT but the size and address are taken
* from a named DT reg property.
*
* Example for an instance of a driver belonging to the "foo" subsystem
* that will have two DT-defined regions named 'chip' and 'dale':
*
* @code{.dts}
*
* foo@E5000000 {
* reg = <0xE5000000 0x1000>, <0xE6000000 0x1000>;
* reg-names = "chip", "dale";
* ...
* };
*
* @endcode
*
* @code{.c}
*
* struct foo_config my_config = {
* bar = 7;
* DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(chip, DT_DRV_INST(...));
* DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(dale, DT_DRV_INST(...));
* baz = 2;
* ...
* }
*
* @endcode
*
* @see DEVICE_MMIO_NAMED_ROM_INIT()
*
* @param name Member name within config for the MMIO region and name of the
* reg property in the DT
* @param node_id DTS node identifier
*/
#define DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(name, node_id) \
.name = Z_DEVICE_MMIO_NAMED_ROM_INITIALIZER(name, node_id)
/**
* @def DEVICE_MMIO_NAMED_MAP(dev, name, flags)
*

View file

@ -42,4 +42,13 @@
reg = <0xE4000000 0x2000>;
status = "okay";
};
fakedriver_multireg@E5000000 {
compatible = "fakedriver_multireg";
reg = <0xE5000000 0x1000>,
<0xE6000000 0x1000>;
reg-names = "chip",
"dale";
status = "okay";
};
};

View file

@ -24,6 +24,7 @@ extern void test_mmio_multiple(void);
extern void test_mmio_toplevel(void);
extern void test_mmio_single(void);
extern void test_mmio_device_map(void);
extern void test_mmio_multireg(void);
/**
* @brief Test cases to verify device objects
@ -364,6 +365,7 @@ void test_main(void)
ztest_unit_test(test_mmio_single),
ztest_unit_test(test_mmio_multiple),
ztest_unit_test(test_mmio_toplevel),
ztest_unit_test(test_mmio_device_map));
ztest_unit_test(test_mmio_device_map),
ztest_unit_test(test_mmio_multireg));
ztest_run_test_suite(device);
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2020 Intel Corporation
* Copyright (c) 2022 Carlo Caione <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <zephyr/device.h>
#define DT_DRV_COMPAT fakedriver_multireg
/*
* Driver with multiple MMIO regions to manage defined into DT
*/
struct foo_multireg_dev_data {
int baz;
DEVICE_MMIO_NAMED_RAM(chip);
DEVICE_MMIO_NAMED_RAM(dale);
};
struct foo_multireg_dev_data foo_multireg_data;
struct foo_multireg_config_info {
DEVICE_MMIO_NAMED_ROM(chip);
DEVICE_MMIO_NAMED_ROM(dale);
};
const struct foo_multireg_config_info foo_multireg_config = {
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(chip, DT_DRV_INST(0)),
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(dale, DT_DRV_INST(0))
};
#define DEV_DATA(dev) ((struct foo_multireg_dev_data *)((dev)->data))
#define DEV_CFG(dev) ((struct foo_multireg_config_info *)((dev)->config))
int foo_multireg_init(const struct device *dev)
{
DEVICE_MMIO_NAMED_MAP(dev, chip, K_MEM_CACHE_NONE);
DEVICE_MMIO_NAMED_MAP(dev, dale, K_MEM_CACHE_NONE);
return 0;
}
DEVICE_DEFINE(foo_multireg, "foo_multireg", foo_multireg_init, NULL,
&foo_multireg_data, &foo_multireg_config,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
(void *)0xDEADBEEF);
/**
* @brief Test DEVICE_MMIO_NAMED_* macros
*
* This is the same as the @ref test_mmio_multiple test but in this test the
* memory regions are created by the named DT property 'reg'.
*
* @see test_mmio_multiple
*
* @ingroup kernel_device_tests
*/
void test_mmio_multireg(void)
{
const struct device *dev = device_get_binding("foo_multireg");
mm_reg_t regs_chip, regs_dale;
const struct z_device_mmio_rom *rom_chip, *rom_dale;
zassert_not_null(dev, "null foo_multireg");
regs_chip = DEVICE_MMIO_NAMED_GET(dev, chip);
regs_dale = DEVICE_MMIO_NAMED_GET(dev, dale);
rom_chip = DEVICE_MMIO_NAMED_ROM_PTR(dev, chip);
rom_dale = DEVICE_MMIO_NAMED_ROM_PTR(dev, dale);
zassert_not_equal(regs_chip, 0, "bad regs_chip");
zassert_not_equal(regs_dale, 0, "bad regs_dale");
#ifdef DEVICE_MMIO_IS_IN_RAM
zassert_equal(rom_chip->phys_addr, DT_INST_REG_ADDR_BY_NAME(0, chip),
"bad phys_addr (chip)");
zassert_equal(rom_chip->size, DT_INST_REG_SIZE_BY_NAME(0, chip),
"bad size (chip)");
zassert_equal(rom_dale->phys_addr, DT_INST_REG_ADDR_BY_NAME(0, dale),
"bad phys_addr (dale)");
zassert_equal(rom_dale->size, DT_INST_REG_SIZE_BY_NAME(0, dale),
"bad size (dale)");
#else
zassert_equal(rom_chip->addr, DT_INST_REG_ADDR_BY_NAME(0, chip),
"bad addr (chip)");
zassert_equal(regs_chip, rom_chip->addr, "bad regs (chip)");
zassert_equal(rom_dale->addr, DT_INST_REG_ADDR_BY_NAME(0, dale),
"bad addr (dale)");
zassert_equal(regs_dale, rom_dale->addr, "bad regs (dale)");
zassert_equal(sizeof(struct foo_multireg_dev_data), sizeof(int),
"too big foo_multireg_dev_data");
#endif
}