18c2ecf20Sopenharmony_ci#include <linux/module.h>
28c2ecf20Sopenharmony_ci#include <linux/pci.h>
38c2ecf20Sopenharmony_ci#include <linux/init.h>
48c2ecf20Sopenharmony_ci#include <linux/agp_backend.h>
58c2ecf20Sopenharmony_ci#include <linux/mm.h>
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <asm/machvec.h>
98c2ecf20Sopenharmony_ci#include <asm/agp_backend.h>
108c2ecf20Sopenharmony_ci#include "../../../arch/alpha/kernel/pci_impl.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "agp.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic vm_fault_t alpha_core_agp_vm_fault(struct vm_fault *vmf)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	alpha_agp_info *agp = agp_bridge->dev_private_data;
178c2ecf20Sopenharmony_ci	dma_addr_t dma_addr;
188c2ecf20Sopenharmony_ci	unsigned long pa;
198c2ecf20Sopenharmony_ci	struct page *page;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	dma_addr = vmf->address - vmf->vma->vm_start + agp->aperture.bus_base;
228c2ecf20Sopenharmony_ci	pa = agp->ops->translate(agp, dma_addr);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	if (pa == (unsigned long)-EINVAL)
258c2ecf20Sopenharmony_ci		return VM_FAULT_SIGBUS;	/* no translation */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	/*
288c2ecf20Sopenharmony_ci	 * Get the page, inc the use count, and return it
298c2ecf20Sopenharmony_ci	 */
308c2ecf20Sopenharmony_ci	page = virt_to_page(__va(pa));
318c2ecf20Sopenharmony_ci	get_page(page);
328c2ecf20Sopenharmony_ci	vmf->page = page;
338c2ecf20Sopenharmony_ci	return 0;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic struct aper_size_info_fixed alpha_core_agp_sizes[] =
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	{ 0, 0, 0 }, /* filled in by alpha_core_agp_setup */
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic const struct vm_operations_struct alpha_core_agp_vm_ops = {
428c2ecf20Sopenharmony_ci	.fault = alpha_core_agp_vm_fault,
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int alpha_core_agp_fetch_size(void)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	return alpha_core_agp_sizes[0].size;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic int alpha_core_agp_configure(void)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	alpha_agp_info *agp = agp_bridge->dev_private_data;
548c2ecf20Sopenharmony_ci	agp_bridge->gart_bus_addr = agp->aperture.bus_base;
558c2ecf20Sopenharmony_ci	return 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void alpha_core_agp_cleanup(void)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	alpha_agp_info *agp = agp_bridge->dev_private_data;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	agp->ops->cleanup(agp);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void alpha_core_agp_tlbflush(struct agp_memory *mem)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	alpha_agp_info *agp = agp_bridge->dev_private_data;
688c2ecf20Sopenharmony_ci	alpha_mv.mv_pci_tbi(agp->hose, 0, -1);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void alpha_core_agp_enable(struct agp_bridge_data *bridge, u32 mode)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	alpha_agp_info *agp = bridge->dev_private_data;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	agp->mode.lw = agp_collect_device_status(bridge, mode,
768c2ecf20Sopenharmony_ci					agp->capability.lw);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	agp->mode.bits.enable = 1;
798c2ecf20Sopenharmony_ci	agp->ops->configure(agp);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	agp_device_command(agp->mode.lw, false);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int alpha_core_agp_insert_memory(struct agp_memory *mem, off_t pg_start,
858c2ecf20Sopenharmony_ci					int type)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	alpha_agp_info *agp = agp_bridge->dev_private_data;
888c2ecf20Sopenharmony_ci	int num_entries, status;
898c2ecf20Sopenharmony_ci	void *temp;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES)
928c2ecf20Sopenharmony_ci		return -EINVAL;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	temp = agp_bridge->current_size;
958c2ecf20Sopenharmony_ci	num_entries = A_SIZE_FIX(temp)->num_entries;
968c2ecf20Sopenharmony_ci	if ((pg_start + mem->page_count) > num_entries)
978c2ecf20Sopenharmony_ci		return -EINVAL;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	status = agp->ops->bind(agp, pg_start, mem);
1008c2ecf20Sopenharmony_ci	mb();
1018c2ecf20Sopenharmony_ci	alpha_core_agp_tlbflush(mem);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return status;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int alpha_core_agp_remove_memory(struct agp_memory *mem, off_t pg_start,
1078c2ecf20Sopenharmony_ci					int type)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	alpha_agp_info *agp = agp_bridge->dev_private_data;
1108c2ecf20Sopenharmony_ci	int status;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	status = agp->ops->unbind(agp, pg_start, mem);
1138c2ecf20Sopenharmony_ci	alpha_core_agp_tlbflush(mem);
1148c2ecf20Sopenharmony_ci	return status;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int alpha_core_agp_create_free_gatt_table(struct agp_bridge_data *a)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistruct agp_bridge_driver alpha_core_agp_driver = {
1238c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
1248c2ecf20Sopenharmony_ci	.aperture_sizes		= alpha_core_agp_sizes,
1258c2ecf20Sopenharmony_ci	.num_aperture_sizes	= 1,
1268c2ecf20Sopenharmony_ci	.size_type		= FIXED_APER_SIZE,
1278c2ecf20Sopenharmony_ci	.cant_use_aperture	= true,
1288c2ecf20Sopenharmony_ci	.masks			= NULL,
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	.fetch_size		= alpha_core_agp_fetch_size,
1318c2ecf20Sopenharmony_ci	.configure		= alpha_core_agp_configure,
1328c2ecf20Sopenharmony_ci	.agp_enable		= alpha_core_agp_enable,
1338c2ecf20Sopenharmony_ci	.cleanup		= alpha_core_agp_cleanup,
1348c2ecf20Sopenharmony_ci	.tlb_flush		= alpha_core_agp_tlbflush,
1358c2ecf20Sopenharmony_ci	.mask_memory		= agp_generic_mask_memory,
1368c2ecf20Sopenharmony_ci	.cache_flush		= global_cache_flush,
1378c2ecf20Sopenharmony_ci	.create_gatt_table	= alpha_core_agp_create_free_gatt_table,
1388c2ecf20Sopenharmony_ci	.free_gatt_table	= alpha_core_agp_create_free_gatt_table,
1398c2ecf20Sopenharmony_ci	.insert_memory		= alpha_core_agp_insert_memory,
1408c2ecf20Sopenharmony_ci	.remove_memory		= alpha_core_agp_remove_memory,
1418c2ecf20Sopenharmony_ci	.alloc_by_type		= agp_generic_alloc_by_type,
1428c2ecf20Sopenharmony_ci	.free_by_type		= agp_generic_free_by_type,
1438c2ecf20Sopenharmony_ci	.agp_alloc_page		= agp_generic_alloc_page,
1448c2ecf20Sopenharmony_ci	.agp_alloc_pages	= agp_generic_alloc_pages,
1458c2ecf20Sopenharmony_ci	.agp_destroy_page	= agp_generic_destroy_page,
1468c2ecf20Sopenharmony_ci	.agp_destroy_pages	= agp_generic_destroy_pages,
1478c2ecf20Sopenharmony_ci	.agp_type_to_mask_type  = agp_generic_type_to_mask_type,
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistruct agp_bridge_data *alpha_bridge;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ciint __init
1538c2ecf20Sopenharmony_cialpha_core_agp_setup(void)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	alpha_agp_info *agp = alpha_mv.agp_info();
1568c2ecf20Sopenharmony_ci	struct pci_dev *pdev;	/* faked */
1578c2ecf20Sopenharmony_ci	struct aper_size_info_fixed *aper_size;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (!agp)
1608c2ecf20Sopenharmony_ci		return -ENODEV;
1618c2ecf20Sopenharmony_ci	if (agp->ops->setup(agp))
1628c2ecf20Sopenharmony_ci		return -ENODEV;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/*
1658c2ecf20Sopenharmony_ci	 * Build the aperture size descriptor
1668c2ecf20Sopenharmony_ci	 */
1678c2ecf20Sopenharmony_ci	aper_size = alpha_core_agp_sizes;
1688c2ecf20Sopenharmony_ci	aper_size->size = agp->aperture.size / (1024 * 1024);
1698c2ecf20Sopenharmony_ci	aper_size->num_entries = agp->aperture.size / PAGE_SIZE;
1708c2ecf20Sopenharmony_ci	aper_size->page_order = __ffs(aper_size->num_entries / 1024);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/*
1738c2ecf20Sopenharmony_ci	 * Build a fake pci_dev struct
1748c2ecf20Sopenharmony_ci	 */
1758c2ecf20Sopenharmony_ci	pdev = pci_alloc_dev(NULL);
1768c2ecf20Sopenharmony_ci	if (!pdev)
1778c2ecf20Sopenharmony_ci		return -ENOMEM;
1788c2ecf20Sopenharmony_ci	pdev->vendor = 0xffff;
1798c2ecf20Sopenharmony_ci	pdev->device = 0xffff;
1808c2ecf20Sopenharmony_ci	pdev->sysdata = agp->hose;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	alpha_bridge = agp_alloc_bridge();
1838c2ecf20Sopenharmony_ci	if (!alpha_bridge)
1848c2ecf20Sopenharmony_ci		goto fail;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	alpha_bridge->driver = &alpha_core_agp_driver;
1878c2ecf20Sopenharmony_ci	alpha_bridge->vm_ops = &alpha_core_agp_vm_ops;
1888c2ecf20Sopenharmony_ci	alpha_bridge->current_size = aper_size; /* only 1 size */
1898c2ecf20Sopenharmony_ci	alpha_bridge->dev_private_data = agp;
1908c2ecf20Sopenharmony_ci	alpha_bridge->dev = pdev;
1918c2ecf20Sopenharmony_ci	alpha_bridge->mode = agp->capability.lw;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	printk(KERN_INFO PFX "Detected AGP on hose %d\n", agp->hose->index);
1948c2ecf20Sopenharmony_ci	return agp_add_bridge(alpha_bridge);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci fail:
1978c2ecf20Sopenharmony_ci	kfree(pdev);
1988c2ecf20Sopenharmony_ci	return -ENOMEM;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic int __init agp_alpha_core_init(void)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	if (agp_off)
2048c2ecf20Sopenharmony_ci		return -EINVAL;
2058c2ecf20Sopenharmony_ci	if (alpha_mv.agp_info)
2068c2ecf20Sopenharmony_ci		return alpha_core_agp_setup();
2078c2ecf20Sopenharmony_ci	return -ENODEV;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic void __exit agp_alpha_core_cleanup(void)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	agp_remove_bridge(alpha_bridge);
2138c2ecf20Sopenharmony_ci	agp_put_bridge(alpha_bridge);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cimodule_init(agp_alpha_core_init);
2178c2ecf20Sopenharmony_cimodule_exit(agp_alpha_core_cleanup);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jeff Wiedemeier <Jeff.Wiedemeier@hp.com>");
2208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL and additional rights");
221