diff options
Diffstat (limited to 'qemu/hw/char/ipoctal232.c')
-rw-r--r-- | qemu/hw/char/ipoctal232.c | 604 |
1 files changed, 0 insertions, 604 deletions
diff --git a/qemu/hw/char/ipoctal232.c b/qemu/hw/char/ipoctal232.c deleted file mode 100644 index bc0ae4980..000000000 --- a/qemu/hw/char/ipoctal232.c +++ /dev/null @@ -1,604 +0,0 @@ -/* - * QEMU GE IP-Octal 232 IndustryPack emulation - * - * Copyright (C) 2012 Igalia, S.L. - * Author: Alberto Garcia <agarcia@igalia.com> - * - * This code is licensed under the GNU GPL v2 or (at your option) any - * later version. - */ - -#include "qemu/osdep.h" -#include "hw/ipack/ipack.h" -#include "qemu/bitops.h" -#include "sysemu/char.h" - -/* #define DEBUG_IPOCTAL */ - -#ifdef DEBUG_IPOCTAL -#define DPRINTF2(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF2(fmt, ...) do { } while (0) -#endif - -#define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__) - -#define RX_FIFO_SIZE 3 - -/* The IP-Octal has 8 channels (a-h) - divided into 4 blocks (A-D) */ -#define N_CHANNELS 8 -#define N_BLOCKS 4 - -#define REG_MRa 0x01 -#define REG_MRb 0x11 -#define REG_SRa 0x03 -#define REG_SRb 0x13 -#define REG_CSRa 0x03 -#define REG_CSRb 0x13 -#define REG_CRa 0x05 -#define REG_CRb 0x15 -#define REG_RHRa 0x07 -#define REG_RHRb 0x17 -#define REG_THRa 0x07 -#define REG_THRb 0x17 -#define REG_ACR 0x09 -#define REG_ISR 0x0B -#define REG_IMR 0x0B -#define REG_OPCR 0x1B - -#define CR_ENABLE_RX BIT(0) -#define CR_DISABLE_RX BIT(1) -#define CR_ENABLE_TX BIT(2) -#define CR_DISABLE_TX BIT(3) -#define CR_CMD(cr) ((cr) >> 4) -#define CR_NO_OP 0 -#define CR_RESET_MR 1 -#define CR_RESET_RX 2 -#define CR_RESET_TX 3 -#define CR_RESET_ERR 4 -#define CR_RESET_BRKINT 5 -#define CR_START_BRK 6 -#define CR_STOP_BRK 7 -#define CR_ASSERT_RTSN 8 -#define CR_NEGATE_RTSN 9 -#define CR_TIMEOUT_ON 10 -#define CR_TIMEOUT_OFF 12 - -#define SR_RXRDY BIT(0) -#define SR_FFULL BIT(1) -#define SR_TXRDY BIT(2) -#define SR_TXEMT BIT(3) -#define SR_OVERRUN BIT(4) -#define SR_PARITY BIT(5) -#define SR_FRAMING BIT(6) -#define SR_BREAK BIT(7) - -#define ISR_TXRDYA BIT(0) -#define ISR_RXRDYA BIT(1) -#define ISR_BREAKA BIT(2) -#define ISR_CNTRDY BIT(3) -#define ISR_TXRDYB BIT(4) -#define ISR_RXRDYB BIT(5) -#define ISR_BREAKB BIT(6) -#define ISR_MPICHG BIT(7) -#define ISR_TXRDY(CH) (((CH) & 1) ? BIT(4) : BIT(0)) -#define ISR_RXRDY(CH) (((CH) & 1) ? BIT(5) : BIT(1)) -#define ISR_BREAK(CH) (((CH) & 1) ? BIT(6) : BIT(2)) - -typedef struct IPOctalState IPOctalState; -typedef struct SCC2698Channel SCC2698Channel; -typedef struct SCC2698Block SCC2698Block; - -struct SCC2698Channel { - IPOctalState *ipoctal; - CharDriverState *dev; - bool rx_enabled; - uint8_t mr[2]; - uint8_t mr_idx; - uint8_t sr; - uint8_t rhr[RX_FIFO_SIZE]; - uint8_t rhr_idx; - uint8_t rx_pending; -}; - -struct SCC2698Block { - uint8_t imr; - uint8_t isr; -}; - -struct IPOctalState { - IPackDevice parent_obj; - - SCC2698Channel ch[N_CHANNELS]; - SCC2698Block blk[N_BLOCKS]; - uint8_t irq_vector; -}; - -#define TYPE_IPOCTAL "ipoctal232" - -#define IPOCTAL(obj) \ - OBJECT_CHECK(IPOctalState, (obj), TYPE_IPOCTAL) - -static const VMStateDescription vmstate_scc2698_channel = { - .name = "scc2698_channel", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(rx_enabled, SCC2698Channel), - VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2), - VMSTATE_UINT8(mr_idx, SCC2698Channel), - VMSTATE_UINT8(sr, SCC2698Channel), - VMSTATE_UINT8_ARRAY(rhr, SCC2698Channel, RX_FIFO_SIZE), - VMSTATE_UINT8(rhr_idx, SCC2698Channel), - VMSTATE_UINT8(rx_pending, SCC2698Channel), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_scc2698_block = { - .name = "scc2698_block", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(imr, SCC2698Block), - VMSTATE_UINT8(isr, SCC2698Block), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ipoctal = { - .name = "ipoctal232", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_IPACK_DEVICE(parent_obj, IPOctalState), - VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1, - vmstate_scc2698_channel, SCC2698Channel), - VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1, - vmstate_scc2698_block, SCC2698Block), - VMSTATE_UINT8(irq_vector, IPOctalState), - VMSTATE_END_OF_LIST() - } -}; - -/* data[10] is 0x0C, not 0x0B as the doc says */ -static const uint8_t id_prom_data[] = { - 0x49, 0x50, 0x41, 0x43, 0xF0, 0x22, - 0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC -}; - -static void update_irq(IPOctalState *dev, unsigned block) -{ - IPackDevice *idev = IPACK_DEVICE(dev); - /* Blocks A and B interrupt on INT0#, C and D on INT1#. - Thus, to get the status we have to check two blocks. */ - SCC2698Block *blk0 = &dev->blk[block]; - SCC2698Block *blk1 = &dev->blk[block^1]; - unsigned intno = block / 2; - - if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) { - qemu_irq_raise(idev->irq[intno]); - } else { - qemu_irq_lower(idev->irq[intno]); - } -} - -static void write_cr(IPOctalState *dev, unsigned channel, uint8_t val) -{ - SCC2698Channel *ch = &dev->ch[channel]; - SCC2698Block *blk = &dev->blk[channel / 2]; - - DPRINTF("Write CR%c %u: ", channel + 'a', val); - - /* The lower 4 bits are used to enable and disable Tx and Rx */ - if (val & CR_ENABLE_RX) { - DPRINTF2("Rx on, "); - ch->rx_enabled = true; - } - if (val & CR_DISABLE_RX) { - DPRINTF2("Rx off, "); - ch->rx_enabled = false; - } - if (val & CR_ENABLE_TX) { - DPRINTF2("Tx on, "); - ch->sr |= SR_TXRDY | SR_TXEMT; - blk->isr |= ISR_TXRDY(channel); - } - if (val & CR_DISABLE_TX) { - DPRINTF2("Tx off, "); - ch->sr &= ~(SR_TXRDY | SR_TXEMT); - blk->isr &= ~ISR_TXRDY(channel); - } - - DPRINTF2("cmd: "); - - /* The rest of the bits implement different commands */ - switch (CR_CMD(val)) { - case CR_NO_OP: - DPRINTF2("none"); - break; - case CR_RESET_MR: - DPRINTF2("reset MR"); - ch->mr_idx = 0; - break; - case CR_RESET_RX: - DPRINTF2("reset Rx"); - ch->rx_enabled = false; - ch->rx_pending = 0; - ch->sr &= ~SR_RXRDY; - blk->isr &= ~ISR_RXRDY(channel); - break; - case CR_RESET_TX: - DPRINTF2("reset Tx"); - ch->sr &= ~(SR_TXRDY | SR_TXEMT); - blk->isr &= ~ISR_TXRDY(channel); - break; - case CR_RESET_ERR: - DPRINTF2("reset err"); - ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK); - break; - case CR_RESET_BRKINT: - DPRINTF2("reset brk ch int"); - blk->isr &= ~(ISR_BREAKA | ISR_BREAKB); - break; - default: - DPRINTF2("unsupported 0x%x", CR_CMD(val)); - } - - DPRINTF2("\n"); -} - -static uint16_t io_read(IPackDevice *ip, uint8_t addr) -{ - IPOctalState *dev = IPOCTAL(ip); - uint16_t ret = 0; - /* addr[7:6]: block (A-D) - addr[7:5]: channel (a-h) - addr[5:0]: register */ - unsigned block = addr >> 5; - unsigned channel = addr >> 4; - /* Big endian, accessed using 8-bit bytes at odd locations */ - unsigned offset = (addr & 0x1F) ^ 1; - SCC2698Channel *ch = &dev->ch[channel]; - SCC2698Block *blk = &dev->blk[block]; - uint8_t old_isr = blk->isr; - - switch (offset) { - - case REG_MRa: - case REG_MRb: - ret = ch->mr[ch->mr_idx]; - DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret); - ch->mr_idx = 1; - break; - - case REG_SRa: - case REG_SRb: - ret = ch->sr; - DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret); - break; - - case REG_RHRa: - case REG_RHRb: - ret = ch->rhr[ch->rhr_idx]; - if (ch->rx_pending > 0) { - ch->rx_pending--; - if (ch->rx_pending == 0) { - ch->sr &= ~SR_RXRDY; - blk->isr &= ~ISR_RXRDY(channel); - if (ch->dev) { - qemu_chr_accept_input(ch->dev); - } - } else { - ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE; - } - if (ch->sr & SR_BREAK) { - ch->sr &= ~SR_BREAK; - blk->isr |= ISR_BREAK(channel); - } - } - DPRINTF("Read RHR%c (0x%x)\n", channel + 'a', ret); - break; - - case REG_ISR: - ret = blk->isr; - DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret); - break; - - default: - DPRINTF("Read unknown/unsupported register 0x%02x\n", offset); - } - - if (old_isr != blk->isr) { - update_irq(dev, block); - } - - return ret; -} - -static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val) -{ - IPOctalState *dev = IPOCTAL(ip); - unsigned reg = val & 0xFF; - /* addr[7:6]: block (A-D) - addr[7:5]: channel (a-h) - addr[5:0]: register */ - unsigned block = addr >> 5; - unsigned channel = addr >> 4; - /* Big endian, accessed using 8-bit bytes at odd locations */ - unsigned offset = (addr & 0x1F) ^ 1; - SCC2698Channel *ch = &dev->ch[channel]; - SCC2698Block *blk = &dev->blk[block]; - uint8_t old_isr = blk->isr; - uint8_t old_imr = blk->imr; - - switch (offset) { - - case REG_MRa: - case REG_MRb: - ch->mr[ch->mr_idx] = reg; - DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg); - ch->mr_idx = 1; - break; - - /* Not implemented */ - case REG_CSRa: - case REG_CSRb: - DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg); - break; - - case REG_CRa: - case REG_CRb: - write_cr(dev, channel, reg); - break; - - case REG_THRa: - case REG_THRb: - if (ch->sr & SR_TXRDY) { - DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg); - if (ch->dev) { - uint8_t thr = reg; - qemu_chr_fe_write(ch->dev, &thr, 1); - } - } else { - DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg); - } - break; - - /* Not implemented */ - case REG_ACR: - DPRINTF("Write ACR%c 0x%x\n", block + 'A', val); - break; - - case REG_IMR: - DPRINTF("Write IMR%c 0x%x\n", block + 'A', val); - blk->imr = reg; - break; - - /* Not implemented */ - case REG_OPCR: - DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val); - break; - - default: - DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val); - } - - if (old_isr != blk->isr || old_imr != blk->imr) { - update_irq(dev, block); - } -} - -static uint16_t id_read(IPackDevice *ip, uint8_t addr) -{ - uint16_t ret = 0; - unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */ - - if (pos < ARRAY_SIZE(id_prom_data)) { - ret = id_prom_data[pos]; - } else { - DPRINTF("Attempt to read unavailable PROM data at 0x%x\n", addr); - } - - return ret; -} - -static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val) -{ - IPOctalState *dev = IPOCTAL(ip); - if (addr == 1) { - DPRINTF("Write IRQ vector: %u\n", (unsigned) val); - dev->irq_vector = val; /* Undocumented, but the hw works like that */ - } else { - DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); - } -} - -static uint16_t int_read(IPackDevice *ip, uint8_t addr) -{ - IPOctalState *dev = IPOCTAL(ip); - /* Read address 0 to ACK INT0# and address 2 to ACK INT1# */ - if (addr != 0 && addr != 2) { - DPRINTF("Attempt to read from 0x%x\n", addr); - return 0; - } else { - /* Update interrupts if necessary */ - update_irq(dev, addr); - return dev->irq_vector; - } -} - -static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val) -{ - DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); -} - -static uint16_t mem_read16(IPackDevice *ip, uint32_t addr) -{ - DPRINTF("Attempt to read from 0x%x\n", addr); - return 0; -} - -static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val) -{ - DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); -} - -static uint8_t mem_read8(IPackDevice *ip, uint32_t addr) -{ - DPRINTF("Attempt to read from 0x%x\n", addr); - return 0; -} - -static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val) -{ - IPOctalState *dev = IPOCTAL(ip); - if (addr == 1) { - DPRINTF("Write IRQ vector: %u\n", (unsigned) val); - dev->irq_vector = val; - } else { - DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); - } -} - -static int hostdev_can_receive(void *opaque) -{ - SCC2698Channel *ch = opaque; - int available_bytes = RX_FIFO_SIZE - ch->rx_pending; - return ch->rx_enabled ? available_bytes : 0; -} - -static void hostdev_receive(void *opaque, const uint8_t *buf, int size) -{ - SCC2698Channel *ch = opaque; - IPOctalState *dev = ch->ipoctal; - unsigned pos = ch->rhr_idx + ch->rx_pending; - int i; - - assert(size + ch->rx_pending <= RX_FIFO_SIZE); - - /* Copy data to the RxFIFO */ - for (i = 0; i < size; i++) { - pos %= RX_FIFO_SIZE; - ch->rhr[pos++] = buf[i]; - } - - ch->rx_pending += size; - - /* If the RxFIFO was empty raise an interrupt */ - if (!(ch->sr & SR_RXRDY)) { - unsigned block, channel = 0; - /* Find channel number to update the ISR register */ - while (&dev->ch[channel] != ch) { - channel++; - } - block = channel / 2; - dev->blk[block].isr |= ISR_RXRDY(channel); - ch->sr |= SR_RXRDY; - update_irq(dev, block); - } -} - -static void hostdev_event(void *opaque, int event) -{ - SCC2698Channel *ch = opaque; - switch (event) { - case CHR_EVENT_OPENED: - DPRINTF("Device %s opened\n", ch->dev->label); - break; - case CHR_EVENT_BREAK: { - uint8_t zero = 0; - DPRINTF("Device %s received break\n", ch->dev->label); - - if (!(ch->sr & SR_BREAK)) { - IPOctalState *dev = ch->ipoctal; - unsigned block, channel = 0; - - while (&dev->ch[channel] != ch) { - channel++; - } - block = channel / 2; - - ch->sr |= SR_BREAK; - dev->blk[block].isr |= ISR_BREAK(channel); - } - - /* Put a zero character in the buffer */ - hostdev_receive(ch, &zero, 1); - } - break; - default: - DPRINTF("Device %s received event %d\n", ch->dev->label, event); - } -} - -static void ipoctal_realize(DeviceState *dev, Error **errp) -{ - IPOctalState *s = IPOCTAL(dev); - unsigned i; - - for (i = 0; i < N_CHANNELS; i++) { - SCC2698Channel *ch = &s->ch[i]; - ch->ipoctal = s; - - /* Redirect IP-Octal channels to host character devices */ - if (ch->dev) { - qemu_chr_add_handlers(ch->dev, hostdev_can_receive, - hostdev_receive, hostdev_event, ch); - DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label); - } else { - DPRINTF("Could not redirect channel %u, no chardev set\n", i); - } - } -} - -static Property ipoctal_properties[] = { - DEFINE_PROP_CHR("chardev0", IPOctalState, ch[0].dev), - DEFINE_PROP_CHR("chardev1", IPOctalState, ch[1].dev), - DEFINE_PROP_CHR("chardev2", IPOctalState, ch[2].dev), - DEFINE_PROP_CHR("chardev3", IPOctalState, ch[3].dev), - DEFINE_PROP_CHR("chardev4", IPOctalState, ch[4].dev), - DEFINE_PROP_CHR("chardev5", IPOctalState, ch[5].dev), - DEFINE_PROP_CHR("chardev6", IPOctalState, ch[6].dev), - DEFINE_PROP_CHR("chardev7", IPOctalState, ch[7].dev), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ipoctal_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass); - - ic->realize = ipoctal_realize; - ic->io_read = io_read; - ic->io_write = io_write; - ic->id_read = id_read; - ic->id_write = id_write; - ic->int_read = int_read; - ic->int_write = int_write; - ic->mem_read16 = mem_read16; - ic->mem_write16 = mem_write16; - ic->mem_read8 = mem_read8; - ic->mem_write8 = mem_write8; - - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->desc = "GE IP-Octal 232 8-channel RS-232 IndustryPack"; - dc->props = ipoctal_properties; - dc->vmsd = &vmstate_ipoctal; -} - -static const TypeInfo ipoctal_info = { - .name = TYPE_IPOCTAL, - .parent = TYPE_IPACK_DEVICE, - .instance_size = sizeof(IPOctalState), - .class_init = ipoctal_class_init, -}; - -static void ipoctal_register_types(void) -{ - type_register_static(&ipoctal_info); -} - -type_init(ipoctal_register_types) |