/* * Copyright (c) 2018 Antmicro * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT microsemi_coreuart #include #include #include /* UART REGISTERS DEFINITIONS */ /* TX register */ #define TXDATA_REG_OFFSET 0x0 #define TXDATA_OFFSET 0x0 #define TXDATA_MASK 0xFF #define TXDATA_SHIFT 0 /* RX register */ #define RXDATA_REG_OFFSET 0x4 #define RXDATA_OFFSET 0x4 #define RXDATA_MASK 0xFF #define RXDATA_SHIFT 0 /* Control1 register */ #define CTRL1_REG_OFFSET 0x8 /* Baud value lower 8 bits */ #define CTRL1_BAUDVALUE_OFFSET 0x8 #define CTRL1_BAUDVALUE_MASK 0xFF #define CTRL1_BAUDVALUE_SHIFT 0 /* Control2 register */ #define CTRL2_REG_OFFSET 0xC /* Bit length */ #define CTRL2_BIT_LENGTH_OFFSET 0xC #define CTRL2_BIT_LENGTH_MASK 0x01 #define CTRL2_BIT_LENGTH_SHIFT 0 /* Parity enable */ #define CTRL2_PARITY_EN_OFFSET 0xC #define CTRL2_PARITY_EN_MASK 0x02 #define CTRL2_PARITY_EN_SHIFT 1 /* Odd/even parity configuration */ #define CTRL2_ODD_EVEN_OFFSET 0xC #define CTRL2_ODD_EVEN_MASK 0x04 #define CTRL2_ODD_EVEN_SHIFT 2 /* Baud value higher 5 bits */ #define CTRL2_BAUDVALUE_OFFSET 0xC #define CTRL2_BAUDVALUE_MASK 0xF8 #define CTRL2_BAUDVALUE_SHIFT 3 /* Status register */ #define StatusReg_REG_OFFSET 0x10 #define STATUS_REG_OFFSET 0x10 /* TX ready */ #define STATUS_TXRDY_OFFSET 0x10 #define STATUS_TXRDY_MASK 0x01 #define STATUS_TXRDY_SHIFT 0 /* Receive full - raised even when 1 char arrived */ #define STATUS_RXFULL_OFFSET 0x10 #define STATUS_RXFULL_MASK 0x02 #define STATUS_RXFULL_SHIFT 1 /* Parity error */ #define STATUS_PARITYERR_OFFSET 0x10 #define STATUS_PARITYERR_MASK 0x04 #define STATUS_PARITYERR_SHIFT 2 /* Overflow */ #define STATUS_OVERFLOW_OFFSET 0x10 #define STATUS_OVERFLOW_MASK 0x08 #define STATUS_OVERFLOW_SHIFT 3 /* Frame error */ #define STATUS_FRAMERR_OFFSET 0x10 #define STATUS_FRAMERR_MASK 0x10 #define STATUS_FRAMERR_SHIFT 4 /* Data bits length defines */ #define DATA_7_BITS 0x00 #define DATA_8_BITS 0x01 /* Parity defines */ #define NO_PARITY 0x00 #define EVEN_PARITY 0x02 #define ODD_PARITY 0x06 /* Error Status definitions */ #define UART_PARITY_ERROR 0x01 #define UART_OVERFLOW_ERROR 0x02 #define UART_FRAMING_ERROR 0x04 #define BAUDVALUE_LSB ((uint16_t)(0x00FF)) #define BAUDVALUE_MSB ((uint16_t)(0xFF00)) #define BAUDVALUE_SHIFT ((uint8_t)(5)) #ifdef CONFIG_UART_INTERRUPT_DRIVEN static struct k_thread rx_thread; static K_KERNEL_STACK_DEFINE(rx_stack, 512); #endif struct uart_miv_regs_t { uint8_t tx; uint8_t reserved0[3]; uint8_t rx; uint8_t reserved1[3]; uint8_t ctrlreg1; uint8_t reserved2[3]; uint8_t ctrlreg2; uint8_t reserved3[3]; uint8_t status; }; #ifdef CONFIG_UART_INTERRUPT_DRIVEN typedef void (*irq_cfg_func_t)(const struct device *dev); #endif struct uart_miv_device_config { uint32_t uart_addr; uint32_t sys_clk_freq; uint32_t line_config; uint32_t baud_rate; #ifdef CONFIG_UART_INTERRUPT_DRIVEN irq_cfg_func_t cfg_func; #endif }; struct uart_miv_data { #ifdef CONFIG_UART_INTERRUPT_DRIVEN const struct device *dev; uart_irq_callback_user_data_t callback; void *cb_data; #endif }; #define DEV_CFG(dev) \ ((const struct uart_miv_device_config * const) \ (dev)->config) #define DEV_UART(dev) \ ((struct uart_miv_regs_t *)(DEV_CFG(dev))->uart_addr) #define DEV_DATA(dev) \ ((struct uart_miv_data * const)(dev)->data) static void uart_miv_poll_out(const struct device *dev, unsigned char c) { volatile struct uart_miv_regs_t *uart = DEV_UART(dev); while (!(uart->status & STATUS_TXRDY_MASK)) { } uart->tx = c; } static int uart_miv_poll_in(const struct device *dev, unsigned char *c) { volatile struct uart_miv_regs_t *uart = DEV_UART(dev); if (uart->status & STATUS_RXFULL_MASK) { *c = (unsigned char)(uart->rx & RXDATA_MASK); return 0; } return -1; } static int uart_miv_err_check(const struct device *dev) { volatile struct uart_miv_regs_t *uart = DEV_UART(dev); uint32_t flags = uart->status; int err = 0; if (flags & STATUS_PARITYERR_MASK) { err |= UART_PARITY_ERROR; } if (flags & STATUS_OVERFLOW_MASK) { err |= UART_OVERFLOW_ERROR; } if (flags & STATUS_FRAMERR_MASK) { err |= UART_FRAMING_ERROR; } return err; } #ifdef CONFIG_UART_INTERRUPT_DRIVEN static int uart_miv_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) { volatile struct uart_miv_regs_t *uart = DEV_UART(dev); int i; for (i = 0; i < size && (uart->status & STATUS_TXRDY_MASK); i++) { uart->tx = tx_data[i]; } return i; } static int uart_miv_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) { volatile struct uart_miv_regs_t *uart = DEV_UART(dev); int i; for (i = 0; i < size; i++) { if (uart->status & STATUS_RXFULL_MASK) { rx_data[i] = (unsigned char)(uart->rx & RXDATA_MASK); } else { break; } } return i; } static void uart_miv_irq_tx_enable(const struct device *dev) { ARG_UNUSED(dev); } static void uart_miv_irq_tx_disable(const struct device *dev) { ARG_UNUSED(dev); } static int uart_miv_irq_tx_ready(const struct device *dev) { volatile struct uart_miv_regs_t *uart = DEV_UART(dev); return !(uart->status & STATUS_TXRDY_MASK); } static int uart_miv_irq_tx_complete(const struct device *dev) { ARG_UNUSED(dev); return 1; } static void uart_miv_irq_rx_enable(const struct device *dev) { ARG_UNUSED(dev); } static void uart_miv_irq_rx_disable(const struct device *dev) { ARG_UNUSED(dev); } static int uart_miv_irq_rx_ready(const struct device *dev) { volatile struct uart_miv_regs_t *uart = DEV_UART(dev); return !!(uart->status & STATUS_RXFULL_MASK); } static void uart_miv_irq_err_enable(const struct device *dev) { ARG_UNUSED(dev); } static void uart_miv_irq_err_disable(const struct device *dev) { ARG_UNUSED(dev); } static int uart_miv_irq_is_pending(const struct device *dev) { volatile struct uart_miv_regs_t *uart = DEV_UART(dev); return !!(uart->status & STATUS_RXFULL_MASK); } static int uart_miv_irq_update(const struct device *dev) { return 1; } static void uart_miv_irq_handler(const struct device *dev) { struct uart_miv_data *data = DEV_DATA(dev); if (data->callback) { data->callback(dev, data->cb_data); } } /* * This thread is a workaround for IRQs that are not connected in Mi-V. * Since we cannot rely on IRQs, the rx_thread is working instead and * polling for data. The thread calls the registered callback when data * arrives. */ void uart_miv_rx_thread(void *arg1, void *arg2, void *arg3) { struct uart_miv_data *data = (struct uart_miv_data *)arg1; const struct device *dev = data->dev; volatile struct uart_miv_regs_t *uart = DEV_UART(dev); const struct uart_miv_device_config *const cfg = DEV_CFG(dev); /* Make it go to sleep for a period no longer than * time to receive next character. */ uint32_t delay = 1000000 / cfg->baud_rate; ARG_UNUSED(arg2); ARG_UNUSED(arg3); while (1) { if (uart->status & STATUS_RXFULL_MASK) { uart_miv_irq_handler(dev); } k_sleep(K_USEC(delay)); } } static void uart_miv_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, void *cb_data) { struct uart_miv_data *data = DEV_DATA(dev); data->callback = cb; data->cb_data = cb_data; } #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ static int uart_miv_init(const struct device *dev) { const struct uart_miv_device_config *const cfg = DEV_CFG(dev); volatile struct uart_miv_regs_t *uart = DEV_UART(dev); /* Calculate divider value to set baudrate */ uint16_t baud_value = (cfg->sys_clk_freq / (cfg->baud_rate * 16U)) - 1; /* Set baud rate */ uart->ctrlreg1 = (uint8_t)(baud_value & BAUDVALUE_LSB); uart->ctrlreg2 = (uint8_t)(cfg->line_config) | (uint8_t)((baud_value & BAUDVALUE_MSB) >> BAUDVALUE_SHIFT); #ifdef CONFIG_UART_INTERRUPT_DRIVEN /* Setup thread polling for data */ cfg->cfg_func(dev); #endif return 0; } static const struct uart_driver_api uart_miv_driver_api = { .poll_in = uart_miv_poll_in, .poll_out = uart_miv_poll_out, .err_check = uart_miv_err_check, #ifdef CONFIG_UART_INTERRUPT_DRIVEN .fifo_fill = uart_miv_fifo_fill, .fifo_read = uart_miv_fifo_read, .irq_tx_enable = uart_miv_irq_tx_enable, .irq_tx_disable = uart_miv_irq_tx_disable, .irq_tx_ready = uart_miv_irq_tx_ready, .irq_tx_complete = uart_miv_irq_tx_complete, .irq_rx_enable = uart_miv_irq_rx_enable, .irq_rx_disable = uart_miv_irq_rx_disable, .irq_rx_ready = uart_miv_irq_rx_ready, .irq_err_enable = uart_miv_irq_err_enable, .irq_err_disable = uart_miv_irq_err_disable, .irq_is_pending = uart_miv_irq_is_pending, .irq_update = uart_miv_irq_update, .irq_callback_set = uart_miv_irq_callback_set, #endif }; /* This driver is single-instance. */ BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1, "unsupported uart_miv instance"); #if DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay) static struct uart_miv_data uart_miv_data_0; #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void uart_miv_irq_cfg_func_0(const struct device *dev); #endif static const struct uart_miv_device_config uart_miv_dev_cfg_0 = { .uart_addr = DT_INST_REG_ADDR(0), .sys_clk_freq = DT_INST_PROP(0, clock_frequency), .line_config = MIV_UART_0_LINECFG, .baud_rate = DT_INST_PROP(0, current_speed), #ifdef CONFIG_UART_INTERRUPT_DRIVEN .cfg_func = uart_miv_irq_cfg_func_0, #endif }; DEVICE_DT_INST_DEFINE(0, uart_miv_init, NULL, &uart_miv_data_0, &uart_miv_dev_cfg_0, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, (void *)&uart_miv_driver_api); #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void uart_miv_irq_cfg_func_0(const struct device *dev) { struct uart_miv_data *data = DEV_DATA(dev); data->dev = dev; /* Create a thread which will poll for data - replacement for IRQ */ k_thread_create(&rx_thread, rx_stack, 500, uart_miv_rx_thread, data, NULL, NULL, K_PRIO_COOP(2), 0, K_NO_WAIT); } #endif #endif /* DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay) */