1 /*
2  * Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /******************************************************************************
18  * @file     ck_trng.c
19  * @brief    CSI Source File for TRNG Driver
20  * @version  V1.0
21  * @date     02. June 2017
22  ******************************************************************************/
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include "drv_trng.h"
27 #include "ck_trng.h"
28 
29 
30 #define ERR_TRNG(errno) (CSI_DRV_ERRNO_TRNG_BASE | errno)
31 #define TRNG_NULL_PARAM_CHK(para)                         \
32         do {                                        \
33             if (para == NULL) {                     \
34                 return ERR_TRNG(EDRV_PARAMETER);   \
35             }                                       \
36         } while (0)
37 
38 typedef struct {
39     uint32_t base;
40     trng_event_cb_t cb;
41     trng_status_t status;
42 } ck_trng_priv_t;
43 
44 static ck_trng_priv_t trng_handle[CONFIG_TRNG_NUM];
45 
46 /* Driver Capabilities */
47 static const trng_capabilities_t driver_capabilities = {
48     .lowper_mode = 1 /* low power mode */
49 };
50 
51 //
52 // Functions
53 //
54 
55 ck_trng_reg_t *trng_reg = NULL;
56 
trng_enable(void)57 static int32_t trng_enable(void)
58 {
59     trng_reg->TCR |= TRNG_EN;
60     return 0;
61 }
62 
trng_get_data(void)63 static int32_t trng_get_data(void)
64 {
65     int data = trng_reg->TDR;
66     return data;
67 }
68 
trng_data_is_ready(void)69 static int32_t trng_data_is_ready(void)
70 {
71     int flag = (trng_reg->TCR & TRNG_DATA_READY);
72     return flag;
73 }
74 
75 
target_get_trng_count(void)76 int32_t __attribute__((weak)) target_get_trng_count(void)
77 {
78     return 0;
79 }
80 
target_get_trng(int32_t idx,uint32_t * base)81 int32_t __attribute__((weak)) target_get_trng(int32_t idx, uint32_t *base)
82 {
83     return NULL;
84 }
85 /**
86   \brief       get trng handle count.
87   \return      trng handle count
88 */
csi_trng_get_instance_count(void)89 int32_t csi_trng_get_instance_count(void)
90 {
91     return target_get_trng_count();
92 }
93 
94 /**
95   \brief       Initialize TRNG Interface. 1. Initializes the resources needed for the TRNG interface 2.registers event callback function
96   \param[in]   idx  must not exceed return value of csi_trng_get_instance_count()
97   \param[in]   cb_event  Pointer to \ref trng_event_cb_t
98   \return      pointer to trng handle
99 */
csi_trng_initialize(int32_t idx,trng_event_cb_t cb_event)100 trng_handle_t csi_trng_initialize(int32_t idx, trng_event_cb_t cb_event)
101 {
102 
103     if (idx < 0 || idx >= CONFIG_TRNG_NUM) {
104         return NULL;
105     }
106 
107     /* obtain the trng information */
108     uint32_t base = 0u;
109     int32_t real_idx = target_get_trng(idx, &base);
110 
111     if (real_idx != idx) {
112         return NULL;
113     }
114 
115     ck_trng_priv_t *trng_priv = &trng_handle[idx];
116     trng_priv->base = base;
117 
118     /* initialize the trng context */
119     trng_reg = (ck_trng_reg_t *)(trng_priv->base);
120     trng_priv->cb = cb_event;
121     trng_priv->status.busy = 0;
122     trng_priv->status.data_valid = 0;
123 
124     return (trng_handle_t)trng_priv;
125 }
126 
127 /**
128   \brief       De-initialize TRNG Interface. stops operation and releases the software resources used by the interface
129   \param[in]   handle  trng handle to operate.
130   \return      error code
131 */
csi_trng_uninitialize(trng_handle_t handle)132 int32_t csi_trng_uninitialize(trng_handle_t handle)
133 {
134     TRNG_NULL_PARAM_CHK(handle);
135 
136     ck_trng_priv_t *trng_priv = handle;
137     trng_priv->cb = NULL;
138 
139     return 0;
140 }
141 
142 /**
143   \brief       Get driver capabilities.
144   \param[in]   trng handle to operate.
145   \return      \ref trng_capabilities_t
146 */
csi_trng_get_capabilities(trng_handle_t handle)147 trng_capabilities_t csi_trng_get_capabilities(trng_handle_t handle)
148 {
149     return driver_capabilities;
150 }
151 
152 /**
153   \brief       Get data from the TRNG.
154   \param[in]   handle  trng handle to operate.
155   \param[out]  data  Pointer to buffer with data get from TRNG
156   \param[in]   num   Number of data items to obtain
157   \return      error code
158 */
csi_trng_get_data(trng_handle_t handle,void * data,uint32_t num)159 int32_t csi_trng_get_data(trng_handle_t handle, void *data, uint32_t num)
160 {
161     TRNG_NULL_PARAM_CHK(handle);
162     TRNG_NULL_PARAM_CHK(data);
163     TRNG_NULL_PARAM_CHK(num);
164 
165     ck_trng_priv_t *trng_priv = handle;
166 
167     trng_priv->status.busy = 1U;
168     trng_priv->status.data_valid = 0U;
169 
170     uint8_t left_len = (uint32_t)data & 0x3;
171     uint32_t result = 0;
172 
173     /* if the data addr is not aligned by word */
174     if (left_len) {
175         trng_enable();
176         while (!trng_data_is_ready());
177         result = trng_get_data();
178         /* wait the data is ready */
179         while (trng_data_is_ready());
180 
181         if (num > (4 - left_len)) {
182             memcpy(data, &result, 4 - left_len);
183         } else {
184             memcpy(data, &result, num);
185             trng_priv->status.busy = 0U;
186             trng_priv->status.data_valid = 1U;
187 
188             if (trng_priv->cb) {
189                 trng_priv->cb(TRNG_EVENT_DATA_GENERATE_COMPLETE);
190             }
191             return 0;
192         }
193         num -= (4 - left_len);
194     }
195 
196     uint32_t word_len = num >> 2;
197     left_len = num & 0x3;
198 
199     /* obtain the data by word */
200     while (word_len--) {
201         trng_enable();
202         while (!trng_data_is_ready());
203         result = trng_get_data();
204         while (trng_data_is_ready());
205         *(uint32_t *)data = result;
206         data += 4;
207     }
208 
209     /* if the num is not aligned by word */
210     if (left_len) {
211         trng_enable();
212         while (!trng_data_is_ready());
213         result = trng_get_data();
214         while (trng_data_is_ready());
215         memcpy(data, &result, left_len);
216     }
217 
218     trng_priv->status.busy = 0U;
219     trng_priv->status.data_valid = 1U;
220 
221     if (trng_priv->cb) {
222         trng_priv->cb(TRNG_EVENT_DATA_GENERATE_COMPLETE);
223     }
224 
225     return 0;
226 }
227 
228 /**
229   \brief       Get TRNG status.
230   \param[in]   handle  trng handle to operate.
231   \return      TRNG status \ref trng_status_t
232 */
csi_trng_get_status(trng_handle_t handle)233 trng_status_t csi_trng_get_status(trng_handle_t handle)
234 {
235     ck_trng_priv_t *trng_priv = handle;
236     return trng_priv->status;
237 }
238