summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/cache/qcom,llcc.yaml46
-rw-r--r--Documentation/devicetree/bindings/crypto/qcom,prng.yaml1
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml2
-rw-r--r--Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml3
-rw-r--r--Documentation/devicetree/bindings/sram/sram.yaml2
-rw-r--r--drivers/bus/qcom-ebi2.c7
-rw-r--r--drivers/clk/qcom/common.c2
-rw-r--r--drivers/firmware/qcom/qcom_scm.c507
-rw-r--r--drivers/firmware/qcom/qcom_scm.h2
-rw-r--r--drivers/remoteproc/qcom_q6v5_pas.c165
-rw-r--r--drivers/soc/qcom/cmd-db.c7
-rw-r--r--drivers/soc/qcom/llcc-qcom.c207
-rw-r--r--drivers/soc/qcom/mdt_loader.c51
-rw-r--r--drivers/soc/qcom/pmic_glink_altmode.c188
-rw-r--r--drivers/soc/qcom/qmi_encdec.c137
-rw-r--r--drivers/soc/qcom/smem.c4
-rw-r--r--include/linux/firmware/qcom/qcom_scm.h30
-rw-r--r--include/linux/soc/qcom/llcc-qcom.h4
-rw-r--r--include/linux/soc/qcom/mdt_loader.h22
-rw-r--r--include/linux/soc/qcom/ubwc.h1
20 files changed, 1199 insertions, 189 deletions
diff --git a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml
index a620a2ff5c56..6671e461e34a 100644
--- a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml
+++ b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml
@@ -20,6 +20,7 @@ description: |
properties:
compatible:
enum:
+ - qcom,glymur-llcc
- qcom,ipq5424-llcc
- qcom,kaanapali-llcc
- qcom,qcs615-llcc
@@ -46,11 +47,11 @@ properties:
reg:
minItems: 1
- maxItems: 10
+ maxItems: 14
reg-names:
minItems: 1
- maxItems: 10
+ maxItems: 14
interrupts:
maxItems: 1
@@ -89,6 +90,47 @@ allOf:
compatible:
contains:
enum:
+ - qcom,glymur-llcc
+ then:
+ properties:
+ reg:
+ items:
+ - description: LLCC0 base register region
+ - description: LLCC1 base register region
+ - description: LLCC2 base register region
+ - description: LLCC3 base register region
+ - description: LLCC4 base register region
+ - description: LLCC5 base register region
+ - description: LLCC6 base register region
+ - description: LLCC7 base register region
+ - description: LLCC8 base register region
+ - description: LLCC9 base register region
+ - description: LLCC10 base register region
+ - description: LLCC11 base register region
+ - description: LLCC broadcast base register region
+ - description: LLCC broadcast AND register region
+ reg-names:
+ items:
+ - const: llcc0_base
+ - const: llcc1_base
+ - const: llcc2_base
+ - const: llcc3_base
+ - const: llcc4_base
+ - const: llcc5_base
+ - const: llcc6_base
+ - const: llcc7_base
+ - const: llcc8_base
+ - const: llcc9_base
+ - const: llcc10_base
+ - const: llcc11_base
+ - const: llcc_broadcast_base
+ - const: llcc_broadcast_and_base
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
- qcom,sar1130p-llcc
- qcom,sar2130p-llcc
then:
diff --git a/Documentation/devicetree/bindings/crypto/qcom,prng.yaml b/Documentation/devicetree/bindings/crypto/qcom,prng.yaml
index 597441d94cf1..a9674e29144e 100644
--- a/Documentation/devicetree/bindings/crypto/qcom,prng.yaml
+++ b/Documentation/devicetree/bindings/crypto/qcom,prng.yaml
@@ -21,6 +21,7 @@ properties:
- qcom,ipq5424-trng
- qcom,ipq9574-trng
- qcom,kaanapali-trng
+ - qcom,milos-trng
- qcom,qcs615-trng
- qcom,qcs8300-trng
- qcom,sa8255p-trng
diff --git a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml
index 38d0c2d57dd6..f9321366cae4 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml
@@ -27,6 +27,8 @@ properties:
items:
- enum:
- qcom,glymur-pdc
+ - qcom,kaanapali-pdc
+ - qcom,milos-pdc
- qcom,qcs615-pdc
- qcom,qcs8300-pdc
- qcom,qdu1000-pdc
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml
index 63a82e7a8bf8..68c17bf18987 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml
@@ -44,6 +44,9 @@ properties:
- const: stop-ack
- const: shutdown-ack
+ iommus:
+ maxItems: 1
+
power-domains:
minItems: 1
maxItems: 3
diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml
index 7c1337e159f2..c451140962c8 100644
--- a/Documentation/devicetree/bindings/sram/sram.yaml
+++ b/Documentation/devicetree/bindings/sram/sram.yaml
@@ -34,6 +34,7 @@ properties:
- nvidia,tegra186-sysram
- nvidia,tegra194-sysram
- nvidia,tegra234-sysram
+ - qcom,kaanapali-imem
- qcom,rpm-msg-ram
- rockchip,rk3288-pmu-sram
@@ -89,6 +90,7 @@ patternProperties:
- arm,juno-scp-shmem
- arm,scmi-shmem
- arm,scp-shmem
+ - qcom,pil-reloc-info
- renesas,smp-sram
- rockchip,rk3066-smp-sram
- samsung,exynos4210-sysram
diff --git a/drivers/bus/qcom-ebi2.c b/drivers/bus/qcom-ebi2.c
index c1fef1b4bd89..be8166565e7c 100644
--- a/drivers/bus/qcom-ebi2.c
+++ b/drivers/bus/qcom-ebi2.c
@@ -292,7 +292,6 @@ static void qcom_ebi2_setup_chipselect(struct device_node *np,
static int qcom_ebi2_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct device_node *child;
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *ebi2_base;
@@ -348,15 +347,13 @@ static int qcom_ebi2_probe(struct platform_device *pdev)
writel(val, ebi2_base);
/* Walk over the child nodes and see what chipselects we use */
- for_each_available_child_of_node(np, child) {
+ for_each_available_child_of_node_scoped(np, child) {
u32 csindex;
/* Figure out the chipselect */
ret = of_property_read_u32(child, "reg", &csindex);
- if (ret) {
- of_node_put(child);
+ if (ret)
return ret;
- }
if (csindex > 5) {
dev_err(dev,
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 121591886774..eec369d2173b 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -454,7 +454,7 @@ int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
base = devm_platform_ioremap_resource(pdev, index);
if (IS_ERR(base))
- return -ENOMEM;
+ return PTR_ERR(base);
regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config);
if (IS_ERR(regmap))
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 1a6f85e463e0..8e3c2ac40341 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -27,21 +27,29 @@
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
+#include <linux/remoteproc.h>
#include <linux/sizes.h>
#include <linux/types.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
#include "qcom_scm.h"
#include "qcom_tzmem.h"
static u32 download_mode;
+#define GIC_SPI_BASE 32
+#define GIC_MAX_SPI 1019 // SPIs in GICv3 spec range from 32..1019
+#define GIC_ESPI_BASE 4096
+#define GIC_MAX_ESPI 5119 // ESPIs in GICv3 spec range from 4096..5119
+
struct qcom_scm {
struct device *dev;
struct clk *core_clk;
struct clk *iface_clk;
struct clk *bus_clk;
struct icc_path *path;
- struct completion waitq_comp;
+ struct completion *waitq_comps;
struct reset_controller_dev reset;
/* control access to the interconnect path */
@@ -51,6 +59,7 @@ struct qcom_scm {
u64 dload_mode_addr;
struct qcom_tzmem_pool *mempool;
+ unsigned int wq_cnt;
};
struct qcom_scm_current_perm_info {
@@ -111,6 +120,8 @@ enum qcom_scm_qseecom_tz_cmd_info {
QSEECOM_TZ_CMD_INFO_VERSION = 3,
};
+#define RSCTABLE_BUFFER_NOT_SUFFICIENT 20
+
#define QSEECOM_MAX_APP_NAME_SIZE 64
#define SHMBRIDGE_RESULT_NOTSUPP 4
@@ -130,6 +141,8 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
#define QCOM_DLOAD_MINIDUMP 2
#define QCOM_DLOAD_BOTHDUMP 3
+#define QCOM_SCM_DEFAULT_WAITQ_COUNT 1
+
static const char * const qcom_scm_convention_names[] = {
[SMC_CONVENTION_UNKNOWN] = "unknown",
[SMC_CONVENTION_ARM_32] = "smc arm 32",
@@ -559,15 +572,104 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
}
/**
+ * devm_qcom_scm_pas_context_alloc() - Allocate peripheral authentication service
+ * context for a given peripheral
+ *
+ * PAS context is device-resource managed, so the caller does not need
+ * to worry about freeing the context memory.
+ *
+ * @dev: PAS firmware device
+ * @pas_id: peripheral authentication service id
+ * @mem_phys: Subsystem reserve memory start address
+ * @mem_size: Subsystem reserve memory size
+ *
+ * Returns: The new PAS context, or ERR_PTR() on failure.
+ */
+struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
+ u32 pas_id,
+ phys_addr_t mem_phys,
+ size_t mem_size)
+{
+ struct qcom_scm_pas_context *ctx;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->dev = dev;
+ ctx->pas_id = pas_id;
+ ctx->mem_phys = mem_phys;
+ ctx->mem_size = mem_size;
+
+ return ctx;
+}
+EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc);
+
+static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
+ struct qcom_scm_res *res)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE,
+ .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW),
+ .args[0] = pas_id,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ goto disable_clk;
+
+ desc.args[1] = mdata_phys;
+
+ ret = qcom_scm_call(__scm->dev, &desc, res);
+ qcom_scm_bw_disable();
+
+disable_clk:
+ qcom_scm_clk_disable();
+
+ return ret;
+}
+
+static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
+ const void *metadata, size_t size)
+{
+ struct qcom_scm_res res;
+ phys_addr_t mdata_phys;
+ void *mdata_buf;
+ int ret;
+
+ mdata_buf = qcom_tzmem_alloc(__scm->mempool, size, GFP_KERNEL);
+ if (!mdata_buf)
+ return -ENOMEM;
+
+ memcpy(mdata_buf, metadata, size);
+ mdata_phys = qcom_tzmem_to_phys(mdata_buf);
+
+ ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res);
+ if (ret < 0)
+ qcom_tzmem_free(mdata_buf);
+ else
+ ctx->ptr = mdata_buf;
+
+ return ret ? : res.result[0];
+}
+
+/**
* qcom_scm_pas_init_image() - Initialize peripheral authentication service
* state machine for a given peripheral, using the
* metadata
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
* @metadata: pointer to memory containing ELF header, program header table
* and optional blob of data used for authenticating the metadata
* and the rest of the firmware
* @size: size of the metadata
- * @ctx: optional metadata context
+ * @ctx: optional pas context
*
* Return: 0 on success.
*
@@ -575,20 +677,16 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
* track the metadata allocation, this needs to be released by invoking
* qcom_scm_pas_metadata_release() by the caller.
*/
-int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
- struct qcom_scm_pas_metadata *ctx)
+int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
+ struct qcom_scm_pas_context *ctx)
{
+ struct qcom_scm_res res;
dma_addr_t mdata_phys;
void *mdata_buf;
int ret;
- struct qcom_scm_desc desc = {
- .svc = QCOM_SCM_SVC_PIL,
- .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE,
- .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW),
- .args[0] = peripheral,
- .owner = ARM_SMCCC_OWNER_SIP,
- };
- struct qcom_scm_res res;
+
+ if (ctx && ctx->use_tzmem)
+ return qcom_scm_pas_prep_and_init_image(ctx, metadata, size);
/*
* During the scm call memory protection will be enabled for the meta
@@ -609,23 +707,7 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
memcpy(mdata_buf, metadata, size);
- ret = qcom_scm_clk_enable();
- if (ret)
- goto out;
-
- ret = qcom_scm_bw_enable();
- if (ret)
- goto disable_clk;
-
- desc.args[1] = mdata_phys;
-
- ret = qcom_scm_call(__scm->dev, &desc, &res);
- qcom_scm_bw_disable();
-
-disable_clk:
- qcom_scm_clk_disable();
-
-out:
+ ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res);
if (ret < 0 || !ctx) {
dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
} else if (ctx) {
@@ -640,38 +722,39 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
/**
* qcom_scm_pas_metadata_release() - release metadata context
- * @ctx: metadata context
+ * @ctx: pas context
*/
-void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx)
+void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
{
if (!ctx->ptr)
return;
- dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
+ if (ctx->use_tzmem)
+ qcom_tzmem_free(ctx->ptr);
+ else
+ dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
ctx->ptr = NULL;
- ctx->phys = 0;
- ctx->size = 0;
}
EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release);
/**
* qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
* for firmware loading
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
* @addr: start address of memory area to prepare
* @size: size of the memory area to prepare
*
* Returns 0 on success.
*/
-int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
+int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
{
int ret;
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_PIL,
.cmd = QCOM_SCM_PIL_PAS_MEM_SETUP,
.arginfo = QCOM_SCM_ARGS(3),
- .args[0] = peripheral,
+ .args[0] = pas_id,
.args[1] = addr,
.args[2] = size,
.owner = ARM_SMCCC_OWNER_SIP,
@@ -696,21 +779,189 @@ disable_clk:
}
EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
+static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
+ size_t input_rt_size,
+ size_t *output_rt_size)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_PIL_PAS_GET_RSCTABLE,
+ .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RO, QCOM_SCM_VAL,
+ QCOM_SCM_RW, QCOM_SCM_VAL),
+ .args[0] = pas_id,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+ void *output_rt_tzm;
+ int ret;
+
+ output_rt_tzm = qcom_tzmem_alloc(__scm->mempool, *output_rt_size, GFP_KERNEL);
+ if (!output_rt_tzm)
+ return ERR_PTR(-ENOMEM);
+
+ desc.args[1] = qcom_tzmem_to_phys(input_rt_tzm);
+ desc.args[2] = input_rt_size;
+ desc.args[3] = qcom_tzmem_to_phys(output_rt_tzm);
+ desc.args[4] = *output_rt_size;
+
+ /*
+ * Whether SMC fail or pass, res.result[2] will hold actual resource table
+ * size.
+ *
+ * If passed 'output_rt_size' buffer size is not sufficient to hold the
+ * resource table TrustZone sends, response code in res.result[1] as
+ * RSCTABLE_BUFFER_NOT_SUFFICIENT so that caller can retry this SMC call
+ * with output_rt_tzm buffer with res.result[2] size however, It should not
+ * be of unresonable size.
+ */
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+ if (!ret && res.result[2] > SZ_1G) {
+ ret = -E2BIG;
+ goto free_output_rt;
+ }
+
+ *output_rt_size = res.result[2];
+ if (ret && res.result[1] == RSCTABLE_BUFFER_NOT_SUFFICIENT)
+ ret = -EOVERFLOW;
+
+free_output_rt:
+ if (ret)
+ qcom_tzmem_free(output_rt_tzm);
+
+ return ret ? ERR_PTR(ret) : output_rt_tzm;
+}
+
+/**
+ * qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
+ * for a given peripheral.
+ *
+ * Qualcomm remote processor may rely on both static and dynamic resources for
+ * its functionality. Static resources typically refer to memory-mapped addresses
+ * required by the subsystem and are often embedded within the firmware binary
+ * and dynamic resources, such as shared memory in DDR etc., are determined at
+ * runtime during the boot process.
+ *
+ * On Qualcomm Technologies devices, it's possible that static resources are not
+ * embedded in the firmware binary and instead are provided by TrustZone However,
+ * dynamic resources are always expected to come from TrustZone. This indicates
+ * that for Qualcomm devices, all resources (static and dynamic) will be provided
+ * by TrustZone via the SMC call.
+ *
+ * If the remote processor firmware binary does contain static resources, they
+ * should be passed in input_rt. These will be forwarded to TrustZone for
+ * authentication. TrustZone will then append the dynamic resources and return
+ * the complete resource table in output_rt_tzm.
+ *
+ * If the remote processor firmware binary does not include a resource table,
+ * the caller of this function should set input_rt as NULL and input_rt_size
+ * as zero respectively.
+ *
+ * More about documentation on resource table data structures can be found in
+ * include/linux/remoteproc.h
+ *
+ * @ctx: PAS context
+ * @pas_id: peripheral authentication service id
+ * @input_rt: resource table buffer which is present in firmware binary
+ * @input_rt_size: size of the resource table present in firmware binary
+ * @output_rt_size: TrustZone expects caller should pass worst case size for
+ * the output_rt_tzm.
+ *
+ * Return:
+ * On success, returns a pointer to the allocated buffer containing the final
+ * resource table and output_rt_size will have actual resource table size from
+ * TrustZone. The caller is responsible for freeing the buffer. On failure,
+ * returns ERR_PTR(-errno).
+ */
+struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
+ void *input_rt,
+ size_t input_rt_size,
+ size_t *output_rt_size)
+{
+ struct resource_table empty_rsc = {};
+ size_t size = SZ_16K;
+ void *output_rt_tzm;
+ void *input_rt_tzm;
+ void *tbl_ptr;
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ goto disable_clk;
+
+ /*
+ * TrustZone can not accept buffer as NULL value as argument hence,
+ * we need to pass a input buffer indicating that subsystem firmware
+ * does not have resource table by filling resource table structure.
+ */
+ if (!input_rt) {
+ input_rt = &empty_rsc;
+ input_rt_size = sizeof(empty_rsc);
+ }
+
+ input_rt_tzm = qcom_tzmem_alloc(__scm->mempool, input_rt_size, GFP_KERNEL);
+ if (!input_rt_tzm) {
+ ret = -ENOMEM;
+ goto disable_scm_bw;
+ }
+
+ memcpy(input_rt_tzm, input_rt, input_rt_size);
+
+ output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm,
+ input_rt_size, &size);
+ if (PTR_ERR(output_rt_tzm) == -EOVERFLOW)
+ /* Try again with the size requested by the TZ */
+ output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id,
+ input_rt_tzm,
+ input_rt_size,
+ &size);
+ if (IS_ERR(output_rt_tzm)) {
+ ret = PTR_ERR(output_rt_tzm);
+ goto free_input_rt;
+ }
+
+ tbl_ptr = kzalloc(size, GFP_KERNEL);
+ if (!tbl_ptr) {
+ qcom_tzmem_free(output_rt_tzm);
+ ret = -ENOMEM;
+ goto free_input_rt;
+ }
+
+ memcpy(tbl_ptr, output_rt_tzm, size);
+ *output_rt_size = size;
+ qcom_tzmem_free(output_rt_tzm);
+
+free_input_rt:
+ qcom_tzmem_free(input_rt_tzm);
+
+disable_scm_bw:
+ qcom_scm_bw_disable();
+
+disable_clk:
+ qcom_scm_clk_disable();
+
+ return ret ? ERR_PTR(ret) : tbl_ptr;
+}
+EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table);
+
/**
* qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
* and reset the remote processor
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
*
* Return 0 on success.
*/
-int qcom_scm_pas_auth_and_reset(u32 peripheral)
+int qcom_scm_pas_auth_and_reset(u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_PIL,
.cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET,
.arginfo = QCOM_SCM_ARGS(1),
- .args[0] = peripheral,
+ .args[0] = pas_id,
.owner = ARM_SMCCC_OWNER_SIP,
};
struct qcom_scm_res res;
@@ -734,19 +985,66 @@ disable_clk:
EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset);
/**
+ * qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
+ * remote processor
+ *
+ * @ctx: Context saved during call to qcom_scm_pas_context_init()
+ *
+ * This function performs the necessary steps to prepare a PAS subsystem,
+ * authenticate it using the provided metadata, and initiate a reset sequence.
+ *
+ * It should be used when Linux is in control setting up the IOMMU hardware
+ * for remote subsystem during secure firmware loading processes. The preparation
+ * step sets up a shmbridge over the firmware memory before TrustZone accesses the
+ * firmware memory region for authentication. The authentication step verifies
+ * the integrity and authenticity of the firmware or configuration using secure
+ * metadata. Finally, the reset step ensures the subsystem starts in a clean and
+ * sane state.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
+{
+ u64 handle;
+ int ret;
+
+ /*
+ * When Linux running @ EL1, Gunyah hypervisor running @ EL2 traps the
+ * auth_and_reset call and create an shmbridge on the remote subsystem
+ * memory region and then invokes a call to TrustZone to authenticate.
+ */
+ if (!ctx->use_tzmem)
+ return qcom_scm_pas_auth_and_reset(ctx->pas_id);
+
+ /*
+ * When Linux runs @ EL2 Linux must create the shmbridge itself and then
+ * subsequently call TrustZone for authenticate and reset.
+ */
+ ret = qcom_tzmem_shm_bridge_create(ctx->mem_phys, ctx->mem_size, &handle);
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_pas_auth_and_reset(ctx->pas_id);
+ qcom_tzmem_shm_bridge_delete(handle);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset);
+
+/**
* qcom_scm_pas_shutdown() - Shut down the remote processor
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
*
* Returns 0 on success.
*/
-int qcom_scm_pas_shutdown(u32 peripheral)
+int qcom_scm_pas_shutdown(u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_PIL,
.cmd = QCOM_SCM_PIL_PAS_SHUTDOWN,
.arginfo = QCOM_SCM_ARGS(1),
- .args[0] = peripheral,
+ .args[0] = pas_id,
.owner = ARM_SMCCC_OWNER_SIP,
};
struct qcom_scm_res res;
@@ -772,18 +1070,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown);
/**
* qcom_scm_pas_supported() - Check if the peripheral authentication service is
* available for the given peripherial
- * @peripheral: peripheral id
+ * @pas_id: peripheral authentication service id
*
* Returns true if PAS is supported for this peripheral, otherwise false.
*/
-bool qcom_scm_pas_supported(u32 peripheral)
+bool qcom_scm_pas_supported(u32 pas_id)
{
int ret;
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_PIL,
.cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED,
.arginfo = QCOM_SCM_ARGS(1),
- .args[0] = peripheral,
+ .args[0] = pas_id,
.owner = ARM_SMCCC_OWNER_SIP,
};
struct qcom_scm_res res;
@@ -2007,6 +2305,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
{ .compatible = "lenovo,yoga-slim7x" },
{ .compatible = "microsoft,arcata", },
{ .compatible = "microsoft,blackrock" },
+ { .compatible = "microsoft,denali", },
{ .compatible = "microsoft,romulus13", },
{ .compatible = "microsoft,romulus15", },
{ .compatible = "qcom,hamoa-iot-evk" },
@@ -2208,42 +2507,107 @@ bool qcom_scm_is_available(void)
}
EXPORT_SYMBOL_GPL(qcom_scm_is_available);
-static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx)
+static int qcom_scm_fill_irq_fwspec_params(struct irq_fwspec *fwspec, u32 hwirq)
{
- /* FW currently only supports a single wq_ctx (zero).
- * TODO: Update this logic to include dynamic allocation and lookup of
- * completion structs when FW supports more wq_ctx values.
- */
- if (wq_ctx != 0) {
- dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n");
- return -EINVAL;
+ if (hwirq >= GIC_SPI_BASE && hwirq <= GIC_MAX_SPI) {
+ fwspec->param[0] = GIC_SPI;
+ fwspec->param[1] = hwirq - GIC_SPI_BASE;
+ } else if (hwirq >= GIC_ESPI_BASE && hwirq <= GIC_MAX_ESPI) {
+ fwspec->param[0] = GIC_ESPI;
+ fwspec->param[1] = hwirq - GIC_ESPI_BASE;
+ } else {
+ WARN(1, "Unexpected hwirq: %d\n", hwirq);
+ return -ENXIO;
}
+ fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
+ fwspec->param_count = 3;
+
return 0;
}
-int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
+static int qcom_scm_query_waitq_count(struct qcom_scm *scm)
{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_WAITQ,
+ .cmd = QCOM_SCM_WAITQ_GET_INFO,
+ .owner = ARM_SMCCC_OWNER_SIP
+ };
+ struct qcom_scm_res res;
int ret;
- ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
+ ret = qcom_scm_call_atomic(scm->dev, &desc, &res);
if (ret)
return ret;
- wait_for_completion(&__scm->waitq_comp);
-
- return 0;
+ return res.result[0] & GENMASK(7, 0);
}
-static int qcom_scm_waitq_wakeup(unsigned int wq_ctx)
+static int qcom_scm_get_waitq_irq(struct qcom_scm *scm)
{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_WAITQ,
+ .cmd = QCOM_SCM_WAITQ_GET_INFO,
+ .owner = ARM_SMCCC_OWNER_SIP
+ };
+ struct device_node *parent_irq_node;
+ struct irq_fwspec fwspec;
+ struct qcom_scm_res res;
+ u32 hwirq;
int ret;
- ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
+ ret = qcom_scm_call_atomic(scm->dev, &desc, &res);
+ if (ret)
+ return ret;
+
+ hwirq = res.result[1] & GENMASK(15, 0);
+ ret = qcom_scm_fill_irq_fwspec_params(&fwspec, hwirq);
if (ret)
return ret;
- complete(&__scm->waitq_comp);
+ parent_irq_node = of_irq_find_parent(scm->dev->of_node);
+ if (!parent_irq_node)
+ return -ENODEV;
+
+ fwspec.fwnode = of_fwnode_handle(parent_irq_node);
+
+ return irq_create_fwspec_mapping(&fwspec);
+}
+
+static struct completion *qcom_scm_get_completion(u32 wq_ctx)
+{
+ struct completion *wq;
+
+ if (WARN_ON_ONCE(wq_ctx >= __scm->wq_cnt))
+ return ERR_PTR(-EINVAL);
+
+ wq = &__scm->waitq_comps[wq_ctx];
+
+ return wq;
+}
+
+int qcom_scm_wait_for_wq_completion(u32 wq_ctx)
+{
+ struct completion *wq;
+
+ wq = qcom_scm_get_completion(wq_ctx);
+ if (IS_ERR(wq))
+ return PTR_ERR(wq);
+
+ wait_for_completion_state(wq, TASK_IDLE);
+
+ return 0;
+}
+
+static int qcom_scm_waitq_wakeup(unsigned int wq_ctx)
+{
+ struct completion *wq;
+
+ wq = qcom_scm_get_completion(wq_ctx);
+ if (IS_ERR(wq))
+ return PTR_ERR(wq);
+
+ complete(wq);
return 0;
}
@@ -2319,6 +2683,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
struct qcom_tzmem_pool_config pool_config;
struct qcom_scm *scm;
int irq, ret;
+ int i;
scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
if (!scm)
@@ -2329,7 +2694,6 @@ static int qcom_scm_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- init_completion(&scm->waitq_comp);
mutex_init(&scm->scm_bw_lock);
scm->path = devm_of_icc_get(&pdev->dev, NULL);
@@ -2381,7 +2745,20 @@ static int qcom_scm_probe(struct platform_device *pdev)
return dev_err_probe(scm->dev, PTR_ERR(scm->mempool),
"Failed to create the SCM memory pool\n");
- irq = platform_get_irq_optional(pdev, 0);
+ ret = qcom_scm_query_waitq_count(scm);
+ scm->wq_cnt = ret < 0 ? QCOM_SCM_DEFAULT_WAITQ_COUNT : ret;
+ scm->waitq_comps = devm_kcalloc(&pdev->dev, scm->wq_cnt, sizeof(*scm->waitq_comps),
+ GFP_KERNEL);
+ if (!scm->waitq_comps)
+ return -ENOMEM;
+
+ for (i = 0; i < scm->wq_cnt; i++)
+ init_completion(&scm->waitq_comps[i]);
+
+ irq = qcom_scm_get_waitq_irq(scm);
+ if (irq < 0)
+ irq = platform_get_irq_optional(pdev, 0);
+
if (irq < 0) {
if (irq != -ENXIO)
return irq;
diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h
index a56c8212cc0c..caab80a73e17 100644
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
+#define QCOM_SCM_PIL_PAS_GET_RSCTABLE 0x21
#define QCOM_SCM_SVC_IO 0x05
#define QCOM_SCM_IO_READ 0x01
@@ -152,6 +153,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
#define QCOM_SCM_SVC_WAITQ 0x24
#define QCOM_SCM_WAITQ_RESUME 0x02
#define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03
+#define QCOM_SCM_WAITQ_GET_INFO 0x04
#define QCOM_SCM_SVC_GPU 0x28
#define QCOM_SCM_SVC_GPU_INIT_REGS 0x01
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index 52680ac99589..46204da046fa 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
+#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -117,8 +118,8 @@ struct qcom_pas {
struct qcom_rproc_ssr ssr_subdev;
struct qcom_sysmon *sysmon;
- struct qcom_scm_pas_metadata pas_metadata;
- struct qcom_scm_pas_metadata dtb_pas_metadata;
+ struct qcom_scm_pas_context *pas_ctx;
+ struct qcom_scm_pas_context *dtb_pas_ctx;
};
static void qcom_pas_segment_dump(struct rproc *rproc,
@@ -211,9 +212,9 @@ static int qcom_pas_unprepare(struct rproc *rproc)
* auth_and_reset() was successful, but in other cases clean it up
* here.
*/
- qcom_scm_pas_metadata_release(&pas->pas_metadata);
+ qcom_scm_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
return 0;
}
@@ -239,15 +240,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
return ret;
}
- ret = qcom_mdt_pas_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name,
- pas->dtb_pas_id, pas->dtb_mem_phys,
- &pas->dtb_pas_metadata);
- if (ret)
- goto release_dtb_firmware;
-
- ret = qcom_mdt_load_no_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name,
- pas->dtb_mem_region, pas->dtb_mem_phys,
- pas->dtb_mem_size, &pas->dtb_mem_reloc);
+ ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware,
+ pas->dtb_firmware_name, pas->dtb_mem_region,
+ &pas->dtb_mem_reloc);
if (ret)
goto release_dtb_metadata;
}
@@ -255,14 +250,28 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
return 0;
release_dtb_metadata:
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
-
-release_dtb_firmware:
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
release_firmware(pas->dtb_firmware);
return ret;
}
+static void qcom_pas_unmap_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size)
+{
+ if (rproc->has_iommu)
+ iommu_unmap(rproc->domain, mem_phys, size);
+}
+
+static int qcom_pas_map_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size)
+{
+ int ret = 0;
+
+ if (rproc->has_iommu)
+ ret = iommu_map(rproc->domain, mem_phys, mem_phys, size,
+ IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
+ return ret;
+}
+
static int qcom_pas_start(struct rproc *rproc)
{
struct qcom_pas *pas = rproc->priv;
@@ -297,54 +306,62 @@ static int qcom_pas_start(struct rproc *rproc)
}
if (pas->dtb_pas_id) {
- ret = qcom_scm_pas_auth_and_reset(pas->dtb_pas_id);
+ ret = qcom_pas_map_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
+ if (ret)
+ goto disable_px_supply;
+
+ ret = qcom_scm_pas_prepare_and_auth_reset(pas->dtb_pas_ctx);
if (ret) {
dev_err(pas->dev,
"failed to authenticate dtb image and release reset\n");
- goto disable_px_supply;
+ goto unmap_dtb_carveout;
}
}
- ret = qcom_mdt_pas_init(pas->dev, pas->firmware, rproc->firmware, pas->pas_id,
- pas->mem_phys, &pas->pas_metadata);
- if (ret)
- goto disable_px_supply;
-
- ret = qcom_mdt_load_no_init(pas->dev, pas->firmware, rproc->firmware,
- pas->mem_region, pas->mem_phys, pas->mem_size,
- &pas->mem_reloc);
+ ret = qcom_mdt_pas_load(pas->pas_ctx, pas->firmware, rproc->firmware,
+ pas->mem_region, &pas->mem_reloc);
if (ret)
goto release_pas_metadata;
qcom_pil_info_store(pas->info_name, pas->mem_phys, pas->mem_size);
- ret = qcom_scm_pas_auth_and_reset(pas->pas_id);
+ ret = qcom_pas_map_carveout(rproc, pas->mem_phys, pas->mem_size);
+ if (ret)
+ goto release_pas_metadata;
+
+ ret = qcom_scm_pas_prepare_and_auth_reset(pas->pas_ctx);
if (ret) {
dev_err(pas->dev,
"failed to authenticate image and release reset\n");
- goto release_pas_metadata;
+ goto unmap_carveout;
}
ret = qcom_q6v5_wait_for_start(&pas->q6v5, msecs_to_jiffies(5000));
if (ret == -ETIMEDOUT) {
dev_err(pas->dev, "start timed out\n");
qcom_scm_pas_shutdown(pas->pas_id);
- goto release_pas_metadata;
+ goto unmap_carveout;
}
- qcom_scm_pas_metadata_release(&pas->pas_metadata);
+ qcom_scm_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
/* firmware is used to pass reference from qcom_pas_start(), drop it now */
pas->firmware = NULL;
return 0;
+unmap_carveout:
+ qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
release_pas_metadata:
- qcom_scm_pas_metadata_release(&pas->pas_metadata);
+ qcom_scm_pas_metadata_release(pas->pas_ctx);
+ if (pas->dtb_pas_id)
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
+
+unmap_dtb_carveout:
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
+ qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
disable_px_supply:
if (pas->px_supply)
regulator_disable(pas->px_supply);
@@ -400,8 +417,12 @@ static int qcom_pas_stop(struct rproc *rproc)
ret = qcom_scm_pas_shutdown(pas->dtb_pas_id);
if (ret)
dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret);
+
+ qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
}
+ qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
+
handover = qcom_q6v5_unprepare(&pas->q6v5);
if (handover)
qcom_pas_handover(&pas->q6v5);
@@ -427,6 +448,61 @@ static void *qcom_pas_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is
return pas->mem_region + offset;
}
+static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *fw)
+{
+ struct qcom_pas *pas = rproc->priv;
+ struct resource_table *table = NULL;
+ size_t output_rt_size;
+ void *output_rt;
+ size_t table_sz;
+ int ret;
+
+ ret = qcom_register_dump_segments(rproc, fw);
+ if (ret) {
+ dev_err(pas->dev, "Error in registering dump segments\n");
+ return ret;
+ }
+
+ if (!rproc->has_iommu)
+ return 0;
+
+ ret = rproc_elf_load_rsc_table(rproc, fw);
+ if (ret)
+ dev_dbg(&rproc->dev, "Failed to load resource table from firmware\n");
+
+ table = rproc->table_ptr;
+ table_sz = rproc->table_sz;
+
+ /*
+ * The resources consumed by Qualcomm remote processors fall into two categories:
+ * static (such as the memory carveouts for the rproc firmware) and dynamic (like
+ * shared memory pools). Both are managed by a Qualcomm hypervisor (such as QHEE
+ * or Gunyah), if one is present. Otherwise, a resource table must be retrieved
+ * via an SCM call. That table will list all dynamic resources (if any) and possibly
+ * the static ones. The static resources may also come from a resource table embedded
+ * in the rproc firmware instead.
+ *
+ * Here, we call rproc_elf_load_rsc_table() to check firmware binary has resources
+ * or not and if it is not having then we pass NULL and zero as input resource
+ * table pointer and size respectively to the argument of qcom_scm_pas_get_rsc_table()
+ * and this is even true for Qualcomm remote processor who does follow remoteproc
+ * framework.
+ */
+ output_rt = qcom_scm_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size);
+ ret = IS_ERR(output_rt) ? PTR_ERR(output_rt) : 0;
+ if (ret) {
+ dev_err(pas->dev, "Error in getting resource table: %d\n", ret);
+ return ret;
+ }
+
+ kfree(rproc->cached_table);
+ rproc->cached_table = output_rt;
+ rproc->table_ptr = rproc->cached_table;
+ rproc->table_sz = output_rt_size;
+
+ return ret;
+}
+
static unsigned long qcom_pas_panic(struct rproc *rproc)
{
struct qcom_pas *pas = rproc->priv;
@@ -439,7 +515,7 @@ static const struct rproc_ops qcom_pas_ops = {
.start = qcom_pas_start,
.stop = qcom_pas_stop,
.da_to_va = qcom_pas_da_to_va,
- .parse_fw = qcom_register_dump_segments,
+ .parse_fw = qcom_pas_parse_firmware,
.load = qcom_pas_load,
.panic = qcom_pas_panic,
};
@@ -449,7 +525,7 @@ static const struct rproc_ops qcom_pas_minidump_ops = {
.start = qcom_pas_start,
.stop = qcom_pas_stop,
.da_to_va = qcom_pas_da_to_va,
- .parse_fw = qcom_register_dump_segments,
+ .parse_fw = qcom_pas_parse_firmware,
.load = qcom_pas_load,
.panic = qcom_pas_panic,
.coredump = qcom_pas_minidump,
@@ -697,6 +773,7 @@ static int qcom_pas_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus");
rproc->auto_boot = desc->auto_boot;
rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
@@ -760,6 +837,24 @@ static int qcom_pas_probe(struct platform_device *pdev)
}
qcom_add_ssr_subdev(rproc, &pas->ssr_subdev, desc->ssr_name);
+
+ pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id,
+ pas->mem_phys, pas->mem_size);
+ if (IS_ERR(pas->pas_ctx)) {
+ ret = PTR_ERR(pas->pas_ctx);
+ goto remove_ssr_sysmon;
+ }
+
+ pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id,
+ pas->dtb_mem_phys,
+ pas->dtb_mem_size);
+ if (IS_ERR(pas->dtb_pas_ctx)) {
+ ret = PTR_ERR(pas->dtb_pas_ctx);
+ goto remove_ssr_sysmon;
+ }
+
+ pas->pas_ctx->use_tzmem = rproc->has_iommu;
+ pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu;
ret = rproc_add(rproc);
if (ret)
goto remove_ssr_sysmon;
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c
index ae66c2623d25..84a75d8c4b70 100644
--- a/drivers/soc/qcom/cmd-db.c
+++ b/drivers/soc/qcom/cmd-db.c
@@ -349,15 +349,16 @@ static int cmd_db_dev_probe(struct platform_device *pdev)
return -EINVAL;
}
- cmd_db_header = memremap(rmem->base, rmem->size, MEMREMAP_WC);
- if (!cmd_db_header) {
- ret = -ENOMEM;
+ cmd_db_header = devm_memremap(&pdev->dev, rmem->base, rmem->size, MEMREMAP_WC);
+ if (IS_ERR(cmd_db_header)) {
+ ret = PTR_ERR(cmd_db_header);
cmd_db_header = NULL;
return ret;
}
if (!cmd_db_magic_matches(cmd_db_header)) {
dev_err(&pdev->dev, "Invalid Command DB Magic\n");
+ cmd_db_header = NULL;
return -EINVAL;
}
diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c
index 13e174267294..1abfda7a58f2 100644
--- a/drivers/soc/qcom/llcc-qcom.c
+++ b/drivers/soc/qcom/llcc-qcom.c
@@ -182,6 +182,197 @@ enum llcc_reg_offset {
LLCC_TRP_WRS_CACHEABLE_EN,
};
+static const struct llcc_slice_config glymur_data[] = {
+ {
+ .usecase_id = LLCC_CPUSS,
+ .slice_id = 1,
+ .max_cap = 7680,
+ .priority = 1,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_VIDSC0,
+ .slice_id = 2,
+ .max_cap = 512,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_AUDIO,
+ .slice_id = 6,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_VIDSC1,
+ .slice_id = 4,
+ .max_cap = 512,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_CMPT,
+ .slice_id = 10,
+ .max_cap = 7680,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_GPUHTW,
+ .slice_id = 11,
+ .max_cap = 512,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_GPU,
+ .slice_id = 9,
+ .max_cap = 7680,
+ .priority = 1,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .write_scid_en = true,
+ .write_scid_cacheable_en = true,
+ .stale_en = true,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_MMUHWT,
+ .slice_id = 18,
+ .max_cap = 768,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_AUDHW,
+ .slice_id = 22,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_CVP,
+ .slice_id = 8,
+ .max_cap = 64,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_WRCACHE,
+ .slice_id = 31,
+ .max_cap = 1536,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_CMPTHCP,
+ .slice_id = 17,
+ .max_cap = 256,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_LCPDARE,
+ .slice_id = 30,
+ .max_cap = 768,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .alloc_oneway_en = true,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_AENPU,
+ .slice_id = 3,
+ .max_cap = 3072,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .cache_mode = 2,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_ISLAND1,
+ .slice_id = 12,
+ .max_cap = 5632,
+ .priority = 7,
+ .fixed_size = true,
+ .bonus_ways = 0x0,
+ .res_ways = 0x7FF,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_VIDVSP,
+ .slice_id = 28,
+ .max_cap = 256,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_OOBM_NS,
+ .slice_id = 5,
+ .max_cap = 512,
+ .priority = 1,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }, {
+ .usecase_id = LLCC_CPUSS_OPP,
+ .slice_id = 32,
+ .max_cap = 0,
+ .fixed_size = true,
+ .bonus_ways = 0x0,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_PCIE_TCU,
+ .slice_id = 19,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ .activate_on_init = true,
+ }, {
+ .usecase_id = LLCC_VIDSC_VSP1,
+ .slice_id = 29,
+ .max_cap = 256,
+ .priority = 3,
+ .fixed_size = true,
+ .bonus_ways = 0xFFF,
+ .res_ways = 0x0,
+ .vict_prio = true,
+ }
+};
+
static const struct llcc_slice_config ipq5424_data[] = {
{
.usecase_id = LLCC_CPUSS,
@@ -3872,6 +4063,16 @@ static const struct qcom_llcc_config kaanapali_cfg[] = {
},
};
+static const struct qcom_llcc_config glymur_cfg[] = {
+ {
+ .sct_data = glymur_data,
+ .size = ARRAY_SIZE(glymur_data),
+ .reg_offset = llcc_v6_reg_offset,
+ .edac_reg_offset = &llcc_v2_1_edac_reg_offset,
+ .no_edac = true,
+ },
+};
+
static const struct qcom_llcc_config qcs615_cfg[] = {
{
.sct_data = qcs615_data,
@@ -4103,6 +4304,11 @@ static const struct qcom_sct_config kaanapali_cfgs = {
.num_config = ARRAY_SIZE(kaanapali_cfg),
};
+static const struct qcom_sct_config glymur_cfgs = {
+ .llcc_config = glymur_cfg,
+ .num_config = ARRAY_SIZE(glymur_cfg),
+};
+
static const struct qcom_sct_config qcs615_cfgs = {
.llcc_config = qcs615_cfg,
.num_config = ARRAY_SIZE(qcs615_cfg),
@@ -4941,6 +5147,7 @@ err:
}
static const struct of_device_id qcom_llcc_of_match[] = {
+ { .compatible = "qcom,glymur-llcc", .data = &glymur_cfgs },
{ .compatible = "qcom,ipq5424-llcc", .data = &ipq5424_cfgs},
{ .compatible = "qcom,kaanapali-llcc", .data = &kaanapali_cfgs},
{ .compatible = "qcom,qcs615-llcc", .data = &qcs615_cfgs},
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c
index c239107cb930..c004d444d698 100644
--- a/drivers/soc/qcom/mdt_loader.c
+++ b/drivers/soc/qcom/mdt_loader.c
@@ -227,20 +227,9 @@ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len,
}
EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata);
-/**
- * qcom_mdt_pas_init() - initialize PAS region for firmware loading
- * @dev: device handle to associate resources with
- * @fw: firmware object for the mdt file
- * @fw_name: name of the firmware, for construction of segment file names
- * @pas_id: PAS identifier
- * @mem_phys: physical address of allocated memory region
- * @ctx: PAS metadata context, to be released by caller
- *
- * Returns 0 on success, negative errno otherwise.
- */
-int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
- const char *fw_name, int pas_id, phys_addr_t mem_phys,
- struct qcom_scm_pas_metadata *ctx)
+static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
+ const char *fw_name, int pas_id, phys_addr_t mem_phys,
+ struct qcom_scm_pas_context *ctx)
{
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
@@ -302,7 +291,6 @@ int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
out:
return ret;
}
-EXPORT_SYMBOL_GPL(qcom_mdt_pas_init);
static bool qcom_mdt_bins_are_split(const struct firmware *fw)
{
@@ -469,7 +457,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
{
int ret;
- ret = qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL);
+ ret = __qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL);
if (ret)
return ret;
@@ -478,5 +466,36 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
}
EXPORT_SYMBOL_GPL(qcom_mdt_load);
+/**
+ * qcom_mdt_pas_load - Loads and authenticates the metadata of the firmware
+ * (typically contained in the .mdt file), followed by loading the actual
+ * firmware segments (e.g., .bXX files). Authentication of the segments done
+ * by a separate call.
+ *
+ * The PAS context must be initialized using qcom_scm_pas_context_init()
+ * prior to invoking this function.
+ *
+ * @ctx: Pointer to the PAS (Peripheral Authentication Service) context
+ * @fw: Firmware object representing the .mdt file
+ * @firmware: Name of the firmware used to construct segment file names
+ * @mem_region: Memory region allocated for loading the firmware
+ * @reloc_base: Physical address adjusted after relocation
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw,
+ const char *firmware, void *mem_region, phys_addr_t *reloc_base)
+{
+ int ret;
+
+ ret = __qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx);
+ if (ret)
+ return ret;
+
+ return qcom_mdt_load_no_init(ctx->dev, fw, firmware, mem_region, ctx->mem_phys,
+ ctx->mem_size, reloc_base);
+}
+EXPORT_SYMBOL_GPL(qcom_mdt_pas_load);
+
MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c
index 7f11acd33323..d0afdcb96ee1 100644
--- a/drivers/soc/qcom/pmic_glink_altmode.c
+++ b/drivers/soc/qcom/pmic_glink_altmode.c
@@ -14,10 +14,12 @@
#include <linux/soc/qcom/pdr.h>
#include <drm/bridge/aux-bridge.h>
+#include <linux/usb/pd.h>
#include <linux/usb/typec_altmode.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_retimer.h>
+#include <linux/usb/typec_tbt.h>
#include <linux/soc/qcom/pmic_glink.h>
@@ -37,11 +39,38 @@ struct usbc_write_req {
__le32 reserved;
};
-#define NOTIFY_PAYLOAD_SIZE 16
+struct usbc_sc8280x_dp_data {
+ u8 pin_assignment : 6;
+ u8 hpd_state : 1;
+ u8 hpd_irq : 1;
+ u8 res[7];
+};
+
+/* Used for both TBT and USB4 notifications */
+struct usbc_sc8280x_tbt_data {
+ u8 usb_speed : 3;
+ u8 cable_type : 3;
+ /* This field is NOP on USB4, all cables support rounded rates by spec */
+ u8 rounded_cable : 1;
+ u8 power_limited : 1;
+ u8 res[11];
+};
+
struct usbc_notify {
struct pmic_glink_hdr hdr;
- char payload[NOTIFY_PAYLOAD_SIZE];
- u32 reserved;
+ u8 port_idx;
+ u8 orientation;
+ u8 mux_ctrl;
+#define MUX_CTRL_STATE_NO_CONN 0
+#define MUX_CTRL_STATE_TUNNELING 4
+
+ u8 res;
+ __le16 vid;
+ __le16 svid;
+ union usbc_sc8280x_extended_data {
+ struct usbc_sc8280x_dp_data dp;
+ struct usbc_sc8280x_tbt_data tbt;
+ } extended_data;
};
struct usbc_sc8180x_notify {
@@ -74,6 +103,7 @@ struct pmic_glink_altmode_port {
struct typec_retimer *typec_retimer;
struct typec_retimer_state retimer_state;
struct typec_altmode dp_alt;
+ struct typec_altmode tbt_alt;
struct work_struct work;
@@ -81,10 +111,12 @@ struct pmic_glink_altmode_port {
enum typec_orientation orientation;
u16 svid;
+ struct usbc_sc8280x_tbt_data tbt_data;
u8 dp_data;
u8 mode;
u8 hpd_state;
u8 hpd_irq;
+ u8 mux_ctrl;
};
#define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work)
@@ -170,6 +202,102 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode,
dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret);
}
+static void pmic_glink_altmode_enable_tbt(struct pmic_glink_altmode *altmode,
+ struct pmic_glink_altmode_port *port)
+{
+ struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data;
+ struct typec_thunderbolt_data tbt_data = {};
+ u32 cable_speed;
+ int ret;
+
+ /* Device Discover Mode VDO */
+ tbt_data.device_mode = TBT_MODE;
+ tbt_data.device_mode |= TBT_SET_ADAPTER(TBT_ADAPTER_TBT3);
+
+ /* Cable Discover Mode VDO */
+ tbt_data.cable_mode = TBT_MODE;
+
+ if (tbt->usb_speed == 0) {
+ cable_speed = TBT_CABLE_USB3_PASSIVE;
+ } else if (tbt->usb_speed == 1) {
+ cable_speed = TBT_CABLE_10_AND_20GBPS;
+ } else {
+ dev_err(altmode->dev,
+ "Got illegal TBT3 cable speed value (%u), falling back to passive\n",
+ tbt->usb_speed);
+ cable_speed = TBT_CABLE_USB3_PASSIVE;
+ }
+ tbt_data.cable_mode |= TBT_SET_CABLE_SPEED(cable_speed);
+
+ if (tbt->cable_type) {
+ tbt_data.cable_mode |= TBT_CABLE_ACTIVE_PASSIVE;
+ tbt_data.cable_mode |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable);
+ }
+
+ /* Enter Mode VDO */
+ tbt_data.enter_vdo |= TBT_MODE;
+ tbt_data.enter_vdo |= TBT_ENTER_MODE_CABLE_SPEED(cable_speed);
+
+ if (tbt->cable_type) {
+ tbt_data.enter_vdo |= TBT_CABLE_ACTIVE_PASSIVE;
+ tbt_data.enter_vdo |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable);
+ }
+
+ port->state.alt = &port->tbt_alt;
+ port->state.data = &tbt_data;
+ port->state.mode = TYPEC_MODAL_STATE(port->mode);
+
+ ret = typec_mux_set(port->typec_mux, &port->state);
+ if (ret)
+ dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);
+
+ port->retimer_state.alt = &port->tbt_alt;
+ port->retimer_state.data = &tbt_data;
+ port->retimer_state.mode = TYPEC_MODAL_STATE(port->mode);
+
+ ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
+ if (ret)
+ dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
+}
+
+static void pmic_glink_altmode_enable_usb4(struct pmic_glink_altmode *altmode,
+ struct pmic_glink_altmode_port *port)
+{
+ struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data;
+ struct enter_usb_data data = {};
+ int ret;
+
+ data.eudo = FIELD_PREP(EUDO_USB_MODE_MASK, EUDO_USB_MODE_USB4);
+
+ if (tbt->usb_speed == 0) {
+ data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2);
+ } else if (tbt->usb_speed == 1) {
+ data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN3);
+ } else {
+ pr_err("Got illegal USB4 cable speed value (%u), falling back to G2\n",
+ tbt->usb_speed);
+ data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2);
+ }
+
+ data.eudo |= FIELD_PREP(EUDO_CABLE_TYPE_MASK, tbt->cable_type);
+
+ port->state.alt = NULL;
+ port->state.data = &data;
+ port->state.mode = TYPEC_MODE_USB4;
+
+ ret = typec_mux_set(port->typec_mux, &port->state);
+ if (ret)
+ dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);
+
+ port->retimer_state.alt = NULL;
+ port->retimer_state.data = &data;
+ port->retimer_state.mode = TYPEC_MODE_USB4;
+
+ ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
+ if (ret)
+ dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
+}
+
static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode,
struct pmic_glink_altmode_port *port)
{
@@ -222,15 +350,15 @@ static void pmic_glink_altmode_worker(struct work_struct *work)
typec_switch_set(alt_port->typec_switch, alt_port->orientation);
- if (alt_port->svid == USB_TYPEC_DP_SID) {
- if (alt_port->mode == 0xff) {
- pmic_glink_altmode_safe(altmode, alt_port);
- } else {
- pmic_glink_altmode_enable_dp(altmode, alt_port,
- alt_port->mode,
- alt_port->hpd_state,
- alt_port->hpd_irq);
- }
+ if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) {
+ pmic_glink_altmode_safe(altmode, alt_port);
+ } else if (alt_port->svid == USB_TYPEC_TBT_SID) {
+ pmic_glink_altmode_enable_tbt(altmode, alt_port);
+ } else if (alt_port->svid == USB_TYPEC_DP_SID) {
+ pmic_glink_altmode_enable_dp(altmode, alt_port,
+ alt_port->mode,
+ alt_port->hpd_state,
+ alt_port->hpd_irq);
if (alt_port->hpd_state)
conn_status = connector_status_connected;
@@ -238,6 +366,8 @@ static void pmic_glink_altmode_worker(struct work_struct *work)
conn_status = connector_status_disconnected;
drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, conn_status);
+ } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
+ pmic_glink_altmode_enable_usb4(altmode, alt_port);
} else {
pmic_glink_altmode_enable_usb(altmode, alt_port);
}
@@ -314,11 +444,10 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
u16 svid, const void *data, size_t len)
{
struct pmic_glink_altmode_port *alt_port;
+ const struct usbc_sc8280x_tbt_data *tbt;
+ const struct usbc_sc8280x_dp_data *dp;
const struct usbc_notify *notify;
u8 orientation;
- u8 hpd_state;
- u8 hpd_irq;
- u8 mode;
u8 port;
if (len != sizeof(*notify)) {
@@ -329,11 +458,8 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
notify = data;
- port = notify->payload[0];
- orientation = notify->payload[1];
- mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A;
- hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]);
- hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]);
+ port = notify->port_idx;
+ orientation = notify->orientation;
if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) {
dev_dbg(altmode->dev, "notification on undefined port %d\n", port);
@@ -343,9 +469,21 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
alt_port = &altmode->ports[port];
alt_port->orientation = pmic_glink_altmode_orientation(orientation);
alt_port->svid = svid;
- alt_port->mode = mode;
- alt_port->hpd_state = hpd_state;
- alt_port->hpd_irq = hpd_irq;
+ alt_port->mux_ctrl = notify->mux_ctrl;
+
+ if (svid == USB_TYPEC_DP_SID) {
+ dp = &notify->extended_data.dp;
+
+ alt_port->mode = dp->pin_assignment - DPAM_HPD_A;
+ alt_port->hpd_state = dp->hpd_state;
+ alt_port->hpd_irq = dp->hpd_irq;
+ } else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
+ /* Valid for both USB4 and TBT3 */
+ tbt = &notify->extended_data.tbt;
+
+ alt_port->tbt_data = *tbt;
+ }
+
schedule_work(&alt_port->work);
}
@@ -471,6 +609,10 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev,
alt_port->dp_alt.mode = USB_TYPEC_DP_MODE;
alt_port->dp_alt.active = 1;
+ alt_port->tbt_alt.svid = USB_TYPEC_TBT_SID;
+ alt_port->tbt_alt.mode = TYPEC_TBT_MODE;
+ alt_port->tbt_alt.active = 1;
+
alt_port->typec_mux = fwnode_typec_mux_get(fwnode);
if (IS_ERR(alt_port->typec_mux)) {
fwnode_handle_put(fwnode);
diff --git a/drivers/soc/qcom/qmi_encdec.c b/drivers/soc/qcom/qmi_encdec.c
index 7660a960fb45..28ce6f130b6a 100644
--- a/drivers/soc/qcom/qmi_encdec.c
+++ b/drivers/soc/qcom/qmi_encdec.c
@@ -23,18 +23,60 @@
*p_length |= ((u8)*p_src) << 8; \
} while (0)
-#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \
+#define QMI_ENCDEC_ENCODE_U8(p_dst, p_src) \
do { \
- memcpy(p_dst, p_src, size); \
- p_dst = (u8 *)p_dst + size; \
- p_src = (u8 *)p_src + size; \
+ memcpy(p_dst, p_src, sizeof(u8)); \
+ p_dst = (u8 *)p_dst + sizeof(u8); \
+ p_src = (u8 *)p_src + sizeof(u8); \
} while (0)
-#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \
+#define QMI_ENCDEC_ENCODE_U16(p_dst, p_src) \
do { \
- memcpy(p_dst, p_src, size); \
- p_dst = (u8 *)p_dst + size; \
- p_src = (u8 *)p_src + size; \
+ *(__le16 *)p_dst = __cpu_to_le16(*(u16 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u16); \
+ p_src = (u8 *)p_src + sizeof(u16); \
+} while (0)
+
+#define QMI_ENCDEC_ENCODE_U32(p_dst, p_src) \
+do { \
+ *(__le32 *)p_dst = __cpu_to_le32(*(u32 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u32); \
+ p_src = (u8 *)p_src + sizeof(u32); \
+} while (0)
+
+#define QMI_ENCDEC_ENCODE_U64(p_dst, p_src) \
+do { \
+ *(__le64 *)p_dst = __cpu_to_le64(*(u64 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u64); \
+ p_src = (u8 *)p_src + sizeof(u64); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_U8(p_dst, p_src) \
+do { \
+ memcpy(p_dst, p_src, sizeof(u8)); \
+ p_dst = (u8 *)p_dst + sizeof(u8); \
+ p_src = (u8 *)p_src + sizeof(u8); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_U16(p_dst, p_src) \
+do { \
+ *(u16 *)p_dst = __le16_to_cpu(*(__le16 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u16); \
+ p_src = (u8 *)p_src + sizeof(u16); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_U32(p_dst, p_src) \
+do { \
+ *(u32 *)p_dst = __le32_to_cpu(*(__le32 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u32); \
+ p_src = (u8 *)p_src + sizeof(u32); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_U64(p_dst, p_src) \
+do { \
+ *(u64 *)p_dst = __le64_to_cpu(*(__le64 *)p_src); \
+ p_dst = (u8 *)p_dst + sizeof(u64); \
+ p_src = (u8 *)p_src + sizeof(u64); \
} while (0)
#define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \
@@ -161,7 +203,8 @@ static int qmi_calc_min_msg_len(const struct qmi_elem_info *ei_array,
* of primary data type which include u8 - u64 or similar. This
* function returns the number of bytes of encoded information.
*
- * Return: The number of bytes of encoded information.
+ * Return: The number of bytes of encoded information on success or negative
+ * errno on error.
*/
static int qmi_encode_basic_elem(void *buf_dst, const void *buf_src,
u32 elem_len, u32 elem_size)
@@ -169,7 +212,24 @@ static int qmi_encode_basic_elem(void *buf_dst, const void *buf_src,
u32 i, rc = 0;
for (i = 0; i < elem_len; i++) {
- QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size);
+ switch (elem_size) {
+ case sizeof(u8):
+ QMI_ENCDEC_ENCODE_U8(buf_dst, buf_src);
+ break;
+ case sizeof(u16):
+ QMI_ENCDEC_ENCODE_U16(buf_dst, buf_src);
+ break;
+ case sizeof(u32):
+ QMI_ENCDEC_ENCODE_U32(buf_dst, buf_src);
+ break;
+ case sizeof(u64):
+ QMI_ENCDEC_ENCODE_U64(buf_dst, buf_src);
+ break;
+ default:
+ pr_err("%s: Unrecognized element size\n", __func__);
+ return -EINVAL;
+ }
+
rc += elem_size;
}
@@ -267,11 +327,15 @@ static int qmi_encode_string_elem(const struct qmi_elem_info *ei_array,
}
rc = qmi_encode_basic_elem(buf_dst, &string_len,
1, string_len_sz);
+ if (rc < 0)
+ return rc;
encoded_bytes += rc;
}
rc = qmi_encode_basic_elem(buf_dst + encoded_bytes, buf_src,
string_len, temp_ei->elem_size);
+ if (rc < 0)
+ return rc;
encoded_bytes += rc;
return encoded_bytes;
@@ -333,6 +397,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
case QMI_OPT_FLAG:
rc = qmi_encode_basic_elem(&opt_flag_value, buf_src,
1, sizeof(u8));
+ if (rc < 0)
+ return rc;
if (opt_flag_value)
temp_ei = temp_ei + 1;
else
@@ -340,6 +406,7 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
break;
case QMI_DATA_LEN:
+ memcpy(&data_len_value, buf_src, sizeof(u32));
data_len_sz = temp_ei->elem_size == sizeof(u8) ?
sizeof(u8) : sizeof(u16);
/* Check to avoid out of range buffer access */
@@ -350,15 +417,17 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
return -ETOOSMALL;
}
if (data_len_sz == sizeof(u8)) {
- val8 = *(u8 *)buf_src;
- data_len_value = (u32)val8;
+ val8 = data_len_value;
rc = qmi_encode_basic_elem(buf_dst, &val8,
1, data_len_sz);
+ if (rc < 0)
+ return rc;
} else {
- val16 = *(u16 *)buf_src;
- data_len_value = (u32)le16_to_cpu(val16);
+ val16 = data_len_value;
rc = qmi_encode_basic_elem(buf_dst, &val16,
1, data_len_sz);
+ if (rc < 0)
+ return rc;
}
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
encoded_bytes, tlv_len,
@@ -386,6 +455,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
rc = qmi_encode_basic_elem(buf_dst, buf_src,
data_len_value,
temp_ei->elem_size);
+ if (rc < 0)
+ return rc;
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
encoded_bytes, tlv_len,
encode_tlv, rc);
@@ -444,7 +515,8 @@ static int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf,
* of primary data type which include u8 - u64 or similar. This
* function returns the number of bytes of decoded information.
*
- * Return: The total size of the decoded data elements, in bytes.
+ * Return: The total size of the decoded data elements, in bytes, on success or
+ * negative errno on error.
*/
static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src,
u32 elem_len, u32 elem_size)
@@ -452,7 +524,24 @@ static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src,
u32 i, rc = 0;
for (i = 0; i < elem_len; i++) {
- QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size);
+ switch (elem_size) {
+ case sizeof(u8):
+ QMI_ENCDEC_DECODE_U8(buf_dst, buf_src);
+ break;
+ case sizeof(u16):
+ QMI_ENCDEC_DECODE_U16(buf_dst, buf_src);
+ break;
+ case sizeof(u32):
+ QMI_ENCDEC_DECODE_U32(buf_dst, buf_src);
+ break;
+ case sizeof(u64):
+ QMI_ENCDEC_DECODE_U64(buf_dst, buf_src);
+ break;
+ default:
+ pr_err("%s: Unrecognized element size\n", __func__);
+ return -EINVAL;
+ }
+
rc += elem_size;
}
@@ -544,10 +633,14 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array,
if (string_len_sz == sizeof(u8)) {
rc = qmi_decode_basic_elem(&val8, buf_src,
1, string_len_sz);
+ if (rc < 0)
+ return rc;
string_len = (u32)val8;
} else {
rc = qmi_decode_basic_elem(&val16, buf_src,
1, string_len_sz);
+ if (rc < 0)
+ return rc;
string_len = (u32)val16;
}
decoded_bytes += rc;
@@ -565,6 +658,8 @@ static int qmi_decode_string_elem(const struct qmi_elem_info *ei_array,
rc = qmi_decode_basic_elem(buf_dst, buf_src + decoded_bytes,
string_len, temp_ei->elem_size);
+ if (rc < 0)
+ return rc;
*((char *)buf_dst + string_len) = '\0';
decoded_bytes += rc;
@@ -625,7 +720,6 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct,
int rc;
u8 val8;
u16 val16;
- u32 val32;
while (decoded_bytes < in_buf_len) {
if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI)
@@ -667,14 +761,17 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct,
if (data_len_sz == sizeof(u8)) {
rc = qmi_decode_basic_elem(&val8, buf_src,
1, data_len_sz);
+ if (rc < 0)
+ return rc;
data_len_value = (u32)val8;
} else {
rc = qmi_decode_basic_elem(&val16, buf_src,
1, data_len_sz);
+ if (rc < 0)
+ return rc;
data_len_value = (u32)val16;
}
- val32 = cpu_to_le32(data_len_value);
- memcpy(buf_dst, &val32, sizeof(u32));
+ memcpy(buf_dst, &data_len_value, sizeof(u32));
temp_ei = temp_ei + 1;
buf_dst = out_c_struct + temp_ei->offset;
tlv_len -= data_len_sz;
@@ -701,6 +798,8 @@ static int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct,
rc = qmi_decode_basic_elem(buf_dst, buf_src,
data_len_value,
temp_ei->elem_size);
+ if (rc < 0)
+ return rc;
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
break;
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index fef840b54574..088b2bbee9e6 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -1219,7 +1219,9 @@ static int qcom_smem_probe(struct platform_device *pdev)
smem->item_count = qcom_smem_get_item_count(smem);
break;
case SMEM_GLOBAL_HEAP_VERSION:
- qcom_smem_map_global(smem, size);
+ ret = qcom_smem_map_global(smem, size);
+ if (ret < 0)
+ return ret;
smem->item_count = SMEM_ITEM_COUNT;
break;
default:
diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h
index a55ca771286b..5747bd191bf1 100644
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -66,19 +66,33 @@ int qcom_scm_set_warm_boot_addr(void *entry);
void qcom_scm_cpu_power_down(u32 flags);
int qcom_scm_set_remote_state(u32 state, u32 id);
-struct qcom_scm_pas_metadata {
+struct qcom_scm_pas_context {
+ struct device *dev;
+ u32 pas_id;
+ phys_addr_t mem_phys;
+ size_t mem_size;
void *ptr;
dma_addr_t phys;
ssize_t size;
+ bool use_tzmem;
};
-int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
- struct qcom_scm_pas_metadata *ctx);
-void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx);
-int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size);
-int qcom_scm_pas_auth_and_reset(u32 peripheral);
-int qcom_scm_pas_shutdown(u32 peripheral);
-bool qcom_scm_pas_supported(u32 peripheral);
+struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
+ u32 pas_id,
+ phys_addr_t mem_phys,
+ size_t mem_size);
+int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
+ struct qcom_scm_pas_context *ctx);
+void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx);
+int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size);
+int qcom_scm_pas_auth_and_reset(u32 pas_id);
+int qcom_scm_pas_shutdown(u32 pas_id);
+bool qcom_scm_pas_supported(u32 pas_id);
+struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
+ void *input_rt, size_t input_rt_size,
+ size_t *output_rt_size);
+
+int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx);
int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);
int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);
diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h
index 0287f9182c4d..8243ab3a12a8 100644
--- a/include/linux/soc/qcom/llcc-qcom.h
+++ b/include/linux/soc/qcom/llcc-qcom.h
@@ -74,13 +74,17 @@
#define LLCC_CAMSRTIP 73
#define LLCC_CAMRTRF 74
#define LLCC_CAMSRTRF 75
+#define LLCC_OOBM_NS 81
+#define LLCC_OOBM_S 82
#define LLCC_VIDEO_APV 83
#define LLCC_COMPUTE1 87
#define LLCC_CPUSS_OPP 88
#define LLCC_CPUSSMPAM 89
+#define LLCC_VIDSC_VSP1 91
#define LLCC_CAM_IPE_STROV 92
#define LLCC_CAM_OFE_STROV 93
#define LLCC_CPUSS_HEU 94
+#define LLCC_PCIE_TCU 97
#define LLCC_MDM_PNG_FIXED 100
/**
diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h
index 8ea8230579a2..82372e0db0a1 100644
--- a/include/linux/soc/qcom/mdt_loader.h
+++ b/include/linux/soc/qcom/mdt_loader.h
@@ -10,19 +10,19 @@
struct device;
struct firmware;
-struct qcom_scm_pas_metadata;
+struct qcom_scm_pas_context;
#if IS_ENABLED(CONFIG_QCOM_MDT_LOADER)
ssize_t qcom_mdt_get_size(const struct firmware *fw);
-int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
- const char *fw_name, int pas_id, phys_addr_t mem_phys,
- struct qcom_scm_pas_metadata *pas_metadata_ctx);
int qcom_mdt_load(struct device *dev, const struct firmware *fw,
const char *fw_name, int pas_id, void *mem_region,
phys_addr_t mem_phys, size_t mem_size,
phys_addr_t *reloc_base);
+int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw,
+ const char *firmware, void *mem_region, phys_addr_t *reloc_base);
+
int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw,
const char *fw_name, void *mem_region,
phys_addr_t mem_phys, size_t mem_size,
@@ -37,13 +37,6 @@ static inline ssize_t qcom_mdt_get_size(const struct firmware *fw)
return -ENODEV;
}
-static inline int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw,
- const char *fw_name, int pas_id, phys_addr_t mem_phys,
- struct qcom_scm_pas_metadata *pas_metadata_ctx)
-{
- return -ENODEV;
-}
-
static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw,
const char *fw_name, int pas_id,
void *mem_region, phys_addr_t mem_phys,
@@ -52,6 +45,13 @@ static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw,
return -ENODEV;
}
+static inline int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx,
+ const struct firmware *fw, const char *firmware,
+ void *mem_region, phys_addr_t *reloc_base)
+{
+ return -ENODEV;
+}
+
static inline int qcom_mdt_load_no_init(struct device *dev,
const struct firmware *fw,
const char *fw_name, void *mem_region,
diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h
index 0a4edfe3d96d..f052e241736c 100644
--- a/include/linux/soc/qcom/ubwc.h
+++ b/include/linux/soc/qcom/ubwc.h
@@ -8,6 +8,7 @@
#define __QCOM_UBWC_H__
#include <linux/bits.h>
+#include <linux/printk.h>
#include <linux/types.h>
struct qcom_ubwc_cfg_data {