1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * Common EVDMA functionality models
9 *
10 * Notes:
11 * * Attributes with special meaning/signaling that data should be
12 * processed by the DMA are not yet implemented.
13 */
14
15 #include <stdlib.h>
16 #include <stdint.h>
17 #include <stdbool.h>
18 #include <string.h>
19 #include "NHW_EVDMA.h"
20 #include <bs_tracing.h>
21
22 /**
23 * Read the next job from the joblist into the EVDMA status structure
24 * and move the joblist_ptr to point to the one after.
25 * returns
26 * true if the joblist ended (no new job)
27 * false otherwise
28 */
nhw_EVDMA_read_next_job(EVDMA_status_t * st)29 static bool nhw_EVDMA_read_next_job(EVDMA_status_t *st) {
30 st->job_buff = st->joblist_ptr->job_buff;
31
32 if (st->job_buff == NULL) {
33 return true;
34 }
35
36 uint32_t at_len = st->joblist_ptr->attr_len;
37
38 st->job_attrib = at_len >> 24;
39 st->job_pend_length = at_len & (0xFFFFFF);
40
41 st->joblist_ptr++;
42
43 return false;
44 }
45
46 /**
47 * Peek on the next job from the joblist
48 * and set *job_attrib with that next job attribute
49 * returns
50 * true if the joblist ends in the next one (no new job)
51 * false otherwise
52 */
nhw_EVDMA_peek_next_job_attrib(EVDMA_status_t * st,uint * job_attrib)53 static bool nhw_EVDMA_peek_next_job_attrib(EVDMA_status_t *st, uint *job_attrib) {
54 char *job_buff = st->joblist_ptr->job_buff;
55
56 if (job_buff == NULL) {
57 return true;
58 }
59
60 uint32_t at_len = st->joblist_ptr->attr_len;
61 *job_attrib = at_len >> 24;
62
63 return false;
64 }
65
66 /**
67 * Initialize the EVDMA for a new joblist
68 *
69 * joblist_ptr: pointer to the job list
70 * st : EVDMA internal status to be initialized
71 *
72 * return: 0 on success
73 * != 0 if joblist_ptr is NULL
74 */
nhw_EVDMA_start(EVDMA_status_t * st,void * joblist_ptr)75 int nhw_EVDMA_start(EVDMA_status_t *st, void *joblist_ptr) {
76 st->joblist_ptr = joblist_ptr;
77 st->mode = EVDMA_idle;
78
79 st->job_buff = NULL;
80 st->job_pend_length = 0;
81 st->job_attrib = 0;
82
83 if (joblist_ptr == NULL) {
84 bs_trace_warning_time_line("FW error: Job list pointer is NULL\n");
85 return -1;
86 } else {
87 return 0;
88 }
89 }
90
91 /*
92 * Do an EVMA read/write of <nbytes>
93 *
94 * Returns the number of read/written bytes if all requested where read or a negative number otherwise
95 *
96 * st EVDMA internal status (will be updated)
97 * read_not_write True for read, false for write access.
98 * periph_buf Buffer in the peripheral where the data will be written to/read from
99 * nbytes Number of bytes to read/write
100 * n_actual Number of bytes actually read/written
101 * new_job Skip to the next job even if the current one has pending data
102 *
103 * returns:
104 * >=0 (nbytes) read as many as requested
105 * -1 partial read/write
106 * -2 end of job list reached (n_actual may be != 0 on a partial read/write)
107 * -3 error, joblist_ptr is invalid
108 */
nhw_EVDMA_access(EVDMA_status_t * st,bool read_not_write,uint8_t * periph_buf,size_t nbytes,size_t * n_actual,bool start_new_job)109 int nhw_EVDMA_access(EVDMA_status_t *st,
110 bool read_not_write,
111 uint8_t *periph_buf,
112 size_t nbytes,
113 size_t *n_actual,
114 bool start_new_job) {
115
116 *n_actual = 0;
117
118 if (st->joblist_ptr == NULL) {
119 bs_trace_warning_time_line("FW error: Job list pointer is NULL\n");
120 return -3;
121 }
122
123 if ( ( read_not_write && (st->mode == EVDMA_write))
124 || (!read_not_write && (st->mode == EVDMA_read)) ) {
125 bs_trace_error_time_line("Programming error\n");
126 } else {
127 if (read_not_write) {
128 st->mode = EVDMA_read;
129 } else {
130 st->mode = EVDMA_write;
131 }
132 }
133
134 if ((st->job_pend_length == 0) || start_new_job) {
135 bool list_end = nhw_EVDMA_read_next_job(st);
136 if (list_end) {
137 return -2;
138 }
139 }
140
141 do {
142 uint to_read = BS_MIN(nbytes - *n_actual, st->job_pend_length);
143
144 if (read_not_write) {
145 memcpy(periph_buf, st->job_buff, to_read);
146 } else {
147 memcpy(st->job_buff, periph_buf, to_read);
148 }
149 st->job_pend_length -= to_read;
150 st->job_buff += to_read;
151 *n_actual += to_read;
152 periph_buf += to_read;
153
154 if (*n_actual == nbytes) {
155 return nbytes;
156 }
157
158 uint next_job_attrib;
159 bool list_end = nhw_EVDMA_peek_next_job_attrib(st, &next_job_attrib);
160 if (list_end) {
161 return -2;
162 } else if (next_job_attrib != st->job_attrib) {
163 return -1;
164 }
165
166 nhw_EVDMA_read_next_job(st);
167
168 } while (true);
169 }
170