/* * 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/init.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 void *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_address(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, unsigned long 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; break; 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, mem->memory[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 __devinit 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) { struct list_head *tmp; if (list_empty(info->ca_devices)) continue; list_for_each(tmp, info->ca_devices) { u8 cap_ptr; pdev = pci_dev_b(tmp); 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 __devexit 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");