Browse Source
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2143 c046a42c-6fe2-441c-8c8c-71466251a162stable-0.10
6 changed files with 338 additions and 173 deletions
@ -0,0 +1,249 @@ |
|||
/*
|
|||
* QEMU Sparc32 DMA controller emulation |
|||
* |
|||
* Copyright (c) 2006 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 "vl.h" |
|||
|
|||
/* debug DMA */ |
|||
//#define DEBUG_DMA
|
|||
|
|||
/*
|
|||
* 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
|
|||
*/ |
|||
|
|||
#ifdef DEBUG_DMA |
|||
#define DPRINTF(fmt, args...) \ |
|||
do { printf("DMA: " fmt , ##args); } while (0) |
|||
#define pic_set_irq_new(ctl, irq, level) \ |
|||
do { printf("DMA: set_irq(%d): %d\n", (irq), (level)); \ |
|||
pic_set_irq_new((ctl), (irq),(level));} while (0) |
|||
#else |
|||
#define DPRINTF(fmt, args...) |
|||
#endif |
|||
|
|||
#define DMA_REGS 8 |
|||
#define DMA_MAXADDR (DMA_REGS * 4 - 1) |
|||
|
|||
#define DMA_VER 0xa0000000 |
|||
#define DMA_INTR 1 |
|||
#define DMA_INTREN 0x10 |
|||
#define DMA_WRITE_MEM 0x100 |
|||
#define DMA_LOADED 0x04000000 |
|||
#define DMA_RESET 0x80 |
|||
|
|||
typedef struct DMAState DMAState; |
|||
|
|||
struct DMAState { |
|||
uint32_t dmaregs[DMA_REGS]; |
|||
int espirq, leirq; |
|||
void *iommu, *esp_opaque, *lance_opaque, *intctl; |
|||
}; |
|||
|
|||
void ledma_set_irq(void *opaque, int isr) |
|||
{ |
|||
DMAState *s = opaque; |
|||
|
|||
pic_set_irq_new(s->intctl, s->leirq, isr); |
|||
} |
|||
|
|||
void ledma_memory_read(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len) |
|||
{ |
|||
DMAState *s = opaque; |
|||
|
|||
DPRINTF("DMA write, direction: %c, addr 0x%8.8x\n", |
|||
s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]); |
|||
sparc_iommu_memory_read(s->iommu, addr | s->dmaregs[7], buf, len); |
|||
} |
|||
|
|||
void ledma_memory_write(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len) |
|||
{ |
|||
DMAState *s = opaque; |
|||
|
|||
DPRINTF("DMA read, direction: %c, addr 0x%8.8x\n", |
|||
s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]); |
|||
sparc_iommu_memory_write(s->iommu, addr | s->dmaregs[7], buf, len); |
|||
} |
|||
|
|||
void espdma_raise_irq(void *opaque) |
|||
{ |
|||
DMAState *s = opaque; |
|||
|
|||
s->dmaregs[0] |= DMA_INTR; |
|||
pic_set_irq_new(s->intctl, s->espirq, 1); |
|||
} |
|||
|
|||
void espdma_clear_irq(void *opaque) |
|||
{ |
|||
DMAState *s = opaque; |
|||
|
|||
s->dmaregs[0] &= ~DMA_INTR; |
|||
pic_set_irq_new(s->intctl, s->espirq, 0); |
|||
} |
|||
|
|||
void espdma_memory_read(void *opaque, uint8_t *buf, int len) |
|||
{ |
|||
DMAState *s = opaque; |
|||
|
|||
DPRINTF("DMA read, direction: %c, addr 0x%8.8x\n", |
|||
s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]); |
|||
sparc_iommu_memory_read(s->iommu, s->dmaregs[1], buf, len); |
|||
s->dmaregs[0] |= DMA_INTR; |
|||
s->dmaregs[1] += len; |
|||
} |
|||
|
|||
void espdma_memory_write(void *opaque, uint8_t *buf, int len) |
|||
{ |
|||
DMAState *s = opaque; |
|||
|
|||
DPRINTF("DMA write, direction: %c, addr 0x%8.8x\n", |
|||
s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]); |
|||
sparc_iommu_memory_write(s->iommu, s->dmaregs[1], buf, len); |
|||
s->dmaregs[0] |= DMA_INTR; |
|||
s->dmaregs[1] += len; |
|||
} |
|||
|
|||
static uint32_t dma_mem_readl(void *opaque, target_phys_addr_t addr) |
|||
{ |
|||
DMAState *s = opaque; |
|||
uint32_t saddr; |
|||
|
|||
saddr = (addr & DMA_MAXADDR) >> 2; |
|||
DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->dmaregs[saddr]); |
|||
|
|||
return s->dmaregs[saddr]; |
|||
} |
|||
|
|||
static void dma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) |
|||
{ |
|||
DMAState *s = opaque; |
|||
uint32_t saddr; |
|||
|
|||
saddr = (addr & DMA_MAXADDR) >> 2; |
|||
DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->dmaregs[saddr], val); |
|||
switch (saddr) { |
|||
case 0: |
|||
if (!(val & DMA_INTREN)) |
|||
pic_set_irq_new(s->intctl, s->espirq, 0); |
|||
if (val & DMA_RESET) { |
|||
esp_reset(s->esp_opaque); |
|||
} else if (val & 0x40) { |
|||
val &= ~0x40; |
|||
} else if (val == 0) |
|||
val = 0x40; |
|||
val &= 0x0fffffff; |
|||
val |= DMA_VER; |
|||
break; |
|||
case 1: |
|||
s->dmaregs[0] |= DMA_LOADED; |
|||
break; |
|||
case 4: |
|||
if (!(val & DMA_INTREN)) |
|||
pic_set_irq_new(s->intctl, s->leirq, 0); |
|||
if (val & DMA_RESET) |
|||
pcnet_h_reset(s->lance_opaque); |
|||
val &= 0x0fffffff; |
|||
val |= DMA_VER; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
s->dmaregs[saddr] = val; |
|||
} |
|||
|
|||
static CPUReadMemoryFunc *dma_mem_read[3] = { |
|||
dma_mem_readl, |
|||
dma_mem_readl, |
|||
dma_mem_readl, |
|||
}; |
|||
|
|||
static CPUWriteMemoryFunc *dma_mem_write[3] = { |
|||
dma_mem_writel, |
|||
dma_mem_writel, |
|||
dma_mem_writel, |
|||
}; |
|||
|
|||
static void dma_reset(void *opaque) |
|||
{ |
|||
DMAState *s = opaque; |
|||
|
|||
memset(s->dmaregs, 0, DMA_REGS * 4); |
|||
s->dmaregs[0] = DMA_VER; |
|||
s->dmaregs[4] = DMA_VER; |
|||
} |
|||
|
|||
static void dma_save(QEMUFile *f, void *opaque) |
|||
{ |
|||
DMAState *s = opaque; |
|||
unsigned int i; |
|||
|
|||
for (i = 0; i < DMA_REGS; i++) |
|||
qemu_put_be32s(f, &s->dmaregs[i]); |
|||
} |
|||
|
|||
static int dma_load(QEMUFile *f, void *opaque, int version_id) |
|||
{ |
|||
DMAState *s = opaque; |
|||
unsigned int i; |
|||
|
|||
if (version_id != 1) |
|||
return -EINVAL; |
|||
for (i = 0; i < DMA_REGS; i++) |
|||
qemu_get_be32s(f, &s->dmaregs[i]); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
void *sparc32_dma_init(uint32_t daddr, int espirq, int leirq, void *iommu, void *intctl) |
|||
{ |
|||
DMAState *s; |
|||
int dma_io_memory; |
|||
|
|||
s = qemu_mallocz(sizeof(DMAState)); |
|||
if (!s) |
|||
return NULL; |
|||
|
|||
s->espirq = espirq; |
|||
s->leirq = leirq; |
|||
s->iommu = iommu; |
|||
s->intctl = intctl; |
|||
|
|||
dma_io_memory = cpu_register_io_memory(0, dma_mem_read, dma_mem_write, s); |
|||
cpu_register_physical_memory(daddr, 16 * 2, dma_io_memory); |
|||
|
|||
register_savevm("sparc32_dma", daddr, 1, dma_save, dma_load, s); |
|||
qemu_register_reset(dma_reset, s); |
|||
|
|||
return s; |
|||
} |
|||
|
|||
void sparc32_dma_set_reset_data(void *opaque, void *esp_opaque, |
|||
void *lance_opaque) |
|||
{ |
|||
DMAState *s = opaque; |
|||
|
|||
s->esp_opaque = esp_opaque; |
|||
s->lance_opaque = lance_opaque; |
|||
} |
|||
Loading…
Reference in new issue