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
26 #include "qemu/osdep.h"
27 #include "hw/sysbus.h"
28 #include "chardev/char.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__); \
40 #define DPRINTF(fmt, ...) do {} while(0)
41 #define BADF(fmt, ...) \
42 do { fprintf(stderr, "syborg_serial: error: " fmt , ## __VA_ARGS__);} while (0)
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 */
57 #define SERIAL_INT_FIFO (1u << 0)
58 #define SERIAL_INT_DMA_TX (1u << 1)
59 #define SERIAL_INT_DMA_RX (1u << 2)
76 static void syborg_serial_update(SyborgSerialState
*s
)
80 if ((s
->int_enable
& SERIAL_INT_FIFO
) && s
->read_count
)
82 if (s
->int_enable
& SERIAL_INT_DMA_TX
)
84 if ((s
->int_enable
& SERIAL_INT_DMA_RX
) && s
->dma_rx_size
== 0)
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
];
95 if (s
->read_pos
== s
->fifo_size
)
98 DPRINTF("FIFO pop %x (%d)\n", c
, s
->read_count
);
102 static void fifo_push(SyborgSerialState
*s
, uint32_t new_value
)
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
;
114 static void do_dma_tx(SyborgSerialState
*s
, uint32_t count
)
121 if (s
->chr
!= NULL
) {
122 /* optimize later. Now, 1 byte per iteration */
124 cpu_physical_memory_read(s
->dma_tx_ptr
, &ch
, 1);
125 qemu_chr_fe_write(s
->chr
, &ch
, 1);
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
)
141 dest
= s
->dma_rx_ptr
;
142 if (s
->read_count
< len
) {
143 s
->dma_rx_size
= len
- s
->read_count
;
151 cpu_physical_memory_write(dest
, &ch
, 1);
154 s
->dma_rx_ptr
= dest
;
155 syborg_serial_update(s
);
158 static uint64_t syborg_serial_read(void *opaque
, hwaddr offset
,
161 SyborgSerialState
*s
= (SyborgSerialState
*)opaque
;
165 DPRINTF("read 0x%x\n", (int)offset
);
166 switch(offset
>> 2) {
168 return SYBORG_ID_SERIAL
;
170 if (s
->read_count
> 0)
174 syborg_serial_update(s
);
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
:
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
:
192 cpu_abort(cpu_single_env
, "syborg_serial_read: Bad offset %x\n",
198 static void syborg_serial_write(void *opaque
, hwaddr offset
,
199 uint64_t value
, unsigned size
)
201 SyborgSerialState
*s
= (SyborgSerialState
*)opaque
;
205 DPRINTF("Write 0x%x=0x%x\n", (int)offset
, value
);
206 switch (offset
>> 2) {
210 qemu_chr_fe_write(s
->chr
, &ch
, 1);
212 case SERIAL_INT_ENABLE
:
213 s
->int_enable
= value
;
214 syborg_serial_update(s
);
216 case SERIAL_DMA_TX_ADDR
:
217 s
->dma_tx_ptr
= value
;
219 case SERIAL_DMA_TX_COUNT
:
222 case SERIAL_DMA_RX_ADDR
:
223 /* For safety, writes to this register cancel any pending DMA. */
225 s
->dma_rx_ptr
= value
;
227 case SERIAL_DMA_RX_COUNT
:
228 dma_rx_start(s
, value
);
231 cpu_abort(cpu_single_env
, "syborg_serial_write: Bad offset %x\n",
237 static int syborg_serial_can_receive(void *opaque
)
239 SyborgSerialState
*s
= (SyborgSerialState
*)opaque
;
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
;
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",
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
,
301 sysbus_init_mmio(sbd
, &s
->iomem
);
302 s
->chr
= qemu_char_get_next_serial();
303 qemu_chr_fe_set_handlers(&s
->chr
, syborg_serial_can_receive
,
304 syborg_serial_receive
, syborg_serial_event
,
306 if (s
->fifo_size
<= 0) {
307 fprintf(stderr
, "syborg_serial: fifo too small\n");
310 s
->read_fifo
= g_malloc0(s
->fifo_size
* sizeof(s
->read_fifo
[0]));
315 static Property syborg_serial_properties
[] = {
316 DEFINE_PROP_UINT32("fifo-size", SyborgSerialState
, fifo_size
, 16),
317 DEFINE_PROP_END_OF_LIST()
320 static void syborg_serial_class_init(ObjectClass
*klass
, void *data
)
322 DeviceClass
*dc
= DEVICE_CLASS(klass
);
323 SysBusDeviceClass
*k
= SYS_BUS_DEVICE_CLASS(klass
);
324 dc
->props
= syborg_serial_properties
;
325 dc
->vmsd
= &vmstate_syborg_serial
;
326 k
->init
= syborg_serial_init
;
329 static const TypeInfo syborg_serial_info
= {
330 .name
= "syborg,serial",
331 .parent
= TYPE_SYS_BUS_DEVICE
,
332 .instance_size
= sizeof(SyborgSerialState
),
333 .class_init
= syborg_serial_class_init
336 static void syborg_serial_register_types(void)
338 type_register_static(&syborg_serial_info
);
341 type_init(syborg_serial_register_types
)