| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright (C) 2015 Freescale Semiconductor, Inc. |
| * All rights reserved. |
| * Copyright 2018-2019 NXP. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <assert.h> |
| #include <drivers/imx_uart.h> |
| #include <io.h> |
| #include <keep.h> |
| #include <kernel/dt.h> |
| #include <util.h> |
| |
| /* Register definitions */ |
| #define URXD 0x0 /* Receiver Register */ |
| #define UTXD 0x40 /* Transmitter Register */ |
| #define UCR1 0x80 /* Control Register 1 */ |
| #define UCR2 0x84 /* Control Register 2 */ |
| #define UCR3 0x88 /* Control Register 3 */ |
| #define UCR4 0x8c /* Control Register 4 */ |
| #define UFCR 0x90 /* FIFO Control Register */ |
| #define USR1 0x94 /* Status Register 1 */ |
| #define USR2 0x98 /* Status Register 2 */ |
| #define UESC 0x9c /* Escape Character Register */ |
| #define UTIM 0xa0 /* Escape Timer Register */ |
| #define UBIR 0xa4 /* BRM Incremental Register */ |
| #define UBMR 0xa8 /* BRM Modulator Register */ |
| #define UBRC 0xac /* Baud Rate Count Register */ |
| #define UTS 0xb4 /* UART Test Register (mx31) */ |
| |
| /* UART Control Register Bit Fields.*/ |
| #define URXD_CHARRDY (1<<15) |
| #define URXD_ERR (1<<14) |
| #define URXD_OVRRUN (1<<13) |
| #define URXD_FRMERR (1<<12) |
| #define URXD_BRK (1<<11) |
| #define URXD_PRERR (1<<10) |
| #define URXD_RX_DATA (0xFF) |
| #define UCR1_ADEN (1<<15) /* Auto dectect interrupt */ |
| #define UCR1_ADBR (1<<14) /* Auto detect baud rate */ |
| #define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ |
| #define UCR1_IDEN (1<<12) /* Idle condition interrupt */ |
| #define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ |
| #define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ |
| #define UCR1_IREN (1<<7) /* Infrared interface enable */ |
| #define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */ |
| #define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ |
| #define UCR1_SNDBRK (1<<4) /* Send break */ |
| #define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ |
| #define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */ |
| #define UCR1_DOZE (1<<1) /* Doze */ |
| #define UCR1_UARTEN (1<<0) /* UART enabled */ |
| |
| #define UTS_FRCPERR (1<<13) /* Force parity error */ |
| #define UTS_LOOP (1<<12) /* Loop tx and rx */ |
| #define UTS_TXEMPTY (1<<6) /* TxFIFO empty */ |
| #define UTS_RXEMPTY (1<<5) /* RxFIFO empty */ |
| #define UTS_TXFULL (1<<4) /* TxFIFO full */ |
| #define UTS_RXFULL (1<<3) /* RxFIFO full */ |
| #define UTS_SOFTRST (1<<0) /* Software reset */ |
| |
| 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_uart_flush(struct serial_chip *chip) |
| { |
| vaddr_t base = chip_to_base(chip); |
| |
| |
| while (!(io_read32(base + UTS) & UTS_TXEMPTY)) |
| if (!(io_read32(base + UCR1) & UCR1_UARTEN)) |
| return; |
| } |
| |
| static int imx_uart_getchar(struct serial_chip *chip) |
| { |
| vaddr_t base = chip_to_base(chip); |
| |
| while (io_read32(base + UTS) & UTS_RXEMPTY) |
| ; |
| |
| return (io_read32(base + URXD) & URXD_RX_DATA); |
| } |
| |
| static void imx_uart_putc(struct serial_chip *chip, int ch) |
| { |
| vaddr_t base = chip_to_base(chip); |
| |
| /* Wait until there's space in the TX FIFO */ |
| while (io_read32(base + UTS) & UTS_TXFULL) |
| if (!(io_read32(base + UCR1) & UCR1_UARTEN)) |
| return; |
| |
| io_write32(base + UTXD, ch); |
| } |
| |
| static const struct serial_ops imx_uart_ops = { |
| .flush = imx_uart_flush, |
| .getchar = imx_uart_getchar, |
| .putc = imx_uart_putc, |
| }; |
| KEEP_PAGER(imx_uart_ops); |
| |
| void imx_uart_init(struct imx_uart_data *pd, paddr_t base) |
| { |
| pd->base.pa = base; |
| pd->chip.ops = &imx_uart_ops; |
| |
| /* |
| * Do nothing, debug uart(uart0) share with normal world, |
| * everything for uart0 initialization is done in bootloader. |
| */ |
| } |
| |
| #ifdef CFG_DT |
| static struct serial_chip *imx_uart_dev_alloc(void) |
| { |
| struct imx_uart_data *pd = calloc(1, sizeof(*pd)); |
| |
| if (!pd) |
| return NULL; |
| |
| return &pd->chip; |
| } |
| |
| static int imx_uart_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_uart: 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_uart_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_uart_driver = { |
| .dev_alloc = imx_uart_dev_alloc, |
| .dev_init = imx_uart_dev_init, |
| .dev_free = imx_uart_dev_free, |
| }; |
| |
| static const struct dt_device_match imx_match_table[] = { |
| { .compatible = "fsl,imx6q-uart" }, |
| { 0 } |
| }; |
| |
| const struct dt_driver imx_dt_driver __dt_driver = { |
| .name = "imx_uart", |
| .match_table = imx_match_table, |
| .driver = &imx_uart_driver, |
| }; |
| |
| #endif /* CFG_DT */ |