1 /*
2 * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
3 */
4
5 /*
6 * Copyright (c) 2012 Intel Corporation. All rights reserved.
7 */
8
9 /**
10 * \file drm_pci.h
11 * \brief PCI consistent, DMA-accessible memory functions.
12 *
13 * \author Eric Anholt <anholt@FreeBSD.org>
14 */
15
16 /*-
17 * Copyright 2003 Eric Anholt.
18 * All Rights Reserved.
19 *
20 * Permission is hereby granted, free of charge, to any person obtaining a
21 * copy of this software and associated documentation files (the "Software"),
22 * to deal in the Software without restriction, including without limitation
23 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
24 * and/or sell copies of the Software, and to permit persons to whom the
25 * Software is furnished to do so, subject to the following conditions:
26 *
27 * The above copyright notice and this permission notice (including the next
28 * paragraph) shall be included in all copies or substantial portions of the
29 * Software.
30 *
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
35 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
36 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 */
38
39 /**********************************************************************/
40 /** \name PCI memory */
41 /*@{*/
42
43 #include "drmP.h"
44 #include <vm/seg_kmem.h>
45
46 typedef struct drm_pci_resource {
47 uint_t regnum;
48 unsigned long offset;
49 unsigned long size;
50 } drm_pci_resource_t;
51
52 void
drm_core_ioremap(struct drm_local_map * map,struct drm_device * dev)53 drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev)
54 {
55 if ((map->type == _DRM_AGP) && dev->agp) {
56 /*
57 * During AGP mapping initialization, we map AGP aperture
58 * into kernel space. So, when we access the memory which
59 * managed by agp gart in kernel space, we have to go
60 * through two-level address translation: kernel virtual
61 * address --> aperture address --> physical address. For
62 * improving this, here in opensourced code, agp_remap()
63 * gets invoking to dispose the mapping between agp aperture
64 * and kernel space, and directly map the actual physical
65 * memory which is allocated to agp gart to kernel space.
66 * After that, access to physical memory managed by agp gart
67 * hardware in kernel space doesn't go through agp hardware,
68 * it will be: kernel virtual ---> physical address.
69 * Obviously, it is more efficient. But in Solaris operating
70 * system, the ioctl AGPIOC_ALLOCATE of agpgart driver does
71 * not return physical address. We are unable to create the
72 * direct mapping between kernel space and agp memory. So,
73 * we remove the calling to agp_remap().
74 */
75 DRM_DEBUG("drm_core_ioremap: skipping agp_remap\n");
76 } else {
77 (void) drm_ioremap(dev, map);
78
79 }
80 }
81
82 void
drm_core_ioremapfree(struct drm_local_map * map,struct drm_device * dev)83 drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
84 {
85 _NOTE(ARGUNUSED(dev))
86
87 if (map->type != _DRM_AGP) {
88 if (map->handle && map->size)
89 drm_ioremapfree(map);
90 } else {
91 /*
92 * Refer to the comments in drm_core_ioremap() where we removed
93 * the calling to agp_remap(), correspondingly, we remove the
94 * calling to agp_remap_free(dev, map);
95 */
96 DRM_DEBUG("drm_core_ioremap: skipping agp_remap_free\n");
97 }
98 }
99
100 /*
101 * pci_alloc_consistent()
102 */
103 static ddi_dma_attr_t hw_dma_attr = {
104 DMA_ATTR_V0, /* version */
105 0, /* addr_lo */
106 0xffffffff, /* addr_hi */
107 0xffffffff, /* count_max */
108 4096, /* alignment */
109 0xfff, /* burstsize */
110 1, /* minxfer */
111 0xffffffff, /* maxxfer */
112 0xffffffff, /* seg */
113 1, /* sgllen */
114 4, /* granular */
115 DDI_DMA_FLAGERR, /* flags */
116 };
117
118 static ddi_device_acc_attr_t hw_acc_attr = {
119 DDI_DEVICE_ATTR_V0,
120 DDI_NEVERSWAP_ACC,
121 DDI_STRICTORDER_ACC,
122 DDI_FLAGERR_ACC
123 };
124
125 void *
drm_pci_alloc(struct drm_device * dev,size_t size,size_t align,dma_addr_t maxaddr,int segments)126 drm_pci_alloc(struct drm_device *dev, size_t size,
127 size_t align, dma_addr_t maxaddr, int segments)
128 {
129 struct drm_dma_handle *dmah;
130 uint_t count;
131
132 /* allocate continous physical memory for hw status page */
133 hw_dma_attr.dma_attr_align = (!align) ? 1 : align;
134
135 hw_dma_attr.dma_attr_addr_hi = maxaddr;
136 hw_dma_attr.dma_attr_sgllen = segments;
137
138 dmah = kmem_zalloc(sizeof(struct drm_dma_handle), KM_SLEEP);
139
140 if (ddi_dma_alloc_handle(dev->devinfo, &hw_dma_attr,
141 DDI_DMA_SLEEP, NULL, &dmah->dma_hdl) != DDI_SUCCESS) {
142 DRM_ERROR("ddi_dma_alloc_handle() failed");
143 goto err3;
144 }
145
146 if (ddi_dma_mem_alloc(dmah->dma_hdl, size, &hw_acc_attr,
147 DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
148 DDI_DMA_SLEEP, NULL, (caddr_t *)&dmah->vaddr,
149 &dmah->real_sz, &dmah->acc_hdl) != DDI_SUCCESS) {
150 DRM_ERROR("ddi_dma_mem_alloc() failed\n");
151 goto err2;
152 }
153
154 if (ddi_dma_addr_bind_handle(dmah->dma_hdl, NULL,
155 (caddr_t)dmah->vaddr, dmah->real_sz,
156 DDI_DMA_RDWR|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
157 NULL, &dmah->cookie, &count) != DDI_DMA_MAPPED) {
158 DRM_ERROR("ddi_dma_addr_bind_handle() failed");
159 goto err1;
160 }
161
162 if (count > segments) {
163 (void) ddi_dma_unbind_handle(dmah->dma_hdl);
164 goto err1;
165 }
166
167 dmah->cookie_num = count;
168 if (count == 1)
169 dmah->paddr = dmah->cookie.dmac_address;
170
171 return (dmah);
172
173 err1:
174 ddi_dma_mem_free(&dmah->acc_hdl);
175 err2:
176 ddi_dma_free_handle(&dmah->dma_hdl);
177 err3:
178 kmem_free(dmah, sizeof (*dmah));
179 return (NULL);
180 }
181
182 void
drm_pci_free(drm_dma_handle_t * dmah)183 drm_pci_free(drm_dma_handle_t *dmah)
184 {
185 ASSERT(dmah != NULL);
186
187 (void) ddi_dma_unbind_handle(dmah->dma_hdl);
188 ddi_dma_mem_free(&dmah->acc_hdl);
189 ddi_dma_free_handle(&dmah->dma_hdl);
190 kmem_free(dmah, sizeof (drm_dma_handle_t));
191 }
192
193 int
do_get_pci_res(struct drm_device * dev,drm_pci_resource_t * resp)194 do_get_pci_res(struct drm_device *dev, drm_pci_resource_t *resp)
195 {
196 int length;
197 pci_regspec_t *regs;
198
199 if (ddi_getlongprop(
200 DDI_DEV_T_ANY, dev->devinfo, DDI_PROP_DONTPASS,
201 "assigned-addresses", (caddr_t)®s, &length) !=
202 DDI_PROP_SUCCESS) {
203 DRM_ERROR("do_get_pci_res: ddi_getlongprop failed!\n");
204 return (EFAULT);
205 }
206 resp->offset =
207 (unsigned long)regs[resp->regnum].pci_phys_low;
208 resp->size =
209 (unsigned long)regs[resp->regnum].pci_size_low;
210 kmem_free(regs, (size_t)length);
211
212 return (0);
213 }
214
215 unsigned long
drm_get_resource_start(struct drm_device * dev,unsigned int regnum)216 drm_get_resource_start(struct drm_device *dev, unsigned int regnum)
217 {
218 drm_pci_resource_t res;
219 int ret;
220
221 res.regnum = regnum;
222
223 ret = do_get_pci_res(dev, &res);
224
225 if (ret != 0) {
226 DRM_ERROR("drm_get_resource_start: ioctl failed");
227 return (0);
228 }
229
230 return (res.offset);
231
232 }
233
234 unsigned long
drm_get_resource_len(struct drm_device * softstate,unsigned int regnum)235 drm_get_resource_len(struct drm_device *softstate, unsigned int regnum)
236 {
237 drm_pci_resource_t res;
238 int ret;
239
240 res.regnum = regnum;
241
242 ret = do_get_pci_res(softstate, &res);
243
244 if (ret != 0) {
245 DRM_ERROR("drm_get_resource_len: ioctl failed");
246 return (0);
247 }
248
249 return (res.size);
250 }
251
252