drm_pci.c revision 1450
1185N/A/*
1185N/A * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
1185N/A */
1185N/A
1185N/A/**
1185N/A * \file drm_pci.h
1185N/A * \brief PCI consistent, DMA-accessible memory functions.
1185N/A *
1185N/A * \author Eric Anholt <anholt@FreeBSD.org>
1185N/A */
1185N/A
1185N/A/*-
1185N/A * Copyright 2003 Eric Anholt.
1185N/A * All Rights Reserved.
1185N/A *
1185N/A * Permission is hereby granted, free of charge, to any person obtaining a
1185N/A * copy of this software and associated documentation files (the "Software"),
1185N/A * to deal in the Software without restriction, including without limitation
1220N/A * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1185N/A * and/or sell copies of the Software, and to permit persons to whom the
1185N/A * Software is furnished to do so, subject to the following conditions:
1458N/A *
1185N/A * The above copyright notice and this permission notice (including the next
1185N/A * paragraph) shall be included in all copies or substantial portions of the
1185N/A * Software.
1185N/A *
1185N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1185N/A * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1185N/A * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1185N/A * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1185N/A * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1185N/A * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1244N/A */
1244N/A
1185N/A/**********************************************************************/
1198N/A/** \name PCI memory */
1213N/A/*@{*/
1185N/A
1194N/A#include "drmP.h"
1194N/A#include <vm/seg_kmem.h>
1185N/A
1185N/A#define PCI_DEVICE(x) (((x)>>11) & 0x1f)
1367N/A#define PCI_FUNCTION(x) (((x) & 0x700) >> 8)
1185N/A#define PCI_BUS(x) (((x) & 0xff0000) >> 16)
1185N/A
1185N/Atypedef struct drm_pci_resource {
1185N/A uint_t regnum;
1185N/A unsigned long offset;
1185N/A unsigned long size;
1185N/A} drm_pci_resource_t;
1185N/A
1470N/A
1185N/Aint
1185N/Apci_get_info(drm_device_t *softstate, int *bus, int *slot, int *func)
1185N/A{
1185N/A int *regs_list;
1185N/A uint_t nregs = 0;
1185N/A
1194N/A if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, softstate->dip,
1185N/A DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs)
1185N/A != DDI_PROP_SUCCESS) {
1190N/A DRM_ERROR("pci_get_info: get pci function bus device failed");
1367N/A goto error;
1367N/A }
1367N/A *bus = (int)PCI_BUS(regs_list[0]);
1185N/A *slot = (int)PCI_DEVICE(regs_list[0]);
1190N/A *func = (int)PCI_FUNCTION(regs_list[0]);
1190N/A
1190N/A if (nregs > 0) {
1185N/A ddi_prop_free(regs_list);
1190N/A }
1185N/A return (DDI_SUCCESS);
1185N/Aerror:
1185N/A if (nregs > 0) {
1185N/A ddi_prop_free(regs_list);
1190N/A }
1185N/A return (DDI_FAILURE);
1185N/A}
1185N/A
1210N/Aint
1185N/Apci_get_irq(drm_device_t *statep)
1185N/A{
1185N/A int irq;
1470N/A
1185N/A extern int drm_supp_get_irq(void *);
1185N/A
1185N/A irq = ddi_prop_get_int(DDI_DEV_T_ANY,
1185N/A statep->dip, DDI_PROP_DONTPASS, "interrupts", -1);
1185N/A
1185N/A if (irq > 0) {
1185N/A irq = drm_supp_get_irq(statep->drm_handle);
1185N/A }
1185N/A
1185N/A return (irq);
1185N/A}
1185N/A
1185N/Aint
1185N/Apci_get_vendor(drm_device_t *statep)
1185N/A{
1185N/A int vendorid;
1213N/A
1185N/A vendorid = ddi_prop_get_int(DDI_DEV_T_ANY,
1244N/A statep->dip, DDI_PROP_DONTPASS, "vendor-id", 0);
1185N/A
1185N/A return (vendorid);
1185N/A}
1211N/A
1211N/Aint
1185N/Apci_get_device(drm_device_t *statep)
1190N/A{
1185N/A int deviceid;
1190N/A
1185N/A deviceid = ddi_prop_get_int(DDI_DEV_T_ANY,
1185N/A statep->dip, DDI_PROP_DONTPASS, "device-id", 0);
1185N/A
1185N/A return (deviceid);
1185N/A}
1185N/A
1185N/Avoid
1185N/Adrm_core_ioremap(struct drm_local_map *map, drm_device_t *dev)
1185N/A{
1185N/A if ((map->type == _DRM_AGP) && dev->agp) {
1185N/A /*
1185N/A * During AGP mapping initialization, we map AGP aperture
1185N/A * into kernel space. So, when we access the memory which
1190N/A * managed by agp gart in kernel space, we have to go
1185N/A * through two-level address translation: kernel virtual
1185N/A * address --> aperture address --> physical address. For
1185N/A * improving this, here in opensourced code, agp_remap()
1185N/A * gets invoking to dispose the mapping between agp aperture
1185N/A * and kernel space, and directly map the actual physical
1185N/A * memory which is allocated to agp gart to kernel space.
1185N/A * After that, access to physical memory managed by agp gart
1185N/A * hardware in kernel space doesn't go through agp hardware,
1185N/A * it will be: kernel virtual ---> physical address.
1185N/A * Obviously, it is more efficient. But in solaris operating
1190N/A * system, the ioctl AGPIOC_ALLOCATE of apggart driver does
1190N/A * not return physical address. We are unable to create the
1190N/A * direct mapping between kernel space and agp memory. So,
1185N/A * we remove the calling to agp_remap().
1465N/A */
1185N/A DRM_DEBUG("drm_core_ioremap: skipping agp_remap\n");
1461N/A } else {
1185N/A (void) drm_ioremap(dev, map);
1185N/A
1388N/A }
1185N/A}
1185N/A
1185N/Avoid
1185N/Adrm_core_ioremapfree(struct drm_local_map *map, drm_device_t *dev)
1388N/A{
1212N/A _NOTE(ARGUNUSED(dev))
1185N/A
1185N/A if (map->type != _DRM_AGP) {
1185N/A if (map->handle && map->size)
1185N/A drm_ioremapfree(map);
1185N/A } else {
1185N/A /*
1185N/A * Refer to the comments in drm_core_ioremap() where we removed
1212N/A * the calling to agp_remap(), correspondingly, we remove the
1388N/A * calling to agp_remap_free(dev, map);
1185N/A */
1185N/A DRM_DEBUG("drm_core_ioremap: skipping agp_remap_free\n");
1465N/A }
1465N/A}
1388N/A
1388N/Astruct drm_local_map *
1388N/Adrm_core_findmap(drm_device_t *dev, unsigned long handle)
1388N/A{
1185N/A drm_local_map_t *map;
1388N/A
1388N/A DRM_SPINLOCK_ASSERT(&dev->dev_lock);
1185N/A
1185N/A/*
1388N/A * For the time being, we compare the low 32 bit only,
1226N/A * We will hash handle to 32-bit to solve this issue later.
1465N/A */
1465N/A TAILQ_FOREACH(map, &dev->maplist, link) {
1465N/A if ((((unsigned long)map->handle) & 0x00000000ffffffff)
1465N/A == (handle & 0x00000000ffffffff))
1465N/A return (map);
1465N/A }
1465N/A
1465N/A return (NULL);
1465N/A}
1465N/A
1465N/A/*
1469N/A * pci_alloc_consistent()
1465N/A */
1465N/Astatic ddi_dma_attr_t hw_dma_attr = {
1185N/A DMA_ATTR_V0, /* version */
1465N/A 0, /* addr_lo */
1465N/A 0xffffffff, /* addr_hi */
1465N/A 0xffffffff, /* count_max */
1465N/A 4096, /* alignment */
1465N/A 0xfff, /* burstsize */
1465N/A 1, /* minxfer */
1465N/A 0xffffffff, /* maxxfer */
1185N/A 0xffffffff, /* seg */
1465N/A 1, /* sgllen */
1465N/A 4, /* granular */
1465N/A 0 /* flags */
1226N/A};
1226N/A
1185N/Astatic ddi_device_acc_attr_t hw_acc_attr = {
1465N/A DDI_DEVICE_ATTR_V0,
1465N/A DDI_NEVERSWAP_ACC,
1465N/A DDI_STRICTORDER_ACC
1465N/A};
1465N/A
1465N/A
1465N/Avoid *
1185N/Adrm_pci_alloc(drm_device_t *dev, size_t size,
1185N/A size_t align, dma_addr_t maxaddr, int segments)
1465N/A{
1465N/A drm_dma_handle_t *dmah;
1465N/A uint_t count;
1465N/A int ret = DDI_FAILURE;
1465N/A
1465N/A /* allocat continous physical memory for hw status page */
1465N/A if (align == 0)
1465N/A hw_dma_attr.dma_attr_align = 1;
1465N/A else
1465N/A hw_dma_attr.dma_attr_align = align;
1465N/A
1465N/A hw_dma_attr.dma_attr_addr_hi = maxaddr;
1465N/A hw_dma_attr.dma_attr_sgllen = segments;
1465N/A
1465N/A dmah = kmem_zalloc(sizeof (drm_dma_handle_t), KM_SLEEP);
1185N/A if (ret = ddi_dma_alloc_handle(dev->dip, &hw_dma_attr,
1465N/A DDI_DMA_SLEEP, NULL, &dmah->dma_hdl)) {
1465N/A DRM_ERROR("drm_pci_alloc:ddi_dma_alloc_handle failed\n");
1465N/A goto err3;
1465N/A }
1465N/A
1185N/A if (ret = ddi_dma_mem_alloc(dmah->dma_hdl, size, &hw_acc_attr,
1465N/A DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
1185N/A DDI_DMA_SLEEP, NULL, (caddr_t *)&dmah->vaddr,
1465N/A &dmah->real_sz, &dmah->acc_hdl)) {
1465N/A DRM_ERROR("drm_pci_alloc: ddi_dma_mem_alloc failed\n");
1465N/A goto err2;
1465N/A }
1465N/A
1465N/A ret = ddi_dma_addr_bind_handle(dmah->dma_hdl, NULL,
1465N/A (caddr_t)dmah->vaddr, dmah->real_sz,
1465N/A DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
1465N/A DDI_DMA_SLEEP, NULL, &dmah->cookie, &count);
1185N/A if (ret != DDI_DMA_MAPPED) {
1465N/A DRM_ERROR("drm_pci_alloc: alloc phys memory failed");
1465N/A goto err1;
1469N/A }
1465N/A
1465N/A if (count > segments) {
1465N/A (void) ddi_dma_unbind_handle(dmah->dma_hdl);
1465N/A goto err1;
1465N/A }
1465N/A
1185N/A dmah->cookie_num = count;
1185N/A if (count == 1)
1185N/A dmah->paddr = dmah->cookie.dmac_address;
1185N/A
1185N/A return (dmah);
1185N/A
1185N/Aerr1:
1185N/A ddi_dma_mem_free(&dmah->acc_hdl);
1185N/Aerr2:
1185N/A ddi_dma_free_handle(&dmah->dma_hdl);
1185N/Aerr3:
1185N/A kmem_free(dmah, sizeof (*dmah));
1185N/A return (NULL);
1185N/A}
1185N/A
1185N/A/*
1185N/A * pci_free_consistent()
1190N/A */
1190N/A/*ARGSUSED*/
1190N/Avoid
1185N/Adrm_pci_free(drm_device_t *dev, drm_dma_handle_t *dmah)
1185N/A{
1185N/A ASSERT(dmah != NULL);
1185N/A (void) ddi_dma_unbind_handle(dmah->dma_hdl);
1185N/A ddi_dma_mem_free(&dmah->acc_hdl);
1185N/A ddi_dma_free_handle(&dmah->dma_hdl);
1185N/A kmem_free(dmah, sizeof (drm_dma_handle_t));
1226N/A}
1185N/A
1190N/Aint
1185N/Ado_get_pci_res(drm_device_t *dev, drm_pci_resource_t *resp)
1190N/A{
1190N/A int length;
1226N/A pci_regspec_t *regs;
1190N/A
1185N/A if (ddi_getlongprop(
1474N/A DDI_DEV_T_ANY, dev->dip, DDI_PROP_DONTPASS,
1185N/A "assigned-addresses", (caddr_t)&regs, &length) !=
1185N/A DDI_PROP_SUCCESS) {
1474N/A DRM_ERROR("do_get_pci_res: ddi_getlongprop failed!\n");
1474N/A return (EFAULT);
1185N/A }
1185N/A resp->offset =
1473N/A (unsigned long)regs[resp->regnum].pci_phys_low;
1473N/A resp->size =
1473N/A (unsigned long)regs[resp->regnum].pci_size_low;
1473N/A kmem_free(regs, (size_t)length);
1473N/A
1473N/A return (0);
1473N/A}
1473N/A
1473N/Aunsigned long
1473N/Adrm_get_resource_start(drm_device_t *softstate, unsigned int regnum)
1466N/A{
1466N/A drm_pci_resource_t res;
1474N/A int ret;
1474N/A
1474N/A res.regnum = regnum;
1474N/A
1474N/A ret = do_get_pci_res(softstate, &res);
1474N/A
1474N/A if (ret != 0) {
1474N/A DRM_ERROR("drm_get_resource_start: ioctl failed");
1474N/A return (0);
1474N/A }
1474N/A
1474N/A return (res.offset);
1474N/A
1185N/A}
1475N/A
1475N/Aunsigned long
1475N/Adrm_get_resource_len(drm_device_t *softstate, unsigned int regnum)
1185N/A{
1474N/A drm_pci_resource_t res;
1474N/A int ret;
1474N/A
1474N/A res.regnum = regnum;
1477N/A
1477N/A ret = do_get_pci_res(softstate, &res);
1477N/A
1474N/A if (ret != 0) {
1474N/A DRM_ERROR("drm_get_resource_len: ioctl failed");
1474N/A return (0);
1185N/A }
1185N/A
1185N/A return (res.size);
1466N/A}
1474N/A