1*5a4a335aSLinus Walleij // SPDX-License-Identifier: GPL-2.0-or-later
2*5a4a335aSLinus Walleij /*
3*5a4a335aSLinus Walleij * Copyright © 2007 Eugene Konev <ejka@openwrt.org>
4*5a4a335aSLinus Walleij *
5*5a4a335aSLinus Walleij * TI AR7 flash partition table.
6*5a4a335aSLinus Walleij * Based on ar7 map by Felix Fietkau <nbd@openwrt.org>
7*5a4a335aSLinus Walleij */
8*5a4a335aSLinus Walleij
9*5a4a335aSLinus Walleij #include <linux/kernel.h>
10*5a4a335aSLinus Walleij #include <linux/slab.h>
11*5a4a335aSLinus Walleij
12*5a4a335aSLinus Walleij #include <linux/mtd/mtd.h>
13*5a4a335aSLinus Walleij #include <linux/mtd/partitions.h>
14*5a4a335aSLinus Walleij #include <linux/memblock.h>
15*5a4a335aSLinus Walleij #include <linux/module.h>
16*5a4a335aSLinus Walleij
17*5a4a335aSLinus Walleij #include <uapi/linux/magic.h>
18*5a4a335aSLinus Walleij
19*5a4a335aSLinus Walleij #define AR7_PARTS 4
20*5a4a335aSLinus Walleij #define ROOT_OFFSET 0xe0000
21*5a4a335aSLinus Walleij
22*5a4a335aSLinus Walleij #define LOADER_MAGIC1 le32_to_cpu(0xfeedfa42)
23*5a4a335aSLinus Walleij #define LOADER_MAGIC2 le32_to_cpu(0xfeed1281)
24*5a4a335aSLinus Walleij
25*5a4a335aSLinus Walleij struct ar7_bin_rec {
26*5a4a335aSLinus Walleij unsigned int checksum;
27*5a4a335aSLinus Walleij unsigned int length;
28*5a4a335aSLinus Walleij unsigned int address;
29*5a4a335aSLinus Walleij };
30*5a4a335aSLinus Walleij
create_mtd_partitions(struct mtd_info * master,const struct mtd_partition ** pparts,struct mtd_part_parser_data * data)31*5a4a335aSLinus Walleij static int create_mtd_partitions(struct mtd_info *master,
32*5a4a335aSLinus Walleij const struct mtd_partition **pparts,
33*5a4a335aSLinus Walleij struct mtd_part_parser_data *data)
34*5a4a335aSLinus Walleij {
35*5a4a335aSLinus Walleij struct ar7_bin_rec header;
36*5a4a335aSLinus Walleij unsigned int offset;
37*5a4a335aSLinus Walleij size_t len;
38*5a4a335aSLinus Walleij unsigned int pre_size = master->erasesize, post_size = 0;
39*5a4a335aSLinus Walleij unsigned int root_offset = ROOT_OFFSET;
40*5a4a335aSLinus Walleij
41*5a4a335aSLinus Walleij int retries = 10;
42*5a4a335aSLinus Walleij struct mtd_partition *ar7_parts;
43*5a4a335aSLinus Walleij
44*5a4a335aSLinus Walleij ar7_parts = kcalloc(AR7_PARTS, sizeof(*ar7_parts), GFP_KERNEL);
45*5a4a335aSLinus Walleij if (!ar7_parts)
46*5a4a335aSLinus Walleij return -ENOMEM;
47*5a4a335aSLinus Walleij ar7_parts[0].name = "loader";
48*5a4a335aSLinus Walleij ar7_parts[0].offset = 0;
49*5a4a335aSLinus Walleij ar7_parts[0].size = master->erasesize;
50*5a4a335aSLinus Walleij ar7_parts[0].mask_flags = MTD_WRITEABLE;
51*5a4a335aSLinus Walleij
52*5a4a335aSLinus Walleij ar7_parts[1].name = "config";
53*5a4a335aSLinus Walleij ar7_parts[1].offset = 0;
54*5a4a335aSLinus Walleij ar7_parts[1].size = master->erasesize;
55*5a4a335aSLinus Walleij ar7_parts[1].mask_flags = 0;
56*5a4a335aSLinus Walleij
57*5a4a335aSLinus Walleij do { /* Try 10 blocks starting from master->erasesize */
58*5a4a335aSLinus Walleij offset = pre_size;
59*5a4a335aSLinus Walleij mtd_read(master, offset, sizeof(header), &len,
60*5a4a335aSLinus Walleij (uint8_t *)&header);
61*5a4a335aSLinus Walleij if (!strncmp((char *)&header, "TIENV0.8", 8))
62*5a4a335aSLinus Walleij ar7_parts[1].offset = pre_size;
63*5a4a335aSLinus Walleij if (header.checksum == LOADER_MAGIC1)
64*5a4a335aSLinus Walleij break;
65*5a4a335aSLinus Walleij if (header.checksum == LOADER_MAGIC2)
66*5a4a335aSLinus Walleij break;
67*5a4a335aSLinus Walleij pre_size += master->erasesize;
68*5a4a335aSLinus Walleij } while (retries--);
69*5a4a335aSLinus Walleij
70*5a4a335aSLinus Walleij pre_size = offset;
71*5a4a335aSLinus Walleij
72*5a4a335aSLinus Walleij if (!ar7_parts[1].offset) {
73*5a4a335aSLinus Walleij ar7_parts[1].offset = master->size - master->erasesize;
74*5a4a335aSLinus Walleij post_size = master->erasesize;
75*5a4a335aSLinus Walleij }
76*5a4a335aSLinus Walleij
77*5a4a335aSLinus Walleij switch (header.checksum) {
78*5a4a335aSLinus Walleij case LOADER_MAGIC1:
79*5a4a335aSLinus Walleij while (header.length) {
80*5a4a335aSLinus Walleij offset += sizeof(header) + header.length;
81*5a4a335aSLinus Walleij mtd_read(master, offset, sizeof(header), &len,
82*5a4a335aSLinus Walleij (uint8_t *)&header);
83*5a4a335aSLinus Walleij }
84*5a4a335aSLinus Walleij root_offset = offset + sizeof(header) + 4;
85*5a4a335aSLinus Walleij break;
86*5a4a335aSLinus Walleij case LOADER_MAGIC2:
87*5a4a335aSLinus Walleij while (header.length) {
88*5a4a335aSLinus Walleij offset += sizeof(header) + header.length;
89*5a4a335aSLinus Walleij mtd_read(master, offset, sizeof(header), &len,
90*5a4a335aSLinus Walleij (uint8_t *)&header);
91*5a4a335aSLinus Walleij }
92*5a4a335aSLinus Walleij root_offset = offset + sizeof(header) + 4 + 0xff;
93*5a4a335aSLinus Walleij root_offset &= ~(uint32_t)0xff;
94*5a4a335aSLinus Walleij break;
95*5a4a335aSLinus Walleij default:
96*5a4a335aSLinus Walleij printk(KERN_WARNING "Unknown magic: %08x\n", header.checksum);
97*5a4a335aSLinus Walleij break;
98*5a4a335aSLinus Walleij }
99*5a4a335aSLinus Walleij
100*5a4a335aSLinus Walleij mtd_read(master, root_offset, sizeof(header), &len, (u8 *)&header);
101*5a4a335aSLinus Walleij if (header.checksum != SQUASHFS_MAGIC) {
102*5a4a335aSLinus Walleij root_offset += master->erasesize - 1;
103*5a4a335aSLinus Walleij root_offset &= ~(master->erasesize - 1);
104*5a4a335aSLinus Walleij }
105*5a4a335aSLinus Walleij
106*5a4a335aSLinus Walleij ar7_parts[2].name = "linux";
107*5a4a335aSLinus Walleij ar7_parts[2].offset = pre_size;
108*5a4a335aSLinus Walleij ar7_parts[2].size = master->size - pre_size - post_size;
109*5a4a335aSLinus Walleij ar7_parts[2].mask_flags = 0;
110*5a4a335aSLinus Walleij
111*5a4a335aSLinus Walleij ar7_parts[3].name = "rootfs";
112*5a4a335aSLinus Walleij ar7_parts[3].offset = root_offset;
113*5a4a335aSLinus Walleij ar7_parts[3].size = master->size - root_offset - post_size;
114*5a4a335aSLinus Walleij ar7_parts[3].mask_flags = 0;
115*5a4a335aSLinus Walleij
116*5a4a335aSLinus Walleij *pparts = ar7_parts;
117*5a4a335aSLinus Walleij return AR7_PARTS;
118*5a4a335aSLinus Walleij }
119*5a4a335aSLinus Walleij
120*5a4a335aSLinus Walleij static struct mtd_part_parser ar7_parser = {
121*5a4a335aSLinus Walleij .parse_fn = create_mtd_partitions,
122*5a4a335aSLinus Walleij .name = "ar7part",
123*5a4a335aSLinus Walleij };
124*5a4a335aSLinus Walleij module_mtd_part_parser(ar7_parser);
125*5a4a335aSLinus Walleij
126*5a4a335aSLinus Walleij MODULE_LICENSE("GPL");
127*5a4a335aSLinus Walleij MODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, "
128*5a4a335aSLinus Walleij "Eugene Konev <ejka@openwrt.org>");
129*5a4a335aSLinus Walleij MODULE_DESCRIPTION("MTD partitioning for TI AR7");
130