* [PATCH 08/12] crypto: atmel - move device sanity check to core driver
From: Lothar Rubusch @ 2026-05-12 22:43 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260512224349.64621-1-l.rubusch@gmail.com>
Move the device sanity check implementation into the shared Atmel
I2C core driver and reuse the generic EEPROM access helpers for
reading the CONFIG zone lock state.
This removes duplicate CONFIG zone handling and consolidates common
response index and lock state definitions under the Atmel I2C core
namespace.
Update both SHA204A and ECC drivers to invoke the shared sanity
check helper during probe.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 10 ++++++--
drivers/crypto/atmel-i2c.c | 43 ++++++++++++----------------------
drivers/crypto/atmel-i2c.h | 14 +++--------
drivers/crypto/atmel-sha204a.c | 6 +++++
4 files changed, 32 insertions(+), 41 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index f08fdf284b60..f6d1a9694d63 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -81,7 +81,7 @@ static void atmel_ecdh_done(struct atmel_i2c_work_data *work_data, void *areq,
/* copy the shared secret */
copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst, n_sz),
- &cmd->data[RSP_DATA_IDX], n_sz);
+ &cmd->data[ATMEL_I2C_RSP_DATA_IDX], n_sz);
if (copied != n_sz)
status = -EINVAL;
@@ -144,7 +144,7 @@ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
goto free_public_key;
/* save the public key */
- memcpy(public_key, &cmd->data[RSP_DATA_IDX], ATMEL_ECC_PUBKEY_SIZE);
+ memcpy(public_key, &cmd->data[ATMEL_I2C_RSP_DATA_IDX], ATMEL_ECC_PUBKEY_SIZE);
ctx->public_key = public_key;
kfree(cmd);
@@ -323,6 +323,12 @@ static int atmel_ecc_probe(struct i2c_client *client)
i2c_priv->data = data;
i2c_priv->caps = BIT(ATMEL_CAP_ECDH);
+ ret = atmel_i2c_device_sanity_check(client);
+ if (ret) {
+ dev_err(&client->dev, "failed to read EEPROM, is hardware attached?\n");
+ goto done;
+ }
+
/* add to client list */
spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
list_add_tail(&i2c_priv->i2c_client_list_node,
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index 26863573a10f..50b6bce478d2 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -23,6 +23,11 @@
#define ATMEL_I2C_COMMAND 0x03 /* packet function */
+/* Definitions for the device lock state */
+#define ATMEL_I2C_DEVICE_LOCK_ADDR 0x15
+#define ATMEL_I2C_LOCK_VALUE_IDX (ATMEL_I2C_RSP_DATA_IDX + 2)
+#define ATMEL_I2C_LOCK_CONFIG_IDX (ATMEL_I2C_RSP_DATA_IDX + 3)
+
/* Command opcode */
#define ATMEL_I2C_OPCODE_ECDH 0x43
#define ATMEL_I2C_OPCODE_GENKEY 0x40
@@ -129,26 +134,6 @@ static int atmel_i2c_init_read_eeprom_cmd(struct atmel_i2c_cmd *cmd, u16 addr,
return 0;
}
-void atmel_i2c_init_read_config_cmd(struct atmel_i2c_cmd *cmd,
- const struct atmel_i2c_max_exec_timings *timings)
-{
- cmd->word_addr = ATMEL_I2C_COMMAND;
- cmd->opcode = ATMEL_I2C_OPCODE_READ;
- /*
- * Read the word from Configuration zone that contains the lock bytes
- * (UserExtra, Selector, LockValue, LockConfig).
- */
- cmd->param1 = CONFIGURATION_ZONE;
- cmd->param2 = cpu_to_le16(DEVICE_LOCK_ADDR);
- cmd->count = ATMEL_I2C_READ_COUNT;
-
- atmel_i2c_checksum(cmd);
-
- cmd->msecs = timings->max_exec_time_read;
- cmd->rxsize = ATMEL_I2C_READ_RSP_SIZE;
-}
-EXPORT_SYMBOL(atmel_i2c_init_read_config_cmd);
-
void atmel_i2c_init_random_cmd(struct atmel_i2c_cmd *cmd,
const struct atmel_i2c_max_exec_timings *timings)
{
@@ -247,7 +232,7 @@ static int atmel_i2c_rng_read_nonblocking(struct hwrng *rng, void *buf,
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);
+ memcpy(buf, &work_data->cmd.data[ATMEL_I2C_RSP_DATA_IDX], max);
rng->priv = 0;
} else {
work_data = kmalloc_obj(*work_data, GFP_ATOMIC);
@@ -287,7 +272,7 @@ static int atmel_i2c_rng_read(struct hwrng *rng, void *buf, size_t max,
return ret;
max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max);
- memcpy(buf, &cmd.data[RSP_DATA_IDX], max);
+ memcpy(buf, &cmd.data[ATMEL_I2C_RSP_DATA_IDX], max);
return max;
}
@@ -338,7 +323,7 @@ static int atmel_i2c_eeprom_read(struct i2c_client *client, u16 addr,
goto err;
}
- memcpy(buf, cmd->data + RSP_DATA_IDX, 4);
+ memcpy(buf, cmd->data + ATMEL_I2C_RSP_DATA_IDX, 4);
err:
kfree(cmd);
@@ -542,7 +527,7 @@ static inline size_t atmel_i2c_wake_token_sz(u32 bus_clk_rate)
return DIV_ROUND_UP(no_of_bits, 8);
}
-static int device_sanity_check(struct i2c_client *client)
+int atmel_i2c_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;
@@ -553,7 +538,8 @@ static int device_sanity_check(struct i2c_client *client)
if (!cmd)
return -ENOMEM;
- atmel_i2c_init_read_config_cmd(cmd, &data->timings);
+ atmel_i2c_init_read_eeprom_cmd(cmd, ATMEL_I2C_DEVICE_LOCK_ADDR,
+ ATMEL_EEPROM_CONFIG_ZONE, data);
ret = atmel_i2c_send_receive(client, cmd);
if (ret)
@@ -565,8 +551,8 @@ static int device_sanity_check(struct i2c_client *client)
* Failure to lock these zones may permit modification of any secret
* keys and may lead to other security problems.
*/
- if (cmd->data[LOCK_CONFIG_IDX] || cmd->data[LOCK_VALUE_IDX]) {
- dev_err(&client->dev, "Configuration or Data and OTP zones are unlocked!\n");
+ if (cmd->data[ATMEL_I2C_LOCK_CONFIG_IDX] || cmd->data[ATMEL_I2C_LOCK_VALUE_IDX]) {
+ dev_err(&client->dev, "Config, Data and OTP zones are unlocked!\n");
ret = -ENOTSUPP;
}
@@ -575,6 +561,7 @@ static int device_sanity_check(struct i2c_client *client)
kfree(cmd);
return ret;
}
+EXPORT_SYMBOL(atmel_i2c_device_sanity_check);
void atmel_i2c_unregister_client(struct atmel_i2c_client_priv *i2c_priv)
{
@@ -633,7 +620,7 @@ int atmel_i2c_probe(struct i2c_client *client)
i2c_set_clientdata(client, i2c_priv);
- return device_sanity_check(client);
+ return 0;
}
EXPORT_SYMBOL(atmel_i2c_probe);
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index e30e0c417de2..2f76e107340e 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -82,18 +82,10 @@ struct atmel_i2c_of_match_data {
#define STATUS_NOERR 0x00
#define STATUS_WAKE_SUCCESSFUL 0x11
-/* Definitions for eeprom organization */
-#define CONFIGURATION_ZONE 0
-
/* Definitions for Indexes common to all commands */
-#define RSP_DATA_IDX 1 /* buffer index of data in response */
+#define ATMEL_I2C_RSP_DATA_IDX 1 /* buffer index of data in response */
#define DATA_SLOT_2 2 /* used for ECDH private key */
-/* Definitions for the device lock state */
-#define DEVICE_LOCK_ADDR 0x15
-#define LOCK_VALUE_IDX (RSP_DATA_IDX + 2)
-#define LOCK_CONFIG_IDX (RSP_DATA_IDX + 3)
-
/*
* Wake High delay to data communication (microseconds). SDA should be stable
* high for this entire duration.
@@ -195,8 +187,6 @@ 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,
- 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,
@@ -207,6 +197,8 @@ int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
int atmel_i2c_register_rng(struct atmel_i2c_client_priv *i2c_priv,
struct device *dev);
+int atmel_i2c_device_sanity_check(struct i2c_client *client);
+
ssize_t atmel_i2c_eeprom_display(struct device *dev,
struct device_attribute *attr,
char *buf,
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index 341554b7b7a2..88726f6ef87c 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -64,6 +64,12 @@ static int atmel_sha204a_probe(struct i2c_client *client)
i2c_priv->data = data;
i2c_priv->caps = 0;
+ ret = atmel_i2c_device_sanity_check(client);
+ if (ret) {
+ dev_err(&client->dev, "failed to read EEPROM, is hardware attached?\n");
+ goto done;
+ }
+
/* add to client list */
spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
list_add_tail(&i2c_priv->i2c_client_list_node,
--
2.53.0
^ permalink raw reply related
* [PATCH 07/12] crypto: atmel - expose CONFIG zone through sysfs
From: Lothar Rubusch @ 2026-05-12 22:43 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260512224349.64621-1-l.rubusch@gmail.com>
Expose the CONFIG EEPROM zone through a read-only sysfs attribute for
Atmel I2C crypto devices.
The CONFIG zone contains device configuration state, including slot
configuration and lock status, which is useful for debugging and
verifying provisioning state.
Reuse the generic EEPROM display helper provided by the Atmel I2C core
driver to expose the CONFIG zone for both SHA204A and ECC devices.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 7 +++++++
drivers/crypto/atmel-sha204a.c | 7 +++++++
2 files changed, 14 insertions(+)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index b5f2d44ec74c..f08fdf284b60 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -23,6 +23,12 @@
#include <crypto/kpp.h>
#include "atmel-i2c.h"
+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);
+}
+static DEVICE_ATTR_ADMIN_RO(config);
+
static ssize_t otp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return atmel_i2c_eeprom_display(dev, attr, buf, ATMEL_EEPROM_OTP_ZONE);
@@ -30,6 +36,7 @@ static ssize_t otp_show(struct device *dev, struct device_attribute *attr, char
static DEVICE_ATTR_RO(otp);
static struct attribute *atmel_ecc508a_attrs[] = {
+ &dev_attr_config.attr,
&dev_attr_otp.attr,
NULL
};
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index 4f10e826e675..341554b7b7a2 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -19,6 +19,12 @@
#include <linux/workqueue.h>
#include "atmel-i2c.h"
+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);
+}
+static DEVICE_ATTR_ADMIN_RO(config);
+
static ssize_t otp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -27,6 +33,7 @@ static ssize_t otp_show(struct device *dev,
static DEVICE_ATTR_RO(otp);
static struct attribute *atmel_sha204a_attrs[] = {
+ &dev_attr_config.attr,
&dev_attr_otp.attr,
NULL
};
--
2.53.0
^ permalink raw reply related
* [PATCH 06/12] crypto: atmel - move EEPROM access support into common i2c core
From: Lothar Rubusch @ 2026-05-12 22:43 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260512224349.64621-1-l.rubusch@gmail.com>
Move EEPROM read support from atmel-sha204a and atmel-ecc into the
shared atmel-i2c core and provide a generic interface for accessing
EEPROM zones on compatible Atmel devices.
Introduce enum atmel_i2c_eeprom_zones together with per-device EEPROM
zone sizing in struct atmel_i2c_of_match_data. Add common helpers for
EEPROM readout and sysfs formatting, and convert existing OTP sysfs
handling to use the shared infrastructure.
This removes duplicated EEPROM access logic from individual drivers and
extends support to ECC devices. The common implementation supports
CONFIG, OTP, and DATA zones using device-specific layout information
supplied via match data tables.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 36 ++++++++
drivers/crypto/atmel-i2c.c | 153 +++++++++++++++++++++++++--------
drivers/crypto/atmel-i2c.h | 30 +++----
drivers/crypto/atmel-sha204a.c | 65 ++++----------
4 files changed, 186 insertions(+), 98 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 67fa5975fa7f..b5f2d44ec74c 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -23,6 +23,22 @@
#include <crypto/kpp.h>
#include "atmel-i2c.h"
+static ssize_t otp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return atmel_i2c_eeprom_display(dev, attr, buf, ATMEL_EEPROM_OTP_ZONE);
+}
+static DEVICE_ATTR_RO(otp);
+
+static struct attribute *atmel_ecc508a_attrs[] = {
+ &dev_attr_otp.attr,
+ NULL
+};
+
+static const struct attribute_group atmel_ecc508a_groups = {
+ .name = "atecc508a",
+ .attrs = atmel_ecc508a_attrs,
+};
+
/**
* struct atmel_ecdh_ctx - transformation context
* @client : pointer to i2c client device
@@ -306,6 +322,18 @@ static int atmel_ecc_probe(struct i2c_client *client)
&atmel_i2c_mgmt.i2c_client_list);
spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+ /* EEPROM read out */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ ret = -ENODEV;
+ goto err_list_del;
+ }
+
+ ret = sysfs_create_group(&client->dev.kobj, &atmel_ecc508a_groups);
+ if (ret) {
+ dev_err(&client->dev, "failed to register sysfs entry\n");
+ goto err_list_del;
+ }
+
/* register rng */
ret = atmel_i2c_register_rng(i2c_priv, &client->dev);
if (ret) {
@@ -326,6 +354,7 @@ static int atmel_ecc_probe(struct i2c_client *client)
goto done;
err_list_del:
+ sysfs_remove_group(&client->dev.kobj, &atmel_ecc508a_groups);
spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
list_del(&i2c_priv->i2c_client_list_node);
spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
@@ -361,6 +390,8 @@ static void atmel_ecc_remove(struct i2c_client *client)
kfree((void *)i2c_priv->hwrng.priv);
i2c_priv->hwrng.priv = 0;
}
+
+ sysfs_remove_group(&client->dev.kobj, &atmel_ecc508a_groups);
}
static const struct atmel_i2c_of_match_data atecc508a_match_data = {
@@ -371,6 +402,11 @@ static const struct atmel_i2c_of_match_data atecc508a_match_data = {
.max_exec_time_read = 1,
.max_exec_time_write = 42,
},
+ .eeprom_zone_size = {
+ [ATMEL_EEPROM_CONFIG_ZONE] = 128,
+ [ATMEL_EEPROM_OTP_ZONE] = 64,
+ [ATMEL_EEPROM_DATA_ZONE] = 1208
+ },
};
static const struct of_device_id atmel_ecc_dt_ids[] = {
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index d451017171d8..26863573a10f 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -21,6 +21,15 @@
#include <linux/workqueue.h>
#include "atmel-i2c.h"
+#define ATMEL_I2C_COMMAND 0x03 /* packet function */
+
+/* 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
+
struct atmel_i2c_client_mgmt atmel_i2c_mgmt = {
.i2c_list_lock = __SPIN_LOCK_UNLOCKED(atmel_i2c_mgmt.i2c_list_lock),
.i2c_client_list = LIST_HEAD_INIT(atmel_i2c_mgmt.i2c_client_list),
@@ -96,56 +105,55 @@ 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,
- const struct atmel_i2c_max_exec_timings *timings)
+static int atmel_i2c_init_read_eeprom_cmd(struct atmel_i2c_cmd *cmd, u16 addr,
+ enum atmel_i2c_eeprom_zones zone,
+ const struct atmel_i2c_of_match_data *data)
{
- cmd->word_addr = COMMAND;
- cmd->opcode = OPCODE_READ;
- /*
- * Read the word from Configuration zone that contains the lock bytes
- * (UserExtra, Selector, LockValue, LockConfig).
- */
- cmd->param1 = CONFIGURATION_ZONE;
- cmd->param2 = cpu_to_le16(DEVICE_LOCK_ADDR);
- cmd->count = READ_COUNT;
+ const struct atmel_i2c_max_exec_timings *timings = &data->timings;
+ size_t zone_size = data->eeprom_zone_size[zone];
+
+ if (addr > zone_size)
+ return -EINVAL;
+
+ cmd->word_addr = ATMEL_I2C_COMMAND;
+ cmd->opcode = ATMEL_I2C_OPCODE_READ;
+ cmd->param1 = zone;
+ cmd->param2 = cpu_to_le16(addr);
+ cmd->count = ATMEL_I2C_READ_COUNT;
atmel_i2c_checksum(cmd);
cmd->msecs = timings->max_exec_time_read;
- cmd->rxsize = READ_RSP_SIZE;
+ cmd->rxsize = ATMEL_I2C_READ_RSP_SIZE;
+
+ return 0;
}
-EXPORT_SYMBOL(atmel_i2c_init_read_config_cmd);
-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_read_config_cmd(struct atmel_i2c_cmd *cmd,
+ const struct atmel_i2c_max_exec_timings *timings)
{
- if (addr >= OTP_ZONE_SIZE / 4)
- return -EINVAL;
-
- cmd->word_addr = COMMAND;
- cmd->opcode = OPCODE_READ;
+ cmd->word_addr = ATMEL_I2C_COMMAND;
+ cmd->opcode = ATMEL_I2C_OPCODE_READ;
/*
- * Read the word from OTP zone that may contain e.g. serial
- * numbers or similar if persistently pre-initialized and locked
+ * Read the word from Configuration zone that contains the lock bytes
+ * (UserExtra, Selector, LockValue, LockConfig).
*/
- cmd->param1 = OTP_ZONE;
- cmd->param2 = cpu_to_le16(addr);
- cmd->count = READ_COUNT;
+ cmd->param1 = CONFIGURATION_ZONE;
+ cmd->param2 = cpu_to_le16(DEVICE_LOCK_ADDR);
+ cmd->count = ATMEL_I2C_READ_COUNT;
atmel_i2c_checksum(cmd);
cmd->msecs = timings->max_exec_time_read;
- cmd->rxsize = READ_RSP_SIZE;
-
- return 0;
+ cmd->rxsize = ATMEL_I2C_READ_RSP_SIZE;
}
-EXPORT_SYMBOL(atmel_i2c_init_read_otp_cmd);
+EXPORT_SYMBOL(atmel_i2c_init_read_config_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;
+ cmd->word_addr = ATMEL_I2C_COMMAND;
+ cmd->opcode = ATMEL_I2C_OPCODE_RANDOM;
cmd->param1 = 0;
cmd->param2 = 0;
cmd->count = RANDOM_COUNT;
@@ -160,9 +168,9 @@ EXPORT_SYMBOL(atmel_i2c_init_random_cmd);
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->word_addr = ATMEL_I2C_COMMAND;
cmd->count = GENKEY_COUNT;
- cmd->opcode = OPCODE_GENKEY;
+ cmd->opcode = ATMEL_I2C_OPCODE_GENKEY;
cmd->param1 = GENKEY_MODE_PRIVATE;
/* a random private key will be generated and stored in slot keyID */
cmd->param2 = cpu_to_le16(keyid);
@@ -180,9 +188,9 @@ int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
{
size_t copied;
- cmd->word_addr = COMMAND;
+ cmd->word_addr = ATMEL_I2C_COMMAND;
cmd->count = ECDH_COUNT;
- cmd->opcode = OPCODE_ECDH;
+ cmd->opcode = ATMEL_I2C_OPCODE_ECDH;
cmd->param1 = ECDH_PREFIX_MODE;
/* private key slot */
cmd->param2 = cpu_to_le16(DATA_SLOT_2);
@@ -301,6 +309,81 @@ int atmel_i2c_register_rng(struct atmel_i2c_client_priv *i2c_priv,
}
EXPORT_SYMBOL(atmel_i2c_register_rng);
+static int atmel_i2c_eeprom_read(struct i2c_client *client, u16 addr,
+ enum atmel_i2c_eeprom_zones zone, u8 *buf)
+{
+ 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 = -1;
+
+ cmd = kmalloc_obj(*cmd);
+ if (!cmd)
+ return -ENOMEM;
+
+ ret = atmel_i2c_init_read_eeprom_cmd(cmd, addr, zone, data);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed, invalid eeprom address %04X\n",
+ addr);
+ goto err;
+ }
+
+ ret = atmel_i2c_send_receive(client, cmd);
+ if (ret)
+ goto err;
+
+ if (cmd->data[0] == 0xff) {
+ dev_err(&client->dev, "failed, device not ready\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ memcpy(buf, cmd->data + RSP_DATA_IDX, 4);
+
+err:
+ kfree(cmd);
+ return ret;
+}
+
+ssize_t atmel_i2c_eeprom_display(struct device *dev,
+ struct device_attribute *attr,
+ char *buf,
+ enum atmel_i2c_eeprom_zones zone)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ const struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
+ const struct atmel_i2c_of_match_data *data = i2c_priv->data;
+ const size_t *eeprom = data->eeprom_zone_size;
+ u16 block_addr;
+ u8 *eeprom_buf;
+ ssize_t len = 0;
+ int i, ret = 0;
+
+ eeprom_buf = kcalloc(eeprom[zone], sizeof(*eeprom_buf), GFP_KERNEL);
+ if (!eeprom_buf)
+ return -ENOMEM;
+
+ for (block_addr = 0; block_addr < eeprom[zone] / 4; block_addr++) {
+ ret = atmel_i2c_eeprom_read(client, block_addr, zone,
+ eeprom_buf + block_addr * 4);
+ if (ret < 0) {
+ dev_err(dev, "failed to read %s zone\n",
+ zone == ATMEL_EEPROM_CONFIG_ZONE ? "CONFIG"
+ : (zone == ATMEL_EEPROM_OTP_ZONE ? "OTP" : "DATA"));
+ goto err;
+ }
+ }
+
+ for (i = 0; i < eeprom[zone]; i++)
+ len += sysfs_emit_at(buf, len, "%02X", eeprom_buf[i]);
+ len += sysfs_emit_at(buf, len, "\n");
+ ret = len;
+err:
+ kfree(eeprom_buf);
+ return ret;
+}
+EXPORT_SYMBOL(atmel_i2c_eeprom_display);
+
/*
* 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 5f6c9ff0cf64..e30e0c417de2 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -12,7 +12,6 @@
#define ATMEL_ECC_PRIORITY 300
-#define COMMAND 0x03 /* packet function */
#define SLEEP_TOKEN 0x01
#define WAKE_TOKEN_MAX_SIZE 8
@@ -30,7 +29,7 @@
#define ECDH_RSP_SIZE (32 + CMD_OVERHEAD_SIZE)
#define GENKEY_RSP_SIZE (ATMEL_ECC_PUBKEY_SIZE + \
CMD_OVERHEAD_SIZE)
-#define READ_RSP_SIZE (4 + CMD_OVERHEAD_SIZE)
+#define ATMEL_I2C_READ_RSP_SIZE (4 + CMD_OVERHEAD_SIZE)
#define RANDOM_RSP_SIZE (32 + CMD_OVERHEAD_SIZE)
#define MAX_RSP_SIZE GENKEY_RSP_SIZE
@@ -57,6 +56,13 @@ struct atmel_i2c_cmd {
u16 rxsize;
} __packed;
+/* Definitions for eeprom organization */
+enum atmel_i2c_eeprom_zones {
+ ATMEL_EEPROM_CONFIG_ZONE = 0,
+ ATMEL_EEPROM_OTP_ZONE = 1,
+ ATMEL_EEPROM_DATA_ZONE = 2,
+};
+
struct atmel_i2c_max_exec_timings {
unsigned int max_exec_time_genkey;
unsigned int max_exec_time_ecdh;
@@ -68,6 +74,7 @@ struct atmel_i2c_max_exec_timings {
struct atmel_i2c_of_match_data {
const unsigned short needs_legacy_hwrng;
struct atmel_i2c_max_exec_timings timings;
+ size_t eeprom_zone_size[3]; /* all atmel devices have three zones */
};
/* Status/Error codes */
@@ -77,10 +84,6 @@ struct atmel_i2c_of_match_data {
/* Definitions for eeprom organization */
#define CONFIGURATION_ZONE 0
-#define OTP_ZONE 1
-
-/* Definitions for eeprom zone sizes */
-#define OTP_ZONE_SIZE 64
/* Definitions for Indexes common to all commands */
#define RSP_DATA_IDX 1 /* buffer index of data in response */
@@ -101,14 +104,8 @@ struct atmel_i2c_of_match_data {
/* Wake Low duration */
#define TWLO_USEC 60
-/* Command opcode */
-#define OPCODE_ECDH 0x43
-#define OPCODE_GENKEY 0x40
-#define OPCODE_READ 0x02
-#define OPCODE_RANDOM 0x1b
-
/* Definitions for the READ Command */
-#define READ_COUNT 7
+#define ATMEL_I2C_READ_COUNT 7
/* Definitions for the RANDOM Command */
#define RANDOM_COUNT 7
@@ -200,8 +197,6 @@ 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,
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,
@@ -212,6 +207,11 @@ int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
int atmel_i2c_register_rng(struct atmel_i2c_client_priv *i2c_priv,
struct device *dev);
+ssize_t atmel_i2c_eeprom_display(struct device *dev,
+ struct device_attribute *attr,
+ char *buf,
+ enum atmel_i2c_eeprom_zones zone);
+
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 ae24d8fbabf9..4f10e826e675 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -19,57 +19,10 @@
#include <linux/workqueue.h>
#include "atmel-i2c.h"
-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, &data->timings);
- if (ret < 0) {
- dev_err(&client->dev, "failed, invalid otp address %04X\n",
- addr);
- return ret;
- }
-
- ret = atmel_i2c_send_receive(client, &cmd);
- if (ret < 0) {
- dev_err(&client->dev, "failed to read otp at %04X\n", addr);
- return ret;
- }
-
- if (cmd.data[0] == 0xff) {
- dev_err(&client->dev, "failed, device not ready\n");
- return -EIO;
- }
-
- memcpy(otp, cmd.data+1, 4);
-
- return ret;
-}
-
static ssize_t otp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- u16 addr;
- u8 otp[OTP_ZONE_SIZE];
- struct i2c_client *client = to_i2c_client(dev);
- ssize_t len = 0;
- int i, ret;
-
- for (addr = 0; addr < OTP_ZONE_SIZE / 4; addr++) {
- ret = atmel_sha204a_otp_read(client, addr, otp + addr * 4);
- if (ret < 0) {
- dev_err(dev, "failed to read otp zone\n");
- return ret;
- }
- }
-
- for (i = 0; i < OTP_ZONE_SIZE; i++)
- len += sysfs_emit_at(buf, len, "%02X", otp[i]);
- len += sysfs_emit_at(buf, len, "\n");
- return len;
+ return atmel_i2c_eeprom_display(dev, attr, buf, ATMEL_EEPROM_OTP_ZONE);
}
static DEVICE_ATTR_RO(otp);
@@ -110,6 +63,12 @@ static int atmel_sha204a_probe(struct i2c_client *client)
&atmel_i2c_mgmt.i2c_client_list);
spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+ /* EEPROM read out */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ ret = -ENODEV;
+ goto err_list_del;
+ }
+
ret = sysfs_create_group(&client->dev.kobj, &atmel_sha204a_groups);
if (ret) {
dev_err(&client->dev, "failed to register sysfs entry\n");
@@ -157,6 +116,11 @@ static const struct atmel_i2c_of_match_data atsha204_match_data = {
.max_exec_time_read = 4,
.max_exec_time_write = 42,
},
+ .eeprom_zone_size = {
+ [ATMEL_EEPROM_CONFIG_ZONE] = 88,
+ [ATMEL_EEPROM_OTP_ZONE] = 64,
+ [ATMEL_EEPROM_DATA_ZONE] = 512
+ },
/*
* According to review by Bill Cox [1], the ATSHA204 has very low entropy.
* [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html
@@ -171,6 +135,11 @@ static const struct atmel_i2c_of_match_data atsha204a_match_data = {
.max_exec_time_read = 4,
.max_exec_time_write = 42,
},
+ .eeprom_zone_size = {
+ [ATMEL_EEPROM_CONFIG_ZONE] = 88,
+ [ATMEL_EEPROM_OTP_ZONE] = 64,
+ [ATMEL_EEPROM_DATA_ZONE] = 512
+ },
};
static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = {
--
2.53.0
^ permalink raw reply related
* [PATCH 05/12] crypto: atmel - move RNG support into common i2c core
From: Lothar Rubusch @ 2026-05-12 22:43 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
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
^ permalink raw reply related
* [PATCH 04/12] crypto: atmel - add per-device timing and match-data driven configuration
From: Lothar Rubusch @ 2026-05-12 22:43 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260512224349.64621-1-l.rubusch@gmail.com>
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 <l.rubusch@gmail.com>
---
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
^ permalink raw reply related
* [PATCH 03/12] crypto: atmel - remove obsolete CONFIG_OF guard
From: Lothar Rubusch @ 2026-05-12 22:43 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260512224349.64621-1-l.rubusch@gmail.com>
Remove the CONFIG_OF preprocessor guard around the OF device match
table in atmel-ecc.
OF match tables are expected to be present unconditionally and the
MODULE_DEVICE_TABLE(of, ...) handling already accounts for
configurations where OF support is disabled. Keeping the additional
guard provides no benefit and only adds unnecessary conditional
compilation.
Also compact the match table formatting while touching the code.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index c63d30947bd7..0dede3707b73 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -339,18 +339,12 @@ static void atmel_ecc_remove(struct i2c_client *client)
crypto_unregister_kpp(&atmel_ecdh_nist_p256);
}
-#ifdef CONFIG_OF
static const struct of_device_id atmel_ecc_dt_ids[] = {
- {
- .compatible = "atmel,atecc508a",
- }, {
- .compatible = "atmel,atecc608b",
- }, {
- /* sentinel */
- }
+ { .compatible = "atmel,atecc508a", },
+ { .compatible = "atmel,atecc608b", },
+ { }
};
MODULE_DEVICE_TABLE(of, atmel_ecc_dt_ids);
-#endif
static const struct i2c_device_id atmel_ecc_id[] = {
{ "atecc508a" },
--
2.53.0
^ permalink raw reply related
* [PATCH 02/12] crypto: atmel - move capability-based client allocation into i2c core
From: Lothar Rubusch @ 2026-05-12 22:43 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260512224349.64621-1-l.rubusch@gmail.com>
Move the i2c client allocation logic from atmel-ecc into the shared
atmel-i2c core and extend it to support capability-based client
selection.
Introduce enum atmel_i2c_capability and add capability flags to
struct atmel_i2c_client_priv. Devices now advertise their supported
features during probe, allowing atmel_i2c_client_alloc() to select a
compatible client from the shared i2c client list.
The allocation logic continues to balance crypto transformation usage
across devices by selecting the client with the lowest tfm_count, but
is no longer limited to ECC-capable devices.
This centralizes shared client management in the common atmel-i2c core
and prepares the infrastructure for additional shared crypto features
across compatible Atmel devices.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 39 +++-------------------------------
drivers/crypto/atmel-i2c.c | 39 ++++++++++++++++++++++++++++++++++
drivers/crypto/atmel-i2c.h | 7 ++++++
drivers/crypto/atmel-sha204a.c | 2 ++
4 files changed, 51 insertions(+), 36 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index cba4238735cc..c63d30947bd7 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -200,41 +200,6 @@ static int atmel_ecdh_compute_shared_secret(struct kpp_request *req)
return ret;
}
-static struct i2c_client *atmel_ecc_i2c_client_alloc(void)
-{
- struct atmel_i2c_client_priv *i2c_priv, *min_i2c_priv = NULL;
- struct i2c_client *client = ERR_PTR(-ENODEV);
- int min_tfm_cnt = INT_MAX;
- int tfm_cnt;
-
- spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
-
- if (list_empty(&atmel_i2c_mgmt.i2c_client_list)) {
- spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
- return ERR_PTR(-ENODEV);
- }
-
- list_for_each_entry(i2c_priv, &atmel_i2c_mgmt.i2c_client_list,
- i2c_client_list_node) {
- tfm_cnt = atomic_read(&i2c_priv->tfm_count);
- if (tfm_cnt < min_tfm_cnt) {
- min_tfm_cnt = tfm_cnt;
- min_i2c_priv = i2c_priv;
- }
- if (!min_tfm_cnt)
- break;
- }
-
- if (min_i2c_priv) {
- atomic_inc(&min_i2c_priv->tfm_count);
- client = min_i2c_priv->client;
- }
-
- spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
-
- return client;
-}
-
static void atmel_ecc_i2c_client_free(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
@@ -249,7 +214,7 @@ static int atmel_ecdh_init_tfm(struct crypto_kpp *tfm)
struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);
ctx->curve_id = ECC_CURVE_NIST_P256;
- ctx->client = atmel_ecc_i2c_client_alloc();
+ ctx->client = atmel_i2c_client_alloc(ATMEL_CAP_ECDH);
if (IS_ERR(ctx->client)) {
pr_err("tfm - i2c_client binding failed\n");
return PTR_ERR(ctx->client);
@@ -321,6 +286,8 @@ static int atmel_ecc_probe(struct i2c_client *client)
i2c_priv = i2c_get_clientdata(client);
+ i2c_priv->caps = BIT(ATMEL_CAP_ECDH);
+
/* add to client list */
spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
list_add_tail(&i2c_priv->i2c_client_list_node,
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index 861af52d7a88..b7ee2ec37531 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -57,6 +57,45 @@ static void atmel_i2c_checksum(struct atmel_i2c_cmd *cmd)
*__crc16 = cpu_to_le16(bitrev16(crc16(0, data, len)));
}
+struct i2c_client *atmel_i2c_client_alloc(enum atmel_i2c_capability cap)
+{
+ struct atmel_i2c_client_priv *i2c_priv, *min_i2c_priv = NULL;
+ struct i2c_client *client = ERR_PTR(-ENODEV);
+ int min_tfm_cnt = INT_MAX;
+ int tfm_cnt;
+
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+
+ if (list_empty(&atmel_i2c_mgmt.i2c_client_list)) {
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+ return ERR_PTR(-ENODEV);
+ }
+
+ list_for_each_entry(i2c_priv, &atmel_i2c_mgmt.i2c_client_list,
+ i2c_client_list_node) {
+ if (!(i2c_priv->caps & BIT(cap)))
+ continue;
+
+ tfm_cnt = atomic_read(&i2c_priv->tfm_count);
+ if (tfm_cnt < min_tfm_cnt) {
+ min_tfm_cnt = tfm_cnt;
+ min_i2c_priv = i2c_priv;
+ }
+ if (!min_tfm_cnt)
+ break;
+ }
+
+ if (min_i2c_priv) {
+ atomic_inc(&min_i2c_priv->tfm_count);
+ client = min_i2c_priv->client;
+ }
+
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+
+ return client;
+}
+EXPORT_SYMBOL(atmel_i2c_client_alloc);
+
void atmel_i2c_init_read_config_cmd(struct atmel_i2c_cmd *cmd)
{
cmd->word_addr = COMMAND;
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 43a0c1cfcd94..70579b438256 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -115,6 +115,10 @@ struct atmel_i2c_cmd {
#define ECDH_PREFIX_MODE 0x00
/* Used for binding tfm objects to i2c clients. */
+enum atmel_i2c_capability {
+ ATMEL_CAP_ECDH = 0,
+};
+
struct atmel_i2c_client_mgmt {
struct list_head i2c_client_list;
spinlock_t i2c_list_lock;
@@ -130,6 +134,7 @@ extern struct atmel_i2c_client_mgmt atmel_i2c_mgmt;
* @wake_token_sz : size in bytes of the wake_token
* @tfm_count : number of active crypto transformations on i2c client
* @hwrng : hold the hardware generated rng
+ * @caps : feature capability of the particular driver
*
* 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
@@ -146,6 +151,7 @@ struct atmel_i2c_client_priv {
size_t wake_token_sz;
atomic_t tfm_count ____cacheline_aligned;
struct hwrng hwrng;
+ u32 caps;
};
/**
@@ -190,6 +196,7 @@ 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);
+struct i2c_client *atmel_i2c_client_alloc(enum atmel_i2c_capability cap);
void atmel_i2c_unregister_client(struct atmel_i2c_client_priv *i2c_priv);
#endif /* __ATMEL_I2C_H__ */
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index e6808c2bc891..ab758c9cd410 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -173,6 +173,8 @@ static int atmel_sha204a_probe(struct i2c_client *client)
i2c_priv = i2c_get_clientdata(client);
+ i2c_priv->caps = 0;
+
/* add to client list */
spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
list_add_tail(&i2c_priv->i2c_client_list_node,
--
2.53.0
^ permalink raw reply related
* [PATCH 01/12] crypto: atmel - introduce shared I2C client management
From: Lothar Rubusch @ 2026-05-12 22:43 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260512224349.64621-1-l.rubusch@gmail.com>
Introduce a shared I2C client management infrastructure in the
atmel-i2c core and convert the atmel-ecc and atmel-sha204a drivers
to use it.
Replace the driver-local atmel_ecc_driver_data structure with the
common atmel_i2c_mgmt instance, providing a shared client list and
locking for compatible Atmel secure element devices. Add a common
atmel_i2c_unregister_client() helper to centralize client removal
handling.
Refactor both drivers to use module_i2c_driver() and move duplicated
client list handling into the shared infrastructure. Probe and remove
paths are updated accordingly, including consistent error unwinding
for client registration failures.
Subsequent patches will build on the shared client infrastructure.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 58 ++++++++++++++--------------------
drivers/crypto/atmel-i2c.c | 15 +++++++++
drivers/crypto/atmel-i2c.h | 5 ++-
drivers/crypto/atmel-sha204a.c | 38 ++++++++++++----------
4 files changed, 65 insertions(+), 51 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 3738a4eb8701..cba4238735cc 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -23,8 +23,6 @@
#include <crypto/kpp.h>
#include "atmel-i2c.h"
-static struct atmel_ecc_driver_data driver_data;
-
/**
* struct atmel_ecdh_ctx - transformation context
* @client : pointer to i2c client device
@@ -209,14 +207,14 @@ static struct i2c_client *atmel_ecc_i2c_client_alloc(void)
int min_tfm_cnt = INT_MAX;
int tfm_cnt;
- spin_lock(&driver_data.i2c_list_lock);
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
- if (list_empty(&driver_data.i2c_client_list)) {
- spin_unlock(&driver_data.i2c_list_lock);
+ if (list_empty(&atmel_i2c_mgmt.i2c_client_list)) {
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
return ERR_PTR(-ENODEV);
}
- list_for_each_entry(i2c_priv, &driver_data.i2c_client_list,
+ list_for_each_entry(i2c_priv, &atmel_i2c_mgmt.i2c_client_list,
i2c_client_list_node) {
tfm_cnt = atomic_read(&i2c_priv->tfm_count);
if (tfm_cnt < min_tfm_cnt) {
@@ -232,7 +230,7 @@ static struct i2c_client *atmel_ecc_i2c_client_alloc(void)
client = min_i2c_priv->client;
}
- spin_unlock(&driver_data.i2c_list_lock);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
return client;
}
@@ -319,27 +317,34 @@ static int atmel_ecc_probe(struct i2c_client *client)
ret = atmel_i2c_probe(client);
if (ret)
- return ret;
+ goto done;
i2c_priv = i2c_get_clientdata(client);
- spin_lock(&driver_data.i2c_list_lock);
+ /* add to client list */
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
list_add_tail(&i2c_priv->i2c_client_list_node,
- &driver_data.i2c_client_list);
- spin_unlock(&driver_data.i2c_list_lock);
+ &atmel_i2c_mgmt.i2c_client_list);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+ /* register algorithms */
ret = crypto_register_kpp(&atmel_ecdh_nist_p256);
if (ret) {
- spin_lock(&driver_data.i2c_list_lock);
- list_del(&i2c_priv->i2c_client_list_node);
- spin_unlock(&driver_data.i2c_list_lock);
-
dev_err(&client->dev, "%s alg registration failed\n",
atmel_ecdh_nist_p256.base.cra_driver_name);
+ goto err_list_del;
} else {
dev_info(&client->dev, "atmel ecc algorithms registered in /proc/crypto\n");
}
+ goto done;
+
+err_list_del:
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+ list_del(&i2c_priv->i2c_client_list_node);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+
+done:
return ret;
}
@@ -361,11 +366,10 @@ static void atmel_ecc_remove(struct i2c_client *client)
return;
}
- crypto_unregister_kpp(&atmel_ecdh_nist_p256);
+ atmel_i2c_unregister_client(i2c_priv);
+ atmel_i2c_flush_queue();
- spin_lock(&driver_data.i2c_list_lock);
- list_del(&i2c_priv->i2c_client_list_node);
- spin_unlock(&driver_data.i2c_list_lock);
+ crypto_unregister_kpp(&atmel_ecdh_nist_p256);
}
#ifdef CONFIG_OF
@@ -398,21 +402,7 @@ static struct i2c_driver atmel_ecc_driver = {
.id_table = atmel_ecc_id,
};
-static int __init atmel_ecc_init(void)
-{
- spin_lock_init(&driver_data.i2c_list_lock);
- INIT_LIST_HEAD(&driver_data.i2c_client_list);
- return i2c_add_driver(&atmel_ecc_driver);
-}
-
-static void __exit atmel_ecc_exit(void)
-{
- atmel_i2c_flush_queue();
- i2c_del_driver(&atmel_ecc_driver);
-}
-
-module_init(atmel_ecc_init);
-module_exit(atmel_ecc_exit);
+module_i2c_driver(atmel_ecc_driver);
MODULE_AUTHOR("Tudor Ambarus");
MODULE_DESCRIPTION("Microchip / Atmel ECC (I2C) driver");
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index 0e275dbdc8c5..861af52d7a88 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -21,6 +21,12 @@
#include <linux/workqueue.h>
#include "atmel-i2c.h"
+struct atmel_i2c_client_mgmt atmel_i2c_mgmt = {
+ .i2c_list_lock = __SPIN_LOCK_UNLOCKED(atmel_i2c_mgmt.i2c_list_lock),
+ .i2c_client_list = LIST_HEAD_INIT(atmel_i2c_mgmt.i2c_client_list),
+};
+EXPORT_SYMBOL_GPL(atmel_i2c_mgmt);
+
static const struct {
u8 value;
const char *error_text;
@@ -348,6 +354,15 @@ static int device_sanity_check(struct i2c_client *client)
return ret;
}
+void atmel_i2c_unregister_client(struct atmel_i2c_client_priv *i2c_priv)
+{
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+ if (!list_empty(&i2c_priv->i2c_client_list_node))
+ list_del_init(&i2c_priv->i2c_client_list_node);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+}
+EXPORT_SYMBOL(atmel_i2c_unregister_client);
+
int atmel_i2c_probe(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv;
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 72f04c15682f..43a0c1cfcd94 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -115,10 +115,11 @@ struct atmel_i2c_cmd {
#define ECDH_PREFIX_MODE 0x00
/* Used for binding tfm objects to i2c clients. */
-struct atmel_ecc_driver_data {
+struct atmel_i2c_client_mgmt {
struct list_head i2c_client_list;
spinlock_t i2c_list_lock;
} ____cacheline_aligned;
+extern struct atmel_i2c_client_mgmt atmel_i2c_mgmt;
/**
* atmel_i2c_client_priv - i2c_client private data
@@ -189,4 +190,6 @@ 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);
+void atmel_i2c_unregister_client(struct atmel_i2c_client_priv *i2c_priv);
+
#endif /* __ATMEL_I2C_H__ */
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index ed7d69bf6890..e6808c2bc891 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -169,10 +169,17 @@ static int atmel_sha204a_probe(struct i2c_client *client)
ret = atmel_i2c_probe(client);
if (ret)
- return ret;
+ goto done;
i2c_priv = i2c_get_clientdata(client);
+ /* add to client list */
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+ list_add_tail(&i2c_priv->i2c_client_list_node,
+ &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);
@@ -183,15 +190,26 @@ static int atmel_sha204a_probe(struct i2c_client *client)
i2c_priv->hwrng.quality = *quality;
ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng);
- if (ret)
+ if (ret) {
dev_warn(&client->dev, "failed to register RNG (%d)\n", ret);
+ goto err_list_del;
+ }
ret = sysfs_create_group(&client->dev.kobj, &atmel_sha204a_groups);
if (ret) {
dev_err(&client->dev, "failed to register sysfs entry\n");
- return ret;
+ goto err_list_del;
}
+ goto done;
+
+err_list_del:
+ sysfs_remove_group(&client->dev.kobj, &atmel_sha204a_groups);
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+ list_del(&i2c_priv->i2c_client_list_node);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+
+done:
return ret;
}
@@ -230,19 +248,7 @@ static struct i2c_driver atmel_sha204a_driver = {
.driver.of_match_table = of_match_ptr(atmel_sha204a_dt_ids),
};
-static int __init atmel_sha204a_init(void)
-{
- return i2c_add_driver(&atmel_sha204a_driver);
-}
-
-static void __exit atmel_sha204a_exit(void)
-{
- atmel_i2c_flush_queue();
- i2c_del_driver(&atmel_sha204a_driver);
-}
-
-module_init(atmel_sha204a_init);
-module_exit(atmel_sha204a_exit);
+module_i2c_driver(atmel_sha204a_driver);
MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
MODULE_DESCRIPTION("Microchip / Atmel SHA204A (I2C) driver");
--
2.53.0
^ permalink raw reply related
* [PATCH 00/12] crypto: atmel - refactor common i2c support and add SHA256 ahash support
From: Lothar Rubusch @ 2026-05-12 22:43 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
This series restructures the Atmel secure element drivers around a
shared atmel-i2c core and adds SHA256 ahash support for ATSHA204A and
ECC based devices.
The existing drivers duplicated substantial parts of the transport,
RNG, EEPROM and device management logic. This series consolidates the
common functionality into the shared i2c core and converts the client
drivers to capability based allocation.
The series also introduces per-device timing configuration through
match data, moves sanity checks and RNG handling into the core driver,
updates workqueue handling and cleans up internal constants and helper
definitions.
The final patch adds SHA256 ahash support using the hardware SHA engine
provided by the devices.
ATSHA204A devices require software-side SHA256 padding according to
FIPS 180-4, while newer ECC devices provide a dedicated SHA final
command and perform padding internally in hardware.
Supporting the SHA engine also requires changes to the command
transport path. SHA operations must execute as a strict uninterrupted
sequence consisting of SHA INIT, one or more SHA COMPUTE commands and,
for ECC devices, a terminating SHA FINAL command. The device loses its
internal SHA state if it enters sleep mode or if unrelated commands
are interleaved during the transaction.
To satisfy these hardware requirements, the send/receive path is split
into a low-level transfer helper and a higher-level wrapper managing
wakeup, sleep and locking. SHA operations keep the device awake and
hold the i2c lock for the full duration of the hashing transaction.
The series has been tested on ATSHA204A and ATECC508A devices.
Tests are ongoing/pending on ATECC608A and ATECC608B.
---
Lothar Rubusch (12):
crypto: atmel - introduce shared I2C client management
crypto: atmel - move capability-based client allocation into i2c core
crypto: atmel - remove obsolete CONFIG_OF guard
crypto: atmel - add per-device timing and match-data driven
configuration
crypto: atmel - move RNG support into common i2c core
crypto: atmel - move EEPROM access support into common i2c core
crypto: atmel - expose CONFIG zone through sysfs
crypto: atmel - move device sanity check to core driver
crypto: atmel - check client data in remove callbacks
crypto: atmel - update workqueue flags and add flush on exit
crypto: atmel - refactor and localize driver constants
crypto: atmel - add SHA256 ahash support
drivers/crypto/atmel-ecc.c | 252 +++++++-----
drivers/crypto/atmel-i2c.c | 679 +++++++++++++++++++++++++++++----
drivers/crypto/atmel-i2c.h | 180 +++++----
drivers/crypto/atmel-sha204a.c | 284 +++++++-------
4 files changed, 1010 insertions(+), 385 deletions(-)
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
base-commit: f7dd32c5179d7755de18e21d5674b08f9e5cb180
--
2.53.0
^ permalink raw reply
* Re: [PATCH] crypto: af_alg - Document the deprecation of AF_ALG
From: Ignat Korchagin @ 2026-05-12 21:18 UTC (permalink / raw)
To: Eric Biggers
Cc: Kamran Khan, Jeff Barnes, Andy Lutomirski,
linux-crypto@vger.kernel.org, Herbert Xu,
linux-doc@vger.kernel.org, linux-api@vger.kernel.org,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
Linus Torvalds
In-Reply-To: <20260511213829.GA316710@google.com>
On Mon, May 11, 2026 at 10:38 PM Eric Biggers <ebiggers@kernel.org> wrote:
>
> On Mon, May 11, 2026 at 10:03:21PM +0100, Ignat Korchagin wrote:
> > I don't think fully discounting hardware offloading is beneficial here. HW
> > accelerators will be produced and without a common interface vendors would
> > start implementing their own "bespoke" drivers with bespoke userspace
> > interfaces (we already had such proposals), which in turn may introduce more
> > attack surface. Yes, AF_ALG needs substantial improvement, but at least it
> > can be a standardisation point.
>
> That isn't the best way to accelerate symmetric crypto anymore though,
> if it ever was. This has been known for a long time.
>
> > > In any case, any hypothetical security benefit provided by AF_ALG would
> > > have to be *very high* to outweigh the continuous stream of
> > > vulnerabilities in it. I understand that people using AF_ALG might not
> > > be familiar with that continuous stream of vulnerabilities, but it would
> >
> >
> > Is it actually that much compared to other features/subsystems, like eBPF or
> > user namespaces? But we don't rush to deprecate those - instead trying to
> > harden them and come up with better design.
>
> There are plenty of other kernel features with a large attack surface,
> of course. But they tend to be much more useful than AF_ALG. It's all
> about weighing benefits vs. risks.
If divide number of CVEs in such systems on imaginary units of
usefulness, I think the ratio is similar.
> When we get the point where a large number of Linux users *had* to
> disable AF_ALG as an emergency vulnerability response, and at the same
> time their systems weren't even using AF_ALG so nothing even broke and
> they could have just done that to begin with, I think we get a very
Well, there were: cryptsetup, RHEL fips check, so there are some...
> clear idea of which side is heavier for AF_ALG in the real world.
Same thing could be said for unprivileged user namespaces - distros
even put a custom sysctl to restrict it and no-one noticed.
> The main relevance of AF_ALG to the Linux community is that it allows
> their systems to be exploited.
To be clear I'm not arguing for the current AF_ALG implementation. I
agree, the splice zero-copy is... suboptimal (to be soft) and is
actually not-so-zero copy. But I think it was just added before we had
more modern approaches like io_uring (have their own can of worms, but
hey - people adopt it fast).
But I advocate for the usefulness of the concept itself - kernel/OS
providing crypto services to userspace. As mentioned in other threads,
other operating systems have it and Linux lags behind. There are use
cases: common interface for HW accelerators, embedded systems, which
don't have the space to bring a userspace lib etc. Even non-technical:
there are environments that just don't want to rely on third-party
userspace libraries like OpenSSL purely for licensing reasons. And I
agree, that it is hard to do it right, but we can piggy-back on other
subsystems (such as io_uring mentioned or other ideas).
> - Eric
>
Ignat
^ permalink raw reply
* Re: (subset) [PATCH 0/2] crypto: qcom: Add QCE for Eliza SoC
From: Bjorn Andersson @ 2026-05-12 20:22 UTC (permalink / raw)
To: Thara Gopinath, Herbert Xu, David S. Miller, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio,
Krzysztof Kozlowski
Cc: linux-arm-msm, linux-crypto, devicetree, linux-kernel
In-Reply-To: <20260407-crypto-qcom-eliza-v1-0-40f61a1454a2@oss.qualcomm.com>
On Tue, 07 Apr 2026 15:51:41 +0200, Krzysztof Kozlowski wrote:
> Bindings for Qualcomm Eliza SoC crypto engine and DTS patch.
>
> Best regards,
> Krzysztof
>
Applied, thanks!
[2/2] arm64: dts: qcom: eliza: Add QCE crypto
commit: 5a95bd4771ffd591e810717e1c6dcc568ebf45d8
Best regards,
--
Bjorn Andersson <andersson@kernel.org>
^ permalink raw reply
* Re: (subset) [PATCH v5 00/13] Add explicit clock vote and enable power-domain for QCOM-ICE
From: Bjorn Andersson @ 2026-05-12 20:22 UTC (permalink / raw)
To: Herbert Xu, David S. Miller, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Konrad Dybcio, Abel Vesa, Manivannan Sadhasivam,
cros-qcom-dts-watchers, Eric Biggers, Dmitry Baryshkov,
Jingyi Wang, Tengfei Fan, Bartosz Golaszewski, David Wronek,
Luca Weiss, Neil Armstrong, Melody Olvera, Alexander Koskovich,
Abel Vesa, Harshal Dev
Cc: Brian Masney, Neeraj Soni, Gaurav Kashyap, linux-arm-msm,
linux-crypto, devicetree, linux-kernel, Krzysztof Kozlowski,
Konrad Dybcio, Kuldeep Singh, Krzysztof Kozlowski,
Manivannan Sadhasivam
In-Reply-To: <20260416-qcom_ice_power_and_clk_vote-v5-0-5ccf5d7e2846@oss.qualcomm.com>
On Thu, 16 Apr 2026 17:29:17 +0530, Harshal Dev wrote:
> When the kernel is booted without the 'clk_ignore_unused' and
> 'pd_ignore_unused' command‑line flags, votes for unused clocks and power
> domains are dropped by the kernel post late_init and deferred probe
> timeout. Depending on the relative timing between the ICE probe and the
> kernel disabling the unused clocks and power domains occasional unclocked
> register accesses or 'stuck' clocks are observed during QCOM‑ICE probe.
> When the 'iface' clock is not voted on, unclocked register access would
> be observed. On the other hand, if the associated power-domain for ICE
> is not enabled, a 'stuck' clock is observed.
>
> [...]
Applied, thanks!
[02/13] soc: qcom: ice: Allow explicit votes on 'iface' clock for ICE
commit: 0d5dc5818191b55e4364d04b1b898a14a2ccac38
Best regards,
--
Bjorn Andersson <andersson@kernel.org>
^ permalink raw reply
* Re: (subset) [PATCH v2 0/2] Add Crypto Engine support for the Glymur SoC
From: Bjorn Andersson @ 2026-05-12 20:22 UTC (permalink / raw)
To: Thara Gopinath, Herbert Xu, David S. Miller, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio,
Dmitry Baryshkov, Harshal Dev
Cc: Neeraj Soni, Kuldeep Singh, Abel Vesa, linux-arm-msm,
linux-crypto, devicetree, linux-kernel, Krzysztof Kozlowski
In-Reply-To: <20260505-glymur_crypto_enablement-v2-0-bf115aeb1459@oss.qualcomm.com>
On Tue, 05 May 2026 13:10:02 +0530, Harshal Dev wrote:
> Document and add the device-tree nodes to enable the Crypto Engine
> and its BAM for Glymur.
>
>
Applied, thanks!
[2/2] arm64: dts: qcom: glymur: Add crypto engine and BAM
commit: 93e08fdc55f227847dc9b249fd5eb43403e7e8b9
Best regards,
--
Bjorn Andersson <andersson@kernel.org>
^ permalink raw reply
* Re: (subset) [PATCH 0/2] arm64: dts: qcom: milos: Add QCrypto support
From: Bjorn Andersson @ 2026-05-12 20:22 UTC (permalink / raw)
To: Thara Gopinath, Herbert Xu, David S. Miller, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio,
Alexander Koskovich
Cc: linux-crypto, linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <20260405-milos-qce-v1-0-6996fb0b8a9c@pm.me>
On Mon, 06 Apr 2026 02:09:59 +0000, Alexander Koskovich wrote:
> Add the dt-bindings and device tree nodes for the Qualcomm Crypto
> Engine (QCE) and its associated BAM DMA controller on the Milos
> platform.
>
>
Applied, thanks!
[2/2] arm64: dts: qcom: milos: Add QCrypto nodes
commit: 208af18362e01f59bfe4a71a8606b09acb673cd0
Best regards,
--
Bjorn Andersson <andersson@kernel.org>
^ permalink raw reply
* Re: [PATCH v1 1/2] dt-bindings: Add bindings for StarFive JHB100 SoC trng controller.
From: Conor Dooley @ 2026-05-12 19:35 UTC (permalink / raw)
To: lianfeng.ouyang
Cc: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, linux-crypto, devicetree,
linux-kernel
In-Reply-To: <20260512-seventeen-deduct-fa7eead281ef@spud>
[-- Attachment #1: Type: text/plain, Size: 1315 bytes --]
On Tue, May 12, 2026 at 06:15:07PM +0100, Conor Dooley wrote:
> On Tue, May 12, 2026 at 02:24:03PM +0800, lianfeng.ouyang wrote:
> > From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
> >
> > Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
> > ---
> > Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml b/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
> > index 4639247e9e51..11346d77b2f6 100644
> > --- a/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
> > +++ b/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
> > @@ -13,8 +13,8 @@ properties:
> > compatible:
> > oneOf:
> > - items:
> > - - const: starfive,jh8100-trng
> > - const: starfive,jh7110-trng
> > + - const: starfive,jhb100-trng
>
> You need to add a commit message here explaining why removing the jh8100
> is okay.
> pw-bot: changes-requested
Additionally, given the driver changes, it looks like using a jh7110
fallback is invalid anyway.
>
> > - const: starfive,jh7110-trng
> >
> > reg:
> > --
> > 2.43.0
> >
> >
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v1 1/2] dt-bindings: Add bindings for StarFive JHB100 SoC trng controller.
From: Conor Dooley @ 2026-05-12 17:15 UTC (permalink / raw)
To: lianfeng.ouyang
Cc: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, linux-crypto, devicetree,
linux-kernel
In-Reply-To: <20260512062404.4540-2-lianfeng.ouyang@starfivetech.com>
[-- Attachment #1: Type: text/plain, Size: 1083 bytes --]
On Tue, May 12, 2026 at 02:24:03PM +0800, lianfeng.ouyang wrote:
> From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
>
> Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
> ---
> Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml b/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
> index 4639247e9e51..11346d77b2f6 100644
> --- a/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
> +++ b/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
> @@ -13,8 +13,8 @@ properties:
> compatible:
> oneOf:
> - items:
> - - const: starfive,jh8100-trng
> - const: starfive,jh7110-trng
> + - const: starfive,jhb100-trng
You need to add a commit message here explaining why removing the jh8100
is okay.
pw-bot: changes-requested
> - const: starfive,jh7110-trng
>
> reg:
> --
> 2.43.0
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH v5 9/9] crypto: atmel: Use dmaengine_prep_config_single() API
From: Frank Li @ 2026-05-12 16:42 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
Using new API dmaengine_prep_config_single() to simple code.
No functional change.
Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/crypto/atmel-aes.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
index b393689400b4c17294cba73fcd16567fdd6687f4..d890b5a277b9c1394d2c7912bd663ff86321099f 100644
--- a/drivers/crypto/atmel-aes.c
+++ b/drivers/crypto/atmel-aes.c
@@ -795,7 +795,6 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
struct dma_slave_config config;
dma_async_tx_callback callback;
struct atmel_aes_dma *dma;
- int err;
memset(&config, 0, sizeof(config));
config.src_addr_width = addr_width;
@@ -820,12 +819,9 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
return -EINVAL;
}
- err = dmaengine_slave_config(dma->chan, &config);
- if (err)
- return err;
-
- desc = dmaengine_prep_slave_sg(dma->chan, dma->sg, dma->sg_len, dir,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ desc = dmaengine_prep_config_sg(dma->chan, dma->sg, dma->sg_len, dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
+ &config);
if (!desc)
return -ENOMEM;
--
2.43.0
^ permalink raw reply related
* [PATCH v5 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code
From: Frank Li @ 2026-05-12 16:42 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
Use dmaengine_prep_config_single() to simplify
pci_epf_mhi_edma_read[_sync]() and pci_epf_mhi_edma_write[_sync]().
No functional change.
Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Keep mutex lock because sync with other function.
---
drivers/pci/endpoint/functions/pci-epf-mhi.c | 52 +++++++++-------------------
1 file changed, 16 insertions(+), 36 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c
index 7f5326925ed54abf4ae75c465dfe0a9bab37ce40..c3e3b58fb86cd75e175b69ca45530610c500b99e 100644
--- a/drivers/pci/endpoint/functions/pci-epf-mhi.c
+++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c
@@ -328,12 +328,6 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl,
config.direction = DMA_DEV_TO_MEM;
config.src_addr = buf_info->host_addr;
- ret = dmaengine_slave_config(chan, &config);
- if (ret) {
- dev_err(dev, "Failed to configure DMA channel\n");
- goto err_unlock;
- }
-
dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
DMA_FROM_DEVICE);
ret = dma_mapping_error(dma_dev, dst_addr);
@@ -342,9 +336,10 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl,
goto err_unlock;
}
- desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size,
- DMA_DEV_TO_MEM,
- DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+ desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size,
+ DMA_DEV_TO_MEM,
+ DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
+ &config);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;
@@ -401,12 +396,6 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl,
config.direction = DMA_MEM_TO_DEV;
config.dst_addr = buf_info->host_addr;
- ret = dmaengine_slave_config(chan, &config);
- if (ret) {
- dev_err(dev, "Failed to configure DMA channel\n");
- goto err_unlock;
- }
-
src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
DMA_TO_DEVICE);
ret = dma_mapping_error(dma_dev, src_addr);
@@ -415,9 +404,10 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl,
goto err_unlock;
}
- desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size,
- DMA_MEM_TO_DEV,
- DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+ desc = dmaengine_prep_config_single(chan, src_addr, buf_info->size,
+ DMA_MEM_TO_DEV,
+ DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
+ &config);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;
@@ -506,12 +496,6 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
config.direction = DMA_DEV_TO_MEM;
config.src_addr = buf_info->host_addr;
- ret = dmaengine_slave_config(chan, &config);
- if (ret) {
- dev_err(dev, "Failed to configure DMA channel\n");
- goto err_unlock;
- }
-
dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
DMA_FROM_DEVICE);
ret = dma_mapping_error(dma_dev, dst_addr);
@@ -520,9 +504,10 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
goto err_unlock;
}
- desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size,
- DMA_DEV_TO_MEM,
- DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+ desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size,
+ DMA_DEV_TO_MEM,
+ DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
+ &config);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;
@@ -585,12 +570,6 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl,
config.direction = DMA_MEM_TO_DEV;
config.dst_addr = buf_info->host_addr;
- ret = dmaengine_slave_config(chan, &config);
- if (ret) {
- dev_err(dev, "Failed to configure DMA channel\n");
- goto err_unlock;
- }
-
src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
DMA_TO_DEVICE);
ret = dma_mapping_error(dma_dev, src_addr);
@@ -599,9 +578,10 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl,
goto err_unlock;
}
- desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size,
- DMA_MEM_TO_DEV,
- DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+ desc = dmaengine_prep_config_single(chan, src_addr, buf_info->size,
+ DMA_MEM_TO_DEV,
+ DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
+ &config);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;
--
2.43.0
^ permalink raw reply related
* [PATCH v5 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
From: Frank Li @ 2026-05-12 16:42 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
Use the new dmaengine_prep_config_single_safe() API to combine the
configuration and descriptor preparation into a single call.
Since dmaengine_prep_config_single_safe() performs the configuration and
preparation atomically and the mutex can be removed.
Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/nvme/target/pci-epf.c | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
index 2afe8f4d0e46104a1b3c98db3905cf33e8c9e011..04d8f48d6950349ca97d2dbeae4e38e4714ad0d4 100644
--- a/drivers/nvme/target/pci-epf.c
+++ b/drivers/nvme/target/pci-epf.c
@@ -388,22 +388,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
return -EINVAL;
}
- mutex_lock(lock);
-
dma_dev = dmaengine_get_dma_device(chan);
dma_addr = dma_map_single(dma_dev, seg->buf, seg->length, dir);
ret = dma_mapping_error(dma_dev, dma_addr);
if (ret)
- goto unlock;
-
- ret = dmaengine_slave_config(chan, &sconf);
- if (ret) {
- dev_err(dev, "Failed to configure DMA channel\n");
- goto unmap;
- }
+ return ret;
- desc = dmaengine_prep_slave_single(chan, dma_addr, seg->length,
- sconf.direction, DMA_CTRL_ACK);
+ desc = dmaengine_prep_config_single_safe(chan, dma_addr, seg->length,
+ sconf.direction,
+ DMA_CTRL_ACK, &sconf);
if (!desc) {
dev_err(dev, "Failed to prepare DMA\n");
ret = -EIO;
@@ -426,9 +419,6 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
unmap:
dma_unmap_single(dma_dev, dma_addr, seg->length, dir);
-unlock:
- mutex_unlock(lock);
-
return ret;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v5 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer
From: Frank Li @ 2026-05-12 16:42 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li,
Damien Le Moal
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
dmaengine_terminate_sync() cancels all pending requests. Calling it for
every DMA transfer is unnecessary and counterproductive. This function is
generally intended for cleanup paths such as module removal, device close,
or unbind operations.
Remove the redundant calls for success path and keep it only at error path.
Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
This one also fix stress test failure after remove mutex and use new API
dmaengine_prep_slave_sg_config().
---
drivers/nvme/target/pci-epf.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
index 4e9db96ebfecd796244e5dc67c23e1abb1a14974..2afe8f4d0e46104a1b3c98db3905cf33e8c9e011 100644
--- a/drivers/nvme/target/pci-epf.c
+++ b/drivers/nvme/target/pci-epf.c
@@ -420,10 +420,9 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
if (dma_sync_wait(chan, cookie) != DMA_COMPLETE) {
dev_err(dev, "DMA transfer failed\n");
ret = -EIO;
+ dmaengine_terminate_sync(chan);
}
- dmaengine_terminate_sync(chan);
-
unmap:
dma_unmap_single(dma_dev, dma_addr, seg->length, dir);
--
2.43.0
^ permalink raw reply related
* [PATCH v5 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()
From: Frank Li @ 2026-05-12 16:42 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
Pass dma_slave_config to dw_edma_device_transfer() to support atomic
configuration and descriptor preparation when a non-NULL config is
provided to device_prep_config_sg().
Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v3
- rewrite dw_edma_device_slave_config() according to Damien's suggestion.
---
drivers/dma/dw-edma/dw-edma-core.c | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index f7f58b0010e26b529ffb7382d5b166a703587c71..ec6f6b1e482568a27ebe90852d5679672b24a1e9 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -267,6 +267,20 @@ static int dw_edma_device_config(struct dma_chan *dchan,
return 0;
}
+static struct dma_slave_config *
+dw_edma_device_get_config(struct dma_chan *dchan,
+ struct dma_slave_config *config)
+{
+ struct dw_edma_chan *chan;
+
+ if (config)
+ return config;
+
+ chan = dchan2dw_edma_chan(dchan);
+
+ return &chan->config;
+}
+
static int dw_edma_device_pause(struct dma_chan *dchan)
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
@@ -385,7 +399,8 @@ dw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
}
static struct dma_async_tx_descriptor *
-dw_edma_device_transfer(struct dw_edma_transfer *xfer)
+dw_edma_device_transfer(struct dw_edma_transfer *xfer,
+ struct dma_slave_config *config)
{
struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
enum dma_transfer_direction dir = xfer->direction;
@@ -472,8 +487,8 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
src_addr = xfer->xfer.il->src_start;
dst_addr = xfer->xfer.il->dst_start;
} else {
- src_addr = chan->config.src_addr;
- dst_addr = chan->config.dst_addr;
+ src_addr = config->src_addr;
+ dst_addr = config->dst_addr;
}
if (dir == DMA_DEV_TO_MEM)
@@ -595,7 +610,7 @@ dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
if (config)
dw_edma_device_config(dchan, config);
- return dw_edma_device_transfer(&xfer);
+ return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, config));
}
static struct dma_async_tx_descriptor *
@@ -614,7 +629,7 @@ dw_edma_device_prep_dma_cyclic(struct dma_chan *dchan, dma_addr_t paddr,
xfer.flags = flags;
xfer.type = EDMA_XFER_CYCLIC;
- return dw_edma_device_transfer(&xfer);
+ return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL));
}
static struct dma_async_tx_descriptor *
@@ -630,7 +645,7 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
xfer.flags = flags;
xfer.type = EDMA_XFER_INTERLEAVED;
- return dw_edma_device_transfer(&xfer);
+ return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL));
}
static void dw_hdma_set_callback_result(struct virt_dma_desc *vd,
--
2.43.0
^ permalink raw reply related
* [PATCH v5 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback
From: Frank Li @ 2026-05-12 16:42 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li,
Damien Le Moal
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
Use the new .device_prep_config_sg() callback to combine configuration and
descriptor preparation.
No functional changes.
Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v4
- drop context in callback.
change in v3
- add Damien Le Moal review tag
---
drivers/dma/dw-edma/dw-edma-core.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index c2feb3adc79fa94b016913443305b9fae9deef12..f7f58b0010e26b529ffb7382d5b166a703587c71 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -577,10 +577,11 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
}
static struct dma_async_tx_descriptor *
-dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
- unsigned int len,
- enum dma_transfer_direction direction,
- unsigned long flags, void *context)
+dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
+ unsigned int len,
+ enum dma_transfer_direction direction,
+ unsigned long flags,
+ struct dma_slave_config *config)
{
struct dw_edma_transfer xfer;
@@ -591,6 +592,9 @@ dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
xfer.flags = flags;
xfer.type = EDMA_XFER_SCATTER_GATHER;
+ if (config)
+ dw_edma_device_config(dchan, config);
+
return dw_edma_device_transfer(&xfer);
}
@@ -970,7 +974,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
dma->device_terminate_all = dw_edma_device_terminate_all;
dma->device_issue_pending = dw_edma_device_issue_pending;
dma->device_tx_status = dw_edma_device_tx_status;
- dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg;
+ dma->device_prep_config_sg = dw_edma_device_prep_config_sg;
dma->device_prep_dma_cyclic = dw_edma_device_prep_dma_cyclic;
dma->device_prep_interleaved_dma = dw_edma_device_prep_interleaved_dma;
--
2.43.0
^ permalink raw reply related
* [PATCH v5 3/9] PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code
From: Frank Li @ 2026-05-12 16:42 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li,
Damien Le Moal
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
Use dmaenigne_prep_config_single() to simplify code.
No functional change.
Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v3
- add Damien Le Moal review tag
---
drivers/pci/endpoint/functions/pci-epf-test.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 591d301fa89d89addf5df16e775e80460b689589..0f5cf2d7951088af3801ea1cc240b2ea8627eed5 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -182,12 +182,8 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test,
else
sconf.src_addr = dma_remote;
- if (dmaengine_slave_config(chan, &sconf)) {
- dev_err(dev, "DMA slave config fail\n");
- return -EIO;
- }
- tx = dmaengine_prep_slave_single(chan, dma_local, len, dir,
- flags);
+ tx = dmaengine_prep_config_single(chan, dma_local, len,
+ dir, flags, &sconf);
} else {
tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len,
flags);
--
2.43.0
^ permalink raw reply related
* [PATCH v5 2/9] dmaengine: Add safe API to combine configuration and preparation
From: Frank Li @ 2026-05-12 16:42 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
Introduce dmaengine_prep_config_single_safe() and
dmaengine_prep_config_sg_safe() to provide a reentrant-safe way to
combine slave configuration and transfer preparation.
Drivers may implement the new device_prep_config_sg() callback to perform
both steps atomically. If the callback is not provided, the helpers fall
back to calling dmaengine_slave_config() followed by
dmaengine_prep_slave_sg() under per-channel mutex protection.
Tested-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
chagne in v5
- remove reduntant lock commments.
- use kernel doc to descritp API
chagne in v4
- use spinlock() to protect config() and prep()
change in v3
- new patch
---
drivers/dma/dmaengine.c | 2 ++
include/linux/dmaengine.h | 85 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 87 insertions(+)
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 405bd2fbb4a3b94fd0bf44526f656f6a19feaad0..ba29e60160c1a0148793bb299849bccfebb6d32b 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -1099,6 +1099,8 @@ static int __dma_async_device_channel_register(struct dma_device *device,
chan->dev->device.parent = device->dev;
chan->dev->chan = chan;
chan->dev->dev_id = device->dev_id;
+ spin_lock_init(&chan->lock);
+
if (!name)
dev_set_name(&chan->dev->device, "dma%dchan%d", device->dev_id, chan->chan_id);
else
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index defa377d2ef54d94e6337cdfa7826a091295535e..83e8547de89bf56424f048c316bdc8d798791e25 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -322,6 +322,8 @@ struct dma_router {
* @slave: ptr to the device using this channel
* @cookie: last cookie value returned to client
* @completed_cookie: last completed cookie for this channel
+ * @lock: protect between config and prepare transfer when driver have not
+ * implemented callback device_prep_config_sg().
* @chan_id: channel ID for sysfs
* @dev: class device for sysfs
* @name: backlink name for sysfs
@@ -341,6 +343,12 @@ struct dma_chan {
dma_cookie_t cookie;
dma_cookie_t completed_cookie;
+ /*
+ * protect between config and prepare transfer because *_prep() may be
+ * called from complete callback, which is in GFP_NOSLEEP context.
+ */
+ spinlock_t lock;
+
/* sysfs */
int chan_id;
struct dma_chan_dev *dev;
@@ -1068,6 +1076,83 @@ dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL);
}
+/**
+ * dmaengine_prep_config_sg_safe - prepare a scatter-gather DMA transfer
+ * with atomic slave configuration update
+ * @chan: DMA channel
+ * @sgl: scatterlist for the transfer
+ * @sg_len: number of entries in @sgl
+ * @dir: DMA transfer direction
+ * @flags: transfer preparation flags
+ * @config: DMA slave configuration for this transfer
+ *
+ * Prepare a DMA scatter-gather transfer together with a corresponding slave
+ * configuration update in a re-entrant and race-safe manner.
+ *
+ * DMA engine drivers may implement the optional
+ * device_prep_config_sg() callback to perform both the slave configuration
+ * and descriptor preparation atomically. In this case, the operation is
+ * fully handled by the DMA engine driver.
+ *
+ * If the DMA engine driver does not implement device_prep_config_sg(), falls
+ * back to calling dmaengine_slave_config() followed by dmaengine_prep_slave_sg().
+ * The fallback path is protected by a per-channel spinlock to ensure that
+ * concurrent callers cannot interleave configuration and descriptor preparation
+ * on the same DMA channel.
+ *
+ * Return: Pointer to a prepared DMA async transaction descriptor on success,
+ * or %NULL if the transfer could not be prepared.
+ */
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_sg_safe(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len,
+ enum dma_transfer_direction dir,
+ unsigned long flags,
+ struct dma_slave_config *config)
+{
+ struct dma_async_tx_descriptor *tx;
+
+ if (!chan || !chan->device)
+ return NULL;
+
+ if (!chan->device->device_prep_config_sg)
+ spin_lock(&chan->lock);
+
+ tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config);
+
+ if (!chan->device->device_prep_config_sg)
+ spin_unlock(&chan->lock);
+
+ return tx;
+}
+
+/**
+ * dmaengine_prep_config_single_safe - prepare a single-buffer DMA transfer
+ * with atomic slave configuration update
+ * @chan: DMA channel
+ * @buf: DMA buffer address
+ * @len: length of the transfer in bytes
+ * @dir: DMA transfer direction
+ * @flags: transfer preparation flags
+ * @config: DMA slave configuration for this transfer
+ *
+ * Detail see dmaengine_prep_config_sg_safe().
+ */
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_single_safe(struct dma_chan *chan, dma_addr_t buf,
+ size_t len, enum dma_transfer_direction dir,
+ unsigned long flags,
+ struct dma_slave_config *config)
+{
+ struct scatterlist sg;
+
+ sg_init_table(&sg, 1);
+ sg_dma_address(&sg) = buf;
+ sg_dma_len(&sg) = len;
+
+ return dmaengine_prep_config_sg_safe(chan, &sg, 1, dir, flags, config);
+}
+
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
struct rio_dma_ext;
static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(
--
2.43.0
^ permalink raw reply related
* [PATCH v5 1/9] dmaengine: Add API to combine configuration and preparation (sg and single)
From: Frank Li @ 2026-05-12 16:41 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
Previously, configuration and preparation required two separate calls. This
works well when configuration is done only once during initialization.
However, in cases where the burst length or source/destination address must
be adjusted for each transfer, calling two functions is verbose and
requires additional locking to ensure both steps complete atomically.
Add a new API dmaengine_prep_config_single() and dmaengine_prep_config_sg()
and callback device_prep_config_sg() that combines configuration and
preparation into a single operation. If the configuration argument is
passed as NULL, fall back to the existing implementation.
Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v4
- drop context in device_prep_config_sg()
change in v3
- remove Deprecated for callback device_prep_slave_sg().
- Move condition check before sg init.
- split function at return type.
- move safe version to next patch
change in v2
- add () for function
- use short name device_prep_sg(), remove "slave" and "config". the 'slave'
is reduntant. after remove slave, the function name is difference existed
one, so remove _config suffix.
---
Documentation/driver-api/dmaengine/client.rst | 9 ++++
include/linux/dmaengine.h | 63 +++++++++++++++++++++++----
2 files changed, 64 insertions(+), 8 deletions(-)
diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst
index d491e385d61a98b8a804cd823caf254a2dc62cf4..5ee5d4a3596dd986b02f1bce3078ca6c4c1fb45a 100644
--- a/Documentation/driver-api/dmaengine/client.rst
+++ b/Documentation/driver-api/dmaengine/client.rst
@@ -80,6 +80,10 @@ The details of these operations are:
- slave_sg: DMA a list of scatter gather buffers from/to a peripheral
+ - config_sg: Similar with slave_sg, just pass down dma_slave_config
+ struct to avoid calling dmaengine_slave_config() every time adjusting the
+ burst length or the FIFO address is needed.
+
- peripheral_dma_vec: DMA an array of scatter gather buffers from/to a
peripheral. Similar to slave_sg, but uses an array of dma_vec
structures instead of a scatterlist.
@@ -106,6 +110,11 @@ The details of these operations are:
unsigned int sg_len, enum dma_data_direction direction,
unsigned long flags);
+ struct dma_async_tx_descriptor *dmaengine_prep_config_sg(
+ struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction dir,
+ unsigned long flags, struct dma_slave_config *config);
+
struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
struct dma_chan *chan, const struct dma_vec *vecs,
size_t nents, enum dma_data_direction direction,
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index b3d251c9734e95e1b75cf6763d4d2c3a1c6a9910..defa377d2ef54d94e6337cdfa7826a091295535e 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -835,6 +835,7 @@ struct dma_filter {
* where the address and size of each segment is located in one entry of
* the dma_vec array.
* @device_prep_slave_sg: prepares a slave dma operation
+ * @device_prep_config_sg: prepares a slave DMA operation with dma_slave_config
* @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio.
* The function takes a buffer of size buf_len. The callback function will
* be called after period_len bytes have been transferred.
@@ -934,6 +935,10 @@ struct dma_device {
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
unsigned long flags, void *context);
+ struct dma_async_tx_descriptor *(*device_prep_config_sg)(
+ struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, struct dma_slave_config *config);
struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
@@ -974,22 +979,44 @@ static inline bool is_slave_direction(enum dma_transfer_direction direction)
(direction == DMA_DEV_TO_DEV);
}
-static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
- struct dma_chan *chan, dma_addr_t buf, size_t len,
- enum dma_transfer_direction dir, unsigned long flags)
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
+ enum dma_transfer_direction dir,
+ unsigned long flags,
+ struct dma_slave_config *config)
{
struct scatterlist sg;
+
+ if (!chan || !chan->device)
+ return NULL;
+
sg_init_table(&sg, 1);
sg_dma_address(&sg) = buf;
sg_dma_len(&sg) = len;
- if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
+ if (chan->device->device_prep_config_sg)
+ return chan->device->device_prep_config_sg(chan, &sg, 1, dir,
+ flags, config);
+
+ if (config)
+ if (dmaengine_slave_config(chan, config))
+ return NULL;
+
+ if (!chan->device->device_prep_slave_sg)
return NULL;
return chan->device->device_prep_slave_sg(chan, &sg, 1,
dir, flags, NULL);
}
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_slave_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
+ enum dma_transfer_direction dir,
+ unsigned long flags)
+{
+ return dmaengine_prep_config_single(chan, buf, len, dir, flags, NULL);
+}
+
/**
* dmaengine_prep_peripheral_dma_vec() - Prepare a DMA scatter-gather descriptor
* @chan: The channel to be used for this descriptor
@@ -1010,17 +1037,37 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
dir, flags);
}
-static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
- struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
- enum dma_transfer_direction dir, unsigned long flags)
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_sg(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction dir,
+ unsigned long flags, struct dma_slave_config *config)
{
- if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
+ if (!chan || !chan->device)
+ return NULL;
+
+ if (chan->device->device_prep_config_sg)
+ return chan->device->device_prep_config_sg(chan, sgl, sg_len,
+ dir, flags, config);
+
+ if (config)
+ if (dmaengine_slave_config(chan, config))
+ return NULL;
+
+ if (!chan->device->device_prep_slave_sg)
return NULL;
return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
dir, flags, NULL);
}
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction dir,
+ unsigned long flags)
+{
+ return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL);
+}
+
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
struct rio_dma_ext;
static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox