ata - Busmaster DMA
Posted: Tue Jan 27, 2009 4:08 pm
Hi all,
I'm currently working on getting PCI DMA in ata. I got it to work on qemu but on real hardware (some VIA-Chipset) nothing seems to happen when I issue the READ DMA command. No IRQ arrives and so the driver fails with an IRQ-Timeout.
The driver works well with PIO on real hardware. I allready looked ath the Wiki entry and the intel spec linked there.
Here are the (i think) relevant snippets of code:
The second function is called after the ata registers had been initialized.
Here you can have a look at the whole driver. The read is initiated from ata_drv_rw_sectors in ata.c.
Any ideas what I'm doing wrong? I tried to enable an mwdma-Mode first using the SET FEATURES command, but that didn't change anything.
Thanks in advance.
I'm currently working on getting PCI DMA in ata. I got it to work on qemu but on real hardware (some VIA-Chipset) nothing seems to happen when I issue the READ DMA command. No IRQ arrives and so the driver fails with an IRQ-Timeout.
The driver works well with PIO on real hardware. I allready looked ath the Wiki entry and the intel spec linked there.
Here are the (i think) relevant snippets of code:
Code: Select all
static int ata_request_dma_init(struct ata_request* request)
{
struct ata_device* dev = request->dev;
struct ata_controller* ctrl = dev->controller;
uint64_t size = request->block_size * request->block_count;
*ctrl->prdt_virt = (uint32_t) ctrl->dma_buf_phys;
// Groesse nicht ueber 64K, 0 == 64K
*ctrl->prdt_virt |= (size & (ATA_DMA_MAXSIZE - 1)) << 32L;
// Letzter Eintrag in PRDT
*ctrl->prdt_virt |= (uint64_t) 1L << 63L;
// Die laufenden Transfers anhalten
cdi_outb(ctrl->port_bmr_base + BMR_COMMAND, 0);
cdi_outb(ctrl->port_bmr_base + BMR_STATUS,
cdi_inb(ctrl->port_bmr_base + BMR_STATUS) | 0x06);
// Adresse der PRDT eintragen
cdi_outl(ctrl->port_bmr_base + BMR_PRDT, (uint32_t) ctrl->prdt_phys);
if (request->flags.direction != READ) {
memcpy(ctrl->dma_buf_virt, request->buffer, size);
}
return 1;
}
static int ata_protocol_dma(struct ata_request* request)
{
struct ata_device* dev = request->dev;
struct ata_controller* ctrl = dev->controller;
// Aktueller Status im Protokol
enum {
IRQ_WAIT,
CHECK_STATUS,
TRANSFER_DATA
} state;
cdi_inb(ctrl->port_bmr_base + BMR_COMMAND);
cdi_inb(ctrl->port_bmr_base + BMR_STATUS);
if (request->flags.direction != READ) {
cdi_outb(ctrl->port_bmr_base + BMR_COMMAND, 1 | (1 << 3));
} else {
cdi_outb(ctrl->port_bmr_base + BMR_COMMAND, 1);
}
cdi_inb(ctrl->port_bmr_base + BMR_COMMAND);
cdi_inb(ctrl->port_bmr_base + BMR_STATUS);
state = IRQ_WAIT;
while (1) {
puts("a");
switch (state) {
case IRQ_WAIT:
// Auf IRQ warten
if (!ata_wait_irq(ctrl, ATA_IRQ_TIMEOUT)) {
request->error = IRQ_TIMEOUT;
DEBUG("pio_out IRQ-Timeout\n");
return 0;
}
state = CHECK_STATUS;
break;
default:
case CHECK_STATUS: {
uint8_t status = ata_reg_inb(ctrl, REG_STATUS);
printf("\nstat=%x\n", status);
// Transfer abgeschlossen
if ((status & (STATUS_BSY | STATUS_DRQ)) == 0) {
cdi_inb(ctrl->port_bmr_base + BMR_STATUS);
cdi_outb(ctrl->port_bmr_base + BMR_COMMAND, 0);
goto out_success;
}
break;
}
}
}
out_success:
if (request->flags.direction == READ) {
memcpy(request->buffer, ctrl->dma_buf_virt,
request->block_size * request->block_count);
}
return 1;
}
Here you can have a look at the whole driver. The read is initiated from ata_drv_rw_sectors in ata.c.
Any ideas what I'm doing wrong? I tried to enable an mwdma-Mode first using the SET FEATURES command, but that didn't change anything.
Thanks in advance.