Commit ee22fbd7 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'qcom-drivers-for-6.11' of...

Merge tag 'qcom-drivers-for-6.11' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into soc/drivers

Qualcomm driver updates for v6.11

Support for Shared Memory (shm) Bridge is added, which provides a
stricter interface for handling of buffers passed to TrustZone.

The X1Elite platform is added to uefisecapp allow list, to instantiate
the efivars implementation.

A new in-kernel implementation of the pd-mapper (or servreg) service is
introduced, to replace the userspace dependency for USB Type-C and
battery management.

Support for sharing interrupts across multiple bwmon instances is added,
and a refcount imbalance issue is corrected.

The LLCC support for recent platforms is corrected, and SA8775P support
is added.

A new interface is added to SMEM, to expose "feature codes". One example
of the usecase for this is to indicate to the GPU driver which
frequencies are available on the given device.

The interrupt consumer and provider side of SMP2P is updated to provide
more useful names in interrupt stats.

Support for using the mailbox binding and driver for outgoing IPC
interrupt in the SMSM driver is introduced.

socinfo driver learns about SDM670 and IPQ5321, as well as get some
updates to the X1E PMICs.

pmic_glink is bumped to now support managing 3 USB Type-C ports.

* tag 'qcom-drivers-for-6.11' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: (48 commits)
  soc: qcom: smp2p: Use devname for interrupt descriptions
  soc: qcom: smsm: Add missing mailbox dependency to Kconfig
  soc: qcom: add missing pd-mapper dependencies
  soc: qcom: icc-bwmon: Allow for interrupts to be shared across instances
  dt-bindings: interconnect: qcom,msm8998-bwmon: Add X1E80100 BWMON instances
  dt-bindings: interconnect: qcom,msm8998-bwmon: Remove opp-table from the required list
  firmware: qcom: tzmem: export devm_qcom_tzmem_pool_new()
  soc: qcom: add pd-mapper implementation
  soc: qcom: pdr: extract PDR message marshalling data
  soc: qcom: pdr: fix parsing of domains lists
  soc: qcom: pdr: protect locator_addr with the main mutex
  firmware: qcom: scm: clarify the comment in qcom_scm_pas_init_image()
  firmware: qcom: scm: add support for SHM bridge memory carveout
  firmware: qcom: tzmem: enable SHM Bridge support
  firmware: qcom: scm: add support for SHM bridge operations
  firmware: qcom: qseecom: convert to using the TZ allocator
  firmware: qcom: scm: make qcom_scm_qseecom_app_get_id() use the TZ allocator
  firmware: qcom: scm: make qcom_scm_lmh_dcvsh() use the TZ allocator
  firmware: qcom: scm: make qcom_scm_ice_set_key() use the TZ allocator
  firmware: qcom: scm: make qcom_scm_assign_mem() use the TZ allocator
  ...

Link: https://lore.kernel.org/r/20240705034410.13968-1-andersson@kernel.orgSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents e9a316af e49380c1
...@@ -21,6 +21,7 @@ properties: ...@@ -21,6 +21,7 @@ properties:
compatible: compatible:
enum: enum:
- qcom,qdu1000-llcc - qcom,qdu1000-llcc
- qcom,sa8775p-llcc
- qcom,sc7180-llcc - qcom,sc7180-llcc
- qcom,sc7280-llcc - qcom,sc7280-llcc
- qcom,sc8180x-llcc - qcom,sc8180x-llcc
...@@ -80,6 +81,33 @@ allOf: ...@@ -80,6 +81,33 @@ allOf:
- const: llcc0_base - const: llcc0_base
- const: llcc_broadcast_base - const: llcc_broadcast_base
- if:
properties:
compatible:
contains:
enum:
- qcom,sa8775p-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: LLCC broadcast base register region
reg-names:
items:
- const: llcc0_base
- const: llcc1_base
- const: llcc2_base
- const: llcc3_base
- const: llcc4_base
- const: llcc5_base
- const: llcc_broadcast_base
- if: - if:
properties: properties:
compatible: compatible:
...@@ -141,8 +169,31 @@ allOf: ...@@ -141,8 +169,31 @@ allOf:
- qcom,sm8150-llcc - qcom,sm8150-llcc
- qcom,sm8250-llcc - qcom,sm8250-llcc
- qcom,sm8350-llcc - qcom,sm8350-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: LLCC broadcast base register region
reg-names:
items:
- const: llcc0_base
- const: llcc1_base
- const: llcc2_base
- const: llcc3_base
- const: llcc_broadcast_base
- if:
properties:
compatible:
contains:
enum:
- qcom,sm8450-llcc - qcom,sm8450-llcc
- qcom,sm8550-llcc - qcom,sm8550-llcc
- qcom,sm8650-llcc
then: then:
properties: properties:
reg: reg:
...@@ -151,7 +202,8 @@ allOf: ...@@ -151,7 +202,8 @@ allOf:
- description: LLCC1 base register region - description: LLCC1 base register region
- description: LLCC2 base register region - description: LLCC2 base register region
- description: LLCC3 base register region - description: LLCC3 base register region
- description: LLCC broadcast base register region - description: LLCC broadcast OR register region
- description: LLCC broadcast AND register region
reg-names: reg-names:
items: items:
- const: llcc0_base - const: llcc0_base
...@@ -159,6 +211,7 @@ allOf: ...@@ -159,6 +211,7 @@ allOf:
- const: llcc2_base - const: llcc2_base
- const: llcc3_base - const: llcc3_base
- const: llcc_broadcast_base - const: llcc_broadcast_base
- const: llcc_broadcast_and_base
additionalProperties: false additionalProperties: false
......
...@@ -93,6 +93,11 @@ properties: ...@@ -93,6 +93,11 @@ properties:
protocol to handle sleeping SCM calls. protocol to handle sleeping SCM calls.
maxItems: 1 maxItems: 1
memory-region:
description:
Phandle to the memory region reserved for the shared memory bridge to TZ.
maxItems: 1
qcom,sdi-enabled: qcom,sdi-enabled:
description: description:
Indicates that the SDI (Secure Debug Image) has been enabled by TZ Indicates that the SDI (Secure Debug Image) has been enabled by TZ
...@@ -193,6 +198,16 @@ allOf: ...@@ -193,6 +198,16 @@ allOf:
then: then:
properties: properties:
interrupts: false interrupts: false
- if:
not:
properties:
compatible:
contains:
enum:
- qcom,scm-sa8775p
then:
properties:
memory-region: false
required: required:
- compatible - compatible
......
...@@ -35,6 +35,7 @@ properties: ...@@ -35,6 +35,7 @@ properties:
- qcom,sm8250-cpu-bwmon - qcom,sm8250-cpu-bwmon
- qcom,sm8550-cpu-bwmon - qcom,sm8550-cpu-bwmon
- qcom,sm8650-cpu-bwmon - qcom,sm8650-cpu-bwmon
- qcom,x1e80100-cpu-bwmon
- const: qcom,sdm845-bwmon # BWMON v4, unified register space - const: qcom,sdm845-bwmon # BWMON v4, unified register space
- items: - items:
- enum: - enum:
...@@ -44,6 +45,7 @@ properties: ...@@ -44,6 +45,7 @@ properties:
- qcom,sm8250-llcc-bwmon - qcom,sm8250-llcc-bwmon
- qcom,sm8550-llcc-bwmon - qcom,sm8550-llcc-bwmon
- qcom,sm8650-llcc-bwmon - qcom,sm8650-llcc-bwmon
- qcom,x1e80100-llcc-bwmon
- const: qcom,sc7280-llcc-bwmon - const: qcom,sc7280-llcc-bwmon
- const: qcom,sc7280-llcc-bwmon # BWMON v5 - const: qcom,sc7280-llcc-bwmon # BWMON v5
- const: qcom,sdm845-llcc-bwmon # BWMON v5 - const: qcom,sdm845-llcc-bwmon # BWMON v5
...@@ -72,7 +74,6 @@ required: ...@@ -72,7 +74,6 @@ required:
- interconnects - interconnects
- interrupts - interrupts
- operating-points-v2 - operating-points-v2
- opp-table
- reg - reg
additionalProperties: false additionalProperties: false
......
...@@ -31,6 +31,7 @@ properties: ...@@ -31,6 +31,7 @@ properties:
- qcom,sc7280-aoss-qmp - qcom,sc7280-aoss-qmp
- qcom,sc8180x-aoss-qmp - qcom,sc8180x-aoss-qmp
- qcom,sc8280xp-aoss-qmp - qcom,sc8280xp-aoss-qmp
- qcom,sdx75-aoss-qmp
- qcom,sdm845-aoss-qmp - qcom,sdm845-aoss-qmp
- qcom,sm6350-aoss-qmp - qcom,sm6350-aoss-qmp
- qcom,sm8150-aoss-qmp - qcom,sm8150-aoss-qmp
......
...@@ -41,6 +41,7 @@ properties: ...@@ -41,6 +41,7 @@ properties:
description: description:
Three entries specifying the outgoing ipc bit used for signaling the Three entries specifying the outgoing ipc bit used for signaling the
remote end of the smp2p edge. remote end of the smp2p edge.
deprecated: true
qcom,local-pid: qcom,local-pid:
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint32
...@@ -128,7 +129,7 @@ examples: ...@@ -128,7 +129,7 @@ examples:
compatible = "qcom,smp2p"; compatible = "qcom,smp2p";
qcom,smem = <431>, <451>; qcom,smem = <431>, <451>;
interrupts = <GIC_SPI 143 IRQ_TYPE_EDGE_RISING>; interrupts = <GIC_SPI 143 IRQ_TYPE_EDGE_RISING>;
qcom,ipc = <&apcs 8 18>; mboxes = <&apcs 18>;
qcom,local-pid = <0>; qcom,local-pid = <0>;
qcom,remote-pid = <4>; qcom,remote-pid = <4>;
......
...@@ -33,6 +33,14 @@ properties: ...@@ -33,6 +33,14 @@ properties:
specifier of the column in the subscription matrix representing the local specifier of the column in the subscription matrix representing the local
processor. processor.
mboxes:
minItems: 1
maxItems: 5
description:
Reference to the mailbox representing the outgoing doorbell in APCS for
this client. Each entry represents the N:th remote processor by index
(0-indexed).
'#size-cells': '#size-cells':
const: 0 const: 0
...@@ -47,6 +55,7 @@ patternProperties: ...@@ -47,6 +55,7 @@ patternProperties:
description: description:
Three entries specifying the outgoing ipc bit used for signaling the N:th Three entries specifying the outgoing ipc bit used for signaling the N:th
remote processor. remote processor.
deprecated: true
"@[0-9a-f]$": "@[0-9a-f]$":
type: object type: object
...@@ -98,7 +107,10 @@ required: ...@@ -98,7 +107,10 @@ required:
- '#address-cells' - '#address-cells'
- '#size-cells' - '#size-cells'
anyOf: oneOf:
- required:
- mboxes
- anyOf:
- required: - required:
- qcom,ipc-1 - qcom,ipc-1
- required: - required:
...@@ -122,7 +134,7 @@ examples: ...@@ -122,7 +134,7 @@ examples:
compatible = "qcom,smsm"; compatible = "qcom,smsm";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
qcom,ipc-3 = <&apcs 8 19>; mboxes = <0>, <0>, <0>, <&apcs 19>;
apps_smsm: apps@0 { apps_smsm: apps@0 {
reg = <0>; reg = <0>;
......
...@@ -18607,6 +18607,14 @@ F: Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst ...@@ -18607,6 +18607,14 @@ F: Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst
F: drivers/net/ethernet/qualcomm/rmnet/ F: drivers/net/ethernet/qualcomm/rmnet/
F: include/linux/if_rmnet.h F: include/linux/if_rmnet.h
QUALCOMM TRUST ZONE MEMORY ALLOCATOR
M: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: drivers/firmware/qcom/qcom_tzmem.c
F: drivers/firmware/qcom/qcom_tzmem.h
F: include/linux/firmware/qcom/qcom_tzmem.h
QUALCOMM TSENS THERMAL DRIVER QUALCOMM TSENS THERMAL DRIVER
M: Amit Kucheria <amitk@kernel.org> M: Amit Kucheria <amitk@kernel.org>
M: Thara Gopinath <thara.gopinath@gmail.com> M: Thara Gopinath <thara.gopinath@gmail.com>
......
...@@ -191,6 +191,7 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, ...@@ -191,6 +191,7 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
case QCOM_ID_IPQ5312: case QCOM_ID_IPQ5312:
case QCOM_ID_IPQ5302: case QCOM_ID_IPQ5302:
case QCOM_ID_IPQ5300: case QCOM_ID_IPQ5300:
case QCOM_ID_IPQ5321:
case QCOM_ID_IPQ9514: case QCOM_ID_IPQ9514:
case QCOM_ID_IPQ9550: case QCOM_ID_IPQ9550:
case QCOM_ID_IPQ9554: case QCOM_ID_IPQ9554:
......
...@@ -7,8 +7,39 @@ ...@@ -7,8 +7,39 @@
menu "Qualcomm firmware drivers" menu "Qualcomm firmware drivers"
config QCOM_SCM config QCOM_SCM
select QCOM_TZMEM
tristate tristate
config QCOM_TZMEM
tristate
select GENERIC_ALLOCATOR
choice
prompt "TrustZone interface memory allocator mode"
default QCOM_TZMEM_MODE_GENERIC
help
Selects the mode of the memory allocator providing memory buffers of
suitable format for sharing with the TrustZone. If in doubt, select
'Generic'.
config QCOM_TZMEM_MODE_GENERIC
bool "Generic"
help
Use the generic allocator mode. The memory is page-aligned, non-cachable
and physically contiguous.
config QCOM_TZMEM_MODE_SHMBRIDGE
bool "SHM Bridge"
help
Use Qualcomm Shared Memory Bridge. The memory has the same alignment as
in the 'Generic' allocator but is also explicitly marked as an SHM Bridge
buffer.
With this selected, all buffers passed to the TrustZone must be allocated
using the TZMem allocator or else the TrustZone will refuse to use them.
endchoice
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
bool "Qualcomm download mode enabled by default" bool "Qualcomm download mode enabled by default"
depends on QCOM_SCM depends on QCOM_SCM
......
...@@ -5,5 +5,6 @@ ...@@ -5,5 +5,6 @@
obj-$(CONFIG_QCOM_SCM) += qcom-scm.o obj-$(CONFIG_QCOM_SCM) += qcom-scm.o
qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o
obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o
obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o
...@@ -13,11 +13,14 @@ ...@@ -13,11 +13,14 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/ucs2_string.h> #include <linux/ucs2_string.h>
#include <linux/firmware/qcom/qcom_qseecom.h> #include <linux/firmware/qcom/qcom_qseecom.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/firmware/qcom/qcom_tzmem.h>
/* -- Qualcomm "uefisecapp" interface definitions. -------------------------- */ /* -- Qualcomm "uefisecapp" interface definitions. -------------------------- */
...@@ -272,6 +275,7 @@ struct qsee_rsp_uefi_query_variable_info { ...@@ -272,6 +275,7 @@ struct qsee_rsp_uefi_query_variable_info {
struct qcuefi_client { struct qcuefi_client {
struct qseecom_client *client; struct qseecom_client *client;
struct efivars efivars; struct efivars efivars;
struct qcom_tzmem_pool *mempool;
}; };
static struct device *qcuefi_dev(struct qcuefi_client *qcuefi) static struct device *qcuefi_dev(struct qcuefi_client *qcuefi)
...@@ -293,12 +297,11 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e ...@@ -293,12 +297,11 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
{ {
struct qsee_req_uefi_get_variable *req_data; struct qsee_req_uefi_get_variable *req_data;
struct qsee_rsp_uefi_get_variable *rsp_data; struct qsee_rsp_uefi_get_variable *rsp_data;
void *cmd_buf __free(qcom_tzmem) = NULL;
unsigned long buffer_size = *data_size; unsigned long buffer_size = *data_size;
efi_status_t efi_status = EFI_SUCCESS;
unsigned long name_length; unsigned long name_length;
dma_addr_t cmd_buf_dma; efi_status_t efi_status;
size_t cmd_buf_size; size_t cmd_buf_size;
void *cmd_buf;
size_t guid_offs; size_t guid_offs;
size_t name_offs; size_t name_offs;
size_t req_size; size_t req_size;
...@@ -333,11 +336,9 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e ...@@ -333,11 +336,9 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
__reqdata_offs(rsp_size, &rsp_offs) __reqdata_offs(rsp_size, &rsp_offs)
); );
cmd_buf = qseecom_dma_alloc(qcuefi->client, cmd_buf_size, &cmd_buf_dma, GFP_KERNEL); cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);
if (!cmd_buf) { if (!cmd_buf)
efi_status = EFI_OUT_OF_RESOURCES; return EFI_OUT_OF_RESOURCES;
goto out;
}
req_data = cmd_buf + req_offs; req_data = cmd_buf + req_offs;
rsp_data = cmd_buf + rsp_offs; rsp_data = cmd_buf + rsp_offs;
...@@ -351,30 +352,22 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e ...@@ -351,30 +352,22 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
req_data->length = req_size; req_data->length = req_size;
status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length); status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);
if (status < 0) { if (status < 0)
efi_status = EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
goto out_free;
}
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
status = qcom_qseecom_app_send(qcuefi->client, status = qcom_qseecom_app_send(qcuefi->client,
cmd_buf_dma + req_offs, req_size, cmd_buf + req_offs, req_size,
cmd_buf_dma + rsp_offs, rsp_size); cmd_buf + rsp_offs, rsp_size);
if (status) { if (status)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->command_id != QSEE_CMD_UEFI_GET_VARIABLE) { if (rsp_data->command_id != QSEE_CMD_UEFI_GET_VARIABLE)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->length < sizeof(*rsp_data)) { if (rsp_data->length < sizeof(*rsp_data))
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->status) { if (rsp_data->status) {
dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
...@@ -388,18 +381,14 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e ...@@ -388,18 +381,14 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
*attributes = rsp_data->attributes; *attributes = rsp_data->attributes;
} }
goto out_free; return qsee_uefi_status_to_efi(rsp_data->status);
} }
if (rsp_data->length > rsp_size) { if (rsp_data->length > rsp_size)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->data_offset + rsp_data->data_size > rsp_data->length) { if (rsp_data->data_offset + rsp_data->data_size > rsp_data->length)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
/* /*
* Note: We need to set attributes and data size even if the buffer is * Note: We need to set attributes and data size even if the buffer is
...@@ -422,22 +411,15 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e ...@@ -422,22 +411,15 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
if (attributes) if (attributes)
*attributes = rsp_data->attributes; *attributes = rsp_data->attributes;
if (buffer_size == 0 && !data) { if (buffer_size == 0 && !data)
efi_status = EFI_SUCCESS; return EFI_SUCCESS;
goto out_free;
}
if (buffer_size < rsp_data->data_size) { if (buffer_size < rsp_data->data_size)
efi_status = EFI_BUFFER_TOO_SMALL; return EFI_BUFFER_TOO_SMALL;
goto out_free;
}
memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size); memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size);
out_free: return EFI_SUCCESS;
qseecom_dma_free(qcuefi->client, cmd_buf_size, cmd_buf, cmd_buf_dma);
out:
return efi_status;
} }
static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name, static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name,
...@@ -446,11 +428,9 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e ...@@ -446,11 +428,9 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e
{ {
struct qsee_req_uefi_set_variable *req_data; struct qsee_req_uefi_set_variable *req_data;
struct qsee_rsp_uefi_set_variable *rsp_data; struct qsee_rsp_uefi_set_variable *rsp_data;
efi_status_t efi_status = EFI_SUCCESS; void *cmd_buf __free(qcom_tzmem) = NULL;
unsigned long name_length; unsigned long name_length;
dma_addr_t cmd_buf_dma;
size_t cmd_buf_size; size_t cmd_buf_size;
void *cmd_buf;
size_t name_offs; size_t name_offs;
size_t guid_offs; size_t guid_offs;
size_t data_offs; size_t data_offs;
...@@ -486,11 +466,9 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e ...@@ -486,11 +466,9 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e
__reqdata_offs(sizeof(*rsp_data), &rsp_offs) __reqdata_offs(sizeof(*rsp_data), &rsp_offs)
); );
cmd_buf = qseecom_dma_alloc(qcuefi->client, cmd_buf_size, &cmd_buf_dma, GFP_KERNEL); cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);
if (!cmd_buf) { if (!cmd_buf)
efi_status = EFI_OUT_OF_RESOURCES; return EFI_OUT_OF_RESOURCES;
goto out;
}
req_data = cmd_buf + req_offs; req_data = cmd_buf + req_offs;
rsp_data = cmd_buf + rsp_offs; rsp_data = cmd_buf + rsp_offs;
...@@ -506,10 +484,8 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e ...@@ -506,10 +484,8 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e
req_data->length = req_size; req_data->length = req_size;
status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length); status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);
if (status < 0) { if (status < 0)
efi_status = EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
goto out_free;
}
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
...@@ -517,33 +493,24 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e ...@@ -517,33 +493,24 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e
memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size); memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size);
status = qcom_qseecom_app_send(qcuefi->client, status = qcom_qseecom_app_send(qcuefi->client,
cmd_buf_dma + req_offs, req_size, cmd_buf + req_offs, req_size,
cmd_buf_dma + rsp_offs, sizeof(*rsp_data)); cmd_buf + rsp_offs, sizeof(*rsp_data));
if (status) { if (status)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->command_id != QSEE_CMD_UEFI_SET_VARIABLE) { if (rsp_data->command_id != QSEE_CMD_UEFI_SET_VARIABLE)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->length != sizeof(*rsp_data)) { if (rsp_data->length != sizeof(*rsp_data))
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->status) { if (rsp_data->status) {
dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
__func__, rsp_data->status); __func__, rsp_data->status);
efi_status = qsee_uefi_status_to_efi(rsp_data->status); return qsee_uefi_status_to_efi(rsp_data->status);
} }
out_free: return EFI_SUCCESS;
qseecom_dma_free(qcuefi->client, cmd_buf_size, cmd_buf, cmd_buf_dma);
out:
return efi_status;
} }
static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi, static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
...@@ -552,10 +519,9 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi, ...@@ -552,10 +519,9 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
{ {
struct qsee_req_uefi_get_next_variable *req_data; struct qsee_req_uefi_get_next_variable *req_data;
struct qsee_rsp_uefi_get_next_variable *rsp_data; struct qsee_rsp_uefi_get_next_variable *rsp_data;
efi_status_t efi_status = EFI_SUCCESS; void *cmd_buf __free(qcom_tzmem) = NULL;
dma_addr_t cmd_buf_dma; efi_status_t efi_status;
size_t cmd_buf_size; size_t cmd_buf_size;
void *cmd_buf;
size_t guid_offs; size_t guid_offs;
size_t name_offs; size_t name_offs;
size_t req_size; size_t req_size;
...@@ -587,11 +553,9 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi, ...@@ -587,11 +553,9 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
__reqdata_offs(rsp_size, &rsp_offs) __reqdata_offs(rsp_size, &rsp_offs)
); );
cmd_buf = qseecom_dma_alloc(qcuefi->client, cmd_buf_size, &cmd_buf_dma, GFP_KERNEL); cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);
if (!cmd_buf) { if (!cmd_buf)
efi_status = EFI_OUT_OF_RESOURCES; return EFI_OUT_OF_RESOURCES;
goto out;
}
req_data = cmd_buf + req_offs; req_data = cmd_buf + req_offs;
rsp_data = cmd_buf + rsp_offs; rsp_data = cmd_buf + rsp_offs;
...@@ -606,28 +570,20 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi, ...@@ -606,28 +570,20 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size); memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name,
*name_size / sizeof(*name)); *name_size / sizeof(*name));
if (status < 0) { if (status < 0)
efi_status = EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
goto out_free;
}
status = qcom_qseecom_app_send(qcuefi->client, status = qcom_qseecom_app_send(qcuefi->client,
cmd_buf_dma + req_offs, req_size, cmd_buf + req_offs, req_size,
cmd_buf_dma + rsp_offs, rsp_size); cmd_buf + rsp_offs, rsp_size);
if (status) { if (status)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->command_id != QSEE_CMD_UEFI_GET_NEXT_VARIABLE) { if (rsp_data->command_id != QSEE_CMD_UEFI_GET_NEXT_VARIABLE)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->length < sizeof(*rsp_data)) { if (rsp_data->length < sizeof(*rsp_data))
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->status) { if (rsp_data->status) {
dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
...@@ -642,53 +598,40 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi, ...@@ -642,53 +598,40 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
if (efi_status == EFI_BUFFER_TOO_SMALL) if (efi_status == EFI_BUFFER_TOO_SMALL)
*name_size = rsp_data->name_size; *name_size = rsp_data->name_size;
goto out_free; return efi_status;
} }
if (rsp_data->length > rsp_size) { if (rsp_data->length > rsp_size)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->name_offset + rsp_data->name_size > rsp_data->length) { if (rsp_data->name_offset + rsp_data->name_size > rsp_data->length)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->guid_offset + rsp_data->guid_size > rsp_data->length) { if (rsp_data->guid_offset + rsp_data->guid_size > rsp_data->length)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->name_size > *name_size) { if (rsp_data->name_size > *name_size) {
*name_size = rsp_data->name_size; *name_size = rsp_data->name_size;
efi_status = EFI_BUFFER_TOO_SMALL; return EFI_BUFFER_TOO_SMALL;
goto out_free;
} }
if (rsp_data->guid_size != sizeof(*guid)) { if (rsp_data->guid_size != sizeof(*guid))
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
memcpy(guid, ((void *)rsp_data) + rsp_data->guid_offset, rsp_data->guid_size); memcpy(guid, ((void *)rsp_data) + rsp_data->guid_offset, rsp_data->guid_size);
status = ucs2_strscpy(name, ((void *)rsp_data) + rsp_data->name_offset, status = ucs2_strscpy(name, ((void *)rsp_data) + rsp_data->name_offset,
rsp_data->name_size / sizeof(*name)); rsp_data->name_size / sizeof(*name));
*name_size = rsp_data->name_size; *name_size = rsp_data->name_size;
if (status < 0) { if (status < 0)
/* /*
* Return EFI_DEVICE_ERROR here because the buffer size should * Return EFI_DEVICE_ERROR here because the buffer size should
* have already been validated above, causing this function to * have already been validated above, causing this function to
* bail with EFI_BUFFER_TOO_SMALL. * bail with EFI_BUFFER_TOO_SMALL.
*/ */
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
}
out_free: return EFI_SUCCESS;
qseecom_dma_free(qcuefi->client, cmd_buf_size, cmd_buf, cmd_buf_dma);
out:
return efi_status;
} }
static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, u32 attr, static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, u32 attr,
...@@ -697,10 +640,8 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, ...@@ -697,10 +640,8 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi,
{ {
struct qsee_req_uefi_query_variable_info *req_data; struct qsee_req_uefi_query_variable_info *req_data;
struct qsee_rsp_uefi_query_variable_info *rsp_data; struct qsee_rsp_uefi_query_variable_info *rsp_data;
efi_status_t efi_status = EFI_SUCCESS; void *cmd_buf __free(qcom_tzmem) = NULL;
dma_addr_t cmd_buf_dma;
size_t cmd_buf_size; size_t cmd_buf_size;
void *cmd_buf;
size_t req_offs; size_t req_offs;
size_t rsp_offs; size_t rsp_offs;
int status; int status;
...@@ -710,11 +651,9 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, ...@@ -710,11 +651,9 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi,
__reqdata_offs(sizeof(*rsp_data), &rsp_offs) __reqdata_offs(sizeof(*rsp_data), &rsp_offs)
); );
cmd_buf = qseecom_dma_alloc(qcuefi->client, cmd_buf_size, &cmd_buf_dma, GFP_KERNEL); cmd_buf = qcom_tzmem_alloc(qcuefi->mempool, cmd_buf_size, GFP_KERNEL);
if (!cmd_buf) { if (!cmd_buf)
efi_status = EFI_OUT_OF_RESOURCES; return EFI_OUT_OF_RESOURCES;
goto out;
}
req_data = cmd_buf + req_offs; req_data = cmd_buf + req_offs;
rsp_data = cmd_buf + rsp_offs; rsp_data = cmd_buf + rsp_offs;
...@@ -724,28 +663,21 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, ...@@ -724,28 +663,21 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi,
req_data->length = sizeof(*req_data); req_data->length = sizeof(*req_data);
status = qcom_qseecom_app_send(qcuefi->client, status = qcom_qseecom_app_send(qcuefi->client,
cmd_buf_dma + req_offs, sizeof(*req_data), cmd_buf + req_offs, sizeof(*req_data),
cmd_buf_dma + rsp_offs, sizeof(*rsp_data)); cmd_buf + rsp_offs, sizeof(*rsp_data));
if (status) { if (status)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->command_id != QSEE_CMD_UEFI_QUERY_VARIABLE_INFO) { if (rsp_data->command_id != QSEE_CMD_UEFI_QUERY_VARIABLE_INFO)
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->length != sizeof(*rsp_data)) { if (rsp_data->length != sizeof(*rsp_data))
efi_status = EFI_DEVICE_ERROR; return EFI_DEVICE_ERROR;
goto out_free;
}
if (rsp_data->status) { if (rsp_data->status) {
dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n", dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
__func__, rsp_data->status); __func__, rsp_data->status);
efi_status = qsee_uefi_status_to_efi(rsp_data->status); return qsee_uefi_status_to_efi(rsp_data->status);
goto out_free;
} }
if (storage_space) if (storage_space)
...@@ -757,10 +689,7 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, ...@@ -757,10 +689,7 @@ static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi,
if (max_variable_size) if (max_variable_size)
*max_variable_size = rsp_data->max_variable_size; *max_variable_size = rsp_data->max_variable_size;
out_free: return EFI_SUCCESS;
qseecom_dma_free(qcuefi->client, cmd_buf_size, cmd_buf, cmd_buf_dma);
out:
return efi_status;
} }
/* -- Global efivar interface. ---------------------------------------------- */ /* -- Global efivar interface. ---------------------------------------------- */
...@@ -871,6 +800,7 @@ static const struct efivar_operations qcom_efivar_ops = { ...@@ -871,6 +800,7 @@ static const struct efivar_operations qcom_efivar_ops = {
static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev, static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev,
const struct auxiliary_device_id *aux_dev_id) const struct auxiliary_device_id *aux_dev_id)
{ {
struct qcom_tzmem_pool_config pool_config;
struct qcuefi_client *qcuefi; struct qcuefi_client *qcuefi;
int status; int status;
...@@ -889,6 +819,16 @@ static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev, ...@@ -889,6 +819,16 @@ static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev,
if (status) if (status)
qcuefi_set_reference(NULL); qcuefi_set_reference(NULL);
memset(&pool_config, 0, sizeof(pool_config));
pool_config.initial_size = SZ_4K;
pool_config.policy = QCOM_TZMEM_POLICY_MULTIPLIER;
pool_config.increment = 2;
pool_config.max_size = SZ_256K;
qcuefi->mempool = devm_qcom_tzmem_pool_new(&aux_dev->dev, &pool_config);
if (IS_ERR(qcuefi->mempool))
return PTR_ERR(qcuefi->mempool);
return status; return status;
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved. /* Copyright (c) 2015,2019 The Linux Foundation. All rights reserved.
*/ */
#include <linux/cleanup.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -9,6 +10,7 @@ ...@@ -9,6 +10,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/firmware/qcom/qcom_scm.h> #include <linux/firmware/qcom/qcom_scm.h>
#include <linux/firmware/qcom/qcom_tzmem.h>
#include <linux/arm-smccc.h> #include <linux/arm-smccc.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
...@@ -150,11 +152,10 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, ...@@ -150,11 +152,10 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
enum qcom_scm_convention qcom_convention, enum qcom_scm_convention qcom_convention,
struct qcom_scm_res *res, bool atomic) struct qcom_scm_res *res, bool atomic)
{ {
struct qcom_tzmem_pool *mempool = qcom_scm_get_tzmem_pool();
int arglen = desc->arginfo & 0xf; int arglen = desc->arginfo & 0xf;
int i, ret; int i, ret;
dma_addr_t args_phys = 0; void *args_virt __free(qcom_tzmem) = NULL;
void *args_virt = NULL;
size_t alloc_len;
gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL; u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
u32 qcom_smccc_convention = (qcom_convention == SMC_CONVENTION_ARM_32) ? u32 qcom_smccc_convention = (qcom_convention == SMC_CONVENTION_ARM_32) ?
...@@ -172,9 +173,9 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, ...@@ -172,9 +173,9 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i]; smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i];
if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) { if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) {
alloc_len = SCM_SMC_N_EXT_ARGS * sizeof(u64); args_virt = qcom_tzmem_alloc(mempool,
args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); SCM_SMC_N_EXT_ARGS * sizeof(u64),
flag);
if (!args_virt) if (!args_virt)
return -ENOMEM; return -ENOMEM;
...@@ -192,25 +193,10 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, ...@@ -192,25 +193,10 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
SCM_SMC_FIRST_EXT_IDX]); SCM_SMC_FIRST_EXT_IDX]);
} }
args_phys = dma_map_single(dev, args_virt, alloc_len, smc.args[SCM_SMC_LAST_REG_IDX] = qcom_tzmem_to_phys(args_virt);
DMA_TO_DEVICE);
if (dma_mapping_error(dev, args_phys)) {
kfree(args_virt);
return -ENOMEM;
}
smc.args[SCM_SMC_LAST_REG_IDX] = args_phys;
} }
/* ret error check follows after args_virt cleanup*/
ret = __scm_smc_do(dev, &smc, &smc_res, atomic); ret = __scm_smc_do(dev, &smc, &smc_res, atomic);
if (args_virt) {
dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
kfree(args_virt);
}
if (ret) if (ret)
return ret; return ret;
......
...@@ -6,12 +6,15 @@ ...@@ -6,12 +6,15 @@
#include <linux/arm-smccc.h> #include <linux/arm-smccc.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/firmware/qcom/qcom_scm.h> #include <linux/firmware/qcom/qcom_scm.h>
#include <linux/firmware/qcom/qcom_tzmem.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interconnect.h> #include <linux/interconnect.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -20,11 +23,14 @@ ...@@ -20,11 +23,14 @@
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
#include <linux/sizes.h>
#include <linux/types.h> #include <linux/types.h>
#include "qcom_scm.h" #include "qcom_scm.h"
#include "qcom_tzmem.h"
static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT); static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT);
module_param(download_mode, bool, 0); module_param(download_mode, bool, 0);
...@@ -43,6 +49,8 @@ struct qcom_scm { ...@@ -43,6 +49,8 @@ struct qcom_scm {
int scm_vote_count; int scm_vote_count;
u64 dload_mode_addr; u64 dload_mode_addr;
struct qcom_tzmem_pool *mempool;
}; };
struct qcom_scm_current_perm_info { struct qcom_scm_current_perm_info {
...@@ -114,7 +122,6 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = { ...@@ -114,7 +122,6 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
}; };
#define QCOM_SMC_WAITQ_FLAG_WAKE_ONE BIT(0) #define QCOM_SMC_WAITQ_FLAG_WAKE_ONE BIT(0)
#define QCOM_SMC_WAITQ_FLAG_WAKE_ALL BIT(1)
#define QCOM_DLOAD_MASK GENMASK(5, 4) #define QCOM_DLOAD_MASK GENMASK(5, 4)
#define QCOM_DLOAD_NODUMP 0 #define QCOM_DLOAD_NODUMP 0
...@@ -198,6 +205,11 @@ static void qcom_scm_bw_disable(void) ...@@ -198,6 +205,11 @@ static void qcom_scm_bw_disable(void)
enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN; enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN;
static DEFINE_SPINLOCK(scm_query_lock); static DEFINE_SPINLOCK(scm_query_lock);
struct qcom_tzmem_pool *qcom_scm_get_tzmem_pool(void)
{
return __scm->mempool;
}
static enum qcom_scm_convention __get_convention(void) static enum qcom_scm_convention __get_convention(void)
{ {
unsigned long flags; unsigned long flags;
...@@ -570,6 +582,13 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, ...@@ -570,6 +582,13 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size,
* During the scm call memory protection will be enabled for the meta * During the scm call memory protection will be enabled for the meta
* data blob, so make sure it's physically contiguous, 4K aligned and * data blob, so make sure it's physically contiguous, 4K aligned and
* non-cachable to avoid XPU violations. * non-cachable to avoid XPU violations.
*
* For PIL calls the hypervisor creates SHM Bridges for the blob
* buffers on behalf of Linux so we must not do it ourselves hence
* not using the TZMem allocator here.
*
* If we pass a buffer that is already part of an SHM Bridge to this
* call, it will fail.
*/ */
mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
GFP_KERNEL); GFP_KERNEL);
...@@ -1008,14 +1027,13 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, ...@@ -1008,14 +1027,13 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
struct qcom_scm_mem_map_info *mem_to_map; struct qcom_scm_mem_map_info *mem_to_map;
phys_addr_t mem_to_map_phys; phys_addr_t mem_to_map_phys;
phys_addr_t dest_phys; phys_addr_t dest_phys;
dma_addr_t ptr_phys; phys_addr_t ptr_phys;
size_t mem_to_map_sz; size_t mem_to_map_sz;
size_t dest_sz; size_t dest_sz;
size_t src_sz; size_t src_sz;
size_t ptr_sz; size_t ptr_sz;
int next_vm; int next_vm;
__le32 *src; __le32 *src;
void *ptr;
int ret, i, b; int ret, i, b;
u64 srcvm_bits = *srcvm; u64 srcvm_bits = *srcvm;
...@@ -1025,10 +1043,13 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, ...@@ -1025,10 +1043,13 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) +
ALIGN(dest_sz, SZ_64); ALIGN(dest_sz, SZ_64);
ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); void *ptr __free(qcom_tzmem) = qcom_tzmem_alloc(__scm->mempool,
ptr_sz, GFP_KERNEL);
if (!ptr) if (!ptr)
return -ENOMEM; return -ENOMEM;
ptr_phys = qcom_tzmem_to_phys(ptr);
/* Fill source vmid detail */ /* Fill source vmid detail */
src = ptr; src = ptr;
i = 0; i = 0;
...@@ -1057,7 +1078,6 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, ...@@ -1057,7 +1078,6 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz,
ptr_phys, src_sz, dest_phys, dest_sz); ptr_phys, src_sz, dest_phys, dest_sz);
dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_phys);
if (ret) { if (ret) {
dev_err(__scm->dev, dev_err(__scm->dev,
"Assign memory protection call failed %d\n", ret); "Assign memory protection call failed %d\n", ret);
...@@ -1205,32 +1225,21 @@ int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, ...@@ -1205,32 +1225,21 @@ int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size,
.args[4] = data_unit_size, .args[4] = data_unit_size,
.owner = ARM_SMCCC_OWNER_SIP, .owner = ARM_SMCCC_OWNER_SIP,
}; };
void *keybuf;
dma_addr_t key_phys;
int ret;
/* int ret;
* 'key' may point to vmalloc()'ed memory, but we need to pass a
* physical address that's been properly flushed. The sanctioned way to
* do this is by using the DMA API. But as is best practice for crypto
* keys, we also must wipe the key after use. This makes kmemdup() +
* dma_map_single() not clearly correct, since the DMA API can use
* bounce buffers. Instead, just use dma_alloc_coherent(). Programming
* keys is normally rare and thus not performance-critical.
*/
keybuf = dma_alloc_coherent(__scm->dev, key_size, &key_phys, void *keybuf __free(qcom_tzmem) = qcom_tzmem_alloc(__scm->mempool,
key_size,
GFP_KERNEL); GFP_KERNEL);
if (!keybuf) if (!keybuf)
return -ENOMEM; return -ENOMEM;
memcpy(keybuf, key, key_size); memcpy(keybuf, key, key_size);
desc.args[1] = key_phys; desc.args[1] = qcom_tzmem_to_phys(keybuf);
ret = qcom_scm_call(__scm->dev, &desc, NULL); ret = qcom_scm_call(__scm->dev, &desc, NULL);
memzero_explicit(keybuf, key_size); memzero_explicit(keybuf, key_size);
dma_free_coherent(__scm->dev, key_size, keybuf, key_phys);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key); EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key);
...@@ -1342,6 +1351,66 @@ bool qcom_scm_lmh_dcvsh_available(void) ...@@ -1342,6 +1351,66 @@ bool qcom_scm_lmh_dcvsh_available(void)
} }
EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available); EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available);
int qcom_scm_shm_bridge_enable(void)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_MP,
.cmd = QCOM_SCM_MP_SHM_BRIDGE_ENABLE,
.owner = ARM_SMCCC_OWNER_SIP
};
struct qcom_scm_res res;
if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP,
QCOM_SCM_MP_SHM_BRIDGE_ENABLE))
return -EOPNOTSUPP;
return qcom_scm_call(__scm->dev, &desc, &res) ?: res.result[0];
}
EXPORT_SYMBOL_GPL(qcom_scm_shm_bridge_enable);
int qcom_scm_shm_bridge_create(struct device *dev, u64 pfn_and_ns_perm_flags,
u64 ipfn_and_s_perm_flags, u64 size_and_flags,
u64 ns_vmids, u64 *handle)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_MP,
.cmd = QCOM_SCM_MP_SHM_BRIDGE_CREATE,
.owner = ARM_SMCCC_OWNER_SIP,
.args[0] = pfn_and_ns_perm_flags,
.args[1] = ipfn_and_s_perm_flags,
.args[2] = size_and_flags,
.args[3] = ns_vmids,
.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL,
QCOM_SCM_VAL, QCOM_SCM_VAL),
};
struct qcom_scm_res res;
int ret;
ret = qcom_scm_call(__scm->dev, &desc, &res);
if (handle && !ret)
*handle = res.result[1];
return ret ?: res.result[0];
}
EXPORT_SYMBOL_GPL(qcom_scm_shm_bridge_create);
int qcom_scm_shm_bridge_delete(struct device *dev, u64 handle)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_MP,
.cmd = QCOM_SCM_MP_SHM_BRIDGE_DELETE,
.owner = ARM_SMCCC_OWNER_SIP,
.args[0] = handle,
.arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL),
};
return qcom_scm_call(__scm->dev, &desc, NULL);
}
EXPORT_SYMBOL_GPL(qcom_scm_shm_bridge_delete);
int qcom_scm_lmh_profile_change(u32 profile_id) int qcom_scm_lmh_profile_change(u32 profile_id)
{ {
struct qcom_scm_desc desc = { struct qcom_scm_desc desc = {
...@@ -1359,8 +1428,6 @@ EXPORT_SYMBOL_GPL(qcom_scm_lmh_profile_change); ...@@ -1359,8 +1428,6 @@ EXPORT_SYMBOL_GPL(qcom_scm_lmh_profile_change);
int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val,
u64 limit_node, u32 node_id, u64 version) u64 limit_node, u32 node_id, u64 version)
{ {
dma_addr_t payload_phys;
u32 *payload_buf;
int ret, payload_size = 5 * sizeof(u32); int ret, payload_size = 5 * sizeof(u32);
struct qcom_scm_desc desc = { struct qcom_scm_desc desc = {
...@@ -1375,7 +1442,9 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, ...@@ -1375,7 +1442,9 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val,
.owner = ARM_SMCCC_OWNER_SIP, .owner = ARM_SMCCC_OWNER_SIP,
}; };
payload_buf = dma_alloc_coherent(__scm->dev, payload_size, &payload_phys, GFP_KERNEL); u32 *payload_buf __free(qcom_tzmem) = qcom_tzmem_alloc(__scm->mempool,
payload_size,
GFP_KERNEL);
if (!payload_buf) if (!payload_buf)
return -ENOMEM; return -ENOMEM;
...@@ -1385,15 +1454,28 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, ...@@ -1385,15 +1454,28 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val,
payload_buf[3] = 1; payload_buf[3] = 1;
payload_buf[4] = payload_val; payload_buf[4] = payload_val;
desc.args[0] = payload_phys; desc.args[0] = qcom_tzmem_to_phys(payload_buf);
ret = qcom_scm_call(__scm->dev, &desc, NULL); ret = qcom_scm_call(__scm->dev, &desc, NULL);
dma_free_coherent(__scm->dev, payload_size, payload_buf, payload_phys);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh); EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh);
int qcom_scm_gpu_init_regs(u32 gpu_req)
{
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_GPU,
.cmd = QCOM_SCM_SVC_GPU_INIT_REGS,
.arginfo = QCOM_SCM_ARGS(1),
.args[0] = gpu_req,
.owner = ARM_SMCCC_OWNER_SIP,
};
return qcom_scm_call(__scm->dev, &desc, NULL);
}
EXPORT_SYMBOL_GPL(qcom_scm_gpu_init_regs);
static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
{ {
struct device_node *tcsr; struct device_node *tcsr;
...@@ -1545,37 +1627,27 @@ int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id) ...@@ -1545,37 +1627,27 @@ int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id)
unsigned long app_name_len = strlen(app_name); unsigned long app_name_len = strlen(app_name);
struct qcom_scm_desc desc = {}; struct qcom_scm_desc desc = {};
struct qcom_scm_qseecom_resp res = {}; struct qcom_scm_qseecom_resp res = {};
dma_addr_t name_buf_phys;
char *name_buf;
int status; int status;
if (app_name_len >= name_buf_size) if (app_name_len >= name_buf_size)
return -EINVAL; return -EINVAL;
name_buf = kzalloc(name_buf_size, GFP_KERNEL); char *name_buf __free(qcom_tzmem) = qcom_tzmem_alloc(__scm->mempool,
name_buf_size,
GFP_KERNEL);
if (!name_buf) if (!name_buf)
return -ENOMEM; return -ENOMEM;
memcpy(name_buf, app_name, app_name_len); memcpy(name_buf, app_name, app_name_len);
name_buf_phys = dma_map_single(__scm->dev, name_buf, name_buf_size, DMA_TO_DEVICE);
status = dma_mapping_error(__scm->dev, name_buf_phys);
if (status) {
kfree(name_buf);
dev_err(__scm->dev, "qseecom: failed to map dma address\n");
return status;
}
desc.owner = QSEECOM_TZ_OWNER_QSEE_OS; desc.owner = QSEECOM_TZ_OWNER_QSEE_OS;
desc.svc = QSEECOM_TZ_SVC_APP_MGR; desc.svc = QSEECOM_TZ_SVC_APP_MGR;
desc.cmd = QSEECOM_TZ_CMD_APP_LOOKUP; desc.cmd = QSEECOM_TZ_CMD_APP_LOOKUP;
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL); desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL);
desc.args[0] = name_buf_phys; desc.args[0] = qcom_tzmem_to_phys(name_buf);
desc.args[1] = app_name_len; desc.args[1] = app_name_len;
status = qcom_scm_qseecom_call(&desc, &res); status = qcom_scm_qseecom_call(&desc, &res);
dma_unmap_single(__scm->dev, name_buf_phys, name_buf_size, DMA_TO_DEVICE);
kfree(name_buf);
if (status) if (status)
return status; return status;
...@@ -1597,9 +1669,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id); ...@@ -1597,9 +1669,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id);
/** /**
* qcom_scm_qseecom_app_send() - Send to and receive data from a given QSEE app. * qcom_scm_qseecom_app_send() - Send to and receive data from a given QSEE app.
* @app_id: The ID of the target app. * @app_id: The ID of the target app.
* @req: DMA address of the request buffer sent to the app. * @req: Request buffer sent to the app (must be TZ memory)
* @req_size: Size of the request buffer. * @req_size: Size of the request buffer.
* @rsp: DMA address of the response buffer, written to by the app. * @rsp: Response buffer, written to by the app (must be TZ memory)
* @rsp_size: Size of the response buffer. * @rsp_size: Size of the response buffer.
* *
* Sends a request to the QSEE app associated with the given ID and read back * Sends a request to the QSEE app associated with the given ID and read back
...@@ -1610,13 +1682,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id); ...@@ -1610,13 +1682,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id);
* *
* Return: Zero on success, nonzero on failure. * Return: Zero on success, nonzero on failure.
*/ */
int qcom_scm_qseecom_app_send(u32 app_id, dma_addr_t req, size_t req_size, int qcom_scm_qseecom_app_send(u32 app_id, void *req, size_t req_size,
dma_addr_t rsp, size_t rsp_size) void *rsp, size_t rsp_size)
{ {
struct qcom_scm_qseecom_resp res = {}; struct qcom_scm_qseecom_resp res = {};
struct qcom_scm_desc desc = {}; struct qcom_scm_desc desc = {};
phys_addr_t req_phys;
phys_addr_t rsp_phys;
int status; int status;
req_phys = qcom_tzmem_to_phys(req);
rsp_phys = qcom_tzmem_to_phys(rsp);
desc.owner = QSEECOM_TZ_OWNER_TZ_APPS; desc.owner = QSEECOM_TZ_OWNER_TZ_APPS;
desc.svc = QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER; desc.svc = QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER;
desc.cmd = QSEECOM_TZ_CMD_APP_SEND; desc.cmd = QSEECOM_TZ_CMD_APP_SEND;
...@@ -1624,9 +1701,9 @@ int qcom_scm_qseecom_app_send(u32 app_id, dma_addr_t req, size_t req_size, ...@@ -1624,9 +1701,9 @@ int qcom_scm_qseecom_app_send(u32 app_id, dma_addr_t req, size_t req_size,
QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL,
QCOM_SCM_RW, QCOM_SCM_VAL); QCOM_SCM_RW, QCOM_SCM_VAL);
desc.args[0] = app_id; desc.args[0] = app_id;
desc.args[1] = req; desc.args[1] = req_phys;
desc.args[2] = req_size; desc.args[2] = req_size;
desc.args[3] = rsp; desc.args[3] = rsp_phys;
desc.args[4] = rsp_size; desc.args[4] = rsp_size;
status = qcom_scm_qseecom_call(&desc, &res); status = qcom_scm_qseecom_call(&desc, &res);
...@@ -1649,6 +1726,8 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { ...@@ -1649,6 +1726,8 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
{ .compatible = "lenovo,flex-5g" }, { .compatible = "lenovo,flex-5g" },
{ .compatible = "lenovo,thinkpad-x13s", }, { .compatible = "lenovo,thinkpad-x13s", },
{ .compatible = "qcom,sc8180x-primus" }, { .compatible = "qcom,sc8180x-primus" },
{ .compatible = "qcom,x1e80100-crd" },
{ .compatible = "qcom,x1e80100-qcp" },
{ } { }
}; };
...@@ -1793,9 +1872,8 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data) ...@@ -1793,9 +1872,8 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data)
goto out; goto out;
} }
if (flags != QCOM_SMC_WAITQ_FLAG_WAKE_ONE && if (flags != QCOM_SMC_WAITQ_FLAG_WAKE_ONE) {
flags != QCOM_SMC_WAITQ_FLAG_WAKE_ALL) { dev_err(scm->dev, "Invalid flags received for wq_ctx: %u\n", flags);
dev_err(scm->dev, "Invalid flags found for wq_ctx: %u\n", flags);
goto out; goto out;
} }
...@@ -1810,6 +1888,7 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data) ...@@ -1810,6 +1888,7 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data)
static int qcom_scm_probe(struct platform_device *pdev) static int qcom_scm_probe(struct platform_device *pdev)
{ {
struct qcom_tzmem_pool_config pool_config;
struct qcom_scm *scm; struct qcom_scm *scm;
int irq, ret; int irq, ret;
...@@ -1885,6 +1964,26 @@ static int qcom_scm_probe(struct platform_device *pdev) ...@@ -1885,6 +1964,26 @@ static int qcom_scm_probe(struct platform_device *pdev)
if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled")) if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled"))
qcom_scm_disable_sdi(); qcom_scm_disable_sdi();
ret = of_reserved_mem_device_init(__scm->dev);
if (ret && ret != -ENODEV)
return dev_err_probe(__scm->dev, ret,
"Failed to setup the reserved memory region for TZ mem\n");
ret = qcom_tzmem_enable(__scm->dev);
if (ret)
return dev_err_probe(__scm->dev, ret,
"Failed to enable the TrustZone memory allocator\n");
memset(&pool_config, 0, sizeof(pool_config));
pool_config.initial_size = 0;
pool_config.policy = QCOM_TZMEM_POLICY_ON_DEMAND;
pool_config.max_size = SZ_256K;
__scm->mempool = devm_qcom_tzmem_pool_new(__scm->dev, &pool_config);
if (IS_ERR(__scm->mempool))
return dev_err_probe(__scm->dev, PTR_ERR(__scm->mempool),
"Failed to create the SCM memory pool\n");
/* /*
* Initialize the QSEECOM interface. * Initialize the QSEECOM interface.
* *
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#define __QCOM_SCM_INT_H #define __QCOM_SCM_INT_H
struct device; struct device;
struct qcom_tzmem_pool;
enum qcom_scm_convention { enum qcom_scm_convention {
SMC_CONVENTION_UNKNOWN, SMC_CONVENTION_UNKNOWN,
...@@ -78,6 +79,8 @@ int scm_legacy_call_atomic(struct device *dev, const struct qcom_scm_desc *desc, ...@@ -78,6 +79,8 @@ int scm_legacy_call_atomic(struct device *dev, const struct qcom_scm_desc *desc,
int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
struct qcom_scm_res *res); struct qcom_scm_res *res);
struct qcom_tzmem_pool *qcom_scm_get_tzmem_pool(void);
#define QCOM_SCM_SVC_BOOT 0x01 #define QCOM_SCM_SVC_BOOT 0x01
#define QCOM_SCM_BOOT_SET_ADDR 0x01 #define QCOM_SCM_BOOT_SET_ADDR 0x01
#define QCOM_SCM_BOOT_TERMINATE_PC 0x02 #define QCOM_SCM_BOOT_TERMINATE_PC 0x02
...@@ -113,6 +116,9 @@ int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, ...@@ -113,6 +116,9 @@ int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
#define QCOM_SCM_MP_IOMMU_SET_CP_POOL_SIZE 0x05 #define QCOM_SCM_MP_IOMMU_SET_CP_POOL_SIZE 0x05
#define QCOM_SCM_MP_VIDEO_VAR 0x08 #define QCOM_SCM_MP_VIDEO_VAR 0x08
#define QCOM_SCM_MP_ASSIGN 0x16 #define QCOM_SCM_MP_ASSIGN 0x16
#define QCOM_SCM_MP_SHM_BRIDGE_ENABLE 0x1c
#define QCOM_SCM_MP_SHM_BRIDGE_DELETE 0x1d
#define QCOM_SCM_MP_SHM_BRIDGE_CREATE 0x1e
#define QCOM_SCM_SVC_OCMEM 0x0f #define QCOM_SCM_SVC_OCMEM 0x0f
#define QCOM_SCM_OCMEM_LOCK_CMD 0x01 #define QCOM_SCM_OCMEM_LOCK_CMD 0x01
...@@ -138,6 +144,9 @@ int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, ...@@ -138,6 +144,9 @@ int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
#define QCOM_SCM_WAITQ_RESUME 0x02 #define QCOM_SCM_WAITQ_RESUME 0x02
#define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03 #define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03
#define QCOM_SCM_SVC_GPU 0x28
#define QCOM_SCM_SVC_GPU_INIT_REGS 0x01
/* common error codes */ /* common error codes */
#define QCOM_SCM_V2_EBUSY -12 #define QCOM_SCM_V2_EBUSY -12
#define QCOM_SCM_ENOMEM -5 #define QCOM_SCM_ENOMEM -5
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Memory allocator for buffers shared with the TrustZone.
*
* Copyright (C) 2023-2024 Linaro Ltd.
*/
#include <linux/bug.h>
#include <linux/cleanup.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/firmware/qcom/qcom_tzmem.h>
#include <linux/genalloc.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/radix-tree.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include "qcom_tzmem.h"
struct qcom_tzmem_area {
struct list_head list;
void *vaddr;
dma_addr_t paddr;
size_t size;
void *priv;
};
struct qcom_tzmem_pool {
struct gen_pool *genpool;
struct list_head areas;
enum qcom_tzmem_policy policy;
size_t increment;
size_t max_size;
spinlock_t lock;
};
struct qcom_tzmem_chunk {
phys_addr_t paddr;
size_t size;
struct qcom_tzmem_pool *owner;
};
static struct device *qcom_tzmem_dev;
static RADIX_TREE(qcom_tzmem_chunks, GFP_ATOMIC);
static DEFINE_SPINLOCK(qcom_tzmem_chunks_lock);
#if IS_ENABLED(CONFIG_QCOM_TZMEM_MODE_GENERIC)
static int qcom_tzmem_init(void)
{
return 0;
}
static int qcom_tzmem_init_area(struct qcom_tzmem_area *area)
{
return 0;
}
static void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area)
{
}
#elif IS_ENABLED(CONFIG_QCOM_TZMEM_MODE_SHMBRIDGE)
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/of.h>
#define QCOM_SHM_BRIDGE_NUM_VM_SHIFT 9
static bool qcom_tzmem_using_shm_bridge;
/* List of machines that are known to not support SHM bridge correctly. */
static const char *const qcom_tzmem_blacklist[] = {
"qcom,sc8180x",
NULL
};
static int qcom_tzmem_init(void)
{
const char *const *platform;
int ret;
for (platform = qcom_tzmem_blacklist; *platform; platform++) {
if (of_machine_is_compatible(*platform))
goto notsupp;
}
ret = qcom_scm_shm_bridge_enable();
if (ret == -EOPNOTSUPP)
goto notsupp;
if (!ret)
qcom_tzmem_using_shm_bridge = true;
return ret;
notsupp:
dev_info(qcom_tzmem_dev, "SHM Bridge not supported\n");
return 0;
}
static int qcom_tzmem_init_area(struct qcom_tzmem_area *area)
{
u64 pfn_and_ns_perm, ipfn_and_s_perm, size_and_flags;
int ret;
if (!qcom_tzmem_using_shm_bridge)
return 0;
pfn_and_ns_perm = (u64)area->paddr | QCOM_SCM_PERM_RW;
ipfn_and_s_perm = (u64)area->paddr | QCOM_SCM_PERM_RW;
size_and_flags = area->size | (1 << QCOM_SHM_BRIDGE_NUM_VM_SHIFT);
u64 *handle __free(kfree) = kzalloc(sizeof(*handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
ret = qcom_scm_shm_bridge_create(qcom_tzmem_dev, pfn_and_ns_perm,
ipfn_and_s_perm, size_and_flags,
QCOM_SCM_VMID_HLOS, handle);
if (ret)
return ret;
area->priv = no_free_ptr(handle);
return 0;
}
static void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area)
{
u64 *handle = area->priv;
if (!qcom_tzmem_using_shm_bridge)
return;
qcom_scm_shm_bridge_delete(qcom_tzmem_dev, *handle);
kfree(handle);
}
#endif /* CONFIG_QCOM_TZMEM_MODE_SHMBRIDGE */
static int qcom_tzmem_pool_add_memory(struct qcom_tzmem_pool *pool,
size_t size, gfp_t gfp)
{
int ret;
struct qcom_tzmem_area *area __free(kfree) = kzalloc(sizeof(*area),
gfp);
if (!area)
return -ENOMEM;
area->size = PAGE_ALIGN(size);
area->vaddr = dma_alloc_coherent(qcom_tzmem_dev, area->size,
&area->paddr, gfp);
if (!area->vaddr)
return -ENOMEM;
ret = qcom_tzmem_init_area(area);
if (ret) {
dma_free_coherent(qcom_tzmem_dev, area->size,
area->vaddr, area->paddr);
return ret;
}
ret = gen_pool_add_virt(pool->genpool, (unsigned long)area->vaddr,
(phys_addr_t)area->paddr, size, -1);
if (ret) {
dma_free_coherent(qcom_tzmem_dev, area->size,
area->vaddr, area->paddr);
return ret;
}
scoped_guard(spinlock_irqsave, &pool->lock)
list_add_tail(&area->list, &pool->areas);
area = NULL;
return 0;
}
/**
* qcom_tzmem_pool_new() - Create a new TZ memory pool.
* @config: Pool configuration.
*
* Create a new pool of memory suitable for sharing with the TrustZone.
*
* Must not be used in atomic context.
*
* Return: New memory pool address or ERR_PTR() on error.
*/
struct qcom_tzmem_pool *
qcom_tzmem_pool_new(const struct qcom_tzmem_pool_config *config)
{
int ret = -ENOMEM;
might_sleep();
switch (config->policy) {
case QCOM_TZMEM_POLICY_STATIC:
if (!config->initial_size)
return ERR_PTR(-EINVAL);
break;
case QCOM_TZMEM_POLICY_MULTIPLIER:
if (!config->increment)
return ERR_PTR(-EINVAL);
break;
case QCOM_TZMEM_POLICY_ON_DEMAND:
break;
default:
return ERR_PTR(-EINVAL);
}
struct qcom_tzmem_pool *pool __free(kfree) = kzalloc(sizeof(*pool),
GFP_KERNEL);
if (!pool)
return ERR_PTR(-ENOMEM);
pool->genpool = gen_pool_create(PAGE_SHIFT, -1);
if (!pool->genpool)
return ERR_PTR(-ENOMEM);
gen_pool_set_algo(pool->genpool, gen_pool_best_fit, NULL);
pool->policy = config->policy;
pool->increment = config->increment;
pool->max_size = config->max_size;
INIT_LIST_HEAD(&pool->areas);
spin_lock_init(&pool->lock);
if (config->initial_size) {
ret = qcom_tzmem_pool_add_memory(pool, config->initial_size,
GFP_KERNEL);
if (ret) {
gen_pool_destroy(pool->genpool);
return ERR_PTR(ret);
}
}
return no_free_ptr(pool);
}
EXPORT_SYMBOL_GPL(qcom_tzmem_pool_new);
/**
* qcom_tzmem_pool_free() - Destroy a TZ memory pool and free all resources.
* @pool: Memory pool to free.
*
* Must not be called if any of the allocated chunks has not been freed.
* Must not be used in atomic context.
*/
void qcom_tzmem_pool_free(struct qcom_tzmem_pool *pool)
{
struct qcom_tzmem_area *area, *next;
struct qcom_tzmem_chunk *chunk;
struct radix_tree_iter iter;
bool non_empty = false;
void __rcu **slot;
might_sleep();
if (!pool)
return;
scoped_guard(spinlock_irqsave, &qcom_tzmem_chunks_lock) {
radix_tree_for_each_slot(slot, &qcom_tzmem_chunks, &iter, 0) {
chunk = radix_tree_deref_slot_protected(slot,
&qcom_tzmem_chunks_lock);
if (chunk->owner == pool)
non_empty = true;
}
}
WARN(non_empty, "Freeing TZ memory pool with memory still allocated");
list_for_each_entry_safe(area, next, &pool->areas, list) {
list_del(&area->list);
qcom_tzmem_cleanup_area(area);
dma_free_coherent(qcom_tzmem_dev, area->size,
area->vaddr, area->paddr);
kfree(area);
}
gen_pool_destroy(pool->genpool);
kfree(pool);
}
EXPORT_SYMBOL_GPL(qcom_tzmem_pool_free);
static void devm_qcom_tzmem_pool_free(void *data)
{
struct qcom_tzmem_pool *pool = data;
qcom_tzmem_pool_free(pool);
}
/**
* devm_qcom_tzmem_pool_new() - Managed variant of qcom_tzmem_pool_new().
* @dev: Device managing this resource.
* @config: Pool configuration.
*
* Must not be used in atomic context.
*
* Return: Address of the managed pool or ERR_PTR() on failure.
*/
struct qcom_tzmem_pool *
devm_qcom_tzmem_pool_new(struct device *dev,
const struct qcom_tzmem_pool_config *config)
{
struct qcom_tzmem_pool *pool;
int ret;
pool = qcom_tzmem_pool_new(config);
if (IS_ERR(pool))
return pool;
ret = devm_add_action_or_reset(dev, devm_qcom_tzmem_pool_free, pool);
if (ret)
return ERR_PTR(ret);
return pool;
}
EXPORT_SYMBOL_GPL(devm_qcom_tzmem_pool_new);
static bool qcom_tzmem_try_grow_pool(struct qcom_tzmem_pool *pool,
size_t requested, gfp_t gfp)
{
size_t current_size = gen_pool_size(pool->genpool);
if (pool->max_size && (current_size + requested) > pool->max_size)
return false;
switch (pool->policy) {
case QCOM_TZMEM_POLICY_STATIC:
return false;
case QCOM_TZMEM_POLICY_MULTIPLIER:
requested = current_size * pool->increment;
break;
case QCOM_TZMEM_POLICY_ON_DEMAND:
break;
}
return !qcom_tzmem_pool_add_memory(pool, requested, gfp);
}
/**
* qcom_tzmem_alloc() - Allocate a memory chunk suitable for sharing with TZ.
* @pool: TZ memory pool from which to allocate memory.
* @size: Number of bytes to allocate.
* @gfp: GFP flags.
*
* Can be used in any context.
*
* Return:
* Address of the allocated buffer or NULL if no more memory can be allocated.
* The buffer must be released using qcom_tzmem_free().
*/
void *qcom_tzmem_alloc(struct qcom_tzmem_pool *pool, size_t size, gfp_t gfp)
{
unsigned long vaddr;
int ret;
if (!size)
return NULL;
size = PAGE_ALIGN(size);
struct qcom_tzmem_chunk *chunk __free(kfree) = kzalloc(sizeof(*chunk),
gfp);
if (!chunk)
return NULL;
again:
vaddr = gen_pool_alloc(pool->genpool, size);
if (!vaddr) {
if (qcom_tzmem_try_grow_pool(pool, size, gfp))
goto again;
return NULL;
}
chunk->paddr = gen_pool_virt_to_phys(pool->genpool, vaddr);
chunk->size = size;
chunk->owner = pool;
scoped_guard(spinlock_irqsave, &qcom_tzmem_chunks_lock) {
ret = radix_tree_insert(&qcom_tzmem_chunks, vaddr, chunk);
if (ret) {
gen_pool_free(pool->genpool, vaddr, size);
return NULL;
}
chunk = NULL;
}
return (void *)vaddr;
}
EXPORT_SYMBOL_GPL(qcom_tzmem_alloc);
/**
* qcom_tzmem_free() - Release a buffer allocated from a TZ memory pool.
* @vaddr: Virtual address of the buffer.
*
* Can be used in any context.
*/
void qcom_tzmem_free(void *vaddr)
{
struct qcom_tzmem_chunk *chunk;
scoped_guard(spinlock_irqsave, &qcom_tzmem_chunks_lock)
chunk = radix_tree_delete_item(&qcom_tzmem_chunks,
(unsigned long)vaddr, NULL);
if (!chunk) {
WARN(1, "Virtual address %p not owned by TZ memory allocator",
vaddr);
return;
}
scoped_guard(spinlock_irqsave, &chunk->owner->lock)
gen_pool_free(chunk->owner->genpool, (unsigned long)vaddr,
chunk->size);
kfree(chunk);
}
EXPORT_SYMBOL_GPL(qcom_tzmem_free);
/**
* qcom_tzmem_to_phys() - Map the virtual address of a TZ buffer to physical.
* @vaddr: Virtual address of the buffer allocated from a TZ memory pool.
*
* Can be used in any context. The address must have been returned by a call
* to qcom_tzmem_alloc().
*
* Returns: Physical address of the buffer.
*/
phys_addr_t qcom_tzmem_to_phys(void *vaddr)
{
struct qcom_tzmem_chunk *chunk;
guard(spinlock_irqsave)(&qcom_tzmem_chunks_lock);
chunk = radix_tree_lookup(&qcom_tzmem_chunks, (unsigned long)vaddr);
if (!chunk)
return 0;
return chunk->paddr;
}
EXPORT_SYMBOL_GPL(qcom_tzmem_to_phys);
int qcom_tzmem_enable(struct device *dev)
{
if (qcom_tzmem_dev)
return -EBUSY;
qcom_tzmem_dev = dev;
return qcom_tzmem_init();
}
EXPORT_SYMBOL_GPL(qcom_tzmem_enable);
MODULE_DESCRIPTION("TrustZone memory allocator for Qualcomm firmware drivers");
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
MODULE_LICENSE("GPL");
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2023-2024 Linaro Ltd.
*/
#ifndef __QCOM_TZMEM_PRIV_H
#define __QCOM_TZMEM_PRIV_H
struct device;
int qcom_tzmem_enable(struct device *dev);
#endif /* __QCOM_TZMEM_PRIV_H */
...@@ -72,11 +72,28 @@ config QCOM_OCMEM ...@@ -72,11 +72,28 @@ config QCOM_OCMEM
requirements. This is typically used by the GPU, camera/video, and requirements. This is typically used by the GPU, camera/video, and
audio components on some Snapdragon SoCs. audio components on some Snapdragon SoCs.
config QCOM_PD_MAPPER
tristate "Qualcomm Protection Domain Mapper"
select QCOM_QMI_HELPERS
select QCOM_PDR_MSG
select AUXILIARY_BUS
depends on NET && QRTR
default QCOM_RPROC_COMMON
help
The Protection Domain Mapper maps registered services to the domains
and instances handled by the remote DSPs. This is a kernel-space
implementation of the service. It is a simpler alternative to the
userspace daemon.
config QCOM_PDR_HELPERS config QCOM_PDR_HELPERS
tristate tristate
select QCOM_QMI_HELPERS select QCOM_QMI_HELPERS
select QCOM_PDR_MSG
depends on NET depends on NET
config QCOM_PDR_MSG
tristate
config QCOM_PMIC_PDCHARGER_ULOG config QCOM_PMIC_PDCHARGER_ULOG
tristate "Qualcomm PMIC PDCharger ULOG driver" tristate "Qualcomm PMIC PDCharger ULOG driver"
depends on RPMSG depends on RPMSG
...@@ -194,6 +211,7 @@ config QCOM_SMP2P ...@@ -194,6 +211,7 @@ config QCOM_SMP2P
config QCOM_SMSM config QCOM_SMSM
tristate "Qualcomm Shared Memory State Machine" tristate "Qualcomm Shared Memory State Machine"
depends on MAILBOX
depends on QCOM_SMEM depends on QCOM_SMEM
select QCOM_SMEM_STATE select QCOM_SMEM_STATE
select IRQ_DOMAIN select IRQ_DOMAIN
......
...@@ -7,7 +7,9 @@ obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o ...@@ -7,7 +7,9 @@ obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
obj-$(CONFIG_QCOM_OCMEM) += ocmem.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o
obj-$(CONFIG_QCOM_PD_MAPPER) += qcom_pd_mapper.o
obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o
obj-$(CONFIG_QCOM_PDR_MSG) += qcom_pdr_msg.o
obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink.o obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink.o
obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink_altmode.o obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink_altmode.o
obj-$(CONFIG_QCOM_PMIC_PDCHARGER_ULOG) += pmic_pdcharger_ulog.o obj-$(CONFIG_QCOM_PMIC_PDCHARGER_ULOG) += pmic_pdcharger_ulog.o
......
...@@ -565,7 +565,7 @@ static void bwmon_start(struct icc_bwmon *bwmon) ...@@ -565,7 +565,7 @@ static void bwmon_start(struct icc_bwmon *bwmon)
int window; int window;
/* No need to check for errors, as this must have succeeded before. */ /* No need to check for errors, as this must have succeeded before. */
dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_low, 0); dev_pm_opp_put(dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_low, 0));
bwmon_clear_counters(bwmon, true); bwmon_clear_counters(bwmon, true);
...@@ -772,18 +772,25 @@ static int bwmon_probe(struct platform_device *pdev) ...@@ -772,18 +772,25 @@ static int bwmon_probe(struct platform_device *pdev)
opp = dev_pm_opp_find_bw_floor(dev, &bwmon->max_bw_kbps, 0); opp = dev_pm_opp_find_bw_floor(dev, &bwmon->max_bw_kbps, 0);
if (IS_ERR(opp)) if (IS_ERR(opp))
return dev_err_probe(dev, PTR_ERR(opp), "failed to find max peak bandwidth\n"); return dev_err_probe(dev, PTR_ERR(opp), "failed to find max peak bandwidth\n");
dev_pm_opp_put(opp);
bwmon->min_bw_kbps = 0; bwmon->min_bw_kbps = 0;
opp = dev_pm_opp_find_bw_ceil(dev, &bwmon->min_bw_kbps, 0); opp = dev_pm_opp_find_bw_ceil(dev, &bwmon->min_bw_kbps, 0);
if (IS_ERR(opp)) if (IS_ERR(opp))
return dev_err_probe(dev, PTR_ERR(opp), "failed to find min peak bandwidth\n"); return dev_err_probe(dev, PTR_ERR(opp), "failed to find min peak bandwidth\n");
dev_pm_opp_put(opp);
bwmon->dev = dev; bwmon->dev = dev;
bwmon_disable(bwmon); bwmon_disable(bwmon);
ret = devm_request_threaded_irq(dev, bwmon->irq, bwmon_intr,
bwmon_intr_thread, /*
IRQF_ONESHOT, dev_name(dev), bwmon); * SoCs with multiple cpu-bwmon instances can end up using a shared interrupt
* line. Using the devm_ variant might result in the IRQ handler being executed
* after bwmon_disable in bwmon_remove()
*/
ret = request_threaded_irq(bwmon->irq, bwmon_intr, bwmon_intr_thread,
IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), bwmon);
if (ret) if (ret)
return dev_err_probe(dev, ret, "failed to request IRQ\n"); return dev_err_probe(dev, ret, "failed to request IRQ\n");
...@@ -798,6 +805,7 @@ static void bwmon_remove(struct platform_device *pdev) ...@@ -798,6 +805,7 @@ static void bwmon_remove(struct platform_device *pdev)
struct icc_bwmon *bwmon = platform_get_drvdata(pdev); struct icc_bwmon *bwmon = platform_get_drvdata(pdev);
bwmon_disable(bwmon); bwmon_disable(bwmon);
free_irq(bwmon->irq, bwmon);
} }
static const struct icc_bwmon_data msm8998_bwmon_data = { static const struct icc_bwmon_data msm8998_bwmon_data = {
......
...@@ -150,6 +150,25 @@ enum llcc_reg_offset { ...@@ -150,6 +150,25 @@ enum llcc_reg_offset {
LLCC_COMMON_STATUS0, LLCC_COMMON_STATUS0,
}; };
static const struct llcc_slice_config sa8775p_data[] = {
{LLCC_CPUSS, 1, 2048, 1, 0, 0x00FF, 0x0, 0, 0, 0, 1, 1, 0, 0},
{LLCC_VIDSC0, 2, 512, 3, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0},
{LLCC_CPUSS1, 3, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0},
{LLCC_CPUHWT, 5, 512, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0},
{LLCC_AUDIO, 6, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 0, 0, 0, 0},
{LLCC_CMPT, 10, 4096, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0},
{LLCC_GPUHTW, 11, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0},
{LLCC_GPU, 12, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 1, 0},
{LLCC_MMUHWT, 13, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 0, 1, 0, 0},
{LLCC_CMPTDMA, 15, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0},
{LLCC_DISP, 16, 4096, 2, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0},
{LLCC_VIDFW, 17, 3072, 1, 0, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0},
{LLCC_AUDHW, 22, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 0, 0, 0, 0},
{LLCC_CVP, 28, 256, 3, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0},
{LLCC_APTCM, 30, 1024, 3, 1, 0x0, 0xF0, 1, 0, 0, 1, 0, 0, 0},
{LLCC_WRCACHE, 31, 512, 1, 1, 0x00FF, 0x0, 0, 0, 0, 0, 1, 0, 0},
};
static const struct llcc_slice_config sc7180_data[] = { static const struct llcc_slice_config sc7180_data[] = {
{ LLCC_CPUSS, 1, 256, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 1 }, { LLCC_CPUSS, 1, 256, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 1 },
{ LLCC_MDM, 8, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, { LLCC_MDM, 8, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 },
...@@ -552,6 +571,16 @@ static const struct qcom_llcc_config qdu1000_cfg[] = { ...@@ -552,6 +571,16 @@ static const struct qcom_llcc_config qdu1000_cfg[] = {
}, },
}; };
static const struct qcom_llcc_config sa8775p_cfg[] = {
{
.sct_data = sa8775p_data,
.size = ARRAY_SIZE(sa8775p_data),
.need_llcc_cfg = true,
.reg_offset = llcc_v2_1_reg_offset,
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
},
};
static const struct qcom_llcc_config sc7180_cfg[] = { static const struct qcom_llcc_config sc7180_cfg[] = {
{ {
.sct_data = sc7180_data, .sct_data = sc7180_data,
...@@ -698,6 +727,11 @@ static const struct qcom_sct_config qdu1000_cfgs = { ...@@ -698,6 +727,11 @@ static const struct qcom_sct_config qdu1000_cfgs = {
.num_config = ARRAY_SIZE(qdu1000_cfg), .num_config = ARRAY_SIZE(qdu1000_cfg),
}; };
static const struct qcom_sct_config sa8775p_cfgs = {
.llcc_config = sa8775p_cfg,
.num_config = ARRAY_SIZE(sa8775p_cfg),
};
static const struct qcom_sct_config sc7180_cfgs = { static const struct qcom_sct_config sc7180_cfgs = {
.llcc_config = sc7180_cfg, .llcc_config = sc7180_cfg,
.num_config = ARRAY_SIZE(sc7180_cfg), .num_config = ARRAY_SIZE(sc7180_cfg),
...@@ -821,6 +855,7 @@ EXPORT_SYMBOL_GPL(llcc_slice_putd); ...@@ -821,6 +855,7 @@ EXPORT_SYMBOL_GPL(llcc_slice_putd);
static int llcc_update_act_ctrl(u32 sid, static int llcc_update_act_ctrl(u32 sid,
u32 act_ctrl_reg_val, u32 status) u32 act_ctrl_reg_val, u32 status)
{ {
struct regmap *regmap;
u32 act_ctrl_reg; u32 act_ctrl_reg;
u32 act_clear_reg; u32 act_clear_reg;
u32 status_reg; u32 status_reg;
...@@ -849,7 +884,8 @@ static int llcc_update_act_ctrl(u32 sid, ...@@ -849,7 +884,8 @@ static int llcc_update_act_ctrl(u32 sid,
return ret; return ret;
if (drv_data->version >= LLCC_VERSION_4_1_0_0) { if (drv_data->version >= LLCC_VERSION_4_1_0_0) {
ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg, regmap = drv_data->bcast_and_regmap ?: drv_data->bcast_regmap;
ret = regmap_read_poll_timeout(regmap, status_reg,
slice_status, (slice_status & ACT_COMPLETE), slice_status, (slice_status & ACT_COMPLETE),
0, LLCC_STATUS_READ_DELAY); 0, LLCC_STATUS_READ_DELAY);
if (ret) if (ret)
...@@ -1284,6 +1320,18 @@ static int qcom_llcc_probe(struct platform_device *pdev) ...@@ -1284,6 +1320,18 @@ static int qcom_llcc_probe(struct platform_device *pdev)
drv_data->version = version; drv_data->version = version;
/* Applicable only when drv_data->version >= 4.1 */
if (drv_data->version >= LLCC_VERSION_4_1_0_0) {
drv_data->bcast_and_regmap = qcom_llcc_init_mmio(pdev, i + 1, "llcc_broadcast_and_base");
if (IS_ERR(drv_data->bcast_and_regmap)) {
ret = PTR_ERR(drv_data->bcast_and_regmap);
if (ret == -EINVAL)
drv_data->bcast_and_regmap = NULL;
else
goto err;
}
}
llcc_cfg = cfg->sct_data; llcc_cfg = cfg->sct_data;
sz = cfg->size; sz = cfg->size;
...@@ -1332,6 +1380,7 @@ static int qcom_llcc_probe(struct platform_device *pdev) ...@@ -1332,6 +1380,7 @@ static int qcom_llcc_probe(struct platform_device *pdev)
static const struct of_device_id qcom_llcc_of_match[] = { static const struct of_device_id qcom_llcc_of_match[] = {
{ .compatible = "qcom,qdu1000-llcc", .data = &qdu1000_cfgs}, { .compatible = "qcom,qdu1000-llcc", .data = &qdu1000_cfgs},
{ .compatible = "qcom,sa8775p-llcc", .data = &sa8775p_cfgs },
{ .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfgs }, { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfgs },
{ .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfgs }, { .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfgs },
{ .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfgs }, { .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfgs },
......
...@@ -76,12 +76,12 @@ static int pdr_locator_new_server(struct qmi_handle *qmi, ...@@ -76,12 +76,12 @@ static int pdr_locator_new_server(struct qmi_handle *qmi,
locator_hdl); locator_hdl);
struct pdr_service *pds; struct pdr_service *pds;
mutex_lock(&pdr->lock);
/* Create a local client port for QMI communication */ /* Create a local client port for QMI communication */
pdr->locator_addr.sq_family = AF_QIPCRTR; pdr->locator_addr.sq_family = AF_QIPCRTR;
pdr->locator_addr.sq_node = svc->node; pdr->locator_addr.sq_node = svc->node;
pdr->locator_addr.sq_port = svc->port; pdr->locator_addr.sq_port = svc->port;
mutex_lock(&pdr->lock);
pdr->locator_init_complete = true; pdr->locator_init_complete = true;
mutex_unlock(&pdr->lock); mutex_unlock(&pdr->lock);
...@@ -104,10 +104,10 @@ static void pdr_locator_del_server(struct qmi_handle *qmi, ...@@ -104,10 +104,10 @@ static void pdr_locator_del_server(struct qmi_handle *qmi,
mutex_lock(&pdr->lock); mutex_lock(&pdr->lock);
pdr->locator_init_complete = false; pdr->locator_init_complete = false;
mutex_unlock(&pdr->lock);
pdr->locator_addr.sq_node = 0; pdr->locator_addr.sq_node = 0;
pdr->locator_addr.sq_port = 0; pdr->locator_addr.sq_port = 0;
mutex_unlock(&pdr->lock);
} }
static const struct qmi_ops pdr_locator_ops = { static const struct qmi_ops pdr_locator_ops = {
...@@ -365,12 +365,14 @@ static int pdr_get_domain_list(struct servreg_get_domain_list_req *req, ...@@ -365,12 +365,14 @@ static int pdr_get_domain_list(struct servreg_get_domain_list_req *req,
if (ret < 0) if (ret < 0)
return ret; return ret;
mutex_lock(&pdr->lock);
ret = qmi_send_request(&pdr->locator_hdl, ret = qmi_send_request(&pdr->locator_hdl,
&pdr->locator_addr, &pdr->locator_addr,
&txn, SERVREG_GET_DOMAIN_LIST_REQ, &txn, SERVREG_GET_DOMAIN_LIST_REQ,
SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN, SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN,
servreg_get_domain_list_req_ei, servreg_get_domain_list_req_ei,
req); req);
mutex_unlock(&pdr->lock);
if (ret < 0) { if (ret < 0) {
qmi_txn_cancel(&txn); qmi_txn_cancel(&txn);
return ret; return ret;
...@@ -415,7 +417,7 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds) ...@@ -415,7 +417,7 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds)
if (ret < 0) if (ret < 0)
goto out; goto out;
for (i = domains_read; i < resp->domain_list_len; i++) { for (i = 0; i < resp->domain_list_len; i++) {
entry = &resp->domain_list[i]; entry = &resp->domain_list[i];
if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name)) if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name))
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#define SERVREG_SET_ACK_REQ 0x23 #define SERVREG_SET_ACK_REQ 0x23
#define SERVREG_RESTART_PD_REQ 0x24 #define SERVREG_RESTART_PD_REQ 0x24
#define SERVREG_LOC_PFR_REQ 0x24
#define SERVREG_DOMAIN_LIST_LENGTH 32 #define SERVREG_DOMAIN_LIST_LENGTH 32
#define SERVREG_RESTART_PD_REQ_MAX_LEN 67 #define SERVREG_RESTART_PD_REQ_MAX_LEN 67
#define SERVREG_REGISTER_LISTENER_REQ_LEN 71 #define SERVREG_REGISTER_LISTENER_REQ_LEN 71
...@@ -20,6 +22,7 @@ ...@@ -20,6 +22,7 @@
#define SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN 74 #define SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN 74
#define SERVREG_STATE_UPDATED_IND_MAX_LEN 79 #define SERVREG_STATE_UPDATED_IND_MAX_LEN 79
#define SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN 2389 #define SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN 2389
#define SERVREG_LOC_PFR_RESP_MAX_LEN 10
struct servreg_location_entry { struct servreg_location_entry {
char name[SERVREG_NAME_LENGTH + 1]; char name[SERVREG_NAME_LENGTH + 1];
...@@ -28,83 +31,12 @@ struct servreg_location_entry { ...@@ -28,83 +31,12 @@ struct servreg_location_entry {
u32 instance; u32 instance;
}; };
static const struct qmi_elem_info servreg_location_entry_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset = offsetof(struct servreg_location_entry,
name),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset = offsetof(struct servreg_location_entry,
instance),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset = offsetof(struct servreg_location_entry,
service_data_valid),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset = offsetof(struct servreg_location_entry,
service_data),
},
{}
};
struct servreg_get_domain_list_req { struct servreg_get_domain_list_req {
char service_name[SERVREG_NAME_LENGTH + 1]; char service_name[SERVREG_NAME_LENGTH + 1];
u8 domain_offset_valid; u8 domain_offset_valid;
u32 domain_offset; u32 domain_offset;
}; };
static const struct qmi_elem_info servreg_get_domain_list_req_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_get_domain_list_req,
service_name),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_get_domain_list_req,
domain_offset_valid),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_get_domain_list_req,
domain_offset),
},
{}
};
struct servreg_get_domain_list_resp { struct servreg_get_domain_list_resp {
struct qmi_response_type_v01 resp; struct qmi_response_type_v01 resp;
u8 total_domains_valid; u8 total_domains_valid;
...@@ -116,264 +48,60 @@ struct servreg_get_domain_list_resp { ...@@ -116,264 +48,60 @@ struct servreg_get_domain_list_resp {
struct servreg_location_entry domain_list[SERVREG_DOMAIN_LIST_LENGTH]; struct servreg_location_entry domain_list[SERVREG_DOMAIN_LIST_LENGTH];
}; };
static const struct qmi_elem_info servreg_get_domain_list_resp_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_get_domain_list_resp,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_get_domain_list_resp,
total_domains_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_get_domain_list_resp,
total_domains),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset = offsetof(struct servreg_get_domain_list_resp,
db_rev_count_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset = offsetof(struct servreg_get_domain_list_resp,
db_rev_count),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x12,
.offset = offsetof(struct servreg_get_domain_list_resp,
domain_list_valid),
},
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x12,
.offset = offsetof(struct servreg_get_domain_list_resp,
domain_list_len),
},
{
.data_type = QMI_STRUCT,
.elem_len = SERVREG_DOMAIN_LIST_LENGTH,
.elem_size = sizeof(struct servreg_location_entry),
.array_type = VAR_LEN_ARRAY,
.tlv_type = 0x12,
.offset = offsetof(struct servreg_get_domain_list_resp,
domain_list),
.ei_array = servreg_location_entry_ei,
},
{}
};
struct servreg_register_listener_req { struct servreg_register_listener_req {
u8 enable; u8 enable;
char service_path[SERVREG_NAME_LENGTH + 1]; char service_path[SERVREG_NAME_LENGTH + 1];
}; };
static const struct qmi_elem_info servreg_register_listener_req_ei[] = {
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_register_listener_req,
enable),
},
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_register_listener_req,
service_path),
},
{}
};
struct servreg_register_listener_resp { struct servreg_register_listener_resp {
struct qmi_response_type_v01 resp; struct qmi_response_type_v01 resp;
u8 curr_state_valid; u8 curr_state_valid;
enum servreg_service_state curr_state; enum servreg_service_state curr_state;
}; };
static const struct qmi_elem_info servreg_register_listener_resp_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_register_listener_resp,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_register_listener_resp,
curr_state_valid),
},
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
.elem_size = sizeof(enum servreg_service_state),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_register_listener_resp,
curr_state),
},
{}
};
struct servreg_restart_pd_req { struct servreg_restart_pd_req {
char service_path[SERVREG_NAME_LENGTH + 1]; char service_path[SERVREG_NAME_LENGTH + 1];
}; };
static const struct qmi_elem_info servreg_restart_pd_req_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_restart_pd_req,
service_path),
},
{}
};
struct servreg_restart_pd_resp { struct servreg_restart_pd_resp {
struct qmi_response_type_v01 resp; struct qmi_response_type_v01 resp;
}; };
static const struct qmi_elem_info servreg_restart_pd_resp_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_restart_pd_resp,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{}
};
struct servreg_state_updated_ind { struct servreg_state_updated_ind {
enum servreg_service_state curr_state; enum servreg_service_state curr_state;
char service_path[SERVREG_NAME_LENGTH + 1]; char service_path[SERVREG_NAME_LENGTH + 1];
u16 transaction_id; u16 transaction_id;
}; };
static const struct qmi_elem_info servreg_state_updated_ind_ei[] = {
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_state_updated_ind,
curr_state),
},
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_state_updated_ind,
service_path),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x03,
.offset = offsetof(struct servreg_state_updated_ind,
transaction_id),
},
{}
};
struct servreg_set_ack_req { struct servreg_set_ack_req {
char service_path[SERVREG_NAME_LENGTH + 1]; char service_path[SERVREG_NAME_LENGTH + 1];
u16 transaction_id; u16 transaction_id;
}; };
static const struct qmi_elem_info servreg_set_ack_req_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_set_ack_req,
service_path),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_set_ack_req,
transaction_id),
},
{}
};
struct servreg_set_ack_resp { struct servreg_set_ack_resp {
struct qmi_response_type_v01 resp; struct qmi_response_type_v01 resp;
}; };
static const struct qmi_elem_info servreg_set_ack_resp_ei[] = { struct servreg_loc_pfr_req {
{ char service[SERVREG_NAME_LENGTH + 1];
.data_type = QMI_STRUCT, char reason[257];
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_set_ack_resp,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{}
}; };
struct servreg_loc_pfr_resp {
struct qmi_response_type_v01 rsp;
};
extern const struct qmi_elem_info servreg_location_entry_ei[];
extern const struct qmi_elem_info servreg_get_domain_list_req_ei[];
extern const struct qmi_elem_info servreg_get_domain_list_resp_ei[];
extern const struct qmi_elem_info servreg_register_listener_req_ei[];
extern const struct qmi_elem_info servreg_register_listener_resp_ei[];
extern const struct qmi_elem_info servreg_restart_pd_req_ei[];
extern const struct qmi_elem_info servreg_restart_pd_resp_ei[];
extern const struct qmi_elem_info servreg_state_updated_ind_ei[];
extern const struct qmi_elem_info servreg_set_ack_req_ei[];
extern const struct qmi_elem_info servreg_set_ack_resp_ei[];
extern const struct qmi_elem_info servreg_loc_pfr_req_ei[];
extern const struct qmi_elem_info servreg_loc_pfr_resp_ei[];
#endif #endif
...@@ -369,8 +369,17 @@ static struct platform_driver pmic_glink_driver = { ...@@ -369,8 +369,17 @@ static struct platform_driver pmic_glink_driver = {
static int pmic_glink_init(void) static int pmic_glink_init(void)
{ {
platform_driver_register(&pmic_glink_driver); int ret;
register_rpmsg_driver(&pmic_glink_rpmsg_driver);
ret = platform_driver_register(&pmic_glink_driver);
if (ret < 0)
return ret;
ret = register_rpmsg_driver(&pmic_glink_rpmsg_driver);
if (ret < 0) {
platform_driver_unregister(&pmic_glink_driver);
return ret;
}
return 0; return 0;
} }
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include <linux/soc/qcom/pmic_glink.h> #include <linux/soc/qcom/pmic_glink.h>
#define PMIC_GLINK_MAX_PORTS 2 #define PMIC_GLINK_MAX_PORTS 3
#define USBC_SC8180X_NOTIFY_IND 0x13 #define USBC_SC8180X_NOTIFY_IND 0x13
#define USBC_CMD_WRITE_REQ 0x15 #define USBC_CMD_WRITE_REQ 0x15
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Qualcomm Protection Domain mapper
*
* Copyright (c) 2023 Linaro Ltd.
*/
#include <linux/auxiliary_bus.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/soc/qcom/qmi.h>
#include "pdr_internal.h"
#define SERVREG_QMI_VERSION 0x101
#define SERVREG_QMI_INSTANCE 0
#define TMS_SERVREG_SERVICE "tms/servreg"
struct qcom_pdm_domain_data {
const char *domain;
u32 instance_id;
/* NULL-terminated array */
const char * services[];
};
struct qcom_pdm_domain {
struct list_head list;
const char *name;
u32 instance_id;
};
struct qcom_pdm_service {
struct list_head list;
struct list_head domains;
const char *name;
};
struct qcom_pdm_data {
refcount_t refcnt;
struct qmi_handle handle;
struct list_head services;
};
static DEFINE_MUTEX(qcom_pdm_mutex); /* protects __qcom_pdm_data */
static struct qcom_pdm_data *__qcom_pdm_data;
static struct qcom_pdm_service *qcom_pdm_find(struct qcom_pdm_data *data,
const char *name)
{
struct qcom_pdm_service *service;
list_for_each_entry(service, &data->services, list) {
if (!strcmp(service->name, name))
return service;
}
return NULL;
}
static int qcom_pdm_add_service_domain(struct qcom_pdm_data *data,
const char *service_name,
const char *domain_name,
u32 instance_id)
{
struct qcom_pdm_service *service;
struct qcom_pdm_domain *domain;
service = qcom_pdm_find(data, service_name);
if (service) {
list_for_each_entry(domain, &service->domains, list) {
if (!strcmp(domain->name, domain_name))
return -EBUSY;
}
} else {
service = kzalloc(sizeof(*service), GFP_KERNEL);
if (!service)
return -ENOMEM;
INIT_LIST_HEAD(&service->domains);
service->name = service_name;
list_add_tail(&service->list, &data->services);
}
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
if (!domain) {
if (list_empty(&service->domains)) {
list_del(&service->list);
kfree(service);
}
return -ENOMEM;
}
domain->name = domain_name;
domain->instance_id = instance_id;
list_add_tail(&domain->list, &service->domains);
return 0;
}
static int qcom_pdm_add_domain(struct qcom_pdm_data *data,
const struct qcom_pdm_domain_data *domain)
{
int ret;
int i;
ret = qcom_pdm_add_service_domain(data,
TMS_SERVREG_SERVICE,
domain->domain,
domain->instance_id);
if (ret)
return ret;
for (i = 0; domain->services[i]; i++) {
ret = qcom_pdm_add_service_domain(data,
domain->services[i],
domain->domain,
domain->instance_id);
if (ret)
return ret;
}
return 0;
}
static void qcom_pdm_free_domains(struct qcom_pdm_data *data)
{
struct qcom_pdm_service *service, *tservice;
struct qcom_pdm_domain *domain, *tdomain;
list_for_each_entry_safe(service, tservice, &data->services, list) {
list_for_each_entry_safe(domain, tdomain, &service->domains, list) {
list_del(&domain->list);
kfree(domain);
}
list_del(&service->list);
kfree(service);
}
}
static void qcom_pdm_get_domain_list(struct qmi_handle *qmi,
struct sockaddr_qrtr *sq,
struct qmi_txn *txn,
const void *decoded)
{
struct qcom_pdm_data *data = container_of(qmi, struct qcom_pdm_data, handle);
const struct servreg_get_domain_list_req *req = decoded;
struct servreg_get_domain_list_resp *rsp;
struct qcom_pdm_service *service;
u32 offset;
int ret;
rsp = kzalloc(sizeof(*rsp), GFP_KERNEL);
if (!rsp)
return;
offset = req->domain_offset_valid ? req->domain_offset : 0;
rsp->resp.result = QMI_RESULT_SUCCESS_V01;
rsp->resp.error = QMI_ERR_NONE_V01;
rsp->db_rev_count_valid = true;
rsp->db_rev_count = 1;
rsp->total_domains_valid = true;
rsp->total_domains = 0;
mutex_lock(&qcom_pdm_mutex);
service = qcom_pdm_find(data, req->service_name);
if (service) {
struct qcom_pdm_domain *domain;
rsp->domain_list_valid = true;
rsp->domain_list_len = 0;
list_for_each_entry(domain, &service->domains, list) {
u32 i = rsp->total_domains++;
if (i >= offset && i < SERVREG_DOMAIN_LIST_LENGTH) {
u32 j = rsp->domain_list_len++;
strscpy(rsp->domain_list[j].name, domain->name,
sizeof(rsp->domain_list[i].name));
rsp->domain_list[j].instance = domain->instance_id;
pr_debug("PDM: found %s / %d\n", domain->name,
domain->instance_id);
}
}
}
pr_debug("PDM: service '%s' offset %d returning %d domains (of %d)\n", req->service_name,
req->domain_offset_valid ? req->domain_offset : -1, rsp->domain_list_len, rsp->total_domains);
ret = qmi_send_response(qmi, sq, txn, SERVREG_GET_DOMAIN_LIST_REQ,
SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN,
servreg_get_domain_list_resp_ei, rsp);
if (ret)
pr_err("Error sending servreg response: %d\n", ret);
mutex_unlock(&qcom_pdm_mutex);
kfree(rsp);
}
static void qcom_pdm_pfr(struct qmi_handle *qmi,
struct sockaddr_qrtr *sq,
struct qmi_txn *txn,
const void *decoded)
{
const struct servreg_loc_pfr_req *req = decoded;
struct servreg_loc_pfr_resp rsp = {};
int ret;
pr_warn_ratelimited("PDM: service '%s' crash: '%s'\n", req->service, req->reason);
rsp.rsp.result = QMI_RESULT_SUCCESS_V01;
rsp.rsp.error = QMI_ERR_NONE_V01;
ret = qmi_send_response(qmi, sq, txn, SERVREG_LOC_PFR_REQ,
SERVREG_LOC_PFR_RESP_MAX_LEN,
servreg_loc_pfr_resp_ei, &rsp);
if (ret)
pr_err("Error sending servreg response: %d\n", ret);
}
static const struct qmi_msg_handler qcom_pdm_msg_handlers[] = {
{
.type = QMI_REQUEST,
.msg_id = SERVREG_GET_DOMAIN_LIST_REQ,
.ei = servreg_get_domain_list_req_ei,
.decoded_size = sizeof(struct servreg_get_domain_list_req),
.fn = qcom_pdm_get_domain_list,
},
{
.type = QMI_REQUEST,
.msg_id = SERVREG_LOC_PFR_REQ,
.ei = servreg_loc_pfr_req_ei,
.decoded_size = sizeof(struct servreg_loc_pfr_req),
.fn = qcom_pdm_pfr,
},
{ },
};
static const struct qcom_pdm_domain_data adsp_audio_pd = {
.domain = "msm/adsp/audio_pd",
.instance_id = 74,
.services = {
"avs/audio",
NULL,
},
};
static const struct qcom_pdm_domain_data adsp_charger_pd = {
.domain = "msm/adsp/charger_pd",
.instance_id = 74,
.services = { NULL },
};
static const struct qcom_pdm_domain_data adsp_root_pd = {
.domain = "msm/adsp/root_pd",
.instance_id = 74,
.services = { NULL },
};
static const struct qcom_pdm_domain_data adsp_root_pd_pdr = {
.domain = "msm/adsp/root_pd",
.instance_id = 74,
.services = {
"tms/pdr_enabled",
NULL,
},
};
static const struct qcom_pdm_domain_data adsp_sensor_pd = {
.domain = "msm/adsp/sensor_pd",
.instance_id = 74,
.services = { NULL },
};
static const struct qcom_pdm_domain_data msm8996_adsp_audio_pd = {
.domain = "msm/adsp/audio_pd",
.instance_id = 4,
.services = { NULL },
};
static const struct qcom_pdm_domain_data msm8996_adsp_root_pd = {
.domain = "msm/adsp/root_pd",
.instance_id = 4,
.services = { NULL },
};
static const struct qcom_pdm_domain_data cdsp_root_pd = {
.domain = "msm/cdsp/root_pd",
.instance_id = 76,
.services = { NULL },
};
static const struct qcom_pdm_domain_data slpi_root_pd = {
.domain = "msm/slpi/root_pd",
.instance_id = 90,
.services = { NULL },
};
static const struct qcom_pdm_domain_data slpi_sensor_pd = {
.domain = "msm/slpi/sensor_pd",
.instance_id = 90,
.services = { NULL },
};
static const struct qcom_pdm_domain_data mpss_root_pd = {
.domain = "msm/modem/root_pd",
.instance_id = 180,
.services = {
NULL,
},
};
static const struct qcom_pdm_domain_data mpss_root_pd_gps = {
.domain = "msm/modem/root_pd",
.instance_id = 180,
.services = {
"gps/gps_service",
NULL,
},
};
static const struct qcom_pdm_domain_data mpss_root_pd_gps_pdr = {
.domain = "msm/modem/root_pd",
.instance_id = 180,
.services = {
"gps/gps_service",
"tms/pdr_enabled",
NULL,
},
};
static const struct qcom_pdm_domain_data msm8996_mpss_root_pd = {
.domain = "msm/modem/root_pd",
.instance_id = 100,
.services = { NULL },
};
static const struct qcom_pdm_domain_data mpss_wlan_pd = {
.domain = "msm/modem/wlan_pd",
.instance_id = 180,
.services = {
"kernel/elf_loader",
"wlan/fw",
NULL,
},
};
static const struct qcom_pdm_domain_data *msm8996_domains[] = {
&msm8996_adsp_audio_pd,
&msm8996_adsp_root_pd,
&msm8996_mpss_root_pd,
NULL,
};
static const struct qcom_pdm_domain_data *msm8998_domains[] = {
&mpss_root_pd,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *qcm2290_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&adsp_sensor_pd,
&mpss_root_pd_gps,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *qcs404_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&adsp_sensor_pd,
&cdsp_root_pd,
&mpss_root_pd,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sc7180_domains[] = {
&adsp_audio_pd,
&adsp_root_pd_pdr,
&adsp_sensor_pd,
&mpss_root_pd_gps_pdr,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sc7280_domains[] = {
&adsp_audio_pd,
&adsp_root_pd_pdr,
&adsp_charger_pd,
&adsp_sensor_pd,
&cdsp_root_pd,
&mpss_root_pd_gps_pdr,
NULL,
};
static const struct qcom_pdm_domain_data *sc8180x_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&adsp_charger_pd,
&cdsp_root_pd,
&mpss_root_pd_gps,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sc8280xp_domains[] = {
&adsp_audio_pd,
&adsp_root_pd_pdr,
&adsp_charger_pd,
&cdsp_root_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sdm660_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&adsp_sensor_pd,
&cdsp_root_pd,
&mpss_root_pd,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sdm670_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&cdsp_root_pd,
&mpss_root_pd,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sdm845_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&cdsp_root_pd,
&mpss_root_pd,
&mpss_wlan_pd,
&slpi_root_pd,
&slpi_sensor_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sm6115_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&adsp_sensor_pd,
&cdsp_root_pd,
&mpss_root_pd_gps,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sm6350_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&adsp_sensor_pd,
&cdsp_root_pd,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sm8150_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&cdsp_root_pd,
&mpss_root_pd_gps,
&mpss_wlan_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sm8250_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&cdsp_root_pd,
&slpi_root_pd,
&slpi_sensor_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sm8350_domains[] = {
&adsp_audio_pd,
&adsp_root_pd_pdr,
&adsp_charger_pd,
&cdsp_root_pd,
&mpss_root_pd_gps,
&slpi_root_pd,
&slpi_sensor_pd,
NULL,
};
static const struct qcom_pdm_domain_data *sm8550_domains[] = {
&adsp_audio_pd,
&adsp_root_pd,
&adsp_charger_pd,
&adsp_sensor_pd,
&cdsp_root_pd,
&mpss_root_pd_gps,
NULL,
};
static const struct of_device_id qcom_pdm_domains[] = {
{ .compatible = "qcom,apq8064", .data = NULL, },
{ .compatible = "qcom,apq8074", .data = NULL, },
{ .compatible = "qcom,apq8084", .data = NULL, },
{ .compatible = "qcom,apq8096", .data = msm8996_domains, },
{ .compatible = "qcom,msm8226", .data = NULL, },
{ .compatible = "qcom,msm8974", .data = NULL, },
{ .compatible = "qcom,msm8996", .data = msm8996_domains, },
{ .compatible = "qcom,msm8998", .data = msm8998_domains, },
{ .compatible = "qcom,qcm2290", .data = qcm2290_domains, },
{ .compatible = "qcom,qcs404", .data = qcs404_domains, },
{ .compatible = "qcom,sc7180", .data = sc7180_domains, },
{ .compatible = "qcom,sc7280", .data = sc7280_domains, },
{ .compatible = "qcom,sc8180x", .data = sc8180x_domains, },
{ .compatible = "qcom,sc8280xp", .data = sc8280xp_domains, },
{ .compatible = "qcom,sda660", .data = sdm660_domains, },
{ .compatible = "qcom,sdm660", .data = sdm660_domains, },
{ .compatible = "qcom,sdm670", .data = sdm670_domains, },
{ .compatible = "qcom,sdm845", .data = sdm845_domains, },
{ .compatible = "qcom,sm4250", .data = sm6115_domains, },
{ .compatible = "qcom,sm6115", .data = sm6115_domains, },
{ .compatible = "qcom,sm6350", .data = sm6350_domains, },
{ .compatible = "qcom,sm8150", .data = sm8150_domains, },
{ .compatible = "qcom,sm8250", .data = sm8250_domains, },
{ .compatible = "qcom,sm8350", .data = sm8350_domains, },
{ .compatible = "qcom,sm8450", .data = sm8350_domains, },
{ .compatible = "qcom,sm8550", .data = sm8550_domains, },
{ .compatible = "qcom,sm8650", .data = sm8550_domains, },
{},
};
static void qcom_pdm_stop(struct qcom_pdm_data *data)
{
qcom_pdm_free_domains(data);
/* The server is removed automatically */
qmi_handle_release(&data->handle);
kfree(data);
}
static struct qcom_pdm_data *qcom_pdm_start(void)
{
const struct qcom_pdm_domain_data * const *domains;
const struct of_device_id *match;
struct qcom_pdm_data *data;
struct device_node *root;
int ret, i;
root = of_find_node_by_path("/");
if (!root)
return ERR_PTR(-ENODEV);
match = of_match_node(qcom_pdm_domains, root);
of_node_put(root);
if (!match) {
pr_notice("PDM: no support for the platform, userspace daemon might be required.\n");
return ERR_PTR(-ENODEV);
}
domains = match->data;
if (!domains) {
pr_debug("PDM: no domains\n");
return ERR_PTR(-ENODEV);
}
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&data->services);
ret = qmi_handle_init(&data->handle, SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN,
NULL, qcom_pdm_msg_handlers);
if (ret) {
kfree(data);
return ERR_PTR(ret);
}
refcount_set(&data->refcnt, 1);
for (i = 0; domains[i]; i++) {
ret = qcom_pdm_add_domain(data, domains[i]);
if (ret)
goto err_stop;
}
ret = qmi_add_server(&data->handle, SERVREG_LOCATOR_SERVICE,
SERVREG_QMI_VERSION, SERVREG_QMI_INSTANCE);
if (ret) {
pr_err("PDM: error adding server %d\n", ret);
goto err_stop;
}
return data;
err_stop:
qcom_pdm_stop(data);
return ERR_PTR(ret);
}
static int qcom_pdm_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *id)
{
struct qcom_pdm_data *data;
int ret = 0;
mutex_lock(&qcom_pdm_mutex);
if (!__qcom_pdm_data) {
data = qcom_pdm_start();
if (IS_ERR(data))
ret = PTR_ERR(data);
else
__qcom_pdm_data = data;
}
auxiliary_set_drvdata(auxdev, __qcom_pdm_data);
mutex_unlock(&qcom_pdm_mutex);
return ret;
}
static void qcom_pdm_remove(struct auxiliary_device *auxdev)
{
struct qcom_pdm_data *data;
data = auxiliary_get_drvdata(auxdev);
if (!data)
return;
if (refcount_dec_and_mutex_lock(&data->refcnt, &qcom_pdm_mutex)) {
__qcom_pdm_data = NULL;
qcom_pdm_stop(data);
mutex_unlock(&qcom_pdm_mutex);
}
}
static const struct auxiliary_device_id qcom_pdm_table[] = {
{ .name = "qcom_common.pd-mapper" },
{},
};
MODULE_DEVICE_TABLE(auxiliary, qcom_pdm_table);
static struct auxiliary_driver qcom_pdm_drv = {
.name = "qcom-pdm-mapper",
.id_table = qcom_pdm_table,
.probe = qcom_pdm_probe,
.remove = qcom_pdm_remove,
};
module_auxiliary_driver(qcom_pdm_drv);
MODULE_DESCRIPTION("Qualcomm Protection Domain Mapper");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/soc/qcom/qmi.h>
#include "pdr_internal.h"
const struct qmi_elem_info servreg_location_entry_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset = offsetof(struct servreg_location_entry,
name),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset = offsetof(struct servreg_location_entry,
instance),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset = offsetof(struct servreg_location_entry,
service_data_valid),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset = offsetof(struct servreg_location_entry,
service_data),
},
{}
};
EXPORT_SYMBOL_GPL(servreg_location_entry_ei);
const struct qmi_elem_info servreg_get_domain_list_req_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_get_domain_list_req,
service_name),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_get_domain_list_req,
domain_offset_valid),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_get_domain_list_req,
domain_offset),
},
{}
};
EXPORT_SYMBOL_GPL(servreg_get_domain_list_req_ei);
const struct qmi_elem_info servreg_get_domain_list_resp_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_get_domain_list_resp,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_get_domain_list_resp,
total_domains_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_get_domain_list_resp,
total_domains),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset = offsetof(struct servreg_get_domain_list_resp,
db_rev_count_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset = offsetof(struct servreg_get_domain_list_resp,
db_rev_count),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x12,
.offset = offsetof(struct servreg_get_domain_list_resp,
domain_list_valid),
},
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x12,
.offset = offsetof(struct servreg_get_domain_list_resp,
domain_list_len),
},
{
.data_type = QMI_STRUCT,
.elem_len = SERVREG_DOMAIN_LIST_LENGTH,
.elem_size = sizeof(struct servreg_location_entry),
.array_type = VAR_LEN_ARRAY,
.tlv_type = 0x12,
.offset = offsetof(struct servreg_get_domain_list_resp,
domain_list),
.ei_array = servreg_location_entry_ei,
},
{}
};
EXPORT_SYMBOL_GPL(servreg_get_domain_list_resp_ei);
const struct qmi_elem_info servreg_register_listener_req_ei[] = {
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_register_listener_req,
enable),
},
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_register_listener_req,
service_path),
},
{}
};
EXPORT_SYMBOL_GPL(servreg_register_listener_req_ei);
const struct qmi_elem_info servreg_register_listener_resp_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_register_listener_resp,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_register_listener_resp,
curr_state_valid),
},
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
.elem_size = sizeof(enum servreg_service_state),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct servreg_register_listener_resp,
curr_state),
},
{}
};
EXPORT_SYMBOL_GPL(servreg_register_listener_resp_ei);
const struct qmi_elem_info servreg_restart_pd_req_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_restart_pd_req,
service_path),
},
{}
};
EXPORT_SYMBOL_GPL(servreg_restart_pd_req_ei);
const struct qmi_elem_info servreg_restart_pd_resp_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_restart_pd_resp,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{}
};
EXPORT_SYMBOL_GPL(servreg_restart_pd_resp_ei);
const struct qmi_elem_info servreg_state_updated_ind_ei[] = {
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_state_updated_ind,
curr_state),
},
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_state_updated_ind,
service_path),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x03,
.offset = offsetof(struct servreg_state_updated_ind,
transaction_id),
},
{}
};
EXPORT_SYMBOL_GPL(servreg_state_updated_ind_ei);
const struct qmi_elem_info servreg_set_ack_req_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_set_ack_req,
service_path),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_set_ack_req,
transaction_id),
},
{}
};
EXPORT_SYMBOL_GPL(servreg_set_ack_req_ei);
const struct qmi_elem_info servreg_set_ack_resp_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_set_ack_resp,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{}
};
EXPORT_SYMBOL_GPL(servreg_set_ack_resp_ei);
const struct qmi_elem_info servreg_loc_pfr_req_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = VAR_LEN_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct servreg_loc_pfr_req, service)
},
{
.data_type = QMI_STRING,
.elem_len = SERVREG_NAME_LENGTH + 1,
.elem_size = sizeof(char),
.array_type = VAR_LEN_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct servreg_loc_pfr_req, reason)
},
{}
};
EXPORT_SYMBOL_GPL(servreg_loc_pfr_req_ei);
const struct qmi_elem_info servreg_loc_pfr_resp_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof_field(struct servreg_loc_pfr_resp, rsp),
.tlv_type = 0x02,
.offset = offsetof(struct servreg_loc_pfr_resp, rsp),
.ei_array = qmi_response_type_v01_ei,
},
{}
};
EXPORT_SYMBOL_GPL(servreg_loc_pfr_resp_ei);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Qualcomm Protection Domain messages data");
...@@ -646,13 +646,14 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) ...@@ -646,13 +646,14 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg)
{ {
struct tcs_group *tcs; struct tcs_group *tcs;
int tcs_id; int tcs_id;
unsigned long flags;
might_sleep();
tcs = get_tcs_for_msg(drv, msg); tcs = get_tcs_for_msg(drv, msg);
if (IS_ERR(tcs)) if (IS_ERR(tcs))
return PTR_ERR(tcs); return PTR_ERR(tcs);
spin_lock_irqsave(&drv->lock, flags); spin_lock_irq(&drv->lock);
/* Wait forever for a free tcs. It better be there eventually! */ /* Wait forever for a free tcs. It better be there eventually! */
wait_event_lock_irq(drv->tcs_wait, wait_event_lock_irq(drv->tcs_wait,
...@@ -670,7 +671,7 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) ...@@ -670,7 +671,7 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg)
write_tcs_reg_sync(drv, drv->regs[RSC_DRV_CMD_ENABLE], tcs_id, 0); write_tcs_reg_sync(drv, drv->regs[RSC_DRV_CMD_ENABLE], tcs_id, 0);
enable_tcs_irq(drv, tcs_id, true); enable_tcs_irq(drv, tcs_id, true);
} }
spin_unlock_irqrestore(&drv->lock, flags); spin_unlock_irq(&drv->lock);
/* /*
* These two can be done after the lock is released because: * These two can be done after the lock is released because:
......
...@@ -183,7 +183,6 @@ static int __rpmh_write(const struct device *dev, enum rpmh_state state, ...@@ -183,7 +183,6 @@ static int __rpmh_write(const struct device *dev, enum rpmh_state state,
} }
if (state == RPMH_ACTIVE_ONLY_STATE) { if (state == RPMH_ACTIVE_ONLY_STATE) {
WARN_ON(irqs_disabled());
ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg);
} else { } else {
/* Clean up our call by spoofing tx_done */ /* Clean up our call by spoofing tx_done */
......
...@@ -795,6 +795,39 @@ int qcom_smem_get_soc_id(u32 *id) ...@@ -795,6 +795,39 @@ int qcom_smem_get_soc_id(u32 *id)
} }
EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id); EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id);
/**
* qcom_smem_get_feature_code() - return the feature code
* @code: On success, return the feature code here.
*
* Look up the feature code identifier from SMEM and return it.
*
* Return: 0 on success, negative errno on failure.
*/
int qcom_smem_get_feature_code(u32 *code)
{
struct socinfo *info;
u32 raw_code;
info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
if (IS_ERR(info))
return PTR_ERR(info);
/* This only makes sense for socinfo >= 16 */
if (__le32_to_cpu(info->fmt) < SOCINFO_VERSION(0, 16))
return -EOPNOTSUPP;
raw_code = __le32_to_cpu(info->feature_code);
/* Ensure the value makes sense */
if (raw_code > SOCINFO_FC_INT_MAX)
raw_code = SOCINFO_FC_UNKNOWN;
*code = raw_code;
return 0;
}
EXPORT_SYMBOL_GPL(qcom_smem_get_feature_code);
static int qcom_smem_get_sbl_version(struct qcom_smem *smem) static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
{ {
struct smem_header *header; struct smem_header *header;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_wakeirq.h> #include <linux/pm_wakeirq.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/seq_file.h>
#include <linux/soc/qcom/smem.h> #include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h> #include <linux/soc/qcom/smem_state.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -353,11 +354,19 @@ static int smp2p_set_irq_type(struct irq_data *irqd, unsigned int type) ...@@ -353,11 +354,19 @@ static int smp2p_set_irq_type(struct irq_data *irqd, unsigned int type)
return 0; return 0;
} }
static void smp2p_irq_print_chip(struct irq_data *irqd, struct seq_file *p)
{
struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
seq_printf(p, " %8s", dev_name(entry->smp2p->dev));
}
static struct irq_chip smp2p_irq_chip = { static struct irq_chip smp2p_irq_chip = {
.name = "smp2p", .name = "smp2p",
.irq_mask = smp2p_mask_irq, .irq_mask = smp2p_mask_irq,
.irq_unmask = smp2p_unmask_irq, .irq_unmask = smp2p_unmask_irq,
.irq_set_type = smp2p_set_irq_type, .irq_set_type = smp2p_set_irq_type,
.irq_print_chip = smp2p_irq_print_chip,
}; };
static int smp2p_irq_map(struct irq_domain *d, static int smp2p_irq_map(struct irq_domain *d,
...@@ -617,7 +626,7 @@ static int qcom_smp2p_probe(struct platform_device *pdev) ...@@ -617,7 +626,7 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(&pdev->dev, irq, ret = devm_request_threaded_irq(&pdev->dev, irq,
NULL, qcom_smp2p_intr, NULL, qcom_smp2p_intr,
IRQF_ONESHOT, IRQF_ONESHOT,
"smp2p", (void *)smp2p); NULL, (void *)smp2p);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to request interrupt\n"); dev_err(&pdev->dev, "failed to request interrupt\n");
goto unwind_interfaces; goto unwind_interfaces;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/mailbox_client.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
...@@ -71,6 +72,7 @@ struct smsm_host; ...@@ -71,6 +72,7 @@ struct smsm_host;
* @lock: spinlock for read-modify-write of the outgoing state * @lock: spinlock for read-modify-write of the outgoing state
* @entries: context for each of the entries * @entries: context for each of the entries
* @hosts: context for each of the hosts * @hosts: context for each of the hosts
* @mbox_client: mailbox client handle
*/ */
struct qcom_smsm { struct qcom_smsm {
struct device *dev; struct device *dev;
...@@ -88,6 +90,8 @@ struct qcom_smsm { ...@@ -88,6 +90,8 @@ struct qcom_smsm {
struct smsm_entry *entries; struct smsm_entry *entries;
struct smsm_host *hosts; struct smsm_host *hosts;
struct mbox_client mbox_client;
}; };
/** /**
...@@ -120,11 +124,14 @@ struct smsm_entry { ...@@ -120,11 +124,14 @@ struct smsm_entry {
* @ipc_regmap: regmap for outgoing interrupt * @ipc_regmap: regmap for outgoing interrupt
* @ipc_offset: offset in @ipc_regmap for outgoing interrupt * @ipc_offset: offset in @ipc_regmap for outgoing interrupt
* @ipc_bit: bit in @ipc_regmap + @ipc_offset for outgoing interrupt * @ipc_bit: bit in @ipc_regmap + @ipc_offset for outgoing interrupt
* @mbox_chan: apcs ipc mailbox channel handle
*/ */
struct smsm_host { struct smsm_host {
struct regmap *ipc_regmap; struct regmap *ipc_regmap;
int ipc_offset; int ipc_offset;
int ipc_bit; int ipc_bit;
struct mbox_chan *mbox_chan;
}; };
/** /**
...@@ -172,7 +179,13 @@ static int smsm_update_bits(void *data, u32 mask, u32 value) ...@@ -172,7 +179,13 @@ static int smsm_update_bits(void *data, u32 mask, u32 value)
hostp = &smsm->hosts[host]; hostp = &smsm->hosts[host];
val = readl(smsm->subscription + host); val = readl(smsm->subscription + host);
if (val & changes && hostp->ipc_regmap) { if (!(val & changes))
continue;
if (hostp->mbox_chan) {
mbox_send_message(hostp->mbox_chan, NULL);
mbox_client_txdone(hostp->mbox_chan, 0);
} else if (hostp->ipc_regmap) {
regmap_write(hostp->ipc_regmap, regmap_write(hostp->ipc_regmap,
hostp->ipc_offset, hostp->ipc_offset,
BIT(hostp->ipc_bit)); BIT(hostp->ipc_bit));
...@@ -352,6 +365,28 @@ static const struct irq_domain_ops smsm_irq_ops = { ...@@ -352,6 +365,28 @@ static const struct irq_domain_ops smsm_irq_ops = {
.xlate = irq_domain_xlate_twocell, .xlate = irq_domain_xlate_twocell,
}; };
/**
* smsm_parse_mbox() - requests an mbox channel
* @smsm: smsm driver context
* @host_id: index of the remote host to be resolved
*
* Requests the desired channel using the mbox interface which is needed for
* sending the outgoing interrupts to a remove hosts - identified by @host_id.
*/
static int smsm_parse_mbox(struct qcom_smsm *smsm, unsigned int host_id)
{
struct smsm_host *host = &smsm->hosts[host_id];
int ret = 0;
host->mbox_chan = mbox_request_channel(&smsm->mbox_client, host_id);
if (IS_ERR(host->mbox_chan)) {
ret = PTR_ERR(host->mbox_chan);
host->mbox_chan = NULL;
}
return ret;
}
/** /**
* smsm_parse_ipc() - parses a qcom,ipc-%d device tree property * smsm_parse_ipc() - parses a qcom,ipc-%d device tree property
* @smsm: smsm driver context * @smsm: smsm driver context
...@@ -521,8 +556,16 @@ static int qcom_smsm_probe(struct platform_device *pdev) ...@@ -521,8 +556,16 @@ static int qcom_smsm_probe(struct platform_device *pdev)
"qcom,local-host", "qcom,local-host",
&smsm->local_host); &smsm->local_host);
smsm->mbox_client.dev = &pdev->dev;
smsm->mbox_client.knows_txdone = true;
/* Parse the host properties */ /* Parse the host properties */
for (id = 0; id < smsm->num_hosts; id++) { for (id = 0; id < smsm->num_hosts; id++) {
/* Try using mbox interface first, otherwise fall back to syscon */
ret = smsm_parse_mbox(smsm, id);
if (!ret)
continue;
ret = smsm_parse_ipc(smsm, id); ret = smsm_parse_ipc(smsm, id);
if (ret < 0) if (ret < 0)
goto out_put; goto out_put;
...@@ -609,6 +652,9 @@ static int qcom_smsm_probe(struct platform_device *pdev) ...@@ -609,6 +652,9 @@ static int qcom_smsm_probe(struct platform_device *pdev)
qcom_smem_state_unregister(smsm->state); qcom_smem_state_unregister(smsm->state);
out_put: out_put:
for (id = 0; id < smsm->num_hosts; id++)
mbox_free_channel(smsm->hosts[id].mbox_chan);
of_node_put(local_node); of_node_put(local_node);
return ret; return ret;
} }
...@@ -622,6 +668,9 @@ static void qcom_smsm_remove(struct platform_device *pdev) ...@@ -622,6 +668,9 @@ static void qcom_smsm_remove(struct platform_device *pdev)
if (smsm->entries[id].domain) if (smsm->entries[id].domain)
irq_domain_remove(smsm->entries[id].domain); irq_domain_remove(smsm->entries[id].domain);
for (id = 0; id < smsm->num_hosts; id++)
mbox_free_channel(smsm->hosts[id].mbox_chan);
qcom_smem_state_unregister(smsm->state); qcom_smem_state_unregister(smsm->state);
} }
......
...@@ -21,14 +21,6 @@ ...@@ -21,14 +21,6 @@
#include <dt-bindings/arm/qcom,ids.h> #include <dt-bindings/arm/qcom,ids.h>
/*
* SoC version type with major number in the upper 16 bits and minor
* number in the lower 16 bits.
*/
#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff)
#define SOCINFO_MINOR(ver) ((ver) & 0xffff)
#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
/* Helper macros to create soc_id table */ /* Helper macros to create soc_id table */
#define qcom_board_id(id) QCOM_ID_ ## id, __stringify(id) #define qcom_board_id(id) QCOM_ID_ ## id, __stringify(id)
#define qcom_board_id_named(id, name) QCOM_ID_ ## id, (name) #define qcom_board_id_named(id, name) QCOM_ID_ ## id, (name)
...@@ -133,7 +125,8 @@ static const char *const pmic_models[] = { ...@@ -133,7 +125,8 @@ static const char *const pmic_models[] = {
[72] = "PMR735D", [72] = "PMR735D",
[73] = "PM8550", [73] = "PM8550",
[74] = "PMK8550", [74] = "PMK8550",
[82] = "SMB2360", [82] = "PMC8380",
[83] = "SMB2360",
}; };
struct socinfo_params { struct socinfo_params {
...@@ -348,6 +341,7 @@ static const struct soc_id soc_id[] = { ...@@ -348,6 +341,7 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(SDA630) }, { qcom_board_id(SDA630) },
{ qcom_board_id(MSM8905) }, { qcom_board_id(MSM8905) },
{ qcom_board_id(SDX202) }, { qcom_board_id(SDX202) },
{ qcom_board_id(SDM670) },
{ qcom_board_id(SDM450) }, { qcom_board_id(SDM450) },
{ qcom_board_id(SM8150) }, { qcom_board_id(SM8150) },
{ qcom_board_id(SDA845) }, { qcom_board_id(SDA845) },
...@@ -445,6 +439,7 @@ static const struct soc_id soc_id[] = { ...@@ -445,6 +439,7 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(QCS8550) }, { qcom_board_id(QCS8550) },
{ qcom_board_id(QCM8550) }, { qcom_board_id(QCM8550) },
{ qcom_board_id(IPQ5300) }, { qcom_board_id(IPQ5300) },
{ qcom_board_id(IPQ5321) },
}; };
static const char *socinfo_machine(struct device *dev, unsigned int id) static const char *socinfo_machine(struct device *dev, unsigned int id)
......
...@@ -572,4 +572,5 @@ static int __init qcom_spm_init(void) ...@@ -572,4 +572,5 @@ static int __init qcom_spm_init(void)
} }
arch_initcall(qcom_spm_init); arch_initcall(qcom_spm_init);
MODULE_DESCRIPTION("Qualcomm Subsystem Power Manager (SPM)");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -175,6 +175,7 @@ ...@@ -175,6 +175,7 @@
#define QCOM_ID_SDA630 327 #define QCOM_ID_SDA630 327
#define QCOM_ID_MSM8905 331 #define QCOM_ID_MSM8905 331
#define QCOM_ID_SDX202 333 #define QCOM_ID_SDX202 333
#define QCOM_ID_SDM670 336
#define QCOM_ID_SDM450 338 #define QCOM_ID_SDM450 338
#define QCOM_ID_SM8150 339 #define QCOM_ID_SM8150 339
#define QCOM_ID_SDA845 341 #define QCOM_ID_SDA845 341
...@@ -272,6 +273,7 @@ ...@@ -272,6 +273,7 @@
#define QCOM_ID_QCS8550 603 #define QCOM_ID_QCS8550 603
#define QCOM_ID_QCM8550 604 #define QCOM_ID_QCM8550 604
#define QCOM_ID_IPQ5300 624 #define QCOM_ID_IPQ5300 624
#define QCOM_ID_IPQ5321 650
/* /*
* The board type and revision information, used by Qualcomm bootloaders and * The board type and revision information, used by Qualcomm bootloaders and
......
...@@ -73,9 +73,9 @@ static inline void qseecom_dma_free(struct qseecom_client *client, size_t size, ...@@ -73,9 +73,9 @@ static inline void qseecom_dma_free(struct qseecom_client *client, size_t size,
/** /**
* qcom_qseecom_app_send() - Send to and receive data from a given QSEE app. * qcom_qseecom_app_send() - Send to and receive data from a given QSEE app.
* @client: The QSEECOM client associated with the target app. * @client: The QSEECOM client associated with the target app.
* @req: DMA address of the request buffer sent to the app. * @req: Request buffer sent to the app (must be TZ memory).
* @req_size: Size of the request buffer. * @req_size: Size of the request buffer.
* @rsp: DMA address of the response buffer, written to by the app. * @rsp: Response buffer, written to by the app (must be TZ memory).
* @rsp_size: Size of the response buffer. * @rsp_size: Size of the response buffer.
* *
* Sends a request to the QSEE app associated with the given client and read * Sends a request to the QSEE app associated with the given client and read
...@@ -90,8 +90,8 @@ static inline void qseecom_dma_free(struct qseecom_client *client, size_t size, ...@@ -90,8 +90,8 @@ static inline void qseecom_dma_free(struct qseecom_client *client, size_t size,
* Return: Zero on success, nonzero on failure. * Return: Zero on success, nonzero on failure.
*/ */
static inline int qcom_qseecom_app_send(struct qseecom_client *client, static inline int qcom_qseecom_app_send(struct qseecom_client *client,
dma_addr_t req, size_t req_size, void *req, size_t req_size,
dma_addr_t rsp, size_t rsp_size) void *rsp, size_t rsp_size)
{ {
return qcom_scm_qseecom_app_send(client->app_id, req, req_size, rsp, rsp_size); return qcom_scm_qseecom_app_send(client->app_id, req, req_size, rsp, rsp_size);
} }
......
...@@ -115,11 +115,40 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, ...@@ -115,11 +115,40 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val,
int qcom_scm_lmh_profile_change(u32 profile_id); int qcom_scm_lmh_profile_change(u32 profile_id);
bool qcom_scm_lmh_dcvsh_available(void); bool qcom_scm_lmh_dcvsh_available(void);
/*
* Request TZ to program set of access controlled registers necessary
* irrespective of any features
*/
#define QCOM_SCM_GPU_ALWAYS_EN_REQ BIT(0)
/*
* Request TZ to program BCL id to access controlled register when BCL is
* enabled
*/
#define QCOM_SCM_GPU_BCL_EN_REQ BIT(1)
/*
* Request TZ to program set of access controlled register for CLX feature
* when enabled
*/
#define QCOM_SCM_GPU_CLX_EN_REQ BIT(2)
/*
* Request TZ to program tsense ids to access controlled registers for reading
* gpu temperature sensors
*/
#define QCOM_SCM_GPU_TSENSE_EN_REQ BIT(3)
int qcom_scm_gpu_init_regs(u32 gpu_req);
int qcom_scm_shm_bridge_enable(void);
int qcom_scm_shm_bridge_create(struct device *dev, u64 pfn_and_ns_perm_flags,
u64 ipfn_and_s_perm_flags, u64 size_and_flags,
u64 ns_vmids, u64 *handle);
int qcom_scm_shm_bridge_delete(struct device *dev, u64 handle);
#ifdef CONFIG_QCOM_QSEECOM #ifdef CONFIG_QCOM_QSEECOM
int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id); int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id);
int qcom_scm_qseecom_app_send(u32 app_id, dma_addr_t req, size_t req_size, int qcom_scm_qseecom_app_send(u32 app_id, void *req, size_t req_size,
dma_addr_t rsp, size_t rsp_size); void *rsp, size_t rsp_size);
#else /* CONFIG_QCOM_QSEECOM */ #else /* CONFIG_QCOM_QSEECOM */
...@@ -129,8 +158,8 @@ static inline int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id) ...@@ -129,8 +158,8 @@ static inline int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id)
} }
static inline int qcom_scm_qseecom_app_send(u32 app_id, static inline int qcom_scm_qseecom_app_send(u32 app_id,
dma_addr_t req, size_t req_size, void *req, size_t req_size,
dma_addr_t rsp, size_t rsp_size) void *rsp, size_t rsp_size)
{ {
return -EINVAL; return -EINVAL;
} }
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2023-2024 Linaro Ltd.
*/
#ifndef __QCOM_TZMEM_H
#define __QCOM_TZMEM_H
#include <linux/cleanup.h>
#include <linux/gfp.h>
#include <linux/types.h>
struct device;
struct qcom_tzmem_pool;
/**
* enum qcom_tzmem_policy - Policy for pool growth.
*/
enum qcom_tzmem_policy {
/**< Static pool, never grow above initial size. */
QCOM_TZMEM_POLICY_STATIC = 1,
/**< When out of memory, add increment * current size of memory. */
QCOM_TZMEM_POLICY_MULTIPLIER,
/**< When out of memory add as much as is needed until max_size. */
QCOM_TZMEM_POLICY_ON_DEMAND,
};
/**
* struct qcom_tzmem_pool_config - TZ memory pool configuration.
* @initial_size: Number of bytes to allocate for the pool during its creation.
* @policy: Pool size growth policy.
* @increment: Used with policies that allow pool growth.
* @max_size: Size above which the pool will never grow.
*/
struct qcom_tzmem_pool_config {
size_t initial_size;
enum qcom_tzmem_policy policy;
size_t increment;
size_t max_size;
};
struct qcom_tzmem_pool *
qcom_tzmem_pool_new(const struct qcom_tzmem_pool_config *config);
void qcom_tzmem_pool_free(struct qcom_tzmem_pool *pool);
struct qcom_tzmem_pool *
devm_qcom_tzmem_pool_new(struct device *dev,
const struct qcom_tzmem_pool_config *config);
void *qcom_tzmem_alloc(struct qcom_tzmem_pool *pool, size_t size, gfp_t gfp);
void qcom_tzmem_free(void *ptr);
DEFINE_FREE(qcom_tzmem, void *, if (_T) qcom_tzmem_free(_T))
phys_addr_t qcom_tzmem_to_phys(void *ptr);
#endif /* __QCOM_TZMEM */
...@@ -115,7 +115,8 @@ struct llcc_edac_reg_offset { ...@@ -115,7 +115,8 @@ struct llcc_edac_reg_offset {
/** /**
* struct llcc_drv_data - Data associated with the llcc driver * struct llcc_drv_data - Data associated with the llcc driver
* @regmaps: regmaps associated with the llcc device * @regmaps: regmaps associated with the llcc device
* @bcast_regmap: regmap associated with llcc broadcast offset * @bcast_regmap: regmap associated with llcc broadcast OR offset
* @bcast_and_regmap: regmap associated with llcc broadcast AND offset
* @cfg: pointer to the data structure for slice configuration * @cfg: pointer to the data structure for slice configuration
* @edac_reg_offset: Offset of the LLCC EDAC registers * @edac_reg_offset: Offset of the LLCC EDAC registers
* @lock: mutex associated with each slice * @lock: mutex associated with each slice
...@@ -129,6 +130,7 @@ struct llcc_edac_reg_offset { ...@@ -129,6 +130,7 @@ struct llcc_edac_reg_offset {
struct llcc_drv_data { struct llcc_drv_data {
struct regmap **regmaps; struct regmap **regmaps;
struct regmap *bcast_regmap; struct regmap *bcast_regmap;
struct regmap *bcast_and_regmap;
const struct llcc_slice_config *cfg; const struct llcc_slice_config *cfg;
const struct llcc_edac_reg_offset *edac_reg_offset; const struct llcc_edac_reg_offset *edac_reg_offset;
struct mutex lock; struct mutex lock;
......
...@@ -13,5 +13,6 @@ int qcom_smem_get_free_space(unsigned host); ...@@ -13,5 +13,6 @@ int qcom_smem_get_free_space(unsigned host);
phys_addr_t qcom_smem_virt_to_phys(void *p); phys_addr_t qcom_smem_virt_to_phys(void *p);
int qcom_smem_get_soc_id(u32 *id); int qcom_smem_get_soc_id(u32 *id);
int qcom_smem_get_feature_code(u32 *code);
#endif #endif
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
#ifndef __QCOM_SOCINFO_H__ #ifndef __QCOM_SOCINFO_H__
#define __QCOM_SOCINFO_H__ #define __QCOM_SOCINFO_H__
#include <linux/types.h>
/* /*
* SMEM item id, used to acquire handles to respective * SMEM item id, used to acquire handles to respective
* SMEM region. * SMEM region.
...@@ -12,6 +14,14 @@ ...@@ -12,6 +14,14 @@
#define SMEM_SOCINFO_BUILD_ID_LENGTH 32 #define SMEM_SOCINFO_BUILD_ID_LENGTH 32
#define SMEM_SOCINFO_CHIP_ID_LENGTH 32 #define SMEM_SOCINFO_CHIP_ID_LENGTH 32
/*
* SoC version type with major number in the upper 16 bits and minor
* number in the lower 16 bits.
*/
#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff)
#define SOCINFO_MINOR(ver) ((ver) & 0xffff)
#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
/* Socinfo SMEM item structure */ /* Socinfo SMEM item structure */
struct socinfo { struct socinfo {
__le32 fmt; __le32 fmt;
...@@ -74,4 +84,28 @@ struct socinfo { ...@@ -74,4 +84,28 @@ struct socinfo {
__le32 boot_core; __le32 boot_core;
}; };
/* Internal feature codes */
enum qcom_socinfo_feature_code {
/* External feature codes */
SOCINFO_FC_UNKNOWN = 0x0,
SOCINFO_FC_AA,
SOCINFO_FC_AB,
SOCINFO_FC_AC,
SOCINFO_FC_AD,
SOCINFO_FC_AE,
SOCINFO_FC_AF,
SOCINFO_FC_AG,
SOCINFO_FC_AH,
};
/* Internal feature codes */
/* Valid values: 0 <= n <= 0xf */
#define SOCINFO_FC_Yn(n) (0xf1 + (n))
#define SOCINFO_FC_INT_MAX SOCINFO_FC_Yn(0xf)
/* Product codes */
#define SOCINFO_PC_UNKNOWN 0
#define SOCINFO_PCn(n) ((n) + 1)
#define SOCINFO_PC_RESERVE (BIT(31) - 1)
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment