0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/kernel.h>
0010 #include <linux/slab.h>
0011 #include <linux/mtd/mtd.h>
0012 #include <linux/mtd/partitions.h>
0013 #include <linux/module.h>
0014
0015 #define MOD_NAME "scpart"
0016
0017 #ifdef pr_fmt
0018 #undef pr_fmt
0019 #endif
0020
0021 #define pr_fmt(fmt) MOD_NAME ": " fmt
0022
0023 #define ID_ALREADY_FOUND 0xffffffffUL
0024
0025 #define MAP_OFFS_IN_BLK 0x800
0026 #define MAP_MIRROR_NUM 2
0027
0028 static const char sc_part_magic[] = {
0029 'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
0030 };
0031 #define PART_MAGIC_LEN sizeof(sc_part_magic)
0032
0033
0034 struct sc_part_desc {
0035 uint32_t part_id;
0036 uint32_t part_offs;
0037 uint32_t part_bytes;
0038 };
0039
0040 static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)
0041 {
0042 return ((pdesc->part_id != 0xffffffffUL) &&
0043 (pdesc->part_offs != 0xffffffffUL) &&
0044 (pdesc->part_bytes != 0xffffffffUL));
0045 }
0046
0047 static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
0048 struct sc_part_desc **ppdesc)
0049 {
0050 int cnt = 0;
0051 int res = 0;
0052 int res2;
0053 loff_t offs;
0054 size_t retlen;
0055 struct sc_part_desc *pdesc = NULL;
0056 struct sc_part_desc *tmpdesc;
0057 uint8_t *buf;
0058
0059 buf = kzalloc(master->erasesize, GFP_KERNEL);
0060 if (!buf) {
0061 res = -ENOMEM;
0062 goto out;
0063 }
0064
0065 res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
0066 if (res2 || retlen != master->erasesize) {
0067 res = -EIO;
0068 goto free;
0069 }
0070
0071 for (offs = MAP_OFFS_IN_BLK;
0072 offs < master->erasesize - sizeof(*tmpdesc);
0073 offs += sizeof(*tmpdesc)) {
0074 tmpdesc = (struct sc_part_desc *)&buf[offs];
0075 if (!scpart_desc_is_valid(tmpdesc))
0076 break;
0077 cnt++;
0078 }
0079
0080 if (cnt > 0) {
0081 int bytes = cnt * sizeof(*pdesc);
0082
0083 pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);
0084 if (!pdesc) {
0085 res = -ENOMEM;
0086 goto free;
0087 }
0088 memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
0089
0090 *ppdesc = pdesc;
0091 res = cnt;
0092 }
0093
0094 free:
0095 kfree(buf);
0096
0097 out:
0098 return res;
0099 }
0100
0101 static int scpart_find_partmap(struct mtd_info *master,
0102 struct sc_part_desc **ppdesc)
0103 {
0104 int magic_found = 0;
0105 int res = 0;
0106 int res2;
0107 loff_t offs = 0;
0108 size_t retlen;
0109 uint8_t rdbuf[PART_MAGIC_LEN];
0110
0111 while ((magic_found < MAP_MIRROR_NUM) &&
0112 (offs < master->size) &&
0113 !mtd_block_isbad(master, offs)) {
0114 res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
0115 if (res2 || retlen != PART_MAGIC_LEN) {
0116 res = -EIO;
0117 goto out;
0118 }
0119 if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
0120 pr_debug("Signature found at 0x%llx\n", offs);
0121 magic_found++;
0122 res = scpart_scan_partmap(master, offs, ppdesc);
0123 if (res > 0)
0124 goto out;
0125 }
0126 offs += master->erasesize;
0127 }
0128
0129 out:
0130 if (res > 0)
0131 pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);
0132 else
0133 pr_info("No valid 'SC PART MAP' was found\n");
0134
0135 return res;
0136 }
0137
0138 static int scpart_parse(struct mtd_info *master,
0139 const struct mtd_partition **pparts,
0140 struct mtd_part_parser_data *data)
0141 {
0142 const char *partname;
0143 int n;
0144 int nr_scparts;
0145 int nr_parts = 0;
0146 int res = 0;
0147 struct sc_part_desc *scpart_map = NULL;
0148 struct mtd_partition *parts = NULL;
0149 struct device_node *mtd_node;
0150 struct device_node *ofpart_node;
0151 struct device_node *pp;
0152
0153 mtd_node = mtd_get_of_node(master);
0154 if (!mtd_node) {
0155 res = -ENOENT;
0156 goto out;
0157 }
0158
0159 ofpart_node = of_get_child_by_name(mtd_node, "partitions");
0160 if (!ofpart_node) {
0161 pr_info("%s: 'partitions' subnode not found on %pOF.\n",
0162 master->name, mtd_node);
0163 res = -ENOENT;
0164 goto out;
0165 }
0166
0167 nr_scparts = scpart_find_partmap(master, &scpart_map);
0168 if (nr_scparts <= 0) {
0169 pr_info("No any partitions was found in 'SC PART MAP'.\n");
0170 res = -ENOENT;
0171 goto free;
0172 }
0173
0174 parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),
0175 GFP_KERNEL);
0176 if (!parts) {
0177 res = -ENOMEM;
0178 goto free;
0179 }
0180
0181 for_each_child_of_node(ofpart_node, pp) {
0182 u32 scpart_id;
0183
0184 if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))
0185 continue;
0186
0187 for (n = 0 ; n < nr_scparts ; n++)
0188 if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
0189 (scpart_id == scpart_map[n].part_id))
0190 break;
0191 if (n >= nr_scparts)
0192
0193 continue;
0194
0195
0196 parts[nr_parts].offset = scpart_map[n].part_offs;
0197 parts[nr_parts].size = scpart_map[n].part_bytes;
0198 parts[nr_parts].of_node = pp;
0199
0200 if (!of_property_read_string(pp, "label", &partname))
0201 parts[nr_parts].name = partname;
0202 if (of_property_read_bool(pp, "read-only"))
0203 parts[nr_parts].mask_flags |= MTD_WRITEABLE;
0204 if (of_property_read_bool(pp, "lock"))
0205 parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
0206
0207
0208 scpart_map[n].part_id = ID_ALREADY_FOUND;
0209
0210 nr_parts++;
0211 }
0212
0213 if (nr_parts > 0) {
0214 *pparts = parts;
0215 res = nr_parts;
0216 } else
0217 pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");
0218
0219 of_node_put(pp);
0220
0221 free:
0222 of_node_put(ofpart_node);
0223 kfree(scpart_map);
0224 if (res <= 0)
0225 kfree(parts);
0226
0227 out:
0228 return res;
0229 }
0230
0231 static const struct of_device_id scpart_parser_of_match_table[] = {
0232 { .compatible = "sercomm,sc-partitions" },
0233 {},
0234 };
0235 MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
0236
0237 static struct mtd_part_parser scpart_parser = {
0238 .parse_fn = scpart_parse,
0239 .name = "scpart",
0240 .of_match_table = scpart_parser_of_match_table,
0241 };
0242 module_mtd_part_parser(scpart_parser);
0243
0244
0245 MODULE_ALIAS("scpart");
0246 MODULE_LICENSE("GPL");
0247 MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
0248 MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
0249 MODULE_DESCRIPTION("Sercomm partition parser");