120dcee94Spbrook /*
220dcee94Spbrook * ColdFire UART emulation.
320dcee94Spbrook *
420dcee94Spbrook * Copyright (c) 2007 CodeSourcery.
520dcee94Spbrook *
68e31bf38SMatthew Fernandez * This code is licensed under the GPL
720dcee94Spbrook */
80b8fa32fSMarkus Armbruster
90430891cSPeter Maydell #include "qemu/osdep.h"
1064552b6bSMarkus Armbruster #include "hw/irq.h"
11d9ff1d35SThomas Huth #include "hw/sysbus.h"
120b8fa32fSMarkus Armbruster #include "qemu/module.h"
133e80f690SMarkus Armbruster #include "qapi/error.h"
140d09e41aSPaolo Bonzini #include "hw/m68k/mcf.h"
15a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
16ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
174d43a603SMarc-André Lureau #include "chardev/char-fe.h"
18db1015e9SEduardo Habkost #include "qom/object.h"
1920dcee94Spbrook
203d978e7bSPhilippe Mathieu-Daudé #define FIFO_DEPTH 4
213d978e7bSPhilippe Mathieu-Daudé
22db1015e9SEduardo Habkost struct mcf_uart_state {
23d9ff1d35SThomas Huth SysBusDevice parent_obj;
24d9ff1d35SThomas Huth
25aa6e4986SBenoît Canet MemoryRegion iomem;
2620dcee94Spbrook uint8_t mr[2];
2720dcee94Spbrook uint8_t sr;
2820dcee94Spbrook uint8_t isr;
2920dcee94Spbrook uint8_t imr;
3020dcee94Spbrook uint8_t bg1;
3120dcee94Spbrook uint8_t bg2;
323d978e7bSPhilippe Mathieu-Daudé uint8_t fifo[FIFO_DEPTH];
3320dcee94Spbrook uint8_t tb;
3420dcee94Spbrook int current_mr;
3520dcee94Spbrook int fifo_len;
3620dcee94Spbrook int tx_enabled;
3720dcee94Spbrook int rx_enabled;
3820dcee94Spbrook qemu_irq irq;
3932a6ebecSMarc-André Lureau CharBackend chr;
40db1015e9SEduardo Habkost };
4120dcee94Spbrook
42d9ff1d35SThomas Huth #define TYPE_MCF_UART "mcf-uart"
OBJECT_DECLARE_SIMPLE_TYPE(mcf_uart_state,MCF_UART)438063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(mcf_uart_state, MCF_UART)
44d9ff1d35SThomas Huth
4520dcee94Spbrook /* UART Status Register bits. */
4620dcee94Spbrook #define MCF_UART_RxRDY 0x01
4720dcee94Spbrook #define MCF_UART_FFULL 0x02
4820dcee94Spbrook #define MCF_UART_TxRDY 0x04
4920dcee94Spbrook #define MCF_UART_TxEMP 0x08
5020dcee94Spbrook #define MCF_UART_OE 0x10
5120dcee94Spbrook #define MCF_UART_PE 0x20
5220dcee94Spbrook #define MCF_UART_FE 0x40
5320dcee94Spbrook #define MCF_UART_RB 0x80
5420dcee94Spbrook
5520dcee94Spbrook /* Interrupt flags. */
5620dcee94Spbrook #define MCF_UART_TxINT 0x01
5720dcee94Spbrook #define MCF_UART_RxINT 0x02
5820dcee94Spbrook #define MCF_UART_DBINT 0x04
5920dcee94Spbrook #define MCF_UART_COSINT 0x80
6020dcee94Spbrook
6120dcee94Spbrook /* UMR1 flags. */
6220dcee94Spbrook #define MCF_UART_BC0 0x01
6320dcee94Spbrook #define MCF_UART_BC1 0x02
6420dcee94Spbrook #define MCF_UART_PT 0x04
6520dcee94Spbrook #define MCF_UART_PM0 0x08
6620dcee94Spbrook #define MCF_UART_PM1 0x10
6720dcee94Spbrook #define MCF_UART_ERR 0x20
6820dcee94Spbrook #define MCF_UART_RxIRQ 0x40
6920dcee94Spbrook #define MCF_UART_RxRTS 0x80
7020dcee94Spbrook
7120dcee94Spbrook static void mcf_uart_update(mcf_uart_state *s)
7220dcee94Spbrook {
7320dcee94Spbrook s->isr &= ~(MCF_UART_TxINT | MCF_UART_RxINT);
7420dcee94Spbrook if (s->sr & MCF_UART_TxRDY)
7520dcee94Spbrook s->isr |= MCF_UART_TxINT;
7620dcee94Spbrook if ((s->sr & ((s->mr[0] & MCF_UART_RxIRQ)
7720dcee94Spbrook ? MCF_UART_FFULL : MCF_UART_RxRDY)) != 0)
7820dcee94Spbrook s->isr |= MCF_UART_RxINT;
7920dcee94Spbrook
8020dcee94Spbrook qemu_set_irq(s->irq, (s->isr & s->imr) != 0);
8120dcee94Spbrook }
8220dcee94Spbrook
mcf_uart_read(void * opaque,hwaddr addr,unsigned size)83a8170e5eSAvi Kivity uint64_t mcf_uart_read(void *opaque, hwaddr addr,
84aa6e4986SBenoît Canet unsigned size)
8520dcee94Spbrook {
8620dcee94Spbrook mcf_uart_state *s = (mcf_uart_state *)opaque;
8720dcee94Spbrook switch (addr & 0x3f) {
8820dcee94Spbrook case 0x00:
8920dcee94Spbrook return s->mr[s->current_mr];
9020dcee94Spbrook case 0x04:
9120dcee94Spbrook return s->sr;
9220dcee94Spbrook case 0x0c:
9320dcee94Spbrook {
9420dcee94Spbrook uint8_t val;
9520dcee94Spbrook int i;
9620dcee94Spbrook
9720dcee94Spbrook if (s->fifo_len == 0)
9820dcee94Spbrook return 0;
9920dcee94Spbrook
10020dcee94Spbrook val = s->fifo[0];
10120dcee94Spbrook s->fifo_len--;
10220dcee94Spbrook for (i = 0; i < s->fifo_len; i++)
10320dcee94Spbrook s->fifo[i] = s->fifo[i + 1];
10420dcee94Spbrook s->sr &= ~MCF_UART_FFULL;
10520dcee94Spbrook if (s->fifo_len == 0)
10620dcee94Spbrook s->sr &= ~MCF_UART_RxRDY;
10720dcee94Spbrook mcf_uart_update(s);
1085345fdb4SMarc-André Lureau qemu_chr_fe_accept_input(&s->chr);
10920dcee94Spbrook return val;
11020dcee94Spbrook }
11120dcee94Spbrook case 0x10:
11220dcee94Spbrook /* TODO: Implement IPCR. */
11320dcee94Spbrook return 0;
11420dcee94Spbrook case 0x14:
11520dcee94Spbrook return s->isr;
11620dcee94Spbrook case 0x18:
11720dcee94Spbrook return s->bg1;
11820dcee94Spbrook case 0x1c:
11920dcee94Spbrook return s->bg2;
12020dcee94Spbrook default:
12120dcee94Spbrook return 0;
12220dcee94Spbrook }
12320dcee94Spbrook }
12420dcee94Spbrook
12520dcee94Spbrook /* Update TxRDY flag and set data if present and enabled. */
mcf_uart_do_tx(mcf_uart_state * s)12620dcee94Spbrook static void mcf_uart_do_tx(mcf_uart_state *s)
12720dcee94Spbrook {
12820dcee94Spbrook if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) {
1296ab3fc32SDaniel P. Berrange /* XXX this blocks entire thread. Rewrite to use
1306ab3fc32SDaniel P. Berrange * qemu_chr_fe_write and background I/O callbacks */
1315345fdb4SMarc-André Lureau qemu_chr_fe_write_all(&s->chr, (unsigned char *)&s->tb, 1);
13220dcee94Spbrook s->sr |= MCF_UART_TxEMP;
13320dcee94Spbrook }
13420dcee94Spbrook if (s->tx_enabled) {
13520dcee94Spbrook s->sr |= MCF_UART_TxRDY;
13620dcee94Spbrook } else {
13720dcee94Spbrook s->sr &= ~MCF_UART_TxRDY;
13820dcee94Spbrook }
13920dcee94Spbrook }
14020dcee94Spbrook
mcf_do_command(mcf_uart_state * s,uint8_t cmd)14120dcee94Spbrook static void mcf_do_command(mcf_uart_state *s, uint8_t cmd)
14220dcee94Spbrook {
14320dcee94Spbrook /* Misc command. */
144491ffc1fSPaolo Bonzini switch ((cmd >> 4) & 7) {
14520dcee94Spbrook case 0: /* No-op. */
14620dcee94Spbrook break;
14720dcee94Spbrook case 1: /* Reset mode register pointer. */
14820dcee94Spbrook s->current_mr = 0;
14920dcee94Spbrook break;
15020dcee94Spbrook case 2: /* Reset receiver. */
15120dcee94Spbrook s->rx_enabled = 0;
15220dcee94Spbrook s->fifo_len = 0;
15320dcee94Spbrook s->sr &= ~(MCF_UART_RxRDY | MCF_UART_FFULL);
15420dcee94Spbrook break;
15520dcee94Spbrook case 3: /* Reset transmitter. */
15620dcee94Spbrook s->tx_enabled = 0;
15720dcee94Spbrook s->sr |= MCF_UART_TxEMP;
15820dcee94Spbrook s->sr &= ~MCF_UART_TxRDY;
15920dcee94Spbrook break;
16020dcee94Spbrook case 4: /* Reset error status. */
16120dcee94Spbrook break;
16220dcee94Spbrook case 5: /* Reset break-change interrupt. */
16320dcee94Spbrook s->isr &= ~MCF_UART_DBINT;
16420dcee94Spbrook break;
16520dcee94Spbrook case 6: /* Start break. */
16620dcee94Spbrook case 7: /* Stop break. */
16720dcee94Spbrook break;
16820dcee94Spbrook }
16920dcee94Spbrook
17020dcee94Spbrook /* Transmitter command. */
17120dcee94Spbrook switch ((cmd >> 2) & 3) {
17220dcee94Spbrook case 0: /* No-op. */
17320dcee94Spbrook break;
17420dcee94Spbrook case 1: /* Enable. */
17520dcee94Spbrook s->tx_enabled = 1;
17620dcee94Spbrook mcf_uart_do_tx(s);
17720dcee94Spbrook break;
17820dcee94Spbrook case 2: /* Disable. */
17920dcee94Spbrook s->tx_enabled = 0;
18020dcee94Spbrook mcf_uart_do_tx(s);
18120dcee94Spbrook break;
18220dcee94Spbrook case 3: /* Reserved. */
18320dcee94Spbrook fprintf(stderr, "mcf_uart: Bad TX command\n");
18420dcee94Spbrook break;
18520dcee94Spbrook }
18620dcee94Spbrook
18720dcee94Spbrook /* Receiver command. */
18820dcee94Spbrook switch (cmd & 3) {
18920dcee94Spbrook case 0: /* No-op. */
19020dcee94Spbrook break;
19120dcee94Spbrook case 1: /* Enable. */
19220dcee94Spbrook s->rx_enabled = 1;
19320dcee94Spbrook break;
19420dcee94Spbrook case 2:
19520dcee94Spbrook s->rx_enabled = 0;
19620dcee94Spbrook break;
19720dcee94Spbrook case 3: /* Reserved. */
19820dcee94Spbrook fprintf(stderr, "mcf_uart: Bad RX command\n");
19920dcee94Spbrook break;
20020dcee94Spbrook }
20120dcee94Spbrook }
20220dcee94Spbrook
mcf_uart_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)203a8170e5eSAvi Kivity void mcf_uart_write(void *opaque, hwaddr addr,
204aa6e4986SBenoît Canet uint64_t val, unsigned size)
20520dcee94Spbrook {
20620dcee94Spbrook mcf_uart_state *s = (mcf_uart_state *)opaque;
20720dcee94Spbrook switch (addr & 0x3f) {
20820dcee94Spbrook case 0x00:
20920dcee94Spbrook s->mr[s->current_mr] = val;
21020dcee94Spbrook s->current_mr = 1;
21120dcee94Spbrook break;
21220dcee94Spbrook case 0x04:
21320dcee94Spbrook /* CSR is ignored. */
21420dcee94Spbrook break;
21520dcee94Spbrook case 0x08: /* Command Register. */
21620dcee94Spbrook mcf_do_command(s, val);
21720dcee94Spbrook break;
21820dcee94Spbrook case 0x0c: /* Transmit Buffer. */
21920dcee94Spbrook s->sr &= ~MCF_UART_TxEMP;
22020dcee94Spbrook s->tb = val;
22120dcee94Spbrook mcf_uart_do_tx(s);
22220dcee94Spbrook break;
22320dcee94Spbrook case 0x10:
22420dcee94Spbrook /* ACR is ignored. */
22520dcee94Spbrook break;
22620dcee94Spbrook case 0x14:
22720dcee94Spbrook s->imr = val;
22820dcee94Spbrook break;
22920dcee94Spbrook default:
23020dcee94Spbrook break;
23120dcee94Spbrook }
23220dcee94Spbrook mcf_uart_update(s);
23320dcee94Spbrook }
23420dcee94Spbrook
mcf_uart_reset(DeviceState * dev)235d9ff1d35SThomas Huth static void mcf_uart_reset(DeviceState *dev)
23620dcee94Spbrook {
237d9ff1d35SThomas Huth mcf_uart_state *s = MCF_UART(dev);
238d9ff1d35SThomas Huth
23920dcee94Spbrook s->fifo_len = 0;
24020dcee94Spbrook s->mr[0] = 0;
24120dcee94Spbrook s->mr[1] = 0;
24220dcee94Spbrook s->sr = MCF_UART_TxEMP;
24320dcee94Spbrook s->tx_enabled = 0;
24420dcee94Spbrook s->rx_enabled = 0;
24520dcee94Spbrook s->isr = 0;
24620dcee94Spbrook s->imr = 0;
24720dcee94Spbrook }
24820dcee94Spbrook
mcf_uart_push_byte(mcf_uart_state * s,uint8_t data)24920dcee94Spbrook static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data)
25020dcee94Spbrook {
25120dcee94Spbrook /* Break events overwrite the last byte if the fifo is full. */
2523d978e7bSPhilippe Mathieu-Daudé if (s->fifo_len == FIFO_DEPTH) {
25320dcee94Spbrook s->fifo_len--;
2543d978e7bSPhilippe Mathieu-Daudé }
25520dcee94Spbrook
25620dcee94Spbrook s->fifo[s->fifo_len] = data;
25720dcee94Spbrook s->fifo_len++;
25820dcee94Spbrook s->sr |= MCF_UART_RxRDY;
2593d978e7bSPhilippe Mathieu-Daudé if (s->fifo_len == FIFO_DEPTH) {
26020dcee94Spbrook s->sr |= MCF_UART_FFULL;
2613d978e7bSPhilippe Mathieu-Daudé }
26220dcee94Spbrook
26320dcee94Spbrook mcf_uart_update(s);
26420dcee94Spbrook }
26520dcee94Spbrook
mcf_uart_event(void * opaque,QEMUChrEvent event)266083b266fSPhilippe Mathieu-Daudé static void mcf_uart_event(void *opaque, QEMUChrEvent event)
26720dcee94Spbrook {
26820dcee94Spbrook mcf_uart_state *s = (mcf_uart_state *)opaque;
26920dcee94Spbrook
27020dcee94Spbrook switch (event) {
27120dcee94Spbrook case CHR_EVENT_BREAK:
27220dcee94Spbrook s->isr |= MCF_UART_DBINT;
27320dcee94Spbrook mcf_uart_push_byte(s, 0);
27420dcee94Spbrook break;
27520dcee94Spbrook default:
27620dcee94Spbrook break;
27720dcee94Spbrook }
27820dcee94Spbrook }
27920dcee94Spbrook
mcf_uart_can_receive(void * opaque)28020dcee94Spbrook static int mcf_uart_can_receive(void *opaque)
28120dcee94Spbrook {
28220dcee94Spbrook mcf_uart_state *s = (mcf_uart_state *)opaque;
28320dcee94Spbrook
2843ca8af54SPhilippe Mathieu-Daudé return s->rx_enabled ? FIFO_DEPTH - s->fifo_len : 0;
28520dcee94Spbrook }
28620dcee94Spbrook
mcf_uart_receive(void * opaque,const uint8_t * buf,int size)28720dcee94Spbrook static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size)
28820dcee94Spbrook {
28920dcee94Spbrook mcf_uart_state *s = (mcf_uart_state *)opaque;
29020dcee94Spbrook
2913ca8af54SPhilippe Mathieu-Daudé for (int i = 0; i < size; i++) {
2923ca8af54SPhilippe Mathieu-Daudé mcf_uart_push_byte(s, buf[i]);
2933ca8af54SPhilippe Mathieu-Daudé }
29420dcee94Spbrook }
29520dcee94Spbrook
296aa6e4986SBenoît Canet static const MemoryRegionOps mcf_uart_ops = {
297aa6e4986SBenoît Canet .read = mcf_uart_read,
298aa6e4986SBenoît Canet .write = mcf_uart_write,
299aa6e4986SBenoît Canet .endianness = DEVICE_NATIVE_ENDIAN,
30020dcee94Spbrook };
30120dcee94Spbrook
mcf_uart_instance_init(Object * obj)302d9ff1d35SThomas Huth static void mcf_uart_instance_init(Object *obj)
30320dcee94Spbrook {
304d9ff1d35SThomas Huth SysBusDevice *dev = SYS_BUS_DEVICE(obj);
305d9ff1d35SThomas Huth mcf_uart_state *s = MCF_UART(dev);
30620dcee94Spbrook
307d9ff1d35SThomas Huth memory_region_init_io(&s->iomem, obj, &mcf_uart_ops, s, "uart", 0x40);
308d9ff1d35SThomas Huth sysbus_init_mmio(dev, &s->iomem);
309d9ff1d35SThomas Huth
310d9ff1d35SThomas Huth sysbus_init_irq(dev, &s->irq);
311d9ff1d35SThomas Huth }
312d9ff1d35SThomas Huth
mcf_uart_realize(DeviceState * dev,Error ** errp)313d9ff1d35SThomas Huth static void mcf_uart_realize(DeviceState *dev, Error **errp)
314d9ff1d35SThomas Huth {
315d9ff1d35SThomas Huth mcf_uart_state *s = MCF_UART(dev);
316d9ff1d35SThomas Huth
317d9ff1d35SThomas Huth qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive, mcf_uart_receive,
31881517ba3SAnton Nefedov mcf_uart_event, NULL, s, NULL, true);
319d9ff1d35SThomas Huth }
320d9ff1d35SThomas Huth
321312f37d1SRichard Henderson static const Property mcf_uart_properties[] = {
322d9ff1d35SThomas Huth DEFINE_PROP_CHR("chardev", mcf_uart_state, chr),
323d9ff1d35SThomas Huth };
324d9ff1d35SThomas Huth
mcf_uart_class_init(ObjectClass * oc,const void * data)325*12d1a768SPhilippe Mathieu-Daudé static void mcf_uart_class_init(ObjectClass *oc, const void *data)
326d9ff1d35SThomas Huth {
327d9ff1d35SThomas Huth DeviceClass *dc = DEVICE_CLASS(oc);
328d9ff1d35SThomas Huth
329d9ff1d35SThomas Huth dc->realize = mcf_uart_realize;
330e3d08143SPeter Maydell device_class_set_legacy_reset(dc, mcf_uart_reset);
3314f67d30bSMarc-André Lureau device_class_set_props(dc, mcf_uart_properties);
332d9ff1d35SThomas Huth set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
333d9ff1d35SThomas Huth }
334d9ff1d35SThomas Huth
335d9ff1d35SThomas Huth static const TypeInfo mcf_uart_info = {
336d9ff1d35SThomas Huth .name = TYPE_MCF_UART,
337d9ff1d35SThomas Huth .parent = TYPE_SYS_BUS_DEVICE,
338d9ff1d35SThomas Huth .instance_size = sizeof(mcf_uart_state),
339d9ff1d35SThomas Huth .instance_init = mcf_uart_instance_init,
340d9ff1d35SThomas Huth .class_init = mcf_uart_class_init,
341d9ff1d35SThomas Huth };
342d9ff1d35SThomas Huth
mcf_uart_register(void)343d9ff1d35SThomas Huth static void mcf_uart_register(void)
344d9ff1d35SThomas Huth {
345d9ff1d35SThomas Huth type_register_static(&mcf_uart_info);
346d9ff1d35SThomas Huth }
347d9ff1d35SThomas Huth
type_init(mcf_uart_register)348d9ff1d35SThomas Huth type_init(mcf_uart_register)
349d9ff1d35SThomas Huth
350f213ccc9SPhilippe Mathieu-Daudé DeviceState *mcf_uart_create(qemu_irq irq, Chardev *chrdrv)
351d9ff1d35SThomas Huth {
352d9ff1d35SThomas Huth DeviceState *dev;
353d9ff1d35SThomas Huth
3543e80f690SMarkus Armbruster dev = qdev_new(TYPE_MCF_UART);
355d9ff1d35SThomas Huth if (chrdrv) {
356d9ff1d35SThomas Huth qdev_prop_set_chr(dev, "chardev", chrdrv);
357d9ff1d35SThomas Huth }
3583c6ef471SMarkus Armbruster sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
359d9ff1d35SThomas Huth sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
360d9ff1d35SThomas Huth
361d9ff1d35SThomas Huth return dev;
362d9ff1d35SThomas Huth }
363d9ff1d35SThomas Huth
mcf_uart_create_mmap(hwaddr base,qemu_irq irq,Chardev * chrdrv)364f213ccc9SPhilippe Mathieu-Daudé DeviceState *mcf_uart_create_mmap(hwaddr base, qemu_irq irq, Chardev *chrdrv)
365d9ff1d35SThomas Huth {
366d9ff1d35SThomas Huth DeviceState *dev;
367d9ff1d35SThomas Huth
368f213ccc9SPhilippe Mathieu-Daudé dev = mcf_uart_create(irq, chrdrv);
369d9ff1d35SThomas Huth sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
370f213ccc9SPhilippe Mathieu-Daudé
371f213ccc9SPhilippe Mathieu-Daudé return dev;
37220dcee94Spbrook }
373