162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2007 Eugene Konev <ejka@openwrt.org> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * TI AR7 flash partition table. 662306a36Sopenharmony_ci * Based on ar7 map by Felix Fietkau <nbd@openwrt.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1362306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1462306a36Sopenharmony_ci#include <linux/memblock.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <uapi/linux/magic.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define AR7_PARTS 4 2062306a36Sopenharmony_ci#define ROOT_OFFSET 0xe0000 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define LOADER_MAGIC1 le32_to_cpu(0xfeedfa42) 2362306a36Sopenharmony_ci#define LOADER_MAGIC2 le32_to_cpu(0xfeed1281) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct ar7_bin_rec { 2662306a36Sopenharmony_ci unsigned int checksum; 2762306a36Sopenharmony_ci unsigned int length; 2862306a36Sopenharmony_ci unsigned int address; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int create_mtd_partitions(struct mtd_info *master, 3262306a36Sopenharmony_ci const struct mtd_partition **pparts, 3362306a36Sopenharmony_ci struct mtd_part_parser_data *data) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct ar7_bin_rec header; 3662306a36Sopenharmony_ci unsigned int offset; 3762306a36Sopenharmony_ci size_t len; 3862306a36Sopenharmony_ci unsigned int pre_size = master->erasesize, post_size = 0; 3962306a36Sopenharmony_ci unsigned int root_offset = ROOT_OFFSET; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci int retries = 10; 4262306a36Sopenharmony_ci struct mtd_partition *ar7_parts; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci ar7_parts = kcalloc(AR7_PARTS, sizeof(*ar7_parts), GFP_KERNEL); 4562306a36Sopenharmony_ci if (!ar7_parts) 4662306a36Sopenharmony_ci return -ENOMEM; 4762306a36Sopenharmony_ci ar7_parts[0].name = "loader"; 4862306a36Sopenharmony_ci ar7_parts[0].offset = 0; 4962306a36Sopenharmony_ci ar7_parts[0].size = master->erasesize; 5062306a36Sopenharmony_ci ar7_parts[0].mask_flags = MTD_WRITEABLE; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci ar7_parts[1].name = "config"; 5362306a36Sopenharmony_ci ar7_parts[1].offset = 0; 5462306a36Sopenharmony_ci ar7_parts[1].size = master->erasesize; 5562306a36Sopenharmony_ci ar7_parts[1].mask_flags = 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci do { /* Try 10 blocks starting from master->erasesize */ 5862306a36Sopenharmony_ci offset = pre_size; 5962306a36Sopenharmony_ci mtd_read(master, offset, sizeof(header), &len, 6062306a36Sopenharmony_ci (uint8_t *)&header); 6162306a36Sopenharmony_ci if (!strncmp((char *)&header, "TIENV0.8", 8)) 6262306a36Sopenharmony_ci ar7_parts[1].offset = pre_size; 6362306a36Sopenharmony_ci if (header.checksum == LOADER_MAGIC1) 6462306a36Sopenharmony_ci break; 6562306a36Sopenharmony_ci if (header.checksum == LOADER_MAGIC2) 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci pre_size += master->erasesize; 6862306a36Sopenharmony_ci } while (retries--); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci pre_size = offset; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (!ar7_parts[1].offset) { 7362306a36Sopenharmony_ci ar7_parts[1].offset = master->size - master->erasesize; 7462306a36Sopenharmony_ci post_size = master->erasesize; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci switch (header.checksum) { 7862306a36Sopenharmony_ci case LOADER_MAGIC1: 7962306a36Sopenharmony_ci while (header.length) { 8062306a36Sopenharmony_ci offset += sizeof(header) + header.length; 8162306a36Sopenharmony_ci mtd_read(master, offset, sizeof(header), &len, 8262306a36Sopenharmony_ci (uint8_t *)&header); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci root_offset = offset + sizeof(header) + 4; 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci case LOADER_MAGIC2: 8762306a36Sopenharmony_ci while (header.length) { 8862306a36Sopenharmony_ci offset += sizeof(header) + header.length; 8962306a36Sopenharmony_ci mtd_read(master, offset, sizeof(header), &len, 9062306a36Sopenharmony_ci (uint8_t *)&header); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci root_offset = offset + sizeof(header) + 4 + 0xff; 9362306a36Sopenharmony_ci root_offset &= ~(uint32_t)0xff; 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci default: 9662306a36Sopenharmony_ci printk(KERN_WARNING "Unknown magic: %08x\n", header.checksum); 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci mtd_read(master, root_offset, sizeof(header), &len, (u8 *)&header); 10162306a36Sopenharmony_ci if (header.checksum != SQUASHFS_MAGIC) { 10262306a36Sopenharmony_ci root_offset += master->erasesize - 1; 10362306a36Sopenharmony_ci root_offset &= ~(master->erasesize - 1); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ar7_parts[2].name = "linux"; 10762306a36Sopenharmony_ci ar7_parts[2].offset = pre_size; 10862306a36Sopenharmony_ci ar7_parts[2].size = master->size - pre_size - post_size; 10962306a36Sopenharmony_ci ar7_parts[2].mask_flags = 0; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ar7_parts[3].name = "rootfs"; 11262306a36Sopenharmony_ci ar7_parts[3].offset = root_offset; 11362306a36Sopenharmony_ci ar7_parts[3].size = master->size - root_offset - post_size; 11462306a36Sopenharmony_ci ar7_parts[3].mask_flags = 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci *pparts = ar7_parts; 11762306a36Sopenharmony_ci return AR7_PARTS; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic struct mtd_part_parser ar7_parser = { 12162306a36Sopenharmony_ci .parse_fn = create_mtd_partitions, 12262306a36Sopenharmony_ci .name = "ar7part", 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_cimodule_mtd_part_parser(ar7_parser); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 12762306a36Sopenharmony_ciMODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, " 12862306a36Sopenharmony_ci "Eugene Konev <ejka@openwrt.org>"); 12962306a36Sopenharmony_ciMODULE_DESCRIPTION("MTD partitioning for TI AR7"); 130