35387e34c7db8237528e9e018545d46e2410aa97
[openwrt/staging/nbd.git] /
1 From b3ed8645c45567b598bef0868dca166f8ed166a0 Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
3 Date: Mon, 1 Jul 2024 13:30:08 +0200
4 Subject: [PATCH 06/11] platform: cznic: turris-omnia-mcu: Add support for MCU
5 provided TRNG
6 MIME-Version: 1.0
7 Content-Type: text/plain; charset=UTF-8
8 Content-Transfer-Encoding: 8bit
9
10 Add support for true random number generator provided by the MCU.
11 New Omnia boards come without the Atmel SHA204-A chip. Instead the
12 crypto functionality is provided by new microcontroller, which has
13 a TRNG peripheral.
14
15 Signed-off-by: Marek BehĂșn <kabel@kernel.org>
16 Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
17 Link: https://lore.kernel.org/r/20240701113010.16447-7-kabel@kernel.org
18 Signed-off-by: Arnd Bergmann <arnd@arndb.de>
19 ---
20 drivers/platform/cznic/Kconfig | 2 +
21 drivers/platform/cznic/Makefile | 1 +
22 .../platform/cznic/turris-omnia-mcu-base.c | 6 +-
23 .../platform/cznic/turris-omnia-mcu-gpio.c | 2 +-
24 .../platform/cznic/turris-omnia-mcu-trng.c | 105 ++++++++++++++++++
25 drivers/platform/cznic/turris-omnia-mcu.h | 8 ++
26 6 files changed, 122 insertions(+), 2 deletions(-)
27 create mode 100644 drivers/platform/cznic/turris-omnia-mcu-trng.c
28
29 --- a/drivers/platform/cznic/Kconfig
30 +++ b/drivers/platform/cznic/Kconfig
31 @@ -18,6 +18,7 @@ config TURRIS_OMNIA_MCU
32 depends on I2C
33 select GPIOLIB
34 select GPIOLIB_IRQCHIP
35 + select HW_RANDOM
36 select RTC_CLASS
37 select WATCHDOG_CORE
38 help
39 @@ -27,6 +28,7 @@ config TURRIS_OMNIA_MCU
40 - board poweroff into true low power mode (with voltage regulators
41 disabled) and the ability to configure wake up from this mode (via
42 rtcwake)
43 + - true random number generator (if available on the MCU)
44 - MCU watchdog
45 - GPIO pins
46 - to get front button press events (the front button can be
47 --- a/drivers/platform/cznic/Makefile
48 +++ b/drivers/platform/cznic/Makefile
49 @@ -4,4 +4,5 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris
50 turris-omnia-mcu-y := turris-omnia-mcu-base.o
51 turris-omnia-mcu-y += turris-omnia-mcu-gpio.o
52 turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o
53 +turris-omnia-mcu-y += turris-omnia-mcu-trng.o
54 turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o
55 --- a/drivers/platform/cznic/turris-omnia-mcu-base.c
56 +++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
57 @@ -380,7 +380,11 @@ static int omnia_mcu_probe(struct i2c_cl
58 if (err)
59 return err;
60
61 - return omnia_mcu_register_gpiochip(mcu);
62 + err = omnia_mcu_register_gpiochip(mcu);
63 + if (err)
64 + return err;
65 +
66 + return omnia_mcu_register_trng(mcu);
67 }
68
69 static const struct of_device_id of_omnia_mcu_match[] = {
70 --- a/drivers/platform/cznic/turris-omnia-mcu-gpio.c
71 +++ b/drivers/platform/cznic/turris-omnia-mcu-gpio.c
72 @@ -194,7 +194,7 @@ static const struct omnia_gpio omnia_gpi
73 };
74
75 /* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */
76 -static const u8 omnia_int_to_gpio_idx[32] = {
77 +const u8 omnia_int_to_gpio_idx[32] = {
78 [__bf_shf(OMNIA_INT_CARD_DET)] = 4,
79 [__bf_shf(OMNIA_INT_MSATA_IND)] = 5,
80 [__bf_shf(OMNIA_INT_USB30_OVC)] = 6,
81 --- /dev/null
82 +++ b/drivers/platform/cznic/turris-omnia-mcu-trng.c
83 @@ -0,0 +1,105 @@
84 +// SPDX-License-Identifier: GPL-2.0
85 +/*
86 + * CZ.NIC's Turris Omnia MCU TRNG driver
87 + *
88 + * 2024 by Marek BehĂșn <kabel@kernel.org>
89 + */
90 +
91 +#include <linux/bitfield.h>
92 +#include <linux/completion.h>
93 +#include <linux/container_of.h>
94 +#include <linux/errno.h>
95 +#include <linux/gpio/consumer.h>
96 +#include <linux/gpio/driver.h>
97 +#include <linux/hw_random.h>
98 +#include <linux/i2c.h>
99 +#include <linux/interrupt.h>
100 +#include <linux/minmax.h>
101 +#include <linux/string.h>
102 +#include <linux/types.h>
103 +
104 +#include "../../gpio/gpiolib.h"
105 +
106 +#include <linux/turris-omnia-mcu-interface.h>
107 +#include "turris-omnia-mcu.h"
108 +
109 +#define OMNIA_CMD_TRNG_MAX_ENTROPY_LEN 64
110 +
111 +static irqreturn_t omnia_trng_irq_handler(int irq, void *dev_id)
112 +{
113 + struct omnia_mcu *mcu = dev_id;
114 +
115 + complete(&mcu->trng_entropy_ready);
116 +
117 + return IRQ_HANDLED;
118 +}
119 +
120 +static int omnia_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
121 +{
122 + struct omnia_mcu *mcu = container_of(rng, struct omnia_mcu, trng);
123 + u8 reply[1 + OMNIA_CMD_TRNG_MAX_ENTROPY_LEN];
124 + int err, bytes;
125 +
126 + if (!wait && !completion_done(&mcu->trng_entropy_ready))
127 + return 0;
128 +
129 + do {
130 + if (wait_for_completion_interruptible(&mcu->trng_entropy_ready))
131 + return -ERESTARTSYS;
132 +
133 + err = omnia_cmd_read(mcu->client,
134 + OMNIA_CMD_TRNG_COLLECT_ENTROPY,
135 + reply, sizeof(reply));
136 + if (err)
137 + return err;
138 +
139 + bytes = min3(reply[0], max, OMNIA_CMD_TRNG_MAX_ENTROPY_LEN);
140 + } while (wait && !bytes);
141 +
142 + memcpy(data, &reply[1], bytes);
143 +
144 + return bytes;
145 +}
146 +
147 +int omnia_mcu_register_trng(struct omnia_mcu *mcu)
148 +{
149 + struct device *dev = &mcu->client->dev;
150 + u8 irq_idx, dummy;
151 + int irq, err;
152 +
153 + if (!(mcu->features & OMNIA_FEAT_TRNG))
154 + return 0;
155 +
156 + irq_idx = omnia_int_to_gpio_idx[__bf_shf(OMNIA_INT_TRNG)];
157 + irq = gpiod_to_irq(gpiochip_get_desc(&mcu->gc, irq_idx));
158 + if (!irq)
159 + return dev_err_probe(dev, -ENXIO, "Cannot get TRNG IRQ\n");
160 +
161 + /*
162 + * If someone else cleared the TRNG interrupt but did not read the
163 + * entropy, a new interrupt won't be generated, and entropy collection
164 + * will be stuck. Ensure an interrupt will be generated by executing
165 + * the collect entropy command (and discarding the result).
166 + */
167 + err = omnia_cmd_read(mcu->client, OMNIA_CMD_TRNG_COLLECT_ENTROPY,
168 + &dummy, 1);
169 + if (err)
170 + return err;
171 +
172 + init_completion(&mcu->trng_entropy_ready);
173 +
174 + err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler,
175 + IRQF_ONESHOT, "turris-omnia-mcu-trng",
176 + mcu);
177 + if (err)
178 + return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n");
179 +
180 + mcu->trng.name = "turris-omnia-mcu-trng";
181 + mcu->trng.read = omnia_trng_read;
182 +
183 + err = devm_hwrng_register(dev, &mcu->trng);
184 + if (err)
185 + return dev_err_probe(dev, err, "Cannot register TRNG\n");
186 +
187 + return 0;
188 +}
189 --- a/drivers/platform/cznic/turris-omnia-mcu.h
190 +++ b/drivers/platform/cznic/turris-omnia-mcu.h
191 @@ -9,7 +9,9 @@
192 #define __TURRIS_OMNIA_MCU_H
193
194 #include <linux/bitops.h>
195 +#include <linux/completion.h>
196 #include <linux/gpio/driver.h>
197 +#include <linux/hw_random.h>
198 #include <linux/if_ether.h>
199 #include <linux/mutex.h>
200 #include <linux/types.h>
201 @@ -47,6 +49,10 @@ struct omnia_mcu {
202
203 /* MCU watchdog */
204 struct watchdog_device wdt;
205 +
206 + /* true random number generator */
207 + struct hwrng trng;
208 + struct completion trng_entropy_ready;
209 };
210
211 int omnia_cmd_write_read(const struct i2c_client *client,
212 @@ -176,11 +182,13 @@ static inline int omnia_cmd_read_u8(cons
213 return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
214 }
215
216 +extern const u8 omnia_int_to_gpio_idx[32];
217 extern const struct attribute_group omnia_mcu_gpio_group;
218 extern const struct attribute_group omnia_mcu_poweroff_group;
219
220 int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
221 int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
222 +int omnia_mcu_register_trng(struct omnia_mcu *mcu);
223 int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);
224
225 #endif /* __TURRIS_OMNIA_MCU_H */