diff --git a/drivers/mtd/spi-nor/everspin.c b/drivers/mtd/spi-nor/everspin.c index 96eda6e3dc5c..7de8f6c2ba53 100644 --- a/drivers/mtd/spi-nor/everspin.c +++ b/drivers/mtd/spi-nor/everspin.c @@ -5,10 +5,18 @@ */ #include +#include #include +#include +#include #include #include "core.h" +/* Private data stored in nor->priv for MRAM mmap support */ +struct everspin_mram_priv { + struct map_info map; +}; + /* Optimization for 200 MHz: 20 Dummy Cycles are typically required */ #define EVERSPIN_MRAM_DUMMY_CYCLES 8 #define EVERSPIN_MRAM_DUMMY_CYCLES_FAST 20 @@ -289,6 +297,9 @@ static int everspin_mram_setup(struct spi_nor *nor, static int everspin_mram_late_init(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; + struct platform_device *ctlr_pdev; + struct everspin_mram_priv *priv; + struct resource *res_ahb; dev_info(nor->dev, "Finalizing 8s-8s-8s STR: Write/Read fully functional.\n"); @@ -326,6 +337,59 @@ static int everspin_mram_late_init(struct spi_nor *nor) nor->params->setup = everspin_mram_setup; + /* + * Set up MTD_RAM so that /dev/mtd0 supports mmap(). + * + * The Cadence OSPI controller exposes the flash memory as a directly + * accessible window in the CPU address space (DAC / AHB mode). We + * read the physical base address of that window from the controller's + * platform_device resource[1] – the same resource that cqspi_probe() + * uses for ioremap. This lets mtdchar_mmap() call vm_iomap_memory() + * and hand the physical pages directly to user-space. + */ + priv = devm_kzalloc(nor->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* + * nor->spimem->spi->controller->dev.parent is the platform_device + * of the Cadence QSPI controller. Its IORESOURCE_MEM[1] is the AHB + * (DAC) window – index 0 is the APB register block. + */ + ctlr_pdev = to_platform_device(nor->spimem->spi->controller->dev.parent); + res_ahb = platform_get_resource(ctlr_pdev, IORESOURCE_MEM, 1); + if (!res_ahb) { + dev_warn(nor->dev, + "MRAM: cannot find AHB resource – mmap() disabled\n"); + devm_kfree(nor->dev, priv); + return 0; + } + + priv->map.name = nor->mtd.name; + priv->map.phys = res_ahb->start; + priv->map.size = resource_size(res_ahb); + priv->map.virt = NULL; /* not needed for mmap path */ + + nor->priv = priv; + + /* + * Advertise this device as byte-addressable RAM so that + * mtdchar_mmap() takes the vm_iomap_memory() path. + * MTD_CAP_RAM = MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE + */ + nor->mtd.type = MTD_RAM; + nor->mtd.flags = MTD_CAP_RAM; + + /* + * mtdchar_mmap() casts mtd->priv to struct map_info * when + * mtd->type == MTD_RAM. Point it at our embedded map_info. + */ + nor->mtd.priv = &priv->map; + + dev_info(nor->dev, + "MRAM: AHB window at %pa size %pa – mmap() enabled\n", + &res_ahb->start, &priv->map.size); + return 0; }