You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1223 lines
39 KiB
1223 lines
39 KiB
/* uart_xlnx_ps.c - Xilinx Zynq family serial driver */ |
|
|
|
/* |
|
* Copyright (c) 2018 Xilinx, Inc. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT xlnx_xuartps |
|
|
|
/** |
|
* @brief Xilinx Zynq Family Serial Driver |
|
* |
|
* This is the driver for the Xilinx Zynq family cadence serial device. |
|
* |
|
* Before individual UART port can be used, uart_xlnx_ps_init() has to be |
|
* called to setup the port. |
|
* |
|
* - the following macro for the number of bytes between register addresses: |
|
* |
|
* UART_REG_ADDR_INTERVAL |
|
*/ |
|
|
|
#include <errno.h> |
|
#include <zephyr/kernel.h> |
|
#include <zephyr/arch/cpu.h> |
|
#include <zephyr/types.h> |
|
|
|
#include <zephyr/init.h> |
|
#include <zephyr/toolchain.h> |
|
#include <zephyr/linker/sections.h> |
|
#include <zephyr/drivers/uart.h> |
|
#include <zephyr/sys/sys_io.h> |
|
#include <zephyr/irq.h> |
|
|
|
#ifdef CONFIG_PINCTRL |
|
#include <zephyr/drivers/pinctrl.h> |
|
#endif |
|
|
|
/* For all register offsets and bits / bit masks: |
|
* Comp. Xilinx Zynq-7000 Technical Reference Manual (ug585), chap. B.33 |
|
*/ |
|
|
|
/* Register offsets within the UART device's register space */ |
|
#define XUARTPS_CR_OFFSET 0x0000U /**< Control Register [8:0] */ |
|
#define XUARTPS_MR_OFFSET 0x0004U /**< Mode Register [9:0] */ |
|
#define XUARTPS_IER_OFFSET 0x0008U /**< Interrupt Enable [12:0] */ |
|
#define XUARTPS_IDR_OFFSET 0x000CU /**< Interrupt Disable [12:0] */ |
|
#define XUARTPS_IMR_OFFSET 0x0010U /**< Interrupt Mask [12:0] */ |
|
#define XUARTPS_ISR_OFFSET 0x0014U /**< Interrupt Status [12:0]*/ |
|
#define XUARTPS_BAUDGEN_OFFSET 0x0018U /**< Baud Rate Generator [15:0] */ |
|
#define XUARTPS_RXTOUT_OFFSET 0x001CU /**< RX Timeout [7:0] */ |
|
#define XUARTPS_RXWM_OFFSET 0x0020U /**< RX FIFO Trigger Level [5:0] */ |
|
#define XUARTPS_MODEMCR_OFFSET 0x0024U /**< Modem Control [5:0] */ |
|
#define XUARTPS_MODEMSR_OFFSET 0x0028U /**< Modem Status [8:0] */ |
|
#define XUARTPS_SR_OFFSET 0x002CU /**< Channel Status [14:0] */ |
|
#define XUARTPS_FIFO_OFFSET 0x0030U /**< FIFO [7:0] */ |
|
#define XUARTPS_BAUDDIV_OFFSET 0x0034U /**< Baud Rate Divider [7:0] */ |
|
#define XUARTPS_FLOWDEL_OFFSET 0x0038U /**< Flow Delay [5:0] */ |
|
#define XUARTPS_TXWM_OFFSET 0x0044U /**< TX FIFO Trigger Level [5:0] */ |
|
#define XUARTPS_RXBS_OFFSET 0x0048U /**< RX FIFO Byte Status [11:0] */ |
|
|
|
/* Control Register Bits Definition */ |
|
#define XUARTPS_CR_STOPBRK 0x00000100U /**< Stop transmission of break */ |
|
#define XUARTPS_CR_STARTBRK 0x00000080U /**< Set break */ |
|
#define XUARTPS_CR_TORST 0x00000040U /**< RX timeout counter restart */ |
|
#define XUARTPS_CR_TX_DIS 0x00000020U /**< TX disabled. */ |
|
#define XUARTPS_CR_TX_EN 0x00000010U /**< TX enabled */ |
|
#define XUARTPS_CR_RX_DIS 0x00000008U /**< RX disabled. */ |
|
#define XUARTPS_CR_RX_EN 0x00000004U /**< RX enabled */ |
|
#define XUARTPS_CR_EN_DIS_MASK 0x0000003CU /**< Enable/disable Mask */ |
|
#define XUARTPS_CR_TXRST 0x00000002U /**< TX logic reset */ |
|
#define XUARTPS_CR_RXRST 0x00000001U /**< RX logic reset */ |
|
|
|
/* Mode Register Bits Definition */ |
|
#define XUARTPS_MR_CCLK 0x00000400U /**< Input clock select */ |
|
#define XUARTPS_MR_CHMODE_R_LOOP 0x00000300U /**< Remote loopback mode */ |
|
#define XUARTPS_MR_CHMODE_L_LOOP 0x00000200U /**< Local loopback mode */ |
|
#define XUARTPS_MR_CHMODE_ECHO 0x00000100U /**< Auto echo mode */ |
|
#define XUARTPS_MR_CHMODE_NORM 0x00000000U /**< Normal mode */ |
|
#define XUARTPS_MR_CHMODE_SHIFT 8U /**< Mode shift */ |
|
#define XUARTPS_MR_CHMODE_MASK 0x00000300U /**< Mode mask */ |
|
#define XUARTPS_MR_STOPMODE_2_BIT 0x00000080U /**< 2 stop bits */ |
|
#define XUARTPS_MR_STOPMODE_1_5_BIT 0x00000040U /**< 1.5 stop bits */ |
|
#define XUARTPS_MR_STOPMODE_1_BIT 0x00000000U /**< 1 stop bit */ |
|
#define XUARTPS_MR_STOPMODE_SHIFT 6U /**< Stop bits shift */ |
|
#define XUARTPS_MR_STOPMODE_MASK 0x000000A0U /**< Stop bits mask */ |
|
#define XUARTPS_MR_PARITY_NONE 0x00000020U /**< No parity mode */ |
|
#define XUARTPS_MR_PARITY_MARK 0x00000018U /**< Mark parity mode */ |
|
#define XUARTPS_MR_PARITY_SPACE 0x00000010U /**< Space parity mode */ |
|
#define XUARTPS_MR_PARITY_ODD 0x00000008U /**< Odd parity mode */ |
|
#define XUARTPS_MR_PARITY_EVEN 0x00000000U /**< Even parity mode */ |
|
#define XUARTPS_MR_PARITY_SHIFT 3U /**< Parity setting shift */ |
|
#define XUARTPS_MR_PARITY_MASK 0x00000038U /**< Parity mask */ |
|
#define XUARTPS_MR_CHARLEN_6_BIT 0x00000006U /**< 6 bits data */ |
|
#define XUARTPS_MR_CHARLEN_7_BIT 0x00000004U /**< 7 bits data */ |
|
#define XUARTPS_MR_CHARLEN_8_BIT 0x00000000U /**< 8 bits data */ |
|
#define XUARTPS_MR_CHARLEN_SHIFT 1U /**< Data Length shift */ |
|
#define XUARTPS_MR_CHARLEN_MASK 0x00000006U /**< Data length mask */ |
|
#define XUARTPS_MR_CLKSEL 0x00000001U /**< Input clock select */ |
|
|
|
/* Interrupt Register Bits Definition */ |
|
#define XUARTPS_IXR_RBRK 0x00002000U /**< Rx FIFO break detect interrupt */ |
|
#define XUARTPS_IXR_TOVR 0x00001000U /**< Tx FIFO Overflow interrupt */ |
|
#define XUARTPS_IXR_TNFUL 0x00000800U /**< Tx FIFO Nearly Full interrupt */ |
|
#define XUARTPS_IXR_TTRIG 0x00000400U /**< Tx Trig interrupt */ |
|
#define XUARTPS_IXR_DMS 0x00000200U /**< Modem status change interrupt */ |
|
#define XUARTPS_IXR_TOUT 0x00000100U /**< Timeout error interrupt */ |
|
#define XUARTPS_IXR_PARITY 0x00000080U /**< Parity error interrupt */ |
|
#define XUARTPS_IXR_FRAMING 0x00000040U /**< Framing error interrupt */ |
|
#define XUARTPS_IXR_RXOVR 0x00000020U /**< Overrun error interrupt */ |
|
#define XUARTPS_IXR_TXFULL 0x00000010U /**< TX FIFO full interrupt. */ |
|
#define XUARTPS_IXR_TXEMPTY 0x00000008U /**< TX FIFO empty interrupt. */ |
|
#define XUARTPS_IXR_RXFULL 0x00000004U /**< RX FIFO full interrupt. */ |
|
#define XUARTPS_IXR_RXEMPTY 0x00000002U /**< RX FIFO empty interrupt. */ |
|
#define XUARTPS_IXR_RTRIG 0x00000001U /**< RX FIFO trigger interrupt. */ |
|
#define XUARTPS_IXR_MASK 0x00003FFFU /**< Valid bit mask */ |
|
|
|
/* Modem Control Register Bits Definition */ |
|
#define XUARTPS_MODEMCR_FCM_RTS_CTS 0x00000020 /**< RTS/CTS hardware flow control. */ |
|
#define XUARTPS_MODEMCR_FCM_NONE 0x00000000 /**< No hardware flow control. */ |
|
#define XUARTPS_MODEMCR_FCM_MASK 0x00000020 /**< Hardware flow control mask. */ |
|
#define XUARTPS_MODEMCR_RTS_SHIFT 1U /**< RTS bit shift */ |
|
#define XUARTPS_MODEMCR_DTR_SHIFT 0U /**< DTR bit shift */ |
|
|
|
/* Channel Status Register */ |
|
#define XUARTPS_SR_TNFUL 0x00004000U /**< TX FIFO Nearly Full Status */ |
|
#define XUARTPS_SR_TTRIG 0x00002000U /**< TX FIFO Trigger Status */ |
|
#define XUARTPS_SR_FLOWDEL 0x00001000U /**< RX FIFO fill over flow delay */ |
|
#define XUARTPS_SR_TACTIVE 0x00000800U /**< TX active */ |
|
#define XUARTPS_SR_RACTIVE 0x00000400U /**< RX active */ |
|
#define XUARTPS_SR_TXFULL 0x00000010U /**< TX FIFO full */ |
|
#define XUARTPS_SR_TXEMPTY 0x00000008U /**< TX FIFO empty */ |
|
#define XUARTPS_SR_RXFULL 0x00000004U /**< RX FIFO full */ |
|
#define XUARTPS_SR_RXEMPTY 0x00000002U /**< RX FIFO empty */ |
|
#define XUARTPS_SR_RTRIG 0x00000001U /**< RX FIFO fill over trigger */ |
|
|
|
/** Device configuration structure */ |
|
struct uart_xlnx_ps_dev_config { |
|
DEVICE_MMIO_ROM; |
|
uint32_t sys_clk_freq; |
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
|
uart_irq_config_func_t irq_config_func; |
|
#endif |
|
#ifdef CONFIG_PINCTRL |
|
const struct pinctrl_dev_config *pincfg; |
|
#endif |
|
uint32_t baud_rate; |
|
}; |
|
|
|
/** Device data structure */ |
|
struct uart_xlnx_ps_dev_data_t { |
|
DEVICE_MMIO_RAM; |
|
uint32_t parity; |
|
uint32_t stopbits; |
|
uint32_t databits; |
|
uint32_t flowctrl; |
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
|
const struct device *dev; |
|
struct k_timer timer; |
|
uart_irq_callback_user_data_t user_cb; |
|
void *user_data; |
|
#endif |
|
}; |
|
|
|
/** |
|
* @brief Disables the UART's RX and TX function. |
|
* |
|
* Writes 'Disable RX' and 'Disable TX' command bits into the respective |
|
* UART's Command Register, thus disabling the operation of the UART. |
|
* |
|
* While writing the disable command bits, the opposing enable command |
|
* bits, which are set when enabling the UART, are cleared. |
|
* |
|
* This function must be called before any configuration parameters |
|
* of the UART are modified at run-time. |
|
* |
|
* @param reg_base Base address of the respective UART's register space. |
|
*/ |
|
static void xlnx_ps_disable_uart(uintptr_t reg_base) |
|
{ |
|
uint32_t reg_val = sys_read32(reg_base + XUARTPS_CR_OFFSET); |
|
|
|
reg_val &= (~XUARTPS_CR_EN_DIS_MASK); |
|
/* Set control register bits [5]: TX_DIS and [3]: RX_DIS */ |
|
reg_val |= XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS; |
|
sys_write32(reg_val, reg_base + XUARTPS_CR_OFFSET); |
|
} |
|
|
|
/** |
|
* @brief Enables the UART's RX and TX function. |
|
* |
|
* Writes 'Enable RX' and 'Enable TX' command bits into the respective |
|
* UART's Command Register, thus enabling the operation of the UART. |
|
* |
|
* While writing the enable command bits, the opposing disable command |
|
* bits, which are set when disabling the UART, are cleared. |
|
* |
|
* This function must not be called while any configuration parameters |
|
* of the UART are being modified at run-time. |
|
* |
|
* @param reg_base Base address of the respective UART's register space. |
|
*/ |
|
static void xlnx_ps_enable_uart(uintptr_t reg_base) |
|
{ |
|
uint32_t reg_val = sys_read32(reg_base + XUARTPS_CR_OFFSET); |
|
|
|
reg_val &= (~XUARTPS_CR_EN_DIS_MASK); |
|
/* Set control register bits [4]: TX_EN and [2]: RX_EN */ |
|
reg_val |= XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN; |
|
sys_write32(reg_val, reg_base + XUARTPS_CR_OFFSET); |
|
} |
|
|
|
/** |
|
* @brief Calculates and sets the values of the BAUDDIV and BAUDGEN registers. |
|
* |
|
* Calculates and sets the values of the BAUDDIV and BAUDGEN registers, which |
|
* determine the prescaler applied to the clock driving the UART, based on |
|
* the target baud rate, which is provided as a decimal value. |
|
* |
|
* The calculation of the values to be written to the BAUDDIV and BAUDGEN |
|
* registers is described in the Zynq-7000 TRM, chapter 19.2.3 'Baud Rate |
|
* Generator'. |
|
* |
|
* @param dev UART device struct |
|
* @param baud_rate The desired baud rate as a decimal value |
|
*/ |
|
static void set_baudrate(const struct device *dev, uint32_t baud_rate) |
|
{ |
|
const struct uart_xlnx_ps_dev_config *dev_cfg = dev->config; |
|
uint32_t baud = dev_cfg->baud_rate; |
|
uint32_t clk_freq = dev_cfg->sys_clk_freq; |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
uint32_t divisor, generator; |
|
|
|
/* Calculate divisor and baud rate generator value */ |
|
if ((baud != 0) && (clk_freq != 0)) { |
|
/* Covering case where input clock is so slow */ |
|
if (clk_freq < 1000000U && baud > 4800U) { |
|
baud = 4800; |
|
} |
|
|
|
for (divisor = 4; divisor < 255; divisor++) { |
|
uint32_t tmpbaud, bauderr; |
|
|
|
generator = clk_freq / (baud * (divisor + 1)); |
|
if (generator < 2 || generator > 65535) { |
|
continue; |
|
} |
|
tmpbaud = clk_freq / (generator * (divisor + 1)); |
|
|
|
if (baud > tmpbaud) { |
|
bauderr = baud - tmpbaud; |
|
} else { |
|
bauderr = tmpbaud - baud; |
|
} |
|
if (((bauderr * 1000) / baud) < 3) { |
|
break; |
|
} |
|
} |
|
|
|
/* |
|
* Set baud rate divisor and generator. |
|
* -> This function is always called from a context in which |
|
* the receiver/transmitter is disabled, the baud rate can |
|
* be changed safely at this time. |
|
*/ |
|
sys_write32(divisor, reg_base + XUARTPS_BAUDDIV_OFFSET); |
|
sys_write32(generator, reg_base + XUARTPS_BAUDGEN_OFFSET); |
|
} |
|
} |
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
|
|
|
/** |
|
* @brief Trigger a UART callback based on a timer. |
|
* |
|
* @param timer Timer that triggered the callback |
|
*/ |
|
static void uart_xlnx_ps_soft_isr(struct k_timer *timer) |
|
{ |
|
struct uart_xlnx_ps_dev_data_t *data = |
|
CONTAINER_OF(timer, struct uart_xlnx_ps_dev_data_t, timer); |
|
|
|
if (data->user_cb) { |
|
data->user_cb(data->dev, data->user_data); |
|
} |
|
} |
|
|
|
#endif |
|
|
|
/** |
|
* @brief Initialize individual UART port |
|
* |
|
* This routine is called to reset the chip in a quiescent state. |
|
* |
|
* @param dev UART device struct |
|
* |
|
* @return 0 if successful, failed otherwise |
|
*/ |
|
static int uart_xlnx_ps_init(const struct device *dev) |
|
{ |
|
const struct uart_xlnx_ps_dev_config *dev_cfg = dev->config; |
|
uint32_t reg_val; |
|
#ifdef CONFIG_PINCTRL |
|
int err; |
|
#endif |
|
|
|
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
#ifdef CONFIG_PINCTRL |
|
err = pinctrl_apply_state(dev_cfg->pincfg, PINCTRL_STATE_DEFAULT); |
|
if (err < 0) { |
|
return err; |
|
} |
|
#endif |
|
|
|
/* Set RX/TX reset and disable RX/TX */ |
|
sys_write32(XUARTPS_CR_STOPBRK | XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS | XUARTPS_CR_TXRST | |
|
XUARTPS_CR_RXRST, |
|
reg_base + XUARTPS_CR_OFFSET); |
|
|
|
/* Set initial character length / start/stop bit / parity configuration */ |
|
reg_val = sys_read32(reg_base + XUARTPS_MR_OFFSET); |
|
reg_val &= (~(XUARTPS_MR_CHARLEN_MASK | XUARTPS_MR_STOPMODE_MASK | XUARTPS_MR_PARITY_MASK)); |
|
reg_val |= XUARTPS_MR_CHARLEN_8_BIT | XUARTPS_MR_STOPMODE_1_BIT | XUARTPS_MR_PARITY_NONE; |
|
sys_write32(reg_val, reg_base + XUARTPS_MR_OFFSET); |
|
|
|
/* Set RX FIFO trigger at 1 data bytes. */ |
|
sys_write32(0x01U, reg_base + XUARTPS_RXWM_OFFSET); |
|
|
|
/* Disable all interrupts, polling mode is default */ |
|
sys_write32(XUARTPS_IXR_MASK, reg_base + XUARTPS_IDR_OFFSET); |
|
|
|
/* Set the baud rate */ |
|
set_baudrate(dev, dev_cfg->baud_rate); |
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
|
struct uart_xlnx_ps_dev_data_t *dev_data = dev->data; |
|
|
|
/* Clear any pending interrupt flags */ |
|
sys_write32(XUARTPS_IXR_MASK, reg_base + XUARTPS_ISR_OFFSET); |
|
|
|
dev_data->dev = dev; |
|
k_timer_init(&dev_data->timer, &uart_xlnx_ps_soft_isr, NULL); |
|
|
|
/* Attach to & unmask the corresponding interrupt vector */ |
|
dev_cfg->irq_config_func(dev); |
|
|
|
#endif |
|
|
|
xlnx_ps_enable_uart(reg_base); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* @brief Poll the device for input. |
|
* |
|
* @param dev UART device struct |
|
* @param c Pointer to character |
|
* |
|
* @return 0 if a character arrived, -1 if the input buffer if empty. |
|
*/ |
|
static int uart_xlnx_ps_poll_in(const struct device *dev, unsigned char *c) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
uint32_t reg_val = sys_read32(reg_base + XUARTPS_SR_OFFSET); |
|
|
|
if ((reg_val & XUARTPS_SR_RXEMPTY) == 0) { |
|
*c = (unsigned char)sys_read32(reg_base + XUARTPS_FIFO_OFFSET); |
|
return 0; |
|
} else { |
|
return -1; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Output a character in polled mode. |
|
* |
|
* Checks if the transmitter has available TX FIFO space. If so, a character is written to |
|
* the data register. |
|
* |
|
* If the hardware flow control is enabled then the handshake signal CTS has to |
|
* be asserted in order to send a character. |
|
* |
|
* @param dev UART device struct |
|
* @param c Character to send |
|
* |
|
* @return Sent character |
|
*/ |
|
static void uart_xlnx_ps_poll_out(const struct device *dev, unsigned char c) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
/* wait for transmitter to be ready to accept a character */ |
|
while ((sys_read32(reg_base + XUARTPS_SR_OFFSET) & XUARTPS_SR_TXFULL) != 0) { |
|
} |
|
|
|
sys_write32((uint32_t)(c & 0xFF), reg_base + XUARTPS_FIFO_OFFSET); |
|
} |
|
|
|
/** |
|
* Check for pending TX/TX errors on the port. |
|
* |
|
* @param dev UART device struct |
|
* @return 0 if no errors, UART_ERROR_* flags otherwise |
|
*/ |
|
static int uart_xlnx_ps_err_check(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
uint32_t reg_val = sys_read32(reg_base + XUARTPS_ISR_OFFSET); |
|
int err = 0; |
|
|
|
if (reg_val & XUARTPS_IXR_RBRK) { |
|
err |= UART_BREAK; |
|
} |
|
|
|
if (reg_val & XUARTPS_IXR_TOVR) { |
|
err |= UART_ERROR_OVERRUN; |
|
} |
|
|
|
if (reg_val & XUARTPS_IXR_TOUT) { |
|
err |= UART_ERROR_NOISE; |
|
} |
|
|
|
if (reg_val & XUARTPS_IXR_PARITY) { |
|
err |= UART_ERROR_PARITY; |
|
} |
|
|
|
if (reg_val & XUARTPS_IXR_FRAMING) { |
|
err |= UART_ERROR_FRAMING; |
|
} |
|
|
|
if (reg_val & XUARTPS_IXR_RXOVR) { |
|
err |= UART_ERROR_OVERRUN; |
|
} |
|
sys_write32(reg_val & (XUARTPS_IXR_RBRK | XUARTPS_IXR_TOVR | XUARTPS_IXR_TOUT | |
|
XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING | XUARTPS_IXR_RXOVR), |
|
reg_base + XUARTPS_ISR_OFFSET); |
|
|
|
return err; |
|
} |
|
|
|
/** |
|
* @brief Converts a parity enum value to a Mode Register bit mask. |
|
* |
|
* Converts a value of an enumeration type provided by the driver |
|
* framework for the configuration of the UART's parity setting |
|
* into a bit mask within the Mode Register. |
|
* |
|
* It is assumed that the Mode Register contents that are being |
|
* modified within this function come with the bits modified by |
|
* this function already masked out by the caller. |
|
* |
|
* @param mode_reg Pointer to the Mode Register contents to which |
|
* the parity configuration shall be added. |
|
* @param parity Enumeration value to be converted to a bit mask. |
|
* |
|
* @return Indication of success, always true for this function |
|
* as all parity modes supported by the API are also supported |
|
* by the hardware. |
|
*/ |
|
static inline bool uart_xlnx_ps_cfg2ll_parity(uint32_t *mode_reg, enum uart_config_parity parity) |
|
{ |
|
/* |
|
* Translate the new parity configuration to the mode register's |
|
* bits [5..3] (PAR): |
|
* 000b : even |
|
* 001b : odd |
|
* 010b : space |
|
* 011b : mark |
|
* 1xxb : none |
|
*/ |
|
|
|
switch (parity) { |
|
default: |
|
case UART_CFG_PARITY_EVEN: |
|
*mode_reg |= XUARTPS_MR_PARITY_EVEN; |
|
break; |
|
case UART_CFG_PARITY_ODD: |
|
*mode_reg |= XUARTPS_MR_PARITY_ODD; |
|
break; |
|
case UART_CFG_PARITY_SPACE: |
|
*mode_reg |= XUARTPS_MR_PARITY_SPACE; |
|
break; |
|
case UART_CFG_PARITY_MARK: |
|
*mode_reg |= XUARTPS_MR_PARITY_MARK; |
|
break; |
|
case UART_CFG_PARITY_NONE: |
|
*mode_reg |= XUARTPS_MR_PARITY_NONE; |
|
break; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/** |
|
* @brief Converts a stop bit enum value to a Mode Register bit mask. |
|
* |
|
* Converts a value of an enumeration type provided by the driver |
|
* framework for the configuration of the UART's stop bit setting |
|
* into a bit mask within the Mode Register. |
|
* |
|
* It is assumed that the Mode Register contents that are being |
|
* modified within this function come with the bits modified by |
|
* this function already masked out by the caller. |
|
* |
|
* @param mode_reg Pointer to the Mode Register contents to which |
|
* the stop bit configuration shall be added. |
|
* @param stopbits Enumeration value to be converted to a bit mask. |
|
* |
|
* @return Indication of success or failure in case of an unsupported |
|
* stop bit configuration being provided by the caller. |
|
*/ |
|
static inline bool uart_xlnx_ps_cfg2ll_stopbits(uint32_t *mode_reg, |
|
enum uart_config_stop_bits stopbits) |
|
{ |
|
/* |
|
* Translate the new stop bit configuration to the mode register's |
|
* bits [7..6] (NBSTOP): |
|
* 00b : 1 stop bit |
|
* 01b : 1.5 stop bits |
|
* 10b : 2 stop bits |
|
* 11b : reserved |
|
*/ |
|
|
|
switch (stopbits) { |
|
case UART_CFG_STOP_BITS_0_5: |
|
/* Controller doesn't support 0.5 stop bits */ |
|
return false; |
|
default: |
|
case UART_CFG_STOP_BITS_1: |
|
*mode_reg |= XUARTPS_MR_STOPMODE_1_BIT; |
|
break; |
|
case UART_CFG_STOP_BITS_1_5: |
|
*mode_reg |= XUARTPS_MR_STOPMODE_1_5_BIT; |
|
break; |
|
case UART_CFG_STOP_BITS_2: |
|
*mode_reg |= XUARTPS_MR_STOPMODE_2_BIT; |
|
break; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/** |
|
* @brief Converts a data bit enum value to a Mode Register bit mask. |
|
* |
|
* Converts a value of an enumeration type provided by the driver |
|
* framework for the configuration of the UART's data bit setting |
|
* into a bit mask within the Mode Register. |
|
* |
|
* It is assumed that the Mode Register contents that are being |
|
* modified within this function come with the bits modified by |
|
* this function already masked out by the caller. |
|
* |
|
* @param mode_reg Pointer to the Mode Register contents to which |
|
* the data bit configuration shall be added. |
|
* @param databits Enumeration value to be converted to a bit mask. |
|
* |
|
* @return Indication of success or failure in case of an unsupported |
|
* data bit configuration being provided by the caller. |
|
*/ |
|
static inline bool uart_xlnx_ps_cfg2ll_databits(uint32_t *mode_reg, |
|
enum uart_config_data_bits databits) |
|
{ |
|
/* |
|
* Translate the new data bit configuration to the mode register's |
|
* bits [2..1] (CHRL): |
|
* 0xb : 8 data bits |
|
* 10b : 7 data bits |
|
* 11b : 6 data bits |
|
*/ |
|
|
|
switch (databits) { |
|
case UART_CFG_DATA_BITS_5: |
|
case UART_CFG_DATA_BITS_9: |
|
/* Controller doesn't support 5 or 9 data bits */ |
|
return false; |
|
default: |
|
case UART_CFG_DATA_BITS_8: |
|
*mode_reg |= XUARTPS_MR_CHARLEN_8_BIT; |
|
break; |
|
case UART_CFG_DATA_BITS_7: |
|
*mode_reg |= XUARTPS_MR_CHARLEN_7_BIT; |
|
break; |
|
case UART_CFG_DATA_BITS_6: |
|
*mode_reg |= XUARTPS_MR_CHARLEN_6_BIT; |
|
break; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/** |
|
* @brief Converts a flow control enum value to a Modem Control |
|
* Register bit mask. |
|
* |
|
* Converts a value of an enumeration type provided by the driver |
|
* framework for the configuration of the UART's flow control |
|
* setting into a bit mask within the Modem Control Register. |
|
* |
|
* It is assumed that the Modem Control Register contents that are |
|
* being modified within this function come with the bits modified |
|
* by this function already masked out by the caller. |
|
* |
|
* @param modemcr_reg Pointer to the Modem Control Register contents |
|
* to which the flow control configuration shall |
|
* be added. |
|
* @param hwctrl Enumeration value to be converted to a bit mask. |
|
* |
|
* @return Indication of success or failure in case of an unsupported |
|
* flow control configuration being provided by the caller. |
|
*/ |
|
static inline bool uart_xlnx_ps_cfg2ll_hwctrl(uint32_t *modemcr_reg, |
|
enum uart_config_flow_control hwctrl) |
|
{ |
|
/* |
|
* Translate the new flow control configuration to the modem |
|
* control register's bit [5] (FCM): |
|
* 0b : no flow control |
|
* 1b : RTS/CTS |
|
*/ |
|
|
|
if (hwctrl == UART_CFG_FLOW_CTRL_RTS_CTS) { |
|
*modemcr_reg |= XUARTPS_MODEMCR_FCM_RTS_CTS; |
|
} else if (hwctrl == UART_CFG_FLOW_CTRL_NONE) { |
|
*modemcr_reg |= XUARTPS_MODEMCR_FCM_NONE; |
|
} else { |
|
/* Only no flow control or RTS/CTS is supported. */ |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE |
|
/** |
|
* @brief Configures the UART device at run-time. |
|
* |
|
* Configures the UART device at run-time according to the |
|
* configuration data provided by the caller. |
|
* |
|
* @param dev UART device struct |
|
* @param cfg The configuration parameters to be applied. |
|
* |
|
* @return 0 if the configuration completed successfully, ENOTSUP |
|
* error if an unsupported configuration parameter is detected. |
|
*/ |
|
static int uart_xlnx_ps_configure(const struct device *dev, const struct uart_config *cfg) |
|
{ |
|
struct uart_xlnx_ps_dev_config *dev_cfg = (struct uart_xlnx_ps_dev_config *)dev->config; |
|
|
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
uint32_t mode_reg = 0; |
|
uint32_t modemcr_reg = 0; |
|
|
|
/* Read the current mode register & modem control register values */ |
|
mode_reg = sys_read32(reg_base + XUARTPS_MR_OFFSET); |
|
modemcr_reg = sys_read32(reg_base + XUARTPS_MODEMCR_OFFSET); |
|
|
|
/* Mask out all items that might be re-configured */ |
|
mode_reg &= (~XUARTPS_MR_PARITY_MASK); |
|
mode_reg &= (~XUARTPS_MR_STOPMODE_MASK); |
|
mode_reg &= (~XUARTPS_MR_CHARLEN_MASK); |
|
modemcr_reg &= (~XUARTPS_MODEMCR_FCM_MASK); |
|
|
|
/* Assemble the updated registers, validity checks contained within */ |
|
if ((!uart_xlnx_ps_cfg2ll_parity(&mode_reg, cfg->parity)) || |
|
(!uart_xlnx_ps_cfg2ll_stopbits(&mode_reg, cfg->stop_bits)) || |
|
(!uart_xlnx_ps_cfg2ll_databits(&mode_reg, cfg->data_bits)) || |
|
(!uart_xlnx_ps_cfg2ll_hwctrl(&modemcr_reg, cfg->flow_ctrl))) { |
|
return -ENOTSUP; |
|
} |
|
|
|
/* Disable the controller before modifying any config registers */ |
|
xlnx_ps_disable_uart(reg_base); |
|
|
|
/* Set the baud rate */ |
|
set_baudrate(dev, cfg->baudrate); |
|
dev_cfg->baud_rate = cfg->baudrate; |
|
|
|
/* Write the two control registers */ |
|
sys_write32(mode_reg, reg_base + XUARTPS_MR_OFFSET); |
|
sys_write32(modemcr_reg, reg_base + XUARTPS_MODEMCR_OFFSET); |
|
|
|
/* Re-enable the controller */ |
|
xlnx_ps_enable_uart(reg_base); |
|
|
|
return 0; |
|
}; |
|
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ |
|
|
|
/** |
|
* @brief Converts a Mode Register bit mask to a parity configuration |
|
* enum value. |
|
* |
|
* Converts a bit mask representing the UART's parity setting within |
|
* the UART's Mode Register into a value of an enumeration type provided |
|
* by the UART driver API. |
|
* |
|
* @param mode_reg The current Mode Register contents from which the |
|
* parity setting shall be extracted. |
|
* |
|
* @return The current parity setting mapped to the UART driver API's |
|
* enum type. |
|
*/ |
|
static inline enum uart_config_parity uart_xlnx_ps_ll2cfg_parity(uint32_t mode_reg) |
|
{ |
|
/* |
|
* Obtain the current parity configuration from the mode register's |
|
* bits [5..3] (PAR): |
|
* 000b : even -> reset value |
|
* 001b : odd |
|
* 010b : space |
|
* 011b : mark |
|
* 1xxb : none |
|
*/ |
|
|
|
switch ((mode_reg & XUARTPS_MR_PARITY_MASK)) { |
|
case XUARTPS_MR_PARITY_EVEN: |
|
default: |
|
return UART_CFG_PARITY_EVEN; |
|
case XUARTPS_MR_PARITY_ODD: |
|
return UART_CFG_PARITY_ODD; |
|
case XUARTPS_MR_PARITY_SPACE: |
|
return UART_CFG_PARITY_SPACE; |
|
case XUARTPS_MR_PARITY_MARK: |
|
return UART_CFG_PARITY_MARK; |
|
case XUARTPS_MR_PARITY_NONE: |
|
return UART_CFG_PARITY_NONE; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Converts a Mode Register bit mask to a stop bit configuration |
|
* enum value. |
|
* |
|
* Converts a bit mask representing the UART's stop bit setting within |
|
* the UART's Mode Register into a value of an enumeration type provided |
|
* by the UART driver API. |
|
* |
|
* @param mode_reg The current Mode Register contents from which the |
|
* stop bit setting shall be extracted. |
|
* |
|
* @return The current stop bit setting mapped to the UART driver API's |
|
* enum type. |
|
*/ |
|
static inline enum uart_config_stop_bits uart_xlnx_ps_ll2cfg_stopbits(uint32_t mode_reg) |
|
{ |
|
/* |
|
* Obtain the current stop bit configuration from the mode register's |
|
* bits [7..6] (NBSTOP): |
|
* 00b : 1 stop bit -> reset value |
|
* 01b : 1.5 stop bits |
|
* 10b : 2 stop bits |
|
* 11b : reserved |
|
*/ |
|
|
|
switch ((mode_reg & XUARTPS_MR_STOPMODE_MASK)) { |
|
case XUARTPS_MR_STOPMODE_1_BIT: |
|
default: |
|
return UART_CFG_STOP_BITS_1; |
|
case XUARTPS_MR_STOPMODE_1_5_BIT: |
|
return UART_CFG_STOP_BITS_1_5; |
|
case XUARTPS_MR_STOPMODE_2_BIT: |
|
return UART_CFG_STOP_BITS_2; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Converts a Mode Register bit mask to a data bit configuration |
|
* enum value. |
|
* |
|
* Converts a bit mask representing the UART's data bit setting within |
|
* the UART's Mode Register into a value of an enumeration type provided |
|
* by the UART driver API. |
|
* |
|
* @param mode_reg The current Mode Register contents from which the |
|
* data bit setting shall be extracted. |
|
* |
|
* @return The current data bit setting mapped to the UART driver API's |
|
* enum type. |
|
*/ |
|
static inline enum uart_config_data_bits uart_xlnx_ps_ll2cfg_databits(uint32_t mode_reg) |
|
{ |
|
/* |
|
* Obtain the current data bit configuration from the mode register's |
|
* bits [2..1] (CHRL): |
|
* 0xb : 8 data bits -> reset value |
|
* 10b : 7 data bits |
|
* 11b : 6 data bits |
|
*/ |
|
|
|
switch ((mode_reg & XUARTPS_MR_CHARLEN_MASK)) { |
|
case XUARTPS_MR_CHARLEN_8_BIT: |
|
default: |
|
return UART_CFG_DATA_BITS_8; |
|
case XUARTPS_MR_CHARLEN_7_BIT: |
|
return UART_CFG_DATA_BITS_7; |
|
case XUARTPS_MR_CHARLEN_6_BIT: |
|
return UART_CFG_DATA_BITS_6; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Converts a Modem Control Register bit mask to a flow control |
|
* configuration enum value. |
|
* |
|
* Converts a bit mask representing the UART's flow control setting within |
|
* the UART's Modem Control Register into a value of an enumeration type |
|
* provided by the UART driver API. |
|
* |
|
* @param modemcr_reg The current Modem Control Register contents from |
|
* which the parity setting shall be extracted. |
|
* |
|
* @return The current flow control setting mapped to the UART driver API's |
|
* enum type. |
|
*/ |
|
static inline enum uart_config_flow_control uart_xlnx_ps_ll2cfg_hwctrl(uint32_t modemcr_reg) |
|
{ |
|
/* |
|
* Obtain the current flow control configuration from the modem |
|
* control register's bit [5] (FCM): |
|
* 0b : no flow control -> reset value |
|
* 1b : RTS/CTS |
|
*/ |
|
|
|
if ((modemcr_reg & XUARTPS_MODEMCR_FCM_MASK) == XUARTPS_MODEMCR_FCM_RTS_CTS) { |
|
return UART_CFG_FLOW_CTRL_RTS_CTS; |
|
} |
|
|
|
return UART_CFG_FLOW_CTRL_NONE; |
|
} |
|
|
|
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE |
|
/** |
|
* @brief Returns the current configuration of the UART at run-time. |
|
* |
|
* Returns the current configuration of the UART at run-time by obtaining |
|
* the current configuration from the UART's Mode and Modem Control Registers |
|
* (exception: baud rate). |
|
* |
|
* @param dev UART device struct |
|
* @param cfg Pointer to the data structure to which the current configuration |
|
* shall be written. |
|
* |
|
* @return always 0. |
|
*/ |
|
static int uart_xlnx_ps_config_get(const struct device *dev, struct uart_config *cfg) |
|
{ |
|
const struct uart_xlnx_ps_dev_config *dev_cfg = dev->config; |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
/* |
|
* Read the Mode & Modem control registers - they contain |
|
* the current data / stop bit and parity settings (Mode |
|
* Register) and the current flow control setting (Modem |
|
* Control register). |
|
*/ |
|
uint32_t mode_reg = sys_read32(reg_base + XUARTPS_MR_OFFSET); |
|
uint32_t modemcr_reg = sys_read32(reg_base + XUARTPS_MODEMCR_OFFSET); |
|
|
|
cfg->baudrate = dev_cfg->baud_rate; |
|
cfg->parity = uart_xlnx_ps_ll2cfg_parity(mode_reg); |
|
cfg->stop_bits = uart_xlnx_ps_ll2cfg_stopbits(mode_reg); |
|
cfg->data_bits = uart_xlnx_ps_ll2cfg_databits(mode_reg); |
|
cfg->flow_ctrl = uart_xlnx_ps_ll2cfg_hwctrl(modemcr_reg); |
|
|
|
return 0; |
|
} |
|
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ |
|
|
|
#if CONFIG_UART_INTERRUPT_DRIVEN |
|
|
|
/** |
|
* @brief Fill FIFO with data |
|
* |
|
* @param dev UART device struct |
|
* @param tx_data Data to transmit |
|
* @param size Number of bytes to send |
|
* |
|
* @return Number of bytes sent |
|
*/ |
|
static int uart_xlnx_ps_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
int count = 0; |
|
|
|
while (count < size && |
|
(sys_read32(reg_base + XUARTPS_SR_OFFSET) & XUARTPS_SR_TXFULL) == 0) { |
|
sys_write32((uint32_t)tx_data[count++], reg_base + XUARTPS_FIFO_OFFSET); |
|
} |
|
|
|
return count; |
|
} |
|
|
|
/** |
|
* @brief Read data from FIFO |
|
* |
|
* @param dev UART device struct |
|
* @param rxData Data container |
|
* @param size Container size |
|
* |
|
* @return Number of bytes read |
|
*/ |
|
static int uart_xlnx_ps_fifo_read(const struct device *dev, uint8_t *rx_data, const int size) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
int count = 0; |
|
|
|
while (count < size && |
|
(sys_read32(reg_base + XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY) == 0) { |
|
rx_data[count++] = (uint8_t)sys_read32(reg_base + XUARTPS_FIFO_OFFSET); |
|
} |
|
|
|
return count; |
|
} |
|
|
|
/** |
|
* @brief Enable TX interrupt in IER |
|
* |
|
* @param dev UART device struct |
|
*/ |
|
static void uart_xlnx_ps_irq_tx_enable(const struct device *dev) |
|
{ |
|
struct uart_xlnx_ps_dev_data_t *dev_data = dev->data; |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
sys_write32((XUARTPS_IXR_TTRIG | XUARTPS_IXR_TXEMPTY), reg_base + XUARTPS_IER_OFFSET); |
|
if ((sys_read32(reg_base + XUARTPS_SR_OFFSET) & (XUARTPS_SR_TTRIG | XUARTPS_SR_TXEMPTY)) != |
|
0) { |
|
/* |
|
* Enabling TX empty interrupts does not cause an interrupt |
|
* if the FIFO is already empty. |
|
* Generate a soft interrupt and have it call the |
|
* callback function in timer isr context. |
|
*/ |
|
k_timer_start(&dev_data->timer, K_NO_WAIT, K_NO_WAIT); |
|
} |
|
} |
|
|
|
/** |
|
* @brief Disable TX interrupt in IER |
|
* |
|
* @param dev UART device struct |
|
*/ |
|
static void uart_xlnx_ps_irq_tx_disable(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
sys_write32((XUARTPS_IXR_TTRIG | XUARTPS_IXR_TXEMPTY), reg_base + XUARTPS_IDR_OFFSET); |
|
} |
|
|
|
/** |
|
* @brief Check if Tx IRQ has been raised |
|
* |
|
* @param dev UART device struct |
|
* |
|
* @return 1 if an IRQ is ready, 0 otherwise |
|
*/ |
|
static int uart_xlnx_ps_irq_tx_ready(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
uint32_t reg_val = sys_read32(reg_base + XUARTPS_SR_OFFSET); |
|
|
|
if ((reg_val & (XUARTPS_SR_TTRIG | XUARTPS_SR_TXEMPTY)) == 0) { |
|
return 0; |
|
} else { |
|
return 1; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Check if nothing remains to be transmitted |
|
* |
|
* @param dev UART device struct |
|
* |
|
* @return 1 if nothing remains to be transmitted, 0 otherwise |
|
*/ |
|
static int uart_xlnx_ps_irq_tx_complete(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
uint32_t reg_val = sys_read32(reg_base + XUARTPS_SR_OFFSET); |
|
|
|
return (reg_val & XUARTPS_SR_TXEMPTY) != 0; |
|
} |
|
|
|
/** |
|
* @brief Enable RX interrupt in IER |
|
* |
|
* @param dev UART device struct |
|
*/ |
|
static void uart_xlnx_ps_irq_rx_enable(const struct device *dev) |
|
{ |
|
struct uart_xlnx_ps_dev_data_t *dev_data = dev->data; |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
sys_write32(XUARTPS_IXR_RTRIG, reg_base + XUARTPS_IER_OFFSET); |
|
if ((sys_read32(reg_base + XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY) == 0) { |
|
/* |
|
* Enabling RX trigger interrupts does not cause an interrupt |
|
* if the FIFO already contains RX data. |
|
* Generate a soft interrupt and have it call the |
|
* callback function in timer isr context. |
|
*/ |
|
k_timer_start(&dev_data->timer, K_NO_WAIT, K_NO_WAIT); |
|
} |
|
} |
|
|
|
/** |
|
* @brief Disable RX interrupt in IER |
|
* |
|
* @param dev UART device struct |
|
*/ |
|
static void uart_xlnx_ps_irq_rx_disable(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
sys_write32(XUARTPS_IXR_RTRIG, reg_base + XUARTPS_IDR_OFFSET); |
|
} |
|
|
|
/** |
|
* @brief Check if Rx IRQ has been raised |
|
* |
|
* @param dev UART device struct |
|
* |
|
* @return 1 if an IRQ is ready, 0 otherwise |
|
*/ |
|
static int uart_xlnx_ps_irq_rx_ready(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
uint32_t reg_val = sys_read32(reg_base + XUARTPS_SR_OFFSET); |
|
|
|
return (reg_val & XUARTPS_SR_RXEMPTY) == 0; |
|
} |
|
|
|
/** |
|
* @brief Enable error interrupt in IER |
|
* |
|
* @param dev UART device struct |
|
*/ |
|
static void uart_xlnx_ps_irq_err_enable(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
sys_write32(XUARTPS_IXR_RBRK /* [13] Receiver Break */ |
|
| XUARTPS_IXR_TOVR /* [12] Transmitter FIFO Overflow */ |
|
| XUARTPS_IXR_TOUT /* [8] Receiver Timerout */ |
|
| XUARTPS_IXR_PARITY /* [7] Parity Error */ |
|
| XUARTPS_IXR_FRAMING /* [6] Receiver Framing Error */ |
|
| XUARTPS_IXR_RXOVR, /* [5] Receiver Overflow Error */ |
|
reg_base + XUARTPS_IER_OFFSET); |
|
} |
|
|
|
/** |
|
* @brief Disable error interrupt in IER |
|
* |
|
* @param dev UART device struct |
|
* |
|
* @return 1 if an IRQ is ready, 0 otherwise |
|
*/ |
|
static void uart_xlnx_ps_irq_err_disable(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
sys_write32(XUARTPS_IXR_RBRK /* [13] Receiver Break */ |
|
| XUARTPS_IXR_TOVR /* [12] Transmitter FIFO Overflow */ |
|
| XUARTPS_IXR_TOUT /* [8] Receiver Timerout */ |
|
| XUARTPS_IXR_PARITY /* [7] Parity Error */ |
|
| XUARTPS_IXR_FRAMING /* [6] Receiver Framing Error */ |
|
| XUARTPS_IXR_RXOVR, /* [5] Receiver Overflow Error */ |
|
reg_base + XUARTPS_IDR_OFFSET); |
|
} |
|
|
|
/** |
|
* @brief Check if any IRQ is pending |
|
* |
|
* @param dev UART device struct |
|
* |
|
* @return 1 if an IRQ is pending, 0 otherwise |
|
*/ |
|
static int uart_xlnx_ps_irq_is_pending(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
uint32_t reg_imr = sys_read32(reg_base + XUARTPS_IMR_OFFSET); |
|
uint32_t reg_isr = sys_read32(reg_base + XUARTPS_ISR_OFFSET); |
|
|
|
if ((reg_imr & reg_isr) != 0) { |
|
return 1; |
|
} else { |
|
return 0; |
|
} |
|
} |
|
|
|
/** |
|
* @brief Update cached contents of IIR |
|
* |
|
* @param dev UART device struct |
|
* |
|
* @return Always 1 |
|
*/ |
|
static int uart_xlnx_ps_irq_update(const struct device *dev) |
|
{ |
|
uintptr_t reg_base = DEVICE_MMIO_GET(dev); |
|
|
|
/* Clear RX/TX interrupts */ |
|
sys_write32(XUARTPS_IXR_TTRIG | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RTRIG, |
|
reg_base + XUARTPS_ISR_OFFSET); |
|
return 1; |
|
} |
|
|
|
/** |
|
* @brief Set the callback function pointer for IRQ. |
|
* |
|
* @param dev UART device struct |
|
* @param cb Callback function pointer. |
|
*/ |
|
static void uart_xlnx_ps_irq_callback_set(const struct device *dev, |
|
uart_irq_callback_user_data_t cb, void *cb_data) |
|
{ |
|
struct uart_xlnx_ps_dev_data_t *dev_data = dev->data; |
|
|
|
dev_data->user_cb = cb; |
|
dev_data->user_data = cb_data; |
|
} |
|
|
|
/** |
|
* @brief Interrupt ce routine. |
|
* |
|
* This simply calls the callback function, if one exists. |
|
* |
|
* @param arg Argument to ISR. |
|
*/ |
|
static void uart_xlnx_ps_isr(const struct device *dev) |
|
{ |
|
const struct uart_xlnx_ps_dev_data_t *data = dev->data; |
|
|
|
if (data->user_cb) { |
|
data->user_cb(dev, data->user_data); |
|
} |
|
} |
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
|
|
|
static DEVICE_API(uart, uart_xlnx_ps_driver_api) = { |
|
.poll_in = uart_xlnx_ps_poll_in, |
|
.poll_out = uart_xlnx_ps_poll_out, |
|
.err_check = uart_xlnx_ps_err_check, |
|
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE |
|
.configure = uart_xlnx_ps_configure, |
|
.config_get = uart_xlnx_ps_config_get, |
|
#endif |
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
|
.fifo_fill = uart_xlnx_ps_fifo_fill, |
|
.fifo_read = uart_xlnx_ps_fifo_read, |
|
.irq_tx_enable = uart_xlnx_ps_irq_tx_enable, |
|
.irq_tx_disable = uart_xlnx_ps_irq_tx_disable, |
|
.irq_tx_ready = uart_xlnx_ps_irq_tx_ready, |
|
.irq_tx_complete = uart_xlnx_ps_irq_tx_complete, |
|
.irq_rx_enable = uart_xlnx_ps_irq_rx_enable, |
|
.irq_rx_disable = uart_xlnx_ps_irq_rx_disable, |
|
.irq_rx_ready = uart_xlnx_ps_irq_rx_ready, |
|
.irq_err_enable = uart_xlnx_ps_irq_err_enable, |
|
.irq_err_disable = uart_xlnx_ps_irq_err_disable, |
|
.irq_is_pending = uart_xlnx_ps_irq_is_pending, |
|
.irq_update = uart_xlnx_ps_irq_update, |
|
.irq_callback_set = uart_xlnx_ps_irq_callback_set, |
|
#endif |
|
}; |
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
|
|
|
#define UART_XLNX_PS_IRQ_CONF_FUNC_SET(port) .irq_config_func = uart_xlnx_ps_irq_config_##port, |
|
|
|
#define UART_XLNX_PS_IRQ_CONF_FUNC(port) \ |
|
static void uart_xlnx_ps_irq_config_##port(const struct device *dev) \ |
|
{ \ |
|
IRQ_CONNECT(DT_INST_IRQN(port), DT_INST_IRQ(port, priority), uart_xlnx_ps_isr, \ |
|
DEVICE_DT_INST_GET(port), 0); \ |
|
irq_enable(DT_INST_IRQN(port)); \ |
|
} |
|
|
|
#else |
|
|
|
#define UART_XLNX_PS_IRQ_CONF_FUNC_SET(port) |
|
#define UART_XLNX_PS_IRQ_CONF_FUNC(port) |
|
|
|
#endif /*CONFIG_UART_INTERRUPT_DRIVEN */ |
|
|
|
#define UART_XLNX_PS_DEV_DATA(port) \ |
|
static struct uart_xlnx_ps_dev_data_t uart_xlnx_ps_dev_data_##port |
|
|
|
#if CONFIG_PINCTRL |
|
#define UART_XLNX_PS_PINCTRL_DEFINE(port) PINCTRL_DT_INST_DEFINE(port); |
|
#define UART_XLNX_PS_PINCTRL_INIT(port) .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(port), |
|
#else |
|
#define UART_XLNX_PS_PINCTRL_DEFINE(port) |
|
#define UART_XLNX_PS_PINCTRL_INIT(port) |
|
#endif /* CONFIG_PINCTRL */ |
|
|
|
#define UART_XLNX_PS_DEV_CFG(port) \ |
|
static struct uart_xlnx_ps_dev_config uart_xlnx_ps_dev_cfg_##port = { \ |
|
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(port)), \ |
|
.sys_clk_freq = DT_INST_PROP(port, clock_frequency), \ |
|
.baud_rate = DT_INST_PROP(port, current_speed), \ |
|
UART_XLNX_PS_IRQ_CONF_FUNC_SET(port) UART_XLNX_PS_PINCTRL_INIT(port)} |
|
|
|
#define UART_XLNX_PS_INIT(port) \ |
|
DEVICE_DT_INST_DEFINE(port, uart_xlnx_ps_init, NULL, &uart_xlnx_ps_dev_data_##port, \ |
|
&uart_xlnx_ps_dev_cfg_##port, PRE_KERNEL_1, \ |
|
CONFIG_SERIAL_INIT_PRIORITY, &uart_xlnx_ps_driver_api) |
|
|
|
#define UART_XLNX_INSTANTIATE(inst) \ |
|
UART_XLNX_PS_PINCTRL_DEFINE(inst) \ |
|
UART_XLNX_PS_IRQ_CONF_FUNC(inst); \ |
|
UART_XLNX_PS_DEV_DATA(inst); \ |
|
UART_XLNX_PS_DEV_CFG(inst); \ |
|
UART_XLNX_PS_INIT(inst); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(UART_XLNX_INSTANTIATE)
|
|
|