diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-22 15:05:47 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 15:18:42 +0200 |
commit | f004ace8280bd973b6b986402a805f684263aefb (patch) | |
tree | 52973fe737bd95f93605e60d17448b749ae1749e /bsps/arm/altera-cyclone-v/contrib/hwlib/src/hwmgr/alt_16550_uart.c | |
parent | bsps: Move make/custom/* files to bsps (diff) | |
download | rtems-f004ace8280bd973b6b986402a805f684263aefb.tar.bz2 |
bsp/altera-cyclone-v: Move hwlib to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/arm/altera-cyclone-v/contrib/hwlib/src/hwmgr/alt_16550_uart.c')
-rw-r--r-- | bsps/arm/altera-cyclone-v/contrib/hwlib/src/hwmgr/alt_16550_uart.c | 1179 |
1 files changed, 1179 insertions, 0 deletions
diff --git a/bsps/arm/altera-cyclone-v/contrib/hwlib/src/hwmgr/alt_16550_uart.c b/bsps/arm/altera-cyclone-v/contrib/hwlib/src/hwmgr/alt_16550_uart.c new file mode 100644 index 0000000000..7ed75c39d3 --- /dev/null +++ b/bsps/arm/altera-cyclone-v/contrib/hwlib/src/hwmgr/alt_16550_uart.c @@ -0,0 +1,1179 @@ +/****************************************************************************** + * + * Copyright 2013 Altera Corporation. All Rights Reserved. + * + * 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. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "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 AUTHOR 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 <bsp/alt_16550_uart.h> +#include <bsp/alt_clock_manager.h> +#include <bsp/socal/alt_rstmgr.h> +#include <bsp/socal/alt_uart.h> +#include <bsp/socal/hps.h> +#include <bsp/socal/socal.h> + +///// + +#define ALT_16550_HANDLE_DATA_UART_ENABLED_MSK (1UL << 31) +#define ALT_16550_HANDLE_DATA_DIVISOR_VALUE_GET(value) (value & 0xffff) + +#define ALT_ALTERA_16550_CPR_OFST (0xF4) +#define ALT_ALTERA_16550_CPR_ADDR(base) ALT_CAST(void *, (ALT_CAST(char *, (base)) + ALT_ALTERA_16550_CPR_OFST)) +#define ALT_ALTERA_16550_CPR_FIFO_MODE_GET(value) (((value) >> 16) & 0xff) +#define ALT_ALTERA_16550_CPR_AFCE_MODE_SET_MSK (1 << 4) + +///// + +// Remove these macros as part of case:123835. +#define ALT_UART_IER_DLH_VALUE_SET(value) ((value) & 0xff) +#define ALT_UART_IER_DLH_ETBEI_DLH1_SET_MSK ALT_UART_IER_DLH_ETBEI_DLHL_SET_MSK + +///// + +// +// Helper function which resets the UART and if requested, initializes the UART +// to the default settings. Currently the default settings are: +// - 8 databits +// - no parity +// - 1 stopbit +// - 57600 baudrate +// The reset routines depends on the hardware implementation of the UART. +// + +// This helper is needed because the regular alt_read_word(src) essentially +// resolves to "*(volatile uint32_t *)src". As there is no assignment, this +// could potentially be optimized away. With the helper, the actual register +// read should occur and be returned (and subsequently discarded). +static inline uint32_t alt_read_word_helper(const void * addr) +{ + return alt_read_word(addr); +} + +// +// Helper function write the divisor in hardware. +// +static ALT_STATUS_CODE alt_16550_write_divisor_helper(ALT_16550_HANDLE_t * handle, + uint32_t divisor) +{ + // Validate the divisor parameter. + if (divisor > 0xffff) + { + // This should never happen as it is verified in divisor_set. + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Set LCR::DLAB (Line Control Register :: Divisor Latch Access Bit) + alt_setbits_word(ALT_UART_LCR_ADDR(handle->location), ALT_UART_LCR_DLAB_SET_MSK); + + // Write DLL (Divisor Latch Low). + alt_write_word(ALT_UART_RBR_THR_DLL_ADDR(handle->location), ALT_UART_RBR_THR_DLL_VALUE_SET(divisor)); + + // Write DLH (Divisor Latch High). + alt_write_word(ALT_UART_IER_DLH_ADDR(handle->location), ALT_UART_IER_DLH_VALUE_SET(divisor >> 8)); + + // Clear LCR::DLAB (Line Control Register :: Divisor Latch Access Bit) + alt_clrbits_word(ALT_UART_LCR_ADDR(handle->location), ALT_UART_LCR_DLAB_SET_MSK); + + break; + + default: + return ALT_E_ERROR; + } + + // Update the enabled state in the handle data. + if (divisor != 0) + { + handle->data |= ALT_16550_HANDLE_DATA_UART_ENABLED_MSK; + } + else + { + handle->data &= ~ALT_16550_HANDLE_DATA_UART_ENABLED_MSK; + } + + return ALT_E_SUCCESS; +} + +// +// Helper function to reset the UART. +// +static ALT_STATUS_CODE alt_16550_reset_helper(ALT_16550_HANDLE_t * handle, bool enable_init) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + // Write SRR::UR (Shadow Reset Register :: UART Reset) + alt_write_word(ALT_UART_SRR_ADDR(handle->location), ALT_UART_SRR_UR_SET_MSK); + + // Read the MSR to work around case:119085. + alt_read_word_helper(ALT_UART_MSR_ADDR(handle->location)); + break; + + case ALT_16550_DEVICE_ALTERA_16550_UART: + alt_16550_write_divisor_helper(handle, 0); // Disable UART + alt_16550_int_disable_all(handle); // Disable interrupts + alt_16550_fifo_disable(handle); // Disable FIFOs + alt_write_word(ALT_UART_MCR_ADDR(handle->location), 0); // 0 -> MCR (AFCE, LP, OUT2, OUT1, RTS, DTR) + break; + + default: + return ALT_E_ERROR; + } + + // If we are initializing (as opposed to just uninitializing) + if (enable_init) + { + ALT_STATUS_CODE status; + + // Set bit IER::PTIME (Interrupt Enable Register :: Programmable THRE Mode Enable) + alt_setbits_word(ALT_UART_IER_DLH_ADDR(handle->location), ALT_UART_IER_DLH_PTIME_DLH7_SET_MSK); + + // Set the line configuration to use 8-N-1. + status = alt_16550_line_config_set(handle, ALT_16550_DATABITS_8, + ALT_16550_PARITY_DISABLE, + ALT_16550_STOPBITS_1); + if (status != ALT_E_SUCCESS) + { + return status; + } + + uint32_t divisor = ALT_16550_HANDLE_DATA_DIVISOR_VALUE_GET(handle->data); + if (divisor == 0) + { + // Set the default baudrate to 57600. + status = alt_16550_baudrate_set(handle, ALT_16550_BAUDRATE_57600); + if (status != ALT_E_SUCCESS) + { + return status; + } + } + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_init(ALT_16550_DEVICE_t device, + void * location, + alt_freq_t clock_freq, + ALT_16550_HANDLE_t * handle) +{ + handle->device = device; + handle->data = 0; + handle->fcr = 0; + + switch (device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + // The ALT_CLK_L4_SP is required for all SoCFPGA UARTs. Check that it's enabled. + if (alt_clk_is_enabled(ALT_CLK_L4_SP) != ALT_E_TRUE) + { + return ALT_E_BAD_CLK; + } + else + { + ALT_STATUS_CODE status; + status = alt_clk_freq_get(ALT_CLK_L4_SP, &handle->clock_freq); + if (status != ALT_E_SUCCESS) + { + return status; + } + + if (device == ALT_16550_DEVICE_SOCFPGA_UART0) + { + handle->location = ALT_UART0_ADDR; + + // Bring UART0 out of reset. + alt_clrbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_UART0_SET_MSK); + } + else // device == ALT_16550_DEVICE_SOCFPGA_UART1 + { + handle->location = ALT_UART1_ADDR; + + // Bring UART1 out of reset. + alt_clrbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_UART1_SET_MSK); + } + + // Verify the UCR (UART Component Version) + uint32_t ucr = alt_read_word(ALT_UART_UCV_ADDR(handle->location)); + if (ucr != ALT_UART_UCV_UART_COMPONENT_VER_RESET) + { + return ALT_E_ERROR; + } + } + break; + case ALT_16550_DEVICE_ALTERA_16550_UART: + handle->location = location; + handle->clock_freq = clock_freq; + break; + default: + return ALT_E_BAD_ARG; + } + + return alt_16550_reset_helper(handle, true); +} + +ALT_STATUS_CODE alt_16550_uninit(ALT_16550_HANDLE_t * handle) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + alt_setbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_UART0_SET_MSK); + return ALT_E_SUCCESS; + case ALT_16550_DEVICE_SOCFPGA_UART1: + alt_setbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_UART1_SET_MSK); + return ALT_E_SUCCESS; + case ALT_16550_DEVICE_ALTERA_16550_UART: + default: + return alt_16550_reset_helper(handle, false); + } +} + +ALT_STATUS_CODE alt_16550_reset(ALT_16550_HANDLE_t * handle) +{ + return alt_16550_reset_helper(handle, true); +} + +ALT_STATUS_CODE alt_16550_enable(ALT_16550_HANDLE_t * handle) +{ + // Write the divisor cached in the handle data to the divisor registers. + // This will effectively enable the UART. + return alt_16550_write_divisor_helper(handle, + ALT_16550_HANDLE_DATA_DIVISOR_VALUE_GET(handle->data)); +} + +ALT_STATUS_CODE alt_16550_disable(ALT_16550_HANDLE_t * handle) +{ + // Write 0 to the divisor the divisor registers. This will effectively + // disable the UART. + return alt_16550_write_divisor_helper(handle, 0); +} + +ALT_STATUS_CODE alt_16550_read(ALT_16550_HANDLE_t * handle, + char * item) +{ + // Verify that the UART is enabled + if (!(handle->data & ALT_16550_HANDLE_DATA_UART_ENABLED_MSK)) + { + return ALT_E_ERROR; + } + + // Verify that the FIFO is disabled + if (handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK) + { + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Read the RBR (Receive Buffer Register) into *item. + *item = ALT_UART_RBR_THR_DLL_VALUE_GET(alt_read_word(ALT_UART_RBR_THR_DLL_ADDR(handle->location))); + break; + default: + return ALT_E_ERROR; + } + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_write(ALT_16550_HANDLE_t * handle, + char item) +{ + // Verify that the UART is enabled + if (!(handle->data & ALT_16550_HANDLE_DATA_UART_ENABLED_MSK)) + { + return ALT_E_ERROR; + } + + // Verify that the FIFO is disabled + if (handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK) + { + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Write the buffer into the THR (Transmit Holding Register) + alt_write_word(ALT_UART_RBR_THR_DLL_ADDR(handle->location), item); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +///// + +ALT_STATUS_CODE alt_16550_fifo_enable(ALT_16550_HANDLE_t * handle) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Set FCR::FIFOE (FIFO Control Register :: FIFO Enable) bit. + handle->fcr |= ALT_UART_FCR_FIFOE_SET_MSK; + alt_write_word(ALT_UART_FCR_ADDR(handle->location), handle->fcr); + break; + default: + return ALT_E_ERROR; + } + + // No need to reset / clear the FIFOs. This is done automatically when + // FCR::FIFOE is changed. + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_disable(ALT_16550_HANDLE_t * handle) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Clear FCR::FIFOE (FIFO Control Register :: FIFO Enable) bit. + handle->fcr &= ~ALT_UART_FCR_FIFOE_SET_MSK; + alt_write_word(ALT_UART_FCR_ADDR(handle->location), handle->fcr); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_read(ALT_16550_HANDLE_t * handle, + char * buffer, + size_t count) +{ + // Verify that the UART is enabled + if (!(handle->data & ALT_16550_HANDLE_DATA_UART_ENABLED_MSK)) + { + return ALT_E_ERROR; + } + + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Read the RBR (Receive Buffer Register) into the buffer + for (size_t i = 0; i < count; ++i) + { + buffer[i] = ALT_UART_RBR_THR_DLL_VALUE_GET(alt_read_word(ALT_UART_RBR_THR_DLL_ADDR(handle->location))); + } + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_write(ALT_16550_HANDLE_t * handle, + const char * buffer, + size_t count) +{ + // Verify that the UART is enabled + if (!(handle->data & ALT_16550_HANDLE_DATA_UART_ENABLED_MSK)) + { + return ALT_E_ERROR; + } + + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Write the buffer into the THR (Transmit Holding Register) + for (size_t i = 0; i < count; ++i) + { + alt_write_word(ALT_UART_RBR_THR_DLL_ADDR(handle->location), buffer[i]); + } + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_clear_rx(ALT_16550_HANDLE_t * handle) +{ + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + // Write SRR::RFR (Shadow Reset Register :: Receiver FIFO Reset) bit. + alt_write_word(ALT_UART_SRR_ADDR(handle->location), ALT_UART_SRR_RFR_SET_MSK); + break; + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Write FCR::RFIFOR (FIFO Control Register :: Receiver FIFO Reset) bit. + alt_write_word(ALT_UART_FCR_ADDR(handle->location), handle->fcr | ALT_UART_FCR_RFIFOR_SET_MSK); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_clear_tx(ALT_16550_HANDLE_t * handle) +{ + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + // Write SRR::XFR (Shadow Reset Register :: Xmitter FIFO Reset) bit. + alt_write_word(ALT_UART_SRR_ADDR(handle->location), ALT_UART_SRR_XFR_SET_MSK); + break; + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Write FCR::XFIFOR (FIFO Control Register :: Xmitter FIFO Reset) bit. + alt_write_word(ALT_UART_FCR_ADDR(handle->location), handle->fcr | ALT_UART_FCR_XFIFOR_SET_MSK); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_clear_all(ALT_16550_HANDLE_t * handle) +{ + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + // Write SRR::(RFR | XFR) + // (Shadow Reset Register :: (Receiver FIFO Reset | Xmitter FIFO Reset)) bits. + alt_write_word(ALT_UART_SRR_ADDR(handle->location), + ALT_UART_SRR_RFR_SET_MSK | ALT_UART_SRR_XFR_SET_MSK); + break; + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Write FCR::(RFIFOR |XFIFOR) + // (FIFO Control Register :: (Receiver FIFO Reset | Xmitter FIFO Reset)) bits. + alt_write_word(ALT_UART_FCR_ADDR(handle->location), + handle->fcr | ALT_UART_FCR_RFIFOR_SET_MSK | ALT_UART_FCR_XFIFOR_SET_MSK); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_size_get_rx(ALT_16550_HANDLE_t * handle, + uint32_t * size) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + // Read the CPR::FIFO_Mod (Component Parameter Register :: FIFO Mode). + // The FIFO size is 16x this value. + *size = ALT_UART_CPR_FIFO_MOD_GET(alt_read_word(ALT_UART_CPR_ADDR(handle->location))) << 4; + break; + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Altera 16550 Compatible Soft UARTs have a configurable size and is + // stored in the CPR::FIFO_Mode (Component Parameter Register :: FIFO Depth). + *size = ALT_ALTERA_16550_CPR_FIFO_MODE_GET(alt_read_word(ALT_ALTERA_16550_CPR_ADDR(handle->location))) << 4; + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_size_get_tx(ALT_16550_HANDLE_t * handle, + uint32_t * size) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + // Read the CPR::FIFO_Mod (Component Parameter Register :: FIFO Mode). + // The FIFO size is 16x this value. + *size = ALT_UART_CPR_FIFO_MOD_GET(alt_read_word(ALT_UART_CPR_ADDR(handle->location))) << 4; + break; + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Altera 16550 Compatible Soft UARTs have a configurable size and is + // stored in the CPR::FIFO_Mode (Component Parameter Register :: FIFO Depth). + // The FIFO size is 16x this value. + *size = ALT_ALTERA_16550_CPR_FIFO_MODE_GET(alt_read_word(ALT_ALTERA_16550_CPR_ADDR(handle->location))) << 4; + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_level_get_rx(ALT_16550_HANDLE_t * handle, + uint32_t * level) +{ + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + // Read RFL (Receive FIFO Level). + *level = alt_read_word(ALT_UART_RFL_ADDR(handle->location)); + break; + case ALT_16550_DEVICE_ALTERA_16550_UART: + // RFL not implemented. Return 0. + *level = 0; + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_level_get_tx(ALT_16550_HANDLE_t * handle, + uint32_t * level) +{ + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + // Read TFL (Transmit FIFO Level). + *level = alt_read_word(ALT_UART_TFL_ADDR(handle->location)); + break; + case ALT_16550_DEVICE_ALTERA_16550_UART: + // TFL not implemented. Return 0. + *level = 0; + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_trigger_set_rx(ALT_16550_HANDLE_t * handle, + ALT_16550_FIFO_TRIGGER_RX_t trigger) +{ + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + // Verify triggering parameter + switch (trigger) + { + case ALT_16550_FIFO_TRIGGER_RX_ANY: + case ALT_16550_FIFO_TRIGGER_RX_QUARTER_FULL: + case ALT_16550_FIFO_TRIGGER_RX_HALF_FULL: + case ALT_16550_FIFO_TRIGGER_RX_ALMOST_FULL: + break; + default: + return ALT_E_BAD_ARG; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Update FCR::RT (FIFO Control Register :: Receiver Trigger) + handle->fcr &= ~ALT_UART_FCR_RT_SET_MSK; + handle->fcr |= ALT_UART_FCR_RT_SET(trigger); + alt_write_word(ALT_UART_FCR_ADDR(handle->location), handle->fcr); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_fifo_trigger_set_tx(ALT_16550_HANDLE_t * handle, + ALT_16550_FIFO_TRIGGER_TX_t trigger) +{ + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + // Verify triggering parameter + switch (trigger) + { + case ALT_16550_FIFO_TRIGGER_TX_EMPTY: + case ALT_16550_FIFO_TRIGGER_TX_ALMOST_EMPTY: + case ALT_16550_FIFO_TRIGGER_TX_QUARTER_FULL: + case ALT_16550_FIFO_TRIGGER_TX_HALF_FULL: + break; + default: + return ALT_E_BAD_ARG; + } + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Update FCR::TET (FIFO Control Register :: Transmit Empty Trigger) + handle->fcr &= ~ALT_UART_FCR_TET_SET_MSK; + handle->fcr |= ALT_UART_FCR_TET_SET(trigger); + alt_write_word(ALT_UART_FCR_ADDR(handle->location), handle->fcr); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +///// + +ALT_STATUS_CODE alt_16550_baudrate_get(ALT_16550_HANDLE_t * handle, + uint32_t * baudrate) +{ + // Query the divisor cached in the handle data + uint32_t divisor = ALT_16550_HANDLE_DATA_DIVISOR_VALUE_GET(handle->data); + + // The divisor should never be zero. It is set to allow for a baud of 57600 + // on initialization and a valid value is checked at + // alt_16550_divisor_set(). We do not check for users altering the data in + // the handle structure. + + // Formula for calculating the baudrate: + // baudrate = clock / (16 * divisor) + + *baudrate = (handle->clock_freq >> 4) / divisor; + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_baudrate_set(ALT_16550_HANDLE_t * handle, + uint32_t baudrate) +{ + if (baudrate == 0) + { + return ALT_E_ARG_RANGE; + } + + // Formula for calculating the divisor: + // baudrate = clock / (16 * divisor) + // => baudrate * 16 * divisor = clock + // => divisor = clock / (baudrate * 16) + // => divisor = (clock / 16) / baudrate + + // Add half of the denominator to address rounding errors. + uint32_t divisor = ((handle->clock_freq + (8 * baudrate)) / (16 * baudrate)); + + // Check for divisor range is in alt_16550_divisor_set(). + return alt_16550_divisor_set(handle, divisor); +} + +ALT_STATUS_CODE alt_16550_divisor_get(ALT_16550_HANDLE_t * handle, + uint32_t * divisor) +{ + // Just read the divisor portion of the handle data. + *divisor = ALT_16550_HANDLE_DATA_DIVISOR_VALUE_GET(handle->data); + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_divisor_set(ALT_16550_HANDLE_t * handle, + uint32_t divisor) +{ + // Verify divisor value is in range. + if ((divisor > 0xffff) || (divisor == 0)) + { + return ALT_E_ARG_RANGE; + } + + // Set the divisor portion of the handle data. + handle->data &= ~(0xffff); + handle->data |= divisor; + + // Even if the UART is enabled, don't do anything. It is documented that + // the change will take effect when the UART move to the enabled state. + + return ALT_E_SUCCESS; +} + +///// + +static ALT_STATUS_CODE alt_16550_ier_mask_set_helper(ALT_16550_HANDLE_t * handle, uint32_t setmask) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Set bit in IER (Interrupt Enable Register) + alt_setbits_word(ALT_UART_IER_DLH_ADDR(handle->location), setmask); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +static ALT_STATUS_CODE alt_16550_ier_mask_clr_helper(ALT_16550_HANDLE_t * handle, uint32_t setmask) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Clear bit in IER (Interrupt Enable Register) + alt_clrbits_word(ALT_UART_IER_DLH_ADDR(handle->location), setmask); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_int_enable_rx(ALT_16550_HANDLE_t * handle) +{ + // Set the IER::ERBFI (Interrupt Enable Register :: Enable Receive Buffer Full Interrupt) bit. + return alt_16550_ier_mask_set_helper(handle, ALT_UART_IER_DLH_ERBFI_DLH0_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_int_disable_rx(ALT_16550_HANDLE_t * handle) +{ + // Clear the IER::ERBFI (Interrupt Enable Register :: Enable Receive Buffer Full Interrupt) bit. + return alt_16550_ier_mask_clr_helper(handle, ALT_UART_IER_DLH_ERBFI_DLH0_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_int_enable_tx(ALT_16550_HANDLE_t * handle) +{ + // Set the IER::ETBEI (Interrupt Enable Register :: Enable Transmit Buffer Empty Interrupt) bit. + return alt_16550_ier_mask_set_helper(handle, ALT_UART_IER_DLH_ETBEI_DLH1_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_int_disable_tx(ALT_16550_HANDLE_t * handle) +{ + // Clear the IER::ETBEI (Interrupt Enable Register :: Enable Transmit Buffer Empty Interrupt) bit. + return alt_16550_ier_mask_clr_helper(handle, ALT_UART_IER_DLH_ETBEI_DLH1_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_int_enable_line(ALT_16550_HANDLE_t * handle) +{ + // Set the IER::ELSI (Interrupt Enable Register :: Enable Line Status Interrupt) bit. + return alt_16550_ier_mask_set_helper(handle, ALT_UART_IER_DLH_ELSI_DHL2_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_int_disable_line(ALT_16550_HANDLE_t * handle) +{ + // Clear the IER::ELSI (Interrupt Enable Register :: Enable Line Status Interrupt) bit. + return alt_16550_ier_mask_clr_helper(handle, ALT_UART_IER_DLH_ELSI_DHL2_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_int_enable_modem(ALT_16550_HANDLE_t * handle) +{ + // Set the IER::EDSSI (Interrupt Enable Register :: Enable Modem Status Interrupt) bit. + return alt_16550_ier_mask_set_helper(handle, ALT_UART_IER_DLH_EDSSI_DHL3_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_int_disable_modem(ALT_16550_HANDLE_t * handle) +{ + // Clear the IER::EDSSI (Interrupt Enable Register :: Enable Modem Status Interrupt) bit. + return alt_16550_ier_mask_clr_helper(handle, ALT_UART_IER_DLH_EDSSI_DHL3_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_int_disable_all(ALT_16550_HANDLE_t * handle) +{ + // Clear the IER::(ERBFI | ETBEI | ELSI | EDSSI) + // (Interrupt Enable Register :: (Enable Receive Buffer Full Interrupt | + // Enable Transmit Buffer Empty Interrupt | + // Enable Line Status Interrupt | + // Enable Modem Status Interrupt)) bits + return alt_16550_ier_mask_clr_helper(handle, ALT_UART_IER_DLH_ERBFI_DLH0_SET_MSK | + ALT_UART_IER_DLH_ETBEI_DLH1_SET_MSK | + ALT_UART_IER_DLH_ELSI_DHL2_SET_MSK | + ALT_UART_IER_DLH_EDSSI_DHL3_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_int_status_get(ALT_16550_HANDLE_t * handle, + ALT_16550_INT_STATUS_t * status) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Read IIR::IID (Interrupt Identity Register :: Interrupt ID) + *status = (ALT_16550_INT_STATUS_t) ALT_UART_IIR_ID_GET(alt_read_word(ALT_UART_IIR_ADDR(handle->location))); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +///// + +ALT_STATUS_CODE alt_16550_line_config_set(ALT_16550_HANDLE_t * handle, + ALT_16550_DATABITS_t databits, + ALT_16550_PARITY_t parity, + ALT_16550_STOPBITS_t stopbits) +{ + // Validate the databits parameter. + switch (databits) + { + case ALT_16550_DATABITS_5: + case ALT_16550_DATABITS_6: + case ALT_16550_DATABITS_7: + case ALT_16550_DATABITS_8: + break; + default: + return ALT_E_ERROR; + } + + // Validate the parity parameter. + switch (parity) + { + case ALT_16550_PARITY_DISABLE: + case ALT_16550_PARITY_ODD: + case ALT_16550_PARITY_EVEN: + break; + default: + return ALT_E_ERROR; + } + + // Validate the stopbits parameter. + switch (stopbits) + { + case ALT_16550_STOPBITS_1: + case ALT_16550_STOPBITS_2: + break; + default: + return ALT_E_ERROR; + } + + // LCR (Line Control Register) cache. + uint32_t lcr = 0; + + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + + // Configure the number of databits + lcr |= ALT_UART_LCR_DLS_SET(databits); + + // Configure the number of stopbits + lcr |= ALT_UART_LCR_STOP_SET(stopbits); + + // Configure the parity + if (parity != ALT_16550_PARITY_DISABLE) + { + // Enable parity in LCR + lcr |= ALT_UART_LCR_PEN_SET_MSK; + + if (parity == ALT_16550_PARITY_EVEN) + { + // Enable even parity in LCR; otherwise it's odd parity. + lcr |= ALT_UART_LCR_EPS_SET_MSK; + } + } + + // Update LCR (Line Control Register) + alt_replbits_word(ALT_UART_LCR_ADDR(handle->location), + ALT_UART_LCR_DLS_SET_MSK + | ALT_UART_LCR_STOP_SET_MSK + | ALT_UART_LCR_PEN_SET_MSK + | ALT_UART_LCR_EPS_SET_MSK, + lcr); + + break; + + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_line_break_enable(ALT_16550_HANDLE_t * handle) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Set the LCR::Break (Line Control Register :: Break) bit. + alt_setbits_word(ALT_UART_LCR_ADDR(handle->location), ALT_UART_LCR_BREAK_SET_MSK); + break; + + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_line_break_disable(ALT_16550_HANDLE_t * handle) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Clear the LCR::Break (Line Control Register :: Break) bit. + alt_clrbits_word(ALT_UART_LCR_ADDR(handle->location), ALT_UART_LCR_BREAK_SET_MSK); + break; + + default: + return ALT_E_ERROR; + } + + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_line_status_get(ALT_16550_HANDLE_t * handle, + uint32_t * status) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Read the LSR (Line Status Register). + *status = alt_read_word(ALT_UART_LSR_ADDR(handle->location)); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +///// + +static ALT_STATUS_CODE alt_16550_mcr_mask_set_helper(ALT_16550_HANDLE_t * handle, + uint32_t setmask) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Set the bit in MCR (Modem Control Register). + alt_setbits_word(ALT_UART_MCR_ADDR(handle->location), setmask); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +static ALT_STATUS_CODE alt_16550_mcr_mask_clr_helper(ALT_16550_HANDLE_t * handle, uint32_t setmask) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Clear the bit in MCR (Modem Control Register). + alt_clrbits_word(ALT_UART_MCR_ADDR(handle->location), setmask); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} + +ALT_STATUS_CODE alt_16550_flowcontrol_enable(ALT_16550_HANDLE_t * handle) +{ + // Verify that the FIFO is enabled + if (!(handle->fcr & ALT_UART_FCR_FIFOE_SET_MSK)) + { + return ALT_E_ERROR; + } + + // For the Altera 16550 Compatible Soft UART, check that Hardware Flowcontrol is enabled. + if (handle->device == ALT_16550_DEVICE_ALTERA_16550_UART) + { + // Read the CPR::AFCE_Mode (Component Parameter Register :: Auto Flow Control mode) bit. + uint32_t cpr = alt_read_word(ALT_ALTERA_16550_CPR_ADDR(handle->location)); + if (!(ALT_ALTERA_16550_CPR_AFCE_MODE_SET_MSK & cpr)) + { + return ALT_E_ERROR; + } + } + + // Set MCR::AFCE (Modem Control Register :: Automatic FlowControl Enable) bit. + return alt_16550_mcr_mask_set_helper(handle, ALT_UART_MCR_AFCE_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_flowcontrol_disable(ALT_16550_HANDLE_t * handle) +{ + // Clear MCR::AFCE (Modem Control Register :: Automatic FlowControl Enable) bit. + return alt_16550_mcr_mask_clr_helper(handle, ALT_UART_MCR_AFCE_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_loopback_enable(ALT_16550_HANDLE_t * handle) +{ + // Loopback is not implemented in the Altera 16550 Compatible Soft UART. + if (handle->device == ALT_16550_DEVICE_ALTERA_16550_UART) + { + return ALT_E_ERROR; + } + + // Set MCR::Loopback (Modem Control Register :: Loopback) bit. + return alt_16550_mcr_mask_set_helper(handle, ALT_UART_MCR_LOOPBACK_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_loopback_disable(ALT_16550_HANDLE_t * handle) +{ + // Clear MCR::Loopback (Modem Control Register :: Loopback) bit. + return alt_16550_mcr_mask_clr_helper(handle, ALT_UART_MCR_LOOPBACK_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_modem_enable_out1(ALT_16550_HANDLE_t * handle) +{ + // Set MCR::Out1 (Modem Control Register :: Out1) bit. + return alt_16550_mcr_mask_set_helper(handle, ALT_UART_MCR_OUT1_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_modem_disable_out1(ALT_16550_HANDLE_t * handle) +{ + // Clear MCR::Out1 (Modem Control Register :: Out1) bit. + return alt_16550_mcr_mask_clr_helper(handle, ALT_UART_MCR_OUT1_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_modem_enable_out2(ALT_16550_HANDLE_t * handle) +{ + // Set MCR::Out2 (Modem Control Register :: Out2) bit. + return alt_16550_mcr_mask_set_helper(handle, ALT_UART_MCR_OUT2_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_modem_disable_out2(ALT_16550_HANDLE_t * handle) +{ + // Clear MCR::Out2 (Modem Control Register :: Out2) bit. + return alt_16550_mcr_mask_clr_helper(handle, ALT_UART_MCR_OUT2_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_modem_enable_rts(ALT_16550_HANDLE_t * handle) +{ + // Set MCR::RTS (Modem Control Register :: Request To Send) bit. + return alt_16550_mcr_mask_set_helper(handle, ALT_UART_MCR_RTS_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_modem_disable_rts(ALT_16550_HANDLE_t * handle) +{ + // Clear MCR::RTS (Modem Control Register :: Request To Send) bit. + return alt_16550_mcr_mask_clr_helper(handle, ALT_UART_MCR_RTS_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_modem_enable_dtr(ALT_16550_HANDLE_t * handle) +{ + // Set MCR::DTR (Modem Control Register :: Data Terminal Ready) bit. + return alt_16550_mcr_mask_set_helper(handle, ALT_UART_MCR_DTR_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_modem_disable_dtr(ALT_16550_HANDLE_t * handle) +{ + // Clear MCR::DTR (Modem Control Register :: Data Terminal Ready) bit. + return alt_16550_mcr_mask_clr_helper(handle, ALT_UART_MCR_DTR_SET_MSK); +} + +ALT_STATUS_CODE alt_16550_modem_status_get(ALT_16550_HANDLE_t * handle, + uint32_t * status) +{ + switch (handle->device) + { + case ALT_16550_DEVICE_SOCFPGA_UART0: + case ALT_16550_DEVICE_SOCFPGA_UART1: + case ALT_16550_DEVICE_ALTERA_16550_UART: + // Read the MSR (Modem Status Register). + *status = alt_read_word(ALT_UART_MSR_ADDR(handle->location)); + break; + default: + return ALT_E_ERROR; + } + + return ALT_E_SUCCESS; +} |