> --- Ursprüngliche Nachricht ---
> Von: Benjamin Herrenschmidt <[EMAIL PROTECTED]>
> An: Gerhard Pircher <[EMAIL PROTECTED]>
> Kopie: [EMAIL PROTECTED], debian-powerpc@lists.debian.org
> Betreff: Re: AGPGART driver for ArticiaS - ioremap() problem
> Datum: Mon, 16 Jan 2006 08:48:28 +1100
>
> I don't understand. I would need to now more about what
> the bridge actually does to get any sense out of that.
Thanks for answering! It's hard to explain for me what the code does, but I
will try.
I included a cleaned-up version of the AGPGART driver code for the ArticiaS
(AmigaOne/Pegasos1). As mentioned, the driver is based on the VIA AGPGART
driver and uses the agp_generic_create_gatt_table() and
agp_generic_free_gatt_table() functions (well, I copied and renamed this
functions to insert debug code).
Actually at least the bridge does everthing right until now (IMHO :-). The
bridge is detected and also the aperture size is read out correctly from the
registers. After that, the agp_backend_initialize() function in
drivers/cahr/agp/backend.c calls the create_gatt_table() function of the
driver (articias_create_gatt_table). Based on the detected aperture size and
its definitions, this function allocates pages for the GATT table
(alloc_gatt_pages). The function then tries to map this pages with
ioremap_nocache(virt_to_gatt(table, (PAGE_SIZE * (1 << page_order))). But
ioremap_nocache() cannot remap the pages, because the following code snipped
in __ioremap (arch/ppc/mm/pgtable.c) is trigged:
/*
* Don't allow anybody to remap normal RAM that we're using.
* mem_init() sets high_memory so only do the check after that.
*/
if ( mem_init_done && (p < virt_to_phys(high_memory)) )
{
printk("__ioremap(): phys addr "PHYS_FMT" is RAM lr %p\n", p,
__builtin_return_address(0));
return NULL;
}
As it can be seen in the following log, the table address (0xde200000 ->
virt_to_gart(table) = 0x1e200000) is lower than the maximum amount of memory
0x20000000 (512MB) and therefore __ioremap() does not remap the pages.
Debuglog:
agpgart: [ARTICIAS] articias_fetch_size()
agpgart: [ARTICIAS] * non masked temp = 0x6
agpgart: [ARTICIAS] * aperature size loop index #0
agpgart: [ARTICIAS] * values[0].size_value = 1
agpgart: [ARTICIAS] * aperature size loop index #1
agpgart: [ARTICIAS] * values[1].size_value = 2
agpgart: [ARTICIAS] * aperature size loop index #2
agpgart: [ARTICIAS] * values[2].size_value = 3
agpgart: [ARTICIAS] * aperature size loop index #3
agpgart: [ARTICIAS] * values[3].size_value = 4
agpgart: [ARTICIAS] * aperature size loop index #4
agpgart: [ARTICIAS] * values[4].size_value = 5
agpgart: [ARTICIAS] * aperature size loop index #5
agpgart: [ARTICIAS] * values[5].size_value = 6
agpgart: [ARTICIAS] * masked temp = 0x6
agpgart: [ARTICIAS] * values[5].size = 128 (128MB aperture size)
agpgart: [ARTICIAS] * current_size->size = 128
agpgart: [ARTICIAS] * current_size->page_order = 5
agpgart: [ARTICIAS] * current_size->num_entries = 32768
agpgart: [ARTICIAS] * current_size->size_value = 6
agpgart: [ARTICIAS] articias_create_gatt_table()
agpgart: [ARTICIAS] * table address = 0xde200000
agpgart: [ARTICIAS] * table end address = 0xde21ffff
agpgart: [ARTICIAS] * page = 0xc09c4400
agpgart: [ARTICIAS] * gatt_table_real = 0xde200000
agpgart: [ARTICIAS] * agp_gatt_table = 0xde200000
agpgart: [ARTICIAS] * virt_to_gart(table) = 0x1e200000
__ioremap() debug: addr = 0x1e200000
__ioremap() debug: phys addr = 0x1e200000
__ioremap() debug: size = 0x20000
__ioremap() debug: Highmem check
__ioremap() debug: high_memory = 0xe0000000
__ioremap() debug: virt_to_phys(high_memory) = 0x20000000
__ioremap(): phys addr 1e200000 is RAM lr c000f210
agpgart: [ARTICIAS] * gatt_table_real = 0x0
agpgart: unable to get memory for graphics translation table.
agpgart: agp_backend_initialize() failed.
agpgart-articias: probe of 0000:00:00.0 failed with error -12
How can I achieve it that ioremap_nocache() can map the pages or that
alloc_gatt_pages() returns valid pages for the GATT table?
Thanks again!
Regards,
Gerhard
--
Lust, ein paar Euro nebenbei zu verdienen? Ohne Kosten, ohne Risiko!
Satte Provisionen für GMX Partner: http://www.gmx.net/de/go/partner
/*
* ArticiaS AGPGART routines.
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/pagemap.h>
#include <linux/pci.h>
#include <asm/pci-bridge.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
#include "agp.h"
static struct pci_device_id agp_articias_pci_table[];
__u32 *agp_gatt_table;
#define ARTICIAS_AGP_EN 0x49 /* bit 0 -> AGP enable */
#define ARTICIAS_GART_EN 0x58 /* bit 6 -> GART enable */
#define ARTICIAS_APSIZE 0x59 /* bits 2:0 set size */
#define ARTICIAS_APBASE 0x59 /* TLB address Base [31:12]*/
#define ARTICIAS_GATTBASE 0x10 /* GART base address register */
#define ARTICIAS_TLB_BASE 0x5A /* bits 16:31 of TLB base address */
#define ARTICIAS_GATT_MASK 0xFFFFF000
#define ARTICIAS_SIZE_MASK 0x07 /* Mask aperture size bits. */
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);
/* Mask the GART/Aperture size selection bits. */
temp = temp & ARTICIAS_SIZE_MASK;
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);
/* Geri: Was <(void *) (values)> before, but that
* didn't make sense to me!? Otherwise it would always
* point to the same value!
*/
agp_bridge->aperture_size_idx = i;
return values[i].size;
}
}
printk(KERN_ERR PFX "Unknown aperture size from AGP bridge (0x%x)\n", temp);
return 0;
}
static int articias_configure(void)
{
u32 temp = 0;
u16 shift16 = 0;
u8 shift8 = 0;
struct aper_size_info_8 *current_size;
/* Get current aperture size */
current_size = A_SIZE_8(agp_bridge->current_size);
temp = (u32) agp_bridge->gatt_table_real;
/* Get upper word from dword. Note that the ArticiaS should have 20
* bits for the TLB base address. Otherwise the PCI write config code
* for the aperture size below doesn't make sense!?
*/
shift16 = (u16) (temp>>16);
pci_write_config_word(agp_bridge->dev, ARTICIAS_TLB_BASE, shift16);
/* Get the byte 1 from dword and mask it out with the aperture size. */
shift8 = (u8) (temp>>8);
shift8 &= ~(ARTICIAS_SIZE_MASK);
shift8 |= current_size->size_value;
pci_write_config_byte(agp_bridge->dev, ARTICIAS_APBASE, shift8);
/* Get address to map too */
pci_read_config_dword(agp_bridge->dev, ARTICIAS_GATTBASE, (void *)&temp);
temp = temp & ARTICIAS_GATT_MASK;
agp_bridge->gart_bus_addr = temp;
/* GART control register */
/* Enable GART and bus concurrency */
pci_write_config_byte(agp_bridge->dev, ARTICIAS_GART_EN, 0x41);
/* Enable AGP operation */
pci_write_config_byte(agp_bridge->dev, ARTICIAS_AGP_EN, 0x01);
return 0;
}
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;
/* The generic routines 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;
if (bridge->driver->size_type != FIXED_APER_SIZE) {
do {
switch (bridge->driver->size_type) {
case U8_APER_SIZE:
size = A_SIZE_8(temp)->size;
page_order =
A_SIZE_8(temp)->page_order;
num_entries =
A_SIZE_8(temp)->num_entries;
break;
case U16_APER_SIZE:
size = A_SIZE_16(temp)->size;
page_order = A_SIZE_16(temp)->page_order;
num_entries = A_SIZE_16(temp)->num_entries;
break;
case U32_APER_SIZE:
size = A_SIZE_32(temp)->size;
page_order = A_SIZE_32(temp)->page_order;
num_entries = A_SIZE_32(temp)->num_entries;
break;
/* This case will never really happen. */
case FIXED_APER_SIZE:
case LVL2_APER_SIZE:
default:
size = page_order = num_entries = 0;
break;
}
table = alloc_gatt_pages(page_order);
if (table == NULL) {
i++;
switch (bridge->driver->size_type) {
case U8_APER_SIZE:
bridge->current_size = A_IDX8(bridge);
break;
case U16_APER_SIZE:
bridge->current_size = A_IDX16(bridge);
break;
case U32_APER_SIZE:
bridge->current_size = A_IDX32(bridge);
break;
/* This case will never really happen. */
case FIXED_APER_SIZE:
case LVL2_APER_SIZE:
default:
bridge->current_size =
bridge->current_size;
break;
}
temp = bridge->current_size;
} else {
bridge->aperture_size_idx = i;
}
} while (!table && (i < bridge->driver->num_aperture_sizes));
} else {
size = ((struct aper_size_info_fixed *) temp)->size;
page_order = ((struct aper_size_info_fixed *) temp)->page_order;
num_entries = ((struct aper_size_info_fixed *) temp)->num_entries;
table = alloc_gatt_pages(page_order);
}
if (table == NULL)
return -ENOMEM;
table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
SetPageReserved(page);
bridge->gatt_table_real = (u32 *) table;
agp_gatt_table = (void *) table;
bridge->driver->cache_flush();
bridge->gatt_table = ioremap_nocache(virt_to_gart(table),
(PAGE_SIZE * (1 << page_order)));
bridge->driver->cache_flush();
if (bridge->gatt_table == NULL) {
for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
ClearPageReserved(page);
free_gatt_pages(table, page_order);
return -ENOMEM;
}
bridge->gatt_bus_addr = virt_to_gart(bridge->gatt_table_real);
/* AK: bogus, should encode addresses > 4GB */
for (i = 0; i < num_entries; i++) {
writel(bridge->scratch_page, bridge->gatt_table+i);
readl(bridge->gatt_table+i); /* PCI Posting. */
}
return 0;
}
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;
switch (bridge->driver->size_type) {
case U8_APER_SIZE:
page_order = A_SIZE_8(temp)->page_order;
break;
case U16_APER_SIZE:
page_order = A_SIZE_16(temp)->page_order;
break;
case U32_APER_SIZE:
page_order = A_SIZE_32(temp)->page_order;
break;
case FIXED_APER_SIZE:
page_order = A_SIZE_FIX(temp)->page_order;
break;
case LVL2_APER_SIZE:
/* The generic routines can't deal with 2 level gatt's */
return -EINVAL;
break;
default:
page_order = 0;
break;
}
/* Do not worry about freeing memory, because if this is
* called, then all agp memory is deallocated and removed
* from the table. */
iounmap(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_gatt_pages(bridge->gatt_table_real, page_order);
agp_gatt_table = NULL;
bridge->gatt_table = NULL;
bridge->gatt_table_real = NULL;
bridge->gatt_bus_addr = 0;
return 0;
}
#endif
static void articias_cleanup(void)
{
struct aper_size_info_8 *previous_size;
previous_size = A_SIZE_8(agp_bridge->previous_size);
pci_write_config_byte(agp_bridge->dev, ARTICIAS_APSIZE,
previous_size->size_value);
}
static void articias_tlbflush(struct agp_memory *mem)
{
u8 temp;
pci_read_config_byte(agp_bridge->dev, ARTICIAS_GART_EN, &temp);
pci_write_config_byte(agp_bridge->dev, ARTICIAS_GART_EN, (temp | 0x80));
pci_write_config_byte(agp_bridge->dev, ARTICIAS_GART_EN, (temp & 0x7F));
return;
}
static struct aper_size_info_8 articias_generic_sizes[7] =
{
/* size, num_entries, page_order, size_value */
{4, 1024, 1, 1},
{8, 2048, 1, 2},
{16, 4096, 2, 3},
{32, 8192, 3, 4},
{64, 16384, 4, 5},
{128, 32768, 5, 6},
{256, 65536, 6, 7},
};
static struct agp_bridge_driver articias_driver = {
.owner = THIS_MODULE,
.aperture_sizes = articias_generic_sizes,
.size_type = U8_APER_SIZE,
.num_aperture_sizes = 7,
.configure = articias_configure,
.fetch_size = articias_fetch_size,
.cleanup = articias_cleanup,
.tlb_flush = articias_tlbflush,
.mask_memory = agp_generic_mask_memory,
.masks = NULL,
.agp_enable = agp_generic_enable,
.cache_flush = global_cache_flush,
.create_gatt_table = articias_create_gatt_table,
.free_gatt_table = articias_free_gatt_table,
.insert_memory = agp_generic_insert_memory,
.remove_memory = agp_generic_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_destroy_page = agp_generic_destroy_page,
};
static struct agp_device_ids articias_agp_device_ids[] __devinitdata =
{
{
.device_id = PCI_DEVICE_ID_MAI_ARTICIAS,
.chipset_name = "ArticiaS",
},
{ }, /* dummy final entry, always present */
};
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;
int j = 0;
u8 cap_ptr;
cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
if (!cap_ptr)
return -ENODEV;
j = ent - agp_articias_pci_table;
printk (KERN_INFO PFX "Detected MAI %s chipset\n", devs[j].chipset_name);
bridge = agp_alloc_bridge();
if (!bridge)
return -ENOMEM;
#ifdef ARTICIAS_DEBUG
/* Geri: Print all AGP relevant registers of the ArticiaS. */
articias_print_agp_register(pdev);
#endif
bridge->dev = pdev;
bridge->capndx = cap_ptr;
bridge->driver = &articias_driver;
/* Set a higher aperture size! Now it should be 128MB. */
pci_write_config_byte(pdev, ARTICIAS_APSIZE, 0x06);
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);
}
#ifdef CONFIG_PM
static int agp_articias_suspend(struct pci_dev *pdev, pm_message_t state)
{
pci_save_state (pdev);
pci_set_power_state (pdev, PCI_D3hot);
return 0;
}
static int agp_articias_resume(struct pci_dev *pdev)
{
struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
pci_set_power_state (pdev, PCI_D0);
pci_restore_state(pdev);
if (bridge->driver == &articias_driver)
return articias_configure();
return 0;
}
#endif /* CONFIG_PM */
/* must be the same order as name table above */
static struct pci_device_id agp_articias_pci_table[] = {
#define ID(x) \
{ \
.class = (PCI_CLASS_BRIDGE_HOST << 8), \
.class_mask = ~0, \
.vendor = PCI_VENDOR_ID_MAI, \
.device = x, \
.subvendor = PCI_ANY_ID, \
.subdevice = PCI_ANY_ID, \
}
ID(PCI_DEVICE_ID_MAI_ARTICIAS),
{ }
};
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,
#ifdef CONFIG_PM
.suspend = agp_articias_suspend,
.resume = agp_articias_resume,
#endif
};
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_LICENSE("GPL");
MODULE_AUTHOR("Dave Jones <[EMAIL PROTECTED]>");