Linux cryptographic layer development
 help / color / mirror / Atom feed
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 12/12] crypto: atmel - add SHA256 ahash support
Date: Tue, 12 May 2026 22:43:49 +0000	[thread overview]
Message-ID: <20260512224349.64621-13-l.rubusch@gmail.com> (raw)
In-Reply-To: <20260512224349.64621-1-l.rubusch@gmail.com>

Add SHA256 ahash support for ATSHA204A and ECC based devices using
the hardware SHA engine provided by the Atmel secure element devices.

Implement common SHA256 request handling in the atmel-i2c core driver,
including init, update, final, finup, digest, export and import
operations. Scatterlist input is processed using the crypto hash
walker.

ATSHA204A devices require software-side SHA256 padding according to
FIPS 180-4 before submitting the final data blocks to the device.
Newer ECC devices instead support a dedicated SHA final command which
performs padding internally in hardware. For these devices, the final
block length is passed through the command parameter field.

The SHA engine requires a strict multi-command transaction sequence:
SHA INIT, followed by one or more SHA COMPUTE operations and, on ECC
devices, a terminating SHA FINAL operation. The device SHA context is
lost if the device enters sleep mode or if unrelated commands are
interleaved during the sequence.

To support these hardware requirements, split the existing
send/receive helper into a low-level transfer helper and a higher
level wrapper handling wakeup, sleep and locking. SHA operations keep
the device awake and hold the i2c client lock across the complete hash
transaction until the final digest has been retrieved.

Register the SHA256 ahash algorithm in both atmel-sha204a and
atmel-ecc drivers and add capability based client allocation for SHA
operations.

Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
 drivers/crypto/atmel-ecc.c     |  50 +++++-
 drivers/crypto/atmel-i2c.c     | 273 +++++++++++++++++++++++++++++++--
 drivers/crypto/atmel-i2c.h     |  40 +++++
 drivers/crypto/atmel-sha204a.c |  55 ++++++-
 4 files changed, 407 insertions(+), 11 deletions(-)

diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index ed8c0ce5562b..aacf9e8add7a 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -19,10 +19,50 @@
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 #include <crypto/internal/kpp.h>
+#include <crypto/internal/hash.h>
 #include <crypto/ecdh.h>
 #include <crypto/kpp.h>
+#include <crypto/sha2.h>
 #include "atmel-i2c.h"
 
+static int atmel_ecc_sha_init_tfm(struct crypto_tfm *tfm)
+{
+	struct atmel_i2c_sha_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	ctx->client = atmel_i2c_client_alloc(ATMEL_CAP_SHA);
+	if (IS_ERR(ctx->client)) {
+		pr_err("tfm - i2c_client binding failed\n");
+		return PTR_ERR(ctx->client);
+	}
+
+	return 0;
+}
+
+static struct ahash_alg atmel_ecc_sha = {
+	.init = atmel_i2c_sha_init,
+	.update	= atmel_i2c_sha_update,
+	.final = atmel_i2c_sha_final,
+	.finup = atmel_i2c_sha_finup,
+	.digest	= atmel_i2c_sha_digest,
+	.export = atmel_i2c_sha_export,
+	.import = atmel_i2c_sha_import,
+	.halg = {
+		.digestsize = SHA256_DIGEST_SIZE,
+		.statesize = sizeof(struct atmel_i2c_sha_reqctx),
+		.base = {
+			.cra_name		= "sha256",
+			.cra_driver_name	= "atmel-sha256",
+			.cra_init		= atmel_ecc_sha_init_tfm,
+			.cra_priority		= ATMEL_I2C_PRIORITY,
+			.cra_flags		= CRYPTO_ALG_TYPE_AHASH,
+			.cra_blocksize		= SHA256_BLOCK_SIZE,
+			.cra_ctxsize		= sizeof(struct atmel_i2c_sha_ctx),
+			.cra_reqsize		= sizeof(struct atmel_i2c_sha_reqctx),
+			.cra_module		= THIS_MODULE,
+		}
+	}
+};
+
 static ssize_t config_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	return atmel_i2c_eeprom_display(dev, attr, buf, ATMEL_EEPROM_CONFIG_ZONE);
@@ -321,7 +361,7 @@ static int atmel_ecc_probe(struct i2c_client *client)
 
 	i2c_priv = i2c_get_clientdata(client);
 	i2c_priv->data = data;
-	i2c_priv->caps = BIT(ATMEL_CAP_ECDH);
+	i2c_priv->caps = BIT(ATMEL_CAP_ECDH) | BIT(ATMEL_CAP_SHA);
 
 	ret = atmel_i2c_device_sanity_check(client);
 	if (ret) {
@@ -364,6 +404,12 @@ static int atmel_ecc_probe(struct i2c_client *client)
 		dev_info(&client->dev, "atmel ecc algorithms registered in /proc/crypto\n");
 	}
 
+	ret = crypto_register_ahash(&atmel_ecc_sha);
+	if (ret) {
+		dev_err(&client->dev, "SHA256 registration failed\n");
+		goto err_list_del;
+	}
+
 	goto done;
 
 err_list_del:
@@ -392,6 +438,7 @@ static void atmel_ecc_remove(struct i2c_client *client)
 	atmel_i2c_flush_queue();
 
 	crypto_unregister_kpp(&atmel_ecdh_nist_p256);
+	crypto_unregister_ahash(&atmel_ecc_sha);
 
 	if (i2c_priv->hwrng.priv) {
 		kfree((void *)i2c_priv->hwrng.priv);
@@ -405,6 +452,7 @@ static const struct atmel_i2c_of_match_data atecc508a_match_data = {
 		.max_exec_time_genkey = 115,
 		.max_exec_time_random = 23,
 		.max_exec_time_read = 1,
+		.max_exec_time_sha = 9,
 		.max_exec_time_write = 42,
 	},
 	.eeprom_zone_size = {
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index 53aba2f4bedb..cbdc8c0e5aca 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -19,6 +19,10 @@
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
+#include <crypto/hash.h>
+#include <crypto/sha2.h>
+#include <crypto/internal/hash.h>
+
 #include "atmel-i2c.h"
 
 #define ATMEL_I2C_COMMAND		0x03 /* packet function */
@@ -49,12 +53,17 @@
 #define ATMEL_I2C_ECDH_RSP_SIZE		(32 + ATMEL_I2C_RSP_OVERHEAD_SIZE)
 #define ATMEL_I2C_ECDH_PREFIX_MODE	0x00
 
+/* Definitions for the SHA Command */
+#define ATMEL_I2C_SHA_RSP_SIZE		(ATMEL_I2C_RSP_OVERHEAD_SIZE + \
+					SHA256_DIGEST_SIZE)
+
 /* Command opcode */
 #define ATMEL_I2C_OPCODE_ECDH		0x43
 #define ATMEL_I2C_OPCODE_GENKEY		0x40
 #define ATMEL_I2C_OPCODE_READ		0x02
 #define ATMEL_I2C_OPCODE_RANDOM		0x1b
 #define ATMEL_I2C_OPCODE_WRITE		0x12
+#define ATMEL_I2C_OPCODE_SHA		0x47
 
 /*
  * Wake High delay to data communication (microseconds). SDA should be stable
@@ -244,6 +253,43 @@ int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
 }
 EXPORT_SYMBOL(atmel_i2c_init_ecdh_cmd);
 
+int atmel_i2c_init_sha_cmd(struct atmel_i2c_cmd *cmd,
+			   u8 *challenge, size_t len,
+			   enum atmel_i2c_sha_engine_cmd sha_engine_cmd,
+			   const struct atmel_i2c_max_exec_timings *timings)
+{
+	cmd->word_addr = ATMEL_I2C_COMMAND;
+	cmd->opcode = ATMEL_I2C_OPCODE_SHA;
+	cmd->param1 = sha_engine_cmd;
+
+	cmd->param2 = cpu_to_le16(0);
+	/*
+	 * Starting with the bigger ECCs, the device learned how to do SHA256
+	 * padding (FIPS 180-4). Since SHA UPDATE always consumes 64B (SHA256
+	 * block size), the only length needed to communicate is the number of
+	 * used bytes in the final block. For the Atmel ECC series, this is
+	 * passed in the param2.
+	 */
+	if (sha_engine_cmd == atmel_sha_ecc_end)
+		cmd->param2 = cpu_to_le16(len);
+
+	cmd->count = ATMEL_I2C_COUNT_OVERHEAD_SIZE;
+	if (sha_engine_cmd == atmel_sha_init) {
+		memset(cmd->data, 0, sizeof(cmd->data));
+	} else {
+		memcpy(cmd->data, challenge, len);
+		cmd->count += len;
+	}
+
+	atmel_i2c_checksum(cmd);
+
+	cmd->msecs = timings->max_exec_time_sha;
+	cmd->rxsize = atmel_i2c_sha_rsp_size[sha_engine_cmd];
+
+	return 0;
+}
+EXPORT_SYMBOL(atmel_i2c_init_sha_cmd);
+
 static void atmel_i2c_rng_done(struct atmel_i2c_work_data *work_data,
 			       void *areq, int status)
 {
@@ -492,21 +538,15 @@ static int atmel_i2c_sleep(struct i2c_client *client)
  * counter other than to put the device into sleep or idle mode and then
  * wake it up again.
  */
-int atmel_i2c_send_receive(struct i2c_client *client, struct atmel_i2c_cmd *cmd)
+static int _atmel_i2c_send_receive(struct i2c_client *client,
+				   struct atmel_i2c_cmd *cmd)
 {
-	struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
 	int ret;
 
-	mutex_lock(&i2c_priv->lock);
-
-	ret = atmel_i2c_wakeup(client);
-	if (ret)
-		goto err;
-
 	/* send the command */
 	ret = i2c_master_send(client, (u8 *)cmd, cmd->count + ATMEL_I2C_ADDR_SIZE);
 	if (ret < 0)
-		goto err;
+		return ret;
 
 	/* delay the appropriate amount of time for command to execute */
 	msleep(cmd->msecs);
@@ -514,6 +554,24 @@ int atmel_i2c_send_receive(struct i2c_client *client, struct atmel_i2c_cmd *cmd)
 	/* receive the response */
 	ret = i2c_master_recv(client, cmd->data, cmd->rxsize);
 	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int atmel_i2c_send_receive(struct i2c_client *client, struct atmel_i2c_cmd *cmd)
+{
+	struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
+	int ret;
+
+	mutex_lock(&i2c_priv->lock);
+
+	ret = atmel_i2c_wakeup(client);
+	if (ret)
+		goto err;
+
+	ret = _atmel_i2c_send_receive(client, cmd);
+	if (ret)
 		goto err;
 
 	/* put the device into low-power mode */
@@ -529,6 +587,203 @@ int atmel_i2c_send_receive(struct i2c_client *client, struct atmel_i2c_cmd *cmd)
 }
 EXPORT_SYMBOL(atmel_i2c_send_receive);
 
+int atmel_i2c_sha_init(struct ahash_request *req)
+{
+	struct atmel_i2c_sha_reqctx *rctx = ahash_request_ctx(req);
+	struct atmel_i2c_sha_ctx *ctx = crypto_tfm_ctx(req->base.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;
+	int ret;
+
+	rctx->bufcnt = 0;
+	rctx->total = 0;
+	rctx->ctx = i2c_get_clientdata(ctx->client);
+
+	cmd = kmalloc_obj(*cmd);
+	if (!cmd)
+		return -ENOMEM;
+
+	/* SHA init */
+	ret = atmel_i2c_init_sha_cmd(cmd, NULL, 0, atmel_sha_init, &data->timings);
+	if (ret)
+		goto err_free;
+
+	mutex_lock(&i2c_priv->lock);
+
+	ret = atmel_i2c_wakeup(ctx->client);
+	if (ret)
+		goto err;
+
+	ret = _atmel_i2c_send_receive(ctx->client, cmd);
+	if (ret)
+		goto err;
+
+	/* we keep the lock hold until error out or _sha_final() is called */
+	return 0;
+err:
+	mutex_unlock(&i2c_priv->lock);
+err_free:
+	kfree_sensitive(cmd);
+	return ret;
+}
+EXPORT_SYMBOL(atmel_i2c_sha_init);
+
+int atmel_i2c_sha_update(struct ahash_request *req)
+{
+	struct atmel_i2c_sha_reqctx *rctx = ahash_request_ctx(req);
+	struct atmel_i2c_sha_ctx *ctx = crypto_tfm_ctx(req->base.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;
+	struct crypto_hash_walk walk;
+	int nbytes, take, copied = 0;
+	const u8 *pdata;
+	int ret;
+
+	rctx->total += req->nbytes;
+
+	cmd = kmalloc_obj(*cmd);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto err_nomem;
+	}
+
+	/*
+	 * Note, we are actively holding the i2c_priv->lock while the SHA engine
+	 * operates. This covers init, update and final steps.
+	 */
+	nbytes = crypto_hash_walk_first(req, &walk);
+	for (; nbytes > 0; nbytes = crypto_hash_walk_done(&walk, copied)) {
+		copied = nbytes;
+		pdata = walk.data;
+		while (copied > 0) {
+			take = min(copied, SHA256_BLOCK_SIZE - rctx->bufcnt);
+
+			memcpy(rctx->buffer + rctx->bufcnt, pdata, take);
+			pdata += take;
+			copied -= take;
+			rctx->bufcnt += take;
+			if (rctx->bufcnt == SHA256_BLOCK_SIZE) {
+				ret = atmel_i2c_init_sha_cmd(cmd, rctx->buffer,
+							     SHA256_BLOCK_SIZE,
+							     atmel_sha_compute,
+							     &data->timings);
+				if (ret)
+					goto err;
+
+				ret = _atmel_i2c_send_receive(ctx->client, cmd);
+				if (ret)
+					goto err;
+
+				rctx->bufcnt = 0;
+			}
+		}
+	}
+
+	kfree_sensitive(cmd);
+	return 0;
+err:
+	kfree_sensitive(cmd);
+err_nomem:
+	mutex_unlock(&i2c_priv->lock);
+	return ret;
+}
+EXPORT_SYMBOL(atmel_i2c_sha_update);
+
+int atmel_i2c_sha_final(struct ahash_request *req)
+{
+	struct atmel_i2c_sha_reqctx *rctx = ahash_request_ctx(req);
+	struct atmel_i2c_sha_ctx *ctx = crypto_tfm_ctx(req->base.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;
+	u8 final_blocks[2 * SHA256_BLOCK_SIZE];
+	u32 total_pad;
+	__be64 bits;
+	int i, ret = 0;
+
+	cmd = kmalloc_obj(*cmd);
+	if (!cmd) {
+		ret = -ENOMEM;
+		goto err_nomem;
+	}
+
+	if (data->needs_sha_padding) {
+		/*
+		 * Determine if padding fits in current block or needs another,
+		 * SHA256 needs 8 bytes for length at the end of a 64-byte block.
+		 */
+		memset(final_blocks, 0, sizeof(final_blocks));
+		memcpy(final_blocks, rctx->buffer, rctx->bufcnt);
+		final_blocks[rctx->bufcnt] = 0x80; /* pad bit */
+		total_pad = SHA256_BLOCK_SIZE * (rctx->bufcnt < 56 ? 1 : 2);
+		bits = cpu_to_be64((u64)rctx->total << 3); /* needs num of bits */
+		memcpy(final_blocks + total_pad - 8, &bits, 8);
+		for (i = 0; i < total_pad; i += SHA256_BLOCK_SIZE) {
+			ret = atmel_i2c_init_sha_cmd(cmd, final_blocks + i,
+						     SHA256_BLOCK_SIZE,
+						     atmel_sha_compute, &data->timings);
+			if (ret)
+				goto err_or_done;
+
+			ret = _atmel_i2c_send_receive(ctx->client, cmd);
+			if (ret)
+				goto err_or_done;
+		}
+	} else {
+		ret = atmel_i2c_init_sha_cmd(cmd, rctx->buffer, rctx->bufcnt,
+					     atmel_sha_ecc_end, &data->timings);
+		if (ret)
+			goto err_or_done;
+
+		ret = _atmel_i2c_send_receive(ctx->client, cmd);
+		if (ret)
+			goto err_or_done;
+	}
+
+	memcpy(req->result, &cmd->data[ATMEL_I2C_RSP_DATA_IDX],
+	       SHA256_DIGEST_SIZE);
+
+	/* Sleep returns a positive int on success, API requires 0 on success */
+	ret = atmel_i2c_sleep(ctx->client);
+	if (ret < 0)
+		goto err_or_done;
+	ret = 0;
+err_or_done:
+	kfree_sensitive(cmd);
+err_nomem:
+	mutex_unlock(&i2c_priv->lock);
+	return ret;
+}
+EXPORT_SYMBOL(atmel_i2c_sha_final);
+
+int atmel_i2c_sha_finup(struct ahash_request *req)
+{
+	return atmel_i2c_sha_update(req) ? : atmel_i2c_sha_final(req);
+}
+EXPORT_SYMBOL(atmel_i2c_sha_finup);
+
+int atmel_i2c_sha_digest(struct ahash_request *req)
+{
+	return atmel_i2c_sha_init(req) ? : atmel_i2c_sha_finup(req);
+}
+EXPORT_SYMBOL(atmel_i2c_sha_digest);
+
+int atmel_i2c_sha_export(struct ahash_request *req, void *out)
+{
+	memcpy(out, ahash_request_ctx(req), sizeof(struct atmel_i2c_sha_reqctx));
+	return 0;
+}
+EXPORT_SYMBOL(atmel_i2c_sha_export);
+
+int atmel_i2c_sha_import(struct ahash_request *req, const void *in)
+{
+	memcpy(ahash_request_ctx(req), in, sizeof(struct atmel_i2c_sha_reqctx));
+	return 0;
+}
+EXPORT_SYMBOL(atmel_i2c_sha_import);
+
 static void atmel_i2c_work_handler(struct work_struct *work)
 {
 	struct atmel_i2c_work_data *work_data =
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 20afe2da4f8d..e0021d4ea686 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -7,8 +7,11 @@
 #ifndef __ATMEL_I2C_H__
 #define __ATMEL_I2C_H__
 
+#include <linux/device.h>
+#include <crypto/internal/hash.h>
 #include <linux/hw_random.h>
 #include <linux/types.h>
+#include <crypto/sha2.h>
 
 #define ATMEL_I2C_PRIORITY		300
 
@@ -79,11 +82,13 @@ struct atmel_i2c_max_exec_timings {
 	unsigned int max_exec_time_ecdh;
 	unsigned int max_exec_time_random;
 	unsigned int max_exec_time_read;
+	unsigned int max_exec_time_sha;
 	unsigned int max_exec_time_write;
 };
 
 struct atmel_i2c_of_match_data {
 	const unsigned short needs_legacy_hwrng;
+	const unsigned short needs_sha_padding;
 	struct atmel_i2c_max_exec_timings timings;
 	size_t eeprom_zone_size[3]; /* all atmel devices have three zones */
 };
@@ -91,6 +96,30 @@ struct atmel_i2c_of_match_data {
 /* Used for binding tfm objects to i2c clients. */
 enum atmel_i2c_capability {
 	ATMEL_CAP_ECDH = 0,
+	ATMEL_CAP_SHA,
+};
+
+enum atmel_i2c_sha_engine_cmd {
+	atmel_sha_init = 0,
+	atmel_sha_compute,
+	atmel_sha_ecc_end,
+};
+
+size_t atmel_i2c_sha_rsp_size[] = {
+	[atmel_sha_init] = ATMEL_I2C_STATUS_RSP_SIZE,
+	[atmel_sha_compute] = SHA256_DIGEST_SIZE + ATMEL_I2C_RSP_OVERHEAD_SIZE,
+	[atmel_sha_ecc_end] = SHA256_DIGEST_SIZE + ATMEL_I2C_RSP_OVERHEAD_SIZE,
+};
+
+struct atmel_i2c_sha_ctx {
+	struct i2c_client *client;
+};
+
+struct atmel_i2c_sha_reqctx {
+	u8 buffer[SHA256_BLOCK_SIZE];
+	size_t bufcnt;
+	size_t total; /* size of full input, needed for padding */
+	struct atmel_i2c_client_priv *ctx;
 };
 
 struct atmel_i2c_client_mgmt {
@@ -172,9 +201,20 @@ 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_init_sha_cmd(struct atmel_i2c_cmd *cmd, u8 *challenge, size_t len,
+			   enum atmel_i2c_sha_engine_cmd sha_engine_cmd,
+			   const struct atmel_i2c_max_exec_timings *timings);
 int atmel_i2c_register_rng(struct atmel_i2c_client_priv *i2c_priv,
 			   struct device *dev);
 
+int atmel_i2c_sha_init(struct ahash_request *req);
+int atmel_i2c_sha_update(struct ahash_request *req);
+int atmel_i2c_sha_final(struct ahash_request *req);
+int atmel_i2c_sha_finup(struct ahash_request *req);
+int atmel_i2c_sha_digest(struct ahash_request *req);
+int atmel_i2c_sha_export(struct ahash_request *req, void *out);
+int atmel_i2c_sha_import(struct ahash_request *req, const void *in);
+
 int atmel_i2c_device_sanity_check(struct i2c_client *client);
 
 ssize_t atmel_i2c_eeprom_display(struct device *dev,
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index 6a41024ae40d..74535480edeb 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -17,8 +17,48 @@
 #include <linux/slab.h>
 #include <linux/sysfs.h>
 #include <linux/workqueue.h>
+#include <crypto/sha2.h>
+
 #include "atmel-i2c.h"
 
+static int atmel_sha204a_sha_init_tfm(struct crypto_tfm *tfm)
+{
+	struct atmel_i2c_sha_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	ctx->client = atmel_i2c_client_alloc(ATMEL_CAP_SHA);
+	if (IS_ERR(ctx->client)) {
+		pr_err("tfm - i2c_client binding failed\n");
+		return PTR_ERR(ctx->client);
+	}
+
+	return 0;
+}
+
+static struct ahash_alg atmel_sha204a_sha = {
+	.init = atmel_i2c_sha_init,
+	.update	= atmel_i2c_sha_update,
+	.final = atmel_i2c_sha_final,
+	.finup = atmel_i2c_sha_finup,
+	.digest	= atmel_i2c_sha_digest,
+	.export = atmel_i2c_sha_export,
+	.import = atmel_i2c_sha_import,
+	.halg = {
+		.digestsize = SHA256_DIGEST_SIZE,
+		.statesize = sizeof(struct atmel_i2c_sha_reqctx),
+		.base = {
+			.cra_name		= "sha256",
+			.cra_driver_name	= "atmel-sha256",
+			.cra_init		= atmel_sha204a_sha_init_tfm,
+			.cra_priority		= ATMEL_I2C_PRIORITY,
+			.cra_flags		= CRYPTO_ALG_TYPE_AHASH,
+			.cra_blocksize		= SHA256_BLOCK_SIZE,
+			.cra_ctxsize		= sizeof(struct atmel_i2c_sha_ctx),
+			.cra_reqsize		= sizeof(struct atmel_i2c_sha_reqctx),
+			.cra_module		= THIS_MODULE,
+		}
+	}
+};
+
 static ssize_t config_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	return atmel_i2c_eeprom_display(dev, attr, buf, ATMEL_EEPROM_CONFIG_ZONE);
@@ -62,7 +102,7 @@ static int atmel_sha204a_probe(struct i2c_client *client)
 
 	i2c_priv = i2c_get_clientdata(client);
 	i2c_priv->data = data;
-	i2c_priv->caps = 0;
+	i2c_priv->caps = BIT(ATMEL_CAP_SHA);
 
 	ret = atmel_i2c_device_sanity_check(client);
 	if (ret) {
@@ -95,6 +135,13 @@ static int atmel_sha204a_probe(struct i2c_client *client)
 		goto err_list_del;
 	}
 
+	/* register algorithms */
+	ret = crypto_register_ahash(&atmel_sha204a_sha);
+	if (ret) {
+		dev_err(&client->dev, "SHA256 registration failed\n");
+		goto err_list_del;
+	}
+
 	goto done;
 
 err_list_del:
@@ -119,6 +166,8 @@ static void atmel_sha204a_remove(struct i2c_client *client)
 	devm_hwrng_unregister(&client->dev, &i2c_priv->hwrng);
 	atmel_i2c_flush_queue();
 
+	crypto_unregister_ahash(&atmel_sha204a_sha);
+
 	if (i2c_priv->hwrng.priv) {
 		kfree((void *)i2c_priv->hwrng.priv);
 		i2c_priv->hwrng.priv = 0;
@@ -130,6 +179,7 @@ static const struct atmel_i2c_of_match_data atsha204_match_data = {
 		.max_exec_time_genkey = 43,
 		.max_exec_time_random = 50,
 		.max_exec_time_read = 4,
+		.max_exec_time_sha = 22,
 		.max_exec_time_write = 42,
 	},
 	.eeprom_zone_size = {
@@ -142,6 +192,7 @@ static const struct atmel_i2c_of_match_data atsha204_match_data = {
 	 * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html
 	 */
 	.needs_legacy_hwrng = 1,
+	.needs_sha_padding = 1,
 };
 
 static const struct atmel_i2c_of_match_data atsha204a_match_data = {
@@ -149,6 +200,7 @@ static const struct atmel_i2c_of_match_data atsha204a_match_data = {
 		.max_exec_time_genkey = 43,
 		.max_exec_time_random = 50,
 		.max_exec_time_read = 4,
+		.max_exec_time_sha = 22,
 		.max_exec_time_write = 42,
 	},
 	.eeprom_zone_size = {
@@ -156,6 +208,7 @@ static const struct atmel_i2c_of_match_data atsha204a_match_data = {
 		[ATMEL_EEPROM_OTP_ZONE] = 64,
 		[ATMEL_EEPROM_DATA_ZONE] = 512
 	},
+	.needs_sha_padding = 1,
 };
 
 static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = {
-- 
2.53.0


      parent reply	other threads:[~2026-05-12 22:44 UTC|newest]

Thread overview: 13+ 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 ` [PATCH 05/12] crypto: atmel - move RNG support into common i2c core Lothar Rubusch
2026-05-12 22:43 ` [PATCH 06/12] crypto: atmel - move EEPROM access " 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 ` Lothar Rubusch [this message]

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-13-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox