Skip to content

Commit da001fb

Browse files
Ard Biesheuvelherbertx
authored andcommitted
crypto: atmel-i2c - add support for SHA204A random number generator
The Linaro/96boards Secure96 mezzanine contains (among other things) an Atmel SHA204A symmetric crypto processor. This chip implements a number of different functionalities, but one that is highly useful for many different 96boards platforms is the random number generator. So let's implement a driver for the SHA204A, and for the time being, implement support for the random number generator only. Reviewed-by: Linus Walleij <[email protected]> Signed-off-by: Ard Biesheuvel <[email protected]> Signed-off-by: Herbert Xu <[email protected]>
1 parent c34a320 commit da001fb

File tree

5 files changed

+211
-0
lines changed

5 files changed

+211
-0
lines changed

drivers/crypto/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,20 @@ config CRYPTO_DEV_ATMEL_ECC
536536
To compile this driver as a module, choose M here: the module
537537
will be called atmel-ecc.
538538

539+
config CRYPTO_DEV_ATMEL_SHA204A
540+
tristate "Support for Microchip / Atmel SHA accelerator and RNG"
541+
depends on I2C
542+
select CRYPTO_DEV_ATMEL_I2C
543+
select HW_RANDOM
544+
help
545+
Microhip / Atmel SHA accelerator and RNG.
546+
Select this if you want to use the Microchip / Atmel SHA204A
547+
module as a random number generator. (Other functions of the
548+
chip are currently not exposed by this driver)
549+
550+
To compile this driver as a module, choose M here: the module
551+
will be called atmel-sha204a.
552+
539553
config CRYPTO_DEV_CCP
540554
bool "Support for AMD Secure Processor"
541555
depends on ((X86 && PCI) || (ARM64 && (OF_ADDRESS || ACPI))) && HAS_IOMEM

drivers/crypto/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA) += atmel-sha.o
44
obj-$(CONFIG_CRYPTO_DEV_ATMEL_TDES) += atmel-tdes.o
55
obj-$(CONFIG_CRYPTO_DEV_ATMEL_I2C) += atmel-i2c.o
66
obj-$(CONFIG_CRYPTO_DEV_ATMEL_ECC) += atmel-ecc.o
7+
obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA204A) += atmel-sha204a.o
78
obj-$(CONFIG_CRYPTO_DEV_CAVIUM_ZIP) += cavium/
89
obj-$(CONFIG_CRYPTO_DEV_CCP) += ccp/
910
obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/

drivers/crypto/atmel-i2c.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,21 @@ void atmel_i2c_init_read_cmd(struct atmel_i2c_cmd *cmd)
5858
}
5959
EXPORT_SYMBOL(atmel_i2c_init_read_cmd);
6060

61+
void atmel_i2c_init_random_cmd(struct atmel_i2c_cmd *cmd)
62+
{
63+
cmd->word_addr = COMMAND;
64+
cmd->opcode = OPCODE_RANDOM;
65+
cmd->param1 = 0;
66+
cmd->param2 = 0;
67+
cmd->count = RANDOM_COUNT;
68+
69+
atmel_i2c_checksum(cmd);
70+
71+
cmd->msecs = MAX_EXEC_TIME_RANDOM;
72+
cmd->rxsize = RANDOM_RSP_SIZE;
73+
}
74+
EXPORT_SYMBOL(atmel_i2c_init_random_cmd);
75+
6176
void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid)
6277
{
6378
cmd->word_addr = COMMAND;

drivers/crypto/atmel-i2c.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#ifndef __ATMEL_I2C_H__
88
#define __ATMEL_I2C_H__
99

10+
#include <linux/hw_random.h>
11+
1012
#define ATMEL_ECC_PRIORITY 300
1113

1214
#define COMMAND 0x03 /* packet function */
@@ -28,6 +30,7 @@
2830
#define GENKEY_RSP_SIZE (ATMEL_ECC_PUBKEY_SIZE + \
2931
CMD_OVERHEAD_SIZE)
3032
#define READ_RSP_SIZE (4 + CMD_OVERHEAD_SIZE)
33+
#define RANDOM_RSP_SIZE (32 + CMD_OVERHEAD_SIZE)
3134
#define MAX_RSP_SIZE GENKEY_RSP_SIZE
3235

3336
/**
@@ -96,15 +99,20 @@ static const struct {
9699
#define MAX_EXEC_TIME_ECDH 58
97100
#define MAX_EXEC_TIME_GENKEY 115
98101
#define MAX_EXEC_TIME_READ 1
102+
#define MAX_EXEC_TIME_RANDOM 50
99103

100104
/* Command opcode */
101105
#define OPCODE_ECDH 0x43
102106
#define OPCODE_GENKEY 0x40
103107
#define OPCODE_READ 0x02
108+
#define OPCODE_RANDOM 0x1b
104109

105110
/* Definitions for the READ Command */
106111
#define READ_COUNT 7
107112

113+
/* Definitions for the RANDOM Command */
114+
#define RANDOM_COUNT 7
115+
108116
/* Definitions for the GenKey Command */
109117
#define GENKEY_COUNT 7
110118
#define GENKEY_MODE_PRIVATE 0x04
@@ -142,6 +150,7 @@ struct atmel_i2c_client_priv {
142150
u8 wake_token[WAKE_TOKEN_MAX_SIZE];
143151
size_t wake_token_sz;
144152
atomic_t tfm_count ____cacheline_aligned;
153+
struct hwrng hwrng;
145154
};
146155

147156
/**
@@ -179,6 +188,7 @@ void atmel_i2c_enqueue(struct atmel_i2c_work_data *work_data,
179188
int atmel_i2c_send_receive(struct i2c_client *client, struct atmel_i2c_cmd *cmd);
180189

181190
void atmel_i2c_init_read_cmd(struct atmel_i2c_cmd *cmd);
191+
void atmel_i2c_init_random_cmd(struct atmel_i2c_cmd *cmd);
182192
void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid);
183193
int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
184194
struct scatterlist *pubkey);

drivers/crypto/atmel-sha204a.c

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Microchip / Atmel SHA204A (I2C) driver.
4+
*
5+
* Copyright (c) 2019 Linaro, Ltd. <[email protected]>
6+
*/
7+
8+
#include <linux/delay.h>
9+
#include <linux/device.h>
10+
#include <linux/err.h>
11+
#include <linux/errno.h>
12+
#include <linux/i2c.h>
13+
#include <linux/init.h>
14+
#include <linux/kernel.h>
15+
#include <linux/module.h>
16+
#include <linux/scatterlist.h>
17+
#include <linux/slab.h>
18+
#include <linux/workqueue.h>
19+
#include "atmel-i2c.h"
20+
21+
static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data,
22+
void *areq, int status)
23+
{
24+
struct atmel_i2c_client_priv *i2c_priv = work_data->ctx;
25+
struct hwrng *rng = areq;
26+
27+
if (status)
28+
dev_warn_ratelimited(&i2c_priv->client->dev,
29+
"i2c transaction failed (%d)\n",
30+
status);
31+
32+
rng->priv = (unsigned long)work_data;
33+
atomic_dec(&i2c_priv->tfm_count);
34+
}
35+
36+
static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data,
37+
size_t max)
38+
{
39+
struct atmel_i2c_client_priv *i2c_priv;
40+
struct atmel_i2c_work_data *work_data;
41+
42+
i2c_priv = container_of(rng, struct atmel_i2c_client_priv, hwrng);
43+
44+
/* keep maximum 1 asynchronous read in flight at any time */
45+
if (!atomic_add_unless(&i2c_priv->tfm_count, 1, 1))
46+
return 0;
47+
48+
if (rng->priv) {
49+
work_data = (struct atmel_i2c_work_data *)rng->priv;
50+
max = min(sizeof(work_data->cmd.data), max);
51+
memcpy(data, &work_data->cmd.data, max);
52+
rng->priv = 0;
53+
} else {
54+
work_data = kmalloc(sizeof(*work_data), GFP_ATOMIC);
55+
if (!work_data)
56+
return -ENOMEM;
57+
58+
work_data->ctx = i2c_priv;
59+
work_data->client = i2c_priv->client;
60+
61+
max = 0;
62+
}
63+
64+
atmel_i2c_init_random_cmd(&work_data->cmd);
65+
atmel_i2c_enqueue(work_data, atmel_sha204a_rng_done, rng);
66+
67+
return max;
68+
}
69+
70+
static int atmel_sha204a_rng_read(struct hwrng *rng, void *data, size_t max,
71+
bool wait)
72+
{
73+
struct atmel_i2c_client_priv *i2c_priv;
74+
struct atmel_i2c_cmd cmd;
75+
int ret;
76+
77+
if (!wait)
78+
return atmel_sha204a_rng_read_nonblocking(rng, data, max);
79+
80+
i2c_priv = container_of(rng, struct atmel_i2c_client_priv, hwrng);
81+
82+
atmel_i2c_init_random_cmd(&cmd);
83+
84+
ret = atmel_i2c_send_receive(i2c_priv->client, &cmd);
85+
if (ret)
86+
return ret;
87+
88+
max = min(sizeof(cmd.data), max);
89+
memcpy(data, cmd.data, max);
90+
91+
return max;
92+
}
93+
94+
static int atmel_sha204a_probe(struct i2c_client *client,
95+
const struct i2c_device_id *id)
96+
{
97+
struct atmel_i2c_client_priv *i2c_priv;
98+
int ret;
99+
100+
ret = atmel_i2c_probe(client, id);
101+
if (ret)
102+
return ret;
103+
104+
i2c_priv = i2c_get_clientdata(client);
105+
106+
memset(&i2c_priv->hwrng, 0, sizeof(i2c_priv->hwrng));
107+
108+
i2c_priv->hwrng.name = dev_name(&client->dev);
109+
i2c_priv->hwrng.read = atmel_sha204a_rng_read;
110+
i2c_priv->hwrng.quality = 1024;
111+
112+
ret = hwrng_register(&i2c_priv->hwrng);
113+
if (ret)
114+
dev_warn(&client->dev, "failed to register RNG (%d)\n", ret);
115+
116+
return ret;
117+
}
118+
119+
static int atmel_sha204a_remove(struct i2c_client *client)
120+
{
121+
struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
122+
123+
if (atomic_read(&i2c_priv->tfm_count)) {
124+
dev_err(&client->dev, "Device is busy\n");
125+
return -EBUSY;
126+
}
127+
128+
if (i2c_priv->hwrng.priv)
129+
kfree((void *)i2c_priv->hwrng.priv);
130+
hwrng_unregister(&i2c_priv->hwrng);
131+
132+
return 0;
133+
}
134+
135+
static const struct of_device_id atmel_sha204a_dt_ids[] = {
136+
{ .compatible = "atmel,atsha204a", },
137+
{ /* sentinel */ }
138+
};
139+
MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids);
140+
141+
static const struct i2c_device_id atmel_sha204a_id[] = {
142+
{ "atsha204a", 0 },
143+
{ /* sentinel */ }
144+
};
145+
MODULE_DEVICE_TABLE(i2c, atmel_sha204a_id);
146+
147+
static struct i2c_driver atmel_sha204a_driver = {
148+
.probe = atmel_sha204a_probe,
149+
.remove = atmel_sha204a_remove,
150+
.id_table = atmel_sha204a_id,
151+
152+
.driver.name = "atmel-sha204a",
153+
.driver.of_match_table = of_match_ptr(atmel_sha204a_dt_ids),
154+
};
155+
156+
static int __init atmel_sha204a_init(void)
157+
{
158+
return i2c_add_driver(&atmel_sha204a_driver);
159+
}
160+
161+
static void __exit atmel_sha204a_exit(void)
162+
{
163+
flush_scheduled_work();
164+
i2c_del_driver(&atmel_sha204a_driver);
165+
}
166+
167+
module_init(atmel_sha204a_init);
168+
module_exit(atmel_sha204a_exit);
169+
170+
MODULE_AUTHOR("Ard Biesheuvel <[email protected]>");
171+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)