401-v6.0-mtd-parsers-add-support-for-Sercomm-partitions.patch 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. From 9b78ef0c7997052e9eaa0f7a4513d546fa17358c Mon Sep 17 00:00:00 2001
  2. From: Mikhail Zhilkin <[email protected]>
  3. Date: Sun, 29 May 2022 11:07:14 +0000
  4. Subject: [PATCH] mtd: parsers: add support for Sercomm partitions
  5. This adds an MTD partition parser for the Sercomm partition table that
  6. is used in some Beeline, Netgear and Sercomm routers.
  7. The Sercomm partition map table contains real partition offsets, which
  8. may differ from device to device depending on the number and location of
  9. bad blocks on NAND.
  10. Original patch (proposed by NOGUCHI Hiroshi):
  11. Link: https://github.com/openwrt/openwrt/pull/1318#issuecomment-420607394
  12. Signed-off-by: NOGUCHI Hiroshi <[email protected]>
  13. Signed-off-by: Mikhail Zhilkin <[email protected]>
  14. Signed-off-by: Miquel Raynal <[email protected]>
  15. Link: https://lore.kernel.org/linux-mtd/[email protected]
  16. ---
  17. drivers/mtd/parsers/Kconfig | 9 ++
  18. drivers/mtd/parsers/Makefile | 1 +
  19. drivers/mtd/parsers/scpart.c | 248 +++++++++++++++++++++++++++++++++++
  20. 3 files changed, 258 insertions(+)
  21. create mode 100644 drivers/mtd/parsers/scpart.c
  22. --- a/drivers/mtd/parsers/Kconfig
  23. +++ b/drivers/mtd/parsers/Kconfig
  24. @@ -186,3 +186,12 @@ config MTD_QCOMSMEM_PARTS
  25. help
  26. This provides support for parsing partitions from Shared Memory (SMEM)
  27. for NAND and SPI flash on Qualcomm platforms.
  28. +
  29. +config MTD_SERCOMM_PARTS
  30. + tristate "Sercomm partition table parser"
  31. + depends on MTD && RALINK
  32. + help
  33. + This provides partitions table parser for devices with Sercomm
  34. + partition map. This partition table contains real partition
  35. + offsets, which may differ from device to device depending on the
  36. + number and location of bad blocks on NAND.
  37. --- a/drivers/mtd/parsers/Makefile
  38. +++ b/drivers/mtd/parsers/Makefile
  39. @@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)
  40. obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
  41. obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
  42. obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
  43. +obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
  44. obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
  45. obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
  46. obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o
  47. --- /dev/null
  48. +++ b/drivers/mtd/parsers/scpart.c
  49. @@ -0,0 +1,248 @@
  50. +// SPDX-License-Identifier: GPL-2.0-or-later
  51. +/*
  52. + * drivers/mtd/scpart.c: Sercomm Partition Parser
  53. + *
  54. + * Copyright (C) 2018 NOGUCHI Hiroshi
  55. + * Copyright (C) 2022 Mikhail Zhilkin
  56. + */
  57. +
  58. +#include <linux/kernel.h>
  59. +#include <linux/slab.h>
  60. +#include <linux/mtd/mtd.h>
  61. +#include <linux/mtd/partitions.h>
  62. +#include <linux/module.h>
  63. +
  64. +#define MOD_NAME "scpart"
  65. +
  66. +#ifdef pr_fmt
  67. +#undef pr_fmt
  68. +#endif
  69. +
  70. +#define pr_fmt(fmt) MOD_NAME ": " fmt
  71. +
  72. +#define ID_ALREADY_FOUND 0xffffffffUL
  73. +
  74. +#define MAP_OFFS_IN_BLK 0x800
  75. +#define MAP_MIRROR_NUM 2
  76. +
  77. +static const char sc_part_magic[] = {
  78. + 'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
  79. +};
  80. +#define PART_MAGIC_LEN sizeof(sc_part_magic)
  81. +
  82. +/* assumes that all fields are set by CPU native endian */
  83. +struct sc_part_desc {
  84. + uint32_t part_id;
  85. + uint32_t part_offs;
  86. + uint32_t part_bytes;
  87. +};
  88. +
  89. +static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)
  90. +{
  91. + return ((pdesc->part_id != 0xffffffffUL) &&
  92. + (pdesc->part_offs != 0xffffffffUL) &&
  93. + (pdesc->part_bytes != 0xffffffffUL));
  94. +}
  95. +
  96. +static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
  97. + struct sc_part_desc **ppdesc)
  98. +{
  99. + int cnt = 0;
  100. + int res = 0;
  101. + int res2;
  102. + loff_t offs;
  103. + size_t retlen;
  104. + struct sc_part_desc *pdesc = NULL;
  105. + struct sc_part_desc *tmpdesc;
  106. + uint8_t *buf;
  107. +
  108. + buf = kzalloc(master->erasesize, GFP_KERNEL);
  109. + if (!buf) {
  110. + res = -ENOMEM;
  111. + goto out;
  112. + }
  113. +
  114. + res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
  115. + if (res2 || retlen != master->erasesize) {
  116. + res = -EIO;
  117. + goto free;
  118. + }
  119. +
  120. + for (offs = MAP_OFFS_IN_BLK;
  121. + offs < master->erasesize - sizeof(*tmpdesc);
  122. + offs += sizeof(*tmpdesc)) {
  123. + tmpdesc = (struct sc_part_desc *)&buf[offs];
  124. + if (!scpart_desc_is_valid(tmpdesc))
  125. + break;
  126. + cnt++;
  127. + }
  128. +
  129. + if (cnt > 0) {
  130. + int bytes = cnt * sizeof(*pdesc);
  131. +
  132. + pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);
  133. + if (!pdesc) {
  134. + res = -ENOMEM;
  135. + goto free;
  136. + }
  137. + memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
  138. +
  139. + *ppdesc = pdesc;
  140. + res = cnt;
  141. + }
  142. +
  143. +free:
  144. + kfree(buf);
  145. +
  146. +out:
  147. + return res;
  148. +}
  149. +
  150. +static int scpart_find_partmap(struct mtd_info *master,
  151. + struct sc_part_desc **ppdesc)
  152. +{
  153. + int magic_found = 0;
  154. + int res = 0;
  155. + int res2;
  156. + loff_t offs = 0;
  157. + size_t retlen;
  158. + uint8_t rdbuf[PART_MAGIC_LEN];
  159. +
  160. + while ((magic_found < MAP_MIRROR_NUM) &&
  161. + (offs < master->size) &&
  162. + !mtd_block_isbad(master, offs)) {
  163. + res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
  164. + if (res2 || retlen != PART_MAGIC_LEN) {
  165. + res = -EIO;
  166. + goto out;
  167. + }
  168. + if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
  169. + pr_debug("Signature found at 0x%llx\n", offs);
  170. + magic_found++;
  171. + res = scpart_scan_partmap(master, offs, ppdesc);
  172. + if (res > 0)
  173. + goto out;
  174. + }
  175. + offs += master->erasesize;
  176. + }
  177. +
  178. +out:
  179. + if (res > 0)
  180. + pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);
  181. + else
  182. + pr_info("No valid 'SC PART MAP' was found\n");
  183. +
  184. + return res;
  185. +}
  186. +
  187. +static int scpart_parse(struct mtd_info *master,
  188. + const struct mtd_partition **pparts,
  189. + struct mtd_part_parser_data *data)
  190. +{
  191. + const char *partname;
  192. + int n;
  193. + int nr_scparts;
  194. + int nr_parts = 0;
  195. + int res = 0;
  196. + struct sc_part_desc *scpart_map = NULL;
  197. + struct mtd_partition *parts = NULL;
  198. + struct device_node *mtd_node;
  199. + struct device_node *ofpart_node;
  200. + struct device_node *pp;
  201. +
  202. + mtd_node = mtd_get_of_node(master);
  203. + if (!mtd_node) {
  204. + res = -ENOENT;
  205. + goto out;
  206. + }
  207. +
  208. + ofpart_node = of_get_child_by_name(mtd_node, "partitions");
  209. + if (!ofpart_node) {
  210. + pr_info("%s: 'partitions' subnode not found on %pOF.\n",
  211. + master->name, mtd_node);
  212. + res = -ENOENT;
  213. + goto out;
  214. + }
  215. +
  216. + nr_scparts = scpart_find_partmap(master, &scpart_map);
  217. + if (nr_scparts <= 0) {
  218. + pr_info("No any partitions was found in 'SC PART MAP'.\n");
  219. + res = -ENOENT;
  220. + goto free;
  221. + }
  222. +
  223. + parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),
  224. + GFP_KERNEL);
  225. + if (!parts) {
  226. + res = -ENOMEM;
  227. + goto free;
  228. + }
  229. +
  230. + for_each_child_of_node(ofpart_node, pp) {
  231. + u32 scpart_id;
  232. +
  233. + if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))
  234. + continue;
  235. +
  236. + for (n = 0 ; n < nr_scparts ; n++)
  237. + if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
  238. + (scpart_id == scpart_map[n].part_id))
  239. + break;
  240. + if (n >= nr_scparts)
  241. + /* not match */
  242. + continue;
  243. +
  244. + /* add the partition found in OF into MTD partition array */
  245. + parts[nr_parts].offset = scpart_map[n].part_offs;
  246. + parts[nr_parts].size = scpart_map[n].part_bytes;
  247. + parts[nr_parts].of_node = pp;
  248. +
  249. + if (!of_property_read_string(pp, "label", &partname))
  250. + parts[nr_parts].name = partname;
  251. + if (of_property_read_bool(pp, "read-only"))
  252. + parts[nr_parts].mask_flags |= MTD_WRITEABLE;
  253. + if (of_property_read_bool(pp, "lock"))
  254. + parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
  255. +
  256. + /* mark as 'done' */
  257. + scpart_map[n].part_id = ID_ALREADY_FOUND;
  258. +
  259. + nr_parts++;
  260. + }
  261. +
  262. + if (nr_parts > 0) {
  263. + *pparts = parts;
  264. + res = nr_parts;
  265. + } else
  266. + pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");
  267. +
  268. + of_node_put(pp);
  269. +
  270. +free:
  271. + kfree(scpart_map);
  272. + if (res <= 0)
  273. + kfree(parts);
  274. +
  275. +out:
  276. + return res;
  277. +}
  278. +
  279. +static const struct of_device_id scpart_parser_of_match_table[] = {
  280. + { .compatible = "sercomm,sc-partitions" },
  281. + {},
  282. +};
  283. +MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
  284. +
  285. +static struct mtd_part_parser scpart_parser = {
  286. + .parse_fn = scpart_parse,
  287. + .name = "scpart",
  288. + .of_match_table = scpart_parser_of_match_table,
  289. +};
  290. +module_mtd_part_parser(scpart_parser);
  291. +
  292. +/* mtd parsers will request the module by parser name */
  293. +MODULE_ALIAS("scpart");
  294. +MODULE_LICENSE("GPL");
  295. +MODULE_AUTHOR("NOGUCHI Hiroshi <[email protected]>");
  296. +MODULE_AUTHOR("Mikhail Zhilkin <[email protected]>");
  297. +MODULE_DESCRIPTION("Sercomm partition parser");