Subject: [PATCH 04/12] crypto: atmel - add per-device timing and match-data driven configuration

From: Lothar Rubusch <[email-masked]>
Date: Tue, 12 May 2026 22:43:41 +0000

The ATSHA204(A) and ATECC device families define different maximum
command execution times in their datasheets. The current driver uses a
mixed set of timing constants, which can result in insufficient wait
times for some devices.

Introduce struct atmel_i2c_of_match_data to provide per-device timing
information through the device match tables. Store the match data in the
client private structure and pass the timing parameters to the command
initialization helpers instead of relying on global timing constants.

This allows the common atmel-i2c core to use device-specific command
timeouts for operations such as READ, RANDOM, GENKEY, and ECDH.

Also move the legacy hwrng quality information into the match data
structure to consolidate per-device configuration in a single place.

Signed-off-by: Lothar Rubusch <[email-masked]>
---
 drivers/crypto/atmel-ecc.c     | 32 +++++++++++++---
 drivers/crypto/atmel-i2c.c     | 29 ++++++++------
 drivers/crypto/atmel-i2c.h     | 36 ++++++++++++------
 drivers/crypto/atmel-sha204a.c | 69 ++++++++++++++++++++++++----------
 4 files changed, 120 insertions(+), 46 deletions(-)

diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 0dede3707b73..7793f7b4e97e 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -76,6 +76,8 @@ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
 				 unsigned int len)
 {
 	struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);
+	struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(ctx->client);
+	const struct atmel_i2c_of_match_data *data = i2c_priv->data;
 	struct atmel_i2c_cmd *cmd;
 	void *public_key;
 	struct ecdh params;
@@ -112,7 +114,7 @@ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
 
 	ctx->do_fallback = false;
 
-	atmel_i2c_init_genkey_cmd(cmd, DATA_SLOT_2);
+	atmel_i2c_init_genkey_cmd(cmd, DATA_SLOT_2, &data->timings);
 
 	ret = atmel_i2c_send_receive(ctx->client, cmd);
 	if (ret)
@@ -164,6 +166,8 @@ static int atmel_ecdh_compute_shared_secret(struct kpp_request *req)
 {
 	struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
 	struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);
+	struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(ctx->client);
+	const struct atmel_i2c_of_match_data *data = i2c_priv->data;
 	struct atmel_i2c_work_data *work_data;
 	gfp_t gfp;
 	int ret;
@@ -187,7 +191,7 @@ static int atmel_ecdh_compute_shared_secret(struct kpp_request *req)
 	work_data->ctx = ctx;
 	work_data->client = ctx->client;
 
-	ret = atmel_i2c_init_ecdh_cmd(&work_data->cmd, req->src);
+	ret = atmel_i2c_init_ecdh_cmd(&work_data->cmd, req->src, &data->timings);
 	if (ret)
 		goto free_work_data;
 
@@ -278,14 +282,22 @@ static struct kpp_alg atmel_ecdh_nist_p256 = {
 static int atmel_ecc_probe(struct i2c_client *client)
 {
 	struct atmel_i2c_client_priv *i2c_priv;
+	const struct atmel_i2c_of_match_data *data;
 	int ret;
 
 	ret = atmel_i2c_probe(client);
 	if (ret)
 		goto done;
 
-	i2c_priv = i2c_get_clientdata(client);
+	data = device_get_match_data(&client->dev);
+	if (!data) {
+		dev_err(&client->dev, "no match data found via OF or ID table\n");
+		ret = -ENODEV;
+		goto done;
+	}
 
+	i2c_priv = i2c_get_clientdata(client);
+	i2c_priv->data = data;
 	i2c_priv->caps = BIT(ATMEL_CAP_ECDH);
 
 	/* add to client list */
@@ -339,9 +351,19 @@ static void atmel_ecc_remove(struct i2c_client *client)
 	crypto_unregister_kpp(&atmel_ecdh_nist_p256);
 }
 
+static const struct atmel_i2c_of_match_data atecc508a_match_data = {
+	.timings = {
+		.max_exec_time_ecdh = 58,
+		.max_exec_time_genkey = 115,
+		.max_exec_time_random = 23,
+		.max_exec_time_read = 1,
+		.max_exec_time_write = 42,
+	},
+};
+
 static const struct of_device_id atmel_ecc_dt_ids[] = {
-	{ .compatible = "atmel,atecc508a", },
-	{ .compatible = "atmel,atecc608b", },
+	{ .compatible = "atmel,atecc508a", .data = &atecc508a_match_data, },
+	{ .compatible = "atmel,atecc608b", .data = &atecc508a_match_data, },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, atmel_ecc_dt_ids);
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index b7ee2ec37531..7fa7cf9ab3c1 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -96,7 +96,8 @@ struct i2c_client *atmel_i2c_client_alloc(enum atmel_i2c_capability cap)
 }
 EXPORT_SYMBOL(atmel_i2c_client_alloc);
 
-void atmel_i2c_init_read_config_cmd(struct atmel_i2c_cmd *cmd)
+void atmel_i2c_init_read_config_cmd(struct atmel_i2c_cmd *cmd,
+				    const struct atmel_i2c_max_exec_timings *timings)
 {
 	cmd->word_addr = COMMAND;
 	cmd->opcode = OPCODE_READ;
@@ -110,12 +111,13 @@ void atmel_i2c_init_read_config_cmd(struct atmel_i2c_cmd *cmd)
 
 	atmel_i2c_checksum(cmd);
 
-	cmd->msecs = MAX_EXEC_TIME_READ;
+	cmd->msecs = timings->max_exec_time_read;
 	cmd->rxsize = READ_RSP_SIZE;
 }
 EXPORT_SYMBOL(atmel_i2c_init_read_config_cmd);
 
-int atmel_i2c_init_read_otp_cmd(struct atmel_i2c_cmd *cmd, u16 addr)
+int atmel_i2c_init_read_otp_cmd(struct atmel_i2c_cmd *cmd, u16 addr,
+				const struct atmel_i2c_max_exec_timings *timings)
 {
 	if (addr >= OTP_ZONE_SIZE / 4)
 		return -EINVAL;
@@ -132,14 +134,15 @@ int atmel_i2c_init_read_otp_cmd(struct atmel_i2c_cmd *cmd, u16 addr)
 
 	atmel_i2c_checksum(cmd);
 
-	cmd->msecs = MAX_EXEC_TIME_READ;
+	cmd->msecs = timings->max_exec_time_read;
 	cmd->rxsize = READ_RSP_SIZE;
 
 	return 0;
 }
 EXPORT_SYMBOL(atmel_i2c_init_read_otp_cmd);
 
-void atmel_i2c_init_random_cmd(struct atmel_i2c_cmd *cmd)
+void atmel_i2c_init_random_cmd(struct atmel_i2c_cmd *cmd,
+			       const struct atmel_i2c_max_exec_timings *timings)
 {
 	cmd->word_addr = COMMAND;
 	cmd->opcode = OPCODE_RANDOM;
@@ -149,12 +152,13 @@ void atmel_i2c_init_random_cmd(struct atmel_i2c_cmd *cmd)
 
 	atmel_i2c_checksum(cmd);
 
-	cmd->msecs = MAX_EXEC_TIME_RANDOM;
+	cmd->msecs = timings->max_exec_time_random;
 	cmd->rxsize = RANDOM_RSP_SIZE;
 }
 EXPORT_SYMBOL(atmel_i2c_init_random_cmd);
 
-void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid)
+void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid,
+			       const struct atmel_i2c_max_exec_timings *timings)
 {
 	cmd->word_addr = COMMAND;
 	cmd->count = GENKEY_COUNT;
@@ -165,13 +169,14 @@ void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid)
 
 	atmel_i2c_checksum(cmd);
 
-	cmd->msecs = MAX_EXEC_TIME_GENKEY;
+	cmd->msecs = timings->max_exec_time_genkey;
 	cmd->rxsize = GENKEY_RSP_SIZE;
 }
 EXPORT_SYMBOL(atmel_i2c_init_genkey_cmd);
 
 int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
-			    struct scatterlist *pubkey)
+			    struct scatterlist *pubkey,
+			    const struct atmel_i2c_max_exec_timings *timings)
 {
 	size_t copied;
 
@@ -196,7 +201,7 @@ int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
 
 	atmel_i2c_checksum(cmd);
 
-	cmd->msecs = MAX_EXEC_TIME_ECDH;
+	cmd->msecs = timings->max_exec_time_ecdh;
 	cmd->rxsize = ECDH_RSP_SIZE;
 
 	return 0;
@@ -363,6 +368,8 @@ static inline size_t atmel_i2c_wake_token_sz(u32 bus_clk_rate)
 
 static int device_sanity_check(struct i2c_client *client)
 {
+	struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
+	const struct atmel_i2c_of_match_data *data = i2c_priv->data;
 	struct atmel_i2c_cmd *cmd;
 	int ret;
 
@@ -370,7 +377,7 @@ static int device_sanity_check(struct i2c_client *client)
 	if (!cmd)
 		return -ENOMEM;
 
-	atmel_i2c_init_read_config_cmd(cmd);
+	atmel_i2c_init_read_config_cmd(cmd, &data->timings);
 
 	ret = atmel_i2c_send_receive(client, cmd);
 	if (ret)
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 70579b438256..5224a62c16c9 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -57,6 +57,19 @@ struct atmel_i2c_cmd {
 	u16 rxsize;
 } __packed;
 
+struct atmel_i2c_max_exec_timings {
+	unsigned int max_exec_time_genkey;
+	unsigned int max_exec_time_ecdh;
+	unsigned int max_exec_time_random;
+	unsigned int max_exec_time_read;
+	unsigned int max_exec_time_write;
+};
+
+struct atmel_i2c_of_match_data {
+	const unsigned short *legacy_hwrng;
+	struct atmel_i2c_max_exec_timings timings;
+};
+
 /* Status/Error codes */
 #define STATUS_SIZE			0x04
 #define STATUS_NOERR			0x00
@@ -88,12 +101,6 @@ struct atmel_i2c_cmd {
 /* Wake Low duration */
 #define TWLO_USEC			60
 
-/* Command execution time (milliseconds) */
-#define MAX_EXEC_TIME_ECDH		58
-#define MAX_EXEC_TIME_GENKEY		115
-#define MAX_EXEC_TIME_READ		1
-#define MAX_EXEC_TIME_RANDOM		50
-
 /* Command opcode */
 #define OPCODE_ECDH			0x43
 #define OPCODE_GENKEY			0x40
@@ -135,6 +142,7 @@ extern struct atmel_i2c_client_mgmt atmel_i2c_mgmt;
  * @tfm_count           : number of active crypto transformations on i2c client
  * @hwrng               : hold the hardware generated rng
  * @caps                : feature capability of the particular driver
+ * @data                : preinitialized driver data
  *
  * Reads and writes from/to the i2c client are sequential. The first byte
  * transmitted to the device is treated as the byte size. Any attempt to send
@@ -152,6 +160,7 @@ struct atmel_i2c_client_priv {
 	atomic_t tfm_count ____cacheline_aligned;
 	struct hwrng hwrng;
 	u32 caps;
+	const struct atmel_i2c_of_match_data *data;
 };
 
 /**
@@ -189,12 +198,17 @@ void atmel_i2c_flush_queue(void);
 
 int atmel_i2c_send_receive(struct i2c_client *client, struct atmel_i2c_cmd *cmd);
 
-void atmel_i2c_init_read_config_cmd(struct atmel_i2c_cmd *cmd);
-int atmel_i2c_init_read_otp_cmd(struct atmel_i2c_cmd *cmd, u16 addr);
-void atmel_i2c_init_random_cmd(struct atmel_i2c_cmd *cmd);
-void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid);
+void atmel_i2c_init_read_config_cmd(struct atmel_i2c_cmd *cmd,
+				    const struct atmel_i2c_max_exec_timings *timings);
+int atmel_i2c_init_read_otp_cmd(struct atmel_i2c_cmd *cmd, u16 addr,
+				const struct atmel_i2c_max_exec_timings *timings);
+void atmel_i2c_init_random_cmd(struct atmel_i2c_cmd *cmd,
+			       const struct atmel_i2c_max_exec_timings *timings);
+void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid,
+			       const struct atmel_i2c_max_exec_timings *timings);
 int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
-			    struct scatterlist *pubkey);
+			    struct scatterlist *pubkey,
+			    const struct atmel_i2c_max_exec_timings *timings);
 
 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 ab758c9cd410..febf9891b167 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -40,14 +40,15 @@ static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data,
 	atomic_dec(&i2c_priv->tfm_count);
 }
 
-static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data,
+static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *buf,
 					      size_t max)
 {
-	struct atmel_i2c_client_priv *i2c_priv;
+	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;
 
-	i2c_priv = container_of(rng, struct atmel_i2c_client_priv, hwrng);
-
 	/* keep maximum 1 asynchronous read in flight at any time */
 	if (!atomic_add_unless(&i2c_priv->tfm_count, 1, 1))
 		return 0;
@@ -55,7 +56,7 @@ static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data,
 	if (rng->priv) {
 		work_data = (struct atmel_i2c_work_data *)rng->priv;
 		max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max);
-		memcpy(data, &work_data->cmd.data[RSP_DATA_IDX], max);
+		memcpy(buf, &work_data->cmd.data[RSP_DATA_IDX], max);
 		rng->priv = 0;
 	} else {
 		work_data = kmalloc_obj(*work_data, GFP_ATOMIC);
@@ -69,42 +70,45 @@ static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data,
 		max = 0;
 	}
 
-	atmel_i2c_init_random_cmd(&work_data->cmd);
+	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 *data, size_t max,
+static int atmel_sha204a_rng_read(struct hwrng *rng, void *buf, size_t max,
 				  bool wait)
 {
-	struct atmel_i2c_client_priv *i2c_priv;
+	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, data, max);
-
-	i2c_priv = container_of(rng, struct atmel_i2c_client_priv, hwrng);
+		return atmel_sha204a_rng_read_nonblocking(rng, buf, max);
 
-	atmel_i2c_init_random_cmd(&cmd);
+	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(data, &cmd.data[RSP_DATA_IDX], 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);
+	const struct atmel_i2c_of_match_data *data = i2c_priv->data;
 	struct atmel_i2c_cmd cmd;
 	int ret;
 
-	ret = atmel_i2c_init_read_otp_cmd(&cmd, addr);
+	ret = atmel_i2c_init_read_otp_cmd(&cmd, addr, &data->timings);
 	if (ret < 0) {
 		dev_err(&client->dev, "failed, invalid otp address %04X\n",
 			addr);
@@ -164,6 +168,7 @@ static const struct attribute_group atmel_sha204a_groups = {
 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;
 
@@ -171,8 +176,15 @@ static int atmel_sha204a_probe(struct i2c_client *client)
 	if (ret)
 		goto done;
 
-	i2c_priv = i2c_get_clientdata(client);
+	data = device_get_match_data(&client->dev);
+	if (!data) {
+		dev_err(&client->dev, "no match data found via OF or ID table\n");
+		ret = -ENODEV;
+		goto done;
+	}
 
+	i2c_priv = i2c_get_clientdata(client);
+	i2c_priv->data = data;
 	i2c_priv->caps = 0;
 
 	/* add to client list */
@@ -187,7 +199,7 @@ static int atmel_sha204a_probe(struct i2c_client *client)
 	i2c_priv->hwrng.name = dev_name(&client->dev);
 	i2c_priv->hwrng.read = atmel_sha204a_rng_read;
 
-	quality = i2c_get_match_data(client);
+	quality = i2c_priv->data->legacy_hwrng;
 	if (quality)
 		i2c_priv->hwrng.quality = *quality;
 
@@ -227,15 +239,34 @@ static void atmel_sha204a_remove(struct i2c_client *client)
 	kfree((void *)i2c_priv->hwrng.priv);
 }
 
+static const struct atmel_i2c_of_match_data atsha204_match_data = {
+	.timings = {
+		.max_exec_time_genkey = 43,
+		.max_exec_time_random = 50,
+		.max_exec_time_read = 4,
+		.max_exec_time_write = 42,
+	},
+	.legacy_hwrng = &atsha204_quality,
+};
+
+static const struct atmel_i2c_of_match_data atsha204a_match_data = {
+	.timings = {
+		.max_exec_time_genkey = 43,
+		.max_exec_time_random = 50,
+		.max_exec_time_read = 4,
+		.max_exec_time_write = 42,
+	},
+};
+
 static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = {
-	{ .compatible = "atmel,atsha204", .data = &atsha204_quality },
-	{ .compatible = "atmel,atsha204a", },
+	{ .compatible = "atmel,atsha204", .data = &atsha204_match_data, },
+	{ .compatible = "atmel,atsha204a", .data = &atsha204a_match_data, },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids);
 
 static const struct i2c_device_id atmel_sha204a_id[] = {
-	{ "atsha204", (kernel_ulong_t)&atsha204_quality },
+	{ "atsha204" },
 	{ "atsha204a" },
 	{ /* sentinel */ }
 };
-- 
2.53.0