1*5a3fa75aSNicolas Saenz Julienne // SPDX-License-Identifier: GPL-2.0+ 2*5a3fa75aSNicolas Saenz Julienne /* 3*5a3fa75aSNicolas Saenz Julienne * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de> 4*5a3fa75aSNicolas Saenz Julienne */ 5*5a3fa75aSNicolas Saenz Julienne 6*5a3fa75aSNicolas Saenz Julienne #include <linux/io.h> 7*5a3fa75aSNicolas Saenz Julienne #include <linux/module.h> 8*5a3fa75aSNicolas Saenz Julienne #include <linux/nvmem-provider.h> 9*5a3fa75aSNicolas Saenz Julienne #include <linux/of_reserved_mem.h> 10*5a3fa75aSNicolas Saenz Julienne #include <linux/platform_device.h> 11*5a3fa75aSNicolas Saenz Julienne 12*5a3fa75aSNicolas Saenz Julienne struct rmem { 13*5a3fa75aSNicolas Saenz Julienne struct device *dev; 14*5a3fa75aSNicolas Saenz Julienne struct nvmem_device *nvmem; 15*5a3fa75aSNicolas Saenz Julienne struct reserved_mem *mem; 16*5a3fa75aSNicolas Saenz Julienne 17*5a3fa75aSNicolas Saenz Julienne phys_addr_t size; 18*5a3fa75aSNicolas Saenz Julienne }; 19*5a3fa75aSNicolas Saenz Julienne 20*5a3fa75aSNicolas Saenz Julienne static int rmem_read(void *context, unsigned int offset, 21*5a3fa75aSNicolas Saenz Julienne void *val, size_t bytes) 22*5a3fa75aSNicolas Saenz Julienne { 23*5a3fa75aSNicolas Saenz Julienne struct rmem *priv = context; 24*5a3fa75aSNicolas Saenz Julienne size_t available = priv->mem->size; 25*5a3fa75aSNicolas Saenz Julienne loff_t off = offset; 26*5a3fa75aSNicolas Saenz Julienne void *addr; 27*5a3fa75aSNicolas Saenz Julienne int count; 28*5a3fa75aSNicolas Saenz Julienne 29*5a3fa75aSNicolas Saenz Julienne /* 30*5a3fa75aSNicolas Saenz Julienne * Only map the reserved memory at this point to avoid potential rogue 31*5a3fa75aSNicolas Saenz Julienne * kernel threads inadvertently modifying it. Based on the current 32*5a3fa75aSNicolas Saenz Julienne * uses-cases for this driver, the performance hit isn't a concern. 33*5a3fa75aSNicolas Saenz Julienne * Nor is likely to be, given the nature of the subsystem. Most nvmem 34*5a3fa75aSNicolas Saenz Julienne * devices operate over slow buses to begin with. 35*5a3fa75aSNicolas Saenz Julienne * 36*5a3fa75aSNicolas Saenz Julienne * An alternative would be setting the memory as RO, set_memory_ro(), 37*5a3fa75aSNicolas Saenz Julienne * but as of Dec 2020 this isn't possible on arm64. 38*5a3fa75aSNicolas Saenz Julienne */ 39*5a3fa75aSNicolas Saenz Julienne addr = memremap(priv->mem->base, available, MEMREMAP_WB); 40*5a3fa75aSNicolas Saenz Julienne if (IS_ERR(addr)) { 41*5a3fa75aSNicolas Saenz Julienne dev_err(priv->dev, "Failed to remap memory region\n"); 42*5a3fa75aSNicolas Saenz Julienne return PTR_ERR(addr); 43*5a3fa75aSNicolas Saenz Julienne } 44*5a3fa75aSNicolas Saenz Julienne 45*5a3fa75aSNicolas Saenz Julienne count = memory_read_from_buffer(val, bytes, &off, addr, available); 46*5a3fa75aSNicolas Saenz Julienne 47*5a3fa75aSNicolas Saenz Julienne memunmap(addr); 48*5a3fa75aSNicolas Saenz Julienne 49*5a3fa75aSNicolas Saenz Julienne return count; 50*5a3fa75aSNicolas Saenz Julienne } 51*5a3fa75aSNicolas Saenz Julienne 52*5a3fa75aSNicolas Saenz Julienne static int rmem_probe(struct platform_device *pdev) 53*5a3fa75aSNicolas Saenz Julienne { 54*5a3fa75aSNicolas Saenz Julienne struct nvmem_config config = { }; 55*5a3fa75aSNicolas Saenz Julienne struct device *dev = &pdev->dev; 56*5a3fa75aSNicolas Saenz Julienne struct reserved_mem *mem; 57*5a3fa75aSNicolas Saenz Julienne struct rmem *priv; 58*5a3fa75aSNicolas Saenz Julienne 59*5a3fa75aSNicolas Saenz Julienne priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 60*5a3fa75aSNicolas Saenz Julienne if (!priv) 61*5a3fa75aSNicolas Saenz Julienne return -ENOMEM; 62*5a3fa75aSNicolas Saenz Julienne priv->dev = dev; 63*5a3fa75aSNicolas Saenz Julienne 64*5a3fa75aSNicolas Saenz Julienne mem = of_reserved_mem_lookup(dev->of_node); 65*5a3fa75aSNicolas Saenz Julienne if (!mem) { 66*5a3fa75aSNicolas Saenz Julienne dev_err(dev, "Failed to lookup reserved memory\n"); 67*5a3fa75aSNicolas Saenz Julienne return -EINVAL; 68*5a3fa75aSNicolas Saenz Julienne } 69*5a3fa75aSNicolas Saenz Julienne priv->mem = mem; 70*5a3fa75aSNicolas Saenz Julienne 71*5a3fa75aSNicolas Saenz Julienne config.dev = dev; 72*5a3fa75aSNicolas Saenz Julienne config.priv = priv; 73*5a3fa75aSNicolas Saenz Julienne config.name = "rmem"; 74*5a3fa75aSNicolas Saenz Julienne config.size = mem->size; 75*5a3fa75aSNicolas Saenz Julienne config.reg_read = rmem_read; 76*5a3fa75aSNicolas Saenz Julienne 77*5a3fa75aSNicolas Saenz Julienne return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); 78*5a3fa75aSNicolas Saenz Julienne } 79*5a3fa75aSNicolas Saenz Julienne 80*5a3fa75aSNicolas Saenz Julienne static const struct of_device_id rmem_match[] = { 81*5a3fa75aSNicolas Saenz Julienne { .compatible = "nvmem-rmem", }, 82*5a3fa75aSNicolas Saenz Julienne { /* sentinel */ }, 83*5a3fa75aSNicolas Saenz Julienne }; 84*5a3fa75aSNicolas Saenz Julienne MODULE_DEVICE_TABLE(of, rmem_match); 85*5a3fa75aSNicolas Saenz Julienne 86*5a3fa75aSNicolas Saenz Julienne static struct platform_driver rmem_driver = { 87*5a3fa75aSNicolas Saenz Julienne .probe = rmem_probe, 88*5a3fa75aSNicolas Saenz Julienne .driver = { 89*5a3fa75aSNicolas Saenz Julienne .name = "rmem", 90*5a3fa75aSNicolas Saenz Julienne .of_match_table = rmem_match, 91*5a3fa75aSNicolas Saenz Julienne }, 92*5a3fa75aSNicolas Saenz Julienne }; 93*5a3fa75aSNicolas Saenz Julienne module_platform_driver(rmem_driver); 94*5a3fa75aSNicolas Saenz Julienne 95*5a3fa75aSNicolas Saenz Julienne MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>"); 96*5a3fa75aSNicolas Saenz Julienne MODULE_DESCRIPTION("Reserved Memory Based nvmem Driver"); 97*5a3fa75aSNicolas Saenz Julienne MODULE_LICENSE("GPL"); 98