1 /*
2  * Copyright (c) 2015 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include <lk/trace.h>
9 #include <lk/err.h>
10 #include <malloc.h>
11 #include <lk/init.h>
12 #include <arch/arm.h>
13 #include <arch/arm/dcc.h>
14 #include <kernel/thread.h>
15 #include <kernel/mutex.h>
16 #include <platform.h>
17 #include <lk/console_cmd.h>
18 #include <string.h>
19 
20 struct dcc_state {
21     dcc_rx_callback_t rx_callback;
22     mutex_t  lock;
23     thread_t *worker;
24 };
25 
26 #define SLOW_POLL_RATE 100
27 #define FAST_POLL_TIMEOUT 5
28 
dcc_worker_entry(void * arg)29 static int dcc_worker_entry(void *arg) {
30     struct dcc_state *dcc = (struct dcc_state *)arg;
31     lk_time_t fast_poll_start;
32     bool fast_poll;
33 
34     fast_poll = false;
35     for (;;) {
36         // wait for a bit if we're in slow poll mode
37         if (!fast_poll) {
38             thread_sleep(SLOW_POLL_RATE);
39         }
40 
41         if (arm_dcc_read_available()) {
42             uint32_t val = arm_read_dbgdtrrxint();
43 
44             dcc->rx_callback(val);
45 
46             // we just received something, so go to a faster poll rate
47             fast_poll = true;
48             fast_poll_start = current_time();
49         } else {
50             // didn't see anything
51             if (fast_poll && current_time() - fast_poll_start >= FAST_POLL_TIMEOUT) {
52                 fast_poll = false; // go back to slow poll
53             }
54         }
55     }
56 
57     return 0;
58 }
59 
arm_dcc_enable(dcc_rx_callback_t rx_callback)60 status_t arm_dcc_enable(dcc_rx_callback_t rx_callback) {
61     struct dcc_state *state = malloc(sizeof(struct dcc_state));
62     if (!state)
63         return ERR_NO_MEMORY;
64 
65     state->rx_callback = rx_callback;
66     mutex_init(&state->lock);
67 
68     state->worker = thread_create("dcc worker", dcc_worker_entry, state, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
69     thread_resume(state->worker);
70 
71     return NO_ERROR;
72 }
73 
arm_dcc_read_available(void)74 bool arm_dcc_read_available(void) {
75     uint32_t dscr = arm_read_dbgdscr();
76     if (dscr & (1<<30)) { // rx full
77         return true;
78     } else {
79         return false;
80     }
81 }
82 
arm_dcc_read(uint32_t * buf,size_t len,lk_time_t timeout)83 ssize_t arm_dcc_read(uint32_t *buf, size_t len, lk_time_t timeout) {
84     lk_time_t start = 0;
85 
86     if (timeout != 0)
87         start = current_time();
88 
89     ssize_t count = 0;
90     while (count < (ssize_t)len) {
91 
92         uint32_t dscr = arm_read_dbgdscr();
93         if (dscr & (1<<30)) { // rx full
94             uint32_t val = arm_read_dbgdtrrxint();
95             *buf++ = val;
96 
97             count++;
98         } else {
99             if (timeout == 0 || current_time() - start >= timeout) {
100                 break;
101             }
102         }
103     }
104 
105     return count;
106 }
107 
arm_dcc_write(const uint32_t * buf,size_t len,lk_time_t timeout)108 ssize_t arm_dcc_write(const uint32_t *buf, size_t len, lk_time_t timeout) {
109     lk_time_t start = 0;
110 
111     if (timeout != 0)
112         start = current_time();
113 
114     ssize_t count = 0;
115     while (count < (ssize_t)len) {
116 
117         uint32_t dscr = arm_read_dbgdscr();
118         if ((dscr & (1<<29)) == 0) { // tx empty
119             arm_write_dbgdtrrxint(*buf);
120             count++;
121             buf++;
122         } else {
123             if (timeout == 0 || current_time() - start >= timeout) {
124                 break;
125             }
126         }
127     }
128 
129     return count;
130 }
131 
dcc_rx_callback(uint32_t val)132 static void dcc_rx_callback(uint32_t val) {
133     static int count = 0;
134     count += 4;
135     if ((count % 1000) == 0)
136         printf("count %d\n", count);
137 }
138 
cmd_dcc(int argc,const console_cmd_args * argv)139 static int cmd_dcc(int argc, const console_cmd_args *argv) {
140     static bool dcc_started = false;
141 
142     if (argc < 2) {
143         printf("not enough args\n");
144         return -1;
145     }
146 
147     if (!strcmp(argv[1].str, "start")) {
148         if (!dcc_started) {
149             printf("starting dcc\n");
150 
151             status_t err = arm_dcc_enable(&dcc_rx_callback);
152             printf("arm_dcc_enable returns %d\n", err);
153             dcc_started = true;
154         }
155     } else if (!strcmp(argv[1].str, "write")) {
156         for (int i = 2; i < argc; i++) {
157             uint32_t buf[128];
158             size_t len = strlen(argv[i].str);
159             for (uint j = 0; j < len; j++) {
160                 buf[j] = argv[i].str[j];
161             }
162             arm_dcc_write(buf, strlen(argv[i].str), 1000);
163         }
164     } else if (!strcmp(argv[1].str, "read")) {
165         uint32_t buf[128];
166 
167         ssize_t len = arm_dcc_read(buf, sizeof(buf), 1000);
168         printf("arm_dcc_read returns %ld\n", len);
169         if (len > 0) {
170             hexdump(buf, len);
171         }
172     } else {
173         printf("unknown args\n");
174     }
175 
176     return 0;
177 }
178 
179 STATIC_COMMAND_START
180 #if LK_DEBUGLEVEL > 1
181 STATIC_COMMAND("dcc", "dcc stuff", &cmd_dcc)
182 #endif
183 STATIC_COMMAND_END(dcc);
184 
185