ESP: fix ESP DMA access when DMA is not enabled

Sending ESP a command caused it to trigger DMA immediately
even if DMA was not enabled at the DMA controller.

Add a signal from DMA controller to ESP to tell ESP about changes in
DMA enable bit. Also use the correct function for setting up GPIO outputs.

This fixes NetBSD 1.6.1 through 3.0 boot.

Thanks to Artyom Tarasenko for extensive debugging of the problem.

Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
Blue Swirl 2010-09-11 16:38:33 +00:00
parent 24e0e38b83
commit 73d7434279
5 changed files with 88 additions and 18 deletions

View file

@ -80,6 +80,8 @@ struct ESPState {
ESPDMAMemoryReadWriteFunc dma_memory_read;
ESPDMAMemoryReadWriteFunc dma_memory_write;
void *dma_opaque;
int dma_enabled;
void (*dma_cb)(ESPState *s);
};
#define ESP_TCLO 0x0
@ -167,6 +169,24 @@ static void esp_lower_irq(ESPState *s)
}
}
static void esp_dma_enable(void *opaque, int irq, int level)
{
DeviceState *d = opaque;
ESPState *s = container_of(d, ESPState, busdev.qdev);
if (level) {
s->dma_enabled = 1;
DPRINTF("Raise enable\n");
if (s->dma_cb) {
s->dma_cb(s);
s->dma_cb = NULL;
}
} else {
DPRINTF("Lower enable\n");
s->dma_enabled = 0;
}
}
static uint32_t get_cmd(ESPState *s, uint8_t *buf)
{
uint32_t dmalen;
@ -243,6 +263,10 @@ static void handle_satn(ESPState *s)
uint8_t buf[32];
int len;
if (!s->dma_enabled) {
s->dma_cb = handle_satn;
return;
}
len = get_cmd(s, buf);
if (len)
do_cmd(s, buf);
@ -253,6 +277,10 @@ static void handle_s_without_atn(ESPState *s)
uint8_t buf[32];
int len;
if (!s->dma_enabled) {
s->dma_cb = handle_s_without_atn;
return;
}
len = get_cmd(s, buf);
if (len) {
do_busid_cmd(s, buf, 0);
@ -261,6 +289,10 @@ static void handle_s_without_atn(ESPState *s)
static void handle_satn_stop(ESPState *s)
{
if (!s->dma_enabled) {
s->dma_cb = handle_satn_stop;
return;
}
s->cmdlen = get_cmd(s, s->cmdbuf);
if (s->cmdlen) {
DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen);
@ -431,6 +463,7 @@ static void esp_hard_reset(DeviceState *d)
s->ti_wptr = 0;
s->dma = 0;
s->do_cmd = 0;
s->dma_cb = NULL;
s->rregs[ESP_CFG1] = 7;
}
@ -450,6 +483,18 @@ static void parent_esp_reset(void *opaque, int irq, int level)
}
}
static void esp_gpio_demux(void *opaque, int irq, int level)
{
switch (irq) {
case 0:
parent_esp_reset(opaque, irq, level);
break;
case 1:
esp_dma_enable(opaque, irq, level);
break;
}
}
static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
{
ESPState *s = opaque;
@ -646,7 +691,8 @@ static const VMStateDescription vmstate_esp = {
void esp_init(target_phys_addr_t espaddr, int it_shift,
ESPDMAMemoryReadWriteFunc dma_memory_read,
ESPDMAMemoryReadWriteFunc dma_memory_write,
void *dma_opaque, qemu_irq irq, qemu_irq *reset)
void *dma_opaque, qemu_irq irq, qemu_irq *reset,
qemu_irq *dma_enable)
{
DeviceState *dev;
SysBusDevice *s;
@ -658,11 +704,14 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
esp->dma_memory_write = dma_memory_write;
esp->dma_opaque = dma_opaque;
esp->it_shift = it_shift;
/* XXX for now until rc4030 has been changed to use DMA enable signal */
esp->dma_enabled = 1;
qdev_init_nofail(dev);
s = sysbus_from_qdev(dev);
sysbus_connect_irq(s, 0, irq);
sysbus_mmio_map(s, 0, espaddr);
*reset = qdev_get_gpio_in(dev, 0);
*dma_enable = qdev_get_gpio_in(dev, 1);
}
static int esp_init1(SysBusDevice *dev)
@ -676,7 +725,7 @@ static int esp_init1(SysBusDevice *dev)
esp_io_memory = cpu_register_io_memory(esp_mem_read, esp_mem_write, s);
sysbus_init_mmio(dev, ESP_REGS << s->it_shift, esp_io_memory);
qdev_init_gpio_in(&dev->qdev, parent_esp_reset, 1);
qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2);
scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete);
return scsi_bus_legacy_handle_cmdline(&s->bus);

View file

@ -7,6 +7,7 @@ typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len);
void esp_init(target_phys_addr_t espaddr, int it_shift,
ESPDMAMemoryReadWriteFunc dma_memory_read,
ESPDMAMemoryReadWriteFunc dma_memory_write,
void *dma_opaque, qemu_irq irq, qemu_irq *reset);
void *dma_opaque, qemu_irq irq, qemu_irq *reset,
qemu_irq *dma_enable);
#endif

View file

@ -137,7 +137,7 @@ void mips_jazz_init (ram_addr_t ram_size,
NICInfo *nd;
PITState *pit;
DriveInfo *fds[MAX_FD];
qemu_irq esp_reset;
qemu_irq esp_reset, dma_enable;
qemu_irq *cpu_exit_irq;
ram_addr_t ram_offset;
ram_addr_t bios_offset;
@ -245,7 +245,7 @@ void mips_jazz_init (ram_addr_t ram_size,
/* SCSI adapter */
esp_init(0x80002000, 0,
rc4030_dma_read, rc4030_dma_write, dmas[0],
rc4030[5], &esp_reset);
rc4030[5], &esp_reset, &dma_enable);
/* Floppy */
if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) {

View file

@ -58,6 +58,7 @@
#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
@ -72,7 +73,12 @@ struct DMAState {
uint32_t dmaregs[DMA_REGS];
qemu_irq irq;
void *iommu;
qemu_irq dev_reset;
qemu_irq gpio[2];
};
enum {
GPIO_RESET = 0,
GPIO_DMA,
};
/* Note: on sparc, the lance 16 bit bus is swapped */
@ -201,12 +207,21 @@ static void dma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
}
}
if (val & DMA_RESET) {
qemu_irq_raise(s->dev_reset);
qemu_irq_lower(s->dev_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)) {
DPRINTF("Raise DMA enable\n");
qemu_irq_raise(s->gpio[GPIO_DMA]);
} else if (!(val & DMA_EN) && !!(s->dmaregs[0] & DMA_EN)) {
DPRINTF("Lower DMA enable\n");
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;
@ -262,7 +277,7 @@ static int sparc32_dma_init1(SysBusDevice *dev)
sysbus_init_mmio(dev, DMA_SIZE, dma_io_memory);
qdev_init_gpio_in(&dev->qdev, dma_set_irq, 1);
qdev_init_gpio_out(&dev->qdev, &s->dev_reset, 1);
qdev_init_gpio_out(&dev->qdev, s->gpio, 2);
return 0;
}

View file

@ -810,7 +810,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
void *iommu, *espdma, *ledma, *nvram;
qemu_irq *cpu_irqs[MAX_CPUS], slavio_irq[32], slavio_cpu_irq[MAX_CPUS],
espdma_irq, ledma_irq;
qemu_irq esp_reset;
qemu_irq esp_reset, dma_enable;
qemu_irq fdc_tc;
qemu_irq *cpu_halt;
unsigned long kernel_size;
@ -930,11 +930,12 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
exit(1);
}
esp_reset = qdev_get_gpio_in(espdma, 0);
esp_init(hwdef->esp_base, 2,
espdma_memory_read, espdma_memory_write,
espdma, espdma_irq, &esp_reset);
espdma, espdma_irq, &esp_reset, &dma_enable);
qdev_connect_gpio_out(espdma, 0, esp_reset);
qdev_connect_gpio_out(espdma, 1, dma_enable);
if (hwdef->cs_base) {
sysbus_create_simple("SUNW,CS4231", hwdef->cs_base,
@ -1494,7 +1495,7 @@ static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size,
void *iounits[MAX_IOUNITS], *espdma, *ledma, *nvram;
qemu_irq *cpu_irqs[MAX_CPUS], sbi_irq[32], sbi_cpu_irq[MAX_CPUS],
espdma_irq, ledma_irq;
qemu_irq esp_reset;
qemu_irq esp_reset, dma_enable;
unsigned long kernel_size;
void *fw_cfg;
DeviceState *dev;
@ -1561,10 +1562,12 @@ static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size,
exit(1);
}
esp_reset = qdev_get_gpio_in(espdma, 0);
esp_init(hwdef->esp_base, 2,
espdma_memory_read, espdma_memory_write,
espdma, espdma_irq, &esp_reset);
espdma, espdma_irq, &esp_reset, &dma_enable);
qdev_connect_gpio_out(espdma, 0, esp_reset);
qdev_connect_gpio_out(espdma, 1, dma_enable);
kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename,
RAM_size);
@ -1683,7 +1686,7 @@ static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size,
{
void *iommu, *espdma, *ledma, *nvram;
qemu_irq *cpu_irqs, slavio_irq[8], espdma_irq, ledma_irq;
qemu_irq esp_reset;
qemu_irq esp_reset, dma_enable;
qemu_irq fdc_tc;
unsigned long kernel_size;
DriveInfo *fd[MAX_FD];
@ -1751,10 +1754,12 @@ static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size,
exit(1);
}
esp_reset = qdev_get_gpio_in(espdma, 0);
esp_init(hwdef->esp_base, 2,
espdma_memory_read, espdma_memory_write,
espdma, espdma_irq, &esp_reset);
espdma, espdma_irq, &esp_reset, &dma_enable);
qdev_connect_gpio_out(espdma, 0, esp_reset);
qdev_connect_gpio_out(espdma, 1, dma_enable);
kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename,
RAM_size);