Hi,

A long time ago I was working on an AGPGART driver for my PPC machine,
but couldn't get it working due to missing datasheet, etc. Now I started
working on it again, as Radeon KMS works well on my machine so far and I
also rediscovered an old binary-only AGPGART driver for which objdump
revealed some interesting information.

Well, the current driver code is basically a copy of the UniNorth AGPGART
driver. The driver initializes fine, as the excerpt below shows:

calling  agp_init+0x0/0x54 @ 1
Linux agpgart interface v0.103
initcall agp_init+0x0/0x54 returned 0 after 2749 usecs
calling  agp_articias_init+0x0/0x58 @ 1
agpgart-articias 0000:00:00.0: MAI Logic Articia S chipset
agpgart-articias 0000:00:00.0: configuring for size idx: 1
agpgart-articias 0000:00:00.0: AGP aperture is 4M @ 0xc0000000
initcall agp_articias_init+0x0/0x58 returned 0 after 19494 usecs
calling  drm_core_init+0x0/0x158 @ 1
[drm] Initialized drm 1.1.0 20060810
initcall drm_core_init+0x0/0x158 returned 0 after 4526 usecs
calling  ttm_init+0x0/0x8c @ 1
initcall ttm_init+0x0/0x8c returned 0 after 1170 usecs
calling  radeon_init+0x0/0x100 @ 1
[drm] radeon defaulting to kernel modesetting.
[drm] radeon kernel modesetting enabled.
[drm] radeon: Initializing kernel modesetting.
[drm] register mmio base: 0x88000000
[drm] register mmio size: 65536
[drm] GPU reset succeed (RBBM_STATUS=0x00000140)
[drm] Generation 2 PCI interface, using max accessible memory
[drm] AGP mode requested: 1
agpgart-articias 0000:00:00.0: AGP 1.0 bridge
agpgart-articias 0000:00:00.0: putting AGP V2 device into 1x mode
radeon 0000:01:00.0: putting AGP V2 device into 1x mode
[drm] radeon: VRAM 128M
[drm] radeon: VRAM from 0x00000000 to 0x07FFFFFF
[drm] radeon: GTT 4M
[drm] radeon: GTT from 0xC0000000 to 0xC03FFFFF
[drm] radeon: irq initialized.
[drm] Detected VRAM RAM=128M, BAR=128M
[drm] RAM width 128bits DDR
[TTM] Zone  kernel: Available graphics memory: 377416 kiB.
[TTM] Zone highmem: Available graphics memory: 770632 kiB.
[drm] radeon: 128M of VRAM memory ready
[drm] radeon: 4M of GTT memory ready.
[drm] radeon: cp idle (0x02000603)
[drm] Loading R200 Microcode
platform radeon_cp.0: firmware: using built-in firmware radeon/R200_cp.bin
agpgart-articias 0000:00:00.0: TLB flush!
[drm] radeon: ring at 0x00000000C0000000
[drm] ring test succeeded in 0 usecs
agpgart-articias 0000:00:00.0: TLB flush!
agpgart-articias 0000:00:00.0: TLB flush!
[drm] radeon: ib pool ready.
[drm] ib test succeeded in 0 usecs
[drm] DFP table revision: 3
[drm] Radeon Display Connectors
[drm] Connector 0:
[drm]   VGA
[drm]   DDC: 0x60 0x60 0x60 0x60 0x60 0x60 0x60 0x60
[drm]   Encoders:
[drm]     CRT1: INTERNAL_DAC1
[drm] Connector 1:
[drm]   DVI-I
[drm]   DDC: 0x64 0x64 0x64 0x64 0x64 0x64 0x64 0x64
[drm]   Encoders:
[drm]     CRT2: INTERNAL_DAC2
[drm]     DFP1: INTERNAL_TMDS1
[drm] fb mappable at 0x80040000
[drm] vram apper at 0x80000000
[drm] size 7680000
[drm] fb depth is 24
[drm]    pitch is 6400
[drm] TMDS-12: set mode 1600x1200 24
Console: switching to colour frame buffer device 200x75
fb0: radeondrmfb frame buffer device
registered panic notifier
[drm] Initialized radeon 2.0.0 20080528 for 0000:01:00.0 on minor 0
initcall radeon_init+0x0/0x100 returned 0 after 635215 usecs

Judging from what the log says, I would expect that the radeon driver
can make use of the AGP aperture (as the the ring and ib test succeed -
is this assumption correct?).

Next I booted the kernel with radeon.test=1 and this test fails
immediately with a message like this:

[drm:radeon_test_moves] *ERROR* Incorrect GTT->VRAM copy 0: Got 0xf14a88f0, 
expected 0xf14a6680 (GTT map 0xf14a6000-0xf15a6000)

A different aperture size doesn't make any difference. However the test
works in PCIGART mode.
Now I wonder what the problem could be, as I don't have a clue about
the radeon/DRM code. Can somebody explain me, how the test works and
what the error message means for the AGPGART driver?

Thanks!

best regards,

Gerhard

PS: Please put me on CC:.

articias-agp.c:

/*
 * Articia S AGPGART routines.
 */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
#include <linux/agp_backend.h>
#include "agp.h"

#define ARTICIAS_GARTCTRL       0x58    /* bit 6 -> GART enable */
#define ARTICIAS_APSIZE         0x59    /* bit 2:0 -> aperture size */
#define ARTICIAS_TLBCTRL        0xe0    /* bit 5 -> TLB flush */
#define ARTICIAS_GATTBASE       0x58    /* 31:12 GATT base address */

static int articias_fetch_size(void)
{
        int i;
        u8 temp;
        struct aper_size_info_8 *values;

        values = A_SIZE_8(agp_bridge->driver->aperture_sizes);
        pci_read_config_byte(agp_bridge->dev, ARTICIAS_APSIZE, &temp);
        temp &= 0x07;

        for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
                if (temp == values[i].size_value) {
                        agp_bridge->previous_size =
                                agp_bridge->current_size = (void *) (values + 
i);
                        agp_bridge->aperture_size_idx = i;
                        return values[i].size;
                }
        }
        dev_err(&agp_bridge->dev->dev, "unknown aperture size from AGP bridge 
(0x%x)\n",
                temp);
        return 0;
}

static void articias_tlbflush(struct agp_memory *mem)
{
        /* Reverse engineered from binary-only driver. */
        /* Does this really work? Register seems to be read-only? */
        pci_write_config_byte(agp_bridge->dev, ARTICIAS_TLBCTRL, 0x20);
        pci_write_config_byte(agp_bridge->dev, ARTICIAS_TLBCTRL, 0);

        dev_info(&agp_bridge->dev->dev, "TLB flush!\n");
}

static void articias_cleanup(void)
{
        u8 temp;
        struct aper_size_info_8 *previous_size;

        /* Disable GART transfer control. */
        pci_read_config_byte(agp_bridge->dev, ARTICIAS_GARTCTRL, &temp);
        pci_write_config_byte(agp_bridge->dev, ARTICIAS_GARTCTRL, temp & ~0x40);

        /* Write back previous aperture size. */
        previous_size = A_SIZE_8(agp_bridge->previous_size);
        pci_write_config_byte(agp_bridge->dev, ARTICIAS_APSIZE,
                              previous_size->size_value);
}

static int articias_configure(void)
{
        u32 temp;
        struct aper_size_info_8 *current_size;

        current_size = A_SIZE_8(agp_bridge->current_size);

        dev_info(&agp_bridge->dev->dev, "configuring for size idx: %d\n",
                 current_size->size_value);

        /* Setup aperture size and GATT base address. */
        pci_read_config_dword(agp_bridge->dev, ARTICIAS_GATTBASE, &temp);
        temp = (temp & 0x000000ff) | ((u32)current_size->size_value)<<8 |
               (agp_bridge->gatt_bus_addr & 0xfffff000);
        pci_write_config_dword(agp_bridge->dev, ARTICIAS_GATTBASE, temp);

        /* Aperture address to map too. */
        pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
        agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);

        /* Enable GART transfer control. */
        pci_read_config_dword(agp_bridge->dev, ARTICIAS_GARTCTRL, &temp);
        pci_write_config_dword(agp_bridge->dev, ARTICIAS_GARTCTRL, temp | 0x40);

        return 0;
}

static void articias_agp_enable(struct agp_bridge_data *bridge, u32 
requested_mode)
{
        u32 bridge_agpstat;

        dev_info(&agp_bridge->dev->dev, "AGP %d.%d bridge\n",
                 agp_bridge->major_version, agp_bridge->minor_version);

        pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx + 
PCI_AGP_STATUS,
                              &bridge_agpstat);
        /* AGP 4X status bit is lying. */
        bridge_agpstat &= ~AGPSTAT2_4X;

        bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode, 
bridge_agpstat);

        bridge_agpstat |= AGPSTAT_AGP_ENABLE;

        agp_device_command(bridge_agpstat, false);
}

static int articias_insert_memory(struct agp_memory *mem, off_t pg_start, int 
type)
{
        int i, num_entries, mask_type;
        void *temp;
        u32 *gp;

        if (type != mem->type)
                return -EINVAL;

        mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
        if (mask_type != 0) {
                /* We know nothing of memory types */
                return -EINVAL;
        }

        if (mem->page_count == 0)
                return 0;

        temp = agp_bridge->current_size;
        num_entries = A_SIZE_8(temp)->num_entries;

        if ((pg_start + mem->page_count) > num_entries)
                return -EINVAL;

        gp = (u32 *)&agp_bridge->gatt_table[pg_start];
        for (i = 0; i < mem->page_count; ++i) {
                if (gp[i]) {
                        dev_info(&agp_bridge->dev->dev,
                                 "articias_insert_memory: entry 0x%x occupied 
(%x)\n",
                                 i, gp[i]);
                        return -EBUSY;
                }
        }

        for (i = 0; i < mem->page_count; i++) {
                gp[i] = cpu_to_le32(page_to_phys(mem->pages[i]) & 0xfffff000UL);
                flush_dcache_range((unsigned 
long)__va(page_to_phys(mem->pages[i])),
                                   (unsigned 
long)__va(page_to_phys(mem->pages[i]))+0x1000);
        }
        mb();
        articias_tlbflush(mem);

        return 0;
}

int articias_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
{
        size_t i;
        u32 *gp;
        int mask_type;

        if (type != mem->type)
                return -EINVAL;

        mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
        if (mask_type != 0) {
                /* We know nothing of memory types */
                return -EINVAL;
        }

        if (mem->page_count == 0)
                return 0;

        gp = (u32 *)&agp_bridge->gatt_table[pg_start];
        for (i = 0; i < mem->page_count; ++i)
                gp[i] = 0;
        mb();
        articias_tlbflush(mem);

        return 0;
}

static int articias_create_gatt_table(struct agp_bridge_data *bridge)
{
        char *table;
        char *table_end;
        int size;
        int page_order;
        int num_entries;
        int i;
        void *temp;
        struct page *page;
        struct page **pages;

        /* We can't handle 2 level gatt's */
        if (bridge->driver->size_type == LVL2_APER_SIZE)
                return -EINVAL;

        table = NULL;
        i = bridge->aperture_size_idx;
        temp = bridge->current_size;
        size = page_order = num_entries = 0;

        do {
                size = A_SIZE_8(temp)->size;
                page_order = A_SIZE_8(temp)->page_order;
                num_entries = A_SIZE_8(temp)->num_entries;

                table = (char *) __get_free_pages(GFP_KERNEL, page_order);

                if (table == NULL) {
                        i++;
                        bridge->current_size = A_IDX8(bridge);
                } else {
                        bridge->aperture_size_idx = i;
                }
        } while (!table && (i < bridge->driver->num_aperture_sizes));

        if (table == NULL)
                return -ENOMEM;

        pages = kmalloc((1 << page_order) * sizeof(struct page*), GFP_KERNEL);
        if (pages == NULL)
                goto enomem;

        table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);

        for (page = virt_to_page(table), i = 0; page <= virt_to_page(table_end);
             page++, i++) {
                SetPageReserved(page);
                pages[i] = page;
        }

        bridge->gatt_table_real = (u32 *)table;
        /* Need to clear out any dirty data still sitting in caches. */
        flush_dcache_range((unsigned long)table,
                           (unsigned long)(table_end + PAGE_SIZE));
        bridge->gatt_table = vmap(pages, (1 << page_order), 0, PAGE_KERNEL_NCG);

        if (bridge->gatt_table == NULL)
                goto enomem;

        bridge->gatt_bus_addr = virt_to_phys(table);

        for (i = 0; i < num_entries; i++)
                bridge->gatt_table[i] = 0;

        return 0;

enomem:
        kfree(pages);
        if (table)
                free_pages((unsigned long)table, page_order);
        return -ENOMEM;
}

static int articias_free_gatt_table(struct agp_bridge_data *bridge)
{
        int page_order;
        char *table, *table_end;
        void *temp;
        struct page *page;

        temp = bridge->current_size;
        page_order = A_SIZE_8(temp)->page_order;

        /*
         * Do not worry about freeing memory, because if this is called,
         * then all agp memory is deallocated and removed from the table.
         */

        vunmap(bridge->gatt_table);
        table = (char *)bridge->gatt_table_real;
        table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);

        for (page = virt_to_page(table); page <= virt_to_page(table_end); 
page++)
                ClearPageReserved(page);

        free_pages((unsigned long)bridge->gatt_table_real, page_order);

        return 0;
}

void articias_null_cache_flush(void)
{
        mb();
}

static const struct aper_size_info_8 articias_sizes[] =
{
        /* size, num_entries, page_order, size_value */
        {256, 65536, 6, 7},
        {128, 32768, 5, 6},
        {64, 16384, 4, 5},
        {32, 8192, 3, 4},
        {16, 4096, 2, 3},
        {8, 2048, 1, 2},
        {4, 1024, 1, 1}
};

const struct agp_bridge_driver articias_agp_driver = {
        .owner                  = THIS_MODULE,
        .aperture_sizes         = (void *)articias_sizes,
        .size_type              = U8_APER_SIZE,
        .num_aperture_sizes     = ARRAY_SIZE(articias_sizes),
        .configure              = articias_configure,
        .fetch_size             = articias_fetch_size,
        .cleanup                = articias_cleanup,
        .tlb_flush              = articias_tlbflush,
        .mask_memory            = agp_generic_mask_memory,
        .masks                  = NULL,
        .cache_flush            = articias_null_cache_flush,
        .agp_enable             = articias_agp_enable,
/*      .agp_enable             = agp_generic_enable, */
        .create_gatt_table      = articias_create_gatt_table,
        .free_gatt_table        = articias_free_gatt_table,
        .insert_memory          = articias_insert_memory,
        .remove_memory          = articias_remove_memory,
        .alloc_by_type          = agp_generic_alloc_by_type,
        .free_by_type           = agp_generic_free_by_type,
        .agp_alloc_page         = agp_generic_alloc_page,
        .agp_alloc_pages        = agp_generic_alloc_pages,
        .agp_destroy_page       = agp_generic_destroy_page,
        .agp_destroy_pages      = agp_generic_destroy_pages,
        .agp_type_to_mask_type  = agp_generic_type_to_mask_type,
        .cant_use_aperture      = true,
};

static struct agp_device_ids articias_agp_device_ids[] __devinitdata = {
        {
                .device_id      = PCI_DEVICE_ID_MAI_ARTICIAS,
                .chipset_name   = "Articia S",
        },
        { }
};

static int __devinit agp_articias_probe(struct pci_dev *pdev,
                                        const struct pci_device_id *ent)
{
        struct agp_device_ids *devs = articias_agp_device_ids;
        struct agp_bridge_data *bridge;
        u8 cap_ptr;
        int j;

        cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
        if (cap_ptr == 0)
                return -ENODEV;

        /* Probe for known chipsets. */
        for (j = 0; devs[j].chipset_name != NULL; ++j) {
                if (pdev->device == devs[j].device_id) {
                        dev_info(&pdev->dev, "MAI Logic %s chipset\n",
                                 devs[j].chipset_name);
                        goto found;
                }
        }

        dev_err(&pdev->dev, "unsupported MAI Logic chipset [%04x/%04x]\n",
                pdev->vendor, pdev->device);
        return -ENODEV;

found:
        bridge = agp_alloc_bridge();
        if (!bridge)
                return -ENOMEM;

        bridge->dev = pdev;
        bridge->capndx = cap_ptr;
        bridge->driver = &articias_agp_driver;
        /* AGP 2X isn't reliable and the AGP 4X status bit is wrong. */
//      bridge->flags = AGP_ERRATA_1X;

        /* Get bridge revision. */
        get_agp_version(bridge);

        /* Fill in the mode register. */
        pci_read_config_dword(pdev, bridge->capndx + PCI_AGP_STATUS,
                              &bridge->mode);

        pci_set_drvdata(pdev, bridge);
        return agp_add_bridge(bridge);
}

static void __devexit agp_articias_remove(struct pci_dev *pdev)
{
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);

        agp_remove_bridge(bridge);
        agp_put_bridge(bridge);
}

static struct pci_device_id agp_articias_pci_table[] = {
        {
                .class          = (PCI_CLASS_BRIDGE_HOST << 8),
                .class_mask     = ~0,
                .vendor         = PCI_VENDOR_ID_MAI,
                .device         = PCI_DEVICE_ID_MAI_ARTICIAS,
                .subvendor      = PCI_ANY_ID,
                .subdevice      = PCI_ANY_ID,
        },
        { }
};
MODULE_DEVICE_TABLE(pci, agp_articias_pci_table);

static struct pci_driver agp_articias_pci_driver = {
        .name           = "agpgart-articias",
        .id_table       = agp_articias_pci_table,
        .probe          = agp_articias_probe,
        .remove         = agp_articias_remove,
};

static int __init agp_articias_init(void)
{
        if (agp_off)
                return -EINVAL;
        return pci_register_driver(&agp_articias_pci_driver);
}

static void __exit agp_articias_cleanup(void)
{
        pci_unregister_driver(&agp_articias_pci_driver);
}

module_init(agp_articias_init);
module_exit(agp_articias_cleanup);

MODULE_AUTHOR("Gerhard Pircher <gerhard_pirc...@gmx.net");
MODULE_LICENSE("GPL");

-- 
Jetzt kostenlos herunterladen: Internet Explorer 8 und Mozilla Firefox 3.5 -
sicherer, schneller und einfacher! http://portal.gmx.net/de/go/atbrowser

------------------------------------------------------------------------------
Return on Information:
Google Enterprise Search pays you back
Get the facts.
http://p.sf.net/sfu/google-dev2dev
--
_______________________________________________
Dri-devel mailing list
Dri-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to