/branches/gl-inet/target/linux/ipq806x/patches-4.14/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch |
@@ -0,0 +1,282 @@ |
From d8eeb4de90e968ba32d956728c866f20752cf2c3 Mon Sep 17 00:00:00 2001 |
From: Mathieu Olivari <mathieu@codeaurora.org> |
Date: Thu, 9 Mar 2017 08:18:08 +0100 |
Subject: [PATCH 31/69] mtd: add SMEM parser for QCOM platforms |
|
On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is |
used to store partition layout. This new parser can now be used to read |
SMEM and use it to register an MTD layout according to its content. |
|
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> |
Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org> |
--- |
drivers/mtd/Kconfig | 7 ++ |
drivers/mtd/Makefile | 1 + |
drivers/mtd/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 236 insertions(+) |
create mode 100644 drivers/mtd/qcom_smem_part.c |
|
--- a/drivers/mtd/Kconfig |
+++ b/drivers/mtd/Kconfig |
@@ -194,6 +194,13 @@ config MTD_MYLOADER_PARTS |
You will still need the parsing functions to be called by the driver |
for your particular device. It won't happen automatically. |
|
+config MTD_QCOM_SMEM_PARTS |
+ tristate "QCOM SMEM partitioning support" |
+ depends on QCOM_SMEM |
+ help |
+ This provides partitions parser for QCOM devices using SMEM |
+ such as IPQ806x. |
+ |
comment "User Modules And Translation Layers" |
|
# |
--- /dev/null |
+++ b/drivers/mtd/qcom_smem_part.c |
@@ -0,0 +1,235 @@ |
+/* |
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
+ * |
+ * This program is free software; you can redistribute it and/or modify |
+ * it under the terms of the GNU General Public License version 2 and |
+ * only version 2 as published by the Free Software Foundation. |
+ * |
+ * This program is distributed in the hope that it will be useful, |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ * GNU General Public License for more details. |
+ */ |
+ |
+#include <linux/kernel.h> |
+#include <linux/device.h> |
+#include <linux/slab.h> |
+ |
+#include <linux/mtd/mtd.h> |
+#include <linux/mtd/partitions.h> |
+#include <linux/spi/spi.h> |
+#include <linux/module.h> |
+ |
+#include <linux/soc/qcom/smem.h> |
+ |
+/* Processor/host identifier for the application processor */ |
+#define SMEM_HOST_APPS 0 |
+ |
+/* SMEM items index */ |
+#define SMEM_AARM_PARTITION_TABLE 9 |
+#define SMEM_BOOT_FLASH_TYPE 421 |
+#define SMEM_BOOT_FLASH_BLOCK_SIZE 424 |
+ |
+/* SMEM Flash types */ |
+#define SMEM_FLASH_NAND 2 |
+#define SMEM_FLASH_SPI 6 |
+ |
+#define SMEM_PART_NAME_SZ 16 |
+#define SMEM_PARTS_MAX 32 |
+ |
+struct smem_partition { |
+ char name[SMEM_PART_NAME_SZ]; |
+ __le32 start; |
+ __le32 size; |
+ __le32 attr; |
+}; |
+ |
+struct smem_partition_table { |
+ u8 magic[8]; |
+ __le32 version; |
+ __le32 len; |
+ struct smem_partition parts[SMEM_PARTS_MAX]; |
+}; |
+ |
+/* SMEM Magic values in partition table */ |
+static const u8 SMEM_PTABLE_MAGIC[] = { |
+ 0xaa, 0x73, 0xee, 0x55, |
+ 0xdb, 0xbd, 0x5e, 0xe3, |
+}; |
+ |
+static int qcom_smem_get_flash_blksz(u64 **smem_blksz) |
+{ |
+ size_t size; |
+ |
+ *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE, |
+ &size); |
+ |
+ if (IS_ERR(*smem_blksz)) { |
+ pr_err("Unable to read flash blksz from SMEM\n"); |
+ return -ENOENT; |
+ } |
+ |
+ if (size != sizeof(**smem_blksz)) { |
+ pr_err("Invalid flash blksz size in SMEM\n"); |
+ return -EINVAL; |
+ } |
+ |
+ return 0; |
+} |
+ |
+static int qcom_smem_get_flash_type(u64 **smem_flash_type) |
+{ |
+ size_t size; |
+ |
+ *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE, |
+ &size); |
+ |
+ if (IS_ERR(*smem_flash_type)) { |
+ pr_err("Unable to read flash type from SMEM\n"); |
+ return -ENOENT; |
+ } |
+ |
+ if (size != sizeof(**smem_flash_type)) { |
+ pr_err("Invalid flash type size in SMEM\n"); |
+ return -EINVAL; |
+ } |
+ |
+ return 0; |
+} |
+ |
+static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts) |
+{ |
+ size_t size; |
+ |
+ *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE, |
+ &size); |
+ |
+ if (IS_ERR(*pparts)) { |
+ pr_err("Unable to read partition table from SMEM\n"); |
+ return -ENOENT; |
+ } |
+ |
+ return 0; |
+} |
+ |
+static int of_dev_node_match(struct device *dev, void *data) |
+{ |
+ return dev->of_node == data; |
+} |
+ |
+static bool is_spi_device(struct device_node *np) |
+{ |
+ struct device *dev; |
+ |
+ dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match); |
+ if (!dev) |
+ return false; |
+ |
+ put_device(dev); |
+ return true; |
+} |
+ |
+static int parse_qcom_smem_partitions(struct mtd_info *master, |
+ const struct mtd_partition **pparts, |
+ struct mtd_part_parser_data *data) |
+{ |
+ struct smem_partition_table *smem_parts; |
+ u64 *smem_flash_type, *smem_blksz; |
+ struct mtd_partition *mtd_parts; |
+ struct device_node *of_node = master->dev.of_node; |
+ int i, ret; |
+ |
+ /* |
+ * SMEM will only store the partition table of the boot device. |
+ * If this is not the boot device, do not return any partition. |
+ */ |
+ ret = qcom_smem_get_flash_type(&smem_flash_type); |
+ if (ret < 0) |
+ return ret; |
+ |
+ if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master)) |
+ || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node))) |
+ return 0; |
+ |
+ /* |
+ * Just for sanity purpose, make sure the block size in SMEM matches the |
+ * block size of the MTD device |
+ */ |
+ ret = qcom_smem_get_flash_blksz(&smem_blksz); |
+ if (ret < 0) |
+ return ret; |
+ |
+ if (*smem_blksz != master->erasesize) { |
+ pr_err("SMEM block size differs from MTD block size\n"); |
+ return -EINVAL; |
+ } |
+ |
+ /* Get partition pointer from SMEM */ |
+ ret = qcom_smem_get_flash_partitions(&smem_parts); |
+ if (ret < 0) |
+ return ret; |
+ |
+ if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic, |
+ sizeof(SMEM_PTABLE_MAGIC))) { |
+ pr_err("SMEM partition magic invalid\n"); |
+ return -EINVAL; |
+ } |
+ |
+ /* Allocate and populate the mtd structures */ |
+ mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts), |
+ GFP_KERNEL); |
+ if (!mtd_parts) |
+ return -ENOMEM; |
+ |
+ for (i = 0; i < smem_parts->len; i++) { |
+ struct smem_partition *s_part = &smem_parts->parts[i]; |
+ struct mtd_partition *m_part = &mtd_parts[i]; |
+ |
+ m_part->name = s_part->name; |
+ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); |
+ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); |
+ |
+ /* |
+ * The last SMEM partition may have its size marked as |
+ * something like 0xffffffff, which means "until the end of the |
+ * flash device". In this case, truncate it. |
+ */ |
+ if (m_part->offset + m_part->size > master->size) |
+ m_part->size = master->size - m_part->offset; |
+ } |
+ |
+ *pparts = mtd_parts; |
+ |
+ return smem_parts->len; |
+} |
+ |
+static const struct of_device_id qcom_smem_of_match_table[] = { |
+ { .compatible = "qcom,smem" }, |
+ {}, |
+}; |
+MODULE_DEVICE_TABLE(of, qcom_smem_of_match_table); |
+ |
+static struct mtd_part_parser qcom_smem_parser = { |
+ .owner = THIS_MODULE, |
+ .parse_fn = parse_qcom_smem_partitions, |
+ .name = "qcom-smem", |
+ .of_match_table = qcom_smem_of_match_table, |
+}; |
+ |
+static int __init qcom_smem_parser_init(void) |
+{ |
+ register_mtd_parser(&qcom_smem_parser); |
+ return 0; |
+} |
+ |
+static void __exit qcom_smem_parser_exit(void) |
+{ |
+ deregister_mtd_parser(&qcom_smem_parser); |
+} |
+ |
+module_init(qcom_smem_parser_init); |
+module_exit(qcom_smem_parser_exit); |
+ |
+MODULE_LICENSE("GPL"); |
+MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>"); |
+MODULE_DESCRIPTION("Parsing code for SMEM based partition tables"); |
--- a/drivers/mtd/Makefile |
+++ b/drivers/mtd/Makefile |
@@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o |
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o |
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o |
obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o |
+obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o |
obj-y += parsers/ |
|
# 'Users' - code which presents functionality to userspace. |