1 /*
2 * Copyright © 2008 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Eric Anholt <eric@anholt.net>
25 * Ben Widawsky <ben@bwidawsk.net>
26 *
27 */
28
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #include <stdbool.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <err.h>
39 #include <assert.h>
40 #include <sys/ioctl.h>
41 #include <sys/stat.h>
42 #include <sys/mman.h>
43
44 #include "intel_gpu_tools.h"
45
46 #define FAKEKEY 0x2468ace0
47
48 void *mmio;
49
50 static struct _mmio_data {
51 int inited;
52 bool safe;
53 char debugfs_path[FILENAME_MAX];
54 char debugfs_forcewake_path[FILENAME_MAX];
55 uint32_t i915_devid;
56 struct intel_register_map map;
57 int key;
58 } mmio_data;
59
60 void
intel_map_file(char * file)61 intel_map_file(char *file)
62 {
63 int fd;
64 struct stat st;
65
66 fd = open(file, O_RDWR);
67 if (fd == -1) {
68 fprintf(stderr, "Couldn't open %s: %s\n", file,
69 strerror(errno));
70 exit(1);
71 }
72 fstat(fd, &st);
73 mmio = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
74 if (mmio == MAP_FAILED) {
75 fprintf(stderr, "Couldn't mmap %s: %s\n", file,
76 strerror(errno));
77 exit(1);
78 }
79 close(fd);
80 }
81
82 void
intel_get_mmio(struct pci_device * pci_dev)83 intel_get_mmio(struct pci_device *pci_dev)
84 {
85 uint32_t devid, gen;
86 int mmio_bar, mmio_size;
87 int error;
88
89 devid = pci_dev->device_id;
90 if (IS_GEN2(devid))
91 mmio_bar = 1;
92 else
93 mmio_bar = 0;
94
95 gen = intel_gen(devid);
96 if (gen < 3)
97 mmio_size = 512*1024;
98 else if (gen < 5)
99 mmio_size = 512*1024;
100 else
101 mmio_size = 2*1024*1024;
102
103 error = pci_device_map_range (pci_dev,
104 pci_dev->regions[mmio_bar].base_addr,
105 mmio_size,
106 PCI_DEV_MAP_FLAG_WRITABLE,
107 &mmio);
108
109 if (error != 0) {
110 fprintf(stderr, "Couldn't map MMIO region: %s\n",
111 strerror(error));
112 exit(1);
113 }
114 }
115
116 /*
117 * If successful, i915_debugfs_path and i915_debugfs_forcewake_path are both
118 * updated with the correct path.
119 */
120 static int
find_debugfs_path(const char * dri_base)121 find_debugfs_path(const char *dri_base)
122 {
123 char buf[FILENAME_MAX];
124 struct stat sb;
125 int i, ret;
126
127 for (i = 0; i < 16; i++) {
128 snprintf(buf, FILENAME_MAX, "%s/%i/name", dri_base, i);
129
130 snprintf(mmio_data.debugfs_path, FILENAME_MAX,
131 "%s/%i/", dri_base, i);
132 snprintf(mmio_data.debugfs_forcewake_path, FILENAME_MAX,
133 "%s/%i/i915_forcewake_user", dri_base, i);
134
135 ret = stat(mmio_data.debugfs_forcewake_path, &sb);
136 if (ret) {
137 mmio_data.debugfs_path[0] = 0;
138 mmio_data.debugfs_forcewake_path[0] = 0;
139 } else
140 return 0;
141 }
142
143 return -1;
144 }
145
146 static int
get_forcewake_lock(void)147 get_forcewake_lock(void)
148 {
149 return open(mmio_data.debugfs_forcewake_path, 0);
150 }
151
152 static void
release_forcewake_lock(int fd)153 release_forcewake_lock(int fd)
154 {
155 close(fd);
156 }
157
158 /* Dumb check to see if i915 was loaded */
159 static bool
i915_loaded(void)160 i915_loaded(void)
161 {
162 struct stat sb;
163 int ret;
164
165 ret = stat("/sys/module/i915/", &sb);
166 if (ret) {
167 return false;
168 }
169
170 assert(S_ISDIR(sb.st_mode));
171 return true;
172 }
173
174 /*
175 * Initialize register access library.
176 *
177 * @pci_dev: pci device we're mucking with
178 * @safe: use safe register access tables
179 */
180 int
intel_register_access_init(struct pci_device * pci_dev,int safe)181 intel_register_access_init(struct pci_device *pci_dev, int safe)
182 {
183 int ret;
184
185 /* after old API is deprecated, remove this */
186 if (mmio == NULL)
187 intel_get_mmio(pci_dev);
188
189 assert(mmio != NULL);
190
191 if (mmio_data.inited)
192 return -1;
193
194 mmio_data.safe = (safe != 0 &&
195 intel_gen(pci_dev->device_id) >= 4) ? true : false;
196 mmio_data.i915_devid = pci_dev->device_id;
197 if (mmio_data.safe)
198 mmio_data.map = intel_get_register_map(mmio_data.i915_devid);
199
200 /* Find where the forcewake lock is. Forcewake doesn't exist
201 * gen < 6, but the debugfs should do the right things for us.
202 */
203 ret = find_debugfs_path("/sys/kernel/debug/dri");
204 if (ret) {
205 ret = find_debugfs_path("/debug/dri");
206 if (ret) {
207 fprintf(stderr, "Couldn't find path to dri/debugfs entry\n");
208 if (i915_loaded()) {
209 fprintf(stderr, "i915 loaded; not proceeding.\n");
210 return ret;
211 }
212 }
213 mmio_data.key = FAKEKEY;
214 } else
215 mmio_data.key = get_forcewake_lock();
216
217 mmio_data.inited++;
218 return 0;
219 }
220 static int
intel_register_access_needs_wake(void)221 intel_register_access_needs_wake(void)
222 {
223 return mmio_data.key != FAKEKEY;
224 }
225
intel_register_access_needs_fakewake(void)226 int intel_register_access_needs_fakewake(void)
227 {
228 return mmio_data.key == FAKEKEY;
229 }
230
231 void
intel_register_access_fini(void)232 intel_register_access_fini(void)
233 {
234 if (mmio_data.key && intel_register_access_needs_wake())
235 release_forcewake_lock(mmio_data.key);
236 mmio_data.inited--;
237 }
238
239 uint32_t
intel_register_read(uint32_t reg)240 intel_register_read(uint32_t reg)
241 {
242 struct intel_register_range *range;
243 uint32_t ret;
244
245 assert(mmio_data.inited);
246
247 if (intel_gen(mmio_data.i915_devid) >= 6)
248 assert(mmio_data.key != -1);
249
250 if (!mmio_data.safe)
251 goto read_out;
252
253 range = intel_get_register_range(mmio_data.map,
254 reg,
255 INTEL_RANGE_READ);
256
257 if(!range) {
258 fprintf(stderr, "Register read blocked for safety "
259 "(*0x%08x)\n", reg);
260 ret = 0xffffffff;
261 goto out;
262 }
263
264 read_out:
265 ret = *(volatile uint32_t *)((volatile char *)mmio + reg);
266 out:
267 return ret;
268 }
269
270 void
intel_register_write(uint32_t reg,uint32_t val)271 intel_register_write(uint32_t reg, uint32_t val)
272 {
273 struct intel_register_range *range;
274
275 assert(mmio_data.inited);
276
277 if (intel_gen(mmio_data.i915_devid) >= 6)
278 assert(mmio_data.key != -1);
279
280 if (!mmio_data.safe)
281 goto write_out;
282
283 range = intel_get_register_range(mmio_data.map,
284 reg,
285 INTEL_RANGE_WRITE);
286
287 if (!range) {
288 fprintf(stderr, "Register write blocked for safety "
289 "(*0x%08x = 0x%x)\n", reg, val);
290 }
291
292 write_out:
293 *(volatile uint32_t *)((volatile char *)mmio + reg) = val;
294 }
295