1 /*
   2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /* BEGIN CSTYLED */
   7 /**
   8  * \file drm_pci.h
   9  * \brief PCI consistent, DMA-accessible memory functions.
  10  *
  11  * \author Eric Anholt <anholt@FreeBSD.org>
  12  */
  13 
  14 /*-
  15  * Copyright 2003 Eric Anholt.
  16  * All Rights Reserved.
  17  *
  18  * Permission is hereby granted, free of charge, to any person obtaining a
  19  * copy of this software and associated documentation files (the "Software"),
  20  * to deal in the Software without restriction, including without limitation
  21  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  22  * and/or sell copies of the Software, and to permit persons to whom the
  23  * Software is furnished to do so, subject to the following conditions:
  24  *
  25  * The above copyright notice and this permission notice (including the next
  26  * paragraph) shall be included in all copies or substantial portions of the
  27  * Software.
  28  *
  29  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  30  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  31  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  32  * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  33  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  34  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  35  */
  36 
  37 /**********************************************************************/
  38 /** \name PCI memory */
  39 /*@{*/
  40 /* END CSTYLED */
  41 
  42 #pragma ident   "%Z%%M% %I%     %E% SMI"
  43 
  44 #include "drmP.h"
  45 #include <vm/seg_kmem.h>
  46 
  47 #define PCI_DEVICE(x)   (((x)>>11) & 0x1f)
  48 #define PCI_FUNCTION(x) (((x) & 0x700) >> 8)
  49 #define PCI_BUS(x)      (((x) & 0xff0000) >> 16)
  50 
  51 typedef struct drm_pci_resource {
  52         uint_t  regnum;
  53         unsigned long offset;
  54         unsigned long size;
  55 } drm_pci_resource_t;
  56 
  57 int
  58 pci_get_info(drm_device_t *softstate, int *bus, int *slot, int *func)
  59 {
  60         int *regs_list;
  61         uint_t nregs = 0;
  62 
  63         if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, softstate->dip,
  64             DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs)
  65             != DDI_PROP_SUCCESS) {
  66                 DRM_ERROR("pci_get_info: get pci function bus device failed");
  67                 goto error;
  68         }
  69         *bus  = (int)PCI_BUS(regs_list[0]);
  70         *slot = (int)PCI_DEVICE(regs_list[0]);
  71         *func = (int)PCI_FUNCTION(regs_list[0]);
  72 
  73         if (nregs > 0) {
  74                 ddi_prop_free(regs_list);
  75         }
  76         return (DDI_SUCCESS);
  77 error:
  78         if (nregs > 0) {
  79                 ddi_prop_free(regs_list);
  80         }
  81         return (DDI_FAILURE);
  82 }
  83 
  84 int
  85 pci_get_irq(drm_device_t *statep)
  86 {
  87         int irq;
  88 
  89         extern int drm_supp_get_irq(void *);
  90 
  91         irq = ddi_prop_get_int(DDI_DEV_T_ANY,
  92             statep->dip, DDI_PROP_DONTPASS, "interrupts", -1);
  93 
  94         if (irq > 0) {
  95                 irq = drm_supp_get_irq(statep->drm_handle);
  96         }
  97 
  98         return (irq);
  99 }
 100 
 101 int
 102 pci_get_vendor(drm_device_t *statep)
 103 {
 104         int vendorid;
 105 
 106         vendorid = ddi_prop_get_int(DDI_DEV_T_ANY,
 107             statep->dip, DDI_PROP_DONTPASS, "vendor-id", 0);
 108 
 109         return (vendorid);
 110 }
 111 
 112 int
 113 pci_get_device(drm_device_t *statep)
 114 {
 115         int deviceid;
 116 
 117         deviceid = ddi_prop_get_int(DDI_DEV_T_ANY,
 118             statep->dip, DDI_PROP_DONTPASS, "device-id", 0);
 119 
 120         return (deviceid);
 121 }
 122 
 123 void
 124 drm_core_ioremap(struct drm_local_map *map, drm_device_t *dev)
 125 {
 126         if ((map->type == _DRM_AGP) && dev->agp) {
 127                 /*
 128                  * During AGP mapping initialization, we map AGP aperture
 129                  * into kernel space. So, when we access the memory which
 130                  * managed by agp gart in kernel space, we have to go
 131                  * through two-level address translation: kernel virtual
 132                  * address --> aperture address --> physical address. For
 133                  * improving this, here in opensourced code, agp_remap()
 134                  * gets invoking to dispose the mapping between agp aperture
 135                  * and kernel space, and directly map the actual physical
 136                  * memory which is allocated to agp gart to kernel space.
 137                  * After that, access to physical memory managed by agp gart
 138                  * hardware in kernel space doesn't go through agp hardware,
 139                  * it will be: kernel virtual ---> physical address.
 140                  * Obviously, it is more efficient. But in solaris operating
 141                  * system, the ioctl AGPIOC_ALLOCATE of apggart driver does
 142                  * not return physical address. We are unable to create the
 143                  * direct mapping between kernel space and agp memory. So,
 144                  * we remove the calling to agp_remap().
 145                  */
 146                 DRM_DEBUG("drm_core_ioremap: skipping agp_remap\n");
 147         } else {
 148                 (void) drm_ioremap(dev, map);
 149 
 150         }
 151 }
 152 
 153 /*ARGSUSED*/
 154 void
 155 drm_core_ioremapfree(struct drm_local_map *map, drm_device_t *dev)
 156 {
 157         if (map->type != _DRM_AGP) {
 158                 if (map->handle && map->size)
 159                         drm_ioremapfree(map);
 160         } else {
 161                 /*
 162                  * Refer to the comments in drm_core_ioremap() where we removed
 163                  * the calling to agp_remap(), correspondingly, we remove the
 164                  * calling to agp_remap_free(dev, map);
 165                  */
 166                 DRM_DEBUG("drm_core_ioremap: skipping agp_remap_free\n");
 167         }
 168 }
 169 
 170 struct drm_local_map *
 171 drm_core_findmap(drm_device_t *dev, unsigned long handle)
 172 {
 173         drm_local_map_t *map;
 174 
 175         DRM_SPINLOCK_ASSERT(&dev->dev_lock);
 176 
 177 /*
 178  * For the time being, we compare the low 32 bit only,
 179  * We will hash handle to 32-bit to solve this issue later.
 180  */
 181         TAILQ_FOREACH(map, &dev->maplist, link) {
 182                 if ((((unsigned long)map->handle) & 0x00000000ffffffff)
 183                     == (handle & 0x00000000ffffffff))
 184                         return (map);
 185         }
 186 
 187         return (NULL);
 188 }
 189 
 190 /*
 191  * pci_alloc_consistent()
 192  */
 193 static ddi_dma_attr_t   hw_dma_attr = {
 194                 DMA_ATTR_V0,            /* version */
 195                 0,                      /* addr_lo */
 196                 0xffffffff,     /* addr_hi */
 197                 0xffffffff,     /* count_max */
 198                 4096,                   /* alignment */
 199                 0xfff,                  /* burstsize */
 200                 1,                      /* minxfer */
 201                 0xffffffff,             /* maxxfer */
 202                 0xffffffff,             /* seg */
 203                 1,                      /* sgllen */
 204                 4,                      /* granular */
 205                 0                       /* flags */
 206 };
 207 
 208 static ddi_device_acc_attr_t hw_acc_attr = {
 209         DDI_DEVICE_ATTR_V0,
 210         DDI_NEVERSWAP_ACC,
 211         DDI_STRICTORDER_ACC
 212 };
 213 
 214 
 215 void *
 216 drm_pci_alloc(drm_device_t *dev, size_t size,
 217     size_t align,  dma_addr_t maxaddr, int segments)
 218 {
 219         drm_dma_handle_t        *dmah;
 220         uint_t  count;
 221         int ret = DDI_FAILURE;
 222 
 223         /* allocat continous physical memory for hw status page */
 224         if (align == 0)
 225                 hw_dma_attr.dma_attr_align =  1;
 226         else
 227                 hw_dma_attr.dma_attr_align = align;
 228 
 229         hw_dma_attr.dma_attr_addr_hi = maxaddr;
 230         hw_dma_attr.dma_attr_sgllen = segments;
 231 
 232         dmah = kmem_zalloc(sizeof (drm_dma_handle_t), KM_SLEEP);
 233         if (ret = ddi_dma_alloc_handle(dev->dip, &hw_dma_attr,
 234             DDI_DMA_SLEEP, NULL, &dmah->dma_hdl)) {
 235                 DRM_ERROR("drm_pci_alloc:ddi_dma_alloc_handle failed\n");
 236                 goto err3;
 237         }
 238 
 239         if (ret = ddi_dma_mem_alloc(dmah->dma_hdl, size, &hw_acc_attr,
 240             DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
 241             DDI_DMA_SLEEP, NULL, (caddr_t *)&dmah->vaddr,
 242             &dmah->real_sz, &dmah->acc_hdl)) {
 243                 DRM_ERROR("drm_pci_alloc: ddi_dma_mem_alloc failed\n");
 244                 goto err2;
 245         }
 246 
 247         ret = ddi_dma_addr_bind_handle(dmah->dma_hdl, NULL,
 248             (caddr_t)dmah->vaddr, dmah->real_sz,
 249             DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
 250             DDI_DMA_SLEEP, NULL, &dmah->cookie,  &count);
 251         if (ret != DDI_DMA_MAPPED) {
 252                 DRM_ERROR("drm_pci_alloc: alloc phys memory failed");
 253                 goto err1;
 254         }
 255 
 256         if (count > segments) {
 257                 (void) ddi_dma_unbind_handle(dmah->dma_hdl);
 258                 goto err1;
 259         }
 260 
 261         dmah->cookie_num = count;
 262         if (count == 1)
 263                 dmah->paddr = dmah->cookie.dmac_address;
 264 
 265         return (dmah);
 266 
 267 err1:
 268         ddi_dma_mem_free(&dmah->acc_hdl);
 269 err2:
 270         ddi_dma_free_handle(&dmah->dma_hdl);
 271 err3:
 272         kmem_free(dmah, sizeof (*dmah));
 273         return (NULL);
 274 }
 275 
 276 /*
 277  * pci_free_consistent()
 278  */
 279 /*ARGSUSED*/
 280 void
 281 drm_pci_free(drm_device_t *dev, drm_dma_handle_t *dmah)
 282 {
 283         ASSERT(dmah != NULL);
 284         (void) ddi_dma_unbind_handle(dmah->dma_hdl);
 285         ddi_dma_mem_free(&dmah->acc_hdl);
 286         ddi_dma_free_handle(&dmah->dma_hdl);
 287         kmem_free(dmah, sizeof (drm_dma_handle_t));
 288 }
 289 
 290 int
 291 do_get_pci_res(drm_device_t *dev, drm_pci_resource_t *resp)
 292 {
 293         int length;
 294         pci_regspec_t   *regs;
 295 
 296         if (ddi_getlongprop(
 297             DDI_DEV_T_ANY, dev->dip, DDI_PROP_DONTPASS,
 298             "assigned-addresses", (caddr_t)&regs, &length) !=
 299             DDI_PROP_SUCCESS) {
 300                 DRM_ERROR("do_get_pci_res: ddi_getlongprop failed!\n");
 301                 return (EFAULT);
 302         }
 303         resp->offset =
 304             (unsigned long)regs[resp->regnum].pci_phys_low;
 305         resp->size =
 306             (unsigned long)regs[resp->regnum].pci_size_low;
 307         kmem_free(regs, (size_t)length);
 308 
 309         return (0);
 310 }
 311 
 312 /*ARGSUSED*/
 313 unsigned long
 314 drm_get_resource_start(drm_device_t *softstate, unsigned int regnum)
 315 {
 316         drm_pci_resource_t res;
 317         int ret;
 318 
 319         res.regnum = regnum;
 320 
 321         ret = do_get_pci_res(softstate, &res);
 322 
 323         if (ret != 0) {
 324                 DRM_ERROR("drm_get_resource_start: ioctl failed");
 325                 return (0);
 326         }
 327 
 328         return (res.offset);
 329 
 330 }
 331 
 332 /*ARGSUSED*/
 333 unsigned long
 334 drm_get_resource_len(drm_device_t *softstate, unsigned int regnum)
 335 {
 336         drm_pci_resource_t res;
 337         int ret;
 338 
 339         res.regnum = regnum;
 340 
 341         ret = do_get_pci_res(softstate, &res);
 342 
 343         if (ret != 0) {
 344                 DRM_ERROR("drm_get_resource_len: ioctl failed");
 345                 return (0);
 346         }
 347 
 348         return (res.size);
 349 }