mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2025-02-16 15:39:09 +08:00
379 lines
9.1 KiB
C
379 lines
9.1 KiB
C
/*
|
|
* Copyright (c) 2006-2024 RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2022-11-21 GuEe-GUI first version
|
|
*/
|
|
|
|
#include <rthw.h>
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
|
|
#include <ioremap.h>
|
|
#include <cpuport.h>
|
|
|
|
#include "regs.h"
|
|
#include "serial_dm.h"
|
|
|
|
#define UART_USR 0x1f /* In: UART Status Register */
|
|
#define UART_USR_RX_FIFO_FULL 0x10 /* Receive FIFO full */
|
|
#define UART_USR_RX_FIFO_NOT_EMPTY 0x08 /* Receive FIFO not empty */
|
|
#define UART_USR_TX_FIFO_EMPTY 0x04 /* Transmit FIFO empty */
|
|
#define UART_USR_TX_FIFO_NOT_FULL 0x02 /* Transmit FIFO not full */
|
|
#define UART_USR_BUSY 0x01 /* UART busy indicator */
|
|
#define UART_SRR 0x22 /* software reset register */
|
|
|
|
#define FIQ_DEBUGGER_NO_CHAR -1
|
|
#define FIQ_DEBUGGER_BREAK -2
|
|
|
|
struct rockchip_fiq_debugger
|
|
{
|
|
struct rt_serial_device parent;
|
|
|
|
int irq;
|
|
int baudrate;
|
|
void *debug_port_base;
|
|
rt_bool_t break_seen;
|
|
};
|
|
|
|
#define raw_to_fiq_debugger(raw) rt_container_of(raw, struct rockchip_fiq_debugger, parent)
|
|
|
|
rt_inline void rockchip_fiq_write(struct rockchip_fiq_debugger *t, rt_uint32_t val, int off)
|
|
{
|
|
HWREG32(t->debug_port_base + off * 4) = val;
|
|
}
|
|
|
|
rt_inline rt_uint32_t rockchip_fiq_read(struct rockchip_fiq_debugger *t, int off)
|
|
{
|
|
return HWREG32(t->debug_port_base + off * 4);
|
|
}
|
|
|
|
rt_inline rt_uint32_t rockchip_fiq_read_lsr(struct rockchip_fiq_debugger *t)
|
|
{
|
|
rt_uint32_t ret = rockchip_fiq_read(t, UART_LSR);
|
|
|
|
if (ret & UART_LSR_BI)
|
|
{
|
|
t->break_seen = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void fiq_debugger_isr(int irqno, void *param)
|
|
{
|
|
rt_uint32_t usr;
|
|
struct rt_serial_device *serial = (struct rt_serial_device*)param;
|
|
struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
|
|
|
|
usr = rockchip_fiq_read(t, UART_USR);
|
|
|
|
if ((usr & UART_USR_RX_FIFO_NOT_EMPTY) == UART_USR_RX_FIFO_NOT_EMPTY)
|
|
{
|
|
rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
|
|
}
|
|
|
|
if ((usr & UART_USR_BUSY) == UART_USR_BUSY)
|
|
{
|
|
/* Clear the USR */
|
|
(void)rockchip_fiq_read(t, UART_USR);
|
|
}
|
|
}
|
|
|
|
static rt_err_t fiq_debugger_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
|
|
{
|
|
int dll = 0, dlm = 0;
|
|
rt_uint32_t count = 10000;
|
|
struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
|
|
|
|
if (rockchip_fiq_read(t, UART_LSR) & UART_LSR_DR)
|
|
{
|
|
(void)rockchip_fiq_read(t, UART_RX);
|
|
}
|
|
|
|
while (!(rockchip_fiq_read(t, UART_LSR) & UART_LSR_TEMT) && count--)
|
|
{
|
|
rt_hw_us_delay(10);
|
|
}
|
|
|
|
switch (t->baudrate)
|
|
{
|
|
case 1500000:
|
|
dll = 0x1;
|
|
break;
|
|
case 115200:
|
|
default:
|
|
dll = 0xd;
|
|
break;
|
|
}
|
|
/* reset uart */
|
|
rockchip_fiq_write(t, (1 << 1) | (1 << 2), UART_SRR);
|
|
rt_hw_us_delay(10);
|
|
/* set uart to loop back mode */
|
|
rockchip_fiq_write(t, 0x10, UART_MCR);
|
|
|
|
rockchip_fiq_write(t, 0x83, UART_LCR);
|
|
/* set baud rate */
|
|
rockchip_fiq_write(t, dll, UART_DLL);
|
|
rockchip_fiq_write(t, dlm, UART_DLM);
|
|
rockchip_fiq_write(t, 0x03, UART_LCR);
|
|
|
|
/* enable rx interrupt */
|
|
rockchip_fiq_write(t, UART_IER_RDI, UART_IER);
|
|
|
|
/*
|
|
* interrupt on every character when received, but we can enable fifo for TX
|
|
* I found that if we enable the RX fifo, some problem may vanish such as
|
|
* when you continuously input characters in the command line the uart irq
|
|
* may be disable because of the uart irq is served when CPU is at IRQ
|
|
* exception, but it is found unregistered, so it is disable.
|
|
*/
|
|
rockchip_fiq_write(t, 0x01, UART_FCR);
|
|
|
|
/* disbale loop back mode */
|
|
rockchip_fiq_write(t, 0x0, UART_MCR);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t fiq_debugger_uart_control(struct rt_serial_device *serial, int cmd, void *arg)
|
|
{
|
|
struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
|
|
|
|
switch (cmd)
|
|
{
|
|
case RT_DEVICE_CTRL_CLR_INT:
|
|
rt_pic_irq_mask(t->irq);
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_SET_INT:
|
|
rt_pic_irq_unmask(t->irq);
|
|
break;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static int fiq_debugger_uart_putc(struct rt_serial_device *serial, char c)
|
|
{
|
|
struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
|
|
rt_uint32_t count = 10000;
|
|
|
|
while (!(rockchip_fiq_read(t, UART_USR) & UART_USR_TX_FIFO_NOT_FULL) && count--)
|
|
{
|
|
rt_hw_cpu_relax();
|
|
}
|
|
|
|
rockchip_fiq_write(t, c, UART_TX);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int fiq_debugger_uart_getc(struct rt_serial_device *serial)
|
|
{
|
|
int ch = FIQ_DEBUGGER_NO_CHAR;
|
|
rt_uint32_t lsr, temp;
|
|
static rt_uint32_t n = 0;
|
|
static char buf[32] = {};
|
|
struct rockchip_fiq_debugger *t = raw_to_fiq_debugger(serial);
|
|
|
|
/* Clear uart interrupt status */
|
|
rockchip_fiq_read(t, UART_USR);
|
|
lsr = rockchip_fiq_read_lsr(t);
|
|
|
|
if (lsr & UART_LSR_DR)
|
|
{
|
|
temp = rockchip_fiq_read(t, UART_RX);
|
|
buf[n & 0x1f] = temp;
|
|
n++;
|
|
|
|
if (temp == 'q' && n > 2)
|
|
{
|
|
if ((buf[(n - 2) & 0x1f] == 'i') && (buf[(n - 3) & 0x1f] == 'f'))
|
|
{
|
|
ch = FIQ_DEBUGGER_BREAK;
|
|
}
|
|
else
|
|
{
|
|
ch = temp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ch = temp;
|
|
}
|
|
}
|
|
|
|
return ch;
|
|
}
|
|
|
|
static const struct rt_uart_ops fiq_debugger_uart_ops =
|
|
{
|
|
.configure = fiq_debugger_uart_configure,
|
|
.control = fiq_debugger_uart_control,
|
|
.putc = fiq_debugger_uart_putc,
|
|
.getc = fiq_debugger_uart_getc,
|
|
};
|
|
|
|
struct rockchip_fiq_debugger *rk_serial_debug_init(void *base, rt_ubase_t paddr,
|
|
int irq, int signal_irq, int wakeup_irq, rt_uint32_t baudrate)
|
|
{
|
|
struct rockchip_fiq_debugger *t = rt_calloc(1, sizeof(*t));
|
|
|
|
if (t)
|
|
{
|
|
const char *name;
|
|
|
|
t->parent.ops = &fiq_debugger_uart_ops;
|
|
t->parent.config = (struct serial_configure)RT_SERIAL_CONFIG_DEFAULT;
|
|
t->parent.config.baud_rate = baudrate;
|
|
t->irq = irq;
|
|
t->baudrate = baudrate;
|
|
t->debug_port_base = base;
|
|
t->break_seen = RT_FALSE;
|
|
|
|
serial_dev_set_name(&t->parent);
|
|
name = rt_dm_dev_get_name(&t->parent.parent);
|
|
|
|
rt_hw_serial_register(&t->parent, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, t);
|
|
rt_pic_attach_irq(t->irq, fiq_debugger_isr, &t->parent, name, RT_IRQ_F_NONE);
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
static rt_err_t rockchip_fiq_debugger_probe(struct rt_platform_device *pdev)
|
|
{
|
|
rt_err_t err = RT_EOK;
|
|
void *base;
|
|
rt_uint64_t regs[2];
|
|
struct rt_clk *clk, *pclk;
|
|
rt_bool_t found = RT_FALSE;
|
|
char dev_name[RT_NAME_MAX];
|
|
int irq, signal_irq = -1;
|
|
rt_uint32_t serial_id, baudrate = 0, irq_mode = 0, wake_irq = -1;
|
|
struct rt_ofw_node *np = pdev->parent.ofw_node;
|
|
|
|
if (rt_ofw_prop_read_u32(np, "rockchip,serial-id", &serial_id) || serial_id == -1)
|
|
{
|
|
return -RT_EINVAL;
|
|
}
|
|
|
|
if (rt_ofw_prop_read_u32(np, "rockchip,irq-mode-enable", &irq_mode))
|
|
{
|
|
irq_mode = -1;
|
|
}
|
|
|
|
if (irq_mode == 1)
|
|
{
|
|
signal_irq = -1;
|
|
}
|
|
else if (!(signal_irq = rt_ofw_get_irq(np, 0)))
|
|
{
|
|
return -RT_EINVAL;
|
|
}
|
|
|
|
if (rt_ofw_prop_read_u32(np, "rockchip,wake-irq", &wake_irq))
|
|
{
|
|
wake_irq = -1;
|
|
}
|
|
|
|
if (rt_ofw_prop_read_u32(np, "rockchip,baudrate", &baudrate))
|
|
{
|
|
baudrate = 1500000;
|
|
}
|
|
|
|
rt_snprintf(dev_name, RT_NAME_MAX, "serial%d", serial_id);
|
|
|
|
np = RT_NULL;
|
|
|
|
do {
|
|
np = rt_ofw_find_node_by_tag(np, "serial");
|
|
|
|
if (np && rt_ofw_get_alias_id(np, "serial") == serial_id)
|
|
{
|
|
found = RT_TRUE;
|
|
break;
|
|
}
|
|
} while(np);
|
|
|
|
if (!found)
|
|
{
|
|
return -RT_EINVAL;
|
|
}
|
|
|
|
rt_memset(regs, 0, sizeof(regs));
|
|
rt_ofw_get_address_array(np, 1, regs);
|
|
|
|
pclk = rt_ofw_get_clk_by_name(np, "apb_pclk");
|
|
clk = rt_ofw_get_clk_by_name(np, "baudclk");
|
|
|
|
if (!pclk || !clk)
|
|
{
|
|
err = -RT_ERROR;
|
|
goto _fail;
|
|
}
|
|
|
|
rt_clk_enable(clk);
|
|
rt_clk_enable(pclk);
|
|
|
|
if ((irq = rt_ofw_get_irq(np, 0)) < 0)
|
|
{
|
|
err = -RT_ERROR;
|
|
goto _fail;
|
|
}
|
|
|
|
if ((base = rt_ioremap((void *)regs[0], regs[1])))
|
|
{
|
|
struct rockchip_fiq_debugger *t = rk_serial_debug_init(base,
|
|
(rt_ubase_t)regs[0], irq, signal_irq, wake_irq, baudrate);
|
|
|
|
if (t)
|
|
{
|
|
rt_dm_dev_bind_fwdata(&t->parent.parent, pdev->parent.ofw_node, &t->parent);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
|
|
_fail:
|
|
if (clk)
|
|
{
|
|
rt_clk_disable(clk);
|
|
rt_clk_put(clk);
|
|
}
|
|
|
|
if (pclk)
|
|
{
|
|
rt_clk_disable(pclk);
|
|
rt_clk_put(pclk);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static const struct rt_ofw_node_id rockchip_fiq_debugger_ofw_ids[] =
|
|
{
|
|
{ .type = "ttyFIQ", .compatible = "rockchip,fiq-debugger" },
|
|
{ /* sentinel */ }
|
|
};
|
|
|
|
static struct rt_platform_driver rockchip_fiq_debugger_driver =
|
|
{
|
|
.name = "rockchip-fiq-debugger",
|
|
.ids = rockchip_fiq_debugger_ofw_ids,
|
|
|
|
.probe = rockchip_fiq_debugger_probe,
|
|
};
|
|
|
|
static int rockchip_fiq_debugger_drv_register(void)
|
|
{
|
|
rt_platform_driver_register(&rockchip_fiq_debugger_driver);
|
|
|
|
return 0;
|
|
}
|
|
INIT_PLATFORM_EXPORT(rockchip_fiq_debugger_drv_register);
|