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