blob: c41f6f43eed123fa9d2ec0fe604b478485a5e43e [file] [log] [blame]
/****************************************************************************
* arch/arm64/src/imx9/imx9_lowputc.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <fixedmath.h>
#include <assert.h>
#include <errno.h>
#include "hardware/imx9_pinmux.h"
#include "hardware/imx9_lpuart.h"
#include "arm64_internal.h"
#include "imx9_lowputc.h"
#include "imx9_ccm.h"
#include "imx9_iomuxc.h"
#include "hardware/imx9_ccm.h"
#include "hardware/imx9_pinmux.h"
#include <arch/board/board.h> /* Include last: has dependencies */
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#if defined(CONFIG_LPUART1_SERIAL_CONSOLE)
# define IMX9_CONSOLE_DEVOFF 0
# define IMX9_CONSOLE_BASE IMX9_LPUART1_BASE
# define IMX9_CONSOLE_BAUD CONFIG_LPUART1_BAUD
# define IMX9_CONSOLE_BITS CONFIG_LPUART1_BITS
# define IMX9_CONSOLE_PARITY CONFIG_LPUART1_PARITY
# define IMX9_CONSOLE_2STOP CONFIG_LPUART1_2STOP
#elif defined(CONFIG_LPUART2_SERIAL_CONSOLE)
# define IMX9_CONSOLE_DEVNUM 1
# define IMX9_CONSOLE_BASE IMX9_LPUART2_BASE
# define IMX9_CONSOLE_BAUD CONFIG_LPUART2_BAUD
# define IMX9_CONSOLE_BITS CONFIG_LPUART2_BITS
# define IMX9_CONSOLE_PARITY CONFIG_LPUART2_PARITY
# define IMX9_CONSOLE_2STOP CONFIG_LPUART2_2STOP
#elif defined(CONFIG_LPUART3_SERIAL_CONSOLE)
# define IMX9_CONSOLE_DEVNUM 2
# define IMX9_CONSOLE_BASE IMX9_LPUART3_BASE
# define IMX9_CONSOLE_BAUD CONFIG_LPUART3_BAUD
# define IMX9_CONSOLE_BITS CONFIG_LPUART3_BITS
# define IMX9_CONSOLE_PARITY CONFIG_LPUART3_PARITY
# define IMX9_CONSOLE_2STOP CONFIG_LPUART3_2STOP
#elif defined(CONFIG_LPUART4_SERIAL_CONSOLE)
# define IMX9_CONSOLE_DEVNUM 3
# define IMX9_CONSOLE_BASE IMX9_LPUART4_BASE
# define IMX9_CONSOLE_BAUD CONFIG_LPUART4_BAUD
# define IMX9_CONSOLE_BITS CONFIG_LPUART4_BITS
# define IMX9_CONSOLE_PARITY CONFIG_LPUART4_PARITY
# define IMX9_CONSOLE_2STOP CONFIG_LPUART4_2STOP
#elif defined(CONFIG_LPUART5_SERIAL_CONSOLE)
# define IMX9_CONSOLE_DEVNUM 4
# define IMX9_CONSOLE_BASE IMX9_LPUART5_BASE
# define IMX9_CONSOLE_BAUD CONFIG_LPUART5_BAUD
# define IMX9_CONSOLE_BITS CONFIG_LPUART5_BITS
# define IMX9_CONSOLE_PARITY CONFIG_LPUART5_PARITY
# define IMX9_CONSOLE_2STOP CONFIG_LPUART5_2STOP
#elif defined(CONFIG_LPUART6_SERIAL_CONSOLE)
# define IMX9_CONSOLE_DEVNUM 5
# define IMX9_CONSOLE_BASE IMX9_LPUART6_BASE
# define IMX9_CONSOLE_BAUD CONFIG_LPUART6_BAUD
# define IMX9_CONSOLE_BITS CONFIG_LPUART6_BITS
# define IMX9_CONSOLE_PARITY CONFIG_LPUART6_PARITY
# define IMX9_CONSOLE_2STOP CONFIG_LPUART6_2STOP
#elif defined(CONFIG_LPUART7_SERIAL_CONSOLE)
# define IMX9_CONSOLE_DEVNUM 6
# define IMX9_CONSOLE_BASE IMX9_LPUART7_BASE
# define IMX9_CONSOLE_BAUD CONFIG_LPUART7_BAUD
# define IMX9_CONSOLE_BITS CONFIG_LPUART7_BITS
# define IMX9_CONSOLE_PARITY CONFIG_LPUART7_PARITY
# define IMX9_CONSOLE_2STOP CONFIG_LPUART7_2STOP
#elif defined(CONFIG_LPUART8_SERIAL_CONSOLE)
# define IMX9_CONSOLE_DEVNUM 7
# define IMX9_CONSOLE_BASE IMX9_LPUART8_BASE
# define IMX9_CONSOLE_BAUD CONFIG_LPUART8_BAUD
# define IMX9_CONSOLE_BITS CONFIG_LPUART8_BITS
# define IMX9_CONSOLE_PARITY CONFIG_LPUART8_PARITY
# define IMX9_CONSOLE_2STOP CONFIG_LPUART8_2STOP
#endif
#define ABS(n) (((n) < 0) ? -(n) : (n))
/* Clocking *****************************************************************/
/* Functional clocking is provided via the PCC. The PCC clocking must
* be configured by board-specific logic prior to using the LPUART.
*/
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef IMX9_CONSOLE_BASE
static const struct uart_config_s g_console_config =
{
.baud = IMX9_CONSOLE_BAUD, /* Configured baud */
.parity = IMX9_CONSOLE_PARITY, /* 0=none, 1=odd, 2=even */
.bits = IMX9_CONSOLE_BITS, /* Number of bits (5-9) */
.stopbits2 = IMX9_CONSOLE_2STOP, /* true: Configure with 2 stop bits instead of 1 */
};
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: imx9_lowsetup
*
* Description:
* Called at the very beginning of _start. Performs low level
* initialization including setup of the console UART. This UART done
* early so that the serial console is available for debugging very early
* in the boot sequence.
*
****************************************************************************/
void imx9_lowsetup(void)
{
#ifndef CONFIG_SUPPRESS_LPUART_CONFIG
#ifdef CONFIG_IMX9_LPUART1
/* Configure LPUART1 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx9_iomux_configure(MUX_LPUART1_RX);
imx9_iomux_configure(MUX_LPUART1_TX);
#ifdef CONFIG_LPUART1_OFLOWCONTROL
imx9_iomux_configure(MUX_LPUART1_CTS);
#endif
#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)) || \
(defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)))
imx9_iomux_configure(MUX_LPUART1_RTS);
#endif
#endif
#ifdef CONFIG_IMX9_LPUART2
/* Configure LPUART2 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx9_iomux_configure(MUX_LPUART2_RX);
imx9_iomux_configure(MUX_LPUART2_TX);
#ifdef CONFIG_LPUART2_OFLOWCONTROL
imx9_iomux_configure(MUX_LPUART2_CTS);
#endif
#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)) || \
(defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL)))
imx9_iomux_configure(MUX_LPUART2_RTS);
#endif
#endif
#ifdef CONFIG_IMX9_LPUART3
/* Configure LPUART3 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx9_iomux_configure(MUX_LPUART3_RX);
imx9_iomux_configure(MUX_LPUART3_TX);
#ifdef CONFIG_LPUART3_OFLOWCONTROL
imx9_iomux_configure(MUX_LPUART3_CTS);
#endif
#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART3_RS485RTSCONTROL)) || \
(defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART3_IFLOWCONTROL)))
imx9_iomux_configure(MUX_LPUART3_RTS);
#endif
#endif
#ifdef CONFIG_IMX9_LPUART4
/* Configure LPUART4 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx9_iomux_configure(LPUART4_RX);
imx9_iomux_configure(LPUART4_TX);
#ifdef CONFIG_LPUART4_OFLOWCONTROL
imx9_iomux_configure(LPUART4_CTS);
#endif
#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART4_RS485RTSCONTROL)) || \
(defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART4_IFLOWCONTROL)))
imx9_iomux_configure(LPUART4_RTS);
#endif
#endif
#ifdef CONFIG_IMX9_LPUART5
/* Configure LPUART5 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx9_iomux_configure(MUX_LPUART5_RX);
imx9_iomux_configure(MUX_LPUART5_TX);
#ifdef CONFIG_LPUART5_OFLOWCONTROL
imx9_iomux_configure(MUX_LPUART5_CTS);
#endif
#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART5_RS485RTSCONTROL)) || \
(defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART5_IFLOWCONTROL)))
imx9_iomux_configure(MUX_LPUART5_RTS);
#endif
#endif
#ifdef CONFIG_IMX9_LPUART6
/* Configure LPUART6 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx9_iomux_configure(MUX_LPUART6_RX);
imx9_iomux_configure(MUX_LPUART6_TX);
#ifdef CONFIG_LPUART6_OFLOWCONTROL
imx9_iomux_configure(MUX_LPUART6_CTS);
#endif
#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART6_RS485RTSCONTROL)) || \
(defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART6_IFLOWCONTROL)))
imx9_iomux_configure(MUX_LPUART6_RTS);
#endif
#endif
#ifdef CONFIG_IMX9_LPUART7
/* Configure LPUART7 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx9_iomux_configure(MUX_LPUART7_RX);
imx9_iomux_configure(MUX_LPUART7_TX);
#ifdef CONFIG_LPUART7_OFLOWCONTROL
imx9_iomux_configure(MUX_LPUART7_CTS);
#endif
#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART7_RS485RTSCONTROL)) || \
(defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART7_IFLOWCONTROL)))
imx9_iomux_configure(MUX_LPUART7_RTS);
#endif
#endif
#ifdef CONFIG_IMX9_LPUART8
/* Configure LPUART8 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx9_iomux_configure(MUX_LPUART8_RX);
imx9_iomux_configure(MUX_LPUART8_TX);
#ifdef CONFIG_LPUART0_OFLOWCONTROL
imx9_iomux_configure(MUX_LPUART8_CTS);
#endif
#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART8_RS485RTSCONTROL)) || \
(defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART8_IFLOWCONTROL)))
imx9_iomux_configure(MUX_LPUART8_RTS);
#endif
#endif
#ifdef IMX9_CONSOLE_BASE
/* Configure the serial console for initial, non-interrupt driver mode */
imx9_lpuart_configure(IMX9_CONSOLE_BASE, IMX9_CONSOLE_DEVOFF,
&g_console_config);
#endif
#endif /* CONFIG_SUPPRESS_LPUART_CONFIG */
}
/****************************************************************************
* Name: imx9_lpuart_configure
*
* Description:
* Configure a UART for non-interrupt driven operation
*
****************************************************************************/
int imx9_lpuart_configure(uint32_t base, int uartnum,
const struct uart_config_s *config)
{
int lpuart_freq = 24000000;
uint16_t sbr;
uint16_t temp_sbr;
uint32_t osr;
uint32_t temp_osr;
int temp_diff;
int calculated_baud;
int baud_diff;
uint32_t regval;
/* Configure root clock to 24MHz OSC */
imx9_ccm_configure_root_clock(CCM_CR_LPUART1 + uartnum - 1, OSC_24M, 1);
/* Enable peripheral clock */
imx9_ccm_gate_on(CCM_LPCG_LPUART1 + uartnum - 1, true);
/* This LPUART instantiation uses a slightly different baud rate
* calculation. The idea is to use the best OSR (over-sampling rate)
* possible.
*
* NOTE: OSR is typically hard-set to 16 in other LPUART instantiations
* loop to find the best OSR value possible, one that generates minimum
* baud_diff iterate through the rest of the supported values of OSR
*/
baud_diff = config->baud;
osr = 0;
sbr = 0;
for (temp_osr = 4; temp_osr <= 32; temp_osr++)
{
/* Calculate the temporary sbr value */
temp_sbr = (lpuart_freq / (config->baud * temp_osr));
/* Set temp_sbr to 1 if the sourceClockInHz can not satisfy the
* desired baud rate.
*/
if (temp_sbr == 0)
{
temp_sbr = 1;
}
/* Calculate the baud rate based on the temporary OSR and SBR values */
calculated_baud = (lpuart_freq / (temp_osr * temp_sbr));
temp_diff = ABS(calculated_baud - config->baud);
/* Select the better value between srb and (sbr + 1) */
calculated_baud = (lpuart_freq / (temp_osr * (temp_sbr + 1)));
if (temp_diff >
ABS(calculated_baud - config->baud))
{
temp_diff = ABS(calculated_baud - config->baud);
temp_sbr++;
}
if (temp_diff <= baud_diff)
{
baud_diff = temp_diff;
osr = temp_osr;
sbr = temp_sbr;
}
}
if (baud_diff > ((config->baud * 3) / 100))
{
/* Unacceptable baud rate difference of more than 3% */
return ERROR;
}
/* Reset all internal logic and registers, except the Global Register */
regval = getreg32(base + IMX9_LPUART_GLOBAL_OFFSET);
regval |= LPUART_GLOBAL_RST;
putreg32(regval, base + IMX9_LPUART_GLOBAL_OFFSET);
regval &= ~LPUART_GLOBAL_RST;
putreg32(regval, base + IMX9_LPUART_GLOBAL_OFFSET);
/* Construct MODIR register */
regval = 0;
if (config->userts)
{
regval |= LPUART_MODIR_RXRTSE;
}
else if (config->users485)
{
/* Both TX and RX side can't control RTS, so this gives
* the RX side precedence. This should have been filtered
* in layers above anyway, but it's just a precaution.
*/
regval |= LPUART_MODIR_TXRTSE;
}
if (config->usects)
{
regval |= LPUART_MODIR_TXCTSE;
}
if (config->invrts)
{
regval |= LPUART_MODIR_TXRTSPOL;
}
putreg32(regval, base + IMX9_LPUART_MODIR_OFFSET);
regval = 0;
if ((osr > 3) && (osr < 8))
{
regval |= LPUART_BAUD_BOTHEDGE;
}
if (config->stopbits2)
{
regval |= LPUART_BAUD_SBNS;
}
regval |= LPUART_BAUD_OSR(osr) | LPUART_BAUD_SBR(sbr);
putreg32(regval, base + IMX9_LPUART_BAUD_OFFSET);
regval = 0;
if (config->parity == 1)
{
regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_ODD;
}
else if (config->parity == 2)
{
regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_EVEN;
}
if (config->bits == 9 || (config->bits == 8 && config->parity != 0))
{
regval |= LPUART_CTRL_M;
}
else if ((config->bits == 8))
{
regval &= ~LPUART_CTRL_M;
}
else
{
/* REVISIT: Here should be added support of other bit modes. */
return -ENOSYS;
}
regval |= LPUART_CTRL_RE | LPUART_CTRL_TE;
putreg32(regval, base + IMX9_LPUART_CTRL_OFFSET);
return OK;
}
/****************************************************************************
* Name: arm64_earlyprintinit
*
* Description:
* Configure LPUART1 for non-interrupt driven operation
*
****************************************************************************/
void arm64_earlyprintinit(char ch)
{
/* Assume bootloader has already set up the LPUART1 */
}
/****************************************************************************
* Name: arm64_lowputc
*
* Description:
* Output a byte with as few system dependencies as possible. This will
* even work BEFORE the console is initialized if we are booting from U-
* Boot (and the same UART is used for the console, of course.)
*
****************************************************************************/
void arm64_lowputc(char ch)
{
#ifdef IMX9_CONSOLE_BASE
while ((getreg32(IMX9_CONSOLE_BASE + IMX9_LPUART_STAT_OFFSET) &
LPUART_STAT_TDRE) == 0)
{
}
/* If the character to output is a newline,
* then pre-pend a carriage return
*/
if (ch == '\n')
{
/* Send the carriage return by writing it into the UART_TXD register. */
putreg32((uint32_t)'\r',
IMX9_CONSOLE_BASE + IMX9_LPUART_DATA_OFFSET);
/* Wait for the transmit register to be emptied. When the TXFE bit is
* non-zero, the TX Buffer FIFO is empty.
*/
while ((getreg32(IMX9_CONSOLE_BASE + IMX9_LPUART_STAT_OFFSET) &
LPUART_STAT_TDRE) == 0)
{
}
}
/* Send the character by writing it into the UART_TXD register. */
putreg32((uint32_t)ch, IMX9_CONSOLE_BASE + IMX9_LPUART_DATA_OFFSET);
/* Wait for the transmit register to be emptied. When the TXFE bit is
* non-zero, the TX Buffer FIFO is empty.
*/
while ((getreg32(IMX9_CONSOLE_BASE + IMX9_LPUART_STAT_OFFSET) &
LPUART_STAT_TDRE) == 0)
{
}
#endif
}