| // SPDX-License-Identifier: MIT | |
| /* | |
| * Copyright © 2021-2023 Intel Corporation | |
| */ | |
| #include "xe_mmio.h" | |
| #include <linux/delay.h> | |
| #include <linux/io-64-nonatomic-lo-hi.h> | |
| #include <linux/minmax.h> | |
| #include <linux/pci.h> | |
| #include <drm/drm_managed.h> | |
| #include "regs/xe_bars.h" | |
| #include "xe_device.h" | |
| #include "xe_gt_sriov_vf.h" | |
| #include "xe_printk.h" | |
| #include "xe_sriov.h" | |
| #include "xe_tile_printk.h" | |
| #include "xe_trace.h" | |
| #include "xe_wa.h" | |
| #include "generated/xe_device_wa_oob.h" | |
| /* | |
| * On multi-tile devices, partition the BAR space for MMIO on each tile, | |
| * possibly accounting for register override on the number of tiles available. | |
| * tile_mmio_size contains both the tile's 4MB register space, as well as | |
| * additional space for the GTT and other (possibly unused) regions). | |
| * Resulting memory layout is like below: | |
| * | |
| * .----------------------. <- tile_count * tile_mmio_size | |
| * | .... | | |
| * |----------------------| <- 2 * tile_mmio_size | |
| * | tile1 GTT + other | | |
| * |----------------------| <- 1 * tile_mmio_size + 4MB | |
| * | tile1->mmio.regs | | |
| * |----------------------| <- 1 * tile_mmio_size | |
| * | tile0 GTT + other | | |
| * |----------------------| <- 4MB | |
| * | tile0->mmio.regs | | |
| * '----------------------' <- 0MB | |
| */ | |
| static void mmio_multi_tile_setup(struct xe_device *xe, size_t tile_mmio_size) | |
| { | |
| struct xe_tile *tile; | |
| u8 id; | |
| for_each_remote_tile(tile, xe, id) | |
| xe_mmio_init(&tile->mmio, tile, xe->mmio.regs + id * tile_mmio_size, SZ_4M); | |
| } | |
| /** | |
| * xe_mmio_probe_tiles() - Initialize all tiles' MMIO | |
| * @xe: the &xe_device | |
| * | |
| * Initialize the remaining tiles' MMIO instances. | |
| * | |
| * Return: 0 on success or a negative error code on failure. | |
| */ | |
| int xe_mmio_probe_tiles(struct xe_device *xe) | |
| { | |
| size_t tile_mmio_size = SZ_16M; | |
| /* | |
| * Nothing to be done as tile 0 has already been setup earlier with the | |
| * entire BAR mapped - see xe_mmio_probe_early() | |
| */ | |
| if (xe->info.tile_count == 1) | |
| return 0; | |
| if (xe->mmio.size < xe->info.tile_count * tile_mmio_size) { | |
| xe_err(xe, "GTTMMADR_BAR is too small for %d tiles: %zu\n", | |
| xe->info.tile_count, xe->mmio.size); | |
| return -EIO; | |
| } | |
| mmio_multi_tile_setup(xe, tile_mmio_size); | |
| return 0; | |
| } | |
| static void mmio_fini(void *arg) | |
| { | |
| struct xe_device *xe = arg; | |
| xe->mmio.regs = NULL; | |
| } | |
| /** | |
| * xe_mmio_probe_early() - Probe and initialize device's MMIO | |
| * @xe: the &xe_device | |
| * | |
| * Map the entire GTTMMADR_BAR and initialize the first tile's MMIO instance. | |
| * | |
| * The first 16MB of the GTTMMADR_BAR always belongs to the root tile, and | |
| * includes: registers (0-4MB), reserved space (4MB-8MB) and GGTT (8MB-16MB). | |
| * | |
| * Return: 0 on success or a negative error code on failure. | |
| */ | |
| int xe_mmio_probe_early(struct xe_device *xe) | |
| { | |
| struct xe_tile *root_tile = xe_device_get_root_tile(xe); | |
| struct pci_dev *pdev = to_pci_dev(xe->drm.dev); | |
| xe->mmio.regs = pcim_iomap(pdev, GTTMMADR_BAR, 0); | |
| if (!xe->mmio.regs) { | |
| xe_err(xe, "Failed to map GTTMMADR_BAR\n"); | |
| return -EIO; | |
| } | |
| xe->mmio.size = pci_resource_len(pdev, GTTMMADR_BAR); | |
| if (xe->mmio.size < SZ_16M) { | |
| xe_err(xe, "GTTMMADR_BAR is too small: %zu\n", xe->mmio.size); | |
| return -EIO; | |
| } | |
| /* Setup first tile; other tiles (if present) will be setup later. */ | |
| xe_mmio_init(&root_tile->mmio, root_tile, xe->mmio.regs, SZ_4M); | |
| return devm_add_action_or_reset(xe->drm.dev, mmio_fini, xe); | |
| } | |
| ALLOW_ERROR_INJECTION(xe_mmio_probe_early, ERRNO); /* See xe_pci_probe() */ | |
| /** | |
| * xe_mmio_init() - Initialize an MMIO instance | |
| * @mmio: Pointer to the MMIO instance to initialize | |
| * @tile: The tile to which the MMIO region belongs | |
| * @ptr: Pointer to the start of the MMIO region | |
| * @size: The size of the MMIO region in bytes | |
| * | |
| * This is a convenience function for minimal initialization of struct xe_mmio. | |
| */ | |
| void xe_mmio_init(struct xe_mmio *mmio, struct xe_tile *tile, void __iomem *ptr, u32 size) | |
| { | |
| xe_tile_assert(tile, size <= XE_REG_ADDR_MAX); | |
| mmio->regs = ptr; | |
| mmio->regs_size = size; | |
| mmio->tile = tile; | |
| } | |
| static bool mmio_available(struct xe_mmio *mmio) | |
| { | |
| return !xe_tile_WARN_ON_ONCE(mmio->tile, !mmio->tile->xe->mmio.regs); | |
| } | |
| static void mmio_flush_pending_writes(struct xe_mmio *mmio) | |
| { | |
| #define DUMMY_REG_OFFSET 0x130030 | |
| int i; | |
| if (!XE_DEVICE_WA(mmio->tile->xe, 15015404425)) | |
| return; | |
| /* 4 dummy writes */ | |
| for (i = 0; i < 4; i++) | |
| writel(0, mmio->regs + DUMMY_REG_OFFSET); | |
| } | |
| u8 xe_mmio_read8(struct xe_mmio *mmio, struct xe_reg reg) | |
| { | |
| u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); | |
| u8 val; | |
| if (!mmio_available(mmio)) | |
| return 0; | |
| mmio_flush_pending_writes(mmio); | |
| val = readb(mmio->regs + addr); | |
| trace_xe_reg_rw(mmio, false, addr, val, sizeof(val)); | |
| return val; | |
| } | |
| void xe_mmio_write8(struct xe_mmio *mmio, struct xe_reg reg, u8 val) | |
| { | |
| u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); | |
| if (!mmio_available(mmio)) | |
| return; | |
| trace_xe_reg_rw(mmio, true, addr, val, sizeof(val)); | |
| writeb(val, mmio->regs + addr); | |
| } | |
| u16 xe_mmio_read16(struct xe_mmio *mmio, struct xe_reg reg) | |
| { | |
| u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); | |
| u16 val; | |
| if (!mmio_available(mmio)) | |
| return 0; | |
| mmio_flush_pending_writes(mmio); | |
| val = readw(mmio->regs + addr); | |
| trace_xe_reg_rw(mmio, false, addr, val, sizeof(val)); | |
| return val; | |
| } | |
| void xe_mmio_write32(struct xe_mmio *mmio, struct xe_reg reg, u32 val) | |
| { | |
| u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); | |
| if (!mmio_available(mmio)) | |
| return; | |
| trace_xe_reg_rw(mmio, true, addr, val, sizeof(val)); | |
| if (!reg.vf && IS_SRIOV_VF(mmio->tile->xe)) | |
| xe_gt_sriov_vf_write32(mmio->sriov_vf_gt ?: | |
| mmio->tile->primary_gt, reg, val); | |
| else | |
| writel(val, mmio->regs + addr); | |
| } | |
| u32 xe_mmio_read32(struct xe_mmio *mmio, struct xe_reg reg) | |
| { | |
| u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); | |
| u32 val; | |
| if (!mmio_available(mmio)) | |
| return 0; | |
| mmio_flush_pending_writes(mmio); | |
| if (!reg.vf && IS_SRIOV_VF(mmio->tile->xe)) | |
| val = xe_gt_sriov_vf_read32(mmio->sriov_vf_gt ?: | |
| mmio->tile->primary_gt, reg); | |
| else | |
| val = readl(mmio->regs + addr); | |
| trace_xe_reg_rw(mmio, false, addr, val, sizeof(val)); | |
| return val; | |
| } | |
| u32 xe_mmio_rmw32(struct xe_mmio *mmio, struct xe_reg reg, u32 clr, u32 set) | |
| { | |
| u32 old, reg_val; | |
| old = xe_mmio_read32(mmio, reg); | |
| reg_val = (old & ~clr) | set; | |
| xe_mmio_write32(mmio, reg, reg_val); | |
| return old; | |
| } | |
| int xe_mmio_write32_and_verify(struct xe_mmio *mmio, | |
| struct xe_reg reg, u32 val, u32 mask, u32 eval) | |
| { | |
| u32 reg_val; | |
| xe_mmio_write32(mmio, reg, val); | |
| reg_val = xe_mmio_read32(mmio, reg); | |
| return (reg_val & mask) != eval ? -EINVAL : 0; | |
| } | |
| bool xe_mmio_in_range(const struct xe_mmio *mmio, | |
| const struct xe_mmio_range *range, | |
| struct xe_reg reg) | |
| { | |
| u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); | |
| return range && addr >= range->start && addr <= range->end; | |
| } | |
| /** | |
| * xe_mmio_read64_2x32() - Read a 64-bit register as two 32-bit reads | |
| * @mmio: MMIO target | |
| * @reg: register to read value from | |
| * | |
| * Although Intel GPUs have some 64-bit registers, the hardware officially | |
| * only supports GTTMMADR register reads of 32 bits or smaller. Even if | |
| * a readq operation may return a reasonable value, that violation of the | |
| * spec shouldn't be relied upon and all 64-bit register reads should be | |
| * performed as two 32-bit reads of the upper and lower dwords. | |
| * | |
| * When reading registers that may be changing (such as | |
| * counters), a rollover of the lower dword between the two 32-bit reads | |
| * can be problematic. This function attempts to ensure the upper dword has | |
| * stabilized before returning the 64-bit value. | |
| * | |
| * Note that because this function may re-read the register multiple times | |
| * while waiting for the value to stabilize it should not be used to read | |
| * any registers where read operations have side effects. | |
| * | |
| * Returns the value of the 64-bit register. | |
| */ | |
| u64 xe_mmio_read64_2x32(struct xe_mmio *mmio, struct xe_reg reg) | |
| { | |
| struct xe_reg reg_udw = { .addr = reg.addr + 0x4 }; | |
| u32 ldw, udw, oldudw, retries; | |
| /* | |
| * The two dwords of a 64-bit register can never straddle the offset | |
| * adjustment cutoff. | |
| */ | |
| xe_tile_assert(mmio->tile, !in_range(mmio->adj_limit, reg.addr + 1, 7)); | |
| oldudw = xe_mmio_read32(mmio, reg_udw); | |
| for (retries = 5; retries; --retries) { | |
| ldw = xe_mmio_read32(mmio, reg); | |
| udw = xe_mmio_read32(mmio, reg_udw); | |
| if (udw == oldudw) | |
| break; | |
| oldudw = udw; | |
| } | |
| xe_tile_WARN(mmio->tile, retries == 0, | |
| "MMIO: 64-bit read of %#x did not stabilize\n", reg.addr); | |
| return (u64)udw << 32 | ldw; | |
| } | |
| #define __XE_MMIO_WAIT_MAX_BACKOFF_100MS (100 * USEC_PER_MSEC) | |
| static int __xe_mmio_wait32(struct xe_mmio *mmio, struct xe_reg reg, u32 mask, u32 val, | |
| u32 timeout_us, u32 *out_val, bool atomic, bool expect_match) | |
| { | |
| ktime_t cur = ktime_get_raw(); | |
| const ktime_t end = ktime_add_us(cur, timeout_us); | |
| int ret = -ETIMEDOUT; | |
| s64 wait = 10; | |
| u32 read; | |
| bool check; | |
| for (;;) { | |
| read = xe_mmio_read32(mmio, reg); | |
| check = (read & mask) == val; | |
| if (!expect_match) | |
| check = !check; | |
| if (check) { | |
| ret = 0; | |
| break; | |
| } | |
| cur = ktime_get_raw(); | |
| if (!ktime_before(cur, end)) | |
| break; | |
| if (ktime_after(ktime_add_us(cur, wait), end)) | |
| wait = ktime_us_delta(end, cur); | |
| if (atomic) | |
| udelay(min_t(s64, wait, (MAX_UDELAY_MS * 1000))); | |
| else | |
| usleep_range(wait, wait << 1); | |
| wait = min_t(s64, wait << 1, __XE_MMIO_WAIT_MAX_BACKOFF_100MS); | |
| } | |
| if (ret != 0) { | |
| read = xe_mmio_read32(mmio, reg); | |
| check = (read & mask) == val; | |
| if (!expect_match) | |
| check = !check; | |
| if (check) | |
| ret = 0; | |
| } | |
| if (out_val) | |
| *out_val = read; | |
| return ret; | |
| } | |
| /** | |
| * xe_mmio_wait32() - Wait for a register to match the desired masked value | |
| * @mmio: MMIO target | |
| * @reg: register to read value from | |
| * @mask: mask to be applied to the value read from the register | |
| * @val: desired value after applying the mask | |
| * @timeout_us: time out after this period of time. Wait logic tries to be | |
| * smart, applying an exponential backoff until @timeout_us is reached. | |
| * @out_val: if not NULL, points where to store the last unmasked value | |
| * @atomic: needs to be true if calling from an atomic context | |
| * | |
| * This function polls for the desired masked value and returns zero on success | |
| * or -ETIMEDOUT if timed out. | |
| * | |
| * Note that @timeout_us represents the minimum amount of time to wait before | |
| * giving up. The actual time taken by this function can be a little more than | |
| * @timeout_us for different reasons, specially in non-atomic contexts. Thus, | |
| * it is possible that this function succeeds even after @timeout_us has passed. | |
| */ | |
| int xe_mmio_wait32(struct xe_mmio *mmio, struct xe_reg reg, u32 mask, u32 val, u32 timeout_us, | |
| u32 *out_val, bool atomic) | |
| { | |
| return __xe_mmio_wait32(mmio, reg, mask, val, timeout_us, out_val, atomic, true); | |
| } | |
| /** | |
| * xe_mmio_wait32_not() - Wait for a register to return anything other than the given masked value | |
| * @mmio: MMIO target | |
| * @reg: register to read value from | |
| * @mask: mask to be applied to the value read from the register | |
| * @val: value not to be matched after applying the mask | |
| * @timeout_us: time out after this period of time | |
| * @out_val: if not NULL, points where to store the last unmasked value | |
| * @atomic: needs to be true if calling from an atomic context | |
| * | |
| * This function works exactly like xe_mmio_wait32() with the exception that | |
| * @val is expected not to be matched. | |
| */ | |
| int xe_mmio_wait32_not(struct xe_mmio *mmio, struct xe_reg reg, u32 mask, u32 val, u32 timeout_us, | |
| u32 *out_val, bool atomic) | |
| { | |
| return __xe_mmio_wait32(mmio, reg, mask, val, timeout_us, out_val, atomic, false); | |
| } | |
| #ifdef CONFIG_PCI_IOV | |
| static size_t vf_regs_stride(struct xe_device *xe) | |
| { | |
| return GRAPHICS_VERx100(xe) > 1200 ? 0x400 : 0x1000; | |
| } | |
| /** | |
| * xe_mmio_init_vf_view() - Initialize an MMIO instance for accesses like the VF | |
| * @mmio: the target &xe_mmio to initialize as VF's view | |
| * @base: the source &xe_mmio to initialize from | |
| * @vfid: the VF identifier | |
| */ | |
| void xe_mmio_init_vf_view(struct xe_mmio *mmio, const struct xe_mmio *base, unsigned int vfid) | |
| { | |
| struct xe_tile *tile = base->tile; | |
| struct xe_device *xe = tile->xe; | |
| size_t offset = vf_regs_stride(xe) * vfid; | |
| xe_assert(xe, IS_SRIOV_PF(xe)); | |
| xe_assert(xe, vfid); | |
| xe_assert(xe, !base->sriov_vf_gt); | |
| xe_assert(xe, base->regs_size > offset); | |
| *mmio = *base; | |
| mmio->regs += offset; | |
| mmio->regs_size -= offset; | |
| } | |
| #endif |