Add functions that handle loading the FIT image nodes having the Qclib
executable and executing it. Also provides 'weak' hooks that can be
overridden by SoC specific implementations.

Signed-off-by: Varadarajan Narayanan <[email protected]>
---
 arch/arm/mach-snapdragon/Makefile             |   1 +
 arch/arm/mach-snapdragon/include/mach/qclib.h |  82 +++++++++++++
 arch/arm/mach-snapdragon/qclib.c              | 160 ++++++++++++++++++++++++++
 3 files changed, 243 insertions(+)

diff --git a/arch/arm/mach-snapdragon/Makefile 
b/arch/arm/mach-snapdragon/Makefile
index 2701f7c6fb1..fc39f7381b3 100644
--- a/arch/arm/mach-snapdragon/Makefile
+++ b/arch/arm/mach-snapdragon/Makefile
@@ -9,6 +9,7 @@ ifeq ($(CONFIG_SPL_BUILD),y)
 obj-y += board_spl.o
 obj-y += fit-handler.o
 obj-y += pbl-shared-data.o
+obj-y += qclib.o
 else
 obj-y += board.o
 obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += capsule_update.o
diff --git a/arch/arm/mach-snapdragon/include/mach/qclib.h 
b/arch/arm/mach-snapdragon/include/mach/qclib.h
new file mode 100644
index 00000000000..b44874c514e
--- /dev/null
+++ b/arch/arm/mach-snapdragon/include/mach/qclib.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __QCLIB_H__
+#define __QCLIB_H__
+
+#include <linux/bitops.h>
+#include <linux/types.h>
+
+#define MAGIC_KEY                      "QCLIB_CB"
+#define MAX_ENTRIES                    0xF
+#define IF_TABLE_VERSION               0x1
+#define QCSDI                          "qcsdi"
+#define QCLIB_LOG_BUFFER               "qclib_log_buffer"
+
+/* interface_table.global_attributes bits */
+#define QCLIB_GA_ENABLE_UART_LOGGING   BIT(0)
+
+/**
+ * struct interface_table_entry - Meta data for blobs in QCLIB interface
+ * @entry_name:        Name of the data blob (e.g., "dcb_settings").
+ * @address:   Address of the data blob.
+ * @size:      Size of the data blob.
+ * @attributes:        Attributes for the blob (e.g., save to storage).
+ */
+struct interface_table_entry {
+       char entry_name[24];
+       u64 address;
+       u32 size;
+       u32 attributes;
+};
+
+/**
+ * struct interface_table - QCLIB Interface table header
+ * @magic_key:         Magic key for validation ("QCLIB_CB").
+ * @version:           Interface table version.
+ * @num_entries:       Number of valid entries.
+ * @max_entries:       Maximum allowable entries.
+ * @global_attributes: Flags for global attributes (e.g., SDI path).
+ * @reserved1:         Reserved for future use.
+ * @reserved2:         Reserved for future use.
+ * @if_table_entries:  Array of interface table entries.
+ */
+struct interface_table {
+       char magic_key[8];
+       u32 version;
+       u32 num_entries;
+       u32 max_entries;
+       u32 global_attributes;
+       u32 reserved1;
+       u32 reserved2;
+       struct interface_table_entry if_table_entries[MAX_ENTRIES];
+};
+
+/* Exported by fit-handler.c */
+int qcom_spl_get_fit_img_entry_point(void *fit, int node, u64 *entry_point);
+int qcom_spl_get_iftbl_entry_by_name(struct interface_table *if_tbl,
+                                    char *name,
+                                    struct interface_table_entry *entry);
+
+/* QCSDI address accessors, populated post-QCLIB, consumed by fit-handler.c */
+u64 qclib_get_qcsdi_address(void);
+void qclib_set_qcsdi_address(u64 address);
+
+/* qclib_log_buffer address accessors, populated post-QCLIB */
+u64 qclib_get_log_buffer_entry(void);
+void qclib_set_log_buffer_entry(u64 address);
+
+/*
+ * __weak SoC extension points. A board's spl-<soc>.c may provide a strong
+ * override for any of these; the defaults in qclib.c are all no-ops.
+ */
+bool qcom_spl_soc_check_dload_mode(void);
+int qcom_spl_soc_pre_qclib_routine(void);
+int qcom_spl_soc_qclib_override(struct interface_table *table, const void *fit,
+                               int images_node);
+int qcom_spl_soc_post_qclib_routine(struct interface_table *if_tbl);
+
+int qclib_populate_interface_table(struct interface_table *if_tbl,
+                                  const void *fit, int images_node);
+int qcom_spl_invoke_qclib(void);
+
+#endif /* __QCLIB_H__ */
diff --git a/arch/arm/mach-snapdragon/qclib.c b/arch/arm/mach-snapdragon/qclib.c
new file mode 100644
index 00000000000..7f25f37b5d9
--- /dev/null
+++ b/arch/arm/mach-snapdragon/qclib.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * QCLIB interface-table protocol: generic, SoC-agnostic orchestration of the
+ * hand-off to the QCLIB firmware image, plus __weak extension points a
+ * per-board spl-<soc>.c may override to populate SoC-specific table entries.
+ */
+
+#include <cpu_func.h>
+#include <errno.h>
+#include <fdt_support.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <mach/qclib.h>
+
+static u64 g_qcsdi_address __section(".data");
+static u64 g_log_buffer_entry __section(".data");
+
+u64 qclib_get_qcsdi_address(void)
+{
+       return g_qcsdi_address;
+}
+
+void qclib_set_qcsdi_address(u64 addr)
+{
+       g_qcsdi_address = addr;
+}
+
+u64 qclib_get_log_buffer_entry(void)
+{
+       return g_log_buffer_entry;
+}
+
+void qclib_set_log_buffer_entry(u64 address)
+{
+       g_log_buffer_entry = address;
+}
+
+bool __weak qcom_spl_soc_check_dload_mode(void)
+{
+       return false;
+}
+
+int __weak qcom_spl_soc_pre_qclib_routine(void)
+{
+       return 0;
+}
+
+int __weak qcom_spl_soc_qclib_override(struct interface_table *table,
+                                      const void *fit, int images_node)
+{
+       return 0;
+}
+
+int __weak qcom_spl_soc_post_qclib_routine(struct interface_table *if_tbl)
+{
+       return 0;
+}
+
+int __weak qclib_populate_interface_table(struct interface_table *if_tbl,
+                                         const void *fit, int images_node)
+{
+       struct interface_table_entry *log_buffer_entry;
+
+       memset(if_tbl, 0, sizeof(struct interface_table));
+       memcpy(if_tbl->magic_key, MAGIC_KEY, strlen(MAGIC_KEY));
+       if_tbl->version = IF_TABLE_VERSION;
+       if_tbl->num_entries = 0;
+       if_tbl->max_entries = MAX_ENTRIES;
+
+       /*
+        * Reserve the "qclib_log_buffer" slot — common to every SoC.
+        * QCLIB fills in the address itself; SPL just allocates the entry.
+        */
+       log_buffer_entry = &if_tbl->if_table_entries[if_tbl->num_entries];
+       memcpy(log_buffer_entry->entry_name, QCLIB_LOG_BUFFER,
+              strlen(QCLIB_LOG_BUFFER));
+       log_buffer_entry->address = 0;
+       log_buffer_entry->size = 0;
+       log_buffer_entry->attributes = 0;
+       if_tbl->num_entries++;
+
+       return qcom_spl_soc_qclib_override(if_tbl, fit, images_node);
+}
+
+static int qclib_find_fit_nodes(const void *fit, int *images_node,
+                               int *qclib_node)
+{
+       *images_node = fdt_subnode_offset(fit, 0, "images");
+       if (*images_node < 0) {
+               pr_err("Failed to find images node in FIT\n");
+               return -ENOENT;
+       }
+
+       *qclib_node = fdt_subnode_offset(fit, *images_node, "qclib_1");
+       if (*qclib_node < 0) {
+               pr_err("Failed to find qclib_1 node in FIT\n");
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+int qcom_spl_invoke_qclib(void)
+{
+       int ret;
+       int images_node;
+       int qclib_node;
+       const void *fit;
+       struct interface_table if_tbl;
+       u64 entry_point;
+
+       /* Get FIT image from SPL load address */
+       fit = (const void *)CONFIG_SPL_LOAD_FIT_ADDRESS;
+
+       pr_debug("QCLIB invoke: fit=%p\n", fit);
+
+       ret = qclib_find_fit_nodes(fit, &images_node, &qclib_node);
+       if (ret)
+               return ret;
+
+       ret = qcom_spl_soc_pre_qclib_routine();
+       if (ret)
+               return ret;
+
+       ret = qclib_populate_interface_table(&if_tbl, fit, images_node);
+       if (ret)
+               return ret;
+
+       ret = qcom_spl_get_fit_img_entry_point((void *)fit, qclib_node, 
&entry_point);
+       if (ret) {
+               pr_err("Failed to get qcom-lib-1 entry point (%d)\n", ret);
+               return ret;
+       }
+
+       pr_info("Jumping to qcom-lib-1 at 0x%llx\n", entry_point);
+
+       /*
+        * QCLIB is a separately-linked firmware image, not an AAPCS64
+        * callee - a plain C function-pointer call risks the compiler
+        * caching a live value in a callee-saved register across the jump.
+        * Pin the args/target to fixed registers and clobber everything the
+        * ABI doesn't guarantee QCLIB will preserve.
+        */
+       {
+               register void *x0 asm("x0") = &if_tbl;
+               register void *x1 asm("x1") = NULL;
+               register u64 x8 asm("x8") = entry_point;
+
+               asm volatile ("blr x8"
+                             : "+r" (x0)
+                             : "r" (x1), "r" (x8)
+                             : "x2", "x3", "x4", "x5", "x6", "x7", "x9", "x10",
+                               "x11", "x12", "x13", "x14", "x15", "x16", "x17",
+                               "x19", "x20", "x21", "x22", "x23", "x24", "x25",
+                               "x26", "x27", "x28", "x30", "cc", "memory");
+       }
+
+       return qcom_spl_soc_post_qclib_routine(&if_tbl);
+}

-- 
2.34.1

Reply via email to