aboutsummaryrefslogtreecommitdiff
path: root/hw/char/omap_uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/char/omap_uart.c')
-rw-r--r--hw/char/omap_uart.c50
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;