mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 04:04:43 +01:00
Qualcomm driver updates for v6.20
Support multiple wait queues in the SCM firmware interface and provide discovery of the wait queue interrupt to deal with the cases where bootloader didn't patch the DeviceTree with the IRQ information. Refactor the MDT loader and the SCM driver's peripheral authentication service interface and introduce support for passing a remoteproc resource table to the firmware. The remoteproc patches that uses this and uses this to configure the IOMMU are included here due to bidirectional dependencies. The end result is remoteproc support on the Glymur platform. Enable QSEECOM and thereby UEFI variable access, on the Surface Pro 11. Make the QMI interface endianness aware, to support ath1Xk on big endian machines. Add the Glymur support in LLCC driver. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBd4DzF816k8JZtUlCx85Pw2ZrcUFAmluWgUACgkQCx85Pw2Z rcX0UxAAjouQqOvIdStDcuq25dYbOe9EH+kwJUI5snU9zWcuFmn1+q9Qz6W6611I IYXWIfkyPaSqvngt53/zq/CgtmX9ez8FFK/+IzdaiuCRLVZZheC7nvg2SnQE+Xav qQIyFWtVI3ufT9x1I+27c42sYRbAXa5JHg+yOPMVG8I9ARK7gcgmkQ+1FL6fut0a C6nfwHg/mHt/TQ4iHDyrJ6Ttx9JWAXdXFl8ekOCe+eXTaiaGj3DgrN5EjRzhiilb lbxOXDzJ4CfwjfoT9eeXRe3GA2rTpY+gaIoOEBRcDkjHVJvRMAz3NqbeKgm40f+o 3iBLWWgHlsFBrspSzKWAUr7X84XO4gpm9w61TADUIeoHn6pb2DWN1P7SvrbdZGrr ASz3dQZT9w++5qxqjY9JWubNx3akt5bJLcHd1PrVXeZugUyAmoGMpm7LKgXNTV3q KKpvRwZN9h+Ebf+8y4Y2WUsQn+u/AXLZi2yoYBrZVUVRlsntsfxWT7eF47ROnIMv KoKVkOmglcB61HmUWolYdGEtCTarQlmOBA9wn0DQll64hfjppWX+/VmrleWMqECR 4dngxoj4/d4gXNB8H6BD3nftInqu8P9va82oT6yca/2Ce0WnA37IQ43l5yFpnejm VzCTPpzbKkcNZgAU51o0T2SzmrP6SN/LLq7XpDdz01rcPBCwTic= =8a2h -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmlw9LYACgkQmmx57+YA GNktRA//apmectfvVwEtqzYpPhPcZgGisBMS00YEJx3DbZCS5d628qol765m22sj jr8NinoGfPwSfWA4AoOAMMdylPnSVMlde3tg1yZS4S/4MmyyqP0VbiqiQ0B7nSap MeTfQVOgihhgJKfek66T2AL3xgWpyYGEe1UmkzLmpn0xxg/EGgTTI/N4uIz7s5zR eGrdugP1+4wQ22vOcUPA17IBklOHow8G2ejT8ChMc0yHtfER6yENOxMLKIQjMhps XJta5yjl8HmulmPqmfc84izNGaVst4So8J+rIXx/ZwmeegByKw4oWzZBk1WD0C6x IhGgRZ47oZiTP9i5lh1StxPp3+mynNqOigeNGCCuHOMOKD2U6mqdiWFeYlHW/EGe pVO+LH7c/3vUo2PFuQWAsme5ldaYZS4nhH9WNTyGkoULezl1mnZyLalRYVQGcx/0 vPUqiQflju5dqlu9Cl7PemZ2uItcRFt/ypKyYSgEgecPxzELlmZlop4Kqxv/aeXW Tx0PJWfgCXnu/gi1i9IAZIc869qBu+uyI/XtGMBAr3DcRu9r3xgsJEpd89lNp4bM 0fRM7ZaYqY3pExcQPuHeaQgCfo4tYsSkFbYbrbk/mU0QS80P54fIr4iBIokvQS0h e4tbTWZZ1gd6LAGMSGOvf9dGvhzoXD9kybVfxtKDn/kEPNR3BiQ= =X8K9 -----END PGP SIGNATURE----- Merge tag 'qcom-drivers-for-6.20' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into soc/drivers Qualcomm driver updates for v6.20 Support multiple wait queues in the SCM firmware interface and provide discovery of the wait queue interrupt to deal with the cases where bootloader didn't patch the DeviceTree with the IRQ information. Refactor the MDT loader and the SCM driver's peripheral authentication service interface and introduce support for passing a remoteproc resource table to the firmware. The remoteproc patches that uses this and uses this to configure the IOMMU are included here due to bidirectional dependencies. The end result is remoteproc support on the Glymur platform. Enable QSEECOM and thereby UEFI variable access, on the Surface Pro 11. Make the QMI interface endianness aware, to support ath1Xk on big endian machines. Add the Glymur support in LLCC driver. * tag 'qcom-drivers-for-6.20' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: (33 commits) soc: qcom: preserve CPU endianness for QMI_DATA_LEN soc: qcom: fix QMI encoding/decoding for basic elements soc: qcom: check QMI basic element error codes soc: qcom: ubwc: add missing include remoteproc: qcom: pas: Enable Secure PAS support with IOMMU managed by Linux remoteproc: pas: Extend parse_fw callback to fetch resources via SMC call firmware: qcom_scm: Add qcom_scm_pas_get_rsc_table() to get resource table firmware: qcom_scm: Add SHM bridge handling for PAS when running without QHEE firmware: qcom_scm: Refactor qcom_scm_pas_init_image() firmware: qcom_scm: Add a prep version of auth_and_reset function soc: qcom: mdtloader: Remove qcom_mdt_pas_init() from exported symbols soc: qcom: mdtloader: Add PAS context aware qcom_mdt_pas_load() function remoteproc: pas: Replace metadata context with PAS context structure firmware: qcom_scm: Introduce PAS context allocator helper function firmware: qcom_scm: Rename peripheral as pas_id firmware: qcom_scm: Remove redundant piece of code dt-bindings: remoteproc: qcom,pas: Add iommus property soc: qcom: cmd-db: Use devm_memremap() to fix memory leak in cmd_db_dev_probe soc: qcom: pmic_glink_altmode: Consume TBT3/USB4 mode notifications dt-bindings: qcom,pdc: document the Milos Power Domain Controller ... Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
733f0303c2
20 changed files with 1199 additions and 189 deletions
|
|
@ -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
|
||||
|
|
@ -84,6 +85,47 @@ allOf:
|
|||
items:
|
||||
- const: llcc0_base
|
||||
|
||||
- if:
|
||||
properties:
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ properties:
|
|||
- const: stop-ack
|
||||
- const: shutdown-ack
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
@ -558,16 +571,105 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
|
|||
dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
|
@ -733,20 +984,67 @@ 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 res.result[0] & GENMASK(7, 0);
|
||||
}
|
||||
|
||||
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_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;
|
||||
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
struct completion *wq;
|
||||
|
||||
ret = qcom_scm_assert_valid_wq_ctx(wq_ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
wq = qcom_scm_get_completion(wq_ctx);
|
||||
if (IS_ERR(wq))
|
||||
return PTR_ERR(wq);
|
||||
|
||||
complete(&__scm->waitq_comp);
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_metadata);
|
||||
qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
|
||||
|
||||
unmap_dtb_carveout:
|
||||
if (pas->dtb_pas_id)
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue