18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright © 2007 Eugene Konev <ejka@openwrt.org>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * TI AR7 flash partition table.
68c2ecf20Sopenharmony_ci * Based on ar7 map by Felix Fietkau <nbd@openwrt.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
138c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
148c2ecf20Sopenharmony_ci#include <linux/memblock.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <uapi/linux/magic.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define AR7_PARTS	4
208c2ecf20Sopenharmony_ci#define ROOT_OFFSET	0xe0000
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define LOADER_MAGIC1	le32_to_cpu(0xfeedfa42)
238c2ecf20Sopenharmony_ci#define LOADER_MAGIC2	le32_to_cpu(0xfeed1281)
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct ar7_bin_rec {
268c2ecf20Sopenharmony_ci	unsigned int checksum;
278c2ecf20Sopenharmony_ci	unsigned int length;
288c2ecf20Sopenharmony_ci	unsigned int address;
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int create_mtd_partitions(struct mtd_info *master,
328c2ecf20Sopenharmony_ci				 const struct mtd_partition **pparts,
338c2ecf20Sopenharmony_ci				 struct mtd_part_parser_data *data)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct ar7_bin_rec header;
368c2ecf20Sopenharmony_ci	unsigned int offset;
378c2ecf20Sopenharmony_ci	size_t len;
388c2ecf20Sopenharmony_ci	unsigned int pre_size = master->erasesize, post_size = 0;
398c2ecf20Sopenharmony_ci	unsigned int root_offset = ROOT_OFFSET;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	int retries = 10;
428c2ecf20Sopenharmony_ci	struct mtd_partition *ar7_parts;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	ar7_parts = kcalloc(AR7_PARTS, sizeof(*ar7_parts), GFP_KERNEL);
458c2ecf20Sopenharmony_ci	if (!ar7_parts)
468c2ecf20Sopenharmony_ci		return -ENOMEM;
478c2ecf20Sopenharmony_ci	ar7_parts[0].name = "loader";
488c2ecf20Sopenharmony_ci	ar7_parts[0].offset = 0;
498c2ecf20Sopenharmony_ci	ar7_parts[0].size = master->erasesize;
508c2ecf20Sopenharmony_ci	ar7_parts[0].mask_flags = MTD_WRITEABLE;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	ar7_parts[1].name = "config";
538c2ecf20Sopenharmony_ci	ar7_parts[1].offset = 0;
548c2ecf20Sopenharmony_ci	ar7_parts[1].size = master->erasesize;
558c2ecf20Sopenharmony_ci	ar7_parts[1].mask_flags = 0;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	do { /* Try 10 blocks starting from master->erasesize */
588c2ecf20Sopenharmony_ci		offset = pre_size;
598c2ecf20Sopenharmony_ci		mtd_read(master, offset, sizeof(header), &len,
608c2ecf20Sopenharmony_ci			 (uint8_t *)&header);
618c2ecf20Sopenharmony_ci		if (!strncmp((char *)&header, "TIENV0.8", 8))
628c2ecf20Sopenharmony_ci			ar7_parts[1].offset = pre_size;
638c2ecf20Sopenharmony_ci		if (header.checksum == LOADER_MAGIC1)
648c2ecf20Sopenharmony_ci			break;
658c2ecf20Sopenharmony_ci		if (header.checksum == LOADER_MAGIC2)
668c2ecf20Sopenharmony_ci			break;
678c2ecf20Sopenharmony_ci		pre_size += master->erasesize;
688c2ecf20Sopenharmony_ci	} while (retries--);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	pre_size = offset;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (!ar7_parts[1].offset) {
738c2ecf20Sopenharmony_ci		ar7_parts[1].offset = master->size - master->erasesize;
748c2ecf20Sopenharmony_ci		post_size = master->erasesize;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	switch (header.checksum) {
788c2ecf20Sopenharmony_ci	case LOADER_MAGIC1:
798c2ecf20Sopenharmony_ci		while (header.length) {
808c2ecf20Sopenharmony_ci			offset += sizeof(header) + header.length;
818c2ecf20Sopenharmony_ci			mtd_read(master, offset, sizeof(header), &len,
828c2ecf20Sopenharmony_ci				 (uint8_t *)&header);
838c2ecf20Sopenharmony_ci		}
848c2ecf20Sopenharmony_ci		root_offset = offset + sizeof(header) + 4;
858c2ecf20Sopenharmony_ci		break;
868c2ecf20Sopenharmony_ci	case LOADER_MAGIC2:
878c2ecf20Sopenharmony_ci		while (header.length) {
888c2ecf20Sopenharmony_ci			offset += sizeof(header) + header.length;
898c2ecf20Sopenharmony_ci			mtd_read(master, offset, sizeof(header), &len,
908c2ecf20Sopenharmony_ci				 (uint8_t *)&header);
918c2ecf20Sopenharmony_ci		}
928c2ecf20Sopenharmony_ci		root_offset = offset + sizeof(header) + 4 + 0xff;
938c2ecf20Sopenharmony_ci		root_offset &= ~(uint32_t)0xff;
948c2ecf20Sopenharmony_ci		break;
958c2ecf20Sopenharmony_ci	default:
968c2ecf20Sopenharmony_ci		printk(KERN_WARNING "Unknown magic: %08x\n", header.checksum);
978c2ecf20Sopenharmony_ci		break;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	mtd_read(master, root_offset, sizeof(header), &len, (u8 *)&header);
1018c2ecf20Sopenharmony_ci	if (header.checksum != SQUASHFS_MAGIC) {
1028c2ecf20Sopenharmony_ci		root_offset += master->erasesize - 1;
1038c2ecf20Sopenharmony_ci		root_offset &= ~(master->erasesize - 1);
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ar7_parts[2].name = "linux";
1078c2ecf20Sopenharmony_ci	ar7_parts[2].offset = pre_size;
1088c2ecf20Sopenharmony_ci	ar7_parts[2].size = master->size - pre_size - post_size;
1098c2ecf20Sopenharmony_ci	ar7_parts[2].mask_flags = 0;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	ar7_parts[3].name = "rootfs";
1128c2ecf20Sopenharmony_ci	ar7_parts[3].offset = root_offset;
1138c2ecf20Sopenharmony_ci	ar7_parts[3].size = master->size - root_offset - post_size;
1148c2ecf20Sopenharmony_ci	ar7_parts[3].mask_flags = 0;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	*pparts = ar7_parts;
1178c2ecf20Sopenharmony_ci	return AR7_PARTS;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct mtd_part_parser ar7_parser = {
1218c2ecf20Sopenharmony_ci	.parse_fn = create_mtd_partitions,
1228c2ecf20Sopenharmony_ci	.name = "ar7part",
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_cimodule_mtd_part_parser(ar7_parser);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1278c2ecf20Sopenharmony_ciMODULE_AUTHOR(	"Felix Fietkau <nbd@openwrt.org>, "
1288c2ecf20Sopenharmony_ci		"Eugene Konev <ejka@openwrt.org>");
1298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD partitioning for TI AR7");
130