[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 3/8] hw/char/escc2: Add character device backend
From: |
Jasper Lowell |
Subject: |
[PATCH 3/8] hw/char/escc2: Add character device backend |
Date: |
Wed, 17 Jun 2020 18:23:57 +1000 |
Signed-off-by: Jasper Lowell <jasper.lowell@bt.com>
---
hw/char/escc2.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 234 insertions(+)
diff --git a/hw/char/escc2.c b/hw/char/escc2.c
index 926346482f..e16049ce4d 100644
--- a/hw/char/escc2.c
+++ b/hw/char/escc2.c
@@ -7,6 +7,8 @@
*/
#include "qemu/osdep.h"
+#include "chardev/char-fe.h"
+#include "chardev/char-serial.h"
#include "hw/char/escc2.h"
#include "hw/irq.h"
#include "hw/isa/isa.h"
@@ -17,6 +19,13 @@
/* STAR. */
#define REGISTER_STAR_OFFSET 0x20
+#define REGISTER_STAR_XDOV 0x80
+#define REGISTER_STAR_XFW 0x40
+#define REGISTER_STAR_RFNE 0x20
+#define REGISTER_STAR_FCS 0x10
+#define REGISTER_STAR_TEC 0x8
+#define REGISTER_STAR_CEC 0x4
+#define REGISTER_STAR_CTS 0x2
/* CMDR. */
#define REGISTER_CMDR_OFFSET 0x20
@@ -38,9 +47,40 @@
/* DAFO. */
#define REGISTER_DAFO_OFFSET 0x27
+#define REGISTER_DAFO_XBRK 0x40
+#define REGISTER_DAFO_STOP 0x20
+#define REGISTER_DAFO_PAR1 0x10
+#define REGISTER_DAFO_PAR0 0x8
+#define REGISTER_DAFO_PARE 0x4
+#define REGISTER_DAFO_CHL1 0x2
+#define REGISTER_DAFO_CHL0 0x1
+
+#define REGISTER_DAFO_PAR_MASK \
+ (REGISTER_DAFO_PAR1 | REGISTER_DAFO_PAR0)
+#define REGISTER_DAFO_PAR_SPACE 0x0
+#define REGISTER_DAFO_PAR_ODD (REGISTER_DAFO_PAR0)
+#define REGISTER_DAFO_PAR_EVEN (REGISTER_DAFO_PAR1)
+#define REGISTER_DAFO_PAR_MARK \
+ (REGISTER_DAFO_PAR1 | REGISTER_DAFO_PAR0)
+#define REGISTER_DAFO_CHL_MASK \
+ (REGISTER_DAFO_CHL1 | REGISTER_DAFO_CHL0)
+#define REGISTER_DAFO_CHL_CS8 0x0
+#define REGISTER_DAFO_CHL_CS7 (REGISTER_DAFO_CHL0)
+#define REGISTER_DAFO_CHL_CS6 (REGISTER_DAFO_CHL1)
+#define REGISTER_DAFO_CHL_CS5 \
+ (REGISTER_DAFO_CHL1 | REGISTER_DAFO_CHL0)
/* RFC. */
#define REGISTER_RFC_OFFSET 0x28
+#define REGISTER_RFC_DPS 0x40
+#define REGISTER_RFC_DXS 0x20
+#define REGISTER_RFC_RFDF 0x10
+#define REGISTER_RFC_RFTH1 0x8
+#define REGISTER_RFC_RFTH0 0x4
+#define REGISTER_RFC_TCDE 0x1
+
+#define REGISTER_RFC_RFTH_MASK \
+ (REGISTER_RFC_RFTH1 | REGISTER_RFC_RFTH0)
/* RBCL. */
#define REGISTER_RBCL_OFFSET 0x2a
@@ -122,6 +162,14 @@
/* ISR0. */
#define REGISTER_ISR0_OFFSET 0x3a
+#define REGISTER_ISR0_TCD 0x80
+#define REGISTER_ISR0_TIME 0x40
+#define REGISTER_ISR0_PERR 0x20
+#define REGISTER_ISR0_FERR 0x10
+#define REGISTER_ISR0_PLLA 0x8
+#define REGISTER_ISR0_CDSC 0x4
+#define REGISTER_ISR0_RFO 0x2
+#define REGISTER_ISR0_RPF 0x1
/* IMR0. */
#define REGISTER_IMR0_OFFSET 0x3a
@@ -196,6 +244,8 @@ typedef struct ESCC2State ESCC2State;
typedef struct ESCC2ChannelState {
ESCC2State *controller;
+ CharBackend chardev;
+
/*
* The SAB 82532 ships with 64 byte FIFO queues for transmitting and
* receiving but only 32 bytes are addressable.
@@ -294,6 +344,102 @@ static void escc2_irq_update(ESCC2State *controller)
trace_escc2_irq_update(gis);
}
+static void escc2_channel_irq_event(ESCC2ChannelState *channel,
+ uint8_t status_register, uint8_t event)
+{
+ /*
+ * Ensure that event does not have more than one bit set when calling this
+ * function.
+ */
+ uint8_t mask, tmp;
+
+ switch (status_register) {
+ case REGISTER_ISR0:
+ mask = REGISTER_READ(channel, REGISTER_IMR0);
+ break;
+ case REGISTER_ISR1:
+ mask = REGISTER_READ(channel, REGISTER_IMR1);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if ((event & ~(mask))
+ || (REGISTER_READ(channel, REGISTER_IPC) & REGISTER_IPC_VIS)) {
+ tmp = REGISTER_READ(channel, status_register);
+ tmp |= event;
+ REGISTER_WRITE(channel, status_register, tmp);
+ }
+
+ if (event & ~(mask)) {
+ escc2_irq_update(channel->controller);
+ }
+}
+
+static void escc2_channel_parameters_update(ESCC2ChannelState *channel)
+{
+ uint8_t dafo;
+ QEMUSerialSetParams ssp;
+
+ if (!qemu_chr_fe_backend_connected(&channel->chardev)) {
+ return;
+ }
+
+ /* Check if parity is enabled. */
+ dafo = REGISTER_READ(channel, REGISTER_DAFO);
+ if (dafo & REGISTER_DAFO_PARE) {
+ /* Determine the parity. */
+ switch (dafo & REGISTER_DAFO_PAR_MASK) {
+ case REGISTER_DAFO_PAR_SPACE:
+ case REGISTER_DAFO_PAR_MARK:
+ /*
+ * XXX: QEMU doesn't support stick parity yet. Silently fail and
+ * fall to the next case.
+ */
+ case REGISTER_DAFO_PAR_ODD:
+ ssp.parity = 'O';
+ break;
+ case REGISTER_DAFO_PAR_EVEN:
+ ssp.parity = 'E';
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ } else {
+ ssp.parity = 'N';
+ }
+
+ /* Determine the number of data bits. */
+ switch (dafo & REGISTER_DAFO_CHL_MASK) {
+ case REGISTER_DAFO_CHL_CS8:
+ ssp.data_bits = 8;
+ break;
+ case REGISTER_DAFO_CHL_CS7:
+ ssp.data_bits = 7;
+ break;
+ case REGISTER_DAFO_CHL_CS6:
+ ssp.data_bits = 6;
+ break;
+ case REGISTER_DAFO_CHL_CS5:
+ ssp.data_bits = 5;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ /* Determine the number of stop bits. */
+ if (dafo & REGISTER_DAFO_STOP) {
+ ssp.stop_bits = 2;
+ } else {
+ ssp.stop_bits = 1;
+ }
+
+ /* XXX */
+ ssp.speed = 0;
+
+ qemu_chr_fe_ioctl(&channel->chardev, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
static void escc2_channel_reset(ESCC2ChannelState *channel)
{
unsigned int i;
@@ -469,6 +615,7 @@ static void escc2_mem_write(void *opaque, hwaddr addr,
uint64_t value,
break;
case REGISTER_DAFO_OFFSET:
REGISTER_WRITE(channel, REGISTER_DAFO, value);
+ escc2_channel_parameters_update(channel);
break;
case REGISTER_RFC_OFFSET:
REGISTER_WRITE(channel, REGISTER_RFC, value);
@@ -484,6 +631,7 @@ static void escc2_mem_write(void *opaque, hwaddr addr,
uint64_t value,
break;
case REGISTER_CCR1_OFFSET:
REGISTER_WRITE(channel, REGISTER_CCR1, value);
+ escc2_channel_parameters_update(channel);
break;
case REGISTER_CCR2_OFFSET:
REGISTER_WRITE(channel, REGISTER_CCR2, value);
@@ -505,9 +653,11 @@ static void escc2_mem_write(void *opaque, hwaddr addr,
uint64_t value,
break;
case REGISTER_BGR_OFFSET:
REGISTER_WRITE(channel, REGISTER_BGR, value);
+ escc2_channel_parameters_update(channel);
break;
case REGISTER_TIC_OFFSET:
REGISTER_WRITE(channel, REGISTER_TIC, value);
+ qemu_chr_fe_write_all(&channel->chardev, (uint8_t *)&value, 1);
break;
case REGISTER_MXN_OFFSET:
REGISTER_WRITE(channel, REGISTER_MXN, value);
@@ -542,6 +692,7 @@ static void escc2_mem_write(void *opaque, hwaddr addr,
uint64_t value,
break;
case REGISTER_CCR4_OFFSET:
REGISTER_WRITE(channel, REGISTER_CCR4, value);
+ escc2_channel_parameters_update(channel);
break;
default:
/* Registers do not exhaustively cover the addressable region. */
@@ -551,6 +702,74 @@ static void escc2_mem_write(void *opaque, hwaddr addr,
uint64_t value,
trace_escc2_mem_write(CHANNEL_CHAR(channel), offset, value);
}
+static unsigned int escc2_channel_rfifo_threshold(ESCC2ChannelState *channel)
+{
+ unsigned int threshold;
+
+ switch (REGISTER_READ(channel, REGISTER_RFC) & REGISTER_RFC_RFTH_MASK) {
+ case 0:
+ threshold = 1;
+ break;
+ case 1:
+ threshold = 4;
+ break;
+ case 2:
+ threshold = 16;
+ break;
+ case 3:
+ threshold = 32;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return threshold;
+}
+
+static int escc2_channel_chardev_can_receive(void *opaque)
+{
+ uint8_t tmp;
+ ESCC2ChannelState *channel = opaque;
+ unsigned int threshold = escc2_channel_rfifo_threshold(channel);
+
+ tmp = REGISTER_READ(channel, REGISTER_RBCL);
+ if (threshold > tmp) {
+ return threshold - tmp;
+ } else {
+ return 0;
+ }
+}
+
+static void escc2_channel_chardev_receive(void *opaque, const uint8_t *buf,
+ int size)
+{
+ uint8_t tmp, rbcl;
+ unsigned int i, nbytes;
+ ESCC2ChannelState *channel = opaque;
+
+ /* Determine the number of characters that can be safely consumed. */
+ rbcl = REGISTER_READ(channel, REGISTER_RBCL);
+ if (rbcl + size > CHANNEL_FIFO_LENGTH) {
+ nbytes = CHANNEL_FIFO_LENGTH - rbcl;
+ } else {
+ nbytes = size;
+ }
+
+ /* Consume characters. */
+ for (i = 0; i < nbytes; i++) {
+ channel->fifo_receive[rbcl + i] = buf[i];
+ }
+ REGISTER_WRITE(channel, REGISTER_RBCL, rbcl + nbytes);
+
+ tmp = REGISTER_READ(channel, REGISTER_STAR);
+ tmp |= REGISTER_STAR_RFNE;
+ REGISTER_WRITE(channel, REGISTER_STAR, tmp);
+
+ if (escc2_channel_chardev_can_receive(channel) == 0) {
+ escc2_channel_irq_event(channel, REGISTER_ISR0, REGISTER_ISR0_RPF);
+ }
+}
+
static void escc2_realize(DeviceState *dev, Error **errp)
{
unsigned int i;
@@ -560,6 +779,13 @@ static void escc2_realize(DeviceState *dev, Error **errp)
for (i = 0; i < CHANNEL_COUNT; i++) {
channel = &controller->channel[i];
channel->controller = controller;
+
+ if (qemu_chr_fe_backend_connected(&channel->chardev)) {
+ qemu_chr_fe_set_handlers(&channel->chardev,
+ escc2_channel_chardev_can_receive,
+ escc2_channel_chardev_receive, NULL, NULL, channel, NULL,
+ true);
+ }
}
qemu_register_reset(escc2_reset, controller);
@@ -605,7 +831,13 @@ static void escc2_isa_realize(DeviceState *dev, Error
**errp)
static void escc2_unrealize(DeviceState *dev)
{
+ unsigned int i;
ESCC2State *controller = ESCC2(dev);
+
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ qemu_chr_fe_deinit(&controller->channel[i].chardev, false);
+ }
+
qemu_unregister_reset(escc2_reset, controller);
}
@@ -618,6 +850,8 @@ static void escc2_isa_instance_init(Object *o)
}
static Property escc2_properties[] = {
+ DEFINE_PROP_CHR("chardevA", ESCC2State, channel[CHANNEL_A].chardev),
+ DEFINE_PROP_CHR("chardevB", ESCC2State, channel[CHANNEL_B].chardev),
DEFINE_PROP_END_OF_LIST()
};
--
2.26.2
- [PATCH 0/8] ESCC2, Jasper Lowell, 2020/06/17
- [PATCH 1/8] hw/char/escc2: Add device, Jasper Lowell, 2020/06/17
- [PATCH 2/8] hw/char/escc2: Handle interrupt generation, Jasper Lowell, 2020/06/17
- [PATCH 3/8] hw/char/escc2: Add character device backend,
Jasper Lowell <=
- [PATCH 5/8] hw/char/escc2: Add Receiver Reset (RRES) command, Jasper Lowell, 2020/06/17
- [PATCH 4/8] hw/char/escc2: Add clock generation, Jasper Lowell, 2020/06/17
- [PATCH 6/8] hw/char/escc2: Add RFRD command, Jasper Lowell, 2020/06/17
- [PATCH 7/8] hw/char/escc2: Add Transmit Frame (XF) command, Jasper Lowell, 2020/06/17
- [PATCH 8/8] hw/char/escc2: Add XRES command, Jasper Lowell, 2020/06/17
- Re: [PATCH 0/8] ESCC2, no-reply, 2020/06/17
- Re: [PATCH 0/8] ESCC2, Philippe Mathieu-Daudé, 2020/06/17
- Re: [PATCH 0/8] ESCC2, Artyom Tarasenko, 2020/06/17