12820Skz151634 /*
2*5804Scg149915 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
32820Skz151634 * Use is subject to license terms.
42820Skz151634 */
52820Skz151634
62820Skz151634 /* BEGIN CSTYLED */
72820Skz151634 /**
82820Skz151634 * \file drm_pci.h
92820Skz151634 * \brief PCI consistent, DMA-accessible memory functions.
102820Skz151634 *
112820Skz151634 * \author Eric Anholt <anholt@FreeBSD.org>
122820Skz151634 */
132820Skz151634
142820Skz151634 /*-
152820Skz151634 * Copyright 2003 Eric Anholt.
162820Skz151634 * All Rights Reserved.
172820Skz151634 *
182820Skz151634 * Permission is hereby granted, free of charge, to any person obtaining a
192820Skz151634 * copy of this software and associated documentation files (the "Software"),
202820Skz151634 * to deal in the Software without restriction, including without limitation
212820Skz151634 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
222820Skz151634 * and/or sell copies of the Software, and to permit persons to whom the
232820Skz151634 * Software is furnished to do so, subject to the following conditions:
242820Skz151634 *
252820Skz151634 * The above copyright notice and this permission notice (including the next
262820Skz151634 * paragraph) shall be included in all copies or substantial portions of the
272820Skz151634 * Software.
282820Skz151634 *
292820Skz151634 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
302820Skz151634 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
312820Skz151634 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
322820Skz151634 * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
332820Skz151634 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
342820Skz151634 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
352820Skz151634 */
362820Skz151634
372820Skz151634 /**********************************************************************/
382820Skz151634 /** \name PCI memory */
392820Skz151634 /*@{*/
402820Skz151634 /* END CSTYLED */
412820Skz151634
422820Skz151634 #pragma ident "%Z%%M% %I% %E% SMI"
432820Skz151634
442820Skz151634 #include "drmP.h"
45*5804Scg149915 #include <vm/seg_kmem.h>
462820Skz151634
472820Skz151634 #define PCI_DEVICE(x) (((x)>>11) & 0x1f)
482820Skz151634 #define PCI_FUNCTION(x) (((x) & 0x700) >> 8)
492820Skz151634 #define PCI_BUS(x) (((x) & 0xff0000) >> 16)
502820Skz151634
512820Skz151634 typedef struct drm_pci_resource {
522820Skz151634 uint_t regnum;
532820Skz151634 unsigned long offset;
542820Skz151634 unsigned long size;
552820Skz151634 } drm_pci_resource_t;
562820Skz151634
572820Skz151634 int
pci_get_info(drm_device_t * softstate,int * bus,int * slot,int * func)58*5804Scg149915 pci_get_info(drm_device_t *softstate, int *bus, int *slot, int *func)
592820Skz151634 {
602820Skz151634 int *regs_list;
612820Skz151634 uint_t nregs = 0;
622820Skz151634
632820Skz151634 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, softstate->dip,
642820Skz151634 DDI_PROP_DONTPASS, "reg", (int **)®s_list, &nregs)
652820Skz151634 != DDI_PROP_SUCCESS) {
662820Skz151634 DRM_ERROR("pci_get_info: get pci function bus device failed");
672820Skz151634 goto error;
682820Skz151634 }
692820Skz151634 *bus = (int)PCI_BUS(regs_list[0]);
702820Skz151634 *slot = (int)PCI_DEVICE(regs_list[0]);
712820Skz151634 *func = (int)PCI_FUNCTION(regs_list[0]);
722820Skz151634
732820Skz151634 if (nregs > 0) {
742820Skz151634 ddi_prop_free(regs_list);
752820Skz151634 }
762820Skz151634 return (DDI_SUCCESS);
772820Skz151634 error:
782820Skz151634 if (nregs > 0) {
792820Skz151634 ddi_prop_free(regs_list);
802820Skz151634 }
812820Skz151634 return (DDI_FAILURE);
822820Skz151634 }
832820Skz151634
842820Skz151634 int
pci_get_irq(drm_device_t * statep)85*5804Scg149915 pci_get_irq(drm_device_t *statep)
862820Skz151634 {
87*5804Scg149915 int irq;
88*5804Scg149915
89*5804Scg149915 extern int drm_supp_get_irq(void *);
902820Skz151634
91*5804Scg149915 irq = ddi_prop_get_int(DDI_DEV_T_ANY,
92*5804Scg149915 statep->dip, DDI_PROP_DONTPASS, "interrupts", -1);
932820Skz151634
94*5804Scg149915 if (irq > 0) {
95*5804Scg149915 irq = drm_supp_get_irq(statep->drm_handle);
96*5804Scg149915 }
97*5804Scg149915
98*5804Scg149915 return (irq);
992820Skz151634 }
1002820Skz151634
1012820Skz151634 int
pci_get_vendor(drm_device_t * statep)102*5804Scg149915 pci_get_vendor(drm_device_t *statep)
1032820Skz151634 {
104*5804Scg149915 int vendorid;
1052820Skz151634
106*5804Scg149915 vendorid = ddi_prop_get_int(DDI_DEV_T_ANY,
107*5804Scg149915 statep->dip, DDI_PROP_DONTPASS, "vendor-id", 0);
1082820Skz151634
109*5804Scg149915 return (vendorid);
1102820Skz151634 }
1112820Skz151634
1122820Skz151634 int
pci_get_device(drm_device_t * statep)113*5804Scg149915 pci_get_device(drm_device_t *statep)
1142820Skz151634 {
115*5804Scg149915 int deviceid;
1162820Skz151634
117*5804Scg149915 deviceid = ddi_prop_get_int(DDI_DEV_T_ANY,
118*5804Scg149915 statep->dip, DDI_PROP_DONTPASS, "device-id", 0);
1192820Skz151634
120*5804Scg149915 return (deviceid);
1212820Skz151634 }
1222820Skz151634
1232820Skz151634 void
drm_core_ioremap(struct drm_local_map * map,drm_device_t * dev)124*5804Scg149915 drm_core_ioremap(struct drm_local_map *map, drm_device_t *dev)
1252820Skz151634 {
126*5804Scg149915 if ((map->type == _DRM_AGP) && dev->agp) {
127*5804Scg149915 /*
128*5804Scg149915 * During AGP mapping initialization, we map AGP aperture
129*5804Scg149915 * into kernel space. So, when we access the memory which
130*5804Scg149915 * managed by agp gart in kernel space, we have to go
131*5804Scg149915 * through two-level address translation: kernel virtual
132*5804Scg149915 * address --> aperture address --> physical address. For
133*5804Scg149915 * improving this, here in opensourced code, agp_remap()
134*5804Scg149915 * gets invoking to dispose the mapping between agp aperture
135*5804Scg149915 * and kernel space, and directly map the actual physical
136*5804Scg149915 * memory which is allocated to agp gart to kernel space.
137*5804Scg149915 * After that, access to physical memory managed by agp gart
138*5804Scg149915 * hardware in kernel space doesn't go through agp hardware,
139*5804Scg149915 * it will be: kernel virtual ---> physical address.
140*5804Scg149915 * Obviously, it is more efficient. But in solaris operating
141*5804Scg149915 * system, the ioctl AGPIOC_ALLOCATE of apggart driver does
142*5804Scg149915 * not return physical address. We are unable to create the
143*5804Scg149915 * direct mapping between kernel space and agp memory. So,
144*5804Scg149915 * we remove the calling to agp_remap().
145*5804Scg149915 */
146*5804Scg149915 DRM_DEBUG("drm_core_ioremap: skipping agp_remap\n");
147*5804Scg149915 } else {
148*5804Scg149915 (void) drm_ioremap(dev, map);
1492820Skz151634
150*5804Scg149915 }
1512820Skz151634 }
1522820Skz151634
1532820Skz151634 /*ARGSUSED*/
1542820Skz151634 void
drm_core_ioremapfree(struct drm_local_map * map,drm_device_t * dev)155*5804Scg149915 drm_core_ioremapfree(struct drm_local_map *map, drm_device_t *dev)
1562820Skz151634 {
157*5804Scg149915 if (map->type != _DRM_AGP) {
1582820Skz151634 if (map->handle && map->size)
1592820Skz151634 drm_ioremapfree(map);
1602820Skz151634 } else {
161*5804Scg149915 /*
162*5804Scg149915 * Refer to the comments in drm_core_ioremap() where we removed
163*5804Scg149915 * the calling to agp_remap(), correspondingly, we remove the
164*5804Scg149915 * calling to agp_remap_free(dev, map);
165*5804Scg149915 */
166*5804Scg149915 DRM_DEBUG("drm_core_ioremap: skipping agp_remap_free\n");
1672820Skz151634 }
1682820Skz151634 }
1692820Skz151634
1702820Skz151634 struct drm_local_map *
drm_core_findmap(drm_device_t * dev,unsigned long handle)171*5804Scg149915 drm_core_findmap(drm_device_t *dev, unsigned long handle)
1722820Skz151634 {
1732820Skz151634 drm_local_map_t *map;
1742820Skz151634
1752820Skz151634 DRM_SPINLOCK_ASSERT(&dev->dev_lock);
176*5804Scg149915
177*5804Scg149915 /*
178*5804Scg149915 * For the time being, we compare the low 32 bit only,
179*5804Scg149915 * We will hash handle to 32-bit to solve this issue later.
180*5804Scg149915 */
1812820Skz151634 TAILQ_FOREACH(map, &dev->maplist, link) {
182*5804Scg149915 if ((((unsigned long)map->handle) & 0x00000000ffffffff)
183*5804Scg149915 == (handle & 0x00000000ffffffff))
1842820Skz151634 return (map);
1852820Skz151634 }
186*5804Scg149915
1872820Skz151634 return (NULL);
1882820Skz151634 }
1892820Skz151634
1902820Skz151634 /*
1912820Skz151634 * pci_alloc_consistent()
1922820Skz151634 */
193*5804Scg149915 static ddi_dma_attr_t hw_dma_attr = {
194*5804Scg149915 DMA_ATTR_V0, /* version */
195*5804Scg149915 0, /* addr_lo */
196*5804Scg149915 0xffffffff, /* addr_hi */
197*5804Scg149915 0xffffffff, /* count_max */
198*5804Scg149915 4096, /* alignment */
199*5804Scg149915 0xfff, /* burstsize */
200*5804Scg149915 1, /* minxfer */
201*5804Scg149915 0xffffffff, /* maxxfer */
202*5804Scg149915 0xffffffff, /* seg */
203*5804Scg149915 1, /* sgllen */
204*5804Scg149915 4, /* granular */
205*5804Scg149915 0 /* flags */
2062820Skz151634 };
2072820Skz151634
2082820Skz151634 static ddi_device_acc_attr_t hw_acc_attr = {
2092820Skz151634 DDI_DEVICE_ATTR_V0,
2102820Skz151634 DDI_NEVERSWAP_ACC,
2112820Skz151634 DDI_STRICTORDER_ACC
2122820Skz151634 };
2132820Skz151634
214*5804Scg149915
2152820Skz151634 void *
drm_pci_alloc(drm_device_t * dev,size_t size,size_t align,dma_addr_t maxaddr,int segments)216*5804Scg149915 drm_pci_alloc(drm_device_t *dev, size_t size,
217*5804Scg149915 size_t align, dma_addr_t maxaddr, int segments)
2182820Skz151634 {
219*5804Scg149915 drm_dma_handle_t *dmah;
220*5804Scg149915 uint_t count;
2212820Skz151634 int ret = DDI_FAILURE;
2222820Skz151634
2232820Skz151634 /* allocat continous physical memory for hw status page */
224*5804Scg149915 if (align == 0)
225*5804Scg149915 hw_dma_attr.dma_attr_align = 1;
226*5804Scg149915 else
227*5804Scg149915 hw_dma_attr.dma_attr_align = align;
2282820Skz151634
229*5804Scg149915 hw_dma_attr.dma_attr_addr_hi = maxaddr;
230*5804Scg149915 hw_dma_attr.dma_attr_sgllen = segments;
231*5804Scg149915
232*5804Scg149915 dmah = kmem_zalloc(sizeof (drm_dma_handle_t), KM_SLEEP);
233*5804Scg149915 if (ret = ddi_dma_alloc_handle(dev->dip, &hw_dma_attr,
234*5804Scg149915 DDI_DMA_SLEEP, NULL, &dmah->dma_hdl)) {
2352820Skz151634 DRM_ERROR("drm_pci_alloc:ddi_dma_alloc_handle failed\n");
2362820Skz151634 goto err3;
2372820Skz151634 }
2382820Skz151634
239*5804Scg149915 if (ret = ddi_dma_mem_alloc(dmah->dma_hdl, size, &hw_acc_attr,
240*5804Scg149915 DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
241*5804Scg149915 DDI_DMA_SLEEP, NULL, (caddr_t *)&dmah->vaddr,
242*5804Scg149915 &dmah->real_sz, &dmah->acc_hdl)) {
2432820Skz151634 DRM_ERROR("drm_pci_alloc: ddi_dma_mem_alloc failed\n");
2442820Skz151634 goto err2;
2452820Skz151634 }
2462820Skz151634
247*5804Scg149915 ret = ddi_dma_addr_bind_handle(dmah->dma_hdl, NULL,
248*5804Scg149915 (caddr_t)dmah->vaddr, dmah->real_sz,
249*5804Scg149915 DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
250*5804Scg149915 DDI_DMA_SLEEP, NULL, &dmah->cookie, &count);
251*5804Scg149915 if (ret != DDI_DMA_MAPPED) {
252*5804Scg149915 DRM_ERROR("drm_pci_alloc: alloc phys memory failed");
253*5804Scg149915 goto err1;
254*5804Scg149915 }
2552820Skz151634
256*5804Scg149915 if (count > segments) {
257*5804Scg149915 (void) ddi_dma_unbind_handle(dmah->dma_hdl);
2582820Skz151634 goto err1;
2592820Skz151634 }
260*5804Scg149915
261*5804Scg149915 dmah->cookie_num = count;
262*5804Scg149915 if (count == 1)
263*5804Scg149915 dmah->paddr = dmah->cookie.dmac_address;
264*5804Scg149915
265*5804Scg149915 return (dmah);
2662820Skz151634
2672820Skz151634 err1:
268*5804Scg149915 ddi_dma_mem_free(&dmah->acc_hdl);
2692820Skz151634 err2:
270*5804Scg149915 ddi_dma_free_handle(&dmah->dma_hdl);
2712820Skz151634 err3:
272*5804Scg149915 kmem_free(dmah, sizeof (*dmah));
2732820Skz151634 return (NULL);
2742820Skz151634 }
2752820Skz151634
2762820Skz151634 /*
2772820Skz151634 * pci_free_consistent()
2782820Skz151634 */
2792820Skz151634 /*ARGSUSED*/
2802820Skz151634 void
drm_pci_free(drm_device_t * dev,drm_dma_handle_t * dmah)281*5804Scg149915 drm_pci_free(drm_device_t *dev, drm_dma_handle_t *dmah)
2822820Skz151634 {
283*5804Scg149915 ASSERT(dmah != NULL);
284*5804Scg149915 (void) ddi_dma_unbind_handle(dmah->dma_hdl);
285*5804Scg149915 ddi_dma_mem_free(&dmah->acc_hdl);
286*5804Scg149915 ddi_dma_free_handle(&dmah->dma_hdl);
287*5804Scg149915 kmem_free(dmah, sizeof (drm_dma_handle_t));
2882820Skz151634 }
2892820Skz151634
2902820Skz151634 int
do_get_pci_res(drm_device_t * dev,drm_pci_resource_t * resp)291*5804Scg149915 do_get_pci_res(drm_device_t *dev, drm_pci_resource_t *resp)
2922820Skz151634 {
2932820Skz151634 int length;
2942820Skz151634 pci_regspec_t *regs;
2952820Skz151634
2962820Skz151634 if (ddi_getlongprop(
297*5804Scg149915 DDI_DEV_T_ANY, dev->dip, DDI_PROP_DONTPASS,
2982820Skz151634 "assigned-addresses", (caddr_t)®s, &length) !=
2992820Skz151634 DDI_PROP_SUCCESS) {
3002820Skz151634 DRM_ERROR("do_get_pci_res: ddi_getlongprop failed!\n");
301*5804Scg149915 return (EFAULT);
3022820Skz151634 }
3032820Skz151634 resp->offset =
3042820Skz151634 (unsigned long)regs[resp->regnum].pci_phys_low;
3052820Skz151634 resp->size =
3062820Skz151634 (unsigned long)regs[resp->regnum].pci_size_low;
3072820Skz151634 kmem_free(regs, (size_t)length);
3082820Skz151634
309*5804Scg149915 return (0);
3102820Skz151634 }
3112820Skz151634
3122820Skz151634 /*ARGSUSED*/
3132820Skz151634 unsigned long
drm_get_resource_start(drm_device_t * softstate,unsigned int regnum)314*5804Scg149915 drm_get_resource_start(drm_device_t *softstate, unsigned int regnum)
3152820Skz151634 {
3162820Skz151634 drm_pci_resource_t res;
3172820Skz151634 int ret;
3182820Skz151634
3192820Skz151634 res.regnum = regnum;
3202820Skz151634
3212820Skz151634 ret = do_get_pci_res(softstate, &res);
3222820Skz151634
323*5804Scg149915 if (ret != 0) {
324*5804Scg149915 DRM_ERROR("drm_get_resource_start: ioctl failed");
3252820Skz151634 return (0);
3262820Skz151634 }
3272820Skz151634
3282820Skz151634 return (res.offset);
3292820Skz151634
3302820Skz151634 }
3312820Skz151634
3322820Skz151634 /*ARGSUSED*/
3332820Skz151634 unsigned long
drm_get_resource_len(drm_device_t * softstate,unsigned int regnum)334*5804Scg149915 drm_get_resource_len(drm_device_t *softstate, unsigned int regnum)
3352820Skz151634 {
3362820Skz151634 drm_pci_resource_t res;
3372820Skz151634 int ret;
3382820Skz151634
3392820Skz151634 res.regnum = regnum;
3402820Skz151634
3412820Skz151634 ret = do_get_pci_res(softstate, &res);
3422820Skz151634
343*5804Scg149915 if (ret != 0) {
344*5804Scg149915 DRM_ERROR("drm_get_resource_len: ioctl failed");
3452820Skz151634 return (0);
3462820Skz151634 }
3472820Skz151634
3482820Skz151634 return (res.size);
3492820Skz151634 }
350