root / hw / char / exynos4210_uart.c @ 80270a19
History | View | Annotate | Download (19.2 kB)
1 |
/*
|
---|---|
2 |
* Exynos4210 UART Emulation
|
3 |
*
|
4 |
* Copyright (C) 2011 Samsung Electronics Co Ltd.
|
5 |
* Maksim Kozlov, <m.kozlov@samsung.com>
|
6 |
*
|
7 |
* This program is free software; you can redistribute it and/or modify it
|
8 |
* under the terms of the GNU General Public License as published by the
|
9 |
* Free Software Foundation; either version 2 of the License, or
|
10 |
* (at your option) any later version.
|
11 |
*
|
12 |
* This program is distributed in the hope that it will be useful, but WITHOUT
|
13 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
14 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
15 |
* for more details.
|
16 |
*
|
17 |
* You should have received a copy of the GNU General Public License along
|
18 |
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
19 |
*
|
20 |
*/
|
21 |
|
22 |
#include "hw/sysbus.h" |
23 |
#include "sysemu/sysemu.h" |
24 |
#include "sysemu/char.h" |
25 |
|
26 |
#include "hw/arm/exynos4210.h" |
27 |
|
28 |
#undef DEBUG_UART
|
29 |
#undef DEBUG_UART_EXTEND
|
30 |
#undef DEBUG_IRQ
|
31 |
#undef DEBUG_Rx_DATA
|
32 |
#undef DEBUG_Tx_DATA
|
33 |
|
34 |
#define DEBUG_UART 0 |
35 |
#define DEBUG_UART_EXTEND 0 |
36 |
#define DEBUG_IRQ 0 |
37 |
#define DEBUG_Rx_DATA 0 |
38 |
#define DEBUG_Tx_DATA 0 |
39 |
|
40 |
#if DEBUG_UART
|
41 |
#define PRINT_DEBUG(fmt, args...) \
|
42 |
do { \
|
43 |
fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ |
44 |
} while (0) |
45 |
|
46 |
#if DEBUG_UART_EXTEND
|
47 |
#define PRINT_DEBUG_EXTEND(fmt, args...) \
|
48 |
do { \
|
49 |
fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ |
50 |
} while (0) |
51 |
#else
|
52 |
#define PRINT_DEBUG_EXTEND(fmt, args...) \
|
53 |
do {} while (0) |
54 |
#endif /* EXTEND */ |
55 |
|
56 |
#else
|
57 |
#define PRINT_DEBUG(fmt, args...) \
|
58 |
do {} while (0) |
59 |
#define PRINT_DEBUG_EXTEND(fmt, args...) \
|
60 |
do {} while (0) |
61 |
#endif
|
62 |
|
63 |
#define PRINT_ERROR(fmt, args...) \
|
64 |
do { \
|
65 |
fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ |
66 |
} while (0) |
67 |
|
68 |
/*
|
69 |
* Offsets for UART registers relative to SFR base address
|
70 |
* for UARTn
|
71 |
*
|
72 |
*/
|
73 |
#define ULCON 0x0000 /* Line Control */ |
74 |
#define UCON 0x0004 /* Control */ |
75 |
#define UFCON 0x0008 /* FIFO Control */ |
76 |
#define UMCON 0x000C /* Modem Control */ |
77 |
#define UTRSTAT 0x0010 /* Tx/Rx Status */ |
78 |
#define UERSTAT 0x0014 /* UART Error Status */ |
79 |
#define UFSTAT 0x0018 /* FIFO Status */ |
80 |
#define UMSTAT 0x001C /* Modem Status */ |
81 |
#define UTXH 0x0020 /* Transmit Buffer */ |
82 |
#define URXH 0x0024 /* Receive Buffer */ |
83 |
#define UBRDIV 0x0028 /* Baud Rate Divisor */ |
84 |
#define UFRACVAL 0x002C /* Divisor Fractional Value */ |
85 |
#define UINTP 0x0030 /* Interrupt Pending */ |
86 |
#define UINTSP 0x0034 /* Interrupt Source Pending */ |
87 |
#define UINTM 0x0038 /* Interrupt Mask */ |
88 |
|
89 |
/*
|
90 |
* for indexing register in the uint32_t array
|
91 |
*
|
92 |
* 'reg' - register offset (see offsets definitions above)
|
93 |
*
|
94 |
*/
|
95 |
#define I_(reg) (reg / sizeof(uint32_t)) |
96 |
|
97 |
typedef struct Exynos4210UartReg { |
98 |
const char *name; /* the only reason is the debug output */ |
99 |
hwaddr offset; |
100 |
uint32_t reset_value; |
101 |
} Exynos4210UartReg; |
102 |
|
103 |
static Exynos4210UartReg exynos4210_uart_regs[] = {
|
104 |
{"ULCON", ULCON, 0x00000000}, |
105 |
{"UCON", UCON, 0x00003000}, |
106 |
{"UFCON", UFCON, 0x00000000}, |
107 |
{"UMCON", UMCON, 0x00000000}, |
108 |
{"UTRSTAT", UTRSTAT, 0x00000006}, /* RO */ |
109 |
{"UERSTAT", UERSTAT, 0x00000000}, /* RO */ |
110 |
{"UFSTAT", UFSTAT, 0x00000000}, /* RO */ |
111 |
{"UMSTAT", UMSTAT, 0x00000000}, /* RO */ |
112 |
{"UTXH", UTXH, 0x5c5c5c5c}, /* WO, undefined reset value*/ |
113 |
{"URXH", URXH, 0x00000000}, /* RO */ |
114 |
{"UBRDIV", UBRDIV, 0x00000000}, |
115 |
{"UFRACVAL", UFRACVAL, 0x00000000}, |
116 |
{"UINTP", UINTP, 0x00000000}, |
117 |
{"UINTSP", UINTSP, 0x00000000}, |
118 |
{"UINTM", UINTM, 0x00000000}, |
119 |
}; |
120 |
|
121 |
#define EXYNOS4210_UART_REGS_MEM_SIZE 0x3C |
122 |
|
123 |
/* UART FIFO Control */
|
124 |
#define UFCON_FIFO_ENABLE 0x1 |
125 |
#define UFCON_Rx_FIFO_RESET 0x2 |
126 |
#define UFCON_Tx_FIFO_RESET 0x4 |
127 |
#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT 8 |
128 |
#define UFCON_Tx_FIFO_TRIGGER_LEVEL (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT) |
129 |
#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT 4 |
130 |
#define UFCON_Rx_FIFO_TRIGGER_LEVEL (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT) |
131 |
|
132 |
/* Uart FIFO Status */
|
133 |
#define UFSTAT_Rx_FIFO_COUNT 0xff |
134 |
#define UFSTAT_Rx_FIFO_FULL 0x100 |
135 |
#define UFSTAT_Rx_FIFO_ERROR 0x200 |
136 |
#define UFSTAT_Tx_FIFO_COUNT_SHIFT 16 |
137 |
#define UFSTAT_Tx_FIFO_COUNT (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT) |
138 |
#define UFSTAT_Tx_FIFO_FULL_SHIFT 24 |
139 |
#define UFSTAT_Tx_FIFO_FULL (1 << UFSTAT_Tx_FIFO_FULL_SHIFT) |
140 |
|
141 |
/* UART Interrupt Source Pending */
|
142 |
#define UINTSP_RXD 0x1 /* Receive interrupt */ |
143 |
#define UINTSP_ERROR 0x2 /* Error interrupt */ |
144 |
#define UINTSP_TXD 0x4 /* Transmit interrupt */ |
145 |
#define UINTSP_MODEM 0x8 /* Modem interrupt */ |
146 |
|
147 |
/* UART Line Control */
|
148 |
#define ULCON_IR_MODE_SHIFT 6 |
149 |
#define ULCON_PARITY_SHIFT 3 |
150 |
#define ULCON_STOP_BIT_SHIFT 1 |
151 |
|
152 |
/* UART Tx/Rx Status */
|
153 |
#define UTRSTAT_TRANSMITTER_EMPTY 0x4 |
154 |
#define UTRSTAT_Tx_BUFFER_EMPTY 0x2 |
155 |
#define UTRSTAT_Rx_BUFFER_DATA_READY 0x1 |
156 |
|
157 |
/* UART Error Status */
|
158 |
#define UERSTAT_OVERRUN 0x1 |
159 |
#define UERSTAT_PARITY 0x2 |
160 |
#define UERSTAT_FRAME 0x4 |
161 |
#define UERSTAT_BREAK 0x8 |
162 |
|
163 |
typedef struct { |
164 |
uint8_t *data; |
165 |
uint32_t sp, rp; /* store and retrieve pointers */
|
166 |
uint32_t size; |
167 |
} Exynos4210UartFIFO; |
168 |
|
169 |
typedef struct { |
170 |
SysBusDevice busdev; |
171 |
MemoryRegion iomem; |
172 |
|
173 |
uint32_t reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)];
|
174 |
Exynos4210UartFIFO rx; |
175 |
Exynos4210UartFIFO tx; |
176 |
|
177 |
CharDriverState *chr; |
178 |
qemu_irq irq; |
179 |
|
180 |
uint32_t channel; |
181 |
|
182 |
} Exynos4210UartState; |
183 |
|
184 |
|
185 |
#if DEBUG_UART
|
186 |
/* Used only for debugging inside PRINT_DEBUG_... macros */
|
187 |
static const char *exynos4210_uart_regname(hwaddr offset) |
188 |
{ |
189 |
|
190 |
int regs_number = sizeof(exynos4210_uart_regs) / sizeof(Exynos4210UartReg); |
191 |
int i;
|
192 |
|
193 |
for (i = 0; i < regs_number; i++) { |
194 |
if (offset == exynos4210_uart_regs[i].offset) {
|
195 |
return exynos4210_uart_regs[i].name;
|
196 |
} |
197 |
} |
198 |
|
199 |
return NULL; |
200 |
} |
201 |
#endif
|
202 |
|
203 |
|
204 |
static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch) |
205 |
{ |
206 |
q->data[q->sp] = ch; |
207 |
q->sp = (q->sp + 1) % q->size;
|
208 |
} |
209 |
|
210 |
static uint8_t fifo_retrieve(Exynos4210UartFIFO *q)
|
211 |
{ |
212 |
uint8_t ret = q->data[q->rp]; |
213 |
q->rp = (q->rp + 1) % q->size;
|
214 |
return ret;
|
215 |
} |
216 |
|
217 |
static int fifo_elements_number(Exynos4210UartFIFO *q) |
218 |
{ |
219 |
if (q->sp < q->rp) {
|
220 |
return q->size - q->rp + q->sp;
|
221 |
} |
222 |
|
223 |
return q->sp - q->rp;
|
224 |
} |
225 |
|
226 |
static int fifo_empty_elements_number(Exynos4210UartFIFO *q) |
227 |
{ |
228 |
return q->size - fifo_elements_number(q);
|
229 |
} |
230 |
|
231 |
static void fifo_reset(Exynos4210UartFIFO *q) |
232 |
{ |
233 |
if (q->data != NULL) { |
234 |
g_free(q->data); |
235 |
q->data = NULL;
|
236 |
} |
237 |
|
238 |
q->data = (uint8_t *)g_malloc0(q->size); |
239 |
|
240 |
q->sp = 0;
|
241 |
q->rp = 0;
|
242 |
} |
243 |
|
244 |
static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s)
|
245 |
{ |
246 |
uint32_t level = 0;
|
247 |
uint32_t reg; |
248 |
|
249 |
reg = (s->reg[I_(UFCON)] & UFCON_Tx_FIFO_TRIGGER_LEVEL) >> |
250 |
UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT; |
251 |
|
252 |
switch (s->channel) {
|
253 |
case 0: |
254 |
level = reg * 32;
|
255 |
break;
|
256 |
case 1: |
257 |
case 4: |
258 |
level = reg * 8;
|
259 |
break;
|
260 |
case 2: |
261 |
case 3: |
262 |
level = reg * 2;
|
263 |
break;
|
264 |
default:
|
265 |
level = 0;
|
266 |
PRINT_ERROR("Wrong UART channel number: %d\n", s->channel);
|
267 |
} |
268 |
|
269 |
return level;
|
270 |
} |
271 |
|
272 |
static void exynos4210_uart_update_irq(Exynos4210UartState *s) |
273 |
{ |
274 |
/*
|
275 |
* The Tx interrupt is always requested if the number of data in the
|
276 |
* transmit FIFO is smaller than the trigger level.
|
277 |
*/
|
278 |
if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
|
279 |
|
280 |
uint32_t count = (s->reg[I_(UFSTAT)] & UFSTAT_Tx_FIFO_COUNT) >> |
281 |
UFSTAT_Tx_FIFO_COUNT_SHIFT; |
282 |
|
283 |
if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) {
|
284 |
s->reg[I_(UINTSP)] |= UINTSP_TXD; |
285 |
} |
286 |
} |
287 |
|
288 |
s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)]; |
289 |
|
290 |
if (s->reg[I_(UINTP)]) {
|
291 |
qemu_irq_raise(s->irq); |
292 |
|
293 |
#if DEBUG_IRQ
|
294 |
fprintf(stderr, "UART%d: IRQ has been raised: %08x\n",
|
295 |
s->channel, s->reg[I_(UINTP)]); |
296 |
#endif
|
297 |
|
298 |
} else {
|
299 |
qemu_irq_lower(s->irq); |
300 |
} |
301 |
} |
302 |
|
303 |
static void exynos4210_uart_update_parameters(Exynos4210UartState *s) |
304 |
{ |
305 |
int speed, parity, data_bits, stop_bits, frame_size;
|
306 |
QEMUSerialSetParams ssp; |
307 |
uint64_t uclk_rate; |
308 |
|
309 |
if (s->reg[I_(UBRDIV)] == 0) { |
310 |
return;
|
311 |
} |
312 |
|
313 |
frame_size = 1; /* start bit */ |
314 |
if (s->reg[I_(ULCON)] & 0x20) { |
315 |
frame_size++; /* parity bit */
|
316 |
if (s->reg[I_(ULCON)] & 0x28) { |
317 |
parity = 'E';
|
318 |
} else {
|
319 |
parity = 'O';
|
320 |
} |
321 |
} else {
|
322 |
parity = 'N';
|
323 |
} |
324 |
|
325 |
if (s->reg[I_(ULCON)] & 0x4) { |
326 |
stop_bits = 2;
|
327 |
} else {
|
328 |
stop_bits = 1;
|
329 |
} |
330 |
|
331 |
data_bits = (s->reg[I_(ULCON)] & 0x3) + 5; |
332 |
|
333 |
frame_size += data_bits + stop_bits; |
334 |
|
335 |
uclk_rate = 24000000;
|
336 |
|
337 |
speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) + |
338 |
(s->reg[I_(UFRACVAL)] & 0x7) + 16); |
339 |
|
340 |
ssp.speed = speed; |
341 |
ssp.parity = parity; |
342 |
ssp.data_bits = data_bits; |
343 |
ssp.stop_bits = stop_bits; |
344 |
|
345 |
qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); |
346 |
|
347 |
PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n",
|
348 |
s->channel, speed, parity, data_bits, stop_bits); |
349 |
} |
350 |
|
351 |
static void exynos4210_uart_write(void *opaque, hwaddr offset, |
352 |
uint64_t val, unsigned size)
|
353 |
{ |
354 |
Exynos4210UartState *s = (Exynos4210UartState *)opaque; |
355 |
uint8_t ch; |
356 |
|
357 |
PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel,
|
358 |
offset, exynos4210_uart_regname(offset), (long long unsigned int)val); |
359 |
|
360 |
switch (offset) {
|
361 |
case ULCON:
|
362 |
case UBRDIV:
|
363 |
case UFRACVAL:
|
364 |
s->reg[I_(offset)] = val; |
365 |
exynos4210_uart_update_parameters(s); |
366 |
break;
|
367 |
case UFCON:
|
368 |
s->reg[I_(UFCON)] = val; |
369 |
if (val & UFCON_Rx_FIFO_RESET) {
|
370 |
fifo_reset(&s->rx); |
371 |
s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET; |
372 |
PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel);
|
373 |
} |
374 |
if (val & UFCON_Tx_FIFO_RESET) {
|
375 |
fifo_reset(&s->tx); |
376 |
s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET; |
377 |
PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel);
|
378 |
} |
379 |
break;
|
380 |
|
381 |
case UTXH:
|
382 |
if (s->chr) {
|
383 |
s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY | |
384 |
UTRSTAT_Tx_BUFFER_EMPTY); |
385 |
ch = (uint8_t)val; |
386 |
qemu_chr_fe_write(s->chr, &ch, 1);
|
387 |
#if DEBUG_Tx_DATA
|
388 |
fprintf(stderr, "%c", ch);
|
389 |
#endif
|
390 |
s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY | |
391 |
UTRSTAT_Tx_BUFFER_EMPTY; |
392 |
s->reg[I_(UINTSP)] |= UINTSP_TXD; |
393 |
exynos4210_uart_update_irq(s); |
394 |
} |
395 |
break;
|
396 |
|
397 |
case UINTP:
|
398 |
s->reg[I_(UINTP)] &= ~val; |
399 |
s->reg[I_(UINTSP)] &= ~val; |
400 |
PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n",
|
401 |
s->channel, offset, s->reg[I_(UINTP)]); |
402 |
exynos4210_uart_update_irq(s); |
403 |
break;
|
404 |
case UTRSTAT:
|
405 |
case UERSTAT:
|
406 |
case UFSTAT:
|
407 |
case UMSTAT:
|
408 |
case URXH:
|
409 |
PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n",
|
410 |
s->channel, exynos4210_uart_regname(offset), offset); |
411 |
break;
|
412 |
case UINTSP:
|
413 |
s->reg[I_(UINTSP)] &= ~val; |
414 |
break;
|
415 |
case UINTM:
|
416 |
s->reg[I_(UINTM)] = val; |
417 |
exynos4210_uart_update_irq(s); |
418 |
break;
|
419 |
case UCON:
|
420 |
case UMCON:
|
421 |
default:
|
422 |
s->reg[I_(offset)] = val; |
423 |
break;
|
424 |
} |
425 |
} |
426 |
static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, |
427 |
unsigned size)
|
428 |
{ |
429 |
Exynos4210UartState *s = (Exynos4210UartState *)opaque; |
430 |
uint32_t res; |
431 |
|
432 |
switch (offset) {
|
433 |
case UERSTAT: /* Read Only */ |
434 |
res = s->reg[I_(UERSTAT)]; |
435 |
s->reg[I_(UERSTAT)] = 0;
|
436 |
return res;
|
437 |
case UFSTAT: /* Read Only */ |
438 |
s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff;
|
439 |
if (fifo_empty_elements_number(&s->rx) == 0) { |
440 |
s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL; |
441 |
s->reg[I_(UFSTAT)] &= ~0xff;
|
442 |
} |
443 |
return s->reg[I_(UFSTAT)];
|
444 |
case URXH:
|
445 |
if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
|
446 |
if (fifo_elements_number(&s->rx)) {
|
447 |
res = fifo_retrieve(&s->rx); |
448 |
#if DEBUG_Rx_DATA
|
449 |
fprintf(stderr, "%c", res);
|
450 |
#endif
|
451 |
if (!fifo_elements_number(&s->rx)) {
|
452 |
s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; |
453 |
} else {
|
454 |
s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; |
455 |
} |
456 |
} else {
|
457 |
s->reg[I_(UINTSP)] |= UINTSP_ERROR; |
458 |
exynos4210_uart_update_irq(s); |
459 |
res = 0;
|
460 |
} |
461 |
} else {
|
462 |
s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; |
463 |
res = s->reg[I_(URXH)]; |
464 |
} |
465 |
return res;
|
466 |
case UTXH:
|
467 |
PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n",
|
468 |
s->channel, exynos4210_uart_regname(offset), offset); |
469 |
break;
|
470 |
default:
|
471 |
return s->reg[I_(offset)];
|
472 |
} |
473 |
|
474 |
return 0; |
475 |
} |
476 |
|
477 |
static const MemoryRegionOps exynos4210_uart_ops = { |
478 |
.read = exynos4210_uart_read, |
479 |
.write = exynos4210_uart_write, |
480 |
.endianness = DEVICE_NATIVE_ENDIAN, |
481 |
.valid = { |
482 |
.max_access_size = 4,
|
483 |
.unaligned = false
|
484 |
}, |
485 |
}; |
486 |
|
487 |
static int exynos4210_uart_can_receive(void *opaque) |
488 |
{ |
489 |
Exynos4210UartState *s = (Exynos4210UartState *)opaque; |
490 |
|
491 |
return fifo_empty_elements_number(&s->rx);
|
492 |
} |
493 |
|
494 |
|
495 |
static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size) |
496 |
{ |
497 |
Exynos4210UartState *s = (Exynos4210UartState *)opaque; |
498 |
int i;
|
499 |
|
500 |
if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
|
501 |
if (fifo_empty_elements_number(&s->rx) < size) {
|
502 |
for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) { |
503 |
fifo_store(&s->rx, buf[i]); |
504 |
} |
505 |
s->reg[I_(UINTSP)] |= UINTSP_ERROR; |
506 |
s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; |
507 |
} else {
|
508 |
for (i = 0; i < size; i++) { |
509 |
fifo_store(&s->rx, buf[i]); |
510 |
} |
511 |
s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; |
512 |
} |
513 |
/* XXX: Around here we maybe should check Rx trigger level */
|
514 |
s->reg[I_(UINTSP)] |= UINTSP_RXD; |
515 |
} else {
|
516 |
s->reg[I_(URXH)] = buf[0];
|
517 |
s->reg[I_(UINTSP)] |= UINTSP_RXD; |
518 |
s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; |
519 |
} |
520 |
|
521 |
exynos4210_uart_update_irq(s); |
522 |
} |
523 |
|
524 |
|
525 |
static void exynos4210_uart_event(void *opaque, int event) |
526 |
{ |
527 |
Exynos4210UartState *s = (Exynos4210UartState *)opaque; |
528 |
|
529 |
if (event == CHR_EVENT_BREAK) {
|
530 |
/* When the RxDn is held in logic 0, then a null byte is pushed into the
|
531 |
* fifo */
|
532 |
fifo_store(&s->rx, '\0');
|
533 |
s->reg[I_(UERSTAT)] |= UERSTAT_BREAK; |
534 |
exynos4210_uart_update_irq(s); |
535 |
} |
536 |
} |
537 |
|
538 |
|
539 |
static void exynos4210_uart_reset(DeviceState *dev) |
540 |
{ |
541 |
Exynos4210UartState *s = |
542 |
container_of(dev, Exynos4210UartState, busdev.qdev); |
543 |
int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg); |
544 |
int i;
|
545 |
|
546 |
for (i = 0; i < regs_number; i++) { |
547 |
s->reg[I_(exynos4210_uart_regs[i].offset)] = |
548 |
exynos4210_uart_regs[i].reset_value; |
549 |
} |
550 |
|
551 |
fifo_reset(&s->rx); |
552 |
fifo_reset(&s->tx); |
553 |
|
554 |
PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size);
|
555 |
} |
556 |
|
557 |
static const VMStateDescription vmstate_exynos4210_uart_fifo = { |
558 |
.name = "exynos4210.uart.fifo",
|
559 |
.version_id = 1,
|
560 |
.minimum_version_id = 1,
|
561 |
.minimum_version_id_old = 1,
|
562 |
.fields = (VMStateField[]) { |
563 |
VMSTATE_UINT32(sp, Exynos4210UartFIFO), |
564 |
VMSTATE_UINT32(rp, Exynos4210UartFIFO), |
565 |
VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size), |
566 |
VMSTATE_END_OF_LIST() |
567 |
} |
568 |
}; |
569 |
|
570 |
static const VMStateDescription vmstate_exynos4210_uart = { |
571 |
.name = "exynos4210.uart",
|
572 |
.version_id = 1,
|
573 |
.minimum_version_id = 1,
|
574 |
.minimum_version_id_old = 1,
|
575 |
.fields = (VMStateField[]) { |
576 |
VMSTATE_STRUCT(rx, Exynos4210UartState, 1,
|
577 |
vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO), |
578 |
VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState, |
579 |
EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)),
|
580 |
VMSTATE_END_OF_LIST() |
581 |
} |
582 |
}; |
583 |
|
584 |
DeviceState *exynos4210_uart_create(hwaddr addr, |
585 |
int fifo_size,
|
586 |
int channel,
|
587 |
CharDriverState *chr, |
588 |
qemu_irq irq) |
589 |
{ |
590 |
DeviceState *dev; |
591 |
SysBusDevice *bus; |
592 |
|
593 |
const char chr_name[] = "serial"; |
594 |
char label[ARRAY_SIZE(chr_name) + 1]; |
595 |
|
596 |
dev = qdev_create(NULL, "exynos4210.uart"); |
597 |
|
598 |
if (!chr) {
|
599 |
if (channel >= MAX_SERIAL_PORTS) {
|
600 |
hw_error("Only %d serial ports are supported by QEMU.\n",
|
601 |
MAX_SERIAL_PORTS); |
602 |
} |
603 |
chr = serial_hds[channel]; |
604 |
if (!chr) {
|
605 |
snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel);
|
606 |
chr = qemu_chr_new(label, "null", NULL); |
607 |
if (!(chr)) {
|
608 |
hw_error("Can't assign serial port to UART%d.\n", channel);
|
609 |
} |
610 |
} |
611 |
} |
612 |
|
613 |
qdev_prop_set_chr(dev, "chardev", chr);
|
614 |
qdev_prop_set_uint32(dev, "channel", channel);
|
615 |
qdev_prop_set_uint32(dev, "rx-size", fifo_size);
|
616 |
qdev_prop_set_uint32(dev, "tx-size", fifo_size);
|
617 |
|
618 |
bus = SYS_BUS_DEVICE(dev); |
619 |
qdev_init_nofail(dev); |
620 |
if (addr != (hwaddr)-1) { |
621 |
sysbus_mmio_map(bus, 0, addr);
|
622 |
} |
623 |
sysbus_connect_irq(bus, 0, irq);
|
624 |
|
625 |
return dev;
|
626 |
} |
627 |
|
628 |
static int exynos4210_uart_init(SysBusDevice *dev) |
629 |
{ |
630 |
Exynos4210UartState *s = FROM_SYSBUS(Exynos4210UartState, dev); |
631 |
|
632 |
/* memory mapping */
|
633 |
memory_region_init_io(&s->iomem, &exynos4210_uart_ops, s, "exynos4210.uart",
|
634 |
EXYNOS4210_UART_REGS_MEM_SIZE); |
635 |
sysbus_init_mmio(dev, &s->iomem); |
636 |
|
637 |
sysbus_init_irq(dev, &s->irq); |
638 |
|
639 |
qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive, |
640 |
exynos4210_uart_receive, exynos4210_uart_event, s); |
641 |
|
642 |
return 0; |
643 |
} |
644 |
|
645 |
static Property exynos4210_uart_properties[] = {
|
646 |
DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr),
|
647 |
DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0), |
648 |
DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16), |
649 |
DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16), |
650 |
DEFINE_PROP_END_OF_LIST(), |
651 |
}; |
652 |
|
653 |
static void exynos4210_uart_class_init(ObjectClass *klass, void *data) |
654 |
{ |
655 |
DeviceClass *dc = DEVICE_CLASS(klass); |
656 |
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
657 |
|
658 |
k->init = exynos4210_uart_init; |
659 |
dc->reset = exynos4210_uart_reset; |
660 |
dc->props = exynos4210_uart_properties; |
661 |
dc->vmsd = &vmstate_exynos4210_uart; |
662 |
} |
663 |
|
664 |
static const TypeInfo exynos4210_uart_info = { |
665 |
.name = "exynos4210.uart",
|
666 |
.parent = TYPE_SYS_BUS_DEVICE, |
667 |
.instance_size = sizeof(Exynos4210UartState),
|
668 |
.class_init = exynos4210_uart_class_init, |
669 |
}; |
670 |
|
671 |
static void exynos4210_uart_register(void) |
672 |
{ |
673 |
type_register_static(&exynos4210_uart_info); |
674 |
} |
675 |
|
676 |
type_init(exynos4210_uart_register) |