xref: /openbmc/linux/drivers/mtd/parsers/scpart.c (revision bc33f5e5)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *    drivers/mtd/scpart.c: Sercomm Partition Parser
4  *
5  *    Copyright (C) 2018 NOGUCHI Hiroshi
6  *    Copyright (C) 2022 Mikhail Zhilkin
7  */
8 
9 #include <linux/kernel.h>
10 #include <linux/slab.h>
11 #include <linux/mtd/mtd.h>
12 #include <linux/mtd/partitions.h>
13 #include <linux/module.h>
14 
15 #define	MOD_NAME	"scpart"
16 
17 #ifdef pr_fmt
18 #undef pr_fmt
19 #endif
20 
21 #define pr_fmt(fmt) MOD_NAME ": " fmt
22 
23 #define	ID_ALREADY_FOUND	0xffffffffUL
24 
25 #define	MAP_OFFS_IN_BLK		0x800
26 #define	MAP_MIRROR_NUM		2
27 
28 static const char sc_part_magic[] = {
29 	'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
30 };
31 #define	PART_MAGIC_LEN		sizeof(sc_part_magic)
32 
33 /* assumes that all fields are set by CPU native endian */
34 struct sc_part_desc {
35 	uint32_t	part_id;
36 	uint32_t	part_offs;
37 	uint32_t	part_bytes;
38 };
39 
40 static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)
41 {
42 	return ((pdesc->part_id != 0xffffffffUL) &&
43 		(pdesc->part_offs != 0xffffffffUL) &&
44 		(pdesc->part_bytes != 0xffffffffUL));
45 }
46 
47 static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
48 			       struct sc_part_desc **ppdesc)
49 {
50 	int cnt = 0;
51 	int res = 0;
52 	int res2;
53 	loff_t offs;
54 	size_t retlen;
55 	struct sc_part_desc *pdesc = NULL;
56 	struct sc_part_desc *tmpdesc;
57 	uint8_t *buf;
58 
59 	buf = kzalloc(master->erasesize, GFP_KERNEL);
60 	if (!buf) {
61 		res = -ENOMEM;
62 		goto out;
63 	}
64 
65 	res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
66 	if (res2 || retlen != master->erasesize) {
67 		res = -EIO;
68 		goto free;
69 	}
70 
71 	for (offs = MAP_OFFS_IN_BLK;
72 	     offs < master->erasesize - sizeof(*tmpdesc);
73 	     offs += sizeof(*tmpdesc)) {
74 		tmpdesc = (struct sc_part_desc *)&buf[offs];
75 		if (!scpart_desc_is_valid(tmpdesc))
76 			break;
77 		cnt++;
78 	}
79 
80 	if (cnt > 0) {
81 		int bytes = cnt * sizeof(*pdesc);
82 
83 		pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);
84 		if (!pdesc) {
85 			res = -ENOMEM;
86 			goto free;
87 		}
88 		memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
89 
90 		*ppdesc = pdesc;
91 		res = cnt;
92 	}
93 
94 free:
95 	kfree(buf);
96 
97 out:
98 	return res;
99 }
100 
101 static int scpart_find_partmap(struct mtd_info *master,
102 			       struct sc_part_desc **ppdesc)
103 {
104 	int magic_found = 0;
105 	int res = 0;
106 	int res2;
107 	loff_t offs = 0;
108 	size_t retlen;
109 	uint8_t rdbuf[PART_MAGIC_LEN];
110 
111 	while ((magic_found < MAP_MIRROR_NUM) &&
112 			(offs < master->size) &&
113 			 !mtd_block_isbad(master, offs)) {
114 		res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
115 		if (res2 || retlen != PART_MAGIC_LEN) {
116 			res = -EIO;
117 			goto out;
118 		}
119 		if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
120 			pr_debug("Signature found at 0x%llx\n", offs);
121 			magic_found++;
122 			res = scpart_scan_partmap(master, offs, ppdesc);
123 			if (res > 0)
124 				goto out;
125 		}
126 		offs += master->erasesize;
127 	}
128 
129 out:
130 	if (res > 0)
131 		pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);
132 	else
133 		pr_info("No valid 'SC PART MAP' was found\n");
134 
135 	return res;
136 }
137 
138 static int scpart_parse(struct mtd_info *master,
139 			const struct mtd_partition **pparts,
140 			struct mtd_part_parser_data *data)
141 {
142 	const char *partname;
143 	int n;
144 	int nr_scparts;
145 	int nr_parts = 0;
146 	int res = 0;
147 	struct sc_part_desc *scpart_map = NULL;
148 	struct mtd_partition *parts = NULL;
149 	struct device_node *mtd_node;
150 	struct device_node *ofpart_node;
151 	struct device_node *pp;
152 
153 	mtd_node = mtd_get_of_node(master);
154 	if (!mtd_node) {
155 		res = -ENOENT;
156 		goto out;
157 	}
158 
159 	ofpart_node = of_get_child_by_name(mtd_node, "partitions");
160 	if (!ofpart_node) {
161 		pr_info("%s: 'partitions' subnode not found on %pOF.\n",
162 				master->name, mtd_node);
163 		res = -ENOENT;
164 		goto out;
165 	}
166 
167 	nr_scparts = scpart_find_partmap(master, &scpart_map);
168 	if (nr_scparts <= 0) {
169 		pr_info("No any partitions was found in 'SC PART MAP'.\n");
170 		res = -ENOENT;
171 		goto free;
172 	}
173 
174 	parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),
175 		GFP_KERNEL);
176 	if (!parts) {
177 		res = -ENOMEM;
178 		goto free;
179 	}
180 
181 	for_each_child_of_node(ofpart_node, pp) {
182 		u32 scpart_id;
183 
184 		if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))
185 			continue;
186 
187 		for (n = 0 ; n < nr_scparts ; n++)
188 			if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
189 					(scpart_id == scpart_map[n].part_id))
190 				break;
191 		if (n >= nr_scparts)
192 			/* not match */
193 			continue;
194 
195 		/* add the partition found in OF into MTD partition array */
196 		parts[nr_parts].offset = scpart_map[n].part_offs;
197 		parts[nr_parts].size = scpart_map[n].part_bytes;
198 		parts[nr_parts].of_node = pp;
199 
200 		if (!of_property_read_string(pp, "label", &partname))
201 			parts[nr_parts].name = partname;
202 		if (of_property_read_bool(pp, "read-only"))
203 			parts[nr_parts].mask_flags |= MTD_WRITEABLE;
204 		if (of_property_read_bool(pp, "lock"))
205 			parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
206 
207 		/* mark as 'done' */
208 		scpart_map[n].part_id = ID_ALREADY_FOUND;
209 
210 		nr_parts++;
211 	}
212 
213 	if (nr_parts > 0) {
214 		*pparts = parts;
215 		res = nr_parts;
216 	} else
217 		pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");
218 
219 	of_node_put(pp);
220 
221 free:
222 	of_node_put(ofpart_node);
223 	kfree(scpart_map);
224 	if (res <= 0)
225 		kfree(parts);
226 
227 out:
228 	return res;
229 }
230 
231 static const struct of_device_id scpart_parser_of_match_table[] = {
232 	{ .compatible = "sercomm,sc-partitions" },
233 	{},
234 };
235 MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
236 
237 static struct mtd_part_parser scpart_parser = {
238 	.parse_fn = scpart_parse,
239 	.name = "scpart",
240 	.of_match_table = scpart_parser_of_match_table,
241 };
242 module_mtd_part_parser(scpart_parser);
243 
244 /* mtd parsers will request the module by parser name */
245 MODULE_ALIAS("scpart");
246 MODULE_LICENSE("GPL");
247 MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
248 MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
249 MODULE_DESCRIPTION("Sercomm partition parser");
250