diff options
Diffstat (limited to 'drivers/char/agp/sgi-agp.c')
-rw-r--r-- | drivers/char/agp/sgi-agp.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c new file mode 100644 index 000000000..3051c73bc --- /dev/null +++ b/drivers/char/agp/sgi-agp.c @@ -0,0 +1,338 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2003-2005 Silicon Graphics, Inc. All Rights Reserved. + */ + +/* + * SGI TIOCA AGPGART routines. + * + */ + +#include <linux/acpi.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/agp_backend.h> +#include <asm/sn/addrs.h> +#include <asm/sn/io.h> +#include <asm/sn/pcidev.h> +#include <asm/sn/pcibus_provider_defs.h> +#include <asm/sn/tioca_provider.h> +#include "agp.h" + +extern int agp_memory_reserved; +extern uint32_t tioca_gart_found; +extern struct list_head tioca_list; +static struct agp_bridge_data **sgi_tioca_agp_bridges; + +/* + * The aperature size and related information is set up at TIOCA init time. + * Values for this table will be extracted and filled in at + * sgi_tioca_fetch_size() time. + */ + +static struct aper_size_info_fixed sgi_tioca_sizes[] = { + {0, 0, 0}, +}; + +static struct page *sgi_tioca_alloc_page(struct agp_bridge_data *bridge) +{ + struct page *page; + int nid; + struct tioca_kernel *info = + (struct tioca_kernel *)bridge->dev_private_data; + + nid = info->ca_closest_node; + page = alloc_pages_node(nid, GFP_KERNEL, 0); + if (!page) + return NULL; + + get_page(page); + atomic_inc(&agp_bridge->current_memory_agp); + return page; +} + +/* + * Flush GART tlb's. Cannot selectively flush based on memory so the mem + * arg is ignored. + */ + +static void sgi_tioca_tlbflush(struct agp_memory *mem) +{ + tioca_tlbflush(mem->bridge->dev_private_data); +} + +/* + * Given an address of a host physical page, turn it into a valid gart + * entry. + */ +static unsigned long +sgi_tioca_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, + int type) +{ + return tioca_physpage_to_gart(addr); +} + +static void sgi_tioca_agp_enable(struct agp_bridge_data *bridge, u32 mode) +{ + tioca_fastwrite_enable(bridge->dev_private_data); +} + +/* + * sgi_tioca_configure() doesn't have anything to do since the base CA driver + * has alreay set up the GART. + */ + +static int sgi_tioca_configure(void) +{ + return 0; +} + +/* + * Determine gfx aperature size. This has already been determined by the + * CA driver init, so just need to set agp_bridge values accordingly. + */ + +static int sgi_tioca_fetch_size(void) +{ + struct tioca_kernel *info = + (struct tioca_kernel *)agp_bridge->dev_private_data; + + sgi_tioca_sizes[0].size = info->ca_gfxap_size / MB(1); + sgi_tioca_sizes[0].num_entries = info->ca_gfxgart_entries; + + return sgi_tioca_sizes[0].size; +} + +static int sgi_tioca_create_gatt_table(struct agp_bridge_data *bridge) +{ + struct tioca_kernel *info = + (struct tioca_kernel *)bridge->dev_private_data; + + bridge->gatt_table_real = (u32 *) info->ca_gfxgart; + bridge->gatt_table = bridge->gatt_table_real; + bridge->gatt_bus_addr = info->ca_gfxgart_base; + + return 0; +} + +static int sgi_tioca_free_gatt_table(struct agp_bridge_data *bridge) +{ + return 0; +} + +static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start, + int type) +{ + int num_entries; + size_t i; + off_t j; + void *temp; + struct agp_bridge_data *bridge; + u64 *table; + + bridge = mem->bridge; + if (!bridge) + return -EINVAL; + + table = (u64 *)bridge->gatt_table; + + temp = bridge->current_size; + + switch (bridge->driver->size_type) { + case U8_APER_SIZE: + num_entries = A_SIZE_8(temp)->num_entries; + break; + case U16_APER_SIZE: + num_entries = A_SIZE_16(temp)->num_entries; + break; + case U32_APER_SIZE: + num_entries = A_SIZE_32(temp)->num_entries; + break; + case FIXED_APER_SIZE: + num_entries = A_SIZE_FIX(temp)->num_entries; + break; + case LVL2_APER_SIZE: + return -EINVAL; + default: + num_entries = 0; + break; + } + + num_entries -= agp_memory_reserved / PAGE_SIZE; + if (num_entries < 0) + num_entries = 0; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + + if ((pg_start + mem->page_count) > num_entries) + return -EINVAL; + + j = pg_start; + + while (j < (pg_start + mem->page_count)) { + if (table[j]) + return -EBUSY; + j++; + } + + if (!mem->is_flushed) { + bridge->driver->cache_flush(); + mem->is_flushed = true; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + table[j] = + bridge->driver->mask_memory(bridge, + page_to_phys(mem->pages[i]), + mem->type); + } + + bridge->driver->tlb_flush(mem); + return 0; +} + +static int sgi_tioca_remove_memory(struct agp_memory *mem, off_t pg_start, + int type) +{ + size_t i; + struct agp_bridge_data *bridge; + u64 *table; + + bridge = mem->bridge; + if (!bridge) + return -EINVAL; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + + table = (u64 *)bridge->gatt_table; + + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + table[i] = 0; + } + + bridge->driver->tlb_flush(mem); + return 0; +} + +static void sgi_tioca_cache_flush(void) +{ +} + +/* + * Cleanup. Nothing to do as the CA driver owns the GART. + */ + +static void sgi_tioca_cleanup(void) +{ +} + +static struct agp_bridge_data *sgi_tioca_find_bridge(struct pci_dev *pdev) +{ + struct agp_bridge_data *bridge; + + list_for_each_entry(bridge, &agp_bridges, list) { + if (bridge->dev->bus == pdev->bus) + break; + } + return bridge; +} + +const struct agp_bridge_driver sgi_tioca_driver = { + .owner = THIS_MODULE, + .size_type = U16_APER_SIZE, + .configure = sgi_tioca_configure, + .fetch_size = sgi_tioca_fetch_size, + .cleanup = sgi_tioca_cleanup, + .tlb_flush = sgi_tioca_tlbflush, + .mask_memory = sgi_tioca_mask_memory, + .agp_enable = sgi_tioca_agp_enable, + .cache_flush = sgi_tioca_cache_flush, + .create_gatt_table = sgi_tioca_create_gatt_table, + .free_gatt_table = sgi_tioca_free_gatt_table, + .insert_memory = sgi_tioca_insert_memory, + .remove_memory = sgi_tioca_remove_memory, + .alloc_by_type = agp_generic_alloc_by_type, + .free_by_type = agp_generic_free_by_type, + .agp_alloc_page = sgi_tioca_alloc_page, + .agp_destroy_page = agp_generic_destroy_page, + .agp_type_to_mask_type = agp_generic_type_to_mask_type, + .cant_use_aperture = true, + .needs_scratch_page = false, + .num_aperture_sizes = 1, +}; + +static int agp_sgi_init(void) +{ + unsigned int j; + struct tioca_kernel *info; + struct pci_dev *pdev = NULL; + + if (tioca_gart_found) + printk(KERN_INFO PFX "SGI TIO CA GART driver initialized.\n"); + else + return 0; + + sgi_tioca_agp_bridges = kmalloc(tioca_gart_found * + sizeof(struct agp_bridge_data *), + GFP_KERNEL); + if (!sgi_tioca_agp_bridges) + return -ENOMEM; + + j = 0; + list_for_each_entry(info, &tioca_list, ca_list) { + if (list_empty(info->ca_devices)) + continue; + list_for_each_entry(pdev, info->ca_devices, bus_list) { + u8 cap_ptr; + + if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8)) + continue; + cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); + if (!cap_ptr) + continue; + } + sgi_tioca_agp_bridges[j] = agp_alloc_bridge(); + printk(KERN_INFO PFX "bridge %d = 0x%p\n", j, + sgi_tioca_agp_bridges[j]); + if (sgi_tioca_agp_bridges[j]) { + sgi_tioca_agp_bridges[j]->dev = pdev; + sgi_tioca_agp_bridges[j]->dev_private_data = info; + sgi_tioca_agp_bridges[j]->driver = &sgi_tioca_driver; + sgi_tioca_agp_bridges[j]->gart_bus_addr = + info->ca_gfxap_base; + sgi_tioca_agp_bridges[j]->mode = (0x7D << 24) | /* 126 requests */ + (0x1 << 9) | /* SBA supported */ + (0x1 << 5) | /* 64-bit addresses supported */ + (0x1 << 4) | /* FW supported */ + (0x1 << 3) | /* AGP 3.0 mode */ + 0x2; /* 8x transfer only */ + sgi_tioca_agp_bridges[j]->current_size = + sgi_tioca_agp_bridges[j]->previous_size = + (void *)&sgi_tioca_sizes[0]; + agp_add_bridge(sgi_tioca_agp_bridges[j]); + } + j++; + } + + agp_find_bridge = &sgi_tioca_find_bridge; + return 0; +} + +static void agp_sgi_cleanup(void) +{ + kfree(sgi_tioca_agp_bridges); + sgi_tioca_agp_bridges = NULL; +} + +module_init(agp_sgi_init); +module_exit(agp_sgi_cleanup); + +MODULE_LICENSE("GPL and additional rights"); |