Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
0004  */
0005 
0006 #include <linux/io.h>
0007 #include <linux/module.h>
0008 #include <linux/nvmem-provider.h>
0009 #include <linux/of_reserved_mem.h>
0010 #include <linux/platform_device.h>
0011 
0012 struct rmem {
0013     struct device *dev;
0014     struct nvmem_device *nvmem;
0015     struct reserved_mem *mem;
0016 
0017     phys_addr_t size;
0018 };
0019 
0020 static int rmem_read(void *context, unsigned int offset,
0021              void *val, size_t bytes)
0022 {
0023     struct rmem *priv = context;
0024     size_t available = priv->mem->size;
0025     loff_t off = offset;
0026     void *addr;
0027     int count;
0028 
0029     /*
0030      * Only map the reserved memory at this point to avoid potential rogue
0031      * kernel threads inadvertently modifying it. Based on the current
0032      * uses-cases for this driver, the performance hit isn't a concern.
0033      * Nor is likely to be, given the nature of the subsystem. Most nvmem
0034      * devices operate over slow buses to begin with.
0035      *
0036      * An alternative would be setting the memory as RO, set_memory_ro(),
0037      * but as of Dec 2020 this isn't possible on arm64.
0038      */
0039     addr = memremap(priv->mem->base, available, MEMREMAP_WB);
0040     if (IS_ERR(addr)) {
0041         dev_err(priv->dev, "Failed to remap memory region\n");
0042         return PTR_ERR(addr);
0043     }
0044 
0045     count = memory_read_from_buffer(val, bytes, &off, addr, available);
0046 
0047     memunmap(addr);
0048 
0049     return count;
0050 }
0051 
0052 static int rmem_probe(struct platform_device *pdev)
0053 {
0054     struct nvmem_config config = { };
0055     struct device *dev = &pdev->dev;
0056     struct reserved_mem *mem;
0057     struct rmem *priv;
0058 
0059     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0060     if (!priv)
0061         return -ENOMEM;
0062     priv->dev = dev;
0063 
0064     mem = of_reserved_mem_lookup(dev->of_node);
0065     if (!mem) {
0066         dev_err(dev, "Failed to lookup reserved memory\n");
0067         return -EINVAL;
0068     }
0069     priv->mem = mem;
0070 
0071     config.dev = dev;
0072     config.priv = priv;
0073     config.name = "rmem";
0074     config.size = mem->size;
0075     config.reg_read = rmem_read;
0076 
0077     return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
0078 }
0079 
0080 static const struct of_device_id rmem_match[] = {
0081     { .compatible = "nvmem-rmem", },
0082     { /* sentinel */ },
0083 };
0084 MODULE_DEVICE_TABLE(of, rmem_match);
0085 
0086 static struct platform_driver rmem_driver = {
0087     .probe = rmem_probe,
0088     .driver = {
0089         .name = "rmem",
0090         .of_match_table = rmem_match,
0091     },
0092 };
0093 module_platform_driver(rmem_driver);
0094 
0095 MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
0096 MODULE_DESCRIPTION("Reserved Memory Based nvmem Driver");
0097 MODULE_LICENSE("GPL");