diff options
| -rw-r--r-- | Documentation/devicetree/bindings/cache/qcom,llcc.yaml | 46 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/crypto/qcom,prng.yaml | 1 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml | 2 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml | 3 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/sram/sram.yaml | 2 | ||||
| -rw-r--r-- | drivers/bus/qcom-ebi2.c | 7 | ||||
| -rw-r--r-- | drivers/clk/qcom/common.c | 2 | ||||
| -rw-r--r-- | drivers/firmware/qcom/qcom_scm.c | 507 | ||||
| -rw-r--r-- | drivers/firmware/qcom/qcom_scm.h | 2 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_q6v5_pas.c | 165 | ||||
| -rw-r--r-- | drivers/soc/qcom/cmd-db.c | 7 | ||||
| -rw-r--r-- | drivers/soc/qcom/llcc-qcom.c | 207 | ||||
| -rw-r--r-- | drivers/soc/qcom/mdt_loader.c | 51 | ||||
| -rw-r--r-- | drivers/soc/qcom/pmic_glink_altmode.c | 188 | ||||
| -rw-r--r-- | drivers/soc/qcom/qmi_encdec.c | 137 | ||||
| -rw-r--r-- | drivers/soc/qcom/smem.c | 4 | ||||
| -rw-r--r-- | include/linux/firmware/qcom/qcom_scm.h | 30 | ||||
| -rw-r--r-- | include/linux/soc/qcom/llcc-qcom.h | 4 | ||||
| -rw-r--r-- | include/linux/soc/qcom/mdt_loader.h | 22 | ||||
| -rw-r--r-- | include/linux/soc/qcom/ubwc.h | 1 |
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 = ¬ify->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 = ¬ify->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 { |
