Blackfin: dynamically update UART speed when initializing
Previously, booting over the UART required the baud rate to be known ahead of time. Using a bit of tricky simple math, we can calculate the new board rate based on the old divisors. Signed-off-by: Mike Frysinger <vapier@gentoo.org> Signed-off-by: Robin Getz <rgetz@blackfin.uclinux.org>
This commit is contained in:
parent
97f265f14f
commit
f790ef6ff1
|
@ -20,7 +20,7 @@
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
|
||||||
__attribute__((always_inline))
|
__attribute__((always_inline))
|
||||||
static inline uint32_t serial_init(void)
|
static inline void serial_init(void)
|
||||||
{
|
{
|
||||||
#ifdef __ADSPBF54x__
|
#ifdef __ADSPBF54x__
|
||||||
# ifdef BFIN_BOOT_UART_USE_RTS
|
# ifdef BFIN_BOOT_UART_USE_RTS
|
||||||
|
@ -61,25 +61,16 @@ static inline uint32_t serial_init(void)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t old_baud;
|
|
||||||
if (BFIN_DEBUG_EARLY_SERIAL || CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART)
|
|
||||||
old_baud = serial_early_get_baud();
|
|
||||||
else
|
|
||||||
old_baud = CONFIG_BAUDRATE;
|
|
||||||
|
|
||||||
if (BFIN_DEBUG_EARLY_SERIAL) {
|
if (BFIN_DEBUG_EARLY_SERIAL) {
|
||||||
|
int ucen = *pUART_GCTL & UCEN;
|
||||||
serial_early_init();
|
serial_early_init();
|
||||||
|
|
||||||
/* If the UART is off, that means we need to program
|
/* If the UART is off, that means we need to program
|
||||||
* the baud rate ourselves initially.
|
* the baud rate ourselves initially.
|
||||||
*/
|
*/
|
||||||
if (!old_baud) {
|
if (ucen != UCEN)
|
||||||
old_baud = CONFIG_BAUDRATE;
|
|
||||||
serial_early_set_baud(CONFIG_BAUDRATE);
|
serial_early_set_baud(CONFIG_BAUDRATE);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return old_baud;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline))
|
__attribute__((always_inline))
|
||||||
|
@ -93,30 +84,6 @@ static inline void serial_deinit(void)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We need to reset the baud rate when we have early debug turned on
|
|
||||||
* or when we are booting over the UART.
|
|
||||||
* XXX: we should fix this to calc the old baud and restore it rather
|
|
||||||
* than hardcoding it via CONFIG_LDR_LOAD_BAUD ... but we have
|
|
||||||
* to figure out how to avoid the division in the baud calc ...
|
|
||||||
*/
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline void serial_reset_baud(uint32_t baud)
|
|
||||||
{
|
|
||||||
if (!BFIN_DEBUG_EARLY_SERIAL && CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_UART)
|
|
||||||
return;
|
|
||||||
|
|
||||||
#ifndef CONFIG_LDR_LOAD_BAUD
|
|
||||||
# define CONFIG_LDR_LOAD_BAUD 115200
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS)
|
|
||||||
serial_early_set_baud(baud);
|
|
||||||
else if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART)
|
|
||||||
serial_early_set_baud(CONFIG_LDR_LOAD_BAUD);
|
|
||||||
else
|
|
||||||
serial_early_set_baud(CONFIG_BAUDRATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
__attribute__((always_inline))
|
||||||
static inline void serial_putc(char c)
|
static inline void serial_putc(char c)
|
||||||
{
|
{
|
||||||
|
@ -239,7 +206,14 @@ static inline void serial_putc(char c)
|
||||||
BOOTROM_CALLED_FUNC_ATTR
|
BOOTROM_CALLED_FUNC_ATTR
|
||||||
void initcode(ADI_BOOT_DATA *bootstruct)
|
void initcode(ADI_BOOT_DATA *bootstruct)
|
||||||
{
|
{
|
||||||
uint32_t old_baud = serial_init();
|
/* Save the clock pieces that are used in baud rate calculation */
|
||||||
|
unsigned int sdivB, divB, vcoB;
|
||||||
|
serial_init();
|
||||||
|
if (BFIN_DEBUG_EARLY_SERIAL || CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) {
|
||||||
|
sdivB = bfin_read_PLL_DIV() & 0xf;
|
||||||
|
vcoB = (bfin_read_PLL_CTL() >> 9) & 0x3f;
|
||||||
|
divB = serial_early_get_div();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_HW_WATCHDOG
|
#ifdef CONFIG_HW_WATCHDOG
|
||||||
# ifndef CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE
|
# ifndef CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE
|
||||||
|
@ -334,8 +308,20 @@ void initcode(ADI_BOOT_DATA *bootstruct)
|
||||||
|
|
||||||
/* Since we've changed the SCLK above, we may need to update
|
/* Since we've changed the SCLK above, we may need to update
|
||||||
* the UART divisors (UART baud rates are based on SCLK).
|
* the UART divisors (UART baud rates are based on SCLK).
|
||||||
|
* Do the division by hand as there are no native instructions
|
||||||
|
* for dividing which means we'd generate a libgcc reference.
|
||||||
*/
|
*/
|
||||||
serial_reset_baud(old_baud);
|
if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) {
|
||||||
|
unsigned int sdivR, vcoR;
|
||||||
|
sdivR = bfin_read_PLL_DIV() & 0xf;
|
||||||
|
vcoR = (bfin_read_PLL_CTL() >> 9) & 0x3f;
|
||||||
|
int dividend = sdivB * divB * vcoR;
|
||||||
|
int divisor = vcoB * sdivR;
|
||||||
|
unsigned int quotient;
|
||||||
|
for (quotient = 0; dividend > 0; ++quotient)
|
||||||
|
dividend -= divisor;
|
||||||
|
serial_early_put_div(quotient - ANOMALY_05000230);
|
||||||
|
}
|
||||||
|
|
||||||
serial_putc('F');
|
serial_putc('F');
|
||||||
|
|
||||||
|
|
|
@ -156,44 +156,8 @@ static inline void serial_early_init(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline))
|
__attribute__((always_inline))
|
||||||
static inline uint32_t serial_early_get_baud(void)
|
static inline void serial_early_put_div(uint16_t divisor)
|
||||||
{
|
{
|
||||||
/* If the UART isnt enabled, then we are booting an LDR
|
|
||||||
* from a non-UART source (so like flash) which means
|
|
||||||
* the baud rate here is meaningless.
|
|
||||||
*/
|
|
||||||
if ((*pUART_GCTL & UCEN) != UCEN)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#if (0) /* See comment for serial_reset_baud() in initcode.c */
|
|
||||||
/* Set DLAB in LCR to Access DLL and DLH */
|
|
||||||
ACCESS_LATCH();
|
|
||||||
SSYNC();
|
|
||||||
|
|
||||||
uint8_t dll = *pUART_DLL;
|
|
||||||
uint8_t dlh = *pUART_DLH;
|
|
||||||
uint16_t divisor = (dlh << 8) | dll;
|
|
||||||
uint32_t baud = get_sclk() / (divisor * 16);
|
|
||||||
|
|
||||||
/* Clear DLAB in LCR to Access THR RBR IER */
|
|
||||||
ACCESS_PORT_IER();
|
|
||||||
SSYNC();
|
|
||||||
|
|
||||||
return baud;
|
|
||||||
#else
|
|
||||||
return CONFIG_BAUDRATE;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline void serial_early_set_baud(uint32_t baud)
|
|
||||||
{
|
|
||||||
/* Translate from baud into divisor in terms of SCLK. The
|
|
||||||
* weird multiplication is to make sure we over sample just
|
|
||||||
* a little rather than under sample the incoming signals.
|
|
||||||
*/
|
|
||||||
uint16_t divisor = (get_sclk() + (baud * 8)) / (baud * 16) - ANOMALY_05000230;
|
|
||||||
|
|
||||||
/* Set DLAB in LCR to Access DLL and DLH */
|
/* Set DLAB in LCR to Access DLL and DLH */
|
||||||
ACCESS_LATCH();
|
ACCESS_LATCH();
|
||||||
SSYNC();
|
SSYNC();
|
||||||
|
@ -208,6 +172,34 @@ static inline void serial_early_set_baud(uint32_t baud)
|
||||||
SSYNC();
|
SSYNC();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__attribute__((always_inline))
|
||||||
|
static inline uint16_t serial_early_get_div(void)
|
||||||
|
{
|
||||||
|
/* Set DLAB in LCR to Access DLL and DLH */
|
||||||
|
ACCESS_LATCH();
|
||||||
|
SSYNC();
|
||||||
|
|
||||||
|
uint8_t dll = *pUART_DLL;
|
||||||
|
uint8_t dlh = *pUART_DLH;
|
||||||
|
uint16_t divisor = (dlh << 8) | dll;
|
||||||
|
|
||||||
|
/* Clear DLAB in LCR to Access THR RBR IER */
|
||||||
|
ACCESS_PORT_IER();
|
||||||
|
SSYNC();
|
||||||
|
|
||||||
|
return divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((always_inline))
|
||||||
|
static inline void serial_early_set_baud(uint32_t baud)
|
||||||
|
{
|
||||||
|
/* Translate from baud into divisor in terms of SCLK. The
|
||||||
|
* weird multiplication is to make sure we over sample just
|
||||||
|
* a little rather than under sample the incoming signals.
|
||||||
|
*/
|
||||||
|
serial_early_put_div((get_sclk() + (baud * 8)) / (baud * 16) - ANOMALY_05000230);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef BFIN_IN_INITCODE
|
#ifndef BFIN_IN_INITCODE
|
||||||
__attribute__((always_inline))
|
__attribute__((always_inline))
|
||||||
static inline void serial_early_puts(const char *s)
|
static inline void serial_early_puts(const char *s)
|
||||||
|
@ -235,32 +227,6 @@ static inline void serial_early_puts(const char *s)
|
||||||
#endif
|
#endif
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
/* Recursively expand calls to _serial_putc for every byte
|
|
||||||
* passed to us. Append a newline when we're all done.
|
|
||||||
*/
|
|
||||||
.macro _serial_early_putc byte:req morebytes:vararg
|
|
||||||
#ifdef CONFIG_DEBUG_EARLY_SERIAL
|
|
||||||
R0 = \byte;
|
|
||||||
call _serial_putc;
|
|
||||||
.ifnb \morebytes
|
|
||||||
_serial_early_putc \morebytes
|
|
||||||
.else
|
|
||||||
.if (\byte != '\n')
|
|
||||||
_serial_early_putc '\n'
|
|
||||||
.endif
|
|
||||||
.endif
|
|
||||||
#endif
|
|
||||||
.endm
|
|
||||||
|
|
||||||
/* Wrapper around recurisve _serial_early_putc macro which
|
|
||||||
* simply prepends the string "Early: "
|
|
||||||
*/
|
|
||||||
.macro serial_early_putc byte:req morebytes:vararg
|
|
||||||
#ifdef CONFIG_DEBUG_EARLY_SERIAL
|
|
||||||
_serial_early_putc 'E', 'a', 'r', 'l', 'y', ':', ' ', \byte, \morebytes
|
|
||||||
#endif
|
|
||||||
.endm
|
|
||||||
|
|
||||||
/* Since we embed the string right into our .text section, we need
|
/* Since we embed the string right into our .text section, we need
|
||||||
* to find its address. We do this by getting our PC and adding 2
|
* to find its address. We do this by getting our PC and adding 2
|
||||||
* bytes (which is the length of the jump instruction). Then we
|
* bytes (which is the length of the jump instruction). Then we
|
||||||
|
|
Loading…
Reference in New Issue