1 /* swo-uart1.c
2  *
3  * Copyright 2015 Brian Swetland <swetland@frotz.net>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include <string.h>
19 #include <lk/debug.h>
20 #include <lk/reg.h>
21 #include <kernel/thread.h>
22 
23 #include <dev/udc.h>
24 #include <arch/arm/cm.h>
25 
26 #include <platform/lpc43xx-uart.h>
27 #include <platform/lpc43xx-gpdma.h>
28 #include <platform/lpc43xx-clocks.h>
29 
30 #include "rswdp.h"
31 
32 #define UART_BASE   UART1_BASE
33 #define BASE_UART_CLK   BASE_UART1_CLK
34 
35 extern uint8_t __lpc43xx_main_clock_sel;
36 extern uint32_t __lpc43xx_main_clock_mhz;
37 
38 #define TXNSIZE     128
39 #define TXNCOUNT    4
40 
41 typedef struct swo_txn {
42     unsigned buf[TXNSIZE/4 + 2];
43     struct swo_txn *next;
44     udc_request_t *req;
45     unsigned busy;
46     unsigned num;
47 } txn_t;
48 
49 static txn_t TXN[TXNCOUNT];
50 static txn_t *txwr = TXN;
51 static udc_endpoint_t *txept;
52 
53 void swo_start_dma(void *ptr);
54 
tx_done(udc_request_t * req,unsigned actual,int status)55 static void tx_done(udc_request_t *req, unsigned actual, int status) {
56     txn_t *txn = req->context;
57     txn->busy = 0;
58     if (txwr == txn) {
59         // writer wants to write here, and is waiting...
60         swo_start_dma(txn->buf + 2);
61     }
62 }
63 
lpc43xx_DMA_IRQ(void)64 void lpc43xx_DMA_IRQ(void) {
65     txn_t *txn = txwr;
66     arm_cm_irq_entry();
67     writel(0xFF, DMA_INTTCCLR);
68     writel(0xFF, DMA_INTERRCLR);
69     if (udc_request_queue(txept, txn->req)) {
70         // failed, usb probably offline, just re-use the buffer
71     } else {
72         txn->busy = 1;
73         txwr = txn = txn->next;
74     }
75     if (!txn->busy) {
76         // if busy, when the usb txn completes, it will start dma then
77         swo_start_dma(txn->buf + 2);
78     }
79     arm_cm_irq_exit(0);
80 }
81 
swo_init(udc_endpoint_t * _txept)82 void swo_init(udc_endpoint_t *_txept) {
83     int n;
84     txept = _txept;
85     for (n = 0; n < TXNCOUNT; n++) {
86         TXN[n].req = udc_request_alloc();
87         TXN[n].req->context = TXN + n;
88         TXN[n].req->buffer = TXN[n].buf;
89         TXN[n].req->length = TXNSIZE + 8;
90         TXN[n].req->complete = tx_done;
91         TXN[n].num = n;
92         TXN[n].busy = 0;
93         TXN[n].next = TXN + (n + 1);
94         TXN[n].buf[0] = RSWD_TXN_ASYNC;
95         TXN[n].buf[1] = RSWD_MSG(CMD_SWO_DATA, 0, TXNSIZE);
96     }
97     TXN[n-1].next = TXN;
98 
99     // configure peripheral 4 as uart1_rx
100     writel((readl(DMAMUX_REG) & DMAMUX_M(4)) | DMAMUX_P(4, P4_UART1_RX), DMAMUX_REG);
101     writel(DMA_CONFIG_EN, DMA_CONFIG);
102     NVIC_EnableIRQ(DMA_IRQn);
103 
104     // kick off the process with an initial DMA
105     swo_start_dma(txwr->buf + 2);
106 }
107 
swo_start_dma(void * ptr)108 void swo_start_dma(void *ptr) {
109     writel(UART1_BASE + REG_RBR, DMA_SRC(0));
110     writel((u32) ptr, DMA_DST(0));
111     writel(0, DMA_LLI(0));
112     writel(DMA_XFER_SIZE(TXNSIZE) |
113            DMA_SRC_BURST(BURST_1) | DMA_DST_BURST(BURST_4) |
114            DMA_SRC_BYTE | DMA_DST_WORD | DMA_SRC_MASTER1 | DMA_DST_MASTER0 |
115            DMA_DST_INCR | DMA_PROT1 | DMA_TC_IE,
116            DMA_CTL(0));
117     writel(DMA_ENABLE | DMA_SRC_PERIPH(4) | DMA_FLOW_P2M_DMAc | DMA_TC_IRQ_EN,
118            DMA_CFG(0));
119 }
swo_config(unsigned mhz)120 void swo_config(unsigned mhz) {
121     if (mhz > 0) {
122         uint32_t div = __lpc43xx_main_clock_mhz / 16 / mhz;
123         writel(BASE_CLK_SEL(__lpc43xx_main_clock_sel), BASE_UART_CLK);
124         writel(LCR_DLAB, UART_BASE + REG_LCR);
125         writel(div & 0xFF, UART_BASE + REG_DLL);
126         writel((div >> 8) & 0xFF, UART_BASE + REG_DLM);
127         writel(LCR_WLS_8 | LCR_SBS_1, UART_BASE + REG_LCR);
128         writel(FCR_FIFOEN | FCR_RX_TRIG_1 | FCR_DMAMODE, UART_BASE + REG_FCR);
129     }
130 }
131 
swo_set_clock(unsigned khz)132 unsigned swo_set_clock(unsigned khz) {
133     if (khz >= 12000) {
134         khz = 12000;
135     } else if (khz >= 8000) {
136         khz = 8000;
137     } else if (khz >= 6000) {
138         khz = 6000;
139     } else if (khz >= 4000) {
140         khz = 4000;
141     } else if (khz >= 3000) {
142         khz = 3000;
143     } else if (khz >= 2000) {
144         khz = 2000;
145     } else {
146         khz = 1000;
147     }
148     swo_config(khz * 1000);
149     return khz;
150 }
151 
152