1 /*
2  * Copyright (c) 2021 Henrik Brix Andersen <henrik@brixandersen.dk>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT neorv32_trng
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/syscon.h>
11 #include <zephyr/drivers/entropy.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/pm/device.h>
14 #include <zephyr/sys/sys_io.h>
15 
16 #include <soc.h>
17 
18 LOG_MODULE_REGISTER(neorv32_trng, CONFIG_ENTROPY_LOG_LEVEL);
19 
20 /* TRNG CTRL register bits */
21 #define NEORV32_TRNG_CTRL_DATA_MASK BIT_MASK(8)
22 #define NEORV32_TRNG_CTRL_EN        BIT(30)
23 #define NEORV32_TRNG_CTRL_VALID     BIT(31)
24 
25 struct neorv32_trng_config {
26 	const struct device *syscon;
27 	mm_reg_t base;
28 };
29 
neorv32_trng_read_ctrl(const struct device * dev)30 static inline uint32_t neorv32_trng_read_ctrl(const struct device *dev)
31 {
32 	const struct neorv32_trng_config *config = dev->config;
33 
34 	return sys_read32(config->base);
35 }
36 
neorv32_trng_write_ctrl(const struct device * dev,uint32_t ctrl)37 static inline void neorv32_trng_write_ctrl(const struct device *dev, uint32_t ctrl)
38 {
39 	const struct neorv32_trng_config *config = dev->config;
40 
41 	sys_write32(ctrl, config->base);
42 }
43 
neorv32_trng_get_entropy(const struct device * dev,uint8_t * buffer,uint16_t len)44 static int neorv32_trng_get_entropy(const struct device *dev, uint8_t *buffer, uint16_t len)
45 {
46 	uint32_t ctrl;
47 
48 	while (len > 0) {
49 		ctrl = neorv32_trng_read_ctrl(dev);
50 
51 		if ((ctrl & NEORV32_TRNG_CTRL_VALID) != 0) {
52 			*buffer++ = ctrl & NEORV32_TRNG_CTRL_DATA_MASK;
53 			len--;
54 		}
55 	}
56 
57 	return 0;
58 }
59 
neorv32_trng_get_entropy_isr(const struct device * dev,uint8_t * buffer,uint16_t len,uint32_t flags)60 static int neorv32_trng_get_entropy_isr(const struct device *dev, uint8_t *buffer,
61 					uint16_t len, uint32_t flags)
62 {
63 	uint32_t ctrl;
64 	int err;
65 
66 	if ((flags & ENTROPY_BUSYWAIT) == 0) {
67 		ctrl = neorv32_trng_read_ctrl(dev);
68 		if ((ctrl & NEORV32_TRNG_CTRL_VALID) != 0) {
69 			*buffer = ctrl & NEORV32_TRNG_CTRL_DATA_MASK;
70 			return 1;
71 		}
72 
73 		/* No entropy available */
74 		return -ENODATA;
75 	}
76 
77 	err = neorv32_trng_get_entropy(dev, buffer, len);
78 	if (err < 0) {
79 		return err;
80 	}
81 
82 	return len;
83 }
84 
neorv32_trng_init(const struct device * dev)85 static int neorv32_trng_init(const struct device *dev)
86 {
87 	const struct neorv32_trng_config *config = dev->config;
88 	uint32_t features;
89 	int err;
90 
91 	if (!device_is_ready(config->syscon)) {
92 		LOG_ERR("syscon device not ready");
93 		return -EINVAL;
94 	}
95 
96 	err = syscon_read_reg(config->syscon, NEORV32_SYSINFO_FEATURES, &features);
97 	if (err < 0) {
98 		LOG_ERR("failed to determine implemented features (err %d)", err);
99 		return err;
100 	}
101 
102 	if ((features & NEORV32_SYSINFO_FEATURES_IO_TRNG) == 0) {
103 		LOG_ERR("neorv32 trng not supported");
104 		return -ENODEV;
105 	}
106 
107 	neorv32_trng_write_ctrl(dev, NEORV32_TRNG_CTRL_EN);
108 
109 	return 0;
110 }
111 
112 #ifdef CONFIG_PM_DEVICE
neorv32_trng_pm_action(const struct device * dev,enum pm_device_action action)113 static int neorv32_trng_pm_action(const struct device *dev, enum pm_device_action action)
114 {
115 	switch (action) {
116 	case PM_DEVICE_ACTION_SUSPEND:
117 		neorv32_trng_write_ctrl(dev, 0);
118 		break;
119 	case PM_DEVICE_ACTION_RESUME:
120 		neorv32_trng_write_ctrl(dev, NEORV32_TRNG_CTRL_EN);
121 		break;
122 	default:
123 		return -ENOTSUP;
124 	}
125 
126 	return 0;
127 }
128 #endif /* CONFIG_PM_DEVICE */
129 
130 static DEVICE_API(entropy, neorv32_trng_driver_api) = {
131 	.get_entropy = neorv32_trng_get_entropy,
132 	.get_entropy_isr = neorv32_trng_get_entropy_isr,
133 };
134 
135 #define NEORV32_TRNG_INIT(n)						\
136 	static const struct neorv32_trng_config neorv32_trng_##n##_config = { \
137 		.syscon = DEVICE_DT_GET(DT_INST_PHANDLE(n, syscon)),	\
138 		.base = DT_INST_REG_ADDR(n),				\
139 	};								\
140 									\
141 	PM_DEVICE_DT_INST_DEFINE(n, neorv32_trng_pm_action);		\
142 									\
143 	DEVICE_DT_INST_DEFINE(n, &neorv32_trng_init,			\
144 			 PM_DEVICE_DT_INST_GET(n),			\
145 			 NULL,						\
146 			 &neorv32_trng_##n##_config,			\
147 			 PRE_KERNEL_1,					\
148 			 CONFIG_ENTROPY_INIT_PRIORITY,			\
149 			 &neorv32_trng_driver_api);
150 
151 DT_INST_FOREACH_STATUS_OKAY(NEORV32_TRNG_INIT)
152