ata - Busmaster DMA

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
AntoineKaufmann
Posts: 5
Joined: Wed Jun 13, 2007 4:20 am
Location: Switzerland
Contact:

ata - Busmaster DMA

Post by AntoineKaufmann »

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:

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;
}
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.
German OS-Dev Community: http://lowlevel.brainsware.org
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: ata - Busmaster DMA

Post by Brendan »

Hi,
AntoineKaufmann wrote: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.
I'm curious - did you try it on other real computers? There have been reports of problems with some VIA chipsets (like this one).

If you're lucky you might be able to make sure the driver detects if DMA fails and automatically switches to PIO, and then upgrade your BIOS to get DMA working.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
AntoineKaufmann
Posts: 5
Joined: Wed Jun 13, 2007 4:20 am
Location: Switzerland
Contact:

Re: ata - Busmaster DMA

Post by AntoineKaufmann »

Brendan wrote: I'm curious - did you try it on other real computers? There have been reports of problems with some VIA chipsets (like this one).
Good point, thank you. I think I hit that bug. Just tried another machine and it worked like a charm. ;-)

Okay, it's not as fast as I hoped... If I understand the wiki article correct, the BIOS should pick the fastest possible (U)DMA-Mode, or did I misunderstand that?
German OS-Dev Community: http://lowlevel.brainsware.org
AntoineKaufmann
Posts: 5
Joined: Wed Jun 13, 2007 4:20 am
Location: Switzerland
Contact:

Re: ata - Busmaster DMA

Post by AntoineKaufmann »

OT: In general you may be right, but this is the community OS of the german osdev community and the german comments are one of our goals, because there isn't much german information about osdev. ;-)
German OS-Dev Community: http://lowlevel.brainsware.org
User avatar
Firestryke31
Member
Member
Posts: 550
Joined: Sat Nov 29, 2008 1:07 pm
Location: Throw a dart at central Texas
Contact:

Re: ata - Busmaster DMA

Post by Firestryke31 »

Or have bilingual comments: German, then English.
Owner of Fawkes Software.
Wierd Al wrote: You think your Commodore 64 is really neato,
What kind of chip you got in there, a Dorito?
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: ata - Busmaster DMA

Post by bewing »

AntoineKaufmann wrote: Okay, it's not as fast as I hoped... If I understand the wiki article correct, the BIOS should pick the fastest possible (U)DMA-Mode, or did I misunderstand that?
Important point:
The speed ratings that are given for all forms of disk transfer are maximum burst-mode transfer rates. In the real world, you will probably get half that transfer rate, at best -- if your DMA driver is well-written.

The BIOS tries to pick the fastest mode. But BIOSes have bugs.
The PCI disk controller chip has a maximum (U)DMA mode, and the disk drive has a different maximum. The BIOS is supposed to set both devices to the lower of the two -- the highest mode that both of them share.
The BIOS is also supposed to make sure that UDMA mode is actually selected on the drive.

It is easy to find the maximum mode for the disk -- it is in the Identify information. The flag for the "current UDMA mode" is also there.
If you can also get the data from the PCI disk controller device, then you can check for yourself what the proper UDMA mode should be, and see if it is set right, and do some large transfers, to see if you come close to the correct speed.
Post Reply