From: Lothar Rubusch <l.rubusch@gmail.com>
To: thorsten.blum@linux.dev, herbert@gondor.apana.org.au,
davem@davemloft.net, nicolas.ferre@microchip.com,
alexandre.belloni@bootlin.com, claudiu.beznea@tuxon.dev
Cc: linux-crypto@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, l.rubusch@gmail.com
Subject: [PATCH 05/12] crypto: atmel - move RNG support into common i2c core
Date: Tue, 12 May 2026 22:43:42 +0000 [thread overview]
Message-ID: <20260512224349.64621-6-l.rubusch@gmail.com> (raw)
In-Reply-To: <20260512224349.64621-1-l.rubusch@gmail.com>
Move the hardware RNG implementation from atmel-sha204a into the
shared atmel-i2c core.
The ATSHA204(A) and ATECC devices provide compatible RANDOM commands
through the common Atmel I2C interface. Consolidate the RNG handling in
the core driver and provide a shared atmel_i2c_register_rng() helper for
registering the hwrng device.
This removes duplicated RNG code from atmel-sha204a and enables RNG
support for other compatible Atmel devices, including the ECC family.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 12 ++++
drivers/crypto/atmel-i2c.c | 93 ++++++++++++++++++++++++++
drivers/crypto/atmel-i2c.h | 4 +-
drivers/crypto/atmel-sha204a.c | 115 +++++----------------------------
4 files changed, 123 insertions(+), 101 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 7793f7b4e97e..67fa5975fa7f 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -306,6 +306,13 @@ static int atmel_ecc_probe(struct i2c_client *client)
&atmel_i2c_mgmt.i2c_client_list);
spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+ /* register rng */
+ ret = atmel_i2c_register_rng(i2c_priv, &client->dev);
+ if (ret) {
+ dev_err(&client->dev, "failed to register hw_random\n");
+ goto err_list_del;
+ }
+
/* register algorithms */
ret = crypto_register_kpp(&atmel_ecdh_nist_p256);
if (ret) {
@@ -349,6 +356,11 @@ static void atmel_ecc_remove(struct i2c_client *client)
atmel_i2c_flush_queue();
crypto_unregister_kpp(&atmel_ecdh_nist_p256);
+
+ if (i2c_priv->hwrng.priv) {
+ kfree((void *)i2c_priv->hwrng.priv);
+ i2c_priv->hwrng.priv = 0;
+ }
}
static const struct atmel_i2c_of_match_data atecc508a_match_data = {
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index 7fa7cf9ab3c1..d451017171d8 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -208,6 +208,99 @@ int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
}
EXPORT_SYMBOL(atmel_i2c_init_ecdh_cmd);
+static void atmel_i2c_rng_done(struct atmel_i2c_work_data *work_data,
+ void *areq, int status)
+{
+ struct atmel_i2c_client_priv *i2c_priv = work_data->ctx;
+ struct hwrng *rng = areq;
+
+ if (status)
+ dev_warn_ratelimited(&i2c_priv->client->dev,
+ "i2c transaction failed (%d)\n",
+ status);
+
+ rng->priv = (unsigned long)work_data;
+ atomic_dec(&i2c_priv->tfm_count);
+}
+
+static int atmel_i2c_rng_read_nonblocking(struct hwrng *rng, void *buf,
+ size_t max)
+{
+ struct atmel_i2c_client_priv *i2c_priv = container_of(rng,
+ struct atmel_i2c_client_priv,
+ hwrng);
+ const struct atmel_i2c_of_match_data *data = i2c_priv->data;
+ struct atmel_i2c_work_data *work_data;
+
+ /* keep maximum 1 asynchronous read in flight at any time */
+ if (!atomic_add_unless(&i2c_priv->tfm_count, 1, 1))
+ return 0;
+
+ if (rng->priv) {
+ work_data = (struct atmel_i2c_work_data *)rng->priv;
+ max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max);
+ memcpy(buf, &work_data->cmd.data[RSP_DATA_IDX], max);
+ rng->priv = 0;
+ } else {
+ work_data = kmalloc_obj(*work_data, GFP_ATOMIC);
+ if (!work_data) {
+ atomic_dec(&i2c_priv->tfm_count);
+ return -ENOMEM;
+ }
+ work_data->ctx = i2c_priv;
+ work_data->client = i2c_priv->client;
+
+ max = 0;
+ }
+
+ atmel_i2c_init_random_cmd(&work_data->cmd, &data->timings);
+ atmel_i2c_enqueue(work_data, atmel_i2c_rng_done, rng);
+
+ return max;
+}
+
+static int atmel_i2c_rng_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct atmel_i2c_client_priv *i2c_priv = container_of(rng,
+ struct atmel_i2c_client_priv,
+ hwrng);
+ const struct atmel_i2c_of_match_data *data = i2c_priv->data;
+ struct atmel_i2c_cmd cmd;
+ int ret;
+
+ if (!wait)
+ return atmel_i2c_rng_read_nonblocking(rng, buf, max);
+
+ atmel_i2c_init_random_cmd(&cmd, &data->timings);
+
+ ret = atmel_i2c_send_receive(i2c_priv->client, &cmd);
+ if (ret)
+ return ret;
+
+ max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max);
+ memcpy(buf, &cmd.data[RSP_DATA_IDX], max);
+
+ return max;
+}
+
+int atmel_i2c_register_rng(struct atmel_i2c_client_priv *i2c_priv,
+ struct device *dev)
+{
+ const struct atmel_i2c_of_match_data *data = i2c_priv->data;
+
+ memset(&i2c_priv->hwrng, 0, sizeof(i2c_priv->hwrng));
+
+ i2c_priv->hwrng.name = dev_name(dev);
+ i2c_priv->hwrng.read = atmel_i2c_rng_read;
+
+ if (data->needs_legacy_hwrng)
+ i2c_priv->hwrng.quality = data->needs_legacy_hwrng;
+
+ return devm_hwrng_register(dev, &i2c_priv->hwrng);
+}
+EXPORT_SYMBOL(atmel_i2c_register_rng);
+
/*
* After wake and after execution of a command, there will be error, status, or
* result bytes in the device's output register that can be retrieved by the
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 5224a62c16c9..5f6c9ff0cf64 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -66,7 +66,7 @@ struct atmel_i2c_max_exec_timings {
};
struct atmel_i2c_of_match_data {
- const unsigned short *legacy_hwrng;
+ const unsigned short needs_legacy_hwrng;
struct atmel_i2c_max_exec_timings timings;
};
@@ -209,6 +209,8 @@ void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid,
int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
struct scatterlist *pubkey,
const struct atmel_i2c_max_exec_timings *timings);
+int atmel_i2c_register_rng(struct atmel_i2c_client_priv *i2c_priv,
+ struct device *dev);
struct i2c_client *atmel_i2c_client_alloc(enum atmel_i2c_capability cap);
void atmel_i2c_unregister_client(struct atmel_i2c_client_priv *i2c_priv);
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index febf9891b167..ae24d8fbabf9 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -19,88 +19,6 @@
#include <linux/workqueue.h>
#include "atmel-i2c.h"
-/*
- * According to review by Bill Cox [1], the ATSHA204 has very low entropy.
- * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html
- */
-static const unsigned short atsha204_quality = 1;
-
-static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data,
- void *areq, int status)
-{
- struct atmel_i2c_client_priv *i2c_priv = work_data->ctx;
- struct hwrng *rng = areq;
-
- if (status)
- dev_warn_ratelimited(&i2c_priv->client->dev,
- "i2c transaction failed (%d)\n",
- status);
-
- rng->priv = (unsigned long)work_data;
- atomic_dec(&i2c_priv->tfm_count);
-}
-
-static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *buf,
- size_t max)
-{
- struct atmel_i2c_client_priv *i2c_priv = container_of(rng,
- struct atmel_i2c_client_priv,
- hwrng);
- const struct atmel_i2c_of_match_data *data = i2c_priv->data;
- struct atmel_i2c_work_data *work_data;
-
- /* keep maximum 1 asynchronous read in flight at any time */
- if (!atomic_add_unless(&i2c_priv->tfm_count, 1, 1))
- return 0;
-
- if (rng->priv) {
- work_data = (struct atmel_i2c_work_data *)rng->priv;
- max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max);
- memcpy(buf, &work_data->cmd.data[RSP_DATA_IDX], max);
- rng->priv = 0;
- } else {
- work_data = kmalloc_obj(*work_data, GFP_ATOMIC);
- if (!work_data) {
- atomic_dec(&i2c_priv->tfm_count);
- return -ENOMEM;
- }
- work_data->ctx = i2c_priv;
- work_data->client = i2c_priv->client;
-
- max = 0;
- }
-
- atmel_i2c_init_random_cmd(&work_data->cmd, &data->timings);
- atmel_i2c_enqueue(work_data, atmel_sha204a_rng_done, rng);
-
- return max;
-}
-
-static int atmel_sha204a_rng_read(struct hwrng *rng, void *buf, size_t max,
- bool wait)
-{
- struct atmel_i2c_client_priv *i2c_priv = container_of(rng,
- struct atmel_i2c_client_priv,
- hwrng);
- const struct atmel_i2c_of_match_data *data = i2c_priv->data;
- struct atmel_i2c_cmd cmd;
- int ret;
-
- if (!wait)
- return atmel_sha204a_rng_read_nonblocking(rng, buf, max);
-
- atmel_i2c_init_random_cmd(&cmd, &data->timings);
-
- ret = atmel_i2c_send_receive(i2c_priv->client, &cmd);
- if (ret)
- return ret;
-
- max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max);
- memcpy(buf, &cmd.data[RSP_DATA_IDX], max);
-
- return max;
-}
-
static int atmel_sha204a_otp_read(struct i2c_client *client, u16 addr, u8 *otp)
{
struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
@@ -169,7 +87,6 @@ static int atmel_sha204a_probe(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv;
const struct atmel_i2c_of_match_data *data;
- const unsigned short *quality;
int ret;
ret = atmel_i2c_probe(client);
@@ -193,25 +110,16 @@ static int atmel_sha204a_probe(struct i2c_client *client)
&atmel_i2c_mgmt.i2c_client_list);
spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
- /* register rng */
- memset(&i2c_priv->hwrng, 0, sizeof(i2c_priv->hwrng));
-
- i2c_priv->hwrng.name = dev_name(&client->dev);
- i2c_priv->hwrng.read = atmel_sha204a_rng_read;
-
- quality = i2c_priv->data->legacy_hwrng;
- if (quality)
- i2c_priv->hwrng.quality = *quality;
-
- ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng);
+ ret = sysfs_create_group(&client->dev.kobj, &atmel_sha204a_groups);
if (ret) {
- dev_warn(&client->dev, "failed to register RNG (%d)\n", ret);
+ dev_err(&client->dev, "failed to register sysfs entry\n");
goto err_list_del;
}
- ret = sysfs_create_group(&client->dev.kobj, &atmel_sha204a_groups);
+ /* register rng */
+ ret = atmel_i2c_register_rng(i2c_priv, &client->dev);
if (ret) {
- dev_err(&client->dev, "failed to register sysfs entry\n");
+ dev_err(&client->dev, "failed to register hw_random\n");
goto err_list_del;
}
@@ -234,9 +142,12 @@ static void atmel_sha204a_remove(struct i2c_client *client)
devm_hwrng_unregister(&client->dev, &i2c_priv->hwrng);
atmel_i2c_flush_queue();
- sysfs_remove_group(&client->dev.kobj, &atmel_sha204a_groups);
+ if (i2c_priv->hwrng.priv) {
+ kfree((void *)i2c_priv->hwrng.priv);
+ i2c_priv->hwrng.priv = 0;
+ }
- kfree((void *)i2c_priv->hwrng.priv);
+ sysfs_remove_group(&client->dev.kobj, &atmel_sha204a_groups);
}
static const struct atmel_i2c_of_match_data atsha204_match_data = {
@@ -246,7 +157,11 @@ static const struct atmel_i2c_of_match_data atsha204_match_data = {
.max_exec_time_read = 4,
.max_exec_time_write = 42,
},
- .legacy_hwrng = &atsha204_quality,
+ /*
+ * According to review by Bill Cox [1], the ATSHA204 has very low entropy.
+ * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html
+ */
+ .needs_legacy_hwrng = 1,
};
static const struct atmel_i2c_of_match_data atsha204a_match_data = {
--
2.53.0
next prev parent reply other threads:[~2026-05-12 22:44 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-12 22:43 [PATCH 00/12] crypto: atmel - refactor common i2c support and add SHA256 ahash support Lothar Rubusch
2026-05-12 22:43 ` [PATCH 01/12] crypto: atmel - introduce shared I2C client management Lothar Rubusch
2026-05-12 22:43 ` [PATCH 02/12] crypto: atmel - move capability-based client allocation into i2c core Lothar Rubusch
2026-05-12 22:43 ` [PATCH 03/12] crypto: atmel - remove obsolete CONFIG_OF guard Lothar Rubusch
2026-05-12 22:43 ` [PATCH 04/12] crypto: atmel - add per-device timing and match-data driven configuration Lothar Rubusch
2026-05-12 22:43 ` Lothar Rubusch [this message]
2026-05-12 22:43 ` [PATCH 06/12] crypto: atmel - move EEPROM access support into common i2c core Lothar Rubusch
2026-05-12 22:43 ` [PATCH 07/12] crypto: atmel - expose CONFIG zone through sysfs Lothar Rubusch
2026-05-12 22:43 ` [PATCH 08/12] crypto: atmel - move device sanity check to core driver Lothar Rubusch
2026-05-12 22:43 ` [PATCH 09/12] crypto: atmel - check client data in remove callbacks Lothar Rubusch
2026-05-12 22:43 ` [PATCH 10/12] crypto: atmel - update workqueue flags and add flush on exit Lothar Rubusch
2026-05-12 22:43 ` [PATCH 11/12] crypto: atmel - refactor and localize driver constants Lothar Rubusch
2026-05-12 22:43 ` [PATCH 12/12] crypto: atmel - add SHA256 ahash support Lothar Rubusch
2026-05-14 19:51 ` [PATCH 00/12] crypto: atmel - refactor common i2c support and " Thorsten Blum
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260512224349.64621-6-l.rubusch@gmail.com \
--to=l.rubusch@gmail.com \
--cc=alexandre.belloni@bootlin.com \
--cc=claudiu.beznea@tuxon.dev \
--cc=davem@davemloft.net \
--cc=herbert@gondor.apana.org.au \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nicolas.ferre@microchip.com \
--cc=thorsten.blum@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.