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