Corrections for LPC178x UART baud calculation

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5698 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2013-03-02 17:01:08 +00:00
parent 5f9f94502d
commit 5e6c1c9fa4
2 changed files with 147 additions and 76 deletions

View File

@ -139,82 +139,95 @@
* BAUD = PCLK / (16 * DL), or * BAUD = PCLK / (16 * DL), or
* DL = PCLK / BAUD / 16 * DL = PCLK / BAUD / 16
* *
* Where: * Where for the LPC176x the PCLK is determined by the UART-specific divisor in
* PCLKSEL0 or PCLKSEL1:
* *
* PCLK = CCLK / divisor * PCLK = CCLK / divisor
* *
* And for the LPC178x, the PCLK is determined by the global divisor setting in
* the PLKSEL register.
*
* Ignoring the fractional divider for now. (If you want to extend this driver * Ignoring the fractional divider for now. (If you want to extend this driver
* to support the fractional divider, see lpc43xx_uart.c. The LPC43xx uses * to support the fractional divider, see lpc43xx_uart.c. The LPC43xx uses
* the same peripheral and that logic could easily leveraged here). * the same peripheral and that logic could easily leveraged here).
*
* 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 CONSOLE_BAUD < (LPC17_CCLK / 16 / UART_MINDL) #ifdef LPC178x
# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK /* Use the global PCLK frequency */
# define CONSOLE_NUMERATOR (LPC17_CCLK)
/* Check divisor == 2. This works if: # define CONSOLE_NUMERATOR BOARD_PCLK_FREQUENCY
*
* 2 * CCLK / BAUD / 16 < 0xffff, or
* BAUD > CCLK / 0xffff / 8
*
* And
*
* 2 * CCLK / BAUD / 16 >= MinDL, or
* BAUD <= CCLK / 8 / MinDL
*/
#elif CONSOLE_BAUD < (LPC17_CCLK / 8 / UART_MINDL) #else
# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK2 /* Calculate and optimal PCLKSEL0/1 divisor.
# define CONSOLE_NUMERATOR (LPC17_CCLK / 2) * First, 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
*/
/* Check divisor == 4. This works if: # if CONSOLE_BAUD < (LPC17_CCLK / 16 / UART_MINDL)
* # define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK
* 4 * CCLK / BAUD / 16 < 0xffff, or # define CONSOLE_NUMERATOR (LPC17_CCLK)
* BAUD > CCLK / 0xffff / 4
*
* And
*
* 4 * CCLK / BAUD / 16 >= MinDL, or
* BAUD <= CCLK / 4 / MinDL
*/
#elif CONSOLE_BAUD < (LPC17_CCLK / 4 / UART_MINDL) /* Check divisor == 2. This works if:
# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK4 *
# define CONSOLE_NUMERATOR (LPC17_CCLK / 4) * 2 * CCLK / BAUD / 16 < 0xffff, or
* BAUD > CCLK / 0xffff / 8
*
* And
*
* 2 * CCLK / BAUD / 16 >= MinDL, or
* BAUD <= CCLK / 8 / MinDL
*/
/* Check divisor == 8. This works if: # elif CONSOLE_BAUD < (LPC17_CCLK / 8 / UART_MINDL)
* # define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK2
* 8 * CCLK / BAUD / 16 < 0xffff, or # define CONSOLE_NUMERATOR (LPC17_CCLK / 2)
* BAUD > CCLK / 0xffff / 2
*
* And
*
* 8 * CCLK / BAUD / 16 >= MinDL, or
* BAUD <= CCLK / 2 / MinDL
*/
#else /* if CONSOLE_BAUD < (LPC17_CCLK / 2 / UART_MINDL) */ /* Check divisor == 4. This works if:
# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK8 *
# define CONSOLE_NUMERATOR (LPC17_CCLK / 8) * 4 * CCLK / BAUD / 16 < 0xffff, or
#endif * BAUD > CCLK / 0xffff / 4
*
* And
*
* 4 * CCLK / BAUD / 16 >= MinDL, or
* BAUD <= CCLK / 4 / MinDL
*/
# elif CONSOLE_BAUD < (LPC17_CCLK / 4 / UART_MINDL)
# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK4
# define CONSOLE_NUMERATOR (LPC17_CCLK / 4)
/* 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 CONSOLE_BAUD < (LPC17_CCLK / 2 / UART_MINDL) */
# define CONSOLE_CCLKDIV SYSCON_PCLKSEL_CCLK8
# define CONSOLE_NUMERATOR (LPC17_CCLK / 8)
# endif
#endif /* LPC178x */
/* Then this is the value to use for the DLM and DLL registers */ /* Then this is the value to use for the DLM and DLL registers */
#define CONSOLE_DL (CONSOLE_NUMERATOR / (CONSOLE_BAUD << 4)) #define CONSOLE_DL (CONSOLE_NUMERATOR / (CONSOLE_BAUD << 4))
/************************************************************************** /**************************************************************************
* Private Types * Private Types

View File

@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
* arch/arm/src/lpc17xx/lpc17_serial.c * arch/arm/src/lpc17xx/lpc17_serial.c
* *
* Copyright (C) 2010-2012 Gregory Nutt. All rights reserved. * Copyright (C) 2010-2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org> * Author: Gregory Nutt <gnutt@nuttx.org>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -91,7 +91,9 @@ struct up_dev_s
uint8_t irq; /* IRQ associated with this UART */ uint8_t irq; /* IRQ associated with this UART */
uint8_t parity; /* 0=none, 1=odd, 2=even */ uint8_t parity; /* 0=none, 1=odd, 2=even */
uint8_t bits; /* Number of bits (7 or 8) */ uint8_t bits; /* Number of bits (7 or 8) */
#ifdef LPC176x
uint8_t cclkdiv; /* Divisor needed to get PCLK from CCLK */ uint8_t cclkdiv; /* Divisor needed to get PCLK from CCLK */
#endif
bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */ bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */
}; };
@ -554,7 +556,17 @@ static inline void up_enablebreaks(struct up_dev_s *priv, bool enable)
* PCLK = CCLK / divisor * PCLK = CCLK / divisor
* BAUD = PCLK / (16 * DL) * BAUD = PCLK / (16 * DL)
* *
* Ignoring the fractional divider for now. * Ignoring the fractional divider for now. (If you want to extend this driver
* to support the fractional divider, see lpc43xx_uart.c. The LPC43xx uses
* the same peripheral and that logic could easily leveraged here).
*
* For the LPC176x the PCLK is determined by the UART-specific divisor in
* PCLKSEL0 or PCLKSEL1:
*
* PCLK = CCLK / divisor
*
* For the LPC178x, the PCLK is determined by the global divisor setting in
* the PLKSEL register (and, in that case, this function is not needed).
* *
* NOTE: This is an inline function. If a typical optimization level is used and * 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 * a constant is provided for the desired frequency, then most of the following
@ -562,6 +574,7 @@ static inline void up_enablebreaks(struct up_dev_s *priv, bool enable)
* *
************************************************************************************/ ************************************************************************************/
#ifdef LPC176x
static inline uint32_t lpc17_uartcclkdiv(uint32_t baud) static inline uint32_t lpc17_uartcclkdiv(uint32_t baud)
{ {
/* Ignoring the fractional divider, the BAUD is given by: /* Ignoring the fractional divider, the BAUD is given by:
@ -569,11 +582,17 @@ static inline uint32_t lpc17_uartcclkdiv(uint32_t baud)
* BAUD = PCLK / (16 * DL), or * BAUD = PCLK / (16 * DL), or
* DL = PCLK / BAUD / 16 * DL = PCLK / BAUD / 16
* *
* Where: * Where for the LPC176x the PCLK is determined by the UART-specific divisor in
* PCLKSEL0 or PCLKSEL1:
* *
* PCLK = CCLK / divisor. * PCLK = CCLK / divisor
* *
* Check divisor == 1. This works if the upper limit is met * And for the LPC178x, the PCLK is determined by the global divisor setting in
* the PLKSEL register (and, in that case, this function is not needed).
*/
/* Calculate and optimal PCLKSEL0/1 divisor.
* First, check divisor == 1. This works if the upper limit is met:
* *
* DL < 0xffff, or * DL < 0xffff, or
* PCLK / BAUD / 16 < 0xffff, or * PCLK / BAUD / 16 < 0xffff, or
@ -641,6 +660,7 @@ static inline uint32_t lpc17_uartcclkdiv(uint32_t baud)
return SYSCON_PCLKSEL_CCLK8; return SYSCON_PCLKSEL_CCLK8;
} }
} }
#endif /* LPC176x */
/************************************************************************************ /************************************************************************************
* Name: lpc17_uart0config, uart1config, uart2config, and uart3config * Name: lpc17_uart0config, uart1config, uart2config, and uart3config
@ -661,7 +681,7 @@ static inline uint32_t lpc17_uartcclkdiv(uint32_t baud)
************************************************************************************/ ************************************************************************************/
#ifdef CONFIG_LPC17_UART0 #ifdef CONFIG_LPC17_UART0
static inline void lpc17_uart0config(uint32_t clkdiv) static inline void lpc17_uart0config(void)
{ {
uint32_t regval; uint32_t regval;
irqstate_t flags; irqstate_t flags;
@ -675,10 +695,12 @@ static inline void lpc17_uart0config(uint32_t clkdiv)
/* Step 2: Enable clocking on UART */ /* Step 2: Enable clocking on UART */
#ifdef LPC176x
regval = getreg32(LPC17_SYSCON_PCLKSEL0); regval = getreg32(LPC17_SYSCON_PCLKSEL0);
regval &= ~SYSCON_PCLKSEL0_UART0_MASK; regval &= ~SYSCON_PCLKSEL0_UART0_MASK;
regval |= (clkdiv << SYSCON_PCLKSEL0_UART0_SHIFT); regval |= ((uint32_t)g_uart0priv.cclkdiv << SYSCON_PCLKSEL0_UART0_SHIFT);
putreg32(regval, LPC17_SYSCON_PCLKSEL0); putreg32(regval, LPC17_SYSCON_PCLKSEL0);
#endif
/* Step 3: Configure I/O pins */ /* Step 3: Configure I/O pins */
@ -689,7 +711,7 @@ static inline void lpc17_uart0config(uint32_t clkdiv)
#endif #endif
#ifdef CONFIG_LPC17_UART1 #ifdef CONFIG_LPC17_UART1
static inline void lpc17_uart1config(uint32_t clkdiv) static inline void lpc17_uart1config(void)
{ {
uint32_t regval; uint32_t regval;
irqstate_t flags; irqstate_t flags;
@ -703,10 +725,12 @@ static inline void lpc17_uart1config(uint32_t clkdiv)
/* Step 2: Enable clocking on UART */ /* Step 2: Enable clocking on UART */
#ifdef LPC176x
regval = getreg32(LPC17_SYSCON_PCLKSEL0); regval = getreg32(LPC17_SYSCON_PCLKSEL0);
regval &= ~SYSCON_PCLKSEL0_UART1_MASK; regval &= ~SYSCON_PCLKSEL0_UART1_MASK;
regval |= (clkdiv << SYSCON_PCLKSEL0_UART1_SHIFT); regval |= ((uint32_t)g_uart1priv.cclkdiv << SYSCON_PCLKSEL0_UART1_SHIFT);
putreg32(regval, LPC17_SYSCON_PCLKSEL0); putreg32(regval, LPC17_SYSCON_PCLKSEL0);
#endif
/* Step 3: Configure I/O pins */ /* Step 3: Configure I/O pins */
@ -727,7 +751,7 @@ static inline void lpc17_uart1config(uint32_t clkdiv)
#endif #endif
#ifdef CONFIG_LPC17_UART2 #ifdef CONFIG_LPC17_UART2
static inline void lpc17_uart2config(uint32_t clkdiv) static inline void lpc17_uart2config(void)
{ {
uint32_t regval; uint32_t regval;
irqstate_t flags; irqstate_t flags;
@ -741,10 +765,12 @@ static inline void lpc17_uart2config(uint32_t clkdiv)
/* Step 2: Enable clocking on UART */ /* Step 2: Enable clocking on UART */
#ifdef LPC176x
regval = getreg32(LPC17_SYSCON_PCLKSEL1); regval = getreg32(LPC17_SYSCON_PCLKSEL1);
regval &= ~SYSCON_PCLKSEL1_UART2_MASK; regval &= ~SYSCON_PCLKSEL1_UART2_MASK;
regval |= (clkdiv << SYSCON_PCLKSEL1_UART2_SHIFT); regval |= ((uint32_t)g_uart2priv.cclkdiv << SYSCON_PCLKSEL1_UART2_SHIFT);
putreg32(regval, LPC17_SYSCON_PCLKSEL1); putreg32(regval, LPC17_SYSCON_PCLKSEL1);
#endif
/* Step 3: Configure I/O pins */ /* Step 3: Configure I/O pins */
@ -755,7 +781,7 @@ static inline void lpc17_uart2config(uint32_t clkdiv)
#endif #endif
#ifdef CONFIG_LPC17_UART3 #ifdef CONFIG_LPC17_UART3
static inline void lpc17_uart3config(uint32_t clkdiv) static inline void lpc17_uart3config(void)
{ {
uint32_t regval; uint32_t regval;
irqstate_t flags; irqstate_t flags;
@ -769,10 +795,12 @@ static inline void lpc17_uart3config(uint32_t clkdiv)
/* Step 2: Enable clocking on UART */ /* Step 2: Enable clocking on UART */
#ifdef LPC176x
regval = getreg32(LPC17_SYSCON_PCLKSEL1); regval = getreg32(LPC17_SYSCON_PCLKSEL1);
regval &= ~SYSCON_PCLKSEL1_UART3_MASK; regval &= ~SYSCON_PCLKSEL1_UART3_MASK;
regval |= (clkdiv << SYSCON_PCLKSEL1_UART3_SHIFT); regval |= ((uint32_t)g_uart3priv.cclkdiv << SYSCON_PCLKSEL1_UART3_SHIFT);
putreg32(regval, LPC17_SYSCON_PCLKSEL1); putreg32(regval, LPC17_SYSCON_PCLKSEL1);
#endif
/* Step 3: Configure I/O pins */ /* Step 3: Configure I/O pins */
@ -797,6 +825,7 @@ static inline void lpc17_uart3config(uint32_t clkdiv)
* *
************************************************************************************/ ************************************************************************************/
#ifdef LPC176x
static inline uint32_t lpc17_uartdl(uint32_t baud, uint8_t divcode) static inline uint32_t lpc17_uartdl(uint32_t baud, uint8_t divcode)
{ {
uint32_t num; uint32_t num;
@ -823,6 +852,12 @@ static inline uint32_t lpc17_uartdl(uint32_t baud, uint8_t divcode)
return num / (baud << 4); return num / (baud << 4);
} }
#else
static inline uint32_t lpc17_uartdl(uint32_t baud)
{
return (uint32_t)BOARD_PCLK / (baud << 4);
}
#endif
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
@ -889,7 +924,11 @@ static int up_setup(struct uart_dev_s *dev)
/* Set the BAUD divisor */ /* Set the BAUD divisor */
#ifdef LPC176x
dl = lpc17_uartdl(priv->baud, priv->cclkdiv); dl = lpc17_uartdl(priv->baud, priv->cclkdiv);
#else
dl = lpc17_uartdl(priv->baud);
#endif
up_serialout(priv, LPC17_UART_DLM_OFFSET, dl >> 8); up_serialout(priv, LPC17_UART_DLM_OFFSET, dl >> 8);
up_serialout(priv, LPC17_UART_DLL_OFFSET, dl & 0xff); up_serialout(priv, LPC17_UART_DLL_OFFSET, dl & 0xff);
@ -1203,14 +1242,24 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg)
priv->baud = cfgetispeed(termiosp); priv->baud = cfgetispeed(termiosp);
/* Re-calculate the optimal CCLK divisor for the new baud */
#ifdef LPC176x
priv->cclkdiv = lpc17_uartcclkdiv(priv->baud);
#endif
/* DLAB open latch */ /* DLAB open latch */
/* REVISIT: Shouldn't we just call up_setup() to do all of the following? */
lcr = getreg32(priv->uartbase + LPC17_UART_LCR_OFFSET); lcr = getreg32(priv->uartbase + LPC17_UART_LCR_OFFSET);
up_serialout(priv, LPC17_UART_LCR_OFFSET, (lcr | UART_LCR_DLAB)); up_serialout(priv, LPC17_UART_LCR_OFFSET, (lcr | UART_LCR_DLAB));
/* Set the BAUD divisor */ /* Set the BAUD divisor */
#ifdef LPC176x
dl = lpc17_uartdl(priv->baud, priv->cclkdiv); dl = lpc17_uartdl(priv->baud, priv->cclkdiv);
#else
dl = lpc17_uartdl(priv->baud);
#endif
up_serialout(priv, LPC17_UART_DLM_OFFSET, dl >> 8); up_serialout(priv, LPC17_UART_DLM_OFFSET, dl >> 8);
up_serialout(priv, LPC17_UART_DLL_OFFSET, dl & 0xff); up_serialout(priv, LPC17_UART_DLL_OFFSET, dl & 0xff);
@ -1388,33 +1437,42 @@ void up_earlyserialinit(void)
/* Configure all UARTs (except the CONSOLE UART) and disable interrupts */ /* Configure all UARTs (except the CONSOLE UART) and disable interrupts */
#ifdef CONFIG_LPC17_UART0 #ifdef CONFIG_LPC17_UART0
#ifdef LPC176x
g_uart0priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART0_BAUD); g_uart0priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART0_BAUD);
#endif
#ifndef CONFIG_UART0_SERIAL_CONSOLE #ifndef CONFIG_UART0_SERIAL_CONSOLE
lpc17_uart0config(g_uart0priv.cclkdiv); lpc17_uart0config();
#endif #endif
up_disableuartint(&g_uart0priv, NULL); up_disableuartint(&g_uart0priv, NULL);
#endif #endif
#ifdef CONFIG_LPC17_UART1 #ifdef CONFIG_LPC17_UART1
#ifdef LPC176x
g_uart1priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART1_BAUD); g_uart1priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART1_BAUD);
#endif
#ifndef CONFIG_UART1_SERIAL_CONSOLE #ifndef CONFIG_UART1_SERIAL_CONSOLE
lpc17_uart1config(g_uart1priv.cclkdiv); lpc17_uart1config();
#else
#endif #endif
up_disableuartint(&g_uart1priv, NULL); up_disableuartint(&g_uart1priv, NULL);
#endif #endif
#ifdef CONFIG_LPC17_UART2 #ifdef CONFIG_LPC17_UART2
#ifdef LPC176x
g_uart2priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART2_BAUD); g_uart2priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART2_BAUD);
#endif
#ifndef CONFIG_UART2_SERIAL_CONSOLE #ifndef CONFIG_UART2_SERIAL_CONSOLE
lpc17_uart2config(g_uart2priv.cclkdiv); lpc17_uart2config();
#endif #endif
up_disableuartint(&g_uart2priv, NULL); up_disableuartint(&g_uart2priv, NULL);
#endif #endif
#ifdef CONFIG_LPC17_UART3 #ifdef CONFIG_LPC17_UART3
#ifdef LPC176x
g_uart3priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART3_BAUD); g_uart3priv.cclkdiv = lpc17_uartcclkdiv(CONFIG_UART3_BAUD);
#endif
#ifndef CONFIG_UART3_SERIAL_CONSOLE #ifndef CONFIG_UART3_SERIAL_CONSOLE
lpc17_uart3config(g_uart3priv.cclkdiv); lpc17_uart3config();
#endif #endif
up_disableuartint(&g_uart3priv, NULL); up_disableuartint(&g_uart3priv, NULL);
#endif #endif