1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
6 #include <linux/crc32.h>
8 #include <linux/module.h>
9 #include <linux/nvmem-provider.h>
10 #include <linux/of_reserved_mem.h>
11 #include <linux/platform_device.h>
12 #include <linux/slab.h>
16 struct nvmem_device
*nvmem
;
17 struct reserved_mem
*mem
;
20 struct rmem_match_data
{
21 int (*checksum
)(struct rmem
*priv
);
24 struct __packed rmem_eyeq5_header
{
30 #define RMEM_EYEQ5_MAGIC ((u32)0xDABBAD00)
32 static int rmem_read(void *context
, unsigned int offset
,
33 void *val
, size_t bytes
)
35 struct rmem
*priv
= context
;
38 if ((phys_addr_t
)offset
+ bytes
> priv
->mem
->size
)
42 * Only map the reserved memory at this point to avoid potential rogue
43 * kernel threads inadvertently modifying it. Based on the current
44 * uses-cases for this driver, the performance hit isn't a concern.
45 * Nor is likely to be, given the nature of the subsystem. Most nvmem
46 * devices operate over slow buses to begin with.
48 * An alternative would be setting the memory as RO, set_memory_ro(),
49 * but as of Dec 2020 this isn't possible on arm64.
51 addr
= memremap(priv
->mem
->base
, priv
->mem
->size
, MEMREMAP_WB
);
53 dev_err(priv
->dev
, "Failed to remap memory region\n");
57 memcpy(val
, addr
+ offset
, bytes
);
64 static int rmem_eyeq5_checksum(struct rmem
*priv
)
66 void *buf
__free(kfree
) = NULL
;
67 struct rmem_eyeq5_header header
;
68 u32 computed_crc
, *target_crc
;
72 ret
= rmem_read(priv
, 0, &header
, sizeof(header
));
76 if (header
.magic
!= RMEM_EYEQ5_MAGIC
)
80 * Avoid massive kmalloc() if header read is invalid;
81 * the check would be done by the next rmem_read() anyway.
83 if (header
.size
> priv
->mem
->size
)
87 * 0 +-------------------+
88 * | Header (12 bytes) | \
89 * +-------------------+ |
90 * | | | data to be CRCed
93 * data_size +-------------------+
95 * header.size +-------------------+
98 buf
= kmalloc(header
.size
, GFP_KERNEL
);
102 ret
= rmem_read(priv
, 0, buf
, header
.size
);
106 data_size
= header
.size
- sizeof(*target_crc
);
107 target_crc
= buf
+ data_size
;
108 computed_crc
= crc32(U32_MAX
, buf
, data_size
) ^ U32_MAX
;
110 if (computed_crc
== *target_crc
)
114 "checksum failed: computed %#x, expected %#x, header (%#x, %#x, %#x)\n",
115 computed_crc
, *target_crc
, header
.magic
, header
.version
, header
.size
);
119 static int rmem_probe(struct platform_device
*pdev
)
121 struct nvmem_config config
= { };
122 struct device
*dev
= &pdev
->dev
;
123 const struct rmem_match_data
*match_data
= device_get_match_data(dev
);
124 struct reserved_mem
*mem
;
127 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
132 mem
= of_reserved_mem_lookup(dev
->of_node
);
134 dev_err(dev
, "Failed to lookup reserved memory\n");
141 config
.name
= "rmem";
142 config
.id
= NVMEM_DEVID_AUTO
;
143 config
.size
= mem
->size
;
144 config
.reg_read
= rmem_read
;
146 if (match_data
&& match_data
->checksum
) {
147 int ret
= match_data
->checksum(priv
);
153 return PTR_ERR_OR_ZERO(devm_nvmem_register(dev
, &config
));
156 static const struct rmem_match_data rmem_eyeq5_match_data
= {
157 .checksum
= rmem_eyeq5_checksum
,
160 static const struct of_device_id rmem_match
[] = {
161 { .compatible
= "mobileye,eyeq5-bootloader-config", .data
= &rmem_eyeq5_match_data
},
162 { .compatible
= "nvmem-rmem", },
165 MODULE_DEVICE_TABLE(of
, rmem_match
);
167 static struct platform_driver rmem_driver
= {
171 .of_match_table
= rmem_match
,
174 module_platform_driver(rmem_driver
);
176 MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
177 MODULE_DESCRIPTION("Reserved Memory Based nvmem Driver");
178 MODULE_LICENSE("GPL");