]>
Commit | Line | Data |
---|---|---|
40b6f911 PC |
1 | /* |
2 | * IMX31 UARTS | |
3 | * | |
4 | * Copyright (c) 2008 OKL | |
5 | * Originally Written by Hans Jiang | |
6 | * Copyright (c) 2011 NICTA Pty Ltd. | |
cd0bda20 | 7 | * Updated by Jean-Christophe Dubois <[email protected]> |
40b6f911 PC |
8 | * |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | * | |
12 | * This is a `bare-bones' implementation of the IMX series serial ports. | |
13 | * TODO: | |
14 | * -- implement FIFOs. The real hardware has 32 word transmit | |
15 | * and receive FIFOs; we currently use a 1-char buffer | |
16 | * -- implement DMA | |
17 | * -- implement BAUD-rate and modem lines, for when the backend | |
18 | * is a real serial device. | |
19 | */ | |
20 | ||
cd0bda20 | 21 | #include "hw/char/imx_serial.h" |
9c17d615 | 22 | #include "sysemu/sysemu.h" |
dccfcd0e | 23 | #include "sysemu/char.h" |
40b6f911 PC |
24 | |
25 | //#define DEBUG_SERIAL 1 | |
26 | #ifdef DEBUG_SERIAL | |
27 | #define DPRINTF(fmt, args...) \ | |
fa2650a3 | 28 | do { printf("%s: " fmt , TYPE_IMX_SERIAL, ##args); } while (0) |
40b6f911 PC |
29 | #else |
30 | #define DPRINTF(fmt, args...) do {} while (0) | |
31 | #endif | |
32 | ||
33 | /* | |
34 | * Define to 1 for messages about attempts to | |
35 | * access unimplemented registers or similar. | |
36 | */ | |
37 | //#define DEBUG_IMPLEMENTATION 1 | |
38 | #ifdef DEBUG_IMPLEMENTATION | |
39 | # define IPRINTF(fmt, args...) \ | |
fa2650a3 | 40 | do { fprintf(stderr, "%s: " fmt, TYPE_IMX_SERIAL, ##args); } while (0) |
40b6f911 PC |
41 | #else |
42 | # define IPRINTF(fmt, args...) do {} while (0) | |
43 | #endif | |
44 | ||
40b6f911 | 45 | static const VMStateDescription vmstate_imx_serial = { |
fa2650a3 | 46 | .name = TYPE_IMX_SERIAL, |
40b6f911 PC |
47 | .version_id = 1, |
48 | .minimum_version_id = 1, | |
40b6f911 PC |
49 | .fields = (VMStateField[]) { |
50 | VMSTATE_INT32(readbuff, IMXSerialState), | |
51 | VMSTATE_UINT32(usr1, IMXSerialState), | |
52 | VMSTATE_UINT32(usr2, IMXSerialState), | |
53 | VMSTATE_UINT32(ucr1, IMXSerialState), | |
54 | VMSTATE_UINT32(uts1, IMXSerialState), | |
55 | VMSTATE_UINT32(onems, IMXSerialState), | |
56 | VMSTATE_UINT32(ufcr, IMXSerialState), | |
57 | VMSTATE_UINT32(ubmr, IMXSerialState), | |
58 | VMSTATE_UINT32(ubrc, IMXSerialState), | |
59 | VMSTATE_UINT32(ucr3, IMXSerialState), | |
60 | VMSTATE_END_OF_LIST() | |
61 | }, | |
62 | }; | |
63 | ||
40b6f911 PC |
64 | static void imx_update(IMXSerialState *s) |
65 | { | |
66 | uint32_t flags; | |
67 | ||
68 | flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY); | |
dc144220 GR |
69 | if (s->ucr1 & UCR1_TXMPTYEN) { |
70 | flags |= (s->uts1 & UTS1_TXEMPTY); | |
71 | } else { | |
40b6f911 PC |
72 | flags &= ~USR1_TRDY; |
73 | } | |
74 | ||
75 | qemu_set_irq(s->irq, !!flags); | |
76 | } | |
77 | ||
78 | static void imx_serial_reset(IMXSerialState *s) | |
79 | { | |
80 | ||
81 | s->usr1 = USR1_TRDY | USR1_RXDS; | |
82 | /* | |
83 | * Fake attachment of a terminal: assert RTS. | |
84 | */ | |
85 | s->usr1 |= USR1_RTSS; | |
86 | s->usr2 = USR2_TXFE | USR2_TXDC | USR2_DCDIN; | |
87 | s->uts1 = UTS1_RXEMPTY | UTS1_TXEMPTY; | |
88 | s->ucr1 = 0; | |
89 | s->ucr2 = UCR2_SRST; | |
90 | s->ucr3 = 0x700; | |
91 | s->ubmr = 0; | |
92 | s->ubrc = 4; | |
93 | s->readbuff = URXD_ERR; | |
94 | } | |
95 | ||
96 | static void imx_serial_reset_at_boot(DeviceState *dev) | |
97 | { | |
8d8e3481 | 98 | IMXSerialState *s = IMX_SERIAL(dev); |
40b6f911 PC |
99 | |
100 | imx_serial_reset(s); | |
101 | ||
102 | /* | |
103 | * enable the uart on boot, so messages from the linux decompresser | |
104 | * are visible. On real hardware this is done by the boot rom | |
105 | * before anything else is loaded. | |
106 | */ | |
107 | s->ucr1 = UCR1_UARTEN; | |
108 | s->ucr2 = UCR2_TXEN; | |
109 | ||
110 | } | |
111 | ||
a8170e5e | 112 | static uint64_t imx_serial_read(void *opaque, hwaddr offset, |
40b6f911 PC |
113 | unsigned size) |
114 | { | |
115 | IMXSerialState *s = (IMXSerialState *)opaque; | |
116 | uint32_t c; | |
117 | ||
118 | DPRINTF("read(offset=%x)\n", offset >> 2); | |
119 | switch (offset >> 2) { | |
120 | case 0x0: /* URXD */ | |
121 | c = s->readbuff; | |
122 | if (!(s->uts1 & UTS1_RXEMPTY)) { | |
123 | /* Character is valid */ | |
124 | c |= URXD_CHARRDY; | |
125 | s->usr1 &= ~USR1_RRDY; | |
126 | s->usr2 &= ~USR2_RDR; | |
127 | s->uts1 |= UTS1_RXEMPTY; | |
128 | imx_update(s); | |
f7a6785e JCD |
129 | if (s->chr) { |
130 | qemu_chr_accept_input(s->chr); | |
131 | } | |
40b6f911 PC |
132 | } |
133 | return c; | |
134 | ||
135 | case 0x20: /* UCR1 */ | |
136 | return s->ucr1; | |
137 | ||
138 | case 0x21: /* UCR2 */ | |
139 | return s->ucr2; | |
140 | ||
141 | case 0x25: /* USR1 */ | |
142 | return s->usr1; | |
143 | ||
144 | case 0x26: /* USR2 */ | |
145 | return s->usr2; | |
146 | ||
147 | case 0x2A: /* BRM Modulator */ | |
148 | return s->ubmr; | |
149 | ||
150 | case 0x2B: /* Baud Rate Count */ | |
151 | return s->ubrc; | |
152 | ||
153 | case 0x2d: /* Test register */ | |
154 | return s->uts1; | |
155 | ||
156 | case 0x24: /* UFCR */ | |
157 | return s->ufcr; | |
158 | ||
159 | case 0x2c: | |
160 | return s->onems; | |
161 | ||
162 | case 0x22: /* UCR3 */ | |
163 | return s->ucr3; | |
164 | ||
165 | case 0x23: /* UCR4 */ | |
166 | case 0x29: /* BRM Incremental */ | |
167 | return 0x0; /* TODO */ | |
168 | ||
169 | default: | |
fa2650a3 | 170 | IPRINTF("%s: bad offset: 0x%x\n", __func__, (int)offset); |
40b6f911 PC |
171 | return 0; |
172 | } | |
173 | } | |
174 | ||
a8170e5e | 175 | static void imx_serial_write(void *opaque, hwaddr offset, |
fa2650a3 | 176 | uint64_t value, unsigned size) |
40b6f911 PC |
177 | { |
178 | IMXSerialState *s = (IMXSerialState *)opaque; | |
179 | unsigned char ch; | |
180 | ||
181 | DPRINTF("write(offset=%x, value = %x) to %s\n", | |
182 | offset >> 2, | |
183 | (unsigned int)value, s->chr ? s->chr->label : "NODEV"); | |
184 | ||
185 | switch (offset >> 2) { | |
186 | case 0x10: /* UTXD */ | |
187 | ch = value; | |
188 | if (s->ucr2 & UCR2_TXEN) { | |
189 | if (s->chr) { | |
190 | qemu_chr_fe_write(s->chr, &ch, 1); | |
191 | } | |
192 | s->usr1 &= ~USR1_TRDY; | |
193 | imx_update(s); | |
194 | s->usr1 |= USR1_TRDY; | |
195 | imx_update(s); | |
196 | } | |
197 | break; | |
198 | ||
199 | case 0x20: /* UCR1 */ | |
200 | s->ucr1 = value & 0xffff; | |
201 | DPRINTF("write(ucr1=%x)\n", (unsigned int)value); | |
202 | imx_update(s); | |
203 | break; | |
204 | ||
205 | case 0x21: /* UCR2 */ | |
206 | /* | |
207 | * Only a few bits in control register 2 are implemented as yet. | |
208 | * If it's intended to use a real serial device as a back-end, this | |
209 | * register will have to be implemented more fully. | |
210 | */ | |
211 | if (!(value & UCR2_SRST)) { | |
212 | imx_serial_reset(s); | |
213 | imx_update(s); | |
214 | value |= UCR2_SRST; | |
215 | } | |
216 | if (value & UCR2_RXEN) { | |
217 | if (!(s->ucr2 & UCR2_RXEN)) { | |
f7a6785e JCD |
218 | if (s->chr) { |
219 | qemu_chr_accept_input(s->chr); | |
220 | } | |
40b6f911 PC |
221 | } |
222 | } | |
223 | s->ucr2 = value & 0xffff; | |
224 | break; | |
225 | ||
226 | case 0x25: /* USR1 */ | |
227 | value &= USR1_AWAKE | USR1_AIRINT | USR1_DTRD | USR1_AGTIM | | |
fa2650a3 | 228 | USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER; |
40b6f911 PC |
229 | s->usr1 &= ~value; |
230 | break; | |
231 | ||
232 | case 0x26: /* USR2 */ | |
fa2650a3 JCD |
233 | /* |
234 | * Writing 1 to some bits clears them; all other | |
235 | * values are ignored | |
236 | */ | |
40b6f911 | 237 | value &= USR2_ADET | USR2_DTRF | USR2_IDLE | USR2_ACST | |
fa2650a3 JCD |
238 | USR2_RIDELT | USR2_IRINT | USR2_WAKE | |
239 | USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE; | |
40b6f911 PC |
240 | s->usr2 &= ~value; |
241 | break; | |
242 | ||
fa2650a3 JCD |
243 | /* |
244 | * Linux expects to see what it writes to these registers | |
245 | * We don't currently alter the baud rate | |
246 | */ | |
40b6f911 PC |
247 | case 0x29: /* UBIR */ |
248 | s->ubrc = value & 0xffff; | |
249 | break; | |
250 | ||
251 | case 0x2a: /* UBMR */ | |
252 | s->ubmr = value & 0xffff; | |
253 | break; | |
254 | ||
255 | case 0x2c: /* One ms reg */ | |
256 | s->onems = value & 0xffff; | |
257 | break; | |
258 | ||
259 | case 0x24: /* FIFO control register */ | |
260 | s->ufcr = value & 0xffff; | |
261 | break; | |
262 | ||
263 | case 0x22: /* UCR3 */ | |
264 | s->ucr3 = value & 0xffff; | |
265 | break; | |
266 | ||
267 | case 0x2d: /* UTS1 */ | |
268 | case 0x23: /* UCR4 */ | |
269 | IPRINTF("Unimplemented Register %x written to\n", offset >> 2); | |
270 | /* TODO */ | |
271 | break; | |
272 | ||
273 | default: | |
fa2650a3 | 274 | IPRINTF("%s: Bad offset 0x%x\n", __func__, (int)offset); |
40b6f911 PC |
275 | } |
276 | } | |
277 | ||
278 | static int imx_can_receive(void *opaque) | |
279 | { | |
280 | IMXSerialState *s = (IMXSerialState *)opaque; | |
281 | return !(s->usr1 & USR1_RRDY); | |
282 | } | |
283 | ||
284 | static void imx_put_data(void *opaque, uint32_t value) | |
285 | { | |
286 | IMXSerialState *s = (IMXSerialState *)opaque; | |
287 | DPRINTF("received char\n"); | |
288 | s->usr1 |= USR1_RRDY; | |
289 | s->usr2 |= USR2_RDR; | |
290 | s->uts1 &= ~UTS1_RXEMPTY; | |
291 | s->readbuff = value; | |
292 | imx_update(s); | |
293 | } | |
294 | ||
295 | static void imx_receive(void *opaque, const uint8_t *buf, int size) | |
296 | { | |
297 | imx_put_data(opaque, *buf); | |
298 | } | |
299 | ||
300 | static void imx_event(void *opaque, int event) | |
301 | { | |
302 | if (event == CHR_EVENT_BREAK) { | |
303 | imx_put_data(opaque, URXD_BRK); | |
304 | } | |
305 | } | |
306 | ||
307 | ||
308 | static const struct MemoryRegionOps imx_serial_ops = { | |
309 | .read = imx_serial_read, | |
310 | .write = imx_serial_write, | |
311 | .endianness = DEVICE_NATIVE_ENDIAN, | |
312 | }; | |
313 | ||
f6c64000 | 314 | static void imx_serial_realize(DeviceState *dev, Error **errp) |
40b6f911 | 315 | { |
8d8e3481 | 316 | IMXSerialState *s = IMX_SERIAL(dev); |
40b6f911 | 317 | |
40b6f911 PC |
318 | if (s->chr) { |
319 | qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive, | |
320 | imx_event, s); | |
321 | } else { | |
322 | DPRINTF("No char dev for uart at 0x%lx\n", | |
323 | (unsigned long)s->iomem.ram_addr); | |
324 | } | |
f6c64000 JCD |
325 | } |
326 | ||
327 | static void imx_serial_init(Object *obj) | |
328 | { | |
329 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | |
330 | IMXSerialState *s = IMX_SERIAL(obj); | |
40b6f911 | 331 | |
f6c64000 JCD |
332 | memory_region_init_io(&s->iomem, obj, &imx_serial_ops, s, |
333 | TYPE_IMX_SERIAL, 0x1000); | |
334 | sysbus_init_mmio(sbd, &s->iomem); | |
335 | sysbus_init_irq(sbd, &s->irq); | |
40b6f911 PC |
336 | } |
337 | ||
f6c64000 | 338 | static Property imx_serial_properties[] = { |
40b6f911 PC |
339 | DEFINE_PROP_CHR("chardev", IMXSerialState, chr), |
340 | DEFINE_PROP_END_OF_LIST(), | |
341 | }; | |
342 | ||
343 | static void imx_serial_class_init(ObjectClass *klass, void *data) | |
344 | { | |
345 | DeviceClass *dc = DEVICE_CLASS(klass); | |
40b6f911 | 346 | |
f6c64000 | 347 | dc->realize = imx_serial_realize; |
40b6f911 PC |
348 | dc->vmsd = &vmstate_imx_serial; |
349 | dc->reset = imx_serial_reset_at_boot; | |
125ee0ed | 350 | set_bit(DEVICE_CATEGORY_INPUT, dc->categories); |
40b6f911 | 351 | dc->desc = "i.MX series UART"; |
f6c64000 | 352 | dc->props = imx_serial_properties; |
40b6f911 PC |
353 | } |
354 | ||
8c43a6f0 | 355 | static const TypeInfo imx_serial_info = { |
f6c64000 JCD |
356 | .name = TYPE_IMX_SERIAL, |
357 | .parent = TYPE_SYS_BUS_DEVICE, | |
358 | .instance_size = sizeof(IMXSerialState), | |
359 | .instance_init = imx_serial_init, | |
360 | .class_init = imx_serial_class_init, | |
40b6f911 PC |
361 | }; |
362 | ||
363 | static void imx_serial_register_types(void) | |
364 | { | |
365 | type_register_static(&imx_serial_info); | |
366 | } | |
367 | ||
368 | type_init(imx_serial_register_types) |