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 **)®s_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)®s, &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 }