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