Skip to content
Snippets Groups Projects
serial_stm32.c 3.63 KiB
Newer Older
  • Learn to ignore specific revisions
  • rev13@wp.pl's avatar
    rev13@wp.pl committed
    /*
     * (C) Copyright 2015
     * Kamil Lulko, <rev13@wp.pl>
     *
     * SPDX-License-Identifier:	GPL-2.0+
     */
    
    #include <common.h>
    #include <asm/io.h>
    #include <serial.h>
    #include <asm/arch/stm32.h>
    
    
    /*
     * Set up the usart port
     */
    #if (CONFIG_STM32_USART >= 1) && (CONFIG_STM32_USART <= 6)
    #define USART_PORT	(CONFIG_STM32_USART - 1)
    #else
    #define USART_PORT	0
    #endif
    /*
     * Set up the usart base address
     *
     * --STM32_USARTD_BASE means default setting
     */
    
    rev13@wp.pl's avatar
    rev13@wp.pl committed
    #define STM32_USART1_BASE	(STM32_APB2PERIPH_BASE + 0x1000)
    
    #define STM32_USART2_BASE	(STM32_APB1PERIPH_BASE + 0x4400)
    #define STM32_USART3_BASE	(STM32_APB1PERIPH_BASE + 0x4800)
    #define STM32_USART6_BASE	(STM32_APB2PERIPH_BASE + 0x1400)
    #define STM32_USARTD_BASE	STM32_USART1_BASE
    /*
     * RCC USART specific definitions
     *
     * --RCC_ENR_USARTDEN means default setting
     */
    #define RCC_ENR_USART1EN	(1 << 4)
    #define RCC_ENR_USART2EN	(1 << 17)
    #define RCC_ENR_USART3EN	(1 << 18)
    #define RCC_ENR_USART6EN	(1 <<  5)
    #define RCC_ENR_USARTDEN	RCC_ENR_USART1EN
    
    rev13@wp.pl's avatar
    rev13@wp.pl committed
    
    struct stm32_serial {
    	u32 sr;
    	u32 dr;
    	u32 brr;
    	u32 cr1;
    	u32 cr2;
    	u32 cr3;
    	u32 gtpr;
    };
    
    #define USART_CR1_RE		(1 << 2)
    #define USART_CR1_TE		(1 << 3)
    #define USART_CR1_UE		(1 << 13)
    
    #define USART_SR_FLAG_RXNE	(1 << 5)
    #define USART_SR_FLAG_TXE	(1 << 7)
    
    #define USART_BRR_F_MASK	0xF
    #define USART_BRR_M_SHIFT	4
    #define USART_BRR_M_MASK	0xFFF0
    
    DECLARE_GLOBAL_DATA_PTR;
    
    
    static const unsigned long usart_base[] = {
    	STM32_USART1_BASE,
    	STM32_USART2_BASE,
    	STM32_USART3_BASE,
    	STM32_USARTD_BASE,
    	STM32_USARTD_BASE,
    	STM32_USART6_BASE
    };
    
    static const unsigned long rcc_enr_en[] = {
    	RCC_ENR_USART1EN,
    	RCC_ENR_USART2EN,
    	RCC_ENR_USART3EN,
    	RCC_ENR_USARTDEN,
    	RCC_ENR_USARTDEN,
    	RCC_ENR_USART6EN
    };
    
    
    rev13@wp.pl's avatar
    rev13@wp.pl committed
    static void stm32_serial_setbrg(void)
    {
    	serial_init();
    }
    
    static int stm32_serial_init(void)
    {
    
    	struct stm32_serial *usart =
    		(struct stm32_serial *)usart_base[USART_PORT];
    
    rev13@wp.pl's avatar
    rev13@wp.pl committed
    	u32 clock, int_div, frac_div, tmp;
    
    
    	if ((usart_base[USART_PORT] & STM32_BUS_MASK) ==
    			STM32_APB1PERIPH_BASE) {
    		setbits_le32(&STM32_RCC->apb1enr, rcc_enr_en[USART_PORT]);
    
    rev13@wp.pl's avatar
    rev13@wp.pl committed
    		clock = clock_get(CLOCK_APB1);
    
    	} else if ((usart_base[USART_PORT] & STM32_BUS_MASK) ==
    			STM32_APB2PERIPH_BASE) {
    		setbits_le32(&STM32_RCC->apb2enr, rcc_enr_en[USART_PORT]);
    
    rev13@wp.pl's avatar
    rev13@wp.pl committed
    		clock = clock_get(CLOCK_APB2);
    	} else {
    		return -1;
    	}
    
    	int_div = (25 * clock) / (4 * gd->baudrate);
    	tmp = ((int_div / 100) << USART_BRR_M_SHIFT) & USART_BRR_M_MASK;
    	frac_div = int_div - (100 * (tmp >> USART_BRR_M_SHIFT));
    	tmp |= (((frac_div * 16) + 50) / 100) & USART_BRR_F_MASK;
    
    	writel(tmp, &usart->brr);
    	setbits_le32(&usart->cr1, USART_CR1_RE | USART_CR1_TE | USART_CR1_UE);
    
    	return 0;
    }
    
    static int stm32_serial_getc(void)
    {
    
    	struct stm32_serial *usart =
    		(struct stm32_serial *)usart_base[USART_PORT];
    
    rev13@wp.pl's avatar
    rev13@wp.pl committed
    	while ((readl(&usart->sr) & USART_SR_FLAG_RXNE) == 0)
    		;
    	return readl(&usart->dr);
    }
    
    static void stm32_serial_putc(const char c)
    {
    
    	struct stm32_serial *usart =
    		(struct stm32_serial *)usart_base[USART_PORT];
    
    kunhuahuang's avatar
    kunhuahuang committed
    
    	if (c == '\n')
    		stm32_serial_putc('\r');
    
    
    rev13@wp.pl's avatar
    rev13@wp.pl committed
    	while ((readl(&usart->sr) & USART_SR_FLAG_TXE) == 0)
    		;
    	writel(c, &usart->dr);
    }
    
    static int stm32_serial_tstc(void)
    {
    
    	struct stm32_serial *usart =
    		(struct stm32_serial *)usart_base[USART_PORT];
    
    rev13@wp.pl's avatar
    rev13@wp.pl committed
    	u8 ret;
    
    	ret = readl(&usart->sr) & USART_SR_FLAG_RXNE;
    	return ret;
    }
    
    static struct serial_device stm32_serial_drv = {
    	.name	= "stm32_serial",
    	.start	= stm32_serial_init,
    	.stop	= NULL,
    	.setbrg	= stm32_serial_setbrg,
    	.putc	= stm32_serial_putc,
    	.puts	= default_serial_puts,
    	.getc	= stm32_serial_getc,
    	.tstc	= stm32_serial_tstc,
    };
    
    void stm32_serial_initialize(void)
    {
    	serial_register(&stm32_serial_drv);
    }
    
    __weak struct serial_device *default_serial_console(void)
    {
    	return &stm32_serial_drv;
    }