File Index
Files and directories
Sign in
FILE

airoha-trng.c

File preview with syntax highlighting.

linux-6.19.14/drivers/char/hw_random/airoha-trng.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (C) 2024 Christian Marangi */
  3
  4#include <linux/kernel.h>
  5#include <linux/module.h>
  6#include <linux/mod_devicetable.h>
  7#include <linux/bitfield.h>
  8#include <linux/delay.h>
  9#include <linux/hw_random.h>
 10#include <linux/interrupt.h>
 11#include <linux/io.h>
 12#include <linux/iopoll.h>
 13#include <linux/platform_device.h>
 14
 15#define TRNG_IP_RDY			0x800
 16#define   CNT_TRANS			GENMASK(15, 8)
 17#define   SAMPLE_RDY			BIT(0)
 18#define TRNG_NS_SEK_AND_DAT_EN		0x804
 19#define	  RNG_EN			BIT(31) /* referenced as ring_en */
 20#define	  RAW_DATA_EN			BIT(16)
 21#define TRNG_HEALTH_TEST_SW_RST		0x808
 22#define   SW_RST			BIT(0) /* Active High */
 23#define TRNG_INTR_EN			0x818
 24#define   INTR_MASK			BIT(16)
 25#define   CONTINUOUS_HEALTH_INITR_EN	BIT(2)
 26#define   SW_STARTUP_INITR_EN		BIT(1)
 27#define   RST_STARTUP_INITR_EN		BIT(0)
 28/* Notice that Health Test are done only out of Reset and with RNG_EN */
 29#define TRNG_HEALTH_TEST_STATUS		0x824
 30#define   CONTINUOUS_HEALTH_AP_TEST_FAIL BIT(23)
 31#define   CONTINUOUS_HEALTH_RC_TEST_FAIL BIT(22)
 32#define   SW_STARTUP_TEST_DONE		BIT(21)
 33#define   SW_STARTUP_AP_TEST_FAIL	BIT(20)
 34#define   SW_STARTUP_RC_TEST_FAIL	BIT(19)
 35#define   RST_STARTUP_TEST_DONE		BIT(18)
 36#define   RST_STARTUP_AP_TEST_FAIL	BIT(17)
 37#define   RST_STARTUP_RC_TEST_FAIL	BIT(16)
 38#define   RAW_DATA_VALID		BIT(7)
 39
 40#define TRNG_RAW_DATA_OUT		0x828
 41
 42#define TRNG_CNT_TRANS_VALID		0x80
 43#define BUSY_LOOP_SLEEP			10
 44#define BUSY_LOOP_TIMEOUT		(BUSY_LOOP_SLEEP * 10000)
 45
 46struct airoha_trng {
 47	void __iomem *base;
 48	struct hwrng rng;
 49	struct device *dev;
 50
 51	struct completion rng_op_done;
 52};
 53
 54static int airoha_trng_irq_mask(struct airoha_trng *trng)
 55{
 56	u32 val;
 57
 58	val = readl(trng->base + TRNG_INTR_EN);
 59	val |= INTR_MASK;
 60	writel(val, trng->base + TRNG_INTR_EN);
 61
 62	return 0;
 63}
 64
 65static int airoha_trng_irq_unmask(struct airoha_trng *trng)
 66{
 67	u32 val;
 68
 69	val = readl(trng->base + TRNG_INTR_EN);
 70	val &= ~INTR_MASK;
 71	writel(val, trng->base + TRNG_INTR_EN);
 72
 73	return 0;
 74}
 75
 76static int airoha_trng_init(struct hwrng *rng)
 77{
 78	struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng);
 79	int ret;
 80	u32 val;
 81
 82	val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN);
 83	val |= RNG_EN;
 84	writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN);
 85
 86	/* Set out of SW Reset */
 87	airoha_trng_irq_unmask(trng);
 88	writel(0, trng->base + TRNG_HEALTH_TEST_SW_RST);
 89
 90	ret = wait_for_completion_timeout(&trng->rng_op_done, BUSY_LOOP_TIMEOUT);
 91	if (ret <= 0) {
 92		dev_err(trng->dev, "Timeout waiting for Health Check\n");
 93		airoha_trng_irq_mask(trng);
 94		return -ENODEV;
 95	}
 96
 97	/* Check if Health Test Failed */
 98	val = readl(trng->base + TRNG_HEALTH_TEST_STATUS);
 99	if (val & (RST_STARTUP_AP_TEST_FAIL | RST_STARTUP_RC_TEST_FAIL)) {
100		dev_err(trng->dev, "Health Check fail: %s test fail\n",
101			val & RST_STARTUP_AP_TEST_FAIL ? "AP" : "RC");
102		return -ENODEV;
103	}
104
105	/* Check if IP is ready */
106	ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val,
107				 val & SAMPLE_RDY, 10, 1000);
108	if (ret < 0) {
109		dev_err(trng->dev, "Timeout waiting for IP ready");
110		return -ENODEV;
111	}
112
113	/* CNT_TRANS must be 0x80 for IP to be considered ready */
114	ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val,
115				 FIELD_GET(CNT_TRANS, val) == TRNG_CNT_TRANS_VALID,
116				 10, 1000);
117	if (ret < 0) {
118		dev_err(trng->dev, "Timeout waiting for IP ready");
119		return -ENODEV;
120	}
121
122	return 0;
123}
124
125static void airoha_trng_cleanup(struct hwrng *rng)
126{
127	struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng);
128	u32 val;
129
130	val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN);
131	val &= ~RNG_EN;
132	writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN);
133
134	/* Put it in SW Reset */
135	writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST);
136}
137
138static int airoha_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
139{
140	struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng);
141	u32 *data = buf;
142	u32 status;
143	int ret;
144
145	ret = readl_poll_timeout(trng->base + TRNG_HEALTH_TEST_STATUS, status,
146				 status & RAW_DATA_VALID, 10, 1000);
147	if (ret < 0) {
148		dev_err(trng->dev, "Timeout waiting for TRNG RAW Data valid\n");
149		return ret;
150	}
151
152	*data = readl(trng->base + TRNG_RAW_DATA_OUT);
153
154	return 4;
155}
156
157static irqreturn_t airoha_trng_irq(int irq, void *priv)
158{
159	struct airoha_trng *trng = (struct airoha_trng *)priv;
160
161	airoha_trng_irq_mask(trng);
162	/* Just complete the task, we will read the value later */
163	complete(&trng->rng_op_done);
164
165	return IRQ_HANDLED;
166}
167
168static int airoha_trng_probe(struct platform_device *pdev)
169{
170	struct device *dev = &pdev->dev;
171	struct airoha_trng *trng;
172	int irq, ret;
173	u32 val;
174
175	trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL);
176	if (!trng)
177		return -ENOMEM;
178
179	trng->base = devm_platform_ioremap_resource(pdev, 0);
180	if (IS_ERR(trng->base))
181		return PTR_ERR(trng->base);
182
183	irq = platform_get_irq(pdev, 0);
184	if (irq < 0)
185		return irq;
186
187	airoha_trng_irq_mask(trng);
188	ret = devm_request_irq(&pdev->dev, irq, airoha_trng_irq, 0,
189			       pdev->name, (void *)trng);
190	if (ret) {
191		dev_err(dev, "Can't get interrupt working.\n");
192		return ret;
193	}
194
195	init_completion(&trng->rng_op_done);
196
197	/* Enable interrupt for SW reset Health Check */
198	val = readl(trng->base + TRNG_INTR_EN);
199	val |= RST_STARTUP_INITR_EN;
200	writel(val, trng->base + TRNG_INTR_EN);
201
202	/* Set output to raw data */
203	val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN);
204	val |= RAW_DATA_EN;
205	writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN);
206
207	/* Put it in SW Reset */
208	writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST);
209
210	trng->dev = dev;
211	trng->rng.name = pdev->name;
212	trng->rng.init = airoha_trng_init;
213	trng->rng.cleanup = airoha_trng_cleanup;
214	trng->rng.read = airoha_trng_read;
215	trng->rng.quality = 900;
216
217	ret = devm_hwrng_register(dev, &trng->rng);
218	if (ret) {
219		dev_err(dev, "failed to register rng device: %d\n", ret);
220		return ret;
221	}
222
223	return 0;
224}
225
226static const struct of_device_id airoha_trng_of_match[] = {
227	{ .compatible = "airoha,en7581-trng", },
228	{},
229};
230MODULE_DEVICE_TABLE(of, airoha_trng_of_match);
231
232static struct platform_driver airoha_trng_driver = {
233	.driver = {
234		.name = "airoha-trng",
235		.of_match_table	= airoha_trng_of_match,
236	},
237	.probe = airoha_trng_probe,
238};
239
240module_platform_driver(airoha_trng_driver);
241
242MODULE_LICENSE("GPL");
243MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
244MODULE_DESCRIPTION("Airoha True Random Number Generator driver");
~/ Read-only file preview.