summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/lpc17xx/lpc17_serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/arch/arm/src/lpc17xx/lpc17_serial.c')
-rwxr-xr-xnuttx/arch/arm/src/lpc17xx/lpc17_serial.c332
1 files changed, 314 insertions, 18 deletions
diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_serial.c b/nuttx/arch/arm/src/lpc17xx/lpc17_serial.c
index 8df4d5249..3ef2cbf6a 100755
--- a/nuttx/arch/arm/src/lpc17xx/lpc17_serial.c
+++ b/nuttx/arch/arm/src/lpc17xx/lpc17_serial.c
@@ -47,10 +47,13 @@
#include <string.h>
#include <errno.h>
#include <debug.h>
+
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/serial.h>
+
#include <arch/serial.h>
+#include <arch/board/board.h>
#include "chip.h"
#include "up_arch.h"
@@ -426,9 +429,9 @@ static uart_dev_t g_uart3port =
# endif
#endif
-/****************************************************************************
- * Private Functions
- ****************************************************************************/
+/************************************************************************************
+ * Inline Functions
+ ************************************************************************************/
/****************************************************************************
* Name: up_serialin
@@ -491,6 +494,287 @@ static inline void up_enablebreaks(struct up_dev_s *priv, bool enable)
up_serialout(priv, LPC17_UART_LCR_OFFSET, lcr);
}
+/************************************************************************************
+ * Name: lpc17_uartcclkdiv
+ *
+ * Descrption:
+ * Select a CCLK divider to produce the UART PCLK. The stratey is to select the
+ * smallest divisor that results in an solution within range of the 16-bit
+ * DLM and DLL divisor:
+ *
+ * PCLK = CCLK / divisor
+ * BAUD = PCLK / (16 * DL)
+ *
+ * Ignoring the fractional divider for now.
+ *
+ * NOTE: This is an inline function. If a typical optimization level is used and
+ * a constant is provided for the desired frequency, then most of the following
+ * logic will be optimized away.
+ *
+ ************************************************************************************/
+
+static inline uint8_t lpc17_uartcclkdiv(uint32_t baud)
+{
+ /* Ignoring the fractional divider, the BAUD is given by:
+ *
+ * BAUD = PCLK / (16 * DL), or
+ * DL = PCLK / BAUD / 16
+ *
+ * Where:
+ *
+ * PCLK = CCLK / divisor.
+ *
+ * Check divisor == 1. This works if the upper limit is met
+ *
+ * DL < 0xffff, or
+ * PCLK / BAUD / 16 < 0xffff, or
+ * CCLK / BAUD / 16 < 0xffff, or
+ * CCLK < BAUD * 0xffff * 16
+ * BAUD > CCLK / 0xffff / 16
+ *
+ * And the lower limit is met (we can't allow DL to get very close to one).
+ *
+ * DL >= MinDL
+ * CCLK / BAUD / 16 >= MinDL, or
+ * BAUD <= CCLK / 16 / MinDL
+ */
+
+ if (baud < (LPC17_CCLK / 16 / UART_MINDL ))
+ {
+ return SYSCON_PCLKSEL_CCLK;
+ }
+
+ /* Check divisor == 2. This works if:
+ *
+ * 2 * CCLK / BAUD / 16 < 0xffff, or
+ * BAUD > CCLK / 0xffff / 8
+ *
+ * And
+ *
+ * 2 * CCLK / BAUD / 16 >= MinDL, or
+ * BAUD <= CCLK / 8 / MinDL
+ */
+
+ else if (baud < (LPC17_CCLK / 8 / UART_MINDL ))
+ {
+ return SYSCON_PCLKSEL_CCLK2;
+ }
+
+ /* Check divisor == 4. This works if:
+ *
+ * 4 * CCLK / BAUD / 16 < 0xffff, or
+ * BAUD > CCLK / 0xffff / 4
+ *
+ * And
+ *
+ * 4 * CCLK / BAUD / 16 >= MinDL, or
+ * BAUD <= CCLK / 4 / MinDL
+ */
+
+ else if (baud < (LPC17_CCLK / 4 / UART_MINDL ))
+ {
+ return SYSCON_PCLKSEL_CCLK4;
+ }
+
+ /* Check divisor == 8. This works if:
+ *
+ * 8 * CCLK / BAUD / 16 < 0xffff, or
+ * BAUD > CCLK / 0xffff / 2
+ *
+ * And
+ *
+ * 8 * CCLK / BAUD / 16 >= MinDL, or
+ * BAUD <= CCLK / 2 / MinDL
+ */
+
+ else /* if (baud < (LPC17_CCLK / 2 / UART_MINDL )) */
+ {
+ return SYSCON_PCLKSEL_CCLK8;
+ }
+}
+
+/************************************************************************************
+ * Name: lpc17_uart0config, uart1config, uart2config, nad uart3config
+ *
+ * Descrption:
+ * Configure the UART. UART0/1/2/3 peripherals are configured using the following
+ * registers:
+ *
+ * 1. Power: In the PCONP register, set bits PCUART0/1/2/3.
+ * On reset, UART0 and UART 1 are enabled (PCUART0 = 1 and PCUART1 = 1)
+ * and UART2/3 are disabled (PCUART1 = 0 and PCUART3 = 0).
+ * 2. Peripheral clock: In the PCLKSEL0 register, select PCLK_UART0 and
+ * PCLK_UART1; in the PCLKSEL1 register, select PCLK_UART2 and PCLK_UART3.
+ * 3. Pins: Select UART pins through the PINSEL registers and pin modes
+ * through the PINMODE registers. UART receive pins should not have
+ * pull-down resistors enabled.
+ *
+ ************************************************************************************/
+
+#ifdef CONFIG_LPC17_UART0
+static inline void lpc17_uart0config(uint8_t clkdiv)
+{
+ uint32_t regval;
+ irqstate_t flags;
+
+ /* Step 1: Enable power on UART0 */
+
+ flags = irqsave();
+ regval = getreg32(LPC17_SYSCON_PCONP);
+ regval |= ~SYSCON_PCONP_PCUART0;
+ putreg32(regval, LPC17_SYSCON_PCONP);
+
+ /* Step 2: Enable clocking on UART */
+
+ regval = getreg32(LPC17_SYSCON_PCLKSEL0);
+ regval &= ~SYSCON_PCLKSEL0_UART0_MASK;
+ regval |= ((uint32_t)clkdiv << SYSCON_PCLKSEL0_UART0_SHIFT);
+ putreg32(regval, LPC17_SYSCON_PCLKSEL0);
+
+ /* Step 3: Configure I/O pins */
+
+ lpc17_configgpio(GPIO_UART0_TXD);
+ lpc17_configgpio(GPIO_UART0_RXD);
+ irqrestore(flags);
+};
+#endif
+
+#ifdef CONFIG_LPC17_UART1
+static inline void lpc17_uart1config(uint8_t clkdiv)
+{
+ uint32_t regval;
+ irqstate_t flags;
+
+ /* Step 1: Enable power on UART1 */
+
+ flags = irqsave();
+ regval = getreg32(LPC17_SYSCON_PCONP);
+ regval |= ~SYSCON_PCONP_PCUART1;
+ putreg32(regval, LPC17_SYSCON_PCONP);
+
+ /* Step 2: Enable clocking on UART */
+
+ regval = getreg32(LPC17_SYSCON_PCLKSEL0);
+ regval &= ~SYSCON_PCLKSEL0_UART1_MASK;
+ regval |= ((uint32_t)clkdiv << SYSCON_PCLKSEL0_UART1_SHIFT);
+ putreg32(regval, LPC17_SYSCON_PCLKSEL0);
+
+ /* Step 3: Configure I/O pins */
+
+ lpc17_configgpio(GPIO_UART1_TXD);
+ lpc17_configgpio(GPIO_UART1_RXD);
+#ifdef CONFIG_UART0_FLOWCONTROL
+ lpc17_configgpio(GPIO_UART1_CTS);
+ lpc17_configgpio(GPIO_UART1_DCD);
+ lpc17_configgpio(GPIO_UART1_DSR);
+ lpc17_configgpio(GPIO_UART1_DTR);
+ lpc17_configgpio(GPIO_UART1_RI);
+ lpc17_configgpio(GPIO_UART1_RTS);
+#endif
+ irqrestore(flags);
+};
+#endif
+
+#ifdef CONFIG_LPC17_UART2
+static inline void lpc17_uart2config(uint8_t clkdiv)
+{
+ uint32_t regval;
+ irqstate_t flags;
+
+ /* Step 1: Enable power on UART2 */
+
+ flags = irqsave();
+ regval = getreg32(LPC17_SYSCON_PCONP);
+ regval |= SYSCON_PCONP_PCUART2;
+ putreg32(regval, LPC17_SYSCON_PCONP);
+
+ /* Step 2: Enable clocking on UART */
+
+ regval = getreg32(LPC17_SYSCON_PCLKSEL1);
+ regval &= ~SYSCON_PCLKSEL0_UART2_MASK;
+ regval |= ((uint32_t)clkdiv << SYSCON_PCLKSEL1_UART2_SHIFT);
+ putreg32(regval, LPC17_SYSCON_PCLKSEL1);
+
+ /* Step 3: Configure I/O pins */
+
+ lpc17_configgpio(GPIO_UART2_TXD);
+ lpc17_configgpio(GPIO_UART2_RXD);
+ irqrestore(flags);
+};
+#endif
+
+#ifdef CONFIG_LPC17_UART3
+static inline void lpc17_uart3config(uint8_t clkdiv)
+{
+ uint32_t regval;
+ irqstate_t flags;
+
+ /* Step 1: Enable power on UART3 */
+
+ flags = irqsave();
+ regval = getreg32(LPC17_SYSCON_PCONP);
+ regval |= ~SYSCON_PCONP_PCUART3;
+ putreg32(regval, LPC17_SYSCON_PCONP);
+
+ /* Step 2: Enable clocking on UART */
+
+ regval = getreg32(LPC17_SYSCON_PCLKSEL1);
+ regval &= ~SYSCON_PCLKSEL0_UART3_MASK;
+ regval |= ((uint32_t)clkdiv << SYSCON_PCLKSEL1_UART3_SHIFT);
+ putreg32(regval, LPC17_SYSCON_PCLKSEL1);
+
+ /* Step 3: Configure I/O pins */
+
+ lpc17_configgpio(GPIO_UART3_TXD);
+ lpc17_configgpio(GPIO_UART3_RXD);
+ irqrestore(flags);
+};
+#endif
+
+/************************************************************************************
+ * Name: lpc17_uartdl
+ *
+ * Descrption:
+ * Select a divider to produce the BAUD from the UART PCLK.
+ *
+ * BAUD = PCLK / (16 * DL), or
+ * DL = PCLK / BAUD / 16
+ *
+ * Ignoring the fractional divider for now.
+ *
+ ************************************************************************************/
+
+static inline uint32_t lpc17_uartdl(uint32_t baud, uint8_t divcode)
+{
+ uint32_t num;
+
+ switch (divcode)
+ {
+
+ case SYSCON_PCLKSEL_CCLK4: /* PCLK_peripheral = CCLK/4 */
+ num = (LPC17_CCLK / 4);
+ break;
+
+ case SYSCON_PCLKSEL_CCLK: /* PCLK_peripheral = CCLK */
+ num = LPC17_CCLK;
+ break;
+
+ case SYSCON_PCLKSEL_CCLK2: /* PCLK_peripheral = CCLK/2 */
+ num = (LPC17_CCLK / 2);
+ break;
+
+ case SYSCON_PCLKSEL_CCLK8: /* PCLK_peripheral = CCLK/8 (except CAN1, CAN2, and CAN) */
+ default:
+ num = (LPC17_CCLK / 8);
+ break;
+ }
+ return num / (baud << 4);
+}
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
/****************************************************************************
* Name: up_setup
*
@@ -952,30 +1236,42 @@ static bool up_txempty(struct uart_dev_s *dev)
* serial console will be available during bootup. This must be called
* before up_serialinit.
*
- * NOTE: Power, clocking, and pin configuration was performed in
- * up_lowsetup() very, early in the boot sequence.
+ * NOTE: Configuration of the CONSOLE UART was performed by up_lowsetup()
+ * very early in the boot sequence.
*
****************************************************************************/
void up_earlyserialinit(void)
{
- /* Disable all UARTS */
+ /* Configure all UARTs (except the CONSOLE UART) and disable interrupts */
-#ifdef TTYS0_DEV
- TTYS0_DEV.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART0_BAUD);
- up_disableuartint(TTYS0_DEV.priv, NULL);
+#ifdef CONFIG_LPC17_UART0
+ g_uart0priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART0_BAUD);
+#ifndef CONFIG_UART0_SERIAL_CONSOLE
+ lpc17_uart0config(g_uart0priv.cclkdiv);
#endif
-#ifdef TTYS1_DEV
- TTYS1_DEV.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART1_BAUD);
- up_disableuartint(TTYS1_DEV.priv, NULL);
+ up_disableuartint(g_uart0priv.priv, NULL);
#endif
-#ifdef TTYS2_DEV
- TTYS2_DEV.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART2_BAUD);
- up_disableuartint(TTYS2_DEV.priv, NULL);
+#ifdef CONFIG_LPC17_UART1
+ g_uart1priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART1_BAUD);
+#ifndef CONFIG_UART1_SERIAL_CONSOLE
+ lpc17_uart1config(g_uart1priv.cclkdiv);
#endif
-#ifdef TTYS3_DEV
- TTYS3_DEV.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART3_BAUD);
- up_disableuartint(TTYS3_DEV.priv, NULL);
+ up_disableuartint(g_uart1priv.priv, NULL);
+#endif
+#ifdef CONFIG_LPC17_UART2
+ g_uart2priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART2_BAUD);
+#ifndef CONFIG_UART2_SERIAL_CONSOLE
+ lpc17_uart2config(g_uart2priv.cclkdiv);
+#endif
+ up_disableuartint(g_uart2priv.priv, NULL);
+#endif
+#ifdef CONFIG_LPC17_UART3
+ g_uart3priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART3_BAUD);
+#ifndef CONFIG_UART3_SERIAL_CONSOLE
+ lpc17_uart3config(g_uart3priv.cclkdiv);
+#endif
+ up_disableuartint(g_uart3priv.priv, NULL);
#endif
/* Configuration whichever one is the console */