diff options
Diffstat (limited to 'qemu/hw/dma')
-rw-r--r-- | qemu/hw/dma/Makefile.objs | 14 | ||||
-rw-r--r-- | qemu/hw/dma/bcm2835_dma.c | 409 | ||||
-rw-r--r-- | qemu/hw/dma/etraxfs_dma.c | 783 | ||||
-rw-r--r-- | qemu/hw/dma/i82374.c | 156 | ||||
-rw-r--r-- | qemu/hw/dma/i8257.c | 643 | ||||
-rw-r--r-- | qemu/hw/dma/omap_dma.c | 2103 | ||||
-rw-r--r-- | qemu/hw/dma/pl080.c | 423 | ||||
-rw-r--r-- | qemu/hw/dma/pl330.c | 1668 | ||||
-rw-r--r-- | qemu/hw/dma/puv3_dma.c | 114 | ||||
-rw-r--r-- | qemu/hw/dma/pxa2xx_dma.c | 576 | ||||
-rw-r--r-- | qemu/hw/dma/rc4030.c | 842 | ||||
-rw-r--r-- | qemu/hw/dma/soc_dma.c | 362 | ||||
-rw-r--r-- | qemu/hw/dma/sparc32_dma.c | 323 | ||||
-rw-r--r-- | qemu/hw/dma/sun4m_iommu.c | 393 | ||||
-rw-r--r-- | qemu/hw/dma/xilinx_axidma.c | 666 |
15 files changed, 0 insertions, 9475 deletions
diff --git a/qemu/hw/dma/Makefile.objs b/qemu/hw/dma/Makefile.objs deleted file mode 100644 index a1abbcf74..000000000 --- a/qemu/hw/dma/Makefile.objs +++ /dev/null @@ -1,14 +0,0 @@ -common-obj-$(CONFIG_PUV3) += puv3_dma.o -common-obj-$(CONFIG_RC4030) += rc4030.o -common-obj-$(CONFIG_PL080) += pl080.o -common-obj-$(CONFIG_PL330) += pl330.o -common-obj-$(CONFIG_I82374) += i82374.o -common-obj-$(CONFIG_I8257) += i8257.o -common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o -common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o -common-obj-$(CONFIG_STP2000) += sparc32_dma.o -common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o - -obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o -obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o -obj-$(CONFIG_RASPI) += bcm2835_dma.o diff --git a/qemu/hw/dma/bcm2835_dma.c b/qemu/hw/dma/bcm2835_dma.c deleted file mode 100644 index 542117599..000000000 --- a/qemu/hw/dma/bcm2835_dma.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Raspberry Pi emulation (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/dma/bcm2835_dma.h" - -/* DMA CS Control and Status bits */ -#define BCM2708_DMA_ACTIVE (1 << 0) -#define BCM2708_DMA_END (1 << 1) /* GE */ -#define BCM2708_DMA_INT (1 << 2) -#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ -#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ -#define BCM2708_DMA_ERR (1 << 8) -#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ -#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ - -/* DMA control block "info" field bits */ -#define BCM2708_DMA_INT_EN (1 << 0) -#define BCM2708_DMA_TDMODE (1 << 1) -#define BCM2708_DMA_WAIT_RESP (1 << 3) -#define BCM2708_DMA_D_INC (1 << 4) -#define BCM2708_DMA_D_WIDTH (1 << 5) -#define BCM2708_DMA_D_DREQ (1 << 6) -#define BCM2708_DMA_D_IGNORE (1 << 7) -#define BCM2708_DMA_S_INC (1 << 8) -#define BCM2708_DMA_S_WIDTH (1 << 9) -#define BCM2708_DMA_S_DREQ (1 << 10) -#define BCM2708_DMA_S_IGNORE (1 << 11) - -/* Register offsets */ -#define BCM2708_DMA_CS 0x00 /* Control and Status */ -#define BCM2708_DMA_ADDR 0x04 /* Control block address */ -/* the current control block appears in the following registers - read only */ -#define BCM2708_DMA_INFO 0x08 -#define BCM2708_DMA_SOURCE_AD 0x0c -#define BCM2708_DMA_DEST_AD 0x10 -#define BCM2708_DMA_TXFR_LEN 0x14 -#define BCM2708_DMA_STRIDE 0x18 -#define BCM2708_DMA_NEXTCB 0x1C -#define BCM2708_DMA_DEBUG 0x20 - -#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */ -#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */ - -#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */ - -static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c) -{ - BCM2835DMAChan *ch = &s->chan[c]; - uint32_t data, xlen, ylen; - int16_t dst_stride, src_stride; - - if (!(s->enable & (1 << c))) { - return; - } - - while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) { - /* CB fetch */ - ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad); - ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4); - ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8); - ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12); - ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16); - ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20); - - if (ch->ti & BCM2708_DMA_TDMODE) { - /* 2D transfer mode */ - ylen = (ch->txfr_len >> 16) & 0x3fff; - xlen = ch->txfr_len & 0xffff; - dst_stride = ch->stride >> 16; - src_stride = ch->stride & 0xffff; - } else { - ylen = 1; - xlen = ch->txfr_len; - dst_stride = 0; - src_stride = 0; - } - - while (ylen != 0) { - /* Normal transfer mode */ - while (xlen != 0) { - if (ch->ti & BCM2708_DMA_S_IGNORE) { - /* Ignore reads */ - data = 0; - } else { - data = ldl_le_phys(&s->dma_as, ch->source_ad); - } - if (ch->ti & BCM2708_DMA_S_INC) { - ch->source_ad += 4; - } - - if (ch->ti & BCM2708_DMA_D_IGNORE) { - /* Ignore writes */ - } else { - stl_le_phys(&s->dma_as, ch->dest_ad, data); - } - if (ch->ti & BCM2708_DMA_D_INC) { - ch->dest_ad += 4; - } - - /* update remaining transfer length */ - xlen -= 4; - if (ch->ti & BCM2708_DMA_TDMODE) { - ch->txfr_len = (ylen << 16) | xlen; - } else { - ch->txfr_len = xlen; - } - } - - if (--ylen != 0) { - ch->source_ad += src_stride; - ch->dest_ad += dst_stride; - } - } - ch->cs |= BCM2708_DMA_END; - if (ch->ti & BCM2708_DMA_INT_EN) { - ch->cs |= BCM2708_DMA_INT; - s->int_status |= (1 << c); - qemu_set_irq(ch->irq, 1); - } - - /* Process next CB */ - ch->conblk_ad = ch->nextconbk; - } - - ch->cs &= ~BCM2708_DMA_ACTIVE; - ch->cs |= BCM2708_DMA_ISPAUSED; -} - -static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch) -{ - ch->cs = 0; - ch->conblk_ad = 0; -} - -static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset, - unsigned size, unsigned c) -{ - BCM2835DMAChan *ch; - uint32_t res = 0; - - assert(size == 4); - assert(c < BCM2835_DMA_NCHANS); - - ch = &s->chan[c]; - - switch (offset) { - case BCM2708_DMA_CS: - res = ch->cs; - break; - case BCM2708_DMA_ADDR: - res = ch->conblk_ad; - break; - case BCM2708_DMA_INFO: - res = ch->ti; - break; - case BCM2708_DMA_SOURCE_AD: - res = ch->source_ad; - break; - case BCM2708_DMA_DEST_AD: - res = ch->dest_ad; - break; - case BCM2708_DMA_TXFR_LEN: - res = ch->txfr_len; - break; - case BCM2708_DMA_STRIDE: - res = ch->stride; - break; - case BCM2708_DMA_NEXTCB: - res = ch->nextconbk; - break; - case BCM2708_DMA_DEBUG: - res = ch->debug; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", - __func__, offset); - break; - } - return res; -} - -static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset, - uint64_t value, unsigned size, unsigned c) -{ - BCM2835DMAChan *ch; - uint32_t oldcs; - - assert(size == 4); - assert(c < BCM2835_DMA_NCHANS); - - ch = &s->chan[c]; - - switch (offset) { - case BCM2708_DMA_CS: - oldcs = ch->cs; - if (value & BCM2708_DMA_RESET) { - bcm2835_dma_chan_reset(ch); - } - if (value & BCM2708_DMA_ABORT) { - /* abort is a no-op, since we always run to completion */ - } - if (value & BCM2708_DMA_END) { - ch->cs &= ~BCM2708_DMA_END; - } - if (value & BCM2708_DMA_INT) { - ch->cs &= ~BCM2708_DMA_INT; - s->int_status &= ~(1 << c); - qemu_set_irq(ch->irq, 0); - } - ch->cs &= ~BCM2708_DMA_CS_RW_MASK; - ch->cs |= (value & BCM2708_DMA_CS_RW_MASK); - if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) { - bcm2835_dma_update(s, c); - } - break; - case BCM2708_DMA_ADDR: - ch->conblk_ad = value; - break; - case BCM2708_DMA_DEBUG: - ch->debug = value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", - __func__, offset); - break; - } -} - -static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size) -{ - BCM2835DMAState *s = opaque; - - if (offset < 0xf00) { - return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf); - } else { - switch (offset) { - case BCM2708_DMA_INT_STATUS: - return s->int_status; - case BCM2708_DMA_ENABLE: - return s->enable; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", - __func__, offset); - return 0; - } - } -} - -static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size) -{ - return bcm2835_dma_read(opaque, (offset & 0xff), size, 15); -} - -static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - BCM2835DMAState *s = opaque; - - if (offset < 0xf00) { - bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf); - } else { - switch (offset) { - case BCM2708_DMA_INT_STATUS: - break; - case BCM2708_DMA_ENABLE: - s->enable = (value & 0xffff); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", - __func__, offset); - } - } - -} - -static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15); -} - -static const MemoryRegionOps bcm2835_dma0_ops = { - .read = bcm2835_dma0_read, - .write = bcm2835_dma0_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid.min_access_size = 4, - .valid.max_access_size = 4, -}; - -static const MemoryRegionOps bcm2835_dma15_ops = { - .read = bcm2835_dma15_read, - .write = bcm2835_dma15_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid.min_access_size = 4, - .valid.max_access_size = 4, -}; - -static const VMStateDescription vmstate_bcm2835_dma_chan = { - .name = TYPE_BCM2835_DMA "-chan", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cs, BCM2835DMAChan), - VMSTATE_UINT32(conblk_ad, BCM2835DMAChan), - VMSTATE_UINT32(ti, BCM2835DMAChan), - VMSTATE_UINT32(source_ad, BCM2835DMAChan), - VMSTATE_UINT32(dest_ad, BCM2835DMAChan), - VMSTATE_UINT32(txfr_len, BCM2835DMAChan), - VMSTATE_UINT32(stride, BCM2835DMAChan), - VMSTATE_UINT32(nextconbk, BCM2835DMAChan), - VMSTATE_UINT32(debug, BCM2835DMAChan), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_bcm2835_dma = { - .name = TYPE_BCM2835_DMA, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1, - vmstate_bcm2835_dma_chan, BCM2835DMAChan), - VMSTATE_UINT32(int_status, BCM2835DMAState), - VMSTATE_UINT32(enable, BCM2835DMAState), - VMSTATE_END_OF_LIST() - } -}; - -static void bcm2835_dma_init(Object *obj) -{ - BCM2835DMAState *s = BCM2835_DMA(obj); - int n; - - /* DMA channels 0-14 occupy a contiguous block of IO memory, along - * with the global enable and interrupt status bits. Channel 15 - * has the same register map, but is mapped at a discontiguous - * address in a separate IO block. - */ - memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s, - TYPE_BCM2835_DMA, 0x1000); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0); - - memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s, - TYPE_BCM2835_DMA "-chan15", 0x100); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15); - - for (n = 0; n < 16; n++) { - sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq); - } -} - -static void bcm2835_dma_reset(DeviceState *dev) -{ - BCM2835DMAState *s = BCM2835_DMA(dev); - int n; - - s->enable = 0xffff; - s->int_status = 0; - for (n = 0; n < BCM2835_DMA_NCHANS; n++) { - bcm2835_dma_chan_reset(&s->chan[n]); - } -} - -static void bcm2835_dma_realize(DeviceState *dev, Error **errp) -{ - BCM2835DMAState *s = BCM2835_DMA(dev); - Error *err = NULL; - Object *obj; - - obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); - if (obj == NULL) { - error_setg(errp, "%s: required dma-mr link not found: %s", - __func__, error_get_pretty(err)); - return; - } - - s->dma_mr = MEMORY_REGION(obj); - address_space_init(&s->dma_as, s->dma_mr, NULL); - - bcm2835_dma_reset(dev); -} - -static void bcm2835_dma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = bcm2835_dma_realize; - dc->reset = bcm2835_dma_reset; - dc->vmsd = &vmstate_bcm2835_dma; -} - -static TypeInfo bcm2835_dma_info = { - .name = TYPE_BCM2835_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(BCM2835DMAState), - .class_init = bcm2835_dma_class_init, - .instance_init = bcm2835_dma_init, -}; - -static void bcm2835_dma_register_types(void) -{ - type_register_static(&bcm2835_dma_info); -} - -type_init(bcm2835_dma_register_types) diff --git a/qemu/hw/dma/etraxfs_dma.c b/qemu/hw/dma/etraxfs_dma.c deleted file mode 100644 index d5650eb88..000000000 --- a/qemu/hw/dma/etraxfs_dma.c +++ /dev/null @@ -1,783 +0,0 @@ -/* - * QEMU ETRAX DMA Controller. - * - * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "exec/address-spaces.h" -#include "qemu-common.h" -#include "sysemu/sysemu.h" - -#include "hw/cris/etraxfs_dma.h" - -#define D(x) - -#define RW_DATA (0x0 / 4) -#define RW_SAVED_DATA (0x58 / 4) -#define RW_SAVED_DATA_BUF (0x5c / 4) -#define RW_GROUP (0x60 / 4) -#define RW_GROUP_DOWN (0x7c / 4) -#define RW_CMD (0x80 / 4) -#define RW_CFG (0x84 / 4) -#define RW_STAT (0x88 / 4) -#define RW_INTR_MASK (0x8c / 4) -#define RW_ACK_INTR (0x90 / 4) -#define R_INTR (0x94 / 4) -#define R_MASKED_INTR (0x98 / 4) -#define RW_STREAM_CMD (0x9c / 4) - -#define DMA_REG_MAX (0x100 / 4) - -/* descriptors */ - -// ------------------------------------------------------------ dma_descr_group -typedef struct dma_descr_group { - uint32_t next; - unsigned eol : 1; - unsigned tol : 1; - unsigned bol : 1; - unsigned : 1; - unsigned intr : 1; - unsigned : 2; - unsigned en : 1; - unsigned : 7; - unsigned dis : 1; - unsigned md : 16; - struct dma_descr_group *up; - union { - struct dma_descr_context *context; - struct dma_descr_group *group; - } down; -} dma_descr_group; - -// ---------------------------------------------------------- dma_descr_context -typedef struct dma_descr_context { - uint32_t next; - unsigned eol : 1; - unsigned : 3; - unsigned intr : 1; - unsigned : 1; - unsigned store_mode : 1; - unsigned en : 1; - unsigned : 7; - unsigned dis : 1; - unsigned md0 : 16; - unsigned md1; - unsigned md2; - unsigned md3; - unsigned md4; - uint32_t saved_data; - uint32_t saved_data_buf; -} dma_descr_context; - -// ------------------------------------------------------------- dma_descr_data -typedef struct dma_descr_data { - uint32_t next; - uint32_t buf; - unsigned eol : 1; - unsigned : 2; - unsigned out_eop : 1; - unsigned intr : 1; - unsigned wait : 1; - unsigned : 2; - unsigned : 3; - unsigned in_eop : 1; - unsigned : 4; - unsigned md : 16; - uint32_t after; -} dma_descr_data; - -/* Constants */ -enum { - regk_dma_ack_pkt = 0x00000100, - regk_dma_anytime = 0x00000001, - regk_dma_array = 0x00000008, - regk_dma_burst = 0x00000020, - regk_dma_client = 0x00000002, - regk_dma_copy_next = 0x00000010, - regk_dma_copy_up = 0x00000020, - regk_dma_data_at_eol = 0x00000001, - regk_dma_dis_c = 0x00000010, - regk_dma_dis_g = 0x00000020, - regk_dma_idle = 0x00000001, - regk_dma_intern = 0x00000004, - regk_dma_load_c = 0x00000200, - regk_dma_load_c_n = 0x00000280, - regk_dma_load_c_next = 0x00000240, - regk_dma_load_d = 0x00000140, - regk_dma_load_g = 0x00000300, - regk_dma_load_g_down = 0x000003c0, - regk_dma_load_g_next = 0x00000340, - regk_dma_load_g_up = 0x00000380, - regk_dma_next_en = 0x00000010, - regk_dma_next_pkt = 0x00000010, - regk_dma_no = 0x00000000, - regk_dma_only_at_wait = 0x00000000, - regk_dma_restore = 0x00000020, - regk_dma_rst = 0x00000001, - regk_dma_running = 0x00000004, - regk_dma_rw_cfg_default = 0x00000000, - regk_dma_rw_cmd_default = 0x00000000, - regk_dma_rw_intr_mask_default = 0x00000000, - regk_dma_rw_stat_default = 0x00000101, - regk_dma_rw_stream_cmd_default = 0x00000000, - regk_dma_save_down = 0x00000020, - regk_dma_save_up = 0x00000020, - regk_dma_set_reg = 0x00000050, - regk_dma_set_w_size1 = 0x00000190, - regk_dma_set_w_size2 = 0x000001a0, - regk_dma_set_w_size4 = 0x000001c0, - regk_dma_stopped = 0x00000002, - regk_dma_store_c = 0x00000002, - regk_dma_store_descr = 0x00000000, - regk_dma_store_g = 0x00000004, - regk_dma_store_md = 0x00000001, - regk_dma_sw = 0x00000008, - regk_dma_update_down = 0x00000020, - regk_dma_yes = 0x00000001 -}; - -enum dma_ch_state -{ - RST = 1, - STOPPED = 2, - RUNNING = 4 -}; - -struct fs_dma_channel -{ - qemu_irq irq; - struct etraxfs_dma_client *client; - - /* Internal status. */ - int stream_cmd_src; - enum dma_ch_state state; - - unsigned int input : 1; - unsigned int eol : 1; - - struct dma_descr_group current_g; - struct dma_descr_context current_c; - struct dma_descr_data current_d; - - /* Control registers. */ - uint32_t regs[DMA_REG_MAX]; -}; - -struct fs_dma_ctrl -{ - MemoryRegion mmio; - int nr_channels; - struct fs_dma_channel *channels; - - QEMUBH *bh; -}; - -static void DMA_run(void *opaque); -static int channel_out_run(struct fs_dma_ctrl *ctrl, int c); - -static inline uint32_t channel_reg(struct fs_dma_ctrl *ctrl, int c, int reg) -{ - return ctrl->channels[c].regs[reg]; -} - -static inline int channel_stopped(struct fs_dma_ctrl *ctrl, int c) -{ - return channel_reg(ctrl, c, RW_CFG) & 2; -} - -static inline int channel_en(struct fs_dma_ctrl *ctrl, int c) -{ - return (channel_reg(ctrl, c, RW_CFG) & 1) - && ctrl->channels[c].client; -} - -static inline int fs_channel(hwaddr addr) -{ - /* Every channel has a 0x2000 ctrl register map. */ - return addr >> 13; -} - -#ifdef USE_THIS_DEAD_CODE -static void channel_load_g(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_GROUP); - - /* Load and decode. FIXME: handle endianness. */ - cpu_physical_memory_read (addr, - (void *) &ctrl->channels[c].current_g, - sizeof ctrl->channels[c].current_g); -} - -static void dump_c(int ch, struct dma_descr_context *c) -{ - printf("%s ch=%d\n", __func__, ch); - printf("next=%x\n", c->next); - printf("saved_data=%x\n", c->saved_data); - printf("saved_data_buf=%x\n", c->saved_data_buf); - printf("eol=%x\n", (uint32_t) c->eol); -} - -static void dump_d(int ch, struct dma_descr_data *d) -{ - printf("%s ch=%d\n", __func__, ch); - printf("next=%x\n", d->next); - printf("buf=%x\n", d->buf); - printf("after=%x\n", d->after); - printf("intr=%x\n", (uint32_t) d->intr); - printf("out_eop=%x\n", (uint32_t) d->out_eop); - printf("in_eop=%x\n", (uint32_t) d->in_eop); - printf("eol=%x\n", (uint32_t) d->eol); -} -#endif - -static void channel_load_c(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); - - /* Load and decode. FIXME: handle endianness. */ - cpu_physical_memory_read (addr, - (void *) &ctrl->channels[c].current_c, - sizeof ctrl->channels[c].current_c); - - D(dump_c(c, &ctrl->channels[c].current_c)); - /* I guess this should update the current pos. */ - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data; - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data_buf; -} - -static void channel_load_d(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA); - - /* Load and decode. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); - cpu_physical_memory_read (addr, - (void *) &ctrl->channels[c].current_d, - sizeof ctrl->channels[c].current_d); - - D(dump_d(c, &ctrl->channels[c].current_d)); - ctrl->channels[c].regs[RW_DATA] = addr; -} - -static void channel_store_c(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); - - /* Encode and store. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); - D(dump_d(c, &ctrl->channels[c].current_d)); - cpu_physical_memory_write (addr, - (void *) &ctrl->channels[c].current_c, - sizeof ctrl->channels[c].current_c); -} - -static void channel_store_d(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA); - - /* Encode and store. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); - cpu_physical_memory_write (addr, - (void *) &ctrl->channels[c].current_d, - sizeof ctrl->channels[c].current_d); -} - -static inline void channel_stop(struct fs_dma_ctrl *ctrl, int c) -{ - /* FIXME: */ -} - -static inline void channel_start(struct fs_dma_ctrl *ctrl, int c) -{ - if (ctrl->channels[c].client) - { - ctrl->channels[c].eol = 0; - ctrl->channels[c].state = RUNNING; - if (!ctrl->channels[c].input) - channel_out_run(ctrl, c); - } else - printf("WARNING: starting DMA ch %d with no client\n", c); - - qemu_bh_schedule_idle(ctrl->bh); -} - -static void channel_continue(struct fs_dma_ctrl *ctrl, int c) -{ - if (!channel_en(ctrl, c) - || channel_stopped(ctrl, c) - || ctrl->channels[c].state != RUNNING - /* Only reload the current data descriptor if it has eol set. */ - || !ctrl->channels[c].current_d.eol) { - D(printf("continue failed ch=%d state=%d stopped=%d en=%d eol=%d\n", - c, ctrl->channels[c].state, - channel_stopped(ctrl, c), - channel_en(ctrl,c), - ctrl->channels[c].eol)); - D(dump_d(c, &ctrl->channels[c].current_d)); - return; - } - - /* Reload the current descriptor. */ - channel_load_d(ctrl, c); - - /* If the current descriptor cleared the eol flag and we had already - reached eol state, do the continue. */ - if (!ctrl->channels[c].current_d.eol && ctrl->channels[c].eol) { - D(printf("continue %d ok %x\n", c, - ctrl->channels[c].current_d.next)); - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl->channels[c].current_d.next; - channel_load_d(ctrl, c); - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf; - - channel_start(ctrl, c); - } - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf; -} - -static void channel_stream_cmd(struct fs_dma_ctrl *ctrl, int c, uint32_t v) -{ - unsigned int cmd = v & ((1 << 10) - 1); - - D(printf("%s ch=%d cmd=%x\n", - __func__, c, cmd)); - if (cmd & regk_dma_load_d) { - channel_load_d(ctrl, c); - if (cmd & regk_dma_burst) - channel_start(ctrl, c); - } - - if (cmd & regk_dma_load_c) { - channel_load_c(ctrl, c); - } -} - -static void channel_update_irq(struct fs_dma_ctrl *ctrl, int c) -{ - D(printf("%s %d\n", __func__, c)); - ctrl->channels[c].regs[R_INTR] &= - ~(ctrl->channels[c].regs[RW_ACK_INTR]); - - ctrl->channels[c].regs[R_MASKED_INTR] = - ctrl->channels[c].regs[R_INTR] - & ctrl->channels[c].regs[RW_INTR_MASK]; - - D(printf("%s: chan=%d masked_intr=%x\n", __func__, - c, - ctrl->channels[c].regs[R_MASKED_INTR])); - - qemu_set_irq(ctrl->channels[c].irq, - !!ctrl->channels[c].regs[R_MASKED_INTR]); -} - -static int channel_out_run(struct fs_dma_ctrl *ctrl, int c) -{ - uint32_t len; - uint32_t saved_data_buf; - unsigned char buf[2 * 1024]; - - struct dma_context_metadata meta; - bool send_context = true; - - if (ctrl->channels[c].eol) - return 0; - - do { - bool out_eop; - D(printf("ch=%d buf=%x after=%x\n", - c, - (uint32_t)ctrl->channels[c].current_d.buf, - (uint32_t)ctrl->channels[c].current_d.after)); - - if (send_context) { - if (ctrl->channels[c].client->client.metadata_push) { - meta.metadata = ctrl->channels[c].current_d.md; - ctrl->channels[c].client->client.metadata_push( - ctrl->channels[c].client->client.opaque, - &meta); - } - send_context = false; - } - - channel_load_d(ctrl, c); - saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF); - len = (uint32_t)(unsigned long) - ctrl->channels[c].current_d.after; - len -= saved_data_buf; - - if (len > sizeof buf) - len = sizeof buf; - cpu_physical_memory_read (saved_data_buf, buf, len); - - out_eop = ((saved_data_buf + len) == - ctrl->channels[c].current_d.after) && - ctrl->channels[c].current_d.out_eop; - - D(printf("channel %d pushes %x %u bytes eop=%u\n", c, - saved_data_buf, len, out_eop)); - - if (ctrl->channels[c].client->client.push) { - if (len > 0) { - ctrl->channels[c].client->client.push( - ctrl->channels[c].client->client.opaque, - buf, len, out_eop); - } - } else { - printf("WARNING: DMA ch%d dataloss," - " no attached client.\n", c); - } - - saved_data_buf += len; - - if (saved_data_buf == (uint32_t)(unsigned long) - ctrl->channels[c].current_d.after) { - /* Done. Step to next. */ - if (ctrl->channels[c].current_d.out_eop) { - send_context = true; - } - if (ctrl->channels[c].current_d.intr) { - /* data intr. */ - D(printf("signal intr %d eol=%d\n", - len, ctrl->channels[c].current_d.eol)); - ctrl->channels[c].regs[R_INTR] |= (1 << 2); - channel_update_irq(ctrl, c); - } - channel_store_d(ctrl, c); - if (ctrl->channels[c].current_d.eol) { - D(printf("channel %d EOL\n", c)); - ctrl->channels[c].eol = 1; - - /* Mark the context as disabled. */ - ctrl->channels[c].current_c.dis = 1; - channel_store_c(ctrl, c); - - channel_stop(ctrl, c); - } else { - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl-> - channels[c].current_d.next; - /* Load new descriptor. */ - channel_load_d(ctrl, c); - saved_data_buf = (uint32_t)(unsigned long) - ctrl->channels[c].current_d.buf; - } - - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - saved_data_buf; - D(dump_d(c, &ctrl->channels[c].current_d)); - } - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf; - } while (!ctrl->channels[c].eol); - return 1; -} - -static int channel_in_process(struct fs_dma_ctrl *ctrl, int c, - unsigned char *buf, int buflen, int eop) -{ - uint32_t len; - uint32_t saved_data_buf; - - if (ctrl->channels[c].eol == 1) - return 0; - - channel_load_d(ctrl, c); - saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF); - len = (uint32_t)(unsigned long)ctrl->channels[c].current_d.after; - len -= saved_data_buf; - - if (len > buflen) - len = buflen; - - cpu_physical_memory_write (saved_data_buf, buf, len); - saved_data_buf += len; - - if (saved_data_buf == - (uint32_t)(unsigned long)ctrl->channels[c].current_d.after - || eop) { - uint32_t r_intr = ctrl->channels[c].regs[R_INTR]; - - D(printf("in dscr end len=%d\n", - ctrl->channels[c].current_d.after - - ctrl->channels[c].current_d.buf)); - ctrl->channels[c].current_d.after = saved_data_buf; - - /* Done. Step to next. */ - if (ctrl->channels[c].current_d.intr) { - /* TODO: signal eop to the client. */ - /* data intr. */ - ctrl->channels[c].regs[R_INTR] |= 3; - } - if (eop) { - ctrl->channels[c].current_d.in_eop = 1; - ctrl->channels[c].regs[R_INTR] |= 8; - } - if (r_intr != ctrl->channels[c].regs[R_INTR]) - channel_update_irq(ctrl, c); - - channel_store_d(ctrl, c); - D(dump_d(c, &ctrl->channels[c].current_d)); - - if (ctrl->channels[c].current_d.eol) { - D(printf("channel %d EOL\n", c)); - ctrl->channels[c].eol = 1; - - /* Mark the context as disabled. */ - ctrl->channels[c].current_c.dis = 1; - channel_store_c(ctrl, c); - - channel_stop(ctrl, c); - } else { - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl-> - channels[c].current_d.next; - /* Load new descriptor. */ - channel_load_d(ctrl, c); - saved_data_buf = (uint32_t)(unsigned long) - ctrl->channels[c].current_d.buf; - } - } - - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf; - return len; -} - -static inline int channel_in_run(struct fs_dma_ctrl *ctrl, int c) -{ - if (ctrl->channels[c].client->client.pull) { - ctrl->channels[c].client->client.pull( - ctrl->channels[c].client->client.opaque); - return 1; - } else - return 0; -} - -static uint32_t dma_rinvalid (void *opaque, hwaddr addr) -{ - hw_error("Unsupported short raccess. reg=" TARGET_FMT_plx "\n", addr); - return 0; -} - -static uint64_t -dma_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct fs_dma_ctrl *ctrl = opaque; - int c; - uint32_t r = 0; - - if (size != 4) { - dma_rinvalid(opaque, addr); - } - - /* Make addr relative to this channel and bounded to nr regs. */ - c = fs_channel(addr); - addr &= 0xff; - addr >>= 2; - switch (addr) - { - case RW_STAT: - r = ctrl->channels[c].state & 7; - r |= ctrl->channels[c].eol << 5; - r |= ctrl->channels[c].stream_cmd_src << 8; - break; - - default: - r = ctrl->channels[c].regs[addr]; - D(printf ("%s c=%d addr=" TARGET_FMT_plx "\n", - __func__, c, addr)); - break; - } - return r; -} - -static void -dma_winvalid (void *opaque, hwaddr addr, uint32_t value) -{ - hw_error("Unsupported short waccess. reg=" TARGET_FMT_plx "\n", addr); -} - -static void -dma_update_state(struct fs_dma_ctrl *ctrl, int c) -{ - if (ctrl->channels[c].regs[RW_CFG] & 2) - ctrl->channels[c].state = STOPPED; - if (!(ctrl->channels[c].regs[RW_CFG] & 1)) - ctrl->channels[c].state = RST; -} - -static void -dma_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - struct fs_dma_ctrl *ctrl = opaque; - uint32_t value = val64; - int c; - - if (size != 4) { - dma_winvalid(opaque, addr, value); - } - - /* Make addr relative to this channel and bounded to nr regs. */ - c = fs_channel(addr); - addr &= 0xff; - addr >>= 2; - switch (addr) - { - case RW_DATA: - ctrl->channels[c].regs[addr] = value; - break; - - case RW_CFG: - ctrl->channels[c].regs[addr] = value; - dma_update_state(ctrl, c); - break; - case RW_CMD: - /* continue. */ - if (value & ~1) - printf("Invalid store to ch=%d RW_CMD %x\n", - c, value); - ctrl->channels[c].regs[addr] = value; - channel_continue(ctrl, c); - break; - - case RW_SAVED_DATA: - case RW_SAVED_DATA_BUF: - case RW_GROUP: - case RW_GROUP_DOWN: - ctrl->channels[c].regs[addr] = value; - break; - - case RW_ACK_INTR: - case RW_INTR_MASK: - ctrl->channels[c].regs[addr] = value; - channel_update_irq(ctrl, c); - if (addr == RW_ACK_INTR) - ctrl->channels[c].regs[RW_ACK_INTR] = 0; - break; - - case RW_STREAM_CMD: - if (value & ~1023) - printf("Invalid store to ch=%d " - "RW_STREAMCMD %x\n", - c, value); - ctrl->channels[c].regs[addr] = value; - D(printf("stream_cmd ch=%d\n", c)); - channel_stream_cmd(ctrl, c, value); - break; - - default: - D(printf ("%s c=%d " TARGET_FMT_plx "\n", - __func__, c, addr)); - break; - } -} - -static const MemoryRegionOps dma_ops = { - .read = dma_read, - .write = dma_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } -}; - -static int etraxfs_dmac_run(void *opaque) -{ - struct fs_dma_ctrl *ctrl = opaque; - int i; - int p = 0; - - for (i = 0; - i < ctrl->nr_channels; - i++) - { - if (ctrl->channels[i].state == RUNNING) - { - if (ctrl->channels[i].input) { - p += channel_in_run(ctrl, i); - } else { - p += channel_out_run(ctrl, i); - } - } - } - return p; -} - -int etraxfs_dmac_input(struct etraxfs_dma_client *client, - void *buf, int len, int eop) -{ - return channel_in_process(client->ctrl, client->channel, - buf, len, eop); -} - -/* Connect an IRQ line with a channel. */ -void etraxfs_dmac_connect(void *opaque, int c, qemu_irq *line, int input) -{ - struct fs_dma_ctrl *ctrl = opaque; - ctrl->channels[c].irq = *line; - ctrl->channels[c].input = input; -} - -void etraxfs_dmac_connect_client(void *opaque, int c, - struct etraxfs_dma_client *cl) -{ - struct fs_dma_ctrl *ctrl = opaque; - cl->ctrl = ctrl; - cl->channel = c; - ctrl->channels[c].client = cl; -} - - -static void DMA_run(void *opaque) -{ - struct fs_dma_ctrl *etraxfs_dmac = opaque; - int p = 1; - - if (runstate_is_running()) - p = etraxfs_dmac_run(etraxfs_dmac); - - if (p) - qemu_bh_schedule_idle(etraxfs_dmac->bh); -} - -void *etraxfs_dmac_init(hwaddr base, int nr_channels) -{ - struct fs_dma_ctrl *ctrl = NULL; - - ctrl = g_malloc0(sizeof *ctrl); - - ctrl->bh = qemu_bh_new(DMA_run, ctrl); - - ctrl->nr_channels = nr_channels; - ctrl->channels = g_malloc0(sizeof ctrl->channels[0] * nr_channels); - - memory_region_init_io(&ctrl->mmio, NULL, &dma_ops, ctrl, "etraxfs-dma", - nr_channels * 0x2000); - memory_region_add_subregion(get_system_memory(), base, &ctrl->mmio); - - return ctrl; -} diff --git a/qemu/hw/dma/i82374.c b/qemu/hw/dma/i82374.c deleted file mode 100644 index 6c0f975df..000000000 --- a/qemu/hw/dma/i82374.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * QEMU Intel 82374 emulation (Enhanced DMA controller) - * - * Copyright (c) 2010 Hervé Poussineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/isa/isa.h" - -#define TYPE_I82374 "i82374" -#define I82374(obj) OBJECT_CHECK(I82374State, (obj), TYPE_I82374) - -//#define DEBUG_I82374 - -#ifdef DEBUG_I82374 -#define DPRINTF(fmt, ...) \ -do { fprintf(stderr, "i82374: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ -do {} while (0) -#endif -#define BADF(fmt, ...) \ -do { fprintf(stderr, "i82374 ERROR: " fmt , ## __VA_ARGS__); } while (0) - -typedef struct I82374State { - ISADevice parent_obj; - - uint32_t iobase; - uint8_t commands[8]; - PortioList port_list; -} I82374State; - -static const VMStateDescription vmstate_i82374 = { - .name = "i82374", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8_ARRAY(commands, I82374State, 8), - VMSTATE_END_OF_LIST() - }, -}; - -static uint32_t i82374_read_isr(void *opaque, uint32_t nport) -{ - uint32_t val = 0; - - BADF("%s: %08x\n", __func__, nport); - - DPRINTF("%s: %08x=%08x\n", __func__, nport, val); - return val; -} - -static void i82374_write_command(void *opaque, uint32_t nport, uint32_t data) -{ - DPRINTF("%s: %08x=%08x\n", __func__, nport, data); - - if (data != 0x42) { - /* Not Stop S/G command */ - BADF("%s: %08x=%08x\n", __func__, nport, data); - } -} - -static uint32_t i82374_read_status(void *opaque, uint32_t nport) -{ - uint32_t val = 0; - - BADF("%s: %08x\n", __func__, nport); - - DPRINTF("%s: %08x=%08x\n", __func__, nport, val); - return val; -} - -static void i82374_write_descriptor(void *opaque, uint32_t nport, uint32_t data) -{ - DPRINTF("%s: %08x=%08x\n", __func__, nport, data); - - BADF("%s: %08x=%08x\n", __func__, nport, data); -} - -static uint32_t i82374_read_descriptor(void *opaque, uint32_t nport) -{ - uint32_t val = 0; - - BADF("%s: %08x\n", __func__, nport); - - DPRINTF("%s: %08x=%08x\n", __func__, nport, val); - return val; -} - -static const MemoryRegionPortio i82374_portio_list[] = { - { 0x0A, 1, 1, .read = i82374_read_isr, }, - { 0x10, 8, 1, .write = i82374_write_command, }, - { 0x18, 8, 1, .read = i82374_read_status, }, - { 0x20, 0x20, 1, - .write = i82374_write_descriptor, .read = i82374_read_descriptor, }, - PORTIO_END_OF_LIST(), -}; - -static void i82374_realize(DeviceState *dev, Error **errp) -{ - I82374State *s = I82374(dev); - - portio_list_init(&s->port_list, OBJECT(s), i82374_portio_list, s, - "i82374"); - portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj), - s->iobase); - - DMA_init(isa_bus_from_device(ISA_DEVICE(dev)), 1); - memset(s->commands, 0, sizeof(s->commands)); -} - -static Property i82374_properties[] = { - DEFINE_PROP_UINT32("iobase", I82374State, iobase, 0x400), - DEFINE_PROP_END_OF_LIST() -}; - -static void i82374_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = i82374_realize; - dc->vmsd = &vmstate_i82374; - dc->props = i82374_properties; -} - -static const TypeInfo i82374_info = { - .name = TYPE_I82374, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(I82374State), - .class_init = i82374_class_init, -}; - -static void i82374_register_types(void) -{ - type_register_static(&i82374_info); -} - -type_init(i82374_register_types) diff --git a/qemu/hw/dma/i8257.c b/qemu/hw/dma/i8257.c deleted file mode 100644 index f345c5476..000000000 --- a/qemu/hw/dma/i8257.c +++ /dev/null @@ -1,643 +0,0 @@ -/* - * QEMU DMA emulation - * - * Copyright (c) 2003-2004 Vassili Karpov (malc) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/isa/isa.h" -#include "hw/isa/i8257.h" -#include "qemu/main-loop.h" -#include "trace.h" - -#define I8257(obj) \ - OBJECT_CHECK(I8257State, (obj), TYPE_I8257) - -/* #define DEBUG_DMA */ - -#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__) -#ifdef DEBUG_DMA -#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) -#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__) -#else -#define linfo(...) -#define ldebug(...) -#endif - -#define ADDR 0 -#define COUNT 1 - -enum { - CMD_MEMORY_TO_MEMORY = 0x01, - CMD_FIXED_ADDRESS = 0x02, - CMD_BLOCK_CONTROLLER = 0x04, - CMD_COMPRESSED_TIME = 0x08, - CMD_CYCLIC_PRIORITY = 0x10, - CMD_EXTENDED_WRITE = 0x20, - CMD_LOW_DREQ = 0x40, - CMD_LOW_DACK = 0x80, - CMD_NOT_SUPPORTED = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS - | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE - | CMD_LOW_DREQ | CMD_LOW_DACK - -}; - -static void i8257_dma_run(void *opaque); - -static const int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0}; - -static void i8257_write_page(void *opaque, uint32_t nport, uint32_t data) -{ - I8257State *d = opaque; - int ichan; - - ichan = channels[nport & 7]; - if (-1 == ichan) { - dolog ("invalid channel %#x %#x\n", nport, data); - return; - } - d->regs[ichan].page = data; -} - -static void i8257_write_pageh(void *opaque, uint32_t nport, uint32_t data) -{ - I8257State *d = opaque; - int ichan; - - ichan = channels[nport & 7]; - if (-1 == ichan) { - dolog ("invalid channel %#x %#x\n", nport, data); - return; - } - d->regs[ichan].pageh = data; -} - -static uint32_t i8257_read_page(void *opaque, uint32_t nport) -{ - I8257State *d = opaque; - int ichan; - - ichan = channels[nport & 7]; - if (-1 == ichan) { - dolog ("invalid channel read %#x\n", nport); - return 0; - } - return d->regs[ichan].page; -} - -static uint32_t i8257_read_pageh(void *opaque, uint32_t nport) -{ - I8257State *d = opaque; - int ichan; - - ichan = channels[nport & 7]; - if (-1 == ichan) { - dolog ("invalid channel read %#x\n", nport); - return 0; - } - return d->regs[ichan].pageh; -} - -static inline void i8257_init_chan(I8257State *d, int ichan) -{ - I8257Regs *r; - - r = d->regs + ichan; - r->now[ADDR] = r->base[ADDR] << d->dshift; - r->now[COUNT] = 0; -} - -static inline int i8257_getff(I8257State *d) -{ - int ff; - - ff = d->flip_flop; - d->flip_flop = !ff; - return ff; -} - -static uint64_t i8257_read_chan(void *opaque, hwaddr nport, unsigned size) -{ - I8257State *d = opaque; - int ichan, nreg, iport, ff, val, dir; - I8257Regs *r; - - iport = (nport >> d->dshift) & 0x0f; - ichan = iport >> 1; - nreg = iport & 1; - r = d->regs + ichan; - - dir = ((r->mode >> 5) & 1) ? -1 : 1; - ff = i8257_getff(d); - if (nreg) - val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; - else - val = r->now[ADDR] + r->now[COUNT] * dir; - - ldebug ("read_chan %#x -> %d\n", iport, val); - return (val >> (d->dshift + (ff << 3))) & 0xff; -} - -static void i8257_write_chan(void *opaque, hwaddr nport, uint64_t data, - unsigned int size) -{ - I8257State *d = opaque; - int iport, ichan, nreg; - I8257Regs *r; - - iport = (nport >> d->dshift) & 0x0f; - ichan = iport >> 1; - nreg = iport & 1; - r = d->regs + ichan; - if (i8257_getff(d)) { - r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00); - i8257_init_chan(d, ichan); - } else { - r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff); - } -} - -static void i8257_write_cont(void *opaque, hwaddr nport, uint64_t data, - unsigned int size) -{ - I8257State *d = opaque; - int iport, ichan = 0; - - iport = (nport >> d->dshift) & 0x0f; - switch (iport) { - case 0x00: /* command */ - if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { - dolog("command %"PRIx64" not supported\n", data); - return; - } - d->command = data; - break; - - case 0x01: - ichan = data & 3; - if (data & 4) { - d->status |= 1 << (ichan + 4); - } - else { - d->status &= ~(1 << (ichan + 4)); - } - d->status &= ~(1 << ichan); - i8257_dma_run(d); - break; - - case 0x02: /* single mask */ - if (data & 4) - d->mask |= 1 << (data & 3); - else - d->mask &= ~(1 << (data & 3)); - i8257_dma_run(d); - break; - - case 0x03: /* mode */ - { - ichan = data & 3; -#ifdef DEBUG_DMA - { - int op, ai, dir, opmode; - op = (data >> 2) & 3; - ai = (data >> 4) & 1; - dir = (data >> 5) & 1; - opmode = (data >> 6) & 3; - - linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", - ichan, op, ai, dir, opmode); - } -#endif - d->regs[ichan].mode = data; - break; - } - - case 0x04: /* clear flip flop */ - d->flip_flop = 0; - break; - - case 0x05: /* reset */ - d->flip_flop = 0; - d->mask = ~0; - d->status = 0; - d->command = 0; - break; - - case 0x06: /* clear mask for all channels */ - d->mask = 0; - i8257_dma_run(d); - break; - - case 0x07: /* write mask for all channels */ - d->mask = data; - i8257_dma_run(d); - break; - - default: - dolog ("unknown iport %#x\n", iport); - break; - } - -#ifdef DEBUG_DMA - if (0xc != iport) { - linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n", - nport, ichan, data); - } -#endif -} - -static uint64_t i8257_read_cont(void *opaque, hwaddr nport, unsigned size) -{ - I8257State *d = opaque; - int iport, val; - - iport = (nport >> d->dshift) & 0x0f; - switch (iport) { - case 0x00: /* status */ - val = d->status; - d->status &= 0xf0; - break; - case 0x01: /* mask */ - val = d->mask; - break; - default: - val = 0; - break; - } - - ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val); - return val; -} - -static IsaDmaTransferMode i8257_dma_get_transfer_mode(IsaDma *obj, int nchan) -{ - I8257State *d = I8257(obj); - return (d->regs[nchan & 3].mode >> 2) & 3; -} - -static bool i8257_dma_has_autoinitialization(IsaDma *obj, int nchan) -{ - I8257State *d = I8257(obj); - return (d->regs[nchan & 3].mode >> 4) & 1; -} - -static void i8257_dma_hold_DREQ(IsaDma *obj, int nchan) -{ - I8257State *d = I8257(obj); - int ichan; - - ichan = nchan & 3; - d->status |= 1 << (ichan + 4); - i8257_dma_run(d); -} - -static void i8257_dma_release_DREQ(IsaDma *obj, int nchan) -{ - I8257State *d = I8257(obj); - int ichan; - - ichan = nchan & 3; - d->status &= ~(1 << (ichan + 4)); - i8257_dma_run(d); -} - -static void i8257_channel_run(I8257State *d, int ichan) -{ - int ncont = d->dshift; - int n; - I8257Regs *r = &d->regs[ichan]; -#ifdef DEBUG_DMA - int dir, opmode; - - dir = (r->mode >> 5) & 1; - opmode = (r->mode >> 6) & 3; - - if (dir) { - dolog ("DMA in address decrement mode\n"); - } - if (opmode != 1) { - dolog ("DMA not in single mode select %#x\n", opmode); - } -#endif - - n = r->transfer_handler (r->opaque, ichan + (ncont << 2), - r->now[COUNT], (r->base[COUNT] + 1) << ncont); - r->now[COUNT] = n; - ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont); - if (n == (r->base[COUNT] + 1) << ncont) { - ldebug("transfer done\n"); - d->status |= (1 << ichan); - } -} - -static void i8257_dma_run(void *opaque) -{ - I8257State *d = opaque; - int ichan; - int rearm = 0; - - if (d->running) { - rearm = 1; - goto out; - } else { - d->running = 1; - } - - for (ichan = 0; ichan < 4; ichan++) { - int mask; - - mask = 1 << ichan; - - if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) { - i8257_channel_run(d, ichan); - rearm = 1; - } - } - - d->running = 0; -out: - if (rearm) { - qemu_bh_schedule_idle(d->dma_bh); - d->dma_bh_scheduled = true; - } -} - -static void i8257_dma_register_channel(IsaDma *obj, int nchan, - IsaDmaTransferHandler transfer_handler, - void *opaque) -{ - I8257State *d = I8257(obj); - I8257Regs *r; - int ichan; - - ichan = nchan & 3; - - r = d->regs + ichan; - r->transfer_handler = transfer_handler; - r->opaque = opaque; -} - -static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos, - int len) -{ - I8257State *d = I8257(obj); - I8257Regs *r = &d->regs[nchan & 3]; - hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; - - if (r->mode & 0x20) { - int i; - uint8_t *p = buf; - - cpu_physical_memory_read (addr - pos - len, buf, len); - /* What about 16bit transfers? */ - for (i = 0; i < len >> 1; i++) { - uint8_t b = p[len - i - 1]; - p[i] = b; - } - } - else - cpu_physical_memory_read (addr + pos, buf, len); - - return len; -} - -static int i8257_dma_write_memory(IsaDma *obj, int nchan, void *buf, int pos, - int len) -{ - I8257State *s = I8257(obj); - I8257Regs *r = &s->regs[nchan & 3]; - hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; - - if (r->mode & 0x20) { - int i; - uint8_t *p = buf; - - cpu_physical_memory_write (addr - pos - len, buf, len); - /* What about 16bit transfers? */ - for (i = 0; i < len; i++) { - uint8_t b = p[len - i - 1]; - p[i] = b; - } - } - else - cpu_physical_memory_write (addr + pos, buf, len); - - return len; -} - -/* request the emulator to transfer a new DMA memory block ASAP (even - * if the idle bottom half would not have exited the iothread yet). - */ -static void i8257_dma_schedule(IsaDma *obj) -{ - I8257State *d = I8257(obj); - if (d->dma_bh_scheduled) { - qemu_notify_event(); - } -} - -static void i8257_reset(DeviceState *dev) -{ - I8257State *d = I8257(dev); - i8257_write_cont(d, (0x05 << d->dshift), 0, 1); -} - -static int i8257_phony_handler(void *opaque, int nchan, int dma_pos, - int dma_len) -{ - trace_i8257_unregistered_dma(nchan, dma_pos, dma_len); - return dma_pos; -} - - -static const MemoryRegionOps channel_io_ops = { - .read = i8257_read_chan, - .write = i8257_write_chan, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -/* IOport from page_base */ -static const MemoryRegionPortio page_portio_list[] = { - { 0x01, 3, 1, .write = i8257_write_page, .read = i8257_read_page, }, - { 0x07, 1, 1, .write = i8257_write_page, .read = i8257_read_page, }, - PORTIO_END_OF_LIST(), -}; - -/* IOport from pageh_base */ -static const MemoryRegionPortio pageh_portio_list[] = { - { 0x01, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, }, - { 0x07, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, }, - PORTIO_END_OF_LIST(), -}; - -static const MemoryRegionOps cont_io_ops = { - .read = i8257_read_cont, - .write = i8257_write_cont, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static const VMStateDescription vmstate_i8257_regs = { - .name = "dma_regs", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32_ARRAY(now, I8257Regs, 2), - VMSTATE_UINT16_ARRAY(base, I8257Regs, 2), - VMSTATE_UINT8(mode, I8257Regs), - VMSTATE_UINT8(page, I8257Regs), - VMSTATE_UINT8(pageh, I8257Regs), - VMSTATE_UINT8(dack, I8257Regs), - VMSTATE_UINT8(eop, I8257Regs), - VMSTATE_END_OF_LIST() - } -}; - -static int i8257_post_load(void *opaque, int version_id) -{ - I8257State *d = opaque; - i8257_dma_run(d); - - return 0; -} - -static const VMStateDescription vmstate_i8257 = { - .name = "dma", - .version_id = 1, - .minimum_version_id = 1, - .post_load = i8257_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(command, I8257State), - VMSTATE_UINT8(mask, I8257State), - VMSTATE_UINT8(flip_flop, I8257State), - VMSTATE_INT32(dshift, I8257State), - VMSTATE_STRUCT_ARRAY(regs, I8257State, 4, 1, vmstate_i8257_regs, - I8257Regs), - VMSTATE_END_OF_LIST() - } -}; - -static void i8257_realize(DeviceState *dev, Error **errp) -{ - ISADevice *isa = ISA_DEVICE(dev); - I8257State *d = I8257(dev); - int i; - - memory_region_init_io(&d->channel_io, NULL, &channel_io_ops, d, - "dma-chan", 8 << d->dshift); - memory_region_add_subregion(isa_address_space_io(isa), - d->base, &d->channel_io); - - isa_register_portio_list(isa, d->page_base, page_portio_list, d, - "dma-page"); - if (d->pageh_base >= 0) { - isa_register_portio_list(isa, d->pageh_base, pageh_portio_list, d, - "dma-pageh"); - } - - memory_region_init_io(&d->cont_io, OBJECT(isa), &cont_io_ops, d, - "dma-cont", 8 << d->dshift); - memory_region_add_subregion(isa_address_space_io(isa), - d->base + (8 << d->dshift), &d->cont_io); - - for (i = 0; i < ARRAY_SIZE(d->regs); ++i) { - d->regs[i].transfer_handler = i8257_phony_handler; - } - - d->dma_bh = qemu_bh_new(i8257_dma_run, d); -} - -static Property i8257_properties[] = { - DEFINE_PROP_INT32("base", I8257State, base, 0x00), - DEFINE_PROP_INT32("page-base", I8257State, page_base, 0x80), - DEFINE_PROP_INT32("pageh-base", I8257State, pageh_base, 0x480), - DEFINE_PROP_INT32("dshift", I8257State, dshift, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void i8257_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IsaDmaClass *idc = ISADMA_CLASS(klass); - - dc->realize = i8257_realize; - dc->reset = i8257_reset; - dc->vmsd = &vmstate_i8257; - dc->props = i8257_properties; - - idc->get_transfer_mode = i8257_dma_get_transfer_mode; - idc->has_autoinitialization = i8257_dma_has_autoinitialization; - idc->read_memory = i8257_dma_read_memory; - idc->write_memory = i8257_dma_write_memory; - idc->hold_DREQ = i8257_dma_hold_DREQ; - idc->release_DREQ = i8257_dma_release_DREQ; - idc->schedule = i8257_dma_schedule; - idc->register_channel = i8257_dma_register_channel; -} - -static const TypeInfo i8257_info = { - .name = TYPE_I8257, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(I8257State), - .class_init = i8257_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_ISADMA }, - { } - } -}; - -static void i8257_register_types(void) -{ - type_register_static(&i8257_info); -} - -type_init(i8257_register_types) - -void DMA_init(ISABus *bus, int high_page_enable) -{ - ISADevice *isa1, *isa2; - DeviceState *d; - - isa1 = isa_create(bus, TYPE_I8257); - d = DEVICE(isa1); - qdev_prop_set_int32(d, "base", 0x00); - qdev_prop_set_int32(d, "page-base", 0x80); - qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x480 : -1); - qdev_prop_set_int32(d, "dshift", 0); - qdev_init_nofail(d); - - isa2 = isa_create(bus, TYPE_I8257); - d = DEVICE(isa2); - qdev_prop_set_int32(d, "base", 0xc0); - qdev_prop_set_int32(d, "page-base", 0x88); - qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x488 : -1); - qdev_prop_set_int32(d, "dshift", 1); - qdev_init_nofail(d); - - isa_bus_dma(bus, ISADMA(isa1), ISADMA(isa2)); -} diff --git a/qemu/hw/dma/omap_dma.c b/qemu/hw/dma/omap_dma.c deleted file mode 100644 index 700cd6b43..000000000 --- a/qemu/hw/dma/omap_dma.c +++ /dev/null @@ -1,2103 +0,0 @@ -/* - * TI OMAP DMA gigacell. - * - * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org> - * Copyright (C) 2007-2008 Lauro Ramos Venancio <lauro.venancio@indt.org.br> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/arm/omap.h" -#include "hw/irq.h" -#include "hw/arm/soc_dma.h" - -struct omap_dma_channel_s { - /* transfer data */ - int burst[2]; - int pack[2]; - int endian[2]; - int endian_lock[2]; - int translate[2]; - enum omap_dma_port port[2]; - hwaddr addr[2]; - omap_dma_addressing_t mode[2]; - uint32_t elements; - uint16_t frames; - int32_t frame_index[2]; - int16_t element_index[2]; - int data_type; - - /* transfer type */ - int transparent_copy; - int constant_fill; - uint32_t color; - int prefetch; - - /* auto init and linked channel data */ - int end_prog; - int repeat; - int auto_init; - int link_enabled; - int link_next_ch; - - /* interruption data */ - int interrupts; - int status; - int cstatus; - - /* state data */ - int active; - int enable; - int sync; - int src_sync; - int pending_request; - int waiting_end_prog; - uint16_t cpc; - int set_update; - - /* sync type */ - int fs; - int bs; - - /* compatibility */ - int omap_3_1_compatible_disable; - - qemu_irq irq; - struct omap_dma_channel_s *sibling; - - struct omap_dma_reg_set_s { - hwaddr src, dest; - int frame; - int element; - int pck_element; - int frame_delta[2]; - int elem_delta[2]; - int frames; - int elements; - int pck_elements; - } active_set; - - struct soc_dma_ch_s *dma; - - /* unused parameters */ - int write_mode; - int priority; - int interleave_disabled; - int type; - int suspend; - int buf_disable; -}; - -struct omap_dma_s { - struct soc_dma_s *dma; - MemoryRegion iomem; - - struct omap_mpu_state_s *mpu; - omap_clk clk; - qemu_irq irq[4]; - void (*intr_update)(struct omap_dma_s *s); - enum omap_dma_model model; - int omap_3_1_mapping_disabled; - - uint32_t gcr; - uint32_t ocp; - uint32_t caps[5]; - uint32_t irqen[4]; - uint32_t irqstat[4]; - - int chans; - struct omap_dma_channel_s ch[32]; - struct omap_dma_lcd_channel_s lcd_ch; -}; - -/* Interrupts */ -#define TIMEOUT_INTR (1 << 0) -#define EVENT_DROP_INTR (1 << 1) -#define HALF_FRAME_INTR (1 << 2) -#define END_FRAME_INTR (1 << 3) -#define LAST_FRAME_INTR (1 << 4) -#define END_BLOCK_INTR (1 << 5) -#define SYNC (1 << 6) -#define END_PKT_INTR (1 << 7) -#define TRANS_ERR_INTR (1 << 8) -#define MISALIGN_INTR (1 << 11) - -static inline void omap_dma_interrupts_update(struct omap_dma_s *s) -{ - s->intr_update(s); -} - -static void omap_dma_channel_load(struct omap_dma_channel_s *ch) -{ - struct omap_dma_reg_set_s *a = &ch->active_set; - int i, normal; - int omap_3_1 = !ch->omap_3_1_compatible_disable; - - /* - * TODO: verify address ranges and alignment - * TODO: port endianness - */ - - a->src = ch->addr[0]; - a->dest = ch->addr[1]; - a->frames = ch->frames; - a->elements = ch->elements; - a->pck_elements = ch->frame_index[!ch->src_sync]; - a->frame = 0; - a->element = 0; - a->pck_element = 0; - - if (unlikely(!ch->elements || !ch->frames)) { - printf("%s: bad DMA request\n", __FUNCTION__); - return; - } - - for (i = 0; i < 2; i ++) - switch (ch->mode[i]) { - case constant: - a->elem_delta[i] = 0; - a->frame_delta[i] = 0; - break; - case post_incremented: - a->elem_delta[i] = ch->data_type; - a->frame_delta[i] = 0; - break; - case single_index: - a->elem_delta[i] = ch->data_type + - ch->element_index[omap_3_1 ? 0 : i] - 1; - a->frame_delta[i] = 0; - break; - case double_index: - a->elem_delta[i] = ch->data_type + - ch->element_index[omap_3_1 ? 0 : i] - 1; - a->frame_delta[i] = ch->frame_index[omap_3_1 ? 0 : i] - - ch->element_index[omap_3_1 ? 0 : i]; - break; - default: - break; - } - - normal = !ch->transparent_copy && !ch->constant_fill && - /* FIFO is big-endian so either (ch->endian[n] == 1) OR - * (ch->endian_lock[n] == 1) mean no endianism conversion. */ - (ch->endian[0] | ch->endian_lock[0]) == - (ch->endian[1] | ch->endian_lock[1]); - for (i = 0; i < 2; i ++) { - /* TODO: for a->frame_delta[i] > 0 still use the fast path, just - * limit min_elems in omap_dma_transfer_setup to the nearest frame - * end. */ - if (!a->elem_delta[i] && normal && - (a->frames == 1 || !a->frame_delta[i])) - ch->dma->type[i] = soc_dma_access_const; - else if (a->elem_delta[i] == ch->data_type && normal && - (a->frames == 1 || !a->frame_delta[i])) - ch->dma->type[i] = soc_dma_access_linear; - else - ch->dma->type[i] = soc_dma_access_other; - - ch->dma->vaddr[i] = ch->addr[i]; - } - soc_dma_ch_update(ch->dma); -} - -static void omap_dma_activate_channel(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - if (!ch->active) { - if (ch->set_update) { - /* It's not clear when the active set is supposed to be - * loaded from registers. We're already loading it when the - * channel is enabled, and for some guests this is not enough - * but that may be also because of a race condition (no - * delays in qemu) in the guest code, which we're just - * working around here. */ - omap_dma_channel_load(ch); - ch->set_update = 0; - } - - ch->active = 1; - soc_dma_set_request(ch->dma, 1); - if (ch->sync) - ch->status |= SYNC; - } -} - -static void omap_dma_deactivate_channel(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - /* Update cpc */ - ch->cpc = ch->active_set.dest & 0xffff; - - if (ch->pending_request && !ch->waiting_end_prog && ch->enable) { - /* Don't deactivate the channel */ - ch->pending_request = 0; - return; - } - - /* Don't deactive the channel if it is synchronized and the DMA request is - active */ - if (ch->sync && ch->enable && (s->dma->drqbmp & (1ULL << ch->sync))) - return; - - if (ch->active) { - ch->active = 0; - ch->status &= ~SYNC; - soc_dma_set_request(ch->dma, 0); - } -} - -static void omap_dma_enable_channel(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - if (!ch->enable) { - ch->enable = 1; - ch->waiting_end_prog = 0; - omap_dma_channel_load(ch); - /* TODO: theoretically if ch->sync && ch->prefetch && - * !s->dma->drqbmp[ch->sync], we should also activate and fetch - * from source and then stall until signalled. */ - if ((!ch->sync) || (s->dma->drqbmp & (1ULL << ch->sync))) { - omap_dma_activate_channel(s, ch); - } - } -} - -static void omap_dma_disable_channel(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - if (ch->enable) { - ch->enable = 0; - /* Discard any pending request */ - ch->pending_request = 0; - omap_dma_deactivate_channel(s, ch); - } -} - -static void omap_dma_channel_end_prog(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - if (ch->waiting_end_prog) { - ch->waiting_end_prog = 0; - if (!ch->sync || ch->pending_request) { - ch->pending_request = 0; - omap_dma_activate_channel(s, ch); - } - } -} - -static void omap_dma_interrupts_3_1_update(struct omap_dma_s *s) -{ - struct omap_dma_channel_s *ch = s->ch; - - /* First three interrupts are shared between two channels each. */ - if (ch[0].status | ch[6].status) - qemu_irq_raise(ch[0].irq); - if (ch[1].status | ch[7].status) - qemu_irq_raise(ch[1].irq); - if (ch[2].status | ch[8].status) - qemu_irq_raise(ch[2].irq); - if (ch[3].status) - qemu_irq_raise(ch[3].irq); - if (ch[4].status) - qemu_irq_raise(ch[4].irq); - if (ch[5].status) - qemu_irq_raise(ch[5].irq); -} - -static void omap_dma_interrupts_3_2_update(struct omap_dma_s *s) -{ - struct omap_dma_channel_s *ch = s->ch; - int i; - - for (i = s->chans; i; ch ++, i --) - if (ch->status) - qemu_irq_raise(ch->irq); -} - -static void omap_dma_enable_3_1_mapping(struct omap_dma_s *s) -{ - s->omap_3_1_mapping_disabled = 0; - s->chans = 9; - s->intr_update = omap_dma_interrupts_3_1_update; -} - -static void omap_dma_disable_3_1_mapping(struct omap_dma_s *s) -{ - s->omap_3_1_mapping_disabled = 1; - s->chans = 16; - s->intr_update = omap_dma_interrupts_3_2_update; -} - -static void omap_dma_process_request(struct omap_dma_s *s, int request) -{ - int channel; - int drop_event = 0; - struct omap_dma_channel_s *ch = s->ch; - - for (channel = 0; channel < s->chans; channel ++, ch ++) { - if (ch->enable && ch->sync == request) { - if (!ch->active) - omap_dma_activate_channel(s, ch); - else if (!ch->pending_request) - ch->pending_request = 1; - else { - /* Request collision */ - /* Second request received while processing other request */ - ch->status |= EVENT_DROP_INTR; - drop_event = 1; - } - } - } - - if (drop_event) - omap_dma_interrupts_update(s); -} - -static void omap_dma_transfer_generic(struct soc_dma_ch_s *dma) -{ - uint8_t value[4]; - struct omap_dma_channel_s *ch = dma->opaque; - struct omap_dma_reg_set_s *a = &ch->active_set; - int bytes = dma->bytes; -#ifdef MULTI_REQ - uint16_t status = ch->status; -#endif - - do { - /* Transfer a single element */ - /* FIXME: check the endianness */ - if (!ch->constant_fill) - cpu_physical_memory_read(a->src, value, ch->data_type); - else - *(uint32_t *) value = ch->color; - - if (!ch->transparent_copy || *(uint32_t *) value != ch->color) - cpu_physical_memory_write(a->dest, value, ch->data_type); - - a->src += a->elem_delta[0]; - a->dest += a->elem_delta[1]; - a->element ++; - -#ifndef MULTI_REQ - if (a->element == a->elements) { - /* End of Frame */ - a->element = 0; - a->src += a->frame_delta[0]; - a->dest += a->frame_delta[1]; - a->frame ++; - - /* If the channel is async, update cpc */ - if (!ch->sync) - ch->cpc = a->dest & 0xffff; - } - } while ((bytes -= ch->data_type)); -#else - /* If the channel is element synchronized, deactivate it */ - if (ch->sync && !ch->fs && !ch->bs) - omap_dma_deactivate_channel(s, ch); - - /* If it is the last frame, set the LAST_FRAME interrupt */ - if (a->element == 1 && a->frame == a->frames - 1) - if (ch->interrupts & LAST_FRAME_INTR) - ch->status |= LAST_FRAME_INTR; - - /* If the half of the frame was reached, set the HALF_FRAME - interrupt */ - if (a->element == (a->elements >> 1)) - if (ch->interrupts & HALF_FRAME_INTR) - ch->status |= HALF_FRAME_INTR; - - if (ch->fs && ch->bs) { - a->pck_element ++; - /* Check if a full packet has beed transferred. */ - if (a->pck_element == a->pck_elements) { - a->pck_element = 0; - - /* Set the END_PKT interrupt */ - if ((ch->interrupts & END_PKT_INTR) && !ch->src_sync) - ch->status |= END_PKT_INTR; - - /* If the channel is packet-synchronized, deactivate it */ - if (ch->sync) - omap_dma_deactivate_channel(s, ch); - } - } - - if (a->element == a->elements) { - /* End of Frame */ - a->element = 0; - a->src += a->frame_delta[0]; - a->dest += a->frame_delta[1]; - a->frame ++; - - /* If the channel is frame synchronized, deactivate it */ - if (ch->sync && ch->fs && !ch->bs) - omap_dma_deactivate_channel(s, ch); - - /* If the channel is async, update cpc */ - if (!ch->sync) - ch->cpc = a->dest & 0xffff; - - /* Set the END_FRAME interrupt */ - if (ch->interrupts & END_FRAME_INTR) - ch->status |= END_FRAME_INTR; - - if (a->frame == a->frames) { - /* End of Block */ - /* Disable the channel */ - - if (ch->omap_3_1_compatible_disable) { - omap_dma_disable_channel(s, ch); - if (ch->link_enabled) - omap_dma_enable_channel(s, - &s->ch[ch->link_next_ch]); - } else { - if (!ch->auto_init) - omap_dma_disable_channel(s, ch); - else if (ch->repeat || ch->end_prog) - omap_dma_channel_load(ch); - else { - ch->waiting_end_prog = 1; - omap_dma_deactivate_channel(s, ch); - } - } - - if (ch->interrupts & END_BLOCK_INTR) - ch->status |= END_BLOCK_INTR; - } - } - } while (status == ch->status && ch->active); - - omap_dma_interrupts_update(s); -#endif -} - -enum { - omap_dma_intr_element_sync, - omap_dma_intr_last_frame, - omap_dma_intr_half_frame, - omap_dma_intr_frame, - omap_dma_intr_frame_sync, - omap_dma_intr_packet, - omap_dma_intr_packet_sync, - omap_dma_intr_block, - __omap_dma_intr_last, -}; - -static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) -{ - struct omap_dma_port_if_s *src_p, *dest_p; - struct omap_dma_reg_set_s *a; - struct omap_dma_channel_s *ch = dma->opaque; - struct omap_dma_s *s = dma->dma->opaque; - int frames, min_elems, elements[__omap_dma_intr_last]; - - a = &ch->active_set; - - src_p = &s->mpu->port[ch->port[0]]; - dest_p = &s->mpu->port[ch->port[1]]; - if ((!ch->constant_fill && !src_p->addr_valid(s->mpu, a->src)) || - (!dest_p->addr_valid(s->mpu, a->dest))) { -#if 0 - /* Bus time-out */ - if (ch->interrupts & TIMEOUT_INTR) - ch->status |= TIMEOUT_INTR; - omap_dma_deactivate_channel(s, ch); - continue; -#endif - printf("%s: Bus time-out in DMA%i operation\n", - __FUNCTION__, dma->num); - } - - min_elems = INT_MAX; - - /* Check all the conditions that terminate the transfer starting - * with those that can occur the soonest. */ -#define INTR_CHECK(cond, id, nelements) \ - if (cond) { \ - elements[id] = nelements; \ - if (elements[id] < min_elems) \ - min_elems = elements[id]; \ - } else \ - elements[id] = INT_MAX; - - /* Elements */ - INTR_CHECK( - ch->sync && !ch->fs && !ch->bs, - omap_dma_intr_element_sync, - 1) - - /* Frames */ - /* TODO: for transfers where entire frames can be read and written - * using memcpy() but a->frame_delta is non-zero, try to still do - * transfers using soc_dma but limit min_elems to a->elements - ... - * See also the TODO in omap_dma_channel_load. */ - INTR_CHECK( - (ch->interrupts & LAST_FRAME_INTR) && - ((a->frame < a->frames - 1) || !a->element), - omap_dma_intr_last_frame, - (a->frames - a->frame - 2) * a->elements + - (a->elements - a->element + 1)) - INTR_CHECK( - ch->interrupts & HALF_FRAME_INTR, - omap_dma_intr_half_frame, - (a->elements >> 1) + - (a->element >= (a->elements >> 1) ? a->elements : 0) - - a->element) - INTR_CHECK( - ch->sync && ch->fs && (ch->interrupts & END_FRAME_INTR), - omap_dma_intr_frame, - a->elements - a->element) - INTR_CHECK( - ch->sync && ch->fs && !ch->bs, - omap_dma_intr_frame_sync, - a->elements - a->element) - - /* Packets */ - INTR_CHECK( - ch->fs && ch->bs && - (ch->interrupts & END_PKT_INTR) && !ch->src_sync, - omap_dma_intr_packet, - a->pck_elements - a->pck_element) - INTR_CHECK( - ch->fs && ch->bs && ch->sync, - omap_dma_intr_packet_sync, - a->pck_elements - a->pck_element) - - /* Blocks */ - INTR_CHECK( - 1, - omap_dma_intr_block, - (a->frames - a->frame - 1) * a->elements + - (a->elements - a->element)) - - dma->bytes = min_elems * ch->data_type; - - /* Set appropriate interrupts and/or deactivate channels */ - -#ifdef MULTI_REQ - /* TODO: should all of this only be done if dma->update, and otherwise - * inside omap_dma_transfer_generic below - check what's faster. */ - if (dma->update) { -#endif - - /* If the channel is element synchronized, deactivate it */ - if (min_elems == elements[omap_dma_intr_element_sync]) - omap_dma_deactivate_channel(s, ch); - - /* If it is the last frame, set the LAST_FRAME interrupt */ - if (min_elems == elements[omap_dma_intr_last_frame]) - ch->status |= LAST_FRAME_INTR; - - /* If exactly half of the frame was reached, set the HALF_FRAME - interrupt */ - if (min_elems == elements[omap_dma_intr_half_frame]) - ch->status |= HALF_FRAME_INTR; - - /* If a full packet has been transferred, set the END_PKT interrupt */ - if (min_elems == elements[omap_dma_intr_packet]) - ch->status |= END_PKT_INTR; - - /* If the channel is packet-synchronized, deactivate it */ - if (min_elems == elements[omap_dma_intr_packet_sync]) - omap_dma_deactivate_channel(s, ch); - - /* If the channel is frame synchronized, deactivate it */ - if (min_elems == elements[omap_dma_intr_frame_sync]) - omap_dma_deactivate_channel(s, ch); - - /* Set the END_FRAME interrupt */ - if (min_elems == elements[omap_dma_intr_frame]) - ch->status |= END_FRAME_INTR; - - if (min_elems == elements[omap_dma_intr_block]) { - /* End of Block */ - /* Disable the channel */ - - if (ch->omap_3_1_compatible_disable) { - omap_dma_disable_channel(s, ch); - if (ch->link_enabled) - omap_dma_enable_channel(s, &s->ch[ch->link_next_ch]); - } else { - if (!ch->auto_init) - omap_dma_disable_channel(s, ch); - else if (ch->repeat || ch->end_prog) - omap_dma_channel_load(ch); - else { - ch->waiting_end_prog = 1; - omap_dma_deactivate_channel(s, ch); - } - } - - if (ch->interrupts & END_BLOCK_INTR) - ch->status |= END_BLOCK_INTR; - } - - /* Update packet number */ - if (ch->fs && ch->bs) { - a->pck_element += min_elems; - a->pck_element %= a->pck_elements; - } - - /* TODO: check if we really need to update anything here or perhaps we - * can skip part of this. */ -#ifndef MULTI_REQ - if (dma->update) { -#endif - a->element += min_elems; - - frames = a->element / a->elements; - a->element = a->element % a->elements; - a->frame += frames; - a->src += min_elems * a->elem_delta[0] + frames * a->frame_delta[0]; - a->dest += min_elems * a->elem_delta[1] + frames * a->frame_delta[1]; - - /* If the channel is async, update cpc */ - if (!ch->sync && frames) - ch->cpc = a->dest & 0xffff; - - /* TODO: if the destination port is IMIF or EMIFF, set the dirty - * bits on it. */ -#ifndef MULTI_REQ - } -#else - } -#endif - - omap_dma_interrupts_update(s); -} - -void omap_dma_reset(struct soc_dma_s *dma) -{ - int i; - struct omap_dma_s *s = dma->opaque; - - soc_dma_reset(s->dma); - if (s->model < omap_dma_4) - s->gcr = 0x0004; - else - s->gcr = 0x00010010; - s->ocp = 0x00000000; - memset(&s->irqstat, 0, sizeof(s->irqstat)); - memset(&s->irqen, 0, sizeof(s->irqen)); - s->lcd_ch.src = emiff; - s->lcd_ch.condition = 0; - s->lcd_ch.interrupts = 0; - s->lcd_ch.dual = 0; - if (s->model < omap_dma_4) - omap_dma_enable_3_1_mapping(s); - for (i = 0; i < s->chans; i ++) { - s->ch[i].suspend = 0; - s->ch[i].prefetch = 0; - s->ch[i].buf_disable = 0; - s->ch[i].src_sync = 0; - memset(&s->ch[i].burst, 0, sizeof(s->ch[i].burst)); - memset(&s->ch[i].port, 0, sizeof(s->ch[i].port)); - memset(&s->ch[i].mode, 0, sizeof(s->ch[i].mode)); - memset(&s->ch[i].frame_index, 0, sizeof(s->ch[i].frame_index)); - memset(&s->ch[i].element_index, 0, sizeof(s->ch[i].element_index)); - memset(&s->ch[i].endian, 0, sizeof(s->ch[i].endian)); - memset(&s->ch[i].endian_lock, 0, sizeof(s->ch[i].endian_lock)); - memset(&s->ch[i].translate, 0, sizeof(s->ch[i].translate)); - s->ch[i].write_mode = 0; - s->ch[i].data_type = 0; - s->ch[i].transparent_copy = 0; - s->ch[i].constant_fill = 0; - s->ch[i].color = 0x00000000; - s->ch[i].end_prog = 0; - s->ch[i].repeat = 0; - s->ch[i].auto_init = 0; - s->ch[i].link_enabled = 0; - if (s->model < omap_dma_4) - s->ch[i].interrupts = 0x0003; - else - s->ch[i].interrupts = 0x0000; - s->ch[i].status = 0; - s->ch[i].cstatus = 0; - s->ch[i].active = 0; - s->ch[i].enable = 0; - s->ch[i].sync = 0; - s->ch[i].pending_request = 0; - s->ch[i].waiting_end_prog = 0; - s->ch[i].cpc = 0x0000; - s->ch[i].fs = 0; - s->ch[i].bs = 0; - s->ch[i].omap_3_1_compatible_disable = 0; - memset(&s->ch[i].active_set, 0, sizeof(s->ch[i].active_set)); - s->ch[i].priority = 0; - s->ch[i].interleave_disabled = 0; - s->ch[i].type = 0; - } -} - -static int omap_dma_ch_reg_read(struct omap_dma_s *s, - struct omap_dma_channel_s *ch, int reg, uint16_t *value) -{ - switch (reg) { - case 0x00: /* SYS_DMA_CSDP_CH0 */ - *value = (ch->burst[1] << 14) | - (ch->pack[1] << 13) | - (ch->port[1] << 9) | - (ch->burst[0] << 7) | - (ch->pack[0] << 6) | - (ch->port[0] << 2) | - (ch->data_type >> 1); - break; - - case 0x02: /* SYS_DMA_CCR_CH0 */ - if (s->model <= omap_dma_3_1) - *value = 0 << 10; /* FIFO_FLUSH reads as 0 */ - else - *value = ch->omap_3_1_compatible_disable << 10; - *value |= (ch->mode[1] << 14) | - (ch->mode[0] << 12) | - (ch->end_prog << 11) | - (ch->repeat << 9) | - (ch->auto_init << 8) | - (ch->enable << 7) | - (ch->priority << 6) | - (ch->fs << 5) | ch->sync; - break; - - case 0x04: /* SYS_DMA_CICR_CH0 */ - *value = ch->interrupts; - break; - - case 0x06: /* SYS_DMA_CSR_CH0 */ - *value = ch->status; - ch->status &= SYNC; - if (!ch->omap_3_1_compatible_disable && ch->sibling) { - *value |= (ch->sibling->status & 0x3f) << 6; - ch->sibling->status &= SYNC; - } - qemu_irq_lower(ch->irq); - break; - - case 0x08: /* SYS_DMA_CSSA_L_CH0 */ - *value = ch->addr[0] & 0x0000ffff; - break; - - case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ - *value = ch->addr[0] >> 16; - break; - - case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ - *value = ch->addr[1] & 0x0000ffff; - break; - - case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ - *value = ch->addr[1] >> 16; - break; - - case 0x10: /* SYS_DMA_CEN_CH0 */ - *value = ch->elements; - break; - - case 0x12: /* SYS_DMA_CFN_CH0 */ - *value = ch->frames; - break; - - case 0x14: /* SYS_DMA_CFI_CH0 */ - *value = ch->frame_index[0]; - break; - - case 0x16: /* SYS_DMA_CEI_CH0 */ - *value = ch->element_index[0]; - break; - - case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ - if (ch->omap_3_1_compatible_disable) - *value = ch->active_set.src & 0xffff; /* CSAC */ - else - *value = ch->cpc; - break; - - case 0x1a: /* DMA_CDAC */ - *value = ch->active_set.dest & 0xffff; /* CDAC */ - break; - - case 0x1c: /* DMA_CDEI */ - *value = ch->element_index[1]; - break; - - case 0x1e: /* DMA_CDFI */ - *value = ch->frame_index[1]; - break; - - case 0x20: /* DMA_COLOR_L */ - *value = ch->color & 0xffff; - break; - - case 0x22: /* DMA_COLOR_U */ - *value = ch->color >> 16; - break; - - case 0x24: /* DMA_CCR2 */ - *value = (ch->bs << 2) | - (ch->transparent_copy << 1) | - ch->constant_fill; - break; - - case 0x28: /* DMA_CLNK_CTRL */ - *value = (ch->link_enabled << 15) | - (ch->link_next_ch & 0xf); - break; - - case 0x2a: /* DMA_LCH_CTRL */ - *value = (ch->interleave_disabled << 15) | - ch->type; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_ch_reg_write(struct omap_dma_s *s, - struct omap_dma_channel_s *ch, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* SYS_DMA_CSDP_CH0 */ - ch->burst[1] = (value & 0xc000) >> 14; - ch->pack[1] = (value & 0x2000) >> 13; - ch->port[1] = (enum omap_dma_port) ((value & 0x1e00) >> 9); - ch->burst[0] = (value & 0x0180) >> 7; - ch->pack[0] = (value & 0x0040) >> 6; - ch->port[0] = (enum omap_dma_port) ((value & 0x003c) >> 2); - ch->data_type = 1 << (value & 3); - if (ch->port[0] >= __omap_dma_port_last) - printf("%s: invalid DMA port %i\n", __FUNCTION__, - ch->port[0]); - if (ch->port[1] >= __omap_dma_port_last) - printf("%s: invalid DMA port %i\n", __FUNCTION__, - ch->port[1]); - if ((value & 3) == 3) - printf("%s: bad data_type for DMA channel\n", __FUNCTION__); - break; - - case 0x02: /* SYS_DMA_CCR_CH0 */ - ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14); - ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12); - ch->end_prog = (value & 0x0800) >> 11; - if (s->model >= omap_dma_3_2) - ch->omap_3_1_compatible_disable = (value >> 10) & 0x1; - ch->repeat = (value & 0x0200) >> 9; - ch->auto_init = (value & 0x0100) >> 8; - ch->priority = (value & 0x0040) >> 6; - ch->fs = (value & 0x0020) >> 5; - ch->sync = value & 0x001f; - - if (value & 0x0080) - omap_dma_enable_channel(s, ch); - else - omap_dma_disable_channel(s, ch); - - if (ch->end_prog) - omap_dma_channel_end_prog(s, ch); - - break; - - case 0x04: /* SYS_DMA_CICR_CH0 */ - ch->interrupts = value & 0x3f; - break; - - case 0x06: /* SYS_DMA_CSR_CH0 */ - OMAP_RO_REG((hwaddr) reg); - break; - - case 0x08: /* SYS_DMA_CSSA_L_CH0 */ - ch->addr[0] &= 0xffff0000; - ch->addr[0] |= value; - break; - - case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ - ch->addr[0] &= 0x0000ffff; - ch->addr[0] |= (uint32_t) value << 16; - break; - - case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ - ch->addr[1] &= 0xffff0000; - ch->addr[1] |= value; - break; - - case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ - ch->addr[1] &= 0x0000ffff; - ch->addr[1] |= (uint32_t) value << 16; - break; - - case 0x10: /* SYS_DMA_CEN_CH0 */ - ch->elements = value; - break; - - case 0x12: /* SYS_DMA_CFN_CH0 */ - ch->frames = value; - break; - - case 0x14: /* SYS_DMA_CFI_CH0 */ - ch->frame_index[0] = (int16_t) value; - break; - - case 0x16: /* SYS_DMA_CEI_CH0 */ - ch->element_index[0] = (int16_t) value; - break; - - case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ - OMAP_RO_REG((hwaddr) reg); - break; - - case 0x1c: /* DMA_CDEI */ - ch->element_index[1] = (int16_t) value; - break; - - case 0x1e: /* DMA_CDFI */ - ch->frame_index[1] = (int16_t) value; - break; - - case 0x20: /* DMA_COLOR_L */ - ch->color &= 0xffff0000; - ch->color |= value; - break; - - case 0x22: /* DMA_COLOR_U */ - ch->color &= 0xffff; - ch->color |= (uint32_t)value << 16; - break; - - case 0x24: /* DMA_CCR2 */ - ch->bs = (value >> 2) & 0x1; - ch->transparent_copy = (value >> 1) & 0x1; - ch->constant_fill = value & 0x1; - break; - - case 0x28: /* DMA_CLNK_CTRL */ - ch->link_enabled = (value >> 15) & 0x1; - if (value & (1 << 14)) { /* Stop_Lnk */ - ch->link_enabled = 0; - omap_dma_disable_channel(s, ch); - } - ch->link_next_ch = value & 0x1f; - break; - - case 0x2a: /* DMA_LCH_CTRL */ - ch->interleave_disabled = (value >> 15) & 0x1; - ch->type = value & 0xf; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, - uint16_t value) -{ - switch (offset) { - case 0xbc0: /* DMA_LCD_CSDP */ - s->brust_f2 = (value >> 14) & 0x3; - s->pack_f2 = (value >> 13) & 0x1; - s->data_type_f2 = (1 << ((value >> 11) & 0x3)); - s->brust_f1 = (value >> 7) & 0x3; - s->pack_f1 = (value >> 6) & 0x1; - s->data_type_f1 = (1 << ((value >> 0) & 0x3)); - break; - - case 0xbc2: /* DMA_LCD_CCR */ - s->mode_f2 = (value >> 14) & 0x3; - s->mode_f1 = (value >> 12) & 0x3; - s->end_prog = (value >> 11) & 0x1; - s->omap_3_1_compatible_disable = (value >> 10) & 0x1; - s->repeat = (value >> 9) & 0x1; - s->auto_init = (value >> 8) & 0x1; - s->running = (value >> 7) & 0x1; - s->priority = (value >> 6) & 0x1; - s->bs = (value >> 4) & 0x1; - break; - - case 0xbc4: /* DMA_LCD_CTRL */ - s->dst = (value >> 8) & 0x1; - s->src = ((value >> 6) & 0x3) << 1; - s->condition = 0; - /* Assume no bus errors and thus no BUS_ERROR irq bits. */ - s->interrupts = (value >> 1) & 1; - s->dual = value & 1; - break; - - case 0xbc8: /* TOP_B1_L */ - s->src_f1_top &= 0xffff0000; - s->src_f1_top |= 0x0000ffff & value; - break; - - case 0xbca: /* TOP_B1_U */ - s->src_f1_top &= 0x0000ffff; - s->src_f1_top |= (uint32_t)value << 16; - break; - - case 0xbcc: /* BOT_B1_L */ - s->src_f1_bottom &= 0xffff0000; - s->src_f1_bottom |= 0x0000ffff & value; - break; - - case 0xbce: /* BOT_B1_U */ - s->src_f1_bottom &= 0x0000ffff; - s->src_f1_bottom |= (uint32_t) value << 16; - break; - - case 0xbd0: /* TOP_B2_L */ - s->src_f2_top &= 0xffff0000; - s->src_f2_top |= 0x0000ffff & value; - break; - - case 0xbd2: /* TOP_B2_U */ - s->src_f2_top &= 0x0000ffff; - s->src_f2_top |= (uint32_t) value << 16; - break; - - case 0xbd4: /* BOT_B2_L */ - s->src_f2_bottom &= 0xffff0000; - s->src_f2_bottom |= 0x0000ffff & value; - break; - - case 0xbd6: /* BOT_B2_U */ - s->src_f2_bottom &= 0x0000ffff; - s->src_f2_bottom |= (uint32_t) value << 16; - break; - - case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ - s->element_index_f1 = value; - break; - - case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ - s->frame_index_f1 &= 0xffff0000; - s->frame_index_f1 |= 0x0000ffff & value; - break; - - case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ - s->frame_index_f1 &= 0x0000ffff; - s->frame_index_f1 |= (uint32_t) value << 16; - break; - - case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ - s->element_index_f2 = value; - break; - - case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ - s->frame_index_f2 &= 0xffff0000; - s->frame_index_f2 |= 0x0000ffff & value; - break; - - case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ - s->frame_index_f2 &= 0x0000ffff; - s->frame_index_f2 |= (uint32_t) value << 16; - break; - - case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ - s->elements_f1 = value; - break; - - case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ - s->frames_f1 = value; - break; - - case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ - s->elements_f2 = value; - break; - - case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ - s->frames_f2 = value; - break; - - case 0xbea: /* DMA_LCD_LCH_CTRL */ - s->lch_type = value & 0xf; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, - uint16_t *ret) -{ - switch (offset) { - case 0xbc0: /* DMA_LCD_CSDP */ - *ret = (s->brust_f2 << 14) | - (s->pack_f2 << 13) | - ((s->data_type_f2 >> 1) << 11) | - (s->brust_f1 << 7) | - (s->pack_f1 << 6) | - ((s->data_type_f1 >> 1) << 0); - break; - - case 0xbc2: /* DMA_LCD_CCR */ - *ret = (s->mode_f2 << 14) | - (s->mode_f1 << 12) | - (s->end_prog << 11) | - (s->omap_3_1_compatible_disable << 10) | - (s->repeat << 9) | - (s->auto_init << 8) | - (s->running << 7) | - (s->priority << 6) | - (s->bs << 4); - break; - - case 0xbc4: /* DMA_LCD_CTRL */ - qemu_irq_lower(s->irq); - *ret = (s->dst << 8) | - ((s->src & 0x6) << 5) | - (s->condition << 3) | - (s->interrupts << 1) | - s->dual; - break; - - case 0xbc8: /* TOP_B1_L */ - *ret = s->src_f1_top & 0xffff; - break; - - case 0xbca: /* TOP_B1_U */ - *ret = s->src_f1_top >> 16; - break; - - case 0xbcc: /* BOT_B1_L */ - *ret = s->src_f1_bottom & 0xffff; - break; - - case 0xbce: /* BOT_B1_U */ - *ret = s->src_f1_bottom >> 16; - break; - - case 0xbd0: /* TOP_B2_L */ - *ret = s->src_f2_top & 0xffff; - break; - - case 0xbd2: /* TOP_B2_U */ - *ret = s->src_f2_top >> 16; - break; - - case 0xbd4: /* BOT_B2_L */ - *ret = s->src_f2_bottom & 0xffff; - break; - - case 0xbd6: /* BOT_B2_U */ - *ret = s->src_f2_bottom >> 16; - break; - - case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ - *ret = s->element_index_f1; - break; - - case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ - *ret = s->frame_index_f1 & 0xffff; - break; - - case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ - *ret = s->frame_index_f1 >> 16; - break; - - case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ - *ret = s->element_index_f2; - break; - - case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ - *ret = s->frame_index_f2 & 0xffff; - break; - - case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ - *ret = s->frame_index_f2 >> 16; - break; - - case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ - *ret = s->elements_f1; - break; - - case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ - *ret = s->frames_f1; - break; - - case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ - *ret = s->elements_f2; - break; - - case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ - *ret = s->frames_f2; - break; - - case 0xbea: /* DMA_LCD_LCH_CTRL */ - *ret = s->lch_type; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_3_1_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, - uint16_t value) -{ - switch (offset) { - case 0x300: /* SYS_DMA_LCD_CTRL */ - s->src = (value & 0x40) ? imif : emiff; - s->condition = 0; - /* Assume no bus errors and thus no BUS_ERROR irq bits. */ - s->interrupts = (value >> 1) & 1; - s->dual = value & 1; - break; - - case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ - s->src_f1_top &= 0xffff0000; - s->src_f1_top |= 0x0000ffff & value; - break; - - case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ - s->src_f1_top &= 0x0000ffff; - s->src_f1_top |= (uint32_t)value << 16; - break; - - case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ - s->src_f1_bottom &= 0xffff0000; - s->src_f1_bottom |= 0x0000ffff & value; - break; - - case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ - s->src_f1_bottom &= 0x0000ffff; - s->src_f1_bottom |= (uint32_t)value << 16; - break; - - case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ - s->src_f2_top &= 0xffff0000; - s->src_f2_top |= 0x0000ffff & value; - break; - - case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ - s->src_f2_top &= 0x0000ffff; - s->src_f2_top |= (uint32_t)value << 16; - break; - - case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ - s->src_f2_bottom &= 0xffff0000; - s->src_f2_bottom |= 0x0000ffff & value; - break; - - case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ - s->src_f2_bottom &= 0x0000ffff; - s->src_f2_bottom |= (uint32_t)value << 16; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_3_1_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, - uint16_t *ret) -{ - int i; - - switch (offset) { - case 0x300: /* SYS_DMA_LCD_CTRL */ - i = s->condition; - s->condition = 0; - qemu_irq_lower(s->irq); - *ret = ((s->src == imif) << 6) | (i << 3) | - (s->interrupts << 1) | s->dual; - break; - - case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ - *ret = s->src_f1_top & 0xffff; - break; - - case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ - *ret = s->src_f1_top >> 16; - break; - - case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ - *ret = s->src_f1_bottom & 0xffff; - break; - - case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ - *ret = s->src_f1_bottom >> 16; - break; - - case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ - *ret = s->src_f2_top & 0xffff; - break; - - case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ - *ret = s->src_f2_top >> 16; - break; - - case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ - *ret = s->src_f2_bottom & 0xffff; - break; - - case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ - *ret = s->src_f2_bottom >> 16; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_sys_write(struct omap_dma_s *s, int offset, uint16_t value) -{ - switch (offset) { - case 0x400: /* SYS_DMA_GCR */ - s->gcr = value; - break; - - case 0x404: /* DMA_GSCR */ - if (value & 0x8) - omap_dma_disable_3_1_mapping(s); - else - omap_dma_enable_3_1_mapping(s); - break; - - case 0x408: /* DMA_GRST */ - if (value & 0x1) - omap_dma_reset(s->dma); - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_sys_read(struct omap_dma_s *s, int offset, - uint16_t *ret) -{ - switch (offset) { - case 0x400: /* SYS_DMA_GCR */ - *ret = s->gcr; - break; - - case 0x404: /* DMA_GSCR */ - *ret = s->omap_3_1_mapping_disabled << 3; - break; - - case 0x408: /* DMA_GRST */ - *ret = 0; - break; - - case 0x442: /* DMA_HW_ID */ - case 0x444: /* DMA_PCh2_ID */ - case 0x446: /* DMA_PCh0_ID */ - case 0x448: /* DMA_PCh1_ID */ - case 0x44a: /* DMA_PChG_ID */ - case 0x44c: /* DMA_PChD_ID */ - *ret = 1; - break; - - case 0x44e: /* DMA_CAPS_0_U */ - *ret = (s->caps[0] >> 16) & 0xffff; - break; - case 0x450: /* DMA_CAPS_0_L */ - *ret = (s->caps[0] >> 0) & 0xffff; - break; - - case 0x452: /* DMA_CAPS_1_U */ - *ret = (s->caps[1] >> 16) & 0xffff; - break; - case 0x454: /* DMA_CAPS_1_L */ - *ret = (s->caps[1] >> 0) & 0xffff; - break; - - case 0x456: /* DMA_CAPS_2 */ - *ret = s->caps[2]; - break; - - case 0x458: /* DMA_CAPS_3 */ - *ret = s->caps[3]; - break; - - case 0x45a: /* DMA_CAPS_4 */ - *ret = s->caps[4]; - break; - - case 0x460: /* DMA_PCh2_SR */ - case 0x480: /* DMA_PCh0_SR */ - case 0x482: /* DMA_PCh1_SR */ - case 0x4c0: /* DMA_PChD_SR_0 */ - printf("%s: Physical Channel Status Registers not implemented.\n", - __FUNCTION__); - *ret = 0xff; - break; - - default: - return 1; - } - return 0; -} - -static uint64_t omap_dma_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int reg, ch; - uint16_t ret; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x300 ... 0x3fe: - if (s->model <= omap_dma_3_1 || !s->omap_3_1_mapping_disabled) { - if (omap_dma_3_1_lcd_read(&s->lcd_ch, addr, &ret)) - break; - return ret; - } - /* Fall through. */ - case 0x000 ... 0x2fe: - reg = addr & 0x3f; - ch = (addr >> 6) & 0x0f; - if (omap_dma_ch_reg_read(s, &s->ch[ch], reg, &ret)) - break; - return ret; - - case 0x404 ... 0x4fe: - if (s->model <= omap_dma_3_1) - break; - /* Fall through. */ - case 0x400: - if (omap_dma_sys_read(s, addr, &ret)) - break; - return ret; - - case 0xb00 ... 0xbfe: - if (s->model == omap_dma_3_2 && s->omap_3_1_mapping_disabled) { - if (omap_dma_3_2_lcd_read(&s->lcd_ch, addr, &ret)) - break; - return ret; - } - break; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_dma_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int reg, ch; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x300 ... 0x3fe: - if (s->model <= omap_dma_3_1 || !s->omap_3_1_mapping_disabled) { - if (omap_dma_3_1_lcd_write(&s->lcd_ch, addr, value)) - break; - return; - } - /* Fall through. */ - case 0x000 ... 0x2fe: - reg = addr & 0x3f; - ch = (addr >> 6) & 0x0f; - if (omap_dma_ch_reg_write(s, &s->ch[ch], reg, value)) - break; - return; - - case 0x404 ... 0x4fe: - if (s->model <= omap_dma_3_1) - break; - case 0x400: - /* Fall through. */ - if (omap_dma_sys_write(s, addr, value)) - break; - return; - - case 0xb00 ... 0xbfe: - if (s->model == omap_dma_3_2 && s->omap_3_1_mapping_disabled) { - if (omap_dma_3_2_lcd_write(&s->lcd_ch, addr, value)) - break; - return; - } - break; - } - - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap_dma_ops = { - .read = omap_dma_read, - .write = omap_dma_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_dma_request(void *opaque, int drq, int req) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - /* The request pins are level triggered in QEMU. */ - if (req) { - if (~s->dma->drqbmp & (1ULL << drq)) { - s->dma->drqbmp |= 1ULL << drq; - omap_dma_process_request(s, drq); - } - } else - s->dma->drqbmp &= ~(1ULL << drq); -} - -/* XXX: this won't be needed once soc_dma knows about clocks. */ -static void omap_dma_clk_update(void *opaque, int line, int on) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int i; - - s->dma->freq = omap_clk_getrate(s->clk); - - for (i = 0; i < s->chans; i ++) - if (s->ch[i].active) - soc_dma_set_request(s->ch[i].dma, on); -} - -static void omap_dma_setcaps(struct omap_dma_s *s) -{ - switch (s->model) { - default: - case omap_dma_3_1: - break; - case omap_dma_3_2: - case omap_dma_4: - /* XXX Only available for sDMA */ - s->caps[0] = - (1 << 19) | /* Constant Fill Capability */ - (1 << 18); /* Transparent BLT Capability */ - s->caps[1] = - (1 << 1); /* 1-bit palettized capability (DMA 3.2 only) */ - s->caps[2] = - (1 << 8) | /* SEPARATE_SRC_AND_DST_INDEX_CPBLTY */ - (1 << 7) | /* DST_DOUBLE_INDEX_ADRS_CPBLTY */ - (1 << 6) | /* DST_SINGLE_INDEX_ADRS_CPBLTY */ - (1 << 5) | /* DST_POST_INCRMNT_ADRS_CPBLTY */ - (1 << 4) | /* DST_CONST_ADRS_CPBLTY */ - (1 << 3) | /* SRC_DOUBLE_INDEX_ADRS_CPBLTY */ - (1 << 2) | /* SRC_SINGLE_INDEX_ADRS_CPBLTY */ - (1 << 1) | /* SRC_POST_INCRMNT_ADRS_CPBLTY */ - (1 << 0); /* SRC_CONST_ADRS_CPBLTY */ - s->caps[3] = - (1 << 6) | /* BLOCK_SYNCHR_CPBLTY (DMA 4 only) */ - (1 << 7) | /* PKT_SYNCHR_CPBLTY (DMA 4 only) */ - (1 << 5) | /* CHANNEL_CHAINING_CPBLTY */ - (1 << 4) | /* LCh_INTERLEAVE_CPBLTY */ - (1 << 3) | /* AUTOINIT_REPEAT_CPBLTY (DMA 3.2 only) */ - (1 << 2) | /* AUTOINIT_ENDPROG_CPBLTY (DMA 3.2 only) */ - (1 << 1) | /* FRAME_SYNCHR_CPBLTY */ - (1 << 0); /* ELMNT_SYNCHR_CPBLTY */ - s->caps[4] = - (1 << 7) | /* PKT_INTERRUPT_CPBLTY (DMA 4 only) */ - (1 << 6) | /* SYNC_STATUS_CPBLTY */ - (1 << 5) | /* BLOCK_INTERRUPT_CPBLTY */ - (1 << 4) | /* LAST_FRAME_INTERRUPT_CPBLTY */ - (1 << 3) | /* FRAME_INTERRUPT_CPBLTY */ - (1 << 2) | /* HALF_FRAME_INTERRUPT_CPBLTY */ - (1 << 1) | /* EVENT_DROP_INTERRUPT_CPBLTY */ - (1 << 0); /* TIMEOUT_INTERRUPT_CPBLTY (DMA 3.2 only) */ - break; - } -} - -struct soc_dma_s *omap_dma_init(hwaddr base, qemu_irq *irqs, - MemoryRegion *sysmem, - qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk, - enum omap_dma_model model) -{ - int num_irqs, memsize, i; - struct omap_dma_s *s = g_new0(struct omap_dma_s, 1); - - if (model <= omap_dma_3_1) { - num_irqs = 6; - memsize = 0x800; - } else { - num_irqs = 16; - memsize = 0xc00; - } - s->model = model; - s->mpu = mpu; - s->clk = clk; - s->lcd_ch.irq = lcd_irq; - s->lcd_ch.mpu = mpu; - - s->dma = soc_dma_init((model <= omap_dma_3_1) ? 9 : 16); - s->dma->freq = omap_clk_getrate(clk); - s->dma->transfer_fn = omap_dma_transfer_generic; - s->dma->setup_fn = omap_dma_transfer_setup; - s->dma->drq = qemu_allocate_irqs(omap_dma_request, s, 32); - s->dma->opaque = s; - - while (num_irqs --) - s->ch[num_irqs].irq = irqs[num_irqs]; - for (i = 0; i < 3; i ++) { - s->ch[i].sibling = &s->ch[i + 6]; - s->ch[i + 6].sibling = &s->ch[i]; - } - for (i = (model <= omap_dma_3_1) ? 8 : 15; i >= 0; i --) { - s->ch[i].dma = &s->dma->ch[i]; - s->dma->ch[i].opaque = &s->ch[i]; - } - - omap_dma_setcaps(s); - omap_clk_adduser(s->clk, qemu_allocate_irq(omap_dma_clk_update, s, 0)); - omap_dma_reset(s->dma); - omap_dma_clk_update(s, 0, 1); - - memory_region_init_io(&s->iomem, NULL, &omap_dma_ops, s, "omap.dma", memsize); - memory_region_add_subregion(sysmem, base, &s->iomem); - - mpu->drq = s->dma->drq; - - return s->dma; -} - -static void omap_dma_interrupts_4_update(struct omap_dma_s *s) -{ - struct omap_dma_channel_s *ch = s->ch; - uint32_t bmp, bit; - - for (bmp = 0, bit = 1; bit; ch ++, bit <<= 1) - if (ch->status) { - bmp |= bit; - ch->cstatus |= ch->status; - ch->status = 0; - } - if ((s->irqstat[0] |= s->irqen[0] & bmp)) - qemu_irq_raise(s->irq[0]); - if ((s->irqstat[1] |= s->irqen[1] & bmp)) - qemu_irq_raise(s->irq[1]); - if ((s->irqstat[2] |= s->irqen[2] & bmp)) - qemu_irq_raise(s->irq[2]); - if ((s->irqstat[3] |= s->irqen[3] & bmp)) - qemu_irq_raise(s->irq[3]); -} - -static uint64_t omap_dma4_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int irqn = 0, chnum; - struct omap_dma_channel_s *ch; - - if (size == 1) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x00: /* DMA4_REVISION */ - return 0x40; - - case 0x14: /* DMA4_IRQSTATUS_L3 */ - irqn ++; - /* fall through */ - case 0x10: /* DMA4_IRQSTATUS_L2 */ - irqn ++; - /* fall through */ - case 0x0c: /* DMA4_IRQSTATUS_L1 */ - irqn ++; - /* fall through */ - case 0x08: /* DMA4_IRQSTATUS_L0 */ - return s->irqstat[irqn]; - - case 0x24: /* DMA4_IRQENABLE_L3 */ - irqn ++; - /* fall through */ - case 0x20: /* DMA4_IRQENABLE_L2 */ - irqn ++; - /* fall through */ - case 0x1c: /* DMA4_IRQENABLE_L1 */ - irqn ++; - /* fall through */ - case 0x18: /* DMA4_IRQENABLE_L0 */ - return s->irqen[irqn]; - - case 0x28: /* DMA4_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x2c: /* DMA4_OCP_SYSCONFIG */ - return s->ocp; - - case 0x64: /* DMA4_CAPS_0 */ - return s->caps[0]; - case 0x6c: /* DMA4_CAPS_2 */ - return s->caps[2]; - case 0x70: /* DMA4_CAPS_3 */ - return s->caps[3]; - case 0x74: /* DMA4_CAPS_4 */ - return s->caps[4]; - - case 0x78: /* DMA4_GCR */ - return s->gcr; - - case 0x80 ... 0xfff: - addr -= 0x80; - chnum = addr / 0x60; - ch = s->ch + chnum; - addr -= chnum * 0x60; - break; - - default: - OMAP_BAD_REG(addr); - return 0; - } - - /* Per-channel registers */ - switch (addr) { - case 0x00: /* DMA4_CCR */ - return (ch->buf_disable << 25) | - (ch->src_sync << 24) | - (ch->prefetch << 23) | - ((ch->sync & 0x60) << 14) | - (ch->bs << 18) | - (ch->transparent_copy << 17) | - (ch->constant_fill << 16) | - (ch->mode[1] << 14) | - (ch->mode[0] << 12) | - (0 << 10) | (0 << 9) | - (ch->suspend << 8) | - (ch->enable << 7) | - (ch->priority << 6) | - (ch->fs << 5) | (ch->sync & 0x1f); - - case 0x04: /* DMA4_CLNK_CTRL */ - return (ch->link_enabled << 15) | ch->link_next_ch; - - case 0x08: /* DMA4_CICR */ - return ch->interrupts; - - case 0x0c: /* DMA4_CSR */ - return ch->cstatus; - - case 0x10: /* DMA4_CSDP */ - return (ch->endian[0] << 21) | - (ch->endian_lock[0] << 20) | - (ch->endian[1] << 19) | - (ch->endian_lock[1] << 18) | - (ch->write_mode << 16) | - (ch->burst[1] << 14) | - (ch->pack[1] << 13) | - (ch->translate[1] << 9) | - (ch->burst[0] << 7) | - (ch->pack[0] << 6) | - (ch->translate[0] << 2) | - (ch->data_type >> 1); - - case 0x14: /* DMA4_CEN */ - return ch->elements; - - case 0x18: /* DMA4_CFN */ - return ch->frames; - - case 0x1c: /* DMA4_CSSA */ - return ch->addr[0]; - - case 0x20: /* DMA4_CDSA */ - return ch->addr[1]; - - case 0x24: /* DMA4_CSEI */ - return ch->element_index[0]; - - case 0x28: /* DMA4_CSFI */ - return ch->frame_index[0]; - - case 0x2c: /* DMA4_CDEI */ - return ch->element_index[1]; - - case 0x30: /* DMA4_CDFI */ - return ch->frame_index[1]; - - case 0x34: /* DMA4_CSAC */ - return ch->active_set.src & 0xffff; - - case 0x38: /* DMA4_CDAC */ - return ch->active_set.dest & 0xffff; - - case 0x3c: /* DMA4_CCEN */ - return ch->active_set.element; - - case 0x40: /* DMA4_CCFN */ - return ch->active_set.frame; - - case 0x44: /* DMA4_COLOR */ - /* XXX only in sDMA */ - return ch->color; - - default: - OMAP_BAD_REG(addr); - return 0; - } -} - -static void omap_dma4_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int chnum, irqn = 0; - struct omap_dma_channel_s *ch; - - if (size == 1) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x14: /* DMA4_IRQSTATUS_L3 */ - irqn ++; - /* fall through */ - case 0x10: /* DMA4_IRQSTATUS_L2 */ - irqn ++; - /* fall through */ - case 0x0c: /* DMA4_IRQSTATUS_L1 */ - irqn ++; - /* fall through */ - case 0x08: /* DMA4_IRQSTATUS_L0 */ - s->irqstat[irqn] &= ~value; - if (!s->irqstat[irqn]) - qemu_irq_lower(s->irq[irqn]); - return; - - case 0x24: /* DMA4_IRQENABLE_L3 */ - irqn ++; - /* fall through */ - case 0x20: /* DMA4_IRQENABLE_L2 */ - irqn ++; - /* fall through */ - case 0x1c: /* DMA4_IRQENABLE_L1 */ - irqn ++; - /* fall through */ - case 0x18: /* DMA4_IRQENABLE_L0 */ - s->irqen[irqn] = value; - return; - - case 0x2c: /* DMA4_OCP_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_dma_reset(s->dma); - s->ocp = value & 0x3321; - if (((s->ocp >> 12) & 3) == 3) /* MIDLEMODE */ - fprintf(stderr, "%s: invalid DMA power mode\n", __FUNCTION__); - return; - - case 0x78: /* DMA4_GCR */ - s->gcr = value & 0x00ff00ff; - if ((value & 0xff) == 0x00) /* MAX_CHANNEL_FIFO_DEPTH */ - fprintf(stderr, "%s: wrong FIFO depth in GCR\n", __FUNCTION__); - return; - - case 0x80 ... 0xfff: - addr -= 0x80; - chnum = addr / 0x60; - ch = s->ch + chnum; - addr -= chnum * 0x60; - break; - - case 0x00: /* DMA4_REVISION */ - case 0x28: /* DMA4_SYSSTATUS */ - case 0x64: /* DMA4_CAPS_0 */ - case 0x6c: /* DMA4_CAPS_2 */ - case 0x70: /* DMA4_CAPS_3 */ - case 0x74: /* DMA4_CAPS_4 */ - OMAP_RO_REG(addr); - return; - - default: - OMAP_BAD_REG(addr); - return; - } - - /* Per-channel registers */ - switch (addr) { - case 0x00: /* DMA4_CCR */ - ch->buf_disable = (value >> 25) & 1; - ch->src_sync = (value >> 24) & 1; /* XXX For CamDMA must be 1 */ - if (ch->buf_disable && !ch->src_sync) - fprintf(stderr, "%s: Buffering disable is not allowed in " - "destination synchronised mode\n", __FUNCTION__); - ch->prefetch = (value >> 23) & 1; - ch->bs = (value >> 18) & 1; - ch->transparent_copy = (value >> 17) & 1; - ch->constant_fill = (value >> 16) & 1; - ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14); - ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12); - ch->suspend = (value & 0x0100) >> 8; - ch->priority = (value & 0x0040) >> 6; - ch->fs = (value & 0x0020) >> 5; - if (ch->fs && ch->bs && ch->mode[0] && ch->mode[1]) - fprintf(stderr, "%s: For a packet transfer at least one port " - "must be constant-addressed\n", __FUNCTION__); - ch->sync = (value & 0x001f) | ((value >> 14) & 0x0060); - /* XXX must be 0x01 for CamDMA */ - - if (value & 0x0080) - omap_dma_enable_channel(s, ch); - else - omap_dma_disable_channel(s, ch); - - break; - - case 0x04: /* DMA4_CLNK_CTRL */ - ch->link_enabled = (value >> 15) & 0x1; - ch->link_next_ch = value & 0x1f; - break; - - case 0x08: /* DMA4_CICR */ - ch->interrupts = value & 0x09be; - break; - - case 0x0c: /* DMA4_CSR */ - ch->cstatus &= ~value; - break; - - case 0x10: /* DMA4_CSDP */ - ch->endian[0] =(value >> 21) & 1; - ch->endian_lock[0] =(value >> 20) & 1; - ch->endian[1] =(value >> 19) & 1; - ch->endian_lock[1] =(value >> 18) & 1; - if (ch->endian[0] != ch->endian[1]) - fprintf(stderr, "%s: DMA endiannes conversion enable attempt\n", - __FUNCTION__); - ch->write_mode = (value >> 16) & 3; - ch->burst[1] = (value & 0xc000) >> 14; - ch->pack[1] = (value & 0x2000) >> 13; - ch->translate[1] = (value & 0x1e00) >> 9; - ch->burst[0] = (value & 0x0180) >> 7; - ch->pack[0] = (value & 0x0040) >> 6; - ch->translate[0] = (value & 0x003c) >> 2; - if (ch->translate[0] | ch->translate[1]) - fprintf(stderr, "%s: bad MReqAddressTranslate sideband signal\n", - __FUNCTION__); - ch->data_type = 1 << (value & 3); - if ((value & 3) == 3) - printf("%s: bad data_type for DMA channel\n", __FUNCTION__); - break; - - case 0x14: /* DMA4_CEN */ - ch->set_update = 1; - ch->elements = value & 0xffffff; - break; - - case 0x18: /* DMA4_CFN */ - ch->frames = value & 0xffff; - ch->set_update = 1; - break; - - case 0x1c: /* DMA4_CSSA */ - ch->addr[0] = (hwaddr) (uint32_t) value; - ch->set_update = 1; - break; - - case 0x20: /* DMA4_CDSA */ - ch->addr[1] = (hwaddr) (uint32_t) value; - ch->set_update = 1; - break; - - case 0x24: /* DMA4_CSEI */ - ch->element_index[0] = (int16_t) value; - ch->set_update = 1; - break; - - case 0x28: /* DMA4_CSFI */ - ch->frame_index[0] = (int32_t) value; - ch->set_update = 1; - break; - - case 0x2c: /* DMA4_CDEI */ - ch->element_index[1] = (int16_t) value; - ch->set_update = 1; - break; - - case 0x30: /* DMA4_CDFI */ - ch->frame_index[1] = (int32_t) value; - ch->set_update = 1; - break; - - case 0x44: /* DMA4_COLOR */ - /* XXX only in sDMA */ - ch->color = value; - break; - - case 0x34: /* DMA4_CSAC */ - case 0x38: /* DMA4_CDAC */ - case 0x3c: /* DMA4_CCEN */ - case 0x40: /* DMA4_CCFN */ - OMAP_RO_REG(addr); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_dma4_ops = { - .read = omap_dma4_read, - .write = omap_dma4_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct soc_dma_s *omap_dma4_init(hwaddr base, qemu_irq *irqs, - MemoryRegion *sysmem, - struct omap_mpu_state_s *mpu, int fifo, - int chans, omap_clk iclk, omap_clk fclk) -{ - int i; - struct omap_dma_s *s = g_new0(struct omap_dma_s, 1); - - s->model = omap_dma_4; - s->chans = chans; - s->mpu = mpu; - s->clk = fclk; - - s->dma = soc_dma_init(s->chans); - s->dma->freq = omap_clk_getrate(fclk); - s->dma->transfer_fn = omap_dma_transfer_generic; - s->dma->setup_fn = omap_dma_transfer_setup; - s->dma->drq = qemu_allocate_irqs(omap_dma_request, s, 64); - s->dma->opaque = s; - for (i = 0; i < s->chans; i ++) { - s->ch[i].dma = &s->dma->ch[i]; - s->dma->ch[i].opaque = &s->ch[i]; - } - - memcpy(&s->irq, irqs, sizeof(s->irq)); - s->intr_update = omap_dma_interrupts_4_update; - - omap_dma_setcaps(s); - omap_clk_adduser(s->clk, qemu_allocate_irq(omap_dma_clk_update, s, 0)); - omap_dma_reset(s->dma); - omap_dma_clk_update(s, 0, !!s->dma->freq); - - memory_region_init_io(&s->iomem, NULL, &omap_dma4_ops, s, "omap.dma4", 0x1000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - mpu->drq = s->dma->drq; - - return s->dma; -} - -struct omap_dma_lcd_channel_s *omap_dma_get_lcdch(struct soc_dma_s *dma) -{ - struct omap_dma_s *s = dma->opaque; - - return &s->lcd_ch; -} diff --git a/qemu/hw/dma/pl080.c b/qemu/hw/dma/pl080.c deleted file mode 100644 index 9318108b8..000000000 --- a/qemu/hw/dma/pl080.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Arm PrimeCell PL080/PL081 DMA controller - * - * Copyright (c) 2006 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" - -#define PL080_MAX_CHANNELS 8 -#define PL080_CONF_E 0x1 -#define PL080_CONF_M1 0x2 -#define PL080_CONF_M2 0x4 - -#define PL080_CCONF_H 0x40000 -#define PL080_CCONF_A 0x20000 -#define PL080_CCONF_L 0x10000 -#define PL080_CCONF_ITC 0x08000 -#define PL080_CCONF_IE 0x04000 -#define PL080_CCONF_E 0x00001 - -#define PL080_CCTRL_I 0x80000000 -#define PL080_CCTRL_DI 0x08000000 -#define PL080_CCTRL_SI 0x04000000 -#define PL080_CCTRL_D 0x02000000 -#define PL080_CCTRL_S 0x01000000 - -typedef struct { - uint32_t src; - uint32_t dest; - uint32_t lli; - uint32_t ctrl; - uint32_t conf; -} pl080_channel; - -#define TYPE_PL080 "pl080" -#define PL080(obj) OBJECT_CHECK(PL080State, (obj), TYPE_PL080) - -typedef struct PL080State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint8_t tc_int; - uint8_t tc_mask; - uint8_t err_int; - uint8_t err_mask; - uint32_t conf; - uint32_t sync; - uint32_t req_single; - uint32_t req_burst; - pl080_channel chan[PL080_MAX_CHANNELS]; - int nchannels; - /* Flag to avoid recursive DMA invocations. */ - int running; - qemu_irq irq; -} PL080State; - -static const VMStateDescription vmstate_pl080_channel = { - .name = "pl080_channel", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(src, pl080_channel), - VMSTATE_UINT32(dest, pl080_channel), - VMSTATE_UINT32(lli, pl080_channel), - VMSTATE_UINT32(ctrl, pl080_channel), - VMSTATE_UINT32(conf, pl080_channel), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pl080 = { - .name = "pl080", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(tc_int, PL080State), - VMSTATE_UINT8(tc_mask, PL080State), - VMSTATE_UINT8(err_int, PL080State), - VMSTATE_UINT8(err_mask, PL080State), - VMSTATE_UINT32(conf, PL080State), - VMSTATE_UINT32(sync, PL080State), - VMSTATE_UINT32(req_single, PL080State), - VMSTATE_UINT32(req_burst, PL080State), - VMSTATE_UINT8(tc_int, PL080State), - VMSTATE_UINT8(tc_int, PL080State), - VMSTATE_UINT8(tc_int, PL080State), - VMSTATE_STRUCT_ARRAY(chan, PL080State, PL080_MAX_CHANNELS, - 1, vmstate_pl080_channel, pl080_channel), - VMSTATE_INT32(running, PL080State), - VMSTATE_END_OF_LIST() - } -}; - -static const unsigned char pl080_id[] = -{ 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; - -static const unsigned char pl081_id[] = -{ 0x81, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; - -static void pl080_update(PL080State *s) -{ - if ((s->tc_int & s->tc_mask) - || (s->err_int & s->err_mask)) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); -} - -static void pl080_run(PL080State *s) -{ - int c; - int flow; - pl080_channel *ch; - int swidth; - int dwidth; - int xsize; - int n; - int src_id; - int dest_id; - int size; - uint8_t buff[4]; - uint32_t req; - - s->tc_mask = 0; - for (c = 0; c < s->nchannels; c++) { - if (s->chan[c].conf & PL080_CCONF_ITC) - s->tc_mask |= 1 << c; - if (s->chan[c].conf & PL080_CCONF_IE) - s->err_mask |= 1 << c; - } - - if ((s->conf & PL080_CONF_E) == 0) - return; - -hw_error("DMA active\n"); - /* If we are already in the middle of a DMA operation then indicate that - there may be new DMA requests and return immediately. */ - if (s->running) { - s->running++; - return; - } - s->running = 1; - while (s->running) { - for (c = 0; c < s->nchannels; c++) { - ch = &s->chan[c]; -again: - /* Test if thiws channel has any pending DMA requests. */ - if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E)) - != PL080_CCONF_E) - continue; - flow = (ch->conf >> 11) & 7; - if (flow >= 4) { - hw_error( - "pl080_run: Peripheral flow control not implemented\n"); - } - src_id = (ch->conf >> 1) & 0x1f; - dest_id = (ch->conf >> 6) & 0x1f; - size = ch->ctrl & 0xfff; - req = s->req_single | s->req_burst; - switch (flow) { - case 0: - break; - case 1: - if ((req & (1u << dest_id)) == 0) - size = 0; - break; - case 2: - if ((req & (1u << src_id)) == 0) - size = 0; - break; - case 3: - if ((req & (1u << src_id)) == 0 - || (req & (1u << dest_id)) == 0) - size = 0; - break; - } - if (!size) - continue; - - /* Transfer one element. */ - /* ??? Should transfer multiple elements for a burst request. */ - /* ??? Unclear what the proper behavior is when source and - destination widths are different. */ - swidth = 1 << ((ch->ctrl >> 18) & 7); - dwidth = 1 << ((ch->ctrl >> 21) & 7); - for (n = 0; n < dwidth; n+= swidth) { - cpu_physical_memory_read(ch->src, buff + n, swidth); - if (ch->ctrl & PL080_CCTRL_SI) - ch->src += swidth; - } - xsize = (dwidth < swidth) ? swidth : dwidth; - /* ??? This may pad the value incorrectly for dwidth < 32. */ - for (n = 0; n < xsize; n += dwidth) { - cpu_physical_memory_write(ch->dest + n, buff + n, dwidth); - if (ch->ctrl & PL080_CCTRL_DI) - ch->dest += swidth; - } - - size--; - ch->ctrl = (ch->ctrl & 0xfffff000) | size; - if (size == 0) { - /* Transfer complete. */ - if (ch->lli) { - ch->src = address_space_ldl_le(&address_space_memory, - ch->lli, - MEMTXATTRS_UNSPECIFIED, - NULL); - ch->dest = address_space_ldl_le(&address_space_memory, - ch->lli + 4, - MEMTXATTRS_UNSPECIFIED, - NULL); - ch->ctrl = address_space_ldl_le(&address_space_memory, - ch->lli + 12, - MEMTXATTRS_UNSPECIFIED, - NULL); - ch->lli = address_space_ldl_le(&address_space_memory, - ch->lli + 8, - MEMTXATTRS_UNSPECIFIED, - NULL); - } else { - ch->conf &= ~PL080_CCONF_E; - } - if (ch->ctrl & PL080_CCTRL_I) { - s->tc_int |= 1 << c; - } - } - goto again; - } - if (--s->running) - s->running = 1; - } -} - -static uint64_t pl080_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL080State *s = (PL080State *)opaque; - uint32_t i; - uint32_t mask; - - if (offset >= 0xfe0 && offset < 0x1000) { - if (s->nchannels == 8) { - return pl080_id[(offset - 0xfe0) >> 2]; - } else { - return pl081_id[(offset - 0xfe0) >> 2]; - } - } - if (offset >= 0x100 && offset < 0x200) { - i = (offset & 0xe0) >> 5; - if (i >= s->nchannels) - goto bad_offset; - switch (offset >> 2) { - case 0: /* SrcAddr */ - return s->chan[i].src; - case 1: /* DestAddr */ - return s->chan[i].dest; - case 2: /* LLI */ - return s->chan[i].lli; - case 3: /* Control */ - return s->chan[i].ctrl; - case 4: /* Configuration */ - return s->chan[i].conf; - default: - goto bad_offset; - } - } - switch (offset >> 2) { - case 0: /* IntStatus */ - return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask); - case 1: /* IntTCStatus */ - return (s->tc_int & s->tc_mask); - case 3: /* IntErrorStatus */ - return (s->err_int & s->err_mask); - case 5: /* RawIntTCStatus */ - return s->tc_int; - case 6: /* RawIntErrorStatus */ - return s->err_int; - case 7: /* EnbldChns */ - mask = 0; - for (i = 0; i < s->nchannels; i++) { - if (s->chan[i].conf & PL080_CCONF_E) - mask |= 1 << i; - } - return mask; - case 8: /* SoftBReq */ - case 9: /* SoftSReq */ - case 10: /* SoftLBReq */ - case 11: /* SoftLSReq */ - /* ??? Implement these. */ - return 0; - case 12: /* Configuration */ - return s->conf; - case 13: /* Sync */ - return s->sync; - default: - bad_offset: - qemu_log_mask(LOG_GUEST_ERROR, - "pl080_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void pl080_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL080State *s = (PL080State *)opaque; - int i; - - if (offset >= 0x100 && offset < 0x200) { - i = (offset & 0xe0) >> 5; - if (i >= s->nchannels) - goto bad_offset; - switch (offset >> 2) { - case 0: /* SrcAddr */ - s->chan[i].src = value; - break; - case 1: /* DestAddr */ - s->chan[i].dest = value; - break; - case 2: /* LLI */ - s->chan[i].lli = value; - break; - case 3: /* Control */ - s->chan[i].ctrl = value; - break; - case 4: /* Configuration */ - s->chan[i].conf = value; - pl080_run(s); - break; - } - } - switch (offset >> 2) { - case 2: /* IntTCClear */ - s->tc_int &= ~value; - break; - case 4: /* IntErrorClear */ - s->err_int &= ~value; - break; - case 8: /* SoftBReq */ - case 9: /* SoftSReq */ - case 10: /* SoftLBReq */ - case 11: /* SoftLSReq */ - /* ??? Implement these. */ - qemu_log_mask(LOG_UNIMP, "pl080_write: Soft DMA not implemented\n"); - break; - case 12: /* Configuration */ - s->conf = value; - if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) { - qemu_log_mask(LOG_UNIMP, - "pl080_write: Big-endian DMA not implemented\n"); - } - pl080_run(s); - break; - case 13: /* Sync */ - s->sync = value; - break; - default: - bad_offset: - qemu_log_mask(LOG_GUEST_ERROR, - "pl080_write: Bad offset %x\n", (int)offset); - } - pl080_update(s); -} - -static const MemoryRegionOps pl080_ops = { - .read = pl080_read, - .write = pl080_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pl080_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - PL080State *s = PL080(obj); - - memory_region_init_io(&s->iomem, OBJECT(s), &pl080_ops, s, "pl080", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - s->nchannels = 8; -} - -static void pl081_init(Object *obj) -{ - PL080State *s = PL080(obj); - - s->nchannels = 2; -} - -static void pl080_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->vmsd = &vmstate_pl080; -} - -static const TypeInfo pl080_info = { - .name = TYPE_PL080, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL080State), - .instance_init = pl080_init, - .class_init = pl080_class_init, -}; - -static const TypeInfo pl081_info = { - .name = "pl081", - .parent = TYPE_PL080, - .instance_init = pl081_init, -}; - -/* The PL080 and PL081 are the same except for the number of channels - they implement (8 and 2 respectively). */ -static void pl080_register_types(void) -{ - type_register_static(&pl080_info); - type_register_static(&pl081_info); -} - -type_init(pl080_register_types) diff --git a/qemu/hw/dma/pl330.c b/qemu/hw/dma/pl330.c deleted file mode 100644 index ea89ecb00..000000000 --- a/qemu/hw/dma/pl330.c +++ /dev/null @@ -1,1668 +0,0 @@ -/* - * ARM PrimeCell PL330 DMA Controller - * - * Copyright (c) 2009 Samsung Electronics. - * Contributed by Kirill Batuzov <batuzovk@ispras.ru> - * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) - * Copyright (c) 2012 PetaLogix Pty Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 or later. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "sysemu/dma.h" - -#ifndef PL330_ERR_DEBUG -#define PL330_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(lvl, fmt, args...) do {\ - if (PL330_ERR_DEBUG >= lvl) {\ - fprintf(stderr, "PL330: %s:" fmt, __func__, ## args);\ - } \ -} while (0); - -#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) - -#define PL330_PERIPH_NUM 32 -#define PL330_MAX_BURST_LEN 128 -#define PL330_INSN_MAXSIZE 6 - -#define PL330_FIFO_OK 0 -#define PL330_FIFO_STALL 1 -#define PL330_FIFO_ERR (-1) - -#define PL330_FAULT_UNDEF_INSTR (1 << 0) -#define PL330_FAULT_OPERAND_INVALID (1 << 1) -#define PL330_FAULT_DMAGO_ERR (1 << 4) -#define PL330_FAULT_EVENT_ERR (1 << 5) -#define PL330_FAULT_CH_PERIPH_ERR (1 << 6) -#define PL330_FAULT_CH_RDWR_ERR (1 << 7) -#define PL330_FAULT_ST_DATA_UNAVAILABLE (1 << 12) -#define PL330_FAULT_FIFOEMPTY_ERR (1 << 13) -#define PL330_FAULT_INSTR_FETCH_ERR (1 << 16) -#define PL330_FAULT_DATA_WRITE_ERR (1 << 17) -#define PL330_FAULT_DATA_READ_ERR (1 << 18) -#define PL330_FAULT_DBG_INSTR (1 << 30) -#define PL330_FAULT_LOCKUP_ERR (1 << 31) - -#define PL330_UNTAGGED 0xff - -#define PL330_SINGLE 0x0 -#define PL330_BURST 0x1 - -#define PL330_WATCHDOG_LIMIT 1024 - -/* IOMEM mapped registers */ -#define PL330_REG_DSR 0x000 -#define PL330_REG_DPC 0x004 -#define PL330_REG_INTEN 0x020 -#define PL330_REG_INT_EVENT_RIS 0x024 -#define PL330_REG_INTMIS 0x028 -#define PL330_REG_INTCLR 0x02C -#define PL330_REG_FSRD 0x030 -#define PL330_REG_FSRC 0x034 -#define PL330_REG_FTRD 0x038 -#define PL330_REG_FTR_BASE 0x040 -#define PL330_REG_CSR_BASE 0x100 -#define PL330_REG_CPC_BASE 0x104 -#define PL330_REG_CHANCTRL 0x400 -#define PL330_REG_DBGSTATUS 0xD00 -#define PL330_REG_DBGCMD 0xD04 -#define PL330_REG_DBGINST0 0xD08 -#define PL330_REG_DBGINST1 0xD0C -#define PL330_REG_CR0_BASE 0xE00 -#define PL330_REG_PERIPH_ID 0xFE0 - -#define PL330_IOMEM_SIZE 0x1000 - -#define CFG_BOOT_ADDR 2 -#define CFG_INS 3 -#define CFG_PNS 4 -#define CFG_CRD 5 - -static const uint32_t pl330_id[] = { - 0x30, 0x13, 0x24, 0x00, 0x0D, 0xF0, 0x05, 0xB1 -}; - -/* DMA channel states as they are described in PL330 Technical Reference Manual - * Most of them will not be used in emulation. - */ -typedef enum { - pl330_chan_stopped = 0, - pl330_chan_executing = 1, - pl330_chan_cache_miss = 2, - pl330_chan_updating_pc = 3, - pl330_chan_waiting_event = 4, - pl330_chan_at_barrier = 5, - pl330_chan_queue_busy = 6, - pl330_chan_waiting_periph = 7, - pl330_chan_killing = 8, - pl330_chan_completing = 9, - pl330_chan_fault_completing = 14, - pl330_chan_fault = 15, -} PL330ChanState; - -typedef struct PL330State PL330State; - -typedef struct PL330Chan { - uint32_t src; - uint32_t dst; - uint32_t pc; - uint32_t control; - uint32_t status; - uint32_t lc[2]; - uint32_t fault_type; - uint32_t watchdog_timer; - - bool ns; - uint8_t request_flag; - uint8_t wakeup; - uint8_t wfp_sbp; - - uint8_t state; - uint8_t stall; - - bool is_manager; - PL330State *parent; - uint8_t tag; -} PL330Chan; - -static const VMStateDescription vmstate_pl330_chan = { - .name = "pl330_chan", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(src, PL330Chan), - VMSTATE_UINT32(dst, PL330Chan), - VMSTATE_UINT32(pc, PL330Chan), - VMSTATE_UINT32(control, PL330Chan), - VMSTATE_UINT32(status, PL330Chan), - VMSTATE_UINT32_ARRAY(lc, PL330Chan, 2), - VMSTATE_UINT32(fault_type, PL330Chan), - VMSTATE_UINT32(watchdog_timer, PL330Chan), - VMSTATE_BOOL(ns, PL330Chan), - VMSTATE_UINT8(request_flag, PL330Chan), - VMSTATE_UINT8(wakeup, PL330Chan), - VMSTATE_UINT8(wfp_sbp, PL330Chan), - VMSTATE_UINT8(state, PL330Chan), - VMSTATE_UINT8(stall, PL330Chan), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct PL330Fifo { - uint8_t *buf; - uint8_t *tag; - uint32_t head; - uint32_t num; - uint32_t buf_size; -} PL330Fifo; - -static const VMStateDescription vmstate_pl330_fifo = { - .name = "pl330_chan", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, 0, buf_size), - VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, 0, buf_size), - VMSTATE_UINT32(head, PL330Fifo), - VMSTATE_UINT32(num, PL330Fifo), - VMSTATE_UINT32(buf_size, PL330Fifo), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct PL330QueueEntry { - uint32_t addr; - uint32_t len; - uint8_t n; - bool inc; - bool z; - uint8_t tag; - uint8_t seqn; -} PL330QueueEntry; - -static const VMStateDescription vmstate_pl330_queue_entry = { - .name = "pl330_queue_entry", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(addr, PL330QueueEntry), - VMSTATE_UINT32(len, PL330QueueEntry), - VMSTATE_UINT8(n, PL330QueueEntry), - VMSTATE_BOOL(inc, PL330QueueEntry), - VMSTATE_BOOL(z, PL330QueueEntry), - VMSTATE_UINT8(tag, PL330QueueEntry), - VMSTATE_UINT8(seqn, PL330QueueEntry), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct PL330Queue { - PL330State *parent; - PL330QueueEntry *queue; - uint32_t queue_size; -} PL330Queue; - -static const VMStateDescription vmstate_pl330_queue = { - .name = "pl330_queue", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_VARRAY_UINT32(queue, PL330Queue, queue_size, 1, - vmstate_pl330_queue_entry, PL330QueueEntry), - VMSTATE_END_OF_LIST() - } -}; - -struct PL330State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq_abort; - qemu_irq *irq; - - /* Config registers. cfg[5] = CfgDn. */ - uint32_t cfg[6]; -#define EVENT_SEC_STATE 3 -#define PERIPH_SEC_STATE 4 - /* cfg 0 bits and pieces */ - uint32_t num_chnls; - uint8_t num_periph_req; - uint8_t num_events; - uint8_t mgr_ns_at_rst; - /* cfg 1 bits and pieces */ - uint8_t i_cache_len; - uint8_t num_i_cache_lines; - /* CRD bits and pieces */ - uint8_t data_width; - uint8_t wr_cap; - uint8_t wr_q_dep; - uint8_t rd_cap; - uint8_t rd_q_dep; - uint16_t data_buffer_dep; - - PL330Chan manager; - PL330Chan *chan; - PL330Fifo fifo; - PL330Queue read_queue; - PL330Queue write_queue; - uint8_t *lo_seqn; - uint8_t *hi_seqn; - QEMUTimer *timer; /* is used for restore dma. */ - - uint32_t inten; - uint32_t int_status; - uint32_t ev_status; - uint32_t dbg[2]; - uint8_t debug_status; - uint8_t num_faulting; - uint8_t periph_busy[PL330_PERIPH_NUM]; - -}; - -#define TYPE_PL330 "pl330" -#define PL330(obj) OBJECT_CHECK(PL330State, (obj), TYPE_PL330) - -static const VMStateDescription vmstate_pl330 = { - .name = "pl330", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan), - VMSTATE_STRUCT_VARRAY_UINT32(chan, PL330State, num_chnls, 0, - vmstate_pl330_chan, PL330Chan), - VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, 0, num_chnls), - VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, 0, num_chnls), - VMSTATE_STRUCT(fifo, PL330State, 0, vmstate_pl330_fifo, PL330Fifo), - VMSTATE_STRUCT(read_queue, PL330State, 0, vmstate_pl330_queue, - PL330Queue), - VMSTATE_STRUCT(write_queue, PL330State, 0, vmstate_pl330_queue, - PL330Queue), - VMSTATE_TIMER_PTR(timer, PL330State), - VMSTATE_UINT32(inten, PL330State), - VMSTATE_UINT32(int_status, PL330State), - VMSTATE_UINT32(ev_status, PL330State), - VMSTATE_UINT32_ARRAY(dbg, PL330State, 2), - VMSTATE_UINT8(debug_status, PL330State), - VMSTATE_UINT8(num_faulting, PL330State), - VMSTATE_UINT8_ARRAY(periph_busy, PL330State, PL330_PERIPH_NUM), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct PL330InsnDesc { - /* OPCODE of the instruction */ - uint8_t opcode; - /* Mask so we can select several sibling instructions, such as - DMALD, DMALDS and DMALDB */ - uint8_t opmask; - /* Size of instruction in bytes */ - uint8_t size; - /* Interpreter */ - void (*exec)(PL330Chan *, uint8_t opcode, uint8_t *args, int len); -} PL330InsnDesc; - - -/* MFIFO Implementation - * - * MFIFO is implemented as a cyclic buffer of BUF_SIZE size. Tagged bytes are - * stored in this buffer. Data is stored in BUF field, tags - in the - * corresponding array elements of TAG field. - */ - -/* Initialize queue. */ - -static void pl330_fifo_init(PL330Fifo *s, uint32_t size) -{ - s->buf = g_malloc0(size); - s->tag = g_malloc0(size); - s->buf_size = size; -} - -/* Cyclic increment */ - -static inline int pl330_fifo_inc(PL330Fifo *s, int x) -{ - return (x + 1) % s->buf_size; -} - -/* Number of empty bytes in MFIFO */ - -static inline int pl330_fifo_num_free(PL330Fifo *s) -{ - return s->buf_size - s->num; -} - -/* Push LEN bytes of data stored in BUF to MFIFO and tag it with TAG. - * Zero returned on success, PL330_FIFO_STALL if there is no enough free - * space in MFIFO to store requested amount of data. If push was unsuccessful - * no data is stored to MFIFO. - */ - -static int pl330_fifo_push(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag) -{ - int i; - - if (s->buf_size - s->num < len) { - return PL330_FIFO_STALL; - } - for (i = 0; i < len; i++) { - int push_idx = (s->head + s->num + i) % s->buf_size; - s->buf[push_idx] = buf[i]; - s->tag[push_idx] = tag; - } - s->num += len; - return PL330_FIFO_OK; -} - -/* Get LEN bytes of data from MFIFO and store it to BUF. Tag value of each - * byte is verified. Zero returned on success, PL330_FIFO_ERR on tag mismatch - * and PL330_FIFO_STALL if there is no enough data in MFIFO. If get was - * unsuccessful no data is removed from MFIFO. - */ - -static int pl330_fifo_get(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag) -{ - int i; - - if (s->num < len) { - return PL330_FIFO_STALL; - } - for (i = 0; i < len; i++) { - if (s->tag[s->head] == tag) { - int get_idx = (s->head + i) % s->buf_size; - buf[i] = s->buf[get_idx]; - } else { /* Tag mismatch - Rollback transaction */ - return PL330_FIFO_ERR; - } - } - s->head = (s->head + len) % s->buf_size; - s->num -= len; - return PL330_FIFO_OK; -} - -/* Reset MFIFO. This completely erases all data in it. */ - -static inline void pl330_fifo_reset(PL330Fifo *s) -{ - s->head = 0; - s->num = 0; -} - -/* Return tag of the first byte stored in MFIFO. If MFIFO is empty - * PL330_UNTAGGED is returned. - */ - -static inline uint8_t pl330_fifo_tag(PL330Fifo *s) -{ - return (!s->num) ? PL330_UNTAGGED : s->tag[s->head]; -} - -/* Returns non-zero if tag TAG is present in fifo or zero otherwise */ - -static int pl330_fifo_has_tag(PL330Fifo *s, uint8_t tag) -{ - int i, n; - - i = s->head; - for (n = 0; n < s->num; n++) { - if (s->tag[i] == tag) { - return 1; - } - i = pl330_fifo_inc(s, i); - } - return 0; -} - -/* Remove all entry tagged with TAG from MFIFO */ - -static void pl330_fifo_tagged_remove(PL330Fifo *s, uint8_t tag) -{ - int i, t, n; - - t = i = s->head; - for (n = 0; n < s->num; n++) { - if (s->tag[i] != tag) { - s->buf[t] = s->buf[i]; - s->tag[t] = s->tag[i]; - t = pl330_fifo_inc(s, t); - } else { - s->num = s->num - 1; - } - i = pl330_fifo_inc(s, i); - } -} - -/* Read-Write Queue implementation - * - * A Read-Write Queue stores up to QUEUE_SIZE instructions (loads or stores). - * Each instruction is described by source (for loads) or destination (for - * stores) address ADDR, width of data to be loaded/stored LEN, number of - * stores/loads to be performed N, INC bit, Z bit and TAG to identify channel - * this instruction belongs to. Queue does not store any information about - * nature of the instruction: is it load or store. PL330 has different queues - * for loads and stores so this is already known at the top level where it - * matters. - * - * Queue works as FIFO for instructions with equivalent tags, but can issue - * instructions with different tags in arbitrary order. SEQN field attached to - * each instruction helps to achieve this. For each TAG queue contains - * instructions with consecutive SEQN values ranging from LO_SEQN[TAG] to - * HI_SEQN[TAG]-1 inclusive. SEQN is 8-bit unsigned integer, so SEQN=255 is - * followed by SEQN=0. - * - * Z bit indicates that zeroes should be stored. No MFIFO fetches are performed - * in this case. - */ - -static void pl330_queue_reset(PL330Queue *s) -{ - int i; - - for (i = 0; i < s->queue_size; i++) { - s->queue[i].tag = PL330_UNTAGGED; - } -} - -/* Initialize queue */ -static void pl330_queue_init(PL330Queue *s, int size, PL330State *parent) -{ - s->parent = parent; - s->queue = g_new0(PL330QueueEntry, size); - s->queue_size = size; -} - -/* Returns pointer to an empty slot or NULL if queue is full */ -static PL330QueueEntry *pl330_queue_find_empty(PL330Queue *s) -{ - int i; - - for (i = 0; i < s->queue_size; i++) { - if (s->queue[i].tag == PL330_UNTAGGED) { - return &s->queue[i]; - } - } - return NULL; -} - -/* Put instruction in queue. - * Return value: - * - zero - OK - * - non-zero - queue is full - */ - -static int pl330_queue_put_insn(PL330Queue *s, uint32_t addr, - int len, int n, bool inc, bool z, uint8_t tag) -{ - PL330QueueEntry *entry = pl330_queue_find_empty(s); - - if (!entry) { - return 1; - } - entry->tag = tag; - entry->addr = addr; - entry->len = len; - entry->n = n; - entry->z = z; - entry->inc = inc; - entry->seqn = s->parent->hi_seqn[tag]; - s->parent->hi_seqn[tag]++; - return 0; -} - -/* Returns a pointer to queue slot containing instruction which satisfies - * following conditions: - * - it has valid tag value (not PL330_UNTAGGED) - * - if enforce_seq is set it has to be issuable without violating queue - * logic (see above) - * - if TAG argument is not PL330_UNTAGGED this instruction has tag value - * equivalent to the argument TAG value. - * If such instruction cannot be found NULL is returned. - */ - -static PL330QueueEntry *pl330_queue_find_insn(PL330Queue *s, uint8_t tag, - bool enforce_seq) -{ - int i; - - for (i = 0; i < s->queue_size; i++) { - if (s->queue[i].tag != PL330_UNTAGGED) { - if ((!enforce_seq || - s->queue[i].seqn == s->parent->lo_seqn[s->queue[i].tag]) && - (s->queue[i].tag == tag || tag == PL330_UNTAGGED || - s->queue[i].z)) { - return &s->queue[i]; - } - } - } - return NULL; -} - -/* Removes instruction from queue. */ - -static inline void pl330_queue_remove_insn(PL330Queue *s, PL330QueueEntry *e) -{ - s->parent->lo_seqn[e->tag]++; - e->tag = PL330_UNTAGGED; -} - -/* Removes all instructions tagged with TAG from queue. */ - -static inline void pl330_queue_remove_tagged(PL330Queue *s, uint8_t tag) -{ - int i; - - for (i = 0; i < s->queue_size; i++) { - if (s->queue[i].tag == tag) { - s->queue[i].tag = PL330_UNTAGGED; - } - } -} - -/* DMA instruction execution engine */ - -/* Moves DMA channel to the FAULT state and updates it's status. */ - -static inline void pl330_fault(PL330Chan *ch, uint32_t flags) -{ - DB_PRINT("ch: %p, flags: %" PRIx32 "\n", ch, flags); - ch->fault_type |= flags; - if (ch->state == pl330_chan_fault) { - return; - } - ch->state = pl330_chan_fault; - ch->parent->num_faulting++; - if (ch->parent->num_faulting == 1) { - DB_PRINT("abort interrupt raised\n"); - qemu_irq_raise(ch->parent->irq_abort); - } -} - -/* - * For information about instructions see PL330 Technical Reference Manual. - * - * Arguments: - * CH - channel executing the instruction - * OPCODE - opcode - * ARGS - array of 8-bit arguments - * LEN - number of elements in ARGS array - */ - -static void pl330_dmaadxh(PL330Chan *ch, uint8_t *args, bool ra, bool neg) -{ - uint32_t im = (args[1] << 8) | args[0]; - if (neg) { - im |= 0xffffu << 16; - } - - if (ch->is_manager) { - pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); - return; - } - if (ra) { - ch->dst += im; - } else { - ch->src += im; - } -} - -static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - pl330_dmaadxh(ch, args, extract32(opcode, 1, 1), false); -} - -static void pl330_dmaadnh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - pl330_dmaadxh(ch, args, extract32(opcode, 1, 1), true); -} - -static void pl330_dmaend(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - PL330State *s = ch->parent; - - if (ch->state == pl330_chan_executing && !ch->is_manager) { - /* Wait for all transfers to complete */ - if (pl330_fifo_has_tag(&s->fifo, ch->tag) || - pl330_queue_find_insn(&s->read_queue, ch->tag, false) != NULL || - pl330_queue_find_insn(&s->write_queue, ch->tag, false) != NULL) { - - ch->stall = 1; - return; - } - } - DB_PRINT("DMA ending!\n"); - pl330_fifo_tagged_remove(&s->fifo, ch->tag); - pl330_queue_remove_tagged(&s->read_queue, ch->tag); - pl330_queue_remove_tagged(&s->write_queue, ch->tag); - ch->state = pl330_chan_stopped; -} - -static void pl330_dmaflushp(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t periph_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - periph_id = (args[0] >> 3) & 0x1f; - if (periph_id >= ch->parent->num_periph_req) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { - pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); - return; - } - /* Do nothing */ -} - -static void pl330_dmago(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t chan_id; - uint8_t ns; - uint32_t pc; - PL330Chan *s; - - DB_PRINT("\n"); - - if (!ch->is_manager) { - pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); - return; - } - ns = !!(opcode & 2); - chan_id = args[0] & 7; - if ((args[0] >> 3)) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (chan_id >= ch->parent->num_chnls) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - pc = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) | - (((uint32_t)args[2]) << 8) | (((uint32_t)args[1])); - if (ch->parent->chan[chan_id].state != pl330_chan_stopped) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !ns) { - pl330_fault(ch, PL330_FAULT_DMAGO_ERR); - return; - } - s = &ch->parent->chan[chan_id]; - s->ns = ns; - s->pc = pc; - s->state = pl330_chan_executing; -} - -static void pl330_dmald(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t bs = opcode & 3; - uint32_t size, num; - bool inc; - - if (bs == 2) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if ((bs == 1 && ch->request_flag == PL330_BURST) || - (bs == 3 && ch->request_flag == PL330_SINGLE)) { - /* Perform NOP */ - return; - } - if (bs == 1 && ch->request_flag == PL330_SINGLE) { - num = 1; - } else { - num = ((ch->control >> 4) & 0xf) + 1; - } - size = (uint32_t)1 << ((ch->control >> 1) & 0x7); - inc = !!(ch->control & 1); - ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src, - size, num, inc, 0, ch->tag); - if (!ch->stall) { - DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32 - " num:%" PRId32 " %c\n", - ch->tag, ch->src, size, num, inc ? 'Y' : 'N'); - ch->src += inc ? size * num - (ch->src & (size - 1)) : 0; - } -} - -static void pl330_dmaldp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t periph_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - periph_id = (args[0] >> 3) & 0x1f; - if (periph_id >= ch->parent->num_periph_req) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { - pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); - return; - } - pl330_dmald(ch, opcode, args, len); -} - -static void pl330_dmalp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t lc = (opcode & 2) >> 1; - - ch->lc[lc] = args[0]; -} - -static void pl330_dmakill(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - if (ch->state == pl330_chan_fault || - ch->state == pl330_chan_fault_completing) { - /* This is the only way for a channel to leave the faulting state */ - ch->fault_type = 0; - ch->parent->num_faulting--; - if (ch->parent->num_faulting == 0) { - DB_PRINT("abort interrupt lowered\n"); - qemu_irq_lower(ch->parent->irq_abort); - } - } - ch->state = pl330_chan_killing; - pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag); - pl330_queue_remove_tagged(&ch->parent->read_queue, ch->tag); - pl330_queue_remove_tagged(&ch->parent->write_queue, ch->tag); - ch->state = pl330_chan_stopped; -} - -static void pl330_dmalpend(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t nf = (opcode & 0x10) >> 4; - uint8_t bs = opcode & 3; - uint8_t lc = (opcode & 4) >> 2; - - if (bs == 2) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if ((bs == 1 && ch->request_flag == PL330_BURST) || - (bs == 3 && ch->request_flag == PL330_SINGLE)) { - /* Perform NOP */ - return; - } - if (!nf || ch->lc[lc]) { - if (nf) { - ch->lc[lc]--; - } - DB_PRINT("loop reiteration\n"); - ch->pc -= args[0]; - ch->pc -= len + 1; - /* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */ - } else { - DB_PRINT("loop fallthrough\n"); - } -} - - -static void pl330_dmamov(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t rd = args[0] & 7; - uint32_t im; - - if ((args[0] >> 3)) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - im = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) | - (((uint32_t)args[2]) << 8) | (((uint32_t)args[1])); - switch (rd) { - case 0: - ch->src = im; - break; - case 1: - ch->control = im; - break; - case 2: - ch->dst = im; - break; - default: - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } -} - -static void pl330_dmanop(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - /* NOP is NOP. */ -} - -static void pl330_dmarmb(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - if (pl330_queue_find_insn(&ch->parent->read_queue, ch->tag, false)) { - ch->state = pl330_chan_at_barrier; - ch->stall = 1; - return; - } else { - ch->state = pl330_chan_executing; - } -} - -static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t ev_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - ev_id = (args[0] >> 3) & 0x1f; - if (ev_id >= ch->parent->num_events) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) { - pl330_fault(ch, PL330_FAULT_EVENT_ERR); - return; - } - if (ch->parent->inten & (1 << ev_id)) { - ch->parent->int_status |= (1 << ev_id); - DB_PRINT("event interrupt raised %" PRId8 "\n", ev_id); - qemu_irq_raise(ch->parent->irq[ev_id]); - } - DB_PRINT("event raised %" PRId8 "\n", ev_id); - ch->parent->ev_status |= (1 << ev_id); -} - -static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t bs = opcode & 3; - uint32_t size, num; - bool inc; - - if (bs == 2) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if ((bs == 1 && ch->request_flag == PL330_BURST) || - (bs == 3 && ch->request_flag == PL330_SINGLE)) { - /* Perform NOP */ - return; - } - num = ((ch->control >> 18) & 0xf) + 1; - size = (uint32_t)1 << ((ch->control >> 15) & 0x7); - inc = !!((ch->control >> 14) & 1); - ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, - size, num, inc, 0, ch->tag); - if (!ch->stall) { - DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32 - " num:%" PRId32 " %c\n", - ch->tag, ch->dst, size, num, inc ? 'Y' : 'N'); - ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0; - } -} - -static void pl330_dmastp(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t periph_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - periph_id = (args[0] >> 3) & 0x1f; - if (periph_id >= ch->parent->num_periph_req) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { - pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); - return; - } - pl330_dmast(ch, opcode, args, len); -} - -static void pl330_dmastz(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint32_t size, num; - bool inc; - - num = ((ch->control >> 18) & 0xf) + 1; - size = (uint32_t)1 << ((ch->control >> 15) & 0x7); - inc = !!((ch->control >> 14) & 1); - ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, - size, num, inc, 1, ch->tag); - if (inc) { - ch->dst += size * num; - } -} - -static void pl330_dmawfe(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t ev_id; - int i; - - if (args[0] & 5) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - ev_id = (args[0] >> 3) & 0x1f; - if (ev_id >= ch->parent->num_events) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) { - pl330_fault(ch, PL330_FAULT_EVENT_ERR); - return; - } - ch->wakeup = ev_id; - ch->state = pl330_chan_waiting_event; - if (~ch->parent->inten & ch->parent->ev_status & 1 << ev_id) { - ch->state = pl330_chan_executing; - /* If anyone else is currently waiting on the same event, let them - * clear the ev_status so they pick up event as well - */ - for (i = 0; i < ch->parent->num_chnls; ++i) { - PL330Chan *peer = &ch->parent->chan[i]; - if (peer->state == pl330_chan_waiting_event && - peer->wakeup == ev_id) { - return; - } - } - ch->parent->ev_status &= ~(1 << ev_id); - DB_PRINT("event lowered %" PRIx8 "\n", ev_id); - } else { - ch->stall = 1; - } -} - -static void pl330_dmawfp(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t bs = opcode & 3; - uint8_t periph_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - periph_id = (args[0] >> 3) & 0x1f; - if (periph_id >= ch->parent->num_periph_req) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { - pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); - return; - } - switch (bs) { - case 0: /* S */ - ch->request_flag = PL330_SINGLE; - ch->wfp_sbp = 0; - break; - case 1: /* P */ - ch->request_flag = PL330_BURST; - ch->wfp_sbp = 2; - break; - case 2: /* B */ - ch->request_flag = PL330_BURST; - ch->wfp_sbp = 1; - break; - default: - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - - if (ch->parent->periph_busy[periph_id]) { - ch->state = pl330_chan_waiting_periph; - ch->stall = 1; - } else if (ch->state == pl330_chan_waiting_periph) { - ch->state = pl330_chan_executing; - } -} - -static void pl330_dmawmb(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - if (pl330_queue_find_insn(&ch->parent->write_queue, ch->tag, false)) { - ch->state = pl330_chan_at_barrier; - ch->stall = 1; - return; - } else { - ch->state = pl330_chan_executing; - } -} - -/* NULL terminated array of the instruction descriptions. */ -static const PL330InsnDesc insn_desc[] = { - { .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh, }, - { .opcode = 0x5c, .opmask = 0xFD, .size = 3, .exec = pl330_dmaadnh, }, - { .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend, }, - { .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp, }, - { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, }, - { .opcode = 0x04, .opmask = 0xFC, .size = 1, .exec = pl330_dmald, }, - { .opcode = 0x25, .opmask = 0xFD, .size = 2, .exec = pl330_dmaldp, }, - { .opcode = 0x20, .opmask = 0xFD, .size = 2, .exec = pl330_dmalp, }, - /* dmastp must be before dmalpend in this list, because their maps - * are overlapping - */ - { .opcode = 0x29, .opmask = 0xFD, .size = 2, .exec = pl330_dmastp, }, - { .opcode = 0x28, .opmask = 0xE8, .size = 2, .exec = pl330_dmalpend, }, - { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, }, - { .opcode = 0xBC, .opmask = 0xFF, .size = 6, .exec = pl330_dmamov, }, - { .opcode = 0x18, .opmask = 0xFF, .size = 1, .exec = pl330_dmanop, }, - { .opcode = 0x12, .opmask = 0xFF, .size = 1, .exec = pl330_dmarmb, }, - { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, }, - { .opcode = 0x08, .opmask = 0xFC, .size = 1, .exec = pl330_dmast, }, - { .opcode = 0x0C, .opmask = 0xFF, .size = 1, .exec = pl330_dmastz, }, - { .opcode = 0x36, .opmask = 0xFF, .size = 2, .exec = pl330_dmawfe, }, - { .opcode = 0x30, .opmask = 0xFC, .size = 2, .exec = pl330_dmawfp, }, - { .opcode = 0x13, .opmask = 0xFF, .size = 1, .exec = pl330_dmawmb, }, - { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, } -}; - -/* Instructions which can be issued via debug registers. */ -static const PL330InsnDesc debug_insn_desc[] = { - { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, }, - { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, }, - { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, }, - { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, } -}; - -static inline const PL330InsnDesc *pl330_fetch_insn(PL330Chan *ch) -{ - uint8_t opcode; - int i; - - dma_memory_read(&address_space_memory, ch->pc, &opcode, 1); - for (i = 0; insn_desc[i].size; i++) { - if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode) { - return &insn_desc[i]; - } - } - return NULL; -} - -static inline void pl330_exec_insn(PL330Chan *ch, const PL330InsnDesc *insn) -{ - uint8_t buf[PL330_INSN_MAXSIZE]; - - assert(insn->size <= PL330_INSN_MAXSIZE); - dma_memory_read(&address_space_memory, ch->pc, buf, insn->size); - insn->exec(ch, buf[0], &buf[1], insn->size - 1); -} - -static inline void pl330_update_pc(PL330Chan *ch, - const PL330InsnDesc *insn) -{ - ch->pc += insn->size; -} - -/* Try to execute current instruction in channel CH. Number of executed - instructions is returned (0 or 1). */ -static int pl330_chan_exec(PL330Chan *ch) -{ - const PL330InsnDesc *insn; - - if (ch->state != pl330_chan_executing && - ch->state != pl330_chan_waiting_periph && - ch->state != pl330_chan_at_barrier && - ch->state != pl330_chan_waiting_event) { - return 0; - } - ch->stall = 0; - insn = pl330_fetch_insn(ch); - if (!insn) { - DB_PRINT("pl330 undefined instruction\n"); - pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); - return 0; - } - pl330_exec_insn(ch, insn); - if (!ch->stall) { - pl330_update_pc(ch, insn); - ch->watchdog_timer = 0; - return 1; - /* WDT only active in exec state */ - } else if (ch->state == pl330_chan_executing) { - ch->watchdog_timer++; - if (ch->watchdog_timer >= PL330_WATCHDOG_LIMIT) { - pl330_fault(ch, PL330_FAULT_LOCKUP_ERR); - } - } - return 0; -} - -/* Try to execute 1 instruction in each channel, one instruction from read - queue and one instruction from write queue. Number of successfully executed - instructions is returned. */ -static int pl330_exec_cycle(PL330Chan *channel) -{ - PL330State *s = channel->parent; - PL330QueueEntry *q; - int i; - int num_exec = 0; - int fifo_res = 0; - uint8_t buf[PL330_MAX_BURST_LEN]; - - /* Execute one instruction in each channel */ - num_exec += pl330_chan_exec(channel); - - /* Execute one instruction from read queue */ - q = pl330_queue_find_insn(&s->read_queue, PL330_UNTAGGED, true); - if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) { - int len = q->len - (q->addr & (q->len - 1)); - - dma_memory_read(&address_space_memory, q->addr, buf, len); - if (PL330_ERR_DEBUG > 1) { - DB_PRINT("PL330 read from memory @%08" PRIx32 " (size = %08x):\n", - q->addr, len); - qemu_hexdump((char *)buf, stderr, "", len); - } - fifo_res = pl330_fifo_push(&s->fifo, buf, len, q->tag); - if (fifo_res == PL330_FIFO_OK) { - if (q->inc) { - q->addr += len; - } - q->n--; - if (!q->n) { - pl330_queue_remove_insn(&s->read_queue, q); - } - num_exec++; - } - } - - /* Execute one instruction from write queue. */ - q = pl330_queue_find_insn(&s->write_queue, pl330_fifo_tag(&s->fifo), true); - if (q != NULL) { - int len = q->len - (q->addr & (q->len - 1)); - - if (q->z) { - for (i = 0; i < len; i++) { - buf[i] = 0; - } - } else { - fifo_res = pl330_fifo_get(&s->fifo, buf, len, q->tag); - } - if (fifo_res == PL330_FIFO_OK || q->z) { - dma_memory_write(&address_space_memory, q->addr, buf, len); - if (PL330_ERR_DEBUG > 1) { - DB_PRINT("PL330 read from memory @%08" PRIx32 - " (size = %08x):\n", q->addr, len); - qemu_hexdump((char *)buf, stderr, "", len); - } - if (q->inc) { - q->addr += len; - } - num_exec++; - } else if (fifo_res == PL330_FIFO_STALL) { - pl330_fault(&channel->parent->chan[q->tag], - PL330_FAULT_FIFOEMPTY_ERR); - } - q->n--; - if (!q->n) { - pl330_queue_remove_insn(&s->write_queue, q); - } - } - - return num_exec; -} - -static int pl330_exec_channel(PL330Chan *channel) -{ - int insr_exec = 0; - - /* TODO: Is it all right to execute everything or should we do per-cycle - simulation? */ - while (pl330_exec_cycle(channel)) { - insr_exec++; - } - - /* Detect deadlock */ - if (channel->state == pl330_chan_executing) { - pl330_fault(channel, PL330_FAULT_LOCKUP_ERR); - } - /* Situation when one of the queues has deadlocked but all channels - * have finished their programs should be impossible. - */ - - return insr_exec; -} - -static inline void pl330_exec(PL330State *s) -{ - DB_PRINT("\n"); - int i, insr_exec; - do { - insr_exec = pl330_exec_channel(&s->manager); - - for (i = 0; i < s->num_chnls; i++) { - insr_exec += pl330_exec_channel(&s->chan[i]); - } - } while (insr_exec); -} - -static void pl330_exec_cycle_timer(void *opaque) -{ - PL330State *s = (PL330State *)opaque; - pl330_exec(s); -} - -/* Stop or restore dma operations */ - -static void pl330_dma_stop_irq(void *opaque, int irq, int level) -{ - PL330State *s = (PL330State *)opaque; - - if (s->periph_busy[irq] != level) { - s->periph_busy[irq] = level; - timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - } -} - -static void pl330_debug_exec(PL330State *s) -{ - uint8_t args[5]; - uint8_t opcode; - uint8_t chan_id; - int i; - PL330Chan *ch; - const PL330InsnDesc *insn; - - s->debug_status = 1; - chan_id = (s->dbg[0] >> 8) & 0x07; - opcode = (s->dbg[0] >> 16) & 0xff; - args[0] = (s->dbg[0] >> 24) & 0xff; - args[1] = (s->dbg[1] >> 0) & 0xff; - args[2] = (s->dbg[1] >> 8) & 0xff; - args[3] = (s->dbg[1] >> 16) & 0xff; - args[4] = (s->dbg[1] >> 24) & 0xff; - DB_PRINT("chan id: %" PRIx8 "\n", chan_id); - if (s->dbg[0] & 1) { - ch = &s->chan[chan_id]; - } else { - ch = &s->manager; - } - insn = NULL; - for (i = 0; debug_insn_desc[i].size; i++) { - if ((opcode & debug_insn_desc[i].opmask) == debug_insn_desc[i].opcode) { - insn = &debug_insn_desc[i]; - } - } - if (!insn) { - pl330_fault(ch, PL330_FAULT_UNDEF_INSTR | PL330_FAULT_DBG_INSTR); - return ; - } - ch->stall = 0; - insn->exec(ch, opcode, args, insn->size - 1); - if (ch->fault_type) { - ch->fault_type |= PL330_FAULT_DBG_INSTR; - } - if (ch->stall) { - qemu_log_mask(LOG_UNIMP, "pl330: stall of debug instruction not " - "implemented\n"); - } - s->debug_status = 0; -} - -/* IOMEM mapped registers */ - -static void pl330_iomem_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL330State *s = (PL330State *) opaque; - int i; - - DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value); - - switch (offset) { - case PL330_REG_INTEN: - s->inten = value; - break; - case PL330_REG_INTCLR: - for (i = 0; i < s->num_events; i++) { - if (s->int_status & s->inten & value & (1 << i)) { - DB_PRINT("event interrupt lowered %d\n", i); - qemu_irq_lower(s->irq[i]); - } - } - s->ev_status &= ~(value & s->inten); - s->int_status &= ~(value & s->inten); - break; - case PL330_REG_DBGCMD: - if ((value & 3) == 0) { - pl330_debug_exec(s); - pl330_exec(s); - } else { - qemu_log_mask(LOG_GUEST_ERROR, "pl330: write of illegal value %u " - "for offset " TARGET_FMT_plx "\n", (unsigned)value, - offset); - } - break; - case PL330_REG_DBGINST0: - DB_PRINT("s->dbg[0] = %08x\n", (unsigned)value); - s->dbg[0] = value; - break; - case PL330_REG_DBGINST1: - DB_PRINT("s->dbg[1] = %08x\n", (unsigned)value); - s->dbg[1] = value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad write offset " TARGET_FMT_plx - "\n", offset); - break; - } -} - -static inline uint32_t pl330_iomem_read_imp(void *opaque, - hwaddr offset) -{ - PL330State *s = (PL330State *)opaque; - int chan_id; - int i; - uint32_t res; - - if (offset >= PL330_REG_PERIPH_ID && offset < PL330_REG_PERIPH_ID + 32) { - return pl330_id[(offset - PL330_REG_PERIPH_ID) >> 2]; - } - if (offset >= PL330_REG_CR0_BASE && offset < PL330_REG_CR0_BASE + 24) { - return s->cfg[(offset - PL330_REG_CR0_BASE) >> 2]; - } - if (offset >= PL330_REG_CHANCTRL && offset < PL330_REG_DBGSTATUS) { - offset -= PL330_REG_CHANCTRL; - chan_id = offset >> 5; - if (chan_id >= s->num_chnls) { - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - return 0; - } - switch (offset & 0x1f) { - case 0x00: - return s->chan[chan_id].src; - case 0x04: - return s->chan[chan_id].dst; - case 0x08: - return s->chan[chan_id].control; - case 0x0C: - return s->chan[chan_id].lc[0]; - case 0x10: - return s->chan[chan_id].lc[1]; - default: - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - return 0; - } - } - if (offset >= PL330_REG_CSR_BASE && offset < 0x400) { - offset -= PL330_REG_CSR_BASE; - chan_id = offset >> 3; - if (chan_id >= s->num_chnls) { - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - return 0; - } - switch ((offset >> 2) & 1) { - case 0x0: - res = (s->chan[chan_id].ns << 21) | - (s->chan[chan_id].wakeup << 4) | - (s->chan[chan_id].state) | - (s->chan[chan_id].wfp_sbp << 14); - return res; - case 0x1: - return s->chan[chan_id].pc; - default: - qemu_log_mask(LOG_GUEST_ERROR, "pl330: read error\n"); - return 0; - } - } - if (offset >= PL330_REG_FTR_BASE && offset < 0x100) { - offset -= PL330_REG_FTR_BASE; - chan_id = offset >> 2; - if (chan_id >= s->num_chnls) { - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - return 0; - } - return s->chan[chan_id].fault_type; - } - switch (offset) { - case PL330_REG_DSR: - return (s->manager.ns << 9) | (s->manager.wakeup << 4) | - (s->manager.state & 0xf); - case PL330_REG_DPC: - return s->manager.pc; - case PL330_REG_INTEN: - return s->inten; - case PL330_REG_INT_EVENT_RIS: - return s->ev_status; - case PL330_REG_INTMIS: - return s->int_status; - case PL330_REG_INTCLR: - /* Documentation says that we can't read this register - * but linux kernel does it - */ - return 0; - case PL330_REG_FSRD: - return s->manager.state ? 1 : 0; - case PL330_REG_FSRC: - res = 0; - for (i = 0; i < s->num_chnls; i++) { - if (s->chan[i].state == pl330_chan_fault || - s->chan[i].state == pl330_chan_fault_completing) { - res |= 1 << i; - } - } - return res; - case PL330_REG_FTRD: - return s->manager.fault_type; - case PL330_REG_DBGSTATUS: - return s->debug_status; - default: - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - } - return 0; -} - -static uint64_t pl330_iomem_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint32_t ret = pl330_iomem_read_imp(opaque, offset); - DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx32 "\n", offset, ret); - return ret; -} - -static const MemoryRegionOps pl330_ops = { - .read = pl330_iomem_read, - .write = pl330_iomem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - } -}; - -/* Controller logic and initialization */ - -static void pl330_chan_reset(PL330Chan *ch) -{ - ch->src = 0; - ch->dst = 0; - ch->pc = 0; - ch->state = pl330_chan_stopped; - ch->watchdog_timer = 0; - ch->stall = 0; - ch->control = 0; - ch->status = 0; - ch->fault_type = 0; -} - -static void pl330_reset(DeviceState *d) -{ - int i; - PL330State *s = PL330(d); - - s->inten = 0; - s->int_status = 0; - s->ev_status = 0; - s->debug_status = 0; - s->num_faulting = 0; - s->manager.ns = s->mgr_ns_at_rst; - pl330_fifo_reset(&s->fifo); - pl330_queue_reset(&s->read_queue); - pl330_queue_reset(&s->write_queue); - - for (i = 0; i < s->num_chnls; i++) { - pl330_chan_reset(&s->chan[i]); - } - for (i = 0; i < s->num_periph_req; i++) { - s->periph_busy[i] = 0; - } - - timer_del(s->timer); -} - -static void pl330_realize(DeviceState *dev, Error **errp) -{ - int i; - PL330State *s = PL330(dev); - - sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq_abort); - memory_region_init_io(&s->iomem, OBJECT(s), &pl330_ops, s, - "dma", PL330_IOMEM_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); - - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pl330_exec_cycle_timer, s); - - s->cfg[0] = (s->mgr_ns_at_rst ? 0x4 : 0) | - (s->num_periph_req > 0 ? 1 : 0) | - ((s->num_chnls - 1) & 0x7) << 4 | - ((s->num_periph_req - 1) & 0x1f) << 12 | - ((s->num_events - 1) & 0x1f) << 17; - - switch (s->i_cache_len) { - case (4): - s->cfg[1] |= 2; - break; - case (8): - s->cfg[1] |= 3; - break; - case (16): - s->cfg[1] |= 4; - break; - case (32): - s->cfg[1] |= 5; - break; - default: - error_setg(errp, "Bad value for i-cache_len property: %" PRIx8, - s->i_cache_len); - return; - } - s->cfg[1] |= ((s->num_i_cache_lines - 1) & 0xf) << 4; - - s->chan = g_new0(PL330Chan, s->num_chnls); - s->hi_seqn = g_new0(uint8_t, s->num_chnls); - s->lo_seqn = g_new0(uint8_t, s->num_chnls); - for (i = 0; i < s->num_chnls; i++) { - s->chan[i].parent = s; - s->chan[i].tag = (uint8_t)i; - } - s->manager.parent = s; - s->manager.tag = s->num_chnls; - s->manager.is_manager = true; - - s->irq = g_new0(qemu_irq, s->num_events); - for (i = 0; i < s->num_events; i++) { - sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); - } - - qdev_init_gpio_in(dev, pl330_dma_stop_irq, PL330_PERIPH_NUM); - - switch (s->data_width) { - case (32): - s->cfg[CFG_CRD] |= 0x2; - break; - case (64): - s->cfg[CFG_CRD] |= 0x3; - break; - case (128): - s->cfg[CFG_CRD] |= 0x4; - break; - default: - error_setg(errp, "Bad value for data_width property: %" PRIx8, - s->data_width); - return; - } - - s->cfg[CFG_CRD] |= ((s->wr_cap - 1) & 0x7) << 4 | - ((s->wr_q_dep - 1) & 0xf) << 8 | - ((s->rd_cap - 1) & 0x7) << 12 | - ((s->rd_q_dep - 1) & 0xf) << 16 | - ((s->data_buffer_dep - 1) & 0x1ff) << 20; - - pl330_queue_init(&s->read_queue, s->rd_q_dep, s); - pl330_queue_init(&s->write_queue, s->wr_q_dep, s); - pl330_fifo_init(&s->fifo, s->data_width / 4 * s->data_buffer_dep); -} - -static Property pl330_properties[] = { - /* CR0 */ - DEFINE_PROP_UINT32("num_chnls", PL330State, num_chnls, 8), - DEFINE_PROP_UINT8("num_periph_req", PL330State, num_periph_req, 4), - DEFINE_PROP_UINT8("num_events", PL330State, num_events, 16), - DEFINE_PROP_UINT8("mgr_ns_at_rst", PL330State, mgr_ns_at_rst, 0), - /* CR1 */ - DEFINE_PROP_UINT8("i-cache_len", PL330State, i_cache_len, 4), - DEFINE_PROP_UINT8("num_i-cache_lines", PL330State, num_i_cache_lines, 8), - /* CR2-4 */ - DEFINE_PROP_UINT32("boot_addr", PL330State, cfg[CFG_BOOT_ADDR], 0), - DEFINE_PROP_UINT32("INS", PL330State, cfg[CFG_INS], 0), - DEFINE_PROP_UINT32("PNS", PL330State, cfg[CFG_PNS], 0), - /* CRD */ - DEFINE_PROP_UINT8("data_width", PL330State, data_width, 64), - DEFINE_PROP_UINT8("wr_cap", PL330State, wr_cap, 8), - DEFINE_PROP_UINT8("wr_q_dep", PL330State, wr_q_dep, 16), - DEFINE_PROP_UINT8("rd_cap", PL330State, rd_cap, 8), - DEFINE_PROP_UINT8("rd_q_dep", PL330State, rd_q_dep, 16), - DEFINE_PROP_UINT16("data_buffer_dep", PL330State, data_buffer_dep, 256), - - DEFINE_PROP_END_OF_LIST(), -}; - -static void pl330_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pl330_realize; - dc->reset = pl330_reset; - dc->props = pl330_properties; - dc->vmsd = &vmstate_pl330; -} - -static const TypeInfo pl330_type_info = { - .name = TYPE_PL330, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL330State), - .class_init = pl330_class_init, -}; - -static void pl330_register_types(void) -{ - type_register_static(&pl330_type_info); -} - -type_init(pl330_register_types) diff --git a/qemu/hw/dma/puv3_dma.c b/qemu/hw/dma/puv3_dma.c deleted file mode 100644 index b97a6c176..000000000 --- a/qemu/hw/dma/puv3_dma.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * DMA device simulation in PKUnity SoC - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, or any later version. - * See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" - -#undef DEBUG_PUV3 -#include "hw/unicore32/puv3.h" - -#define PUV3_DMA_CH_NR (6) -#define PUV3_DMA_CH_MASK (0xff) -#define PUV3_DMA_CH(offset) ((offset) >> 8) - -#define TYPE_PUV3_DMA "puv3_dma" -#define PUV3_DMA(obj) OBJECT_CHECK(PUV3DMAState, (obj), TYPE_PUV3_DMA) - -typedef struct PUV3DMAState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t reg_CFG[PUV3_DMA_CH_NR]; -} PUV3DMAState; - -static uint64_t puv3_dma_read(void *opaque, hwaddr offset, - unsigned size) -{ - PUV3DMAState *s = opaque; - uint32_t ret = 0; - - assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR); - - switch (offset & PUV3_DMA_CH_MASK) { - case 0x10: - ret = s->reg_CFG[PUV3_DMA_CH(offset)]; - break; - default: - DPRINTF("Bad offset 0x%x\n", offset); - } - DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); - - return ret; -} - -static void puv3_dma_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PUV3DMAState *s = opaque; - - assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR); - - switch (offset & PUV3_DMA_CH_MASK) { - case 0x10: - s->reg_CFG[PUV3_DMA_CH(offset)] = value; - break; - default: - DPRINTF("Bad offset 0x%x\n", offset); - } - DPRINTF("offset 0x%x, value 0x%x\n", offset, value); -} - -static const MemoryRegionOps puv3_dma_ops = { - .read = puv3_dma_read, - .write = puv3_dma_write, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int puv3_dma_init(SysBusDevice *dev) -{ - PUV3DMAState *s = PUV3_DMA(dev); - int i; - - for (i = 0; i < PUV3_DMA_CH_NR; i++) { - s->reg_CFG[i] = 0x0; - } - - memory_region_init_io(&s->iomem, OBJECT(s), &puv3_dma_ops, s, "puv3_dma", - PUV3_REGS_OFFSET); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void puv3_dma_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = puv3_dma_init; -} - -static const TypeInfo puv3_dma_info = { - .name = TYPE_PUV3_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PUV3DMAState), - .class_init = puv3_dma_class_init, -}; - -static void puv3_dma_register_type(void) -{ - type_register_static(&puv3_dma_info); -} - -type_init(puv3_dma_register_type) diff --git a/qemu/hw/dma/pxa2xx_dma.c b/qemu/hw/dma/pxa2xx_dma.c deleted file mode 100644 index 2306abc35..000000000 --- a/qemu/hw/dma/pxa2xx_dma.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Intel XScale PXA255/270 DMA controller. - * - * Copyright (c) 2006 Openedhand Ltd. - * Copyright (c) 2006 Thorsten Zitterell - * Written by Andrzej Zaborowski <balrog@zabor.org> - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/arm/pxa.h" -#include "hw/sysbus.h" - -#define PXA255_DMA_NUM_CHANNELS 16 -#define PXA27X_DMA_NUM_CHANNELS 32 - -#define PXA2XX_DMA_NUM_REQUESTS 75 - -typedef struct { - uint32_t descr; - uint32_t src; - uint32_t dest; - uint32_t cmd; - uint32_t state; - int request; -} PXA2xxDMAChannel; - -#define TYPE_PXA2XX_DMA "pxa2xx-dma" -#define PXA2XX_DMA(obj) OBJECT_CHECK(PXA2xxDMAState, (obj), TYPE_PXA2XX_DMA) - -typedef struct PXA2xxDMAState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - - uint32_t stopintr; - uint32_t eorintr; - uint32_t rasintr; - uint32_t startintr; - uint32_t endintr; - - uint32_t align; - uint32_t pio; - - int channels; - PXA2xxDMAChannel *chan; - - uint8_t req[PXA2XX_DMA_NUM_REQUESTS]; - - /* Flag to avoid recursive DMA invocations. */ - int running; -} PXA2xxDMAState; - -#define DCSR0 0x0000 /* DMA Control / Status register for Channel 0 */ -#define DCSR31 0x007c /* DMA Control / Status register for Channel 31 */ -#define DALGN 0x00a0 /* DMA Alignment register */ -#define DPCSR 0x00a4 /* DMA Programmed I/O Control Status register */ -#define DRQSR0 0x00e0 /* DMA DREQ<0> Status register */ -#define DRQSR1 0x00e4 /* DMA DREQ<1> Status register */ -#define DRQSR2 0x00e8 /* DMA DREQ<2> Status register */ -#define DINT 0x00f0 /* DMA Interrupt register */ -#define DRCMR0 0x0100 /* Request to Channel Map register 0 */ -#define DRCMR63 0x01fc /* Request to Channel Map register 63 */ -#define D_CH0 0x0200 /* Channel 0 Descriptor start */ -#define DRCMR64 0x1100 /* Request to Channel Map register 64 */ -#define DRCMR74 0x1128 /* Request to Channel Map register 74 */ - -/* Per-channel register */ -#define DDADR 0x00 -#define DSADR 0x01 -#define DTADR 0x02 -#define DCMD 0x03 - -/* Bit-field masks */ -#define DRCMR_CHLNUM 0x1f -#define DRCMR_MAPVLD (1 << 7) -#define DDADR_STOP (1 << 0) -#define DDADR_BREN (1 << 1) -#define DCMD_LEN 0x1fff -#define DCMD_WIDTH(x) (1 << ((((x) >> 14) & 3) - 1)) -#define DCMD_SIZE(x) (4 << (((x) >> 16) & 3)) -#define DCMD_FLYBYT (1 << 19) -#define DCMD_FLYBYS (1 << 20) -#define DCMD_ENDIRQEN (1 << 21) -#define DCMD_STARTIRQEN (1 << 22) -#define DCMD_CMPEN (1 << 25) -#define DCMD_FLOWTRG (1 << 28) -#define DCMD_FLOWSRC (1 << 29) -#define DCMD_INCTRGADDR (1 << 30) -#define DCMD_INCSRCADDR (1 << 31) -#define DCSR_BUSERRINTR (1 << 0) -#define DCSR_STARTINTR (1 << 1) -#define DCSR_ENDINTR (1 << 2) -#define DCSR_STOPINTR (1 << 3) -#define DCSR_RASINTR (1 << 4) -#define DCSR_REQPEND (1 << 8) -#define DCSR_EORINT (1 << 9) -#define DCSR_CMPST (1 << 10) -#define DCSR_MASKRUN (1 << 22) -#define DCSR_RASIRQEN (1 << 23) -#define DCSR_CLRCMPST (1 << 24) -#define DCSR_SETCMPST (1 << 25) -#define DCSR_EORSTOPEN (1 << 26) -#define DCSR_EORJMPEN (1 << 27) -#define DCSR_EORIRQEN (1 << 28) -#define DCSR_STOPIRQEN (1 << 29) -#define DCSR_NODESCFETCH (1 << 30) -#define DCSR_RUN (1 << 31) - -static inline void pxa2xx_dma_update(PXA2xxDMAState *s, int ch) -{ - if (ch >= 0) { - if ((s->chan[ch].state & DCSR_STOPIRQEN) && - (s->chan[ch].state & DCSR_STOPINTR)) - s->stopintr |= 1 << ch; - else - s->stopintr &= ~(1 << ch); - - if ((s->chan[ch].state & DCSR_EORIRQEN) && - (s->chan[ch].state & DCSR_EORINT)) - s->eorintr |= 1 << ch; - else - s->eorintr &= ~(1 << ch); - - if ((s->chan[ch].state & DCSR_RASIRQEN) && - (s->chan[ch].state & DCSR_RASINTR)) - s->rasintr |= 1 << ch; - else - s->rasintr &= ~(1 << ch); - - if (s->chan[ch].state & DCSR_STARTINTR) - s->startintr |= 1 << ch; - else - s->startintr &= ~(1 << ch); - - if (s->chan[ch].state & DCSR_ENDINTR) - s->endintr |= 1 << ch; - else - s->endintr &= ~(1 << ch); - } - - if (s->stopintr | s->eorintr | s->rasintr | s->startintr | s->endintr) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); -} - -static inline void pxa2xx_dma_descriptor_fetch( - PXA2xxDMAState *s, int ch) -{ - uint32_t desc[4]; - hwaddr daddr = s->chan[ch].descr & ~0xf; - if ((s->chan[ch].descr & DDADR_BREN) && (s->chan[ch].state & DCSR_CMPST)) - daddr += 32; - - cpu_physical_memory_read(daddr, desc, 16); - s->chan[ch].descr = desc[DDADR]; - s->chan[ch].src = desc[DSADR]; - s->chan[ch].dest = desc[DTADR]; - s->chan[ch].cmd = desc[DCMD]; - - if (s->chan[ch].cmd & DCMD_FLOWSRC) - s->chan[ch].src &= ~3; - if (s->chan[ch].cmd & DCMD_FLOWTRG) - s->chan[ch].dest &= ~3; - - if (s->chan[ch].cmd & (DCMD_CMPEN | DCMD_FLYBYS | DCMD_FLYBYT)) - printf("%s: unsupported mode in channel %i\n", __FUNCTION__, ch); - - if (s->chan[ch].cmd & DCMD_STARTIRQEN) - s->chan[ch].state |= DCSR_STARTINTR; -} - -static void pxa2xx_dma_run(PXA2xxDMAState *s) -{ - int c, srcinc, destinc; - uint32_t n, size; - uint32_t width; - uint32_t length; - uint8_t buffer[32]; - PXA2xxDMAChannel *ch; - - if (s->running ++) - return; - - while (s->running) { - s->running = 1; - for (c = 0; c < s->channels; c ++) { - ch = &s->chan[c]; - - while ((ch->state & DCSR_RUN) && !(ch->state & DCSR_STOPINTR)) { - /* Test for pending requests */ - if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && !ch->request) - break; - - length = ch->cmd & DCMD_LEN; - size = DCMD_SIZE(ch->cmd); - width = DCMD_WIDTH(ch->cmd); - - srcinc = (ch->cmd & DCMD_INCSRCADDR) ? width : 0; - destinc = (ch->cmd & DCMD_INCTRGADDR) ? width : 0; - - while (length) { - size = MIN(length, size); - - for (n = 0; n < size; n += width) { - cpu_physical_memory_read(ch->src, buffer + n, width); - ch->src += srcinc; - } - - for (n = 0; n < size; n += width) { - cpu_physical_memory_write(ch->dest, buffer + n, width); - ch->dest += destinc; - } - - length -= size; - - if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && - !ch->request) { - ch->state |= DCSR_EORINT; - if (ch->state & DCSR_EORSTOPEN) - ch->state |= DCSR_STOPINTR; - if ((ch->state & DCSR_EORJMPEN) && - !(ch->state & DCSR_NODESCFETCH)) - pxa2xx_dma_descriptor_fetch(s, c); - break; - } - } - - ch->cmd = (ch->cmd & ~DCMD_LEN) | length; - - /* Is the transfer complete now? */ - if (!length) { - if (ch->cmd & DCMD_ENDIRQEN) - ch->state |= DCSR_ENDINTR; - - if ((ch->state & DCSR_NODESCFETCH) || - (ch->descr & DDADR_STOP) || - (ch->state & DCSR_EORSTOPEN)) { - ch->state |= DCSR_STOPINTR; - ch->state &= ~DCSR_RUN; - - break; - } - - ch->state |= DCSR_STOPINTR; - break; - } - } - } - - s->running --; - } -} - -static uint64_t pxa2xx_dma_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxDMAState *s = (PXA2xxDMAState *) opaque; - unsigned int channel; - - if (size != 4) { - hw_error("%s: Bad access width\n", __FUNCTION__); - return 5; - } - - switch (offset) { - case DRCMR64 ... DRCMR74: - offset -= DRCMR64 - DRCMR0 - (64 << 2); - /* Fall through */ - case DRCMR0 ... DRCMR63: - channel = (offset - DRCMR0) >> 2; - return s->req[channel]; - - case DRQSR0: - case DRQSR1: - case DRQSR2: - return 0; - - case DCSR0 ... DCSR31: - channel = offset >> 2; - if (s->chan[channel].request) - return s->chan[channel].state | DCSR_REQPEND; - return s->chan[channel].state; - - case DINT: - return s->stopintr | s->eorintr | s->rasintr | - s->startintr | s->endintr; - - case DALGN: - return s->align; - - case DPCSR: - return s->pio; - } - - if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) { - channel = (offset - D_CH0) >> 4; - switch ((offset & 0x0f) >> 2) { - case DDADR: - return s->chan[channel].descr; - case DSADR: - return s->chan[channel].src; - case DTADR: - return s->chan[channel].dest; - case DCMD: - return s->chan[channel].cmd; - } - } - - hw_error("%s: Bad offset 0x" TARGET_FMT_plx "\n", __FUNCTION__, offset); - return 7; -} - -static void pxa2xx_dma_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxDMAState *s = (PXA2xxDMAState *) opaque; - unsigned int channel; - - if (size != 4) { - hw_error("%s: Bad access width\n", __FUNCTION__); - return; - } - - switch (offset) { - case DRCMR64 ... DRCMR74: - offset -= DRCMR64 - DRCMR0 - (64 << 2); - /* Fall through */ - case DRCMR0 ... DRCMR63: - channel = (offset - DRCMR0) >> 2; - - if (value & DRCMR_MAPVLD) - if ((value & DRCMR_CHLNUM) > s->channels) - hw_error("%s: Bad DMA channel %i\n", - __FUNCTION__, (unsigned)value & DRCMR_CHLNUM); - - s->req[channel] = value; - break; - - case DRQSR0: - case DRQSR1: - case DRQSR2: - /* Nothing to do */ - break; - - case DCSR0 ... DCSR31: - channel = offset >> 2; - s->chan[channel].state &= 0x0000071f & ~(value & - (DCSR_EORINT | DCSR_ENDINTR | - DCSR_STARTINTR | DCSR_BUSERRINTR)); - s->chan[channel].state |= value & 0xfc800000; - - if (s->chan[channel].state & DCSR_STOPIRQEN) - s->chan[channel].state &= ~DCSR_STOPINTR; - - if (value & DCSR_NODESCFETCH) { - /* No-descriptor-fetch mode */ - if (value & DCSR_RUN) { - s->chan[channel].state &= ~DCSR_STOPINTR; - pxa2xx_dma_run(s); - } - } else { - /* Descriptor-fetch mode */ - if (value & DCSR_RUN) { - s->chan[channel].state &= ~DCSR_STOPINTR; - pxa2xx_dma_descriptor_fetch(s, channel); - pxa2xx_dma_run(s); - } - } - - /* Shouldn't matter as our DMA is synchronous. */ - if (!(value & (DCSR_RUN | DCSR_MASKRUN))) - s->chan[channel].state |= DCSR_STOPINTR; - - if (value & DCSR_CLRCMPST) - s->chan[channel].state &= ~DCSR_CMPST; - if (value & DCSR_SETCMPST) - s->chan[channel].state |= DCSR_CMPST; - - pxa2xx_dma_update(s, channel); - break; - - case DALGN: - s->align = value; - break; - - case DPCSR: - s->pio = value & 0x80000001; - break; - - default: - if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) { - channel = (offset - D_CH0) >> 4; - switch ((offset & 0x0f) >> 2) { - case DDADR: - s->chan[channel].descr = value; - break; - case DSADR: - s->chan[channel].src = value; - break; - case DTADR: - s->chan[channel].dest = value; - break; - case DCMD: - s->chan[channel].cmd = value; - break; - default: - goto fail; - } - - break; - } - fail: - hw_error("%s: Bad offset " TARGET_FMT_plx "\n", __FUNCTION__, offset); - } -} - -static const MemoryRegionOps pxa2xx_dma_ops = { - .read = pxa2xx_dma_read, - .write = pxa2xx_dma_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pxa2xx_dma_request(void *opaque, int req_num, int on) -{ - PXA2xxDMAState *s = opaque; - int ch; - if (req_num < 0 || req_num >= PXA2XX_DMA_NUM_REQUESTS) - hw_error("%s: Bad DMA request %i\n", __FUNCTION__, req_num); - - if (!(s->req[req_num] & DRCMR_MAPVLD)) - return; - ch = s->req[req_num] & DRCMR_CHLNUM; - - if (!s->chan[ch].request && on) - s->chan[ch].state |= DCSR_RASINTR; - else - s->chan[ch].state &= ~DCSR_RASINTR; - if (s->chan[ch].request && !on) - s->chan[ch].state |= DCSR_EORINT; - - s->chan[ch].request = on; - if (on) { - pxa2xx_dma_run(s); - pxa2xx_dma_update(s, ch); - } -} - -static int pxa2xx_dma_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PXA2xxDMAState *s = PXA2XX_DMA(dev); - int i; - - if (s->channels <= 0) { - return -1; - } - - s->chan = g_new0(PXA2xxDMAChannel, s->channels); - - for (i = 0; i < s->channels; i ++) - s->chan[i].state = DCSR_STOPINTR; - - memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS); - - qdev_init_gpio_in(dev, pxa2xx_dma_request, PXA2XX_DMA_NUM_REQUESTS); - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_dma_ops, s, - "pxa2xx.dma", 0x00010000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - - return 0; -} - -DeviceState *pxa27x_dma_init(hwaddr base, qemu_irq irq) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "pxa2xx-dma"); - qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - return dev; -} - -DeviceState *pxa255_dma_init(hwaddr base, qemu_irq irq) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "pxa2xx-dma"); - qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - return dev; -} - -static bool is_version_0(void *opaque, int version_id) -{ - return version_id == 0; -} - -static VMStateDescription vmstate_pxa2xx_dma_chan = { - .name = "pxa2xx_dma_chan", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(descr, PXA2xxDMAChannel), - VMSTATE_UINT32(src, PXA2xxDMAChannel), - VMSTATE_UINT32(dest, PXA2xxDMAChannel), - VMSTATE_UINT32(cmd, PXA2xxDMAChannel), - VMSTATE_UINT32(state, PXA2xxDMAChannel), - VMSTATE_INT32(request, PXA2xxDMAChannel), - VMSTATE_END_OF_LIST(), - }, -}; - -static VMStateDescription vmstate_pxa2xx_dma = { - .name = "pxa2xx_dma", - .version_id = 1, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UNUSED_TEST(is_version_0, 4), - VMSTATE_UINT32(stopintr, PXA2xxDMAState), - VMSTATE_UINT32(eorintr, PXA2xxDMAState), - VMSTATE_UINT32(rasintr, PXA2xxDMAState), - VMSTATE_UINT32(startintr, PXA2xxDMAState), - VMSTATE_UINT32(endintr, PXA2xxDMAState), - VMSTATE_UINT32(align, PXA2xxDMAState), - VMSTATE_UINT32(pio, PXA2xxDMAState), - VMSTATE_BUFFER(req, PXA2xxDMAState), - VMSTATE_STRUCT_VARRAY_POINTER_INT32(chan, PXA2xxDMAState, channels, - vmstate_pxa2xx_dma_chan, PXA2xxDMAChannel), - VMSTATE_END_OF_LIST(), - }, -}; - -static Property pxa2xx_dma_properties[] = { - DEFINE_PROP_INT32("channels", PXA2xxDMAState, channels, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa2xx_dma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pxa2xx_dma_init; - dc->desc = "PXA2xx DMA controller"; - dc->vmsd = &vmstate_pxa2xx_dma; - dc->props = pxa2xx_dma_properties; -} - -static const TypeInfo pxa2xx_dma_info = { - .name = TYPE_PXA2XX_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxDMAState), - .class_init = pxa2xx_dma_class_init, -}; - -static void pxa2xx_dma_register_types(void) -{ - type_register_static(&pxa2xx_dma_info); -} - -type_init(pxa2xx_dma_register_types) diff --git a/qemu/hw/dma/rc4030.c b/qemu/hw/dma/rc4030.c deleted file mode 100644 index a06c2359a..000000000 --- a/qemu/hw/dma/rc4030.c +++ /dev/null @@ -1,842 +0,0 @@ -/* - * QEMU JAZZ RC4030 chipset - * - * Copyright (c) 2007-2013 Hervé Poussineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/mips/mips.h" -#include "hw/sysbus.h" -#include "qemu/timer.h" -#include "exec/address-spaces.h" -#include "trace.h" - -/********************************************************/ -/* rc4030 emulation */ - -#define MAX_TL_ENTRIES 512 - -typedef struct dma_pagetable_entry { - int32_t frame; - int32_t owner; -} QEMU_PACKED dma_pagetable_entry; - -#define DMA_PAGESIZE 4096 -#define DMA_REG_ENABLE 1 -#define DMA_REG_COUNT 2 -#define DMA_REG_ADDRESS 3 - -#define DMA_FLAG_ENABLE 0x0001 -#define DMA_FLAG_MEM_TO_DEV 0x0002 -#define DMA_FLAG_TC_INTR 0x0100 -#define DMA_FLAG_MEM_INTR 0x0200 -#define DMA_FLAG_ADDR_INTR 0x0400 - -#define TYPE_RC4030 "rc4030" -#define RC4030(obj) \ - OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030) - -typedef struct rc4030State -{ - SysBusDevice parent; - - uint32_t config; /* 0x0000: RC4030 config register */ - uint32_t revision; /* 0x0008: RC4030 Revision register */ - uint32_t invalid_address_register; /* 0x0010: Invalid Address register */ - - /* DMA */ - uint32_t dma_regs[8][4]; - uint32_t dma_tl_base; /* 0x0018: DMA transl. table base */ - uint32_t dma_tl_limit; /* 0x0020: DMA transl. table limit */ - - /* cache */ - uint32_t cache_maint; /* 0x0030: Cache Maintenance */ - uint32_t remote_failed_address; /* 0x0038: Remote Failed Address */ - uint32_t memory_failed_address; /* 0x0040: Memory Failed Address */ - uint32_t cache_ptag; /* 0x0048: I/O Cache Physical Tag */ - uint32_t cache_ltag; /* 0x0050: I/O Cache Logical Tag */ - uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */ - - uint32_t nmi_interrupt; /* 0x0200: interrupt source */ - uint32_t memory_refresh_rate; /* 0x0210: memory refresh rate */ - uint32_t nvram_protect; /* 0x0220: NV ram protect register */ - uint32_t rem_speed[16]; - uint32_t imr_jazz; /* Local bus int enable mask */ - uint32_t isr_jazz; /* Local bus int source */ - - /* timer */ - QEMUTimer *periodic_timer; - uint32_t itr; /* Interval timer reload */ - - qemu_irq timer_irq; - qemu_irq jazz_bus_irq; - - /* biggest translation table */ - MemoryRegion dma_tt; - /* translation table memory region alias, added to system RAM */ - MemoryRegion dma_tt_alias; - /* whole DMA memory region, root of DMA address space */ - MemoryRegion dma_mr; - /* translation table entry aliases, added to DMA memory region */ - MemoryRegion dma_mrs[MAX_TL_ENTRIES]; - AddressSpace dma_as; - - MemoryRegion iomem_chipset; - MemoryRegion iomem_jazzio; -} rc4030State; - -static void set_next_tick(rc4030State *s) -{ - qemu_irq_lower(s->timer_irq); - uint32_t tm_hz; - - tm_hz = 1000 / (s->itr + 1); - - timer_mod(s->periodic_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND / tm_hz); -} - -/* called for accesses to rc4030 */ -static uint64_t rc4030_read(void *opaque, hwaddr addr, unsigned int size) -{ - rc4030State *s = opaque; - uint32_t val; - - addr &= 0x3fff; - switch (addr & ~0x3) { - /* Global config register */ - case 0x0000: - val = s->config; - break; - /* Revision register */ - case 0x0008: - val = s->revision; - break; - /* Invalid Address register */ - case 0x0010: - val = s->invalid_address_register; - break; - /* DMA transl. table base */ - case 0x0018: - val = s->dma_tl_base; - break; - /* DMA transl. table limit */ - case 0x0020: - val = s->dma_tl_limit; - break; - /* Remote Failed Address */ - case 0x0038: - val = s->remote_failed_address; - break; - /* Memory Failed Address */ - case 0x0040: - val = s->memory_failed_address; - break; - /* I/O Cache Byte Mask */ - case 0x0058: - val = s->cache_bmask; - /* HACK */ - if (s->cache_bmask == (uint32_t)-1) - s->cache_bmask = 0; - break; - /* Remote Speed Registers */ - case 0x0070: - case 0x0078: - case 0x0080: - case 0x0088: - case 0x0090: - case 0x0098: - case 0x00a0: - case 0x00a8: - case 0x00b0: - case 0x00b8: - case 0x00c0: - case 0x00c8: - case 0x00d0: - case 0x00d8: - case 0x00e0: - case 0x00e8: - val = s->rem_speed[(addr - 0x0070) >> 3]; - break; - /* DMA channel base address */ - case 0x0100: - case 0x0108: - case 0x0110: - case 0x0118: - case 0x0120: - case 0x0128: - case 0x0130: - case 0x0138: - case 0x0140: - case 0x0148: - case 0x0150: - case 0x0158: - case 0x0160: - case 0x0168: - case 0x0170: - case 0x0178: - case 0x0180: - case 0x0188: - case 0x0190: - case 0x0198: - case 0x01a0: - case 0x01a8: - case 0x01b0: - case 0x01b8: - case 0x01c0: - case 0x01c8: - case 0x01d0: - case 0x01d8: - case 0x01e0: - case 0x01e8: - case 0x01f0: - case 0x01f8: - { - int entry = (addr - 0x0100) >> 5; - int idx = (addr & 0x1f) >> 3; - val = s->dma_regs[entry][idx]; - } - break; - /* Interrupt source */ - case 0x0200: - val = s->nmi_interrupt; - break; - /* Error type */ - case 0x0208: - val = 0; - break; - /* Memory refresh rate */ - case 0x0210: - val = s->memory_refresh_rate; - break; - /* NV ram protect register */ - case 0x0220: - val = s->nvram_protect; - break; - /* Interval timer count */ - case 0x0230: - val = 0; - qemu_irq_lower(s->timer_irq); - break; - /* EISA interrupt */ - case 0x0238: - val = 7; /* FIXME: should be read from EISA controller */ - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "rc4030: invalid read at 0x%x", (int)addr); - val = 0; - break; - } - - if ((addr & ~3) != 0x230) { - trace_rc4030_read(addr, val); - } - - return val; -} - -static void rc4030_dma_as_update_one(rc4030State *s, int index, uint32_t frame) -{ - if (index < MAX_TL_ENTRIES) { - memory_region_set_enabled(&s->dma_mrs[index], false); - } - - if (!frame) { - return; - } - - if (index >= MAX_TL_ENTRIES) { - qemu_log_mask(LOG_UNIMP, - "rc4030: trying to use too high " - "translation table entry %d (max allowed=%d)", - index, MAX_TL_ENTRIES); - return; - } - memory_region_set_alias_offset(&s->dma_mrs[index], frame); - memory_region_set_enabled(&s->dma_mrs[index], true); -} - -static void rc4030_dma_tt_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - rc4030State *s = opaque; - - /* write memory */ - memcpy(memory_region_get_ram_ptr(&s->dma_tt) + addr, &data, size); - - /* update dma address space (only if frame field has been written) */ - if (addr % sizeof(dma_pagetable_entry) == 0) { - int index = addr / sizeof(dma_pagetable_entry); - memory_region_transaction_begin(); - rc4030_dma_as_update_one(s, index, (uint32_t)data); - memory_region_transaction_commit(); - } -} - -static const MemoryRegionOps rc4030_dma_tt_ops = { - .write = rc4030_dma_tt_write, - .impl.min_access_size = 4, - .impl.max_access_size = 4, -}; - -static void rc4030_dma_tt_update(rc4030State *s, uint32_t new_tl_base, - uint32_t new_tl_limit) -{ - int entries, i; - dma_pagetable_entry *dma_tl_contents; - - if (s->dma_tl_limit) { - /* write old dma tl table to physical memory */ - memory_region_del_subregion(get_system_memory(), &s->dma_tt_alias); - cpu_physical_memory_write(s->dma_tl_limit & 0x7fffffff, - memory_region_get_ram_ptr(&s->dma_tt), - memory_region_size(&s->dma_tt_alias)); - } - object_unparent(OBJECT(&s->dma_tt_alias)); - - s->dma_tl_base = new_tl_base; - s->dma_tl_limit = new_tl_limit; - new_tl_base &= 0x7fffffff; - - if (s->dma_tl_limit) { - uint64_t dma_tt_size; - if (s->dma_tl_limit <= memory_region_size(&s->dma_tt)) { - dma_tt_size = s->dma_tl_limit; - } else { - dma_tt_size = memory_region_size(&s->dma_tt); - } - memory_region_init_alias(&s->dma_tt_alias, OBJECT(s), - "dma-table-alias", - &s->dma_tt, 0, dma_tt_size); - dma_tl_contents = memory_region_get_ram_ptr(&s->dma_tt); - cpu_physical_memory_read(new_tl_base, dma_tl_contents, dma_tt_size); - - memory_region_transaction_begin(); - entries = dma_tt_size / sizeof(dma_pagetable_entry); - for (i = 0; i < entries; i++) { - rc4030_dma_as_update_one(s, i, dma_tl_contents[i].frame); - } - memory_region_add_subregion(get_system_memory(), new_tl_base, - &s->dma_tt_alias); - memory_region_transaction_commit(); - } else { - memory_region_init(&s->dma_tt_alias, OBJECT(s), - "dma-table-alias", 0); - } -} - -static void rc4030_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - rc4030State *s = opaque; - uint32_t val = data; - addr &= 0x3fff; - - trace_rc4030_write(addr, val); - - switch (addr & ~0x3) { - /* Global config register */ - case 0x0000: - s->config = val; - break; - /* DMA transl. table base */ - case 0x0018: - rc4030_dma_tt_update(s, val, s->dma_tl_limit); - break; - /* DMA transl. table limit */ - case 0x0020: - rc4030_dma_tt_update(s, s->dma_tl_base, val); - break; - /* DMA transl. table invalidated */ - case 0x0028: - break; - /* Cache Maintenance */ - case 0x0030: - s->cache_maint = val; - break; - /* I/O Cache Physical Tag */ - case 0x0048: - s->cache_ptag = val; - break; - /* I/O Cache Logical Tag */ - case 0x0050: - s->cache_ltag = val; - break; - /* I/O Cache Byte Mask */ - case 0x0058: - s->cache_bmask |= val; /* HACK */ - break; - /* I/O Cache Buffer Window */ - case 0x0060: - /* HACK */ - if (s->cache_ltag == 0x80000001 && s->cache_bmask == 0xf0f0f0f) { - hwaddr dest = s->cache_ptag & ~0x1; - dest += (s->cache_maint & 0x3) << 3; - cpu_physical_memory_write(dest, &val, 4); - } - break; - /* Remote Speed Registers */ - case 0x0070: - case 0x0078: - case 0x0080: - case 0x0088: - case 0x0090: - case 0x0098: - case 0x00a0: - case 0x00a8: - case 0x00b0: - case 0x00b8: - case 0x00c0: - case 0x00c8: - case 0x00d0: - case 0x00d8: - case 0x00e0: - case 0x00e8: - s->rem_speed[(addr - 0x0070) >> 3] = val; - break; - /* DMA channel base address */ - case 0x0100: - case 0x0108: - case 0x0110: - case 0x0118: - case 0x0120: - case 0x0128: - case 0x0130: - case 0x0138: - case 0x0140: - case 0x0148: - case 0x0150: - case 0x0158: - case 0x0160: - case 0x0168: - case 0x0170: - case 0x0178: - case 0x0180: - case 0x0188: - case 0x0190: - case 0x0198: - case 0x01a0: - case 0x01a8: - case 0x01b0: - case 0x01b8: - case 0x01c0: - case 0x01c8: - case 0x01d0: - case 0x01d8: - case 0x01e0: - case 0x01e8: - case 0x01f0: - case 0x01f8: - { - int entry = (addr - 0x0100) >> 5; - int idx = (addr & 0x1f) >> 3; - s->dma_regs[entry][idx] = val; - } - break; - /* Memory refresh rate */ - case 0x0210: - s->memory_refresh_rate = val; - break; - /* Interval timer reload */ - case 0x0228: - s->itr = val; - qemu_irq_lower(s->timer_irq); - set_next_tick(s); - break; - /* EISA interrupt */ - case 0x0238: - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "rc4030: invalid write of 0x%02x at 0x%x", - val, (int)addr); - break; - } -} - -static const MemoryRegionOps rc4030_ops = { - .read = rc4030_read, - .write = rc4030_write, - .impl.min_access_size = 4, - .impl.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void update_jazz_irq(rc4030State *s) -{ - uint16_t pending; - - pending = s->isr_jazz & s->imr_jazz; - - if (pending != 0) - qemu_irq_raise(s->jazz_bus_irq); - else - qemu_irq_lower(s->jazz_bus_irq); -} - -static void rc4030_irq_jazz_request(void *opaque, int irq, int level) -{ - rc4030State *s = opaque; - - if (level) { - s->isr_jazz |= 1 << irq; - } else { - s->isr_jazz &= ~(1 << irq); - } - - update_jazz_irq(s); -} - -static void rc4030_periodic_timer(void *opaque) -{ - rc4030State *s = opaque; - - set_next_tick(s); - qemu_irq_raise(s->timer_irq); -} - -static uint64_t jazzio_read(void *opaque, hwaddr addr, unsigned int size) -{ - rc4030State *s = opaque; - uint32_t val; - uint32_t irq; - addr &= 0xfff; - - switch (addr) { - /* Local bus int source */ - case 0x00: { - uint32_t pending = s->isr_jazz & s->imr_jazz; - val = 0; - irq = 0; - while (pending) { - if (pending & 1) { - val = (irq + 1) << 2; - break; - } - irq++; - pending >>= 1; - } - break; - } - /* Local bus int enable mask */ - case 0x02: - val = s->imr_jazz; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "rc4030/jazzio: invalid read at 0x%x", (int)addr); - val = 0; - break; - } - - trace_jazzio_read(addr, val); - - return val; -} - -static void jazzio_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - rc4030State *s = opaque; - uint32_t val = data; - addr &= 0xfff; - - trace_jazzio_write(addr, val); - - switch (addr) { - /* Local bus int enable mask */ - case 0x02: - s->imr_jazz = val; - update_jazz_irq(s); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "rc4030/jazzio: invalid write of 0x%02x at 0x%x", - val, (int)addr); - break; - } -} - -static const MemoryRegionOps jazzio_ops = { - .read = jazzio_read, - .write = jazzio_write, - .impl.min_access_size = 2, - .impl.max_access_size = 2, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void rc4030_reset(DeviceState *dev) -{ - rc4030State *s = RC4030(dev); - int i; - - s->config = 0x410; /* some boards seem to accept 0x104 too */ - s->revision = 1; - s->invalid_address_register = 0; - - memset(s->dma_regs, 0, sizeof(s->dma_regs)); - rc4030_dma_tt_update(s, 0, 0); - - s->remote_failed_address = s->memory_failed_address = 0; - s->cache_maint = 0; - s->cache_ptag = s->cache_ltag = 0; - s->cache_bmask = 0; - - s->memory_refresh_rate = 0x18186; - s->nvram_protect = 7; - for (i = 0; i < 15; i++) - s->rem_speed[i] = 7; - s->imr_jazz = 0x10; /* XXX: required by firmware, but why? */ - s->isr_jazz = 0; - - s->itr = 0; - - qemu_irq_lower(s->timer_irq); - qemu_irq_lower(s->jazz_bus_irq); -} - -static int rc4030_load(QEMUFile *f, void *opaque, int version_id) -{ - rc4030State* s = opaque; - int i, j; - - if (version_id != 2) - return -EINVAL; - - s->config = qemu_get_be32(f); - s->invalid_address_register = qemu_get_be32(f); - for (i = 0; i < 8; i++) - for (j = 0; j < 4; j++) - s->dma_regs[i][j] = qemu_get_be32(f); - s->dma_tl_base = qemu_get_be32(f); - s->dma_tl_limit = qemu_get_be32(f); - s->cache_maint = qemu_get_be32(f); - s->remote_failed_address = qemu_get_be32(f); - s->memory_failed_address = qemu_get_be32(f); - s->cache_ptag = qemu_get_be32(f); - s->cache_ltag = qemu_get_be32(f); - s->cache_bmask = qemu_get_be32(f); - s->memory_refresh_rate = qemu_get_be32(f); - s->nvram_protect = qemu_get_be32(f); - for (i = 0; i < 15; i++) - s->rem_speed[i] = qemu_get_be32(f); - s->imr_jazz = qemu_get_be32(f); - s->isr_jazz = qemu_get_be32(f); - s->itr = qemu_get_be32(f); - - set_next_tick(s); - update_jazz_irq(s); - - return 0; -} - -static void rc4030_save(QEMUFile *f, void *opaque) -{ - rc4030State* s = opaque; - int i, j; - - qemu_put_be32(f, s->config); - qemu_put_be32(f, s->invalid_address_register); - for (i = 0; i < 8; i++) - for (j = 0; j < 4; j++) - qemu_put_be32(f, s->dma_regs[i][j]); - qemu_put_be32(f, s->dma_tl_base); - qemu_put_be32(f, s->dma_tl_limit); - qemu_put_be32(f, s->cache_maint); - qemu_put_be32(f, s->remote_failed_address); - qemu_put_be32(f, s->memory_failed_address); - qemu_put_be32(f, s->cache_ptag); - qemu_put_be32(f, s->cache_ltag); - qemu_put_be32(f, s->cache_bmask); - qemu_put_be32(f, s->memory_refresh_rate); - qemu_put_be32(f, s->nvram_protect); - for (i = 0; i < 15; i++) - qemu_put_be32(f, s->rem_speed[i]); - qemu_put_be32(f, s->imr_jazz); - qemu_put_be32(f, s->isr_jazz); - qemu_put_be32(f, s->itr); -} - -static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write) -{ - rc4030State *s = opaque; - hwaddr dma_addr; - int dev_to_mem; - - s->dma_regs[n][DMA_REG_ENABLE] &= ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR); - - /* Check DMA channel consistency */ - dev_to_mem = (s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_MEM_TO_DEV) ? 0 : 1; - if (!(s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_ENABLE) || - (is_write != dev_to_mem)) { - s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR; - s->nmi_interrupt |= 1 << n; - return; - } - - /* Get start address and len */ - if (len > s->dma_regs[n][DMA_REG_COUNT]) - len = s->dma_regs[n][DMA_REG_COUNT]; - dma_addr = s->dma_regs[n][DMA_REG_ADDRESS]; - - /* Read/write data at right place */ - address_space_rw(&s->dma_as, dma_addr, MEMTXATTRS_UNSPECIFIED, - buf, len, is_write); - - s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR; - s->dma_regs[n][DMA_REG_COUNT] -= len; -} - -struct rc4030DMAState { - void *opaque; - int n; -}; - -void rc4030_dma_read(void *dma, uint8_t *buf, int len) -{ - rc4030_dma s = dma; - rc4030_do_dma(s->opaque, s->n, buf, len, 0); -} - -void rc4030_dma_write(void *dma, uint8_t *buf, int len) -{ - rc4030_dma s = dma; - rc4030_do_dma(s->opaque, s->n, buf, len, 1); -} - -static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n) -{ - rc4030_dma *s; - struct rc4030DMAState *p; - int i; - - s = (rc4030_dma *)g_malloc0(sizeof(rc4030_dma) * n); - p = (struct rc4030DMAState *)g_malloc0(sizeof(struct rc4030DMAState) * n); - for (i = 0; i < n; i++) { - p->opaque = opaque; - p->n = i; - s[i] = p; - p++; - } - return s; -} - -static void rc4030_initfn(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - rc4030State *s = RC4030(obj); - SysBusDevice *sysbus = SYS_BUS_DEVICE(obj); - - qdev_init_gpio_in(dev, rc4030_irq_jazz_request, 16); - - sysbus_init_irq(sysbus, &s->timer_irq); - sysbus_init_irq(sysbus, &s->jazz_bus_irq); - - register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s); - - sysbus_init_mmio(sysbus, &s->iomem_chipset); - sysbus_init_mmio(sysbus, &s->iomem_jazzio); -} - -static void rc4030_realize(DeviceState *dev, Error **errp) -{ - rc4030State *s = RC4030(dev); - Object *o = OBJECT(dev); - int i; - - s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - rc4030_periodic_timer, s); - - memory_region_init_io(&s->iomem_chipset, NULL, &rc4030_ops, s, - "rc4030.chipset", 0x300); - memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s, - "rc4030.jazzio", 0x00001000); - - memory_region_init_rom_device(&s->dma_tt, o, - &rc4030_dma_tt_ops, s, "dma-table", - MAX_TL_ENTRIES * sizeof(dma_pagetable_entry), - NULL); - memory_region_init(&s->dma_tt_alias, o, "dma-table-alias", 0); - memory_region_init(&s->dma_mr, o, "dma", INT32_MAX); - for (i = 0; i < MAX_TL_ENTRIES; ++i) { - memory_region_init_alias(&s->dma_mrs[i], o, "dma-alias", - get_system_memory(), 0, DMA_PAGESIZE); - memory_region_set_enabled(&s->dma_mrs[i], false); - memory_region_add_subregion(&s->dma_mr, i * DMA_PAGESIZE, - &s->dma_mrs[i]); - } - address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma"); -} - -static void rc4030_unrealize(DeviceState *dev, Error **errp) -{ - rc4030State *s = RC4030(dev); - int i; - - timer_free(s->periodic_timer); - - address_space_destroy(&s->dma_as); - object_unparent(OBJECT(&s->dma_tt)); - object_unparent(OBJECT(&s->dma_tt_alias)); - object_unparent(OBJECT(&s->dma_mr)); - for (i = 0; i < MAX_TL_ENTRIES; ++i) { - memory_region_del_subregion(&s->dma_mr, &s->dma_mrs[i]); - object_unparent(OBJECT(&s->dma_mrs[i])); - } -} - -static void rc4030_class_init(ObjectClass *klass, void *class_data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = rc4030_realize; - dc->unrealize = rc4030_unrealize; - dc->reset = rc4030_reset; -} - -static const TypeInfo rc4030_info = { - .name = TYPE_RC4030, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(rc4030State), - .instance_init = rc4030_initfn, - .class_init = rc4030_class_init, -}; - -static void rc4030_register_types(void) -{ - type_register_static(&rc4030_info); -} - -type_init(rc4030_register_types) - -DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr) -{ - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_RC4030); - qdev_init_nofail(dev); - - *dmas = rc4030_allocate_dmas(dev, 4); - *dma_mr = &RC4030(dev)->dma_mr; - return dev; -} diff --git a/qemu/hw/dma/soc_dma.c b/qemu/hw/dma/soc_dma.c deleted file mode 100644 index 9bb499bf9..000000000 --- a/qemu/hw/dma/soc_dma.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * On-chip DMA controller framework. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski <andrew@openedhand.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/arm/soc_dma.h" - -static void transfer_mem2mem(struct soc_dma_ch_s *ch) -{ - memcpy(ch->paddr[0], ch->paddr[1], ch->bytes); - ch->paddr[0] += ch->bytes; - ch->paddr[1] += ch->bytes; -} - -static void transfer_mem2fifo(struct soc_dma_ch_s *ch) -{ - ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes); - ch->paddr[0] += ch->bytes; -} - -static void transfer_fifo2mem(struct soc_dma_ch_s *ch) -{ - ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes); - ch->paddr[1] += ch->bytes; -} - -/* This is further optimisable but isn't very important because often - * DMA peripherals forbid this kind of transfers and even when they don't, - * oprating systems may not need to use them. */ -static void *fifo_buf; -static int fifo_size; -static void transfer_fifo2fifo(struct soc_dma_ch_s *ch) -{ - if (ch->bytes > fifo_size) - fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes); - - /* Implement as transfer_fifo2linear + transfer_linear2fifo. */ - ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes); - ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes); -} - -struct dma_s { - struct soc_dma_s soc; - int chnum; - uint64_t ch_enable_mask; - int64_t channel_freq; - int enabled_count; - - struct memmap_entry_s { - enum soc_dma_port_type type; - hwaddr addr; - union { - struct { - void *opaque; - soc_dma_io_t fn; - int out; - } fifo; - struct { - void *base; - size_t size; - } mem; - } u; - } *memmap; - int memmap_size; - - struct soc_dma_ch_s ch[0]; -}; - -static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) -{ - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - struct dma_s *dma = (struct dma_s *) ch->dma; - - timer_mod(ch->timer, now + delay_bytes / dma->channel_freq); -} - -static void soc_dma_ch_run(void *opaque) -{ - struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque; - - ch->running = 1; - ch->dma->setup_fn(ch); - ch->transfer_fn(ch); - ch->running = 0; - - if (ch->enable) - soc_dma_ch_schedule(ch, ch->bytes); - ch->bytes = 0; -} - -static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma, - hwaddr addr) -{ - struct memmap_entry_s *lo; - int hi; - - lo = dma->memmap; - hi = dma->memmap_size; - - while (hi > 1) { - hi /= 2; - if (lo[hi].addr <= addr) - lo += hi; - } - - return lo; -} - -static inline enum soc_dma_port_type soc_dma_ch_update_type( - struct soc_dma_ch_s *ch, int port) -{ - struct dma_s *dma = (struct dma_s *) ch->dma; - struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]); - - if (entry->type == soc_dma_port_fifo) { - while (entry < dma->memmap + dma->memmap_size && - entry->u.fifo.out != port) - entry ++; - if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port) - return soc_dma_port_other; - - if (ch->type[port] != soc_dma_access_const) - return soc_dma_port_other; - - ch->io_fn[port] = entry->u.fifo.fn; - ch->io_opaque[port] = entry->u.fifo.opaque; - return soc_dma_port_fifo; - } else if (entry->type == soc_dma_port_mem) { - if (entry->addr > ch->vaddr[port] || - entry->addr + entry->u.mem.size <= ch->vaddr[port]) - return soc_dma_port_other; - - /* TODO: support constant memory address for source port as used for - * drawing solid rectangles by PalmOS(R). */ - if (ch->type[port] != soc_dma_access_const) - return soc_dma_port_other; - - ch->paddr[port] = (uint8_t *) entry->u.mem.base + - (ch->vaddr[port] - entry->addr); - /* TODO: save bytes left to the end of the mapping somewhere so we - * can check we're not reading beyond it. */ - return soc_dma_port_mem; - } else - return soc_dma_port_other; -} - -void soc_dma_ch_update(struct soc_dma_ch_s *ch) -{ - enum soc_dma_port_type src, dst; - - src = soc_dma_ch_update_type(ch, 0); - if (src == soc_dma_port_other) { - ch->update = 0; - ch->transfer_fn = ch->dma->transfer_fn; - return; - } - dst = soc_dma_ch_update_type(ch, 1); - - /* TODO: use src and dst as array indices. */ - if (src == soc_dma_port_mem && dst == soc_dma_port_mem) - ch->transfer_fn = transfer_mem2mem; - else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_mem2fifo; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem) - ch->transfer_fn = transfer_fifo2mem; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_fifo2fifo; - else - ch->transfer_fn = ch->dma->transfer_fn; - - ch->update = (dst != soc_dma_port_other); -} - -static void soc_dma_ch_freq_update(struct dma_s *s) -{ - if (s->enabled_count) - /* We completely ignore channel priorities and stuff */ - s->channel_freq = s->soc.freq / s->enabled_count; - else { - /* TODO: Signal that we want to disable the functional clock and let - * the platform code decide what to do with it, i.e. check that - * auto-idle is enabled in the clock controller and if we are stopping - * the clock, do the same with any parent clocks that had only one - * user keeping them on and auto-idle enabled. */ - } -} - -void soc_dma_set_request(struct soc_dma_ch_s *ch, int level) -{ - struct dma_s *dma = (struct dma_s *) ch->dma; - - dma->enabled_count += level - ch->enable; - - if (level) - dma->ch_enable_mask |= 1 << ch->num; - else - dma->ch_enable_mask &= ~(1 << ch->num); - - if (level != ch->enable) { - soc_dma_ch_freq_update(dma); - ch->enable = level; - - if (!ch->enable) - timer_del(ch->timer); - else if (!ch->running) - soc_dma_ch_run(ch); - else - soc_dma_ch_schedule(ch, 1); - } -} - -void soc_dma_reset(struct soc_dma_s *soc) -{ - struct dma_s *s = (struct dma_s *) soc; - - s->soc.drqbmp = 0; - s->ch_enable_mask = 0; - s->enabled_count = 0; - soc_dma_ch_freq_update(s); -} - -/* TODO: take a functional-clock argument */ -struct soc_dma_s *soc_dma_init(int n) -{ - int i; - struct dma_s *s = g_malloc0(sizeof(*s) + n * sizeof(*s->ch)); - - s->chnum = n; - s->soc.ch = s->ch; - for (i = 0; i < n; i ++) { - s->ch[i].dma = &s->soc; - s->ch[i].num = i; - s->ch[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, soc_dma_ch_run, &s->ch[i]); - } - - soc_dma_reset(&s->soc); - fifo_size = 0; - - return &s->soc; -} - -void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base, - soc_dma_io_t fn, void *opaque, int out) -{ - struct memmap_entry_s *entry; - struct dma_s *dma = (struct dma_s *) soc; - - dma->memmap = g_realloc(dma->memmap, sizeof(*entry) * - (dma->memmap_size + 1)); - entry = soc_dma_lookup(dma, virt_base); - - if (dma->memmap_size) { - if (entry->type == soc_dma_port_mem) { - if (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base) { - fprintf(stderr, "%s: FIFO at %"PRIx64 - " collides with RAM region at %"PRIx64 - "-%"PRIx64 "\n", __func__, - virt_base, entry->addr, - (entry->addr + entry->u.mem.size)); - exit(-1); - } - - if (entry->addr <= virt_base) - entry ++; - } else - while (entry < dma->memmap + dma->memmap_size && - entry->addr <= virt_base) { - if (entry->addr == virt_base && entry->u.fifo.out == out) { - fprintf(stderr, "%s: FIFO at %"PRIx64 - " collides FIFO at %"PRIx64 "\n", - __func__, virt_base, entry->addr); - exit(-1); - } - - entry ++; - } - - memmove(entry + 1, entry, - (uint8_t *) (dma->memmap + dma->memmap_size ++) - - (uint8_t *) entry); - } else - dma->memmap_size ++; - - entry->addr = virt_base; - entry->type = soc_dma_port_fifo; - entry->u.fifo.fn = fn; - entry->u.fifo.opaque = opaque; - entry->u.fifo.out = out; -} - -void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, - hwaddr virt_base, size_t size) -{ - struct memmap_entry_s *entry; - struct dma_s *dma = (struct dma_s *) soc; - - dma->memmap = g_realloc(dma->memmap, sizeof(*entry) * - (dma->memmap_size + 1)); - entry = soc_dma_lookup(dma, virt_base); - - if (dma->memmap_size) { - if (entry->type == soc_dma_port_mem) { - if ((entry->addr >= virt_base && entry->addr < virt_base + size) || - (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base)) { - fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64 - " collides with RAM region at %"PRIx64 - "-%"PRIx64 "\n", __func__, - virt_base, virt_base + size, - entry->addr, entry->addr + entry->u.mem.size); - exit(-1); - } - - if (entry->addr <= virt_base) - entry ++; - } else { - if (entry->addr >= virt_base && - entry->addr < virt_base + size) { - fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64 - " collides with FIFO at %"PRIx64 - "\n", __func__, - virt_base, virt_base + size, - entry->addr); - exit(-1); - } - - while (entry < dma->memmap + dma->memmap_size && - entry->addr <= virt_base) - entry ++; - } - - memmove(entry + 1, entry, - (uint8_t *) (dma->memmap + dma->memmap_size ++) - - (uint8_t *) entry); - } else - dma->memmap_size ++; - - entry->addr = virt_base; - entry->type = soc_dma_port_mem; - entry->u.mem.base = phys_base; - entry->u.mem.size = size; -} - -/* TODO: port removal for ports like PCMCIA memory */ diff --git a/qemu/hw/dma/sparc32_dma.c b/qemu/hw/dma/sparc32_dma.c deleted file mode 100644 index 9d545e412..000000000 --- a/qemu/hw/dma/sparc32_dma.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * QEMU Sparc32 DMA controller emulation - * - * Copyright (c) 2006 Fabrice Bellard - * - * Modifications: - * 2010-Feb-14 Artyom Tarasenko : reworked irq generation - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sparc/sparc32_dma.h" -#include "hw/sparc/sun4m.h" -#include "hw/sysbus.h" -#include "trace.h" - -/* - * This is the DMA controller part of chip STP2000 (Master I/O), also - * produced as NCR89C100. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt - * and - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/DMA2.txt - */ - -#define DMA_REGS 4 -#define DMA_SIZE (4 * sizeof(uint32_t)) -/* We need the mask, because one instance of the device is not page - aligned (ledma, start address 0x0010) */ -#define DMA_MASK (DMA_SIZE - 1) -/* OBP says 0x20 bytes for ledma, the extras are aliased to espdma */ -#define DMA_ETH_SIZE (8 * sizeof(uint32_t)) -#define DMA_MAX_REG_OFFSET (2 * DMA_SIZE - 1) - -#define DMA_VER 0xa0000000 -#define DMA_INTR 1 -#define DMA_INTREN 0x10 -#define DMA_WRITE_MEM 0x100 -#define DMA_EN 0x200 -#define DMA_LOADED 0x04000000 -#define DMA_DRAIN_FIFO 0x40 -#define DMA_RESET 0x80 - -/* XXX SCSI and ethernet should have different read-only bit masks */ -#define DMA_CSR_RO_MASK 0xfe000007 - -#define TYPE_SPARC32_DMA "sparc32_dma" -#define SPARC32_DMA(obj) OBJECT_CHECK(DMAState, (obj), TYPE_SPARC32_DMA) - -typedef struct DMAState DMAState; - -struct DMAState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t dmaregs[DMA_REGS]; - qemu_irq irq; - void *iommu; - qemu_irq gpio[2]; - uint32_t is_ledma; -}; - -enum { - GPIO_RESET = 0, - GPIO_DMA, -}; - -/* Note: on sparc, the lance 16 bit bus is swapped */ -void ledma_memory_read(void *opaque, hwaddr addr, - uint8_t *buf, int len, int do_bswap) -{ - DMAState *s = opaque; - int i; - - addr |= s->dmaregs[3]; - trace_ledma_memory_read(addr); - if (do_bswap) { - sparc_iommu_memory_read(s->iommu, addr, buf, len); - } else { - addr &= ~1; - len &= ~1; - sparc_iommu_memory_read(s->iommu, addr, buf, len); - for(i = 0; i < len; i += 2) { - bswap16s((uint16_t *)(buf + i)); - } - } -} - -void ledma_memory_write(void *opaque, hwaddr addr, - uint8_t *buf, int len, int do_bswap) -{ - DMAState *s = opaque; - int l, i; - uint16_t tmp_buf[32]; - - addr |= s->dmaregs[3]; - trace_ledma_memory_write(addr); - if (do_bswap) { - sparc_iommu_memory_write(s->iommu, addr, buf, len); - } else { - addr &= ~1; - len &= ~1; - while (len > 0) { - l = len; - if (l > sizeof(tmp_buf)) - l = sizeof(tmp_buf); - for(i = 0; i < l; i += 2) { - tmp_buf[i >> 1] = bswap16(*(uint16_t *)(buf + i)); - } - sparc_iommu_memory_write(s->iommu, addr, (uint8_t *)tmp_buf, l); - len -= l; - buf += l; - addr += l; - } - } -} - -static void dma_set_irq(void *opaque, int irq, int level) -{ - DMAState *s = opaque; - if (level) { - s->dmaregs[0] |= DMA_INTR; - if (s->dmaregs[0] & DMA_INTREN) { - trace_sparc32_dma_set_irq_raise(); - qemu_irq_raise(s->irq); - } - } else { - if (s->dmaregs[0] & DMA_INTR) { - s->dmaregs[0] &= ~DMA_INTR; - if (s->dmaregs[0] & DMA_INTREN) { - trace_sparc32_dma_set_irq_lower(); - qemu_irq_lower(s->irq); - } - } - } -} - -void espdma_memory_read(void *opaque, uint8_t *buf, int len) -{ - DMAState *s = opaque; - - trace_espdma_memory_read(s->dmaregs[1]); - sparc_iommu_memory_read(s->iommu, s->dmaregs[1], buf, len); - s->dmaregs[1] += len; -} - -void espdma_memory_write(void *opaque, uint8_t *buf, int len) -{ - DMAState *s = opaque; - - trace_espdma_memory_write(s->dmaregs[1]); - sparc_iommu_memory_write(s->iommu, s->dmaregs[1], buf, len); - s->dmaregs[1] += len; -} - -static uint64_t dma_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - DMAState *s = opaque; - uint32_t saddr; - - if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) { - /* aliased to espdma, but we can't get there from here */ - /* buggy driver if using undocumented behavior, just return 0 */ - trace_sparc32_dma_mem_readl(addr, 0); - return 0; - } - saddr = (addr & DMA_MASK) >> 2; - trace_sparc32_dma_mem_readl(addr, s->dmaregs[saddr]); - return s->dmaregs[saddr]; -} - -static void dma_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - DMAState *s = opaque; - uint32_t saddr; - - if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) { - /* aliased to espdma, but we can't get there from here */ - trace_sparc32_dma_mem_writel(addr, 0, val); - return; - } - saddr = (addr & DMA_MASK) >> 2; - trace_sparc32_dma_mem_writel(addr, s->dmaregs[saddr], val); - switch (saddr) { - case 0: - if (val & DMA_INTREN) { - if (s->dmaregs[0] & DMA_INTR) { - trace_sparc32_dma_set_irq_raise(); - qemu_irq_raise(s->irq); - } - } else { - if (s->dmaregs[0] & (DMA_INTR | DMA_INTREN)) { - trace_sparc32_dma_set_irq_lower(); - qemu_irq_lower(s->irq); - } - } - if (val & DMA_RESET) { - qemu_irq_raise(s->gpio[GPIO_RESET]); - qemu_irq_lower(s->gpio[GPIO_RESET]); - } else if (val & DMA_DRAIN_FIFO) { - val &= ~DMA_DRAIN_FIFO; - } else if (val == 0) - val = DMA_DRAIN_FIFO; - - if (val & DMA_EN && !(s->dmaregs[0] & DMA_EN)) { - trace_sparc32_dma_enable_raise(); - qemu_irq_raise(s->gpio[GPIO_DMA]); - } else if (!(val & DMA_EN) && !!(s->dmaregs[0] & DMA_EN)) { - trace_sparc32_dma_enable_lower(); - qemu_irq_lower(s->gpio[GPIO_DMA]); - } - - val &= ~DMA_CSR_RO_MASK; - val |= DMA_VER; - s->dmaregs[0] = (s->dmaregs[0] & DMA_CSR_RO_MASK) | val; - break; - case 1: - s->dmaregs[0] |= DMA_LOADED; - /* fall through */ - default: - s->dmaregs[saddr] = val; - break; - } -} - -static const MemoryRegionOps dma_mem_ops = { - .read = dma_mem_read, - .write = dma_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void dma_reset(DeviceState *d) -{ - DMAState *s = SPARC32_DMA(d); - - memset(s->dmaregs, 0, DMA_SIZE); - s->dmaregs[0] = DMA_VER; -} - -static const VMStateDescription vmstate_dma = { - .name ="sparc32_dma", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(dmaregs, DMAState, DMA_REGS), - VMSTATE_END_OF_LIST() - } -}; - -static int sparc32_dma_init1(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - DMAState *s = SPARC32_DMA(dev); - int reg_size; - - sysbus_init_irq(sbd, &s->irq); - - reg_size = s->is_ledma ? DMA_ETH_SIZE : DMA_SIZE; - memory_region_init_io(&s->iomem, OBJECT(s), &dma_mem_ops, s, - "dma", reg_size); - sysbus_init_mmio(sbd, &s->iomem); - - qdev_init_gpio_in(dev, dma_set_irq, 1); - qdev_init_gpio_out(dev, s->gpio, 2); - - return 0; -} - -static Property sparc32_dma_properties[] = { - DEFINE_PROP_PTR("iommu_opaque", DMAState, iommu), - DEFINE_PROP_UINT32("is_ledma", DMAState, is_ledma, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sparc32_dma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = sparc32_dma_init1; - dc->reset = dma_reset; - dc->vmsd = &vmstate_dma; - dc->props = sparc32_dma_properties; - /* Reason: pointer property "iommu_opaque" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo sparc32_dma_info = { - .name = TYPE_SPARC32_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(DMAState), - .class_init = sparc32_dma_class_init, -}; - -static void sparc32_dma_register_types(void) -{ - type_register_static(&sparc32_dma_info); -} - -type_init(sparc32_dma_register_types) diff --git a/qemu/hw/dma/sun4m_iommu.c b/qemu/hw/dma/sun4m_iommu.c deleted file mode 100644 index b3cbc54c2..000000000 --- a/qemu/hw/dma/sun4m_iommu.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * QEMU Sun4m iommu emulation - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/sparc/sun4m.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" -#include "trace.h" - -/* - * I/O MMU used by Sun4m systems - * - * Chipset docs: - * "Sun-4M System Architecture (revision 2.0) by Chuck Narad", 950-1373-01, - * http://mediacast.sun.com/users/Barton808/media/Sun4M_SystemArchitecture_edited2.pdf - */ - -#define IOMMU_NREGS (4*4096/4) -#define IOMMU_CTRL (0x0000 >> 2) -#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */ -#define IOMMU_CTRL_VERS 0x0f000000 /* Version */ -#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */ -#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */ -#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */ -#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */ -#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */ -#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */ -#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */ -#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */ -#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */ -#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */ -#define IOMMU_CTRL_MASK 0x0000001d - -#define IOMMU_BASE (0x0004 >> 2) -#define IOMMU_BASE_MASK 0x07fffc00 - -#define IOMMU_TLBFLUSH (0x0014 >> 2) -#define IOMMU_TLBFLUSH_MASK 0xffffffff - -#define IOMMU_PGFLUSH (0x0018 >> 2) -#define IOMMU_PGFLUSH_MASK 0xffffffff - -#define IOMMU_AFSR (0x1000 >> 2) -#define IOMMU_AFSR_ERR 0x80000000 /* LE, TO, or BE asserted */ -#define IOMMU_AFSR_LE 0x40000000 /* SBUS reports error after - transaction */ -#define IOMMU_AFSR_TO 0x20000000 /* Write access took more than - 12.8 us. */ -#define IOMMU_AFSR_BE 0x10000000 /* Write access received error - acknowledge */ -#define IOMMU_AFSR_SIZE 0x0e000000 /* Size of transaction causing error */ -#define IOMMU_AFSR_S 0x01000000 /* Sparc was in supervisor mode */ -#define IOMMU_AFSR_RESV 0x00800000 /* Reserved, forced to 0x8 by - hardware */ -#define IOMMU_AFSR_ME 0x00080000 /* Multiple errors occurred */ -#define IOMMU_AFSR_RD 0x00040000 /* A read operation was in progress */ -#define IOMMU_AFSR_FAV 0x00020000 /* IOMMU afar has valid contents */ -#define IOMMU_AFSR_MASK 0xff0fffff - -#define IOMMU_AFAR (0x1004 >> 2) - -#define IOMMU_AER (0x1008 >> 2) /* Arbiter Enable Register */ -#define IOMMU_AER_EN_P0_ARB 0x00000001 /* MBus master 0x8 (Always 1) */ -#define IOMMU_AER_EN_P1_ARB 0x00000002 /* MBus master 0x9 */ -#define IOMMU_AER_EN_P2_ARB 0x00000004 /* MBus master 0xa */ -#define IOMMU_AER_EN_P3_ARB 0x00000008 /* MBus master 0xb */ -#define IOMMU_AER_EN_0 0x00010000 /* SBus slot 0 */ -#define IOMMU_AER_EN_1 0x00020000 /* SBus slot 1 */ -#define IOMMU_AER_EN_2 0x00040000 /* SBus slot 2 */ -#define IOMMU_AER_EN_3 0x00080000 /* SBus slot 3 */ -#define IOMMU_AER_EN_F 0x00100000 /* SBus on-board */ -#define IOMMU_AER_SBW 0x80000000 /* S-to-M asynchronous writes */ -#define IOMMU_AER_MASK 0x801f000f - -#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when - bypass enabled */ -#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */ -#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */ -#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses - produced by this device as pure - physical. */ -#define IOMMU_SBCFG_MASK 0x00010003 - -#define IOMMU_ARBEN (0x2000 >> 2) /* SBUS arbitration enable */ -#define IOMMU_ARBEN_MASK 0x001f0000 -#define IOMMU_MID 0x00000008 - -#define IOMMU_MASK_ID (0x3018 >> 2) /* Mask ID */ -#define IOMMU_MASK_ID_MASK 0x00ffffff - -#define IOMMU_MSII_MASK 0x26000000 /* microSPARC II mask number */ -#define IOMMU_TS_MASK 0x23000000 /* turboSPARC mask number */ - -/* The format of an iopte in the page tables */ -#define IOPTE_PAGE 0xffffff00 /* Physical page number (PA[35:12]) */ -#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or - Viking/MXCC) */ -#define IOPTE_WRITE 0x00000004 /* Writable */ -#define IOPTE_VALID 0x00000002 /* IOPTE is valid */ -#define IOPTE_WAZ 0x00000001 /* Write as zeros */ - -#define IOMMU_PAGE_SHIFT 12 -#define IOMMU_PAGE_SIZE (1 << IOMMU_PAGE_SHIFT) -#define IOMMU_PAGE_MASK ~(IOMMU_PAGE_SIZE - 1) - -#define TYPE_SUN4M_IOMMU "iommu" -#define SUN4M_IOMMU(obj) OBJECT_CHECK(IOMMUState, (obj), TYPE_SUN4M_IOMMU) - -typedef struct IOMMUState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t regs[IOMMU_NREGS]; - hwaddr iostart; - qemu_irq irq; - uint32_t version; -} IOMMUState; - -static uint64_t iommu_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - IOMMUState *s = opaque; - hwaddr saddr; - uint32_t ret; - - saddr = addr >> 2; - switch (saddr) { - default: - ret = s->regs[saddr]; - break; - case IOMMU_AFAR: - case IOMMU_AFSR: - ret = s->regs[saddr]; - qemu_irq_lower(s->irq); - break; - } - trace_sun4m_iommu_mem_readl(saddr, ret); - return ret; -} - -static void iommu_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - IOMMUState *s = opaque; - hwaddr saddr; - - saddr = addr >> 2; - trace_sun4m_iommu_mem_writel(saddr, val); - switch (saddr) { - case IOMMU_CTRL: - switch (val & IOMMU_CTRL_RNGE) { - case IOMMU_RNGE_16MB: - s->iostart = 0xffffffffff000000ULL; - break; - case IOMMU_RNGE_32MB: - s->iostart = 0xfffffffffe000000ULL; - break; - case IOMMU_RNGE_64MB: - s->iostart = 0xfffffffffc000000ULL; - break; - case IOMMU_RNGE_128MB: - s->iostart = 0xfffffffff8000000ULL; - break; - case IOMMU_RNGE_256MB: - s->iostart = 0xfffffffff0000000ULL; - break; - case IOMMU_RNGE_512MB: - s->iostart = 0xffffffffe0000000ULL; - break; - case IOMMU_RNGE_1GB: - s->iostart = 0xffffffffc0000000ULL; - break; - default: - case IOMMU_RNGE_2GB: - s->iostart = 0xffffffff80000000ULL; - break; - } - trace_sun4m_iommu_mem_writel_ctrl(s->iostart); - s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | s->version); - break; - case IOMMU_BASE: - s->regs[saddr] = val & IOMMU_BASE_MASK; - break; - case IOMMU_TLBFLUSH: - trace_sun4m_iommu_mem_writel_tlbflush(val); - s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK; - break; - case IOMMU_PGFLUSH: - trace_sun4m_iommu_mem_writel_pgflush(val); - s->regs[saddr] = val & IOMMU_PGFLUSH_MASK; - break; - case IOMMU_AFAR: - s->regs[saddr] = val; - qemu_irq_lower(s->irq); - break; - case IOMMU_AER: - s->regs[saddr] = (val & IOMMU_AER_MASK) | IOMMU_AER_EN_P0_ARB; - break; - case IOMMU_AFSR: - s->regs[saddr] = (val & IOMMU_AFSR_MASK) | IOMMU_AFSR_RESV; - qemu_irq_lower(s->irq); - break; - case IOMMU_SBCFG0: - case IOMMU_SBCFG1: - case IOMMU_SBCFG2: - case IOMMU_SBCFG3: - s->regs[saddr] = val & IOMMU_SBCFG_MASK; - break; - case IOMMU_ARBEN: - // XXX implement SBus probing: fault when reading unmapped - // addresses, fault cause and address stored to MMU/IOMMU - s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID; - break; - case IOMMU_MASK_ID: - s->regs[saddr] |= val & IOMMU_MASK_ID_MASK; - break; - default: - s->regs[saddr] = val; - break; - } -} - -static const MemoryRegionOps iommu_mem_ops = { - .read = iommu_mem_read, - .write = iommu_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint32_t iommu_page_get_flags(IOMMUState *s, hwaddr addr) -{ - uint32_t ret; - hwaddr iopte; - hwaddr pa = addr; - - iopte = s->regs[IOMMU_BASE] << 4; - addr &= ~s->iostart; - iopte += (addr >> (IOMMU_PAGE_SHIFT - 2)) & ~3; - ret = address_space_ldl_be(&address_space_memory, iopte, - MEMTXATTRS_UNSPECIFIED, NULL); - trace_sun4m_iommu_page_get_flags(pa, iopte, ret); - return ret; -} - -static hwaddr iommu_translate_pa(hwaddr addr, - uint32_t pte) -{ - hwaddr pa; - - pa = ((pte & IOPTE_PAGE) << 4) + (addr & ~IOMMU_PAGE_MASK); - trace_sun4m_iommu_translate_pa(addr, pa, pte); - return pa; -} - -static void iommu_bad_addr(IOMMUState *s, hwaddr addr, - int is_write) -{ - trace_sun4m_iommu_bad_addr(addr); - s->regs[IOMMU_AFSR] = IOMMU_AFSR_ERR | IOMMU_AFSR_LE | IOMMU_AFSR_RESV | - IOMMU_AFSR_FAV; - if (!is_write) - s->regs[IOMMU_AFSR] |= IOMMU_AFSR_RD; - s->regs[IOMMU_AFAR] = addr; - qemu_irq_raise(s->irq); -} - -void sparc_iommu_memory_rw(void *opaque, hwaddr addr, - uint8_t *buf, int len, int is_write) -{ - int l; - uint32_t flags; - hwaddr page, phys_addr; - - while (len > 0) { - page = addr & IOMMU_PAGE_MASK; - l = (page + IOMMU_PAGE_SIZE) - addr; - if (l > len) - l = len; - flags = iommu_page_get_flags(opaque, page); - if (!(flags & IOPTE_VALID)) { - iommu_bad_addr(opaque, page, is_write); - return; - } - phys_addr = iommu_translate_pa(addr, flags); - if (is_write) { - if (!(flags & IOPTE_WRITE)) { - iommu_bad_addr(opaque, page, is_write); - return; - } - cpu_physical_memory_write(phys_addr, buf, l); - } else { - cpu_physical_memory_read(phys_addr, buf, l); - } - len -= l; - buf += l; - addr += l; - } -} - -static const VMStateDescription vmstate_iommu = { - .name ="iommu", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, IOMMUState, IOMMU_NREGS), - VMSTATE_UINT64(iostart, IOMMUState), - VMSTATE_END_OF_LIST() - } -}; - -static void iommu_reset(DeviceState *d) -{ - IOMMUState *s = SUN4M_IOMMU(d); - - memset(s->regs, 0, IOMMU_NREGS * 4); - s->iostart = 0; - s->regs[IOMMU_CTRL] = s->version; - s->regs[IOMMU_ARBEN] = IOMMU_MID; - s->regs[IOMMU_AFSR] = IOMMU_AFSR_RESV; - s->regs[IOMMU_AER] = IOMMU_AER_EN_P0_ARB | IOMMU_AER_EN_P1_ARB; - s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK; -} - -static int iommu_init1(SysBusDevice *dev) -{ - IOMMUState *s = SUN4M_IOMMU(dev); - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &iommu_mem_ops, s, "iommu", - IOMMU_NREGS * sizeof(uint32_t)); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static Property iommu_properties[] = { - DEFINE_PROP_UINT32("version", IOMMUState, version, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void iommu_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = iommu_init1; - dc->reset = iommu_reset; - dc->vmsd = &vmstate_iommu; - dc->props = iommu_properties; -} - -static const TypeInfo iommu_info = { - .name = TYPE_SUN4M_IOMMU, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IOMMUState), - .class_init = iommu_class_init, -}; - -static void iommu_register_types(void) -{ - type_register_static(&iommu_info); -} - -type_init(iommu_register_types) diff --git a/qemu/hw/dma/xilinx_axidma.c b/qemu/hw/dma/xilinx_axidma.c deleted file mode 100644 index a4753e55a..000000000 --- a/qemu/hw/dma/xilinx_axidma.c +++ /dev/null @@ -1,666 +0,0 @@ -/* - * QEMU model of Xilinx AXI-DMA block. - * - * Copyright (c) 2011 Edgar E. Iglesias. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" - -#include "hw/stream.h" - -#define D(x) - -#define TYPE_XILINX_AXI_DMA "xlnx.axi-dma" -#define TYPE_XILINX_AXI_DMA_DATA_STREAM "xilinx-axi-dma-data-stream" -#define TYPE_XILINX_AXI_DMA_CONTROL_STREAM "xilinx-axi-dma-control-stream" - -#define XILINX_AXI_DMA(obj) \ - OBJECT_CHECK(XilinxAXIDMA, (obj), TYPE_XILINX_AXI_DMA) - -#define XILINX_AXI_DMA_DATA_STREAM(obj) \ - OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\ - TYPE_XILINX_AXI_DMA_DATA_STREAM) - -#define XILINX_AXI_DMA_CONTROL_STREAM(obj) \ - OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\ - TYPE_XILINX_AXI_DMA_CONTROL_STREAM) - -#define R_DMACR (0x00 / 4) -#define R_DMASR (0x04 / 4) -#define R_CURDESC (0x08 / 4) -#define R_TAILDESC (0x10 / 4) -#define R_MAX (0x30 / 4) - -#define CONTROL_PAYLOAD_WORDS 5 -#define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t))) - -typedef struct XilinxAXIDMA XilinxAXIDMA; -typedef struct XilinxAXIDMAStreamSlave XilinxAXIDMAStreamSlave; - -enum { - DMACR_RUNSTOP = 1, - DMACR_TAILPTR_MODE = 2, - DMACR_RESET = 4 -}; - -enum { - DMASR_HALTED = 1, - DMASR_IDLE = 2, - DMASR_IOC_IRQ = 1 << 12, - DMASR_DLY_IRQ = 1 << 13, - - DMASR_IRQ_MASK = 7 << 12 -}; - -struct SDesc { - uint64_t nxtdesc; - uint64_t buffer_address; - uint64_t reserved; - uint32_t control; - uint32_t status; - uint8_t app[CONTROL_PAYLOAD_SIZE]; -}; - -enum { - SDESC_CTRL_EOF = (1 << 26), - SDESC_CTRL_SOF = (1 << 27), - - SDESC_CTRL_LEN_MASK = (1 << 23) - 1 -}; - -enum { - SDESC_STATUS_EOF = (1 << 26), - SDESC_STATUS_SOF_BIT = 27, - SDESC_STATUS_SOF = (1 << SDESC_STATUS_SOF_BIT), - SDESC_STATUS_COMPLETE = (1 << 31) -}; - -struct Stream { - QEMUBH *bh; - ptimer_state *ptimer; - qemu_irq irq; - - int nr; - - struct SDesc desc; - int pos; - unsigned int complete_cnt; - uint32_t regs[R_MAX]; - uint8_t app[20]; -}; - -struct XilinxAXIDMAStreamSlave { - Object parent; - - struct XilinxAXIDMA *dma; -}; - -struct XilinxAXIDMA { - SysBusDevice busdev; - MemoryRegion iomem; - uint32_t freqhz; - StreamSlave *tx_data_dev; - StreamSlave *tx_control_dev; - XilinxAXIDMAStreamSlave rx_data_dev; - XilinxAXIDMAStreamSlave rx_control_dev; - - struct Stream streams[2]; - - StreamCanPushNotifyFn notify; - void *notify_opaque; -}; - -/* - * Helper calls to extract info from descriptors and other trivial - * state from regs. - */ -static inline int stream_desc_sof(struct SDesc *d) -{ - return d->control & SDESC_CTRL_SOF; -} - -static inline int stream_desc_eof(struct SDesc *d) -{ - return d->control & SDESC_CTRL_EOF; -} - -static inline int stream_resetting(struct Stream *s) -{ - return !!(s->regs[R_DMACR] & DMACR_RESET); -} - -static inline int stream_running(struct Stream *s) -{ - return s->regs[R_DMACR] & DMACR_RUNSTOP; -} - -static inline int stream_idle(struct Stream *s) -{ - return !!(s->regs[R_DMASR] & DMASR_IDLE); -} - -static void stream_reset(struct Stream *s) -{ - s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */ - s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold. */ -} - -/* Map an offset addr into a channel index. */ -static inline int streamid_from_addr(hwaddr addr) -{ - int sid; - - sid = addr / (0x30); - sid &= 1; - return sid; -} - -static void stream_desc_load(struct Stream *s, hwaddr addr) -{ - struct SDesc *d = &s->desc; - - cpu_physical_memory_read(addr, d, sizeof *d); - - /* Convert from LE into host endianness. */ - d->buffer_address = le64_to_cpu(d->buffer_address); - d->nxtdesc = le64_to_cpu(d->nxtdesc); - d->control = le32_to_cpu(d->control); - d->status = le32_to_cpu(d->status); -} - -static void stream_desc_store(struct Stream *s, hwaddr addr) -{ - struct SDesc *d = &s->desc; - - /* Convert from host endianness into LE. */ - d->buffer_address = cpu_to_le64(d->buffer_address); - d->nxtdesc = cpu_to_le64(d->nxtdesc); - d->control = cpu_to_le32(d->control); - d->status = cpu_to_le32(d->status); - cpu_physical_memory_write(addr, d, sizeof *d); -} - -static void stream_update_irq(struct Stream *s) -{ - unsigned int pending, mask, irq; - - pending = s->regs[R_DMASR] & DMASR_IRQ_MASK; - mask = s->regs[R_DMACR] & DMASR_IRQ_MASK; - - irq = pending & mask; - - qemu_set_irq(s->irq, !!irq); -} - -static void stream_reload_complete_cnt(struct Stream *s) -{ - unsigned int comp_th; - comp_th = (s->regs[R_DMACR] >> 16) & 0xff; - s->complete_cnt = comp_th; -} - -static void timer_hit(void *opaque) -{ - struct Stream *s = opaque; - - stream_reload_complete_cnt(s); - s->regs[R_DMASR] |= DMASR_DLY_IRQ; - stream_update_irq(s); -} - -static void stream_complete(struct Stream *s) -{ - unsigned int comp_delay; - - /* Start the delayed timer. */ - comp_delay = s->regs[R_DMACR] >> 24; - if (comp_delay) { - ptimer_stop(s->ptimer); - ptimer_set_count(s->ptimer, comp_delay); - ptimer_run(s->ptimer, 1); - } - - s->complete_cnt--; - if (s->complete_cnt == 0) { - /* Raise the IOC irq. */ - s->regs[R_DMASR] |= DMASR_IOC_IRQ; - stream_reload_complete_cnt(s); - } -} - -static void stream_process_mem2s(struct Stream *s, StreamSlave *tx_data_dev, - StreamSlave *tx_control_dev) -{ - uint32_t prev_d; - unsigned char txbuf[16 * 1024]; - unsigned int txlen; - - if (!stream_running(s) || stream_idle(s)) { - return; - } - - while (1) { - stream_desc_load(s, s->regs[R_CURDESC]); - - if (s->desc.status & SDESC_STATUS_COMPLETE) { - s->regs[R_DMASR] |= DMASR_HALTED; - break; - } - - if (stream_desc_sof(&s->desc)) { - s->pos = 0; - stream_push(tx_control_dev, s->desc.app, sizeof(s->desc.app)); - } - - txlen = s->desc.control & SDESC_CTRL_LEN_MASK; - if ((txlen + s->pos) > sizeof txbuf) { - hw_error("%s: too small internal txbuf! %d\n", __func__, - txlen + s->pos); - } - - cpu_physical_memory_read(s->desc.buffer_address, - txbuf + s->pos, txlen); - s->pos += txlen; - - if (stream_desc_eof(&s->desc)) { - stream_push(tx_data_dev, txbuf, s->pos); - s->pos = 0; - stream_complete(s); - } - - /* Update the descriptor. */ - s->desc.status = txlen | SDESC_STATUS_COMPLETE; - stream_desc_store(s, s->regs[R_CURDESC]); - - /* Advance. */ - prev_d = s->regs[R_CURDESC]; - s->regs[R_CURDESC] = s->desc.nxtdesc; - if (prev_d == s->regs[R_TAILDESC]) { - s->regs[R_DMASR] |= DMASR_IDLE; - break; - } - } -} - -static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf, - size_t len) -{ - uint32_t prev_d; - unsigned int rxlen; - size_t pos = 0; - int sof = 1; - - if (!stream_running(s) || stream_idle(s)) { - return 0; - } - - while (len) { - stream_desc_load(s, s->regs[R_CURDESC]); - - if (s->desc.status & SDESC_STATUS_COMPLETE) { - s->regs[R_DMASR] |= DMASR_HALTED; - break; - } - - rxlen = s->desc.control & SDESC_CTRL_LEN_MASK; - if (rxlen > len) { - /* It fits. */ - rxlen = len; - } - - cpu_physical_memory_write(s->desc.buffer_address, buf + pos, rxlen); - len -= rxlen; - pos += rxlen; - - /* Update the descriptor. */ - if (!len) { - stream_complete(s); - memcpy(s->desc.app, s->app, sizeof(s->desc.app)); - s->desc.status |= SDESC_STATUS_EOF; - } - - s->desc.status |= sof << SDESC_STATUS_SOF_BIT; - s->desc.status |= SDESC_STATUS_COMPLETE; - stream_desc_store(s, s->regs[R_CURDESC]); - sof = 0; - - /* Advance. */ - prev_d = s->regs[R_CURDESC]; - s->regs[R_CURDESC] = s->desc.nxtdesc; - if (prev_d == s->regs[R_TAILDESC]) { - s->regs[R_DMASR] |= DMASR_IDLE; - break; - } - } - - return pos; -} - -static void xilinx_axidma_reset(DeviceState *dev) -{ - int i; - XilinxAXIDMA *s = XILINX_AXI_DMA(dev); - - for (i = 0; i < 2; i++) { - stream_reset(&s->streams[i]); - } -} - -static size_t -xilinx_axidma_control_stream_push(StreamSlave *obj, unsigned char *buf, - size_t len) -{ - XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM(obj); - struct Stream *s = &cs->dma->streams[1]; - - if (len != CONTROL_PAYLOAD_SIZE) { - hw_error("AXI DMA requires %d byte control stream payload\n", - (int)CONTROL_PAYLOAD_SIZE); - } - - memcpy(s->app, buf, len); - return len; -} - -static bool -xilinx_axidma_data_stream_can_push(StreamSlave *obj, - StreamCanPushNotifyFn notify, - void *notify_opaque) -{ - XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj); - struct Stream *s = &ds->dma->streams[1]; - - if (!stream_running(s) || stream_idle(s)) { - ds->dma->notify = notify; - ds->dma->notify_opaque = notify_opaque; - return false; - } - - return true; -} - -static size_t -xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len) -{ - XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj); - struct Stream *s = &ds->dma->streams[1]; - size_t ret; - - ret = stream_process_s2mem(s, buf, len); - stream_update_irq(s); - return ret; -} - -static uint64_t axidma_read(void *opaque, hwaddr addr, - unsigned size) -{ - XilinxAXIDMA *d = opaque; - struct Stream *s; - uint32_t r = 0; - int sid; - - sid = streamid_from_addr(addr); - s = &d->streams[sid]; - - addr = addr % 0x30; - addr >>= 2; - switch (addr) { - case R_DMACR: - /* Simulate one cycles reset delay. */ - s->regs[addr] &= ~DMACR_RESET; - r = s->regs[addr]; - break; - case R_DMASR: - s->regs[addr] &= 0xffff; - s->regs[addr] |= (s->complete_cnt & 0xff) << 16; - s->regs[addr] |= (ptimer_get_count(s->ptimer) & 0xff) << 24; - r = s->regs[addr]; - break; - default: - r = s->regs[addr]; - D(qemu_log("%s ch=%d addr=" TARGET_FMT_plx " v=%x\n", - __func__, sid, addr * 4, r)); - break; - } - return r; - -} - -static void axidma_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - XilinxAXIDMA *d = opaque; - struct Stream *s; - int sid; - - sid = streamid_from_addr(addr); - s = &d->streams[sid]; - - addr = addr % 0x30; - addr >>= 2; - switch (addr) { - case R_DMACR: - /* Tailptr mode is always on. */ - value |= DMACR_TAILPTR_MODE; - /* Remember our previous reset state. */ - value |= (s->regs[addr] & DMACR_RESET); - s->regs[addr] = value; - - if (value & DMACR_RESET) { - stream_reset(s); - } - - if ((value & 1) && !stream_resetting(s)) { - /* Start processing. */ - s->regs[R_DMASR] &= ~(DMASR_HALTED | DMASR_IDLE); - } - stream_reload_complete_cnt(s); - break; - - case R_DMASR: - /* Mask away write to clear irq lines. */ - value &= ~(value & DMASR_IRQ_MASK); - s->regs[addr] = value; - break; - - case R_TAILDESC: - s->regs[addr] = value; - s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle. */ - if (!sid) { - stream_process_mem2s(s, d->tx_data_dev, d->tx_control_dev); - } - break; - default: - D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n", - __func__, sid, addr * 4, (unsigned)value)); - s->regs[addr] = value; - break; - } - if (sid == 1 && d->notify) { - StreamCanPushNotifyFn notifytmp = d->notify; - d->notify = NULL; - notifytmp(d->notify_opaque); - } - stream_update_irq(s); -} - -static const MemoryRegionOps axidma_ops = { - .read = axidma_read, - .write = axidma_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void xilinx_axidma_realize(DeviceState *dev, Error **errp) -{ - XilinxAXIDMA *s = XILINX_AXI_DMA(dev); - XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(&s->rx_data_dev); - XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM( - &s->rx_control_dev); - Error *local_err = NULL; - - object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA, - (Object **)&ds->dma, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &local_err); - object_property_add_link(OBJECT(cs), "dma", TYPE_XILINX_AXI_DMA, - (Object **)&cs->dma, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &local_err); - if (local_err) { - goto xilinx_axidma_realize_fail; - } - object_property_set_link(OBJECT(ds), OBJECT(s), "dma", &local_err); - object_property_set_link(OBJECT(cs), OBJECT(s), "dma", &local_err); - if (local_err) { - goto xilinx_axidma_realize_fail; - } - - int i; - - for (i = 0; i < 2; i++) { - struct Stream *st = &s->streams[i]; - - st->nr = i; - st->bh = qemu_bh_new(timer_hit, st); - st->ptimer = ptimer_init(st->bh); - ptimer_set_freq(st->ptimer, s->freqhz); - } - return; - -xilinx_axidma_realize_fail: - if (!*errp) { - *errp = local_err; - } -} - -static void xilinx_axidma_init(Object *obj) -{ - XilinxAXIDMA *s = XILINX_AXI_DMA(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, - (Object **)&s->tx_data_dev, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - object_property_add_link(obj, "axistream-control-connected", - TYPE_STREAM_SLAVE, - (Object **)&s->tx_control_dev, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - - object_initialize(&s->rx_data_dev, sizeof(s->rx_data_dev), - TYPE_XILINX_AXI_DMA_DATA_STREAM); - object_initialize(&s->rx_control_dev, sizeof(s->rx_control_dev), - TYPE_XILINX_AXI_DMA_CONTROL_STREAM); - object_property_add_child(OBJECT(s), "axistream-connected-target", - (Object *)&s->rx_data_dev, &error_abort); - object_property_add_child(OBJECT(s), "axistream-control-connected-target", - (Object *)&s->rx_control_dev, &error_abort); - - sysbus_init_irq(sbd, &s->streams[0].irq); - sysbus_init_irq(sbd, &s->streams[1].irq); - - memory_region_init_io(&s->iomem, obj, &axidma_ops, s, - "xlnx.axi-dma", R_MAX * 4 * 2); - sysbus_init_mmio(sbd, &s->iomem); -} - -static Property axidma_properties[] = { - DEFINE_PROP_UINT32("freqhz", XilinxAXIDMA, freqhz, 50000000), - DEFINE_PROP_END_OF_LIST(), -}; - -static void axidma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = xilinx_axidma_realize, - dc->reset = xilinx_axidma_reset; - dc->props = axidma_properties; -} - -static StreamSlaveClass xilinx_axidma_data_stream_class = { - .push = xilinx_axidma_data_stream_push, - .can_push = xilinx_axidma_data_stream_can_push, -}; - -static StreamSlaveClass xilinx_axidma_control_stream_class = { - .push = xilinx_axidma_control_stream_push, -}; - -static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data) -{ - StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); - - ssc->push = ((StreamSlaveClass *)data)->push; - ssc->can_push = ((StreamSlaveClass *)data)->can_push; -} - -static const TypeInfo axidma_info = { - .name = TYPE_XILINX_AXI_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XilinxAXIDMA), - .class_init = axidma_class_init, - .instance_init = xilinx_axidma_init, -}; - -static const TypeInfo xilinx_axidma_data_stream_info = { - .name = TYPE_XILINX_AXI_DMA_DATA_STREAM, - .parent = TYPE_OBJECT, - .instance_size = sizeof(struct XilinxAXIDMAStreamSlave), - .class_init = xilinx_axidma_stream_class_init, - .class_data = &xilinx_axidma_data_stream_class, - .interfaces = (InterfaceInfo[]) { - { TYPE_STREAM_SLAVE }, - { } - } -}; - -static const TypeInfo xilinx_axidma_control_stream_info = { - .name = TYPE_XILINX_AXI_DMA_CONTROL_STREAM, - .parent = TYPE_OBJECT, - .instance_size = sizeof(struct XilinxAXIDMAStreamSlave), - .class_init = xilinx_axidma_stream_class_init, - .class_data = &xilinx_axidma_control_stream_class, - .interfaces = (InterfaceInfo[]) { - { TYPE_STREAM_SLAVE }, - { } - } -}; - -static void xilinx_axidma_register_types(void) -{ - type_register_static(&axidma_info); - type_register_static(&xilinx_axidma_data_stream_info); - type_register_static(&xilinx_axidma_control_stream_info); -} - -type_init(xilinx_axidma_register_types) |