diff --git a/drivers/entropy/CMakeLists.txt b/drivers/entropy/CMakeLists.txt index d5588378f6f3..238416b57f53 100644 --- a/drivers/entropy/CMakeLists.txt +++ b/drivers/entropy/CMakeLists.txt @@ -34,6 +34,7 @@ zephyr_library_sources_ifdef(CONFIG_ENTROPY_GECKO_SE entropy_gecko_se.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_PSA_CRYPTO_RNG entropy_psa_crypto.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_NPCX_DRBG entropy_npcx_drbg.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_MAX32_TRNG entropy_max32.c) +zephyr_library_sources_ifdef(CONFIG_ENTROPY_MAXQ10XX_RNG entropy_maxq10xx.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_RENESAS_RA entropy_renesas_ra.c) zephyr_library_link_libraries_ifdef(CONFIG_BUILD_WITH_TFM tfm_api) diff --git a/drivers/entropy/Kconfig b/drivers/entropy/Kconfig index debd0f8d7a11..c2a19878b07f 100644 --- a/drivers/entropy/Kconfig +++ b/drivers/entropy/Kconfig @@ -37,6 +37,7 @@ source "drivers/entropy/Kconfig.bt_hci" source "drivers/entropy/Kconfig.psa_crypto" source "drivers/entropy/Kconfig.npcx" source "drivers/entropy/Kconfig.max32" +source "drivers/entropy/Kconfig.maxq10xx" source "drivers/entropy/Kconfig.renesas_ra" config ENTROPY_HAS_DRIVER diff --git a/drivers/entropy/Kconfig.maxq10xx b/drivers/entropy/Kconfig.maxq10xx new file mode 100644 index 000000000000..e7e5ee66b05f --- /dev/null +++ b/drivers/entropy/Kconfig.maxq10xx @@ -0,0 +1,22 @@ +# MAXQ10XX entropy generator driver configuration + +# Copyright (c) 2025 Vogl Electronic GmbH +# SPDX-License-Identifier: Apache-2.0 + +config ENTROPY_MAXQ10XX_RNG + bool "MAXQ10xx entropy number generator driver" + default y + depends on DT_HAS_ADI_MAXQ10XX_TRNG_ENABLED + select ENTROPY_HAS_DRIVER + select SPI + select MFD + select CRC + help + This option enables the entropy number generator driver for the + MAXQ10xx crypto chips. + +# Don't use use the MAXQ10XX RNG as a random source since it can be quite slow. +# Instead, use the software implemented xoshiro RNG. +choice RNG_GENERATOR_CHOICE + default XOSHIRO_RANDOM_GENERATOR if ENTROPY_MAXQ10XX_RNG +endchoice diff --git a/drivers/entropy/entropy_maxq10xx.c b/drivers/entropy/entropy_maxq10xx.c new file mode 100644 index 000000000000..a0d12a0f5e36 --- /dev/null +++ b/drivers/entropy/entropy_maxq10xx.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2025 Vogl Electronic GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_maxq10xx_trng + +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(entropy_maxq10xx, CONFIG_ENTROPY_LOG_LEVEL); + +#define MAXQ10XX_CMD_HEADER 0xAA +#define MAXQ10XX_CMD_GET_RANDOM 0xC9 +#define MAXQ10XX_CMD_GET_RANDOM_INPUT_DATA 0x02 +#define MAXQ10XX_CMD_READ_READY 0x55 + +#define MAXQ10XX_CRC16_POLYNOMIAL 0xA001 +#define MAXQ10XX_CRC16_INITIAL_VALUE 0x0000 + +#define MAXQ10XX_WAIT_TIME K_MSEC(1) + +struct entropy_maxq10xx_config { + struct spi_dt_spec spi; + const struct device *parent; +}; + +static int entropy_maxq10xx_send_cmd(const struct device *dev, uint16_t length) +{ + const struct entropy_maxq10xx_config *config = dev->config; + + uint8_t buffer_tx[9]; + uint16_t crc; + int ret; + + buffer_tx[0] = MAXQ10XX_CMD_HEADER; + buffer_tx[1] = 0x00; + buffer_tx[2] = MAXQ10XX_CMD_GET_RANDOM; + buffer_tx[3] = 0x00; + buffer_tx[4] = MAXQ10XX_CMD_GET_RANDOM_INPUT_DATA; + + sys_put_be16(length, &buffer_tx[5]); + + crc = crc16_reflect(MAXQ10XX_CRC16_POLYNOMIAL, MAXQ10XX_CRC16_INITIAL_VALUE, buffer_tx, 7); + + sys_put_le16(crc, &buffer_tx[7]); + + const struct spi_buf tx_buf[] = {{ + .buf = buffer_tx, + .len = ARRAY_SIZE(buffer_tx), + }}; + const struct spi_buf_set tx = { + .buffers = tx_buf, + .count = ARRAY_SIZE(tx_buf), + }; + + LOG_HEXDUMP_DBG(buffer_tx, sizeof(buffer_tx), "TX buffer"); + + ret = spi_write_dt(&config->spi, &tx); + + return ret; +} + +static int entropy_maxq10xx_wait(const struct device *dev) +{ + const struct entropy_maxq10xx_config *config = dev->config; + uint8_t buffer_rx[1]; + int ret; + + const struct spi_buf rx_buf[] = {{ + .buf = buffer_rx, + .len = ARRAY_SIZE(buffer_rx), + }}; + const struct spi_buf_set rx = { + .buffers = rx_buf, + .count = ARRAY_SIZE(rx_buf), + }; + + while (1) { + ret = spi_read_dt(&config->spi, &rx); + if ((ret < 0) || (buffer_rx[0] == MAXQ10XX_CMD_READ_READY)) { + break; + } + + k_sleep(MAXQ10XX_WAIT_TIME); + }; + + return ret; +} + +static int entropy_maxq10xx_read(const struct device *dev, uint8_t *buffer, uint16_t length) +{ + const struct entropy_maxq10xx_config *config = dev->config; + uint8_t execution_status[2]; + uint8_t length_data[2]; + uint8_t crc[2]; + uint16_t crc_calc; + int ret; + + const struct spi_buf rx_buf[] = { + { + .buf = execution_status, + .len = ARRAY_SIZE(execution_status), + }, + { + .buf = length_data, + .len = ARRAY_SIZE(length_data), + } + }; + const struct spi_buf_set rx = { + .buffers = rx_buf, + .count = ARRAY_SIZE(rx_buf), + }; + + ret = spi_read_dt(&config->spi, &rx); + if (ret < 0) { + return ret; + } + + if (execution_status[0] != 0x00 || execution_status[1] != 0x00) { + LOG_DBG("Execution status: 0x%02X 0x%02X", execution_status[0], + execution_status[1]); + return -EIO; + } + + if (length != sys_get_be16(length_data)) { + LOG_ERR("Length mismatch: %d != %d", length, sys_get_be16(length_data)); + return -EIO; + } + + const struct spi_buf rx_data_buf[] = { + { + .buf = buffer, + .len = length, + }, + { + .buf = crc, + .len = sizeof(crc), + } + }; + + const struct spi_buf_set rx_data = { + .buffers = rx_data_buf, + .count = ARRAY_SIZE(rx_data_buf), + }; + + ret = spi_read_dt(&config->spi, &rx_data); + if (ret < 0) { + return ret; + } + + uint8_t header_tx[1] = {MAXQ10XX_CMD_READ_READY}; + + crc_calc = crc16_reflect(MAXQ10XX_CRC16_POLYNOMIAL, MAXQ10XX_CRC16_INITIAL_VALUE, header_tx, + sizeof(header_tx)); + crc_calc = crc16_reflect(MAXQ10XX_CRC16_POLYNOMIAL, crc_calc, execution_status, + sizeof(execution_status)); + crc_calc = crc16_reflect(MAXQ10XX_CRC16_POLYNOMIAL, crc_calc, length_data, + sizeof(length_data)); + crc_calc = crc16_reflect(MAXQ10XX_CRC16_POLYNOMIAL, crc_calc, buffer, length); + + if (crc_calc != sys_get_le16(crc)) { + LOG_ERR("CRC error: 0x%04X != 0x%04X", crc_calc, sys_get_le16(crc)); + return -EIO; + } + + return ret; +} + +static int entropy_maxq10xx_get_entropy(const struct device *dev, uint8_t *buffer, uint16_t length) +{ + const struct entropy_maxq10xx_config *config = dev->config; + struct k_sem *sem_lock = mfd_maxq10xx_get_lock(config->parent); + + int ret; + + if (!spi_is_ready_dt(&config->spi)) { + return -EAGAIN; + } + + k_sem_take(sem_lock, K_FOREVER); + + ret = entropy_maxq10xx_send_cmd(dev, length); + if (ret < 0) { + LOG_ERR("Failed to send command"); + goto exit; + } + + ret = entropy_maxq10xx_wait(dev); + if (ret < 0) { + LOG_ERR("Failed to wait for ready"); + goto exit; + } + + ret = entropy_maxq10xx_read(dev, buffer, length); + if (ret < 0) { + LOG_ERR("Failed to read data"); + } +exit: + k_sem_give(sem_lock); + return ret; +} + +static int entropy_maxq10xx_init(const struct device *dev) +{ + /* Nothing to do */ + return 0; +} + +static DEVICE_API(entropy, entropy_maxq10xx_api) = { + .get_entropy = entropy_maxq10xx_get_entropy +}; + +#define DEFINE_MAXQ10XX_ENTROPY(_num) \ + static const struct entropy_maxq10xx_config entropy_maxq10xx_config##_num = { \ + .spi = SPI_DT_SPEC_GET(DT_INST_PARENT(_num), SPI_WORD_SET(8), 0), \ + .parent = DEVICE_DT_GET(DT_INST_PARENT(_num)), \ + }; \ + DEVICE_DT_INST_DEFINE(_num, entropy_maxq10xx_init, NULL, NULL, \ + &entropy_maxq10xx_config##_num, POST_KERNEL, \ + CONFIG_MFD_MAXQ10XX_INIT_PRIORITY, &entropy_maxq10xx_api); + +DT_INST_FOREACH_STATUS_OKAY(DEFINE_MAXQ10XX_ENTROPY); diff --git a/drivers/mfd/CMakeLists.txt b/drivers/mfd/CMakeLists.txt index 8fe7ac34caee..0fa5ab6dbf0d 100644 --- a/drivers/mfd/CMakeLists.txt +++ b/drivers/mfd/CMakeLists.txt @@ -14,6 +14,7 @@ zephyr_library_sources_ifdef(CONFIG_MFD_AD559X mfd_ad559x.c) zephyr_library_sources_ifdef(CONFIG_MFD_AD559X_BUS_I2C mfd_ad559x_i2c.c) zephyr_library_sources_ifdef(CONFIG_MFD_AD559X_BUS_SPI mfd_ad559x_spi.c) zephyr_library_sources_ifdef(CONFIG_MFD_MAX31790 mfd_max31790.c) +zephyr_library_sources_ifdef(CONFIG_MFD_MAXQ10XX mfd_maxq10xx.c) zephyr_library_sources_ifdef(CONFIG_NXP_LP_FLEXCOMM mfd_nxp_lp_flexcomm.c) zephyr_library_sources_ifdef(CONFIG_MFD_BD8LB600FS mfd_bd8lb600fs.c) zephyr_library_sources_ifdef(CONFIG_MFD_TLE9104 mfd_tle9104.c) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b11e3dfd4b24..07f5ff72c928 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -26,6 +26,7 @@ source "drivers/mfd/Kconfig.bd8lb600fs" source "drivers/mfd/Kconfig.ds3231" source "drivers/mfd/Kconfig.max20335" source "drivers/mfd/Kconfig.max31790" +source "drivers/mfd/Kconfig.maxq10xx" source "drivers/mfd/Kconfig.nct38xx" source "drivers/mfd/Kconfig.npm1300" source "drivers/mfd/Kconfig.npm2100" diff --git a/drivers/mfd/Kconfig.maxq10xx b/drivers/mfd/Kconfig.maxq10xx new file mode 100644 index 000000000000..76422cb38561 --- /dev/null +++ b/drivers/mfd/Kconfig.maxq10xx @@ -0,0 +1,26 @@ +# MAXQ10XX entropy generator driver configuration + +# Copyright (c) 2025 Vogl Electronic GmbH +# SPDX-License-Identifier: Apache-2.0 + +config MFD_MAXQ10XX + bool "MAXQ10xx mfd driver" + default y + depends on DT_HAS_ADI_MAXQ10XX_ENABLED + # using select SPI at this point introduces a cyclic dependency + depends on SPI + help + This option enables the mfd driver for the + MAXQ10xx crypto chips. + +if MFD_MAXQ10XX + +config MFD_MAXQ10XX_INIT_PRIORITY + int "MAXQ10xx init priority" + default 51 + help + Device driver initialization priority. + Device is connected to SPI bus, it has to + be initialized after SPI driver. + +endif # MFD_MAXQ10XX diff --git a/drivers/mfd/mfd_maxq10xx.c b/drivers/mfd/mfd_maxq10xx.c new file mode 100644 index 000000000000..d5c5642783f3 --- /dev/null +++ b/drivers/mfd/mfd_maxq10xx.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 Vogl Electronic GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_maxq10xx + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(mfd_maxq10xx, CONFIG_MFD_LOG_LEVEL); + +struct mfd_maxq10xx_config { + struct spi_dt_spec spi; +}; + +struct mfd_maxq10xx_data { + struct k_sem sem_lock; +}; + +struct k_sem *mfd_maxq10xx_get_lock(const struct device *dev) +{ + struct mfd_maxq10xx_data *data = dev->data; + + return &data->sem_lock; +} + +static int mfd_maxq10xx_init(const struct device *dev) +{ + const struct mfd_maxq10xx_config *config = dev->config; + + if (!spi_is_ready_dt(&config->spi)) { + return -ENODEV; + } + + return 0; +} + +BUILD_ASSERT(CONFIG_SPI_INIT_PRIORITY < CONFIG_MFD_MAXQ10XX_INIT_PRIORITY, + "SPI driver must be initialized before maxq10xx mfd driver"); + +#define DEFINE_MAXQ10XX_MFD(_num) \ + static const struct mfd_maxq10xx_config mfd_maxq10xx_config##_num = { \ + .spi = SPI_DT_SPEC_INST_GET(_num, SPI_WORD_SET(8), 0), \ + }; \ + static struct mfd_maxq10xx_data mfd_maxq10xx_data##_num = { \ + .sem_lock = Z_SEM_INITIALIZER(mfd_maxq10xx_data##_num.sem_lock, 1, 1), \ + }; \ + DEVICE_DT_INST_DEFINE(_num, mfd_maxq10xx_init, NULL, &mfd_maxq10xx_data##_num, \ + &mfd_maxq10xx_config##_num, POST_KERNEL, \ + CONFIG_MFD_MAXQ10XX_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(DEFINE_MAXQ10XX_MFD); diff --git a/dts/bindings/mfd/adi,maxq10xx.yaml b/dts/bindings/mfd/adi,maxq10xx.yaml new file mode 100644 index 000000000000..2db03be3daec --- /dev/null +++ b/dts/bindings/mfd/adi,maxq10xx.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 Vogl Electronic GmbH +# SPDX-License-Identifier: Apache-2.0 + +description: ADI MAXQ10XX + +compatible: "adi,maxq10xx" + +include: spi-device.yaml diff --git a/dts/bindings/rng/adi,maxq10xx-trng.yaml b/dts/bindings/rng/adi,maxq10xx-trng.yaml new file mode 100644 index 000000000000..5d18d467d753 --- /dev/null +++ b/dts/bindings/rng/adi,maxq10xx-trng.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Vogl Electronic GmbH +# SPDX-License-Identifier: Apache-2.0 + +description: ADI MAXQ10XX RNG + +compatible: "adi,maxq10xx-trng" + +include: base.yaml diff --git a/include/zephyr/drivers/mfd/mfd_maxq10xx.h b/include/zephyr/drivers/mfd/mfd_maxq10xx.h new file mode 100644 index 000000000000..2ffeec9cd2e3 --- /dev/null +++ b/include/zephyr/drivers/mfd/mfd_maxq10xx.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 Vogl Electronic GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_MAXQ1XX_H_ +#define ZEPHYR_INCLUDE_DRIVERS_MFD_MAXQ1XX_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get the semaphore reference for a MAXQ1xx instance. Callers + * should pass the return value to k_sem_take/k_sem_give + * + * @param[in] dev Pointer to device struct of the driver instance + * + * @return Address of the semaphore + */ +struct k_sem *mfd_maxq10xx_get_lock(const struct device *dev); + + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_MAXQ1XX_H_ */ diff --git a/tests/drivers/build_all/mfd/app.overlay b/tests/drivers/build_all/mfd/app.overlay index 5f4ae6e8a8e4..92e4cd79c003 100644 --- a/tests/drivers/build_all/mfd/app.overlay +++ b/tests/drivers/build_all/mfd/app.overlay @@ -40,5 +40,37 @@ }; }; }; + + test_gpio: gpio@deadbeef { + compatible = "vnd,gpio"; + gpio-controller; + reg = <0xdeadbeef 0x1000>; + #gpio-cells = <0x2>; + status = "okay"; + }; + + test_spi: spi@33334444 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "vnd,spi"; + reg = <0x33334444 0x1000>; + status = "okay"; + clock-frequency = <2000000>; + + /* one entry for every spi devices */ + cs-gpios = <&test_gpio 0 0>; + + test_spi_maxq10xx@0 { + compatible = "adi,maxq10xx"; + reg = <0x0>; + status = "okay"; + spi-max-frequency = <0>; + + test_spi_maxq10xx_trng: trng { + compatible = "adi,maxq10xx-trng"; + status = "okay"; + }; + }; + }; }; }; diff --git a/tests/drivers/build_all/mfd/prj.conf b/tests/drivers/build_all/mfd/prj.conf index 3862bfcbf6d7..78155e5638e6 100644 --- a/tests/drivers/build_all/mfd/prj.conf +++ b/tests/drivers/build_all/mfd/prj.conf @@ -2,5 +2,8 @@ CONFIG_TEST=y CONFIG_GPIO=y CONFIG_TEST_USERSPACE=y CONFIG_I2C=y +CONFIG_SPI=y +CONFIG_MFD=y +CONFIG_ENTROPY_GENERATOR=y CONFIG_SERIAL=y CONFIG_UART_USE_RUNTIME_CONFIGURE=y