nuttx/arch/arm/src/imx6/imx_lowputc.c
Xiang Xiao 03c31d332f arch/arm: Remove FAR and CODE from chip folder(1)
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2022-05-03 16:50:52 +03:00

589 lines
17 KiB
C

/****************************************************************************
* arch/arm/src/imx6/imx_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 "hardware/imx_iomuxc.h"
#include "hardware/imx_ccm.h"
#include "hardware/imx_uart.h"
#include "imx_config.h"
#include "imx_iomuxc.h"
#include "imx_gpio.h"
#include "imx_lowputc.h"
#include "arm_internal.h"
#include "hardware/imx_pinmux.h"
#include <arch/board/board.h> /* Include last: has dependencies */
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifdef IMX_HAVE_UART_CONSOLE
# if defined(CONFIG_UART1_SERIAL_CONSOLE)
# define IMX_CONSOLE_VBASE IMX_UART1_VBASE
# define IMX_CONSOLE_BAUD CONFIG_UART1_BAUD
# define IMX_CONSOLE_BITS CONFIG_UART1_BITS
# define IMX_CONSOLE_PARITY CONFIG_UART1_PARITY
# define IMX_CONSOLE_2STOP CONFIG_UART1_2STOP
# elif defined(CONFIG_UART2_SERIAL_CONSOLE)
# define IMX_CONSOLE_VBASE IMX_UART2_VBASE
# define IMX_CONSOLE_BAUD CONFIG_UART2_BAUD
# define IMX_CONSOLE_BITS CONFIG_UART2_BITS
# define IMX_CONSOLE_PARITY CONFIG_UART2_PARITY
# define IMX_CONSOLE_2STOP CONFIG_UART2_2STOP
# elif defined(CONFIG_UART3_SERIAL_CONSOLE)
# define IMX_CONSOLE_VBASE IMX_UART3_VBASE
# define IMX_CONSOLE_BAUD CONFIG_UART3_BAUD
# define IMX_CONSOLE_BITS CONFIG_UART3_BITS
# define IMX_CONSOLE_PARITY CONFIG_UART3_PARITY
# define IMX_CONSOLE_2STOP CONFIG_UART3_2STOP
# elif defined(CONFIG_UART4_SERIAL_CONSOLE)
# define IMX_CONSOLE_VBASE IMX_UART4_VBASE
# define IMX_CONSOLE_BAUD CONFIG_UART4_BAUD
# define IMX_CONSOLE_BITS CONFIG_UART4_BITS
# define IMX_CONSOLE_PARITY CONFIG_UART4_PARITY
# define IMX_CONSOLE_2STOP CONFIG_UART4_2STOP
# elif defined(CONFIG_UART5_SERIAL_CONSOLE)
# define IMX_CONSOLE_VBASE IMX_UART5_VBASE
# define IMX_CONSOLE_BAUD CONFIG_UART5_BAUD
# define IMX_CONSOLE_BITS CONFIG_UART5_BITS
# define IMX_CONSOLE_PARITY CONFIG_UART5_PARITY
# define IMX_CONSOLE_2STOP CONFIG_UART5_2STOP
# endif
#endif
/* Clocking *****************************************************************/
/* the UART module receives two clocks, a peripheral_clock (ipg_clk) and the
* module_clock (ipg_perclk). The peripheral_clock is used as write clock
* of the TxFIFO, read clock of the RxFIFO and synchronization of the modem
* control input pins. It must always be running when UART is enabled.
*
* The default ipg_clk is 66MHz (max 66.5MHz). ipg_clk is gated by
* CCGR5[CG12], uart_clk_enable. ipg_clk is shared among many modules and
* should not be controlled by the UART logic.
*
* The module_clock is for all the state machines, writing RxFIFO, reading
* TxFIFO, etc. It must always be running when UART is sending or receiving
* characters. This clock is used in order to allow frequency scaling on
* peripheral_clock without changing configuration of baud rate.
*
* The default ipg_perclk is 80MHz (max 80MHz). ipg_perclk is gated by
* CCGR5[CG13], uart_serial_clk_enable. The clock generation sequence is:
*
* pll3_sw_clk (480M) -> CCGR5[CG13] -> 3 bit divider cg podf=6 ->
* PLL3_80M (80Mhz) -> CDCDR1: uart_clk_podf ->
* 6 bit divider default=1 -> UART_CLK_ROOT
*
* REVISIT: This logic assumes that all dividers are at the default value
* and that the value of the ipg_perclk is 80MHz.
*/
#define IPG_PERCLK_FREQUENCY 80000000
/* The BRM sub-block receives ref_clk (module_clock clock after divider).
* From this clock, and with integer and non-integer division, BRM generates
* a 16x baud rate clock.
*/
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef IMX_HAVE_UART_CONSOLE
static const struct uart_config_s g_console_config =
{
.baud = IMX_CONSOLE_BAUD, /* Configured baud */
.parity = IMX_CONSOLE_PARITY, /* 0=none, 1=odd, 2=even */
.bits = IMX_CONSOLE_BITS, /* Number of bits (5-9) */
.stopbits2 = IMX_CONSOLE_2STOP, /* true: Configure with 2 stop bits instead of 1 */
};
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: imx_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 imx_lowsetup(void)
{
#ifndef CONFIG_SUPPRESS_UART_CONFIG
#ifdef IMX_HAVE_UART
uint32_t regval;
/* Make certain that the ipg_clock and ipg_perclk are enabled for the UART
* modules. Here we set BOTH the ipg_clk and ipg_perclk so that clocking
* is on in all modes (except STOP).
*/
regval = getreg32(IMX_CCM_CCGR5);
regval &= ~(CCM_CCGR5_CG12_MASK | CCM_CCGR5_CG13_MASK);
regval |= (CCM_CCGR5_CG12(CCM_CCGR_ALLMODES) |
CCM_CCGR5_CG13(CCM_CCGR_ALLMODES));
putreg32(regval, IMX_CCM_CCGR5);
#ifdef CONFIG_IMX6_UART1
/* Disable and configure UART1 */
putreg32(0, IMX_UART1_VBASE + UART_UCR1_OFFSET);
putreg32(0, IMX_UART1_VBASE + UART_UCR2_OFFSET);
putreg32(0, IMX_UART1_VBASE + UART_UCR3_OFFSET);
putreg32(0, IMX_UART1_VBASE + UART_UCR4_OFFSET);
/* Configure UART1 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled. REVISIT: DTR, DCD, RI, and DSR -- not configured.
*/
imx_config_gpio(GPIO_UART1_RX_DATA);
imx_config_gpio(GPIO_UART1_TX_DATA);
#ifdef CONFIG_UART1_OFLOWCONTROL
imx_config_gpio(GPIO_UART1_CTS);
#endif
#ifdef CONFIG_UART1_IFLOWCONTROL
imx_config_gpio(GPIO_UART1_RTS);
#endif
#endif
#ifdef CONFIG_IMX6_UART2
/* Disable and configure UART2 */
putreg32(0, IMX_UART2_VBASE + UART_UCR1_OFFSET);
putreg32(0, IMX_UART2_VBASE + UART_UCR2_OFFSET);
putreg32(0, IMX_UART2_VBASE + UART_UCR3_OFFSET);
putreg32(0, IMX_UART2_VBASE + UART_UCR4_OFFSET);
/* Configure UART2 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx_config_gpio(GPIO_UART2_RX_DATA);
imx_config_gpio(GPIO_UART2_TX_DATA);
#ifdef CONFIG_UART1_OFLOWCONTROL
imx_config_gpio(GPIO_UART2_CTS);
#endif
#ifdef CONFIG_UART1_IFLOWCONTROL
imx_config_gpio(GPIO_UART2_RTS);
#endif
#endif
#ifdef CONFIG_IMX6_UART3
/* Disable and configure UART3 */
putreg32(0, IMX_UART3_VBASE + UART_UCR1_OFFSET);
putreg32(0, IMX_UART3_VBASE + UART_UCR2_OFFSET);
putreg32(0, IMX_UART3_VBASE + UART_UCR3_OFFSET);
putreg32(0, IMX_UART3_VBASE + UART_UCR4_OFFSET);
/* Configure UART3 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx_config_gpio(GPIO_UART3_RX_DATA);
imx_config_gpio(GPIO_UART3_TX_DATA);
#ifdef CONFIG_UART1_OFLOWCONTROL
imx_config_gpio(GPIO_UART3_CTS);
#endif
#ifdef CONFIG_UART1_IFLOWCONTROL
imx_config_gpio(GPIO_UART3_RTS);
#endif
#endif
#ifdef CONFIG_IMX6_UART4
/* Disable and configure UART4 */
putreg32(0, IMX_UART4_VBASE + UART_UCR1_OFFSET);
putreg32(0, IMX_UART4_VBASE + UART_UCR2_OFFSET);
putreg32(0, IMX_UART4_VBASE + UART_UCR3_OFFSET);
putreg32(0, IMX_UART4_VBASE + UART_UCR4_OFFSET);
/* Configure UART4 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx_config_gpio(GPIO_UART4_RX_DATA);
imx_config_gpio(GPIO_UART4_TX_DATA);
#ifdef CONFIG_UART1_OFLOWCONTROL
imx_config_gpio(GPIO_UART4_CTS);
#endif
#ifdef CONFIG_UART1_IFLOWCONTROL
imx_config_gpio(GPIO_UART4_RTS);
#endif
#endif
#ifdef CONFIG_IMX6_UART5
/* Disable and configure UART5 */
putreg32(0, IMX_UART5_VBASE + UART_UCR1_OFFSET);
putreg32(0, IMX_UART5_VBASE + UART_UCR2_OFFSET);
putreg32(0, IMX_UART5_VBASE + UART_UCR3_OFFSET);
putreg32(0, IMX_UART5_VBASE + UART_UCR4_OFFSET);
/* Configure UART5 pins: RXD and TXD. Also configure RTS and CTS if flow
* control is enabled.
*/
imx_config_gpio(GPIO_UART5_RX_DATA);
imx_config_gpio(GPIO_UART5_TX_DATA);
#ifdef CONFIG_UART1_OFLOWCONTROL
imx_config_gpio(GPIO_UART5_CTS);
#endif
#ifdef CONFIG_UART1_IFLOWCONTROL
imx_config_gpio(GPIO_UART5_RTS);
#endif
#endif
#ifdef IMX_HAVE_UART_CONSOLE
/* Configure the serial console for initial, non-interrupt driver mode */
imx_uart_configure(IMX_CONSOLE_VBASE, &g_console_config);
#endif
#endif /* IMX_HAVE_UART */
#endif /* CONFIG_SUPPRESS_UART_CONFIG */
}
/****************************************************************************
* Name: imx_uart_configure
*
* Description:
* Configure a UART for non-interrupt driven operation
*
****************************************************************************/
#ifdef IMX_HAVE_UART
int imx_uart_configure(uint32_t base, const struct uart_config_s *config)
{
#ifndef CONFIG_SUPPRESS_UART_CONFIG
uint64_t tmp;
uint32_t regval;
uint32_t ucr2;
uint32_t refclk;
uint32_t div;
uint32_t num;
uint32_t den;
b16_t ratio;
/* Disable the UART */
putreg32(0, base + UART_UCR1_OFFSET);
putreg32(0, base + UART_UCR2_OFFSET);
putreg32(0, base + UART_UCR3_OFFSET);
putreg32(0, base + UART_UCR4_OFFSET);
/* Wait for the UART to come out of reset */
while ((getreg32(base + UART_UCR2_OFFSET) & UART_UCR2_SRST) == 0);
/* Set up UCR2, Clearing all bits that will be configured below. */
ucr2 = getreg32(base + UART_UCR2_OFFSET);
ucr2 &= ~(UART_UCR2_WS | UART_UCR2_STPB | UART_UCR2_PREN |
UART_UCR2_PROE | UART_UCR2_IRTS | UART_UCR2_CTSC);
/* Select the number of data bits */
DEBUGASSERT(config->bits == 7 || config->bits == 8);
if (config->bits == 8)
{
ucr2 |= UART_UCR2_WS;
}
/* Select the number of stop bits */
if (config->stopbits2)
{
ucr2 |= UART_UCR2_STPB;
}
/* Select even/odd parity */
if (config->parity != 0)
{
DEBUGASSERT(config->parity == 1 || config->parity == 2);
ucr2 |= UART_UCR2_PREN;
if (config->parity == 1)
{
ucr2 |= UART_UCR2_PROE;
}
}
/* Setup hardware flow control */
regval = 0;
#if 0
if (config->hwfc)
{
/* CTS controlled by Rx FIFO */
ucr2 |= UART_UCR2_CTSC;
/* Set CTS trigger level */
regval |= 30 << UART_UCR4_CTSTL_SHIFT;
/* REVISIT: There are other relevant bits that must be managed in
* UCR1 and UCR3.
*/
}
else
#endif
{
/* Ignore RTS */
ucr2 |= UART_UCR2_IRTS;
}
putreg32(regval, base + UART_UCR4_OFFSET);
/* Setup the new UART configuration */
putreg32(ucr2, base + UART_UCR2_OFFSET);
/* Select a reference clock divider.
* REVISIT: For now we just use a divider of 2. That might not be
* optimal for very high or very low baud settings.
*/
div = 2;
refclk = (IPG_PERCLK_FREQUENCY >> 1);
/* Set the baud.
*
* baud = REFFREQ / (16 * NUM/DEN)
* baud = REFFREQ / 16 / RATIO
* RATIO = REFREQ / 16 / baud;
*
* NUM = SCALE * RATIO
* DEN = SCALE
*
* UMBR = NUM-1
* UBIR = DEN-1;
*/
tmp = ((uint64_t)refclk << (16 - 4)) / config->baud;
DEBUGASSERT(tmp < 0x0000000100000000ll);
ratio = (b16_t)tmp;
/* Pick a scale factor that gives us about 14 bits of accuracy.
* REVISIT: Why not go all the way to 16-bits?
*/
if (ratio < b16HALF)
{
den = (1 << 15);
num = b16toi(ratio << 15);
DEBUGASSERT(num > 0);
}
else if (ratio < b16ONE)
{
den = (1 << 14);
num = b16toi(ratio << 14);
}
else if (ratio < itob16(2))
{
den = (1 << 13);
num = b16toi(ratio << 13);
}
else if (ratio < itob16(4))
{
den = (1 << 12);
num = b16toi(ratio << 12);
}
else if (ratio < itob16(8))
{
den = (1 << 11);
num = b16toi(ratio << 11);
}
else if (ratio < itob16(16))
{
den = (1 << 10);
num = b16toi(ratio << 10);
}
else if (ratio < itob16(32))
{
den = (1 << 9);
num = b16toi(ratio << 9);
}
else if (ratio < itob16(64))
{
den = (1 << 8);
num = b16toi(ratio << 8);
}
else if (ratio < itob16(128))
{
den = (1 << 7);
num = b16toi(ratio << 7);
}
else if (ratio < itob16(256))
{
den = (1 << 6);
num = b16toi(ratio << 6);
}
else if (ratio < itob16(512))
{
den = (1 << 5);
num = b16toi(ratio << 5);
}
else if (ratio < itob16(1024))
{
den = (1 << 4);
num = b16toi(ratio << 4);
}
else if (ratio < itob16(2048))
{
den = (1 << 3);
num = b16toi(ratio << 3);
}
else if (ratio < itob16(4096))
{
den = (1 << 2);
num = b16toi(ratio << 2);
}
else if (ratio < itob16(8192))
{
den = (1 << 1);
num = b16toi(ratio << 1);
}
else /* if (ratio < itob16(16384)) */
{
DEBUGASSERT(ratio < itob16(16384));
den = (1 << 0);
num = b16toi(ratio);
}
/* Reduce if possible without losing accuracy. */
while ((num & 1) == 0 && (den & 1) == 0)
{
num >>= 1;
den >>= 1;
}
/* The actual values are we write to the registers need to be
* decremented by 1. NOTE that the UBIR must be set before
* the UBMR.
*/
putreg32(den - 1, base + UART_UBIR_OFFSET);
putreg32(num - 1, base + UART_UBMR_OFFSET);
/* Fixup the divisor, the value in the UFCR register is
*
* 000 = Divide input clock by 6
* 001 = Divide input clock by 5
* 010 = Divide input clock by 4
* 011 = Divide input clock by 3
* 100 = Divide input clock by 2
* 101 = Divide input clock by 1
* 110 = Divide input clock by 7
*/
if (div == 7)
{
div = 6;
}
else
{
div = 6 - div;
}
regval = div << UART_UFCR_RFDIV_SHIFT;
/* Set the TX trigger level to interrupt when the TxFIFO has 2 or fewer
* characters. Set the RX trigger level to interrupt when the RxFIFO has
* 1 character.
*/
regval |= ((2 << UART_UFCR_TXTL_SHIFT) | (1 << UART_UFCR_RXTL_SHIFT));
putreg32(regval, base + UART_UFCR_OFFSET);
/* Selected. Selects proper input pins for serial and Infrared input
* signal. NOTE: In this chip, UARTs are used in MUXED mode, so that this
* bit should always be set.
*/
putreg32(UART_UCR3_RXDMUXSEL, base + UART_UCR3_OFFSET);
/* Enable the TX and RX */
ucr2 |= (UART_UCR2_TXEN | UART_UCR2_RXEN);
putreg32(ucr2, base + UART_UCR2_OFFSET);
/* Enable the UART */
regval = getreg32(base + UART_UCR1_OFFSET);
regval |= UART_UCR1_UARTEN;
putreg32(regval, base + UART_UCR1_OFFSET);
#endif
return OK;
}
#endif /* IMX_HAVE_UART */
/****************************************************************************
* Name: imx_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.)
*
****************************************************************************/
#ifdef IMX_HAVE_UART
void imx_lowputc(int ch)
{
/* Poll the TX fifo trigger level bit of the UART status register. When the
* TXFE bit is non-zero, the TX Buffer FIFO is empty.
*/
while ((getreg32(IMX_CONSOLE_VBASE + UART_USR2_OFFSET) &
UART_USR2_TXFE) == 0);
/* If the character to output is a newline, then pre-pend a carriage
* return
*/
/* Send the character by writing it into the UART_TXD register. */
putreg32((uint32_t)ch, IMX_CONSOLE_VBASE + UART_TXD_OFFSET);
}
#endif