19b78ef0cSMikhail Zhilkin // SPDX-License-Identifier: GPL-2.0-or-later
29b78ef0cSMikhail Zhilkin /*
39b78ef0cSMikhail Zhilkin * drivers/mtd/scpart.c: Sercomm Partition Parser
49b78ef0cSMikhail Zhilkin *
59b78ef0cSMikhail Zhilkin * Copyright (C) 2018 NOGUCHI Hiroshi
69b78ef0cSMikhail Zhilkin * Copyright (C) 2022 Mikhail Zhilkin
79b78ef0cSMikhail Zhilkin */
89b78ef0cSMikhail Zhilkin
99b78ef0cSMikhail Zhilkin #include <linux/kernel.h>
109b78ef0cSMikhail Zhilkin #include <linux/slab.h>
119b78ef0cSMikhail Zhilkin #include <linux/mtd/mtd.h>
129b78ef0cSMikhail Zhilkin #include <linux/mtd/partitions.h>
139b78ef0cSMikhail Zhilkin #include <linux/module.h>
149b78ef0cSMikhail Zhilkin
159b78ef0cSMikhail Zhilkin #define MOD_NAME "scpart"
169b78ef0cSMikhail Zhilkin
179b78ef0cSMikhail Zhilkin #ifdef pr_fmt
189b78ef0cSMikhail Zhilkin #undef pr_fmt
199b78ef0cSMikhail Zhilkin #endif
209b78ef0cSMikhail Zhilkin
219b78ef0cSMikhail Zhilkin #define pr_fmt(fmt) MOD_NAME ": " fmt
229b78ef0cSMikhail Zhilkin
239b78ef0cSMikhail Zhilkin #define ID_ALREADY_FOUND 0xffffffffUL
249b78ef0cSMikhail Zhilkin
259b78ef0cSMikhail Zhilkin #define MAP_OFFS_IN_BLK 0x800
269b78ef0cSMikhail Zhilkin #define MAP_MIRROR_NUM 2
279b78ef0cSMikhail Zhilkin
289b78ef0cSMikhail Zhilkin static const char sc_part_magic[] = {
299b78ef0cSMikhail Zhilkin 'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
309b78ef0cSMikhail Zhilkin };
319b78ef0cSMikhail Zhilkin #define PART_MAGIC_LEN sizeof(sc_part_magic)
329b78ef0cSMikhail Zhilkin
339b78ef0cSMikhail Zhilkin /* assumes that all fields are set by CPU native endian */
349b78ef0cSMikhail Zhilkin struct sc_part_desc {
359b78ef0cSMikhail Zhilkin uint32_t part_id;
369b78ef0cSMikhail Zhilkin uint32_t part_offs;
379b78ef0cSMikhail Zhilkin uint32_t part_bytes;
389b78ef0cSMikhail Zhilkin };
399b78ef0cSMikhail Zhilkin
scpart_desc_is_valid(struct sc_part_desc * pdesc)409b78ef0cSMikhail Zhilkin static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)
419b78ef0cSMikhail Zhilkin {
429b78ef0cSMikhail Zhilkin return ((pdesc->part_id != 0xffffffffUL) &&
439b78ef0cSMikhail Zhilkin (pdesc->part_offs != 0xffffffffUL) &&
449b78ef0cSMikhail Zhilkin (pdesc->part_bytes != 0xffffffffUL));
459b78ef0cSMikhail Zhilkin }
469b78ef0cSMikhail Zhilkin
scpart_scan_partmap(struct mtd_info * master,loff_t partmap_offs,struct sc_part_desc ** ppdesc)479b78ef0cSMikhail Zhilkin static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
489b78ef0cSMikhail Zhilkin struct sc_part_desc **ppdesc)
499b78ef0cSMikhail Zhilkin {
509b78ef0cSMikhail Zhilkin int cnt = 0;
519b78ef0cSMikhail Zhilkin int res = 0;
529b78ef0cSMikhail Zhilkin int res2;
53*105c14b8SMikhail Zhilkin uint32_t offs;
549b78ef0cSMikhail Zhilkin size_t retlen;
559b78ef0cSMikhail Zhilkin struct sc_part_desc *pdesc = NULL;
569b78ef0cSMikhail Zhilkin struct sc_part_desc *tmpdesc;
579b78ef0cSMikhail Zhilkin uint8_t *buf;
589b78ef0cSMikhail Zhilkin
599b78ef0cSMikhail Zhilkin buf = kzalloc(master->erasesize, GFP_KERNEL);
609b78ef0cSMikhail Zhilkin if (!buf) {
619b78ef0cSMikhail Zhilkin res = -ENOMEM;
629b78ef0cSMikhail Zhilkin goto out;
639b78ef0cSMikhail Zhilkin }
649b78ef0cSMikhail Zhilkin
659b78ef0cSMikhail Zhilkin res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
669b78ef0cSMikhail Zhilkin if (res2 || retlen != master->erasesize) {
679b78ef0cSMikhail Zhilkin res = -EIO;
689b78ef0cSMikhail Zhilkin goto free;
699b78ef0cSMikhail Zhilkin }
709b78ef0cSMikhail Zhilkin
719b78ef0cSMikhail Zhilkin for (offs = MAP_OFFS_IN_BLK;
729b78ef0cSMikhail Zhilkin offs < master->erasesize - sizeof(*tmpdesc);
739b78ef0cSMikhail Zhilkin offs += sizeof(*tmpdesc)) {
749b78ef0cSMikhail Zhilkin tmpdesc = (struct sc_part_desc *)&buf[offs];
759b78ef0cSMikhail Zhilkin if (!scpart_desc_is_valid(tmpdesc))
769b78ef0cSMikhail Zhilkin break;
779b78ef0cSMikhail Zhilkin cnt++;
789b78ef0cSMikhail Zhilkin }
799b78ef0cSMikhail Zhilkin
809b78ef0cSMikhail Zhilkin if (cnt > 0) {
819b78ef0cSMikhail Zhilkin int bytes = cnt * sizeof(*pdesc);
829b78ef0cSMikhail Zhilkin
839b78ef0cSMikhail Zhilkin pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);
849b78ef0cSMikhail Zhilkin if (!pdesc) {
859b78ef0cSMikhail Zhilkin res = -ENOMEM;
869b78ef0cSMikhail Zhilkin goto free;
879b78ef0cSMikhail Zhilkin }
889b78ef0cSMikhail Zhilkin memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
899b78ef0cSMikhail Zhilkin
909b78ef0cSMikhail Zhilkin *ppdesc = pdesc;
919b78ef0cSMikhail Zhilkin res = cnt;
929b78ef0cSMikhail Zhilkin }
939b78ef0cSMikhail Zhilkin
949b78ef0cSMikhail Zhilkin free:
959b78ef0cSMikhail Zhilkin kfree(buf);
969b78ef0cSMikhail Zhilkin
979b78ef0cSMikhail Zhilkin out:
989b78ef0cSMikhail Zhilkin return res;
999b78ef0cSMikhail Zhilkin }
1009b78ef0cSMikhail Zhilkin
scpart_find_partmap(struct mtd_info * master,struct sc_part_desc ** ppdesc)1019b78ef0cSMikhail Zhilkin static int scpart_find_partmap(struct mtd_info *master,
1029b78ef0cSMikhail Zhilkin struct sc_part_desc **ppdesc)
1039b78ef0cSMikhail Zhilkin {
1049b78ef0cSMikhail Zhilkin int magic_found = 0;
1059b78ef0cSMikhail Zhilkin int res = 0;
1069b78ef0cSMikhail Zhilkin int res2;
1079b78ef0cSMikhail Zhilkin loff_t offs = 0;
1089b78ef0cSMikhail Zhilkin size_t retlen;
1099b78ef0cSMikhail Zhilkin uint8_t rdbuf[PART_MAGIC_LEN];
1109b78ef0cSMikhail Zhilkin
1119b78ef0cSMikhail Zhilkin while ((magic_found < MAP_MIRROR_NUM) &&
1129b78ef0cSMikhail Zhilkin (offs < master->size) &&
1139b78ef0cSMikhail Zhilkin !mtd_block_isbad(master, offs)) {
1149b78ef0cSMikhail Zhilkin res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
1159b78ef0cSMikhail Zhilkin if (res2 || retlen != PART_MAGIC_LEN) {
1169b78ef0cSMikhail Zhilkin res = -EIO;
1179b78ef0cSMikhail Zhilkin goto out;
1189b78ef0cSMikhail Zhilkin }
1199b78ef0cSMikhail Zhilkin if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
1209b78ef0cSMikhail Zhilkin pr_debug("Signature found at 0x%llx\n", offs);
1219b78ef0cSMikhail Zhilkin magic_found++;
1229b78ef0cSMikhail Zhilkin res = scpart_scan_partmap(master, offs, ppdesc);
1239b78ef0cSMikhail Zhilkin if (res > 0)
1249b78ef0cSMikhail Zhilkin goto out;
1259b78ef0cSMikhail Zhilkin }
1269b78ef0cSMikhail Zhilkin offs += master->erasesize;
1279b78ef0cSMikhail Zhilkin }
1289b78ef0cSMikhail Zhilkin
1299b78ef0cSMikhail Zhilkin out:
1309b78ef0cSMikhail Zhilkin if (res > 0)
1319b78ef0cSMikhail Zhilkin pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);
1329b78ef0cSMikhail Zhilkin else
1339b78ef0cSMikhail Zhilkin pr_info("No valid 'SC PART MAP' was found\n");
1349b78ef0cSMikhail Zhilkin
1359b78ef0cSMikhail Zhilkin return res;
1369b78ef0cSMikhail Zhilkin }
1379b78ef0cSMikhail Zhilkin
scpart_parse(struct mtd_info * master,const struct mtd_partition ** pparts,struct mtd_part_parser_data * data)1389b78ef0cSMikhail Zhilkin static int scpart_parse(struct mtd_info *master,
1399b78ef0cSMikhail Zhilkin const struct mtd_partition **pparts,
1409b78ef0cSMikhail Zhilkin struct mtd_part_parser_data *data)
1419b78ef0cSMikhail Zhilkin {
1429b78ef0cSMikhail Zhilkin const char *partname;
1439b78ef0cSMikhail Zhilkin int n;
1449b78ef0cSMikhail Zhilkin int nr_scparts;
1459b78ef0cSMikhail Zhilkin int nr_parts = 0;
1469b78ef0cSMikhail Zhilkin int res = 0;
1479b78ef0cSMikhail Zhilkin struct sc_part_desc *scpart_map = NULL;
1489b78ef0cSMikhail Zhilkin struct mtd_partition *parts = NULL;
1499b78ef0cSMikhail Zhilkin struct device_node *mtd_node;
1509b78ef0cSMikhail Zhilkin struct device_node *ofpart_node;
1519b78ef0cSMikhail Zhilkin struct device_node *pp;
1529b78ef0cSMikhail Zhilkin
1539b78ef0cSMikhail Zhilkin mtd_node = mtd_get_of_node(master);
1549b78ef0cSMikhail Zhilkin if (!mtd_node) {
1559b78ef0cSMikhail Zhilkin res = -ENOENT;
1569b78ef0cSMikhail Zhilkin goto out;
1579b78ef0cSMikhail Zhilkin }
1589b78ef0cSMikhail Zhilkin
1599b78ef0cSMikhail Zhilkin ofpart_node = of_get_child_by_name(mtd_node, "partitions");
1609b78ef0cSMikhail Zhilkin if (!ofpart_node) {
1619b78ef0cSMikhail Zhilkin pr_info("%s: 'partitions' subnode not found on %pOF.\n",
1629b78ef0cSMikhail Zhilkin master->name, mtd_node);
1639b78ef0cSMikhail Zhilkin res = -ENOENT;
1649b78ef0cSMikhail Zhilkin goto out;
1659b78ef0cSMikhail Zhilkin }
1669b78ef0cSMikhail Zhilkin
1679b78ef0cSMikhail Zhilkin nr_scparts = scpart_find_partmap(master, &scpart_map);
1689b78ef0cSMikhail Zhilkin if (nr_scparts <= 0) {
1699b78ef0cSMikhail Zhilkin pr_info("No any partitions was found in 'SC PART MAP'.\n");
1709b78ef0cSMikhail Zhilkin res = -ENOENT;
1719b78ef0cSMikhail Zhilkin goto free;
1729b78ef0cSMikhail Zhilkin }
1739b78ef0cSMikhail Zhilkin
1749b78ef0cSMikhail Zhilkin parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),
1759b78ef0cSMikhail Zhilkin GFP_KERNEL);
1769b78ef0cSMikhail Zhilkin if (!parts) {
1779b78ef0cSMikhail Zhilkin res = -ENOMEM;
1789b78ef0cSMikhail Zhilkin goto free;
1799b78ef0cSMikhail Zhilkin }
1809b78ef0cSMikhail Zhilkin
1819b78ef0cSMikhail Zhilkin for_each_child_of_node(ofpart_node, pp) {
1829b78ef0cSMikhail Zhilkin u32 scpart_id;
1839b78ef0cSMikhail Zhilkin
1849b78ef0cSMikhail Zhilkin if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))
1859b78ef0cSMikhail Zhilkin continue;
1869b78ef0cSMikhail Zhilkin
1879b78ef0cSMikhail Zhilkin for (n = 0 ; n < nr_scparts ; n++)
1889b78ef0cSMikhail Zhilkin if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
1899b78ef0cSMikhail Zhilkin (scpart_id == scpart_map[n].part_id))
1909b78ef0cSMikhail Zhilkin break;
1919b78ef0cSMikhail Zhilkin if (n >= nr_scparts)
1929b78ef0cSMikhail Zhilkin /* not match */
1939b78ef0cSMikhail Zhilkin continue;
1949b78ef0cSMikhail Zhilkin
1959b78ef0cSMikhail Zhilkin /* add the partition found in OF into MTD partition array */
1969b78ef0cSMikhail Zhilkin parts[nr_parts].offset = scpart_map[n].part_offs;
1979b78ef0cSMikhail Zhilkin parts[nr_parts].size = scpart_map[n].part_bytes;
1989b78ef0cSMikhail Zhilkin parts[nr_parts].of_node = pp;
1999b78ef0cSMikhail Zhilkin
2009b78ef0cSMikhail Zhilkin if (!of_property_read_string(pp, "label", &partname))
2019b78ef0cSMikhail Zhilkin parts[nr_parts].name = partname;
2029b78ef0cSMikhail Zhilkin if (of_property_read_bool(pp, "read-only"))
2039b78ef0cSMikhail Zhilkin parts[nr_parts].mask_flags |= MTD_WRITEABLE;
2049b78ef0cSMikhail Zhilkin if (of_property_read_bool(pp, "lock"))
2059b78ef0cSMikhail Zhilkin parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
2069b78ef0cSMikhail Zhilkin
2079b78ef0cSMikhail Zhilkin /* mark as 'done' */
2089b78ef0cSMikhail Zhilkin scpart_map[n].part_id = ID_ALREADY_FOUND;
2099b78ef0cSMikhail Zhilkin
2109b78ef0cSMikhail Zhilkin nr_parts++;
2119b78ef0cSMikhail Zhilkin }
2129b78ef0cSMikhail Zhilkin
2139b78ef0cSMikhail Zhilkin if (nr_parts > 0) {
2149b78ef0cSMikhail Zhilkin *pparts = parts;
2159b78ef0cSMikhail Zhilkin res = nr_parts;
2169b78ef0cSMikhail Zhilkin } else
2179b78ef0cSMikhail Zhilkin pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");
2189b78ef0cSMikhail Zhilkin
2199b78ef0cSMikhail Zhilkin of_node_put(pp);
2209b78ef0cSMikhail Zhilkin
2219b78ef0cSMikhail Zhilkin free:
222278811d5SYang Yingliang of_node_put(ofpart_node);
2239b78ef0cSMikhail Zhilkin kfree(scpart_map);
2249b78ef0cSMikhail Zhilkin if (res <= 0)
2259b78ef0cSMikhail Zhilkin kfree(parts);
2269b78ef0cSMikhail Zhilkin
2279b78ef0cSMikhail Zhilkin out:
2289b78ef0cSMikhail Zhilkin return res;
2299b78ef0cSMikhail Zhilkin }
2309b78ef0cSMikhail Zhilkin
2319b78ef0cSMikhail Zhilkin static const struct of_device_id scpart_parser_of_match_table[] = {
2329b78ef0cSMikhail Zhilkin { .compatible = "sercomm,sc-partitions" },
2339b78ef0cSMikhail Zhilkin {},
2349b78ef0cSMikhail Zhilkin };
2359b78ef0cSMikhail Zhilkin MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
2369b78ef0cSMikhail Zhilkin
2379b78ef0cSMikhail Zhilkin static struct mtd_part_parser scpart_parser = {
2389b78ef0cSMikhail Zhilkin .parse_fn = scpart_parse,
2399b78ef0cSMikhail Zhilkin .name = "scpart",
2409b78ef0cSMikhail Zhilkin .of_match_table = scpart_parser_of_match_table,
2419b78ef0cSMikhail Zhilkin };
2429b78ef0cSMikhail Zhilkin module_mtd_part_parser(scpart_parser);
2439b78ef0cSMikhail Zhilkin
2449b78ef0cSMikhail Zhilkin /* mtd parsers will request the module by parser name */
2459b78ef0cSMikhail Zhilkin MODULE_ALIAS("scpart");
2469b78ef0cSMikhail Zhilkin MODULE_LICENSE("GPL");
2479b78ef0cSMikhail Zhilkin MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
2489b78ef0cSMikhail Zhilkin MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
2499b78ef0cSMikhail Zhilkin MODULE_DESCRIPTION("Sercomm partition parser");
250