From 49db23b73378d7260fa97dc140aa2d08d6ad4606 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Sat, 13 Dec 2025 01:49:08 +0800 Subject: [PATCH 1/4] [dm][serial] make DM Kconfig import Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/drivers/serial/Kconfig | 6 +++++- components/drivers/serial/device/Kconfig | 1 + components/drivers/serial/device/SConscript | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 components/drivers/serial/device/Kconfig create mode 100644 components/drivers/serial/device/SConscript diff --git a/components/drivers/serial/Kconfig b/components/drivers/serial/Kconfig index b031c68b6c9..95b375c3e6a 100644 --- a/components/drivers/serial/Kconfig +++ b/components/drivers/serial/Kconfig @@ -1,5 +1,5 @@ menuconfig RT_USING_SERIAL - bool "USING Serial device drivers" + bool "Using Serial device drivers" select RT_USING_DEVICE_IPC select RT_USING_DEVICE default y @@ -36,3 +36,7 @@ menuconfig RT_USING_SERIAL bool "Using serial bypass" default n endif + +if RT_USING_DM && RT_USING_SERIAL + rsource "device/Kconfig" +endif diff --git a/components/drivers/serial/device/Kconfig b/components/drivers/serial/device/Kconfig new file mode 100644 index 00000000000..aaa4c2b7bfc --- /dev/null +++ b/components/drivers/serial/device/Kconfig @@ -0,0 +1 @@ +osource "$(SOC_DM_SERIAL_DIR)/Kconfig" diff --git a/components/drivers/serial/device/SConscript b/components/drivers/serial/device/SConscript new file mode 100644 index 00000000000..31a72472aad --- /dev/null +++ b/components/drivers/serial/device/SConscript @@ -0,0 +1,19 @@ +from building import * + +objs = [] + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../../include'] + +src = [] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs') From 9376225d111fefeb6144ed3af68c169dfeddd6b2 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Sat, 13 Dec 2025 01:50:20 +0800 Subject: [PATCH 2/4] [dm][serial] replace rt_hw_atomic_add to rt_atomic_add, clean code Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/drivers/serial/serial_dm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/drivers/serial/serial_dm.c b/components/drivers/serial/serial_dm.c index dc281cd367a..73a0427e73c 100644 --- a/components/drivers/serial/serial_dm.c +++ b/components/drivers/serial/serial_dm.c @@ -30,11 +30,11 @@ int serial_dev_set_name(struct rt_serial_device *sdev) id = rt_ofw_get_alias_id(sdev->parent.ofw_node, "uart"); } } -#endif +#endif /* RT_USING_OFW */ if (id < 0) { - id = (int)rt_hw_atomic_add(&uid, 1); + id = (int)rt_atomic_add(&uid, 1); } return rt_dm_dev_set_name(&sdev->parent, "uart%u", id); @@ -53,7 +53,7 @@ static int serial_dm_naming_framework_init(void) uid_min = uid_min < 0 ? 0 : (uid_min + 1); rt_hw_atomic_store(&uid, uid_min); -#endif +#endif /* RT_USING_OFW */ return 0; } @@ -154,7 +154,7 @@ struct serial_configure serial_cfg_from_args(char *_str) rt_memset(str, 0, RT_ARRAY_SIZE(earlycon_magic)); } } - #endif + #endif /* RT_USING_OFW */ } return cfg; From cd6451c3e832fb358b691230fb31fe19e84baf9e Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Sat, 13 Dec 2025 01:51:42 +0800 Subject: [PATCH 3/4] [dm][serial] Fixup serial tty check Just return if is not a tty device, don't assert Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/drivers/serial/serial_tty.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/drivers/serial/serial_tty.c b/components/drivers/serial/serial_tty.c index b5c4549da29..aa8054876d4 100644 --- a/components/drivers/serial/serial_tty.c +++ b/components/drivers/serial/serial_tty.c @@ -62,7 +62,11 @@ static char *alloc_device_name(struct rt_serial_device *serial) * must be obtained using the serial_dev_set_name function, * and it should begin with "uart". */ - RT_ASSERT((strlen(serial_name) > strlen("uart")) && (strncmp(serial_name, "uart", 4) == 0)); + if (!serial_name || rt_strlen(serial_name) < 4 || rt_strncmp(serial_name, "uart", 4)) + { + return RT_NULL; + } + long digits_len = (sizeof(TTY_NAME_PREFIX) - 1) /* raw prefix */ + strlen(serial_name + sizeof("uart") - 1) /* suffix of serial device name*/ + 1; /* tailing \0 */ From 99975f0983fedaff975d6cdfa31b588ea337e203 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Sat, 13 Dec 2025 01:59:12 +0800 Subject: [PATCH 4/4] [dm][serial] add new serial driver for DM 1. 8250 serila family (OFW, PCI, DWC, early) 2. Virtual serial (by graphic and input) 3. HVC early serial 4. ARM PL011 serial Signed-off-by: GuEe-GUI <2991707448@qq.com> --- .../drivers/serial/device/8250/8250-dw.c | 351 ++++++++ .../drivers/serial/device/8250/8250-ofw.c | 211 +++++ .../drivers/serial/device/8250/8250-pci.c | 169 ++++ components/drivers/serial/device/8250/8250.h | 86 ++ components/drivers/serial/device/8250/Kconfig | 14 + .../drivers/serial/device/8250/SConscript | 23 + components/drivers/serial/device/8250/core.c | 510 +++++++++++ components/drivers/serial/device/8250/early.c | 162 ++++ components/drivers/serial/device/8250/regs.h | 365 ++++++++ components/drivers/serial/device/Kconfig | 11 + components/drivers/serial/device/SConscript | 6 + .../drivers/serial/device/serial-early-hvc.c | 34 + .../drivers/serial/device/serial-pl011.c | 417 +++++++++ .../drivers/serial/device/virtual/.gitignore | 1 + .../drivers/serial/device/virtual/Kconfig | 17 + .../drivers/serial/device/virtual/SConscript | 39 + .../device/virtual/font-uni2-fixed16.psf | Bin 0 -> 10804 bytes .../serial/device/virtual/font-uni2-vga16.psf | Bin 0 -> 10804 bytes .../drivers/serial/device/virtual/psf.c | 105 +++ .../drivers/serial/device/virtual/psf.h | 92 ++ .../drivers/serial/device/virtual/render.c | 661 +++++++++++++++ .../drivers/serial/device/virtual/render.h | 60 ++ .../drivers/serial/device/virtual/virtual.c | 789 ++++++++++++++++++ 23 files changed, 4123 insertions(+) create mode 100644 components/drivers/serial/device/8250/8250-dw.c create mode 100644 components/drivers/serial/device/8250/8250-ofw.c create mode 100644 components/drivers/serial/device/8250/8250-pci.c create mode 100644 components/drivers/serial/device/8250/8250.h create mode 100644 components/drivers/serial/device/8250/Kconfig create mode 100644 components/drivers/serial/device/8250/SConscript create mode 100644 components/drivers/serial/device/8250/core.c create mode 100644 components/drivers/serial/device/8250/early.c create mode 100644 components/drivers/serial/device/8250/regs.h create mode 100644 components/drivers/serial/device/serial-early-hvc.c create mode 100644 components/drivers/serial/device/serial-pl011.c create mode 100644 components/drivers/serial/device/virtual/.gitignore create mode 100644 components/drivers/serial/device/virtual/Kconfig create mode 100644 components/drivers/serial/device/virtual/SConscript create mode 100644 components/drivers/serial/device/virtual/font-uni2-fixed16.psf create mode 100644 components/drivers/serial/device/virtual/font-uni2-vga16.psf create mode 100644 components/drivers/serial/device/virtual/psf.c create mode 100644 components/drivers/serial/device/virtual/psf.h create mode 100644 components/drivers/serial/device/virtual/render.c create mode 100644 components/drivers/serial/device/virtual/render.h create mode 100644 components/drivers/serial/device/virtual/virtual.c diff --git a/components/drivers/serial/device/8250/8250-dw.c b/components/drivers/serial/device/8250/8250-dw.c new file mode 100644 index 00000000000..b482f05b38f --- /dev/null +++ b/components/drivers/serial/device/8250/8250-dw.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-22 GuEe-GUI first version + */ + +/* + * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the + * LCR is written whilst busy. If it is, then a busy detect interrupt is + * raised, the LCR needs to be rewritten and the uart status register read. + */ + +#include + +#include "8250.h" + +/* Offsets for the DesignWare specific registers */ +#define DW_UART_USR 0x1f /* UART Status Register */ +#define DW_UART_DMASA 0xa8 /* DMA Software Ack */ + +#define OCTEON_UART_USR 0x27 /* UART Status Register */ + +#define RZN1_UART_TDMACR 0x10c /* DMA Control Register Transmit Mode */ +#define RZN1_UART_RDMACR 0x110 /* DMA Control Register Receive Mode */ + +/* DesignWare specific register fields */ +#define DW_UART_MCR_SIRE RT_BIT(6) + +/* Renesas specific register fields */ +#define RZN1_UART_xDMACR_DMA_EN RT_BIT(0) +#define RZN1_UART_xDMACR_1_WORD_BURST (0 << 1) +#define RZN1_UART_xDMACR_4_WORD_BURST (1 << 1) +#define RZN1_UART_xDMACR_8_WORD_BURST (2 << 1) +#define RZN1_UART_xDMACR_BLK_SZ(x) ((x) << 3) + +/* Quirks */ +#define DW_UART_QUIRK_OCTEON RT_BIT(0) +#define DW_UART_QUIRK_ARMADA_38X RT_BIT(1) +#define DW_UART_QUIRK_SKIP_SET_RATE RT_BIT(2) +#define DW_UART_QUIRK_IS_DMA_FC RT_BIT(3) + +struct dw8250_platform_data +{ + rt_uint8_t usr_reg; + rt_uint32_t cpr_val; + rt_uint32_t quirks; +}; + +struct dw8250 +{ + struct serial8250 parent; + struct rt_spinlock spinlock; + + struct rt_clk *pclk; + + rt_bool_t uart_16550_compatible; + struct dw8250_platform_data *platform_data; +}; + +#define to_dw8250(serial8250) rt_container_of(serial8250, struct dw8250, parent) + +static void dw8250_check_lcr(struct serial8250 *serial, int value) +{ + void *offset = (void *)(serial->base + (UART_LCR << serial->regshift)); + int tries = 1000; + + /* Make sure LCR write wasn't ignored */ + while (tries--) + { + rt_uint32_t lcr = serial->serial_in(serial, UART_LCR); + + if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) + { + break; + } + + serial->serial_out(serial, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial->serial_in(serial, UART_RX); + + if (serial->iotype == PORT_MMIO32) + { + HWREG32(offset) = value; + } + else if (serial->iotype == PORT_MMIO32BE) + { + HWREG32(offset) = rt_cpu_to_be32(value); + } + else + { + HWREG8(offset) = value; + } + } +} + +static void dw8250_serial_out32(struct serial8250 *serial, int offset, int value) +{ + struct dw8250 *dw8250 = to_dw8250(serial); + + HWREG32(serial->base + (offset << serial->regshift)) = value; + + if (offset == UART_LCR && !dw8250->uart_16550_compatible) + { + dw8250_check_lcr(serial, value); + } +} + +static rt_uint32_t dw8250_serial_in32(struct serial8250 *serial, int offset) +{ + return HWREG32(serial->base + (offset << serial->regshift)); +} + +static rt_err_t dw8250_isr(struct serial8250 *serial, int irq) +{ + unsigned int iir, status; + struct dw8250 *dw8250 = to_dw8250(serial); + + iir = serial8250_in(serial, UART_IIR); + + /* + * If don't do this in non-DMA mode then the "RX TIMEOUT" interrupt will + * fire forever. + */ + if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) + { + rt_base_t level = rt_spin_lock_irqsave(&dw8250->spinlock); + + status = serial8250_in(serial, UART_LSR); + + if (!(status & (UART_LSR_DR | UART_LSR_BI))) + { + serial8250_in(serial, UART_RX); + } + + rt_spin_unlock_irqrestore(&dw8250->spinlock, level); + } + + if (!(iir & UART_IIR_NO_INT)) + { + rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_RX_IND); + } + + if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) + { + /* Clear the USR */ + serial8250_in(serial, dw8250->platform_data->usr_reg); + } + + return RT_EOK; +} + +static void dw8250_free_resource(struct dw8250 *dw8250) +{ + struct serial8250 *serial = &dw8250->parent; + + if (serial->base) + { + rt_iounmap(serial->base); + } + + if (!rt_is_err_or_null(serial->clk)) + { + rt_clk_disable_unprepare(serial->clk); + rt_clk_put(serial->clk); + } + + if (!rt_is_err_or_null(dw8250->pclk)) + { + rt_clk_disable_unprepare(dw8250->pclk); + rt_clk_put(dw8250->pclk); + } + + rt_free(dw8250); +} + +static void dw8250_serial_remove(struct serial8250 *serial) +{ + struct dw8250 *dw8250 = to_dw8250(serial); + + dw8250_free_resource(dw8250); +} + +static rt_err_t dw8250_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t val; + struct serial8250 *serial; + struct rt_device *dev = &pdev->parent; + struct dw8250 *dw8250 = serial8250_alloc(dw8250); + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + if (!dw8250) + { + return -RT_ENOMEM; + } + + serial = &dw8250->parent; + serial->base = rt_dm_dev_iomap(dev, 0); + + if (!serial->base) + { + err = -RT_EIO; + + goto _free_res; + } + + serial->irq = rt_dm_dev_get_irq(dev, 0); + + if (serial->irq < 0) + { + err = serial->irq; + + goto _free_res; + } + + serial->clk = rt_clk_get_by_name(dev, "baudclk"); + dw8250->pclk = rt_clk_get_by_name(dev, "apb_pclk"); + + if (rt_is_err_or_null(serial->clk)) + { + if ((err = rt_dm_dev_prop_read_u32(dev, "clock-frequency", &serial->freq))) + { + goto _free_res; + } + } + else + { + if ((err = rt_clk_prepare_enable(serial->clk))) + { + goto _free_res; + } + + serial->freq = rt_clk_get_rate(serial->clk); + } + + if (rt_is_err(dw8250->pclk)) + { + err = rt_ptr_err(dw8250->pclk); + + goto _free_res; + } + + if ((err = rt_clk_prepare_enable(dw8250->pclk))) + { + goto _free_res; + } + + if (!rt_dm_dev_prop_read_u32(dev, "reg-io-width", &val) && val == 4) + { + serial->iotype = PORT_MMIO32; + serial->serial_in = &dw8250_serial_in32; + serial->serial_out = &dw8250_serial_out32; + } + + dw8250->uart_16550_compatible = rt_dm_dev_prop_read_bool(dev, "snps,uart-16550-compatible"); + dw8250->platform_data = (struct dw8250_platform_data *)pdev->id->data; + + rt_dm_dev_bind_fwdata(&serial->parent.parent, pdev->parent.ofw_node, &serial->parent); + + dev->user_data = serial; + + serial->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + serial->handle_irq = &dw8250_isr; + serial->remove = &dw8250_serial_remove; + serial->data = dw8250; + + rt_spin_lock_init(&dw8250->spinlock); + + if ((err = serial8250_setup(serial))) + { + goto _free_res; + } + + return RT_EOK; + +_free_res: + dw8250_free_resource(dw8250); + + return err; +} + +static rt_err_t dw8250_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct serial8250 *serial = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + return serial8250_remove(serial); +} + +static const struct dw8250_platform_data dw8250_dw_apb = +{ + .usr_reg = DW_UART_USR, +}; + +static const struct dw8250_platform_data dw8250_octeon_3860_data = +{ + .usr_reg = OCTEON_UART_USR, + .quirks = DW_UART_QUIRK_OCTEON, +}; + +static const struct dw8250_platform_data dw8250_armada_38x_data = +{ + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_ARMADA_38X, +}; + +static const struct dw8250_platform_data dw8250_renesas_rzn1_data = +{ + .usr_reg = DW_UART_USR, + .cpr_val = 0x00012f32, + .quirks = DW_UART_QUIRK_IS_DMA_FC, +}; + +static const struct dw8250_platform_data dw8250_starfive_jh7100_data = +{ + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_SKIP_SET_RATE, +}; + +static const struct rt_ofw_node_id dw8250_ofw_ids[] = +{ + { .type = "ttyS", .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb }, + { .type = "ttyS", .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data }, + { .type = "ttyS", .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data }, + { .type = "ttyS", .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data }, + { .type = "ttyS", .compatible = "starfive,jh7100-uart", .data = &dw8250_starfive_jh7100_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver dw8250_driver = +{ + .name = "dw-apb-uart", + .ids = dw8250_ofw_ids, + + .probe = dw8250_probe, + .remove = dw8250_remove, +}; + +static int dw8250_drv_register(void) +{ + rt_platform_driver_register(&dw8250_driver); + + return 0; +} +INIT_PLATFORM_EXPORT(dw8250_drv_register); diff --git a/components/drivers/serial/device/8250/8250-ofw.c b/components/drivers/serial/device/8250/8250-ofw.c new file mode 100644 index 00000000000..68ce13af3f7 --- /dev/null +++ b/components/drivers/serial/device/8250/8250-ofw.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-09 GuEe-GUI first version + */ + +#include + +#include "8250.h" + +struct ofw_platform_8250 +{ + struct serial8250 parent; + + struct rt_reset_control *rstc; +}; + +#define to_ofw_platform_8250(serial8250) rt_container_of(serial8250, struct ofw_platform_8250, parent) + +static void ofw_platform_8250_free_resource(struct ofw_platform_8250 *op8250) +{ + struct serial8250 *serial = &op8250->parent; + + if (serial->base) + { + rt_iounmap(serial->base); + } + + if (!rt_is_err_or_null(serial->clk)) + { + rt_clk_disable_unprepare(serial->clk); + rt_clk_put(serial->clk); + } + + if (!rt_is_err_or_null(op8250->rstc)) + { + rt_reset_control_put(op8250->rstc); + } + + rt_free(op8250); +} + +static void op8250_remove(struct serial8250 *serial) +{ + struct ofw_platform_8250 *op8250 = to_ofw_platform_8250(serial); + + ofw_platform_8250_free_resource(op8250); +} + +static rt_err_t ofw_platform_8250_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t val; + struct serial8250 *serial; + struct ofw_platform_8250 *op8250; + struct rt_ofw_node *np = pdev->parent.ofw_node; + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + if (rt_ofw_prop_read_bool(np, "used-by-rtas")) + { + return -RT_EBUSY; + } + + op8250 = serial8250_alloc(op8250); + + if (!op8250) + { + return -RT_ENOMEM; + } + + serial = &op8250->parent; + serial->base = rt_ofw_iomap(np, 0); + + if (!serial->base) + { + err = -RT_EIO; + + goto _fail; + } + + serial->irq = rt_ofw_get_irq(np, 0); + + if (serial->irq < 0) + { + err = serial->irq; + + goto _fail; + } + + if (!rt_ofw_prop_read_u32(np, "clock-frequency", &val)) + { + serial->freq = val; + } + else + { + serial->clk = rt_ofw_get_clk(np, 0); + + if (rt_is_err(serial->clk)) + { + err = rt_ptr_err(serial->clk); + goto _fail; + } + + if ((err = rt_clk_prepare_enable(serial->clk))) + { + goto _fail; + } + + serial->freq = rt_clk_get_rate(serial->clk); + } + + if (!rt_ofw_prop_read_u32(np, "reg-shift", &val)) + { + serial->regshift = val; + } + + serial->iotype = PORT_MMIO; + if (!rt_ofw_prop_read_u32(np, "reg-io-width", &val)) + { + switch (val) + { + case 1: + serial->iotype = PORT_MMIO; + break; + + case 2: + serial->iotype = PORT_MMIO16; + break; + + case 4: + serial->iotype = rt_ofw_prop_read_bool(np, "big-endian") ? + PORT_MMIO32BE : PORT_MMIO32; + break; + } + } + + op8250->rstc = rt_ofw_get_reset_control_by_index(np, 0); + + if (rt_is_err(op8250->rstc)) + { + err = rt_ptr_err(op8250->rstc); + goto _fail; + } + + if (op8250->rstc && (err = rt_reset_control_deassert(op8250->rstc))) + { + goto _fail; + } + + rt_dm_dev_bind_fwdata(&serial->parent.parent, pdev->parent.ofw_node, &serial->parent); + + pdev->parent.user_data = serial; + + serial->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + serial->remove = &op8250_remove; + serial->data = op8250; + + if ((err = serial8250_setup(serial))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + ofw_platform_8250_free_resource(op8250); + + return err; +} + +static rt_err_t ofw_platform_8250_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct serial8250 *serial = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + return serial8250_remove(serial); +} + +static const struct rt_ofw_node_id ofw_platform_8250_ofw_ids[] = +{ + { .type = "ttyS", .compatible = "ns16550a" }, + { .type = "ttyS", .compatible = "ns16550" }, +#ifndef RT_SERIAL_8250_BCM7271 + { .type = "ttyS", .compatible = "brcm,bcm7271-uart" }, +#endif + { /* sentinel */ } +}; + +static struct rt_platform_driver ofw_platform_8250_driver = +{ + .name = "8250-ofw", + .ids = ofw_platform_8250_ofw_ids, + + .probe = ofw_platform_8250_probe, + .remove = ofw_platform_8250_remove, +}; + +static int ofw_platform_8250_drv_register(void) +{ + rt_platform_driver_register(&ofw_platform_8250_driver); + + return 0; +} +INIT_PLATFORM_EXPORT(ofw_platform_8250_drv_register); diff --git a/components/drivers/serial/device/8250/8250-pci.c b/components/drivers/serial/device/8250/8250-pci.c new file mode 100644 index 00000000000..5099b89bbe1 --- /dev/null +++ b/components/drivers/serial/device/8250/8250-pci.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-09 GuEe-GUI first version + */ + +#include + +#include "8250.h" + +#define IO_PORT_BAR 0 + +enum +{ + PCI_SERIAL = 0, + PCI_SERIAL2 = 2, + PCI_SERIAL4 = 4, +}; + +enum +{ + SERIAL_8250 = 0, + SERIAL_16450, + SERIAL_16550, + SERIAL_16650, + SERIAL_16750, + SERIAL_16850, + SERIAL_16950, +}; + +struct pci_serial +{ + struct serial8250 parent; + struct rt_spinlock spinlock; + + struct rt_pci_device *pci_dev; + + rt_uint8_t type; + rt_uint8_t compat; +}; + +#define to_pci_serial(raw) rt_container_of(raw, struct pci_serial, parent) + +static rt_err_t pci_serial_isr(struct serial8250 *serial, int irq) +{ + rt_uint32_t iir; + void *base = serial->base; + + iir = HWREG8(base + UART_IIR); + + if (!(iir & UART_IIR_NO_INT)) + { + rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_RX_IND); + } + + return RT_EOK; +} + +static rt_ubase_t pci_serial_clock(struct pci_serial *pci_serial) +{ + rt_ubase_t clock = 1843200; + + return clock; +} + +static void pci_serial_free_resource(struct pci_serial *pci_serial) +{ + struct serial8250 *serial = &pci_serial->parent; + + if (serial->base) + { + rt_iounmap(serial->base); + } + + rt_free(pci_serial); +} + +static void pci_8250serial_remove(struct serial8250 *serial) +{ + struct pci_serial *pci_serial = to_pci_serial(serial); + + rt_pci_irq_mask(pci_serial->pci_dev); + + pci_serial_free_resource(pci_serial); +} + +static rt_err_t pci_serial_probe(struct rt_pci_device *pdev) +{ + rt_err_t err; + struct serial8250 *serial; + struct pci_serial *pci_serial = serial8250_alloc(pci_serial); + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + if (!pci_serial) + { + return -RT_ENOMEM; + } + + serial = &pci_serial->parent; + serial->size = pdev->resource[IO_PORT_BAR].size; + serial->base = rt_pci_iomap(pdev, IO_PORT_BAR); + + if (!serial->base) + { + err = -RT_EIO; + + goto _free_res; + } + + pdev->parent.user_data = serial; + + serial->irq = pdev->irq; + + serial->parent.ops = &serial8250_uart_ops; + serial->parent.config = config; + serial->freq = pci_serial_clock(pci_serial); + serial->handle_irq = &pci_serial_isr; + serial->iotype = PORT_MMIO; + serial->remove = &pci_8250serial_remove; + serial->data = pci_serial; + + pci_serial->pci_dev = pdev; + pci_serial->type = (rt_ubase_t)pdev->id->data; + rt_spin_lock_init(&pci_serial->spinlock); + rt_pci_read_config_u8(pdev, PCIR_PROGIF, &pci_serial->compat); + + if ((err = serial8250_setup(serial))) + { + goto _free_res; + } + + rt_pci_irq_unmask(pci_serial->pci_dev); + + return RT_EOK; + +_free_res: + pci_serial_free_resource(pci_serial); + + return err; +} + +static rt_err_t pci_serial_remove(struct rt_pci_device *pdev) +{ + struct serial8250 *serial = pdev->parent.user_data; + + return serial8250_remove(serial); +} + +static const struct rt_pci_device_id pci_serial_pci_ids[] = +{ + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0002), .data = (void *)PCI_SERIAL, }, + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0003), .data = (void *)PCI_SERIAL2, }, + { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0004), .data = (void *)PCI_SERIAL4, }, + { /* sentinel */ } +}; + +static struct rt_pci_driver pci_serial_driver = +{ + .name = "serial-pci", + + .ids = pci_serial_pci_ids, + .probe = pci_serial_probe, + .remove = pci_serial_remove, +}; +RT_PCI_DRIVER_EXPORT(pci_serial_driver); diff --git a/components/drivers/serial/device/8250/8250.h b/components/drivers/serial/device/8250/8250.h new file mode 100644 index 00000000000..8416f1dc493 --- /dev/null +++ b/components/drivers/serial/device/8250/8250.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-16 GuEe-GUI first version + */ + +#ifndef __SERIAL_8250_H__ +#define __SERIAL_8250_H__ + +#include +#include +#include +#include + +#include + +#include "regs.h" + +enum +{ + PORT_UNKNOWN, + PORT_IO, + PORT_MMIO, + PORT_MMIO16, + PORT_MMIO32, + PORT_MMIO32BE, +}; + +struct serial8250 +{ + struct rt_serial_device parent; + struct rt_clk *clk; + + int irq; + void *base; + rt_size_t size; + rt_uint32_t freq; /* frequency */ + rt_uint32_t regshift; /* reg offset shift */ + rt_uint8_t iotype; /* io access style */ + + struct rt_spinlock spinlock; + + rt_err_t (*serial_ios)(struct serial8250 *, struct serial_configure *ios); + rt_uint32_t (*serial_in)(struct serial8250 *, int offset); + void (*serial_out)(struct serial8250 *, int offset, int value); + rt_err_t (*serial_dma_enable)(struct serial8250 *, rt_bool_t enabled); + rt_ssize_t (*serial_dma_tx)(struct serial8250 *, const rt_uint8_t *buf, rt_size_t size); + rt_ssize_t (*serial_dma_rx)(struct serial8250 *, rt_uint8_t *buf, rt_size_t size); + rt_err_t (*handle_irq)(struct serial8250 *, int irq); + + /* Free all resource (and parent) by child */ + void (*remove)(struct serial8250 *); + void *data; +}; + +#define serial8250_alloc(obj) rt_calloc(1, sizeof(typeof(*obj))) +#define raw_to_serial8250(raw_serial) rt_container_of(raw_serial, struct serial8250, parent) + +rt_err_t serial8250_config(struct serial8250 *serial, const char *options); +rt_err_t serial8250_setup(struct serial8250 *serial); +rt_err_t serial8250_remove(struct serial8250 *serial); + +rt_uint32_t serial8250_in(struct serial8250 *serial, int offset); +void serial8250_out(struct serial8250 *serial, int offset, int value); +void serial8250_dma_tx_done(struct serial8250 *serial); +void serial8250_dma_rx_done(struct serial8250 *serial, int recv_len); +rt_err_t serial8250_ios(struct serial8250 *serial, struct serial_configure *cfg); + +rt_err_t serial8250_uart_configure(struct rt_serial_device *raw_serial, struct serial_configure *cfg); +rt_err_t serial8250_uart_control(struct rt_serial_device *raw_serial, int cmd, void *arg); +int serial8250_uart_putc(struct rt_serial_device *raw_serial, char c); +int serial8250_uart_getc(struct rt_serial_device *raw_serial); +rt_ssize_t serial8250_uart_dma_transmit(struct rt_serial_device *raw_serial, + rt_uint8_t *buf, rt_size_t size, int direction); + +int serial8250_early_putc(struct rt_serial_device *raw_serial, char c); +rt_err_t serial8250_early_fdt_setup(struct serial8250 *serial, struct rt_fdt_earlycon *con, const char *options); + +extern struct serial8250 early_serial8250; +extern const struct rt_uart_ops serial8250_uart_ops; + +#endif /* __SERIAL_8250_H__ */ diff --git a/components/drivers/serial/device/8250/Kconfig b/components/drivers/serial/device/8250/Kconfig new file mode 100644 index 00000000000..f482731bfd0 --- /dev/null +++ b/components/drivers/serial/device/8250/Kconfig @@ -0,0 +1,14 @@ +menuconfig RT_SERIAL_8250 + bool "8250 Serial Family" + default n + +config RT_SERIAL_8250_DW + bool "Synopsys DesignWare 8250" + depends on RT_SERIAL_8250 + default n + +config RT_SERIAL_8250_PCI + bool "8250 PCI/2x/4x" + depends on RT_SERIAL_8250 + depends on RT_USING_PCI + default n diff --git a/components/drivers/serial/device/8250/SConscript b/components/drivers/serial/device/8250/SConscript new file mode 100644 index 00000000000..7d24e6df97a --- /dev/null +++ b/components/drivers/serial/device/8250/SConscript @@ -0,0 +1,23 @@ +from building import * + +group = [] + +if not GetDepend(['RT_SERIAL_8250']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +src = ['core.c', 'early.c'] + +if GetDepend(['RT_SERIAL_8250_DW']): + src += ['8250-dw.c'] + +if GetDepend(['RT_USING_OFW', 'RT_USING_RESET']): + src += ['8250-ofw.c'] + +if GetDepend(['RT_SERIAL_8250_PCI']): + src += ['8250-pci.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) +Return('group') diff --git a/components/drivers/serial/device/8250/core.c b/components/drivers/serial/device/8250/core.c new file mode 100644 index 00000000000..2c00ffdb9cf --- /dev/null +++ b/components/drivers/serial/device/8250/core.c @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-16 GuEe-GUI first version + */ + +#include "8250.h" + +rt_err_t serial8250_config(struct serial8250 *serial, const char *options) +{ + rt_err_t ret = -RT_EINVAL; + + if (serial) + { + char *arg; + rt_bool_t has_iotype = RT_FALSE; + + /* + * uart8250,io,[,options] + * uart8250,mmio,[,options] + * uart8250,mmio16,[,options] + * uart8250,mmio32,[,options] + * uart8250,mmio32be,[,options] + * uart8250,0x[,options] + */ + serial_for_each_args(arg, options) + { + if (!rt_strcmp(arg, "uart8250")) + { + ret = RT_EOK; + continue; + } + /* User call error */ + if (ret) + { + break; + } + if (!rt_strncmp(arg, "0x", 2)) + { + serial->base = serial_base_from_args(arg); + continue; + } + if (!has_iotype) + { + const struct + { + char *param; + int type; + } iotype_table[] = + { + { "io", PORT_IO }, + { "mmio", PORT_MMIO }, + { "mmio16", PORT_MMIO16 }, + { "mmio32", PORT_MMIO32 }, + { "mmio32be", PORT_MMIO32BE }, + }; + + serial->iotype = PORT_MMIO32; + + for (int i = 0; i < RT_ARRAY_SIZE(iotype_table); ++i) + { + if (!rt_strcmp(arg, iotype_table[i].param)) + { + serial->iotype = iotype_table[i].type; + break; + } + } + + has_iotype = RT_TRUE; + continue; + } + + serial->parent.config = serial_cfg_from_args(arg); + break; + } + + if (!serial->size) + { + serial->size = 0x1000; + } + } + + return ret; +} + +static void serial8250_isr(int irqno, void *param) +{ + struct serial8250 *serial = (struct serial8250 *)param; + + if (serial->handle_irq) + { + serial->handle_irq(serial, irqno); + } + else + { + rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_RX_IND); + } +} + +static rt_err_t serial8250_dma_enable_dummy(struct serial8250 *serial, rt_bool_t enabled) +{ + return RT_EOK; +} + +static rt_ssize_t serial8250_dma_tx_dummy(struct serial8250 *serial, + const rt_uint8_t *buf, rt_size_t size) +{ + return 0; +} + +static rt_ssize_t serial8250_dma_rx_dummy(struct serial8250 *serial, + rt_uint8_t *buf, rt_size_t size) +{ + return 0; +} + +rt_err_t serial8250_setup(struct serial8250 *serial) +{ + rt_uint32_t flags; + rt_err_t err = RT_EOK; + const char *uart_name; + char dev_name[RT_NAME_MAX]; + + if (serial) + { + struct rt_device *dev = &serial->parent.parent; + + rt_spin_lock_init(&serial->spinlock); + flags = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX; + + if (!serial->regshift) + { + rt_dm_dev_prop_read_u32(dev, "reg-shift", &serial->regshift); + } + + if (serial->iotype == PORT_UNKNOWN) + { + rt_uint32_t width = sizeof(rt_uint32_t); + + rt_dm_dev_prop_read_u32(dev, "reg-io-width", &width); + + switch (width) + { + case sizeof(rt_uint8_t): + serial->iotype = PORT_MMIO; + break; + + case sizeof(rt_uint16_t): + serial->iotype = PORT_MMIO16; + break; + + case sizeof(rt_uint32_t): + default: + serial->iotype = rt_dm_dev_is_big_endian(dev) ? PORT_MMIO32BE : PORT_MMIO32; + break; + } + } + + serial->serial_in = serial->serial_in ? : &serial8250_in; + serial->serial_out = serial->serial_out ? : &serial8250_out; + serial->serial_dma_enable = serial->serial_dma_enable ? : &serial8250_dma_enable_dummy; + + if (serial->serial_dma_tx) + { + flags |= RT_DEVICE_FLAG_DMA_TX; + } + else + { + serial->serial_dma_tx = &serial8250_dma_tx_dummy; + } + + if (serial->serial_dma_rx) + { + flags |= RT_DEVICE_FLAG_DMA_RX; + } + else + { + serial->serial_dma_rx = &serial8250_dma_rx_dummy; + } + + serial_dev_set_name(&serial->parent); + uart_name = rt_dm_dev_get_name(&serial->parent.parent); + + rt_hw_serial_register(&serial->parent, uart_name, flags, serial->data); + + rt_snprintf(dev_name, sizeof(dev_name), "%s-8250", uart_name); + rt_hw_interrupt_install(serial->irq, serial8250_isr, serial, dev_name); + } + else + { + err = -RT_EINVAL; + } + + return err; +} + +rt_err_t serial8250_remove(struct serial8250 *serial) +{ + rt_err_t err; + + rt_iounmap((void *)serial->base); + serial->base = RT_NULL; + + rt_hw_interrupt_mask(serial->irq); + rt_pic_detach_irq(serial->irq, serial); + + err = rt_device_unregister(&serial->parent.parent); + + if (!err && serial->remove) + { + serial->remove(serial); + } + + return err; +} + +rt_uint32_t serial8250_in(struct serial8250 *serial, int offset) +{ + rt_uint32_t ret = 0; + offset <<= serial->regshift; + + switch (serial->iotype) + { + case PORT_MMIO: + ret = HWREG8(serial->base + offset); + break; + case PORT_MMIO16: + ret = HWREG16(serial->base + offset); + break; + case PORT_MMIO32: + ret = HWREG32(serial->base + offset); + break; + case PORT_MMIO32BE: + ret = rt_cpu_to_be32(HWREG32(serial->base + offset)); + break; +#ifdef ARCH_SUPPORT_PIO + case PORT_IO: + ret = inb(serial->base + offset); + break; +#endif + default: + break; + } + + return ret; +} + +void serial8250_out(struct serial8250 *serial, int offset, int value) +{ + offset <<= serial->regshift; + + switch (serial->iotype) + { + case PORT_MMIO: + HWREG8(serial->base + offset) = value; + break; + case PORT_MMIO16: + HWREG16(serial->base + offset) = value; + break; + case PORT_MMIO32: + HWREG32(serial->base + offset) = value; + break; + case PORT_MMIO32BE: + HWREG32(serial->base + offset) = rt_cpu_to_be32(value); + break; +#ifdef ARCH_SUPPORT_PIO + case PORT_IO: + outb(serial->base + offset, value); + break; +#endif + default: + break; + } +} + +void serial8250_dma_tx_done(struct serial8250 *serial) +{ + rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_TX_DMADONE); +} + +void serial8250_dma_rx_done(struct serial8250 *serial, int recv_len) +{ + rt_hw_serial_isr(&serial->parent, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8)); +} + +rt_inline rt_ubase_t baudrate_to_divisor(rt_ubase_t freq, rt_ubase_t baudrate) +{ + return (freq + baudrate * 8) / (16 * baudrate); +} + +static void serial8250_hw_ios(struct serial8250 *serial, struct serial_configure *cfg) +{ + rt_uint32_t ier; + + /* Disable RX interrupt */ + ier = serial->serial_in(serial, UART_IER); + ier &= ~UART_IER_RDI; + ier |= UART_IER_UUE; + serial->serial_out(serial, UART_IER, ier); + + /* Enable FIFO, Clear FIFO */ + serial->serial_out(serial, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + + /* DTR + RTS */ + serial->serial_out(serial, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS); + + if (serial->freq) + { + rt_uint32_t wlen = cfg->data_bits - DATA_BITS_5 + UART_LCR_WLEN5; + rt_uint32_t divisor = baudrate_to_divisor(serial->freq, cfg->baud_rate); + + /* Enable access DLL & DLH */ + serial->serial_out(serial, UART_LCR, serial->serial_in(serial, UART_LCR) | UART_LCR_DLAB); + serial->serial_out(serial, UART_DLL, (divisor & 0xff)); + serial->serial_out(serial, UART_DLM, (divisor >> 8) & 0xff); + /* Clear DLAB bit */ + serial->serial_out(serial, UART_LCR, serial->serial_in(serial, UART_LCR) & (~UART_LCR_DLAB)); + + serial->serial_out(serial, UART_LCR, (serial->serial_in(serial, UART_LCR) & (~wlen)) | wlen); + serial->serial_out(serial, UART_LCR, serial->serial_in(serial, UART_LCR) & (~UART_LCR_STOP)); + serial->serial_out(serial, UART_LCR, serial->serial_in(serial, UART_LCR) & (~UART_LCR_PARITY)); + } + + /* Enable RX interrupt */ + ier |= UART_IER_RDI; + serial->serial_out(serial, UART_IER, ier); +} + +rt_err_t serial8250_ios(struct serial8250 *serial, struct serial_configure *cfg) +{ + rt_err_t err = RT_EOK; + rt_uint32_t count = 10000; + rt_uint32_t divisor = baudrate_to_divisor(serial->freq, cfg->baud_rate); + + /* Flush FIFO */ + while (!(serial->serial_in(serial, UART_LSR) & UART_LSR_TEMT) && count--) + { + rt_hw_cpu_relax(); + } + + /* Need to increase CLK frequency */ + if (divisor == 0 && serial->freq) + { + if (serial->clk) + { + for (rt_ubase_t round = serial->freq, freq = 0; ; round += round) + { + if (baudrate_to_divisor(round, cfg->baud_rate)) + { + freq = rt_clk_round_rate(serial->clk, freq); + + if (freq < 0) + { + continue; + } + + rt_clk_disable_unprepare(serial->clk); + + err = rt_clk_set_rate(serial->clk, freq); + + if (!err) + { + serial->freq = rt_clk_get_rate(serial->clk); + } + + rt_clk_prepare_enable(serial->clk); + + break; + } + } + } + else + { + err = -RT_ENOSYS; + } + } + + if (!err) + { + serial8250_hw_ios(serial, cfg); + } + + return err; +} + +rt_err_t serial8250_uart_configure(struct rt_serial_device *raw_serial, struct serial_configure *cfg) +{ + rt_err_t err; + struct serial8250 *serial = raw_to_serial8250(raw_serial); + + if (serial->serial_ios) + { + err = serial->serial_ios(serial, cfg); + } + else + { + err = serial8250_ios(serial, cfg); + } + + return err; +} + +rt_err_t serial8250_uart_control(struct rt_serial_device *raw_serial, int cmd, void *arg) +{ + rt_uint32_t value; + rt_err_t err = RT_EOK; + rt_ubase_t ctrl = (rt_ubase_t)arg; + struct serial8250 *serial = raw_to_serial8250(raw_serial); + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + if (ctrl == RT_DEVICE_FLAG_INT_RX) + { + /* Disable RX irq */ + value = serial->serial_in(serial, UART_IER); + value &= ~UART_IER_RDI; + value |= UART_IER_UUE; + serial->serial_out(serial, UART_IER, value); + rt_hw_interrupt_mask(serial->irq); + } + else if (ctrl == RT_DEVICE_FLAG_DMA_RX) + { + err = serial->serial_dma_enable(serial, RT_FALSE); + } + break; + + case RT_DEVICE_CTRL_SET_INT: + if (ctrl == RT_DEVICE_FLAG_INT_RX) + { + /* Enable RX irq */ + value = serial->serial_in(serial, UART_IER); + serial->serial_out(serial, UART_IER, value | UART_IER_RDI); + rt_hw_interrupt_umask(serial->irq); + } + else if (ctrl == RT_DEVICE_FLAG_DMA_RX) + { + err = serial->serial_dma_enable(serial, RT_TRUE); + } + break; + + case RT_DEVICE_CTRL_CLOSE: + err = serial->serial_dma_enable(serial, RT_FALSE); + break; + + default: + err = -RT_EINVAL; + break; + } + + return err; +} + +int serial8250_uart_putc(struct rt_serial_device *raw_serial, char c) +{ + struct serial8250 *serial = raw_to_serial8250(raw_serial); + + while (!(serial->serial_in(serial, UART_LSR) & UART_LSR_THRE)) + { + rt_hw_cpu_relax(); + } + + serial->serial_out(serial, UART_TX, c); + + return 1; +} + +int serial8250_uart_getc(struct rt_serial_device *raw_serial) +{ + int ch = -1; + struct serial8250 *serial = raw_to_serial8250(raw_serial); + + if ((serial->serial_in(serial, UART_LSR) & UART_LSR_DR)) + { + ch = serial->serial_in(serial, UART_RX) & 0xff; + } + + return ch; +} + +rt_ssize_t serial8250_uart_dma_transmit(struct rt_serial_device *raw_serial, + rt_uint8_t *buf, rt_size_t size, int direction) +{ + rt_ssize_t res = 0; + struct serial8250 *serial = raw_to_serial8250(raw_serial); + + if (direction == RT_SERIAL_DMA_TX) + { + res = serial->serial_dma_tx(serial, (const rt_uint8_t *)buf, size); + } + else if (direction == RT_SERIAL_DMA_RX) + { + res = serial->serial_dma_rx(serial, buf, size); + } + + return res; +} + +const struct rt_uart_ops serial8250_uart_ops = +{ + .configure = serial8250_uart_configure, + .control = serial8250_uart_control, + .putc = serial8250_uart_putc, + .getc = serial8250_uart_getc, + .dma_transmit = serial8250_uart_dma_transmit, +}; diff --git a/components/drivers/serial/device/8250/early.c b/components/drivers/serial/device/8250/early.c new file mode 100644 index 00000000000..75fdb178736 --- /dev/null +++ b/components/drivers/serial/device/8250/early.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-16 GuEe-GUI first version + */ + +#include "8250.h" + +struct serial8250 early_serial8250 = { 0 }; + +static rt_uint32_t serial8250_early_in(struct serial8250 *serial, int offset) +{ + return serial8250_in(serial, offset); +} + +static void serial8250_early_out(struct serial8250 *serial, int offset, int value) +{ + serial8250_out(serial, offset, value); +} + +int serial8250_early_putc(struct rt_serial_device *raw_serial, char c) +{ + if (raw_serial) + { + /* FIFO and shifting register empty */ + const int uart_lsr_both_empty = (UART_LSR_TEMT | UART_LSR_THRE); + struct serial8250 *serial = rt_container_of(raw_serial, struct serial8250, parent); + + serial8250_early_out(serial, UART_TX, c); + + while ((serial8250_early_in(serial, UART_LSR) & uart_lsr_both_empty) != uart_lsr_both_empty) + { + rt_hw_cpu_relax(); + } + } + + return 1; +} + +static void init_serial(struct serial8250 *serial) +{ + unsigned char c; + rt_uint32_t ier, divisor; + + serial8250_early_out(serial, UART_LCR, 0x3); /* 8n1 */ + ier = serial8250_early_in(serial, UART_IER); + serial8250_early_out(serial, UART_IER, ier & UART_IER_UUE); /* No interrupt */ + serial8250_early_out(serial, UART_FCR, 0); /* No fifo */ + serial8250_early_out(serial, UART_MCR, 0x3); /* DTR + RTS */ + + if (serial->freq) + { + divisor = RT_DIV_ROUND_CLOSEST(serial->freq, 16 * serial->parent.config.baud_rate); + c = serial8250_early_in(serial, UART_LCR); + serial8250_early_out(serial, UART_LCR, c | UART_LCR_DLAB); + serial8250_early_out(serial, UART_DLL, divisor & 0xff); + serial8250_early_out(serial, UART_DLM, (divisor >> 8) & 0xff); + serial8250_early_out(serial, UART_LCR, c & ~UART_LCR_DLAB); + } +} + +static void serial8250_early_kick(struct rt_fdt_earlycon *con, int why) +{ + struct serial8250 *serial = raw_to_serial8250(con->data); + + switch (why) + { + case FDT_EARLYCON_KICK_UPDATE: + serial->base = rt_ioremap((void *)con->mmio, con->size); + break; + + case FDT_EARLYCON_KICK_COMPLETED: + rt_iounmap(serial->base); + break; + + default: + break; + } +} + +rt_err_t serial8250_early_fdt_setup(struct serial8250 *serial, struct rt_fdt_earlycon *con, const char *options) +{ + rt_err_t ret = RT_EOK; + + if (!serial->base && con) + { + serial8250_config(serial, options); + con->mmio = (rt_ubase_t)serial->base; + con->size = serial->size; + } + + if (serial->base && con) + { + serial->base = rt_ioremap_early((void *)serial->base, serial->size); + } + + if (serial->base && con) + { + con->console_putc = (typeof(con->console_putc))&serial8250_early_putc; + con->console_kick = serial8250_early_kick; + con->data = &serial->parent; + + if (!serial->parent.config.baud_rate) + { + /* Assume the device was initialized, only mask interrupts */ + rt_uint32_t ier = serial8250_early_in(serial, UART_IER); + serial8250_early_out(serial, UART_IER, ier & UART_IER_UUE); + } + else + { + init_serial(serial); + } + } + else + { + ret = -RT_ERROR; + } + + return ret; +} + +static void common_init(struct serial8250 *serial, struct rt_fdt_earlycon *con) +{ + serial->base = (void *)con->mmio; + serial->size = con->size; + serial->iotype = PORT_MMIO32; +} + +static rt_err_t common_early_setup(struct rt_fdt_earlycon *con, const char *options) +{ + struct serial8250 *serial = &early_serial8250; + + common_init(serial, con); + serial->regshift = 2; + fdt_getprop_u32(con->fdt, con->nodeoffset, "reg-shift", &serial->regshift, RT_NULL); + + return serial8250_early_fdt_setup(serial, con, options); +} +RT_FDT_EARLYCON_EXPORT(bcm2835aux, "uart8250", "brcm,bcm2835-aux-uart", common_early_setup); +RT_FDT_EARLYCON_EXPORT(tegra20, "uart8250", "nvidia,tegra20-uart", common_early_setup); +RT_FDT_EARLYCON_EXPORT(dw8250, "uart8250", "snps,dw-apb-uart", common_early_setup); +RT_FDT_EARLYCON_EXPORT(ns16550a, "uart8250", "ns16550a", common_early_setup); +RT_FDT_EARLYCON_EXPORT(ns16550, "uart8250", "ns16550", common_early_setup); + +#ifdef RT_USING_8250_OMAP +static rt_err_t omap8250_early_setup(struct rt_fdt_earlycon *con, const char *options) +{ + struct serial8250 *serial = &early_serial8250; + + common_init(serial, con); + serial->regshift = 2; + + return serial8250_early_fdt_setup(serial, con, options); +} +RT_FDT_EARLYCON_EXPORT(omap8250, "uart8250", "ti,omap2-uart", omap8250_early_setup); +RT_FDT_EARLYCON_EXPORT(omap8250, "uart8250", "ti,omap3-uart", omap8250_early_setup); +RT_FDT_EARLYCON_EXPORT(omap8250, "uart8250", "ti,omap4-uart", omap8250_early_setup); +#endif /* RT_USING_8250_OMAP */ diff --git a/components/drivers/serial/device/8250/regs.h b/components/drivers/serial/device/8250/regs.h new file mode 100644 index 00000000000..6f05efe87ad --- /dev/null +++ b/components/drivers/serial/device/8250/regs.h @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SERIAL_8250_REGS_H__ +#define __SERIAL_8250_REGS_H__ + +/* + * DLAB=0 + */ +#define UART_RX 0 /* In: Receive buffer */ +#define UART_TX 0 /* Out: Transmit buffer */ + +#define UART_IER 1 /* Out: Interrupt Enable Register */ +#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ +/* + * Sleep mode for ST16650 and TI16750. For the ST16650, EFR[4]=1 + */ +#define UART_IERX_SLEEP 0x10 /* Enable sleep mode */ + +#define UART_IIR 2 /* In: Interrupt ID Register */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x0e /* Mask for the interrupt ID */ +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ + +#define UART_IIR_BUSY 0x07 /* DesignWare APB Busy Detect */ + +#define UART_IIR_RX_TIMEOUT 0x0c /* OMAP RX Timeout interrupt */ +#define UART_IIR_XOFF 0x10 /* OMAP XOFF/Special Character */ +#define UART_IIR_CTS_RTS_DSR 0x20 /* OMAP CTS/RTS/DSR Change */ + +#define UART_FCR 2 /* Out: FIFO Control Register */ +#define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ +#define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ +#define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */ +#define UART_FCR_DMA_SELECT 0x08 /* For DMA applications */ +/* + * Note: The FIFO trigger levels are chip specific: + * RX:76 = 00 01 10 11 TX:54 = 00 01 10 11 + * PC16550D: 1 4 8 14 xx xx xx xx + * TI16C550A: 1 4 8 14 xx xx xx xx + * TI16C550C: 1 4 8 14 xx xx xx xx + * ST16C550: 1 4 8 14 xx xx xx xx + * ST16C650: 8 16 24 28 16 8 24 30 PORT_16650V2 + * NS16C552: 1 4 8 14 xx xx xx xx + * ST16C654: 8 16 56 60 8 16 32 56 PORT_16654 + * TI16C750: 1 16 32 56 xx xx xx xx PORT_16750 + * TI16C752: 8 16 56 60 8 16 32 56 + * OX16C950: 16 32 112 120 16 32 64 112 PORT_16C950 + * Tegra: 1 4 8 14 16 8 4 1 PORT_TEGRA + */ +#define UART_FCR_R_TRIG_00 0x00 +#define UART_FCR_R_TRIG_01 0x40 +#define UART_FCR_R_TRIG_10 0x80 +#define UART_FCR_R_TRIG_11 0xc0 +#define UART_FCR_T_TRIG_00 0x00 +#define UART_FCR_T_TRIG_01 0x10 +#define UART_FCR_T_TRIG_10 0x20 +#define UART_FCR_T_TRIG_11 0x30 + +#define UART_FCR_TRIGGER_MASK 0xC0 /* Mask for the FIFO trigger range */ +#define UART_FCR_TRIGGER_1 0x00 /* Mask for trigger set at 1 */ +#define UART_FCR_TRIGGER_4 0x40 /* Mask for trigger set at 4 */ +#define UART_FCR_TRIGGER_8 0x80 /* Mask for trigger set at 8 */ +#define UART_FCR_TRIGGER_14 0xC0 /* Mask for trigger set at 14 */ +/* 16650 definitions */ +#define UART_FCR6_R_TRIGGER_8 0x00 /* Mask for receive trigger set at 1 */ +#define UART_FCR6_R_TRIGGER_16 0x40 /* Mask for receive trigger set at 4 */ +#define UART_FCR6_R_TRIGGER_24 0x80 /* Mask for receive trigger set at 8 */ +#define UART_FCR6_R_TRIGGER_28 0xC0 /* Mask for receive trigger set at 14 */ +#define UART_FCR6_T_TRIGGER_16 0x00 /* Mask for transmit trigger set at 16 */ +#define UART_FCR6_T_TRIGGER_8 0x10 /* Mask for transmit trigger set at 8 */ +#define UART_FCR6_T_TRIGGER_24 0x20 /* Mask for transmit trigger set at 24 */ +#define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */ +#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode (TI16C750 and some Freescale UARTs) */ + +#define UART_FCR_R_TRIG_SHIFT 6 +#define UART_FCR_R_TRIG_BITS(x) (((x) & UART_FCR_TRIGGER_MASK) >> UART_FCR_R_TRIG_SHIFT) +#define UART_FCR_R_TRIG_MAX_STATE 4 + +#define UART_LCR 3 /* Out: Line Control Register */ +/* + * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting + * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. + */ +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 bit, 1=2 bits */ +#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +/* + * Access to some registers depends on register access / configuration mode. + */ +#define UART_LCR_CONF_MODE_A UART_LCR_DLAB /* Configutation mode A */ +#define UART_LCR_CONF_MODE_B 0xBF /* Configutation mode B */ + +#define UART_MCR 4 /* Out: Modem Control Register */ +#define UART_MCR_CLKSEL 0x80 /* Divide clock by 4 (TI16C752, EFR[4]=1) */ +#define UART_MCR_TCRTLR 0x40 /* Access TCR/TLR (TI16C752, EFR[4]=1) */ +#define UART_MCR_XONANY 0x20 /* Enable Xon Any (TI16C752, EFR[4]=1) */ +#define UART_MCR_AFE 0x20 /* Enable auto-RTS/CTS (TI16C550C/TI16C750) */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_OUT1 0x04 /* Out1 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR complement */ + +#define UART_LSR 5 /* In: Line Status Register */ +#define UART_LSR_FIFOE 0x80 /* Fifo error */ +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ +#define UART_LSR_BRK_ERROR_BITS (UART_LSR_BI|UART_LSR_FE|UART_LSR_PE|UART_LSR_OE) + +#define UART_MSR 6 /* In: Modem Status Register */ +#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ +#define UART_MSR_RI 0x40 /* Ring Indicator */ +#define UART_MSR_DSR 0x20 /* Data Set Ready */ +#define UART_MSR_CTS 0x10 /* Clear to Send */ +#define UART_MSR_DDCD 0x08 /* Delta DCD */ +#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ +#define UART_MSR_DDSR 0x02 /* Delta DSR */ +#define UART_MSR_DCTS 0x01 /* Delta CTS */ +#define UART_MSR_ANY_DELTA (UART_MSR_DDCD|UART_MSR_TERI|UART_MSR_DDSR|UART_MSR_DCTS) + +#define UART_SCR 7 /* I/O: Scratch Register */ + +/* + * DLAB=1 + */ +#define UART_DLL 0 /* Out: Divisor Latch Low */ +#define UART_DLM 1 /* Out: Divisor Latch High */ +#define UART_DIV_MAX 0xFFFF /* Max divisor value */ + +/* + * LCR=0xBF (or DLAB=1 for 16C660) + */ +#define UART_EFR 2 /* I/O: Extended Features Register */ +#define UART_XR_EFR 9 /* I/O: Extended Features Register (XR17D15x) */ +#define UART_EFR_CTS 0x80 /* CTS flow control */ +#define UART_EFR_RTS 0x40 /* RTS flow control */ +#define UART_EFR_SCD 0x20 /* Special character detect */ +#define UART_EFR_ECB 0x10 /* Enhanced control bit */ +/* + * the low four bits control software flow control + */ + +/* + * LCR=0xBF, TI16C752, ST16650, ST16650A, ST16654 + */ +#define UART_XON1 4 /* I/O: Xon character 1 */ +#define UART_XON2 5 /* I/O: Xon character 2 */ +#define UART_XOFF1 6 /* I/O: Xoff character 1 */ +#define UART_XOFF2 7 /* I/O: Xoff character 2 */ + +/* + * EFR[4]=1 MCR[6]=1, TI16C752 + */ +#define UART_TI752_TCR 6 /* I/O: transmission control register */ +#define UART_TI752_TLR 7 /* I/O: trigger level register */ + +/* + * LCR=0xBF, XR16C85x + */ +#define UART_TRG 0 /* FCTR bit 7 selects Rx or Tx In: Fifo count Out: Fifo custom trigger levels */ +/* + * These are the definitions for the Programmable Trigger Register + */ +#define UART_TRG_1 0x01 +#define UART_TRG_4 0x04 +#define UART_TRG_8 0x08 +#define UART_TRG_16 0x10 +#define UART_TRG_32 0x20 +#define UART_TRG_64 0x40 +#define UART_TRG_96 0x60 +#define UART_TRG_120 0x78 +#define UART_TRG_128 0x80 + +#define UART_FCTR 1 /* Feature Control Register */ +#define UART_FCTR_RTS_NODELAY 0x00 /* RTS flow control delay */ +#define UART_FCTR_RTS_4DELAY 0x01 +#define UART_FCTR_RTS_6DELAY 0x02 +#define UART_FCTR_RTS_8DELAY 0x03 +#define UART_FCTR_IRDA 0x04 /* IrDa data encode select */ +#define UART_FCTR_TX_INT 0x08 /* Tx interrupt type select */ +#define UART_FCTR_TRGA 0x00 /* Tx/Rx 550 trigger table select */ +#define UART_FCTR_TRGB 0x10 /* Tx/Rx 650 trigger table select */ +#define UART_FCTR_TRGC 0x20 /* Tx/Rx 654 trigger table select */ +#define UART_FCTR_TRGD 0x30 /* Tx/Rx 850 programmable trigger select */ +#define UART_FCTR_SCR_SWAP 0x40 /* Scratch pad register swap */ +#define UART_FCTR_RX 0x00 /* Programmable trigger mode select */ +#define UART_FCTR_TX 0x80 /* Programmable trigger mode select */ + +/* + * LCR=0xBF, FCTR[6]=1 + */ +#define UART_EMSR 7 /* Extended Mode Select Register */ +#define UART_EMSR_FIFO_COUNT 0x01 /* Rx/Tx select */ +#define UART_EMSR_ALT_COUNT 0x02 /* Alternating count select */ + +/* + * The Intel XScale on-chip UARTs define these bits + */ +#define UART_IER_DMAE 0x80 /* DMA Requests Enable */ +#define UART_IER_UUE 0x40 /* UART Unit Enable */ +#define UART_IER_NRZE 0x20 /* NRZ coding Enable */ +#define UART_IER_RTOIE 0x10 /* Receiver Time Out Interrupt Enable */ + +#define UART_IIR_TOD 0x08 /* Character Timeout Indication Detected */ + +#define UART_FCR_PXAR1 0x00 /* receive FIFO threshold = 1 */ +#define UART_FCR_PXAR8 0x40 /* receive FIFO threshold = 8 */ +#define UART_FCR_PXAR16 0x80 /* receive FIFO threshold = 16 */ +#define UART_FCR_PXAR32 0xc0 /* receive FIFO threshold = 32 */ + +/* + * These register definitions are for the 16C950 + */ +#define UART_ASR 0x01 /* Additional Status Register */ +#define UART_RFL 0x03 /* Receiver FIFO level */ +#define UART_TFL 0x04 /* Transmitter FIFO level */ +#define UART_ICR 0x05 /* Index Control Register */ + +/* The 16950 ICR registers */ +#define UART_ACR 0x00 /* Additional Control Register */ +#define UART_CPR 0x01 /* Clock Prescalar Register */ +#define UART_TCR 0x02 /* Times Clock Register */ +#define UART_CKS 0x03 /* Clock Select Register */ +#define UART_TTL 0x04 /* Transmitter Interrupt Trigger Level */ +#define UART_RTL 0x05 /* Receiver Interrupt Trigger Level */ +#define UART_FCL 0x06 /* Flow Control Level Lower */ +#define UART_FCH 0x07 /* Flow Control Level Higher */ +#define UART_ID1 0x08 /* ID #1 */ +#define UART_ID2 0x09 /* ID #2 */ +#define UART_ID3 0x0A /* ID #3 */ +#define UART_REV 0x0B /* Revision */ +#define UART_CSR 0x0C /* Channel Software Reset */ +#define UART_NMR 0x0D /* Nine-bit Mode Register */ +#define UART_CTR 0xFF + +/* + * The 16C950 Additional Control Register + */ +#define UART_ACR_RXDIS 0x01 /* Receiver disable */ +#define UART_ACR_TXDIS 0x02 /* Transmitter disable */ +#define UART_ACR_DSRFC 0x04 /* DSR Flow Control */ +#define UART_ACR_TLENB 0x20 /* 950 trigger levels enable */ +#define UART_ACR_ICRRD 0x40 /* ICR Read enable */ +#define UART_ACR_ASREN 0x80 /* Additional status enable */ + +/* + * These definitions are for the RSA-DV II/S card, from + * + * Kiyokazu SUTO + */ + +#define UART_RSA_BASE (-8) + +#define UART_RSA_MSR ((UART_RSA_BASE) + 0) /* I/O: Mode Select Register */ + +#define UART_RSA_MSR_SWAP (1 << 0) /* Swap low/high 8 bytes in I/O port addr */ +#define UART_RSA_MSR_FIFO (1 << 2) /* Enable the external FIFO */ +#define UART_RSA_MSR_FLOW (1 << 3) /* Enable the auto RTS/CTS flow control */ +#define UART_RSA_MSR_ITYP (1 << 4) /* Level (1) / Edge triger (0) */ + +#define UART_RSA_IER ((UART_RSA_BASE) + 1) /* I/O: Interrupt Enable Register */ + +#define UART_RSA_IER_Rx_FIFO_H (1 << 0) /* Enable Rx FIFO half full int. */ +#define UART_RSA_IER_Tx_FIFO_H (1 << 1) /* Enable Tx FIFO half full int. */ +#define UART_RSA_IER_Tx_FIFO_E (1 << 2) /* Enable Tx FIFO empty int. */ +#define UART_RSA_IER_Rx_TOUT (1 << 3) /* Enable char receive timeout int */ +#define UART_RSA_IER_TIMER (1 << 4) /* Enable timer interrupt */ + +#define UART_RSA_SRR ((UART_RSA_BASE) + 2) /* IN: Status Read Register */ + +#define UART_RSA_SRR_Tx_FIFO_NEMP (1 << 0) /* Tx FIFO is not empty (1) */ +#define UART_RSA_SRR_Tx_FIFO_NHFL (1 << 1) /* Tx FIFO is not half full (1) */ +#define UART_RSA_SRR_Tx_FIFO_NFUL (1 << 2) /* Tx FIFO is not full (1) */ +#define UART_RSA_SRR_Rx_FIFO_NEMP (1 << 3) /* Rx FIFO is not empty (1) */ +#define UART_RSA_SRR_Rx_FIFO_NHFL (1 << 4) /* Rx FIFO is not half full (1) */ +#define UART_RSA_SRR_Rx_FIFO_NFUL (1 << 5) /* Rx FIFO is not full (1) */ +#define UART_RSA_SRR_Rx_TOUT (1 << 6) /* Character reception timeout occurred (1) */ +#define UART_RSA_SRR_TIMER (1 << 7) /* Timer interrupt occurred */ + +#define UART_RSA_FRR ((UART_RSA_BASE) + 2) /* OUT: FIFO Reset Register */ + +#define UART_RSA_TIVSR ((UART_RSA_BASE) + 3) /* I/O: Timer Interval Value Set Register */ + +#define UART_RSA_TCR ((UART_RSA_BASE) + 4) /* OUT: Timer Control Register */ + +#define UART_RSA_TCR_SWITCH (1 << 0) /* Timer on */ + +/* + * The RSA DSV/II board has two fixed clock frequencies. One is the + * standard rate, and the other is 8 times faster. + */ +#define SERIAL_RSA_BAUD_BASE (921600) +#define SERIAL_RSA_BAUD_BASE_LO (SERIAL_RSA_BAUD_BASE / 8) + +/* Extra registers for TI DA8xx/66AK2x */ +#define UART_DA830_PWREMU_MGMT 12 + +/* PWREMU_MGMT register bits */ +#define UART_DA830_PWREMU_MGMT_FREE (1 << 0) /* Free-running mode */ +#define UART_DA830_PWREMU_MGMT_URRST (1 << 13) /* Receiver reset/enable */ +#define UART_DA830_PWREMU_MGMT_UTRST (1 << 14) /* Transmitter reset/enable */ + +/* + * Extra serial register definitions for the internal UARTs + * in TI OMAP processors. + */ +#define OMAP1_UART1_BASE 0xfffb0000 +#define OMAP1_UART2_BASE 0xfffb0800 +#define OMAP1_UART3_BASE 0xfffb9800 +#define UART_OMAP_MDR1 0x08 /* Mode definition register */ +#define UART_OMAP_MDR2 0x09 /* Mode definition register 2 */ +#define UART_OMAP_SCR 0x10 /* Supplementary control register */ +#define UART_OMAP_SSR 0x11 /* Supplementary status register */ +#define UART_OMAP_EBLR 0x12 /* BOF length register */ +#define UART_OMAP_OSC_12M_SEL 0x13 /* OMAP1510 12MHz osc select */ +#define UART_OMAP_MVER 0x14 /* Module version register */ +#define UART_OMAP_SYSC 0x15 /* System configuration register */ +#define UART_OMAP_SYSS 0x16 /* System status register */ +#define UART_OMAP_WER 0x17 /* Wake-up enable register */ +#define UART_OMAP_TX_LVL 0x1a /* TX FIFO level register */ + +/* + * These are the definitions for the MDR1 register + */ +#define UART_OMAP_MDR1_16X_MODE 0x00 /* UART 16x mode */ +#define UART_OMAP_MDR1_SIR_MODE 0x01 /* SIR mode */ +#define UART_OMAP_MDR1_16X_ABAUD_MODE 0x02 /* UART 16x auto-baud */ +#define UART_OMAP_MDR1_13X_MODE 0x03 /* UART 13x mode */ +#define UART_OMAP_MDR1_MIR_MODE 0x04 /* MIR mode */ +#define UART_OMAP_MDR1_FIR_MODE 0x05 /* FIR mode */ +#define UART_OMAP_MDR1_CIR_MODE 0x06 /* CIR mode */ +#define UART_OMAP_MDR1_DISABLE 0x07 /* Disable (default state) */ + +/* + * These are definitions for the Altera ALTR_16550_F32/F64/F128 + * Normalized from 0x100 to 0x40 because of shift by 2 (32 bit regs). + */ +#define UART_ALTR_AFR 0x40 /* Additional Features Register */ +#define UART_ALTR_EN_TXFIFO_LW 0x01 /* Enable the TX FIFO Low Watermark */ +#define UART_ALTR_TX_LOW 0x41 /* Tx FIFO Low Watermark */ + +#endif /* __SERIAL_8250_REGS_H__ */ diff --git a/components/drivers/serial/device/Kconfig b/components/drivers/serial/device/Kconfig index aaa4c2b7bfc..98a38e634c3 100644 --- a/components/drivers/serial/device/Kconfig +++ b/components/drivers/serial/device/Kconfig @@ -1 +1,12 @@ +config RT_SERIAL_EARLY_HVC + bool "Early HVC" + default n + +config RT_SERIAL_PL011 + bool "ARM PL011" + default n + +rsource "8250/Kconfig" +rsource "virtual/Kconfig" + osource "$(SOC_DM_SERIAL_DIR)/Kconfig" diff --git a/components/drivers/serial/device/SConscript b/components/drivers/serial/device/SConscript index 31a72472aad..49ed756e9f4 100644 --- a/components/drivers/serial/device/SConscript +++ b/components/drivers/serial/device/SConscript @@ -8,6 +8,12 @@ CPPPATH = [cwd + '/../../include'] src = [] +if GetDepend(['RT_SERIAL_EARLY_HVC']): + src += ['serial-early-hvc.c'] + +if GetDepend(['RT_SERIAL_PL011']): + src += ['serial-pl011.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) for d in list: diff --git a/components/drivers/serial/device/serial-early-hvc.c b/components/drivers/serial/device/serial-early-hvc.c new file mode 100644 index 00000000000..f3d944df71f --- /dev/null +++ b/components/drivers/serial/device/serial-early-hvc.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#include + +#include + +static void hvc_early_console_putchar(void *data, char c) +{ + rt_hv_console(c); +} + +static rt_err_t hvc_early_setup(struct rt_fdt_earlycon *con, const char *options) +{ + rt_err_t err; + rt_uint32_t version; + + if ((err = rt_hv_version(&version))) + { + return err; + } + + con->console_putc = hvc_early_console_putchar; + + return err; +} +RT_FDT_EARLYCON_EXPORT(hvc, "hvc", "vmrt-thread,hvc-console", hvc_early_setup); diff --git a/components/drivers/serial/device/serial-pl011.c b/components/drivers/serial/device/serial-pl011.c new file mode 100644 index 00000000000..1f8cc3a26e3 --- /dev/null +++ b/components/drivers/serial/device/serial-pl011.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-05-05 Bernard The first version + * 2022-08-24 GuEe-GUI add OFW support + */ + +#include +#include +#include +#include + +#include + +#include + +#define PL011_OEIM RT_BIT(10) /* overrun error interrupt mask */ +#define PL011_BEIM RT_BIT(9) /* break error interrupt mask */ +#define PL011_PEIM RT_BIT(8) /* parity error interrupt mask */ +#define PL011_FEIM RT_BIT(7) /* framing error interrupt mask */ +#define PL011_RTIM RT_BIT(6) /* receive timeout interrupt mask */ +#define PL011_TXIM RT_BIT(5) /* transmit interrupt mask */ +#define PL011_RXIM RT_BIT(4) /* receive interrupt mask */ +#define PL011_DSRMIM RT_BIT(3) /* DSR interrupt mask */ +#define PL011_DCDMIM RT_BIT(2) /* DCD interrupt mask */ +#define PL011_CTSMIM RT_BIT(1) /* CTS interrupt mask */ +#define PL011_RIMIM RT_BIT(0) /* RI interrupt mask */ + +#define PL011_DR 0x000 +#define PL011_FR 0x018 +#define PL011_IBRD 0x024 +#define PL011_FBRD 0x028 +#define PL011_LCR 0x02c +#define PL011_CR 0x030 +#define PL011_IMSC 0x038 +#define PL011_RIS 0x03c +#define PL011_DMACR 0x048 + +#define PL011_LCRH_SPS (1 << 7) +#define PL011_LCRH_WLEN_8 (3 << 5) +#define PL011_LCRH_WLEN_7 (2 << 5) +#define PL011_LCRH_WLEN_6 (1 << 5) +#define PL011_LCRH_WLEN_5 (0 << 5) +#define PL011_LCRH_FEN (1 << 4) +#define PL011_LCRH_STP2 (1 << 3) +#define PL011_LCRH_EPS (1 << 2) +#define PL011_LCRH_PEN (1 << 1) +#define PL011_LCRH_BRK (1 << 0) + +#define PL011_LCRH_WLEN(n) ((n - 5) << 5) + +#define PL011_CR_CTSEN RT_BIT(15) +#define PL011_CR_RTSEN RT_BIT(14) +#define PL011_CR_RTS RT_BIT(11) +#define PL011_CR_DTR RT_BIT(10) +#define PL011_CR_RXE RT_BIT(9) +#define PL011_CR_TXE RT_BIT(8) +#define PL011_CR_LBE RT_BIT(7) +#define PL011_CR_SIRLP RT_BIT(2) +#define PL011_CR_SIREN RT_BIT(1) +#define PL011_CR_UARTEN RT_BIT(0) + +struct pl011 +{ + struct rt_serial_device parent; + + int irq; + void *base; + rt_ubase_t freq; + struct rt_clk *clk; + struct rt_clk *pclk; + + struct rt_spinlock spinlock; +}; + +#define raw_to_pl011(raw) rt_container_of(raw, struct pl011, parent) + +rt_inline rt_uint32_t pl011_read(struct pl011 *pl011, int offset) +{ + return HWREG32(pl011->base + offset); +} + +rt_inline void pl011_write(struct pl011 *pl011, int offset, rt_uint32_t value) +{ + HWREG32(pl011->base + offset) = value; +} + +static void pl011_isr(int irqno, void *param) +{ + struct pl011 *pl011 = param; + + /* Check irq */ + if (pl011_read(pl011, PL011_RIS) & PL011_RXIM) + { + rt_base_t level = rt_spin_lock_irqsave(&pl011->spinlock); + + rt_hw_serial_isr(&pl011->parent, RT_SERIAL_EVENT_RX_IND); + + rt_spin_unlock_irqrestore(&pl011->spinlock, level); + } +} + +static rt_err_t pl011_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + rt_ubase_t quot; + struct pl011 *pl011 = raw_to_pl011(serial); + + /* Clear UART setting */ + pl011_write(pl011, PL011_CR, 0); + /* Disable FIFO */ + pl011_write(pl011, PL011_LCR, 0); + + if (cfg->baud_rate > pl011->freq / 16) + { + quot = RT_DIV_ROUND_CLOSEST(pl011->freq * 8, cfg->baud_rate); + } + else + { + quot = RT_DIV_ROUND_CLOSEST(pl011->freq * 4, cfg->baud_rate); + } + + pl011_write(pl011, PL011_IBRD, quot >> 6); + pl011_write(pl011, PL011_FBRD, quot & 0x3f); + /* FIFO */ + pl011_write(pl011, PL011_LCR, PL011_LCRH_WLEN(cfg->data_bits)); + + /* Art enable, TX/RX enable */ + pl011_write(pl011, PL011_CR, PL011_CR_UARTEN | PL011_CR_TXE | PL011_CR_RXE); + + return RT_EOK; +} + +static rt_err_t pl011_uart_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct pl011 *pl011 = raw_to_pl011(serial); + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + /* Disable rx irq */ + pl011_write(pl011, PL011_IMSC, pl011_read(pl011, PL011_IMSC) & ~PL011_RXIM); + + rt_hw_interrupt_mask(pl011->irq); + + break; + + case RT_DEVICE_CTRL_SET_INT: + /* Enable rx irq */ + pl011_write(pl011, PL011_IMSC, pl011_read(pl011, PL011_IMSC) | PL011_RXIM); + + rt_hw_interrupt_umask(pl011->irq); + + break; + } + + return RT_EOK; +} + +static int pl011_uart_putc(struct rt_serial_device *serial, char c) +{ + struct pl011 *pl011 = raw_to_pl011(serial); + + while (pl011_read(pl011, PL011_FR) & PL011_TXIM) + { + rt_hw_cpu_relax(); + } + + pl011_write(pl011, PL011_DR, c); + + return 1; +} + +static int pl011_uart_getc(struct rt_serial_device *serial) +{ + int ch = -1; + struct pl011 *pl011 = raw_to_pl011(serial); + + if (!(pl011_read(pl011, PL011_FR) & PL011_RXIM)) + { + ch = pl011_read(pl011, PL011_DR); + } + + return ch; +} + +static const struct rt_uart_ops pl011_uart_ops = +{ + .configure = pl011_uart_configure, + .control = pl011_uart_control, + .putc = pl011_uart_putc, + .getc = pl011_uart_getc, +}; + +static void pl011_early_kick(struct rt_fdt_earlycon *con, int why) +{ + struct pl011 *pl011 = raw_to_pl011(con->data); + + switch (why) + { + case FDT_EARLYCON_KICK_UPDATE: + pl011->base = rt_ioremap((void *)con->mmio, con->size); + break; + + case FDT_EARLYCON_KICK_COMPLETED: + rt_iounmap(pl011->base); + break; + + default: + break; + } +} + +static rt_err_t pl011_early_setup(struct rt_fdt_earlycon *con, const char *options) +{ + rt_err_t err = RT_EOK; + static struct pl011 pl011 = { }; + + if (options && !con->mmio) + { + char *arg; + + con->mmio = RT_NULL; + + /* + * The pl011 serial port must already be setup and configured in early. + * Options are not yet supported. + * pl011, + * pl011,mmio32, + */ + serial_for_each_args(arg, options) + { + if (!rt_strcmp(arg, "pl011") || !rt_strcmp(arg, "mmio32")) + { + continue; + } + if (!con->mmio) + { + con->mmio = (rt_ubase_t)serial_base_from_args(arg); + break; + } + } + } + + if (!con->size) + { + con->size = 0x1000; + } + + if (con->mmio) + { + pl011.base = rt_ioremap_early((void *)con->mmio, con->size); + } + + if (pl011.base) + { + con->console_putc = (typeof(con->console_putc))&pl011_uart_putc; + con->console_kick = pl011_early_kick; + con->data = &pl011.parent; + pl011.parent.config = (typeof(pl011.parent.config))RT_SERIAL_CONFIG_DEFAULT; + } + else + { + err = -RT_ERROR; + } + + return err; +} +RT_FDT_EARLYCON_EXPORT(pl011, "pl011", "arm,pl011", pl011_early_setup); + +static void pl011_free(struct pl011 *pl011) +{ + if (pl011->base) + { + rt_iounmap(pl011->base); + } + + if (!rt_is_err_or_null(pl011->clk)) + { + rt_clk_disable(pl011->clk); + rt_clk_put(pl011->clk); + } + + if (!rt_is_err_or_null(pl011->pclk)) + { + rt_clk_disable_unprepare(pl011->pclk); + rt_clk_put(pl011->pclk); + } + + rt_free(pl011); +} + +static rt_err_t pl011_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *name; + char isr_name[RT_NAME_MAX]; + struct rt_device *dev = &pdev->parent; + struct pl011 *pl011 = rt_calloc(1, sizeof(*pl011)); + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + if (!pl011) + { + return -RT_ENOMEM; + } + + pl011->base = rt_dm_dev_iomap(dev, 0); + + if (!pl011->base) + { + err = -RT_EIO; + + goto _fail; + } + + pl011->irq = rt_dm_dev_get_irq(dev, 0); + + if (pl011->irq < 0) + { + err = pl011->irq; + + goto _fail; + } + + pl011->clk = rt_clk_get_by_index(dev, 0); + + if (rt_is_err(pl011->clk)) + { + err = rt_ptr_err(pl011->clk); + + goto _fail; + } + + pl011->pclk = rt_clk_get_by_name(dev, "apb_pclk"); + + if (rt_is_err(pl011->pclk)) + { + err = rt_ptr_err(pl011->pclk); + + goto _fail; + } + + if ((err = rt_clk_prepare_enable(pl011->pclk))) + { + goto _fail; + } + + rt_dm_dev_bind_fwdata(&pl011->parent.parent, dev->ofw_node, &pl011->parent); + + rt_clk_enable(pl011->clk); + pl011->freq = rt_clk_get_rate(pl011->clk); + + dev->user_data = pl011; + + pl011->parent.ops = &pl011_uart_ops; + pl011->parent.config = config; + + rt_spin_lock_init(&pl011->spinlock); + + serial_dev_set_name(&pl011->parent); + name = rt_dm_dev_get_name(&pl011->parent.parent); + + rt_hw_serial_register(&pl011->parent, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, pl011); + rt_snprintf(isr_name, RT_NAME_MAX, "%s-pl011", name); + rt_hw_interrupt_install(pl011->irq, pl011_isr, pl011, isr_name); + + return RT_EOK; + +_fail: + pl011_free(pl011); + + return err; +} + +static rt_err_t pl011_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct pl011 *pl011 = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + + rt_hw_interrupt_mask(pl011->irq); + rt_pic_detach_irq(pl011->irq, pl011); + + rt_device_unregister(&pl011->parent.parent); + + pl011_free(pl011); + + return RT_EOK; +} + +static const struct rt_ofw_node_id pl011_ofw_ids[] = +{ + { .type = "ttyAMA", .compatible = "arm,pl011" }, + { .type = "ttyAMA", .compatible = "arm,pl011-axi" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver pl011_driver = +{ + .name = "serial-pl011", + .ids = pl011_ofw_ids, + + .probe = pl011_probe, + .remove = pl011_remove, +}; + +static int pl011_drv_register(void) +{ + rt_platform_driver_register(&pl011_driver); + + return 0; +} +INIT_PLATFORM_EXPORT(pl011_drv_register); diff --git a/components/drivers/serial/device/virtual/.gitignore b/components/drivers/serial/device/virtual/.gitignore new file mode 100644 index 00000000000..d9dde248222 --- /dev/null +++ b/components/drivers/serial/device/virtual/.gitignore @@ -0,0 +1 @@ +psf.inc diff --git a/components/drivers/serial/device/virtual/Kconfig b/components/drivers/serial/device/virtual/Kconfig new file mode 100644 index 00000000000..cae76d2783a --- /dev/null +++ b/components/drivers/serial/device/virtual/Kconfig @@ -0,0 +1,17 @@ +menuconfig RT_SERIAL_VIRTUAL + bool "Virtual Serial in video" + depends on RT_GRAPHIC_FB + depends on RT_INPUT_KEYBOARD + default n + +choice + prompt "Rendering font(psf)" + default RT_SERIAL_VIRTUAL_FONT_UNI2_FIXED16 + depends on RT_SERIAL_VIRTUAL + + config RT_SERIAL_VIRTUAL_FONT_UNI2_FIXED16 + bool "uni2 fixed16" + + config RT_SERIAL_VIRTUAL_FONT_UNI2_VGA16 + bool "uni2 vga16" +endchoice diff --git a/components/drivers/serial/device/virtual/SConscript b/components/drivers/serial/device/virtual/SConscript new file mode 100644 index 00000000000..64481dad41d --- /dev/null +++ b/components/drivers/serial/device/virtual/SConscript @@ -0,0 +1,39 @@ +from building import * + +group = [] + +if not GetDepend(['RT_SERIAL_VIRTUAL']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] +CPPDEFINES = [] + +src = [] + +font_path = None + +if GetDepend(['RT_SERIAL_VIRTUAL_FONT_UNI2_FIXED16']): + font_path = cwd + '/font-uni2-fixed16.psf' + +if GetDepend(['RT_SERIAL_VIRTUAL_FONT_UNI2_VGA16']): + font_path = cwd + '/font-uni2-vga16.psf' + +if font_path != None: + with open(font_path, 'rb') as psf: + psf_data = psf.read() + + first_byte = True + + with open(cwd + '/psf.inc', 'w') as font: + for byte in psf_data: + if not first_byte: + font.write(", ") + + font.write("0x{:02x}".format(byte)) + first_byte = False + + src += ['psf.c', 'render.c', 'virtual.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES) +Return('group') diff --git a/components/drivers/serial/device/virtual/font-uni2-fixed16.psf b/components/drivers/serial/device/virtual/font-uni2-fixed16.psf new file mode 100644 index 0000000000000000000000000000000000000000..45a865b30c495bf4eafd79377c7a8d00c4060402 GIT binary patch literal 10804 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 z&iJgfhC7UVsheader1->magic[0] == PSF1_MAGIC0 && + psf->header1->magic[1] == PSF1_MAGIC1) + { + psf->font_map = (void *)&psf->header1[1]; + psf->type = PSF_TYPE_1; + + if (psf->header1->mode & PSF1_MODE512) + { + psf->count = 512; + } + else + { + psf->count = 256; + } + + psf->size = psf->header1->charsize; + psf->height = psf->header1->charsize; + psf->width = 8; + } + else if (psf->header2->magic[0] == PSF2_MAGIC0 && + psf->header2->magic[1] == PSF2_MAGIC1 && + psf->header2->magic[2] == PSF2_MAGIC2 && + psf->header2->magic[3] == PSF2_MAGIC3) + { + psf->font_map = (void *)&psf->header2[1]; + psf->type = PSF_TYPE_2; + psf->count = psf->header2->length; + psf->size = psf->header2->charsize; + psf->height = psf->header2->height; + psf->width = psf->header2->width; + } + else + { + psf->type = PSF_TYPE_UNKNOW; + } + + psf->glyph = psf->height * psf->width; +} + +rt_err_t psf_initialize(const void *psf_data, struct psf_font *out_psf) +{ + struct psf_font *psf = out_psf; + + psf->raw_data = psf_data; + psf_read_header(psf); + + if (psf->type == PSF_TYPE_UNKNOW) + { + return -RT_ENOSYS; + } + + return RT_EOK; +} + +rt_err_t psf_parse(struct psf_font *psf, const rt_uint8_t *font_data, + rt_uint8_t *tmpglyph, rt_uint32_t color_size, + rt_uint32_t foreground, rt_uint32_t background) +{ + rt_uint8_t *font = (void *)font_data, *map = (void *)psf->font_map; + + psf->font_data = font_data; + + for (int n = 0; n < psf->count; ++n, map += psf->size) + { + rt_memcpy(tmpglyph, map, psf->size); + + for (int i = 0; i < psf->size; ++i) + { + for (int j = 0; j < 8; ++j) + { + if (i % (psf->size / psf->height) * 8 + j < psf->width) + { + if (tmpglyph[i] & 0x80) + { + rt_memcpy(font, &foreground, color_size); + } + else + { + rt_memcpy(font, &background, color_size); + } + + font += color_size; + } + + tmpglyph[i] = tmpglyph[i] << 1; + } + } + } + + return RT_EOK; +} diff --git a/components/drivers/serial/device/virtual/psf.h b/components/drivers/serial/device/virtual/psf.h new file mode 100644 index 00000000000..077ca5468ed --- /dev/null +++ b/components/drivers/serial/device/virtual/psf.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __PSF_H__ +#define __PSF_H__ + +#include + +#define PSF1_MAGIC0 0x36 +#define PSF1_MAGIC1 0x04 + +#define PSF1_MODE512 0x01 +#define PSF1_MODEHASTAB 0x02 +#define PSF1_MODEHASSEQ 0x04 +#define PSF1_MAXMODE 0x05 + +#define PSF1_SEPARATOR 0xffff +#define PSF1_STARTSEQ 0xfffe + +struct psf1_header +{ + rt_uint8_t magic[2]; /* Magic number */ + rt_uint8_t mode; /* PSF font mode */ + rt_uint8_t charsize; /* Character size */ +}; + +#define PSF2_MAGIC0 0x72 +#define PSF2_MAGIC1 0xb5 +#define PSF2_MAGIC2 0x4a +#define PSF2_MAGIC3 0x86 + +/* bits used in flags */ +#define PSF2_HAS_UNICODE_TABLE 0x01 + +/* max version recognized so far */ +#define PSF2_MAXVERSION 0 + +/* UTF8 separators */ +#define PSF2_SEPARATOR 0xff +#define PSF2_STARTSEQ 0xfe + +struct psf2_header +{ + rt_uint8_t magic[4]; + rt_uint32_t version; + rt_uint32_t headersize; /* offset of bitmaps in file */ + rt_uint32_t flags; + rt_uint32_t length; /* number of glyphs */ + rt_uint32_t charsize; /* number of bytes for each character, charsize = height * ((width + 7) / 8) */ + rt_uint32_t height, width; /* max dimensions of glyphs */ +}; + +enum psf_type +{ + PSF_TYPE_1, + PSF_TYPE_2, + PSF_TYPE_UNKNOW, +}; + +struct psf_font +{ + union + { + const rt_uint8_t *raw_data; + struct psf1_header *header1; + struct psf2_header *header2; + }; + const rt_uint8_t *font_map; + const rt_uint8_t *font_data; + + enum psf_type type; + + rt_uint32_t count; /* fonts count */ + rt_uint32_t size; + rt_uint32_t height; + rt_uint32_t width; + rt_uint32_t glyph; /* height * width */ +}; + +rt_err_t psf_initialize(const void *psf_data, struct psf_font *out_psf); +rt_err_t psf_parse(struct psf_font *psf, const rt_uint8_t *font_data, + rt_uint8_t *tmpglyph, rt_uint32_t color_size, + rt_uint32_t foreground, rt_uint32_t background); + +#endif /* __PSF_H__ */ diff --git a/components/drivers/serial/device/virtual/render.c b/components/drivers/serial/device/virtual/render.c new file mode 100644 index 00000000000..e3e4853e201 --- /dev/null +++ b/components/drivers/serial/device/virtual/render.c @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#include "psf.h" +#include "render.h" + +struct render +{ + struct rt_device *fbdev; + struct rt_device_graphic_info info; + struct fb_var_screeninfo var; + struct fb_fix_screeninfo fix; + + struct psf_font psf; + rt_uint32_t xlate; + rt_uint32_t font_line_length; + rt_uint32_t buffer_idx; + rt_size_t buffer_size; + rt_size_t line_size; + rt_size_t screen_size; + rt_size_t font_size; + rt_uint8_t color_size; + rt_uint8_t *cursor, *cursor_backend; + + struct render_point position, start_point, end_point; + + rt_uint32_t foreground, background; + rt_uint32_t raw_foreground, raw_background; + rt_uint32_t red_off, green_off, blue_off, alpha_off; + rt_uint32_t red_mask, green_mask, blue_mask, alpha_mask; + + void *hook_ptr; + + void (*set_pixel)(void *fb, rt_uint32_t color); + rt_uint32_t (*get_pixel)(void *fb); + + void *(*last_buffer)(void); + void *(*next_buffer)(void); + void (*move_buffer)(void *dst, void *src, rt_size_t size); + void (*pan_display)(void); +}; + +static struct render _render = {}; + +rt_err_t render_load_fbdev(struct rt_device *fbdev) +{ + rt_err_t err = RT_EOK; + struct render *rd = &_render; + + if ((err = rt_device_open(fbdev, 0))) + { + return err; + } + + rd->fbdev = fbdev; + + err |= rt_device_control(fbdev, FBIOGET_VSCREENINFO, &rd->var); + err |= rt_device_control(fbdev, FBIOGET_FSCREENINFO, &rd->fix); + err |= rt_device_control(fbdev, RTGRAPHIC_CTRL_GET_INFO, &rd->info); + + rt_device_close(fbdev); + + return err; +} + +static rt_uint32_t parse_color(struct render_color *color) +{ + rt_uint32_t color_value; + struct render *rd = &_render; + + color_value = ((color->red & rd->red_mask) << rd->red_off) | + ((color->green & rd->green_mask) << rd->green_off) | + ((color->blue & rd->blue_mask) << rd->blue_off); + + if (rd->var.transp.length) + { + color_value |= (color->alpha & rd->alpha_mask) << rd->alpha_off; + } + + return color_value; +} + +static void set_pixel16(void *fb, rt_uint32_t color) +{ + rt_memcpy(fb, &color, 2); +} + +static rt_uint32_t get_pixel16(void *fb) +{ + rt_uint16_t color; + + rt_memcpy(&color, fb, 2); + + return color; +} + +static void set_pixel24(void *fb, rt_uint32_t color) +{ + rt_memcpy(fb, &color, 3); +} + +static rt_uint32_t get_pixel24(void *fb) +{ + rt_uint32_t color; + + rt_memcpy(&color, fb, 3); + + return color; +} + +static void set_pixel32(void *fb, rt_uint32_t color) +{ + *(rt_uint32_t *)fb = color; +} + +static rt_uint32_t get_pixel32(void *fb) +{ + return *(rt_uint32_t *)fb; +} + +static void *last_buffer_single(void) +{ + struct render *rd = &_render; + + return (void *)rd->info.framebuffer; +} + +static void *fb_next_buffer_dummy(void) +{ + return last_buffer_single(); +} + +static void fb_move_buffer_single(void *dst, void *src, rt_size_t size) +{ + rt_memmove(dst, src, size); +} + +static void fb_pan_display_dummy(void) +{ +} + +static void *last_buffer_multi(void) +{ + struct render *rd = &_render; + + return (void *)rd->info.framebuffer + rd->buffer_idx * rd->line_size; +} + +static void *fb_next_buffer(void) +{ + struct render *rd = &_render; + + if (rd->buffer_idx < rd->buffer_size) + { + ++rd->buffer_idx; + } + else + { + rd->buffer_idx = 0; + } + + return last_buffer_multi(); +} + +static void fb_move_buffer_multi(void *dst, void *src, rt_size_t size) +{ + struct render *rd = &_render; + + if (rd->buffer_idx == 0) + { + fb_move_buffer_single(dst, src, size); + } +} + +static void fb_pan_display(void) +{ + struct render *rd = &_render; + + rd->var.yoffset = rd->buffer_idx * rd->psf.height; + + rt_device_control(rd->fbdev, FBIOPAN_DISPLAY, &rd->var); +} + +static void color_hook_dummy(void *, rt_uint32_t *); + +rt_err_t render_load_font(const char *psf_data, rt_size_t size, + struct render_color *foreground, struct render_color *background, + struct render_point *out_start_point, struct render_point *out_end_point) +{ + rt_err_t err; + rt_uint8_t *font_data, *cursor, *cursor_backend; + rt_uint32_t foreground_color, background_color; + struct psf_font new_psf; + struct render *rd = &_render; + + if ((err = psf_initialize(psf_data, &new_psf))) + { + return err; + } + + if (!new_psf.count) + { + return -RT_EEMPTY; + } + + /* Font + Cursor + Cursor backend */ + rd->color_size = rd->var.bits_per_pixel / 8; + font_data = rt_malloc_align(rd->color_size * new_psf.glyph * (new_psf.count + 2), sizeof(rt_ubase_t)); + + if (!font_data) + { + return -RT_ENOMEM; + } + + cursor = font_data + rd->color_size * new_psf.glyph * new_psf.count; + cursor_backend = cursor + rd->color_size * new_psf.glyph * new_psf.count; + + rd->red_off = rd->var.red.offset; + rd->red_mask = RT_GENMASK(rd->var.red.length, 0); + rd->green_off = rd->var.green.offset; + rd->green_mask = RT_GENMASK(rd->var.green.length, 0); + rd->blue_off = rd->var.blue.offset; + rd->blue_mask = RT_GENMASK(rd->var.blue.length, 0); + rd->alpha_off = rd->var.transp.offset; + rd->alpha_mask = RT_GENMASK(rd->var.transp.length, 0); + + foreground_color = parse_color(foreground); + background_color = parse_color(background); + + if ((err = psf_parse(&new_psf, font_data, cursor_backend, + rd->color_size, foreground_color, background_color))) + { + rt_free(font_data); + + return err; + } + + if (rd->psf.font_data) + { + rt_free((void *)rd->psf.font_data); + } + + rd->foreground = rd->raw_foreground = foreground_color; + rd->background = rd->raw_background = background_color; + rd->hook_ptr = &color_hook_dummy; + + rt_memcpy(&rd->psf, &new_psf, sizeof(rd->psf)); + + rd->font_line_length = rd->psf.width * rd->color_size; + rd->xlate = rd->fix.line_length - rd->font_line_length; + + rd->line_size = rd->psf.height * rd->fix.line_length; + rd->screen_size = rd->var.xres * rd->var.yres * rd->color_size; + rd->font_size = rd->psf.glyph * rd->color_size; + rd->buffer_idx = 0; + rd->buffer_size = (rd->fix.smem_len - rd->screen_size) / rd->line_size; + + if (rd->buffer_size) + { + rd->var.yres_virtual = (rd->fix.smem_len / rd->screen_size) * rd->var.yres; + + if (rt_device_control(rd->fbdev, FBIOPUT_VSCREENINFO, &rd->var)) + { + /* Enable double buffer fail */ + rd->buffer_size = 0; + } + } + + if (rd->buffer_size) + { + rd->last_buffer = last_buffer_multi; + rd->next_buffer = fb_next_buffer; + rd->move_buffer = fb_move_buffer_single; + rd->pan_display = fb_pan_display; + } + else + { + rd->last_buffer = last_buffer_single; + rd->next_buffer = fb_next_buffer_dummy; + rd->move_buffer = fb_move_buffer_multi; + rd->pan_display = fb_pan_display_dummy; + } + + rd->cursor = cursor; + rd->cursor_backend = cursor_backend; + + rd->start_point.row = rd->var.yoffset / rd->psf.height; + rd->start_point.col = rd->var.xoffset / rd->psf.width; + rd->end_point.row = rd->var.yres / rd->psf.height - 1; + rd->end_point.col = rd->var.xres / rd->psf.width - 1; + + if (out_start_point) + { + rt_memcpy(out_start_point, &rd->start_point, sizeof(*out_start_point)); + } + + if (out_end_point) + { + rt_memcpy(out_end_point, &rd->end_point, sizeof(*out_end_point)); + } + + switch (rd->var.bits_per_pixel) + { + case 32: + rd->set_pixel = set_pixel32; + rd->get_pixel = get_pixel32; + break; + + case 24: + rd->set_pixel = set_pixel24; + rd->get_pixel = get_pixel24; + break; + + case 16: + rd->set_pixel = set_pixel16; + rd->get_pixel = get_pixel16; + break; + + default: break; + } + + return RT_EOK; +} + +void render_clear_display(void) +{ + void *fb, *fb_end; + rt_uint32_t color; + rt_size_t color_size; + struct render *rd = &_render; + typeof(rd->set_pixel) set_pixel_handler = rd->set_pixel; + + fb = rd->next_buffer(); + fb_end = fb + rd->screen_size; + + color = rd->background; + color_size = rd->color_size; + + while (fb < fb_end) + { + set_pixel_handler(fb, color); + + fb += color_size; + } + + rd->pan_display(); + render_move_cursor(RT_NULL); +} + +static void roll_display(void) +{ + rt_uint32_t color; + rt_size_t screen_size, flush_size, color_size; + void *old_fb, *fb, *fb_end; + struct render *rd = &_render; + typeof(rd->set_pixel) set_pixel_handler = rd->set_pixel; + + color = rd->background; + color_size = rd->color_size; + screen_size = rd->screen_size; + flush_size = screen_size - rd->line_size; + + old_fb = rd->last_buffer(); + fb = rd->next_buffer(); + fb_end = fb + screen_size; + + rd->move_buffer(fb, old_fb + rd->line_size, flush_size); + /* The last line */ + fb += flush_size; + + while (fb < fb_end) + { + set_pixel_handler(fb, color); + + fb += color_size; + } + + rd->pan_display(); + rt_device_control(rd->fbdev, FBIO_WAITFORVSYNC, RT_NULL); +} + +static void color_hook_dummy(void *fb, rt_uint32_t *out_color) +{ +} + +static void color_hook_transform(void *fb, rt_uint32_t *out_color) +{ + rt_uint32_t color; + struct render *rd = &_render; + + rt_memcpy(&color, out_color, rd->color_size); + + if (color == rd->raw_foreground) + { + *out_color = rd->foreground; + } + else if (color == rd->raw_background) + { + *out_color = rd->background; + } +} + +void render_set_foreground(struct render_color *foreground) +{ + struct render *rd = &_render; + + rd->foreground = parse_color(foreground); + + if (rd->foreground != rd->raw_foreground) + { + rd->hook_ptr = color_hook_transform; + } + else + { + rd->hook_ptr = color_hook_dummy; + } +} + +void render_set_background(struct render_color *background) +{ + struct render *rd = &_render; + + rd->background = parse_color(background); + + if (rd->foreground != rd->raw_foreground) + { + rd->hook_ptr = color_hook_transform; + } + else + { + rd->hook_ptr = color_hook_dummy; + } +} + +static void color_hook_invert(void *fb, rt_uint32_t *out_color) +{ + struct render *rd = &_render; + + *out_color ^= rd->get_pixel(fb) & ~(rd->alpha_mask << rd->alpha_off); +} + +static void draw_block(void *block, void (*color_hook)(void *fb, rt_uint32_t *out_color)) +{ + void *fb; + rt_size_t color_size; + int font_width, font_height, xlate; + struct render *rd = &_render; + typeof(rd->set_pixel) set_pixel_handler = rd->set_pixel; + + color_size = rd->color_size; + font_width = rd->psf.width; + font_height = rd->psf.height; + xlate = rd->xlate; + + fb = rd->last_buffer(); + fb += rd->position.col * rd->font_line_length + rd->position.row * rd->line_size; + + for (int y = 0; y < font_height; ++y) + { + for (int x = 0; x < font_width; ++x) + { + rt_uint32_t color = *(rt_uint32_t *)block; + + color_hook(fb, &color); + set_pixel_handler(fb, color); + + fb += color_size; + block += color_size; + } + + fb += xlate; + } +} + +static void cursor_leave(void) +{ + struct render *rd = &_render; + + draw_block(rd->cursor, &color_hook_invert); +} + +static void cursor_update(void) +{ + struct render *rd = &_render; + + draw_block(rd->cursor, &color_hook_invert); + + rt_device_control(rd->fbdev, FBIO_WAITFORVSYNC, RT_NULL); +} + +void render_select_cursor(enum cursor shape) +{ + void *cursor; + rt_size_t color_size; + int font_width, font_height; + rt_uint32_t foreground, background; + struct render *rd = &_render; + typeof(rd->set_pixel) set_pixel_handler = rd->set_pixel; + + cursor = rd->cursor; + font_width = rd->psf.width; + font_height = rd->psf.height; + foreground = rd->foreground; + background = rd->background; + color_size = rd->color_size; + + switch (shape) + { + case CURSOR_HLINE: + for (int y = 0; y < font_height; ++y) + { + for (int x = 0; x < font_width; ++x) + { + if (y + 1 < font_height) + { + set_pixel_handler(cursor, background); + } + else + { + set_pixel_handler(cursor, foreground); + } + + cursor += color_size; + } + } + break; + + case CURSOR_VLINE: + for (int i = 0; i < font_height; ++i) + { + set_pixel_handler(cursor, foreground); + cursor += color_size; + + for (int x = 1; x < font_width; ++x) + { + set_pixel_handler(cursor, background); + + cursor += color_size; + } + } + break; + + case CURSOR_BLOCK: + for (int y = 0; y < font_height; ++y) + { + for (int x = 0; x < font_width; ++x) + { + set_pixel_handler(cursor, foreground); + + cursor += color_size; + } + } + break; + + default: + return; + } + + cursor_update(); +} + +void render_move_cursor(struct render_point *position) +{ + struct render *rd = &_render; + + if (position) + { + cursor_leave(); + + if (position->row > rd->end_point.row || position->col > rd->end_point.col) + { + return; + } + + rt_memcpy(&rd->position, position, sizeof(rd->position)); + } + else + { + rt_memset(&rd->position, 0, sizeof(rd->position)); + } + + cursor_update(); +} + +void render_reset_cursor(struct render_point *out_position) +{ + struct render *rd = &_render; + + cursor_leave(); + + rd->position.col = rd->start_point.col; + + cursor_update(); + render_current_cursor(out_position); +} + +void render_return_cursor(struct render_point *out_position) +{ + struct render *rd = &_render; + + cursor_leave(); + + rd->position.col = rd->start_point.col; + + if (rd->position.row >= rd->end_point.row) + { + roll_display(); + } + else + { + ++rd->position.row; + } + + cursor_update(); + render_current_cursor(out_position); +} + +void render_current_cursor(struct render_point *out_position) +{ + struct render *rd = &_render; + + if (out_position) + { + rt_memcpy(out_position, &rd->position, sizeof(rd->position)); + } +} + +void render_put_char(char ch) +{ + struct render *rd = &_render; + + draw_block((void *)rd->psf.font_data + ch * rd->font_size, rd->hook_ptr); + + ++rd->position.col; + + if (rd->position.col > rd->end_point.col) + { + rd->position.col = rd->start_point.col; + + if (rd->position.row >= rd->end_point.row) + { + roll_display(); + } + else + { + ++rd->position.row; + } + } + + cursor_update(); +} diff --git a/components/drivers/serial/device/virtual/render.h b/components/drivers/serial/device/virtual/render.h new file mode 100644 index 00000000000..bf254e9a2bb --- /dev/null +++ b/components/drivers/serial/device/virtual/render.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __VIRTUAL_RENDER_H__ +#define __VIRTUAL_RENDER_H__ + +#include + +struct render_color +{ + rt_uint32_t red; + rt_uint32_t green; + rt_uint32_t blue; + rt_uint32_t alpha; +}; + +struct render_point +{ + rt_uint32_t row; + rt_uint32_t col; +}; + +enum cursor +{ + CURSOR_HLINE, + CURSOR_VLINE, + CURSOR_BLOCK, +}; + +rt_err_t render_load_fbdev(struct rt_device *fbdev); +rt_err_t render_load_font(const char *psf_data, rt_size_t size, + struct render_color *foreground, struct render_color *background, + struct render_point *out_start_point, struct render_point *out_end_point); + +void render_clear_display(void); + +void render_set_foreground(struct render_color *foreground); +void render_set_background(struct render_color *background); +rt_inline void render_set_color(struct render_color *foreground, struct render_color *background) +{ + render_set_foreground(foreground); + render_set_background(background); +} + +void render_select_cursor(enum cursor shape); +void render_move_cursor(struct render_point *position); +void render_reset_cursor(struct render_point *out_position); +void render_return_cursor(struct render_point *out_position); +void render_current_cursor(struct render_point *out_position); + +void render_put_char(char ch); + +#endif /* __VIRTUAL_RENDER_H__ */ diff --git a/components/drivers/serial/device/virtual/virtual.c b/components/drivers/serial/device/virtual/virtual.c new file mode 100644 index 00000000000..0876ce84e33 --- /dev/null +++ b/components/drivers/serial/device/virtual/virtual.c @@ -0,0 +1,789 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "serial.virtual" +#define DBG_LVL DBG_INFO +#include + +#include "render.h" + +struct virtual_serial +{ + struct rt_serial_device parent; + + struct rt_device *fbdev; + struct rt_thread *render_task; + + struct rt_device *idev; + struct rt_input_handler input_handler; + + int param; + rt_bool_t input_int; + struct render_point start, end; + + struct rt_spinlock lock; + + rt_uint8_t in[RT_CONSOLEBUF_SIZE]; + rt_uint16_t in_head, in_tail; + rt_bool_t shift, ctrl, alt, caps; + rt_bool_t is_escape, is_bracket; + rt_bool_t in_pending; + + rt_uint8_t out[RT_CONSOLEBUF_SIZE]; + rt_uint16_t out_head, out_tail; + rt_bool_t out_pending; +}; + +#define raw_to_virtual_serial(raw) rt_container_of(raw, struct virtual_serial, parent) + +static rt_uint8_t virtual_font[] = +{ +#include "psf.inc" +}; + +enum +{ + COLOR_BLACK, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW, + COLOR_BLUE, + COLOR_MAGENTA, + COLOR_CYAN, + COLOR_LIGHT_GRAY, + COLOR_DARK_GRAY, + COLOR_LIGHT_RED, + COLOR_LIGHT_GREEN, + COLOR_LIGHT_YELLOW, + COLOR_LIGHT_BLUE, + COLOR_LIGHT_MAGENTA, + COLOR_LIGHT_CYAN, + COLOR_WHITE, +}; + +static struct render_color font_colors[] = +{ /* R G B A */ + [COLOR_BLACK] = { 0, 0, 0, 255 }, + [COLOR_RED] = { 205, 0, 0, 255 }, + [COLOR_GREEN] = { 0, 205, 0, 255 }, + [COLOR_YELLOW] = { 205, 205, 0, 255 }, + [COLOR_BLUE] = { 0, 0, 238, 255 }, + [COLOR_MAGENTA] = { 205, 0, 205, 255 }, + [COLOR_CYAN] = { 0, 205, 205, 255 }, + [COLOR_LIGHT_GRAY] = { 229, 229, 229, 255 }, + [COLOR_DARK_GRAY] = { 127, 127, 127, 255 }, + [COLOR_LIGHT_RED] = { 255, 0, 0, 255 }, + [COLOR_LIGHT_GREEN] = { 0, 255, 0, 255 }, + [COLOR_LIGHT_YELLOW] = { 255, 255, 0, 255 }, + [COLOR_LIGHT_BLUE] = { 92, 92, 255, 255 }, + [COLOR_LIGHT_MAGENTA] = { 255, 0, 255, 255 }, + [COLOR_LIGHT_CYAN] = { 0, 255, 255, 255 }, + [COLOR_WHITE] = { 255, 255, 255, 255 }, +}; + +static const rt_uint8_t unix_color_map[] = +{ + /* \033[Xm */ + [0] = COLOR_WHITE, + [30] = COLOR_BLACK, + [31] = COLOR_RED, + [32] = COLOR_GREEN, + [33] = COLOR_YELLOW, + [34] = COLOR_BLUE, + [35] = COLOR_MAGENTA, + [36] = COLOR_CYAN, + [37] = COLOR_LIGHT_GRAY, + [90] = COLOR_DARK_GRAY, + [91] = COLOR_LIGHT_RED, + [92] = COLOR_LIGHT_GREEN, + [93] = COLOR_LIGHT_YELLOW, + [94] = COLOR_LIGHT_BLUE, + [95] = COLOR_LIGHT_MAGENTA, + [96] = COLOR_LIGHT_CYAN, + [97] = COLOR_WHITE, +}; + + +static char key_map[] = +{ + [KEY_1] = '1', + [KEY_2] = '2', + [KEY_3] = '3', + [KEY_4] = '4', + [KEY_5] = '5', + [KEY_6] = '6', + [KEY_7] = '7', + [KEY_8] = '8', + [KEY_9] = '9', + [KEY_0] = '0', + [KEY_MINUS] = '-', + [KEY_EQUAL] = '=', + [KEY_Q] = 'q', + [KEY_W] = 'w', + [KEY_E] = 'e', + [KEY_R] = 'r', + [KEY_T] = 't', + [KEY_Y] = 'y', + [KEY_U] = 'u', + [KEY_I] = 'i', + [KEY_O] = 'o', + [KEY_P] = 'p', + [KEY_LEFTBRACE] = '[', + [KEY_RIGHTBRACE] = ']', + [KEY_A] = 'a', + [KEY_S] = 's', + [KEY_D] = 'd', + [KEY_F] = 'f', + [KEY_G] = 'g', + [KEY_H] = 'h', + [KEY_J] = 'j', + [KEY_K] = 'k', + [KEY_L] = 'l', + [KEY_SEMICOLON] = ';', + [KEY_APOSTROPHE] = '\'', + [KEY_BACKSLASH] = '\\', + [KEY_Z] = 'z', + [KEY_X] = 'x', + [KEY_C] = 'c', + [KEY_V] = 'v', + [KEY_B] = 'b', + [KEY_N] = 'n', + [KEY_M] = 'm', + [KEY_COMMA] = ',', + [KEY_DOT] = '.', + [KEY_SLASH] = '/', + [KEY_SPACE] = ' ', +}; + +static char key_shift_map[] = +{ + [KEY_1] = '!', + [KEY_2] = '@', + [KEY_3] = '#', + [KEY_4] = '$', + [KEY_5] = '%', + [KEY_6] = '^', + [KEY_7] = '&', + [KEY_8] = '*', + [KEY_9] = '(', + [KEY_0] = ')', + [KEY_MINUS] = '_', + [KEY_EQUAL] = '+', + [KEY_Q] = 'Q', + [KEY_W] = 'W', + [KEY_E] = 'E', + [KEY_R] = 'R', + [KEY_T] = 'T', + [KEY_Y] = 'Y', + [KEY_U] = 'U', + [KEY_I] = 'I', + [KEY_O] = 'O', + [KEY_P] = 'P', + [KEY_LEFTBRACE] = '{', + [KEY_RIGHTBRACE] = '}', + [KEY_A] = 'A', + [KEY_S] = 'S', + [KEY_D] = 'D', + [KEY_F] = 'F', + [KEY_G] = 'G', + [KEY_H] = 'H', + [KEY_J] = 'J', + [KEY_K] = 'K', + [KEY_L] = 'L', + [KEY_SEMICOLON] = ':', + [KEY_APOSTROPHE] = '\"', + [KEY_BACKSLASH] = '|', + [KEY_Z] = 'Z', + [KEY_X] = 'X', + [KEY_C] = 'C', + [KEY_V] = 'V', + [KEY_B] = 'B', + [KEY_N] = 'N', + [KEY_M] = 'M', + [KEY_COMMA] = '<', + [KEY_DOT] = '>', + [KEY_SLASH] = '?', + [KEY_SPACE] = ' ', +}; + +static enum cursor cursor_shape = CURSOR_BLOCK; + +static struct virtual_serial _vs = {}; + +static void push_out_char(struct virtual_serial *vs, char c); +static void virtual_serial_render_char(struct virtual_serial *vs, char ch); +static int pop_in_char(struct virtual_serial *vs); + +static rt_err_t virtual_serial_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + return RT_EOK; +} + +static rt_err_t virtual_serial_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct virtual_serial *vs = raw_to_virtual_serial(serial); + + switch (cmd) + { + case RT_DEVICE_CTRL_SUSPEND: + case RT_DEVICE_CTRL_CLR_INT: + vs->input_int = RT_FALSE; + break; + + case RT_DEVICE_CTRL_RESUME: + case RT_DEVICE_CTRL_SET_INT: + vs->input_int = RT_TRUE; + break; + + default: + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static int virtual_serial_putc(struct rt_serial_device *serial, char c) +{ + struct virtual_serial *vs = raw_to_virtual_serial(serial); + + rt_spin_lock(&vs->lock); + + if (!rt_critical_level()) + { + virtual_serial_render_char(vs, c); + } + else + { + push_out_char(vs, c); + rt_thread_resume(vs->render_task); + } + + rt_spin_unlock(&vs->lock); + + return 1; +} + +static int virtual_serial_getc(struct rt_serial_device *serial) +{ + struct virtual_serial *vs = raw_to_virtual_serial(serial); + + return pop_in_char(vs); +} + +static const struct rt_uart_ops virtual_serial_ops = +{ + .configure = virtual_serial_configure, + .control = virtual_serial_control, + .putc = virtual_serial_putc, + .getc = virtual_serial_getc, +}; + +static rt_bool_t parse_ctrl(struct virtual_serial *vs, char c) +{ + if (vs->is_bracket && c == 'm') + { + render_set_foreground(&font_colors[unix_color_map[vs->param]]); + + return RT_TRUE; + } + + if (c == 'J' && vs->param == 2) + { + render_clear_display(); + + return RT_TRUE; + } + + if (c == 'K' && vs->param == 2) + { + rt_uint32_t old_col; + struct render_point point; + + render_current_cursor(&point); + old_col = point.col; + + for (int i = vs->start.col; i < old_col; ++i) + { + --point.col; + render_move_cursor(&point); + render_put_char(' '); + } + + point.col = old_col; + render_move_cursor(&point); + + return RT_TRUE; + } + + if (c == 'H') + { + render_move_cursor(&vs->start); + + return RT_TRUE; + } + + return RT_FALSE; +} + +static void push_out_char(struct virtual_serial *vs, char c) +{ + rt_uint16_t next = (vs->out_head + 1) % sizeof(vs->out); + + if (next == vs->out_tail) + { + /* Full */ + return; + } + + vs->out[vs->out_head] = c; + vs->out_head = next; + vs->out_pending = RT_TRUE; +} + +static int pop_out_char(struct virtual_serial *vs) +{ + int c; + + if (vs->out_head == vs->out_tail) + { + return -1; + } + + c = vs->out[vs->out_tail]; + vs->out_tail = (vs->out_tail + 1) % sizeof(vs->out); + + return c; +} + +static void push_in_char(struct virtual_serial *vs, char c) +{ + rt_uint16_t next = (vs->in_head + 1) % sizeof(vs->in); + + if (next == vs->in_tail) + { + /* Full, drop */ + return; + } + + vs->in[vs->in_head] = (rt_uint8_t)c; + vs->in_head = next; + vs->in_pending = RT_TRUE; +} + +static int pop_in_char(struct virtual_serial *vs) +{ + int ch; + + if (vs->in_head == vs->in_tail) + { + return -1; + } + + ch = vs->in[vs->in_tail]; + vs->in_tail = (vs->in_tail + 1) % sizeof(vs->in); + + return ch; +} + +static char key_to_char(struct virtual_serial *vs, int code) +{ + char base, shiftc; + + base = key_map[code]; + shiftc = key_shift_map[code]; + + if (!base && !shiftc) + { + return 0; + } + + if (code >= KEY_A && code <= KEY_Z) + { + rt_bool_t upper = (vs->shift ^ vs->caps); + + return upper ? (shiftc ? shiftc : base - 'a' + 'A') + : (base ? base : shiftc + 'a' - 'A'); + } + + if (vs->shift && shiftc) + { + return shiftc; + } + + return base; +} + +static void virtual_serial_render_char(struct virtual_serial *vs, char ch) +{ + if (vs->is_escape) + { + if (ch == '[') + { + vs->param = 0; + vs->is_bracket = RT_TRUE; + return; + } + else if (vs->is_bracket && (ch >= '0' && ch <= '9')) + { + vs->param = vs->param * 10 + (ch - '0'); + return; + } + else + { + if (!parse_ctrl(vs, ch)) + { + goto _render_char; + } + + vs->is_bracket = RT_FALSE; + vs->is_escape = RT_FALSE; + return; + } + } + +_render_char: + if (ch >= ' ') + { + render_put_char(ch); + } + else + { + struct render_point point; + + switch (ch) + { + case '\n': + render_return_cursor(RT_NULL); + break; + + case '\033': + vs->is_escape = RT_TRUE; + break; + + case '\t': + render_current_cursor(&point); + point.col = (point.col / 4 + 1) * 4; + if (point.col > vs->end.col) + { + point.col -= vs->end.col; + point.row++; + } + render_move_cursor(&point); + break; + + case '\r': + render_reset_cursor(RT_NULL); + break; + + case '\b': + render_current_cursor(&point); + if (point.col > 0) + { + point.col--; + render_move_cursor(&point); + } + break; + + default: + break; + } + } +} + +static void virtual_serial_render_task(void *param) +{ + struct virtual_serial *vs = param; + + while (RT_TRUE) + { + rt_spin_lock(&vs->lock); + + if (vs->out_head == vs->out_tail) + { + rt_spin_unlock(&vs->lock); + rt_thread_suspend(rt_thread_self()); + rt_schedule(); + + continue; + } + + virtual_serial_render_char(vs, pop_out_char(vs)); + + rt_spin_unlock(&vs->lock); + } +} + +static rt_bool_t virtual_serial_input_test_cap(struct rt_input_handler *handler, + struct rt_input_device *idev) +{ + return rt_bitmap_test_bit(idev->key_map, KEY_ENTER); +} + +static rt_bool_t virtual_serial_input_callback(struct rt_input_handler *handler, + struct rt_input_event *ev) +{ + char ch; + struct virtual_serial *vs = handler->priv; + + if (ev->type == EV_KEY) + { + if (!vs->input_int) + { + return RT_TRUE; + } + + if (ev->value == 0) + { + switch (ev->code) + { + case KEY_LEFTSHIFT: + case KEY_RIGHTSHIFT: + vs->shift = RT_FALSE; + break; + + case KEY_LEFTCTRL: + case KEY_RIGHTCTRL: + vs->ctrl = RT_FALSE; + break; + + case KEY_LEFTALT: + case KEY_RIGHTALT: + vs->alt = RT_FALSE; + break; + + default: + break; + } + + return RT_TRUE; + } + + if (ev->value != 1) + { + return RT_TRUE; + } + + switch (ev->code) + { + case KEY_LEFTSHIFT: + case KEY_RIGHTSHIFT: + vs->shift = RT_TRUE; + break; + + case KEY_LEFTCTRL: + case KEY_RIGHTCTRL: + vs->ctrl = RT_TRUE; + break; + + case KEY_LEFTALT: + case KEY_RIGHTALT: + vs->alt = RT_TRUE; + break; + + case KEY_CAPSLOCK: + vs->caps = !vs->caps; + break; + + case KEY_ENTER: + case KEY_KPENTER: + push_in_char(vs, '\r'); + break; + + case KEY_BACKSPACE: + push_in_char(vs, '\b'); + break; + + case KEY_TAB: + push_in_char(vs, '\t'); + break; + + case KEY_UP: + push_in_char(vs, 0x1b); + push_in_char(vs, '['); + push_in_char(vs, 'A'); + break; + + case KEY_DOWN: + push_in_char(vs, 0x1b); + push_in_char(vs, '['); + push_in_char(vs, 'B'); + break; + + case KEY_LEFT: + push_in_char(vs, 0x1b); + push_in_char(vs, '['); + push_in_char(vs, 'D'); + break; + + case KEY_RIGHT: + push_in_char(vs, 0x1b); + push_in_char(vs, '['); + push_in_char(vs, 'C'); + break; + + default: + if ((ch = key_to_char(vs, ev->code))) + { + push_in_char(vs, ch); + } + break; + } + + return RT_TRUE; + } + else if (ev->type == EV_SYN) + { + if (vs->input_int && vs->in_pending) + { + vs->in_pending = RT_FALSE; + rt_hw_serial_isr(&vs->parent, RT_SERIAL_EVENT_RX_IND); + } + + return RT_TRUE; + } + + return RT_FALSE; +} + +static int virtual_serial_setup(void) +{ + rt_err_t err; + + for (int id = 0; id < RT_DM_IDA_NUM; ++id) + { + if ((_vs.fbdev = rt_dm_device_find(MASTER_ID_GRAPHIC_FRAMEBUFFER, id))) + { + break; + } + } + + if (!_vs.fbdev) + { + return (int)-RT_ENOSYS; + } + + _vs.input_handler.idev = RT_NULL; + _vs.input_handler.identify = &virtual_serial_input_test_cap; + _vs.input_handler.callback = &virtual_serial_input_callback; + _vs.input_handler.priv = &_vs; + + if ((err = rt_input_add_handler(&_vs.input_handler))) + { + return (int)err; + } + + if (!(_vs.render_task = rt_thread_create("vuart", virtual_serial_render_task, &_vs, + DM_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 2, 32))) + { + goto _fail; + } + + if ((err = render_load_fbdev(_vs.fbdev))) + { + LOG_E("Load fbdev error = %s", rt_strerror(err)); + + goto _fail; + } + + if ((err = render_load_font((void *)virtual_font, sizeof(virtual_font), + &font_colors[COLOR_WHITE], &font_colors[COLOR_BLACK], + &_vs.start, &_vs.end))) + { + LOG_E("Load PSF font error = %s", rt_strerror(err)); + + goto _fail; + } + + render_select_cursor(cursor_shape); + + rt_device_open(_vs.fbdev, 0); + rt_spin_lock_init(&_vs.lock); + + rt_thread_startup(_vs.render_task); + + _vs.input_int = RT_TRUE; + _vs.parent.ops = &virtual_serial_ops; + _vs.parent.config = (struct serial_configure)RT_SERIAL_CONFIG_DEFAULT; + rt_hw_serial_register(&_vs.parent, "vuart", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, &_vs); + + return 0; + +_fail: + rt_input_del_handler(&_vs.input_handler); + + return (int)err; +} +INIT_EXPORT(virtual_serial_setup, "3.end"); + +#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH) +static int virtual_serial_cmd_color(int argc, char**argv) +{ + const char *color; + static const char * const color_names[] = + { + [COLOR_BLACK] = "black", + [COLOR_RED] = "red", + [COLOR_GREEN] = "green", + [COLOR_YELLOW] = "yellow", + [COLOR_BLUE] = "blue", + [COLOR_MAGENTA] = "magenta", + [COLOR_CYAN] = "cyan", + [COLOR_LIGHT_GRAY] = "light gray", + [COLOR_DARK_GRAY] = "dark gray", + [COLOR_LIGHT_RED] = "light red", + [COLOR_LIGHT_GREEN] = "light green", + [COLOR_LIGHT_YELLOW] = "light yellow", + [COLOR_LIGHT_BLUE] = "light blue", + [COLOR_LIGHT_MAGENTA] = "light magenta", + [COLOR_LIGHT_CYAN] = "light cyan", + [COLOR_WHITE] = "white", + }; + + if (argc != 2) + { + goto _help; + } + + color = argv[1]; + + if (!((color[0] >= '0' && color[0] <= '9') || (color[0] >= 'a' && color[0] <= 'f')) || + !((color[1] >= '0' && color[1] <= '9') || (color[1] >= 'a' && color[1] <= 'f'))) + { + goto _help; + } + + if (color[0] == color[1]) + { + rt_kprintf("foreground cannot equal background\n"); + + return (int)-RT_EINVAL; + } + + render_set_foreground(&font_colors[color[0] - (color[0] >= 'a' ? ('a' - 10) : '0')]); + render_set_background(&font_colors[color[1] - (color[1] >= 'a' ? ('a' - 10) : '0')]); + + return 0; + +_help: + rt_kprintf("Usage: color [attr]\nattr:\n"); + + for (int i = 0; i < RT_ARRAY_SIZE(font_colors); ++i) + { + rt_kprintf("\t%x = %s\n", i, color_names[i]); + } + + return (int)-RT_EINVAL; +} +MSH_CMD_EXPORT_ALIAS(virtual_serial_cmd_color, color, set virtual serial foreground and background); +#endif /* RT_USING_CONSOLE && RT_USING_MSH */