18c2ecf20Sopenharmony_ci/** 28c2ecf20Sopenharmony_ci * \file ati_pcigart.c 38c2ecf20Sopenharmony_ci * ATI PCI GART support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * \author Gareth Hughes <gareth@valinux.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 128c2ecf20Sopenharmony_ci * All Rights Reserved. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 158c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 168c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 178c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 188c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 198c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 228c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 238c2ecf20Sopenharmony_ci * Software. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 268c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 278c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 288c2ecf20Sopenharmony_ci * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 298c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 308c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 318c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/export.h> 358c2ecf20Sopenharmony_ci#include <linux/pci.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 388c2ecf20Sopenharmony_ci#include <drm/drm_legacy.h> 398c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include "ati_pcigart.h" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci# define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int drm_ati_alloc_pcigart_table(struct drm_device *dev, 468c2ecf20Sopenharmony_ci struct drm_ati_pcigart_info *gart_info) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci gart_info->table_handle = drm_pci_alloc(dev, gart_info->table_size, 498c2ecf20Sopenharmony_ci PAGE_SIZE); 508c2ecf20Sopenharmony_ci if (gart_info->table_handle == NULL) 518c2ecf20Sopenharmony_ci return -ENOMEM; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return 0; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void drm_ati_free_pcigart_table(struct drm_device *dev, 578c2ecf20Sopenharmony_ci struct drm_ati_pcigart_info *gart_info) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci drm_pci_free(dev, gart_info->table_handle); 608c2ecf20Sopenharmony_ci gart_info->table_handle = NULL; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciint drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct drm_sg_mem *entry = dev->sg; 668c2ecf20Sopenharmony_ci unsigned long pages; 678c2ecf20Sopenharmony_ci int i; 688c2ecf20Sopenharmony_ci int max_pages; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* we need to support large memory configurations */ 718c2ecf20Sopenharmony_ci if (!entry) { 728c2ecf20Sopenharmony_ci DRM_ERROR("no scatter/gather memory!\n"); 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (gart_info->bus_addr) { 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci max_pages = (gart_info->table_size / sizeof(u32)); 798c2ecf20Sopenharmony_ci pages = (entry->pages <= max_pages) 808c2ecf20Sopenharmony_ci ? entry->pages : max_pages; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci for (i = 0; i < pages; i++) { 838c2ecf20Sopenharmony_ci if (!entry->busaddr[i]) 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci pci_unmap_page(dev->pdev, entry->busaddr[i], 868c2ecf20Sopenharmony_ci PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) 908c2ecf20Sopenharmony_ci gart_info->bus_addr = 0; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (gart_info->gart_table_location == DRM_ATI_GART_MAIN && 948c2ecf20Sopenharmony_ci gart_info->table_handle) { 958c2ecf20Sopenharmony_ci drm_ati_free_pcigart_table(dev, gart_info); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 1; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciint drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct drm_local_map *map = &gart_info->mapping; 1048c2ecf20Sopenharmony_ci struct drm_sg_mem *entry = dev->sg; 1058c2ecf20Sopenharmony_ci void *address = NULL; 1068c2ecf20Sopenharmony_ci unsigned long pages; 1078c2ecf20Sopenharmony_ci u32 *pci_gart = NULL, page_base, gart_idx; 1088c2ecf20Sopenharmony_ci dma_addr_t bus_address = 0; 1098c2ecf20Sopenharmony_ci int i, j, ret = -ENOMEM; 1108c2ecf20Sopenharmony_ci int max_ati_pages, max_real_pages; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!entry) { 1138c2ecf20Sopenharmony_ci DRM_ERROR("no scatter/gather memory!\n"); 1148c2ecf20Sopenharmony_ci goto done; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 1188c2ecf20Sopenharmony_ci DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n"); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) { 1218c2ecf20Sopenharmony_ci DRM_ERROR("fail to set dma mask to 0x%Lx\n", 1228c2ecf20Sopenharmony_ci (unsigned long long)gart_info->table_mask); 1238c2ecf20Sopenharmony_ci ret = -EFAULT; 1248c2ecf20Sopenharmony_ci goto done; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ret = drm_ati_alloc_pcigart_table(dev, gart_info); 1288c2ecf20Sopenharmony_ci if (ret) { 1298c2ecf20Sopenharmony_ci DRM_ERROR("cannot allocate PCI GART page!\n"); 1308c2ecf20Sopenharmony_ci goto done; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci pci_gart = gart_info->table_handle->vaddr; 1348c2ecf20Sopenharmony_ci address = gart_info->table_handle->vaddr; 1358c2ecf20Sopenharmony_ci bus_address = gart_info->table_handle->busaddr; 1368c2ecf20Sopenharmony_ci } else { 1378c2ecf20Sopenharmony_ci address = gart_info->addr; 1388c2ecf20Sopenharmony_ci bus_address = gart_info->bus_addr; 1398c2ecf20Sopenharmony_ci DRM_DEBUG("PCI: Gart Table: VRAM %08LX mapped at %08lX\n", 1408c2ecf20Sopenharmony_ci (unsigned long long)bus_address, 1418c2ecf20Sopenharmony_ci (unsigned long)address); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci max_ati_pages = (gart_info->table_size / sizeof(u32)); 1468c2ecf20Sopenharmony_ci max_real_pages = max_ati_pages / (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); 1478c2ecf20Sopenharmony_ci pages = (entry->pages <= max_real_pages) 1488c2ecf20Sopenharmony_ci ? entry->pages : max_real_pages; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 1518c2ecf20Sopenharmony_ci memset(pci_gart, 0, max_ati_pages * sizeof(u32)); 1528c2ecf20Sopenharmony_ci } else { 1538c2ecf20Sopenharmony_ci memset_io((void __iomem *)map->handle, 0, max_ati_pages * sizeof(u32)); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci gart_idx = 0; 1578c2ecf20Sopenharmony_ci for (i = 0; i < pages; i++) { 1588c2ecf20Sopenharmony_ci /* we need to support large memory configurations */ 1598c2ecf20Sopenharmony_ci entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i], 1608c2ecf20Sopenharmony_ci 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); 1618c2ecf20Sopenharmony_ci if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) { 1628c2ecf20Sopenharmony_ci DRM_ERROR("unable to map PCIGART pages!\n"); 1638c2ecf20Sopenharmony_ci drm_ati_pcigart_cleanup(dev, gart_info); 1648c2ecf20Sopenharmony_ci address = NULL; 1658c2ecf20Sopenharmony_ci bus_address = 0; 1668c2ecf20Sopenharmony_ci ret = -ENOMEM; 1678c2ecf20Sopenharmony_ci goto done; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci page_base = (u32) entry->busaddr[i]; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { 1728c2ecf20Sopenharmony_ci u32 offset; 1738c2ecf20Sopenharmony_ci u32 val; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci switch(gart_info->gart_reg_if) { 1768c2ecf20Sopenharmony_ci case DRM_ATI_GART_IGP: 1778c2ecf20Sopenharmony_ci val = page_base | 0xc; 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case DRM_ATI_GART_PCIE: 1808c2ecf20Sopenharmony_ci val = (page_base >> 8) | 0xc; 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci default: 1838c2ecf20Sopenharmony_ci case DRM_ATI_GART_PCI: 1848c2ecf20Sopenharmony_ci val = page_base; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci if (gart_info->gart_table_location == 1888c2ecf20Sopenharmony_ci DRM_ATI_GART_MAIN) { 1898c2ecf20Sopenharmony_ci pci_gart[gart_idx] = cpu_to_le32(val); 1908c2ecf20Sopenharmony_ci } else { 1918c2ecf20Sopenharmony_ci offset = gart_idx * sizeof(u32); 1928c2ecf20Sopenharmony_ci writel(val, (void __iomem *)map->handle + offset); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci gart_idx++; 1958c2ecf20Sopenharmony_ci page_base += ATI_PCIGART_PAGE_SIZE; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci ret = 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__) 2018c2ecf20Sopenharmony_ci wbinvd(); 2028c2ecf20Sopenharmony_ci#else 2038c2ecf20Sopenharmony_ci mb(); 2048c2ecf20Sopenharmony_ci#endif 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci done: 2078c2ecf20Sopenharmony_ci gart_info->addr = address; 2088c2ecf20Sopenharmony_ci gart_info->bus_addr = bus_address; 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci} 211