1*677dec6eSriastradh /* $NetBSD: ati_pcigart.c,v 1.2 2021/12/18 23:45:42 riastradh Exp $ */
21571a7a1Sriastradh
31571a7a1Sriastradh /**
41571a7a1Sriastradh * \file ati_pcigart.c
51571a7a1Sriastradh * ATI PCI GART support
61571a7a1Sriastradh *
71571a7a1Sriastradh * \author Gareth Hughes <gareth@valinux.com>
81571a7a1Sriastradh */
91571a7a1Sriastradh
101571a7a1Sriastradh /*
111571a7a1Sriastradh * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com
121571a7a1Sriastradh *
131571a7a1Sriastradh * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
141571a7a1Sriastradh * All Rights Reserved.
151571a7a1Sriastradh *
161571a7a1Sriastradh * Permission is hereby granted, free of charge, to any person obtaining a
171571a7a1Sriastradh * copy of this software and associated documentation files (the "Software"),
181571a7a1Sriastradh * to deal in the Software without restriction, including without limitation
191571a7a1Sriastradh * the rights to use, copy, modify, merge, publish, distribute, sublicense,
201571a7a1Sriastradh * and/or sell copies of the Software, and to permit persons to whom the
211571a7a1Sriastradh * Software is furnished to do so, subject to the following conditions:
221571a7a1Sriastradh *
231571a7a1Sriastradh * The above copyright notice and this permission notice (including the next
241571a7a1Sriastradh * paragraph) shall be included in all copies or substantial portions of the
251571a7a1Sriastradh * Software.
261571a7a1Sriastradh *
271571a7a1Sriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
281571a7a1Sriastradh * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
291571a7a1Sriastradh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
301571a7a1Sriastradh * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
311571a7a1Sriastradh * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
321571a7a1Sriastradh * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
331571a7a1Sriastradh * DEALINGS IN THE SOFTWARE.
341571a7a1Sriastradh */
351571a7a1Sriastradh
361571a7a1Sriastradh #include <sys/cdefs.h>
37*677dec6eSriastradh __KERNEL_RCSID(0, "$NetBSD: ati_pcigart.c,v 1.2 2021/12/18 23:45:42 riastradh Exp $");
381571a7a1Sriastradh
391571a7a1Sriastradh #include <linux/export.h>
401571a7a1Sriastradh
411571a7a1Sriastradh #include <drm/drm_device.h>
421571a7a1Sriastradh #include <drm/drm_pci.h>
431571a7a1Sriastradh #include <drm/drm_print.h>
441571a7a1Sriastradh
451571a7a1Sriastradh #include "ati_pcigart.h"
461571a7a1Sriastradh
471571a7a1Sriastradh # define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */
481571a7a1Sriastradh
drm_ati_alloc_pcigart_table(struct drm_device * dev,struct drm_ati_pcigart_info * gart_info)491571a7a1Sriastradh static int drm_ati_alloc_pcigart_table(struct drm_device *dev,
501571a7a1Sriastradh struct drm_ati_pcigart_info *gart_info)
511571a7a1Sriastradh {
521571a7a1Sriastradh gart_info->table_handle = drm_pci_alloc(dev, gart_info->table_size,
531571a7a1Sriastradh PAGE_SIZE);
541571a7a1Sriastradh if (gart_info->table_handle == NULL)
551571a7a1Sriastradh return -ENOMEM;
561571a7a1Sriastradh
571571a7a1Sriastradh return 0;
581571a7a1Sriastradh }
591571a7a1Sriastradh
drm_ati_free_pcigart_table(struct drm_device * dev,struct drm_ati_pcigart_info * gart_info)601571a7a1Sriastradh static void drm_ati_free_pcigart_table(struct drm_device *dev,
611571a7a1Sriastradh struct drm_ati_pcigart_info *gart_info)
621571a7a1Sriastradh {
631571a7a1Sriastradh drm_pci_free(dev, gart_info->table_handle);
641571a7a1Sriastradh gart_info->table_handle = NULL;
651571a7a1Sriastradh }
661571a7a1Sriastradh
drm_ati_pcigart_cleanup(struct drm_device * dev,struct drm_ati_pcigart_info * gart_info)671571a7a1Sriastradh int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info)
681571a7a1Sriastradh {
691571a7a1Sriastradh struct drm_sg_mem *entry = dev->sg;
701571a7a1Sriastradh unsigned long pages;
711571a7a1Sriastradh int i;
721571a7a1Sriastradh int max_pages;
731571a7a1Sriastradh
741571a7a1Sriastradh /* we need to support large memory configurations */
751571a7a1Sriastradh if (!entry) {
761571a7a1Sriastradh DRM_ERROR("no scatter/gather memory!\n");
771571a7a1Sriastradh return 0;
781571a7a1Sriastradh }
791571a7a1Sriastradh
801571a7a1Sriastradh if (gart_info->bus_addr) {
811571a7a1Sriastradh
821571a7a1Sriastradh max_pages = (gart_info->table_size / sizeof(u32));
831571a7a1Sriastradh pages = (entry->pages <= max_pages)
841571a7a1Sriastradh ? entry->pages : max_pages;
851571a7a1Sriastradh
861571a7a1Sriastradh for (i = 0; i < pages; i++) {
871571a7a1Sriastradh if (!entry->busaddr[i])
881571a7a1Sriastradh break;
891571a7a1Sriastradh pci_unmap_page(dev->pdev, entry->busaddr[i],
901571a7a1Sriastradh PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
911571a7a1Sriastradh }
921571a7a1Sriastradh
931571a7a1Sriastradh if (gart_info->gart_table_location == DRM_ATI_GART_MAIN)
941571a7a1Sriastradh gart_info->bus_addr = 0;
951571a7a1Sriastradh }
961571a7a1Sriastradh
971571a7a1Sriastradh if (gart_info->gart_table_location == DRM_ATI_GART_MAIN &&
981571a7a1Sriastradh gart_info->table_handle) {
991571a7a1Sriastradh drm_ati_free_pcigart_table(dev, gart_info);
1001571a7a1Sriastradh }
1011571a7a1Sriastradh
1021571a7a1Sriastradh return 1;
1031571a7a1Sriastradh }
1041571a7a1Sriastradh
drm_ati_pcigart_init(struct drm_device * dev,struct drm_ati_pcigart_info * gart_info)1051571a7a1Sriastradh int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info)
1061571a7a1Sriastradh {
1071571a7a1Sriastradh struct drm_local_map *map = &gart_info->mapping;
1081571a7a1Sriastradh struct drm_sg_mem *entry = dev->sg;
1091571a7a1Sriastradh void *address = NULL;
1101571a7a1Sriastradh unsigned long pages;
1111571a7a1Sriastradh u32 *pci_gart = NULL, page_base, gart_idx;
1121571a7a1Sriastradh dma_addr_t bus_address = 0;
1131571a7a1Sriastradh int i, j, ret = -ENOMEM;
1141571a7a1Sriastradh int max_ati_pages, max_real_pages;
1151571a7a1Sriastradh
1161571a7a1Sriastradh if (!entry) {
1171571a7a1Sriastradh DRM_ERROR("no scatter/gather memory!\n");
1181571a7a1Sriastradh goto done;
1191571a7a1Sriastradh }
1201571a7a1Sriastradh
1211571a7a1Sriastradh if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) {
1221571a7a1Sriastradh DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n");
1231571a7a1Sriastradh
1241571a7a1Sriastradh if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) {
1251571a7a1Sriastradh DRM_ERROR("fail to set dma mask to 0x%Lx\n",
1261571a7a1Sriastradh (unsigned long long)gart_info->table_mask);
1271571a7a1Sriastradh ret = -EFAULT;
1281571a7a1Sriastradh goto done;
1291571a7a1Sriastradh }
1301571a7a1Sriastradh
1311571a7a1Sriastradh ret = drm_ati_alloc_pcigart_table(dev, gart_info);
1321571a7a1Sriastradh if (ret) {
1331571a7a1Sriastradh DRM_ERROR("cannot allocate PCI GART page!\n");
1341571a7a1Sriastradh goto done;
1351571a7a1Sriastradh }
1361571a7a1Sriastradh
1371571a7a1Sriastradh pci_gart = gart_info->table_handle->vaddr;
1381571a7a1Sriastradh address = gart_info->table_handle->vaddr;
1391571a7a1Sriastradh bus_address = gart_info->table_handle->busaddr;
1401571a7a1Sriastradh } else {
1411571a7a1Sriastradh address = gart_info->addr;
1421571a7a1Sriastradh bus_address = gart_info->bus_addr;
1431571a7a1Sriastradh DRM_DEBUG("PCI: Gart Table: VRAM %08LX mapped at %08lX\n",
1441571a7a1Sriastradh (unsigned long long)bus_address,
1451571a7a1Sriastradh (unsigned long)address);
1461571a7a1Sriastradh }
1471571a7a1Sriastradh
1481571a7a1Sriastradh
1491571a7a1Sriastradh max_ati_pages = (gart_info->table_size / sizeof(u32));
1501571a7a1Sriastradh max_real_pages = max_ati_pages / (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE);
1511571a7a1Sriastradh pages = (entry->pages <= max_real_pages)
1521571a7a1Sriastradh ? entry->pages : max_real_pages;
1531571a7a1Sriastradh
1541571a7a1Sriastradh if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) {
1551571a7a1Sriastradh memset(pci_gart, 0, max_ati_pages * sizeof(u32));
1561571a7a1Sriastradh } else {
1571571a7a1Sriastradh memset_io((void __iomem *)map->handle, 0, max_ati_pages * sizeof(u32));
1581571a7a1Sriastradh }
1591571a7a1Sriastradh
1601571a7a1Sriastradh gart_idx = 0;
1611571a7a1Sriastradh for (i = 0; i < pages; i++) {
1621571a7a1Sriastradh /* we need to support large memory configurations */
1631571a7a1Sriastradh entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i],
1641571a7a1Sriastradh 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
1651571a7a1Sriastradh if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) {
1661571a7a1Sriastradh DRM_ERROR("unable to map PCIGART pages!\n");
1671571a7a1Sriastradh drm_ati_pcigart_cleanup(dev, gart_info);
1681571a7a1Sriastradh address = NULL;
1691571a7a1Sriastradh bus_address = 0;
1701571a7a1Sriastradh ret = -ENOMEM;
1711571a7a1Sriastradh goto done;
1721571a7a1Sriastradh }
1731571a7a1Sriastradh page_base = (u32) entry->busaddr[i];
1741571a7a1Sriastradh
1751571a7a1Sriastradh for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) {
1761571a7a1Sriastradh u32 offset;
1771571a7a1Sriastradh u32 val;
1781571a7a1Sriastradh
1791571a7a1Sriastradh switch(gart_info->gart_reg_if) {
1801571a7a1Sriastradh case DRM_ATI_GART_IGP:
1811571a7a1Sriastradh val = page_base | 0xc;
1821571a7a1Sriastradh break;
1831571a7a1Sriastradh case DRM_ATI_GART_PCIE:
1841571a7a1Sriastradh val = (page_base >> 8) | 0xc;
1851571a7a1Sriastradh break;
1861571a7a1Sriastradh default:
1871571a7a1Sriastradh case DRM_ATI_GART_PCI:
1881571a7a1Sriastradh val = page_base;
1891571a7a1Sriastradh break;
1901571a7a1Sriastradh }
1911571a7a1Sriastradh if (gart_info->gart_table_location ==
1921571a7a1Sriastradh DRM_ATI_GART_MAIN) {
1931571a7a1Sriastradh pci_gart[gart_idx] = cpu_to_le32(val);
1941571a7a1Sriastradh } else {
1951571a7a1Sriastradh offset = gart_idx * sizeof(u32);
1961571a7a1Sriastradh writel(val, (void __iomem *)map->handle + offset);
1971571a7a1Sriastradh }
1981571a7a1Sriastradh gart_idx++;
1991571a7a1Sriastradh page_base += ATI_PCIGART_PAGE_SIZE;
2001571a7a1Sriastradh }
2011571a7a1Sriastradh }
2021571a7a1Sriastradh ret = 0;
2031571a7a1Sriastradh
2041571a7a1Sriastradh #if defined(__i386__) || defined(__x86_64__)
2051571a7a1Sriastradh wbinvd();
2061571a7a1Sriastradh #else
2071571a7a1Sriastradh mb();
2081571a7a1Sriastradh #endif
2091571a7a1Sriastradh
2101571a7a1Sriastradh done:
2111571a7a1Sriastradh gart_info->addr = address;
2121571a7a1Sriastradh gart_info->bus_addr = bus_address;
2131571a7a1Sriastradh return ret;
2141571a7a1Sriastradh }
215