mtd: spi-nor: everspin: enable mmap() for MRAM via Cadence OSPI DAC window
The Cadence OSPI controller (ti,am654-ospi) provides a Direct Access
Controller (DAC) mode: the flash/MRAM address space is visible as a
contiguous window in the CPU's physical address space via the AHB bus
(resource[1] in the platform device, base address ~0x60000000 on AM62x).
When DAC mode is active the driver already uses memcpy_fromio() /
memcpy_toio() against this window for all data transfers. There is
therefore a 1:1 mapping between MTD offsets and physical addresses:
phys_addr = res_ahb->start + mtd_offset
Wire this up to allow user-space mmap() on /dev/mtd0:
1. Allocate a struct everspin_mram_priv (contains an embedded
struct map_info) via devm_kzalloc() in the late_init fixup hook.
2. Read the AHB window physical base address from the controller's
platform_device resource[1] using platform_get_resource().
This is the same resource that cqspi_probe() uses for ioremap,
so it is always valid when DAC mode is active.
3. Fill in map_info.phys and map_info.size, store it in nor->priv
and point mtd->priv at it.
4. Set mtd->type = MTD_RAM and mtd->flags = MTD_CAP_RAM so that
mtdchar_mmap() takes the vm_iomap_memory() path introduced in the
companion patch 'mtd: mtdchar: enable mmap() for MTD_RAM devices
with a physical map'.
If the AHB resource is not found (e.g. non-Cadence controller) the
function logs a warning and continues without mmap support; all other
functionality is unaffected.
After this change user-space can do:
int fd = open("/dev/mtd0", O_RDWR | O_SYNC);
void *p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
and get direct access to the MRAM without any kernel copy overhead.
Tested on AM6232 (PFC-750-8xxx) with Everspin EM008LXO in Octal-STR
(8-8-8) mode at 200 MHz.
Signed-off-by: Heinrich Toews <ht@twx-software.de>
This commit is contained in:
@@ -5,10 +5,18 @@
|
||||
*/
|
||||
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user