| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright 2017-2019 NXP |
| */ |
| |
| #include <assert.h> |
| #include <drivers/imx_uart.h> |
| #include <io.h> |
| #include <keep.h> |
| #include <kernel/dt.h> |
| #include <util.h> |
| |
| #define STAT 0x14 |
| #define DATA 0x1C |
| #define STAT_TDRE BIT(23) |
| #define STAT_RDRF BIT(21) |
| #define STAT_OR BIT(19) |
| |
| static vaddr_t chip_to_base(struct serial_chip *chip) |
| { |
| struct imx_uart_data *pd = |
| container_of(chip, struct imx_uart_data, chip); |
| |
| return io_pa_or_va(&pd->base); |
| } |
| |
| static void imx_lpuart_flush(struct serial_chip *chip __unused) |
| { |
| } |
| |
| static int imx_lpuart_getchar(struct serial_chip *chip) |
| { |
| int ch = 0; |
| vaddr_t base = chip_to_base(chip); |
| |
| while (io_read32(base + STAT) & STAT_RDRF) |
| ; |
| |
| ch = io_read32(base + DATA) & 0x3ff; |
| |
| if (io_read32(base + STAT) & STAT_OR) |
| io_write32(base + STAT, STAT_OR); |
| |
| return ch; |
| } |
| |
| static void imx_lpuart_putc(struct serial_chip *chip, int ch) |
| { |
| vaddr_t base = chip_to_base(chip); |
| |
| while (!(io_read32(base + STAT) & STAT_TDRE)) |
| ; |
| |
| io_write32(base + DATA, ch); |
| } |
| |
| static const struct serial_ops imx_lpuart_ops = { |
| .flush = imx_lpuart_flush, |
| .getchar = imx_lpuart_getchar, |
| .putc = imx_lpuart_putc, |
| }; |
| KEEP_PAGER(imx_lpuart_ops); |
| |
| void imx_uart_init(struct imx_uart_data *pd, paddr_t base) |
| { |
| pd->base.pa = base; |
| pd->chip.ops = &imx_lpuart_ops; |
| |
| /* |
| * Do nothing, debug uart(sc lpuart) shared with normal world, |
| * everything for uart initialization is done in bootloader. |
| */ |
| } |
| |
| #ifdef CFG_DT |
| static struct serial_chip *imx_lpuart_dev_alloc(void) |
| { |
| struct imx_uart_data *pd = calloc(1, sizeof(*pd)); |
| |
| if (!pd) |
| return NULL; |
| |
| return &pd->chip; |
| } |
| |
| static int imx_lpuart_dev_init(struct serial_chip *chip, const void *fdt, |
| int offs, const char *parms) |
| { |
| struct imx_uart_data *pd = |
| container_of(chip, struct imx_uart_data, chip); |
| vaddr_t vbase = 0; |
| paddr_t pbase = 0; |
| size_t size = 0; |
| |
| if (parms && parms[0]) |
| IMSG("imx_lpuart: device parameters ignored (%s)", parms); |
| |
| if (dt_map_dev(fdt, offs, &vbase, &size) < 0) |
| return -1; |
| |
| pbase = virt_to_phys((void *)vbase); |
| imx_uart_init(pd, pbase); |
| |
| return 0; |
| } |
| |
| static void imx_lpuart_dev_free(struct serial_chip *chip) |
| { |
| struct imx_uart_data *pd = |
| container_of(chip, struct imx_uart_data, chip); |
| |
| free(pd); |
| } |
| |
| static const struct serial_driver imx_lpuart_driver = { |
| .dev_alloc = imx_lpuart_dev_alloc, |
| .dev_init = imx_lpuart_dev_init, |
| .dev_free = imx_lpuart_dev_free, |
| }; |
| |
| static const struct dt_device_match imx_match_table[] = { |
| { .compatible = "fsl,imx7ulp-lpuart" }, |
| { .compatible = "fsl,imx8qm-lpuart" }, |
| { 0 } |
| }; |
| |
| const struct dt_driver imx_dt_driver __dt_driver = { |
| .name = "imx_lpuart", |
| .match_table = imx_match_table, |
| .driver = &imx_lpuart_driver, |
| }; |
| |
| #endif /* CFG_DT */ |