summaryrefslogtreecommitdiffstats
path: root/drivers/serial/serial_stm32.c
blob: 8b2830b946a83b9bf3675c4fef03d2c8fc815882 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*
 * (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
 */
#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

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
};

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];
	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]);
		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]);
		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];
	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];

	if (c == '\n')
		stm32_serial_putc('\r');

	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];
	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;
}
OpenPOWER on IntegriCloud