|
@@ -13,6 +13,7 @@
|
|
#include "qemu/cutils.h"
|
|
#include "qemu/cutils.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/error.h"
|
|
|
|
+#include "qemu/log.h"
|
|
#include "cpu.h"
|
|
#include "cpu.h"
|
|
#include "hw/hw.h"
|
|
#include "hw/hw.h"
|
|
#include "exec/address-spaces.h"
|
|
#include "exec/address-spaces.h"
|
|
@@ -802,6 +803,227 @@ void ppc4xx_ahb_init(CPUPPCState *env)
|
|
qemu_register_reset(ppc4xx_ahb_reset, ahb);
|
|
qemu_register_reset(ppc4xx_ahb_reset, ahb);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*****************************************************************************/
|
|
|
|
+/* DMA controller */
|
|
|
|
+
|
|
|
|
+#define DMA0_CR_CE (1 << 31)
|
|
|
|
+#define DMA0_CR_PW (1 << 26 | 1 << 25)
|
|
|
|
+#define DMA0_CR_DAI (1 << 24)
|
|
|
|
+#define DMA0_CR_SAI (1 << 23)
|
|
|
|
+#define DMA0_CR_DEC (1 << 2)
|
|
|
|
+
|
|
|
|
+enum {
|
|
|
|
+ DMA0_CR = 0x00,
|
|
|
|
+ DMA0_CT,
|
|
|
|
+ DMA0_SAH,
|
|
|
|
+ DMA0_SAL,
|
|
|
|
+ DMA0_DAH,
|
|
|
|
+ DMA0_DAL,
|
|
|
|
+ DMA0_SGH,
|
|
|
|
+ DMA0_SGL,
|
|
|
|
+
|
|
|
|
+ DMA0_SR = 0x20,
|
|
|
|
+ DMA0_SGC = 0x23,
|
|
|
|
+ DMA0_SLP = 0x25,
|
|
|
|
+ DMA0_POL = 0x26,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+typedef struct {
|
|
|
|
+ uint32_t cr;
|
|
|
|
+ uint32_t ct;
|
|
|
|
+ uint64_t sa;
|
|
|
|
+ uint64_t da;
|
|
|
|
+ uint64_t sg;
|
|
|
|
+} PPC4xxDmaChnl;
|
|
|
|
+
|
|
|
|
+typedef struct {
|
|
|
|
+ int base;
|
|
|
|
+ PPC4xxDmaChnl ch[4];
|
|
|
|
+ uint32_t sr;
|
|
|
|
+} PPC4xxDmaState;
|
|
|
|
+
|
|
|
|
+static uint32_t dcr_read_dma(void *opaque, int dcrn)
|
|
|
|
+{
|
|
|
|
+ PPC4xxDmaState *dma = opaque;
|
|
|
|
+ uint32_t val = 0;
|
|
|
|
+ int addr = dcrn - dma->base;
|
|
|
|
+ int chnl = addr / 8;
|
|
|
|
+
|
|
|
|
+ switch (addr) {
|
|
|
|
+ case 0x00 ... 0x1f:
|
|
|
|
+ switch (addr % 8) {
|
|
|
|
+ case DMA0_CR:
|
|
|
|
+ val = dma->ch[chnl].cr;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_CT:
|
|
|
|
+ val = dma->ch[chnl].ct;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SAH:
|
|
|
|
+ val = dma->ch[chnl].sa >> 32;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SAL:
|
|
|
|
+ val = dma->ch[chnl].sa;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_DAH:
|
|
|
|
+ val = dma->ch[chnl].da >> 32;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_DAL:
|
|
|
|
+ val = dma->ch[chnl].da;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SGH:
|
|
|
|
+ val = dma->ch[chnl].sg >> 32;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SGL:
|
|
|
|
+ val = dma->ch[chnl].sg;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SR:
|
|
|
|
+ val = dma->sr;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ qemu_log_mask(LOG_UNIMP, "%s: unimplemented register %x (%d, %x)\n",
|
|
|
|
+ __func__, dcrn, chnl, addr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return val;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void dcr_write_dma(void *opaque, int dcrn, uint32_t val)
|
|
|
|
+{
|
|
|
|
+ PPC4xxDmaState *dma = opaque;
|
|
|
|
+ int addr = dcrn - dma->base;
|
|
|
|
+ int chnl = addr / 8;
|
|
|
|
+
|
|
|
|
+ switch (addr) {
|
|
|
|
+ case 0x00 ... 0x1f:
|
|
|
|
+ switch (addr % 8) {
|
|
|
|
+ case DMA0_CR:
|
|
|
|
+ dma->ch[chnl].cr = val;
|
|
|
|
+ if (val & DMA0_CR_CE) {
|
|
|
|
+ int count = dma->ch[chnl].ct & 0xffff;
|
|
|
|
+
|
|
|
|
+ if (count) {
|
|
|
|
+ int width, i, sidx, didx;
|
|
|
|
+ uint8_t *rptr, *wptr;
|
|
|
|
+ hwaddr rlen, wlen;
|
|
|
|
+
|
|
|
|
+ sidx = didx = 0;
|
|
|
|
+ width = 1 << ((val & DMA0_CR_PW) >> 25);
|
|
|
|
+ rptr = cpu_physical_memory_map(dma->ch[chnl].sa, &rlen, 0);
|
|
|
|
+ wptr = cpu_physical_memory_map(dma->ch[chnl].da, &wlen, 1);
|
|
|
|
+ if (rptr && wptr) {
|
|
|
|
+ if (!(val & DMA0_CR_DEC) &&
|
|
|
|
+ val & DMA0_CR_SAI && val & DMA0_CR_DAI) {
|
|
|
|
+ /* optimise common case */
|
|
|
|
+ memmove(wptr, rptr, count * width);
|
|
|
|
+ sidx = didx = count * width;
|
|
|
|
+ } else {
|
|
|
|
+ /* do it the slow way */
|
|
|
|
+ for (sidx = didx = i = 0; i < count; i++) {
|
|
|
|
+ uint64_t v = ldn_le_p(rptr + sidx, width);
|
|
|
|
+ stn_le_p(wptr + didx, width, v);
|
|
|
|
+ if (val & DMA0_CR_SAI) {
|
|
|
|
+ sidx += width;
|
|
|
|
+ }
|
|
|
|
+ if (val & DMA0_CR_DAI) {
|
|
|
|
+ didx += width;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (wptr) {
|
|
|
|
+ cpu_physical_memory_unmap(wptr, wlen, 1, didx);
|
|
|
|
+ }
|
|
|
|
+ if (wptr) {
|
|
|
|
+ cpu_physical_memory_unmap(rptr, rlen, 0, sidx);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_CT:
|
|
|
|
+ dma->ch[chnl].ct = val;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SAH:
|
|
|
|
+ dma->ch[chnl].sa &= 0xffffffffULL;
|
|
|
|
+ dma->ch[chnl].sa |= (uint64_t)val << 32;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SAL:
|
|
|
|
+ dma->ch[chnl].sa &= 0xffffffff00000000ULL;
|
|
|
|
+ dma->ch[chnl].sa |= val;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_DAH:
|
|
|
|
+ dma->ch[chnl].da &= 0xffffffffULL;
|
|
|
|
+ dma->ch[chnl].da |= (uint64_t)val << 32;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_DAL:
|
|
|
|
+ dma->ch[chnl].da &= 0xffffffff00000000ULL;
|
|
|
|
+ dma->ch[chnl].da |= val;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SGH:
|
|
|
|
+ dma->ch[chnl].sg &= 0xffffffffULL;
|
|
|
|
+ dma->ch[chnl].sg |= (uint64_t)val << 32;
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SGL:
|
|
|
|
+ dma->ch[chnl].sg &= 0xffffffff00000000ULL;
|
|
|
|
+ dma->ch[chnl].sg |= val;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case DMA0_SR:
|
|
|
|
+ dma->sr &= ~val;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ qemu_log_mask(LOG_UNIMP, "%s: unimplemented register %x (%d, %x)\n",
|
|
|
|
+ __func__, dcrn, chnl, addr);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void ppc4xx_dma_reset(void *opaque)
|
|
|
|
+{
|
|
|
|
+ PPC4xxDmaState *dma = opaque;
|
|
|
|
+ int dma_base = dma->base;
|
|
|
|
+
|
|
|
|
+ memset(dma, 0, sizeof(*dma));
|
|
|
|
+ dma->base = dma_base;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void ppc4xx_dma_init(CPUPPCState *env, int dcr_base)
|
|
|
|
+{
|
|
|
|
+ PPC4xxDmaState *dma;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ dma = g_malloc0(sizeof(*dma));
|
|
|
|
+ dma->base = dcr_base;
|
|
|
|
+ qemu_register_reset(&ppc4xx_dma_reset, dma);
|
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
|
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_CR,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_CT,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SAH,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SAL,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_DAH,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_DAL,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SGH,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SGL,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ }
|
|
|
|
+ ppc_dcr_register(env, dcr_base + DMA0_SR,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + DMA0_SGC,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + DMA0_SLP,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+ ppc_dcr_register(env, dcr_base + DMA0_POL,
|
|
|
|
+ dma, &dcr_read_dma, &dcr_write_dma);
|
|
|
|
+}
|
|
|
|
+
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
/* PCI Express controller */
|
|
/* PCI Express controller */
|
|
/* FIXME: This is not complete and does not work, only implemented partially
|
|
/* FIXME: This is not complete and does not work, only implemented partially
|