]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/xe/nvm: add support for non-posted erase
authorReuven Abliyev <reuven.abliyev@intel.com>
Tue, 17 Jun 2025 14:51:58 +0000 (17:51 +0300)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Mon, 23 Jun 2025 17:14:50 +0000 (13:14 -0400)
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>
drivers/gpu/drm/xe/xe_nvm.c
drivers/mtd/devices/mtd_intel_dg.c
include/linux/intel_dg_nvm_aux.h

index 20aa3b5d3637bcf5e4e915141273584643248255..61b0a1531a539ba40014f8fa1b1e507ae6de29cd 100644 (file)
 #include "xe_sriov.h"
 
 #define GEN12_GUNIT_NVM_BASE 0x00102040
+#define GEN12_DEBUG_NVM_BASE 0x00101018
+
+#define GEN12_CNTL_PROTECTED_NVM_REG 0x0010100C
+
 #define GEN12_GUNIT_NVM_SIZE 0x80
+#define GEN12_DEBUG_NVM_SIZE 0x4
+
+#define NVM_NON_POSTED_ERASE_CHICKEN_BIT BIT(13)
+
 #define HECI_FW_STATUS_2_NVM_ACCESS_MODE BIT(3)
 
 static const struct intel_dg_nvm_region regions[INTEL_DG_NVM_REGIONS] = {
@@ -29,6 +37,16 @@ static void xe_nvm_release_dev(struct device *dev)
 {
 }
 
+static bool xe_nvm_non_posted_erase(struct xe_device *xe)
+{
+       struct xe_gt *gt = xe_root_mmio_gt(xe);
+
+       if (xe->info.platform != XE_BATTLEMAGE)
+               return false;
+       return !(xe_mmio_read32(&gt->mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) &
+                NVM_NON_POSTED_ERASE_CHICKEN_BIT);
+}
+
 static bool xe_nvm_writable_override(struct xe_device *xe)
 {
        struct xe_gt *gt = xe_root_mmio_gt(xe);
@@ -86,6 +104,7 @@ int xe_nvm_init(struct xe_device *xe)
        nvm = xe->nvm;
 
        nvm->writable_override = xe_nvm_writable_override(xe);
+       nvm->non_posted_erase = xe_nvm_non_posted_erase(xe);
        nvm->bar.parent = &pdev->resource[0];
        nvm->bar.start = GEN12_GUNIT_NVM_BASE + pdev->resource[0].start;
        nvm->bar.end = nvm->bar.start + GEN12_GUNIT_NVM_SIZE - 1;
@@ -93,6 +112,12 @@ int xe_nvm_init(struct xe_device *xe)
        nvm->bar.desc = IORES_DESC_NONE;
        nvm->regions = regions;
 
+       nvm->bar2.parent = &pdev->resource[0];
+       nvm->bar2.start = GEN12_DEBUG_NVM_BASE + pdev->resource[0].start;
+       nvm->bar2.end = nvm->bar2.start + GEN12_DEBUG_NVM_SIZE - 1;
+       nvm->bar2.flags = IORESOURCE_MEM;
+       nvm->bar2.desc = IORES_DESC_NONE;
+
        aux_dev = &nvm->aux_dev;
 
        aux_dev->name = "nvm";
index 97e1dc1ada5dc4ca8fbc260821f429ff08a91752..b438ee5aacc34a89d4ef8691ad950df7220aa833 100644 (file)
@@ -25,6 +25,9 @@ struct intel_dg_nvm {
        struct mtd_info mtd;
        struct mutex lock; /* region access lock */
        void __iomem *base;
+       void __iomem *base2;
+       bool non_posted_erase;
+
        size_t size;
        unsigned int nregions;
        struct {
@@ -41,6 +44,7 @@ struct intel_dg_nvm {
 #define NVM_VALSIG_REG        0x00000010
 #define NVM_ADDRESS_REG       0x00000040
 #define NVM_REGION_ID_REG     0x00000044
+#define NVM_DEBUG_REG         0x00000000
 /*
  * [15:0]-Erase size = 0x0010 4K 0x0080 32K 0x0100 64K
  * [23:16]-Reserved
@@ -72,6 +76,9 @@ struct intel_dg_nvm {
 #define NVM_FREG_ADDR_SHIFT 12
 #define NVM_FREG_MIN_REGION_SIZE 0xFFF
 
+#define NVM_NON_POSTED_ERASE_DONE BIT(23)
+#define NVM_NON_POSTED_ERASE_DONE_ITER 3000
+
 static inline void idg_nvm_set_region_id(struct intel_dg_nvm *nvm, u8 region)
 {
        iowrite32((u32)region, nvm->base + NVM_REGION_ID_REG);
@@ -373,13 +380,32 @@ static ssize_t idg_read(struct intel_dg_nvm *nvm, u8 region,
 static ssize_t
 idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_addr)
 {
+       void __iomem *base2 = nvm->base2;
        void __iomem *base = nvm->base;
        const u32 block = 0x10;
+       u32 iter = 0;
+       u32 reg;
        u64 i;
 
        for (i = 0; i < len; i += SZ_4K) {
                iowrite32(from + i, base + NVM_ADDRESS_REG);
                iowrite32(region << 24 | block, base + NVM_ERASE_REG);
+               if (nvm->non_posted_erase) {
+                       /* Wait for Erase Done */
+                       reg = ioread32(base2 + NVM_DEBUG_REG);
+                       while (!(reg & NVM_NON_POSTED_ERASE_DONE) &&
+                              ++iter < NVM_NON_POSTED_ERASE_DONE_ITER) {
+                               msleep(10);
+                               reg = ioread32(base2 + NVM_DEBUG_REG);
+                       }
+                       if (reg & NVM_NON_POSTED_ERASE_DONE) {
+                               /* Clear Erase Done */
+                               iowrite32(reg, base2 + NVM_DEBUG_REG);
+                       } else {
+                               *fail_addr = from + i;
+                               return -ETIME;
+                       }
+               }
                /* Since the writes are via sgunit
                 * we cannot do back to back erases.
                 */
@@ -388,7 +414,8 @@ idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_a
        return len;
 }
 
-static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device)
+static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device,
+                            bool non_posted_erase)
 {
        u32 access_map = 0;
        unsigned int i, n;
@@ -448,7 +475,10 @@ static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device)
                        n++;
        }
 
+       nvm->non_posted_erase = non_posted_erase;
+
        dev_dbg(device, "Registered %d regions\n", n);
+       dev_dbg(device, "Non posted erase %d\n", nvm->non_posted_erase);
 
        /* Need to add 1 to the amount of memory
         * so it is reported as an even block
@@ -729,7 +759,15 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
                goto err;
        }
 
-       ret = intel_dg_nvm_init(nvm, device);
+       if (invm->non_posted_erase) {
+               nvm->base2 = devm_ioremap_resource(device, &invm->bar2);
+               if (IS_ERR(nvm->base2)) {
+                       ret = PTR_ERR(nvm->base2);
+                       goto err;
+               }
+       }
+
+       ret = intel_dg_nvm_init(nvm, device, invm->non_posted_erase);
        if (ret < 0) {
                dev_err(device, "cannot initialize nvm %d\n", ret);
                goto err;
index 00b6c1301bd8a5b66410c84739e2c545cc18ac4c..625d46a6b96e5cb3224c77a825e5fba9e20b603c 100644 (file)
@@ -20,7 +20,9 @@ struct intel_dg_nvm_region {
 struct intel_dg_nvm_dev {
        struct auxiliary_device aux_dev;
        bool writable_override;
+       bool non_posted_erase;
        struct resource bar;
+       struct resource bar2;
        const struct intel_dg_nvm_region *regions;
 };