| /* |
| * Copyright © 2008 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| * Authors: |
| * Eric Anholt <eric@anholt.net> |
| * Ben Widawsky <ben@bwidawsk.net> |
| * |
| */ |
| |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <err.h> |
| #include <assert.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/mman.h> |
| |
| #include "intel_io.h" |
| #include "igt_core.h" |
| #include "igt_gt.h" |
| #include "intel_chipset.h" |
| |
| /** |
| * SECTION:intel_io |
| * @short_description: Register access and sideband I/O library |
| * @title: I/O |
| * @include: igt.h |
| * @section_id: intel-gpu-tools-IO |
| * |
| * This library provides register I/O helpers in both a basic version and a more |
| * fancy version which also handles forcewake and can optionally check registers |
| * against a white-list. All register function are compatible. Hence the same |
| * code can be used to decode registers with either of them, or also from a dump |
| * file using intel_mmio_use_dump_file(). |
| * |
| * Furthermore this library also provides helper functions for accessing the |
| * various sideband interfaces found on Valleyview/Baytrail based platforms. |
| */ |
| |
| #define FAKEKEY 0x2468ace0 |
| |
| /** |
| * igt_global_mmio: |
| * |
| * Pointer to the register range, initialized using intel_register_access_init() |
| * or intel_mmio_use_dump_file(). It is not recommended to use this directly. |
| */ |
| void *igt_global_mmio; |
| |
| static struct _mmio_data { |
| int inited; |
| bool safe; |
| uint32_t i915_devid; |
| struct intel_register_map map; |
| int key; |
| } mmio_data; |
| |
| /** |
| * intel_mmio_use_dump_file: |
| * @file: name of the register dump file to open |
| * |
| * Sets up #igt_global_mmio to point at the data contained in @file. This allows |
| * the same code to get reused for dumping and decoding from running hardware as |
| * from register dumps. |
| */ |
| void |
| intel_mmio_use_dump_file(char *file) |
| { |
| int fd; |
| struct stat st; |
| |
| fd = open(file, O_RDWR); |
| igt_fail_on_f(fd == -1, |
| "Couldn't open %s\n", file); |
| |
| fstat(fd, &st); |
| igt_global_mmio = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); |
| igt_fail_on_f(igt_global_mmio == MAP_FAILED, |
| "Couldn't mmap %s\n", file); |
| close(fd); |
| } |
| |
| /** |
| * intel_mmio_use_pci_bar: |
| * @pci_dev: intel gracphis pci device |
| * |
| * Sets up #igt_global_mmio to point at the mmio bar. |
| * |
| * @pci_dev can be obtained from intel_get_pci_device(). |
| */ |
| void |
| intel_mmio_use_pci_bar(struct pci_device *pci_dev) |
| { |
| uint32_t devid, gen; |
| int mmio_bar, mmio_size; |
| int error; |
| |
| devid = pci_dev->device_id; |
| if (IS_GEN2(devid)) |
| mmio_bar = 1; |
| else |
| mmio_bar = 0; |
| |
| gen = intel_gen(devid); |
| if (gen < 3) |
| mmio_size = 512*1024; |
| else if (gen < 5) |
| mmio_size = 512*1024; |
| else |
| mmio_size = 2*1024*1024; |
| |
| error = pci_device_map_range (pci_dev, |
| pci_dev->regions[mmio_bar].base_addr, |
| mmio_size, |
| PCI_DEV_MAP_FLAG_WRITABLE, |
| &igt_global_mmio); |
| |
| igt_fail_on_f(error != 0, |
| "Couldn't map MMIO region\n"); |
| } |
| |
| static void |
| release_forcewake_lock(int fd) |
| { |
| close(fd); |
| } |
| |
| /** |
| * intel_register_access_init: |
| * @pci_dev: intel graphics pci device |
| * @safe: use safe register access tables |
| * |
| * This initializes the new register access library, which supports forcewake |
| * handling and also allows register access to be checked with an explicit |
| * whitelist. |
| * |
| * It also initializes #igt_global_mmio like intel_mmio_use_pci_bar(). |
| * |
| * @pci_dev can be obtained from intel_get_pci_device(). |
| */ |
| int |
| intel_register_access_init(struct pci_device *pci_dev, int safe, int fd) |
| { |
| int ret; |
| |
| /* after old API is deprecated, remove this */ |
| if (igt_global_mmio == NULL) |
| intel_mmio_use_pci_bar(pci_dev); |
| |
| igt_assert(igt_global_mmio != NULL); |
| |
| if (mmio_data.inited) |
| return -1; |
| |
| mmio_data.safe = (safe != 0 && |
| intel_gen(pci_dev->device_id) >= 4) ? true : false; |
| mmio_data.i915_devid = pci_dev->device_id; |
| if (mmio_data.safe) |
| mmio_data.map = intel_get_register_map(mmio_data.i915_devid); |
| |
| /* Find where the forcewake lock is. Forcewake doesn't exist |
| * gen < 6, but the debugfs should do the right things for us. |
| */ |
| ret = igt_open_forcewake_handle(fd); |
| if (ret == -1) |
| mmio_data.key = FAKEKEY; |
| else |
| mmio_data.key = ret; |
| |
| mmio_data.inited++; |
| return 0; |
| } |
| |
| static int |
| intel_register_access_needs_wake(void) |
| { |
| return mmio_data.key != FAKEKEY; |
| } |
| |
| /** |
| * intel_register_access_needs_fakewake: |
| * |
| * Returns: |
| * Non-zero when forcewake initialization failed. |
| */ |
| int intel_register_access_needs_fakewake(void) |
| { |
| return mmio_data.key == FAKEKEY; |
| } |
| |
| /** |
| * intel_register_access_fini: |
| * |
| * Clean up the register access helper initialized with |
| * intel_register_access_init(). |
| */ |
| void |
| intel_register_access_fini(void) |
| { |
| if (mmio_data.key && intel_register_access_needs_wake()) |
| release_forcewake_lock(mmio_data.key); |
| mmio_data.inited--; |
| } |
| |
| /** |
| * intel_register_read: |
| * @reg: register offset |
| * |
| * 32-bit read of the register at @offset. This function only works when the new |
| * register access helper is initialized with intel_register_access_init(). |
| * |
| * Compared to INREG() it can do optional checking with the register access |
| * white lists. |
| * |
| * Returns: |
| * The value read from the register. |
| */ |
| uint32_t |
| intel_register_read(uint32_t reg) |
| { |
| struct intel_register_range *range; |
| uint32_t ret; |
| |
| igt_assert(mmio_data.inited); |
| |
| if (intel_gen(mmio_data.i915_devid) >= 6) |
| igt_assert(mmio_data.key != -1); |
| |
| if (!mmio_data.safe) |
| goto read_out; |
| |
| range = intel_get_register_range(mmio_data.map, |
| reg, |
| INTEL_RANGE_READ); |
| |
| if(!range) { |
| igt_warn("Register read blocked for safety ""(*0x%08x)\n", reg); |
| ret = 0xffffffff; |
| goto out; |
| } |
| |
| read_out: |
| ret = *(volatile uint32_t *)((volatile char *)igt_global_mmio + reg); |
| out: |
| return ret; |
| } |
| |
| /** |
| * intel_register_write: |
| * @reg: register offset |
| * @val: value to write |
| * |
| * 32-bit write to the register at @offset. This function only works when the new |
| * register access helper is initialized with intel_register_access_init(). |
| * |
| * Compared to OUTREG() it can do optional checking with the register access |
| * white lists. |
| */ |
| void |
| intel_register_write(uint32_t reg, uint32_t val) |
| { |
| struct intel_register_range *range; |
| |
| igt_assert(mmio_data.inited); |
| |
| if (intel_gen(mmio_data.i915_devid) >= 6) |
| igt_assert(mmio_data.key != -1); |
| |
| if (!mmio_data.safe) |
| goto write_out; |
| |
| range = intel_get_register_range(mmio_data.map, |
| reg, |
| INTEL_RANGE_WRITE); |
| |
| igt_warn_on_f(!range, |
| "Register write blocked for safety ""(*0x%08x = 0x%x)\n", reg, val); |
| |
| write_out: |
| *(volatile uint32_t *)((volatile char *)igt_global_mmio + reg) = val; |
| } |
| |
| |
| /** |
| * INREG: |
| * @reg: register offset |
| * |
| * 32-bit read of the register at offset @reg. This function only works when the |
| * new register access helper is initialized with intel_register_access_init(). |
| * |
| * This function directly accesses the #igt_global_mmio without safety checks. |
| * |
| * Returns: |
| * The value read from the register. |
| */ |
| uint32_t INREG(uint32_t reg) |
| { |
| return *(volatile uint32_t *)((volatile char *)igt_global_mmio + reg); |
| } |
| |
| /** |
| * INREG16: |
| * @reg: register offset |
| * |
| * 16-bit read of the register at offset @reg. This function only works when the |
| * new register access helper is initialized with intel_register_access_init(). |
| * |
| * This function directly accesses the #igt_global_mmio without safety checks. |
| * |
| * Returns: |
| * The value read from the register. |
| */ |
| uint16_t INREG16(uint32_t reg) |
| { |
| return *(volatile uint16_t *)((volatile char *)igt_global_mmio + reg); |
| } |
| |
| /** |
| * INREG8: |
| * @reg: register offset |
| * |
| * 8-bit read of the register at offset @reg. This function only works when the |
| * new register access helper is initialized with intel_register_access_init(). |
| * |
| * This function directly accesses the #igt_global_mmio without safety checks. |
| * |
| * Returns: |
| * The value read from the register. |
| */ |
| uint8_t INREG8(uint32_t reg) |
| { |
| return *((volatile uint8_t *)igt_global_mmio + reg); |
| } |
| |
| /** |
| * OUTREG: |
| * @reg: register offset |
| * @val: value to write |
| * |
| * 32-bit write of @val to the register at offset @reg. This function only works |
| * when the new register access helper is initialized with |
| * intel_register_access_init(). |
| * |
| * This function directly accesses the #igt_global_mmio without safety checks. |
| */ |
| void OUTREG(uint32_t reg, uint32_t val) |
| { |
| *(volatile uint32_t *)((volatile char *)igt_global_mmio + reg) = val; |
| } |
| |
| /** |
| * OUTREG16: |
| * @reg: register offset |
| * @val: value to write |
| * |
| * 16-bit write of @val to the register at offset @reg. This function only works |
| * when the new register access helper is initialized with |
| * intel_register_access_init(). |
| * |
| * This function directly accesses the #igt_global_mmio without safety checks. |
| */ |
| void OUTREG16(uint32_t reg, uint16_t val) |
| { |
| *(volatile uint16_t *)((volatile char *)igt_global_mmio + reg) = val; |
| } |
| |
| /** |
| * OUTREG8: |
| * @reg: register offset |
| * @val: value to write |
| * |
| * 8-bit write of @val to the register at offset @reg. This function only works |
| * when the new register access helper is initialized with |
| * intel_register_access_init(). |
| * |
| * This function directly accesses the #igt_global_mmio without safety checks. |
| */ |
| void OUTREG8(uint32_t reg, uint8_t val) |
| { |
| *((volatile uint8_t *)igt_global_mmio + reg) = val; |
| } |