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