Subject: [PATCH 12/12] crypto: atmel - add SHA256 ahash support

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

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 <[email-masked]>
---
 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