162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2022 Rafał Miłecki <rafal@milecki.pl> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1062306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define BRCM_U_BOOT_MAX_OFFSET 0x200000 1362306a36Sopenharmony_ci#define BRCM_U_BOOT_STEP 0x1000 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define BRCM_U_BOOT_MAX_PARTS 2 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define BRCM_U_BOOT_MAGIC 0x75456e76 /* uEnv */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct brcm_u_boot_header { 2062306a36Sopenharmony_ci __le32 magic; 2162306a36Sopenharmony_ci __le32 length; 2262306a36Sopenharmony_ci} __packed; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic const char *names[BRCM_U_BOOT_MAX_PARTS] = { 2562306a36Sopenharmony_ci "u-boot-env", 2662306a36Sopenharmony_ci "u-boot-env-backup", 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int brcm_u_boot_parse(struct mtd_info *mtd, 3062306a36Sopenharmony_ci const struct mtd_partition **pparts, 3162306a36Sopenharmony_ci struct mtd_part_parser_data *data) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct brcm_u_boot_header header; 3462306a36Sopenharmony_ci struct mtd_partition *parts; 3562306a36Sopenharmony_ci size_t bytes_read; 3662306a36Sopenharmony_ci size_t offset; 3762306a36Sopenharmony_ci int err; 3862306a36Sopenharmony_ci int i = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci parts = kcalloc(BRCM_U_BOOT_MAX_PARTS, sizeof(*parts), GFP_KERNEL); 4162306a36Sopenharmony_ci if (!parts) 4262306a36Sopenharmony_ci return -ENOMEM; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci for (offset = 0; 4562306a36Sopenharmony_ci offset < min_t(size_t, mtd->size, BRCM_U_BOOT_MAX_OFFSET); 4662306a36Sopenharmony_ci offset += BRCM_U_BOOT_STEP) { 4762306a36Sopenharmony_ci err = mtd_read(mtd, offset, sizeof(header), &bytes_read, (uint8_t *)&header); 4862306a36Sopenharmony_ci if (err && !mtd_is_bitflip(err)) { 4962306a36Sopenharmony_ci pr_err("Failed to read from %s at 0x%zx: %d\n", mtd->name, offset, err); 5062306a36Sopenharmony_ci continue; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (le32_to_cpu(header.magic) != BRCM_U_BOOT_MAGIC) 5462306a36Sopenharmony_ci continue; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci parts[i].name = names[i]; 5762306a36Sopenharmony_ci parts[i].offset = offset; 5862306a36Sopenharmony_ci parts[i].size = sizeof(header) + le32_to_cpu(header.length); 5962306a36Sopenharmony_ci i++; 6062306a36Sopenharmony_ci pr_info("offset:0x%zx magic:0x%08x BINGO\n", offset, header.magic); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (i == BRCM_U_BOOT_MAX_PARTS) 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci *pparts = parts; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return i; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic const struct of_device_id brcm_u_boot_of_match_table[] = { 7262306a36Sopenharmony_ci { .compatible = "brcm,u-boot" }, 7362306a36Sopenharmony_ci {}, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, brcm_u_boot_of_match_table); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct mtd_part_parser brcm_u_boot_mtd_parser = { 7862306a36Sopenharmony_ci .parse_fn = brcm_u_boot_parse, 7962306a36Sopenharmony_ci .name = "brcm_u-boot", 8062306a36Sopenharmony_ci .of_match_table = brcm_u_boot_of_match_table, 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_cimodule_mtd_part_parser(brcm_u_boot_mtd_parser); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 85