Commit 41bb142a authored by Marek Behún's avatar Marek Behún Committed by Arnd Bergmann

platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG

Add support for true random number generator provided by the MCU.
New Omnia boards come without the Atmel SHA204-A chip. Instead the
crypto functionality is provided by new microcontroller, which has
a TRNG peripheral.
Signed-off-by: default avatarMarek Behún <kabel@kernel.org>
Acked-by: default avatarBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Link: https://lore.kernel.org/r/20240701113010.16447-7-kabel@kernel.orgSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parent ab89fb5f
...@@ -18,6 +18,7 @@ config TURRIS_OMNIA_MCU ...@@ -18,6 +18,7 @@ config TURRIS_OMNIA_MCU
depends on I2C depends on I2C
select GPIOLIB select GPIOLIB
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
select HW_RANDOM
select RTC_CLASS select RTC_CLASS
select WATCHDOG_CORE select WATCHDOG_CORE
help help
...@@ -27,6 +28,7 @@ config TURRIS_OMNIA_MCU ...@@ -27,6 +28,7 @@ config TURRIS_OMNIA_MCU
- board poweroff into true low power mode (with voltage regulators - board poweroff into true low power mode (with voltage regulators
disabled) and the ability to configure wake up from this mode (via disabled) and the ability to configure wake up from this mode (via
rtcwake) rtcwake)
- true random number generator (if available on the MCU)
- MCU watchdog - MCU watchdog
- GPIO pins - GPIO pins
- to get front button press events (the front button can be - to get front button press events (the front button can be
......
...@@ -4,4 +4,5 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o ...@@ -4,4 +4,5 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
turris-omnia-mcu-y := turris-omnia-mcu-base.o turris-omnia-mcu-y := turris-omnia-mcu-base.o
turris-omnia-mcu-y += turris-omnia-mcu-gpio.o turris-omnia-mcu-y += turris-omnia-mcu-gpio.o
turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o
turris-omnia-mcu-y += turris-omnia-mcu-trng.o
turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o
...@@ -381,7 +381,11 @@ static int omnia_mcu_probe(struct i2c_client *client) ...@@ -381,7 +381,11 @@ static int omnia_mcu_probe(struct i2c_client *client)
if (err) if (err)
return err; return err;
return omnia_mcu_register_gpiochip(mcu); err = omnia_mcu_register_gpiochip(mcu);
if (err)
return err;
return omnia_mcu_register_trng(mcu);
} }
static const struct of_device_id of_omnia_mcu_match[] = { static const struct of_device_id of_omnia_mcu_match[] = {
......
...@@ -195,7 +195,7 @@ static const struct omnia_gpio omnia_gpios[64] = { ...@@ -195,7 +195,7 @@ static const struct omnia_gpio omnia_gpios[64] = {
}; };
/* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */ /* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */
static const u8 omnia_int_to_gpio_idx[32] = { const u8 omnia_int_to_gpio_idx[32] = {
[__bf_shf(OMNIA_INT_CARD_DET)] = 4, [__bf_shf(OMNIA_INT_CARD_DET)] = 4,
[__bf_shf(OMNIA_INT_MSATA_IND)] = 5, [__bf_shf(OMNIA_INT_MSATA_IND)] = 5,
[__bf_shf(OMNIA_INT_USB30_OVC)] = 6, [__bf_shf(OMNIA_INT_USB30_OVC)] = 6,
......
// SPDX-License-Identifier: GPL-2.0
/*
* CZ.NIC's Turris Omnia MCU TRNG driver
*
* 2024 by Marek Behún <kabel@kernel.org>
*/
#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/container_of.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/hw_random.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/minmax.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/turris-omnia-mcu-interface.h>
#include "turris-omnia-mcu.h"
#define OMNIA_CMD_TRNG_MAX_ENTROPY_LEN 64
static irqreturn_t omnia_trng_irq_handler(int irq, void *dev_id)
{
struct omnia_mcu *mcu = dev_id;
complete(&mcu->trng_entropy_ready);
return IRQ_HANDLED;
}
static int omnia_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
struct omnia_mcu *mcu = container_of(rng, struct omnia_mcu, trng);
u8 reply[1 + OMNIA_CMD_TRNG_MAX_ENTROPY_LEN];
int err, bytes;
if (!wait && !completion_done(&mcu->trng_entropy_ready))
return 0;
do {
if (wait_for_completion_interruptible(&mcu->trng_entropy_ready))
return -ERESTARTSYS;
err = omnia_cmd_read(mcu->client,
OMNIA_CMD_TRNG_COLLECT_ENTROPY,
reply, sizeof(reply));
if (err)
return err;
bytes = min3(reply[0], max, OMNIA_CMD_TRNG_MAX_ENTROPY_LEN);
} while (wait && !bytes);
memcpy(data, &reply[1], bytes);
return bytes;
}
int omnia_mcu_register_trng(struct omnia_mcu *mcu)
{
struct device *dev = &mcu->client->dev;
u8 irq_idx, dummy;
int irq, err;
if (!(mcu->features & OMNIA_FEAT_TRNG))
return 0;
irq_idx = omnia_int_to_gpio_idx[__bf_shf(OMNIA_INT_TRNG)];
irq = gpiod_to_irq(gpio_device_get_desc(mcu->gc.gpiodev, irq_idx));
if (!irq)
return dev_err_probe(dev, -ENXIO, "Cannot get TRNG IRQ\n");
/*
* If someone else cleared the TRNG interrupt but did not read the
* entropy, a new interrupt won't be generated, and entropy collection
* will be stuck. Ensure an interrupt will be generated by executing
* the collect entropy command (and discarding the result).
*/
err = omnia_cmd_read(mcu->client, OMNIA_CMD_TRNG_COLLECT_ENTROPY,
&dummy, 1);
if (err)
return err;
init_completion(&mcu->trng_entropy_ready);
err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler,
IRQF_ONESHOT, "turris-omnia-mcu-trng",
mcu);
if (err)
return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n");
mcu->trng.name = "turris-omnia-mcu-trng";
mcu->trng.read = omnia_trng_read;
err = devm_hwrng_register(dev, &mcu->trng);
if (err)
return dev_err_probe(dev, err, "Cannot register TRNG\n");
return 0;
}
...@@ -9,7 +9,9 @@ ...@@ -9,7 +9,9 @@
#define __TURRIS_OMNIA_MCU_H #define __TURRIS_OMNIA_MCU_H
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/completion.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/hw_random.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -47,6 +49,10 @@ struct omnia_mcu { ...@@ -47,6 +49,10 @@ struct omnia_mcu {
/* MCU watchdog */ /* MCU watchdog */
struct watchdog_device wdt; struct watchdog_device wdt;
/* true random number generator */
struct hwrng trng;
struct completion trng_entropy_ready;
}; };
int omnia_cmd_write_read(const struct i2c_client *client, int omnia_cmd_write_read(const struct i2c_client *client,
...@@ -176,11 +182,13 @@ static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd, ...@@ -176,11 +182,13 @@ static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd,
return omnia_cmd_read(client, cmd, reply, sizeof(*reply)); return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
} }
extern const u8 omnia_int_to_gpio_idx[32];
extern const struct attribute_group omnia_mcu_gpio_group; extern const struct attribute_group omnia_mcu_gpio_group;
extern const struct attribute_group omnia_mcu_poweroff_group; extern const struct attribute_group omnia_mcu_poweroff_group;
int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu); int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu); int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
int omnia_mcu_register_trng(struct omnia_mcu *mcu);
int omnia_mcu_register_watchdog(struct omnia_mcu *mcu); int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);
#endif /* __TURRIS_OMNIA_MCU_H */ #endif /* __TURRIS_OMNIA_MCU_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment