/* * Copyright (c) 2019-2020 Cobham Gaisler AB * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT gaisler_apbuart #include #include #include #include /* APBUART registers * * Offset | Name | Description * ------ | ------ | ---------------------------------------- * 0x0000 | data | UART data register * 0x0004 | status | UART status register * 0x0008 | ctrl | UART control register * 0x000c | scaler | UART scaler register * 0x0010 | debug | UART FIFO debug register */ struct apbuart_regs { /** @brief UART data register * * Bit | Name | Description * ------ | ------ | ---------------------------------------- * 7-0 | data | Holding register or FIFO */ uint32_t data; /* 0x0000 */ /** @brief UART status register * * Bit | Name | Description * ------ | ------ | ---------------------------------------- * 31-26 | RCNT | Receiver FIFO count * 25-20 | TCNT | Transmitter FIFO count * 10 | RF | Receiver FIFO full * 9 | TF | Transmitter FIFO full * 8 | RH | Receiver FIFO half-full * 7 | TH | Transmitter FIFO half-full * 6 | FE | Framing error * 5 | PE | Parity error * 4 | OV | Overrun * 3 | BR | Break received * 2 | TE | Transmitter FIFO empty * 1 | TS | Transmitter shift register empty * 0 | DR | Data ready */ uint32_t status; /* 0x0004 */ /** @brief UART control register * * Bit | Name | Description * ------ | ------ | ---------------------------------------- * 31 | FA | FIFOs available * 14 | SI | Transmitter shift register empty interrupt enable * 13 | DI | Delayed interrupt enable * 12 | BI | Break interrupt enable * 11 | DB | FIFO debug mode enable * 10 | RF | Receiver FIFO interrupt enable * 9 | TF | Transmitter FIFO interrupt enable * 8 | EC | External clock * 7 | LB | Loop back * 6 | FL | Flow control * 5 | PE | Parity enable * 4 | PS | Parity select * 3 | TI | Transmitter interrupt enable * 2 | RI | Receiver interrupt enable * 1 | TE | Transmitter enable * 0 | RE | Receiver enable */ uint32_t ctrl; /* 0x0008 */ /** @brief UART scaler register * * Bit | Name | Description * ------ | ------ | ---------------------------------------- * 11-0 | RELOAD | Scaler reload value */ uint32_t scaler; /* 0x000c */ /** @brief UART FIFO debug register * * Bit | Name | Description * ------ | ------ | ---------------------------------------- * 7-0 | data | Holding register or FIFO */ uint32_t debug; /* 0x0010 */ }; /* APBUART register bits. */ /* Control register */ #define APBUART_CTRL_FA (1 << 31) #define APBUART_CTRL_DB (1 << 11) #define APBUART_CTRL_RF (1 << 10) #define APBUART_CTRL_TF (1 << 9) #define APBUART_CTRL_LB (1 << 7) #define APBUART_CTRL_FL (1 << 6) #define APBUART_CTRL_PE (1 << 5) #define APBUART_CTRL_PS (1 << 4) #define APBUART_CTRL_TI (1 << 3) #define APBUART_CTRL_RI (1 << 2) #define APBUART_CTRL_TE (1 << 1) #define APBUART_CTRL_RE (1 << 0) /* Status register */ #define APBUART_STATUS_RF (1 << 10) #define APBUART_STATUS_TF (1 << 9) #define APBUART_STATUS_RH (1 << 8) #define APBUART_STATUS_TH (1 << 7) #define APBUART_STATUS_FE (1 << 6) #define APBUART_STATUS_PE (1 << 5) #define APBUART_STATUS_OV (1 << 4) #define APBUART_STATUS_BR (1 << 3) #define APBUART_STATUS_TE (1 << 2) #define APBUART_STATUS_TS (1 << 1) #define APBUART_STATUS_DR (1 << 0) /* For APBUART implemented without FIFO */ #define APBUART_STATUS_HOLD_REGISTER_EMPTY (1 << 2) struct apbuart_dev_cfg { struct apbuart_regs *regs; int interrupt; }; struct apbuart_dev_data { int usefifo; #ifdef CONFIG_UART_INTERRUPT_DRIVEN uart_irq_callback_user_data_t cb; void *cb_data; #endif }; /* * This routine waits for the TX holding register or TX FIFO to be ready and * then it writes a character to the data register. */ static void apbuart_poll_out(const struct device *dev, unsigned char x) { const struct apbuart_dev_cfg *config = dev->config; struct apbuart_dev_data *data = dev->data; volatile struct apbuart_regs *regs = (void *) config->regs; if (data->usefifo) { /* Transmitter FIFO full flag is available. */ while (regs->status & APBUART_STATUS_TF) { ; } } else { /* * Transmitter "hold register empty" AKA "FIFO empty" flag is * available. */ while (!(regs->status & APBUART_STATUS_HOLD_REGISTER_EMPTY)) { ; } } regs->data = x & 0xff; } static int apbuart_poll_in(const struct device *dev, unsigned char *c) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; if ((regs->status & APBUART_STATUS_DR) == 0) { return -1; } *c = regs->data & 0xff; return 0; } static int apbuart_err_check(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; const uint32_t status = regs->status; int err = 0; if (status & APBUART_STATUS_FE) { err |= UART_ERROR_FRAMING; } if (status & APBUART_STATUS_PE) { err |= UART_ERROR_PARITY; } if (status & APBUART_STATUS_OV) { err |= UART_ERROR_OVERRUN; } if (status & APBUART_STATUS_BR) { err |= UART_BREAK; } return err; } #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE static int get_baud(volatile struct apbuart_regs *const regs) { unsigned int core_clk_hz; unsigned int scaler; scaler = regs->scaler; core_clk_hz = sys_clock_hw_cycles_per_sec(); /* Calculate baud rate from generator "scaler" number */ return core_clk_hz / ((scaler + 1) * 8); } static void set_baud(volatile struct apbuart_regs *const regs, uint32_t baud) { unsigned int core_clk_hz; unsigned int scaler; if (baud == 0) { return; } core_clk_hz = sys_clock_hw_cycles_per_sec(); /* Calculate Baud rate generator "scaler" number */ scaler = (core_clk_hz / (baud * 8)) - 1; /* Set new baud rate by setting scaler */ regs->scaler = scaler; } static int apbuart_configure(const struct device *dev, const struct uart_config *cfg) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; uint32_t ctrl = 0; uint32_t newctrl = 0; switch (cfg->parity) { case UART_CFG_PARITY_NONE: break; case UART_CFG_PARITY_EVEN: newctrl |= APBUART_CTRL_PE; break; case UART_CFG_PARITY_ODD: newctrl |= APBUART_CTRL_PE | APBUART_CTRL_PS; break; default: return -ENOTSUP; } if (cfg->stop_bits != UART_CFG_STOP_BITS_1) { return -ENOTSUP; } if (cfg->data_bits != UART_CFG_DATA_BITS_8) { return -ENOTSUP; } switch (cfg->flow_ctrl) { case UART_CFG_FLOW_CTRL_NONE: break; case UART_CFG_FLOW_CTRL_RTS_CTS: newctrl |= APBUART_CTRL_FL; break; default: return -ENOTSUP; } set_baud(regs, cfg->baudrate); ctrl = regs->ctrl; ctrl &= ~(APBUART_CTRL_PE | APBUART_CTRL_PS | APBUART_CTRL_FL); regs->ctrl = ctrl | newctrl; return 0; } static int apbuart_config_get(const struct device *dev, struct uart_config *cfg) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; const uint32_t ctrl = regs->ctrl; cfg->parity = UART_CFG_PARITY_NONE; if (ctrl & APBUART_CTRL_PE) { if (ctrl & APBUART_CTRL_PS) { cfg->parity = UART_CFG_PARITY_ODD; } else { cfg->parity = UART_CFG_PARITY_EVEN; } } cfg->flow_ctrl = UART_CFG_FLOW_CTRL_NONE; if (ctrl & APBUART_CTRL_FL) { cfg->flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS; } cfg->baudrate = get_baud(regs); cfg->data_bits = UART_CFG_DATA_BITS_8; cfg->stop_bits = UART_CFG_STOP_BITS_1; return 0; } #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void apbuart_isr(const struct device *dev); static int apbuart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) { const struct apbuart_dev_cfg *config = dev->config; struct apbuart_dev_data *data = dev->data; volatile struct apbuart_regs *regs = (void *) config->regs; int i; if (data->usefifo) { /* Transmitter FIFO full flag is available. */ for ( i = 0; (i < size) && !(regs->status & APBUART_STATUS_TF); i++ ) { regs->data = tx_data[i]; } return i; } for (i = 0; (i < size) && (regs->status & APBUART_STATUS_TE); i++) { regs->data = tx_data[i]; } return i; } static int apbuart_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; int i; for (i = 0; (i < size) && (regs->status & APBUART_STATUS_DR); i++) { rx_data[i] = regs->data & 0xff; } return i; } static void apbuart_irq_tx_enable(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; struct apbuart_dev_data *data = dev->data; volatile struct apbuart_regs *regs = (void *) config->regs; unsigned int key; if (data->usefifo) { /* Enable the FIFO level interrupt */ regs->ctrl |= APBUART_CTRL_TF; return; } regs->ctrl |= APBUART_CTRL_TI; /* * The "TI" interrupt is an edge interrupt. It fires each time the TX * holding register (or FIFO if implemented) moves from non-empty to * empty. * * When the APBUART is implemented _without_ FIFO, the TI interrupt is * the only TX interrupt we have. When the APBUART is implemented * _with_ FIFO, the TI will fire on each TX byte. */ regs->ctrl |= APBUART_CTRL_TI; /* Fire the first "TI" edge interrupt to get things going. */ key = irq_lock(); apbuart_isr(dev); irq_unlock(key); } static void apbuart_irq_tx_disable(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; regs->ctrl &= ~(APBUART_CTRL_TF | APBUART_CTRL_TI); } static int apbuart_irq_tx_ready(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; struct apbuart_dev_data *data = dev->data; volatile struct apbuart_regs *regs = (void *) config->regs; if (data->usefifo) { return !(regs->status & APBUART_STATUS_TF); } return !!(regs->status & APBUART_STATUS_TE); } static int apbuart_irq_tx_complete(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; return !!(regs->status & APBUART_STATUS_TS); } static void apbuart_irq_rx_enable(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; regs->ctrl |= APBUART_CTRL_RI; } static void apbuart_irq_rx_disable(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; regs->ctrl &= ~APBUART_CTRL_RI; } static int apbuart_irq_rx_ready(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; volatile struct apbuart_regs *regs = (void *) config->regs; return !!(regs->status & APBUART_STATUS_DR); } static int apbuart_irq_is_pending(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; struct apbuart_dev_data *data = dev->data; volatile struct apbuart_regs *regs = (void *) config->regs; uint32_t status = regs->status; uint32_t ctrl = regs->ctrl; if ((ctrl & APBUART_CTRL_RI) && (status & APBUART_STATUS_DR)) { return 1; } if (data->usefifo) { /* TH is the TX FIFO half-empty flag */ if (status & APBUART_STATUS_TH) { return 1; } } else { if ((ctrl & APBUART_CTRL_TI) && (status & APBUART_STATUS_TE)) { return 1; } } return 0; } static int apbuart_irq_update(const struct device *dev) { return 1; } static void apbuart_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, void *cb_data) { struct apbuart_dev_data *const dev_data = dev->data; dev_data->cb = cb; dev_data->cb_data = cb_data; } static void apbuart_isr(const struct device *dev) { struct apbuart_dev_data *const dev_data = dev->data; if (dev_data->cb) { dev_data->cb(dev, dev_data->cb_data); } } #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ static int apbuart_init(const struct device *dev) { const struct apbuart_dev_cfg *config = dev->config; struct apbuart_dev_data *data = dev->data; volatile struct apbuart_regs *regs = (void *) config->regs; const uint32_t APBUART_DEBUG_MASK = APBUART_CTRL_DB | APBUART_CTRL_FL; uint32_t dm; uint32_t ctrl; ctrl = regs->ctrl; data->usefifo = !!(ctrl & APBUART_CTRL_FA); /* NOTE: CTRL_FL has reset value 0. CTRL_DB has no reset value. */ dm = ctrl & APBUART_DEBUG_MASK; if (dm == APBUART_DEBUG_MASK) { /* Debug mode enabled so assume APBUART already initialized. */ ; } else { regs->ctrl = APBUART_CTRL_TE | APBUART_CTRL_RE; } regs->status = 0; #ifdef CONFIG_UART_INTERRUPT_DRIVEN irq_connect_dynamic(config->interrupt, 0, (void (*)(const void *))apbuart_isr, dev, 0); irq_enable(config->interrupt); #endif return 0; } /* Driver API defined in uart.h */ static const struct uart_driver_api apbuart_driver_api = { .poll_in = apbuart_poll_in, .poll_out = apbuart_poll_out, .err_check = apbuart_err_check, #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE .configure = apbuart_configure, .config_get = apbuart_config_get, #endif #ifdef CONFIG_UART_INTERRUPT_DRIVEN .fifo_fill = apbuart_fifo_fill, .fifo_read = apbuart_fifo_read, .irq_tx_enable = apbuart_irq_tx_enable, .irq_tx_disable = apbuart_irq_tx_disable, .irq_tx_ready = apbuart_irq_tx_ready, .irq_rx_enable = apbuart_irq_rx_enable, .irq_rx_disable = apbuart_irq_rx_disable, .irq_tx_complete = apbuart_irq_tx_complete, .irq_rx_ready = apbuart_irq_rx_ready, .irq_is_pending = apbuart_irq_is_pending, .irq_update = apbuart_irq_update, .irq_callback_set = apbuart_irq_callback_set, #endif }; #define APBUART_INIT(index) \ static const struct apbuart_dev_cfg apbuart##index##_config = { \ .regs = (struct apbuart_regs *) \ DT_INST_REG_ADDR(index), \ IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \ (.interrupt = DT_INST_IRQN(index),)) \ }; \ \ static struct apbuart_dev_data apbuart##index##_data = { \ .usefifo = 0, \ }; \ \ DEVICE_DT_INST_DEFINE(index, \ &apbuart_init, \ NULL, \ &apbuart##index##_data, \ &apbuart##index##_config, \ PRE_KERNEL_1, \ CONFIG_SERIAL_INIT_PRIORITY, \ &apbuart_driver_api); DT_INST_FOREACH_STATUS_OKAY(APBUART_INIT)