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