Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/xe/nvm: add support for non-posted erase

Erase command is slow on discrete graphics storage
and may overshot PCI completion timeout.
BMG introduces the ability to have non-posted erase.
Add driver support for non-posted erase with polling
for erase completion.

Reviewed-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Signed-off-by: Reuven Abliyev <reuven.abliyev@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Link: https://lore.kernel.org/r/20250617145159.3803852-9-alexander.usyskin@intel.com
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>

authored by

Reuven Abliyev and committed by
Rodrigo Vivi
a1c940cb 87e1ebba

+67 -2
+25
drivers/gpu/drm/xe/xe_nvm.c
··· 14 14 #include "xe_sriov.h" 15 15 16 16 #define GEN12_GUNIT_NVM_BASE 0x00102040 17 + #define GEN12_DEBUG_NVM_BASE 0x00101018 18 + 19 + #define GEN12_CNTL_PROTECTED_NVM_REG 0x0010100C 20 + 17 21 #define GEN12_GUNIT_NVM_SIZE 0x80 22 + #define GEN12_DEBUG_NVM_SIZE 0x4 23 + 24 + #define NVM_NON_POSTED_ERASE_CHICKEN_BIT BIT(13) 25 + 18 26 #define HECI_FW_STATUS_2_NVM_ACCESS_MODE BIT(3) 19 27 20 28 static const struct intel_dg_nvm_region regions[INTEL_DG_NVM_REGIONS] = { ··· 35 27 36 28 static void xe_nvm_release_dev(struct device *dev) 37 29 { 30 + } 31 + 32 + static bool xe_nvm_non_posted_erase(struct xe_device *xe) 33 + { 34 + struct xe_gt *gt = xe_root_mmio_gt(xe); 35 + 36 + if (xe->info.platform != XE_BATTLEMAGE) 37 + return false; 38 + return !(xe_mmio_read32(&gt->mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) & 39 + NVM_NON_POSTED_ERASE_CHICKEN_BIT); 38 40 } 39 41 40 42 static bool xe_nvm_writable_override(struct xe_device *xe) ··· 104 86 nvm = xe->nvm; 105 87 106 88 nvm->writable_override = xe_nvm_writable_override(xe); 89 + nvm->non_posted_erase = xe_nvm_non_posted_erase(xe); 107 90 nvm->bar.parent = &pdev->resource[0]; 108 91 nvm->bar.start = GEN12_GUNIT_NVM_BASE + pdev->resource[0].start; 109 92 nvm->bar.end = nvm->bar.start + GEN12_GUNIT_NVM_SIZE - 1; 110 93 nvm->bar.flags = IORESOURCE_MEM; 111 94 nvm->bar.desc = IORES_DESC_NONE; 112 95 nvm->regions = regions; 96 + 97 + nvm->bar2.parent = &pdev->resource[0]; 98 + nvm->bar2.start = GEN12_DEBUG_NVM_BASE + pdev->resource[0].start; 99 + nvm->bar2.end = nvm->bar2.start + GEN12_DEBUG_NVM_SIZE - 1; 100 + nvm->bar2.flags = IORESOURCE_MEM; 101 + nvm->bar2.desc = IORES_DESC_NONE; 113 102 114 103 aux_dev = &nvm->aux_dev; 115 104
+40 -2
drivers/mtd/devices/mtd_intel_dg.c
··· 25 25 struct mtd_info mtd; 26 26 struct mutex lock; /* region access lock */ 27 27 void __iomem *base; 28 + void __iomem *base2; 29 + bool non_posted_erase; 30 + 28 31 size_t size; 29 32 unsigned int nregions; 30 33 struct { ··· 44 41 #define NVM_VALSIG_REG 0x00000010 45 42 #define NVM_ADDRESS_REG 0x00000040 46 43 #define NVM_REGION_ID_REG 0x00000044 44 + #define NVM_DEBUG_REG 0x00000000 47 45 /* 48 46 * [15:0]-Erase size = 0x0010 4K 0x0080 32K 0x0100 64K 49 47 * [23:16]-Reserved ··· 75 71 #define NVM_FREG_ADDR_MASK GENMASK(31, 16) 76 72 #define NVM_FREG_ADDR_SHIFT 12 77 73 #define NVM_FREG_MIN_REGION_SIZE 0xFFF 74 + 75 + #define NVM_NON_POSTED_ERASE_DONE BIT(23) 76 + #define NVM_NON_POSTED_ERASE_DONE_ITER 3000 78 77 79 78 static inline void idg_nvm_set_region_id(struct intel_dg_nvm *nvm, u8 region) 80 79 { ··· 380 373 static ssize_t 381 374 idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_addr) 382 375 { 376 + void __iomem *base2 = nvm->base2; 383 377 void __iomem *base = nvm->base; 384 378 const u32 block = 0x10; 379 + u32 iter = 0; 380 + u32 reg; 385 381 u64 i; 386 382 387 383 for (i = 0; i < len; i += SZ_4K) { 388 384 iowrite32(from + i, base + NVM_ADDRESS_REG); 389 385 iowrite32(region << 24 | block, base + NVM_ERASE_REG); 386 + if (nvm->non_posted_erase) { 387 + /* Wait for Erase Done */ 388 + reg = ioread32(base2 + NVM_DEBUG_REG); 389 + while (!(reg & NVM_NON_POSTED_ERASE_DONE) && 390 + ++iter < NVM_NON_POSTED_ERASE_DONE_ITER) { 391 + msleep(10); 392 + reg = ioread32(base2 + NVM_DEBUG_REG); 393 + } 394 + if (reg & NVM_NON_POSTED_ERASE_DONE) { 395 + /* Clear Erase Done */ 396 + iowrite32(reg, base2 + NVM_DEBUG_REG); 397 + } else { 398 + *fail_addr = from + i; 399 + return -ETIME; 400 + } 401 + } 390 402 /* Since the writes are via sgunit 391 403 * we cannot do back to back erases. 392 404 */ ··· 414 388 return len; 415 389 } 416 390 417 - static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device) 391 + static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device, 392 + bool non_posted_erase) 418 393 { 419 394 u32 access_map = 0; 420 395 unsigned int i, n; ··· 475 448 n++; 476 449 } 477 450 451 + nvm->non_posted_erase = non_posted_erase; 452 + 478 453 dev_dbg(device, "Registered %d regions\n", n); 454 + dev_dbg(device, "Non posted erase %d\n", nvm->non_posted_erase); 479 455 480 456 /* Need to add 1 to the amount of memory 481 457 * so it is reported as an even block ··· 759 729 goto err; 760 730 } 761 731 762 - ret = intel_dg_nvm_init(nvm, device); 732 + if (invm->non_posted_erase) { 733 + nvm->base2 = devm_ioremap_resource(device, &invm->bar2); 734 + if (IS_ERR(nvm->base2)) { 735 + ret = PTR_ERR(nvm->base2); 736 + goto err; 737 + } 738 + } 739 + 740 + ret = intel_dg_nvm_init(nvm, device, invm->non_posted_erase); 763 741 if (ret < 0) { 764 742 dev_err(device, "cannot initialize nvm %d\n", ret); 765 743 goto err;
+2
include/linux/intel_dg_nvm_aux.h
··· 20 20 struct intel_dg_nvm_dev { 21 21 struct auxiliary_device aux_dev; 22 22 bool writable_override; 23 + bool non_posted_erase; 23 24 struct resource bar; 25 + struct resource bar2; 24 26 const struct intel_dg_nvm_region *regions; 25 27 }; 26 28