1 /*
2 * Copyright (c) 2006-2024 RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-11-21 GuEe-GUI first version
9 */
10
11 #include <rthw.h>
12 #include <rtthread.h>
13 #include <rtdevice.h>
14
15 #include <ioremap.h>
16 #include <cpuport.h>
17
18 #include "regs.h"
19 #include "serial_dm.h"
20
21 #define UART_USR 0x1f /* In: UART Status Register */
22 #define UART_USR_RX_FIFO_FULL 0x10 /* Receive FIFO full */
23 #define UART_USR_RX_FIFO_NOT_EMPTY 0x08 /* Receive FIFO not empty */
24 #define UART_USR_TX_FIFO_EMPTY 0x04 /* Transmit FIFO empty */
25 #define UART_USR_TX_FIFO_NOT_FULL 0x02 /* Transmit FIFO not full */
26 #define UART_USR_BUSY 0x01 /* UART busy indicator */
27 #define UART_SRR 0x22 /* software reset register */
28
29 #define FIQ_DEBUGGER_NO_CHAR -1
30 #define FIQ_DEBUGGER_BREAK -2
31
32 struct rockchip_fiq_debugger
33 {
34 struct rt_serial_device parent;
35
36 int irq;
37 int baudrate;
38 void *debug_port_base;
39 rt_bool_t break_seen;
40 };
41
42 #define raw_to_fiq_debugger(raw) rt_container_of(raw, struct rockchip_fiq_debugger, parent)
43
rockchip_fiq_write(struct rockchip_fiq_debugger * t,rt_uint32_t val,int off)44 rt_inline void rockchip_fiq_write(struct rockchip_fiq_debugger *t, rt_uint32_t val, int off)
45 {
46 HWREG32(t->debug_port_base + off * 4) = val;
47 }
48
rockchip_fiq_read(struct rockchip_fiq_debugger * t,int off)49 rt_inline rt_uint32_t rockchip_fiq_read(struct rockchip_fiq_debugger *t, int off)
50 {
51 return HWREG32(t->debug_port_base + off * 4);
52 }
53
rockchip_fiq_read_lsr(struct rockchip_fiq_debugger * t)54 rt_inline rt_uint32_t rockchip_fiq_read_lsr(struct rockchip_fiq_debugger *t)
55 {
56 rt_uint32_t ret = rockchip_fiq_read(t, UART_LSR);
57
58 if (ret & UART_LSR_BI)
59 {
60 t->break_seen = true;
61 }
62
63 return ret;
64 }
65
fiq_debugger_isr(int irqno,void * param)66 static void fiq_debugger_isr(int irqno, void *param)
67 {
68 rt_uint32_t usr;
69 struct rt_serial_device *serial = (struct rt_serial_device*)param;
70 struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
71
72 usr = rockchip_fiq_read(t, UART_USR);
73
74 if ((usr & UART_USR_RX_FIFO_NOT_EMPTY) == UART_USR_RX_FIFO_NOT_EMPTY)
75 {
76 rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
77 }
78
79 if ((usr & UART_USR_BUSY) == UART_USR_BUSY)
80 {
81 /* Clear the USR */
82 (void)rockchip_fiq_read(t, UART_USR);
83 }
84 }
85
fiq_debugger_uart_configure(struct rt_serial_device * serial,struct serial_configure * cfg)86 static rt_err_t fiq_debugger_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
87 {
88 int dll = 0, dlm = 0;
89 rt_uint32_t count = 10000;
90 struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
91
92 if (rockchip_fiq_read(t, UART_LSR) & UART_LSR_DR)
93 {
94 (void)rockchip_fiq_read(t, UART_RX);
95 }
96
97 while (!(rockchip_fiq_read(t, UART_LSR) & UART_LSR_TEMT) && count--)
98 {
99 rt_hw_us_delay(10);
100 }
101
102 switch (t->baudrate)
103 {
104 case 1500000:
105 dll = 0x1;
106 break;
107 case 115200:
108 default:
109 dll = 0xd;
110 break;
111 }
112 /* reset uart */
113 rockchip_fiq_write(t, (1 << 1) | (1 << 2), UART_SRR);
114 rt_hw_us_delay(10);
115 /* set uart to loop back mode */
116 rockchip_fiq_write(t, 0x10, UART_MCR);
117
118 rockchip_fiq_write(t, 0x83, UART_LCR);
119 /* set baud rate */
120 rockchip_fiq_write(t, dll, UART_DLL);
121 rockchip_fiq_write(t, dlm, UART_DLM);
122 rockchip_fiq_write(t, 0x03, UART_LCR);
123
124 /* enable rx interrupt */
125 rockchip_fiq_write(t, UART_IER_RDI, UART_IER);
126
127 /*
128 * interrupt on every character when received, but we can enable fifo for TX
129 * I found that if we enable the RX fifo, some problem may vanish such as
130 * when you continuously input characters in the command line the uart irq
131 * may be disable because of the uart irq is served when CPU is at IRQ
132 * exception, but it is found unregistered, so it is disable.
133 */
134 rockchip_fiq_write(t, 0x01, UART_FCR);
135
136 /* disbale loop back mode */
137 rockchip_fiq_write(t, 0x0, UART_MCR);
138
139 return RT_EOK;
140 }
141
fiq_debugger_uart_control(struct rt_serial_device * serial,int cmd,void * arg)142 static rt_err_t fiq_debugger_uart_control(struct rt_serial_device *serial, int cmd, void *arg)
143 {
144 struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
145
146 switch (cmd)
147 {
148 case RT_DEVICE_CTRL_CLR_INT:
149 rt_pic_irq_mask(t->irq);
150 break;
151
152 case RT_DEVICE_CTRL_SET_INT:
153 rt_pic_irq_unmask(t->irq);
154 break;
155 }
156
157 return RT_EOK;
158 }
159
fiq_debugger_uart_putc(struct rt_serial_device * serial,char c)160 static int fiq_debugger_uart_putc(struct rt_serial_device *serial, char c)
161 {
162 struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
163 rt_uint32_t count = 10000;
164
165 while (!(rockchip_fiq_read(t, UART_USR) & UART_USR_TX_FIFO_NOT_FULL) && count--)
166 {
167 rt_hw_cpu_relax();
168 }
169
170 rockchip_fiq_write(t, c, UART_TX);
171
172 return 1;
173 }
174
fiq_debugger_uart_getc(struct rt_serial_device * serial)175 static int fiq_debugger_uart_getc(struct rt_serial_device *serial)
176 {
177 int ch = FIQ_DEBUGGER_NO_CHAR;
178 rt_uint32_t lsr, temp;
179 static rt_uint32_t n = 0;
180 static char buf[32] = {};
181 struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
182
183 /* Clear uart interrupt status */
184 rockchip_fiq_read(t, UART_USR);
185 lsr = rockchip_fiq_read_lsr(t);
186
187 if (lsr & UART_LSR_DR)
188 {
189 temp = rockchip_fiq_read(t, UART_RX);
190 buf[n & 0x1f] = temp;
191 n++;
192
193 if (temp == 'q' && n > 2)
194 {
195 if ((buf[(n - 2) & 0x1f] == 'i') && (buf[(n - 3) & 0x1f] == 'f'))
196 {
197 ch = FIQ_DEBUGGER_BREAK;
198 }
199 else
200 {
201 ch = temp;
202 }
203 }
204 else
205 {
206 ch = temp;
207 }
208 }
209
210 return ch;
211 }
212
213 static const struct rt_uart_ops fiq_debugger_uart_ops =
214 {
215 .configure = fiq_debugger_uart_configure,
216 .control = fiq_debugger_uart_control,
217 .putc = fiq_debugger_uart_putc,
218 .getc = fiq_debugger_uart_getc,
219 };
220
rk_serial_debug_init(void * base,rt_ubase_t paddr,int irq,int signal_irq,int wakeup_irq,rt_uint32_t baudrate)221 struct rockchip_fiq_debugger *rk_serial_debug_init(void *base, rt_ubase_t paddr,
222 int irq, int signal_irq, int wakeup_irq, rt_uint32_t baudrate)
223 {
224 struct rockchip_fiq_debugger *t = rt_calloc(1, sizeof(*t));
225
226 if (t)
227 {
228 const char *name;
229
230 t->parent.ops = &fiq_debugger_uart_ops;
231 t->parent.config = (struct serial_configure)RT_SERIAL_CONFIG_DEFAULT;
232 t->parent.config.baud_rate = baudrate;
233 t->irq = irq;
234 t->baudrate = baudrate;
235 t->debug_port_base = base;
236 t->break_seen = RT_FALSE;
237
238 serial_dev_set_name(&t->parent);
239 name = rt_dm_dev_get_name(&t->parent.parent);
240
241 rt_hw_serial_register(&t->parent, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, t);
242 rt_pic_attach_irq(t->irq, fiq_debugger_isr, &t->parent, name, RT_IRQ_F_NONE);
243 }
244
245 return t;
246 }
247
rockchip_fiq_debugger_probe(struct rt_platform_device * pdev)248 static rt_err_t rockchip_fiq_debugger_probe(struct rt_platform_device *pdev)
249 {
250 rt_err_t err = RT_EOK;
251 void *base;
252 rt_uint64_t regs[2];
253 struct rt_clk *clk, *pclk;
254 rt_bool_t found = RT_FALSE;
255 char dev_name[RT_NAME_MAX];
256 int irq, signal_irq = -1;
257 rt_uint32_t serial_id, baudrate = 0, irq_mode = 0, wake_irq = -1;
258 struct rt_ofw_node *np = pdev->parent.ofw_node;
259
260 if (rt_ofw_prop_read_u32(np, "rockchip,serial-id", &serial_id) || serial_id == -1)
261 {
262 return -RT_EINVAL;
263 }
264
265 if (rt_ofw_prop_read_u32(np, "rockchip,irq-mode-enable", &irq_mode))
266 {
267 irq_mode = -1;
268 }
269
270 if (irq_mode == 1)
271 {
272 signal_irq = -1;
273 }
274 else if (!(signal_irq = rt_ofw_get_irq(np, 0)))
275 {
276 return -RT_EINVAL;
277 }
278
279 if (rt_ofw_prop_read_u32(np, "rockchip,wake-irq", &wake_irq))
280 {
281 wake_irq = -1;
282 }
283
284 if (rt_ofw_prop_read_u32(np, "rockchip,baudrate", &baudrate))
285 {
286 baudrate = 1500000;
287 }
288
289 rt_snprintf(dev_name, RT_NAME_MAX, "serial%d", serial_id);
290
291 np = RT_NULL;
292
293 do {
294 np = rt_ofw_find_node_by_tag(np, "serial");
295
296 if (np && rt_ofw_get_alias_id(np, "serial") == serial_id)
297 {
298 found = RT_TRUE;
299 break;
300 }
301 } while(np);
302
303 if (!found)
304 {
305 return -RT_EINVAL;
306 }
307
308 rt_memset(regs, 0, sizeof(regs));
309 rt_ofw_get_address_array(np, 1, regs);
310
311 pclk = rt_ofw_get_clk_by_name(np, "apb_pclk");
312 clk = rt_ofw_get_clk_by_name(np, "baudclk");
313
314 if (!pclk || !clk)
315 {
316 err = -RT_ERROR;
317 goto _fail;
318 }
319
320 rt_clk_enable(clk);
321 rt_clk_enable(pclk);
322
323 if ((irq = rt_ofw_get_irq(np, 0)) < 0)
324 {
325 err = -RT_ERROR;
326 goto _fail;
327 }
328
329 if ((base = rt_ioremap((void *)regs[0], regs[1])))
330 {
331 struct rockchip_fiq_debugger *t = rk_serial_debug_init(base,
332 (rt_ubase_t)regs[0], irq, signal_irq, wake_irq, baudrate);
333
334 if (t)
335 {
336 rt_dm_dev_bind_fwdata(&t->parent.parent, pdev->parent.ofw_node, &t->parent);
337 }
338 }
339
340 return err;
341
342 _fail:
343 if (clk)
344 {
345 rt_clk_disable(clk);
346 rt_clk_put(clk);
347 }
348
349 if (pclk)
350 {
351 rt_clk_disable(pclk);
352 rt_clk_put(pclk);
353 }
354
355 return err;
356 }
357
358 static const struct rt_ofw_node_id rockchip_fiq_debugger_ofw_ids[] =
359 {
360 { .type = "ttyFIQ", .compatible = "rockchip,fiq-debugger" },
361 { /* sentinel */ }
362 };
363
364 static struct rt_platform_driver rockchip_fiq_debugger_driver =
365 {
366 .name = "rockchip-fiq-debugger",
367 .ids = rockchip_fiq_debugger_ofw_ids,
368
369 .probe = rockchip_fiq_debugger_probe,
370 };
371
rockchip_fiq_debugger_drv_register(void)372 static int rockchip_fiq_debugger_drv_register(void)
373 {
374 rt_platform_driver_register(&rockchip_fiq_debugger_driver);
375
376 return 0;
377 }
378 INIT_PLATFORM_EXPORT(rockchip_fiq_debugger_drv_register);
379