]>
Commit | Line | Data |
---|---|---|
f93ae788 WD |
1 | /* |
2 | * Copyright (C) 2004-2006 Atmel Corporation | |
3 | * | |
125637c5 AB |
4 | * Modified to support C structur SoC access by |
5 | * Andreas Bießmann <[email protected]> | |
6 | * | |
1a459660 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
f93ae788 WD |
8 | */ |
9 | #include <common.h> | |
0f65f48b SG |
10 | #include <dm.h> |
11 | #include <errno.h> | |
843a2654 | 12 | #include <watchdog.h> |
cfba4573 MV |
13 | #include <serial.h> |
14 | #include <linux/compiler.h> | |
f93ae788 | 15 | |
f93ae788 | 16 | #include <asm/io.h> |
0f65f48b SG |
17 | #ifdef CONFIG_DM_SERIAL |
18 | #include <asm/arch/atmel_serial.h> | |
19 | #endif | |
df548d3c | 20 | #include <asm/arch/clk.h> |
329f0f52 | 21 | #include <asm/arch/hardware.h> |
f93ae788 WD |
22 | |
23 | #include "atmel_usart.h" | |
24 | ||
25 | DECLARE_GLOBAL_DATA_PTR; | |
26 | ||
62137fc0 SG |
27 | static void atmel_serial_setbrg_internal(atmel_usart3_t *usart, int id, |
28 | int baudrate) | |
f93ae788 WD |
29 | { |
30 | unsigned long divisor; | |
31 | unsigned long usart_hz; | |
32 | ||
33 | /* | |
34 | * Master Clock | |
35 | * Baud Rate = -------------- | |
36 | * 16 * CD | |
37 | */ | |
62137fc0 SG |
38 | usart_hz = get_usart_clk_rate(id); |
39 | divisor = (usart_hz / 16 + baudrate / 2) / baudrate; | |
125637c5 | 40 | writel(USART3_BF(CD, divisor), &usart->brgr); |
f93ae788 WD |
41 | } |
42 | ||
62137fc0 | 43 | static void atmel_serial_init_internal(atmel_usart3_t *usart) |
f93ae788 | 44 | { |
1f4faedd XH |
45 | /* |
46 | * Just in case: drain transmitter register | |
47 | * 1000us is enough for baudrate >= 9600 | |
48 | */ | |
49 | if (!(readl(&usart->csr) & USART3_BIT(TXEMPTY))) | |
50 | __udelay(1000); | |
51 | ||
125637c5 | 52 | writel(USART3_BIT(RSTRX) | USART3_BIT(RSTTX), &usart->cr); |
62137fc0 | 53 | } |
f93ae788 | 54 | |
62137fc0 SG |
55 | static void atmel_serial_activate(atmel_usart3_t *usart) |
56 | { | |
125637c5 | 57 | writel((USART3_BF(USART_MODE, USART3_USART_MODE_NORMAL) |
df548d3c HS |
58 | | USART3_BF(USCLKS, USART3_USCLKS_MCK) |
59 | | USART3_BF(CHRL, USART3_CHRL_8) | |
60 | | USART3_BF(PAR, USART3_PAR_NONE) | |
125637c5 AB |
61 | | USART3_BF(NBSTOP, USART3_NBSTOP_1)), |
62 | &usart->mr); | |
1f4faedd XH |
63 | writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr); |
64 | /* 100us is enough for the new settings to be settled */ | |
65 | __udelay(100); | |
62137fc0 SG |
66 | } |
67 | ||
0f65f48b | 68 | #ifndef CONFIG_DM_SERIAL |
62137fc0 SG |
69 | static void atmel_serial_setbrg(void) |
70 | { | |
71 | atmel_serial_setbrg_internal((atmel_usart3_t *)CONFIG_USART_BASE, | |
72 | CONFIG_USART_ID, gd->baudrate); | |
73 | } | |
74 | ||
75 | static int atmel_serial_init(void) | |
76 | { | |
77 | atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; | |
78 | ||
79 | atmel_serial_init_internal(usart); | |
80 | serial_setbrg(); | |
81 | atmel_serial_activate(usart); | |
f93ae788 WD |
82 | |
83 | return 0; | |
84 | } | |
85 | ||
cfba4573 | 86 | static void atmel_serial_putc(char c) |
f93ae788 | 87 | { |
329f0f52 | 88 | atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; |
125637c5 | 89 | |
f93ae788 WD |
90 | if (c == '\n') |
91 | serial_putc('\r'); | |
92 | ||
125637c5 AB |
93 | while (!(readl(&usart->csr) & USART3_BIT(TXRDY))); |
94 | writel(c, &usart->thr); | |
f93ae788 WD |
95 | } |
96 | ||
cfba4573 | 97 | static int atmel_serial_getc(void) |
f93ae788 | 98 | { |
329f0f52 | 99 | atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; |
125637c5 AB |
100 | |
101 | while (!(readl(&usart->csr) & USART3_BIT(RXRDY))) | |
843a2654 | 102 | WATCHDOG_RESET(); |
125637c5 | 103 | return readl(&usart->rhr); |
f93ae788 WD |
104 | } |
105 | ||
cfba4573 | 106 | static int atmel_serial_tstc(void) |
f93ae788 | 107 | { |
329f0f52 | 108 | atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; |
125637c5 | 109 | return (readl(&usart->csr) & USART3_BIT(RXRDY)) != 0; |
f93ae788 | 110 | } |
cfba4573 | 111 | |
cfba4573 MV |
112 | static struct serial_device atmel_serial_drv = { |
113 | .name = "atmel_serial", | |
114 | .start = atmel_serial_init, | |
115 | .stop = NULL, | |
116 | .setbrg = atmel_serial_setbrg, | |
117 | .putc = atmel_serial_putc, | |
ec3fd689 | 118 | .puts = default_serial_puts, |
cfba4573 MV |
119 | .getc = atmel_serial_getc, |
120 | .tstc = atmel_serial_tstc, | |
121 | }; | |
122 | ||
123 | void atmel_serial_initialize(void) | |
124 | { | |
125 | serial_register(&atmel_serial_drv); | |
126 | } | |
127 | ||
128 | __weak struct serial_device *default_serial_console(void) | |
129 | { | |
130 | return &atmel_serial_drv; | |
131 | } | |
0f65f48b SG |
132 | #endif |
133 | ||
134 | #ifdef CONFIG_DM_SERIAL | |
135 | ||
136 | struct atmel_serial_priv { | |
137 | atmel_usart3_t *usart; | |
138 | }; | |
139 | ||
140 | int atmel_serial_setbrg(struct udevice *dev, int baudrate) | |
141 | { | |
142 | struct atmel_serial_priv *priv = dev_get_priv(dev); | |
143 | ||
144 | atmel_serial_setbrg_internal(priv->usart, 0 /* ignored */, baudrate); | |
145 | atmel_serial_activate(priv->usart); | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
150 | static int atmel_serial_getc(struct udevice *dev) | |
151 | { | |
152 | struct atmel_serial_priv *priv = dev_get_priv(dev); | |
153 | ||
154 | if (!(readl(&priv->usart->csr) & USART3_BIT(RXRDY))) | |
155 | return -EAGAIN; | |
156 | ||
157 | return readl(&priv->usart->rhr); | |
158 | } | |
159 | ||
160 | static int atmel_serial_putc(struct udevice *dev, const char ch) | |
161 | { | |
162 | struct atmel_serial_priv *priv = dev_get_priv(dev); | |
163 | ||
164 | if (!(readl(&priv->usart->csr) & USART3_BIT(TXRDY))) | |
165 | return -EAGAIN; | |
166 | ||
167 | writel(ch, &priv->usart->thr); | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
172 | static int atmel_serial_pending(struct udevice *dev, bool input) | |
173 | { | |
174 | struct atmel_serial_priv *priv = dev_get_priv(dev); | |
175 | uint32_t csr = readl(&priv->usart->csr); | |
176 | ||
177 | if (input) | |
178 | return csr & USART3_BIT(RXRDY) ? 1 : 0; | |
179 | else | |
180 | return csr & USART3_BIT(TXEMPTY) ? 0 : 1; | |
181 | } | |
182 | ||
183 | static const struct dm_serial_ops atmel_serial_ops = { | |
184 | .putc = atmel_serial_putc, | |
185 | .pending = atmel_serial_pending, | |
186 | .getc = atmel_serial_getc, | |
187 | .setbrg = atmel_serial_setbrg, | |
188 | }; | |
189 | ||
190 | static int atmel_serial_probe(struct udevice *dev) | |
191 | { | |
192 | struct atmel_serial_platdata *plat = dev->platdata; | |
193 | struct atmel_serial_priv *priv = dev_get_priv(dev); | |
194 | ||
195 | priv->usart = (atmel_usart3_t *)plat->base_addr; | |
196 | atmel_serial_init_internal(priv->usart); | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
201 | U_BOOT_DRIVER(serial_atmel) = { | |
202 | .name = "serial_atmel", | |
203 | .id = UCLASS_SERIAL, | |
204 | .probe = atmel_serial_probe, | |
205 | .ops = &atmel_serial_ops, | |
206 | .flags = DM_FLAG_PRE_RELOC, | |
207 | .priv_auto_alloc_size = sizeof(struct atmel_serial_priv), | |
208 | }; | |
209 | #endif |