Linux Audio

Check our new training course

Loading...
v6.8
  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
 28static 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 */
 34struct sc_part_desc {
 35	uint32_t	part_id;
 36	uint32_t	part_offs;
 37	uint32_t	part_bytes;
 38};
 39
 40static 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
 47static 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	uint32_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
 94free:
 95	kfree(buf);
 96
 97out:
 98	return res;
 99}
100
101static 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
129out:
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
138static 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
221free:
222	of_node_put(ofpart_node);
223	kfree(scpart_map);
224	if (res <= 0)
225		kfree(parts);
226
227out:
228	return res;
229}
230
231static const struct of_device_id scpart_parser_of_match_table[] = {
232	{ .compatible = "sercomm,sc-partitions" },
233	{},
234};
235MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
236
237static struct mtd_part_parser scpart_parser = {
238	.parse_fn = scpart_parse,
239	.name = "scpart",
240	.of_match_table = scpart_parser_of_match_table,
241};
242module_mtd_part_parser(scpart_parser);
243
244/* mtd parsers will request the module by parser name */
245MODULE_ALIAS("scpart");
246MODULE_LICENSE("GPL");
247MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
248MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
249MODULE_DESCRIPTION("Sercomm partition parser");
v6.2
  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
 28static 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 */
 34struct sc_part_desc {
 35	uint32_t	part_id;
 36	uint32_t	part_offs;
 37	uint32_t	part_bytes;
 38};
 39
 40static 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
 47static 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	uint32_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
 94free:
 95	kfree(buf);
 96
 97out:
 98	return res;
 99}
100
101static 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
129out:
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
138static 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
221free:
222	of_node_put(ofpart_node);
223	kfree(scpart_map);
224	if (res <= 0)
225		kfree(parts);
226
227out:
228	return res;
229}
230
231static const struct of_device_id scpart_parser_of_match_table[] = {
232	{ .compatible = "sercomm,sc-partitions" },
233	{},
234};
235MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
236
237static struct mtd_part_parser scpart_parser = {
238	.parse_fn = scpart_parse,
239	.name = "scpart",
240	.of_match_table = scpart_parser_of_match_table,
241};
242module_mtd_part_parser(scpart_parser);
243
244/* mtd parsers will request the module by parser name */
245MODULE_ALIAS("scpart");
246MODULE_LICENSE("GPL");
247MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
248MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
249MODULE_DESCRIPTION("Sercomm partition parser");