489 lines
16 KiB
C
489 lines
16 KiB
C
/*
|
||
* Copyright : (C) 2022 Phytium Information Technology, Inc.
|
||
* All Rights Reserved.
|
||
*
|
||
* This program is OPEN SOURCE software: you can redistribute it and/or modify it
|
||
* under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd,
|
||
* either version 1.0 of the License, or (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
|
||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
* See the Phytium Public License for more details.
|
||
*
|
||
*
|
||
* FilePath: fpl011_options.c
|
||
* Date: 2021-11-02 14:53:42
|
||
* LastEditTime: 2022-02-18 09:06:45
|
||
* Description: This file is for uart option setting
|
||
*
|
||
* Modify History:
|
||
* Ver Who Date Changes
|
||
* ----- ------ -------- --------------------------------------
|
||
* 1.0 huanghe 2021/11/2 first commit
|
||
* 1.1 liushengming 2022/02/18 fix bug
|
||
*/
|
||
|
||
|
||
/***************************** Include Files *********************************/
|
||
|
||
#include "fpl011.h"
|
||
#include "fpl011_hw.h"
|
||
#include "ftypes.h"
|
||
|
||
/************************** Variable Definitions ****************************/
|
||
/************************** Constant Definitions *****************************/
|
||
/**************************** Type Definitions *******************************/
|
||
/*
|
||
* The following data type is a map from an option to the offset in the
|
||
* register to which it belongs as well as its bit mask in that register.
|
||
*/
|
||
typedef struct
|
||
{
|
||
u32 option;
|
||
u32 register_offset;
|
||
u32 mask;
|
||
} Mapping;
|
||
|
||
static Mapping option_table[] =
|
||
{
|
||
{FPL011_OPTION_UARTEN, FPL011CR_OFFSET, FPL011CR_UARTEN},
|
||
{FPL011_OPTION_RXEN, FPL011CR_OFFSET, FPL011CR_RXE},
|
||
{FPL011_OPTION_TXEN, FPL011CR_OFFSET, FPL011CR_TXE},
|
||
{FPL011_OPTION_FIFOEN, FPL011LCR_H_OFFSET, FPL011LCR_H_FEN},
|
||
{FPL011_OPTION_RTS, FPL011CR_OFFSET, FPL011CR_RTS},
|
||
{FPL011_OPTION_DTR, FPL011CR_OFFSET, FPL011CR_DTR},
|
||
{FPL011_OPTION_RTSEN, FPL011CR_OFFSET, FPL011CR_RTSEN},
|
||
{FPL011_OPTION_CTSEN, FPL011CR_OFFSET, FPL011CR_CTSEN},
|
||
{FPL011_OPTION_TXDMAEN, FPL011DMACR_OFFSET, FPL011DMACR_TXDMAE},
|
||
{FPL011_OPTION_RXDMAEN, FPL011DMACR_OFFSET, FPL011DMACR_RXDMAE}
|
||
};
|
||
|
||
/***************** Macros (Inline Functions) Definitions *********************/
|
||
|
||
#define FUART_NUM_OPITIONS (sizeof(option_table) / sizeof(Mapping))
|
||
/************************** Function Prototypes ******************************/
|
||
/*****************************************************************************/
|
||
/**
|
||
* @name: FPl011SetOptions
|
||
* @msg: Sets the options for the specified driver instance. The options are implemented as bit masks such that multiple options may be enabled or disabled simultaneously.
|
||
* @param uart_p is a pointer to the uart instance.
|
||
* @param options contains the options to be set which are bit masks
|
||
* contained in the file FPl011_uart.h and named FUART_OPTION_*.
|
||
*/
|
||
void FPl011SetOptions(FPl011 *uart_p, u32 options)
|
||
{
|
||
u32 index;
|
||
u32 reg_value;
|
||
FASSERT(uart_p != NULL);
|
||
FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY);
|
||
|
||
for (index = 0; index < FUART_NUM_OPITIONS; index++)
|
||
{
|
||
reg_value = FUART_READREG32(uart_p->config.base_address, option_table[index].register_offset);
|
||
|
||
if ((options & option_table[index].option) != (u32)(0))
|
||
{
|
||
reg_value |= option_table[index].mask;
|
||
}
|
||
else
|
||
{
|
||
reg_value &= ~option_table[index].mask;
|
||
}
|
||
|
||
FUART_WRITEREG32(uart_p->config.base_address, option_table[index].register_offset, reg_value);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @name: FPl011SetSpecificOptions
|
||
* @msg: Sets the options for the specified driver instance.
|
||
* @param {FPl011} *uart_p is a pointer to the uart instance.
|
||
* @param {u32} options contains the options to be set which are bit masks
|
||
* contained in the file FPl011_uart.h and named FUART_OPTION_*.
|
||
*/
|
||
void FPl011SetSpecificOptions(FPl011 *uart_p, u32 options)
|
||
{
|
||
u32 index;
|
||
u32 reg_value;
|
||
FASSERT(uart_p != NULL);
|
||
|
||
for (index = 0; index < FUART_NUM_OPITIONS; index++)
|
||
{
|
||
if ((options & option_table[index].option) == (u32)(0))
|
||
{
|
||
continue;
|
||
}
|
||
reg_value = FUART_READREG32(uart_p->config.base_address, option_table[index].register_offset);
|
||
|
||
/* set specific options */
|
||
reg_value |= option_table[index].mask;
|
||
FUART_WRITEREG32(uart_p->config.base_address, option_table[index].register_offset, reg_value);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @name: FPl011ClearSpecificOptions
|
||
* @msg: Clear the options for the specified driver instance.
|
||
* @param uart_p is a pointer to the uart instance.
|
||
* @param options contains the options to be set which are bit masks
|
||
* contained in the file FPl011_uart.h and named FUART_OPTION_*.
|
||
*/
|
||
void FPl011ClearSpecificOptions(FPl011 *uart_p, u32 options)
|
||
{
|
||
u32 index;
|
||
u32 reg_value;
|
||
FASSERT(uart_p != NULL);
|
||
|
||
for (index = 0; index < FUART_NUM_OPITIONS; index++)
|
||
{
|
||
if ((options & option_table[index].option) == (u32)(0))
|
||
{
|
||
continue;
|
||
}
|
||
reg_value = FUART_READREG32(uart_p->config.base_address, option_table[index].register_offset);
|
||
|
||
/* remove specific options */
|
||
reg_value &= ~option_table[index].mask;
|
||
FUART_WRITEREG32(uart_p->config.base_address, option_table[index].register_offset, reg_value);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @name: FPl011SetDataFormat
|
||
* @msg: Sets the data format for the specified UART.
|
||
* @param uart_p is a pointer to the uart instance.
|
||
* @param format_p is a pointer to a format structure that will
|
||
* contain the data format after this call completes.
|
||
* @return
|
||
* FT_SUCCESS if everything configured as expected
|
||
* FPL011_ERROR_PARAM if one of the parameters was not valid.
|
||
*/
|
||
FError FPl011SetDataFormat(FPl011 *uart_p, FPl011Format *format_p)
|
||
{
|
||
FError ret ;
|
||
u32 line_ctrl_reg ;
|
||
FPl011Config *config_p;
|
||
FASSERT(uart_p != NULL);
|
||
FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY);
|
||
FASSERT(format_p != NULL) ;
|
||
|
||
config_p = &uart_p->config;
|
||
|
||
if ((format_p->data_bits > ((u32)(FPL011_FORMAT_WORDLENGTH_8BIT))) ||
|
||
(format_p->parity > ((u32)(FPL011_FORMAT_PARITY_MASK))) ||
|
||
(format_p->stopbits > ((u32)(FPL011_FORMAT_PARITY_MASK)))
|
||
)
|
||
{
|
||
return FPL011_ERROR_PARAM ;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* Try to set the baud rate and if it's not successful then
|
||
* don't continue altering the data format, this is done
|
||
* first to avoid the format from being altered when an
|
||
* error occurs
|
||
*/
|
||
ret = FPl011SetBaudRate(uart_p, format_p->baudrate) ;
|
||
if (ret != FT_SUCCESS)
|
||
{
|
||
|
||
}
|
||
else
|
||
{
|
||
line_ctrl_reg = FUART_READREG32(config_p->base_address, FPL011LCR_H_OFFSET);
|
||
|
||
/*
|
||
* Set the length of data (8,7,6) by first clearing
|
||
* out the bits that control it in the register,
|
||
* then set the length in the register
|
||
*/
|
||
line_ctrl_reg &= ~(u32)FPL011LCR_H_WLEN ;
|
||
line_ctrl_reg |= (format_p->data_bits <<
|
||
FPL011LCR_H_WLEN_SHIFT);
|
||
|
||
/*
|
||
* Set the number of stop bits in the mode register by
|
||
* first clearing out the bits that control it in the
|
||
* register, then set the number of stop bits in the
|
||
* register.
|
||
*/
|
||
line_ctrl_reg &= ~FPL011LCR_H_STP_MASK;
|
||
line_ctrl_reg |= (format_p->stopbits <<
|
||
FPL011LCR_H_STP_SHIFT);
|
||
|
||
/*
|
||
* Set the parity by first clearing out the bits that
|
||
* control it in the register, then set the bits in
|
||
* the register, the default is no parity after
|
||
* clearing the register bits
|
||
*/
|
||
line_ctrl_reg &= ~FPL011LCR_H_PARITY_MASK;
|
||
line_ctrl_reg |= ((format_p->parity &
|
||
FPL011_FORMAT_EN_PARITY) <<
|
||
FPL011LCR_H_PARITY_SHIFT);
|
||
|
||
/* Even/Odd parity set */
|
||
line_ctrl_reg |= ((format_p->parity &
|
||
FPL011_FORMAT_EVEN_PARITY) <<
|
||
FPL011_FORMAT_EVEN_PARITY_SHIFT);
|
||
|
||
/* Stick parity enable/disable */
|
||
line_ctrl_reg |= ((format_p->parity &
|
||
FPL011_FORMAT_EN_STICK_PARITY) <<
|
||
FPL011_FORMAT_EN_STICK_PARITY_SHIFT);
|
||
|
||
/* Update the Line control register */
|
||
FUART_WRITEREG32(config_p->base_address, FPL011LCR_H_OFFSET, line_ctrl_reg) ;
|
||
|
||
return FT_SUCCESS ;
|
||
}
|
||
}
|
||
|
||
return ret ;
|
||
}
|
||
|
||
/**
|
||
* @name: FPl011GetDataFormat
|
||
* @msg: Gets the data format for the specified UART.
|
||
* @param uart_p is a pointer to the uart instance.
|
||
* @param format_p is a pointer to a format structure that will
|
||
* contain the data format after this call completes.
|
||
*/
|
||
void FPl011GetDataFormat(FPl011 *uart_p, FPl011Format *format_p)
|
||
{
|
||
u32 line_ctrl_reg ;
|
||
FPl011Config *config_p;
|
||
/* Assert validates the input arguments */
|
||
FASSERT(uart_p != NULL);
|
||
FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY);
|
||
FASSERT(format_p != NULL) ;
|
||
|
||
config_p = &uart_p->config;
|
||
/*
|
||
* Get the baud rate from the instance, this is not retrieved from
|
||
* the hardware because it is only kept as a divisor such that it
|
||
* is more difficult to get back to the baud rate
|
||
*/
|
||
format_p->baudrate = uart_p->config.baudrate ;
|
||
|
||
line_ctrl_reg = FUART_READREG32(config_p->base_address, FPL011LCR_H_OFFSET);
|
||
|
||
/* Get the length of data (8,7,6,5) */
|
||
format_p->data_bits = ((line_ctrl_reg & FPL011LCR_H_WLEN) >> FPL011LCR_H_WLEN_SHIFT) ;
|
||
|
||
/* Get the number of stop bits */
|
||
format_p->stopbits = (u8)((line_ctrl_reg & FPL011LCR_H_STP_MASK) >> FPL011LCR_H_STP_SHIFT) ;
|
||
|
||
/* Determine what parity is */
|
||
format_p->parity = (u32)((line_ctrl_reg & (u32)FPL011LCR_H_PARITY_MASK) >> FPL011LCR_H_PARITY_SHIFT) ;
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @name: FPl011SetTxFifoThreadHold
|
||
* @msg: This functions sets the Tx FIFO trigger level to the 'TriggerLevel'
|
||
* argument.
|
||
* @param uart_p is a pointer to the uart instance.
|
||
* @param trigger_level contains the trigger level to set. This is a value
|
||
* from 0-32 (FPL011IFLS_TXIFLSEL_1_8 - FPL011IFLS_TXIFLSEL_7_8)
|
||
*/
|
||
void FPl011SetTxFifoThreadHold(FPl011 *uart_p, u8 trigger_level)
|
||
{
|
||
u32 fifo_trig_reg;
|
||
FPl011Config *config_p;
|
||
FASSERT(uart_p != NULL);
|
||
FASSERT(trigger_level <= (u8)FPL011IFLS_TXIFLSEL_MASK) ;
|
||
FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY);
|
||
config_p = &uart_p->config;
|
||
|
||
trigger_level = trigger_level & (u8)FPL011IFLS_TXIFLSEL_MASK;
|
||
|
||
fifo_trig_reg = FUART_READREG32(config_p->base_address,
|
||
FPL011IFLS_OFFSET);
|
||
|
||
fifo_trig_reg &= ~(FPL011IFLS_TXIFLSEL_MASK);
|
||
|
||
fifo_trig_reg |= (u32)trigger_level;
|
||
|
||
/*
|
||
* Write the new value for the FIFO control register to it such that
|
||
* the threshold is changed
|
||
*/
|
||
FUART_WRITEREG32(config_p->base_address,
|
||
FPL011IFLS_OFFSET, fifo_trig_reg);
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @name: FPl011SetRxFifoThreadhold
|
||
* @msg: This functions sets the Rx FIFO trigger level to the 'TriggerLevel'
|
||
* argument.
|
||
* @param uart_p is a pointer to the uart instance.
|
||
* @param trigger_level contains the trigger level to set. This is a value
|
||
* from 0-32 (FPL011IFLS_RXIFLSEL_1_8 - FPL011IFLS_RXIFLSEL_7_8)
|
||
*/
|
||
void FPl011SetRxFifoThreadhold(FPl011 *uart_p, u8 trigger_level)
|
||
{
|
||
u32 fifo_trig_reg;
|
||
FPl011Config *config_p;
|
||
FASSERT(uart_p != NULL);
|
||
FASSERT(trigger_level <= (u8)FPL011IFLS_RXIFLSEL_MASK) ;
|
||
FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY);
|
||
config_p = &uart_p->config;
|
||
|
||
trigger_level = trigger_level & (u8)FPL011IFLS_RXIFLSEL_MASK;
|
||
|
||
fifo_trig_reg = FUART_READREG32(config_p->base_address,
|
||
FPL011IFLS_OFFSET);
|
||
|
||
fifo_trig_reg &= ~FPL011IFLS_RXIFLSEL_MASK;
|
||
|
||
fifo_trig_reg |= (u32)trigger_level ;
|
||
|
||
/*
|
||
* Write the new value for the FIFO control register to it such that
|
||
* the threshold is changed
|
||
*/
|
||
FUART_WRITEREG32(config_p->base_address,
|
||
FPL011IFLS_OFFSET, fifo_trig_reg);
|
||
}
|
||
|
||
/**
|
||
* @name: FPl011SetBaudRate
|
||
* @msg: Sets the baud rate for the device.
|
||
* @param uart_p is a pointer to the FPl011 instance
|
||
* @param BaudRate to be set
|
||
* @return
|
||
* FT_SUCCESS if everything configured as expected
|
||
* FPL011_ERROR_PARAM if the requested rate is not available
|
||
* because there was too much error
|
||
*/
|
||
FError FPl011SetBaudRate(FPl011 *uart_p, u32 baudrate)
|
||
{
|
||
u32 temp;
|
||
u32 divider;
|
||
u32 remainder;
|
||
u32 fraction;
|
||
|
||
FASSERT(NULL != uart_p);
|
||
if ((baudrate * 2) > uart_p->config.ref_clock_hz)
|
||
{
|
||
return FPL011_ERROR_PARAM;
|
||
}
|
||
|
||
/* calculate baud rate divisor */
|
||
temp = 16 * baudrate;
|
||
divider = uart_p->config.ref_clock_hz / temp;
|
||
remainder = uart_p->config.ref_clock_hz % temp;
|
||
temp = (128 * remainder) / temp;
|
||
fraction = temp / 2;
|
||
|
||
if (0 != (temp & 1))
|
||
{
|
||
fraction++;
|
||
}
|
||
|
||
FPl011ClearSpecificOptions(uart_p, FPL011_OPTION_RXEN | FPL011_OPTION_TXEN);
|
||
/* set baud register */
|
||
FUART_WRITEREG32(uart_p->config.base_address, FPL011IBRD_OFFSET, divider);
|
||
FUART_WRITEREG32(uart_p->config.base_address, FPL011FBRD_OFFSET, fraction);
|
||
FPl011SetSpecificOptions(uart_p, FPL011_OPTION_RXEN | FPL011_OPTION_TXEN);
|
||
uart_p->config.baudrate = baudrate;
|
||
return FT_SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @name: FPl011ProgramCtlReg
|
||
* @msg: This function reprograms the control register according to the following
|
||
* sequence mentioned in the TRM
|
||
* @param uart_p is a pointer to the FPl011 instance
|
||
* @param ctrl_reg value to be written
|
||
*/
|
||
void FPl011ProgramCtlReg(FPl011 *uart_p, u32 ctrl_reg)
|
||
{
|
||
u32 line_ctrl_reg;
|
||
u32 temp_ctrl_reg;
|
||
u32 isbusy;
|
||
u32 addr = uart_p->config.base_address;
|
||
FASSERT(uart_p);
|
||
|
||
/*
|
||
* Check is TX completed. If Uart is disabled in the middle, cannot
|
||
* recover. So, keep this check before disable.
|
||
*/
|
||
isbusy = FUART_ISTRANSMITBUSY(addr);
|
||
while (isbusy == (u32)TRUE)
|
||
{
|
||
isbusy = (u32)FUART_ISTRANSMITBUSY(addr);
|
||
}
|
||
|
||
/* Disable UART */
|
||
temp_ctrl_reg = FUART_READREG32(addr, FPL011CR_OFFSET);
|
||
temp_ctrl_reg &= (~FPL011CR_UARTEN);
|
||
FUART_WRITEREG32(addr, FPL011CR_OFFSET, temp_ctrl_reg);
|
||
|
||
/*
|
||
* Flush the transmit FIFO by setting the FEN bit to 0 in the
|
||
* Line Control Register
|
||
*/
|
||
line_ctrl_reg = FUART_READREG32(addr, FPL011LCR_H_OFFSET);
|
||
line_ctrl_reg &= ~FPL011LCR_H_FEN;
|
||
FUART_WRITEREG32(addr, FPL011LCR_H_OFFSET, line_ctrl_reg);
|
||
|
||
/* Setup the Control Register with the passed argument.*/
|
||
FUART_WRITEREG32(addr, FPL011CR_OFFSET, ctrl_reg);
|
||
|
||
/* By default, driver works in FIFO mode, so set FEN as it is
|
||
* cleared above
|
||
*/
|
||
line_ctrl_reg |= FPL011LCR_H_FEN;
|
||
FUART_WRITEREG32(addr, FPL011LCR_H_OFFSET, line_ctrl_reg);
|
||
|
||
/* Enable UART */
|
||
temp_ctrl_reg = FUART_READREG32(addr, FPL011CR_OFFSET);
|
||
temp_ctrl_reg |= FPL011CR_UARTEN;
|
||
FUART_WRITEREG32(addr, FPL011CR_OFFSET, temp_ctrl_reg);
|
||
}
|
||
|
||
/**
|
||
* @name: FPl011SetOperMode
|
||
* @msg: This function sets the operational mode of the UART. The UART can operate
|
||
* in one of four modes: Normal, Local Loopback.
|
||
* @param uart_p is a pointer to the FPl011 instance.
|
||
* @param operation_mode is the mode of the UART.
|
||
*/
|
||
void FPl011SetOperMode(FPl011 *uart_p, u8 operation_mode)
|
||
{
|
||
u32 ctrl_reg;
|
||
FPl011Config *config_p;
|
||
FASSERT(uart_p != NULL);
|
||
FASSERT(operation_mode <= (u8)FPL011_OPER_MODE_LOCAL_LOOP) ;
|
||
FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY);
|
||
config_p = &uart_p->config;
|
||
|
||
ctrl_reg = FUART_READREG32(config_p->base_address, FPL011CR_OFFSET) ;
|
||
|
||
/* Set the correct value by masking the bits, then ORing the const. */
|
||
ctrl_reg &= ~(u32)FPL011CR_LBE;
|
||
|
||
switch (operation_mode)
|
||
{
|
||
case FPL011_OPER_MODE_NORMAL:
|
||
ctrl_reg |= FPL011CR_MODE_NORMAL;
|
||
break;
|
||
case FPL011_OPER_MODE_LOCAL_LOOP:
|
||
ctrl_reg |= FPL011CR_LBE;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
/* Setup the Control Register with the passed argument.*/
|
||
FPl011ProgramCtlReg(uart_p, ctrl_reg);
|
||
}
|