Clean include statements
[qemu/ar7.git] / hw / arm / syborg_serial.c
blob9fc3deb88fe68b55dd19e989659f0966c7c70fbb
1 /*
2 * Syborg serial port
4 * Copyright (c) 2008 CodeSourcery
5 * Copyright (c) 2010, 2013 Stefan Weil
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
26 #include "qemu/osdep.h"
27 #include "hw/sysbus.h"
28 #include "sysemu/char.h"
29 #include "syborg.h"
31 //#define DEBUG_SYBORG_SERIAL
33 #ifdef DEBUG_SYBORG_SERIAL
34 #define DPRINTF(fmt, ...) \
35 do { printf("syborg_serial: " fmt , ##args); } while (0)
36 #define BADF(fmt, ...) \
37 do { fprintf(stderr, "syborg_serial: error: " fmt , ## __VA_ARGS__); \
38 exit(1);} while (0)
39 #else
40 #define DPRINTF(fmt, ...) do {} while(0)
41 #define BADF(fmt, ...) \
42 do { fprintf(stderr, "syborg_serial: error: " fmt , ## __VA_ARGS__);} while (0)
43 #endif
45 enum {
46 SERIAL_ID = 0,
47 SERIAL_DATA = 1,
48 SERIAL_FIFO_COUNT = 2,
49 SERIAL_INT_ENABLE = 3,
50 SERIAL_DMA_TX_ADDR = 4,
51 SERIAL_DMA_TX_COUNT = 5, /* triggers dma */
52 SERIAL_DMA_RX_ADDR = 6,
53 SERIAL_DMA_RX_COUNT = 7, /* triggers dma */
54 SERIAL_FIFO_SIZE = 8
57 #define SERIAL_INT_FIFO (1u << 0)
58 #define SERIAL_INT_DMA_TX (1u << 1)
59 #define SERIAL_INT_DMA_RX (1u << 2)
61 typedef struct {
62 SysBusDevice busdev;
63 MemoryRegion iomem;
64 uint32_t int_enable;
65 uint32_t fifo_size;
66 uint32_t *read_fifo;
67 int read_pos;
68 int read_count;
69 CharDriverState *chr;
70 qemu_irq irq;
71 uint32_t dma_tx_ptr;
72 uint32_t dma_rx_ptr;
73 uint32_t dma_rx_size;
74 } SyborgSerialState;
76 static void syborg_serial_update(SyborgSerialState *s)
78 int level;
79 level = 0;
80 if ((s->int_enable & SERIAL_INT_FIFO) && s->read_count)
81 level = 1;
82 if (s->int_enable & SERIAL_INT_DMA_TX)
83 level = 1;
84 if ((s->int_enable & SERIAL_INT_DMA_RX) && s->dma_rx_size == 0)
85 level = 1;
87 qemu_set_irq(s->irq, level);
90 static uint32_t fifo_pop(SyborgSerialState *s)
92 const uint32_t c = s->read_fifo[s->read_pos];
93 s->read_count--;
94 s->read_pos++;
95 if (s->read_pos == s->fifo_size)
96 s->read_pos = 0;
98 DPRINTF("FIFO pop %x (%d)\n", c, s->read_count);
99 return c;
102 static void fifo_push(SyborgSerialState *s, uint32_t new_value)
104 int slot;
106 DPRINTF("FIFO push %x (%d)\n", new_value, s->read_count);
107 slot = s->read_pos + s->read_count;
108 if (slot >= s->fifo_size)
109 slot -= s->fifo_size;
110 s->read_fifo[slot] = new_value;
111 s->read_count++;
114 static void do_dma_tx(SyborgSerialState *s, uint32_t count)
116 unsigned char ch;
118 if (count == 0)
119 return;
121 if (s->chr != NULL) {
122 /* optimize later. Now, 1 byte per iteration */
123 while (count--) {
124 cpu_physical_memory_read(s->dma_tx_ptr, &ch, 1);
125 qemu_chr_fe_write(s->chr, &ch, 1);
126 s->dma_tx_ptr++;
128 } else {
129 s->dma_tx_ptr += count;
131 /* QEMU char backends do not have a nonblocking mode, so we transmit all
132 the data immediately and the interrupt status will be unchanged. */
135 /* Initiate RX DMA, and transfer data from the FIFO. */
136 static void dma_rx_start(SyborgSerialState *s, uint32_t len)
138 uint32_t dest;
139 unsigned char ch;
141 dest = s->dma_rx_ptr;
142 if (s->read_count < len) {
143 s->dma_rx_size = len - s->read_count;
144 len = s->read_count;
145 } else {
146 s->dma_rx_size = 0;
149 while (len--) {
150 ch = fifo_pop(s);
151 cpu_physical_memory_write(dest, &ch, 1);
152 dest++;
154 s->dma_rx_ptr = dest;
155 syborg_serial_update(s);
158 static uint64_t syborg_serial_read(void *opaque, hwaddr offset,
159 unsigned size)
161 SyborgSerialState *s = (SyborgSerialState *)opaque;
162 uint32_t c;
164 offset &= 0xfff;
165 DPRINTF("read 0x%x\n", (int)offset);
166 switch(offset >> 2) {
167 case SERIAL_ID:
168 return SYBORG_ID_SERIAL;
169 case SERIAL_DATA:
170 if (s->read_count > 0)
171 c = fifo_pop(s);
172 else
173 c = -1;
174 syborg_serial_update(s);
175 return c;
176 case SERIAL_FIFO_COUNT:
177 return s->read_count;
178 case SERIAL_INT_ENABLE:
179 return s->int_enable;
180 case SERIAL_DMA_TX_ADDR:
181 return s->dma_tx_ptr;
182 case SERIAL_DMA_TX_COUNT:
183 return 0;
184 case SERIAL_DMA_RX_ADDR:
185 return s->dma_rx_ptr;
186 case SERIAL_DMA_RX_COUNT:
187 return s->dma_rx_size;
188 case SERIAL_FIFO_SIZE:
189 return s->fifo_size;
191 default:
192 cpu_abort(cpu_single_env, "syborg_serial_read: Bad offset %x\n",
193 (int)offset);
194 return 0;
198 static void syborg_serial_write(void *opaque, hwaddr offset,
199 uint64_t value, unsigned size)
201 SyborgSerialState *s = (SyborgSerialState *)opaque;
202 unsigned char ch;
204 offset &= 0xfff;
205 DPRINTF("Write 0x%x=0x%x\n", (int)offset, value);
206 switch (offset >> 2) {
207 case SERIAL_DATA:
208 ch = value;
209 if (s->chr)
210 qemu_chr_fe_write(s->chr, &ch, 1);
211 break;
212 case SERIAL_INT_ENABLE:
213 s->int_enable = value;
214 syborg_serial_update(s);
215 break;
216 case SERIAL_DMA_TX_ADDR:
217 s->dma_tx_ptr = value;
218 break;
219 case SERIAL_DMA_TX_COUNT:
220 do_dma_tx(s, value);
221 break;
222 case SERIAL_DMA_RX_ADDR:
223 /* For safety, writes to this register cancel any pending DMA. */
224 s->dma_rx_size = 0;
225 s->dma_rx_ptr = value;
226 break;
227 case SERIAL_DMA_RX_COUNT:
228 dma_rx_start(s, value);
229 break;
230 default:
231 cpu_abort(cpu_single_env, "syborg_serial_write: Bad offset %x\n",
232 (int)offset);
233 break;
237 static int syborg_serial_can_receive(void *opaque)
239 SyborgSerialState *s = (SyborgSerialState *)opaque;
241 if (s->dma_rx_size)
242 return s->dma_rx_size;
243 return s->fifo_size - s->read_count;
246 static void syborg_serial_receive(void *opaque, const uint8_t *buf, int size)
248 SyborgSerialState *s = (SyborgSerialState *)opaque;
250 if (s->dma_rx_size) {
251 /* Place it in the DMA buffer. */
252 cpu_physical_memory_write(s->dma_rx_ptr, buf, size);
253 s->dma_rx_size -= size;
254 s->dma_rx_ptr += size;
255 } else {
256 while (size--)
257 fifo_push(s, *buf);
260 syborg_serial_update(s);
263 static void syborg_serial_event(void *opaque, int event)
265 /* TODO: Report BREAK events? */
268 static const MemoryRegionOps syborg_serial_ops = {
269 .read = syborg_serial_read,
270 .write = syborg_serial_write,
271 .endianness = DEVICE_NATIVE_ENDIAN,
274 static const VMStateDescription vmstate_syborg_serial = {
275 .name = "syborg_serial",
276 .version_id = 1,
277 .minimum_version_id = 1,
278 .minimum_version_id_old = 1,
279 .fields = (VMStateField[]) {
280 VMSTATE_UINT32_EQUAL(fifo_size, SyborgSerialState),
281 VMSTATE_UINT32(int_enable, SyborgSerialState),
282 VMSTATE_INT32(read_pos, SyborgSerialState),
283 VMSTATE_INT32(read_count, SyborgSerialState),
284 VMSTATE_UINT32(dma_tx_ptr, SyborgSerialState),
285 VMSTATE_UINT32(dma_rx_ptr, SyborgSerialState),
286 VMSTATE_UINT32(dma_rx_size, SyborgSerialState),
287 VMSTATE_VARRAY_UINT32(read_fifo, SyborgSerialState, fifo_size, 1,
288 vmstate_info_uint32, uint32),
289 VMSTATE_END_OF_LIST()
293 static int syborg_serial_init(SysBusDevice *sbd)
295 DeviceState *dev = DEVICE(sbd);
296 SyborgSerialState *s = SYBORG_SERIAL(dev);
298 sysbus_init_irq(dev, &s->irq);
299 memory_region_init_io(&s->iomem, &syborg_serial_ops, s,
300 "serial", 0x1000);
301 sysbus_init_mmio(sbd, &s->iomem);
302 s->chr = qemu_char_get_next_serial();
303 if (s->chr) {
304 qemu_chr_add_handlers(s->chr, syborg_serial_can_receive,
305 syborg_serial_receive, syborg_serial_event, s);
307 if (s->fifo_size <= 0) {
308 fprintf(stderr, "syborg_serial: fifo too small\n");
309 s->fifo_size = 16;
311 s->read_fifo = g_malloc0(s->fifo_size * sizeof(s->read_fifo[0]));
313 return 0;
316 static Property syborg_serial_properties[] = {
317 DEFINE_PROP_UINT32("fifo-size", SyborgSerialState, fifo_size, 16),
318 DEFINE_PROP_END_OF_LIST()
321 static void syborg_serial_class_init(ObjectClass *klass, void *data)
323 DeviceClass *dc = DEVICE_CLASS(klass);
324 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
325 dc->props = syborg_serial_properties;
326 dc->vmsd = &vmstate_syborg_serial;
327 k->init = syborg_serial_init;
330 static const TypeInfo syborg_serial_info = {
331 .name = "syborg,serial",
332 .parent = TYPE_SYS_BUS_DEVICE,
333 .instance_size = sizeof(SyborgSerialState),
334 .class_init = syborg_serial_class_init
337 static void syborg_serial_register_types(void)
339 type_register_static(&syborg_serial_info);
342 type_init(syborg_serial_register_types)