diff options
Diffstat (limited to 'hw/char/omap_uart.c')
-rw-r--r-- | hw/char/omap_uart.c | 50 |
1 files changed, 35 insertions, 15 deletions
diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 746b635d6..5ee352ca2 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -51,7 +51,13 @@ typedef struct omap_uart_s { qemu_irq tx_drq; qemu_irq rx_drq; - uint8_t lcr_cache; + /* Register access mode, which affects what registers you see */ + enum { + regs_operational, + regs_config_a, + regs_config_b + } access_mode; + uint8_t eblr; uint8_t syscontrol; uint8_t wkup; @@ -69,6 +75,14 @@ typedef struct omap_uart_s { uint8_t xon[2], xoff[2]; } omap_uart_s; +static int tcr_tlr_mode(struct omap_uart_s *s) +{ + /* Return true if registers 0x18 and 0x1c are TCR/TLR + * (as opposed to SPR/MSR/XOFF) + */ + return (s->efr & 0x10) && (s->mcr_cache & 0x40); +} + static void omap_uart_reset(DeviceState *qdev) { struct omap_uart_s *s = OMAP_UART(qdev); @@ -80,7 +94,7 @@ static void omap_uart_reset(DeviceState *qdev) s->clksel = 0; s->blr = 0x40; s->acreg = 0; - s->lcr_cache = 0; + s->access_mode = regs_operational; s->mcr_cache = 0; s->tcr = 0x0f; @@ -101,13 +115,13 @@ static uint64_t omap_uart_read(void *opaque, hwaddr addr, case 0x0c: return s->serial_ops->read(s->serial, addr, size); case 0x08: - if (s->lcr_cache == 0xbf) { + if (s->access_mode == regs_config_b) { return s->efr; } return s->serial_ops->read(s->serial, addr, size); case 0x10: case 0x14: - if (s->lcr_cache == 0xbf) { + if (s->access_mode == regs_config_b) { return s->xon[(addr & 7) >> 2]; } else if (addr == 0x10) { return s->serial_ops->read(s->serial, addr, size) @@ -116,10 +130,10 @@ static uint64_t omap_uart_read(void *opaque, hwaddr addr, return s->serial_ops->read(s->serial, addr, size); case 0x18: case 0x1c: - if ((s->efr & 0x10) && (s->mcr_cache & 0x40)) { + if (tcr_tlr_mode(s)) { return (addr == 0x18) ? s->tcr : s->tlr; } - if (s->lcr_cache == 0xbf) { + if (s->access_mode == regs_config_b) { return s->xoff[(addr & 7) >> 2]; } return s->serial_ops->read(s->serial, addr, size); @@ -136,12 +150,12 @@ static uint64_t omap_uart_read(void *opaque, hwaddr addr, case 0x34: /* SFREGH */ return 0; case 0x38: /* UASR/BLR */ - if ((s->lcr_cache & 0x80)) { + if (s->access_mode != regs_operational) { return 0; /* FIXME: return correct autodetect value */ } return s->blr; case 0x3c: /* ACREG */ - return (s->lcr_cache & 0x80) ? 0 : s->acreg; + return (s->access_mode != regs_operational) ? 0 : s->acreg; case 0x40: /* SCR */ return s->scr; case 0x44: /* SSR */ @@ -177,19 +191,25 @@ static void omap_uart_write(void *opaque, hwaddr addr, s->serial_ops->write(s->serial, addr, value, size); break; case 0x08: - if (s->lcr_cache == 0xbf) { + if (s->access_mode == regs_config_b) { s->efr = value; } else { s->serial_ops->write(s->serial, addr, value, size); } break; case 0x0c: - s->lcr_cache = value; + if ((value & 0xff) == 0xbf) { + s->access_mode = regs_config_b; + } else if (value & 0x80) { + s->access_mode = regs_config_a; + } else { + s->access_mode = regs_operational; + } s->serial_ops->write(s->serial, addr, value, size); break; case 0x10: case 0x14: - if (s->lcr_cache == 0xbf) { + if (s->access_mode == regs_config_b) { s->xon[(addr & 7) >> 2] = value; } else { if (addr == 0x10) { @@ -200,13 +220,13 @@ static void omap_uart_write(void *opaque, hwaddr addr, break; case 0x18: case 0x1c: - if ((s->efr & 0x10) && (s->mcr_cache & 0x40)) { + if (tcr_tlr_mode(s)) { if (addr == 0x18) { s->tcr = value & 0xff; } else { s->tlr = value & 0xff; } - } else if (s->lcr_cache == 0xbf) { + } else if (s->access_mode == regs_config_b) { s->xoff[(addr & 7) >> 2] = value; } else { s->serial_ops->write(s->serial, addr, value, size); @@ -225,12 +245,12 @@ static void omap_uart_write(void *opaque, hwaddr addr, /* ignored */ break; case 0x38: /* BLR */ - if (!(s->lcr_cache & 0x80)) { + if (s->access_mode == regs_operational) { s->blr = value & 0xc0; } break; case 0x3c: /* ACREG */ - if (!(s->lcr_cache & 0x80)) { + if (s->access_mode == regs_operational) { s->acreg = value & 0xff; } break; |