/******************************************************************************
*
* 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 "alt_16550_uart.h"
//#include "alt_clock_manager.h"
//#include "socal/alt_rstmgr.h"
#include "alt_uart.h"
//#include "socal/hps.h"
#include "socal.h"
#define ALT_UART0_OFST 0x40000000
/* The start address of the ALT_UART0 component. */
#define ALT_UART0_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_HPS_ADDR) + ALT_UART0_OFST))
#define ALT_UART1_OFST 0x40000400
/* The start address of the ALT_UART1 component. */
#define ALT_UART1_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_HPS_ADDR) + ALT_UART1_OFST))
/////
#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)
{
uint32_t divisor;
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;
}
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,
uint32_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:
handle->location = ALT_UART0_ADDR;
handle->clock_freq = clock_freq;
#if 0
// 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;
- 1
- 2
前往页