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